scuffle_h264/sps/frame_crop_info.rs
1use std::io;
2
3use scuffle_bytes_util::{BitReader, BitWriter};
4use scuffle_expgolomb::{BitReaderExpGolombExt, BitWriterExpGolombExt, size_of_exp_golomb};
5
6/// `FrameCropInfo` contains the frame cropping info.
7///
8/// This includes `frame_crop_left_offset`, `frame_crop_right_offset`, `frame_crop_top_offset`,
9/// and `frame_crop_bottom_offset`.
10#[derive(Debug, Clone, PartialEq)]
11pub struct FrameCropInfo {
12 /// The `frame_crop_left_offset` is the the left crop offset which is used to compute the width:
13 ///
14 /// `width = ((pic_width_in_mbs_minus1 + 1) * 16) - frame_crop_right_offset * 2 - frame_crop_left_offset * 2`
15 ///
16 /// This is a variable number of bits as it is encoded by an exp golomb (unsigned).
17 /// ISO/IEC-14496-10-2022 - 7.4.2.1.1
18 ///
19 /// For more information:
20 ///
21 /// <https://en.wikipedia.org/wiki/Exponential-Golomb_coding>
22 pub frame_crop_left_offset: u64,
23
24 /// The `frame_crop_right_offset` is the the right crop offset which is used to compute the width:
25 ///
26 /// `width = ((pic_width_in_mbs_minus1 + 1) * 16) - frame_crop_right_offset * 2 - frame_crop_left_offset * 2`
27 ///
28 /// This is a variable number of bits as it is encoded by an exp golomb (unsigned).
29 /// ISO/IEC-14496-10-2022 - 7.4.2.1.1
30 ///
31 /// For more information:
32 ///
33 /// <https://en.wikipedia.org/wiki/Exponential-Golomb_coding>
34 pub frame_crop_right_offset: u64,
35
36 /// The `frame_crop_top_offset` is the the top crop offset which is used to compute the height:
37 ///
38 /// `height = ((2 - frame_mbs_only_flag as u64) * (pic_height_in_map_units_minus1 + 1) * 16)
39 /// - frame_crop_bottom_offset * 2 - frame_crop_top_offset * 2`
40 ///
41 /// This is a variable number of bits as it is encoded by an exp golomb (unsigned).
42 /// ISO/IEC-14496-10-2022 - 7.4.2.1.1
43 ///
44 /// For more information:
45 ///
46 /// <https://en.wikipedia.org/wiki/Exponential-Golomb_coding>
47 pub frame_crop_top_offset: u64,
48
49 /// The `frame_crop_bottom_offset` is the the bottom crop offset which is used to compute the height:
50 ///
51 /// `height = ((2 - frame_mbs_only_flag as u64) * (pic_height_in_map_units_minus1 + 1) * 16)
52 /// - frame_crop_bottom_offset * 2 - frame_crop_top_offset * 2`
53 ///
54 /// This is a variable number of bits as it is encoded by an exp golomb (unsigned).
55 /// ISO/IEC-14496-10-2022 - 7.4.2.1.1
56 ///
57 /// For more information:
58 ///
59 /// <https://en.wikipedia.org/wiki/Exponential-Golomb_coding>
60 pub frame_crop_bottom_offset: u64,
61}
62
63impl FrameCropInfo {
64 /// Parses the fields defined when the `frame_cropping_flag == 1` from a bitstream.
65 /// Returns a `FrameCropInfo` struct.
66 pub fn parse<T: io::Read>(reader: &mut BitReader<T>) -> io::Result<Self> {
67 let frame_crop_left_offset = reader.read_exp_golomb()?;
68 let frame_crop_right_offset = reader.read_exp_golomb()?;
69 let frame_crop_top_offset = reader.read_exp_golomb()?;
70 let frame_crop_bottom_offset = reader.read_exp_golomb()?;
71
72 Ok(FrameCropInfo {
73 frame_crop_left_offset,
74 frame_crop_right_offset,
75 frame_crop_top_offset,
76 frame_crop_bottom_offset,
77 })
78 }
79
80 /// Builds the FrameCropInfo struct into a byte stream.
81 /// Returns a built byte stream.
82 pub fn build<T: io::Write>(&self, writer: &mut BitWriter<T>) -> io::Result<()> {
83 writer.write_exp_golomb(self.frame_crop_left_offset)?;
84 writer.write_exp_golomb(self.frame_crop_right_offset)?;
85 writer.write_exp_golomb(self.frame_crop_top_offset)?;
86 writer.write_exp_golomb(self.frame_crop_bottom_offset)?;
87 Ok(())
88 }
89
90 /// Returns the total bits of the FrameCropInfo struct.
91 ///
92 /// Note that this isn't the bytesize since aligning it may cause some values to be different.
93 pub fn bitsize(&self) -> u64 {
94 size_of_exp_golomb(self.frame_crop_left_offset)
95 + size_of_exp_golomb(self.frame_crop_right_offset)
96 + size_of_exp_golomb(self.frame_crop_top_offset)
97 + size_of_exp_golomb(self.frame_crop_bottom_offset)
98 }
99
100 /// Returns the total bytes of the FrameCropInfo struct.
101 ///
102 /// Note that this calls [`FrameCropInfo::bitsize()`] and calculates the number of bytes
103 /// including any necessary padding such that the bitstream is byte aligned.
104 pub fn bytesize(&self) -> u64 {
105 self.bitsize().div_ceil(8)
106 }
107}
108
109#[cfg(test)]
110#[cfg_attr(all(test, coverage_nightly), coverage(off))]
111mod tests {
112 use scuffle_bytes_util::{BitReader, BitWriter};
113 use scuffle_expgolomb::BitWriterExpGolombExt;
114
115 use crate::sps::FrameCropInfo;
116
117 #[test]
118 fn test_build_size_frame_crop() {
119 // create bitstream for frame_crop
120 let mut data = Vec::new();
121 let mut writer = BitWriter::new(&mut data);
122
123 writer.write_exp_golomb(1).unwrap();
124 writer.write_exp_golomb(10).unwrap();
125 writer.write_exp_golomb(7).unwrap();
126 writer.write_exp_golomb(38).unwrap();
127
128 writer.finish().unwrap();
129
130 // parse bitstream
131 let mut reader = BitReader::new_from_slice(&mut data);
132 let frame_crop_info = FrameCropInfo::parse(&mut reader).unwrap();
133
134 // create a writer for the builder
135 let mut buf = Vec::new();
136 let mut writer2 = BitWriter::new(&mut buf);
137
138 // build from the example result
139 frame_crop_info.build(&mut writer2).unwrap();
140 writer2.finish().unwrap();
141
142 assert_eq!(buf, data);
143
144 // now we re-parse so we can compare the bit sizes.
145 // create a reader for the parser
146 let mut reader2 = BitReader::new_from_slice(buf);
147 let rebuilt_frame_crop_info = FrameCropInfo::parse(&mut reader2).unwrap();
148
149 // now we can check the size:
150 assert_eq!(rebuilt_frame_crop_info.bitsize(), frame_crop_info.bitsize());
151 assert_eq!(rebuilt_frame_crop_info.bytesize(), frame_crop_info.bytesize());
152 }
153}