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}