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.
8.6 KiB
DVD Title Scan (src/disc/dvd.rs) — Internal Notes
Rationale, regression history, and test-fixture layout notes that were
originally inline comments/doc-comments on private items in
src/disc/dvd.rs, relocated here to satisfy the comment-guard's internal-
comment cap. Each pointed-to site in the source carries a short // See docs/dvd.md — <topic> line.
Disc::scan_dvd_titles halt handling
Cancellation: halt is polled before the IFO tree is read, and an IFO read
that fails with Error::Halted — how a live drive reports a Stop, since
Drive::checked_exec fails every command once its flag is set — is
propagated rather than swallowed. Every other IFO failure keeps its
best-effort Ok(vec![]).
It has to be an error and not an empty title list, for the same reason
spelled out on Disc::scan_hddvd_titles: a cancelled enumeration that
returned Ok would be indistinguishable from a disc that genuinely holds
fewer titles. This one was the worst of the three enumerators — a bare
Err(_) => return Vec::new() turned an operator Stop into ZERO titles at
rc=0, a disc reported as carrying no video at all.
scan_dvd_titles returns the scanned titles plus, when the disc's own
First-Play navigation deterministically dispatches to a title, that title's
playlist_id — the nav-resolved main feature. The caller feeds it into
sort_titles_by_main_feature as the DVD nav_feature, the same seam the BD
path uses. It is None (existing ranking heuristics stand) whenever the
navigation is menu-only, malformed, or does not converge.
DefaultDuration / cadence TODO
measured_cicp TODO(spec): populate top_field_first + measured colour
from Mpeg2Parser once tracks are built after frame parsing — needs a
CodecParser->title channel like codec_private. Until wired, None
falls back to TFF / PAL-NTSC guess.
DefaultDuration TODO(spec): comes from IFO frame_rate, so a
soft-telecined 23.976-in-29.97 DVD reports 29.97 instead of true film
rate. Cadence is detectable via nb_fields (2:3 pulldown); needs the same
channel as top_field_first above.
Test fixture builders: build_vmg / build_vts
build_vmg builds a VIDEO_TS.IFO: magic "DVDVIDEO-VMG"@0, TT_SRPT sector
ptr@0xC4. TT_SRPT lives at tt_srpt_sector*2048: num_titles(u16)@0, then
12-byte entries from +8. Each entry: num_chapters(u16)@+2, vts_number@+6,
vts_title_num@+7.
build_vts builds a VTS_XX_0.IFO. Layout per ifo.rs:
- magic "DVDVIDEO-VTS"@0
vtstt_vobs(Title VOBS start sector, u32 BE)@0xC4 — the productionvob_start_sectorthe cell sectors are relative to. (0xC0 isvtsm_vobs, the menu VOBS, which the scan must NOT use.)- VTS_PGCIT sector ptr(u32 BE)@0xCC
- video attr byte@0x200
num_audio(u16 BE)@0x202, audio blocks (8B) @0x204num_subs(u16 BE)@0x254, subtitle blocks (6B) @0x256
PGCIT (at pgcit_sector*2048): num_pgcs(u16)@0, PGC info entries (8B)
from +8 with PGC byte offset(u32 BE)@+4. PGC: nr_programs@0x02,
nr_cells@0x03, BCD time@0x04, pgm_map ptr@0xE6, cell_playback ptr@0xE8
(both u16 BE rel to PGC start).
HaltingReader test double
A SectorSource that fails every read at or above halt_at with
Error::Halted — how a LIVE DRIVE behaves once the operator presses Stop:
Drive::checked_exec fails every SCSI command with Halted from then on,
and Drive::read deliberately preserves the variant. Reads below the
threshold still succeed, so the scan gets far enough to have something to
truncate.
Test: halted_ifo_read_is_not_reported_as_a_shorter_disc
A Stop on a LIVE DRIVE never touches ScanOptions::halt: Drive has its
own flag and checked_exec fails every SCSI command with Error::Halted
once it is set. The DVD enumerator must not swallow that into a
successful scan.
This was the worst of the three enumerators. RED BEFORE GREEN, two distinct swallows, both measured with the fix reverted:
ifo::parse_vmgtreats a failed title set as a placeholder entry and continues, so a cancel landing on VTS_02's IFO returnedOk([VTS_01_1.VOB])— one title from a two-title disc.scan_dvd_titles'sErr(_) => return Vec::new()turned a cancel landing on VIDEO_TS.IFO itself into ZERO titles at rc=0 — a disc reported as holding no video at all.
Both are indistinguishable from a real disc, and both are now
Err(Error::Halted).
Fixture: two title sets, VTS_01's IFO at PART_START+6000, VTS_02's at
PART_START+7000. Both ICBs sit far below, so filesystem metadata
resolves fine and only the second title set's CONTENT read is cancelled —
the truncation case.
Test: scan_dvd_titles_uses_title_vobs_not_menu_vobs
Regression (first-play menu prepended to the feature): vob_start must
come from the Title VOBS pointer vtstt_vobs (VTS_IFO 0xC4), NOT the
menu VOBS pointer vtsm_vobs (0xC0). On discs with a per-title menu —
e.g. the Universal "the parental level has been set, press yes" first-play
still — vtsm_vobs points at that menu VOB, which sits just before the
title VOB. Cell first_sector values are relative to vtstt_vobs; reading
0xC0 prepended the menu and shifted every extent back by
vtstt_vobs - vtsm_vobs, so the rip opened on the parental prompt instead
of the movie (one real NTSC R1 disc: vtsm=44, vtstt=3640).
Here build_vts stamps vtstt_vobs = 3640 (0xC4); the test additionally
stamps a different vtsm_vobs = 44 (0xC0). The extent must resolve
from 3640.
Test: scan_dvd_titles_extent_is_absolute_three_term_sum
ABSOLUTE-REBASE regression (real-disc menu-at-start fix): ifo::parse_vts
now sets vob_start_sector = file_start_lba(IFO) + vtstt_vobs, so an
extent's start_lba must equal the sum of THREE independent terms — the
IFO file's absolute on-disc LBA, the IFO-relative vtstt_vobs (0xC4), and
the cell's first_sector — none of which may be dropped. The earlier code
used the bare relative vtstt_vobs, placing every extent ifo_lba
sectors too early (the rip opened in the VMGI/menu region before drifting
into the movie). The other tests fold two of the three terms together
(zero cell offset, or a single combined expectation); this one keeps all
three distinct and non-overlapping so a regression to ANY two-term
combination is caught.
Test: scan_dvd_titles_lpcm_routes_to_a0_pid_range
LPCM SCAN ROUTING (audit §2 / §5 #6): the 0xA0..=0xA7 PID range was never
exercised in the dvd.rs scan. An LPCM stream (coding_mode 4) at audio
position 1 must get sub_stream_id 0xA1 → PID 0xBDA1 via
dvd_audio_pid, distinct from the AC-3 0xBD80 space, with its real
channel count preserved.
Test: scan_dvd_titles_multiple_vobsub_tracks_distinct_pids
MULTI-VOBSUB SCAN (audit §2 / §5 #6): the single-subtitle test covered one
track; a multi-subtitle VTS must emit one Stream::Subtitle per entry
with distinct PIDs (0x20 + ordinal) and per-language tags, all sharing
the PGC palette codec_data.
stamp_first_play_jumptt test helper
Stamp a First-Play PGC into a VMG whose pre-command list is a single
unconditional JumpTT ttn, so dvdnav::resolve_main_title resolves that
title through TT_SRPT. Placed in sector 0, clear of the magic / FP_PGC /
TT_SRPT pointers build_vmg already writes.
Test: scan_dvd_titles_nav_promotes_first_play_target
The DVD First-Play nav promotion branch (issue #40): when
resolve_main_title returns a (vtsn, vts_ttn) target, scan_dvd_titles
must map it — via the 1-based vts_ttn == vts_title_idx + 1 join within
the matching vts_number — to the running title_number, and surface it
as the nav_feature. TT_SRPT here maps title 2 → (VTS 2, title-in-set 1);
the First-Play unconditionally JumpTT 2, so the promoted feature must be
the SECOND scanned title (VTS_02 → title_number 2). An off-by-one on the
join or a vtsn/vts_ttn field swap would promote the wrong title or
none.
Test: scan_dvd_titles_drops_leading_scene_index_cell
End-to-end bug-4 fix: a feature PGC that opens with a leading interleaved- angle sub-block cell (category 0x90 = in-block cell of an angle block) must have that cell DROPPED from the muxed extents, so the rip starts at the real feature. Chapters shift earlier by the dropped duration.
Test: scan_dvd_titles_mp2_audio_pid_fallback_is_additive
Audio PID fallback (dvd.rs Disc::scan_dvd_titles): when an audio stream
has no on-wire private_stream_1 sub-stream id — MP1/MP2 audio, per
ifo::assign_audio_sub_stream_ids — the PID falls back to 0xBD00 + i
where i is the stream's positional index in the IFO audio-attribute
table. Two MPEG-audio (coding_mode 2) streams must land on two DISTINCT,
correctly-offset PIDs: 0xBD00 and 0xBD01. This pins the + (not -/*)
so the second stream doesn't collide with, or wrap under, the first.