TS Analyze Perfect Streamer Toolkit v2.3 — TR 101 290¶
Part of the Perfect Streamer Toolkit — https://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 multicast |
|
UDP multicast on the specified interface |
|
UDP unicast |
|
UDP unicast on loopback (test source) |
|
Local TS file |
RTP encapsulation of UDP is detected and unwrapped automatically.
Options¶
Option |
Description |
Default |
|---|---|---|
|
Analysis duration in seconds |
|
|
Short summary report |
– |
|
Full detailed report |
yes |
|
TS bitrate hint for file mode |
|
|
Analyze only the specified PCR PID (decimal or |
auto |
|
PCR repetition error limit in ms |
|
|
Skip EIT analysis — P3.7..P3.10 are reported as |
EIT enabled |
|
Skip NIT analysis — P3.1, P3.2 are reported as |
NIT enabled |
|
Disable ANSI colours in the output |
colours enabled |
|
Structured XML on stdout (no stderr) |
text |
|
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 |
|---|---|---|
|
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. |
|
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 the0xprefix).The
resultattribute takes the valuesPASS/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"andresult="N/A", so that schema consumers see a stable shape.<drift>carriesverdict_mode="informational"for30 s ≤ T < 300 sandverdict_mode="hard"forT ≥ 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 |
|---|---|---|
|
green |
The check conforms to the standard |
|
yellow |
Soft violation, does not affect OVERALL |
|
red |
Standard violation, affects OVERALL |
|
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 |
|---|---|---|
|
skipped (noise dominates relative to the ±30 ppm limit) |
none |
|
WARN — informational only |
none |
|
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 |
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 |
|---|---|
|
Analysis completed, OVERALL = PASS |
|
Argument or input error |
|
No PCR data in the stream |
|
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) |
|
Analysis completed, OVERALL = FAIL — TR 101 290 / ISO 13818-1 violation |
|
Interrupted by SIGINT (Ctrl+C) — the analysis is cancelled, no report is produced |
|
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.