2.0.0 — typed demux, trait-driven dispatch, decoded JSON
2.0.0 — typed demux, trait-driven dispatch, decoded JSON
Feed 188-byte TS packets in; get typed, decoded SI out. That's the whole story.
SiDemux filters by PID, validates CRCs, follows the PAT to PMT PIDs, and
version-gates so a steady carousel emits each table only once — one SectionEvent
per changed section. Every event carries owning Bytes (cheap clone, no lifetime
fight) and a lazy event.table() that dispatches to the right typed AnyTable
variant via declare_tables!, the single source of truth that also generates the
AnyDescriptor dispatcher and declare_payloads! in dvb-t2mi. For
tvheadend-class workloads the demux hot path is version-gated and allocation-free
per packet; for one-off parsing, parse_loop(descriptor_bytes) gives a lazy
iterator that never panics and surfaces Unknown for unrecognised tags. All four
crates released in lockstep at 2.0.0.
Highlights
Demux in 10 lines
use dvb_si::demux::SiDemux;
use dvb_si::tables::AnyTable;
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(AnyTable::Pat(pat)) = event.table() {
println!("PAT v{} on {}", event.version().unwrap_or(0), event.pid());
let _ = pat;
}
}
}See examples/si_dump.rs
for a complete file-reading CLI (cargo run -p dvb-si --example si_dump -- file.ts [--json]).
parse_loop → decoded JSON
use dvb_si::descriptors::{parse_loop, AnyDescriptor};
for item in parse_loop(eit_event.descriptors) {
if let Ok(AnyDescriptor::ShortEvent(se)) = item {
// se.event_name is DvbText — decoded on demand, never in the hot path
println!("{}", se.event_name.decode());
}
}With the serde feature:
let items: Vec<_> = parse_loop(eit_event.descriptors)
.collect::<Result<_, _>>()?;
println!("{}", serde_json::to_string_pretty(&items)?);[
{
"shortEvent": {
"language_code": "fre",
"event_name": "Journal",
"text": ""
}
}
]Note: camelCase variant key (shortEvent), snake_case field names (event_name,
language_code) — only the enum wrapper is renamed.
T2-MI → BBFrame → SI chain
use dvb_t2mi::pump::T2miPump;
use dvb_t2mi::payload::AnyPayload;
use dvb_bbframe::{header::Bbheader, packet::up_iter};
use dvb_si::demux::SiDemux;
let mut pump = T2miPump::new(0x0006); // T2-MI PID in the outer TS
let mut si = SiDemux::builder().build();
for ts_packet in ts_packets {
for t2mi_event in pump.feed_ts(&ts_packet) {
if let Ok(AnyPayload::Bbframe(bb)) = t2mi_event.payload() {
// bb.bbframe = BBHEADER + data field; extract the inner TS packets
let header = Bbheader::parse(bb.bbframe)?;
for up in up_iter(&bb.bbframe[10..], &header) {
for section_event in si.feed(&up) {
println!("{:?}", section_event.table());
}
}
}
}
}(Working end-to-end version: dvb-t2mi/tests/chain.rs.)
Breaking changes
| Change | Migration |
|---|---|
Text fields (event_name, service_name, …) are DvbText<'a> instead of &'a [u8] |
Call .raw() for wire bytes, .decode() for Cow<str>; Deref<[u8]> still works |
Language/country codes are LangCode instead of [u8; 3] |
Access raw bytes via .0; call .as_str() for Cow<str>; Deref<[u8;3]> still works |
Deserialize removed on text-bearing structs |
Re-parse from wire bytes instead of deserializing from JSON |
Old descriptors::Descriptor subset enum removed |
Use parse_loop + AnyDescriptor; see §4 of the migration guide |
| serde JSON shape changed: decoded strings, camelCase variant keys | Update JSON expectations; see §5 of the migration guide |
pid::well_known constants are Pid (not u16) |
Call .value() or u16::from(pid) for the raw integer |
Full details and before/after code for every break:
MIGRATION-2.0.md
Links
All four crates released in lockstep at 2.0.0.