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

Part of the Perfect Streamer Toolkit — https://meshwork.pstreamer.tv

Command-line MPEG-TS transport stream analyser 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 analyser 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>

Analyse 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 is intentionally absent (closed networks, laboratory sources, OTT-only delivery and so on). In such streams the corresponding TR 101 290 P3 checks always give FAIL in the OVERALL verdict, and that FAIL means nothing.

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)

With --xml the analyser writes a single self-contained UTF-8 XML document to 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 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.4">
  <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 programs that parse the XML always get the same structure.

  • <drift> contains 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 result 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. The -t <sec> option increases it (for example, for PCR drift acceptance testing) or shortens it (for quick smoke checks).

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

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

The line is redrawn with a carriage return (\r) and takes up one line in the terminal; to suppress it, redirect stderr (2>/dev/null).

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.

Because of this, the analyser 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 over 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). It is long enough for the measurement noise to average out below 1 ppm even on a bursty/loopback delivery path, 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 analysed over several short windows and the drift value fluctuates from window to window by more than a few ppm, the cause is delivery jitter (sender pacing or the network) rather than the encoder clock. In that case, 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 analyser 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

Code 65 corresponds to EX_DATAERR from POSIX <sysexits.h>: “the input data was incorrect”. Use it in CI / monitoring as the stream conformance criterion:

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 are built by the POSIX shell rule 128 + signal_number, so $? after Ctrl+C matches what bash returns for any process terminated by SIGINT/SIGTERM. On interruption the analyser prints a single line on stderr (Analysis interrupted by signal N — no report produced.) and produces no report.

Sample output

Full report (excerpt)

========================================================================
  MPEG-TS ANALYZER v2.4 — TR 101 290 FULL REPORT
  Perfect Streamer Toolkit https://meshwork.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.4 | Perfect Streamer Toolkit https://meshwork.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, because they require a real-time reference. All other checks work in both modes.

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