A multi-clip playlist's source PTS restarts at each clip join. The m2ts sink passed it
through, so one TS stepped back in time at every join, and the DTS deriver's order guard
then wrote every later reordered AU as PTS only (a 34-clip UHD title kept DTS on its first
clip alone). The sink now maps each frame through TimelineContinuity, driven by the base
video view, and fails a seam plan that dropped most of the title, as the other sinks do.
The image Read policy refused any extent tail shorter than one 3-sector AACS unit. Blu-ray
stream files always end on the unit grid, but an HD DVD .EVO need not, and a clear HD DVD
now rips through the keyed pipeline (unit_align 3), so the main title of a real disc failed
E9030 at the end of its first off-grid extent. The tail is now one short read, as the live
policy already did; the decrypt stage passes it when clear and refuses it (E7013) when it is
the head of an encrypted unit.
KeyRing::acquire probes the pieces in scope once, each unit judged by its format's detector
(BD/UHD: the CPI flag; HD DVD: flagged or not whole MPEG-PS). Content read in the clear asks
no source, whatever key files the disc declares; for FMTS the forensic segments must read clear
too. Encrypted content goes to the format's keying (BD/UHD per piece, HD DVD best effort) and is
refused E7022 / E7032 when no source has the key. Replaces 9e9ff21's HD DVD-only check.
DefaultDuration came from the IFO's video standard (29.97 for NTSC). A soft-telecined film
title carries 23.976 coded frames a second, so the header disagreed with the frame count
(11440 frames over 476 s on the dvd fixture's title 2). finish() now rewrites the value when
the kept frames' PTS span measures another standard rate.
ed02bc1 named every disc layout's output title by its meta title, else its volume id. A
folder's volume id is its directory name, so a DVD title ripped from an image and from its
extracted folder got different segment titles and the two MKVs stopped being byte-identical.
A drive still names by meta title, else volume id.
M2TS output had no PCR and zero TP_extra_header arrival stamps, and an AAC
access unit from a multi-block ADTS frame was re-framed as one block. Write
adaptation-only PCR packets on PID 0x1001 (the PMT's PCR_PID) at most 100 ms
apart, stamp every packet's ATS from the same 27 MHz clock (at most 1 s before
its DTS, 128 Mb/s per packet), restart with the discontinuity_indicator on a
timeline jump past 10 s, and declare the ADTS block count from the duration.
Parity goldens re-blessed: only the m2ts output lines change (the added PCR
packets and non-zero ATS).
M2TS output had no PAT/PMT, so FFmpeg could not name BD LPCM (or AAC):
write PAT + HDMV-registered PMT every 100 ms of PTS progress, re-frame AAC
as ADTS from its ASC, and give MPEG audio the 0xC0 stream_id. Loose m2ts
input types FFmpeg's stream_type 0x06 audio from its stream_id and ES sync.
mp4:// carries AAC/MP2/MP3 as mp4a + esds.
Parity goldens re-blessed: only m2ts output bytes change (the added
PAT/PMT packets).
A byte stream is ruled out from five bytes, so network:// and stdio:// parse
the FMKV header as soon as it arrives. A stale CPI flag is cleared only on
wholly clear TS. A flagged partial unit is blanked only when it ends the
source. A file stage finds a VOB's first aligned pack past damaged sectors,
and one damaged unit at a clip's head no longer hides its source packets.
Raw mpg falls back to the raw head on E7022 too; an uncrackable PS verdict is
kept so a retry does not scan again; SectorBytes reports a short source as
UnexpectedEof; disc_root_of canonicalizes, follows SSIF and drops HD DVD.
Disc::mkv_staging_ranges(reader, titles) lists what an image staged for an
MKV rip needs: the volume structures before the partition, the Metadata File,
every directory and File Entry (stream files' too), the data of every file
outside /BDMV/STREAM, and the chosen titles' extents. AACS BD Pre-recorded
Book 0.953 section 3.7: "the BEF shall be set to 0b for the sectors that do not
correspond to Clip AV stream files under \BDMV\STREAM directory", and every
title extent is in the drive's bus map, so the staged image holds no
bus-encrypted byte even when a stream file is unmapped.
Error::ImageScoped (E6022) names an image staged that way when it is offered
as a whole-disc source.
Review fixes for the unlocatable bus-encrypted stream file:
- A File Entry whose read fails gets at most 2 recovery+FUA re-reads (past the
buffered reader's cache), from a budget of 4 per scan, each after the
engine's 5 s FAIL_PAUSE, which a Stop ends as Halted. A count, not a
timeout: HARDWARE ERROR / ILLEGAL REQUEST sense (the BU40N fast-fail wedge)
gets no re-read, nor does a dead transport, a gone source or an entry that
read but does not parse. Only then is the file recorded as unmapped.
- E6021 names each file with its read cause: `path (cause)`.
- DecryptingSectorSource, whole_disc::UnitAligned, PrefetchedSectorSource
(snapshotted at construction) and FileReadAhead forward
unmapped_stream_files(). A source-scan test fails any production
SectorSource impl that neither forwards nor is a listed leaf.
On a host-key (cert-route) bus-encrypted disc the bus map is built from every
/BDMV/STREAM file. A file whose File Entry extents could not be read was
dropped with a warning, so its sectors passed through still bus-encrypted
into iso://, sweep and dir:// output.
AACS BD Pre-recorded Book 0.953 section 3.7 sets the BEF on the Clip AV
stream files under \BDMV\STREAM, and its Note requires the PC host to decrypt
a bus-encrypted Clip AV stream file. A file that cannot be located cannot be.
The scan now records such files (path, ICB, cause) on the drive's bus map
and carries on: titles that play the clip are dropped as before, and every
locatable file is still de-bussed, so MKV rips and disc info are unaffected.
ensure_image_debussable() refuses a whole-disc image with the new
Error::BusStreamUnmapped (E6021) naming the file(s); SectorSource gains
unmapped_stream_files(), forwarded by every wrapper. extract_tree never reads
or writes such a file and counts it lost in bytes_unreadable.
stream_content_ranges fails closed with E6021. A Stop during the walk still
returns Halted and is not logged as a disc fault; an embedded file holds no
6144-byte Aligned Unit (section 3.10.1) and is skipped. Tests quote the spec.
probe_mkv reads the EBML header, Segment Info and Tracks and stops before
the first cluster, returning the muxing/writing app, declared duration,
title, timestamp scale and a minimal per-track view (number, kind, codec
id, language). parse_mkv_header now captures MuxingApp and WritingApp and
keeps "no Duration" distinct from a zero one; MkvStream::open is otherwise
unchanged.
parse_freemkv_version pulls (major, minor, patch) out of the stamp our
writer puts in MuxingApp ("freemkv 1.7.7 (gc8e67f1)"). Older builds wrote
a bare "freemkv" and get None.
On Duration: finish() only back-patches it when the title declared no
runtime (the placeholder path, e.g. HD-DVD). A title with a declared
duration keeps that value in the header whatever was actually muxed, so
the header alone can't tell a short mux from a full one. For that,
probe_mkv_with_cues follows the SeekHead to the Cues (seeking past the
clusters, never reading one) and reports the last cue time as a lower
bound on the muxed runtime. declared_duration_is_kept_not_measured pins
the current writer behaviour.
Titles with no /BDMV/STREAM (or /HVDVD_TS) fail as E6003 naming the folder, not
E7013's key-refresh advice. A stop landing in the last key lookup ends before any
output. Docs: CSS is descrambled; the mid-pass E7032 is the one late refusal.
Moved from freemkv-engine and freemkv so the GUI, CLI and image paths share one
set of rules. A key must open two probed units before it keys an unplayed file,
and alternates are the held base keys only (forensic FMTS keys never).
A decrypted whole-disc image would keep encrypted pieces: a stream file no
title plays is encrypted and no held key opens it. The fix is an MKV rip or a
raw copy, not a key, so it gets its own code instead of E7013.
Standard order: UDF, then Unit_Key_RO.inf, the content cert and the MKB as plain
reads, then the handshake only on an AACS disc. An unreadable key file on a live
disc fails the scan with the new E7031 unless ScanOptions::raw_copy is set; a dead
bus or Stop during the AACS/CSS handshake aborts it; no drive MKB read for certs.
wait_ready returns the 3Ah sense after 10 empty polls with no 04/01 seen; Drop
sends ALLOW inline only if the spawn fails; no-sysfs list drops EACCES/EPERM
nodes; start_unit moved off spin_cycle's doc; presence docs say "derived".
disc_presence(path) -> Present/Absent/Settling (only 3Ah absent; 04/01 Settling);
drive_has_disc counts Settling as a disc. wait_ready: 5 s budget from the first
failure's completion, START UNIT on 04/02, fail fast on 30h. Drop/list fixes.
A layout with no N.M variant (e.g. 5.2) used to keep the container value,
understating MKV Channels; use from_count instead. Note the new layout strings.
CrackOutcome gains Unreadable and is non_exhaustive; decrypt_keys_for_title
takes a halt token and returns Result; all-rejected read-time keys are
AacsKeyRejected; Disc::inputs_with_samples fills validation samples.
Bumps Cargo.toml + Cargo.lock to 1.7.6. Also fixes a CI-shape defect the
previous release left behind: dev's CHANGELOG still said
`## [1.7.5] — UNRELEASED` after 1.7.5 shipped (release-orchestrate dated
main but did not push a fresh UNRELEASED heading back to dev). Corrects
that here so the changelog job accepts 1.7.6 on the way to qa:
## [1.7.6] — UNRELEASED
- Version aligned; unified release driven by freemkv 1.7.6.
## [1.7.5] — 2026-09-23
- Version aligned (or actual content, per the shipped main entry).
The driver for 1.7.6 is the freemkv Linux desktop shell (GTK4 +
libadwaita) + rip-finished desktop notification (issue #56).
Version aligned to 1.7.5. No functional change to this crate — the release
is driven by freemkv-unlock mirroring the freemkv-firmware 0.9.0 ABI, where
the drive's separate Ake (0x06) and Bus (0x07) levers are retired into a
single Encryption (0x06) lever.