From c7455f5a37d349e021d6611fd8198a493b23ff6d Mon Sep 17 00:00:00 2001 From: shaunjstokes Date: Mon, 14 Oct 2024 13:18:57 +0200 Subject: [PATCH 1/2] [core/media] allow transcoding between different rates in Opus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In vars.xml , when you use codec settings like this: `` The switch_core_media.c line 5520 only takes the OPUS default rate (48k) instead of 16k in the config. Because of this, FS won't be able to choose opus if your client asks for 16k opus. You will see same log symptom as #2226 We've found that the patch #2582 has the side effect of causing calls that fail to negotiate the same codec as Leg A for Leg B to fail with SIP 488 INCOMPATIBLE_DESTINATION. That's not an option for us, we need backwards compatibility with other codecs. Rather than apply this for all codecs it should only apply for Opus. Now calls using Opus and other codecs establish correctly on both legs, and there are no issues with codec negotiation when the codecs on Leg A and Leg B don't match. Tested-by: Jérôme Poulin Co-authored-by: magiclin99 --- src/switch_core_media.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/switch_core_media.c b/src/switch_core_media.c index de5d0eff74c..8b533fa50eb 100644 --- a/src/switch_core_media.c +++ b/src/switch_core_media.c @@ -5517,6 +5517,10 @@ SWITCH_DECLARE(uint8_t) switch_core_media_negotiate_sdp(switch_core_session_t *s uint32_t bit_rate = imp->bits_per_second; uint32_t codec_rate = imp->samples_per_second; + if (!strcasecmp(map->rm_encoding, "opus")) { + codec_rate = imp->actual_samples_per_second; + } + if (imp->codec_type != SWITCH_CODEC_TYPE_AUDIO) { continue; } From ac8b8afe8180d73b1be87b53a7d0f9ef23a27756 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Poulin?= Date: Fri, 1 Aug 2025 10:23:13 -0400 Subject: [PATCH 2/2] [mod_opus] set RTP interval for RFC 7587 compliance Configure proper RTP timestamp increments for Opus codec to ensure compliance with RFC 7587. This fixes timestamp handling when transcoding between different sample rates. --- src/mod/codecs/mod_opus/mod_opus.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/mod/codecs/mod_opus/mod_opus.c b/src/mod/codecs/mod_opus/mod_opus.c index 96933e6ea59..21bf642c3f6 100644 --- a/src/mod/codecs/mod_opus/mod_opus.c +++ b/src/mod/codecs/mod_opus/mod_opus.c @@ -144,6 +144,7 @@ struct opus_context { enc_stats_t encoder_stats; codec_control_state_t control_state; switch_bool_t recreate_decoder; + uint32_t encoder_samplerate; }; struct { @@ -177,6 +178,11 @@ static switch_bool_t switch_opus_acceptable_rate(int rate) return SWITCH_TRUE; } +static inline uint32_t opus_ts_step(uint32_t ptime_ms, uint32_t enc_rate) +{ + return (ptime_ms * 48); /* 48 kHz RTP clock rate */ +} + static uint32_t switch_opus_encoder_set_audio_bandwidth(OpusEncoder *encoder_object,int enc_samplerate) { if (enc_samplerate == 8000) { /* Audio Bandwidth: 0-4000Hz Sampling Rate: 8000Hz */ @@ -618,6 +624,9 @@ static switch_status_t switch_opus_init(switch_codec_t *codec, switch_codec_flag return SWITCH_STATUS_GENERR; } + /* Store encoder sample rate for RTP timestamp scaling */ + context->encoder_samplerate = enc_samplerate; + /* https://tools.ietf.org/html/rfc7587 */ if (opus_codec_settings.maxaveragebitrate) { opus_encoder_ctl(context->encoder_object, OPUS_SET_BITRATE(opus_codec_settings.maxaveragebitrate)); @@ -680,6 +689,7 @@ static switch_status_t switch_opus_init(switch_codec_t *codec, switch_codec_flag if (opus_prefs.adjust_bitrate) { switch_set_flag(codec, SWITCH_CODEC_FLAG_HAS_ADJ_BITRATE); } + } if (decoding) { @@ -718,6 +728,19 @@ static switch_status_t switch_opus_init(switch_codec_t *codec, switch_codec_flag } } + /* Set RTP interval for RFC 7587 compliance */ + if (codec->session) { + switch_rtp_t *rtp = switch_core_media_get_rtp_session(codec->session, SWITCH_MEDIA_TYPE_AUDIO); + if (rtp) { + uint32_t ptime_ms = codec->implementation->microseconds_per_packet / 1000; + uint32_t step = opus_ts_step(ptime_ms, codec->implementation->actual_samples_per_second); + switch_rtp_set_interval(rtp, ptime_ms, step); + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, + "Opus codec initialized without session - RTP interval not configured\n"); + } + context->codec_settings = opus_codec_settings; codec->private_info = context;