diff --git a/internal/playback/remux.go b/internal/playback/remux.go index 4fdd10b499..b2dd5ea712 100644 --- a/internal/playback/remux.go +++ b/internal/playback/remux.go @@ -153,7 +153,7 @@ const ( // buildRemuxArgs constructs the ffmpeg argument list for a remux operation. // The args perform codec copy (-c copy) into the target container format, -// using fragmented output for streaming (frag_keyframe+delay_moov+default_base_moof) and +// using fragmented output for streaming (frag_keyframe+delay_moov) and // pipe:1 for stdout output. // When transcodeAudio is true, video is copied but audio is transcoded to // stereo AAC (handles cases like DTS/TrueHD that browsers cannot decode). @@ -266,7 +266,14 @@ func buildRemuxArgsWithAudioV3(filePath, outputFormat string, seekSeconds float6 // delay_moov lets the MP4 muxer inspect the first audio packet before // writing codec configuration. empty_moov fails immediately for copied // E-AC-3/Atmos tracks because their frame size is not known at header time. - "-movflags", "frag_keyframe+delay_moov+default_base_moof", + // + // No default_base_moof: this is one progressive byte stream, not MSE/CMAF + // segments, so explicit tfhd base data offsets are correct and every + // progressive demuxer reads them. Samsung Tizen's native player does not + // follow moof-relative offsets: it plays the first fragment, then stalls + // with no audio (verified on a QN700B, Tizen 6.5, with the same copied + // HEVC + AAC 5.1 stream with and without the flag). + "-movflags", "frag_keyframe+delay_moov", "pipe:1", ) @@ -276,7 +283,7 @@ func buildRemuxArgsWithAudioV3(filePath, outputFormat string, seekSeconds float6 // StartRemux starts an ffmpeg process that copies codecs to a new container. // When transcodeAudio is false the command is: // -// ffmpeg -i {input} -c copy -f {format} -movflags frag_keyframe+delay_moov+default_base_moof pipe:1 +// ffmpeg -i {input} -c copy -f {format} -movflags frag_keyframe+delay_moov pipe:1 // // When transcodeAudio is true video is copied but audio is transcoded to AAC. // The caller must call Close() when done to clean up resources. diff --git a/internal/playback/remux_dv_test.go b/internal/playback/remux_dv_test.go index 9c9537b958..635f347037 100644 --- a/internal/playback/remux_dv_test.go +++ b/internal/playback/remux_dv_test.go @@ -208,11 +208,20 @@ func TestStartRemuxRejectsUnknownModeForAllProfiles(t *testing.T) { func TestBuildRemuxArgsDelaysMoovForCopiedAtmosConfiguration(t *testing.T) { args := buildRemuxArgs("/x.mkv", "mp4", 0, false, -1, 8, false, false) - if !argsContainPair(args, "-movflags", "frag_keyframe+delay_moov+default_base_moof") { + if !argsContainPair(args, "-movflags", "frag_keyframe+delay_moov") { t.Fatalf("remux must delay moov until copied audio is parsed, args=%v", strings.Join(args, " ")) } } +// Tizen's native player stalls after the first fragment when tfhd offsets are +// moof-relative, so the progressive stream must not set default_base_moof. +func TestBuildRemuxArgsOmitsDefaultBaseMoof(t *testing.T) { + args := buildRemuxArgs("/x.mkv", "mp4", 0, true, -1, 8, false, false) + if strings.Contains(strings.Join(args, " "), "default_base_moof") { + t.Fatalf("progressive remux must not use default_base_moof, args=%v", strings.Join(args, " ")) + } +} + // writeProbeAwareFFmpeg stands in for an ffmpeg that carries the dovi_rpu // filter but cannot parse this source's RPU: it advertises the filter, fails // the probe the way the real one does (rejecting packets while exiting 0), and diff --git a/scripts/prairie-invariants.txt b/scripts/prairie-invariants.txt index 73aba7a74e..ebaccd8e16 100644 --- a/scripts/prairie-invariants.txt +++ b/scripts/prairie-invariants.txt @@ -42,3 +42,6 @@ web/src/api/client.ts 1 X-Prairie- client identity headers web/src/player/components/PlaybackInfoOverlay.tsx 1 Recent events web stats overlay shows the player event log web/src/player/components/VideoPlayer.tsx 1 new PlaybackEventLog\( web player records events for the stats overlay internal/api/handlers/stream.go 1 userID == 0 && !apimw\.IsStreamTokenAuthorized\( v2 progressive/direct delivery honors st (Tizen direct play) + +# --- Progressive remux that Samsung Tizen native players can follow. +internal/playback/remux.go 1 "-movflags", "frag_keyframe\+delay_moov", no default_base_moof: Tizen stalls after the first fragment (Iron Man, QN700B)