Recursive AI

Read MPEG-TS (.ts) file info in C#: programs and PIDs

All onemedia recipes

Media.Open reads an MPEG transport stream like any other video: duration, tracks and codecs. The transport stream reader underneath, MpegTsDemuxer, also lists the programs from the PAT and PMT, with the PMT and PCR PIDs and each elementary stream’s PID and stream type. It reads 188-byte .ts and 192-byte .m2ts/.mts (AVCHD and Blu-ray) streams. The input is the Sintel trailer (CC BY 3.0, © copyright Blender Foundation | durian.blender.org) copied into a transport stream with FFmpeg, without re-encoding.

Terminal
dotnet new console -o TsInfo && cd TsInfo
dotnet add package RecursiveAI.OneMedia --version 0.2.0-preview.1
curl -L -o sintel.mp4 https://download.blender.org/durian/trailer/sintel_trailer-480p.mp4
ffmpeg -v error -i sintel.mp4 -c copy sintel.ts

Put this in Program.cs and run dotnet run:

C#
using RecursiveAI.OneMedia;
using RecursiveAI.OneMedia.MpegTs;
 
using MediaFile media = Media.Open("sintel.ts");
Console.WriteLine($"{media.Format}, {media.Duration}");
 
if (media.Demuxer is MpegTsDemuxer ts)
{
    Console.WriteLine($"{ts.PacketSize}-byte packets," +
                      $" first time stamp {ts.StartTime.ToTimeSpan()}");
    foreach (TransportStreamProgram program in ts.Programs)
    {
        Console.WriteLine($"Program {program.ProgramNumber}:" +
                          $" PMT PID 0x{program.PmtPid:X4}," +
                          $" PCR PID 0x{program.PcrPid:X4}");
        for (int i = 0; i < program.Streams.Count; i++)
        {
            TransportStreamEntry stream = program.Streams[i];
            MediaTrack track = media.Tracks[program.TrackIndexes[i]];
            string detail = track.Video is { } v ? $"{v.Width}x{v.Height}"
                          : track.Audio is { } a ? $"{a.SampleRate} Hz {a.Channels} ch"
                          : "";
            Console.WriteLine($"  PID 0x{stream.Pid:X4}  type 0x{stream.StreamType:X2}"
                              + $"  {track.Kind} {track.Codec} {detail}");
        }
    }
}
Text
mpegts, 00:00:52.2083333
188-byte packets, first time stamp 00:00:01.4833333
Program 1: PMT PID 0x1000, PCR PID 0x0100
  PID 0x0100  type 0x1B  Video h264 854x480
  PID 0x0101  type 0x0F  Audio aac 48000 Hz 2 ch

A transport stream can carry several programs, each a set of elementary streams listed in its program map table (PMT). Programs lists them in PAT order; TrackIndexes maps each stream to its entry in media.Tracks, so the usual track properties (codec, size, rate, language) apply. The stream type is the PMT’s byte: 0x1B is H.264 and 0x0F AAC in ADTS. PacketSize is 192 for M2TS and AVCHD .mts files.

Times in a transport stream are the 90 kHz clock as stored, not rebased to zero: this stream starts at 1.483 s, the offset FFmpeg writes. media.Duration is the span from the first to the last presentation time. media.Packets(track) gives one packet per access unit for video and one frame for audio, with keyframes taken from the bitstream.

Check it with ffprobe

Terminal
ffprobe -v error -show_entries program=program_num,pmt_pid,pcr_pid:program_stream=id,codec_name -of compact sintel.ts
Text
program|program_num=1|pmt_pid=4096|pcr_pid=256|stream|codec_name=h264|id=0x100
stream|codec_name=aac|id=0x101

ffprobe prints the PIDs in decimal (4096 is 0x1000, 256 is 0x100) and the stream PIDs as id.

Limits

  • Service names. OneMedia reads the DVB service name and provider from the SDT (program.ServiceName, ServiceProvider, and the mpegts native tags), but in 0.2.0-preview.1 they come out empty for streams written by FFmpeg, as this one is; ffprobe -show_programs shows them (“Sintel Trailer” from “FFmpeg” here).
  • H.264 codec strings. H.264 in a transport stream has no configuration record, so track.Rfc6381Codec() is null for it in 0.2.0-preview.1. AAC, AC-3 and the others get theirs.
  • Audio decoding. media.Decode() on the AAC track of a transport stream throws AudioFormatException in 0.2.0-preview.1; see Extract the audio from a video. AC-3 and E-AC-3 don’t decode through the adapter in any container.
  • Reading only. No cutting, remuxing or editing: muxing is not in this release.
  • Known differences from ffprobe (leading and trailing fragments, damaged packets, DTS and Opus in TS not framed, AVCHD sidecar files listed but not decoded) are in MPEG-TS, PS and raw video.

For the same duration and codec details across MP4, MKV, AVI and TS, see Get video duration, resolution and codec.