←Back
⚡ Quick Reference

TSDuck Cheat Sheet

The commands you actually reach for, all executed against TSDuck 3.44-4676 with real output. One pipeline shape, twenty recipes, and the traps that waste an afternoon.

-I -P -O
The whole model
43
Commands in 3.44
16
Packets lost = invisible
--help
Always authoritative

The pipeline

One input, any number of processors, one output. Packets flow left to right; each stage sees what the previous one left.

tsp -I file in.ts -P plugin1 -P plugin2 -O file out.ts
Order matters. Filtering before analysing answers a different question from analysing before filtering. And plugins like analyze, history, stats and pcrverify pass packets through untouched — so you can instrument a live pipeline without changing what it delivers.

Look at an unknown stream

CommandAnswers
tsanalyze in.tsEverything — services, PIDs, bitrate, errors, scrambling
tsanalyze --service-list in.tsDecimal service IDs, one line, pipeline-friendly
tsanalyze --pid-list in.tsEvery PID present, including 8191 (null)
tsanalyze --normalized in.tskey=value records a script can parse
tsanalyze --json in.tsJSON to stdout
tsbitrate in.tsJust the bitrate, 188- and 204-byte
tsdump --raw --max-packets 1 in.tsHex of the packet, header first
The number to look at first is Unreferenced. Non-zero means PIDs carry data no PMT declares — bandwidth you are paying for and nothing can play. Second is Scrambled: check it before troubleshooting a CAM.
188 vs 204 bytes: the same stream with and without Reed-Solomon parity. Quote 204 for a transponder, 188 for an IP link.

Live stream over HTTP — the everyday command

Analysing a live SAT>IP feed is the most-used recipe there is. Note the quotes.

tsp -I http 'http://host:8875/?freq=12606&sr=35300&pol=V&msys=dvbs2&mtype=8psk&fec=23&isi=5&plsc=131070&plsm=gold&t2mi_pid=auto&t2mi_plp=all&pids=all' -P analyze -P until --seconds 5 -O drop
⚠️ Quote the URL — this is the number-one mistake. A SAT>IP URL is full of &, which is the shell's background operator. Unquoted on bash the command is split at the first &: tsp receives only ?freq=…, the rest become shell variables, and the command is backgrounded so your prompt returns as if it succeeded. The server gets a frequency with no symbol rate or polarisation and cannot tune — so it presents as a signal fault that never left your shell. On zsh it is a loud parse error near '&' instead. Prefer single quotes: a $ is still expanded inside double quotes.
Also: -O drop is not optional — without an output plugin the packets flood your terminal. And -P until is what makes a live input terminate so analyze ever prints its report; --seconds is wall clock, --packets is deterministic.
ParameterWhy it is there
isi=Multistream carrier — selects one of several streams sharing it. Omit it and you get nothing usable
plsm=gold plsc=Physical-layer scrambling, required with isi. plsm takes a name, not a number — a numeric value is read as root mode and never locks
bbframe=1Demod emits raw DVB-S2 BBFrames; the server rebuilds the outer TS in software. Fixes the ~9% hardware de-encapsulation loss that causes near-total T2-MI CRC failure on a perfect carrier
t2mi_pid=autoDe-encapsulate T2-MI (tries 4096, then 4095). Output becomes the inner TS
t2mi_plp=allMerge every PLP — right when signalling is in a common PLP and content in others
pids=allFull MPTS. Client-controlled per session; nothing is filtered for you unless you ask
Reading a live report: Unreferenced should be 0. Fewer PCR PIDs than services means services share a PCR — normal. Several services at exactly the same bitrate means those carry only signalling, not content. And use --europe if service names come back with mangled accents (it is --dvb --default-charset ISO-8859-15).

Read and edit the signalling

CommandDoes
tstables --tid 0x00 in.tsPAT. Use 0x02 for PMTs, 0x42 for SDT, 0x01 for CAT
tstables --pid 0 --max-tables 1 in.tsBy PID, capped so it terminates on a live input
tstables --json-output t.json in.tsStructured output — needs a real filename
tstables --log-json-line in.tsOne JSON object per line, on stdout
tstabcomp -d t.bin -o t.xmlBinary sections → editable XML
tstabcomp -c t.xml -o t.binXML → binary sections
When table XML will not compile, read the shipped schema rather than guessing: find / -name tsduck.tables.model.xml. It is generated from the same definitions the compiler uses, so it cannot be out of date.

Cut a multiplex down

PluginBehaviour
zap 'SERVICE NAME'Keep one service, rewrite PAT and PMT → a valid SPTS
svremove 'NAME'Drop named services, leave the rest untouched
rmorphanSweep up PIDs nothing references any more
filter --pid NRaw PIDs, no understanding of services
svrename 'OLD' --name 'NEW'Rename in the SDT, nothing repacked
Always pair svremove with rmorphan. Removing a service leaves its elementary PIDs orphaned — still carried, still costing bandwidth:
tsp -I file in.ts -P svremove 'OLD SVC' -P rmorphan -O file out.ts

Timing

PluginUse
pcrverify --jitter-max 100000Pass/fail on PCR jitter, in microseconds. One summary line
pcrextract --pcr --pts --dts --output-file p.csvEvery timestamp to CSV — the lip-sync investigation tool
pcradjustRewrite PCRs consistent with a constant bitrate
pcrbitrateRecompute bitrate live so later plugins see a real value
regulatePace output to real time. Mandatory for file → network
Forget regulate and you transmit a file as fast as the disk allows — hundreds of times real time. The receiver's symptoms look exactly like a network fault.

The continuity counter blind spot — measured

The CC field is 4 bits, so it wraps every 16 packets. Dropping a controlled number of packets and asking tsanalyze what it noticed:

Lost on the PIDmod 16CC step seenDiscontinuitiesDuplicates
1601 — looks perfect00
171210
15150 — looks repeated01
11111210
Losing exactly 16 packets is completely invisible. Zero discontinuities, zero duplicates, no indication at all. And a loss of 15 is reported as a duplicate, which a monitor treating duplicates as benign also misses. Continuity counting detects loss only when the count is not a multiple of 16 — useful, never sufficient. Byte-accurate accounting has to come from PCR jitter, bitrate deviation, or an RTP sequence number.
Repairing bad counters: tsfixcc in.ts fixes in place. Run tsfixcc --no-action in.ts first — many corrections means the real problem is upstream, and rewriting hides it.

Monitor, convert, fail over

TaskCommand
Per-PID rate stabilitytsp -I file in.ts -P stats -O drop
Structural event logtsp -I … -P history --milli-seconds -O drop
Metrics to Grafana-P influx --org o --bucket b --interval 5
File → multicasttsp -I file in.ts -P regulate -O ip 239.0.0.1:1234
Multicast → SRTtsp -I ip 239.0.0.1:1234 -O srt --caller host:9000 --latency 200
HLS → TStsp -I hls https://…/master.m3u8 -O file out.ts
Analyse a Wireshark capturetspcap cap.pcapng
Extract T2-MI inner stream-P t2mi --extract --pid 4096 --plp 0
Scramble for testing-P scrambler --dvb-cissa --cw <32 hex digits> 'SVC'
Dual-input failovertsswitch --primary-input 0 --fast-switch -I … -I … -O …
--fast-switch is what makes failover seamless rather than merely automatic. Without it the backup only starts once the primary is declared dead, so the gap includes the backup's startup time.

The traps

1
An abbreviated option overwrote the input file
tstables --json in.ts

Resolved as --json-output in.ts, which takes a filename — so the input was overwritten with a 4-byte empty array. Every later command then reported an empty stream. Use full option names; never let an input path sit where an output filename goes.

2
--pid is not a comma list
--pid 0,17,256 → "must be <= 8,191"

The whole string parses as one integer. Repeat the option, and use a-b for ranges: --pid 0 --pid 17 --pid 273-275.

3
Plugins do not share a vocabulary
Guessed names fail outright

It is pcrverify --jitter-max, not --time-limit. It is fuzz --corrupt-probability, not --probability. slice takes --drop/--pass/--null and has no --pid at all.

4
A crafted stream is not a realistic stream
Same PCR, same CC on every packet

Bitrate reads as Unknown and dropped packets produce zero discontinuities. Run pcradjust then tsfixcc before treating a generated stream as broadcast-like.

5
Payload corruption sets no error flag
fuzz --corrupt-probability alone

The transport error indicator is set by demodulators when FEC fails, not by whatever mangled the bytes. Add --sync-byte for header-level faults, and --seed so a failure you find can be replayed.

All five were hit while writing this sheet. tsp -P <plugin> --help is authoritative, takes two seconds, and would have prevented every one.

About the author

Gleb Sazanov

Project Leader

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.