v0.20.0: delete FrameSource/FrameSink, keep single Stream trait
The 0.18 trait split into FrameSource (read-only) and FrameSink
(write-only) was an over-engineered API. Consumers don't think
"frame source backed by MKV" — they think "open MKV for reading".
The split paid a real API-complexity cost (two trait names, two
re-exports, dual impls per bidirectional type, deprecation bridge)
for one marginal property: compile-time direction-safety at the
trait-object boundary. The runtime error path on a wrong-direction
call (StreamReadOnly / StreamWriteOnly) is unambiguous and rare in
practice.
Deletions:
- pes::Stream is no longer #[deprecated]
- pes::FrameSource trait + its blanket-from-Stream bridge
- pes::FrameSink trait + the trampoline impls on every concrete type
- The compile-time-direction-safety test scaffolding
- Crate-root FrameSource / FrameSink re-exports
Additions:
- Stream is now Send-bounded (Stream: Send supertrait). Every
concrete impl was already Send-compliant — Box<dyn Read + Send>
and Box<dyn Write + Send> were already in place on the trait
objects MkvStream / M2tsStream / etc hold internally. Promoting
Send into the trait makes Box<dyn Stream> Send too, which lets
autorip drop its SendStream unsafe newtype.
The public API is now: one Stream trait, one concrete type per
format, two constructors (open/create or input/output). Bidirectional
types route through internal Mode { Read | Write } discriminants.
Net: -347 lines libfreemkv, -38 lines autorip, -5 lines freemkv.
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "libfreemkv"
|
name = "libfreemkv"
|
||||||
version = "0.19.1"
|
version = "0.20.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
rust-version = "1.86"
|
rust-version = "1.86"
|
||||||
license = "AGPL-3.0-only"
|
license = "AGPL-3.0-only"
|
||||||
|
|||||||
+7
-10
@@ -165,8 +165,8 @@ pub use decrypt::{DecryptKeys, decrypt_sectors};
|
|||||||
// structured representation; never compare against display strings.
|
// structured representation; never compare against display strings.
|
||||||
// Note: `disc::Stream` here is the codec enum (audio / video / sub kind)
|
// Note: `disc::Stream` here is the codec enum (audio / video / sub kind)
|
||||||
// — not the `pes::Stream` trait re-exported below as `PesStream`. Two
|
// — not the `pes::Stream` trait re-exported below as `PesStream`. Two
|
||||||
// different concepts, the same short name; both stay because both are
|
// different concepts, the same short name; the trait gets the `Pes`
|
||||||
// load-bearing in their respective domains.
|
// prefix at the crate root to keep both addressable.
|
||||||
pub use disc::{
|
pub use disc::{
|
||||||
AacsState, AudioChannels, AudioStream, Clip, Codec, ColorSpace, ContentFormat, DamageSeverity,
|
AacsState, AudioChannels, AudioStream, Clip, Codec, ColorSpace, ContentFormat, DamageSeverity,
|
||||||
Disc, DiscFormat, DiscId, DiscTitle, Extent, FrameRate, HdrFormat, KeySource, LabelPurpose,
|
Disc, DiscFormat, DiscId, DiscTitle, Extent, FrameRate, HdrFormat, KeySource, LabelPurpose,
|
||||||
@@ -179,7 +179,7 @@ pub use disc::{
|
|||||||
// All stream types implement `pes::Stream` — read PES frames from a source,
|
// All stream types implement `pes::Stream` — read PES frames from a source,
|
||||||
// write PES frames to a sink. Pick the right type at construction:
|
// write PES frames to a sink. Pick the right type at construction:
|
||||||
//
|
//
|
||||||
// - `DiscStream` — physical drive or ISO (any `SectorReader`). Always read.
|
// - `DiscStream` — physical drive or ISO (any `SectorReader`). Read-only.
|
||||||
// - `MkvStream` — Matroska container. Read on `open()`, write on `create()`.
|
// - `MkvStream` — Matroska container. Read on `open()`, write on `create()`.
|
||||||
// - `M2tsStream` — Blu-ray Transport Stream. Read on `open()`, write on `create()`.
|
// - `M2tsStream` — Blu-ray Transport Stream. Read on `open()`, write on `create()`.
|
||||||
// - `NetworkStream` — TCP. Read on `listen()`, write on `connect()`.
|
// - `NetworkStream` — TCP. Read on `listen()`, write on `connect()`.
|
||||||
@@ -189,14 +189,11 @@ pub use disc::{
|
|||||||
// Most consumers use the URL resolvers (`input()` / `output()`) which pick
|
// Most consumers use the URL resolvers (`input()` / `output()`) which pick
|
||||||
// the right type from a scheme:// URL. Direct construction is for callers
|
// the right type from a scheme:// URL. Direct construction is for callers
|
||||||
// that need to wire custom readers (e.g. autorip's drive-session reuse).
|
// that need to wire custom readers (e.g. autorip's drive-session reuse).
|
||||||
// 0.18 trait split: `FrameSource` (read-only) and `FrameSink` (write-only)
|
// The trait is re-exported as `PesStream` here to disambiguate from
|
||||||
// supersede the unified `pes::Stream`. The old `Stream` re-export below
|
// `disc::Stream` (the codec-kind enum re-exported above), which would
|
||||||
// stays available for the deprecation window — re-exported as
|
// otherwise collide at the crate root.
|
||||||
// `PesStream` to disambiguate from `disc::Stream` (the codec-kind enum
|
pub use pes::PesFrame;
|
||||||
// re-exported above), which would otherwise collide at the crate root.
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub use pes::Stream as PesStream;
|
pub use pes::Stream as PesStream;
|
||||||
pub use pes::{FrameSink, FrameSource, PesFrame};
|
|
||||||
|
|
||||||
pub use mux::DiscStream;
|
pub use mux::DiscStream;
|
||||||
pub use mux::M2tsStream;
|
pub use mux::M2tsStream;
|
||||||
|
|||||||
+14
-22
@@ -403,7 +403,6 @@ impl DiscStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(deprecated)] // 0.18 trait split: migrate to FrameSource/FrameSink in follow-up commit.
|
|
||||||
impl crate::pes::Stream for DiscStream {
|
impl crate::pes::Stream for DiscStream {
|
||||||
fn read(&mut self) -> io::Result<Option<crate::pes::PesFrame>> {
|
fn read(&mut self) -> io::Result<Option<crate::pes::PesFrame>> {
|
||||||
if let Some(frame) = self.pending_frames.pop_front() {
|
if let Some(frame) = self.pending_frames.pop_front() {
|
||||||
@@ -584,23 +583,18 @@ impl crate::pes::Stream for DiscStream {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
//! `DiscStream` is the only meaningful `FrameSource` impl in tree (every
|
//! `DiscStream` is the only read-only `Stream` impl in tree (every
|
||||||
//! other concrete `pes::Stream` impl in `mux/*` is a sink). The 0.18
|
//! other concrete impl in `mux/*` is bidirectional or write-only).
|
||||||
//! round-1 blanket `impl<T: pes::Stream + Send> pes::FrameSource for T`
|
//! These tests lock down a static `Send` assertion plus a
|
||||||
//! covers `DiscStream` for free as long as it is `Send`. These tests
|
//! `Box<dyn Stream>` round trip exercising every method through the
|
||||||
//! lock that down: a static `Send` assertion plus a `Box<dyn FrameSource>`
|
//! trait object, so future Send-breaking edits to `DiscStream`'s
|
||||||
//! round trip exercising every `FrameSource` method through the trait
|
//! interior types fail at compile time.
|
||||||
//! object, so future Send-breaking edits to `DiscStream`'s interior
|
|
||||||
//! types fail at compile time and the trait-bridge dispatch is verified
|
|
||||||
//! at runtime.
|
|
||||||
#![allow(deprecated)] // exercising the 0.18 deprecation-window blanket bridge.
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::disc::{ContentFormat, DiscTitle};
|
use crate::disc::{ContentFormat, DiscTitle};
|
||||||
use crate::pes::FrameSource;
|
use crate::pes::Stream;
|
||||||
|
|
||||||
/// Static-assert `DiscStream: Send`. The blanket
|
/// Static-assert `DiscStream: Send`. The `Stream` trait has `Send` as a
|
||||||
/// `impl<T: pes::Stream + Send> pes::FrameSource for T` only fires for
|
/// supertrait — if a future field on `DiscStream` is non-`Send` (e.g.
|
||||||
/// `Send` types — if a future field on `DiscStream` is non-`Send` (e.g.
|
|
||||||
/// a `Box<dyn Read>` instead of `Box<dyn SectorReader>`), this fails
|
/// a `Box<dyn Read>` instead of `Box<dyn SectorReader>`), this fails
|
||||||
/// at compile time, before the runtime trait-object test below.
|
/// at compile time, before the runtime trait-object test below.
|
||||||
fn _assert_disc_stream_is_send() {
|
fn _assert_disc_stream_is_send() {
|
||||||
@@ -644,13 +638,11 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Smallest credible witness that `DiscStream` flows through the
|
/// Smallest credible witness that `DiscStream` flows through `dyn Stream`:
|
||||||
/// `FrameSource` blanket impl: build a `Box<dyn FrameSource>`, drive
|
/// build a `Box<dyn Stream>`, drive `read()` to EOF, exercise `info()` /
|
||||||
/// `read()` to EOF, exercise `info()` / `headers_ready()` /
|
/// `headers_ready()` / `codec_private()` through the trait object.
|
||||||
/// `codec_private()` through the trait object. The trait-bridge
|
|
||||||
/// correctness is what's being verified — not demuxer behaviour.
|
|
||||||
#[test]
|
#[test]
|
||||||
fn frame_source_via_dyn_object() {
|
fn stream_via_dyn_object() {
|
||||||
let reader = ZeroReader { capacity: 8 };
|
let reader = ZeroReader { capacity: 8 };
|
||||||
let title = synthetic_title(8);
|
let title = synthetic_title(8);
|
||||||
let stream = DiscStream::new(
|
let stream = DiscStream::new(
|
||||||
@@ -661,7 +653,7 @@ mod tests {
|
|||||||
ContentFormat::BdTs,
|
ContentFormat::BdTs,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut src: Box<dyn FrameSource> = Box::new(stream);
|
let mut src: Box<dyn Stream> = Box::new(stream);
|
||||||
|
|
||||||
// Empty-title fixture has no streams configured, so headers are
|
// Empty-title fixture has no streams configured, so headers are
|
||||||
// trivially ready and codec_private() yields nothing on track 0.
|
// trivially ready and codec_private() yields nothing on track 0.
|
||||||
|
|||||||
@@ -173,7 +173,6 @@ impl M2tsStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(deprecated)] // 0.18 trait split: migrate to FrameSource/FrameSink in follow-up commit.
|
|
||||||
impl crate::pes::Stream for M2tsStream {
|
impl crate::pes::Stream for M2tsStream {
|
||||||
fn read(&mut self) -> io::Result<Option<crate::pes::PesFrame>> {
|
fn read(&mut self) -> io::Result<Option<crate::pes::PesFrame>> {
|
||||||
if let Some(frame) = self.pending_frames.pop_front() {
|
if let Some(frame) = self.pending_frames.pop_front() {
|
||||||
@@ -284,27 +283,3 @@ impl crate::pes::Stream for M2tsStream {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// FrameSink sibling to the deprecated Stream impl; both coexist during the
|
|
||||||
/// 0.18 deprecation window. Caller may pick either at the trait-object
|
|
||||||
/// boundary — `Box<dyn Stream>` (deprecated) or `Box<dyn FrameSink>` (new).
|
|
||||||
/// Use `M2tsStream::create(writer, title)` to construct the write half;
|
|
||||||
/// calling `FrameSink::write` on an `M2tsStream::open(reader)` instance
|
|
||||||
/// returns `StreamReadOnly`.
|
|
||||||
#[allow(deprecated)] // delegating to deprecated Stream during the 0.18 deprecation window so callers don't see the deprecation twice.
|
|
||||||
impl crate::pes::FrameSink for M2tsStream {
|
|
||||||
fn write(&mut self, frame: &crate::pes::PesFrame) -> io::Result<()> {
|
|
||||||
<Self as crate::pes::Stream>::write(self, frame)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn finish(self: Box<Self>) -> io::Result<()> {
|
|
||||||
// Why: Stream::finish takes &mut self, FrameSink::finish takes Box<Self>.
|
|
||||||
// Re-borrow inside the box, call Stream::finish, drop the box.
|
|
||||||
let mut s: Self = *self;
|
|
||||||
<Self as crate::pes::Stream>::finish(&mut s)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn info(&self) -> &crate::disc::DiscTitle {
|
|
||||||
<Self as crate::pes::Stream>::info(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -92,7 +92,6 @@ impl MkvStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(deprecated)] // 0.18 trait split: migrate to FrameSource/FrameSink in follow-up commit.
|
|
||||||
impl crate::pes::Stream for MkvStream {
|
impl crate::pes::Stream for MkvStream {
|
||||||
fn read(&mut self) -> io::Result<Option<crate::pes::PesFrame>> {
|
fn read(&mut self) -> io::Result<Option<crate::pes::PesFrame>> {
|
||||||
let rs = match self.mode {
|
let rs = match self.mode {
|
||||||
@@ -189,35 +188,6 @@ impl crate::pes::Stream for MkvStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// FrameSink sibling to the deprecated Stream impl; both coexist during the
|
|
||||||
/// 0.18 deprecation window. Caller may pick either at the trait-object
|
|
||||||
/// boundary — `Box<dyn Stream>` (deprecated) or `Box<dyn FrameSink>` (new).
|
|
||||||
/// Use `MkvStream::create(writer, title)` to construct the write half;
|
|
||||||
/// calling `FrameSink::write` on a `MkvStream::open(reader)` instance returns
|
|
||||||
/// `StreamReadOnly`. `finish` is where the Cues index is written, so it must
|
|
||||||
/// be called for the resulting MKV to be seekable.
|
|
||||||
#[allow(deprecated)] // delegating to deprecated Stream during the 0.18 deprecation window so callers don't see the deprecation twice.
|
|
||||||
impl crate::pes::FrameSink for MkvStream {
|
|
||||||
fn write(&mut self, frame: &crate::pes::PesFrame) -> io::Result<()> {
|
|
||||||
<Self as crate::pes::Stream>::write(self, frame)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn finish(self: Box<Self>) -> io::Result<()> {
|
|
||||||
// Why: Stream::finish takes &mut self, FrameSink::finish takes Box<Self>.
|
|
||||||
// Re-borrow inside the box, call Stream::finish, drop the box.
|
|
||||||
// The inner `MkvMuxer` is owned via `Option`, and `Stream::finish`
|
|
||||||
// already takes it via `Option::take()` — moving `*self` out of the
|
|
||||||
// box hands it the same field by-value, so the muxer's own
|
|
||||||
// by-value `finish()` runs correctly.
|
|
||||||
let mut s: Self = *self;
|
|
||||||
<Self as crate::pes::Stream>::finish(&mut s)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn info(&self) -> &crate::disc::DiscTitle {
|
|
||||||
<Self as crate::pes::Stream>::info(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── MKV header parsing (read side) ────────────────────────────
|
// ── MKV header parsing (read side) ────────────────────────────
|
||||||
|
|
||||||
/// Returns (DiscTitle, codec_privates: Vec<(track_number, codec_private_bytes)>)
|
/// Returns (DiscTitle, codec_privates: Vec<(track_number, codec_private_bytes)>)
|
||||||
|
|||||||
@@ -70,7 +70,6 @@ impl NetworkStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(deprecated)] // 0.18 trait split: migrate to FrameSource/FrameSink in follow-up commit.
|
|
||||||
impl crate::pes::Stream for NetworkStream {
|
impl crate::pes::Stream for NetworkStream {
|
||||||
fn read(&mut self) -> io::Result<Option<crate::pes::PesFrame>> {
|
fn read(&mut self) -> io::Result<Option<crate::pes::PesFrame>> {
|
||||||
match &mut self.mode {
|
match &mut self.mode {
|
||||||
@@ -109,30 +108,6 @@ impl crate::pes::Stream for NetworkStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// FrameSink sibling to the deprecated Stream impl; both coexist during the
|
|
||||||
/// 0.18 deprecation window. Caller may pick either at the trait-object
|
|
||||||
/// boundary — `Box<dyn Stream>` (deprecated) or `Box<dyn FrameSink>` (new).
|
|
||||||
/// Use `NetworkStream::connect(addr).meta(title)` to construct the write
|
|
||||||
/// half; calling `FrameSink::write` on `NetworkStream::listen(addr)` returns
|
|
||||||
/// `StreamReadOnly`.
|
|
||||||
#[allow(deprecated)] // delegating to deprecated Stream during the 0.18 deprecation window so callers don't see the deprecation twice.
|
|
||||||
impl crate::pes::FrameSink for NetworkStream {
|
|
||||||
fn write(&mut self, frame: &crate::pes::PesFrame) -> io::Result<()> {
|
|
||||||
<Self as crate::pes::Stream>::write(self, frame)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn finish(self: Box<Self>) -> io::Result<()> {
|
|
||||||
// Why: Stream::finish takes &mut self, FrameSink::finish takes Box<Self>.
|
|
||||||
// Re-borrow inside the box, call Stream::finish, drop the box.
|
|
||||||
let mut s: Self = *self;
|
|
||||||
<Self as crate::pes::Stream>::finish(&mut s)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn info(&self) -> &DiscTitle {
|
|
||||||
<Self as crate::pes::Stream>::info(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NetworkStream is PES-only — no IOStream/Read/Write byte interface.
|
// NetworkStream is PES-only — no IOStream/Read/Write byte interface.
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -182,7 +157,6 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore] // Requires TCP; may be flaky in CI environments
|
#[ignore] // Requires TCP; may be flaky in CI environments
|
||||||
#[allow(deprecated)] // 0.18 trait split: migrate to FrameSource/FrameSink in follow-up commit.
|
|
||||||
fn network_pes_roundtrip() {
|
fn network_pes_roundtrip() {
|
||||||
use crate::pes;
|
use crate::pes;
|
||||||
|
|
||||||
|
|||||||
+4
-34
@@ -16,7 +16,6 @@ impl NullStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(deprecated)] // 0.18 trait split: migrate to FrameSink in follow-up commit.
|
|
||||||
impl crate::pes::Stream for NullStream {
|
impl crate::pes::Stream for NullStream {
|
||||||
fn read(&mut self) -> io::Result<Option<crate::pes::PesFrame>> {
|
fn read(&mut self) -> io::Result<Option<crate::pes::PesFrame>> {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
@@ -32,43 +31,16 @@ impl crate::pes::Stream for NullStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// FrameSink sibling to the deprecated Stream impl; both coexist during the
|
|
||||||
/// 0.18 deprecation window. Caller may pick either at the trait-object
|
|
||||||
/// boundary — `Box<dyn Stream>` (deprecated) or `Box<dyn FrameSink>` (new).
|
|
||||||
/// The deprecation-window callers eventually migrate; this impl exists so
|
|
||||||
/// new callers can target `FrameSink` without waiting for the rest of the
|
|
||||||
/// migration to complete.
|
|
||||||
#[allow(deprecated)] // delegating to deprecated Stream during the 0.18 deprecation window so callers don't see the deprecation twice.
|
|
||||||
impl crate::pes::FrameSink for NullStream {
|
|
||||||
fn write(&mut self, frame: &crate::pes::PesFrame) -> io::Result<()> {
|
|
||||||
<Self as crate::pes::Stream>::write(self, frame)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn finish(self: Box<Self>) -> io::Result<()> {
|
|
||||||
// Why: Stream::finish takes &mut self, FrameSink::finish takes Box<Self>.
|
|
||||||
// Re-borrow inside the box, call Stream::finish, drop the box.
|
|
||||||
let mut s: Self = *self;
|
|
||||||
<Self as crate::pes::Stream>::finish(&mut s)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn info(&self) -> &DiscTitle {
|
|
||||||
<Self as crate::pes::Stream>::info(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::pes::FrameSink;
|
use crate::pes::Stream;
|
||||||
|
|
||||||
/// Smallest credible witness that the new FrameSink impl on a concrete
|
/// Verify NullStream routes through the `Stream` trait object cleanly.
|
||||||
/// `mux/*` sink works through the trait object: build a boxed
|
|
||||||
/// `dyn FrameSink`, write a frame, finish it. The trait-bridge correctness
|
|
||||||
/// is what's being verified — not NullStream-specific behaviour.
|
|
||||||
#[test]
|
#[test]
|
||||||
fn frame_sink_via_dyn_object_writes_and_finishes() {
|
fn stream_via_dyn_object_writes_and_finishes() {
|
||||||
let title = DiscTitle::empty();
|
let title = DiscTitle::empty();
|
||||||
let mut sink: Box<dyn FrameSink> = Box::new(NullStream::new(&title));
|
let mut sink: Box<dyn Stream> = Box::new(NullStream::new(&title));
|
||||||
|
|
||||||
let frame = crate::pes::PesFrame {
|
let frame = crate::pes::PesFrame {
|
||||||
track: 0,
|
track: 0,
|
||||||
@@ -77,9 +49,7 @@ mod tests {
|
|||||||
data: vec![0x01, 0x02, 0x03],
|
data: vec![0x01, 0x02, 0x03],
|
||||||
};
|
};
|
||||||
sink.write(&frame).unwrap();
|
sink.write(&frame).unwrap();
|
||||||
// info() routes through the trait object.
|
|
||||||
let _ = sink.info();
|
let _ = sink.info();
|
||||||
// finish() consumes the Box<Self> — must compile and run.
|
|
||||||
sink.finish().unwrap();
|
sink.finish().unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,7 +167,6 @@ pub struct InputOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Open a PES input stream (produces PES frames).
|
/// Open a PES input stream (produces PES frames).
|
||||||
#[allow(deprecated)] // 0.18 trait split: migrate to FrameSource in follow-up commit.
|
|
||||||
pub fn input(url: &str, opts: &InputOptions) -> io::Result<Box<dyn crate::pes::Stream>> {
|
pub fn input(url: &str, opts: &InputOptions) -> io::Result<Box<dyn crate::pes::Stream>> {
|
||||||
let parsed = parse_url(url);
|
let parsed = parse_url(url);
|
||||||
match parsed {
|
match parsed {
|
||||||
@@ -239,7 +238,6 @@ pub fn input(url: &str, opts: &InputOptions) -> io::Result<Box<dyn crate::pes::S
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Open a PES output stream (consumes PES frames).
|
/// Open a PES output stream (consumes PES frames).
|
||||||
#[allow(deprecated)] // 0.18 trait split: migrate to FrameSink in follow-up commit.
|
|
||||||
pub fn output(
|
pub fn output(
|
||||||
url: &str,
|
url: &str,
|
||||||
title: &crate::disc::DiscTitle,
|
title: &crate::disc::DiscTitle,
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ impl StdioStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(deprecated)] // 0.18 trait split: migrate to FrameSource/FrameSink in follow-up commit.
|
|
||||||
impl crate::pes::Stream for StdioStream {
|
impl crate::pes::Stream for StdioStream {
|
||||||
fn read(&mut self) -> io::Result<Option<crate::pes::PesFrame>> {
|
fn read(&mut self) -> io::Result<Option<crate::pes::PesFrame>> {
|
||||||
self.ensure_header_read()?;
|
self.ensure_header_read()?;
|
||||||
@@ -105,26 +104,3 @@ impl crate::pes::Stream for StdioStream {
|
|||||||
self.header_read || self.writer.is_some()
|
self.header_read || self.writer.is_some()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// FrameSink sibling to the deprecated Stream impl; both coexist during the
|
|
||||||
/// 0.18 deprecation window. Caller may pick either at the trait-object
|
|
||||||
/// boundary — `Box<dyn Stream>` (deprecated) or `Box<dyn FrameSink>` (new).
|
|
||||||
/// Use `StdioStream::output(title)` to construct the write half; calling
|
|
||||||
/// `FrameSink::write` on a `StdioStream::input()` returns `StreamReadOnly`.
|
|
||||||
#[allow(deprecated)] // delegating to deprecated Stream during the 0.18 deprecation window so callers don't see the deprecation twice.
|
|
||||||
impl crate::pes::FrameSink for StdioStream {
|
|
||||||
fn write(&mut self, frame: &crate::pes::PesFrame) -> io::Result<()> {
|
|
||||||
<Self as crate::pes::Stream>::write(self, frame)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn finish(self: Box<Self>) -> io::Result<()> {
|
|
||||||
// Why: Stream::finish takes &mut self, FrameSink::finish takes Box<Self>.
|
|
||||||
// Re-borrow inside the box, call Stream::finish, drop the box.
|
|
||||||
let mut s: Self = *self;
|
|
||||||
<Self as crate::pes::Stream>::finish(&mut s)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn info(&self) -> &DiscTitle {
|
|
||||||
<Self as crate::pes::Stream>::info(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
+44
-233
@@ -78,19 +78,20 @@ impl PesFrame {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deprecated; use [`FrameSource`] for read-only sources or [`FrameSink`]
|
/// A PES frame stream. One trait per format — same type opens for read
|
||||||
/// for write-only sinks. The runtime direction-error semantics
|
/// (`open()` / `listen()` / `input()`) or write (`create()` / `connect()` /
|
||||||
/// (`StreamReadOnly` / `StreamWriteOnly` from a wrong-direction call) are
|
/// `output()`). Calling the wrong-direction method returns a typed
|
||||||
/// removed in 0.18 — direction is type-checked.
|
/// `StreamReadOnly` / `StreamWriteOnly` error.
|
||||||
#[deprecated(
|
///
|
||||||
since = "0.18.0",
|
/// `Send` is required so streams can move across the producer/consumer
|
||||||
note = "use FrameSource (read-only) or FrameSink (write-only) instead"
|
/// threads in autorip's mux pipeline.
|
||||||
)]
|
pub trait Stream: Send {
|
||||||
pub trait Stream {
|
/// Read the next frame, or `Ok(None)` at end of stream. Returns
|
||||||
/// Read the next frame, or `Ok(None)` at end of stream.
|
/// `StreamWriteOnly` on a stream opened for writing.
|
||||||
fn read(&mut self) -> std::io::Result<Option<PesFrame>>;
|
fn read(&mut self) -> std::io::Result<Option<PesFrame>>;
|
||||||
|
|
||||||
/// Write a frame to the sink.
|
/// Write a frame to the sink. Returns `StreamReadOnly` on a stream
|
||||||
|
/// opened for reading.
|
||||||
fn write(&mut self, frame: &PesFrame) -> std::io::Result<()>;
|
fn write(&mut self, frame: &PesFrame) -> std::io::Result<()>;
|
||||||
|
|
||||||
/// Finalize the stream: flush buffered frames, write any container
|
/// Finalize the stream: flush buffered frames, write any container
|
||||||
@@ -116,115 +117,10 @@ pub trait Stream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read-only source of PES frames.
|
|
||||||
///
|
|
||||||
/// Replaces the read half of the deprecated [`Stream`] trait. Implementors
|
|
||||||
/// produce frames via [`read`](FrameSource::read) and never accept writes —
|
|
||||||
/// passing a `FrameSource` where a sink is expected is a compile error,
|
|
||||||
/// not a runtime `E9001`.
|
|
||||||
///
|
|
||||||
/// `info()` returns the source's `DiscTitle` metadata (track list, codec
|
|
||||||
/// info, duration). It must be stable across the lifetime of the source.
|
|
||||||
///
|
|
||||||
/// `codec_private(track)` exposes per-track codec initialization data
|
|
||||||
/// (H.264 SPS/PPS, HEVC VPS/SPS/PPS, AC-3 fscod, etc.) that downstream
|
|
||||||
/// muxers may need before any frame is written. `headers_ready()` returns
|
|
||||||
/// false until enough input frames have been seen to populate every video
|
|
||||||
/// track's codec-private blob — callers buffer frames they read until
|
|
||||||
/// `headers_ready()` returns true.
|
|
||||||
pub trait FrameSource: Send {
|
|
||||||
/// Read the next frame, or `Ok(None)` at end of stream.
|
|
||||||
fn read(&mut self) -> std::io::Result<Option<PesFrame>>;
|
|
||||||
|
|
||||||
/// Source metadata. Stable across reads — implementors must return a
|
|
||||||
/// consistent reference for the lifetime of the source.
|
|
||||||
fn info(&self) -> &crate::disc::DiscTitle;
|
|
||||||
|
|
||||||
/// Codec initialization data for a track (SPS/PPS, AC-3 fscod, etc.).
|
|
||||||
/// `None` for tracks that don't need codec_private (raw passthrough).
|
|
||||||
fn codec_private(&self, _track: usize) -> Option<Vec<u8>> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// True when `codec_private` is available for every video track —
|
|
||||||
/// callers buffer input frames until this flips, since some output
|
|
||||||
/// formats (MKV) can't write frames without codec init data.
|
|
||||||
fn headers_ready(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write-only sink of PES frames.
|
|
||||||
///
|
|
||||||
/// Replaces the write half of the deprecated [`Stream`] trait. Implementors
|
|
||||||
/// accept frames via [`write`](FrameSink::write) and finalize via
|
|
||||||
/// [`finish`](FrameSink::finish) — passing a `FrameSink` where a source is
|
|
||||||
/// expected is a compile error, not a runtime `E9000`.
|
|
||||||
///
|
|
||||||
/// `finish` takes `Box<Self>` (rather than `&mut self` like `Stream::finish`)
|
|
||||||
/// so that finalization is a one-shot terminal operation: callers cannot use
|
|
||||||
/// the sink after `finish` returns. This is the standard idiom for terminal
|
|
||||||
/// methods on `dyn Trait` objects.
|
|
||||||
pub trait FrameSink: Send {
|
|
||||||
/// Write a frame to the sink.
|
|
||||||
fn write(&mut self, frame: &PesFrame) -> std::io::Result<()>;
|
|
||||||
|
|
||||||
/// Finalize the sink: flush buffered frames, write any container index
|
|
||||||
/// (e.g. MKV `Cues`), close the underlying file/socket. Consumes the
|
|
||||||
/// sink — callers cannot use it afterwards.
|
|
||||||
fn finish(self: Box<Self>) -> std::io::Result<()>;
|
|
||||||
|
|
||||||
/// Sink metadata. Stable across writes — implementors must return a
|
|
||||||
/// consistent reference for the lifetime of the sink.
|
|
||||||
fn info(&self) -> &crate::disc::DiscTitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bridge: any **`Send`** type implementing the deprecated `Stream` trait
|
|
||||||
// is also a `FrameSource`. This lets existing concrete `Stream` impls in
|
|
||||||
// `mux/*` satisfy `FrameSource` bounds without per-type migration during
|
|
||||||
// the 0.18 deprecation window.
|
|
||||||
//
|
|
||||||
// **Send caveat (read me before tightening `Stream` itself).** This
|
|
||||||
// blanket carries a `T: Send` bound rather than promoting `Send` to a
|
|
||||||
// supertrait of `Stream`, because not every concrete in-tree `Stream`
|
|
||||||
// impl is `Send`: `MkvStream` and `M2tsStream` carry `Box<dyn Read>`
|
|
||||||
// and `Box<dyn Write>` fields whose trait objects don't include `Send`.
|
|
||||||
// Adding `Stream: Send` would force a wider audit (every `Box<dyn Read>`
|
|
||||||
// becomes `Box<dyn Read + Send>`) than this commit is taking on, and
|
|
||||||
// the type-level migration target is `FrameSource` / `FrameSink`
|
|
||||||
// directly anyway. Consequence: coercing a non-Send `Box<dyn Stream>`
|
|
||||||
// (the return shape of `crate::mux::input` / `output`) to
|
|
||||||
// `Box<dyn FrameSource>` will fail with a `T: Send` trait-bound error.
|
|
||||||
// The fix on the consumer side is to construct a Send-compliant
|
|
||||||
// `FrameSource` / `FrameSink` directly rather than relying on this
|
|
||||||
// bridge for non-Send streams.
|
|
||||||
//
|
|
||||||
// Note: `FrameSink` cannot be blanket-impl'd from `Stream` because
|
|
||||||
// `Stream::finish` takes `&mut self` while `FrameSink::finish` takes
|
|
||||||
// `Box<Self>`; concrete types will be migrated in a follow-up commit.
|
|
||||||
#[allow(deprecated)]
|
|
||||||
impl<T: Stream + Send + ?Sized> FrameSource for T {
|
|
||||||
fn read(&mut self) -> std::io::Result<Option<PesFrame>> {
|
|
||||||
<Self as Stream>::read(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn info(&self) -> &crate::disc::DiscTitle {
|
|
||||||
<Self as Stream>::info(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn codec_private(&self, track: usize) -> Option<Vec<u8>> {
|
|
||||||
<Self as Stream>::codec_private(self, track)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn headers_ready(&self) -> bool {
|
|
||||||
<Self as Stream>::headers_ready(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wraps any output stream and counts bytes written.
|
/// Wraps any output stream and counts bytes written.
|
||||||
///
|
///
|
||||||
/// Progress tracking is a CLI concern — streams don't know their size.
|
/// Progress tracking is a CLI concern — streams don't know their size.
|
||||||
/// Wrap the output with CountingStream, then query bytes_written().
|
/// Wrap the output with `CountingStream`, then query `bytes_written()`.
|
||||||
///
|
///
|
||||||
/// ```text
|
/// ```text
|
||||||
/// let mut output = CountingStream::new(libfreemkv::output(dest, &title)?);
|
/// let mut output = CountingStream::new(libfreemkv::output(dest, &title)?);
|
||||||
@@ -233,13 +129,11 @@ impl<T: Stream + Send + ?Sized> FrameSource for T {
|
|||||||
/// let pct = output.bytes_written() as f64 / total as f64;
|
/// let pct = output.bytes_written() as f64 / total as f64;
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[allow(deprecated)]
|
|
||||||
pub struct CountingStream {
|
pub struct CountingStream {
|
||||||
inner: Box<dyn Stream>,
|
inner: Box<dyn Stream>,
|
||||||
written: u64,
|
written: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
impl CountingStream {
|
impl CountingStream {
|
||||||
pub fn new(inner: Box<dyn Stream>) -> Self {
|
pub fn new(inner: Box<dyn Stream>) -> Self {
|
||||||
Self { inner, written: 0 }
|
Self { inner, written: 0 }
|
||||||
@@ -251,7 +145,6 @@ impl CountingStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
impl Stream for CountingStream {
|
impl Stream for CountingStream {
|
||||||
fn read(&mut self) -> std::io::Result<Option<PesFrame>> {
|
fn read(&mut self) -> std::io::Result<Option<PesFrame>> {
|
||||||
self.inner.read()
|
self.inner.read()
|
||||||
@@ -293,88 +186,35 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Direct `FrameSource` impl (not via the deprecated `Stream` blanket).
|
/// Minimal in-memory `Stream` for trait-shape tests. `read` replays
|
||||||
struct MockSource {
|
/// pre-seeded frames; `write` collects them.
|
||||||
frames: std::vec::IntoIter<PesFrame>,
|
struct MockStream {
|
||||||
|
read_queue: std::vec::IntoIter<PesFrame>,
|
||||||
|
written: Vec<PesFrame>,
|
||||||
title: DiscTitle,
|
title: DiscTitle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MockSource {
|
impl MockStream {
|
||||||
fn new(frames: Vec<PesFrame>) -> Self {
|
fn new(read_frames: Vec<PesFrame>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
frames: frames.into_iter(),
|
read_queue: read_frames.into_iter(),
|
||||||
|
written: Vec::new(),
|
||||||
title: DiscTitle::empty(),
|
title: DiscTitle::empty(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FrameSource for MockSource {
|
impl Stream for MockStream {
|
||||||
fn read(&mut self) -> std::io::Result<Option<PesFrame>> {
|
fn read(&mut self) -> std::io::Result<Option<PesFrame>> {
|
||||||
Ok(self.frames.next())
|
Ok(self.read_queue.next())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn info(&self) -> &DiscTitle {
|
|
||||||
&self.title
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Direct `FrameSink` impl (not via the deprecated `Stream` blanket).
|
|
||||||
struct MockSink {
|
|
||||||
collected: Vec<PesFrame>,
|
|
||||||
title: DiscTitle,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MockSink {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
collected: Vec::new(),
|
|
||||||
title: DiscTitle::empty(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FrameSink for MockSink {
|
|
||||||
fn write(&mut self, frame: &PesFrame) -> std::io::Result<()> {
|
fn write(&mut self, frame: &PesFrame) -> std::io::Result<()> {
|
||||||
self.collected.push(frame.clone());
|
self.written.push(frame.clone());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(self: Box<Self>) -> std::io::Result<()> {
|
fn finish(&mut self) -> std::io::Result<()> {
|
||||||
// Drop self; in real sinks this is where flush/fsync/close happens.
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn info(&self) -> &DiscTitle {
|
|
||||||
&self.title
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Variant of `MockSink` whose `finish` returns the collected frames so
|
|
||||||
/// the test can assert on them after the consuming `Box<Self>` call.
|
|
||||||
struct CollectingSink {
|
|
||||||
collected: Vec<PesFrame>,
|
|
||||||
title: DiscTitle,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CollectingSink {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
collected: Vec::new(),
|
|
||||||
title: DiscTitle::empty(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FrameSink for CollectingSink {
|
|
||||||
fn write(&mut self, frame: &PesFrame) -> std::io::Result<()> {
|
|
||||||
self.collected.push(frame.clone());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn finish(self: Box<Self>) -> std::io::Result<()> {
|
|
||||||
// Real CollectingSink consumers would expose `take()` before
|
|
||||||
// finish; this trait method just confirms the boxed signature
|
|
||||||
// compiles and runs.
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -384,72 +224,43 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn frame_source_yields_frames_then_eof() {
|
fn stream_read_yields_frames_then_eof() {
|
||||||
let frames = vec![make_frame(0, 0), make_frame(1, 1_000), make_frame(0, 2_000)];
|
let frames = vec![make_frame(0, 0), make_frame(1, 1_000), make_frame(0, 2_000)];
|
||||||
let mut src = MockSource::new(frames.clone());
|
let mut s = MockStream::new(frames.clone());
|
||||||
|
|
||||||
let f0 = src.read().unwrap().expect("first frame");
|
let f0 = s.read().unwrap().expect("first frame");
|
||||||
assert_eq!(f0.track, frames[0].track);
|
assert_eq!(f0.track, frames[0].track);
|
||||||
assert_eq!(f0.pts, frames[0].pts);
|
assert_eq!(f0.pts, frames[0].pts);
|
||||||
assert!(f0.keyframe);
|
assert!(f0.keyframe);
|
||||||
|
|
||||||
let f1 = src.read().unwrap().expect("second frame");
|
let f1 = s.read().unwrap().expect("second frame");
|
||||||
assert_eq!(f1.track, frames[1].track);
|
|
||||||
assert_eq!(f1.pts, frames[1].pts);
|
assert_eq!(f1.pts, frames[1].pts);
|
||||||
|
|
||||||
let f2 = src.read().unwrap().expect("third frame");
|
let f2 = s.read().unwrap().expect("third frame");
|
||||||
assert_eq!(f2.track, frames[2].track);
|
|
||||||
assert_eq!(f2.pts, frames[2].pts);
|
assert_eq!(f2.pts, frames[2].pts);
|
||||||
|
|
||||||
assert!(src.read().unwrap().is_none());
|
assert!(s.read().unwrap().is_none());
|
||||||
assert!(src.read().unwrap().is_none()); // idempotent at EOF
|
assert!(s.read().unwrap().is_none()); // idempotent at EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn frame_sink_collects_then_finishes() {
|
fn stream_write_collects_then_finishes() {
|
||||||
let mut sink = MockSink::new();
|
let mut s = MockStream::new(Vec::new());
|
||||||
let frames = [make_frame(0, 0), make_frame(1, 100), make_frame(2, 200)];
|
let frames = [make_frame(0, 0), make_frame(1, 100), make_frame(2, 200)];
|
||||||
|
|
||||||
for f in &frames {
|
for f in &frames {
|
||||||
sink.write(f).unwrap();
|
s.write(f).unwrap();
|
||||||
}
|
}
|
||||||
assert_eq!(sink.collected.len(), 3);
|
assert_eq!(s.written.len(), 3);
|
||||||
assert_eq!(sink.collected[0].pts, 0);
|
s.finish().unwrap();
|
||||||
assert_eq!(sink.collected[1].pts, 100);
|
|
||||||
assert_eq!(sink.collected[2].pts, 200);
|
|
||||||
|
|
||||||
// Box-and-finish — the `self: Box<Self>` shape must compile and run.
|
|
||||||
Box::new(sink).finish().unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn frame_sink_via_dyn_object() {
|
fn stream_via_dyn_object() {
|
||||||
let frames = [make_frame(0, 0), make_frame(0, 33)];
|
let mut s: Box<dyn Stream> = Box::new(MockStream::new(vec![make_frame(0, 0)]));
|
||||||
let mut sink: Box<dyn FrameSink> = Box::new(CollectingSink::new());
|
let frame = s.read().unwrap().expect("first frame");
|
||||||
|
s.write(&frame).unwrap();
|
||||||
for f in &frames {
|
let _ = s.info();
|
||||||
sink.write(f).unwrap();
|
s.finish().unwrap();
|
||||||
}
|
|
||||||
// info() routes through the trait object.
|
|
||||||
let _ = sink.info();
|
|
||||||
sink.finish().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The deprecated blanket impl: any concrete `Stream` should also act as
|
|
||||||
/// a `FrameSource`. `NullStream` has the simplest constructor of every
|
|
||||||
/// concrete `Stream` impl in `mux/*`, so it's the smallest credible
|
|
||||||
/// witness that the bridge compiles and dispatches correctly.
|
|
||||||
#[test]
|
|
||||||
#[allow(deprecated)]
|
|
||||||
fn deprecated_stream_satisfies_frame_source() {
|
|
||||||
let title = DiscTitle::empty();
|
|
||||||
let mut null = crate::mux::NullStream::new(&title);
|
|
||||||
|
|
||||||
let src: &mut dyn FrameSource = &mut null;
|
|
||||||
// NullStream::read returns Ok(None) — it's a write-only sink.
|
|
||||||
assert!(src.read().unwrap().is_none());
|
|
||||||
// info() forwards through the blanket impl.
|
|
||||||
let _ = src.info();
|
|
||||||
assert!(src.headers_ready());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,6 @@
|
|||||||
//! Integration tests for progress reporting, halt behavior, drop safety,
|
//! Integration tests for progress reporting, halt behavior, drop safety,
|
||||||
//! and the file-backed sector reader round trip.
|
//! and the file-backed sector reader round trip.
|
||||||
|
|
||||||
// 0.18 trait split: this suite still drives the deprecated `pes::Stream`
|
|
||||||
// trait directly. It will be migrated to `FrameSource`/`FrameSink` in the
|
|
||||||
// follow-up that ports concrete impls.
|
|
||||||
#![allow(deprecated)]
|
|
||||||
|
|
||||||
use libfreemkv::disc::{CopyOptions, DiscRegion};
|
use libfreemkv::disc::{CopyOptions, DiscRegion};
|
||||||
use libfreemkv::error::Result;
|
use libfreemkv::error::Result;
|
||||||
use libfreemkv::pes::Stream as PesStream;
|
use libfreemkv::pes::Stream as PesStream;
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
//! Integration tests for the PES stream pipeline.
|
//! Integration tests for the PES stream pipeline.
|
||||||
|
|
||||||
// 0.18 trait split: this suite still drives the deprecated `pes::Stream`
|
|
||||||
// trait directly. It will be migrated to `FrameSource`/`FrameSink` in the
|
|
||||||
// follow-up that ports concrete impls.
|
|
||||||
#![allow(deprecated)]
|
|
||||||
|
|
||||||
use libfreemkv::mux::meta::M2tsMeta;
|
use libfreemkv::mux::meta::M2tsMeta;
|
||||||
use libfreemkv::pes::Stream as PesStream;
|
use libfreemkv::pes::Stream as PesStream;
|
||||||
use libfreemkv::*;
|
use libfreemkv::*;
|
||||||
|
|||||||
Reference in New Issue
Block a user