use crate::{Codec, Dimensions, Error, Image, Result};
use crate::lossy::bool_dec::BoolDecoder;
use crate::lossy::constants::{
B_DC_PRED, CoeffProbas, NUM_MB_SEGMENTS, NUM_MODE_LF_DELTAS, NUM_REF_LF_DELTAS,
};
use crate::lossy::frame_header::{FrameHeader, KEY_FRAME_HEADER_LEN};
use crate::lossy::loop_filter::FInfo;
use crate::lossy::prelude::*;
use crate::lossy::reconstruct::{self, Planes};
pub(crate) struct SegmentHeader {
pub(crate) use_segment: bool,
pub(crate) update_map: bool,
pub(crate) absolute_delta: bool,
pub(crate) quantizer: [i32; NUM_MB_SEGMENTS],
pub(crate) filter_strength: [i32; NUM_MB_SEGMENTS],
}
#[derive(Default)]
pub(crate) struct FilterHeader {
pub(crate) simple: bool,
pub(crate) level: i32,
pub(crate) sharpness: i32,
pub(crate) use_lf_delta: bool,
pub(crate) ref_lf_delta: [i32; NUM_REF_LF_DELTAS],
pub(crate) mode_lf_delta: [i32; NUM_MODE_LF_DELTAS],
}
#[derive(Clone, Copy, Default)]
pub(crate) struct QuantMatrix {
pub(crate) y1: [i32; 2],
pub(crate) y2: [i32; 2],
pub(crate) uv: [i32; 2],
}
pub(crate) struct Proba {
pub(crate) segments: [u8; crate::lossy::constants::MB_FEATURE_TREE_PROBS],
pub(crate) bands: CoeffProbas,
pub(crate) use_skip: bool,
pub(crate) skip_p: u8,
}
pub(crate) struct MbData {
pub(crate) coeffs: [i16; 384],
pub(crate) is_i4x4: bool,
pub(crate) imodes: [u8; 16],
pub(crate) uvmode: u8,
pub(crate) non_zero_y: u32,
pub(crate) non_zero_uv: u32,
pub(crate) skip: bool,
pub(crate) segment: u8,
}
impl Default for MbData {
fn default() -> Self {
Self {
coeffs: [0; 384],
is_i4x4: false,
imodes: [0; 16],
uvmode: 0,
non_zero_y: 0,
non_zero_uv: 0,
skip: false,
segment: 0,
}
}
}
#[derive(Clone, Copy, Default)]
pub(crate) struct MbContext {
pub(crate) nz: u32,
pub(crate) nz_dc: u32,
}
pub(crate) struct Frame {
pub(crate) mb_w: usize,
pub(crate) mb_h: usize,
pub(crate) segment: SegmentHeader,
pub(crate) filter: FilterHeader,
pub(crate) filter_type: u8,
pub(crate) proba: Proba,
pub(crate) dqm: [QuantMatrix; NUM_MB_SEGMENTS],
pub(crate) num_parts: usize,
pub(crate) mb_data: Vec<MbData>,
pub(crate) intra_t: Vec<u8>,
pub(crate) intra_l: [u8; 4],
pub(crate) mb_info: Vec<MbContext>,
}
impl Frame {
pub(crate) fn new(fh: FrameHeader) -> Result<Self> {
let width = u32::from(fh.width);
let height = u32::from(fh.height);
Dimensions::new(width, height).map_err(|_| Error::InvalidBitstream {
codec: Codec::Lossy,
})?;
let mb_w = (usize::try_from(width).unwrap_or(0) + 15) >> 4;
let mb_h = (usize::try_from(height).unwrap_or(0) + 15) >> 4;
Ok(Self {
mb_w,
mb_h,
segment: SegmentHeader {
use_segment: false,
update_map: false,
absolute_delta: true,
quantizer: [0; NUM_MB_SEGMENTS],
filter_strength: [0; NUM_MB_SEGMENTS],
},
filter: FilterHeader::default(),
filter_type: 0,
proba: Proba {
segments: [255; crate::lossy::constants::MB_FEATURE_TREE_PROBS],
bands: [[[[0; 11]; 3]; 8]; 4],
use_skip: false,
skip_p: 0,
},
dqm: [QuantMatrix::default(); NUM_MB_SEGMENTS],
num_parts: 1,
mb_data: (0..mb_w).map(|_| MbData::default()).collect(),
intra_t: vec![B_DC_PRED; 4 * mb_w],
intra_l: [B_DC_PRED; 4],
mb_info: vec![MbContext::default(); mb_w + 1],
})
}
pub(crate) fn init_scanline(&mut self) {
self.mb_info[0] = MbContext::default();
self.intra_l = [B_DC_PRED; 4];
}
#[cfg(test)]
pub(crate) fn test_frame(mb_w: usize, mb_h: usize) -> Self {
Self {
mb_w,
mb_h,
segment: SegmentHeader {
use_segment: false,
update_map: false,
absolute_delta: true,
quantizer: [0; NUM_MB_SEGMENTS],
filter_strength: [0; NUM_MB_SEGMENTS],
},
filter: FilterHeader::default(),
filter_type: 0,
proba: Proba {
segments: [255; crate::lossy::constants::MB_FEATURE_TREE_PROBS],
bands: [[[[0; 11]; 3]; 8]; 4],
use_skip: false,
skip_p: 0,
},
dqm: [QuantMatrix::default(); NUM_MB_SEGMENTS],
num_parts: 1,
mb_data: (0..mb_w).map(|_| MbData::default()).collect(),
intra_t: vec![B_DC_PRED; 4 * mb_w],
intra_l: [B_DC_PRED; 4],
mb_info: vec![MbContext::default(); mb_w + 1],
}
}
}
pub(crate) fn reconstruct_to_planes(payload: &[u8]) -> Result<(Planes, usize, usize)> {
let fh = FrameHeader::parse_key_frame(payload)?;
let (width, height) = (usize::from(fh.width), usize::from(fh.height));
let mut frame = Frame::new(fh)?;
let after_header = payload
.get(KEY_FRAME_HEADER_LEN..)
.ok_or(Error::Truncated)?;
let part0_len = usize::try_from(fh.first_partition_size).unwrap_or(usize::MAX);
let part0 = after_header.get(..part0_len).ok_or(Error::Truncated)?;
let after_part0 = &after_header[part0_len..];
let mut br = BoolDecoder::new(part0);
frame.parse_headers(&mut br);
let token_partitions = frame.parse_partitions(&mut br, after_part0)?;
frame.parse_quant(&mut br);
let _update_proba = br.read_flag(); frame.parse_proba(&mut br);
let mut planes = Planes::new(frame.mb_w, frame.mb_h);
let fstrengths = reconstruct::compute_fstrengths(&frame.segment, &frame.filter);
let use_skip = frame.proba.use_skip;
let mut finfo = vec![FInfo::default(); frame.mb_w * frame.mb_h];
let mut token_brs: Vec<BoolDecoder<'_>> = token_partitions
.iter()
.copied()
.map(BoolDecoder::new)
.collect();
let part_mask = frame.num_parts - 1;
for mb_y in 0..frame.mb_h {
frame.parse_intra_mode_row(&mut br);
let token_br = &mut token_brs[mb_y & part_mask];
for mb_x in 0..frame.mb_w {
if frame.mb_data[mb_x].skip {
frame.skip_residuals(mb_x);
} else {
frame.parse_residuals(token_br, mb_x);
}
}
for mb_x in 0..frame.mb_w {
let block = &frame.mb_data[mb_x];
reconstruct::reconstruct_mb(&mut planes, block, mb_x, mb_y, frame.mb_w);
finfo[mb_y * frame.mb_w + mb_x] =
reconstruct::resolve_finfo(fstrengths, block, use_skip);
}
frame.init_scanline();
}
reconstruct::filter_frame(
&mut planes,
&finfo,
frame.mb_w,
frame.mb_h,
frame.filter_type,
);
Ok((planes, width, height))
}
pub(crate) fn decode_frame(payload: &[u8]) -> Result<Image> {
let (planes, width, height) = reconstruct_to_planes(payload)?;
reconstruct::to_image(&planes, width, height)
}
#[cfg(feature = "oracle")]
pub(crate) fn frame_uses_skip(payload: &[u8]) -> Option<bool> {
let fh = FrameHeader::parse_key_frame(payload).ok()?;
let mut frame = Frame::new(fh).ok()?;
let after_header = payload.get(KEY_FRAME_HEADER_LEN..)?;
let part0_len = usize::try_from(fh.first_partition_size).unwrap_or(usize::MAX);
let part0 = after_header.get(..part0_len)?;
let after_part0 = &after_header[part0_len..];
let mut br = BoolDecoder::new(part0);
frame.parse_headers(&mut br);
frame.parse_partitions(&mut br, after_part0).ok()?;
frame.parse_quant(&mut br);
let _update_proba = br.read_flag();
frame.parse_proba(&mut br);
Some(frame.proba.use_skip)
}
#[cfg(feature = "oracle")]
pub(crate) fn frame_filter_level(payload: &[u8]) -> Option<i32> {
let fh = FrameHeader::parse_key_frame(payload).ok()?;
let mut frame = Frame::new(fh).ok()?;
let after_header = payload.get(KEY_FRAME_HEADER_LEN..)?;
let part0_len = usize::try_from(fh.first_partition_size).unwrap_or(usize::MAX);
let part0 = after_header.get(..part0_len)?;
let mut br = BoolDecoder::new(part0);
frame.parse_headers(&mut br);
Some(frame.filter.level)
}
#[cfg(feature = "oracle")]
pub(crate) fn frame_uses_i4x4(payload: &[u8]) -> Option<bool> {
let fh = FrameHeader::parse_key_frame(payload).ok()?;
let mut frame = Frame::new(fh).ok()?;
let after_header = payload.get(KEY_FRAME_HEADER_LEN..)?;
let part0_len = usize::try_from(fh.first_partition_size).unwrap_or(usize::MAX);
let part0 = after_header.get(..part0_len)?;
let after_part0 = &after_header[part0_len..];
let mut br = BoolDecoder::new(part0);
frame.parse_headers(&mut br);
frame.parse_partitions(&mut br, after_part0).ok()?;
frame.parse_quant(&mut br);
let _update_proba = br.read_flag();
frame.parse_proba(&mut br);
for _mb_y in 0..frame.mb_h {
frame.parse_intra_mode_row(&mut br);
if frame.mb_data[..frame.mb_w].iter().any(|d| d.is_i4x4) {
return Some(true);
}
frame.init_scanline();
}
Some(false)
}
#[cfg(feature = "oracle")]
pub(crate) fn frame_segment_count(payload: &[u8]) -> Option<usize> {
let fh = FrameHeader::parse_key_frame(payload).ok()?;
let mut frame = Frame::new(fh).ok()?;
let after_header = payload.get(KEY_FRAME_HEADER_LEN..)?;
let part0_len = usize::try_from(fh.first_partition_size).unwrap_or(usize::MAX);
let part0 = after_header.get(..part0_len)?;
let after_part0 = &after_header[part0_len..];
let mut br = BoolDecoder::new(part0);
frame.parse_headers(&mut br);
frame.parse_partitions(&mut br, after_part0).ok()?;
frame.parse_quant(&mut br);
let _update_proba = br.read_flag();
frame.parse_proba(&mut br);
if !frame.segment.use_segment {
return Some(1);
}
let mut seen = [false; NUM_MB_SEGMENTS];
for _mb_y in 0..frame.mb_h {
frame.parse_intra_mode_row(&mut br);
for d in &frame.mb_data[..frame.mb_w] {
seen[usize::from(d.segment)] = true;
}
frame.init_scanline();
}
Some(seen.iter().filter(|&&b| b).count())
}