rusty_h264_encoder/
params.rs1use crate::config::EncoderConfig;
8use rusty_h264_common::{BitWriter, NalUnit, NalUnitType};
9
10#[derive(Debug, Clone)]
12pub struct Sps {
13 pub profile_idc: u8,
14 pub constraint_set1_flag: bool,
15 pub level_idc: u8,
16 pub seq_parameter_set_id: u32,
17 pub log2_max_frame_num_minus4: u32,
18 pub pic_order_cnt_type: u32,
19 pub log2_max_pic_order_cnt_lsb_minus4: u32,
20 pub max_num_ref_frames: u32,
21 pub pic_width_in_mbs_minus1: u32,
22 pub pic_height_in_map_units_minus1: u32,
23 pub frame_crop_right: u32,
26 pub frame_crop_bottom: u32,
27}
28
29impl Sps {
30 pub fn from_config(cfg: &EncoderConfig) -> Self {
32 let mb_w = cfg.mb_width();
33 let mb_h = cfg.mb_height();
34 let crop_right = (mb_w * 16 - cfg.width) / 2;
37 let crop_bottom = (mb_h * 16 - cfg.height) / 2;
38 Self {
39 profile_idc: cfg.profile.profile_idc(),
40 constraint_set1_flag: true, level_idc: cfg.level_idc,
42 seq_parameter_set_id: 0,
43 log2_max_frame_num_minus4: 0, pic_order_cnt_type: 0,
45 log2_max_pic_order_cnt_lsb_minus4: 0, max_num_ref_frames: cfg.num_ref_frames.max(1),
47 pic_width_in_mbs_minus1: (mb_w - 1) as u32,
48 pic_height_in_map_units_minus1: (mb_h - 1) as u32,
49 frame_crop_right: crop_right as u32,
50 frame_crop_bottom: crop_bottom as u32,
51 }
52 }
53
54 pub fn write_rbsp(&self, w: &mut BitWriter) {
56 w.write_bits(self.profile_idc as u32, 8);
57 let mut constraints = 0u32;
59 if self.constraint_set1_flag {
60 constraints |= 1 << 6; }
62 w.write_bits(constraints, 8);
63 w.write_bits(self.level_idc as u32, 8);
64 w.write_ue(self.seq_parameter_set_id);
65 if self.profile_idc >= 100 {
68 w.write_ue(1); w.write_ue(0); w.write_ue(0); w.write_bit(false); w.write_bit(false); }
74 w.write_ue(self.log2_max_frame_num_minus4);
75 w.write_ue(self.pic_order_cnt_type);
76 if self.pic_order_cnt_type == 0 {
77 w.write_ue(self.log2_max_pic_order_cnt_lsb_minus4);
78 }
79 w.write_ue(self.max_num_ref_frames);
80 w.write_bit(false); w.write_ue(self.pic_width_in_mbs_minus1);
82 w.write_ue(self.pic_height_in_map_units_minus1);
83 w.write_bit(true); w.write_bit(false); let cropping = self.frame_crop_right != 0 || self.frame_crop_bottom != 0;
86 w.write_bit(cropping); if cropping {
88 w.write_ue(0); w.write_ue(self.frame_crop_right);
90 w.write_ue(0); w.write_ue(self.frame_crop_bottom);
92 }
93 w.write_bit(false); w.rbsp_trailing_bits();
95 }
96
97 pub fn to_nal(&self) -> NalUnit {
99 let mut w = BitWriter::new();
100 self.write_rbsp(&mut w);
101 NalUnit::new(3, NalUnitType::Sps, w.into_bytes())
102 }
103}
104
105#[derive(Debug, Clone)]
107pub struct Pps {
108 pub pic_parameter_set_id: u32,
109 pub seq_parameter_set_id: u32,
110 pub num_ref_idx_l0_default_active_minus1: u32,
111 pub pic_init_qp_minus26: i32,
112 pub deblocking_filter_control_present_flag: bool,
113 pub weighted_bipred_idc: u8,
118 pub entropy_coding_mode_flag: bool,
120 pub transform_8x8_mode_flag: bool,
123}
124
125impl Pps {
126 pub fn from_config(cfg: &EncoderConfig) -> Self {
128 Self {
129 pic_parameter_set_id: 0,
130 seq_parameter_set_id: 0,
131 num_ref_idx_l0_default_active_minus1: cfg.num_ref_frames.max(1) - 1,
132 pic_init_qp_minus26: cfg.qp as i32 - 26,
133 deblocking_filter_control_present_flag: true,
137 weighted_bipred_idc: if cfg.bframes > 0 { 2 } else { 0 },
138 entropy_coding_mode_flag: cfg.cabac,
139 transform_8x8_mode_flag: cfg.transform_8x8,
140 }
141 }
142
143 pub fn write_rbsp(&self, w: &mut BitWriter) {
145 w.write_ue(self.pic_parameter_set_id);
146 w.write_ue(self.seq_parameter_set_id);
147 w.write_bit(self.entropy_coding_mode_flag); w.write_bit(false); w.write_ue(0); w.write_ue(self.num_ref_idx_l0_default_active_minus1);
151 w.write_ue(0); w.write_bit(false); w.write_bits(self.weighted_bipred_idc as u32, 2); w.write_se(self.pic_init_qp_minus26);
155 w.write_se(0); w.write_se(0); w.write_bit(self.deblocking_filter_control_present_flag);
158 w.write_bit(false); w.write_bit(false); if self.transform_8x8_mode_flag {
163 w.write_bit(true); w.write_bit(false); w.write_se(0); }
167 w.rbsp_trailing_bits();
168 }
169
170 pub fn to_nal(&self) -> NalUnit {
172 let mut w = BitWriter::new();
173 self.write_rbsp(&mut w);
174 NalUnit::new(3, NalUnitType::Pps, w.into_bytes())
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181 use rusty_h264_common::{nal::emulation_unprevent, BitReader};
182
183 #[test]
186 fn default_config_signals_main_profile_and_cabac() {
187 let cfg = EncoderConfig::new(320, 240);
188 let sps = Sps::from_config(&cfg);
189 let rbsp = emulation_unprevent(&sps.to_nal().rbsp);
190 let mut r = BitReader::new(&rbsp);
191 assert_eq!(r.read_bits(8).unwrap(), 77, "profile_idc should be Main");
192
193 let pps = Pps::from_config(&cfg);
194 let rbsp = emulation_unprevent(&pps.to_nal().rbsp);
195 let mut r = BitReader::new(&rbsp);
196 assert_eq!(r.read_ue().unwrap(), 0); assert_eq!(r.read_ue().unwrap(), 0); assert!(r.read_bit().unwrap(), "entropy_coding_mode should be CABAC");
199 }
200
201 #[test]
202 fn sps_roundtrips_through_reader() {
203 let mut cfg = EncoderConfig::new(1920, 1080); cfg.profile = rusty_h264_common::Profile::ConstrainedBaseline;
207 cfg.cabac = false;
208 let sps = Sps::from_config(&cfg);
209 let nal = sps.to_nal();
210
211 let rbsp = emulation_unprevent(&nal.rbsp);
212 let mut r = BitReader::new(&rbsp);
213 assert_eq!(r.read_bits(8).unwrap(), 66); let constraints = r.read_bits(8).unwrap();
215 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); }
233
234 #[test]
235 fn pps_roundtrips_through_reader() {
236 let mut cfg = EncoderConfig::new(640, 480);
237 cfg.profile = rusty_h264_common::Profile::ConstrainedBaseline;
238 cfg.cabac = false;
239 let pps = Pps::from_config(&cfg);
240 let nal = pps.to_nal();
241
242 let rbsp = emulation_unprevent(&nal.rbsp);
243 let mut r = BitReader::new(&rbsp);
244 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); }
255}