use crate::pgs::PgsSegment;
pub struct PesReassembler {
buffer: Vec<u8>,
has_data: bool,
}
impl Default for PesReassembler {
fn default() -> Self {
Self::new()
}
}
impl PesReassembler {
pub fn new() -> Self {
Self {
buffer: Vec::with_capacity(64 * 1024),
has_data: false,
}
}
pub fn push(&mut self, pusi: bool, payload: &[u8]) -> Vec<PgsSegment> {
let mut result = Vec::new();
if pusi && self.has_data {
result = self.emit();
}
if pusi {
self.buffer.clear();
self.has_data = true;
}
if self.has_data {
self.buffer.extend_from_slice(payload);
}
result
}
pub fn flush(&mut self) -> Vec<PgsSegment> {
if self.has_data {
self.emit()
} else {
Vec::new()
}
}
fn emit(&mut self) -> Vec<PgsSegment> {
let segments = parse_pes_for_pgs(&self.buffer);
self.buffer.clear();
self.has_data = false;
segments
}
}
fn parse_pes_for_pgs(data: &[u8]) -> Vec<PgsSegment> {
if data.len() < 9 {
return Vec::new();
}
if data[0] != 0x00 || data[1] != 0x00 || data[2] != 0x01 {
return Vec::new();
}
let pts_dts_flags = (data[7] >> 6) & 0x03;
let header_data_length = data[8] as usize;
let pes_header_end = 9 + header_data_length;
if pes_header_end > data.len() {
return Vec::new();
}
let pts = if pts_dts_flags >= 2 && header_data_length >= 5 {
parse_timestamp(&data[9..14])
} else {
0
};
let dts = if pts_dts_flags == 3 && header_data_length >= 10 {
parse_timestamp(&data[14..19])
} else {
0
};
PgsSegment::parse_raw_segments(pts, dts, &data[pes_header_end..])
}
fn parse_timestamp(bytes: &[u8]) -> u64 {
let a = ((bytes[0] >> 1) & 0x07) as u64;
let b = bytes[1] as u64;
let c = ((bytes[2] >> 1) & 0x7F) as u64;
let d = bytes[3] as u64;
let e = ((bytes[4] >> 1) & 0x7F) as u64;
(a << 30) | (b << 22) | (c << 15) | (d << 7) | e
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pgs::SegmentType;
#[test]
fn test_parse_timestamp() {
let bytes = [0x21, 0x00, 0x05, 0xBF, 0x21];
assert_eq!(parse_timestamp(&bytes), 90000);
}
#[test]
fn test_parse_timestamp_large() {
let bytes = [0x0F, 0xFF, 0xFF, 0xFF, 0xFF];
assert_eq!(parse_timestamp(&bytes), 0x1FFFFFFFF);
}
#[test]
fn test_parse_raw_segments_end() {
let data = [0x80, 0x00, 0x00];
let segments = PgsSegment::parse_raw_segments(90000, 0, &data);
assert_eq!(segments.len(), 1);
assert_eq!(segments[0].segment_type, SegmentType::EndOfDisplaySet);
assert_eq!(segments[0].pts, 90000);
assert!(segments[0].payload.is_empty());
}
#[test]
fn test_parse_raw_segments_multiple() {
let mut data = Vec::new();
data.extend_from_slice(&[0x14, 0x00, 0x03, 0x00, 0x01, 0x02]);
data.extend_from_slice(&[0x80, 0x00, 0x00]);
let segments = PgsSegment::parse_raw_segments(45000, 0, &data);
assert_eq!(segments.len(), 2);
assert_eq!(segments[0].segment_type, SegmentType::PaletteDefinition);
assert_eq!(segments[0].payload, vec![0x00, 0x01, 0x02]);
assert_eq!(segments[1].segment_type, SegmentType::EndOfDisplaySet);
}
#[test]
fn test_pes_reassembler() {
let mut asm = PesReassembler::new();
let mut pes = Vec::new();
pes.extend_from_slice(&[0x00, 0x00, 0x01]); pes.push(0xBD); pes.extend_from_slice(&[0x00, 0x08]); pes.push(0x80); pes.push(0x80); pes.push(0x05); pes.extend_from_slice(&[0x21, 0x00, 0x05, 0xBF, 0x21]);
pes.extend_from_slice(&[0x80, 0x00, 0x00]);
let mid = pes.len() / 2;
let r1 = asm.push(true, &pes[..mid]);
assert!(r1.is_empty());
let r2 = asm.push(false, &pes[mid..]);
assert!(r2.is_empty());
let r3 = asm.flush();
assert_eq!(r3.len(), 1);
assert_eq!(r3[0].segment_type, SegmentType::EndOfDisplaySet);
assert_eq!(r3[0].pts, 90000);
}
#[test]
fn test_pes_reassembler_two_pes_packets() {
let mut asm = PesReassembler::new();
let make_pes = |pts_bytes: &[u8]| {
let mut pes = Vec::new();
pes.extend_from_slice(&[0x00, 0x00, 0x01, 0xBD]);
pes.extend_from_slice(&[0x00, 0x08]);
pes.push(0x80);
pes.push(0x80);
pes.push(0x05);
pes.extend_from_slice(pts_bytes);
pes.extend_from_slice(&[0x80, 0x00, 0x00]);
pes
};
let pes1 = make_pes(&[0x21, 0x00, 0x05, 0xBF, 0x21]); let pes2 = make_pes(&[0x21, 0x00, 0x0B, 0x7E, 0x41]);
let r1 = asm.push(true, &pes1);
assert!(r1.is_empty());
let r2 = asm.push(true, &pes2);
assert_eq!(r2.len(), 1);
assert_eq!(r2[0].pts, 90000);
let r3 = asm.flush();
assert_eq!(r3.len(), 1);
assert_eq!(r3[0].pts, 180000);
}
}