mod_conference add vid-floor command to branch off the floor for video on demand and force it to a particular member vid-floor can be api or bound to dtmf but who gets the floor must toggle it off before someone else can have it. vid-floor-force (vid-floor <member> force)can be clobbered at any time

This commit is contained in:
Anthony Minessale 2013-07-15 11:07:56 -05:00
parent 51d3282d06
commit e46050d487
1 changed files with 194 additions and 188 deletions

View 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_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", "<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]))