use crate::encoder::bitstream::BitWriter;
use crate::encoder::nal::build_nal_unit;
use crate::nal::NalUnitType;
use crate::vui::HrdParameters;
pub fn build_buffering_period_payload(
seq_parameter_set_id: u32,
hrd: &HrdParameters,
nal_delays: &[(u32, u32)],
) -> Vec<u8> {
debug_assert_eq!(
nal_delays.len(),
hrd.cpb_cnt_minus1 as usize + 1,
"one delay pair per SchedSelIdx (§D.1.2)"
);
let bits = hrd.initial_cpb_removal_delay_length_minus1 as u32 + 1;
let mut w = BitWriter::new();
w.ue(seq_parameter_set_id);
for &(delay, offset) in nal_delays {
debug_assert!(delay > 0, "initial_cpb_removal_delay shall be > 0 (§D.2.2)");
w.u(bits, delay);
w.u(bits, offset);
}
finish_sei_payload(w)
}
pub fn build_pic_timing_payload(
hrd: &HrdParameters,
cpb_removal_delay: u32,
dpb_output_delay: u32,
) -> Vec<u8> {
let mut w = BitWriter::new();
w.u(
hrd.cpb_removal_delay_length_minus1 as u32 + 1,
cpb_removal_delay,
);
w.u(
hrd.dpb_output_delay_length_minus1 as u32 + 1,
dpb_output_delay,
);
finish_sei_payload(w)
}
pub fn build_recovery_point_payload(
recovery_frame_cnt: u32,
exact_match_flag: bool,
broken_link_flag: bool,
changing_slice_group_idc: u8,
) -> Vec<u8> {
debug_assert!(changing_slice_group_idc <= 2, "§D.2.8: idc in 0..=2");
let mut w = BitWriter::new();
w.ue(recovery_frame_cnt);
w.u(1, u32::from(exact_match_flag));
w.u(1, u32::from(broken_link_flag));
w.u(2, u32::from(changing_slice_group_idc));
finish_sei_payload(w)
}
fn finish_sei_payload(mut w: BitWriter) -> Vec<u8> {
if !w.byte_aligned() {
w.u(1, 1);
while !w.byte_aligned() {
w.u(1, 0);
}
}
w.into_bytes()
}
pub fn build_sei_nal(messages: &[(u32, Vec<u8>)]) -> Vec<u8> {
let mut rbsp = Vec::new();
for (payload_type, payload) in messages {
let mut t = *payload_type;
while t >= 255 {
rbsp.push(0xFF);
t -= 255;
}
rbsp.push(t as u8);
let mut sz = payload.len();
while sz >= 255 {
rbsp.push(0xFF);
sz -= 255;
}
rbsp.push(sz as u8);
rbsp.extend_from_slice(payload);
}
rbsp.push(0x80);
build_nal_unit(0, NalUnitType::Sei, &rbsp)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::nal::{parse_nal_unit, AnnexBSplitter};
use crate::non_vcl::parse_sei_rbsp;
use crate::sei::{parse_buffering_period, parse_pic_timing, SeiContext};
fn test_hrd() -> HrdParameters {
HrdParameters {
cpb_cnt_minus1: 0,
bit_rate_scale: 0,
cpb_size_scale: 0,
bit_rate_value_minus1: vec![1874], cpb_size_value_minus1: vec![7499], cbr_flag: vec![true],
initial_cpb_removal_delay_length_minus1: 23,
cpb_removal_delay_length_minus1: 23,
dpb_output_delay_length_minus1: 23,
time_offset_length: 24,
}
}
fn ctx_for(hrd: &HrdParameters) -> SeiContext {
SeiContext {
initial_cpb_removal_delay_length_minus1: hrd.initial_cpb_removal_delay_length_minus1,
cpb_removal_delay_length_minus1: hrd.cpb_removal_delay_length_minus1,
dpb_output_delay_length_minus1: hrd.dpb_output_delay_length_minus1,
time_offset_length: hrd.time_offset_length,
nal_hrd_cpb_cnt_minus1: Some(hrd.cpb_cnt_minus1),
vcl_hrd_cpb_cnt_minus1: None,
pic_struct_present_flag: false,
cpb_dpb_delays_present_flag: true,
..SeiContext::default()
}
}
#[test]
fn bp_and_pt_sei_round_trip_through_own_parsers() {
let hrd = test_hrd();
let bp = build_buffering_period_payload(0, &hrd, &[(54_000, 36_000)]);
let pt = build_pic_timing_payload(&hrd, 6, 0);
let nal = build_sei_nal(&[(0, bp), (1, pt)]);
let mut nals: Vec<&[u8]> = AnnexBSplitter::new(&nal).collect();
assert_eq!(nals.len(), 1);
let nu = parse_nal_unit(nals.remove(0)).expect("nal parses");
assert_eq!(nu.header.nal_unit_type, crate::nal::NalUnitType::Sei);
let msgs = parse_sei_rbsp(&nu.rbsp).expect("sei envelope parses");
assert_eq!(msgs.len(), 2);
assert_eq!(msgs[0].payload_type, 0);
assert_eq!(msgs[1].payload_type, 1);
let ctx = ctx_for(&hrd);
let bp = parse_buffering_period(&msgs[0].payload, &ctx).expect("bp parses");
assert_eq!(bp.seq_parameter_set_id, 0);
let nal_hrd = bp.nal_hrd.expect("nal hrd delays present");
assert_eq!(nal_hrd.len(), 1);
assert_eq!(nal_hrd[0].initial_cpb_removal_delay, 54_000);
assert_eq!(nal_hrd[0].initial_cpb_removal_delay_offset, 36_000);
assert!(bp.vcl_hrd.is_none());
let pt = parse_pic_timing(&msgs[1].payload, &ctx).expect("pt parses");
assert_eq!(pt.cpb_removal_delay, 6);
assert_eq!(pt.dpb_output_delay, 0);
assert!(pt.pic_struct.is_none());
}
#[test]
fn odd_width_delays_are_bit_aligned_correctly() {
let mut hrd = test_hrd();
hrd.initial_cpb_removal_delay_length_minus1 = 17; hrd.cpb_removal_delay_length_minus1 = 9; hrd.dpb_output_delay_length_minus1 = 4; let bp = build_buffering_period_payload(3, &hrd, &[(0x2_0001, 0x1_5555)]);
let pt = build_pic_timing_payload(&hrd, 0x3FF, 0x1F);
let ctx = ctx_for(&hrd);
let parsed_bp = parse_buffering_period(&bp, &ctx).expect("bp parses");
assert_eq!(parsed_bp.seq_parameter_set_id, 3);
let d = &parsed_bp.nal_hrd.unwrap()[0];
assert_eq!(d.initial_cpb_removal_delay, 0x2_0001);
assert_eq!(d.initial_cpb_removal_delay_offset, 0x1_5555);
let parsed_pt = parse_pic_timing(&pt, &ctx).expect("pt parses");
assert_eq!(parsed_pt.cpb_removal_delay, 0x3FF);
assert_eq!(parsed_pt.dpb_output_delay, 0x1F);
}
}