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}