diff --git a/README.md b/README.md index 82c4b02d3..e6dd84c1d 100644 --- a/README.md +++ b/README.md @@ -514,6 +514,12 @@ was not received will ever be requested, and a CMR will be sent only once per in specify a CMR interval of 500 milliseconds (with `=` escapes): `codec-transcode-AMR-WB/16000/1/23850//mode-set--0,1,2/CMR-interval--500` +Similar to the `CMR-interval` option, *rtpengine* can optionally attempt to periodically increase the +outgoing bitrate without being requested to by the peer via a CMR. To enable this, set the option +`mode-change-interval` to the desired interval in milliseconds. If the last CMR from the AMR peer was +longer than this interval ago, *rtpengine* will increase the bitrate by one step if possible. Afterwards, +the interval starts over. + Call recording ============== diff --git a/lib/codeclib.c b/lib/codeclib.c index ed6030a4f..3dfeba08a 100644 --- a/lib/codeclib.c +++ b/lib/codeclib.c @@ -1650,12 +1650,17 @@ static void amr_set_dec_codec_options(str *key, str *value, void *data) { if (!str_cmp(key, "CMR-interval")) dec->codec_options.amr.cmr_interval = str_to_i(value, 0); + else if (!str_cmp(key, "mode-change-interval")) + dec->codec_options.amr.mode_change_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 + ; // not an encoder option + else if (!str_cmp(key, "mode-change-interval")) + ; // not an encoder option else { // our string might not be null terminated char *s = g_strdup_printf(STR_FORMAT, STR_FMT(key)); @@ -1770,8 +1775,21 @@ static int amr_decoder_input(decoder_t *dec, const str *data, GQueue *out) { goto err; unsigned int cmr_int = cmr_chr[0] >> 4; - if (cmr_int != 15) + if (cmr_int != 15) { decoder_event(dec, CE_AMR_CMR_RECV, GUINT_TO_POINTER(cmr_int)); + dec->u.avc.u.amr.last_cmr = rtpe_now; + } + else if (dec->codec_options.amr.mode_change_interval) { + // no CMR, check if we're due to do our own mode change + if (!dec->u.avc.u.amr.last_cmr.tv_sec) // start tracking now + dec->u.avc.u.amr.last_cmr = rtpe_now; + else if (timeval_diff(&rtpe_now, &dec->u.avc.u.amr.last_cmr) + >= dec->codec_options.amr.mode_change_interval * 1000) { + // switch up if we can + decoder_event(dec, CE_AMR_CMR_RECV, GUINT_TO_POINTER(0xffff)); + dec->u.avc.u.amr.last_cmr = rtpe_now; + } + } if (dec->codec_options.amr.octet_aligned) { if (bitstr_shift(&d, 4)) @@ -1874,6 +1892,41 @@ err: return -1; } +static unsigned int amr_encoder_find_next_mode(encoder_t *enc) { + int mode = -1; + for (int i = 0; i < AMR_FT_TYPES; i++) { + int br = enc->codec_options.amr.bitrates[i]; + if (!br) // end of list + break; + if (br == enc->u.avc.avcctx->bit_rate) { + mode = i; + break; + } + } + if (mode == -1) + return -1; + int next_mode = mode + 1; + // if modes are restricted, find the next one up + if (enc->codec_options.amr.mode_set) { + // is there anything? + if ((1 << next_mode) > enc->codec_options.amr.mode_set) + return -1; + int next_up = -1; + for (; next_mode < AMR_FT_TYPES; next_mode++) { + if (!(enc->codec_options.amr.mode_set & (1 << next_mode))) + continue; + next_up = next_mode; + break; + } + if (next_up == -1) + return -1; + next_mode = next_up; + } + // valid mode? + if (next_mode >= AMR_FT_TYPES || enc->codec_options.amr.bitrates[next_mode] == 0) + return -1; + return next_mode; +} static void amr_encoder_mode_change(encoder_t *enc) { if (!memcmp(&enc->codec_options.amr.cmr.cmr_in_ts, &enc->u.avc.u.amr.cmr_in_ts, sizeof(struct timeval))) @@ -1882,6 +1935,8 @@ static void amr_encoder_mode_change(encoder_t *enc) { if (enc->codec_options.amr.mode_change_period == 2 && (enc->u.avc.u.amr.pkt_seq & 1) != 0) return; unsigned int cmr = enc->codec_options.amr.cmr.cmr_in; + if (cmr == 0xffff) + cmr = amr_encoder_find_next_mode(enc); if (cmr >= AMR_FT_TYPES) return; // ignore CMR for invalid modes diff --git a/lib/codeclib.h b/lib/codeclib.h index 7dbce16f7..a293de4f8 100644 --- a/lib/codeclib.h +++ b/lib/codeclib.h @@ -100,6 +100,7 @@ union codec_options_u { int interleaving; unsigned int mode_set; // bitfield int mode_change_period; + int mode_change_interval; int octet_aligned:1; int crc:1; int robust_sorting:1; @@ -184,6 +185,7 @@ struct decoder_s { struct { uint16_t bitrate_tracker[AMR_FT_TYPES]; struct timeval tracker_end; + struct timeval last_cmr; } amr; } u; } avc;