vpx_rs/enc.rs
1/*
2 * Copyright (c) 2025, Saso Kiselkov. All rights reserved.
3 *
4 * Use of this source code is governed by the 2-clause BSD license,
5 * which can be found in a file named LICENSE, located at the root
6 * of this source tree.
7 */
8
9use bitflags::bitflags;
10use std::ffi::c_void;
11use std::fmt::Debug;
12use std::mem::MaybeUninit;
13use std::num::NonZero;
14use std::time::Duration;
15use vpx_sys::vpx_codec_enc_cfg;
16
17use crate::common::StreamInfo;
18use crate::image::{YUVImageData, YUVPixelType};
19use crate::NoDebugWrapper;
20use crate::{call_vpx, call_vpx_ptr};
21use crate::{Error, Result};
22use ctrl::EncoderControlSet;
23
24/// Contains definitions for the various control options which can be
25/// passed to [`Encoder::codec_control_set()`].
26pub mod ctrl;
27
28#[allow(missing_docs)]
29/// Specifies the codec to be used for encoding.
30#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
31pub enum CodecId {
32 VP8,
33 VP9,
34}
35
36impl CodecId {
37 pub(crate) fn iface(&self) -> Result<*const vpx_sys::vpx_codec_iface_t> {
38 match self {
39 Self::VP8 => call_vpx_ptr!(
40 vpx_sys::vpx_codec_vp8_cx(),
41 Error::CodecInterfaceNotAvailable,
42 ),
43 Self::VP9 => call_vpx_ptr!(
44 vpx_sys::vpx_codec_vp9_cx(),
45 Error::CodecInterfaceNotAvailable,
46 ),
47 }
48 }
49}
50
51/// The main VP8/VP9 video encoder.
52///
53/// # Example
54/// The example below expects the input image in YUV 4:2:0 planar format.
55/// While the encoder supports a number of YUV format variants
56/// (see [`crate::image::ImageFormat`]), if you are working non-YUV formats
57/// (e.g. RGB), you will first have to convert your data to one of the YUV
58/// formats supported by the encoder. The
59/// [yuv](https://docs.rs/yuv/latest/yuv/#functions) crate
60/// contains many highly optimized pixel format conversion functions. See
61/// [`crate::ImageFormat`] for an example.
62///
63/// Note that the encoder below has most of its settings set to their default
64/// values for the codec. The [`EncoderConfig`] struct gives you a lot more
65/// more tuning options.
66///
67/// ```
68/// use std::num::NonZero;
69/// use vpx_rs::{
70/// Encoder, EncoderConfig, EncoderFrameFlags, Error, Packet,
71/// RateControl, Timebase,
72/// };
73///
74/// // Initializes a VP8 encoder instance.
75/// fn new_vp8_encoder(width: u32, height: u32) -> Result<Encoder<u8>, Error> {
76/// // Start by preparing the encoder configuration.
77/// let mut config = EncoderConfig::<u8>::new(
78/// vpx_rs::enc::CodecId::VP8,
79/// width,
80/// height,
81/// // assume a framerate of 30 fps, implying a 1/30 timebase
82/// Timebase {
83/// num: NonZero::new(1).unwrap(),
84/// den: NonZero::new(30).unwrap(),
85/// },
86/// RateControl::ConstantBitRate(8000),
87/// )?;
88/// // Construct the actual encoder and return the result.
89/// Encoder::new(config)
90/// }
91///
92/// // Encodes a single frame in YUV 4:2:0 planar format and sends the
93/// // compressed bitstream to a writer.
94/// fn encode_yuv420_frame<W: std::io::Write>(
95/// encoder: &mut Encoder<u8>,
96/// frame_counter: i64,
97/// width: u32,
98/// height: u32,
99/// yuv420_pixels: &[u8],
100/// output_stream: &mut W,
101/// ) -> Result<(), Box<dyn std::error::Error>> {
102/// // Wrap the input buffer in an ImageData structure, together
103/// // with information about the data format and dimensions.
104/// let image = vpx_rs::YUVImageData::<u8>::from_raw_data(
105/// vpx_rs::ImageFormat::I420,
106/// width as usize,
107/// height as usize,
108/// yuv420_pixels,
109/// )?;
110/// // Send the image to the encoder for compression.
111/// let packets = encoder.encode(
112/// frame_counter,
113/// 1,
114/// image,
115/// vpx_rs::EncodingDeadline::default(),
116/// EncoderFrameFlags::empty(),
117/// )?;
118/// // The encoder can spit out a series of various data packets.
119/// for packet in packets {
120/// match packet {
121/// // CompressedFrame packets contain the actual compressed
122/// // video bitstream, which we need to send to the output.
123/// Packet::CompressedFrame(frame) => {
124/// println!(
125/// "Encoder produced frame with {} bytes, pts: {}",
126/// frame.data.len(),
127/// frame.pts,
128/// );
129/// // send the compressed video data to the output
130/// output_stream.write(&frame.data)?;
131/// },
132/// // Remaining packet types contain various metainformation.
133/// // Most of these need to be enabled manually by calling
134/// // Encoder::codec_control_set() and setting various
135/// // advanced encoder options.
136/// _ => {
137/// println!(
138/// "Encoder also produced something else: {packet:?}",
139/// );
140/// }
141/// }
142/// }
143/// Ok(())
144/// }
145/// ```
146#[derive(Debug)]
147pub struct Encoder<T: YUVPixelType> {
148 ctx: NoDebugWrapper<vpx_sys::vpx_codec_ctx_t>,
149 input_pixel_type: std::marker::PhantomData<T>,
150}
151
152// This is safe to implement, because we never hold any libvpx mutexes
153// between function calls, and this crate is entirely sync code. Also,
154// we have no interior mutability through immutable references.
155unsafe impl<T: YUVPixelType> Send for Encoder<T> {}
156unsafe impl<T: YUVPixelType> Sync for Encoder<T> {}
157
158impl<T: YUVPixelType> Encoder<T> {
159 /// Constructs a new encoder.
160 pub fn new(config: EncoderConfig<T>) -> Result<Self> {
161 let (iface, cfg) = unsafe { config.cfg()? };
162 let mut ctx = unsafe {
163 MaybeUninit::<vpx_sys::vpx_codec_ctx_t>::zeroed().assume_init()
164 };
165 call_vpx!(
166 vpx_sys::vpx_codec_enc_init_ver(
167 &mut ctx,
168 iface,
169 &cfg,
170 config.flags.bits() as _,
171 vpx_sys::VPX_ENCODER_ABI_VERSION as _,
172 ),
173 Error::VpxCodecInitFailed,
174 )?;
175 let mut encoder = Self {
176 ctx: NoDebugWrapper(ctx),
177 input_pixel_type: std::marker::PhantomData,
178 };
179 match config.rate_control {
180 RateControl::ConstrainedQuality {
181 max_quality: qual, ..
182 }
183 | RateControl::ConstantQuality(qual) => {
184 encoder.codec_control_set(EncoderControlSet::CQLevel(qual))?;
185 }
186 RateControl::Lossless => match config.codec {
187 CodecId::VP8 => return Err(Error::UnsupportedRateControlMode),
188 CodecId::VP9 => {
189 encoder.codec_control_set(
190 EncoderControlSet::Vp9CodingMode(
191 ctrl::Vp9CodingMode::Lossless,
192 ),
193 )?;
194 }
195 },
196 _ => (),
197 }
198 Ok(encoder)
199 }
200 /// Encodes a frame of image data.
201 ///
202 /// - `pts`: The presentation timestamp of this frame. This should be
203 /// specified in the timebase units set up in the encoder config.
204 /// - `duration`: Duration to show frame, in timebase units.
205 /// - `image`: The image to be encoded.
206 /// - `deadline`: The encoding deadline to pass to the encoder.
207 ///
208 /// Returns an interator of stream packets which were generated from this
209 /// frame. Please note that the encoder need not generate new packets for
210 /// every frame passed in. It can buffer multiple frames and then emit
211 /// multiple stream packets at once. The `deadline` argument hints the
212 /// encoder how much it should trade off reduced latency for better
213 /// compression.
214 pub fn encode(
215 &mut self,
216 pts: i64,
217 duration: u64,
218 image: YUVImageData<T>,
219 deadline: EncodingDeadline,
220 flags: EncoderFrameFlags,
221 ) -> Result<PacketIterator> {
222 let wrapped = unsafe { image.vpx_img_wrap()? };
223 call_vpx!(
224 vpx_sys::vpx_codec_encode(
225 &mut self.ctx.0,
226 &wrapped,
227 pts as _,
228 duration as _,
229 flags.bits() as _,
230 deadline.into(),
231 ),
232 Error::ErrorEncodingImage,
233 )?;
234 Ok(PacketIterator {
235 ctx: &mut self.ctx.0,
236 iter: std::ptr::null(),
237 })
238 }
239 /// Returns information about the stream being produced.
240 pub fn stream_info(&mut self) -> Option<StreamInfo> {
241 crate::common::get_stream_info(&mut self.ctx.0)
242 }
243 /// Provides fine-grained codec control after initialization.
244 pub fn codec_control_set(
245 &mut self,
246 control: ctrl::EncoderControlSet,
247 ) -> Result<()> {
248 control.set(&mut self.ctx.0)
249 }
250}
251
252impl<T: YUVPixelType> Drop for Encoder<T> {
253 fn drop(&mut self) {
254 // We assert here, because the only documented ways for this to fail
255 // are when passing a null pointer (which we cannot do) and the context
256 // not being initialized (which by design cannot happen in Rust).
257 unsafe {
258 let error = vpx_sys::vpx_codec_destroy(&mut self.ctx.0);
259 assert_eq!(error, vpx_sys::VPX_CODEC_OK);
260 }
261 }
262}
263
264/// Specifies an initial configuration of the encoder. Fields prefixed
265/// with `rc_` denote rate-control parameters.
266#[derive(Clone, Debug, Eq, PartialEq, Hash)]
267pub struct EncoderConfig<T: YUVPixelType> {
268 /// The codec to utilize when producing a bitstream.
269 pub codec: CodecId,
270
271 /// Bitstream profile to use.
272 pub profile: EncoderProfile,
273
274 /// Flags to configure on the encoder.
275 pub flags: EncoderFlags,
276
277 /// The display width of the video stream in pixels.
278 pub width: u32,
279
280 /// The display height of the video stream in pixels.
281 pub height: u32,
282
283 /// The timebase for the presentation timestamps passed to the encoder.
284 pub timebase: Timebase,
285
286 /// The type of rate control to employ.
287 pub rate_control: RateControl,
288
289 /// Number of threads to employ in encoding.
290 pub threads: u32,
291
292 /// Bit-depth of the codec.
293 ///
294 /// This value identifies the bit_depth of the codec and the generated
295 /// output bitstream. It doesn't imply a specific input data bit depth
296 /// (which is defined by the associated generic type of this struct).
297 /// The default is `VPX_BITS_8`.
298 pub output_bit_depth: vpx_sys::vpx_bit_depth,
299
300 /// Bitstream error resiliency flags.
301 pub error_resilient: ErrorResilient,
302
303 /// Multi-pass encoding support:
304 /// - `VPX_RC_ONE_PASS`: Single-pass mode (default).
305 /// - `VPX_RC_FIRST_PASS`: First pass of multi-pass mode.
306 /// - `VPX_RC_LAST_PASS`: Final pass of multi-pass mode.
307 pub pass: vpx_sys::vpx_enc_pass,
308
309 /// Allow lagged encoding
310 ///
311 /// If set, this value allows the encoder to consume a number of input
312 /// frames before producing output frames. This allows the encoder to
313 /// base decisions for the current frame on future frames. This does
314 /// increase the latency of the encoding pipeline, so it is not
315 /// appropriate in all situations (ex: realtime encoding).
316 ///
317 /// Note that this is a maximum value -- the encoder may produce frames
318 /// sooner than the given limit. Set this value to 0 to disable this
319 /// feature (this is the default).
320 pub lag_in_frames: u32,
321
322 /// Temporal resampling configuration, if supported by the codec.
323 ///
324 /// Temporal resampling allows the codec to "drop" frames as a strategy to
325 /// meet its target data rate. This can cause temporal discontinuities in
326 /// the encoded video, which may appear as stuttering during playback.
327 /// This trade-off is often acceptable, but for many applications is not.
328 /// It can be disabled in these cases.
329 ///
330 /// This threshold is described as a percentage of the target data buffer.
331 /// When the data buffer falls below this percentage of fullness, a
332 /// dropped frame is indicated. Set the threshold to zero (0) to disable
333 /// this feature.
334 pub rc_dropframe_thresh: u32,
335
336 /// Enable/disable spatial resampling, if supported by the codec.
337 ///
338 /// Spatial resampling allows the codec to compress a lower resolution
339 /// version of the frame, which is then upscaled by the encoder to the
340 /// correct presentation resolution. This increases visual quality at
341 /// low data rates, at the expense of CPU time on the encoder/decoder.
342 ///
343 /// If set to `None`, spatial resizing is disallowed.
344 pub rc_resize_allowed: Option<SpatialResizeParams>,
345
346 /// Two-pass stats buffer.
347 ///
348 /// A buffer containing all of the stats packets produced in the first
349 /// pass, concatenated.
350 ///
351 /// If set to `None` (the default), no two-pass stats are passed in.
352 pub rc_twopass_stats_in: Option<FixedBuffer>,
353
354 /// First pass mb stats buffer.
355 ///
356 /// A buffer containing all of the first pass mb stats packets produced
357 /// in the first pass, concatenated.
358 ///
359 /// If set to `None` (the default), no first-pass MB stats are passed in.
360 pub rc_firstpass_mb_stats_in: Option<FixedBuffer>,
361
362 /// Rate control adaptation undershoot control
363 ///
364 /// VP8: Expressed as a percentage of the target bitrate, controls the
365 /// maximum allowed adaptation speed of the codec. This factor controls
366 /// the maximum amount of bits that can be subtracted from the target
367 /// bitrate in order to compensate for prior overshoot.
368 ///
369 /// VP9: Expressed as a percentage of the target bitrate, a threshold
370 /// undershoot level (current rate vs target) beyond which more aggressive
371 /// corrective measures are taken.
372 ///
373 /// Valid values in the range VP8:0-100 VP9: 0-100.
374 pub rc_undershoot_pct: u32,
375
376 /// Rate control adaptation overshoot control
377 ///
378 /// VP8: Expressed as a percentage of the target bitrate, controls the
379 /// maximum allowed adaptation speed of the codec. This factor controls
380 /// the maximum amount of bits that can be added to the target bitrate in
381 /// order to compensate for prior undershoot.
382 ///
383 /// VP9: Expressed as a percentage of the target bitrate, a threshold
384 /// overshoot level (current rate vs target) beyond which more aggressive
385 /// corrective measures are taken.
386 ///
387 /// Valid values in the range VP8:0-100 VP9: 0-100.
388 pub rc_overshoot_pct: u32,
389
390 /// Decoder Buffer Size
391 ///
392 /// This value indicates the amount of data that may be buffered by the
393 /// decoding application. Note that this value is expressed in units of
394 /// time.
395 pub rc_buf_sz: Duration,
396
397 /// Decoder Buffer Initial Size
398 ///
399 /// This value indicates the amount of data that will be buffered by the
400 /// decoding application prior to beginning playback. This value is
401 /// expressed in units of time
402 pub rc_buf_initial_sz: Duration,
403
404 /// Decoder Buffer Optimal Size
405 ///
406 /// This value indicates the amount of data that the encoder should try
407 /// to maintain in the decoder's buffer. This value is expressed in units
408 /// of time.
409 pub rc_buf_optimal_sz: Duration,
410
411 /// Two-pass mode CBR/VBR bias
412 ///
413 /// Bias, expressed on a scale of 0 to 100, for determining target size
414 /// for the current frame. The value 0 indicates the optimal CBR mode
415 /// value should be used. The value 100 indicates the optimal VBR mode
416 /// value should be used. Values in between indicate which way the
417 /// encoder should "lean."
418 pub rc_2pass_vbr_bias_pct: u32,
419
420 /// Two-pass mode per-GOP minimum bitrate
421 ///
422 /// This value, expressed as a percentage of the target bitrate, indicates
423 /// the minimum bitrate to be used for a single GOP (aka "section")
424 pub rc_2pass_vbr_minsection_pct: u32,
425
426 /// Two-pass mode per-GOP maximum bitrate
427 ///
428 /// This value, expressed as a percentage of the target bitrate, indicates
429 /// the maximum bitrate to be used for a single GOP (aka "section")
430 pub rc_2pass_vbr_maxsection_pct: u32,
431
432 /// Two-pass corpus vbr mode complexity control
433 ///
434 /// Used only in VP9: A value representing the corpus midpoint complexity
435 /// for corpus vbr mode. This value defaults to 0 which disables corpus vbr
436 /// mode in favour of normal vbr mode.
437 pub rc_2pass_vbr_corpus_complexity: u32,
438
439 /// Keyframe placement mode
440 ///
441 /// This value indicates whether the encoder should place keyframes at a
442 /// fixed interval, or determine the optimal placement automatically
443 /// (as governed by the `min_dist` and `max_dist` parameters).
444 pub kf_mode: KeyFrameMode,
445
446 /// Configures the spatial coding layers to be used by the encoder.
447 pub spatial_layers: Vec<SpatialLayer>,
448
449 /// Temporal coding layers.
450 ///
451 /// <div class="warning">
452 ///
453 /// This vector MUST NOT contain more than [`vpx_sys::VPX_TS_MAX_LAYERS`]
454 /// temporal layers.
455 ///
456 /// </div>
457 pub temporal_layers: Vec<TemporalLayer>,
458
459 /// Template defining the membership of frames to temporal layers.
460 ///
461 /// This vector defines the membership of frames to temporal coding layers.
462 /// For a 2-layer encoding that assigns even numbered frames to one
463 /// temporal layer (0) and odd numbered frames to a second temporal layer
464 /// (1), then `cfg.temporal_periodicity = vec![0,1,0,1,0,1,0,1]`.
465 ///
466 /// <div class="warning">
467 ///
468 /// This vector MUST NOT contain more than
469 /// [`vpx_sys::VPX_TS_MAX_PERIODICITY`] items.
470 ///
471 /// </div>
472 pub temporal_periodicity: Vec<u32>,
473
474 /// Target bitrate for each spatial/temporal layer.
475 ///
476 /// These values specify the target coding bitrate to be used for each
477 /// spatial/temporal layer (in kbps).
478 pub layer_target_bitrate: [u32; vpx_sys::VPX_MAX_LAYERS as usize],
479
480 /// Temporal layering mode indicating which temporal layering scheme to use.
481 ///
482 /// <div class="warning">This enum will only be populated when using VP9,
483 /// since VP8 doesn't support defining the temporal layering mode.
484 ///
485 /// If this is set to `None`, the codec-default layering mode is
486 /// used.</div>
487 pub temporal_layering_mode: Option<vpx_sys::vp9e_temporal_layering_mode>,
488
489 input_pixel_type: std::marker::PhantomData<T>,
490}
491
492impl<T: YUVPixelType> EncoderConfig<T> {
493 /// Constructs a new EncoderConfig, with most values set to their defaults.
494 ///
495 /// <div class="warning">Note: all bitrates are in kbit/s.</div>
496 pub fn new(
497 codec: CodecId,
498 width: u32,
499 height: u32,
500 timebase: Timebase,
501 rate_control: RateControl,
502 ) -> Result<Self> {
503 // Generate the default configuration and read back its values
504 // into our state.
505 let mut cfg = unsafe { MaybeUninit::zeroed().assume_init() };
506 call_vpx!(
507 vpx_sys::vpx_codec_enc_config_default(codec.iface()?, &mut cfg, 0),
508 Error::EncoderConfigInitFailed,
509 )?;
510 let temporal_layering_mode = if codec == CodecId::VP9 {
511 Self::try_from_temporal_layering_mode(cfg.temporal_layering_mode)
512 } else {
513 None
514 };
515 Ok(Self {
516 // Global params
517 codec,
518 profile: EncoderProfile::default(),
519 flags: EncoderFlags::empty(),
520 width,
521 height,
522 timebase,
523 rate_control,
524 threads: cfg.g_threads,
525 output_bit_depth: cfg.g_bit_depth,
526 error_resilient: ErrorResilient::empty(),
527 pass: cfg.g_pass,
528 lag_in_frames: cfg.g_lag_in_frames,
529
530 // Rate control params
531 rc_dropframe_thresh: cfg.rc_dropframe_thresh,
532 rc_resize_allowed: SpatialResizeParams::from_cfg(&cfg),
533 rc_twopass_stats_in: None,
534 rc_firstpass_mb_stats_in: None,
535 rc_undershoot_pct: cfg.rc_undershoot_pct,
536 rc_overshoot_pct: cfg.rc_overshoot_pct,
537 rc_buf_sz: Duration::from_millis(cfg.rc_buf_sz as u64),
538 rc_buf_initial_sz: Duration::from_millis(
539 cfg.rc_buf_initial_sz as u64,
540 ),
541 rc_buf_optimal_sz: Duration::from_millis(
542 cfg.rc_buf_optimal_sz as u64,
543 ),
544 rc_2pass_vbr_bias_pct: cfg.rc_2pass_vbr_bias_pct,
545 rc_2pass_vbr_minsection_pct: cfg.rc_2pass_vbr_minsection_pct,
546 rc_2pass_vbr_maxsection_pct: cfg.rc_2pass_vbr_maxsection_pct,
547 rc_2pass_vbr_corpus_complexity: cfg.rc_2pass_vbr_corpus_complexity,
548
549 // Key frame control
550 kf_mode: KeyFrameMode::from_cfg(&cfg),
551
552 // Spatial scalability settings (ss)
553 spatial_layers: SpatialLayer::from_cfg(&cfg),
554
555 // Temporal scalability settings (ts)
556 temporal_layers: TemporalLayer::from_cfg(&cfg),
557 temporal_periodicity: Self::ts_periodicity_from_cfg(&cfg),
558
559 layer_target_bitrate: cfg.layer_target_bitrate,
560 temporal_layering_mode,
561 // Type marker
562 input_pixel_type: std::marker::PhantomData,
563 })
564 }
565 /// # Safety
566 ///
567 /// If the configuration contained `rc_twopass_stats_in` or
568 /// `rc_firstpass_mb_stats_in`, the generated `vpx_codec_enc_cfg` will
569 /// reference that data by pointer. Care must be taken NOT to drop
570 /// the `EncoderConfig` before the `Encoder` has been constructed (at
571 /// which point the stats will be read).
572 unsafe fn cfg(
573 &self,
574 ) -> Result<(
575 *const vpx_sys::vpx_codec_iface_t,
576 vpx_sys::vpx_codec_enc_cfg,
577 )> {
578 let iface = self.codec.iface()?;
579 let mut cfg = unsafe { MaybeUninit::zeroed().assume_init() };
580 call_vpx!(
581 vpx_sys::vpx_codec_enc_config_default(iface, &mut cfg, 0),
582 Error::EncoderConfigInitFailed,
583 )?;
584
585 // Global settings
586 cfg.g_w = self.width;
587 cfg.g_h = self.height;
588 cfg.g_timebase.num = self.timebase.num.get() as _;
589 cfg.g_timebase.den = self.timebase.den.get() as _;
590 cfg.g_threads = self.threads;
591 cfg.g_error_resilient = self.error_resilient.bits();
592 cfg.g_pass = self.pass;
593 cfg.g_bit_depth = self.output_bit_depth;
594 cfg.g_input_bit_depth = (size_of::<T>() * 8) as u32;
595 cfg.g_lag_in_frames = self.lag_in_frames;
596 if self.profile != EncoderProfile::Default && self.codec == CodecId::VP8
597 {
598 return Err(Error::InvalidProfileSelected);
599 }
600 cfg.g_profile = self.profile as _;
601
602 // Rate control settings
603 match self.rate_control {
604 RateControl::VariableBitRate(bitrate) => {
605 cfg.rc_end_usage = vpx_sys::vpx_rc_mode::VPX_VBR;
606 cfg.rc_target_bitrate = bitrate;
607 }
608 RateControl::ConstantBitRate(bitrate) => {
609 cfg.rc_end_usage = vpx_sys::vpx_rc_mode::VPX_CBR;
610 cfg.rc_target_bitrate = bitrate;
611 }
612 RateControl::ConstrainedQuality { bitrate, .. } => {
613 cfg.rc_end_usage = vpx_sys::vpx_rc_mode::VPX_CQ;
614 cfg.rc_target_bitrate = bitrate;
615 }
616 RateControl::ConstantQuality(_) => {
617 cfg.rc_end_usage = vpx_sys::vpx_rc_mode::VPX_Q;
618 }
619 _ => (),
620 }
621 cfg.rc_dropframe_thresh = self.rc_dropframe_thresh;
622 SpatialResizeParams::to_cfg(&self.rc_resize_allowed, &mut cfg);
623 if let Some(stats) = &self.rc_twopass_stats_in {
624 // WARNING: care must be taken NOT to drop the `EncoderConfig`
625 // before the `Encoder` has been constructed!
626 cfg.rc_twopass_stats_in = vpx_sys::vpx_fixed_buf {
627 buf: stats.0.as_ptr() as *mut c_void,
628 sz: stats.0.len(),
629 };
630 }
631 if let Some(stats) = &self.rc_firstpass_mb_stats_in {
632 // WARNING: care must be taken NOT to drop the `EncoderConfig`
633 // before the `Encoder` has been constructed!
634 cfg.rc_firstpass_mb_stats_in = vpx_sys::vpx_fixed_buf {
635 buf: stats.0.as_ptr() as *mut c_void,
636 sz: stats.0.len(),
637 };
638 }
639 cfg.rc_undershoot_pct = self.rc_undershoot_pct;
640 cfg.rc_overshoot_pct = self.rc_overshoot_pct;
641 cfg.rc_buf_sz = self.rc_buf_sz.as_millis() as u32;
642 cfg.rc_buf_initial_sz = self.rc_buf_initial_sz.as_millis() as u32;
643 cfg.rc_buf_optimal_sz = self.rc_buf_optimal_sz.as_millis() as u32;
644 cfg.rc_2pass_vbr_bias_pct = self.rc_2pass_vbr_bias_pct;
645 cfg.rc_2pass_vbr_minsection_pct = self.rc_2pass_vbr_minsection_pct;
646 cfg.rc_2pass_vbr_maxsection_pct = self.rc_2pass_vbr_maxsection_pct;
647 cfg.rc_2pass_vbr_corpus_complexity =
648 self.rc_2pass_vbr_corpus_complexity;
649
650 // Key frame control
651 self.kf_mode.to_cfg(&mut cfg);
652
653 // Spatial scalability settings (ss)
654 SpatialLayer::to_cfg(&self.spatial_layers, &mut cfg);
655
656 // Temporal scalability settings (ss)
657 TemporalLayer::to_cfg(&self.temporal_layers, &mut cfg);
658 assert!(
659 self.temporal_periodicity.len()
660 < vpx_sys::VPX_TS_MAX_PERIODICITY as usize
661 );
662 let n_ts_periodicity = self.temporal_periodicity.len();
663 cfg.ts_periodicity = n_ts_periodicity as u32;
664 cfg.ts_layer_id[..n_ts_periodicity]
665 .copy_from_slice(&self.temporal_periodicity[..n_ts_periodicity]);
666
667 cfg.layer_target_bitrate = self.layer_target_bitrate;
668 if let Some(x) = self.temporal_layering_mode {
669 cfg.temporal_layering_mode = x as i32;
670 }
671
672 Ok((iface, cfg))
673 }
674 fn ts_periodicity_from_cfg(cfg: &vpx_sys::vpx_codec_enc_cfg) -> Vec<u32> {
675 let mut periodicity = vec![];
676 for i in 0..cfg.ts_periodicity as usize {
677 periodicity.push(cfg.ts_layer_id[i]);
678 }
679 periodicity
680 }
681 fn try_from_temporal_layering_mode(
682 value: i32,
683 ) -> Option<vpx_sys::vp9e_temporal_layering_mode> {
684 use vpx_sys::vp9e_temporal_layering_mode::*;
685 match value {
686 x if x == VP9E_TEMPORAL_LAYERING_MODE_NOLAYERING as i32 => {
687 Some(VP9E_TEMPORAL_LAYERING_MODE_NOLAYERING)
688 }
689 x if x == VP9E_TEMPORAL_LAYERING_MODE_BYPASS as i32 => {
690 Some(VP9E_TEMPORAL_LAYERING_MODE_BYPASS)
691 }
692 x if x == VP9E_TEMPORAL_LAYERING_MODE_0101 as i32 => {
693 Some(VP9E_TEMPORAL_LAYERING_MODE_0101)
694 }
695 x if x == VP9E_TEMPORAL_LAYERING_MODE_0212 as i32 => {
696 Some(VP9E_TEMPORAL_LAYERING_MODE_0212)
697 }
698 _ => None,
699 }
700 }
701}
702
703/// If spatial resampling is enabled this specifies the size of the
704/// encoded frame.
705#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
706pub struct SpatialResizeParams {
707 /// Internal coded frame width.
708 pub width: u32,
709 /// Internal coded frame height.
710 pub height: u32,
711 /// Spatial resampling up watermark.
712 ///
713 /// This threshold is described as a percentage of the target data buffer.
714 /// When the data buffer rises above this percentage of fullness, the
715 /// encoder will step up to a higher resolution version of the frame.
716 pub resize_up_thresh: u32,
717 /// Spatial resampling down watermark.
718 ///
719 /// This threshold is described as a percentage of the target data buffer.
720 /// When the data buffer falls below this percentage of fullness, the
721 /// encoder will step down to a lower resolution version of the frame.
722 pub resize_down_thresh: u32,
723}
724
725impl SpatialResizeParams {
726 /// Constructs a new set of resize params pre-populated with
727 /// the defaults employed by the specific codec.
728 pub fn default_for_codec(codec: CodecId) -> Result<Self> {
729 let iface = codec.iface()?;
730 let mut cfg = unsafe { MaybeUninit::zeroed().assume_init() };
731 call_vpx!(
732 vpx_sys::vpx_codec_enc_config_default(iface, &mut cfg, 0),
733 Error::EncoderConfigInitFailed,
734 )?;
735 Ok(Self {
736 width: cfg.rc_scaled_width,
737 height: cfg.rc_scaled_height,
738 resize_up_thresh: cfg.rc_resize_up_thresh,
739 resize_down_thresh: cfg.rc_resize_down_thresh,
740 })
741 }
742 fn from_cfg(cfg: &vpx_codec_enc_cfg) -> Option<Self> {
743 (cfg.rc_resize_allowed != 0).then_some(Self {
744 width: cfg.rc_scaled_width,
745 height: cfg.rc_scaled_height,
746 resize_up_thresh: cfg.rc_resize_up_thresh,
747 resize_down_thresh: cfg.rc_resize_down_thresh,
748 })
749 }
750 fn to_cfg(
751 params: &Option<SpatialResizeParams>,
752 cfg: &mut vpx_codec_enc_cfg,
753 ) {
754 if let Some(params) = params {
755 cfg.rc_resize_allowed = 1;
756 cfg.rc_scaled_width = params.width;
757 cfg.rc_scaled_height = params.height;
758 cfg.rc_resize_up_thresh = params.resize_up_thresh;
759 cfg.rc_resize_down_thresh = params.resize_down_thresh;
760 } else {
761 cfg.rc_resize_allowed = 0;
762 }
763 }
764}
765
766/// Configures the type of rate control the codec should employ.
767#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
768pub enum RateControl {
769 /// Constant Bit Rate (CBR) mode, bitrate in kbit/s.
770 ConstantBitRate(u32),
771 /// Variable Bit Rate (VBR) mode, bitrate in kbit/s.
772 VariableBitRate(u32),
773 /// Constrained Quality (CQ) mode. This is variable bitrate mode, but
774 /// an additional quality cap, so as not to waste bits on scenes where
775 /// additional bitrate would be wasted. Use the following control sets
776 /// to obtain fine-grained over the bounds of the variable bitrate
777 /// algorithm (or just let the codec decide):
778 /// - [`ctrl::EncoderControlSet::MaxIntraBitratePct`]
779 ///
780 /// In addition, for VP9 the following extra sets are available:
781 /// - [`ctrl::EncoderControlSet::Vp9MaxInterBitratePct`]
782 /// - [`ctrl::EncoderControlSet::Vp9AQMode`]
783 /// - [`ctrl::EncoderControlSet::Vp9FramePeriodicBoost`]
784 ConstrainedQuality {
785 /// Target bitrate in kbit/s.
786 bitrate: u32,
787 /// Minimum quality setting the encoder should use.
788 /// Allowable values 0..63 (lower value = higher quality).
789 max_quality: u32,
790 },
791 /// Constant Quality/Quantizer (Q) mode.
792 /// Allowable values 0..63 (lower value = higher quality).
793 ConstantQuality(u32),
794 /// Lossless quality mode. The decoded bitstream will be an exact
795 /// bit-accurate copy of the input. Only supported by the VP9 codec.
796 Lossless,
797}
798
799/// Stream timebase units.
800///
801/// Indicates the smallest interval of time, in seconds, used by the stream.
802/// For fixed frame rate material, or variable frame rate material where
803/// frames are timed at a multiple of a given clock (ex: video capture), the
804/// RECOMMENDED method is to set the timebase to the reciprocal of the frame
805/// rate (ex: 1001/30000 for 29.970 Hz NTSC). This allows the pts to
806/// correspond to the frame number, which can be handy. For re-encoding video
807/// from containers with absolute time timestamps, the RECOMMENDED method is
808/// to set the timebase to that of the parent container or multimedia
809/// framework (ex: 1/1000 for ms, as in FLV).
810///
811#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
812pub struct Timebase {
813 /// The numerator of the timebase.
814 pub num: NonZero<u32>,
815 /// The denominator of the timebase.
816 pub den: NonZero<u32>,
817}
818
819/// Encode Deadline
820///
821/// The encoder supports the notion of a soft real-time deadline. Given a
822/// non-zero value to the deadline parameter (i.e. anything other than
823/// `BestQuality`), the encoder will make a "best effort" guarantee to
824/// return before the given time slice expires. It is implicit that limiting
825/// the available time to encode will degrade the output quality. The encoder
826/// can be given an unlimited time to produce the best possible frame by
827/// specifying a deadline of `BestQuality`. A `Custom` deadline supersedes
828/// the VPx notion of "best quality, good quality, realtime".
829///
830/// The default is `GoodQuality`.
831#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
832pub enum EncodingDeadline {
833 /// Deadline indicating that the encoder return as soon as possible
834 /// (equal to [`vpx_sys::VPX_DL_REALTIME`] microseconds).
835 Realtime,
836 /// Deadline equal to [`vpx_sys::VPX_DL_GOOD_QUALITY`] microseconds
837 /// (the default).
838 #[default]
839 GoodQuality,
840 /// Deadline equal to [`vpx_sys::VPX_DL_BEST_QUALITY`] (unlimited
841 /// encoding time to produce the best frame possible).
842 BestQuality,
843 /// Custom deadline setting to be passed to the encoder.
844 Custom(Duration),
845}
846
847impl From<EncodingDeadline> for std::ffi::c_ulong {
848 fn from(value: EncodingDeadline) -> Self {
849 match value {
850 EncodingDeadline::Realtime => vpx_sys::VPX_DL_REALTIME as _,
851 EncodingDeadline::GoodQuality => vpx_sys::VPX_DL_GOOD_QUALITY as _,
852 EncodingDeadline::BestQuality => vpx_sys::VPX_DL_BEST_QUALITY as _,
853 EncodingDeadline::Custom(dl) => dl.as_micros() as _,
854 }
855 }
856}
857
858/// The VP9 codec supports a notion of multiple bitstream profiles.
859/// This maps to a set of features that are turned on or off. Often the
860/// profile to use is determined by the features of the intended decoder.
861#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
862#[repr(u32)]
863pub enum EncoderProfile {
864 /// The default bitstream profile for any codec.
865 /// - VP8 only supports this basic profile
866 /// - VP9 treats this as bitstream Profile 0
867 ///
868 /// This profile only supports 8-bit 4:2:0 (for both VP8 and VP9).
869 #[default]
870 Default = 0,
871 /// VP 9 Profile 1. 8-bit 4:4:4, 4:2:2, and 4:4:0.
872 Vp9Profile1 = 1,
873 /// VP9 Profile 2. 10-bit and 12-bit color only, with 4:2:0 sampling.
874 Vp9Profile2 = 2,
875 /// VP9 Profile 3. 10-bit and 12-bit color only, with 4:2:2/4:4:4/4:4:0
876 /// sampling.
877 Vp9Profile3 = 3,
878}
879
880bitflags! {
881 /// These flags define which error resilient features to enable in the
882 /// encoder.
883 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
884 pub struct ErrorResilient: u32 {
885 /// Improve resiliency against losses of whole frames.
886 const DEFAULT = vpx_sys::VPX_ERROR_RESILIENT_DEFAULT;
887 /// The frame partitions are independently decodable by the bool
888 /// decoder, meaning that partitions can be decoded even though
889 /// earlier partitions have been lost. Note that intra prediction
890 /// is still done over the partition boundary.
891 ///
892 /// <div class="warning">This is only supported by VP8.</div>
893 const RESILIENT_PARTITIONS = vpx_sys::VPX_ERROR_RESILIENT_PARTITIONS;
894 }
895 /// Encoder configuration flags to enable special global behaviors.
896 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
897 pub struct EncoderFlags: u32 {
898 /// Calculate PSNR on each frame.
899 const PSNR = vpx_sys::VPX_CODEC_USE_PSNR;
900 /// Make the encoder output one partition at a time.
901 const OUTPUT_PARTITION = vpx_sys::VPX_CODEC_USE_OUTPUT_PARTITION;
902 /// Use high bitdepth.
903 const HIGH_BIT_DEPTH = vpx_sys::VPX_CODEC_USE_HIGHBITDEPTH;
904 }
905 /// This type indicates a bitfield to [`Encoder::encode()`], defining
906 /// per-frame boolean values.
907 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
908 pub struct EncoderFrameFlags: u32 {
909 /// Force this frame to be a keyframe
910 const FORCE_KF = vpx_sys::VPX_EFLAG_FORCE_KF;
911 /// Calculate PSNR on this frame, requires
912 /// [`EncoderConfig::lag_in_frames`] to be 0.
913 const CALCULATE_PSNR = 1 << 1;
914
915 // Caveat: despite these flags having the `VP8_' prefix in libvpx,
916 // they seem to still be inspected by the VP9 encoder (seen in
917 // `vp9_apply_encoding_flags'), so we drop the algorithm prefix
918 // from the names here.
919
920 /// Don't reference the last frame.
921 ///
922 /// When this flag is set, the encoder will not use the last frame
923 /// as a predictor. When not set, the encoder will choose whether
924 /// to use the last frame or not automatically.
925 const NO_REF_LAST = vpx_sys::VP8_EFLAG_NO_REF_LAST;
926 /// Don't reference the golden frame.
927 ///
928 /// When this flag is set, the encoder will not use the golden frame
929 /// as a predictor. When not set, the encoder will choose whether to
930 /// use the golden frame or not automatically.
931 const NO_REF_GF = vpx_sys::VP8_EFLAG_NO_REF_GF;
932 /// Don't reference the alternate reference frame.
933 ///
934 /// When this flag is set, the encoder will not use the alt ref frame
935 /// as a predictor. When not set, the encoder will choose whether to
936 /// use the alt ref frame or not automatically.
937 const NO_REF_ARF = vpx_sys::VP8_EFLAG_NO_REF_ARF;
938 /// Don't update the last frame.
939 ///
940 /// When this flag is set, the encoder will not update the last frame
941 /// with the contents of the current frame.
942 const NO_UDP_LAST = vpx_sys::VP8_EFLAG_NO_UPD_LAST;
943 /// Don't update the golden frame.
944 ///
945 /// When this flag is set, the encoder will not update the golden frame
946 /// with the contents of the current frame..
947 const NO_UDP_GF = vpx_sys::VP8_EFLAG_NO_UPD_GF;
948 /// Don't update the alternate reference frame.
949 ///
950 /// When this flag is set, the encoder will not update the alt ref
951 /// frame with the contents of the current frame.
952 const NO_UDP_ARF = vpx_sys::VP8_EFLAG_NO_UPD_ARF;
953 /// Force golden frame update.
954 ///
955 /// When this flag is set, the encoder copy the contents of the
956 /// current frame to the golden frame buffer.
957 const FORCE_GF = vpx_sys::VP8_EFLAG_FORCE_GF;
958 /// Force alternate reference frame update.
959 ///
960 /// When this flag is set, the encoder copy the contents of the
961 /// current frame to the alternate reference frame buffer.
962 const FORCE_ARF = vpx_sys::VP8_EFLAG_FORCE_ARF;
963 /// Disable entropy update.
964 ///
965 /// When this flag is set, the encoder will not update its internal
966 /// entropy model based on the entropy of this frame.
967 const NO_UDP_ENTROPY = vpx_sys::VP8_EFLAG_NO_UPD_ENTROPY;
968 }
969}
970
971/// Keyframe placement mode
972///
973/// This value indicates whether the encoder should place keyframes at a
974/// fixed interval, or determine the optimal placement automatically
975/// (as governed by the `min_dist` and `max_dist` parameters)
976#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
977pub enum KeyFrameMode {
978 /// Encoder determines optimal placement automatically
979 Auto {
980 /// Keyframe minimum interval
981 ///
982 /// This value, expressed as a number of frames, prevents the encoder
983 /// from placing a keyframe nearer than `min_dist` to the previous
984 /// keyframe. At least `min_dist` frames non-keyframes will be coded
985 /// before the next keyframe. Set `min_dist` equal to `max_dist` for
986 /// a fixed interval.
987 min_dist: u32,
988 /// Keyframe maximum interval
989 ///
990 /// This value, expressed as a number of frames, forces the encoder
991 /// to code a keyframe if one has not been coded in the last
992 /// `max_dist` frames. A value of 0 implies all frames will be
993 /// keyframes. Set `min_dist` equal to `max_dist` for a fixed interval.
994 max_dist: u32,
995 },
996 /// Encoder does not place keyframes.
997 Disabled,
998}
999
1000impl KeyFrameMode {
1001 fn from_cfg(cfg: &vpx_sys::vpx_codec_enc_cfg) -> Self {
1002 match cfg.kf_mode {
1003 vpx_sys::vpx_kf_mode::VPX_KF_AUTO => Self::Auto {
1004 min_dist: cfg.kf_min_dist,
1005 max_dist: cfg.kf_max_dist,
1006 },
1007 // VPX_KF_FIXED is an alias for VPX_KF_DISABLED
1008 vpx_sys::vpx_kf_mode::VPX_KF_DISABLED => Self::Disabled,
1009 }
1010 }
1011 fn to_cfg(self, cfg: &mut vpx_sys::vpx_codec_enc_cfg) {
1012 match self {
1013 KeyFrameMode::Auto { min_dist, max_dist } => {
1014 cfg.kf_mode = vpx_sys::vpx_kf_mode::VPX_KF_AUTO;
1015 cfg.kf_min_dist = min_dist;
1016 cfg.kf_max_dist = max_dist;
1017 }
1018 KeyFrameMode::Disabled => {
1019 cfg.kf_mode = vpx_sys::vpx_kf_mode::VPX_KF_DISABLED;
1020 }
1021 }
1022 }
1023}
1024
1025/// Configures the parameters for a spatial coding layer.
1026#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
1027pub struct SpatialLayer {
1028 /// Enable auto alt reference flags for this spatial layer.
1029 ///
1030 /// Defines if auto alt reference frame is enabled for this layer.
1031 pub enable_auto_alt_ref: bool,
1032
1033 /// Target bitrate for this spatial layer.
1034 ///
1035 /// Defines the target coding bitrate to be used for this layer (in kbps).
1036 pub target_bitrate: u32,
1037}
1038
1039impl SpatialLayer {
1040 fn from_cfg(cfg: &vpx_sys::vpx_codec_enc_cfg) -> Vec<Self> {
1041 let mut layers = vec![];
1042 for i in 0..cfg.ss_number_layers as usize {
1043 layers.push(SpatialLayer {
1044 enable_auto_alt_ref: cfg.ss_enable_auto_alt_ref[i] != 0,
1045 target_bitrate: cfg.ss_target_bitrate[i],
1046 });
1047 }
1048 layers
1049 }
1050 fn to_cfg(layers: &[Self], cfg: &mut vpx_sys::vpx_codec_enc_cfg) {
1051 assert!(layers.len() < vpx_sys::VPX_SS_MAX_LAYERS as usize);
1052 cfg.ss_number_layers = layers.len() as u32;
1053 layers.iter().enumerate().for_each(|(i, layer)| {
1054 cfg.ss_enable_auto_alt_ref[i] = layer.enable_auto_alt_ref as i32;
1055 cfg.ss_target_bitrate[i] = layer.target_bitrate;
1056 });
1057 }
1058}
1059
1060/// Configures the parameters for a temporal coding layer.
1061#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
1062pub struct TemporalLayer {
1063 /// Target bitrate for the temporal layer.
1064 ///
1065 /// The target coding bitrate to be used for the temporal layer (in kbps).
1066 target_bitrate: u32,
1067 /// Frame rate decimation factor for the temporal layer.
1068 ///
1069 /// Specifies the frame rate decimation factor to apply to this layer.
1070 rate_decimator: u32,
1071}
1072
1073impl TemporalLayer {
1074 fn from_cfg(cfg: &vpx_sys::vpx_codec_enc_cfg) -> Vec<Self> {
1075 let mut layers = vec![];
1076 for i in 0..cfg.ts_number_layers as usize {
1077 layers.push(Self {
1078 target_bitrate: cfg.ts_target_bitrate[i],
1079 rate_decimator: cfg.ts_rate_decimator[i],
1080 })
1081 }
1082 layers
1083 }
1084 fn to_cfg(layers: &[Self], cfg: &mut vpx_sys::vpx_codec_enc_cfg) {
1085 assert!(layers.len() < vpx_sys::VPX_TS_MAX_LAYERS as usize);
1086 cfg.ts_number_layers = layers.len() as u32;
1087 layers.iter().enumerate().for_each(|(i, layer)| {
1088 cfg.ts_target_bitrate[i] = layer.target_bitrate;
1089 cfg.ts_rate_decimator[i] = layer.rate_decimator;
1090 });
1091 }
1092}
1093
1094/// An iterator which returns packets after a call to
1095/// [`Encoder::encode()`]. You should drain all packets when the encoder
1096/// generates them.
1097pub struct PacketIterator<'a> {
1098 ctx: &'a mut vpx_sys::vpx_codec_ctx_t,
1099 iter: vpx_sys::vpx_codec_iter_t,
1100}
1101
1102// Safe to send between threads, because we don't hold on to any mutexes
1103// between function calls and no functions are async.
1104unsafe impl Send for PacketIterator<'_> {}
1105
1106impl Iterator for PacketIterator<'_> {
1107 type Item = Packet;
1108 fn next(&mut self) -> Option<Self::Item> {
1109 loop {
1110 let pkt = unsafe {
1111 vpx_sys::vpx_codec_get_cx_data(self.ctx, &mut self.iter)
1112 .as_ref()?
1113 };
1114 if let Some(pkt) = Packet::from_vpx_packet(pkt) {
1115 return Some(pkt);
1116 }
1117 }
1118 }
1119}
1120
1121/// A packet output from the encoder in response to a call to
1122/// [`Encoder::encode()`]. The encoded bitstream consists of the
1123/// contents of compressed frame packets, but the encoder can sometimes
1124/// also produce some other ancillary information packets. For example,
1125/// when doing multi-pass encoding, or when running the PSNR analyzer,
1126/// the encoder will generate packets containing this additional
1127/// metainformation. In general, though, you only need the packets
1128/// containing the [`CompressedFrame`] data to decode a video stream.
1129#[derive(Clone, Debug, PartialEq, PartialOrd)]
1130pub enum Packet {
1131 /// A compressed video frame.
1132 CompressedFrame(CompressedFrame),
1133 /// Two-pass statistics for this frame.
1134 TwoPassStats(FixedBuffer),
1135 /// First pass mb statistics for this frame.
1136 FirstPassMbStats(FixedBuffer),
1137 /// PSNR statistics for this frame.
1138 PSNRPacket(PSNRPacket),
1139 /// Algorithm extensions.
1140 Custom(FixedBuffer),
1141}
1142
1143impl Packet {
1144 fn from_vpx_packet(pkt: &vpx_sys::vpx_codec_cx_pkt) -> Option<Self> {
1145 match pkt.kind {
1146 vpx_sys::vpx_codec_cx_pkt_kind::VPX_CODEC_CX_FRAME_PKT => {
1147 let frame = unsafe { &pkt.data.frame };
1148 if frame.buf.is_null() {
1149 return None;
1150 }
1151 Some(Self::CompressedFrame(CompressedFrame {
1152 data: unsafe {
1153 std::slice::from_raw_parts(frame.buf as _, frame.sz)
1154 }
1155 .to_vec(),
1156 pts: frame.pts,
1157 // frame.duration is a c_ulong, so need to cast
1158 duration: frame.duration as _,
1159 flags: CompressedFrameFlags::from(frame.flags),
1160 partition_id: frame.partition_id,
1161 width: frame.width,
1162 height: frame.height,
1163 spatial_layer_encoded: frame.spatial_layer_encoded,
1164 }))
1165 }
1166 vpx_sys::vpx_codec_cx_pkt_kind::VPX_CODEC_STATS_PKT => {
1167 Some(Self::TwoPassStats(FixedBuffer::from_vpx(unsafe {
1168 &pkt.data.twopass_stats
1169 })?))
1170 }
1171 vpx_sys::vpx_codec_cx_pkt_kind::VPX_CODEC_FPMB_STATS_PKT => {
1172 Some(Self::FirstPassMbStats(FixedBuffer::from_vpx(unsafe {
1173 &pkt.data.firstpass_mb_stats
1174 })?))
1175 }
1176 vpx_sys::vpx_codec_cx_pkt_kind::VPX_CODEC_PSNR_PKT => {
1177 let psnr = unsafe { &pkt.data.psnr };
1178 Some(Self::PSNRPacket(PSNRPacket {
1179 samples: PSNRSamples {
1180 total: psnr.samples[0],
1181 y: psnr.samples[1],
1182 u: psnr.samples[2],
1183 v: psnr.samples[3],
1184 },
1185 sse: PSNRSumSquaredError {
1186 total: psnr.sse[0],
1187 y: psnr.sse[1],
1188 u: psnr.sse[2],
1189 v: psnr.sse[3],
1190 },
1191 psnr: PSNR {
1192 total: psnr.psnr[0],
1193 y: psnr.psnr[1],
1194 u: psnr.psnr[2],
1195 v: psnr.psnr[3],
1196 },
1197 }))
1198 }
1199 _ => Some(Self::Custom(FixedBuffer::from_vpx(unsafe {
1200 &pkt.data.raw
1201 })?)),
1202 }
1203 }
1204}
1205
1206/// A compressed frame produced by the encoder.
1207#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
1208pub struct CompressedFrame {
1209 /// Compressed data of the frame. This is what you need to store or
1210 /// send over the wire, in order to produce a decodable bistream.
1211 pub data: Vec<u8>,
1212 /// Presentation timestamp (in timebase units).
1213 pub pts: i64,
1214 /// Duration in timebase units.
1215 pub duration: u64,
1216 /// Frame flags.
1217 pub flags: CompressedFrameFlags,
1218 /// The partition id defines the decoding order of the partitions.
1219 /// Only applicable when "output partition" mode is enabled. First
1220 /// partition has id 0.
1221 pub partition_id: i32,
1222 /// Width of frames in this packet. VP8 will only use the first one.
1223 pub width: [u32; vpx_sys::VPX_SS_MAX_LAYERS as usize],
1224 /// Height of frames in this packet. VP8 will only use the first one.
1225 pub height: [u32; vpx_sys::VPX_SS_MAX_LAYERS as usize],
1226 /// Flag to indicate if spatial layer frame in this packet is encoded
1227 /// or dropped. VP8 will always be set to 1.
1228 pub spatial_layer_encoded: [u8; vpx_sys::VPX_SS_MAX_LAYERS as usize],
1229}
1230
1231/// Flags pertaining to an encoded frame.
1232#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
1233pub struct CompressedFrameFlags {
1234 /// Frame is the start of a Group Of Pictures (GOP), i.e. a keyframe.
1235 pub is_key: bool,
1236 /// Frame can be dropped without affecting the stream (no future frame
1237 /// depends on this one).
1238 pub is_droppable: bool,
1239 /// Frame should be decoded but will not be shown.
1240 pub is_invisible: bool,
1241 /// This is a fragment of the encoded frame.
1242 pub is_fragment: bool,
1243}
1244
1245impl From<vpx_sys::vpx_codec_frame_flags_t> for CompressedFrameFlags {
1246 fn from(value: vpx_sys::vpx_codec_frame_flags_t) -> Self {
1247 Self {
1248 is_key: (value & vpx_sys::VPX_FRAME_IS_KEY) != 0,
1249 is_droppable: (value & vpx_sys::VPX_FRAME_IS_DROPPABLE) != 0,
1250 is_invisible: (value & vpx_sys::VPX_FRAME_IS_INVISIBLE) != 0,
1251 is_fragment: (value & vpx_sys::VPX_FRAME_IS_FRAGMENT) != 0,
1252 }
1253 }
1254}
1255
1256/// An opaque buffer representing arbitrary data given to us by libvpx
1257/// in a returned Packet. You can access the underlying data by calling
1258/// the unsafe [`FixedBuffer::get_inner()`] method with the appropriate
1259/// return type. If the libvpx buffer is null, this type will never be
1260/// constructed, so if you see a `FixedBuffer`, you can be sure that
1261/// there is *something* there.
1262#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
1263pub struct FixedBuffer(Vec<u8>);
1264
1265impl FixedBuffer {
1266 /// Unsafely casts the pointer contained in the fixed buffer to a
1267 /// reference of the return type of this function.
1268 ///
1269 /// # Safety
1270 ///
1271 /// We cannot fully check what type the contained data is, so it is up
1272 /// to the caller to make sure they're only grabbing the correct type
1273 /// here. This function only checks that the size of the buffer target
1274 /// matches the return type's size.
1275 pub unsafe fn inner<T: Sized>(&self) -> Option<&T> {
1276 if self.0.len() != size_of::<T>() {
1277 return None;
1278 }
1279 unsafe { (self.0.as_ptr() as *const T).as_ref() }
1280 }
1281 fn from_vpx(value: &vpx_sys::vpx_fixed_buf_t) -> Option<Self> {
1282 let buf = unsafe {
1283 std::slice::from_raw_parts(
1284 std::ptr::NonNull::new(value.buf)?.as_ptr() as *const u8,
1285 value.sz,
1286 )
1287 .to_vec()
1288 };
1289
1290 Some(Self(buf))
1291 }
1292}
1293
1294/// PSNR statistics for a frame.
1295#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
1296pub struct PSNRPacket {
1297 /// Number of samples.
1298 pub samples: PSNRSamples,
1299 /// Sum squared error.
1300 pub sse: PSNRSumSquaredError,
1301 /// PSNR.
1302 pub psnr: PSNR,
1303}
1304
1305/// PSNR stats samples.
1306#[allow(missing_docs)]
1307#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
1308pub struct PSNRSamples {
1309 pub total: u32,
1310 pub y: u32,
1311 pub u: u32,
1312 pub v: u32,
1313}
1314
1315/// PSNR sum squared error.
1316#[allow(missing_docs)]
1317#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
1318pub struct PSNRSumSquaredError {
1319 pub total: u64,
1320 pub y: u64,
1321 pub u: u64,
1322 pub v: u64,
1323}
1324
1325/// Perceptual Signal-to-Noise Ratio
1326#[allow(missing_docs)]
1327#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
1328pub struct PSNR {
1329 pub total: f64,
1330 pub y: f64,
1331 pub u: f64,
1332 pub v: f64,
1333}
1334
1335#[cfg(test)]
1336mod test {
1337 use super::{
1338 CodecId, CompressedFrame, CompressedFrameFlags, Encoder, EncoderConfig,
1339 EncoderFlags, EncoderFrameFlags, ErrorResilient, FixedBuffer,
1340 KeyFrameMode, PSNRPacket, PSNRSamples, PSNRSumSquaredError,
1341 PacketIterator, RateControl, SpatialLayer, SpatialResizeParams,
1342 TemporalLayer, Timebase, PSNR,
1343 };
1344 #[test]
1345 fn test_send() {
1346 fn assert_send<T: Send>() {}
1347 assert_send::<CodecId>();
1348 assert_send::<CompressedFrame>();
1349 assert_send::<CompressedFrameFlags>();
1350 assert_send::<Encoder<u8>>();
1351 assert_send::<EncoderConfig<u8>>();
1352 assert_send::<EncoderFlags>();
1353 assert_send::<EncoderFrameFlags>();
1354 assert_send::<ErrorResilient>();
1355 assert_send::<FixedBuffer>();
1356 assert_send::<KeyFrameMode>();
1357 assert_send::<PacketIterator>();
1358 assert_send::<PSNR>();
1359 assert_send::<PSNRPacket>();
1360 assert_send::<PSNRSamples>();
1361 assert_send::<PSNRSumSquaredError>();
1362 assert_send::<RateControl>();
1363 assert_send::<SpatialLayer>();
1364 assert_send::<SpatialResizeParams>();
1365 assert_send::<TemporalLayer>();
1366 assert_send::<Timebase>();
1367 }
1368 #[test]
1369 fn test_sync() {
1370 fn assert_sync<T: Sync>() {}
1371 assert_sync::<CodecId>();
1372 assert_sync::<CompressedFrame>();
1373 assert_sync::<CompressedFrameFlags>();
1374 assert_sync::<Encoder<u8>>();
1375 assert_sync::<EncoderConfig<u8>>();
1376 assert_sync::<EncoderFlags>();
1377 assert_sync::<EncoderFrameFlags>();
1378 assert_sync::<ErrorResilient>();
1379 assert_sync::<FixedBuffer>();
1380 assert_sync::<KeyFrameMode>();
1381 assert_sync::<PSNR>();
1382 assert_sync::<PSNRPacket>();
1383 assert_sync::<PSNRSamples>();
1384 assert_sync::<PSNRSumSquaredError>();
1385 assert_sync::<RateControl>();
1386 assert_sync::<SpatialLayer>();
1387 assert_sync::<SpatialResizeParams>();
1388 assert_sync::<TemporalLayer>();
1389 assert_sync::<Timebase>();
1390 }
1391}