mediacodec.dev

Codec profiles and levels

Why a valid H.264 file refuses to play on a TV, and what profile, level and tier actually constrain.

Updated

A file can be perfectly valid H.264 and still refuse to play on a device that plays H.264. The reason is almost always a profile or level mismatch, and the error message rarely says so.

Profiles: which tools are allowed

A profile is a named subset of the codec’s toolbox. Every decoder claiming a profile must implement everything in it, so a profile is a contract about what an encoder is permitted to use.

For H.264 the ones you meet are:

Baseline forbids B-frames and CABAC entropy coding. It exists for hardware too weak to reorder frames, and it costs perhaps 10 to 15 percent efficiency. Video calls used it for years; almost nothing needs it now.

Main adds B-frames, CABAC and interlacing support. The old broadcast default.

High adds 8x8 transforms and custom quantisation matrices, and is what essentially everything uses today. It is x264’s default, it plays everywhere that matters, and choosing anything else needs a reason.

High 10 / High 4:2:2 / High 4:4:4 add 10-bit depth and richer chroma sampling. These are the trap: they are legitimate H.264, and consumer hardware decoders overwhelmingly do not support them. A 10-bit H.264 file will play in VLC on a laptop and fail on a TV. HEVC made 10-bit part of its Main 10 profile, which is why 10-bit is normal there and exotic in H.264.

Levels: how much of it

A level caps the numbers: maximum resolution, frame rate, bitrate, and decoded picture buffer size. Level 4.0 covers 1080p30, level 4.2 covers 1080p60, level 5.1 covers 4K30, level 6.x covers 8K. A decoder advertising level 4.0 is promising it can keep up with that much data and no more.

HEVC adds tiers, Main and High, splitting each level by bitrate ceiling. Main tier is consumer; High tier is for contribution and mastering, where bitrates run far above anything you would stream.

Levels are the reason “it plays on my phone but not my TV” happens with two files at the same resolution: a higher frame rate or a higher peak bitrate can push a file over a level boundary the TV enforces.

Where it bites in practice

Old smart TVs and set-top boxes enforce profile and level strictly, because their decoders are fixed silicon with real limits.

Safari and iOS historically refused files above their supported profile rather than degrading, though modern devices are permissive by comparison.

Hardware decoders in general fail hard, not gracefully. Software players like VLC and mpv will often decode anything, which is exactly why testing only in VLC is how these bugs reach production.

Setting them

With x264 and x265, ask FFmpeg for the constraint and the encoder will keep within it:

ffmpeg -i input.mp4 -c:v libx264 -profile:v high -level 4.0 -crf 20 output.mp4

Two things are worth knowing. Constraining the level can force the encoder to raise the bitrate or drop reference frames to comply, so an aggressive level cap is not free. And -profile:v is silently ignored if the pixel format demands more: encoding 10-bit input while asking for High gives you High 10 anyway. Add -pix_fmt yuv420p when you need the guarantee, which is the single most useful compatibility flag in FFmpeg and the reason it appears in the convert to MP4 command.

The safe recipe for “must play absolutely everywhere” is unchanged after a decade: H.264 High profile, level 4.0 or lower, yuv420p, AAC audio, MP4 container with +faststart.