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}