scuffle_h264/sps/
chroma_sample_loc.rs

1use std::io;
2
3use scuffle_bytes_util::{BitReader, BitWriter, range_check};
4use scuffle_expgolomb::{BitReaderExpGolombExt, BitWriterExpGolombExt, size_of_exp_golomb};
5
6/// `ChromaSampleLoc` contains the fields that are set when `chroma_loc_info_present_flag == 1`,
7///
8/// This contains the following fields: `chroma_sample_loc_type_top_field` and `chroma_sample_loc_type_bottom_field`.
9#[derive(Debug, Clone, PartialEq)]
10pub struct ChromaSampleLoc {
11    /// The `chroma_sample_loc_type_top_field` specifies the location of chroma samples.
12    ///
13    /// The value of this ranges from \[0, 5\]. By default, this value is set to 0.
14    ///
15    /// See ISO/IEC-14496-10-2022 - E.2.1 Figure E-1 for more info.
16    ///
17    /// This is a variable number of bits as it is encoded by an exp golomb (unsigned).
18    /// The smallest encoding would be for `0` which is encoded as `1`, which is a single bit.
19    /// The largest encoding would be for `5` which is encoded as `0 0110`, which is 5 bits.
20    /// ISO/IEC-14496-10-2022 - E.2.1
21    ///
22    /// For more information:
23    ///
24    /// <https://en.wikipedia.org/wiki/Exponential-Golomb_coding>
25    pub chroma_sample_loc_type_top_field: u8,
26
27    /// The `chroma_sample_loc_type_bottom_field`
28    ///
29    /// The value of this ranges from \[0, 5\]. By default, this value is set to 0.
30    ///
31    /// See ISO/IEC-14496-10-2022 - E.2.1 Figure E-1 for more info.
32    ///
33    /// This is a variable number of bits as it is encoded by an exp golomb (unsigned).
34    /// The smallest encoding would be for `0` which is encoded as `1`, which is a single bit.
35    /// The largest encoding would be for `5` which is encoded as `0 0110`, which is 5 bits.
36    /// ISO/IEC-14496-10-2022 - E.2.1
37    ///
38    /// For more information:
39    ///
40    /// <https://en.wikipedia.org/wiki/Exponential-Golomb_coding>
41    pub chroma_sample_loc_type_bottom_field: u8,
42}
43
44impl ChromaSampleLoc {
45    /// Parses the fields defined when the `chroma_loc_info_present_flag == 1` from a bitstream.
46    /// Returns a `ChromaSampleLoc` struct.
47    pub fn parse<T: io::Read>(reader: &mut BitReader<T>) -> io::Result<Self> {
48        let chroma_sample_loc_type_top_field = reader.read_exp_golomb()?;
49        range_check!(chroma_sample_loc_type_top_field, 0, 5)?;
50        let chroma_sample_loc_type_top_field = chroma_sample_loc_type_top_field as u8;
51
52        let chroma_sample_loc_type_bottom_field = reader.read_exp_golomb()?;
53        range_check!(chroma_sample_loc_type_bottom_field, 0, 5)?;
54        let chroma_sample_loc_type_bottom_field = chroma_sample_loc_type_bottom_field as u8;
55
56        Ok(ChromaSampleLoc {
57            chroma_sample_loc_type_top_field,
58            chroma_sample_loc_type_bottom_field,
59        })
60    }
61
62    /// Builds the ChromaSampleLoc struct into a byte stream.
63    /// Returns a built byte stream.
64    pub fn build<T: io::Write>(&self, writer: &mut BitWriter<T>) -> io::Result<()> {
65        writer.write_exp_golomb(self.chroma_sample_loc_type_top_field as u64)?;
66        writer.write_exp_golomb(self.chroma_sample_loc_type_bottom_field as u64)?;
67        Ok(())
68    }
69
70    /// Returns the total bits of the ChromaSampleLoc struct.
71    ///
72    /// Note that this isn't the bytesize since aligning it may cause some values to be different.
73    pub fn bitsize(&self) -> u64 {
74        size_of_exp_golomb(self.chroma_sample_loc_type_top_field as u64)
75            + size_of_exp_golomb(self.chroma_sample_loc_type_bottom_field as u64)
76    }
77
78    /// Returns the total bytes of the ChromaSampleLoc struct.
79    ///
80    /// Note that this calls [`ChromaSampleLoc::bitsize()`] and calculates the number of bytes
81    /// including any necessary padding such that the bitstream is byte aligned.
82    pub fn bytesize(&self) -> u64 {
83        self.bitsize().div_ceil(8)
84    }
85}
86
87#[cfg(test)]
88#[cfg_attr(all(test, coverage_nightly), coverage(off))]
89mod tests {
90    use scuffle_bytes_util::{BitReader, BitWriter};
91    use scuffle_expgolomb::BitWriterExpGolombExt;
92
93    use crate::sps::ChromaSampleLoc;
94
95    #[test]
96    fn test_build_size_chroma_sample() {
97        // create bitstream for chroma_sample_loc
98        let mut data = Vec::new();
99        let mut writer = BitWriter::new(&mut data);
100
101        writer.write_exp_golomb(1).unwrap();
102        writer.write_exp_golomb(5).unwrap();
103        writer.finish().unwrap();
104
105        // parse bitstream
106        let mut reader = BitReader::new_from_slice(&mut data);
107        let chroma_sample_loc = ChromaSampleLoc::parse(&mut reader).unwrap();
108
109        // create a writer for the builder
110        let mut buf = Vec::new();
111        let mut writer2 = BitWriter::new(&mut buf);
112
113        // build from the example result
114        chroma_sample_loc.build(&mut writer2).unwrap();
115        writer2.finish().unwrap();
116
117        assert_eq!(buf, data);
118
119        // now we re-parse so we can compare the bit sizes.
120        // create a reader for the parser
121        let mut reader2 = BitReader::new_from_slice(buf);
122        let rebuilt_chroma_sample_loc = ChromaSampleLoc::parse(&mut reader2).unwrap();
123
124        // now we can check the size:
125        assert_eq!(rebuilt_chroma_sample_loc.bitsize(), chroma_sample_loc.bitsize());
126        assert_eq!(rebuilt_chroma_sample_loc.bytesize(), chroma_sample_loc.bytesize());
127    }
128}