diff --git a/lib/codeclib.c b/lib/codeclib.c index 4af91d8fc..ed6030a4f 100644 --- a/lib/codeclib.c +++ b/lib/codeclib.c @@ -361,7 +361,7 @@ static codec_def_t __codec_defs[] = { .default_channels = 1, .default_bitrate = 6700, .default_ptime = 20, - .default_fmtp = "octet-align=1", + .default_fmtp = "octet-align=1;mode-change-capability=2", .packetizer = packetizer_amr, .bits_per_sample = 2, // max is 12200 / 8000 = 1.525 bits per sample, rounded up .media_type = MT_AUDIO, @@ -378,7 +378,7 @@ static codec_def_t __codec_defs[] = { .default_channels = 1, .default_bitrate = 14250, .default_ptime = 20, - .default_fmtp = "octet-align=1", + .default_fmtp = "octet-align=1;mode-change-capability=2", .packetizer = packetizer_amr, .bits_per_sample = 2, // max is 23850 / 16000 = 1.490625 bits per sample, rounded up .media_type = MT_AUDIO, @@ -1626,7 +1626,12 @@ static void amr_set_encdec_options_cb(str *key, str *token, void *data) { opts->amr.mode_set |= (1 << m); } } - // XXX other options + else if (!str_cmp(key, "mode-change-period")) + opts->amr.mode_change_period = str_to_i(token, 0); + else if (!str_cmp(key, "mode-change-neighbor")) { + if (token->len == 1 && token->s[0] == '1') + opts->amr.mode_change_neighbor = 1; + } } static void amr_set_encdec_options(codec_options_t *opts, const str *fmtp, const codec_def_t *def) { if (!strcmp(def->rtpname, "AMR")) { @@ -1873,6 +1878,9 @@ 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))) return; + // mode change requested: check if this is allowed right now + 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 >= AMR_FT_TYPES) return; @@ -1882,10 +1890,43 @@ static void amr_encoder_mode_change(encoder_t *enc) { int req_br = enc->codec_options.amr.bitrates[cmr]; if (!req_br) return; + int cmr_done = 1; + if (enc->codec_options.amr.mode_change_neighbor) { + // handle non-neighbour mode changes + int cur_br = enc->u.avc.avcctx->bit_rate; + // step up or down from the requested bitrate towards the current one + int cmr_diff = (req_br > cur_br) ? -1 : 1; + int neigh_br = req_br; + int cmr_br = req_br; + while (1) { + // step up or down towards the current bitrate + cmr += cmr_diff; + // still in bounds? + if (cmr < 0 || cmr >= AMR_FT_TYPES) + break; + cmr_br = enc->codec_options.amr.bitrates[cmr]; + if (cmr_br == cur_br) + break; + // allowed by mode set? + if (enc->codec_options.amr.mode_set) { + if (!(enc->codec_options.amr.mode_set & (1 << cmr))) + continue; // go to next mode + } + // valid bitrate - continue stepping + neigh_br = cmr_br; + } + // did we finish stepping or is there more to go? + if (neigh_br != req_br) + cmr_done = 0; + req_br = neigh_br; // set to this + } enc->u.avc.avcctx->bit_rate = req_br; + if (cmr_done) + enc->u.avc.u.amr.cmr_in_ts = enc->codec_options.amr.cmr.cmr_in_ts; } static void amr_encoder_got_packet(encoder_t *enc) { amr_encoder_mode_change(enc); + enc->u.avc.u.amr.pkt_seq++; } static int packetizer_amr(AVPacket *pkt, GString *buf, str *output, encoder_t *enc) { assert(pkt->size >= 1); diff --git a/lib/codeclib.h b/lib/codeclib.h index 565ff092e..7dbce16f7 100644 --- a/lib/codeclib.h +++ b/lib/codeclib.h @@ -99,9 +99,11 @@ union codec_options_u { struct { int interleaving; unsigned int mode_set; // bitfield + int mode_change_period; int octet_aligned:1; int crc:1; int robust_sorting:1; + int mode_change_neighbor:1; const unsigned int *bits_per_frame; const unsigned int *bitrates; @@ -220,6 +222,7 @@ struct encoder_s { struct timeval cmr_in_ts; struct timeval cmr_out_ts; unsigned int cmr_out_seq; + uint64_t pkt_seq; } amr; } u; } avc; diff --git a/t/auto-daemon-tests.pl b/t/auto-daemon-tests.pl index 288efe72b..2aa14889f 100755 --- a/t/auto-daemon-tests.pl +++ b/t/auto-daemon-tests.pl @@ -60,7 +60,7 @@ m=audio PORT RTP/AVP 8 96 c=IN IP4 203.0.113.1 a=rtpmap:8 PCMA/8000 a=rtpmap:96 AMR-WB/16000 -a=fmtp:96 octet-align=1 +a=fmtp:96 octet-align=1;mode-change-capability=2 a=sendrecv a=rtcp:PORT SDP @@ -74,7 +74,7 @@ t=0 0 m=audio 3002 RTP/AVP 96 c=IN IP4 198.51.100.10 a=rtpmap:96 AMR-WB/16000 -a=fmtp:96 octet-align=1 +a=fmtp:96 octet-align=1;mode-change-capability=2 a=sendrecv -------------------------------------- v=0 @@ -115,7 +115,7 @@ m=audio PORT RTP/AVP 8 96 c=IN IP4 203.0.113.1 a=rtpmap:8 PCMA/8000 a=rtpmap:96 AMR-WB/16000 -a=fmtp:96 octet-align=1 +a=fmtp:96 octet-align=1;mode-change-capability=2 a=sendrecv a=rtcp:PORT SDP @@ -129,7 +129,7 @@ t=0 0 m=audio 3006 RTP/AVP 96 c=IN IP4 198.51.100.10 a=rtpmap:96 AMR-WB/16000 -a=fmtp:96 octet-align=1 +a=fmtp:96 octet-align=1;mode-change-capability=2 a=sendrecv -------------------------------------- v=0 @@ -170,7 +170,7 @@ m=audio PORT RTP/AVP 8 96 c=IN IP4 203.0.113.1 a=rtpmap:8 PCMA/8000 a=rtpmap:96 AMR-WB/16000 -a=fmtp:96 octet-align=1 +a=fmtp:96 octet-align=1;mode-change-capability=2 a=sendrecv a=rtcp:PORT SDP @@ -184,7 +184,7 @@ t=0 0 m=audio 3010 RTP/AVP 96 c=IN IP4 198.51.100.10 a=rtpmap:96 AMR-WB/16000 -a=fmtp:96 octet-align=1; mode-set=0,1,2,3,4 +a=fmtp:96 octet-align=1; mode-set=0,1,2,3,4; mode-change-capability=2 a=sendrecv -------------------------------------- v=0