use crate::bitreader::BitReader;
use crate::Error;
pub const TOTAL_REFS_PER_FRAME: usize = 8;
pub const MAX_SEGMENTS: usize = 8;
pub const SEG_LVL_MAX: usize = 8;
pub const SEG_LVL_ALT_Q: usize = 0;
pub const SEG_LVL_ALT_LF_Y_V: usize = 1;
pub const SEG_LVL_ALT_LF_Y_H: usize = 2;
pub const SEG_LVL_ALT_LF_U: usize = 3;
pub const SEG_LVL_ALT_LF_V: usize = 4;
pub const SEG_LVL_REF_FRAME: usize = 5;
pub const SEG_LVL_SKIP: usize = 6;
pub const SEG_LVL_GLOBALMV: usize = 7;
pub const MAX_LOOP_FILTER: i16 = 63;
pub const SEGMENTATION_FEATURE_BITS: [u32; SEG_LVL_MAX] = [8, 6, 6, 6, 6, 3, 0, 0];
pub const SEGMENTATION_FEATURE_SIGNED: [bool; SEG_LVL_MAX] =
[true, true, true, true, true, false, false, false];
pub const SEGMENTATION_FEATURE_MAX: [i16; SEG_LVL_MAX] = [
255,
MAX_LOOP_FILTER,
MAX_LOOP_FILTER,
MAX_LOOP_FILTER,
MAX_LOOP_FILTER,
7,
0,
0,
];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InterpolationFilter {
Eighttap,
EighttapSmooth,
EighttapSharp,
Bilinear,
Switchable,
}
impl InterpolationFilter {
pub fn from_raw(raw: u8) -> Self {
match raw & 0x3 {
0 => Self::Eighttap,
1 => Self::EighttapSmooth,
2 => Self::EighttapSharp,
_ => Self::Bilinear,
}
}
pub fn as_raw(&self) -> u8 {
match self {
Self::Eighttap => 0,
Self::EighttapSmooth => 1,
Self::EighttapSharp => 2,
Self::Bilinear => 3,
Self::Switchable => 4,
}
}
pub fn is_switchable(&self) -> bool {
matches!(self, Self::Switchable)
}
}
pub fn parse_interpolation_filter(payload: &[u8]) -> Result<(InterpolationFilter, usize), Error> {
let mut br = BitReader::new(payload);
let filt = read_interpolation_filter(&mut br)?;
Ok((filt, br.position()))
}
pub(crate) fn read_interpolation_filter(
br: &mut BitReader<'_>,
) -> Result<InterpolationFilter, Error> {
let is_filter_switchable = br.f(1)? == 1;
if is_filter_switchable {
Ok(InterpolationFilter::Switchable)
} else {
let raw = br.f(2)? as u8;
Ok(InterpolationFilter::from_raw(raw))
}
}
pub const LOOP_FILTER_REF_DELTAS_DEFAULT: [i8; TOTAL_REFS_PER_FRAME] = {
[1, 0, 0, 0, -1, 0, -1, -1]
};
pub const LOOP_FILTER_MODE_DELTAS_DEFAULT: [i8; 2] = [0, 0];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LoopFilterParams {
pub loop_filter_level: [u8; 4],
pub loop_filter_sharpness: u8,
pub loop_filter_delta_enabled: bool,
pub loop_filter_delta_update: bool,
pub loop_filter_ref_deltas: [i8; TOTAL_REFS_PER_FRAME],
pub loop_filter_mode_deltas: [i8; 2],
pub short_circuited: bool,
}
impl LoopFilterParams {
pub const fn short_circuit() -> Self {
Self {
loop_filter_level: [0; 4],
loop_filter_sharpness: 0,
loop_filter_delta_enabled: false,
loop_filter_delta_update: false,
loop_filter_ref_deltas: LOOP_FILTER_REF_DELTAS_DEFAULT,
loop_filter_mode_deltas: LOOP_FILTER_MODE_DELTAS_DEFAULT,
short_circuited: true,
}
}
}
pub fn parse_loop_filter_params(
payload: &[u8],
num_planes: u8,
coded_lossless: bool,
allow_intrabc: bool,
) -> Result<(LoopFilterParams, usize), Error> {
let mut br = BitReader::new(payload);
let lf = read_loop_filter_params(&mut br, num_planes, coded_lossless, allow_intrabc, None)?;
Ok((lf, br.position()))
}
pub(crate) fn read_loop_filter_params(
br: &mut BitReader<'_>,
num_planes: u8,
coded_lossless: bool,
allow_intrabc: bool,
prev_deltas: Option<(&[i8; TOTAL_REFS_PER_FRAME], &[i8; 2])>,
) -> Result<LoopFilterParams, Error> {
if coded_lossless || allow_intrabc {
return Ok(LoopFilterParams::short_circuit());
}
let mut loop_filter_level = [0u8; 4];
loop_filter_level[0] = br.f(6)? as u8;
loop_filter_level[1] = br.f(6)? as u8;
if num_planes > 1 && (loop_filter_level[0] != 0 || loop_filter_level[1] != 0) {
loop_filter_level[2] = br.f(6)? as u8;
loop_filter_level[3] = br.f(6)? as u8;
}
let loop_filter_sharpness = br.f(3)? as u8;
let loop_filter_delta_enabled = br.f(1)? == 1;
let (mut loop_filter_ref_deltas, mut loop_filter_mode_deltas) = match prev_deltas {
Some((r, m)) => (*r, *m),
None => (
LOOP_FILTER_REF_DELTAS_DEFAULT,
LOOP_FILTER_MODE_DELTAS_DEFAULT,
),
};
let mut loop_filter_delta_update = false;
if loop_filter_delta_enabled {
loop_filter_delta_update = br.f(1)? == 1;
if loop_filter_delta_update {
for slot in loop_filter_ref_deltas.iter_mut() {
let update_ref_delta = br.f(1)? == 1;
if update_ref_delta {
*slot = br.su(7)? as i8;
}
}
for slot in loop_filter_mode_deltas.iter_mut() {
let update_mode_delta = br.f(1)? == 1;
if update_mode_delta {
*slot = br.su(7)? as i8;
}
}
}
}
Ok(LoopFilterParams {
loop_filter_level,
loop_filter_sharpness,
loop_filter_delta_enabled,
loop_filter_delta_update,
loop_filter_ref_deltas,
loop_filter_mode_deltas,
short_circuited: false,
})
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct QuantizationParams {
pub base_q_idx: u8,
pub delta_q_y_dc: i8,
pub diff_uv_delta: bool,
pub delta_q_u_dc: i8,
pub delta_q_u_ac: i8,
pub delta_q_v_dc: i8,
pub delta_q_v_ac: i8,
pub using_qmatrix: bool,
pub qm_y: u8,
pub qm_u: u8,
pub qm_v: u8,
}
pub fn parse_quantization_params(
payload: &[u8],
num_planes: u8,
separate_uv_delta_q: bool,
) -> Result<(QuantizationParams, usize), Error> {
let mut br = BitReader::new(payload);
let q = read_quantization_params(&mut br, num_planes, separate_uv_delta_q)?;
Ok((q, br.position()))
}
pub(crate) fn read_quantization_params(
br: &mut BitReader<'_>,
num_planes: u8,
separate_uv_delta_q: bool,
) -> Result<QuantizationParams, Error> {
let base_q_idx = br.f(8)? as u8;
let delta_q_y_dc = read_delta_q(br)?;
let mut diff_uv_delta = false;
let mut delta_q_u_dc = 0i8;
let mut delta_q_u_ac = 0i8;
let mut delta_q_v_dc = 0i8;
let mut delta_q_v_ac = 0i8;
if num_planes > 1 {
if separate_uv_delta_q {
diff_uv_delta = br.f(1)? == 1;
}
delta_q_u_dc = read_delta_q(br)?;
delta_q_u_ac = read_delta_q(br)?;
if diff_uv_delta {
delta_q_v_dc = read_delta_q(br)?;
delta_q_v_ac = read_delta_q(br)?;
} else {
delta_q_v_dc = delta_q_u_dc;
delta_q_v_ac = delta_q_u_ac;
}
}
let using_qmatrix = br.f(1)? == 1;
let (qm_y, qm_u, qm_v) = if using_qmatrix {
let qm_y = br.f(4)? as u8;
let qm_u = br.f(4)? as u8;
let qm_v = if separate_uv_delta_q {
br.f(4)? as u8
} else {
qm_u
};
(qm_y, qm_u, qm_v)
} else {
(0, 0, 0)
};
Ok(QuantizationParams {
base_q_idx,
delta_q_y_dc,
diff_uv_delta,
delta_q_u_dc,
delta_q_u_ac,
delta_q_v_dc,
delta_q_v_ac,
using_qmatrix,
qm_y,
qm_u,
qm_v,
})
}
fn read_delta_q(br: &mut BitReader<'_>) -> Result<i8, Error> {
let delta_coded = br.f(1)? == 1;
if delta_coded {
Ok(br.su(7)? as i8)
} else {
Ok(0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SegmentationParams {
pub enabled: bool,
pub update_map: bool,
pub temporal_update: bool,
pub update_data: bool,
pub segment_feature_active: [[bool; SEG_LVL_MAX]; MAX_SEGMENTS],
pub segment_feature_data: [[i16; SEG_LVL_MAX]; MAX_SEGMENTS],
pub seg_id_pre_skip: bool,
pub last_active_seg_id: u8,
}
impl SegmentationParams {
pub const fn disabled() -> Self {
Self {
enabled: false,
update_map: false,
temporal_update: false,
update_data: false,
segment_feature_active: [[false; SEG_LVL_MAX]; MAX_SEGMENTS],
segment_feature_data: [[0; SEG_LVL_MAX]; MAX_SEGMENTS],
seg_id_pre_skip: false,
last_active_seg_id: 0,
}
}
}
pub fn parse_segmentation_params(
payload: &[u8],
primary_ref_frame: u8,
) -> Result<(SegmentationParams, usize), Error> {
let mut br = BitReader::new(payload);
let s = read_segmentation_params(&mut br, primary_ref_frame, None)?;
Ok((s, br.position()))
}
pub type LoadedSegFeatures<'a> = (
&'a [[bool; SEG_LVL_MAX]; MAX_SEGMENTS],
&'a [[i16; SEG_LVL_MAX]; MAX_SEGMENTS],
);
pub(crate) fn read_segmentation_params(
br: &mut BitReader<'_>,
primary_ref_frame: u8,
loaded: Option<LoadedSegFeatures<'_>>,
) -> Result<SegmentationParams, Error> {
let enabled = br.f(1)? == 1;
if !enabled {
return Ok(SegmentationParams::disabled());
}
let (update_map, temporal_update, update_data) =
if primary_ref_frame == crate::frame_header::PRIMARY_REF_NONE {
(true, false, true)
} else {
let um = br.f(1)? == 1;
let tu = if um { br.f(1)? == 1 } else { false };
let ud = br.f(1)? == 1;
(um, tu, ud)
};
let (mut feature_active, mut feature_data) = match loaded {
Some((a, d)) => (*a, *d),
None => (
[[false; SEG_LVL_MAX]; MAX_SEGMENTS],
[[0i16; SEG_LVL_MAX]; MAX_SEGMENTS],
),
};
if update_data {
feature_active = [[false; SEG_LVL_MAX]; MAX_SEGMENTS];
feature_data = [[0i16; SEG_LVL_MAX]; MAX_SEGMENTS];
for i in 0..MAX_SEGMENTS {
for j in 0..SEG_LVL_MAX {
let feature_enabled = br.f(1)? == 1;
feature_active[i][j] = feature_enabled;
let clipped = if feature_enabled {
let bits_to_read = SEGMENTATION_FEATURE_BITS[j];
let limit = SEGMENTATION_FEATURE_MAX[j];
if SEGMENTATION_FEATURE_SIGNED[j] {
let feature_value = br.su(1 + bits_to_read)?;
clip3_i32(-i32::from(limit), i32::from(limit), feature_value) as i16
} else if bits_to_read == 0 {
0
} else {
let feature_value = br.f(bits_to_read)? as i32;
clip3_i32(0, i32::from(limit), feature_value) as i16
}
} else {
0
};
feature_data[i][j] = clipped;
}
}
}
let mut seg_id_pre_skip = false;
let mut last_active_seg_id: u8 = 0;
for (i, row) in feature_active.iter().enumerate() {
for (j, &active) in row.iter().enumerate() {
if active {
last_active_seg_id = i as u8;
if j >= SEG_LVL_REF_FRAME {
seg_id_pre_skip = true;
}
}
}
}
Ok(SegmentationParams {
enabled,
update_map,
temporal_update,
update_data,
segment_feature_active: feature_active,
segment_feature_data: feature_data,
seg_id_pre_skip,
last_active_seg_id,
})
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct DeltaQParams {
pub delta_q_present: bool,
pub delta_q_res: u8,
}
pub fn parse_delta_q_params(
payload: &[u8],
base_q_idx: u8,
) -> Result<(DeltaQParams, usize), Error> {
let mut br = BitReader::new(payload);
let d = read_delta_q_params(&mut br, base_q_idx)?;
Ok((d, br.position()))
}
pub(crate) fn read_delta_q_params(
br: &mut BitReader<'_>,
base_q_idx: u8,
) -> Result<DeltaQParams, Error> {
let mut delta_q_present = false;
let mut delta_q_res = 0u8;
if base_q_idx > 0 {
delta_q_present = br.f(1)? == 1;
}
if delta_q_present {
delta_q_res = br.f(2)? as u8;
}
Ok(DeltaQParams {
delta_q_present,
delta_q_res,
})
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct DeltaLfParams {
pub delta_lf_present: bool,
pub delta_lf_res: u8,
pub delta_lf_multi: bool,
}
pub fn parse_delta_lf_params(
payload: &[u8],
delta_q_present: bool,
allow_intrabc: bool,
) -> Result<(DeltaLfParams, usize), Error> {
let mut br = BitReader::new(payload);
let d = read_delta_lf_params(&mut br, delta_q_present, allow_intrabc)?;
Ok((d, br.position()))
}
pub(crate) fn read_delta_lf_params(
br: &mut BitReader<'_>,
delta_q_present: bool,
allow_intrabc: bool,
) -> Result<DeltaLfParams, Error> {
let mut delta_lf_present = false;
let mut delta_lf_res = 0u8;
let mut delta_lf_multi = false;
if delta_q_present {
if !allow_intrabc {
delta_lf_present = br.f(1)? == 1;
}
if delta_lf_present {
delta_lf_res = br.f(2)? as u8;
delta_lf_multi = br.f(1)? == 1;
}
}
Ok(DeltaLfParams {
delta_lf_present,
delta_lf_res,
delta_lf_multi,
})
}
pub const CDEF_MAX_STRENGTHS: usize = 8;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CdefParams {
pub cdef_damping: u8,
pub cdef_bits: u8,
pub cdef_y_pri_strength: [u8; CDEF_MAX_STRENGTHS],
pub cdef_y_sec_strength: [u8; CDEF_MAX_STRENGTHS],
pub cdef_uv_pri_strength: [u8; CDEF_MAX_STRENGTHS],
pub cdef_uv_sec_strength: [u8; CDEF_MAX_STRENGTHS],
pub short_circuited: bool,
}
impl CdefParams {
pub const fn short_circuit() -> Self {
Self {
cdef_damping: 3,
cdef_bits: 0,
cdef_y_pri_strength: [0; CDEF_MAX_STRENGTHS],
cdef_y_sec_strength: [0; CDEF_MAX_STRENGTHS],
cdef_uv_pri_strength: [0; CDEF_MAX_STRENGTHS],
cdef_uv_sec_strength: [0; CDEF_MAX_STRENGTHS],
short_circuited: true,
}
}
}
pub fn parse_cdef_params(
payload: &[u8],
num_planes: u8,
coded_lossless: bool,
allow_intrabc: bool,
enable_cdef: bool,
) -> Result<(CdefParams, usize), Error> {
let mut br = BitReader::new(payload);
let cdef = read_cdef_params(
&mut br,
num_planes,
coded_lossless,
allow_intrabc,
enable_cdef,
)?;
Ok((cdef, br.position()))
}
pub(crate) fn read_cdef_params(
br: &mut BitReader<'_>,
num_planes: u8,
coded_lossless: bool,
allow_intrabc: bool,
enable_cdef: bool,
) -> Result<CdefParams, Error> {
if coded_lossless || allow_intrabc || !enable_cdef {
return Ok(CdefParams::short_circuit());
}
let cdef_damping_minus_3 = br.f(2)? as u8;
let cdef_damping = cdef_damping_minus_3 + 3;
let cdef_bits = br.f(2)? as u8;
let mut cdef_y_pri_strength = [0u8; CDEF_MAX_STRENGTHS];
let mut cdef_y_sec_strength = [0u8; CDEF_MAX_STRENGTHS];
let mut cdef_uv_pri_strength = [0u8; CDEF_MAX_STRENGTHS];
let mut cdef_uv_sec_strength = [0u8; CDEF_MAX_STRENGTHS];
let count = 1usize << cdef_bits;
for i in 0..count {
cdef_y_pri_strength[i] = br.f(4)? as u8;
let mut y_sec = br.f(2)? as u8;
if y_sec == 3 {
y_sec += 1;
}
cdef_y_sec_strength[i] = y_sec;
if num_planes > 1 {
cdef_uv_pri_strength[i] = br.f(4)? as u8;
let mut uv_sec = br.f(2)? as u8;
if uv_sec == 3 {
uv_sec += 1;
}
cdef_uv_sec_strength[i] = uv_sec;
}
}
Ok(CdefParams {
cdef_damping,
cdef_bits,
cdef_y_pri_strength,
cdef_y_sec_strength,
cdef_uv_pri_strength,
cdef_uv_sec_strength,
short_circuited: false,
})
}
pub const RESTORATION_TILESIZE_MAX: u32 = 256;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum FrameRestorationType {
None = 0,
Wiener = 1,
SgrProj = 2,
Switchable = 3,
}
impl FrameRestorationType {
#[must_use]
pub const fn remap(lr_type: u8) -> Self {
match lr_type {
0 => FrameRestorationType::None,
1 => FrameRestorationType::Switchable,
2 => FrameRestorationType::Wiener,
3 => FrameRestorationType::SgrProj,
_ => FrameRestorationType::None,
}
}
#[must_use]
pub const fn as_u8(self) -> u8 {
self as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LrParams {
pub frame_restoration_type: [FrameRestorationType; 3],
pub uses_lr: bool,
pub uses_chroma_lr: bool,
pub lr_unit_shift: u8,
pub lr_uv_shift: u8,
pub loop_restoration_size: [u32; 3],
pub short_circuited: bool,
}
impl LrParams {
#[must_use]
pub const fn short_circuit() -> Self {
Self {
frame_restoration_type: [FrameRestorationType::None; 3],
uses_lr: false,
uses_chroma_lr: false,
lr_unit_shift: 0,
lr_uv_shift: 0,
loop_restoration_size: [0; 3],
short_circuited: true,
}
}
}
#[allow(clippy::too_many_arguments)]
pub fn parse_lr_params(
payload: &[u8],
num_planes: u8,
subsampling_x: bool,
subsampling_y: bool,
use_128x128_superblock: bool,
all_lossless: bool,
allow_intrabc: bool,
enable_restoration: bool,
) -> Result<(LrParams, usize), Error> {
let mut br = BitReader::new(payload);
let lr = read_lr_params(
&mut br,
num_planes,
subsampling_x,
subsampling_y,
use_128x128_superblock,
all_lossless,
allow_intrabc,
enable_restoration,
)?;
Ok((lr, br.position()))
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn read_lr_params(
br: &mut BitReader<'_>,
num_planes: u8,
subsampling_x: bool,
subsampling_y: bool,
use_128x128_superblock: bool,
all_lossless: bool,
allow_intrabc: bool,
enable_restoration: bool,
) -> Result<LrParams, Error> {
if all_lossless || allow_intrabc || !enable_restoration {
return Ok(LrParams::short_circuit());
}
let mut frame_restoration_type = [FrameRestorationType::None; 3];
let mut uses_lr = false;
let mut uses_chroma_lr = false;
for (i, slot) in frame_restoration_type
.iter_mut()
.enumerate()
.take(usize::from(num_planes))
{
let lr_type = br.f(2)? as u8;
let rtype = FrameRestorationType::remap(lr_type);
*slot = rtype;
if rtype != FrameRestorationType::None {
uses_lr = true;
if i > 0 {
uses_chroma_lr = true;
}
}
}
let mut lr_unit_shift = 0u8;
let mut lr_uv_shift = 0u8;
let mut loop_restoration_size = [0u32; 3];
if uses_lr {
if use_128x128_superblock {
lr_unit_shift = br.f(1)? as u8;
lr_unit_shift += 1;
} else {
lr_unit_shift = br.f(1)? as u8;
if lr_unit_shift != 0 {
let lr_unit_extra_shift = br.f(1)? as u8;
lr_unit_shift += lr_unit_extra_shift;
}
}
loop_restoration_size[0] = RESTORATION_TILESIZE_MAX >> (2 - u32::from(lr_unit_shift));
if subsampling_x && subsampling_y && uses_chroma_lr {
lr_uv_shift = br.f(1)? as u8;
} else {
lr_uv_shift = 0;
}
loop_restoration_size[1] = loop_restoration_size[0] >> u32::from(lr_uv_shift);
loop_restoration_size[2] = loop_restoration_size[0] >> u32::from(lr_uv_shift);
}
Ok(LrParams {
frame_restoration_type,
uses_lr,
uses_chroma_lr,
lr_unit_shift,
lr_uv_shift,
loop_restoration_size,
short_circuited: false,
})
}
pub const TX_MODES: u8 = 3;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum TxMode {
Only4x4 = 0,
TxModeLargest = 1,
TxModeSelect = 2,
}
impl TxMode {
#[must_use]
pub const fn as_u8(self) -> u8 {
self as u8
}
}
pub fn parse_tx_mode(payload: &[u8], coded_lossless: bool) -> Result<(TxMode, usize), Error> {
let mut br = BitReader::new(payload);
let tx_mode = read_tx_mode(&mut br, coded_lossless)?;
Ok((tx_mode, br.position()))
}
pub(crate) fn read_tx_mode(br: &mut BitReader<'_>, coded_lossless: bool) -> Result<TxMode, Error> {
if coded_lossless {
return Ok(TxMode::Only4x4);
}
let tx_mode_select = br.f(1)? == 1;
Ok(if tx_mode_select {
TxMode::TxModeSelect
} else {
TxMode::TxModeLargest
})
}
pub const REFS_PER_FRAME: usize = 7;
pub const INTRA_FRAME: usize = 0;
pub const LAST_FRAME: usize = 1;
pub const LAST2_FRAME: usize = 2;
pub const LAST3_FRAME: usize = 3;
pub const GOLDEN_FRAME: usize = 4;
pub const BWDREF_FRAME: usize = 5;
pub const ALTREF2_FRAME: usize = 6;
pub const ALTREF_FRAME: usize = 7;
pub const SINGLE_REFERENCE: u8 = 0;
pub const COMPOUND_REFERENCE: u8 = 1;
pub const UNIDIR_COMP_REFERENCE: u8 = 0;
pub const BIDIR_COMP_REFERENCE: u8 = 1;
pub const WARPEDMODEL_PREC_BITS: u32 = 16;
pub const GM_ABS_TRANS_BITS: u32 = 12;
pub const GM_ABS_TRANS_ONLY_BITS: u32 = 9;
pub const GM_ABS_ALPHA_BITS: u32 = 12;
pub const GM_ALPHA_PREC_BITS: u32 = 15;
pub const GM_TRANS_PREC_BITS: u32 = 6;
pub const GM_TRANS_ONLY_PREC_BITS: u32 = 3;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum WarpModelType {
Identity = 0,
Translation = 1,
RotZoom = 2,
Affine = 3,
}
impl WarpModelType {
#[must_use]
pub const fn as_u8(self) -> u8 {
self as u8
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GlobalMotionParams {
pub gm_type: [WarpModelType; TOTAL_REFS_PER_FRAME],
pub gm_params: [[i32; 6]; TOTAL_REFS_PER_FRAME],
pub short_circuited: bool,
}
impl GlobalMotionParams {
#[must_use]
pub const fn identity() -> Self {
let one = 1i32 << WARPEDMODEL_PREC_BITS;
Self {
gm_type: [WarpModelType::Identity; TOTAL_REFS_PER_FRAME],
gm_params: [[0, 0, one, 0, 0, one]; TOTAL_REFS_PER_FRAME],
short_circuited: true,
}
}
}
#[must_use]
pub const fn prev_gm_params_default() -> [[i32; 6]; TOTAL_REFS_PER_FRAME] {
let one = 1i32 << WARPEDMODEL_PREC_BITS;
[[0, 0, one, 0, 0, one]; TOTAL_REFS_PER_FRAME]
}
fn inverse_recenter(r: i64, v: i64) -> i64 {
if v > 2 * r {
v
} else if v & 1 != 0 {
r - ((v + 1) >> 1)
} else {
r + (v >> 1)
}
}
fn decode_subexp(br: &mut BitReader<'_>, num_syms: u32) -> Result<u32, Error> {
let mut i: u32 = 0;
let mut mk: u32 = 0;
let k: u32 = 3;
loop {
let b2 = if i != 0 { k + i - 1 } else { k };
let a = 1u32 << b2;
if num_syms <= mk + 3 * a {
let subexp_final_bits = br.ns(num_syms - mk)?;
return Ok(subexp_final_bits + mk);
}
let subexp_more_bits = br.f(1)? == 1;
if subexp_more_bits {
i += 1;
mk += a;
} else {
let subexp_bits = br.f(b2)? as u32;
return Ok(subexp_bits + mk);
}
}
}
fn decode_unsigned_subexp_with_ref(br: &mut BitReader<'_>, mx: i64, r: i64) -> Result<i64, Error> {
let v = i64::from(decode_subexp(br, mx as u32)?);
if (r << 1) <= mx {
Ok(inverse_recenter(r, v))
} else {
Ok(mx - 1 - inverse_recenter(mx - 1 - r, v))
}
}
fn decode_signed_subexp_with_ref(
br: &mut BitReader<'_>,
low: i64,
high: i64,
r: i64,
) -> Result<i64, Error> {
let x = decode_unsigned_subexp_with_ref(br, high - low, r - low)?;
Ok(x + low)
}
#[allow(clippy::too_many_arguments)]
fn read_global_param(
br: &mut BitReader<'_>,
gm_type: WarpModelType,
gm_params: &mut [[i32; 6]; TOTAL_REFS_PER_FRAME],
prev_gm_params: &[[i32; 6]; TOTAL_REFS_PER_FRAME],
allow_high_precision_mv: bool,
ref_idx: usize,
idx: usize,
) -> Result<(), Error> {
let not_high_prec: u32 = u32::from(!allow_high_precision_mv);
let (abs_bits, prec_bits) = if idx < 2 {
if gm_type == WarpModelType::Translation {
(
GM_ABS_TRANS_ONLY_BITS - not_high_prec,
GM_TRANS_ONLY_PREC_BITS - not_high_prec,
)
} else {
(GM_ABS_TRANS_BITS, GM_TRANS_PREC_BITS)
}
} else {
(GM_ABS_ALPHA_BITS, GM_ALPHA_PREC_BITS)
};
let prec_diff = WARPEDMODEL_PREC_BITS - prec_bits;
let round: i64 = if idx % 3 == 2 {
1i64 << WARPEDMODEL_PREC_BITS
} else {
0
};
let sub: i64 = if idx % 3 == 2 { 1i64 << prec_bits } else { 0 };
let mx: i64 = 1i64 << abs_bits;
let r: i64 = (i64::from(prev_gm_params[ref_idx][idx]) >> prec_diff) - sub;
let decoded = decode_signed_subexp_with_ref(br, -mx, mx + 1, r)?;
let value = (decoded << prec_diff) + round;
gm_params[ref_idx][idx] = value as i32;
Ok(())
}
pub fn parse_global_motion_params(
payload: &[u8],
frame_is_intra: bool,
allow_high_precision_mv: bool,
prev_gm_params: &[[i32; 6]; TOTAL_REFS_PER_FRAME],
) -> Result<(GlobalMotionParams, usize), Error> {
let mut br = BitReader::new(payload);
let gm = read_global_motion_params(
&mut br,
frame_is_intra,
allow_high_precision_mv,
prev_gm_params,
)?;
Ok((gm, br.position()))
}
pub(crate) fn read_global_motion_params(
br: &mut BitReader<'_>,
frame_is_intra: bool,
allow_high_precision_mv: bool,
prev_gm_params: &[[i32; 6]; TOTAL_REFS_PER_FRAME],
) -> Result<GlobalMotionParams, Error> {
let mut gm = GlobalMotionParams::identity();
if frame_is_intra {
return Ok(gm);
}
gm.short_circuited = false;
for ref_idx in LAST_FRAME..=ALTREF_FRAME {
let is_global = br.f(1)? == 1;
let gm_type = if is_global {
let is_rot_zoom = br.f(1)? == 1;
if is_rot_zoom {
WarpModelType::RotZoom
} else {
let is_translation = br.f(1)? == 1;
if is_translation {
WarpModelType::Translation
} else {
WarpModelType::Affine
}
}
} else {
WarpModelType::Identity
};
gm.gm_type[ref_idx] = gm_type;
if gm_type as u8 >= WarpModelType::RotZoom as u8 {
read_global_param(
br,
gm_type,
&mut gm.gm_params,
prev_gm_params,
allow_high_precision_mv,
ref_idx,
2,
)?;
read_global_param(
br,
gm_type,
&mut gm.gm_params,
prev_gm_params,
allow_high_precision_mv,
ref_idx,
3,
)?;
if gm_type == WarpModelType::Affine {
read_global_param(
br,
gm_type,
&mut gm.gm_params,
prev_gm_params,
allow_high_precision_mv,
ref_idx,
4,
)?;
read_global_param(
br,
gm_type,
&mut gm.gm_params,
prev_gm_params,
allow_high_precision_mv,
ref_idx,
5,
)?;
} else {
gm.gm_params[ref_idx][4] = -gm.gm_params[ref_idx][3];
gm.gm_params[ref_idx][5] = gm.gm_params[ref_idx][2];
}
}
if gm_type as u8 >= WarpModelType::Translation as u8 {
read_global_param(
br,
gm_type,
&mut gm.gm_params,
prev_gm_params,
allow_high_precision_mv,
ref_idx,
0,
)?;
read_global_param(
br,
gm_type,
&mut gm.gm_params,
prev_gm_params,
allow_high_precision_mv,
ref_idx,
1,
)?;
}
}
Ok(gm)
}
pub const MAX_NUM_Y_POINTS: usize = 14;
pub const MAX_NUM_CHROMA_POINTS: usize = 10;
pub const MAX_AR_COEFFS_Y: usize = 24;
pub const MAX_AR_COEFFS_UV: usize = 25;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FilmGrainParams {
pub apply_grain: bool,
pub grain_seed: u16,
pub update_grain: bool,
pub film_grain_params_ref_idx: u8,
pub num_y_points: u8,
pub point_y_value: [u8; MAX_NUM_Y_POINTS],
pub point_y_scaling: [u8; MAX_NUM_Y_POINTS],
pub chroma_scaling_from_luma: bool,
pub num_cb_points: u8,
pub point_cb_value: [u8; MAX_NUM_CHROMA_POINTS],
pub point_cb_scaling: [u8; MAX_NUM_CHROMA_POINTS],
pub num_cr_points: u8,
pub point_cr_value: [u8; MAX_NUM_CHROMA_POINTS],
pub point_cr_scaling: [u8; MAX_NUM_CHROMA_POINTS],
pub grain_scaling: u8,
pub ar_coeff_lag: u8,
pub ar_coeffs_y_plus_128: [u8; MAX_AR_COEFFS_Y],
pub ar_coeffs_cb_plus_128: [u8; MAX_AR_COEFFS_UV],
pub ar_coeffs_cr_plus_128: [u8; MAX_AR_COEFFS_UV],
pub ar_coeff_shift: u8,
pub grain_scale_shift: u8,
pub cb_mult: u8,
pub cb_luma_mult: u8,
pub cb_offset: u16,
pub cr_mult: u8,
pub cr_luma_mult: u8,
pub cr_offset: u16,
pub overlap_flag: bool,
pub clip_to_restricted_range: bool,
pub predicted: bool,
}
impl FilmGrainParams {
#[must_use]
pub const fn reset() -> Self {
Self {
apply_grain: false,
grain_seed: 0,
update_grain: false,
film_grain_params_ref_idx: 0,
num_y_points: 0,
point_y_value: [0; MAX_NUM_Y_POINTS],
point_y_scaling: [0; MAX_NUM_Y_POINTS],
chroma_scaling_from_luma: false,
num_cb_points: 0,
point_cb_value: [0; MAX_NUM_CHROMA_POINTS],
point_cb_scaling: [0; MAX_NUM_CHROMA_POINTS],
num_cr_points: 0,
point_cr_value: [0; MAX_NUM_CHROMA_POINTS],
point_cr_scaling: [0; MAX_NUM_CHROMA_POINTS],
grain_scaling: 0,
ar_coeff_lag: 0,
ar_coeffs_y_plus_128: [0; MAX_AR_COEFFS_Y],
ar_coeffs_cb_plus_128: [0; MAX_AR_COEFFS_UV],
ar_coeffs_cr_plus_128: [0; MAX_AR_COEFFS_UV],
ar_coeff_shift: 0,
grain_scale_shift: 0,
cb_mult: 0,
cb_luma_mult: 0,
cb_offset: 0,
cr_mult: 0,
cr_luma_mult: 0,
cr_offset: 0,
overlap_flag: false,
clip_to_restricted_range: false,
predicted: false,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct FilmGrainContext {
pub film_grain_params_present: bool,
pub show_frame: bool,
pub showable_frame: bool,
pub is_inter_frame: bool,
pub mono_chrome: bool,
pub subsampling_x: bool,
pub subsampling_y: bool,
}
pub fn parse_film_grain_params(
payload: &[u8],
ctx: FilmGrainContext,
) -> Result<(FilmGrainParams, usize), Error> {
let mut br = BitReader::new(payload);
let fg = read_film_grain_params(&mut br, ctx)?;
Ok((fg, br.position()))
}
pub(crate) fn read_film_grain_params(
br: &mut BitReader<'_>,
ctx: FilmGrainContext,
) -> Result<FilmGrainParams, Error> {
if !ctx.film_grain_params_present || (!ctx.show_frame && !ctx.showable_frame) {
return Ok(FilmGrainParams::reset());
}
let apply_grain = br.f(1)? == 1;
if !apply_grain {
return Ok(FilmGrainParams::reset());
}
let mut fg = FilmGrainParams::reset();
fg.apply_grain = true;
fg.grain_seed = br.f(16)? as u16;
fg.update_grain = if ctx.is_inter_frame {
br.f(1)? == 1
} else {
true
};
if !fg.update_grain {
fg.film_grain_params_ref_idx = br.f(3)? as u8;
fg.predicted = true;
return Ok(fg);
}
fg.num_y_points = br.f(4)? as u8;
if usize::from(fg.num_y_points) > MAX_NUM_Y_POINTS {
return Err(Error::FilmGrainPointCountOverflow);
}
for i in 0..usize::from(fg.num_y_points) {
fg.point_y_value[i] = br.f(8)? as u8;
fg.point_y_scaling[i] = br.f(8)? as u8;
}
fg.chroma_scaling_from_luma = if ctx.mono_chrome {
false
} else {
br.f(1)? == 1
};
let suppress_chroma = ctx.mono_chrome
|| fg.chroma_scaling_from_luma
|| (ctx.subsampling_x && ctx.subsampling_y && fg.num_y_points == 0);
if suppress_chroma {
fg.num_cb_points = 0;
fg.num_cr_points = 0;
} else {
fg.num_cb_points = br.f(4)? as u8;
if usize::from(fg.num_cb_points) > MAX_NUM_CHROMA_POINTS {
return Err(Error::FilmGrainPointCountOverflow);
}
for i in 0..usize::from(fg.num_cb_points) {
fg.point_cb_value[i] = br.f(8)? as u8;
fg.point_cb_scaling[i] = br.f(8)? as u8;
}
fg.num_cr_points = br.f(4)? as u8;
if usize::from(fg.num_cr_points) > MAX_NUM_CHROMA_POINTS {
return Err(Error::FilmGrainPointCountOverflow);
}
for i in 0..usize::from(fg.num_cr_points) {
fg.point_cr_value[i] = br.f(8)? as u8;
fg.point_cr_scaling[i] = br.f(8)? as u8;
}
}
let grain_scaling_minus_8 = br.f(2)? as u8;
fg.grain_scaling = grain_scaling_minus_8 + 8;
fg.ar_coeff_lag = br.f(2)? as u8;
let num_pos_luma = 2 * usize::from(fg.ar_coeff_lag) * (usize::from(fg.ar_coeff_lag) + 1);
let num_pos_chroma = if fg.num_y_points > 0 {
for i in 0..num_pos_luma {
fg.ar_coeffs_y_plus_128[i] = br.f(8)? as u8;
}
num_pos_luma + 1
} else {
num_pos_luma
};
if fg.chroma_scaling_from_luma || fg.num_cb_points > 0 {
for i in 0..num_pos_chroma {
fg.ar_coeffs_cb_plus_128[i] = br.f(8)? as u8;
}
}
if fg.chroma_scaling_from_luma || fg.num_cr_points > 0 {
for i in 0..num_pos_chroma {
fg.ar_coeffs_cr_plus_128[i] = br.f(8)? as u8;
}
}
let ar_coeff_shift_minus_6 = br.f(2)? as u8;
fg.ar_coeff_shift = ar_coeff_shift_minus_6 + 6;
fg.grain_scale_shift = br.f(2)? as u8;
if fg.num_cb_points > 0 {
fg.cb_mult = br.f(8)? as u8;
fg.cb_luma_mult = br.f(8)? as u8;
fg.cb_offset = br.f(9)? as u16;
}
if fg.num_cr_points > 0 {
fg.cr_mult = br.f(8)? as u8;
fg.cr_luma_mult = br.f(8)? as u8;
fg.cr_offset = br.f(9)? as u16;
}
fg.overlap_flag = br.f(1)? == 1;
fg.clip_to_restricted_range = br.f(1)? == 1;
Ok(fg)
}
fn clip3_i32(a: i32, b: i32, x: i32) -> i32 {
if x < a {
a
} else if x > b {
b
} else {
x
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn interpolation_filter_switchable_one_bit() {
let payload = [0b1000_0000u8];
let (filt, n) = parse_interpolation_filter(&payload).expect("decodes");
assert_eq!(filt, InterpolationFilter::Switchable);
assert!(filt.is_switchable());
assert_eq!(n, 1);
}
#[test]
fn interpolation_filter_eighttap_three_bits() {
let payload = [0b0000_0000u8];
let (filt, n) = parse_interpolation_filter(&payload).expect("decodes");
assert_eq!(filt, InterpolationFilter::Eighttap);
assert_eq!(n, 3);
}
#[test]
fn interpolation_filter_eighttap_smooth() {
let payload = [0b0010_0000u8];
let (filt, n) = parse_interpolation_filter(&payload).expect("decodes");
assert_eq!(filt, InterpolationFilter::EighttapSmooth);
assert_eq!(n, 3);
}
#[test]
fn interpolation_filter_eighttap_sharp() {
let payload = [0b0100_0000u8];
let (filt, _) = parse_interpolation_filter(&payload).expect("decodes");
assert_eq!(filt, InterpolationFilter::EighttapSharp);
}
#[test]
fn interpolation_filter_bilinear() {
let payload = [0b0110_0000u8];
let (filt, _) = parse_interpolation_filter(&payload).expect("decodes");
assert_eq!(filt, InterpolationFilter::Bilinear);
}
#[test]
fn interpolation_filter_raw_round_trip() {
for raw in 0u8..4u8 {
let f = InterpolationFilter::from_raw(raw);
assert_eq!(f.as_raw(), raw);
}
assert_eq!(InterpolationFilter::Switchable.as_raw(), 4);
}
#[test]
fn interpolation_filter_unexpected_end() {
let payload: [u8; 0] = [];
let err = parse_interpolation_filter(&payload).expect_err("must err");
assert_eq!(err, Error::UnexpectedEnd);
}
#[test]
fn loop_filter_short_circuit_coded_lossless() {
let payload: [u8; 0] = [];
let (lf, n) = parse_loop_filter_params(&payload, 3, true, false).expect("decodes");
assert!(lf.short_circuited);
assert_eq!(n, 0);
assert_eq!(lf.loop_filter_level, [0; 4]);
assert_eq!(lf.loop_filter_sharpness, 0);
assert!(!lf.loop_filter_delta_enabled);
assert_eq!(lf.loop_filter_ref_deltas, LOOP_FILTER_REF_DELTAS_DEFAULT);
assert_eq!(lf.loop_filter_mode_deltas, LOOP_FILTER_MODE_DELTAS_DEFAULT);
assert_eq!(lf.loop_filter_ref_deltas[0], 1);
assert_eq!(lf.loop_filter_ref_deltas[4], -1);
assert_eq!(lf.loop_filter_ref_deltas[6], -1);
assert_eq!(lf.loop_filter_ref_deltas[7], -1);
}
#[test]
fn loop_filter_short_circuit_allow_intrabc() {
let payload: [u8; 0] = [];
let (lf, n) = parse_loop_filter_params(&payload, 3, false, true).expect("decodes");
assert!(lf.short_circuited);
assert_eq!(n, 0);
}
#[test]
fn loop_filter_full_path_levels_only() {
let payload = [0u8; 2];
let (lf, n) = parse_loop_filter_params(&payload, 3, false, false).expect("decodes");
assert!(!lf.short_circuited);
assert_eq!(lf.loop_filter_level, [0; 4]);
assert_eq!(lf.loop_filter_sharpness, 0);
assert!(!lf.loop_filter_delta_enabled);
assert!(!lf.loop_filter_delta_update);
assert_eq!(n, 16);
}
#[test]
fn loop_filter_levels_with_planes() {
let bits: &[u8] = &[
1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, ];
let mut payload = vec![0u8; bits.len().div_ceil(8)];
for (i, &b) in bits.iter().enumerate() {
if b != 0 {
payload[i / 8] |= 1 << (7 - (i % 8));
}
}
let (lf, n) = parse_loop_filter_params(&payload, 3, false, false).expect("decodes");
assert_eq!(lf.loop_filter_level, [42, 17, 3, 4]);
assert_eq!(lf.loop_filter_sharpness, 5);
assert!(!lf.loop_filter_delta_enabled);
assert_eq!(n, bits.len());
}
#[test]
fn loop_filter_mono_skips_plane2_plane3() {
let bits: &[u8] = &[
0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, ];
let mut payload = vec![0u8; bits.len().div_ceil(8)];
for (i, &b) in bits.iter().enumerate() {
if b != 0 {
payload[i / 8] |= 1 << (7 - (i % 8));
}
}
let (lf, n) = parse_loop_filter_params(&payload, 1, false, false).expect("decodes");
assert_eq!(lf.loop_filter_level, [10, 20, 0, 0]);
assert_eq!(n, 16);
}
#[test]
fn loop_filter_delta_update_walks_refs_and_modes() {
let mut bits: Vec<u8> = vec![
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, ];
bits.push(1); for i in (0..7).rev() {
bits.push(((0x7Du32 >> i) & 1) as u8); }
bits.resize(bits.len() + (TOTAL_REFS_PER_FRAME - 1), 0);
bits.push(0); bits.push(1); for i in (0..7).rev() {
bits.push(((5u32 >> i) & 1) as u8); }
let mut payload = vec![0u8; bits.len().div_ceil(8)];
for (i, &b) in bits.iter().enumerate() {
if b != 0 {
payload[i / 8] |= 1 << (7 - (i % 8));
}
}
let (lf, n) = parse_loop_filter_params(&payload, 1, false, false).expect("decodes");
assert!(lf.loop_filter_delta_enabled);
assert!(lf.loop_filter_delta_update);
assert_eq!(lf.loop_filter_ref_deltas[0], -3);
assert_eq!(lf.loop_filter_mode_deltas[1], 5);
assert_eq!(n, bits.len());
}
#[test]
fn quantization_mono_no_qm() {
let payload = [0x40u8, 0x00];
let (q, n) = parse_quantization_params(&payload, 1, false).expect("decodes");
assert_eq!(q.base_q_idx, 64);
assert_eq!(q.delta_q_y_dc, 0);
assert_eq!(q.delta_q_u_dc, 0);
assert_eq!(q.delta_q_u_ac, 0);
assert_eq!(q.delta_q_v_dc, 0);
assert_eq!(q.delta_q_v_ac, 0);
assert!(!q.using_qmatrix);
assert!(!q.diff_uv_delta);
assert_eq!(n, 10);
}
#[test]
fn quantization_3plane_no_separate_uv() {
let bits: Vec<u8> = {
let mut b = vec![];
for i in (0..8).rev() {
b.push(((128u32 >> i) & 1) as u8);
}
b.push(1);
for i in (0..7).rev() {
b.push(((0x7Cu32 >> i) & 1) as u8);
}
b.push(0);
b.push(1);
for i in (0..7).rev() {
b.push(((3u32 >> i) & 1) as u8);
}
b.push(0);
b
};
let mut payload = vec![0u8; bits.len().div_ceil(8)];
for (i, &b) in bits.iter().enumerate() {
if b != 0 {
payload[i / 8] |= 1 << (7 - (i % 8));
}
}
let (q, n) = parse_quantization_params(&payload, 3, false).expect("decodes");
assert_eq!(q.base_q_idx, 128);
assert_eq!(q.delta_q_y_dc, -4);
assert!(!q.diff_uv_delta);
assert_eq!(q.delta_q_u_dc, 0);
assert_eq!(q.delta_q_u_ac, 3);
assert_eq!(q.delta_q_v_dc, q.delta_q_u_dc);
assert_eq!(q.delta_q_v_ac, q.delta_q_u_ac);
assert!(!q.using_qmatrix);
assert_eq!(n, bits.len());
}
#[test]
fn quantization_separate_uv_with_diff() {
let mut bits = vec![];
for i in (0..8).rev() {
bits.push(((100u32 >> i) & 1) as u8);
}
bits.push(0); bits.push(1); bits.push(1); for i in (0..7).rev() {
bits.push(((1u32 >> i) & 1) as u8);
}
bits.push(0); bits.push(1); for i in (0..7).rev() {
bits.push(((0x7Fu32 >> i) & 1) as u8); }
bits.push(0); bits.push(1); for i in (0..4).rev() {
bits.push(((3u32 >> i) & 1) as u8); }
for i in (0..4).rev() {
bits.push(((4u32 >> i) & 1) as u8); }
for i in (0..4).rev() {
bits.push(((5u32 >> i) & 1) as u8); }
let mut payload = vec![0u8; bits.len().div_ceil(8)];
for (i, &b) in bits.iter().enumerate() {
if b != 0 {
payload[i / 8] |= 1 << (7 - (i % 8));
}
}
let (q, n) = parse_quantization_params(&payload, 3, true).expect("decodes");
assert_eq!(q.base_q_idx, 100);
assert_eq!(q.delta_q_y_dc, 0);
assert!(q.diff_uv_delta);
assert_eq!(q.delta_q_u_dc, 1);
assert_eq!(q.delta_q_u_ac, 0);
assert_eq!(q.delta_q_v_dc, -1);
assert_eq!(q.delta_q_v_ac, 0);
assert!(q.using_qmatrix);
assert_eq!(q.qm_y, 3);
assert_eq!(q.qm_u, 4);
assert_eq!(q.qm_v, 5);
assert_eq!(n, bits.len());
}
#[test]
fn quantization_using_qmatrix_without_separate_uv() {
let mut bits = vec![0u8; 8]; bits.push(0); bits.push(0); bits.push(0); bits.push(1); for i in (0..4).rev() {
bits.push(((7u32 >> i) & 1) as u8); }
for i in (0..4).rev() {
bits.push(((9u32 >> i) & 1) as u8); }
let mut payload = vec![0u8; bits.len().div_ceil(8)];
for (i, &b) in bits.iter().enumerate() {
if b != 0 {
payload[i / 8] |= 1 << (7 - (i % 8));
}
}
let (q, _) = parse_quantization_params(&payload, 3, false).expect("decodes");
assert!(q.using_qmatrix);
assert_eq!(q.qm_y, 7);
assert_eq!(q.qm_u, 9);
assert_eq!(q.qm_v, 9, "qm_v mirrors qm_u when !separate_uv_delta_q");
}
#[test]
fn quantization_unexpected_end() {
let payload: [u8; 0] = [];
let err = parse_quantization_params(&payload, 1, false).expect_err("must err");
assert_eq!(err, Error::UnexpectedEnd);
}
use crate::frame_header::PRIMARY_REF_NONE;
#[test]
fn segmentation_disabled_one_bit() {
let payload = [0b0000_0000u8];
let (s, n) = parse_segmentation_params(&payload, PRIMARY_REF_NONE).expect("decodes");
assert!(!s.enabled);
assert!(!s.update_map);
assert!(!s.temporal_update);
assert!(!s.update_data);
assert_eq!(
s.segment_feature_active,
[[false; SEG_LVL_MAX]; MAX_SEGMENTS]
);
assert_eq!(s.segment_feature_data, [[0i16; SEG_LVL_MAX]; MAX_SEGMENTS]);
assert!(!s.seg_id_pre_skip);
assert_eq!(s.last_active_seg_id, 0);
assert_eq!(n, 1);
}
#[test]
fn segmentation_primary_ref_none_no_active_features() {
let mut bits = vec![1u8]; bits.resize(1 + MAX_SEGMENTS * SEG_LVL_MAX, 0);
let mut payload = vec![0u8; bits.len().div_ceil(8)];
for (i, &b) in bits.iter().enumerate() {
if b != 0 {
payload[i / 8] |= 1 << (7 - (i % 8));
}
}
let (s, n) = parse_segmentation_params(&payload, PRIMARY_REF_NONE).expect("decodes");
assert!(s.enabled);
assert!(s.update_map);
assert!(!s.temporal_update);
assert!(s.update_data);
for i in 0..MAX_SEGMENTS {
for j in 0..SEG_LVL_MAX {
assert!(!s.segment_feature_active[i][j]);
assert_eq!(s.segment_feature_data[i][j], 0);
}
}
assert!(!s.seg_id_pre_skip);
assert_eq!(s.last_active_seg_id, 0);
assert_eq!(n, bits.len());
}
#[test]
fn segmentation_with_primary_ref_reads_three_update_bits() {
let payload = [0b1110_0000u8];
let (s, n) = parse_segmentation_params(&payload, 0).expect("decodes");
assert!(s.enabled);
assert!(s.update_map);
assert!(s.temporal_update);
assert!(!s.update_data);
assert_eq!(n, 4);
}
#[test]
fn segmentation_update_map_zero_skips_temporal_bit() {
let payload = [0b1000_0000u8];
let (s, n) = parse_segmentation_params(&payload, 0).expect("decodes");
assert!(s.enabled);
assert!(!s.update_map);
assert!(!s.temporal_update);
assert!(!s.update_data);
assert_eq!(n, 3);
}
#[test]
fn segmentation_feature_alt_q_signed() {
let mut bits = vec![1u8]; bits.push(1); for i in (0..9).rev() {
bits.push(((462u32 >> i) & 1) as u8);
}
let tail_zeros = (SEG_LVL_MAX - 1) + (MAX_SEGMENTS - 1) * SEG_LVL_MAX;
bits.resize(bits.len() + tail_zeros, 0);
let mut payload = vec![0u8; bits.len().div_ceil(8)];
for (i, &b) in bits.iter().enumerate() {
if b != 0 {
payload[i / 8] |= 1 << (7 - (i % 8));
}
}
let (s, n) = parse_segmentation_params(&payload, PRIMARY_REF_NONE).expect("decodes");
assert!(s.enabled);
assert!(s.segment_feature_active[0][SEG_LVL_ALT_Q]);
assert_eq!(s.segment_feature_data[0][SEG_LVL_ALT_Q], -50);
for i in 0..MAX_SEGMENTS {
for j in 0..SEG_LVL_MAX {
if (i, j) != (0, SEG_LVL_ALT_Q) {
assert!(!s.segment_feature_active[i][j]);
}
}
}
assert!(!s.seg_id_pre_skip);
assert_eq!(s.last_active_seg_id, 0);
assert_eq!(n, 74);
}
#[test]
fn segmentation_feature_alt_q_clipped_high() {
let mut bits = vec![1u8]; bits.push(1); for i in (0..9).rev() {
bits.push(((0x100u32 >> i) & 1) as u8);
}
let tail_zeros = (SEG_LVL_MAX - 1) + (MAX_SEGMENTS - 1) * SEG_LVL_MAX;
bits.resize(bits.len() + tail_zeros, 0);
let mut payload = vec![0u8; bits.len().div_ceil(8)];
for (i, &b) in bits.iter().enumerate() {
if b != 0 {
payload[i / 8] |= 1 << (7 - (i % 8));
}
}
let (s, _) = parse_segmentation_params(&payload, PRIMARY_REF_NONE).expect("decodes");
assert_eq!(s.segment_feature_data[0][SEG_LVL_ALT_Q], -255);
}
#[test]
fn segmentation_feature_ref_frame_unsigned() {
let mut bits = vec![1u8]; bits.resize(bits.len() + SEG_LVL_REF_FRAME, 0);
bits.push(1);
for i in (0..3).rev() {
bits.push(((6u32 >> i) & 1) as u8);
}
let tail_zeros = 2 + (MAX_SEGMENTS - 1) * SEG_LVL_MAX;
bits.resize(bits.len() + tail_zeros, 0);
let mut payload = vec![0u8; bits.len().div_ceil(8)];
for (i, &b) in bits.iter().enumerate() {
if b != 0 {
payload[i / 8] |= 1 << (7 - (i % 8));
}
}
let (s, _) = parse_segmentation_params(&payload, PRIMARY_REF_NONE).expect("decodes");
assert!(s.segment_feature_active[0][SEG_LVL_REF_FRAME]);
assert_eq!(s.segment_feature_data[0][SEG_LVL_REF_FRAME], 6);
assert!(s.seg_id_pre_skip);
assert_eq!(s.last_active_seg_id, 0);
}
#[test]
fn segmentation_feature_skip_no_value_bits() {
let mut bits = vec![1u8]; bits.resize(bits.len() + 3 * SEG_LVL_MAX + SEG_LVL_SKIP, 0);
bits.push(1); bits.push(0); bits.resize(bits.len() + (MAX_SEGMENTS - 4) * SEG_LVL_MAX, 0);
let mut payload = vec![0u8; bits.len().div_ceil(8)];
for (i, &b) in bits.iter().enumerate() {
if b != 0 {
payload[i / 8] |= 1 << (7 - (i % 8));
}
}
let (s, n) = parse_segmentation_params(&payload, PRIMARY_REF_NONE).expect("decodes");
assert!(s.segment_feature_active[3][SEG_LVL_SKIP]);
assert_eq!(s.segment_feature_data[3][SEG_LVL_SKIP], 0);
assert!(s.seg_id_pre_skip);
assert_eq!(s.last_active_seg_id, 3);
assert_eq!(n, 65);
}
#[test]
fn segmentation_unexpected_end() {
let payload: [u8; 0] = [];
let err = parse_segmentation_params(&payload, PRIMARY_REF_NONE).expect_err("must err");
assert_eq!(err, Error::UnexpectedEnd);
}
#[test]
fn delta_q_base_q_idx_zero_reads_nothing() {
let payload: [u8; 0] = [];
let (d, n) = parse_delta_q_params(&payload, 0).expect("decodes");
assert!(!d.delta_q_present);
assert_eq!(d.delta_q_res, 0);
assert_eq!(n, 0);
}
#[test]
fn delta_q_present_zero_reads_one_bit() {
let payload = [0b0000_0000u8];
let (d, n) = parse_delta_q_params(&payload, 120).expect("decodes");
assert!(!d.delta_q_present);
assert_eq!(d.delta_q_res, 0);
assert_eq!(n, 1);
}
#[test]
fn delta_q_present_one_reads_res() {
let payload = [0b1100_0000u8];
let (d, n) = parse_delta_q_params(&payload, 120).expect("decodes");
assert!(d.delta_q_present);
assert_eq!(d.delta_q_res, 2);
assert_eq!(n, 3);
}
#[test]
fn delta_q_present_bit_unexpected_end() {
let payload: [u8; 0] = [];
let err = parse_delta_q_params(&payload, 120).expect_err("must err");
assert_eq!(err, Error::UnexpectedEnd);
}
#[test]
fn delta_lf_gated_off_when_delta_q_absent() {
let payload: [u8; 0] = [];
let (d, n) = parse_delta_lf_params(&payload, false, false).expect("decodes");
assert!(!d.delta_lf_present);
assert_eq!(d.delta_lf_res, 0);
assert!(!d.delta_lf_multi);
assert_eq!(n, 0);
}
#[test]
fn delta_lf_present_zero_reads_one_bit() {
let payload = [0b0000_0000u8];
let (d, n) = parse_delta_lf_params(&payload, true, false).expect("decodes");
assert!(!d.delta_lf_present);
assert_eq!(d.delta_lf_res, 0);
assert!(!d.delta_lf_multi);
assert_eq!(n, 1);
}
#[test]
fn delta_lf_present_full_path() {
let payload = [0b1111_0000u8];
let (d, n) = parse_delta_lf_params(&payload, true, false).expect("decodes");
assert!(d.delta_lf_present);
assert_eq!(d.delta_lf_res, 3);
assert!(d.delta_lf_multi);
assert_eq!(n, 4);
}
#[test]
fn delta_lf_suppressed_by_allow_intrabc() {
let payload: [u8; 0] = [];
let (d, n) = parse_delta_lf_params(&payload, true, true).expect("decodes");
assert!(!d.delta_lf_present);
assert_eq!(d.delta_lf_res, 0);
assert!(!d.delta_lf_multi);
assert_eq!(n, 0);
}
#[test]
fn delta_lf_present_bit_unexpected_end() {
let payload: [u8; 0] = [];
let err = parse_delta_lf_params(&payload, true, false).expect_err("must err");
assert_eq!(err, Error::UnexpectedEnd);
}
fn pack_bits(bits: &[u8]) -> Vec<u8> {
let mut payload = vec![0u8; bits.len().div_ceil(8)];
for (i, &b) in bits.iter().enumerate() {
if b != 0 {
payload[i / 8] |= 1 << (7 - (i % 8));
}
}
payload
}
#[test]
fn cdef_short_circuit_coded_lossless() {
let payload: [u8; 0] = [];
let (c, n) = parse_cdef_params(&payload, 3, true, false, true).expect("decodes");
assert!(c.short_circuited);
assert_eq!(c.cdef_damping, 3);
assert_eq!(c.cdef_bits, 0);
assert_eq!(c.cdef_y_pri_strength, [0; CDEF_MAX_STRENGTHS]);
assert_eq!(c.cdef_y_sec_strength, [0; CDEF_MAX_STRENGTHS]);
assert_eq!(n, 0);
}
#[test]
fn cdef_short_circuit_allow_intrabc() {
let payload: [u8; 0] = [];
let (c, n) = parse_cdef_params(&payload, 3, false, true, true).expect("decodes");
assert!(c.short_circuited);
assert_eq!(n, 0);
}
#[test]
fn cdef_short_circuit_enable_cdef_off() {
let payload: [u8; 0] = [];
let (c, n) = parse_cdef_params(&payload, 3, false, false, false).expect("decodes");
assert!(c.short_circuited);
assert_eq!(c.cdef_damping, 3);
assert_eq!(n, 0);
}
#[test]
fn cdef_full_path_single_entry_3plane() {
let bits: &[u8] = &[
1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, ];
let payload = pack_bits(bits);
let (c, n) = parse_cdef_params(&payload, 3, false, false, true).expect("decodes");
assert!(!c.short_circuited);
assert_eq!(c.cdef_damping, 5);
assert_eq!(c.cdef_bits, 0);
assert_eq!(c.cdef_y_pri_strength[0], 9);
assert_eq!(c.cdef_y_sec_strength[0], 1);
assert_eq!(c.cdef_uv_pri_strength[0], 6);
assert_eq!(c.cdef_uv_sec_strength[0], 2);
assert_eq!(c.cdef_y_pri_strength[1], 0);
assert_eq!(n, 16);
}
#[test]
fn cdef_sec_strength_three_becomes_four() {
let bits: &[u8] = &[
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, ];
let payload = pack_bits(bits);
let (c, n) = parse_cdef_params(&payload, 3, false, false, true).expect("decodes");
assert_eq!(c.cdef_y_sec_strength[0], 4);
assert_eq!(c.cdef_uv_sec_strength[0], 4);
assert_eq!(c.cdef_damping, 3);
assert_eq!(n, 16);
}
#[test]
fn cdef_full_path_mono_skips_chroma() {
let bits: &[u8] = &[
0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, ];
let payload = pack_bits(bits);
let (c, n) = parse_cdef_params(&payload, 1, false, false, true).expect("decodes");
assert!(!c.short_circuited);
assert_eq!(c.cdef_damping, 4);
assert_eq!(c.cdef_bits, 1);
assert_eq!(c.cdef_y_pri_strength[0], 15);
assert_eq!(c.cdef_y_sec_strength[0], 2);
assert_eq!(c.cdef_y_pri_strength[1], 8);
assert_eq!(c.cdef_y_sec_strength[1], 0);
assert_eq!(c.cdef_uv_pri_strength, [0; CDEF_MAX_STRENGTHS]);
assert_eq!(c.cdef_uv_sec_strength, [0; CDEF_MAX_STRENGTHS]);
assert_eq!(n, 16);
}
#[test]
fn cdef_full_path_eight_entries() {
let mut bits: Vec<u8> = Vec::new();
bits.extend_from_slice(&[0, 0]); bits.extend_from_slice(&[1, 1]); for i in 0..8u8 {
for b in (0..4).rev() {
bits.push((i >> b) & 1);
}
bits.push(0);
bits.push(1);
}
let payload = pack_bits(&bits);
let (c, n) = parse_cdef_params(&payload, 1, false, false, true).expect("decodes");
assert_eq!(c.cdef_bits, 3);
for i in 0..8usize {
assert_eq!(c.cdef_y_pri_strength[i], i as u8);
assert_eq!(c.cdef_y_sec_strength[i], 1);
}
assert_eq!(n, 52);
}
#[test]
fn cdef_full_path_unexpected_end() {
let payload: [u8; 0] = [];
let err = parse_cdef_params(&payload, 3, false, false, true).expect_err("must err");
assert_eq!(err, Error::UnexpectedEnd);
}
#[test]
fn lr_remap_table_matches_spec() {
assert_eq!(FrameRestorationType::remap(0), FrameRestorationType::None);
assert_eq!(
FrameRestorationType::remap(1),
FrameRestorationType::Switchable
);
assert_eq!(FrameRestorationType::remap(2), FrameRestorationType::Wiener);
assert_eq!(
FrameRestorationType::remap(3),
FrameRestorationType::SgrProj
);
assert_eq!(FrameRestorationType::None.as_u8(), 0);
assert_eq!(FrameRestorationType::Wiener.as_u8(), 1);
assert_eq!(FrameRestorationType::SgrProj.as_u8(), 2);
assert_eq!(FrameRestorationType::Switchable.as_u8(), 3);
}
#[test]
fn lr_short_circuit_all_lossless() {
let payload: [u8; 0] = [];
let (lr, n) =
parse_lr_params(&payload, 3, true, true, false, true, false, true).expect("decodes");
assert!(lr.short_circuited);
assert!(!lr.uses_lr);
assert!(!lr.uses_chroma_lr);
assert_eq!(lr.frame_restoration_type, [FrameRestorationType::None; 3]);
assert_eq!(lr.lr_unit_shift, 0);
assert_eq!(lr.lr_uv_shift, 0);
assert_eq!(lr.loop_restoration_size, [0; 3]);
assert_eq!(n, 0);
}
#[test]
fn lr_short_circuit_allow_intrabc() {
let payload: [u8; 0] = [];
let (lr, n) =
parse_lr_params(&payload, 3, true, true, false, false, true, true).expect("decodes");
assert!(lr.short_circuited);
assert_eq!(n, 0);
}
#[test]
fn lr_short_circuit_enable_restoration_off() {
let payload: [u8; 0] = [];
let (lr, n) =
parse_lr_params(&payload, 3, true, true, false, false, false, false).expect("decodes");
assert!(lr.short_circuited);
assert!(!lr.uses_lr);
assert_eq!(n, 0);
}
#[test]
fn lr_uses_lr_zero_reads_only_three_types() {
let payload = pack_bits(&[
0, 0, 0, 0, 0, 0, ]);
let (lr, n) =
parse_lr_params(&payload, 3, true, true, false, false, false, true).expect("decodes");
assert!(!lr.short_circuited);
assert!(!lr.uses_lr);
assert!(!lr.uses_chroma_lr);
assert_eq!(lr.frame_restoration_type, [FrameRestorationType::None; 3]);
assert_eq!(lr.lr_unit_shift, 0);
assert_eq!(lr.loop_restoration_size, [0; 3]);
assert_eq!(n, 6);
}
#[test]
fn lr_luma_only_non128_shift_zero() {
let payload = pack_bits(&[
1, 0, 0, 0, 0, 0, 0, ]);
let (lr, n) =
parse_lr_params(&payload, 3, true, true, false, false, false, true).expect("decodes");
assert!(lr.uses_lr);
assert!(!lr.uses_chroma_lr);
assert_eq!(lr.frame_restoration_type[0], FrameRestorationType::Wiener);
assert_eq!(lr.frame_restoration_type[1], FrameRestorationType::None);
assert_eq!(lr.frame_restoration_type[2], FrameRestorationType::None);
assert_eq!(lr.lr_unit_shift, 0);
assert_eq!(lr.lr_uv_shift, 0);
assert_eq!(lr.loop_restoration_size[0], 64);
assert_eq!(lr.loop_restoration_size[1], 64);
assert_eq!(lr.loop_restoration_size[2], 64);
assert_eq!(n, 7);
}
#[test]
fn lr_non128_unit_shift_one_plus_extra() {
let payload = pack_bits(&[
1, 1, 0, 0, 0, 0, 1, 1, ]);
let (lr, _n) =
parse_lr_params(&payload, 3, true, true, false, false, false, true).expect("decodes");
assert!(lr.uses_lr);
assert_eq!(lr.frame_restoration_type[0], FrameRestorationType::SgrProj);
assert_eq!(lr.lr_unit_shift, 2);
assert_eq!(lr.loop_restoration_size[0], 256);
assert_eq!(lr.loop_restoration_size[1], 256);
}
#[test]
fn lr_non128_unit_shift_one_extra_zero() {
let payload = pack_bits(&[
1, 0, 0, 0, 0, 0, 1, 0, ]);
let (lr, _n) =
parse_lr_params(&payload, 3, true, true, false, false, false, true).expect("decodes");
assert_eq!(lr.lr_unit_shift, 1);
assert_eq!(lr.loop_restoration_size[0], 128);
}
#[test]
fn lr_128_superblock_post_increment() {
let payload = pack_bits(&[
0, 1, 0, 0, 0, 0, 1, ]);
let (lr, _n) =
parse_lr_params(&payload, 3, true, true, true, false, false, true).expect("decodes");
assert_eq!(
lr.frame_restoration_type[0],
FrameRestorationType::Switchable
);
assert!(lr.uses_lr);
assert!(!lr.uses_chroma_lr);
assert_eq!(lr.lr_unit_shift, 2);
assert_eq!(lr.loop_restoration_size[0], 256);
}
#[test]
fn lr_128_superblock_bit_zero_gives_shift_one() {
let payload = pack_bits(&[
1, 0, 0, 0, 0, 0, 0, ]);
let (lr, _n) =
parse_lr_params(&payload, 3, true, true, true, false, false, true).expect("decodes");
assert_eq!(lr.lr_unit_shift, 1);
assert_eq!(lr.loop_restoration_size[0], 128);
}
#[test]
fn lr_chroma_lr_420_reads_uv_shift() {
let payload = pack_bits(&[
0, 0, 0, 0, 1, 0, 1, 0, ]);
let (lr, _n) =
parse_lr_params(&payload, 3, true, true, true, false, false, true).expect("decodes");
assert!(lr.uses_lr);
assert!(lr.uses_chroma_lr);
assert_eq!(lr.frame_restoration_type[2], FrameRestorationType::Wiener);
assert_eq!(lr.lr_unit_shift, 2);
assert_eq!(lr.lr_uv_shift, 0);
assert_eq!(lr.loop_restoration_size[0], 256);
assert_eq!(lr.loop_restoration_size[1], 256);
assert_eq!(lr.loop_restoration_size[2], 256);
}
#[test]
fn lr_chroma_lr_420_uv_shift_one_halves_chroma() {
let payload = pack_bits(&[
0, 0, 1, 0, 0, 0, 1, 1, ]);
let (lr, _n) =
parse_lr_params(&payload, 3, true, true, true, false, false, true).expect("decodes");
assert!(lr.uses_chroma_lr);
assert_eq!(lr.lr_uv_shift, 1);
assert_eq!(lr.loop_restoration_size[0], 256);
assert_eq!(lr.loop_restoration_size[1], 128);
assert_eq!(lr.loop_restoration_size[2], 128);
}
#[test]
fn lr_chroma_lr_non420_skips_uv_shift() {
let payload = pack_bits(&[
0, 0, 1, 0, 0, 0, 1, ]);
let (lr, n) =
parse_lr_params(&payload, 3, false, false, true, false, false, true).expect("decodes");
assert!(lr.uses_chroma_lr);
assert_eq!(lr.lr_uv_shift, 0);
assert_eq!(n, 7);
assert_eq!(lr.loop_restoration_size[1], lr.loop_restoration_size[0]);
}
#[test]
fn lr_chroma_lr_422_skips_uv_shift() {
let payload = pack_bits(&[
0, 0, 0, 0, 1, 0, 1, ]);
let (lr, _n) =
parse_lr_params(&payload, 3, true, false, true, false, false, true).expect("decodes");
assert!(lr.uses_chroma_lr);
assert_eq!(lr.lr_uv_shift, 0);
}
#[test]
fn lr_monochrome_reads_one_type_only() {
let payload = pack_bits(&[
1, 0, 0, ]);
let (lr, n) =
parse_lr_params(&payload, 1, true, true, false, false, false, true).expect("decodes");
assert!(lr.uses_lr);
assert!(!lr.uses_chroma_lr);
assert_eq!(lr.frame_restoration_type[0], FrameRestorationType::Wiener);
assert_eq!(lr.frame_restoration_type[1], FrameRestorationType::None);
assert_eq!(lr.frame_restoration_type[2], FrameRestorationType::None);
assert_eq!(lr.lr_uv_shift, 0);
assert_eq!(n, 3);
}
#[test]
fn lr_all_planes_distinct_types() {
let payload = pack_bits(&[
0, 1, 1, 0, 1, 1, 0, 0, ]);
let (lr, _n) =
parse_lr_params(&payload, 3, true, true, false, false, false, true).expect("decodes");
assert_eq!(
lr.frame_restoration_type[0],
FrameRestorationType::Switchable
);
assert_eq!(lr.frame_restoration_type[1], FrameRestorationType::Wiener);
assert_eq!(lr.frame_restoration_type[2], FrameRestorationType::SgrProj);
assert!(lr.uses_lr);
assert!(lr.uses_chroma_lr);
assert_eq!(lr.lr_unit_shift, 0);
assert_eq!(lr.loop_restoration_size[0], 64);
}
#[test]
fn lr_full_path_unexpected_end() {
let payload: [u8; 0] = [];
let err = parse_lr_params(&payload, 3, true, true, false, false, false, true)
.expect_err("must err");
assert_eq!(err, Error::UnexpectedEnd);
}
#[test]
fn lr_unexpected_end_at_unit_shift() {
let payload = pack_bits(&[
1, 0, 0, 0, 1, 0, 1, 1, ]);
let err = parse_lr_params(&payload, 3, true, true, false, false, false, true)
.expect_err("must err");
assert_eq!(err, Error::UnexpectedEnd);
}
#[test]
fn lr_unexpected_end_at_first_type() {
let empty: [u8; 0] = [];
let err = parse_lr_params(&empty, 1, false, false, false, false, false, true)
.expect_err("must err");
assert_eq!(err, Error::UnexpectedEnd);
}
#[test]
fn tx_mode_symbol_values() {
assert_eq!(TxMode::Only4x4.as_u8(), 0);
assert_eq!(TxMode::TxModeLargest.as_u8(), 1);
assert_eq!(TxMode::TxModeSelect.as_u8(), 2);
assert_eq!(TX_MODES, 3);
}
#[test]
fn tx_mode_coded_lossless_only_4x4_no_bits() {
let payload: [u8; 0] = [];
let (tx, n) = parse_tx_mode(&payload, true).expect("decodes");
assert_eq!(tx, TxMode::Only4x4);
assert_eq!(n, 0);
}
#[test]
fn tx_mode_select_set_reads_one_bit() {
let payload = pack_bits(&[1]);
let (tx, n) = parse_tx_mode(&payload, false).expect("decodes");
assert_eq!(tx, TxMode::TxModeSelect);
assert_eq!(n, 1);
}
#[test]
fn tx_mode_select_clear_is_largest() {
let payload = pack_bits(&[0]);
let (tx, n) = parse_tx_mode(&payload, false).expect("decodes");
assert_eq!(tx, TxMode::TxModeLargest);
assert_eq!(n, 1);
}
#[test]
fn tx_mode_coded_lossless_ignores_bitstream() {
let payload = pack_bits(&[1]);
let (tx, n) = parse_tx_mode(&payload, true).expect("decodes");
assert_eq!(tx, TxMode::Only4x4);
assert_eq!(n, 0);
}
#[test]
fn tx_mode_unexpected_end() {
let empty: [u8; 0] = [];
let err = parse_tx_mode(&empty, false).expect_err("must err");
assert_eq!(err, Error::UnexpectedEnd);
}
fn pack_fields(fields: &[(u32, u32)]) -> Vec<u8> {
let mut bits: Vec<u8> = Vec::new();
for &(value, width) in fields {
for i in (0..width).rev() {
bits.push(((value >> i) & 1) as u8);
}
}
pack_bits(&bits)
}
#[test]
fn warp_model_type_symbol_values() {
assert_eq!(WarpModelType::Identity.as_u8(), 0);
assert_eq!(WarpModelType::Translation.as_u8(), 1);
assert_eq!(WarpModelType::RotZoom.as_u8(), 2);
assert_eq!(WarpModelType::Affine.as_u8(), 3);
}
#[test]
fn global_motion_identity_default_values() {
let one = 1i32 << WARPEDMODEL_PREC_BITS;
let gm = GlobalMotionParams::identity();
assert!(gm.short_circuited);
for r in 0..TOTAL_REFS_PER_FRAME {
assert_eq!(gm.gm_type[r], WarpModelType::Identity);
assert_eq!(gm.gm_params[r], [0, 0, one, 0, 0, one]);
}
assert_eq!(REFS_PER_FRAME, 7);
assert_eq!(LAST_FRAME, 1);
assert_eq!(ALTREF_FRAME, 7);
assert_eq!(INTRA_FRAME, 0);
}
#[test]
fn global_motion_intra_short_circuit_no_bits() {
let payload: [u8; 0] = [];
let (gm, n) = parse_global_motion_params(&payload, true, false, &prev_gm_params_default())
.expect("decodes");
assert_eq!(n, 0);
assert_eq!(gm, GlobalMotionParams::identity());
}
#[test]
fn global_motion_inter_all_identity_seven_bits() {
let payload = pack_bits(&[0, 0, 0, 0, 0, 0, 0]);
let (gm, n) = parse_global_motion_params(&payload, false, false, &prev_gm_params_default())
.expect("decodes");
assert_eq!(n, 7, "7 `is_global` bits");
assert!(!gm.short_circuited);
let identity_params = prev_gm_params_default();
for (r, want) in identity_params
.iter()
.enumerate()
.take(ALTREF_FRAME + 1)
.skip(LAST_FRAME)
{
assert_eq!(gm.gm_type[r], WarpModelType::Identity);
assert_eq!(gm.gm_params[r], *want);
}
}
#[test]
fn global_motion_inter_single_translation_zero_value() {
let payload = pack_fields(&[
(1, 1), (0, 1), (1, 1), (0, 1), (0, 3), (0, 1), (0, 3), (0, 1), (0, 1), (0, 1), (0, 1), (0, 1), (0, 1), ]);
let (gm, n) = parse_global_motion_params(&payload, false, false, &prev_gm_params_default())
.expect("decodes");
assert_eq!(n, 17);
assert!(!gm.short_circuited);
assert_eq!(gm.gm_type[LAST_FRAME], WarpModelType::Translation);
let one = 1i32 << WARPEDMODEL_PREC_BITS;
assert_eq!(gm.gm_params[LAST_FRAME], [0, 0, one, 0, 0, one]);
for r in (LAST_FRAME + 1)..=ALTREF_FRAME {
assert_eq!(gm.gm_type[r], WarpModelType::Identity);
}
}
#[test]
fn global_motion_inter_unexpected_end() {
let empty: [u8; 0] = [];
let err = parse_global_motion_params(&empty, false, false, &prev_gm_params_default())
.expect_err("must err");
assert_eq!(err, Error::UnexpectedEnd);
}
fn fg_ctx_present_shown() -> FilmGrainContext {
FilmGrainContext {
film_grain_params_present: true,
show_frame: true,
showable_frame: false,
is_inter_frame: false,
mono_chrome: false,
subsampling_x: true,
subsampling_y: true,
}
}
#[test]
fn film_grain_not_present_resets_no_bits() {
let ctx = FilmGrainContext {
film_grain_params_present: false,
..fg_ctx_present_shown()
};
let payload = pack_bits(&[1, 1, 1, 1, 1, 1, 1, 1]);
let (fg, n) = parse_film_grain_params(&payload, ctx).expect("decodes");
assert_eq!(n, 0);
assert_eq!(fg, FilmGrainParams::reset());
}
#[test]
fn film_grain_hidden_frame_resets_no_bits() {
let ctx = FilmGrainContext {
show_frame: false,
showable_frame: false,
..fg_ctx_present_shown()
};
let payload = pack_bits(&[1, 1, 1, 1]);
let (fg, n) = parse_film_grain_params(&payload, ctx).expect("decodes");
assert_eq!(n, 0);
assert_eq!(fg, FilmGrainParams::reset());
}
#[test]
fn film_grain_apply_grain_zero_one_bit() {
let ctx = fg_ctx_present_shown();
let payload = pack_bits(&[0]);
let (fg, n) = parse_film_grain_params(&payload, ctx).expect("decodes");
assert_eq!(n, 1);
assert!(!fg.apply_grain);
assert_eq!(fg, FilmGrainParams::reset());
}
#[test]
fn film_grain_predicted_path_inter_frame() {
let ctx = FilmGrainContext {
is_inter_frame: true,
..fg_ctx_present_shown()
};
let payload = pack_fields(&[
(1, 1), (0xBEEF, 16), (0, 1), (5, 3), ]);
let (fg, n) = parse_film_grain_params(&payload, ctx).expect("decodes");
assert_eq!(n, 21);
assert!(fg.apply_grain);
assert!(!fg.update_grain);
assert!(fg.predicted);
assert_eq!(fg.grain_seed, 0xBEEF);
assert_eq!(fg.film_grain_params_ref_idx, 5);
assert_eq!(fg.num_y_points, 0);
}
#[test]
fn film_grain_chroma_suppressed_420_no_y_points() {
let ctx = fg_ctx_present_shown(); let payload = pack_fields(&[
(1, 1), (0, 16), (0, 4), (0, 1), (1, 2), (0, 2), (2, 2), (0, 2), (1, 1), (0, 1), ]);
let (fg, n) = parse_film_grain_params(&payload, ctx).expect("decodes");
assert_eq!(n, 32);
assert!(fg.apply_grain);
assert!(fg.update_grain);
assert!(!fg.predicted);
assert_eq!(fg.num_y_points, 0);
assert!(!fg.chroma_scaling_from_luma);
assert_eq!(fg.num_cb_points, 0);
assert_eq!(fg.num_cr_points, 0);
assert_eq!(fg.grain_scaling, 9);
assert_eq!(fg.ar_coeff_lag, 0);
assert_eq!(fg.ar_coeff_shift, 8);
assert_eq!(fg.grain_scale_shift, 0);
assert!(fg.overlap_flag);
assert!(!fg.clip_to_restricted_range);
}
#[test]
fn film_grain_full_luma_and_chroma_points() {
let ctx = fg_ctx_present_shown();
let mut fields: Vec<(u32, u32)> = vec![
(1, 1), (0xABCD, 16), (1, 4), (10, 8), (20, 8), (0, 1), (1, 4), (30, 8), (40, 8), (1, 4), (50, 8), (60, 8), (3, 2), (1, 2), ];
for v in [100u32, 101, 102, 103] {
fields.push((v, 8));
}
for v in [110u32, 111, 112, 113, 114] {
fields.push((v, 8));
}
for v in [120u32, 121, 122, 123, 124] {
fields.push((v, 8));
}
fields.push((1, 2)); fields.push((2, 2)); fields.push((128, 8)); fields.push((192, 8)); fields.push((256, 9)); fields.push((130, 8)); fields.push((190, 8)); fields.push((300, 9)); fields.push((0, 1)); fields.push((1, 1)); let payload = pack_fields(&fields);
let (fg, _n) = parse_film_grain_params(&payload, ctx).expect("decodes");
assert!(fg.apply_grain);
assert!(fg.update_grain);
assert_eq!(fg.grain_seed, 0xABCD);
assert_eq!(fg.num_y_points, 1);
assert_eq!(fg.point_y_value[0], 10);
assert_eq!(fg.point_y_scaling[0], 20);
assert!(!fg.chroma_scaling_from_luma);
assert_eq!(fg.num_cb_points, 1);
assert_eq!(fg.point_cb_value[0], 30);
assert_eq!(fg.point_cb_scaling[0], 40);
assert_eq!(fg.num_cr_points, 1);
assert_eq!(fg.point_cr_value[0], 50);
assert_eq!(fg.point_cr_scaling[0], 60);
assert_eq!(fg.grain_scaling, 11);
assert_eq!(fg.ar_coeff_lag, 1);
assert_eq!(&fg.ar_coeffs_y_plus_128[0..4], &[100, 101, 102, 103]);
assert_eq!(&fg.ar_coeffs_cb_plus_128[0..5], &[110, 111, 112, 113, 114]);
assert_eq!(&fg.ar_coeffs_cr_plus_128[0..5], &[120, 121, 122, 123, 124]);
assert_eq!(fg.ar_coeff_shift, 7);
assert_eq!(fg.grain_scale_shift, 2);
assert_eq!(fg.cb_mult, 128);
assert_eq!(fg.cb_luma_mult, 192);
assert_eq!(fg.cb_offset, 256);
assert_eq!(fg.cr_mult, 130);
assert_eq!(fg.cr_luma_mult, 190);
assert_eq!(fg.cr_offset, 300);
assert!(!fg.overlap_flag);
assert!(fg.clip_to_restricted_range);
}
#[test]
fn film_grain_unexpected_end() {
let ctx = fg_ctx_present_shown();
let payload = pack_bits(&[1]); let err = parse_film_grain_params(&payload, ctx).expect_err("must err");
assert_eq!(err, Error::UnexpectedEnd);
}
}