use super::rbsp::{unescape_rbsp, BitReader};
#[derive(Debug, Clone, Copy)]
pub(super) struct SpsSummary {
pub(super) sps_id: u32,
pub(super) chroma_format_idc: u8,
pub(super) bit_depth_luma: u8,
pub(super) bit_depth_chroma: u8,
pub(super) pic_width_in_mbs: u32,
pub(super) pic_height_in_map_units: u32,
}
impl SpsSummary {
pub(super) fn chroma_info(&self) -> (u8, u8, u8) {
(self.chroma_format_idc, self.bit_depth_luma, self.bit_depth_chroma)
}
}
pub(super) fn parse_sps(sps: &[u8]) -> Result<SpsSummary, String> {
let rbsp = unescape_rbsp(&sps[1..]);
let mut r = BitReader::new(&rbsp);
let profile_idc = r.bits(8)? as u8;
r.bits(6)?; let reserved_zero_2bits = r.bits(2)?;
if reserved_zero_2bits != 0 {
return Err(format!(
"reserved_zero_2bits is {reserved_zero_2bits} (7.4.2.1.1 requires 0)"
));
}
r.bits(8)?; let sps_id = r.ue()?;
if sps_id > 31 {
return Err(format!("seq_parameter_set_id {sps_id} exceeds 31"));
}
let chroma_info = match profile_idc {
100 | 110 | 122 | 244 | 44 | 83 | 86 | 118 | 128 | 138 | 139 | 134 | 135 | 144 => {
let chroma_format_idc = r.ue()?;
if chroma_format_idc > 3 {
return Err(format!("invalid chroma_format_idc {chroma_format_idc}"));
}
if chroma_format_idc == 3 {
r.bits(1)?; }
let bit_depth_luma_minus8 = r.ue()?;
let bit_depth_chroma_minus8 = r.ue()?;
if bit_depth_luma_minus8 > 6 || bit_depth_chroma_minus8 > 6 {
return Err(format!(
"invalid SPS bit depth (bit_depth_luma_minus8 {bit_depth_luma_minus8}, \
bit_depth_chroma_minus8 {bit_depth_chroma_minus8}; both must be <= 6)"
));
}
r.bits(1)?; if r.bits(1)? == 1 {
let lists = if chroma_format_idc != 3 { 8 } else { 12 };
for i in 0..lists {
if r.bits(1)? == 1 {
skip_scaling_list(&mut r, if i < 6 { 16 } else { 64 })?;
}
}
}
(
chroma_format_idc as u8,
bit_depth_luma_minus8 as u8 + 8,
bit_depth_chroma_minus8 as u8 + 8,
)
}
_ => (1, 8, 8),
};
let (chroma_format_idc, bit_depth_luma, bit_depth_chroma) = chroma_info;
let log2_max_frame_num_minus4 = r.ue()?;
if log2_max_frame_num_minus4 > 12 {
return Err(format!(
"log2_max_frame_num_minus4 {log2_max_frame_num_minus4} out of range (0..=12)"
));
}
let pic_order_cnt_type = r.ue()?;
match pic_order_cnt_type {
0 => {
let log2_max_poc_lsb_minus4 = r.ue()?;
if log2_max_poc_lsb_minus4 > 12 {
return Err(format!(
"log2_max_pic_order_cnt_lsb_minus4 {log2_max_poc_lsb_minus4} \
out of range (0..=12)"
));
}
}
1 => {
r.bits(1)?; r.se()?; r.se()?; let cycle_len = r.ue()?;
if cycle_len >= 256 {
return Err(format!(
"num_ref_frames_in_pic_order_cnt_cycle {cycle_len} exceeds 255"
));
}
for _ in 0..cycle_len {
r.se()?; }
}
2 => {}
other => return Err(format!("pic_order_cnt_type {other} (must be <= 2)")),
}
let max_num_ref_frames = r.ue()?;
if max_num_ref_frames > 16 {
return Err(format!(
"max_num_ref_frames {max_num_ref_frames} exceeds 16"
));
}
r.bits(1)?; let pic_width_in_mbs = r.ue()? as u64 + 1; let pic_height_in_map_units = r.ue()? as u64 + 1; let frame_mbs_only = r.bits(1)? == 1;
if !frame_mbs_only {
r.bits(1)?; }
let width = 16 * pic_width_in_mbs;
let height = 16 * pic_height_in_map_units * if frame_mbs_only { 1 } else { 2 };
if width > 65535 || height > 65535 {
return Err(format!(
"coded picture size {width}x{height} exceeds 65535 on an axis"
));
}
if (8 * width + 1024) * (height + 128) >= 1 << 31 {
return Err(format!(
"coded picture size {width}x{height} overflows the sample buffer bound"
));
}
r.bits(1)?; if r.bits(1)? == 1 {
let crop_left = r.ue()? as u64;
let crop_right = r.ue()? as u64;
let crop_top = r.ue()? as u64;
let crop_bottom = r.ue()? as u64;
let step_x: u64 = if chroma_format_idc == 1 || chroma_format_idc == 2 { 2 } else { 1 };
let step_y: u64 = (if chroma_format_idc == 1 { 2 } else { 1 })
* (if frame_mbs_only { 1 } else { 2 });
if (crop_left + crop_right) * step_x >= width
|| (crop_top + crop_bottom) * step_y >= height
{
return Err(format!(
"frame cropping {crop_left}/{crop_right}/{crop_top}/{crop_bottom} \
removes the whole {width}x{height} picture"
));
}
}
if r.bits(1)? == 1 {
skip_vui(&mut r, max_num_ref_frames)?;
}
r.finish_rbsp()?;
Ok(SpsSummary {
sps_id,
chroma_format_idc,
bit_depth_luma,
bit_depth_chroma,
pic_width_in_mbs: pic_width_in_mbs as u32,
pic_height_in_map_units: pic_height_in_map_units as u32,
})
}
pub(super) fn skip_scaling_list(r: &mut BitReader, size: u32) -> Result<(), String> {
let mut last_scale: i64 = 8;
let mut next_scale: i64 = 8;
for _ in 0..size {
if next_scale != 0 {
let delta_scale = r.se()?;
if !(-128..=127).contains(&delta_scale) {
return Err(format!("delta_scale {delta_scale} outside [-128, 127]"));
}
next_scale = (last_scale + delta_scale + 256).rem_euclid(256);
}
if next_scale != 0 {
last_scale = next_scale;
}
}
Ok(())
}
fn skip_vui(r: &mut BitReader, max_num_ref_frames: u32) -> Result<(), String> {
if r.bits(1)? == 1 {
if r.bits(8)? == 255 {
r.bits(16)?; r.bits(16)?; }
}
if r.bits(1)? == 1 {
r.bits(1)?; }
if r.bits(1)? == 1 {
r.bits(3)?; r.bits(1)?; if r.bits(1)? == 1 {
r.bits(24)?;
}
}
if r.bits(1)? == 1 {
let top = r.ue()?; let bottom = r.ue()?; if top > 5 || bottom > 5 {
return Err(format!(
"chroma_sample_loc_type {top}/{bottom} exceeds 5"
));
}
}
if r.bits(1)? == 1 {
let num_units_in_tick = r.bits(32)?;
let time_scale = r.bits(32)?;
if num_units_in_tick == 0 || time_scale == 0 {
return Err(format!(
"timing_info {num_units_in_tick}/{time_scale} carries a zero field"
));
}
r.bits(1)?; }
let nal_hrd = r.bits(1)? == 1; if nal_hrd {
skip_hrd_parameters(r)?;
}
let vcl_hrd = r.bits(1)? == 1; if vcl_hrd {
skip_hrd_parameters(r)?;
}
if nal_hrd || vcl_hrd {
r.bits(1)?; }
r.bits(1)?; if r.bits(1)? == 1 {
r.bits(1)?; let max_bytes_per_pic_denom = r.ue()?;
let max_bits_per_mb_denom = r.ue()?;
let log2_max_mv_length_horizontal = r.ue()?;
let log2_max_mv_length_vertical = r.ue()?;
let max_num_reorder_frames = r.ue()?;
let max_dec_frame_buffering = r.ue()?;
if max_bytes_per_pic_denom > 16 {
return Err(format!(
"max_bytes_per_pic_denom {max_bytes_per_pic_denom} exceeds 16"
));
}
if max_bits_per_mb_denom > 16 {
return Err(format!(
"max_bits_per_mb_denom {max_bits_per_mb_denom} exceeds 16"
));
}
if log2_max_mv_length_horizontal > 16 || log2_max_mv_length_vertical > 16 {
return Err(format!(
"log2_max_mv_length {log2_max_mv_length_horizontal}/\
{log2_max_mv_length_vertical} exceeds 16"
));
}
if max_num_reorder_frames > 16 || max_num_reorder_frames > max_dec_frame_buffering {
return Err(format!(
"max_num_reorder_frames {max_num_reorder_frames} exceeds 16 or \
max_dec_frame_buffering {max_dec_frame_buffering}"
));
}
if max_dec_frame_buffering > 16 || max_dec_frame_buffering < max_num_ref_frames {
return Err(format!(
"max_dec_frame_buffering {max_dec_frame_buffering} is outside \
max_num_ref_frames {max_num_ref_frames}..=16"
));
}
}
Ok(())
}
fn skip_hrd_parameters(r: &mut BitReader) -> Result<(), String> {
let cpb_cnt_minus1 = r.ue()?;
if cpb_cnt_minus1 > 31 {
return Err(format!("cpb_cnt_minus1 {cpb_cnt_minus1} exceeds 31"));
}
r.bits(4)?; r.bits(4)?; for _ in 0..=cpb_cnt_minus1 {
r.ue()?; r.ue()?; r.bits(1)?; }
r.bits(5)?; r.bits(5)?; r.bits(5)?; r.bits(5)?; Ok(())
}