Skip to main content

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}