Skip to main content

ff_filter/graph/
graph.rs

1//! [`FilterGraph`] struct definition and push/pull implementations.
2
3use ff_format::{AudioFrame, VideoFrame};
4
5use crate::error::FilterError;
6use crate::filter_inner::FilterGraphInner;
7
8use super::builder::FilterGraphBuilder;
9
10// ── FilterGraph ───────────────────────────────────────────────────────────────
11
12/// An `FFmpeg` libavfilter filter graph.
13///
14/// Constructed via [`FilterGraph::builder()`].  The underlying `AVFilterGraph` is
15/// initialised lazily on the first push call, deriving format information from
16/// the first frame.
17///
18/// # Examples
19///
20/// ```ignore
21/// use ff_filter::FilterGraph;
22///
23/// let mut graph = FilterGraph::builder()
24///     .scale(1280, 720)
25///     .build()?;
26///
27/// // Push decoded frames in …
28/// graph.push_video(0, &video_frame)?;
29///
30/// // … and pull filtered frames out.
31/// while let Some(frame) = graph.pull_video()? {
32///     // use frame
33/// }
34/// ```
35pub struct FilterGraph {
36    pub(crate) inner: FilterGraphInner,
37    pub(crate) output_resolution: Option<(u32, u32)>,
38}
39
40impl std::fmt::Debug for FilterGraph {
41    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42        f.debug_struct("FilterGraph").finish_non_exhaustive()
43    }
44}
45
46impl FilterGraph {
47    /// Create a new builder.
48    #[must_use]
49    pub fn builder() -> FilterGraphBuilder {
50        FilterGraphBuilder::new()
51    }
52
53    /// Creates a `FilterGraph` from a pre-built [`FilterGraphInner`].
54    ///
55    /// Used by [`MultiTrackComposer`](crate::MultiTrackComposer) and
56    /// [`MultiTrackAudioMixer`](crate::MultiTrackAudioMixer) to wrap
57    /// source-only filter graphs that need no external `buffersrc`.
58    pub(crate) fn from_prebuilt(inner: FilterGraphInner) -> Self {
59        Self {
60            inner,
61            output_resolution: None,
62        }
63    }
64
65    /// Returns the output resolution produced by this graph's `scale` filter step,
66    /// if one was configured.
67    ///
68    /// When multiple `scale` steps are chained, the **last** one's dimensions are
69    /// returned. Returns `None` when no `scale` step was added.
70    #[must_use]
71    pub fn output_resolution(&self) -> Option<(u32, u32)> {
72        self.output_resolution
73    }
74
75    /// Push a video frame into input slot `slot`.
76    ///
77    /// On the first call the filter graph is initialised using this frame's
78    /// format, resolution, and time base.
79    ///
80    /// # Errors
81    ///
82    /// - [`FilterError::InvalidInput`] if `slot` is out of range.
83    /// - [`FilterError::BuildFailed`] if the graph cannot be initialised.
84    /// - [`FilterError::ProcessFailed`] if the `FFmpeg` push fails.
85    pub fn push_video(&mut self, slot: usize, frame: &VideoFrame) -> Result<(), FilterError> {
86        self.inner.push_video(slot, frame)
87    }
88
89    /// Pull the next filtered video frame, if one is available.
90    ///
91    /// Returns `None` when the internal `FFmpeg` buffer is empty (EAGAIN) or
92    /// at end-of-stream.
93    ///
94    /// # Errors
95    ///
96    /// Returns [`FilterError::ProcessFailed`] on an unexpected `FFmpeg` error.
97    pub fn pull_video(&mut self) -> Result<Option<VideoFrame>, FilterError> {
98        self.inner.pull_video()
99    }
100
101    /// Push an audio frame into input slot `slot`.
102    ///
103    /// On the first call the audio filter graph is initialised using this
104    /// frame's format, sample rate, and channel count.
105    ///
106    /// # Errors
107    ///
108    /// - [`FilterError::InvalidInput`] if `slot` is out of range.
109    /// - [`FilterError::BuildFailed`] if the graph cannot be initialised.
110    /// - [`FilterError::ProcessFailed`] if the `FFmpeg` push fails.
111    pub fn push_audio(&mut self, slot: usize, frame: &AudioFrame) -> Result<(), FilterError> {
112        self.inner.push_audio(slot, frame)
113    }
114
115    /// Pull the next filtered audio frame, if one is available.
116    ///
117    /// Returns `None` when the internal `FFmpeg` buffer is empty (EAGAIN) or
118    /// at end-of-stream.
119    ///
120    /// # Errors
121    ///
122    /// Returns [`FilterError::ProcessFailed`] on an unexpected `FFmpeg` error.
123    pub fn pull_audio(&mut self) -> Result<Option<AudioFrame>, FilterError> {
124        self.inner.pull_audio()
125    }
126}