/// AV1 reference-frame management: the eight decoded-frame slots, the seven
/// named references that index them, and the per-slot header state that
/// `frame_size_with_refs`, `load_previous` and skip-mode derivation inherit
/// (AV1 §7.20, §7.21).
///
/// Pixels are held at native bit depth in the same `Av1FramePlanes` layout the
/// in-loop filters produce, after superres upscaling, because motion vectors
/// address the upscaled reference.
///|
/// AV1 §3, §6.4.2 and §6.10.24 symbols.
let av1_refs_per_frame : Int = 7
///|
let av1_total_refs_per_frame : Int = 8
///|
let av1_num_ref_frames : Int = 8
///|
let av1_primary_ref_none : Int = 7
///|
let av1_all_ref_frames_mask : Int = 0xff
///|
let av1_none_frame : Int = -1
///|
let av1_intra_frame : Int = 0
///|
let av1_last_frame : Int = 1
///|
let av1_last2_frame : Int = 2
///|
let av1_last3_frame : Int = 3
///|
let av1_golden_frame : Int = 4
///|
let av1_bwd_ref_frame : Int = 5
///|
let av1_alt_ref2_frame : Int = 6
///|
let av1_alt_ref_frame : Int = 7
///|
let av1_key_frame : Int = 0
///|
let av1_inter_frame : Int = 1
///|
let av1_intra_only_frame : Int = 2
///|
let av1_switch_frame : Int = 3
///|
let av1_gm_identity : Int = 0
///|
let av1_gm_translation : Int = 1
///|
let av1_gm_rot_zoom : Int = 2
///|
let av1_gm_affine : Int = 3
///|
let av1_warped_model_prec_bits : Int = 16
///|
/// The identity warp model from AV1 §7.20: the two diagonal coefficients carry
/// the `1 << WARPEDMODEL_PREC_BITS` bias every consumer subtracts.
fn av1_identity_gm_params() -> Array[Int] {
[0, 0, 1 << av1_warped_model_prec_bits, 0, 0, 1 << av1_warped_model_prec_bits]
}
///|
/// reset_grain_params for a reference whose frame carried no grain syntax.
fn av1_frame_grain_empty() -> Av1FilmGrainParams {
{
apply_grain: false,
grain_seed: 0,
num_y_points: 0,
point_y_value: [],
point_y_scaling: [],
chroma_scaling_from_luma: false,
num_cb_points: 0,
point_cb_value: [],
point_cb_scaling: [],
num_cr_points: 0,
point_cr_value: [],
point_cr_scaling: [],
grain_scaling_minus8: 0,
ar_coeff_lag: 0,
ar_coeffs_y: [],
ar_coeffs_cb: [],
ar_coeffs_cr: [],
ar_coeff_shift_minus6: 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,
}
}
///|
/// Reference snapshots and a frame inheriting them own their array storage.
fn av1_frame_grain_copy(params : Av1FilmGrainParams) -> Av1FilmGrainParams {
{
..params,
point_y_value: params.point_y_value.copy(),
point_y_scaling: params.point_y_scaling.copy(),
point_cb_value: params.point_cb_value.copy(),
point_cb_scaling: params.point_cb_scaling.copy(),
point_cr_value: params.point_cr_value.copy(),
point_cr_scaling: params.point_cr_scaling.copy(),
ar_coeffs_y: params.ar_coeffs_y.copy(),
ar_coeffs_cb: params.ar_coeffs_cb.copy(),
ar_coeffs_cr: params.ar_coeffs_cr.copy(),
}
}
///|
/// One decoded picture in the reference buffer. Geometry is flattened out of
/// the private `Av1FramePlanes` layout so this stays a describable public type.
pub(all) struct Av1RefFrame {
/// Native bit-depth samples in the MI-padded coding layout; the stride of
/// plane zero is `coded_width`, chroma strides follow from `sub_x`.
planes : Array[Array[Int]]
coded_width : Int
coded_height : Int
/// Visible extent. Motion vectors address `width`/`height`, which are the
/// post-superres dimensions (AV1 §7.18).
width : Int
height : Int
sub_x : Int
sub_y : Int
bit_depth : Int
/// Entropy-coded width before superres, needed by the reference-scaling and
/// `frame_size_with_refs` derivations.
frame_width : Int
upscaled_width : Int
frame_height : Int
render_width : Int
render_height : Int
order_hint : Int
frame_type : Int
frame_id : Int
/// Per-reference-list order hints inherited by the next frame (§5.9.3).
order_hints : Array[Int]
gm_params : Array[Array[Int]]
loop_filter_ref_deltas : Array[Int]
loop_filter_mode_deltas : Array[Int]
/// The entropy context stored with this picture (AV1 §7.20 `save_cdfs`).
/// A disabled frame-end update stores this frame's initial context, including
/// its quantizer-dependent defaults. Decoded frames always store a snapshot.
cdfs : Av1FrameCdfs?
/// `MfRefFrames`/`MfMvs` from the motion-field storage process (§7.19), at
/// MI granularity with two motion vectors per unit. Empty for a frame that
/// never went through the inter block stage, which is what makes a key frame
/// ineligible as a temporal-candidate source.
mf_refs : Array[Int]
mf_mvs : Array[Int]
mi_cols : Int
mi_rows : Int
/// `SavedOrderHints`, the per-reference-name order hints this frame carried.
/// The temporal projection needs them to measure how far a vector saved by an
/// older frame has to travel (AV1 §7.9.2 `refOffset`).
saved_order_hints : Array[Int]
}
///|
/// The eight-slot reference buffer plus the derived slot metadata the frame
/// header reads before any pixels exist.
pub(all) struct Av1FrameMap {
slots : Array[Av1RefFrame?]
/// `ref_frame_idx` projections and presence, indexed by slot.
valid : Array[Bool]
order_hints : Array[Int]
frame_types : Array[Int]
frame_ids : Array[Int]
upscaled_widths : Array[Int]
frame_widths : Array[Int]
frame_heights : Array[Int]
render_widths : Array[Int]
render_heights : Array[Int]
saved_gm_params : Array[Array[Array[Int]]]
saved_loop_filter_ref_deltas : Array[Array[Int]]
saved_loop_filter_mode_deltas : Array[Array[Int]]
saved_segmentation : Array[Av1SegmentationParams]
saved_segment_ids : Array[Array[Int]]
/// Film-grain syntax is saved separately from the grain-free native pixels.
saved_film_grain : Array[Av1FilmGrainParams?]
saved_showable : Array[Bool]
mut current_frame_id : Int
mut has_frame_id : Bool
/// The most recently stored frame context, retained for inspection. Inheritance
/// resolves the selected reference slot rather than this latest snapshot.
/// `None` means no frame has been stored yet.
mut saved_cdfs : Av1FrameCdfs?
}
///|
/// An empty reference buffer. Every slot starts invalid with identity motion
/// and neutral loop-filter deltas, matching `setup_past_independence`.
pub fn av1_frame_map() -> Av1FrameMap {
{
slots: Array::make(av1_num_ref_frames, None),
valid: Array::make(av1_num_ref_frames, false),
order_hints: Array::make(av1_num_ref_frames, 0),
frame_types: Array::make(av1_num_ref_frames, av1_key_frame),
frame_ids: Array::make(av1_num_ref_frames, 0),
upscaled_widths: Array::make(av1_num_ref_frames, 0),
frame_widths: Array::make(av1_num_ref_frames, 0),
frame_heights: Array::make(av1_num_ref_frames, 0),
render_widths: Array::make(av1_num_ref_frames, 0),
render_heights: Array::make(av1_num_ref_frames, 0),
saved_gm_params: Array::make(
av1_num_ref_frames,
Array::make(av1_total_refs_per_frame, av1_identity_gm_params()),
),
saved_loop_filter_ref_deltas: Array::make(
av1_num_ref_frames,
av1_default_loop_filter_ref_deltas(),
),
saved_loop_filter_mode_deltas: Array::make(av1_num_ref_frames, [0, 0]),
saved_segmentation: Array::makei(av1_num_ref_frames, _ => {
av1_segmentation_empty()
}),
saved_segment_ids: Array::makei(av1_num_ref_frames, _ => []),
saved_film_grain: Array::make(av1_num_ref_frames, None),
saved_showable: Array::make(av1_num_ref_frames, false),
current_frame_id: 0,
has_frame_id: false,
saved_cdfs: None,
}
}
///|
/// The §7.20 default reference loop-filter deltas, indexed by reference name.
fn av1_default_loop_filter_ref_deltas() -> Array[Int] {
[1, 0, 0, 0, -1, 0, -1, -1]
}
///|
/// Invalidates every slot. A showing key frame must not leave stale references
/// behind (AV1 §7.20).
fn av1_frame_map_invalidate(map : Av1FrameMap) -> Unit {
map.has_frame_id = false
for i in 0.. (Int, Int)? {
let diff = sequence.delta_frame_id_length_minus_2 + 2
let total = sequence.additional_frame_id_length_minus_1 + diff + 1
if diff < 2 || total <= diff || total > 16 {
return None
}
Some((diff, total))
}
///|
/// mark_ref_frames: keep age zero and age 2^diffLen, expire only larger ages.
/// Invalid slots remain invalid even when their saved numeric ID is in range.
fn av1_frame_mark_refs(
map : Av1FrameMap,
current : Int,
diff_len : Int,
id_len : Int,
) -> Unit {
let modulus = 1 << id_len
let window = 1 << diff_len
for slot in 0.. window {
map.valid[slot] = false
}
}
}
///|
/// The signed circular distance between two order hints (AV1 §5.9.3). Zero is
/// returned when the sequence disables order hints, which makes every sign bias
/// forward.
pub fn av1_relative_dist(sequence : Av1SequenceInfo, a : Int, b : Int) -> Int {
if !sequence.enable_order_hint {
return 0
}
let diff = a - b
let m = 1 << (sequence.order_hint_bits - 1)
(diff & (m - 1)) - (diff & m)
}
///|
/// Derives the seven reference indices from `last_frame_idx` and
/// `gold_frame_idx` when `frame_refs_short_signaling` is set (AV1 §6.10.24).
/// Slots are ranked by their projected distance from the current frame,
/// backwards references first for the alternate references, then forwards for
/// the remaining golden-tier names.
fn av1_set_frame_refs(
sequence : Av1SequenceInfo,
map : Av1FrameMap,
order_hint : Int,
last_frame_idx : Int,
gold_frame_idx : Int,
ref_frame_idx : Array[Int],
) -> Unit {
for i in 0.. {
cur + av1_relative_dist(sequence, map.order_hints[i], order_hint)
})
// Picks the latest (earliest) slot on the requested side of the current
// frame, skipping slots already bound to a named reference.
let find = fn(backward : Bool, earliest : Bool) -> Int {
let mut picked = -1
let mut best = 0
for i in 0..= cur {
continue
}
if picked < 0 || (earliest && hint < best) || (!earliest && hint >= best) {
picked = i
best = hint
}
}
picked
}
let alt_ref = find(true, false)
if alt_ref >= 0 {
ref_frame_idx[av1_alt_ref_frame - av1_last_frame] = alt_ref
used[alt_ref] = true
}
// The two remaining backward slots take the earliest leftovers in order.
for name in [av1_bwd_ref_frame, av1_alt_ref2_frame] {
let picked = find(true, true)
if picked >= 0 {
ref_frame_idx[name - av1_last_frame] = picked
used[picked] = true
}
}
for
name in [
av1_last2_frame, av1_last3_frame, av1_bwd_ref_frame, av1_alt_ref2_frame, av1_alt_ref_frame,
] {
if ref_frame_idx[name - av1_last_frame] < 0 {
let picked = find(false, false)
if picked >= 0 {
ref_frame_idx[name - av1_last_frame] = picked
used[picked] = true
}
}
}
// Any name still unbound falls back to the earliest slot overall.
let mut fallback = -1
let mut earliest = 0
for i in 0.. Av1FrameCdfs {
if av1_cdf_load_enabled &&
header.primary_ref_frame >= 0 &&
header.primary_ref_frame < av1_primary_ref_none &&
header.primary_ref_frame < header.ref_frame_idx.length() {
let slot = header.ref_frame_idx[header.primary_ref_frame]
if slot >= 0 && slot < av1_num_ref_frames && map.valid[slot] {
match map.slots[slot] {
Some(stored) =>
match stored.cdfs {
Some(snapshot) => return av1_frame_cdfs_copy(snapshot)
None => ()
}
None => ()
}
}
}
av1_frame_cdfs_default(header.base_q_idx)
}
///|
/// `update_reference_map` (AV1 §7.20). Every slot named by
/// `refresh_frame_flags` starts sharing this picture, its header state and the
/// parameters the next frame may inherit through `load_previous`.
///
/// All refreshed slots deliberately alias one `Av1RefFrame`: a later frame may
/// use any of them as its primary reference and must see identical state.
///
/// `cdfs` is the context_update_tile_id tile's final entropy state. An enabled
/// frame end stores those adapted rows; a disabled one stores the context this
/// frame started from, selected by primary_ref_frame or initialized using this
/// frame's base quantizer. It must not inherit an unrelated, more recent frame
/// (AV1 §8.3 `frame_end_update_cdf`, §7.20 `save_cdfs`).
fn av1_frame_map_update(
map : Av1FrameMap,
header : Av1FrameHeaderInfo,
frame : Av1FramePlanes,
sequence : Av1SequenceInfo,
cdfs? : Av1FrameCdfs? = None,
field? : Av1MotionField? = None,
segment_ids? : Array[Int] = [],
) -> Unit {
// Motion field motion vector storage (§7.19). Runs over the inter frame's
// motion field before the slots are refreshed, so `map.order_hints` still
// names the frames being referenced rather than this one.
let mi_cols = 2 * ((header.frame_width + 7) / 8)
let mi_rows = 2 * ((header.frame_height + 7) / 8)
let mf_refs : Array[Int] = Array::make(mi_cols * mi_rows, av1_none_frame)
let mf_mvs : Array[Int] = Array::make(mi_cols * mi_rows * 2, 0)
match field {
Some(motion) =>
if motion.cols == mi_cols && motion.rows == mi_rows {
for row in 0.. av1_intra_frame {
// `header.order_hints` is indexed by reference name and already
// projects each name onto its slot's stored hint.
let dist = av1_relative_dist(
sequence,
header.order_hints[name],
header.order_hint,
)
if dist < 0 {
let mv_row = motion.mv_rows[unit * 2 + list]
let mv_col = motion.mv_cols[unit * 2 + list]
if mv_row <= av1_mv_refmvs_limit &&
mv_row >= -av1_mv_refmvs_limit &&
mv_col <= av1_mv_refmvs_limit &&
mv_col >= -av1_mv_refmvs_limit {
mf_refs[unit] = name
mf_mvs[unit * 2] = mv_row
mf_mvs[unit * 2 + 1] = mv_col
}
}
}
}
}
}
}
None => ()
}
let snapshot = if header.disable_frame_end_update_cdf {
av1_frame_initial_cdfs(map, header)
} else {
match cdfs {
Some(adapted) => av1_frame_cdfs_copy(adapted)
None => av1_frame_initial_cdfs(map, header)
}
}
// A reference load restarts the adaptation rate schedule. Copying above
// also leaves the decoded tile's working counters and older slots untouched.
av1_frame_cdfs_reset_counts(snapshot)
let slot_cdfs = Some(snapshot)
map.current_frame_id = header.current_frame_id
map.has_frame_id = sequence.frame_id_numbers_present
map.saved_cdfs = Some(av1_frame_cdfs_copy(snapshot))
let ref_frame : Av1RefFrame = {
planes: frame.planes,
coded_width: frame.coded_width,
coded_height: av1_partition_coded_extent(frame.height),
width: frame.width,
height: frame.height,
sub_x: frame.sub_x,
sub_y: frame.sub_y,
bit_depth: frame.bit_depth,
frame_width: header.frame_width,
upscaled_width: header.upscaled_width,
frame_height: header.frame_height,
render_width: header.render_width,
render_height: header.render_height,
order_hint: header.order_hint,
frame_type: header.frame_type,
frame_id: header.current_frame_id,
order_hints: Array::copy(header.order_hints),
gm_params: header.gm_params,
loop_filter_ref_deltas: Array::copy(header.loop_filter_ref_deltas),
loop_filter_mode_deltas: Array::copy(header.loop_filter_mode_deltas),
cdfs: slot_cdfs,
mf_refs,
mf_mvs,
mi_cols,
mi_rows,
saved_order_hints: Array::copy(header.order_hints),
}
for slot in 0..> slot) & 1) == 0 {
continue
}
map.slots[slot] = Some(ref_frame)
map.valid[slot] = true
map.order_hints[slot] = header.order_hint
map.frame_types[slot] = header.frame_type
map.frame_ids[slot] = header.current_frame_id
map.upscaled_widths[slot] = header.upscaled_width
map.frame_widths[slot] = header.frame_width
map.frame_heights[slot] = header.frame_height
map.saved_segmentation[slot] = av1_segmentation_copy(header.segmentation)
map.saved_segment_ids[slot] = segment_ids.copy()
map.saved_film_grain[slot] = header.film_grain.map(av1_frame_grain_copy)
map.saved_showable[slot] = header.showable_frame
map.render_widths[slot] = header.render_width
map.render_heights[slot] = header.render_height
// §7.21 saves the per-reference warp models and the loop-filter deltas so
// a following frame can inherit them via primary_ref_frame.
map.saved_gm_params[slot] = header.gm_params
map.saved_loop_filter_ref_deltas[slot] = Array::copy(
header.loop_filter_ref_deltas,
)
map.saved_loop_filter_mode_deltas[slot] = Array::copy(
header.loop_filter_mode_deltas,
)
}
}
///|
/// Resolve a show-existing envelope against the buffer. A buffered frame
/// carries no tile data, so this is the whole reconstruction step for it.
pub fn av1_frame_map_show(map : Av1FrameMap, slot : Int) -> Av1RefFrame? {
if slot < 0 || slot >= av1_num_ref_frames || !map.valid[slot] {
return None
}
map.slots[slot]
}