What Is an MP4 File? A Box Container for Video, and Why Some Files Will Not Play
An MP4 file is a box container for video and audio, so keep the original and its hash. Jump to your question below, or work through it in order.
What Is an MP4 File? A box container for video and audio, saved as .mp4
Short answer
An MP4 file is a container for video and audio. It is a row of boxes, and the three that matter are ftyp, moov and mdat. Its name ends in .mp4, and the ending names the container and no codec.
We read 231 public MP4 files and made 132 damaged copies of 4 MP4 files that we built. Our two checks flagged 82 of the 99 copies that decoded differently, and 17 got no flag from either one.
To see the boxes, go to the box layout. To check an MP4 file, follow the six steps. To fix a file that will not play, read the causes.
An MP4 file is a box container for video, audio and text. The ending .mp4 names the container and no codec, so what plays depends on the codecs inside. The position of the index box decides what a cut or half saved MP4 file gives you.
ISO/IEC 14496-14 defines the MP4 file format. Its abstract says the format is derived from the ISO Base Media File format.
The current edition is the third, published on 27 January 2020, and it replaced the 2018 edition, which ISO lists as withdrawn.
Wikipedia says MP4 followed in 2003 and that it is based on the QuickTime file format.
Sources differ on the first year. Wikipedia names 2001 for the first MPEG-4 file format, and MDN names 1999, so we print both. They agree that Part 14 came in 2003. FileInfo gives 2001 for the first release and 2003 for version 2.
The ending .mp4 and its relatives
Wikipedia says .mp4 is the only ending that the standard defines. The others are habits. Audio only files use .m4a, audiobooks use .m4b, protected audio uses .m4p, ringtones use .m4r and some video files use .m4v.
PRONOM, the format registry of the UK National Archives, lists mp4, m4v, m4a, f4v, f4a and m4b in one record named MPEG-4 Media File.
We renamed our sample MP4 file to .mov, .mkv, .m4a, .txt and .dat. FFmpeg and the file command read the same file every time, and the SHA256 hash stayed the same. The check part shows the result. An ending that does not match the content can still hide a good MP4 file from your player.
Container and codec
A container is the box. A codec is the method that squeezes video or sound inside it. The MP4 file format stores squeezed data and an index to it, and it leaves the squeezing to the codec.
MDN lists H.264, AV1, H.263, MPEG-4 Part 2 and VP9 for video in MP4 files. It lists AAC, FLAC, MP3 and Opus for audio.
So an MP4 file can play on one device and fail on another, with the same ending and the same container. The codecs part shows our test. Chromium 141 played an MP4 file with AV1 and Opus and refused one with H.264 and AAC.
Where our MP4 files come from
We worked with three sets of MP4 files. We built 4 test MP4 files with FFmpeg 6.1.1. All are 10 second clips of 640 by 360 pixels with a sine tone.
One has H.264 and AAC with moov first, one is the same clip with moov last and one is fragmented. The fourth has AV1 and Opus. The second set is 132 damaged copies of those 4 MP4 files.
The third set is 231 public MP4 files from test folders of Chromium and Firefox, 153 from the first and 78 from the second. They add up to 50,907,584 bytes, and the largest has 7,839,097 bytes.
We read them with FFmpeg 6.1.1, with MediaInfo through pymediainfo 7.0.1, with the file command 5.45, with Python 3.13 and with our own scripts. We opened the AV1 file and its copies in Chromium 141 through Playwright on Linux.
We did not watch or listen to any file. We compared decoded frames and audio with hashes. We did not test VLC, Windows Media Player, QuickTime, desktop Chrome, Edge, Firefox, Safari or a phone. What we say about those programs comes from their makers’ pages, and we say so every time.
MP4 file facts at a glance
| Property | Value |
|---|---|
| Meaning | A container for video, audio, text and still images, stored as boxes. |
| Standard | ISO/IEC 14496-14, third edition, from January 2020. It builds on ISO base media file format. |
| Endings | .mp4 is the one the standard defines. .m4a, .m4b, .m4p, .m4r and .m4v are habits. |
| Media types | video/mp4, audio/mp4 and application/mp4, registered in RFC 4337 from March 2006. |
| Start |
A 4 byte size, then the four letters ftyp, then the brand. Our sample starts with 00 00 00 20 66 74 79 70.
|
| Byte order |
Big endian. The size 32 is written as 00 00 00 20.
|
| Made by | Cameras, phones, FFmpeg, screen recorders, editors and browsers. |
| Opens with | Most players and browsers, when they support the codec inside. |
| Our tests | 4 built files, 132 damaged copies, 231 public MP4 files, a 22 line boxes script and a 70 line check script. |
Wrong page? Look at the FLV page for Flash video, the GIF page for animated pictures or the SRT page for subtitles.
What Is Inside an MP4 File? Three boxes matter most, which are ftyp, moov and mdat
An MP4 file is a row of boxes. Every box starts with a 4 byte size and a 4 byte name. Three of them matter most. The ftyp box names the brand, moov is the index and mdat stores the audio and video data.
The figure draws our 639,131 byte sample twice, once with moov at the front and once with moov at the back. The two files have the same bytes in a different order.
Below the bars you see the 8 byte header of a box and the boxes inside moov. The code box lists the top level boxes of both files.
$ python3 mp4_boxes.py sample.mp4
0 ftyp 32 brand isom, also isom iso2 avc1 mp41
32 moov 9691
9723 free 8
9731 mdat 629400
639131 bytes, 0 left over
$ python3 mp4_boxes.py sample-moovend.mp4
0 ftyp 32 brand isom, also isom iso2 avc1 mp41
32 free 8
40 mdat 629400
629440 moov 9691
639131 bytes, 0 left over
The box header
A box starts with its size in bytes, a number of 4 bytes, and then its name of 4 letters. The size counts the header itself.
The first 8 bytes of our sample are 00 00 00 20 66 74 79 70, which means a box of 32 bytes named ftyp. A box can contain other boxes, and the outer size covers them.
Two sizes are special. A size of 1 means the true size follows as an 8 byte number after the name, so a box can pass 4 GB. A size of 0 means the box goes on to the end of the MP4 file.
We set the size of mdat to 0 in four copies. The copy with moov first kept playing, because mdat was last anyway. The copy with moov last broke, because mdat then swallowed moov.
$ python3 mp4_check.py k04b.mp4
no moov box
$ python3 mp4_boxes.py k04b.mp4
0 ftyp 32 brand isom, also isom iso2 avc1 mp41
32 free 8
40 mdat 639091
639131 bytes, 0 left over
The ftyp box and its brands
The ftyp box comes first. It names a major brand and a list of compatible brands, and a brand is 4 letters. Ours says isom, with isom, iso2, avc1 and mp41 as compatible brands.
The 231 public MP4 files use 13 different major brands. The most common are isom with 110 MP4 files, iso5 with 61, dash with 19, mp41 with 15 and mp42 with 12.
The file command reads the brand. For isom it prints ISO Media, MP4 Base Media v1 and the number 14496-12. It prints the number 14496-14 for only the 12 public MP4 files with the brand mp42.
A brand is a promise about what the MP4 file can contain, and the codecs are not part of it. The study part lists the other brands.
The moov box is the index
The moov box shows a player how to read the data. It has one trak box for every track, so a clip with video and sound has two. Inside a trak, the stbl box is the sample table.
A sample is one video frame or one block of audio. The stsd box names the codec, the stsz box lists the size of every sample and the stco box says where every chunk of samples starts.
This is why moov is small and matters so much. In our sample it has 9,691 bytes, which is 1.5 percent of the MP4 file.
Without it a player sees 629,400 bytes of mdat and no way to find where a frame starts. Chunk offsets are byte positions in the MP4 file. We changed one of them in 8 copies, and 8 of 8 decoded differently.
The mdat box is the data
The mdat box has no structure of its own. It is a block of squeezed frames and audio blocks that lie one after the other. Only moov knows where a frame starts. The mdat box in our sample has 629,400 bytes, which is 98.5 percent of the MP4 file.
That gives the two kinds of damage their shape. A change in moov breaks the map, and a change in mdat breaks the pictures.
A box that follows the standard can still contain bytes that no decoder can read, so a check of the boxes cannot see most damage in mdat. The damaged part shows the counts.
Fragmented MP4 files
A fragmented file keeps a small moov in front. After it come pairs of boxes, a moof box that is the index of one fragment and an mdat box with its data.
Our fragmented sample has a moov of 1,229 bytes and 10 pairs, and an mfra box at the end. We made it with the FFmpeg options frag_keyframe+empty_moov+default_base_moof.
$ python3 mp4_boxes.py sample-frag.mp4
0 ftyp 28 brand iso5, also iso5 iso6 mp41
28 moov 1229
1257 moof 708
1965 mdat 56353
58318 moof 552
58870 mdat 61657
120527 moof 552
... and 17 more lines
FFmpeg’s documentation says its faststart option does not work with fragmented output, and that the option frag_keyframe starts a new fragment at every video keyframe. Streaming sites use fragments, because a player can start at any fragment. Our fragmented copies cut at 90, 50 and 10 percent still decoded 225, 125 and 25 frames.
List the boxes of an MP4 file
Our 22 line script reads the size and the name of every top level box and prints them with brands. It changes nothing in the MP4 file. The check part lists it with our check script.
If the first box is not ftyp, or the list ends in a box that does not fit, the MP4 file is damaged or it is not an MP4 file.
We read this layout from our 4 built files and 231 public MP4 files. We did not read files from every camera or phone, and some write extra boxes, for example udta for tags or sidx for segment indexes. The boxes above are the ones every player needs.
Which Codecs Does an MP4 File Use? The container is not the codec, and our Chromium test shows why
A container is not a codec. Chromium 141 played our AV1 and Opus MP4 file for all 250 frames, and it refused our H.264 and AAC file and our HEVC and AAC file. All three files have the same container, so the codecs decide what plays on your device.
The table lists the programs that matter, with what we found or what their makers say. We tested Chromium 141 and FFmpeg 6.1.1 only. Every other row comes from the page that we link, and those pages can change.
| Program | What we found |
|---|---|
| Chromium 141 | Played our AV1 and Opus file. Refused the H.264 and AAC file and the HEVC and AAC file with error 4, which means no supported streams. Chromium’s audio and video page lists H.264, AAC and HEVC as limited to Google Chrome, and it says HEVC needs hardware support. |
| Firefox | Not tested. MDN’s guide says Firefox relies on the media support of the operating system for H.264. It lists no Firefox support for H.263 and for MPEG-4 Part 2 video. |
| Windows | Not tested. Microsoft’s page on the MPEG-4 file source says it is the default source for .mp4, .m4a, .m4v and .mov. It parses the container and does not decode. It lists avc1 and mp4a sample entries and no HEVC entry, and its date is 2021, so it says little about current Windows. |
| Apple devices | Not tested. Apple’s support page says HEVC arrived with iOS 11 and macOS High Sierra, and that sharing can convert a clip to H.264 for a device that cannot play HEVC. The page names no container. |
| FFmpeg 6.1.1 | Opened our four built files, the HEVC pair and 225 of the 231 public MP4 files. It is the reader that we used for every hash on this page. |
Our Chromium test
We served our MP4 files from a local web server and set every file as the source of a video element in Chromium 141 on Linux. We then read the error and the frame count.
The first two files are the H.264 sample and an HEVC file that we built with x265. The third is our AV1 and Opus file.
The same build played 250 of 250 frames of the AV1 file and ended at 10 seconds. The container did not change between the three tests.
Chromium’s page names Google Chrome as the home of H.264, AAC and HEVC, and we tested the open build only. So this is a result for one build, and it does not describe desktop Chrome.
Name the codecs inside an MP4 file
A page or a server can say which codecs an MP4 file needs before a player downloads it. RFC 6381 from August 2011 defines the codecs parameter for that, and it updates RFC 4337.
Its examples are video/mp4; codecs="avc1.640028" for H.264 High profile and codecs="mp4a.40.2" for AAC low complexity. ffprobe prints the short tags, and ours say avc1 and mp4a.
$ ffprobe -v error -show_entries stream=codec_name,codec_tag_string,width,height -of csv=p=0 sample.mp4
h264,avc1,640,360
aac,mp4a
The codecs in 231 public MP4 files
| Stream | What we found |
|---|---|
| Video codecs | H.264 in 103 MP4 files, HEVC in 31, AV1 in 17, VP9 in 7 and DV in 2. No file has two video codecs. |
| Audio codecs | AAC in 76 MP4 files, FLAC in 8, Opus in 6, AC4 in 3, DTS in 2, AC3 in 1, E-AC3 in 1 and PCM in 2. |
| Mix | 35 MP4 files have video and audio, 125 have video only and 64 have audio only. FFmpeg found no stream in 6 MP4 files. |
These are test MP4 files that engineers wrote for players, so they cover rare cases on purpose. FLAC and AC4 in MP4 files are far less common than AAC in the wild. the MDN table lists FLAC and Opus as allowed in MP4 files, and our corpus shows both inside them.
The codec tag, avc1, hvc1 and hev1
Every video track names its codec with a 4 letter tag in the stsd box. The public MP4 files use seven tags on their video tracks. They are avc1 on 104, av01 on 17, hev1 on 15, hvc1 on 13, vp09 on 7, dvh1 on 5 and avc3 on 2. HEVC has two tags, hvc1 and hev1.
We built two HEVC files that differ in the tag only. Both have 613,079 bytes and both decode to the same frames.
FFmpeg rewrote hev1 as hvc1 with the options -c copy -tag:v hvc1, and the frames stayed the same.
We did not test which players prefer which tag, and we found no vendor page that says so, so we make no claim.
What Do 231 Public MP4 Files Contain? 13 brands, 84 fragmented files and 78 unfragmented files with moov last
We read 231 public MP4 files from the test folders of Chromium and Firefox. They use 13 brands, 84 are fragmented and 78 of the 95 unfragmented files keep moov at the end. Our script flagged 7 MP4 files, and FFmpeg refused 6.
These are test MP4 files that engineers wrote to check players, so some are damaged on purpose. They add up to 50,907,584 bytes, and the largest has 7,839,097 bytes. They are not a sample of the web. Read them as a picture of what tools write and of what a reader does with it.
The names are simple. 224 end in .mp4, 3 in .m4a, 2 in .mov, 1 in .3gp and 1 in .m4s. The brand in the ftyp box shows more than the ending does. The table lists every brand with the text that the file command printed.
| Brand | Files and the MP4 file text |
|---|---|
| isom | 110 MP4 files. MP4 Base Media v1 with the number 14496-12. |
| iso5 | 61 MP4 files. MP4 Base Media v5. |
| dash | 19 MP4 files. MPEG v4 system, Dynamic Adaptive Streaming. |
| mp41 | 15 MP4 files. MP4 v1 with the number 14496-1 and the words ch13. |
| mp42 | 12 MP4 files. MP4 v2 with the number 14496-14. |
| qt | 6 MP4 files. Apple QuickTime movie. Four of them are named .mp4. |
| Other | 8 MP4 files with M4V, M4A, 3gp6, ccff, avc3 or iso6, and 1 with no ftyp. |
The position of moov in the public MP4 files
84 MP4 files are fragmented, and 82 of those have moov first. 95 MP4 files are not fragmented, with one moov and one mdat. Of those, 78 keep moov at the end and 17 keep it in front.
51 MP4 files have a moov and no data, which are the start of a stream or a test of a reader. One file has no moov at all.
So most unfragmented public MP4 files have moov last. That says how test MP4 files are made, because many tools write moov last unless you ask for faststart. It does not say how common the layout is on the web. The moov part shows what the position costs when an MP4 file is cut.
The PRONOM signature and our MP4 files
PRONOM’s record gives the pattern {4}ftyp{0-64}(mp42|mp41|isom|iso2)*moov for MPEG-4 Media File. RFC 4337 and the Library of Congress list no magic number at all. We tested the PRONOM pattern as we read it on the 231 MP4 files.
It matched 185 of 231. It missed 46 MP4 files. They are 36 MP4 files with the brand iso5 and no isom family brand, and 6 with the brand qt.
One has no ftyp, one has ccff, one has iso6 and one is an isom file that has no moov. So the pattern fits files with the brands it names.
It does not fit every MP4 file, and a file that does not match can still play.
Where our script and FFmpeg disagree
| Group | What we found |
|---|---|
| Flagged, 7 MP4 files | 5 open in FFmpeg. Three named bear_dtsc, bear_dtse and bear_dtsx point past the end of the MP4 file and have no mdat. One init segment has 5 bytes after the last box. One named crbug657437 has no moov box. |
| Flagged and refused | 2 MP4 files that FFmpeg refused. A segment named agtm metadata track frag starts with moof and has no ftyp. An MP4 file named duplicate_track_id has a box that goes past the end of the MP4 file. |
| Refused, no flag | 4 MP4 files that our script passed. Three have xHE-AAC audio and are named noise xhe aac. The fourth is named trun_overflow. We did not trace why FFmpeg 6.1.1 refuses them. |
Our script reads boxes and offsets and decodes nothing, so it cannot see a codec problem. FFmpeg reads the codec data and cannot see a box that no one needs. Both tools miss what the other sees, and the damaged copies below show the same split.
The 132 damaged copies
We took the 4 MP4 files that we built. The first has H.264 and AAC with moov first, with 639,131 bytes. The second is the same clip with moov last and has the same size.
The third is fragmented, with 637,057 bytes. The fourth has AV1 and Opus and 685,182 bytes. We damaged every file in 19 ways and made 33 copies of every file.
A kind that picks a random place got more copies, so the kinds have 4, 8, 12 or 20 copies. We seed the random choice with the MP4 file, kind and copy number, so anyone can make the same copies. We changed one thing at a time and left every other byte alone.
The public MP4 files show what tools write. Built files give us a known good original, and with it we can say for every copy whether the decoded video and audio changed. The next part has the results.
What Happens When an MP4 File Is Damaged? 132 copies, and 17 that got no flag from our two checks
We damaged 4 MP4 files in 19 ways and read all 132 copies. 99 decoded differently from the original. Our box script flagged 27 of the 99, the FFmpeg decode check flagged 79 and together they flagged 82. 17 changed copies got no flag from either one.
We sorted the 19 kinds into four groups. They cover headers at the start, cuts and extra bytes, the index in moov and media data in mdat.
We read every copy with our 70 line script, with the FFmpeg decode check, with ffprobe, with MediaInfo and with the file command. We opened the 33 copies of the AV1 file in Chromium 141.
Changed means the decoded frames or the decoded audio differ from the original. A flag is any line that a check printed.
The decode check is ffmpeg -v error -i copy.mp4 -f null -, and it flags a copy when it prints anything.
A name like k13 in a code box is a copy of kind 13 in the figure.
Green means the copy changed and a check flagged it. Blue means it stayed the same and a check still flagged it. Red means it changed and nobody said anything. Gray means it stayed the same and got no flag. The last two columns count flags from our script and from FFmpeg.
Silent damage in an MP4 file
17 copies got no flag from either check. 14 of them have one changed byte or 1 KiB of zeros in mdat.
The other 3 are one flipped byte in moov, one changed sample size and one copy of the fragmented file with the mdat size set to 0.
In that copy the box then swallowed 225 of the 250 frames and nothing complained.
$ python3 mp4_check.py k16.mp4
no problem found
$ ffmpeg -v error -i k16.mp4 -f null - | wc -l
0
$ cmp -l sample.mp4 k16.mp4 | wc -l
1
$ diff of the frame hashes: lines that differ
25
The script says no problem found, and FFmpeg prints nothing. The cmp command finds one byte that differs, and the frame hashes show 25 frames that differ.
A byte in mdat is a byte of picture data, so a good MP4 file and a damaged file have the same boxes. In the 20 copies of this kind, the changed byte altered between 0 and 217 of the 250 frames.
The 0 is a copy where only the audio changed.
Only a hash of the good MP4 file shows this damage for sure. That is why the check steps starts with a hash and ends with a comparison against a copy you trust.
Damage in the index of an MP4 file
The index kinds are 11 to 15. One changed chunk offset gave 8 of 8 copies that decoded differently. FFmpeg flagged all 8, and our script flagged none, because an offset that moves a little stays inside mdat. One changed sample size changed 7 of 8 copies and FFmpeg flagged all 7.
$ python3 mp4_check.py k13.mp4
no problem found
$ ffmpeg -v error -i k13.mp4 -f null -
[NULL @ 0x55b3819a5fc0] Invalid NAL unit size (531718461 > 1339).
[NULL @ 0x55b3819a5fc0] missing picture in access unit with size 1343
[h264 @ 0x55b381a51140] Invalid NAL unit size (531718461 > 1339).
A moov box renamed to free gave a surprise. Our script flagged all 4 copies, and FFmpeg printed a warning on all 4, yet 3 of 4 decoded the same frames.
FFmpeg finds the index in a free box and goes on, and ffprobe says it detected moov in a free or hoov atom. It failed only on the fragmented copy. A movie timescale of 0 did the same, with 4 flags and 4 copies that played the same.
$ python3 mp4_check.py k11.mp4
no moov box
$ ffprobe -v error -show_entries stream=codec_name -of csv=p=0 k11.mp4
[mov,mp4,m4a,3gp,3g2,mj2 @ 0x5599a26aba00] Detected moov in a free or hoov atom.
h264
aac
One flipped byte in moov was the mixed case. 9 of 12 copies decoded differently. Our script flagged 5 of them, FFmpeg flagged 5 and 1 got no flag. It depends on which byte moved. A byte in a time stamp changes little, and a byte in a size or an offset breaks the picture.
Damage at the start of an MP4 file
A changed brand changed nothing. All 4 copies decoded the same and nobody flagged them. The file command is the only reader that noticed. It prints ISO Media and drops the words MP4 Base Media. Players use codecs and the index, and they do not use the brand to decide.
$ file k01.mp4
k01.mp4: ISO Media
$ file sample.mp4
sample.mp4: ISO Media, MP4 Base Media v1 [ISO 14496-12:2003]
$ python3 mp4_check.py k01.mp4
no problem found
A removed ftyp box changed 3 of 4 copies, and the fragmented copy still played. The file command printed Apple QuickTime movie for 3 of the 4 and data for the moov last copy.
A moov size field raised by 16 changed 1 of 4 copies, the fragmented one, and our script flagged all 4. A size field of 20 on ftyp broke all 4 copies in every reader.
Cuts and extra bytes in an MP4 file
Cuts at 90, 50 and 10 percent changed all 12 copies, and both checks flagged all 12. 100 junk bytes at the end changed nothing in 8 copies, and our script flagged all 8, so they count as alarms.
1 MiB of zeros at the end changed nothing and got no flag. Players read the boxes they need and stop. Bytes after the last box do not hurt them.
$ python3 mp4_check.py k09.mp4
box with a bad name at 639131 has size 2971753348 and the file ends at 639231
$ ffmpeg -v error -i k09.mp4 -f null -
exit code 0
The moov part covers the cuts. The result there depends on which side of mdat the index is on.
Damage in the data of an MP4 file
The data kinds are 16 to 19. One byte inserted at the start of mdat is the harshest. The MP4 file grows by 1 byte, every box still fits, and our script says no problem found. Every offset in moov now points 1 byte too early.
6 of 8 copies gave no frames at all. The 2 fragmented copies gave 225 frames, and all 225 differ. FFmpeg printed 2,458 lines for the first copy.
$ python3 mp4_check.py k18.mp4
no problem found
$ ffmpeg -v error -i k18.mp4 -f null - | wc -l
2458
$ wc -c < sample.mp4 ; wc -c < k18.mp4
639131
639132
The last 10 percent of mdat set to zeros changed all 4 copies. 1 KiB of zeros inside mdat changed all 20, and FFmpeg flagged 14 of them. 6 of those 20 got no flag from either check, and their pixels still differ from the original.
What every reader said
| Reader | What we found |
|---|---|
| Our 70 line script | Flagged 46 copies. 27 had changed and 19 had not. It flagged no copy of the kinds 13, 14, 16, 17, 18 and 19. |
| FFmpeg decode check | Flagged 87 copies. 79 had changed and 8 had not. Of the 99 changed copies it flagged 79. |
| Both together | Flagged 102 copies. 82 of them had changed. 17 changed copies got no flag, and 13 unchanged copies got none. |
| ffprobe | Refused 10 copies, all changed. It opened the other 89 changed copies, so opening is not a pass. |
| MediaInfo | Found no track in 9 changed copies and found tracks in the other 90. |
| file 5.45 | Printed 5 different texts. It reacted to a changed brand and to a removed ftyp, and to no other damage. |
| Chromium 141 | Opened the 33 copies of the AV1 file. Of the 24 that changed, 16 ended in an error, 2 ended early with fewer frames and 6 played all 250 frames with no error. |
Chromium stopped with an error for 4 copies before the first frame and for 12 after it had shown from 21 to 216 frames.
The 6 that played to the end include copies where a byte in mdat or in the index changed. A player that shows 250 frames does not prove that the frames are right.
What the groups show
| Group | What we found |
|---|---|
| A. Start (1 to 5) | 20 copies. 9 caught, 4 alarms, 1 missed and 6 quiet. A changed brand shows that many headers do not matter to a player. |
| B. Cuts (6 to 10) | 24 copies. 12 caught and 8 alarms from the junk bytes. 4 quiet copies had a megabyte of zeros at the end. |
| C. Index (11 to 15) | 36 copies. 23 caught, 8 alarms, 2 missed and 3 quiet. 7 of the 8 alarms are renamed moov boxes and timescales of 0. |
| D. Data (16 to 19) | 52 copies. 38 caught and 14 missed. No copy in this group got a flag from our script. |
Read the grid with care. A flag means a check printed a line, not that a player would refuse the MP4 file. We changed files that were in good shape, and our rules come from the same files and the public set. We damaged 4 MP4 files with 2 codec pairs and tested one browser build.
Where Should the Moov Box Be in an MP4 File? What a cut file gives, and how faststart moves the index
Moov first lets a cut file play up to the cut. In our test an MP4 file cut at 90 percent gave 223 of 250 frames with moov first and none with moov last. Faststart moves moov to the front without a new encode, and the result matched our moov first sample byte for byte.
Every player must find moov before it can play a frame. The standard lets the box come before or after mdat, and many tools write it last because they only know the sizes when the encode ends.
We cut all four of our MP4 files at three points, and the figure shows the result for the two files that differ only in where moov is.
The fragmented file, which has a small moov in front, gave 225, 125 and 25 frames. The AV1 file, with moov first, gave 222, 123 and 21. The moov last file gave 0 at every cut, and FFmpeg said moov atom not found. Cutting removed the only index, and the data that remained had no map.
$ python3 mp4_check.py k06a.mp4
box mdat at 9731 has size 629400 and the file ends at 575217
chunk offset 9739 is outside every mdat
chunk offset 18372 is outside every mdat
the movie lists samples and there is no mdat box
$ python3 mp4_check.py k06b.mp4
box mdat at 40 has size 629400 and the file ends at 575217
no moov box
$ ffprobe -v error k06b.mp4
[mov,mp4,m4a,3gp,3g2,mj2 @ 0x5614418417c0] moov atom not found
k06b.mp4: Invalid data found when processing input
Move moov to the front with faststart
FFmpeg’s format notes describes the faststart option as a second pass that moves the index to the front. It says the pass can take a while and that it does not work with fragmented output. We used it on our moov last file with stream copy, so no video was encoded again.
$ ffmpeg -v error -y -i sample-moovend.mp4 -c copy -movflags +faststart fast.mp4
$ python3 mp4_boxes.py fast.mp4
0 ftyp 32 brand isom, also isom iso2 avc1 mp41
32 moov 9691
9723 free 8
9731 mdat 629400
639131 bytes, 0 left over
$ sha256sum sample.mp4 fast.mp4
84639aba53ff09604c97c7a39ec8e11145783f29eff38c162f46f6fe9d81a97f sample.mp4
84639aba53ff09604c97c7a39ec8e11145783f29eff38c162f46f6fe9d81a97f fast.mp4
The new file has the same SHA256 hash as our moov first sample. Every byte matches, so the decoded frames match. A faststart pass only reorders the boxes and rewrites the offsets in moov. It is the only fix on this page that loses nothing.
Rescue the frames of a cut file
A copy cut at 90 percent still has its moov and most of its data. We copied its streams into a new MP4 file with -c copy.
FFmpeg warned about a partial file, and the new file is complete, with a valid index and 223 frames. Our script found no problem in it. The last 27 frames are gone for good.
$ ffmpeg -v error -i k06a.mp4 -c copy fixed06.mp4
[mov,mp4,m4a,3gp,3g2,mj2 @ 0x5590dd930fc0] stream 1, offset 0x8c7a6: partial file
$ python3 mp4_check.py fixed06.mp4
no problem found
$ ffprobe -count_frames on the video stream of fixed06.mp4
223
The moov last copy cut at 90 percent gave no file at all, because FFmpeg could not find the index. A recovery tool that rebuilds an index from the data needs a good MP4 file of the same camera or encoder as a guide. We did not test such a tool, and we name none.
What Chromium did with the cut AV1 copies
The AV1 file has moov first, so Chromium 141 could start the three cut copies. The copy cut at 90 percent played to the end of its data and showed 222 of 250 frames with no error.
The copy cut at 50 percent showed 123 frames and then stopped with a decode error at 4.7 seconds. The copy cut at 10 percent showed 21 frames and stopped at 0.6 seconds. We did not test a moov last file in Chromium.
We also did not measure how long a player waits for moov on a slow link. Our test only shows that the position decides what is left after a cut. If you save or send video that others will stream, write moov first or fragment the MP4 file, and keep a copy of the original.
What Does Our Expert Say About MP4 Files? A note from Neeraj Singh on keeping the original and its hash
Keep the original of any video you need, and write down its hash. In our test 17 of 99 changed MP4 copies got no flag from our box script or the FFmpeg decode check. Only a hash against the good MP4 file shows them.
Expert insight
An MP4 file is a map and a pile of data, and most damage hides in the pile. In our test one byte inserted at the start of the data gave no frames for 6 of 8 copies. Keep the original, write down its hash and write the index in front.
Why an MP4 file needs a hash
A video looks fine until you play it to the end. Players hide small damage, and our Chromium test showed 6 changed copies that played all 250 frames with no error. A hash of the original answers the question in one line, whether the MP4 file is the same or not.
Write the SHA256 hash when you save the MP4 file, and keep it in a text file next to your video. When you copy or send the MP4 file, hash the copy and compare. If the hashes differ, this copy is not the original, and no player can show how much is wrong.
Keep two things. Keep the original MP4 file and its hash. For a file that others will stream, write moov first with faststart, so that a cut copy still plays up to the cut.
How Do You Open an MP4 File? Use a browser or a player, and FFmpeg to look inside
Open an MP4 file with a browser or a media player that supports the codecs inside it. FFmpeg and MediaInfo show what is inside when a player refuses it. We tested Chromium 141, FFmpeg 6.1.1 and MediaInfo only.
We opened MP4 files with Chromium 141, with FFmpeg 6.1.1, with MediaInfo and with our own scripts on Linux. We did not test VLC, Windows Media Player, QuickTime, desktop Chrome, Edge, Firefox, Safari or a phone. The steps below come from our tests and from the pages that we link.
Open an MP4 file in a browser
- Copy the MP4 file and work on the copy.
- Check the ending. It should be .mp4, but a good MP4 file plays under other endings too.
- Drag the MP4 file into a browser tab, or open it with the browser from your file manager.
- If a player shows an error or only sound, suspect the codec. Go to the codec section.
- If nothing helps, go to the causes part.
Chromium 141 played our AV1 and Opus file for all 250 frames and refused our H.264 and AAC file. So the same container can play in one build and fail in another. The page for a browser lists its codecs, and MDN has a table.
Open an MP4 file in a media player
Microsoft’s page on the MPEG-4 file source says Windows uses it by default for .mp4, .m4a, .m4v and .mov files. Apple’s support page says that HEVC video arrived with iOS 11 and macOS High Sierra. We tested no player on those systems, so treat both pages as claims from their makers.
If a player refuses a file that FFmpeg opens, look at the codec first and at the index second. The check part shows how to list both. A player that supports H.264 and AAC opens most files from cameras and phones. An MP4 file with FLAC, AC4 or DTS audio needs a player that names them.
Look inside an MP4 file with FFmpeg and MediaInfo
The command ffprobe -v error -show_entries stream=codec_name,codec_tag_string,width,height -of csv=p=0 video.mp4 prints codecs, tags and the size of the picture. The codecs part shows its output for our sample. MediaInfo reads the same facts, and we used it through the pymediainfo package.
The command ffmpeg -v error -i video.mp4 -f null - decodes the MP4 file and throws the frames away. Any line it prints is a flag. A file that prints nothing decoded without an error, and that does not prove the pictures are right.
How our tools did on MP4 files
Every tool gets its own row. The numbers cover our 231 public MP4 files and our 132 damaged copies, and the tools that read files on Linux are the ones we used.
| Tool | What we saw |
|---|---|
| FFmpeg 6.1.1 | Opened 225 of the 231 public MP4 files and refused 6. Refused 10 of the 99 changed copies and opened the other 89, so opening is not a pass. |
| MediaInfo | Found tracks in 90 of the 99 changed copies and none in 9. |
| Chromium 141 | Played our AV1 and Opus file and refused the H.264 and HEVC files. Opened the 33 copies of the AV1 file, and 16 of the 24 changed ones ended in an error. We did not open the public MP4 files in it. |
| file 5.45 | Printed 5 different texts for the copies. It reacted to a changed brand and to a removed ftyp box, and to no other damage. |
| Our two scripts | Our boxes script lists top level boxes. Our check script flagged 27 of the 99 changed copies. |
| VLC, Windows Media Player and QuickTime | Not tested. |
| Edge, Chrome, Firefox and Safari | Not tested. The codecs part lists what their makers say. |
How Do You Check an MP4 File? Six steps and two short scripts
Check an MP4 file in six steps. Hash it, read the file command text, list the boxes, use our check script, decode it with FFmpeg and compare the hash with a copy that you trust. Only the last step proves that the data is original.
The six steps to check an MP4 file
-
Copy the MP4 file, write down its SHA256 hash with
sha256sum video.mp4and work on the copy. -
Read the text of the file command with
file video.mp4. A good MP4 file says ISO Media. An MP4 file with another container says so, for example Matroska. - List the boxes with the boxes script below. The first box should be ftyp, a moov box should be there and the last line should say 0 bytes left over.
- Use the check script below. It names the faults that it finds and changes nothing.
-
Decode the MP4 file with
ffmpeg -v error -i video.mp4 -f null -. Any line it prints is a flag. - Compare the hash with the hash of a copy that you trust. If the two differ, this file is not the original.
$ sha256sum sample.mp4
84639aba53ff09604c97c7a39ec8e11145783f29eff38c162f46f6fe9d81a97f sample.mp4
$ file sample.mp4
sample.mp4: ISO Media, MP4 Base Media v1 [ISO 14496-12:2003]
$ wc -c < sample.mp4
639131
$ python3 mp4_check.py sample.mp4
no problem found
$ ffmpeg -v error -i sample.mp4 -f null -
exit code 0
The first line of the file command text shows the family of the container. A hash shows which file you have, and it must match the source before you go on.
The 22 line script that lists the boxes
This Python script does step 3. It reads the size and the name of every top level box and prints them with the brands from ftyp.
It handles a size of 1, where the true size follows in 8 bytes, and a size of 0, where the box goes to the end. It stops when a box does not fit.
#!/usr/bin/env python3
"""mp4_boxes.py lists the top level boxes of an MP4 file.
Use: python3 mp4_boxes.py video.mp4"""
import struct, sys
data = open(sys.argv[1], 'rb').read()
pos = 0
while pos + 8 <= len(data):
size, kind = struct.unpack('>I4s', data[pos:pos + 8])
head = 8
if size == 1:
size, head = struct.unpack('>Q', data[pos + 8:pos + 16])[0], 16
elif size == 0:
size = len(data) - pos
note = ''
if kind == b'ftyp':
note = 'brand ' + data[pos + 8:pos + 12].decode('latin-1') + ', also ' + ' '.join(data[i:i + 4].decode('latin-1') for i in range(pos + 16, pos + size, 4))
print('%9d %-4s %9d %s' % (pos, kind.decode('latin-1'), size, note))
if size < head or pos + size > len(data):
print('stop: box size does not fit the file'); break
pos += size
print('%d bytes, %d left over' % (len(data), len(data) - pos))
On our sample it prints four lines, as the inside part shows. The script changes no file and starts nothing.
The 70 line script that checks the MP4 file
This Python script does step 4. It walks the boxes and the sample tables in moov. It names every fault that it finds, and it changes no file. Use it as python3 mp4_check.py video.mp4.
It flags a first box that is not ftyp and a missing moov. It flags a box that goes past the end of the MP4 file and bytes after the last box.
It also flags a movie timescale of 0 and a moov with no trak. For every track it checks that chunk offsets fall inside an mdat box and that the sample sizes fit.
#!/usr/bin/env python3
"""mp4_check.py names the faults it finds in an MP4 file and changes nothing.
Use: python3 mp4_check.py video.mp4"""
import struct, sys
data = open(sys.argv[1], 'rb').read()
n, problems = len(data), []
def boxes(start, end):
pos = start
while pos + 8 <= end:
size, kind = struct.unpack('>I4s', data[pos:pos + 8])
head = 8
if size == 1:
size, head = struct.unpack('>Q', data[pos + 8:pos + 16])[0], 16
elif size == 0:
size = end - pos
if size < head or pos + size > end:
name = kind.decode('latin1') if kind.isalnum() else 'with a bad name'
problems.append('box %s at %d has size %d and the file ends at %d' % (name, pos, size, end))
return
yield kind, pos + head, pos + size
pos += size
if pos != end and end == n:
problems.append('%d bytes after the last box' % (end - pos))
top = list(boxes(0, n))
kinds = [k for k, a, b in top]
if not top or kinds[0] != b'ftyp':
problems.append('first box is not ftyp')
if b'moov' not in kinds:
problems.append('no moov box')
mdat = [(a, b) for k, a, b in top if k == b'mdat']
samples = 0
for k, a, b in top:
if k != b'moov':
continue
find = lambda box, name: [(x, y) for kk, x, y in boxes(*box) if kk == name]
mvhd = find((a, b), b'mvhd')
if mvhd:
v = data[mvhd[0][0]]
scale = struct.unpack('>I', data[mvhd[0][0] + (20 if v else 12):mvhd[0][0] + (24 if v else 16)])[0]
if scale == 0:
problems.append('movie timescale is 0')
traks = find((a, b), b'trak')
if not traks:
problems.append('moov has no trak')
for t in traks:
stbl = t
for name in (b'mdia', b'minf', b'stbl'):
stbl = (find(stbl, name) or [(0, 0)])[0]
if stbl == (0, 0):
problems.append('a trak has no sample table'); continue
sizes = find(stbl, b'stsz'); offs = find(stbl, b'stco') or find(stbl, b'co64')
if not sizes or not offs:
problems.append('a trak has no stsz or stco box'); continue
w = 4 if find(stbl, b'stco') else 8
cnt = struct.unpack('>I', data[offs[0][0] + 4:offs[0][0] + 8])[0]
for i in range(min(cnt, 100000)):
o = struct.unpack('>I' if w == 4 else '>Q', data[offs[0][0] + 8 + i * w:offs[0][0] + 8 + (i + 1) * w])[0]
if not any(x <= o < y for x, y in mdat):
problems.append('chunk offset %d is outside every mdat' % o); break
fixed, count = struct.unpack('>II', data[sizes[0][0] + 4:sizes[0][0] + 12])
samples += count
total = fixed * count if fixed else sum(struct.unpack('>%dI' % count, data[sizes[0][0] + 12:sizes[0][0] + 12 + 4 * count]))
if mdat and total > sum(y - x for x, y in mdat):
problems.append('samples add up to %d bytes and mdat is smaller' % total)
if samples and not mdat:
problems.append('the movie lists samples and there is no mdat box')
print('\n'.join(problems) if problems else 'no problem found')
The script needs the MP4 file in memory, so a large video needs that much memory. We tested it on files up to 7,839,097 bytes. It decodes nothing, so it cannot see a bad codec or a changed picture.
What the checks cannot see
Our script flagged 46 of the 132 damaged copies, and 27 of those had changed. The FFmpeg decode check flagged 87, and 79 of those had changed. Together they flagged 102, with 82 changed and 20 unchanged. 17 of the 99 changed copies got no flag from either check.
So a report with no flag is not a verdict. A flag is not a verdict either, because 20 flagged copies decoded the same as the original.
Write down the facts of your original MP4 file, which are its size, its SHA256 hash and its box list. A copy that differs in any of them is a different file.
A check never decides whether picture or sound is right. It shows whether the boxes agree with one another, and whether FFmpeg could read them.
Want the causes of a file that will not play? Go to the causes.
Why Will Your MP4 File Not Play? Seven causes and one false alarm
MP4 files fail to play for seven reasons. The codec can be missing or the index can be gone. The start can be damaged, the data can be moved or changed, an offset can be wrong or the ending can be wrong. Junk bytes at the end are a false alarm.
The table lists what we saw in our 132 damaged copies, our 4 built files and Chromium 141. We tested no other player, so read every fix as a first step and not as a final answer.
| Cause | Details and fix |
|---|---|
| The codec is not supported | Chromium 141 refused our H.264 and AAC file and our HEVC and AAC file with error 4, and it played our AV1 and Opus file. Fix: Name the codec with ffprobe, then use a player that supports it. A new copy with a codec your device plays needs a new encode, and that changes the pixels. See the codec test. |
| The index is missing | Our moov last file gave 0 frames at all 3 cuts, and FFmpeg said moov atom not found. A cut file with moov first kept 223, 124 and 23 frames. Fix: Get the MP4 file again from its source. A cut file with moov first can be copied to a new file, as the index section shows. |
| The start is damaged | A size field of 20 on ftyp broke all 4 copies. A removed ftyp changed 3 of 4. A changed brand changed nothing. Fix: List the boxes and compare the first lines with a good MP4 file from the same source. |
| Data moved by one byte | One byte inserted at the start of mdat gave no frames for 6 of 8 copies, and our script said no problem found. Fix: Get the MP4 file again, and compare its hash with the source. |
| An offset or size is wrong | One changed chunk offset changed 8 of 8 copies, and FFmpeg flagged all 8, while our script flagged none. One changed sample size changed 7 of 8. Fix: Get the MP4 file again. We tested no tool that rebuilds an index. |
| Bytes changed in the data | 1 KiB of zeros inside mdat changed all 20 copies. FFmpeg flagged 14, and 6 got no flag from either check. Fix: Compare the hash with the source, and get the MP4 file again if it differs. |
| The ending is wrong | A Matroska file named .mp4 printed Matroska data in the file command, and ffprobe named the format matroska. Our MP4 sample under .mov, .mkv, .m4a, .txt and .dat read the same every time. Fix: Read the text of the file command and rename a copy to match it. |
| Junk bytes at the end | 100 junk bytes at the end changed nothing in 8 copies, and our script flagged all 8. This is a false alarm and not a cause. Fix: None needed. Compare the hash only if the size matters. |
When the check is the problem and not the MP4 file
Our script flagged 19 copies that decoded the same as the original, and 17 changed copies got no flag from either check.
A report with no flag does not prove that an MP4 file is good, and a flag does not prove that it is bad. Test with a second tool before you call an MP4 file good or bad.
Fix an MP4 file in seven steps
- Copy the MP4 file, write down its SHA256 hash and work on the copy.
- Read the text of the file command. If it names another container, rename a copy to match.
- Name the codecs with ffprobe, and compare them with the list of your player.
- List the boxes with the boxes script. A missing moov or a box that does not fit points to a cut or damaged file.
- Use the check script and the FFmpeg decode check. Note every line that they print.
- If moov is last and you need streaming, write it first with faststart. The result is the same file with the boxes in a new order.
- If the MP4 file is cut or the data is damaged, get it again from its source. We tested no recovery tool, so none is promised here.
A file that passes our scripts can still fail in a player. A file that our script flags can still play. Only the player that you use shows what it accepts.
What to send when you ask for help
If a forum or a support team asks about your MP4 file, send these lines. They show what the MP4 file is and where the tools stopped, and they save rounds of questions. Remove names and private data first.
Subject: MP4 file will not play
File: name, size in bytes and SHA256 hash
Made by: the camera, phone or program, and its version if you know it
Check: the output of the file command
Boxes: the output of mp4_boxes.py
Script: the output of mp4_check.py
Decode: the output of ffmpeg -v error -i video.mp4 -f null -
Player: its name and version, and the error you see
History: where the file came from and what changed
Not an error, but a question about similar files? Go to the versus part.
How Do You Convert or Repair an MP4 File? Copy the streams, reorder the boxes or encode again
Copy the streams when you change the container, because a copy keeps every pixel. Faststart only reorders the boxes, and a new encode changes the pixels. We tested copy to MKV and MOV, audio to M4A, AV1 and Opus to WebM, faststart and one new encode with FFmpeg 6.1.1.
This part reports what we tested on our four built files. A converter reads the MP4 file and writes new files, and it leaves the original unchanged. We tested no online converter and no desktop converter.
Copy the streams to another container
The option -c copy moves the video and the audio into a new container without decoding them. We did that with our sample and made an MKV file.
We decoded the video of both files and hashed the frames, and the two hashes match. Then we encoded the sample again with libx264 at CRF 28, and the hash changed.
$ ffmpeg -i sample.mp4 -c copy out.mkv
$ ffmpeg -i sample.mp4 -vn -c:a copy out.m4a
$ pixel hash of sample.mp4
MD5=526f4addb6255ed0f24705ac52926ed1
$ pixel hash of out.mkv
MD5=526f4addb6255ed0f24705ac52926ed1
$ ffmpeg -i sample.mp4 -c:v libx264 -crf 28 -c:a aac re.mp4, then the pixel hash
MD5=c36f22f0ead6cbb8c7e65cffb441a5e4
The sizes differ because every container keeps its own overhead. The MKV copy has 635,469 bytes, which is 3,662 fewer than the original. The MOV copy has 639,182 bytes, which is 51 more. The new encode has 559,224 bytes, which is smaller because it threw away picture detail for good.
$ ffmpeg -v error -y -i sample.mp4 -c copy out.mov
$ wc -c sample.mp4 out.mkv out.mov out.m4a re.mp4
639131 sample.mp4
635469 out.mkv
639182 out.mov
83164 out.m4a
559224 re.mp4
2556170 total
Extract the audio of an MP4 file
The options -vn -c:a copy drop video and copy the audio stream as it is. Our output has 83,164 bytes and the brand M4A. We did not compare the audio samples of the two files, so we do not claim that they match.
$ python3 mp4_boxes.py out.m4a
0 ftyp 28 brand M4A , also M4A isom iso2
28 free 8
36 mdat 80637
80673 moov 2491
83164 bytes, 0 left over
Make a WebM file from an MP4 file
WebM took our AV1 and Opus file, and the output has 684,794 bytes. It refused our H.264 and AAC file, and FFmpeg stopped with exit code 234. So a stream copy to WebM works only when the codecs are on its list.
$ ffmpeg -v error -i sample.mp4 -c copy out.webm
Error opening output files: Invalid argument
exit code 234
$ ffmpeg -v error -i sample-av1.mp4 -c copy av1.webm
exit code 0
$ wc -c sample-av1.mp4 av1.webm
685182 sample-av1.mp4
684794 av1.webm
1369976 total
Change the HEVC tag
An HEVC video track can use the tag hvc1 or hev1. The option -tag:v hev1 rewrote the tag of our MP4 file with -c copy. The size stayed at 613,079 bytes. We did not test which players want which tag.
$ ffmpeg -v error -i sample-hevc.mp4 -c copy -tag:v hev1 hev1.mp4
$ wc -c sample-hevc.mp4 hev1.mp4
613079 sample-hevc.mp4
613079 hev1.mp4
1226158 total
$ ffprobe -v error -show_entries stream=codec_tag_string -select_streams v -of csv=p=0 sample-hevc.mp4
hvc1
$ ffprobe -v error -show_entries stream=codec_tag_string -select_streams v -of csv=p=0 hev1.mp4
hev1
Rename or convert
Renaming changes only the name. Our sample under .mov, .mkv, .m4a, .txt and .dat kept the same SHA256 hash, the same file command text and the same FFmpeg format name. A conversion writes new boxes. If you rename a Matroska file to .mp4, the file command still says Matroska data.
$ cp out.mkv fake.mp4
$ file fake.mp4
fake.mp4: Matroska data
$ ffprobe -v error -show_entries format=format_name -of csv=p=0 fake.mp4
"matroska,webm"
| Task | What we know |
|---|---|
| Faststart | Tested. The new file matched our moov first sample byte for byte. See the faststart part. |
| Copy to MKV | Tested. The decoded video hash matched the original. |
| Copy to MOV | Tested for size only, 639,182 bytes. |
| Audio to M4A | Tested. 83,164 bytes with the brand M4A. |
| AV1 and Opus to WebM | Tested. H.264 and AAC were refused. |
| Rescue a cut file | Tested on a cut file with moov first. We got a valid file with 223 frames. |
| New encode | Tested once with libx264. The pixels changed and the MP4 file shrank. |
| Online and desktop converters | Not tested. |
How Does an MP4 File Differ From MOV, MKV and WebM Files? The same boxes in MOV, M4A and 3GP, and other layouts in MKV and WebM
An MP4 file is a container, and the types next to it are other containers or other kinds of file. MOV, M4A and 3GP use the same box layout. MKV and WebM use Matroska. FLV and SWF are Flash types, and GIF and SRT are pictures and subtitles.
People mix these types up because many of them store video. The table sorts them by what they store and by how they are built. We tested a copy to MKV, MOV, M4A and WebM, and the other rows come from the pages that we link.
| Type | What it is |
|---|---|
| MOV | The QuickTime container. Wikipedia’s page says MP4 is based on the QuickTime file format. Microsoft lists .mov with .mp4 for one file source. Our stream copy to .mov gave 639,182 bytes. |
| M4A and M4B | MP4 files with audio only. Our extract gave an M4A file of 83,164 bytes with the brand M4A. |
| M4V and 3GP | Variants with other brands. Two of our public MP4 files have the brand M4V and one has 3gp6. FFmpeg read them with the same reader as the .mp4 files. |
| MKV and WebM | Matroska containers. Our MKV copy has 635,469 bytes and the file command calls it Matroska data. WebM took AV1 and Opus and refused H.264 and AAC. |
| FLV and SWF | Flash types. Our FLV file guide and our SWF movie guide cover them. |
| GIF and SRT | Animated pictures and subtitle text. Our animated GIF guide and our SRT subtitle guide cover them. |
MP4, MOV, M4A and 3GP share the box layout, so one tool reads all four, and FFmpeg gave them one format name. MKV and WebM do not use boxes, so a tool for one family does not open the other. The file command names the difference in its first line.
What Is the MP4 File Format Made Of? The facts we could check, and where they came from
An MP4 file is a stream of boxes that ISO/IEC 14496-14 defines on top of the base media format. The standard names one ending, and RFC 4337 names three media types. The facts below come from those sources, PRONOM and our checks of 4 built files, 132 copies and 231 public MP4 files.
| Part | What we checked |
|---|---|
| Name | MPEG-4 Part 14, the MP4 file format. PRONOM lists it as MPEG-4 Media File with the endings mp4, m4v, m4a, f4v, f4a and m4b. (ISO, PRONOM) |
| Specification | ISO/IEC 14496-14, third edition, from 27 January 2020. It builds on the ISO base media file format, and Wikipedia says it is based on QuickTime. (ISO, Wikipedia) |
| Endings | .mp4 is the one that the standard defines. .m4a, .m4b, .m4p, .m4r and .m4v are habits. (Wikipedia, PRONOM) |
| Media types | video/mp4, audio/mp4 and application/mp4, registered in RFC 4337 from March 2006. RFC 6381 from August 2011 adds the codecs parameter. (RFC Editor) |
| Signature | RFC 4337 and the Library of Congress list none. An MP4 file starts with a size and the letters ftyp. PRONOM matched 185 of our 231 public MP4 files. (our MP4 files) |
| Byte order |
Big endian. The size 32 is written as 00 00 00 20. (our MP4 files)
|
| Box header | A 4 byte size and a 4 byte name. A size of 1 means an 8 byte size follows. A size of 0 means the box goes to the end of the MP4 file. (our MP4 files) |
| ftyp | A major brand and a list of compatible brands. The 231 public MP4 files use 13 major brands, and isom leads with 110. (our MP4 files) |
| moov and mdat | moov is the index and mdat is the data. Of 95 unfragmented public MP4 files, 78 keep moov last and 17 keep it first. (our MP4 files) |
| Fragments | A small moov, then pairs of moof and mdat. 84 of the 231 public MP4 files are fragmented. (our MP4 files) |
| Codec tags | A 4 letter tag in the stsd box. avc1 is the most common on public video tracks, with 104 of them. (our MP4 files) |
| Our counts | 4 built files, 132 damaged copies, 231 public MP4 files, a 22 line boxes script and a 70 line check script. |
About the values we list
Rules about the format come from ISO, and the registry facts come from the PRONOM entry and from the MP4 media type RFC. Counts come from our MP4 files and scripts. Where sources disagree or say nothing, we say so, and a source in brackets after a value shows where it came from.
What we left out
We damaged 4 MP4 files, and every test changed one thing. An MP4 file with many tracks, subtitles or other codecs can fail in other ways. We read files from the test folders of Chromium and Firefox, and we did not read an MP4 file from a camera, a phone or a screen recorder.
We did not test VLC, Windows Media Player, QuickTime, desktop Chrome, Edge, Firefox, Safari or a phone. We did not test any recovery tool, and we did not test a moov last file in Chromium.
We could not read the IANA record for video/mp4, so we make no claim about it. We did not test which players need hvc1 and which accept hev1.
We did not watch or listen to any file, so every comparison uses hashes of decoded frames and not our eyes. We also did not measure the start time of an MP4 file on a slow link, and we did not test MP4 files with encryption.
Sources checked
For the format, see ISO’s page for Part 14, Wikipedia’s page on the MP4 file format and the Library of Congress record. For the registry, see PRONOM’s record, RFC 4337 itself and the codecs parameter RFC.
For players, see MDN’s guide to containers, Chromium’s audio and video page, Microsoft’s page on the MPEG-4 file source and Apple’s HEVC page. For tools, see FFmpeg’s format documentation and FileInfo’s page for .mp4. Every count came from files and scripts in our research folder.
Frequently Asked Questions About MP4 Files
What is an MP4 file?
An MP4 file is a container for video, audio and text. It is a row of boxes, and the ftyp, moov and mdat boxes matter most. ISO/IEC 14496-14 defines the format, and the ending is .mp4. The ending names the container and no codec.
What does MP4 stand for?
MP4 is the short name of MPEG-4 Part 14, which defines the file format in the MPEG-4 standard. ISO lists it as ISO/IEC 14496-14 and shows a third edition from January 2020. The name says nothing about the codec inside.
Is MP4 a codec or a container?
It is a container. It stores video and audio that a codec squeezed, plus an index to them. Our Chromium 141 test played an MP4 file with AV1 and Opus and refused two others, one with H.264 and one with HEVC. So the codec inside decides what plays.
How do I open an MP4 file?
Open it with a browser or a media player that supports the codecs inside. FFmpeg and MediaInfo show what is inside when a player refuses it. We tested Chromium 141, FFmpeg 6.1.1 and MediaInfo, and we did not test VLC, Windows Media Player or QuickTime.
Why will my MP4 file not play?
The causes in our tests were a codec that the player lacks, a missing index and changed data. Chromium 141 refused two of our three codec pairs. A cut file with moov last gave no frames, and one byte inserted at the start of the data gave none for 6 of 8 copies.
What is the difference between MP4 and MOV?
Wikipedia says MP4 is based on the QuickTime file format that MOV uses. Microsoft says Windows reads both through one file source. Our stream copy of an MP4 file to .mov gave 639,182 bytes, which is 51 bytes more than the original. We tested no player with the two.
What is the moov box in an MP4 file?
The moov box is the index. It lists every track and every sample, with their sizes and their positions in the MP4 file. Ours has 9,691 bytes in an MP4 file of 639,131. Without it a player cannot find a frame, and our moov last file gave no frames when we cut it.
What does faststart do?
Faststart moves the moov box to the front of the MP4 file. FFmpeg does it in a second pass, and it does not work with fragmented output. On our moov last file it made an MP4 file with the same SHA256 hash as our moov first sample, so nothing was lost.
Can I convert an MP4 file to MKV without losing quality?
Yes, when you copy the streams and do not encode them again. With the option -c copy our MKV copy had the same decoded video hash as the original. A new encode with libx264 at CRF 28 changed the pixels and gave an MP4 file of 559,224 bytes.
How do I check if an MP4 file is damaged?
Write down its SHA256 hash, list its boxes and decode it with FFmpeg. Our two checks flagged 82 of the 99 damaged copies that decoded differently, and 17 got no flag from either one. Only a hash of a copy that you trust shows damage in the data.
Does an MP4 file have a signature?
RFC 4337 and the Library of Congress list none. An MP4 file starts with a 4 byte size and the letters ftyp, and our sample starts with 00 00 00 20 66 74 79 70. PRONOM gives a pattern that matched 185 of our 231 public MP4 files, so a file that does not match can still play.
Can I repair a damaged MP4 file?
It depends on the damage. A copy cut at 90 percent that had moov first gave a new valid file of 223 frames when we copied its streams. A cut file with moov last gave nothing. We tested no recovery tool, so we name none.
Still Stuck With an MP4 File? Two routes out, and where to look next
Name the codec and list the boxes, and follow the seven steps. The cause table lists what to try in order.
Look at how an MP4 file is built, and then start our check script.
Is it a Flash video? An FLV file stores Flash video, and an SWF file stores a Flash movie. Our page on FLV files and our page on SWF files cover them, and the versus part shows how they differ from MP4.
Is it a picture or a subtitle? An animated picture is a GIF file, and subtitle text is an SRT file. Our page on GIF files and our page on SRT files cover the two.
Is the data unknown? Our page on DAT files covers files with no clear type, and the file extension resource library lists many more.
Is your type missing? Send it on the submit a file type page.