#![forbid(unsafe_code)]
use super::bitreader::BitReader;
use super::error::H264Error;
use super::nal::NalUnitType;
use super::pps::Pps;
use super::sps::Sps;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum SliceType {
P,
B,
I,
Sp,
Si,
}
impl SliceType {
#[must_use]
pub const fn from_raw(slice_type: u32) -> Self {
match slice_type % 5 {
0 => Self::P,
1 => Self::B,
3 => Self::Sp,
4 => Self::Si,
_ => Self::I,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SliceHeader {
pub first_mb_in_slice: u32,
pub slice_type: SliceType,
pub pic_parameter_set_id: u32,
pub frame_num: u32,
pub idr_pic_id: Option<u32>,
pub pic_order_cnt_lsb: Option<u32>,
pub slice_qp_delta: i32,
}
impl SliceHeader {
pub fn parse(
reader: &mut BitReader<'_>,
sps: &Sps,
pps: &Pps,
nal_unit_type: NalUnitType,
nal_ref_idc: u8,
) -> Result<Self, H264Error> {
if !sps.frame_mbs_only {
return Err(H264Error::UnsupportedFieldCoding);
}
if sps.pic_order_cnt_type != 0 {
return Err(H264Error::UnsupportedPicOrderCntType);
}
let first_mb_in_slice = reader.read_ue()?;
let slice_type = SliceType::from_raw(reader.read_ue()?);
if !matches!(slice_type, SliceType::I) {
return Err(H264Error::UnsupportedSliceType);
}
let pic_parameter_set_id = reader.read_ue()?;
let frame_num = reader.read_bits(sps.log2_max_frame_num)?;
let is_idr = matches!(nal_unit_type, NalUnitType::IdrSlice);
let idr_pic_id = if is_idr {
Some(reader.read_ue()?)
} else {
None
};
let pic_order_cnt_lsb = Some(reader.read_bits(sps.log2_max_pic_order_cnt_lsb)?);
if nal_ref_idc != 0 {
skip_dec_ref_pic_marking(reader, is_idr)?;
}
let slice_qp_delta = reader.read_se()?;
if pps.deblocking_filter_control_present {
let disable_deblocking_filter_idc = reader.read_ue()?;
if disable_deblocking_filter_idc != 1 {
let _slice_alpha_c0_offset_div2 = reader.read_se()?;
let _slice_beta_offset_div2 = reader.read_se()?;
}
}
Ok(Self {
first_mb_in_slice,
slice_type,
pic_parameter_set_id,
frame_num,
idr_pic_id,
pic_order_cnt_lsb,
slice_qp_delta,
})
}
}
fn skip_dec_ref_pic_marking(reader: &mut BitReader<'_>, is_idr: bool) -> Result<(), H264Error> {
if is_idr {
let _no_output_of_prior_pics_flag = reader.read_bit()?;
let _long_term_reference_flag = reader.read_bit()?;
} else {
let adaptive_ref_pic_marking_mode_flag = reader.read_bit()?;
if adaptive_ref_pic_marking_mode_flag != 0 {
loop {
let memory_management_control_operation = reader.read_ue()?;
if memory_management_control_operation == 0 {
break;
}
match memory_management_control_operation {
1 | 3 => {
let _difference_of_pic_nums_minus1 = reader.read_ue()?;
if memory_management_control_operation == 3 {
let _long_term_frame_idx = reader.read_ue()?;
}
}
2 | 4 | 6 => {
let _ = reader.read_ue()?;
}
_ => {}
}
}
}
}
Ok(())
}
#[cfg(test)]
#[path = "slice_tests.rs"]
mod tests;