From 11f2ad5f23360f811ceddd9e34e6d17afffc9012 Mon Sep 17 00:00:00 2001 From: asepharyana Date: Thu, 13 Aug 2026 18:34:28 +0700 Subject: [PATCH] =?UTF-8?q?fix(goLive):=20kill=204.3s=20backlog=20?= =?UTF-8?q?=E2=80=94=20HWM2=20pipes=20+=20wire=20A/V=20sync=20(dank-faithf?= =?UTF-8?q?ul)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lag root cause: vPipe/aPipe were objectMode PassThrough HWM 128 → the pipe held up to 128 frames ≈ 4.3s of video before backpressure reached the encoder. The viewer was watching a 4+ second stale backlog. Fixes (both faithful to @dank074/discord-video-stream): 1. vPipe/aPipe HWM 2 — at most ~1-2 frames in flight (~66ms @ 30fps), so the writeFrame() backpressure pauses ffmpeg stdout almost immediately and the whole chain (encoder → NUT → demuxer → vPipe → BaseMediaStream → WebRTC) runs at the sender's real pace, exactly like dank's 'resume &&= vPipe.write'. 2. Wire vStream.syncStream = aStream — audio is the master clock; video sleeps/wakes on ptsDelta like upstream newApi.js. Prevents A/V drift under variable encoder throughput. --- services/discord-gateway/src/goLive/Demuxer.ts | 10 ++++++++-- services/discord-gateway/src/goLive/prepareStream.ts | 4 ++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/services/discord-gateway/src/goLive/Demuxer.ts b/services/discord-gateway/src/goLive/Demuxer.ts index e8b336d..f9d973b 100644 --- a/services/discord-gateway/src/goLive/Demuxer.ts +++ b/services/discord-gateway/src/goLive/Demuxer.ts @@ -155,8 +155,14 @@ export async function demux( audio: DemuxedStream | undefined; close: () => void; }> { - const vPipe = new PassThrough({ objectMode: true, highWaterMark: 128 }); - const aPipe = new PassThrough({ objectMode: true, highWaterMark: 128 }); + // objectMode pipes carrying one frame per item. HWM 2 keeps backpressure + // near-instant: at most ~1-2 frames in flight (~66ms @ 30fps) before the + // encoder is throttled, so the viewer sees near-live video instead of a + // multi-second backlog (the old HWM 128 held 128 frames ≈ 4.3s of lag). + // BaseMediaStream below has HWM 0, so the chain is tightly coupled to the + // WebRTC sender's real pace — faithful to @dank074/discord-video-stream. + const vPipe = new PassThrough({ objectMode: true, highWaterMark: 2 }); + const aPipe = new PassThrough({ objectMode: true, highWaterMark: 2 }); const isStream = typeof input !== "string"; // NUT/matroska input (prepareStream with includeAudio) carries audio; the diff --git a/services/discord-gateway/src/goLive/prepareStream.ts b/services/discord-gateway/src/goLive/prepareStream.ts index 15e426c..ba8ef50 100644 --- a/services/discord-gateway/src/goLive/prepareStream.ts +++ b/services/discord-gateway/src/goLive/prepareStream.ts @@ -369,6 +369,10 @@ export async function playStream( console.log( `[goLive:playStream] audio stream attached (${audio.codecName})`, ); + // A/V sync (faithful to @dank074 newApi.js): audio is the master clock. + // Video sleeps/wakes based on ptsDelta(video - audio) so they can't drift + // apart under variable encoder throughput. + vStream.syncStream = aStream; } const cleanup = () => {