use std::cell::RefCell;
use std::rc::Rc;
use crate::pixel::PixelData;
use crate::sps::Sps;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PictureReferenceStatus {
UnusedForReference,
ShortTerm,
LongTerm,
}
#[derive(Debug)]
pub struct DecodedPicture {
pub y: PixelData,
pub u: PixelData,
pub v: PixelData,
pub width: u32,
pub height: u32,
pub poc: i32,
pub reference_status: RefCell<PictureReferenceStatus>,
pub output: RefCell<bool>,
pub tab_mvf: Vec<crate::cu_tree::MvField>,
pub log2_min_pu_size: u8,
pub min_pu_width: usize,
pub log2_ctb_size: u8,
pub ref_pic_list_pocs: [Vec<i32>; 2],
}
impl DecodedPicture {
pub fn new(
y: PixelData,
u: PixelData,
v: PixelData,
width: u32,
height: u32,
poc: i32,
) -> Self {
Self {
y,
u,
v,
width,
height,
poc,
reference_status: RefCell::new(PictureReferenceStatus::ShortTerm),
output: RefCell::new(false),
tab_mvf: Vec::new(),
log2_min_pu_size: 2,
min_pu_width: 0,
log2_ctb_size: 0,
ref_pic_list_pocs: [Vec::new(), Vec::new()],
}
}
#[allow(clippy::too_many_arguments)]
pub fn new_with_mvf(
y: PixelData,
u: PixelData,
v: PixelData,
width: u32,
height: u32,
poc: i32,
tab_mvf: Vec<crate::cu_tree::MvField>,
log2_min_pu_size: u8,
min_pu_width: usize,
log2_ctb_size: u8,
ref_pic_list_pocs: [Vec<i32>; 2],
) -> Self {
Self {
y,
u,
v,
width,
height,
poc,
reference_status: RefCell::new(PictureReferenceStatus::ShortTerm),
output: RefCell::new(false),
tab_mvf,
log2_min_pu_size,
min_pu_width,
log2_ctb_size,
ref_pic_list_pocs,
}
}
pub fn mark(&self, status: PictureReferenceStatus) {
*self.reference_status.borrow_mut() = status;
}
pub fn reference_status(&self) -> PictureReferenceStatus {
*self.reference_status.borrow()
}
pub fn is_reference(&self) -> bool {
!matches!(
self.reference_status(),
PictureReferenceStatus::UnusedForReference
)
}
}
#[derive(Debug, Default)]
pub struct DecodedPictureBuffer {
pictures: Vec<Rc<DecodedPicture>>,
max_dec_pic_buffering: usize,
max_num_reorder_pics: usize,
#[allow(dead_code)]
max_latency_increase_plus1: u32,
}
impl DecodedPictureBuffer {
pub fn new() -> Self {
Self::default()
}
pub fn configure_from_sps(&mut self, sps: &Sps) {
self.max_dec_pic_buffering = (sps.sps_max_dec_pic_buffering_minus1 + 1) as usize;
self.max_num_reorder_pics = sps.sps_max_num_reorder_pics as usize;
self.max_latency_increase_plus1 = sps.sps_max_latency_increase_plus1;
}
pub fn insert(&mut self, pic: Rc<DecodedPicture>) {
self.pictures.push(pic);
}
pub fn len(&self) -> usize {
self.pictures.len()
}
pub fn is_empty(&self) -> bool {
self.pictures.is_empty()
}
pub fn find_by_poc(&self, poc: i32) -> Option<Rc<DecodedPicture>> {
self.pictures.iter().find(|p| p.poc == poc).cloned()
}
pub fn find_st_ref(&self, poc: i32) -> Option<Rc<DecodedPicture>> {
self.pictures
.iter()
.find(|p| p.poc == poc && p.reference_status() == PictureReferenceStatus::ShortTerm)
.cloned()
}
pub fn find_lt_ref_by_lsb(&self, poc_lsb: u32, max_poc_lsb: i32) -> Option<Rc<DecodedPicture>> {
self.pictures
.iter()
.find(|p| {
p.reference_status() == PictureReferenceStatus::LongTerm
&& (p.poc.rem_euclid(max_poc_lsb)) == poc_lsb as i32
})
.cloned()
}
pub fn unmark_all_references(&self) {
for p in &self.pictures {
p.mark(PictureReferenceStatus::UnusedForReference);
}
}
pub fn cleanup_unused(&mut self) {
self.pictures
.retain(|p| p.is_reference() || !*p.output.borrow());
}
pub fn bump(&mut self) -> Option<Rc<DecodedPicture>> {
None
}
pub fn pictures(&self) -> &[Rc<DecodedPicture>] {
&self.pictures
}
}
#[derive(Debug, Default, Clone)]
pub struct ReferencePictureSets {
pub st_curr_before: Vec<i32>,
pub st_curr_after: Vec<i32>,
pub st_foll: Vec<i32>,
pub lt_curr: Vec<i32>,
pub lt_foll: Vec<i32>,
pub lt_poc_msb_present: Vec<bool>,
}
impl ReferencePictureSets {
pub fn num_poc_total_curr(&self) -> usize {
self.st_curr_before.len() + self.st_curr_after.len() + self.lt_curr.len()
}
}
pub fn build_ref_pic_list_temp0(
rps: &ReferencePictureSets,
num_rps_curr_temp_list: usize,
) -> Vec<i32> {
build_ref_pic_list_temp(
&[&rps.st_curr_before, &rps.st_curr_after, &rps.lt_curr],
num_rps_curr_temp_list,
)
}
pub fn build_ref_pic_list_temp1(
rps: &ReferencePictureSets,
num_rps_curr_temp_list: usize,
) -> Vec<i32> {
build_ref_pic_list_temp(
&[&rps.st_curr_after, &rps.st_curr_before, &rps.lt_curr],
num_rps_curr_temp_list,
)
}
fn build_ref_pic_list_temp(sub_lists: &[&Vec<i32>], target_len: usize) -> Vec<i32> {
let mut out: Vec<i32> = Vec::with_capacity(target_len);
if target_len == 0 {
return out;
}
let total_in_sub_lists: usize = sub_lists.iter().map(|s| s.len()).sum();
if total_in_sub_lists == 0 {
return out;
}
while out.len() < target_len {
for sub in sub_lists {
for poc in sub.iter() {
if out.len() >= target_len {
break;
}
out.push(*poc);
}
if out.len() >= target_len {
break;
}
}
}
out
}
pub fn apply_ref_pic_list_modification(
temp_list: &[i32],
modification_flag: bool,
list_entry: &[u32],
num_active: usize,
) -> Result<Vec<i32>, crate::error::DecodeError> {
let mut out: Vec<i32> = Vec::with_capacity(num_active);
if modification_flag {
if list_entry.len() < num_active {
return Err(crate::error::DecodeError::InvalidSyntax(
"ref_pic_list_modification has fewer entries than active ref count",
));
}
for &idx in list_entry.iter().take(num_active) {
let idx = idx as usize;
if idx >= temp_list.len() {
return Err(crate::error::DecodeError::InvalidSyntax(
"list_entry index out of temp list bounds",
));
}
out.push(temp_list[idx]);
}
} else {
if temp_list.len() < num_active {
return Err(crate::error::DecodeError::InvalidSyntax(
"temp list shorter than active ref count",
));
}
out.extend_from_slice(&temp_list[..num_active]);
}
Ok(out)
}
pub fn resolve_ref_pics(
dpb: &DecodedPictureBuffer,
pocs: &[i32],
) -> Result<Vec<Rc<DecodedPicture>>, crate::error::DecodeError> {
let mut out: Vec<Rc<DecodedPicture>> = Vec::with_capacity(pocs.len());
for poc in pocs {
let pic = dpb
.find_st_ref(*poc)
.or_else(|| {
dpb.pictures()
.iter()
.find(|p| {
p.poc == *poc && p.reference_status() == PictureReferenceStatus::LongTerm
})
.cloned()
})
.or_else(|| dpb.find_by_poc(*poc));
match pic {
Some(p) => out.push(p),
None => {
return Err(crate::error::DecodeError::InvalidSyntax(
"reference picture POC not present in DPB",
));
}
}
}
Ok(out)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn insert_and_lookup_by_poc() {
let mut dpb = DecodedPictureBuffer::new();
let pic = Rc::new(DecodedPicture::new(
PixelData::U8(vec![0; 16 * 16]),
PixelData::U8(vec![0; 8 * 8]),
PixelData::U8(vec![0; 8 * 8]),
16,
16,
5,
));
dpb.insert(pic);
assert_eq!(dpb.len(), 1);
assert!(dpb.find_by_poc(5).is_some());
assert!(dpb.find_by_poc(4).is_none());
}
#[test]
fn mark_short_term_reference() {
let pic = Rc::new(DecodedPicture::new(
PixelData::U8(vec![]),
PixelData::U8(vec![]),
PixelData::U8(vec![]),
0,
0,
10,
));
assert_eq!(pic.reference_status(), PictureReferenceStatus::ShortTerm);
pic.mark(PictureReferenceStatus::LongTerm);
assert_eq!(pic.reference_status(), PictureReferenceStatus::LongTerm);
pic.mark(PictureReferenceStatus::UnusedForReference);
assert!(!pic.is_reference());
}
#[test]
fn cleanup_drops_unreferenced_and_output() {
let mut dpb = DecodedPictureBuffer::new();
let a = Rc::new(DecodedPicture::new(
PixelData::U8(vec![]),
PixelData::U8(vec![]),
PixelData::U8(vec![]),
0,
0,
0,
));
a.mark(PictureReferenceStatus::UnusedForReference);
*a.output.borrow_mut() = true;
let b = Rc::new(DecodedPicture::new(
PixelData::U8(vec![]),
PixelData::U8(vec![]),
PixelData::U8(vec![]),
0,
0,
1,
));
dpb.insert(a);
dpb.insert(b);
dpb.cleanup_unused();
assert_eq!(dpb.len(), 1);
assert_eq!(dpb.pictures()[0].poc, 1);
}
#[test]
fn num_poc_total_curr_sums_three_sets() {
let rps = ReferencePictureSets {
st_curr_before: vec![-1, -2],
st_curr_after: vec![1],
st_foll: vec![-4],
lt_curr: vec![100],
lt_foll: vec![],
lt_poc_msb_present: vec![],
};
assert_eq!(rps.num_poc_total_curr(), 4);
}
#[test]
fn ref_pic_list_temp0_order_before_then_after_then_lt() {
let rps = ReferencePictureSets {
st_curr_before: vec![3, 2],
st_curr_after: vec![5, 6],
st_foll: vec![],
lt_curr: vec![100],
lt_foll: vec![],
lt_poc_msb_present: vec![],
};
let temp = build_ref_pic_list_temp0(&rps, 5);
assert_eq!(temp, vec![3, 2, 5, 6, 100]);
}
#[test]
fn ref_pic_list_temp1_order_after_then_before_then_lt() {
let rps = ReferencePictureSets {
st_curr_before: vec![3, 2],
st_curr_after: vec![5, 6],
st_foll: vec![],
lt_curr: vec![100],
lt_foll: vec![],
lt_poc_msb_present: vec![],
};
let temp = build_ref_pic_list_temp1(&rps, 5);
assert_eq!(temp, vec![5, 6, 3, 2, 100]);
}
#[test]
fn ref_pic_list_temp0_wraps_around_when_active_exceeds_rps() {
let rps = ReferencePictureSets {
st_curr_before: vec![3, 2],
st_curr_after: vec![],
st_foll: vec![],
lt_curr: vec![],
lt_foll: vec![],
lt_poc_msb_present: vec![],
};
let temp = build_ref_pic_list_temp0(&rps, 5);
assert_eq!(temp, vec![3, 2, 3, 2, 3]);
}
#[test]
fn ref_pic_list_modification_reorders_temp_list() {
let temp = vec![10, 42, 99];
let modified = apply_ref_pic_list_modification(&temp, true, &[1, 0], 2).expect("apply mod");
assert_eq!(modified, vec![42, 10]);
}
#[test]
fn ref_pic_list_modification_disabled_uses_temp_prefix() {
let temp = vec![10, 42, 99, 7];
let result = apply_ref_pic_list_modification(&temp, false, &[], 3).expect("no mod");
assert_eq!(result, vec![10, 42, 99]);
}
#[test]
fn ref_pic_list_modification_rejects_out_of_bounds_entry() {
let temp = vec![10, 42];
let err = apply_ref_pic_list_modification(&temp, true, &[5], 1);
assert!(err.is_err());
}
#[test]
fn resolve_ref_pics_looks_up_by_poc() {
let mut dpb = DecodedPictureBuffer::new();
let p0 = Rc::new(DecodedPicture::new(
PixelData::U8(vec![]),
PixelData::U8(vec![]),
PixelData::U8(vec![]),
0,
0,
2,
));
let p1 = Rc::new(DecodedPicture::new(
PixelData::U8(vec![]),
PixelData::U8(vec![]),
PixelData::U8(vec![]),
0,
0,
5,
));
dpb.insert(p0);
dpb.insert(p1);
let resolved = resolve_ref_pics(&dpb, &[5, 2]).expect("resolve");
assert_eq!(resolved.len(), 2);
assert_eq!(resolved[0].poc, 5);
assert_eq!(resolved[1].poc, 2);
}
#[test]
fn resolve_ref_pics_errors_on_missing_poc() {
let dpb = DecodedPictureBuffer::new();
let err = resolve_ref_pics(&dpb, &[3]);
assert!(err.is_err());
}
#[test]
fn resolve_ref_pics_prefers_short_term() {
let mut dpb = DecodedPictureBuffer::new();
let p = Rc::new(DecodedPicture::new(
PixelData::U8(vec![]),
PixelData::U8(vec![]),
PixelData::U8(vec![]),
0,
0,
7,
));
assert_eq!(p.reference_status(), PictureReferenceStatus::ShortTerm);
dpb.insert(p);
let resolved = resolve_ref_pics(&dpb, &[7]).expect("resolve");
assert_eq!(resolved[0].poc, 7);
assert_eq!(
resolved[0].reference_status(),
PictureReferenceStatus::ShortTerm
);
}
#[test]
fn resolve_ref_pics_finds_long_term() {
let mut dpb = DecodedPictureBuffer::new();
let p = Rc::new(DecodedPicture::new(
PixelData::U8(vec![]),
PixelData::U8(vec![]),
PixelData::U8(vec![]),
0,
0,
7,
));
p.mark(PictureReferenceStatus::LongTerm);
dpb.insert(p);
let resolved = resolve_ref_pics(&dpb, &[7]).expect("resolve");
assert_eq!(
resolved[0].reference_status(),
PictureReferenceStatus::LongTerm
);
}
#[test]
fn resolve_ref_pics_fallback_unmarked() {
let mut dpb = DecodedPictureBuffer::new();
let p = Rc::new(DecodedPicture::new(
PixelData::U8(vec![]),
PixelData::U8(vec![]),
PixelData::U8(vec![]),
0,
0,
7,
));
p.mark(PictureReferenceStatus::UnusedForReference);
dpb.insert(p);
let resolved = resolve_ref_pics(&dpb, &[7]).expect("resolve");
assert_eq!(resolved[0].poc, 7);
}
#[test]
fn apply_rps_marking_marks_st_and_unused() {
use crate::decoder::Decoder;
let mut dpb = DecodedPictureBuffer::new();
let p0 = Rc::new(DecodedPicture::new(
PixelData::U8(vec![]),
PixelData::U8(vec![]),
PixelData::U8(vec![]),
0,
0,
0,
));
let p4 = Rc::new(DecodedPicture::new(
PixelData::U8(vec![]),
PixelData::U8(vec![]),
PixelData::U8(vec![]),
0,
0,
4,
));
let p8 = Rc::new(DecodedPicture::new(
PixelData::U8(vec![]),
PixelData::U8(vec![]),
PixelData::U8(vec![]),
0,
0,
8,
));
dpb.insert(p0.clone());
dpb.insert(p4.clone());
dpb.insert(p8.clone());
let rps = ReferencePictureSets {
st_curr_before: vec![0],
st_curr_after: vec![8],
st_foll: vec![],
lt_curr: vec![],
lt_foll: vec![],
lt_poc_msb_present: vec![],
};
Decoder::apply_rps_marking(&rps, &dpb, 8);
assert_eq!(p0.reference_status(), PictureReferenceStatus::ShortTerm);
assert_eq!(
p4.reference_status(),
PictureReferenceStatus::UnusedForReference
);
assert_eq!(p8.reference_status(), PictureReferenceStatus::ShortTerm);
}
#[test]
fn apply_rps_marking_lt_by_lsb_no_st_override() {
use crate::decoder::Decoder;
let mut dpb = DecodedPictureBuffer::new();
let p3 = Rc::new(DecodedPicture::new(
PixelData::U8(vec![]),
PixelData::U8(vec![]),
PixelData::U8(vec![]),
0,
0,
3,
));
let p19 = Rc::new(DecodedPicture::new(
PixelData::U8(vec![]),
PixelData::U8(vec![]),
PixelData::U8(vec![]),
0,
0,
19,
));
dpb.insert(p3.clone());
dpb.insert(p19.clone());
let rps = ReferencePictureSets {
st_curr_before: vec![3],
st_curr_after: vec![],
st_foll: vec![],
lt_curr: vec![3], lt_foll: vec![],
lt_poc_msb_present: vec![false, false], };
Decoder::apply_rps_marking(&rps, &dpb, 4);
assert_eq!(p3.reference_status(), PictureReferenceStatus::ShortTerm);
assert_eq!(p19.reference_status(), PictureReferenceStatus::LongTerm);
}
#[test]
fn apply_rps_marking_lt_full_poc_matching() {
use crate::decoder::Decoder;
let mut dpb = DecodedPictureBuffer::new();
let p3 = Rc::new(DecodedPicture::new(
PixelData::U8(vec![]),
PixelData::U8(vec![]),
PixelData::U8(vec![]),
0,
0,
3,
));
let p19 = Rc::new(DecodedPicture::new(
PixelData::U8(vec![]),
PixelData::U8(vec![]),
PixelData::U8(vec![]),
0,
0,
19,
));
dpb.insert(p3.clone());
dpb.insert(p19.clone());
let rps = ReferencePictureSets {
st_curr_before: vec![],
st_curr_after: vec![],
st_foll: vec![],
lt_curr: vec![19], lt_foll: vec![],
lt_poc_msb_present: vec![true],
};
Decoder::apply_rps_marking(&rps, &dpb, 4);
assert_eq!(
p3.reference_status(),
PictureReferenceStatus::UnusedForReference
);
assert_eq!(p19.reference_status(), PictureReferenceStatus::LongTerm);
}
#[test]
fn apply_rps_marking_lt_lsb_matching() {
use crate::decoder::Decoder;
let mut dpb = DecodedPictureBuffer::new();
let p19 = Rc::new(DecodedPicture::new(
PixelData::U8(vec![]),
PixelData::U8(vec![]),
PixelData::U8(vec![]),
0,
0,
19,
));
dpb.insert(p19.clone());
let rps = ReferencePictureSets {
st_curr_before: vec![],
st_curr_after: vec![],
st_foll: vec![],
lt_curr: vec![3], lt_foll: vec![],
lt_poc_msb_present: vec![false],
};
Decoder::apply_rps_marking(&rps, &dpb, 4);
assert_eq!(p19.reference_status(), PictureReferenceStatus::LongTerm);
}
}