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:
+50
-3
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user