Skip to main content

oxideav_core/
frame.rs

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