Recursive AI

List the audio, video and subtitle tracks of a video in C#

All onemedia recipes

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

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

C#
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}\"");
Text
#   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:

Terminal
ffprobe -v error -show_entries stream=index,codec_type,codec_name:stream_tags=language,title -of compact test5.mkv
Text
stream|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 IsProtected set; its packets are delivered as stored.
  • Language codes are as stored, not translated: a Matroska track’s LanguageBCP47 when it has one (de), else its ISO 639-2 code (ger or deu).

To decode one of the audio tracks, see Extract the audio from a video.