dvb-si 7.0.0

ETSI EN 300 468 DVB Service Information parser + builder. MPEG-2 PSI included.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
# dvb-si

ETSI EN 300 468 DVB Service Information parser **and builder**, plus the
MPEG-2 PSI tables it builds on, the DVB-allocated companion tables, and the
DSM-CC data carousel.

**Complete coverage: every allocated `table_id` in EN 300 468 V1.19.1
Table 2 (29 section types; 28 dispatched by `AnyTableSection` + the type-keyed MPE datagram view) and every allocated `descriptor_tag` in Table 12
(0x40–0x7F, 64 descriptors) is implemented**, each with a symmetric
`Parse` / `Serialize` pair and round-trip tests. Layouts are derived from the
ETSI specs (vendored in the repo and transcribed into reviewable markdown) and
validated against live broadcast captures.

**MSRV: 1.81.** `no_std + alloc` supported — disable the default `std` feature for embedded targets (non-Latin text charsets and wall-clock time require `std`).

## API model

`dvb-si` makes the PSI/SI layering explicit:

- Section parsers are named `*Section`: `PatSection`, `NitSection`,
  `SdtSection`, `SitSection`, `EitSection`, and so on. Each value is exactly one
  wire section and still borrows from the input bytes where possible.
- `AnyTableSection` dispatches one complete section by `table_id`. Demux events
  expose this through `SectionEvent::table_section()`.
- `collect::SectionSetCollector` assembles every long-form multi-section table
  using the common `section_number` / `last_section_number` fields. A completed
  set owns the original section bytes and can be parsed generically:
  `complete.table::<PatSection>()`.
- `collect::CompleteNit`, `CompleteBat`, `CompleteSdt`, and `CompleteEit` add
  flattened logical-table views where that is more useful than a vector of
  sections. Their descriptor loops are parsed through `AnyDescriptor` while the
  raw descriptor-loop bytes remain available.
- `collect::EitCollector` handles the EIT schedule rule that spans table IDs
  through `last_table_id`; ordinary section-number collection is not enough for
  schedule EIT. EIT schedule sub-tables version independently, and the
  collector exposes `clear()` / `retain_logical()` for long-running EPG pruning.
- `epg::EpgStore` (feature `chrono`) builds an electronic programme guide over
  `EitCollector`: deduplicated, time-ordered events with decoded short and
  extended (§6.2.15 fragment-concatenated) text, content/ratings/CRIDs,
  `now_and_next(key, at)`, SDT service-name join, and `services()` to enumerate.
- `resync::TsResync` recovers 188-byte (and 204-byte Reed-Solomon) packet
  alignment from an arbitrary byte stream — junk prefixes, mid-stream loss.
- `TsPacket::adaptation_field()` decodes the adaptation field: discontinuity /
  random-access flags, PCR & OPCR (`Pcr::as_27mhz()`), and splice countdown.
- `mux::SectionPacketizer` / `mux::SiMux` are the byte-exact inverse: pack
  serialized section bytes back into 188-byte TS packets (ISO/IEC 13818-1 §2.4.4).
- `TableId` variants use Rust CamelCase names (`TableId::Pat`,
  `TableId::NetworkInformationActual`, `TableId::MpeFec`), while section parser
  types carry the `Section` suffix.

## Quickstart

```rust
use dvb_si::demux::SiDemux;
use dvb_si::descriptors::AnyDescriptor;
use dvb_si::tables::AnyTableSection;

let mut demux = SiDemux::builder().build();

// In real code, `packet` is each aligned 188-byte packet from your TS source
// (file, UDP, tuner). See `dvb-tools dump` for a complete file-reading CLI.
for event in demux.feed(&packet) {
    match event.table_section() {
        Ok(AnyTableSection::SdtSection(sdt)) => {
            for service in &sdt.services {
                for item in service.descriptors.iter().flatten() {
                    if let AnyDescriptor::Service(svc) = item {
                        // DvbText decodes EN 300 468 Annex A -> UTF-8.
                        println!("service: {}", svc.service_name.decode());
                    }
                }
            }
        }
        Ok(AnyTableSection::PatSection(pat)) => {
            println!("PAT v{} on {}", event.version().unwrap_or(0), event.pid());
        }
        _ => {}
    }
}
```

See [`dvb-tools dump`](../dvb-tools/) for a complete file-reading CLI
(`cargo run -p dvb-tools -- dump file.ts [--json]`).

## Layer map

```
TS packets ──► demux::SiDemux ──► SectionEvent
                                    │ .table_section()
                              tables::AnyTableSection  (PatSection, SdtSection, …)
                                    │ section.<loop field> : DescriptorLoop
                          descriptors::parse_loop ──► AnyDescriptor
                                    │ field : DvbText / LangCode
                              text::DvbText::decode() ──► UTF-8 String

SectionEvent.bytes() ──► collect::SectionSetCollector ──► CompleteSectionSet
                                                         │ .table::<T>()
                                                         ├ .nit() / .bat() / .sdt() / .eit()
                                                   complete logical tables

serialized sections ──► mux::SectionPacketizer ──► 188-byte TS packets
```

## Section coverage

28 table_ids are dispatched by `AnyTableSection::parse`; `MpeDatagramSection` (0x3E)
is reachable via `AnyTableSection::parse_as` as noted below.
Every section parser has typed header fields, symmetric parse/serialize, and round-trip tests.
Per the crate's zero-copy convention, descriptor loops and repeated sub-structures borrow
from the input bytes. Flat descriptor loops are `DescriptorLoop` values that walk into typed
descriptors; notes below call out only tables that go further or deliberately keep a nested
structure raw.

| table_id | Table | Spec | Notes |
|---|---|---|---|
| 0x00 | PAT — Program Association | ISO/IEC 13818-1 | typed program→PID entries |
| 0x01 | CAT — Conditional Access | ISO/IEC 13818-1 | typed `ca_descriptors()` view |
| 0x02 | PMT — Program Map | ISO/IEC 13818-1 | typed ES loop |
| 0x03 | TSDT — TS Description | ISO/IEC 13818-1 | |
| 0x3A–0x3F | DSM-CC sections | ISO/IEC 13818-6 / EN 301 192 | framing; 0x3B/0x3C payloads typed via [`carousel`]#dsm-cc-data-carousel; 0x3E typed as MPE |
| 0x3E | MPE datagram_section (typed IP/MAC view) | EN 301 192 §7 | MAC reassembly, LLC/SNAP flag, SSI-aware trailer — accessible via `AnyTableSection::parse_as` |
| 0x40/0x41 | NIT actual/other | EN 300 468 §5.2.1 | typed TS loop |
| 0x42/0x46 | SDT actual/other | EN 300 468 §5.2.3 | typed service loop |
| 0x4A | BAT — Bouquet Association | EN 300 468 §5.2.2 | typed TS loop |
| 0x4B | UNT — Update Notification (SSU) | TS 102 006 | typed platform entries |
| 0x4C | INT — IP/MAC Notification | EN 301 192 | |
| 0x4D | SAT — Satellite Access family | EN 300 468 §5.2.11 | typed `SatBody`: Position V2/V3, cell fragment, time association, beamhopping |
| 0x4E–0x6F | EIT p/f + schedule, actual/other | EN 300 468 §5.2.4 | typed event loop; `chrono`-gated MJD+BCD `start_time()` |
| 0x70 | TDT — Time and Date | EN 300 468 §5.2.5 | |
| 0x71 | RST — Running Status | EN 300 468 §5.2.7 | typed event loop |
| 0x72 | ST — Stuffing | EN 300 468 §5.2.8 | |
| 0x73 | TOT — Time Offset | EN 300 468 §5.2.6 | incl. the SSI=0-with-CRC framing exception |
| 0x74 | AIT — Application Information | TS 102 809 | typed application loop; validated vs live HbbTV capture |
| 0x75 | Container | TS 102 323 | `ContainerSection` |
| 0x76 | RCT — Related Content | TS 102 323 | |
| 0x77 | CIT — Content Identifier | TS 102 323 | |
| 0x78 | MPE-FEC | EN 301 192 §9.9 | typed real_time_parameters |
| 0x79 | RNT — Resolution Notification | TS 102 323 | |
| 0x7A | MPE-IFEC | TS 102 772 | typed real_time_parameters |
| 0x7B | Protection message | TS 102 809 §9 | authentication-message + certificate-collection variants by table_id_extension |
| 0x7C | DFIS — Downloadable Font Info | EN 303 560 | typed font_info loop (table_id per EN 300 468 Table 2 NOTE 2) |
| 0x7E | DIT — Discontinuity Information | EN 300 468 | |
| 0x7F | SIT — Selection Information | EN 300 468 | |

Remaining table_id values are *reserved* or *user-defined* in EN 300 468
V1.19.1 Table 2 — there is nothing standardized left to implement.

## Descriptors

**71 descriptor tags are dispatched by `AnyDescriptor`** (via `DescriptorLoop::iter` /
`parse_loop`): 6 MPEG-2/DSM-CC tags, 64 DVB-allocated tags (0x40–0x7F), and the de-facto
private `logical_channel_descriptor` (0x83) — which exists as a variant but is not
auto-dispatched (register it via `DescriptorRegistry` with PDS context).
Unknown or unregistered tags pass through as `AnyDescriptor::Unknown { tag, body }` —
the raw payload is preserved and round-trips losslessly.

### MPEG-2 / DSM-CC descriptors (ISO/IEC 13818-1 / -6)

| tag | Descriptor |
|---|---|
| 0x05 | registration |
| 0x06 | data_stream_alignment |
| 0x09 | CA |
| 0x0A | ISO_639_language |
| 0x0F | private_data_indicator |
| 0x13 | carousel_identifier (ISO/IEC 13818-6) |

### DVB descriptors (EN 300 468, and companion specs)

All 64 allocated tags 0x40–0x7F from EN 300 468 V1.19.1 Table 12 are implemented.
Each parses into a typed struct with a symmetric serializer and round-trip tests;
any free-form byte fields (names, selector tails) stay borrowed `&[u8]`
per the crate's zero-copy convention.

| tag | Descriptor | Spec | Notes |
|---|---|---|---|
| 0x40 | network_name | EN 300 468 | |
| 0x41 | service_list | EN 300 468 | |
| 0x42 | stuffing | EN 300 468 | |
| 0x43 | satellite_delivery_system | EN 300 468 | |
| 0x44 | cable_delivery_system | EN 300 468 | |
| 0x45 | VBI_data | EN 300 468 | typed service loop; one-byte line entries raw per §6.2.47 |
| 0x46 | VBI_teletext | EN 300 468 | |
| 0x47 | bouquet_name | EN 300 468 | |
| 0x48 | service | EN 300 468 | |
| 0x49 | country_availability | EN 300 468 | |
| 0x4A | linkage | EN 300 468 | |
| 0x4B | NVOD_reference | EN 300 468 | |
| 0x4C | time_shifted_service | EN 300 468 | |
| 0x4D | short_event | EN 300 468 | |
| 0x4E | extended_event | EN 300 468 | |
| 0x4F | time_shifted_event | EN 300 468 | |
| 0x50 | component | EN 300 468 | |
| 0x51 | mosaic | EN 300 468 | typed cell + elementary-cell loops, typed cell_linkage variants |
| 0x52 | stream_identifier | EN 300 468 | |
| 0x53 | CA_identifier | EN 300 468 | |
| 0x54 | content | EN 300 468 | |
| 0x55 | parental_rating | EN 300 468 | |
| 0x56 | teletext | EN 300 468 | |
| 0x57 | telephone | EN 300 468 | bit-packed length fields typed |
| 0x58 | local_time_offset | EN 300 468 | |
| 0x59 | subtitling | EN 300 468 | |
| 0x5A | terrestrial_delivery_system | EN 300 468 | |
| 0x5B | multilingual_network_name | EN 300 468 | |
| 0x5C | multilingual_bouquet_name | EN 300 468 | |
| 0x5D | multilingual_service_name | EN 300 468 | |
| 0x5E | multilingual_component | EN 300 468 | |
| 0x5F | private_data_specifier | EN 300 468 | |
| 0x60 | service_move | EN 300 468 | |
| 0x61 | short_smoothing_buffer | EN 300 468 | |
| 0x62 | frequency_list | EN 300 468 | |
| 0x63 | partial_transport_stream | EN 300 468 §7.2.1 | |
| 0x64 | data_broadcast | EN 300 468 | selector raw (interpretation depends on data_broadcast_id) |
| 0x65 | scrambling | EN 300 468 | |
| 0x66 | data_broadcast_id | EN 300 468 / EN 301 192 | id_selector tail raw |
| 0x67 | transport_stream | EN 300 468 | |
| 0x68 | DSNG | EN 300 468 | |
| 0x69 | PDC | EN 300 468 | |
| 0x6A | AC-3 | EN 300 468 Annex D | |
| 0x6B | ancillary_data | EN 300 468 | |
| 0x6C | cell_list | EN 300 468 | both loops typed; 12+12-bit extents unpacked |
| 0x6D | cell_frequency_link | EN 300 468 | both loops typed |
| 0x6E | announcement_support | EN 300 468 | |
| 0x6F | application_signalling | TS 102 809 | |
| 0x70 | adaptation_field_data | EN 300 468 | |
| 0x71 | service_identifier | TS 102 809 | |
| 0x72 | service_availability | EN 300 468 | |
| 0x73 | default_authority | TS 102 323 | |
| 0x74 | related_content | TS 102 323 | |
| 0x75 | TVA_id | TS 102 323 | |
| 0x76 | content_identifier | TS 102 323 | |
| 0x77 | time_slice_fec_identifier | EN 301 192 | |
| 0x78 | ECM_repetition_rate | EN 301 192 | |
| 0x79 | S2_satellite_delivery_system | EN 300 468 | |
| 0x7A | enhanced_AC-3 | EN 300 468 Annex D | |
| 0x7B | DTS | EN 300 468 Annex G | |
| 0x7C | AAC | EN 300 468 Annex H | |
| 0x7D | XAIT_location | TS 102 727 | |
| 0x7E | FTA_content_management | EN 300 468 | |
| 0x7F | extension | EN 300 468 §6.2.18.1 | typed discriminant + typed bodies; see below |

### Private descriptors

| tag | Descriptor | Notes |
|---|---|---|
| 0x83 | logical_channel | EACEM/NorDig private; variant exists, not auto-dispatched — register via `DescriptorRegistry` |

### Extension descriptor registry (tag 0x7F)

The first payload byte (`descriptor_tag_extension`) selects a sub-descriptor
(EN 300 468 §6.4). **21 tag_extensions are fully typed**; everything else is
preserved byte-exact as `ExtensionBody::Raw` and round-trips losslessly. Private
`descriptor_tag_extension` values are surfaced via
`DescriptorLoop::iter_with_extensions(&desc_reg, &ext_reg)` as
`ExtIterItem::CustomExtension`.

| tag_ext | Extension | Spec |
|---|---|---|
| 0x00 | image_icon | Table 145, §6.4.7 |
| 0x04 | T2_delivery_system | Table 133, §6.4.6.3 (cell loop unfolded) |
| 0x05 | SH_delivery_system | Table 119, §6.4.6.2 |
| 0x06 | supplementary_audio | Table 153, §6.4.11 |
| 0x07 | network_change_notify | Table 149, §6.4.9 (cell/change loop unfolded) |
| 0x08 | message | Table 148, §6.4.9 |
| 0x09 | target_region | Table 156, §6.4.12 (region loop unfolded) |
| 0x0A | target_region_name | Table 157, §6.4.13 (region loop unfolded) |
| 0x0B | service_relocated | Table 152, §6.4.10 |
| 0x0D | C2_delivery_system | Table 115, §6.4.6.1 |
| 0x10 | video_depth_range | Table 160, §6.4.16.1 (range loop typed) |
| 0x11 | T2MI | Table 158, §6.4.14 |
| 0x13 | URI_linkage | Table 159, §6.4.16.1 |
| 0x15 | AC-4 | Annex D §D.5 (first level; toc/extra raw) |
| 0x16 | C2_bundle_delivery_system | Table 139, §6.4.6.4 |
| 0x17 | S2X_satellite_delivery_system | Table 140, §6.4.6.5.2 (channel-bond entries typed) |
| 0x19 | audio_preselection | Table 110, §6.4.1 (preselection loop unfolded) |
| 0x20 | TTML_subtitling | EN 303 560 Table 1, §5.2.1.1 |
| 0x22 | service_prominence | Table 162c, §6.4.18 (SOGI loop typed; target_region loop raw) |
| 0x23 | vvc_subpictures | Table 162a, §6.4.17 |
| 0x24 | S2Xv2_satellite_delivery_system | Tables 144a–144c, §6.4.6.5.3 |

Tags whose governing spec is not vendored (`0x01`–`0x03` CPCM, `0x0C` XAIT_PID,
`0x0E`/`0x0F`/`0x21` DTS family, `0x14` CI_ancillary, `0x18` protection_message)
are preserved as `ExtensionBody::Raw` and round-trip byte-exact.

## DSM-CC data carousel

The `carousel` module types the download-protocol payloads carried inside
DSM-CC sections (ISO/IEC 13818-6 §7.2/§7.3 as profiled by DVB — TR 101 202,
TS 102 006 SSU, TS 102 809 object carousels):

| Message / Type | messageId | Notes |
|---|---|---|
| DSI — DownloadServerInitiate | 0x1006 | privateData raw: SSU GroupInfoIndication / OC ServiceGatewayInfo |
| DII — DownloadInfoIndication | 0x1002 | typed module loop |
| DDB — DownloadDataBlock | 0x1003 | |
| `ModuleReassembler` || DDB → complete modules per DII geometry: version-aware, out-of-order tolerant, per-module + aggregate memory caps |

The BIOP object-carousel layer (`carousel::biop`) is also implemented:
`ServiceGatewayInfo`, `BiopMessage` (Directory / File / ServiceGateway / Stream / StreamEvent),
IOR + tagged profiles, and the `CarouselFs` virtual-file-tree walker
(`resolve` / `file_bytes`); ISO/IEC 13818-6 §11 as profiled by TR 101 202.

Validated **byte-exact** against a live French-TNT (M6 HbbTV) capture in the
test suite.

## Text decoding

**Full EN 300 468 Annex A Table A.3 selector coverage:**

| Selector | Table | Decoding |
|---|---|---|
| (none, first byte ≥ 0x20) | default Latin, Figure A.1 | glyph-for-glyph (ISO 6937 superset — € at 0xA4, full non-spacing diacritic row with precomposed forms + combining-mark fallback) |
| 0x01–0x0B | ISO/IEC 8859-5 … -15 | via `encoding_rs` (0x08 is reserved — no ISO 8859-12) |
| 0x10 | ISO/IEC 8859-n (two-byte selector) | via `encoding_rs` |
| 0x11 | ISO/IEC 10646 BMP | UCS-2 BE |
| 0x12 | KS X 1001 (Korean) | EUC-KR |
| 0x13 | GB-2312-1980 (Simplified Chinese) | GBK (GB-2312 superset) |
| 0x14 | Big5 (Traditional Chinese) | Big5 |
| 0x15 | UTF-8 | passthrough |
| 0x1F | `encoding_type_id` escape | id byte consumed; body U+FFFD (no registered broadcast ids) |
| reserved (0x08, 0x0C–0x0F, 0x16–0x1E) || U+FFFD per byte |

Annex A.1 control codes are honored for both the single-byte (0x80–0x9F) and
two-byte (U+E080–U+E09F PUA, Table A.2) tables: emphasis markers dropped,
CR/LF → space, reserved controls stripped.

The non-Latin charsets (`encoding_rs`) require the `std` feature.

## Typed dispatch

You rarely match table_ids or descriptor_tags by hand. `AnyTableSection::parse`
dispatches a complete section to the right typed section parser; a section's descriptor-loop
field is a `DescriptorLoop` whose `.iter()` yields `AnyDescriptor` values (typed
where known, `Unknown` otherwise, never panicking — `parse_loop` does the same
for a free byte slice). `DescriptorRegistry` lets you plug in private
descriptors at runtime, `TableRegistry` does the same for private table_ids,
and `ExtensionRegistry` handles private tag-extension sub-descriptors (tag 0x7F).
All are generated from a single declarative list so the dispatcher can never drift from the
implemented set.

```rust
use dvb_si::descriptors::AnyDescriptor;

for item in eit_event.descriptors.iter() {       // DescriptorLoop::iter()
    match item? {
        AnyDescriptor::ShortEvent(se) => println!("{}", se.event_name.decode()),
        AnyDescriptor::Unknown { tag, .. } => eprintln!("unknown 0x{tag:02X}"),
        _ => {}
    }
}
```

## Multi-section collection and EPG

```rust
use dvb_si::collect::{CompletedEit, EitCollector, SectionSetCollector};
use dvb_si::demux::SiDemux;
use dvb_si::tables::eit;
use dvb_si::tables::nit::{self, NitSection};

let mut demux = SiDemux::builder().build();
let mut nit_sections = SectionSetCollector::new();
let mut eit_sections = EitCollector::new();

for event in demux.feed(&packet) {
    match event.table_id() {
        nit::TABLE_ID_ACTUAL | nit::TABLE_ID_OTHER => {
            if let Some(complete) = nit_sections
                .push_section_with_pid(Some(event.pid().value()), event.bytes())
                .ok()
                .flatten()
            {
                let nit = complete.nit()?;            // flattened logical NIT
                let raw = complete.table::<NitSection>()?; // generic section view
                let _ = (nit, raw);
            }
        }
        eit::TABLE_ID_PF_ACTUAL..=eit::TABLE_ID_SCHEDULE_OTHER_LAST => {
            if let Some(done) = eit_sections
                .push_section_with_pid(Some(event.pid().value()), event.bytes())
                .ok()
                .flatten()
            {
                match done {
                    CompletedEit::PresentFollowing(set) => {
                        let eit = set.eit()?;
                        let _ = eit;
                    }
                    CompletedEit::Schedule(schedule) => {
                        for table in schedule.tables()? {
                            let _ = table.events;
                        }
                    }
                }
            }
        }
        _ => {}
    }
}

// Long-running EPG collectors can prune old state.
eit_sections.retain_logical(|key| application_still_needs(key));
```

## Owning parsed views across threads (feature `yoke`)

Zero-copy parsed types borrow the input slice via `<'a>`. The optional `yoke`
feature bundles the backing bytes and the borrowing view into one `'static`,
cheaply-`Clone`, `Send + Sync` value via the [`yoke`] crate:

```rust
use std::sync::Arc;
use dvb_si::{owned::Owned, tables::pmt::PmtSection};
use dvb_common::Parse;

let bytes: Arc<[u8]> = Arc::from(section);             // own the section
let pmt: Owned<PmtSection<'static>> = Owned::try_new(bytes, |b| PmtSection::parse(b))?;
let view: &PmtSection = pmt.get();                            // no re-parse, no lifetime
```

## Features

```toml
[dependencies]
dvb-si = { version = "7", default-features = false }  # tight build: no_std + alloc
```

| Feature | Default | Description |
|---|---|---|
| `std` | yes | Link std; `no_std + alloc` when off. Non-Latin text charsets and wall-clock time require `std`. |
| `chrono` | yes | MJD+BCD wire fields → `DateTime<Utc>` decoded accessors. Off → raw MJD/BCD bytes returned. |
| `ts` | yes | MPEG-TS packet types, `SectionReassembler`, `SiDemux`, `TsResync`, `SectionPacketizer`, `SiMux`. Off → bring your own section bytes. |
| `serde` | yes | `Serialize` on all parsed types (Serialize-only — re-parse from wire bytes to reconstruct). |
| `yoke` | no | `Yokeable` impls + `Owned<T>` wrapper to retain parsed views past the input buffer's borrow. |
| `flate2` | no | zlib decompression for compressed BIOP modules in object carousels. Off → compressed bytes exposed raw. Requires `std`. |

`serde` is **Serialize-only** — for display/export (JSON via `serde_json`);
parsing FROM JSON is deliberately unsupported — re-parse from the wire bytes.

## Principles

- **Spec fidelity.** Every field in a section's syntax appears in the parsed struct.
- **Parse and construct.** Every parser has a symmetric serializer; round-trip is tested.
- **Zero-copy where possible.** Parsed types borrow from the input via `<'a>` lifetimes.
- **No magic numbers.** Every hex literal outside `#[cfg(test)]` is a named constant or enum.

## Spec grounding

Every layout is cited. The repo vendors the ETSI PDFs and transcribes their
syntax tables into reviewable markdown
([`docs/`](https://github.com/fishloa/rust-dvb/tree/main/dvb-si/docs)) —
each spec below links both the ETSI deliverable and the in-repo
transcription:

| Spec | ETSI deliverable | Transcription |
|---|---|---|
| EN 300 468 V1.19.1 (2025-02) — DVB SI | [PDF]https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.19.01_60/en_300468v011901p.pdf | [en_300_468.md]https://github.com/fishloa/rust-dvb/blob/main/dvb-si/docs/en_300_468.md |
| EN 301 192 v1.7.1 — data broadcasting | [PDF]https://www.etsi.org/deliver/etsi_en/301100_301199/301192/01.07.01_60/en_301192v010701p.pdf | [en_301_192.md]https://github.com/fishloa/rust-dvb/blob/main/dvb-si/docs/en_301_192.md |
| TS 102 006 v1.7.1 — System Software Update | [PDF]https://www.etsi.org/deliver/etsi_ts/102000_102099/102006/01.07.01_60/ts_102006v010701p.pdf | [ts_102_006_ssu.md]https://github.com/fishloa/rust-dvb/blob/main/dvb-si/docs/ts_102_006_ssu.md |
| TS 102 323 v1.4.1 — TV-Anytime carriage | [PDF]https://www.etsi.org/deliver/etsi_ts/102300_102399/102323/01.04.01_60/ts_102323v010401p.pdf | [ts_102_323_tva.md]https://github.com/fishloa/rust-dvb/blob/main/dvb-si/docs/ts_102_323_tva.md |
| TS 102 809 v1.3.1 — application signalling | [PDF]https://www.etsi.org/deliver/etsi_ts/102800_102899/102809/01.03.01_60/ts_102809v010301p.pdf | [ts_102_809_apps.md]https://github.com/fishloa/rust-dvb/blob/main/dvb-si/docs/ts_102_809_apps.md |
| TS 102 772 v1.1.1 — MPE-IFEC | [PDF]https://www.etsi.org/deliver/etsi_ts/102700_102799/102772/01.01.01_60/ts_102772v010101p.pdf | [ts_102_772_mpe_ifec.md]https://github.com/fishloa/rust-dvb/blob/main/dvb-si/docs/ts_102_772_mpe_ifec.md |
| EN 303 560 v1.1.1 — TTML subtitling | [PDF]https://www.etsi.org/deliver/etsi_en/303500_303599/303560/01.01.01_60/en_303560v010101p.pdf | [en_303_560_ttml.md]https://github.com/fishloa/rust-dvb/blob/main/dvb-si/docs/en_303_560_ttml.md |
| TS 102 727 v1.1.1 — MHP (XAIT) | [PDF]https://www.etsi.org/deliver/etsi_ts/102700_102799/102727/01.01.01_60/ts_102727v010101p.pdf | vendored PDF only (cites give page + table) |
| TR 101 202 v1.2.1 — data broadcasting guidelines | [PDF]https://www.etsi.org/deliver/etsi_tr/101200_101299/101202/01.02.01_60/tr_101202v010201p.pdf | profile semantics for `carousel` (no syntax tables) |
| ISO/IEC 13818-6 — DSM-CC | not freely redistributable | [iso_13818_6_carousel.md]https://github.com/fishloa/rust-dvb/blob/main/dvb-si/docs/iso_13818_6_carousel.md (provenance-documented hand transcription) |

The crate has been through five adversarial spec-audit rounds; fixture tests
run against real transponder captures.

## Upgrading

For 3.x → 4.0, see **[MIGRATION-4.0.md](MIGRATION-4.0.md)**:
section parser renames, `AnyTableSection`, `table_section()`, CamelCase
`TableId`, and multi-section collection. For older breaks, see
**[MIGRATION-3.1.md](MIGRATION-3.1.md)** and
**[MIGRATION-2.0.md](MIGRATION-2.0.md)**.

## Family

[`dvb-common`](https://crates.io/crates/dvb-common) — traits + CRC-32\
[`dvb-t2mi`](https://crates.io/crates/dvb-t2mi) — T2-MI, all 12 packet types\
[`dvb-bbframe`](https://crates.io/crates/dvb-bbframe) — S2/S2X/T2 BBFRAME\
[`dvb-conformance`](https://crates.io/crates/dvb-conformance) — TR 101 290 monitor\
[`dvb-tools`](https://crates.io/crates/dvb-tools) — CLI: dump / services / epg / pids / t2mi\
For GSE see the existing [`dvb-gse`](https://crates.io/crates/dvb-gse) crate.

## License

Licensed under either of MIT or Apache-2.0, at your option.