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}