From 23b1aaea1086fbd6dbfa8fe81c5b465e39d97311 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Mon, 31 Aug 2020 11:57:42 -0400 Subject: [PATCH] TT#91003 support AMR mode-set option Change-Id: I9926e940d7e77d869b2ebdd975417745d42ec1fd --- README.md | 26 +++++++++++++++++ lib/codeclib.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++-- lib/codeclib.h | 2 ++ 3 files changed, 101 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f6a4e9762..3e46a028d 100644 --- a/README.md +++ b/README.md @@ -476,6 +476,32 @@ this case if no other codecs are specified. For further information, see the section on the `T.38` dictionary key below. +AMR and AMR-WB +-------------- + +As AMR supports dynamically adapting the encoder bitrate, as well as restricting the available bitrates, +there are some slight peculiarities about its usage when transcoding. + +When setting the bitrate, for example as `AMR-WB/16000/1/23850` in either the `codec-transcode` or the +`codec-set` options, that bitrate will be used as the highest permitted bitrate for the encoder. If +no `mode-set` parameter is communicated in the SDP, then that is the bitrate that will be used. + +If a `mode-set` is present, then the highest bitrate from that mode set which is lower or equal to the +given bitrate will be used. If only higher bitrates are allowed by the mode set, then the next higher +bitrate will be used. + +To produce an SDP that includes the `mode-set` option (when adding AMR to the codec list via +`codec-transcode`), the full format parameter string can be appended to the codec specification, e.g. +`codec-transcode-AMR-WB/16000/1/23850//mode-set=0,1,2,3,4,5;octet-align=1`. In this example, the bitrate +23850 won't actually be used, as the highest permitted mode is 5 (18250 bps) and so that bitrate will +be used. + +If a literal `=` cannot be used due to parsing constraints (i.e. being wrongly interpreted as a +key-value pair), it can be escaped by using two dashes instead, e.g. +`codec-transcode-AMR-WB/16000/1/23850//mode-set--0,1,2,3,4,5;octet-align--1` + +The default (highest) bitrates for AMR and AMR-WB are 6700 and 14250, respectively. + Call recording ============== diff --git a/lib/codeclib.c b/lib/codeclib.c index 5d9ecd6e6..095c5fe5f 100644 --- a/lib/codeclib.c +++ b/lib/codeclib.c @@ -1477,6 +1477,22 @@ static int ilbc_decoder_input(decoder_t *dec, const str *data, GQueue *out) { #define AMR_FT_TYPES 14 +const static unsigned int amr_bitrates[AMR_FT_TYPES] = { + 4750, // 0 + 5150, // 1 + 5900, // 2 + 6700, // 3 + 7400, // 4 + 7950, // 5 + 10200, // 6 + 12200, // 7 + 0, // comfort noise // 8 + 0, // comfort noise // 9 + 0, // comfort noise // 10 + 0, // comfort noise // 11 + 0, // invalid // 12 + 0, // invalid // 13 +}; const static unsigned int amr_bits_per_frame[AMR_FT_TYPES] = { 95, // 4.75 kbit/s // 0 103, // 5.15 kbit/s // 1 @@ -1493,6 +1509,22 @@ const static unsigned int amr_bits_per_frame[AMR_FT_TYPES] = { 0, // invalid // 12 0, // invalid // 13 }; +const static unsigned int amr_wb_bitrates[AMR_FT_TYPES] = { + 6600, // 0 + 8850, // 1 + 12650, // 2 + 14250, // 3 + 15850, // 4 + 18250, // 5 + 19850, // 6 + 23050, // 7 + 23850, // 8 + 0, // comfort noise // 9 + 0, // invalid // 10 + 0, // invalid // 11 + 0, // invalid // 12 + 0, // invalid // 13 +}; const static unsigned int amr_wb_bits_per_frame[AMR_FT_TYPES] = { 132, // 6.60 kbit/s // 0 177, // 8.85 kbit/s // 1 @@ -1510,10 +1542,14 @@ const static unsigned int amr_wb_bits_per_frame[AMR_FT_TYPES] = { 0, // invalid // 13 }; static void amr_set_encdec_options(codec_options_t *opts, const str *fmtp, const codec_def_t *def) { - if (!strcmp(def->rtpname, "AMR")) + if (!strcmp(def->rtpname, "AMR")) { opts->amr.bits_per_frame = amr_bits_per_frame; - else + opts->amr.bitrates = amr_bitrates; + } + else { opts->amr.bits_per_frame = amr_wb_bits_per_frame; + opts->amr.bitrates = amr_wb_bitrates; + } if (!fmtp || !fmtp->s) return; @@ -1547,11 +1583,46 @@ static void amr_set_encdec_options(codec_options_t *opts, const str *fmtp, const opts->amr.octet_aligned = 1; opts->amr.interleaving = str_to_i(&token, 0); } + else if (!str_cmp(&key, "mode-set")) { + str mode; + while (str_token_sep(&mode, &token, ',') == 0) { + int m = str_to_i(&mode, -1); + if (m < 0 || m >= AMR_FT_TYPES) + continue; + opts->amr.mode_set |= (1 << m); + } + } // XXX other options } } static void amr_set_enc_options(encoder_t *enc, const str *fmtp) { amr_set_encdec_options(&enc->codec_options, fmtp, enc->def); + + // if a mode-set was given, pick the highest supported bitrate + if (enc->codec_options.amr.mode_set) { + int max_bitrate = enc->u.avc.avcctx->bit_rate; + int use_bitrate = 0; + for (int i = 0; i < AMR_FT_TYPES; i++) { + if (!(enc->codec_options.amr.mode_set & (1 << i))) + continue; + unsigned int br = enc->codec_options.amr.bitrates[i]; + // we depend on the list being in ascending order, with + // invalid modes at the end + if (!br) // end of list + break; + if (br > max_bitrate && use_bitrate) // done + break; + use_bitrate = br; + } + if (!use_bitrate) + ilog(LOG_WARN, "Unable to determine a valid bitrate from %s mode-set, using default", + enc->def->rtpname); + else { + ilog(LOG_DEBUG, "Using %i as initial %s bitrate based on mode-set", + use_bitrate, enc->def->rtpname); + enc->u.avc.avcctx->bit_rate = use_bitrate; + } + } } static void amr_set_dec_options(decoder_t *dec, const str *fmtp) { amr_set_encdec_options(&dec->codec_options, fmtp, dec->def); diff --git a/lib/codeclib.h b/lib/codeclib.h index 9e077f24e..924af5f70 100644 --- a/lib/codeclib.h +++ b/lib/codeclib.h @@ -87,11 +87,13 @@ struct codec_type_s { union codec_options_u { struct { int interleaving; + unsigned int mode_set; // bitfield int octet_aligned:1; int crc:1; int robust_sorting:1; const unsigned int *bits_per_frame; + const unsigned int *bitrates; } amr; };