Eyevinn mp4ff v0.56.0 shipped on 22 August 2026. The Go MP4 box library now rejects out of range AVC and HEVC NAL fields that turned a few dozen bytes into multi gigabyte allocations, and hevc.ParseSliceHeader infers the Section 7.4.7.1 defaults that made x265 --no-deblock streams fail byte_alignment. The same tag adds QuickTime sound sample description versions 1 and 2, a WaveBox for the wave atom, and the labl Label box used for track labels in DASH-IF Ingest.
The full release notes and downloads are on the GitHub release page.
Parser allocation bounds on NAL and sample groups ¶
Four parser paths sized Go slices from the bitstream with no upper bound. A 7 byte malformed PPS NAL unit could allocate over 10 GiB through avc.ParsePPSNALUnit when num_slice_groups_minus1 was out of range. The spec max is 7 for any profile. A 33 byte malformed VPS NAL unit could churn 30 GiB of HRD parameter allocations in hevc.ParseVPSNALUnit via unbounded vps_num_hrd_parameters and vps_num_layer_sets_minus1. The valid range for the latter is 0 to 1023. avc.ParseSPSNALUnit now rejects num_ref_frames_in_pic_order_cnt_cycle outside 0 to 255 instead of sizing a slice from the raw field. The alst sample group entry no longer underflows its optional entry count when roll_count implies more bytes than description_length, which let a 32 byte sgpd box allocate over 4 GiB.
The notes say these cases were found by fuzzing. If a worker parses untrusted MP4 in the same process as other jobs, the allocation caps are the reason to pick up the tag. Garbage still fails. It no longer allocates a multi gigabyte slice first.
HEVC slice header inferences and large mdat offsets ¶
hevc.ParseSliceHeader used to leave absent slice header fields at the Go zero value. That is wrong when the bitstream omits a field and the spec says copy it from the PPS. The failure mode was concrete: a stream with deblocking disabled in the PPS and no slice header override read a slice_loop_filter_across_slices_enabled_flag bit that is not there, then failed byte_alignment. That is x265 --no-deblock output.
The parser now applies the Section 7.4.7.1 inferences. slice_deblocking_filter_disabled_flag comes from pps_deblocking_filter_disabled_flag. slice_loop_filter_across_slices_enabled_flag comes from pps_loop_filter_across_slices_enabled_flag. slice_beta_offset_div2 and slice_tc_offset_div2 come from the PPS counterparts when the slice header does not override deblocking parameters. pic_output_flag is 1 when output_flag_present_flag is 0.
Separately, MdatBox.HeaderSize now accounts for large lazy payloads. trun data offsets were 8 bytes short for fragments above 4 GiB. Any fragmenter or rewriter that trusts those offsets on large mdat boxes was writing the wrong file.
QuickTime audio, wave atoms, and DASH ingest labels ¶
MOV files and some hybrid MP4 files carry QuickTime sound sample descriptions that the ISO mp4a path does not cover. Versions 1 and 2 now parse into AudioSampleEntryBox.QuickTimeVersion, QuickTimeV1, and QuickTimeV2, and they are preserved on encode along with revision level, vendor, and CompressionID. Child boxes are read at the version dependent offset. Info() reports the effective channel count, sample size, and sample rate from the version 2 struct.
WaveBox models the QuickTime siDecompressionParam atom, so an esds wrapped in a wave child of an audio sample entry is reachable as Wave.Esds. Children that are not well formed boxes, such as the spec mandated terminator atom written with size zero, are kept verbatim. Info() escapes control characters in box types as \xNN, so a type such as the four zero bytes of that terminator stays visible.
The fourcc values .mp3, lpcm, twos, and sowt now decode as AudioSampleEntryBox, reachable as StsdBox.Mp3 and StsdBox.QtPcm, instead of falling through to UnknownBox. A body that does not parse as a sound sample description still becomes an UnknownBox. A body that does parse now encodes with the same fidelity as mp4a: zeroed packet size bytes and an integer sample rate, not a byte verbatim copy. A .mp3 entry carries no esds. Codec facts live in the sound description fields.
On the ingest side, LablBox implements the Label box labl from ISO/IEC 14496-12:2026 Section 8.10.5, with LablIsGroupLabelFlag and IsGroupLabel(), as used for track labels in DASH-IF Ingest. UdtaBox.Labls holds the labl children of a user data box, UdtaBox.GroupLabl() finds a group label, and TrakBox.Udta points at the track user data box.
Upgrade notes ¶
DecodeAudioSampleEntry now errors on bodies too short for the fixed fields. Encoding errors on inconsistent QuickTimeVersion, QuickTimeV1, and QuickTimeV2 combinations. Both are correctness checks, not silent repairs.
The encode path for .mp3, lpcm, twos, and sowt will break a golden file test that expected UnknownBox byte copies. Those entries now encode like mp4a. Original packet size bytes and a non integer sample rate do not survive. QuickTime overlay bytes for version, revision level, and vendor are kept on encode instead of being zeroed. Audio sample entries whose reserved bytes claim a QuickTime version whose layout does not parse fall back to the plain ISO interpretation, so previously decodable files keep decoding.
The allocation guards and the HEVC inferences change parse results on inputs that previously ran out of memory or failed byte_alignment. There is no migration step beyond picking up the tag. This is not a prerelease.
Where to get it ¶
- GitHub release page
- mp4ff repository
- Tag
v0.56.0