Skip to main content

mediaway_common/
lib.rs

1//! Shared Mediaway types: timebase, pixel/sample formats, GPU buffer handles.
2//!
3//! Platform backends and encode/decode crates all depend on this crate.
4//! Keep it dependency-light and `unsafe`-free.
5
6#![forbid(unsafe_code)]
7
8mod formats;
9mod frame;
10mod gpu;
11
12pub use bytes::Bytes;
13pub use formats::{PixelFormat, SampleFormat};
14pub use frame::{AudioFrame, VideoFrame, VideoFrameStorage};
15pub use gpu::{GpuBufferHandle, GpuDeviceHandle, NativeHandle};
16
17/// Integer rational timebase (`num / den` seconds) for precise fractional timestamp conversion.
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19pub struct Rational {
20    /// Numerator (timestamp units).
21    pub num: u64,
22    /// Denominator (timebase / timescale). Must be non-zero.
23    pub den: u32,
24}
25
26impl Rational {
27    /// Construct a rational. Panics are forbidden in production paths — callers
28    /// must validate `den != 0` before construction when input is untrusted.
29    #[must_use]
30    pub const fn new(num: u64, den: u32) -> Self {
31        Self { num, den }
32    }
33}
34
35/// Supported codec types for demuxing and muxing.
36///
37/// `#[repr(u8)]` with explicit discriminants, kept in lockstep with
38/// `mediaway_codec_kind_t` in `crates/mediaway-ffi/include/mediaway/container.h` — this
39/// type crosses the C ABI directly (`mediaway-ffi`'s `MediawayCodecKind` is a type alias to
40/// this enum, not a converting wrapper), so an implicit/compiler-chosen discriminant order
41/// is a real correctness bug, not just a style nit: `Vp8` was appended to the C header at
42/// `= 12` when `WebM` support landed, but this enum's declaration order put it 5th
43/// (discriminant `4`, sandwiched between `Vp9` and `Aac`) — every codec from `Aac` onward
44/// silently carried the wrong wire value across the C ABI until these discriminants were
45/// pinned explicitly to match the header.
46#[repr(u8)]
47#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
48pub enum CodecKind {
49    /// H.264 / AVC video.
50    H264 = 0,
51    /// HEVC / H.265 video.
52    Hevc = 1,
53    /// AV1 video.
54    Av1 = 2,
55    /// VP9 video.
56    Vp9 = 3,
57    /// AAC audio.
58    Aac = 4,
59    /// Opus audio.
60    Opus = 5,
61    /// MP3 (MPEG-1/2/2.5 Layer III) audio.
62    Mp3 = 6,
63    /// Vorbis audio.
64    Vorbis = 7,
65    /// `WebVTT` subtitle.
66    WebVtt = 8,
67    /// Tx3g timed text subtitle.
68    Tx3g = 9,
69    /// Uncompressed / raw video (capture, passthrough).
70    RawVideo = 10,
71    /// Uncompressed / raw PCM audio — the audio analog of [`CodecKind::RawVideo`].
72    /// Covers both capture/passthrough (no container) and container-framed PCM
73    /// (e.g. RIFF/WAVE `data` chunk) — PCM has no encoding to distinguish either way.
74    RawAudio = 11,
75    /// VP8 video.
76    Vp8 = 12,
77}
78
79impl CodecKind {
80    /// Whether this codec kind produces/consumes video frames (has geometry).
81    #[must_use]
82    pub const fn is_video(self) -> bool {
83        matches!(
84            self,
85            Self::H264 | Self::Hevc | Self::Av1 | Self::Vp9 | Self::Vp8 | Self::RawVideo
86        )
87    }
88}
89
90/// Pixel dimensions of a video track.
91#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
92pub struct VideoGeometry {
93    /// Width in pixels.
94    pub width: u32,
95    /// Height in pixels.
96    pub height: u32,
97}
98
99/// Description of a media stream/track.
100///
101/// `Video` and `Audio` are separate variants — rather than one shape with
102/// optional fields — so a track can never claim video dimensions it doesn't
103/// have, or omit dimensions it does: the invariant lives in the type, not in
104/// a convention callers must remember. `Audio` is also used for subtitle
105/// tracks (`WebVtt`/`Tx3g`) — there is no separate `Subtitle` variant yet;
106/// `sample_rate`/`channels` are `0` there (not applicable, not "silence").
107#[derive(Debug, Clone, PartialEq, Eq)]
108#[non_exhaustive]
109pub enum StreamInfo {
110    /// A video track — always carries pixel dimensions.
111    Video {
112        /// Track or stream index.
113        id: u32,
114        /// Codec kind of this track.
115        codec: CodecKind,
116        /// Timebase for timestamps in packets on this track.
117        time_base: Rational,
118        /// Pixel dimensions.
119        geometry: VideoGeometry,
120        /// Extra header data (e.g. AVCC / extradata).
121        extra_data: Bytes,
122    },
123    /// A non-video track (audio, or subtitle until a dedicated variant
124    /// exists) — no geometry.
125    Audio {
126        /// Track or stream index.
127        id: u32,
128        /// Codec kind of this track.
129        codec: CodecKind,
130        /// Timebase for timestamps in packets on this track.
131        time_base: Rational,
132        /// Extra header data (e.g. AVCC / extradata).
133        extra_data: Bytes,
134        /// Sample rate in Hz. `0` when unknown or not applicable (e.g. a
135        /// subtitle track, or a source that doesn't carry this yet).
136        sample_rate: u32,
137        /// Channel count. `0` when unknown or not applicable.
138        channels: u16,
139    },
140}
141
142impl StreamInfo {
143    /// Track or stream index, regardless of kind.
144    #[must_use]
145    pub const fn id(&self) -> u32 {
146        match self {
147            Self::Video { id, .. } | Self::Audio { id, .. } => *id,
148        }
149    }
150
151    /// Return a copy with a different track id (e.g. renumbering before mux registration).
152    #[must_use]
153    pub fn with_id(self, id: u32) -> Self {
154        match self {
155            Self::Video {
156                codec,
157                time_base,
158                geometry,
159                extra_data,
160                ..
161            } => Self::Video {
162                id,
163                codec,
164                time_base,
165                geometry,
166                extra_data,
167            },
168            Self::Audio {
169                codec,
170                time_base,
171                extra_data,
172                sample_rate,
173                channels,
174                ..
175            } => Self::Audio {
176                id,
177                codec,
178                time_base,
179                extra_data,
180                sample_rate,
181                channels,
182            },
183        }
184    }
185
186    /// Codec kind of this track.
187    #[must_use]
188    pub const fn codec(&self) -> CodecKind {
189        match self {
190            Self::Video { codec, .. } | Self::Audio { codec, .. } => *codec,
191        }
192    }
193
194    /// Timebase for timestamps in packets on this track.
195    #[must_use]
196    pub const fn time_base(&self) -> Rational {
197        match self {
198            Self::Video { time_base, .. } | Self::Audio { time_base, .. } => *time_base,
199        }
200    }
201
202    /// Extra header data (e.g. AVCC / extradata).
203    #[must_use]
204    pub const fn extra_data(&self) -> &Bytes {
205        match self {
206            Self::Video { extra_data, .. } | Self::Audio { extra_data, .. } => extra_data,
207        }
208    }
209
210    /// Pixel dimensions, if this is a video track.
211    #[must_use]
212    pub const fn geometry(&self) -> Option<VideoGeometry> {
213        match self {
214            Self::Video { geometry, .. } => Some(*geometry),
215            Self::Audio { .. } => None,
216        }
217    }
218
219    /// Sample rate in Hz, if this is a non-video track. `Some(0)` means
220    /// unknown/not applicable, not "silence" — see [`Self::Audio`].
221    #[must_use]
222    pub const fn sample_rate(&self) -> Option<u32> {
223        match self {
224            Self::Audio { sample_rate, .. } => Some(*sample_rate),
225            Self::Video { .. } => None,
226        }
227    }
228
229    /// Channel count, if this is a non-video track. `Some(0)` means
230    /// unknown/not applicable — see [`Self::Audio`].
231    #[must_use]
232    pub const fn channels(&self) -> Option<u16> {
233        match self {
234            Self::Audio { channels, .. } => Some(*channels),
235            Self::Video { .. } => None,
236        }
237    }
238}
239
240/// Elementary compressed packet passed between demuxers, decoders, encoders, and muxers.
241///
242/// `payload` uses [`Bytes`] so clones are reference-counted (cheap) instead of full
243/// bitstream copies on hot paths.
244#[derive(Debug, Clone, PartialEq, Eq)]
245pub struct Packet {
246    /// Stream / track ID this packet belongs to.
247    pub stream_id: u32,
248    /// Presentation timestamp unit count (may be negative after edit-list remap).
249    pub pts: i64,
250    /// Decode timestamp unit count (may be negative after edit-list remap).
251    pub dts: i64,
252    /// Duration of the packet in timestamp units.
253    pub duration: u64,
254    /// Whether this packet represents a keyframe / random access point.
255    pub is_keyframe: bool,
256    /// Outside the active edit window (decode dependency / padding). Decoders may skip.
257    pub is_discard: bool,
258    /// Compressed bitstream payload bytes.
259    pub payload: Bytes,
260}
261
262#[cfg(test)]
263#[path = "lib_tests.rs"]
264mod tests;