/// Pure MoonBit AV1 bitstream parsing used by the AVIF decoder.
///
/// Parses low-overhead OBU headers and reduced-still or general sequences,
/// retaining native 8/10/12-bit color configuration and the distinction between
/// entropy-coded and displayed dimensions for the tile reconstruction stage.
///|
/// Parsed AV1 sequence configuration shared by still and video decoding.
pub(all) struct Av1SequenceInfo {
profile : Int
width : Int
height : Int
bit_depth : Int
monochrome : Bool
subsampling_x : Bool
subsampling_y : Bool
reduced_still_picture_header : Bool
use_128x128_superblock : Bool
enable_filter_intra : Bool
enable_intra_edge_filter : Bool
enable_superres : Bool
enable_cdef : Bool
enable_restoration : Bool
full_range : Bool
separate_uv_delta_q : Bool
film_grain_params_present : Bool
color_primaries : Int
transfer_characteristics : Int
matrix_coefficients : Int
/// Inter-frame tool flags (AV1 §5.9.1). A reduced still-picture sequence
/// signals none of them, so every default below is the reduced-still
/// derivation rather than an unparse.
still_picture : Bool
enable_order_hint : Bool
order_hint_bits : Int
enable_ref_frame_mvs : Bool
enable_warped_motion : Bool
enable_dual_filter : Bool
enable_jnt_comp : Bool
enable_masked_compound : Bool
enable_interintra_compound : Bool
/// A `seq_force_*` value of 2 (SELECT) means the frame header picks the tool.
seq_force_screen_content_tools : Int
seq_force_integer_mv : Int
frame_id_numbers_present : Bool
delta_frame_id_length_minus_2 : Int
additional_frame_id_length_minus_1 : Int
decoder_model_info_present : Bool
equal_picture_interval : Bool
buffer_delay_length_minus_1 : Int
buffer_removal_time_length_minus_1 : Int
frame_presentation_time_length_minus_1 : Int
operating_points_cnt_minus_1 : Int
operating_point_idc : Array[Int]
decoder_model_present_for_this_op : Array[Bool]
/// `frame_width_bits_minus_1` / `frame_height_bits_minus_1`: the field widths
/// a `frame_size_override` re-reads the coded dimensions with.
frame_width_bits_minus_1 : Int
frame_height_bits_minus_1 : Int
}
///|
/// AV1 §3: a `seq_force_*` field of 2 defers the choice to the frame header.
let av1_select_screen_content_tools : Int = 2
///|
let av1_select_integer_mv : Int = 2
///|
/// Header facts available before tile entropy decoding. Reduced still-picture
/// headers imply a key frame and one displayed frame by construction.
pub(all) struct Av1FrameInfo {
key_frame : Bool
show_frame : Bool
payload_size : Int
}
///|
/// The bounded input handed to the next AV1 tile-decoding stage. This is a
/// data boundary rather than an image: tile bytes are still entropy-coded and
/// must be reconstructed before an `Image` exists.
pub(all) struct Av1Stage1Input {
/// Container/display width after superres; tile geometry uses frame_width.
width : Int
frame_width : Int
superres_denom : Int
height : Int
bit_depth : Int
monochrome : Bool
header_bytes : Int
base_q_idx : Int
disable_cdf_update : Bool
allow_screen_content_tools : Bool
tx_mode_select : Bool
reduced_tx_set : Bool
cdef : Av1CdefConfig?
loop_filter : Av1LoopFilterConfig?
restoration : Av1RestorationConfig?
tile_cols : Int
tile_rows : Int
tile_cols_log2 : Int
tile_rows_log2 : Int
/// Tile boundaries in superblock units, including the terminal boundary.
tile_col_starts_sb : Array[Int]
tile_row_starts_sb : Array[Int]
tile_size_bytes : Int
enable_filter_intra : Bool
enable_intra_edge_filter : Bool
full_range : Bool
matrix_coefficients : Int
use_128x128_superblock : Bool
tile_payload : Array[Byte]
/// Film-grain parameters parsed from the frame header, or None when the
/// sequence disabled the tool. Applied after reconstruction.
film_grain : Av1FilmGrainParams?
/// Per-plane quantizer deltas; V differs from U only when the sequence
/// enables `separate_uv_delta_q`.
quant_deltas : Av1QuantDeltas
}
///|
/// Small AV1 MSAC boolean decoder. It is kept independent from frame syntax so
/// the tile decoder can adopt it without changing the public image API.
priv struct Av1Msac {
data : Array[Byte]
mut pos : Int
mut range : Int
mut value : Int
mut input_byte : Int
mut input_bits : Int
}
///|
fn Av1Msac::new(data : Array[Byte]) -> Av1Msac {
let mut value = 0
let mut pos = 0
for _ in 0..<4 {
let incoming = if pos < data.length() { data[pos].to_int() } else { 0 }
value = (value << 8) | incoming
pos = pos + 1
}
{ data, pos, range: 255, value, input_byte: 0, input_bits: 0, }
}
///|
/// Decodes one AV1 boolean with an 8-bit probability (0..255), normalising the
/// interval as required by the MSAC range coder. Returns None at end of input.
fn Av1Msac::read_bool(self : Av1Msac, probability : Int) -> Int? {
if probability < 0 || probability > 255 {
return None
}
let split = 1 + (((self.range - 1) * probability) >> 8)
let bit = if self.value < split {
self.range = split
0
} else {
self.value = self.value - split
self.range = self.range - split
1
}
while self.range < 128 {
self.range = self.range << 1
self.value = self.value << 1
if self.input_bits == 0 && self.pos < self.data.length() {
self.input_byte = self.data[self.pos].to_int()
self.pos = self.pos + 1
self.input_bits = 8
}
if self.input_bits > 0 {
self.value = self.value | ((self.input_byte >> 7) & 1)
self.input_byte = (self.input_byte << 1) & 0xFF
self.input_bits = self.input_bits - 1
}
}
Some(bit)
}
///|
/// Reads one boolean from an isolated MSAC payload. This small public probe is
/// also useful for conformance tests while the full tile decoder is developed.
pub fn av1_msac_read_first(data : Array[Byte], probability : Int) -> Int? {
let reader = Av1Msac::new(data)
reader.read_bool(probability)
}
///|
/// Reads AV1 bits in specification (MSB-first) order.
priv struct Av1BitReader {
data : Array[Byte]
mut bit_pos : Int
}
///|
fn Av1BitReader::new(data : Array[Byte]) -> Av1BitReader {
{ data, bit_pos: 0, }
}
///|
fn Av1BitReader::read(self : Av1BitReader, count : Int) -> Int? {
if count < 0 || count > 31 || self.bit_pos + count > self.data.length() * 8 {
return None
}
let mut value = 0
for _ in 0..> shift) & 1)
self.bit_pos = self.bit_pos + 1
}
Some(value)
}
///|
fn av1_u(reader : Av1BitReader, count : Int) -> Int? {
reader.read(count)
}
///|
fn av1_skip(reader : Av1BitReader, count : Int) -> Bool {
match reader.read(count) {
Some(_) => true
None => false
}
}
///|
fn av1_leb128(data : Array[Byte], start : Int, end : Int) -> (Int, Int)? {
let mut value = 0
let mut shift = 0
let mut pos = start
while pos < end && shift <= 28 {
let byte = data[pos].to_int()
value = value | ((byte & 0x7F) << shift)
pos = pos + 1
if (byte & 0x80) == 0 {
return Some((value, pos))
}
shift = shift + 7
}
None
}
///|
fn av1_obu_payloads(
data : Array[Byte],
wanted_type : Int,
) -> Array[Array[Byte]] {
let out : Array[Array[Byte]] = []
let mut pos = 0
while pos < data.length() {
if pos >= data.length() {
break
}
let header = data[pos].to_int()
pos = pos + 1
if (header & 0x80) != 0 || (header & 0x01) != 0 {
break
}
let obu_type = (header >> 3) & 0x0F
let extension = (header >> 2) & 1
let has_size = (header >> 1) & 1
if extension == 1 {
if pos >= data.length() {
break
}
pos = pos + 1
}
let payload_len = if has_size == 1 {
match av1_leb128(data, pos, data.length()) {
Some((n, next)) => {
pos = next
n
}
None => break
}
} else {
data.length() - pos
}
if payload_len < 0 || pos + payload_len > data.length() {
break
}
if obu_type == wanted_type {
let payload : Array[Byte] = []
for i in pos..<(pos + payload_len) {
payload.push(data[i])
}
out.push(payload)
}
pos = pos + payload_len
if has_size == 0 {
break
}
}
out
}
///|
priv struct Av1ColorConfig {
bit_depth : Int
monochrome : Bool
subsampling_x : Bool
subsampling_y : Bool
full_range : Bool
separate_uv_delta_q : Bool
primaries : Int
transfer : Int
matrix : Int
chroma_sample_position : Int
}
///|
fn av1_parse_color_config(
reader : Av1BitReader,
profile : Int,
) -> Av1ColorConfig? {
let high = match reader.read(1) {
Some(v) => v == 1
None => return None
}
let twelve = if profile == 2 && high {
match reader.read(1) {
Some(v) => v == 1
None => return None
}
} else {
false
}
let bit_depth = if twelve { 12 } else if high { 10 } else { 8 }
let monochrome = if profile == 1 {
false
} else {
match reader.read(1) {
Some(v) => v == 1
None => return None
}
}
let desc = match reader.read(1) {
Some(v) => v == 1
None => return None
}
let primaries = if desc {
match reader.read(8) {
Some(v) => v
None => return None
}
} else {
2
}
let transfer = if desc {
match reader.read(8) {
Some(v) => v
None => return None
}
} else {
2
}
let matrix = if desc {
match reader.read(8) {
Some(v) => v
None => return None
}
} else {
2
}
if monochrome {
let full_range = match reader.read(1) {
Some(v) => v == 1
None => return None
}
return Some({
bit_depth,
monochrome,
subsampling_x: true,
subsampling_y: true,
full_range,
separate_uv_delta_q: false,
primaries,
transfer,
matrix,
chroma_sample_position: 0,
})
}
let identity = primaries == 1 && transfer == 13 && matrix == 0
let full_range = if identity {
true
} else {
match reader.read(1) {
Some(v) => v == 1
None => return None
}
}
let (sx, sy) = if identity || profile == 1 {
(false, false)
} else if profile == 0 {
(true, true)
} else if bit_depth == 12 {
let x = match reader.read(1) {
Some(v) => v == 1
None => return None
}
let y = if x {
match reader.read(1) {
Some(v) => v == 1
None => return None
}
} else {
false
}
(x, y)
} else {
(true, false)
}
let chroma_sample_position = if sx && sy {
match reader.read(2) {
Some(value) => value
None => return None
}
} else {
0
}
let separate = match reader.read(1) {
Some(v) => v == 1
None => return None
}
Some({
bit_depth,
monochrome,
subsampling_x: sx,
subsampling_y: sy,
full_range,
separate_uv_delta_q: separate,
primaries,
transfer,
matrix,
chroma_sample_position,
})
}
///|
/// Skips a 32-bit syntax element. `Av1BitReader::read` caps single reads at 31
/// bits, so wide fields are consumed as two halves.
fn av1_skip32(reader : Av1BitReader) -> Bool {
av1_skip(reader, 16) && av1_skip(reader, 16)
}
///|
/// Skip UVLC (AV1 §4.10.3): leading zero bits, a one terminator, then the
/// same number of value bits. At least 32 leading zeros encodes the sentinel
/// without a value suffix, but the terminating one must still be consumed.
fn av1_skip_uvlc(reader : Av1BitReader) -> Bool {
let mut i = 0
while reader.bit_pos / 8 < reader.data.length() {
match reader.read(1) {
Some(1) => return i >= 32 || av1_skip(reader, i)
Some(_) => if i < 32 { i += 1 }
None => return false
}
}
false
}
///|
/// Parses `sequence_header_obu` (AV1 §5.5.1). Both the reduced still-picture
/// and the general grammar are supported; the timing and operating-point fields
/// the frame header needs to skip its own optional sections are retained.
fn av1_parse_sequence_payload(payload : Array[Byte]) -> Av1SequenceInfo? {
av1_parse_sequence_payload_details(payload, None)
}
///|
/// Container configuration fields which were historically not exposed in
/// Av1SequenceInfo. Keep that public record and unary parser contract intact.
priv struct Av1SequenceDetails {
mut level : Int
mut tier : Int
mut chroma_sample_position : Int
}
///|
fn av1_parse_sequence_payload_details(
payload : Array[Byte],
details : Av1SequenceDetails?,
) -> Av1SequenceInfo? {
let reader = Av1BitReader::new(payload)
let profile = match av1_u(reader, 3) {
Some(v) => v
None => return None
}
if profile > 2 {
return None
}
let still_picture = match reader.read(1) {
Some(v) => v == 1
None => return None
}
let reduced = match reader.read(1) {
Some(v) => v == 1
None => return None
}
// Operating points (AV1 §5.5.1). A reduced still-picture header fixes the
// single operating point at idc 0 and reads no presence flags at all.
let mut decoder_model_info_present = false
let mut equal_picture_interval = false
let mut buffer_delay_length_minus_1 = 0
let mut buffer_removal_time_length_minus_1 = 0
let mut frame_presentation_time_length_minus_1 = 0
let mut operating_points_cnt_minus_1 = 0
let mut operating_point_idc : Array[Int] = [0]
let mut decoder_model_present_for_this_op : Array[Bool] = [false]
let mut level_0 = 0
let mut tier_0 = 0
if reduced {
level_0 = match av1_u(reader, 5) {
Some(value) => value
None => return None
}
} else {
let timing_info_present = match reader.read(1) {
Some(v) => v == 1
None => return None
}
if timing_info_present {
if !av1_skip32(reader) {
return None
} // num_units_in_display_tick
if !av1_skip32(reader) {
return None
} // time_scale
equal_picture_interval = match reader.read(1) {
Some(v) => v == 1
None => return None
}
if equal_picture_interval && !av1_skip_uvlc(reader) {
return None
}
decoder_model_info_present = match reader.read(1) {
Some(v) => v == 1
None => return None
}
if decoder_model_info_present {
buffer_delay_length_minus_1 = match av1_u(reader, 5) {
Some(v) => v
None => return None
}
if !av1_skip32(reader) {
return None
} // num_units_in_decoding_tick
buffer_removal_time_length_minus_1 = match av1_u(reader, 5) {
Some(v) => v
None => return None
}
frame_presentation_time_length_minus_1 = match av1_u(reader, 5) {
Some(v) => v
None => return None
}
}
}
let initial_display_delay_present = match reader.read(1) {
Some(v) => v == 1
None => return None
}
operating_points_cnt_minus_1 = match av1_u(reader, 5) {
Some(v) => v
None => return None
}
if operating_points_cnt_minus_1 >= 32 {
return None
}
let idcs : Array[Int] = []
let models : Array[Bool] = []
for point in 0..<=operating_points_cnt_minus_1 {
let idc = match av1_u(reader, 12) {
Some(v) => v
None => return None
}
let level = match av1_u(reader, 5) {
Some(v) => v
None => return None
}
let tier = if level > 7 {
match av1_u(reader, 1) {
Some(value) => value
None => return None
}
} else {
0
}
if point == 0 {
level_0 = level
tier_0 = tier
}
if decoder_model_info_present {
let present = match reader.read(1) {
Some(v) => v == 1
None => return None
}
models.push(present)
if present {
let width = buffer_delay_length_minus_1 + 1
if !(if width == 32 {
av1_skip32(reader)
} else {
av1_skip(reader, width)
}) ||
!(if width == 32 {
av1_skip32(reader)
} else {
av1_skip(reader, width)
}) ||
!av1_skip(reader, 1) {
return None
}
}
} else {
models.push(false)
}
if initial_display_delay_present {
let present = match reader.read(1) {
Some(v) => v == 1
None => return None
}
if present && !av1_skip(reader, 4) {
return None
} // initial_display_delay_minus_1
}
idcs.push(idc)
}
operating_point_idc = idcs
decoder_model_present_for_this_op = models
}
let width_bits = match av1_u(reader, 4) {
Some(v) => v + 1
None => return None
}
let height_bits = match av1_u(reader, 4) {
Some(v) => v + 1
None => return None
}
let frame_width_bits_minus_1 = width_bits - 1
let frame_height_bits_minus_1 = height_bits - 1
let width_minus_one = match av1_u(reader, width_bits) {
Some(v) => v
None => return None
}
let height_minus_one = match av1_u(reader, height_bits) {
Some(v) => v
None => return None
}
let width = width_minus_one + 1
let height = height_minus_one + 1
if width <= 0 || height <= 0 || width > 65536 || height > 65536 {
return None
}
// Frame identification (AV1 §5.5.1). Absent from a reduced still header.
let mut frame_id_numbers_present = false
let mut delta_frame_id_length_minus_2 = 0
let mut additional_frame_id_length_minus_1 = 0
if !reduced {
frame_id_numbers_present = match reader.read(1) {
Some(v) => v == 1
None => return None
}
if frame_id_numbers_present {
delta_frame_id_length_minus_2 = match av1_u(reader, 4) {
Some(v) => v
None => return None
}
additional_frame_id_length_minus_1 = match av1_u(reader, 3) {
Some(v) => v
None => return None
}
}
}
// Sequence tools preceding color_config(). Keep each flag explicit so the
// frame stage can make a capability decision without guessing bit offsets.
let use128 = match av1_u(reader, 1) {
Some(v) => v == 1
None => return None
}
let filter_intra = match av1_u(reader, 1) {
Some(v) => v == 1
None => return None
}
let edge_filter = match av1_u(reader, 1) {
Some(v) => v == 1
None => return None
}
let mut interintra_compound = false
let mut masked_compound = false
let mut warped_motion = false
let mut dual_filter = false
let mut order_hint = false
let mut jnt_comp = false
let mut ref_frame_mvs = false
let mut force_screen_content = av1_select_screen_content_tools
let mut force_integer_mv = av1_select_integer_mv
let mut order_hint_bits = 0
if !reduced {
interintra_compound = match reader.read(1) {
Some(v) => v == 1
None => return None
}
masked_compound = match reader.read(1) {
Some(v) => v == 1
None => return None
}
warped_motion = match reader.read(1) {
Some(v) => v == 1
None => return None
}
dual_filter = match reader.read(1) {
Some(v) => v == 1
None => return None
}
order_hint = match reader.read(1) {
Some(v) => v == 1
None => return None
}
if order_hint {
jnt_comp = match reader.read(1) {
Some(v) => v == 1
None => return None
}
ref_frame_mvs = match reader.read(1) {
Some(v) => v == 1
None => return None
}
}
// seq_choose_screen_content_tools: 1 defers the choice to every frame.
force_screen_content = if reader.read(1) == Some(1) {
av1_select_screen_content_tools
} else {
match av1_u(reader, 1) {
Some(v) => v
None => return None
}
}
force_integer_mv = if force_screen_content > 0 {
if reader.read(1) == Some(1) {
av1_select_integer_mv
} else {
match av1_u(reader, 1) {
Some(v) => v
None => return None
}
}
} else {
av1_select_integer_mv
}
if order_hint {
order_hint_bits = match av1_u(reader, 3) {
Some(v) => v + 1
None => return None
}
}
}
let superres = match av1_u(reader, 1) {
Some(v) => v == 1
None => return None
}
let cdef = match av1_u(reader, 1) {
Some(v) => v == 1
None => return None
}
let restoration = match av1_u(reader, 1) {
Some(v) => v == 1
None => return None
}
let color = match av1_parse_color_config(reader, profile) {
Some(v) => v
None => return None
}
let film_grain = match reader.read(1) {
Some(v) => v == 1
None => return None
}
// sequence_header_obu ends with trailing_one_bit and zero padding.
if reader.read(1) != Some(1) {
return None
}
while reader.bit_pos % 8 != 0 {
if reader.read(1) != Some(0) {
return None
}
}
if details is Some(value) {
value.level = level_0
value.tier = tier_0
value.chroma_sample_position = color.chroma_sample_position
}
Some({
profile,
width,
height,
bit_depth: color.bit_depth,
monochrome: color.monochrome,
subsampling_x: color.subsampling_x,
subsampling_y: color.subsampling_y,
reduced_still_picture_header: reduced,
use_128x128_superblock: use128,
enable_filter_intra: filter_intra,
enable_intra_edge_filter: edge_filter,
enable_superres: superres,
enable_cdef: cdef,
enable_restoration: restoration,
full_range: color.full_range,
separate_uv_delta_q: color.separate_uv_delta_q,
film_grain_params_present: film_grain,
color_primaries: color.primaries,
transfer_characteristics: color.transfer,
matrix_coefficients: color.matrix,
still_picture,
enable_order_hint: order_hint,
order_hint_bits,
enable_ref_frame_mvs: ref_frame_mvs,
enable_warped_motion: warped_motion,
enable_dual_filter: dual_filter,
enable_jnt_comp: jnt_comp,
enable_masked_compound: masked_compound,
enable_interintra_compound: interintra_compound,
seq_force_screen_content_tools: force_screen_content,
seq_force_integer_mv: force_integer_mv,
frame_id_numbers_present,
delta_frame_id_length_minus_2,
additional_frame_id_length_minus_1,
decoder_model_info_present,
equal_picture_interval,
buffer_delay_length_minus_1,
buffer_removal_time_length_minus_1,
frame_presentation_time_length_minus_1,
operating_points_cnt_minus_1,
operating_point_idc,
decoder_model_present_for_this_op,
frame_width_bits_minus_1,
frame_height_bits_minus_1,
})
}
///|
/// Parses a raw AV1 low-overhead bitstream and returns its reduced sequence
/// header. The input is OBU data (the AVIF `av1C` configuration is not needed).
pub fn av1_sequence_info(data : Array[Byte]) -> Av1SequenceInfo? {
let mut pos = 0
while pos < data.length() {
if pos >= data.length() {
return None
}
let header = data[pos].to_int()
pos = pos + 1
if (header & 0x80) != 0 || (header & 0x01) != 0 {
return None
}
let obu_type = (header >> 3) & 0x0F
let extension = (header >> 2) & 1
let has_size = (header >> 1) & 1
if extension == 1 {
if pos >= data.length() {
return None
}
pos = pos + 1
}
let payload_len = if has_size == 1 {
let size = av1_leb128(data, pos, data.length())
match size {
Some((n, next)) => {
pos = next
n
}
None => return None
}
} else {
data.length() - pos
}
if payload_len < 0 || pos + payload_len > data.length() {
return None
}
if obu_type == 1 {
let payload : Array[Byte] = []
for i in pos..<(pos + payload_len) {
payload.push(data[i])
}
return av1_parse_sequence_payload(payload)
}
pos = pos + payload_len
if has_size == 0 {
break
}
}
None
}
///|
/// Returns the reduced-still frame header envelope when a frame OBU follows a
/// supported sequence header. Tile payload bytes are retained only as a size
/// check; they are not interpreted as pixels by this milestone.
pub fn av1_frame_info(data : Array[Byte]) -> Av1FrameInfo? {
let sequence = av1_sequence_info(data)
match sequence {
None => None
Some(sequence) => {
if !sequence.reduced_still_picture_header {
return None
}
let frames = av1_obu_payloads(data, 6)
if frames.length() == 0 {
None
} else {
Some({
key_frame: true,
show_frame: true,
payload_size: frames[0].length(),
})
}
}
}
}
///|
/// Extracts the first supported AV1 tile payload from an AVIF container.
///
/// The function validates the container, reduced-still sequence header, and
/// bounded frame envelope. It does not decode entropy symbols or fabricate
/// pixels; callers in the next milestone consume `tile_payload` with the
/// intra tile reconstruction pipeline.
pub fn avif_stage1_input(data : Array[Byte]) -> Av1Stage1Input? {
let container = avif_container_parse_limited(data)
match container {
None => None
Some(c) => {
let sequence = av1_sequence_info(c.payload)
let seq = match sequence {
None => return None
Some(value) => value
}
if !avif_sequence_matches_container(c, seq) {
return None
}
let frames = av1_obu_payloads(c.payload, 6)
if frames.length() == 0 {
return None
}
let header = av1_parse_stage1_frame(
frames[0],
seq,
allow_monochrome=true,
allow_highbd=true,
)
match header {
None => None
Some(frame) =>
Some({
width: c.width,
frame_width: frame.frame_width,
superres_denom: frame.superres_denom,
height: c.height,
bit_depth: c.bit_depth,
monochrome: seq.monochrome,
header_bytes: frame.header_bytes,
base_q_idx: frame.base_q_idx,
disable_cdf_update: frame.disable_cdf_update,
allow_screen_content_tools: frame.allow_screen_content_tools,
tx_mode_select: frame.tx_mode_select,
reduced_tx_set: frame.reduced_tx_set,
cdef: frame.cdef,
loop_filter: frame.loop_filter,
restoration: frame.restoration,
tile_cols: frame.tile_cols,
tile_rows: frame.tile_rows,
tile_cols_log2: frame.tile_cols_log2,
tile_rows_log2: frame.tile_rows_log2,
tile_col_starts_sb: frame.tile_col_starts_sb,
tile_row_starts_sb: frame.tile_row_starts_sb,
tile_size_bytes: frame.tile_size_bytes,
enable_filter_intra: seq.enable_filter_intra,
enable_intra_edge_filter: seq.enable_intra_edge_filter,
full_range: seq.full_range,
matrix_coefficients: seq.matrix_coefficients,
use_128x128_superblock: seq.use_128x128_superblock,
tile_payload: frame.tile_payload,
film_grain: frame.film_grain,
quant_deltas: frame.quant_deltas,
})
}
}
}
}
///|
/// Validate the coded item's configuration before either exposing tile data
/// or decoding through the complete native-depth frame pipeline.
fn avif_sequence_matches_container(
container : AvifContainer,
sequence : Av1SequenceInfo,
) -> Bool {
(
container.bit_depth == 8 ||
container.bit_depth == 10 ||
container.bit_depth == 12
) &&
container.bit_depth == sequence.bit_depth &&
container.monochrome == sequence.monochrome &&
(
container.monochrome ||
(
container.subsampling_x == sequence.subsampling_x &&
container.subsampling_y == sequence.subsampling_y
)
) &&
// ispe describes the selected frame, whereas the sequence stores maxima.
// The public decode entry validates equality with the actual frame later.
container.width > 0 &&
container.width <= sequence.width &&
container.height > 0 &&
container.height <= sequence.height &&
container.operating_point < sequence.operating_point_idc.length()
}