Skip to content

VAPI-3479 Match telephone-event clock rate to the primary audio codec (fixes silent DTMF loss)#11

Merged
smoghe-bw merged 1 commit into
mainfrom
fix/dtmf-telephone-event-clock-match
Jul 13, 2026
Merged

VAPI-3479 Match telephone-event clock rate to the primary audio codec (fixes silent DTMF loss)#11
smoghe-bw merged 1 commit into
mainfrom
fix/dtmf-telephone-event-clock-match

Conversation

@smoghe-bw

@smoghe-bw smoghe-bw commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Problem

DTMF sent from a WebRTC endpoint was silently lost (audio unaffected) on inbound-call-to-endpoint flows.

Root cause: RFC 4733 telephone-event must share the audio codec's RTP clock. When the publish offer forces Opus (codecPreferences {audio:[opus/48000]}), the old logic appended RTCRtpSender.getCapabilities().codecs.find(telephone-event) — the first telephone-event, often telephone-event/8000 — producing an offer of opus/48000 + telephone-event/8000.

Fix

Strip all telephone-event codecs and re-add only the one whose clockRate matches the primary (first) media codec:

  • Opus → telephone-event/48000
  • PCMU → telephone-event/8000

No 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 addStreamToPublishingPeerConnection tests (they previously codified the buggy opus/48000 + telephone-event/8000 pairing) to assert clock-matching across: appended-when-missing, replaces-mismatched, keeps-matched, omits-when-no-match, and no-explicit-prefs. 20/20 pass, tsc --noEmit clean.

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>
@smoghe-bw smoghe-bw requested review from a team as code owners July 13, 2026 14:30
@bwappsec

bwappsec commented Jul 13, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues
Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@smoghe-bw smoghe-bw changed the title Match telephone-event clock rate to the primary audio codec (fixes silent DTMF loss) VAPI-3479 Match telephone-event clock rate to the primary audio codec (fixes silent DTMF loss) Jul 13, 2026
@smoghe-bw smoghe-bw merged commit b485e77 into main Jul 13, 2026
5 checks passed
@smoghe-bw smoghe-bw deleted the fix/dtmf-telephone-event-clock-match branch July 13, 2026 14:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants