ACIDCAT . FILE FORMAT REFERENCE

MP4 / M4A

ISO Base Mediacontainer . big-endian
rev 2026.06
magic "ftyp" @ 4
endian big
container nested boxes
codec AAC / ALAC

An MP4 / M4A is a tree of nested boxes (atoms): every box opens with a big-endian u32 size and a four-character type code, then holds either more boxes or a typed payload. Defined by ISO/IEC 14496-12 (ISO Base Media File Format), originally derived from QuickTime. Hover a field to light its bytes, click a + for its lookup table. Color marks kind (see the key).

box regions

Every MP4 is a flat sequence of top-level boxes; moov holds the entire metadata tree.

ftypfirst box . brand declaration
major_brand4cc identifying the primary spec, e.g. "M4A " for Apple AAC audio
minor_versioninformational; parsers must not gate playback on this value
compatible_brandsarray of 4cc to end of box; any match is sufficient for a reader
moovmovie container . entire metadata tree
mvhdtimescale, duration, creation/modification times (seconds since 1904-01-01; unix = value − 2082844800). Version-0 times are 32-bit and wrap in 2040; version 1 uses 64-bit
trakone per track; audio track contains mdia > minf > stbl > stsd
stsdsample description: "mp4a" = AAC, "alac" = Apple Lossless
udta / ilstiTunes tag atoms nested under moov > udta > meta > ilst
mvhd: where the duration comes from (first 32 bytes)

A FullBox. timescale and duration are the only two fields most readers want.

stco: the chunk offset table (20 bytes, complete)

Absolute file offsets. This is the structure that breaks when a file is rewritten.

stco does not locate a sample on its own. It gives the start of each CHUNK. To find sample N you also need stsc, which maps samples to chunks, and stsz, which gives each sample's size. The three together turn a chunk offset into a byte range; any one alone does not. The bytes they point into live in mdat, which holds the raw coded audio and nothing else -- no framing, no index.
Why a tag edit usually does NOT rewrite the file. iTunes deliberately leaves a free atom next to moov. Growing a tag eats padding; shrinking one gives it back. Nothing after it moves, so every stco offset stays correct and the edit is a patch rather than a rewrite. That padding is the mechanism behind in-place tagging, and a writer that ignores it forces a full rewrite for a one-character title change.
mdatmedia data . raw encoded samples

Raw encoded audio frames with no intrinsic framing. May appear before moov (streaming layout) or after it. The stco/co64 box inside stbl holds absolute file offsets into mdat; duration = sum of the stts sample deltas / mvhd timescale. Because samples are reached only through stsc + stsz + stco/co64, any bytes in mdat that no sample table points at are a legal cavity -- edit leftovers, alignment padding, or a place to hide data.

free / skippadding . in-place edit slack

Payload is zeroed or arbitrary. Sized to let moov be rewritten in place without moving stco sample offsets into mdat.

udta / meta / ilstiTunes tags
pathmoov > udta > meta > ilst > ©nam, ©ART, ©day, aART, trkn, covr
covrartwork: nested data atom, type flag 0x0D = JPEG, 0x0E = PNG
notemeta is a FullBox (version + 3-byte flags precede its children) in ISO-BMFF; in QuickTime (.mov) meta is a plain container with no version/flags, so a parser must sniff (peek whether the first child is a valid box) rather than assume
a tag, end to end (32 bytes)

The whole metadata path in one view: a named atom, its data sub-atom, and the text.

There is an atom in the way. Walking this tree by hand, ilst is not the first thing inside meta. After meta's four version/flags bytes comes an hdlr handler atom -- mdir/appl for iTunes metadata -- and only then ilst. It declares what kind of metadata follows, and skipping past it is the normal thing to do.
box header

8 bytes before every box. Example: 00 00 00 18 66 74 79 70 = size 24, type "ftyp".

ftyp box payload

16-byte payload after the 8-byte header (total box = 24 bytes). major_brand "M4A ", minor_version 0, compatible_brands "M4A " + "mp42".

the box tree

Typical top-level layout for an M4A audio file. All timing, codec, and tag metadata lives inside moov; raw samples live in mdat.

ftyp  brand declaration (first box in file)
moov  movie container
├─ mvhd  timescale + duration  (length = duration / timescale)
├─ trak  audio track
│   └─ mdiaminfstbl  sample tables
│        ├─ stsd  codec  ("mp4a" = AAC, "alac" = ALAC)
│        ├─ stts  time-to-sample table
│        ├─ stsc  sample-to-chunk mapping
│        ├─ stsz  per-sample byte sizes
│        └─ stco  chunk offsets into mdat
└─ udtametailst  iTunes tags (title, artist, artwork, BPM ...)
mdat  raw encoded audio samples
fragmented layout

Streaming files (DASH, HLS) drop the single moov + mdat for repeated fragments. Here moov is an init segment carrying an mvex that declares fragments follow but holds no samples; each fragment is a moof (with traf > trun giving per-sample sizes and offsets) paired with its own mdat. Segments may open with a styp brand box. Sample offsets then live in trun, not stco.