diff options
Diffstat (limited to 'bridges/bridge_softmix.c')
-rw-r--r-- | bridges/bridge_softmix.c | 593 |
1 files changed, 382 insertions, 211 deletions
diff --git a/bridges/bridge_softmix.c b/bridges/bridge_softmix.c index 613601a1f..4583435a0 100644 --- a/bridges/bridge_softmix.c +++ b/bridges/bridge_softmix.c @@ -100,13 +100,15 @@ struct softmix_channel { struct ast_frame read_frame; /*! DSP for detecting silence */ struct ast_dsp *dsp; - /*! Bit used to indicate if a channel is talking or not. This affects how - * the channel's audio is mixed back to it. */ - int talking:1; - /*! Bit used to indicate that the channel provided audio for this mixing interval */ - int have_audio:1; - /*! Bit used to indicate that a frame is available to be written out to the channel */ - int have_frame:1; + /*! + * \brief TRUE if a channel is talking. + * + * \note This affects how the channel's audio is mixed back to + * it. + */ + unsigned int talking:1; + /*! TRUE if the channel provided audio for this mixing interval */ + unsigned int have_audio:1; /*! Buffer containing final mixed audio from all sources */ short final_buf[MAX_DATALEN]; /*! Buffer containing only the audio from the channel */ @@ -117,28 +119,36 @@ struct softmix_channel { struct softmix_bridge_data { struct ast_timer *timer; + /*! Lock for signaling the mixing thread. */ + ast_mutex_t lock; + /*! Condition, used if we need to wake up the mixing thread. */ + ast_cond_t cond; + /*! Thread handling the mixing */ + pthread_t thread; unsigned int internal_rate; unsigned int internal_mixing_interval; + /*! TRUE if the mixing thread should stop */ + unsigned int stop:1; }; struct softmix_stats { - /*! Each index represents a sample rate used above the internal rate. */ - unsigned int sample_rates[16]; - /*! Each index represents the number of channels using the same index in the sample_rates array. */ - unsigned int num_channels[16]; - /*! the number of channels above the internal sample rate */ - unsigned int num_above_internal_rate; - /*! the number of channels at the internal sample rate */ - unsigned int num_at_internal_rate; - /*! the absolute highest sample rate supported by any channel in the bridge */ - unsigned int highest_supported_rate; - /*! Is the sample rate locked by the bridge, if so what is that rate.*/ - unsigned int locked_rate; + /*! Each index represents a sample rate used above the internal rate. */ + unsigned int sample_rates[16]; + /*! Each index represents the number of channels using the same index in the sample_rates array. */ + unsigned int num_channels[16]; + /*! the number of channels above the internal sample rate */ + unsigned int num_above_internal_rate; + /*! the number of channels at the internal sample rate */ + unsigned int num_at_internal_rate; + /*! the absolute highest sample rate supported by any channel in the bridge */ + unsigned int highest_supported_rate; + /*! Is the sample rate locked by the bridge, if so what is that rate.*/ + unsigned int locked_rate; }; struct softmix_mixing_array { - int max_num_entries; - int used_entries; + unsigned int max_num_entries; + unsigned int used_entries; int16_t **buffers; }; @@ -213,7 +223,7 @@ static void softmix_translate_helper_change_rate(struct softmix_translate_helper /*! * \internal * \brief Get the next available audio on the softmix channel's read stream - * and determine if it should be mixed out or not on the write stream. + * and determine if it should be mixed out or not on the write stream. * * \retval pointer to buffer containing the exact number of samples requested on success. * \retval NULL if no samples are present @@ -295,54 +305,9 @@ static void softmix_translate_helper_cleanup(struct softmix_translate_helper *tr } } -static void softmix_bridge_data_destroy(void *obj) -{ - struct softmix_bridge_data *softmix_data = obj; - - if (softmix_data->timer) { - ast_timer_close(softmix_data->timer); - softmix_data->timer = NULL; - } -} - -/*! \brief Function called when a bridge is created */ -static int softmix_bridge_create(struct ast_bridge *bridge) -{ - struct softmix_bridge_data *softmix_data; - - if (!(softmix_data = ao2_alloc(sizeof(*softmix_data), softmix_bridge_data_destroy))) { - return -1; - } - if (!(softmix_data->timer = ast_timer_open())) { - ao2_ref(softmix_data, -1); - return -1; - } - - /* start at 8khz, let it grow from there */ - softmix_data->internal_rate = 8000; - softmix_data->internal_mixing_interval = DEFAULT_SOFTMIX_INTERVAL; - - bridge->bridge_pvt = softmix_data; - return 0; -} - -/*! \brief Function called when a bridge is destroyed */ -static int softmix_bridge_destroy(struct ast_bridge *bridge) -{ - struct softmix_bridge_data *softmix_data; - - softmix_data = bridge->bridge_pvt; - if (!softmix_data) { - return -1; - } - ao2_ref(softmix_data, -1); - bridge->bridge_pvt = NULL; - return 0; -} - static void set_softmix_bridge_data(int rate, int interval, struct ast_bridge_channel *bridge_channel, int reset) { - struct softmix_channel *sc = bridge_channel->bridge_pvt; + struct softmix_channel *sc = bridge_channel->tech_pvt; unsigned int channel_read_rate = ast_format_rate(ast_channel_rawreadformat(bridge_channel->chan)); ast_mutex_lock(&sc->lock); @@ -382,39 +347,89 @@ static void set_softmix_bridge_data(int rate, int interval, struct ast_bridge_ch ast_mutex_unlock(&sc->lock); } +/*! + * \internal + * \brief Poke the mixing thread in case it is waiting for an active channel. + * \since 12.0.0 + * + * \param softmix_data Bridge mixing data. + * + * \return Nothing + */ +static void softmix_poke_thread(struct softmix_bridge_data *softmix_data) +{ + ast_mutex_lock(&softmix_data->lock); + ast_cond_signal(&softmix_data->cond); + ast_mutex_unlock(&softmix_data->lock); +} + +/*! \brief Function called when a channel is unsuspended from the bridge */ +static void softmix_bridge_unsuspend(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel) +{ + if (bridge->tech_pvt) { + softmix_poke_thread(bridge->tech_pvt); + } +} + +/*! + * \internal + * \brief Indicate a source change to the channel. + * \since 12.0.0 + * + * \param bridge_channel Which channel source is changing. + * + * \return Nothing + */ +static void softmix_src_change(struct ast_bridge_channel *bridge_channel) +{ + ast_bridge_channel_queue_control_data(bridge_channel, AST_CONTROL_SRCCHANGE, NULL, 0); +} + /*! \brief Function called when a channel is joined into the bridge */ static int softmix_bridge_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel) { struct softmix_channel *sc; - struct softmix_bridge_data *softmix_data = bridge->bridge_pvt; + struct softmix_bridge_data *softmix_data; + + softmix_data = bridge->tech_pvt; + if (!softmix_data) { + return -1; + } /* Create a new softmix_channel structure and allocate various things on it */ if (!(sc = ast_calloc(1, sizeof(*sc)))) { return -1; } + softmix_src_change(bridge_channel); + /* Can't forget the lock */ ast_mutex_init(&sc->lock); /* Can't forget to record our pvt structure within the bridged channel structure */ - bridge_channel->bridge_pvt = sc; + bridge_channel->tech_pvt = sc; set_softmix_bridge_data(softmix_data->internal_rate, - softmix_data->internal_mixing_interval ? softmix_data->internal_mixing_interval : DEFAULT_SOFTMIX_INTERVAL, + softmix_data->internal_mixing_interval + ? softmix_data->internal_mixing_interval + : DEFAULT_SOFTMIX_INTERVAL, bridge_channel, 0); + softmix_poke_thread(softmix_data); return 0; } /*! \brief Function called when a channel leaves the bridge */ -static int softmix_bridge_leave(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel) +static void softmix_bridge_leave(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel) { - struct softmix_channel *sc = bridge_channel->bridge_pvt; + struct softmix_channel *sc = bridge_channel->tech_pvt; - if (!(bridge_channel->bridge_pvt)) { - return 0; + if (!sc) { + return; } - bridge_channel->bridge_pvt = NULL; + bridge_channel->tech_pvt = NULL; + + softmix_src_change(bridge_channel); /* Drop mutex lock */ ast_mutex_destroy(&sc->lock); @@ -427,111 +442,122 @@ static int softmix_bridge_leave(struct ast_bridge *bridge, struct ast_bridge_cha /* Eep! drop ourselves */ ast_free(sc); - - return 0; } /*! * \internal - * \brief If the bridging core passes DTMF to us, then they want it to be distributed out to all memebers. Do that here. + * \brief Pass the given frame to everyone else. + * \since 12.0.0 + * + * \param bridge What bridge to distribute frame. + * \param bridge_channel Channel to optionally not pass frame to. (NULL to pass to everyone) + * \param frame Frame to pass. + * + * \return Nothing */ -static void softmix_pass_dtmf(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame) +static void softmix_pass_everyone_else(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame) { - struct ast_bridge_channel *tmp; - AST_LIST_TRAVERSE(&bridge->channels, tmp, entry) { - if (tmp == bridge_channel) { + struct ast_bridge_channel *cur; + + AST_LIST_TRAVERSE(&bridge->channels, cur, entry) { + if (cur == bridge_channel) { continue; } - ast_write(tmp->chan, frame); + ast_bridge_channel_queue_frame(cur, frame); } } static void softmix_pass_video_top_priority(struct ast_bridge *bridge, struct ast_frame *frame) { - struct ast_bridge_channel *tmp; - AST_LIST_TRAVERSE(&bridge->channels, tmp, entry) { - if (tmp->suspended) { + struct ast_bridge_channel *cur; + + AST_LIST_TRAVERSE(&bridge->channels, cur, entry) { + if (cur->suspended) { continue; } - if (ast_bridge_is_video_src(bridge, tmp->chan) == 1) { - ast_write(tmp->chan, frame); + if (ast_bridge_is_video_src(bridge, cur->chan) == 1) { + ast_bridge_channel_queue_frame(cur, frame); break; } } } -static void softmix_pass_video_all(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame, int echo) +/*! + * \internal + * \brief Determine what to do with a video frame. + * \since 12.0.0 + * + * \param bridge Which bridge is getting the frame + * \param bridge_channel Which channel is writing the frame. + * \param frame What is being written. + * + * \return Nothing + */ +static void softmix_bridge_write_video(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame) { - struct ast_bridge_channel *tmp; - AST_LIST_TRAVERSE(&bridge->channels, tmp, entry) { - if (tmp->suspended) { - continue; + struct softmix_channel *sc; + int video_src_priority; + + /* Determine if the video frame should be distributed or not */ + switch (bridge->video_mode.mode) { + case AST_BRIDGE_VIDEO_MODE_NONE: + break; + case AST_BRIDGE_VIDEO_MODE_SINGLE_SRC: + video_src_priority = ast_bridge_is_video_src(bridge, bridge_channel->chan); + if (video_src_priority == 1) { + /* Pass to me and everyone else. */ + softmix_pass_everyone_else(bridge, NULL, frame); } - if ((tmp->chan == bridge_channel->chan) && !echo) { - continue; + break; + case AST_BRIDGE_VIDEO_MODE_TALKER_SRC: + sc = bridge_channel->tech_pvt; + ast_mutex_lock(&sc->lock); + ast_bridge_update_talker_src_video_mode(bridge, bridge_channel->chan, + sc->video_talker.energy_average, + ast_format_get_video_mark(&frame->subclass.format)); + ast_mutex_unlock(&sc->lock); + video_src_priority = ast_bridge_is_video_src(bridge, bridge_channel->chan); + if (video_src_priority == 1) { + int num_src = ast_bridge_number_video_src(bridge); + int echo = num_src > 1 ? 0 : 1; + + softmix_pass_everyone_else(bridge, echo ? NULL : bridge_channel, frame); + } else if (video_src_priority == 2) { + softmix_pass_video_top_priority(bridge, frame); } - ast_write(tmp->chan, frame); + break; } } -/*! \brief Function called when a channel writes a frame into the bridge */ -static enum ast_bridge_write_result softmix_bridge_write(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame) +/*! + * \internal + * \brief Determine what to do with a voice frame. + * \since 12.0.0 + * + * \param bridge Which bridge is getting the frame + * \param bridge_channel Which channel is writing the frame. + * \param frame What is being written. + * + * \return Nothing + */ +static void softmix_bridge_write_voice(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame) { - struct softmix_channel *sc = bridge_channel->bridge_pvt; - struct softmix_bridge_data *softmix_data = bridge->bridge_pvt; + struct softmix_channel *sc = bridge_channel->tech_pvt; + struct softmix_bridge_data *softmix_data = bridge->tech_pvt; int totalsilence = 0; int cur_energy = 0; int silence_threshold = bridge_channel->tech_args.silence_threshold ? bridge_channel->tech_args.silence_threshold : DEFAULT_SOFTMIX_SILENCE_THRESHOLD; char update_talking = -1; /* if this is set to 0 or 1, tell the bridge that the channel has started or stopped talking. */ - int res = AST_BRIDGE_WRITE_SUCCESS; - - /* Only accept audio frames, all others are unsupported */ - if (frame->frametype == AST_FRAME_DTMF_END || frame->frametype == AST_FRAME_DTMF_BEGIN) { - softmix_pass_dtmf(bridge, bridge_channel, frame); - goto bridge_write_cleanup; - } else if (frame->frametype != AST_FRAME_VOICE && frame->frametype != AST_FRAME_VIDEO) { - res = AST_BRIDGE_WRITE_UNSUPPORTED; - goto bridge_write_cleanup; - } else if (frame->datalen == 0) { - goto bridge_write_cleanup; - } - - /* Determine if this video frame should be distributed or not */ - if (frame->frametype == AST_FRAME_VIDEO) { - int num_src = ast_bridge_number_video_src(bridge); - int video_src_priority = ast_bridge_is_video_src(bridge, bridge_channel->chan); - - switch (bridge->video_mode.mode) { - case AST_BRIDGE_VIDEO_MODE_NONE: - break; - case AST_BRIDGE_VIDEO_MODE_SINGLE_SRC: - if (video_src_priority == 1) { - softmix_pass_video_all(bridge, bridge_channel, frame, 1); - } - break; - case AST_BRIDGE_VIDEO_MODE_TALKER_SRC: - ast_mutex_lock(&sc->lock); - ast_bridge_update_talker_src_video_mode(bridge, bridge_channel->chan, sc->video_talker.energy_average, ast_format_get_video_mark(&frame->subclass.format)); - ast_mutex_unlock(&sc->lock); - if (video_src_priority == 1) { - int echo = num_src > 1 ? 0 : 1; - softmix_pass_video_all(bridge, bridge_channel, frame, echo); - } else if (video_src_priority == 2) { - softmix_pass_video_top_priority(bridge, frame); - } - break; - } - goto bridge_write_cleanup; - } - /* If we made it here, we are going to write the frame into the conference */ + /* Write the frame into the conference */ ast_mutex_lock(&sc->lock); ast_dsp_silence_with_energy(sc->dsp, frame, &totalsilence, &cur_energy); if (bridge->video_mode.mode == AST_BRIDGE_VIDEO_MODE_TALKER_SRC) { int cur_slot = sc->video_talker.energy_history_cur_slot; + sc->video_talker.energy_accum -= sc->video_talker.energy_history[cur_slot]; sc->video_talker.energy_accum += cur_energy; sc->video_talker.energy_history[cur_slot] = cur_energy; @@ -568,50 +594,77 @@ static enum ast_bridge_write_result softmix_bridge_write(struct ast_bridge *brid ast_slinfactory_feed(&sc->factory, frame); } - /* If a frame is ready to be written out, do so */ - if (sc->have_frame) { - ast_write(bridge_channel->chan, &sc->write_frame); - sc->have_frame = 0; - } - /* Alllll done */ ast_mutex_unlock(&sc->lock); if (update_talking != -1) { - ast_bridge_notify_talking(bridge, bridge_channel, update_talking); + ast_bridge_notify_talking(bridge_channel, update_talking); } - - return res; - -bridge_write_cleanup: - /* Even though the frame is not being written into the conference because it is not audio, - * we should use this opportunity to check to see if a frame is ready to be written out from - * the conference to the channel. */ - ast_mutex_lock(&sc->lock); - if (sc->have_frame) { - ast_write(bridge_channel->chan, &sc->write_frame); - sc->have_frame = 0; - } - ast_mutex_unlock(&sc->lock); - - return res; } -/*! \brief Function called when the channel's thread is poked */ -static int softmix_bridge_poke(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel) +/*! + * \internal + * \brief Determine what to do with a control frame. + * \since 12.0.0 + * + * \param bridge Which bridge is getting the frame + * \param bridge_channel Which channel is writing the frame. + * \param frame What is being written. + * + * \return Nothing + */ +static void softmix_bridge_write_control(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame) { - struct softmix_channel *sc = bridge_channel->bridge_pvt; +/* BUGBUG need to look at channel roles to determine what to do with control frame. */ + /*! \todo BUGBUG softmix_bridge_write_control() not written */ +} - ast_mutex_lock(&sc->lock); +/*! + * \internal + * \brief Determine what to do with a frame written into the bridge. + * \since 12.0.0 + * + * \param bridge Which bridge is getting the frame + * \param bridge_channel Which channel is writing the frame. + * \param frame What is being written. + * + * \retval 0 on success + * \retval -1 on failure + * + * \note On entry, bridge is already locked. + */ +static int softmix_bridge_write(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame) +{ + int res = 0; - if (sc->have_frame) { - ast_write(bridge_channel->chan, &sc->write_frame); - sc->have_frame = 0; + if (!bridge->tech_pvt || !bridge_channel->tech_pvt) { + return -1; } - ast_mutex_unlock(&sc->lock); + switch (frame->frametype) { + case AST_FRAME_DTMF_BEGIN: + case AST_FRAME_DTMF_END: + softmix_pass_everyone_else(bridge, bridge_channel, frame); + break; + case AST_FRAME_VOICE: + softmix_bridge_write_voice(bridge, bridge_channel, frame); + break; + case AST_FRAME_VIDEO: + softmix_bridge_write_video(bridge, bridge_channel, frame); + break; + case AST_FRAME_CONTROL: + softmix_bridge_write_control(bridge, bridge_channel, frame); + break; + case AST_FRAME_BRIDGE_ACTION: + softmix_pass_everyone_else(bridge, bridge_channel, frame); + break; + default: + ast_debug(3, "Frame type %d unsupported\n", frame->frametype); + res = -1; + break; + } - return 0; + return res; } static void gather_softmix_stats(struct softmix_stats *stats, @@ -648,7 +701,7 @@ static void gather_softmix_stats(struct softmix_stats *stats, * \brief Analyse mixing statistics and change bridges internal rate * if necessary. * - * \retval 0, no changes to internal rate + * \retval 0, no changes to internal rate * \ratval 1, internal rate was changed, update all the channels on the next mixing iteration. */ static unsigned int analyse_softmix_stats(struct softmix_stats *stats, struct softmix_bridge_data *softmix_data) @@ -665,7 +718,8 @@ static unsigned int analyse_softmix_stats(struct softmix_stats *stats, struct so * from the current rate we are using. */ if (softmix_data->internal_rate != stats->locked_rate) { softmix_data->internal_rate = stats->locked_rate; - ast_debug(1, " Bridge is locked in at sample rate %d\n", softmix_data->internal_rate); + ast_debug(1, "Bridge is locked in at sample rate %d\n", + softmix_data->internal_rate); return 1; } } else if (stats->num_above_internal_rate >= 2) { @@ -704,13 +758,15 @@ static unsigned int analyse_softmix_stats(struct softmix_stats *stats, struct so } } - ast_debug(1, " Bridge changed from %d To %d\n", softmix_data->internal_rate, best_rate); + ast_debug(1, "Bridge changed from %d To %d\n", + softmix_data->internal_rate, best_rate); softmix_data->internal_rate = best_rate; return 1; } else if (!stats->num_at_internal_rate && !stats->num_above_internal_rate) { /* In this case, the highest supported rate is actually lower than the internal rate */ softmix_data->internal_rate = stats->highest_supported_rate; - ast_debug(1, " Bridge changed from %d to %d\n", softmix_data->internal_rate, stats->highest_supported_rate); + ast_debug(1, "Bridge changed from %d to %d\n", + softmix_data->internal_rate, stats->highest_supported_rate); return 1; } return 0; @@ -745,38 +801,38 @@ static int softmix_mixing_array_grow(struct softmix_mixing_array *mixing_array, return 0; } -/*! \brief Function which acts as the mixing thread */ -static int softmix_bridge_thread(struct ast_bridge *bridge) +/*! + * \brief Mixing loop. + * + * \retval 0 on success + * \retval -1 on failure + */ +static int softmix_mixing_loop(struct ast_bridge *bridge) { struct softmix_stats stats = { { 0 }, }; struct softmix_mixing_array mixing_array; - struct softmix_bridge_data *softmix_data; + struct softmix_bridge_data *softmix_data = bridge->tech_pvt; struct ast_timer *timer; struct softmix_translate_helper trans_helper; int16_t buf[MAX_DATALEN]; unsigned int stat_iteration_counter = 0; /* counts down, gather stats at zero and reset. */ int timingfd; int update_all_rates = 0; /* set this when the internal sample rate has changed */ - int i, x; + unsigned int idx; + unsigned int x; int res = -1; - softmix_data = bridge->bridge_pvt; - if (!softmix_data) { - goto softmix_cleanup; - } - - ao2_ref(softmix_data, 1); timer = softmix_data->timer; timingfd = ast_timer_fd(timer); softmix_translate_helper_init(&trans_helper, softmix_data->internal_rate); ast_timer_set_rate(timer, (1000 / softmix_data->internal_mixing_interval)); /* Give the mixing array room to grow, memory is cheap but allocations are expensive. */ - if (softmix_mixing_array_init(&mixing_array, bridge->num + 10)) { + if (softmix_mixing_array_init(&mixing_array, bridge->num_channels + 10)) { goto softmix_cleanup; } - while (!bridge->stop && !bridge->refresh && bridge->array_num) { + while (!softmix_data->stop && bridge->num_active) { struct ast_bridge_channel *bridge_channel; int timeout = -1; enum ast_format_id cur_slin_id = ast_format_slin_by_rate(softmix_data->internal_rate); @@ -793,8 +849,8 @@ static int softmix_bridge_thread(struct ast_bridge *bridge) } /* Grow the mixing array buffer as participants are added. */ - if (mixing_array.max_num_entries < bridge->num - && softmix_mixing_array_grow(&mixing_array, bridge->num + 5)) { + if (mixing_array.max_num_entries < bridge->num_channels + && softmix_mixing_array_grow(&mixing_array, bridge->num_channels + 5)) { goto softmix_cleanup; } @@ -815,7 +871,7 @@ static int softmix_bridge_thread(struct ast_bridge *bridge) /* Go through pulling audio from each factory that has it available */ AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { - struct softmix_channel *sc = bridge_channel->bridge_pvt; + struct softmix_channel *sc = bridge_channel->tech_pvt; /* Update the sample rate to match the bridge's native sample rate if necessary. */ if (update_all_rates) { @@ -842,15 +898,15 @@ static int softmix_bridge_thread(struct ast_bridge *bridge) /* mix it like crazy */ memset(buf, 0, softmix_datalen); - for (i = 0; i < mixing_array.used_entries; i++) { - for (x = 0; x < softmix_samples; x++) { - ast_slinear_saturated_add(buf + x, mixing_array.buffers[i] + x); + for (idx = 0; idx < mixing_array.used_entries; ++idx) { + for (x = 0; x < softmix_samples; ++x) { + ast_slinear_saturated_add(buf + x, mixing_array.buffers[idx] + x); } } /* Next step go through removing the channel's own audio and creating a good frame... */ AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { - struct softmix_channel *sc = bridge_channel->bridge_pvt; + struct softmix_channel *sc = bridge_channel->tech_pvt; if (bridge_channel->suspended) { continue; @@ -869,13 +925,10 @@ static int softmix_bridge_thread(struct ast_bridge *bridge) /* process the softmix channel's new write audio */ softmix_process_write_audio(&trans_helper, ast_channel_rawwriteformat(bridge_channel->chan), sc); - /* The frame is now ready for use... */ - sc->have_frame = 1; - ast_mutex_unlock(&sc->lock); - /* Poke bridged channel thread just in case */ - pthread_kill(bridge_channel->thread, SIGURG); + /* A frame is now ready for the channel. */ + ast_bridge_channel_queue_frame(bridge_channel, &sc->write_frame); } update_all_rates = 0; @@ -885,17 +938,17 @@ static int softmix_bridge_thread(struct ast_bridge *bridge) } stat_iteration_counter--; - ao2_unlock(bridge); + ast_bridge_unlock(bridge); /* cleanup any translation frame data from the previous mixing iteration. */ softmix_translate_helper_cleanup(&trans_helper); /* Wait for the timing source to tell us to wake up and get things done */ ast_waitfor_n_fd(&timingfd, 1, &timeout, NULL); if (ast_timer_ack(timer, 1) < 0) { ast_log(LOG_ERROR, "Failed to acknowledge timer in softmix bridge.\n"); - ao2_lock(bridge); + ast_bridge_lock(bridge); goto softmix_cleanup; } - ao2_lock(bridge); + ast_bridge_lock(bridge); /* make sure to detect mixing interval changes if they occur. */ if (bridge->internal_mixing_interval && (bridge->internal_mixing_interval != softmix_data->internal_mixing_interval)) { @@ -910,23 +963,141 @@ static int softmix_bridge_thread(struct ast_bridge *bridge) softmix_cleanup: softmix_translate_helper_destroy(&trans_helper); softmix_mixing_array_destroy(&mixing_array); - if (softmix_data) { - ao2_ref(softmix_data, -1); - } return res; } +/*! + * \internal + * \brief Mixing thread. + * \since 12.0.0 + * + * \note The thread does not have its own reference to the + * bridge. The lifetime of the thread is tied to the lifetime + * of the mixing technology association with the bridge. + */ +static void *softmix_mixing_thread(void *data) +{ + struct ast_bridge *bridge = data; + struct softmix_bridge_data *softmix_data; + + ast_bridge_lock(bridge); + if (bridge->callid) { + ast_callid_threadassoc_add(bridge->callid); + } + + ast_debug(1, "Bridge %s: starting mixing thread\n", bridge->uniqueid); + + softmix_data = bridge->tech_pvt; + while (!softmix_data->stop) { + if (!bridge->num_active) { + /* Wait for something to happen to the bridge. */ + ast_bridge_unlock(bridge); + ast_mutex_lock(&softmix_data->lock); + if (!softmix_data->stop) { + ast_cond_wait(&softmix_data->cond, &softmix_data->lock); + } + ast_mutex_unlock(&softmix_data->lock); + ast_bridge_lock(bridge); + continue; + } + + if (softmix_mixing_loop(bridge)) { + /* + * A mixing error occurred. Sleep and try again later so we + * won't flood the logs. + */ + ast_bridge_unlock(bridge); + sleep(1); + ast_bridge_lock(bridge); + } + } + + ast_bridge_unlock(bridge); + + ast_debug(1, "Bridge %s: stopping mixing thread\n", bridge->uniqueid); + + return NULL; +} + +static void softmix_bridge_data_destroy(struct softmix_bridge_data *softmix_data) +{ + if (softmix_data->timer) { + ast_timer_close(softmix_data->timer); + softmix_data->timer = NULL; + } + ast_mutex_destroy(&softmix_data->lock); + ast_free(softmix_data); +} + +/*! \brief Function called when a bridge is created */ +static int softmix_bridge_create(struct ast_bridge *bridge) +{ + struct softmix_bridge_data *softmix_data; + + softmix_data = ast_calloc(1, sizeof(*softmix_data)); + if (!softmix_data) { + return -1; + } + ast_mutex_init(&softmix_data->lock); + softmix_data->timer = ast_timer_open(); + if (!softmix_data->timer) { + softmix_bridge_data_destroy(softmix_data); + return -1; + } + /* start at 8khz, let it grow from there */ + softmix_data->internal_rate = 8000; + softmix_data->internal_mixing_interval = DEFAULT_SOFTMIX_INTERVAL; + + bridge->tech_pvt = softmix_data; + + /* Start the mixing thread. */ + if (ast_pthread_create(&softmix_data->thread, NULL, softmix_mixing_thread, bridge)) { + softmix_data->thread = AST_PTHREADT_NULL; + softmix_bridge_data_destroy(softmix_data); + bridge->tech_pvt = NULL; + return -1; + } + + return 0; +} + +/*! \brief Function called when a bridge is destroyed */ +static void softmix_bridge_destroy(struct ast_bridge *bridge) +{ + struct softmix_bridge_data *softmix_data; + pthread_t thread; + + softmix_data = bridge->tech_pvt; + if (!softmix_data) { + return; + } + + /* Stop the mixing thread. */ + ast_mutex_lock(&softmix_data->lock); + softmix_data->stop = 1; + ast_cond_signal(&softmix_data->cond); + thread = softmix_data->thread; + softmix_data->thread = AST_PTHREADT_NULL; + ast_mutex_unlock(&softmix_data->lock); + if (thread != AST_PTHREADT_NULL) { + ast_debug(1, "Waiting for mixing thread to die.\n"); + pthread_join(thread, NULL); + } + + softmix_bridge_data_destroy(softmix_data); + bridge->tech_pvt = NULL; +} + static struct ast_bridge_technology softmix_bridge = { .name = "softmix", - .capabilities = AST_BRIDGE_CAPABILITY_MULTIMIX | AST_BRIDGE_CAPABILITY_THREAD | AST_BRIDGE_CAPABILITY_MULTITHREADED | AST_BRIDGE_CAPABILITY_OPTIMIZE | AST_BRIDGE_CAPABILITY_VIDEO, - .preference = AST_BRIDGE_PREFERENCE_LOW, + .capabilities = AST_BRIDGE_CAPABILITY_MULTIMIX, + .preference = AST_BRIDGE_PREFERENCE_BASE_MULTIMIX, .create = softmix_bridge_create, .destroy = softmix_bridge_destroy, .join = softmix_bridge_join, .leave = softmix_bridge_leave, + .unsuspend = softmix_bridge_unsuspend, .write = softmix_bridge_write, - .thread = softmix_bridge_thread, - .poke = softmix_bridge_poke, }; static int unload_module(void) |