Skip to main content

dvb_si/
lib.rs

1//! ETSI EN 300 468 v1.19.1 DVB Service Information parser and builder.
2//!
3//! `dvb-si` turns a raw MPEG-TS into typed, decoded DVB tables: feed packets in,
4//! get back PAT/PMT/SDT/EIT/… structs whose text fields decode to UTF-8 and
5//! whose descriptor loops walk into typed descriptors. Every layout is cited to
6//! the ETSI spec and round-trip tested; the same types serialize back to bytes.
7//!
8//! # 30-second quickstart
9//!
10//! Build a [`demux::SiDemux`], feed it TS packets, match on [`tables::AnyTable`],
11//! walk the descriptor loop with [`descriptors::DescriptorLoop::iter`], and print
12//! decoded text via [`text::DvbText::decode`]:
13//!
14//! ```
15//! use dvb_si::demux::SiDemux;
16//! use dvb_si::descriptors::AnyDescriptor;
17//! use dvb_si::tables::AnyTable;
18//!
19//! let mut demux = SiDemux::builder().build();
20//!
21//! // In real code, `packet` is each aligned 188-byte packet from your TS source
22//! // (file, UDP, tuner). Here we hand-build one PAT packet to keep the doctest
23//! // self-contained — see `examples/si_dump.rs` for the file-reading loop.
24//! # let packet = build_pat_packet();
25//! for event in demux.feed(&packet) {
26//!     match event.table() {
27//!         Ok(AnyTable::Sdt(sdt)) => {
28//!             for service in &sdt.services {
29//!                 for item in service.descriptors.iter().flatten() {
30//!                     if let AnyDescriptor::Service(svc) = item {
31//!                         // DvbText decodes EN 300 468 Annex A → UTF-8.
32//!                         println!("service: {}", svc.service_name.decode());
33//!                     }
34//!                 }
35//!             }
36//!         }
37//!         Ok(AnyTable::Pat(pat)) => {
38//!             println!("PAT v{} on {}", event.version().unwrap_or(0), event.pid());
39//!             assert_eq!(pat.entries.len(), 1);
40//!         }
41//!         Ok(other) => { let _ = other; }
42//!         Err(_) => {} // malformed section
43//!     }
44//! }
45//!
46//! # // Minimal PAT-in-a-TS-packet builder used by the doctest above.
47//! # fn build_pat_packet() -> [u8; 188] {
48//! #     use dvb_common::Serialize;
49//! #     use dvb_si::tables::pat::{Pat, PatEntry};
50//! #     let pat = Pat {
51//! #         transport_stream_id: 1, version_number: 0, current_next_indicator: true,
52//! #         section_number: 0, last_section_number: 0,
53//! #         entries: vec![PatEntry { program_number: 1, pid: 0x0100 }],
54//! #     };
55//! #     let mut section = vec![0u8; pat.serialized_len()];
56//! #     pat.serialize_into(&mut section).unwrap();
57//! #     let mut pkt = [0xFFu8; 188];
58//! #     pkt[0] = 0x47; pkt[1] = 0x40; pkt[2] = 0x00; pkt[3] = 0x10; pkt[4] = 0x00;
59//! #     pkt[5..5 + section.len()].copy_from_slice(&section);
60//! #     pkt
61//! # }
62//! ```
63//!
64//! # Layer map
65//!
66//! ```text
67//! TS packets ─▶ demux::SiDemux ─▶ SectionEvent
68//!                                    │ .table()
69//!                                    ▼
70//!                              tables::AnyTable  (PAT, PMT, SDT, EIT, …)
71//!                                    │ table.<loop field> : &[u8]
72//!                                    ▼
73//!                          descriptors::parse_loop ─▶ AnyDescriptor
74//!                                    │ field : DvbText / LangCode
75//!                                    ▼
76//!                              text::DvbText::decode() ─▶ UTF-8 String
77//! ```
78//!
79//! Each layer is independently usable: a caller who already has complete section
80//! bytes can skip [`demux`] and call [`tables::AnyTable::parse`] directly; a
81//! caller with a bare descriptor loop can call [`descriptors::parse_loop`] on it.
82//!
83//! # Features
84//!
85//! | Feature | Default | Enables |
86//! |---|---|---|
87//! | `chrono` | on | MJD + BCD time fields decode to `chrono::DateTime<Utc>` (EIT `start_time()`, TDT/TOT). Off → raw bytes. |
88//! | `ts` | on | [`demux::SiDemux`], [`ts::SectionReassembler`], TS packet parsing. Off → bring your own complete section bytes. |
89//! | `serde` | on | **Serialize-only** — for display/export (JSON via serde_json); parsing FROM JSON is deliberately unsupported, re-parse from wire bytes. `Serialize` on every table/descriptor; [`text::DvbText`] serializes as its **decoded** UTF-8 string (camelCase JSON). |
90//! | `yoke` | off | [`yoke::Yokeable`] on every zero-copy view type + the [`owned::Owned`] wrapper — own a parsed view past the input buffer's borrow (store/cache/send across threads) without re-parsing or a mirror type. |
91//!
92//! ```toml
93//! dvb-si = { version = "3.1", default-features = false }  # tight, no_std-ish build
94//! ```
95//!
96//! # Entry points
97//!
98//! - [`demux::SiDemux`] — PID-filtered, version-gated section pump (feature `ts`).
99//! - [`tables::AnyTable`] / [`descriptors::AnyDescriptor`] — trait-driven dispatch
100//!   on table_id / descriptor_tag; [`descriptors::parse_loop`] walks a loop lazily.
101//! - [`descriptors::DescriptorRegistry`] — register private descriptors at runtime.
102//! - [`text::DvbText`] / [`text::LangCode`] — decoded-on-demand Annex A text.
103//! - [`Parse`](dvb_common::Parse) / [`Serialize`](dvb_common::Serialize) — the two
104//!   symmetric contracts every table and descriptor implements.
105//! - [`tables`] — PAT, PMT, CAT, TSDT, NIT, BAT, SDT, EIT, TDT, TOT, RST, DIT, SIT,
106//!   ST, SAT, AIT, DSM-CC section, UNT, INT, RCT, CIT, RNT, Container, MPE
107//!   datagram, MPE-FEC, MPE-IFEC, protection message, downloadable font info —
108//!   every allocated table_id in EN 300 468 V1.19.1 Table 2.
109//! - [`descriptors`] — every DVB descriptor (tags 0x40..0x7F) plus MPEG-2 descriptors.
110//! - [`carousel`] — DSM-CC data-carousel messages (DSI/DII/DDB) + module
111//!   reassembly on top of the [`tables::dsmcc`] section framing.
112//! - [`pid::well_known`] — reserved DVB/MPEG-2 PIDs.
113//! - [`table_id::TableId`] — typed table_id enum.
114//! - [`descriptor_tag::DescriptorTag`] — typed descriptor_tag enum.
115//!
116//! See the crate README and `docs/` for the structured spec reference, and
117//! `MIGRATION-2.0.md` for the 1.x → 2.0 upgrade guide.
118
119#![warn(missing_docs)]
120
121pub mod carousel;
122pub mod descriptor_tag;
123pub mod descriptors;
124pub mod error;
125pub mod pid;
126pub mod section;
127pub mod table_id;
128pub mod tables;
129pub mod text;
130pub mod traits;
131
132#[cfg(feature = "yoke")]
133pub mod owned;
134
135#[cfg(feature = "ts")]
136pub mod demux;
137#[cfg(feature = "ts")]
138pub mod ts;
139
140pub use descriptor_tag::DescriptorTag;
141pub use error::{Error, Result};
142pub use table_id::TableId;