use crate::DecodeError;
use super::h264_slice::RefPicListModOp;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct H264RefMeta {
pub(super) frame_num: u32,
pub(super) poc: i32,
pub(super) top_field_order_cnt: i32,
pub(super) bottom_field_order_cnt: i32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct RefListEntry {
pub(super) slot: u32,
pub(super) pic_num: i32,
pub(super) poc: i32,
}
fn frame_num_wrap(ref_frame_num: u32, curr_frame_num: u32, max_frame_num: u32) -> i32 {
let (ref_fn, curr_fn, max_fn) = (
i64::from(ref_frame_num),
i64::from(curr_frame_num),
i64::from(max_frame_num),
);
let wrap = if ref_fn > curr_fn {
ref_fn - max_fn
} else {
ref_fn
};
i32::try_from(wrap).unwrap_or(i32::MIN)
}
fn pic_num(meta: H264RefMeta, curr_frame_num: u32, max_frame_num: u32) -> i32 {
frame_num_wrap(meta.frame_num, curr_frame_num, max_frame_num)
}
fn to_entries(
refs: &[(u32, H264RefMeta)],
curr_frame_num: u32,
max_frame_num: u32,
) -> Vec<RefListEntry> {
refs.iter()
.map(|&(slot, meta)| RefListEntry {
slot,
pic_num: pic_num(meta, curr_frame_num, max_frame_num),
poc: meta.poc,
})
.collect()
}
pub(super) fn build_default_list_p(
refs: &[(u32, H264RefMeta)],
curr_frame_num: u32,
max_frame_num: u32,
) -> Vec<RefListEntry> {
let mut list = to_entries(refs, curr_frame_num, max_frame_num);
list.sort_by_key(|e| std::cmp::Reverse(e.pic_num));
list
}
pub(super) fn build_default_lists_b(
refs: &[(u32, H264RefMeta)],
curr_frame_num: u32,
max_frame_num: u32,
curr_poc: i32,
) -> (Vec<RefListEntry>, Vec<RefListEntry>) {
let entries = to_entries(refs, curr_frame_num, max_frame_num);
let mut before: Vec<RefListEntry> = entries
.iter()
.copied()
.filter(|e| e.poc < curr_poc)
.collect();
before.sort_by_key(|e| std::cmp::Reverse(e.poc));
let mut after: Vec<RefListEntry> = entries
.iter()
.copied()
.filter(|e| e.poc > curr_poc)
.collect();
after.sort_by_key(|a| a.poc);
let mut list0 = before.clone(); list0.extend(after.iter().copied());
let mut list1 = after.clone(); list1.extend(before.iter().copied());
if list1.len() > 1 && list1 == list0 {
list1.swap(0, 1);
}
(list0, list1)
}
fn pad_to_length(list: &mut Vec<RefListEntry>, len: usize) -> Result<(), DecodeError> {
let Some(&tail_entry) = list.last() else {
return if len == 0 {
Ok(())
} else {
Err(DecodeError::InvalidInput)
};
};
while list.len() < len {
list.push(tail_entry);
}
Ok(())
}
pub(super) fn apply_modifications(
list: &mut Vec<RefListEntry>,
ops: &[RefPicListModOp],
curr_frame_num: u32,
max_frame_num: u32,
num_ref_idx_active: usize,
) -> Result<(), DecodeError> {
let max_pic_num = i64::from(max_frame_num);
let mut pred = i64::from(curr_frame_num);
let mut idx = 0usize;
for op in ops {
let diff = i64::from(op.abs_diff_pic_num_minus1) + 1;
let no_wrap = if op.add {
let v = pred + diff;
if v >= max_pic_num { v - max_pic_num } else { v }
} else {
let v = pred - diff;
if v < 0 { v + max_pic_num } else { v }
};
pred = no_wrap;
let pic_num_lx = if no_wrap > i64::from(curr_frame_num) {
no_wrap - max_pic_num
} else {
no_wrap
};
let pic_num_lx = i32::try_from(pic_num_lx).unwrap_or(i32::MIN);
let found_pos = list
.iter()
.skip(idx)
.position(|e| e.pic_num == pic_num_lx)
.map(|p| p + idx);
let Some(found_pos) = found_pos else {
return Err(DecodeError::InvalidInput);
};
let entry = list.remove(found_pos);
list.insert(idx, entry);
idx += 1;
if let Some(dup_pos) = list.iter().skip(idx).position(|e| e.slot == entry.slot) {
list.remove(idx + dup_pos);
}
}
pad_to_length(list, num_ref_idx_active)?;
list.truncate(num_ref_idx_active);
Ok(())
}
pub(super) fn sliding_window_evict(
refs: &[(u32, H264RefMeta)],
curr_frame_num: u32,
max_frame_num: u32,
max_num_ref_frames: u32,
) -> Option<u32> {
if refs.is_empty() || refs.len() < max_num_ref_frames.max(1) as usize {
return None;
}
refs.iter()
.min_by_key(|&&(_, meta)| frame_num_wrap(meta.frame_num, curr_frame_num, max_frame_num))
.map(|&(slot, _)| slot)
}
#[cfg(test)]
#[path = "h264_refs_tests.rs"]
mod tests;