Skip to main content

mp4_atom/moov/trak/mdia/minf/stbl/stsd/
vexu.rs

1use crate::*;
2
3use super::spatial::{decode_children, decode_slot, encode_kept, keep_invalid, spatial_children};
4
5/// Video Extended Usage Box, from Apple's [QuickTime and ISO Base Media File
6/// Formats and Spatial and Immersive Media][apple]. The same document defines
7/// its children below, and the sibling [`Hfov`].
8///
9/// Children may come in any order, and the spec expects new ones, so
10/// children this crate does not model (such as the lens collection `lnsc`)
11/// are kept verbatim in `unknown`, as are repeats and children that do not
12/// decode.
13///
14/// [apple]: https://developer.apple.com/av-foundation/Stereo-Video-ISOBMFF-Extensions.pdf
15#[derive(Debug, Clone, PartialEq, Default)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17#[cfg_attr(feature = "serde", serde(default))]
18pub struct Vexu {
19    pub must: Option<Must>,
20    pub proj: Option<VexuProj>,
21    pub eyes: Option<Eyes>,
22    pub pack: Option<Pack>,
23    pub unknown: Vec<Any>,
24}
25
26impl Eq for Vexu {}
27
28impl Atom for Vexu {
29    const KIND: FourCC = FourCC::new(b"vexu");
30
31    // Apple does not prescribe an order. `proj` goes last, after any other
32    // child: a reader may read only the `prji` at the start of `proj` and take
33    // the rest of `proj` for the next sibling. `eyes` goes before `pack`, so a
34    // reader applying them in order ends with the packing from `pack`.
35    spatial_children! { must: Must, eyes: Eyes, pack: Pack; last proj: VexuProj }
36}
37
38/// Required Box Types Box: the types of sibling boxes a reader must
39/// understand to use the parent box. Zero entries are reserved.
40#[derive(Debug, Clone, PartialEq, Eq, Default)]
41#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
42pub struct Must {
43    pub required_box_types: Vec<FourCC>,
44}
45
46impl AtomExt for Must {
47    type Ext = FullBoxV0;
48
49    const KIND_EXT: FourCC = FourCC::new(b"must");
50
51    fn decode_body_ext<B: Buf>(buf: &mut B, _ext: FullBoxV0) -> Result<Self> {
52        let mut required_box_types = Vec::with_capacity(buf.remaining() / 4);
53        while buf.remaining() >= 4 {
54            required_box_types.push(FourCC::decode(buf)?);
55        }
56        Ok(Must { required_box_types })
57    }
58
59    fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<FullBoxV0> {
60        self.required_box_types.encode(buf)?;
61        Ok(FullBoxV0)
62    }
63}
64
65/// The Projection Box inside `vexu`: the projection kind, and optionally the
66/// box for that kind. A writer sets `prji`: a reader may refuse a `proj`
67/// without one.
68#[derive(Debug, Clone, PartialEq, Default)]
69#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
70#[cfg_attr(feature = "serde", serde(default))]
71pub struct VexuProj {
72    pub must: Option<Must>,
73    pub prji: Option<Prji>,
74    pub kind: Option<VexuProjKind>,
75    pub unknown: Vec<Any>,
76}
77
78impl Eq for VexuProj {}
79
80impl Atom for VexuProj {
81    const KIND: FourCC = FourCC::new(b"proj");
82
83    fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
84        let mut proj = VexuProj::default();
85        decode_children(buf, |kind, body| match kind {
86            Must::KIND => decode_slot(Self::KIND, &mut proj.must, body, &mut proj.unknown),
87            Prji::KIND => decode_slot(Self::KIND, &mut proj.prji, body, &mut proj.unknown),
88            kind => match VexuProjKind::from_kind(kind) {
89                Some(projection) => {
90                    let decoded = match proj.kind {
91                        Some(_) => Err(Error::DuplicateBox(kind)),
92                        None => VexuProjKind::check_box(kind, body),
93                    };
94                    match decoded {
95                        Ok(()) => proj.kind = Some(projection),
96                        Err(err) => keep_invalid(Self::KIND, kind, body, err, &mut proj.unknown),
97                    }
98                }
99                None => proj.unknown.push(Any::Unknown(kind, body.to_vec())),
100            },
101        })?;
102        Ok(proj)
103    }
104
105    // `prji` goes first: a reader may refuse a `proj` that starts with
106    // anything else.
107    fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
108        let projection_kind = |kind| VexuProjKind::from_kind(kind).is_some();
109        self.prji.encode(buf)?;
110        encode_kept(&self.unknown, |kind| kind == Prji::KIND, buf)?;
111        self.kind.encode(buf)?;
112        encode_kept(&self.unknown, projection_kind, buf)?;
113        self.must.encode(buf)?;
114        encode_kept(&self.unknown, |kind| kind == Must::KIND, buf)?;
115        encode_kept(
116            &self.unknown,
117            |kind| !matches!(kind, Prji::KIND | Must::KIND) && !projection_kind(kind),
118            buf,
119        )
120    }
121}
122
123/// Projection Information.
124#[derive(Debug, Clone, Copy, PartialEq, Eq)]
125#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
126pub struct Prji {
127    pub projection_kind: FourCC,
128}
129
130impl Prji {
131    pub const RECTILINEAR: FourCC = FourCC::new(b"rect");
132    pub const EQUIRECTANGULAR: FourCC = FourCC::new(b"equi");
133    pub const HALF_EQUIRECTANGULAR: FourCC = FourCC::new(b"hequ");
134    pub const FISHEYE: FourCC = FourCC::new(b"fish");
135    pub const PARAMETRIC_IMMERSIVE: FourCC = FourCC::new(b"prim");
136}
137
138impl AtomExt for Prji {
139    type Ext = FullBoxV0;
140
141    const KIND_EXT: FourCC = FourCC::new(b"prji");
142
143    fn decode_body_ext<B: Buf>(buf: &mut B, _ext: FullBoxV0) -> Result<Self> {
144        Ok(Prji {
145            projection_kind: FourCC::decode(buf)?,
146        })
147    }
148
149    fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<FullBoxV0> {
150        self.projection_kind.encode(buf)?;
151        Ok(FullBoxV0)
152    }
153}
154
155/// The box for a projection kind, a sibling of `prji` whose type matches its
156/// `projection_kind`. None of these boxes defines any fields yet.
157#[derive(Debug, Clone, Copy, PartialEq, Eq)]
158#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
159#[non_exhaustive]
160pub enum VexuProjKind {
161    /// RectilinearProjectionBox, `rect`.
162    Rectilinear,
163    /// FullEquirectangularProjectionBox, `equi`.
164    Equirectangular,
165    /// HalfEquirectangularProjectionBox, `hequ`.
166    HalfEquirectangular,
167    /// FisheyeProjectionBox, `fish`.
168    Fisheye,
169    /// ParametricImmersiveMediaProjectionBox, `prim`.
170    ParametricImmersive,
171}
172
173impl VexuProjKind {
174    /// The box type, which is also the `projection_kind` it goes with.
175    pub fn kind(self) -> FourCC {
176        match self {
177            Self::Rectilinear => Prji::RECTILINEAR,
178            Self::Equirectangular => Prji::EQUIRECTANGULAR,
179            Self::HalfEquirectangular => Prji::HALF_EQUIRECTANGULAR,
180            Self::Fisheye => Prji::FISHEYE,
181            Self::ParametricImmersive => Prji::PARAMETRIC_IMMERSIVE,
182        }
183    }
184
185    fn from_kind(kind: FourCC) -> Option<Self> {
186        Some(match kind {
187            Prji::RECTILINEAR => Self::Rectilinear,
188            Prji::EQUIRECTANGULAR => Self::Equirectangular,
189            Prji::HALF_EQUIRECTANGULAR => Self::HalfEquirectangular,
190            Prji::FISHEYE => Self::Fisheye,
191            Prji::PARAMETRIC_IMMERSIVE => Self::ParametricImmersive,
192            _ => return None,
193        })
194    }
195
196    /// Each kind is a FullBox with version and flags 0 and no fields.
197    fn check_box(kind: FourCC, mut body: &[u8]) -> Result<()> {
198        FullBoxV0::decode(u32::decode(&mut body)?)?;
199        match body.is_empty() {
200            true => Ok(()),
201            false => Err(Error::UnderDecode(kind)),
202        }
203    }
204}
205
206impl Encode for VexuProjKind {
207    fn encode<B: BufMut>(&self, buf: &mut B) -> Result<()> {
208        buf.encode_atom(self.kind(), |buf| 0u32.encode(buf))
209    }
210}
211
212/// Stereo View Box. The spec requires `stri`; it is optional here, as a
213/// parser does not validate, but a writer sets it.
214#[derive(Debug, Clone, PartialEq, Default)]
215#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
216#[cfg_attr(feature = "serde", serde(default))]
217pub struct Eyes {
218    pub must: Option<Must>,
219    pub stri: Option<Stri>,
220    pub hero: Option<Hero>,
221    pub cams: Option<Cams>,
222    pub cmfy: Option<Cmfy>,
223    pub unknown: Vec<Any>,
224}
225
226impl Eq for Eyes {}
227
228impl Atom for Eyes {
229    const KIND: FourCC = FourCC::new(b"eyes");
230
231    spatial_children! { must: Must, stri: Stri, hero: Hero, cams: Cams, cmfy: Cmfy }
232}
233
234/// Stereo View Information. Readers must treat a box with non-zero reserved
235/// bits as invalid, so such a box does not decode and is kept verbatim.
236#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
237#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
238pub struct Stri {
239    pub eye_views_reversed: bool,
240    pub has_additional_views: bool,
241    pub has_right_eye_view: bool,
242    pub has_left_eye_view: bool,
243}
244
245impl AtomExt for Stri {
246    type Ext = FullBoxV0;
247
248    const KIND_EXT: FourCC = FourCC::new(b"stri");
249
250    fn decode_body_ext<B: Buf>(buf: &mut B, _ext: FullBoxV0) -> Result<Self> {
251        let bits = u8::decode(buf)?;
252        if bits & 0xf0 != 0 {
253            return Err(Error::Reserved);
254        }
255        Ok(Stri {
256            eye_views_reversed: bits & 0b1000 != 0,
257            has_additional_views: bits & 0b0100 != 0,
258            has_right_eye_view: bits & 0b0010 != 0,
259            has_left_eye_view: bits & 0b0001 != 0,
260        })
261    }
262
263    fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<FullBoxV0> {
264        let bits = (self.eye_views_reversed as u8) << 3
265            | (self.has_additional_views as u8) << 2
266            | (self.has_right_eye_view as u8) << 1
267            | (self.has_left_eye_view as u8);
268        bits.encode(buf)?;
269        Ok(FullBoxV0)
270    }
271}
272
273/// Hero Stereo Eye Description: the eye to show in 2D. Values of 3 and above
274/// are reserved, and readers treat them as no hero eye.
275#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
276#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
277pub struct Hero {
278    pub hero_eye_indicator: u8,
279}
280
281impl Hero {
282    pub const NONE: u8 = 0;
283    pub const LEFT: u8 = 1;
284    pub const RIGHT: u8 = 2;
285}
286
287impl AtomExt for Hero {
288    type Ext = FullBoxV0;
289
290    const KIND_EXT: FourCC = FourCC::new(b"hero");
291
292    fn decode_body_ext<B: Buf>(buf: &mut B, _ext: FullBoxV0) -> Result<Self> {
293        Ok(Hero {
294            hero_eye_indicator: u8::decode(buf)?,
295        })
296    }
297
298    fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<FullBoxV0> {
299        self.hero_eye_indicator.encode(buf)?;
300        Ok(FullBoxV0)
301    }
302}
303
304/// Stereo Camera System. A reader may refuse a `cams` that holds anything
305/// besides a single `blin`, so a writer leaves `must` and `unknown` empty and
306/// does not write an empty `cams`.
307#[derive(Debug, Clone, PartialEq, Default)]
308#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
309#[cfg_attr(feature = "serde", serde(default))]
310pub struct Cams {
311    pub must: Option<Must>,
312    pub blin: Option<Blin>,
313    pub unknown: Vec<Any>,
314}
315
316impl Eq for Cams {}
317
318impl Atom for Cams {
319    const KIND: FourCC = FourCC::new(b"cams");
320
321    spatial_children! { must: Must, blin: Blin }
322}
323
324/// Stereo Camera System Baseline: the distance between the lens centres, in
325/// micrometres.
326#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
327#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
328pub struct Blin {
329    pub baseline_value: u32,
330}
331
332impl AtomExt for Blin {
333    type Ext = FullBoxV0;
334
335    const KIND_EXT: FourCC = FourCC::new(b"blin");
336
337    fn decode_body_ext<B: Buf>(buf: &mut B, _ext: FullBoxV0) -> Result<Self> {
338        Ok(Blin {
339            baseline_value: u32::decode(buf)?,
340        })
341    }
342
343    fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<FullBoxV0> {
344        self.baseline_value.encode(buf)?;
345        Ok(FullBoxV0)
346    }
347}
348
349/// Stereo Comfort. A reader may refuse a `cmfy` that holds anything besides a
350/// single `dadj`, so a writer leaves `must` and `unknown` empty and does not
351/// write an empty `cmfy`.
352#[derive(Debug, Clone, PartialEq, Default)]
353#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
354#[cfg_attr(feature = "serde", serde(default))]
355pub struct Cmfy {
356    pub must: Option<Must>,
357    pub dadj: Option<Dadj>,
358    pub unknown: Vec<Any>,
359}
360
361impl Eq for Cmfy {}
362
363impl Atom for Cmfy {
364    const KIND: FourCC = FourCC::new(b"cmfy");
365
366    spatial_children! { must: Must, dadj: Dadj }
367}
368
369/// Stereo Comfort Disparity Adjustment, in 1/10000 of the width of one eye's
370/// view (-10000 to 10000). Half of it is added to the left eye and half
371/// subtracted from the right.
372#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
373#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
374pub struct Dadj {
375    pub disparity_adjustment: i32,
376}
377
378impl Dadj {
379    pub const DENOMINATOR: i32 = 10_000;
380}
381
382impl AtomExt for Dadj {
383    type Ext = FullBoxV0;
384
385    const KIND_EXT: FourCC = FourCC::new(b"dadj");
386
387    fn decode_body_ext<B: Buf>(buf: &mut B, _ext: FullBoxV0) -> Result<Self> {
388        Ok(Dadj {
389            disparity_adjustment: i32::decode(buf)?,
390        })
391    }
392
393    fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<FullBoxV0> {
394        self.disparity_adjustment.encode(buf)?;
395        Ok(FullBoxV0)
396    }
397}
398
399/// View Packing.
400#[derive(Debug, Clone, PartialEq, Default)]
401#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
402#[cfg_attr(feature = "serde", serde(default))]
403pub struct Pack {
404    pub must: Option<Must>,
405    pub pkin: Option<Pkin>,
406    pub unknown: Vec<Any>,
407}
408
409impl Eq for Pack {}
410
411impl Atom for Pack {
412    const KIND: FourCC = FourCC::new(b"pack");
413
414    spatial_children! { must: Must, pkin: Pkin }
415}
416
417/// View Packing Information. A `view_packing_kind` of zero is a placeholder
418/// for a packing set later.
419#[derive(Debug, Clone, Copy, PartialEq, Eq)]
420#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
421pub struct Pkin {
422    pub view_packing_kind: FourCC,
423}
424
425impl Pkin {
426    pub const SIDE_BY_SIDE: FourCC = FourCC::new(b"side");
427    pub const OVER_UNDER: FourCC = FourCC::new(b"over");
428}
429
430impl AtomExt for Pkin {
431    type Ext = FullBoxV0;
432
433    const KIND_EXT: FourCC = FourCC::new(b"pkin");
434
435    fn decode_body_ext<B: Buf>(buf: &mut B, _ext: FullBoxV0) -> Result<Self> {
436        Ok(Pkin {
437            view_packing_kind: FourCC::decode(buf)?,
438        })
439    }
440
441    fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<FullBoxV0> {
442        self.view_packing_kind.encode(buf)?;
443        Ok(FullBoxV0)
444    }
445}
446
447/// Horizontal Field of View Box, from Apple's [QuickTime and ISO Base Media
448/// File Formats and Spatial and Immersive Media][apple], in units of 1/1000
449/// degree. A sibling of `vexu` in the sample entry.
450///
451/// [apple]: https://developer.apple.com/av-foundation/Stereo-Video-ISOBMFF-Extensions.pdf
452#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
453#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
454pub struct Hfov {
455    pub field_of_view: u32,
456}
457
458impl Hfov {
459    pub const DENOMINATOR: u32 = 1_000;
460}
461
462impl Atom for Hfov {
463    const KIND: FourCC = FourCC::new(b"hfov");
464
465    fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
466        Ok(Hfov {
467            field_of_view: u32::decode(buf)?,
468        })
469    }
470
471    fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
472        self.field_of_view.encode(buf)
473    }
474}