Skip to main content

dvb_vbi/
lib.rs

1//! VBI data carriage in DVB — ETSI EN 301 775 V1.2.1 §4 (the PES data field).
2//!
3//! EN 301 775 specifies how Vertical Blanking Information (VBI) is carried in
4//! MPEG-2 / DVB Transport Streams using the private PES packet mechanism
5//! (`stream_id = private_stream_1` `0xBD`). It extends EN 300 472 (EBU Teletext
6//! carriage) with **Inverted Teletext**, **VPS** (EN 300 231), **WSS**
7//! (EN 300 294), **Closed Captioning** (line 21, EIA-608 Rev A), and a generic
8//! **monochrome 4:2:2 luminance-sample** transport.
9//!
10//! This crate decodes the **PES data field** ([`DataField`], §4.4.1, Table 1):
11//! a [`DataField::data_identifier`] byte (Table 2) followed by a loop of
12//! [`DataUnit`]s. Each data unit is a [`DataUnitId`] (Table 3) + an 8-bit
13//! `data_unit_length` + a typed [`DataUnitPayload`]:
14//!
15//! - [`TeletextDataField`] — EBU (`0x02`/`0x03`) and Inverted (`0xC0`) Teletext
16//!   (§4.5): a shared [`LineHeader`] + an 8-bit `framing_code` + a 42-byte
17//!   opaque `txt_data_block`. EN 300 706 Teletext coding is out of scope.
18//! - [`VpsDataField`] — VPS (`0xC3`, §4.6): shared header + 13-byte block.
19//! - [`WssDataField`] — WSS (`0xC4`, §4.7): shared header + a 14-bit
20//!   `wss_data_block` + a 2-bit `reserved_future_use` `11` tail.
21//! - [`ClosedCaptioningDataField`] — Closed Captioning (`0xC5`, §4.8): shared
22//!   header + a 16-bit data block.
23//! - [`MonochromeDataField`] — monochrome 4:2:2 samples (`0xC6`, §4.9): its own
24//!   first-byte packing (first/last segment flags + field_parity + line_offset),
25//!   a `first_pixel_position`, `n_pixels`, and the luminance `Y_value` bytes.
26//! - Stuffing (`0xFF`, §4.4.1) and an `Opaque` catch-all for reserved /
27//!   user-defined ids (Table 3: discard) round-trip verbatim.
28//!
29//! ⚠ Table 1's parse branch routes `data_unit_id` `0xC1` to `txt_data_field()`,
30//! but Table 3 marks `0xC1` as *reserved → discard*. Table 3 is authoritative,
31//! so `0xC1` decodes to [`DataUnitId::Reserved`] (see `docs/vbi.md`).
32//!
33//! No raw passthrough: every typed field re-serializes from its parsed value,
34//! `data_unit_length` is recomputed from the typed body on serialize, and a
35//! committed fixture is byte-exact round-tripped in the crate's tests.
36//!
37//! `#![no_std]` + `alloc`; depends only on `broadcast-common`.
38//!
39//! # Examples
40//!
41//! Build a multi-unit VBI PES data field (VPS + WSS) from typed fields and
42//! round-trip it:
43//!
44//! ```
45//! use dvb_vbi::{DataField, DataUnit, LineHeader, VpsDataField, WssDataField};
46//!
47//! let vps = DataUnit::vps(VpsDataField {
48//!     header: LineHeader::new(true, 16),
49//!     vps_data_block: [0u8; 13],
50//! });
51//! let wss = DataUnit::wss(WssDataField {
52//!     header: LineHeader::new(true, 23),
53//!     wss_data_block: 0x1234,
54//! });
55//! let field = DataField::new(0x10, vec![vps, wss]);
56//!
57//! let mut buf = vec![0u8; field.serialized_len()];
58//! field.serialize_into(&mut buf).unwrap();
59//! assert_eq!(DataField::parse(&buf).unwrap(), field);
60//! ```
61#![no_std]
62#![cfg_attr(docsrs, feature(doc_cfg))]
63#![warn(missing_docs)]
64// Runnable examples, embedded so they render on docs.rs and stay in sync with
65// the actual `examples/*.rs` files (shown, not compiled).
66#![doc = "\n## Runnable examples\n"]
67#![doc = "Run with `cargo run -p dvb-vbi --example <name>`.\n"]
68#![doc = "\n### `build_data_field`\n\n```rust,ignore"]
69#![doc = include_str!("../examples/build_data_field.rs")]
70#![doc = "```\n\n### `parse_data_field`\n\n```rust,ignore"]
71#![doc = include_str!("../examples/parse_data_field.rs")]
72#![doc = "```"]
73
74extern crate alloc;
75
76mod data_unit_id;
77mod error;
78mod line_header;
79mod payload;
80
81pub use data_unit_id::{
82    DataUnitId, ID_CLOSED_CAPTIONING, ID_EBU_TELETEXT_NON_SUBTITLE, ID_EBU_TELETEXT_SUBTITLE,
83    ID_INVERTED_TELETEXT, ID_MONOCHROME_422_SAMPLES, ID_STUFFING, ID_VPS, ID_WSS,
84};
85pub use error::{Error, Result};
86pub use line_header::{LINE_HEADER_LEN, LineHeader, RESERVED_PREFIX};
87pub use payload::{
88    CC_FIELD_LEN, ClosedCaptioningDataField, DataField, DataUnit, DataUnitPayload,
89    FRAMING_CODE_EBU, FRAMING_CODE_INVERTED, MONO_HEADER_LEN, MonochromeDataField,
90    TELETEXT_DATA_UNIT_LENGTH, TELETEXT_FIELD_LEN, TXT_DATA_BLOCK_LEN, TeletextDataField,
91    VPS_DATA_BLOCK_LEN, VPS_FIELD_LEN, VpsDataField, WSS_DATA_BLOCK_MASK, WSS_FIELD_LEN,
92    WSS_RESERVED_TAIL, WssDataField,
93};