use std::io::Read;
use media_codec_bitstream::{BigEndian, BitReader};
use media_core::{invalid_data_error, none_param_error, not_found_error, Result};
use smallvec::SmallVec;
use crate::{
constants::{MAX_PPS_COUNT, MAX_QP_COUNT, MAX_REFS, MAX_SLICE_GROUPS, MAX_SPS_COUNT},
ps::ParameterSets,
scaling_list::{ScalingList4x4, ScalingList8x8},
sps::Sps,
tables::CHROMA_QP,
};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ChromaQpTables {
pub cb: [u8; MAX_QP_COUNT],
pub cr: [u8; MAX_QP_COUNT],
}
impl Default for ChromaQpTables {
fn default() -> Self {
Self {
cb: [0u8; MAX_QP_COUNT],
cr: [0u8; MAX_QP_COUNT],
}
}
}
impl ChromaQpTables {
pub fn new(chroma_qp_index_offset: i32, second_chroma_qp_index_offset: i32, bit_depth: u32) -> Self {
let mut tables = Self::default();
Self::build_qp_table(&mut tables.cb, chroma_qp_index_offset, bit_depth);
Self::build_qp_table(&mut tables.cr, second_chroma_qp_index_offset, bit_depth);
tables
}
fn build_qp_table(table: &mut [u8; MAX_QP_COUNT], index: i32, depth: u32) {
let max_qp = (51 + 6 * (depth as i32 - 8)) as usize;
for (i, item) in table.iter_mut().enumerate().take(max_qp + 1) {
let qp = (i as i32 + index).clamp(0, max_qp as i32) as usize;
*item = CHROMA_QP[depth as usize - 8][qp];
}
}
#[inline]
pub fn get_cb(&self, qp_y: i32) -> u8 {
self.cb[qp_y.clamp(0, MAX_QP_COUNT as i32 - 1) as usize]
}
#[inline]
pub fn get_cr(&self, qp_y: i32) -> u8 {
self.cr[qp_y.clamp(0, MAX_QP_COUNT as i32 - 1) as usize]
}
#[inline]
pub fn as_array(&self) -> [&[u8; MAX_QP_COUNT]; 2] {
[&self.cb, &self.cr]
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[repr(u8)]
pub enum SliceGroupMapType {
#[default]
Interleaved = 0,
Dispersed = 1,
ForegroundLeftover = 2,
BoxOut = 3,
RasterScan = 4,
Wipe = 5,
Explicit = 6,
}
impl From<u32> for SliceGroupMapType {
fn from(value: u32) -> Self {
match value {
0 => SliceGroupMapType::Interleaved,
1 => SliceGroupMapType::Dispersed,
2 => SliceGroupMapType::ForegroundLeftover,
3 => SliceGroupMapType::BoxOut,
4 => SliceGroupMapType::RasterScan,
5 => SliceGroupMapType::Wipe,
6 => SliceGroupMapType::Explicit,
_ => SliceGroupMapType::Interleaved,
}
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct SliceGroupParams {
pub slice_group_map_type: SliceGroupMapType,
pub run_length: SmallVec<[u32; MAX_SLICE_GROUPS]>,
pub top_left: SmallVec<[u32; MAX_SLICE_GROUPS]>,
pub bottom_right: SmallVec<[u32; MAX_SLICE_GROUPS]>,
pub slice_group_change_direction_flag: bool,
pub slice_group_change_rate: u32,
pub pic_size_in_map_units: u32,
pub slice_group_id: Vec<u32>,
}
impl SliceGroupParams {
pub fn parse<R: Read>(reader: &mut BitReader<R, BigEndian>, num_slice_groups: u32) -> Result<Self> {
let slice_group_map_type_val = reader.read_ue()?;
let slice_group_map_type = SliceGroupMapType::from(slice_group_map_type_val);
let mut params = Self {
slice_group_map_type,
..Default::default()
};
match slice_group_map_type {
SliceGroupMapType::Interleaved => {
let num_groups = num_slice_groups as usize;
params.run_length.reserve(num_groups);
for _ in 0..num_groups {
params.run_length.push(reader.read_ue()? + 1);
}
}
SliceGroupMapType::Dispersed => {
}
SliceGroupMapType::ForegroundLeftover => {
let num_groups = (num_slice_groups - 1) as usize;
params.top_left.reserve(num_groups);
params.bottom_right.reserve(num_groups);
for _ in 0..num_groups {
params.top_left.push(reader.read_ue()?);
params.bottom_right.push(reader.read_ue()?);
}
}
SliceGroupMapType::BoxOut | SliceGroupMapType::RasterScan | SliceGroupMapType::Wipe => {
params.slice_group_change_direction_flag = reader.read_bit()?;
params.slice_group_change_rate = reader.read_ue()? + 1;
}
SliceGroupMapType::Explicit => {
params.pic_size_in_map_units = reader.read_ue()? + 1;
let num_map_units = params.pic_size_in_map_units as usize;
let bits_needed = (32 - (num_slice_groups - 1).leading_zeros()).max(1);
params.slice_group_id = Vec::with_capacity(num_map_units);
for _ in 0..num_map_units {
params.slice_group_id.push(reader.read_var(bits_needed)?);
}
}
}
Ok(params)
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Pps {
pub pic_parameter_set_id: u8,
pub seq_parameter_set_id: u8,
pub entropy_coding_mode_flag: bool,
pub bottom_field_pic_order_in_frame_present_flag: bool,
pub num_slice_groups: u32,
pub slice_group_params: Option<SliceGroupParams>,
pub num_ref_idx_l0_default_active: u32,
pub num_ref_idx_l1_default_active: u32,
pub weighted_pred_flag: bool,
pub weighted_bipred_idc: u8,
pub pic_init_qp: i32,
pub pic_init_qs: i32,
pub chroma_qp_index_offset: i32,
pub deblocking_filter_control_present_flag: bool,
pub constrained_intra_pred_flag: bool,
pub redundant_pic_cnt_present_flag: bool,
pub transform_8x8_mode_flag: bool,
pub scaling_matrix: Option<PpsScalingMatrix>,
pub second_chroma_qp_index_offset: i32,
pub chroma_qp_tables: ChromaQpTables,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PpsScalingMatrix {
pub scaling_list_4x4: [ScalingList4x4; 6],
pub scaling_list_8x8: SmallVec<[ScalingList8x8; 6]>,
pub scaling_list_4x4_present: [bool; 6],
pub scaling_list_8x8_present: SmallVec<[bool; 6]>,
}
impl Default for PpsScalingMatrix {
fn default() -> Self {
Self {
scaling_list_4x4: [ScalingList4x4::default(); 6],
scaling_list_8x8: smallvec::smallvec![ScalingList8x8::default(); 2],
scaling_list_4x4_present: [false; 6],
scaling_list_8x8_present: smallvec::smallvec![false, false],
}
}
}
impl PpsScalingMatrix {
pub fn new() -> Self {
Self::default()
}
pub fn with_8x8_count(count: usize) -> Self {
Self {
scaling_list_4x4: [ScalingList4x4::default(); 6],
scaling_list_8x8: smallvec::smallvec![ScalingList8x8::default(); count],
scaling_list_4x4_present: [false; 6],
scaling_list_8x8_present: smallvec::smallvec![false; count],
}
}
pub fn parse<R: Read>(reader: &mut BitReader<R, BigEndian>, transform_8x8_mode_flag: bool, chroma_format_idc: u32) -> Result<Self> {
let num_8x8_lists = if !transform_8x8_mode_flag {
0
} else if chroma_format_idc == 3 {
6
} else {
2
};
let total_lists = 6 + num_8x8_lists;
let mut matrix = Self::with_8x8_count(num_8x8_lists);
for i in 0..6 {
let present = reader.read_bit()?;
matrix.scaling_list_4x4_present[i] = present;
if present {
matrix.scaling_list_4x4[i] = ScalingList4x4::parse(reader)?;
} else {
matrix.scaling_list_4x4[i] = ScalingList4x4::fallback();
}
}
for i in 0..num_8x8_lists {
if 6 + i < total_lists {
let present = reader.read_bit()?;
matrix.scaling_list_8x8_present[i] = present;
if present {
matrix.scaling_list_8x8[i] = ScalingList8x8::parse(reader)?;
} else {
matrix.scaling_list_8x8[i] = ScalingList8x8::fallback();
}
}
}
Ok(matrix)
}
}
impl Pps {
pub fn parse_ids(data: &[u8]) -> Result<(u8, u8)> {
let mut reader = BitReader::new(data);
Self::parse_ids_from_bit_reader(&mut reader)
}
pub fn parse_with_sps(data: &[u8], sps: &Sps) -> Result<Self> {
let mut reader = BitReader::new(data);
Self::parse_from_bit_reader(&mut reader, Some(sps), None)
}
pub fn parse_with_param_sets(data: &[u8], param_sets: &ParameterSets) -> Result<Self> {
let mut reader = BitReader::new(data);
Self::parse_from_bit_reader(&mut reader, None, Some(param_sets))
}
pub fn parse_ids_from_bit_reader<R: Read>(reader: &mut BitReader<R, BigEndian>) -> Result<(u8, u8)> {
let pic_parameter_set_id = reader.read_ue()?;
if pic_parameter_set_id as usize >= MAX_PPS_COUNT {
return Err(invalid_data_error!("pps_id", pic_parameter_set_id));
}
let pic_parameter_set_id = pic_parameter_set_id as u8;
let seq_parameter_set_id = reader.read_ue()?;
if seq_parameter_set_id as usize >= MAX_SPS_COUNT {
return Err(invalid_data_error!("sps_id", seq_parameter_set_id));
}
let seq_parameter_set_id = seq_parameter_set_id as u8;
Ok((pic_parameter_set_id, seq_parameter_set_id))
}
pub fn parse_from_bit_reader<R: Read>(
reader: &mut BitReader<R, BigEndian>,
sps: Option<&Sps>,
param_sets: Option<&ParameterSets>,
) -> Result<Self> {
let (pic_parameter_set_id, seq_parameter_set_id) = Self::parse_ids_from_bit_reader(reader)?;
let sps = if let Some(sps) = sps {
if sps.seq_parameter_set_id != seq_parameter_set_id {
return Err(invalid_data_error!("sps_id", seq_parameter_set_id));
};
sps
} else if let Some(param_sets) = param_sets {
param_sets.get_sps(seq_parameter_set_id as u32).ok_or_else(|| not_found_error!("sps_id", seq_parameter_set_id))?
} else {
return Err(none_param_error!("sps or param_sets"));
};
let chroma_format_idc = sps.chroma_format as u32;
let qp_offset = (sps.bit_depth_luma as i32 - 8) * 6;
let entropy_coding_mode_flag = reader.read_bit()?;
let bottom_field_pic_order_in_frame_present_flag = reader.read_bit()?;
let num_slice_groups = reader.read_ue()? + 1;
let slice_group_params = if num_slice_groups > 1 {
Some(SliceGroupParams::parse(reader, num_slice_groups)?)
} else {
None
};
let num_ref_idx_l0_default_active = reader.read_ue()? + 1;
if num_ref_idx_l0_default_active as usize > MAX_REFS {
return Err(invalid_data_error!("num_ref_idx_l0_default_active", num_ref_idx_l0_default_active));
}
let num_ref_idx_l1_default_active = reader.read_ue()? + 1;
if num_ref_idx_l1_default_active as usize > MAX_REFS {
return Err(invalid_data_error!("num_ref_idx_l1_default_active", num_ref_idx_l1_default_active));
}
let weighted_pred_flag = reader.read_bit()?;
let weighted_bipred_idc = reader.read::<2, u8>()?;
let pic_init_qp = reader.read_se()? + 26 + qp_offset;
let pic_init_qs = reader.read_se()? + 26 + qp_offset;
let chroma_qp_index_offset = reader.read_se()?;
if !(-12..=12).contains(&chroma_qp_index_offset) {
return Err(invalid_data_error!("chroma_qp_index_offset", chroma_qp_index_offset));
}
let deblocking_filter_control_present_flag = reader.read_bit()?;
let constrained_intra_pred_flag = reader.read_bit()?;
let redundant_pic_cnt_present_flag = reader.read_bit()?;
let mut transform_8x8_mode_flag = false;
let mut scaling_matrix = None;
let mut second_chroma_qp_index_offset = chroma_qp_index_offset;
if let Some((flag, second_offset, matrix)) = (|| -> Option<_> {
let flag = reader.read_bit().ok()?;
let scaling_present = reader.read_bit().ok()?;
let second_offset = reader.read_se().ok().filter(|v| (-12..=12).contains(v))?;
let matrix = scaling_present.then(|| PpsScalingMatrix::parse(reader, flag, chroma_format_idc).ok()).flatten();
Some((flag, second_offset, matrix))
})() {
transform_8x8_mode_flag = flag;
second_chroma_qp_index_offset = second_offset;
scaling_matrix = matrix;
}
let chroma_qp_tables = ChromaQpTables::new(chroma_qp_index_offset, second_chroma_qp_index_offset, sps.bit_depth_luma as u32);
Ok(Self {
pic_parameter_set_id,
seq_parameter_set_id,
entropy_coding_mode_flag,
bottom_field_pic_order_in_frame_present_flag,
num_slice_groups,
slice_group_params,
num_ref_idx_l0_default_active,
num_ref_idx_l1_default_active,
weighted_pred_flag,
weighted_bipred_idc,
pic_init_qp,
pic_init_qs,
chroma_qp_index_offset,
deblocking_filter_control_present_flag,
constrained_intra_pred_flag,
redundant_pic_cnt_present_flag,
transform_8x8_mode_flag,
scaling_matrix,
second_chroma_qp_index_offset,
chroma_qp_tables,
})
}
#[inline]
pub fn is_cabac(&self) -> bool {
self.entropy_coding_mode_flag
}
#[inline]
pub fn is_cavlc(&self) -> bool {
!self.entropy_coding_mode_flag
}
#[inline]
pub fn has_weighted_prediction(&self) -> bool {
self.weighted_pred_flag
}
#[inline]
pub fn has_weighted_bi_prediction(&self) -> bool {
self.weighted_bipred_idc != 0
}
#[inline]
pub fn weighted_bi_prediction_mode(&self) -> u8 {
self.weighted_bipred_idc
}
#[inline]
pub fn has_8x8_transform(&self) -> bool {
self.transform_8x8_mode_flag
}
#[inline]
pub fn has_deblocking_filter_control(&self) -> bool {
self.deblocking_filter_control_present_flag
}
#[inline]
pub fn is_constrained_intra_pred(&self) -> bool {
self.constrained_intra_pred_flag
}
#[inline]
pub fn has_redundant_pic_cnt(&self) -> bool {
self.redundant_pic_cnt_present_flag
}
#[inline]
pub fn cb_qp_offset(&self) -> i32 {
self.chroma_qp_index_offset
}
#[inline]
pub fn cr_qp_offset(&self) -> i32 {
self.second_chroma_qp_index_offset
}
}