From 4c13e7c095cc47e43f225712b6dc172e1b9a4d5d Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Fri, 18 Nov 2011 12:43:59 -0600 Subject: [PATCH] fix crash in video-bridge mode --- .../mod_conference/mod_conference.c | 158 +++++++++++------- 1 file changed, 102 insertions(+), 56 deletions(-) diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index 65a8523b65..09032c4cb9 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -237,6 +237,12 @@ typedef struct conf_xml_cfg { switch_xml_t controls; } conf_xml_cfg_t; +struct vid_helper { + conference_member_t *member_a; + conference_member_t *member_b; + int up; +}; + /* Conference Object */ typedef struct conference_obj { char *name; @@ -322,6 +328,7 @@ typedef struct conference_obj { switch_time_t start_time; switch_time_t end_time; char *log_dir; + struct vid_helper vh[2]; } conference_obj_t; /* Relationship with another member */ @@ -1140,50 +1147,52 @@ static switch_status_t conference_del_member(conference_obj_t *conference, confe return status; } -struct vid_helper { - conference_member_t *member_a; - conference_member_t *member_b; - int up; -}; - /* Thread bridging video between two members, there will be two threads if video briding is used */ static void *SWITCH_THREAD_FUNC conference_video_bridge_thread_run(switch_thread_t *thread, void *obj) { struct vid_helper *vh = obj; - switch_channel_t *channel_a = switch_core_session_get_channel(vh->member_a->session); - switch_channel_t *channel_b = switch_core_session_get_channel(vh->member_b->session); + switch_core_session_t *session_a = vh->member_a->session; + switch_core_session_t *session_b = vh->member_b->session; + switch_channel_t *channel_a = switch_core_session_get_channel(session_a); + switch_channel_t *channel_b = switch_core_session_get_channel(session_b); switch_status_t status; switch_frame_t *read_frame; + conference_obj_t *conference = vh->member_a->conference; + + switch_thread_rwlock_rdlock(conference->rwlock); + switch_thread_rwlock_rdlock(vh->member_a->rwlock); + switch_thread_rwlock_rdlock(vh->member_b->rwlock); + /* Acquire locks for both sessions so the helper object and member structures don't get destroyed before we exit */ - if (switch_core_session_read_lock(vh->member_a->session) != SWITCH_STATUS_SUCCESS) { - return NULL; - } + switch_core_session_read_lock(session_a); + switch_core_session_read_lock(session_b); - if (switch_core_session_read_lock(vh->member_b->session) != SWITCH_STATUS_SUCCESS) { - switch_core_session_rwunlock(vh->member_a->session); - return NULL; - } - vh->up = 1; - while (switch_test_flag(vh->member_a, MFLAG_RUNNING) && switch_test_flag(vh->member_b, MFLAG_RUNNING) && + while (vh->up == 1 && switch_test_flag(vh->member_a, MFLAG_RUNNING) && switch_test_flag(vh->member_b, MFLAG_RUNNING) && switch_channel_ready(channel_a) && switch_channel_ready(channel_b)) { - status = switch_core_session_read_video_frame(vh->member_a->session, &read_frame, SWITCH_IO_FLAG_NONE, 0); - if (!SWITCH_READ_ACCEPTABLE(status)) { + + 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_b, read_frame, SWITCH_IO_FLAG_NONE, 0) != SWITCH_STATUS_SUCCESS) { break; } - - if (!switch_test_flag(read_frame, SFF_CNG)) { - if (switch_core_session_write_video_frame(vh->member_b->session, read_frame, SWITCH_IO_FLAG_NONE, 0) != SWITCH_STATUS_SUCCESS) { - break; - } - } + } } - - switch_core_session_rwunlock(vh->member_a->session); - switch_core_session_rwunlock(vh->member_b->session); + + switch_thread_rwlock_unlock(vh->member_b->rwlock); + switch_thread_rwlock_unlock(vh->member_a->rwlock); + + switch_core_session_rwunlock(session_a); + switch_core_session_rwunlock(session_b); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s video thread ended.\n", switch_channel_get_name(channel_a)); + + switch_thread_rwlock_unlock(conference->rwlock); vh->up = 0; return NULL; @@ -1434,10 +1443,14 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v } - if (members_with_video && conference->video_running != 1) { - if (!switch_test_flag(conference, CFLAG_VIDEO_BRIDGE)) { - launch_conference_video_thread(conference); - } else if (video_bridge_members[0] && video_bridge_members[1]){ + if (members_with_video) { + if (conference->video_running != 1) { + if (!switch_test_flag(conference, CFLAG_VIDEO_BRIDGE)) { + launch_conference_video_thread(conference); + } + } + + if (conference->vh[0].up != 1 && conference->vh[1].up != 1 && video_bridge_members[0] && video_bridge_members[1]){ launch_conference_video_bridge_thread(video_bridge_members[0], video_bridge_members[1]); } } @@ -1741,6 +1754,18 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v switch_mutex_unlock(conference->member_mutex); switch_mutex_unlock(conference->mutex); + if (conference->vh[0].up == 1) { + conference->vh[0].up = -1; + } + + if (conference->vh[1].up == 1) { + conference->vh[1].up = -1; + } + + while (conference->vh[0].up || conference->vh[1].up) { + switch_cond_next(); + } + if (conference->video_running == 1) { conference->video_running = -1; while (conference->video_running) { @@ -5325,10 +5350,6 @@ SWITCH_STANDARD_API(conf_api_main) conference_obj_t *conference = NULL; if ((conference = conference_find(argv[0]))) { - if (switch_thread_rwlock_tryrdlock(conference->rwlock) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Read Lock Fail\n"); - goto done; - } if (argc >= 2) { conf_api_dispatch(conference, stream, argc, argv, cmd, 1); } else { @@ -5369,7 +5390,7 @@ SWITCH_STANDARD_API(conf_api_main) } } - done: + switch_safe_free(lbuf); return status; @@ -6112,6 +6133,7 @@ SWITCH_STANDARD_APP(conference_function) } if ((conference = conference_find(conf_name))) { + switch_thread_rwlock_unlock(conference->rwlock); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Conference %s already exists!\n", conf_name); goto done; } @@ -6203,19 +6225,21 @@ SWITCH_STANDARD_APP(conference_function) /* Indicate the conference is dynamic */ switch_set_flag_locked(conference, CFLAG_DYNAMIC); + /* acquire a read lock on the thread so it can't leave without us */ + if (switch_thread_rwlock_tryrdlock(conference->rwlock) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Read Lock Fail\n"); + goto done; + } + + rl++; + /* Start the conference thread for this conference */ launch_conference_thread(conference); } else { /* setup user variable */ switch_channel_set_variable(channel, "conference_name", conference->name); + rl++; } - /* acquire a read lock on the thread so it can't leave without us */ - if (switch_thread_rwlock_tryrdlock(conference->rwlock) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Read Lock Fail\n"); - goto done; - } - rl++; - if (zstr(dpin) && conference->pin) { dpin = conference->pin; } @@ -6543,19 +6567,33 @@ static void launch_conference_video_thread(conference_obj_t *conference) /* Create a video thread for the conference and launch it */ static void launch_conference_video_bridge_thread(conference_member_t *member_a, conference_member_t *member_b) { - switch_memory_pool_t *pool = member_a->conference->pool; - struct vid_helper *vh = switch_core_alloc(pool, 2 * sizeof *vh); - - vh[0].member_a = member_a; - vh[0].member_b = member_b; - - vh[1].member_a = member_b; - vh[1].member_b = member_a; - - launch_thread_detached(conference_video_bridge_thread_run, pool, &vh[0]); - launch_thread_detached(conference_video_bridge_thread_run, pool, &vh[1]); + conference_obj_t *conference = member_a->conference; + switch_memory_pool_t *pool = conference->pool; + int sanity = 10000; - member_a->conference->video_running = 1; + memset(conference->vh, 0, sizeof(conference->vh)); + + conference->vh[0].member_a = member_a; + conference->vh[0].member_b = member_b; + + conference->vh[1].member_a = member_b; + conference->vh[1].member_b = member_a; + + launch_thread_detached(conference_video_bridge_thread_run, pool, &conference->vh[0]); + launch_thread_detached(conference_video_bridge_thread_run, pool, &conference->vh[1]); + + while(!(conference->vh[0].up && conference->vh[1].up) && --sanity > 0) { + switch_cond_next(); + } + + if (conference->vh[0].up == 1 && conference->vh[1].up != 1) { + conference->vh[0].up = -1; + } + + if (conference->vh[1].up == 1 && conference->vh[0].up != 1) { + conference->vh[1].up = -1; + } + } static void launch_conference_record_thread(conference_obj_t *conference, char *path) @@ -6643,6 +6681,7 @@ static switch_status_t chat_send(switch_event_t *message_event) switch_core_chat_send_args(proto, CONF_CHAT_PROTO, to, hint && strchr(hint, '/') ? hint : from, "", stream.data, NULL, NULL); switch_safe_free(stream.data); + switch_thread_rwlock_unlock(conference->rwlock); return SWITCH_STATUS_SUCCESS; } @@ -6659,6 +6698,12 @@ static conference_obj_t *conference_find(char *name) conference = NULL; } } + if (conference) { + if (switch_thread_rwlock_tryrdlock(conference->rwlock) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Read Lock Fail\n"); + conference = NULL; + } + } switch_mutex_unlock(globals.hash_mutex); return conference; @@ -7260,6 +7305,7 @@ static void pres_event_handler(switch_event_t *event) switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call-direction", conference->count == 1 ? "outbound" : "inbound"); switch_event_fire(&event); } + switch_thread_rwlock_unlock(conference->rwlock); } else if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", CONF_CHAT_PROTO); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", conf_name);