#![forbid(unsafe_code)]
use crate::DecodeError;
use mediaway_sw::h264::BitReader;
const SELECT_VALUE: u32 = 2;
const UNSPECIFIED: u32 = 2;
const CP_BT_709: u32 = 1;
const TC_SRGB: u32 = 13;
const MC_IDENTITY: u32 = 0;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(
clippy::struct_excessive_bools,
reason = "each bool names one AV1 sequence_header_obu()/color_config() syntax element; a \
state machine would obscure the 1:1 spec mapping this crate relies on for \
review, same precedent as this crate's H.264 Pps"
)]
pub(super) struct SequenceHeader {
pub(super) seq_profile: u8,
pub(super) use_128x128_superblock: bool,
pub(super) enable_order_hint: bool,
pub(super) order_hint_bits: u32,
pub(super) frame_width_bits_minus_1: u32,
pub(super) frame_height_bits_minus_1: u32,
pub(super) max_frame_width_minus_1: u32,
pub(super) max_frame_height_minus_1: u32,
pub(super) seq_force_screen_content_tools: u32,
pub(super) seq_force_integer_mv: u32,
pub(super) color_range: bool,
pub(super) matrix_coefficients: u8,
pub(super) chroma_sample_position: u8,
pub(super) separate_uv_delta_q: bool,
}
impl SequenceHeader {
#[allow(
clippy::too_many_lines,
reason = "one linear, spec-section-ordered read sequence (sequence_header_obu() -> \
color_config()); splitting further would just move consecutive reads of the \
same syntax structure into a same-file helper with no independent reuse"
)]
pub(super) fn parse(data: &[u8]) -> Result<Self, DecodeError> {
let mut r = BitReader::new(data);
let map_err = |_| DecodeError::InvalidInput;
let seq_profile = r.read_bits(3).map_err(map_err)?;
if seq_profile != 0 {
return Err(DecodeError::Unsupported);
}
let _still_picture = r.read_bit().map_err(map_err)? != 0;
let reduced_still_picture_header = r.read_bit().map_err(map_err)? != 0;
if reduced_still_picture_header {
return Err(DecodeError::Unsupported);
}
let timing_info_present_flag = r.read_bit().map_err(map_err)? != 0;
if timing_info_present_flag {
return Err(DecodeError::Unsupported);
}
let initial_display_delay_present_flag = r.read_bit().map_err(map_err)? != 0;
if initial_display_delay_present_flag {
return Err(DecodeError::Unsupported);
}
let operating_points_cnt_minus_1 = r.read_bits(5).map_err(map_err)?;
if operating_points_cnt_minus_1 != 0 {
return Err(DecodeError::Unsupported);
}
let _operating_point_idc = r.read_bits(12).map_err(map_err)?;
let seq_level_idx = r.read_bits(5).map_err(map_err)?;
if seq_level_idx > 7 {
let _seq_tier = r.read_bit().map_err(map_err)?;
}
let frame_width_bits_minus_1 = r.read_bits(4).map_err(map_err)?;
let frame_height_bits_minus_1 = r.read_bits(4).map_err(map_err)?;
let max_frame_width_minus_1 = r.read_bits(frame_width_bits_minus_1 + 1).map_err(map_err)?;
let max_frame_height_minus_1 = r
.read_bits(frame_height_bits_minus_1 + 1)
.map_err(map_err)?;
let frame_id_numbers_present_flag = r.read_bit().map_err(map_err)? != 0;
if frame_id_numbers_present_flag {
return Err(DecodeError::Unsupported);
}
let use_128x128_superblock = r.read_bit().map_err(map_err)? != 0;
if r.read_bit().map_err(map_err)? != 0 {
return Err(DecodeError::Unsupported); }
if r.read_bit().map_err(map_err)? != 0 {
return Err(DecodeError::Unsupported); }
if r.read_bit().map_err(map_err)? != 0 {
return Err(DecodeError::Unsupported); }
if r.read_bit().map_err(map_err)? != 0 {
return Err(DecodeError::Unsupported); }
if r.read_bit().map_err(map_err)? != 0 {
return Err(DecodeError::Unsupported); }
if r.read_bit().map_err(map_err)? != 0 {
return Err(DecodeError::Unsupported); }
let enable_order_hint = r.read_bit().map_err(map_err)? != 0;
if enable_order_hint {
if r.read_bit().map_err(map_err)? != 0 {
return Err(DecodeError::Unsupported); }
if r.read_bit().map_err(map_err)? != 0 {
return Err(DecodeError::Unsupported); }
}
let seq_choose_screen_content_tools = r.read_bit().map_err(map_err)? != 0;
let seq_force_screen_content_tools = if seq_choose_screen_content_tools {
SELECT_VALUE
} else {
r.read_bit().map_err(map_err)?
};
let seq_force_integer_mv = if seq_force_screen_content_tools > 0 {
let seq_choose_integer_mv = r.read_bit().map_err(map_err)? != 0;
if seq_choose_integer_mv {
SELECT_VALUE
} else {
r.read_bit().map_err(map_err)?
}
} else {
SELECT_VALUE
};
let order_hint_bits = if enable_order_hint {
r.read_bits(3).map_err(map_err)? + 1
} else {
0
};
if r.read_bit().map_err(map_err)? != 0 {
return Err(DecodeError::Unsupported); }
if r.read_bit().map_err(map_err)? != 0 {
return Err(DecodeError::Unsupported); }
if r.read_bit().map_err(map_err)? != 0 {
return Err(DecodeError::Unsupported); }
let high_bitdepth = r.read_bit().map_err(map_err)? != 0;
if high_bitdepth {
return Err(DecodeError::Unsupported);
}
let mono_chrome = r.read_bit().map_err(map_err)? != 0;
if mono_chrome {
return Err(DecodeError::Unsupported);
}
let color_description_present_flag = r.read_bit().map_err(map_err)? != 0;
let (color_primaries, transfer_characteristics, matrix_coefficients) =
if color_description_present_flag {
(
r.read_bits(8).map_err(map_err)?,
r.read_bits(8).map_err(map_err)?,
r.read_bits(8).map_err(map_err)?,
)
} else {
(UNSPECIFIED, UNSPECIFIED, UNSPECIFIED)
};
let (color_range, chroma_sample_position, separate_uv_delta_q) = if color_primaries
== CP_BT_709
&& transfer_characteristics == TC_SRGB
&& matrix_coefficients == MC_IDENTITY
{
let separate_uv_delta_q = r.read_bit().map_err(map_err)? != 0;
(true, 0u8, separate_uv_delta_q)
} else {
let color_range = r.read_bit().map_err(map_err)? != 0;
let chroma_sample_position =
u8::try_from(r.read_bits(2).map_err(map_err)?).unwrap_or(0);
let separate_uv_delta_q = r.read_bit().map_err(map_err)? != 0;
(color_range, chroma_sample_position, separate_uv_delta_q)
};
let film_grain_params_present = r.read_bit().map_err(map_err)? != 0;
if film_grain_params_present {
return Err(DecodeError::Unsupported);
}
Ok(Self {
seq_profile: 0,
use_128x128_superblock,
enable_order_hint,
order_hint_bits,
frame_width_bits_minus_1,
frame_height_bits_minus_1,
max_frame_width_minus_1,
max_frame_height_minus_1,
seq_force_screen_content_tools,
seq_force_integer_mv,
color_range,
matrix_coefficients: u8::try_from(matrix_coefficients).unwrap_or(0),
chroma_sample_position,
separate_uv_delta_q,
})
}
#[must_use]
pub(super) const fn width(&self) -> u32 {
self.max_frame_width_minus_1 + 1
}
#[must_use]
pub(super) const fn height(&self) -> u32 {
self.max_frame_height_minus_1 + 1
}
}
#[cfg(test)]
#[path = "sequence_header_tests.rs"]
mod tests;