Fix audit findings: DTS AMODE bound, key-fetch negative memoization, PGS probe coverage

- dts: accept all 16 legal AMODE channel-arrangement codes (0-15), not just
  0-9. Per ETSI TS 102 114 the 6-bit AMODE field has 16 defined arrangements;
  only 16-63 are reserved. a reference decoder the spec per-AMODE channel table confirms 10-15 are
  decodable 6/7/8-channel layouts. The old bound of 10 dropped spec-legal
  multichannel core frames as undecodable, silencing recoverable audio. Add a
  regression test (literal 0..16 range) that fails if the bound reverts to 10.

- keysource: only memoize a NEGATIVE (empty) key-fetch result when every source
  genuinely ran and none held the key — never when a source Err'd (network down,
  unreachable). A transient outage was being cached as a permanent "no key" for
  the fingerprint, permanently dropping a unit that could be recovered once the
  source came back. Thread an `errored` flag out of the drivers and gate the
  cache insert on it. Tests cover both the recover-after-outage case and that a
  genuine absence is still memoized.

- pgs_forced_probe: add happy-path coverage feeding real synthetic BD-TS PGS
  display sets through the full demux -> parse -> observe -> apply path, both a
  forced verdict landing and a non-forced verdict clearing a vendor flag.

- mp4: correct fit_report doc (audio carried is AC-3/E-AC-3 AND DTS/DTS-HD).

- scan_iso test: add independent fixture expectations (volume id) so the parity
  test is no longer purely tautological against a re-run of the same composition.
This commit is contained in:
Matthew Jackson
2026-07-24 08:32:37 -07:00
parent c00384d4df
commit 3bd2fd23b0
5 changed files with 360 additions and 27 deletions
+50 -3
View File
@@ -688,7 +688,14 @@ fn dts_core_duration_ns(data: &[u8]) -> u64 {
/// `DTS_AMODE_COUNT`; `lfe_present == DTS_LFE_FLAG_INVALID` is rejected.
const DTS_PCMBLOCK_SAMPLES: u32 = 32;
const DTS_SUBBAND_SAMPLES: u32 = 8;
const DTS_AMODE_COUNT: u32 = 10;
/// Number of LEGAL `AMODE` (channel-arrangement) codes. The 6-bit AMODE field
/// (ETSI TS 102 114 §5.3.1) has 16 defined channel arrangements, codes 0-15;
/// only 16-63 are reserved/user-defined and undecodable. ffmpeg's
/// `ff_dca_channels[16] = {1,2,2,2,2,3,3,4,4,5,6,6,6,7,8,8}` confirms all 16 are
/// decodable — codes 10-15 are the 6/7/8-channel layouts. A frame is dropped
/// only when `audio_mode >= DTS_AMODE_COUNT` (i.e. a truly reserved 16-63 code);
/// dropping a legal 10-15 multichannel core would silence recoverable audio.
const DTS_AMODE_COUNT: u32 = 16;
const DTS_LFE_FLAG_INVALID: u32 = 3;
/// Sample rate (Hz) per core `SFREQ` code (ETSI TS 102 114 Table 6-4); a `0`
@@ -1946,8 +1953,11 @@ mod tests {
d[5] = (d[5] & 0x03) | (14u8 << 2);
assert_eq!(core_header_drop_reason(&d), Some(DropReason::PcmBlocks));
// audio_mode >= 10: AMODE = byte7 bits3-0 (high 4) + byte8 bits7-6. Set
// AMODE high nibble to 0xF → audio_mode >= 60.
// audio_mode reserved (>= 16): AMODE = byte7 bits3-0 (high 4) + byte8
// bits7-6. Set AMODE high nibble to 0xF → audio_mode = 60, a genuinely
// RESERVED code (16-63) a decoder rejects. (Codes 10-15 are LEGAL
// multichannel layouts and must NOT be dropped — see
// legal_multichannel_amode_is_not_dropped.)
let mut d = good.clone();
d[7] |= 0x0F;
assert_eq!(core_header_drop_reason(&d), Some(DropReason::Amode));
@@ -1975,6 +1985,43 @@ mod tests {
assert_eq!(core_header_drop_reason(&d), Some(DropReason::PcmRes));
}
#[test]
fn legal_multichannel_amode_is_not_dropped() {
// ETSI TS 102 114 §5.3.1: AMODE is a 6-bit field with 16 LEGAL
// channel-arrangement codes (0-15); only 16-63 are reserved. ffmpeg's
// ff_dca_channels[16] = {1,2,2,2,2,3,3,4,4,5,6,6,6,7,8,8} confirms codes
// 10-15 are decodable 6/7/8-channel layouts. The decodability gate must
// KEEP them — dropping a spec-legal multichannel core silences audio the
// recover-100% goal must preserve.
fn set_amode(core: &mut [u8], amode: u32) {
// audio_mode = (byte7 & 0x0F) << 2 | (byte8 >> 6).
core[7] = (core[7] & 0xF0) | ((amode >> 2) & 0x0F) as u8;
core[8] = (core[8] & 0x3F) | (((amode & 0x03) << 6) as u8);
}
// Every legal code 0-15 is kept — the range is a literal (NOT
// DTS_AMODE_COUNT) so reverting the bound to 10 makes 10-15 fail here.
for amode in 0u32..16 {
let mut core = make_dts_core(512);
set_amode(&mut core, amode);
assert_eq!(
core_header_drop_reason(&core),
None,
"legal AMODE {amode} must not be dropped"
);
}
// The first reserved code (16) and above are still rejected.
for amode in [16u32, 40, 63] {
let mut core = make_dts_core(512);
set_amode(&mut core, amode);
assert_eq!(
core_header_drop_reason(&core),
Some(DropReason::Amode),
"reserved AMODE {amode} must be dropped"
);
}
}
/// Real-data fixture (ignored). Re-parses a raw `.dts` elementary stream
/// through `DtsParser` and writes the emitted access units back out, so the
/// garbage-extension → core-only drop can be validated against an actual