broadcast-common 9.4.0

Shared Parse/Serialize traits and CRC-32 MPEG-2 used by the dvb_si / dvb_t2mi / dvb_bbframe family.
Documentation

broadcast-common

crates.io docs.rs License

Shared primitives for the DVB crate family. Every sibling crate (dvb-si, dvb-t2mi, dvb-bbframe, …) depends on this crate and nothing else.

no_std + alloc support

broadcast-common is #![no_std] when the std feature is disabled. Only alloc is required — suitable for embedded/RTOS targets with a heap. The std feature (on by default) re-enables std::error::Error integration and chrono's wall-clock support. core::error::Error is used for the error trait bound in no_std builds (stabilised in Rust 1.81; workspace MSRV is 1.95.0). The MJD↔calendar float arithmetic uses libm::floor uniformly in both std and no_std builds, so the output is bit-identical on all targets.

What's in here

Parse<'a> and Serialize traits (traits)

The symmetric contract every wire type across the family implements:

pub trait Parse<'a>: Sized {
    type Error;
    fn parse(bytes: &'a [u8]) -> Result<Self, Self::Error>;
}

pub trait Serialize {
    type Error;
    fn serialized_len(&self) -> usize;
    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize, Self::Error>;
    fn to_bytes(&self) -> Vec<u8> where Self::Error: core::fmt::Debug;
}

Parse is lifetime-parametric so parsed structs can borrow directly from the input buffer (zero-copy). Serialize is split from Parse so owned types can implement it without carrying a lifetime. to_bytes() is a convenience allocator; serialize_into is the low-level building block.

CRC-32 MPEG-2 (crc32_mpeg2)

Polynomial 0x04C1_1DB7, initial value 0xFFFF_FFFF, MSB-first, no reflection, no final XOR — the CRC used by every PSI/SI section and every T2-MI packet. A precomputed 256-entry table is built at compile time (zero runtime initialisation cost).

let crc = broadcast_common::crc32_mpeg2::compute(&section_bytes[..section_bytes.len() - 4]);

BCD codec (bcd)

Binary-coded decimal helpers for the packed nibble fields throughout DVB (frequencies, symbol rates, HHMMSS time fields, HHMM offsets). Every encode has a symmetric decode; both return None rather than producing garbage on out-of-range input.

use broadcast_common::bcd;
assert_eq!(bcd::from_bcd_byte(0x42), Some(42));
assert_eq!(bcd::to_bcd_byte(42),     Some(0x42));

MJD+BCD time codec (time)

Decodes the 5-byte DVB UTC wire format (16-bit Modified Julian Date + 24-bit BCD HHMMSS) to a plain MjdBcdDateTime struct (no dependency required) or, if the chrono feature is enabled, to a chrono::DateTime<Utc>. The Duration / HHMMSS helpers for event durations are always available without chrono.

Bit-field codec (bits)

BitReader and BitWriter for big-endian MSB-first sub-byte fields — the bit order used throughout DVB/MPEG physical-layer signalling (L1-pre, L1-post, MATYPE, …). Fields can span byte boundaries; up to 64 bits per field. Both sides are symmetric.

use broadcast_common::bits::{BitReader, BitWriter};

let mut buf = [0u8; 2];
let mut w = BitWriter::new(&mut buf);
w.write_bits(0b101, 3).unwrap();
w.write_bits(0x1FF, 9).unwrap();

let mut r = BitReader::new(&buf);
assert_eq!(r.read_bits(3).unwrap(), 0b101);
assert_eq!(r.read_bits(9).unwrap(), 0x1FF);

impl_spec_display! macro

Project-wide helper that generates a Display impl for spec/field enums that delegate to an inherent fn name(&self) -> &'static str. Keeps spec token labels next to variant docs and greppable; removes identical boilerplate.

33-bit wrapping clock (clock33)

PTS/DTS (ISO/IEC 13818-1 §2.4.3.7) and SCTE-35 pts_time share a 2^33 modulus: unwrap_delta (ever-growing unwrapped timeline), wrapping_forward_distance, add / add_signed (modular addition) and signed_distance (shortest signed distance on the circle).

use broadcast_common::clock33::{WRAP_33BIT, add, signed_distance};
assert_eq!(add(WRAP_33BIT - 1, 2), 1);
assert_eq!(signed_distance(WRAP_33BIT - 10, 5), 15);

PUSI accumulation (pusi)

PusiAccumulator splits one PID's payload into payload_unit_start_indicator delimited units (ISO/IEC 13818-1 §2.4.3.2) with a memory cap (DEFAULT_MAX_UNIT_SIZE); mpeg-pes and mpeg-ts both wrap it.

Container-mux traits (mux)

The codec-agnostic vocabulary for the any-to-any muxing hub, mirroring the Parse / Serialize symmetry:

  • Unpackage — demux a packaged container into an in-memory media IR.
  • Package — mux a media IR back into a packaged container.
  • Decrypt / Encrypt — in-place sample (un)protection.

Each is generic over its input/output/media/config/key associated types plus type Error; no concrete media or codec types appear here. Unpackage ⇄ Package and Decrypt ⇄ Encrypt are inverse pairs. Concrete implementations live in the container crates (e.g. transmux).

Feature flags

Feature Default Description
std on Link std; enables std::error::Error and chrono's clock/timezone. Without it the crate is #![no_std] + alloc.
chrono off MJD↔chrono::DateTime<Utc> conversion in the time module.
serde off serde::Serialize derives on the shared spec types (currently cenc::CencScheme).

MSRV

1.95.0 — the workspace MSRV (#949). core::error::Error needed 1.81 (used as the error trait bound in no_std builds; stabilised in 1.81, workspace MSRV is 1.95.0).

Non-goals

  • A shared error enum. Each DVB crate owns a domain-specific Error; the shared traits keep it that way via type Error.
  • CRC-8 (used only by dvb-bbframe). Lives in the consumer.
  • Anything with a non-trivial dependency. If a helper needs bytes / serde, it belongs in a consumer with the matching feature flag.

Examples

Run with cargo run -p broadcast-common --example <name>:

  • crc_and_bcd — CRC-32/MPEG-2 over a buffer (plus the "data+CRC ⇒ 0" check) and BCD coding.
  • implement_parse_serialize — implement the symmetric Parse/Serialize contract for your own wire type and round-trip it.

License

MIT OR Apache-2.0, at your option.