use alloc::vec::Vec;
use broadcast_common::len::need_in;
use broadcast_common::{Parse, Serialize};
use crate::Result;
use crate::error::Error;
use crate::pack_header::{PACK_START_CODE, PackHeader};
use crate::program_stream_map::{MAP_STREAM_ID, ProgramStreamMap};
use crate::system_header::{
PREFIX_LEN as SYSTEM_HEADER_PREFIX_LEN, SYSTEM_HEADER_START_CODE, SystemHeader,
};
const PROGRAM_END_CODE: u32 = 0x0000_01B9;
#[derive(Debug, Clone)]
pub struct Pack<'a> {
pub pack_header: PackHeader<'a>,
pub system_header: Option<SystemHeader>,
pub psm: Option<ProgramStreamMap<'a>>,
pub pes_packets: Vec<mpeg_pes::PesPacket<'a>>,
}
pub fn parse_pack(b: &[u8]) -> Result<(Option<Pack<'_>>, usize)> {
need_in(b, 4, "pack start_code or end_code")?;
let start = u32::from_be_bytes([b[0], b[1], b[2], b[3]]);
if start == PROGRAM_END_CODE {
return Ok((None, 4));
}
let pack_header = PackHeader::parse(b)?;
let hdr_len = pack_header.header_len();
let rest = &b[hdr_len..];
let (system_header, pes_start) = if rest.len() >= 4 {
let maybe_sh = u32::from_be_bytes([rest[0], rest[1], rest[2], rest[3]]);
if maybe_sh == SYSTEM_HEADER_START_CODE {
let sh = SystemHeader::parse(rest)?;
let header_length = u16::from_be_bytes([rest[4], rest[5]]) as usize;
(Some(sh), SYSTEM_HEADER_PREFIX_LEN + header_length)
} else {
(None, 0)
}
} else {
(None, 0)
};
let pes_data = &rest[pes_start..];
let (pes_packets, psm, pes_consumed) = parse_pes_loop(pes_data)?;
let consumed = hdr_len + pes_start + pes_consumed;
Ok((
Some(Pack {
pack_header,
system_header,
psm,
pes_packets,
}),
consumed,
))
}
fn parse_pes_loop(
data: &[u8],
) -> Result<(
Vec<mpeg_pes::PesPacket<'_>>,
Option<ProgramStreamMap<'_>>,
usize,
)> {
let mut packets = Vec::new();
let mut psm = None;
let mut pos = 0;
loop {
if pos + 4 <= data.len() {
let word = u32::from_be_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]]);
if word == PACK_START_CODE || word == PROGRAM_END_CODE {
break;
}
}
if !(pos + 6 <= data.len() && data[pos..pos + 3] == crate::PACKET_START_CODE_PREFIX) {
break;
}
if data[pos + 3] == MAP_STREAM_ID {
let map = ProgramStreamMap::parse(&data[pos..])?;
pos += map.serialized_len();
psm = Some(map);
continue;
}
match mpeg_pes::PesPacket::parse(&data[pos..]) {
Ok(pkt) => {
let pkt_len = pkt.serialized_len();
packets.push(pkt);
pos += pkt_len;
}
Err(e) => return Err(Error::Pes(e)),
}
}
Ok((packets, psm, pos))
}
pub fn parse_all_packs(b: &[u8]) -> Result<(Vec<Pack<'_>>, &[u8])> {
let mut packs = Vec::new();
let mut remaining = b;
while remaining.len() >= 4 {
let (pack_opt, consumed) = parse_pack(remaining)?;
match pack_opt {
Some(pack) => {
remaining = &remaining[consumed..];
packs.push(pack);
}
None => {
remaining = &remaining[4..];
break;
}
}
}
Ok((packs, remaining))
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SkippedRegion {
pub offset: usize,
pub len: usize,
pub error: Error,
}
#[derive(Debug, Clone)]
pub struct PackScan<'a> {
pub packs: Vec<Pack<'a>>,
pub skipped: Vec<SkippedRegion>,
pub remaining: &'a [u8],
}
pub fn scan_packs(b: &[u8]) -> PackScan<'_> {
let mut packs = Vec::new();
let mut skipped = Vec::new();
let mut pos = 0usize;
while b.len() - pos >= 4 {
match parse_pack(&b[pos..]) {
Ok((Some(pack), consumed)) => {
packs.push(pack);
pos += consumed;
}
Ok((None, consumed)) => {
pos += consumed;
break;
}
Err(error) => {
let next = find_pack_start(&b[pos + 1..]).map(|i| pos + 1 + i);
let end = next.unwrap_or(b.len());
skipped.push(SkippedRegion {
offset: pos,
len: end - pos,
error,
});
pos = end;
}
}
}
PackScan {
packs,
skipped,
remaining: &b[pos..],
}
}
fn find_pack_start(data: &[u8]) -> Option<usize> {
let code = PACK_START_CODE.to_be_bytes();
data.windows(code.len()).position(|w| w == code)
}
#[cfg(test)]
mod tests {
use super::parse_pack;
use crate::program_stream_map::{EsMapEntry, ProgramStreamMap};
use alloc::{vec, vec::Vec};
use broadcast_common::Serialize;
#[test]
fn psm_is_surfaced_not_folded_into_pes_packets() {
let psm = ProgramStreamMap {
current_next_indicator: true,
single_extension_stream_flag: false,
version: 1,
program_stream_info: &[],
elementary_stream_map: vec![EsMapEntry {
stream_type: 0x02, elementary_stream_id: 0xE0,
stream_id_extension: None,
descriptors: &[],
}],
crc: 0,
};
let mut psm_bytes = vec![0u8; psm.serialized_len()];
psm.serialize_into(&mut psm_bytes).unwrap();
let mut b: Vec<u8> = vec![
0x00, 0x00, 0x01, 0xBA, 0x44, 0x00, 0x04, 0x00, 0x04, 0x01, 0x43, 0x36, 0x3B, 0xF8,
];
b.extend_from_slice(&psm_bytes);
b.extend_from_slice(&[
0x00, 0x00, 0x01, 0xE0, 0x00, 0x0A, 0x80, 0x80, 0x05, 0x21, 0x00, 0x01, 0x00, 0x01,
0xAA, 0xBB,
]);
let (pack_opt, consumed) = parse_pack(&b).unwrap();
let pack = pack_opt.expect("not an end code");
let parsed_psm = pack.psm.expect("PSM must be surfaced on Pack::psm");
assert_eq!(parsed_psm.elementary_stream_map.len(), 1);
assert_eq!(parsed_psm.elementary_stream_map[0].stream_type, 0x02);
assert_eq!(
pack.pes_packets.len(),
1,
"the PSM must not appear (again, mis-typed) in pes_packets"
);
assert_eq!(consumed, b.len());
}
#[test]
fn system_header_wire_length_used_for_pes_start_not_reserialized_length() {
let mut b: Vec<u8> = vec![
0x00, 0x00, 0x01, 0xBA, 0x44, 0x00, 0x04, 0x00, 0x04, 0x01, 0x43, 0x36, 0x3B, 0xF8,
];
b.extend_from_slice(&[
0x00, 0x00, 0x01, 0xBB, 0x00, 0x08, 0x80, 0x00, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, ]);
b.extend_from_slice(&[
0x00, 0x00, 0x01, 0xE0, 0x00, 0x0A, 0x80, 0x80, 0x05, 0x21, 0x00, 0x01, 0x00, 0x01,
0xAA, 0xBB,
]);
let (pack_opt, consumed) = parse_pack(&b).unwrap();
let pack = pack_opt.expect("not an end code");
assert!(pack.system_header.is_some());
assert_eq!(
pack.pes_packets.len(),
1,
"PES must be found starting after the full declared header_length"
);
assert_eq!(consumed, b.len());
}
#[test]
fn stray_pack_start_code_inside_pes_payload_does_not_truncate() {
let mut b: Vec<u8> = vec![
0x00, 0x00, 0x01, 0xBA, 0x44, 0x00, 0x04, 0x00, 0x04, 0x01, 0x43, 0x36, 0x3B, 0xF8,
];
b.extend_from_slice(&[
0x00, 0x00, 0x01, 0xE0, 0x00, 0x0E, 0x80, 0x80, 0x05, 0x21, 0x00, 0x01, 0x00, 0x01,
0xAA, 0x00, 0x00, 0x01, 0xBA, 0xBB,
]);
let (pack_opt, consumed) = parse_pack(&b).unwrap();
let pack = pack_opt.expect("not an end code");
assert_eq!(
pack.pes_packets.len(),
1,
"the one PES packet must parse in full, not truncate at the embedded 000001BA"
);
assert_eq!(consumed, b.len(), "the whole PES packet must be consumed");
}
#[test]
fn fuzz_regression_mpeg_ps_boundary_underflow() {
let crashing: &[u8] = &[
0x00, 0x00, 0x01, 0xba, 0x5c, 0xf5, 0xf5, 0xc0, 0xff, 0xff, 0x21, 0xf3, 0xf3, 0xf3,
0x90, 0xf3, 0xbb, 0x00, 0x00, 0x01, 0xbb, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x01, 0xba, 0x5c,
0xf5, 0xf5, 0xc0, 0xff, 0xff, 0xf3, 0xf3, 0xf3, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00,
0x00, 0x00,
];
let _ = parse_pack(crashing);
}
#[test]
fn a_truncated_pes_packet_inside_a_pack_is_a_pes_error() {
let mut b: Vec<u8> = vec![
0x00, 0x00, 0x01, 0xBA, 0x44, 0x00, 0x04, 0x00, 0x04, 0x01, 0x43, 0x36, 0x3B, 0xF8,
];
let pes = [0x00, 0x00, 0x01, 0xE0, 0x00, 0x0A, 0x80];
b.extend_from_slice(&pes);
let expected = mpeg_pes::PesPacket::parse(&pes).unwrap_err();
assert_eq!(parse_pack(&b).unwrap_err(), crate::Error::Pes(expected));
}
}