diff options
Diffstat (limited to 'bridges')
-rw-r--r-- | bridges/bridge_multiplexed.c | 19 | ||||
-rw-r--r-- | bridges/bridge_softmix.c | 173 |
2 files changed, 158 insertions, 34 deletions
diff --git a/bridges/bridge_multiplexed.c b/bridges/bridge_multiplexed.c index 0d2b3e254..5a3de43d8 100644 --- a/bridges/bridge_multiplexed.c +++ b/bridges/bridge_multiplexed.c @@ -219,6 +219,9 @@ static void *multiplexed_thread_function(void *data) winner = ast_waitfor_nandfds(multiplexed_thread->chans, multiplexed_thread->service_count, &fds, 1, NULL, &outfd, &to); multiplexed_thread->waiting = 0; ao2_lock(multiplexed_thread); + if (multiplexed_thread->thread == AST_PTHREADT_STOP) { + break; + } if (outfd > -1) { int nudge; @@ -230,7 +233,21 @@ static void *multiplexed_thread_function(void *data) } } if (winner && winner->bridge) { - ast_bridge_handle_trip(winner->bridge, NULL, winner, -1); + struct ast_bridge *bridge = winner->bridge; + int stop = 0; + ao2_unlock(multiplexed_thread); + while ((bridge = winner->bridge) && ao2_trylock(bridge)) { + sched_yield(); + if (multiplexed_thread->thread == AST_PTHREADT_STOP) { + stop = 1; + break; + } + } + if (!stop && bridge) { + ast_bridge_handle_trip(bridge, NULL, winner, -1); + ao2_unlock(bridge); + } + ao2_lock(multiplexed_thread); } } diff --git a/bridges/bridge_softmix.c b/bridges/bridge_softmix.c index b25ab99fa..1ac2780de 100644 --- a/bridges/bridge_softmix.c +++ b/bridges/bridge_softmix.c @@ -52,14 +52,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/astobj2.h" #include "asterisk/timing.h" +#define MAX_DATALEN 3840 + /*! \brief Interval at which mixing will take place. Valid options are 10, 20, and 40. */ #define SOFTMIX_INTERVAL 20 /*! \brief Size of the buffer used for sample manipulation */ -#define SOFTMIX_DATALEN (160 * (SOFTMIX_INTERVAL / 10)) +#define SOFTMIX_DATALEN(rate) ((rate/50) * (SOFTMIX_INTERVAL / 10)) /*! \brief Number of samples we are dealing with */ -#define SOFTMIX_SAMPLES (SOFTMIX_DATALEN / 2) +#define SOFTMIX_SAMPLES(rate) (SOFTMIX_DATALEN(rate) / 2) /*! \brief Define used to turn on 16 kHz audio support */ /* #define SOFTMIX_16_SUPPORT */ @@ -77,40 +79,74 @@ struct softmix_channel { /*! Bit used to indicate that a frame is available to be written out to the channel */ int have_frame:1; /*! Buffer containing final mixed audio from all sources */ - short final_buf[SOFTMIX_DATALEN]; + short final_buf[MAX_DATALEN]; /*! Buffer containing only the audio from the channel */ - short our_buf[SOFTMIX_DATALEN]; + short our_buf[MAX_DATALEN]; +}; + +struct softmix_bridge_data { + struct ast_timer *timer; + unsigned int internal_rate; }; /*! \brief Function called when a bridge is created */ static int softmix_bridge_create(struct ast_bridge *bridge) { - struct ast_timer *timer; + struct softmix_bridge_data *bridge_data; - if (!(timer = ast_timer_open())) { + if (!(bridge_data = ast_calloc(1, sizeof(*bridge_data)))) { + return -1; + } + if (!(bridge_data->timer = ast_timer_open())) { + ast_free(bridge_data); return -1; } - bridge->bridge_pvt = timer; + /* start at 8khz, let it grow from there */ + bridge_data->internal_rate = 8000; + bridge->bridge_pvt = bridge_data; return 0; } /*! \brief Function called when a bridge is destroyed */ static int softmix_bridge_destroy(struct ast_bridge *bridge) { + struct softmix_bridge_data *bridge_data = bridge->bridge_pvt; if (!bridge->bridge_pvt) { return -1; } - ast_timer_close((struct ast_timer *) bridge->bridge_pvt); - + ast_timer_close(bridge_data->timer); + ast_free(bridge_data); return 0; } +static void set_softmix_bridge_data(int rate, struct ast_bridge_channel *bridge_channel, int reset) +{ + struct softmix_channel *sc = bridge_channel->bridge_pvt; + if (reset) { + ast_slinfactory_destroy(&sc->factory); + } + /* Setup frame parameters */ + sc->frame.frametype = AST_FRAME_VOICE; + + ast_format_set(&sc->frame.subclass.format, ast_format_slin_by_rate(rate), 0); + sc->frame.data.ptr = sc->final_buf; + sc->frame.datalen = SOFTMIX_DATALEN(rate); + sc->frame.samples = SOFTMIX_SAMPLES(rate); + + /* Setup smoother */ + ast_slinfactory_init_with_format(&sc->factory, &sc->frame.subclass.format); + + ast_set_read_format(bridge_channel->chan, &sc->frame.subclass.format); + ast_set_write_format(bridge_channel->chan, &sc->frame.subclass.format); +} + /*! \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 = NULL; + struct softmix_bridge_data *bridge_data = bridge->bridge_pvt; /* Create a new softmix_channel structure and allocate various things on it */ if (!(sc = ast_calloc(1, sizeof(*sc)))) { @@ -120,23 +156,11 @@ static int softmix_bridge_join(struct ast_bridge *bridge, struct ast_bridge_chan /* Can't forget the lock */ ast_mutex_init(&sc->lock); - /* Setup smoother */ - ast_slinfactory_init(&sc->factory); - - /* Setup frame parameters */ - sc->frame.frametype = AST_FRAME_VOICE; -#ifdef SOFTMIX_16_SUPPORT - ast_format_set(&sc->frame.subclass.format, AST_FORMAT_SLINEAR16, 0); -#else - ast_format_set(&sc->frame.subclass.format, AST_FORMAT_SLINEAR, 0); -#endif - sc->frame.data.ptr = sc->final_buf; - sc->frame.datalen = SOFTMIX_DATALEN; - sc->frame.samples = SOFTMIX_SAMPLES; - /* Can't forget to record our pvt structure within the bridged channel structure */ bridge_channel->bridge_pvt = sc; + set_softmix_bridge_data(bridge_data->internal_rate, bridge_channel, 0); + return 0; } @@ -170,11 +194,7 @@ static enum ast_bridge_write_result softmix_bridge_write(struct ast_bridge *brid ast_mutex_lock(&sc->lock); /* If a frame was provided add it to the smoother */ -#ifdef SOFTMIX_16_SUPPORT - if (frame->frametype == AST_FRAME_VOICE && frame->subclass.format.id == AST_FORMAT_SLINEAR16) { -#else - if (frame->frametype == AST_FRAME_VOICE && frame->subclass.format.id == AST_FORMAT_SLINEAR) { -#endif + if (frame->frametype == AST_FRAME_VOICE && ast_format_is_slinear(&frame->subclass.format)) { ast_slinfactory_feed(&sc->factory, frame); } @@ -210,29 +230,54 @@ static int softmix_bridge_poke(struct ast_bridge *bridge, struct ast_bridge_chan /*! \brief Function which acts as the mixing thread */ static int softmix_bridge_thread(struct ast_bridge *bridge) { - struct ast_timer *timer = (struct ast_timer *) bridge->bridge_pvt; + struct { + /*! Each index represents a sample rate used above the internal rate. */ + unsigned int sample_rates[8]; + /*! Each index represents the number of channels using the same index in the sample_rates array. */ + unsigned int num_channels[8]; + /*! 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; + } stats; + struct softmix_bridge_data *bridge_data = bridge->bridge_pvt; + struct ast_timer *timer = bridge_data->timer; int timingfd = ast_timer_fd(timer); + int update_all_rates = 0; /* set this when the internal sample rate has changed */ + int i; ast_timer_set_rate(timer, (1000 / SOFTMIX_INTERVAL)); while (!bridge->stop && !bridge->refresh && bridge->array_num) { struct ast_bridge_channel *bridge_channel = NULL; - short buf[SOFTMIX_DATALEN] = {0, }; + short buf[MAX_DATALEN] = {0, }; int timeout = -1; + /* these variables help determine if a rate change is required */ + memset(&stats, 0, sizeof(stats)); + stats.highest_supported_rate = 8000; + /* 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; + int channel_native_rate; ast_mutex_lock(&sc->lock); + if (update_all_rates) { + set_softmix_bridge_data(bridge_data->internal_rate, bridge_channel, 1); + } + /* Try to get audio from the factory if available */ - if (ast_slinfactory_available(&sc->factory) >= SOFTMIX_SAMPLES && ast_slinfactory_read(&sc->factory, sc->our_buf, SOFTMIX_SAMPLES)) { + if (ast_slinfactory_available(&sc->factory) >= SOFTMIX_SAMPLES(bridge_data->internal_rate) && + ast_slinfactory_read(&sc->factory, sc->our_buf, SOFTMIX_SAMPLES(bridge_data->internal_rate))) { short *data1, *data2; int i; /* Put into the local final buffer */ - for (i = 0, data1 = buf, data2 = sc->our_buf; i < SOFTMIX_DATALEN; i++, data1++, data2++) + for (i = 0, data1 = buf, data2 = sc->our_buf; i < SOFTMIX_DATALEN(bridge_data->internal_rate); i++, data1++, data2++) ast_slinear_saturated_add(data1, data2); /* Yay we have our own audio */ sc->have_audio = 1; @@ -240,6 +285,30 @@ static int softmix_bridge_thread(struct ast_bridge *bridge) /* Awww we don't have audio ;( */ sc->have_audio = 0; } + + /* Gather stats about channel sample rates. */ + channel_native_rate = MAX(ast_format_rate(&bridge_channel->chan->rawwriteformat), + ast_format_rate(&bridge_channel->chan->rawreadformat)); + + if (channel_native_rate > stats.highest_supported_rate) { + stats.highest_supported_rate = channel_native_rate; + } + if (channel_native_rate > bridge_data->internal_rate) { + for (i = 0; i < ARRAY_LEN(stats.sample_rates); i++) { + if (stats.sample_rates[i] == channel_native_rate) { + stats.num_channels[i]++; + break; + } else if (!stats.sample_rates[i]) { + stats.sample_rates[i] = channel_native_rate; + stats.num_channels[i]++; + break; + } + } + stats.num_above_internal_rate++; + } else if (channel_native_rate == bridge_data->internal_rate) { + stats.num_at_internal_rate++; + } + ast_mutex_unlock(&sc->lock); } @@ -253,7 +322,7 @@ static int softmix_bridge_thread(struct ast_bridge *bridge) /* If we provided audio then take it out */ if (sc->have_audio) { - for (i = 0; i < SOFTMIX_DATALEN; i++) { + for (i = 0; i < SOFTMIX_DATALEN(bridge_data->internal_rate); i++) { ast_slinear_saturated_subtract(&sc->final_buf[i], &sc->our_buf[i]); } } @@ -265,6 +334,44 @@ static int softmix_bridge_thread(struct ast_bridge *bridge) pthread_kill(bridge_channel->thread, SIGURG); } + /* Re-adjust the internal bridge sample rate if + * 1. two or more channels support a higher sample rate + * 2. no channels support the current sample rate or a higher rate + */ + if (stats.num_above_internal_rate >= 2) { + /* the highest rate is just used as a starting point */ + unsigned int best_rate = stats.highest_supported_rate; + int best_index = -1; + + /* 1. pick the best sample rate two or more channels support + * 2. if two or more channels do not support the same rate, pick the + * lowest sample rate that is still above the internal rate. */ + for (i = 0; ((i < ARRAY_LEN(stats.num_channels)) && stats.num_channels[i]); i++) { + if ((stats.num_channels[i] >= 2 && (best_index == -1)) || + ((best_index != -1) && + (stats.num_channels[i] >= 2) && + (stats.sample_rates[best_index] < stats.sample_rates[i]))) { + + best_rate = stats.sample_rates[i]; + best_index = i; + } else if (best_index == -1) { + best_rate = MIN(best_rate, stats.sample_rates[i]); + } + } + + ast_debug(1, " Bridge changed from %d To %d\n", bridge_data->internal_rate, best_rate); + bridge_data->internal_rate = best_rate; + update_all_rates = 1; + } else if (!stats.num_at_internal_rate && !stats.num_above_internal_rate) { + update_all_rates = 1; + /* in this case, the highest supported rate is actually lower than the internal rate */ + bridge_data->internal_rate = stats.highest_supported_rate; + ast_debug(1, " Bridge changed from %d to %d\n", bridge_data->internal_rate, stats.highest_supported_rate); + update_all_rates = 1; + } else { + update_all_rates = 0; + } + ao2_unlock(bridge); /* Wait for the timing source to tell us to wake up and get things done */ |