From b00f58da25df6d86a409a7e278b987f6ee863fb4 Mon Sep 17 00:00:00 2001 From: David Vossel Date: Thu, 17 Jun 2010 17:23:43 +0000 Subject: [PATCH] adds speex 16khz audio support (closes issue #17501) Reported by: fabled Patches: asterisk-trunk-speex-wideband-v2.patch uploaded by fabled (license 448) Tested by: malcolmd, fabled, dvossel git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@271231 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- CHANGES | 2 + codecs/codec_speex.c | 82 +++++++++++++++++++++++++++++++--------- include/asterisk/frame.h | 5 ++- main/channel.c | 1 + main/frame.c | 24 ++++++------ main/rtp_engine.c | 2 + res/res_rtp_asterisk.c | 1 + 7 files changed, 88 insertions(+), 29 deletions(-) diff --git a/CHANGES b/CHANGES index 0126ed88f5..779adfbbdc 100644 --- a/CHANGES +++ b/CHANGES @@ -500,6 +500,8 @@ Miscellaneous * Distributed devicestate now supports the use of the XMPP protocol, in addition to AIS. For more information, please see doc/distributed_devstate-XMPP.txt * The addition of G.719 pass-through support. + * Added support for 16khz Speex audio. This can be enabled by using 'allow=speex16' + during device configuration. CLI Changes diff --git a/codecs/codec_speex.c b/codecs/codec_speex.c index 2966a7a0c1..85f231f9bb 100644 --- a/codecs/codec_speex.c +++ b/codecs/codec_speex.c @@ -97,12 +97,11 @@ struct speex_coder_pvt { #endif }; - -static int lintospeex_new(struct ast_trans_pvt *pvt) +static int speex_encoder_construct(struct ast_trans_pvt *pvt, const SpeexMode *profile, int sampling_rate) { struct speex_coder_pvt *tmp = pvt->pvt; - if (!(tmp->speex = speex_encoder_init(&speex_nb_mode))) + if (!(tmp->speex = speex_encoder_init(profile))) return -1; speex_bits_init(&tmp->bits); @@ -111,7 +110,7 @@ static int lintospeex_new(struct ast_trans_pvt *pvt) speex_encoder_ctl(tmp->speex, SPEEX_SET_COMPLEXITY, &complexity); #ifdef _SPEEX_TYPES_H if (preproc) { - tmp->pp = speex_preprocess_state_init(tmp->framesize, 8000); /* XXX what is this 8000 ? */ + tmp->pp = speex_preprocess_state_init(tmp->framesize, sampling_rate); speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_VAD, &pp_vad); speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_AGC, &pp_agc); speex_preprocess_ctl(tmp->pp, SPEEX_PREPROCESS_SET_AGC_LEVEL, &pp_agc_level); @@ -139,11 +138,21 @@ static int lintospeex_new(struct ast_trans_pvt *pvt) return 0; } -static int speextolin_new(struct ast_trans_pvt *pvt) +static int lintospeex_new(struct ast_trans_pvt *pvt) +{ + return speex_encoder_construct(pvt, &speex_nb_mode, 8000); +} + +static int lin16tospeexwb_new(struct ast_trans_pvt *pvt) +{ + return speex_encoder_construct(pvt, &speex_wb_mode, 16000); +} + +static int speex_decoder_construct(struct ast_trans_pvt *pvt, const SpeexMode *profile) { struct speex_coder_pvt *tmp = pvt->pvt; - if (!(tmp->speex = speex_decoder_init(&speex_nb_mode))) + if (!(tmp->speex = speex_decoder_init(profile))) return -1; speex_bits_init(&tmp->bits); @@ -154,6 +163,16 @@ static int speextolin_new(struct ast_trans_pvt *pvt) return 0; } +static int speextolin_new(struct ast_trans_pvt *pvt) +{ + return speex_decoder_construct(pvt, &speex_nb_mode); +} + +static int speexwbtolin16_new(struct ast_trans_pvt *pvt) +{ + return speex_decoder_construct(pvt, &speex_wb_mode); +} + /*! \brief convert and store into outbuf */ static int speextolin_framein(struct ast_trans_pvt *pvt, struct ast_frame *f) { @@ -337,6 +356,34 @@ static struct ast_translator lintospeex = { .buf_size = BUFFER_SAMPLES * 2, /* XXX maybe a lot less ? */ }; +static struct ast_translator speexwbtolin16 = { + .name = "speexwbtolin16", + .srcfmt = AST_FORMAT_SPEEX16, + .dstfmt = AST_FORMAT_SLINEAR16, + .newpvt = speexwbtolin16_new, + .framein = speextolin_framein, + .destroy = speextolin_destroy, + .sample = speex_sample, + .desc_size = sizeof(struct speex_coder_pvt), + .buffer_samples = BUFFER_SAMPLES, + .buf_size = BUFFER_SAMPLES * 2, + .native_plc = 1, +}; + +static struct ast_translator lin16tospeexwb = { + .name = "lin16tospeexwb", + .srcfmt = AST_FORMAT_SLINEAR16, + .dstfmt = AST_FORMAT_SPEEX16, + .newpvt = lin16tospeexwb_new, + .framein = lintospeex_framein, + .frameout = lintospeex_frameout, + .destroy = lintospeex_destroy, + .sample = slin8_sample, + .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 }; @@ -441,28 +488,29 @@ static int reload(void) static int unload_module(void) { - int res; + int res = 0; - res = ast_unregister_translator(&lintospeex); res |= ast_unregister_translator(&speextolin); + res |= ast_unregister_translator(&lintospeex); + res |= ast_unregister_translator(&speexwbtolin16); + res |= ast_unregister_translator(&lin16tospeexwb); return res; } static int load_module(void) { - int res; + int res = 0; if (parse_config(0)) return AST_MODULE_LOAD_DECLINE; - res=ast_register_translator(&speextolin); - if (!res) - res=ast_register_translator(&lintospeex); - else - ast_unregister_translator(&speextolin); - if (res) - return AST_MODULE_LOAD_FAILURE; - return AST_MODULE_LOAD_SUCCESS; + + res |= ast_register_translator(&speextolin); + res |= ast_register_translator(&lintospeex); + res |= ast_register_translator(&speexwbtolin16); + res |= ast_register_translator(&lin16tospeexwb); + + return res; } AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Speex Coder/Decoder", diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h index cbe8cfd07b..5cf137fae7 100644 --- a/include/asterisk/frame.h +++ b/include/asterisk/frame.h @@ -296,8 +296,10 @@ extern struct ast_frame ast_null_frame; #define AST_FORMAT_TEXT_MASK (((1ULL << 30)-1) & ~(AST_FORMAT_AUDIO_MASK) & ~(AST_FORMAT_VIDEO_MASK)) /*! G.719 (64 kbps assumed) */ #define AST_FORMAT_G719 (1ULL << 32) +/*! SpeeX Wideband (16kHz) Free Compression */ +#define AST_FORMAT_SPEEX16 (1ULL << 33) /*! Raw mu-law data (G.711) */ -#define AST_FORMAT_TESTLAW (1ULL << 47) +#define AST_FORMAT_TESTLAW (1ULL << 47) /*! Reserved bit - do not use */ #define AST_FORMAT_RESERVED (1ULL << 63) @@ -745,6 +747,7 @@ static force_inline int ast_format_rate(format_t format) case AST_FORMAT_G722: case AST_FORMAT_SLINEAR16: case AST_FORMAT_SIREN7: + case AST_FORMAT_SPEEX16: return 16000; case AST_FORMAT_SIREN14: return 32000; diff --git a/main/channel.c b/main/channel.c index 90ac32de0d..af2c036a9f 100644 --- a/main/channel.c +++ b/main/channel.c @@ -813,6 +813,7 @@ format_t ast_best_codec(format_t fmts) /*! iLBC is not too bad */ AST_FORMAT_ILBC, /*! Speex is free, but computationally more expensive than GSM */ + AST_FORMAT_SPEEX16, AST_FORMAT_SPEEX, /*! Ick, LPC10 sounds terrible, but at least we have code for it, if you're tacky enough to use it */ diff --git a/main/frame.c b/main/frame.c index 1eb7d411b9..9f599a9917 100644 --- a/main/frame.c +++ b/main/frame.c @@ -104,6 +104,7 @@ static const struct ast_format_list AST_FORMAT_LIST[] = { { 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 */ @@ -119,7 +120,7 @@ static const struct ast_format_list AST_FORMAT_LIST[] = { { 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_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 }, }; @@ -1354,7 +1355,7 @@ static unsigned char get_n_bits_at(unsigned char *data, int n, int bit) static int speex_get_wb_sz_at(unsigned char *data, int len, int bit) { static const int SpeexWBSubModeSz[] = { - 0, 36, 112, 192, + 4, 36, 112, 192, 352, 0, 0, 0 }; int off = bit; unsigned char c; @@ -1407,12 +1408,8 @@ static int speex_samples(unsigned char *data, int len) } bit += off; - if ((len * 8 - bit) == 0) { + if ((len * 8 - bit) < 5) break; - } else if ((len * 8 - bit) < 5) { - ast_log(LOG_WARNING, "Not enough bits remaining after wide band for speex samples.\n"); - break; - } /* get control bits */ c = get_n_bits_at(data, 5, bit); @@ -1427,12 +1424,14 @@ static int speex_samples(unsigned char *data, int len) bit += 4; bit += SpeexInBandSz[c]; } else if (c == 13) { - /* user in-band; next 5 bits contain msg len */ - c = get_n_bits_at(data, 5, bit); - bit += 5; - bit += c * 8; + /* user in-band; next 4 bits contain msg len */ + c = get_n_bits_at(data, 4, bit); + bit += 4; + /* after which it's 5-bit signal id + c bytes of data */ + bit += 5 + c * 8; } else if (c > 8) { /* unknown */ + ast_log(LOG_WARNING, "Unknown speex control frame %d\n", c); break; } else { /* skip number bits for submode (less the 5 control bits) */ @@ -1452,6 +1451,9 @@ int ast_codec_get_samples(struct ast_frame *f) case AST_FORMAT_SPEEX: samples = speex_samples(f->data.ptr, f->datalen); break; + case AST_FORMAT_SPEEX16: + samples = 2 * speex_samples(f->data.ptr, f->datalen); + break; case AST_FORMAT_G723_1: samples = g723_samples(f->data.ptr, f->datalen); break; diff --git a/main/rtp_engine.c b/main/rtp_engine.c index c42d3f6fb1..5e4db23094 100644 --- a/main/rtp_engine.c +++ b/main/rtp_engine.c @@ -102,6 +102,7 @@ static const struct ast_rtp_mime_type { {{1, AST_FORMAT_G729A}, "audio", "G729A", 8000}, {{1, AST_FORMAT_G729A}, "audio", "G.729", 8000}, {{1, AST_FORMAT_SPEEX}, "audio", "speex", 8000}, + {{1, AST_FORMAT_SPEEX16}, "audio", "speex", 16000}, {{1, AST_FORMAT_ILBC}, "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 @@ -171,6 +172,7 @@ static const struct ast_rtp_payload_type static_RTP_PT[AST_RTP_MAX_PT] = { [112] = {1, AST_FORMAT_G726_AAL2}, [115] = {1, AST_FORMAT_SIREN14}, [116] = {1, AST_FORMAT_G719}, + [117] = {1, AST_FORMAT_SPEEX16}, [121] = {0, AST_RTP_CISCO_DTMF}, /* Must be type 121 */ }; diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c index d9f54d56c0..7fe8ff9ba5 100644 --- a/res/res_rtp_asterisk.c +++ b/res/res_rtp_asterisk.c @@ -1228,6 +1228,7 @@ static int ast_rtp_write(struct ast_rtp_instance *instance, struct ast_frame *fr switch (subclass) { case AST_FORMAT_SPEEX: + case AST_FORMAT_SPEEX16: case AST_FORMAT_G723_1: case AST_FORMAT_SIREN7: case AST_FORMAT_SIREN14: