webm/lib.rs
1//! A crate for muxing one or more video/audio streams into a `WebM` file.
2//!
3//! Note that this crate is only for muxing media that has already been encoded with the appropriate codec.
4//! Consider a crate such as `vpx` if you need encoding as well.
5//!
6//! Actual writing of muxed data is done through a [`mux::Writer`], which lets you supply your own implementation.
7//! This makes it easy to support muxing to files, in-memory buffers, or whatever else you need. Once you have
8//! a [`mux::Writer`], you create a [`mux::SegmentBuilder`] and add the tracks you need. Finally, you create a
9//! [`mux::Segment`] with that builder, to which you can add media frames.
10//!
11//! In typical usage of this library, where you might mux to a `WebM` file, you would do:
12//! ```no_run
13//! use std::fs::File;
14//! use webm::mux::{Error, SegmentBuilder, SegmentMode, VideoCodecId, Writer};
15//!
16//! fn main() -> Result<(), Error> {
17//! let file = File::create("./my-cool-file.webm").map_err(|_| Error::Unknown)?;
18//! let writer = Writer::new(file);
19//!
20//! // Build a segment with a single video track
21//! let builder = SegmentBuilder::new(writer)?;
22//! let builder = builder.set_mode(SegmentMode::Live)?; // Set live mode for streaming
23//! let (builder, video_track) = builder.add_video_track(640, 480, VideoCodecId::VP8, None)?;
24//! let mut segment = builder.build();
25//!
26//! // Add some video frames
27//! let encoded_video_frame: &[u8] = &[]; // TODO: Your video data here
28//! let timestamp_ns = 0;
29//! let is_keyframe = true;
30//! segment.add_frame(video_track, encoded_video_frame, timestamp_ns, is_keyframe)?;
31//! // TODO: More video frames
32//!
33//! // Done writing frames, finish off the file
34//! segment.finalize(None).map_err(|_| Error::Unknown)?;
35//! Ok(())
36//! }
37//! ```
38
39use webm_sys as ffi;
40
41pub mod mux {
42 mod segment;
43 mod writer;
44
45 pub use crate::ffi::mux::TrackNum;
46 pub use segment::{Segment, SegmentBuilder};
47 pub use writer::Writer;
48
49 use crate::ffi;
50 use std::num::NonZeroU64;
51
52 /// This is a copyable handle equivalent to a track number
53 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
54 pub struct VideoTrack(NonZeroU64);
55
56 impl From<VideoTrack> for TrackNum {
57 #[inline]
58 fn from(track: VideoTrack) -> Self {
59 track.0.get()
60 }
61 }
62
63 /// This is a copyable handle equivalent to a track number
64 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
65 pub struct AudioTrack(NonZeroU64);
66
67 impl From<AudioTrack> for TrackNum {
68 #[inline]
69 fn from(track: AudioTrack) -> Self {
70 track.0.get()
71 }
72 }
73
74 pub trait Track {
75 #[must_use]
76 fn is_audio(&self) -> bool {
77 false
78 }
79
80 #[must_use]
81 fn is_video(&self) -> bool {
82 false
83 }
84
85 #[must_use]
86 fn track_number(&self) -> TrackNum;
87 }
88
89 impl Track for VideoTrack {
90 #[inline]
91 fn is_video(&self) -> bool {
92 true
93 }
94
95 #[inline]
96 fn track_number(&self) -> TrackNum {
97 self.0.get()
98 }
99 }
100
101 impl Track for AudioTrack {
102 #[inline]
103 fn is_audio(&self) -> bool {
104 true
105 }
106
107 #[inline]
108 fn track_number(&self) -> TrackNum {
109 self.0.get()
110 }
111 }
112
113 #[derive(Eq, PartialEq, Clone, Copy, Debug)]
114 #[repr(u32)]
115 pub enum AudioCodecId {
116 Opus = ffi::mux::OPUS_CODEC_ID,
117 Vorbis = ffi::mux::VORBIS_CODEC_ID,
118 }
119
120 impl AudioCodecId {
121 const fn get_id(self) -> u32 {
122 self as u32
123 }
124 }
125
126 #[derive(Eq, PartialEq, Clone, Copy, Debug)]
127 #[repr(u32)]
128 pub enum VideoCodecId {
129 VP8 = ffi::mux::VP8_CODEC_ID,
130 VP9 = ffi::mux::VP9_CODEC_ID,
131 AV1 = ffi::mux::AV1_CODEC_ID,
132 }
133
134 impl VideoCodecId {
135 const fn get_id(self) -> u32 {
136 self as u32
137 }
138 }
139
140 /// The error type for this entire crate. More specific error types will
141 /// be added in the future, hence the current marking as non-exhaustive.
142 #[derive(Debug)]
143 #[non_exhaustive]
144 pub enum Error {
145 /// An parameter with an invalid value was passed to a method.
146 BadParam,
147
148 /// An unknown error occurred. While this is typically the result of
149 /// incorrect parameters to methods, an internal error in libwebm is
150 /// also possible.
151 Unknown,
152 }
153
154 impl Error {
155 pub(crate) fn check_code(code: ffi::mux::ResultCode) -> Result<(), Self> {
156 match code {
157 ffi::mux::ResultCode::Ok => Ok(()),
158 ffi::mux::ResultCode::BadParam => Err(Self::BadParam),
159 ffi::mux::ResultCode::UnknownLibwebmError => Err(Self::Unknown),
160 }
161 }
162 }
163
164 impl std::fmt::Display for Error {
165 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166 match self {
167 Self::BadParam => f.write_str("Bad parameter"),
168 Self::Unknown => f.write_str("Unknown error"),
169 }
170 }
171 }
172
173 impl std::error::Error for Error {}
174
175 /// A specification for how pixels in written video frames are subsampled in chroma channels.
176 ///
177 /// Certain video frame formats (e.g. YUV 4:2:0) have a lower resolution in chroma (Cr/Cb) channels than the
178 /// luminance channel. This structure informs video players how that subsampling is done, using a number of
179 /// subsampling factors. A factor of zero means no subsampling, and a factor of one means that particular dimension
180 /// is half resolution.
181 ///
182 /// You may use [`ColorSubsampling::default()`] to get a specification of no subsampling in any dimension.
183 #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
184 pub struct ColorSubsampling {
185 /// The subsampling factor for both chroma channels in the horizontal direction.
186 pub chroma_horizontal: u8,
187
188 /// The subsampling factor for both chroma channels in the vertical direction.
189 pub chroma_vertical: u8,
190 }
191
192 /// A specification of how the range of colors in the input video frames has been clipped.
193 ///
194 /// Certain screens struggle with the full range of available colors, and video content is thus sometimes tuned to
195 /// a restricted range.
196 #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
197 pub enum ColorRange {
198 /// No claim is made as to how colors have been restricted.
199 #[default]
200 Unspecified = 0,
201
202 /// Color values are restricted to a "broadcast-safe" range.
203 Broadcast = 1,
204
205 /// No color clipping is performed.
206 Full = 2,
207 }
208
209 /// A specification for the segment writing mode.
210 ///
211 /// This controls how the segment is written and affects features like seeking.
212 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
213 pub enum SegmentMode {
214 /// Live mode - optimized for real-time streaming.
215 /// In this mode, seeking information may not be available.
216 Live,
217
218 /// File mode - optimized for file-based playback.
219 /// This enables full seeking and duration information.
220 File,
221 }
222
223 /// Transfer characteristics (EOTF - Electro-Optical Transfer Function).
224 ///
225 /// Specifies how the video signal values relate to light output.
226 /// See ITU-T H.273 / ISO/IEC 23091-2.
227 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
228 #[repr(u64)]
229 pub enum TransferCharacteristics {
230 /// Rec. ITU-R BT.709
231 Bt709 = 1,
232 /// Unspecified
233 Unspecified = 2,
234 /// Rec. ITU-R BT.470-6 System M
235 Bt470M = 4,
236 /// Rec. ITU-R BT.470-6 System B, G
237 Bt470Bg = 5,
238 /// Rec. ITU-R BT.601-7 525 or 625
239 Bt601 = 6,
240 /// SMPTE ST 240
241 Smpte240M = 7,
242 /// Linear transfer characteristics
243 Linear = 8,
244 /// Logarithmic transfer (100:1 range)
245 Log100 = 9,
246 /// Logarithmic transfer (316.22777:1 range)
247 Log316 = 10,
248 /// IEC 61966-2-4
249 Iec61966_2_4 = 11,
250 /// Rec. ITU-R BT.1361-0 extended colour gamut system
251 Bt1361 = 12,
252 /// IEC 61966-2-1 sRGB
253 Iec61966_2_1 = 13,
254 /// Rec. ITU-R BT.2020-2 (10-bit system)
255 Bt2020_10bit = 14,
256 /// Rec. ITU-R BT.2020-2 (12-bit system)
257 Bt2020_12bit = 15,
258 /// SMPTE ST 2084 - Perceptual Quantizer (PQ) for HDR10
259 Smpte2084 = 16,
260 /// SMPTE ST 428-1
261 Smpte428 = 17,
262 /// ARIB STD-B67 - Hybrid Log-Gamma (HLG)
263 AribStdB67 = 18,
264 }
265
266 /// Color primaries specification.
267 ///
268 /// Defines the chromaticity coordinates of the source primaries.
269 /// See ITU-T H.273 / ISO/IEC 23091-2.
270 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
271 #[repr(u64)]
272 pub enum ColorPrimaries {
273 /// Rec. ITU-R BT.709
274 Bt709 = 1,
275 /// Unspecified
276 Unspecified = 2,
277 /// Rec. ITU-R BT.470-6 System M
278 Bt470M = 4,
279 /// Rec. ITU-R BT.470-6 System B, G
280 Bt470Bg = 5,
281 /// Rec. ITU-R BT.601-7 525 or 625
282 Bt601 = 6,
283 /// SMPTE ST 240
284 Smpte240M = 7,
285 /// Generic film (colour filters using Illuminant C)
286 Film = 8,
287 /// Rec. ITU-R BT.2020 / BT.2100 - Wide color gamut for HDR
288 Bt2020 = 9,
289 /// SMPTE ST 428-1 (CIE 1931 XYZ)
290 Smpte428 = 10,
291 /// SMPTE RP 431-2 - DCI P3
292 Smpte431 = 11,
293 /// SMPTE EG 432-1 - Display P3
294 Smpte432 = 12,
295 /// EBU Tech. 3213-E
296 Ebu3213 = 22,
297 }
298
299 /// Matrix coefficients for deriving luma and chroma from RGB.
300 ///
301 /// See ITU-T H.273 / ISO/IEC 23091-2.
302 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
303 #[repr(u64)]
304 pub enum MatrixCoefficients {
305 /// Identity matrix (RGB)
306 Identity = 0,
307 /// Rec. ITU-R BT.709
308 Bt709 = 1,
309 /// Unspecified
310 Unspecified = 2,
311 /// FCC
312 Fcc = 4,
313 /// Rec. ITU-R BT.470-6 System B, G
314 Bt470Bg = 5,
315 /// Rec. ITU-R BT.601-7 525 or 625
316 Bt601 = 6,
317 /// SMPTE ST 240
318 Smpte240M = 7,
319 /// `YCgCo`
320 YCgCo = 8,
321 /// Rec. ITU-R BT.2020-2 non-constant luminance
322 Bt2020Ncl = 9,
323 /// Rec. ITU-R BT.2020-2 constant luminance
324 Bt2020Cl = 10,
325 /// SMPTE ST 2085
326 Smpte2085 = 11,
327 /// Chromaticity-derived non-constant luminance
328 ChromaNcl = 12,
329 /// Chromaticity-derived constant luminance
330 ChromaCl = 13,
331 /// `ICtCp` (Rec. ITU-R BT.2100-0)
332 ICtCp = 14,
333 }
334
335 /// Chromaticity coordinates (CIE 1931 xy).
336 ///
337 /// Values should be in the range 0.0 to 1.0.
338 #[derive(Debug, Clone, Copy, PartialEq)]
339 pub struct Chromaticity {
340 pub x: f32,
341 pub y: f32,
342 }
343
344 impl Chromaticity {
345 /// D65 white point (standard for BT.709, BT.2020)
346 pub const D65: Self = Self {
347 x: 0.3127,
348 y: 0.3290,
349 };
350 }
351
352 /// Display primaries for HDR mastering metadata.
353 #[derive(Debug, Clone, Copy, PartialEq)]
354 pub struct DisplayPrimaries {
355 pub red: Chromaticity,
356 pub green: Chromaticity,
357 pub blue: Chromaticity,
358 }
359
360 impl DisplayPrimaries {
361 /// Rec. ITU-R BT.709 primaries (SDR)
362 pub const BT_709: Self = Self {
363 red: Chromaticity { x: 0.64, y: 0.33 },
364 green: Chromaticity { x: 0.30, y: 0.60 },
365 blue: Chromaticity { x: 0.15, y: 0.06 },
366 };
367
368 /// Rec. ITU-R BT.2020 primaries (HDR/WCG)
369 pub const BT_2020: Self = Self {
370 red: Chromaticity { x: 0.708, y: 0.292 },
371 green: Chromaticity { x: 0.170, y: 0.797 },
372 blue: Chromaticity { x: 0.131, y: 0.046 },
373 };
374
375 /// DCI-P3 primaries
376 pub const DCI_P3: Self = Self {
377 red: Chromaticity { x: 0.680, y: 0.320 },
378 green: Chromaticity { x: 0.265, y: 0.690 },
379 blue: Chromaticity { x: 0.150, y: 0.060 },
380 };
381 }
382
383 /// SMPTE ST 2086 mastering display metadata.
384 ///
385 /// Specifies the color volume and luminance range of the display used for mastering HDR content.
386 #[derive(Debug, Clone, Copy, PartialEq)]
387 pub struct MasteringDisplayMetadata {
388 /// Maximum luminance in candelas per square meter (cd/m² or nits).
389 /// Typical values: 1000.0, 4000.0, 10000.0
390 pub luminance_max: f32,
391
392 /// Minimum luminance in candelas per square meter (cd/m² or nits).
393 /// Typical values: 0.0001, 0.001, 0.01, 0.05
394 pub luminance_min: f32,
395
396 /// Display primaries (red, green, blue chromaticity coordinates)
397 pub primaries: DisplayPrimaries,
398
399 /// White point chromaticity coordinates
400 pub white_point: Chromaticity,
401 }
402
403 /// HDR10 static metadata.
404 ///
405 /// Includes both content light level metadata and mastering display metadata.
406 #[derive(Debug, Clone, Copy, PartialEq)]
407 pub struct HdrMetadata {
408 /// Maximum Content Light Level in cd/m² (nits).
409 /// The maximum light level of any single pixel in the entire video.
410 /// Typical range: 1000-4000 nits.
411 pub max_cll: u64,
412
413 /// Maximum Frame-Average Light Level in cd/m² (nits).
414 /// The maximum average light level of any single frame in the video.
415 /// Typical range: 100-1000 nits.
416 pub max_fall: u64,
417
418 /// Optional SMPTE ST 2086 mastering metadata.
419 pub mastering_metadata: Option<MasteringDisplayMetadata>,
420 }
421}