Skip to main content

libfreemkv/
lib.rs

1//! libfreemkv -- Open source optical drive library for 4K UHD / Blu-ray / DVD.
2//!
3//! Handles drive access, disc structure parsing, AACS decryption, and raw
4//! sector reading. 206 bundled drive profiles. No external files needed.
5//!
6//! # Quick Start
7//!
8//! ```no_run
9//! use libfreemkv::{Drive, Disc, ScanOptions, find_drive};
10//!
11//! let mut drive = find_drive().expect("no optical drive found");
12//! drive.wait_ready().unwrap();
13//! drive.init().unwrap();
14//! let disc = Disc::scan(&mut drive, &ScanOptions::default()).unwrap();
15//!
16//! for title in &disc.titles {
17//!     println!("{} -- {} streams", title.duration_display(), title.streams.len());
18//! }
19//!
20//! // Stream via PES pipeline
21//! let opts = libfreemkv::InputOptions::default();
22//! let mut input = libfreemkv::input("disc://", &opts).unwrap();
23//! let title = input.info().clone();
24//! let mut output = libfreemkv::output("mkv://Movie.mkv", &title).unwrap();
25//! while let Ok(Some(frame)) = input.read() {
26//!     output.write(&frame).unwrap();
27//! }
28//! output.finish().unwrap();
29//! ```
30//!
31//! # Architecture
32//!
33//! ```text
34//! Drive           -- open, identify, unlock, read sectors
35//!   ├── ScsiTransport    -- SG_IO (Linux), IOKit (macOS)
36//!   ├── DriveProfile     -- per-drive unlock parameters (206 bundled)
37//!   ├── DriveId          -- INQUIRY + GET_CONFIG identification
38//!   └── Platform
39//!       └── Mt1959       -- MediaTek unlock/read (Renesas planned)
40//!
41//! Disc                   -- scan titles, streams, AACS state
42//!   ├── UDF reader       -- Blu-ray UDF 2.50 with metadata partitions
43//!   ├── MPLS parser      -- playlists → titles + clips + STN streams
44//!   ├── CLPI parser      -- clip info → EP map → sector extents
45//!   ├── JAR parser       -- BD-J audio track labels
46//!   └── AACS             -- encryption: key resolution + content decrypt
47//!       ├── aacs         -- KEYDB, VUK, MKB, unit decrypt
48//!       └── handshake    -- SCSI auth, ECDH, bus key
49//! ```
50//!
51//! # AACS Encryption
52//!
53//! Disc scanning automatically detects and handles AACS encryption.
54//! If a KEYDB.cfg is available (via `ScanOptions` or standard paths),
55//! the library resolves keys and decrypts content transparently.
56//!
57//! Supports AACS 1.0 (Blu-ray) and AACS 2.0 (UHD, with fallback).
58//!
59//! # Error Codes
60//!
61//! All errors are structured with numeric codes. No user-facing English
62//! text -- applications format their own messages.
63//!
64//! | Range | Category |
65//! |-------|----------|
66//! | E1xxx | Device errors (not found, permission) |
67//! | E2xxx | Profile errors (unsupported drive) |
68//! | E3xxx | Unlock errors (failed, signature) |
69//! | E4xxx | SCSI errors (command failed, timeout) |
70//! | E5xxx | I/O errors |
71//! | E6xxx | Disc format errors |
72//! | E7xxx | AACS errors |
73
74pub mod aacs;
75pub(crate) mod clpi;
76pub mod css;
77pub mod decrypt;
78pub mod disc;
79pub mod drive;
80pub mod error;
81pub mod event;
82pub mod halt;
83pub(crate) mod identity;
84pub(crate) mod ifo;
85pub mod io;
86pub mod keydb;
87pub mod labels;
88pub(crate) mod mpls;
89pub mod mux;
90pub mod pes;
91pub(crate) mod platform;
92pub mod profile;
93pub mod progress;
94pub mod scsi;
95pub mod sector;
96pub(crate) mod speed;
97pub(crate) mod udf;
98pub mod verify;
99
100// Re-export verify types at the crate root for ergonomic imports.
101pub use verify::{SectorRange, SectorStatus, VerifyResult, verify_title};
102
103// ─── Drive lifecycle ────────────────────────────────────────────────────────
104//
105// `Drive::open(path)` → `wait_ready()` → `init()` → `Disc::scan()`. `Drive`
106// owns the SCSI session; `DriveCapture` etc. let advanced callers introspect
107// drive identity / profile data for sharing.
108pub use drive::capture::{
109    CapturedFeature, DriveCapture, capture_drive_data, mask_bytes, mask_string,
110};
111pub use drive::{Drive, DriveStatus, find_drive};
112
113// ─── Errors ─────────────────────────────────────────────────────────────────
114//
115// All fallible APIs return `Result<T, Error>`. `Error` is a typed enum with a
116// numeric `code()`; **no English text in the library** — applications map
117// codes to localized messages. See `error.rs` for the full taxonomy.
118pub use error::{Error, Result};
119
120// ─── 0.18 primitives ────────────────────────────────────────────────────────
121//
122// One-bit cooperative cancellation token, shared by every long-running loop
123// in libfreemkv (sweep, patch, mux). Replaces the ad-hoc `Arc<AtomicBool>`
124// flags scattered across 0.17 (`DiscStream::set_halt`, autorip's
125// `HALT_FLAGS` registry). Clone it cheaply; pass it by value into each
126// component; poll `is_cancelled()` inside the loop body.
127pub use halt::Halt;
128
129// Generic bounded producer/consumer primitive used by sweep, patch, and
130// mux to overlap reads with writes via a dedicated consumer thread.
131// `Pipeline::spawn(name, depth, sink)` spawns a named consumer; `pipe.send(item)`
132// pushes one item with back-pressure; `pipe.finish()` joins the
133// consumer and surfaces its `close()` output. Callers implement `Sink`
134// to define per-item behaviour and end-of-stream finalisation.
135//
136// `DEFAULT_PIPELINE_DEPTH` (=4) is for callers without specific needs;
137// most should use READ_PIPELINE_DEPTH or WRITE_PIPELINE_DEPTH instead.
138// Patch uses `WRITE_THROUGH_DEPTH` (=1). Returning `Flow::Stop` from
139// `apply` ends the consumer cleanly (still calls `close()`).
140pub use io::pipeline::{
141    DEFAULT_PIPELINE_DEPTH, Flow, Pipeline, READ_PIPELINE_DEPTH, Sink, WRITE_PIPELINE_DEPTH,
142    WRITE_THROUGH_DEPTH,
143};
144
145// ─── Drive events (low-level callbacks) ─────────────────────────────────────
146pub use event::{Event, EventKind};
147pub use identity::DriveId;
148pub use profile::DriveProfile;
149// Platform trait is pub(crate) — callers use Drive, not Platform directly.
150
151// ─── Decryption (AACS / CSS) ────────────────────────────────────────────────
152//
153// `Disc::scan()` resolves keys and stores them on `Disc`; in most flows you
154// don't touch `DecryptKeys` directly — `DiscStream::new(reader, title, keys, …)`
155// accepts whatever `Disc::decrypt_keys()` returned. `decrypt_sectors()` is
156// for callers that operate on raw sector buffers (e.g. ISO patching).
157pub use decrypt::{DecryptKeys, decrypt_sectors};
158
159// ─── Disc structure ─────────────────────────────────────────────────────────
160//
161// `Disc::scan()` produces a fully-populated `Disc` (titles, streams, AACS
162// state). `Disc::identify()` is the fast path — UDF only, no playlist parse,
163// for displaying disc name + format quickly while a full scan runs in the
164// background. The codec / channel / resolution enums are the canonical
165// structured representation; never compare against display strings.
166// Note: `disc::Stream` here is the codec enum (audio / video / sub kind)
167// — not the `pes::Stream` trait re-exported below as `PesStream`. Two
168// different concepts, the same short name; the trait gets the `Pes`
169// prefix at the crate root to keep both addressable.
170pub use disc::{
171    AacsState, AudioChannels, AudioStream, Clip, Codec, ColorSpace, ContentFormat, DamageSeverity,
172    Disc, DiscFormat, DiscId, DiscTitle, Extent, FrameRate, HdrFormat, KeySource, LabelPurpose,
173    LabelQualifier, PatchOptions, PatchOutcome, Resolution, SampleRate, ScanOptions, Stream,
174    SubtitleStream, SweepOptions, VideoStream, classify_damage,
175};
176
177// ─── Streams ────────────────────────────────────────────────────────────────
178//
179// All stream types implement `pes::Stream` — read PES frames from a source,
180// write PES frames to a sink. Pick the right type at construction:
181//
182// - `DiscStream` — physical drive or ISO (any `SectorSource`). Read-only.
183// - `MkvStream`  — Matroska container. Read on `open()`, write on `create()`.
184// - `M2tsStream` — Blu-ray Transport Stream. Read on `open()`, write on `create()`.
185// - `NetworkStream` — TCP. Read on `listen()`, write on `connect()`.
186// - `NullStream` — write-only black-hole sink. Useful for benchmarks.
187// - `StdioStream` — pipe to/from stdin/stdout. Read or write.
188//
189// Most consumers use the URL resolvers (`input()` / `output()`) which pick
190// the right type from a scheme:// URL. Direct construction is for callers
191// that need to wire custom readers (e.g. autorip's drive-session reuse).
192// The trait is re-exported as `PesStream` here to disambiguate from
193// `disc::Stream` (the codec-kind enum re-exported above), which would
194// otherwise collide at the crate root.
195pub use pes::PesFrame;
196pub use pes::Stream as PesStream;
197
198pub use mux::DiscStream;
199pub use mux::M2tsStream;
200pub use mux::MkvStream;
201pub use mux::NetworkStream;
202pub use mux::NullStream;
203pub use mux::StdioStream;
204pub use mux::{InputOptions, StreamUrl, input, output, parse_url};
205
206// ─── Lower-level surfaces ───────────────────────────────────────────────────
207//
208// `ScsiTransport` is the platform-abstraction trait Drive uses; expose for
209// out-of-tree platform backends. `SectorSource` / `SectorSink` are the
210// direction-typed read/write traits; `FileSectorSource` and `FileSectorSink`
211// are the ISO-on-disk implementations. [`DecryptingSectorSource`] is the
212// single decrypt-on-read decorator (AACS / CSS / none) — wrap any
213// `SectorSource` to get plaintext sectors out.
214pub use scsi::{DriveInfo, ScsiSense, ScsiTransport, drive_has_disc, list_drives};
215pub use sector::{
216    DecryptingSectorSource, FileSectorSink, FileSectorSource, SectorSink, SectorSource,
217};
218pub use speed::DriveSpeed;
219pub use udf::{UdfFs, read_filesystem};