Skip to content

MP4

current

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 (ISO/IEC 14496-14), closely related to MOV. .m4a and .m4v are the same container used for audio-only and video files; renaming a file does not convert it.

TypeWorks broadlyWorks, but check the playerAvoid or not allowed
VideoH.264 (AVC), HEVC (H.265)AV1, VP9, VVCTheora, ProRes, DNxHR (rejected; use MOV)
AudioAAC, MP3, AC-3, E-AC-3Opus, FLAC, ALACVorbis, PCM (FFmpeg writes them; few players read them)
Subtitlesmov_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, or burn them in; see subtitle formats. Browsers want WebVTT as a separate file next to the video, not inside the MP4.

Terminal window
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:

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

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.

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:

Terminal window
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. Do not combine this with +faststart; they are alternative layouts.

  • 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.
Terminal window
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.