1#[cfg(feature = "backtrace")]
11use std::backtrace::Backtrace;
12
13use super::nal_unit::*;
14use super::*;
15
16pub struct LessEncoder {
22 width: u32,
23 height: u32,
24 mbs_width: usize,
25 mbs_height: usize,
26 sps: Sps,
27 pps: Pps,
28}
29
30impl LessEncoder {
31 pub fn new(y4m_frame: &YCbCrImage) -> Result<(InitialNalUnits, Self)> {
36 let width = y4m_frame.width;
37 let height = y4m_frame.height;
38
39 let bit_depth = y4m_frame.luma_bit_depth();
40
41 match (&y4m_frame.planes, &bit_depth, width % 4 == 0) {
42 (Planes::YCbCr(_), BitDepth::Depth12, false) => {
43 return Err(Error::DataShapeProblem {
44 msg: "for bit depth 12 color, width must be divisible by 4",
45 #[cfg(feature = "backtrace")]
46 backtrace: Backtrace::capture(),
47 });
48 }
49 _ => {}
50 }
51
52 match (&y4m_frame.planes, &bit_depth, width % 2 == 0) {
53 (Planes::Mono(_), BitDepth::Depth8, false) | (_, _, true) => {}
54 (_, _, false) => {
55 return Err(Error::DataShapeProblem {
56 msg: "width must be divisible by 2 (except mono8)",
57 #[cfg(feature = "backtrace")]
58 backtrace: Backtrace::capture(),
59 });
60 }
61 }
62
63 #[allow(non_snake_case)]
64 let (profile_idc, SubWidthC, SubHeightC) = match (&y4m_frame.planes, bit_depth) {
65 (Planes::YCbCr(_), BitDepth::Depth8) => (ProfileIdc::baseline(), 2, 2),
66 (Planes::Mono(_), BitDepth::Depth8) => (
67 ProfileIdc::high(ChromaFormatIdc::Monochrome(bit_depth)),
68 1,
69 1,
70 ),
71 (Planes::Mono(_), BitDepth::Depth12) => (
72 ProfileIdc::high444pp(ChromaFormatIdc::Monochrome(bit_depth)),
73 1,
74 1,
75 ),
76 (Planes::YCbCr(_), BitDepth::Depth12) => (
77 ProfileIdc::high444pp(ChromaFormatIdc::Chroma420(bit_depth)),
78 2,
79 2,
80 ),
81 };
82
83 let pic_width_in_mbs_minus1 = div_ceil(width, 16) - 1;
84 let pic_height_in_map_units_minus1 = div_ceil(height, 16) - 1;
85
86 let frame_cropping = if ((pic_width_in_mbs_minus1 + 1) * 16 != width)
87 || ((pic_height_in_map_units_minus1 + 1) * 16 != height)
88 {
89 let padded_width = (pic_width_in_mbs_minus1 + 1) * 16;
91 let padded_height = (pic_height_in_map_units_minus1 + 1) * 16;
92
93 let lr_pad = padded_width - width;
94 let tb_pad = padded_height - height;
95
96 let lpad = 0;
97 let tpad = 0;
98 let rpad = lr_pad / SubWidthC;
99 let bpad = tb_pad / SubHeightC;
100
101 if (lpad * SubWidthC + width + rpad * SubWidthC != padded_width)
102 || (tpad * SubHeightC + bpad * SubHeightC + height != padded_height)
103 {
104 return Err(crate::Error::UnsupportedImageSize {
105 #[cfg(feature = "backtrace")]
106 backtrace: Backtrace::capture(),
107 });
108 }
109
110 Some([lpad, rpad, tpad, bpad])
111 } else {
112 None
113 };
114
115 let sps = Sps::new(
117 profile_idc,
118 pic_width_in_mbs_minus1,
119 pic_height_in_map_units_minus1,
120 frame_cropping,
121 Some(Vui::new(true)),
122 );
123 let sps_nal_unit = NalUnit::new(
124 NalRefIdc::Three,
125 NalUnitType::SequenceParameterSet,
126 sps.to_rbsp(),
127 );
128
129 let pps = Pps::new(0);
131 let pps_nal_unit = NalUnit::new(
132 NalRefIdc::Three,
133 NalUnitType::PictureParameterSet,
134 pps.to_rbsp(),
135 );
136
137 let mbs_width = (pic_width_in_mbs_minus1 + 1).try_into().unwrap();
138 let mbs_height = (pic_height_in_map_units_minus1 + 1).try_into().unwrap();
139
140 let mut self_ = Self {
141 width,
142 height,
143 mbs_width,
144 mbs_height,
145 sps,
146 pps,
147 };
148
149 let frame_nal_unit = self_.encode(y4m_frame)?;
150 let nal_units = InitialNalUnits {
151 sps: sps_nal_unit,
152 pps: pps_nal_unit,
153 frame: frame_nal_unit,
154 };
155 Ok((nal_units, self_))
156 }
157
158 pub fn encode(&mut self, y4m_frame: &YCbCrImage) -> Result<NalUnit> {
160 y4m_frame.check_sizes()?;
161
162 debug_assert_eq!(self.width, y4m_frame.width);
163 debug_assert_eq!(self.height, y4m_frame.height);
164
165 let mut slice_data = SliceHeader::new().to_rbsp(&self.sps, &self.pps);
166
167 let luma_only = self.sps.profile_idc.is_monochrome();
168
169 let num_macroblocks = self.mbs_height * self.mbs_width;
170
171 let row_sz = match y4m_frame.luma_bit_depth() {
173 BitDepth::Depth8 => 16,
174 BitDepth::Depth12 => 24,
175 };
176 let orig_len = slice_data.data.len();
177
178 let mut reserve_size = if luma_only {
180 num_macroblocks * row_sz * 16
182 } else {
183 num_macroblocks * row_sz * 16 * 3 / 2
185 };
186
187 reserve_size +=
189 (num_macroblocks - 1) * MacroblockType::I_PCM.as_encoded_macroblock_header().len() + 1;
190
191 slice_data.data.reserve(reserve_size);
192
193 for mbs_row in 0..self.mbs_height {
194 for mbs_col in 0..self.mbs_width {
195 macroblock(mbs_row, mbs_col, &mut slice_data, y4m_frame, luma_only);
197 }
198 }
199
200 slice_data.data.push(0x80); let final_len = slice_data.data.len();
203 let should_have_reserved = final_len - orig_len;
204
205 debug_assert_eq!(should_have_reserved, reserve_size);
206
207 Ok(NalUnit::new(
208 NalRefIdc::One,
209 NalUnitType::CodedSliceOfAnIDRPicture,
210 slice_data,
211 ))
212 }
213}