use crate::encoder::deblock::{
deblock_recon_field, deblock_recon_with_chroma_array_type, MbDeblockInfo,
};
use crate::encoder::nal::build_nal_unit;
use crate::encoder::pps::{build_baseline_pps_rbsp, BaselinePpsConfig};
use crate::encoder::slice::{
write_b_slice_header, write_idr_i_slice_header, write_p_slice_header, BSliceHeaderConfig,
EncMmcoOp, EncRplmOp, FieldPicSignal, IdrSliceHeaderConfig, PSliceHeaderConfig,
};
use crate::encoder::sps::{build_baseline_sps_rbsp, BaselineSpsConfig};
use crate::encoder::{
min_level_idc_for_picture_size, EncodedFrameRef, Encoder, EncoderConfig, FrameRefPartitionMv,
YuvFrame,
};
use crate::encoder::{BitWriter, CavlcNcGrid, IntraGrid, MvGrid};
use crate::nal::NalUnitType;
use crate::transform::{qp_bd_offset, qp_y_to_qp_c_with_bd_offset};
#[derive(Debug, Clone)]
pub struct PaffConfig {
pub width: u32,
pub frame_height: u32,
pub qp: i32,
pub p_fields: bool,
pub frame_picture_indices: Vec<usize>,
pub cross_parity_first_bottom: bool,
pub idr_frame_first: bool,
pub b_fields: bool,
pub b_temporal_direct: bool,
pub long_term_anchor: bool,
pub mmco_unpair_first_top: bool,
pub transform_8x8: bool,
pub b_implicit_weight: bool,
pub b_reference_fields: bool,
}
type ReconField = (Vec<u8>, Vec<u8>, Vec<u8>, i32);
struct AnchorField {
y: Vec<u8>,
u: Vec<u8>,
v: Vec<u8>,
poc: i32,
partition_mvs: Vec<FrameRefPartitionMv>,
}
impl AnchorField {
fn as_frame_ref(&self, width: u32, field_h: u32) -> EncodedFrameRef<'_> {
EncodedFrameRef {
width,
height: field_h,
recon_y: &self.y,
recon_u: &self.u,
recon_v: &self.v,
partition_mvs: &self.partition_mvs,
pic_order_cnt: self.poc,
}
}
}
fn partition_mvs_from_grid(mv_grid: &MvGrid) -> Vec<FrameRefPartitionMv> {
let mut out = Vec::with_capacity(mv_grid.slots.len() * 4);
for slot in &mv_grid.slots {
for part in 0..4usize {
let (mv, ref_idx, is_intra) = if !slot.available || slot.is_intra {
(crate::mv_deriv::Mv::ZERO, -1, true)
} else {
(slot.mv_l0_8x8[part], slot.ref_idx_l0_8x8[part], false)
};
out.push(FrameRefPartitionMv {
mv_l0: (
mv.x.clamp(i16::MIN as i32, i16::MAX as i32) as i16,
mv.y.clamp(i16::MIN as i32, i16::MAX as i32) as i16,
),
ref_idx_l0: ref_idx.clamp(-1, 127) as i8,
is_intra,
});
}
}
out
}
pub struct PaffEncoded {
pub annex_b: Vec<u8>,
pub recon_frames: Vec<(Vec<u8>, Vec<u8>, Vec<u8>)>,
}
fn extract_field(
y: &[u8],
u: &[u8],
v: &[u8],
width: usize,
frame_height: usize,
bottom: bool,
) -> (Vec<u8>, Vec<u8>, Vec<u8>) {
let cw = width / 2;
let ch = frame_height / 2;
let start = usize::from(bottom);
let mut fy = Vec::with_capacity(width * frame_height / 2);
for row in (start..frame_height).step_by(2) {
fy.extend_from_slice(&y[row * width..(row + 1) * width]);
}
let mut fu = Vec::with_capacity(cw * ch / 2);
let mut fv = Vec::with_capacity(cw * ch / 2);
for row in (start..ch).step_by(2) {
fu.extend_from_slice(&u[row * cw..(row + 1) * cw]);
fv.extend_from_slice(&v[row * cw..(row + 1) * cw]);
}
(fy, fu, fv)
}
fn interleave_planes(top: &[u8], bottom: &[u8], width: usize) -> Vec<u8> {
debug_assert_eq!(top.len(), bottom.len());
let field_rows = top.len() / width;
let mut out = vec![0u8; top.len() * 2];
for r in 0..field_rows {
out[(2 * r) * width..(2 * r + 1) * width].copy_from_slice(&top[r * width..(r + 1) * width]);
out[(2 * r + 1) * width..(2 * r + 2) * width]
.copy_from_slice(&bottom[r * width..(r + 1) * width]);
}
out
}
fn encode_i_slice_data(
enc: &Encoder,
src: &YuvFrame<'_>,
sw: &mut BitWriter,
) -> (Vec<u8>, Vec<u8>, Vec<u8>, Vec<MbDeblockInfo>) {
let width_mbs = enc.cfg.width / 16;
let height_mbs = enc.cfg.height / 16;
let chroma_width = (enc.cfg.width / 2) as usize;
let chroma_height = (enc.cfg.height / 2) as usize;
let mut recon_y = vec![0u8; (enc.cfg.width * enc.cfg.height) as usize];
let mut recon_u = vec![0u8; chroma_width * chroma_height];
let mut recon_v = vec![0u8; chroma_width * chroma_height];
let qp_y = enc.cfg.qp;
let qp_c = qp_y_to_qp_c_with_bd_offset(qp_y, 0, qp_bd_offset(enc.cfg.bit_depth_chroma_minus8));
let mut nc_grid = CavlcNcGrid::new(width_mbs, height_mbs);
let mut intra_grid = IntraGrid::new(width_mbs as usize, height_mbs as usize);
let mut infos = vec![MbDeblockInfo::default(); (width_mbs * height_mbs) as usize];
let mut qp_tracker = super::MbQpTracker::new(qp_y);
for mb_y in 0..height_mbs as usize {
for mb_x in 0..width_mbs as usize {
let dbl = enc.encode_mb(
src,
mb_x,
mb_y,
qp_y,
qp_c,
chroma_width,
chroma_height,
&mut recon_y,
&mut recon_u,
&mut recon_v,
sw,
&mut nc_grid,
&mut intra_grid,
&mut qp_tracker,
);
infos[mb_y * width_mbs as usize + mb_x] = dbl;
}
}
(recon_y, recon_u, recon_v, infos)
}
type PSliceDataOut = (
Vec<u8>,
Vec<u8>,
Vec<u8>,
Vec<MbDeblockInfo>,
Vec<FrameRefPartitionMv>,
);
fn encode_p_slice_data(
enc: &Encoder,
src: &YuvFrame<'_>,
prev: &EncodedFrameRef<'_>,
sw: &mut BitWriter,
) -> PSliceDataOut {
let width_mbs = enc.cfg.width / 16;
let height_mbs = enc.cfg.height / 16;
let chroma_width = (enc.cfg.width / 2) as usize;
let chroma_height = (enc.cfg.height / 2) as usize;
let mut recon_y = vec![0u8; (enc.cfg.width * enc.cfg.height) as usize];
let mut recon_u = vec![0u8; chroma_width * chroma_height];
let mut recon_v = vec![0u8; chroma_width * chroma_height];
let qp_y = enc.cfg.qp;
let qp_c = qp_y_to_qp_c_with_bd_offset(qp_y, 0, qp_bd_offset(enc.cfg.bit_depth_chroma_minus8));
let mut nc_grid = CavlcNcGrid::new(width_mbs, height_mbs);
let mut intra_grid = IntraGrid::new(width_mbs as usize, height_mbs as usize);
let mut mv_grid = MvGrid::new(width_mbs as usize, height_mbs as usize);
let mut infos = vec![MbDeblockInfo::default(); (width_mbs * height_mbs) as usize];
let mut pending_skip: u32 = 0;
let mut qp_tracker = super::MbQpTracker::new(qp_y);
for mb_y in 0..height_mbs as usize {
for mb_x in 0..width_mbs as usize {
let dbl = enc.encode_p_mb_with_intra_fallback(
src,
prev,
prev.recon_y,
mb_x,
mb_y,
qp_y,
qp_c,
chroma_width,
chroma_height,
&mut recon_y,
&mut recon_u,
&mut recon_v,
sw,
&mut nc_grid,
&mut intra_grid,
&mut mv_grid,
&mut pending_skip,
&mut qp_tracker,
);
infos[mb_y * width_mbs as usize + mb_x] = dbl;
}
}
if pending_skip > 0 {
sw.ue(pending_skip);
}
let partition_mvs = partition_mvs_from_grid(&mv_grid);
(recon_y, recon_u, recon_v, infos, partition_mvs)
}
fn encode_b_slice_data(
enc: &Encoder,
src: &YuvFrame<'_>,
ref_l0: &EncodedFrameRef<'_>,
ref_l1: &EncodedFrameRef<'_>,
curr_poc: i32,
sw: &mut BitWriter,
weighted: Option<super::WeightedBipredLuma>,
) -> PSliceDataOut {
let width = enc.cfg.width as usize;
let width_mbs = enc.cfg.width / 16;
let height_mbs = enc.cfg.height / 16;
let chroma_width = (enc.cfg.width / 2) as usize;
let chroma_height = (enc.cfg.height / 2) as usize;
let mut recon_y = vec![0u8; (enc.cfg.width * enc.cfg.height) as usize];
let mut recon_u = vec![0u8; chroma_width * chroma_height];
let mut recon_v = vec![0u8; chroma_width * chroma_height];
let qp_y = enc.cfg.qp;
let qp_c = qp_y_to_qp_c_with_bd_offset(qp_y, 0, qp_bd_offset(enc.cfg.bit_depth_chroma_minus8));
let mut nc_grid = CavlcNcGrid::new(width_mbs, height_mbs);
let mut intra_grid = IntraGrid::new(width_mbs as usize, height_mbs as usize);
let mut mv_grid_l0 = MvGrid::new(width_mbs as usize, height_mbs as usize);
let mut mv_grid_l1 = MvGrid::new(width_mbs as usize, height_mbs as usize);
let mut infos = vec![MbDeblockInfo::default(); (width_mbs * height_mbs) as usize];
let mut pending_skip: u32 = 0;
let mut qp_tracker = crate::encoder::MbQpTracker::new(qp_y);
for mb_y in 0..height_mbs as usize {
for mb_x in 0..width_mbs as usize {
let dbl = enc.encode_b_mb_with_intra_fallback(
src,
ref_l0,
ref_l1,
mb_x,
mb_y,
qp_y,
qp_c,
chroma_width,
chroma_height,
width,
&mut recon_y,
&mut recon_u,
&mut recon_v,
sw,
&mut nc_grid,
&mut intra_grid,
&mut mv_grid_l0,
&mut mv_grid_l1,
&mut pending_skip,
curr_poc,
weighted,
&mut qp_tracker,
);
infos[mb_y * width_mbs as usize + mb_x] = dbl;
}
}
sw.ue(pending_skip);
let partition_mvs = partition_mvs_from_grid(&mv_grid_l0);
(recon_y, recon_u, recon_v, infos, partition_mvs)
}
fn implicit_field_weights(curr_poc: i32, poc0: i32, poc1: i32) -> super::WeightedBipredLuma {
let td = (poc1 - poc0).clamp(-128, 127);
let (w0, w1) = if td == 0 {
(32, 32)
} else {
let tb = (curr_poc - poc0).clamp(-128, 127);
let tx = (16384 + (td / 2).abs()) / td;
let dsf = ((tb * tx + 32) >> 6).clamp(-1024, 1023);
let w1 = dsf >> 2;
if !(-64..=128).contains(&w1) {
(32, 32)
} else {
(64 - w1, w1)
}
};
super::WeightedBipredLuma {
log2_wd: 5,
weight_l0: w0,
offset_l0: 0,
weight_l1: w1,
offset_l1: 0,
apply_chroma: true,
}
}
pub fn encode_paff_sequence(cfg: &PaffConfig, frames: &[(&[u8], &[u8], &[u8])]) -> PaffEncoded {
assert!(cfg.width % 16 == 0, "width must be MB-aligned");
assert!(
cfg.frame_height % 32 == 0,
"frame height must be a multiple of 32 (each field MB-aligned)",
);
assert!(!frames.is_empty());
assert!(
!cfg.p_fields || cfg.frame_picture_indices.is_empty(),
"frame pictures inside a P-field sequence are out of scope",
);
assert!(
!cfg.frame_picture_indices.contains(&0),
"frame 0 is the IDR field pair",
);
assert!(
!cfg.cross_parity_first_bottom || cfg.p_fields,
"cross_parity_first_bottom is a P-field axis",
);
assert!(
!cfg.idr_frame_first || cfg.p_fields,
"idr_frame_first is a P-field axis",
);
assert!(
!(cfg.idr_frame_first && cfg.cross_parity_first_bottom),
"idr_frame_first replaces frame 0's field pair",
);
assert!(!cfg.b_fields || cfg.p_fields, "b_fields is a P-field axis");
assert!(
!cfg.b_fields || (!cfg.cross_parity_first_bottom && !cfg.idr_frame_first),
"b_fields is incompatible with the other optional axes",
);
assert!(
!cfg.b_temporal_direct || cfg.b_fields,
"b_temporal_direct requires b_fields",
);
assert!(
!cfg.b_implicit_weight || cfg.b_fields,
"b_implicit_weight requires b_fields",
);
assert!(
!cfg.b_reference_fields || cfg.b_fields,
"b_reference_fields requires b_fields",
);
assert!(
!(cfg.b_implicit_weight && cfg.b_reference_fields),
"b_implicit_weight and b_reference_fields are separate stream axes",
);
assert!(
!cfg.long_term_anchor
|| (cfg.p_fields
&& !cfg.b_fields
&& !cfg.cross_parity_first_bottom
&& !cfg.idr_frame_first
&& !cfg.mmco_unpair_first_top),
"long_term_anchor is a standalone P-field axis",
);
assert!(
!cfg.mmco_unpair_first_top
|| (cfg.p_fields
&& !cfg.b_fields
&& !cfg.cross_parity_first_bottom
&& !cfg.idr_frame_first),
"mmco_unpair_first_top is a standalone P-field axis",
);
let width = cfg.width as usize;
let frame_h = cfg.frame_height as usize;
let field_h = cfg.frame_height / 2;
let width_mbs = cfg.width / 16;
let frame_h_mbs = cfg.frame_height / 16;
let log2_max_frame_num_minus4: u32 = 4;
let log2_max_poc_lsb_minus4: u32 = 4;
let frame_num_bits = log2_max_frame_num_minus4 + 4;
let poc_lsb_bits = log2_max_poc_lsb_minus4 + 4;
let profile_idc: u8 = if cfg.transform_8x8 { 100 } else { 77 };
let max_num_ref_frames: u32 = if cfg.b_reference_fields { 4 } else { 2 };
let mk_cfg = |h: u32| {
let mut c = EncoderConfig::new(cfg.width, h);
c.qp = cfg.qp;
c.profile_idc = profile_idc;
c.max_num_ref_frames = max_num_ref_frames;
c.transform_8x8 = cfg.transform_8x8;
c
};
let field_enc = Encoder::new(mk_cfg(field_h));
let frame_enc = Encoder::new(mk_cfg(cfg.frame_height));
let xpar_enc = Encoder::new({
let mut c = mk_cfg(field_h);
c.table_8_10_cy_offset = 2;
c
});
let sps_rbsp = build_baseline_sps_rbsp(&BaselineSpsConfig {
seq_parameter_set_id: 0,
level_idc: min_level_idc_for_picture_size(width_mbs, frame_h_mbs),
width_in_mbs: width_mbs,
height_in_mbs: frame_h_mbs,
log2_max_frame_num_minus4,
log2_max_poc_lsb_minus4,
max_num_ref_frames,
profile_idc,
chroma_format_idc: 1,
separate_colour_plane: false,
seq_scaling_lists: None,
bit_depth_luma_minus8: 0,
bit_depth_chroma_minus8: 0,
interlaced_fields: true,
mbaff: false,
vui: None,
});
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: cfg.qp - 26,
chroma_qp_index_offset: 0,
weighted_pred_flag: false,
weighted_bipred_idc: if cfg.b_implicit_weight { 2 } else { 0 },
entropy_coding_mode_flag: false,
transform_8x8_mode_flag: cfg.transform_8x8,
});
let mut stream: Vec<u8> = Vec::new();
stream.extend_from_slice(&build_nal_unit(3, NalUnitType::Sps, &sps_rbsp));
stream.extend_from_slice(&build_nal_unit(3, NalUnitType::Pps, &pps_rbsp));
if cfg.b_fields {
return encode_paff_b_sequence(cfg, frames, stream, frame_num_bits, poc_lsb_bits);
}
let mut last_top: Option<ReconField> = None;
let mut last_bottom: Option<ReconField> = None;
let mut anchor_top: Option<ReconField> = None;
let mut anchor_bottom: Option<ReconField> = None;
let mut recon_frames = Vec::with_capacity(frames.len());
for (k, &(fy, fu, fv)) in frames.iter().enumerate() {
assert_eq!(fy.len(), width * frame_h);
let frame_num = (k as u32) % (1 << frame_num_bits);
if k == 0 && cfg.idr_frame_first {
let src = YuvFrame {
width: cfg.width,
height: cfg.frame_height,
y: fy,
u: fu,
v: fv,
};
let mut sw = BitWriter::new();
write_idr_i_slice_header(
&mut sw,
&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,
idr_pic_id: 0,
pic_order_cnt_lsb: 0,
poc_lsb_bits,
slice_qp_delta: 0,
disable_deblocking_filter_idc: 0,
slice_alpha_c0_offset_div2: 0,
slice_beta_offset_div2: 0,
field: FieldPicSignal::FramePicture,
idr: true,
nal_ref_idc: 3,
long_term_reference_flag: false,
mmco: &[],
redundant_pic_cnt: None,
slice_group_change_cycle: None,
},
);
let (mut ry, mut ru, mut rv, infos) = encode_i_slice_data(&frame_enc, &src, &mut sw);
sw.rbsp_trailing_bits();
stream.extend_from_slice(&build_nal_unit(3, NalUnitType::SliceIdr, &sw.into_bytes()));
deblock_recon_with_chroma_array_type(
cfg.width,
cfg.frame_height,
cfg.width / 2,
cfg.frame_height / 2,
&mut ry,
&mut ru,
&mut rv,
&infos,
0,
width_mbs,
frame_h_mbs,
1,
);
let (ty, tu, tv) = extract_field(&ry, &ru, &rv, width, frame_h, false);
let (by, bu, bv) = extract_field(&ry, &ru, &rv, width, frame_h, true);
last_top = Some((ty, tu, tv, 0));
last_bottom = Some((by, bu, bv, 0));
recon_frames.push((ry, ru, rv));
continue;
}
if cfg.frame_picture_indices.contains(&k) {
let src = YuvFrame {
width: cfg.width,
height: cfg.frame_height,
y: fy,
u: fu,
v: fv,
};
let mut sw = BitWriter::new();
write_idr_i_slice_header(
&mut sw,
&IdrSliceHeaderConfig {
first_mb_in_slice: 0,
slice_type_raw: 7,
pic_parameter_set_id: 0,
colour_plane_id: None,
frame_num,
frame_num_bits,
idr_pic_id: 0,
pic_order_cnt_lsb: (2 * k as u32) % (1 << poc_lsb_bits),
poc_lsb_bits,
slice_qp_delta: 0,
disable_deblocking_filter_idc: 0,
slice_alpha_c0_offset_div2: 0,
slice_beta_offset_div2: 0,
field: FieldPicSignal::FramePicture,
idr: false,
nal_ref_idc: 2,
long_term_reference_flag: false,
mmco: &[],
redundant_pic_cnt: None,
slice_group_change_cycle: None,
},
);
let (mut ry, mut ru, mut rv, infos) = encode_i_slice_data(&frame_enc, &src, &mut sw);
sw.rbsp_trailing_bits();
stream.extend_from_slice(&build_nal_unit(
2,
NalUnitType::SliceNonIdr,
&sw.into_bytes(),
));
deblock_recon_with_chroma_array_type(
cfg.width,
cfg.frame_height,
cfg.width / 2,
cfg.frame_height / 2,
&mut ry,
&mut ru,
&mut rv,
&infos,
0,
width_mbs,
frame_h_mbs,
1,
);
recon_frames.push((ry, ru, rv));
continue;
}
let mut pair_recon: Vec<(Vec<u8>, Vec<u8>, Vec<u8>)> = Vec::with_capacity(2);
for bottom in [false, true] {
let (sy, su, sv) = extract_field(fy, fu, fv, width, frame_h, bottom);
let src = YuvFrame {
width: cfg.width,
height: field_h,
y: &sy,
u: &su,
v: &sv,
};
let field_signal = if bottom {
FieldPicSignal::BottomField
} else {
FieldPicSignal::TopField
};
let poc_lsb = (2 * k as u32 + u32::from(bottom)) % (1 << poc_lsb_bits);
let is_idr = k == 0 && !bottom;
let ref_field = if cfg.long_term_anchor && k > 0 {
if bottom {
&anchor_bottom
} else {
&anchor_top
}
} else if cfg.mmco_unpair_first_top && k == 2 && !bottom {
&anchor_top
} else if bottom {
&last_bottom
} else {
&last_top
};
let cross = cfg.cross_parity_first_bottom && k == 0 && bottom;
let (as_p, p_enc, p_ref) = if cross {
(last_top.is_some(), &xpar_enc, &last_top)
} else {
(cfg.p_fields && ref_field.is_some(), &field_enc, ref_field)
};
let mut sw = BitWriter::new();
sw.set_field_scan(true);
if as_p {
write_p_slice_header(
&mut sw,
&PSliceHeaderConfig {
first_mb_in_slice: 0,
slice_type_raw: 5,
pic_parameter_set_id: 0,
colour_plane_id: None,
frame_num,
frame_num_bits,
pic_order_cnt_lsb: poc_lsb,
poc_lsb_bits,
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: field_signal,
rplm_l0: if cfg.long_term_anchor && (k > 1 || (k == 1 && bottom)) {
&[EncRplmOp::LongTerm(1)]
} else {
&[]
},
mmco: if cfg.long_term_anchor && k == 1 && !bottom {
&[
EncMmcoOp::SetMaxLongTermIdx(1),
EncMmcoOp::AssignLongTerm(1, 0),
EncMmcoOp::AssignLongTerm(2, 0),
]
} else if cfg.mmco_unpair_first_top && k == 1 && bottom {
&[EncMmcoOp::MarkShortTermUnused(0)]
} else {
&[]
},
pred_weight_table: None,
num_ref_idx_l0_active_minus1: None,
redundant_pic_cnt: None,
slice_group_change_cycle: None,
},
);
} else {
write_idr_i_slice_header(
&mut sw,
&IdrSliceHeaderConfig {
first_mb_in_slice: 0,
slice_type_raw: 7,
pic_parameter_set_id: 0,
colour_plane_id: None,
frame_num,
frame_num_bits,
idr_pic_id: 0,
pic_order_cnt_lsb: poc_lsb,
poc_lsb_bits,
slice_qp_delta: 0,
disable_deblocking_filter_idc: 0,
slice_alpha_c0_offset_div2: 0,
slice_beta_offset_div2: 0,
field: field_signal,
idr: is_idr,
nal_ref_idc: if is_idr { 3 } else { 2 },
long_term_reference_flag: false,
mmco: &[],
redundant_pic_cnt: None,
slice_group_change_cycle: None,
},
);
}
let (mut ry, mut ru, mut rv, infos) = if as_p {
let (py, pu, pv, ppoc) = p_ref.as_ref().unwrap();
let prev = EncodedFrameRef {
width: cfg.width,
height: field_h,
recon_y: py,
recon_u: pu,
recon_v: pv,
partition_mvs: &[],
pic_order_cnt: *ppoc,
};
let (ry, ru, rv, infos, _mvs) = encode_p_slice_data(p_enc, &src, &prev, &mut sw);
(ry, ru, rv, infos)
} else {
encode_i_slice_data(&field_enc, &src, &mut sw)
};
sw.rbsp_trailing_bits();
let (ref_idc, nal_type) = if is_idr {
(3, NalUnitType::SliceIdr)
} else {
(2, NalUnitType::SliceNonIdr)
};
stream.extend_from_slice(&build_nal_unit(ref_idc, nal_type, &sw.into_bytes()));
deblock_recon_field(
cfg.width,
field_h,
cfg.width / 2,
field_h / 2,
&mut ry,
&mut ru,
&mut rv,
&infos,
0,
width_mbs,
field_h / 16,
1,
);
let slot = (ry.clone(), ru.clone(), rv.clone(), poc_lsb as i32);
if k == 0 {
if bottom {
anchor_bottom = Some(slot.clone());
} else {
anchor_top = Some(slot.clone());
}
}
if bottom {
last_bottom = Some(slot);
} else {
last_top = Some(slot);
}
pair_recon.push((ry, ru, rv));
}
let (ty, tu, tv) = &pair_recon[0];
let (by, bu, bv) = &pair_recon[1];
recon_frames.push((
interleave_planes(ty, by, width),
interleave_planes(tu, bu, width / 2),
interleave_planes(tv, bv, width / 2),
));
}
PaffEncoded {
annex_b: stream,
recon_frames,
}
}
fn encode_paff_b_sequence(
cfg: &PaffConfig,
frames: &[(&[u8], &[u8], &[u8])],
mut stream: Vec<u8>,
frame_num_bits: u32,
poc_lsb_bits: u32,
) -> PaffEncoded {
let width = cfg.width as usize;
let frame_h = cfg.frame_height as usize;
let field_h = cfg.frame_height / 2;
let width_mbs = cfg.width / 16;
let mk_cfg = || {
let mut c = EncoderConfig::new(cfg.width, field_h);
c.qp = cfg.qp;
c.profile_idc = if cfg.transform_8x8 { 100u8 } else { 77 };
c.max_num_ref_frames = 2;
c.transform_8x8 = cfg.transform_8x8;
c
};
let field_enc = Encoder::new(mk_cfg());
let b_enc = Encoder::new({
let mut c = mk_cfg();
c.direct_temporal_mv_pred = cfg.b_temporal_direct;
c
});
let mut recon_frames: Vec<(Vec<u8>, Vec<u8>, Vec<u8>)> =
vec![(Vec::new(), Vec::new(), Vec::new()); frames.len()];
let encode_anchor_pair = |d: usize,
frame_num: u32,
prev: Option<&[AnchorField; 2]>,
rplm_l0: &[EncRplmOp],
stream: &mut Vec<u8>,
recon_frames: &mut Vec<(Vec<u8>, Vec<u8>, Vec<u8>)>|
-> [AnchorField; 2] {
let (fy, fu, fv) = frames[d];
assert_eq!(fy.len(), width * frame_h);
let mut out: Vec<AnchorField> = Vec::with_capacity(2);
for bottom in [false, true] {
let (sy, su, sv) = extract_field(fy, fu, fv, width, frame_h, bottom);
let src = YuvFrame {
width: cfg.width,
height: field_h,
y: &sy,
u: &su,
v: &sv,
};
let field_signal = if bottom {
FieldPicSignal::BottomField
} else {
FieldPicSignal::TopField
};
let poc = 2 * d as i32 + i32::from(bottom);
let poc_lsb = (poc as u32) % (1 << poc_lsb_bits);
let is_idr = d == 0 && !bottom;
let mut sw = BitWriter::new();
sw.set_field_scan(true);
let (mut ry, mut ru, mut rv, infos, mvs) = if let Some(prev) = prev {
let prev_field = &prev[usize::from(bottom)];
let prev_ref = prev_field.as_frame_ref(cfg.width, field_h);
write_p_slice_header(
&mut sw,
&PSliceHeaderConfig {
first_mb_in_slice: 0,
slice_type_raw: 5,
pic_parameter_set_id: 0,
colour_plane_id: None,
frame_num,
frame_num_bits,
pic_order_cnt_lsb: poc_lsb,
poc_lsb_bits,
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: field_signal,
rplm_l0,
mmco: &[],
pred_weight_table: None,
num_ref_idx_l0_active_minus1: None,
redundant_pic_cnt: None,
slice_group_change_cycle: None,
},
);
encode_p_slice_data(&field_enc, &src, &prev_ref, &mut sw)
} else {
write_idr_i_slice_header(
&mut sw,
&IdrSliceHeaderConfig {
first_mb_in_slice: 0,
slice_type_raw: 7,
pic_parameter_set_id: 0,
colour_plane_id: None,
frame_num,
frame_num_bits,
idr_pic_id: 0,
pic_order_cnt_lsb: poc_lsb,
poc_lsb_bits,
slice_qp_delta: 0,
disable_deblocking_filter_idc: 0,
slice_alpha_c0_offset_div2: 0,
slice_beta_offset_div2: 0,
field: field_signal,
idr: is_idr,
nal_ref_idc: if is_idr { 3 } else { 2 },
long_term_reference_flag: false,
mmco: &[],
redundant_pic_cnt: None,
slice_group_change_cycle: None,
},
);
let (ry, ru, rv, infos) = encode_i_slice_data(&field_enc, &src, &mut sw);
let n_parts = (width_mbs as usize) * ((field_h / 16) as usize) * 4;
let mvs = vec![
FrameRefPartitionMv {
mv_l0: (0, 0),
ref_idx_l0: -1,
is_intra: true,
};
n_parts
];
(ry, ru, rv, infos, mvs)
};
sw.rbsp_trailing_bits();
let (ref_idc, nal_type) = if is_idr {
(3, NalUnitType::SliceIdr)
} else {
(2, NalUnitType::SliceNonIdr)
};
stream.extend_from_slice(&build_nal_unit(ref_idc, nal_type, &sw.into_bytes()));
deblock_recon_field(
cfg.width,
field_h,
cfg.width / 2,
field_h / 2,
&mut ry,
&mut ru,
&mut rv,
&infos,
0,
width_mbs,
field_h / 16,
1,
);
out.push(AnchorField {
y: ry,
u: ru,
v: rv,
poc,
partition_mvs: mvs,
});
}
recon_frames[d] = (
interleave_planes(&out[0].y, &out[1].y, width),
interleave_planes(&out[0].u, &out[1].u, width / 2),
interleave_planes(&out[0].v, &out[1].v, width / 2),
);
let mut it = out.into_iter();
[it.next().unwrap(), it.next().unwrap()]
};
let encode_b_pair = |d: usize,
frame_num: u32,
l0: &[AnchorField; 2],
l1: &[AnchorField; 2],
is_ref: bool,
enc: &Encoder,
stream: &mut Vec<u8>,
recon_frames: &mut Vec<(Vec<u8>, Vec<u8>, Vec<u8>)>|
-> Option<[AnchorField; 2]> {
let (fy, fu, fv) = frames[d];
assert_eq!(fy.len(), width * frame_h);
let mut pair: Vec<AnchorField> = Vec::with_capacity(2);
for bottom in [false, true] {
let (sy, su, sv) = extract_field(fy, fu, fv, width, frame_h, bottom);
let src = YuvFrame {
width: cfg.width,
height: field_h,
y: &sy,
u: &su,
v: &sv,
};
let field_signal = if bottom {
FieldPicSignal::BottomField
} else {
FieldPicSignal::TopField
};
let poc = 2 * d as i32 + i32::from(bottom);
let poc_lsb = (poc as u32) % (1 << poc_lsb_bits);
let ref_l0 = l0[usize::from(bottom)].as_frame_ref(cfg.width, field_h);
let ref_l1 = l1[usize::from(bottom)].as_frame_ref(cfg.width, field_h);
let weighted = if cfg.b_implicit_weight {
Some(implicit_field_weights(
poc,
l0[usize::from(bottom)].poc,
l1[usize::from(bottom)].poc,
))
} else {
None
};
let ref_idc: u32 = if is_ref { 2 } else { 0 };
let mut sw = BitWriter::new();
sw.set_field_scan(true);
write_b_slice_header(
&mut sw,
&BSliceHeaderConfig {
first_mb_in_slice: 0,
slice_type_raw: 6,
pic_parameter_set_id: 0,
colour_plane_id: None,
frame_num,
frame_num_bits,
pic_order_cnt_lsb: poc_lsb,
poc_lsb_bits,
direct_spatial_mv_pred_flag: !cfg.b_temporal_direct,
slice_qp_delta: 0,
disable_deblocking_filter_idc: 0,
slice_alpha_c0_offset_div2: 0,
slice_beta_offset_div2: 0,
nal_ref_idc: ref_idc,
pred_weight_table: None,
cabac: None,
field: field_signal,
},
);
let (mut ry, mut ru, mut rv, infos, mvs) =
encode_b_slice_data(enc, &src, &ref_l0, &ref_l1, poc, &mut sw, weighted);
sw.rbsp_trailing_bits();
stream.extend_from_slice(&build_nal_unit(
ref_idc as u8,
NalUnitType::SliceNonIdr,
&sw.into_bytes(),
));
deblock_recon_field(
cfg.width,
field_h,
cfg.width / 2,
field_h / 2,
&mut ry,
&mut ru,
&mut rv,
&infos,
0,
width_mbs,
field_h / 16,
1,
);
pair.push(AnchorField {
y: ry,
u: ru,
v: rv,
poc,
partition_mvs: mvs,
});
}
recon_frames[d] = (
interleave_planes(&pair[0].y, &pair[1].y, width),
interleave_planes(&pair[0].u, &pair[1].u, width / 2),
interleave_planes(&pair[0].v, &pair[1].v, width / 2),
);
if is_ref {
let mut it = pair.into_iter();
Some([it.next().unwrap(), it.next().unwrap()])
} else {
None
}
};
let ref_b_enc = Encoder::new({
let mut c = mk_cfg();
c.direct_temporal_mv_pred = cfg.b_temporal_direct;
c.b_l0_bi_only = true;
c
});
if cfg.b_reference_fields {
assert!(
frames.len() >= 5 && frames.len() % 4 == 1,
"b_reference_fields needs 4k+1 display frames (anchors at multiples of 4)",
);
let mut prev_anchor = encode_anchor_pair(0, 0, None, &[], &mut stream, &mut recon_frames);
let mut ref_fn: u32 = 0;
let mut d = 4usize;
while d < frames.len() {
ref_fn += 1;
let anchor_rplm: &[EncRplmOp] = if ref_fn >= 3 {
&[EncRplmOp::Subtract(3)]
} else {
&[]
};
let next_anchor = encode_anchor_pair(
d,
ref_fn % (1 << frame_num_bits),
Some(&prev_anchor),
anchor_rplm,
&mut stream,
&mut recon_frames,
);
ref_fn += 1;
let ref_b = encode_b_pair(
d - 2,
ref_fn % (1 << frame_num_bits),
&prev_anchor,
&next_anchor,
true,
&ref_b_enc,
&mut stream,
&mut recon_frames,
)
.expect("reference B pair returns its fields");
let _ = encode_b_pair(
d - 3,
ref_fn % (1 << frame_num_bits),
&prev_anchor,
&ref_b,
false,
&b_enc,
&mut stream,
&mut recon_frames,
);
let _ = encode_b_pair(
d - 1,
ref_fn % (1 << frame_num_bits),
&ref_b,
&next_anchor,
false,
&b_enc,
&mut stream,
&mut recon_frames,
);
prev_anchor = next_anchor;
d += 4;
}
} else if cfg.b_implicit_weight {
assert!(
frames.len() >= 4 && frames.len() % 3 == 1,
"b_implicit_weight needs 3k+1 display frames (anchors at multiples of 3)",
);
let mut prev_anchor = encode_anchor_pair(0, 0, None, &[], &mut stream, &mut recon_frames);
let mut anchor_fn: u32 = 0;
let mut d = 3usize;
while d < frames.len() {
anchor_fn += 1;
let next_anchor = encode_anchor_pair(
d,
anchor_fn % (1 << frame_num_bits),
Some(&prev_anchor),
&[],
&mut stream,
&mut recon_frames,
);
for b_d in [d - 2, d - 1] {
let _ = encode_b_pair(
b_d,
anchor_fn % (1 << frame_num_bits),
&prev_anchor,
&next_anchor,
false,
&b_enc,
&mut stream,
&mut recon_frames,
);
}
prev_anchor = next_anchor;
d += 3;
}
} else {
let mut prev_anchor = encode_anchor_pair(0, 0, None, &[], &mut stream, &mut recon_frames);
let mut anchor_fn: u32 = 0;
let mut d = 2usize;
while d < frames.len() {
anchor_fn += 1;
let next_anchor = encode_anchor_pair(
d,
anchor_fn % (1 << frame_num_bits),
Some(&prev_anchor),
&[],
&mut stream,
&mut recon_frames,
);
let _ = encode_b_pair(
d - 1,
anchor_fn % (1 << frame_num_bits),
&prev_anchor,
&next_anchor,
false,
&b_enc,
&mut stream,
&mut recon_frames,
);
prev_anchor = next_anchor;
d += 2;
}
if frames.len() % 2 == 0 {
anchor_fn += 1;
let _ = encode_anchor_pair(
frames.len() - 1,
anchor_fn % (1 << frame_num_bits),
Some(&prev_anchor),
&[],
&mut stream,
&mut recon_frames,
);
}
}
PaffEncoded {
annex_b: stream,
recon_frames,
}
}