fix(playout): video-only HLS preview (broken audio time_base was the black-screen cause)
Definitive root cause of the black preview, found via server-side ffmpeg decode of the live playlist: Error while decoding stream #0:1: Invalid data found (x57) [abuffer] Value inf for parameter 'time_base' ... time_base to value 1/0 Stream #0:1 is the AAC audio. CasparCG's real-time channel feeds the HLS consumer an audio stream whose muxed time_base is 1/0 (infinity). ffmpeg itself cannot decode the playlist, and hls.js silently fails to append the fragment after demux, so the <video> stays at readyState 0 (black) even though the video PTS is perfectly continuous and segments serve 200. Fix: drop audio from the HLS confidence monitor (-an). The video track is clean h264 and plays in hls.js. Program audio still rides the primary SRT/RTMP/SDI/NDI output, which is unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
87d988810f
commit
426273129d
1 changed files with 17 additions and 11 deletions
|
|
@ -169,16 +169,23 @@ export class PlayoutManager {
|
|||
// reloads forever ("sliding 0.00 / prev-sn na / MISSED") and never appends
|
||||
// a fragment, so the preview stays black.
|
||||
//
|
||||
// Fix: force a constant output frame rate (-r 30000/1001 = 29.97 CFR). This
|
||||
// regenerates uniform PTS and — critically — makes the frame-based GOP land
|
||||
// on regular time boundaries: at 29.97 fps a 60-frame GOP (-g 60) is exactly
|
||||
// 2.0s, so every keyframe (and therefore every HLS split at -hls_time 2) is
|
||||
// a clean 2.0s boundary. The original used -g 60 WITHOUT -r, so "60 frames"
|
||||
// varied with the channel's irregular feed rate and segments drifted.
|
||||
// The HLS preview is a VIDEO-ONLY confidence monitor. We deliberately drop
|
||||
// audio (-an).
|
||||
//
|
||||
// NOTE: -force_key_frames does NOT work here — CasparCG's FFMPEG consumer
|
||||
// routes args to the muxer, not the encoder, and logs it as "Unused option".
|
||||
// The CFR rate + frame GOP is the combination that actually takes effect.
|
||||
// Why: CasparCG's real-time channel feeds the FFMPEG consumer an audio
|
||||
// stream whose muxed time_base comes out as 1/0 (infinity). ffmpeg itself
|
||||
// can't decode the resulting playlist ("Invalid data ... abuffer: Value inf
|
||||
// for parameter 'time_base'"), and hls.js silently fails to append the
|
||||
// fragment after demux — the video element sits at readyState 0 and the
|
||||
// preview stays black. Dropping audio removes the broken stream entirely;
|
||||
// the remaining video track is clean h264 and plays in hls.js. A confidence
|
||||
// monitor doesn't need audio — the real program audio rides the primary
|
||||
// SRT/RTMP/SDI/NDI output, which is unaffected.
|
||||
//
|
||||
// NOTE: encoder options like -g / -r / -force_key_frames are NOT honored
|
||||
// here — CasparCG's FFMPEG consumer applies args to the muxer, not the
|
||||
// encoder (it logs "Unused option"). Segment cadence follows the channel's
|
||||
// own keyframes; that's fine for a video-only preview.
|
||||
const out = `${HLS_DIR}/index.m3u8`;
|
||||
const args = [
|
||||
`FILE "${out}"`,
|
||||
|
|
@ -187,8 +194,7 @@ export class PlayoutManager {
|
|||
'-hls_list_size 8',
|
||||
'-hls_flags delete_segments+append_list+independent_segments',
|
||||
'-codec:v libx264 -preset:v veryfast -tune:v zerolatency -b:v 800k -maxrate 1M -bufsize 2M',
|
||||
'-r 30000/1001 -g 60 -keyint_min 60 -sc_threshold 0',
|
||||
'-codec:a aac -b:a 96k -ar 48000',
|
||||
'-an',
|
||||
'-filter:v format=yuv420p',
|
||||
].join(' ');
|
||||
await this.amcp.send(`ADD ${CHANNEL} ${args}`);
|
||||
|
|
|
|||
Loading…
Reference in a new issue