use crate::encoder::bitstream::BitWriter;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum FieldPicSignal {
#[default]
FrameMbsOnly,
FramePicture,
TopField,
BottomField,
}
impl FieldPicSignal {
fn write(self, w: &mut BitWriter) {
match self {
FieldPicSignal::FrameMbsOnly => {}
FieldPicSignal::FramePicture => w.u(1, 0),
FieldPicSignal::TopField => {
w.u(1, 1);
w.u(1, 0);
}
FieldPicSignal::BottomField => {
w.u(1, 1);
w.u(1, 1);
}
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct IdrSliceHeaderConfig<'a> {
pub first_mb_in_slice: u32,
pub slice_type_raw: u32,
pub pic_parameter_set_id: u32,
pub colour_plane_id: Option<u8>,
pub frame_num: u32,
pub frame_num_bits: u32,
pub idr_pic_id: u32,
pub pic_order_cnt_lsb: u32,
pub poc_lsb_bits: u32,
pub slice_qp_delta: i32,
pub disable_deblocking_filter_idc: u32,
pub slice_alpha_c0_offset_div2: i32,
pub slice_beta_offset_div2: i32,
pub field: FieldPicSignal,
pub idr: bool,
pub nal_ref_idc: u32,
pub long_term_reference_flag: bool,
pub mmco: &'a [EncMmcoOp],
pub redundant_pic_cnt: Option<u32>,
pub slice_group_change_cycle: Option<(u32, u32)>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EncMmcoOp {
MarkShortTermUnused(u32),
MarkLongTermUnused(u32),
AssignLongTerm(u32, u32),
SetMaxLongTermIdx(u32),
MarkAllUnused,
AssignCurrentLongTerm(u32),
}
fn write_dec_ref_pic_marking_non_idr(w: &mut BitWriter, mmco: &[EncMmcoOp]) {
if mmco.is_empty() {
w.u(1, 0); return;
}
w.u(1, 1); for op in mmco {
match *op {
EncMmcoOp::MarkShortTermUnused(diff) => {
w.ue(1);
w.ue(diff);
}
EncMmcoOp::MarkLongTermUnused(ltpn) => {
w.ue(2);
w.ue(ltpn);
}
EncMmcoOp::AssignLongTerm(diff, ltfi) => {
w.ue(3);
w.ue(diff);
w.ue(ltfi);
}
EncMmcoOp::SetMaxLongTermIdx(max_plus1) => {
w.ue(4);
w.ue(max_plus1);
}
EncMmcoOp::MarkAllUnused => w.ue(5),
EncMmcoOp::AssignCurrentLongTerm(ltfi) => {
w.ue(6);
w.ue(ltfi);
}
}
}
w.ue(0); }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EncRplmOp {
Subtract(u32),
Add(u32),
LongTerm(u32),
}
fn write_rplm_list(w: &mut BitWriter, ops: &[EncRplmOp]) {
if ops.is_empty() {
w.u(1, 0);
return;
}
w.u(1, 1); for op in ops {
match *op {
EncRplmOp::Subtract(v) => {
w.ue(0);
w.ue(v);
}
EncRplmOp::Add(v) => {
w.ue(1);
w.ue(v);
}
EncRplmOp::LongTerm(v) => {
w.ue(2);
w.ue(v);
}
}
}
w.ue(3); }
#[derive(Debug, Clone, Copy)]
pub struct CabacSliceParams {
pub cabac_init_idc: u32,
}
pub fn write_idr_i_slice_header(w: &mut BitWriter, cfg: &IdrSliceHeaderConfig<'_>) {
w.ue(cfg.first_mb_in_slice);
w.ue(cfg.slice_type_raw);
w.ue(cfg.pic_parameter_set_id);
if let Some(plane) = cfg.colour_plane_id {
debug_assert!(plane <= 2, "colour_plane_id must be 0..=2 (§7.4.3)");
w.u(2, plane as u32);
}
w.u(cfg.frame_num_bits, cfg.frame_num);
cfg.field.write(w);
if cfg.idr {
w.ue(cfg.idr_pic_id);
}
w.u(cfg.poc_lsb_bits, cfg.pic_order_cnt_lsb);
if let Some(rpc) = cfg.redundant_pic_cnt {
w.ue(rpc);
}
if cfg.idr {
w.u(1, 0);
w.u(1, u32::from(cfg.long_term_reference_flag));
} else if cfg.nal_ref_idc != 0 {
write_dec_ref_pic_marking_non_idr(w, cfg.mmco);
}
w.se(cfg.slice_qp_delta);
debug_assert!(cfg.disable_deblocking_filter_idc <= 2);
debug_assert!((-6..=6).contains(&cfg.slice_alpha_c0_offset_div2));
debug_assert!((-6..=6).contains(&cfg.slice_beta_offset_div2));
w.ue(cfg.disable_deblocking_filter_idc);
if cfg.disable_deblocking_filter_idc != 1 {
w.se(cfg.slice_alpha_c0_offset_div2);
w.se(cfg.slice_beta_offset_div2);
}
if let Some((value, bits)) = cfg.slice_group_change_cycle {
w.u(bits, value);
}
}
#[derive(Debug, Clone, Copy)]
pub struct PSliceHeaderConfig<'a> {
pub first_mb_in_slice: u32,
pub slice_type_raw: u32,
pub pic_parameter_set_id: u32,
pub colour_plane_id: Option<u8>,
pub frame_num: u32,
pub frame_num_bits: u32,
pub pic_order_cnt_lsb: u32,
pub poc_lsb_bits: u32,
pub slice_qp_delta: i32,
pub disable_deblocking_filter_idc: u32,
pub slice_alpha_c0_offset_div2: i32,
pub slice_beta_offset_div2: i32,
pub nal_ref_idc: u32,
pub cabac: Option<CabacSliceParams>,
pub field: FieldPicSignal,
pub rplm_l0: &'a [EncRplmOp],
pub mmco: &'a [EncMmcoOp],
pub pred_weight_table: Option<ExplicitPredWeightTableL0>,
pub num_ref_idx_l0_active_minus1: Option<u32>,
pub redundant_pic_cnt: Option<u32>,
pub slice_group_change_cycle: Option<(u32, u32)>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ExplicitPredWeightTableL0 {
pub luma_log2_weight_denom: u32,
pub luma: Option<(i32, i32)>,
}
impl ExplicitPredWeightTableL0 {
pub fn effective(&self) -> (i32, i32) {
self.luma
.unwrap_or((1i32 << self.luma_log2_weight_denom, 0))
}
}
pub fn write_p_slice_header(w: &mut BitWriter, cfg: &PSliceHeaderConfig<'_>) {
debug_assert!(cfg.slice_type_raw % 5 == 0); w.ue(cfg.first_mb_in_slice);
w.ue(cfg.slice_type_raw);
w.ue(cfg.pic_parameter_set_id);
if let Some(plane) = cfg.colour_plane_id {
debug_assert!(plane <= 2, "colour_plane_id must be 0..=2 (§7.4.3)");
w.u(2, plane as u32);
}
w.u(cfg.frame_num_bits, cfg.frame_num);
cfg.field.write(w);
w.u(cfg.poc_lsb_bits, cfg.pic_order_cnt_lsb);
if let Some(rpc) = cfg.redundant_pic_cnt {
w.ue(rpc);
}
match cfg.num_ref_idx_l0_active_minus1 {
Some(n) => {
debug_assert!(n <= 31);
w.u(1, 1);
w.ue(n);
}
None => w.u(1, 0),
}
write_rplm_list(w, cfg.rplm_l0);
if let Some(pwt) = cfg.pred_weight_table {
debug_assert!(pwt.luma_log2_weight_denom <= 7);
w.ue(pwt.luma_log2_weight_denom);
w.ue(pwt.luma_log2_weight_denom);
match pwt.luma {
Some((wt, off)) => {
debug_assert!((-128..=127).contains(&wt));
debug_assert!((-128..=127).contains(&off));
w.u(1, 1);
w.se(wt);
w.se(off);
}
None => w.u(1, 0),
}
w.u(1, 0); }
if cfg.nal_ref_idc != 0 {
write_dec_ref_pic_marking_non_idr(w, cfg.mmco);
}
if let Some(c) = cfg.cabac {
debug_assert!(c.cabac_init_idc <= 2);
w.ue(c.cabac_init_idc);
}
w.se(cfg.slice_qp_delta);
debug_assert!(cfg.disable_deblocking_filter_idc <= 2);
debug_assert!((-6..=6).contains(&cfg.slice_alpha_c0_offset_div2));
debug_assert!((-6..=6).contains(&cfg.slice_beta_offset_div2));
w.ue(cfg.disable_deblocking_filter_idc);
if cfg.disable_deblocking_filter_idc != 1 {
w.se(cfg.slice_alpha_c0_offset_div2);
w.se(cfg.slice_beta_offset_div2);
}
if let Some((value, bits)) = cfg.slice_group_change_cycle {
w.u(bits, value);
}
}
#[derive(Debug, Clone, Copy)]
pub struct BSliceHeaderConfig {
pub first_mb_in_slice: u32,
pub slice_type_raw: u32,
pub pic_parameter_set_id: u32,
pub colour_plane_id: Option<u8>,
pub frame_num: u32,
pub frame_num_bits: u32,
pub pic_order_cnt_lsb: u32,
pub poc_lsb_bits: u32,
pub direct_spatial_mv_pred_flag: bool,
pub slice_qp_delta: i32,
pub disable_deblocking_filter_idc: u32,
pub slice_alpha_c0_offset_div2: i32,
pub slice_beta_offset_div2: i32,
pub nal_ref_idc: u32,
pub pred_weight_table: Option<ExplicitBipredWeightTable>,
pub cabac: Option<CabacSliceParams>,
pub field: FieldPicSignal,
}
#[derive(Debug, Clone, Copy)]
pub struct ExplicitBipredWeightTable {
pub luma_log2_weight_denom: u32,
pub luma_weight_l0: i32,
pub luma_offset_l0: i32,
pub luma_weight_l1: i32,
pub luma_offset_l1: i32,
}
pub fn write_b_slice_header(w: &mut BitWriter, cfg: &BSliceHeaderConfig) {
debug_assert!(cfg.slice_type_raw % 5 == 1); w.ue(cfg.first_mb_in_slice);
w.ue(cfg.slice_type_raw);
w.ue(cfg.pic_parameter_set_id);
if let Some(plane) = cfg.colour_plane_id {
debug_assert!(plane <= 2, "colour_plane_id must be 0..=2 (§7.4.3)");
w.u(2, plane as u32);
}
w.u(cfg.frame_num_bits, cfg.frame_num);
cfg.field.write(w);
w.u(cfg.poc_lsb_bits, cfg.pic_order_cnt_lsb);
w.u(
1,
if cfg.direct_spatial_mv_pred_flag {
1
} else {
0
},
);
w.u(1, 0);
w.u(1, 0);
w.u(1, 0);
if let Some(pwt) = cfg.pred_weight_table {
debug_assert!(pwt.luma_log2_weight_denom <= 7);
debug_assert!((-128..=127).contains(&pwt.luma_weight_l0));
debug_assert!((-128..=127).contains(&pwt.luma_offset_l0));
debug_assert!((-128..=127).contains(&pwt.luma_weight_l1));
debug_assert!((-128..=127).contains(&pwt.luma_offset_l1));
w.ue(pwt.luma_log2_weight_denom);
w.ue(pwt.luma_log2_weight_denom);
w.u(1, 1); w.se(pwt.luma_weight_l0);
w.se(pwt.luma_offset_l0);
w.u(1, 0); w.u(1, 1); w.se(pwt.luma_weight_l1);
w.se(pwt.luma_offset_l1);
w.u(1, 0); }
if cfg.nal_ref_idc != 0 {
w.u(1, 0); }
if let Some(c) = cfg.cabac {
debug_assert!(c.cabac_init_idc <= 2);
w.ue(c.cabac_init_idc);
}
w.se(cfg.slice_qp_delta);
debug_assert!(cfg.disable_deblocking_filter_idc <= 2);
debug_assert!((-6..=6).contains(&cfg.slice_alpha_c0_offset_div2));
debug_assert!((-6..=6).contains(&cfg.slice_beta_offset_div2));
w.ue(cfg.disable_deblocking_filter_idc);
if cfg.disable_deblocking_filter_idc != 1 {
w.se(cfg.slice_alpha_c0_offset_div2);
w.se(cfg.slice_beta_offset_div2);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::encoder::nal::build_nal_unit;
use crate::encoder::pps::{build_baseline_pps_rbsp, BaselinePpsConfig};
use crate::encoder::sps::{build_baseline_sps_rbsp, BaselineSpsConfig};
use crate::nal::{parse_nal_unit, AnnexBSplitter, NalHeader, NalUnitType};
use crate::pps::Pps;
use crate::slice_header::{SliceHeader, SliceType};
use crate::sps::Sps;
#[test]
fn idr_slice_header_round_trips_through_decoder_parser() {
let sps_rbsp = build_baseline_sps_rbsp(&BaselineSpsConfig {
bit_depth_luma_minus8: 0,
bit_depth_chroma_minus8: 0,
seq_scaling_lists: None,
interlaced_fields: false,
mbaff: false,
vui: None,
seq_parameter_set_id: 0,
level_idc: 30,
width_in_mbs: 4,
height_in_mbs: 4,
log2_max_frame_num_minus4: 4,
log2_max_poc_lsb_minus4: 4,
max_num_ref_frames: 1,
profile_idc: 66,
chroma_format_idc: 1,
separate_colour_plane: false,
});
let sps = Sps::parse(&sps_rbsp).unwrap();
let pps_rbsp = build_baseline_pps_rbsp(&BaselinePpsConfig::default());
let pps = Pps::parse(&pps_rbsp).unwrap();
let mut w = BitWriter::new();
write_idr_i_slice_header(
&mut w,
&IdrSliceHeaderConfig {
first_mb_in_slice: 0,
slice_type_raw: 7, pic_parameter_set_id: 0,
colour_plane_id: None,
frame_num: 0,
frame_num_bits: sps.log2_max_frame_num_minus4 + 4,
idr_pic_id: 0,
pic_order_cnt_lsb: 0,
poc_lsb_bits: sps.log2_max_pic_order_cnt_lsb_minus4 + 4,
slice_qp_delta: 0,
disable_deblocking_filter_idc: 1,
slice_alpha_c0_offset_div2: 0,
slice_beta_offset_div2: 0,
field: FieldPicSignal::FrameMbsOnly,
idr: true,
nal_ref_idc: 3,
long_term_reference_flag: false,
mmco: &[],
redundant_pic_cnt: None,
slice_group_change_cycle: None,
},
);
w.u(1, 1); w.rbsp_trailing_bits();
let rbsp = w.into_bytes();
let nal = build_nal_unit(3, NalUnitType::SliceIdr, &rbsp);
let mut split = AnnexBSplitter::new(&nal);
let nal_bytes = split.next().unwrap();
let nu = parse_nal_unit(nal_bytes).unwrap();
let header = NalHeader {
forbidden_zero_bit: 0,
nal_ref_idc: 3,
nal_unit_type: NalUnitType::SliceIdr,
};
let parsed = SliceHeader::parse(&nu.rbsp, &sps, &pps, &header).expect("parse slice header");
assert_eq!(parsed.first_mb_in_slice, 0);
assert_eq!(parsed.slice_type, SliceType::I);
assert!(parsed.all_slices_same_type);
assert_eq!(parsed.pic_parameter_set_id, 0);
assert_eq!(parsed.frame_num, 0);
assert_eq!(parsed.idr_pic_id, 0);
assert_eq!(parsed.pic_order_cnt_lsb, 0);
assert_eq!(parsed.slice_qp_delta, 0);
assert_eq!(parsed.disable_deblocking_filter_idc, 1);
}
#[test]
fn p_slice_header_round_trips_through_decoder_parser() {
let sps_rbsp = build_baseline_sps_rbsp(&BaselineSpsConfig {
bit_depth_luma_minus8: 0,
bit_depth_chroma_minus8: 0,
seq_scaling_lists: None,
interlaced_fields: false,
mbaff: false,
vui: None,
seq_parameter_set_id: 0,
level_idc: 30,
width_in_mbs: 4,
height_in_mbs: 4,
log2_max_frame_num_minus4: 4,
log2_max_poc_lsb_minus4: 4,
max_num_ref_frames: 1,
profile_idc: 66,
chroma_format_idc: 1,
separate_colour_plane: false,
});
let sps = Sps::parse(&sps_rbsp).unwrap();
let pps_rbsp = build_baseline_pps_rbsp(&BaselinePpsConfig::default());
let pps = Pps::parse(&pps_rbsp).unwrap();
let mut w = BitWriter::new();
write_p_slice_header(
&mut w,
&PSliceHeaderConfig {
first_mb_in_slice: 0,
slice_type_raw: 5, pic_parameter_set_id: 0,
colour_plane_id: None,
frame_num: 1,
frame_num_bits: sps.log2_max_frame_num_minus4 + 4,
pic_order_cnt_lsb: 2,
poc_lsb_bits: sps.log2_max_pic_order_cnt_lsb_minus4 + 4,
slice_qp_delta: 0,
disable_deblocking_filter_idc: 0,
slice_alpha_c0_offset_div2: 0,
slice_beta_offset_div2: 0,
nal_ref_idc: 2,
cabac: None,
field: FieldPicSignal::FrameMbsOnly,
rplm_l0: &[],
mmco: &[],
pred_weight_table: None,
num_ref_idx_l0_active_minus1: None,
redundant_pic_cnt: None,
slice_group_change_cycle: None,
},
);
w.u(1, 1);
w.rbsp_trailing_bits();
let rbsp = w.into_bytes();
let nal = build_nal_unit(2, NalUnitType::SliceNonIdr, &rbsp);
let mut split = AnnexBSplitter::new(&nal);
let nal_bytes = split.next().unwrap();
let nu = parse_nal_unit(nal_bytes).unwrap();
let header = NalHeader {
forbidden_zero_bit: 0,
nal_ref_idc: 2,
nal_unit_type: NalUnitType::SliceNonIdr,
};
let parsed =
SliceHeader::parse(&nu.rbsp, &sps, &pps, &header).expect("parse P slice header");
assert_eq!(parsed.first_mb_in_slice, 0);
assert_eq!(parsed.slice_type, SliceType::P);
assert!(parsed.all_slices_same_type);
assert_eq!(parsed.frame_num, 1);
assert_eq!(parsed.pic_order_cnt_lsb, 2);
assert_eq!(parsed.disable_deblocking_filter_idc, 0);
assert_eq!(parsed.slice_alpha_c0_offset_div2, 0);
assert_eq!(parsed.slice_beta_offset_div2, 0);
assert!(!parsed.num_ref_idx_active_override_flag);
assert!(parsed.ref_pic_list_modification.modifications_l0.is_empty());
let drm = parsed.dec_ref_pic_marking.as_ref().unwrap();
assert!(drm.adaptive_marking.is_none());
}
#[test]
fn b_slice_header_round_trips_through_decoder_parser() {
let sps_rbsp = build_baseline_sps_rbsp(&BaselineSpsConfig {
bit_depth_luma_minus8: 0,
bit_depth_chroma_minus8: 0,
seq_scaling_lists: None,
interlaced_fields: false,
mbaff: false,
vui: None,
seq_parameter_set_id: 0,
level_idc: 30,
width_in_mbs: 4,
height_in_mbs: 4,
log2_max_frame_num_minus4: 4,
log2_max_poc_lsb_minus4: 4,
max_num_ref_frames: 2,
profile_idc: 77,
chroma_format_idc: 1,
separate_colour_plane: false,
});
let sps = Sps::parse(&sps_rbsp).unwrap();
let pps_rbsp = build_baseline_pps_rbsp(&BaselinePpsConfig::default());
let pps = Pps::parse(&pps_rbsp).unwrap();
let mut w = BitWriter::new();
write_b_slice_header(
&mut w,
&BSliceHeaderConfig {
first_mb_in_slice: 0,
slice_type_raw: 6, pic_parameter_set_id: 0,
colour_plane_id: None,
frame_num: 2,
frame_num_bits: sps.log2_max_frame_num_minus4 + 4,
pic_order_cnt_lsb: 1,
poc_lsb_bits: sps.log2_max_pic_order_cnt_lsb_minus4 + 4,
direct_spatial_mv_pred_flag: true,
slice_qp_delta: 0,
disable_deblocking_filter_idc: 0,
slice_alpha_c0_offset_div2: 0,
slice_beta_offset_div2: 0,
nal_ref_idc: 0, pred_weight_table: None,
cabac: None,
field: FieldPicSignal::FrameMbsOnly,
},
);
w.u(1, 1);
w.rbsp_trailing_bits();
let rbsp = w.into_bytes();
let nal = build_nal_unit(0, NalUnitType::SliceNonIdr, &rbsp);
let mut split = AnnexBSplitter::new(&nal);
let nal_bytes = split.next().unwrap();
let nu = parse_nal_unit(nal_bytes).unwrap();
let header = NalHeader {
forbidden_zero_bit: 0,
nal_ref_idc: 0,
nal_unit_type: NalUnitType::SliceNonIdr,
};
let parsed =
SliceHeader::parse(&nu.rbsp, &sps, &pps, &header).expect("parse B slice header");
assert_eq!(parsed.first_mb_in_slice, 0);
assert_eq!(parsed.slice_type, SliceType::B);
assert!(parsed.all_slices_same_type);
assert_eq!(parsed.frame_num, 2);
assert_eq!(parsed.pic_order_cnt_lsb, 1);
assert!(parsed.direct_spatial_mv_pred_flag);
assert!(!parsed.num_ref_idx_active_override_flag);
assert!(parsed.ref_pic_list_modification.modifications_l0.is_empty());
assert!(parsed.ref_pic_list_modification.modifications_l1.is_empty());
assert!(parsed.dec_ref_pic_marking.is_none());
assert_eq!(parsed.disable_deblocking_filter_idc, 0);
}
#[test]
fn b_slice_header_with_pred_weight_table_round_trips() {
let sps_rbsp = build_baseline_sps_rbsp(&BaselineSpsConfig {
bit_depth_luma_minus8: 0,
bit_depth_chroma_minus8: 0,
seq_scaling_lists: None,
interlaced_fields: false,
mbaff: false,
vui: None,
seq_parameter_set_id: 0,
level_idc: 30,
width_in_mbs: 4,
height_in_mbs: 4,
log2_max_frame_num_minus4: 4,
log2_max_poc_lsb_minus4: 4,
max_num_ref_frames: 2,
profile_idc: 77,
chroma_format_idc: 1,
separate_colour_plane: false,
});
let sps = Sps::parse(&sps_rbsp).unwrap();
let pps_rbsp = build_baseline_pps_rbsp(&BaselinePpsConfig {
redundant_pic_cnt_present_flag: false,
slice_groups: None,
constrained_intra_pred_flag: false,
pic_scaling_lists: None,
chroma_format_idc: 1,
pic_parameter_set_id: 0,
seq_parameter_set_id: 0,
pic_init_qp_minus26: 0,
chroma_qp_index_offset: 0,
weighted_pred_flag: false,
weighted_bipred_idc: 1,
entropy_coding_mode_flag: false,
transform_8x8_mode_flag: false,
});
let pps = Pps::parse(&pps_rbsp).unwrap();
assert_eq!(pps.weighted_bipred_idc, 1);
let pwt = ExplicitBipredWeightTable {
luma_log2_weight_denom: 5,
luma_weight_l0: 45,
luma_offset_l0: 2,
luma_weight_l1: 19,
luma_offset_l1: -1,
};
let mut w = BitWriter::new();
write_b_slice_header(
&mut w,
&BSliceHeaderConfig {
first_mb_in_slice: 0,
slice_type_raw: 6,
pic_parameter_set_id: 0,
colour_plane_id: None,
frame_num: 2,
frame_num_bits: sps.log2_max_frame_num_minus4 + 4,
pic_order_cnt_lsb: 1,
poc_lsb_bits: sps.log2_max_pic_order_cnt_lsb_minus4 + 4,
direct_spatial_mv_pred_flag: true,
slice_qp_delta: 0,
disable_deblocking_filter_idc: 0,
slice_alpha_c0_offset_div2: 0,
slice_beta_offset_div2: 0,
nal_ref_idc: 0,
pred_weight_table: Some(pwt),
cabac: None,
field: FieldPicSignal::FrameMbsOnly,
},
);
w.u(1, 1);
w.rbsp_trailing_bits();
let rbsp = w.into_bytes();
let nal = build_nal_unit(0, NalUnitType::SliceNonIdr, &rbsp);
let mut split = AnnexBSplitter::new(&nal);
let nal_bytes = split.next().unwrap();
let nu = parse_nal_unit(nal_bytes).unwrap();
let header = NalHeader {
forbidden_zero_bit: 0,
nal_ref_idc: 0,
nal_unit_type: NalUnitType::SliceNonIdr,
};
let parsed =
SliceHeader::parse(&nu.rbsp, &sps, &pps, &header).expect("parse B slice header");
let parsed_pwt = parsed.pred_weight_table.as_ref().expect("pwt present");
assert_eq!(parsed_pwt.luma_log2_weight_denom, 5);
assert_eq!(parsed_pwt.chroma_log2_weight_denom, 5);
assert_eq!(parsed_pwt.luma_weights_l0, vec![Some((45, 2))]);
assert_eq!(parsed_pwt.chroma_weights_l0, vec![None]);
assert_eq!(parsed_pwt.luma_weights_l1, vec![Some((19, -1))]);
assert_eq!(parsed_pwt.chroma_weights_l1, vec![None]);
}
}