diff --git a/lib/codeclib.c b/lib/codeclib.c index b7220af65..0a28379a9 100644 --- a/lib/codeclib.c +++ b/lib/codeclib.c @@ -52,6 +52,7 @@ static void libopus_encoder_close(encoder_t *enc); static format_init_f opus_init; static select_encoder_format_f opus_select_encoder_format; static select_decoder_format_f opus_select_decoder_format; +static format_parse_f opus_format_parse; static format_parse_f ilbc_format_parse; static set_enc_options_f ilbc_set_enc_options; @@ -443,6 +444,8 @@ static struct codec_def_s __codec_defs[] = { .media_type = MT_AUDIO, .codec_type = &codec_type_libopus, .init = opus_init, + .default_fmtp = "useinbandfec=1", + .format_parse = opus_format_parse, .format_cmp = format_cmp_ignore, .select_encoder_format = opus_select_encoder_format, .select_decoder_format = opus_select_decoder_format, @@ -1925,7 +1928,6 @@ struct libopus_encoder_options { int complexity; int vbr; int vbr_constraint; - int fec; int pl; int application; }; @@ -1970,10 +1972,6 @@ static void libopus_set_enc_opts(str *key, str *val, void *p) { case CSH_LOOKUP("packet loss"): opts->pl = str_to_i(val, -1); break; - case CSH_LOOKUP("fec"): - case CSH_LOOKUP("FEC"): - opts->fec = str_to_i(val, -1); - break; default: ilog(LOG_WARN | LOG_FLAG_LIMIT, "Unknown Opus encoder option encountered: '" STR_FORMAT "'", STR_FMT(key)); @@ -2036,7 +2034,7 @@ static const char *libopus_encoder_init(encoder_t *enc, const str *extra_opts) { if (err != OPUS_OK) ilog(LOG_WARN | LOG_FLAG_LIMIT, "Failed to set Opus PL%% to %i': %s", opts.complexity, opus_strerror(err)); - err = opus_encoder_ctl(enc->u.opus, OPUS_SET_INBAND_FEC(opts.fec)); + err = opus_encoder_ctl(enc->u.opus, OPUS_SET_INBAND_FEC(enc->format_options.opus.fec_send >= 0)); if (err != OPUS_OK) ilog(LOG_WARN | LOG_FLAG_LIMIT, "Failed to set Opus FEC to %i': %s", opts.complexity, opus_strerror(err)); @@ -2143,6 +2141,42 @@ static void opus_select_decoder_format(decoder_t *dec, const struct rtp_codec_fo if (dec->in_format.channels == 2 && dec->dest_format.channels == 1) dec->in_format.channels = 1; } +static void opus_parse_format_cb(str *key, str *token, void *data) { + union codec_format_options *opts = data; + __auto_type o = &opts->opus; + + switch (__csh_lookup(key)) { +#define YNFLAG(flag, varname) \ + case flag: \ + if (token->len == 1 && token->s[0] == '1') \ + o->varname = 1; \ + else if (token->len == 1 && token->s[0] == '0') \ + o->varname = -1; \ + break; + YNFLAG(CSH_LOOKUP("stereo"), stereo_recv) + YNFLAG(CSH_LOOKUP("sprop-stereo"), stereo_send) + YNFLAG(CSH_LOOKUP("useinbandfec"), fec_recv) + YNFLAG(CSH_LOOKUP("cbr"), cbr) + YNFLAG(CSH_LOOKUP("usedtx"), fec_recv) +#undef YNFLAG + case CSH_LOOKUP("maxplaybackrate"): + opts->opus.maxplaybackrate = str_to_i(token, 0); + break; + case CSH_LOOKUP("sprop-maxcapturerate"): + opts->opus.sprop_maxcapturerate = str_to_i(token, 0); + break; + case CSH_LOOKUP("maxaveragebitrate"): + opts->opus.maxaveragebitrate = str_to_i(token, 0); + break; + case CSH_LOOKUP("minptime"): + opts->opus.minptime = str_to_i(token, 0); + break; + } +} +static int opus_format_parse(struct rtp_codec_format *f, const str *fmtp) { + codeclib_key_value_parse(fmtp, true, opus_parse_format_cb, &f->parsed); + return 0; +} static int ilbc_format_parse(struct rtp_codec_format *f, const str *fmtp) { diff --git a/lib/rtplib.h b/lib/rtplib.h index 898409015..8325efa12 100644 --- a/lib/rtplib.h +++ b/lib/rtplib.h @@ -66,6 +66,23 @@ union codec_format_options { // AMR bit options unsigned int mode_change_neighbor:1; } evs; + + struct { + // 0 = default, 1 = set, -1 = not set (0) + int stereo_recv:2; + int stereo_send:2; + int fec_recv:2; + int fec_send:2; + + // these are parsed out but ignored + int cbr:2; + int usedtx:2; + int maxplaybackrate; + int sprop_maxcapturerate; + int maxaveragebitrate; + + int minptime; // obsolete + } opus; }; struct rtp_codec_format { diff --git a/t/auto-daemon-tests.pl b/t/auto-daemon-tests.pl index 87d8e23e3..5f3c0892c 100755 --- a/t/auto-daemon-tests.pl +++ b/t/auto-daemon-tests.pl @@ -1645,6 +1645,7 @@ t=0 0 m=audio PORT RTP/AVP 8 96 a=rtpmap:8 PCMA/8000 a=rtpmap:96 opus/48000/2 +a=fmtp:96 useinbandfec=1 a=sendrecv a=rtcp:PORT SDP @@ -1747,6 +1748,7 @@ t=0 0 m=audio PORT RTP/AVP 8 96 a=rtpmap:8 PCMA/8000 a=rtpmap:96 opus/48000/2 +a=fmtp:96 useinbandfec=1 a=sendrecv a=rtcp:PORT SDP @@ -6186,6 +6188,7 @@ t=0 0 m=audio PORT RTP/AVP 9 97 108 8 96 98 101 a=rtpmap:9 G722/8000 a=rtpmap:97 opus/48000 +a=fmtp:97 useinbandfec=1 a=rtpmap:108 speex/16000 a=rtpmap:8 PCMA/8000 a=rtpmap:96 telephone-event/8000 @@ -8280,6 +8283,7 @@ m=audio PORT RTP/AVP 0 8 96 97 101 a=rtpmap:0 PCMU/8000 a=rtpmap:8 PCMA/8000 a=rtpmap:96 opus/48000/2 +a=fmtp:96 useinbandfec=1 a=rtpmap:97 telephone-event/48000 a=fmtp:97 0-15 a=rtpmap:101 telephone-event/8000 @@ -8341,6 +8345,7 @@ c=IN IP4 203.0.113.1 t=0 0 m=audio PORT RTP/AVP 97 8 101 102 a=rtpmap:97 opus/48000 +a=fmtp:97 useinbandfec=1 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000 a=rtpmap:102 telephone-event/48000 @@ -17764,6 +17769,7 @@ m=audio PORT RTP/AVP 8 96 98 97 c=IN IP4 203.0.113.1 a=rtpmap:8 PCMA/8000 a=rtpmap:96 opus/48000/2 +a=fmtp:96 useinbandfec=1 a=rtpmap:98 telephone-event/48000 a=fmtp:98 0-15 a=rtpmap:97 telephone-event/8000 @@ -17828,6 +17834,7 @@ t=0 0 m=audio PORT RTP/AVP 96 98 c=IN IP4 203.0.113.1 a=rtpmap:96 opus/48000/2 +a=fmtp:96 useinbandfec=1 a=rtpmap:98 telephone-event/48000 a=fmtp:98 0-15 a=sendrecv diff --git a/t/test-transcode.c b/t/test-transcode.c index 9b64df758..c80fa0091 100644 --- a/t/test-transcode.c +++ b/t/test-transcode.c @@ -1453,7 +1453,7 @@ int main(void) { transcode(PCMA); transcode(telephone-event); offer(); - expect(B, "96/opus/48000 8/PCMA/8000 97/telephone-event/48000/0-15 101/telephone-event/8000"); + expect(B, "96/opus/48000/useinbandfec=1 8/PCMA/8000 97/telephone-event/48000/0-15 101/telephone-event/8000"); sdp_pt(96, opus, 48000); sdp_pt(97, telephone-event, 48000); flags.single_codec = 1;