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;