use crate::{Confidence, Detail, Evidence, Outcome};
const FLV_SIGNATURE: [u8; 3] = *b"FLV";
const FLV_HEADER_LEN: usize = 9;
const TYPE_FLAG_AUDIO: u8 = 0x04;
const TYPE_FLAG_VIDEO: u8 = 0x01;
const TYPE_FLAG_RESERVED_MASK: u8 = !(TYPE_FLAG_AUDIO | TYPE_FLAG_VIDEO);
const TYPE_FLAGS_OFFSET: usize = 4;
const DATA_OFFSET_POS: usize = 5;
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() < FLV_HEADER_LEN {
return Outcome::Insufficient(FLV_HEADER_LEN);
}
if region[..FLV_SIGNATURE.len()] != FLV_SIGNATURE {
return Outcome::None;
}
let type_flags = region[TYPE_FLAGS_OFFSET];
if type_flags & TYPE_FLAG_RESERVED_MASK != 0 {
return Outcome::None;
}
let data_offset = u32::from_be_bytes([
region[DATA_OFFSET_POS],
region[DATA_OFFSET_POS + 1],
region[DATA_OFFSET_POS + 2],
region[DATA_OFFSET_POS + 3],
]) as usize;
if data_offset < FLV_HEADER_LEN {
return Outcome::None;
}
Outcome::Match(Evidence {
confidence: Confidence::STRONG,
detail: Detail::Flv {
has_audio: type_flags & TYPE_FLAG_AUDIO != 0,
has_video: type_flags & TYPE_FLAG_VIDEO != 0,
data_offset: data_offset as u32,
},
})
}
#[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/flv/av.flv");
let region = &data[..FLV_HEADER_LEN - 1];
match probe(region, region.len()) {
Outcome::Insufficient(need) => assert_eq!(need, FLV_HEADER_LEN),
other => panic!("8-byte FLV prefix must be Insufficient(9), got {other:?}"),
}
}
}