Skip to content

Video codec support in browsers and devices

current

Checking a codec name is not enough. A file plays only if the browser handles the whole combination: codec, profile, level or tier, bit depth, chroma, container and audio codec, through the playback API you use.

FormatNative browser playbackCheck on your targets
AVC / H.264All major browsers. Chromium builds without proprietary codecs lack it.Profile, level and MP4 setup
HEVC / H.265Safari. Chrome only with a hardware decoder. Mozilla lists Firefox support as limited.Hardware and OS availability, hvc1 versus hev1, depth, chroma and HDR display behavior
VP8Chrome and Firefox. Safari on macOS since 14.1, on iOS and iPadOS since 17.4.File playback versus WebRTC, audio track and WebM/MSE path
VP9Chrome and Firefox. Safari on macOS since 14.1, on iOS and iPadOS since 17.4.Profile 0 versus 2, 8/10/12-bit, chroma, HDR and hardware decode
AV1Chrome and Firefox. Safari 17+ only on devices with hardware AV1 decoding.Device, profile, depth, level, software decode speed and container
TheoraLegacy. Firefox disabled it by default in 126, and Chromium no longer lists it.Not a web fallback any more
Everything elseNone: VVC, AV2, AVS3, VC-1, FFV1, ProRes, Ut Video, DSV, DSV2, ECMShip a web rendition in one of the codecs above, or bring your own application decoder

Sources: Chromium’s codec list, Mozilla’s supported-format table, Firefox 126 release notes, and WebKit’s Safari 17.0 and Safari 17.4 notes. Edge and other Chromium browsers follow Chrome, except where the vendor removes proprietary codecs.

PathAppropriate checkWhat a positive result leaves unresolved
Native file / HTML videoHTMLMediaElement.canPlayType() and an actual fileDecode rate, seeking, timestamps, audio, HDR and display correctness
Media Source Extensions (MSE)MediaSource.isTypeSupported() for the intended byte-stream typeSegmentation, initialization data, append boundaries and adaptive switching
WebCodecsVideoDecoder.isConfigSupported() for the exact codec configurationDemuxing, timestamps, audio synchronization and frame rendering
JavaScript / WebAssembly codecApplication-specific decode testIntegration, CPU and memory budget, battery cost and renderer correctness

The same browser can give different answers for each path. A WebCodecs decoder does not demux MP4 or play audio by itself, and a software WASM decoder does not add to the browser’s native codec list. The WebCodecs specification defines the configuration queries and the encode and decode model.

Run this in the target page’s browser console. The configurations are examples. Derive the real codec string, dimensions, bitrate and frame rate from your encoded file, and use its container’s MIME type.

const candidates = [
{ name: 'AVC High 8-bit', contentType: 'video/mp4; codecs="avc1.640028"' },
{ name: 'HEVC Main 10', contentType: 'video/mp4; codecs="hvc1.2.4.L120.B0"' },
{ name: 'VP9 profile 0', contentType: 'video/webm; codecs="vp09.00.40.08"' },
{ name: 'VP9 profile 2', contentType: 'video/webm; codecs="vp09.02.40.10"' },
{ name: 'AV1 Main 10-bit', contentType: 'video/mp4; codecs="av01.0.08M.10"' },
];
const element = document.createElement('video');
for (const candidate of candidates) {
const result = {
name: candidate.name,
canPlayType: element.canPlayType(candidate.contentType),
mse: globalThis.MediaSource?.isTypeSupported(candidate.contentType) ?? null,
};
if (navigator.mediaCapabilities?.decodingInfo) {
try {
result.mediaCapabilities = await navigator.mediaCapabilities.decodingInfo({
type: 'file',
video: {
contentType: candidate.contentType,
width: 1920,
height: 1080,
bitrate: 6_000_000,
framerate: 30,
},
});
} catch (error) {
result.error = String(error);
}
}
console.log(result);
}

canPlayType() returns an empty string, maybe or probably. Media Capabilities adds supported, smooth and powerEfficient estimates. These can be unavailable, and they do not certify an HDR display, a specific decoder, or every frame of a file. For MSE, query with type: 'media-source' and your real configuration.

A WebCodecs check uses the same idea with an AV1 configuration:

if (globalThis.VideoDecoder?.isConfigSupported) {
const result = await VideoDecoder.isConfigSupported({
codec: 'av01.0.08M.10',
codedWidth: 1920,
codedHeight: 1080,
hardwareAcceleration: 'prefer-hardware',
});
console.log(result);
}

prefer-hardware is a preference: a supported: true result can still mean software decoding. For AVC, supply the AVC configuration data when your input needs it. The AVC registration distinguishes AVC-format input from Annex B, and the AV1 registration defines AV1 codec strings and chunk expectations. Handle unsupported configurations and decode errors in code rather than choosing a codec from a user-agent string.

Hardware encoding and decoding are separate capabilities. Check the GPU or SoC model, OS, driver, application backend, profile, depth, chroma, resolution and frame-rate limit. The vendor matrix lists encode and decode support per sample format, and a newer chip in a product family is not evidence for an older one.

For each rendition you deliver:

  1. Inspect the final file with ffprobe -show_streams -show_format, and note the codec, profile, pixel format, dimensions, frame rate and color metadata.
  2. Run a full decode with an independent software decoder to catch stream errors before device testing.
  3. Play the exact file on representative browsers and devices, including low-power and older supported targets. Check startup, seeking, audio sync, captions, adaptive switching and dropped frames.
  4. Compare SDR range and matrix, and HDR output, against a known-good source and display path. A picture on screen does not confirm color or tone mapping.

Keep a fallback rendition, and handle playback errors without breaking the page.

For preservation and editing codecs, keep the original or a lossless master, and encode delivery renditions separately. For AV2, ECM or custom codecs, use the matching decoder revision and state the research or deployment constraints of the output.