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::{SegmentBuilder, SegmentMode, VideoCodecId, Writer};
15//!
16//! let file = File::open("./my-cool-file.webm").unwrap();
17//! let writer = Writer::new(file);
18//!
19//! // Build a segment with a single video track
20//! let builder = SegmentBuilder::new(writer).unwrap();
21//! let builder = builder.set_mode(SegmentMode::Live).unwrap(); // Set live mode for streaming
22//! let (builder, video_track) = builder.add_video_track(640, 480, VideoCodecId::VP8, None).unwrap();
23//! let mut segment = builder.build();
24//!
25//! // Add some video frames
26//! let encoded_video_frame: &[u8] = &[]; // TODO: Your video data here
27//! let timestamp_ns = 0;
28//! let is_keyframe = true;
29//! segment.add_frame(video_track, encoded_video_frame, timestamp_ns, is_keyframe).unwrap();
30//! // TODO: More video frames
31//!
32//! // Done writing frames, finish off the file
33//! _ = segment.finalize(None).inspect_err(|_| eprintln!("Could not finalize WebM file"));
34//! ```
35
36use webm_sys as ffi;
37
38pub mod mux {
39    mod segment;
40    mod writer;
41
42    pub use {
43        crate::ffi::mux::TrackNum,
44        segment::{Segment, SegmentBuilder},
45        writer::Writer,
46    };
47
48    use crate::ffi;
49    use std::num::NonZeroU64;
50
51    /// This is a copyable handle equivalent to a track number
52    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
53    pub struct VideoTrack(NonZeroU64);
54
55    impl From<VideoTrack> for TrackNum {
56        #[inline]
57        fn from(track: VideoTrack) -> Self {
58            track.0.get()
59        }
60    }
61
62    /// This is a copyable handle equivalent to a track number
63    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
64    pub struct AudioTrack(NonZeroU64);
65
66    impl From<AudioTrack> for TrackNum {
67        #[inline]
68        fn from(track: AudioTrack) -> Self {
69            track.0.get()
70        }
71    }
72
73    pub trait Track {
74        #[must_use]
75        fn is_audio(&self) -> bool {
76            false
77        }
78
79        #[must_use]
80        fn is_video(&self) -> bool {
81            false
82        }
83
84        #[must_use]
85        fn track_number(&self) -> TrackNum;
86    }
87
88    impl Track for VideoTrack {
89        #[inline]
90        fn is_video(&self) -> bool {
91            true
92        }
93
94        #[inline]
95        fn track_number(&self) -> TrackNum {
96            self.0.get()
97        }
98    }
99
100    impl Track for AudioTrack {
101        #[inline]
102        fn is_audio(&self) -> bool {
103            true
104        }
105
106        #[inline]
107        fn track_number(&self) -> TrackNum {
108            self.0.get()
109        }
110    }
111
112    #[derive(Eq, PartialEq, Clone, Copy, Debug)]
113    #[repr(u32)]
114    pub enum AudioCodecId {
115        Opus = ffi::mux::OPUS_CODEC_ID,
116        Vorbis = ffi::mux::VORBIS_CODEC_ID,
117    }
118
119    impl AudioCodecId {
120        const fn get_id(self) -> u32 {
121            self as u32
122        }
123    }
124
125    #[derive(Eq, PartialEq, Clone, Copy, Debug)]
126    #[repr(u32)]
127    pub enum VideoCodecId {
128        VP8 = ffi::mux::VP8_CODEC_ID,
129        VP9 = ffi::mux::VP9_CODEC_ID,
130        AV1 = ffi::mux::AV1_CODEC_ID,
131    }
132
133    impl VideoCodecId {
134        const fn get_id(self) -> u32 {
135            self as u32
136        }
137    }
138
139    /// The error type for this entire crate. More specific error types will
140    /// be added in the future, hence the current marking as non-exhaustive.
141    #[derive(Debug)]
142    #[non_exhaustive]
143    pub enum Error {
144        /// An parameter with an invalid value was passed to a method.
145        BadParam,
146
147        /// An unknown error occurred. While this is typically the result of
148        /// incorrect parameters to methods, an internal error in libwebm is
149        /// also possible.
150        Unknown,
151    }
152
153    impl std::fmt::Display for Error {
154        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155            match self {
156                Self::BadParam => f.write_str("Bad parameter"),
157                Self::Unknown => f.write_str("Unknown error"),
158            }
159        }
160    }
161
162    impl std::error::Error for Error {}
163
164    /// A specification for how pixels in written video frames are subsampled in chroma channels.
165    ///
166    /// Certain video frame formats (e.g. YUV 4:2:0) have a lower resolution in chroma (Cr/Cb) channels than the
167    /// luminance channel. This structure informs video players how that subsampling is done, using a number of
168    /// subsampling factors. A factor of zero means no subsampling, and a factor of one means that particular dimension
169    /// is half resolution.
170    ///
171    /// You may use [`ColorSubsampling::default()`] to get a specification of no subsampling in any dimension.
172    #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
173    pub struct ColorSubsampling {
174        /// The subsampling factor for both chroma channels in the horizontal direction.
175        pub chroma_horizontal: u8,
176
177        /// The subsampling factor for both chroma channels in the vertical direction.
178        pub chroma_vertical: u8,
179    }
180
181    /// A specification of how the range of colors in the input video frames has been clipped.
182    ///
183    /// Certain screens struggle with the full range of available colors, and video content is thus sometimes tuned to
184    /// a restricted range.
185    #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
186    pub enum ColorRange {
187        /// No claim is made as to how colors have been restricted.
188        #[default]
189        Unspecified = 0,
190
191        /// Color values are restricted to a "broadcast-safe" range.
192        Broadcast = 1,
193
194        /// No color clipping is performed.
195        Full = 2,
196    }
197
198    /// A specification for the segment writing mode.
199    /// 
200    /// This controls how the segment is written and affects features like seeking.
201    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
202    pub enum SegmentMode {
203        /// Live mode - optimized for real-time streaming.
204        /// In this mode, seeking information may not be available.
205        Live,
206        
207        /// File mode - optimized for file-based playback.
208        /// This enables full seeking and duration information.
209        File,
210    }
211}