#![forbid(unsafe_code)]
use crate::DecodeError;
use mediaway_sw::h264::{BitReader, H264Error};
fn map_bit_err<T>(r: Result<T, H264Error>) -> Result<T, DecodeError> {
r.map_err(|_err| DecodeError::InvalidInput)
}
fn read_bit(r: &mut BitReader<'_>) -> Result<bool, DecodeError> {
Ok(map_bit_err(r.read_bit())? != 0)
}
fn read_bits(r: &mut BitReader<'_>, count: u32) -> Result<u32, DecodeError> {
map_bit_err(r.read_bits(count))
}
const SEQ_PROFILE_MAIN: u32 = 0;
const SELECT_SCREEN_CONTENT_TOOLS: u32 = 2;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(
clippy::struct_excessive_bools,
reason = "each bool is a real, independent AV1 sequence-header flag that must be \
echoed into DXVA_PicParams_AV1 exactly as signaled — same reasoning \
hevc_vps_sps_pps.rs's Sps gives for its own identical allow"
)]
pub(super) struct SequenceHeader {
pub(super) max_frame_width: u32,
pub(super) max_frame_height: u32,
pub(super) frame_width_bits: u32,
pub(super) frame_height_bits: u32,
pub(super) use_128x128_superblock: bool,
pub(super) enable_filter_intra: bool,
pub(super) enable_intra_edge_filter: bool,
pub(super) enable_interintra_compound: bool,
pub(super) enable_masked_compound: bool,
pub(super) enable_dual_filter: bool,
pub(super) enable_order_hint: bool,
pub(super) enable_jnt_comp: bool,
pub(super) enable_ref_frame_mvs: bool,
pub(super) order_hint_bits: u32,
pub(super) separate_uv_delta_q: bool,
}
#[allow(
clippy::too_many_lines,
reason = "one linear AV1 spec §5.5.1/§5.5.2 syntax-element sequence through the fields \
this module needs; mirrors hevc_vps_sps_pps.rs::parse_sps's identical shape"
)]
pub(super) fn parse_sequence_header(payload: &[u8]) -> Result<SequenceHeader, DecodeError> {
let mut r = BitReader::new(payload);
let seq_profile = read_bits(&mut r, 3)?;
if seq_profile != SEQ_PROFILE_MAIN {
return Err(DecodeError::Unsupported);
}
let _still_picture = read_bit(&mut r)?;
let reduced_still_picture_header = read_bit(&mut r)?;
if reduced_still_picture_header {
return Err(DecodeError::Unsupported);
}
let timing_info_present_flag = read_bit(&mut r)?;
if timing_info_present_flag {
return Err(DecodeError::Unsupported);
}
let initial_display_delay_present_flag = read_bit(&mut r)?;
if initial_display_delay_present_flag {
return Err(DecodeError::Unsupported);
}
let operating_points_cnt_minus_1 = read_bits(&mut r, 5)?;
if operating_points_cnt_minus_1 != 0 {
return Err(DecodeError::Unsupported);
}
let _operating_point_idc0 = read_bits(&mut r, 12)?;
let seq_level_idx0 = read_bits(&mut r, 5)?;
if seq_level_idx0 > 7 {
let _seq_tier0 = read_bit(&mut r)?;
}
let frame_width_bits = read_bits(&mut r, 4)?
.checked_add(1)
.ok_or(DecodeError::InvalidInput)?;
let frame_height_bits = read_bits(&mut r, 4)?
.checked_add(1)
.ok_or(DecodeError::InvalidInput)?;
let max_frame_width = read_bits(&mut r, frame_width_bits)?
.checked_add(1)
.ok_or(DecodeError::InvalidInput)?;
let max_frame_height = read_bits(&mut r, frame_height_bits)?
.checked_add(1)
.ok_or(DecodeError::InvalidInput)?;
let frame_id_numbers_present_flag = read_bit(&mut r)?;
if frame_id_numbers_present_flag {
return Err(DecodeError::Unsupported);
}
let use_128x128_superblock = read_bit(&mut r)?;
let enable_filter_intra = read_bit(&mut r)?;
let enable_intra_edge_filter = read_bit(&mut r)?;
let enable_interintra_compound = read_bit(&mut r)?;
let enable_masked_compound = read_bit(&mut r)?;
let _enable_warped_motion = read_bit(&mut r)?;
let enable_dual_filter = read_bit(&mut r)?;
let enable_order_hint = read_bit(&mut r)?;
let (enable_jnt_comp, enable_ref_frame_mvs) = if enable_order_hint {
(read_bit(&mut r)?, read_bit(&mut r)?)
} else {
(false, false)
};
let seq_choose_screen_content_tools = read_bit(&mut r)?;
let seq_force_screen_content_tools = if seq_choose_screen_content_tools {
SELECT_SCREEN_CONTENT_TOOLS
} else {
read_bits(&mut r, 1)?
};
if seq_force_screen_content_tools > 0 {
return Err(DecodeError::Unsupported);
}
let order_hint_bits = if enable_order_hint {
read_bits(&mut r, 3)?
.checked_add(1)
.ok_or(DecodeError::InvalidInput)?
} else {
0
};
let enable_superres = read_bit(&mut r)?;
if enable_superres {
return Err(DecodeError::Unsupported);
}
let enable_cdef = read_bit(&mut r)?;
if enable_cdef {
return Err(DecodeError::Unsupported);
}
let enable_restoration = read_bit(&mut r)?;
if enable_restoration {
return Err(DecodeError::Unsupported);
}
let separate_uv_delta_q = parse_color_config(&mut r)?;
let film_grain_params_present = read_bit(&mut r)?;
if film_grain_params_present {
return Err(DecodeError::Unsupported);
}
Ok(SequenceHeader {
max_frame_width,
max_frame_height,
frame_width_bits,
frame_height_bits,
use_128x128_superblock,
enable_filter_intra,
enable_intra_edge_filter,
enable_interintra_compound,
enable_masked_compound,
enable_dual_filter,
enable_order_hint,
enable_jnt_comp,
enable_ref_frame_mvs,
order_hint_bits,
separate_uv_delta_q,
})
}
fn parse_color_config(r: &mut BitReader<'_>) -> Result<bool, DecodeError> {
const CP_BT_709: u32 = 1;
const TC_SRGB: u32 = 13;
const MC_IDENTITY: u32 = 0;
let high_bitdepth = read_bit(r)?;
if high_bitdepth {
return Err(DecodeError::Unsupported);
}
let mono_chrome = read_bit(r)?;
if mono_chrome {
return Err(DecodeError::Unsupported);
}
let color_description_present_flag = read_bit(r)?;
let (color_primaries, transfer_characteristics, matrix_coefficients) =
if color_description_present_flag {
(read_bits(r, 8)?, read_bits(r, 8)?, read_bits(r, 8)?)
} else {
(2, 2, 2)
};
let (subsampling_x, subsampling_y) = if color_primaries == CP_BT_709
&& transfer_characteristics == TC_SRGB
&& matrix_coefficients == MC_IDENTITY
{
(0u32, 0u32)
} else {
let _color_range = read_bit(r)?;
(1u32, 1u32)
};
if subsampling_x != 1 || subsampling_y != 1 {
return Err(DecodeError::Unsupported);
}
if subsampling_x == 1 && subsampling_y == 1 {
let _chroma_sample_position = read_bits(r, 2)?;
}
let separate_uv_delta_q = read_bit(r)?;
Ok(separate_uv_delta_q)
}
#[cfg(test)]
#[path = "av1_sequence_header_tests.rs"]
mod tests;