---
title: "Read MKV and WebM file info in C#"
url: https://recursiveai.org/onemedia/cookbook/read-mkv-webm-info/
description: "Read an MKV or WebM file’s duration, tracks, codecs, title and Matroska tags in C#, with the Segment info (muxing and writing app, date) and tag targets, without MKVToolNix or FFmpeg."
version: "0.1.0-preview.1"
---

# Read MKV and WebM file info in C#

`Media.Open` reads Matroska (`.mkv`, `.mka`, `.mk3d`) and WebM the same way as MP4: duration, tracks and codecs, a common tag view, and every Matroska tag with its target. The inputs are a WebM clip from MDN’s CC0 sample videos and `test5.mkv` from the Matroska project’s test files, 32 MB, cut from Elephants Dream (CC BY: © copyright 2006, Blender Foundation / Netherlands Media Art Institute / www.elephantsdream.org).

```shell
dotnet new console -o MkvInfo && cd MkvInfo
dotnet add package RecursiveAI.OneMedia --version 0.1.0-preview.1
curl -L -o flower.webm https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm
curl -L -o test5.mkv https://raw.githubusercontent.com/ietf-wg-cellar/matroska-test-files/master/test_files/test5.mkv
```

Put this in `Program.cs` and run `dotnet run`:

```csharp
using RecursiveAI.OneMedia;
using RecursiveAI.OneMedia.Matroska;
using RecursiveAI.OneMedia.Tags;

foreach (string path in new[] { "flower.webm", "test5.mkv" })
{
    using MediaFile media = Media.Open(path);

    // Both are "matroska"; the EBML header's DocType tells WebM apart.
    string docType = (media.Demuxer as MatroskaDemuxer)?.Header.DocType ?? "?";
    Console.WriteLine($"{path}: {docType}, {media.Duration}, {media.Tracks.Count} tracks");
    foreach (MediaTrack t in media.Tracks.Take(3))
        Console.WriteLine($"  track {t.Index}: {t.Kind} {t.Codec} [{t.CodecTag}]");

    // Common fields, then each native tag: Segment Info (mkv-info) and Tags (mkv).
    Console.WriteLine($"  title: {media.Tags.Title}");
    foreach (NativeTag tag in media.Metadata.NativeTags)
    {
        string target = tag.TargetTypeValue is long level ? $" (target {level})" : "";
        Console.WriteLine($"  {tag.System}:{tag.Key}{target} = {tag.Text}");
    }
}
```

```text
flower.webm: webm, 00:00:05.0590000, 2 tracks
  track 0: Video vp8 [V_VP8]
  track 1: Audio vorbis [A_VORBIS]
  title:
  mkv-info:MuxingApp = Lavf57.71.100
  mkv-info:WritingApp = Lavf57.71.100
test5.mkv: matroska, 00:00:46.6650000, 11 tracks
  track 0: Video h264 [V_MPEG4/ISO/AVC]
  track 1: Audio aac [A_AAC]
  track 2: Subtitle subrip [S_TEXT/UTF8]
  title: Big Buck Bunny - test 8
  mkv-info:MuxingApp = libebml v1.0.0 + libmatroska v1.0.0
  mkv-info:WritingApp = mkvmerge v4.0.0 ('The Stars were mine') built on Jun  6 2010 16:18:42
  mkv-info:DateUTC = 2010-08-21T18:06:43Z
  mkv:TITLE (target 50) = Big Buck Bunny - test 8
  mkv:DATE_RELEASED (target 50) = 2010
  mkv:COMMENT (target 50) = Matroska Validation File 8, secondary audio commentary track, misc subtitle tracks
```

The tags are what each file stores: `test5.mkv` is titled “Big Buck Bunny - test 8” although its pictures come from Elephants Dream, and `flower.webm` has no title at all.

`mkv-info` holds the Segment Info element: the title, the muxing library, the writing application and `DateUTC`, when the file was muxed. `mkv` holds the `Tags` element: each `SimpleTag` with its target level (50 is the whole movie or album, 30 a track or chapter, per RFC 9559) and the track it targets, if any (`tag.TrackId`). Nested tags are keyed `PARENT/CHILD`.

The common view (`media.Tags`) takes only tags that target the whole file, plus a single-track file’s track tags, and puts the lowest level of 30 or above first. Everything else stays on the native list. Attachments, such as cover art and fonts, are listed as native tags of kind `Image` or `Binary`, and their bytes are read only when you call `tag.ReadValue()`.

## Streams and remote files

`Media.Open(stream)` takes any readable, seekable `Stream` and throws `ArgumentException` for one that can’t seek. The Matroska reader also has a live, forward-only mode for streams that can’t seek, through the lower-level `StreamForwardSource`; it is not behind `Media.Open` in 0.1. For a file on a web server, `Media.OpenAsync` reads byte ranges (see [Read video metadata from a URL](https://recursiveai.org/onemedia/cookbook/read-video-metadata-from-url/)).

## Limits

- **Reading only.** No writing, remuxing or tag editing in 0.1.
- **Subtitles** are listed as tracks, with their packets, but not converted or parsed: subtitles are not in this release.
- **Track durations** are unknown for Matroska: the container states the Segment’s duration only, so `track.Duration` is unknown and `media.Duration` is the Segment’s.
- **Chapters** in Matroska `Chapters` elements are not read yet.
- **Known differences** from ffprobe, MKVToolNix and MediaInfo (attachments are not streams, RealAudio is not de-interleaved, RealVideo timestamps are as stored) are listed in [Matroska and WebM](https://recursiveai.org/onemedia/docs/matroska-differential/).

To list every track with its language and flags, see [List the tracks of a video](https://recursiveai.org/onemedia/cookbook/list-video-tracks/).
