diff options
50 files changed, 4445 insertions, 845 deletions
@@ -40,6 +40,11 @@ CDR * The filter option in cdr_adaptive_odbc now supports negating the argument, thus allowing records which do NOT match the specified filter. +CODECS +-------------------------- + * Ability to define custom SILK formats in codecs.conf. + * Addition of speex32 audio format with translation. + Dialplan Variables ------------------ * Added ASTETCDIR, ASTMODDIR, ASTVARLIBDIR, ASTDBDIR, ASTKEYDIR, ASTDATADIR, diff --git a/apps/app_chanspy.c b/apps/app_chanspy.c index 1fc544afb..274bb2202 100644 --- a/apps/app_chanspy.c +++ b/apps/app_chanspy.c @@ -541,15 +541,15 @@ static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_auto memset(&csth, 0, sizeof(csth)); ast_copy_flags(&csth.spy_audiohook, flags, AST_FLAGS_ALL); - ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy"); + ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy", 0); if (start_spying(spyee_autochan, spyer_name, &csth.spy_audiohook)) { ast_audiohook_destroy(&csth.spy_audiohook); return 0; } - ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy"); - ast_audiohook_init(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Chanspy"); + ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy", 0); + ast_audiohook_init(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Chanspy", 0); if (start_spying(spyee_autochan, spyer_name, &csth.whisper_audiohook)) { ast_log(LOG_WARNING, "Unable to attach whisper audiohook to spyee %s. Whisper mode disabled!\n", name); } diff --git a/apps/app_jack.c b/apps/app_jack.c index d073451a8..07ab8da79 100644 --- a/apps/app_jack.c +++ b/apps/app_jack.c @@ -885,7 +885,7 @@ static int enable_jack_hook(struct ast_channel *chan, char *data) goto return_error; jack_data->has_audiohook = 1; - ast_audiohook_init(&jack_data->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "JACK_HOOK"); + ast_audiohook_init(&jack_data->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "JACK_HOOK", 0); jack_data->audiohook.manipulate_callback = jack_hook_callback; datastore->data = jack_data; diff --git a/apps/app_mixmonitor.c b/apps/app_mixmonitor.c index 8e6adead1..63454895f 100644 --- a/apps/app_mixmonitor.c +++ b/apps/app_mixmonitor.c @@ -426,7 +426,7 @@ static void launch_monitor_thread(struct ast_channel *chan, const char *filename } /* Setup the actual spy before creating our thread */ - if (ast_audiohook_init(&mixmonitor->audiohook, AST_AUDIOHOOK_TYPE_SPY, mixmonitor_spy_type)) { + if (ast_audiohook_init(&mixmonitor->audiohook, AST_AUDIOHOOK_TYPE_SPY, mixmonitor_spy_type, 0)) { mixmonitor_free(mixmonitor); return; } 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 */ diff --git a/channels/chan_gtalk.c b/channels/chan_gtalk.c index a284520af..d8dd736e4 100644 --- a/channels/chan_gtalk.c +++ b/channels/chan_gtalk.c @@ -285,7 +285,7 @@ static struct gtalk *find_gtalk(char *name, char *connection) static int add_codec_to_answer(const struct gtalk_pvt *p, struct ast_format *codec, iks *dcodecs) { int res = 0; - char *format = ast_getformatname(codec); + const char *format = ast_getformatname(codec); if (!strcasecmp("ulaw", format)) { iks *payload_eg711u, *payload_pcmu; diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index 6087a823a..ab28ec6dc 100644 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -1668,7 +1668,7 @@ static iax2_format iax2_best_codec(iax2_format formats) return ast_format_to_old_bitfield(&tmpfmt); } -char *iax2_getformatname(iax2_format format) +const char *iax2_getformatname(iax2_format format) { struct ast_format tmpfmt; if (!(ast_format_from_old_bitfield(&tmpfmt, format))) { diff --git a/channels/chan_jingle.c b/channels/chan_jingle.c index 93d81d954..311f4c91c 100644 --- a/channels/chan_jingle.c +++ b/channels/chan_jingle.c @@ -257,7 +257,7 @@ static struct jingle *find_jingle(char *name, char *connection) static void add_codec_to_answer(const struct jingle_pvt *p, struct ast_format *codec, iks *dcodecs) { - char *format = ast_getformatname(codec); + const char *format = ast_getformatname(codec); if (!strcasecmp("ulaw", format)) { iks *payload_eg711u, *payload_pcmu; diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 06e23ad07..4d9408f38 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -8885,11 +8885,10 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action } ast_debug(4, "We have an owner, now see if we need to change this call\n"); - - if (!(ast_format_cap_has_joint(p->owner->nativeformats, p->jointcaps)) && ast_format_cap_has_type(p->jointcaps, AST_FORMAT_TYPE_AUDIO)) { + if (ast_format_cap_has_type(p->jointcaps, AST_FORMAT_TYPE_AUDIO)) { if (debug) { char s1[SIPBUFSIZE], s2[SIPBUFSIZE]; - ast_debug(1, "Oooh, we need to change our audio formats since our peer supports only %s and not %s\n", + ast_debug(1, "Setting native formats after processing SDP. peer joint formats %s, old nativeformats %s\n", ast_getformatname_multiple(s1, SIPBUFSIZE, p->jointcaps), ast_getformatname_multiple(s2, SIPBUFSIZE, p->owner->nativeformats)); } @@ -9109,13 +9108,12 @@ static int process_sdp_a_audio(const char *a, struct sip_pvt *p, struct ast_rtp_ ast_verbose("Discarded description format %s for ID %d\n", mimeSubtype, codec); } } else if (sscanf(a, "fmtp: %30u %63s", &codec, fmtp_string) == 2) { - struct ast_rtp_payload_type payload; + struct ast_format *format; - payload = ast_rtp_codecs_payload_lookup(newaudiortp, codec); - if (payload.format.id && payload.asterisk_format) { + if ((format = ast_rtp_codecs_get_payload_format(newaudiortp, codec))) { unsigned int bit_rate; - switch ((int) payload.format.id) { + switch ((int) format->id) { case AST_FORMAT_SIREN7: if (sscanf(fmtp_string, "bitrate=%30u", &bit_rate) == 1) { if (bit_rate != 32000) { @@ -9145,6 +9143,21 @@ static int process_sdp_a_audio(const char *a, struct sip_pvt *p, struct ast_rtp_ found = TRUE; } } + break; + case AST_FORMAT_SILK: + { + int val = 0; + if (sscanf(fmtp_string, "maxaveragebitrate=%30u", &val) == 1) { + ast_format_append(format, SILK_ATTR_KEY_MAX_BITRATE, val, AST_FORMAT_ATTR_END); + } + if (sscanf(fmtp_string, "usedtx=%30u", &val) == 1) { + ast_format_append(format, SILK_ATTR_KEY_DTX, val ? 1 : 0, AST_FORMAT_ATTR_END); + } + if (sscanf(fmtp_string, "useinbandfec=%30u", &val) == 1) { + ast_format_append(format, SILK_ATTR_KEY_FEC, val ? 1 : 0, AST_FORMAT_ATTR_END); + } + break; + } } } } @@ -10505,6 +10518,20 @@ static void add_codec_to_sdp(const struct sip_pvt *p, /* Indicate that we only expect 64Kbps */ ast_str_append(a_buf, 0, "a=fmtp:%d bitrate=64000\r\n", rtp_code); break; + case AST_FORMAT_SILK: + { + int val = 0; + if (!ast_format_get_value(format, SILK_ATTR_KEY_MAX_BITRATE, &val) && val > 5000 && val < 40000) { + ast_str_append(a_buf, 0, "a=fmtp:%d maxaveragebitrate=%u\r\n", rtp_code, val); + } + if (!ast_format_get_value(format, SILK_ATTR_KEY_DTX, &val)) { + ast_str_append(a_buf, 0, "a=fmtp:%d usedtx=%u\r\n", rtp_code, val ? 1 : 0); + } + if (!ast_format_get_value(format, SILK_ATTR_KEY_FEC, &val)) { + ast_str_append(a_buf, 0, "a=fmtp:%d useinbandfec=%u\r\n", rtp_code, val ? 1 : 0); + } + break; + } } if (fmt.cur_ms && (fmt.cur_ms < *min_packet_size)) diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c index 3d485fff3..dc8738e1c 100644 --- a/channels/chan_skinny.c +++ b/channels/chan_skinny.c @@ -1796,7 +1796,7 @@ static struct ast_format *codec_skinny2ast(enum skinny_codecs skinnycodec, struc } } -static int codec_ast2skinny(struct ast_format *astcodec) +static int codec_ast2skinny(const struct ast_format *astcodec) { switch (astcodec->id) { case AST_FORMAT_ALAW: @@ -2289,7 +2289,7 @@ static void transmit_connect(struct skinny_device *d, struct skinny_subchannel * req->data.openreceivechannel.conferenceId = htolel(sub->callid); req->data.openreceivechannel.partyId = htolel(sub->callid); req->data.openreceivechannel.packets = htolel(fmt.cur_ms); - req->data.openreceivechannel.capability = htolel(codec_ast2skinny(ast_format_set(&tmpfmt, fmt.id, 0))); + req->data.openreceivechannel.capability = htolel(codec_ast2skinny(&fmt.format)); req->data.openreceivechannel.echo = htolel(0); req->data.openreceivechannel.bitrate = htolel(0); transmit_response(d, req); @@ -2494,7 +2494,6 @@ static void transmit_stopmediatransmission(struct skinny_device *d, struct skinn static void transmit_startmediatransmission(struct skinny_device *d, struct skinny_subchannel *sub, struct sockaddr_in dest, struct ast_format_list fmt) { struct skinny_req *req; - struct ast_format tmpfmt; if (!(req = req_alloc(sizeof(struct start_media_transmission_message), START_MEDIA_TRANSMISSION_MESSAGE))) return; @@ -2504,7 +2503,7 @@ static void transmit_startmediatransmission(struct skinny_device *d, struct skin req->data.startmedia.remoteIp = dest.sin_addr.s_addr; req->data.startmedia.remotePort = htolel(ntohs(dest.sin_port)); req->data.startmedia.packetSize = htolel(fmt.cur_ms); - req->data.startmedia.payloadType = htolel(codec_ast2skinny(ast_format_set(&tmpfmt, fmt.id, 0))); + req->data.startmedia.payloadType = htolel(codec_ast2skinny(&fmt.format)); req->data.startmedia.qualifier.precedence = htolel(127); req->data.startmedia.qualifier.vad = htolel(0); req->data.startmedia.qualifier.packets = htolel(0); @@ -2986,7 +2985,7 @@ static int skinny_set_rtp_peer(struct ast_channel *c, struct ast_rtp_instance *r fmt = ast_codec_pref_getsize(&l->prefs, &tmpfmt); if (skinnydebug) - ast_verb(1, "Setting payloadType to '%s' (%d ms)\n", ast_getformatname(ast_format_set(&tmpfmt, fmt.id, 0)), fmt.cur_ms); + ast_verb(1, "Setting payloadType to '%s' (%d ms)\n", ast_getformatname(&fmt.format), fmt.cur_ms); if (!(l->directmedia) || (l->nat)){ ast_rtp_instance_get_local_address(rtp, &us_tmp); @@ -5760,7 +5759,7 @@ static int handle_open_receive_channel_ack_message(struct skinny_req *req, struc fmt = ast_codec_pref_getsize(&l->prefs, &tmpfmt); if (skinnydebug) - ast_verb(1, "Setting payloadType to '%s' (%d ms)\n", ast_getformatname(ast_format_set(&tmpfmt, fmt.id, 0)), fmt.cur_ms); + ast_verb(1, "Setting payloadType to '%s' (%d ms)\n", ast_getformatname(&fmt.format), fmt.cur_ms); transmit_startmediatransmission(d, sub, us, fmt); diff --git a/channels/iax2.h b/channels/iax2.h index d579ed8aa..87e7bf398 100644 --- a/channels/iax2.h +++ b/channels/iax2.h @@ -218,7 +218,7 @@ enum iax_frame_subclass { typedef int64_t iax2_format; /*!\brief iax2 wrapper function for ast_getformatname */ -char *iax2_getformatname(iax2_format format); +const char *iax2_getformatname(iax2_format format); /*! Full frames are always delivered reliably */ struct ast_iax2_full_hdr { diff --git a/codecs/Makefile b/codecs/Makefile index 846c16364..6ef08daed 100644 --- a/codecs/Makefile +++ b/codecs/Makefile @@ -29,11 +29,13 @@ GSM_INCLUDE:=-Igsm/inc $(if $(filter codec_gsm,$(EMBEDDED_MODS)),modules.link,codec_gsm.so): gsm/lib/libgsm.a endif + clean:: $(MAKE) -C gsm clean $(MAKE) -C lpc10 clean $(MAKE) -C ilbc clean rm -f g722/*.[oa] + rm -f speex/*.[oa] gsm/lib/libgsm.a: @mkdir -p gsm/lib @@ -47,7 +49,17 @@ $(if $(filter codec_lpc10,$(EMBEDDED_MODS)),modules.link,codec_lpc10.so): $(LIBL $(LIBILBC): @$(MAKE) -C ilbc all _ASTCFLAGS="$(filter-out -Wmissing-prototypes -Wmissing-declarations -Wshadow,$(_ASTCFLAGS)) $(AST_NO_STRICT_OVERFLOW)" + $(if $(filter codec_ilbc,$(EMBEDDED_MODS)),modules.link,codec_ilbc.so): $(LIBILBC) $(if $(filter codec_g722,$(EMBEDDED_MODS)),modules.link,codec_g722.so): g722/g722_encode.o g722/g722_decode.o g722/g722_encode.o g722/g722_decode.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,codec_g722) + +ifeq ($(BUILD_CPU),x86_64) +SPEEX_RESAMPLE_CFLAGS:=-fPIC +else +SPEEX_RESAMPLE_CFLAGS:= +endif + +$(if $(filter codec_resample,$(EMBEDDED_MODS)),modules.link,codec_resample.so): speex/resample.o +speex/resample.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,codec_resample) $(SPEEX_RESAMPLE_CFLAGS) diff --git a/codecs/codec_resample.c b/codecs/codec_resample.c index 80823bb7c..20bbc5c9b 100644 --- a/codecs/codec_resample.c +++ b/codecs/codec_resample.c @@ -1,9 +1,10 @@ /* * Asterisk -- An open source telephony toolkit. * - * Copyright (C) 2007, Digium, Inc. + * Copyright (C) 2011, Digium, Inc. * * Russell Bryant <russell@digium.com> + * David Vossel <dvossel@digium.com> * * See http://www.asterisk.org for more information about * the Asterisk project. Please do not directly contact @@ -21,9 +22,6 @@ * * \brief Resample slinear audio * - * \note To install libresample, check it out of the following repository: - * <code>$ svn co http://svn.digium.com/svn/thirdparty/libresample/trunk</code> - * * \ingroup codecs */ @@ -32,170 +30,75 @@ ***/ #include "asterisk.h" +#include "speex/speex_resampler.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision$") -/* These are for SHRT_MAX and FLT_MAX -- { */ -#if defined(__Darwin__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__CYGWIN__) -#include <float.h> -#else -#include <values.h> -#endif -#include <limits.h> -/* } */ - -#include <libresample.h> - #include "asterisk/module.h" #include "asterisk/translate.h" - #include "asterisk/slin.h" -#define RESAMPLER_QUALITY 1 - #define OUTBUF_SIZE 8096 -struct slin16_to_slin8_pvt { - void *resampler; - float resample_factor; -}; - -struct slin8_to_slin16_pvt { - void *resampler; - float resample_factor; +static struct ast_translator *translators; +static int trans_size; +static int id_list[] = { + AST_FORMAT_SLINEAR, + AST_FORMAT_SLINEAR12, + AST_FORMAT_SLINEAR16, + AST_FORMAT_SLINEAR24, + AST_FORMAT_SLINEAR32, + AST_FORMAT_SLINEAR44, + AST_FORMAT_SLINEAR48, + AST_FORMAT_SLINEAR96, + AST_FORMAT_SLINEAR192, }; -static int slin16_to_slin8_new(struct ast_trans_pvt *pvt) +static int resamp_new(struct ast_trans_pvt *pvt) { - struct slin16_to_slin8_pvt *resamp_pvt = pvt->pvt; + int err; - resamp_pvt->resample_factor = 8000.0 / 16000.0; - - if (!(resamp_pvt->resampler = resample_open(RESAMPLER_QUALITY, resamp_pvt->resample_factor, resamp_pvt->resample_factor))) - return -1; - - return 0; -} - -static int slin8_to_slin16_new(struct ast_trans_pvt *pvt) -{ - struct slin8_to_slin16_pvt *resamp_pvt = pvt->pvt; - - resamp_pvt->resample_factor = 16000.0 / 8000.0; - - if (!(resamp_pvt->resampler = resample_open(RESAMPLER_QUALITY, resamp_pvt->resample_factor, resamp_pvt->resample_factor))) + if (!(pvt->pvt = speex_resampler_init(1, ast_format_rate(&pvt->t->src_format), ast_format_rate(&pvt->t->dst_format), 5, &err))) { return -1; + } return 0; } -static void slin16_to_slin8_destroy(struct ast_trans_pvt *pvt) -{ - struct slin16_to_slin8_pvt *resamp_pvt = pvt->pvt; - - if (resamp_pvt->resampler) - resample_close(resamp_pvt->resampler); -} - -static void slin8_to_slin16_destroy(struct ast_trans_pvt *pvt) +static void resamp_destroy(struct ast_trans_pvt *pvt) { - struct slin8_to_slin16_pvt *resamp_pvt = pvt->pvt; - - if (resamp_pvt->resampler) - resample_close(resamp_pvt->resampler); + SpeexResamplerState *resamp_pvt = pvt->pvt; + speex_resampler_destroy(resamp_pvt); } -static int resample_frame(struct ast_trans_pvt *pvt, - void *resampler, float resample_factor, struct ast_frame *f) +static int resamp_framein(struct ast_trans_pvt *pvt, struct ast_frame *f) { - int total_in_buf_used = 0; - int total_out_buf_used = 0; - int16_t *in_buf = (int16_t *) f->data.ptr; - int16_t *out_buf = pvt->outbuf.i16 + pvt->samples; - float in_buf_f[f->samples]; - float out_buf_f[2048]; - int res = 0; - int i; - - for (i = 0; i < f->samples; i++) - in_buf_f[i] = in_buf[i] * (FLT_MAX / SHRT_MAX); - - while (total_in_buf_used < f->samples) { - int in_buf_used, out_buf_used; + SpeexResamplerState *resamp_pvt = pvt->pvt; + unsigned int out_samples = (OUTBUF_SIZE / sizeof(int16_t)) - pvt->samples; + unsigned int in_samples = f->samples; - out_buf_used = resample_process(resampler, resample_factor, - &in_buf_f[total_in_buf_used], f->samples - total_in_buf_used, - 0, &in_buf_used, - &out_buf_f[total_out_buf_used], ARRAY_LEN(out_buf_f) - total_out_buf_used); + speex_resampler_process_int(resamp_pvt, + 0, + f->data.ptr, + &in_samples, + pvt->outbuf.i16 + pvt->samples, + &out_samples); - if (out_buf_used < 0) - break; + pvt->samples += out_samples; + pvt->datalen += out_samples * 2; - total_out_buf_used += out_buf_used; - total_in_buf_used += in_buf_used; - - if (total_out_buf_used == ARRAY_LEN(out_buf_f)) { - ast_log(LOG_ERROR, "Output buffer filled ... need to increase its size\n"); - res = -1; - break; - } - } - - for (i = 0; i < total_out_buf_used; i++) - out_buf[i] = out_buf_f[i] * (SHRT_MAX / FLT_MAX); - - pvt->samples += total_out_buf_used; - pvt->datalen += (total_out_buf_used * sizeof(int16_t)); - - return res; -} - -static int slin16_to_slin8_framein(struct ast_trans_pvt *pvt, struct ast_frame *f) -{ - struct slin16_to_slin8_pvt *resamp_pvt = pvt->pvt; - void *resampler = resamp_pvt->resampler; - float resample_factor = resamp_pvt->resample_factor; - - return resample_frame(pvt, resampler, resample_factor, f); -} - -static int slin8_to_slin16_framein(struct ast_trans_pvt *pvt, struct ast_frame *f) -{ - struct slin8_to_slin16_pvt *resamp_pvt = pvt->pvt; - void *resampler = resamp_pvt->resampler; - float resample_factor = resamp_pvt->resample_factor; - - return resample_frame(pvt, resampler, resample_factor, f); + return 0; } -static struct ast_translator slin16_to_slin8 = { - .name = "slin16_to_slin8", - .newpvt = slin16_to_slin8_new, - .destroy = slin16_to_slin8_destroy, - .framein = slin16_to_slin8_framein, - .sample = slin16_sample, - .desc_size = sizeof(struct slin16_to_slin8_pvt), - .buffer_samples = (OUTBUF_SIZE / sizeof(int16_t)), - .buf_size = OUTBUF_SIZE, -}; - -static struct ast_translator slin8_to_slin16 = { - .name = "slin8_to_slin16", - .newpvt = slin8_to_slin16_new, - .destroy = slin8_to_slin16_destroy, - .framein = slin8_to_slin16_framein, - .sample = slin8_sample, - .desc_size = sizeof(struct slin8_to_slin16_pvt), - .buffer_samples = (OUTBUF_SIZE / sizeof(int16_t)), - .buf_size = OUTBUF_SIZE, -}; - static int unload_module(void) { int res = 0; + int idx; - res |= ast_unregister_translator(&slin16_to_slin8); - res |= ast_unregister_translator(&slin8_to_slin16); + for (idx = 0; idx < trans_size; idx++) { + res |= ast_unregister_translator(&translators[idx]); + } + ast_free(translators); return res; } @@ -203,15 +106,33 @@ static int unload_module(void) static int load_module(void) { int res = 0; + int x, y, idx = 0; - ast_format_set(&slin16_to_slin8.src_format, AST_FORMAT_SLINEAR16, 0); - ast_format_set(&slin16_to_slin8.dst_format, AST_FORMAT_SLINEAR, 0); + trans_size = ARRAY_LEN(id_list) * ARRAY_LEN(id_list); + if (!(translators = ast_calloc(1, sizeof(struct ast_translator) * trans_size))) { + return AST_MODULE_LOAD_FAILURE; + } - ast_format_set(&slin8_to_slin16.src_format, AST_FORMAT_SLINEAR, 0); - ast_format_set(&slin8_to_slin16.dst_format, AST_FORMAT_SLINEAR16, 0); + for (x = 0; x < ARRAY_LEN(id_list); x++) { + for (y = 0; y < ARRAY_LEN(id_list); y++) { + if (x == y) { + continue; + } + translators[idx].newpvt = resamp_new; + translators[idx].destroy = resamp_destroy; + translators[idx].framein = resamp_framein; + translators[idx].desc_size = 0; + translators[idx].buffer_samples = (OUTBUF_SIZE / sizeof(int16_t)); + translators[idx].buf_size = OUTBUF_SIZE; + ast_format_set(&translators[idx].src_format, id_list[x], 0); + ast_format_set(&translators[idx].dst_format, id_list[y], 0); + snprintf(translators[idx].name, sizeof(translators[idx].name), "slin %dkhz -> %dkhz", + ast_format_rate(&translators[idx].src_format), ast_format_rate(&translators[idx].dst_format)); + res |= ast_register_translator(&translators[idx]); + idx++; + } - res |= ast_register_translator(&slin16_to_slin8); - res |= ast_register_translator(&slin8_to_slin16); + } return AST_MODULE_LOAD_SUCCESS; } diff --git a/codecs/codec_speex.c b/codecs/codec_speex.c index aaaa1bea2..d48e21f28 100644 --- a/codecs/codec_speex.c +++ b/codecs/codec_speex.c @@ -148,6 +148,11 @@ static int lin16tospeexwb_new(struct ast_trans_pvt *pvt) return speex_encoder_construct(pvt, &speex_wb_mode, 16000); } +static int lin32tospeexuwb_new(struct ast_trans_pvt *pvt) +{ + return speex_encoder_construct(pvt, &speex_uwb_mode, 32000); +} + static int speex_decoder_construct(struct ast_trans_pvt *pvt, const SpeexMode *profile) { struct speex_coder_pvt *tmp = pvt->pvt; @@ -173,6 +178,11 @@ static int speexwbtolin16_new(struct ast_trans_pvt *pvt) return speex_decoder_construct(pvt, &speex_wb_mode); } +static int speexuwbtolin32_new(struct ast_trans_pvt *pvt) +{ + return speex_decoder_construct(pvt, &speex_uwb_mode); +} + /*! \brief convert and store into outbuf */ static int speextolin_framein(struct ast_trans_pvt *pvt, struct ast_frame *f) { @@ -376,6 +386,28 @@ static struct ast_translator lin16tospeexwb = { .buf_size = BUFFER_SAMPLES * 2, /* XXX maybe a lot less ? */ }; +static struct ast_translator speexuwbtolin32 = { + .name = "speexuwbtolin32", + .newpvt = speexuwbtolin32_new, + .framein = speextolin_framein, + .destroy = speextolin_destroy, + .desc_size = sizeof(struct speex_coder_pvt), + .buffer_samples = BUFFER_SAMPLES, + .buf_size = BUFFER_SAMPLES * 2, + .native_plc = 1, +}; + +static struct ast_translator lin32tospeexuwb = { + .name = "lin32tospeexuwb", + .newpvt = lin32tospeexuwb_new, + .framein = lintospeex_framein, + .frameout = lintospeex_frameout, + .destroy = lintospeex_destroy, + .desc_size = sizeof(struct speex_coder_pvt), + .buffer_samples = BUFFER_SAMPLES, + .buf_size = BUFFER_SAMPLES * 2, /* XXX maybe a lot less ? */ +}; + static int parse_config(int reload) { struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; @@ -486,6 +518,9 @@ static int unload_module(void) res |= ast_unregister_translator(&lintospeex); res |= ast_unregister_translator(&speexwbtolin16); res |= ast_unregister_translator(&lin16tospeexwb); + res |= ast_unregister_translator(&speexuwbtolin32); + res |= ast_unregister_translator(&lin32tospeexuwb); + return res; } @@ -510,10 +545,19 @@ static int load_module(void) ast_format_set(&lin16tospeexwb.src_format, AST_FORMAT_SLINEAR16, 0); ast_format_set(&lin16tospeexwb.dst_format, AST_FORMAT_SPEEX16, 0); + ast_format_set(&speexuwbtolin32.src_format, AST_FORMAT_SPEEX32, 0); + ast_format_set(&speexuwbtolin32.dst_format, AST_FORMAT_SLINEAR32, 0); + + ast_format_set(&lin32tospeexuwb.src_format, AST_FORMAT_SLINEAR32, 0); + ast_format_set(&lin32tospeexuwb.dst_format, AST_FORMAT_SPEEX32, 0); + res |= ast_register_translator(&speextolin); res |= ast_register_translator(&lintospeex); res |= ast_register_translator(&speexwbtolin16); res |= ast_register_translator(&lin16tospeexwb); + res |= ast_register_translator(&speexuwbtolin32); + res |= ast_register_translator(&lin32tospeexuwb); + return res; } diff --git a/codecs/speex/arch.h b/codecs/speex/arch.h new file mode 100644 index 000000000..af42e645d --- /dev/null +++ b/codecs/speex/arch.h @@ -0,0 +1,241 @@ +/* Copyright (C) 2003 Jean-Marc Valin */ +/** + @file arch.h + @brief Various architecture definitions Speex +*/ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef ARCH_H +#define ARCH_H + +#ifndef SPEEX_VERSION +#define SPEEX_MAJOR_VERSION 1 /**< Major Speex version. */ +#define SPEEX_MINOR_VERSION 1 /**< Minor Speex version. */ +#define SPEEX_MICRO_VERSION 15 /**< Micro Speex version. */ +#define SPEEX_EXTRA_VERSION "" /**< Extra Speex version. */ +#define SPEEX_VERSION "speex-1.2beta3" /**< Speex version string. */ +#endif + +#define FIXED_POINT + +/* A couple test to catch stupid option combinations */ +#ifdef FIXED_POINT + +#ifdef FLOATING_POINT +#error You cannot compile as floating point and fixed point at the same time +#endif +#ifdef _USE_SSE +#error SSE is only for floating-point +#endif +#if ((defined (ARM4_ASM)||defined (ARM4_ASM)) && defined(BFIN_ASM)) || (defined (ARM4_ASM)&&defined(ARM5E_ASM)) +#error Make up your mind. What CPU do you have? +#endif +#ifdef VORBIS_PSYCHO +#error Vorbis-psy model currently not implemented in fixed-point +#endif + +#else + +#ifndef FLOATING_POINT +#error You now need to define either FIXED_POINT or FLOATING_POINT +#endif +#if defined (ARM4_ASM) || defined(ARM5E_ASM) || defined(BFIN_ASM) +#error I suppose you can have a [ARM4/ARM5E/Blackfin] that has float instructions? +#endif +#ifdef FIXED_POINT_DEBUG +#error "Don't you think enabling fixed-point is a good thing to do if you want to debug that?" +#endif + + +#endif + +#ifndef OUTSIDE_SPEEX +#include "speex/speex_types.h" +#endif + +#define ABS(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute integer value. */ +#define ABS16(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute 16-bit value. */ +#define MIN16(a,b) ((a) < (b) ? (a) : (b)) /**< Maximum 16-bit value. */ +#define MAX16(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 16-bit value. */ +#define ABS32(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute 32-bit value. */ +#define MIN32(a,b) ((a) < (b) ? (a) : (b)) /**< Maximum 32-bit value. */ +#define MAX32(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 32-bit value. */ + +#ifdef FIXED_POINT + +typedef spx_int16_t spx_word16_t; +typedef spx_int32_t spx_word32_t; +typedef spx_word32_t spx_mem_t; +typedef spx_word16_t spx_coef_t; +typedef spx_word16_t spx_lsp_t; +typedef spx_word32_t spx_sig_t; + +#define Q15ONE 32767 + +#define LPC_SCALING 8192 +#define SIG_SCALING 16384 +#define LSP_SCALING 8192. +#define GAMMA_SCALING 32768. +#define GAIN_SCALING 64 +#define GAIN_SCALING_1 0.015625 + +#define LPC_SHIFT 13 +#define LSP_SHIFT 13 +#define SIG_SHIFT 14 +#define GAIN_SHIFT 6 + +#define VERY_SMALL 0 +#define VERY_LARGE32 ((spx_word32_t)2147483647) +#define VERY_LARGE16 ((spx_word16_t)32767) +#define Q15_ONE ((spx_word16_t)32767) + + +#ifdef FIXED_DEBUG +#include "fixed_debug.h" +#else + +#include "fixed_generic.h" + +#ifdef ARM5E_ASM +#include "fixed_arm5e.h" +#elif defined (ARM4_ASM) +#include "fixed_arm4.h" +#elif defined (BFIN_ASM) +#include "fixed_bfin.h" +#endif + +#endif + + +#else + +typedef float spx_mem_t; +typedef float spx_coef_t; +typedef float spx_lsp_t; +typedef float spx_sig_t; +typedef float spx_word16_t; +typedef float spx_word32_t; + +#define Q15ONE 1.0f +#define LPC_SCALING 1.f +#define SIG_SCALING 1.f +#define LSP_SCALING 1.f +#define GAMMA_SCALING 1.f +#define GAIN_SCALING 1.f +#define GAIN_SCALING_1 1.f + + +#define VERY_SMALL 1e-15f +#define VERY_LARGE32 1e15f +#define VERY_LARGE16 1e15f +#define Q15_ONE ((spx_word16_t)1.f) + +#define QCONST16(x,bits) (x) +#define QCONST32(x,bits) (x) + +#define NEG16(x) (-(x)) +#define NEG32(x) (-(x)) +#define EXTRACT16(x) (x) +#define EXTEND32(x) (x) +#define SHR16(a,shift) (a) +#define SHL16(a,shift) (a) +#define SHR32(a,shift) (a) +#define SHL32(a,shift) (a) +#define PSHR16(a,shift) (a) +#define PSHR32(a,shift) (a) +#define VSHR32(a,shift) (a) +#define SATURATE16(x,a) (x) +#define SATURATE32(x,a) (x) + +#define PSHR(a,shift) (a) +#define SHR(a,shift) (a) +#define SHL(a,shift) (a) +#define SATURATE(x,a) (x) + +#define ADD16(a,b) ((a)+(b)) +#define SUB16(a,b) ((a)-(b)) +#define ADD32(a,b) ((a)+(b)) +#define SUB32(a,b) ((a)-(b)) +#define MULT16_16_16(a,b) ((a)*(b)) +#define MULT16_16(a,b) ((spx_word32_t)(a)*(spx_word32_t)(b)) +#define MAC16_16(c,a,b) ((c)+(spx_word32_t)(a)*(spx_word32_t)(b)) + +#define MULT16_32_Q11(a,b) ((a)*(b)) +#define MULT16_32_Q13(a,b) ((a)*(b)) +#define MULT16_32_Q14(a,b) ((a)*(b)) +#define MULT16_32_Q15(a,b) ((a)*(b)) +#define MULT16_32_P15(a,b) ((a)*(b)) + +#define MAC16_32_Q11(c,a,b) ((c)+(a)*(b)) +#define MAC16_32_Q15(c,a,b) ((c)+(a)*(b)) + +#define MAC16_16_Q11(c,a,b) ((c)+(a)*(b)) +#define MAC16_16_Q13(c,a,b) ((c)+(a)*(b)) +#define MAC16_16_P13(c,a,b) ((c)+(a)*(b)) +#define MULT16_16_Q11_32(a,b) ((a)*(b)) +#define MULT16_16_Q13(a,b) ((a)*(b)) +#define MULT16_16_Q14(a,b) ((a)*(b)) +#define MULT16_16_Q15(a,b) ((a)*(b)) +#define MULT16_16_P15(a,b) ((a)*(b)) +#define MULT16_16_P13(a,b) ((a)*(b)) +#define MULT16_16_P14(a,b) ((a)*(b)) + +#define DIV32_16(a,b) (((spx_word32_t)(a))/(spx_word16_t)(b)) +#define PDIV32_16(a,b) (((spx_word32_t)(a))/(spx_word16_t)(b)) +#define DIV32(a,b) (((spx_word32_t)(a))/(spx_word32_t)(b)) +#define PDIV32(a,b) (((spx_word32_t)(a))/(spx_word32_t)(b)) + + +#endif + + +#if defined (CONFIG_TI_C54X) || defined (CONFIG_TI_C55X) + +/* 2 on TI C5x DSP */ +#define BYTES_PER_CHAR 2 +#define BITS_PER_CHAR 16 +#define LOG2_BITS_PER_CHAR 4 + +#else + +#define BYTES_PER_CHAR 1 +#define BITS_PER_CHAR 8 +#define LOG2_BITS_PER_CHAR 3 + +#endif + + + +#ifdef FIXED_DEBUG +extern long long spx_mips; +#endif + + +#endif diff --git a/codecs/speex/fixed_generic.h b/codecs/speex/fixed_generic.h new file mode 100644 index 000000000..3fb096ed9 --- /dev/null +++ b/codecs/speex/fixed_generic.h @@ -0,0 +1,106 @@ +/* Copyright (C) 2003 Jean-Marc Valin */ +/** + @file fixed_generic.h + @brief Generic fixed-point operations +*/ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef FIXED_GENERIC_H +#define FIXED_GENERIC_H + +#define QCONST16(x,bits) ((spx_word16_t)(.5+(x)*(((spx_word32_t)1)<<(bits)))) +#define QCONST32(x,bits) ((spx_word32_t)(.5+(x)*(((spx_word32_t)1)<<(bits)))) + +#define NEG16(x) (-(x)) +#define NEG32(x) (-(x)) +#define EXTRACT16(x) ((spx_word16_t)(x)) +#define EXTEND32(x) ((spx_word32_t)(x)) +#define SHR16(a,shift) ((a) >> (shift)) +#define SHL16(a,shift) ((a) << (shift)) +#define SHR32(a,shift) ((a) >> (shift)) +#define SHL32(a,shift) ((a) << (shift)) +#define PSHR16(a,shift) (SHR16((a)+((1<<((shift))>>1)),shift)) +#define PSHR32(a,shift) (SHR32((a)+((EXTEND32(1)<<((shift))>>1)),shift)) +#define VSHR32(a, shift) (((shift)>0) ? SHR32(a, shift) : SHL32(a, -(shift))) +#define SATURATE16(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x))) +#define SATURATE32(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x))) + +#define SHR(a,shift) ((a) >> (shift)) +#define SHL(a,shift) ((spx_word32_t)(a) << (shift)) +#define PSHR(a,shift) (SHR((a)+((EXTEND32(1)<<((shift))>>1)),shift)) +#define SATURATE(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x))) + + +#define ADD16(a,b) ((spx_word16_t)((spx_word16_t)(a)+(spx_word16_t)(b))) +#define SUB16(a,b) ((spx_word16_t)(a)-(spx_word16_t)(b)) +#define ADD32(a,b) ((spx_word32_t)(a)+(spx_word32_t)(b)) +#define SUB32(a,b) ((spx_word32_t)(a)-(spx_word32_t)(b)) + + +/* result fits in 16 bits */ +#define MULT16_16_16(a,b) ((((spx_word16_t)(a))*((spx_word16_t)(b)))) + +/* (spx_word32_t)(spx_word16_t) gives TI compiler a hint that it's 16x16->32 multiply */ +#define MULT16_16(a,b) (((spx_word32_t)(spx_word16_t)(a))*((spx_word32_t)(spx_word16_t)(b))) + +#define MAC16_16(c,a,b) (ADD32((c),MULT16_16((a),(b)))) +#define MULT16_32_Q12(a,b) ADD32(MULT16_16((a),SHR((b),12)), SHR(MULT16_16((a),((b)&0x00000fff)),12)) +#define MULT16_32_Q13(a,b) ADD32(MULT16_16((a),SHR((b),13)), SHR(MULT16_16((a),((b)&0x00001fff)),13)) +#define MULT16_32_Q14(a,b) ADD32(MULT16_16((a),SHR((b),14)), SHR(MULT16_16((a),((b)&0x00003fff)),14)) + +#define MULT16_32_Q11(a,b) ADD32(MULT16_16((a),SHR((b),11)), SHR(MULT16_16((a),((b)&0x000007ff)),11)) +#define MAC16_32_Q11(c,a,b) ADD32(c,ADD32(MULT16_16((a),SHR((b),11)), SHR(MULT16_16((a),((b)&0x000007ff)),11))) + +#define MULT16_32_P15(a,b) ADD32(MULT16_16((a),SHR((b),15)), PSHR(MULT16_16((a),((b)&0x00007fff)),15)) +#define MULT16_32_Q15(a,b) ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15)) +#define MAC16_32_Q15(c,a,b) ADD32(c,ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15))) + + +#define MAC16_16_Q11(c,a,b) (ADD32((c),SHR(MULT16_16((a),(b)),11))) +#define MAC16_16_Q13(c,a,b) (ADD32((c),SHR(MULT16_16((a),(b)),13))) +#define MAC16_16_P13(c,a,b) (ADD32((c),SHR(ADD32(4096,MULT16_16((a),(b))),13))) + +#define MULT16_16_Q11_32(a,b) (SHR(MULT16_16((a),(b)),11)) +#define MULT16_16_Q13(a,b) (SHR(MULT16_16((a),(b)),13)) +#define MULT16_16_Q14(a,b) (SHR(MULT16_16((a),(b)),14)) +#define MULT16_16_Q15(a,b) (SHR(MULT16_16((a),(b)),15)) + +#define MULT16_16_P13(a,b) (SHR(ADD32(4096,MULT16_16((a),(b))),13)) +#define MULT16_16_P14(a,b) (SHR(ADD32(8192,MULT16_16((a),(b))),14)) +#define MULT16_16_P15(a,b) (SHR(ADD32(16384,MULT16_16((a),(b))),15)) + +#define MUL_16_32_R15(a,bh,bl) ADD32(MULT16_16((a),(bh)), SHR(MULT16_16((a),(bl)),15)) + +#define DIV32_16(a,b) ((spx_word16_t)(((spx_word32_t)(a))/((spx_word16_t)(b)))) +#define PDIV32_16(a,b) ((spx_word16_t)(((spx_word32_t)(a)+((spx_word16_t)(b)>>1))/((spx_word16_t)(b)))) +#define DIV32(a,b) (((spx_word32_t)(a))/((spx_word32_t)(b))) +#define PDIV32(a,b) (((spx_word32_t)(a)+((spx_word16_t)(b)>>1))/((spx_word32_t)(b))) + +#endif diff --git a/codecs/speex/resample.c b/codecs/speex/resample.c new file mode 100644 index 000000000..2b0395180 --- /dev/null +++ b/codecs/speex/resample.c @@ -0,0 +1,1124 @@ +/* Copyright (C) 2007-2008 Jean-Marc Valin + Copyright (C) 2008 Thorvald Natvig + + File: resample.c + Arbitrary resampling code + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + The design goals of this code are: + - Very fast algorithm + - SIMD-friendly algorithm + - Low memory requirement + - Good *perceptual* quality (and not best SNR) + + Warning: This resampler is relatively new. Although I think I got rid of + all the major bugs and I don't expect the API to change anymore, there + may be something I've missed. So use with caution. + + This algorithm is based on this original resampling algorithm: + Smith, Julius O. Digital Audio Resampling Home Page + Center for Computer Research in Music and Acoustics (CCRMA), + Stanford University, 2007. + Web published at http://www-ccrma.stanford.edu/~jos/resample/. + + There is one main difference, though. This resampler uses cubic + interpolation instead of linear interpolation in the above paper. This + makes the table much smaller and makes it possible to compute that table + on a per-stream basis. In turn, being able to tweak the table for each + stream makes it possible to both reduce complexity on simple ratios + (e.g. 2/3), and get rid of the rounding operations in the inner loop. + The latter both reduces CPU time and makes the algorithm more SIMD-friendly. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +static void *speex_alloc (int size) {return calloc(size,1);} +static void *speex_realloc (void *ptr, int size) {return realloc(ptr, size);} +static void speex_free (void *ptr) {free(ptr);} +#include "speex_resampler.h" +#include "arch.h" + +#include "stack_alloc.h" +#include <math.h> + +#ifndef M_PI +#define M_PI 3.14159263 +#endif + +#ifdef FIXED_POINT +#define WORD2INT(x) ((x) < -32767 ? -32768 : ((x) > 32766 ? 32767 : (x))) +#else +#define WORD2INT(x) ((x) < -32767.5f ? -32768 : ((x) > 32766.5f ? 32767 : floor(.5+(x)))) +#endif + +#define IMAX(a,b) ((a) > (b) ? (a) : (b)) +#define IMIN(a,b) ((a) < (b) ? (a) : (b)) + +#ifndef NULL +#define NULL 0 +#endif + +#ifdef _USE_SSE +#include "resample_sse.h" +#endif + +/* Numer of elements to allocate on the stack */ +#ifdef VAR_ARRAYS +#define FIXED_STACK_ALLOC 8192 +#else +#define FIXED_STACK_ALLOC 1024 +#endif + +typedef int (*resampler_basic_func)(SpeexResamplerState *, spx_uint32_t , const spx_word16_t *, spx_uint32_t *, spx_word16_t *, spx_uint32_t *); + +struct SpeexResamplerState_ { + spx_uint32_t in_rate; + spx_uint32_t out_rate; + spx_uint32_t num_rate; + spx_uint32_t den_rate; + + int quality; + spx_uint32_t nb_channels; + spx_uint32_t filt_len; + spx_uint32_t mem_alloc_size; + spx_uint32_t buffer_size; + int int_advance; + int frac_advance; + float cutoff; + spx_uint32_t oversample; + int initialised; + int started; + + /* These are per-channel */ + spx_int32_t *last_sample; + spx_uint32_t *samp_frac_num; + spx_uint32_t *magic_samples; + + spx_word16_t *mem; + spx_word16_t *sinc_table; + spx_uint32_t sinc_table_length; + resampler_basic_func resampler_ptr; + + int in_stride; + int out_stride; +} ; + +static double kaiser12_table[68] = { + 0.99859849, 1.00000000, 0.99859849, 0.99440475, 0.98745105, 0.97779076, + 0.96549770, 0.95066529, 0.93340547, 0.91384741, 0.89213598, 0.86843014, + 0.84290116, 0.81573067, 0.78710866, 0.75723148, 0.72629970, 0.69451601, + 0.66208321, 0.62920216, 0.59606986, 0.56287762, 0.52980938, 0.49704014, + 0.46473455, 0.43304576, 0.40211431, 0.37206735, 0.34301800, 0.31506490, + 0.28829195, 0.26276832, 0.23854851, 0.21567274, 0.19416736, 0.17404546, + 0.15530766, 0.13794294, 0.12192957, 0.10723616, 0.09382272, 0.08164178, + 0.07063950, 0.06075685, 0.05193064, 0.04409466, 0.03718069, 0.03111947, + 0.02584161, 0.02127838, 0.01736250, 0.01402878, 0.01121463, 0.00886058, + 0.00691064, 0.00531256, 0.00401805, 0.00298291, 0.00216702, 0.00153438, + 0.00105297, 0.00069463, 0.00043489, 0.00025272, 0.00013031, 0.0000527734, + 0.00001000, 0.00000000}; +/* +static double kaiser12_table[36] = { + 0.99440475, 1.00000000, 0.99440475, 0.97779076, 0.95066529, 0.91384741, + 0.86843014, 0.81573067, 0.75723148, 0.69451601, 0.62920216, 0.56287762, + 0.49704014, 0.43304576, 0.37206735, 0.31506490, 0.26276832, 0.21567274, + 0.17404546, 0.13794294, 0.10723616, 0.08164178, 0.06075685, 0.04409466, + 0.03111947, 0.02127838, 0.01402878, 0.00886058, 0.00531256, 0.00298291, + 0.00153438, 0.00069463, 0.00025272, 0.0000527734, 0.00000500, 0.00000000}; +*/ +static double kaiser10_table[36] = { + 0.99537781, 1.00000000, 0.99537781, 0.98162644, 0.95908712, 0.92831446, + 0.89005583, 0.84522401, 0.79486424, 0.74011713, 0.68217934, 0.62226347, + 0.56155915, 0.50119680, 0.44221549, 0.38553619, 0.33194107, 0.28205962, + 0.23636152, 0.19515633, 0.15859932, 0.12670280, 0.09935205, 0.07632451, + 0.05731132, 0.04193980, 0.02979584, 0.02044510, 0.01345224, 0.00839739, + 0.00488951, 0.00257636, 0.00115101, 0.00035515, 0.00000000, 0.00000000}; + +static double kaiser8_table[36] = { + 0.99635258, 1.00000000, 0.99635258, 0.98548012, 0.96759014, 0.94302200, + 0.91223751, 0.87580811, 0.83439927, 0.78875245, 0.73966538, 0.68797126, + 0.63451750, 0.58014482, 0.52566725, 0.47185369, 0.41941150, 0.36897272, + 0.32108304, 0.27619388, 0.23465776, 0.19672670, 0.16255380, 0.13219758, + 0.10562887, 0.08273982, 0.06335451, 0.04724088, 0.03412321, 0.02369490, + 0.01563093, 0.00959968, 0.00527363, 0.00233883, 0.00050000, 0.00000000}; + +static double kaiser6_table[36] = { + 0.99733006, 1.00000000, 0.99733006, 0.98935595, 0.97618418, 0.95799003, + 0.93501423, 0.90755855, 0.87598009, 0.84068475, 0.80211977, 0.76076565, + 0.71712752, 0.67172623, 0.62508937, 0.57774224, 0.53019925, 0.48295561, + 0.43647969, 0.39120616, 0.34752997, 0.30580127, 0.26632152, 0.22934058, + 0.19505503, 0.16360756, 0.13508755, 0.10953262, 0.08693120, 0.06722600, + 0.05031820, 0.03607231, 0.02432151, 0.01487334, 0.00752000, 0.00000000}; + +struct FuncDef { + double *table; + int oversample; +}; + +static struct FuncDef _KAISER12 = {kaiser12_table, 64}; +#define KAISER12 (&_KAISER12) +/*static struct FuncDef _KAISER12 = {kaiser12_table, 32}; +#define KAISER12 (&_KAISER12)*/ +static struct FuncDef _KAISER10 = {kaiser10_table, 32}; +#define KAISER10 (&_KAISER10) +static struct FuncDef _KAISER8 = {kaiser8_table, 32}; +#define KAISER8 (&_KAISER8) +static struct FuncDef _KAISER6 = {kaiser6_table, 32}; +#define KAISER6 (&_KAISER6) + +struct QualityMapping { + int base_length; + int oversample; + float downsample_bandwidth; + float upsample_bandwidth; + struct FuncDef *window_func; +}; + + +/* This table maps conversion quality to internal parameters. There are two + reasons that explain why the up-sampling bandwidth is larger than the + down-sampling bandwidth: + 1) When up-sampling, we can assume that the spectrum is already attenuated + close to the Nyquist rate (from an A/D or a previous resampling filter) + 2) Any aliasing that occurs very close to the Nyquist rate will be masked + by the sinusoids/noise just below the Nyquist rate (guaranteed only for + up-sampling). +*/ +static const struct QualityMapping quality_map[11] = { + { 8, 4, 0.830f, 0.860f, KAISER6 }, /* Q0 */ + { 16, 4, 0.850f, 0.880f, KAISER6 }, /* Q1 */ + { 32, 4, 0.882f, 0.910f, KAISER6 }, /* Q2 */ /* 82.3% cutoff ( ~60 dB stop) 6 */ + { 48, 8, 0.895f, 0.917f, KAISER8 }, /* Q3 */ /* 84.9% cutoff ( ~80 dB stop) 8 */ + { 64, 8, 0.921f, 0.940f, KAISER8 }, /* Q4 */ /* 88.7% cutoff ( ~80 dB stop) 8 */ + { 80, 16, 0.922f, 0.940f, KAISER10}, /* Q5 */ /* 89.1% cutoff (~100 dB stop) 10 */ + { 96, 16, 0.940f, 0.945f, KAISER10}, /* Q6 */ /* 91.5% cutoff (~100 dB stop) 10 */ + {128, 16, 0.950f, 0.950f, KAISER10}, /* Q7 */ /* 93.1% cutoff (~100 dB stop) 10 */ + {160, 16, 0.960f, 0.960f, KAISER10}, /* Q8 */ /* 94.5% cutoff (~100 dB stop) 10 */ + {192, 32, 0.968f, 0.968f, KAISER12}, /* Q9 */ /* 95.5% cutoff (~100 dB stop) 10 */ + {256, 32, 0.975f, 0.975f, KAISER12}, /* Q10 */ /* 96.6% cutoff (~100 dB stop) 10 */ +}; +/*8,24,40,56,80,104,128,160,200,256,320*/ +static double compute_func(float x, struct FuncDef *func) +{ + float y, frac; + double interp[4]; + int ind; + y = x*func->oversample; + ind = (int)floor(y); + frac = (y-ind); + /* CSE with handle the repeated powers */ + interp[3] = -0.1666666667*frac + 0.1666666667*(frac*frac*frac); + interp[2] = frac + 0.5*(frac*frac) - 0.5*(frac*frac*frac); + /*interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;*/ + interp[0] = -0.3333333333*frac + 0.5*(frac*frac) - 0.1666666667*(frac*frac*frac); + /* Just to make sure we don't have rounding problems */ + interp[1] = 1.f-interp[3]-interp[2]-interp[0]; + + /*sum = frac*accum[1] + (1-frac)*accum[2];*/ + return interp[0]*func->table[ind] + interp[1]*func->table[ind+1] + interp[2]*func->table[ind+2] + interp[3]*func->table[ind+3]; +} + +#if 0 +#include <stdio.h> +int main(int argc, char **argv) +{ + int i; + for (i=0;i<256;i++) + { + printf ("%f\n", compute_func(i/256., KAISER12)); + } + return 0; +} +#endif + +#ifdef FIXED_POINT +/* The slow way of computing a sinc for the table. Should improve that some day */ +static spx_word16_t sinc(float cutoff, float x, int N, struct FuncDef *window_func) +{ + /*fprintf (stderr, "%f ", x);*/ + float xx = x * cutoff; + if (fabs(x)<1e-6f) + return WORD2INT(32768.*cutoff); + else if (fabs(x) > .5f*N) + return 0; + /*FIXME: Can it really be any slower than this? */ + return WORD2INT(32768.*cutoff*sin(M_PI*xx)/(M_PI*xx) * compute_func(fabs(2.*x/N), window_func)); +} +#else +/* The slow way of computing a sinc for the table. Should improve that some day */ +static spx_word16_t sinc(float cutoff, float x, int N, struct FuncDef *window_func) +{ + /*fprintf (stderr, "%f ", x);*/ + float xx = x * cutoff; + if (fabs(x)<1e-6) + return cutoff; + else if (fabs(x) > .5*N) + return 0; + /*FIXME: Can it really be any slower than this? */ + return cutoff*sin(M_PI*xx)/(M_PI*xx) * compute_func(fabs(2.*x/N), window_func); +} +#endif + +#ifdef FIXED_POINT +static void cubic_coef(spx_word16_t x, spx_word16_t interp[4]) +{ + /* Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation + but I know it's MMSE-optimal on a sinc */ + spx_word16_t x2, x3; + x2 = MULT16_16_P15(x, x); + x3 = MULT16_16_P15(x, x2); + interp[0] = PSHR32(MULT16_16(QCONST16(-0.16667f, 15),x) + MULT16_16(QCONST16(0.16667f, 15),x3),15); + interp[1] = EXTRACT16(EXTEND32(x) + SHR32(SUB32(EXTEND32(x2),EXTEND32(x3)),1)); + interp[3] = PSHR32(MULT16_16(QCONST16(-0.33333f, 15),x) + MULT16_16(QCONST16(.5f,15),x2) - MULT16_16(QCONST16(0.16667f, 15),x3),15); + /* Just to make sure we don't have rounding problems */ + interp[2] = Q15_ONE-interp[0]-interp[1]-interp[3]; + if (interp[2]<32767) + interp[2]+=1; +} +#else +static void cubic_coef(spx_word16_t frac, spx_word16_t interp[4]) +{ + /* Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation + but I know it's MMSE-optimal on a sinc */ + interp[0] = -0.16667f*frac + 0.16667f*frac*frac*frac; + interp[1] = frac + 0.5f*frac*frac - 0.5f*frac*frac*frac; + /*interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;*/ + interp[3] = -0.33333f*frac + 0.5f*frac*frac - 0.16667f*frac*frac*frac; + /* Just to make sure we don't have rounding problems */ + interp[2] = 1.-interp[0]-interp[1]-interp[3]; +} +#endif + +static int resampler_basic_direct_single(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) +{ + const int N = st->filt_len; + int out_sample = 0; + int last_sample = st->last_sample[channel_index]; + spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index]; + const spx_word16_t *sinc_table = st->sinc_table; + const int out_stride = st->out_stride; + const int int_advance = st->int_advance; + const int frac_advance = st->frac_advance; + const spx_uint32_t den_rate = st->den_rate; + spx_word32_t sum; + int j; + + while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len)) + { + const spx_word16_t *sinc = & sinc_table[samp_frac_num*N]; + const spx_word16_t *iptr = & in[last_sample]; + +#ifndef OVERRIDE_INNER_PRODUCT_SINGLE + float accum[4] = {0,0,0,0}; + + for(j=0;j<N;j+=4) { + accum[0] += sinc[j]*iptr[j]; + accum[1] += sinc[j+1]*iptr[j+1]; + accum[2] += sinc[j+2]*iptr[j+2]; + accum[3] += sinc[j+3]*iptr[j+3]; + } + sum = accum[0] + accum[1] + accum[2] + accum[3]; +#else + sum = inner_product_single(sinc, iptr, N); +#endif + + out[out_stride * out_sample++] = PSHR32(sum, 15); + last_sample += int_advance; + samp_frac_num += frac_advance; + if (samp_frac_num >= den_rate) + { + samp_frac_num -= den_rate; + last_sample++; + } + } + + st->last_sample[channel_index] = last_sample; + st->samp_frac_num[channel_index] = samp_frac_num; + return out_sample; +} + +#ifdef FIXED_POINT +#else +/* This is the same as the previous function, except with a double-precision accumulator */ +static int resampler_basic_direct_double(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) +{ + const int N = st->filt_len; + int out_sample = 0; + int last_sample = st->last_sample[channel_index]; + spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index]; + const spx_word16_t *sinc_table = st->sinc_table; + const int out_stride = st->out_stride; + const int int_advance = st->int_advance; + const int frac_advance = st->frac_advance; + const spx_uint32_t den_rate = st->den_rate; + double sum; + int j; + + while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len)) + { + const spx_word16_t *sinc = & sinc_table[samp_frac_num*N]; + const spx_word16_t *iptr = & in[last_sample]; + +#ifndef OVERRIDE_INNER_PRODUCT_DOUBLE + double accum[4] = {0,0,0,0}; + + for(j=0;j<N;j+=4) { + accum[0] += sinc[j]*iptr[j]; + accum[1] += sinc[j+1]*iptr[j+1]; + accum[2] += sinc[j+2]*iptr[j+2]; + accum[3] += sinc[j+3]*iptr[j+3]; + } + sum = accum[0] + accum[1] + accum[2] + accum[3]; +#else + sum = inner_product_double(sinc, iptr, N); +#endif + + out[out_stride * out_sample++] = PSHR32(sum, 15); + last_sample += int_advance; + samp_frac_num += frac_advance; + if (samp_frac_num >= den_rate) + { + samp_frac_num -= den_rate; + last_sample++; + } + } + + st->last_sample[channel_index] = last_sample; + st->samp_frac_num[channel_index] = samp_frac_num; + return out_sample; +} +#endif + +static int resampler_basic_interpolate_single(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) +{ + const int N = st->filt_len; + int out_sample = 0; + int last_sample = st->last_sample[channel_index]; + spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index]; + const int out_stride = st->out_stride; + const int int_advance = st->int_advance; + const int frac_advance = st->frac_advance; + const spx_uint32_t den_rate = st->den_rate; + int j; + spx_word32_t sum; + + while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len)) + { + const spx_word16_t *iptr = & in[last_sample]; + + const int offset = samp_frac_num*st->oversample/st->den_rate; +#ifdef FIXED_POINT + const spx_word16_t frac = PDIV32(SHL32((samp_frac_num*st->oversample) % st->den_rate,15),st->den_rate); +#else + const spx_word16_t frac = ((float)((samp_frac_num*st->oversample) % st->den_rate))/st->den_rate; +#endif + spx_word16_t interp[4]; + + +#ifndef OVERRIDE_INTERPOLATE_PRODUCT_SINGLE + spx_word32_t accum[4] = {0,0,0,0}; + + for(j=0;j<N;j++) { + const spx_word16_t curr_in=iptr[j]; + accum[0] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-2]); + accum[1] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-1]); + accum[2] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset]); + accum[3] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset+1]); + } + + cubic_coef(frac, interp); + sum = MULT16_32_Q15(interp[0],accum[0]) + MULT16_32_Q15(interp[1],accum[1]) + MULT16_32_Q15(interp[2],accum[2]) + MULT16_32_Q15(interp[3],accum[3]); +#else + cubic_coef(frac, interp); + sum = interpolate_product_single(iptr, st->sinc_table + st->oversample + 4 - offset - 2, N, st->oversample, interp); +#endif + + out[out_stride * out_sample++] = PSHR32(sum,15); + last_sample += int_advance; + samp_frac_num += frac_advance; + if (samp_frac_num >= den_rate) + { + samp_frac_num -= den_rate; + last_sample++; + } + } + + st->last_sample[channel_index] = last_sample; + st->samp_frac_num[channel_index] = samp_frac_num; + return out_sample; +} + +#ifdef FIXED_POINT +#else +/* This is the same as the previous function, except with a double-precision accumulator */ +static int resampler_basic_interpolate_double(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) +{ + const int N = st->filt_len; + int out_sample = 0; + int last_sample = st->last_sample[channel_index]; + spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index]; + const int out_stride = st->out_stride; + const int int_advance = st->int_advance; + const int frac_advance = st->frac_advance; + const spx_uint32_t den_rate = st->den_rate; + int j; + spx_word32_t sum; + + while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len)) + { + const spx_word16_t *iptr = & in[last_sample]; + + const int offset = samp_frac_num*st->oversample/st->den_rate; +#ifdef FIXED_POINT + const spx_word16_t frac = PDIV32(SHL32((samp_frac_num*st->oversample) % st->den_rate,15),st->den_rate); +#else + const spx_word16_t frac = ((float)((samp_frac_num*st->oversample) % st->den_rate))/st->den_rate; +#endif + spx_word16_t interp[4]; + + +#ifndef OVERRIDE_INTERPOLATE_PRODUCT_DOUBLE + double accum[4] = {0,0,0,0}; + + for(j=0;j<N;j++) { + const double curr_in=iptr[j]; + accum[0] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-2]); + accum[1] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-1]); + accum[2] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset]); + accum[3] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset+1]); + } + + cubic_coef(frac, interp); + sum = MULT16_32_Q15(interp[0],accum[0]) + MULT16_32_Q15(interp[1],accum[1]) + MULT16_32_Q15(interp[2],accum[2]) + MULT16_32_Q15(interp[3],accum[3]); +#else + cubic_coef(frac, interp); + sum = interpolate_product_double(iptr, st->sinc_table + st->oversample + 4 - offset - 2, N, st->oversample, interp); +#endif + + out[out_stride * out_sample++] = PSHR32(sum,15); + last_sample += int_advance; + samp_frac_num += frac_advance; + if (samp_frac_num >= den_rate) + { + samp_frac_num -= den_rate; + last_sample++; + } + } + + st->last_sample[channel_index] = last_sample; + st->samp_frac_num[channel_index] = samp_frac_num; + return out_sample; +} +#endif + +static void update_filter(SpeexResamplerState *st) +{ + spx_uint32_t old_length; + + old_length = st->filt_len; + st->oversample = quality_map[st->quality].oversample; + st->filt_len = quality_map[st->quality].base_length; + + if (st->num_rate > st->den_rate) + { + /* down-sampling */ + st->cutoff = quality_map[st->quality].downsample_bandwidth * st->den_rate / st->num_rate; + /* FIXME: divide the numerator and denominator by a certain amount if they're too large */ + st->filt_len = st->filt_len*st->num_rate / st->den_rate; + /* Round down to make sure we have a multiple of 4 */ + st->filt_len &= (~0x3); + if (2*st->den_rate < st->num_rate) + st->oversample >>= 1; + if (4*st->den_rate < st->num_rate) + st->oversample >>= 1; + if (8*st->den_rate < st->num_rate) + st->oversample >>= 1; + if (16*st->den_rate < st->num_rate) + st->oversample >>= 1; + if (st->oversample < 1) + st->oversample = 1; + } else { + /* up-sampling */ + st->cutoff = quality_map[st->quality].upsample_bandwidth; + } + + /* Choose the resampling type that requires the least amount of memory */ + if (st->den_rate <= st->oversample) + { + spx_uint32_t i; + if (!st->sinc_table) + st->sinc_table = (spx_word16_t *)speex_alloc(st->filt_len*st->den_rate*sizeof(spx_word16_t)); + else if (st->sinc_table_length < st->filt_len*st->den_rate) + { + st->sinc_table = (spx_word16_t *)speex_realloc(st->sinc_table,st->filt_len*st->den_rate*sizeof(spx_word16_t)); + st->sinc_table_length = st->filt_len*st->den_rate; + } + for (i=0;i<st->den_rate;i++) + { + spx_int32_t j; + for (j=0;j<st->filt_len;j++) + { + st->sinc_table[i*st->filt_len+j] = sinc(st->cutoff,((j-(spx_int32_t)st->filt_len/2+1)-((float)i)/st->den_rate), st->filt_len, quality_map[st->quality].window_func); + } + } +#ifdef FIXED_POINT + st->resampler_ptr = resampler_basic_direct_single; +#else + if (st->quality>8) + st->resampler_ptr = resampler_basic_direct_double; + else + st->resampler_ptr = resampler_basic_direct_single; +#endif + /*fprintf (stderr, "resampler uses direct sinc table and normalised cutoff %f\n", cutoff);*/ + } else { + spx_int32_t i; + if (!st->sinc_table) + st->sinc_table = (spx_word16_t *)speex_alloc((st->filt_len*st->oversample+8)*sizeof(spx_word16_t)); + else if (st->sinc_table_length < st->filt_len*st->oversample+8) + { + st->sinc_table = (spx_word16_t *)speex_realloc(st->sinc_table,(st->filt_len*st->oversample+8)*sizeof(spx_word16_t)); + st->sinc_table_length = st->filt_len*st->oversample+8; + } + for (i=-4;i<(spx_int32_t)(st->oversample*st->filt_len+4);i++) + st->sinc_table[i+4] = sinc(st->cutoff,(i/(float)st->oversample - st->filt_len/2), st->filt_len, quality_map[st->quality].window_func); +#ifdef FIXED_POINT + st->resampler_ptr = resampler_basic_interpolate_single; +#else + if (st->quality>8) + st->resampler_ptr = resampler_basic_interpolate_double; + else + st->resampler_ptr = resampler_basic_interpolate_single; +#endif + /*fprintf (stderr, "resampler uses interpolated sinc table and normalised cutoff %f\n", cutoff);*/ + } + st->int_advance = st->num_rate/st->den_rate; + st->frac_advance = st->num_rate%st->den_rate; + + + /* Here's the place where we update the filter memory to take into account + the change in filter length. It's probably the messiest part of the code + due to handling of lots of corner cases. */ + if (!st->mem) + { + spx_uint32_t i; + st->mem_alloc_size = st->filt_len-1 + st->buffer_size; + st->mem = (spx_word16_t*)speex_alloc(st->nb_channels*st->mem_alloc_size * sizeof(spx_word16_t)); + for (i=0;i<st->nb_channels*st->mem_alloc_size;i++) + st->mem[i] = 0; + /*speex_warning("init filter");*/ + } else if (!st->started) + { + spx_uint32_t i; + st->mem_alloc_size = st->filt_len-1 + st->buffer_size; + st->mem = (spx_word16_t*)speex_realloc(st->mem, st->nb_channels*st->mem_alloc_size * sizeof(spx_word16_t)); + for (i=0;i<st->nb_channels*st->mem_alloc_size;i++) + st->mem[i] = 0; + /*speex_warning("reinit filter");*/ + } else if (st->filt_len > old_length) + { + spx_int32_t i; + /* Increase the filter length */ + /*speex_warning("increase filter size");*/ + int old_alloc_size = st->mem_alloc_size; + if ((st->filt_len-1 + st->buffer_size) > st->mem_alloc_size) + { + st->mem_alloc_size = st->filt_len-1 + st->buffer_size; + st->mem = (spx_word16_t*)speex_realloc(st->mem, st->nb_channels*st->mem_alloc_size * sizeof(spx_word16_t)); + } + for (i=st->nb_channels-1;i>=0;i--) + { + spx_int32_t j; + spx_uint32_t olen = old_length; + /*if (st->magic_samples[i])*/ + { + /* Try and remove the magic samples as if nothing had happened */ + + /* FIXME: This is wrong but for now we need it to avoid going over the array bounds */ + olen = old_length + 2*st->magic_samples[i]; + for (j=old_length-2+st->magic_samples[i];j>=0;j--) + st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]] = st->mem[i*old_alloc_size+j]; + for (j=0;j<st->magic_samples[i];j++) + st->mem[i*st->mem_alloc_size+j] = 0; + st->magic_samples[i] = 0; + } + if (st->filt_len > olen) + { + /* If the new filter length is still bigger than the "augmented" length */ + /* Copy data going backward */ + for (j=0;j<olen-1;j++) + st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = st->mem[i*st->mem_alloc_size+(olen-2-j)]; + /* Then put zeros for lack of anything better */ + for (;j<st->filt_len-1;j++) + st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = 0; + /* Adjust last_sample */ + st->last_sample[i] += (st->filt_len - olen)/2; + } else { + /* Put back some of the magic! */ + st->magic_samples[i] = (olen - st->filt_len)/2; + for (j=0;j<st->filt_len-1+st->magic_samples[i];j++) + st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]]; + } + } + } else if (st->filt_len < old_length) + { + spx_uint32_t i; + /* Reduce filter length, this a bit tricky. We need to store some of the memory as "magic" + samples so they can be used directly as input the next time(s) */ + for (i=0;i<st->nb_channels;i++) + { + spx_uint32_t j; + spx_uint32_t old_magic = st->magic_samples[i]; + st->magic_samples[i] = (old_length - st->filt_len)/2; + /* We must copy some of the memory that's no longer used */ + /* Copy data going backward */ + for (j=0;j<st->filt_len-1+st->magic_samples[i]+old_magic;j++) + st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]]; + st->magic_samples[i] += old_magic; + } + } + +} + + SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err) +{ + return speex_resampler_init_frac(nb_channels, in_rate, out_rate, in_rate, out_rate, quality, err); +} + + SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, spx_uint32_t ratio_num, spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err) +{ + spx_uint32_t i; + SpeexResamplerState *st; + if (quality > 10 || quality < 0) + { + if (err) + *err = RESAMPLER_ERR_INVALID_ARG; + return NULL; + } + st = (SpeexResamplerState *)speex_alloc(sizeof(SpeexResamplerState)); + st->initialised = 0; + st->started = 0; + st->in_rate = 0; + st->out_rate = 0; + st->num_rate = 0; + st->den_rate = 0; + st->quality = -1; + st->sinc_table_length = 0; + st->mem_alloc_size = 0; + st->filt_len = 0; + st->mem = 0; + st->resampler_ptr = 0; + + st->cutoff = 1.f; + st->nb_channels = nb_channels; + st->in_stride = 1; + st->out_stride = 1; + +#ifdef FIXED_POINT + st->buffer_size = 160; +#else + st->buffer_size = 160; +#endif + + /* Per channel data */ + st->last_sample = (spx_int32_t*)speex_alloc(nb_channels*sizeof(int)); + st->magic_samples = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(int)); + st->samp_frac_num = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(int)); + for (i=0;i<nb_channels;i++) + { + st->last_sample[i] = 0; + st->magic_samples[i] = 0; + st->samp_frac_num[i] = 0; + } + + speex_resampler_set_quality(st, quality); + speex_resampler_set_rate_frac(st, ratio_num, ratio_den, in_rate, out_rate); + + + update_filter(st); + + st->initialised = 1; + if (err) + *err = RESAMPLER_ERR_SUCCESS; + + return st; +} + + void speex_resampler_destroy(SpeexResamplerState *st) +{ + speex_free(st->mem); + speex_free(st->sinc_table); + speex_free(st->last_sample); + speex_free(st->magic_samples); + speex_free(st->samp_frac_num); + speex_free(st); +} + +static int speex_resampler_process_native(SpeexResamplerState *st, spx_uint32_t channel_index, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) +{ + int j=0; + const int N = st->filt_len; + int out_sample = 0; + spx_word16_t *mem = st->mem + channel_index * st->mem_alloc_size; + spx_uint32_t ilen; + + st->started = 1; + + /* Call the right resampler through the function ptr */ + out_sample = st->resampler_ptr(st, channel_index, mem, in_len, out, out_len); + + if (st->last_sample[channel_index] < (spx_int32_t)*in_len) + *in_len = st->last_sample[channel_index]; + *out_len = out_sample; + st->last_sample[channel_index] -= *in_len; + + ilen = *in_len; + + for(j=0;j<N-1;++j) + mem[j] = mem[j+ilen]; + + return RESAMPLER_ERR_SUCCESS; +} + +static int speex_resampler_magic(SpeexResamplerState *st, spx_uint32_t channel_index, spx_word16_t **out, spx_uint32_t out_len) { + spx_uint32_t tmp_in_len = st->magic_samples[channel_index]; + spx_word16_t *mem = st->mem + channel_index * st->mem_alloc_size; + const int N = st->filt_len; + + speex_resampler_process_native(st, channel_index, &tmp_in_len, *out, &out_len); + + st->magic_samples[channel_index] -= tmp_in_len; + + /* If we couldn't process all "magic" input samples, save the rest for next time */ + if (st->magic_samples[channel_index]) + { + spx_uint32_t i; + for (i=0;i<st->magic_samples[channel_index];i++) + mem[N-1+i]=mem[N-1+i+tmp_in_len]; + } + *out += out_len*st->out_stride; + return out_len; +} + +#ifdef FIXED_POINT + int speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len) +#else + int speex_resampler_process_float(SpeexResamplerState *st, spx_uint32_t channel_index, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len) +#endif +{ + int j; + spx_uint32_t ilen = *in_len; + spx_uint32_t olen = *out_len; + spx_word16_t *x = st->mem + channel_index * st->mem_alloc_size; + const int filt_offs = st->filt_len - 1; + const spx_uint32_t xlen = st->mem_alloc_size - filt_offs; + const int istride = st->in_stride; + + if (st->magic_samples[channel_index]) + olen -= speex_resampler_magic(st, channel_index, &out, olen); + if (! st->magic_samples[channel_index]) { + while (ilen && olen) { + spx_uint32_t ichunk = (ilen > xlen) ? xlen : ilen; + spx_uint32_t ochunk = olen; + + if (in) { + for(j=0;j<ichunk;++j) + x[j+filt_offs]=in[j*istride]; + } else { + for(j=0;j<ichunk;++j) + x[j+filt_offs]=0; + } + speex_resampler_process_native(st, channel_index, &ichunk, out, &ochunk); + ilen -= ichunk; + olen -= ochunk; + out += ochunk * st->out_stride; + if (in) + in += ichunk * istride; + } + } + *in_len -= ilen; + *out_len -= olen; + return RESAMPLER_ERR_SUCCESS; +} + +#ifdef FIXED_POINT + int speex_resampler_process_float(SpeexResamplerState *st, spx_uint32_t channel_index, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len) +#else + int speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len) +#endif +{ + int j; + const int istride_save = st->in_stride; + const int ostride_save = st->out_stride; + spx_uint32_t ilen = *in_len; + spx_uint32_t olen = *out_len; + spx_word16_t *x = st->mem + channel_index * st->mem_alloc_size; + const spx_uint32_t xlen = st->mem_alloc_size - (st->filt_len - 1); +#ifdef VAR_ARRAYS + const unsigned int ylen = (olen < FIXED_STACK_ALLOC) ? olen : FIXED_STACK_ALLOC; + VARDECL(spx_word16_t *ystack); + ALLOC(ystack, ylen, spx_word16_t); +#else + const unsigned int ylen = FIXED_STACK_ALLOC; + spx_word16_t ystack[FIXED_STACK_ALLOC]; +#endif + + st->out_stride = 1; + + while (ilen && olen) { + spx_word16_t *y = ystack; + spx_uint32_t ichunk = (ilen > xlen) ? xlen : ilen; + spx_uint32_t ochunk = (olen > ylen) ? ylen : olen; + spx_uint32_t omagic = 0; + + if (st->magic_samples[channel_index]) { + omagic = speex_resampler_magic(st, channel_index, &y, ochunk); + ochunk -= omagic; + olen -= omagic; + } + if (! st->magic_samples[channel_index]) { + if (in) { + for(j=0;j<ichunk;++j) +#ifdef FIXED_POINT + x[j+st->filt_len-1]=WORD2INT(in[j*istride_save]); +#else + x[j+st->filt_len-1]=in[j*istride_save]; +#endif + } else { + for(j=0;j<ichunk;++j) + x[j+st->filt_len-1]=0; + } + + speex_resampler_process_native(st, channel_index, &ichunk, y, &ochunk); + } else { + ichunk = 0; + ochunk = 0; + } + + for (j=0;j<ochunk+omagic;++j) +#ifdef FIXED_POINT + out[j*ostride_save] = ystack[j]; +#else + out[j*ostride_save] = WORD2INT(ystack[j]); +#endif + + ilen -= ichunk; + olen -= ochunk; + out += (ochunk+omagic) * ostride_save; + if (in) + in += ichunk * istride_save; + } + st->out_stride = ostride_save; + *in_len -= ilen; + *out_len -= olen; + + return RESAMPLER_ERR_SUCCESS; +} + + int speex_resampler_process_interleaved_float(SpeexResamplerState *st, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len) +{ + spx_uint32_t i; + int istride_save, ostride_save; + spx_uint32_t bak_len = *out_len; + istride_save = st->in_stride; + ostride_save = st->out_stride; + st->in_stride = st->out_stride = st->nb_channels; + for (i=0;i<st->nb_channels;i++) + { + *out_len = bak_len; + if (in != NULL) + speex_resampler_process_float(st, i, in+i, in_len, out+i, out_len); + else + speex_resampler_process_float(st, i, NULL, in_len, out+i, out_len); + } + st->in_stride = istride_save; + st->out_stride = ostride_save; + return RESAMPLER_ERR_SUCCESS; +} + + int speex_resampler_process_interleaved_int(SpeexResamplerState *st, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len) +{ + spx_uint32_t i; + int istride_save, ostride_save; + spx_uint32_t bak_len = *out_len; + istride_save = st->in_stride; + ostride_save = st->out_stride; + st->in_stride = st->out_stride = st->nb_channels; + for (i=0;i<st->nb_channels;i++) + { + *out_len = bak_len; + if (in != NULL) + speex_resampler_process_int(st, i, in+i, in_len, out+i, out_len); + else + speex_resampler_process_int(st, i, NULL, in_len, out+i, out_len); + } + st->in_stride = istride_save; + st->out_stride = ostride_save; + return RESAMPLER_ERR_SUCCESS; +} + + int speex_resampler_set_rate(SpeexResamplerState *st, spx_uint32_t in_rate, spx_uint32_t out_rate) +{ + return speex_resampler_set_rate_frac(st, in_rate, out_rate, in_rate, out_rate); +} + + void speex_resampler_get_rate(SpeexResamplerState *st, spx_uint32_t *in_rate, spx_uint32_t *out_rate) +{ + *in_rate = st->in_rate; + *out_rate = st->out_rate; +} + + int speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t ratio_num, spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate) +{ + spx_uint32_t fact; + spx_uint32_t old_den; + spx_uint32_t i; + if (st->in_rate == in_rate && st->out_rate == out_rate && st->num_rate == ratio_num && st->den_rate == ratio_den) + return RESAMPLER_ERR_SUCCESS; + + old_den = st->den_rate; + st->in_rate = in_rate; + st->out_rate = out_rate; + st->num_rate = ratio_num; + st->den_rate = ratio_den; + /* FIXME: This is terribly inefficient, but who cares (at least for now)? */ + for (fact=2;fact<=IMIN(st->num_rate, st->den_rate);fact++) + { + while ((st->num_rate % fact == 0) && (st->den_rate % fact == 0)) + { + st->num_rate /= fact; + st->den_rate /= fact; + } + } + + if (old_den > 0) + { + for (i=0;i<st->nb_channels;i++) + { + st->samp_frac_num[i]=st->samp_frac_num[i]*st->den_rate/old_den; + /* Safety net */ + if (st->samp_frac_num[i] >= st->den_rate) + st->samp_frac_num[i] = st->den_rate-1; + } + } + + if (st->initialised) + update_filter(st); + return RESAMPLER_ERR_SUCCESS; +} + + void speex_resampler_get_ratio(SpeexResamplerState *st, spx_uint32_t *ratio_num, spx_uint32_t *ratio_den) +{ + *ratio_num = st->num_rate; + *ratio_den = st->den_rate; +} + + int speex_resampler_set_quality(SpeexResamplerState *st, int quality) +{ + if (quality > 10 || quality < 0) + return RESAMPLER_ERR_INVALID_ARG; + if (st->quality == quality) + return RESAMPLER_ERR_SUCCESS; + st->quality = quality; + if (st->initialised) + update_filter(st); + return RESAMPLER_ERR_SUCCESS; +} + + void speex_resampler_get_quality(SpeexResamplerState *st, int *quality) +{ + *quality = st->quality; +} + + void speex_resampler_set_input_stride(SpeexResamplerState *st, spx_uint32_t stride) +{ + st->in_stride = stride; +} + + void speex_resampler_get_input_stride(SpeexResamplerState *st, spx_uint32_t *stride) +{ + *stride = st->in_stride; +} + + void speex_resampler_set_output_stride(SpeexResamplerState *st, spx_uint32_t stride) +{ + st->out_stride = stride; +} + + void speex_resampler_get_output_stride(SpeexResamplerState *st, spx_uint32_t *stride) +{ + *stride = st->out_stride; +} + + int speex_resampler_get_input_latency(SpeexResamplerState *st) +{ + return st->filt_len / 2; +} + + int speex_resampler_get_output_latency(SpeexResamplerState *st) +{ + return ((st->filt_len / 2) * st->den_rate + (st->num_rate >> 1)) / st->num_rate; +} + + int speex_resampler_skip_zeros(SpeexResamplerState *st) +{ + spx_uint32_t i; + for (i=0;i<st->nb_channels;i++) + st->last_sample[i] = st->filt_len/2; + return RESAMPLER_ERR_SUCCESS; +} + + int speex_resampler_reset_mem(SpeexResamplerState *st) +{ + spx_uint32_t i; + for (i=0;i<st->nb_channels*(st->filt_len-1);i++) + st->mem[i] = 0; + return RESAMPLER_ERR_SUCCESS; +} + + const char *speex_resampler_strerror(int err) +{ + switch (err) + { + case RESAMPLER_ERR_SUCCESS: + return "Success."; + case RESAMPLER_ERR_ALLOC_FAILED: + return "Memory allocation failed."; + case RESAMPLER_ERR_BAD_STATE: + return "Bad resampler state."; + case RESAMPLER_ERR_INVALID_ARG: + return "Invalid argument."; + case RESAMPLER_ERR_PTR_OVERLAP: + return "Input and output buffers overlap."; + default: + return "Unknown error. Bad error code or strange version mismatch."; + } +} diff --git a/codecs/speex/resample_sse.h b/codecs/speex/resample_sse.h new file mode 100644 index 000000000..4bd35a2d0 --- /dev/null +++ b/codecs/speex/resample_sse.h @@ -0,0 +1,128 @@ +/* Copyright (C) 2007-2008 Jean-Marc Valin + * Copyright (C) 2008 Thorvald Natvig + */ +/** + @file resample_sse.h + @brief Resampler functions (SSE version) +*/ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include <xmmintrin.h> + +#define OVERRIDE_INNER_PRODUCT_SINGLE +static inline float inner_product_single(const float *a, const float *b, unsigned int len) +{ + int i; + float ret; + __m128 sum = _mm_setzero_ps(); + for (i=0;i<len;i+=8) + { + sum = _mm_add_ps(sum, _mm_mul_ps(_mm_loadu_ps(a+i), _mm_loadu_ps(b+i))); + sum = _mm_add_ps(sum, _mm_mul_ps(_mm_loadu_ps(a+i+4), _mm_loadu_ps(b+i+4))); + } + sum = _mm_add_ps(sum, _mm_movehl_ps(sum, sum)); + sum = _mm_add_ss(sum, _mm_shuffle_ps(sum, sum, 0x55)); + _mm_store_ss(&ret, sum); + return ret; +} + +#define OVERRIDE_INTERPOLATE_PRODUCT_SINGLE +static inline float interpolate_product_single(const float *a, const float *b, unsigned int len, const spx_uint32_t oversample, float *frac) { + int i; + float ret; + __m128 sum = _mm_setzero_ps(); + __m128 f = _mm_loadu_ps(frac); + for(i=0;i<len;i+=2) + { + sum = _mm_add_ps(sum, _mm_mul_ps(_mm_load1_ps(a+i), _mm_loadu_ps(b+i*oversample))); + sum = _mm_add_ps(sum, _mm_mul_ps(_mm_load1_ps(a+i+1), _mm_loadu_ps(b+(i+1)*oversample))); + } + sum = _mm_mul_ps(f, sum); + sum = _mm_add_ps(sum, _mm_movehl_ps(sum, sum)); + sum = _mm_add_ss(sum, _mm_shuffle_ps(sum, sum, 0x55)); + _mm_store_ss(&ret, sum); + return ret; +} + +#ifdef _USE_SSE2 +#include <emmintrin.h> +#define OVERRIDE_INNER_PRODUCT_DOUBLE + +static inline double inner_product_double(const float *a, const float *b, unsigned int len) +{ + int i; + double ret; + __m128d sum = _mm_setzero_pd(); + __m128 t; + for (i=0;i<len;i+=8) + { + t = _mm_mul_ps(_mm_loadu_ps(a+i), _mm_loadu_ps(b+i)); + sum = _mm_add_pd(sum, _mm_cvtps_pd(t)); + sum = _mm_add_pd(sum, _mm_cvtps_pd(_mm_movehl_ps(t, t))); + + t = _mm_mul_ps(_mm_loadu_ps(a+i+4), _mm_loadu_ps(b+i+4)); + sum = _mm_add_pd(sum, _mm_cvtps_pd(t)); + sum = _mm_add_pd(sum, _mm_cvtps_pd(_mm_movehl_ps(t, t))); + } + sum = _mm_add_sd(sum, (__m128d) _mm_movehl_ps((__m128) sum, (__m128) sum)); + _mm_store_sd(&ret, sum); + return ret; +} + +#define OVERRIDE_INTERPOLATE_PRODUCT_DOUBLE +static inline double interpolate_product_double(const float *a, const float *b, unsigned int len, const spx_uint32_t oversample, float *frac) { + int i; + double ret; + __m128d sum; + __m128d sum1 = _mm_setzero_pd(); + __m128d sum2 = _mm_setzero_pd(); + __m128 f = _mm_loadu_ps(frac); + __m128d f1 = _mm_cvtps_pd(f); + __m128d f2 = _mm_cvtps_pd(_mm_movehl_ps(f,f)); + __m128 t; + for(i=0;i<len;i+=2) + { + t = _mm_mul_ps(_mm_load1_ps(a+i), _mm_loadu_ps(b+i*oversample)); + sum1 = _mm_add_pd(sum1, _mm_cvtps_pd(t)); + sum2 = _mm_add_pd(sum2, _mm_cvtps_pd(_mm_movehl_ps(t, t))); + + t = _mm_mul_ps(_mm_load1_ps(a+i+1), _mm_loadu_ps(b+(i+1)*oversample)); + sum1 = _mm_add_pd(sum1, _mm_cvtps_pd(t)); + sum2 = _mm_add_pd(sum2, _mm_cvtps_pd(_mm_movehl_ps(t, t))); + } + sum1 = _mm_mul_pd(f1, sum1); + sum2 = _mm_mul_pd(f2, sum2); + sum = _mm_add_pd(sum1, sum2); + sum = _mm_add_sd(sum, (__m128d) _mm_movehl_ps((__m128) sum, (__m128) sum)); + _mm_store_sd(&ret, sum); + return ret; +} + +#endif diff --git a/codecs/speex/speex_resampler.h b/codecs/speex/speex_resampler.h new file mode 100644 index 000000000..0247e180e --- /dev/null +++ b/codecs/speex/speex_resampler.h @@ -0,0 +1,342 @@ +/* Copyright (C) 2007 Jean-Marc Valin + + File: speex_resampler.h + Resampling code + + The design goals of this code are: + - Very fast algorithm + - Low memory requirement + - Good *perceptual* quality (and not best SNR) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + + +#ifndef SPEEX_RESAMPLER_H +#define SPEEX_RESAMPLER_H + +#define OUTSIDE_SPEEX + +#ifdef OUTSIDE_SPEEX + +/********* WARNING: MENTAL SANITY ENDS HERE *************/ + +/* If the resampler is defined outside of Speex, we change the symbol names so that + there won't be any clash if linking with Speex later on. */ + +#define RANDOM_PREFIX ast +#ifndef RANDOM_PREFIX +#error "Please define RANDOM_PREFIX (above) to something specific to your project to prevent symbol name clashes" +#endif + +#define CAT_PREFIX2(a,b) a ## b +#define CAT_PREFIX(a,b) CAT_PREFIX2(a, b) + +#define speex_resampler_init CAT_PREFIX(RANDOM_PREFIX,_resampler_init) +#define speex_resampler_init_frac CAT_PREFIX(RANDOM_PREFIX,_resampler_init_frac) +#define speex_resampler_destroy CAT_PREFIX(RANDOM_PREFIX,_resampler_destroy) +#define speex_resampler_process_float CAT_PREFIX(RANDOM_PREFIX,_resampler_process_float) +#define speex_resampler_process_int CAT_PREFIX(RANDOM_PREFIX,_resampler_process_int) +#define speex_resampler_process_interleaved_float CAT_PREFIX(RANDOM_PREFIX,_resampler_process_interleaved_float) +#define speex_resampler_process_interleaved_int CAT_PREFIX(RANDOM_PREFIX,_resampler_process_interleaved_int) +#define speex_resampler_set_rate CAT_PREFIX(RANDOM_PREFIX,_resampler_set_rate) +#define speex_resampler_get_rate CAT_PREFIX(RANDOM_PREFIX,_resampler_get_rate) +#define speex_resampler_set_rate_frac CAT_PREFIX(RANDOM_PREFIX,_resampler_set_rate_frac) +#define speex_resampler_get_ratio CAT_PREFIX(RANDOM_PREFIX,_resampler_get_ratio) +#define speex_resampler_set_quality CAT_PREFIX(RANDOM_PREFIX,_resampler_set_quality) +#define speex_resampler_get_quality CAT_PREFIX(RANDOM_PREFIX,_resampler_get_quality) +#define speex_resampler_set_input_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_set_input_stride) +#define speex_resampler_get_input_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_get_input_stride) +#define speex_resampler_set_output_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_set_output_stride) +#define speex_resampler_get_output_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_get_output_stride) +#define speex_resampler_get_input_latency CAT_PREFIX(RANDOM_PREFIX,_resampler_get_input_latency) +#define speex_resampler_get_output_latency CAT_PREFIX(RANDOM_PREFIX,_resampler_get_output_latency) +#define speex_resampler_skip_zeros CAT_PREFIX(RANDOM_PREFIX,_resampler_skip_zeros) +#define speex_resampler_reset_mem CAT_PREFIX(RANDOM_PREFIX,_resampler_reset_mem) +#define speex_resampler_strerror CAT_PREFIX(RANDOM_PREFIX,_resampler_strerror) + +#define spx_int16_t short +#define spx_int32_t int +#define spx_uint16_t unsigned short +#define spx_uint32_t unsigned int + +#else /* OUTSIDE_SPEEX */ + +#include "speex/speex_types.h" + +#endif /* OUTSIDE_SPEEX */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define SPEEX_RESAMPLER_QUALITY_MAX 10 +#define SPEEX_RESAMPLER_QUALITY_MIN 0 +#define SPEEX_RESAMPLER_QUALITY_DEFAULT 4 +#define SPEEX_RESAMPLER_QUALITY_VOIP 3 +#define SPEEX_RESAMPLER_QUALITY_DESKTOP 5 + +enum { + RESAMPLER_ERR_SUCCESS = 0, + RESAMPLER_ERR_ALLOC_FAILED = 1, + RESAMPLER_ERR_BAD_STATE = 2, + RESAMPLER_ERR_INVALID_ARG = 3, + RESAMPLER_ERR_PTR_OVERLAP = 4, + + RESAMPLER_ERR_MAX_ERROR +}; + +struct SpeexResamplerState_; +typedef struct SpeexResamplerState_ SpeexResamplerState; + +/** Create a new resampler with integer input and output rates. + * @param nb_channels Number of channels to be processed + * @param in_rate Input sampling rate (integer number of Hz). + * @param out_rate Output sampling rate (integer number of Hz). + * @param quality Resampling quality between 0 and 10, where 0 has poor quality + * and 10 has very high quality. + * @return Newly created resampler state + * @retval NULL Error: not enough memory + */ +SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels, + spx_uint32_t in_rate, + spx_uint32_t out_rate, + int quality, + int *err); + +/** Create a new resampler with fractional input/output rates. The sampling + * rate ratio is an arbitrary rational number with both the numerator and + * denominator being 32-bit integers. + * @param nb_channels Number of channels to be processed + * @param ratio_num Numerator of the sampling rate ratio + * @param ratio_den Denominator of the sampling rate ratio + * @param in_rate Input sampling rate rounded to the nearest integer (in Hz). + * @param out_rate Output sampling rate rounded to the nearest integer (in Hz). + * @param quality Resampling quality between 0 and 10, where 0 has poor quality + * and 10 has very high quality. + * @return Newly created resampler state + * @retval NULL Error: not enough memory + */ +SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, + spx_uint32_t ratio_num, + spx_uint32_t ratio_den, + spx_uint32_t in_rate, + spx_uint32_t out_rate, + int quality, + int *err); + +/** Destroy a resampler state. + * @param st Resampler state + */ +void speex_resampler_destroy(SpeexResamplerState *st); + +/** Resample a float array. The input and output buffers must *not* overlap. + * @param st Resampler state + * @param channel_index Index of the channel to process for the multi-channel + * base (0 otherwise) + * @param in Input buffer + * @param in_len Number of input samples in the input buffer. Returns the + * number of samples processed + * @param out Output buffer + * @param out_len Size of the output buffer. Returns the number of samples written + */ +int speex_resampler_process_float(SpeexResamplerState *st, + spx_uint32_t channel_index, + const float *in, + spx_uint32_t *in_len, + float *out, + spx_uint32_t *out_len); + +/** Resample an int array. The input and output buffers must *not* overlap. + * @param st Resampler state + * @param channel_index Index of the channel to process for the multi-channel + * base (0 otherwise) + * @param in Input buffer + * @param in_len Number of input samples in the input buffer. Returns the number + * of samples processed + * @param out Output buffer + * @param out_len Size of the output buffer. Returns the number of samples written + */ +int speex_resampler_process_int(SpeexResamplerState *st, + spx_uint32_t channel_index, + const spx_int16_t *in, + spx_uint32_t *in_len, + spx_int16_t *out, + spx_uint32_t *out_len); + +/** Resample an interleaved float array. The input and output buffers must *not* overlap. + * @param st Resampler state + * @param in Input buffer + * @param in_len Number of input samples in the input buffer. Returns the number + * of samples processed. This is all per-channel. + * @param out Output buffer + * @param out_len Size of the output buffer. Returns the number of samples written. + * This is all per-channel. + */ +int speex_resampler_process_interleaved_float(SpeexResamplerState *st, + const float *in, + spx_uint32_t *in_len, + float *out, + spx_uint32_t *out_len); + +/** Resample an interleaved int array. The input and output buffers must *not* overlap. + * @param st Resampler state + * @param in Input buffer + * @param in_len Number of input samples in the input buffer. Returns the number + * of samples processed. This is all per-channel. + * @param out Output buffer + * @param out_len Size of the output buffer. Returns the number of samples written. + * This is all per-channel. + */ +int speex_resampler_process_interleaved_int(SpeexResamplerState *st, + const spx_int16_t *in, + spx_uint32_t *in_len, + spx_int16_t *out, + spx_uint32_t *out_len); + +/** Set (change) the input/output sampling rates (integer value). + * @param st Resampler state + * @param in_rate Input sampling rate (integer number of Hz). + * @param out_rate Output sampling rate (integer number of Hz). + */ +int speex_resampler_set_rate(SpeexResamplerState *st, + spx_uint32_t in_rate, + spx_uint32_t out_rate); + +/** Get the current input/output sampling rates (integer value). + * @param st Resampler state + * @param in_rate Input sampling rate (integer number of Hz) copied. + * @param out_rate Output sampling rate (integer number of Hz) copied. + */ +void speex_resampler_get_rate(SpeexResamplerState *st, + spx_uint32_t *in_rate, + spx_uint32_t *out_rate); + +/** Set (change) the input/output sampling rates and resampling ratio + * (fractional values in Hz supported). + * @param st Resampler state + * @param ratio_num Numerator of the sampling rate ratio + * @param ratio_den Denominator of the sampling rate ratio + * @param in_rate Input sampling rate rounded to the nearest integer (in Hz). + * @param out_rate Output sampling rate rounded to the nearest integer (in Hz). + */ +int speex_resampler_set_rate_frac(SpeexResamplerState *st, + spx_uint32_t ratio_num, + spx_uint32_t ratio_den, + spx_uint32_t in_rate, + spx_uint32_t out_rate); + +/** Get the current resampling ratio. This will be reduced to the least + * common denominator. + * @param st Resampler state + * @param ratio_num Numerator of the sampling rate ratio copied + * @param ratio_den Denominator of the sampling rate ratio copied + */ +void speex_resampler_get_ratio(SpeexResamplerState *st, + spx_uint32_t *ratio_num, + spx_uint32_t *ratio_den); + +/** Set (change) the conversion quality. + * @param st Resampler state + * @param quality Resampling quality between 0 and 10, where 0 has poor + * quality and 10 has very high quality. + */ +int speex_resampler_set_quality(SpeexResamplerState *st, + int quality); + +/** Get the conversion quality. + * @param st Resampler state + * @param quality Resampling quality between 0 and 10, where 0 has poor + * quality and 10 has very high quality. + */ +void speex_resampler_get_quality(SpeexResamplerState *st, + int *quality); + +/** Set (change) the input stride. + * @param st Resampler state + * @param stride Input stride + */ +void speex_resampler_set_input_stride(SpeexResamplerState *st, + spx_uint32_t stride); + +/** Get the input stride. + * @param st Resampler state + * @param stride Input stride copied + */ +void speex_resampler_get_input_stride(SpeexResamplerState *st, + spx_uint32_t *stride); + +/** Set (change) the output stride. + * @param st Resampler state + * @param stride Output stride + */ +void speex_resampler_set_output_stride(SpeexResamplerState *st, + spx_uint32_t stride); + +/** Get the output stride. + * @param st Resampler state copied + * @param stride Output stride + */ +void speex_resampler_get_output_stride(SpeexResamplerState *st, + spx_uint32_t *stride); + +/** Get the latency in input samples introduced by the resampler. + * @param st Resampler state + */ +int speex_resampler_get_input_latency(SpeexResamplerState *st); + +/** Get the latency in output samples introduced by the resampler. + * @param st Resampler state + */ +int speex_resampler_get_output_latency(SpeexResamplerState *st); + +/** Make sure that the first samples to go out of the resamplers don't have + * leading zeros. This is only useful before starting to use a newly created + * resampler. It is recommended to use that when resampling an audio file, as + * it will generate a file with the same length. For real-time processing, + * it is probably easier not to use this call (so that the output duration + * is the same for the first frame). + * @param st Resampler state + */ +int speex_resampler_skip_zeros(SpeexResamplerState *st); + +/** Reset a resampler so a new (unrelated) stream can be processed. + * @param st Resampler state + */ +int speex_resampler_reset_mem(SpeexResamplerState *st); + +/** Returns the English meaning for an error code + * @param err Error code + * @return English string + */ +const char *speex_resampler_strerror(int err); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/codecs/speex/stack_alloc.h b/codecs/speex/stack_alloc.h new file mode 100644 index 000000000..5264e666b --- /dev/null +++ b/codecs/speex/stack_alloc.h @@ -0,0 +1,115 @@ +/* Copyright (C) 2002 Jean-Marc Valin */ +/** + @file stack_alloc.h + @brief Temporary memory allocation on stack +*/ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef STACK_ALLOC_H +#define STACK_ALLOC_H + +#ifdef USE_ALLOCA +# ifdef WIN32 +# include <malloc.h> +# else +# ifdef HAVE_ALLOCA_H +# include <alloca.h> +# else +# include <stdlib.h> +# endif +# endif +#endif + +/** + * @def ALIGN(stack, size) + * + * Aligns the stack to a 'size' boundary + * + * @param stack Stack + * @param size New size boundary + */ + +/** + * @def PUSH(stack, size, type) + * + * Allocates 'size' elements of type 'type' on the stack + * + * @param stack Stack + * @param size Number of elements + * @param type Type of element + */ + +/** + * @def VARDECL(var) + * + * Declare variable on stack + * + * @param var Variable to declare + */ + +/** + * @def ALLOC(var, size, type) + * + * Allocate 'size' elements of 'type' on stack + * + * @param var Name of variable to allocate + * @param size Number of elements + * @param type Type of element + */ + +#ifdef ENABLE_VALGRIND + +#include <valgrind/memcheck.h> + +#define ALIGN(stack, size) ((stack) += ((size) - (long)(stack)) & ((size) - 1)) + +#define PUSH(stack, size, type) (VALGRIND_MAKE_NOACCESS(stack, 1000),ALIGN((stack),sizeof(type)),VALGRIND_MAKE_WRITABLE(stack, ((size)*sizeof(type))),(stack)+=((size)*sizeof(type)),(type*)((stack)-((size)*sizeof(type)))) + +#else + +#define ALIGN(stack, size) ((stack) += ((size) - (long)(stack)) & ((size) - 1)) + +#define PUSH(stack, size, type) (ALIGN((stack),sizeof(type)),(stack)+=((size)*sizeof(type)),(type*)((stack)-((size)*sizeof(type)))) + +#endif + +#if defined(VAR_ARRAYS) +#define VARDECL(var) +#define ALLOC(var, size, type) type var[size] +#elif defined(USE_ALLOCA) +#define VARDECL(var) var +#define ALLOC(var, size, type) var = alloca(sizeof(type)*(size)) +#else +#define VARDECL(var) var +#define ALLOC(var, size, type) var = PUSH(stack, size, type) +#endif + + +#endif diff --git a/configs/codecs.conf.sample b/configs/codecs.conf.sample index c8caeab60..4404d4a0c 100644 --- a/configs/codecs.conf.sample +++ b/configs/codecs.conf.sample @@ -63,3 +63,74 @@ pp_dereverb_level => 0.3 ; this determines whether to perform generic PLC ; there is a minor performance penalty for this genericplc => true + +; Generate custom formats for formats requiring attributes. +; After defining the custom format, the name used in defining +; the format can be used throughout Asterisk in the format 'allow' +; and 'disallow' options. +; +; Example: silk8 is a predefined custom format in this config file. +; Once this config file is loaded, silk8 can be used anywhere a +; peer's codec capabilities are defined. +; +; In sip.conf 'silk8' can be defined as a capability for a peer. +; [peer1] +; type=peer +; host=dynamic +; disallow=all +; allow=silk8 ;custom codec defined in codecs.conf +; +; LIMITATIONS +; Custom formats can only be defined at startup. Any changes to this +; file made after startup will not take into effect until after Asterisk +; is restarted. +; + +; Default Custom SILK format definitions, only one custom SILK format per +; sample rate is allowed. +[silk8] +type=silk +samprate=8000 +fec=true ; turn on or off encoding with forward error correction. + ; On recommended, off by default. +packetloss_percentage=10 ; Estimated packet loss percentage in uplink direction. This + ; affects how much redundancy is built in when using fec. + ; The higher the percentage, the larger amount of bandwidth is + ; used. Default is 0%, 10% is recommended when fec is in use. + +maxbitrate=10000 ; Use the table below to make sure a useful bitrate is choosen + ; for maxbitrate. If not set or value is not within the bounds + ; of the encoder, a default value is chosen. + ; + ; sample rate | bitrate range + ; 8khz | 5000 - 20000 bps + ; 12khz | 7000 - 25000 bps + ; 16khz | 8000 - 30000 bps + ; 24khz | 20000- 40000 bps + ; +;dtx=true ; Encode using discontinuous transmission mode or not. Turning this + ; on will save bandwidth during periods of silence at the cost of + ; increased computational complexity. Off by default. + +[silk12] +type=silk +samprate=12000 +maxbitrate=12000 +fec=true +packetloss_percentage=10; + +[silk16] +type=silk +samprate=16000 +maxbitrate=20000 +fec=true +packetloss_percentage=10; + + +[silk24] +type=silk +samprate=24000 +maxbitrate=30000 +fec=true +packetloss_percentage=10; + diff --git a/formats/format_attr_silk.c b/formats/format_attr_silk.c new file mode 100644 index 000000000..49122fe80 --- /dev/null +++ b/formats/format_attr_silk.c @@ -0,0 +1,215 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2011, Digium, Inc. + * + * David Vossel <dvossel@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file + * \brief SILK format attribute interface + * + * \author David Vossel <dvossel@digium.com> + */ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/module.h" +#include "asterisk/format.h" + +/*! + * \brief SILK attribute structure. + * + * \note The only attribute that affects compatibility here is the sample rate. + */ +struct silk_attr { + unsigned int samplerate; + unsigned int maxbitrate; + unsigned int dtx; + unsigned int fec; + unsigned int packetloss_percentage; +}; + +static enum ast_format_cmp_res silk_cmp(const struct ast_format_attr *fattr1, const struct ast_format_attr *fattr2) +{ + struct silk_attr *attr1 = (struct silk_attr *) fattr1; + struct silk_attr *attr2 = (struct silk_attr *) fattr2; + + if (attr1->samplerate == attr2->samplerate) { + return AST_FORMAT_CMP_EQUAL; + } + return AST_FORMAT_CMP_NOT_EQUAL; +} + +static int silk_get_val(const struct ast_format_attr *fattr, int key, void *result) +{ + const struct silk_attr *attr = (struct silk_attr *) fattr; + int *val = result; + + switch (key) { + case SILK_ATTR_KEY_SAMP_RATE: + *val = attr->samplerate; + break; + case SILK_ATTR_KEY_MAX_BITRATE: + *val = attr->maxbitrate; + break; + case SILK_ATTR_KEY_DTX: + *val = attr->dtx; + break; + case SILK_ATTR_KEY_FEC: + *val = attr->fec; + break; + case SILK_ATTR_KEY_PACKETLOSS_PERCENTAGE: + *val = attr->packetloss_percentage; + break; + default: + return -1; + ast_log(LOG_WARNING, "unknown attribute type %d\n", key); + } + return 0; +} + +static int silk_isset(const struct ast_format_attr *fattr, va_list ap) +{ + enum silk_attr_keys key; + const struct silk_attr *attr = (struct silk_attr *) fattr; + + for (key = va_arg(ap, int); + key != AST_FORMAT_ATTR_END; + key = va_arg(ap, int)) + { + switch (key) { + case SILK_ATTR_KEY_SAMP_RATE: + if (attr->samplerate != (va_arg(ap, int))) { + return -1; + } + break; + case SILK_ATTR_KEY_MAX_BITRATE: + if (attr->maxbitrate != (va_arg(ap, int))) { + return -1; + } + break; + case SILK_ATTR_KEY_DTX: + if (attr->dtx != (va_arg(ap, int))) { + return -1; + } + break; + case SILK_ATTR_KEY_FEC: + if (attr->fec != (va_arg(ap, int))) { + return -1; + } + break; + case SILK_ATTR_KEY_PACKETLOSS_PERCENTAGE: + if (attr->packetloss_percentage != (va_arg(ap, int))) { + return -1; + } + break; + default: + return -1; + ast_log(LOG_WARNING, "unknown attribute type %d\n", key); + } + } + return 0; +} +static int silk_getjoint(const struct ast_format_attr *fattr1, const struct ast_format_attr *fattr2, struct ast_format_attr *result) +{ + struct silk_attr *attr1 = (struct silk_attr *) fattr1; + struct silk_attr *attr2 = (struct silk_attr *) fattr2; + struct silk_attr *attr_res = (struct silk_attr *) result; + int joint = -1; + + attr_res->samplerate = attr1->samplerate & attr2->samplerate; + /* sample rate is the only attribute that has any bearing on if joint capabilities exist or not */ + if (attr_res->samplerate) { + joint = 0; + } + /* Take the lowest max bitrate */ + attr_res->maxbitrate = MIN(attr1->maxbitrate, attr2->maxbitrate); + + /* Only do dtx if both sides want it. DTX is a trade off between + * computational complexity and bandwidth. */ + attr_res->dtx = attr1->dtx && attr2->dtx ? 1 : 0; + + /* Only do FEC if both sides want it. If a peer specifically requests not + * to receive with FEC, it may be a waste of bandwidth. */ + attr_res->fec = attr1->fec && attr2->fec ? 1 : 0; + + /* Use the maximum packetloss percentage between the two attributes. This affects how + * much redundancy is used in the FEC. */ + attr_res->packetloss_percentage = MAX(attr1->packetloss_percentage, attr2->packetloss_percentage); + return joint; +} + +static void silk_set(struct ast_format_attr *fattr, va_list ap) +{ + enum silk_attr_keys key; + struct silk_attr *attr = (struct silk_attr *) fattr; + + for (key = va_arg(ap, int); + key != AST_FORMAT_ATTR_END; + key = va_arg(ap, int)) + { + switch (key) { + case SILK_ATTR_KEY_SAMP_RATE: + attr->samplerate = (va_arg(ap, int)); + break; + case SILK_ATTR_KEY_MAX_BITRATE: + attr->maxbitrate = (va_arg(ap, int)); + break; + case SILK_ATTR_KEY_DTX: + attr->dtx = (va_arg(ap, int)); + break; + case SILK_ATTR_KEY_FEC: + attr->fec = (va_arg(ap, int)); + break; + case SILK_ATTR_KEY_PACKETLOSS_PERCENTAGE: + attr->packetloss_percentage = (va_arg(ap, int)); + break; + default: + ast_log(LOG_WARNING, "unknown attribute type %d\n", key); + } + } +} + +static struct ast_format_attr_interface silk_interface = { + .id = AST_FORMAT_SILK, + .format_attr_cmp = silk_cmp, + .format_attr_get_joint = silk_getjoint, + .format_attr_set = silk_set, + .format_attr_isset = silk_isset, + .format_attr_get_val = silk_get_val, +}; + +static int load_module(void) +{ + if (ast_format_attr_reg_interface(&silk_interface)) { + return AST_MODULE_LOAD_DECLINE; + } + + return AST_MODULE_LOAD_SUCCESS; +} + +static int unload_module(void) +{ + ast_format_attr_unreg_interface(&silk_interface); + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "SILK Format Attribute Module", + .load = load_module, + .unload = unload_module, + .load_pri = AST_MODPRI_CHANNEL_DEPEND, +); diff --git a/funcs/func_pitchshift.c b/funcs/func_pitchshift.c index 36fa2f6c4..b14894712 100644 --- a/funcs/func_pitchshift.c +++ b/funcs/func_pitchshift.c @@ -170,8 +170,7 @@ static int pitchshift_cb(struct ast_audiohook *audiohook, struct ast_channel *ch } if ((audiohook->status == AST_AUDIOHOOK_STATUS_DONE) || (f->frametype != AST_FRAME_VOICE) || - ((f->subclass.format.id != AST_FORMAT_SLINEAR) && - (f->subclass.format.id != AST_FORMAT_SLINEAR16))) { + !(ast_format_is_slinear(&f->subclass.format))) { return -1; } @@ -209,7 +208,7 @@ static int pitchshift_helper(struct ast_channel *chan, const char *cmd, char *da return 0; } - ast_audiohook_init(&shift->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "pitch_shift"); + ast_audiohook_init(&shift->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "pitch_shift", AST_AUDIOHOOK_MANIPULATE_ALL_RATES); shift->audiohook.manipulate_callback = pitchshift_cb; datastore->data = shift; new = 1; diff --git a/funcs/func_speex.c b/funcs/func_speex.c index 4b8b3a3d6..51cea99e1 100644 --- a/funcs/func_speex.c +++ b/funcs/func_speex.c @@ -105,6 +105,7 @@ struct speex_direction_info { struct speex_info { struct ast_audiohook audiohook; + int lastrate; struct speex_direction_info *tx, *rx; }; @@ -163,12 +164,13 @@ static int speex_callback(struct ast_audiohook *audiohook, struct ast_channel *c return -1; } - if (sdi->samples != frame->samples) { + if ((sdi->samples != frame->samples) || (ast_format_rate(&frame->subclass.format) != si->lastrate)) { + si->lastrate = ast_format_rate(&frame->subclass.format); if (sdi->state) { speex_preprocess_state_destroy(sdi->state); } - if (!(sdi->state = speex_preprocess_state_init((sdi->samples = frame->samples), 8000))) { + if (!(sdi->state = speex_preprocess_state_init((sdi->samples = frame->samples), si->lastrate))) { return -1; } @@ -212,9 +214,9 @@ static int speex_write(struct ast_channel *chan, const char *cmd, char *data, co return 0; } - ast_audiohook_init(&si->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "speex"); + ast_audiohook_init(&si->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "speex", AST_AUDIOHOOK_MANIPULATE_ALL_RATES); si->audiohook.manipulate_callback = speex_callback; - + si->lastrate = 8000; is_new = 1; } else { ast_channel_unlock(chan); diff --git a/funcs/func_volume.c b/funcs/func_volume.c index 88153f603..e94a4edc7 100644 --- a/funcs/func_volume.c +++ b/funcs/func_volume.c @@ -132,7 +132,7 @@ static int volume_write(struct ast_channel *chan, const char *cmd, char *data, c ast_datastore_free(datastore); return 0; } - ast_audiohook_init(&vi->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "Volume"); + ast_audiohook_init(&vi->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "Volume", AST_AUDIOHOOK_MANIPULATE_ALL_RATES); vi->audiohook.manipulate_callback = volume_callback; ast_set_flag(&vi->audiohook, AST_AUDIOHOOK_WANTS_DTMF); is_new = 1; diff --git a/include/asterisk/_private.h b/include/asterisk/_private.h index 560c8c169..a0b171254 100644 --- a/include/asterisk/_private.h +++ b/include/asterisk/_private.h @@ -90,4 +90,16 @@ int ast_xmldoc_load_documentation(void); */ int ast_plc_reload(void); +/*! + * \brief Init the ast_format attribute interface register container. + */ +int ast_format_attr_init(void); + +/*! + * \brief Init the Asterisk global format list after all format attribute modules have been loaded + */ +int ast_format_list_init(void); + +/*! \brief initializes the rtp engine arrays */ +int ast_rtp_engine_init(void); #endif /* _ASTERISK__PRIVATE_H */ diff --git a/include/asterisk/audiohook.h b/include/asterisk/audiohook.h index 75e2c8763..798a6d6e0 100644 --- a/include/asterisk/audiohook.h +++ b/include/asterisk/audiohook.h @@ -65,7 +65,12 @@ enum ast_audiohook_flags { AST_AUDIOHOOK_MUTE_WRITE = (1 << 5), /*!< audiohook should be mute frames written */ }; -#define AST_AUDIOHOOK_SYNC_TOLERANCE 100 /*< Tolerance in milliseconds for audiohooks synchronization */ +enum ast_audiohook_init_flags { + /*! Audiohook manipulate callback is capable of handling slinear at any sample rate. + * Without enabling this flag on initialization the manipulation callback is guaranteed + * 8khz audio only. */ + AST_AUDIOHOOK_MANIPULATE_ALL_RATES = (1 << 0), +}; struct ast_audiohook; @@ -97,6 +102,7 @@ struct ast_audiohook { ast_cond_t trigger; /*!< Trigger condition (if enabled) */ enum ast_audiohook_type type; /*!< Type of audiohook */ enum ast_audiohook_status status; /*!< Status of the audiohook */ + enum ast_audiohook_init_flags init_flags; /*!< Init flags */ const char *source; /*!< Who this audiohook ultimately belongs to */ unsigned int flags; /*!< Flags on the audiohook */ struct ast_slinfactory read_factory; /*!< Factory where frames read from the channel, or read from the whisper source will go through */ @@ -107,6 +113,7 @@ struct ast_audiohook { struct ast_trans_pvt *trans_pvt; /*!< Translation path for reading frames */ ast_audiohook_manipulate_callback manipulate_callback; /*!< Manipulation callback */ struct ast_audiohook_options options; /*!< Applicable options */ + unsigned int hook_internal_samp_rate; /*!< internal read/write sample rate on the audiohook.*/ AST_LIST_ENTRY(ast_audiohook) list; /*!< Linked list information */ }; @@ -116,9 +123,10 @@ struct ast_audiohook_list; * \param audiohook Audiohook structure * \param type Type of audiohook to initialize this as * \param source Who is initializing this audiohook + * \param init flags * \return Returns 0 on success, -1 on failure */ -int ast_audiohook_init(struct ast_audiohook *audiohook, enum ast_audiohook_type type, const char *source); +int ast_audiohook_init(struct ast_audiohook *audiohook, enum ast_audiohook_type type, const char *source, enum ast_audiohook_init_flags flags); /*! \brief Destroys an audiohook structure * \param audiohook Audiohook structure diff --git a/include/asterisk/format.h b/include/asterisk/format.h index 09212abc8..67e4178a2 100644 --- a/include/asterisk/format.h +++ b/include/asterisk/format.h @@ -26,8 +26,9 @@ #ifndef _AST_FORMAT_H_ #define _AST_FORMAT_H_ +#include "asterisk/astobj2.h" +#include "asterisk/silk.h" #define AST_FORMAT_ATTR_SIZE 128 - #define AST_FORMAT_INC 100000 /*! This is the value that ends a var list of format attribute @@ -55,32 +56,49 @@ enum ast_format_id { AST_FORMAT_G726_AAL2 = 5 + AST_FORMAT_TYPE_AUDIO, /*! ADPCM (IMA) */ AST_FORMAT_ADPCM = 6 + AST_FORMAT_TYPE_AUDIO, - /*! Raw 16-bit Signed Linear (8000 Hz) PCM */ - AST_FORMAT_SLINEAR = 7 + AST_FORMAT_TYPE_AUDIO, /*! LPC10, 180 samples/frame */ - AST_FORMAT_LPC10 = 8 + AST_FORMAT_TYPE_AUDIO, + AST_FORMAT_LPC10 = 7 + AST_FORMAT_TYPE_AUDIO, /*! G.729A audio */ - AST_FORMAT_G729A = 9 + AST_FORMAT_TYPE_AUDIO, + AST_FORMAT_G729A = 8 + AST_FORMAT_TYPE_AUDIO, /*! SpeeX Free Compression */ - AST_FORMAT_SPEEX = 10 + AST_FORMAT_TYPE_AUDIO, + AST_FORMAT_SPEEX = 9 + AST_FORMAT_TYPE_AUDIO, /*! iLBC Free Compression */ - AST_FORMAT_ILBC = 11 + AST_FORMAT_TYPE_AUDIO, + AST_FORMAT_ILBC = 10 + AST_FORMAT_TYPE_AUDIO, /*! ADPCM (G.726, 32kbps, RFC3551 codeword packing) */ - AST_FORMAT_G726 = 12 + AST_FORMAT_TYPE_AUDIO, + AST_FORMAT_G726 = 11 + AST_FORMAT_TYPE_AUDIO, /*! G.722 */ - AST_FORMAT_G722 = 13 + AST_FORMAT_TYPE_AUDIO, + AST_FORMAT_G722 = 12 + AST_FORMAT_TYPE_AUDIO, /*! G.722.1 (also known as Siren7, 32kbps assumed) */ - AST_FORMAT_SIREN7 = 14 + AST_FORMAT_TYPE_AUDIO, + AST_FORMAT_SIREN7 = 13 + AST_FORMAT_TYPE_AUDIO, /*! G.722.1 Annex C (also known as Siren14, 48kbps assumed) */ - AST_FORMAT_SIREN14 = 15 + AST_FORMAT_TYPE_AUDIO, - /*! Raw 16-bit Signed Linear (16000 Hz) PCM */ - AST_FORMAT_SLINEAR16 = 16 + AST_FORMAT_TYPE_AUDIO, + AST_FORMAT_SIREN14 = 14 + AST_FORMAT_TYPE_AUDIO, /*! G.719 (64 kbps assumed) */ - AST_FORMAT_G719 = 17 + AST_FORMAT_TYPE_AUDIO, + AST_FORMAT_G719 = 15 + AST_FORMAT_TYPE_AUDIO, /*! SpeeX Wideband (16kHz) Free Compression */ - AST_FORMAT_SPEEX16 = 18 + AST_FORMAT_TYPE_AUDIO, + AST_FORMAT_SPEEX16 = 16 + AST_FORMAT_TYPE_AUDIO, /*! Raw mu-law data (G.711) */ - AST_FORMAT_TESTLAW = 19 + AST_FORMAT_TYPE_AUDIO, + AST_FORMAT_TESTLAW = 17 + AST_FORMAT_TYPE_AUDIO, + /*! SILK format */ + AST_FORMAT_SILK = 18 + AST_FORMAT_TYPE_AUDIO, + /*! Raw 16-bit Signed Linear (8000 Hz) PCM */ + AST_FORMAT_SLINEAR = 19 + AST_FORMAT_TYPE_AUDIO, + /*! Raw 16-bit Signed Linear (12000 Hz) PCM */ + AST_FORMAT_SLINEAR12 = 20 + AST_FORMAT_TYPE_AUDIO, + /*! Raw 16-bit Signed Linear (16000 Hz) PCM */ + AST_FORMAT_SLINEAR16 = 21 + AST_FORMAT_TYPE_AUDIO, + /*! Raw 16-bit Signed Linear (24000 Hz) PCM */ + AST_FORMAT_SLINEAR24 = 22 + AST_FORMAT_TYPE_AUDIO, + /*! Raw 16-bit Signed Linear (32000 Hz) PCM */ + AST_FORMAT_SLINEAR32 = 23 + AST_FORMAT_TYPE_AUDIO, + /*! Raw 16-bit Signed Linear (44100 Hz) PCM just because we can. */ + AST_FORMAT_SLINEAR44 = 24 + AST_FORMAT_TYPE_AUDIO, + /*! Raw 16-bit Signed Linear (48000 Hz) PCM */ + AST_FORMAT_SLINEAR48 = 25 + AST_FORMAT_TYPE_AUDIO, + /*! Raw 16-bit Signed Linear (96000 Hz) PCM */ + AST_FORMAT_SLINEAR96 = 26 + AST_FORMAT_TYPE_AUDIO, + /*! Raw 16-bit Signed Linear (192000 Hz) PCM. maybe we're taking this too far. */ + AST_FORMAT_SLINEAR192 = 27 + AST_FORMAT_TYPE_AUDIO, + AST_FORMAT_SPEEX32 = 28 + AST_FORMAT_TYPE_AUDIO, /*! H.261 Video */ AST_FORMAT_H261 = 1 + AST_FORMAT_TYPE_VIDEO, @@ -107,6 +125,7 @@ enum ast_format_id { /*! Determine what type of media a ast_format_id is. */ #define AST_FORMAT_GET_TYPE(id) (((int) (id / AST_FORMAT_INC)) * AST_FORMAT_INC) + /*! \brief This structure contains the buffer used for format attributes */ struct ast_format_attr { /*! The buffer formats can use to represent attributes */ @@ -133,6 +152,22 @@ enum ast_format_cmp_res { AST_FORMAT_CMP_SUBSET, }; +/*! \brief Definition of supported media formats (codecs) */ +struct ast_format_list { + struct ast_format format; /*!< The unique format. */ + char name[64]; /*!< short name */ + unsigned int samplespersecond; /*!< Number of samples per second (8000/16000) */ + char desc[128]; /*!< Description */ + int fr_len; /*!< Single frame length in bytes */ + int min_ms; /*!< Min value */ + int max_ms; /*!< Max value */ + int inc_ms; /*!< Increment */ + int def_ms; /*!< Default value */ + unsigned int flags; /*!< Smoother flags */ + int cur_ms; /*!< Current value */ + int custom_entry; +}; + /*! \brief A format must register an attribute interface if it requires the use of the format attributes void pointer */ struct ast_format_attr_interface { /*! format type */ @@ -154,6 +189,34 @@ struct ast_format_attr_interface { /*! \brief Set format capabilities from a list of key value pairs ending with AST_FORMAT_ATTR_END. * \note This function does not need to call va_end of the va_list. */ void (* const format_attr_set)(struct ast_format_attr *format_attr, va_list ap); + + /*! + * \brief Find out if format capabilities in va_list are in format. + * \note This function does not need to call va_end of the va_list. + * + * \note This function is optional. In many cases the format_attr_cmp + * function can be used to derive these results. If it is possible + * that some format attributes have no bearing on the equality of two formats, this + * function must exist. + * + * \retval 0 if all attributes exist + * \retval -1 if any of the attributes not present + */ + int (* const format_attr_isset)(const struct ast_format_attr *format_attr, va_list ap); + + /* + * \brief Return a value for a specific format key. Return that value in the void pointer. + * + * \note It is not expected that all key value pairs can be returned, but those that can should + * be documented as such. + * + * \note This function is optional if key value pairs are not allowed to be accessed. This + * will result in -1 always being returned. + * + * \retval 0 Success, value was found and copied into void pointer. + * \retval -1 failure, Value was either not found, or not allowed to be accessed. + */ + int (* const format_attr_get_val)(const struct ast_format_attr *format_attr, int key, void *val); }; /*! @@ -218,7 +281,18 @@ void ast_format_clear(struct ast_format *format); * \return 0, The format key value pairs are within the capabilities defined in this structure. * \return -1, The format key value pairs are _NOT_ within the capabilities of this structure. */ -int ast_format_isset(struct ast_format *format, ... ); +int ast_format_isset(const struct ast_format *format, ... ); + +/*! + * \brief Get a value from a format containing attributes. + * \note The key represents the format attribute to be retrieved, and the void pointer + * is to the structure that value will be stored in. It must be known what structure a + * key represents. + * + * \retval 0, success + * \retval -1, failure + */ +int ast_format_get_value(const struct ast_format *format, int key, void *value); /*! * \brief Compare ast_formats structures @@ -287,6 +361,52 @@ struct ast_format *ast_format_from_old_bitfield(struct ast_format *dst, uint64_t enum ast_format_id ast_format_id_from_old_bitfield(uint64_t src); /*! + * \brief Retrieve the global format list in a read only array. + * \note ast_format_list_destroy must be called on every format + * list retrieved from this function. + */ +const struct ast_format_list *ast_format_list_get(size_t *size); + +/*! + * \brief Destroy an ast_format_list gotten from ast_format_list_get() + */ +const struct ast_format_list *ast_format_list_destroy(const struct ast_format_list *list); + +/*! \brief Get the name of a format + * \param format id of format + * \return A static string containing the name of the format or "unknown" if unknown. + */ +const char* ast_getformatname(const struct ast_format *format); + +/*! \brief Returns a string containing all formats pertaining to an format id. + * \param buf a buffer for the output string + * \param size size of buf (bytes) + * \param format id. + * \return The return value is buf. + */ +char* ast_getformatname_multiple_byid(char *buf, size_t size, enum ast_format_id id); + +/*! + * \brief Gets a format from a name. + * \param name string of format + * \param format structure to return the format in. + * \return This returns the format pointer given to it on success and NULL on failure + */ +struct ast_format *ast_getformatbyname(const char *name, struct ast_format *format); + +/*! + * \brief Get a name from a format + * \param format to get name of + * \return This returns a static string identifying the format on success, 0 on error. + */ +const char *ast_codec2str(struct ast_format *format); + +/*! + * \brief Get the sample rate for a given format. + */ +int ast_format_rate(const struct ast_format *format); + +/*! * \brief register ast_format_attr_interface with core. * * \retval 0 success @@ -303,8 +423,12 @@ int ast_format_attr_reg_interface(const struct ast_format_attr_interface *interf int ast_format_attr_unreg_interface(const struct ast_format_attr_interface *interface); /*! - * \brief Init the ast_format attribute interface register container. + * \brief Determine if a format is 16bit signed linear of any sample rate. */ -int ast_format_attr_init(void); +int ast_format_is_slinear(const struct ast_format *format); +/*! + * \brief Get the best slinear format id for a given sample rate + */ +enum ast_format_id ast_format_slin_by_rate(unsigned int rate); #endif /* _AST_FORMAT_H */ diff --git a/include/asterisk/format_cap.h b/include/asterisk/format_cap.h index cdb5421f9..234767685 100644 --- a/include/asterisk/format_cap.h +++ b/include/asterisk/format_cap.h @@ -70,7 +70,7 @@ void *ast_format_cap_destroy(struct ast_format_cap *cap); * what is placed in the ast_format_cap structure. The actual * input format ptr is not stored. */ -void ast_format_cap_add(struct ast_format_cap *cap, struct ast_format *format); +void ast_format_cap_add(struct ast_format_cap *cap, const struct ast_format *format); /*! * \brief Add all formats Asterisk knows about for a specific type to @@ -155,6 +155,15 @@ void ast_format_cap_remove_all(struct ast_format_cap *cap); void ast_format_cap_set(struct ast_format_cap *cap, struct ast_format *format); /*! + * \brief Find if input ast_format is within the capabilities of the ast_format_cap object + * then return the compatible format from the capabilities structure in the result. + * + * \retval 1 format is compatible with formats held in ast_format_cap object. + * \retval 0 format is not compatible with any formats in ast_format_cap object. + */ +int ast_format_cap_get_compatible_format(const struct ast_format_cap *cap, const struct ast_format *format, struct ast_format *result); + +/*! * \brief Find if ast_format is within the capabilities of the ast_format_cap object. * * retval 1 format is compatible with formats held in ast_format_cap object. @@ -163,6 +172,14 @@ void ast_format_cap_set(struct ast_format_cap *cap, struct ast_format *format); int ast_format_cap_iscompatible(const struct ast_format_cap *cap, const struct ast_format *format); /*! + * \brief Finds the best quality audio format for a given format id and returns it in result. + * + * \retval 1 format found and set to result structure. + * \retval 0 no format found, result structure is cleared. + */ +int ast_format_cap_best_byid(const struct ast_format_cap *cap, enum ast_format_id, struct ast_format *result); + +/*! * \brief is cap1 identical to cap2 * * retval 1 true, identical @@ -278,4 +295,14 @@ uint64_t ast_format_cap_to_old_bitfield(const struct ast_format_cap *cap); */ void ast_format_cap_from_old_bitfield(struct ast_format_cap *dst, uint64_t src); +/*! \brief Get the names of a set of formats + * \param buf a buffer for the output string + * \param size size of buf (bytes) + * \param format the format (combined IDs of codecs) + * Prints a list of readable codec names corresponding to "format". + * ex: for format=AST_FORMAT_GSM|AST_FORMAT_SPEEX|AST_FORMAT_ILBC it will return "0x602 (GSM|SPEEX|ILBC)" + * \return The return value is buf. + */ +char* ast_getformatname_multiple(char *buf, size_t size, struct ast_format_cap *cap); + #endif /* _AST_FORMATCAP_H */ diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h index 63cbb952f..e02df42ed 100644 --- a/include/asterisk/frame.h +++ b/include/asterisk/frame.h @@ -427,23 +427,6 @@ struct ast_option_header { uint8_t data[0]; }; - -/*! \brief Definition of supported media formats (codecs) */ -struct ast_format_list { - enum ast_format_id id; /*!< The format unique id */ - char *name; /*!< short name */ - int samplespersecond; /*!< Number of samples per second (8000/16000) */ - char *desc; /*!< Description */ - int fr_len; /*!< Single frame length in bytes */ - int min_ms; /*!< Min value */ - int max_ms; /*!< Max value */ - int inc_ms; /*!< Increment */ - int def_ms; /*!< Default value */ - unsigned int flags; /*!< Smoother flags */ - int cur_ms; /*!< Current value */ -}; - - /*! \brief Requests a frame to be allocated * * \param source @@ -505,37 +488,6 @@ void ast_swapcopy_samples(void *dst, const void *src, int samples); */ int ast_parse_allow_disallow(struct ast_codec_pref *pref, struct ast_format_cap *cap, const char *list, int allowing); -/*! \brief Get the name of a format - * \param format id of format - * \return A static string containing the name of the format or "unknown" if unknown. - */ -char* ast_getformatname(struct ast_format *format); - -/*! \brief Get the names of a set of formats - * \param buf a buffer for the output string - * \param size size of buf (bytes) - * \param format the format (combined IDs of codecs) - * Prints a list of readable codec names corresponding to "format". - * ex: for format=AST_FORMAT_GSM|AST_FORMAT_SPEEX|AST_FORMAT_ILBC it will return "0x602 (GSM|SPEEX|ILBC)" - * \return The return value is buf. - */ -char* ast_getformatname_multiple(char *buf, size_t size, struct ast_format_cap *cap); - -/*! - * \brief Gets a format from a name. - * \param name string of format - * \param format structure to return the format in. - * \return This returns the format pointer given to it on success and NULL on failure - */ -struct ast_format *ast_getformatbyname(const char *name, struct ast_format *format); - -/*! \brief Get a name from a format - * Gets a name from a format - * \param format to get name of - * \return This returns a static string identifying the format on success, 0 on error. - */ -char *ast_codec2str(struct ast_format *format); - /*! \name AST_Smoother */ /*@{ */ @@ -582,8 +534,6 @@ struct ast_frame *ast_smoother_read(struct ast_smoother *s); #endif /*@} Doxygen marker */ -const struct ast_format_list *ast_get_format_list_index(int index); -const struct ast_format_list *ast_get_format_list(size_t *size); void ast_frame_dump(const char *name, struct ast_frame *f, char *prefix); /*! \brief Returns the number of samples contained in the frame */ @@ -622,26 +572,6 @@ int ast_frame_adjust_volume(struct ast_frame *f, int adjustment); int ast_frame_slinear_sum(struct ast_frame *f1, struct ast_frame *f2); /*! - * \brief Get the sample rate for a given format. - */ -static force_inline int ast_format_rate(struct ast_format *format) -{ - switch (format->id) { - case AST_FORMAT_G722: - case AST_FORMAT_SLINEAR16: - case AST_FORMAT_SIREN7: - case AST_FORMAT_SPEEX16: - return 16000; - case AST_FORMAT_SIREN14: - return 32000; - case AST_FORMAT_G719: - return 48000; - default: - return 8000; - } -} - -/*! * \brief Clear all audio samples from an ast_frame. The frame must be AST_FRAME_VOICE and AST_FORMAT_SLINEAR */ int ast_frame_clear(struct ast_frame *frame); diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h index f13538321..4c5753e84 100644 --- a/include/asterisk/rtp_engine.h +++ b/include/asterisk/rtp_engine.h @@ -1048,6 +1048,19 @@ void ast_rtp_codecs_payloads_unset(struct ast_rtp_codecs *codecs, struct ast_rtp struct ast_rtp_payload_type ast_rtp_codecs_payload_lookup(struct ast_rtp_codecs *codecs, int payload); /*! + * \brief Retrieve the actual ast_format stored on the codecs structure for a specific payload + * + * \param codecs Codecs structure to look in + * \param payload Numerical payload to look up + * + * \retval pointer to format structure on success + * \retval NULL on failure + * + * \since 1.10 + */ +struct ast_format *ast_rtp_codecs_get_payload_format(struct ast_rtp_codecs *codecs, int payload); + +/*! * \brief Get the sample rate associated with known RTP payload types * * \param asterisk_format True if the value in format is to be used. @@ -1798,6 +1811,15 @@ struct ast_channel *ast_rtp_instance_get_chan(struct ast_rtp_instance *instance) int ast_rtp_instance_add_srtp_policy(struct ast_rtp_instance *instance, struct ast_srtp_policy *policy); struct ast_srtp *ast_rtp_instance_get_srtp(struct ast_rtp_instance *instance); +/*! \brief Custom formats declared in codecs.conf at startup must be communicated to the rtp_engine + * so their mime type can payload number can be initialized. */ +int ast_rtp_engine_load_format(const struct ast_format *format); + +/*! \brief Formats requiring the use of a format attribute interface must have that + * interface registered in order for the rtp engine to handle it correctly. If an + * attribute interface is unloaded, this function must be called to notify the rtp_engine. */ +int ast_rtp_engine_unload_format(const struct ast_format *format); + #if defined(__cplusplus) || defined(c_plusplus) } #endif diff --git a/include/asterisk/silk.h b/include/asterisk/silk.h new file mode 100644 index 000000000..5da827e7e --- /dev/null +++ b/include/asterisk/silk.h @@ -0,0 +1,44 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2011, Digium, Inc. + * + * David Vossel <dvossel@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file + * \brief SILK Format Attributes + * + * \author David Vossel <dvossel@digium.com> + */ +#ifndef _AST_FORMAT_SILK_H_ +#define _AST_FORMAT_SILK_H_ + +/*! SILK format attribute key value pairs, all are accessible through ast_format_get_value()*/ +enum silk_attr_keys { + SILK_ATTR_KEY_SAMP_RATE, /*!< value is silk_attr_vals enum */ + SILK_ATTR_KEY_DTX, /*!< value is an int, 1 dtx is enabled, 0 dtx not enabled. */ + SILK_ATTR_KEY_FEC, /*!< value is an int, 1 encode with FEC, 0 do not use FEC. */ + SILK_ATTR_KEY_PACKETLOSS_PERCENTAGE, /*!< value is an int (0-100), Represents estimated packetloss in uplink direction.*/ + SILK_ATTR_KEY_MAX_BITRATE, /*!< value is an int */ +}; + +enum silk_attr_vals { + SILK_ATTR_VAL_SAMP_8KHZ = (1 << 0), + SILK_ATTR_VAL_SAMP_12KHZ = (1 << 1), + SILK_ATTR_VAL_SAMP_16KHZ = (1 << 2), + SILK_ATTR_VAL_SAMP_24KHZ = (1 << 3), +}; + +#endif /* _AST_FORMAT_SILK_H */ diff --git a/include/asterisk/slinfactory.h b/include/asterisk/slinfactory.h index 003c6ac28..324c0ae28 100644 --- a/include/asterisk/slinfactory.h +++ b/include/asterisk/slinfactory.h @@ -56,11 +56,11 @@ void ast_slinfactory_init(struct ast_slinfactory *sf); * \brief Initialize a slinfactory * * \param sf The slinfactory to initialize - * \param sample_rate The output sample rate desired + * \param slin_out the slinear output format desired. * * \return 0 on success, non-zero on failure */ -int ast_slinfactory_init_rate(struct ast_slinfactory *sf, unsigned int sample_rate); +int ast_slinfactory_init_with_format(struct ast_slinfactory *sf, const struct ast_format *slin_out); /*! * \brief Destroy the contents of a slinfactory diff --git a/include/asterisk/time.h b/include/asterisk/time.h index 2ffc691b8..c78ff2db0 100644 --- a/include/asterisk/time.h +++ b/include/asterisk/time.h @@ -171,7 +171,7 @@ struct timeval ast_tv(ast_time_t sec, ast_suseconds_t usec), AST_INLINE_API( struct timeval ast_samp2tv(unsigned int _nsamp, unsigned int _rate), { - return ast_tv(_nsamp / _rate, ((_nsamp % _rate) * (4000000 / _rate)) / 4); /* this calculation is accurate up to 32000Hz. */ + return ast_tv(_nsamp / _rate, (_nsamp % _rate) * (1000000 / (float) _rate)); } ) diff --git a/include/asterisk/translate.h b/include/asterisk/translate.h index 7e73cd1b1..8545f0ae5 100644 --- a/include/asterisk/translate.h +++ b/include/asterisk/translate.h @@ -133,7 +133,7 @@ enum ast_trans_cost_table { * Generic plc is only available for dstfmt = SLINEAR */ struct ast_translator { - const char name[80]; /*!< Name of translator */ + char name[80]; /*!< Name of translator */ struct ast_format src_format; /*!< Source format */ struct ast_format dst_format; /*!< Destination format */ @@ -204,6 +204,12 @@ struct ast_translator { struct ast_trans_pvt { struct ast_translator *t; struct ast_frame f; /*!< used in frameout */ + /*! If a translation path using a format with attributes requires the output + * to be a specific set of attributes, this variable will be set describing those + * attributes to the translator. Otherwise, the translator must choose a set + * of format attributes for the destination that preserves the quality of the + * audio in the best way possible. */ + struct ast_format explicit_dst; int samples; /*!< samples available in outbuf */ /*! \brief actual space used in outbuf */ int datalen; @@ -213,7 +219,7 @@ struct ast_trans_pvt { unsigned char *uc; /*!< the useful portion of the buffer */ int16_t *i16; uint8_t *ui8; - } outbuf; + } outbuf; plc_state_t *plc; /*!< optional plc pointer */ struct ast_trans_pvt *next; /*!< next in translator chain */ struct timeval nextin; diff --git a/main/asterisk.c b/main/asterisk.c index ad8b38103..846848da8 100644 --- a/main/asterisk.c +++ b/main/asterisk.c @@ -143,6 +143,7 @@ int daemon(int, int); /* defined in libresolv of all places */ #include "asterisk/poll-compat.h" #include "asterisk/ccss.h" #include "asterisk/test.h" +#include "asterisk/rtp_engine.h" #include "asterisk/format.h" #include "asterisk/aoc.h" @@ -3708,6 +3709,8 @@ int main(int argc, char *argv[]) astobj2_init(); ast_format_attr_init(); + ast_format_list_init(); + ast_rtp_engine_init(); ast_autoservice_init(); diff --git a/main/audiohook.c b/main/audiohook.c index 6b2df6416..9fd2ca957 100644 --- a/main/audiohook.c +++ b/main/audiohook.c @@ -38,12 +38,22 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/frame.h" #include "asterisk/translate.h" +#define AST_AUDIOHOOK_SYNC_TOLERANCE 100 /*!< Tolerance in milliseconds for audiohooks synchronization */ +#define AST_AUDIOHOOK_SMALL_QUEUE_TOLERANCE 100 /*!< When small queue is enabled, this is the maximum amount of audio that can remain queued at a time. */ + struct ast_audiohook_translate { struct ast_trans_pvt *trans_pvt; struct ast_format format; }; struct ast_audiohook_list { + /* If all the audiohooks in this list are capable + * of processing slinear at any sample rate, this + * variable will be set and the sample rate will + * be preserved during ast_audiohook_write_list()*/ + int native_slin_compatible; + int list_internal_samp_rate;/*!< Internal sample rate used when writing to the audiohook list */ + struct ast_audiohook_translate in_translate[2]; struct ast_audiohook_translate out_translate[2]; AST_LIST_HEAD_NOLOCK(, ast_audiohook) spy_list; @@ -51,13 +61,44 @@ struct ast_audiohook_list { AST_LIST_HEAD_NOLOCK(, ast_audiohook) manipulate_list; }; +static int audiohook_set_internal_rate(struct ast_audiohook *audiohook, int rate, int reset) +{ + struct ast_format slin; + + if (audiohook->hook_internal_samp_rate == rate) { + return 0; + } + + audiohook->hook_internal_samp_rate = rate; + + ast_format_set(&slin, ast_format_slin_by_rate(rate), 0); + /* Setup the factories that are needed for this audiohook type */ + switch (audiohook->type) { + case AST_AUDIOHOOK_TYPE_SPY: + if (reset) { + ast_slinfactory_destroy(&audiohook->read_factory); + } + ast_slinfactory_init_with_format(&audiohook->read_factory, &slin); + /* fall through */ + case AST_AUDIOHOOK_TYPE_WHISPER: + if (reset) { + ast_slinfactory_destroy(&audiohook->write_factory); + } + ast_slinfactory_init_with_format(&audiohook->write_factory, &slin); + break; + default: + break; + } + return 0; +} + /*! \brief Initialize an audiohook structure * \param audiohook Audiohook structure * \param type * \param source * \return Returns 0 on success, -1 on failure */ -int ast_audiohook_init(struct ast_audiohook *audiohook, enum ast_audiohook_type type, const char *source) +int ast_audiohook_init(struct ast_audiohook *audiohook, enum ast_audiohook_type type, const char *source, enum ast_audiohook_init_flags init_flags) { /* Need to keep the type and source */ audiohook->type = type; @@ -67,16 +108,10 @@ int ast_audiohook_init(struct ast_audiohook *audiohook, enum ast_audiohook_type ast_mutex_init(&audiohook->lock); ast_cond_init(&audiohook->trigger, NULL); - /* Setup the factories that are needed for this audiohook type */ - switch (type) { - case AST_AUDIOHOOK_TYPE_SPY: - ast_slinfactory_init(&audiohook->read_factory); - case AST_AUDIOHOOK_TYPE_WHISPER: - ast_slinfactory_init(&audiohook->write_factory); - break; - default: - break; - } + audiohook->init_flags = init_flags; + + /* initialize internal rate at 8khz, this will adjust if necessary */ + audiohook_set_internal_rate(audiohook, 8000, 0); /* Since we are just starting out... this audiohook is new */ ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_NEW); @@ -133,9 +168,9 @@ int ast_audiohook_write_frame(struct ast_audiohook *audiohook, enum ast_audiohoo *rwtime = ast_tvnow(); our_factory_samples = ast_slinfactory_available(factory); - our_factory_ms = ast_tvdiff_ms(*rwtime, previous_time) + (our_factory_samples / 8); + our_factory_ms = ast_tvdiff_ms(*rwtime, previous_time) + (our_factory_samples / (audiohook->hook_internal_samp_rate / 1000)); other_factory_samples = ast_slinfactory_available(other_factory); - other_factory_ms = other_factory_samples / 8; + other_factory_ms = other_factory_samples / (audiohook->hook_internal_samp_rate / 1000); if (ast_test_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC) && other_factory_samples && (our_factory_ms - other_factory_ms > AST_AUDIOHOOK_SYNC_TOLERANCE)) { ast_debug(1, "Flushing audiohook %p so it remains in sync\n", audiohook); @@ -143,7 +178,7 @@ int ast_audiohook_write_frame(struct ast_audiohook *audiohook, enum ast_audiohoo ast_slinfactory_flush(other_factory); } - if (ast_test_flag(audiohook, AST_AUDIOHOOK_SMALL_QUEUE) && (our_factory_samples > 640 || other_factory_samples > 640)) { + if (ast_test_flag(audiohook, AST_AUDIOHOOK_SMALL_QUEUE) && ((our_factory_ms > AST_AUDIOHOOK_SMALL_QUEUE_TOLERANCE) || (other_factory_ms > AST_AUDIOHOOK_SMALL_QUEUE_TOLERANCE))) { ast_debug(1, "Audiohook %p has stale audio in its factories. Flushing them both\n", audiohook); ast_slinfactory_flush(factory); ast_slinfactory_flush(other_factory); @@ -186,7 +221,7 @@ static struct ast_frame *audiohook_read_frame_single(struct ast_audiohook *audio .datalen = sizeof(buf), .samples = samples, }; - ast_format_set(&frame.subclass.format, AST_FORMAT_SLINEAR, 0); + ast_format_set(&frame.subclass.format, ast_format_slin_by_rate(audiohook->hook_internal_samp_rate), 0); /* Ensure the factory is able to give us the samples we want */ if (samples > ast_slinfactory_available(factory)) @@ -213,7 +248,7 @@ static struct ast_frame *audiohook_read_frame_both(struct ast_audiohook *audioho .datalen = sizeof(buf1), .samples = samples, }; - ast_format_set(&frame.subclass.format, AST_FORMAT_SLINEAR, 0); + ast_format_set(&frame.subclass.format, ast_format_slin_by_rate(audiohook->hook_internal_samp_rate), 0); /* Make sure both factories have the required samples */ usable_read = (ast_slinfactory_available(&audiohook->read_factory) >= samples ? 1 : 0); @@ -296,7 +331,7 @@ static struct ast_frame *audiohook_read_frame_both(struct ast_audiohook *audioho /*! \brief Reads a frame in from the audiohook structure * \param audiohook Audiohook structure - * \param samples Number of samples wanted + * \param samples Number of samples wanted in requested output format * \param direction Direction the audio frame came from * \param format Format of frame remote side wants back * \return Returns frame on success, NULL on failure @@ -305,23 +340,39 @@ struct ast_frame *ast_audiohook_read_frame(struct ast_audiohook *audiohook, size { struct ast_frame *read_frame = NULL, *final_frame = NULL; struct ast_format tmp_fmt; + int samples_converted; + + /* the number of samples requested is based on the format they are requesting. Inorder + * to process this correctly samples must be converted to our internal sample rate */ + if (audiohook->hook_internal_samp_rate == ast_format_rate(format)) { + samples_converted = samples; + } else if (audiohook->hook_internal_samp_rate > ast_format_rate(format)) { + samples_converted = samples * (audiohook->hook_internal_samp_rate / (float) ast_format_rate(format)); + } else { + samples_converted = samples * (ast_format_rate(format) / (float) audiohook->hook_internal_samp_rate); + } - if (!(read_frame = (direction == AST_AUDIOHOOK_DIRECTION_BOTH ? audiohook_read_frame_both(audiohook, samples) : audiohook_read_frame_single(audiohook, samples, direction)))) + if (!(read_frame = (direction == AST_AUDIOHOOK_DIRECTION_BOTH ? + audiohook_read_frame_both(audiohook, samples_converted) : + audiohook_read_frame_single(audiohook, samples_converted, direction)))) { return NULL; + } /* If they don't want signed linear back out, we'll have to send it through the translation path */ - if (format->id != AST_FORMAT_SLINEAR) { + if (format->id != ast_format_slin_by_rate(audiohook->hook_internal_samp_rate)) { /* Rebuild translation path if different format then previously */ if (ast_format_cmp(format, &audiohook->format) == AST_FORMAT_CMP_NOT_EQUAL) { if (audiohook->trans_pvt) { ast_translator_free_path(audiohook->trans_pvt); audiohook->trans_pvt = NULL; } + /* Setup new translation path for this format... if we fail we can't very well return signed linear so free the frame and return nothing */ - if (!(audiohook->trans_pvt = ast_translator_build_path(format, ast_format_set(&tmp_fmt, AST_FORMAT_SLINEAR, 0)))) { + if (!(audiohook->trans_pvt = ast_translator_build_path(format, ast_format_set(&tmp_fmt, ast_format_slin_by_rate(audiohook->hook_internal_samp_rate), 0)))) { ast_frfree(read_frame); return NULL; } + ast_format_copy(&audiohook->format, format); } /* Convert to requested format, and allow the read in frame to be freed */ final_frame = ast_translate(audiohook->trans_pvt, read_frame, 1); @@ -332,6 +383,18 @@ struct ast_frame *ast_audiohook_read_frame(struct ast_audiohook *audiohook, size return final_frame; } +static void audiohook_list_set_samplerate_compatibility(struct ast_audiohook_list *audiohook_list) +{ + struct ast_audiohook *ah = NULL; + audiohook_list->native_slin_compatible = 1; + AST_LIST_TRAVERSE(&audiohook_list->manipulate_list, ah, list) { + if (!(ah->init_flags & AST_AUDIOHOOK_MANIPULATE_ALL_RATES)) { + audiohook_list->native_slin_compatible = 0; + return; + } + } +} + /*! \brief Attach audiohook to channel * \param chan Channel * \param audiohook Audiohook structure @@ -350,6 +413,8 @@ int ast_audiohook_attach(struct ast_channel *chan, struct ast_audiohook *audioho AST_LIST_HEAD_INIT_NOLOCK(&chan->audiohooks->spy_list); AST_LIST_HEAD_INIT_NOLOCK(&chan->audiohooks->whisper_list); AST_LIST_HEAD_INIT_NOLOCK(&chan->audiohooks->manipulate_list); + /* This sample rate will adjust as necessary when writing to the list. */ + chan->audiohooks->list_internal_samp_rate = 8000; } /* Drop into respective list */ @@ -360,6 +425,10 @@ int ast_audiohook_attach(struct ast_channel *chan, struct ast_audiohook *audioho else if (audiohook->type == AST_AUDIOHOOK_TYPE_MANIPULATE) AST_LIST_INSERT_TAIL(&chan->audiohooks->manipulate_list, audiohook, list); + + audiohook_set_internal_rate(audiohook, chan->audiohooks->list_internal_samp_rate, 1); + audiohook_list_set_samplerate_compatibility(chan->audiohooks); + /* Change status over to running since it is now attached */ ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_RUNNING); @@ -546,6 +615,7 @@ int ast_audiohook_remove(struct ast_channel *chan, struct ast_audiohook *audioho else if (audiohook->type == AST_AUDIOHOOK_TYPE_MANIPULATE) AST_LIST_REMOVE(&chan->audiohooks->manipulate_list, audiohook, list); + audiohook_list_set_samplerate_compatibility(chan->audiohooks); ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_DONE); ast_channel_unlock(chan); @@ -563,11 +633,13 @@ int ast_audiohook_remove(struct ast_channel *chan, struct ast_audiohook *audioho static struct ast_frame *dtmf_audiohook_write_list(struct ast_channel *chan, struct ast_audiohook_list *audiohook_list, enum ast_audiohook_direction direction, struct ast_frame *frame) { struct ast_audiohook *audiohook = NULL; + int removed = 0; AST_LIST_TRAVERSE_SAFE_BEGIN(&audiohook_list->manipulate_list, audiohook, list) { ast_audiohook_lock(audiohook); if (audiohook->status != AST_AUDIOHOOK_STATUS_RUNNING) { AST_LIST_REMOVE_CURRENT(list); + removed = 1; ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_DONE); ast_audiohook_unlock(audiohook); audiohook->manipulate_callback(audiohook, NULL, NULL, 0); @@ -579,9 +651,77 @@ static struct ast_frame *dtmf_audiohook_write_list(struct ast_channel *chan, str } AST_LIST_TRAVERSE_SAFE_END; + /* if an audiohook got removed, reset samplerate compatibility */ + if (removed) { + audiohook_list_set_samplerate_compatibility(audiohook_list); + } return frame; } +static struct ast_frame *audiohook_list_translate_to_slin(struct ast_audiohook_list *audiohook_list, + enum ast_audiohook_direction direction, struct ast_frame *frame) +{ + struct ast_audiohook_translate *in_translate = (direction == AST_AUDIOHOOK_DIRECTION_READ ? + &audiohook_list->in_translate[0] : &audiohook_list->in_translate[1]); + struct ast_frame *new_frame = frame; + struct ast_format tmp_fmt; + enum ast_format_id slin_id; + + /* If we are capable of maintaining doing samplerates other that 8khz, update + * the internal audiohook_list's rate and higher samplerate audio arrives. By + * updating the list's rate, all the audiohooks in the list will be updated as well + * as the are written and read from. */ + if (audiohook_list->native_slin_compatible) { + audiohook_list->list_internal_samp_rate = + MAX(ast_format_rate(&frame->subclass.format), audiohook_list->list_internal_samp_rate); + } + + slin_id = ast_format_slin_by_rate(audiohook_list->list_internal_samp_rate); + + if (frame->subclass.format.id == slin_id) { + return new_frame; + } + + if (ast_format_cmp(&frame->subclass.format, &in_translate->format) == AST_FORMAT_CMP_NOT_EQUAL) { + if (in_translate->trans_pvt) { + ast_translator_free_path(in_translate->trans_pvt); + } + if (!(in_translate->trans_pvt = ast_translator_build_path(ast_format_set(&tmp_fmt, slin_id, 0), &frame->subclass.format))) { + return NULL; + } + ast_format_copy(&in_translate->format, &frame->subclass.format); + } + if (!(new_frame = ast_translate(in_translate->trans_pvt, frame, 0))) { + return NULL; + } + + return new_frame; +} + +static struct ast_frame *audiohook_list_translate_to_native(struct ast_audiohook_list *audiohook_list, + enum ast_audiohook_direction direction, struct ast_frame *slin_frame, struct ast_format *outformat) +{ + struct ast_audiohook_translate *out_translate = (direction == AST_AUDIOHOOK_DIRECTION_READ ? &audiohook_list->out_translate[0] : &audiohook_list->out_translate[1]); + struct ast_frame *outframe = NULL; + if (ast_format_cmp(&slin_frame->subclass.format, outformat) == AST_FORMAT_CMP_NOT_EQUAL) { + /* rebuild translators if necessary */ + if (ast_format_cmp(&out_translate->format, outformat) == AST_FORMAT_CMP_NOT_EQUAL) { + if (out_translate->trans_pvt) { + ast_translator_free_path(out_translate->trans_pvt); + } + if (!(out_translate->trans_pvt = ast_translator_build_path(outformat, &slin_frame->subclass.format))) { + return NULL; + } + ast_format_copy(&out_translate->format, outformat); + } + /* translate back to the format the frame came in as. */ + if (!(outframe = ast_translate(out_translate->trans_pvt, slin_frame, 0))) { + return NULL; + } + } + return outframe; +} + /*! * \brief Pass an AUDIO frame off to be handled by the audiohook core * @@ -595,15 +735,9 @@ static struct ast_frame *dtmf_audiohook_write_list(struct ast_channel *chan, str * SLINEAR format for Part_2. * Part_2: Send middle_frame off to spies and manipulators. At this point middle_frame is * either a new frame as result of the translation, or points directly to the start_frame - * because no translation to SLINEAR audio was required. The result of this part - * is end_frame will be updated to point to middle_frame if any audiohook manipulation - * took place. - * Part_3: Translate end_frame's audio back into the format of start frame if necessary. - * At this point if middle_frame != end_frame, we are guaranteed that no manipulation - * took place and middle_frame can be freed as it was translated... If middle_frame was - * not translated and still pointed to start_frame, it would be equal to end_frame as well - * regardless if manipulation took place which would not result in this free. The result - * of this part is end_frame is guaranteed to be the format of start_frame for the return. + * because no translation to SLINEAR audio was required. + * Part_3: Translate end_frame's audio back into the format of start frame if necessary. This + * is only necessary if manipulation of middle_frame occurred. * * \param chan Channel that the list is coming off of * \param audiohook_list List of audiohooks @@ -613,27 +747,17 @@ static struct ast_frame *dtmf_audiohook_write_list(struct ast_channel *chan, str */ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, struct ast_audiohook_list *audiohook_list, enum ast_audiohook_direction direction, struct ast_frame *frame) { - struct ast_audiohook_translate *in_translate = (direction == AST_AUDIOHOOK_DIRECTION_READ ? &audiohook_list->in_translate[0] : &audiohook_list->in_translate[1]); - struct ast_audiohook_translate *out_translate = (direction == AST_AUDIOHOOK_DIRECTION_READ ? &audiohook_list->out_translate[0] : &audiohook_list->out_translate[1]); struct ast_frame *start_frame = frame, *middle_frame = frame, *end_frame = frame; struct ast_audiohook *audiohook = NULL; - struct ast_format tmp_fmt; - int samples = frame->samples; + int samples; + int middle_frame_manipulated = 0; + int removed = 0; /* ---Part_1. translate start_frame to SLINEAR if necessary. */ - /* If the frame coming in is not signed linear we have to send it through the in_translate path */ - if (frame->subclass.format.id != AST_FORMAT_SLINEAR) { - if (ast_format_cmp(&frame->subclass.format, &in_translate->format) == AST_FORMAT_CMP_NOT_EQUAL) { - if (in_translate->trans_pvt) - ast_translator_free_path(in_translate->trans_pvt); - if (!(in_translate->trans_pvt = ast_translator_build_path(ast_format_set(&tmp_fmt, AST_FORMAT_SLINEAR, 0), &frame->subclass.format))) - return frame; - ast_format_copy(&in_translate->format, &frame->subclass.format); - } - if (!(middle_frame = ast_translate(in_translate->trans_pvt, frame, 0))) - return frame; - samples = middle_frame->samples; + if (!(middle_frame = audiohook_list_translate_to_slin(audiohook_list, direction, start_frame))) { + return frame; } + samples = middle_frame->samples; /* ---Part_2: Send middle_frame to spy and manipulator lists. middle_frame is guaranteed to be SLINEAR here.*/ /* Queue up signed linear frame to each spy */ @@ -641,10 +765,12 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st ast_audiohook_lock(audiohook); if (audiohook->status != AST_AUDIOHOOK_STATUS_RUNNING) { AST_LIST_REMOVE_CURRENT(list); + removed = 1; ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_DONE); ast_audiohook_unlock(audiohook); continue; } + audiohook_set_internal_rate(audiohook, audiohook_list->list_internal_samp_rate, 1); ast_audiohook_write_frame(audiohook, direction, middle_frame); ast_audiohook_unlock(audiohook); } @@ -659,10 +785,12 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st ast_audiohook_lock(audiohook); if (audiohook->status != AST_AUDIOHOOK_STATUS_RUNNING) { AST_LIST_REMOVE_CURRENT(list); + removed = 1; ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_DONE); ast_audiohook_unlock(audiohook); continue; } + audiohook_set_internal_rate(audiohook, audiohook_list->list_internal_samp_rate, 1); if (ast_slinfactory_available(&audiohook->write_factory) >= samples && ast_slinfactory_read(&audiohook->write_factory, read_buf, samples)) { /* Take audio from this whisper source and combine it into our main buffer */ for (i = 0, data1 = combine_buf, data2 = read_buf; i < samples; i++, data1++, data2++) @@ -672,9 +800,10 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st } AST_LIST_TRAVERSE_SAFE_END; /* We take all of the combined whisper sources and combine them into the audio being written out */ - for (i = 0, data1 = middle_frame->data.ptr, data2 = combine_buf; i < samples; i++, data1++, data2++) + for (i = 0, data1 = middle_frame->data.ptr, data2 = combine_buf; i < samples; i++, data1++, data2++) { ast_slinear_saturated_add(data1, data2); - end_frame = middle_frame; + } + middle_frame_manipulated = 1; } /* Pass off frame to manipulate audiohooks */ @@ -683,12 +812,14 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st ast_audiohook_lock(audiohook); if (audiohook->status != AST_AUDIOHOOK_STATUS_RUNNING) { AST_LIST_REMOVE_CURRENT(list); + removed = 1; ast_audiohook_update_status(audiohook, AST_AUDIOHOOK_STATUS_DONE); ast_audiohook_unlock(audiohook); /* We basically drop all of our links to the manipulate audiohook and prod it to do it's own destructive things */ audiohook->manipulate_callback(audiohook, chan, NULL, direction); continue; } + audiohook_set_internal_rate(audiohook, audiohook_list->list_internal_samp_rate, 1); /* Feed in frame to manipulation. */ if (audiohook->manipulate_callback(audiohook, chan, middle_frame, direction)) { /* XXX IGNORE FAILURE */ @@ -700,35 +831,27 @@ static struct ast_frame *audio_audiohook_write_list(struct ast_channel *chan, st ast_audiohook_unlock(audiohook); } AST_LIST_TRAVERSE_SAFE_END; - end_frame = middle_frame; + middle_frame_manipulated = 1; } /* ---Part_3: Decide what to do with the end_frame (whether to transcode or not) */ - if (middle_frame == end_frame) { - /* Middle frame was modified and became the end frame... let's see if we need to transcode */ - if (ast_format_cmp(&end_frame->subclass.format, &start_frame->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) { - if (ast_format_cmp(&out_translate->format, &start_frame->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) { - if (out_translate->trans_pvt) - ast_translator_free_path(out_translate->trans_pvt); - if (!(out_translate->trans_pvt = ast_translator_build_path(&start_frame->subclass.format, ast_format_set(&tmp_fmt, AST_FORMAT_SLINEAR, 0)))) { - /* We can't transcode this... drop our middle frame and return the original */ - ast_frfree(middle_frame); - return start_frame; - } - ast_format_copy(&out_translate->format, &start_frame->subclass.format); - } - /* Transcode from our middle (signed linear) frame to new format of the frame that came in */ - if (!(end_frame = ast_translate(out_translate->trans_pvt, middle_frame, 0))) { - /* Failed to transcode the frame... drop it and return the original */ - ast_frfree(middle_frame); - return start_frame; - } - /* Here's the scoop... middle frame is no longer of use to us */ - ast_frfree(middle_frame); + if (middle_frame_manipulated) { + if (!(end_frame = audiohook_list_translate_to_native(audiohook_list, direction, middle_frame, &start_frame->subclass.format))) { + /* translation failed, so just pass back the input frame */ + end_frame = start_frame; } } else { - /* No frame was modified, we can just drop our middle frame and pass the frame we got in out */ + end_frame = start_frame; + } + /* clean up our middle_frame if required */ + if (middle_frame != end_frame) { ast_frfree(middle_frame); + middle_frame = NULL; + } + + /* Before returning, if an audiohook got removed, reset samplerate compatibility */ + if (removed) { + audiohook_list_set_samplerate_compatibility(audiohook_list); } return end_frame; @@ -956,7 +1079,7 @@ static struct audiohook_volume *audiohook_volume_get(struct ast_channel *chan, i } /* Setup our audiohook structure so we can manipulate the audio */ - ast_audiohook_init(&audiohook_volume->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "Volume"); + ast_audiohook_init(&audiohook_volume->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "Volume", AST_AUDIOHOOK_MANIPULATE_ALL_RATES); audiohook_volume->audiohook.manipulate_callback = audiohook_volume_callback; /* Attach the audiohook_volume blob to the datastore and attach to the channel */ diff --git a/main/bridging.c b/main/bridging.c index 9de02aa34..f988d9694 100644 --- a/main/bridging.c +++ b/main/bridging.c @@ -721,11 +721,11 @@ static enum ast_bridge_channel_state bridge_channel_join_multithreaded(struct as /* Wait for data to either come from the channel or us to be signalled */ if (!bridge_channel->suspended) { - ast_debug(1, "Going into a multithreaded waitfor for bridge channel %p of bridge %p\n", bridge_channel, bridge_channel->bridge); + ast_debug(10, "Going into a multithreaded waitfor for bridge channel %p of bridge %p\n", bridge_channel, bridge_channel->bridge); chan = ast_waitfor_nandfds(&bridge_channel->chan, 1, fds, nfds, NULL, &outfd, &ms); } else { ast_mutex_lock(&bridge_channel->lock); - ast_debug(1, "Going into a multithreaded signal wait for bridge channel %p of bridge %p\n", bridge_channel, bridge_channel->bridge); + ast_debug(10, "Going into a multithreaded signal wait for bridge channel %p of bridge %p\n", bridge_channel, bridge_channel->bridge); ast_cond_wait(&bridge_channel->cond, &bridge_channel->lock); ast_mutex_unlock(&bridge_channel->lock); } diff --git a/main/channel.c b/main/channel.c index 093d5f619..404732823 100644 --- a/main/channel.c +++ b/main/channel.c @@ -1006,7 +1006,14 @@ struct ast_format *ast_best_codec(struct ast_format_cap *cap, struct ast_format /*! G.722 is better then all below, but not as common as the above... so give ulaw and alaw priority */ AST_FORMAT_G722, /*! Okay, well, signed linear is easy to translate into other stuff */ + AST_FORMAT_SLINEAR192, + AST_FORMAT_SLINEAR96, + AST_FORMAT_SLINEAR48, + AST_FORMAT_SLINEAR44, + AST_FORMAT_SLINEAR32, + AST_FORMAT_SLINEAR24, AST_FORMAT_SLINEAR16, + AST_FORMAT_SLINEAR12, AST_FORMAT_SLINEAR, /*! G.726 is standard ADPCM, in RFC3551 packing order */ AST_FORMAT_G726, @@ -1020,8 +1027,11 @@ struct ast_format *ast_best_codec(struct ast_format_cap *cap, struct ast_format /*! iLBC is not too bad */ AST_FORMAT_ILBC, /*! Speex is free, but computationally more expensive than GSM */ + AST_FORMAT_SPEEX32, AST_FORMAT_SPEEX16, AST_FORMAT_SPEEX, + /*! SILK is pretty awesome. */ + AST_FORMAT_SILK, /*! Ick, LPC10 sounds terrible, but at least we have code for it, if you're tacky enough to use it */ AST_FORMAT_LPC10, @@ -1035,7 +1045,7 @@ struct ast_format *ast_best_codec(struct ast_format_cap *cap, struct ast_format /* Find the first preferred codec in the format given */ for (x = 0; x < ARRAY_LEN(prefs); x++) { - if (ast_format_cap_iscompatible(cap, ast_format_set(result, prefs[x], 0))) { + if (ast_format_cap_best_byid(cap, prefs[x], result)) { return result; } } @@ -5778,12 +5788,16 @@ static int ast_channel_make_compatible_helper(struct ast_channel *from, struct a * no direct conversion available. If generic PLC is * desired, then transcoding via SLINEAR is a requirement */ - use_slin = (best_src_fmt.id == AST_FORMAT_SLINEAR || best_dst_fmt.id == AST_FORMAT_SLINEAR); + use_slin = ast_format_is_slinear(&best_src_fmt) || ast_format_is_slinear(&best_dst_fmt) ? 1 : 0; if ((ast_format_cmp(&best_src_fmt, &best_dst_fmt) == AST_FORMAT_CMP_NOT_EQUAL) && (ast_opt_generic_plc || ast_opt_transcode_via_slin) && (ast_translate_path_steps(&best_dst_fmt, &best_src_fmt) != 1 || use_slin)) { - ast_format_set(&best_dst_fmt, AST_FORMAT_SLINEAR, 0); + int best_sample_rate = ast_format_rate(&best_src_fmt) > ast_format_rate(&best_dst_fmt) ? + ast_format_rate(&best_src_fmt) : ast_format_rate(&best_dst_fmt); + + /* pick the best signed linear format based upon what preserves the sample rate the best. */ + ast_format_set(&best_dst_fmt, ast_format_slin_by_rate(best_sample_rate), 0); } if (ast_set_read_format(from, &best_dst_fmt) < 0) { diff --git a/main/data.c b/main/data.c index 3ca2f7c27..2503cb57d 100644 --- a/main/data.c +++ b/main/data.c @@ -3111,11 +3111,12 @@ int ast_data_add_codec(struct ast_data *root, const char *node_name, struct ast_ if (!codecs) { return -1; } - fmlist = ast_get_format_list(&fmlist_size); + fmlist = ast_format_list_get(&fmlist_size); for (x = 0; x < fmlist_size; x++) { - if (fmlist[x].id == format->id) { + if (ast_format_cmp(&fmlist[x].format, format) == AST_FORMAT_CMP_EQUAL) { codec = ast_data_add_node(codecs, "codec"); if (!codec) { + ast_format_list_destroy(fmlist); return -1; } ast_data_add_str(codec, "name", fmlist[x].name); @@ -3124,6 +3125,7 @@ int ast_data_add_codec(struct ast_data *root, const char *node_name, struct ast_ ast_data_add_int(codec, "frame_length", fmlist[x].fr_len); } } + ast_format_list_destroy(fmlist); return 0; } @@ -3133,18 +3135,18 @@ int ast_data_add_codecs(struct ast_data *root, const char *node_name, struct ast struct ast_data *codecs, *codec; size_t fmlist_size; const struct ast_format_list *fmlist; - struct ast_format tmp_fmt; int x; codecs = ast_data_add_node(root, node_name); if (!codecs) { return -1; } - fmlist = ast_get_format_list(&fmlist_size); + fmlist = ast_format_list_get(&fmlist_size); for (x = 0; x < fmlist_size; x++) { - if (ast_format_cap_iscompatible(cap, ast_format_set(&tmp_fmt, fmlist[x].id, 0))) { + if (ast_format_cap_iscompatible(cap, &fmlist[x].format)) { codec = ast_data_add_node(codecs, "codec"); if (!codec) { + ast_format_list_destroy(fmlist); return -1; } ast_data_add_str(codec, "name", fmlist[x].name); @@ -3153,6 +3155,7 @@ int ast_data_add_codecs(struct ast_data *root, const char *node_name, struct ast ast_data_add_int(codec, "frame_length", fmlist[x].fr_len); } } + ast_format_list_destroy(fmlist); return 0; } diff --git a/main/format.c b/main/format.c index d77d244a6..28b15ae21 100644 --- a/main/format.c +++ b/main/format.c @@ -4,6 +4,7 @@ * Copyright (C) 2010, Digium, Inc. * * David Vossel <dvossel@digium.com> + * Mark Spencer <markster@digium.com> * * See http://www.asterisk.org for more information about * the Asterisk project. Please do not directly contact @@ -21,6 +22,7 @@ * \brief Format API * * \author David Vossel <dvossel@digium.com> + * \author Mark Spencer <markster@digium.com> */ #include "asterisk.h" @@ -32,6 +34,13 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$"); #include "asterisk/format.h" #include "asterisk/astobj2.h" #include "asterisk/lock.h" +#include "asterisk/frame.h" +#include "asterisk/utils.h" +#include "asterisk/cli.h" +#include "asterisk/rtp_engine.h" +#include "asterisk/config.h" + +#define FORMAT_CONFIG "codecs.conf" /*! This is the container for all the format attribute interfaces. * An ao2 container was chosen for fast lookup. */ @@ -51,6 +60,17 @@ struct interface_ao2_wrapper { ast_rwlock_t wraplock; }; +/*! \brief Format List container, This container is never directly accessed outside + * of this file, and It only exists for building the format_list_array. */ +static struct ao2_container *format_list; +/*! \brief Format List array is a read only array protected by a read write lock. + * This array may be used outside this file with the use of reference counting to + * guarantee safety for access by multiple threads. */ +static struct ast_format_list *format_list_array; +static size_t format_list_array_len = 0; +/*! \brief Locks the format list array so a reference can be taken safely. */ +static ast_rwlock_t format_list_array_lock; + static int interface_cmp_cb(void *obj, void *arg, int flags) { struct interface_ao2_wrapper *wrapper1 = obj; @@ -86,6 +106,23 @@ int ast_format_get_video_mark(const struct ast_format *format) return format->fattr.rtp_marker_bit; } +static int has_interface(const struct ast_format *format) +{ + struct interface_ao2_wrapper *wrapper; + struct interface_ao2_wrapper tmp_wrapper = { + .id = format->id, + }; + + ast_rwlock_rdlock(&ilock); + if (!(wrapper = ao2_find(interfaces, &tmp_wrapper, (OBJ_POINTER | OBJ_NOLOCK)))) { + ast_rwlock_unlock(&ilock); + return 0; + } + ast_rwlock_unlock(&ilock); + ao2_ref(wrapper, -1); + return 1; +} + static struct interface_ao2_wrapper *find_interface(const struct ast_format *format) { struct interface_ao2_wrapper *wrapper; @@ -166,7 +203,7 @@ void ast_format_clear(struct ast_format *format) /*! \internal * \brief determine if a list of attribute key value pairs are set on a format */ -static int format_isset_helper(struct ast_format *format, va_list ap) +static int format_isset_helper(const struct ast_format *format, va_list ap) { int res; struct interface_ao2_wrapper *wrapper; @@ -189,18 +226,24 @@ static int format_isset_helper(struct ast_format *format, va_list ap) return -1; } - wrapper->interface->format_attr_set(&tmp.fattr, ap); - - /* use our tmp structure to tell if the attributes are set or not */ - res = wrapper->interface->format_attr_cmp(&tmp.fattr, &format->fattr); + /* if isset is present, use that function, else just build a new + * format and use the cmp function */ + if (wrapper->interface->format_attr_isset) { + res = wrapper->interface->format_attr_isset(&format->fattr, ap); + } else { + wrapper->interface->format_attr_set(&tmp.fattr, ap); + /* use our tmp structure to tell if the attributes are set or not */ + res = wrapper->interface->format_attr_cmp(&tmp.fattr, &format->fattr); + res = (res == AST_FORMAT_CMP_NOT_EQUAL) ? -1 : 0; + } ast_rwlock_unlock(&wrapper->wraplock); ao2_ref(wrapper, -1); - return (res == AST_FORMAT_CMP_NOT_EQUAL) ? -1 : 0; + return res; } -int ast_format_isset(struct ast_format *format, ... ) +int ast_format_isset(const struct ast_format *format, ... ) { va_list ap; int res; @@ -211,6 +254,29 @@ int ast_format_isset(struct ast_format *format, ... ) return res; } +int ast_format_get_value(const struct ast_format *format, int key, void *value) +{ + int res = 0; + struct interface_ao2_wrapper *wrapper; + if (!(wrapper = find_interface(format))) { + return -1; + } + ast_rwlock_rdlock(&wrapper->wraplock); + if (!wrapper->interface || + !wrapper->interface->format_attr_get_val) { + + ast_rwlock_unlock(&wrapper->wraplock); + ao2_ref(wrapper, -1); + return -1; + } + + res = wrapper->interface->format_attr_get_val(&format->fattr, key, value); + + ast_rwlock_unlock(&wrapper->wraplock); + ao2_ref(wrapper, -1); + + return res; +} /*! \internal * \brief cmp format attributes using an interface @@ -372,6 +438,8 @@ uint64_t ast_format_id_to_old_bitfield(enum ast_format_id id) /*! T.140 Text format - ITU T.140, RFC 4103 */ case AST_FORMAT_T140: return (1ULL << 27); + default: + return 0; /* not supported by old bitfield. */ } return 0; @@ -486,20 +554,709 @@ enum ast_format_id ast_format_id_from_old_bitfield(uint64_t src) return 0; } +int ast_format_is_slinear(const struct ast_format *format) +{ + if (format->id == AST_FORMAT_SLINEAR || + format->id == AST_FORMAT_SLINEAR12 || + format->id == AST_FORMAT_SLINEAR16 || + format->id == AST_FORMAT_SLINEAR24 || + format->id == AST_FORMAT_SLINEAR32 || + format->id == AST_FORMAT_SLINEAR44 || + format->id == AST_FORMAT_SLINEAR48 || + format->id == AST_FORMAT_SLINEAR96 || + format->id == AST_FORMAT_SLINEAR192) { + return 1; + } + return 0; +} + +enum ast_format_id ast_format_slin_by_rate(unsigned int rate) +{ + if (rate >= 192000) { + return AST_FORMAT_SLINEAR192; + } else if (rate >= 96000) { + return AST_FORMAT_SLINEAR96; + } else if (rate >= 48000) { + return AST_FORMAT_SLINEAR48; + } else if (rate >= 44100) { + return AST_FORMAT_SLINEAR44; + } else if (rate >= 32000) { + return AST_FORMAT_SLINEAR32; + } else if (rate >= 24000) { + return AST_FORMAT_SLINEAR24; + } else if (rate >= 16000) { + return AST_FORMAT_SLINEAR16; + } else if (rate >= 12000) { + return AST_FORMAT_SLINEAR12; + } + return AST_FORMAT_SLINEAR; +} + +const char* ast_getformatname(const struct ast_format *format) +{ + int x; + const char *ret = "unknown"; + size_t f_len; + const struct ast_format_list *f_list = ast_format_list_get(&f_len); + for (x = 0; x < f_len; x++) { + if (ast_format_cmp(&f_list[x].format, format) == AST_FORMAT_CMP_EQUAL) { + ret = f_list[x].name; + break; + } + } + f_list = ast_format_list_destroy(f_list); + return ret; +} + + +char *ast_getformatname_multiple_byid(char *buf, size_t size, enum ast_format_id id) +{ + int x; + unsigned len; + char *start, *end = buf; + size_t f_len; + const struct ast_format_list *f_list = ast_format_list_get(&f_len); + + if (!size) { + f_list = ast_format_list_destroy(f_list); + return buf; + } + snprintf(end, size, "("); + len = strlen(end); + end += len; + size -= len; + start = end; + for (x = 0; x < f_len; x++) { + if (f_list[x].format.id == id) { + snprintf(end, size, "%s|", f_list[x].name); + len = strlen(end); + end += len; + size -= len; + } + } + if (start == end) { + ast_copy_string(start, "nothing)", size); + } else if (size > 1) { + *(end - 1) = ')'; + } + f_list = ast_format_list_destroy(f_list); + return buf; +} + +static struct ast_codec_alias_table { + const char *alias; + const char *realname; +} ast_codec_alias_table[] = { + { "slinear", "slin"}, + { "slinear16", "slin16"}, + { "g723.1", "g723"}, + { "g722.1", "siren7"}, + { "g722.1c", "siren14"}, +}; + +static const char *ast_expand_codec_alias(const char *in) +{ + int x; + + for (x = 0; x < ARRAY_LEN(ast_codec_alias_table); x++) { + if (!strcmp(in,ast_codec_alias_table[x].alias)) + return ast_codec_alias_table[x].realname; + } + return in; +} + +struct ast_format *ast_getformatbyname(const char *name, struct ast_format *result) +{ + int x; + size_t f_len; + const struct ast_format_list *f_list = ast_format_list_get(&f_len); + + for (x = 0; x < f_len; x++) { + if (!strcasecmp(f_list[x].name, name) || + !strcasecmp(f_list[x].name, ast_expand_codec_alias(name))) { + + ast_format_copy(result, &f_list[x].format); + f_list = ast_format_list_destroy(f_list); + return result; + } + } + f_list = ast_format_list_destroy(f_list); + + return NULL; +} + +const char *ast_codec2str(struct ast_format *format) +{ + int x; + const char *ret = "unknown"; + size_t f_len; + const struct ast_format_list *f_list = ast_format_list_get(&f_len); + + for (x = 0; x < f_len; x++) { + if (ast_format_cmp(&f_list[x].format, format) == AST_FORMAT_CMP_EQUAL) { + ret = f_list[x].desc; + break; + } + } + f_list = ast_format_list_destroy(f_list); + return ret; +} + +int ast_format_rate(const struct ast_format *format) +{ + switch (format->id) { + case AST_FORMAT_SLINEAR12: + return 12000; + case AST_FORMAT_SLINEAR24: + return 24000; + case AST_FORMAT_SLINEAR32: + return 32000; + case AST_FORMAT_SLINEAR44: + return 44100; + case AST_FORMAT_SLINEAR48: + return 48000; + case AST_FORMAT_SLINEAR96: + return 96000; + case AST_FORMAT_SLINEAR192: + return 192000; + case AST_FORMAT_G722: + case AST_FORMAT_SLINEAR16: + case AST_FORMAT_SIREN7: + case AST_FORMAT_SPEEX16: + return 16000; + case AST_FORMAT_SIREN14: + case AST_FORMAT_SPEEX32: + return 32000; + case AST_FORMAT_G719: + return 48000; + case AST_FORMAT_SILK: + if (!(ast_format_isset(format, + SILK_ATTR_KEY_SAMP_RATE, + SILK_ATTR_VAL_SAMP_24KHZ, + AST_FORMAT_ATTR_END))) { + return 24000; + } else if (!(ast_format_isset(format, + SILK_ATTR_KEY_SAMP_RATE, + SILK_ATTR_VAL_SAMP_16KHZ, + AST_FORMAT_ATTR_END))) { + return 16000; + } else if (!(ast_format_isset(format, + SILK_ATTR_KEY_SAMP_RATE, + SILK_ATTR_VAL_SAMP_12KHZ, + AST_FORMAT_ATTR_END))) { + return 12000; + } else { + return 8000; + } + default: + return 8000; + } +} + +static char *show_codecs(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + int x, found=0; + size_t f_len; + const struct ast_format_list *f_list; + + switch (cmd) { + case CLI_INIT: + e->command = "core show codecs [audio|video|image|text]"; + e->usage = + "Usage: core show codecs [audio|video|image|text]\n" + " Displays codec mapping\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if ((a->argc < 3) || (a->argc > 4)) { + return CLI_SHOWUSAGE; + } + + f_list = ast_format_list_get(&f_len); + if (!ast_opt_dont_warn) { + ast_cli(a->fd, "Disclaimer: this command is for informational purposes only.\n" + "\tIt does not indicate anything about your configuration.\n"); + } + + ast_cli(a->fd, "%8s %5s %8s %s\n","ID","TYPE","NAME","DESCRIPTION"); + ast_cli(a->fd, "-----------------------------------------------------------------------------------\n"); + + for (x = 0; x < f_len; x++) { + if (a->argc == 4) { + if (!strcasecmp(a->argv[3], "audio")) { + if (AST_FORMAT_GET_TYPE(f_list[x].format.id) != AST_FORMAT_TYPE_AUDIO) { + continue; + } + } else if (!strcasecmp(a->argv[3], "video")) { + if (AST_FORMAT_GET_TYPE(f_list[x].format.id) != AST_FORMAT_TYPE_VIDEO) { + continue; + } + } else if (!strcasecmp(a->argv[3], "image")) { + if (AST_FORMAT_GET_TYPE(f_list[x].format.id) != AST_FORMAT_TYPE_IMAGE) { + continue; + } + } else if (!strcasecmp(a->argv[3], "text")) { + if (AST_FORMAT_GET_TYPE(f_list[x].format.id) != AST_FORMAT_TYPE_TEXT) { + continue; + } + } else { + continue; + } + } + + ast_cli(a->fd, "%8u %5s %8s (%s)\n", + f_list[x].format.id, + (AST_FORMAT_GET_TYPE(f_list[x].format.id) == AST_FORMAT_TYPE_AUDIO) ? "audio" : + (AST_FORMAT_GET_TYPE(f_list[x].format.id) == AST_FORMAT_TYPE_IMAGE) ? "image" : + (AST_FORMAT_GET_TYPE(f_list[x].format.id) == AST_FORMAT_TYPE_VIDEO) ? "video" : + (AST_FORMAT_GET_TYPE(f_list[x].format.id) == AST_FORMAT_TYPE_TEXT) ? "text" : + "(unk)", + f_list[x].name, + f_list[x].desc); + found = 1; + } + + f_list = ast_format_list_destroy(f_list); + if (!found) { + return CLI_SHOWUSAGE; + } else { + return CLI_SUCCESS; + } +} + +static char *show_codec_n(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + enum ast_format_id format_id; + int x, found = 0; + int type_punned_codec; + size_t f_len; + const struct ast_format_list *f_list; + + switch (cmd) { + case CLI_INIT: + e->command = "core show codec"; + e->usage = + "Usage: core show codec <number>\n" + " Displays codec mapping\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (a->argc != 4) { + return CLI_SHOWUSAGE; + } + + if (sscanf(a->argv[3], "%30d", &type_punned_codec) != 1) { + return CLI_SHOWUSAGE; + } + format_id = type_punned_codec; + + f_list = ast_format_list_get(&f_len); + for (x = 0; x < f_len; x++) { + if (f_list[x].format.id == format_id) { + found = 1; + ast_cli(a->fd, "%11u %s\n", (unsigned int) format_id, f_list[x].desc); + break; + } + } + + if (!found) { + ast_cli(a->fd, "Codec %d not found\n", format_id); + } + + f_list = ast_format_list_destroy(f_list); + return CLI_SUCCESS; +} + +/* Builtin Asterisk CLI-commands for debugging */ +static struct ast_cli_entry my_clis[] = { + AST_CLI_DEFINE(show_codecs, "Displays a list of codecs"), + AST_CLI_DEFINE(show_codec_n, "Shows a specific codec"), +}; +int init_framer(void) +{ + ast_cli_register_multiple(my_clis, ARRAY_LEN(my_clis)); + return 0; +} + +static int format_list_add_custom(struct ast_format_list *new) +{ + struct ast_format_list *entry; + if (!(entry = ao2_alloc(sizeof(*entry), NULL))) { + return -1; + } + memcpy(entry, new, sizeof(struct ast_format_list)); + entry->custom_entry = 1; + ao2_link(format_list, entry); + return 0; +} +static int format_list_add_static( + const struct ast_format *format, + const char *name, + int samplespersecond, + const char *description, + int fr_len, + int min_ms, + int max_ms, + int inc_ms, + int def_ms, + unsigned int flags, + int cur_ms) +{ + struct ast_format_list *entry; + if (!(entry = ao2_alloc(sizeof(*entry), NULL))) { + return -1; + } + ast_format_copy(&entry->format, format); + ast_copy_string(entry->name, name, sizeof(entry->name)); + ast_copy_string(entry->desc, description, sizeof(entry->desc)); + entry->samplespersecond = samplespersecond; + entry->fr_len = fr_len; + entry->min_ms = min_ms; + entry->max_ms = max_ms; + entry->inc_ms = inc_ms; + entry->def_ms = def_ms; + entry->flags = flags; + entry->cur_ms = cur_ms; + entry->custom_entry = 0; + + ao2_link(format_list, entry); + return 0; +} + +static int list_all_custom(void *obj, void *arg, int flag) +{ + struct ast_format_list *entry = obj; + return entry->custom_entry ? CMP_MATCH : 0; +} + +static int list_cmp_cb(void *obj, void *arg, int flags) +{ + struct ast_format_list *entry1 = obj; + struct ast_format_list *entry2 = arg; + + return (ast_format_cmp(&entry1->format, &entry2->format) == AST_FORMAT_CMP_EQUAL) ? CMP_MATCH | CMP_STOP : 0; +} +static int list_hash_cb(const void *obj, const int flags) +{ + return ao2_container_count(format_list); +} + +const struct ast_format_list *ast_format_list_get(size_t *size) +{ + struct ast_format_list *list; + ast_rwlock_rdlock(&format_list_array_lock); + ao2_ref(format_list_array, 1); + list = format_list_array; + *size = format_list_array_len; + ast_rwlock_unlock(&format_list_array_lock); + return list; +} +const struct ast_format_list *ast_format_list_destroy(const struct ast_format_list *list) +{ + ao2_ref((void *) list, -1); + return NULL; +} + +static int build_format_list_array(void) +{ + struct ast_format_list *tmp; + size_t arraysize = sizeof(struct ast_format_list) * ao2_container_count(format_list); + int i = 0; + struct ao2_iterator it; + + ast_rwlock_wrlock(&format_list_array_lock); + tmp = format_list_array; + if (!(format_list_array = ao2_alloc(arraysize, NULL))) { + format_list_array = tmp; + ast_rwlock_unlock(&format_list_array_lock); + return -1; + } + format_list_array_len = ao2_container_count(format_list); + if (tmp) { + ao2_ref(tmp, -1); + } + + /* walk through the container adding elements to the static array */ + it = ao2_iterator_init(format_list, 0); + while ((tmp = ao2_iterator_next(&it)) && (i < format_list_array_len)) { + memcpy(&format_list_array[i], tmp, sizeof(struct ast_format_list)); + ao2_ref(tmp, -1); + i++; + } + ao2_iterator_destroy(&it); + + ast_rwlock_unlock(&format_list_array_lock); + return 0; +} +static int format_list_init(void) +{ + struct ast_format tmpfmt; + if (!(format_list = ao2_container_alloc(283, list_hash_cb, list_cmp_cb))) { + return -1; + } + /* initiate static entries XXX DO NOT CHANGE THIS ORDER! */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_G723_1, 0), "g723", 8000, "G.723.1", 20, 30, 300, 30, 30, 0, 0); /*!< G723.1 */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_GSM, 0), "gsm", 8000, "GSM", 33, 20, 300, 20, 20, 0, 0); /*!< codec_gsm.c */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_ULAW, 0), "ulaw", 8000, "G.711 u-law", 80, 10, 150, 10, 20, 0, 0); /*!< codec_ulaw.c */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_ALAW, 0), "alaw", 8000, "G.711 A-law", 80, 10, 150, 10, 20, 0, 0); /*!< codec_alaw.c */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_G726, 0), "g726", 8000, "G.726 RFC3551", 40, 10, 300, 10, 20, 0, 0); /*!< codec_g726.c */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_ADPCM, 0), "adpcm" , 8000, "ADPCM", 40, 10, 300, 10, 20, 0, 0); /*!< codec_adpcm.c */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0), "slin", 8000, "16 bit Signed Linear PCM", 160, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0); /*!< Signed linear */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_LPC10, 0), "lpc10", 8000, "LPC10", 7, 20, 20, 20, 20, 0, 0); /*!< codec_lpc10.c */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_G729A, 0), "g729", 8000, "G.729A", 10, 10, 230, 10, 20, AST_SMOOTHER_FLAG_G729, 0); /*!< Binary commercial distribution */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SPEEX, 0), "speex", 8000, "SpeeX", 10, 10, 60, 10, 20, 0, 0); /*!< codec_speex.c */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SPEEX16, 0), "speex16", 16000, "SpeeX 16khz", 10, 10, 60, 10, 20, 0, 0); /*!< codec_speex.c */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_ILBC, 0), "ilbc", 8000, "iLBC", 50, 30, 30, 30, 30, 0, 0); /*!< codec_ilbc.c */ /* inc=30ms - workaround */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_G726_AAL2, 0), "g726aal2", 8000, "G.726 AAL2", 40, 10, 300, 10, 20, 0, 0); /*!< codec_g726.c */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_G722, 0), "g722", 16000, "G722", 80, 10, 150, 10, 20, 0, 0); /*!< codec_g722.c */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR16, 0), "slin16", 16000, "16 bit Signed Linear PCM (16kHz)", 320, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0);/*!< Signed linear (16kHz) */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_JPEG, 0), "jpeg", 0, "JPEG image", 0, 0, 0, 0 ,0 ,0 ,0); /*!< See format_jpeg.c */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_PNG, 0), "png", 0, "PNG image", 0, 0, 0, 0 ,0 ,0 ,0); /*!< PNG Image format */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_H261, 0), "h261", 0, "H.261 Video", 0, 0, 0, 0 ,0 ,0 ,0); /*!< H.261 Video Passthrough */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_H263, 0), "h263", 0, "H.263 Video", 0, 0, 0, 0 ,0 ,0 ,0); /*!< H.263 Passthrough support, see format_h263.c */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_H263_PLUS, 0), "h263p", 0, "H.263+ Video", 0, 0, 0,0 ,0 ,0, 0); /*!< H.263plus passthrough support See format_h263.c */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_H264, 0), "h264", 0, "H.264 Video", 0, 0, 0, 0 ,0 ,0, 0); /*!< Passthrough support, see format_h263.c */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_MP4_VIDEO, 0), "mpeg4", 0, "MPEG4 Video", 0, 0, 0, 0, 0 ,0, 0); /*!< Passthrough support for MPEG4 */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_T140RED, 0), "red", 1, "T.140 Realtime Text with redundancy", 0, 0, 0,0 ,0 ,0, 0); /*!< Redundant T.140 Realtime Text */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_T140, 0), "t140", 0, "Passthrough T.140 Realtime Text", 0, 0, 0, 0 ,0 ,0, 0); /*!< Passthrough support for T.140 Realtime Text */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SIREN7, 0), "siren7", 16000, "ITU G.722.1 (Siren7, licensed from Polycom)", 80, 20, 80, 20, 20, 0, 0); /*!< Binary commercial distribution */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SIREN14, 0), "siren14", 32000, "ITU G.722.1 Annex C, (Siren14, licensed from Polycom)", 120, 20, 80, 20, 20, 0, 0); /*!< Binary commercial distribution */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_TESTLAW, 0), "testlaw", 8000, "G.711 test-law", 80, 10, 150, 10, 20, 0, 0); /*!< codec_ulaw.c */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_G719, 0), "g719", 48000, "ITU G.719", 160, 20, 80, 20, 20, 0, 0); + + /* ORDER MAY CHANGE AFTER THIS POINT IN THE LIST */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SPEEX32, 0), "speex32", 32000, "SpeeX 32khz", 10, 10, 60, 10, 20, 0, 0); /*!< codec_speex.c */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR12, 0), "slin12", 12000, "16 bit Signed Linear PCM (12kHz)", 240, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0);/*!< Signed linear (12kHz) */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR24, 0), "slin24", 24000, "16 bit Signed Linear PCM (24kHz)", 480, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0);/*!< Signed linear (24kHz) */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR32, 0), "slin32", 32000, "16 bit Signed Linear PCM (32kHz)", 640, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0);/*!< Signed linear (32kHz) */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR44, 0), "slin44", 44100, "16 bit Signed Linear PCM (44kHz)", 882, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0);/*!< Signed linear (44.1kHz) */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR48, 0), "slin48", 48000, "16 bit Signed Linear PCM (48kHz)", 960, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0);/*!< Signed linear (48kHz) */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR96, 0), "slin96", 96000, "16 bit Signed Linear PCM (96kHz)", 1920, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0);/*!< Signed linear (96kHz) */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR192, 0), "slin192", 192000, "16 bit Signed Linear PCM (192kHz)", 3840, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE, 0);/*!< Signed linear (192kHz) */ + + return 0; +} + +int ast_format_list_init() +{ + if (ast_rwlock_init(&format_list_array_lock)) { + return -1; + } + if (format_list_init()) { + goto init_list_cleanup; + } + if (build_format_list_array()) { + goto init_list_cleanup; + } + + return 0; +init_list_cleanup: + + ast_rwlock_destroy(&format_list_array_lock); + ao2_ref(format_list, -1); + if (format_list_array) { + ao2_ref(format_list_array, -1); + } + return -1; +} + int ast_format_attr_init() { + ast_cli_register_multiple(my_clis, ARRAY_LEN(my_clis)); if (ast_rwlock_init(&ilock)) { return -1; } + if (!(interfaces = ao2_container_alloc(283, interface_hash_cb, interface_cmp_cb))) { ast_rwlock_destroy(&ilock); + goto init_cleanup; + } + return 0; + +init_cleanup: + ast_rwlock_destroy(&ilock); + if (interfaces) { + ao2_ref(interfaces, -1); + } + return -1; +} + +static int custom_silk_format(struct ast_format_list *entry, unsigned int maxbitrate, int usedtx, int usefec, int packetloss_percentage) +{ + if (!entry->samplespersecond) { + ast_log(LOG_WARNING, "Custom SILK format definition '%s' requires sample rate to be defined.\n", entry->name); + } + ast_format_set(&entry->format, AST_FORMAT_SILK, 0); + + if (!has_interface(&entry->format)) { + return -1; + } + + switch (entry->samplespersecond) { + case 8000: + ast_copy_string(entry->desc, "SILK Custom Format 8khz", sizeof(entry->desc)); + ast_format_append(&entry->format, + SILK_ATTR_KEY_SAMP_RATE, SILK_ATTR_VAL_SAMP_8KHZ, + AST_FORMAT_ATTR_END); + break; + case 12000: + ast_copy_string(entry->desc, "SILK Custom Format 12khz", sizeof(entry->desc)); + ast_format_append(&entry->format, + SILK_ATTR_KEY_SAMP_RATE, SILK_ATTR_VAL_SAMP_12KHZ, + AST_FORMAT_ATTR_END); + break; + case 16000: + ast_copy_string(entry->desc, "SILK Custom Format 16khz", sizeof(entry->desc)); + ast_format_append(&entry->format, + SILK_ATTR_KEY_SAMP_RATE, SILK_ATTR_VAL_SAMP_16KHZ, + AST_FORMAT_ATTR_END); + break; + case 24000: + ast_copy_string(entry->desc, "SILK Custom Format 24khz", sizeof(entry->desc)); + ast_format_append(&entry->format, + SILK_ATTR_KEY_SAMP_RATE, SILK_ATTR_VAL_SAMP_24KHZ, + AST_FORMAT_ATTR_END); + break; + default: + ast_log(LOG_WARNING, "Custom SILK format definition '%s' can not support sample rate %d\n", entry->name, entry->samplespersecond); + return -1; + } + ast_format_append(&entry->format, + SILK_ATTR_KEY_MAX_BITRATE, maxbitrate, + SILK_ATTR_KEY_DTX, usedtx ? 1 : 0, + SILK_ATTR_KEY_FEC, usefec ? 1 : 0, + SILK_ATTR_KEY_PACKETLOSS_PERCENTAGE, packetloss_percentage, + AST_FORMAT_ATTR_END); + + entry->fr_len = 80; + entry->min_ms = 20; + entry->max_ms = 20; + entry->inc_ms = 20; + entry->def_ms = 20; + return 0; +} + +static int conf_process_format_name(const char *name, enum ast_format_id *id) +{ + if (!strcasecmp(name, "silk")) { + *id = AST_FORMAT_SILK; + } else { + *id = 0; + return -1; + } + return 0; +} + +static int conf_process_sample_rate(const char *rate, unsigned int *result) +{ + if (!strcasecmp(rate, "8000")) { + *result = 8000; + } else if (!strcasecmp(rate, "12000")) { + *result = 12000; + } else if (!strcasecmp(rate, "16000")) { + *result = 16000; + } else if (!strcasecmp(rate, "24000")) { + *result = 24000; + } else if (!strcasecmp(rate, "32000")) { + *result = 32000; + } else if (!strcasecmp(rate, "48000")) { + *result = 48000; + } else { + *result = 0; return -1; } + + return 0; +} +static int load_format_config(void) +{ + struct ast_flags config_flags = { 0, }; + struct ast_config *cfg = ast_config_load(FORMAT_CONFIG, config_flags); + struct ast_format_list entry; + struct ast_variable *var; + char *cat = NULL; + int add_it = 0; + + struct { + enum ast_format_id id; + unsigned int maxbitrate; + unsigned int packetloss_percentage; + int usefec; + int usedtx; + } settings; + + if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) { + return 0; + } + + /* remove all custom formats from the AO2 Container. Note, this has no affect on the + * global format list until the list is rebuild. That is why this is okay to do while + * reloading the config. */ + ao2_callback(format_list, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, list_all_custom, NULL); + + while ((cat = ast_category_browse(cfg, cat))) { + memset(&entry, 0, sizeof(entry)); + memset(&settings, 0, sizeof(settings)); + add_it = 0; + + if (!(ast_variable_retrieve(cfg, cat, "type"))) { + continue; + } + ast_copy_string(entry.name, cat, sizeof(entry.name)); + var = ast_variable_browse(cfg, cat); + for (var = ast_variable_browse(cfg, cat); var; var = var->next) { + if (!strcasecmp(var->name, "type") && conf_process_format_name(var->value, &settings.id)) { + ast_log(LOG_WARNING, "Can not make custom format type for '%s' at line %d of %s\n", + var->value, var->lineno, FORMAT_CONFIG); + continue; + } else if (!strcasecmp(var->name, "samprate") && conf_process_sample_rate(var->value, &entry.samplespersecond)) { + ast_log(LOG_WARNING, "Sample rate '%s' at line %d of %s is not supported.\n", + var->value, var->lineno, FORMAT_CONFIG); + } else if (!strcasecmp(var->name, "maxbitrate")) { + if (sscanf(var->value, "%30u", &settings.maxbitrate) != 1) { + ast_log(LOG_WARNING, "maxbitrate '%s' at line %d of %s is not supported.\n", + var->value, var->lineno, FORMAT_CONFIG); + } + } else if (!strcasecmp(var->name, "dtx")) { + settings.usedtx = ast_true(var->value) ? 1 : 0; + } else if (!strcasecmp(var->name, "fec")) { + settings.usefec = ast_true(var->value) ? 1 : 0; + } else if (!strcasecmp(var->name, "packetloss_percentage")) { + if ((sscanf(var->value, "%30u", &settings.packetloss_percentage) != 1) || (settings.packetloss_percentage > 100)) { + ast_log(LOG_WARNING, "packetloss_percentage '%s' at line %d of %s is not supported.\n", + var->value, var->lineno, FORMAT_CONFIG); + } + } + } + + switch (settings.id) { + case AST_FORMAT_SILK: + if (!(custom_silk_format(&entry, settings.maxbitrate, settings.usedtx, settings.usefec, settings.packetloss_percentage))) { + add_it = 1; + } + break; + default: + ast_log(LOG_WARNING, "Can not create custom format %s\n", entry.name); + } + + if (add_it) { + format_list_add_custom(&entry); + } + } + ast_config_destroy(cfg); + build_format_list_array(); return 0; } int ast_format_attr_reg_interface(const struct ast_format_attr_interface *interface) { + int x; + size_t f_len; + const struct ast_format_list *f_list; struct interface_ao2_wrapper *wrapper; struct interface_ao2_wrapper tmp_wrapper = { .id = interface->id, @@ -530,11 +1287,25 @@ int ast_format_attr_reg_interface(const struct ast_format_attr_interface *interf ao2_ref(wrapper, -1); + /* This will find all custom formats in codecs.conf for this new registered interface */ + load_format_config(); + + /* update the RTP engine to all custom formats created for this interface */ + f_list = ast_format_list_get(&f_len); + for (x = 0; x < f_len; x++) { + if (f_list[x].format.id == tmp_wrapper.id) { + ast_rtp_engine_load_format(&f_list[x].format); + } + } + return 0; } int ast_format_attr_unreg_interface(const struct ast_format_attr_interface *interface) { + int x; + size_t f_len; + const struct ast_format_list *f_list; struct interface_ao2_wrapper *wrapper; struct interface_ao2_wrapper tmp_wrapper = { .id = interface->id, @@ -554,5 +1325,16 @@ int ast_format_attr_unreg_interface(const struct ast_format_attr_interface *inte ao2_ref(wrapper, -1); + /* update the RTP engine to remove all custom formats created for this interface */ + f_list = ast_format_list_get(&f_len); + for (x = 0; x < f_len; x++) { + if (f_list[x].format.id == tmp_wrapper.id) { + ast_rtp_engine_unload_format(&f_list[x].format); + } + } + + /* This will remove all custom formats previously created for this interface */ + load_format_config(); + return 0; } diff --git a/main/format_cap.c b/main/format_cap.c index c8bdd4fa3..3ef0e74d3 100644 --- a/main/format_cap.c +++ b/main/format_cap.c @@ -99,7 +99,7 @@ void *ast_format_cap_destroy(struct ast_format_cap *cap) return NULL; } -void ast_format_cap_add(struct ast_format_cap *cap, struct ast_format *format) +void ast_format_cap_add(struct ast_format_cap *cap, const struct ast_format *format) { struct ast_format *fnew; @@ -122,26 +122,26 @@ void ast_format_cap_add_all_by_type(struct ast_format_cap *cap, enum ast_format_ { int x; size_t f_len = 0; - struct ast_format tmp_fmt; - const struct ast_format_list *f_list = ast_get_format_list(&f_len); + const struct ast_format_list *f_list = ast_format_list_get(&f_len); for (x = 0; x < f_len; x++) { - if (AST_FORMAT_GET_TYPE(f_list[x].id) == type) { - ast_format_cap_add(cap, ast_format_set(&tmp_fmt, f_list[x].id, 0)); + if (AST_FORMAT_GET_TYPE(f_list[x].format.id) == type) { + ast_format_cap_add(cap, &f_list[x].format); } } + ast_format_list_destroy(f_list); } void ast_format_cap_add_all(struct ast_format_cap *cap) { int x; size_t f_len = 0; - struct ast_format tmp_fmt; - const struct ast_format_list *f_list = ast_get_format_list(&f_len); + const struct ast_format_list *f_list = ast_format_list_get(&f_len); for (x = 0; x < f_len; x++) { - ast_format_cap_add(cap, ast_format_set(&tmp_fmt, f_list[x].id, 0)); + ast_format_cap_add(cap, &f_list[x].format); } + ast_format_list_destroy(f_list); } static int append_cb(void *obj, void *arg, int flag) @@ -288,6 +288,21 @@ void ast_format_cap_set(struct ast_format_cap *cap, struct ast_format *format) ast_format_cap_add(cap, format); } +int ast_format_cap_get_compatible_format(const struct ast_format_cap *cap, const struct ast_format *format, struct ast_format *result) +{ + struct ast_format *f; + struct ast_format_cap *tmp_cap = (struct ast_format_cap *) cap; + f = ao2_find(tmp_cap->formats, (struct ast_format *) format, OBJ_POINTER | tmp_cap->nolock); + + if (f) { + ast_format_copy(result, f); + ao2_ref(f, -1); + return 1; + } + ast_format_clear(result); + return 0; +} + int ast_format_cap_iscompatible(const struct ast_format_cap *cap, const struct ast_format *format) { struct ast_format *f; @@ -302,6 +317,38 @@ int ast_format_cap_iscompatible(const struct ast_format_cap *cap, const struct a return 0; } +struct byid_data { + struct ast_format *result; + enum ast_format_id id; +}; +static int find_best_byid_cb(void *obj, void *arg, int flag) +{ + struct ast_format *format = obj; + struct byid_data *data = arg; + + if (data->id != format->id) { + return 0; + } + if (!data->result->id || (ast_format_rate(data->result) < ast_format_rate(format))) { + ast_format_copy(data->result, format); + } + return 0; +} + +int ast_format_cap_best_byid(const struct ast_format_cap *cap, enum ast_format_id id, struct ast_format *result) +{ + struct byid_data data; + data.result = result; + data.id = id; + + ast_format_clear(result); + ao2_callback(cap->formats, + OBJ_MULTIPLE | OBJ_NODATA | cap->nolock, + find_best_byid_cb, + &data); + return result->id ? 1 : 0; +} + /*! \internal * \brief this struct is just used for the ast_format_cap_joint function so we can provide * both a format and a result ast_format_cap structure as arguments to the find_joint_cb @@ -525,6 +572,42 @@ int ast_format_cap_iter_next(struct ast_format_cap *cap, struct ast_format *form return 0; } +char *ast_getformatname_multiple(char *buf, size_t size, struct ast_format_cap *cap) +{ + int x; + unsigned len; + char *start, *end = buf; + struct ast_format tmp_fmt; + size_t f_len; + const struct ast_format_list *f_list = ast_format_list_get(&f_len); + + if (!size) { + f_list = ast_format_list_destroy(f_list); + return buf; + } + snprintf(end, size, "("); + len = strlen(end); + end += len; + size -= len; + start = end; + for (x = 0; x < f_len; x++) { + ast_format_copy(&tmp_fmt, &f_list[x].format); + if (ast_format_cap_iscompatible(cap, &tmp_fmt)) { + snprintf(end, size, "%s|", f_list[x].name); + len = strlen(end); + end += len; + size -= len; + } + } + if (start == end) { + ast_copy_string(start, "nothing)", size); + } else if (size > 1) { + *(end - 1) = ')'; + } + f_list = ast_format_list_destroy(f_list); + return buf; +} + uint64_t ast_format_cap_to_old_bitfield(const struct ast_format_cap *cap) { uint64_t res = 0; diff --git a/main/format_pref.c b/main/format_pref.c index 26801b648..f24dbec27 100644 --- a/main/format_pref.c +++ b/main/format_pref.c @@ -34,7 +34,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$"); void ast_codec_pref_convert(struct ast_codec_pref *pref, char *buf, size_t size, int right) { size_t f_len; - const struct ast_format_list *f_list = ast_get_format_list(&f_len); + const const struct ast_format_list *f_list = ast_format_list_get(&f_len); int x, differential = (int) 'A', mem; char *from, *to; @@ -57,9 +57,10 @@ void ast_codec_pref_convert(struct ast_codec_pref *pref, char *buf, size_t size, } to[x] = right ? (from[x] + differential) : (from[x] - differential); if (!right && to[x] && (to[x] < f_len)) { - ast_format_set(&pref->formats[x], f_list[to[x]-1].id , 0); + ast_format_copy(&pref->formats[x], &f_list[to[x]-1].format); } } + ast_format_list_destroy(f_list); } int ast_codec_pref_string(struct ast_codec_pref *pref, char *buf, size_t size) @@ -67,7 +68,7 @@ int ast_codec_pref_string(struct ast_codec_pref *pref, char *buf, size_t size) int x; struct ast_format format; size_t total_len, slen; - char *formatname; + const char *formatname; memset(buf, 0, size); total_len = size; @@ -116,23 +117,27 @@ void ast_codec_pref_remove(struct ast_codec_pref *pref, struct ast_format *forma struct ast_codec_pref oldorder; int x, y = 0; size_t f_len = 0; - const struct ast_format_list *f_list = ast_get_format_list(&f_len); + const const struct ast_format_list *f_list; - if (!pref->order[0]) + if (!pref->order[0]) { return; + } + f_list = ast_format_list_get(&f_len); memcpy(&oldorder, pref, sizeof(oldorder)); memset(pref, 0, sizeof(*pref)); for (x = 0; x < f_len; x++) { - if (!oldorder.order[x]) + if (!oldorder.order[x]) { break; - if (f_list[oldorder.order[x]-1].id != format->id) { + } + if (ast_format_cmp(&f_list[oldorder.order[x]-1].format, format) == AST_FORMAT_CMP_NOT_EQUAL) { pref->order[y] = oldorder.order[x]; ast_format_copy(&pref->formats[y], &oldorder.formats[x]); pref->framing[y++] = oldorder.framing[x]; } } + ast_format_list_destroy(f_list); } /*! \brief Append codec to list */ @@ -140,12 +145,12 @@ int ast_codec_pref_append(struct ast_codec_pref *pref, struct ast_format *format { int x, newindex = 0; size_t f_len = 0; - const struct ast_format_list *f_list = ast_get_format_list(&f_len); + const struct ast_format_list *f_list = ast_format_list_get(&f_len); ast_codec_pref_remove(pref, format); for (x = 0; x < f_len; x++) { - if (f_list[x].id == format->id) { + if (ast_format_cmp(&f_list[x].format, format) == AST_FORMAT_CMP_EQUAL) { newindex = x + 1; break; } @@ -161,6 +166,7 @@ int ast_codec_pref_append(struct ast_codec_pref *pref, struct ast_format *format } } + ast_format_list_destroy(f_list); return x; } @@ -169,18 +175,20 @@ void ast_codec_pref_prepend(struct ast_codec_pref *pref, struct ast_format *form { int x, newindex = 0; size_t f_len = 0; - const struct ast_format_list *f_list = ast_get_format_list(&f_len); + const struct ast_format_list *f_list = ast_format_list_get(&f_len); /* First step is to get the codecs "index number" */ for (x = 0; x < f_len; x++) { - if (f_list[x].id == format->id) { + if (ast_format_cmp(&f_list[x].format, format) == AST_FORMAT_CMP_EQUAL) { newindex = x + 1; break; } } /* Done if its unknown */ - if (!newindex) + if (!newindex) { + ast_format_list_destroy(f_list); return; + } /* Now find any existing occurrence, or the end */ for (x = 0; x < AST_CODEC_PREF_SIZE; x++) { @@ -188,8 +196,10 @@ void ast_codec_pref_prepend(struct ast_codec_pref *pref, struct ast_format *form break; } - if (only_if_existing && !pref->order[x]) + if (only_if_existing && !pref->order[x]) { + ast_format_list_destroy(f_list); return; + } /* Move down to make space to insert - either all the way to the end, or as far as the existing location (which will be overwritten) */ @@ -203,6 +213,7 @@ void ast_codec_pref_prepend(struct ast_codec_pref *pref, struct ast_format *form pref->order[0] = newindex; pref->framing[0] = 0; /* ? */ ast_format_copy(&pref->formats[0], format); + ast_format_list_destroy(f_list); } /*! \brief Set packet size for codec */ @@ -210,17 +221,19 @@ int ast_codec_pref_setsize(struct ast_codec_pref *pref, struct ast_format *forma { int x, idx = -1; size_t f_len = 0; - const struct ast_format_list *f_list = ast_get_format_list(&f_len); + const struct ast_format_list *f_list = ast_format_list_get(&f_len); for (x = 0; x < f_len; x++) { - if (f_list[x].id == format->id) { + if (ast_format_cmp(&f_list[x].format, format) == AST_FORMAT_CMP_EQUAL) { idx = x; break; } } - if (idx < 0) + if (idx < 0) { + ast_format_list_destroy(f_list); return -1; + } /* size validation */ if (!framems) @@ -242,6 +255,7 @@ int ast_codec_pref_setsize(struct ast_codec_pref *pref, struct ast_format *forma } } + ast_format_list_destroy(f_list); return x; } @@ -249,12 +263,12 @@ int ast_codec_pref_setsize(struct ast_codec_pref *pref, struct ast_format *forma struct ast_format_list ast_codec_pref_getsize(struct ast_codec_pref *pref, struct ast_format *format) { int x, idx = -1, framems = 0; - struct ast_format_list fmt = { 0, }; + struct ast_format_list fmt = { { 0, }, }; size_t f_len = 0; - const struct ast_format_list *f_list = ast_get_format_list(&f_len); + const struct ast_format_list *f_list = ast_format_list_get(&f_len); for (x = 0; x < f_len; x++) { - if (f_list[x].id == format->id) { + if (ast_format_cmp(&f_list[x].format, format) == AST_FORMAT_CMP_EQUAL) { fmt = f_list[x]; idx = x; break; @@ -282,7 +296,7 @@ struct ast_format_list ast_codec_pref_getsize(struct ast_codec_pref *pref, struc framems = f_list[idx].max_ms; fmt.cur_ms = framems; - + ast_format_list_destroy(f_list); return fmt; } @@ -291,27 +305,23 @@ struct ast_format *ast_codec_choose(struct ast_codec_pref *pref, struct ast_form { int x, slot, found; size_t f_len = 0; - struct ast_format tmp_fmt; - - const struct ast_format_list *f_list = ast_get_format_list(&f_len); - - ast_format_clear(result); + const struct ast_format_list *f_list = ast_format_list_get(&f_len); for (x = 0; x < f_len; x++) { slot = pref->order[x]; if (!slot) break; - if (ast_format_cap_iscompatible(cap, ast_format_set(&tmp_fmt, f_list[slot-1].id, 0))) { - found = 1; /*format is found and stored in tmp_fmt */ + if (ast_format_cap_get_compatible_format(cap, &f_list[slot-1].format, result)) { + found = 1; /*format is found and stored in result */ break; } } - if (found && (AST_FORMAT_GET_TYPE(tmp_fmt.id) == AST_FORMAT_TYPE_AUDIO)) { - ast_format_copy(result, &tmp_fmt); + ast_format_list_destroy(f_list); + if (found && (AST_FORMAT_GET_TYPE(result->id) == AST_FORMAT_TYPE_AUDIO)) { return result; } - + ast_format_clear(result); ast_debug(4, "Could not find preferred codec - %s\n", find_best ? "Going for the best codec" : "Returning zero codec"); return find_best ? ast_best_codec(cap, result) : NULL; diff --git a/main/frame.c b/main/frame.c index 6805fea46..d82a46313 100644 --- a/main/frame.c +++ b/main/frame.c @@ -92,38 +92,6 @@ struct ast_smoother { int len; }; -/*! \brief Definition of supported media formats (codecs) */ -static const struct ast_format_list AST_FORMAT_LIST[] = { - { AST_FORMAT_G723_1 , "g723", 8000, "G.723.1", 20, 30, 300, 30, 30 }, /*!< G723.1 */ - { AST_FORMAT_GSM, "gsm", 8000, "GSM", 33, 20, 300, 20, 20 }, /*!< codec_gsm.c */ - { AST_FORMAT_ULAW, "ulaw", 8000, "G.711 u-law", 80, 10, 150, 10, 20 }, /*!< codec_ulaw.c */ - { AST_FORMAT_ALAW, "alaw", 8000, "G.711 A-law", 80, 10, 150, 10, 20 }, /*!< codec_alaw.c */ - { AST_FORMAT_G726, "g726", 8000, "G.726 RFC3551", 40, 10, 300, 10, 20 }, /*!< codec_g726.c */ - { AST_FORMAT_ADPCM, "adpcm" , 8000, "ADPCM", 40, 10, 300, 10, 20 }, /*!< codec_adpcm.c */ - { AST_FORMAT_SLINEAR, "slin", 8000, "16 bit Signed Linear PCM", 160, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE }, /*!< Signed linear */ - { AST_FORMAT_LPC10, "lpc10", 8000, "LPC10", 7, 20, 20, 20, 20 }, /*!< codec_lpc10.c */ - { AST_FORMAT_G729A, "g729", 8000, "G.729A", 10, 10, 230, 10, 20, AST_SMOOTHER_FLAG_G729 }, /*!< Binary commercial distribution */ - { AST_FORMAT_SPEEX, "speex", 8000, "SpeeX", 10, 10, 60, 10, 20 }, /*!< codec_speex.c */ - { AST_FORMAT_SPEEX16, "speex16", 16000, "SpeeX 16khz", 10, 10, 60, 10, 20 }, /*!< codec_speex.c */ - { AST_FORMAT_ILBC, "ilbc", 8000, "iLBC", 50, 30, 30, 30, 30 }, /*!< codec_ilbc.c */ /* inc=30ms - workaround */ - { AST_FORMAT_G726_AAL2, "g726aal2", 8000, "G.726 AAL2", 40, 10, 300, 10, 20 }, /*!< codec_g726.c */ - { AST_FORMAT_G722, "g722", 16000, "G722", 80, 10, 150, 10, 20 }, /*!< codec_g722.c */ - { AST_FORMAT_SLINEAR16, "slin16", 16000, "16 bit Signed Linear PCM (16kHz)", 320, 10, 70, 10, 20, AST_SMOOTHER_FLAG_BE }, /*!< Signed linear (16kHz) */ - { AST_FORMAT_JPEG, "jpeg", 0, "JPEG image"}, /*!< See format_jpeg.c */ - { AST_FORMAT_PNG, "png", 0, "PNG image"}, /*!< PNG Image format */ - { AST_FORMAT_H261, "h261", 0, "H.261 Video" }, /*!< H.261 Video Passthrough */ - { AST_FORMAT_H263, "h263", 0, "H.263 Video" }, /*!< H.263 Passthrough support, see format_h263.c */ - { AST_FORMAT_H263_PLUS, "h263p", 0, "H.263+ Video" }, /*!< H.263plus passthrough support See format_h263.c */ - { AST_FORMAT_H264, "h264", 0, "H.264 Video" }, /*!< Passthrough support, see format_h263.c */ - { AST_FORMAT_MP4_VIDEO, "mpeg4", 0, "MPEG4 Video" }, /*!< Passthrough support for MPEG4 */ - { AST_FORMAT_T140RED, "red", 1, "T.140 Realtime Text with redundancy"}, /*!< Redundant T.140 Realtime Text */ - { AST_FORMAT_T140, "t140", 0, "Passthrough T.140 Realtime Text" }, /*!< Passthrough support for T.140 Realtime Text */ - { AST_FORMAT_SIREN7, "siren7", 16000, "ITU G.722.1 (Siren7, licensed from Polycom)", 80, 20, 80, 20, 20 }, /*!< Binary commercial distribution */ - { AST_FORMAT_SIREN14, "siren14", 32000, "ITU G.722.1 Annex C, (Siren14, licensed from Polycom)", 120, 20, 80, 20, 20 }, /*!< Binary commercial distribution */ - { AST_FORMAT_TESTLAW, "testlaw", 8000, "G.711 test-law", 80, 10, 150, 10, 20 }, /*!< codec_ulaw.c */ - { AST_FORMAT_G719, "g719", 48000, "ITU G.719", 160, 20, 80, 20, 20 }, -}; - struct ast_frame ast_null_frame = { AST_FRAME_NULL, }; static int smoother_frame_feed(struct ast_smoother *s, struct ast_frame *f, int swap) @@ -554,218 +522,6 @@ void ast_swapcopy_samples(void *dst, const void *src, int samples) dst_s[i] = (src_s[i]<<8) | (src_s[i]>>8); } - -const struct ast_format_list *ast_get_format_list_index(int idx) -{ - return &AST_FORMAT_LIST[idx]; -} - -const struct ast_format_list *ast_get_format_list(size_t *size) -{ - *size = ARRAY_LEN(AST_FORMAT_LIST); - return AST_FORMAT_LIST; -} - -char* ast_getformatname(struct ast_format *format) -{ - int x; - char *ret = "unknown"; - for (x = 0; x < ARRAY_LEN(AST_FORMAT_LIST); x++) { - if (AST_FORMAT_LIST[x].id == format->id) { - ret = AST_FORMAT_LIST[x].name; - break; - } - } - return ret; -} - -char *ast_getformatname_multiple(char *buf, size_t size, struct ast_format_cap *cap) -{ - int x; - unsigned len; - char *start, *end = buf; - struct ast_format tmp_fmt; - - if (!size) - return buf; - snprintf(end, size, "("); - len = strlen(end); - end += len; - size -= len; - start = end; - for (x = 0; x < ARRAY_LEN(AST_FORMAT_LIST); x++) { - ast_format_set(&tmp_fmt, AST_FORMAT_LIST[x].id, 0); - if (ast_format_cap_iscompatible(cap, &tmp_fmt)) { - snprintf(end, size, "%s|", AST_FORMAT_LIST[x].name); - len = strlen(end); - end += len; - size -= len; - } - } - if (start == end) - ast_copy_string(start, "nothing)", size); - else if (size > 1) - *(end - 1) = ')'; - return buf; -} - -static struct ast_codec_alias_table { - char *alias; - char *realname; -} ast_codec_alias_table[] = { - { "slinear", "slin"}, - { "slinear16", "slin16"}, - { "g723.1", "g723"}, - { "g722.1", "siren7"}, - { "g722.1c", "siren14"}, -}; - -static const char *ast_expand_codec_alias(const char *in) -{ - int x; - - for (x = 0; x < ARRAY_LEN(ast_codec_alias_table); x++) { - if (!strcmp(in,ast_codec_alias_table[x].alias)) - return ast_codec_alias_table[x].realname; - } - return in; -} - -struct ast_format *ast_getformatbyname(const char *name, struct ast_format *result) -{ - int x; - - for (x = 0; x < ARRAY_LEN(AST_FORMAT_LIST); x++) { - if (!strcasecmp(AST_FORMAT_LIST[x].name,name) || - !strcasecmp(AST_FORMAT_LIST[x].name, ast_expand_codec_alias(name))) { - - ast_format_set(result, AST_FORMAT_LIST[x].id, 0); - return result; - } - } - - return NULL; -} - -char *ast_codec2str(struct ast_format *format) -{ - int x; - char *ret = "unknown"; - for (x = 0; x < ARRAY_LEN(AST_FORMAT_LIST); x++) { - if (AST_FORMAT_LIST[x].id == format->id) { - ret = AST_FORMAT_LIST[x].desc; - break; - } - } - return ret; -} - -static char *show_codecs(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) -{ - int x, found=0; - - switch (cmd) { - case CLI_INIT: - e->command = "core show codecs [audio|video|image|text]"; - e->usage = - "Usage: core show codecs [audio|video|image|text]\n" - " Displays codec mapping\n"; - return NULL; - case CLI_GENERATE: - return NULL; - } - - if ((a->argc < 3) || (a->argc > 4)) - return CLI_SHOWUSAGE; - - if (!ast_opt_dont_warn) - ast_cli(a->fd, "Disclaimer: this command is for informational purposes only.\n" - "\tIt does not indicate anything about your configuration.\n"); - - ast_cli(a->fd, "%8s %5s %8s %s\n","ID","TYPE","NAME","DESCRIPTION"); - ast_cli(a->fd, "-----------------------------------------------------------------------------------\n"); - - for (x = 0; x < ARRAY_LEN(AST_FORMAT_LIST); x++) { - if (a->argc == 4) { - if (!strcasecmp(a->argv[3], "audio")) { - if (AST_FORMAT_GET_TYPE(AST_FORMAT_LIST[x].id) != AST_FORMAT_TYPE_AUDIO) { - continue; - } - } else if (!strcasecmp(a->argv[3], "video")) { - if (AST_FORMAT_GET_TYPE(AST_FORMAT_LIST[x].id) != AST_FORMAT_TYPE_VIDEO) { - continue; - } - } else if (!strcasecmp(a->argv[3], "image")) { - if (AST_FORMAT_GET_TYPE(AST_FORMAT_LIST[x].id) != AST_FORMAT_TYPE_IMAGE) { - continue; - } - } else if (!strcasecmp(a->argv[3], "text")) { - if (AST_FORMAT_GET_TYPE(AST_FORMAT_LIST[x].id) != AST_FORMAT_TYPE_TEXT) { - continue; - } - } else { - continue; - } - } - - ast_cli(a->fd, "%8u %5s %8s (%s)\n", - AST_FORMAT_LIST[x].id, - (AST_FORMAT_GET_TYPE(AST_FORMAT_LIST[x].id) == AST_FORMAT_TYPE_AUDIO) ? "audio" : - (AST_FORMAT_GET_TYPE(AST_FORMAT_LIST[x].id) == AST_FORMAT_TYPE_IMAGE) ? "image" : - (AST_FORMAT_GET_TYPE(AST_FORMAT_LIST[x].id) == AST_FORMAT_TYPE_VIDEO) ? "video" : - (AST_FORMAT_GET_TYPE(AST_FORMAT_LIST[x].id) == AST_FORMAT_TYPE_TEXT) ? "text" : - "(unk)", - AST_FORMAT_LIST[x].name, - AST_FORMAT_LIST[x].desc); - found = 1; - } - - if (!found) { - return CLI_SHOWUSAGE; - } else { - return CLI_SUCCESS; - } -} - -static char *show_codec_n(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) -{ - enum ast_format_id format_id; - int x, found = 0; - int type_punned_codec; - - switch (cmd) { - case CLI_INIT: - e->command = "core show codec"; - e->usage = - "Usage: core show codec <number>\n" - " Displays codec mapping\n"; - return NULL; - case CLI_GENERATE: - return NULL; - } - - if (a->argc != 4) - return CLI_SHOWUSAGE; - - if (sscanf(a->argv[3], "%30d", &type_punned_codec) != 1) { - return CLI_SHOWUSAGE; - } - format_id = type_punned_codec; - - for (x = 0; x < ARRAY_LEN(AST_FORMAT_LIST); x++) { - if (AST_FORMAT_LIST[x].id == format_id) { - found = 1; - ast_cli(a->fd, "%11u %s\n", (unsigned int) format_id, AST_FORMAT_LIST[x].desc); - break; - } - } - - if (!found) - ast_cli(a->fd, "Codec %d not found\n", format_id); - - return CLI_SUCCESS; -} - /*! Dump a frame for debugging purposes */ void ast_frame_dump(const char *name, struct ast_frame *f, char *prefix) { @@ -972,19 +728,6 @@ void ast_frame_dump(const char *name, struct ast_frame *f, char *prefix) term_color(cn, name, COLOR_YELLOW, COLOR_BLACK, sizeof(cn))); } - -/* Builtin Asterisk CLI-commands for debugging */ -static struct ast_cli_entry my_clis[] = { - AST_CLI_DEFINE(show_codecs, "Displays a list of codecs"), - AST_CLI_DEFINE(show_codec_n, "Shows a specific codec"), -}; - -int init_framer(void) -{ - ast_cli_register_multiple(my_clis, ARRAY_LEN(my_clis)); - return 0; -} - int ast_parse_allow_disallow(struct ast_codec_pref *pref, struct ast_format_cap *cap, const char *list, int allowing) { int errors = 0, framems = 0, all = 0; @@ -1202,6 +945,9 @@ int ast_codec_get_samples(struct ast_frame *f) case AST_FORMAT_SPEEX16: samples = 2 * speex_samples(f->data.ptr, f->datalen); break; + case AST_FORMAT_SPEEX32: + samples = 4 * speex_samples(f->data.ptr, f->datalen); + break; case AST_FORMAT_G723_1: samples = g723_samples(f->data.ptr, f->datalen); break; @@ -1246,6 +992,25 @@ int ast_codec_get_samples(struct ast_frame *f) /* 48,000 samples per second at 64kbps is 8,000 bytes per second */ samples = (int) f->datalen * ((float) 48000 / 8000); break; + case AST_FORMAT_SILK: + if (!(ast_format_isset(&f->subclass.format, + SILK_ATTR_KEY_SAMP_RATE, + SILK_ATTR_VAL_SAMP_24KHZ, + AST_FORMAT_ATTR_END))) { + return 480; + } else if (!(ast_format_isset(&f->subclass.format, + SILK_ATTR_KEY_SAMP_RATE, + SILK_ATTR_VAL_SAMP_16KHZ, + AST_FORMAT_ATTR_END))) { + return 320; + } else if (!(ast_format_isset(&f->subclass.format, + SILK_ATTR_KEY_SAMP_RATE, + SILK_ATTR_VAL_SAMP_12KHZ, + AST_FORMAT_ATTR_END))) { + return 240; + } else { + return 160; + } default: ast_log(LOG_WARNING, "Unable to calculate samples for format %s\n", ast_getformatname(&f->subclass.format)); } @@ -1310,11 +1075,13 @@ int ast_frame_adjust_volume(struct ast_frame *f, int adjustment) short *fdata = f->data.ptr; short adjust_value = abs(adjustment); - if ((f->frametype != AST_FRAME_VOICE) || (f->subclass.format.id != AST_FORMAT_SLINEAR)) + if ((f->frametype != AST_FRAME_VOICE) || !(ast_format_is_slinear(&f->subclass.format))) { return -1; + } - if (!adjustment) + if (!adjustment) { return 0; + } for (count = 0; count < f->samples; count++) { if (adjustment > 0) { diff --git a/main/rtp_engine.c b/main/rtp_engine.c index 738b58fae..b2543893a 100644 --- a/main/rtp_engine.c +++ b/main/rtp_engine.c @@ -39,6 +39,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/pbx.h" #include "asterisk/translate.h" #include "asterisk/netsock2.h" +#include "asterisk/_private.h" struct ast_srtp_res *res_srtp = NULL; struct ast_srtp_policy_res *res_srtp_policy = NULL; @@ -83,50 +84,14 @@ static AST_RWLIST_HEAD_STATIC(glues, ast_rtp_glue); /*! The following array defines the MIME Media type (and subtype) for each of our codecs, or RTP-specific data type. */ -static const struct ast_rtp_mime_type { +static struct ast_rtp_mime_type { struct ast_rtp_payload_type payload_type; char *type; char *subtype; unsigned int sample_rate; -} ast_rtp_mime_types[] = { - {{1, {.id = AST_FORMAT_G723_1}, 0}, "audio", "G723", 8000}, - {{1, {.id = AST_FORMAT_GSM}, 0}, "audio", "GSM", 8000}, - {{1, {.id = AST_FORMAT_ULAW}, 0}, "audio", "PCMU", 8000}, - {{1, {.id = AST_FORMAT_ULAW}, 0}, "audio", "G711U", 8000}, - {{1, {.id = AST_FORMAT_ALAW}, 0}, "audio", "PCMA", 8000}, - {{1, {.id = AST_FORMAT_ALAW}, 0}, "audio", "G711A", 8000}, - {{1, {.id = AST_FORMAT_G726}, 0}, "audio", "G726-32", 8000}, - {{1, {.id = AST_FORMAT_ADPCM}, 0}, "audio", "DVI4", 8000}, - {{1, {.id = AST_FORMAT_SLINEAR}, 0}, "audio", "L16", 8000}, - {{1, {.id = AST_FORMAT_SLINEAR16}, 0}, "audio", "L16", 16000}, - {{1, {.id = AST_FORMAT_LPC10}, 0}, "audio", "LPC", 8000}, - {{1, {.id = AST_FORMAT_G729A}, 0}, "audio", "G729", 8000}, - {{1, {.id = AST_FORMAT_G729A}, 0}, "audio", "G729A", 8000}, - {{1, {.id = AST_FORMAT_G729A}, 0}, "audio", "G.729", 8000}, - {{1, {.id = AST_FORMAT_SPEEX}, 0}, "audio", "speex", 8000}, - {{1, {.id = AST_FORMAT_SPEEX16}, 0}, "audio", "speex", 16000}, - {{1, {.id = AST_FORMAT_ILBC}, 0}, "audio", "iLBC", 8000}, - /* this is the sample rate listed in the RTP profile for the G.722 - codec, *NOT* the actual sample rate of the media stream - */ - {{1, {.id = AST_FORMAT_G722}, 0}, "audio", "G722", 8000}, - {{1, {.id = AST_FORMAT_G726_AAL2}, 0}, "audio", "AAL2-G726-32", 8000}, - {{0, {.id = 0}, AST_RTP_DTMF}, "audio", "telephone-event", 8000}, - {{0, {.id = 0}, AST_RTP_CISCO_DTMF}, "audio", "cisco-telephone-event", 8000}, - {{0, {.id = 0}, AST_RTP_CN}, "audio", "CN", 8000}, - {{1, {.id = AST_FORMAT_JPEG}, 0}, "video", "JPEG", 90000}, - {{1, {.id = AST_FORMAT_PNG}, 0}, "video", "PNG", 90000}, - {{1, {.id = AST_FORMAT_H261}, 0}, "video", "H261", 90000}, - {{1, {.id = AST_FORMAT_H263}, 0}, "video", "H263", 90000}, - {{1, {.id = AST_FORMAT_H263_PLUS}, 0}, "video", "h263-1998", 90000}, - {{1, {.id = AST_FORMAT_H264}, 0}, "video", "H264", 90000}, - {{1, {.id = AST_FORMAT_MP4_VIDEO}, 0}, "video", "MP4V-ES", 90000}, - {{1, {.id = AST_FORMAT_T140RED}, 0}, "text", "RED", 1000}, - {{1, {.id = AST_FORMAT_T140}, 0}, "text", "T140", 1000}, - {{1, {.id = AST_FORMAT_SIREN7}, 0}, "audio", "G7221", 16000}, - {{1, {.id = AST_FORMAT_SIREN14}, 0}, "audio", "G7221", 32000}, - {{1, {.id = AST_FORMAT_G719}, 0}, "audio", "G719", 48000}, -}; +} ast_rtp_mime_types[128]; /* This will Likely not need to grow any time soon. */ +static ast_rwlock_t mime_types_lock; +static int mime_types_len = 0; /*! * \brief Mapping between Asterisk codecs and rtp payload types @@ -138,46 +103,8 @@ static const struct ast_rtp_mime_type { * See http://www.iana.org/assignments/rtp-parameters for a list of * assigned values */ -static const struct ast_rtp_payload_type static_RTP_PT[AST_RTP_MAX_PT] = { - [0] = {1, {.id = AST_FORMAT_ULAW}, 0}, - #ifdef USE_DEPRECATED_G726 - [2] = {1, {.id = AST_FORMAT_G726}, 0},/* Technically this is G.721, but if Cisco can do it, so can we... */ - #endif - [3] = {1, {.id = AST_FORMAT_GSM}, 0}, - [4] = {1, {.id = AST_FORMAT_G723_1}, 0}, - [5] = {1, {.id = AST_FORMAT_ADPCM}, 0},/* 8 kHz */ - [6] = {1, {.id = AST_FORMAT_ADPCM}, 0}, /* 16 kHz */ - [7] = {1, {.id = AST_FORMAT_LPC10}, 0}, - [8] = {1, {.id = AST_FORMAT_ALAW}, 0}, - [9] = {1, {.id = AST_FORMAT_G722}, 0}, - [10] = {1, {.id = AST_FORMAT_SLINEAR}, 0}, /* 2 channels */ - [11] = {1, {.id = AST_FORMAT_SLINEAR}, 0}, /* 1 channel */ - [13] = {0, {.id = 0}, AST_RTP_CN}, - [16] = {1, {.id = AST_FORMAT_ADPCM}, 0}, /* 11.025 kHz */ - [17] = {1, {.id = AST_FORMAT_ADPCM}, 0}, /* 22.050 kHz */ - [18] = {1, {.id = AST_FORMAT_G729A}, 0}, - [19] = {0, {.id = 0}, AST_RTP_CN}, /* Also used for CN */ - [26] = {1, {.id = AST_FORMAT_JPEG}, 0}, - [31] = {1, {.id = AST_FORMAT_H261}, 0}, - [34] = {1, {.id = AST_FORMAT_H263}, 0}, - [97] = {1, {.id = AST_FORMAT_ILBC}, 0}, - [98] = {1, {.id = AST_FORMAT_H263_PLUS}, 0}, - [99] = {1, {.id = AST_FORMAT_H264}, 0}, - [101] = {0, {.id = 0}, AST_RTP_DTMF}, - [102] = {1, {.id = AST_FORMAT_SIREN7}, 0}, - [103] = {1, {.id = AST_FORMAT_H263_PLUS}, 0}, - [104] = {1, {.id = AST_FORMAT_MP4_VIDEO}, 0}, - [105] = {1, {.id = AST_FORMAT_T140RED}, 0}, /* Real time text chat (with redundancy encoding) */ - [106] = {1, {.id = AST_FORMAT_T140}, 0}, /* Real time text chat */ - [110] = {1, {.id = AST_FORMAT_SPEEX}, 0}, - [111] = {1, {.id = AST_FORMAT_G726}, 0}, - [112] = {1, {.id = AST_FORMAT_G726_AAL2}, 0}, - [115] = {1, {.id = AST_FORMAT_SIREN14}, 0}, - [116] = {1, {.id = AST_FORMAT_G719}, 0}, - [117] = {1, {.id = AST_FORMAT_SPEEX16}, 0}, - [118] = {1, {.id = AST_FORMAT_SLINEAR16}, 0}, /* 16 Khz signed linear */ - [121] = {0, {.id = 0}, AST_RTP_CISCO_DTMF}, /* Must be type 121 */ -}; +static struct ast_rtp_payload_type static_RTP_PT[AST_RTP_MAX_PT]; +static ast_rwlock_t static_RTP_PT_lock; int ast_rtp_engine_register2(struct ast_rtp_engine *engine, struct ast_module *module) { @@ -497,6 +424,7 @@ void ast_rtp_codecs_payloads_default(struct ast_rtp_codecs *codecs, struct ast_r { int i; + ast_rwlock_rdlock(&static_RTP_PT_lock); for (i = 0; i < AST_RTP_MAX_PT; i++) { if (static_RTP_PT[i].rtp_code || static_RTP_PT[i].asterisk_format) { @@ -508,6 +436,7 @@ void ast_rtp_codecs_payloads_default(struct ast_rtp_codecs *codecs, struct ast_r } } } + ast_rwlock_unlock(&static_RTP_PT_lock); } void ast_rtp_codecs_payloads_copy(struct ast_rtp_codecs *src, struct ast_rtp_codecs *dest, struct ast_rtp_instance *instance) @@ -529,7 +458,10 @@ void ast_rtp_codecs_payloads_copy(struct ast_rtp_codecs *src, struct ast_rtp_cod void ast_rtp_codecs_payloads_set_m_type(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int payload) { + + ast_rwlock_rdlock(&static_RTP_PT_lock); if (payload < 0 || payload >= AST_RTP_MAX_PT || (!static_RTP_PT[payload].rtp_code && !static_RTP_PT[payload].asterisk_format)) { + ast_rwlock_unlock(&static_RTP_PT_lock); return; } @@ -542,6 +474,7 @@ void ast_rtp_codecs_payloads_set_m_type(struct ast_rtp_codecs *codecs, struct as if (instance && instance->engine && instance->engine->payload_set) { instance->engine->payload_set(instance, payload, codecs->payloads[payload].asterisk_format, &codecs->payloads[payload].format, codecs->payloads[payload].rtp_code); } + ast_rwlock_unlock(&static_RTP_PT_lock); } int ast_rtp_codecs_payloads_set_rtpmap_type_rate(struct ast_rtp_codecs *codecs, struct ast_rtp_instance *instance, int pt, @@ -555,7 +488,8 @@ int ast_rtp_codecs_payloads_set_rtpmap_type_rate(struct ast_rtp_codecs *codecs, if (pt < 0 || pt >= AST_RTP_MAX_PT) return -1; /* bogus payload type */ - for (i = 0; i < ARRAY_LEN(ast_rtp_mime_types); ++i) { + ast_rwlock_rdlock(&mime_types_lock); + for (i = 0; i < mime_types_len; ++i) { const struct ast_rtp_mime_type *t = &ast_rtp_mime_types[i]; if (strcasecmp(mimesubtype, t->subtype)) { @@ -587,6 +521,7 @@ int ast_rtp_codecs_payloads_set_rtpmap_type_rate(struct ast_rtp_codecs *codecs, break; } + ast_rwlock_unlock(&mime_types_lock); return (found ? 0 : -2); } @@ -626,12 +561,26 @@ struct ast_rtp_payload_type ast_rtp_codecs_payload_lookup(struct ast_rtp_codecs ast_format_copy(&result.format, &codecs->payloads[payload].format); if (!result.rtp_code && !result.asterisk_format) { + ast_rwlock_rdlock(&static_RTP_PT_lock); result = static_RTP_PT[payload]; + ast_rwlock_unlock(&static_RTP_PT_lock); } return result; } + +struct ast_format *ast_rtp_codecs_get_payload_format(struct ast_rtp_codecs *codecs, int payload) +{ + if (payload < 0 || payload >= AST_RTP_MAX_PT) { + return NULL; + } + if (!codecs->payloads[payload].asterisk_format) { + return NULL; + } + return &codecs->payloads[payload].format; +} + void ast_rtp_codecs_payload_formats(struct ast_rtp_codecs *codecs, struct ast_format_cap *astformats, int *nonastformats) { int i; @@ -654,7 +603,7 @@ void ast_rtp_codecs_payload_formats(struct ast_rtp_codecs *codecs, struct ast_fo int ast_rtp_codecs_payload_code(struct ast_rtp_codecs *codecs, int asterisk_format, const struct ast_format *format, int code) { int i; - + int res = -1; for (i = 0; i < AST_RTP_MAX_PT; i++) { if (codecs->payloads[i].asterisk_format && asterisk_format && format && (ast_format_cmp(format, &codecs->payloads[i].format) != AST_FORMAT_CMP_NOT_EQUAL)) { @@ -665,56 +614,71 @@ int ast_rtp_codecs_payload_code(struct ast_rtp_codecs *codecs, int asterisk_form } } + ast_rwlock_rdlock(&static_RTP_PT_lock); for (i = 0; i < AST_RTP_MAX_PT; i++) { if (static_RTP_PT[i].asterisk_format && asterisk_format && format && (ast_format_cmp(format, &static_RTP_PT[i].format) != AST_FORMAT_CMP_NOT_EQUAL)) { - return i; + res = i; + break; } else if (!static_RTP_PT[i].asterisk_format && !asterisk_format && (static_RTP_PT[i].rtp_code == code)) { - return i; + res = i; + break; } } + ast_rwlock_unlock(&static_RTP_PT_lock); - return -1; + return res; } const char *ast_rtp_lookup_mime_subtype2(const int asterisk_format, struct ast_format *format, int code, enum ast_rtp_options options) { int i; + const char *res = ""; - for (i = 0; i < ARRAY_LEN(ast_rtp_mime_types); i++) { + ast_rwlock_rdlock(&mime_types_lock); + for (i = 0; i < mime_types_len; i++) { if (ast_rtp_mime_types[i].payload_type.asterisk_format && asterisk_format && format && (ast_format_cmp(format, &ast_rtp_mime_types[i].payload_type.format) != AST_FORMAT_CMP_NOT_EQUAL)) { if ((format->id == AST_FORMAT_G726_AAL2) && (options & AST_RTP_OPT_G726_NONSTANDARD)) { - return "G726-32"; + res = "G726-32"; + break; } else { - return ast_rtp_mime_types[i].subtype; + res = ast_rtp_mime_types[i].subtype; + break; } } else if (!ast_rtp_mime_types[i].payload_type.asterisk_format && !asterisk_format && ast_rtp_mime_types[i].payload_type.rtp_code == code) { - return ast_rtp_mime_types[i].subtype; + res = ast_rtp_mime_types[i].subtype; + break; } } + ast_rwlock_unlock(&mime_types_lock); - return ""; + return res; } unsigned int ast_rtp_lookup_sample_rate2(int asterisk_format, struct ast_format *format, int code) { unsigned int i; + unsigned int res = 0; - for (i = 0; i < ARRAY_LEN(ast_rtp_mime_types); ++i) { + ast_rwlock_rdlock(&mime_types_lock); + for (i = 0; i < mime_types_len; ++i) { if (ast_rtp_mime_types[i].payload_type.asterisk_format && asterisk_format && format && (ast_format_cmp(format, &ast_rtp_mime_types[i].payload_type.format) != AST_FORMAT_CMP_NOT_EQUAL)) { - return ast_rtp_mime_types[i].sample_rate; + res = ast_rtp_mime_types[i].sample_rate; + break; } else if (!ast_rtp_mime_types[i].payload_type.asterisk_format && !asterisk_format && ast_rtp_mime_types[i].payload_type.rtp_code == code) { - return ast_rtp_mime_types[i].sample_rate; + res = ast_rtp_mime_types[i].sample_rate; + break; } } + ast_rwlock_unlock(&mime_types_lock); - return 0; + return res; } char *ast_rtp_lookup_mime_multiple2(struct ast_str *buf, struct ast_format_cap *ast_format_capability, int rtp_capability, const int asterisk_format, enum ast_rtp_options options) @@ -1879,3 +1843,185 @@ struct ast_srtp *ast_rtp_instance_get_srtp(struct ast_rtp_instance *instance) { return instance->srtp; } + +static void set_next_mime_type(const struct ast_format *format, int rtp_code, char *type, char *subtype, unsigned int sample_rate) +{ + int x = mime_types_len; + if (ARRAY_LEN(ast_rtp_mime_types) == mime_types_len) { + return; + } + + ast_rwlock_wrlock(&mime_types_lock); + if (format) { + ast_rtp_mime_types[x].payload_type.asterisk_format = 1; + ast_format_copy(&ast_rtp_mime_types[x].payload_type.format, format); + } else { + ast_rtp_mime_types[x].payload_type.rtp_code = rtp_code; + } + ast_rtp_mime_types[x].type = type; + ast_rtp_mime_types[x].subtype = subtype; + ast_rtp_mime_types[x].sample_rate = sample_rate; + mime_types_len++; + ast_rwlock_unlock(&mime_types_lock); +} + +static void add_static_payload(int map, const struct ast_format *format, int rtp_code) +{ + int x; + ast_rwlock_wrlock(&static_RTP_PT_lock); + if (map < 0) { + /* find next available dynamic payload slot */ + for (x = 96; x < 127; x++) { + if (!static_RTP_PT[x].asterisk_format && !static_RTP_PT[x].rtp_code) { + map = x; + break; + } + } + } + + if (map < 0) { + ast_log(LOG_WARNING, "No Dynamic RTP mapping avaliable for format %s\n" ,ast_getformatname(format)); + ast_rwlock_unlock(&static_RTP_PT_lock); + return; + } + + if (format) { + static_RTP_PT[map].asterisk_format = 1; + ast_format_copy(&static_RTP_PT[map].format, format); + } else { + static_RTP_PT[map].rtp_code = rtp_code; + } + ast_rwlock_unlock(&static_RTP_PT_lock); +} + +int ast_rtp_engine_load_format(const struct ast_format *format) +{ + switch (format->id) { + case AST_FORMAT_SILK: + set_next_mime_type(format, 0, "audio", "SILK", ast_format_rate(format)); + add_static_payload(-1, format, 0); + break; + default: + break; + } + + return 0; +} + +int ast_rtp_engine_unload_format(const struct ast_format *format) +{ + int x; + int y = 0; + + ast_rwlock_wrlock(&static_RTP_PT_lock); + /* remove everything pertaining to this format id from the lists */ + for (x = 0; x < AST_RTP_MAX_PT; x++) { + if (ast_format_cmp(&static_RTP_PT[x].format, format) == AST_FORMAT_CMP_EQUAL) { + memset(&static_RTP_PT[x], 0, sizeof(struct ast_rtp_payload_type)); + } + } + ast_rwlock_unlock(&static_RTP_PT_lock); + + + ast_rwlock_wrlock(&mime_types_lock); + /* rebuild the list skipping the items matching this id */ + for (x = 0; x < mime_types_len; x++) { + if (ast_format_cmp(&ast_rtp_mime_types[x].payload_type.format, format) == AST_FORMAT_CMP_EQUAL) { + continue; + } + ast_rtp_mime_types[y] = ast_rtp_mime_types[x]; + y++; + } + mime_types_len = y; + ast_rwlock_unlock(&mime_types_lock); + return 0; +} + +int ast_rtp_engine_init() +{ + struct ast_format tmpfmt; + + ast_rwlock_init(&mime_types_lock); + ast_rwlock_init(&static_RTP_PT_lock); + + /* Define all the RTP mime types available */ + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_G723_1, 0), 0, "audio", "G723", 8000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_GSM, 0), 0, "audio", "GSM", 8000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_ULAW, 0), 0, "audio", "PCMU", 8000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_ULAW, 0), 0, "audio", "G711U", 8000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_ALAW, 0), 0, "audio", "PCMA", 8000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_ALAW, 0), 0, "audio", "G711A", 8000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_G726, 0), 0, "audio", "G726-32", 8000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_ADPCM, 0), 0, "audio", "DVI4", 8000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0), 0, "audio", "L16", 8000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR16, 0), 0, "audio", "L16", 16000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_LPC10, 0), 0, "audio", "LPC", 8000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_G729A, 0), 0, "audio", "G729", 8000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_G729A, 0), 0, "audio", "G729A", 8000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_G729A, 0), 0, "audio", "G.729", 8000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_SPEEX, 0), 0, "audio", "speex", 8000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_SPEEX16, 0), 0, "audio", "speex", 16000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_SPEEX32, 0), 0, "audio", "speex", 32000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_ILBC, 0), 0, "audio", "iLBC", 8000); + /* this is the sample rate listed in the RTP profile for the G.722 codec, *NOT* the actual sample rate of the media stream */ + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_G722, 0), 0, "audio", "G722", 8000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_G726_AAL2, 0), 0, "audio", "AAL2-G726-32", 8000); + set_next_mime_type(NULL, AST_RTP_DTMF, "audio", "telephone-event", 8000); + set_next_mime_type(NULL, AST_RTP_CISCO_DTMF, "audio", "cisco-telephone-event", 8000); + set_next_mime_type(NULL, AST_RTP_CN, "audio", "CN", 8000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_JPEG, 0), 0, "video", "JPEG", 90000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_PNG, 0), 0, "video", "PNG", 90000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_H261, 0), 0, "video", "H261", 90000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_H263, 0), 0, "video", "H263", 90000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_H263_PLUS, 0), 0, "video", "h263-1998", 90000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_H264, 0), 0, "video", "H264", 90000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_MP4_VIDEO, 0), 0, "video", "MP4V-ES", 90000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_T140RED, 0), 0, "text", "RED", 1000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_T140, 0), 0, "text", "T140", 1000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_SIREN7, 0), 0, "audio", "G7221", 16000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_SIREN14, 0), 0, "audio", "G7221", 32000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_G719, 0), 0, "audio", "G719", 48000); + + /* Define the static rtp payload mappings */ + add_static_payload(0, ast_format_set(&tmpfmt, AST_FORMAT_ULAW, 0), 0); + #ifdef USE_DEPRECATED_G726 + add_static_payload(2, ast_format_set(&tmpfmt, AST_FORMAT_G726, 0), 0);/* Technically this is G.721, but if Cisco can do it, so can we... */ + #endif + add_static_payload(3, ast_format_set(&tmpfmt, AST_FORMAT_GSM, 0), 0); + add_static_payload(4, ast_format_set(&tmpfmt, AST_FORMAT_G723_1, 0), 0); + add_static_payload(5, ast_format_set(&tmpfmt, AST_FORMAT_ADPCM, 0), 0);/* 8 kHz */ + add_static_payload(6, ast_format_set(&tmpfmt, AST_FORMAT_ADPCM, 0), 0); /* 16 kHz */ + add_static_payload(7, ast_format_set(&tmpfmt, AST_FORMAT_LPC10, 0), 0); + add_static_payload(8, ast_format_set(&tmpfmt, AST_FORMAT_ALAW, 0), 0); + add_static_payload(9, ast_format_set(&tmpfmt, AST_FORMAT_G722, 0), 0); + add_static_payload(10, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0), 0); /* 2 channels */ + add_static_payload(11, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0), 0); /* 1 channel */ + add_static_payload(13, NULL, AST_RTP_CN); + add_static_payload(16, ast_format_set(&tmpfmt, AST_FORMAT_ADPCM, 0), 0); /* 11.025 kHz */ + add_static_payload(17, ast_format_set(&tmpfmt, AST_FORMAT_ADPCM, 0), 0); /* 22.050 kHz */ + add_static_payload(18, ast_format_set(&tmpfmt, AST_FORMAT_G729A, 0), 0); + add_static_payload(19, NULL, AST_RTP_CN); /* Also used for CN */ + add_static_payload(26, ast_format_set(&tmpfmt, AST_FORMAT_JPEG, 0), 0); + add_static_payload(31, ast_format_set(&tmpfmt, AST_FORMAT_H261, 0), 0); + add_static_payload(34, ast_format_set(&tmpfmt, AST_FORMAT_H263, 0), 0); + add_static_payload(97, ast_format_set(&tmpfmt, AST_FORMAT_ILBC, 0), 0); + add_static_payload(98, ast_format_set(&tmpfmt, AST_FORMAT_H263_PLUS, 0), 0); + add_static_payload(99, ast_format_set(&tmpfmt, AST_FORMAT_H264, 0), 0); + add_static_payload(101, NULL, AST_RTP_DTMF); + add_static_payload(102, ast_format_set(&tmpfmt, AST_FORMAT_SIREN7, 0), 0); + add_static_payload(103, ast_format_set(&tmpfmt, AST_FORMAT_H263_PLUS, 0), 0); + add_static_payload(104, ast_format_set(&tmpfmt, AST_FORMAT_MP4_VIDEO, 0), 0); + add_static_payload(105, ast_format_set(&tmpfmt, AST_FORMAT_T140RED, 0), 0); /* Real time text chat (with redundancy encoding) */ + add_static_payload(106, ast_format_set(&tmpfmt, AST_FORMAT_T140, 0), 0); /* Real time text chat */ + add_static_payload(110, ast_format_set(&tmpfmt, AST_FORMAT_SPEEX, 0), 0); + add_static_payload(111, ast_format_set(&tmpfmt, AST_FORMAT_G726, 0), 0); + add_static_payload(112, ast_format_set(&tmpfmt, AST_FORMAT_G726_AAL2, 0), 0); + add_static_payload(115, ast_format_set(&tmpfmt, AST_FORMAT_SIREN14, 0), 0); + add_static_payload(116, ast_format_set(&tmpfmt, AST_FORMAT_G719, 0), 0); + add_static_payload(117, ast_format_set(&tmpfmt, AST_FORMAT_SPEEX16, 0), 0); + add_static_payload(118, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR16, 0), 0); /* 16 Khz signed linear */ + add_static_payload(119, ast_format_set(&tmpfmt, AST_FORMAT_SPEEX32, 0), 0); + add_static_payload(121, NULL, AST_RTP_CISCO_DTMF); /* Must be type 121 */ + + return 0; +} diff --git a/main/slinfactory.c b/main/slinfactory.c index f7363ab4b..338305b40 100644 --- a/main/slinfactory.c +++ b/main/slinfactory.c @@ -39,20 +39,14 @@ void ast_slinfactory_init(struct ast_slinfactory *sf) ast_format_set(&sf->output_format, AST_FORMAT_SLINEAR, 0); } -int ast_slinfactory_init_rate(struct ast_slinfactory *sf, unsigned int sample_rate) +int ast_slinfactory_init_with_format(struct ast_slinfactory *sf, const struct ast_format *slin_out) { memset(sf, 0, sizeof(*sf)); sf->offset = sf->hold; - switch (sample_rate) { - case 8000: - ast_format_set(&sf->output_format, AST_FORMAT_SLINEAR, 0); - break; - case 16000: - ast_format_set(&sf->output_format, AST_FORMAT_SLINEAR16, 0); - break; - default: + if (!ast_format_is_slinear(slin_out)) { return -1; } + ast_format_copy(&sf->output_format, slin_out); return 0; } @@ -93,8 +87,11 @@ int ast_slinfactory_feed(struct ast_slinfactory *sf, struct ast_frame *f) if (!sf->trans) { if (!(sf->trans = ast_translator_build_path(&sf->output_format, &f->subclass.format))) { - ast_log(LOG_WARNING, "Cannot build a path from %s to %s\n", ast_getformatname(&f->subclass.format), - ast_getformatname(&sf->output_format)); + ast_log(LOG_WARNING, "Cannot build a path from %s (%d)to %s (%d)\n", + ast_getformatname(&f->subclass.format), + f->subclass.format.id, + ast_getformatname(&sf->output_format), + sf->output_format.id); return 0; } ast_format_copy(&sf->format, &f->subclass.format); diff --git a/main/translate.c b/main/translate.c index 553e70cde..caba2d393 100644 --- a/main/translate.c +++ b/main/translate.c @@ -273,7 +273,7 @@ static struct translator_path *matrix_get(unsigned int x, unsigned int y) * \brief Allocate the descriptor, required outbuf space, * and possibly desc. */ -static void *newpvt(struct ast_translator *t) +static void *newpvt(struct ast_translator *t, const struct ast_format *explicit_dst) { struct ast_trans_pvt *pvt; int len; @@ -287,16 +287,23 @@ static void *newpvt(struct ast_translator *t) if (t->buf_size) len += AST_FRIENDLY_OFFSET + t->buf_size; pvt = ast_calloc(1, len); - if (!pvt) + if (!pvt) { return NULL; + } pvt->t = t; ofs = (char *)(pvt + 1); /* pointer to data space */ if (t->desc_size) { /* first comes the descriptor */ pvt->pvt = ofs; ofs += t->desc_size; } - if (t->buf_size) /* finally buffer and header */ + if (t->buf_size) {/* finally buffer and header */ pvt->outbuf.c = ofs + AST_FRIENDLY_OFFSET; + } + /* if a explicit destination format is provided, set that on the pvt so the + * translator will process it. */ + if (explicit_dst) { + ast_format_copy(&pvt->explicit_dst, explicit_dst); + } /* call local init routine, if present */ if (t->newpvt && t->newpvt(pvt)) { ast_free(pvt); @@ -424,6 +431,7 @@ struct ast_trans_pvt *ast_translator_build_path(struct ast_format *dst, struct a while (src_index != dst_index) { struct ast_trans_pvt *cur; + struct ast_format *explicit_dst = NULL; struct ast_translator *t = matrix_get(src_index, dst_index)->step; if (!t) { int src_id = index2format(src_index); @@ -434,7 +442,10 @@ struct ast_trans_pvt *ast_translator_build_path(struct ast_format *dst, struct a AST_RWLIST_UNLOCK(&translators); return NULL; } - if (!(cur = newpvt(t))) { + if (dst_index == t->dst_fmt_index) { + explicit_dst = dst; + } + if (!(cur = newpvt(t, explicit_dst))) { int src_id = index2format(src_index); int dst_id = index2format(dst_index); ast_log(LOG_WARNING, "Failed to build translator step from %s to %s\n", @@ -565,12 +576,12 @@ static void generate_computational_cost(struct ast_translator *t, int seconds) /* If they don't make samples, give them a terrible score */ if (!t->sample) { - ast_log(LOG_WARNING, "Translator '%s' does not produce sample frames.\n", t->name); + ast_debug(3, "Translator '%s' does not produce sample frames.\n", t->name); t->comp_cost = 999999; return; } - pvt = newpvt(t); + pvt = newpvt(t, NULL); if (!pvt) { ast_log(LOG_WARNING, "Translator '%s' appears to be broken and will probably fail.\n", t->name); t->comp_cost = 999999; @@ -641,13 +652,8 @@ static int generate_table_cost(struct ast_format *src, struct ast_format *dst) * table cost. */ return 0; } - if ((src->id == AST_FORMAT_SLINEAR) || (src->id == AST_FORMAT_SLINEAR16)) { - src_ll = 1; - } - if ((dst->id == AST_FORMAT_SLINEAR) || (dst->id == AST_FORMAT_SLINEAR16)) { - dst_ll = 1; - } - + src_ll = ast_format_is_slinear(src); + dst_ll = ast_format_is_slinear(dst); if (src_ll) { if (dst_ll && (src_rate == dst_rate)) { return AST_TRANS_COST_LL_LL_ORIGSAMP; @@ -778,16 +784,17 @@ static void matrix_rebuild(int samples) const char *ast_translate_path_to_str(struct ast_trans_pvt *p, struct ast_str **str) { struct ast_trans_pvt *pn = p; + char tmp[256]; if (!p || !p->t) { return ""; } - ast_str_set(str, 0, "%s", ast_getformatname(&p->t->src_format)); + ast_str_set(str, 0, "%s", ast_getformatname_multiple_byid(tmp, sizeof(tmp), p->t->src_format.id)); while ( (p = pn) ) { pn = p->next; - ast_str_append(str, 0, "->%s", ast_getformatname(&p->t->dst_format)); + ast_str_append(str, 0, "->%s", ast_getformatname_multiple_byid(tmp, sizeof(tmp), p->t->dst_format.id)); } return ast_str_buffer(*str); @@ -800,10 +807,10 @@ static char *complete_trans_path_choice(const char *line, const char *word, int int i; char *ret = NULL; size_t len = 0; - const struct ast_format_list *format_list = ast_get_format_list(&len); + const struct ast_format_list *format_list = ast_format_list_get(&len); for (i = 0; i < len; i++) { - if (AST_FORMAT_GET_TYPE(format_list[i].id) != AST_FORMAT_TYPE_AUDIO) { + if (AST_FORMAT_GET_TYPE(format_list[i].format.id) != AST_FORMAT_TYPE_AUDIO) { continue; } if (!strncasecmp(word, format_list[i].name, wordlen) && ++which > state) { @@ -811,6 +818,7 @@ static char *complete_trans_path_choice(const char *line, const char *word, int break; } } + ast_format_list_destroy(format_list); return ret; } @@ -835,46 +843,56 @@ static void handle_cli_recalc(struct ast_cli_args *a) static char *handle_show_translation_table(struct ast_cli_args *a) { - int x, y; + int x, y, i, k; int curlen = 0, longest = 0; - struct ast_format tmp_fmt; + int f_len = 0; + const struct ast_format_list *f_list = ast_format_list_get((size_t *) &f_len); + struct ast_str *out = ast_str_create(1024); + AST_RWLIST_RDLOCK(&translators); ast_cli(a->fd, " Translation times between formats (in microseconds) for one second of data\n"); ast_cli(a->fd, " Source Format (Rows) Destination Format (Columns)\n\n"); /* Get the length of the longest (usable?) codec name, so we know how wide the left side should be */ - for (x = 0; x < cur_max_index; x++) { + for (i = 0; i < f_len; i++) { /* translation only applies to audio right now. */ - if (AST_FORMAT_GET_TYPE(index2format(x)) != AST_FORMAT_TYPE_AUDIO) + if (AST_FORMAT_GET_TYPE(f_list[i].format.id) != AST_FORMAT_TYPE_AUDIO) continue; - curlen = strlen(ast_getformatname(ast_format_set(&tmp_fmt, index2format(x), 0))); + curlen = strlen(ast_getformatname(&f_list[i].format)); if (curlen > longest) { longest = curlen; } } - for (x = -1; x < cur_max_index; x++) { - struct ast_str *out = ast_str_alloca(256); + for (i = -1; i < f_len; i++) { + x = -1; + if ((i >= 0) && ((x = format2index(f_list[i].format.id)) == -1)) { + continue; + } /* translation only applies to audio right now. */ - if (x >= 0 && (AST_FORMAT_GET_TYPE(index2format(x)) != AST_FORMAT_TYPE_AUDIO)) { + if (i >= 0 && (AST_FORMAT_GET_TYPE(f_list[i].format.id) != AST_FORMAT_TYPE_AUDIO)) { continue; } /*Go ahead and move to next iteration if dealing with an unknown codec*/ - if (x >= 0 && !strcmp(ast_getformatname(ast_format_set(&tmp_fmt, index2format(x), 0)), "unknown")) { + if (i >= 0 && !strcmp(ast_getformatname(&f_list[i].format), "unknown")) { continue; } - ast_str_set(&out, -1, " "); - for (y = -1; y < cur_max_index; y++) { + ast_str_set(&out, 0, " "); + for (k = -1; k < f_len; k++) { + y = -1; + if ((k >= 0) && ((y = format2index(f_list[k].format.id)) == -1)) { + continue; + } /* translation only applies to audio right now. */ - if (y >= 0 && (AST_FORMAT_GET_TYPE(index2format(y)) != AST_FORMAT_TYPE_AUDIO)) { + if (k >= 0 && (AST_FORMAT_GET_TYPE(f_list[k].format.id) != AST_FORMAT_TYPE_AUDIO)) { continue; } /*Go ahead and move to next iteration if dealing with an unknown codec*/ - if (y >= 0 && !strcmp(ast_getformatname(ast_format_set(&tmp_fmt, index2format(y), 0)), "unknown")) { + if (k >= 0 && !strcmp(ast_getformatname(&f_list[k].format), "unknown")) { continue; } - if (y >= 0) { - curlen = strlen(ast_getformatname(ast_format_set(&tmp_fmt, index2format(y), 0))); + if (k >= 0) { + curlen = strlen(ast_getformatname(&f_list[k].format)); } if (curlen < 5) { curlen = 5; @@ -882,25 +900,27 @@ static char *handle_show_translation_table(struct ast_cli_args *a) if (x >= 0 && y >= 0 && matrix_get(x, y)->step) { /* Actual codec output */ - ast_str_append(&out, -1, "%*d", curlen + 1, (matrix_get(x, y)->table_cost/100)); - } else if (x == -1 && y >= 0) { + ast_str_append(&out, 0, "%*d", curlen + 1, (matrix_get(x, y)->table_cost/100)); + } else if (i == -1 && k >= 0) { /* Top row - use a dynamic size */ - ast_str_append(&out, -1, "%*s", curlen + 1, ast_getformatname(ast_format_set(&tmp_fmt, index2format(y), 0))); - } else if (y == -1 && x >= 0) { + ast_str_append(&out, 0, "%*s", curlen + 1, ast_getformatname(&f_list[k].format)); + } else if (k == -1 && i >= 0) { /* Left column - use a static size. */ - ast_str_append(&out, -1, "%*s", longest, ast_getformatname(ast_format_set(&tmp_fmt, index2format(x), 0))); + ast_str_append(&out, 0, "%*s", longest, ast_getformatname(&f_list[i].format)); } else if (x >= 0 && y >= 0) { /* Codec not supported */ - ast_str_append(&out, -1, "%*s", curlen + 1, "-"); + ast_str_append(&out, 0, "%*s", curlen + 1, "-"); } else { /* Upper left hand corner */ - ast_str_append(&out, -1, "%*s", longest, ""); + ast_str_append(&out, 0, "%*s", longest, ""); } } - ast_str_append(&out, -1, "\n"); + ast_str_append(&out, 0, "\n"); ast_cli(a->fd, "%s", ast_str_buffer(out)); } + ast_free(out); AST_RWLIST_UNLOCK(&translators); + ast_format_list_destroy(f_list); return CLI_SUCCESS; } @@ -909,23 +929,24 @@ static char *handle_show_translation_path(struct ast_cli_args *a) struct ast_format input_src_format; size_t len = 0; int i; - const struct ast_format_list *format_list = ast_get_format_list(&len); - struct ast_str *str = ast_str_alloca(256); + const struct ast_format_list *format_list = ast_format_list_get(&len); + struct ast_str *str = ast_str_alloca(1024); struct ast_translator *step; + char tmp[256]; ast_format_clear(&input_src_format); - for (i = 0; i < len; i++) { - if (AST_FORMAT_GET_TYPE(format_list[i].id) != AST_FORMAT_TYPE_AUDIO) { + if (AST_FORMAT_GET_TYPE(format_list[i].format.id) != AST_FORMAT_TYPE_AUDIO) { continue; } if (!strncasecmp(format_list[i].name, a->argv[4], strlen(format_list[i].name))) { - ast_format_set(&input_src_format, format_list[i].id, 0); + ast_format_copy(&input_src_format, &format_list[i].format); } } if (!input_src_format.id) { ast_cli(a->fd, "Source codec \"%s\" is not found.\n", a->argv[4]); + ast_format_list_destroy(format_list); return CLI_FAILURE; } @@ -934,21 +955,21 @@ static char *handle_show_translation_path(struct ast_cli_args *a) for (i = 0; i < len; i++) { int src; int dst; - if ((AST_FORMAT_GET_TYPE(format_list[i].id) != AST_FORMAT_TYPE_AUDIO) || (format_list[i].id == input_src_format.id)) { + if ((AST_FORMAT_GET_TYPE(format_list[i].format.id) != AST_FORMAT_TYPE_AUDIO) || (format_list[i].format.id == input_src_format.id)) { continue; } - dst = format2index(format_list[i].id); + dst = format2index(format_list[i].format.id); src = format2index(input_src_format.id); ast_str_reset(str); if ((len >= cur_max_index) && (src != -1) && (dst != -1) && matrix_get(src, dst)->step) { - ast_str_append(&str, 0, "%s", ast_getformatname(&matrix_get(src, dst)->step->src_format)); + ast_str_append(&str, 0, "%s", ast_getformatname_multiple_byid(tmp, sizeof(tmp), matrix_get(src, dst)->step->src_format.id)); while (src != dst) { step = matrix_get(src, dst)->step; if (!step) { ast_str_reset(str); break; } - ast_str_append(&str, 0, "->%s", ast_getformatname(&step->dst_format)); + ast_str_append(&str, 0, "->%s", ast_getformatname_multiple_byid(tmp, sizeof(tmp), step->dst_format.id)); src = step->dst_fmt_index; } } @@ -960,6 +981,7 @@ static char *handle_show_translation_path(struct ast_cli_args *a) } AST_RWLIST_UNLOCK(&translators); + ast_format_list_destroy(format_list); return CLI_SUCCESS; } diff --git a/res/res_mutestream.c b/res/res_mutestream.c index 4faf46478..41b2fd831 100644 --- a/res/res_mutestream.c +++ b/res/res_mutestream.c @@ -173,7 +173,7 @@ static struct ast_datastore *initialize_mutehook(struct ast_channel *chan) ast_datastore_free(datastore); return NULL; } - ast_audiohook_init(&mute->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "Mute"); + ast_audiohook_init(&mute->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "Mute", AST_AUDIOHOOK_MANIPULATE_ALL_RATES); mute->audiohook.manipulate_callback = mute_callback; datastore->data = mute; return datastore; diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c index 0082fda96..b2004efab 100644 --- a/res/res_rtp_asterisk.c +++ b/res/res_rtp_asterisk.c @@ -1250,6 +1250,8 @@ static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *fr switch (subclass.id) { case AST_FORMAT_SPEEX: case AST_FORMAT_SPEEX16: + case AST_FORMAT_SPEEX32: + case AST_FORMAT_SILK: case AST_FORMAT_G723_1: case AST_FORMAT_SIREN7: case AST_FORMAT_SIREN14: @@ -2292,7 +2294,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc if (AST_FORMAT_GET_TYPE(rtp->f.subclass.format.id) == AST_FORMAT_TYPE_AUDIO) { rtp->f.samples = ast_codec_get_samples(&rtp->f); - if ((rtp->f.subclass.format.id == AST_FORMAT_SLINEAR) || (rtp->f.subclass.format.id == AST_FORMAT_SLINEAR16)) { + if (ast_format_is_slinear(&rtp->f.subclass.format)) { ast_frame_byteswap_be(&rtp->f); } calc_rxstamp(&rtp->f.delivery, rtp, timestamp, mark); |