MP4 File Documentation
Summary
An MPEG-4 Part 14 (MP4) file is a multimedia container that holds video, audio, subtitles, and metadata in one file. It is the near-universal digital video format, standardised as ISO/IEC 14496-14 with the MIME type video/mp4. Windows, macOS, phones, and every modern browser play a .mp4 file natively, so the common questions are about extracting audio (“MP4 to MP3”) and files that won’t play because of a missing codec.
Technical details
| Feature | Value |
|---|---|
| Full name | MPEG-4 Part 14 (MP4) |
| File extension | .mp4 |
| MIME type | video/mp4 |
| Format type | Multimedia container, box/atom-based binary |
| Base format | ISO Base Media File Format (ISO/IEC 14496-12) |
| Developer | Moving Picture Experts Group (MPEG / ISO/IEC) |
| Introduced | 2001 (MP4 v1); Part 14 standardised 2003 |
| Standard | ISO/IEC 14496-14 |
| Open standard | Yes — published ISO/IEC specification |
| Byte order | Big-endian |
| Magic number (hex) | 66 74 79 70 (ftyp) at offset 4 |
| Video coding formats | H.264/AVC, H.265/HEVC, AV1, MPEG-4 Part 2 |
| Audio coding formats | AAC (usual), MP3, AC-3, ALAC |
| Subtitle support | Yes |
| Chapter support | Yes |
| Streaming support | Yes (progressive and adaptive; moov-at-front for fast start) |
| Resolution | Codec-limited; up to 4K/8K in practice |
| DRM support | Yes (ISO Common Encryption, e.g. Widevine, FairPlay, PlayReady) |
| Related extensions | .m4v, .m4a, .mov, .3gp, .mkv |
| Specification | iso.org/standard/79110.html |
What is an MP4 file?
MP4 is short for MPEG-4 Part 14, standardised by the Moving Picture Experts Group as ISO/IEC 14496-14 in 2003. It is a container: a structured wrapper that stores one or more timed media tracks together with the metadata a player needs to decode and synchronise them. The container itself is defined on top of the ISO Base Media File Format (ISO/IEC 14496-12), the same base structure used by MOV, M4A, .m4v and .3gp. Its MIME type is video/mp4.
A typical .mp4 holds one H.264 or H.265 video track and one AAC audio track, plus optional subtitle, chapter and metadata tracks. None of that coded media is described byte-for-byte by MP4 itself. The container records where each sample lives and when it should be presented; the actual bitstream is the codec’s business. Everything below is about how ISO BMFF encodes that structure, box by box.
The box header: size, type, largesize and uuid
An ISO BMFF file is a flat sequence of nested boxes (the QuickTime term is “atoms”). Every box begins with the same 8-byte header, and boxes contain other boxes, so a parser can walk the whole tree without knowing any specific box in advance.
aligned(8) class Box (unsigned int(32) boxtype) {
unsigned int(32) size; // bytes 0-3: total box length incl. this header
unsigned int(32) type; // bytes 4-7: four-character code, e.g. 'moov'
if (size == 1)
unsigned int(64) largesize; // 64-bit length when size == 1
else if (size == 0)
; // box runs to end of file
if (type == 'uuid')
unsigned int(8)[16] usertype; // 16-byte extended type
// ... box payload follows ...
}
The first field is a 32-bit big-endian size giving the box’s total length including the header. The second is a 32-bit type, four printable ASCII characters such as ftyp, moov or mdat. Two escape values matter. A size of 1 signals that a 64-bit largesize follows the type, which is how an mdat box exceeds 4 GB. A size of 0 means the box extends to the end of the file, valid only for the last box. When type equals uuid, the next 16 bytes are a full UUID that names a vendor extension box, letting private data ride inside a standard file that other readers skip cleanly.
Because each box carries its own length, an unknown box is trivially skipped: read size, jump size bytes, continue. That forward-compatibility rule is why a two-decade-old parser can still open a file that uses boxes invented after it was written. All multi-byte integers in the format are big-endian (network byte order).
ftyp: brands and compatibility
The first box in a well-formed MP4 is ftyp, the file-type box, which is why the ASCII string ftyp appears at byte offset 4 rather than offset 0: bytes 0–3 are its size. Its payload declares which specifications the file conforms to.
ftyp
major_brand : uint32 'mp42' // preferred brand / spec
minor_version : uint32 0 // informative version of major_brand
compatible_brands[]: uint32 'isom','mp41','mp42','avc1' ... // list to EOF
The major_brand is the single brand the file most wants to be read as, for example mp42 (MP4 v2) or isom (the ISO base profile). minor_version is an informative integer, not a strict version gate. The compatible_brands array then lists every brand the file also satisfies, one four-character code per entry, running to the end of the box. A player reads this list and, if it recognises any brand, knows it can handle the file: seeing avc1 tells it H.264 conventions apply, iso6 implies fragmented-file support, M4A marks an audio-only M4A. The brand set is advisory about structure and profile; it does not by itself name the codecs, which are declared later in the sample descriptions.
The moov hierarchy: mvhd, trak and the track tree
The moov box is the movie box: it holds all metadata and no coded media. It contains one mvhd movie header plus one trak box per track. The nesting is deep and every level has a job.
moov
├─ mvhd movie header: timescale, duration, next_track_ID
├─ trak one track (e.g. video)
│ ├─ tkhd track header: track_ID, flags, width, height, matrix
│ └─ mdia media info for this track
│ ├─ mdhd media header: this track's timescale + duration
│ ├─ hdlr handler: 'vide' / 'soun' / 'subt' — what kind of track
│ └─ minf media information container
│ ├─ vmhd/smhd video or sound media header
│ ├─ dinf data information (where the media lives: dref)
│ └─ stbl the SAMPLE TABLE — see below
└─ trak another track (e.g. audio)
The mvhd box carries the movie-level timescale, the number of time units per second against which the overall duration is expressed. A timescale of 1000 means durations are in milliseconds; video tracks often use 90000 (90 kHz). mvhd also stores the total duration in those units and next_track_ID. Each trak opens with tkhd, the track header, holding the track’s track_ID, enabled/in-movie flags, a 3×3 transform matrix, and for video the display width and height as 16.16 fixed-point.
Inside trak, the mdia box describes the media. Its mdhd gives this track’s own timescale and duration (a track can be timed independently of the movie), and hdlr gives the handler type: vide for video, soun for audio, subt or text for subtitles. Below that, minf collects a media header (vmhd or smhd), a data-reference box dinf/dref that says whether the media is in this file or an external one, and finally stbl, where the real indexing lives.
The stbl sample tables
Everything a player needs to find, time and decode an individual sample is packed into the stbl (sample table) box as a set of parallel tables. A sample is one coded unit: one video frame, or one audio frame of AAC. The tables are stored separately so each stays compact, and a reader cross-references them to reconstruct the timeline.
stbl
├─ stsd sample descriptions: codec + config per sample entry
├─ stts time-to-sample: sample durations (run-length coded)
├─ ctts composition time-to-sample: PTS offsets (B-frame reorder)
├─ stsc sample-to-chunk: how many samples per chunk
├─ stsz sample sizes (or stz2 for compact sizes)
├─ stco chunk offsets into the file (or co64 for 64-bit)
└─ stss sync sample table: which samples are keyframes
The stsd box holds one or more sample entries, and this is where the codec is actually named. A video entry typed avc1 or hvc1 nests a codec-configuration box: avcC for H.264 (carrying the SPS and PPS parameter sets), hvcC for H.265, and for audio an esds box holding the MPEG-4 elementary-stream descriptor with the AAC configuration. Without stsd a decoder cannot be initialised.
stts maps sample number to duration using run-length entries (“N samples each lasting D units”), which is why constant-frame-rate video needs only a single entry. ctts adds a per-sample composition offset so presentation timestamps (PTS) can differ from decode timestamps (DTS); it exists because B-frames are decoded out of display order. stsc is the sample-to-chunk map: samples are grouped into chunks, and this box run-length encodes how many samples each run of chunks contains. stsz (or the packed stz2) lists the byte size of every sample, or a single size if all are equal. stco lists the file offset of each chunk; co64 is its 64-bit form for files past 4 GB. Finally stss lists which samples are sync samples (keyframes) so a player can seek to a clean random-access point. If stss is absent, every sample is a sync sample.
| Atom | Purpose |
|---|---|
stsd | Sample descriptions: codec type and config (avcC/hvcC/esds) |
stts | Time-to-sample: decode duration of each sample, run-length coded |
ctts | Composition offsets: PTS−DTS for reordered B-frames |
stsc | Sample-to-chunk: how many samples are packed in each chunk |
stsz / stz2 | Sample sizes in bytes (per sample, or one uniform size) |
stco / co64 | Chunk offsets into the file (32-bit / 64-bit) |
stss | Sync sample table: which samples are keyframes for seeking |
mdat: locating a sample by offset, size and time
The mdat box is a single opaque blob of interleaved coded samples: a run of video frames, then a run of audio frames, then more video, arranged so a player reads roughly sequentially while feeding both decoders. Nothing inside mdat is self-describing; it is meaningless without the sample tables pointing into it. This is deliberate: metadata and media are separated so the index can be read or rewritten without touching the payload.
To fetch sample n, a reader combines four tables. stsc tells it which chunk contains sample n and its position within that chunk; stco/co64 gives that chunk’s absolute file offset; the reader sums the stsz sizes of the preceding samples in the chunk to reach the sample’s start; and stsz gives the sample’s own length. That is the exact byte range to read. Its decode time comes from accumulating stts durations up to n, and its presentation time adds the ctts offset. Seeking works the same way in reverse: pick the nearest stss sync sample at or before the target time, resolve its offset, and start decoding there.
Container versus codec, technically
The .mp4 wrapper and the compressed bitstream inside it are separate layers. The container defines boxes, tracks, timing and offsets; the codec defines how pixels and audio samples are compressed into the bytes that mdat stores. MP4 commonly carries H.264/AVC (declared by an avc1 sample entry with an avcC config), H.265/HEVC (hvc1/hev1 with hvcC), or AV1 (av01 with av1C); audio is almost always AAC in an mp4a entry with an esds descriptor. The container is codec-agnostic, so the same box structure can hold any of them.
This layering is why a file that “won’t play” is rarely a broken container. The box tree parses fine; what a player lacks is a decoder for the bitstream stsd names, most often HEVC on a system without the HEVC decoder. It also explains why a remux to MOV is lossless: MOV shares the ISO BMFF box model, so the coded samples are copied unchanged and only the surrounding boxes are rewritten. The open-codec web equivalent, WebM, instead uses a different container base (a Matroska/MKV subset) around VP9 or AV1, so moving to it means a real transcode.
Faststart: moov placement and progressive download
A player cannot present anything until it has read the moov box, because that box holds the sample tables. Many encoders emit moov after mdat, at the end of the file, since final sample sizes and offsets are only known once encoding finishes. Over HTTP that forces the client to download the whole file before playback starts, because the index is at the very end.
“Faststart” (also called web-optimising) rewrites the file so moov comes before mdat. Now the client reads ftyp and moov from the first bytes, learns the full sample layout, and can begin fetching and decoding mdat ranges immediately: progressive download. The trade-off is that all chunk offsets in stco/co64 shift when moov moves ahead of the media, so the offsets must be recomputed during the rewrite, which is what a faststart pass actually does.
Fragmented MP4: moof and traf
Progressive download still assumes one contiguous file with a single up-front index. Adaptive streaming over MPEG-DASH and HLS needs something else: many independently fetchable, independently timed segments. That is fragmented MP4 (fMP4), and it changes where the timing lives.
ftyp
moov
└─ mvex movie extends: declares fragments will follow
└─ trex per-track defaults (default sample size/duration/flags)
moof movie fragment (one per segment)
├─ mfhd sequence_number of this fragment
└─ traf track fragment
├─ tfhd track fragment header: track_ID, base offset, defaults
├─ tfdt base media DECODE time of this fragment
└─ trun track run: per-sample sizes, durations, flags, CT offset
mdat coded samples for THIS fragment only
moof / mdat ... repeated per segment ...
In an fMP4 the initial moov carries almost no sample data. Instead it contains an mvex box whose trex children declare per-track defaults (default sample duration, size and flags) and signal that the movie continues in fragments. The real timing arrives fragment by fragment. Each fragment is a moof box followed by its own mdat. The moof holds an mfhd with the fragment’s sequence_number, then one traf per track. Inside traf, tfhd sets the track ID and base data offset and overrides defaults; tfdt gives the absolute base media decode time so a client can place the fragment on the timeline without having seen earlier fragments; and trun (track run) lists each sample’s size, duration, flags and composition offset for the samples in that fragment’s mdat.
Because each moof + mdat pair is self-contained and self-timed, a DASH or HLS client can request just the segments it wants, at whatever bitrate, and switch renditions at fragment boundaries. The same fragmented structure, delivered in short segments, is what a browser’s Media Source Extensions feed to play adaptively without ever downloading a whole monolithic file.
References
- ISO/IEC 14496-14 — MP4 file format
- VideoLAN — VLC media player
- HandBrake — open-source video transcoder
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.