Skip to main content

broadcast_common/
mux.rs

1//! Container-mux vocabulary traits: [`Unpackage`] / [`Package`] and
2//! [`Decrypt`] / [`Encrypt`].
3//!
4//! Where [`Parse`](crate::Parse) / [`Serialize`](crate::Serialize) are the
5//! *wire-structure* contract (one concrete type ⇄ its bytes), these four traits
6//! are the *container-mux* contract: they move between a packaged container
7//! representation (an fMP4/CMAF segment, an HLS playlist, an MPEG-TS multiplex,
8//! …) and an in-memory *media* intermediate representation (elementary tracks +
9//! coded samples). They are deliberately abstract — the associated types name
10//! the input/output/media/key material, and no concrete media or codec type
11//! appears in `broadcast-common`. Concrete `Media` IR types and impls live in
12//! the consuming crates (e.g. `transmux`).
13//!
14//! The pairs are inverses:
15//!
16//! - [`Unpackage`] ⇄ [`Package`] — demux a container into media, and mux media
17//!   back into a container. Round-tripping `unpackage` then `package` should
18//!   reproduce the same media structure.
19//! - [`Decrypt`] ⇄ [`Encrypt`] — remove and (re)apply sample protection on an
20//!   in-place media IR. Round-tripping `decrypt` then `encrypt` with the same
21//!   key/config material should reproduce the same protected media.
22//!
23//! Each trait picks its own error type via `type Error`, mirroring the
24//! [`Parse`](crate::Parse) / [`Serialize`](crate::Serialize) style, so
25//! domain-specific error variants stay visible to the caller.
26//!
27//! These four traits are the **batch / whole-file** container-mux contract:
28//! each call takes (or produces) a complete packaged container or media value
29//! in one shot. They are deliberately distinct from
30//! [`crate::stage::Stage`], the **incremental** contract for stages that are
31//! fed bytes over time, poll output as it becomes ready, and may act on a
32//! clock/deadline with no new input at all (streaming demuxers, segmenters,
33//! conformance monitors, …). A single concrete type may implement both where
34//! it makes sense (e.g. a demuxer with both a one-shot `unpackage` and a
35//! streaming `Stage` front end), but neither contract implies the other.
36
37/// Demux a packaged container into an in-memory media representation.
38///
39/// The inverse of [`Package`]. `Self::Input` is the packaged form (typically a
40/// byte slice or a stream of segments); `Self::Media` is the decoded elementary
41/// representation (tracks + coded samples).
42pub trait Unpackage {
43    /// The packaged input this demuxer consumes (e.g. `&[u8]` of an fMP4 file).
44    type Input;
45    /// The in-memory media representation produced.
46    type Media;
47    /// The error type this implementer returns.
48    type Error;
49
50    /// Demux `input` into a [`Self::Media`], borrowing or owning as the
51    /// implementer chooses. Returns `Err(Self::Error)` on any container
52    /// violation or buffer underrun.
53    fn unpackage(&mut self, input: Self::Input) -> Result<Self::Media, Self::Error>;
54}
55
56/// Mux an in-memory media representation into a packaged container.
57///
58/// The inverse of [`Unpackage`]. `Self::Output` is the packaged form produced
59/// (e.g. a `Vec<u8>` of an fMP4 segment or a `String` playlist).
60pub trait Package {
61    /// The in-memory media representation this muxer consumes.
62    type Media;
63    /// The packaged output produced (e.g. `Vec<u8>` or `String`).
64    type Output;
65    /// The error type this implementer returns.
66    type Error;
67
68    /// Mux `media` into a [`Self::Output`]. Returns `Err(Self::Error)` on any
69    /// constraint violation (e.g. an empty track list or an oversized field).
70    fn package(&mut self, media: &Self::Media) -> Result<Self::Output, Self::Error>;
71}
72
73/// Remove sample protection from a media representation, in place.
74///
75/// The inverse of [`Encrypt`]. `Self::Keys` is the key material required to
76/// unprotect the samples (e.g. per-key-ID content keys).
77pub trait Decrypt {
78    /// The in-memory media representation operated on in place.
79    type Media;
80    /// The key material required to unprotect samples.
81    type Keys;
82    /// The error type this implementer returns.
83    type Error;
84
85    /// Decrypt the protected samples in `media` in place using `keys`.
86    fn decrypt(&self, media: &mut Self::Media, keys: &Self::Keys) -> Result<(), Self::Error>;
87}
88
89/// Apply sample protection to a media representation, in place.
90///
91/// The inverse of [`Decrypt`]. `Self::Config` describes the protection scheme
92/// to apply (e.g. `cenc`/`cbcs` scheme + key IDs + IV material).
93///
94/// Takes `&mut self`, not `&self`: an implementer whose scheme requires
95/// per-sample IV uniqueness *per key, for all time* (e.g. AES-CTR) generally
96/// cannot make that guarantee across separate calls from a stateless value —
97/// it needs to carry running state (such as a continuing IV counter) forward
98/// from one call to the next. `&mut self` lets an implementer own that state;
99/// one that needs none is free to ignore the mutability.
100pub trait Encrypt {
101    /// The in-memory media representation operated on in place.
102    type Media;
103    /// The protection scheme configuration to apply.
104    type Config;
105    /// The error type this implementer returns.
106    type Error;
107
108    /// Encrypt the samples in `media` in place per `cfg`.
109    fn encrypt(&mut self, media: &mut Self::Media, cfg: &Self::Config) -> Result<(), Self::Error>;
110}
111
112#[cfg(test)]
113mod tests {
114    use super::*;
115    use alloc::vec::Vec;
116
117    // A trivial media IR + a full set of impls, to prove the traits are usable
118    // (object-safe method shapes, generic associated types resolve) and that
119    // the inverse pairs round-trip on a hand-built value.
120    #[derive(Debug, Clone, PartialEq, Eq, Default)]
121    struct Media {
122        tracks: Vec<Vec<u8>>,
123    }
124
125    struct Codec;
126
127    impl Unpackage for Codec {
128        type Input = &'static [u8];
129        type Media = Media;
130        type Error = ();
131        fn unpackage(&mut self, input: Self::Input) -> Result<Self::Media, Self::Error> {
132            Ok(Media {
133                tracks: input.iter().map(|b| alloc::vec![*b]).collect(),
134            })
135        }
136    }
137
138    impl Package for Codec {
139        type Media = Media;
140        type Output = Vec<u8>;
141        type Error = ();
142        fn package(&mut self, media: &Self::Media) -> Result<Self::Output, Self::Error> {
143            Ok(media.tracks.iter().map(|t| t[0]).collect())
144        }
145    }
146
147    impl Decrypt for Codec {
148        type Media = Media;
149        type Keys = u8;
150        type Error = ();
151        fn decrypt(&self, media: &mut Self::Media, keys: &Self::Keys) -> Result<(), Self::Error> {
152            for t in &mut media.tracks {
153                for b in t {
154                    *b ^= *keys;
155                }
156            }
157            Ok(())
158        }
159    }
160
161    impl Encrypt for Codec {
162        type Media = Media;
163        type Config = u8;
164        type Error = ();
165        fn encrypt(
166            &mut self,
167            media: &mut Self::Media,
168            cfg: &Self::Config,
169        ) -> Result<(), Self::Error> {
170            for t in &mut media.tracks {
171                for b in t {
172                    *b ^= *cfg;
173                }
174            }
175            Ok(())
176        }
177    }
178
179    #[test]
180    fn traits_compile_and_round_trip() {
181        let mut codec = Codec;
182        let input: &'static [u8] = &[1, 2, 3];
183        let media = codec.unpackage(input).unwrap();
184        assert_eq!(media.tracks.len(), 3);
185        // Package ⇄ Unpackage inverse on this trivial IR.
186        assert_eq!(codec.package(&media).unwrap(), alloc::vec![1u8, 2, 3]);
187
188        // Encrypt ⇄ Decrypt inverse (XOR with the same key is self-inverse).
189        let mut m = media.clone();
190        codec.encrypt(&mut m, &0xAA).unwrap();
191        assert_ne!(m, media);
192        codec.decrypt(&mut m, &0xAA).unwrap();
193        assert_eq!(m, media);
194    }
195}