From ed9de316c7b407b9573130a700d8134cb871ec61 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Tue, 1 Sep 2020 15:18:15 -0400 Subject: [PATCH] TT#91003 support sending CMR Change-Id: Ief485087b36ce61c80f9d004e77113b31298b250 --- README.md | 9 +++++ daemon/codec.c | 4 +++ lib/codeclib.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++++- lib/codeclib.h | 19 ++++++++++- 4 files changed, 121 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c00d9aaee..82c4b02d3 100644 --- a/README.md +++ b/README.md @@ -505,6 +505,15 @@ The default (highest) bitrates for AMR and AMR-WB are 6700 and 14250, respective If a Codec Mode Request (CMR) is received from the AMR peer, then *rtpengine* will adhere to the request and switch encoder bitrate unconditionally, even if it's a higher bitrate than originally desired. +To enable sending CMRs to the AMR peer, the codec-specific option `CMR-interval` is provided. It takes +a number of milliseconds as argument. Throughout each interval, *rtpengine* will track which AMR frame +types were received from the peer, and then based on that will make a decision at the end of the +interval. If a higher bitrate is allowed by the mode set that was not received from the AMR peer at all, +then *rtpengine* will request switching to that bitrate per CMR. Only the next-highest bitrate mode that +was not received will ever be requested, and a CMR will be sent only once per interval. Full example to +specify a CMR interval of 500 milliseconds (with `=` escapes): +`codec-transcode-AMR-WB/16000/1/23850//mode-set--0,1,2/CMR-interval--500` + Call recording ============== diff --git a/daemon/codec.c b/daemon/codec.c index f6fceae07..7ca2a7631 100644 --- a/daemon/codec.c +++ b/daemon/codec.c @@ -1735,6 +1735,10 @@ static int codec_decoder_event(enum codec_event event, void *ptr, void *data) { media->u.amr.cmr.cmr_in = GPOINTER_TO_UINT(ptr); media->u.amr.cmr.cmr_in_ts = rtpe_now; break; + case CE_AMR_SEND_CMR: + // ignore locking and races for this + media->u.amr.cmr.cmr_out = GPOINTER_TO_UINT(ptr); + media->u.amr.cmr.cmr_out_ts = rtpe_now; default: break; } diff --git a/lib/codeclib.c b/lib/codeclib.c index c2a91697d..4af91d8fc 100644 --- a/lib/codeclib.c +++ b/lib/codeclib.c @@ -1530,7 +1530,6 @@ static void codeclib_key_value_parse(const str *instr, int need_value, -#define AMR_FT_TYPES 14 const static unsigned int amr_bitrates[AMR_FT_TYPES] = { 4750, // 0 5150, // 1 @@ -1641,9 +1640,29 @@ static void amr_set_encdec_options(codec_options_t *opts, const str *fmtp, const codeclib_key_value_parse(fmtp, 1, amr_set_encdec_options_cb, opts); } +static void amr_set_dec_codec_options(str *key, str *value, void *data) { + decoder_t *dec = data; + + if (!str_cmp(key, "CMR-interval")) + dec->codec_options.amr.cmr_interval = str_to_i(value, 0); +} +static void amr_set_enc_codec_options(str *key, str *value, void *data) { + encoder_t *enc = data; + + if (!str_cmp(key, "CMR-interval")) + ; // not an encoder optoin + else { + // our string might not be null terminated + char *s = g_strdup_printf(STR_FORMAT, STR_FMT(key)); + codeclib_set_av_opt_intstr(enc, s, value); + g_free(s); + } +} static void amr_set_enc_options(encoder_t *enc, const str *fmtp, const str *codec_opts) { amr_set_encdec_options(&enc->codec_options, fmtp, enc->def); + codeclib_key_value_parse(codec_opts, 1, amr_set_enc_codec_options, enc); + // 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; @@ -1672,8 +1691,61 @@ static void amr_set_enc_options(encoder_t *enc, const str *fmtp, const str *code } static void amr_set_dec_options(decoder_t *dec, const str *fmtp, const str *codec_opts) { amr_set_encdec_options(&dec->codec_options, fmtp, dec->def); + codeclib_key_value_parse(codec_opts, 1, amr_set_dec_codec_options, dec); } +static void amr_bitrate_tracker(decoder_t *dec, unsigned int ft) { + if (dec->codec_options.amr.cmr_interval <= 0) + return; + + if (dec->u.avc.u.amr.tracker_end.tv_sec + && timeval_cmp(&dec->u.avc.u.amr.tracker_end, &rtpe_now) >= 0) { + // analyse the data we gathered + int next_highest = -1; + int lowest_used = -1; + for (int i = 0; i < AMR_FT_TYPES; i++) { + unsigned int br = dec->codec_options.amr.bitrates[i]; + if (!br) + break; // end of list + + // ignore restricted modes + if (dec->codec_options.amr.mode_set) { + if (!(dec->codec_options.amr.mode_set & (1 << i))) + continue; + } + + // would this be a "next step up" mode? + if (next_highest == -1) + next_highest = i; + + // did we see any frames? + if (!dec->u.avc.u.amr.bitrate_tracker[i]) + continue; + + next_highest = -1; + lowest_used = i; + } + + if (lowest_used != -1 && next_highest != -1) { + // we can request a switch up + ilog(LOG_DEBUG, "Sending %s CMR to request upping bitrate to %u", + dec->def->rtpname, dec->codec_options.amr.bitrates[next_highest]); + decoder_event(dec, CE_AMR_SEND_CMR, GINT_TO_POINTER(next_highest)); + } + + // and reset tracker + ZERO(dec->u.avc.u.amr.tracker_end); + } + + if (!dec->u.avc.u.amr.tracker_end.tv_sec) { + // init + ZERO(dec->u.avc.u.amr.bitrate_tracker); + dec->u.avc.u.amr.tracker_end = rtpe_now; + timeval_add_usec(&dec->u.avc.u.amr.tracker_end, dec->codec_options.amr.cmr_interval * 1000); + } + + dec->u.avc.u.amr.bitrate_tracker[ft]++; +} static int amr_decoder_input(decoder_t *dec, const str *data, GQueue *out) { const char *err = NULL; @@ -1785,6 +1857,8 @@ static int amr_decoder_input(decoder_t *dec, const str *data, GQueue *out) { err = "failed to decode AMR data"; if (avc_decoder_input(dec, &frame, out)) goto err; + + amr_bitrate_tracker(dec, ft); } return 0; @@ -1829,6 +1903,21 @@ static int packetizer_amr(AVPacket *pkt, GString *buf, str *output, encoder_t *e s[0] = '\xf0'; // no CMR req (4 bits) + // or do we have a CMR? + if (!enc->u.avc.u.amr.cmr_out_seq) { + if (memcmp(&enc->u.avc.u.amr.cmr_out_ts, &enc->codec_options.amr.cmr.cmr_out_ts, + sizeof(struct timeval))) { + enc->u.avc.u.amr.cmr_out_seq += 3; // make this configurable? + enc->u.avc.u.amr.cmr_out_ts = enc->codec_options.amr.cmr.cmr_out_ts; + } + } + if (enc->u.avc.u.amr.cmr_out_seq) { + enc->u.avc.u.amr.cmr_out_seq--; + unsigned int cmr = enc->codec_options.amr.cmr.cmr_out; + if (cmr < AMR_FT_TYPES && enc->codec_options.amr.bitrates[cmr]) + s[0] = cmr << 4; + } + if (enc->codec_options.amr.octet_aligned) { unsigned int offset = 1; // CMR byte if (enc->codec_options.amr.interleaving) diff --git a/lib/codeclib.h b/lib/codeclib.h index 2583c901f..565ff092e 100644 --- a/lib/codeclib.h +++ b/lib/codeclib.h @@ -46,6 +46,8 @@ INLINE enum media_type codec_get_type(const str *type) { #include #endif +#define AMR_FT_TYPES 14 + struct codec_type_s; @@ -88,6 +90,9 @@ struct codec_type_s { struct amr_cmr { struct timeval cmr_in_ts; unsigned int cmr_in; + + struct timeval cmr_out_ts; + unsigned int cmr_out; }; union codec_options_u { @@ -102,6 +107,8 @@ union codec_options_u { const unsigned int *bitrates; struct amr_cmr cmr; // input from external calling code + + int cmr_interval; } amr; }; @@ -154,6 +161,7 @@ struct resample_s { enum codec_event { CE_AMR_CMR_RECV, + CE_AMR_SEND_CMR, }; struct decoder_s { @@ -169,6 +177,13 @@ struct decoder_s { struct { AVCodecContext *avcctx; AVPacket avpkt; + + union { + struct { + uint16_t bitrate_tracker[AMR_FT_TYPES]; + struct timeval tracker_end; + } amr; + } u; } avc; #ifdef HAVE_BCG729 bcg729DecoderChannelContextStruct *bcg729; @@ -202,7 +217,9 @@ struct encoder_s { union { struct { - struct timeval cmr_in_ts; // used internally + struct timeval cmr_in_ts; + struct timeval cmr_out_ts; + unsigned int cmr_out_seq; } amr; } u; } avc;