1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
//! Codec traits for video encoding and decoding.
use crate::{CodecResult, VideoFrame};
use oximedia_core::{CodecId, PixelFormat, Rational};
/// Video decoder trait.
///
/// Implements a push-pull decoding model:
/// 1. Send compressed packets with [`send_packet`](VideoDecoder::send_packet)
/// 2. Receive decoded frames with [`receive_frame`](VideoDecoder::receive_frame)
///
/// # Example
///
/// ```ignore
/// while let Some(packet) = demuxer.read_packet()? {
/// decoder.send_packet(&packet)?;
/// while let Some(frame) = decoder.receive_frame()? {
/// process_frame(frame);
/// }
/// }
/// decoder.flush()?;
/// while let Some(frame) = decoder.receive_frame()? {
/// process_frame(frame);
/// }
/// ```
pub trait VideoDecoder: Send {
/// Get codec identifier.
fn codec(&self) -> CodecId;
/// Send a compressed packet to the decoder.
///
/// # Errors
///
/// Returns error if the packet is invalid or decoder is in error state.
fn send_packet(&mut self, data: &[u8], pts: i64) -> CodecResult<()>;
/// Receive a decoded frame.
///
/// Returns `Ok(None)` if more data is needed.
///
/// # Errors
///
/// Returns error if decoding fails.
fn receive_frame(&mut self) -> CodecResult<Option<VideoFrame>>;
/// Flush the decoder.
///
/// Call this after all packets have been sent to retrieve remaining frames.
///
/// # Errors
///
/// Returns error if flush fails.
fn flush(&mut self) -> CodecResult<()>;
/// Reset the decoder state.
fn reset(&mut self);
/// Get decoder output format.
fn output_format(&self) -> Option<PixelFormat>;
/// Get decoded frame dimensions.
fn dimensions(&self) -> Option<(u32, u32)>;
}
/// Video encoder trait.
///
/// Implements a push-pull encoding model:
/// 1. Send raw frames with [`send_frame`](VideoEncoder::send_frame)
/// 2. Receive compressed packets with [`receive_packet`](VideoEncoder::receive_packet)
pub trait VideoEncoder: Send {
/// Get codec identifier.
fn codec(&self) -> CodecId;
/// Send a raw frame to the encoder.
///
/// # Errors
///
/// Returns error if the frame format is invalid.
fn send_frame(&mut self, frame: &VideoFrame) -> CodecResult<()>;
/// Receive an encoded packet.
///
/// Returns `Ok(None)` if more frames are needed.
///
/// # Errors
///
/// Returns error if encoding fails.
fn receive_packet(&mut self) -> CodecResult<Option<EncodedPacket>>;
/// Flush the encoder.
///
/// Call this after all frames have been sent to retrieve remaining packets.
///
/// # Errors
///
/// Returns error if flush fails.
fn flush(&mut self) -> CodecResult<()>;
/// Get encoder configuration.
fn config(&self) -> &EncoderConfig;
}
/// Encoded packet output from encoder.
#[derive(Clone, Debug)]
pub struct EncodedPacket {
/// Compressed data.
pub data: Vec<u8>,
/// Presentation timestamp.
pub pts: i64,
/// Decode timestamp.
pub dts: i64,
/// Is keyframe.
pub keyframe: bool,
/// Duration in timebase units.
pub duration: Option<i64>,
}
/// Decoder configuration.
#[derive(Clone, Debug)]
pub struct DecoderConfig {
/// Codec to decode.
pub codec: CodecId,
/// Extra data (codec-specific).
pub extradata: Option<Vec<u8>>,
/// Number of decoder threads (0 = auto).
pub threads: usize,
/// Enable low-latency mode.
pub low_latency: bool,
}
impl Default for DecoderConfig {
fn default() -> Self {
Self {
codec: CodecId::Av1,
extradata: None,
threads: 0,
low_latency: false,
}
}
}
/// Encoder configuration.
#[derive(Clone, Debug)]
pub struct EncoderConfig {
/// Target codec.
pub codec: CodecId,
/// Frame width.
pub width: u32,
/// Frame height.
pub height: u32,
/// Input pixel format.
pub pixel_format: PixelFormat,
/// Frame rate.
pub framerate: Rational,
/// Bitrate mode.
pub bitrate: BitrateMode,
/// Encoder preset.
pub preset: EncoderPreset,
/// Profile (codec-specific).
pub profile: Option<String>,
/// Keyframe interval.
pub keyint: u32,
/// Number of encoder threads (0 = auto).
pub threads: usize,
/// Timebase for output packets.
pub timebase: Rational,
}
impl Default for EncoderConfig {
fn default() -> Self {
Self {
codec: CodecId::Av1,
width: 1920,
height: 1080,
pixel_format: PixelFormat::Yuv420p,
framerate: Rational::new(30, 1),
bitrate: BitrateMode::Crf(28.0),
preset: EncoderPreset::Medium,
profile: None,
keyint: 250,
threads: 0,
timebase: Rational::new(1, 1000),
}
}
}
impl EncoderConfig {
/// Create AV1 encoder config.
#[must_use]
pub fn av1(width: u32, height: u32) -> Self {
Self {
codec: CodecId::Av1,
width,
height,
..Default::default()
}
}
/// Create VP9 encoder config.
#[must_use]
pub fn vp9(width: u32, height: u32) -> Self {
Self {
codec: CodecId::Vp9,
width,
height,
..Default::default()
}
}
/// Create VP8 encoder config.
#[must_use]
pub fn vp8(width: u32, height: u32) -> Self {
Self {
codec: CodecId::Vp8,
width,
height,
..Default::default()
}
}
/// Set CRF quality.
#[must_use]
pub fn with_crf(mut self, crf: f32) -> Self {
self.bitrate = BitrateMode::Crf(crf);
self
}
/// Set target bitrate.
#[must_use]
pub fn with_bitrate(mut self, bitrate: u64) -> Self {
self.bitrate = BitrateMode::Cbr(bitrate);
self
}
/// Set encoder preset.
#[must_use]
pub fn with_preset(mut self, preset: EncoderPreset) -> Self {
self.preset = preset;
self
}
}
/// Bitrate control mode.
#[derive(Clone, Debug)]
pub enum BitrateMode {
/// Constant bitrate (bits/sec).
Cbr(u64),
/// Variable bitrate with target and maximum.
Vbr {
/// Target bitrate.
target: u64,
/// Maximum bitrate.
max: u64,
},
/// Constant quality (CRF value, lower = better).
Crf(f32),
/// Lossless encoding.
Lossless,
}
/// Encoder preset (speed vs quality tradeoff).
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum EncoderPreset {
/// Fastest encoding, lowest quality.
Ultrafast,
/// Very fast encoding.
Superfast,
/// Fast encoding.
Veryfast,
/// Faster than medium.
Faster,
/// Fast encoding.
Fast,
/// Balanced speed and quality.
#[default]
Medium,
/// Slower, better quality.
Slow,
/// Even slower, better quality.
Slower,
/// Very slow, high quality.
Veryslow,
/// Maximum quality, slowest.
Placebo,
}
impl EncoderPreset {
/// Get speed value (0-10, 0 = slowest).
#[must_use]
pub fn speed(&self) -> u8 {
match self {
Self::Ultrafast => 10,
Self::Superfast => 9,
Self::Veryfast => 8,
Self::Faster => 7,
Self::Fast => 6,
Self::Medium => 5,
Self::Slow => 4,
Self::Slower => 3,
Self::Veryslow => 2,
Self::Placebo => 0,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encoder_config_builder() {
let config = EncoderConfig::av1(1920, 1080)
.with_crf(24.0)
.with_preset(EncoderPreset::Slow);
assert_eq!(config.width, 1920);
assert_eq!(config.height, 1080);
assert_eq!(config.preset, EncoderPreset::Slow);
assert!(
matches!(config.bitrate, BitrateMode::Crf(crf) if (crf - 24.0).abs() < f32::EPSILON)
);
}
#[test]
fn test_preset_speed() {
assert_eq!(EncoderPreset::Ultrafast.speed(), 10);
assert_eq!(EncoderPreset::Medium.speed(), 5);
assert_eq!(EncoderPreset::Placebo.speed(), 0);
}
}