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}