#![forbid(unsafe_code)]
use mediaway_sw::h264::{BitReader, H264Error};
use smallvec::SmallVec;
use crate::vulkan::hevc_params::{HevcNalUnitType, HevcParamError, HevcPps, HevcSps};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub 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 struct ShortTermRefPicEntry {
pub delta_poc: i32,
pub used_by_curr_pic: bool,
}
#[derive(Debug, Clone, Default)]
pub struct ShortTermRefPicSet {
pub s0: SmallVec<[ShortTermRefPicEntry; 8]>,
pub s1: SmallVec<[ShortTermRefPicEntry; 8]>,
}
impl ShortTermRefPicSet {
pub fn parse(reader: &mut BitReader<'_>) -> Result<Self, HevcParamError> {
let num_negative_pics = reader.read_ue()?;
let num_positive_pics = reader.read_ue()?;
if num_negative_pics > 8 || num_positive_pics > 8 {
return Err(HevcParamError::Unsupported {
reason: "short_term_ref_pic_set with more than 8 negative or positive pictures \
exceeds StdVideoDecodeH265PictureInfo's RefPicSet array capacity",
});
}
let mut s0 = SmallVec::new();
let mut delta_poc = 0i32;
for _ in 0..num_negative_pics {
let delta_poc_s0_minus1 = reader.read_ue()?;
let used_by_curr_pic = reader.read_bit()? != 0;
let step = i32::try_from(delta_poc_s0_minus1)
.ok()
.and_then(|v| v.checked_add(1))
.ok_or(H264Error::FieldOverflow)?;
delta_poc = delta_poc
.checked_sub(step)
.ok_or(H264Error::FieldOverflow)?;
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 = reader.read_ue()?;
let used_by_curr_pic = reader.read_bit()? != 0;
let step = i32::try_from(delta_poc_s1_minus1)
.ok()
.and_then(|v| v.checked_add(1))
.ok_or(H264Error::FieldOverflow)?;
delta_poc = delta_poc
.checked_add(step)
.ok_or(H264Error::FieldOverflow)?;
s1.push(ShortTermRefPicEntry {
delta_poc,
used_by_curr_pic,
});
}
Ok(Self { s0, s1 })
}
#[must_use]
pub fn curr_before_after_poc(
&self,
current_poc: i32,
) -> (SmallVec<[i32; 8]>, SmallVec<[i32; 8]>) {
let before = self
.s0
.iter()
.filter(|entry| entry.used_by_curr_pic)
.map(|entry| current_poc + entry.delta_poc)
.collect();
let after = self
.s1
.iter()
.filter(|entry| entry.used_by_curr_pic)
.map(|entry| current_poc + entry.delta_poc)
.collect();
(before, after)
}
}
#[derive(Debug, Clone)]
pub struct HevcSliceSegmentHeader {
pub slice_type: HevcSliceType,
pub slice_pic_parameter_set_id: u32,
pub pic_order_cnt_lsb: Option<u32>,
pub short_term_rps: Option<ShortTermRefPicSet>,
}
impl HevcSliceSegmentHeader {
pub fn parse(
reader: &mut BitReader<'_>,
sps: &HevcSps,
pps: &HevcPps,
nal_unit_type: HevcNalUnitType,
) -> Result<Self, HevcParamError> {
let first_slice_segment_in_pic_flag = reader.read_bit()? != 0;
if !first_slice_segment_in_pic_flag {
return Err(HevcParamError::Unsupported {
reason: "multi-slice pictures are not supported (first_slice_segment_in_pic_flag must be 1)",
});
}
if matches!(nal_unit_type, HevcNalUnitType::Idr | HevcNalUnitType::Cra) {
let _no_output_of_prior_pics_flag = reader.read_bit()?;
}
let slice_pic_parameter_set_id = reader.read_ue()?;
for _ in 0..pps.num_extra_slice_header_bits {
let _slice_reserved_flag = reader.read_bit()?;
}
let slice_type =
HevcSliceType::from_raw(reader.read_ue()?).ok_or(H264Error::FieldOverflow)?;
if matches!(slice_type, HevcSliceType::B) {
return Err(HevcParamError::Unsupported {
reason: "B-slices are not supported this round",
});
}
if pps.output_flag_present_flag {
let _pic_output_flag = reader.read_bit()?;
}
let (pic_order_cnt_lsb, short_term_rps) = if nal_unit_type.is_idr() {
(None, None)
} else {
let poc_lsb = reader.read_bits(sps.log2_max_pic_order_cnt_lsb)?;
let short_term_ref_pic_set_sps_flag = reader.read_bit()? != 0;
if short_term_ref_pic_set_sps_flag {
return Err(HevcParamError::Unsupported {
reason: "short_term_ref_pic_set_sps_flag == 1 references an SPS-level RPS \
list, which this crate's HevcSps::parse always leaves empty",
});
}
let rps = ShortTermRefPicSet::parse(reader)?;
(Some(poc_lsb), Some(rps))
};
Ok(Self {
slice_type,
slice_pic_parameter_set_id,
pic_order_cnt_lsb,
short_term_rps,
})
}
}
#[cfg(test)]
#[path = "hevc_slice_tests.rs"]
mod tests;