Skip to main content

media_pp/core/
buffer.rs

1//! What travels between elements.
2//!
3//! [`MediaBuffer`] is an enum rather than one opaque buffer type, because
4//! `ffmpeg-next` already hands back strongly-typed packets and frames and
5//! collapsing them would only mean unwrapping again downstream. Its own
6//! documentation covers why each payload is shared rather than copied, and
7//! why a video frame arrives through a pool reference.
8
9use std::sync::Arc;
10
11use ffmpeg_next as ffmpeg;
12
13use crate::pool::UnboundObjectPoolRef;
14
15/// The unit of data that flows between elements.
16///
17/// Compressed and uncompressed data are kept as distinct variants (rather
18/// than a single opaque `Buffer` type like GStreamer) because ffmpeg-next
19/// already gives us strongly-typed `Packet`/`Frame` types — collapsing them
20/// into one type would just mean unwrapping again downstream.
21///
22/// Payloads are `Arc`-wrapped so `MediaBuffer` is cheaply `Clone` —
23/// duplicating a buffer (e.g. [`crate::elements::Tee`] fanning packets out
24/// to a decode branch and a remux branch) is a refcount bump, never a copy
25/// of the encoded/decoded data.
26///
27/// `Video` specifically wraps an [`UnboundObjectPoolRef`], not a plain
28/// `ffmpeg::frame::Video` — that's what lets whichever element produced it
29/// (see [`crate::pool::UnboundObjectPool`], owned as that element's own
30/// struct field) get the underlying buffer back automatically once every
31/// `Arc` clone downstream has been dropped, instead of it just being freed.
32#[derive(Clone)]
33pub enum MediaBuffer {
34    /// Encoded media packet produced by a demuxer or encoder.
35    ///
36    /// Its PTS, DTS, duration, stream index, and time base remain part of the
37    /// packet contract while it travels through packet-level elements.
38    Packet(Arc<ffmpeg::Packet>),
39
40    /// Decoded video frame whose backing storage returns to its producer's
41    /// [`crate::pool::UnboundObjectPool`] after the last `Arc` clone drops.
42    ///
43    /// Treat the published frame as immutable. A transforming element creates
44    /// a replacement frame and preserves PTS, duration, and color metadata
45    /// unless it intentionally establishes a new timeline.
46    Video(Arc<UnboundObjectPoolRef<ffmpeg::frame::Video>>),
47
48    /// Decoded audio frame shared immutably between downstream branches.
49    ///
50    /// Sample format, sample rate, channel layout, PTS, and duration describe
51    /// the audio contract a transforming element must either preserve or
52    /// deliberately replace.
53    Audio(Arc<ffmpeg::frame::Audio>),
54
55    /// Ordered end-of-stream marker.
56    ///
57    /// Stateful elements flush delayed output before forwarding it, and
58    /// muxers finalize their output after receiving it. Unlike
59    /// [`crate::control::ControlMsg::Stop`], this requests natural completion
60    /// rather than abandoning buffered work.
61    Eos,
62}
63
64impl MediaBuffer {
65    /// Returns whether this buffer is the ordered [`MediaBuffer::Eos`] marker.
66    pub fn is_eos(&self) -> bool {
67        matches!(self, MediaBuffer::Eos)
68    }
69
70    /// Stable, human-readable variant name for diagnostics emitted when
71    /// elements are wired to an incompatible media type.
72    pub fn kind(&self) -> &'static str {
73        match self {
74            MediaBuffer::Packet(_) => "Packet",
75            MediaBuffer::Video(_) => "Video",
76            MediaBuffer::Audio(_) => "Audio",
77            MediaBuffer::Eos => "Eos",
78        }
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85
86    #[test]
87    fn kind_reports_each_variant() {
88        assert_eq!(
89            MediaBuffer::Packet(Arc::new(ffmpeg::Packet::empty())).kind(),
90            "Packet"
91        );
92        assert_eq!(
93            MediaBuffer::Audio(Arc::new(ffmpeg::frame::Audio::empty())).kind(),
94            "Audio"
95        );
96        assert_eq!(MediaBuffer::Eos.kind(), "Eos");
97    }
98}