Documentation
MPEG Transport Stream
The container DVB broadcast delivery travels in — carrying today's H.264 and H.265 services, because "MPEG-2" names the container, not the codec. Fixed 188-byte packets, 13-bit PIDs, and the tables that turn an opaque bitstream into television.
- 2026-09-24 10:47
- 2026-09-24 14:09
188
Bytes per packet
0x47
Sync byte, always
8192
Possible PIDs
27 MHz
PCR system clock
First, the name is misleading
MPEG-2 Systems
ISO/IEC 13818-1. The transport stream — the container. Still universal in DVB broadcast.
MPEG-2 Video
ISO/IEC 13818-2. A 1990s codec, now largely legacy. Different standard, same family name.
So an H.265 UHD service is HEVC video inside an MPEG-2 Transport Stream. Both at once. Per ETSI TS 101 154 (V2.10.1), the container carries MPEG-2, AVC, HEVC and VC-1 video, and everything from MPEG-1 Layer II through E-AC-3, AC-4 and MPEG-H audio.
One exception: DVB’s IP profiles — DVB-DASH and DVB-I — use ISOBMFF and CMAF segments, not transport stream. Broadcast feeds are TS; DASH/HLS feeds are not. Do not point a TS demux at a CMAF segment.
The packet in one picture
| Part | Size | Notes |
|---|---|---|
| Header | 4 bytes | Always present. Never scrambled. |
| Adaptation field | 0 – 183 bytes | Optional. Carries PCR, stuffing, flags. |
| Payload | up to 184 bytes | PES media, or table sections. |
Why 188? An ATM AAL-1 cell payload is 47 bytes, and 4 × 47 = 188 — one packet fitted exactly into four cells. ATM never took over broadcast, but the packet size stayed. Align every buffer you write to a multiple of 188.
Header fields worth knowing
| Field | Bits | Why you care |
|---|---|---|
| sync_byte | 8 | 0x47 every time. How the demux finds alignment. |
| transport_error_indicator | 1 | Upstream demod says this payload is damaged. |
| PUSI | 1 | This packet starts a new payload unit. Your resync anchor. |
| PID | 13 | Which elementary stream. 0x0000–0x1FFF. |
| scrambling_control | 2 | Scrambled? Even or odd key? |
| adaptation_field_control | 2 | Adaptation field present? Payload present? |
| continuity_counter | 4 | Loss detection, mod 16. |
Reserved PID map — memorize this
| PID | Table | What it tells you |
|---|---|---|
0x0000 | PAT | Every service, and where its PMT lives. Start here. |
0x0001 | CAT | EMM streams. Absent if nothing is scrambled. |
0x0002 | TSDT | Rarely used. |
0x0010 | NIT | Every transport stream in the network, with tuning parameters. |
0x0011 | SDT / BAT | Service names; bouquet groupings. |
0x0012 | EIT | Now/next banner and the full EPG. |
0x0013 | RST | Event started or ended early. |
0x0014 | TDT / TOT | UTC and local offset. Every 10–30 s only. |
0x1FFF | null | Padding to hold constant bitrate. |
PMT PIDs are not fixed. Each service’s PMT sits wherever the multiplexer put it, announced in the PAT. That is why you always read PID 0 first.
PSI vs SI — which spec to open
PSI — MPEG
ISO/IEC 13818-1. Structure of the stream itself: PAT, PMT, CAT, TSDT. Present in any conformant TS anywhere in the world.
SI — DVB
ETSI EN 300 468. The stream as a service offering: NIT, SDT, BAT, EIT, RST, TDT, TOT. Names, schedules, networks, time.
Rule of thumb: no PMT means it will not play. No SDT means it plays with no channel name. No EIT means no programme guide.
Five things that bite people
Buffers that are not multiples of 188
The classic "random glitch every few seconds"
A round decimal buffer size eventually splits a packet across a read boundary. Align everything to 188.
Continuity counter is blind to multiples of 16
4 bits, wraps at 16
Lose exactly 16, 32 or 48 packets and the counter lands where it would have anyway. A cheap check, not a guarantee.
CC errors with perfect RF are often software
Frontend polling competes with the demux
On some hardware, polling signal statistics itself causes continuity errors. Disabling frontend polling can clear them — at the cost of live signal reporting.
Asking for a program before the PMT arrives
Tables cycle; they are not on demand
Right after tuning, the PMT may not be known yet. Request by explicit PID list and you need no table knowledge; request by program and you must wait.
PCR jitter, not wrong PCR values
Irregular arrival breaks clock recovery
The decoder locks a PLL to PCR arrival. Remultiplexing and reinserting PCRs at the wrong instants breaks playback that was previously fine, even when every value is technically correct.
Timing in three lines
| Stamp | Clock | Job |
|---|---|---|
| PCR | 27 MHz — 33-bit base at 90 kHz + 9-bit extension | Lets the decoder rebuild the encoder’s clock. At least every 100 ms. |
| DTS | 90 kHz | When to decode a frame. |
| PTS | 90 kHz | When to present it. Differs from DTS whenever B-frames are used. |
PCR = PCR_base × 300 + PCR_ext — because 27 MHz ÷ 90 kHz = 300.
Two payload types
PES — media
Video, audio, subtitles, teletext. Up to 65536 bytes per PES packet, spread over many TS packets. Start marked by PUSI.
Sections — tables
Signalling. Up to 4096 bytes per section. Long sections carry a version number and a CRC32; short ones carry neither.
Read more
Gleb Sazanov
Chief Technology Officer
Gleb Sazanov is an accomplished Chief Technology Officer (CTO) with over 20 years of experience in software development, system architecture, and cloud-based solutions. As the CTO of SATLINE, a leading provider of virtual and colocation services tailored to SATCOM businesses, Gleb drives the company’s technological strategy, fostering innovation and efficiency in data center services. His expertise spans various domains, including DevOps, system scaling, and high-performance infrastructure management. With a deep passion for cutting-edge technologies, Gleb plays a pivotal role in shaping the future of the SATCOM industry.