fix(goLive): black screen root cause — h264 muxer can't carry audio; disable self_video camera

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.
This commit is contained in:
asepharyana
2026-08-12 00:25:01 +07:00
parent 968a43b0f4
commit 407e003399
3 changed files with 14 additions and 6 deletions
@@ -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",
@@ -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,
@@ -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;