scuffle_h264/sps/
sample_aspect_ratio.rs

1use std::io;
2
3use byteorder::ReadBytesExt;
4use scuffle_bytes_util::{BitReader, BitWriter};
5
6use crate::AspectRatioIdc;
7
8/// `SarDimensions` contains the fields that are set when `aspect_ratio_info_present_flag == 1`,
9/// and `aspect_ratio_idc == 255`.
10///
11/// This contains the following fields: `sar_width` and `sar_height`.
12#[derive(Debug, Clone, PartialEq)]
13pub struct SarDimensions {
14    /// The `aspect_ratio_idc` is the sample aspect ratio of the luma samples as a u8.
15    ///
16    /// This is a full byte, and defaults to 0.
17    ///
18    /// Refer to the `AspectRatioIdc` nutype enum for more info.
19    ///
20    /// ISO/IEC-14496-10-2022 - E.2.1 Table E-1
21    pub aspect_ratio_idc: AspectRatioIdc,
22
23    /// The `sar_width` is the horizontal size of the aspect ratio as a u16.
24    ///
25    /// This is a full 2 bytes.
26    ///
27    /// The value is supposed to be "relatively prime or equal to 0". If set to 0,
28    /// the sample aspect ratio is considered to be unspecified by ISO/IEC-14496-10-2022.
29    ///
30    /// ISO/IEC-14496-10-2022 - E.2.1
31    pub sar_width: u16,
32
33    /// The `sar_height` is the vertical size of the aspect ratio as a u16.
34    ///
35    /// This is a full 2 bytes.
36    ///
37    /// The value is supposed to be "relatively prime or equal to 0". If set to 0,
38    /// the sample aspect ratio is considered to be unspecified by ISO/IEC-14496-10-2022.
39    ///
40    /// ISO/IEC-14496-10-2022 - E.2.1
41    pub sar_height: u16,
42}
43
44impl SarDimensions {
45    /// Parses the fields defined when the `aspect_ratio_info_present_flag == 1` from a bitstream.
46    /// Returns a `SarDimensions` struct.
47    pub fn parse<T: io::Read>(reader: &mut BitReader<T>) -> io::Result<Self> {
48        let mut sar_width = 0; // defaults to 0, E.2.1
49        let mut sar_height = 0; // deafults to 0, E.2.1
50
51        let aspect_ratio_idc = reader.read_u8()?;
52        if aspect_ratio_idc == 255 {
53            sar_width = reader.read_bits(16)? as u16;
54            sar_height = reader.read_bits(16)? as u16;
55        }
56
57        Ok(SarDimensions {
58            aspect_ratio_idc: AspectRatioIdc(aspect_ratio_idc),
59            sar_width,
60            sar_height,
61        })
62    }
63
64    /// Builds the SarDimensions struct into a byte stream.
65    /// Returns a built byte stream.
66    pub fn build<T: io::Write>(&self, writer: &mut BitWriter<T>) -> io::Result<()> {
67        writer.write_bits(self.aspect_ratio_idc.0 as u64, 8)?;
68
69        if self.aspect_ratio_idc == AspectRatioIdc(255) {
70            writer.write_bits(self.sar_width as u64, 16)?;
71            writer.write_bits(self.sar_height as u64, 16)?;
72        }
73        Ok(())
74    }
75
76    /// Returns the total bits of the SarDimensions struct.
77    pub fn bitsize(&self) -> u64 {
78        8 + // aspect_ratio_idc
79        ((self.aspect_ratio_idc == AspectRatioIdc(255)) as u64) * 32
80    }
81
82    /// Returns the total bytes of the SarDimensions struct.
83    ///
84    /// Note that this calls [`SarDimensions::bitsize()`] and calculates the number of bytes.
85    pub fn bytesize(&self) -> u64 {
86        self.bitsize().div_ceil(8)
87    }
88}
89
90#[cfg(test)]
91#[cfg_attr(all(test, coverage_nightly), coverage(off))]
92mod tests {
93    use scuffle_bytes_util::{BitReader, BitWriter};
94
95    use crate::sps::SarDimensions;
96
97    #[test]
98    fn test_build_size_sar_idc_not_255() {
99        // create bitstream for sample_aspect_ratio
100        let mut data = Vec::new();
101        let mut writer = BitWriter::new(&mut data);
102
103        writer.write_bits(1, 8).unwrap();
104        writer.finish().unwrap();
105
106        // parse bitstream
107        let mut reader = BitReader::new_from_slice(&mut data);
108        let sample_aspect_ratio = SarDimensions::parse(&mut reader).unwrap();
109
110        // create a writer for the builder
111        let mut buf = Vec::new();
112        let mut writer2 = BitWriter::new(&mut buf);
113
114        // build from the example result
115        sample_aspect_ratio.build(&mut writer2).unwrap();
116        writer2.finish().unwrap();
117
118        assert_eq!(buf, data);
119
120        // now we re-parse so we can compare the bit sizes.
121        // create a reader for the parser
122        let mut reader2 = BitReader::new_from_slice(buf);
123        let rebuilt_sample_aspect_ratio = SarDimensions::parse(&mut reader2).unwrap();
124
125        // now we can check the size:
126        assert_eq!(rebuilt_sample_aspect_ratio.bitsize(), sample_aspect_ratio.bitsize());
127        assert_eq!(rebuilt_sample_aspect_ratio.bytesize(), sample_aspect_ratio.bytesize());
128    }
129
130    #[test]
131    fn test_build_size_sar_idc_255() {
132        // create bitstream for sample_aspect_ratio
133        let mut data = Vec::new();
134        let mut writer = BitWriter::new(&mut data);
135
136        writer.write_bits(255, 8).unwrap();
137        writer.write_bits(11, 16).unwrap();
138        writer.write_bits(32, 16).unwrap();
139        writer.finish().unwrap();
140
141        // parse bitstream
142        let mut reader = BitReader::new_from_slice(&mut data);
143        let sample_aspect_ratio = SarDimensions::parse(&mut reader).unwrap();
144
145        // create a writer for the builder
146        let mut buf = Vec::new();
147        let mut writer2 = BitWriter::new(&mut buf);
148
149        // build from the example result
150        sample_aspect_ratio.build(&mut writer2).unwrap();
151        writer2.finish().unwrap();
152
153        assert_eq!(buf, data);
154
155        // now we re-parse so we can compare the bit sizes.
156        // create a reader for the parser
157        let mut reader2 = BitReader::new_from_slice(buf);
158        let rebuilt_sample_aspect_ratio = SarDimensions::parse(&mut reader2).unwrap();
159
160        // now we can check the size:
161        assert_eq!(rebuilt_sample_aspect_ratio.bitsize(), sample_aspect_ratio.bitsize());
162        assert_eq!(rebuilt_sample_aspect_ratio.bytesize(), sample_aspect_ratio.bytesize());
163    }
164}