From c70f4a201868eed3155e4c6e6ecb7583c1a37c1e Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Thu, 31 Dec 2009 20:08:57 +0000 Subject: [PATCH] nut 'n honey git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@16110 d0543943-73ff-0310-b7d9-9358b9ac24b2 --- .../mod_conference/mod_conference.c | 278 ++++++++++++------ 1 file changed, 189 insertions(+), 89 deletions(-) diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index 6588ada658..750404d1bd 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -175,7 +175,8 @@ typedef enum { CFLAG_ANSWERED = (1 << 5), CFLAG_BRIDGE_TO = (1 << 6), CFLAG_WAIT_MOD = (1 << 7), - CFLAG_VID_FLOOR = (1 << 8) + CFLAG_VID_FLOOR = (1 << 8), + CFLAG_WASTE_BANDWIDTH = (1 << 9) } conf_flag_t; typedef enum { @@ -297,6 +298,7 @@ typedef struct conference_obj { uint32_t eflags; uint32_t verbose_events; int end_count; + uint32_t relationship_total; } conference_obj_t; /* Relationship with another member */ @@ -571,7 +573,11 @@ static conference_relationship_t *member_add_relationship(conference_member_t *m rel->id = id; + lock_member(member); + switch_mutex_lock(member->conference->member_mutex); + member->conference->relationship_total++; + switch_mutex_unlock(member->conference->member_mutex); rel->next = member->relationships; member->relationships = rel; unlock_member(member); @@ -599,6 +605,11 @@ static switch_status_t member_del_relationship(conference_member_t *member, uint } else { member->relationships = rel->next; } + + switch_mutex_lock(member->conference->member_mutex); + member->conference->relationship_total--; + switch_mutex_unlock(member->conference->member_mutex); + } last = rel; } @@ -970,6 +981,9 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v switch_event_t *event; uint8_t *file_frame; uint8_t *async_file_frame; + int16_t *bptr; + int x; + int32_t z = 0; file_frame = switch_core_alloc(conference->pool, SWITCH_RECOMMENDED_BUFFER_SIZE); async_file_frame = switch_core_alloc(conference->pool, SWITCH_RECOMMENDED_BUFFER_SIZE); @@ -1111,7 +1125,7 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v for (x = 0; x < file_sample_len; x++) { int32_t z; - int16_t *bptr, *muxed; + int16_t *muxed; muxed = (int16_t *) file_frame; bptr = (int16_t *) async_file_frame; @@ -1127,99 +1141,183 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v } } - if (ready || has_file_data) { - /* Build a muxed frame for every member that contains the mixed audio of everyone else */ + if (switch_test_flag(conference, CFLAG_WASTE_BANDWIDTH) && !has_file_data) { + file_sample_len = bytes / 2; - for (omember = conference->members; omember; omember = omember->next) { - if (has_file_data && file_sample_len) { - uint32_t sample_bytes = file_sample_len * 2; - memcpy(omember->mux_frame, file_frame, sample_bytes); - if (sample_bytes < bytes) { - if (conference->comfort_noise_level) { - switch_generate_sln_silence((int16_t *) omember->mux_frame + sample_bytes, - (bytes - sample_bytes) / 2, conference->comfort_noise_level); - } else { - memset(omember->mux_frame + sample_bytes, 255, bytes - sample_bytes); - } - } - } else { - if (conference->comfort_noise_level) { - switch_generate_sln_silence((int16_t *) omember->mux_frame, bytes / 2, conference->comfort_noise_level); - } else { - memset(omember->mux_frame, 255, bytes); - } - } - for (imember = conference->members; imember; imember = imember->next) { - uint32_t x; - int16_t *bptr, *muxed; - - if (imember == omember || !imember->read) { - /* Don't add audio from yourself or if you didn't read any */ - continue; - } - - /* If they are not supposed to talk to us then don't let them */ - if (omember->relationships) { - conference_relationship_t *rel; - - if ((rel = member_get_relationship(omember, imember))) { - if (!switch_test_flag(rel, RFLAG_CAN_HEAR)) { - continue; - } - } - } - - /* If we are not supposed to hear them then don't let it happen */ - if (imember->relationships) { - conference_relationship_t *rel; - - if ((rel = member_get_relationship(imember, omember))) { - if (!switch_test_flag(rel, RFLAG_CAN_SPEAK)) { - continue; - } - } - } -#if 0 - if (nt && conference->not_talking_buf_len && !switch_test_flag(omember, MFLAG_HAS_AUDIO)) { - memcpy(omember->mux_frame, conference->not_talking_buf, conference->not_talking_buf_len); - continue; - } -#endif - bptr = (int16_t *) imember->frame; - muxed = (int16_t *) omember->mux_frame; - - for (x = 0; x < imember->read / 2; x++) { - int32_t z = muxed[x] + bptr[x]; - switch_normalize_to_16bit(z); - muxed[x] = (int16_t) z; - } -#if 0 - if (total - ready > 1) { - conference->not_talking_buf_len = imember->read; - if (!conference->not_talking_buf) { - conference->not_talking_buf = switch_core_alloc(conference->pool, imember->read + 128); - } - memcpy(conference->not_talking_buf, muxed, conference->not_talking_buf_len); - nt++; - } -#endif - } + if (conference->comfort_noise_level) { + switch_generate_sln_silence((int16_t *) file_frame, file_sample_len, conference->comfort_noise_level); + } else { + memset(file_frame, 255, bytes); } - if (bytes) { - /* Go back and write each member his dedicated copy of the audio frame that does not contain his own audio. */ - for (imember = conference->members; imember; imember = imember->next) { - if (switch_test_flag(imember, MFLAG_RUNNING)) { - switch_size_t ok = 1; - switch_mutex_lock(imember->audio_out_mutex); - ok = switch_buffer_write(imember->mux_buffer, imember->mux_frame, bytes); - switch_mutex_unlock(imember->audio_out_mutex); - if (!ok) { - goto end; + has_file_data = 1; + } + + if (!conference->relationship_total) { + /* If there are no relationships meaning (user x can specificly not speal to and/or hear user y), use a more efficient muxing technique. */ + if (ready || has_file_data) { + /* Use more bits in the main_frame to preserve the exact sum of the audio samples. */ + int main_frame[SWITCH_RECOMMENDED_BUFFER_SIZE / 2] = { 0 }; + int16_t write_frame[SWITCH_RECOMMENDED_BUFFER_SIZE / 2] = { 0 }; + + + /* Init the main frame with file data if there is any. */ + bptr = (int16_t *) file_frame; + if (has_file_data && file_sample_len) { + for (x = 0; x < bytes / 2; x++) { + if (x <= file_sample_len) { + main_frame[x] = (int32_t) bptr[x]; + } else { + main_frame[x] = 255; + } + } + } + + /* Copy audio from every member known to be producing audio into the main frame. */ + for (omember = conference->members; omember; omember = omember->next) { + if (!(switch_test_flag(omember, MFLAG_RUNNING) && switch_test_flag(omember, MFLAG_HAS_AUDIO))) { + continue; + } + bptr = (int16_t *) omember->frame; + for (x = 0; x < omember->read / 2; x++) { + main_frame[x] += (int32_t) bptr[x]; + } + } + + /* Create write frame once per member who is not deaf for each sample in the main frame + check if our audio is involved and if so, subtract it from the sample so we don't hear ourselves. + Since main frame was 32 bit int, we did not lose any detail, now that we have to convert to 16 bit we can + cut it off at the min and max range if need be and write the frame to the output buffer. + */ + for (omember = conference->members; omember; omember = omember->next) { + switch_size_t ok = 1; + + if (!switch_test_flag(omember, MFLAG_RUNNING)) { + continue; + } + + if (!switch_test_flag(omember, MFLAG_CAN_HEAR) && !switch_test_flag(omember, MFLAG_WASTE_BANDWIDTH) + && !switch_test_flag(conference, CFLAG_WASTE_BANDWIDTH)) { + continue; + } + + bptr = (int16_t *) omember->frame; + for (x = 0; x < bytes / 2; x++) { + z = main_frame[x]; + /* bptr[x] represents my own contribution to this audio sample */ + if (switch_test_flag(omember, MFLAG_HAS_AUDIO) && x <= omember->read / 2) { + z -= (int32_t) bptr[x]; + } + /* Now we can convert to 16 bit.*/ + switch_normalize_to_16bit(z); + write_frame[x] = (int16_t) z; + } + + switch_mutex_lock(omember->audio_out_mutex); + ok = switch_buffer_write(omember->mux_buffer, write_frame, bytes); + switch_mutex_unlock(omember->audio_out_mutex); + + if (!ok) { + goto end; + } + } + + } + } else { + if (ready || has_file_data) { + /* Build a muxed frame for every member that contains the mixed audio of everyone else */ + + for (omember = conference->members; omember; omember = omember->next) { + if (has_file_data && file_sample_len) { + uint32_t sample_bytes = file_sample_len * 2; + memcpy(omember->mux_frame, file_frame, sample_bytes); + if (sample_bytes < bytes) { + if (conference->comfort_noise_level) { + switch_generate_sln_silence((int16_t *) omember->mux_frame + sample_bytes, + (bytes - sample_bytes) / 2, conference->comfort_noise_level); + } else { + memset(omember->mux_frame + sample_bytes, 255, bytes - sample_bytes); + } + } + } else { + if (conference->comfort_noise_level) { + switch_generate_sln_silence((int16_t *) omember->mux_frame, bytes / 2, conference->comfort_noise_level); + } else { + memset(omember->mux_frame, 255, bytes); + } + } + for (imember = conference->members; imember; imember = imember->next) { + uint32_t x; + int16_t *muxed; + + if (imember == omember || !imember->read) { + /* Don't add audio from yourself or if you didn't read any */ + continue; + } + + /* If they are not supposed to talk to us then don't let them */ + if (omember->relationships) { + conference_relationship_t *rel; + + if ((rel = member_get_relationship(omember, imember))) { + if (!switch_test_flag(rel, RFLAG_CAN_HEAR)) { + continue; + } + } + } + + /* If we are not supposed to hear them then don't let it happen */ + if (imember->relationships) { + conference_relationship_t *rel; + + if ((rel = member_get_relationship(imember, omember))) { + if (!switch_test_flag(rel, RFLAG_CAN_SPEAK)) { + continue; + } + } + } +#if 0 + if (nt && conference->not_talking_buf_len && !switch_test_flag(omember, MFLAG_HAS_AUDIO)) { + memcpy(omember->mux_frame, conference->not_talking_buf, conference->not_talking_buf_len); + continue; + } +#endif + bptr = (int16_t *) imember->frame; + muxed = (int16_t *) omember->mux_frame; + + for (x = 0; x < imember->read / 2; x++) { + int32_t z = muxed[x] + bptr[x]; + switch_normalize_to_16bit(z); + muxed[x] = (int16_t) z; + } +#if 0 + if (total - ready > 1) { + conference->not_talking_buf_len = imember->read; + if (!conference->not_talking_buf) { + conference->not_talking_buf = switch_core_alloc(conference->pool, imember->read + 128); + } + memcpy(conference->not_talking_buf, muxed, conference->not_talking_buf_len); + nt++; + } +#endif + } + } + if (bytes) { + /* Go back and write each member his dedicated copy of the audio frame that does not contain his own audio. */ + for (imember = conference->members; imember; imember = imember->next) { + if (switch_test_flag(imember, MFLAG_RUNNING)) { + switch_size_t ok = 1; + switch_mutex_lock(imember->audio_out_mutex); + ok = switch_buffer_write(imember->mux_buffer, imember->mux_frame, bytes); + switch_mutex_unlock(imember->audio_out_mutex); + if (!ok) { + goto end; + } } } } } } + if (conference->async_fnode && conference->async_fnode->done) { switch_memory_pool_t *pool; switch_core_file_close(&conference->async_fnode->fh); @@ -2367,7 +2465,7 @@ static void conference_loop_output(conference_member_t *member) } switch_mutex_unlock(member->audio_out_mutex); - } else { + } else if (!switch_test_flag(member->conference, CFLAG_WASTE_BANDWIDTH)) { if (switch_test_flag(member, MFLAG_WASTE_BANDWIDTH)) { if (member->conference->comfort_noise_level) { switch_generate_sln_silence(write_frame.data, samples, member->conference->comfort_noise_level); @@ -4800,6 +4898,8 @@ static void set_cflags(const char *flags, uint32_t *f) *f |= CFLAG_WAIT_MOD; } else if (!strcasecmp(argv[i], "video-floor-only")) { *f |= CFLAG_VID_FLOOR; + } else if (!strcasecmp(argv[i], "waste-bandwidth")) { + *f |= CFLAG_WASTE_BANDWIDTH; } }