///|
/// Native-depth reconstructed frame. Coded planes retain the MI padding until
/// every tile has been assembled and the frame filters have completed.
priv struct Av1FramePlanes {
width : Int
height : Int
coded_width : Int
bit_depth : Int
full_range : Bool
matrix_coefficients : Int
color_primaries : Int
transfer_characteristics : Int
/// Chroma subsampling shifts (0 or 1) for planes 1 and 2.
sub_x : Int
sub_y : Int
planes : Array[Array[Int]]
}
///|
/// Per-axis chroma divisors for a plane: (1, 1) for luma, 2^sub otherwise.
/// The coded luma extent is always a multiple of eight, so a shift is exact.
fn av1_frame_plane_divisor(frame : Av1FramePlanes, plane : Int) -> (Int, Int) {
if plane == 0 {
(1, 1)
} else {
(1 << frame.sub_x, 1 << frame.sub_y)
}
}
///|
fn av1_frame_crop(frame : Av1FramePlanes, plane : Int) -> Array[Int] {
let (divisor_x, divisor_y) = av1_frame_plane_divisor(frame, plane)
let width = (frame.width + divisor_x - 1) / divisor_x
let height = (frame.height + divisor_y - 1) / divisor_y
let stride = frame.coded_width / divisor_x
Array::makei(width * height, fn(i) {
frame.planes[plane][i / width * stride + i % width]
})
}
///|
fn av1_frame_to_rgba(frame : Av1FramePlanes) -> Image? {
if frame.planes.length() == 1 {
let samples = av1_frame_crop(frame, 0)
let image = Image::new(frame.width, frame.height)
let shift = frame.bit_depth - 8
let black = if frame.full_range { 0 } else { 16 << shift }
let range = if frame.full_range {
(1 << frame.bit_depth) - 1
} else {
219 << shift
}
for i in 0.. Array[Int]? {
if length <= 0 ||
length > MAX_IMAGE_PIXELS ||
(superblock_pixels != 64 && superblock_pixels != 128) ||
starts.length() < 2 ||
starts.length() > 1025 ||
starts[0] != 0 {
return None
}
let count = (length - 1) / superblock_pixels + 1
if starts[starts.length() - 1] != count {
return None
}
let output : Array[Int] = [0]
for i in 1.. count {
return None
}
output.push(
if i == starts.length() - 1 {
length
} else {
starts[i] * superblock_pixels
},
)
}
Some(output)
}
///|
fn av1_frame_pixel_starts_valid(
starts : Array[Int],
length : Int,
frame_filters : Bool,
) -> Bool {
if starts.length() < 2 ||
starts.length() > 1025 ||
starts[0] != 0 ||
starts[starts.length() - 1] != length {
return false
}
for i in 1.. length {
return false
}
if i + 1 < starts.length() &&
(starts[i] % 8 != 0 || (frame_filters && starts[i] % 64 != 0)) {
return false
}
}
true
}
///|
/// Apply film grain to the visible picture after reconstruction.
///
/// Grain synthesis (AV1 §7.18.3) runs on the output picture, so the planes are
/// cropped first. The grained samples are scattered back into the same padded
/// layout, leaving the crop-on-convert path unchanged.
fn av1_frame_apply_grain(
frame : Av1FramePlanes,
params : Av1FilmGrainParams,
monochrome : Bool,
subsampling_x : Bool,
subsampling_y : Bool,
) -> Av1FramePlanes? {
if !params.apply_grain {
return Some(frame)
}
let cropped : Array[Array[Int]] = []
for p in 0.. None
Some(_) => {
let display = {
..frame,
planes: frame.planes.map(samples => samples.copy()),
}
for p in 0.. (Av1FramePlanes, Av1RestorationStripeStore?, Av1FrameCdfs?)? {
let output_width = if upscaled_width == 0 { width } else { upscaled_width }
let sub_x = if subsampling_x { 1 } else { 0 }
let sub_y = if subsampling_y { 1 } else { 0 }
if width <= 0 ||
height <= 0 ||
width > MAX_IMAGE_PIXELS / height ||
output_width < width ||
output_width > 65536 ||
output_width > MAX_IMAGE_PIXELS / height ||
output_width > width * 2 ||
(bit_depth != 8 && bit_depth != 10 && bit_depth != 12) ||
!av1_frame_pixel_starts_valid(
col_starts,
width,
cdef is Some(_) || loop_filter is Some(_) || output_width != width,
) ||
!av1_frame_pixel_starts_valid(
row_starts,
height,
cdef is Some(_) || loop_filter is Some(_) || output_width != width,
) {
return None
}
let coded_lossless = av1_frame_coded_lossless(
base_q_idx,
quant_deltas,
segmentation.unwrap_or(av1_segmentation_empty()),
)
if cdef is Some(config) {
if coded_lossless || !av1_cdef_config_valid(config, monochrome~) {
return None
}
}
if restoration is Some(config) {
if !av1_restoration_config_valid(config, monochrome~) {
return None
}
}
let mut filter_active = false
if loop_filter is Some(config) {
if coded_lossless || !av1_loop_filter_config_valid(config) {
return None
}
if delta_lf_present {
filter_active = true
}
if segmentation is Some(params) {
for id in 0..<8 {
for feature in 1..<5 {
if av1_segment_feature(params, id, feature) {
filter_active = true
}
}
}
}
for plane in 0..<(if monochrome { 1 } else { 3 }) {
for direction in 0..<2 {
// Any reference or mode delta can lift an edge, so the frame is only
// inert when none of the eight strengths selects a non-zero level.
for reference in 0..<8 {
for mode_type in 0..<2 {
let level = match
av1_loop_filter_edge_level(
config, plane, direction, reference, mode_type,
) {
Some(value) => value
None => 0
}
if level > 0 {
filter_active = true
}
}
}
}
}
}
let coded_width = av1_partition_coded_extent(width)
let coded_height = av1_partition_coded_extent(height)
if coded_width > MAX_IMAGE_PIXELS / coded_height {
return None
}
let tiles = match
av1_extract_tile_payloads(
payload,
col_starts.length() - 1,
row_starts.length() - 1,
tile_size_bytes,
) {
Some(value) => value
None => return None
}
if context_update_tile_id < 0 || context_update_tile_id >= tiles.length() {
return None
}
let chroma_coded_w = (coded_width + (1 << sub_x) - 1) >> sub_x
let chroma_coded_h = (coded_height + (1 << sub_y) - 1) >> sub_y
let planes : Array[Array[Int]] = [Array::make(coded_width * coded_height, 0)]
if !monochrome {
planes.push(Array::make(chroma_coded_w * chroma_coded_h, 0))
planes.push(Array::make(chroma_coded_w * chroma_coded_h, 0))
}
let tx_sizes : Array[Array[Int]] = []
if filter_active {
for plane in 0..
av1_restoration_layout(
config,
width,
output_width,
height,
monochrome,
!monochrome && sub_x == 1,
!monochrome && sub_y == 1,
superres_denom,
)
None => None
}
// Per-plane unit map indexed by unit_row * unit_cols + unit_col. None
// marks an unread grid position, distinct from a decoded-None unit.
let lr_units : Array[Array[Av1RestorationUnit?]] = []
match lr_layout {
Some(layout) =>
for plane in 0.. ()
}
for tile in tiles {
let left = col_starts[tile.col]
let top = row_starts[tile.row]
let tile_width = col_starts[tile.col + 1] - left
let tile_height = row_starts[tile.row + 1] - top
let tile_cols4 = av1_partition_coded_extent(tile_width) / 4
let tile_rows4 = av1_partition_coded_extent(tile_height) / 4
let previous_tile = if previous_seg_ids.length() == segment_ids.length() {
Array::makei(tile_cols4 * tile_rows4, i => {
previous_seg_ids[(top / 4 + i / tile_cols4) * mi_cols +
left / 4 +
i % tile_cols4]
})
} else {
[]
}
let tile_inter = match inter {
Some(config) =>
Some(
av1_inter_info(
config,
top / 4,
left / 4,
(top + av1_partition_coded_extent(tile_height)) / 4,
(left + av1_partition_coded_extent(tile_width)) / 4,
cdfs~,
),
)
None => None
}
let state = match
av1_decode_intra_tile_state(
tile_width,
tile_height,
tile.bytes,
base_q_idx,
disable_cdf_update,
allow_screen_content_tools,
use_128x128_superblock,
bit_depth,
reduced_tx_set,
enable_filter_intra,
tx_mode_select,
cdef~,
monochrome~,
enable_intra_edge_filter~,
track_loop_filter=filter_active,
restoration_layout=lr_layout,
tile_origin_row_mi=top / 4,
tile_origin_col_mi=left / 4,
quant_deltas~,
sub_x~,
sub_y~,
inter=tile_inter,
segmentation~,
delta_q_present~,
delta_q_res~,
quant_matrix~,
allow_intrabc~,
frame_mi_cols=mi_cols,
frame_mi_rows=coded_height / 4,
previous_seg_ids=previous_tile,
delta_lf_present~,
delta_lf_res~,
delta_lf_multi~,
// Every tile starts from the same frame context; only the selected
// tile contributes adapted rows to the frame-end snapshot.
frame_cdfs=cdfs,
cdfs_out=if tile.index == context_update_tile_id {
cdfs_out
} else {
None
},
) {
Some(value) => value
None => return None
}
for p in 0.. Some(config.field)
None => None
}
if filter_active &&
!av1_loop_filter_frame(
loop_filter.unwrap(),
width,
height,
coded_width,
coded_height,
bit_depth,
planes,
tx_sizes,
sub_x~,
sub_y~,
field~,
block_map=Some({
cols: mi_cols,
rows: coded_height / 4,
segment_ids,
deltas: lf_deltas,
segmentation: segmentation.unwrap_or(av1_segmentation_empty()),
}),
) {
return None
}
// Save the deblocked rows at every internal stripe boundary before
// CDEF modifies them: loop restoration reads these rows instead of
// the neighbouring stripe's post-CDEF output. Only active when some
// plane carries a non-None restoration mode; a failed save aborts the
// decode because the boundary context would be lost.
let stripe_store : Av1RestorationStripeStore? = match restoration {
Some(config) =>
if av1_restoration_active(config) {
match
av1_restoration_save_stripes(
planes,
coded_width,
width,
output_width,
height,
bit_depth,
monochrome,
sub_x~,
sub_y~,
) {
Some(value) => Some(value)
None => return None
}
} else {
None
}
None => None
}
let filtered = match cdef {
None => planes
Some(config) =>
match
av1_cdef_filter_frame(
coded_width,
coded_height,
planes,
skips,
indices,
config,
bit_depth,
sub_x~,
sub_y~,
) {
Some(value) => value
None => return None
}
}
let base : Av1FramePlanes = {
width,
height,
coded_width,
bit_depth,
full_range,
matrix_coefficients,
color_primaries,
transfer_characteristics,
sub_x,
sub_y,
planes: filtered,
}
let frame = if output_width == width {
base
} else {
match av1_frame_upscale(base, output_width) {
Some(value) => value
None => return None
}
}
// Apply loop restoration on the upscaled post-CDEF frame. The source
// view keeps the saved deblocked rows and the unit parameters come
// from the per-tile entropy state; an active configuration always
// has a saved store, and filtering failure aborts the decode.
let frame = match (stripe_store, lr_layout) {
(Some(store), Some(layout)) => {
let strides : Array[Int] = Array::makei(frame.planes.length(), fn(i) {
if i == 0 {
frame.coded_width
} else {
frame.coded_width >> frame.sub_x
}
})
let view : Av1RestorationSourceView = {
store,
frame: frame.planes,
frame_strides: strides,
}
match
av1_restoration_filter_frame(
view,
lr_units,
layout,
bit_depth,
monochrome,
subsampling_x=!monochrome && frame.sub_x == 1,
subsampling_y=!monochrome && frame.sub_y == 1,
) {
Some(value) => {
let planes = frame.planes
for plane in 0.. return None
}
}
_ => frame
}
// The frame context after decoding: the context_update_tile_id tile's final
// rows when a capture was requested, otherwise nothing to hand back.
let frame_cdfs : Av1FrameCdfs? = match cdfs_out {
Some(sink) => if sink.length() == 0 { None } else { sink[0] }
None => None
}
Some((frame, stripe_store, frame_cdfs))
}
///|
/// Upscale complete native rows after deblocking and CDEF. Source MI padding
/// participates in the horizontal taps; output padding is only buffer layout.
fn av1_frame_upscale(frame : Av1FramePlanes, width : Int) -> Av1FramePlanes? {
let coded_width = av1_partition_coded_extent(width)
let coded_height = av1_partition_coded_extent(frame.height)
if coded_width > MAX_IMAGE_PIXELS / coded_height {
return None
}
let planes : Array[Array[Int]] = []
for plane in 0.. value
None => return None
}
let padded = Array::makei(stride * (coded_height / divisor_y), i => {
let row = if i / stride < height { i / stride } else { height - 1 }
let col = if i % stride < target_width {
i % stride
} else {
target_width - 1
}
samples[row * target_width + col]
})
planes.push(padded)
}
Some({
width,
height: frame.height,
coded_width,
planes,
bit_depth: frame.bit_depth,
full_range: frame.full_range,
matrix_coefficients: frame.matrix_coefficients,
color_primaries: frame.color_primaries,
transfer_characteristics: frame.transfer_characteristics,
sub_x: frame.sub_x,
sub_y: frame.sub_y,
})
}
///|
fn av1_decode_frame_planes(
data : Array[Byte],
allow_monochrome? : Bool = false,
map? : Av1FrameMap,
sequence? : Av1SequenceInfo,
operating_point? : Int = 0,
spatial_layer? : Int? = None,
budget? : DecodeBudget? = None,
) -> Av1FramePlanes? {
let state = match map {
Some(value) => value
None => av1_frame_map()
}
let (_, displayed) = match
av1_decode_obu_planes(
data,
state,
sequence,
allow_monochrome,
operating_point~,
spatial_layer~,
budget~,
) {
Some(value) => value
None => return None
}
if displayed.is_empty() {
None
} else {
Some(displayed[0])
}
}
///|
/// Decode and save one assembled frame. Presentation (show_frame and grain)
/// belongs to the OBU sequencer, so hidden frames take this exact same path.
fn av1_decode_parsed_frame_planes(
header : Av1FrameHeaderInfo,
seq : Av1SequenceInfo,
state : Av1FrameMap,
budget? : DecodeBudget? = None,
) -> Av1FramePlanes? {
let width = if header.upscaled_width > header.frame_width {
header.upscaled_width
} else {
header.frame_width
}
if !decode_reserve(budget, width, header.frame_height) {
return None
}
// An inter frame is only decodable once every tool it signals can be acted
// on; a frame that uses one the block stage lacks is refused whole rather
// than decoded as a prefix of itself.
let inter = if header.frame_is_intra {
None
} else if !av1_inter_frame_supported(header, seq) {
decode_fail(
budget,
Unsupported,
"The signaled inter-picture tools are not supported",
)
return None
} else {
match av1_inter_frame_of(header, seq, state) {
Some(value) => Some(value)
None => {
decode_fail(
budget,
MissingReference,
"Inter-picture reference state is unavailable or incompatible",
)
return None
}
}
}
let frame_height = header.frame_height
let sb_size = if seq.use_128x128_superblock { 128 } else { 64 }
let inherited = Some(av1_frame_initial_cdfs(state, header))
let cols = match
av1_frame_tile_starts(
header.tile_col_starts_sb,
sb_size,
header.frame_width,
) {
Some(value) => value
None => return None
}
let rows = match
av1_frame_tile_starts(header.tile_row_starts_sb, sb_size, frame_height) {
Some(value) => value
None => return None
}
let cdfs_sink : Array[Av1FrameCdfs?] = []
let segment_sink : Array[Array[Int]] = []
let previous_segments = if header.primary_ref_frame >= 0 &&
header.primary_ref_frame < av1_primary_ref_none {
let slot = header.ref_frame_idx[header.primary_ref_frame]
if state.valid[slot] &&
state.frame_widths[slot] == header.frame_width &&
state.frame_heights[slot] == header.frame_height {
state.saved_segment_ids[slot]
} else {
[]
}
} else {
[]
}
let (decoded, _stripe_store, frame_cdfs) = match
av1_decode_tile_planes(
header.frame_width,
frame_height,
cols,
rows,
header.tile_payload,
header.tile_size_bytes,
header.base_q_idx,
header.disable_cdf_update,
header.allow_screen_content_tools,
seq.full_range,
seq.matrix_coefficients,
seq.use_128x128_superblock,
seq.bit_depth,
header.reduced_tx_set,
seq.enable_filter_intra,
header.tx_mode_select,
header.cdef,
color_primaries=seq.color_primaries,
transfer_characteristics=seq.transfer_characteristics,
monochrome=seq.monochrome,
enable_intra_edge_filter=seq.enable_intra_edge_filter,
loop_filter=header.loop_filter,
restoration=header.restoration,
upscaled_width=header.upscaled_width,
superres_denom=header.superres_denom,
quant_deltas=header.quant_deltas,
delta_q_present=header.delta_q_present,
delta_q_res=header.delta_q_res,
subsampling_x=seq.subsampling_x,
subsampling_y=seq.subsampling_y,
inter~,
segmentation=Some(header.segmentation),
quant_matrix=header.quant_matrix,
allow_intrabc=header.allow_intrabc,
previous_seg_ids=previous_segments,
delta_lf_present=header.delta_lf_present,
delta_lf_res=header.delta_lf_res,
delta_lf_multi=header.delta_lf_multi,
segment_ids_out=Some(segment_sink),
cdfs=inherited,
context_update_tile_id=header.context_update_tile_id,
cdfs_out=Some(cdfs_sink),
) {
Some(value) => value
None => return None
}
// The reference buffer keeps the grain-free reconstruction: AV1 applies film
// grain only on the displayed picture, so a later frame must not predict
// from noisy samples.
av1_frame_map_update(
state,
header,
decoded,
seq,
cdfs=frame_cdfs,
field=inter.map(fn(info) { info.field }),
segment_ids=if segment_sink.length() > 0 { segment_sink[0] } else { [] },
)
Some(decoded)
}