Files
deckcraft/crates/vp9/src/header.rs
T

662 lines
22 KiB
Rust

//! Uncompressed header (6.2) and compressed header (6.3) parsing, plus the state that persists
//! between frames (loop filter deltas, segmentation parameters, colour configuration).
use crate::boolcoder::BoolDecoder;
use crate::error::{Error, Result, ensure};
use crate::probs::FrameContext;
use crate::tables::*;
use deckcraft_bitstream::BitReader;
pub const KEY_FRAME: u8 = 0;
/// Loop filter parameters (6.2.8); the deltas persist across frames.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct LoopFilterParams {
pub level: u8,
pub sharpness: u8,
pub delta_enabled: bool,
pub ref_deltas: [i8; 4],
pub mode_deltas: [i8; 2],
}
/// Segmentation parameters (6.2.11); feature data persists across frames.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Segmentation {
pub enabled: bool,
pub update_map: bool,
pub tree_probs: [u8; 7],
pub pred_probs: [u8; 3],
pub temporal_update: bool,
pub abs_or_delta_update: bool,
pub feature_enabled: [[bool; 4]; 8],
pub feature_data: [[i16; 4]; 8],
}
impl Segmentation {
#[inline]
pub fn feature_active(&self, segment_id: u8, feature: usize) -> bool {
self.enabled && self.feature_enabled[segment_id as usize][feature]
}
}
/// Colour configuration (6.2.2); persists for inter frames.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ColorConfig {
pub bit_depth: u8,
pub color_space: u8,
pub color_range: bool,
pub subsampling_x: bool,
pub subsampling_y: bool,
}
impl Default for ColorConfig {
fn default() -> Self {
ColorConfig { bit_depth: 8, color_space: 0, color_range: false, subsampling_x: true, subsampling_y: true }
}
}
/// Everything read from the headers of one frame.
#[derive(Clone, Debug, Default)]
pub struct FrameHeader {
pub profile: u8,
pub show_existing_frame: bool,
pub frame_to_show_map_idx: u8,
pub frame_type: u8,
pub show_frame: bool,
pub error_resilient_mode: bool,
pub intra_only: bool,
pub reset_frame_context: u8,
pub color: ColorConfig,
pub refresh_frame_flags: u8,
pub ref_frame_idx: [u8; 3],
/// Indexed by reference frame (LAST_FRAME..ALTREF_FRAME); index 0 unused.
pub ref_frame_sign_bias: [bool; 4],
pub width: u32,
pub height: u32,
pub render_width: u32,
pub render_height: u32,
pub allow_high_precision_mv: bool,
pub interp_filter: u8,
pub refresh_frame_context: bool,
pub frame_parallel_decoding_mode: bool,
pub frame_context_idx: u8,
pub base_q_idx: u8,
pub delta_q_y_dc: i8,
pub delta_q_uv_dc: i8,
pub delta_q_uv_ac: i8,
pub lossless: bool,
pub tile_cols_log2: u32,
pub tile_rows_log2: u32,
pub header_size_in_bytes: u32,
/// Size of the uncompressed header in bytes.
pub uncompressed_size: usize,
// Compressed header.
pub tx_mode: u8,
pub reference_mode: u8,
pub comp_fixed_ref: i8,
pub comp_var_ref: [i8; 2],
// Derived.
pub mi_cols: u32,
pub mi_rows: u32,
pub sb64_cols: u32,
pub sb64_rows: u32,
pub frame_is_intra: bool,
/// The frame size was taken from reference `found_ref` (for statistics).
pub size_from_ref: bool,
}
/// Reference slot information needed while parsing (frame_size_with_refs).
#[derive(Clone, Copy, Debug)]
pub struct RefInfo {
pub width: u32,
pub height: u32,
}
/// State carried between frames that header parsing reads and updates.
pub struct HeaderState {
pub contexts: [FrameContext; 4],
pub lf: LoopFilterParams,
pub seg: Segmentation,
pub color: ColorConfig,
pub last_frame_type: u8,
pub frame_type: u8,
/// Set by setup_past_independence; cleared once consumed by the decoder (PrevSegmentIds reset).
pub reset_segment_map: bool,
}
impl Default for HeaderState {
fn default() -> Self {
let fc = FrameContext::default();
HeaderState {
contexts: [fc.clone(), fc.clone(), fc.clone(), fc],
lf: LoopFilterParams { ref_deltas: [1, 0, -1, -1], delta_enabled: true, ..Default::default() },
seg: Segmentation::default(),
color: ColorConfig::default(),
last_frame_type: KEY_FRAME,
frame_type: KEY_FRAME,
reset_segment_map: false,
}
}
}
fn read_s(r: &mut BitReader, bits: u32) -> Result<i32> {
let v = r.read_bits(bits)? as i32;
Ok(if r.read_flag()? { -v } else { v })
}
fn read_delta_q(r: &mut BitReader) -> Result<i8> {
Ok(if r.read_flag()? { read_s(r, 4)? as i8 } else { 0 })
}
fn read_prob(r: &mut BitReader) -> Result<u8> {
Ok(if r.read_flag()? { r.read_bits(8)? as u8 } else { 255 })
}
fn frame_sync_code(r: &mut BitReader) -> Result<()> {
let (a, b, c) = (r.read_bits(8)?, r.read_bits(8)?, r.read_bits(8)?);
ensure!(a == 0x49 && b == 0x83 && c == 0x42, "invalid frame sync code");
Ok(())
}
fn color_config(r: &mut BitReader, profile: u8) -> Result<ColorConfig> {
let bit_depth = if profile >= 2 { if r.read_flag()? { 12 } else { 10 } } else { 8 };
let color_space = r.read_bits(3)? as u8;
let mut c = ColorConfig { bit_depth, color_space, color_range: true, subsampling_x: true, subsampling_y: true };
if color_space != 7 {
c.color_range = r.read_flag()?;
if profile == 1 || profile == 3 {
c.subsampling_x = r.read_flag()?;
c.subsampling_y = r.read_flag()?;
ensure!(!(c.subsampling_x && c.subsampling_y), "4:2:0 subsampling in profile {profile}");
ensure!(!r.read_flag()?, "reserved bit set in color config");
}
} else {
ensure!(profile == 1 || profile == 3, "RGB color space in profile {profile}");
c.subsampling_x = false;
c.subsampling_y = false;
ensure!(!r.read_flag()?, "reserved bit set in color config");
}
Ok(c)
}
fn frame_size(r: &mut BitReader, h: &mut FrameHeader) -> Result<()> {
h.width = r.read_bits(16)? + 1;
h.height = r.read_bits(16)? + 1;
Ok(())
}
fn render_size(r: &mut BitReader, h: &mut FrameHeader) -> Result<()> {
if r.read_flag()? {
h.render_width = r.read_bits(16)? + 1;
h.render_height = r.read_bits(16)? + 1;
} else {
h.render_width = h.width;
h.render_height = h.height;
}
Ok(())
}
fn compute_image_size(h: &mut FrameHeader) {
h.mi_cols = (h.width + 7) >> 3;
h.mi_rows = (h.height + 7) >> 3;
h.sb64_cols = (h.mi_cols + 7) >> 3;
h.sb64_rows = (h.mi_rows + 7) >> 3;
}
/// setup_past_independence (7.2): resets segmentation features, loop filter deltas and the
/// probabilities of `fc`.
fn setup_past_independence(st: &mut HeaderState, fc: &mut FrameContext) {
st.seg.feature_data = [[0; 4]; 8];
st.seg.feature_enabled = [[false; 4]; 8];
st.seg.abs_or_delta_update = false;
st.lf.delta_enabled = true;
st.lf.ref_deltas = [1, 0, -1, -1];
st.lf.mode_deltas = [0, 0];
st.reset_segment_map = true;
*fc = FrameContext::default();
}
/// Parse the uncompressed header (6.2). `refs` gives the sizes of the eight reference slots.
/// Updates the persistent state (probability contexts on reset, loop filter deltas,
/// segmentation, colour configuration).
pub fn parse_uncompressed(data: &[u8], st: &mut HeaderState, refs: &[Option<RefInfo>; 8]) -> Result<FrameHeader> {
let mut r = BitReader::new(data);
let mut h = FrameHeader::default();
ensure!(r.read_bits(2)? == 2, "invalid frame marker");
let lo = r.read_bits(1)? as u8;
let hi = r.read_bits(1)? as u8;
h.profile = (hi << 1) | lo;
if h.profile == 3 {
ensure!(r.read_bits(1)? == 0, "reserved bit set after profile");
}
h.show_existing_frame = r.read_flag()?;
if h.show_existing_frame {
h.frame_to_show_map_idx = r.read_bits(3)? as u8;
h.show_frame = true;
h.uncompressed_size = r.position().div_ceil(8);
return Ok(h);
}
st.last_frame_type = st.frame_type;
h.frame_type = r.read_bits(1)? as u8;
h.show_frame = r.read_flag()?;
h.error_resilient_mode = r.read_flag()?;
if h.frame_type == KEY_FRAME {
frame_sync_code(&mut r)?;
h.color = color_config(&mut r, h.profile)?;
frame_size(&mut r, &mut h)?;
render_size(&mut r, &mut h)?;
h.refresh_frame_flags = 0xff;
h.frame_is_intra = true;
} else {
h.intra_only = if h.show_frame { false } else { r.read_flag()? };
h.frame_is_intra = h.intra_only;
h.reset_frame_context = if h.error_resilient_mode { 0 } else { r.read_bits(2)? as u8 };
if h.intra_only {
frame_sync_code(&mut r)?;
h.color = if h.profile > 0 {
color_config(&mut r, h.profile)?
} else {
ColorConfig { bit_depth: 8, color_space: 1, color_range: false, subsampling_x: true, subsampling_y: true }
};
h.refresh_frame_flags = r.read_bits(8)? as u8;
frame_size(&mut r, &mut h)?;
render_size(&mut r, &mut h)?;
} else {
h.color = st.color;
h.refresh_frame_flags = r.read_bits(8)? as u8;
for i in 0..3 {
h.ref_frame_idx[i] = r.read_bits(3)? as u8;
h.ref_frame_sign_bias[LAST_FRAME as usize + i] = r.read_flag()?;
}
// frame_size_with_refs (6.2.5)
let mut found = false;
for i in 0..3 {
if r.read_flag()? {
let idx = h.ref_frame_idx[i] as usize;
let Some(info) = refs[idx] else {
return Err(Error::MissingReference(format!("frame size from empty slot {idx}")));
};
h.width = info.width;
h.height = info.height;
found = true;
break;
}
}
if !found {
frame_size(&mut r, &mut h)?;
}
h.size_from_ref = found;
render_size(&mut r, &mut h)?;
h.allow_high_precision_mv = r.read_flag()?;
// read_interpolation_filter (6.2.7)
h.interp_filter = if r.read_flag()? { SWITCHABLE } else { LITERAL_TO_TYPE[r.read_bits(2)? as usize] };
}
}
compute_image_size(&mut h);
if !h.error_resilient_mode {
h.refresh_frame_context = r.read_flag()?;
h.frame_parallel_decoding_mode = r.read_flag()?;
} else {
h.refresh_frame_context = false;
h.frame_parallel_decoding_mode = true;
}
h.frame_context_idx = r.read_bits(2)? as u8;
if h.frame_is_intra || h.error_resilient_mode {
let mut fc = FrameContext::default();
setup_past_independence(st, &mut fc);
if h.frame_type == KEY_FRAME || h.error_resilient_mode || h.reset_frame_context == 3 {
for c in st.contexts.iter_mut() {
*c = fc.clone();
}
} else if h.reset_frame_context == 2 {
st.contexts[h.frame_context_idx as usize] = fc.clone();
}
h.frame_context_idx = 0;
}
// loop_filter_params (6.2.8)
st.lf.level = r.read_bits(6)? as u8;
st.lf.sharpness = r.read_bits(3)? as u8;
st.lf.delta_enabled = r.read_flag()?;
if st.lf.delta_enabled && r.read_flag()? {
for i in 0..4 {
if r.read_flag()? {
st.lf.ref_deltas[i] = read_s(&mut r, 6)? as i8;
}
}
for i in 0..2 {
if r.read_flag()? {
st.lf.mode_deltas[i] = read_s(&mut r, 6)? as i8;
}
}
}
// quantization_params (6.2.9)
h.base_q_idx = r.read_bits(8)? as u8;
h.delta_q_y_dc = read_delta_q(&mut r)?;
h.delta_q_uv_dc = read_delta_q(&mut r)?;
h.delta_q_uv_ac = read_delta_q(&mut r)?;
h.lossless = h.base_q_idx == 0 && h.delta_q_y_dc == 0 && h.delta_q_uv_dc == 0 && h.delta_q_uv_ac == 0;
// segmentation_params (6.2.11)
let seg = &mut st.seg;
seg.enabled = r.read_flag()?;
seg.update_map = false;
seg.temporal_update = false;
if seg.enabled {
seg.update_map = r.read_flag()?;
if seg.update_map {
for i in 0..7 {
seg.tree_probs[i] = read_prob(&mut r)?;
}
seg.temporal_update = r.read_flag()?;
for i in 0..3 {
seg.pred_probs[i] = if seg.temporal_update { read_prob(&mut r)? } else { 255 };
}
}
if r.read_flag()? {
seg.abs_or_delta_update = r.read_flag()?;
for i in 0..MAX_SEGMENTS {
for j in 0..4 {
let mut value = 0i32;
let enabled = r.read_flag()?;
seg.feature_enabled[i][j] = enabled;
if enabled {
value = r.read_bits(SEGMENTATION_FEATURE_BITS[j])? as i32;
if SEGMENTATION_FEATURE_SIGNED[j] && r.read_flag()? {
value = -value;
}
}
seg.feature_data[i][j] = value as i16;
}
}
}
}
// tile_info (6.2.13)
let mut min_log2 = 0;
while (MAX_TILE_WIDTH_B64 << min_log2) < h.sb64_cols {
min_log2 += 1;
}
let mut max_log2 = 1;
while (h.sb64_cols >> max_log2) >= MIN_TILE_WIDTH_B64 {
max_log2 += 1;
}
max_log2 -= 1;
h.tile_cols_log2 = min_log2;
while h.tile_cols_log2 < max_log2 {
if r.read_flag()? {
h.tile_cols_log2 += 1;
} else {
break;
}
}
h.tile_rows_log2 = r.read_bits(1)?;
if h.tile_rows_log2 == 1 {
h.tile_rows_log2 += r.read_bits(1)?;
}
h.header_size_in_bytes = r.read_bits(16)?;
// trailing_bits
h.uncompressed_size = r.position().div_ceil(8);
if h.frame_is_intra {
st.color = h.color;
}
st.frame_type = h.frame_type;
ensure!(h.header_size_in_bytes > 0, "empty compressed header");
Ok(h)
}
/// diff_update_prob (6.3.3).
fn diff_update_prob(bd: &mut BoolDecoder, prob: &mut u8) {
if bd.read_bool(252) {
let delta = decode_term_subexp(bd);
*prob = inv_remap_prob(delta, *prob);
}
}
fn decode_term_subexp(bd: &mut BoolDecoder) -> usize {
if bd.read_literal(1) == 0 {
return bd.read_literal(4) as usize;
}
if bd.read_literal(1) == 0 {
return bd.read_literal(4) as usize + 16;
}
if bd.read_literal(1) == 0 {
return bd.read_literal(5) as usize + 32;
}
let v = bd.read_literal(7) as usize;
if v < 65 {
return v + 64;
}
let bit = bd.read_literal(1) as usize;
(v << 1) - 1 + bit
}
fn inv_recenter_nonneg(v: i32, m: i32) -> i32 {
if v > 2 * m {
v
} else if v & 1 != 0 {
m - ((v + 1) >> 1)
} else {
m + (v >> 1)
}
}
fn inv_remap_prob(delta: usize, prob: u8) -> u8 {
let v = INV_MAP_TABLE[delta.min(254)] as i32;
let m = prob as i32 - 1;
let r = if (m << 1) <= 255 { 1 + inv_recenter_nonneg(v, m) } else { 255 - inv_recenter_nonneg(v, 255 - 1 - m) };
r.clamp(1, 255) as u8
}
/// update_mv_prob (6.3.17).
fn update_mv_prob(bd: &mut BoolDecoder, prob: &mut u8) {
if bd.read_bool(252) {
*prob = ((bd.read_literal(7) << 1) | 1) as u8;
}
}
/// Parse the compressed header (6.3) into `h` and the probabilities `fc`.
pub fn parse_compressed(data: &[u8], h: &mut FrameHeader, fc: &mut FrameContext) -> Result<()> {
let mut bd = BoolDecoder::new(data);
// read_tx_mode (6.3.1)
h.tx_mode = if h.lossless {
0
} else {
let mut m = bd.read_literal(2) as u8;
if m == 3 {
m += bd.read_literal(1) as u8;
}
m
};
if h.tx_mode == TX_MODE_SELECT {
for i in 0..2 {
diff_update_prob(&mut bd, &mut fc.tx[1][i][0]);
}
for i in 0..2 {
for j in 0..2 {
diff_update_prob(&mut bd, &mut fc.tx[2][i][j]);
}
}
for i in 0..2 {
for j in 0..3 {
diff_update_prob(&mut bd, &mut fc.tx[3][i][j]);
}
}
}
// read_coef_probs (6.3.7)
let max_tx = TX_MODE_TO_BIGGEST_TX_SIZE[h.tx_mode as usize] as usize;
for tx in 0..=max_tx {
if bd.read_literal(1) == 1 {
for i in 0..2 {
for j in 0..2 {
for k in 0..6 {
let max_l = if k == 0 { 3 } else { 6 };
for l in 0..max_l {
for m in 0..3 {
diff_update_prob(&mut bd, &mut fc.coef[tx][i][j][k][l][m]);
}
}
}
}
}
}
}
for i in 0..3 {
diff_update_prob(&mut bd, &mut fc.skip[i]);
}
if !h.frame_is_intra {
for i in 0..7 {
for j in 0..3 {
diff_update_prob(&mut bd, &mut fc.inter_mode[i][j]);
}
}
if h.interp_filter == SWITCHABLE {
for j in 0..4 {
for i in 0..2 {
diff_update_prob(&mut bd, &mut fc.interp_filter[j][i]);
}
}
}
for i in 0..4 {
diff_update_prob(&mut bd, &mut fc.is_inter[i]);
}
// frame_reference_mode (6.3.12)
let bias = h.ref_frame_sign_bias;
let compound_allowed = bias[2] != bias[1] || bias[3] != bias[1];
h.reference_mode = 0;
if compound_allowed {
if bd.read_literal(1) == 1 {
h.reference_mode = if bd.read_literal(1) == 1 { 2 } else { 1 };
}
// setup_compound_reference_mode (6.3.18)
if bias[LAST_FRAME as usize] == bias[GOLDEN_FRAME as usize] {
h.comp_fixed_ref = ALTREF_FRAME;
h.comp_var_ref = [LAST_FRAME, GOLDEN_FRAME];
} else if bias[LAST_FRAME as usize] == bias[ALTREF_FRAME as usize] {
h.comp_fixed_ref = GOLDEN_FRAME;
h.comp_var_ref = [LAST_FRAME, ALTREF_FRAME];
} else {
h.comp_fixed_ref = LAST_FRAME;
h.comp_var_ref = [GOLDEN_FRAME, ALTREF_FRAME];
}
}
// frame_reference_mode_probs (6.3.13)
if h.reference_mode == 2 {
for i in 0..5 {
diff_update_prob(&mut bd, &mut fc.comp_mode[i]);
}
}
if h.reference_mode != 1 {
for i in 0..5 {
diff_update_prob(&mut bd, &mut fc.single_ref[i][0]);
diff_update_prob(&mut bd, &mut fc.single_ref[i][1]);
}
}
if h.reference_mode != 0 {
for i in 0..5 {
diff_update_prob(&mut bd, &mut fc.comp_ref[i]);
}
}
for i in 0..4 {
for j in 0..9 {
diff_update_prob(&mut bd, &mut fc.y_mode[i][j]);
}
}
for i in 0..16 {
for j in 0..3 {
diff_update_prob(&mut bd, &mut fc.partition[i][j]);
}
}
// mv_probs (6.3.16)
for j in 0..3 {
update_mv_prob(&mut bd, &mut fc.mv_joint[j]);
}
for i in 0..2 {
update_mv_prob(&mut bd, &mut fc.mv_sign[i]);
for j in 0..10 {
update_mv_prob(&mut bd, &mut fc.mv_class[i][j]);
}
update_mv_prob(&mut bd, &mut fc.mv_class0_bit[i]);
for j in 0..10 {
update_mv_prob(&mut bd, &mut fc.mv_bits[i][j]);
}
}
for i in 0..2 {
for j in 0..2 {
for k in 0..3 {
update_mv_prob(&mut bd, &mut fc.mv_class0_fr[i][j][k]);
}
}
for k in 0..3 {
update_mv_prob(&mut bd, &mut fc.mv_fr[i][k]);
}
}
if h.allow_high_precision_mv {
for i in 0..2 {
update_mv_prob(&mut bd, &mut fc.mv_class0_hp[i]);
update_mv_prob(&mut bd, &mut fc.mv_hp[i]);
}
}
}
Ok(())
}
/// Superframe index parsing (Annex B): the frames of a chunk.
pub fn split_superframe(data: &[u8]) -> Vec<&[u8]> {
if let Some(&last) = data.last()
&& last & 0xe0 == 0xc0
{
let sz_bytes = ((last >> 3) & 3) as usize + 1;
let num = (last & 7) as usize + 1;
let index_size = 2 + num * sz_bytes;
if data.len() >= index_size && data[data.len() - index_size] == last {
let mut frames = Vec::with_capacity(num);
let mut pos = 0usize;
let mut p = data.len() - index_size + 1;
let end = data.len() - index_size;
for _ in 0..num {
let mut sz = 0usize;
for b in 0..sz_bytes {
sz |= (data[p + b] as usize) << (8 * b);
}
p += sz_bytes;
if pos + sz > end {
frames.push(&data[pos..end]);
return frames;
}
frames.push(&data[pos..pos + sz]);
pos += sz;
}
return frames;
}
}
vec![data]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn superframe_index() {
// Two frames of 3 and 2 bytes, 1 byte per size.
let mut d = vec![1u8, 2, 3, 4, 5];
let marker = 0xc0 | 1; // 2 frames, 1 byte sizes
d.extend([marker, 3, 2, marker]);
let f = split_superframe(&d);
assert_eq!(f, vec![&[1u8, 2, 3][..], &[4u8, 5][..]]);
// Not a superframe: last byte is a marker but the first index byte does not match.
let d2 = vec![9u8, 9, 0xc1];
assert_eq!(split_superframe(&d2), vec![&d2[..]]);
}
#[test]
fn inv_remap_is_bijective_per_prob() {
for p in 1..=255u8 {
let mut seen = std::collections::HashSet::new();
for d in 0..254 {
seen.insert(inv_remap_prob(d, p));
}
// Every other probability value is reachable.
assert!(seen.len() >= 253, "p {p}: {}", seen.len());
}
}
}