use crate::encoder::frame_obu::write_frame_header_obu;
use crate::encoder::obu::{build_temporal_unit, write_obu_with_size, ObuFrame, ObuHeader};
use crate::encoder::sequence_obu::write_sequence_header_obu;
use crate::frame_header::FrameHeader;
use crate::obu::ObuType;
use crate::sequence_header::SequenceHeader;
#[derive(Debug, Clone)]
pub struct TemporalUnitPlan<'a> {
pub seq: &'a SequenceHeader,
pub emit_sequence_header: bool,
pub frames: &'a [FrameHeader],
}
pub fn encode_sequence_header_obu(seq: &SequenceHeader) -> Vec<u8> {
let body = write_sequence_header_obu(seq);
let mut out = Vec::new();
write_obu_with_size(&mut out, &ObuHeader::new(ObuType::SequenceHeader), &body);
out
}
pub fn encode_frame_header_obu(fh: &FrameHeader, seq: &SequenceHeader) -> Vec<u8> {
let body = write_frame_header_obu(fh, seq);
let mut out = Vec::new();
write_obu_with_size(&mut out, &ObuHeader::new(ObuType::FrameHeader), &body);
out
}
pub fn encode_temporal_unit(plan: &TemporalUnitPlan<'_>) -> Vec<u8> {
let frame_obus: Vec<ObuFrame> = plan
.frames
.iter()
.map(|fh| ObuFrame::new(ObuType::FrameHeader, write_frame_header_obu(fh, plan.seq)))
.collect();
let seq_body = plan
.emit_sequence_header
.then(|| write_sequence_header_obu(plan.seq));
build_temporal_unit(seq_body.as_deref(), &frame_obus)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::encoder::frame_obu::write_frame_header_obu;
use crate::encoder::sequence_obu::write_sequence_header_obu;
use crate::frame_header::parse_frame_header;
use crate::obu::{ObuIter, ObuType};
use crate::sequence_header::parse_sequence_header;
const TINY_SEQ_PAYLOAD: &[u8] = &[0x00, 0x00, 0x00, 0x01, 0x9f, 0xfb, 0xff, 0xf3, 0x00, 0x80];
const TINY_FRAME_PAYLOAD: &[u8] = &[
0x10, 0x00, 0xbc, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x78, 0x9d, 0x76, 0x2f, 0x67,
0x6c, 0xc7, 0xee, 0x51, 0x80,
];
fn tiny_seq() -> SequenceHeader {
parse_sequence_header(TINY_SEQ_PAYLOAD).unwrap()
}
fn tiny_fh(seq: &SequenceHeader) -> FrameHeader {
parse_frame_header(TINY_FRAME_PAYLOAD, seq).unwrap()
}
#[test]
fn encode_sequence_header_obu_round_trips_through_parser() {
let seq = tiny_seq();
let bytes = encode_sequence_header_obu(&seq);
let mut iter = ObuIter::new(&bytes);
let desc = iter.next().unwrap().unwrap();
assert_eq!(desc.obu_type, ObuType::SequenceHeader);
let reparsed = parse_sequence_header(desc.payload).expect("SH payload parses");
let mut expected = seq.clone();
expected.bits_consumed = reparsed.bits_consumed;
assert_eq!(reparsed, expected);
}
#[test]
fn encode_frame_header_obu_round_trips_through_parser() {
let seq = tiny_seq();
let fh = tiny_fh(&seq);
let bytes = encode_frame_header_obu(&fh, &seq);
let mut iter = ObuIter::new(&bytes);
let desc = iter.next().unwrap().unwrap();
assert_eq!(desc.obu_type, ObuType::FrameHeader);
let reparsed = parse_frame_header(desc.payload, &seq).expect("FH payload parses");
let mut expected = fh.clone();
expected.bits_consumed = reparsed.bits_consumed;
assert_eq!(reparsed, expected);
}
#[test]
fn encode_temporal_unit_emits_td_sh_fh_in_order() {
let seq = tiny_seq();
let fh = tiny_fh(&seq);
let frames = [fh.clone()];
let plan = TemporalUnitPlan {
seq: &seq,
emit_sequence_header: true,
frames: &frames,
};
let bytes = encode_temporal_unit(&plan);
let descs: Vec<_> = ObuIter::new(&bytes).collect::<Result<_, _>>().unwrap();
assert_eq!(descs.len(), 3);
assert_eq!(descs[0].obu_type, ObuType::TemporalDelimiter);
assert_eq!(descs[0].payload_len, 0);
assert_eq!(descs[1].obu_type, ObuType::SequenceHeader);
let expected_sh_body = write_sequence_header_obu(&seq);
assert_eq!(descs[1].payload, &expected_sh_body[..]);
assert_ne!(
*descs[1].payload.last().unwrap(),
0,
"SH body must end with its trailing_bits byte"
);
assert_eq!(descs[2].obu_type, ObuType::FrameHeader);
let expected_fh_body = write_frame_header_obu(&fh, &seq);
assert_eq!(descs[2].payload, &expected_fh_body[..]);
assert_ne!(
*descs[2].payload.last().unwrap(),
0,
"FH body must end with its trailing_bits byte"
);
}
#[test]
fn encode_temporal_unit_without_sh_omits_sequence_header() {
let seq = tiny_seq();
let fh = tiny_fh(&seq);
let frames = [fh];
let plan = TemporalUnitPlan {
seq: &seq,
emit_sequence_header: false,
frames: &frames,
};
let bytes = encode_temporal_unit(&plan);
let descs: Vec<_> = ObuIter::new(&bytes).collect::<Result<_, _>>().unwrap();
assert_eq!(descs.len(), 2);
assert_eq!(descs[0].obu_type, ObuType::TemporalDelimiter);
assert_eq!(descs[1].obu_type, ObuType::FrameHeader);
}
}