Files
libfreemkv/docs/truehd.md
T
Matthew Jackson 6e14fc0c0c libfreemkv: 1.7.0 groundwork — sector-recycle soundness + CI hardening
Security
- Remove the unsafe Vec::set_len over recycled sector buffers — UB reachable
  from safe code (GHSA-j8ww-f5fg-9pmh, low). Uses Vec::resize; pooling unchanged.

CI / tooling
- clippy on stable with the MSRV single-sourced from Cargo.toml; cargo-deny
  dependency-audit gate with first-party git sources allowed (#15); audience-based
  comment-guard (#17).

Docs
- README (keydb ownership, BD-J parser count), drive-access transport/reset
  notes, mux timeline module-doc links.
2026-09-02 12:40:55 -07:00

5.0 KiB

TrueHD codec parser — extended notes

is_truehd_major_sync — why 0xBA must be exact

The 32-bit word that FOLLOWS the sync is laid out per stream type: TrueHD's format_info carries [31..28] audio_sampling_frequency, the 5-bit 6-channel and 13-bit 8-channel presentation channel-assignment masks; MLP's (0xBB) same word carries quantization word lengths and the MLP group sample-rate fields instead. So every site that decodes format_info with the TrueHD layout must require 0xBA exactly — masking the low sync bit there read a quantization code as the rate nibble and pulled channel masks out of unrelated bits, giving the track header a wrong SamplingFrequency and truehd_au_duration_ns a wrong per-AU increment (audio drifting against video for the whole track). None from these helpers is the safe outcome: the caller falls back to its container-derived rate/channel count.

truehd_sample_rate_hz — rate formula and rationale

The MLP rate formula is (ratebits & 8 ? 44100 : 48000) << (ratebits & 7); rather than evaluate it blindly, truehd_sample_rate_hz is a strict whitelist of the only six rates that occur on real BD/UHD TrueHD. Every other code — the invalid 0xF, the formula-only 0x3/0xB, and all reserved values — returns None.

au_check — corruption decision

Decide whether an access unit is corrupt, updating num_substreams from a valid major sync. Per the MLP/TrueHD access-unit decode rules: a major sync with a bad header CRC, or any AU whose header parity fails, is undecodable. Returns false (not corrupt) when the AU is too short to judge or no major sync has established num_substreams yet — we never drop what we cannot verify. Verified against real TrueHD streams (3600/3600 AUs).

ac3_frame_at_head — AC-3 size lookup

Size (bytes) of the AC-3 frame at the buffer head. Distinguishes three cases the caller must treat differently:

  • Unmappable: the header's fscod/frmsizecod don't map to a real frame size (reserved fscod==3, or frmsizecod >= 38). The caller must drain and resync, NOT wait for more data — waiting would stall forever.
  • NeedMore: a valid size, but the frame isn't fully buffered yet.
  • Frame(n): a complete n-byte AC-3 frame is buffered.

Frame sizing reuses ac3::ac3_frame_size so the AC-3 size table has a single source of truth shared with the AC-3 parser; a returned 0 there (reserved fscod or out-of-range frmsizecod) is the unmappable case.

ac3_boundary_corroborated

Secondary validation for an AC-3 frame of frame_bytes at the buffer head: is its computed end a plausible boundary? Accept when the frame fills the rest of the buffer, or the bytes that follow start another AC-3 sync (0x0B77) or a plausible TrueHD access unit (non-zero 12-bit length within the 32 KiB cap). If none holds, the leading 0x0B77 is more likely a TrueHD AU header that happens to look like AC-3, so the AC-3 reading is rejected.

mlp_major_sync_header_size

Major-sync header size in bytes: base 28, plus 2 + extensions*2 when the extension flag (major-sync byte 25, bit 0) is set (extensions = byte 26 high nibble). ms is the major-sync header, i.e. AU bytes [4..]. None when the AU is too short to contain the full header.

mlp_major_sync_crc_ok

Validate the MLP/TrueHD major-sync header checksum (a CRC-16 with polynomial 0x002D). The stored trailer is the last 2 header bytes; because MLP's checksum is byte-reversed relative to a standard CRC, a standard CRC of the header body XOR the little-endian word before the trailer must equal the trailer read LITTLE-endian. (Comparing it big-endian was the bug this function was fixed for; the body and the inline note are authoritative.)

mlp_substr_header_size — test rationale

The substream count and the substream-directory size are the two numbers that position mlp_parity_ok's check window over the AU header. Every other fixture in this module uses the degenerate shape — one substream, no extraword — so neither function's real behaviour was ever exercised: a constant answer agreed with all of them, and would then mis-window the parity check on the multi-substream AUs that carry 7.1 and Atmos, judging clean audio corrupt (or corrupt audio clean).

ref_crc16_2d test oracle

Independent bitwise CRC-16 (poly 0x002D, init 0, MSB-first) — a separate oracle from crc16_mlp, so a fixture built with it is not tautological with the validator under test. Anchored to the catalogue check value (0x4FF7 for "123456789") so the oracle itself is proven correct without reference to the code under test.

finalize_major_sync test helper

Turn a synthetic major-sync AU (sync bytes already set at offset 4, any format_info set) into one that passes the decodability gate: 1 substream, a clean substream directory, a valid major-sync CRC-16, and a valid header parity nibble. Mirrors what a real encoder writes (verified against real TrueHD streams). The AU must be ≥ 36 bytes (4 AU header + 28 major-sync header + 2 directory + slack), which every make_truehd_unit(≥200) is.