From 407e0033993a7ecd75adcccaccb78e39d84c97a3 Mon Sep 17 00:00:00 2001 From: asepharyana Date: Wed, 12 Aug 2026 00:25:01 +0700 Subject: [PATCH] =?UTF-8?q?fix(goLive):=20black=20screen=20root=20cause=20?= =?UTF-8?q?=E2=80=94=20h264=20muxer=20can't=20carry=20audio;=20disable=20s?= =?UTF-8?q?elf=5Fvideo=20camera?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ROOT CAUSE of empty GoLive tile (finally): prepareStream ran with includeAudio: true + output -f h264. The h264 muxer cannot mux audio ('h264 muxer does not support any stream of type audio') → header write fails -22 → stdout empty → Demuxer ffmpeg 'Invalid data found when processing input' → 0 frames → black tile. Reproduced locally end-to-end (13s backpressure delay + prepareStream + demux). Fixes: - screenShareController: includeAudio: false (video-only GoLive; demux path never delivers audio anyway) - Demuxer: pin input format -f h264 for stream inputs (raw AnnexB H264 has no magic header → auto-detect unreliable on delayed pipes) - Streamer.signalStream: self_video: false — stop flipping on the bot's camera in Discord (user request; screen share ≠ camera) Verified: local repro now emits 644 frames 1280x720 (was 0); tsc/biome/ vitest all green. --- services/discord-gateway/src/goLive/Demuxer.ts | 5 +++++ services/discord-gateway/src/goLive/Streamer.ts | 9 ++++----- .../src/modules/voice-recording/screenShareController.ts | 6 +++++- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/services/discord-gateway/src/goLive/Demuxer.ts b/services/discord-gateway/src/goLive/Demuxer.ts index 22f76fd..a2b5c9d 100644 --- a/services/discord-gateway/src/goLive/Demuxer.ts +++ b/services/discord-gateway/src/goLive/Demuxer.ts @@ -161,6 +161,11 @@ export async function demux( // stderr and are parsed for dimensions/fps. "-loglevel", "info", + // Input format hint: prepareStream always emits raw AnnexB H264 on + // pipe:0. Raw H264 has NO magic header, so ffmpeg's auto-detection + // fails with "Invalid data found when processing input" whenever the + // first bytes arrive late/buffered. Pin the demuxer input format. + ...(isStream ? ["-f", "h264"] : []), "-i", isStream ? "pipe:0" : input, "-c:v", diff --git a/services/discord-gateway/src/goLive/Streamer.ts b/services/discord-gateway/src/goLive/Streamer.ts index 844642e..d6779cb 100644 --- a/services/discord-gateway/src/goLive/Streamer.ts +++ b/services/discord-gateway/src/goLive/Streamer.ts @@ -302,16 +302,15 @@ export class Streamer { channelId: channel_id, botId: user_id, } = this.voiceConnection; - // Mimic the real Discord client when starting Go Live: flip the voice - // state to video-enabled (and un-deafen) BEFORE requesting the stream. - // Discord's gateway silently ignores STREAM_CREATE while the user's - // voice state still has self_video: false. + // Un-deafen before requesting the stream (mimic real client). Do NOT + // set self_video: true — that flips on the bot's camera in Discord + // (visible to everyone); screen share should not enable the camera. this.sendOpcode(GatewayOpCodes.VOICE_STATE_UPDATE, { guild_id, channel_id, self_mute: false, self_deaf: false, - self_video: true, + self_video: false, }); this.sendOpcode(GatewayOpCodes.STREAM_CREATE, { type, diff --git a/services/discord-gateway/src/modules/voice-recording/screenShareController.ts b/services/discord-gateway/src/modules/voice-recording/screenShareController.ts index 2916aad..62bbada 100644 --- a/services/discord-gateway/src/modules/voice-recording/screenShareController.ts +++ b/services/discord-gateway/src/modules/voice-recording/screenShareController.ts @@ -105,7 +105,11 @@ export class ScreenShareController { frameRate: 30, bitrateVideo: 2500, bitrateVideoMax: 4000, - includeAudio: true, + // Video-only GoLive: the -f h264 output muxer cannot carry audio + // ("h264 muxer does not support any stream of type audio" → header + // write fails → empty stdout → demux 'Invalid data' → black tile). + // Audio is not delivered by the GoLive demux path anyway. + includeAudio: false, videoCodec: normalizeVideoCodec("H264"), }); const { command } = prepared;