aboutsummaryrefslogtreecommitdiffstats
path: root/bridges/bridge_softmix.c
diff options
context:
space:
mode:
authordvossel <dvossel@f38db490-d61c-443f-a65b-d21fe96a405b>2011-02-22 23:04:49 +0000
committerdvossel <dvossel@f38db490-d61c-443f-a65b-d21fe96a405b>2011-02-22 23:04:49 +0000
commitf27e928f0588f5cbf85ac8202cef912efcc51a9c (patch)
treeb061487de973558358757bd1b6e457aaccf41638 /bridges/bridge_softmix.c
parent70442b4e1767b35ed1699d27cfc24109c617f445 (diff)
Media Project Phase2: SILK 8khz-24khz, SLINEAR 8khz-192khz, SPEEX 32khz, hd audio ConfBridge, and other stuff
-Functional changes 1. Dynamic global format list build by codecs defined in codecs.conf 2. SILK 8khz, 12khz, 16khz, and 24khz with custom attributes defined in codecs.conf 3. Negotiation of SILK attributes in chan_sip. 4. SPEEX 32khz with translation 5. SLINEAR 8khz, 12khz, 24khz, 32khz, 44.1khz, 48khz, 96khz, 192khz with translation using codec_resample.c 6. Various changes to RTP code required to properly handle the dynamic format list and formats with attributes. 7. ConfBridge now dynamically jumps to the best possible sample rate. This allows for conferences to take advantage of HD audio (Which sounds awesome) 8. Audiohooks are no longer limited to 8khz audio, and most effects have been updated to take advantage of this such as Volume, DENOISE, PITCH_SHIFT. 9. codec_resample now uses its own code rather than depending on libresample. -Organizational changes Global format list is moved from frame.c to format.c Various format specific functions moved from frame.c to format.c Review: https://reviewboard.asterisk.org/r/1104/ git-svn-id: http://svn.digium.com/svn/asterisk/trunk@308582 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'bridges/bridge_softmix.c')
-rw-r--r--bridges/bridge_softmix.c173
1 files changed, 140 insertions, 33 deletions
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 */