diff --git a/internal/transport/vp8channel/transport.go b/internal/transport/vp8channel/transport.go index ee1718d..86d06ff 100644 --- a/internal/transport/vp8channel/transport.go +++ b/internal/transport/vp8channel/transport.go @@ -680,19 +680,11 @@ func (p *streamTransport) handleIncomingFrame(frame []byte) { if !p.hadPeer.Swap(true) { p.handleFirstPeer(peerEpoch) } else if prev := p.peerEpoch.Load(); prev != peerEpoch { - // Peer restarted its KCP session. Reset ours so the conv state - // machines re-converge. CAS guards against double-reset when - // fragmented frames straddle the epoch boundary. - if p.peerEpoch.CompareAndSwap(prev, peerEpoch) { - p.resetKCP() - p.reconnectMu.Lock() - fn := p.reconnectFn - p.reconnectMu.Unlock() - if fn != nil { - fn() - } - } - // Drop this packet: it predates our fresh KCP session. + // In a multi-participant room, other clients also publish VP8 + // tracks. Their epochs differ from our latched peer (the server). + // Simply ignore frames that don't match our peer — they belong to + // other participants we don't communicate with. + logger.Debugf("vp8channel: ignoring frame from unknown epoch=0x%08x (latched=0x%08x)", peerEpoch, prev) return } diff --git a/internal/transport/vp8channel/transport_unit_test.go b/internal/transport/vp8channel/transport_unit_test.go index 920d787..b1a328a 100644 --- a/internal/transport/vp8channel/transport_unit_test.go +++ b/internal/transport/vp8channel/transport_unit_test.go @@ -374,8 +374,13 @@ func TestHandleIncomingFrameEpochFilteringAndReconnect(t *testing.T) { t.Fatalf("stream reconnect did not reset/callback: reconnected=%v kcp=%v", reconnected, tr.kcp) } reconnected = false - tr.handleIncomingFrame(mkFrame(tr.bindingToken, 2, []byte("after-restart"))) - if !reconnected || tr.peerEpoch.Load() != 2 || tr.kcp == nil { - t.Fatalf("epoch change did not reset/reconnect: reconnected=%v epoch=%d kcp=%v", reconnected, tr.peerEpoch.Load(), tr.kcp) //nolint:lll // long test description + // In single-peer mode, frames from a different epoch are ignored (other + // participants in the room). The client does NOT reconnect. + tr.handleIncomingFrame(mkFrame(tr.bindingToken, 2, []byte("other-participant"))) + if reconnected { + t.Fatal("epoch change from another participant should not trigger reconnect") + } + if tr.peerEpoch.Load() != 1 { + t.Fatalf("peer epoch changed unexpectedly: got %d want 1", tr.peerEpoch.Load()) } }