TS Analyze Perfect Streamer Toolkit v2.2 — TR 101 290

Part of the Perfect Streamer Toolkithttps://pstreamer.tv

Command-line MPEG-TS transport stream analyzer with ETSI TR 101 290 V1.4.1 compliance checking and ISO/IEC 13818-1 T-STD buffer-model validation.

Reads UDP multicast/unicast or TS files, auto-discovers PCR PIDs via PAT/PMT, and prints a detailed or summary report to stdout.

What it checks

The analyzer walks every TS packet and reports violations of:

  • Priority 1 (TS decodability): TS sync, sync loss, PAT/PMT presence and CRC, continuity counter, PID presence

  • Priority 2 (recommended monitoring): transport error indicator, CRC errors, PCR repetition / accuracy / discontinuity, PTS interval, CAT presence

  • Priority 3 (extended monitoring): NIT/SDT/EIT/TDT intervals, unreferenced PIDs, T-STD buffer overflow / underflow

In addition, it produces:

  • PCR accuracy to ±500 ns precision via byte-position regression

  • PCR drift in ppm (live mode)

  • T-STD buffer model per elementary stream (live mode)

  • SI section size validation against ISO/EN limits, with EIT-on-STB compatibility warnings (>1024 B)

  • UDP IAT (inter-arrival time) statistics — datagram-level network jitter (live mode only)

Usage

ts_analyze [options] <input>

Inputs

Form

Description

udp://239.1.1.1:1234

UDP multicast

udp://eth0@239.1.1.1:1234

UDP multicast on a specific interface

udp://192.168.1.100:1234

UDP unicast

udp://lo@127.0.0.1:12655

UDP unicast on loopback (test source)

/path/to/file.ts

Local TS file

RTP-encapsulated UDP is auto-detected and de-encapsulated.

Options

Option

Description

Default

-t, --time <sec>

Analysis duration in seconds

30

-s, --short

Short summary report

-f, --full

Full detailed report

yes

-b, --bitrate <Mbps>

TS bitrate hint for file mode

38.8

-p, --pcr-pid <pid>

Analyse a specific PCR PID only (decimal or 0xHHHH)

auto

-l, --pcr-limit <ms>

PCR repetition error limit in ms

40

--no-eit

Skip EIT analysis — P3.7..P3.10 reported as N/A, EIT contributions to P2.2 / section-size totals removed

EIT enabled

--no-nit

Skip NIT analysis — P3.1, P3.2 reported as N/A, NIT contributions to P2.2 / section-size totals removed

NIT enabled

--no-color

Disable ANSI colour output

colours on

--xml

Emit structured XML on stdout (quiet stderr)

text

-h, --help

Show usage

Examples

# 30-second TR 101 290 check on a multicast stream
ts_analyze -t 30 udp://239.10.10.1:1234

# brief summary, saved to a log (no color)
ts_analyze -s --no-color -t 60 udp://239.10.10.1:1234 > report.txt

# analyze a TS file
ts_analyze -t 30 recording.ts

# analyze a single PCR PID only
ts_analyze -p 0x0100 -t 30 udp://239.10.10.1:1234

# machine-readable XML for CI / monitoring
ts_analyze --xml -t 30 udp://239.10.10.1:1234 > result.xml

# skip EIT/NIT analysis (e.g. for streams that intentionally have none)
ts_analyze --no-eit --no-nit -t 30 udp://239.10.10.1:1234

Disabling EIT or NIT analysis

Some streams intentionally omit EIT or NIT (closed networks, lab feeds, OTT-only contribution, etc.). In that case the corresponding TR 101 290 P3 checks would always FAIL the OVERALL gate, which is just noise.

Use --no-eit and/or --no-nit to drop those checks:

Flag

Skipped checks

Side effects

--no-eit

P3.7 (EIT actual P/F), P3.8 (EIT other P/F), P3.9 (EIT actual schedule), P3.10 (EIT other schedule)

EIT sections are not assembled, so they contribute zero to P2.2 (CRC) and to the SI section-size summary. EIT-on-STB compatibility warning is suppressed.

--no-nit

P3.1 (NIT actual), P3.2 (NIT other)

NIT sections are not assembled, so they contribute zero to P2.2 (CRC) and to the SI section-size summary.

Skipped checks appear in the report as N/A with a disabled (--no-eit) / disabled (--no-nit) note, and in the XML as applicable="false" result="N/A". The short report shows NIT=off / EIT=off instead of an error count.

The flags affect only EIT (PID 0x0012) and NIT (PID 0x0010) processing — every other TR 101 290 check (P1.x, P2.x, SDT, TDT, CAT, T-STD, PCR drift, IAT) runs normally.

XML output mode (--xml)

--xml makes the analyzer emit a single self-contained UTF-8 XML document on stdout. All informational chatter (banner, “Stream locked”, “PCR PIDs discovered”, per-second progress, capture summary, short-duration warnings) is suppressed; stderr stays empty unless something genuinely fails (input cannot be opened, no PCR data, stream too short, interrupted by signal). ANSI colours are forced off.

The exit code is the same as in text mode: 0 for OVERALL=PASS, 65 for OVERALL=FAIL, plus the standard error / signal codes (1, 2, 3, 130, 143).

Top-level XML structure:

<?xml version="1.0" encoding="UTF-8"?>
<ts_analyze version="2.2">
  <source>udp://239.1.1.1:5000</source>
  <timestamp>2026-04-30T12:00:00+0300</timestamp>
  <duration_s>30.00</duration_s>
  <packets total="..." null="..."/>
  <ts_bitrate_mbps>20.012</ts_bitrate_mbps>

  <programs>
    <program number="1" pmt_pid="0x0100" pcr_pid="0x0101">
      <es pid="0x0101" stream_type="0x1b" name="H.264/AVC"/>
      <es pid="0x0102" stream_type="0x03" name="MPEG-2 Audio"/>
    </program>
  </programs>

  <tr101290>
    <check id="1.1" name="TS Sync Byte Error" applicable="true" errors="0" result="PASS"/>
    ...
    <check id="2.3" name="PCR Repetition Error"
           applicable="true" errors="0" result="PASS" soft_violations="3"/>
    ...
    <check id="3.4" name="Unreferenced PIDs" applicable="true" count="2" result="INFO"/>
    ...
  </tr101290>

  <si_section_size oversize_total="0" eit_stb_warn_total="12">
    <table name="EIT_actual_pf" max_bytes="1380" std_limit="4096"
           oversize="0" eit_stb_warn="12" result="WARN_STB"/>
    ...
  </si_section_size>

  <iat datagrams="33252" intervals="33251" min_ms="0.002" max_ms="5.009"
       avg_ms="0.150" stddev_ms="0.295" p95_ms="0.872" p99_ms="1.076"
       max_jitter_ms="4.272" gap_threshold_ms="100.0" gaps_over_threshold="0"/>

  <pcr_pids>
    <pcr_pid value="0x0101" sid="1" pcr_count="1500" discontinuities="0"
             estimated_bitrate_mbps="18.750">
      <interval samples="1499" min_ms="19.812" max_ms="20.195"
                avg_ms="20.001" p95_ms="20.102"
                iso_hard_violations="0" tr_soft_violations="0"
                rec_violations="0" result="PASS"/>
      <accuracy samples="1499" min_ns="-148.3" max_ns="201.7" abs_max_ns="201.7"
                avg_ns="2.1" stddev_ns="45.6" p95_ns="102.3"
                violations="0" result="PASS"/>
      <drift measured="true" ppm="0.618" limit_ppm="30"
             verdict_mode="informational" result="PASS"/>
      <tstd overflows="0" underflows="0" max_fill_bytes="34218" result="PASS">
        <es_buffer es_pid="0x0101" stream_type="0x1b"
                   capacity_bytes="3000000" measuring="true"
                   es_bitrate_mbps="15.200" max_fill_bytes="34218"
                   overflows="0" underflows="0"/>
      </tstd>
    </pcr_pid>
  </pcr_pids>

  <unreferenced_pids count="2">
    <pid value="0x01ff"/>
    <pid value="0x0200"/>
  </unreferenced_pids>

  <overall result="PASS"/>
</ts_analyze>

Key conventions:

  • All PIDs are formatted as 0xHHHH (4-digit hex with 0x prefix).

  • result attribute uses PASS / FAIL / WARN / N/A / INFO / SKIP / ok / WARN_STB.

  • For checks that are not applicable (file mode, scrambling absent, etc.) the element still appears with applicable="false" and result="N/A" so that schema consumers see a stable shape.

  • <drift> carries verdict_mode="informational" for 30 s T < 300 s and verdict_mode="hard" for T 300 s; result="SKIP" for runs shorter than 30 s.

  • <iat> is omitted for file-mode runs.

  • <overall> reflects the same gate as the text report’s OVERALL line and matches the process exit code.

Output is well-formed XML (validated with xmllint --noout); pipe directly into XSLT, Python lxml, etc., without parsing tweaks.

Reading the report

Status colours

Status

Colour

Meaning

PASS / ok

green

Test satisfies the standard

WARN / WARNING / WARN(STB)

yellow

Soft violation, does not affect OVERALL

FAIL / ERROR

red

Standard violation, affects OVERALL

INFO / NOTE / N/A

default

Informational only

Use --no-color when piping into log files or non-ANSI terminals.

Minimum analysis duration

Duration

Coverage

Exit code

< 2 s

Insufficient — analysis refused

3 (error)

2–10 s

P1 + P2 reliable; some P3 checks may lack data

0 + WARN

10–30 s

P1 + P2 + most P3; TDT (30 s) may lack data

0 + NOTE

≥ 30 s

Full coverage of all TR 101 290 checks

0

The default duration is 30 seconds — sufficient for full TR 101 290 coverage. Use -t <sec> to extend (e.g. for PCR-drift acceptance) or shorten (e.g. for quick smoke tests).

While the analyzer is running, a one-line progress indicator updates every second on stderr:

Progress: 47.3%  (14.2s / 30.0s, 330614 packets)

The line uses carriage-return (\r) so it stays on a single line in a terminal; redirect stderr to suppress (2>/dev/null).

PCR drift verdict — two-tier window

PCR clock tolerance per ISO/IEC 13818-1 §2.4.2.1 and ETSI TR 101 290 is ±30 ppm. The drift figure printed in the report comes from a linear regression of cumulative PCR seconds against wall-clock arrival time; its statistical error decreases as 1 / T^(3/2), so the analysis window must be long enough that measurement noise is well below the ±30 ppm boundary.

The analyzer therefore gates the drift verdict by the analysis duration:

Window

Verdict on out-of-spec drift

OVERALL impact

T < 30 s

skipped (noise dominates the ±30 ppm boundary)

none

30 s T < 300 s

WARN — informational only

none

T 300 s

FAIL

OVERALL = FAIL, exit 65

300 s is the DVB TR 101 297 acceptance-test window — long enough that even a bursty/loopback delivery path averages below 1 ppm of measurement noise, so an out-of-spec result reflects the encoder clock, not the network. The full report shows the current tier on the Verdict mode line of the PCR DRIFT block.

To get a hard PASS/FAIL drift verdict, run with -t 300 or longer.

Source-quality guideline (informational; does not change verdict tiers):

Source

Min window for ±5 ppm confidence

For ±2 ppm

Acceptance test

Broadcast multicast (CBR network, jitter < 100 µs)

30 s

60 s

5 min

Stable IP network (jitter < 200 µs)

30 s

2 min

5–10 min

Loopback / bursty sender (UDP unicast on lo)

5 min

15 min

30 min

Calibration / lab measurement

30 min

1+ hour

Examples:

# Quick PCR drift check on a real broadcast multicast (30 s)
ts_analyze -t 30 -s udp://239.1.1.1:5000

# Reliable check on a loopback source (5 min)
ts_analyze -t 300 -s udp://lo@127.0.0.1:12655

# Lab acceptance (30 min, full report to file)
ts_analyze -t 1800 -f --no-color udp://239.1.1.1:5000 > acceptance.txt

If the same stream is analysed in several short windows and the drift value varies by more than a few ppm between windows, the bottleneck is delivery jitter (sender pacing or network), not the encoder clock — extend the window.

Exit codes

Code

Meaning

0

Analysis completed and OVERALL = PASS

1

Argument or input error

2

Stream contains no PCR data

3

Stream duration below 2 s minimum

65

Analysis completed but OVERALL = FAIL — TR 101 290 / ISO 13818-1 violation

130

Interrupted by SIGINT (Ctrl+C) — analysis aborted, no report produced

143

Interrupted by SIGTERM — analysis aborted, no report produced

65 is EX_DATAERR from POSIX <sysexits.h> — “input data was incorrect”. Use it in CI / monitoring scripts to gate on stream conformance:

ts_analyze -s -t 60 udp://239.1.1.1:5000 || {
    case $? in
        65)  echo "stream non-compliant — see report" >&2 ;;
        130) echo "interrupted by user" >&2 ;;
        *)   echo "tool error" >&2 ;;
    esac
}

Codes 130/143 follow the POSIX shell convention 128 + signal_number so that $? after Ctrl+C matches what bash reports for any process killed by SIGINT/SIGTERM. On interruption the analyzer prints a single line to stderr (Analysis interrupted by signal N no report produced.) and skips report generation entirely.

Sample output

Full report (excerpt)

========================================================================
  MPEG-TS ANALYZER v2.2 — TR 101 290 FULL REPORT
========================================================================
  Source     : udp://239.1.1.1:5000
  Duration   : 30.00 s
  Packets    : 398936 total, 12045 null
  TS bitrate : 20.012 Mbit/s
------------------------------------------------------------------------

========================================================================
  TR 101 290 — PRIORITY 1 (TS decodability)
========================================================================
  |  1.1   TS Sync Byte Error            :        0 errors  PASS
  |  1.4   Continuity Count Error        :        0 errors  PASS
  |  1.6   PID Error (5s absence)        :        0 errors  PASS
  ...

========================================================================
  TR 101 290 — PRIORITY 2 (recommended monitoring)
========================================================================
  |  2.3   PCR Repetition Error          :        0 errors  PASS
  |  2.5   PCR Accuracy Error            :        0 errors  PASS
  ...

========================================================================
  OVERALL COMPLIANCE: PASS  — stream is TR 101 290 compliant
========================================================================

Short report

MPEG-TS Analyzer v2.2
TR 101 290 Summary | udp://239.1.1.1:5000 | 30.0s
----------------------------------------------------------------------------------------------------
  P1: sync=0 CC=0 PAT=0 PMT=0 PID=0  P2: TEI=0 CRC=0 PTS=0  P3: NIT=0 SDT=0 EIT=0 TDT=0 unref=2
  IAT: dgrams=33252 avg=0.150 ms max=5.009 ms p99=1.076 ms gaps>100ms=0
----------------------------------------------------------------------------------------------------
PCR PID       SID     Count     Intv max      Jitter max    Drift     Interval  Accuracy  T-STD
----------------------------------------------------------------------------------------------------
0x0101        1       1500      20.195 ms     76.4 ns       0ppm      PASS      PASS      PASS
----------------------------------------------------------------------------------------------------
OVERALL: PASS

Notes

  • File mode: PCR drift, T-STD buffer model and UDP IAT are not measured — they require a real-time reference. All other checks work in both modes.

  • Single transport stream: one MPTS or SPTS analysed at a time.