From 161b8bc4012c943807bcaf22f6b3139ecaa9edbd Mon Sep 17 00:00:00 2001 From: Hoan HL Date: Sun, 28 Jun 2026 17:55:24 +0700 Subject: [PATCH] fix SDES issue for prodigal --- lib/call-session.js | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/lib/call-session.js b/lib/call-session.js index cd41398..9d39f85 100644 --- a/lib/call-session.js +++ b/lib/call-session.js @@ -549,6 +549,46 @@ class CallSession extends Emitter { localSdpA: async(sdp, res) => { this.rtpEngineOpts.uac.tag = res.getParsedHeader('To').params.tag; + /* HACK (rtcp-mux): some far ends answer an m-line WITHOUT a=rtcp-mux even though + we offered it, advertising a separate RTCP port + a 2nd ICE component they never + service. That dead RTCP component blocks ICE completion and therefore DTLS on that + stream (no SRTP keys -> "no crypto suite negotiated", no audio). Force rtcp-mux back + on every audio m-line and drop the standalone a=rtcp port / component-2 candidates + so rtpengine keeps a single muxed component. */ + const forceRtcpMux = (sdpStr) => { + if (!sdpStr || !sdpStr.includes('m=')) return sdpStr; + const eol = sdpStr.includes('\r\n') ? '\r\n' : '\n'; + const lines = sdpStr.split(/\r?\n/); + const result = []; + let section = []; + let isAudio = false; + const flush = () => { + if (!section.length) return; + if (isAudio) { + const hasMux = section.some((l) => l.startsWith('a=rtcp-mux')); + let kept = section.filter((l) => { + if (/^a=rtcp:\d+/.test(l)) return false; // separate RTCP port + const m = /^a=candidate:\S+\s+(\d+)\s+/.exec(l); // component 2 == RTCP + if (m && m[1] === '2') return false; + return true; + }); + if (!hasMux) kept = [kept[0], 'a=rtcp-mux', ...kept.slice(1)]; + result.push(...kept); + } else { + result.push(...section); + } + section = []; + }; + for (const l of lines) { + if (l.startsWith('m=')) { flush(); section = [l]; isAudio = l.startsWith('m=audio'); } + else if (section.length) section.push(l); + else result.push(l); + } + flush(); + return result.join(eol); + }; + sdp = forceRtcpMux(sdp); + const opts = { ...this.rtpEngineOpts.common, ...this.rtpEngineOpts.uas.mediaOpts,