---
title: "Read MPEG-TS (.ts) file info in C#: programs and PIDs"
url: https://recursiveai.org/onemedia/cookbook/read-mpeg-ts-info/
description: "Read an MPEG transport stream (.ts, .m2ts, .mts) in C#: duration, programs, PMT and PCR PIDs, each elementary stream’s PID, stream type and codec, and the start time, without FFmpeg or ffprobe."
version: "0.2.0-preview.1"
---

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

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

```shell
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`:

```csharp
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

```shell
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](https://recursiveai.org/onemedia/cookbook/extract-audio-from-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](https://recursiveai.org/onemedia/docs/mpeg/).

For the same duration and codec details across MP4, MKV, AVI and TS, see [Get video duration, resolution and codec](https://recursiveai.org/onemedia/cookbook/get-video-duration-resolution-codec/).
