WebRTC

WebRTC No Audio & One-Way Audio: FAQ for React, SIP.js and PBX Teams

Use a fixed diagnostic order—signaling, SDP, ICE, RTP counters, decoded energy, playback—before changing NAT, TURN or dialplan on production.

FreeSWITCHAsteriskFusionPBXWebRTCSIPPBX
WebRTC No Audio & One-Way Audio: FAQ for React, SIP.js and PBX Teams
WebRTC No Audio & One-Way Audio: FAQ for React, SIP.js and PBX Teams

Scenario: The agent sees “call connected” in your React softphone. SIP registration is green. CDR shows ANSWERED. Neither side hears audio—or only one side does. The obvious fix (open more firewall ports, change NAT, rewrite the dialplan) may be wrong and can make production worse.

Direct answer

Work in a fixed order: registration/signaling → SIP/SDP offer/answer → ICE connectivity → DTLS-SRTP → inbound/outbound RTP counters → decoded audio energy → remote track attached and .play() → PBX/media path → far-end capture/device.

If packetsReceived rises but audioLevel / totalAudioEnergy stay near zero, you usually have media arriving without usable decode (codec/payload, one-way RTP, far-end mute)—not only a React srcObject bug. Do not change NAT or TURN until stats and SDP support it.

Evidence path: React SIP / WebRTC Diagnostic Lab · case study · WebRTC development for production fixes.

Symptom matrix

What users reportOften meansFirst checks
No audio both waysICE failure, SRTP mismatch, or both legs one-wayICE state, DTLS, RTP on both directions
One-way audioAsymmetric RTP/NAT, wrong codec on one leg, muted far-endCompare inbound vs outbound stats per peer
Delayed audio then OKICE gathering, late candidate, bufferTimestamps on first RTP vs answer
Audio drops after holdRe-INVITE/SDP, track replaced, autoplay policyHold/resume SDP diff, track muted
Connect then instant hangup488/606 codec, auth, or media timeoutSIP response code, PBX hangup cause

Troubleshooting decision tree

Call "connected" in UI?
├─ NO → SIP signaling (401/403/408), WSS cert, registrar reachability
└─ YES → Remote audio element playing? (not muted, not zero volume)
    ├─ NO → srcObject, autoplay policy, user gesture, detached track
    └─ YES → getStats(): packetsReceived increasing on inbound-rtp?
        ├─ NO → ICE failed / no route / firewall UDP / need TURN
        └─ YES → audioLevel or totalAudioEnergy rising?
            ├─ NO → codec/payload, PT mismatch, far-end not sending speech, encrypt mismatch
            └─ YES → check outbound leg symmetrically; if one-way, NAT/RTP symmetric routing on PBX

What each step proves: registration proves signaling only; RTP counters prove packets reached the browser; energy proves decode produced speech. None of these alone proves the far-end user’s microphone works.

Diagnostic order (technical)

  1. Registration — 200 OK on REGISTER does not validate media.
  2. SDP — codec list, m=audio port, a=rtcp-mux, DTLS fingerprint alignment between offer/answer.
  3. ICE — connected vs failed; host/srflx/relay candidates; whether relay appears when symmetric NAT is suspected.
  4. DTLS-SRTP — handshake errors show as failed transports before RTP counts move.
  5. RTP — bytesReceived, packetsReceived, packetsLost, jitter; compare A→B and B→A.
  6. Decoded audio — audioLevel, totalAudioEnergy (Chrome); sustained near-zero with rising packets → investigate codec/far-end.
  7. Playback — HTMLMediaElement, setSinkId, tab focus, corporate policies blocking autoplay.

Reading getStats() (illustrative)

On a healthy inbound audio track you typically see increasing packetsReceived and non-trivial totalAudioEnergy while someone speaks on the far end. Synthetic example for education only:

inbound-rtp (audio):
  packetsReceived: 1842
  bytesReceived: 312400
  audioLevel: 0.12
  totalAudioEnergy: 4.8

If packetsReceived climbs above ~500 in a 20s test call but totalAudioEnergy stays < 0.01, suspect payload/codec or silent far-end—not “React is broken” by default.

Use chrome://webrtc-internals (or Firefox about:webrtc) to export the same era as your SIP trace. Align timestamps with FreeSWITCH sofia or Asterisk pjsip logs.

React/SIP.js vs PBX vs network

LayerSymptom hintEvidence
React UITrack never attached, play() rejectedConsole, remoteAudio.srcObject, autoplay
SIP.jsWrong session description handler, early BYESIP message log, SDP diff
FusionPBX/FreeSWITCHOne leg no RTP in show channelsrtp_audio_in/out, codec in channel vars
NAT/TURNBoth legs fail off-LANICE relay usage, symmetric RTP settings

A minimal SIP.js baseline on the same PC and network as production is the fastest A/B test. The diagnostic lab is that baseline packaged for engagement; integrating fixes into your product is scoped separately.

When to touch Sofia, NAT and TURN

Inspect PBX RTP when browser stats show no inbound packets despite connected ICE—check external RTP IP, NDLB, firewall pinholes, and whether WebRTC profile matches TLS/WSS path. Add TURN when candidates stay srflx but media is asymmetric across VPN/corporate egress—not as the first knob on every ticket.

Deep server-side media notes: one-way audio in FreeSWITCH/FusionPBX.

Acceptance criteria (before you close the ticket)

  • Reproduced on defined browsers/networks (document VPN vs office).
  • Two-way speech confirmed with stats screenshot or exported internals and PBX leg counters.
  • Hold/resume and second call if in scope.
  • Written root cause: client, PBX config, or network—with what was not verified.

Regression checklist

  • Chrome + Firefox (+ Safari if in scope)
  • Office Wi‑Fi vs home vs VPN
  • Headset vs built-in mic (far-end and near-end)
  • After deploy: cache, service worker, old bundle still served

Fault-isolation matrix (summary)

EvidenceLikely layerUnlikely to be sole cause
ICE failedNetwork/TURN/firewallReact CSS
ICE connected, RTP zeroPBX not sending to browser IP:portUser volume slider
RTP high, energy zeroCodec/payload/far-end muteSTUN server brand
Energy OK, user hears nothingPlayback/autoplay/setSinkIdSIP REGISTER expiry
One leg onlyAsymmetric RTP/NATSingle global codec setting

Minimal SIP.js baseline (conceptual)

Run a stock SIP.js user agent with the same WSS URL, credentials and ICE servers as production—no Redux, no custom audio worklets. If baseline audio works, bisect production changes (track attachment, hold logic, third-party SDK). If baseline fails, bisect network and PBX with the same stats exports.

Connected path on this site

Troubleshooting article → diagnostic lab case study → lab estimator or WebRTC development for product integration.

Author note: Methodology comes from production rescues; browser and PBX versions in your environment may differ—validate on your builds.

Limitations: Stats field names vary by browser version; we document tested Chromium-era fields. Production packet capture may need customer approval.

Next step: Diagnose my WebRTC audio issue · Production rescue if the PBX path is on fire

Questions this article answers

REGISTER completes on the signaling path; RTP/SRTP and playback are separate. A green registration indicator is not an audio health check.

If inbound-rtp packets increase but audioLevel stays near zero, suspect codec/payload or far-end capture; if packets do not increase, suspect ICE/NAT/PBX media before blaming UI state.

Packets prove delivery to the browser; audioLevel/totalAudioEnergy prove decoded speech energy—they answer different questions.

When ICE fails across symmetric NAT, VPN or corporate egress and host/srflx candidates cannot carry media—after stats show connected ICE without RTP, not as the first guess.

If answer omits the offered codec or payload type disagrees, you may see connected state with garbage or zero decode—compare SDP and PBX codec allow lists.

VPN changes routing and UDP behavior; split tunneling and firewall rules often block RTP unless TURN relay is used.

Compare inbound vs outbound stats on both browsers and RTP counters on the channel—one-way usually asymmetry, not a single missing React prop.

webrtc-internals or getStats export, SIP/SDP capture, and PBX leg counters aligned in time—change NAT only when evidence points to blocked RTP.

Need evidence before changing production?

Start with diagnostic triage or a fixed-scope investigation on the WebRTC lab product page.

Configure & quote →