Skip to main content

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

1use crate::*;
2
3/// Photo - JPEG ('jpeg'), QuickTime's Motion JPEG visual sample entry.
4///
5/// Unlike most video codecs, JPEG has no separate decoder configuration box:
6/// the quantization/Huffman tables and everything else needed to decode are
7/// self-contained within each JPEG-coded sample (baseline JPEG per
8/// [ITU-T T.81](https://www.itu.int/rec/T-REC-T.81-199209-I/en) | ISO/IEC 10918-1, the
9/// core JPEG bitstream standard), so this is just a [`Visual`] sample entry
10/// plus the usual optional extension boxes.
11///
12/// `'jpeg'` itself is a QuickTime-registered codec, not part of the ISO
13/// base media file format -- see its
14/// [MP4RA registration](https://mp4ra.org/registered-types/codecs) (spec:
15/// "QT") and Apple's QuickTime documentation on the
16/// [Photo Compressor](https://developer.apple.com/Mac/library/documentation/QuickTime/RM/CompressDecompress/ImageComprMgr/E-Chapter/5CompressorsSupplied.html),
17/// which each sample's JPEG bitstream conforms to.
18#[derive(Debug, Clone, PartialEq, Eq, Default)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20pub struct Jpeg {
21    pub visual: Visual,
22    pub btrt: Option<Btrt>,
23    pub colr: Option<Colr>,
24    pub pasp: Option<Pasp>,
25    pub fiel: Option<Fiel>,
26    pub spatial: Option<Box<Spatial>>,
27}
28
29impl Atom for Jpeg {
30    const KIND: FourCC = FourCC::new(b"jpeg");
31
32    fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
33        let visual = Visual::decode(buf)?;
34
35        let mut btrt = None;
36        let mut colr = None;
37        let mut pasp = None;
38        let mut fiel = None;
39        let mut spatial = None;
40        while let Some(atom) = Any::decode_maybe(buf)? {
41            match atom {
42                Any::Btrt(atom) => btrt = atom.into(),
43                Any::Colr(atom) => colr = atom.into(),
44                Any::Pasp(atom) => pasp = atom.into(),
45                Any::Fiel(atom) => fiel = atom.into(),
46                atom => Spatial::decode_child::<Self>(&mut spatial, atom)?,
47            }
48        }
49        skip_trailing_padding(buf);
50
51        Ok(Self {
52            visual,
53            btrt,
54            colr,
55            pasp,
56            fiel,
57            spatial,
58        })
59    }
60
61    fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
62        self.visual.encode(buf)?;
63        self.btrt.encode(buf)?;
64        self.colr.encode(buf)?;
65        if let Some(spatial) = &self.spatial {
66            spatial.encode(buf)?;
67        }
68        self.pasp.encode(buf)?;
69        self.fiel.encode(buf)?;
70
71        Ok(())
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    #[test]
80    fn test_jpeg_roundtrip() {
81        let expected = Jpeg {
82            visual: Visual {
83                data_reference_index: 1,
84                width: 1920,
85                height: 1080,
86                compressor: "Photo - JPEG".into(),
87                ..Default::default()
88            },
89            btrt: Some(Btrt {
90                buffer_size_db: 0,
91                max_bitrate: 5_000_000,
92                avg_bitrate: 2_000_000,
93            }),
94            colr: None,
95            pasp: Some(Pasp {
96                h_spacing: 1,
97                v_spacing: 1,
98            }),
99            fiel: None,
100            spatial: Default::default(),
101        };
102
103        let mut buf = Vec::new();
104        expected.encode(&mut buf).unwrap();
105
106        let decoded = Jpeg::decode(&mut buf.as_slice()).expect("failed to decode jpeg");
107        assert_eq!(decoded, expected);
108    }
109
110    #[test]
111    fn test_jpeg_minimal() {
112        // No optional boxes at all -- just the bare VisualSampleEntry.
113        let expected = Jpeg {
114            visual: Visual {
115                data_reference_index: 1,
116                width: 640,
117                height: 480,
118                ..Default::default()
119            },
120            ..Default::default()
121        };
122
123        let mut buf = Vec::new();
124        expected.encode(&mut buf).unwrap();
125
126        let decoded = Jpeg::decode(&mut buf.as_slice()).expect("failed to decode jpeg");
127        assert_eq!(decoded, expected);
128    }
129}