diff --git a/services/discord-gateway/src/goLive/Demuxer.ts b/services/discord-gateway/src/goLive/Demuxer.ts index 6e94dd9..22f76fd 100644 --- a/services/discord-gateway/src/goLive/Demuxer.ts +++ b/services/discord-gateway/src/goLive/Demuxer.ts @@ -173,6 +173,9 @@ export async function demux( const proc = spawn(FFMPEG, args, { stdio: isStream ? ["pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"], }); + console.log( + `[goLive:Demuxer] spawn ffmpeg pid=${proc.pid} input=${isStream ? "stream" : input} args=${args.join(" ")}`, + ); // Pipe live input straight into ffmpeg stdin — never await stream end. if (isStream && proc.stdin) { @@ -199,7 +202,14 @@ export async function demux( let stderrBuf = ""; if (proc.stderr) { proc.stderr.on("data", (d: Buffer) => { - stderrBuf = (stderrBuf + d.toString()).slice(-16384); + const text = d.toString(); + stderrBuf = (stderrBuf + text).slice(-16384); + // Surface actionable lines: ffmpeg errors + stream init lines + if (/error|invalid|no such|failed|cannot|not found|unable/i.test(text)) { + console.log( + `[goLive:Demuxer] ffmpeg stderr: ${text.trim().split("\n").slice(0, 4).join(" | ")}`, + ); + } if (parsedMeta) return; const streamRe = /Stream #0:(\d+): (Video|Audio): ([^,]+)/g; let m: RegExpExecArray | null; @@ -284,6 +294,11 @@ export async function demux( free: () => {}, }); frameCount++; + if (frameCount === 1 || frameCount % 30 === 0) { + console.log( + `[goLive:Demuxer] frames=${frameCount} last=${nal.length}B key=${isKeyFrame}`, + ); + } }; if (proc.stdout) { diff --git a/services/discord-gateway/src/goLive/WebRtcWrapper.ts b/services/discord-gateway/src/goLive/WebRtcWrapper.ts index 012346d..aa49540 100644 --- a/services/discord-gateway/src/goLive/WebRtcWrapper.ts +++ b/services/discord-gateway/src/goLive/WebRtcWrapper.ts @@ -68,6 +68,7 @@ export class WebRtcConnWrapper { private _audioTrack: NativeTrack | null = null; private _videoTrack: NativeTrack | null = null; private _videoCodec: WebRtcVideoCodec | null = null; + private _videoFrameLog = 0; /** Assigned by BaseMediaConnection to send the gathered SDP to Discord. */ onLocalDescription: ((sdp: string) => void) | null = null; @@ -114,7 +115,15 @@ export class WebRtcConnWrapper { } sendVideoFrame(frame: Buffer, frametime: number): void { - if (!this.ready || !this._videoTrack) return; + if (!this.ready || !this._videoTrack) { + if (this._videoFrameLog === 0) { + console.log( + `[goLive:WebRtc] sendVideoFrame DROPPED ready=${this.ready} track=${this._videoTrack !== null}`, + ); + this._videoFrameLog++; + } + return; + } const clockRate = CodecPayloadType[this._videoCodec ?? "H264"].clockRate; if (this._videoCodec === "H264") { let spsRewritten = false; @@ -157,6 +166,12 @@ export class WebRtcConnWrapper { } this._videoTrack.sendFrame(frame); this._videoTrack.addTimestamp(Math.round((frametime * clockRate) / 1000)); + this._videoFrameLog++; + if (this._videoFrameLog === 1 || this._videoFrameLog % 30 === 0) { + console.log( + `[goLive:WebRtc] sendVideoFrame #${this._videoFrameLog} bytes=${frame.length} ready=${this.ready}`, + ); + } } setPacketizer(videoCodec: string): void { diff --git a/services/discord-gateway/src/goLive/prepareStream.ts b/services/discord-gateway/src/goLive/prepareStream.ts index 41a50ee..e32e790 100644 --- a/services/discord-gateway/src/goLive/prepareStream.ts +++ b/services/discord-gateway/src/goLive/prepareStream.ts @@ -262,15 +262,22 @@ export async function playStream( options: PlayStreamOptions = {}, ): Promise { const conn = await streamer.createStream(); + console.log("[goLive:playStream] createStream resolved"); const { video, close: demuxClose } = await demux(prepared.output, { format: options.format ?? "nut", }); + console.log( + `[goLive:playStream] demux done codec=${video?.codecName ?? "?"} ${video?.width ?? 0}x${video?.height ?? 0} fps=${video ? video.framerate_num / video.framerate_den || 30 : 30}`, + ); if (!video) throw new Error("No video stream in media"); conn.setPacketizer(video.codecName); conn.mediaConnection.setSpeaking(true); + console.log( + `[goLive:playStream] setPacketizer(${video.codecName}) + setSpeaking done`, + ); const w = typeof options.width === "function"