use std::io;
use byteorder::ReadBytesExt;
use bytes::Bytes;
use scuffle_bytes_util::BitReader;
use scuffle_expgolomb::BitReaderExpGolombExt;
#[derive(Debug, Clone, PartialEq)]
pub struct Sps {
pub width: u64,
pub height: u64,
pub frame_rate: f64,
pub color_config: Option<ColorConfig>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ColorConfig {
pub full_range: bool,
pub color_primaries: u8,
pub transfer_characteristics: u8,
pub matrix_coefficients: u8,
}
impl Sps {
pub fn parse(data: Bytes) -> io::Result<Self> {
let mut vec = Vec::with_capacity(data.len());
let mut i = 0;
while i < data.len() - 3 {
if data[i] == 0x00 && data[i + 1] == 0x00 && data[i + 2] == 0x03 {
vec.push(0x00);
vec.push(0x00);
i += 3;
} else {
vec.push(data[i]);
i += 1;
}
}
let mut bit_reader = BitReader::new_from_slice(vec);
let forbidden_zero_bit = bit_reader.read_bit()?;
if forbidden_zero_bit {
return Err(io::Error::new(io::ErrorKind::InvalidData, "forbidden_zero_bit is not zero"));
}
let nalu_type = bit_reader.read_bits(6)?;
if nalu_type != 33 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"nalu_type is not 33", ));
}
bit_reader.seek_bits(
6 + 3 + 4, )?;
let sps_max_sub_layers_minus1 = bit_reader.read_bits(3)?;
bit_reader.seek_bits(1)?; {
bit_reader.seek_bits(
2 + 1 + 5 + 32 + 1 + 1 + 1 + 1 + 43 + 1 + 8, )?;
let mut sub_layer_level_present_flags = vec![false; sps_max_sub_layers_minus1 as usize];
for v in sub_layer_level_present_flags.iter_mut() {
bit_reader.seek_bits(1)?; *v = bit_reader.read_bit()?; }
if sps_max_sub_layers_minus1 > 0 && sps_max_sub_layers_minus1 < 8 {
bit_reader.seek_bits(2 * (8 - sps_max_sub_layers_minus1 as i64))?;
}
for v in sub_layer_level_present_flags.drain(..) {
bit_reader.seek_bits(
2 + 1 + 5 + 32 + 1 + 1 + 1 + 1 + 43 + 1, )?;
if v {
bit_reader.seek_bits(8)?; }
}
}
bit_reader.read_exp_golomb()?; let chroma_format_idc = bit_reader.read_exp_golomb()?;
if chroma_format_idc == 3 {
bit_reader.read_bit()?;
}
let pic_width_in_luma_samples = bit_reader.read_exp_golomb()?;
let pic_height_in_luma_samples = bit_reader.read_exp_golomb()?;
let conformance_window_flag = bit_reader.read_bit()?;
let conf_win_left_offset;
let conf_win_right_offset;
let conf_win_top_offset;
let conf_win_bottom_offset;
if conformance_window_flag {
conf_win_left_offset = bit_reader.read_exp_golomb()?;
conf_win_right_offset = bit_reader.read_exp_golomb()?;
conf_win_top_offset = bit_reader.read_exp_golomb()?;
conf_win_bottom_offset = bit_reader.read_exp_golomb()?;
} else {
conf_win_left_offset = 0;
conf_win_right_offset = 0;
conf_win_top_offset = 0;
conf_win_bottom_offset = 0;
}
let (sub_width_c, sub_height_c) = match chroma_format_idc {
0 => (1, 1),
1 => (2, 2),
2 => (2, 1),
3 => (1, 1),
_ => return Err(io::Error::new(io::ErrorKind::InvalidData, "chroma_format_idc is not 0-3")),
};
let width = pic_width_in_luma_samples - sub_width_c * (conf_win_left_offset + conf_win_right_offset);
let height = pic_height_in_luma_samples - sub_height_c * (conf_win_top_offset + conf_win_bottom_offset);
bit_reader.read_exp_golomb()?; bit_reader.read_exp_golomb()?; bit_reader.read_exp_golomb()?; let sps_sub_layer_ordering_info_present_flag = bit_reader.read_bit()?;
if sps_sub_layer_ordering_info_present_flag {
for _ in 0..=sps_max_sub_layers_minus1 {
bit_reader.read_exp_golomb()?; bit_reader.read_exp_golomb()?; bit_reader.read_exp_golomb()?; }
};
bit_reader.read_exp_golomb()?; bit_reader.read_exp_golomb()?; bit_reader.read_exp_golomb()?; bit_reader.read_exp_golomb()?; bit_reader.read_exp_golomb()?; bit_reader.read_exp_golomb()?;
let scaling_list_enabled_flag = bit_reader.read_bit()?;
if scaling_list_enabled_flag {
let sps_scaling_list_data_present_flag = bit_reader.read_bit()?;
if sps_scaling_list_data_present_flag {
for size_id in 0..4 {
let mut matrix_id = 0;
while matrix_id < 6 {
let scaling_list_pred_mode_flag = bit_reader.read_bit()?;
if !scaling_list_pred_mode_flag {
bit_reader.read_exp_golomb()?; } else {
let coef_num = 64.min(1 << (4 + (size_id << 1)));
let mut next_coef = 8;
if size_id > 1 {
let scaling_list_dc_coef_minus8 = bit_reader.read_signed_exp_golomb()?;
next_coef = 8 + scaling_list_dc_coef_minus8;
}
for _ in 0..coef_num {
let scaling_list_delta_coef = bit_reader.read_signed_exp_golomb()?;
next_coef = (next_coef + scaling_list_delta_coef + 256) % 256;
}
}
matrix_id += if size_id == 3 { 3 } else { 1 };
}
}
}
}
bit_reader.seek_bits(1)?; bit_reader.seek_bits(1)?;
if bit_reader.read_bit()? {
bit_reader.seek_bits(4)?; bit_reader.seek_bits(4)?; bit_reader.read_exp_golomb()?; bit_reader.read_exp_golomb()?; bit_reader.seek_bits(1)?; }
let num_short_term_ref_pic_sets = bit_reader.read_exp_golomb()?;
let mut num_delta_pocs = vec![0; num_short_term_ref_pic_sets as usize];
for st_rps_idx in 0..num_short_term_ref_pic_sets {
if st_rps_idx != 0 && bit_reader.read_bit()? {
bit_reader.seek_bits(1)?;
bit_reader.read_exp_golomb()?;
num_delta_pocs[st_rps_idx as usize] = 0;
for _ in 0..num_delta_pocs[(st_rps_idx - 1) as usize] {
let used_by_curr_pic_flag = bit_reader.read_bit()?;
let use_delta_flag = if !used_by_curr_pic_flag {
bit_reader.read_bit()? } else {
false
};
if used_by_curr_pic_flag || use_delta_flag {
num_delta_pocs[st_rps_idx as usize] += 1;
}
}
} else {
let num_negative_pics = bit_reader.read_exp_golomb()?;
let num_positive_pics = bit_reader.read_exp_golomb()?;
num_delta_pocs[st_rps_idx as usize] = num_negative_pics + num_positive_pics;
for _ in 0..num_negative_pics {
bit_reader.read_exp_golomb()?; bit_reader.seek_bits(1)?; }
for _ in 0..num_positive_pics {
bit_reader.read_exp_golomb()?; bit_reader.seek_bits(1)?; }
}
}
let long_term_ref_pics_present_flag = bit_reader.read_bit()?;
if long_term_ref_pics_present_flag {
let num_long_term_ref_pics_sps = bit_reader.read_exp_golomb()?;
for _ in 0..num_long_term_ref_pics_sps {
bit_reader.read_exp_golomb()?; bit_reader.seek_bits(1)?; }
}
bit_reader.seek_bits(1)?; bit_reader.seek_bits(1)?; let vui_parameters_present_flag = bit_reader.read_bit()?;
let mut color_config = None;
let mut frame_rate = 0.0;
if vui_parameters_present_flag {
let aspect_ratio_info_present_flag = bit_reader.read_bit()?;
if aspect_ratio_info_present_flag {
let aspect_ratio_idc = bit_reader.read_bits(8)?;
if aspect_ratio_idc == 255 {
bit_reader.seek_bits(16)?; bit_reader.seek_bits(16)?; }
}
let overscan_info_present_flag = bit_reader.read_bit()?;
if overscan_info_present_flag {
bit_reader.seek_bits(1)?; }
let video_signal_type_present_flag = bit_reader.read_bit()?;
if video_signal_type_present_flag {
bit_reader.seek_bits(3)?; let full_range = bit_reader.read_bit()?; let color_primaries;
let transfer_characteristics;
let matrix_coefficients;
let colour_description_present_flag = bit_reader.read_bit()?;
if colour_description_present_flag {
color_primaries = bit_reader.read_u8()?; transfer_characteristics = bit_reader.read_u8()?; matrix_coefficients = bit_reader.read_u8()?; } else {
color_primaries = 2; transfer_characteristics = 2; matrix_coefficients = 2; }
color_config = Some(ColorConfig {
full_range,
color_primaries,
transfer_characteristics,
matrix_coefficients,
});
}
let chroma_loc_info_present_flag = bit_reader.read_bit()?;
if chroma_loc_info_present_flag {
bit_reader.read_exp_golomb()?; bit_reader.read_exp_golomb()?; }
bit_reader.seek_bits(1)?;
bit_reader.seek_bits(1)?;
bit_reader.seek_bits(1)?;
let default_display_window_flag = bit_reader.read_bit()?;
if default_display_window_flag {
bit_reader.read_exp_golomb()?; bit_reader.read_exp_golomb()?; bit_reader.read_exp_golomb()?; bit_reader.read_exp_golomb()?; }
let vui_timing_info_present_flag = bit_reader.read_bit()?;
if vui_timing_info_present_flag {
let num_units_in_tick = bit_reader.read_bits(32)?; let time_scale = bit_reader.read_bits(32)?;
if num_units_in_tick == 0 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"vui_num_units_in_tick cannot be zero",
));
}
frame_rate = time_scale as f64 / num_units_in_tick as f64;
}
}
Ok(Sps {
width,
height,
frame_rate,
color_config,
})
}
}