MKV File Documentation
Summary
An MKV (Matroska Video) file is an open, royalty-free multimedia container that stores video, several audio tracks, subtitles, chapters and attachments in one file. Its extension is .mkv and its MIME type is video/x-matroska. Matroska is built on the EBML binary format standardised as IETF RFC 9559 (2024). Windows Media Player and VLC play it natively; a file a TV rejects can usually be remuxed to MP4 without re-encoding.
Technical details
| Feature | Value |
|---|---|
| Full name | Matroska Video |
| File extension | .mkv |
| MIME type | video/x-matroska |
| Format type | EBML-based multimedia container (binary) |
| Base format | EBML (Extensible Binary Meta Language), IETF RFC 8794 |
| Developer | Matroska.org (project started by Steve Lhomme) |
| Introduced | 2002 (fork of the MCF project) |
| Standard | IETF RFC 9559 (Matroska, 2024); EBML in RFC 8794 (2020) |
| Open standard | Yes — royalty-free, published spec |
| Byte order | Big-endian (EBML element IDs and sizes) |
| Magic number (hex) | 1A 45 DF A3 (EBML header) at offset 0 |
| DocType | matroska (WebM uses webm) |
| Video codecs | H.264/AVC, H.265/HEVC, VP9, AV1, MPEG-2 |
| Audio codecs | AAC, AC-3, DTS, FLAC, Opus, Vorbis, MP3 |
| Subtitle formats | SRT, ASS/SSA, PGS, VobSub (multiple tracks) |
| Chapters | Yes (ordered chapters and editions) |
| Attachments | Yes (embedded fonts, cover art) |
| Default timestamp scale | 1,000,000 ns (millisecond precision) |
| Related extensions | .mka, .mks, .mk3d, .webm |
| Specification | matroska.org/technical/elements.html |
What is an MKV file?
MKV is the video profile of Matroska, an open, royalty-free multimedia container started in 2002 as a fork of the abandoned Multimedia Container Format (MCF) project by Steve Lhomme and others. A container is not a codec: an .mkv file wraps separately encoded streams — video (H.264/AVC, H.265/HEVC, VP9, AV1), audio (AAC, AC-3, DTS, FLAC, Opus), subtitles (SRT, ASS/SSA, PGS), plus chapters, attachments and metadata — and records how they interleave and when each piece plays. Its MIME type is video/x-matroska.
Matroska is built on EBML (Extensible Binary Meta Language), a binary analogue of XML that the IETF standardised as RFC 8794 in 2020; the Matroska container itself was published as RFC 9559 in 2024 through the IETF CELLAR working group. Everything below is about how EBML encodes that structure and how the Matroska elements sit inside it. Google’s WebM (2010) is a constrained subset of the same format, limited to VP8/VP9/AV1 video and Vorbis/Opus audio.
EBML: element IDs and variable-length integers
An EBML document is a tree of elements, and every element is stored the same way: an Element ID, then a data size, then the payload (which for a master element is more elements). There are no tags to close and no whitespace; the size field tells a parser exactly how many bytes the element spans, so unknown elements are skipped by jumping over them. That is what lets a decade-old parser read a file using elements defined after it shipped.
Both the ID and the size use a VINT (variable-length integer) whose leading byte announces its own length. The parser counts the leading zero bits before the first 1: zero leading zeros means a 1-byte value, one leading zero means 2 bytes, and so on. That first 1 is a marker bit and is not part of the value.
Length Leading pattern (first byte) Usable value bits
1 byte 1xxx xxxx 7
2 byte 01xx xxxx xxxx xxxx 14
3 byte 001x xxxx xxxx xxxx xxxx xxxx 21
4 byte 0001 xxxx ... 28
For an Element ID the marker bits are kept as part of the ID, which is why the IDs look the way they do in a hex dump. The EBML header ID is 1A 45 DF A3: the leading byte 0x1A is 0001 1010, three leading zeros, so it is a 4-byte ID. A data size instead strips the marker bit and uses the remaining bits as the length; an all-ones size (e.g. 0xFF in a 1-byte size) means “unknown length”, used for live streams where the total is not known in advance. All values are big-endian.
The EBML header and the DocType
Every Matroska file opens with an EBML header element (0x1A45DFA3) that describes the document before any media is seen. It carries EBMLVersion and EBMLReadVersion, the maximum ID and size lengths the file uses, and, critically, a DocType string with DocTypeVersion.
1A 45 DF A3 EBML header (master)
42 86 .. 01 EBMLVersion = 1
42 F7 .. 01 EBMLReadVersion = 1
42 82 .. "matroska" DocType
42 87 .. 04 DocTypeVersion = 4
42 85 .. 02 DocTypeReadVersion = 2
The DocType is the field that distinguishes formats sharing the EBML base. It reads matroska for MKV/MKA and webm for a WebM file. Because the raw four header bytes are identical for both, a tool that only checks magic bytes cannot tell an MKV from a WebM; it must read the DocType string a few bytes in. After the header comes exactly one Segment element (0x18538067), the root that holds everything else.
The Segment and its top-level elements
The Segment is a single master element that contains the whole movie. Its direct children are the Top-Level elements, each with a fixed 4-octet ID, and a SeekHead near the front acts as an index of where the others live so a player does not have to scan the file linearly.
| Element | ID (hex) | Holds |
|---|---|---|
| SeekHead | 114D9B74 | Offsets of the other top-level elements (the seek index) |
| Info | 1549A966 | TimestampScale, Duration, Title, muxing app |
| Tracks | 1654AE6B | One TrackEntry per video/audio/subtitle stream |
| Cluster | 1F43B675 | The actual interleaved media data, in timed blocks |
| Cues | 1C53BB6B | Seek points: timestamp → Cluster position |
| Chapters | 1043A770 | Named chapter points, editions, ordered chapters |
| Attachments | 1941A469 | Embedded files: subtitle fonts, cover art |
| Tags | 1254C367 | Metadata: title, episode, encoder, ratings |
The Info element carries the TimestampScale, whose default is 1,000,000 nanoseconds, meaning block timestamps are expressed in milliseconds. Duration is a floating-point count of those units. Every timestamp elsewhere in the file is interpreted against this single scale.
Tracks: TrackEntry, CodecID and CodecPrivate
The Tracks element defines each stream once, up front, in a TrackEntry. A TrackEntry gives the TrackNumber (referenced later by every block), a TrackType (1 = video, 2 = audio, 17 = subtitle), a Language (ISO 639), a human Name, and default/forced flags a player uses to pick a track automatically.
The codec is named by a CodecID string, not a numeric tag: V_MPEG4/ISO/AVC for H.264, V_MPEGH/ISO/HEVC for H.265, V_VP9, V_AV1, A_AAC, A_AC3, A_DTS, A_OPUS, S_TEXT/ASS for styled subtitles. Codec initialisation data that the decoder needs (for H.264 the SPS/PPS parameter sets, for FLAC the STREAMINFO block) is stored in a CodecPrivate element inside the TrackEntry. Because tracks are declared here rather than per-frame, Matroska places no limit on how many audio or subtitle tracks a file may carry, which is why multi-language rips and anime fansubs favour it.
Clusters, SimpleBlock and lacing
The coded media lives in a series of Cluster elements (0x1F43B675). Each Cluster begins with a Timestamp element (ID 0xE7) giving the cluster’s base time in TimestampScale units; every block inside then stores only a small signed offset from that base, keeping timestamps compact.
A frame is carried in a SimpleBlock (ID 0xA3) or, when extra per-frame data is needed, a BlockGroup (0xA0) wrapping a Block plus a BlockDuration or reference. The block’s body starts with the track number (a VINT), a 16-bit signed timestamp offset, and a flags byte whose top bit marks a keyframe.
SimpleBlock (0xA3) payload:
[VINT] track number
int16 timestamp offset from Cluster Timestamp
uint8 flags (bit 7 = keyframe; bits 1-2 = lacing type)
... frame data (one or more frames if laced)
Lacing is Matroska’s trick for packing several small frames into one block to cut overhead, common for audio. The flags byte selects no lacing, Xiph lacing, fixed-size lacing or EBML lacing; the block then stores the frame count and the size of each laced frame before the concatenated frame data. A player reading a block splits it back into individual frames using those sizes.
Cues: seeking without scanning
Random access uses the Cues element (0x1C53BB6B), a table of CuePoint entries. Each CuePoint pairs a CueTime with a CueTrackPositions that names a track and the byte position of the Cluster holding the keyframe at that time. To seek, a player looks up the nearest CueTime at or before the target, jumps straight to that Cluster’s offset, and starts decoding from its keyframe. Without Cues a player would have to walk Clusters from the start, so a file muxed without a cue index seeks slowly even though it plays fine linearly. The SeekHead serves the analogous purpose one level up, indexing the top-level elements themselves.
Container versus codec, and lossless remuxing
The .mkv wrapper and the compressed bitstreams it holds are separate layers. Matroska defines the elements, tracks, timing and block layout; the codec defines how the bytes inside each block are compressed. This is why a file that “won’t play” is rarely a broken container: the element tree parses fine and the player simply lacks a decoder for what a CodecID names, most often HEVC video or DTS audio on a TV.
It is also why converting MKV to MP4 is usually a remux, not a re-encode. If the streams are already MP4-compatible (H.264/H.265 video, AAC/AC-3 audio) the coded frames are copied byte-for-byte into the ISO BMFF box structure with no quality loss, in seconds. Re-encoding is only needed when a stream is genuinely incompatible with the target container. The catch is subtitles and attachments: MP4 carries only mov_text, so ASS/SSA styling and image-based PGS tracks that Matroska holds natively are dropped or must be OCR’d or burned in. Tools like MKVToolNix operate directly on the element tree to inspect, split, merge and remux without touching the media payload.
References
- Matroska.org — Technical element specifications
- IETF RFC 9559 — Matroska Media Container Format
- IETF RFC 8794 — Extensible Binary Meta Language (EBML)
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.