use crate::DecodeError;
use mediaway_sw::h264::{BitReader, NalUnitType};
use smallvec::SmallVec;
use super::h264_sps_pps::{Pps, Sps};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub(super) enum SliceType {
P,
B,
#[default]
I,
Sp,
Si,
}
impl SliceType {
const fn from_u32(value: u32) -> Option<Self> {
match value % 5 {
0 => Some(Self::P),
1 => Some(Self::B),
2 => Some(Self::I),
3 => Some(Self::Sp),
4 => Some(Self::Si),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct RefPicListModOp {
pub(super) add: bool,
pub(super) abs_diff_pic_num_minus1: u32,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub(super) struct SliceHeader {
pub(super) slice_type: SliceType,
pub(super) frame_num: u32,
pub(super) idr_pic_id: Option<u32>,
pub(super) pic_order_cnt_lsb: u32,
pub(super) delta_pic_order_cnt_bottom: i32,
pub(super) delta_pic_order_cnt: [i32; 2],
pub(super) direct_spatial_mv_pred_flag: bool,
pub(super) num_ref_idx_l0_active_minus1: u32,
pub(super) num_ref_idx_l1_active_minus1: u32,
pub(super) ref_pic_list_modification_l0: SmallVec<[RefPicListModOp; 4]>,
pub(super) ref_pic_list_modification_l1: SmallVec<[RefPicListModOp; 4]>,
pub(super) no_output_of_prior_pics_flag: bool,
pub(super) cabac_init_idc: u32,
pub(super) slice_qp_delta: i32,
pub(super) disable_deblocking_filter_idc: u32,
pub(super) slice_alpha_c0_offset_div2: i32,
pub(super) slice_beta_offset_div2: i32,
}
fn map_bit_err<T>(r: Result<T, mediaway_sw::h264::H264Error>) -> Result<T, DecodeError> {
r.map_err(|_err| DecodeError::InvalidInput)
}
fn read_bit(r: &mut BitReader<'_>) -> Result<bool, DecodeError> {
Ok(map_bit_err(r.read_bit())? != 0)
}
fn read_bits(r: &mut BitReader<'_>, count: u32) -> Result<u32, DecodeError> {
map_bit_err(r.read_bits(count))
}
fn read_ue(r: &mut BitReader<'_>) -> Result<u32, DecodeError> {
map_bit_err(r.read_ue())
}
fn read_se(r: &mut BitReader<'_>) -> Result<i32, DecodeError> {
map_bit_err(r.read_se())
}
fn parse_ref_pic_list_modification(
r: &mut BitReader<'_>,
) -> Result<SmallVec<[RefPicListModOp; 4]>, DecodeError> {
let mut ops = SmallVec::new();
if !read_bit(r)? {
return Ok(ops);
}
loop {
let idc = read_ue(r)?;
match idc {
0 | 1 => {
let abs_diff_pic_num_minus1 = read_ue(r)?;
ops.push(RefPicListModOp {
add: idc == 1,
abs_diff_pic_num_minus1,
});
}
2 => return Err(DecodeError::Unsupported),
3 => break,
_ => return Err(DecodeError::InvalidInput),
}
}
Ok(ops)
}
#[allow(
clippy::too_many_lines,
reason = "one linear slice_header() parse sequence; splitting fragments the bit-position invariant"
)]
pub(super) fn parse_slice_header(
rbsp: &[u8],
nal_unit_type: NalUnitType,
nal_ref_idc: u8,
sps: &Sps,
pps: &Pps,
) -> Result<(SliceHeader, usize), DecodeError> {
let is_idr = matches!(nal_unit_type, NalUnitType::IdrSlice);
let mut r = BitReader::new(rbsp);
let first_mb_in_slice = read_ue(&mut r)?;
if first_mb_in_slice != 0 {
return Err(DecodeError::Unsupported);
}
let slice_type_raw = read_ue(&mut r)?;
let slice_type = SliceType::from_u32(slice_type_raw).ok_or(DecodeError::InvalidInput)?;
if matches!(slice_type, SliceType::Sp | SliceType::Si) {
return Err(DecodeError::Unsupported);
}
let _pic_parameter_set_id = read_ue(&mut r)?;
let frame_num = read_bits(&mut r, sps.log2_max_frame_num)?;
let idr_pic_id = if is_idr { Some(read_ue(&mut r)?) } else { None };
let mut pic_order_cnt_lsb = 0u32;
let mut delta_pic_order_cnt_bottom = 0i32;
let mut delta_pic_order_cnt = [0i32; 2];
if sps.pic_order_cnt_type == 0 {
pic_order_cnt_lsb = read_bits(&mut r, sps.log2_max_pic_order_cnt_lsb)?;
if pps.bottom_field_pic_order_in_frame_present_flag {
delta_pic_order_cnt_bottom = read_se(&mut r)?;
}
} else if sps.pic_order_cnt_type == 1 && !sps.delta_pic_order_always_zero_flag {
delta_pic_order_cnt[0] = read_se(&mut r)?;
if pps.bottom_field_pic_order_in_frame_present_flag {
delta_pic_order_cnt[1] = read_se(&mut r)?;
}
}
if pps.redundant_pic_cnt_present_flag {
let redundant_pic_cnt = read_ue(&mut r)?;
if redundant_pic_cnt != 0 {
return Err(DecodeError::Unsupported);
}
}
let direct_spatial_mv_pred_flag = if matches!(slice_type, SliceType::B) {
read_bit(&mut r)?
} else {
false
};
let mut num_ref_idx_l0_active_minus1 = pps.num_ref_idx_l0_default_active_minus1;
let mut num_ref_idx_l1_active_minus1 = pps.num_ref_idx_l1_default_active_minus1;
if matches!(slice_type, SliceType::P | SliceType::Sp | SliceType::B) {
let num_ref_idx_active_override_flag = read_bit(&mut r)?;
if num_ref_idx_active_override_flag {
num_ref_idx_l0_active_minus1 = read_ue(&mut r)?;
if matches!(slice_type, SliceType::B) {
num_ref_idx_l1_active_minus1 = read_ue(&mut r)?;
}
}
}
let mut ref_pic_list_modification_l0 = SmallVec::new();
let mut ref_pic_list_modification_l1 = SmallVec::new();
if !matches!(slice_type, SliceType::I | SliceType::Si) {
ref_pic_list_modification_l0 = parse_ref_pic_list_modification(&mut r)?;
if matches!(slice_type, SliceType::B) {
ref_pic_list_modification_l1 = parse_ref_pic_list_modification(&mut r)?;
}
}
let weighted_table_present = (pps.weighted_pred_flag
&& matches!(slice_type, SliceType::P | SliceType::Sp))
|| (pps.weighted_bipred_idc == 1 && matches!(slice_type, SliceType::B));
if weighted_table_present {
return Err(DecodeError::Unsupported);
}
let mut no_output_of_prior_pics_flag = false;
if nal_ref_idc != 0 {
if is_idr {
no_output_of_prior_pics_flag = read_bit(&mut r)?;
let long_term_reference_flag = read_bit(&mut r)?;
if long_term_reference_flag {
return Err(DecodeError::Unsupported);
}
} else {
let adaptive_ref_pic_marking_mode_flag = read_bit(&mut r)?;
if adaptive_ref_pic_marking_mode_flag {
return Err(DecodeError::Unsupported);
}
}
}
let cabac_init_idc =
if pps.entropy_coding_mode_flag && !matches!(slice_type, SliceType::I | SliceType::Si) {
read_ue(&mut r)?
} else {
0u32
};
let slice_qp_delta = read_se(&mut r)?;
let mut disable_deblocking_filter_idc = 0u32;
let mut slice_alpha_c0_offset_div2 = 0i32;
let mut slice_beta_offset_div2 = 0i32;
if pps.deblocking_filter_control_present_flag {
disable_deblocking_filter_idc = read_ue(&mut r)?;
if disable_deblocking_filter_idc != 1 {
slice_alpha_c0_offset_div2 = read_se(&mut r)?;
slice_beta_offset_div2 = read_se(&mut r)?;
}
}
Ok((
SliceHeader {
slice_type,
frame_num,
idr_pic_id,
pic_order_cnt_lsb,
delta_pic_order_cnt_bottom,
delta_pic_order_cnt,
direct_spatial_mv_pred_flag,
num_ref_idx_l0_active_minus1,
num_ref_idx_l1_active_minus1,
ref_pic_list_modification_l0,
ref_pic_list_modification_l1,
no_output_of_prior_pics_flag,
cabac_init_idc,
slice_qp_delta,
disable_deblocking_filter_idc,
slice_alpha_c0_offset_div2,
slice_beta_offset_div2,
},
r.bits_read(),
))
}
pub(super) fn rbsp_bit_offset_to_raw_bit_offset(
raw_nal_payload: &[u8],
deemulated_bit_count: usize,
) -> u32 {
let deemulated_bytes_needed = deemulated_bit_count / 8;
let remainder_bits = deemulated_bit_count % 8;
let mut zero_run = 0u32;
let mut emitted = 0usize;
let mut raw_index = 0usize;
while raw_index < raw_nal_payload.len() {
let byte = raw_nal_payload[raw_index];
if zero_run >= 2 && byte == 0x03 {
zero_run = 0;
raw_index += 1;
continue;
}
if emitted == deemulated_bytes_needed {
let bits = raw_index.saturating_mul(8).saturating_add(remainder_bits);
return u32::try_from(bits).unwrap_or(u32::MAX);
}
zero_run = if byte == 0 { zero_run + 1 } else { 0 };
emitted += 1;
raw_index += 1;
}
let bits = raw_index.saturating_mul(8).saturating_add(remainder_bits);
u32::try_from(bits).unwrap_or(u32::MAX)
}
#[cfg(test)]
#[path = "h264_slice_tests.rs"]
mod tests;