Skip to main content

broadcast_common/
lib.rs

1//! Shared primitives for the dvb_si / dvb_t2mi / dvb_bbframe family.
2//!
3//! See individual modules for documentation: the [`Parse`] / [`Serialize`]
4//! traits every wire type implements, the MPEG-2 [`crc32_mpeg2`] CRC, the
5//! [`bcd`] / [`time`] / [`hex`] codecs, the [`len`] range-checked wire-field
6//! narrowing those serializers use, the [`mux`] container-mux traits, the
7//! [`cenc`] Common Encryption scheme identity those traits protect with, and
8//! the [`clock33`] 33-bit wrapping-clock arithmetic (`add`, `add_signed`,
9//! `signed_distance`, `unwrap_delta`), the [`pusi`] PUSI-delimited unit
10//! accumulator, and
11//! the [`ts_dup`] ITU-T H.222.0 §2.4.3.3 legal-duplicate-packet check shared
12//! by `dvb-conformance`, `media-doctor` and `ts-fix`.
13//!
14//! # Container-mux traits ([`mux`])
15//!
16//! The [`mux`] module defines the codec-agnostic vocabulary for the any-to-any
17//! muxing hub, mirroring the [`Parse`] / [`Serialize`] symmetry:
18//!
19//! - [`Unpackage`] — demux a packaged container into an in-memory media IR.
20//! - [`Package`] — mux a media IR back into a packaged container.
21//! - [`Decrypt`] / [`Encrypt`] — in-place sample (un)protection.
22//!
23//! Each is generic over its input/output/media/config/key associated types plus
24//! `type Error`; no concrete media or codec types appear here. `Unpackage` ⇄
25//! `Package` and `Decrypt` ⇄ `Encrypt` are inverse pairs. Concrete
26//! implementations live in the container crates (e.g. `transmux`).
27//!
28//! These are the *batch* container-mux contract, operating on a whole packaged
29//! container at once. For the complementary *incremental* contract — feed
30//! bytes in, poll typed output out, drive on a clock — see [`stage`].
31//!
32//! # Incremental staging ([`stage`])
33//!
34//! The [`stage`] module defines [`Stage`], the drive shape every
35//! streaming stage in the workspace (TS/FLV demuxers, HLS/LL-HLS segmenters,
36//! conformance monitors, …) is converging on: `feed`/`poll`/`finish` plus a
37//! [`Timestamp`] clock parameter and deadline hooks for
38//! purely time-driven work, and a [`Demand`] backpressure hint.
39//!
40//! # Quick start
41//! ```
42//! use broadcast_common::{bcd, crc32_mpeg2};
43//!
44//! // Binary-coded decimal (as used in MJD/BCD time fields):
45//! assert_eq!(bcd::from_bcd_byte(0x42), Some(42));
46//! assert_eq!(bcd::to_bcd_byte(42), Some(0x42));
47//!
48//! // MPEG-2 CRC-32 over a section body (deterministic):
49//! let crc = crc32_mpeg2::compute(&[0xDE, 0xAD, 0xBE, 0xEF]);
50//! assert_eq!(crc, crc32_mpeg2::compute(&[0xDE, 0xAD, 0xBE, 0xEF]));
51//! ```
52
53#![forbid(unsafe_code)]
54#![warn(missing_docs)]
55#![cfg_attr(docsrs, feature(doc_cfg))]
56#![cfg_attr(not(feature = "std"), no_std)]
57// The crate's runnable examples, embedded so they render on docs.rs and stay in
58// sync with the actual `examples/*.rs` files (shown, not compiled).
59#![doc = "\n# Examples\n"]
60#![doc = "Two runnable examples ship with this crate (`cargo run -p broadcast-common --example <name>`).\n"]
61#![doc = "\n## `crc_and_bcd`\n\n```rust,ignore"]
62#![doc = include_str!("../examples/crc_and_bcd.rs")]
63#![doc = "```\n\n## `implement_parse_serialize`\n\n```rust,ignore"]
64#![doc = include_str!("../examples/implement_parse_serialize.rs")]
65#![doc = "```"]
66
67extern crate alloc;
68
69pub mod bcd;
70pub mod bits;
71pub mod cenc;
72pub mod clock33;
73pub mod crc32_mpeg2;
74pub mod hex;
75pub mod len;
76pub mod mux;
77pub mod pusi;
78pub mod seq;
79pub mod stage;
80pub mod time;
81pub mod traits;
82pub mod ts_dup;
83
84pub use cenc::CencScheme;
85pub use mux::{Decrypt, Encrypt, Package, Unpackage};
86pub use stage::{Demand, Stage, Timestamp};
87pub use traits::{Parse, Serialize};
88
89/// Generate a [`core::fmt::Display`] impl for a spec/field enum that delegates
90/// to an inherent `fn name(&self) -> &'static str`.
91///
92/// This is the project-wide convention for every public spec/field enum across
93/// the `dvb-*` crates (see issue #204): `name()` is the hand-written,
94/// zero-alloc static spec token (lossy on the reserved/unknown arm, which
95/// returns `"reserved"`), and `Display` is the lossless, composable view that
96/// delegates to it. The labels themselves live in `name()` in source — never in
97/// this macro — so they sit next to the variant docs and stay greppable. This
98/// macro carries no labels; it only removes the otherwise-identical `Display`
99/// boilerplate and keeps the two in lockstep.
100///
101/// # Forms
102/// - `impl_spec_display!(Ty)` — every variant's `Display` is exactly `name()`.
103///   Use when there is no byte-bearing catch-all (or its byte need not be
104///   shown), e.g. a unit `Reserved` variant.
105/// - `impl_spec_display!(Ty, Var1, Var2, …)` — each named variant is a
106///   single-field tuple binding a byte; `Display` renders it as
107///   `"{name}(0x{:02X})"` so the value is preserved (e.g. `Reserved(0x1A)` →
108///   `reserved(0x1A)`, `UserDefined(0x1A)` → `user defined(0x1A)`). All other
109///   variants delegate to `name()`.
110///
111/// ```
112/// pub enum Mode { Normal, HighEfficiency, Reserved(u8) }
113/// impl Mode {
114///     pub fn name(&self) -> &'static str {
115///         match self {
116///             Self::Normal => "normal",
117///             Self::HighEfficiency => "high efficiency",
118///             Self::Reserved(_) => "reserved",
119///         }
120///     }
121/// }
122/// broadcast_common::impl_spec_display!(Mode, Reserved);
123/// assert_eq!(Mode::Normal.to_string(), "normal");
124/// assert_eq!(Mode::Reserved(0x1A).to_string(), "reserved(0x1A)");
125/// ```
126#[macro_export]
127macro_rules! impl_spec_display {
128    ($ty:ty) => {
129        impl ::core::fmt::Display for $ty {
130            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
131                f.write_str(self.name())
132            }
133        }
134    };
135    ($ty:ty, $($resv:ident),+ $(,)?) => {
136        impl ::core::fmt::Display for $ty {
137            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
138                match self {
139                    $( Self::$resv(v) => ::core::write!(f, "{}(0x{:02X})", self.name(), v), )+
140                    other => f.write_str(other.name()),
141                }
142            }
143        }
144    };
145}