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}