1use std::io::Write;
5
6use thiserror::Error;
7
8use crate::codec::h264::nalu_writer::NaluWriter;
9use crate::codec::h264::nalu_writer::NaluWriterError;
10use crate::codec::h264::parser::HrdParams;
11use crate::codec::h264::parser::NaluType;
12use crate::codec::h264::parser::Pps;
13use crate::codec::h264::parser::Sps;
14use crate::codec::h264::parser::DEFAULT_4X4_INTER;
15use crate::codec::h264::parser::DEFAULT_4X4_INTRA;
16use crate::codec::h264::parser::DEFAULT_8X8_INTER;
17use crate::codec::h264::parser::DEFAULT_8X8_INTRA;
18
19mod private {
20 pub trait NaluStruct {}
21}
22
23impl private::NaluStruct for Sps {}
24
25impl private::NaluStruct for Pps {}
26
27#[derive(Error, Debug)]
28pub enum SynthesizerError {
29 #[error("tried to synthesize unsupported settings")]
30 Unsupported,
31 #[error(transparent)]
32 NaluWriter(#[from] NaluWriterError),
33}
34
35pub type SynthesizerResult<T> = Result<T, SynthesizerError>;
36
37pub struct Synthesizer<'n, N: private::NaluStruct, W: Write> {
39 writer: NaluWriter<W>,
40 nalu: &'n N,
41}
42
43const EXTENDED_SAR: u8 = 255;
45
46impl<N: private::NaluStruct, W: Write> Synthesizer<'_, N, W> {
47 fn u<T: Into<u32>>(&mut self, bits: usize, value: T) -> SynthesizerResult<()> {
48 self.writer.write_u(bits, value)?;
49 Ok(())
50 }
51
52 fn f<T: Into<u32>>(&mut self, bits: usize, value: T) -> SynthesizerResult<()> {
53 self.writer.write_f(bits, value)?;
54 Ok(())
55 }
56
57 fn ue<T: Into<u32>>(&mut self, value: T) -> SynthesizerResult<()> {
58 self.writer.write_ue(value)?;
59 Ok(())
60 }
61
62 fn se<T: Into<i32>>(&mut self, value: T) -> SynthesizerResult<()> {
63 self.writer.write_se(value)?;
64 Ok(())
65 }
66
67 fn scaling_list(&mut self, list: &[u8], default: &[u8]) -> SynthesizerResult<()> {
68 if list == default {
70 self.se(-8)?;
71 return Ok(());
72 }
73
74 let mut run = list.len();
76
77 for j in (1..list.len()).rev() {
80 if list[j - 1] != list[j] {
81 break;
82 }
83 run -= 1;
84 }
85
86 let mut last_scale = 8;
88 for scale in &list[0..run] {
89 let delta_scale = *scale as i32 - last_scale;
90 self.se(delta_scale)?;
91 last_scale = *scale as i32;
92 }
93
94 if run < list.len() {
98 self.se(-last_scale)?;
99 }
100
101 Ok(())
102 }
103
104 fn default_scaling_list(i: usize) -> &'static [u8] {
105 match i {
107 0 => &DEFAULT_4X4_INTRA[..],
108 1 => &DEFAULT_4X4_INTRA[..],
109 2 => &DEFAULT_4X4_INTRA[..],
110 3 => &DEFAULT_4X4_INTER[..],
111 4 => &DEFAULT_4X4_INTER[..],
112 5 => &DEFAULT_4X4_INTER[..],
113 6 => &DEFAULT_8X8_INTRA[..],
114 7 => &DEFAULT_8X8_INTER[..],
115 8 => &DEFAULT_8X8_INTRA[..],
116 9 => &DEFAULT_8X8_INTER[..],
117 10 => &DEFAULT_8X8_INTRA[..],
118 11 => &DEFAULT_8X8_INTER[..],
119 _ => unreachable!(),
120 }
121 }
122
123 fn rbsp_trailing_bits(&mut self) -> SynthesizerResult<()> {
124 self.f(1, 1u32)?;
125
126 while !self.writer.aligned() {
127 self.f(1, 0u32)?;
128 }
129
130 Ok(())
131 }
132}
133
134impl<'n, W: Write> Synthesizer<'n, Sps, W> {
135 pub fn synthesize(
136 ref_idc: u8,
137 sps: &'n Sps,
138 writer: W,
139 ep_enabled: bool,
140 ) -> SynthesizerResult<()> {
141 let mut s = Self {
142 writer: NaluWriter::<W>::new(writer, ep_enabled),
143 nalu: sps,
144 };
145
146 s.writer.write_header(ref_idc, NaluType::Sps as u8)?;
147 s.seq_parameter_set_data()?;
148 s.rbsp_trailing_bits()
149 }
150
151 fn hrd_parameters(&mut self, hrd_params: &HrdParams) -> SynthesizerResult<()> {
152 self.ue(hrd_params.cpb_cnt_minus1)?;
153 self.u(4, hrd_params.bit_rate_scale)?;
154 self.u(4, hrd_params.cpb_size_scale)?;
155
156 for i in 0..=(hrd_params.cpb_cnt_minus1 as usize) {
157 self.ue(hrd_params.bit_rate_value_minus1[i])?;
158 self.ue(hrd_params.cpb_size_value_minus1[i])?;
159 self.u(1, hrd_params.cbr_flag[i])?;
160 }
161
162 self.u(5, hrd_params.initial_cpb_removal_delay_length_minus1)?;
163 self.u(5, hrd_params.cpb_removal_delay_length_minus1)?;
164 self.u(5, hrd_params.dpb_output_delay_length_minus1)?;
165 self.u(5, hrd_params.time_offset_length)?;
166
167 Ok(())
168 }
169
170 fn vui_parameters(&mut self) -> SynthesizerResult<()> {
171 let vui_params = &self.nalu.vui_parameters;
173
174 self.u(1, vui_params.aspect_ratio_info_present_flag)?;
175 if vui_params.aspect_ratio_info_present_flag {
176 self.u(8, vui_params.aspect_ratio_idc)?;
177 if vui_params.aspect_ratio_idc == EXTENDED_SAR {
178 self.u(16, vui_params.sar_width)?;
179 self.u(16, vui_params.sar_height)?;
180 }
181 }
182
183 self.u(1, vui_params.overscan_info_present_flag)?;
184 if vui_params.overscan_info_present_flag {
185 self.u(1, vui_params.overscan_appropriate_flag)?;
186 }
187
188 self.u(1, vui_params.video_signal_type_present_flag)?;
189 if vui_params.video_signal_type_present_flag {
190 self.u(3, vui_params.video_format)?;
191 self.u(1, vui_params.video_full_range_flag)?;
192
193 self.u(1, vui_params.colour_description_present_flag)?;
194 if vui_params.colour_description_present_flag {
195 self.u(8, vui_params.colour_primaries)?;
196 self.u(8, vui_params.transfer_characteristics)?;
197 self.u(8, vui_params.matrix_coefficients)?;
198 }
199 }
200
201 self.u(1, vui_params.chroma_loc_info_present_flag)?;
202 if vui_params.chroma_loc_info_present_flag {
203 self.ue(vui_params.chroma_sample_loc_type_top_field)?;
204 self.ue(self.nalu.vui_parameters.chroma_sample_loc_type_bottom_field)?;
205 }
206
207 self.u(1, vui_params.timing_info_present_flag)?;
208 if vui_params.timing_info_present_flag {
209 self.u(32, vui_params.num_units_in_tick)?;
210 self.u(32, vui_params.time_scale)?;
211 self.u(1, vui_params.fixed_frame_rate_flag)?;
212 }
213
214 self.u(1, vui_params.nal_hrd_parameters_present_flag)?;
215 if vui_params.nal_hrd_parameters_present_flag {
216 self.hrd_parameters(&vui_params.nal_hrd_parameters)?;
217 }
218 self.u(1, vui_params.vcl_hrd_parameters_present_flag)?;
219 if vui_params.vcl_hrd_parameters_present_flag {
220 self.hrd_parameters(&vui_params.vcl_hrd_parameters)?;
221 }
222
223 if vui_params.nal_hrd_parameters_present_flag || vui_params.vcl_hrd_parameters_present_flag
224 {
225 self.u(1, vui_params.low_delay_hrd_flag)?;
226 }
227
228 self.u(1, vui_params.pic_struct_present_flag)?;
229
230 self.u(1, vui_params.bitstream_restriction_flag)?;
231 if vui_params.bitstream_restriction_flag {
232 self.u(1, vui_params.motion_vectors_over_pic_boundaries_flag)?;
233 self.ue(vui_params.max_bytes_per_pic_denom)?;
234 self.ue(vui_params.max_bits_per_mb_denom)?;
235 self.ue(vui_params.log2_max_mv_length_horizontal)?;
236 self.ue(vui_params.log2_max_mv_length_vertical)?;
237 self.ue(vui_params.max_num_reorder_frames)?;
238 self.ue(vui_params.max_dec_frame_buffering)?;
239 }
240
241 Ok(())
242 }
243
244 fn seq_parameter_set_data(&mut self) -> SynthesizerResult<()> {
245 self.u(8, self.nalu.profile_idc)?;
247 self.u(1, self.nalu.constraint_set0_flag)?;
248 self.u(1, self.nalu.constraint_set1_flag)?;
249 self.u(1, self.nalu.constraint_set2_flag)?;
250 self.u(1, self.nalu.constraint_set3_flag)?;
251 self.u(1, self.nalu.constraint_set4_flag)?;
252 self.u(1, self.nalu.constraint_set5_flag)?;
253 self.u(2, 0u32)?;
254 self.u(8, self.nalu.level_idc as u32)?;
255 self.ue(self.nalu.seq_parameter_set_id)?;
256
257 if self.nalu.profile_idc == 100
258 || self.nalu.profile_idc == 110
259 || self.nalu.profile_idc == 122
260 || self.nalu.profile_idc == 244
261 || self.nalu.profile_idc == 44
262 || self.nalu.profile_idc == 83
263 || self.nalu.profile_idc == 86
264 || self.nalu.profile_idc == 118
265 || self.nalu.profile_idc == 128
266 || self.nalu.profile_idc == 138
267 || self.nalu.profile_idc == 139
268 || self.nalu.profile_idc == 134
269 || self.nalu.profile_idc == 135
270 {
271 self.ue(self.nalu.chroma_format_idc)?;
272
273 if self.nalu.chroma_format_idc == 3 {
274 self.u(1, self.nalu.separate_colour_plane_flag)?;
275 }
276
277 self.ue(self.nalu.bit_depth_luma_minus8)?;
278 self.ue(self.nalu.bit_depth_chroma_minus8)?;
279 self.u(1, self.nalu.qpprime_y_zero_transform_bypass_flag)?;
280 self.u(1, self.nalu.seq_scaling_matrix_present_flag)?;
281
282 if self.nalu.seq_scaling_matrix_present_flag {
283 let scaling_list_count = if self.nalu.chroma_format_idc != 3 {
284 8
285 } else {
286 12
287 };
288
289 for i in 0..scaling_list_count {
290 if i < 6 {
292 if self.nalu.scaling_lists_4x4[i] == [0; 16] {
293 self.u(1, false)?;
294 } else {
295 self.u(1, true)?;
296 self.scaling_list(
297 &self.nalu.scaling_lists_4x4[i],
298 Self::default_scaling_list(i),
299 )?;
300 }
301 } else if self.nalu.scaling_lists_8x8[i - 6] == [0; 64] {
302 self.u(1, false)?;
303 } else {
304 self.u(1, true)?;
305 self.scaling_list(
306 &self.nalu.scaling_lists_8x8[i - 6],
307 Self::default_scaling_list(i),
308 )?;
309 }
310 }
311 }
312 }
313
314 self.ue(self.nalu.log2_max_frame_num_minus4)?;
315 self.ue(self.nalu.pic_order_cnt_type)?;
316
317 if self.nalu.pic_order_cnt_type == 0 {
318 self.ue(self.nalu.log2_max_pic_order_cnt_lsb_minus4)?;
319 } else if self.nalu.pic_order_cnt_type == 1 {
320 self.u(1, self.nalu.delta_pic_order_always_zero_flag)?;
321 self.se(self.nalu.offset_for_non_ref_pic)?;
322 self.se(self.nalu.offset_for_top_to_bottom_field)?;
323 self.ue(self.nalu.num_ref_frames_in_pic_order_cnt_cycle)?;
324
325 for offset_for_ref_frame in &self.nalu.offset_for_ref_frame {
326 self.se(*offset_for_ref_frame)?;
327 }
328 }
329
330 self.ue(self.nalu.max_num_ref_frames)?;
331 self.u(1, self.nalu.gaps_in_frame_num_value_allowed_flag)?;
332 self.ue(self.nalu.pic_width_in_mbs_minus1)?;
333 self.ue(self.nalu.pic_height_in_map_units_minus1)?;
334 self.u(1, self.nalu.frame_mbs_only_flag)?;
335 if !self.nalu.frame_mbs_only_flag {
336 self.u(1, self.nalu.mb_adaptive_frame_field_flag)?;
337 }
338 self.u(1, self.nalu.direct_8x8_inference_flag)?;
339
340 self.u(1, self.nalu.frame_cropping_flag)?;
341 if self.nalu.frame_cropping_flag {
342 self.ue(self.nalu.frame_crop_left_offset)?;
343 self.ue(self.nalu.frame_crop_right_offset)?;
344 self.ue(self.nalu.frame_crop_top_offset)?;
345 self.ue(self.nalu.frame_crop_bottom_offset)?;
346 }
347
348 self.u(1, self.nalu.vui_parameters_present_flag)?;
349 if self.nalu.vui_parameters_present_flag {
350 self.vui_parameters()?;
351 }
352
353 Ok(())
354 }
355}
356
357impl<'n, W: Write> Synthesizer<'n, Pps, W> {
358 pub fn synthesize(
359 ref_idc: u8,
360 pps: &'n Pps,
361 writer: W,
362 ep_enabled: bool,
363 ) -> SynthesizerResult<()> {
364 let mut s = Self {
365 writer: NaluWriter::<W>::new(writer, ep_enabled),
366 nalu: pps,
367 };
368
369 s.writer.write_header(ref_idc, NaluType::Pps as u8)?;
370 s.pic_parameter_set_rbsp()?;
371 s.rbsp_trailing_bits()
372 }
373
374 fn pic_parameter_set_rbsp(&mut self) -> SynthesizerResult<()> {
375 self.ue(self.nalu.pic_parameter_set_id)?;
376 self.ue(self.nalu.seq_parameter_set_id)?;
377 self.u(1, self.nalu.entropy_coding_mode_flag)?;
378 self.u(1, self.nalu.bottom_field_pic_order_in_frame_present_flag)?;
379
380 self.ue(self.nalu.num_slice_groups_minus1)?;
381 if self.nalu.num_slice_groups_minus1 > 0 {
382 return Err(SynthesizerError::Unsupported);
383 }
384
385 self.ue(self.nalu.num_ref_idx_l0_default_active_minus1)?;
386 self.ue(self.nalu.num_ref_idx_l1_default_active_minus1)?;
387 self.u(1, self.nalu.weighted_pred_flag)?;
388 self.u(2, self.nalu.weighted_bipred_idc)?;
389 self.se(self.nalu.pic_init_qp_minus26)?;
390 self.se(self.nalu.pic_init_qs_minus26)?;
391 self.se(self.nalu.chroma_qp_index_offset)?;
392 self.u(1, self.nalu.deblocking_filter_control_present_flag)?;
393 self.u(1, self.nalu.constrained_intra_pred_flag)?;
394 self.u(1, self.nalu.redundant_pic_cnt_present_flag)?;
395
396 if !(self.nalu.transform_8x8_mode_flag
397 || self.nalu.pic_scaling_matrix_present_flag
398 || self.nalu.second_chroma_qp_index_offset != 0)
399 {
400 return Ok(());
401 }
402
403 self.u(1, self.nalu.transform_8x8_mode_flag)?;
404 self.u(1, self.nalu.pic_scaling_matrix_present_flag)?;
405
406 if self.nalu.pic_scaling_matrix_present_flag {
407 let mut scaling_list_count = 6;
408 if self.nalu.transform_8x8_mode_flag {
409 if self.nalu.sps.chroma_format_idc != 3 {
410 scaling_list_count += 2;
411 } else {
412 scaling_list_count += 6;
413 }
414 }
415
416 for i in 0..scaling_list_count {
417 if i < 6 {
419 if self.nalu.scaling_lists_4x4[i] == [0; 16] {
420 self.u(1, false)?;
421 } else {
422 self.u(1, true)?;
423 self.scaling_list(
424 &self.nalu.scaling_lists_4x4[i],
425 Self::default_scaling_list(i),
426 )?;
427 }
428 } else if self.nalu.scaling_lists_8x8[i - 6] == [0; 64] {
429 self.u(1, false)?;
430 } else {
431 self.u(1, true)?;
432 self.scaling_list(
433 &self.nalu.scaling_lists_8x8[i - 6],
434 Self::default_scaling_list(i),
435 )?;
436 }
437 }
438 }
439
440 self.se(self.nalu.second_chroma_qp_index_offset)?;
441
442 Ok(())
443 }
444}
445
446#[cfg(test)]
447mod tests {
448 use std::io::Cursor;
449
450 use super::*;
451 use crate::codec::h264::parser::Nalu;
452 use crate::codec::h264::parser::NaluType;
453 use crate::codec::h264::parser::Parser;
454 use crate::codec::h264::parser::Profile;
455
456 #[test]
457 fn synthesize_sps() {
458 let raw_sps_buf = [0x00, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x0a, 0xfb, 0x88];
459 let mut raw_sps = Cursor::new(&raw_sps_buf[..]);
460
461 let nalu = Nalu::next(&mut raw_sps).unwrap();
462 assert_eq!(nalu.header.type_, NaluType::Sps);
463
464 let mut parser = Parser::default();
465 let sps = parser.parse_sps(&nalu).unwrap();
466
467 let mut buf = Vec::<u8>::new();
468 Synthesizer::<'_, Sps, _>::synthesize(0, sps, &mut buf, false).unwrap();
469
470 assert_eq!(buf, raw_sps_buf);
471
472 let write_to_file = std::option_env!("CROS_CODECS_TEST_WRITE_TO_FILE") == Some("true");
473 if write_to_file {
474 let mut out = std::fs::File::create("sps.h264").unwrap();
475 out.write_all(&buf).unwrap();
476 out.flush().unwrap();
477 }
478
479 let mut cursor = Cursor::new(&buf[..]);
480 let nalu = Nalu::next(&mut cursor).unwrap();
481
482 let mut parser = Parser::default();
483
484 let sps2 = parser.parse_sps(&nalu).unwrap();
485
486 assert_eq!(sps, sps2);
487 }
488
489 #[test]
490 fn synthesize_sps_scaling_lists() {
491 let sps = Sps {
492 profile_idc: Profile::High as u8,
493 seq_scaling_matrix_present_flag: true,
494 scaling_lists_4x4: [[
495 11, 20, 10, 20, 10, 22, 10, 20, 10, 20, 13, 20, 10, 20, 10, 24,
496 ]; 6],
497 scaling_lists_8x8: [
498 [
499 33, 20, 10, 21, 33, 20, 12, 20, 33, 23, 10, 20, 33, 20, 10, 20, 33, 24, 10, 20,
500 33, 20, 15, 20, 33, 20, 10, 26, 33, 20, 17, 20, 33, 28, 10, 20, 33, 20, 10, 20,
501 33, 29, 10, 20, 33, 20, 11, 20, 33, 20, 10, 20, 33, 20, 10, 20, 33, 20, 10, 20,
502 33, 20, 10, 20,
503 ],
504 [
505 10, 77, 11, 20, 10, 77, 12, 20, 10, 77, 13, 20, 10, 77, 14, 20, 10, 77, 15, 20,
506 10, 77, 16, 20, 10, 77, 17, 20, 10, 77, 18, 20, 10, 77, 19, 20, 10, 77, 10, 20,
507 10, 77, 10, 21, 10, 77, 10, 22, 10, 77, 10, 23, 10, 77, 10, 24, 10, 77, 10, 26,
508 10, 77, 10, 28,
509 ],
510 [0; 64],
511 [0; 64],
512 [0; 64],
513 [0; 64],
514 ],
515 frame_mbs_only_flag: true,
516 ..Default::default()
517 };
518
519 let mut buf = Vec::<u8>::new();
520 Synthesizer::<'_, Sps, _>::synthesize(0, &sps, &mut buf, false).unwrap();
521
522 let write_to_file = std::option_env!("CROS_CODECS_TEST_WRITE_TO_FILE") == Some("true");
523 if write_to_file {
524 let mut out = std::fs::File::create("sps.h264").unwrap();
525 out.write_all(&buf).unwrap();
526 out.flush().unwrap();
527 }
528
529 let mut cursor = Cursor::new(&buf[..]);
530 let nalu = Nalu::next(&mut cursor).unwrap();
531
532 let mut parser = Parser::default();
533
534 let sps2 = parser.parse_sps(&nalu).unwrap();
535
536 assert_eq!(sps.scaling_lists_4x4, sps2.scaling_lists_4x4);
537 assert_eq!(sps.scaling_lists_8x8, sps2.scaling_lists_8x8);
538 }
539
540 #[test]
541 fn synthesize_pps() {
542 let raw_sps_pps = [
543 0x00, 0x00, 0x00, 0x01, 0x07, 0x4d, 0x40, 0x0d, 0xa9, 0x18, 0x28, 0x3e, 0x60, 0x0d,
544 0x41, 0x80, 0x41, 0xad, 0xb0, 0xad, 0x7b, 0xdf, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08,
545 0xde, 0x09, 0x88,
546 ];
547
548 let mut buf = Vec::<u8>::new();
549 let mut out = Cursor::new(&mut buf);
550
551 let mut cursor = Cursor::new(&raw_sps_pps[..]);
552 let mut parser: Parser = Default::default();
553
554 while let Ok(nalu) = Nalu::next(&mut cursor) {
555 match nalu.header.type_ {
556 NaluType::Sps => {
557 let sps = parser.parse_sps(&nalu).unwrap();
558 Synthesizer::<'_, Sps, _>::synthesize(0, sps, &mut out, false).unwrap();
559 }
560 NaluType::Pps => {
561 let pps = parser.parse_pps(&nalu).unwrap();
562 Synthesizer::<'_, Pps, _>::synthesize(0, pps, &mut out, false).unwrap();
563 }
564 _ => panic!(),
565 }
566 }
567
568 let write_to_file = std::option_env!("CROS_CODECS_TEST_WRITE_TO_FILE") == Some("true");
569 if write_to_file {
570 let mut out = std::fs::File::create("sps_pps.h264").unwrap();
571 out.write_all(&buf).unwrap();
572 out.flush().unwrap();
573
574 let mut out = std::fs::File::create("sps_pps_ref.h264").unwrap();
575 out.write_all(&raw_sps_pps).unwrap();
576 out.flush().unwrap();
577 }
578
579 assert_eq!(buf, raw_sps_pps);
580 }
581}