Recursive AI

Read MKV and WebM file info in C#

All onemedia recipes

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).

Terminal
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:

C#
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).

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.

To list every track with its language and flags, see List the tracks of a video.