]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
mod_conference add vid-floor command to branch off the floor for video on demand...
authorAnthony Minessale <anthm@freeswitch.org>
Mon, 15 Jul 2013 16:07:56 +0000 (11:07 -0500)
committerAnthony Minessale <anthm@freeswitch.org>
Mon, 15 Jul 2013 16:07:56 +0000 (11:07 -0500)
src/mod/applications/mod_conference/mod_conference.c

index bb572f4e78407a54ab552bcfba9d8dd9a02b08d5..23f08f69b251592813258ef81367faf2d85ff5b8 100644 (file)
@@ -334,6 +334,7 @@ typedef struct conference_obj {
        switch_mutex_t *mutex;
        conference_member_t *members;
        conference_member_t *floor_holder;
+       conference_member_t *video_floor_holder;
        switch_mutex_t *member_mutex;
        conference_file_node_t *fnode;
        conference_file_node_t *async_fnode;
@@ -527,7 +528,6 @@ static switch_status_t chat_send(switch_event_t *message_event);
 
 static void launch_conference_record_thread(conference_obj_t *conference, char *path);
 static int launch_conference_video_bridge_thread(conference_member_t *member_a, conference_member_t *member_b);
-static void launch_conference_video_mirror_thread(conference_member_t *member_a);
 
 typedef switch_status_t (*conf_api_args_cmd_t) (conference_obj_t *, switch_stream_handle_t *, int, char **);
 typedef switch_status_t (*conf_api_member_cmd_t) (conference_member_t *, switch_stream_handle_t *, void *);
@@ -542,6 +542,8 @@ static switch_status_t conf_api_sub_undeaf(conference_member_t *member, switch_s
 static switch_status_t conference_add_event_data(conference_obj_t *conference, switch_event_t *event);
 static switch_status_t conference_add_event_member_data(conference_member_t *member, switch_event_t *event);
 static switch_status_t conf_api_sub_floor(conference_member_t *member, switch_stream_handle_t *stream, void *data);
+static switch_status_t conf_api_sub_vid_floor(conference_member_t *member, switch_stream_handle_t *stream, void *data);
+static switch_status_t conf_api_sub_clear_vid_floor(conference_obj_t *conference, switch_stream_handle_t *stream, void *data);
 static switch_status_t conf_api_sub_enforce_floor(conference_member_t *member, switch_stream_handle_t *stream, void *data);
 
 
@@ -1455,7 +1457,9 @@ static switch_status_t conference_add_member(conference_obj_t *conference, confe
 
 
                if (switch_channel_test_flag(channel, CF_VIDEO)) {
-                       switch_channel_clear_flag(channel, CF_VIDEO_ECHO);
+                       if (!switch_test_flag(conference, CFLAG_VIDEO_BRIDGE)) {
+                               switch_channel_clear_flag(channel, CF_VIDEO_ECHO);
+                       }
                        /* Tell the channel to request a fresh vid frame */
                        switch_core_session_refresh_video(member->session);
                }
@@ -1572,18 +1576,94 @@ static switch_status_t conference_add_member(conference_obj_t *conference, confe
        return status;
 }
 
+static void conference_set_video_floor_holder(conference_obj_t *conference, conference_member_t *member)
+{
+       switch_event_t *event;
+       int old_id = 0;
+
+       if (switch_test_flag(conference, CFLAG_VIDEO_BRIDGE)) {
+               return;
+       }
+       
+       if (conference->video_floor_holder) {
+               if (conference->video_floor_holder == member) {
+                       return;
+               } else {
+                       old_id = conference->video_floor_holder->id;
+                       switch_channel_clear_flag(conference->video_floor_holder->channel, CF_VIDEO_PASSIVE);
+                       switch_core_session_refresh_video(conference->video_floor_holder->session);
+               }
+       }
+
+       if ((conference->video_floor_holder = member)) {
+               switch_channel_set_flag(member->channel, CF_VIDEO_PASSIVE);
+               switch_core_session_refresh_video(conference->video_floor_holder->session);
+               switch_set_flag(conference, CFLAG_FLOOR_CHANGE);
+       }
+
+       if (old_id > -1 && test_eflag(conference, EFLAG_FLOOR_CHANGE)) {
+               switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT);
+               conference_add_event_data(conference, event);
+               switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "video-floor-change");
+               if (old_id) {
+                       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Old-ID", "%d", old_id);
+               } else {
+                       switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Old-ID", "none");
+               }
+               if (conference->video_floor_holder) {
+                       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "New-ID", "%d", conference->video_floor_holder->id);
+               } else {
+                       switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "New-ID", "none");
+               }
+               switch_event_fire(&event);
+       }
+
+}
+
 static void conference_set_floor_holder(conference_obj_t *conference, conference_member_t *member)
 {
+       switch_event_t *event;
+       int old_id = 0;
 
-       if (conference->floor_holder && conference->floor_holder != member) {
-               switch_channel_clear_flag(conference->floor_holder->channel, CF_VIDEO_PASSIVE);
+       if (conference->floor_holder) {
+               if (conference->floor_holder == member) {
+                       return;
+               } else {
+                       old_id = conference->floor_holder->id;
+                       if (!conference->video_floor_holder && !switch_test_flag(conference, CFLAG_VIDEO_BRIDGE)) {
+                               switch_channel_clear_flag(conference->floor_holder->channel, CF_VIDEO_PASSIVE);
+                               switch_core_session_refresh_video(conference->floor_holder->session);
+                       }
+               }
        }
 
        if ((conference->floor_holder = member)) {
-               switch_channel_set_flag(member->channel, CF_VIDEO_PASSIVE);
-               switch_core_session_refresh_video(conference->floor_holder->session);
+               if (!conference->video_floor_holder && !switch_test_flag(conference, CFLAG_VIDEO_BRIDGE)) {
+                       switch_channel_set_flag(member->channel, CF_VIDEO_PASSIVE);
+                       switch_core_session_refresh_video(conference->floor_holder->session);
+               }
                switch_set_flag(conference, CFLAG_FLOOR_CHANGE);
        }
+
+       if (old_id > -1 && test_eflag(conference, EFLAG_FLOOR_CHANGE)) {
+               switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT);
+               conference_add_event_data(conference, event);
+               switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "floor-change");
+               if (old_id) {
+                       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Old-ID", "%d", old_id);
+               } else {
+                       switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Old-ID", "none");
+               }
+
+               if (conference->floor_holder) {
+                       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "New-ID", "%d", conference->floor_holder->id);
+               } else {
+                       switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "New-ID", "none");
+               }
+
+               switch_event_fire(&event);
+       }
+
 }
 
 /* Gain exclusive access and remove the member from the list */
@@ -1669,18 +1749,12 @@ static switch_status_t conference_del_member(conference_obj_t *conference, confe
        }
 
        if (member == member->conference->floor_holder) {
-               //member->conference->floor_holder = NULL;
                conference_set_floor_holder(member->conference, NULL);
+       }
 
 
-               if (test_eflag(conference, EFLAG_FLOOR_CHANGE)) {
-                       switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT);
-                       conference_add_event_data(conference, event); 
-                       switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "floor-change");
-                       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Old-ID", "%d", member->id);
-                       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "New-ID", "none");
-                       switch_event_fire(&event);
-               }
+       if (member == member->conference->video_floor_holder) {
+               conference_set_video_floor_holder(member->conference, NULL);
        }
 
        member->conference = NULL;
@@ -1758,6 +1832,8 @@ static void *SWITCH_THREAD_FUNC conference_video_bridge_thread_run(switch_thread
        switch_thread_rwlock_rdlock(vh->member_b->rwlock);
        
 
+       switch_channel_set_flag(channel_a, CF_VIDEO_PASSIVE);
+
        /* Acquire locks for both sessions so the helper object and member structures don't get destroyed before we exit */
        switch_core_session_read_lock(session_a);
        switch_core_session_read_lock(session_b);
@@ -1786,6 +1862,7 @@ static void *SWITCH_THREAD_FUNC conference_video_bridge_thread_run(switch_thread
                        }
                }
        }
+       switch_channel_clear_flag(channel_a, CF_VIDEO_PASSIVE);
 
        switch_thread_rwlock_unlock(vh->member_b->rwlock);
        switch_thread_rwlock_unlock(vh->member_a->rwlock);
@@ -1802,62 +1879,6 @@ static void *SWITCH_THREAD_FUNC conference_video_bridge_thread_run(switch_thread
 }
 
 
-
-static void *SWITCH_THREAD_FUNC conference_video_mirror_thread_run(switch_thread_t *thread, void *obj)
-{
-       struct vid_helper *vh = obj;
-       switch_core_session_t *session_a = vh->member_a->session;
-       switch_channel_t *channel_a = switch_core_session_get_channel(session_a);
-       switch_status_t status;
-       switch_frame_t *read_frame;
-       conference_obj_t *conference = vh->member_a->conference;
-       switch_core_session_message_t msg = { 0 };
-       
-       switch_thread_rwlock_rdlock(conference->rwlock);
-       switch_thread_rwlock_rdlock(vh->member_a->rwlock);
-
-       /* Acquire locks for both sessions so the helper object and member structures don't get destroyed before we exit */
-       switch_core_session_read_lock(session_a);
-
-       /* Tell the channel to request a fresh vid frame */
-       msg.from = __FILE__;
-       msg.message_id = SWITCH_MESSAGE_INDICATE_VIDEO_REFRESH_REQ;
-       switch_core_session_receive_message(session_a, &msg);
-       
-       vh->up = 1;
-       while (vh->up > 0 && switch_test_flag(vh->member_a, MFLAG_RUNNING) && 
-                  switch_channel_ready(channel_a))  {
-
-               if (vh->up == 1) {
-                       status = switch_core_session_read_video_frame(session_a, &read_frame, SWITCH_IO_FLAG_NONE, 0);
-                       if (!SWITCH_READ_ACCEPTABLE(status)) {
-                               break;
-                       }
-
-                       if (!switch_test_flag(read_frame, SFF_CNG)) {
-                               if (switch_core_session_write_video_frame(session_a, read_frame, SWITCH_IO_FLAG_NONE, 0) != SWITCH_STATUS_SUCCESS) {
-                                       break;
-                               }
-                       }
-               } else {
-                       switch_yield(100000);
-               }
-       }
-
-
-       switch_thread_rwlock_unlock(vh->member_a->rwlock);
-
-       switch_core_session_rwunlock(session_a);
-
-       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s video mirror thread ended.\n", switch_channel_get_name(channel_a));
-
-       switch_thread_rwlock_unlock(conference->rwlock);
-       
-       vh->up = 0;
-       return NULL;
-}
-
-
 /* Main video monitor thread (1 per distinct conference room) */
 static void *SWITCH_THREAD_FUNC conference_video_thread_run(switch_thread_t *thread, void *obj)
 {
@@ -1869,6 +1890,7 @@ static void *SWITCH_THREAD_FUNC conference_video_thread_run(switch_thread_t *thr
        int yield = 0;
        switch_core_session_t *session;
        char buf[65536];
+       conference_member_t *floor_holder = NULL;
 
        conference->video_running = 1;
        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Video thread started for conference %s\n", conference->name);
@@ -1881,17 +1903,24 @@ static void *SWITCH_THREAD_FUNC conference_video_thread_run(switch_thread_t *thr
 
                switch_mutex_lock(conference->mutex);
 
-               if (!conference->floor_holder) {
+               if (conference->video_floor_holder) {
+                       floor_holder = conference->video_floor_holder;
+               } else {
+                       floor_holder = conference->floor_holder;
+               }
+
+
+               if (!floor_holder) {
                        yield = 100000;
                        goto do_continue;
                }
 
-               if (!switch_channel_test_flag(switch_core_session_get_channel(conference->floor_holder->session), CF_VIDEO)) {
+               if (!switch_channel_test_flag(switch_core_session_get_channel(floor_holder->session), CF_VIDEO)) {
                        yield = 100000;
                        goto do_continue;
                }
 
-               session = conference->floor_holder->session;
+               session = floor_holder->session;
 
                if ((status = switch_core_session_read_lock(session)) == SWITCH_STATUS_SUCCESS) {
                        switch_mutex_unlock(conference->mutex);
@@ -2070,48 +2099,9 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v
                        switch_mutex_unlock(imember->audio_in_mutex);
                }
                
-
-
                if (floor_holder != conference->floor_holder) {
-                       switch_event_t *event = NULL;
-
-                       if (test_eflag(conference, EFLAG_FLOOR_CHANGE)) {
-                               switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT);
-
-                               switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "floor-change");
-
-                               if (floor_holder) {
-                                       conference_add_event_member_data(floor_holder, event); 
-                                       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "New-ID", "%d", floor_holder->id);
-                               } else {
-                                       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "New-ID", "none");
-                               }
-
-                               if (conference->floor_holder) {
-                                       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Old-ID", "%d", conference->floor_holder->id);
-                               } else {
-                                       switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Old-ID", "none");
-                               }
-
-                               switch_event_fire(&event);
-                       }
-
-                       if (floor_holder) {
-                               switch_channel_t *floor_channel = switch_core_session_get_channel(floor_holder->session);
-                               if (switch_channel_test_flag(floor_channel, CF_VIDEO)) {
-                                       switch_core_session_message_t msg = { 0 };
-
-                                       msg.from = __FILE__;
-                                       msg.message_id = SWITCH_MESSAGE_INDICATE_VIDEO_REFRESH_REQ;
-                       
-                                       switch_core_session_receive_message(floor_holder->session, &msg);
-                               }
-                       }
-                       
-                       //conference->floor_holder = floor_holder;
                        conference_set_floor_holder(conference, floor_holder);
                }
-               
 
                if (conference->perpetual_sound && !conference->async_fnode) {
                        conference_play_file(conference, conference->perpetual_sound, CONF_DEFAULT_LEADIN, NULL, 1);
@@ -2187,16 +2177,6 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v
                                } else {
                                        conference->mh.up = -1;
                                }
-                       } else if (conference->vh[0].up == 0 &&
-                                          conference->vh[1].up == 0 &&
-                                          conference->mh.up == 0 &&
-                                          video_bridge_members[0] &&
-                                          !video_bridge_members[1] &&
-                                          switch_test_flag(video_bridge_members[0], MFLAG_RUNNING) && 
-                                          switch_channel_ready(switch_core_session_get_channel(video_bridge_members[0]->session))
-                                          ) {
-                               
-                               launch_conference_video_mirror_thread(video_bridge_members[0]);
                        }
                }
 
@@ -2573,6 +2553,20 @@ static void conference_loop_fn_floor_toggle(conference_member_t *member, caller_
        conf_api_sub_floor(member, NULL, NULL);
 }
 
+static void conference_loop_fn_vid_floor_toggle(conference_member_t *member, caller_control_action_t *action)
+{
+       if (member == NULL) return;
+
+       conf_api_sub_vid_floor(member, NULL, NULL);
+}
+
+static void conference_loop_fn_vid_floor_force(conference_member_t *member, caller_control_action_t *action)
+{
+       if (member == NULL) return;
+
+       conf_api_sub_vid_floor(member, NULL, "force");
+}
+
 static void conference_loop_fn_enforce_floor(conference_member_t *member, caller_control_action_t *action)
 {
        if (member == NULL) return;
@@ -5242,7 +5236,6 @@ static switch_status_t conf_api_sub_list(conference_obj_t *conference, switch_st
 
 static switch_status_t conf_api_sub_floor(conference_member_t *member, switch_stream_handle_t *stream, void *data)
 {
-       switch_event_t *event;
 
        if (member == NULL)
                return SWITCH_STATUS_GENERR;
@@ -5250,38 +5243,14 @@ static switch_status_t conf_api_sub_floor(conference_member_t *member, switch_st
        switch_mutex_lock(member->conference->mutex);
 
        if (member->conference->floor_holder == member) {
-               //member->conference->floor_holder = NULL;
                conference_set_floor_holder(member->conference, NULL);
-               if (test_eflag(member->conference, EFLAG_FLOOR_CHANGE)) {
-                       switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT);
-                       conference_add_event_data(member->conference, event);
-                       switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "floor-change");
-                       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Old-ID", "%d", member->id);
-                       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "New-ID", "none");
-                       switch_event_fire(&event);
-                       if (stream != NULL) {
-                               stream->write_function(stream, "OK floor none\n");
-                       }
+               if (stream != NULL) {
+                       stream->write_function(stream, "OK floor none\n");
                }
        } else if (member->conference->floor_holder == NULL) {
-               //member->conference->floor_holder = member;
                conference_set_floor_holder(member->conference, member);
-               if (test_eflag(member->conference, EFLAG_FLOOR_CHANGE)) {
-                       switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT);
-                       conference_add_event_data(member->conference, event);
-                       switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "floor-change");
-                       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Old-ID", "none");
-                       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "New-ID", "%d", member->id);
-                       switch_event_fire(&event);
-                       if (stream != NULL) {
-                               stream->write_function(stream, "OK floor %u\n", member->id);
-                       }
-
-                       if (switch_core_session_read_lock(member->session) == SWITCH_STATUS_SUCCESS) {
-                               /* Tell the channel to request a fresh vid frame */
-                               switch_channel_set_flag(switch_core_session_get_channel(member->session), CF_VIDEO_REFRESH_REQ);
-                               switch_core_session_rwunlock(member->session);
-                       }
+               if (stream != NULL) {
+                       stream->write_function(stream, "OK floor %u\n", member->id);
                }
        } else {
                if (stream != NULL) {
@@ -5294,39 +5263,85 @@ static switch_status_t conf_api_sub_floor(conference_member_t *member, switch_st
        return SWITCH_STATUS_SUCCESS;
 }
 
-static switch_status_t conf_api_sub_enforce_floor(conference_member_t *member, switch_stream_handle_t *stream, void *data)
+static switch_status_t conf_api_sub_clear_vid_floor(conference_obj_t *conference, switch_stream_handle_t *stream, void *data)
 {
-       switch_event_t *event;
+
+       if (switch_test_flag(conference, CFLAG_VIDEO_BRIDGE)) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, 
+                                                 "conference %s is in video bridge mode, this functionality is not compatible\n", conference->name);
+               return SWITCH_STATUS_FALSE;
+       }
+
+       switch_mutex_lock(conference->mutex);
+       conference_set_video_floor_holder(conference, NULL);
+       switch_mutex_unlock(conference->mutex);
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t conf_api_sub_vid_floor(conference_member_t *member, switch_stream_handle_t *stream, void *data)
+{
+       int force = 0;
 
        if (member == NULL)
                return SWITCH_STATUS_GENERR;
 
+       if (switch_test_flag(member->conference, CFLAG_VIDEO_BRIDGE)) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, 
+                                                 "conference %s is in video bridge mode, this functionality is not compatible\n", member->conference->name);
+               return SWITCH_STATUS_FALSE;
+       }
+
        switch_mutex_lock(member->conference->mutex);
 
-       if (member->conference->floor_holder != member) {
-               conference_member_t *old_member = member->conference->floor_holder;
-               //member->conference->floor_holder = member;
-               conference_set_floor_holder(member->conference, member);
+       if (data && switch_stristr("force", (char *) data)) {
+               force = 1;
+       }
+
+       if (!force && member->conference->video_floor_holder == member) {
+               conference_set_video_floor_holder(member->conference, NULL);
+               if (stream == NULL) {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "conference %s OK floor none\n", member->conference->name);
+               } else {
+                       stream->write_function(stream, "OK floor none\n");
+               } 
+               
+       } else if (force || member->conference->video_floor_holder == NULL) {
+               conference_set_video_floor_holder(member->conference, member);
                if (test_eflag(member->conference, EFLAG_FLOOR_CHANGE)) {
-                       switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT);
-                       conference_add_event_data(member->conference, event);
-                       switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "floor-change");
-                       if (old_member == NULL) {
-                               switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Old-ID", "none");
+                       if (stream == NULL) {
+                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "conference %s OK floor %d %s\n", 
+                                                                 member->conference->name, member->id, switch_channel_get_name(member->channel));
                        } else {
-                               switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Old-ID", "%d", old_member->id);
-                       }
-                       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "New-ID", "%d", member->id);
-                       switch_event_fire(&event);
-                       if (stream != NULL) {
                                stream->write_function(stream, "OK floor %u\n", member->id);
                        }
                }
+       } else {
+               if (stream == NULL) {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "conference %s floor already held by %d %s\n", 
+                                                         member->conference->name, member->id, switch_channel_get_name(member->channel));
+               } else {
+                       stream->write_function(stream, "ERR floor is held by %u\n", member->conference->video_floor_holder->id);
+               }
+       }
 
-               if (switch_core_session_read_lock(member->session) == SWITCH_STATUS_SUCCESS) {
-                       /* Tell the channel to request a fresh vid frame */
-                       switch_channel_set_flag(switch_core_session_get_channel(member->session), CF_VIDEO_REFRESH_REQ);
-                       switch_core_session_rwunlock(member->session);
+       switch_mutex_unlock(member->conference->mutex);
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t conf_api_sub_enforce_floor(conference_member_t *member, switch_stream_handle_t *stream, void *data)
+{
+       if (member == NULL)
+               return SWITCH_STATUS_GENERR;
+
+       switch_mutex_lock(member->conference->mutex);
+
+       if (member->conference->floor_holder != member) {
+               conference_set_floor_holder(member->conference, member);
+
+               if (stream != NULL) {
+                       stream->write_function(stream, "OK floor %u\n", member->id);
                }
        }
 
@@ -6528,6 +6543,8 @@ static api_command_t conf_api_sub_commands[] = {
        {"get", (void_fn_t) & conf_api_sub_get, CONF_API_SUB_ARGS_SPLIT, "get", "<parameter-name>"},
        {"set", (void_fn_t) & conf_api_sub_set, CONF_API_SUB_ARGS_SPLIT, "set", "<max_members|sound_prefix|caller_id_name|caller_id_number|endconf_grace_time> <value>"},
        {"floor", (void_fn_t) & conf_api_sub_floor, CONF_API_SUB_MEMBER_TARGET, "floor", "<member_id|last>"},
+       {"vid-floor", (void_fn_t) & conf_api_sub_vid_floor, CONF_API_SUB_MEMBER_TARGET, "vid-floor", "<member_id|last> [force]"},
+       {"clear-vid-floor", (void_fn_t) & conf_api_sub_clear_vid_floor, CONF_API_SUB_ARGS_AS_ONE, "clear-vid-floor", ""},
        {"enforce_floor", (void_fn_t) & conf_api_sub_enforce_floor, CONF_API_SUB_MEMBER_TARGET, "enforce_floor", "<member_id|last>"},
 };
 
@@ -8035,19 +8052,6 @@ static int launch_conference_video_bridge_thread(conference_member_t *member_a,
 
 
 
-/* Create a video thread for the conference and launch it */
-static void launch_conference_video_mirror_thread(conference_member_t *member_a)
-{
-       conference_obj_t *conference = member_a->conference;
-       switch_memory_pool_t *pool = conference->pool;
-
-       memset(&conference->mh, 0, sizeof(conference->mh));
-
-       conference->mh.member_a = member_a;
-       
-       launch_thread_detached(conference_video_mirror_thread_run, pool, &conference->mh);
-}
-
 static void launch_conference_record_thread(conference_obj_t *conference, char *path)
 {
        switch_thread_t *thread;
@@ -9116,6 +9120,8 @@ static struct _mapping control_mappings[] = {
     {"transfer", conference_loop_fn_transfer},
     {"execute_application", conference_loop_fn_exec_app},
     {"floor", conference_loop_fn_floor_toggle},
+    {"vid-floor", conference_loop_fn_vid_floor_toggle},
+    {"vid-floor-force", conference_loop_fn_vid_floor_force},
     {"enforce_floor", conference_loop_fn_enforce_floor},
 };
 #define MAPPING_LEN (sizeof(control_mappings)/sizeof(control_mappings[0]))