# MP4

Canonical URL: https://encode.wiki/containers/mp4/
Content version: ca42e61ede0a24f789d7287a0001244ad22f4abf
Status: current
License: CC BY-SA 4.0
Checksum (SHA-256): e733bc7f0c6fdb8cb17dff58d930deca649c6dbac511230e70c61ffa5350ccad

MP4 is the container you reach for when a file has to play everywhere: phones, TVs, browsers, game consoles, editors, social platforms. It is a profile of the [ISO Base Media File Format](/containers/isobmff/) (ISO/IEC 14496-14), closely related to [MOV](/containers/mov/). `.m4a` and `.m4v` are the same container used for audio-only and video files; renaming a file does not convert it.

## What goes in an MP4

| Type      | Works broadly             | Works, but check the player | Avoid or not allowed                                          |
| --------- | ------------------------- | --------------------------- | ------------------------------------------------------------- |
| Video     | H.264 (AVC), HEVC (H.265) | AV1, VP9, VVC               | Theora, ProRes, DNxHR (rejected; use [MOV](/containers/mov/)) |
| Audio     | AAC, MP3, AC-3, E-AC-3    | Opus, FLAC, ALAC            | Vorbis, PCM (FFmpeg writes them; few players read them)       |
| Subtitles | `mov_text` (tx3g)         | —                           | SRT, ASS/SSA, WebVTT, PGS, VobSub as such                     |

MP4 can only carry text subtitles as `mov_text`, which keeps basic italics/bold/colour and loses everything ASS does. Convert SRT with `-c:s mov_text`. For ASS or image subtitles use [Matroska](/containers/matroska/), or burn them in; see [subtitle formats](/codecs/subtitles/). Browsers want WebVTT as a separate file next to the video, not inside the MP4.

## Remux into MP4

```sh
ffmpeg -i input.mkv -c copy -movflags +faststart output.mp4
```

`+faststart` rewrites the file so the index (`moov`) sits before the media data (`mdat`). Without it, a player streaming the file over HTTP has to fetch the end of the file before it can start, and players or servers without range requests download the whole thing first. It is a cheap second pass and harmless for local files, so use it for anything you will upload.

FFmpeg's default stream selection quietly drops an SRT track here; with `-map 0` the same command fails instead ("codec not currently supported in container"). To keep SRT subtitles, convert them:

```sh
ffmpeg -i input.mkv -map 0:v -map 0:a -map '0:s?' -c copy -c:s mov_text output.mp4
```

> **Tip — HEVC on Apple devices.**
>
> Apple players only accept HEVC tagged `hvc1`. FFmpeg writes `hev1` by default, which plays on most other things but not in Safari, QuickTime or iOS. Add `-tag:v hvc1` when encoding or remuxing HEVC for Apple devices.
If a remux fails with "codec not currently supported in container", that stream cannot go into MP4. Encode it to something that can (for example `-c:a aac` or `-c:a libopus`), or choose another container.

## Streaming and fragmented MP4

A normal MP4 has one `moov` index describing the whole file. A fragmented MP4 instead stores a short `moof` index before each chunk of media, so it can be written live and read before it is finished. DASH, CMAF, HLS with fMP4 segments and browser Media Source Extensions all use fragmented MP4:

```sh
ffmpeg -i input.mp4 -c copy -movflags +frag_keyframe+empty_moov+default_base_moof fragmented.mp4
```

Fragments start at keyframes, so fragment length follows your keyframe interval. This produces one fragmented file, not a playlist or manifest; for adaptive streaming use FFmpeg's `hls`/`dash` muxers or [MP4Box](/tools/utilities/mp4box/). Do not combine this with `+faststart`; they are alternative layouts.

## Pitfalls

- **Chapters become an extra stream.** FFmpeg stores chapters as a QuickTime text track, which `ffprobe` lists as a `bin_data` stream. That is expected, not a broken map.
- **Metadata and covers.** MP4 supports iTunes-style tags and one cover image (`-disposition:v:1 attached_pic`). There is no attachment mechanism like Matroska's, so embedded fonts and files are lost.
- **Cutting with `-c copy`** can only start on a keyframe. A cut elsewhere gives a frozen or black start, or an audio/video offset, because the leading frames cannot be decoded. Cut on keyframes or re-encode.
- **Truncated or crashed encodes.** If recording stopped before `moov` was written, the file has media but no index and nothing will open it. Record to MKV or fragmented MP4 instead, since those survive interruption.
- **"Optimised for the web" is a layout, not a quality setting.** It changes where the index is, nothing else.

## Inspect a file

```sh
ffprobe -v error -show_format -show_streams -show_chapters input.mp4
```

`ffprobe -v trace input.mp4 2>&1 | grep -E "type:'(ftyp|moov|mdat|moof)'"` shows the top-level box order if you need to confirm that `moov` comes first.
