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.
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:
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}"); } } }
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
ffprobe -v error -show_entries program=program_num,pmt_pid,pcr_pid:program_stream=id,codec_name -of compact sintel.tsprogram|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 thempegtsnative tags), but in 0.2.0-preview.1 they come out empty for streams written by FFmpeg, as this one is;ffprobe -show_programsshows 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 throwsAudioFormatExceptionin 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.