use crate::encoder::{EncodedFrameRef, EncodedIdr, EncodedP, Encoder, EncoderConfig, YuvFrame};
#[derive(Debug, Clone)]
pub struct ScpConfig {
pub width: u32,
pub height: u32,
pub qp: i32,
pub cabac: bool,
pub b_frame: bool,
pub direct_temporal: bool,
pub transform_8x8: bool,
}
pub struct ScpEncoded {
pub annex_b: Vec<u8>,
pub recon_frames: Vec<[Vec<u8>; 3]>,
}
fn split_annex_b(data: &[u8]) -> Vec<&[u8]> {
let mut nals = Vec::new();
let mut i = 0usize;
let mut start: Option<usize> = None;
while i + 2 < data.len() {
if data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1 {
if let Some(s) = start {
let mut end = i;
if end > s && data[end - 1] == 0 {
end -= 1;
}
nals.push(&data[s..end]);
}
start = Some(i + 3);
i += 3;
} else {
i += 1;
}
}
if let Some(s) = start {
nals.push(&data[s..]);
}
nals
}
fn filter_nals(data: &[u8], keep: impl Fn(u8) -> bool) -> Vec<u8> {
let mut out = Vec::new();
for nal in split_annex_b(data) {
let Some(&h) = nal.first() else { continue };
if keep(h & 0x1F) {
out.extend_from_slice(&[0, 0, 0, 1]);
out.extend_from_slice(nal);
}
}
out
}
pub fn encode_scp_sequence(cfg: &ScpConfig, frames: &[(&[u8], &[u8], &[u8])]) -> ScpEncoded {
assert!(!frames.is_empty(), "need at least one frame");
let mk_enc = |plane: u8| {
Encoder::new(EncoderConfig {
chroma_format_idc: 0,
profile_idc: 244,
colour_plane_id: Some(plane),
cabac: cfg.cabac,
qp: cfg.qp,
direct_temporal_mv_pred: cfg.direct_temporal,
transform_8x8: cfg.transform_8x8,
max_num_ref_frames: 2,
..EncoderConfig::new(cfg.width, cfg.height)
})
};
let encs = [mk_enc(0), mk_enc(1), mk_enc(2)];
let plane_src = |k: usize, p: usize| -> &[u8] {
match p {
0 => frames[k].0,
1 => frames[k].1,
_ => frames[k].2,
}
};
fn plane_yuv<'a>(w: u32, h: u32, y: &'a [u8]) -> YuvFrame<'a> {
YuvFrame {
width: w,
height: h,
y,
u: &[],
v: &[],
}
}
let mut annex_b: Vec<u8> = Vec::new();
let mut recon_frames: Vec<[Vec<u8>; 3]> = Vec::new();
let mut idrs: Vec<EncodedIdr> = Vec::with_capacity(3);
for (p, enc) in encs.iter().enumerate() {
let f = plane_yuv(cfg.width, cfg.height, plane_src(0, p));
let idr = if cfg.cabac {
enc.encode_idr_cabac(&f)
} else {
enc.encode_idr(&f)
};
if p == 0 {
annex_b.extend_from_slice(&idr.annex_b);
} else {
annex_b.extend_from_slice(&filter_nals(&idr.annex_b, |t| t != 7 && t != 8));
}
idrs.push(idr);
}
recon_frames.push([
idrs[0].recon_y.clone(),
idrs[1].recon_y.clone(),
idrs[2].recon_y.clone(),
]);
if cfg.b_frame {
assert_eq!(
frames.len(),
3,
"b_frame mode codes exactly one IDR-B-P mini-GOP (3 frames)"
);
let mut ps: Vec<EncodedP> = Vec::with_capacity(3);
for (p, enc) in encs.iter().enumerate() {
let f = plane_yuv(cfg.width, cfg.height, plane_src(2, p));
let coded = if cfg.cabac {
enc.encode_p_cabac(&f, &EncodedFrameRef::from(&idrs[p]), 1, 4)
} else {
enc.encode_p(&f, &EncodedFrameRef::from(&idrs[p]), 1, 4)
};
annex_b.extend_from_slice(&coded.annex_b);
ps.push(coded);
}
let mut recon_b: Vec<Vec<u8>> = Vec::with_capacity(3);
for (p, enc) in encs.iter().enumerate() {
let f = plane_yuv(cfg.width, cfg.height, plane_src(1, p));
let l0 = EncodedFrameRef::from(&idrs[p]);
let l1 = EncodedFrameRef::from(&ps[p]);
let coded = if cfg.cabac {
enc.encode_b_cabac(&f, &l0, &l1, 1, 2)
} else {
enc.encode_b(&f, &l0, &l1, 1, 2)
};
annex_b.extend_from_slice(&coded.annex_b);
recon_b.push(coded.recon_y.clone());
}
recon_frames.push([recon_b[0].clone(), recon_b[1].clone(), recon_b[2].clone()]);
recon_frames.push([
ps[0].recon_y.clone(),
ps[1].recon_y.clone(),
ps[2].recon_y.clone(),
]);
return ScpEncoded {
annex_b,
recon_frames,
};
}
let mut prev_p: Option<[EncodedP; 3]> = None;
for k in 1..frames.len() {
let mut aus: Vec<EncodedP> = Vec::with_capacity(3);
for (p, enc) in encs.iter().enumerate() {
let f = plane_yuv(cfg.width, cfg.height, plane_src(k, p));
let prev_ref = match &prev_p {
Some(ps) => EncodedFrameRef::from(&ps[p]),
None => EncodedFrameRef::from(&idrs[p]),
};
let coded = if cfg.cabac {
enc.encode_p_cabac(&f, &prev_ref, k as u32, 2 * k as u32)
} else {
enc.encode_p(&f, &prev_ref, k as u32, 2 * k as u32)
};
annex_b.extend_from_slice(&coded.annex_b);
aus.push(coded);
}
recon_frames.push([
aus[0].recon_y.clone(),
aus[1].recon_y.clone(),
aus[2].recon_y.clone(),
]);
let arr: [EncodedP; 3] = aus.try_into().map_err(|_| ()).expect("three planes");
prev_p = Some(arr);
}
ScpEncoded {
annex_b,
recon_frames,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn split_annex_b_handles_3_and_4_byte_start_codes() {
let data = [
0, 0, 0, 1, 0x67, 0xAA, 0, 0, 1, 0x68, 0xBB, 0, 0, 0, 1, 0x65, 0xCC, 0xDD, ];
let nals = split_annex_b(&data);
assert_eq!(nals.len(), 3);
assert_eq!(nals[0], &[0x67, 0xAA][..]);
assert_eq!(nals[1], &[0x68, 0xBB][..]);
assert_eq!(nals[2], &[0x65, 0xCC, 0xDD][..]);
}
#[test]
fn filter_nals_drops_parameter_sets() {
let data = [
0, 0, 0, 1, 0x67, 0xAA, 0, 0, 0, 1, 0x68, 0xBB, 0, 0, 0, 1, 0x65, 0xCC,
];
let out = filter_nals(&data, |t| t != 7 && t != 8);
assert_eq!(out, vec![0, 0, 0, 1, 0x65, 0xCC]);
}
}