#![forbid(unsafe_code)]
#![allow(
clippy::cast_possible_truncation,
reason = "every count here comes from an Exp-Golomb-decoded HEVC syntax element, always \
small in practice — mirrors vulkan::hevc_slice's identical allow"
)]
use smallvec::SmallVec;
use crate::DecodeError;
use mediaway_sw::h264::BitReader;
use super::hevc_pps::HevcPps;
use super::hevc_sps::HevcSps;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum HevcSliceType {
B,
P,
I,
}
impl HevcSliceType {
#[must_use]
const fn from_raw(value: u32) -> Option<Self> {
match value {
0 => Some(Self::B),
1 => Some(Self::P),
2 => Some(Self::I),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct ShortTermRefPicEntry {
pub(super) delta_poc: i32,
pub(super) used_by_curr_pic: bool,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(super) struct ShortTermRefPicSet {
pub(super) s0: SmallVec<[ShortTermRefPicEntry; 8]>,
pub(super) s1: SmallVec<[ShortTermRefPicEntry; 8]>,
}
impl ShortTermRefPicSet {
pub(super) fn parse(r: &mut BitReader<'_>) -> Result<Self, DecodeError> {
let map_err = |_| DecodeError::InvalidInput;
let num_negative_pics = r.read_ue().map_err(map_err)?;
let num_positive_pics = r.read_ue().map_err(map_err)?;
if num_negative_pics > 8 || num_positive_pics > 8 {
return Err(DecodeError::Unsupported);
}
let mut s0 = SmallVec::new();
let mut delta_poc = 0i32;
for _ in 0..num_negative_pics {
let delta_poc_s0_minus1 = r.read_ue().map_err(map_err)?;
let used_by_curr_pic = r.read_bit().map_err(map_err)? != 0;
let step = i32::try_from(delta_poc_s0_minus1)
.ok()
.and_then(|v| v.checked_add(1))
.ok_or(DecodeError::InvalidInput)?;
delta_poc = delta_poc
.checked_sub(step)
.ok_or(DecodeError::InvalidInput)?;
s0.push(ShortTermRefPicEntry {
delta_poc,
used_by_curr_pic,
});
}
let mut s1 = SmallVec::new();
let mut delta_poc = 0i32;
for _ in 0..num_positive_pics {
let delta_poc_s1_minus1 = r.read_ue().map_err(map_err)?;
let used_by_curr_pic = r.read_bit().map_err(map_err)? != 0;
let step = i32::try_from(delta_poc_s1_minus1)
.ok()
.and_then(|v| v.checked_add(1))
.ok_or(DecodeError::InvalidInput)?;
delta_poc = delta_poc
.checked_add(step)
.ok_or(DecodeError::InvalidInput)?;
s1.push(ShortTermRefPicEntry {
delta_poc,
used_by_curr_pic,
});
}
Ok(Self { s0, s1 })
}
#[must_use]
pub(super) fn is_single_forward_reference(&self) -> bool {
self.s0.len() == 1
&& self.s1.is_empty()
&& self.s0[0].delta_poc == -1
&& self.s0[0].used_by_curr_pic
}
}
#[allow(
clippy::struct_excessive_bools,
reason = "each bool is a real, independent ITU-T H.265 slice-header syntax element that \
must be echoed into SliceParameterBufferHEVC exactly as signaled — mirrors \
HevcSps/HevcPps's identical allow"
)]
#[derive(Debug, Clone, PartialEq)]
pub(super) struct HevcSliceSegmentHeader {
pub(super) slice_type: HevcSliceType,
pub(super) slice_pic_parameter_set_id: u32,
pub(super) pic_order_cnt_lsb: Option<u32>,
pub(super) short_term_rps: Option<ShortTermRefPicSet>,
pub(super) slice_sao_luma_flag: bool,
pub(super) slice_sao_chroma_flag: bool,
pub(super) slice_temporal_mvp_enabled_flag: bool,
pub(super) num_ref_idx_l0_active: u32,
pub(super) cabac_init_flag: bool,
pub(super) five_minus_max_num_merge_cand: u32,
pub(super) slice_qp_delta: i32,
pub(super) slice_cb_qp_offset: i32,
pub(super) slice_cr_qp_offset: i32,
pub(super) slice_loop_filter_across_slices_enabled_flag: bool,
pub(super) st_rps_bits: u32,
pub(super) bits_consumed: usize,
}
impl HevcSliceSegmentHeader {
#[allow(
clippy::too_many_lines,
reason = "linear ITU-T H.265 § 7.3.6.1 syntax-element sequence through every reachable \
field this crate's own accepted-stream shape has — mirrors \
vulkan::HevcSliceSegmentHeader::parse's identical allow, extended well past \
that function's own documented stopping point (see module doc)"
)]
pub(super) fn parse(
r: &mut BitReader<'_>,
sps: &HevcSps,
pps: &HevcPps,
is_idr: bool,
) -> Result<Self, DecodeError> {
let map_err = |_| DecodeError::InvalidInput;
let first_slice_segment_in_pic_flag = r.read_bit().map_err(map_err)? != 0;
if !first_slice_segment_in_pic_flag {
return Err(DecodeError::Unsupported);
}
if is_idr {
let _no_output_of_prior_pics_flag = r.read_bit().map_err(map_err)?;
}
let slice_pic_parameter_set_id = r.read_ue().map_err(map_err)?;
for _ in 0..pps.num_extra_slice_header_bits {
let _slice_reserved_flag = r.read_bit().map_err(map_err)?;
}
let slice_type = HevcSliceType::from_raw(r.read_ue().map_err(map_err)?)
.ok_or(DecodeError::InvalidInput)?;
if matches!(slice_type, HevcSliceType::B) {
return Err(DecodeError::Unsupported);
}
let is_p_slice = matches!(slice_type, HevcSliceType::P);
if pps.output_flag_present_flag {
let _pic_output_flag = r.read_bit().map_err(map_err)?;
}
let mut pic_order_cnt_lsb = None;
let mut short_term_rps = None;
let mut st_rps_bits = 0u32;
let mut slice_temporal_mvp_enabled_flag = false;
if !is_idr {
pic_order_cnt_lsb = Some(
r.read_bits(sps.log2_max_pic_order_cnt_lsb)
.map_err(map_err)?,
);
let short_term_ref_pic_set_sps_flag = r.read_bit().map_err(map_err)? != 0;
if short_term_ref_pic_set_sps_flag {
return Err(DecodeError::Unsupported);
}
let bits_before_rps = r.bits_read();
let rps = ShortTermRefPicSet::parse(r)?;
st_rps_bits = u32::try_from(r.bits_read() - bits_before_rps)
.map_err(|_| DecodeError::InvalidInput)?;
if !rps.is_single_forward_reference() {
return Err(DecodeError::Unsupported);
}
short_term_rps = Some(rps);
if sps.sps_temporal_mvp_enabled_flag {
slice_temporal_mvp_enabled_flag = r.read_bit().map_err(map_err)? != 0;
}
}
let (slice_sao_luma_flag, slice_sao_chroma_flag) =
if sps.sample_adaptive_offset_enabled_flag {
let luma = r.read_bit().map_err(map_err)? != 0;
let chroma = r.read_bit().map_err(map_err)? != 0;
(luma, chroma)
} else {
(false, false)
};
let (num_ref_idx_l0_active, cabac_init_flag, five_minus_max_num_merge_cand) = if is_p_slice
{
if pps.weighted_pred_flag {
return Err(DecodeError::Unsupported);
}
let num_ref_idx_active_override_flag = r.read_bit().map_err(map_err)? != 0;
let num_ref_idx_l0_active = if num_ref_idx_active_override_flag {
r.read_ue()
.map_err(map_err)?
.checked_add(1)
.ok_or(DecodeError::InvalidInput)?
} else {
pps.num_ref_idx_l0_default_active
};
if num_ref_idx_l0_active != 1 {
return Err(DecodeError::Unsupported);
}
let cabac_init_flag = if pps.cabac_init_present_flag {
r.read_bit().map_err(map_err)? != 0
} else {
false
};
let five_minus_max_num_merge_cand = r.read_ue().map_err(map_err)?;
(
num_ref_idx_l0_active,
cabac_init_flag,
five_minus_max_num_merge_cand,
)
} else {
(0u32, false, 0u32)
};
let slice_qp_delta = r.read_se().map_err(map_err)?;
let (slice_cb_qp_offset, slice_cr_qp_offset) =
if pps.pps_slice_chroma_qp_offsets_present_flag {
(r.read_se().map_err(map_err)?, r.read_se().map_err(map_err)?)
} else {
(0, 0)
};
let slice_loop_filter_across_slices_enabled_flag =
if pps.pps_loop_filter_across_slices_enabled_flag {
r.read_bit().map_err(map_err)? != 0
} else {
false
};
let alignment_bit_equal_to_one = r.read_bit().map_err(map_err)? != 0;
if !alignment_bit_equal_to_one {
return Err(DecodeError::Unsupported);
}
let pad_bits = (8 - (r.bits_read() % 8)) % 8;
if pad_bits > 0 {
let _alignment_bit_equal_to_zero = r
.read_bits(u32::try_from(pad_bits).map_err(|_| DecodeError::InvalidInput)?)
.map_err(map_err)?;
}
let bits_consumed = r.bits_read();
Ok(Self {
slice_type,
slice_pic_parameter_set_id,
pic_order_cnt_lsb,
short_term_rps,
slice_sao_luma_flag,
slice_sao_chroma_flag,
slice_temporal_mvp_enabled_flag,
num_ref_idx_l0_active,
cabac_init_flag,
five_minus_max_num_merge_cand,
slice_qp_delta,
slice_cb_qp_offset,
slice_cr_qp_offset,
slice_loop_filter_across_slices_enabled_flag,
st_rps_bits,
bits_consumed,
})
}
}
#[cfg(test)]
#[path = "hevc_slice_tests.rs"]
mod tests;