use crate::bitreader::BitReader;
use crate::error::{Error, Result};
use alloc::string::String;
use alloc::vec::Vec;
const H264_HIGH_PROFILES: &[u8] = &[100, 110, 122, 244, 44, 83, 86, 118, 128, 138, 139, 134, 135];
pub(crate) fn is_high_profile(profile_idc: u8) -> bool {
H264_HIGH_PROFILES.contains(&profile_idc)
}
fn ue_to_u8(v: u64, spec_max: u64, field: &'static str) -> Result<u8> {
if v > spec_max {
return Err(Error::InvalidValue {
field,
value: v,
reason: "out of spec range",
});
}
u8::try_from(v).map_err(|_| Error::InvalidValue {
field,
value: v,
reason: "does not fit in u8",
})
}
fn ue_bit_depth(minus8: u64, spec_max: u64, field: &'static str) -> Result<u8> {
let minus8 = ue_to_u8(minus8, spec_max, field)?;
minus8.checked_add(8).ok_or(Error::InvalidValue {
field,
value: minus8 as u64,
reason: "bit depth overflow",
})
}
fn sub_width_c(chroma_format_idc: u8) -> u32 {
match chroma_format_idc {
0 => 1,
1 => 2,
2 => 2,
_ => 1,
}
}
fn sub_height_c(chroma_format_idc: u8) -> u32 {
match chroma_format_idc {
0 => 1,
1 => 2,
2 => 1,
_ => 1,
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct AvcSpsInfo {
pub profile_idc: u8,
pub constraint_flags: u8,
pub level_idc: u8,
pub chroma_format_idc: u8,
pub separate_colour_plane: bool,
pub bit_depth_luma: u8,
pub bit_depth_chroma: u8,
pub frame_mbs_only: bool,
pub width: u32,
pub height: u32,
pub num_units_in_tick: Option<u32>,
pub time_scale: Option<u32>,
pub fps: Option<f32>,
}
pub fn decode_avc_sps(sps_bytes: &[u8]) -> Result<AvcSpsInfo> {
decode_avc_sps_inner(sps_bytes).map(|(info, _)| info)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AvcSliceSps {
pub sps_id: u8,
pub separate_colour_plane: bool,
pub log2_max_frame_num: u8,
pub pic_order_cnt_type: u8,
pub log2_max_poc_lsb: u8,
pub delta_pic_order_always_zero: bool,
pub frame_mbs_only: bool,
pub mb_adaptive_frame_field: bool,
pub pic_timing: Option<AvcPicTimingLayout>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AvcPicTimingLayout {
pub delay_bits: Option<(u8, u8)>,
}
pub fn decode_avc_slice_sps(sps_bytes: &[u8]) -> Result<AvcSliceSps> {
decode_avc_sps_inner(sps_bytes).map(|(_, s)| s)
}
fn decode_avc_sps_inner(sps_bytes: &[u8]) -> Result<(AvcSpsInfo, AvcSliceSps)> {
if sps_bytes.len() < 4 {
return Err(Error::BufferTooShort {
need: 4,
have: sps_bytes.len(),
what: "H.264 SPS header",
});
}
let profile_idc = sps_bytes[1];
let constraint_flags = sps_bytes[2];
let level_idc = sps_bytes[3];
let mut r = BitReader::with_unescape(&sps_bytes[4..], "H.264 SPS")?;
let sps_id = r.read_ue("seq_parameter_set_id")?.min(u8::MAX as u64) as u8;
let (chroma_format_idc, separate_colour_plane, bit_depth_luma, bit_depth_chroma) =
if is_high_profile(profile_idc) {
let chroma_format_idc =
ue_to_u8(r.read_ue("chroma_format_idc")?, 3, "chroma_format_idc")?;
let separate_colour_plane = if chroma_format_idc == 3 {
r.read_flag("separate_colour_plane_flag")?
} else {
false
};
let bit_depth_luma = ue_bit_depth(
r.read_ue("bit_depth_luma_minus8")?,
6,
"bit_depth_luma_minus8",
)?;
let bit_depth_chroma = ue_bit_depth(
r.read_ue("bit_depth_chroma_minus8")?,
6,
"bit_depth_chroma_minus8",
)?;
let _ = r.read_flag("qpprime_y_zero_transform_bypass_flag")?;
let scaling_matrix_present = r.read_flag("seq_scaling_matrix_present_flag")?;
if scaling_matrix_present {
let list_count = if chroma_format_idc != 3 { 8 } else { 12 };
for _i in 0..list_count {
let list_present = r.read_flag("seq_scaling_list_present_flag[i]")?;
if list_present {
let size: usize = if _i < 6 { 16 } else { 64 };
let mut last_scale: i64 = 8;
let mut next_scale: i64 = 8;
for _j in 0..size {
if next_scale != 0 {
let delta_scale = r.read_se("delta_scale")?;
next_scale = (last_scale + delta_scale + 256) % 256;
}
if next_scale == 0 {
} else {
last_scale = next_scale;
}
}
}
}
}
(
chroma_format_idc,
separate_colour_plane,
bit_depth_luma,
bit_depth_chroma,
)
} else {
(1, false, 8, 8)
};
let log2_max_frame_num = r.read_ue("log2_max_frame_num_minus4")?.min(12) as u8 + 4;
let pic_order_cnt_type = r.read_ue("pic_order_cnt_type")?;
let mut log2_max_poc_lsb = 0u8;
let mut delta_pic_order_always_zero = false;
if pic_order_cnt_type == 0 {
log2_max_poc_lsb = r.read_ue("log2_max_pic_order_cnt_lsb_minus4")?.min(12) as u8 + 4;
} else if pic_order_cnt_type == 1 {
delta_pic_order_always_zero = r.read_flag("delta_pic_order_always_zero_flag")?;
let _ = r.read_se("offset_for_non_ref_pic")?;
let _ = r.read_se("offset_for_top_to_bottom_field")?;
let num_ref_frames = r.read_ue("num_ref_frames_in_pic_order_cnt_cycle")?;
if num_ref_frames > 255 {
return Err(Error::InvalidValue {
field: "num_ref_frames_in_pic_order_cnt_cycle",
value: num_ref_frames,
reason: "out of range 0..=255 (H.264 §7.4.2.1)",
});
}
for _ in 0..num_ref_frames {
let _ = r.read_se("offset_for_ref_frame[i]")?;
}
}
let _ = r.read_ue("max_num_ref_frames")?;
let _ = r.read_flag("gaps_in_frame_num_value_allowed_flag")?;
let pic_width_in_mbs_minus1 = r.read_ue("pic_width_in_mbs_minus1")?;
let pic_height_in_map_units_minus1 = r.read_ue("pic_height_in_map_units_minus1")?;
let frame_mbs_only = r.read_flag("frame_mbs_only_flag")?;
let mut mb_adaptive_frame_field = false;
if !frame_mbs_only {
mb_adaptive_frame_field = r.read_flag("mb_adaptive_frame_field_flag")?;
}
let _ = r.read_flag("direct_8x8_inference_flag")?;
let frame_cropping = r.read_flag("frame_cropping_flag")?;
let (crop_left, crop_right, crop_top, crop_bottom) = if frame_cropping {
let cl = r.read_ue("frame_crop_left_offset")?;
let cr = r.read_ue("frame_crop_right_offset")?;
let ct = r.read_ue("frame_crop_top_offset")?;
let cb = r.read_ue("frame_crop_bottom_offset")?;
(cl, cr, ct, cb)
} else {
(0, 0, 0, 0)
};
let pic_width_in_mbs = pic_width_in_mbs_minus1 + 1;
let pic_height_in_map_units = pic_height_in_map_units_minus1 + 1;
let frame_height_in_mbs = (2 - frame_mbs_only as u64) * pic_height_in_map_units;
let crop_unit_x = sub_width_c(chroma_format_idc) as u64;
let crop_unit_y = sub_height_c(chroma_format_idc) as u64 * (2 - frame_mbs_only as u64);
let width = (pic_width_in_mbs * 16).saturating_sub(crop_unit_x * (crop_left + crop_right));
let height = (frame_height_in_mbs * 16).saturating_sub(crop_unit_y * (crop_top + crop_bottom));
let vui = parse_avc_vui(&mut r);
let (num_units_in_tick, time_scale, fps) = vui.timing;
Ok((
AvcSpsInfo {
profile_idc,
constraint_flags,
level_idc,
chroma_format_idc,
separate_colour_plane,
bit_depth_luma,
bit_depth_chroma,
frame_mbs_only,
width: width as u32,
height: height as u32,
num_units_in_tick,
time_scale,
fps,
},
AvcSliceSps {
sps_id,
separate_colour_plane,
log2_max_frame_num,
pic_order_cnt_type: pic_order_cnt_type.min(u8::MAX as u64) as u8,
log2_max_poc_lsb,
delta_pic_order_always_zero,
frame_mbs_only,
mb_adaptive_frame_field,
pic_timing: vui.pic_timing,
},
))
}
#[derive(Default)]
struct AvcVuiFacts {
timing: (Option<u32>, Option<u32>, Option<f32>),
pic_timing: Option<AvcPicTimingLayout>,
}
fn parse_avc_vui(r: &mut BitReader) -> AvcVuiFacts {
let mut facts = AvcVuiFacts::default();
let _ = parse_avc_vui_inner(r, &mut facts);
facts
}
const HRD_LENGTH_FIELD_BITS: usize = 5;
const HRD_MAX_CPB_CNT_MINUS1: u64 = 31;
fn parse_avc_hrd(r: &mut BitReader) -> crate::error::Result<(u8, u8)> {
let cpb_cnt_minus1 = r.read_ue("cpb_cnt_minus1")?;
if cpb_cnt_minus1 > HRD_MAX_CPB_CNT_MINUS1 {
return Err(Error::InvalidValue {
field: "cpb_cnt_minus1",
value: cpb_cnt_minus1,
reason: "out of range 0..=31 (H.264 §E.2.2)",
});
}
let _ = r.read_bits(4, "bit_rate_scale")?;
let _ = r.read_bits(4, "cpb_size_scale")?;
for _ in 0..=cpb_cnt_minus1 {
let _ = r.read_ue("bit_rate_value_minus1")?;
let _ = r.read_ue("cpb_size_value_minus1")?;
let _ = r.read_flag("cbr_flag")?;
}
let _ = r.read_bits(
HRD_LENGTH_FIELD_BITS,
"initial_cpb_removal_delay_length_minus1",
)?;
let cpb = r.read_bits(HRD_LENGTH_FIELD_BITS, "cpb_removal_delay_length_minus1")? as u8 + 1;
let dpb = r.read_bits(HRD_LENGTH_FIELD_BITS, "dpb_output_delay_length_minus1")? as u8 + 1;
let _ = r.read_bits(HRD_LENGTH_FIELD_BITS, "time_offset_length")?;
Ok((cpb, dpb))
}
fn parse_avc_vui_inner(r: &mut BitReader, facts: &mut AvcVuiFacts) -> crate::error::Result<()> {
let vui_present = r.read_flag("vui_parameters_present_flag")?;
if !vui_present {
return Ok(());
}
let ari_present = r.read_flag("aspect_ratio_info_present_flag")?;
if ari_present {
let aspect_ratio_idc = r.read_bits(8, "aspect_ratio_idc")? as u8;
if aspect_ratio_idc == 255 {
let _ = r.read_bits(16, "sar_width")?;
let _ = r.read_bits(16, "sar_height")?;
}
}
let overscan_present = r.read_flag("overscan_info_present_flag")?;
if overscan_present {
let _ = r.read_flag("overscan_appropriate_flag")?;
}
let vst_present = r.read_flag("video_signal_type_present_flag")?;
if vst_present {
let _ = r.read_bits(3, "video_format")?;
let _ = r.read_flag("video_full_range_flag")?;
let colour_desc = r.read_flag("colour_description_present_flag")?;
if colour_desc {
let _ = r.read_bits(8, "colour_primaries")?;
let _ = r.read_bits(8, "transfer_characteristics")?;
let _ = r.read_bits(8, "matrix_coefficients")?;
}
}
let cli_present = r.read_flag("chroma_loc_info_present_flag")?;
if cli_present {
let _ = r.read_ue("chroma_sample_loc_type_top_field")?;
let _ = r.read_ue("chroma_sample_loc_type_bottom_field")?;
}
let timing_present = r.read_flag("timing_info_present_flag")?;
if timing_present {
let num_units = r.read_bits(32, "num_units_in_tick")? as u32;
let ts = r.read_bits(32, "time_scale")? as u32;
let _ = r.read_flag("fixed_frame_rate_flag")?;
let fps = if num_units > 0 {
Some(ts as f32 / (2.0 * num_units as f32))
} else {
None
};
facts.timing = (Some(num_units), Some(ts), fps);
}
let nal_hrd = if r.read_flag("nal_hrd_parameters_present_flag")? {
Some(parse_avc_hrd(r)?)
} else {
None
};
let vcl_hrd = if r.read_flag("vcl_hrd_parameters_present_flag")? {
Some(parse_avc_hrd(r)?)
} else {
None
};
if nal_hrd.is_some() || vcl_hrd.is_some() {
let _ = r.read_flag("low_delay_hrd_flag")?;
}
let pic_struct_present = r.read_flag("pic_struct_present_flag")?;
if pic_struct_present {
let delay_bits = match (nal_hrd, vcl_hrd) {
(Some(a), Some(b)) if a != b => return Ok(()),
(Some(a), _) | (None, Some(a)) => Some(a),
(None, None) => None,
};
facts.pic_timing = Some(AvcPicTimingLayout { delay_bits });
}
Ok(())
}
#[derive(Debug, Clone, PartialEq)]
pub struct HevcSpsInfo {
pub general_profile_space: u8,
pub general_tier_flag: bool,
pub general_profile_idc: u8,
pub general_profile_compatibility_flags: u32,
pub general_constraint_indicator_flags: u64,
pub general_level_idc: u8,
pub chroma_format_idc: u8,
pub bit_depth_luma: u8,
pub bit_depth_chroma: u8,
pub width: u32,
pub height: u32,
pub num_units_in_tick: Option<u32>,
pub time_scale: Option<u32>,
pub fps: Option<f32>,
}
pub fn decode_hevc_sps(sps_bytes: &[u8]) -> Result<HevcSpsInfo> {
decode_hevc_sps_inner(sps_bytes).map(|(info, _)| info)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct HevcInterlaceSps {
pub field_seq: bool,
pub frame_field_info_present: bool,
}
pub fn decode_hevc_interlace_sps(sps_bytes: &[u8]) -> Result<HevcInterlaceSps> {
decode_hevc_sps_inner(sps_bytes).map(|(_, i)| i)
}
const PTL_PROGRESSIVE_SOURCE_BIT: u64 = 1 << 47;
const PTL_INTERLACED_SOURCE_BIT: u64 = 1 << 46;
fn decode_hevc_sps_inner(sps_bytes: &[u8]) -> Result<(HevcSpsInfo, HevcInterlaceSps)> {
if sps_bytes.len() < 2 {
return Err(Error::BufferTooShort {
need: 2,
have: sps_bytes.len(),
what: "HEVC SPS header",
});
}
let mut r = BitReader::with_unescape(&sps_bytes[2..], "HEVC SPS")?;
let _ = r.read_bits(4, "sps_video_parameter_set_id")?;
let sps_max_sub_layers_minus1 = r.read_bits(3, "sps_max_sub_layers_minus1")? as u8;
let _ = r.read_flag("sps_temporal_id_nesting_flag")?;
let (
general_profile_space,
general_tier_flag,
general_profile_idc,
general_profile_compatibility_flags,
general_constraint_indicator_flags,
general_level_idc,
) = decode_ptl(&mut r, true, sps_max_sub_layers_minus1)?;
let _ = r.read_ue("sps_seq_parameter_set_id")?;
let chroma_format_idc = ue_to_u8(r.read_ue("chroma_format_idc")?, 3, "chroma_format_idc")?;
if chroma_format_idc == 3 {
let _ = r.read_flag("separate_colour_plane_flag")?;
}
let pic_width_in_luma_samples = r.read_ue("pic_width_in_luma_samples")?;
let pic_height_in_luma_samples = r.read_ue("pic_height_in_luma_samples")?;
let conformance_window = r.read_flag("conformance_window_flag")?;
let (conf_win_left, conf_win_right, conf_win_top, conf_win_bottom) = if conformance_window {
let left = r.read_ue("conf_win_left_offset")?;
let right = r.read_ue("conf_win_right_offset")?;
let top = r.read_ue("conf_win_top_offset")?;
let bottom = r.read_ue("conf_win_bottom_offset")?;
(left, right, top, bottom)
} else {
(0, 0, 0, 0)
};
let bit_depth_luma = ue_bit_depth(
r.read_ue("bit_depth_luma_minus8")?,
8,
"bit_depth_luma_minus8",
)?;
let bit_depth_chroma = ue_bit_depth(
r.read_ue("bit_depth_chroma_minus8")?,
8,
"bit_depth_chroma_minus8",
)?;
let sub_width_c = match chroma_format_idc {
1 | 2 => 2,
3 => 1,
_ => 1,
};
let sub_height_c = match chroma_format_idc {
1 => 2,
2 | 3 => 1,
_ => 1,
};
let width = pic_width_in_luma_samples
.saturating_sub((sub_width_c as u64) * (conf_win_left + conf_win_right));
let height = pic_height_in_luma_samples
.saturating_sub((sub_height_c as u64) * (conf_win_top + conf_win_bottom));
let mut interlace = HevcInterlaceSps {
field_seq: false,
frame_field_info_present: general_constraint_indicator_flags & PTL_PROGRESSIVE_SOURCE_BIT
!= 0
&& general_constraint_indicator_flags & PTL_INTERLACED_SOURCE_BIT != 0,
};
let (num_units_in_tick, time_scale, fps) =
parse_hevc_sps_to_vui_timing(&mut r, sps_max_sub_layers_minus1, &mut interlace);
Ok((
HevcSpsInfo {
general_profile_space,
general_tier_flag,
general_profile_idc,
general_profile_compatibility_flags,
general_constraint_indicator_flags,
general_level_idc,
chroma_format_idc,
bit_depth_luma,
bit_depth_chroma,
width: width as u32,
height: height as u32,
num_units_in_tick,
time_scale,
fps,
},
interlace,
))
}
fn parse_hevc_sps_to_vui_timing(
r: &mut BitReader,
sps_max_sub_layers_minus1: u8,
interlace: &mut HevcInterlaceSps,
) -> (Option<u32>, Option<u32>, Option<f32>) {
parse_hevc_sps_to_vui_timing_inner(r, sps_max_sub_layers_minus1, interlace)
.unwrap_or((None, None, None))
}
const HEVC_MAX_RPS_PICS: u64 = 16;
fn parse_hevc_sps_to_vui_timing_inner(
r: &mut BitReader,
sps_max_sub_layers_minus1: u8,
interlace: &mut HevcInterlaceSps,
) -> crate::error::Result<(Option<u32>, Option<u32>, Option<f32>)> {
let log2_max_poc_lsb = r.read_ue("log2_max_pic_order_cnt_lsb_minus4")?;
let sub_layer_ordering = r.read_flag("sps_sub_layer_ordering_info_present_flag")?;
let start_layer = if sub_layer_ordering {
0
} else {
sps_max_sub_layers_minus1
};
for _i in start_layer..=sps_max_sub_layers_minus1 {
let _ = r.read_ue("sps_max_dec_pic_buffering_minus1")?;
let _ = r.read_ue("sps_max_num_reorder_pics")?;
let _ = r.read_ue("sps_max_latency_increase_plus1")?;
}
let _ = r.read_ue("log2_min_luma_coding_block_size_minus3")?;
let _ = r.read_ue("log2_diff_max_min_luma_coding_block_size")?;
let _ = r.read_ue("log2_min_luma_transform_block_size_minus2")?;
let _ = r.read_ue("log2_diff_max_min_luma_transform_block_size")?;
let _ = r.read_ue("max_transform_hierarchy_depth_inter")?;
let _ = r.read_ue("max_transform_hierarchy_depth_intra")?;
let scaling_list_enabled = r.read_flag("scaling_list_enabled_flag")?;
if scaling_list_enabled {
let sps_scaling_list_data_present = r.read_flag("sps_scaling_list_data_present_flag")?;
if sps_scaling_list_data_present {
for size_id in 0u32..4 {
let matrix_count = if size_id == 3 { 2 } else { 6 };
for _matrix_id in 0..matrix_count {
let pred_mode = r.read_flag("scaling_list_pred_mode_flag")?;
if !pred_mode {
let _ = r.read_ue("scaling_list_pred_matrix_id_delta")?;
} else {
let coef_num = core::cmp::min(64u32, 1 << (4 + (size_id << 1)));
if size_id > 1 {
let _ = r.read_ue("scaling_list_dc_coef_minus8")?;
}
for _i in 0..coef_num {
let _ = r.read_ue("scaling_list_delta_coef")?;
}
}
}
}
}
}
let _ = r.read_flag("amp_enabled_flag")?;
let _ = r.read_flag("sample_adaptive_offset_enabled_flag")?;
let pcm_enabled = r.read_flag("pcm_enabled_flag")?;
if pcm_enabled {
let _ = r.read_bits(4, "pcm_sample_bit_depth_luma_minus1")?;
let _ = r.read_bits(4, "pcm_sample_bit_depth_chroma_minus1")?;
let _ = r.read_ue("log2_min_pcm_luma_coding_block_size_minus3")?;
let _ = r.read_ue("log2_diff_max_min_pcm_luma_coding_block_size")?;
let _ = r.read_flag("pcm_loop_filter_disabled_flag")?;
}
let num_short_term = r.read_ue("num_short_term_ref_pic_sets")?;
if num_short_term > 64 {
return Err(Error::InvalidValue {
field: "num_short_term_ref_pic_sets",
value: num_short_term,
reason: "out of range 0..=64 (H.265 §7.4.3.2)",
});
}
let mut rps_sets: Vec<(Vec<i64>, Vec<i64>)> = Vec::new();
for i in 0..num_short_term {
let inter_ref = if i != 0 {
r.read_flag("inter_ref_pic_set_prediction_flag")?
} else {
false
};
let set = if inter_ref {
let (ref_s0, ref_s1) = &rps_sets[rps_sets.len() - 1];
let delta_rps_sign = r.read_flag("delta_rps_sign")?;
let abs_delta_rps_minus1 = r.read_ue("abs_delta_rps_minus1")?;
if abs_delta_rps_minus1 >= 1 << 15 {
return Err(Error::InvalidValue {
field: "abs_delta_rps_minus1",
value: abs_delta_rps_minus1,
reason: "out of range 0..=2^15-1 (H.265 §7.4.8)",
});
}
let magnitude = abs_delta_rps_minus1 as i64 + 1;
let delta_rps = if delta_rps_sign {
-magnitude
} else {
magnitude
};
let num_delta_pocs = ref_s0.len() + ref_s1.len();
let mut use_delta = Vec::with_capacity(num_delta_pocs + 1);
for _j in 0..=num_delta_pocs {
let used_by_curr = r.read_flag("used_by_curr_pic_flag")?;
use_delta.push(if used_by_curr {
true
} else {
r.read_flag("use_delta_flag")?
});
}
let (n0, n1) = (ref_s0.len(), ref_s1.len());
let mut s0: Vec<i64> = Vec::new();
let mut s1: Vec<i64> = Vec::new();
for j in (0..n1).rev() {
let d = ref_s1[j] + delta_rps;
if d < 0 && use_delta[n0 + j] {
s0.push(d);
}
}
if delta_rps < 0 && use_delta[num_delta_pocs] {
s0.push(delta_rps);
}
for j in 0..n0 {
let d = ref_s0[j] + delta_rps;
if d < 0 && use_delta[j] {
s0.push(d);
}
}
for j in (0..n0).rev() {
let d = ref_s0[j] + delta_rps;
if d > 0 && use_delta[j] {
s1.push(d);
}
}
if delta_rps > 0 && use_delta[num_delta_pocs] {
s1.push(delta_rps);
}
for j in 0..n1 {
let d = ref_s1[j] + delta_rps;
if d > 0 && use_delta[n0 + j] {
s1.push(d);
}
}
(s0, s1)
} else {
let num_negative = r.read_ue("num_negative_pics")?;
let num_positive = r.read_ue("num_positive_pics")?;
if num_negative > HEVC_MAX_RPS_PICS || num_positive > HEVC_MAX_RPS_PICS {
return Err(Error::InvalidValue {
field: "num_negative_pics/num_positive_pics",
value: num_negative.max(num_positive),
reason: "out of range 0..=16 (H.265 §7.4.8)",
});
}
let mut s0 = Vec::new();
let mut acc = 0i64;
for _j in 0..num_negative {
acc -= r.read_ue("delta_poc_s0_minus1")? as i64 + 1;
let _ = r.read_flag("used_by_curr_pic_s0_flag")?;
s0.push(acc);
}
let mut s1 = Vec::new();
let mut acc = 0i64;
for _j in 0..num_positive {
acc += r.read_ue("delta_poc_s1_minus1")? as i64 + 1;
let _ = r.read_flag("used_by_curr_pic_s1_flag")?;
s1.push(acc);
}
(s0, s1)
};
if set.0.len() > HEVC_MAX_RPS_PICS as usize || set.1.len() > HEVC_MAX_RPS_PICS as usize {
return Err(Error::InvalidValue {
field: "NumNegativePics/NumPositivePics",
value: set.0.len().max(set.1.len()) as u64,
reason: "derived RPS larger than 16 entries (H.265 §7.4.8)",
});
}
rps_sets.push(set);
}
let ltrp_present = r.read_flag("long_term_ref_pics_present_flag")?;
if ltrp_present {
let num_lt = r.read_ue("num_long_term_ref_pics_sps")?;
let poc_bits = log2_max_poc_lsb + 4;
for _i in 0..num_lt {
let _ = r.read_bits(poc_bits as usize, "lt_ref_pic_poc_lsb_sps")?;
let _ = r.read_flag("used_by_curr_pic_lt_sps_flag")?;
}
}
let _ = r.read_flag("sps_temporal_mvp_enabled_flag")?;
let _ = r.read_flag("strong_intra_smoothing_enabled_flag")?;
let vui_present = r.read_flag("vui_parameters_present_flag")?;
if !vui_present {
return Ok((None, None, None));
}
let ari_present = r.read_flag("aspect_ratio_info_present_flag")?;
if ari_present {
let aspect_ratio_idc = r.read_bits(8, "aspect_ratio_idc")? as u8;
if aspect_ratio_idc == 255 {
let _ = r.read_bits(16, "sar_width")?;
let _ = r.read_bits(16, "sar_height")?;
}
}
let overscan_present = r.read_flag("overscan_info_present_flag")?;
if overscan_present {
let _ = r.read_flag("overscan_appropriate_flag")?;
}
let vst_present = r.read_flag("video_signal_type_present_flag")?;
if vst_present {
let _ = r.read_bits(3, "video_format")?;
let _ = r.read_flag("video_full_range_flag")?;
let colour_desc = r.read_flag("colour_description_present_flag")?;
if colour_desc {
let _ = r.read_bits(8, "colour_primaries")?;
let _ = r.read_bits(8, "transfer_characteristics")?;
let _ = r.read_bits(8, "matrix_coefficients")?;
}
}
let cli_present = r.read_flag("chroma_loc_info_present_flag")?;
if cli_present {
let _ = r.read_ue("chroma_sample_loc_type_top_field")?;
let _ = r.read_ue("chroma_sample_loc_type_bottom_field")?;
}
let _ = r.read_flag("neutral_chroma_indication_flag")?;
interlace.field_seq = r.read_flag("field_seq_flag")?;
interlace.frame_field_info_present = r.read_flag("frame_field_info_present_flag")?;
let ddw_present = r.read_flag("default_display_window_flag")?;
if ddw_present {
let _ = r.read_ue("def_disp_win_left_offset")?;
let _ = r.read_ue("def_disp_win_right_offset")?;
let _ = r.read_ue("def_disp_win_top_offset")?;
let _ = r.read_ue("def_disp_win_bottom_offset")?;
}
let timing_present = r.read_flag("vui_timing_info_present_flag")?;
if !timing_present {
return Ok((None, None, None));
}
let num_units = r.read_bits(32, "vui_num_units_in_tick")? as u32;
let ts = r.read_bits(32, "vui_time_scale")? as u32;
let fps = if num_units > 0 {
Some(ts as f32 / num_units as f32)
} else {
None
};
Ok((Some(num_units), Some(ts), fps))
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VvcSpsInfo {
pub chroma_format_idc: u8,
pub general_profile_idc: u8,
pub general_tier_flag: bool,
pub general_level_idc: u8,
pub width: u32,
pub height: u32,
}
pub fn decode_vvc_sps(sps_bytes: &[u8]) -> Result<VvcSpsInfo> {
if sps_bytes.len() < 2 {
return Err(Error::BufferTooShort {
need: 2,
have: sps_bytes.len(),
what: "VVC SPS header",
});
}
let mut r = BitReader::with_unescape(&sps_bytes[2..], "VVC SPS")?;
let _ = r.read_bits(4, "sps_seq_parameter_set_id")?;
let _ = r.read_bits(4, "sps_video_parameter_set_id")?;
let sps_max_sublayers_minus1 = r.read_bits(3, "sps_max_sublayers_minus1")? as u8;
let chroma_format_idc = r.read_bits(2, "sps_chroma_format_idc")? as u8;
let _ = r.read_bits(2, "sps_log2_ctu_size_minus5")?;
let ptl_present = r.read_flag("sps_ptl_dpb_hrd_params_present_flag")?;
let (general_profile_idc, general_tier_flag, general_level_idc) = if ptl_present {
decode_vvc_ptl(&mut r, sps_max_sublayers_minus1)?
} else {
(0, false, 0)
};
let _ = r.read_flag("sps_gdr_enabled_flag")?;
let ref_pic_resampling = r.read_flag("sps_ref_pic_resampling_enabled_flag")?;
if ref_pic_resampling {
let _ = r.read_flag("sps_res_change_in_clvs_allowed_flag")?;
}
let width = r.read_ue("sps_pic_width_max_in_luma_samples")?;
let height = r.read_ue("sps_pic_height_max_in_luma_samples")?;
Ok(VvcSpsInfo {
chroma_format_idc,
general_profile_idc,
general_tier_flag,
general_level_idc,
width: width as u32,
height: height as u32,
})
}
fn decode_vvc_ptl(r: &mut BitReader, max_sublayers_minus1: u8) -> Result<(u8, bool, u8)> {
let general_profile_idc = r.read_bits(7, "general_profile_idc")? as u8;
let general_tier_flag = r.read_flag("general_tier_flag")?;
let general_level_idc = r.read_bits(8, "general_level_idc")? as u8;
let _ = r.read_flag("ptl_frame_only_constraint_flag")?;
let _ = r.read_flag("ptl_multilayer_enabled_flag")?;
skip_general_constraints_info(r)?;
let mut sublayer_present = [false; 8];
for i in (0..max_sublayers_minus1).rev() {
sublayer_present[i as usize] = r.read_flag("ptl_sublayer_level_present_flag[i]")?;
}
if max_sublayers_minus1 > 0 {
r.align_to_byte("ptl_reserved_zero_bit")?;
}
for i in (0..max_sublayers_minus1).rev() {
if sublayer_present[i as usize] {
let _ = r.read_bits(8, "sublayer_level_idc[i]")?;
}
}
let num_sub_profiles = r.read_bits(8, "ptl_num_sub_profiles")?;
for _ in 0..num_sub_profiles {
let _ = r.read_bits(32, "general_sub_profile_idc[j]")?;
}
Ok((general_profile_idc, general_tier_flag, general_level_idc))
}
const GCI_FLAG_BLOCK_BITS: [usize; 6] = [3, 4, 2, 16, 2, 43];
const GCI_ADDITIONAL_FLAG_BITS: usize = 6;
const GCI_ADDITIONAL_THRESHOLD: u64 = 5;
fn skip_general_constraints_info(r: &mut BitReader) -> Result<()> {
let present = r.read_flag("gci_present_flag")?;
if present {
for bits in GCI_FLAG_BLOCK_BITS {
let _ = r.read_bits(bits, "general_constraints_info flag")?;
}
let additional = r.read_bits(8, "gci_num_additional_bits")?;
let used = if additional > GCI_ADDITIONAL_THRESHOLD {
let _ = r.read_bits(GCI_ADDITIONAL_FLAG_BITS, "gci additional constraint flag")?;
GCI_ADDITIONAL_FLAG_BITS as u64
} else {
0
};
for _ in 0..(additional - used) {
let _ = r.read_bits(1, "gci_reserved_bit[i]")?;
}
}
r.align_to_byte("gci_alignment_zero_bit")?;
Ok(())
}
pub fn rfc6381_avc1(profile_idc: u8, constraint_flags: u8, level_idc: u8) -> String {
let mut s = String::with_capacity(12);
s.push_str("avc1.");
write_hex_byte(&mut s, profile_idc);
write_hex_byte(&mut s, constraint_flags);
write_hex_byte(&mut s, level_idc);
s
}
fn write_hex_byte(s: &mut String, byte: u8) {
let hi = (byte >> 4) & 0xF;
let lo = byte & 0xF;
s.push(hex_char(hi));
s.push(hex_char(lo));
}
fn hex_char(nibble: u8) -> char {
match nibble {
0..=9 => (b'0' + nibble) as char,
_ => (b'A' + (nibble - 10)) as char,
}
}
pub fn rfc6381_hvc1(info: &HevcSpsInfo) -> String {
let mut s = String::with_capacity(48);
s.push_str("hvc1.");
let profile_byte = (info.general_profile_space << 5) | info.general_profile_idc;
write_hex_byte(&mut s, profile_byte);
s.push('.');
write_compat_flags(&mut s, info.general_profile_compatibility_flags);
s.push('.');
let tier_char = if info.general_tier_flag { 'H' } else { 'L' };
s.push(tier_char);
write_hex_byte(&mut s, info.general_level_idc);
let constraints = info.general_constraint_indicator_flags;
let bytes: [u8; 6] = [
((constraints >> 40) & 0xFF) as u8,
((constraints >> 32) & 0xFF) as u8,
((constraints >> 24) & 0xFF) as u8,
((constraints >> 16) & 0xFF) as u8,
((constraints >> 8) & 0xFF) as u8,
(constraints & 0xFF) as u8,
];
let last_nonzero = bytes.iter().rposition(|&b| b != 0);
if let Some(end) = last_nonzero {
for (i, &b) in bytes.iter().enumerate() {
if i > end {
break;
}
s.push('.');
write_hex_byte(&mut s, b);
}
}
s
}
fn write_compat_flags(s: &mut String, flags: u32) {
let reversed = flags.reverse_bits();
let mut hex = [0u8; 8];
let mut v = reversed;
for i in (0..8).rev() {
let nib = (v & 0xF) as u8;
hex[i] = if nib < 10 {
b'0' + nib
} else {
b'A' + (nib - 10)
};
v >>= 4;
}
let end = (0..8)
.rev()
.find(|&i| hex[i] != b'0')
.map(|i| i + 1)
.unwrap_or(1);
for &b in &hex[..end] {
s.push(b as char);
}
}
pub fn rfc6381_vvc1(
general_profile_idc: u8,
general_tier_flag: bool,
general_level_idc: u8,
general_constraint_info: &[u8],
num_bytes_constraint_info: u8,
) -> String {
let mut s = String::with_capacity(24);
s.push_str("vvc1.");
write_decimal(&mut s, general_profile_idc);
s.push('.');
s.push(if general_tier_flag { 'H' } else { 'L' });
write_decimal(&mut s, general_level_idc);
if num_bytes_constraint_info > 0 {
let n = num_bytes_constraint_info as usize;
let block = &general_constraint_info[..general_constraint_info.len().min(n)];
if let Some(end) = block.iter().rposition(|&b| b != 0) {
s.push_str(".CTA-");
for &b in &block[..=end] {
write_hex_byte(&mut s, b);
}
}
}
s
}
pub fn rfc6381_mp4a(aot: u8) -> String {
let mut s = String::with_capacity(16);
s.push_str("mp4a.40.");
write_decimal(&mut s, aot);
s
}
fn write_decimal(s: &mut String, mut n: u8) {
if n == 0 {
s.push('0');
return;
}
let mut buf = [0u8; 4];
let mut len = 0;
while n > 0 {
buf[len] = b'0' + (n % 10);
n /= 10;
len += 1;
}
for i in (0..len).rev() {
s.push(buf[i] as char);
}
}
fn decode_ptl(
r: &mut BitReader,
profile_present_flag: bool,
max_sub_layers_minus1: u8,
) -> Result<(u8, bool, u8, u32, u64, u8)> {
let (general_profile_space, general_tier_flag, general_profile_idc) = if profile_present_flag {
(
r.read_bits(2, "general_profile_space")? as u8,
r.read_flag("general_tier_flag")?,
r.read_bits(5, "general_profile_idc")? as u8,
)
} else {
(0, false, 0)
};
let general_profile_compatibility_flags =
r.read_bits(32, "general_profile_compatibility_flags")? as u32;
let general_constraint_indicator_flags =
r.read_bits(48, "general_constraint_indicator_flags")?;
let general_level_idc = r.read_bits(8, "general_level_idc")? as u8;
let mut sub_layer_profile_present_flag = [false; 7];
let mut sub_layer_level_present_flag = [false; 7];
for i in 0..max_sub_layers_minus1 {
sub_layer_profile_present_flag[i as usize] =
r.read_flag("sub_layer_profile_present_flag[i]")?;
sub_layer_level_present_flag[i as usize] =
r.read_flag("sub_layer_level_present_flag[i]")?;
}
if max_sub_layers_minus1 > 0 {
for _i in max_sub_layers_minus1..8 {
let _ = r.read_bits(2, "reserved_zero_2bits")?;
}
}
for i in 0..max_sub_layers_minus1 {
if sub_layer_profile_present_flag[i as usize] {
let _ = r.read_bits(2, "sub_layer_profile_space")?;
let _ = r.read_flag("sub_layer_tier_flag")?;
let _ = r.read_bits(5, "sub_layer_profile_idc")?;
let _ = r.read_bits(32, "sub_layer_profile_compatibility_flags")?;
let _ = r.read_bits(48, "sub_layer_constraint_indicator_flags")?;
}
if sub_layer_level_present_flag[i as usize] {
let _ = r.read_bits(8, "sub_layer_level_idc")?;
}
}
Ok((
general_profile_space,
general_tier_flag,
general_profile_idc,
general_profile_compatibility_flags,
general_constraint_indicator_flags,
general_level_idc,
))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rfc6381_avc1_baseline() {
assert_eq!(rfc6381_avc1(66, 0xC0, 13), "avc1.42C00D");
}
#[test]
fn rfc6381_avc1_main() {
assert_eq!(rfc6381_avc1(77, 0x40, 13), "avc1.4D400D");
}
#[test]
fn rfc6381_avc1_high() {
assert_eq!(rfc6381_avc1(100, 0x00, 13), "avc1.64000D");
}
#[test]
fn rfc6381_mp4a_aac_lc() {
assert_eq!(rfc6381_mp4a(2), "mp4a.40.2");
}
#[test]
fn avc_sps_vui_timing_25fps() {
let sps: &[u8] = &[
0x67, 0x64, 0x00, 0x0D, 0xAC, 0xD9, 0x41, 0x41, 0xFB, 0x01, 0x10, 0x00, 0x00, 0x03,
0x00, 0x10, 0x00, 0x00, 0x03, 0x03, 0x20, 0xF1, 0x42, 0x99, 0x60,
];
let info = decode_avc_sps(sps).unwrap();
assert_eq!(info.profile_idc, 100);
assert_eq!(info.level_idc, 13);
assert_eq!(info.num_units_in_tick, Some(1));
assert_eq!(info.time_scale, Some(50));
let fps = info.fps.expect("fps must be Some for this SPS");
assert!(
(fps - 25.0_f32).abs() < 0.001,
"expected 25.0 fps, got {fps}"
);
}
#[test]
fn avc_sps_no_timing_info_yields_none() {
let sps: &[u8] = &[
0x67, 0x64, 0x00, 0x0D, 0xAD, 0xC8, 0xBF, 0xFE, 0x03, 0xC1, 0x41, 0xF9,
];
let info = decode_avc_sps(sps).unwrap();
assert_eq!(
info.num_units_in_tick, None,
"no timing_info → num_units_in_tick must be None"
);
assert_eq!(
info.time_scale, None,
"no timing_info → time_scale must be None"
);
assert_eq!(info.fps, None, "no timing_info → fps must be None");
}
#[test]
fn decode_high_sps_with_scaling_lists() {
let sps_nal: Vec<u8> = vec![
0x67, 0x64, 0x00, 0x0D, 0xAD, 0xC8, 0xBF, 0xFE, 0x03, 0xC1, 0x41, 0xF9,
];
let info = decode_avc_sps(&sps_nal).unwrap();
assert_eq!(info.profile_idc, 100);
assert_eq!(info.level_idc, 13);
assert_eq!(info.chroma_format_idc, 1);
assert_eq!(info.bit_depth_luma, 8);
assert_eq!(info.bit_depth_chroma, 8);
assert_eq!(info.width, 320);
assert_eq!(info.height, 240);
assert!(info.frame_mbs_only);
}
#[test]
fn hevc_sps_vui_timing_25fps() {
let sps: &[u8] = &[
0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00,
0x00, 0x03, 0x00, 0x3c, 0xa0, 0x0a, 0x08, 0x0f, 0x16, 0x59, 0x59, 0xa4, 0x93, 0x2b,
0xc0, 0x5a, 0x02, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x03, 0x00, 0x32, 0x10,
];
let info = decode_hevc_sps(sps).unwrap();
assert_eq!(info.width, 320, "width regression");
assert_eq!(info.height, 240, "height regression");
assert_eq!(info.chroma_format_idc, 1, "chroma_format_idc regression");
assert_eq!(info.bit_depth_luma, 8, "bit_depth_luma regression");
assert_eq!(info.bit_depth_chroma, 8, "bit_depth_chroma regression");
assert_eq!(
info.general_profile_idc, 1,
"general_profile_idc regression"
);
assert_eq!(info.general_level_idc, 60, "general_level_idc regression");
assert_eq!(
info.num_units_in_tick,
Some(1),
"vui_num_units_in_tick must be 1"
);
assert_eq!(info.time_scale, Some(25), "vui_time_scale must be 25");
let fps = info.fps.expect("fps must be Some for this SPS");
assert!(
(fps - 25.0_f32).abs() < 0.001,
"expected 25.0 fps, got {fps}"
);
}
#[test]
fn hevc_sps_no_vui_yields_none() {
let truncated: &[u8] = &[
0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x90, ];
let info = decode_hevc_sps(truncated).ok();
if let Some(info) = info {
assert_eq!(
info.num_units_in_tick, None,
"truncated/no-VUI SPS must yield None for num_units_in_tick"
);
assert_eq!(
info.time_scale, None,
"truncated/no-VUI SPS must yield None for time_scale"
);
assert_eq!(
info.fps, None,
"truncated/no-VUI SPS must yield None for fps"
);
}
}
fn vvc_sps_with_gci(additional: u8, width: u64, height: u64) -> Vec<u8> {
const GCI_FLAG_BITS: u32 = 3 + 4 + 2 + 16 + 2 + 43;
let mut s = BitSink::new();
s.bits(0, 8); s.bits(0, 3); s.bits(1, 2); s.bits(0, 2); s.bit(true); s.bits(1, 7); s.bit(false); s.bits(32, 8); s.bit(false); s.bit(false); s.bit(true); for _ in 0..GCI_FLAG_BITS {
s.bit(false);
}
s.bits(u64::from(additional), 8); if additional > 5 {
for _ in 0..6 {
s.bit(false); }
for _ in 0..(additional - 6) {
s.bit(false); }
} else {
for _ in 0..additional {
s.bit(false); }
}
while !s.nbits.is_multiple_of(8) {
s.bit(false); }
s.bits(0, 8); s.bit(false); s.bit(false); s.ue(width);
s.ue(height);
let rbsp = s.finish();
let mut escaped = Vec::with_capacity(rbsp.len() + 4);
let mut zeros = 0usize;
for &byte in &rbsp {
if zeros >= 2 && byte <= 0x03 {
escaped.push(0x03);
zeros = 0;
}
escaped.push(byte);
zeros = if byte == 0 { zeros + 1 } else { 0 };
}
let mut nal = alloc::vec![0x00, 0x00];
nal.extend_from_slice(&escaped);
nal[1] = 15 << 3; nal
}
#[test]
fn vvc_sps_gci_additional_bits_counts_decode_dimensions() {
for additional in [0u8, 1, 3, 5, 6, 8, 255] {
let nal = vvc_sps_with_gci(additional, 1920, 1080);
let info = decode_vvc_sps(&nal)
.unwrap_or_else(|e| panic!("additional={additional} must decode, got {e:?}"));
assert_eq!(
(info.width, info.height),
(1920, 1080),
"additional={additional} must land on the right dimensions"
);
}
}
#[test]
fn vvc_sps_gci_truncated_is_error_not_panic_for_every_count() {
for additional in [0u8, 3, 5, 6, 8, 255] {
let nal = vvc_sps_with_gci(additional, 1920, 1080);
for cut in 4..nal.len() {
assert!(
decode_vvc_sps(&nal[..cut]).is_err(),
"additional={additional} cut={cut} of {} must error",
nal.len()
);
}
}
}
#[test]
fn vvc_sps_gci_present_flag_is_skipped_to_dimensions() {
const GCI_BITS: u32 = 1 + 3 + 4 + 2 + 13 + 6 + 3 + 6 + 15 + 7 + 5 + 6 + 8;
let mut s = BitSink::new();
s.bits(0, 8);
s.bits(0, 3);
s.bits(1, 2);
s.bits(0, 2);
s.bit(true);
s.bits(1, 7); s.bit(false); s.bits(32, 8); s.bit(false); s.bit(false); s.bit(true);
for _ in 1..GCI_BITS {
s.bit(false);
}
while !s.nbits.is_multiple_of(8) {
s.bit(false);
}
s.bits(0, 8);
s.bit(false);
s.bit(false);
s.ue(1920);
s.ue(1080);
let rbsp = s.finish();
let mut nal = alloc::vec![0x00, 0x00];
nal.extend_from_slice(&rbsp);
nal[1] = 15 << 3;
let info = decode_vvc_sps(&nal).expect("a GCI-present SPS must decode");
assert_eq!(info.chroma_format_idc, 1);
assert_eq!(info.general_profile_idc, 1);
assert_eq!(info.general_level_idc, 32);
assert_eq!(info.width, 1920);
assert_eq!(info.height, 1080);
}
#[test]
fn vvc_sps_gci_absent_still_decodes() {
let mut s = BitSink::new();
s.bits(0, 8); s.bits(0, 3); s.bits(1, 2); s.bits(0, 2); s.bit(true); s.bits(1, 7); s.bit(false); s.bits(32, 8); s.bit(false); s.bit(false); s.bit(false); while !s.nbits.is_multiple_of(8) {
s.bit(false); }
s.bits(0, 8); s.bit(false); s.bit(false); s.ue(640);
s.ue(360);
let rbsp = s.finish();
let mut nal = alloc::vec![0x00, 0x00];
nal.extend_from_slice(&rbsp);
nal[1] = 15 << 3;
let info = decode_vvc_sps(&nal).expect("GCI-absent SPS must decode");
assert_eq!((info.width, info.height), (640, 360));
}
#[test]
fn vvc_sps_width_above_u16_is_not_truncated() {
let mut s = BitSink::new();
s.bits(0, 8); s.bits(0, 3); s.bits(1, 2); s.bits(0, 2); s.bit(true); s.bits(1, 7); s.bit(false); s.bits(32, 8); s.bit(false); s.bit(false); s.bit(false); while !s.nbits.is_multiple_of(8) {
s.bit(false);
}
s.bits(0, 8); s.bit(false); s.bit(false); s.ue(70_000);
s.ue(70_000);
let rbsp = s.finish();
let mut nal = alloc::vec![0x00, 0x00];
nal.extend_from_slice(&rbsp);
nal[1] = 15 << 3;
let info = decode_vvc_sps(&nal).expect("oversized SPS must decode");
assert_eq!((info.width, info.height), (70_000, 70_000));
}
#[test]
fn vvc_sps_gci_present_truncated_is_error_not_panic() {
let nal: &[u8] = &[0x00, 0x78, 0x00, 0x40];
assert!(decode_vvc_sps(nal).is_err());
assert!(decode_vvc_sps(&[0x00, 0x78]).is_err());
}
use alloc::vec;
use alloc::vec::Vec;
struct BitSink {
bytes: Vec<u8>,
cur: u8,
nbits: u32,
}
impl BitSink {
fn new() -> Self {
Self {
bytes: Vec::new(),
cur: 0,
nbits: 0,
}
}
fn bit(&mut self, b: bool) {
if b {
self.cur |= 1 << (7 - self.nbits);
}
self.nbits += 1;
if self.nbits == 8 {
self.bytes.push(self.cur);
self.cur = 0;
self.nbits = 0;
}
}
fn bits(&mut self, v: u64, n: u32) {
for i in (0..n).rev() {
self.bit((v >> i) & 1 == 1);
}
}
fn ue(&mut self, v: u64) {
let code = v + 1;
let len = 64 - code.leading_zeros();
for _ in 0..len - 1 {
self.bit(false);
}
self.bits(code, len);
}
fn ue_huge(&mut self) {
for _ in 0..32 {
self.bit(false);
}
self.bit(true);
for _ in 0..32 {
self.bit(false);
}
}
fn finish(mut self) -> Vec<u8> {
if self.nbits > 0 {
self.bytes.push(self.cur);
}
self.bytes
}
}
#[test]
fn avc_sps_huge_poc_cycle_count_is_rejected() {
let mut nal = vec![0x67u8, 66, 0x00, 30]; let mut s = BitSink::new();
s.ue(0); s.ue(0); s.ue(1); s.bit(false); s.bit(true); s.bit(true); s.ue_huge(); nal.extend(s.finish());
let err =
decode_avc_sps(&nal).expect_err("H.264 §7.4.2.1 bounds the cycle count to 0..=255");
assert!(
matches!(
err,
Error::InvalidValue {
field: "num_ref_frames_in_pic_order_cnt_cycle",
..
}
),
"{err:?}"
);
}
#[test]
fn avc_sps_chroma_format_idc_above_3_is_rejected() {
let mut nal = vec![0x67u8, 100, 0x00, 30]; let mut s = BitSink::new();
s.ue(0); s.ue(4); nal.extend(s.finish());
let err = decode_avc_sps(&nal).expect_err("chroma_format_idc shall be 0..=3");
assert!(
matches!(
err,
Error::InvalidValue {
field: "chroma_format_idc",
..
}
),
"{err:?}"
);
}
#[test]
fn avc_sps_bit_depth_minus8_above_6_is_rejected() {
let mut nal = vec![0x67u8, 100, 0x00, 30];
let mut s = BitSink::new();
s.ue(0); s.ue(1); s.ue(7); nal.extend(s.finish());
let err = decode_avc_sps(&nal).expect_err("bit_depth_luma_minus8 shall be 0..=6");
assert!(
matches!(
err,
Error::InvalidValue {
field: "bit_depth_luma_minus8",
..
}
),
"{err:?}"
);
}
#[test]
fn hevc_sps_huge_short_term_ref_pic_set_count_terminates_promptly() {
let mut nal = vec![0x42u8, 0x01]; let mut s = BitSink::new();
s.bits(0, 4); s.bits(0, 3); s.bit(false); s.bits(0, 2); s.bit(false); s.bits(1, 5); s.bits(0, 32); s.bits(0xE0_00_00_00_00_00, 48); s.bits(93, 8); s.ue(0); s.ue(1); s.ue(16); s.ue(16); s.bit(false); s.ue(2); s.ue(2); s.ue(3); s.bit(false); s.ue(0); s.ue(0); s.ue(0); s.ue(0); s.ue(3); s.ue(0); s.ue(3); s.ue(1); s.ue(1); s.bit(false); s.bit(false); s.bit(false); s.bit(false); s.ue_huge(); nal.extend(s.finish());
let info = decode_hevc_sps(&nal)
.expect("mandatory fields parse; the bounded VUI walk error is swallowed");
assert_eq!(info.fps, None);
}
use crate::interlace::{PicStruct, SourceScanType, avc_facts, hevc_facts};
use crate::slice_header::AvcParamSets;
fn hevc_interlace_sps(field_seq: bool, ffi: bool, vui: bool, ptl_bits: u64) -> Vec<u8> {
let mut nal = vec![0x42u8, 0x01];
let mut s = BitSink::new();
s.bits(0, 4); s.bits(0, 3); s.bit(true); s.bits(0, 2); s.bit(false); s.bits(1, 5); s.bits(0, 32); s.bits(ptl_bits, 48); s.bits(93, 8); s.ue(0); s.ue(1); s.ue(16); s.ue(16); s.bit(false); s.ue(0); s.ue(0); s.ue(3); s.bit(false); s.ue(0);
s.ue(0);
s.ue(0); s.ue(0);
s.ue(3);
s.ue(0);
s.ue(3); s.ue(1);
s.ue(1); s.bit(false); s.bit(false); s.bit(false); s.bit(false); s.ue(3); s.ue(1); s.ue(1); s.ue(0); s.bit(true); s.ue(1); s.bit(true); s.bit(true); s.bit(true); s.ue(2); s.bit(true); s.bit(false); s.bit(false); s.bit(true); s.bit(true); s.bit(false); s.ue(0); for _ in 0..3 {
s.bit(true); }
s.bit(false); s.bit(false); s.bit(false); s.bit(vui); if vui {
s.bit(false); s.bit(false); s.bit(false); s.bit(false); s.bit(false); s.bit(field_seq); s.bit(ffi); s.bit(false); s.bit(false); s.bit(false); }
s.bit(true); nal.extend(s.finish());
nal
}
#[test]
fn hevc_field_seq_is_read_past_inter_rps_prediction() {
let sps = hevc_interlace_sps(true, true, true, 0);
let i = decode_hevc_interlace_sps(&sps).expect("parses");
assert!(i.field_seq, "field_seq_flag read through 3 st_ref_pic_sets");
assert!(i.frame_field_info_present);
let frames = decode_hevc_interlace_sps(&hevc_interlace_sps(false, false, true, 0)).unwrap();
assert_eq!(
(frames.field_seq, frames.frame_field_info_present),
(false, false)
);
assert!(decode_hevc_sps(&sps).is_ok());
}
#[test]
fn hevc_frame_field_info_is_inferred_from_ptl_when_vui_absent() {
let both = decode_hevc_interlace_sps(&hevc_interlace_sps(
false,
false,
false,
0xC0_00_00_00_00_00,
))
.unwrap();
assert!(both.frame_field_info_present && !both.field_seq);
let one = decode_hevc_interlace_sps(&hevc_interlace_sps(
false,
false,
false,
0x80_00_00_00_00_00,
))
.unwrap();
assert!(!one.frame_field_info_present);
}
fn hevc_nal(nal_type: u8, body: &[u8]) -> Vec<u8> {
let mut v = vec![nal_type << 1, 0x01];
v.extend_from_slice(body);
v
}
fn hevc_pic_timing_sei(pic_struct: u64, scan: u64) -> Vec<u8> {
let mut p = BitSink::new();
p.bits(pic_struct, 4);
p.bits(scan, 2);
p.bit(false); let payload = p.finish(); let mut body = vec![1u8, payload.len() as u8]; body.extend_from_slice(&payload);
body.push(0x80); hevc_nal(39, &body)
}
#[test]
fn hevc_pic_struct_and_scan_type_are_read_from_prefix_sei() {
let sps = hevc_interlace_sps(true, true, true, 0);
let vcl = hevc_nal(1, &[0x80]);
for (code, want, bottom_first) in [
(1u64, PicStruct::TopField, Some(false)),
(2, PicStruct::BottomField, Some(true)),
(10, PicStruct::BottomPairedWithPreviousTop, Some(true)),
(11, PicStruct::TopPairedWithNextBottom, Some(false)),
] {
let sei = hevc_pic_timing_sei(code, 0);
let f = hevc_facts(&sps, &[sei.as_slice(), vcl.as_slice()]).expect("facts");
assert!(f.field_pic);
assert_eq!(f.fields, 1);
assert_eq!(f.pic_struct, Some(want));
assert_eq!(f.bottom_field_first, bottom_first);
assert_eq!(f.source_scan_type, Some(SourceScanType::Interlaced));
}
let sei = hevc_pic_timing_sei(1, 3);
let f = hevc_facts(&sps, &[sei.as_slice(), vcl.as_slice()]).unwrap();
assert_eq!(f.source_scan_type, Some(SourceScanType::Unknown));
let sei = hevc_pic_timing_sei(13, 0);
let f = hevc_facts(&sps, &[sei.as_slice(), vcl.as_slice()]).unwrap();
assert_eq!(f.pic_struct, None);
let f = hevc_facts(&sps, &[vcl.as_slice()]).unwrap();
assert!(f.field_pic);
assert_eq!((f.pic_struct, f.bottom_field_first), (None, None));
assert!(hevc_facts(&sps, &[sei.as_slice()]).is_none());
let sps0 = hevc_interlace_sps(false, false, true, 0);
let sei = hevc_pic_timing_sei(4, 0);
let f = hevc_facts(&sps0, &[sei.as_slice(), vcl.as_slice()]).unwrap();
assert!(!f.field_pic);
assert_eq!(f.pic_struct, None);
}
fn avc_interlace_sps(
mbaff: bool,
pic_struct_present: bool,
nal_hrd: Option<(u64, u64)>,
vcl_hrd: Option<(u64, u64)>,
) -> Vec<u8> {
fn hrd(s: &mut BitSink, (cpb, dpb): (u64, u64)) {
s.ue(1); s.bits(0, 4); s.bits(0, 4); for _ in 0..2 {
s.ue(7); s.ue(9); s.bit(true); }
s.bits(22, 5); s.bits(cpb, 5); s.bits(dpb, 5); s.bits(24, 5); }
let mut nal = vec![0x67u8, 66, 0x00, 30];
let mut s = BitSink::new();
s.ue(0); s.ue(0); s.ue(2); s.ue(1); s.bit(false); s.ue(3); s.ue(1); s.bit(false); s.bit(mbaff); s.bit(true); s.bit(false); s.bit(true); s.bit(false); s.bit(false); s.bit(false); s.bit(false); s.bit(true); s.bits(1, 32); s.bits(50, 32); s.bit(true); s.bit(nal_hrd.is_some()); if let Some(h) = nal_hrd {
hrd(&mut s, h);
}
s.bit(vcl_hrd.is_some()); if let Some(h) = vcl_hrd {
hrd(&mut s, h);
}
if nal_hrd.is_some() || vcl_hrd.is_some() {
s.bit(false); }
s.bit(pic_struct_present); s.bit(false); s.bit(true); nal.extend(s.finish());
nal
}
fn avc_pps() -> Vec<u8> {
let mut s = BitSink::new();
s.ue(0); s.ue(0); s.bit(false); s.bit(false); s.ue(0); s.ue(0);
s.ue(0); s.bit(false); s.bits(0, 2); s.ue(0); s.ue(0); s.ue(0); s.bit(false); s.bit(false); s.bit(false); s.bit(true);
let mut nal = vec![0x68u8];
nal.extend(s.finish());
nal
}
fn avc_slice(field: Option<bool>) -> Vec<u8> {
let mut s = BitSink::new();
s.ue(0); s.ue(7); s.ue(0); s.bits(0, 4); match field {
None => s.bit(false), Some(bottom) => {
s.bit(true); s.bit(bottom); }
}
s.bits(0, 8); let mut nal = vec![0x61u8]; nal.extend(s.finish());
nal
}
fn avc_pic_timing_sei(delays: Option<(u32, u32)>, pic_struct: u64) -> Vec<u8> {
let mut p = BitSink::new();
if let Some((cpb, dpb)) = delays {
p.bits(0x15555 & ((1 << cpb) - 1), cpb); p.bits(0x2AAAA & ((1 << dpb) - 1), dpb); }
p.bits(pic_struct, 4);
p.bits(0, 3); let payload = p.finish();
let mut nal = vec![0x06u8, 1, payload.len() as u8];
nal.extend_from_slice(&payload);
nal.push(0x80);
nal
}
fn avc_ps(sps: &[u8]) -> AvcParamSets {
let mut ps = AvcParamSets::new();
ps.observe_nal(sps);
ps.observe_nal(&avc_pps());
ps
}
#[test]
fn avc_sps_scan_flags_and_pic_timing_layout() {
let paff = decode_avc_slice_sps(&avc_interlace_sps(false, false, None, None)).unwrap();
assert!(!paff.frame_mbs_only && !paff.mb_adaptive_frame_field);
assert_eq!(paff.pic_timing, None, "pic_struct_present_flag = 0");
let mbaff = decode_avc_slice_sps(&avc_interlace_sps(true, true, None, None)).unwrap();
assert!(mbaff.mb_adaptive_frame_field);
assert_eq!(
mbaff.pic_timing,
Some(AvcPicTimingLayout { delay_bits: None })
);
let hrd = decode_avc_slice_sps(&avc_interlace_sps(true, true, Some((4, 3)), None)).unwrap();
assert_eq!(
hrd.pic_timing,
Some(AvcPicTimingLayout {
delay_bits: Some((5, 4))
})
);
let both = decode_avc_slice_sps(&avc_interlace_sps(true, true, Some((4, 3)), Some((4, 3))))
.unwrap();
assert_eq!(both.pic_timing, hrd.pic_timing);
let clash =
decode_avc_slice_sps(&avc_interlace_sps(true, true, Some((4, 3)), Some((6, 3))))
.unwrap();
assert_eq!(clash.pic_timing, None);
}
#[test]
fn avc_pic_struct_is_read_after_the_hrd_delays() {
let sps = avc_interlace_sps(true, true, Some((4, 3)), None);
let ps = avc_ps(&sps);
let frame = avc_slice(None);
let sei = avc_pic_timing_sei(Some((5, 4)), 4);
let f = avc_facts(&ps, &[sei.as_slice(), frame.as_slice()]).expect("facts");
assert!(!f.field_pic);
assert_eq!(f.fields, 0);
assert_eq!(f.pic_struct, Some(PicStruct::BottomTop));
assert_eq!(f.bottom_field_first, Some(true));
let sei = avc_pic_timing_sei(Some((5, 4)), 9);
let f = avc_facts(&ps, &[sei.as_slice(), frame.as_slice()]).unwrap();
assert_eq!(f.pic_struct, None);
let sps = avc_interlace_sps(false, true, None, None);
let ps = avc_ps(&sps);
let sei = avc_pic_timing_sei(None, 3);
let f = avc_facts(&ps, &[sei.as_slice(), frame.as_slice()]).unwrap();
assert_eq!(f.pic_struct, Some(PicStruct::TopBottom));
assert_eq!(f.bottom_field_first, Some(false));
}
#[test]
fn avc_field_pair_parity_comes_from_the_first_slice() {
let ps = avc_ps(&avc_interlace_sps(false, false, None, None));
let (top, bottom) = (avc_slice(Some(false)), avc_slice(Some(true)));
let pair = avc_facts(&ps, &[top.as_slice(), bottom.as_slice()]).unwrap();
assert_eq!(
(pair.field_pic, pair.fields, pair.bottom_field_first),
(true, 2, Some(false))
);
let pair = avc_facts(&ps, &[bottom.as_slice(), top.as_slice()]).unwrap();
assert_eq!(
(pair.field_pic, pair.fields, pair.bottom_field_first),
(true, 2, Some(true))
);
let lone = avc_facts(&ps, &[bottom.as_slice()]).unwrap();
assert_eq!((lone.fields, lone.bottom_field_first), (1, Some(true)));
let same = avc_facts(&ps, &[top.as_slice(), top.as_slice()]).unwrap();
assert_eq!(same.fields, 1);
assert!(avc_facts(&AvcParamSets::new(), &[top.as_slice()]).is_none());
}
}