media_pp/lib.rs
1//! A small, GStreamer-flavored media pipeline library built on
2//! [`ffmpeg-next`](https://docs.rs/ffmpeg-next).
3//!
4//! A pipeline has one or more [`SourceElement`](element::SourceElement)s,
5//! each feeding a graph of [`Filter`](element::Filter)s that ends in a
6//! [`Sink`](element::Sink):
7//!
8//! ```text
9//! FileDemuxer -> SwDecoder -> Queue -> Pacer -> FrameCounter
10//! ```
11//!
12//! Each source registered with a [`Pipeline`](pipeline::Pipeline) runs on its
13//! own background thread. Within that source's graph,
14//! [`Sink::consume`](element::Sink::consume) is otherwise a plain synchronous
15//! call that returns [`Result`], so a stage's failure propagates straight back
16//! up the call stack with `?`. A [`Queue`](queue::Queue) adds another explicit
17//! thread boundary inside a branch: it owns a worker thread and a bounded
18//! channel, which is also where error handling changes shape — past that
19//! boundary a downstream failure can no longer be returned to the pusher, so
20//! it is reported on the [`Bus`](bus::Bus) as
21//! [`BusEvent::Error`](bus::BusEvent::Error) and the worker keeps going.
22//!
23//! ```no_run
24//! use std::{sync::atomic::Ordering, time::Duration};
25//!
26//! use media_pp::{
27//! elements::{FrameCounter, TestVideoOptions, TestVideoSource},
28//! pipeline::Pipeline,
29//! };
30//!
31//! # fn main() -> media_pp::Result<()> {
32//! media_pp::init()?;
33//!
34//! let source = TestVideoSource::new("source", TestVideoOptions::default());
35//! let (counter, frames) = FrameCounter::new("counter");
36//!
37//! let pipeline = Pipeline::new("demo", source, |source, ctx| {
38//! let branch = ctx.branch().to(Box::new(counter))?;
39//! ctx.attach(source, 0, branch)?;
40//! Ok(())
41//! })?;
42//!
43//! pipeline.run()?;
44//! std::thread::sleep(Duration::from_millis(200));
45//! pipeline.stop();
46//!
47//! println!("frames: {}", frames.load(Ordering::Relaxed));
48//! # Ok(())
49//! # }
50//! ```
51//!
52//! # Where to start
53//!
54//! - [`elements`] is the inventory of built-in sources, filters, and sinks.
55//! Each type's own documentation states what buffers it accepts, what it
56//! owns, and how it behaves under error and runtime control.
57//! - [`pipeline`] builds and runs a graph; [`element`] and [`pad`] are the
58//! traits and the one output port everything is wired through.
59//! - [`buffer`] is what travels between elements, [`control`] is what
60//! Pause/Resume/Stop/Seek travel through, and [`bus`] is how an element
61//! reports something the caller could not have been handed directly.
62//!
63//! # Buffer and timeline contract
64//!
65//! [`MediaBuffer`](buffer::MediaBuffer) payloads are `Arc`-wrapped, so
66//! fan-out clones a reference rather than the media itself. PTS, duration,
67//! packet time bases, and video color information survive every stage that
68//! does not deliberately create a new timeline.
69//!
70//! [`Eos`](buffer::MediaBuffer::Eos) is data, and it is forwarded like data:
71//! stateful stages (encoders holding delayed frames, muxers, resamplers)
72//! flush on it before passing it on. That is what separates the two ways a
73//! pipeline ends — [`Pipeline::finish`](pipeline::Pipeline::finish) sends
74//! ordered EOS from the source and drains everything behind it, while
75//! [`Pipeline::stop`](pipeline::Pipeline::stop) abandons buffered work.
76//!
77//! # Features and platforms
78//!
79//! The crate has no default features. Hardware backends (`d3d11`, `d3d12`,
80//! `dxgi-capture`, `cuda`, `wasapi-*`, `pipewire-*`) and the optional `ort`
81//! and `webrtc` integrations are each behind their own Cargo feature, and
82//! backend-specific types carry the backend's prefix. [docs.rs] builds this
83//! crate for Linux and therefore omits the Windows-only API; the complete
84//! reference is published separately (see the repository README).
85//!
86//! # Logging
87//!
88//! Diagnostics never install a global `log` logger or `tracing` subscriber.
89//! The file logger in [`log`] is private and opt-in through
90//! [`log::init`], and the caller owns the returned guard for as long as
91//! records must keep being written and flushed.
92//!
93//! [docs.rs]: https://docs.rs/media-pp
94
95// docs.rs passes `--cfg docsrs` (see `package.metadata.docs.rs`), which labels
96// every feature-gated item with the Cargo feature that enables it. Stable
97// builds never see the `feature` attribute.
98#![cfg_attr(docsrs, feature(doc_cfg))]
99
100mod core;
101pub mod elements;
102pub mod error;
103mod platform;
104#[cfg(test)]
105mod test_support;
106
107// Flat re-export: `core/` only exists to group these files on disk (see
108// its module doc) — every external and internal caller keeps using
109// `crate::pipeline`/`media_pp::pipeline` etc., never `crate::core::...`.
110pub use core::{
111 buffer, bus, clock, color, control, driver, element, graph, log, pad, pipeline, playback_clock,
112 pool, pp_log, queue,
113};
114
115// Same flat-namespace reasoning as above, but crate-private: `schedule`/
116// `time` are pacing/rescale internals `crate::elements` builds on, not
117// exposed in any public element's own field/method signature — nothing
118// downstream of this crate needs `PeriodicSchedule`/`ActiveTimeline`/
119// `MediaTimestamp`/`TimeBase` itself. `pub(crate) use` keeps the same
120// `crate::schedule`/`crate::time` paths working for every internal caller
121// without also making them part of this crate's external API surface.
122pub(crate) use core::{schedule, time};
123
124pub use error::{Error, Result};
125
126/// The [`ffmpeg-next`](https://docs.rs/ffmpeg-next) this crate is built on.
127///
128/// Re-exported because it is part of this crate's API, not an implementation
129/// detail behind it: [`MediaBuffer`](buffer::MediaBuffer) carries `ffmpeg`
130/// packets and frames directly, an encoder's `parameters()`/`time_base` are
131/// `ffmpeg` types, and [`Error::Ffmpeg`] wraps `ffmpeg`'s own error.
132///
133/// Use this rather than depending on `ffmpeg-next` separately. A separate
134/// dependency has to resolve to the same version as this crate's — when it
135/// does not, the two `ffmpeg-next`s are distinct crates to the compiler and
136/// every one of the types above stops matching, with nothing in the error
137/// pointing at the version as the cause.
138pub use ffmpeg_next as ffmpeg;
139
140/// Must be called once before using any element that touches ffmpeg.
141pub fn init() -> Result<()> {
142 ffmpeg_next::init()?;
143 Ok(())
144}