7abcfaab728aaf4c2a308cd690b4c8453554b90c
19
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
7abcfaab72 |
Stop the HD-DVD VTI read and the extract progress channel from hiding loss
Two audit fixes plus two consistency cleanups, all in the changed 1.6.5 surface: - hddvd: the VTI clip-order read used `.ok()`, flattening an unreadable authored order (a scratched sector under the .vti — the name came from the directory, so it is never "absent") into "no order" with no diagnostic. The sibling clip-extent arms log every read failure with its code; this one now does too, then falls back to the per-clip heuristic exactly as an unauthored disc would. Behaviour is otherwise unchanged; the loud line is the point. - extract: the progress sink hardcoded bytes_unreadable_total: 0 and counted every zero-filled hole as good, so a progress-only consumer saw a holed extraction climb to a clean 100%. Thread the running unreadable total through and report the real good/unreadable split. The authoritative ExtractResult was already truthful; only the live channel lied. - labels: correct a stale comment that described testlog's old lock-based capture; it now installs one global subscriber and routes to a thread-local sink. - ps: import SYSTEM_HEADER from consts instead of re-declaring 0xBB, matching its sibling stream-id constants and the file's single-source rule. |
||
|
|
efb69e3ba5 |
Close the Ok-but-empty clip hole on HD-DVD; keep it open, and say why, on BD
The same hole, two disc families, two answers — and the asymmetry is now a
decision written into both files instead of an oversight in one.
`file_extents` can return `Ok` and still yield no usable extent: an empty
allocation-descriptor list, or one every entry of which the `sectors > 0 &&
lba > 0` filter discards. An ordinary zero-byte file reaches it; no crafted
disc is needed.
On HD-DVD that was the flagship failure shape. The clip entered neither
`clip_extents` nor `unusable`, and nothing was logged, so the composer's
`any(|n| unusable.contains(..))` guard missed it while the
`filter(|n| clip_extents.contains_key(..))` beside it quietly deleted the
part: a `FEATURE_2.EVO` of size 0 next to a healthy `FEATURE_1.EVO` composed
a FEATURE title out of part one alone, still advertising the whole runtime,
at rc=0, in silence. Half a movie presented as a whole one. Round 1 accounted
for every `Err` from the resolver and left this route open. It now marks the
clip unusable and logs it under its own new code, E6019
(`E_UDF_NO_USABLE_EXTENT`) — deliberately not the neighbouring E6017, which
would file a zero-length file as an authoring hole and send whoever triages
it at the wrong population.
On Blu-ray the identical hole stays open, as previously decided, and the
reasons are now recorded on both sides. BD has no `unusable` set, so closing
it there means inventing a post-loop "every clip_id must appear in `spans`"
invariant that DROPS the title, and it is not settled that an empty-but-Ok
resolve is always a defect; dropping healthy titles is worse than the gap.
The consequence is milder too: on BD the clip is one PlayItem of an otherwise
whole title, on HD-DVD the feature is COMPOSED from parts. Same hole,
different price.
Also in this change:
* bluray: a non-absence SSIF failure that the `.m2ts` fallback papers over is
logged. `unresolved` had exactly one reader, `if let (None, Some(code))`, so
when `/BDMV/STREAM/SSIF/<clip>.ssif` failed with DiscRead /
UdfAdChainTooLong / UdfEmbeddedData and the base view then resolved, the
code was recorded and thrown away: the title shipped base-view 2D off a 3D
disc at rc=0 with no log at all. The site's own doctrine is "ABSENCE is the
only benign failure". Logged, not refused — the base view is a real rip.
* drive: `wait_ready` polled TEST UNIT READY through a bare `execute` and its
60 x 500 ms loop never read `self.halt`, so a Stop during spin-up did
nothing for ~30 s while every other drive path returns Halted at the next
command boundary. `spin_cycle` issued both START STOP UNIT commands outside
`checked_exec` and slept `SPIN_DOWN_IDLE_SECS` + `SPIN_UP_SETTLE_SECS`
blind — ~15 s deaf to Stop, from the recovery path, exactly when the
operator is most likely to press it. Both now use `checked_exec` and
`sleep_until_halted`, which already lived in this file with four tests and
was `#[cfg(test)]`, called from nowhere. It is production code again.
* drive: a READ(10) that returns GOOD status with a residual underrun was
correctly refused and logged NOWHERE, while the sibling `Err` arm warns with
lba/count/status. A residual-underrunning drive was indistinguishable from a
scratched disc — two populations with opposite remedies. It now warns with
transferred vs expected, which is the whole signal.
* error: `all_error_code_constants_are_unique` was a hand-maintained `vec![]`
naming 109 of the 127 declared codes while its doc claimed to pin them all,
and an earlier audit trusted that claim while assigning new ones. The list
is now derived from the declarations by parsing `include_str!("error.rs")`,
so a new constant is covered the moment it is written. A parser self-test
cross-checks the count and three known name/value pairs, so it cannot pass
vacuously.
* testlog: a test-only `tracing` capture (~120 lines, no new dependency) so
the logging contract is enforced rather than commented. Three sites carry
long comments insisting they log the error's OWN code; putting a literal
back broke nothing. They are pinned now, along with the two new log lines.
Captures are serialised process-wide: `tracing`'s interest cache is global
while `with_default` is thread-local, and the rebuild on the exiting
capture can land after the entering one's, leaving the cache at "never"
while a capture is live. That produced a real empty-event flake.
* disc: `scan_with`'s halt wiring for the BD and DVD enumerators had no test —
every BD/DVD cancellation test calls the scanners directly, so passing
`None` on either branch left the suite green while Stop did nothing.
* mux::network: `accept_from_rejects_stream_without_fmkv_header` half-closes
instead of `Shutdown::Both`, which raced an RST against the server's read
and returned ConnectionReset instead of InvalidInput under load. The port
was already ephemeral; that was never the cause.
Gate: fmt, clippy --all-targets -D warnings, and 3439 tests green on 1.97;
precommit.sh libfreemkv clean.
|
||
|
|
0563b58f2e |
Audit round 7: account for every clip that cannot be resolved
Ten lenses over v1.6.4..HEAD, every claim read against the code before it was believed. Seven confirmed; six are here, one is recorded for the next round. All of these are the same family — a failure wearing the shape of success — which is the family that once shipped 9 MB of ciphertext inside a main-movie m2ts at rc=0. A clip whose extents cannot be resolved is now accounted for, in both disc readers. Only `UdfUnrecordedExtent` used to count: every other way `file_extents` can fail — a scratched sector under the clip's ICB (DiscRead), an allocation-descriptor chain that never terminated, a file whose data is embedded rather than extent-mapped — fell through to the ordinary "file absent" path. On Blu-ray that yielded a title advertising its full runtime with a clip's bytes silently missing, because the size and the play-item timing had already counted it. On HD-DVD it was worse: the clip was never added to `unusable`, so a split feature still composed from FEATURE_1 alone and offered half a movie as the whole thing. Neither emitted a single log line. Absence is still benign — a 2D disc has no .ssif and the extension fallback exists for exactly that. `Halted` is excluded deliberately, and that exclusion is the whole reason the first version of this fix was wrong. Cancellation makes EVERY drive command return `Halted`; classifying it as a disc defect would have dropped each remaining playlist in turn and handed back a truncated title list at success — the same defect, wearing a cancel. `parse_playlist` returns Option and has no channel to propagate a halt, so the existing behaviour is preserved rather than made worse. Propagating it properly is next round's work. Both log sites now emit the error's OWN code instead of a hardcoded 6017. Accounting a scratched disc (E6000) as an authoring hole would send anyone triaging it looking for the wrong thing entirely. AD type 3 is embedded data, not a descriptor list (ECMA-167 4/14.6.8). `read_icb_extents` lumped it in with the reserved values and decoded the file's own CONTENT as (length, LBA) pairs, manufacturing extents out of arbitrary bytes and pointing the reader at unrelated sectors. This same release already taught `read_directory` to honour type 3; this is the file half of that decision. It is an error rather than an empty list, because an empty list reaches the caller as a clip that contributed nothing while its declared duration still counts it — the silent loss pointed the other way. A legally zero-length embedded file still returns an empty list. New code E6018: reusing DiscRead would have mislabelled a deterministic structural property as transient I/O and fed the retry and NonTrimmed machinery a byte that will never change. `file_extents_addressing`, `extents_abs_at` and `AbsExtent` drop to `pub(crate)`. The first hands back unrecorded extents UNFLAGGED, in a shape identical to the safe call's return; its doc says callers must use `file_extents` instead, but a doc comment is not a guard. No dependent crate references any of the three. Three tests close gaps the audit found, each proven red before green: a held AC-3 access unit must not resume as a normal frame after its track poisons; the PS resume cursor must survive a drain that rebases it (three separate mutants caught); and AD type 3 must be refused rather than decoded. The first attempt at the HD-DVD test passed with the fix reverted, which made it worthless — it needed a VTI fixture before the composition path ran at all. Also: four error codes were missing from the uniqueness test that claims to cover every published code, so a new variant reusing 6014, 6016 or 6017 would have passed it. |
||
|
|
68a1a55958 |
Audit round 4-6: disc parsing, extents, codecs and drive faults
Squashed from 12 commits. Every fix was proven red-before-green and killed by a mutation; the reasoning for each is in the private audit record. UDF and extents Honour ICB types rather than assuming a Short AD, so an AD-type-3 directory is no longer decoded from FID bytes into a silently empty listing. Carry the ECMA-167 recorded flag through to the resolvers: an allocated-but-never- written extent used to reach the read plan as ordinary content and splice undefined sectors into the rip. file_extents now refuses such a file, and only when the hole actually occupies byte space — a zero-length one displaces nothing, and refusing on it dropped whole titles off discs that ripped correctly. Type-2 sparse extents are kept alongside type-1; they were falling into a catch-all that exited the descriptor loop and returned a truncated list as complete. merge_ranges no longer claims a sector neither input covered. A short skip or an over-long AD chain errors instead of truncating. HD-DVD and Blu-ray scanning Bound the XPL nesting depth, title count, clips and chapters per title, and memoize the clip-name fallback probe — four separate amplification axes, each of which alone left the worst case unbounded. The clip and title caps are 512, ~10x any retail disc, and a test pins the product of cap and probe budget. The scan is cancellable: it returned Ok with titles carrying no streams when halted, presenting a cancelled scan as a successful one. A clip dropped for an unrecorded extent now says so. Codecs and muxing Resume a held E-AC-3 access unit rather than rescanning from its first frame, and drop it on a discontinuity — a stale hold indexed past the end of the new buffer. Map every ISO 639-1 code instead of collapsing fifteen languages to und. Correct the DVD palette order. Detect a skip past EOF. Drive and I/O Classify dead-bus faults so the wedged-drive path can see them; a catch-all arm had been flattening the variants before the classifier ran. A prefetch producer that dies now reports SourceTerminated instead of Ok(0), which the reader legitimately read as a short read and zero-filled — a whole title could be fabricated and the pass reported complete. Also: charge Ok(0) reads to the CSS crack budget, drop the unreachable soft re-crack, and send disc-derived strings to logs through the debug formatter so a crafted label cannot paint an operator's terminal. |
||
|
|
b9ca75f471 |
Identify a frame clip by provenance, not by inferring it from timestamps
Four audit rounds each fixed one rule in SeamPlan::place and broke another, because the question the rules were answering has no answer. Inside a seamless-branching overlap clip k OUT comes AFTER clip k+1 IN — 57.8s of overlap on the real fixture table — so a single timestamp is legitimately inside two clips, and a clip file is not trimmed to its marks, so it also carries material from before its own IN. No rule over timestamps can say which clip a frame came from, and each attempt was right for one disc layout and silently wrong for another: 65s of rewind, 17 minutes stranded, 28 minutes dropped, 55s refused. Frames already carry the byte offset they were read from (PesFrame::source, stamped by the TS demuxer). Clip now carries the byte span its stream occupies in the title feed, recorded while the extents are gathered. So the clip is a LOOKUP: the offset falls in exactly one span. There is no decision to get wrong. Every track of a clip lives in the same stream file and therefore shares one span, so video, audio and subtitles agree by construction. Divergence between them — each track guessing separately under its own tolerance — is how audio and video ended up on different clips and drifted apart in the first place. spans_trusted gates the whole path: unless the spans tile the feed contiguously from zero, an offset means nothing and provenance is ignored in favour of the mark heuristics, which is the 1.6.0 behaviour. A broken map degrades instead of confidently selecting a wrong clip for every frame. A clip referenced twice reuses its first span (the bytes are read once) and is still trusted. Sources that stamp no provenance — a mkv:// remux, the deserialize hop — take the heuristics, which is what they have always used and where they have always been right, because they have no overlapping clips to be ambiguous about. 36 timeline tests, six of them new and covering: the overlap case marks cannot see, all tracks agreeing, out-of-marks material dropped AND counted, a holed span map, a discontiguous one, a repeated clip, and no provenance at all. |
||
|
|
71686f1407 |
Lint the test code, and fix the 74 findings it had been hiding
Every other repo's CI now runs clippy with --all-targets. libfreemkv, the crate the other seven build against and the one held up as the reference workflow, was the last one still linting the library only — so its ~3,000 tests, by far the largest body of test code in the project, had never been linted at all. Turning the flag on surfaced 74 findings. Most were mechanical and applied with clippy --fix. The rest, by hand: - Four discarded Results in decrypt.rs. css::descramble_region returns a Result and four CSS tests threw it away, so a descramble that FAILED would have surfaced as a confusing buffer-comparison mismatch instead of the actual error. They expect() now. - A dead `kp` field on the PlantedWalk fixture. The test deliberately asserts Kp as the explicit AES-G3(dk, 1) relation from [C] §3.2.4 rather than against a stored value — its doc comment says so — which makes the field not just unused but a trap: the obvious "fix" of asserting against it would quietly weaken the test to comparing the fixture with itself. Removed. - Two hand-rolled ICB counters in the HD-DVD fixtures, a needless mut, three vec!s that only ever needed arrays, a filter_map whose every arm was Some, and a Vec::new()+push chain. - Doc list indentation in mkv.rs and mp4/read.rs, which was mis-rendering in the generated docs. - A five-[u8; 16]-tuple return type named FourLevelParts. Three lints are allowed at the specific sites, with reasons, because they are wrong for this domain: the underscores in the bitstream-header literals mark BITFIELD boundaries, not digit groups, so regrouping them uniformly would satisfy the lint by destroying the only thing they encode; and in three table-validation loops the loop variable is the domain value under test (a DTS SFREQ code, an AMODE value, a palette entry number), which is what the assertion messages name. |
||
|
|
5360f8d309 |
test: salvage the orphaned labels/disc triage, and extract build_labels
Thirteen agents triaging src/labels and src/disc died on a saturated
machine, leaving 5,836 insertions across 28 files uncommitted in a
worktree. Recovered by 3-way apply onto twelve commits of drift; zero
conflicts. The diff was archived to freemkv-private first, because a
worktree is not a backup and this one had already nearly been lost.
One production change, and it is the right one: mpls_universal::parse
read every playlist off the disc AND converted the entries to labels in
a single function, so the conversion — stream-type mapping, dedup key,
the dense global counters — could only be reached through a synthetic
UDF image. Extracted to build_labels(&[Playlist]), which unit tests can
drive from already-parsed values. Behaviour-preserving: same iteration
order, same skip-on-error.
Two collisions resolved by hand:
A second mod pass_progress_tests, written independently against the
same survivors as the one committed in
|
||
|
|
5f8dc392c0 |
Sweep the pinned toolchain to Rust 1.97
The Windows UI needs current winsafe, whose real minimum is 1.89 (its manifest under-declares 1.87 while it uses NonNull::from_ref). Rather than stop at the minimum, this goes to current stable and fixes what that costs. The counter-intuitive result: 1.97 is CHEAPER than 1.89. libfreemkv had 54 clippy errors at 1.89 and 6 at 1.97, because clippy tightened the noisy collapsible_if lint in between. Stopping at the minimum would have been the most expensive choice available. Roughly 47 lints across the eight repos, the large majority auto-fixed: libfreemkv 6, freemkv-engine 14, bdemu 8, freemkv-keysources 7, autorip 6, freemkv-unlock 3, freemkv-i18n 3. The hand-fixed ones are a descending sort to sort_by_key(Reverse), four manual checked-division sites, a loop counter replaced by enumerate, and a loop whose first let-else became a while-let. Worth recording for whoever bumps next: clippy is MSRV-AWARE. Those 54 lints only appear once the crate DECLARES 1.89 or later, because let-chains become available. A bare `cargo +1.89 clippy` against a manifest still pinned at 1.87 reports clean and is meaningless — gate with the real precommit script, which is also the only thing that covers build scripts. The pin still sits below the Mac default, so it keeps doing its job: catching lint drift locally before CI sees it. |
||
|
|
3cb0a8f41c |
remove user-facing English from the library (KeyOrigin::name, hddvd Title)
Library holds ZERO user-facing English (CLAUDE.md). Removed KeyOrigin::name()'s English prose — apps map the typed enum (freemkv gets key_origin_label); diag logs the enum's Debug repr. hddvd unnamed-title fallback 'Title N' -> neutral TITLE_N identifier (UDF volume-label style). |
||
|
|
24e2bc33cf |
hddvd: authoritative title composition from the Advanced-Content playlist
Parse ADV_OBJ/VPLST000.XPL (the real HD-DVD player playlist) with roxmltree into one DiscTitle per <Title>: its PrimaryAudioVideoClip clips in order (EVO via the .MAP sidecar), the titleDuration, the displayName, and the ChapterList. A layer-break split (FEATURE_1+FEATURE_2, feature/feature_Divide) composes into ONE title with the two parts as clips, each carrying its title-time in/out points (45kHz ticks) for seamless-join splicing. Falls back to the clip-name heuristic when no playlist is present. Fixes unknown runtimes (real durations), poor names (FEATURE -> 'Main Movie'), and gives authoritative composition + chapters. Validated on ANCHORMAN (97m), SHAUN (99m), HARRY_POTTER (152m). |
||
|
|
18082d0df1 |
audit: void empty-timeline duration, cover sniff overlap
Round-11 findings from the 10-phase release audit (no real HIGH): - When a no-declared-duration source (HD-DVD) muxes a degenerate single frame at tick 0 with no per-frame duration, max_block_ticks stays 0 and the reserved DURATION placeholder was left as a literal 0.0 (players read that as a zero-length file). Void the element instead, so the Segment omits DURATION as an unknown-duration source did before the back-patch. - Add a regression test for the sniff_video_codec overlap fix (a picture_start_code whose payload begins 00 00 followed by a real start code) so the i+=4 marker skip can't silently regress to i+=3. |
||
|
|
640502d5a8 |
audit: lock DTS rate table, fix sniff overflow-scan, cover decrypt loss
Round-10 findings from the 10-phase release audit: - A finder claimed the DTS SFREQ→rate table was wrong at 11/12; verified it against ffmpeg's avpriv_dca_sample_rates (12k/24k/48k/96k/192k at 11-15) — the table is CORRECT. Added a test that locks the full table so it can't be mis-"fixed". - sniff_video_codec advanced 3 bytes after a matched start code, re-reading the code byte as an overlapping start code; skip the full 4-byte marker. - Guard the HD-DVD next_id title counter with saturating_add so a crafted disc with >65536 clips can't overflow (panic in debug). - Add a test that an undecryptable unit (DecryptFailed) is zero-filled and counted as loss through ExtractResult (complete=false, bytes_lost>0) — the recovery-seam consolidation folded that bucket into bytes_unreadable. |
||
|
|
7d852419b5 |
audit: byte caps on GOP buffers, opener-scan resume, honest video codec
Round-6 findings from the 10-phase release audit: - Wire the documented MAX_PENDING_BYTES byte cap into the MPEG-2 GOP buffer (it was dead code) and add an equivalent MAX_GOP_BYTES cap to the sparse-PTS reorder, so a crafted stream of few-but-huge access units cannot over-allocate — both were bounded only by frame count before. - probe_evo_streams defaulted an unsniffable HD-DVD video stream to H.264, which mis-parses a VC-1 (or still-encrypted) clip into a corrupt track. Emit the video stream only when the codec is actually identified — the honest outcome, matching the audio path (a real clear clip always carries its sequence header at the head). - Resume the AU-opener search from a cursor (like the boundary search), so a long unsynced junk run is O(bytes), not O(buffer) per push. - Mark mpeg2's now-dead MAX_AU_BUFFER test-only; restore #[doc(hidden)] on the aacs probe harness module. - Add regression tests: the 0xFD video-routing guard, the FMTS-is-UHD key state, and the GOP byte caps. |
||
|
|
9066433c29 |
audit: guard 0xFD video routing, carry frame duration, add cap tests
Round-5 findings from the 10-phase release audit: - collect_es routed EVERY extended-stream-id (0xFD) PES into the video ES buffer, so a 0xFD HD-audio sub-stream (MLP/TrueHD) could pollute the video sample and — if it preceded the video PES — stamp the video track with the audio PID, losing the video. Only the VC-1 extension (0x55) is now treated as video; routing 0xFD audio to its own track is deferred to the HD-DVD program-chain follow-up. - The sparse-PTS reorder now carries its calibrated per-frame duration onto each frame, so the muxer emits a BlockDuration and the back-patched Segment Duration covers the final frame instead of understating it. - Add regression tests for the MAX_MARKS and MAX_VTI_HITS caps (promote MAX_VTI_HITS to module scope); make the differential-test factory array a named type; drop an identity-op in a reorder test. |
||
|
|
c81a6e05cd |
audit: fix AU mark-field loss, VTI tie determinism, and mark/perf issues
Round-4 findings from the 10-phase release audit (the first fully clean round; it dug into the new #22/#18 refactor code): - AuAssembler closed each AU from only the FRONT mark's fields, so when one PES fragment carried the source and a later fragment of the same AU carried the PTS, the second field was dropped — a regression vs the old separate pts/source mark deques. Now merge the first Some of each field across all in-range marks. - parse_vti_clip_order picked the largest residue bucket with HashMap::into_values().max_by_key(), nondeterministic on a size tie (randomized HashMap iteration) — could select a different clip table run-to-run. Break ties by smallest offset. - Bound the marks/disc_marks deques (MAX_MARKS): the buf-size cap prunes marks only when bytes accumulate, so a run of zero-length timed fragments could grow them without bound on hostile input. - Add push_owned so the PS path moves the PES payload into a passthrough AU with no copy (MPEG-2 video + all audio), removing a per-PES malloc+memcpy the refactor had introduced on the DVD path. - Back-patch the MKV duration from the block END (start + its own duration) so it covers the final frame instead of understating by one. - Add direct tests for the MKB record-framing walker; drop a stale drain_complete_aus doc comment left on process_au. |
||
|
|
26423187d3 |
audit: bound the VTI clip-table scan; fix stale aacs doc links
Round-2 findings from the 10-phase release audit: - parse_vti_clip_order bucketed hits by residue with an O(stride*hits) rescan and no hit cap, so a crafted HD-DVD VTI packed with millions of `.EVO` tokens (up to the 64 MiB UDF read cap) could burn seconds of CPU on a routine scan. Bucket in a single O(hits) pass and cap collected hits at MAX_VTI_HITS (a real table holds a few dozen). - Fix the stale `super::keys::…` intra-doc links left by the aacs module rename: the referenced fns live in `super::derive`. |
||
|
|
5090ddab6c |
disc: compose the HD-DVD feature from its VTI clip table
HD-DVD Standard Content splits the main feature across clips at the layer break (FEATURE_1/FEATURE_2, or feature/feature_Divide). The scanner enumerated one title per .evo, so main-title selection picked only part 1 (e.g. Shaun's 11 GB FEATURE_1, missing the 6.8 GB FEATURE_2). Parse the HVA*.VTI navigation file's clip table — a fixed-stride record list naming every clip in authored order, isolated by residue-mod-stride rather than the imprecise header pointer — and concatenate the feature clips (matched by the feature* naming convention) into one title whose extents run in authored order. Every other clip stays its own title. Falls back to one-title-per-clip when the VTI is absent or unparseable, so nothing regresses on a disc with no readable navigation. Validated on real discs: Shaun 17.8 GB / Anchorman 20.1 GB / Harry Potter 24.4 GB now enumerate as one 2-clip FEATURE title (largest = the movie). |
||
|
|
48bec4cc03 |
mux: HD-DVD VC-1 demux via extended stream id 0xFD
VC-1 HD-DVDs (e.g. Shaun of the Dead) carry video on MPEG-PS extended stream id 0xFD, with the real stream selector in stream_id_extension inside the PES extension. Parse that field so the video routes to a distinct track (pid 0xFD00|ext) instead of being dropped. Reframe VC-1 access units in AuAssembler with a dedicated Mode::Vc1: an AU is delimited by the next frame BDU (0x0D) once a frame has already been seen, so the sequence (0x0F) and entry-point (0x0E) headers that precede an I-frame stay attached to the frame they describe. The old single-start-code split stranded those headers on the prior AU, which the decoder reported as bits-overconsumption and hard decode failures. hddvd probe now tracks the video pid it detects and emits VC-1 on 0xFD. |
||
|
|
85347597cc |
disc: first-class FMTS + HD-DVD formats; CPI sample selection
Add DiscFormat::Fmts (AACS 2.1) and DiscFormat::HdDvd as first-class peers. Format derives from the AACS MKB generation (mkb_type().generation(): V10=BD, V20=UHD, V21=FMTS), reusing existing AACS code, and from the on-disc tree for HD-DVD/DVD. One detector (detect_disc_format) shared by the coarse DiscId probe and the full scan — no more 'default BluRay, defer to full scan'. FMTS is a BD-tree stream variant: parse_playlist resolves the clip stream via CLIP_STREAM_EXTS (.m2ts -> .fmts -> .ssif), so the .fmts main feature yields real extents (previously silently empty). HD-DVD is a tree-level peer with its own enumerator (disc/hddvd.rs): HVDVD_TS/*.evo -> MpegPs titles with real extents (playlist/stream parsing honestly stubbed). Sample selection for key resolution now uses the authoritative AACS CPI flag (aacs_unit_encrypted, byte-0 & 0xC0) not the ts_sync_destroyed heuristic — container-agnostic (M2TS/FMTS/EVO; TS-sync is meaningless on HD-DVD program streams) and stops the decode-server '0 encrypted units' rejection. Tests live with each format (bluray/hddvd/mod); generic UDF fixture builders extracted to a shared udf::fixture module. |