VAPI-3479 Match telephone-event clock rate to the primary audio codec (fixes silent DTMF loss)#11
Merged
Conversation
DTMF from a WebRTC endpoint was silently lost (audio unaffected) when the publish offer paired telephone-event/8000 with Opus/48000. RFC 4733 requires telephone-event to share the audio codec's RTP clock, so a peer that only registers telephone-event at the audio clock (e.g. pv-gateway at 48000 for Opus) fails to negotiate it, and endpoint DTMF is then dropped/mislabeled before it reaches the media server. The prior logic appended getCapabilities().find(telephone-event) — the first match, often the 8000 variant — whenever telephone-event was absent from the codec preferences (which is exactly the case when Opus is forced). Now strip all telephone-event codecs and re-add only the one whose clockRate matches the primary (first) media codec: Opus -> 48000, PCMU -> 8000, without hardcoding either rate. Prefer a match already in the caller's preferences, fall back to full capabilities, and omit telephone-event entirely rather than offer a mismatched one the peer will drop. Tests rewritten to assert clock-matching (they previously codified the buggy 8000 pairing). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
dhelms-bw
approved these changes
Jul 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
DTMF sent from a WebRTC endpoint was silently lost (audio unaffected) on inbound-call-to-endpoint flows.
Root cause: RFC 4733
telephone-eventmust share the audio codec's RTP clock. When the publish offer forces Opus (codecPreferences {audio:[opus/48000]}), the old logic appendedRTCRtpSender.getCapabilities().codecs.find(telephone-event)— the first telephone-event, oftentelephone-event/8000— producing an offer ofopus/48000 + telephone-event/8000.Fix
Strip all telephone-event codecs and re-add only the one whose
clockRatematches the primary (first) media codec:telephone-event/48000telephone-event/8000No rate is hardcoded. It prefers a match already in the caller's preferences, falls back to full capabilities, and omits telephone-event rather than offer a mismatched one the peer will drop.
Tests
Rewrote the
addStreamToPublishingPeerConnectiontests (they previously codified the buggyopus/48000 + telephone-event/8000pairing) to assert clock-matching across: appended-when-missing, replaces-mismatched, keeps-matched, omits-when-no-match, and no-explicit-prefs.20/20pass,tsc --noEmitclean.