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