#![allow(dead_code)]
#![allow(clippy::too_many_arguments)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RefMarking {
ShortTerm,
LongTerm,
Unused,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PicStructure {
TopField,
BottomField,
Frame,
FieldPair,
}
impl PicStructure {
pub fn is_field(self) -> bool {
matches!(self, PicStructure::TopField | PicStructure::BottomField)
}
pub fn is_bottom(self) -> bool {
matches!(self, PicStructure::BottomField)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FieldParity {
Top,
Bottom,
}
impl FieldParity {
fn flip(self) -> FieldParity {
match self {
FieldParity::Top => FieldParity::Bottom,
FieldParity::Bottom => FieldParity::Top,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RefFieldEntry {
pub dpb_key: u32,
pub parity: FieldParity,
}
#[derive(Debug, Clone)]
pub struct DpbEntry {
pub frame_num: u32,
pub top_field_order_cnt: i32,
pub bottom_field_order_cnt: i32,
pub pic_order_cnt: i32,
pub structure: PicStructure,
pub marking: RefMarking,
pub long_term_frame_idx: u32,
pub dpb_key: u32,
pub field_markings: [RefMarking; 2],
}
impl DpbEntry {
pub fn pic_num(
&self,
current_frame_num: u32,
max_frame_num: u32,
current_is_field: bool,
current_bottom: bool,
) -> i32 {
let frame_num_wrap = if self.frame_num > current_frame_num {
self.frame_num as i64 - max_frame_num as i64
} else {
self.frame_num as i64
};
if !current_is_field {
frame_num_wrap as i32
} else {
let same_parity = match self.structure {
PicStructure::TopField => !current_bottom,
PicStructure::BottomField => current_bottom,
PicStructure::Frame | PicStructure::FieldPair => true,
};
if same_parity {
(2 * frame_num_wrap + 1) as i32
} else {
(2 * frame_num_wrap) as i32
}
}
}
fn field_pic_num(
&self,
field_parity: FieldParity,
current_frame_num: u32,
max_frame_num: u32,
current_bottom: bool,
) -> i32 {
let frame_num_wrap = if self.frame_num > current_frame_num {
self.frame_num as i64 - max_frame_num as i64
} else {
self.frame_num as i64
};
let same_parity = (field_parity == FieldParity::Bottom) == current_bottom;
if same_parity {
(2 * frame_num_wrap + 1) as i32
} else {
(2 * frame_num_wrap) as i32
}
}
fn field_long_term_pic_num(&self, field_parity: FieldParity, current_bottom: bool) -> i32 {
let same_parity = (field_parity == FieldParity::Bottom) == current_bottom;
if same_parity {
(2 * self.long_term_frame_idx + 1) as i32
} else {
(2 * self.long_term_frame_idx) as i32
}
}
pub fn long_term_pic_num(&self, current_is_field: bool, current_bottom: bool) -> i32 {
if !current_is_field {
self.long_term_frame_idx as i32
} else {
let same_parity = match self.structure {
PicStructure::TopField => !current_bottom,
PicStructure::BottomField => current_bottom,
PicStructure::Frame | PicStructure::FieldPair => true,
};
if same_parity {
(2 * self.long_term_frame_idx + 1) as i32
} else {
(2 * self.long_term_frame_idx) as i32
}
}
}
fn is_short_term(&self) -> bool {
matches!(self.marking, RefMarking::ShortTerm)
}
pub fn is_long_term(&self) -> bool {
matches!(self.marking, RefMarking::LongTerm)
}
fn is_ref(&self) -> bool {
!matches!(self.marking, RefMarking::Unused)
}
fn field_marking(&self, parity: FieldParity) -> RefMarking {
self.field_markings[usize::from(parity == FieldParity::Bottom)]
}
fn set_field_marking(&mut self, parity: FieldParity, m: RefMarking) {
self.field_markings[usize::from(parity == FieldParity::Bottom)] = m;
self.marking = match self.structure {
PicStructure::TopField => self.field_markings[0],
PicStructure::BottomField => self.field_markings[1],
PicStructure::Frame | PicStructure::FieldPair => {
if self.field_markings[0] == self.field_markings[1] {
self.field_markings[0]
} else {
RefMarking::Unused
}
}
};
}
fn mark_all(&mut self, m: RefMarking) {
self.marking = m;
self.sync_field_markings();
}
pub fn sync_field_markings(&mut self) {
self.field_markings = match self.structure {
PicStructure::TopField => [self.marking, RefMarking::Unused],
PicStructure::BottomField => [RefMarking::Unused, self.marking],
PicStructure::Frame | PicStructure::FieldPair => [self.marking, self.marking],
};
}
fn any_field_is(&self, m: RefMarking) -> bool {
[FieldParity::Top, FieldParity::Bottom]
.into_iter()
.any(|p| self.has_field(p) && self.field_marking(p) == m)
}
pub fn is_any_field_ref(&self) -> bool {
self.any_field_is(RefMarking::ShortTerm) || self.any_field_is(RefMarking::LongTerm)
}
fn has_field_marked(&self, parity: FieldParity, m: RefMarking) -> bool {
self.has_field(parity) && self.field_marking(parity) == m
}
fn has_field(&self, parity: FieldParity) -> bool {
match self.structure {
PicStructure::Frame | PicStructure::FieldPair => true,
PicStructure::TopField => parity == FieldParity::Top,
PicStructure::BottomField => parity == FieldParity::Bottom,
}
}
fn field_poc(&self, parity: FieldParity) -> i32 {
match parity {
FieldParity::Top => self.top_field_order_cnt,
FieldParity::Bottom => self.bottom_field_order_cnt,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RplmOp {
Subtract(u32),
Add(u32),
LongTerm(u32),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MmcoOp {
MarkShortTermUnused(u32),
MarkLongTermUnused(u32),
AssignLongTerm(u32, u32),
SetMaxLongTermIdx(u32),
MarkAllUnused,
AssignCurrentLongTerm(u32),
}
pub fn init_ref_pic_list_p(
dpb: &[DpbEntry],
current_frame_num: u32,
max_frame_num: u32,
current_structure: PicStructure,
current_bottom: bool,
) -> Vec<u32> {
let current_is_field = current_structure.is_field();
let mut short: Vec<(i32, u32)> = dpb
.iter()
.filter(|e| e.is_short_term())
.map(|e| {
(
e.pic_num(
current_frame_num,
max_frame_num,
current_is_field,
current_bottom,
),
e.dpb_key,
)
})
.collect();
short.sort_by_key(|e| std::cmp::Reverse(e.0));
let mut long: Vec<(i32, u32)> = dpb
.iter()
.filter(|e| e.is_long_term())
.map(|e| {
(
e.long_term_pic_num(current_is_field, current_bottom),
e.dpb_key,
)
})
.collect();
long.sort_by_key(|a| a.0);
let mut out: Vec<u32> = Vec::with_capacity(short.len() + long.len());
out.extend(short.into_iter().map(|(_, k)| k));
out.extend(long.into_iter().map(|(_, k)| k));
out
}
pub fn init_ref_pic_lists_b(
dpb: &[DpbEntry],
current_poc: i32,
current_structure: PicStructure,
current_bottom: bool,
) -> (Vec<u32>, Vec<u32>) {
let current_is_field = current_structure.is_field();
let mut st_less: Vec<(i32, u32)> = Vec::new();
let mut st_geq: Vec<(i32, u32)> = Vec::new();
let mut st_greater: Vec<(i32, u32)> = Vec::new();
let mut st_leq: Vec<(i32, u32)> = Vec::new();
for e in dpb.iter().filter(|e| e.is_short_term()) {
let poc = e.pic_order_cnt;
if poc < current_poc {
st_less.push((poc, e.dpb_key));
} else {
st_geq.push((poc, e.dpb_key));
}
if poc > current_poc {
st_greater.push((poc, e.dpb_key));
} else {
st_leq.push((poc, e.dpb_key));
}
}
st_less.sort_by_key(|e| std::cmp::Reverse(e.0));
st_geq.sort_by_key(|a| a.0);
st_greater.sort_by_key(|a| a.0);
st_leq.sort_by_key(|e| std::cmp::Reverse(e.0));
let mut long: Vec<(i32, u32)> = dpb
.iter()
.filter(|e| e.is_long_term())
.map(|e| {
(
e.long_term_pic_num(current_is_field, current_bottom),
e.dpb_key,
)
})
.collect();
long.sort_by_key(|a| a.0);
let mut list0: Vec<u32> = Vec::new();
list0.extend(st_less.iter().map(|(_, k)| *k));
list0.extend(st_geq.iter().map(|(_, k)| *k));
list0.extend(long.iter().map(|(_, k)| *k));
let mut list1: Vec<u32> = Vec::new();
list1.extend(st_greater.iter().map(|(_, k)| *k));
list1.extend(st_leq.iter().map(|(_, k)| *k));
list1.extend(long.iter().map(|(_, k)| *k));
if list1.len() > 1 && list1 == list0 {
list1.swap(0, 1);
}
(list0, list1)
}
fn interleave_fields(
frame_order: &[u32],
dpb: &[DpbEntry],
current_parity: FieldParity,
class: RefMarking,
) -> Vec<RefFieldEntry> {
let lookup = |key: u32| -> Option<&DpbEntry> { dpb.iter().find(|e| e.dpb_key == key) };
let mut out: Vec<RefFieldEntry> = Vec::with_capacity(frame_order.len() * 2);
let mut used_top = vec![false; frame_order.len()];
let mut used_bot = vec![false; frame_order.len()];
let mut want = current_parity;
let mut cursor_top = 0usize;
let mut cursor_bot = 0usize;
loop {
let (cursor, used) = match want {
FieldParity::Top => (&mut cursor_top, &mut used_top),
FieldParity::Bottom => (&mut cursor_bot, &mut used_bot),
};
let mut found: Option<usize> = None;
while *cursor < frame_order.len() {
let i = *cursor;
*cursor += 1;
if used[i] {
continue;
}
if lookup(frame_order[i])
.map(|e| e.has_field_marked(want, class))
.unwrap_or(false)
{
found = Some(i);
break;
}
}
match found {
Some(i) => {
used[i] = true;
out.push(RefFieldEntry {
dpb_key: frame_order[i],
parity: want,
});
want = want.flip();
}
None => {
let other = want.flip();
let other_has_more = frame_order.iter().enumerate().any(|(i, &k)| {
let used_other = match other {
FieldParity::Top => used_top[i],
FieldParity::Bottom => used_bot[i],
};
!used_other
&& lookup(k)
.map(|e| e.has_field_marked(other, class))
.unwrap_or(false)
});
if other_has_more {
want = other;
} else {
break;
}
}
}
}
out
}
pub fn init_ref_pic_list_p_field(
dpb: &[DpbEntry],
current_frame_num: u32,
max_frame_num: u32,
current_bottom: bool,
) -> Vec<RefFieldEntry> {
let current_parity = if current_bottom {
FieldParity::Bottom
} else {
FieldParity::Top
};
let mut short: Vec<(i32, u32)> = dpb
.iter()
.filter(|e| e.any_field_is(RefMarking::ShortTerm))
.map(|e| {
(
e.pic_num(current_frame_num, max_frame_num, false, false),
e.dpb_key,
)
})
.collect();
short.sort_by_key(|e| std::cmp::Reverse(e.0));
let short_frames: Vec<u32> = short.into_iter().map(|(_, k)| k).collect();
let mut long: Vec<(u32, u32)> = dpb
.iter()
.filter(|e| e.any_field_is(RefMarking::LongTerm))
.map(|e| (e.long_term_frame_idx, e.dpb_key))
.collect();
long.sort_by_key(|a| a.0);
let long_frames: Vec<u32> = long.into_iter().map(|(_, k)| k).collect();
let mut out = interleave_fields(&short_frames, dpb, current_parity, RefMarking::ShortTerm);
out.extend(interleave_fields(
&long_frames,
dpb,
current_parity,
RefMarking::LongTerm,
));
out
}
pub fn init_ref_pic_lists_b_field(
dpb: &[DpbEntry],
current_poc: i32,
current_bottom: bool,
) -> (Vec<RefFieldEntry>, Vec<RefFieldEntry>) {
let current_parity = if current_bottom {
FieldParity::Bottom
} else {
FieldParity::Top
};
let mut l0_le: Vec<(i32, u32)> = Vec::new();
let mut l0_gt: Vec<(i32, u32)> = Vec::new();
let mut l1_gt: Vec<(i32, u32)> = Vec::new();
let mut l1_le: Vec<(i32, u32)> = Vec::new();
for e in dpb.iter().filter(|e| e.any_field_is(RefMarking::ShortTerm)) {
let poc = e.pic_order_cnt;
if poc <= current_poc {
l0_le.push((poc, e.dpb_key));
l1_le.push((poc, e.dpb_key));
} else {
l0_gt.push((poc, e.dpb_key));
l1_gt.push((poc, e.dpb_key));
}
}
l0_le.sort_by_key(|e| std::cmp::Reverse(e.0));
l0_gt.sort_by_key(|a| a.0);
l1_gt.sort_by_key(|a| a.0);
l1_le.sort_by_key(|e| std::cmp::Reverse(e.0));
let mut l0_frames: Vec<u32> = Vec::new();
l0_frames.extend(l0_le.iter().map(|(_, k)| *k));
l0_frames.extend(l0_gt.iter().map(|(_, k)| *k));
let mut l1_frames: Vec<u32> = Vec::new();
l1_frames.extend(l1_gt.iter().map(|(_, k)| *k));
l1_frames.extend(l1_le.iter().map(|(_, k)| *k));
let mut long: Vec<(u32, u32)> = dpb
.iter()
.filter(|e| e.any_field_is(RefMarking::LongTerm))
.map(|e| (e.long_term_frame_idx, e.dpb_key))
.collect();
long.sort_by_key(|a| a.0);
let long_frames: Vec<u32> = long.into_iter().map(|(_, k)| k).collect();
let mut list0 = interleave_fields(&l0_frames, dpb, current_parity, RefMarking::ShortTerm);
list0.extend(interleave_fields(
&long_frames,
dpb,
current_parity,
RefMarking::LongTerm,
));
let mut list1 = interleave_fields(&l1_frames, dpb, current_parity, RefMarking::ShortTerm);
list1.extend(interleave_fields(
&long_frames,
dpb,
current_parity,
RefMarking::LongTerm,
));
if list1.len() > 1 && list1 == list0 {
list1.swap(0, 1);
}
(list0, list1)
}
pub fn modify_ref_pic_list(
list: &mut Vec<u32>,
ops: &[RplmOp],
dpb: &[DpbEntry],
num_active: u32,
current_frame_num: u32,
max_frame_num: u32,
current_is_field: bool,
current_bottom: bool,
) {
let target = num_active as usize;
if list.len() > target {
list.truncate(target);
}
while list.len() < target {
list.push(u32::MAX); }
if ops.is_empty() {
return;
}
let curr_pic_num: i32 = if current_is_field {
(2 * current_frame_num + 1) as i32
} else {
current_frame_num as i32
};
let max_pic_num: i32 = if current_is_field {
(2 * max_frame_num) as i32
} else {
max_frame_num as i32
};
let mut ref_idx_lx: usize = 0;
let mut pic_num_lx_pred: i32 = curr_pic_num;
for op in ops {
match *op {
RplmOp::Subtract(abs_diff) | RplmOp::Add(abs_diff) => {
let delta = (abs_diff + 1) as i32;
let pic_num_lx_no_wrap: i32 = if matches!(op, RplmOp::Subtract(_)) {
if pic_num_lx_pred - delta < 0 {
pic_num_lx_pred - delta + max_pic_num
} else {
pic_num_lx_pred - delta
}
} else {
if pic_num_lx_pred + delta >= max_pic_num {
pic_num_lx_pred + delta - max_pic_num
} else {
pic_num_lx_pred + delta
}
};
pic_num_lx_pred = pic_num_lx_no_wrap;
let pic_num_lx: i32 = if pic_num_lx_no_wrap > curr_pic_num {
pic_num_lx_no_wrap - max_pic_num
} else {
pic_num_lx_no_wrap
};
let target_key = dpb
.iter()
.find(|e| {
e.is_short_term()
&& e.pic_num(
current_frame_num,
max_frame_num,
current_is_field,
current_bottom,
) == pic_num_lx
})
.map(|e| e.dpb_key)
.unwrap_or(u32::MAX);
splice_into_list(list, ref_idx_lx, num_active as usize, target_key, |k| {
k != target_key
});
ref_idx_lx += 1;
}
RplmOp::LongTerm(long_term_pic_num) => {
let target_key = dpb
.iter()
.find(|e| {
e.is_long_term()
&& e.long_term_pic_num(current_is_field, current_bottom)
== long_term_pic_num as i32
})
.map(|e| e.dpb_key)
.unwrap_or(u32::MAX);
splice_into_list(list, ref_idx_lx, num_active as usize, target_key, |k| {
k != target_key
});
ref_idx_lx += 1;
}
}
}
}
fn splice_into_list<F>(
list: &mut Vec<u32>,
ref_idx_lx: usize,
num_active: usize,
target_key: u32,
filter_retain: F,
) where
F: Fn(u32) -> bool,
{
if ref_idx_lx >= num_active {
return;
}
let mut tmp: Vec<u32> = Vec::with_capacity(num_active + 1);
tmp.extend_from_slice(&list[..ref_idx_lx]);
tmp.push(target_key);
for &k in &list[ref_idx_lx..] {
if filter_retain(k) {
tmp.push(k);
}
}
while tmp.len() < num_active {
tmp.push(u32::MAX);
}
tmp.truncate(num_active);
*list = tmp;
}
fn no_ref_field() -> RefFieldEntry {
RefFieldEntry {
dpb_key: u32::MAX,
parity: FieldParity::Top,
}
}
pub fn modify_ref_pic_list_field(
list: &mut Vec<RefFieldEntry>,
ops: &[RplmOp],
dpb: &[DpbEntry],
num_active: u32,
current_frame_num: u32,
max_frame_num: u32,
current_bottom: bool,
) {
let target = num_active as usize;
if list.len() > target {
list.truncate(target);
}
while list.len() < target {
list.push(no_ref_field());
}
if ops.is_empty() {
return;
}
let curr_pic_num: i32 = (2 * current_frame_num + 1) as i32;
let max_pic_num: i32 = (2 * max_frame_num) as i32;
let mut ref_idx_lx: usize = 0;
let mut pic_num_lx_pred: i32 = curr_pic_num;
for op in ops {
match *op {
RplmOp::Subtract(abs_diff) | RplmOp::Add(abs_diff) => {
let delta = (abs_diff + 1) as i32;
let pic_num_lx_no_wrap: i32 = if matches!(op, RplmOp::Subtract(_)) {
if pic_num_lx_pred - delta < 0 {
pic_num_lx_pred - delta + max_pic_num
} else {
pic_num_lx_pred - delta
}
} else if pic_num_lx_pred + delta >= max_pic_num {
pic_num_lx_pred + delta - max_pic_num
} else {
pic_num_lx_pred + delta
};
pic_num_lx_pred = pic_num_lx_no_wrap;
let pic_num_lx: i32 = if pic_num_lx_no_wrap > curr_pic_num {
pic_num_lx_no_wrap - max_pic_num
} else {
pic_num_lx_no_wrap
};
let target = find_field_short_term(
dpb,
pic_num_lx,
current_frame_num,
max_frame_num,
current_bottom,
);
splice_field_into_list(list, ref_idx_lx, num_active as usize, target);
ref_idx_lx += 1;
}
RplmOp::LongTerm(long_term_pic_num) => {
let target = find_field_long_term(dpb, long_term_pic_num as i32, current_bottom);
splice_field_into_list(list, ref_idx_lx, num_active as usize, target);
ref_idx_lx += 1;
}
}
}
}
fn find_field_short_term(
dpb: &[DpbEntry],
pic_num_lx: i32,
current_frame_num: u32,
max_frame_num: u32,
current_bottom: bool,
) -> RefFieldEntry {
for e in dpb.iter() {
for parity in [FieldParity::Top, FieldParity::Bottom] {
if e.has_field_marked(parity, RefMarking::ShortTerm)
&& e.field_pic_num(parity, current_frame_num, max_frame_num, current_bottom)
== pic_num_lx
{
return RefFieldEntry {
dpb_key: e.dpb_key,
parity,
};
}
}
}
no_ref_field()
}
fn find_field_long_term(
dpb: &[DpbEntry],
long_term_pic_num: i32,
current_bottom: bool,
) -> RefFieldEntry {
for e in dpb.iter() {
for parity in [FieldParity::Top, FieldParity::Bottom] {
if e.has_field_marked(parity, RefMarking::LongTerm)
&& e.field_long_term_pic_num(parity, current_bottom) == long_term_pic_num
{
return RefFieldEntry {
dpb_key: e.dpb_key,
parity,
};
}
}
}
no_ref_field()
}
fn splice_field_into_list(
list: &mut Vec<RefFieldEntry>,
ref_idx_lx: usize,
num_active: usize,
target: RefFieldEntry,
) {
if ref_idx_lx >= num_active {
return;
}
let mut tmp: Vec<RefFieldEntry> = Vec::with_capacity(num_active + 1);
tmp.extend_from_slice(&list[..ref_idx_lx]);
tmp.push(target);
for &e in &list[ref_idx_lx..] {
if e != target {
tmp.push(e);
}
}
while tmp.len() < num_active {
tmp.push(no_ref_field());
}
tmp.truncate(num_active);
*list = tmp;
}
pub fn sliding_window_marking(
dpb: &mut [DpbEntry],
max_num_ref_frames: u32,
current_frame_num: u32,
max_frame_num: u32,
current_entry: Option<&DpbEntry>,
) {
if let Some(cur) = current_entry {
if cur.structure.is_field() {
let first_field_is_short_term_ref = dpb.iter().any(|e| {
e.structure.is_field()
&& e.frame_num == cur.frame_num
&& e.structure.is_bottom() != cur.structure.is_bottom()
&& e.is_short_term()
});
if first_field_is_short_term_ref {
return;
}
}
}
let units = ref_units(dpb);
let num_short = units
.iter()
.filter(|u| {
u.iter()
.any(|&i| dpb[i].any_field_is(RefMarking::ShortTerm))
})
.count() as u32;
let num_long = units
.iter()
.filter(|u| u.iter().any(|&i| dpb[i].any_field_is(RefMarking::LongTerm)))
.count() as u32;
let cap = max_num_ref_frames.max(1);
if num_short + num_long < cap {
return; }
if num_short == 0 {
return; }
let fnw = |frame_num: u32| -> i64 {
if frame_num > current_frame_num {
frame_num as i64 - max_frame_num as i64
} else {
frame_num as i64
}
};
let best = units
.iter()
.filter(|u| {
u.iter()
.any(|&i| dpb[i].any_field_is(RefMarking::ShortTerm))
})
.min_by_key(|u| fnw(dpb[u[0]].frame_num));
if let Some(unit) = best {
for &i in unit {
dpb[i].mark_all(RefMarking::Unused);
}
}
}
fn ref_units(dpb: &[DpbEntry]) -> Vec<Vec<usize>> {
let mut units: Vec<Vec<usize>> = Vec::new();
let mut used = vec![false; dpb.len()];
for i in 0..dpb.len() {
if used[i] || !dpb[i].is_any_field_ref() {
continue;
}
used[i] = true;
let mut unit = vec![i];
if dpb[i].structure.is_field() {
for (j, e) in dpb.iter().enumerate().skip(i + 1) {
if !used[j]
&& e.is_any_field_ref()
&& e.structure.is_field()
&& e.frame_num == dpb[i].frame_num
&& e.structure.is_bottom() != dpb[i].structure.is_bottom()
{
used[j] = true;
unit.push(j);
break;
}
}
}
units.push(unit);
}
units
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FieldPairKeys {
pub unit_key: u32,
pub top_key: u32,
pub bottom_key: u32,
}
pub fn collapse_field_pairs(dpb: &[DpbEntry]) -> (Vec<DpbEntry>, Vec<FieldPairKeys>) {
let mut out: Vec<DpbEntry> = Vec::with_capacity(dpb.len());
let mut pairings: Vec<FieldPairKeys> = Vec::new();
for unit in ref_units(dpb) {
match unit.as_slice() {
[i] => {
let e = &dpb[*i];
match e.structure {
PicStructure::Frame | PicStructure::FieldPair => out.push(e.clone()),
PicStructure::TopField | PicStructure::BottomField => {}
}
}
[i, j] => {
let (top, bottom) = if dpb[*i].structure.is_bottom() {
(&dpb[*j], &dpb[*i])
} else {
(&dpb[*i], &dpb[*j])
};
let top_m = top.field_marking(FieldParity::Top);
let bottom_m = bottom.field_marking(FieldParity::Bottom);
let marking = if top_m == bottom_m {
top_m
} else {
RefMarking::Unused
};
if marking == RefMarking::Unused {
continue;
}
let top_poc = top.field_poc(FieldParity::Top);
let bottom_poc = bottom.field_poc(FieldParity::Bottom);
out.push(DpbEntry {
frame_num: top.frame_num,
top_field_order_cnt: top_poc,
bottom_field_order_cnt: bottom_poc,
pic_order_cnt: top_poc.min(bottom_poc),
structure: PicStructure::FieldPair,
marking,
long_term_frame_idx: top.long_term_frame_idx,
dpb_key: top.dpb_key,
field_markings: [top_m, bottom_m],
});
pairings.push(FieldPairKeys {
unit_key: top.dpb_key,
top_key: top.dpb_key,
bottom_key: bottom.dpb_key,
});
}
_ => unreachable!("ref_units yields 1- or 2-entry units"),
}
}
(out, pairings)
}
pub fn apply_mmco(
dpb: &mut [DpbEntry],
ops: &[MmcoOp],
current_entry_ref: &mut DpbEntry,
current_frame_num: u32,
max_frame_num: u32,
) -> bool {
let mut mmco5 = false;
let current_is_field = current_entry_ref.structure.is_field();
let current_bottom = current_entry_ref.structure.is_bottom();
let curr_pic_num: i32 = if current_is_field {
(2 * current_frame_num + 1) as i32
} else {
current_frame_num as i32
};
let find_st_field = |dpb: &[DpbEntry], pic_num_x: i32| -> Option<(usize, FieldParity)> {
for (i, e) in dpb.iter().enumerate() {
for parity in [FieldParity::Top, FieldParity::Bottom] {
if e.has_field_marked(parity, RefMarking::ShortTerm)
&& e.field_pic_num(parity, current_frame_num, max_frame_num, current_bottom)
== pic_num_x
{
return Some((i, parity));
}
}
}
None
};
let evict_ltfi = |dpb: &mut [DpbEntry],
ltfi: u32,
spare: Option<(usize, FieldParity)>,
spare_frame_num: Option<u32>| {
for (j, e) in dpb.iter_mut().enumerate() {
if e.long_term_frame_idx != ltfi {
continue;
}
let top_lt = e.has_field_marked(FieldParity::Top, RefMarking::LongTerm);
let bot_lt = e.has_field_marked(FieldParity::Bottom, RefMarking::LongTerm);
if !top_lt && !bot_lt {
continue;
}
if top_lt && bot_lt {
e.mark_all(RefMarking::Unused);
continue;
}
let lt_parity = if top_lt {
FieldParity::Top
} else {
FieldParity::Bottom
};
if let Some((si, sp)) = spare {
if si == j && sp != lt_parity {
continue;
}
}
if let Some(sfn) = spare_frame_num {
if e.structure.is_field() && e.frame_num == sfn {
continue;
}
}
e.set_field_marking(lt_parity, RefMarking::Unused);
}
};
for op in ops {
match *op {
MmcoOp::MarkShortTermUnused(diff) => {
let pic_num_x = curr_pic_num - (diff as i32 + 1);
if current_is_field {
if let Some((i, parity)) = find_st_field(dpb, pic_num_x) {
dpb[i].set_field_marking(parity, RefMarking::Unused);
}
} else {
for e in dpb.iter_mut() {
if e.is_short_term()
&& e.pic_num(current_frame_num, max_frame_num, false, false)
== pic_num_x
{
e.mark_all(RefMarking::Unused);
}
}
}
}
MmcoOp::MarkLongTermUnused(ltpn) => {
if current_is_field {
let mut found: Option<(usize, FieldParity)> = None;
'outer: for (i, e) in dpb.iter().enumerate() {
for parity in [FieldParity::Top, FieldParity::Bottom] {
if e.has_field_marked(parity, RefMarking::LongTerm)
&& e.field_long_term_pic_num(parity, current_bottom) == ltpn as i32
{
found = Some((i, parity));
break 'outer;
}
}
}
if let Some((i, parity)) = found {
dpb[i].set_field_marking(parity, RefMarking::Unused);
}
} else {
for e in dpb.iter_mut() {
if e.is_long_term() && e.long_term_pic_num(false, false) == ltpn as i32 {
e.mark_all(RefMarking::Unused);
}
}
}
}
MmcoOp::AssignLongTerm(diff, ltfi) => {
let pic_num_x = curr_pic_num - (diff as i32 + 1);
if current_is_field {
let target = find_st_field(dpb, pic_num_x);
let spare_frame_num = target.map(|(i, _)| dpb[i].frame_num);
evict_ltfi(dpb, ltfi, target, spare_frame_num);
if let Some((i, parity)) = target {
dpb[i].set_field_marking(parity, RefMarking::LongTerm);
dpb[i].long_term_frame_idx = ltfi;
}
} else {
evict_ltfi(dpb, ltfi, None, None);
for e in dpb.iter_mut() {
if e.is_short_term()
&& e.pic_num(current_frame_num, max_frame_num, false, false)
== pic_num_x
{
e.mark_all(RefMarking::LongTerm);
e.long_term_frame_idx = ltfi;
}
}
}
}
MmcoOp::SetMaxLongTermIdx(max_plus1) => {
let clear_all = max_plus1 == 0;
let max_ltfi = max_plus1.saturating_sub(1);
for e in dpb.iter_mut() {
if !e.any_field_is(RefMarking::LongTerm) {
continue;
}
if clear_all || e.long_term_frame_idx > max_ltfi {
for parity in [FieldParity::Top, FieldParity::Bottom] {
if e.has_field_marked(parity, RefMarking::LongTerm) {
e.set_field_marking(parity, RefMarking::Unused);
}
}
}
}
}
MmcoOp::MarkAllUnused => {
for e in dpb.iter_mut() {
e.mark_all(RefMarking::Unused);
}
mmco5 = true;
}
MmcoOp::AssignCurrentLongTerm(ltfi) => {
let spare_frame_num = if current_is_field {
Some(current_entry_ref.frame_num)
} else {
None
};
evict_ltfi(dpb, ltfi, None, spare_frame_num);
current_entry_ref.marking = RefMarking::LongTerm;
current_entry_ref.long_term_frame_idx = ltfi;
current_entry_ref.sync_field_markings();
if current_is_field {
for e in dpb.iter_mut() {
if e.structure.is_field()
&& e.frame_num == current_entry_ref.frame_num
&& e.structure.is_bottom() != current_bottom
&& e.any_field_is(RefMarking::LongTerm)
{
e.long_term_frame_idx = ltfi;
}
}
}
}
}
}
mmco5
}
#[allow(clippy::too_many_arguments)]
pub fn perform_marking(
dpb: &mut [DpbEntry],
current_entry: &mut DpbEntry,
max_num_ref_frames: u32,
is_idr: bool,
long_term_reference_flag_for_idr: bool,
_no_output_of_prior_pics_flag: bool,
adaptive_ops: Option<&[MmcoOp]>,
current_frame_num: u32,
max_frame_num: u32,
) -> bool {
if is_idr {
for e in dpb.iter_mut() {
e.mark_all(RefMarking::Unused);
}
if long_term_reference_flag_for_idr {
current_entry.marking = RefMarking::LongTerm;
current_entry.long_term_frame_idx = 0;
} else {
current_entry.marking = RefMarking::ShortTerm;
}
current_entry.sync_field_markings();
return false;
}
match adaptive_ops {
Some(ops) => {
let mmco5 = apply_mmco(dpb, ops, current_entry, current_frame_num, max_frame_num);
if !matches!(current_entry.marking, RefMarking::LongTerm) {
current_entry.marking = RefMarking::ShortTerm;
}
current_entry.sync_field_markings();
mmco5
}
None => {
sliding_window_marking(
dpb,
max_num_ref_frames,
current_frame_num,
max_frame_num,
Some(current_entry),
);
current_entry.marking = RefMarking::ShortTerm;
current_entry.sync_field_markings();
false
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn st_frame(frame_num: u32, poc: i32, key: u32) -> DpbEntry {
let mut e = DpbEntry {
frame_num,
top_field_order_cnt: poc,
bottom_field_order_cnt: poc,
pic_order_cnt: poc,
structure: PicStructure::Frame,
marking: RefMarking::ShortTerm,
long_term_frame_idx: 0,
dpb_key: key,
field_markings: [RefMarking::Unused; 2],
};
e.sync_field_markings();
e
}
fn lt_frame(ltfi: u32, poc: i32, key: u32) -> DpbEntry {
let mut e = DpbEntry {
frame_num: 0,
top_field_order_cnt: poc,
bottom_field_order_cnt: poc,
pic_order_cnt: poc,
structure: PicStructure::Frame,
marking: RefMarking::LongTerm,
long_term_frame_idx: ltfi,
dpb_key: key,
field_markings: [RefMarking::Unused; 2],
};
e.sync_field_markings();
e
}
fn st_pair(frame_num: u32, top_poc: i32, bot_poc: i32, key: u32) -> DpbEntry {
let mut e = DpbEntry {
frame_num,
top_field_order_cnt: top_poc,
bottom_field_order_cnt: bot_poc,
pic_order_cnt: top_poc.min(bot_poc),
structure: PicStructure::FieldPair,
marking: RefMarking::ShortTerm,
long_term_frame_idx: 0,
dpb_key: key,
field_markings: [RefMarking::Unused; 2],
};
e.sync_field_markings();
e
}
fn st_single_field(frame_num: u32, parity: FieldParity, poc: i32, key: u32) -> DpbEntry {
let structure = match parity {
FieldParity::Top => PicStructure::TopField,
FieldParity::Bottom => PicStructure::BottomField,
};
let mut e = DpbEntry {
frame_num,
top_field_order_cnt: poc,
bottom_field_order_cnt: poc,
pic_order_cnt: poc,
structure,
marking: RefMarking::ShortTerm,
long_term_frame_idx: 0,
dpb_key: key,
field_markings: [RefMarking::Unused; 2],
};
e.sync_field_markings();
e
}
fn lt_pair(ltfi: u32, poc: i32, key: u32) -> DpbEntry {
let mut e = DpbEntry {
frame_num: 0,
top_field_order_cnt: poc,
bottom_field_order_cnt: poc,
pic_order_cnt: poc,
structure: PicStructure::FieldPair,
marking: RefMarking::LongTerm,
long_term_frame_idx: ltfi,
dpb_key: key,
field_markings: [RefMarking::Unused; 2],
};
e.sync_field_markings();
e
}
fn top(key: u32) -> RefFieldEntry {
RefFieldEntry {
dpb_key: key,
parity: FieldParity::Top,
}
}
fn bot(key: u32) -> RefFieldEntry {
RefFieldEntry {
dpb_key: key,
parity: FieldParity::Bottom,
}
}
#[test]
fn pic_num_no_wrap() {
let e = st_frame(3, 0, 1);
let pn = e.pic_num(5, 16, false, false);
assert_eq!(pn, 3);
}
#[test]
fn pic_num_wrap() {
let e = st_frame(15, 0, 1);
let pn = e.pic_num(2, 16, false, false);
assert_eq!(pn, -1);
}
#[test]
fn long_term_pic_num_frame() {
let e = lt_frame(7, 0, 1);
assert_eq!(e.long_term_pic_num(false, false), 7);
}
#[test]
fn long_term_pic_num_field_same_parity() {
let mut e = lt_frame(3, 0, 1);
e.structure = PicStructure::TopField;
assert_eq!(e.long_term_pic_num(true, false), 2 * 3 + 1);
}
#[test]
fn long_term_pic_num_field_opposite_parity() {
let mut e = lt_frame(3, 0, 1);
e.structure = PicStructure::TopField;
assert_eq!(e.long_term_pic_num(true, true), 2 * 3);
}
#[test]
fn init_p_list_short_only() {
let dpb = vec![
st_frame(1, 0, 101),
st_frame(2, 0, 102),
st_frame(3, 0, 103),
st_frame(4, 0, 104),
];
let list = init_ref_pic_list_p(&dpb, 5, 16, PicStructure::Frame, false);
assert_eq!(list, vec![104, 103, 102, 101]);
}
#[test]
fn init_p_list_spec_example() {
let dpb = vec![
st_frame(300, 0, 300),
st_frame(302, 0, 302),
st_frame(303, 0, 303),
lt_frame(0, 0, 1000),
lt_frame(3, 0, 1003),
];
let list = init_ref_pic_list_p(&dpb, 304, 1 << 12, PicStructure::Frame, false);
assert_eq!(list, vec![303, 302, 300, 1000, 1003]);
}
#[test]
fn init_b_lists_spec_rules() {
let dpb = vec![
st_frame(1, 5, 105),
st_frame(2, 6, 106),
st_frame(3, 15, 115),
st_frame(4, 20, 120),
];
let (l0, l1) = init_ref_pic_lists_b(&dpb, 10, PicStructure::Frame, false);
assert_eq!(l0, vec![106, 105, 115, 120]);
assert_eq!(l1, vec![115, 120, 106, 105]);
}
#[test]
fn init_b_lists_identical_swap() {
let dpb = vec![st_frame(1, 1, 1), st_frame(2, 2, 2), st_frame(3, 3, 3)];
let (l0, l1) = init_ref_pic_lists_b(&dpb, 10, PicStructure::Frame, false);
assert_eq!(l0, vec![3, 2, 1]);
assert_eq!(l1, vec![2, 3, 1]);
}
#[test]
fn init_b_lists_with_long_term() {
let dpb = vec![
st_frame(1, 5, 5),
st_frame(2, 15, 15),
lt_frame(2, 0, 92),
lt_frame(0, 0, 90),
];
let (l0, l1) = init_ref_pic_lists_b(&dpb, 10, PicStructure::Frame, false);
assert_eq!(l0, vec![5, 15, 90, 92]);
assert_eq!(l1, vec![15, 5, 90, 92]);
}
#[test]
fn modify_subtract_applies() {
let dpb = vec![
st_frame(1, 0, 1),
st_frame(2, 0, 2),
st_frame(3, 0, 3),
st_frame(4, 0, 4),
];
let mut list = init_ref_pic_list_p(&dpb, 5, 16, PicStructure::Frame, false);
let ops = [RplmOp::Subtract(0)];
modify_ref_pic_list(&mut list, &ops, &dpb, 4, 5, 16, false, false);
assert_eq!(list[0], 4);
assert_eq!(&list[1..], &[3, 2, 1]);
}
#[test]
fn modify_add_applies() {
let dpb = vec![st_frame(0, 0, 42)];
let mut list = vec![42];
let ops = [RplmOp::Add(5)];
modify_ref_pic_list(&mut list, &ops, &dpb, 1, 10, 16, false, false);
assert_eq!(list, vec![42]);
}
#[test]
fn modify_long_term_applies() {
let dpb = vec![
st_frame(1, 0, 10),
lt_frame(3, 0, 20), lt_frame(5, 0, 30), ];
let mut list = init_ref_pic_list_p(&dpb, 5, 16, PicStructure::Frame, false);
assert_eq!(list, vec![10, 20, 30]);
let ops = [RplmOp::LongTerm(5)];
modify_ref_pic_list(&mut list, &ops, &dpb, 3, 5, 16, false, false);
assert_eq!(list, vec![30, 10, 20]);
}
#[test]
fn sliding_window_evicts_oldest() {
let mut dpb = vec![st_frame(1, 0, 1), st_frame(2, 0, 2), st_frame(3, 0, 3)];
sliding_window_marking(&mut dpb, 2, 4, 16, None);
assert_eq!(dpb[0].marking, RefMarking::Unused);
assert_eq!(dpb[1].marking, RefMarking::ShortTerm);
assert_eq!(dpb[2].marking, RefMarking::ShortTerm);
}
#[test]
fn sliding_window_no_eviction_when_below_cap() {
let mut dpb = vec![st_frame(1, 0, 1)];
sliding_window_marking(&mut dpb, 2, 2, 16, None);
assert_eq!(dpb[0].marking, RefMarking::ShortTerm);
}
#[test]
fn sliding_window_counts_field_pair_as_one_frame() {
let mut dpb = vec![
st_single_field(0, FieldParity::Top, 0, 10),
st_single_field(0, FieldParity::Bottom, 1, 11),
st_single_field(1, FieldParity::Top, 4, 12),
st_single_field(1, FieldParity::Bottom, 5, 13),
];
let cur = st_single_field(2, FieldParity::Top, 8, 14);
sliding_window_marking(&mut dpb, 2, 2, 16, Some(&cur));
assert_eq!(dpb[0].marking, RefMarking::Unused);
assert_eq!(dpb[1].marking, RefMarking::Unused);
assert_eq!(dpb[2].marking, RefMarking::ShortTerm);
assert_eq!(dpb[3].marking, RefMarking::ShortTerm);
}
#[test]
fn sliding_window_second_field_of_ref_pair_is_exempt() {
let mut dpb = vec![
st_single_field(0, FieldParity::Top, 0, 10),
st_single_field(0, FieldParity::Bottom, 1, 11),
st_single_field(1, FieldParity::Top, 4, 12),
];
let cur = st_single_field(1, FieldParity::Bottom, 5, 13);
sliding_window_marking(&mut dpb, 2, 1, 16, Some(&cur));
assert!(dpb.iter().all(|e| e.marking == RefMarking::ShortTerm));
}
#[test]
fn sliding_window_non_paired_field_is_own_unit() {
let mut dpb = vec![
st_single_field(0, FieldParity::Top, 0, 10),
st_single_field(1, FieldParity::Top, 4, 12),
st_single_field(1, FieldParity::Bottom, 5, 13),
];
let cur = st_single_field(2, FieldParity::Top, 8, 14);
sliding_window_marking(&mut dpb, 2, 2, 16, Some(&cur));
assert_eq!(dpb[0].marking, RefMarking::Unused);
assert_eq!(dpb[1].marking, RefMarking::ShortTerm);
assert_eq!(dpb[2].marking, RefMarking::ShortTerm);
}
#[test]
fn mmco_mark_short_term_unused() {
let mut dpb = vec![st_frame(1, 0, 1), st_frame(2, 0, 2), st_frame(3, 0, 3)];
let mut current = st_frame(4, 0, 4);
let ops = [MmcoOp::MarkShortTermUnused(0)];
let mmco5 = apply_mmco(&mut dpb, &ops, &mut current, 4, 16);
assert!(!mmco5);
assert_eq!(dpb[0].marking, RefMarking::ShortTerm);
assert_eq!(dpb[1].marking, RefMarking::ShortTerm);
assert_eq!(dpb[2].marking, RefMarking::Unused);
}
#[test]
fn mmco_mark_long_term_unused() {
let mut dpb = vec![lt_frame(0, 0, 100), lt_frame(3, 0, 103)];
let mut current = st_frame(4, 0, 4);
let ops = [MmcoOp::MarkLongTermUnused(3)];
let mmco5 = apply_mmco(&mut dpb, &ops, &mut current, 4, 16);
assert!(!mmco5);
assert_eq!(dpb[0].marking, RefMarking::LongTerm);
assert_eq!(dpb[1].marking, RefMarking::Unused);
}
#[test]
fn mmco_assign_long_term() {
let mut dpb = vec![
st_frame(2, 0, 2),
st_frame(3, 0, 3),
lt_frame(5, 0, 105), ];
let mut current = st_frame(4, 0, 4);
let ops = [MmcoOp::AssignLongTerm(0, 5)];
let mmco5 = apply_mmco(&mut dpb, &ops, &mut current, 4, 16);
assert!(!mmco5);
assert_eq!(dpb[0].marking, RefMarking::ShortTerm); assert_eq!(dpb[1].marking, RefMarking::LongTerm); assert_eq!(dpb[1].long_term_frame_idx, 5);
assert_eq!(dpb[2].marking, RefMarking::Unused); }
#[test]
fn mmco_set_max_long_term_idx() {
let mut dpb = vec![lt_frame(0, 0, 10), lt_frame(3, 0, 13), lt_frame(7, 0, 17)];
let mut current = st_frame(5, 0, 5);
let ops = [MmcoOp::SetMaxLongTermIdx(4)];
apply_mmco(&mut dpb, &ops, &mut current, 5, 16);
assert_eq!(dpb[0].marking, RefMarking::LongTerm);
assert_eq!(dpb[1].marking, RefMarking::LongTerm);
assert_eq!(dpb[2].marking, RefMarking::Unused);
}
#[test]
fn mmco_set_max_long_term_idx_zero_clears_all() {
let mut dpb = vec![lt_frame(0, 0, 10), lt_frame(3, 0, 13)];
let mut current = st_frame(5, 0, 5);
let ops = [MmcoOp::SetMaxLongTermIdx(0)];
apply_mmco(&mut dpb, &ops, &mut current, 5, 16);
assert_eq!(dpb[0].marking, RefMarking::Unused);
assert_eq!(dpb[1].marking, RefMarking::Unused);
}
#[test]
fn mmco_mark_all_unused() {
let mut dpb = vec![st_frame(1, 0, 1), lt_frame(0, 0, 10)];
let mut current = st_frame(5, 0, 5);
let ops = [MmcoOp::MarkAllUnused];
let mmco5 = apply_mmco(&mut dpb, &ops, &mut current, 5, 16);
assert!(mmco5);
assert_eq!(dpb[0].marking, RefMarking::Unused);
assert_eq!(dpb[1].marking, RefMarking::Unused);
}
#[test]
fn mmco_assign_current_long_term() {
let mut dpb = vec![lt_frame(3, 0, 103)];
let mut current = st_frame(5, 0, 5);
let ops = [MmcoOp::AssignCurrentLongTerm(3)];
apply_mmco(&mut dpb, &ops, &mut current, 5, 16);
assert_eq!(dpb[0].marking, RefMarking::Unused);
assert_eq!(current.marking, RefMarking::LongTerm);
assert_eq!(current.long_term_frame_idx, 3);
}
fn lt_single_field(frame_num: u32, parity: FieldParity, ltfi: u32, key: u32) -> DpbEntry {
let mut e = st_single_field(frame_num, parity, 0, key);
e.marking = RefMarking::LongTerm;
e.long_term_frame_idx = ltfi;
e.sync_field_markings();
e
}
#[test]
fn mmco1_field_marks_single_field_of_pair() {
let mut dpb = vec![
st_single_field(1, FieldParity::Top, 2, 10),
st_single_field(1, FieldParity::Bottom, 3, 11),
];
let mut current = st_single_field(2, FieldParity::Top, 4, 12);
let ops = [MmcoOp::MarkShortTermUnused(1)];
apply_mmco(&mut dpb, &ops, &mut current, 2, 16);
assert_eq!(dpb[0].marking, RefMarking::Unused);
assert_eq!(
dpb[1].marking,
RefMarking::ShortTerm,
"other field untouched"
);
}
#[test]
fn mmco1_field_unmarks_one_parity_of_stored_frame() {
let mut dpb = vec![st_frame(1, 0, 10)];
let mut current = st_single_field(2, FieldParity::Top, 4, 12);
let ops = [MmcoOp::MarkShortTermUnused(2)];
apply_mmco(&mut dpb, &ops, &mut current, 2, 16);
let e = &dpb[0];
assert_eq!(e.marking, RefMarking::Unused);
assert!(e.has_field_marked(FieldParity::Top, RefMarking::ShortTerm));
assert!(!e.has_field_marked(FieldParity::Bottom, RefMarking::ShortTerm));
assert!(e.is_any_field_ref());
let list = init_ref_pic_list_p_field(&dpb, 2, 16, false);
assert_eq!(list, vec![top(10)]);
}
#[test]
fn mmco2_field_marks_single_long_term_field() {
let mut dpb = vec![
lt_single_field(0, FieldParity::Top, 0, 10),
lt_single_field(0, FieldParity::Bottom, 0, 11),
];
let mut current = st_single_field(1, FieldParity::Top, 2, 12);
let ops = [MmcoOp::MarkLongTermUnused(0)];
apply_mmco(&mut dpb, &ops, &mut current, 1, 16);
assert_eq!(dpb[0].marking, RefMarking::LongTerm, "top LT survives");
assert_eq!(dpb[1].marking, RefMarking::Unused, "bottom LT unmarked");
}
#[test]
fn mmco3_field_promotion_completes_long_term_pair() {
let mut dpb = vec![
lt_single_field(1, FieldParity::Top, 0, 10),
st_single_field(1, FieldParity::Bottom, 3, 11),
];
let mut current = st_single_field(2, FieldParity::Top, 4, 12);
let ops = [MmcoOp::AssignLongTerm(2, 0)];
apply_mmco(&mut dpb, &ops, &mut current, 2, 16);
assert_eq!(
dpb[0].marking,
RefMarking::LongTerm,
"pair exception spares the first field"
);
assert_eq!(
dpb[1].marking,
RefMarking::LongTerm,
"target field promoted"
);
assert_eq!(dpb[0].long_term_frame_idx, 0);
assert_eq!(dpb[1].long_term_frame_idx, 0);
}
#[test]
fn mmco3_field_promotion_evicts_unrelated_long_term_field() {
let mut dpb = vec![
lt_single_field(0, FieldParity::Top, 0, 10),
st_single_field(1, FieldParity::Top, 2, 11),
];
let mut current = st_single_field(2, FieldParity::Top, 4, 12);
let ops = [MmcoOp::AssignLongTerm(1, 0)];
apply_mmco(&mut dpb, &ops, &mut current, 2, 16);
assert_eq!(dpb[0].marking, RefMarking::Unused, "old holder evicted");
assert_eq!(dpb[1].marking, RefMarking::LongTerm);
assert_eq!(dpb[1].long_term_frame_idx, 0);
}
#[test]
fn mmco6_second_field_completes_long_term_pair() {
let mut dpb = vec![lt_single_field(0, FieldParity::Top, 0, 10)];
let mut current = st_single_field(0, FieldParity::Bottom, 1, 11);
let ops = [MmcoOp::AssignCurrentLongTerm(0)];
apply_mmco(&mut dpb, &ops, &mut current, 0, 16);
assert_eq!(dpb[0].marking, RefMarking::LongTerm, "first field spared");
assert_eq!(dpb[0].long_term_frame_idx, 0);
assert_eq!(current.marking, RefMarking::LongTerm);
assert_eq!(current.long_term_frame_idx, 0);
}
#[test]
fn mmco6_field_evicts_unrelated_holder() {
let mut dpb = vec![lt_single_field(0, FieldParity::Top, 0, 10)];
let mut current = st_single_field(2, FieldParity::Top, 4, 11);
let ops = [MmcoOp::AssignCurrentLongTerm(0)];
apply_mmco(&mut dpb, &ops, &mut current, 2, 16);
assert_eq!(dpb[0].marking, RefMarking::Unused);
assert_eq!(current.marking, RefMarking::LongTerm);
}
#[test]
fn interleave_skips_unmarked_parity_field() {
let mut pair0_top = st_single_field(0, FieldParity::Top, 0, 10);
let pair0_bot = st_single_field(0, FieldParity::Bottom, 1, 11);
let pair1_top = st_single_field(1, FieldParity::Top, 2, 12);
let pair1_bot = st_single_field(1, FieldParity::Bottom, 3, 13);
pair0_top.mark_all(RefMarking::Unused);
let dpb = vec![pair0_top, pair0_bot, pair1_top, pair1_bot];
let list = init_ref_pic_list_p_field(&dpb, 2, 16, false);
assert_eq!(list, vec![top(12), bot(13), bot(11)]);
}
#[test]
fn marking_idr_short_term() {
let mut dpb = vec![st_frame(1, 0, 1), lt_frame(0, 0, 10)];
let mut current = st_frame(0, 0, 99);
let mmco5 = perform_marking(
&mut dpb,
&mut current,
4,
true, false, false,
None,
0,
16,
);
assert!(!mmco5);
assert_eq!(dpb[0].marking, RefMarking::Unused);
assert_eq!(dpb[1].marking, RefMarking::Unused);
assert_eq!(current.marking, RefMarking::ShortTerm);
}
#[test]
fn marking_idr_long_term() {
let mut dpb = vec![st_frame(1, 0, 1)];
let mut current = st_frame(0, 0, 99);
perform_marking(&mut dpb, &mut current, 4, true, true, false, None, 0, 16);
assert_eq!(dpb[0].marking, RefMarking::Unused);
assert_eq!(current.marking, RefMarking::LongTerm);
assert_eq!(current.long_term_frame_idx, 0);
}
#[test]
fn marking_non_idr_sliding_window() {
let mut dpb = vec![st_frame(1, 0, 1), st_frame(2, 0, 2), st_frame(3, 0, 3)];
let mut current = st_frame(4, 0, 4);
let mmco5 = perform_marking(
&mut dpb,
&mut current,
2, false,
false,
false,
None,
4,
16,
);
assert!(!mmco5);
assert_eq!(dpb[0].marking, RefMarking::Unused);
assert_eq!(current.marking, RefMarking::ShortTerm);
}
#[test]
fn marking_non_idr_adaptive_mmco5() {
let mut dpb = vec![st_frame(1, 0, 1)];
let mut current = st_frame(4, 0, 4);
let ops = [MmcoOp::MarkAllUnused];
let mmco5 = perform_marking(
&mut dpb,
&mut current,
4,
false,
false,
false,
Some(&ops),
4,
16,
);
assert!(mmco5);
assert_eq!(dpb[0].marking, RefMarking::Unused);
assert_eq!(current.marking, RefMarking::ShortTerm);
}
#[test]
fn modify_pads_and_truncates() {
let dpb = vec![st_frame(1, 0, 1)];
let mut list = vec![1];
modify_ref_pic_list(&mut list, &[], &dpb, 3, 2, 16, false, false);
assert_eq!(list.len(), 3);
assert_eq!(list[0], 1);
assert_eq!(list[1], u32::MAX);
assert_eq!(list[2], u32::MAX);
}
#[test]
fn interleave_two_full_frames_top_first() {
let dpb = vec![st_pair(1, 0, 1, 1), st_pair(2, 2, 3, 2)];
let order = vec![1u32, 2];
let out = interleave_fields(&order, &dpb, FieldParity::Top, RefMarking::ShortTerm);
assert_eq!(out, vec![top(1), bot(1), top(2), bot(2)]);
}
#[test]
fn interleave_two_full_frames_bottom_first() {
let dpb = vec![st_pair(1, 0, 1, 1), st_pair(2, 2, 3, 2)];
let order = vec![1u32, 2];
let out = interleave_fields(&order, &dpb, FieldParity::Bottom, RefMarking::ShortTerm);
assert_eq!(out, vec![bot(1), top(1), bot(2), top(2)]);
}
#[test]
fn interleave_skips_missing_parity() {
let dpb = vec![
st_pair(1, 0, 1, 1),
st_single_field(2, FieldParity::Top, 2, 2),
];
let order = vec![1u32, 2];
let out = interleave_fields(&order, &dpb, FieldParity::Top, RefMarking::ShortTerm);
assert_eq!(out, vec![top(1), bot(1), top(2)]);
}
#[test]
fn interleave_one_parity_only_appends_leftovers() {
let dpb = vec![
st_single_field(1, FieldParity::Top, 0, 1),
st_single_field(2, FieldParity::Top, 2, 2),
st_single_field(3, FieldParity::Top, 4, 3),
];
let order = vec![1u32, 2, 3];
let out = interleave_fields(&order, &dpb, FieldParity::Top, RefMarking::ShortTerm);
assert_eq!(out, vec![top(1), top(2), top(3)]);
}
#[test]
fn interleave_opposite_parity_only() {
let dpb = vec![
st_single_field(1, FieldParity::Bottom, 0, 1),
st_single_field(2, FieldParity::Bottom, 2, 2),
];
let order = vec![1u32, 2];
let out = interleave_fields(&order, &dpb, FieldParity::Top, RefMarking::ShortTerm);
assert_eq!(out, vec![bot(1), bot(2)]);
}
#[test]
fn interleave_empty() {
let dpb: Vec<DpbEntry> = vec![];
let out = interleave_fields(&[], &dpb, FieldParity::Top, RefMarking::ShortTerm);
assert!(out.is_empty());
}
#[test]
fn p_field_two_full_frames() {
let dpb = vec![st_pair(1, 0, 1, 1), st_pair(2, 2, 3, 2)];
let out = init_ref_pic_list_p_field(&dpb, 5, 16, false);
assert_eq!(out, vec![top(2), bot(2), top(1), bot(1)]);
}
#[test]
fn p_field_short_then_long() {
let dpb = vec![
st_pair(3, 0, 1, 3),
st_pair(4, 2, 3, 4),
lt_pair(2, 10, 102),
lt_pair(0, 8, 100),
];
let out = init_ref_pic_list_p_field(&dpb, 6, 16, true);
assert_eq!(
out,
vec![
bot(4),
top(4),
bot(3),
top(3),
bot(100),
top(100),
bot(102),
top(102),
]
);
}
#[test]
fn p_field_framenumwrap_order() {
let dpb = vec![st_pair(1, 0, 1, 1), st_pair(15, 4, 5, 15)];
let out = init_ref_pic_list_p_field(&dpb, 1, 16, false);
assert_eq!(out, vec![top(1), bot(1), top(15), bot(15)]);
}
#[test]
fn b_field_lists_spec_rules() {
let dpb = vec![
st_pair(0, 5, 5, 5),
st_pair(1, 6, 6, 6),
st_pair(2, 15, 15, 15),
st_pair(3, 20, 20, 20),
];
let (l0, l1) = init_ref_pic_lists_b_field(&dpb, 10, false);
assert_eq!(
l0,
vec![
top(6),
bot(6),
top(5),
bot(5),
top(15),
bot(15),
top(20),
bot(20),
]
);
assert_eq!(
l1,
vec![
top(15),
bot(15),
top(20),
bot(20),
top(6),
bot(6),
top(5),
bot(5),
]
);
}
#[test]
fn b_field_identical_list_swap() {
let dpb = vec![st_pair(0, 6, 6, 6), st_pair(1, 8, 8, 8)];
let (l0, l1) = init_ref_pic_lists_b_field(&dpb, 10, false);
assert_eq!(l0, vec![top(8), bot(8), top(6), bot(6)]);
assert_eq!(l1, vec![bot(8), top(8), top(6), bot(6)]);
}
#[test]
fn b_field_short_then_long() {
let dpb = vec![st_pair(0, 5, 5, 5), lt_pair(0, 99, 100)];
let (l0, l1) = init_ref_pic_lists_b_field(&dpb, 10, false);
assert_eq!(l0, vec![top(5), bot(5), top(100), bot(100)]);
assert_eq!(l1, vec![bot(5), top(5), top(100), bot(100)]);
}
#[test]
fn b_field_nonpaired_ref() {
let dpb = vec![st_single_field(0, FieldParity::Bottom, 5, 7)];
let (l0, _l1) = init_ref_pic_lists_b_field(&dpb, 10, false);
assert_eq!(l0, vec![bot(7)]);
}
#[test]
fn field_pic_num_eqs() {
let e = st_pair(3, 0, 1, 3);
assert_eq!(e.field_pic_num(FieldParity::Top, 5, 16, false), 7);
assert_eq!(e.field_pic_num(FieldParity::Bottom, 5, 16, false), 6);
assert_eq!(e.field_pic_num(FieldParity::Top, 5, 16, true), 6);
assert_eq!(e.field_pic_num(FieldParity::Bottom, 5, 16, true), 7);
}
#[test]
fn field_modify_subtract_reorders() {
let dpb = vec![st_pair(3, 0, 1, 3), st_pair(4, 2, 3, 4)];
let mut list = init_ref_pic_list_p_field(&dpb, 5, 16, false);
assert_eq!(list, vec![top(4), bot(4), top(3), bot(3)]);
modify_ref_pic_list_field(&mut list, &[RplmOp::Subtract(2)], &dpb, 4, 5, 16, false);
assert_eq!(list, vec![bot(4), top(4), top(3), bot(3)]);
}
#[test]
fn field_modify_long_term_splices() {
let dpb = vec![st_pair(4, 2, 3, 4), lt_pair(1, 9, 100)];
let mut list = init_ref_pic_list_p_field(&dpb, 5, 16, false);
assert_eq!(list, vec![top(4), bot(4), top(100), bot(100)]);
modify_ref_pic_list_field(&mut list, &[RplmOp::LongTerm(2)], &dpb, 4, 5, 16, false);
assert_eq!(list, vec![bot(100), top(4), bot(4), top(100)]);
}
#[test]
fn field_modify_pads_and_truncates() {
let dpb = vec![st_pair(1, 0, 1, 1)];
let mut list = vec![top(1)];
modify_ref_pic_list_field(&mut list, &[], &dpb, 3, 2, 16, false);
assert_eq!(list.len(), 3);
assert_eq!(list[0], top(1));
assert_eq!(list[1], no_ref_field());
assert_eq!(list[2], no_ref_field());
}
#[test]
fn field_modify_pred_chains_across_ops() {
let dpb = vec![
st_pair(2, 4, 5, 2),
st_pair(3, 6, 7, 3),
st_pair(4, 8, 9, 4),
];
let mut list = init_ref_pic_list_p_field(&dpb, 5, 16, false);
assert_eq!(list, vec![top(4), bot(4), top(3), bot(3), top(2), bot(2)]);
modify_ref_pic_list_field(
&mut list,
&[RplmOp::Subtract(2), RplmOp::Subtract(0)],
&dpb,
6,
5,
16,
false,
);
assert_eq!(list[0], bot(4));
assert_eq!(list[1], top(3));
}
}