use broadcast_common::Unpackage;
use transmux::{CodecConfig, Fmp4Demux, Media, ProgressiveDemux, iter_length_prefixed_nals};
use crate::report::{Finding, Location, Report, Severity};
const HEVC_NAL_SPS: u8 = 33;
pub fn check_container_codec(bytes: &[u8], report: &mut Report) {
if !looks_like_isobmff(bytes) {
return;
}
let media = match Fmp4Demux::new().unpackage(bytes) {
Ok(m) => Some(m),
Err(_) => ProgressiveDemux::new(bytes.len())
.ok()
.and_then(|mut demux| demux.unpackage(bytes).ok()),
};
if let Some(media) = media {
check_media(&media, report);
}
}
fn looks_like_isobmff(bytes: &[u8]) -> bool {
if bytes.len() < 8 {
return false;
}
matches!(
&bytes[4..8],
b"ftyp" | b"styp" | b"moov" | b"moof" | b"free" | b"skip"
)
}
fn check_media(media: &Media, report: &mut Report) {
for track in &media.tracks {
let track_id = track.track_id() as u16;
let is_nal_track = matches!(
&track.spec.config,
CodecConfig::Avc { .. } | CodecConfig::Hevc { .. }
);
match &track.spec.config {
CodecConfig::Avc {
config,
width,
height,
} => check_avc_track(track_id, &config.config, *width, *height, report),
CodecConfig::Hevc {
config,
width,
height,
} => check_hevc_track(track_id, &config.config, *width, *height, report),
_ => {}
}
if !is_nal_track {
continue;
}
for (idx, sample) in track.samples.iter().enumerate() {
if iter_length_prefixed_nals(&sample.data).is_err() {
report.push(Finding::new(
Severity::Error,
Location::new(idx, track_id),
"length-prefix-violation",
alloc::format!(
"Sample {idx} on track {track_id} is not a well-formed 4-byte \
length-prefixed NAL sequence (declared length runs past the sample's \
own end) — a hallmark of an Annex B start code left in place of a \
length prefix (ISO/IEC 14496-15:2017 §5.4.3.2.3)",
),
));
break;
}
}
}
}
fn check_avc_track(
track_id: u16,
record: &transmux::AVCDecoderConfigurationRecord,
width: u16,
height: u16,
report: &mut Report,
) {
let Some(sps) = record.sps.first() else {
return;
};
let Ok(info) = sps.decode() else {
return;
};
if record.profile_indication != info.profile_idc {
report.push(Finding::new(
Severity::Error,
Location::new(0, track_id),
"avcc-sps-mismatch",
alloc::format!(
"avcC AVCProfileIndication 0x{:02X} on track {track_id} disagrees with the \
embedded SPS profile_idc 0x{:02X} — ISO/IEC 14496-15:2017 §5.3.3.1.1",
record.profile_indication,
info.profile_idc,
),
));
}
if record.level_indication != info.level_idc {
report.push(Finding::new(
Severity::Error,
Location::new(0, track_id),
"avcc-sps-mismatch",
alloc::format!(
"avcC AVCLevelIndication 0x{:02X} on track {track_id} disagrees with the \
embedded SPS level_idc 0x{:02X} — ISO/IEC 14496-15:2017 §5.3.3.1.1",
record.level_indication,
info.level_idc,
),
));
}
if let Some(chroma) = record.chroma_format
&& chroma != info.chroma_format_idc
{
report.push(Finding::new(
Severity::Error,
Location::new(0, track_id),
"avcc-sps-mismatch",
alloc::format!(
"avcC chroma_format {chroma} on track {track_id} disagrees with the \
embedded SPS chroma_format_idc {} — ISO/IEC 14496-15:2017 §5.3.3.1.2",
info.chroma_format_idc,
),
));
}
if width != 0 && height != 0 && (width as u32, height as u32) != (info.width, info.height) {
report.push(Finding::new(
Severity::Error,
Location::new(0, track_id),
"container-sps-dimension-mismatch",
alloc::format!(
"Sample entry declares {width}x{height} on track {track_id} but the embedded \
SPS decodes to {}x{} — ITU-T H.264 §7.4.2.1.1 cropped coded geometry",
info.width,
info.height,
),
));
}
if !info.frame_mbs_only {
report.push(Finding::new(
Severity::Info,
Location::new(0, track_id),
"avc-interlaced-content",
alloc::format!(
"AVC SPS on track {track_id} has frame_mbs_only_flag=0 (interlaced coding \
tools enabled) — ITU-T H.264 §7.3.2.1.1",
),
));
}
}
fn check_hevc_track(
track_id: u16,
record: &transmux::HEVCDecoderConfigurationRecord,
width: u16,
height: u16,
report: &mut Report,
) {
let Some(info) = record
.arrays
.iter()
.find(|a| a.nal_unit_type == HEVC_NAL_SPS)
.and_then(|a| a.nalus.first())
.and_then(|n| n.decode_sps().ok().flatten())
else {
return;
};
if record.general_profile_idc != info.general_profile_idc {
report.push(Finding::new(
Severity::Error,
Location::new(0, track_id),
"hvcc-sps-mismatch",
alloc::format!(
"hvcC general_profile_idc {} on track {track_id} disagrees with the embedded \
SPS general_profile_idc {} — ISO/IEC 14496-15:2017 §8.3.3.1",
record.general_profile_idc,
info.general_profile_idc,
),
));
}
if record.general_level_idc != info.general_level_idc {
report.push(Finding::new(
Severity::Error,
Location::new(0, track_id),
"hvcc-sps-mismatch",
alloc::format!(
"hvcC general_level_idc {} on track {track_id} disagrees with the embedded SPS \
general_level_idc {} — ISO/IEC 14496-15:2017 §8.3.3.1",
record.general_level_idc,
info.general_level_idc,
),
));
}
if record.chroma_format_idc != info.chroma_format_idc {
report.push(Finding::new(
Severity::Error,
Location::new(0, track_id),
"hvcc-sps-mismatch",
alloc::format!(
"hvcC chroma_format_idc {} on track {track_id} disagrees with the embedded SPS \
chroma_format_idc {} — ISO/IEC 14496-15:2017 §8.3.3.1",
record.chroma_format_idc,
info.chroma_format_idc,
),
));
}
if width != 0 && height != 0 && (width as u32, height as u32) != (info.width, info.height) {
report.push(Finding::new(
Severity::Error,
Location::new(0, track_id),
"container-sps-dimension-mismatch",
alloc::format!(
"Sample entry declares {width}x{height} on track {track_id} but the embedded \
SPS decodes to {}x{} — ITU-T H.265 §7.4.3.2.1 cropped coded geometry",
info.width,
info.height,
),
));
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn non_isobmff_input_yields_no_findings() {
let mut report = Report::new();
check_container_codec(&[0x47, 0x00, 0x00, 0x10], &mut report);
assert!(report.is_empty());
}
#[test]
fn garbage_isobmff_yields_no_findings() {
let mut report = Report::new();
check_container_codec(b"\x00\x00\x00\x08ftyp", &mut report);
assert!(report.is_empty());
}
}