diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index bb572f4e78..23f08f69b2 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -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_clear_vid_floor(conference_obj_t *conference, switch_stream_handle_t *stream, void *data) +{ + + 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 (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)) { + 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 { + 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); + } + } + + 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) { - switch_event_t *event; - if (member == NULL) return SWITCH_STATUS_GENERR; 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 (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"); - } 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); - } - } - 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); } } @@ -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", ""}, {"set", (void_fn_t) & conf_api_sub_set, CONF_API_SUB_ARGS_SPLIT, "set", " "}, {"floor", (void_fn_t) & conf_api_sub_floor, CONF_API_SUB_MEMBER_TARGET, "floor", ""}, + {"vid-floor", (void_fn_t) & conf_api_sub_vid_floor, CONF_API_SUB_MEMBER_TARGET, "vid-floor", " [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", ""}, }; @@ -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]))