From 2eb363fbbf6eb6ed07ba6c6fd216bba239b754e4 Mon Sep 17 00:00:00 2001 From: Andrei Misecichin Date: Wed, 22 Jul 2026 17:09:33 +0300 Subject: [PATCH] fix(webrtc): reuse existing Receiver when remote switches payload type mid-session Some WebRTC producers (observed with the Nest/SDM source) start sending media on a different payload type than the one negotiated at connection setup, without a full renegotiation. Since Conn.GetTrack deduplicates Receivers by exact *Codec pointer identity, a payload-type change for the same Media creates a brand-new, unwired Receiver instead of reusing the existing one - permanently starving every already-connected consumer (RTSP/WebRTC senders) of video, even though the producer is actively receiving valid frames. Reproduced against a live Nest doorbell (WEB_RTC protocol): go2rtc negotiates H264 on payload type 96, then the Nest media server begins transmitting on payload type 98 a few seconds into the session. Before this fix, /api/streams shows two H264 receivers - the wired one stuck at 0 bytes forever, and an orphan one accumulating real data with no consumer attached. ffmpeg consuming the RTSP restream fails with: Could not find codec parameters for stream 0 (Video: h264, none): unspecified size After this fix, there is a single Receiver correctly wired to the consumer, and ffmpeg reports a valid stream (H264 High profile, 384x512 @ 30fps) with megabytes of real video flowing through. Reproduced and fix verified on v1.9.0 and v1.9.10; the bug is present in both, so this isn't a version-specific regression. --- pkg/webrtc/producer.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pkg/webrtc/producer.go b/pkg/webrtc/producer.go index 32e958ee5..c6111b3f3 100644 --- a/pkg/webrtc/producer.go +++ b/pkg/webrtc/producer.go @@ -34,6 +34,22 @@ func (c *Conn) GetTrack(media *core.Media, codec *core.Codec) (*core.Receiver, e // Passive producers: OBS Studio via WHIP or Browser // Active producers: go2rtc as WebRTC client or WebTorrent + // Some remote peers (observed with Nest/SDM WebRTC) start sending + // media on a different payload type than the one originally wired + // to consumers (e.g. switch from PT 96 to PT 98 mid-session for the + // same video media, without a renegotiation). Since each payload + // type is a distinct *Codec pointer, the exact-codec lookup above + // misses and a brand-new, unwired Receiver would otherwise be + // created for it, silently starving every already-connected + // consumer. Reuse the existing Receiver for this Media instead, so + // already-wired Senders keep receiving packets regardless of which + // declared payload type the remote actually transmits on. + for _, track := range c.Receivers { + if track.Media == media { + return track, nil + } + } + default: panic(core.Caller()) }