Skip to main content

oxideav_core/
frame.rs

1//! Uncompressed audio and video frames.
2
3use crate::subtitle::SubtitleCue;
4use crate::vector::VectorFrame;
5
6/// A decoded chunk of uncompressed data: either audio samples, a video
7/// picture, or (for subtitle streams) a single styled cue.
8///
9/// Marked `#[non_exhaustive]` — consumers that match on variants must
10/// include a wildcard arm. This lets the crate add new frame kinds (data
11/// tracks, hap rops, …) without breaking downstream code.
12#[derive(Clone, Debug)]
13#[non_exhaustive]
14pub enum Frame {
15    Audio(AudioFrame),
16    Video(VideoFrame),
17    /// A single subtitle cue. Timing is carried inside the cue itself
18    /// (`start_us`/`end_us`) so it's independent of container time bases,
19    /// but the enclosing pipeline/muxer can still rescale via `pts` at
20    /// the packet layer.
21    Subtitle(SubtitleCue),
22    /// A resolution-independent vector-graphics frame. Produced by
23    /// vector-format decoders (`oxideav-svg`, the vector path of
24    /// `oxideav-pdf`) and consumed by vector renderers / writers.
25    /// See [`crate::vector`] for the full primitive set.
26    Vector(VectorFrame),
27}
28
29impl Frame {
30    pub fn pts(&self) -> Option<i64> {
31        match self {
32            Self::Audio(a) => a.pts,
33            Self::Video(v) => v.pts,
34            Self::Subtitle(s) => Some(s.start_us),
35            Self::Vector(v) => v.pts,
36        }
37    }
38}
39
40/// Uncompressed audio frame.
41///
42/// Stream-level properties (sample format, channel count, sample rate,
43/// time base) are NOT carried per-frame — read them from the stream's
44/// [`CodecParameters`](crate::CodecParameters). Frames stay lightweight
45/// because real-time playback moves thousands per second per stream.
46///
47/// Sample layout is determined by the stream's `SampleFormat`:
48/// - Interleaved formats: `data` has one plane; samples are stored as
49///   `ch0 ch1 ... chN ch0 ch1 ... chN ...`.
50/// - Planar formats: `data` has one plane per channel.
51///
52/// Use [`SampleFormat::plane_count`](crate::SampleFormat::plane_count)
53/// with the stream's channel count to compute the expected `data.len()`.
54#[derive(Clone, Debug)]
55pub struct AudioFrame {
56    /// Number of samples *per channel* in this frame. Variable per-frame
57    /// for VBR codecs and on partial flushes.
58    pub samples: u32,
59    pub pts: Option<i64>,
60    /// Raw sample bytes. Length matches `format.plane_count(channels)`
61    /// from the stream's `CodecParameters`.
62    pub data: Vec<Vec<u8>>,
63}
64
65/// Uncompressed video frame.
66///
67/// Stream-level properties (pixel format, width, height, time base) are
68/// NOT carried per-frame — read them from the stream's
69/// [`CodecParameters`](crate::CodecParameters). Frames stay lightweight
70/// because real-time playback moves thousands per second per stream.
71#[derive(Clone, Debug)]
72pub struct VideoFrame {
73    pub pts: Option<i64>,
74    /// One entry per plane (e.g., 3 for Yuv420P). Each entry is `(stride, bytes)`.
75    pub planes: Vec<VideoPlane>,
76}
77
78#[derive(Clone, Debug)]
79pub struct VideoPlane {
80    /// Bytes per row in `data`.
81    pub stride: usize,
82    pub data: Vec<u8>,
83}