use crate::config::EncoderConfig;
use rusty_h264_common::{BitWriter, NalUnit, NalUnitType};
#[derive(Debug, Clone)]
pub struct Sps {
pub profile_idc: u8,
pub constraint_set1_flag: bool,
pub level_idc: u8,
pub seq_parameter_set_id: u32,
pub log2_max_frame_num_minus4: u32,
pub pic_order_cnt_type: u32,
pub log2_max_pic_order_cnt_lsb_minus4: u32,
pub max_num_ref_frames: u32,
pub pic_width_in_mbs_minus1: u32,
pub pic_height_in_map_units_minus1: u32,
pub frame_crop_right: u32,
pub frame_crop_bottom: u32,
}
impl Sps {
pub fn from_config(cfg: &EncoderConfig) -> Self {
let mb_w = cfg.mb_width();
let mb_h = cfg.mb_height();
let crop_right = (mb_w * 16 - cfg.width) / 2;
let crop_bottom = (mb_h * 16 - cfg.height) / 2;
Self {
profile_idc: cfg.profile.profile_idc(),
constraint_set1_flag: true, level_idc: cfg.level_idc,
seq_parameter_set_id: 0,
log2_max_frame_num_minus4: 0, pic_order_cnt_type: 0,
log2_max_pic_order_cnt_lsb_minus4: 0, max_num_ref_frames: cfg.num_ref_frames.max(1),
pic_width_in_mbs_minus1: (mb_w - 1) as u32,
pic_height_in_map_units_minus1: (mb_h - 1) as u32,
frame_crop_right: crop_right as u32,
frame_crop_bottom: crop_bottom as u32,
}
}
pub fn write_rbsp(&self, w: &mut BitWriter) {
w.write_bits(self.profile_idc as u32, 8);
let mut constraints = 0u32;
if self.constraint_set1_flag {
constraints |= 1 << 6; }
w.write_bits(constraints, 8);
w.write_bits(self.level_idc as u32, 8);
w.write_ue(self.seq_parameter_set_id);
w.write_ue(self.log2_max_frame_num_minus4);
w.write_ue(self.pic_order_cnt_type);
if self.pic_order_cnt_type == 0 {
w.write_ue(self.log2_max_pic_order_cnt_lsb_minus4);
}
w.write_ue(self.max_num_ref_frames);
w.write_bit(false); w.write_ue(self.pic_width_in_mbs_minus1);
w.write_ue(self.pic_height_in_map_units_minus1);
w.write_bit(true); w.write_bit(false); let cropping = self.frame_crop_right != 0 || self.frame_crop_bottom != 0;
w.write_bit(cropping); if cropping {
w.write_ue(0); w.write_ue(self.frame_crop_right);
w.write_ue(0); w.write_ue(self.frame_crop_bottom);
}
w.write_bit(false); w.rbsp_trailing_bits();
}
pub fn to_nal(&self) -> NalUnit {
let mut w = BitWriter::new();
self.write_rbsp(&mut w);
NalUnit::new(3, NalUnitType::Sps, w.into_bytes())
}
}
#[derive(Debug, Clone)]
pub struct Pps {
pub pic_parameter_set_id: u32,
pub seq_parameter_set_id: u32,
pub num_ref_idx_l0_default_active_minus1: u32,
pub pic_init_qp_minus26: i32,
pub deblocking_filter_control_present_flag: bool,
}
impl Pps {
pub fn from_config(cfg: &EncoderConfig) -> Self {
Self {
pic_parameter_set_id: 0,
seq_parameter_set_id: 0,
num_ref_idx_l0_default_active_minus1: cfg.num_ref_frames.max(1) - 1,
pic_init_qp_minus26: cfg.qp as i32 - 26,
deblocking_filter_control_present_flag: true,
}
}
pub fn write_rbsp(&self, w: &mut BitWriter) {
w.write_ue(self.pic_parameter_set_id);
w.write_ue(self.seq_parameter_set_id);
w.write_bit(false); w.write_bit(false); w.write_ue(0); w.write_ue(self.num_ref_idx_l0_default_active_minus1);
w.write_ue(0); w.write_bit(false); w.write_bits(0, 2); w.write_se(self.pic_init_qp_minus26);
w.write_se(0); w.write_se(0); w.write_bit(self.deblocking_filter_control_present_flag);
w.write_bit(false); w.write_bit(false); w.rbsp_trailing_bits();
}
pub fn to_nal(&self) -> NalUnit {
let mut w = BitWriter::new();
self.write_rbsp(&mut w);
NalUnit::new(3, NalUnitType::Pps, w.into_bytes())
}
}
#[cfg(test)]
mod tests {
use super::*;
use rusty_h264_common::{nal::emulation_unprevent, BitReader};
#[test]
fn sps_roundtrips_through_reader() {
let cfg = EncoderConfig::new(1920, 1080); let sps = Sps::from_config(&cfg);
let nal = sps.to_nal();
let rbsp = emulation_unprevent(&nal.rbsp);
let mut r = BitReader::new(&rbsp);
assert_eq!(r.read_bits(8).unwrap(), 66); let constraints = r.read_bits(8).unwrap();
assert_eq!((constraints >> 6) & 1, 1); assert_eq!(r.read_bits(8).unwrap(), 30); assert_eq!(r.read_ue().unwrap(), 0); assert_eq!(r.read_ue().unwrap(), 0); assert_eq!(r.read_ue().unwrap(), 0); assert_eq!(r.read_ue().unwrap(), 0); assert_eq!(r.read_ue().unwrap(), 1); assert!(!r.read_bit().unwrap()); assert_eq!(r.read_ue().unwrap(), 119); assert_eq!(r.read_ue().unwrap(), 67); assert!(r.read_bit().unwrap()); assert!(!r.read_bit().unwrap()); assert!(r.read_bit().unwrap()); assert_eq!(r.read_ue().unwrap(), 0); assert_eq!(r.read_ue().unwrap(), 0); assert_eq!(r.read_ue().unwrap(), 0); assert_eq!(r.read_ue().unwrap(), 4); }
#[test]
fn pps_roundtrips_through_reader() {
let cfg = EncoderConfig::new(640, 480);
let pps = Pps::from_config(&cfg);
let nal = pps.to_nal();
let rbsp = emulation_unprevent(&nal.rbsp);
let mut r = BitReader::new(&rbsp);
assert_eq!(r.read_ue().unwrap(), 0); assert_eq!(r.read_ue().unwrap(), 0); assert!(!r.read_bit().unwrap()); assert!(!r.read_bit().unwrap()); assert_eq!(r.read_ue().unwrap(), 0); assert_eq!(r.read_ue().unwrap(), 0); assert_eq!(r.read_ue().unwrap(), 0); assert!(!r.read_bit().unwrap()); assert_eq!(r.read_bits(2).unwrap(), 0); assert_eq!(r.read_se().unwrap(), 0); }
}