use crate::bitstream::BitstreamReader;
use crate::sps::Sps;
#[derive(Debug)]
pub struct Pps {
pub pic_parameter_set_id: u32,
pub seq_parameter_set_id: u32,
pub entropy_coding_mode_flag: bool,
pub bottom_field_pic_order_in_frame_present_flag: bool,
pub num_slice_groups_minus1: u32,
pub num_ref_idx_l0_default_active_minus1: u32,
pub num_ref_idx_l1_default_active_minus1: u32,
pub weighted_pred_flag: bool,
pub weighted_bipred_idc: u8,
pub pic_init_qp_minus26: i32,
pub pic_init_qs_minus26: 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 pic_scaling_matrix_present_flag: bool,
pub second_chroma_qp_index_offset: i32,
pub scaling_list_4x4: [[u8; 16]; 6],
pub scaling_list_8x8: [[u8; 64]; 2],
}
pub fn parse_pps(rbsp: &[u8], sps: Option<&Sps>) -> Result<Pps, &'static str> {
let mut r = BitstreamReader::new(rbsp);
let pic_parameter_set_id = r.read_ue()?;
let seq_parameter_set_id = r.read_ue()?;
let entropy_coding_mode_flag = r.read_bit()? != 0;
let bottom_field_pic_order_in_frame_present_flag = r.read_bit()? != 0;
let num_slice_groups_minus1 = r.read_ue()?;
if num_slice_groups_minus1 > 0 {
return Err("slice groups not yet supported");
}
let num_ref_idx_l0_default_active_minus1 = r.read_ue()?;
let num_ref_idx_l1_default_active_minus1 = r.read_ue()?;
let weighted_pred_flag = r.read_bit()? != 0;
let weighted_bipred_idc = r.read_bits(2)? as u8;
let pic_init_qp_minus26 = r.read_se()?;
let pic_init_qs_minus26 = r.read_se()?;
let chroma_qp_index_offset = r.read_se()?;
let deblocking_filter_control_present_flag = r.read_bit()? != 0;
let constrained_intra_pred_flag = r.read_bit()? != 0;
let redundant_pic_cnt_present_flag = r.read_bit()? != 0;
let mut transform_8x8_mode_flag = false;
let mut pic_scaling_matrix_present_flag = false;
let mut second_chroma_qp_index_offset = chroma_qp_index_offset;
let mut scaling_list_4x4 = sps
.map(|s| s.scaling_list_4x4)
.unwrap_or([crate::sps::FLAT_SCALING_4X4; 6]);
let mut scaling_list_8x8 = sps.map(|s| s.scaling_list_8x8).unwrap_or([[16u8; 64]; 2]);
if r.more_rbsp_data() {
transform_8x8_mode_flag = r.read_bit()? != 0;
pic_scaling_matrix_present_flag = r.read_bit()? != 0;
if pic_scaling_matrix_present_flag {
let count = 6 + if transform_8x8_mode_flag { 2 } else { 0 };
for i in 0..count {
let present = r.read_bit()? != 0;
if present {
if i < 6 {
scaling_list_4x4[i] = crate::sps::parse_scaling_list::<16>(&mut r, 16)?;
} else {
scaling_list_8x8[i - 6] = crate::sps::parse_scaling_list::<64>(&mut r, 64)?;
}
} else if i < 6 {
let sps_has_list = sps.is_some_and(|s| s.seq_scaling_matrix_present_flag);
if !sps_has_list {
scaling_list_4x4[i] = match i {
0 => crate::sps::DEFAULT_SCALING_4X4_INTRA,
3 => crate::sps::DEFAULT_SCALING_4X4_INTER,
_ => scaling_list_4x4[i - 1],
};
}
}
}
}
second_chroma_qp_index_offset = r.read_se()?;
}
Ok(Pps {
pic_parameter_set_id,
seq_parameter_set_id,
entropy_coding_mode_flag,
bottom_field_pic_order_in_frame_present_flag,
num_slice_groups_minus1,
num_ref_idx_l0_default_active_minus1,
num_ref_idx_l1_default_active_minus1,
weighted_pred_flag,
weighted_bipred_idc,
pic_init_qp_minus26,
pic_init_qs_minus26,
chroma_qp_index_offset,
deblocking_filter_control_present_flag,
constrained_intra_pred_flag,
redundant_pic_cnt_present_flag,
transform_8x8_mode_flag,
pic_scaling_matrix_present_flag,
second_chroma_qp_index_offset,
scaling_list_4x4,
scaling_list_8x8,
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::nal::{parse_annex_b, NalUnitType};
#[test]
fn test_parse_pps_single_frame() {
let data = std::fs::read(concat!(
env!("CARGO_MANIFEST_DIR"),
"/testdata/single_frame.h264"
))
.unwrap();
let nals = parse_annex_b(&data);
let pps_nal = nals
.iter()
.find(|n| n.nal_unit_type == NalUnitType::Pps)
.unwrap();
let pps = parse_pps(&pps_nal.rbsp, None).unwrap();
assert_eq!(pps.pic_parameter_set_id, 0);
assert_eq!(pps.seq_parameter_set_id, 0);
assert!(!pps.entropy_coding_mode_flag); assert!(!pps.weighted_pred_flag);
assert!(pps.deblocking_filter_control_present_flag);
assert!(!pps.constrained_intra_pred_flag);
assert!(!pps.redundant_pic_cnt_present_flag);
}
}