oxideav_core/layer.rs
1//! Layer / view identity for multi-layer and multi-view video.
2//!
3//! Scalable and multi-view coding schemes (spatial / quality / view
4//! scalability in H.264 Annexes G and H, H.265 Annex F, H.266 Annex F,
5//! AV1 operating points with spatial or temporal layers, …) put more
6//! than one *layer* of coded pictures in a single stream. Each layer
7//! carries an identifier in its coded units (H.265 / H.266
8//! `nuh_layer_id`, AV1 `spatial_id`, …); the layer with identifier `0`
9//! is the base layer, and a stream's parameter sets may map layer
10//! identifiers to *views* (a camera position in stereoscopic or
11//! multi-view content), to dependency / quality levels, or to auxiliary
12//! pictures (alpha, depth).
13//!
14//! This module gives decoders a way to tag every output frame with the
15//! layer it belongs to — [`LayerIdentity`], attached through
16//! [`VideoFrame::layer`](crate::VideoFrame::layer) — and containers /
17//! decoders a way to describe the stream's layer structure up front —
18//! [`LayerInfo`], carried in
19//! [`CodecParameters::layers`](crate::CodecParameters::layers). Both
20//! are optional and absent for single-layer streams, which remain
21//! byte-for-byte what they always were.
22//!
23//! The identifiers are the codec's own values passed through verbatim;
24//! this crate assigns no meaning of its own to them beyond "`0` is the
25//! base layer". A container that selects a subset of layers (an
26//! operating point, a single view) filters on these ids; a renderer
27//! that shows a stereo pair routes frames by `view_id`; a converter
28//! ignores them.
29
30/// Which layer / view a decoded [`VideoFrame`](crate::VideoFrame)
31/// belongs to.
32///
33/// `#[non_exhaustive]`: build it with [`new`](Self::new) and the
34/// `with_*` builders, read the public fields directly. `Default` is
35/// the base layer with no view and no access-unit index.
36#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
37#[non_exhaustive]
38pub struct LayerIdentity {
39 /// The codec's layer identifier for the frame (H.265 / H.266
40 /// `nuh_layer_id`, AV1 `spatial_id`, …). `0` is the base layer.
41 pub layer_id: u16,
42 /// The view the frame belongs to, for multi-view content (the
43 /// codec's view identifier, e.g. H.265 Annex F `ViewId`). `None`
44 /// when the stream is not multi-view or the decoder does not
45 /// know the mapping.
46 pub view_id: Option<u16>,
47 /// Running index of the *access unit* (the set of pictures across
48 /// all layers that share one presentation instant) the frame comes
49 /// from, when the decoder counts them. Lets a consumer regroup the
50 /// per-layer frames of one instant without relying on equal `pts`
51 /// values. `None` when not tracked.
52 pub access_unit: Option<u64>,
53}
54
55impl LayerIdentity {
56 /// Size in bytes of the frame side-channel encoding
57 /// ([`to_bytes`](Self::to_bytes)).
58 pub const WIRE_LEN: usize = 13;
59
60 /// Identity for `layer_id`, with no view and no access-unit index.
61 pub const fn new(layer_id: u16) -> Self {
62 Self {
63 layer_id,
64 view_id: None,
65 access_unit: None,
66 }
67 }
68
69 /// The base layer (`layer_id == 0`), no view, no access unit —
70 /// identical to `Default`.
71 pub const fn base() -> Self {
72 Self::new(0)
73 }
74
75 /// Builder: set the view identifier.
76 pub const fn with_view_id(mut self, view_id: u16) -> Self {
77 self.view_id = Some(view_id);
78 self
79 }
80
81 /// Builder: set the access-unit index.
82 pub const fn with_access_unit(mut self, index: u64) -> Self {
83 self.access_unit = Some(index);
84 self
85 }
86
87 /// `true` for the base layer (`layer_id == 0`).
88 pub const fn is_base_layer(&self) -> bool {
89 self.layer_id == 0
90 }
91
92 /// Fixed-size wire form used by the [`VideoFrame`](crate::VideoFrame)
93 /// side-channel record: one flags byte (bit 0 = `view_id`
94 /// present, bit 1 = `access_unit` present), then `layer_id` as
95 /// two little-endian bytes, `view_id` as two (zero when absent)
96 /// and `access_unit` as eight (zero when absent).
97 pub fn to_bytes(&self) -> [u8; Self::WIRE_LEN] {
98 let mut out = [0u8; Self::WIRE_LEN];
99 let mut flags = 0u8;
100 if self.view_id.is_some() {
101 flags |= 1;
102 }
103 if self.access_unit.is_some() {
104 flags |= 2;
105 }
106 out[0] = flags;
107 out[1..3].copy_from_slice(&self.layer_id.to_le_bytes());
108 out[3..5].copy_from_slice(&self.view_id.unwrap_or(0).to_le_bytes());
109 out[5..13].copy_from_slice(&self.access_unit.unwrap_or(0).to_le_bytes());
110 out
111 }
112
113 /// Inverse of [`to_bytes`](Self::to_bytes). Accepts any slice of at
114 /// least [`WIRE_LEN`](Self::WIRE_LEN) bytes (extra trailing bytes
115 /// are reserved and ignored); returns `None` for a shorter slice.
116 /// Unknown flag bits are ignored.
117 pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
118 let b = bytes.get(..Self::WIRE_LEN)?;
119 let flags = b[0];
120 let layer_id = u16::from_le_bytes([b[1], b[2]]);
121 let view_id = (flags & 1 != 0).then(|| u16::from_le_bytes([b[3], b[4]]));
122 let access_unit = (flags & 2 != 0).then(|| {
123 let mut au = [0u8; 8];
124 au.copy_from_slice(&b[5..13]);
125 u64::from_le_bytes(au)
126 });
127 Some(Self {
128 layer_id,
129 view_id,
130 access_unit,
131 })
132 }
133}
134
135impl std::fmt::Display for LayerIdentity {
136 /// `layer N`, `layer N view V`, optionally `@au A`.
137 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138 write!(f, "layer {}", self.layer_id)?;
139 if let Some(v) = self.view_id {
140 write!(f, " view {v}")?;
141 }
142 if let Some(au) = self.access_unit {
143 write!(f, " @au {au}")?;
144 }
145 Ok(())
146 }
147}
148
149/// Stream-level description of one layer of a multi-layer video
150/// stream, carried in
151/// [`CodecParameters::layers`](crate::CodecParameters::layers).
152///
153/// `#[non_exhaustive]`: build it with [`new`](Self::new) and the
154/// `with_*` builders, read the public fields directly.
155#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
156#[non_exhaustive]
157pub struct LayerInfo {
158 /// The codec's layer identifier (the value frames of this layer
159 /// report in [`LayerIdentity::layer_id`]). `0` is the base layer.
160 pub layer_id: u16,
161 /// The view this layer codes, for multi-view content; `None` when
162 /// the layer is not a view (spatial / quality enhancement,
163 /// auxiliary pictures) or the mapping is unknown.
164 pub view_id: Option<u16>,
165 /// Layer identifiers this layer predicts from directly (its
166 /// reference layers). Empty for the base layer and for streams
167 /// whose dependency structure is not described. A consumer that
168 /// wants to decode this layer must also feed every layer reachable
169 /// through `depends_on`.
170 pub depends_on: Vec<u16>,
171}
172
173impl LayerInfo {
174 /// Description of layer `layer_id` with no view and no declared
175 /// dependencies.
176 pub fn new(layer_id: u16) -> Self {
177 Self {
178 layer_id,
179 view_id: None,
180 depends_on: Vec::new(),
181 }
182 }
183
184 /// Builder: set the view identifier.
185 pub fn with_view_id(mut self, view_id: u16) -> Self {
186 self.view_id = Some(view_id);
187 self
188 }
189
190 /// Builder: replace the direct reference-layer list.
191 pub fn with_depends_on(mut self, layers: impl Into<Vec<u16>>) -> Self {
192 self.depends_on = layers.into();
193 self
194 }
195
196 /// `true` for the base layer (`layer_id == 0`).
197 pub fn is_base_layer(&self) -> bool {
198 self.layer_id == 0
199 }
200
201 /// The [`LayerIdentity`] a frame of this layer would carry
202 /// (`layer_id` and `view_id`; no access-unit index).
203 pub fn identity(&self) -> LayerIdentity {
204 LayerIdentity {
205 layer_id: self.layer_id,
206 view_id: self.view_id,
207 access_unit: None,
208 }
209 }
210}
211
212#[cfg(test)]
213mod tests {
214 use super::*;
215
216 #[test]
217 fn default_identity_is_the_base_layer() {
218 let id = LayerIdentity::default();
219 assert_eq!(id, LayerIdentity::base());
220 assert_eq!(id, LayerIdentity::new(0));
221 assert!(id.is_base_layer());
222 assert_eq!(id.layer_id, 0);
223 assert_eq!(id.view_id, None);
224 assert_eq!(id.access_unit, None);
225 assert_eq!(id.to_string(), "layer 0");
226 }
227
228 #[test]
229 fn builders_set_fields() {
230 let id = LayerIdentity::new(3).with_view_id(1).with_access_unit(42);
231 assert_eq!(id.layer_id, 3);
232 assert_eq!(id.view_id, Some(1));
233 assert_eq!(id.access_unit, Some(42));
234 assert!(!id.is_base_layer());
235 assert_eq!(id.to_string(), "layer 3 view 1 @au 42");
236 assert_eq!(
237 LayerIdentity::new(2).with_access_unit(7).to_string(),
238 "layer 2 @au 7"
239 );
240 }
241
242 #[test]
243 fn wire_bytes_round_trip() {
244 for id in [
245 LayerIdentity::base(),
246 LayerIdentity::new(0x1234),
247 LayerIdentity::new(1).with_view_id(0),
248 LayerIdentity::new(1).with_view_id(0xBEEF),
249 LayerIdentity::new(5).with_access_unit(0),
250 LayerIdentity::new(5).with_access_unit(u64::MAX),
251 LayerIdentity::new(u16::MAX)
252 .with_view_id(u16::MAX)
253 .with_access_unit(0x0102_0304_0506_0708),
254 ] {
255 let bytes = id.to_bytes();
256 assert_eq!(LayerIdentity::from_bytes(&bytes), Some(id), "{id}");
257 }
258 // Explicit layout check.
259 let b = LayerIdentity::new(0x0201)
260 .with_view_id(0x0403)
261 .with_access_unit(0x0C0B_0A09_0807_0605)
262 .to_bytes();
263 assert_eq!(b, [3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
264 // A present-but-zero view is distinguishable from an absent one.
265 assert_eq!(LayerIdentity::new(1).with_view_id(0).to_bytes()[0], 1);
266 assert_eq!(LayerIdentity::new(1).to_bytes()[0], 0);
267 }
268
269 #[test]
270 fn wire_bytes_tolerate_extra_and_reject_short() {
271 let mut long = LayerIdentity::new(9).to_bytes().to_vec();
272 long.extend_from_slice(&[0xFF; 5]);
273 assert_eq!(
274 LayerIdentity::from_bytes(&long),
275 Some(LayerIdentity::new(9))
276 );
277 assert_eq!(LayerIdentity::from_bytes(&long[..12]), None);
278 assert_eq!(LayerIdentity::from_bytes(&[]), None);
279 // Unknown flag bits are ignored.
280 let mut b = LayerIdentity::new(1).to_bytes();
281 b[0] |= 0xF0;
282 assert_eq!(LayerIdentity::from_bytes(&b), Some(LayerIdentity::new(1)));
283 }
284
285 #[test]
286 fn layer_info_builders_and_identity() {
287 let base = LayerInfo::new(0);
288 assert!(base.is_base_layer());
289 assert_eq!(base, LayerInfo::default());
290 assert!(base.depends_on.is_empty());
291 assert_eq!(base.identity(), LayerIdentity::base());
292
293 let right = LayerInfo::new(1).with_view_id(1).with_depends_on([0u16]);
294 assert_eq!(right.layer_id, 1);
295 assert_eq!(right.view_id, Some(1));
296 assert_eq!(right.depends_on, vec![0]);
297 assert_eq!(right.identity(), LayerIdentity::new(1).with_view_id(1));
298
299 let quality = LayerInfo::new(2).with_depends_on(vec![0, 1]);
300 assert_eq!(quality.depends_on, vec![0, 1]);
301 assert_eq!(quality.clone(), quality);
302 }
303}