/// Bounded reduced-still frame header parsing for the first AVIF pixel stage.
///
/// The parser follows the reduced-still ordering from AV1 §5.9.2 and keeps
/// the checked tile-group envelope needed by the intra reconstruction path.
///|
///|
/// `MAX_SEGMENTS`: how many segment ids a frame may assign.
pub let av1_segments : Int = 8
///|
/// `SEG_LVL_MAX`: the eight per-segment features.
pub let av1_segment_levels : Int = 8
///|
/// `SEG_LVL_REF_FRAME`: features at or above it set `SegIdPreSkip`.
pub let av1_seg_level_ref_frame : Int = 5
///|
/// `SEG_LVL_ALT_Q`: the one per-segment feature this decoder applies. It only
/// shifts the block's quantizer index, so the residual reading applies it with
/// no further plumbing. Every other level changes the loop filter, the block's
/// reference or its prediction mode, and the block stage refuses a frame that
/// enables one (see `av1_inter_frame_supported`).
pub let av1_seg_level_alt_q : Int = 0
///|
/// The parsed `segmentation_params` (AV1 §6.10.9). `enabled` without a map
/// update or an active feature changes nothing about the decode.
pub(all) struct Av1SegmentationParams {
enabled : Bool
update_map : Bool
temporal_update : Bool
update_data : Bool
feature_enabled : Array[Array[Bool]]
feature_data : Array[Array[Int]]
seg_id_pre_skip : Bool
last_active_seg_id : Int
}
///|
/// `MAX_LOOP_FILTER` as the segment feature table needs it (AV1 §6.10.9).
fn av1_segmentation_loop_filter_limit(sequence : Av1SequenceInfo) -> Int {
ignore(sequence)
63
}
///|
pub(all) struct Av1FrameHeaderInfo {
/// Visible entropy-coded width, before superres horizontal upscaling.
frame_width : Int
upscaled_width : Int
/// Entropy-coded height. A general header can override the sequence maximum
/// (AV1 5.9.6), so nothing downstream may assume `sequence.height`.
frame_height : Int
/// Eight disables superres; active denominators are nine through sixteen.
superres_denom : Int
header_bytes : Int
tile_payload : Array[Byte]
base_q_idx : Int
quant_matrix : Av1QuantMatrix?
delta_lf_res : Int
delta_lf_multi : Bool
tile_cols : Int
tile_rows : Int
tile_cols_log2 : Int
tile_rows_log2 : Int
/// Tile boundaries in superblock units, including the terminal boundary.
/// These arrays preserve non-uniform `tile_info()` geometry for callers that
/// can place independently-sized tile images. The first element is always
/// zero and the final element is the superblock count for that axis.
tile_col_starts_sb : Array[Int]
tile_row_starts_sb : Array[Int]
/// Size-prefix width for the tile group (0 for the legacy isolated tile).
tile_size_bytes : Int
/// Tile whose adapted CDF rows become the frame-end probability context.
context_update_tile_id : Int
tx_mode_select : Bool
reduced_tx_set : Bool
coded_lossless : Bool
cdef : Av1CdefConfig?
loop_filter : Av1LoopFilterConfig?
/// Loop-restoration modes and unit sizes, or None when the frame omitted
/// the syntax (all-lossless, disabled sequence tool or intrabc).
restoration : Av1RestorationConfig?
disable_cdf_update : Bool
allow_screen_content_tools : Bool
/// Film-grain parameters, or None when the sequence omitted the tool.
film_grain : Av1FilmGrainParams?
/// Per-plane quantizer deltas from quantization_params.
quant_deltas : Av1QuantDeltas
/// `delta_q_params` (AV1 §6.10.10): whether each superblock codes its own
/// quantizer delta, and the log2 scale those deltas carry.
delta_q_present : Bool
delta_q_res : Int
/// `delta_lf_params`: whether each superblock also codes loop-filter deltas.
/// Per-superblock deltas feed the reconstructed frame's loop-filter levels.
delta_lf_present : Bool
/// Parsed segmentation_params; None is impossible once parsed (an absent
/// segmentation is the empty struct).
segmentation : Av1SegmentationParams
/// Frame classification (AV1 §6.8.2): 0 key, 1 inter, 2 intra-only, 3 switch.
frame_type : Int
frame_is_intra : Bool
show_frame : Bool
show_existing_frame : Bool
/// Slot of the buffered frame a show-existing frame displays.
frame_to_show_map_idx : Int
error_resilient : Bool
/// Whether the frame may serve as a reference for display-order decisions
/// (AV1 §5.9.2). Only a hidden frame signals it.
showable_frame : Bool
/// §7.21: when set, the entropy-coded frame contexts are not carried over to
/// the next tile group, so every tile starts from the frame-level snapshot.
disable_frame_end_update_cdf : Bool
/// 0/1 when signalled, 2 (`av1_select_integer_mv`) when the sequence fixes it.
force_integer_mv : Int
current_frame_id : Int
order_hint : Int
/// `primary_ref_frame`; `av1_primary_ref_none` selects a fresh frame context.
primary_ref_frame : Int
/// Eight-bit `refresh_frame_flags` mask over the reference buffer slots.
refresh_frame_flags : Int
/// Slot index for each of the seven named references, `LAST` first.
ref_frame_idx : Array[Int]
/// Per-reference-name order hints and forward/backward sign bias (§5.9.3).
order_hints : Array[Int]
ref_frame_sign_bias : Array[Int]
allow_high_precision_mv : Bool
/// 0..3 select a fixed filter set, 4 means the block signals its own.
interpolation_filter : Int
is_motion_mode_switchable : Bool
use_ref_frame_mvs : Bool
allow_warped_motion : Bool
reference_select : Bool
skip_mode_present : Bool
/// The two ordered references a skip-mode block copies its motion from.
skip_mode_frame : Array[Int]
/// Global motion type and six-tap warp model per reference name (§5.9.24).
gm_type : Array[Int]
gm_params : Array[Array[Int]]
/// Warp models inherited from the primary reference by `load_previous`.
prev_gm_params : Array[Array[Int]]
mut loop_filter_ref_deltas : Array[Int]
mut loop_filter_mode_deltas : Array[Int]
allow_intrabc : Bool
render_width : Int
render_height : Int
}
///|
priv struct Av1FrameBits {
data : Array[Byte]
mut bit_pos : Int
}
///|
fn Av1FrameBits::take(self : Av1FrameBits, count : Int) -> Int? {
if count < 0 || count > 31 || self.bit_pos + count > self.data.length() * 8 {
return None
}
let mut out = 0
for _ in 0..> 3
out = (out << 1) |
((self.data[byte_pos].to_int() >> (7 - (self.bit_pos & 7))) & 1)
self.bit_pos = self.bit_pos + 1
}
Some(out)
}
///|
fn Av1FrameBits::signed(self : Av1FrameBits, count : Int) -> Int? {
let value = match self.take(count) {
Some(v) => v
None => return None
}
if (value & (1 << (count - 1))) != 0 {
Some(value - (1 << count))
} else {
Some(value)
}
}
///|
/// Parse cdef_params after the caller has handled its frame/sequence gates.
/// NumPlanes==1 omits both UV syntax fields from every table entry.
fn av1_parse_cdef_config(
reader : Av1FrameBits,
monochrome : Bool,
) -> Av1CdefConfig? {
let damping = match reader.take(2) {
Some(value) => value + 3
None => return None
}
let bits = match reader.take(2) {
Some(value) => value
None => return None
}
let y_primary : Array[Int] = []
let y_secondary : Array[Int] = []
let uv_primary : Array[Int] = []
let uv_secondary : Array[Int] = []
for _ in 0..<(1 << bits) {
let primary = match reader.take(4) {
Some(value) => value
None => return None
}
let secondary = match reader.take(2) {
Some(value) => value
None => return None
}
y_primary.push(primary)
y_secondary.push(if secondary == 3 { 4 } else { secondary })
if !monochrome {
let primary = match reader.take(4) {
Some(value) => value
None => return None
}
let secondary = match reader.take(2) {
Some(value) => value
None => return None
}
uv_primary.push(primary)
uv_secondary.push(if secondary == 3 { 4 } else { secondary })
}
}
Some({ bits, damping, y_primary, y_secondary, uv_primary, uv_secondary, })
}
///|
/// Parse loop-restoration modes and unit sizes from the uncompressed header,
/// in the AV1 §5.9.11 position between CDEF and tx_mode. Two header bits per
/// actual plane select the mode in Y, U, V order (`00` None, `01` Switchable,
/// `10` Wiener, `11` Sgrproj); monochrome omits UV. When any plane is active
/// the luma unit size follows the superblock size with one bit (SB64) or two
/// bits (SB128 keeps its size above 64, halving only through chroma), and
/// 4:2:0 chroma may halve that size with one final bit. All planes None emits
/// no size bits and receives the implicit U256.
fn av1_parse_restoration_config(
reader : Av1FrameBits,
monochrome : Bool,
use_128x128_superblock : Bool,
subsampling_x : Bool,
subsampling_y : Bool,
) -> Av1RestorationConfig? {
let num_planes = if monochrome { 1 } else { 3 }
let modes : Array[Av1RestorationType] = []
let mut all_none = true
let mut chroma_none = true
for plane in 0.. value
None => return None
}
modes.push(mode)
if mode is Av1RestorationNone {
continue
}
all_none = false
chroma_none = chroma_none && plane == 0
}
let unit_sizes : Array[Int] = []
if all_none {
// libaom assigns RESTORATION_UNITSIZE_MAX without signalling any bits.
for _ in 0.. value
None => return None
}
luma_unit = luma_unit << bit
}
if luma_unit > 64 {
let bit = match reader.take(1) {
Some(value) => value
None => return None
}
luma_unit = luma_unit << bit
}
if !monochrome {
let s = if subsampling_x && subsampling_y { 1 } else { 0 }
let uv_unit = if s != 0 && !chroma_none {
let bit = match reader.take(1) {
Some(value) => value
None => return None
}
luma_unit >> (bit * s)
} else {
luma_unit
}
unit_sizes.push(luma_unit)
unit_sizes.push(uv_unit)
unit_sizes.push(uv_unit)
} else {
unit_sizes.push(luma_unit)
}
}
Some({ modes, unit_sizes, })
}
///|
/// Two header bits map to the internal restoration enum. The integer is not
/// the internal code: `01` is Switchable and `10` is Wiener.
fn av1_restoration_header_mode(reader : Av1FrameBits) -> Av1RestorationType? {
let first = match reader.take(1) {
Some(value) => value
None => return None
}
let second = match reader.take(1) {
Some(value) => value
None => return None
}
if first == 0 {
Some(
if second == 0 {
Av1RestorationNone
} else {
Av1RestorationSwitchable
},
)
} else {
Some(if second == 0 { Av1RestorationWiener } else { Av1RestorationSgrproj })
}
}
///|
/// ceil(log2(target / block_size)), matching AV1 tile_log2().
fn av1_tile_log2(block_size : Int, target : Int) -> Int {
let mut out = 0
while block_size << out < target {
out = out + 1
}
out
}
///|
/// AV1 `ns(n)` descriptor from §4. The result is in [0, n); malformed or
/// truncated input returns None. `n == 1` consumes no bits as required by the
/// descriptor.
fn Av1FrameBits::ns(self : Av1FrameBits, n : Int) -> Int? {
if n <= 0 {
return None
}
let mut w = 0
let mut power = 1
while power < n {
power = power << 1
w = w + 1
}
// For n=1, w=0 and m=0; the descriptor has a zero-bit payload.
if w == 0 {
return Some(0)
}
let m = (1 << w) - n
let value = match self.take(w - 1) {
Some(v) => v
None => return None
}
if value < m {
Some(value)
} else {
match self.take(1) {
Some(bit) => Some((value << 1) - m + bit)
None => None
}
}
}
///|
/// Consume the tile-group header and return the byte-aligned tile data.
///
/// OBU_FRAME aligns the frame header before reading the tile-group header.
/// Its tile_start_and_end_present_flag must be zero (AV1 section 6.10.1).
fn av1_frame_tile_group_payload(
frame_payload : Array[Byte],
bit_pos : Int,
tile_cols : Int,
tile_rows : Int,
) -> (Int, Array[Byte])? {
let reader : Av1FrameBits = { data: frame_payload, bit_pos, }
let tile_count = tile_cols * tile_rows
if tile_count <= 0 {
return None
}
while (reader.bit_pos & 7) != 0 {
if reader.take(1) != Some(0) {
return None
}
}
if tile_count > 1 {
if reader.take(1) != Some(0) {
return None
}
}
while (reader.bit_pos & 7) != 0 {
if reader.take(1) != Some(0) {
return None
}
}
let offset = reader.bit_pos / 8
if offset >= frame_payload.length() {
return None
}
let tile_payload : Array[Byte] = []
for i in offset.. Av1FilmGrainParams? {
let apply_grain = match reader.take(1) {
Some(v) => v == 1
None => return None
}
let mut grain_seed = 0
let mut num_y_points = 0
let point_y_value : Array[Int] = []
let point_y_scaling : Array[Int] = []
let mut chroma_scaling_from_luma = false
let mut num_cb_points = 0
let point_cb_value : Array[Int] = []
let point_cb_scaling : Array[Int] = []
let mut num_cr_points = 0
let point_cr_value : Array[Int] = []
let point_cr_scaling : Array[Int] = []
let mut grain_scaling_minus8 = 0
let mut ar_coeff_lag = 0
let ar_coeffs_y : Array[Int] = []
let ar_coeffs_cb : Array[Int] = []
let ar_coeffs_cr : Array[Int] = []
let mut ar_coeff_shift_minus6 = 0
let mut grain_scale_shift = 0
let mut cb_mult = 0
let mut cb_luma_mult = 0
let mut cb_offset = 0
let mut cr_mult = 0
let mut cr_luma_mult = 0
let mut cr_offset = 0
let mut overlap_flag = false
let mut clip_to_restricted_range = false
if apply_grain {
grain_seed = match reader.take(16) {
Some(v) => v
None => return None
}
if frame_type == av1_inter_frame {
let update = match reader.take(1) {
Some(value) => value == 1
None => return None
}
if !update {
let slot = match reader.take(3) {
Some(value) => value
None => return None
}
let state = match map {
Some(value) => value
None => return None
}
if !state.valid[slot] || !ref_frame_idx.contains(slot) {
return None
}
let stored = state.saved_film_grain[slot].unwrap_or(
av1_frame_grain_empty(),
)
let copied = av1_frame_grain_copy(stored)
return Some({ ..copied, apply_grain: true, grain_seed, })
}
}
num_y_points = match reader.take(4) {
Some(v) => v
None => return None
}
if num_y_points > 14 {
return None
}
for _ in 0.. v
None => return None
}
let scaling = match reader.take(8) {
Some(v) => v
None => return None
}
if point_y_value.length() > 0 &&
point_y_value[point_y_value.length() - 1] >= value {
return None
}
point_y_value.push(value)
point_y_scaling.push(scaling)
}
if !monochrome {
chroma_scaling_from_luma = match reader.take(1) {
Some(v) => v == 1
None => return None
}
}
if monochrome ||
chroma_scaling_from_luma ||
(subsampling_x && subsampling_y && num_y_points == 0) {
num_cb_points = 0
num_cr_points = 0
} else {
num_cb_points = match reader.take(4) {
Some(v) => v
None => return None
}
if num_cb_points > 10 {
return None
}
for _ in 0.. v
None => return None
}
let scaling = match reader.take(8) {
Some(v) => v
None => return None
}
if point_cb_value.length() > 0 &&
point_cb_value[point_cb_value.length() - 1] >= value {
return None
}
point_cb_value.push(value)
point_cb_scaling.push(scaling)
}
num_cr_points = match reader.take(4) {
Some(v) => v
None => return None
}
if num_cr_points > 10 {
return None
}
for _ in 0.. v
None => return None
}
let scaling = match reader.take(8) {
Some(v) => v
None => return None
}
if point_cr_value.length() > 0 &&
point_cr_value[point_cr_value.length() - 1] >= value {
return None
}
point_cr_value.push(value)
point_cr_scaling.push(scaling)
}
}
if subsampling_x &&
subsampling_y &&
(num_cb_points == 0) != (num_cr_points == 0) {
return None
}
grain_scaling_minus8 = match reader.take(2) {
Some(v) => v
None => return None
}
ar_coeff_lag = match reader.take(2) {
Some(v) => v
None => return None
}
let num_pos_luma = 2 * ar_coeff_lag * (ar_coeff_lag + 1)
let num_pos_chroma = if num_y_points > 0 {
num_pos_luma + 1
} else {
num_pos_luma
}
if num_y_points > 0 {
for _ in 0.. v
None => return None
}
ar_coeffs_y.push(value)
}
}
if chroma_scaling_from_luma || num_cb_points > 0 {
for _ in 0.. v
None => return None
}
ar_coeffs_cb.push(value)
}
}
if chroma_scaling_from_luma || num_cr_points > 0 {
for _ in 0.. v
None => return None
}
ar_coeffs_cr.push(value)
}
}
ar_coeff_shift_minus6 = match reader.take(2) {
Some(v) => v
None => return None
}
grain_scale_shift = match reader.take(2) {
Some(v) => v
None => return None
}
if num_cb_points > 0 {
cb_mult = match reader.take(8) {
Some(v) => v
None => return None
}
cb_luma_mult = match reader.take(8) {
Some(v) => v
None => return None
}
cb_offset = match reader.take(9) {
Some(v) => v
None => return None
}
}
if num_cr_points > 0 {
cr_mult = match reader.take(8) {
Some(v) => v
None => return None
}
cr_luma_mult = match reader.take(8) {
Some(v) => v
None => return None
}
cr_offset = match reader.take(9) {
Some(v) => v
None => return None
}
}
overlap_flag = match reader.take(1) {
Some(v) => v == 1
None => return None
}
clip_to_restricted_range = match reader.take(1) {
Some(v) => v == 1
None => return None
}
}
Some({
apply_grain,
grain_seed,
num_y_points,
point_y_value,
point_y_scaling,
chroma_scaling_from_luma,
num_cb_points,
point_cb_value,
point_cb_scaling,
num_cr_points,
point_cr_value,
point_cr_scaling,
grain_scaling_minus8,
ar_coeff_lag,
ar_coeffs_y,
ar_coeffs_cb,
ar_coeffs_cr,
ar_coeff_shift_minus6,
grain_scale_shift,
cb_mult,
cb_luma_mult,
cb_offset,
cr_mult,
cr_luma_mult,
cr_offset,
overlap_flag,
clip_to_restricted_range,
})
}
///|
/// Everything `frame_header()` establishes before `tile_info()`: the frame
/// class, the reference list, the motion-tool flags and the frame geometry.
/// A reduced still-picture header is the degenerate case where most of these
/// are derived rather than signalled (AV1 §5.9.2).
priv struct Av1FramePrefix {
mut disable_cdf_update : Bool
mut allow_screen_content_tools : Int
mut force_integer_mv : Int
mut frame_type : Int
mut frame_is_intra : Bool
mut show_frame : Bool
mut show_existing_frame : Bool
mut frame_to_show_map_idx : Int
mut error_resilient : Bool
mut current_frame_id : Int
mut order_hint : Int
mut primary_ref_frame : Int
mut refresh_frame_flags : Int
ref_frame_idx : Array[Int]
order_hints : Array[Int]
ref_frame_sign_bias : Array[Int]
mut allow_high_precision_mv : Bool
mut interpolation_filter : Int
mut is_motion_mode_switchable : Bool
mut use_ref_frame_mvs : Bool
mut allow_intrabc : Bool
mut upscaled_width : Int
mut frame_width : Int
mut frame_height : Int
mut superres_denom : Int
mut render_width : Int
mut render_height : Int
/// `frame_size_override_flag`, needed again by `frame_size_with_refs`.
mut frame_size_override_flag : Int
mut showable_frame : Bool
mut disable_frame_end_update_cdf : Bool
mut loop_filter_ref_deltas : Array[Int]
mut loop_filter_mode_deltas : Array[Int]
mut prev_gm_params : Array[Array[Int]]
}
///|
/// `frame_size()` plus `superres_params()` (AV1 §5.9.6, §5.9.8). The coded
/// width shrinks under active superres while `upscaled_width` keeps the
/// resolution motion vectors address.
fn av1_frame_prefix_size(
r : Av1FrameBits,
prefix : Av1FramePrefix,
sequence : Av1SequenceInfo,
) -> Bool {
let override_size = prefix.frame_size_override_flag == 1
let (width, height) = if override_size {
let w = match r.take(sequence.frame_width_bits_minus_1 + 1) {
Some(v) => v + 1
None => return false
}
let h = match r.take(sequence.frame_height_bits_minus_1 + 1) {
Some(v) => v + 1
None => return false
}
(w, h)
} else {
(sequence.width, sequence.height)
}
av1_frame_prefix_superres(r, prefix, sequence, width, height)
}
///|
/// A frame inherits display geometry, never the preceding frame's coded width.
/// Both explicit and reference-derived sizes read a fresh superres denominator.
/// libaom and dav1d clamp the coded width to min(16, UpscaledWidth), including
/// legal active-superres streams whose small display width stays unchanged.
fn av1_frame_prefix_superres(
r : Av1FrameBits,
prefix : Av1FramePrefix,
sequence : Av1SequenceInfo,
upscaled : Int,
height : Int,
) -> Bool {
let denom = if sequence.enable_superres {
let use_superres = match r.take(1) {
Some(v) => v == 1
None => return false
}
if use_superres {
match r.take(3) {
Some(value) => value + 9
None => return false
}
} else {
8
}
} else {
8
}
if upscaled <= 0 || height <= 0 || upscaled > 65536 || height > 65536 {
return false
}
let minimum = if upscaled < 16 { upscaled } else { 16 }
let rounded = (upscaled * 8 + denom / 2) / denom
let coded = if rounded < minimum { minimum } else { rounded }
prefix.upscaled_width = upscaled
prefix.frame_width = coded
prefix.frame_height = height
prefix.superres_denom = denom
true
}
///|
/// `render_size()` (AV1 §5.9.9). Render dimensions only affect display, so a
/// frame may carry them without any downstream stage consuming them.
fn av1_frame_prefix_render_size(
r : Av1FrameBits,
prefix : Av1FramePrefix,
) -> Bool {
let different = match r.take(1) {
Some(v) => v == 1
None => return false
}
if different {
let w = match r.take(16) {
Some(v) => v
None => return false
}
let h = match r.take(16) {
Some(v) => v
None => return false
}
prefix.render_width = w + 1
prefix.render_height = h + 1
} else {
prefix.render_width = prefix.upscaled_width
prefix.render_height = prefix.frame_height
}
true
}
///|
/// `inter_refs()` and `frame_size_with_refs()` (AV1 §5.9.2, §5.9.7). Fills the
/// seven named-reference slot indices and derives the per-reference order
/// hints and sign biases the block stage compares against.
fn av1_frame_prefix_inter_refs(
r : Av1FrameBits,
prefix : Av1FramePrefix,
sequence : Av1SequenceInfo,
map : Av1FrameMap,
) -> Bool {
let mut short_signaling = false
if sequence.enable_order_hint {
short_signaling = match r.take(1) {
Some(v) => v == 1
None => return false
}
if short_signaling {
let last = match r.take(3) {
Some(v) => v
None => return false
}
let gold = match r.take(3) {
Some(v) => v
None => return false
}
av1_set_frame_refs(
sequence,
map,
prefix.order_hint,
last,
gold,
prefix.ref_frame_idx,
)
}
}
for i in 0.. v
None => return false
}
prefix.ref_frame_idx[i] = slot
}
if sequence.frame_id_numbers_present {
let (diff_len, id_len) = match av1_frame_id_lengths(sequence) {
Some(value) => value
None => return false
}
let delta = match r.take(diff_len) {
Some(value) => value + 1
None => return false
}
let slot = prefix.ref_frame_idx[i]
let mask = (1 << id_len) - 1
let expected = (prefix.current_frame_id - delta) & mask
if !map.valid[slot] || map.frame_ids[slot] != expected {
return false
}
}
}
// A reference-sized frame inherits the full geometry of the first slot that
// declares itself present.
let mut inherited = false
if prefix.frame_size_override_flag == 1 && !prefix.error_resilient {
for i in 0.. v == 1
None => return false
}
if found {
let slot = prefix.ref_frame_idx[i]
if !map.valid[slot] ||
!av1_frame_prefix_superres(
r,
prefix,
sequence,
map.upscaled_widths[slot],
map.frame_heights[slot],
) {
return false
}
prefix.render_width = map.render_widths[slot]
prefix.render_height = map.render_heights[slot]
inherited = true
break
}
}
if !inherited {
if !av1_frame_prefix_size(r, prefix, sequence) {
return false
}
if !av1_frame_prefix_render_size(r, prefix) {
return false
}
}
} else {
if !av1_frame_prefix_size(r, prefix, sequence) {
return false
}
if !av1_frame_prefix_render_size(r, prefix) {
return false
}
}
prefix.allow_high_precision_mv = if prefix.force_integer_mv != 0 {
false
} else {
match r.take(1) {
Some(v) => v == 1
None => return false
}
}
// `interpolation_filter`: a switchable frame lets each block pick, otherwise
// the three-bit selection is fixed for the whole frame (AV1 §5.9.11).
let switchable = match r.take(1) {
Some(v) => v == 1
None => return false
}
prefix.interpolation_filter = if switchable {
4
} else {
match r.take(2) {
Some(v) => v
None => return false
}
}
prefix.is_motion_mode_switchable = match r.take(1) {
Some(v) => v == 1
None => return false
}
prefix.use_ref_frame_mvs = if prefix.error_resilient ||
!sequence.enable_ref_frame_mvs {
false
} else {
match r.take(1) {
Some(v) => v == 1
None => return false
}
}
for i in 0..
0 {
1
} else {
0
}
}
true
}
///|
/// Reads `frame_header()` up to (but excluding) `tile_info()`. `map` carries the
/// reference buffer state the general grammar reads and updates; a reduced
/// still-picture sequence needs none of it.
fn av1_parse_frame_prefix(
r : Av1FrameBits,
sequence : Av1SequenceInfo,
map : Av1FrameMap?,
temporal_id? : Int = 0,
spatial_id? : Int = 0,
) -> Av1FramePrefix? {
let prefix : Av1FramePrefix = {
disable_cdf_update: false,
allow_screen_content_tools: 0,
// §5.9.2: a frame that does not signal it means "integer MV not forced".
// The sequence-level SELECT value (2) never reaches this field.
force_integer_mv: 0,
frame_type: av1_key_frame,
frame_is_intra: true,
show_frame: true,
show_existing_frame: false,
frame_to_show_map_idx: 0,
error_resilient: false,
current_frame_id: 0,
order_hint: 0,
primary_ref_frame: av1_primary_ref_none,
refresh_frame_flags: av1_all_ref_frames_mask,
ref_frame_idx: Array::make(av1_refs_per_frame, 0),
order_hints: Array::make(av1_total_refs_per_frame, 0),
ref_frame_sign_bias: Array::make(av1_total_refs_per_frame, 0),
allow_high_precision_mv: false,
interpolation_filter: 4,
is_motion_mode_switchable: false,
use_ref_frame_mvs: false,
allow_intrabc: false,
upscaled_width: sequence.width,
frame_width: sequence.width,
frame_height: sequence.height,
superres_denom: 8,
render_width: sequence.width,
render_height: sequence.height,
frame_size_override_flag: 0,
showable_frame: true,
disable_frame_end_update_cdf: sequence.reduced_still_picture_header,
loop_filter_ref_deltas: av1_default_loop_filter_ref_deltas(),
loop_filter_mode_deltas: [0, 0],
prev_gm_params: Array::make(
av1_total_refs_per_frame,
av1_identity_gm_params(),
),
}
let id_len = if sequence.frame_id_numbers_present {
match av1_frame_id_lengths(sequence) {
Some((_, value)) => value
None => return None
}
} else {
0
}
if !sequence.reduced_still_picture_header {
let show_existing = match r.take(1) {
Some(v) => v == 1
None => return None
}
if show_existing {
prefix.show_existing_frame = true
prefix.frame_to_show_map_idx = match r.take(3) {
Some(v) => v
None => return None
}
if sequence.decoder_model_info_present &&
!sequence.equal_picture_interval &&
av1_frame_timing_value(
r,
sequence.frame_presentation_time_length_minus_1 + 1,
)
is None {
return None
}
if sequence.frame_id_numbers_present {
let display_id = match r.take(id_len) {
Some(value) => value
None => return None
}
let state = match map {
Some(value) => value
None => return None
}
let slot = prefix.frame_to_show_map_idx
if !state.valid[slot] || state.frame_ids[slot] != display_id {
return None
}
prefix.current_frame_id = display_id
}
// The remaining fields keep their reduced-still defaults; a
// show-existing frame carries no new syntax, so the caller resolves it
// against the reference buffer instead of decoding tiles.
match map {
Some(state) =>
prefix.frame_type = state.frame_types[prefix.frame_to_show_map_idx]
None => return None
}
prefix.show_frame = true
return Some(prefix)
}
prefix.frame_type = match r.take(2) {
Some(v) => v
None => return None
}
prefix.frame_is_intra = prefix.frame_type == av1_key_frame ||
prefix.frame_type == av1_intra_only_frame
prefix.show_frame = match r.take(1) {
Some(v) => v == 1
None => return None
}
if prefix.show_frame &&
sequence.decoder_model_info_present &&
!sequence.equal_picture_interval &&
av1_frame_timing_value(
r,
sequence.frame_presentation_time_length_minus_1 + 1,
)
is None {
return None
}
prefix.showable_frame = if prefix.show_frame {
// A shown frame is showable unless it is a key frame; only a hidden
// frame says so explicitly (AV1 §5.9.2).
prefix.frame_type != av1_key_frame
} else {
match r.take(1) {
Some(v) => v == 1
None => return None
}
}
prefix.error_resilient = if prefix.frame_type == av1_switch_frame ||
(prefix.frame_type == av1_key_frame && prefix.show_frame) {
true
} else {
match r.take(1) {
Some(v) => v == 1
None => return None
}
}
}
// A showing key frame invalidates every buffered reference (AV1 §7.20).
if prefix.frame_type == av1_key_frame && prefix.show_frame {
match map {
Some(state) => av1_frame_map_invalidate(state)
None => ()
}
}
prefix.disable_cdf_update = match r.take(1) {
Some(v) => v == 1
None => return None
}
prefix.allow_screen_content_tools = if sequence.seq_force_screen_content_tools ==
av1_select_screen_content_tools {
match r.take(1) {
Some(v) => v
None => return None
}
} else {
sequence.seq_force_screen_content_tools
}
if prefix.allow_screen_content_tools != 0 {
prefix.force_integer_mv = if sequence.seq_force_integer_mv ==
av1_select_integer_mv {
match r.take(1) {
Some(v) => v
None => return None
}
} else {
sequence.seq_force_integer_mv
}
}
if prefix.frame_is_intra {
prefix.force_integer_mv = 1
}
prefix.current_frame_id = if sequence.frame_id_numbers_present {
match r.take(id_len) {
Some(v) => v
None => return None
}
} else {
0
}
if sequence.frame_id_numbers_present {
let state = match map {
Some(value) => value
None => return None
}
let (diff_len, _) = av1_frame_id_lengths(sequence).unwrap()
if state.has_frame_id &&
!(prefix.frame_type == av1_key_frame && prefix.show_frame) {
let modulus = 1 << id_len
let advance = (prefix.current_frame_id + modulus - state.current_frame_id) &
(modulus - 1)
if advance == 0 || advance >= modulus / 2 {
return None
}
}
av1_frame_mark_refs(state, prefix.current_frame_id, diff_len, id_len)
}
prefix.frame_size_override_flag = if prefix.frame_type == av1_switch_frame {
1
} else if sequence.reduced_still_picture_header {
0
} else {
match r.take(1) {
Some(v) => v
None => return None
}
}
if sequence.order_hint_bits > 0 {
prefix.order_hint = match r.take(sequence.order_hint_bits) {
Some(v) => v
None => return None
}
}
prefix.primary_ref_frame = if prefix.frame_is_intra || prefix.error_resilient {
av1_primary_ref_none
} else {
match r.take(3) {
Some(v) => v
None => return None
}
}
if sequence.decoder_model_info_present {
let present = match r.take(1) {
Some(v) => v == 1
None => return None
}
if present {
// Only operating points with their own decoder model and this OBU's
// actual temporal/spatial layer code a buffer-removal timestamp.
for op in 0..<=sequence.operating_points_cnt_minus_1 {
let idc = sequence.operating_point_idc[op]
let needed = sequence.decoder_model_present_for_this_op[op] &&
(
idc == 0 ||
(
((idc >> temporal_id) & 1) == 1 &&
((idc >> (spatial_id + 8)) & 1) == 1
)
)
if needed &&
av1_frame_timing_value(
r,
sequence.buffer_removal_time_length_minus_1 + 1,
)
is None {
return None
}
}
}
}
prefix.refresh_frame_flags = if prefix.frame_type == av1_switch_frame ||
(prefix.frame_type == av1_key_frame && prefix.show_frame) {
av1_all_ref_frames_mask
} else {
match r.take(8) {
Some(v) => v
None => return None
}
}
if !prefix.frame_is_intra ||
prefix.refresh_frame_flags != av1_all_ref_frames_mask {
if prefix.error_resilient && sequence.enable_order_hint {
for _ in 0.. v == 1
None => return None
}
}
} else {
match map {
Some(state) =>
if !av1_frame_prefix_inter_refs(r, prefix, sequence, state) {
return None
}
None => return None
}
}
prefix.disable_frame_end_update_cdf = if sequence.reduced_still_picture_header ||
prefix.disable_cdf_update {
true
} else {
match r.take(1) {
Some(v) => v == 1
None => return None
}
}
// `setup_past_independence` versus `load_previous` (AV1 §7.20, §7.21). The
// frame context a non-erasable frame inherits covers the warp models and the
// loop-filter deltas, both of which the block stage reads per reference.
if prefix.primary_ref_frame == av1_primary_ref_none {
prefix.loop_filter_ref_deltas = av1_default_loop_filter_ref_deltas()
prefix.loop_filter_mode_deltas = [0, 0]
} else {
match map {
Some(state) => {
let slot = prefix.ref_frame_idx[prefix.primary_ref_frame]
prefix.loop_filter_ref_deltas = Array::copy(
state.saved_loop_filter_ref_deltas[slot],
)
prefix.loop_filter_mode_deltas = Array::copy(
state.saved_loop_filter_mode_deltas[slot],
)
prefix.prev_gm_params = Array::makei(av1_total_refs_per_frame, i => {
Array::copy(state.saved_gm_params[slot][i])
})
}
None => return None
}
}
Some(prefix)
}
///|
/// Raw-bit `decode_subexp_with_ref` (AV1 §5.9.27). The global-motion
/// parameters use this non-arithmetic coder, distinct from the MSAC variant in
/// `av1_msac.mbt`.
fn av1_bits_decode_subexp(r : Av1FrameBits, num_syms : Int) -> Int? {
let k = 3
let mut i = 0
let mut mk = 0
let mut done = false
let mut out : Int? = None
while !done {
let b2 = if i == 0 { k } else { k + i - 1 }
let a = 1 << b2
if num_syms <= mk + 3 * a {
out = match r.ns(num_syms - mk) {
Some(v) => Some(v + mk)
None => None
}
done = true
} else {
match r.take(1) {
Some(1) => {
i = i + 1
mk = mk + a
}
Some(_) => {
out = match r.take(b2) {
Some(v) => Some(v + mk)
None => None
}
done = true
}
None => {
out = None
done = true
}
}
}
}
out
}
///|
fn av1_bits_decode_signed_subexp_with_ref(
r : Av1FrameBits,
low : Int,
high : Int,
reference : Int,
) -> Int? {
let mx = high - low
let v = match av1_bits_decode_subexp(r, mx) {
Some(value) => value
None => return None
}
let shifted = reference - low
let x = if shifted << 1 <= mx {
av1_msac_inverse_recenter(shifted, v)
} else {
mx - 1 - av1_msac_inverse_recenter(mx - 1 - shifted, v)
}
Some(x + low)
}
///|
/// One decoded warp coefficient, re-expressed in `WARPEDMODEL_PREC_BITS`
/// precision. The diagonal entries add the identity bias back.
fn av1_frame_global_param(
r : Av1FrameBits,
typ : Int,
idx : Int,
prev : Array[Int],
allow_high_precision_mv : Bool,
) -> Int? {
let mut abs_bits = 12
let mut prec_bits = 15
if idx < 2 {
if typ == av1_gm_translation {
// A pure translation codes one bit less range and precision when the
// frame does not allow quarter-pel motion vectors.
let hp = if allow_high_precision_mv { 0 } else { 1 }
abs_bits = 9 - hp
prec_bits = 3 - hp
} else {
abs_bits = 12
prec_bits = 6
}
}
let prec_diff = av1_warped_model_prec_bits - prec_bits
let bias = if idx % 3 == 2 { 1 << av1_warped_model_prec_bits } else { 0 }
let sub_ = if idx % 3 == 2 { 1 << prec_bits } else { 0 }
let mx = 1 << abs_bits
let base = (prev[idx] >> prec_diff) - sub_
let delta = match
av1_bits_decode_signed_subexp_with_ref(r, -mx, mx + 1, base) {
Some(value) => value
None => return None
}
Some((delta << prec_diff) + bias)
}
///|
/// `global_motion_params()` (AV1 §5.9.24). Each reference carries a warp model
/// coded as deltas against the model `load_previous` inherited.
fn av1_frame_global_motion(
r : Av1FrameBits,
prefix : Av1FramePrefix,
) -> (Array[Int], Array[Array[Int]])? {
let gm_type = Array::make(av1_total_refs_per_frame, av1_gm_identity)
let gm_params = Array::makei(av1_total_refs_per_frame, _ => {
av1_identity_gm_params()
})
if prefix.frame_is_intra {
return Some((gm_type, gm_params))
}
let read_param = fn(idx : Int, name : Int) -> Int? {
av1_frame_global_param(
r,
gm_type[name],
idx,
prefix.prev_gm_params[name],
prefix.allow_high_precision_mv,
)
}
for name = av1_last_frame; name <= av1_alt_ref_frame; name = name + 1 {
let mut typ = av1_gm_identity
if (match r.take(1) {
Some(v) => v == 1
None => return None
}) {
if (match r.take(1) {
Some(v) => v == 1
None => return None
}) {
typ = av1_gm_rot_zoom
} else if (match r.take(1) {
Some(v) => v == 1
None => return None
}) {
typ = av1_gm_translation
} else {
typ = av1_gm_affine
}
}
gm_type[name] = typ
let indices = if typ == av1_gm_affine {
[2, 3, 4, 5]
} else if typ == av1_gm_rot_zoom {
[2, 3]
} else {
[]
}
for idx in indices {
match read_param(idx, name) {
Some(value) => gm_params[name][idx] = value
None => return None
}
}
if typ == av1_gm_rot_zoom {
// A rot-zoom model is a scaled rotation, so the second row follows.
gm_params[name][4] = -gm_params[name][3]
gm_params[name][5] = gm_params[name][2]
}
if typ >= av1_gm_translation {
for idx in [0, 1] {
match read_param(idx, name) {
Some(value) => gm_params[name][idx] = value
None => return None
}
}
}
}
Some((gm_type, gm_params))
}
///|
/// `skip_mode_params()` (AV1 §5.9.22). A frame may declare that every block
/// copies its motion from a fixed pair of ordered references, which removes
/// per-block motion syntax entirely.
fn av1_frame_skip_mode(
r : Av1FrameBits,
prefix : Av1FramePrefix,
sequence : Av1SequenceInfo,
reference_select : Bool,
) -> (Bool, Array[Int])? {
let frames = [av1_last_frame, av1_last_frame]
let mut allowed = false
if !prefix.frame_is_intra && reference_select && sequence.enable_order_hint {
let dist = fn(hint : Int) -> Int {
av1_relative_dist(sequence, hint, prefix.order_hint)
}
let mut forward_idx = -1
let mut forward_hint = 0
let mut backward_idx = -1
let mut backward_hint = 0
for i in 0.. dist(forward_hint) {
forward_idx = i
forward_hint = hint
}
} else if d > 0 {
if backward_idx < 0 || dist(hint) < dist(backward_hint) {
backward_idx = i
backward_hint = hint
}
}
}
// Pair the closest earlier/later references. With no later reference,
// pair the closest earlier reference with the next distinct earlier one.
let mut second_idx = -1
let mut second_hint = 0
if forward_idx >= 0 && backward_idx < 0 {
for i in 0.. dist(second_hint) {
second_idx = i
second_hint = hint
}
}
}
}
let pair = if forward_idx < 0 {
None
} else if backward_idx >= 0 {
Some((forward_idx, backward_idx))
} else if second_idx >= 0 {
Some((forward_idx, second_idx))
} else {
None
}
match pair {
Some((a, b)) => {
allowed = true
let lo = if a < b { a } else { b }
let hi = if a < b { b } else { a }
frames[0] = av1_last_frame + lo
frames[1] = av1_last_frame + hi
}
None => ()
}
}
if !allowed {
Some((false, frames))
} else {
match r.take(1) {
Some(v) => Some((v == 1, frames))
None => None
}
}
}
///|
/// Parses a frame header and returns its entropy-coded tile group. Unsupported
/// syntax is rejected before any bytes are exposed to the reconstruction
/// stage. A general (non reduced-still) sequence reads and updates the
/// reference buffer, so `map` is required for one.
pub fn av1_parse_stage1_frame(
frame_payload : Array[Byte],
sequence : Av1SequenceInfo,
allow_monochrome? : Bool = false,
allow_highbd? : Bool = false,
map? : Av1FrameMap,
frame_header_obu? : Bool = false,
tile_groups? : Array[Array[Byte]] = [],
temporal_id? : Int = 0,
spatial_id? : Int = 0,
) -> Av1FrameHeaderInfo? {
// Profile and bit-depth pairing from AV1 section 6.4.1: profiles 0 and 1
// are 8/10-bit, profile 2 also allows 12-bit. Chroma subsampling is not a
// gate: the color config already fixed it from the profile, and every
// plane geometry below is parameterised by it.
let profile_ok = if sequence.profile == 0 || sequence.profile == 1 {
sequence.bit_depth == 8 || sequence.bit_depth == 10
} else {
sequence.profile == 2
}
if sequence.width <= 0 ||
sequence.width > 65536 ||
sequence.height <= 0 ||
sequence.height > 65536 ||
sequence.width > MAX_IMAGE_PIXELS / sequence.height ||
!profile_ok ||
(sequence.bit_depth != 8 && !allow_highbd) ||
(sequence.monochrome && !allow_monochrome) {
return None
}
if !sequence.reduced_still_picture_header && map is None {
return None
}
let r : Av1FrameBits = { data: frame_payload, bit_pos: 0, }
let prefix = match
av1_parse_frame_prefix(r, sequence, map, temporal_id~, spatial_id~) {
Some(value) => value
None => return None
}
if prefix.show_existing_frame {
// A buffered frame carries no tile data: the caller resolves the envelope
// against the reference buffer instead of decoding a second time.
return None
}
let disable_cdf_update = prefix.disable_cdf_update
let allow_tools = prefix.allow_screen_content_tools != 0
let superres_denom = prefix.superres_denom
let upscaled_width = prefix.upscaled_width
let frame_width = prefix.frame_width
let frame_height = prefix.frame_height
let mi_cols = 2 * ((frame_width + 7) / 8)
let mi_rows = 2 * ((frame_height + 7) / 8)
let sb_shift = if sequence.use_128x128_superblock { 5 } else { 4 }
let sb_cols = (mi_cols + (1 << sb_shift) - 1) >> sb_shift
let sb_rows = (mi_rows + (1 << sb_shift) - 1) >> sb_shift
if sb_cols <= 0 || sb_rows <= 0 {
return None
}
let uniform = match r.take(1) {
Some(v) => v == 1
None => return None
}
// AV1 §5.9.15 tile_info(). The limits are expressed in superblocks and
// match the profile-independent maxima from §3.
let sb_size = sb_shift + 2
let max_tile_width_sb = 4096 >> sb_size
let max_tile_area_sb = (4096 * 2304) >> (2 * sb_size)
let min_log2_tile_cols = av1_tile_log2(max_tile_width_sb, sb_cols)
let bounded_cols = if sb_cols < 64 { sb_cols } else { 64 }
let bounded_rows = if sb_rows < 64 { sb_rows } else { 64 }
let max_log2_tile_cols = av1_tile_log2(1, bounded_cols)
let max_log2_tile_rows = av1_tile_log2(1, bounded_rows)
let total_sb = sb_cols * sb_rows
let min_log2_tiles = {
let area_log2 = av1_tile_log2(max_tile_area_sb, total_sb)
if min_log2_tile_cols > area_log2 {
min_log2_tile_cols
} else {
area_log2
}
}
if min_log2_tile_cols > max_log2_tile_cols {
return None
}
let mut tile_cols_log2 = 0
let mut tile_rows_log2 = 0
let mut tile_cols = 0
let mut tile_rows = 0
let tile_col_starts_sb : Array[Int] = []
let tile_row_starts_sb : Array[Int] = []
if uniform {
tile_cols_log2 = min_log2_tile_cols
while tile_cols_log2 < max_log2_tile_cols {
let more = match r.take(1) {
Some(v) => v == 1
None => return None
}
if !more {
break
}
tile_cols_log2 = tile_cols_log2 + 1
}
let min_log2_tile_rows = {
let value = min_log2_tiles - tile_cols_log2
if value > 0 {
value
} else {
0
}
}
if min_log2_tile_rows > max_log2_tile_rows {
return None
}
tile_rows_log2 = min_log2_tile_rows
while tile_rows_log2 < max_log2_tile_rows {
let more = match r.take(1) {
Some(v) => v == 1
None => return None
}
if !more {
break
}
tile_rows_log2 = tile_rows_log2 + 1
}
let tile_width_sb = (sb_cols + (1 << tile_cols_log2) - 1) >> tile_cols_log2
let tile_height_sb = (sb_rows + (1 << tile_rows_log2) - 1) >> tile_rows_log2
let mut start_sb = 0
while start_sb < sb_cols {
tile_col_starts_sb.push(start_sb)
start_sb = start_sb + tile_width_sb
}
tile_col_starts_sb.push(sb_cols)
tile_cols = tile_col_starts_sb.length() - 1
start_sb = 0
while start_sb < sb_rows {
tile_row_starts_sb.push(start_sb)
start_sb = start_sb + tile_height_sb
}
tile_row_starts_sb.push(sb_rows)
tile_rows = tile_row_starts_sb.length() - 1
} else {
// Non-uniform spacing codes each tile width with ns(max_width)+1. The
// widest column bounds the legal tile-area height, as in tile_info().
let mut widest_tile_sb = 0
let mut start_sb = 0
while start_sb < sb_cols {
tile_col_starts_sb.push(start_sb)
let remaining = sb_cols - start_sb
let max_width = if remaining < max_tile_width_sb {
remaining
} else {
max_tile_width_sb
}
let width = match r.ns(max_width) {
Some(v) => v + 1
None => return None
}
if width <= 0 || width > max_width {
return None
}
if width > widest_tile_sb {
widest_tile_sb = width
}
start_sb = start_sb + width
}
tile_col_starts_sb.push(sb_cols)
tile_cols = tile_col_starts_sb.length() - 1
tile_cols_log2 = av1_tile_log2(1, tile_cols)
let mut max_area_sb = if min_log2_tiles > 0 {
total_sb >> (min_log2_tiles + 1)
} else {
total_sb
}
if max_area_sb < 1 {
max_area_sb = 1
}
let mut max_height_sb = max_area_sb /
(if widest_tile_sb > 0 { widest_tile_sb } else { 1 })
if max_height_sb < 1 {
max_height_sb = 1
}
start_sb = 0
while start_sb < sb_rows {
tile_row_starts_sb.push(start_sb)
let remaining = sb_rows - start_sb
let max_height = if remaining < max_height_sb {
remaining
} else {
max_height_sb
}
let height = match r.ns(max_height) {
Some(v) => v + 1
None => return None
}
if height <= 0 || height > max_height {
return None
}
start_sb = start_sb + height
}
tile_row_starts_sb.push(sb_rows)
tile_rows = tile_row_starts_sb.length() - 1
tile_rows_log2 = av1_tile_log2(1, tile_rows)
}
if tile_cols <= 0 || tile_rows <= 0 || tile_cols > 64 || tile_rows > 64 {
return None
}
let mut tile_size_bytes = 0
let mut context_update_tile_id = 0
if tile_cols_log2 > 0 || tile_rows_log2 > 0 {
context_update_tile_id = match r.take(tile_cols_log2 + tile_rows_log2) {
Some(value) => value
None => return None
}
if context_update_tile_id >= tile_cols * tile_rows {
return None
}
tile_size_bytes = match r.take(2) {
Some(v) => v + 1
None => return None
}
}
let base_q_idx = match r.take(8) {
Some(v) => v
None => return None
}
// quantization_params(): the deltas feed the per-plane quantizer index, so
// they are kept instead of rejected. `separate_uv_delta_q` codes one
// additional flag and, when set, an independent V delta pair (AV1 5.9.14).
let mut delta_y = 0
let mut delta_u = 0
let mut delta_ua = 0
let mut delta_vd = 0
let mut delta_va = 0
if (match r.take(1) {
Some(v) => v == 1
None => return None
}) {
delta_y = match r.signed(7) {
Some(v) => v
None => return None
}
}
if !sequence.monochrome {
let mut diff_uv_delta = false
if sequence.separate_uv_delta_q {
diff_uv_delta = match r.take(1) {
Some(v) => v == 1
None => return None
}
}
if (match r.take(1) {
Some(v) => v == 1
None => return None
}) {
delta_u = match r.signed(7) {
Some(v) => v
None => return None
}
}
if (match r.take(1) {
Some(v) => v == 1
None => return None
}) {
delta_ua = match r.signed(7) {
Some(v) => v
None => return None
}
}
// Without separate deltas V simply reuses the U pair.
if diff_uv_delta {
if (match r.take(1) {
Some(v) => v == 1
None => return None
}) {
delta_vd = match r.signed(7) {
Some(v) => v
None => return None
}
}
if (match r.take(1) {
Some(v) => v == 1
None => return None
}) {
delta_va = match r.signed(7) {
Some(v) => v
None => return None
}
}
} else {
delta_vd = delta_u
delta_va = delta_ua
}
}
let quant_deltas : Av1QuantDeltas = {
y_dc: delta_y,
u_dc: delta_u,
u_ac: delta_ua,
v_dc: delta_vd,
v_ac: delta_va,
}
let using_qmatrix = match r.take(1) {
Some(v) => v == 1
None => return None
}
let quant_matrix = if using_qmatrix {
// qm_y and qm_u are present even for monochrome; qm_v is conditional.
let y = match r.take(4) {
Some(v) => v
None => return None
}
let u = match r.take(4) {
Some(v) => v
None => return None
}
let v = if sequence.separate_uv_delta_q {
match r.take(4) {
Some(v) => v
None => return None
}
} else {
u
}
Some(Av1QuantMatrix::{ y, u, v, })
} else {
None
}
let segmentation = match
av1_parse_segmentation_params(
r,
prefix.primary_ref_frame,
av1_segmentation_loop_filter_limit(sequence),
inherited=av1_segmentation_primary(
map,
prefix.primary_ref_frame,
prefix.ref_frame_idx,
),
) {
Some(value) => value
None => return None
}
// A frame that writes the segment map applies each feature per block. What
// this decoder can decode is narrow and deliberate: the map must be coded in
// the frame itself (no temporal prediction, no inherited feature data, so
// nothing has to be carried across frames), and the only feature it may
// enable is ALT_Q, which shifts the block's quantizer index and is applied
// where the residual reads it. Every other level changes the loop filter,
// the block's reference frame or its prediction mode, and a frame that maps
// no block at all still inherits per-block features - all refused whole
// rather than decoded with the segmentation silently dropped.
if segmentation.enabled && !av1_segmentation_supported(segmentation) {
return None
}
// `delta_q_params` (AV1 §6.10.10). The per-superblock quantizer delta is
// implemented: the tile reads one symbol at each superblock origin and the
// residual dequantises with that superblock's index. The per-superblock
// loop-filter deltas are not, so a frame that signals them is refused whole
// after their bits are consumed - the entropy stream stays in sync for the
// next frame either way.
let mut delta_q_present = false
let mut delta_q_res = 0
let mut delta_lf_present = false
let mut delta_lf_res = 0
let mut delta_lf_multi = false
if base_q_idx > 0 {
delta_q_present = match r.take(1) {
Some(v) => v == 1
None => return None
}
if delta_q_present {
delta_q_res = match r.take(2) {
Some(v) => v
None => return None
}
if !prefix.allow_intrabc {
delta_lf_present = match r.take(1) {
Some(v) => v == 1
None => return None
}
}
if delta_lf_present {
// Actual delta values occur in the tile entropy stream, not this header.
delta_lf_res = match r.take(2) {
Some(v) => v
None => return None
}
delta_lf_multi = match r.take(1) {
Some(v) => v == 1
None => return None
}
}
}
}
// CodedLossless is the AND of all eight LosslessArray entries. Segment
// ALT_Q participates; superblock delta-Q does not (AV1 5.9.14).
let coded_lossless = av1_frame_coded_lossless(
base_q_idx, quant_deltas, segmentation,
)
let loop_filter = if coded_lossless || prefix.allow_intrabc {
None
} else {
match
av1_parse_loop_filter_config(
r,
sequence.monochrome,
ref_deltas=prefix.loop_filter_ref_deltas,
mode_deltas=prefix.loop_filter_mode_deltas,
) {
Some(config) => Some(config)
None => return None
}
}
let cdef = if !coded_lossless && !prefix.allow_intrabc && sequence.enable_cdef {
match av1_parse_cdef_config(r, sequence.monochrome) {
Some(config) => Some(config)
None => return None
}
} else {
None
}
// Superres changes samples even when every transform is lossless. Such a
// frame still carries restoration syntax, unlike an all-lossless frame.
let all_lossless = coded_lossless && superres_denom == 8
let restoration : Av1RestorationConfig? = if !all_lossless &&
!prefix.allow_intrabc &&
sequence.enable_restoration {
match
av1_parse_restoration_config(
r,
sequence.monochrome,
sequence.use_128x128_superblock,
sequence.subsampling_x,
sequence.subsampling_y,
) {
Some(config) => Some(config)
None => return None
}
} else {
None
}
let tx_mode_select = if coded_lossless {
false
} else {
match r.take(1) {
Some(value) => value == 1
None => return None
}
}
// reference_select (AV1 5.9.21) only exists for a frame with references, and
// it gates skip_mode, which is the only way a whole frame can be coded
// without per-block motion syntax.
let reference_select = if prefix.frame_is_intra {
false
} else {
match r.take(1) {
Some(value) => value == 1
None => return None
}
}
let (skip_mode_present, skip_mode_frame) = match
av1_frame_skip_mode(r, prefix, sequence, reference_select) {
Some(value) => value
None => return None
}
let allow_warped_motion = if prefix.frame_is_intra ||
prefix.error_resilient ||
!sequence.enable_warped_motion {
false
} else {
match r.take(1) {
Some(value) => value == 1
None => return None
}
}
// This flag is present for lossless frames too, after the implicit TX mode.
let reduced_tx_set = match r.take(1) {
Some(value) => value == 1
None => return None
}
let (gm_type, gm_params) = match av1_frame_global_motion(r, prefix) {
Some(value) => value
None => return None
}
// A hidden frame that cannot be shown has no film-grain syntax.
let film_grain : Av1FilmGrainParams? = if sequence.film_grain_params_present &&
(prefix.show_frame || prefix.showable_frame) {
match
av1_parse_film_grain_params(
r,
sequence.monochrome,
sequence.subsampling_x,
sequence.subsampling_y,
frame_type=prefix.frame_type,
map~,
ref_frame_idx=prefix.ref_frame_idx,
) {
Some(params) => Some(params)
None => return None
}
} else {
None
}
let (offset, tile_payload) = if frame_header_obu {
if !av1_obu_trailing_bits(frame_payload, r.bit_pos) {
return None
}
let payload = match
av1_assemble_tile_groups(
tile_groups,
tile_cols,
tile_rows,
tile_cols_log2 + tile_rows_log2,
tile_size_bytes,
) {
Some(value) => value
None => return None
}
tile_size_bytes = if tile_cols * tile_rows > 1 { 4 } else { 0 }
(frame_payload.length(), payload)
} else {
match
av1_frame_tile_group_payload(
frame_payload,
r.bit_pos,
tile_cols,
tile_rows,
) {
Some(value) => value
None => return None
}
}
Some({
frame_width,
upscaled_width,
frame_height,
superres_denom,
header_bytes: offset,
tile_payload,
base_q_idx,
quant_matrix,
delta_lf_res,
delta_lf_multi,
tile_cols,
tile_rows,
tile_cols_log2,
tile_rows_log2,
tile_col_starts_sb,
tile_row_starts_sb,
tile_size_bytes,
context_update_tile_id,
tx_mode_select,
reduced_tx_set,
coded_lossless,
cdef,
loop_filter,
restoration,
disable_cdf_update,
allow_screen_content_tools: allow_tools,
film_grain,
quant_deltas,
delta_q_present,
delta_q_res,
delta_lf_present,
segmentation,
frame_type: prefix.frame_type,
frame_is_intra: prefix.frame_is_intra,
show_frame: prefix.show_frame,
show_existing_frame: prefix.show_existing_frame,
frame_to_show_map_idx: prefix.frame_to_show_map_idx,
error_resilient: prefix.error_resilient,
showable_frame: prefix.showable_frame,
disable_frame_end_update_cdf: prefix.disable_frame_end_update_cdf,
force_integer_mv: prefix.force_integer_mv,
current_frame_id: prefix.current_frame_id,
order_hint: prefix.order_hint,
primary_ref_frame: prefix.primary_ref_frame,
refresh_frame_flags: prefix.refresh_frame_flags,
ref_frame_idx: prefix.ref_frame_idx,
order_hints: prefix.order_hints,
ref_frame_sign_bias: prefix.ref_frame_sign_bias,
allow_high_precision_mv: prefix.allow_high_precision_mv,
interpolation_filter: prefix.interpolation_filter,
is_motion_mode_switchable: prefix.is_motion_mode_switchable,
use_ref_frame_mvs: prefix.use_ref_frame_mvs,
allow_warped_motion,
reference_select,
skip_mode_present,
skip_mode_frame,
gm_type,
gm_params,
prev_gm_params: prefix.prev_gm_params,
loop_filter_ref_deltas: prefix.loop_filter_ref_deltas,
loop_filter_mode_deltas: prefix.loop_filter_mode_deltas,
allow_intrabc: prefix.allow_intrabc,
render_width: prefix.render_width,
render_height: prefix.render_height,
})
}
///|
/// `Segmentation_Feature_Bits`, `Segmentation_Feature_Signed` and
/// `Segmentation_Feature_Max` (AV1 §6.10.9): the eight segment levels.
let av1_segmentation_feature_bits : Array[Int] = [8, 6, 6, 6, 6, 3, 0, 0]
///|
let av1_segmentation_feature_signed : Array[Int] = [1, 1, 1, 1, 1, 0, 0, 0]
///|
/// `MAX_LOOP_FILTER` is 63 for eight-bit streams and 255 when the frame is
/// high bit depth (AV1 §6.10.9 note); the caller passes its own limit.
fn av1_parse_segmentation_params(
r : Av1FrameBits,
primary_ref_frame : Int,
max_loop_filter : Int,
inherited? : Av1SegmentationParams? = None,
) -> Av1SegmentationParams? {
let enabled = match r.take(1) {
Some(v) => v == 1
None => return None
}
if !enabled {
return Some(av1_segmentation_empty())
}
let mut update_map = true
let mut temporal_update = false
let mut update_data = true
if primary_ref_frame != av1_primary_ref_none {
update_map = match r.take(1) {
Some(v) => v == 1
None => return None
}
if update_map {
temporal_update = match r.take(1) {
Some(v) => v == 1
None => return None
}
}
update_data = match r.take(1) {
Some(v) => v == 1
None => return None
}
}
let feature_enabled = Array::makei(av1_segments, _ => {
Array::make(av1_segment_levels, false)
})
let feature_data = Array::makei(av1_segments, _ => {
Array::make(av1_segment_levels, 0)
})
if update_data {
let limit = [
255, max_loop_filter, max_loop_filter, max_loop_filter, max_loop_filter, 7,
0, 0,
]
for i in 0.. v == 1
None => return None
}
feature_enabled[i][j] = on
if !on {
continue
}
let bits = av1_segmentation_feature_bits[j]
if bits == 0 {
continue
}
let signed = av1_segmentation_feature_signed[j] == 1
let value = if signed {
match Av1FrameBits::signed(r, bits + 1) {
Some(v) => v
None => return None
}
} else {
match r.take(bits) {
Some(v) => v
None => return None
}
}
let bottom = if signed { -limit[j] } else { 0 }
feature_data[i][j] = if value < bottom {
bottom
} else if value > limit[j] {
limit[j]
} else {
value
}
}
}
}
if !update_data {
let previous = match inherited {
Some(value) => value
None => av1_segmentation_empty()
}
for i in 0..= av1_seg_level_ref_frame {
seg_id_pre_skip = true
}
}
}
}
Some({
enabled,
update_map,
temporal_update,
update_data,
feature_enabled,
feature_data,
seg_id_pre_skip,
last_active_seg_id: last_active,
})
}
///|
/// Validate all eight segment feature ranges. Quantization, four loop-filter
/// deltas, forced references, skip and global motion each have tile consumers.
pub fn av1_segmentation_features_supported(
segmentation : Av1SegmentationParams,
) -> Bool {
if segmentation.feature_enabled.length() != 8 ||
segmentation.feature_data.length() != 8 {
return false
}
for i in 0.. max {
return false
}
}
}
true
}
///|
/// Frame parsing resolves inherited feature tables from the primary reference;
/// tile decoding resolves unchanged or temporally predicted segment maps from
/// the saved frame state. This check validates the resulting feature data.
pub fn av1_segmentation_supported(segmentation : Av1SegmentationParams) -> Bool {
if !segmentation.enabled {
return true
}
av1_segmentation_features_supported(segmentation)
}
///|
fn av1_segmentation_empty() -> Av1SegmentationParams {
{
enabled: false,
update_map: false,
temporal_update: false,
update_data: false,
feature_enabled: Array::makei(av1_segments, _ => {
Array::make(av1_segment_levels, false)
}),
feature_data: Array::makei(av1_segments, _ => {
Array::make(av1_segment_levels, 0)
}),
seg_id_pre_skip: false,
last_active_seg_id: -1,
}
}
///|
/// Apply SEG_LVL_ALT_Q to a supplied base index (AV1 get_qindex). Callers
/// supply the frame base for syntax, or the current SB index for dequant.
fn av1_segment_qindex(
base_q_idx : Int,
segmentation : Av1SegmentationParams,
segment_id : Int,
) -> Int {
if !segmentation.enabled ||
segment_id < 0 ||
segment_id >= av1_segments ||
!segmentation.feature_enabled[segment_id][av1_seg_level_alt_q] {
return base_q_idx
}
let value = base_q_idx +
segmentation.feature_data[segment_id][av1_seg_level_alt_q]
if value < 0 {
0
} else if value > 255 {
255
} else {
value
}
}
///|
fn av1_frame_coded_lossless(
base_q_idx : Int,
quant_deltas : Av1QuantDeltas,
segmentation : Av1SegmentationParams,
) -> Bool {
if !av1_quant_deltas_lossless(quant_deltas) {
return false
}
for segment_id in 0.. Array[Av1TilePayload]? {
av1_extract_tile_payloads(
frame.tile_payload,
frame.tile_cols,
frame.tile_rows,
frame.tile_size_bytes,
)
}