From e529bdc40b958a3f1f58e218efa6c5016c68a2ec Mon Sep 17 00:00:00 2001 From: Eli Mallon Date: Tue, 13 Oct 2026 21:26:37 -0700 Subject: [PATCH] Track WebRTC stall progress per inbound stream With stems the player receives several audio streams, and the stall check kept one shared last-packet timestamp for all of them. Once the stream froze, their different final timestamps overwrote it on every poll, so it never looked stuck and never reconnected. Keep the last progress per stats id. --- .../components/mobile-player/use-webrtc.tsx | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/js/components/src/components/mobile-player/use-webrtc.tsx b/js/components/src/components/mobile-player/use-webrtc.tsx index 5a4ae8c6c..f8d4dad97 100644 --- a/js/components/src/components/mobile-player/use-webrtc.tsx +++ b/js/components/src/components/mobile-player/use-webrtc.tsx @@ -100,31 +100,33 @@ export default function useWebRTC( ); }); - let lastFramesReceived = 0; - let lastAudioFramesReceived = 0; + // The last progress seen on each inbound stream, by stats id: with + // several audio tracks, one shared value flips between them forever. + const lastProgress = new Map(); const handle = setInterval(async () => { const stats = await peerConnection.getStats(); stats.forEach((stat) => { - const mediaType = stat.mediaType /* web */ ?? stat.kind; /* native */ - if (stat.type === "inbound-rtp" && mediaType === "audio") { - const audioFramesReceived = stat.lastPacketReceivedTimestamp; - if (lastAudioFramesReceived !== audioFramesReceived) { - lastAudioFramesReceived = audioFramesReceived; - lastChange.current = Date.now(); - setStatus(PlayerStatus.PLAYING); - setStuck(false); - } + if (stat.type !== "inbound-rtp") { + return; } - if (stat.type === "inbound-rtp" && mediaType === "video") { - const framesReceived = stat.framesReceived; - if (lastFramesReceived !== framesReceived) { - lastFramesReceived = framesReceived; - lastChange.current = Date.now(); - setStatus(PlayerStatus.PLAYING); - setStuck(false); - } + const mediaType = stat.mediaType /* web */ ?? stat.kind; /* native */ + const progress = + mediaType === "audio" + ? stat.lastPacketReceivedTimestamp + : mediaType === "video" + ? stat.framesReceived + : undefined; + if ( + progress === undefined || + (lastProgress.get(stat.id) ?? 0) === progress + ) { + return; } + lastProgress.set(stat.id, progress); + lastChange.current = Date.now(); + setStatus(PlayerStatus.PLAYING); + setStuck(false); }); if (Date.now() - lastChange.current > 2000) { setStuck(true); -- knotmirror 0.0.1