diff --git a/docs/aacs.md b/docs/aacs.md index daba46c..e892ec4 100644 --- a/docs/aacs.md +++ b/docs/aacs.md @@ -65,27 +65,38 @@ if disc.encrypted { } } -// Read content -- decryption is automatic -let mut reader = disc.open_title(&mut session, 0).unwrap(); -while let Some(unit) = reader.read_unit().unwrap() { - // decrypted content +// Read content -- decryption is applied on read by the DiscStream decorator. +// Live disc does NOT go through the URL resolver: `input("disc://...")` returns +// Error::DiscUrlNotDirect by design. +let keys = disc.decrypt_keys(); +let mut stream = DiscStream::new( + Box::new(drive), + disc.titles[0].clone(), + keys, + batch_sectors, + disc.titles[0].content_format, + false, // raw: false → decrypt on read + None, // halt +)?; +while let Ok(Some(frame)) = stream.read() { + // decrypted PES frames } ``` -The application never touches keys, never calls decryption functions, and never -manages handshakes. All of that is internal to `Disc::scan()` and the content -reader. +The application never calls decryption functions and never manages the +drive-level handshake. It DOES own key resolution — see below. -### KEYDB Location +### Key resolution is the caller's job -`ScanOptions` controls where the keydb is loaded from. If no explicit path is -set, the library checks the standard config locations. To specify an explicit -path: +`libfreemkv` is **lookup-free: it resolves no keys and reads no keydb.** There is +no `ScanOptions::with_keydb`, and `ScanOptions` has no keydb field — its only +scan input is the optional drive credentials for the live-drive authenticated +handshake. -```rust -let opts = ScanOptions::with_keydb("/path/to/keydb.cfg"); -let disc = Disc::scan(&mut session, &opts).unwrap(); -``` +The caller resolves a key out-of-band through a key source and applies it with +[`Disc::decrypt_with`]. `freemkv-keysources` is the crate that implements the +keydb and key-server sources; `ScanOptions::key_sources` takes them as +`Box`. ### AacsState @@ -97,7 +108,7 @@ After a successful scan, `disc.aacs` contains an `AacsState`: | `bus_encryption` | `bool` | Whether bus encryption is active | | `mkb_version` | `Option` | MKB version from disc | | `disc_hash` | `String` | Identifier for the disc's key-input files | -| `key_source` | `KeySource` | How the disc's key was resolved | +| `key_source` | `KeyOrigin` | How the disc's key was resolved | ## keydb.cfg diff --git a/src/ifo.rs b/src/ifo.rs index 9bdee53..88e6923 100644 --- a/src/ifo.rs +++ b/src/ifo.rs @@ -571,10 +571,14 @@ fn parse_audio_attr(data: &[u8], offset: usize) -> Result { let b1 = byte_at(data, offset + 1)?; let coding_mode = (b0 >> 5) & 0x07; + // DVD-Video audio_coding_mode. Modes 2 and 3 are both MPEG audio Layer II + // (3 adds the MPEG-2 multichannel extension), so both are Codec::Mp2. + // Mode 2 previously mapped to Codec::Mpeg1 — a VIDEO variant, so + // Codec::kind() reported Video and the audio stream was classified and + // handled as video everywhere downstream. let codec = match coding_mode { 0 => Codec::Ac3, - 2 => Codec::Mpeg1, - 3 => Codec::Mp2, + 2 | 3 => Codec::Mp2, 4 => Codec::Lpcm, 6 => Codec::Dts, _ => Codec::Unknown(coding_mode), @@ -1218,6 +1222,33 @@ mod tests { assert_eq!(crate::mux::ps::dvd_audio_pid(0x89), Some(0xBD89)); } + #[test] + fn every_audio_coding_mode_maps_to_an_audio_codec() { + // An audio attribute block must never yield a codec whose kind() is Video. + // Mode 2 (MPEG-1 audio Layer II) mapped to Codec::Mpeg1 — the MPEG-1 VIDEO + // variant — so a DVD MPEG-audio stream was classified as video downstream. + for (mode, want) in [ + (0u8, Codec::Ac3), + (2, Codec::Mp2), + (3, Codec::Mp2), + (4, Codec::Lpcm), + (6, Codec::Dts), + ] { + let mut data = vec![0u8; 16]; + data[0] = mode << 5; + data[2] = b'e'; + data[3] = b'n'; + let attr = parse_audio_attr(&data, 0).unwrap(); + assert_eq!(attr.codec, want, "coding_mode {mode} must map to {want:?}"); + assert_eq!( + attr.codec.kind(), + crate::disc::CodecKind::Audio, + "coding_mode {mode} produced {:?}, whose kind is not Audio", + attr.codec + ); + } + } + #[test] fn audio_attr_dts() { let mut data = vec![0u8; 16];