#1151451 asterisk: 2016_opus_plc.patch silences transcoded calls after an RTP SSRC change

Package:
asterisk
Source:
asterisk
Description:
Open Source Private Branch Exchange (PBX)
Submitter:
3angular Solutions GmbH – Tech
Date:
2026-10-20 19:05:02 UTC
Severity:
normal
Tags:
#1151451#5
Date:
2026-10-20 19:03:53 UTC
From:
To:
Dear Maintainer,

debian/patches/2016_opus_plc.patch (added for #1060275) makes Asterisk
drop all audio on a transcoded call leg once the far end switches to a
new RTP stream whose sequence numbers start below the old ones. Every
frame is then discarded, without a log message, until the new numbers
catch up with the old: up to 32767 packets, i.e. up to 11 minutes at
50 packets/s, in practice the rest of the call. This hits any
transcoded leg, not only Opus: we found it in Zamfono, a PBX built on
Debian's Asterisk packages, with AMR-WB from a mobile carrier (the
caller heard nothing after a transfer) and reproduced it with
PCMA -> G.722. Carriers and SBCs change the SSRC during early
media and on transfers. These upstream reports, both from Debian
builds, look like the same thing:
https://github.com/asterisk/asterisk/issues/1324
https://community.asterisk.org/t/ssrc-change-bridging/103903

The experimental 22.11.0 release candidate ships the same patch file.

Analysis
--------

The patch makes ast_translate() (main/translate.c) compare each frame's
RTP sequence number with the previous one on the translation path:

- A frame up to 32767 behind counts as late and is freed (return NULL).
  After a new stream starts k numbers lower, the next k frames are all
  "late".
- A new stream up to 32767 ahead makes the loop synthesize that many
  empty "lost" frames in a single call. Decoders with native PLC (Opus,
  Speex, iLBC) turn each into a frame of concealment audio, so the next
  leg gets a burst of up to 11 minutes of audio at once.
- At the 65535 -> 0 wrap the gap is computed off by one: frame 0 is
  dropped as late and nothing is concealed.

The late-frame drop runs on every translation path, including those
whose decoder has no native PLC, where the patch has nothing to offer.

Proposed fix
------------

The attached 2016_opus_plc.patch replaces the current one. It keeps the
native PLC that #1060275 asked for:

1. The sequence number logic only runs on translation paths that start
   with a native-PLC decoder. Other paths never drop frames as late.
2. Gaps of up to 50 packets are still concealed and packets up to 50
   behind are still dropped as late. A larger jump either way is taken
   as a new stream: the path follows its numbering, nothing is
   synthesized and nothing is dropped.
3. The distance is computed modulo 2^16, which fixes the wrap.
4. ast_debug() lines for concealed gaps, late drops and new streams
   replace the commented-out NOTICE (which contained a nested comment
   opener).

The second attachment, translate.c-vs-debian.diff, shows the change
against translate.c as the current patch leaves it.

Testing
-------

Built 1:22.10.1+dfsg+~cs6.17.60671434-1 with the revised patch on
trixie (amd64). Calls go through Asterisk from a SIPp far end playing
crafted RTP to a baresip phone on G.722 (SRTP). Packets that reached
the phone, with the current patch / the revised patch / the patch left
out:

- PCMA, new SSRC 5024 numbers lower: silence after the switch / all /
  all.
- Opus, new SSRC 5000 numbers higher, then another 5000 lower: a burst
  of 4999 synthesized packets, then silence / all, no burst / all.
- Opus, 5 % loss and 4 swapped pairs: 500 of 500 / 500 of 500 / 478
  (no PLC).
- Opus across 65535 -> 0: 299 of 300 / 300 of 300 / not run.

With the revised patch and debug enabled, the loss run logs exactly the
26 concealed frames (22 lost, 4 arriving after their successor) and the 4
late drops. The jump run logs two "new stream" lines and nothing else.

I can send the RTP generator and SIPp scenarios if useful.

Regards,
Lukas Fabrykowski
3angular Solutions GmbH