3D MVC mux: audit round 2 (converged)

Second audit round converged (severity collapsed 6 HIGH -> 1; the one
HIGH was a bounded 32-element scan, not a defect; the sole spec MEDIUM
was the same false-positive re-raised — 0xBF matches ISO/IEC 14496-15
§7.6.2 verbatim). One genuine robustness fix plus coverage:

- extract_mvc_params: skip a zero-length NAL instead of abandoning the
  scan, so a stray length prefix before the subset SPS/PPS no longer
  silently drops 3D signalling. Test proves params after a zero-length
  NAL are still found.
- Tests: parser_for_mvc_dependent routes H.264 to a passthrough parser;
  passthrough with an IDR does not re-assert param sets (the keyframe &&
  !mvc branch).
- Document the per-playlist (not per-clip) is_3d latching as a known
  limitation (real main-feature playlists are uniformly 3D).
This commit is contained in:
Matthew Jackson
2026-07-13 11:39:07 -07:00
parent d4021114cd
commit 422f2b6bcf
3 changed files with 72 additions and 2 deletions
+48
View File
@@ -714,6 +714,54 @@ mod tests {
assert!(types.contains(&20), "slice kept: {types:?}");
}
#[test]
fn parser_for_mvc_dependent_h264_is_passthrough() {
// The dependent-view stream must get a passthrough parser: a PPS in a
// non-keyframe AU is kept in-band, not stripped like the base parser.
let mut p = crate::mux::codec::parser_for_mvc_dependent(crate::disc::Codec::H264, false);
let mut d = Vec::new();
d.extend_from_slice(&h264_nal(0x68, &[0xCE, 0x01])); // PPS (8)
d.extend_from_slice(&h264_nal(0x74, &[0x11, 0x22])); // slice-ext (20)
let f = p.parse(&make_pes(d, Some(90000)));
assert_eq!(f.len(), 1);
let types: Vec<u8> = h264_nals_in(&f[0].data)
.iter()
.map(|n| n[0] & 0x1F)
.collect();
assert!(
types.contains(&8),
"dependent parser keeps PPS in-band (passthrough): {types:?}"
);
}
#[test]
fn mvc_passthrough_with_idr_does_not_reassert_param_sets() {
// With an IDR present (keyframe=true), passthrough must NOT re-assert the
// param sets (the `keyframe && !mvc` guard), so SPS/PPS appear exactly
// once — a duplicate would corrupt the dependent BlockAdditional.
let mut p = H264Parser::new().with_mvc_passthrough(true);
let mut d = Vec::new();
d.extend_from_slice(&h264_nal(0x67, &[0x42, 0x00, 0x1E, 0x01])); // SPS (7)
d.extend_from_slice(&h264_nal(0x68, &[0xCE, 0x01])); // PPS (8)
d.extend_from_slice(&h264_nal(0x65, &[0x88, 0x00])); // IDR slice (5)
let f = p.parse(&make_pes(d, Some(90000)));
assert_eq!(f.len(), 1);
let types: Vec<u8> = h264_nals_in(&f[0].data)
.iter()
.map(|n| n[0] & 0x1F)
.collect();
assert_eq!(
types.iter().filter(|&&t| t == 7).count(),
1,
"exactly one SPS, no keyframe re-assert under passthrough: {types:?}"
);
assert_eq!(
types.iter().filter(|&&t| t == 8).count(),
1,
"exactly one PPS, no keyframe re-assert under passthrough: {types:?}"
);
}
#[test]
fn h264_populates_measured_coding_type_and_source() {
use super::super::coding::CodingType;