Skip to main content

oxideav_dvd/
lib.rs

1//! Read-only **DVD-Video** disc reader — ISO 9660 + UDF 1.02 mount +
2//! `VIDEO_TS/` directory walk.
3//!
4//! Phase 1 (this release) handles the physical + filesystem + disc-
5//! identification layers — enough to point a player at a DVD-Video
6//! disc image or block device and enumerate the title-set files.
7//! **No IFO / VOB / PGC / VM / CSS parsing yet.**
8//!
9//! ## Scope
10//!
11//! - ISO 9660 PVD + path-table + directory walk (the ECMA-268
12//!   bridge layer).
13//! - UDF 1.02 mount: AVDP, Volume Descriptor Sequence, File Set
14//!   Descriptor, root File Identifier Descriptor walk, File Entry
15//!   parsing with short_ad / long_ad / ext_ad allocation descriptors.
16//! - `VIDEO_TS/` file enumeration: `VIDEO_TS.IFO` + per-VTS
17//!   `VTS_xx_0.IFO` / `VTS_xx_0.VOB` (menu) / `VTS_xx_1..9.VOB`
18//!   (title content) / `VTS_xx_0.BUP`.
19//! - `dvd://` URI handler (default-on `registry` feature) that
20//!   surfaces a `DvdDiscSource` to `oxideav_core::SourceRegistry`.
21//!
22//! Out of scope (deferred to Phase 2 / Phase 3):
23//! - IFO body parsing (VMGI, VTSI, PGCI, cell-address tables).
24//! - VOB demuxing (MPEG-2 Program Stream + nav-pack overlays).
25//! - VM execution (HDMV navigation opcodes, SPRMs / GPRMs).
26//! - CSS authentication + descrambling (lives in a future
27//!   `oxideav-css` sibling crate).
28//!
29//! ## Sub-Picture Unit decoder (`spu` module)
30//!
31//! The `spu` module parses one DVD subpicture (subtitle / menu
32//! overlay) blob assembled from concatenated PES packet payloads
33//! on substream `0x20..=0x3F`: SPUH + the chained
34//! `SP_DCSQT` command stream + the two PXD fields' 2-bit
35//! run-length-encoded pixel data. [`spu::SubPictureUnit::composite`]
36//! optionally resolves those palette indices through the PGC's
37//! 16-entry [`ifo::PaletteEntry`] colour-LUT (BT.601 studio-swing
38//! YCbCr → RGB plus the SET_CONTR alpha) into a finished RGBA
39//! [`spu::SpuBitmap`] overlay; blending it onto the decoded video
40//! frame stays with the player.
41//!
42//! ## Phase 3b — `mkv-output` feature (default off)
43//!
44//! Enable `mkv-output` to pull in [`pipeline::convert_dvd_to_mkv`],
45//! which walks a title's VOBs and writes a Matroska file with the
46//! PGC's chapter timeline. The feature pulls in `oxideav-mkv` as a
47//! runtime dependency; default builds (and default-feature CI) stay
48//! free of it so the crate keeps compiling against any published
49//! `oxideav-mkv` version.
50//!
51//! ## Clean-room references
52//!
53//! - `docs/container/dvd/physical/ECMA-267_3rd_edition_april_2001.pdf`
54//! - `docs/container/dvd/physical/ECMA-268_3rd_edition_april_2001.pdf`
55//! - `docs/container/dvd/physical/OSTA_UDF_1.02.pdf`
56//! - `docs/container/bluray/ECMA-167_3rd_edition_june_1997.pdf` (cross-ref)
57//! - `docs/container/dvd/application/mpucoder-ifo.html` (for the
58//!   VIDEO_TS directory layout, file-naming convention, and BUP
59//!   backup semantics only — IFO bodies are out of Phase-1 scope)
60//!
61//! ## Quick start
62//!
63//! ```no_run
64//! use oxideav_dvd::DvdDisc;
65//!
66//! let disc = DvdDisc::open("path/to/disc.iso").unwrap();
67//! println!("volume_id = {}", disc.volume_id);
68//! println!("title_set_count = {}", disc.title_set_count);
69//! for f in &disc.video_ts_files {
70//!     println!("  {:?}  lba={}  size={}", f.kind, f.lba, f.size);
71//! }
72//! ```
73//!
74//! ## Standalone build
75//!
76//! `oxideav-core` is gated behind the default-on `registry` feature.
77//! Drop the framework dependency entirely with:
78//!
79//! ```toml
80//! oxideav-dvd = { version = "0.0", default-features = false }
81//! ```
82
83#![deny(unsafe_code)]
84#![warn(missing_debug_implementations)]
85
86pub mod ac3;
87pub mod disc;
88pub mod error;
89pub mod ifo;
90pub mod iso9660;
91pub mod lpcm;
92pub mod nav;
93pub mod source;
94pub mod spu;
95pub mod udf;
96pub mod uops;
97pub mod vm;
98pub mod vob;
99
100#[cfg(feature = "mkv-output")]
101pub mod mkv_writer;
102#[cfg(feature = "mkv-output")]
103pub mod pipeline;
104
105pub use ac3::{Ac3AudioCodingMode, Ac3BitstreamMode, Ac3Header, Ac3SampleRate, AC3_SYNC_WORD};
106pub use disc::{DvdDisc, DvdFile, DvdFileKind};
107pub use error::{Error, Result};
108pub use ifo::{
109    menu_existence, AudioApplicationMode, AudioAttributes, AudioCodingMode, AudioLanguageType,
110    AudioQuantizationDrc, CellAddrEntry, CellPlaybackInfo, CellPositionInfo, DvdChapter, DvdTitle,
111    DvdTitleEntry, FrameRate, McExtensionEntry, MenuAttributes, MenuType, NavCommand, PaletteEntry,
112    Pgc, PgcCommandTable, PgcTime, Pgci, PgciLu, PgciLuSrp, PgciSrp, PgciUt, PgciUtSrp, PtlMait,
113    Ptt, PttTitle, SubpictureAttributes, SubpictureCodingMode, SubpictureLanguageType,
114    TitleAttributes, TmapEntry, TtSrpt, VideoAspectRatio, VideoAttributes, VideoCodingMode,
115    VideoResolution, VideoStandard, VmgIfo, VmgPtlMait, VmgVtsAtrt, VmgVtsAtrtEntry, VobuAdmap,
116    VtsCAdt, VtsIfo, VtsPttSrpt, VtsTmap, VtsTmapti, VtsiMat, DVD_SECTOR, VMG_MAGIC, VTS_MAGIC,
117};
118pub use iso9660::{
119    DirectoryRecord, Iso9660Entry, Iso9660Volume, PathTableEntry, PrimaryVolumeDescriptor,
120    VolumeDescriptorType,
121};
122pub use lpcm::{
123    peel_lpcm_payload, LpcmHeader, LpcmQuantisation, LpcmSampleFrequency,
124    DVD_LPCM_MAX_BITRATE_KBPS, LPCM_HEADER_LEN,
125};
126pub use nav::{
127    CallSSTarget, CmpOp, JumpSSTarget, LinkSubset, NavInstruction, Operand, Register, SetOp,
128};
129pub use source::{parse_dvd_uri, DvdDiscSource, DvdUri};
130pub use spu::{
131    decode_rle_field, render_field, spdcsq_stm_to_ms, ycbcr_to_rgb, PixelRun, SpDcSq, SpuBitmap,
132    SpuCommand, SpuHeader, SubPictureUnit,
133};
134pub use udf::{
135    AdType, AnchorVolumeDescriptorPointer, DescriptorTag, ExtAd, Extent, FileEntry,
136    FileIdentifierDescriptor, FileSetDescriptor, IcbTag, LbAddr, LogicalVolumeDescriptor, LongAd,
137    PartitionDescriptor, ShortAd, TagId, UdfFile, UdfVolume,
138};
139pub use uops::{
140    title_type_uop_mask, UopIter, UopLevel, UopMask, UserOp, UOP_ANGLE_CHANGE,
141    UOP_AUDIO_STREAM_CHANGE, UOP_BACKWARD_SCAN, UOP_BIT_COUNT, UOP_BUTTON_SELECT_OR_ACTIVATE,
142    UOP_DEFINED_BITS, UOP_FORWARD_SCAN, UOP_GO_UP, UOP_KARAOKE_AUDIO_MIX_CHANGE,
143    UOP_MENU_CALL_ANGLE, UOP_MENU_CALL_AUDIO, UOP_MENU_CALL_PTT, UOP_MENU_CALL_ROOT,
144    UOP_MENU_CALL_SUBPICTURE, UOP_MENU_CALL_TITLE, UOP_NEXT_PG_SEARCH, UOP_PAUSE_ON,
145    UOP_PTT_PLAY_OR_SEARCH, UOP_RESUME, UOP_STILL_OFF, UOP_STOP, UOP_SUBPICTURE_STREAM_CHANGE,
146    UOP_TIME_OR_PTT_SEARCH, UOP_TIME_PLAY_OR_SEARCH, UOP_TITLE_PLAY, UOP_TOP_PG_OR_PREV_PG_SEARCH,
147    UOP_VIDEO_PRESENTATION_MODE_CHANGE,
148};
149pub use vm::{
150    AspectRatio, AudioCapabilities, AudioLanguageExt, AudioMixMode, AudioStreamSelector,
151    DisplayMode, LanguageCode, LinkAction, ParentalLevel, RegisterFile, ResumePoint,
152    SubpictureLanguageExt, SubpictureStreamView, VideoPreference, Vm, VmAction, GPRM_COUNT,
153    MAX_RSM_DEPTH, SPRM_AMXMD, SPRM_ANGLE, SPRM_AUDIO_CAPS, SPRM_AUDIO_STREAM, SPRM_CC_PLT,
154    SPRM_COUNT, SPRM_HL_BTNN, SPRM_MENU_LANG, SPRM_NV_PGCN, SPRM_NV_TIMER, SPRM_PARENTAL_LEVEL,
155    SPRM_PGCN, SPRM_PREF_AUDIO_LANG, SPRM_PREF_AUDIO_LANG_EXT, SPRM_PREF_SUBP_LANG,
156    SPRM_PREF_SUBP_LANG_EXT, SPRM_PTT, SPRM_REGION_MASK, SPRM_SUBPICTURE_STREAM, SPRM_TITLE,
157    SPRM_VIDEO_PREF, SPRM_VTS_TITLE,
158};
159pub use vob::{
160    demux_vobs, demux_vobs_path, looks_like_nav_pack, ButtonInfo, ButtonMode, CellId, DsiGi,
161    DsiPacket, DvdSubstream, ElementaryStream, HighlightInfo, HighlightStatus, NavPack, NsmlAgli,
162    NsmlAngleCell, PackHeader, PciPacket, PesPacket, SlColi, SlColiCell, SmlAgli, SmlAngleCell,
163    SmlAudioGap, SmlPbi, Synci, VobDemuxer, VobId, VobStreams, VobuSri,
164};
165
166#[cfg(feature = "registry")]
167pub use source::register;
168
169#[cfg(feature = "mkv-output")]
170pub use mkv_writer::{pgc_time_to_ns, write_title_to_mkv};
171#[cfg(feature = "mkv-output")]
172pub use pipeline::{convert_dvd_to_mkv, list_titles};
173
174// Canonical sibling entry point. Registers the `dvd://` source driver
175// under `oxideav_core::RuntimeContext::sources`.
176#[cfg(feature = "registry")]
177oxideav_core::register!("dvd", source::register);