Spec-grounded DVB protocol parsers and builders in Rust. Feed a transport stream in; get typed, decoded, serde-ready data out. Every wire layout is cited to its ETSI / ISO clause, has a symmetric serializer, and is round-trip tested.
TS (T2-MI PID) ─▶ dvb-t2mi ─▶ BBFrame ─▶ dvb-bbframe ─▶ inner TS ─▶ mpeg-ts ─▶ dvb-si ─▶ typed SI
T2-MI pump AnyPayload Bbheader + up_iter SiDemux AnyTableSection + collect
Each crate is independently useful; together they decode a DVB-T2 modulator feed all the way down to a service name string.
| Versions | 5 lockstep core crates (dvb-si, dvb-t2mi, dvb-bbframe, dvb-conformance, dvb-tools) released together from one v<version> tag; broadcast-common was ejected from that lockstep in #862 and, like every other crate below, versions independently — see each crate's own badge for its current version |
| MSRV | 1.95.0 across the workspace (pinned in rust-toolchain.toml) |
| Edition | 2024 across the workspace |
no_std |
Most parser/builder library crates below build #![no_std] + alloc under --no-default-features (suitable for embedded targets with a heap) — see each crate's own docs for its exact story. dvb-tools, dvb-stream, dvb-ci-runtime, multimux, multimux-cli and other tokio/axum-based crates require std and are not embedded-suitable. |
To build a no_std crate for an embedded target, build it directly with --no-default-features rather than the whole workspace (feature unification across the workspace can otherwise mask a std-only call — see docs/ for details), e.g.:
$ cargo build -p dvb-si --no-default-features --locked| Crate | Version | Docs | What it does |
|---|---|---|---|
broadcast-common |
Shared Parse/Serialize traits, the mux container-mux traits, and CRC-32/MPEG-2 that everything else builds on. |
||
dvb-si |
ETSI EN 300 468 Service Information + MPEG-2 PSI: every table_id and descriptor, DSM-CC carousel, Annex A text, a version-gated SiDemux. TS framing lives in mpeg-ts. |
||
dvb-t2mi |
ETSI TS 102 773 DVB-T2 Modulator Interface (T2-MI): all 12 packet types + a feed-and-iterate pump. | ||
dvb-bbframe |
DVB-S2 / S2X / T2 BBFRAME headers (MATYPE/UPL/DFL/SYNCD) + user-packet extraction. | ||
dvb-conformance |
ETSI TR 101 290 stream conformance monitor: Priority-1/2 + SI-repetition indicators on a caller-supplied clock. | ||
mpeg-ts |
MPEG-2 TS framing (ITU-T H.222.0 / ISO/IEC 13818-1): TS packet, adaptation field, PCR, PSI section reassembly + packetisation, resync. no_std. Independently versioned. |
||
mpeg-pes |
PES depacketization + PTS/DTS (ISO/IEC 13818-1 §2.4.3): PesPacket, 33-bit Pts/Dts, per-PID PesAssembler. no_std, depends only on broadcast-common. Independently versioned. |
||
mpeg-ps |
MPEG-1/2 Program Stream (.mpg/.vob) framing (ISO/IEC 13818-1 §2.5): pack header (42-bit SCR), system header, program stream map (PSM), pack walker; PES via mpeg-pes. no_std. Independently versioned. |
||
dvb-stream |
Async/tokio stream adapters: SectionStream and T2miEventStream over any AsyncRead source (file, TCP, UDP multicast). Independently versioned (tokio MSRV moves faster than the workspace). |
| Crate | Version | Docs | What it does |
|---|---|---|---|
broadcast-hls |
HLS (M3U8) playlist syntax (RFC 8216 / RFC 8216bis): MediaPlaylist/MasterPlaylist parse + serialize, Low-Latency HLS directives, I-frame trick-play, discontinuity signalling, CENC/CBCS #EXT-X-KEY. Extracted from transmux (issue #878) so a consumer that only needs playlist syntax doesn't pull in the whole container-muxing hub. no_std+alloc, depends only on broadcast-common. Independently versioned. |
||
container-probe |
Media container-format detection over a byte prefix: MPEG-2 TS (a stride x phase lattice across 188/192/204/208, so M2TS and mid-packet captures are found), ISOBMFF (with a fragmented/progressive layout verdict), Matroska/WebM, MPEG-PS, FLV, MXF, WAV, Ogg, ASF, and the ADTS/MP3/AnnexB elementary streams. Every prober always runs and scores its evidence; the highest wins and a near-tie reports Ambiguous rather than silently picking. Scoring is structural, not magic-byte: a lane needs 50% sync coverage and an elementary stream needs a frame-length chain. no_std+alloc, depends only on broadcast-common. Independently versioned. |
||
transmux |
Any-to-any media container muxing hub (ISO/IEC 14496-12 / 13818-1 / 23009-1, RFC 8216/3550): demux TS/fMP4/PS/WebM/FLV/RTMP into one neutral IR and mux to CMAF/progressive-MP4/TS/HLS/DASH/LL-DASH/LL-HLS/Smooth, plus CENC decrypt, RTP/RTCP, IR transforms (splice/SSAI, trick-play), and a conformance validator. HLS playlist syntax lives in broadcast-hls. no_std+alloc. Independently versioned. |
||
ts-fix |
MPEG-2 TS stream-conditioning CLI: continuity/PID-filter/PAT-PMT regen/stuffing/PCR-restamp repair. Independently versioned. | ||
media-doctor |
Container/stream diagnostics: pluggable lint-style checks (sync, PAT/PMT versioning, CC anomalies, PCR, PTS/DTS monotonicity, SCTE-35 splice consistency) + HLS/fMP4/CMAF playlist and structural validation. Independently versioned. | ||
mp4-emsg |
ISO BMFF / DASH Event Message Box (emsg): parse and build emsg boxes (v0 and v1) for in-band event signalling (DASH-IF, SCTE-35 inband, ID3). no_std. Independently versioned. |
||
timed-metadata |
Converts DPI/timed-metadata signalling between SCTE-35, HLS EXT-X-DATERANGE (RFC 8216 §4.4.5.1), and DASH emsg (ANSI/SCTE 214-3): lossless round-trips, 33-bit PTS wrap-unroll via a Timeline. no_std. Independently versioned. |
||
scte35-splice |
ANSI/SCTE 35 splice information (DPI cueing): every command + splice descriptor, the segmentation assignment tables, round-trip builders. no_std. Independently versioned. |
||
scte104 |
ANSI/SCTE 104 2023 automation→compression DPI signalling: single/multiple operation messages + all ~20 operations (splice/time_signal/insert-descriptor/segmentation/…). no_std. Independently versioned. |
||
dvb-mabr |
unpublished | — | Unpublished. DVB multicast ABR (ETSI TS 103 769) session configuration XML parser/serializer. no_std+alloc. |
ssai-runtime |
unpublished | — | Unpublished. Sans-IO SCTE-35 SSAI session core: per-session ad-break state, a pluggable ad-decision trait, splice-point conditioning, and per-session HLS Interstitial (EXT-X-DATERANGE CLASS="com.apple.hls.interstitial") playlist rendering. No HTTP client, no VAST/VMAP. no_std+alloc. |
playout-runtime |
unpublished | — | Unpublished. Sans-IO linear channel playout: a schedule model (programme/ad/slate), transition planning across a join (PTS-rebase offset + discontinuity detection), and SCTE-35 splice_insert() emission points built with scte35-splice. No HTTP, no tokio, no transcoding. no_std+alloc. |
| Crate | Version | Docs | What it does |
|---|---|---|---|
rtsp-runtime |
Sans-IO RTSP 1.0 (RFC 2326) session engine: driveable client + server state machines, interleaved RTP/RTCP framing, Basic/Digest/Bearer auth (via broadcast-auth); optional tokio (+ TLS) socket adapter. Independently versioned. |
||
rtp-packet |
RTP fixed header + CSRC list + generic header extension (RFC 3550 §5.1/§5.3.1) — spec-complete parse/serialize. no_std. Independently versioned. |
||
rtcp-packet |
RTCP control packets: SR/RR/SDES/BYE/APP + compound packet (RFC 3550 §6) — spec-complete parse/serialize. no_std. Independently versioned. |
||
srt-runtime |
SRT packet codecs + sans-IO HSv5 Caller-Listener/Rendezvous handshake state machines, ARQ, TSBPD delivery scheduling, Live/File congestion control, optional payload encryption and an optional async UDP socket adapter. no_std core. Independently versioned. |
||
broadcast-auth |
Shared multi-scheme HTTP/RTSP auth: client Credentials/Authenticator (Basic/Digest/Bearer) + server Verifier (challenge+verify, incl. a reverse-proxy forwarded scheme). Independently versioned. |
||
hls-runtime |
Sans-IO Low-Latency HLS (RFC 8216bis) client + server engines in one crate (blocking reload, part prefetch, rolling-window origin), with an optional tokio+reqwest IO adapter. Independently versioned. | ||
rtmp-runtime |
Sans-IO RTMP 1.0 ingest/publish session engine: handshake, chunk-stream (de)assembly, AMF0 command routing, server-side state machine, with an optional tokio socket adapter. Independently versioned. | ||
media-plane |
The ingress/egress spine a live origin is built on: Dialer/Listener → byte stages → IngestSession → Trunk (bounded sample/segment/event/part rings with cursor subscribers), plus three egress shapes (served/push/segment), tiered retention and DVR pinning. Byte layer is no_std + alloc; Trunk needs std. Independently versioned. |
||
multimux |
Multi-input (RTSP/RTP/TS-UDP/TS-HTTP/SRT/HLS-pull/DASH-pull/Smooth-pull/RTMP/file), multi-output (LL-HLS/DASH/LL-DASH/Smooth/TS-HLS/catch-up + SRT/RTMP/RTSP push) just-in-time repackaging HTTP origin (tokio + axum), with shared output auth and an external scheme plugin registry. Independently versioned. | ||
multimux-cli |
CLI — no library API | The multimux CLI binary: config-driven multi-route hub, or a single-route quick start. Independently versioned. |
|
webrtc-runtime |
unpublished | — | Unpublished. Sans-IO WHIP (RFC 9725) + WHEP (draft-ietf-wish-whep) HTTP signalling engine: SDP offer/answer, Trickle ICE, ICE restart, session lifecycle. No IO adapter — the caller drives HTTP. An optional media feature (ICE + DTLS-SRTP transport) is what multimux's whip/whep features build on; the default build is no_std-capable and never touches it. |
rist-runtime |
unpublished | — | Unpublished. RIST Simple Profile (VSF TR-06-1:2020) RTCP message types: Generic NACK (RFC 4585), Range NACK, RTT Echo, and compound packet builders. no_std+alloc. |
st2022 |
unpublished | — | Unpublished. SMPTE ST 2022-6 HBRMT (SDI-over-IP) RTP payload header parser/serializer. no_std. |
| Crate | Version | Docs | What it does |
|---|---|---|---|
cc-data |
DVB closed-caption carriage: cc_data() (ETSI TS 101 154 Table B.9) → typed CEA-608/708 triplets + 608/708 split. no_std. Independently versioned. |
||
dvb-subtitle |
ETSI EN 300 743 DVB (bitmap) subtitling: page/region/CLUT/object/display-definition/disparity segments + 2/4/8-bit pixel-data sub-blocks, fed the PES data field. no_std, depends only on broadcast-common. Independently versioned. |
||
st291 |
SMPTE ST 291-1 ancillary (ANC) data content: typed parse/serialize for its transports — ST 2038:2021 MPEG-2 TS/PES carriage (anc_data_descriptor + ANC data PES packet) and RFC 8331 / ST 2110-40 RTP carriage. no_std. Independently versioned. |
||
dvb-vbi |
VBI data carriage in DVB (ETSI EN 301 775) — the PES data field: VPS, WSS, Closed Captioning, EBU/Inverted Teletext, and monochrome 4:2:2 luminance sample data units. no_std. Independently versioned. |
||
caption-convert |
unpublished | — | Unpublished. CEA-608/708 and EBU Teletext caption conversion to WebVTT, and WebVTT <-> SRT, layered on the extractors timed-metadata already implements. DVB bitmap subtitles and TTML/IMSC source conversion are documented gaps (see crate docs), never silently degraded. no_std+alloc. |
ttml-subtitle |
W3C TTML2 / IMSC 1.1 timed-text subtitle parser: full TTML2 element tree (26 elements, 56 style properties), exhaustive <time-expression> grammar, and IMSC 1.1 profile validation (159-row feature disposition table). Parse/validate split; from-scratch authoring. no_std+alloc. Independently versioned. |
||
ule |
Unidirectional Lightweight Encapsulation (RFC 4326 + RFC 5163): SNDU framing, extension-header chains, and TS-packet de-fragmentation over DVB-S/T/C MPEG-2 TS. no_std. Independently versioned. |
||
rmt-flute |
ALC/LCT/FLUTE/NORM multicast object-delivery wire formats (RFC 5651/5775/6726/5740): LCT headers, header-extension chains, ALC + FEC Payload IDs, FLUTE EXT_FDT/EXT_CENC, and NORM messages. no_std. Independently versioned. (Renamed from dvb-flute, which implemented no DVB standard; all dvb-flute versions are yanked.) |
||
st12-1 |
SMPTE ST 12-1:2014 Linear Timecode (LTC) — the 80-bit logical LTC codeword: BCD time address, drop/color frame flags, binary groups, sync word. no_std. Independently versioned. |
||
st337 |
SMPTE ST 337-2015 non-PCM audio/data burst-preamble framing over AES3 — spec-complete parse/serialize. no_std. Independently versioned. |
||
rdd29 |
SMPTE RDD 29:2019 Dolby Atmos bitstream — frame/element framing + bed/object metadata. no_std. Independently versioned. |
||
st377-1 |
SMPTE ST 377-1:2019 Material Exchange Format (MXF) — KLV framing, Partition/Primer Pack, local-set structural metadata, Random Index Pack. no_std. Independently versioned. |
Parsers for the ATSC 3.0 signalling/delivery stack — the ATSC analogue of the
DVB SI + multicast-delivery crates above. Both crates are unpublished; see
docs/ for maturity notes before depending on either.
| Crate | Version | Docs | What it does |
|---|---|---|---|
atsc3 |
unpublished | — | Unpublished. ATSC 3.0 (A/331) LLS binary envelope + Service List Table (SLT) parser. no_std+alloc. |
atsc3-route |
unpublished | — | Unpublished. ATSC A/331 Annex A ROUTE binary framing: EXT_ROUTE_PRESENTATION_TIME/EXT_TOL LCT header extensions, source/repair FEC Payload ID layouts, and the Codepoint (CP) delivery-object semantics table. Built on rmt-flute's LCT/ALC/FLUTE; no XML/S-TSID/SLS. no_std+alloc. |
| Crate | Version | Docs | What it does |
|---|---|---|---|
dvb-ci |
DVB Common Interface (ETSI EN 50221): APDU/resource objects (ca_info, ca_pmt, ca_pmt_reply, application_info, …), ASN.1 length codec, SPDU/TPDU framing, and a build_ca_pmt builder from a dvb-si PMT. no_std. Independently versioned. |
||
dvb-ci-runtime |
Pure-Rust EN 50221 DVB Common Interface driver runtime: device I/O, TPDU/SPDU poll loop, and resource state machines over the dvb-ci codecs. Independently versioned. |
||
dvb-csa |
DVB Common Scrambling Algorithm (CSA2): pure-Rust, oracle-validated block + stream cipher. Scramble/descramble TS payloads with an 8-byte control word. no_std. Independently versioned. |
||
dvb-simulcrypt |
DVB SimulCrypt head-end CA message framing (ETSI TS 103 197): the generic TLV message structure plus the ECMG⇔SCS and EMMG/PDG⇔MUX registries. Signalling only — CW/ECM/EMM/datagram payloads stay opaque. no_std. Independently versioned. |
| Crate | Version | Docs | What it does |
|---|---|---|---|
dvb-tools |
CLI — no library API | Command-line analyzer over the family: dump / services / epg / pids / t2mi. |
|
broadcast-loudness |
EBU R 128 / ITU-R BS.1770-5 loudness measurement: K-weighting, integrated/short-term/momentary loudness (LUFS), loudness range (LRA), and true-peak (dBTP). no_std+alloc, depends only on broadcast-common. Independently versioned. |
||
compliance-probe |
unpublished | — | Unpublished. Live probe over a media-plane Trunk/ByteTap: drives dvb-conformance's TR 101 290 indicators, a PCR-drift/jitter estimate (explicitly distinct from TR 101 290 2.4, which dvb-conformance does not emit), and SCTE-35 splice_insert cue-sanity checks, exported through the metrics facade for a host process to render as Prometheus. |
This project does not publish re-export shims. When a crate is renamed, every
version under the old name is yanked, so an old Cargo.toml reference
fails loudly instead of silently resolving to a frozen copy. Renamed so far:
dvb-flute -> rmt-flute, smpte2038/dvb-smpte2038 ->
st291, and ll-hls-runtime -> hls-runtime (this
last one is the exception: its old versions are still live, because the rename
tracked a scope change rather than a mistaken name).
For GSE, see the existing dvb-gse crate.
cpix/ and st2110/ hold spec-transcription docs/ trees
only — no Cargo.toml, no src/, not workspace members, and never published.
They exist so a future crate has real, cited spec groundwork to start from.
Demux a .ts capture and print its SI sections — the
dvb-tools dump CLI:
$ cargo run -p dvb-tools -- dump dvb-si/tests/fixtures/m6-single.ts
pid=0x0000 PROGRAM_ASSOCIATION v0 sn=0
pid=0x0064 PROGRAM_MAP v1 sn=0
-- packets=1264 sections=47 emitted=3 suppressed=44 crc_failures=0 malformed=0
$ cargo run -p dvb-tools -- dump dvb-si/tests/fixtures/m6-single.ts --json
{
"pat": {
"transport_stream_id": 1,
"entries": [ { "program_number": 1025, "pid": 100 } ]
// … (other fields elided for brevity)
}
}In code, the section-level pipeline is a feed-and-match loop:
use dvb_si::demux::SiDemux;
use dvb_si::descriptors::AnyDescriptor;
use dvb_si::tables::AnyTableSection;
let mut demux = SiDemux::builder().build();
for packet in ts_packets { // each aligned 188-byte packet
for event in demux.feed(&packet) { // changed sections only
if let Ok(AnyTableSection::SdtSection(sdt)) = event.table_section() {
for service in &sdt.services {
for item in service.descriptors.iter().flatten() {
if let AnyDescriptor::Service(svc) = item {
println!("{}", svc.service_name.decode()); // Annex A → UTF-8
}
}
}
}
}
}dvb-si ships complete end-to-end support for the DVB-SSU receiver chain
(ETSI TS 102 006). Every layer is typed:
NIT linkage_descriptor (type 0x0A)
└─▶ PMT data_broadcast_id_descriptor (tag 0x66, id = 0x000A)
└─▶ IdSelector::Ssu → SsuIdSelector (TS 102 006 §7.1 Table 4)
UNT (table_id 0x4B) on the signalled PID
└─▶ UntPlatform × N (compatibilityDescriptor + descriptors)
DSM-CC carousel: DSI (messageId 0x1006) + DII + DDB
└─▶ GroupInfoIndication (TS 102 006 §8.1.1 Table 6)
ModuleReassembler → complete firmware module bytes
To decode an SSU stream:
- Parse a
NitSection; find alinkage_descriptorwithlinkage_type = 0x0A— it points to the network carrying the UNT. - Parse the
PmtSectionfor the SSU service; find aDataBroadcastIdDescriptorwithdata_broadcast_id = 0x000A. Itsid_selectorwill beIdSelector::Ssu(SsuIdSelector { oui_entries, … }). - The same PMT ES entry's PID carries UNT sections (
table_id 0x4B). ParseUntSection; eachUntPlatformdescribes a compatible device group with its ownCompatibilityDescriptorand operational descriptors. - Feed the carousel PID into
SiDemux+DsmccSection→UnMessage::Dsi. Decodedsi.private_dataasGroupInfoIndication::parse(dsi.private_data)to find the update groups and their sizes. - Parse
UnMessage::Diito enumerate modules; feedDownloadDataBlockmessages intoModuleReassemblerto reconstruct complete firmware bytes.
These are not "good enough to parse the common case" parsers. The defining discipline is spec fidelity, verified several ways over:
- Grounded in the ETSI deliverables. The PDFs are vendored in the repo and
their syntax tables transcribed into reviewable markdown under
dvb-si/docs/; every module doc cites its spec, section, and tag/table_id. No magic numbers — every hex literal outside tests is a named constant or enum. - Symmetric and round-trip tested — these crates emit as well as parse.
Every table and descriptor implements
Serialize, not justParse: build aPatSection/PmtSection/CaDescriptorand callserialize_intoto get a complete section (CRC-32 included). Parse → serialize → parse is byte-identical, a hard project invariant enforced by tests. So there's no need to hand-roll PSI encoders. - Decoded, not just typed. Spec-enumerated codes are typed enums with decoded
names —
running_statusis aRunningStatus,stream_typeaStreamType,service_typeaServiceType; content genre, parental-rating age, AC-3/E-AC-3 (0x6A/0x7A typed descriptors), and more decode in the library, so consumers never re-implement an ETSI lookup table. - Five adversarial spec-audit rounds against the transcriptions, plus fixture tests run against real transponder captures (e.g. a live French TNT / M6 HbbTV mux; a 10 s satellite capture decoding "Emission Spéciale Politique" out of an EIT).
- Complete coverage. Every allocated
table_idin EN 300 468 V1.19.1 Table 2 and everydescriptor_tagin Table 12; all 12 T2-MI packet types.
- Per-crate front pages: dvb-si · dvb-t2mi · dvb-bbframe · broadcast-common · dvb-tools · dvb-conformance
- Adding a parser crate — how a new sibling crate (e.g.
scte35-splice) plugs its own wire types into the existing dispatch via the runtime registries and open*Deftraits, with zero breaking change. dvb-si4.0 migration guide — 3.x → 4.0 breaking changes: section parser names (NitSection,SitSection, …),AnyTableSection, CamelCaseTableId, and complete multi-section table collection.dvb-si3.1 migration guide — 1.x / 2.x → 3.1 breaking changes (typedDescriptorLoop, Serialize-only serde, typed SIT, optionalyoke) with before/after code.dvb-si2.0 migration guide — 1.x → 2.0 breaking changes with before/after code.- API docs: docs.rs/dvb-si (each crate's docs.rs front page carries a runnable quickstart).
Licensed under either of MIT or Apache-2.0, at your option. Contributions are accepted under the same dual license.