mux: StreamSelection primitive + apply sites for per-title stream selection
The demux pipeline is declaration-driven off DiscTitle.streams (build_demux_state,
DiscStream::new, and the MKV writer all key off that list), so 'which streams to
keep' is already a pipeline capability with no public knob. This adds the knob:
- mux/select.rs: StreamSelection { audio, subtitle: PidFilter::All | Only(Vec<u16>) }
+ apply(&mut DiscTitle): keep Video always, keep Audio/Subtitle whose PID the
filter lists, prune the rest (and the parallel codec_privates in lockstep);
error SelectionPidUnknown on a listed PID absent from the title (fail loud, not
a silently-missing track). Pure; 6 unit tests. Re-exported at crate root.
- Error::SelectionPidUnknown (E6014).
- MuxOptions gains (+ derives Default now) applied in mux_stream's
Iso/Session arms before the highway/DiscStream builds demux state (and before
probe_and_remap's DVD AC-3 PID rewrite). InputOptions gains applied
in input()'s iso arm right after the title-index bounds check.
PIDs not languages -- language->PID is caller/engine policy. Default All/All is a
no-op (apply gated on !is_all()), so the no-selection path is byte-identical:
nothing below the title-finalization line changes (ts/ps/demux_thread/
pipelined_stream/mkv/disc untouched). All 2488 lib tests pass on 1.86.
Design: freemkv-private/audit/engine-split/STREAM-SELECTION-DESIGN.md (Fable).
This commit is contained in:
@@ -330,6 +330,12 @@ pub struct InputOptions {
|
||||
/// still-scrambled unit is re-tried via the application's key source.
|
||||
/// Application seam only; the library makes no network call.
|
||||
pub key_fetch: Option<crate::sector::KeyFetch>,
|
||||
/// Which audio/subtitle streams to keep. `input()` scans the source and
|
||||
/// picks the title internally, so the caller can't prune the `DiscTitle`
|
||||
/// itself — it passes the selection here and `input()` applies it right
|
||||
/// after the title-index bounds check. Default keeps every stream (video is
|
||||
/// always kept). See [`crate::StreamSelection`].
|
||||
pub selection: crate::StreamSelection,
|
||||
}
|
||||
|
||||
// `KeyFetchFactory` holds a trait object that is not `Debug`; hand-roll the
|
||||
@@ -342,6 +348,7 @@ impl std::fmt::Debug for InputOptions {
|
||||
.field("title_index", &self.title_index)
|
||||
.field("raw", &self.raw)
|
||||
.field("key_fetch", &self.key_fetch.is_some())
|
||||
.field("selection", &self.selection)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -403,6 +410,14 @@ pub fn input(url: &str, opts: &InputOptions) -> io::Result<Box<dyn crate::pes::S
|
||||
}
|
||||
.into());
|
||||
}
|
||||
// Prune to the selected audio/subtitle streams now, on the scanned
|
||||
// (pre-`probe_and_remap`) title, so everything downstream — the
|
||||
// TrueHD channel-correction probe, the final title clone, and
|
||||
// `build_iso_pipeline`'s demux/track construction — sees the pruned
|
||||
// list. Video is always kept; a no-op for the default All/All.
|
||||
opts.selection
|
||||
.apply(&mut disc.titles[idx])
|
||||
.map_err(|e| -> io::Error { e.into() })?;
|
||||
// Per-title key resolution. DVD CSS is resolved at exactly ONE site —
|
||||
// `build_iso_pipeline`'s per-title crack (below), which decrypts a
|
||||
// crackable title, passes a genuinely-clear one through, and
|
||||
|
||||
Reference in New Issue
Block a user