TS Analyze Perfect Streamer Toolkit v2.3 — 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 conformance checking and ISO/IEC 13818-1 T-STD buffer model validation.

Reads UDP multicast/unicast or TS files, automatically discovers the PCR PIDs via PAT/PMT and prints a full or short report to stdout.

The tool is part of the pstreamer package and, once installed, resides at /opt/pss/tools/ts_analyze — no separate installation is required.

What it checks

The analyzer walks every TS packet and reports violations:

  • 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 / discontinuities, PTS interval, CAT presence

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

It additionally reports:

  • PCR accuracy down to ±500 ns — regression over byte positions

  • PCR drift in ppm (live mode only)

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

  • Validation of SI section sizes against the ISO/EN limits, with EIT-on-STB compatibility warnings (>1024 B)

  • UDP IAT (inter-arrival time) — datagram-level jitter statistics (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 the specified 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 encapsulation of UDP is detected and unwrapped automatically.

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>

Analyze only the specified PCR PID (decimal or 0xHHHH)

auto

-l, --pcr-limit <ms>

PCR repetition error limit in ms

40

--no-eit

Skip EIT analysis — P3.7..P3.10 are reported as N/A, the EIT contribution to P2.2 / to the section size totals is excluded

EIT enabled

--no-nit

Skip NIT analysis — P3.1, P3.2 are reported as N/A, the NIT contribution to P2.2 / to the section size totals is excluded

NIT enabled

--no-color

Disable ANSI colours in the output

colours enabled

--xml

Structured XML on stdout (no stderr)

text

-h, --help

Show help

Examples

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

# short summary, save to 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 only a single PCR PID
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 where they are intentionally absent)
ts_analyze --no-eit --no-nit -t 30 udp://239.10.10.1:1234

Disabling EIT or NIT analysis

In some streams EIT or NIT are intentionally absent (closed networks, laboratory sources, OTT-only delivery and so on). The corresponding TR 101 290 P3 checks then always FAIL the OVERALL gate — which is pure noise.

Use --no-eit and/or --no-nit to exclude these 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 collected, so their contribution to P2.2 (CRC) and to the SI section size summary is zero. The EIT-on-STB compatibility warning is suppressed.

--no-nit

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

NIT sections are not collected, so their contribution to P2.2 (CRC) and to the SI section size summary is zero.

Skipped checks appear in the report as N/A marked disabled (--no-eit) / disabled (--no-nit), and in XML as applicable="false" result="N/A". In the short report NIT=off / EIT=off is shown instead of the error counter.

The flags affect only EIT (PID 0x0012) and NIT (PID 0x0010) processing — all other TR 101 290 checks (P1.x, P2.x, SDT, TDT, CAT, T-STD, PCR drift, IAT) are performed as usual.

XML output mode (--xml)

--xml makes the analyzer emit a single self-contained UTF-8 XML document on stdout. All ancillary output (the banner, “Stream locked”, “PCR PIDs discovered”, the per-second progress line, the capture summary, short-duration warnings) is suppressed; stderr stays empty unless a genuine failure occurs (the input could not be opened, no PCR data, the stream is too short, interruption by a signal). ANSI colours are forcibly disabled.

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.3">
  <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 the 0x prefix).

  • The result attribute takes the values PASS / FAIL / WARN / N/A / INFO / SKIP / ok / WARN_STB.

  • For checks that are not applicable (file mode, absence of scrambling and so on) the element is still present, 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 absent for file-mode runs.

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

The output is well-formed XML (it validates with xmllint --noout); feed it directly to XSLT, Python lxml and the like without any parsing workarounds.

Reading the report

Status colours

Status

Colour

Meaning

PASS / ok

green

The check conforms to 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 redirecting to log files or to non-ANSI terminals.

Minimum analysis duration

Duration

Coverage

Exit code

< 2 s

Insufficient — the analysis is rejected

3 (error)

2–10 s

P1 + P2 are reliable; some P3 checks may lack data

0 + WARN

10–30 s

P1 + P2 + most of P3; TDT (30 s) may not complete in time

0 + NOTE

≥ 30 s

Full coverage of all TR 101 290 checks

0

The default duration is 30 seconds, which is sufficient for full TR 101 290 coverage. Use -t <sec> to increase it (for example, for PCR drift acceptance testing) or to shorten it (quick smoke checks).

While running, the analyzer updates a single-line progress indicator on stderr once per second:

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

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

PCR drift verdict — two-tier window

The PCR clock tolerance per ISO/IEC 13818-1 §2.4.2.1 and ETSI TR 101 290 is ±30 ppm. The drift value in the report is obtained by linear regression of cumulative PCR seconds against the arrival time measured by the test bench clock; the statistical error decreases as 1 / T^(3/2), so the analysis window must be long enough for the measurement noise to fall below the ±30 ppm limit.

The analyzer therefore makes the drift verdict dependent on the analysis duration:

Window

Verdict when the drift exceeds the tolerance

Effect on OVERALL

T < 30 s

skipped (noise dominates relative to the ±30 ppm limit)

none

30 s T < 300 s

WARN — informational only

none

T 300 s

FAIL

OVERALL = FAIL, exit 65

300 s is the acceptance window (a product value; the ETSI TR 101 290 standard does not specify measurement intervals) — long enough for even a bursty/loopback delivery path to average out below 1 ppm of measurement noise, so that an out-of-tolerance result reflects the encoder clock rather than the network. The full report shows the current tier in the Verdict mode line of the PCR DRIFT block.

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

Source quality recommendations (informational; they do not change the verdict tiers):

Source

Minimum window for ±5 ppm

For ±2 ppm

Acceptance

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 / laboratory 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 analyzed over several short windows and the drift value fluctuates by more than a few ppm between windows, the bottleneck is delivery jitter (sender pacing or the network) rather than the encoder clock — increase the window.

Exit codes

Code

Meaning

0

Analysis completed, OVERALL = PASS

1

Argument or input error

2

No PCR data in the stream

3

The stream duration is below the 2 s minimum, or no data was received at all (the source is not transmitting, wrong address, multicast join not performed, firewall or wrong interface — the analyzer prints a hint)

65

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

130

Interrupted by SIGINT (Ctrl+C) — the analysis is cancelled, no report is produced

143

Interrupted by SIGTERM — the analysis is cancelled, no report is produced

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

ts_analyze -s -t 60 udp://239.1.1.1:5000 || {
    case $? in
        65)  echo "stream does not conform — 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 $? after Ctrl+C matches what bash returns for any process killed by SIGINT/SIGTERM. On interruption the analyzer prints a single line on stderr (Analysis interrupted by signal N no report produced.) and skips report generation entirely.

Sample output

Full report (excerpt)

========================================================================
  MPEG-TS ANALYZER v2.3 — TR 101 290 FULL REPORT
  Perfect Streamer Toolkit https://pstreamer.tv
========================================================================
  Source     : udp://239.1.1.1:5000
  Timestamp  : 2026-04-30 12:00:00
  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                  :        0           PASS
  |  1.4   Continuity Count              :        0           PASS
  |  1.6   PID Absence (5s)              :        0           PASS
  ...

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

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

Short report

MPEG-TS Analyzer v2.3 | Perfect Streamer Toolkit https://pstreamer.tv
TR 101 290 Summary | udp://239.1.1.1:5000 | 30.0s | 2026-04-30 12:00:00
----------------------------------------------------------------------------------------------------
  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 stddev=0.295 ms p99=1.076 ms gaps>100.000 ms=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, the 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 is analyzed at a time.