media.Tracks lists every track of an MP4, MOV, MKV or WebM file with its kind, codec, language, name and flags, in file order. The input is test5.mkv from the Matroska project’s test files: one video track, two audio tracks and eight subtitle tracks, cut from Elephants Dream (CC BY: © copyright 2006, Blender Foundation / Netherlands Media Art Institute / www.elephantsdream.org).
dotnet new console -o ListTracks && cd ListTracks dotnet add package RecursiveAI.OneMedia --version 0.1.0-preview.1 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:
using RecursiveAI.OneMedia; using MediaFile media = Media.Open("test5.mkv"); Console.WriteLine("# kind codec lang name flags"); foreach (MediaTrack t in media.Tracks) { string detail = t.Video is { } v ? $" {v.Width}x{v.Height}" : t.Audio is { } a ? $" {a.SampleRate} Hz {a.Channels} ch" : ""; Console.WriteLine($"{t.Index,-3} {t.Kind,-9} {t.Codec,-7} {t.Language,-5} " + $"{t.Name ?? "",-11} {t.Disposition}{detail}"); } // Pick tracks the way a player would. MediaTrack? english = media.Tracks.FirstOrDefault(t => t.Kind == TrackKind.Subtitle && t.Language == "eng"); MediaTrack? secondAudio = media.Tracks.Where(t => t.Kind == TrackKind.Audio) .Skip(1).FirstOrDefault(); Console.WriteLine($"English subtitles: track {english?.Index}"); Console.WriteLine($"Second audio: track {secondAudio?.Index}, \"{secondAudio?.Name}\"");
# kind codec lang name flags 0 Video h264 und Default 1024x576 1 Audio aac und Default 48000 Hz 2 ch 2 Subtitle subrip eng Default 3 Subtitle subrip hun None 4 Subtitle subrip ger None 5 Subtitle subrip fre None 6 Subtitle subrip spa None 7 Subtitle subrip ita None 8 Audio aac eng Commentary None 44100 Hz 1 ch 9 Subtitle subrip jpn None 10 Subtitle subrip und None English subtitles: track 2 Second audio: track 8, "Commentary"
Index is the track’s position in media.Tracks and the number every other call uses. Id is the number the container gives it (the Matroska TrackNumber or the MP4 track_ID), which need not be in order: here track 10 has Id 7. Language is the code as stored: Matroska and MP4 use ISO 639-2 (eng, ger, fre), and und means undetermined. Disposition holds the default, forced, commentary, hearing-impaired, original and similar flags the file sets.
Check it with ffprobe
ffprobe lists the same tracks in the same order:
ffprobe -v error -show_entries stream=index,codec_type,codec_name:stream_tags=language,title -of compact test5.mkvstream|index=0|codec_name=h264|codec_type=video stream|index=1|codec_name=aac|codec_type=audio stream|index=2|codec_name=subrip|codec_type=subtitle|tag:language=eng stream|index=3|codec_name=subrip|codec_type=subtitle|tag:language=hun stream|index=4|codec_name=subrip|codec_type=subtitle|tag:language=ger stream|index=5|codec_name=subrip|codec_type=subtitle|tag:language=fre stream|index=6|codec_name=subrip|codec_type=subtitle|tag:language=spa stream|index=7|codec_name=subrip|codec_type=subtitle|tag:language=ita stream|index=8|codec_name=aac|codec_type=audio|tag:language=eng|tag:title=Commentary stream|index=9|codec_name=subrip|codec_type=subtitle|tag:language=jpn stream|index=10|codec_name=subrip|codec_type=subtitle
Limits
- Listing, not extracting subtitles. Subtitle tracks are listed and their packets can be read with
media.Packets(track), but OneMedia 0.1 doesn’t parse or convert subtitles, and it can’t write a track out to a new file: remuxing is not in this release. - Attachments are not tracks. Fonts and cover art in a Matroska file are native tags, not entries in
media.Tracks(ffprobe lists them as streams). - Protected tracks. An encrypted track is listed with
IsProtectedset; its packets are delivered as stored. - Language codes are as stored, not translated: a Matroska track’s
LanguageBCP47when it has one (de), else its ISO 639-2 code (gerordeu).
To decode one of the audio tracks, see Extract the audio from a video.