movenc: Make hybrid_fragmented retain the fragmented form headers

This makes the final file truly hybrid: Externally the file
is a regular, non-fragmented file, but internally, the fragmented
form also exists un-overwritten.

To make any use of that, first, the fragments need to be muxed in
a position independent form, i.e. with empty_moov+default_base_moof
(or the dash or cmaf meta-flags).

Making use of the fragmented form when the file is finalized is
not entirely obvious though. One can dump the contents of the
single mdat box, and get the fragmented form. (This is a neat
trick, but not something that anybody really is expected to
want to do.)

The main expected use case is accessing fragments in the form of
byte range segments, for e.g. HLS.

Previously, the start of the file would look like this:

- ftyp
- free
- moov
 - (moov contents)

After finalizing the file, it would look like this:

- ftyp
- free
- mdat (previously moov)
 - (moov contents)

In this form, the size and type of the original moov box were
overwritten, and the original moov contents is just leftover
as unused data in the mdat box.

To avoid this issue, the start of the file now looks like this:

- ftyp
- free
- free
 - ftyp
- moov
 - (moov contents)

The second, hidden ftyp box inside mdat, would normally never be
seen.

After finalizing, the difference is that the mdat box now is
extended to cover the ftyp and the whole moov including its header
(and all the following fragments).

I.e., the start of the file looks like this:

- ftyp
- free
- mdat
 - ftyp
 - moov
  - (moov contents)

This allows accessing the "ftyp+moov" pair sequentially as such,
with a byte range - this range is untouched when finalizing,
producing the same ftyp+moov pair both while writing, when the
file is fragmented, and after finalizing, when the file is
transformed to non-fragmented externally.

Note; the sequential two "free+free" boxes may look slightly
silly; it could be tempting to make the second one an mdat
from the get-go. However, some players of fragmented mp4 (in
particular, Apple's HLS player) bail out if the initialization
segment contains an mdat box - therefore, use a free box.

It could also be possible to use just one single free box with
8 bytes of padding at the start - but that would require more
changes to the finalization logic.

For a segmenting user of the muxer, the only unclarity is how
to determine the right byte range for the internal ftyp+moov
pair. Currently, this requires parsing the muxer output and skip
past anything up to the start of the non-empty free box.
This commit is contained in:
Martin Storsjö
2025-06-27 15:12:32 +03:00
parent a06aee359b
commit 8313dc1120
3 changed files with 18 additions and 4 deletions

View File

@@ -92,7 +92,7 @@ fate-mov-frag-overlap: CMD = framemd5 -i $(TARGET_SAMPLES)/mov/frag_overlap.mp4
fate-mov-mp4-frag-flush: CMD = md5 -f lavfi -i color=blue,format=rgb24,trim=duration=0.04 -f lavfi -i anullsrc,aformat=s16,atrim=duration=2 -c:v png -c:a pcm_s16le -movflags +empty_moov+hybrid_fragmented -frag_duration 1000000 -frag_interleave 1 -bitexact -f mp4
fate-mov-mp4-frag-flush: CMP = oneline
fate-mov-mp4-frag-flush: REF = a1ac687d15552505f3c01a43285f32d0
fate-mov-mp4-frag-flush: REF = 48d833e4773f7542f65dadb446f8bf61
FATE_MOV_FFMPEG-$(call ALLYES, LAVFI_INDEV COLOR_FILTER FORMAT_FILTER TRIM_FILTER \
ANULLSRC_FILTER AFORMAT_FILTER ATRIM_FILTER \
WRAPPED_AVFRAME_DECODER PCM_S16LE_DECODER PCM_S16BE_DECODER \

View File

@@ -1,3 +1,3 @@
7f79311fa73287c093aa029f58b71476 *tests/data/lavf/lavf.mov_hybrid_frag
358436 tests/data/lavf/lavf.mov_hybrid_frag
b79a7ecf125aef1f97c8e9b7df7066a0 *tests/data/lavf/lavf.mov_hybrid_frag
358464 tests/data/lavf/lavf.mov_hybrid_frag
tests/data/lavf/lavf.mov_hybrid_frag CRC=0xbb2b949b