use crate::bitreader::BitReader;
use crate::Error;
pub const SELECT_SCREEN_CONTENT_TOOLS: u8 = 2;
pub const SELECT_INTEGER_MV: u8 = 2;
pub const CP_UNSPECIFIED: u8 = 2;
pub const TC_UNSPECIFIED: u8 = 2;
pub const MC_UNSPECIFIED: u8 = 2;
pub const CSP_UNKNOWN: u8 = 0;
const CP_BT_709: u8 = 1;
const TC_SRGB: u8 = 13;
const MC_IDENTITY: u8 = 0;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TimingInfo {
pub num_units_in_display_tick: u32,
pub time_scale: u32,
pub equal_picture_interval: bool,
pub num_ticks_per_picture_minus_1: Option<u32>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DecoderModelInfo {
pub buffer_delay_length_minus_1: u8,
pub num_units_in_decoding_tick: u32,
pub buffer_removal_time_length_minus_1: u8,
pub frame_presentation_time_length_minus_1: u8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct OperatingParametersInfo {
pub decoder_buffer_delay: u64,
pub encoder_buffer_delay: u64,
pub low_delay_mode_flag: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct OperatingPoint {
pub operating_point_idc: u16, pub seq_level_idx: u8, pub seq_tier: u8, pub decoder_model_present_for_this_op: bool,
pub operating_parameters_info: Option<OperatingParametersInfo>,
pub initial_display_delay_present_for_this_op: bool,
pub initial_display_delay_minus_1: Option<u8>, }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ColorConfig {
pub high_bitdepth: bool,
pub twelve_bit: bool,
pub bit_depth: u8,
pub mono_chrome: bool,
pub num_planes: u8,
pub color_description_present_flag: bool,
pub color_primaries: u8,
pub transfer_characteristics: u8,
pub matrix_coefficients: u8,
pub color_range: bool,
pub subsampling_x: bool,
pub subsampling_y: bool,
pub chroma_sample_position: u8,
pub separate_uv_delta_q: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SequenceHeader {
pub seq_profile: u8, pub still_picture: bool,
pub reduced_still_picture_header: bool,
pub timing_info_present_flag: bool,
pub timing_info: Option<TimingInfo>,
pub decoder_model_info_present_flag: bool,
pub decoder_model_info: Option<DecoderModelInfo>,
pub initial_display_delay_present_flag: bool,
pub operating_points_cnt_minus_1: u8, pub operating_points: Vec<OperatingPoint>,
pub frame_width_bits_minus_1: u8, pub frame_height_bits_minus_1: u8, pub max_frame_width_minus_1: u32,
pub max_frame_height_minus_1: u32,
pub frame_id_numbers_present_flag: bool,
pub delta_frame_id_length_minus_2: u8, pub additional_frame_id_length_minus_1: u8, pub use_128x128_superblock: bool,
pub enable_filter_intra: bool,
pub enable_intra_edge_filter: bool,
pub enable_interintra_compound: bool,
pub enable_masked_compound: bool,
pub enable_warped_motion: bool,
pub enable_dual_filter: bool,
pub enable_order_hint: bool,
pub enable_jnt_comp: bool,
pub enable_ref_frame_mvs: bool,
pub seq_force_screen_content_tools: u8,
pub seq_force_integer_mv: u8,
pub order_hint_bits: u8, pub enable_superres: bool,
pub enable_cdef: bool,
pub enable_restoration: bool,
pub color_config: ColorConfig,
pub film_grain_params_present: bool,
pub bits_consumed: usize,
}
pub fn parse_sequence_header(payload: &[u8]) -> Result<SequenceHeader, Error> {
let mut br = BitReader::new(payload);
let seq_profile = br.f(3)? as u8;
if seq_profile > 2 {
return Err(Error::ReservedProfile(seq_profile));
}
let still_picture = br.f(1)? == 1;
let reduced_still_picture_header = br.f(1)? == 1;
if reduced_still_picture_header && !still_picture {
return Err(Error::ReducedStillRequiresStill);
}
let timing_info_present_flag;
let mut timing_info = None;
let mut decoder_model_info_present_flag = false;
let mut decoder_model_info = None;
let initial_display_delay_present_flag;
let operating_points_cnt_minus_1;
let mut operating_points: Vec<OperatingPoint> = Vec::new();
if reduced_still_picture_header {
timing_info_present_flag = false;
initial_display_delay_present_flag = false;
operating_points_cnt_minus_1 = 0;
let seq_level_idx = br.f(5)? as u8;
operating_points.push(OperatingPoint {
operating_point_idc: 0,
seq_level_idx,
seq_tier: 0,
decoder_model_present_for_this_op: false,
operating_parameters_info: None,
initial_display_delay_present_for_this_op: false,
initial_display_delay_minus_1: None,
});
} else {
timing_info_present_flag = br.f(1)? == 1;
if timing_info_present_flag {
let ti = parse_timing_info(&mut br)?;
timing_info = Some(ti);
decoder_model_info_present_flag = br.f(1)? == 1;
if decoder_model_info_present_flag {
decoder_model_info = Some(parse_decoder_model_info(&mut br)?);
}
}
initial_display_delay_present_flag = br.f(1)? == 1;
operating_points_cnt_minus_1 = br.f(5)? as u8;
for _ in 0..=operating_points_cnt_minus_1 {
let operating_point_idc = br.f(12)? as u16;
let seq_level_idx = br.f(5)? as u8;
let seq_tier = if seq_level_idx > 7 { br.f(1)? as u8 } else { 0 };
let mut dec_model_present_for_this_op = false;
let mut op_params = None;
if decoder_model_info_present_flag {
dec_model_present_for_this_op = br.f(1)? == 1;
if dec_model_present_for_this_op {
let dmi = decoder_model_info.ok_or(Error::UnexpectedEnd)?;
op_params = Some(parse_operating_parameters_info(&mut br, &dmi)?);
}
}
let mut idd_present_for_op = false;
let mut idd_minus_1 = None;
if initial_display_delay_present_flag {
idd_present_for_op = br.f(1)? == 1;
if idd_present_for_op {
idd_minus_1 = Some(br.f(4)? as u8);
}
}
operating_points.push(OperatingPoint {
operating_point_idc,
seq_level_idx,
seq_tier,
decoder_model_present_for_this_op: dec_model_present_for_this_op,
operating_parameters_info: op_params,
initial_display_delay_present_for_this_op: idd_present_for_op,
initial_display_delay_minus_1: idd_minus_1,
});
}
}
let frame_width_bits_minus_1 = br.f(4)? as u8;
let frame_height_bits_minus_1 = br.f(4)? as u8;
let n_w = u32::from(frame_width_bits_minus_1) + 1;
let n_h = u32::from(frame_height_bits_minus_1) + 1;
let max_frame_width_minus_1 = br.f(n_w)? as u32;
let max_frame_height_minus_1 = br.f(n_h)? as u32;
let frame_id_numbers_present_flag = if reduced_still_picture_header {
false
} else {
br.f(1)? == 1
};
let mut delta_frame_id_length_minus_2 = 0u8;
let mut additional_frame_id_length_minus_1 = 0u8;
if frame_id_numbers_present_flag {
delta_frame_id_length_minus_2 = br.f(4)? as u8;
additional_frame_id_length_minus_1 = br.f(3)? as u8;
}
let use_128x128_superblock = br.f(1)? == 1;
let enable_filter_intra = br.f(1)? == 1;
let enable_intra_edge_filter = br.f(1)? == 1;
let enable_interintra_compound;
let enable_masked_compound;
let enable_warped_motion;
let enable_dual_filter;
let enable_order_hint;
let enable_jnt_comp;
let enable_ref_frame_mvs;
let seq_force_screen_content_tools;
let seq_force_integer_mv;
let order_hint_bits;
if reduced_still_picture_header {
enable_interintra_compound = false;
enable_masked_compound = false;
enable_warped_motion = false;
enable_dual_filter = false;
enable_order_hint = false;
enable_jnt_comp = false;
enable_ref_frame_mvs = false;
seq_force_screen_content_tools = SELECT_SCREEN_CONTENT_TOOLS;
seq_force_integer_mv = SELECT_INTEGER_MV;
order_hint_bits = 0;
} else {
enable_interintra_compound = br.f(1)? == 1;
enable_masked_compound = br.f(1)? == 1;
enable_warped_motion = br.f(1)? == 1;
enable_dual_filter = br.f(1)? == 1;
enable_order_hint = br.f(1)? == 1;
if enable_order_hint {
enable_jnt_comp = br.f(1)? == 1;
enable_ref_frame_mvs = br.f(1)? == 1;
} else {
enable_jnt_comp = false;
enable_ref_frame_mvs = false;
}
let seq_choose_screen_content_tools = br.f(1)? == 1;
seq_force_screen_content_tools = if seq_choose_screen_content_tools {
SELECT_SCREEN_CONTENT_TOOLS
} else {
br.f(1)? as u8
};
seq_force_integer_mv = if seq_force_screen_content_tools > 0 {
let seq_choose_integer_mv = br.f(1)? == 1;
if seq_choose_integer_mv {
SELECT_INTEGER_MV
} else {
br.f(1)? as u8
}
} else {
SELECT_INTEGER_MV
};
if enable_order_hint {
let order_hint_bits_minus_1 = br.f(3)? as u8;
order_hint_bits = order_hint_bits_minus_1 + 1;
} else {
order_hint_bits = 0;
}
}
let enable_superres = br.f(1)? == 1;
let enable_cdef = br.f(1)? == 1;
let enable_restoration = br.f(1)? == 1;
let color_config = parse_color_config(&mut br, seq_profile)?;
let film_grain_params_present = br.f(1)? == 1;
Ok(SequenceHeader {
seq_profile,
still_picture,
reduced_still_picture_header,
timing_info_present_flag,
timing_info,
decoder_model_info_present_flag,
decoder_model_info,
initial_display_delay_present_flag,
operating_points_cnt_minus_1,
operating_points,
frame_width_bits_minus_1,
frame_height_bits_minus_1,
max_frame_width_minus_1,
max_frame_height_minus_1,
frame_id_numbers_present_flag,
delta_frame_id_length_minus_2,
additional_frame_id_length_minus_1,
use_128x128_superblock,
enable_filter_intra,
enable_intra_edge_filter,
enable_interintra_compound,
enable_masked_compound,
enable_warped_motion,
enable_dual_filter,
enable_order_hint,
enable_jnt_comp,
enable_ref_frame_mvs,
seq_force_screen_content_tools,
seq_force_integer_mv,
order_hint_bits,
enable_superres,
enable_cdef,
enable_restoration,
color_config,
film_grain_params_present,
bits_consumed: br.position(),
})
}
fn parse_timing_info(br: &mut BitReader<'_>) -> Result<TimingInfo, Error> {
let num_units_in_display_tick = br.f(32)? as u32;
let time_scale = br.f(32)? as u32;
let equal_picture_interval = br.f(1)? == 1;
let num_ticks_per_picture_minus_1 = if equal_picture_interval {
Some(br.uvlc()?)
} else {
None
};
Ok(TimingInfo {
num_units_in_display_tick,
time_scale,
equal_picture_interval,
num_ticks_per_picture_minus_1,
})
}
fn parse_decoder_model_info(br: &mut BitReader<'_>) -> Result<DecoderModelInfo, Error> {
let buffer_delay_length_minus_1 = br.f(5)? as u8;
let num_units_in_decoding_tick = br.f(32)? as u32;
let buffer_removal_time_length_minus_1 = br.f(5)? as u8;
let frame_presentation_time_length_minus_1 = br.f(5)? as u8;
Ok(DecoderModelInfo {
buffer_delay_length_minus_1,
num_units_in_decoding_tick,
buffer_removal_time_length_minus_1,
frame_presentation_time_length_minus_1,
})
}
fn parse_operating_parameters_info(
br: &mut BitReader<'_>,
dmi: &DecoderModelInfo,
) -> Result<OperatingParametersInfo, Error> {
let n = u32::from(dmi.buffer_delay_length_minus_1) + 1;
let decoder_buffer_delay = br.f(n)?;
let encoder_buffer_delay = br.f(n)?;
let low_delay_mode_flag = br.f(1)? == 1;
Ok(OperatingParametersInfo {
decoder_buffer_delay,
encoder_buffer_delay,
low_delay_mode_flag,
})
}
fn parse_color_config(br: &mut BitReader<'_>, seq_profile: u8) -> Result<ColorConfig, Error> {
let high_bitdepth = br.f(1)? == 1;
let mut twelve_bit = false;
let bit_depth = if seq_profile == 2 && high_bitdepth {
twelve_bit = br.f(1)? == 1;
if twelve_bit {
12
} else {
10
}
} else {
if high_bitdepth {
10
} else {
8
}
};
let mono_chrome = if seq_profile == 1 {
false
} else {
br.f(1)? == 1
};
let num_planes = if mono_chrome { 1 } else { 3 };
let color_description_present_flag = br.f(1)? == 1;
let (color_primaries, transfer_characteristics, matrix_coefficients) =
if color_description_present_flag {
(br.f(8)? as u8, br.f(8)? as u8, br.f(8)? as u8)
} else {
(CP_UNSPECIFIED, TC_UNSPECIFIED, MC_UNSPECIFIED)
};
if mono_chrome {
let color_range = br.f(1)? == 1;
return Ok(ColorConfig {
high_bitdepth,
twelve_bit,
bit_depth,
mono_chrome,
num_planes,
color_description_present_flag,
color_primaries,
transfer_characteristics,
matrix_coefficients,
color_range,
subsampling_x: true,
subsampling_y: true,
chroma_sample_position: CSP_UNKNOWN,
separate_uv_delta_q: false,
});
}
let color_range;
let subsampling_x;
let subsampling_y;
let mut chroma_sample_position = CSP_UNKNOWN;
if color_primaries == CP_BT_709
&& transfer_characteristics == TC_SRGB
&& matrix_coefficients == MC_IDENTITY
{
color_range = true;
subsampling_x = false;
subsampling_y = false;
} else {
color_range = br.f(1)? == 1;
if seq_profile == 0 {
subsampling_x = true;
subsampling_y = true;
} else if seq_profile == 1 {
subsampling_x = false;
subsampling_y = false;
} else {
if bit_depth == 12 {
subsampling_x = br.f(1)? == 1;
subsampling_y = if subsampling_x { br.f(1)? == 1 } else { false };
} else {
subsampling_x = true;
subsampling_y = false;
}
}
if subsampling_x && subsampling_y {
chroma_sample_position = br.f(2)? as u8;
}
}
let separate_uv_delta_q = br.f(1)? == 1;
Ok(ColorConfig {
high_bitdepth,
twelve_bit,
bit_depth,
mono_chrome,
num_planes,
color_description_present_flag,
color_primaries,
transfer_characteristics,
matrix_coefficients,
color_range,
subsampling_x,
subsampling_y,
chroma_sample_position,
separate_uv_delta_q,
})
}
#[cfg(test)]
mod tests {
use super::*;
const TINY_16X16_PROF0: &[u8] = &[0x00, 0x00, 0x00, 0x01, 0x9f, 0xfb, 0xff, 0xf3, 0x00, 0x80];
#[test]
fn parses_tiny_16x16_profile0_fixture() {
let sh = parse_sequence_header(TINY_16X16_PROF0).expect("decodes");
assert_eq!(sh.seq_profile, 0);
assert!(!sh.still_picture);
assert!(!sh.reduced_still_picture_header);
assert!(!sh.timing_info_present_flag);
assert!(!sh.decoder_model_info_present_flag);
assert!(!sh.initial_display_delay_present_flag);
assert_eq!(sh.operating_points_cnt_minus_1, 0);
assert_eq!(sh.operating_points.len(), 1);
let op = &sh.operating_points[0];
assert_eq!(op.operating_point_idc, 0);
assert_eq!(op.seq_level_idx, 0);
assert_eq!(op.seq_tier, 0);
assert!(!op.decoder_model_present_for_this_op);
assert!(!op.initial_display_delay_present_for_this_op);
assert_eq!(sh.max_frame_width_minus_1, 15);
assert_eq!(sh.max_frame_height_minus_1, 15);
assert!(!sh.frame_id_numbers_present_flag);
assert!(sh.use_128x128_superblock);
assert!(sh.enable_filter_intra);
assert!(sh.enable_intra_edge_filter);
assert!(sh.enable_interintra_compound);
assert!(sh.enable_masked_compound);
assert!(sh.enable_warped_motion);
assert!(sh.enable_dual_filter);
assert!(sh.enable_order_hint);
assert!(sh.enable_jnt_comp);
assert!(sh.enable_ref_frame_mvs);
assert_eq!(sh.order_hint_bits, 7);
assert!(!sh.enable_superres);
assert!(sh.enable_cdef);
assert!(sh.enable_restoration);
assert_eq!(sh.color_config.bit_depth, 8);
assert!(!sh.color_config.mono_chrome);
assert_eq!(sh.color_config.num_planes, 3);
assert!(!sh.color_config.color_description_present_flag);
assert_eq!(sh.color_config.color_primaries, CP_UNSPECIFIED);
assert_eq!(sh.color_config.transfer_characteristics, TC_UNSPECIFIED);
assert_eq!(sh.color_config.matrix_coefficients, MC_UNSPECIFIED);
assert!(!sh.color_config.color_range);
assert!(sh.color_config.subsampling_x);
assert!(sh.color_config.subsampling_y);
assert!(!sh.film_grain_params_present);
}
const MONOCHROME_GREY_ONLY: &[u8] =
&[0x00, 0x00, 0x00, 0x02, 0xaf, 0xff, 0xbf, 0xff, 0x35, 0x40];
#[test]
fn parses_monochrome_fixture() {
let sh = parse_sequence_header(MONOCHROME_GREY_ONLY).expect("decodes");
assert_eq!(sh.seq_profile, 0);
assert_eq!(sh.max_frame_width_minus_1, 63);
assert_eq!(sh.max_frame_height_minus_1, 63);
assert!(sh.color_config.mono_chrome);
assert_eq!(sh.color_config.num_planes, 1);
assert!(sh.color_config.color_range);
assert!(sh.color_config.subsampling_x);
assert!(sh.color_config.subsampling_y);
assert!(sh.use_128x128_superblock);
assert_eq!(sh.order_hint_bits, 7);
}
const PROFILE2_422_12BIT: &[u8] = &[
0x40, 0x00, 0x00, 0x02, 0xaf, 0xff, 0xbf, 0xff, 0x3c, 0x44, 0x32, 0x1b, 0x14,
];
#[test]
fn parses_profile2_422_12bit_fixture() {
let sh = parse_sequence_header(PROFILE2_422_12BIT).expect("decodes");
assert_eq!(sh.seq_profile, 2);
assert_eq!(sh.color_config.bit_depth, 12);
assert!(sh.color_config.high_bitdepth);
assert!(sh.color_config.twelve_bit);
assert!(sh.color_config.subsampling_x);
assert!(!sh.color_config.subsampling_y);
assert!(!sh.color_config.mono_chrome);
assert_eq!(sh.max_frame_width_minus_1, 63);
assert_eq!(sh.max_frame_height_minus_1, 63);
}
#[test]
fn rejects_reserved_profile() {
let payload = [0b0110_0000u8];
let err = parse_sequence_header(&payload).expect_err("profile 3 must be rejected");
assert!(matches!(err, Error::ReservedProfile(3)));
}
#[test]
fn rejects_reduced_still_without_still_picture() {
let payload = [0x08u8, 0, 0, 0, 0, 0, 0, 0, 0, 0];
let err = parse_sequence_header(&payload).expect_err("reduced w/o still must be rejected");
assert!(matches!(err, Error::ReducedStillRequiresStill));
}
#[test]
fn unexpected_end_on_truncated_payload() {
let payload = [0x00u8];
let err = parse_sequence_header(&payload).expect_err("truncated payload");
assert_eq!(err, Error::UnexpectedEnd);
}
#[test]
fn reduced_still_picture_path_minimal() {
let bits = "0001100000000000000010000000000000";
let payload = pack_bits_msb(bits);
let sh = parse_sequence_header(&payload).expect("reduced-still SH decodes");
assert!(sh.still_picture);
assert!(sh.reduced_still_picture_header);
assert_eq!(sh.operating_points_cnt_minus_1, 0);
assert_eq!(sh.max_frame_width_minus_1, 0);
assert_eq!(sh.max_frame_height_minus_1, 0);
assert!(sh.use_128x128_superblock);
assert!(!sh.enable_filter_intra);
assert!(!sh.enable_interintra_compound);
assert!(!sh.enable_masked_compound);
assert!(!sh.enable_warped_motion);
assert!(!sh.enable_dual_filter);
assert!(!sh.enable_order_hint);
assert!(!sh.enable_jnt_comp);
assert!(!sh.enable_ref_frame_mvs);
assert_eq!(
sh.seq_force_screen_content_tools,
SELECT_SCREEN_CONTENT_TOOLS
);
assert_eq!(sh.seq_force_integer_mv, SELECT_INTEGER_MV);
assert_eq!(sh.order_hint_bits, 0);
assert_eq!(sh.color_config.bit_depth, 8);
assert!(!sh.color_config.mono_chrome);
assert_eq!(sh.color_config.num_planes, 3);
assert!(sh.color_config.subsampling_x);
assert!(sh.color_config.subsampling_y);
assert_eq!(sh.color_config.chroma_sample_position, CSP_UNKNOWN);
}
fn pack_bits_msb(bits: &str) -> Vec<u8> {
let mut out = Vec::with_capacity(bits.len().div_ceil(8));
let mut cur: u8 = 0;
let mut n: u32 = 0;
for ch in bits.chars() {
cur = (cur << 1) | if ch == '1' { 1 } else { 0 };
n += 1;
if n == 8 {
out.push(cur);
cur = 0;
n = 0;
}
}
if n > 0 {
cur <<= 8 - n;
out.push(cur);
}
out
}
}