use std::fmt;
use std::io::Write;
use crate::codec::h264::nalu_writer::NaluWriter;
use crate::codec::h264::nalu_writer::NaluWriterError;
use crate::codec::h264::parser::HrdParams;
use crate::codec::h264::parser::NaluType;
use crate::codec::h264::parser::Pps;
use crate::codec::h264::parser::SliceHeader;
#[cfg(feature = "vaapi")]
use crate::codec::h264::parser::SliceType;
use crate::codec::h264::parser::Sps;
use crate::codec::h264::parser::DEFAULT_4X4_INTER;
use crate::codec::h264::parser::DEFAULT_4X4_INTRA;
use crate::codec::h264::parser::DEFAULT_8X8_INTER;
use crate::codec::h264::parser::DEFAULT_8X8_INTRA;
#[cfg(feature = "vaapi")]
use crate::encoder::stateless::h264::IsReference;
mod private {
pub trait NaluStruct {}
}
impl private::NaluStruct for Sps {}
impl private::NaluStruct for Pps {}
impl private::NaluStruct for SliceHeader {}
#[derive(Debug)]
pub enum SynthesizerError {
Unsupported,
NaluWriter(NaluWriterError),
}
impl fmt::Display for SynthesizerError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
SynthesizerError::Unsupported => write!(f, "tried to synthesize unsupported settings"),
SynthesizerError::NaluWriter(x) => write!(f, "{}", x.to_string()),
}
}
}
impl From<NaluWriterError> for SynthesizerError {
fn from(err: NaluWriterError) -> Self {
SynthesizerError::NaluWriter(err)
}
}
pub type SynthesizerResult<T> = Result<T, SynthesizerError>;
pub struct Synthesizer<'n, N: private::NaluStruct, W: Write> {
writer: NaluWriter<W>,
nalu: &'n N,
}
const EXTENDED_SAR: u8 = 255;
impl<N: private::NaluStruct, W: Write> Synthesizer<'_, N, W> {
fn u<T: Into<u32>>(&mut self, bits: usize, value: T) -> SynthesizerResult<()> {
self.writer.write_u(bits, value)?;
Ok(())
}
fn f<T: Into<u32>>(&mut self, bits: usize, value: T) -> SynthesizerResult<()> {
self.writer.write_f(bits, value)?;
Ok(())
}
fn ue<T: Into<u32>>(&mut self, value: T) -> SynthesizerResult<()> {
self.writer.write_ue(value)?;
Ok(())
}
fn se<T: Into<i32>>(&mut self, value: T) -> SynthesizerResult<()> {
self.writer.write_se(value)?;
Ok(())
}
fn scaling_list(&mut self, list: &[u8], default: &[u8]) -> SynthesizerResult<()> {
if list == default {
self.se(-8)?;
return Ok(());
}
let mut run = list.len();
for j in (1..list.len()).rev() {
if list[j - 1] != list[j] {
break;
}
run -= 1;
}
let mut last_scale = 8;
for scale in &list[0..run] {
let delta_scale = *scale as i32 - last_scale;
self.se(delta_scale)?;
last_scale = *scale as i32;
}
if run < list.len() {
self.se(-last_scale)?;
}
Ok(())
}
fn default_scaling_list(i: usize) -> &'static [u8] {
match i {
0 => &DEFAULT_4X4_INTRA[..],
1 => &DEFAULT_4X4_INTRA[..],
2 => &DEFAULT_4X4_INTRA[..],
3 => &DEFAULT_4X4_INTER[..],
4 => &DEFAULT_4X4_INTER[..],
5 => &DEFAULT_4X4_INTER[..],
6 => &DEFAULT_8X8_INTRA[..],
7 => &DEFAULT_8X8_INTER[..],
8 => &DEFAULT_8X8_INTRA[..],
9 => &DEFAULT_8X8_INTER[..],
10 => &DEFAULT_8X8_INTRA[..],
11 => &DEFAULT_8X8_INTER[..],
_ => unreachable!(),
}
}
fn rbsp_trailing_bits(&mut self) -> SynthesizerResult<()> {
self.f(1, 1u32)?;
while !self.writer.aligned() {
self.f(1, 0u32)?;
}
Ok(())
}
}
impl<'n, W: Write> Synthesizer<'n, Sps, W> {
pub fn synthesize(
ref_idc: u8,
sps: &'n Sps,
writer: W,
ep_enabled: bool,
) -> SynthesizerResult<()> {
let mut s = Self { writer: NaluWriter::<W>::new(writer, ep_enabled), nalu: sps };
s.writer.write_header(ref_idc, NaluType::Sps as u8)?;
s.seq_parameter_set_data()?;
s.rbsp_trailing_bits()
}
fn hrd_parameters(&mut self, hrd_params: &HrdParams) -> SynthesizerResult<()> {
self.ue(hrd_params.cpb_cnt_minus1)?;
self.u(4, hrd_params.bit_rate_scale)?;
self.u(4, hrd_params.cpb_size_scale)?;
for i in 0..=(hrd_params.cpb_cnt_minus1 as usize) {
self.ue(hrd_params.bit_rate_value_minus1[i])?;
self.ue(hrd_params.cpb_size_value_minus1[i])?;
self.u(1, hrd_params.cbr_flag[i])?;
}
self.u(5, hrd_params.initial_cpb_removal_delay_length_minus1)?;
self.u(5, hrd_params.cpb_removal_delay_length_minus1)?;
self.u(5, hrd_params.dpb_output_delay_length_minus1)?;
self.u(5, hrd_params.time_offset_length)?;
Ok(())
}
fn vui_parameters(&mut self) -> SynthesizerResult<()> {
let vui_params = &self.nalu.vui_parameters;
self.u(1, vui_params.aspect_ratio_info_present_flag)?;
if vui_params.aspect_ratio_info_present_flag {
self.u(8, vui_params.aspect_ratio_idc)?;
if vui_params.aspect_ratio_idc == EXTENDED_SAR {
self.u(16, vui_params.sar_width)?;
self.u(16, vui_params.sar_height)?;
}
}
self.u(1, vui_params.overscan_info_present_flag)?;
if vui_params.overscan_info_present_flag {
self.u(1, vui_params.overscan_appropriate_flag)?;
}
self.u(1, vui_params.video_signal_type_present_flag)?;
if vui_params.video_signal_type_present_flag {
self.u(3, vui_params.video_format)?;
self.u(1, vui_params.video_full_range_flag)?;
self.u(1, vui_params.colour_description_present_flag)?;
if vui_params.colour_description_present_flag {
self.u(8, vui_params.colour_primaries)?;
self.u(8, vui_params.transfer_characteristics)?;
self.u(8, vui_params.matrix_coefficients)?;
}
}
self.u(1, vui_params.chroma_loc_info_present_flag)?;
if vui_params.chroma_loc_info_present_flag {
self.ue(vui_params.chroma_sample_loc_type_top_field)?;
self.ue(self.nalu.vui_parameters.chroma_sample_loc_type_bottom_field)?;
}
self.u(1, vui_params.timing_info_present_flag)?;
if vui_params.timing_info_present_flag {
self.u(32, vui_params.num_units_in_tick)?;
self.u(32, vui_params.time_scale)?;
self.u(1, vui_params.fixed_frame_rate_flag)?;
}
self.u(1, vui_params.nal_hrd_parameters_present_flag)?;
if vui_params.nal_hrd_parameters_present_flag {
self.hrd_parameters(&vui_params.nal_hrd_parameters)?;
}
self.u(1, vui_params.vcl_hrd_parameters_present_flag)?;
if vui_params.vcl_hrd_parameters_present_flag {
self.hrd_parameters(&vui_params.vcl_hrd_parameters)?;
}
if vui_params.nal_hrd_parameters_present_flag || vui_params.vcl_hrd_parameters_present_flag
{
self.u(1, vui_params.low_delay_hrd_flag)?;
}
self.u(1, vui_params.pic_struct_present_flag)?;
self.u(1, vui_params.bitstream_restriction_flag)?;
if vui_params.bitstream_restriction_flag {
self.u(1, vui_params.motion_vectors_over_pic_boundaries_flag)?;
self.ue(vui_params.max_bytes_per_pic_denom)?;
self.ue(vui_params.max_bits_per_mb_denom)?;
self.ue(vui_params.log2_max_mv_length_horizontal)?;
self.ue(vui_params.log2_max_mv_length_vertical)?;
self.ue(vui_params.max_num_reorder_frames)?;
self.ue(vui_params.max_dec_frame_buffering)?;
}
Ok(())
}
fn seq_parameter_set_data(&mut self) -> SynthesizerResult<()> {
self.u(8, self.nalu.profile_idc)?;
self.u(1, self.nalu.constraint_set0_flag)?;
self.u(1, self.nalu.constraint_set1_flag)?;
self.u(1, self.nalu.constraint_set2_flag)?;
self.u(1, self.nalu.constraint_set3_flag)?;
self.u(1, self.nalu.constraint_set4_flag)?;
self.u(1, self.nalu.constraint_set5_flag)?;
self.u(2, 0u32)?;
self.u(8, self.nalu.level_idc as u32)?;
self.ue(self.nalu.seq_parameter_set_id)?;
if self.nalu.profile_idc == 100
|| self.nalu.profile_idc == 110
|| self.nalu.profile_idc == 122
|| self.nalu.profile_idc == 244
|| self.nalu.profile_idc == 44
|| self.nalu.profile_idc == 83
|| self.nalu.profile_idc == 86
|| self.nalu.profile_idc == 118
|| self.nalu.profile_idc == 128
|| self.nalu.profile_idc == 138
|| self.nalu.profile_idc == 139
|| self.nalu.profile_idc == 134
|| self.nalu.profile_idc == 135
{
self.ue(self.nalu.chroma_format_idc)?;
if self.nalu.chroma_format_idc == 3 {
self.u(1, self.nalu.separate_colour_plane_flag)?;
}
self.ue(self.nalu.bit_depth_luma_minus8)?;
self.ue(self.nalu.bit_depth_chroma_minus8)?;
self.u(1, self.nalu.qpprime_y_zero_transform_bypass_flag)?;
self.u(1, self.nalu.seq_scaling_matrix_present_flag)?;
if self.nalu.seq_scaling_matrix_present_flag {
let scaling_list_count = if self.nalu.chroma_format_idc != 3 { 8 } else { 12 };
for i in 0..scaling_list_count {
if i < 6 {
if self.nalu.scaling_lists_4x4[i] == [0; 16] {
self.u(1, false)?;
} else {
self.u(1, true)?;
self.scaling_list(
&self.nalu.scaling_lists_4x4[i],
Self::default_scaling_list(i),
)?;
}
} else if self.nalu.scaling_lists_8x8[i - 6] == [0; 64] {
self.u(1, false)?;
} else {
self.u(1, true)?;
self.scaling_list(
&self.nalu.scaling_lists_8x8[i - 6],
Self::default_scaling_list(i),
)?;
}
}
}
}
self.ue(self.nalu.log2_max_frame_num_minus4)?;
self.ue(self.nalu.pic_order_cnt_type)?;
if self.nalu.pic_order_cnt_type == 0 {
self.ue(self.nalu.log2_max_pic_order_cnt_lsb_minus4)?;
} else if self.nalu.pic_order_cnt_type == 1 {
self.u(1, self.nalu.delta_pic_order_always_zero_flag)?;
self.se(self.nalu.offset_for_non_ref_pic)?;
self.se(self.nalu.offset_for_top_to_bottom_field)?;
self.ue(self.nalu.num_ref_frames_in_pic_order_cnt_cycle)?;
for offset_for_ref_frame in &self.nalu.offset_for_ref_frame {
self.se(*offset_for_ref_frame)?;
}
}
self.ue(self.nalu.max_num_ref_frames)?;
self.u(1, self.nalu.gaps_in_frame_num_value_allowed_flag)?;
self.ue(self.nalu.pic_width_in_mbs_minus1)?;
self.ue(self.nalu.pic_height_in_map_units_minus1)?;
self.u(1, self.nalu.frame_mbs_only_flag)?;
if !self.nalu.frame_mbs_only_flag {
self.u(1, self.nalu.mb_adaptive_frame_field_flag)?;
}
self.u(1, self.nalu.direct_8x8_inference_flag)?;
self.u(1, self.nalu.frame_cropping_flag)?;
if self.nalu.frame_cropping_flag {
self.ue(self.nalu.frame_crop_left_offset)?;
self.ue(self.nalu.frame_crop_right_offset)?;
self.ue(self.nalu.frame_crop_top_offset)?;
self.ue(self.nalu.frame_crop_bottom_offset)?;
}
self.u(1, self.nalu.vui_parameters_present_flag)?;
if self.nalu.vui_parameters_present_flag {
self.vui_parameters()?;
}
Ok(())
}
}
impl<'n, W: Write> Synthesizer<'n, Pps, W> {
pub fn synthesize(
ref_idc: u8,
pps: &'n Pps,
writer: W,
ep_enabled: bool,
) -> SynthesizerResult<()> {
let mut s = Self { writer: NaluWriter::<W>::new(writer, ep_enabled), nalu: pps };
s.writer.write_header(ref_idc, NaluType::Pps as u8)?;
s.pic_parameter_set_rbsp()?;
s.rbsp_trailing_bits()
}
fn pic_parameter_set_rbsp(&mut self) -> SynthesizerResult<()> {
self.ue(self.nalu.pic_parameter_set_id)?;
self.ue(self.nalu.seq_parameter_set_id)?;
self.u(1, self.nalu.entropy_coding_mode_flag)?;
self.u(1, self.nalu.bottom_field_pic_order_in_frame_present_flag)?;
self.ue(self.nalu.num_slice_groups_minus1)?;
if self.nalu.num_slice_groups_minus1 > 0 {
return Err(SynthesizerError::Unsupported);
}
self.ue(self.nalu.num_ref_idx_l0_default_active_minus1)?;
self.ue(self.nalu.num_ref_idx_l1_default_active_minus1)?;
self.u(1, self.nalu.weighted_pred_flag)?;
self.u(2, self.nalu.weighted_bipred_idc)?;
self.se(self.nalu.pic_init_qp_minus26)?;
self.se(self.nalu.pic_init_qs_minus26)?;
self.se(self.nalu.chroma_qp_index_offset)?;
self.u(1, self.nalu.deblocking_filter_control_present_flag)?;
self.u(1, self.nalu.constrained_intra_pred_flag)?;
self.u(1, self.nalu.redundant_pic_cnt_present_flag)?;
if !(self.nalu.transform_8x8_mode_flag
|| self.nalu.pic_scaling_matrix_present_flag
|| self.nalu.second_chroma_qp_index_offset != 0)
{
return Ok(());
}
self.u(1, self.nalu.transform_8x8_mode_flag)?;
self.u(1, self.nalu.pic_scaling_matrix_present_flag)?;
if self.nalu.pic_scaling_matrix_present_flag {
let mut scaling_list_count = 6;
if self.nalu.transform_8x8_mode_flag {
if self.nalu.sps.chroma_format_idc != 3 {
scaling_list_count += 2;
} else {
scaling_list_count += 6;
}
}
for i in 0..scaling_list_count {
if i < 6 {
if self.nalu.scaling_lists_4x4[i] == [0; 16] {
self.u(1, false)?;
} else {
self.u(1, true)?;
self.scaling_list(
&self.nalu.scaling_lists_4x4[i],
Self::default_scaling_list(i),
)?;
}
} else if self.nalu.scaling_lists_8x8[i - 6] == [0; 64] {
self.u(1, false)?;
} else {
self.u(1, true)?;
self.scaling_list(
&self.nalu.scaling_lists_8x8[i - 6],
Self::default_scaling_list(i),
)?;
}
}
}
self.se(self.nalu.second_chroma_qp_index_offset)?;
Ok(())
}
}
#[cfg(feature = "vaapi")]
pub type NumTrailingBits = u8;
#[cfg(feature = "vaapi")]
impl<'n, W: Write> Synthesizer<'n, SliceHeader, W> {
pub fn synthesize(
header: &'n SliceHeader,
sps: &'n Sps,
pps: &'n Pps,
is_idr: bool,
is_ref: IsReference,
writer: W,
ep_enabled: bool,
) -> SynthesizerResult<NumTrailingBits> {
let mut s = Self { writer: NaluWriter::<W>::new(writer, ep_enabled), nalu: header };
let ref_idc = if is_idr {
3
} else if is_ref == IsReference::LongTerm {
2
} else if is_ref == IsReference::ShortTerm {
1
} else {
0
};
let nalu_type = if is_idr { NaluType::SliceIdr } else { NaluType::Slice };
s.writer.write_header(ref_idc, nalu_type as u8)?;
s.slice_header_data(sps, pps, is_idr, is_ref)?;
let num_trailing_bits = s.writer.flush()?;
Ok(num_trailing_bits)
}
fn slice_header_data(
&mut self,
sps: &Sps,
pps: &Pps,
is_idr: bool,
is_ref: IsReference,
) -> SynthesizerResult<()> {
let hdr = self.nalu;
self.ue(hdr.first_mb_in_slice)?;
self.ue(hdr.slice_type as u32)?;
self.ue(hdr.pic_parameter_set_id)?;
if sps.separate_colour_plane_flag {
self.u(2, hdr.colour_plane_id)?;
}
let frame_num_bits = sps.log2_max_frame_num_minus4 as usize + 4;
self.u(frame_num_bits, hdr.frame_num)?;
if !sps.frame_mbs_only_flag {
self.u(1, hdr.field_pic_flag as u32)?;
if hdr.field_pic_flag {
self.u(1, hdr.bottom_field_flag as u32)?;
}
}
if is_idr {
self.ue(hdr.idr_pic_id)?;
}
if sps.pic_order_cnt_type == 0 {
let pic_order_cnt_lsb_bits = sps.log2_max_pic_order_cnt_lsb_minus4 as usize + 4;
self.u(pic_order_cnt_lsb_bits, hdr.pic_order_cnt_lsb)?;
if pps.bottom_field_pic_order_in_frame_present_flag && !hdr.field_pic_flag {
self.se(hdr.delta_pic_order_cnt_bottom)?;
}
}
if sps.pic_order_cnt_type == 1 && !sps.delta_pic_order_always_zero_flag {
self.se(hdr.delta_pic_order_cnt[0])?;
if pps.bottom_field_pic_order_in_frame_present_flag && !hdr.field_pic_flag {
self.se(hdr.delta_pic_order_cnt[1])?;
}
}
if pps.redundant_pic_cnt_present_flag {
self.ue(hdr.redundant_pic_cnt)?;
}
if hdr.slice_type == SliceType::B {
self.u(1, hdr.direct_spatial_mv_pred_flag as u32)?;
}
if hdr.slice_type == SliceType::P
|| hdr.slice_type == SliceType::Sp
|| hdr.slice_type == SliceType::B
{
self.u(1, hdr.num_ref_idx_active_override_flag as u32)?;
if hdr.num_ref_idx_active_override_flag {
self.ue(hdr.num_ref_idx_l0_active_minus1)?;
if hdr.slice_type == SliceType::B {
self.ue(hdr.num_ref_idx_l1_active_minus1)?;
}
}
}
self.ref_pic_list_modification(hdr)?;
if (pps.weighted_pred_flag
&& (hdr.slice_type == SliceType::P || hdr.slice_type == SliceType::Sp))
|| (pps.weighted_bipred_idc == 1 && hdr.slice_type == SliceType::B)
{
self.pred_weight_table(hdr, sps)?;
}
if is_ref != IsReference::No {
self.dec_ref_pic_marking(hdr, is_idr)?;
}
if pps.entropy_coding_mode_flag
&& hdr.slice_type != SliceType::I
&& hdr.slice_type != SliceType::Si
{
self.ue(hdr.cabac_init_idc)?;
}
self.se(hdr.slice_qp_delta)?;
if hdr.slice_type == SliceType::Sp || hdr.slice_type == SliceType::Si {
if hdr.slice_type == SliceType::Sp {
self.u(1, hdr.sp_for_switch_flag as u32)?;
}
self.se(hdr.slice_qs_delta)?;
}
if pps.deblocking_filter_control_present_flag {
self.ue(hdr.disable_deblocking_filter_idc)?;
if hdr.disable_deblocking_filter_idc != 1 {
self.se(hdr.slice_alpha_c0_offset_div2)?;
self.se(hdr.slice_beta_offset_div2)?;
}
}
if pps.num_slice_groups_minus1 > 0 {
return Err(SynthesizerError::Unsupported);
}
Ok(())
}
fn ref_pic_list_modification(&mut self, hdr: &SliceHeader) -> SynthesizerResult<()> {
let slice_type_mod5 = hdr.slice_type as u8 % 5;
if slice_type_mod5 != 2 && slice_type_mod5 != 4 {
self.u(1, hdr.ref_pic_list_modification_flag_l0 as u32)?;
if hdr.ref_pic_list_modification_flag_l0 {
for modification in &hdr.ref_pic_list_modification_l0 {
self.ue(modification.modification_of_pic_nums_idc)?;
if modification.modification_of_pic_nums_idc == 0
|| modification.modification_of_pic_nums_idc == 1
{
self.ue(modification.abs_diff_pic_num_minus1)?;
} else if modification.modification_of_pic_nums_idc == 2 {
self.ue(modification.long_term_pic_num)?;
}
}
self.ue(3u32)?;
}
}
if slice_type_mod5 == 1 {
self.u(1, hdr.ref_pic_list_modification_flag_l1 as u32)?;
if hdr.ref_pic_list_modification_flag_l1 {
for modification in &hdr.ref_pic_list_modification_l1 {
self.ue(modification.modification_of_pic_nums_idc)?;
if modification.modification_of_pic_nums_idc == 0
|| modification.modification_of_pic_nums_idc == 1
{
self.ue(modification.abs_diff_pic_num_minus1)?;
} else if modification.modification_of_pic_nums_idc == 2 {
self.ue(modification.long_term_pic_num)?;
}
}
self.ue(3u32)?;
}
}
Ok(())
}
fn pred_weight_table(&mut self, hdr: &SliceHeader, sps: &Sps) -> SynthesizerResult<()> {
let pwt = &hdr.pred_weight_table;
let chroma_array_type = sps.chroma_array_type();
self.ue(pwt.luma_log2_weight_denom)?;
if chroma_array_type != 0 {
self.ue(pwt.chroma_log2_weight_denom)?;
}
let num_ref_idx_l0 = (hdr.num_ref_idx_l0_active_minus1 + 1) as usize;
for i in 0..num_ref_idx_l0 {
let luma_weight_l0_flag = pwt.luma_weight_l0[i] != (1 << pwt.luma_log2_weight_denom);
self.u(1, luma_weight_l0_flag as u32)?;
if luma_weight_l0_flag {
self.se(pwt.luma_weight_l0[i])?;
self.se(pwt.luma_offset_l0[i])?;
}
if chroma_array_type != 0 {
let default_weight = 1 << pwt.chroma_log2_weight_denom;
let chroma_weight_l0_flag = pwt.chroma_weight_l0[i][0] != default_weight
|| pwt.chroma_weight_l0[i][1] != default_weight
|| pwt.chroma_offset_l0[i][0] != 0
|| pwt.chroma_offset_l0[i][1] != 0;
self.u(1, chroma_weight_l0_flag as u32)?;
if chroma_weight_l0_flag {
for j in 0..2 {
self.se(pwt.chroma_weight_l0[i][j])?;
self.se(pwt.chroma_offset_l0[i][j])?;
}
}
}
}
if hdr.slice_type as u8 % 5 == 1 {
let num_ref_idx_l1 = (hdr.num_ref_idx_l1_active_minus1 + 1) as usize;
for i in 0..num_ref_idx_l1 {
let luma_weight_l1_flag =
pwt.luma_weight_l1[i] != (1 << pwt.luma_log2_weight_denom) as i16;
self.u(1, luma_weight_l1_flag as u32)?;
if luma_weight_l1_flag {
self.se(pwt.luma_weight_l1[i])?;
self.se(pwt.luma_offset_l1[i])?;
}
if chroma_array_type != 0 {
let default_weight = (1 << pwt.chroma_log2_weight_denom) as i16;
let chroma_weight_l1_flag = pwt.chroma_weight_l1[i][0] != default_weight
|| pwt.chroma_weight_l1[i][1] != default_weight
|| pwt.chroma_offset_l1[i][0] != 0
|| pwt.chroma_offset_l1[i][1] != 0;
self.u(1, chroma_weight_l1_flag as u32)?;
if chroma_weight_l1_flag {
for j in 0..2 {
self.se(pwt.chroma_weight_l1[i][j])?;
self.se(pwt.chroma_offset_l1[i][j])?;
}
}
}
}
}
Ok(())
}
fn dec_ref_pic_marking(&mut self, hdr: &SliceHeader, is_idr: bool) -> SynthesizerResult<()> {
let rpm = &hdr.dec_ref_pic_marking;
if is_idr {
self.u(1, rpm.no_output_of_prior_pics_flag as u32)?;
self.u(1, rpm.long_term_reference_flag as u32)?;
} else {
self.u(1, rpm.adaptive_ref_pic_marking_mode_flag as u32)?;
if rpm.adaptive_ref_pic_marking_mode_flag {
for marking in &rpm.inner {
self.ue(marking.memory_management_control_operation)?;
if marking.memory_management_control_operation == 1
|| marking.memory_management_control_operation == 3
{
self.ue(marking.difference_of_pic_nums_minus1)?;
}
if marking.memory_management_control_operation == 2 {
self.ue(marking.long_term_pic_num)?;
}
if marking.memory_management_control_operation == 3
|| marking.memory_management_control_operation == 6
{
self.ue(marking.long_term_frame_idx)?;
}
if marking.memory_management_control_operation == 4 {
self.ue(marking.max_long_term_frame_idx.to_value_plus1())?;
}
}
self.ue(0u32)?;
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use std::io::Cursor;
use super::*;
use crate::codec::h264::parser::Nalu;
use crate::codec::h264::parser::NaluType;
use crate::codec::h264::parser::Parser;
use crate::codec::h264::parser::Profile;
#[test]
fn synthesize_sps() {
let raw_sps_buf = [0x00, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x0a, 0xfb, 0x88];
let mut raw_sps = Cursor::new(&raw_sps_buf[..]);
let nalu = Nalu::next(&mut raw_sps).unwrap();
assert_eq!(nalu.header.type_, NaluType::Sps);
let mut parser = Parser::default();
let sps = parser.parse_sps(&nalu).unwrap();
let mut buf = Vec::<u8>::new();
Synthesizer::<'_, Sps, _>::synthesize(0, sps, &mut buf, false).unwrap();
assert_eq!(buf, raw_sps_buf);
let write_to_file = std::option_env!("CROS_CODECS_TEST_WRITE_TO_FILE") == Some("true");
if write_to_file {
let mut out = std::fs::File::create("sps.h264").unwrap();
out.write_all(&buf).unwrap();
out.flush().unwrap();
}
let mut cursor = Cursor::new(&buf[..]);
let nalu = Nalu::next(&mut cursor).unwrap();
let mut parser = Parser::default();
let sps2 = parser.parse_sps(&nalu).unwrap();
assert_eq!(sps, sps2);
}
#[test]
fn synthesize_sps_scaling_lists() {
let sps = Sps {
profile_idc: Profile::High as u8,
seq_scaling_matrix_present_flag: true,
scaling_lists_4x4: [[11, 20, 10, 20, 10, 22, 10, 20, 10, 20, 13, 20, 10, 20, 10, 24];
6],
scaling_lists_8x8: [
[
33, 20, 10, 21, 33, 20, 12, 20, 33, 23, 10, 20, 33, 20, 10, 20, 33, 24, 10, 20,
33, 20, 15, 20, 33, 20, 10, 26, 33, 20, 17, 20, 33, 28, 10, 20, 33, 20, 10, 20,
33, 29, 10, 20, 33, 20, 11, 20, 33, 20, 10, 20, 33, 20, 10, 20, 33, 20, 10, 20,
33, 20, 10, 20,
],
[
10, 77, 11, 20, 10, 77, 12, 20, 10, 77, 13, 20, 10, 77, 14, 20, 10, 77, 15, 20,
10, 77, 16, 20, 10, 77, 17, 20, 10, 77, 18, 20, 10, 77, 19, 20, 10, 77, 10, 20,
10, 77, 10, 21, 10, 77, 10, 22, 10, 77, 10, 23, 10, 77, 10, 24, 10, 77, 10, 26,
10, 77, 10, 28,
],
[0; 64],
[0; 64],
[0; 64],
[0; 64],
],
frame_mbs_only_flag: true,
..Default::default()
};
let mut buf = Vec::<u8>::new();
Synthesizer::<'_, Sps, _>::synthesize(0, &sps, &mut buf, false).unwrap();
let write_to_file = std::option_env!("CROS_CODECS_TEST_WRITE_TO_FILE") == Some("true");
if write_to_file {
let mut out = std::fs::File::create("sps.h264").unwrap();
out.write_all(&buf).unwrap();
out.flush().unwrap();
}
let mut cursor = Cursor::new(&buf[..]);
let nalu = Nalu::next(&mut cursor).unwrap();
let mut parser = Parser::default();
let sps2 = parser.parse_sps(&nalu).unwrap();
assert_eq!(sps.scaling_lists_4x4, sps2.scaling_lists_4x4);
assert_eq!(sps.scaling_lists_8x8, sps2.scaling_lists_8x8);
}
#[test]
fn synthesize_pps() {
let raw_sps_pps = [
0x00, 0x00, 0x00, 0x01, 0x07, 0x4d, 0x40, 0x0d, 0xa9, 0x18, 0x28, 0x3e, 0x60, 0x0d,
0x41, 0x80, 0x41, 0xad, 0xb0, 0xad, 0x7b, 0xdf, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08,
0xde, 0x09, 0x88,
];
let mut buf = Vec::<u8>::new();
let mut out = Cursor::new(&mut buf);
let mut cursor = Cursor::new(&raw_sps_pps[..]);
let mut parser: Parser = Default::default();
while let Ok(nalu) = Nalu::next(&mut cursor) {
match nalu.header.type_ {
NaluType::Sps => {
let sps = parser.parse_sps(&nalu).unwrap();
Synthesizer::<'_, Sps, _>::synthesize(0, sps, &mut out, false).unwrap();
}
NaluType::Pps => {
let pps = parser.parse_pps(&nalu).unwrap();
Synthesizer::<'_, Pps, _>::synthesize(0, pps, &mut out, false).unwrap();
}
_ => panic!(),
}
}
let write_to_file = std::option_env!("CROS_CODECS_TEST_WRITE_TO_FILE") == Some("true");
if write_to_file {
let mut out = std::fs::File::create("sps_pps.h264").unwrap();
out.write_all(&buf).unwrap();
out.flush().unwrap();
let mut out = std::fs::File::create("sps_pps_ref.h264").unwrap();
out.write_all(&raw_sps_pps).unwrap();
out.flush().unwrap();
}
assert_eq!(buf, raw_sps_pps);
}
}