AACS pipeline reshape + TrueHD metadata + central consts + clippy/fmt clean

- AACS: delete in-lib keydb parser (Step 3); boil-down primitives
  (mk_from_dk/vuk_from_mk/uk_from_vuk) + newtypes; KeySource->get_uk(ctx)+
  ResolveCtx; Unlocker->unlock()->Result<Vid,UnlockError> + AacsCertUnlocker;
  OEM bus-key gate (AacsBusKeyUnavailable); structured ResolutionTrace (Step 4).
- TrueHD: sample-rate from major-sync, Atmos label, 44.1k AU duration.
- consts: central media/format constants module; 17 duplicate const-defs
  centralized (sector/TS-packet/source-packet); mpls stream-entry + category
  codes named.
- clippy --all-targets -D warnings clean (1.86); fmt clean; 2199 lib tests.
This commit is contained in:
Matthew Jackson
2026-06-26 12:19:24 -07:00
parent 05729f5dfe
commit decb87a250
48 changed files with 2208 additions and 2031 deletions
+44 -42
View File
@@ -6,11 +6,9 @@
//!
//! This demuxer extracts PES packets from selected PIDs, with PTS/DTS timestamps.
/// BD transport stream packet size (4-byte extra header + 188-byte TS).
const BD_TS_PACKET_SIZE: usize = 192;
use crate::consts::BD_SOURCE_PACKET_BYTES;
/// Standard TS packet size.
const TS_PACKET_SIZE: usize = 188;
use crate::consts::TS_PACKET_BYTES;
/// TS sync byte.
const SYNC_BYTE: u8 = 0x47;
@@ -259,13 +257,13 @@ impl TsDemuxer {
// call, complete it from the head of `data` without touching
// the rest of `data`.
if !self.remainder.is_empty() {
let need = BD_TS_PACKET_SIZE - self.remainder.len();
let need = BD_SOURCE_PACKET_BYTES - self.remainder.len();
if data.len() < need {
// Still not a full packet — accumulate and wait.
self.remainder.extend_from_slice(data);
return completed;
}
let mut boundary = [0u8; BD_TS_PACKET_SIZE];
let mut boundary = [0u8; BD_SOURCE_PACKET_BYTES];
boundary[..self.remainder.len()].copy_from_slice(&self.remainder);
boundary[self.remainder.len()..].copy_from_slice(&data[..need]);
self.remainder.clear();
@@ -279,10 +277,10 @@ impl TsDemuxer {
}
// Aligned-packets fast path — reads directly out of `data`.
while offset + BD_TS_PACKET_SIZE <= data.len() {
let packet = &data[offset..offset + BD_TS_PACKET_SIZE];
while offset + BD_SOURCE_PACKET_BYTES <= data.len() {
let packet = &data[offset..offset + BD_SOURCE_PACKET_BYTES];
let src = self.pkt_source(offset);
offset += BD_TS_PACKET_SIZE;
offset += BD_SOURCE_PACKET_BYTES;
self.process_packet(packet, src, &mut completed);
}
// Advance the running base past every byte consumed this feed so the
@@ -295,7 +293,7 @@ impl TsDemuxer {
// prevent unbounded growth on a desynchronised stream).
if offset < data.len() {
let leftover = &data[offset..];
if leftover.len() < BD_TS_PACKET_SIZE {
if leftover.len() < BD_SOURCE_PACKET_BYTES {
self.remainder.extend_from_slice(leftover);
} else {
self.remainder.clear();
@@ -353,7 +351,7 @@ impl TsDemuxer {
4
};
if payload_start >= TS_PACKET_SIZE {
if payload_start >= TS_PACKET_BYTES {
return;
}
// adaptation == 0x02 → AF only, no payload.
@@ -532,7 +530,7 @@ fn is_resync_point(data: &[u8], offset: usize) -> bool {
if data.get(offset + 4) != Some(&SYNC_BYTE) {
return false;
}
match data.get(offset + BD_TS_PACKET_SIZE + 4) {
match data.get(offset + BD_SOURCE_PACKET_BYTES + 4) {
Some(&b) => b == SYNC_BYTE,
None => true, // last packet in the buffer — no follower to corroborate
}
@@ -544,7 +542,7 @@ fn is_resync_point(data: &[u8], offset: usize) -> bool {
/// Accounts for the adaptation_field_control (bits 5:4 of the 4th TS header
/// byte). Returns `None` when the packet carries no payload (AFC 0b10 = AF
/// only, or the reserved 0b00) or when the adaptation field length runs past
/// the packet. `pkt` must be at least [`BD_TS_PACKET_SIZE`] bytes.
/// the packet. `pkt` must be at least [`BD_SOURCE_PACKET_BYTES`] bytes.
fn psi_payload_base(pkt: &[u8]) -> Option<usize> {
// TS header is pkt[4..]; byte pkt[7] holds AFC in bits 5:4.
let afc = (pkt[7] >> 4) & 0x03;
@@ -555,7 +553,7 @@ fn psi_payload_base(pkt: &[u8]) -> Option<usize> {
// payload starts after it.
let af_len = pkt[8] as usize;
let base = 9 + af_len; // 4 + 4 + 1(length byte) + af_len
if base < BD_TS_PACKET_SIZE {
if base < BD_SOURCE_PACKET_BYTES {
Some(base)
} else {
None // AF overruns the packet
@@ -587,7 +585,7 @@ fn psi_payload_base(pkt: &[u8]) -> Option<usize> {
/// matching section is found.
fn collect_psi_section(data: &[u8], target_pid: u16, table_id: u8) -> Option<Vec<u8>> {
let mut offset = 0;
while offset + BD_TS_PACKET_SIZE <= data.len() {
while offset + BD_SOURCE_PACKET_BYTES <= data.len() {
if !is_resync_point(data, offset) {
offset += 1;
continue;
@@ -599,12 +597,13 @@ fn collect_psi_section(data: &[u8], target_pid: u16, table_id: u8) -> Option<Vec
// Locate the payload (pointer_field) accounting for any
// adaptation field. A packet with no payload (AF only) or an
// AF that overruns the packet is skipped.
let Some(payload_off) = psi_payload_base(&data[offset..offset + BD_TS_PACKET_SIZE])
let Some(payload_off) =
psi_payload_base(&data[offset..offset + BD_SOURCE_PACKET_BYTES])
else {
offset += BD_TS_PACKET_SIZE;
offset += BD_SOURCE_PACKET_BYTES;
continue;
};
let payload = &data[offset + payload_off..offset + BD_TS_PACKET_SIZE];
let payload = &data[offset + payload_off..offset + BD_SOURCE_PACKET_BYTES];
// pointer_field is the FIRST payload byte; the section starts
// pointer_field bytes after it. Bound the start to within
// THIS packet's payload — a pointer that runs into the next
@@ -612,7 +611,7 @@ fn collect_psi_section(data: &[u8], target_pid: u16, table_id: u8) -> Option<Vec
let pointer = payload[0] as usize;
let sec_start = 1 + pointer;
if sec_start + 3 > payload.len() || payload[sec_start] != table_id {
offset += BD_TS_PACKET_SIZE;
offset += BD_SOURCE_PACKET_BYTES;
continue;
}
let section_len =
@@ -631,9 +630,9 @@ fn collect_psi_section(data: &[u8], target_pid: u16, table_id: u8) -> Option<Vec
// dropped/duplicated packet → the assembled section is corrupt, so
// abandon it rather than splicing in misordered payload.
let mut expected_cc = ((data[offset + 7] & 0x0F) + 1) & 0x0F;
let mut scan = offset + BD_TS_PACKET_SIZE;
let mut scan = offset + BD_SOURCE_PACKET_BYTES;
let mut desync = false;
while scan + BD_TS_PACKET_SIZE <= data.len() && section.len() < total {
while scan + BD_SOURCE_PACKET_BYTES <= data.len() && section.len() < total {
// Require a corroborated resync point (this sync byte plus the
// follower one packet ahead) before trusting the header. A
// stray 0x47 in corrupt payload would otherwise misread the CC
@@ -653,16 +652,19 @@ fn collect_psi_section(data: &[u8], target_pid: u16, table_id: u8) -> Option<Vec
expected_cc = (cc + 1) & 0x0F;
// Continuation packets may also carry an adaptation
// field; compute their payload base the same way.
if let Some(cbase) = psi_payload_base(&data[scan..scan + BD_TS_PACKET_SIZE]) {
section.extend_from_slice(&data[scan + cbase..scan + BD_TS_PACKET_SIZE]);
if let Some(cbase) =
psi_payload_base(&data[scan..scan + BD_SOURCE_PACKET_BYTES])
{
section
.extend_from_slice(&data[scan + cbase..scan + BD_SOURCE_PACKET_BYTES]);
}
}
scan += BD_TS_PACKET_SIZE;
scan += BD_SOURCE_PACKET_BYTES;
}
if desync {
// Restart PSI assembly from the next packet after this PUSI;
// a later clean copy of the section may still appear.
offset += BD_TS_PACKET_SIZE;
offset += BD_SOURCE_PACKET_BYTES;
continue;
}
if section.len() >= total {
@@ -672,7 +674,7 @@ fn collect_psi_section(data: &[u8], target_pid: u16, table_id: u8) -> Option<Vec
// Incomplete section (truncated input) — stop looking.
return None;
}
offset += BD_TS_PACKET_SIZE;
offset += BD_SOURCE_PACKET_BYTES;
}
None
}
@@ -833,7 +835,7 @@ mod tests {
/// continuity_counter, carrying `payload` (truncated/padded to 184 bytes,
/// payload-only adaptation).
fn ts_payload_packet(pid: u16, pusi: bool, cc: u8, payload: &[u8]) -> Vec<u8> {
let mut pkt = vec![0u8; BD_TS_PACKET_SIZE];
let mut pkt = vec![0u8; BD_SOURCE_PACKET_BYTES];
pkt[4] = SYNC_BYTE;
pkt[5] = ((pid >> 8) as u8) & 0x1F;
if pusi {
@@ -934,7 +936,7 @@ mod tests {
/// Wrap a 188-byte TS packet body in a 192-byte BD-TS packet
/// (4-byte timecode prefix the scanner skips).
fn bdts_packet(body: [u8; 184], pid: u16, pusi: bool) -> Vec<u8> {
let mut pkt = vec![0u8; BD_TS_PACKET_SIZE];
let mut pkt = vec![0u8; BD_SOURCE_PACKET_BYTES];
// 4-byte timecode prefix is ignored; leave zero.
pkt[4] = SYNC_BYTE;
pkt[5] = ((pid >> 8) as u8) & 0x1F;
@@ -1010,7 +1012,7 @@ mod tests {
/// Build a 192-byte BD-TS data packet on `pid` carrying `payload`
/// (payload-only adaptation, truncated/padded to fit one packet).
fn data_packet(pid: u16, pusi: bool, payload: &[u8]) -> Vec<u8> {
let mut pkt = vec![0u8; BD_TS_PACKET_SIZE];
let mut pkt = vec![0u8; BD_SOURCE_PACKET_BYTES];
pkt[4] = SYNC_BYTE;
pkt[5] = ((pid >> 8) as u8) & 0x1F;
if pusi {
@@ -1018,7 +1020,7 @@ mod tests {
}
pkt[6] = (pid & 0xFF) as u8;
pkt[7] = 0x10; // payload only, no adaptation field
let room = TS_PACKET_SIZE - 4; // 184 ES bytes after the 4-byte TS header
let room = TS_PACKET_BYTES - 4; // 184 ES bytes after the 4-byte TS header
let n = payload.len().min(room);
pkt[8..8 + n].copy_from_slice(&payload[..n]);
pkt
@@ -1029,7 +1031,7 @@ mod tests {
/// payload base computation in scan_streams.
fn pmt_packet_with_af(pmt_pid: u16, entries: &[(u8, u16)]) -> Vec<u8> {
let af_len: u8 = 2; // 1 flags byte + 1 stuffing byte
let mut pkt = vec![0u8; BD_TS_PACKET_SIZE];
let mut pkt = vec![0u8; BD_SOURCE_PACKET_BYTES];
pkt[4] = SYNC_BYTE;
pkt[5] = (((pmt_pid >> 8) as u8) & 0x1F) | 0x40; // PUSI set
pkt[6] = (pmt_pid & 0xFF) as u8;
@@ -1039,7 +1041,7 @@ mod tests {
pkt[10] = 0xFF; // stuffing
// Payload (PSI) begins at 4 + 4 + 1 + af_len = 11.
let payload_off = 4 + 4 + 1 + af_len as usize;
let mut body = vec![0xFFu8; BD_TS_PACKET_SIZE - payload_off];
let mut body = vec![0xFFu8; BD_SOURCE_PACKET_BYTES - payload_off];
body[0] = 0x00; // pointer_field
let s = 1;
body[s] = 0x02; // table_id = PMT
@@ -1270,9 +1272,9 @@ mod tests {
}
let mut pmt = pmt_two_packets(pmt_pid, &entries);
// Corrupt the continuation packet's CC. pmt is exactly two BD-TS
// packets; the second starts at BD_TS_PACKET_SIZE. Its CC (low nibble
// packets; the second starts at BD_SOURCE_PACKET_BYTES. Its CC (low nibble
// of offset+7) was set to 1 by pmt_two_packets; flip it to a gap (5).
let cc_off = BD_TS_PACKET_SIZE + 7;
let cc_off = BD_SOURCE_PACKET_BYTES + 7;
pmt[cc_off] = (pmt[cc_off] & 0xF0) | 0x05;
let mut data = pat_packet(pmt_pid);
@@ -1296,16 +1298,16 @@ mod tests {
/// bytes the demuxer must produce, unlike `data_packet` which leaves
/// zero padding that a length-0 (unbounded) PES would absorb as ES.
fn es_packet_exact(pid: u16, pusi: bool, payload: &[u8]) -> Vec<u8> {
const TS_PAYLOAD: usize = 184;
assert!(payload.len() <= TS_PAYLOAD);
let mut pkt = vec![0u8; BD_TS_PACKET_SIZE];
use crate::consts::TS_PAYLOAD_BYTES;
assert!(payload.len() <= TS_PAYLOAD_BYTES);
let mut pkt = vec![0u8; BD_SOURCE_PACKET_BYTES];
pkt[4] = SYNC_BYTE;
pkt[5] = ((pid >> 8) as u8) & 0x1F;
if pusi {
pkt[5] |= 0x40;
}
pkt[6] = (pid & 0xFF) as u8;
let pad = TS_PAYLOAD - payload.len();
let pad = TS_PAYLOAD_BYTES - payload.len();
if pad == 0 {
pkt[7] = 0x10; // payload only
pkt[8..8 + payload.len()].copy_from_slice(payload);
@@ -1550,7 +1552,7 @@ mod tests {
demux.feed(&es_packet_exact(pid, true, &start));
// …then an AF-only continuation packet whose "payload" bytes must
// be discarded.
let mut afonly = vec![0u8; BD_TS_PACKET_SIZE];
let mut afonly = vec![0u8; BD_SOURCE_PACKET_BYTES];
afonly[4] = SYNC_BYTE;
afonly[5] = ((pid >> 8) as u8) & 0x1F; // no PUSI
afonly[6] = (pid & 0xFF) as u8;
@@ -1576,7 +1578,7 @@ mod tests {
// the TS packet. The AF bytes must NOT appear in the ES.
let pid = 0x1011;
let mut demux = TsDemuxer::new(&[pid]);
let mut pkt = vec![0u8; BD_TS_PACKET_SIZE];
let mut pkt = vec![0u8; BD_SOURCE_PACKET_BYTES];
pkt[4] = SYNC_BYTE;
pkt[5] = (((pid >> 8) as u8) & 0x1F) | 0x40; // PUSI
pkt[6] = (pid & 0xFF) as u8;
@@ -1616,7 +1618,7 @@ mod tests {
// A larger value runs past the packet and must be discarded.
let pid = 0x1011;
let mut demux = TsDemuxer::new(&[pid]);
let mut pkt = vec![0u8; BD_TS_PACKET_SIZE];
let mut pkt = vec![0u8; BD_SOURCE_PACKET_BYTES];
pkt[4] = SYNC_BYTE;
pkt[5] = (((pid >> 8) as u8) & 0x1F) | 0x40;
pkt[6] = (pid & 0xFF) as u8;
@@ -1808,7 +1810,7 @@ mod tests {
#[test]
fn scan_streams_no_pat_returns_none() {
// Without a PAT (table_id 0x00 on PID 0) there is no program to find.
let data = vec![0u8; BD_TS_PACKET_SIZE * 2]; // all zero, no sync bytes
let data = vec![0u8; BD_SOURCE_PACKET_BYTES * 2]; // all zero, no sync bytes
assert!(scan_streams(&data).is_none());
}