use crate::{Confidence, Detail, Evidence, Outcome};
const PACK_START_CODE: [u8; 4] = [0x00, 0x00, 0x01, 0xBA];
const SCR_PREFIX_MASK: u8 = 0xC0;
const SCR_PREFIX_EXPECTED: u8 = 0x40;
const SCR_MARKER_BITS: [(usize, u8); 4] = [
(0, 0x04), (2, 0x04), (4, 0x04), (5, 0x01), ];
const SCR_OFFSET: usize = 4;
const MUX_RATE_MARKERS_BYTE: usize = 12;
const MUX_RATE_MARKERS_MASK: u8 = 0x03;
const PACK_HEADER_MIN_LEN: usize = 14;
pub(crate) fn probe(data: &[u8], limit: usize) -> Outcome {
debug_assert!(limit <= data.len(), "harness caps limit at data.len()");
let region = &data[..limit];
if region.len() < PACK_HEADER_MIN_LEN {
return Outcome::Insufficient(PACK_HEADER_MIN_LEN);
}
if region[..PACK_START_CODE.len()] != PACK_START_CODE {
return Outcome::None;
}
let structurally_valid = markers_valid(region);
Outcome::Match(Evidence {
confidence: if structurally_valid {
Confidence::STRUCTURAL
} else {
Confidence::HEURISTIC
},
detail: Detail::MpegPs { structurally_valid },
})
}
fn markers_valid(p: &[u8]) -> bool {
if p[SCR_OFFSET] & SCR_PREFIX_MASK != SCR_PREFIX_EXPECTED {
return false;
}
let scr = &p[SCR_OFFSET..];
for &(byte_off, bit) in &SCR_MARKER_BITS {
if scr[byte_off] & bit == 0 {
return false;
}
}
if p[MUX_RATE_MARKERS_BYTE] & MUX_RATE_MARKERS_MASK != MUX_RATE_MARKERS_MASK {
return false;
}
true
}
#[cfg(test)]
mod drift {
#[test]
fn pack_start_code_prefix_matches_mpeg_ps() {
assert_eq!(
super::PACK_START_CODE[..3],
mpeg_ps::PACKET_START_CODE_PREFIX[..],
"container-probe's PACK_START_CODE prefix has drifted from mpeg-ps's"
);
assert_eq!(
super::PACK_START_CODE[3],
0xBA,
"pack_start_code must end in BA (ISO/IEC 13818-1 §2.5.3.3)"
);
}
}
#[cfg(test)]
mod tests {
use super::*;
fn fixture_bytes(rel: &str) -> std::vec::Vec<u8> {
std::fs::read(std::format!("{}/../{}", env!("CARGO_MANIFEST_DIR"), rel))
.unwrap_or_else(|e| panic!("failed to read {rel}: {e}"))
}
#[test]
fn short_prefix_is_insufficient() {
let data = fixture_bytes("fixtures/ps/h264_ac3.ps");
let region = &data[..PACK_HEADER_MIN_LEN - 1];
match probe(region, region.len()) {
Outcome::Insufficient(need) => assert_eq!(need, PACK_HEADER_MIN_LEN),
other => panic!("13-byte MPEG-PS prefix must be Insufficient(14), got {other:?}"),
}
}
}