scuffle_ffmpeg/
decoder.rs

1use crate::codec::DecoderCodec;
2use crate::error::{FfmpegError, FfmpegErrorCode};
3use crate::ffi::*;
4use crate::frame::{AudioFrame, GenericFrame, VideoFrame};
5use crate::packet::Packet;
6use crate::rational::Rational;
7use crate::smart_object::SmartPtr;
8use crate::stream::Stream;
9use crate::{AVCodecID, AVMediaType, AVPixelFormat, AVSampleFormat};
10
11/// Either a [`VideoDecoder`] or an [`AudioDecoder`].
12///
13/// This is the most common way to interact with decoders.
14#[derive(Debug)]
15pub enum Decoder {
16    /// A video decoder.
17    Video(VideoDecoder),
18    /// An audio decoder.
19    Audio(AudioDecoder),
20}
21
22/// A generic decoder that can be used to decode any type of media.
23pub struct GenericDecoder {
24    decoder: SmartPtr<AVCodecContext>,
25}
26
27/// Safety: `GenericDecoder` can be sent between threads.
28unsafe impl Send for GenericDecoder {}
29
30impl std::fmt::Debug for GenericDecoder {
31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        f.debug_struct("Decoder")
33            .field("time_base", &self.time_base())
34            .field("codec_type", &self.codec_type())
35            .finish()
36    }
37}
38
39/// A video decoder.
40pub struct VideoDecoder(GenericDecoder);
41
42impl std::fmt::Debug for VideoDecoder {
43    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44        f.debug_struct("VideoDecoder")
45            .field("time_base", &self.time_base())
46            .field("width", &self.width())
47            .field("height", &self.height())
48            .field("pixel_format", &self.pixel_format())
49            .field("frame_rate", &self.frame_rate())
50            .field("sample_aspect_ratio", &self.sample_aspect_ratio())
51            .finish()
52    }
53}
54
55/// An audio decoder.
56pub struct AudioDecoder(GenericDecoder);
57
58impl std::fmt::Debug for AudioDecoder {
59    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60        f.debug_struct("AudioDecoder")
61            .field("time_base", &self.time_base())
62            .field("sample_rate", &self.sample_rate())
63            .field("channels", &self.channels())
64            .field("sample_fmt", &self.sample_format())
65            .finish()
66    }
67}
68
69/// Options for creating a [`Decoder`].
70pub struct DecoderOptions {
71    /// The codec to use for decoding.
72    pub codec: Option<DecoderCodec>,
73    /// The number of threads to use for decoding.
74    pub thread_count: i32,
75}
76
77/// The default options for a [`Decoder`].
78impl Default for DecoderOptions {
79    fn default() -> Self {
80        Self {
81            codec: None,
82            thread_count: 1,
83        }
84    }
85}
86
87impl Decoder {
88    /// Creates a new [`Decoder`] with the default options.
89    pub fn new(ist: &Stream) -> Result<Self, FfmpegError> {
90        Self::with_options(ist, Default::default())
91    }
92
93    /// Creates a new [`Decoder`] with the given options.
94    pub fn with_options(ist: &Stream, options: DecoderOptions) -> Result<Self, FfmpegError> {
95        let Some(codec_params) = ist.codec_parameters() else {
96            return Err(FfmpegError::NoDecoder);
97        };
98
99        let codec = options
100            .codec
101            .or_else(|| DecoderCodec::new(AVCodecID(codec_params.codec_id as i32)))
102            .ok_or(FfmpegError::NoDecoder)?;
103
104        if codec.is_empty() {
105            return Err(FfmpegError::NoDecoder);
106        }
107
108        // Safety: `avcodec_alloc_context3` is safe to call and all arguments are valid.
109        let decoder = unsafe { avcodec_alloc_context3(codec.as_ptr()) };
110
111        let destructor = |ptr: &mut *mut AVCodecContext| {
112            // Safety: The pointer here is valid.
113            unsafe { avcodec_free_context(ptr) };
114        };
115
116        // Safety: `decoder` is a valid pointer, and `destructor` has been setup to free the context.
117        let mut decoder = unsafe { SmartPtr::wrap_non_null(decoder, destructor) }.ok_or(FfmpegError::Alloc)?;
118
119        // Safety: `codec_params` is a valid pointer, and `decoder` is a valid pointer.
120        FfmpegErrorCode(unsafe { avcodec_parameters_to_context(decoder.as_mut_ptr(), codec_params) }).result()?;
121
122        let decoder_mut = decoder.as_deref_mut_except();
123
124        decoder_mut.pkt_timebase = ist.time_base().into();
125        decoder_mut.time_base = ist.time_base().into();
126        decoder_mut.thread_count = options.thread_count;
127
128        if AVMediaType(decoder_mut.codec_type) == AVMediaType::Video {
129            // Safety: Even though we are upcasting `AVFormatContext` from a const pointer to a
130            // mutable pointer, it is still safe becasuse av_guess_frame_rate does not use
131            // the pointer to modify the `AVFormatContext`. https://github.com/FFmpeg/FFmpeg/blame/268d0b6527cba1ebac1f44347578617341f85c35/libavformat/avformat.c#L763
132            // The function does not use the pointer at all, it only uses the `AVStream`
133            // pointer to get the `AVRational`
134            let format_context = unsafe { ist.format_context() };
135
136            decoder_mut.framerate =
137                // Safety: See above.
138                unsafe { av_guess_frame_rate(format_context, ist.as_ptr() as *mut AVStream, std::ptr::null_mut()) };
139        }
140
141        if matches!(AVMediaType(decoder_mut.codec_type), AVMediaType::Video | AVMediaType::Audio) {
142            // Safety: `codec` is a valid pointer, and `decoder` is a valid pointer.
143            FfmpegErrorCode(unsafe { avcodec_open2(decoder_mut, codec.as_ptr(), std::ptr::null_mut()) }).result()?;
144        }
145
146        Ok(match AVMediaType(decoder_mut.codec_type) {
147            AVMediaType::Video => Self::Video(VideoDecoder(GenericDecoder { decoder })),
148            AVMediaType::Audio => Self::Audio(AudioDecoder(GenericDecoder { decoder })),
149            _ => Err(FfmpegError::NoDecoder)?,
150        })
151    }
152
153    /// Returns the video decoder if the decoder is a video decoder.
154    pub fn video(self) -> Result<VideoDecoder, Self> {
155        match self {
156            Self::Video(video) => Ok(video),
157            _ => Err(self),
158        }
159    }
160
161    /// Returns the audio decoder if the decoder is an audio decoder.
162    pub fn audio(self) -> Result<AudioDecoder, Self> {
163        match self {
164            Self::Audio(audio) => Ok(audio),
165            _ => Err(self),
166        }
167    }
168}
169
170impl GenericDecoder {
171    /// Returns the codec type of the decoder.
172    pub const fn codec_type(&self) -> AVMediaType {
173        AVMediaType(self.decoder.as_deref_except().codec_type)
174    }
175
176    /// Returns the time base of the decoder.
177    pub const fn time_base(&self) -> AVRational {
178        self.decoder.as_deref_except().time_base
179    }
180
181    /// Sends a packet to the decoder.
182    pub fn send_packet(&mut self, packet: &Packet) -> Result<(), FfmpegError> {
183        // Safety: `packet` is a valid pointer, and `self.decoder` is a valid pointer.
184        FfmpegErrorCode(unsafe { avcodec_send_packet(self.decoder.as_mut_ptr(), packet.as_ptr()) }).result()?;
185        Ok(())
186    }
187
188    /// Sends an end-of-file packet to the decoder.
189    pub fn send_eof(&mut self) -> Result<(), FfmpegError> {
190        // Safety: `self.decoder` is a valid pointer.
191        FfmpegErrorCode(unsafe { avcodec_send_packet(self.decoder.as_mut_ptr(), std::ptr::null()) }).result()?;
192        Ok(())
193    }
194
195    /// Receives a frame from the decoder.
196    pub fn receive_frame(&mut self) -> Result<Option<GenericFrame>, FfmpegError> {
197        let mut frame = GenericFrame::new()?;
198
199        // Safety: `frame` is a valid pointer, and `self.decoder` is a valid pointer.
200        let ret = FfmpegErrorCode(unsafe { avcodec_receive_frame(self.decoder.as_mut_ptr(), frame.as_mut_ptr()) });
201
202        match ret {
203            FfmpegErrorCode::Eagain | FfmpegErrorCode::Eof => Ok(None),
204            code if code.is_success() => {
205                frame.set_time_base(self.decoder.as_deref_except().time_base);
206                Ok(Some(frame))
207            }
208            code => Err(FfmpegError::Code(code)),
209        }
210    }
211}
212
213impl VideoDecoder {
214    /// Returns the width of the video frame.
215    pub const fn width(&self) -> i32 {
216        self.0.decoder.as_deref_except().width
217    }
218
219    /// Returns the height of the video frame.
220    pub const fn height(&self) -> i32 {
221        self.0.decoder.as_deref_except().height
222    }
223
224    /// Returns the pixel format of the video frame.
225    pub const fn pixel_format(&self) -> AVPixelFormat {
226        AVPixelFormat(self.0.decoder.as_deref_except().pix_fmt)
227    }
228
229    /// Returns the frame rate of the video frame.
230    pub fn frame_rate(&self) -> Rational {
231        self.0.decoder.as_deref_except().framerate.into()
232    }
233
234    /// Returns the sample aspect ratio of the video frame.
235    pub fn sample_aspect_ratio(&self) -> Rational {
236        self.0.decoder.as_deref_except().sample_aspect_ratio.into()
237    }
238
239    /// Receives a frame from the decoder.
240    pub fn receive_frame(&mut self) -> Result<Option<VideoFrame>, FfmpegError> {
241        Ok(self.0.receive_frame()?.map(|frame| frame.video()))
242    }
243}
244
245impl std::ops::Deref for VideoDecoder {
246    type Target = GenericDecoder;
247
248    fn deref(&self) -> &Self::Target {
249        &self.0
250    }
251}
252
253impl std::ops::DerefMut for VideoDecoder {
254    fn deref_mut(&mut self) -> &mut Self::Target {
255        &mut self.0
256    }
257}
258
259impl AudioDecoder {
260    /// Returns the sample rate of the audio frame.
261    pub const fn sample_rate(&self) -> i32 {
262        self.0.decoder.as_deref_except().sample_rate
263    }
264
265    /// Returns the number of channels in the audio frame.
266    pub const fn channels(&self) -> i32 {
267        self.0.decoder.as_deref_except().ch_layout.nb_channels
268    }
269
270    /// Returns the sample format of the audio frame.
271    pub const fn sample_format(&self) -> AVSampleFormat {
272        AVSampleFormat(self.0.decoder.as_deref_except().sample_fmt)
273    }
274
275    /// Receives a frame from the decoder.
276    pub fn receive_frame(&mut self) -> Result<Option<AudioFrame>, FfmpegError> {
277        Ok(self.0.receive_frame()?.map(|frame| frame.audio()))
278    }
279}
280
281impl std::ops::Deref for AudioDecoder {
282    type Target = GenericDecoder;
283
284    fn deref(&self) -> &Self::Target {
285        &self.0
286    }
287}
288
289impl std::ops::DerefMut for AudioDecoder {
290    fn deref_mut(&mut self) -> &mut Self::Target {
291        &mut self.0
292    }
293}
294
295#[cfg(test)]
296#[cfg_attr(all(test, coverage_nightly), coverage(off))]
297mod tests {
298    use crate::codec::DecoderCodec;
299    use crate::decoder::{Decoder, DecoderOptions};
300    use crate::io::Input;
301    use crate::{AVCodecID, AVMediaType};
302
303    #[test]
304    fn test_generic_decoder_debug() {
305        let valid_file_path = "../../assets/avc_aac_large.mp4";
306        let input = Input::open(valid_file_path).expect("Failed to open valid file");
307        let streams = input.streams();
308        let stream = streams
309            .iter()
310            .find(|s| {
311                s.codec_parameters()
312                    .map(|p| AVMediaType(p.codec_type) == AVMediaType::Video)
313                    .unwrap_or(false)
314            })
315            .expect("No video stream found");
316        let codec_params = stream.codec_parameters().expect("Missing codec parameters");
317        assert_eq!(
318            AVMediaType(codec_params.codec_type),
319            AVMediaType::Video,
320            "Expected the stream to be a video stream"
321        );
322        let decoder_options = DecoderOptions {
323            codec: Some(DecoderCodec::new(AVCodecID::H264).expect("Failed to find H264 codec")),
324            thread_count: 2,
325        };
326        let decoder = Decoder::with_options(&stream, decoder_options).expect("Failed to create Decoder");
327        let generic_decoder = match decoder {
328            Decoder::Video(video_decoder) => video_decoder.0,
329            Decoder::Audio(audio_decoder) => audio_decoder.0,
330        };
331
332        insta::assert_debug_snapshot!(generic_decoder, @r"
333        Decoder {
334            time_base: AVRational {
335                num: 1,
336                den: 15360,
337            },
338            codec_type: AVMediaType::Video,
339        }
340        ");
341    }
342
343    #[test]
344    fn test_video_decoder_debug() {
345        let valid_file_path = "../../assets/avc_aac_large.mp4";
346        let input = Input::open(valid_file_path).expect("Failed to open valid file");
347        let streams = input.streams();
348        let stream = streams
349            .iter()
350            .find(|s| {
351                s.codec_parameters()
352                    .map(|p| AVMediaType(p.codec_type) == AVMediaType::Video)
353                    .unwrap_or(false)
354            })
355            .expect("No video stream found");
356        let codec_params = stream.codec_parameters().expect("Missing codec parameters");
357        assert_eq!(
358            AVMediaType(codec_params.codec_type),
359            AVMediaType::Video,
360            "Expected the stream to be a video stream"
361        );
362
363        let decoder_options = DecoderOptions {
364            codec: Some(DecoderCodec::new(AVCodecID::H264).expect("Failed to find H264 codec")),
365            thread_count: 2,
366        };
367        let decoder = Decoder::with_options(&stream, decoder_options).expect("Failed to create Decoder");
368
369        let generic_decoder = match decoder {
370            Decoder::Video(video_decoder) => video_decoder,
371            _ => panic!("Expected a video decoder, got something else"),
372        };
373
374        insta::assert_debug_snapshot!(generic_decoder, @r"
375        VideoDecoder {
376            time_base: AVRational {
377                num: 1,
378                den: 15360,
379            },
380            width: 3840,
381            height: 2160,
382            pixel_format: AVPixelFormat::Yuv420p,
383            frame_rate: Rational {
384                numerator: 60,
385                denominator: 1,
386            },
387            sample_aspect_ratio: Rational {
388                numerator: 1,
389                denominator: 1,
390            },
391        }
392        ");
393    }
394
395    #[test]
396    fn test_audio_decoder_debug() {
397        let valid_file_path = "../../assets/avc_aac_large.mp4";
398        let input = Input::open(valid_file_path).expect("Failed to open valid file");
399        let streams = input.streams();
400        let stream = streams
401            .iter()
402            .find(|s| {
403                s.codec_parameters()
404                    .map(|p| AVMediaType(p.codec_type) == AVMediaType::Audio)
405                    .unwrap_or(false)
406            })
407            .expect("No audio stream found");
408        let codec_params = stream.codec_parameters().expect("Missing codec parameters");
409        assert_eq!(
410            AVMediaType(codec_params.codec_type),
411            AVMediaType::Audio,
412            "Expected the stream to be an audio stream"
413        );
414        let decoder_options = DecoderOptions {
415            codec: Some(DecoderCodec::new(AVCodecID::Aac).expect("Failed to find AAC codec")),
416            thread_count: 2,
417        };
418        let decoder = Decoder::with_options(&stream, decoder_options).expect("Failed to create Decoder");
419        let audio_decoder = match decoder {
420            Decoder::Audio(audio_decoder) => audio_decoder,
421            _ => panic!("Expected an audio decoder, got something else"),
422        };
423
424        insta::assert_debug_snapshot!(audio_decoder, @r"
425        AudioDecoder {
426            time_base: AVRational {
427                num: 1,
428                den: 48000,
429            },
430            sample_rate: 48000,
431            channels: 2,
432            sample_fmt: AVSampleFormat::Fltp,
433        }
434        ");
435    }
436
437    #[test]
438    fn test_decoder_options_default() {
439        let default_options = DecoderOptions::default();
440
441        assert!(default_options.codec.is_none(), "Expected default codec to be None");
442        assert_eq!(default_options.thread_count, 1, "Expected default thread_count to be 1");
443    }
444
445    #[test]
446    fn test_decoder_new() {
447        let valid_file_path = "../../assets/avc_aac_large.mp4";
448        let input = Input::open(valid_file_path).expect("Failed to open valid file");
449        let streams = input.streams();
450        let stream = streams
451            .iter()
452            .find(|s| {
453                s.codec_parameters()
454                    .map(|p| AVMediaType(p.codec_type) == AVMediaType::Video)
455                    .unwrap_or(false)
456            })
457            .expect("No video stream found");
458
459        let decoder_result = Decoder::new(&stream);
460        assert!(decoder_result.is_ok(), "Expected Decoder::new to succeed, but it failed");
461
462        let decoder = decoder_result.unwrap();
463        if let Decoder::Video(video_decoder) = decoder {
464            assert_eq!(video_decoder.width(), 3840, "Expected valid width for video stream");
465            assert_eq!(video_decoder.height(), 2160, "Expected valid height for video stream");
466        } else {
467            panic!("Expected a video decoder, but got a different type");
468        }
469    }
470
471    #[test]
472    fn test_decoder_with_options_missing_codec_parameters() {
473        let valid_file_path = "../../assets/avc_aac_large.mp4";
474        let mut input = Input::open(valid_file_path).expect("Failed to open valid file");
475        let mut streams = input.streams_mut();
476        let mut stream = streams.get(0).expect("Expected a valid stream");
477        // Safety: Stream is a valid pointer.
478        let codecpar = unsafe { (*stream.as_mut_ptr()).codecpar };
479        // Safety: We are setting the `codecpar` to `null` to simulate a missing codec parameters.
480        unsafe {
481            (*stream.as_mut_ptr()).codecpar = std::ptr::null_mut();
482        }
483        let decoder_result = Decoder::with_options(&stream, DecoderOptions::default());
484        // Safety: Stream is a valid pointer.
485        unsafe {
486            (*stream.as_mut_ptr()).codecpar = codecpar;
487        }
488
489        assert!(decoder_result.is_err(), "Expected Decoder creation to fail");
490        if let Err(err) = decoder_result {
491            match err {
492                crate::error::FfmpegError::NoDecoder => (),
493                _ => panic!("Unexpected error type: {:?}", err),
494            }
495        }
496    }
497
498    #[test]
499    fn test_decoder_with_options_non_video_audio_codec_type() {
500        let valid_file_path = "../../assets/avc_aac_large.mp4";
501        let mut input = Input::open(valid_file_path).expect("Failed to open valid file");
502        let mut streams = input.streams_mut();
503        let mut stream = streams.get(0).expect("Expected a valid stream");
504        // Safety: We are setting the `codecpar` to `null` to simulate a missing codec parameters.
505        let codecpar = unsafe { (*stream.as_mut_ptr()).codecpar };
506        // Safety: We are setting the `codec_type` to `AVMEDIA_TYPE_SUBTITLE` to simulate a non-video/audio codec type.
507        unsafe {
508            (*codecpar).codec_type = AVMediaType::Subtitle.into();
509        }
510        let decoder_result = Decoder::with_options(&stream, DecoderOptions::default());
511
512        assert!(
513            decoder_result.is_err(),
514            "Expected Decoder creation to fail for non-video/audio codec type"
515        );
516        if let Err(err) = decoder_result {
517            match err {
518                crate::error::FfmpegError::NoDecoder => (),
519                _ => panic!("Unexpected error type: {:?}", err),
520            }
521        }
522    }
523
524    #[test]
525    fn test_video_decoder_deref_mut_safe() {
526        let valid_file_path = "../../assets/avc_aac_large.mp4";
527        let input = Input::open(valid_file_path).expect("Failed to open valid file");
528        let streams = input.streams();
529        let stream = streams
530            .iter()
531            .find(|s| {
532                s.codec_parameters()
533                    .map(|p| AVMediaType(p.codec_type) == AVMediaType::Video)
534                    .unwrap_or(false)
535            })
536            .expect("No video stream found");
537        let decoder_options = DecoderOptions {
538            codec: None,
539            thread_count: 2,
540        };
541        let decoder = Decoder::with_options(&stream, decoder_options).expect("Failed to create Decoder");
542        let mut video_decoder = match decoder {
543            Decoder::Video(video_decoder) => video_decoder,
544            _ => panic!("Expected a VideoDecoder, got something else"),
545        };
546        {
547            let generic_decoder = &mut *video_decoder;
548            let mut time_base = generic_decoder.time_base();
549            time_base.num = 1000;
550            time_base.den = 1;
551            generic_decoder.decoder.as_deref_mut_except().time_base = time_base;
552        }
553        let generic_decoder = &*video_decoder;
554        let time_base = generic_decoder.decoder.as_deref_except().time_base;
555
556        assert_eq!(time_base.num, 1000, "Expected time_base.num to be updated via DerefMut");
557        assert_eq!(time_base.den, 1, "Expected time_base.den to be updated via DerefMut");
558    }
559
560    #[test]
561    fn test_audio_decoder_deref_mut() {
562        let valid_file_path = "../../assets/avc_aac_large.mp4";
563        let input = Input::open(valid_file_path).expect("Failed to open valid file");
564        let streams = input.streams();
565        let stream = streams
566            .iter()
567            .find(|s| {
568                s.codec_parameters()
569                    .map(|p| AVMediaType(p.codec_type) == AVMediaType::Audio)
570                    .unwrap_or(false)
571            })
572            .expect("No audio stream found");
573        let decoder_options = DecoderOptions {
574            codec: None,
575            thread_count: 2,
576        };
577        let decoder = Decoder::with_options(&stream, decoder_options).expect("Failed to create Decoder");
578        let mut audio_decoder = match decoder {
579            Decoder::Audio(audio_decoder) => audio_decoder,
580            _ => panic!("Expected an AudioDecoder, got something else"),
581        };
582        {
583            let generic_decoder = &mut *audio_decoder;
584            let mut time_base = generic_decoder.time_base();
585            time_base.num = 48000;
586            time_base.den = 1;
587            generic_decoder.decoder.as_deref_mut_except().time_base = time_base;
588        }
589        let generic_decoder = &*audio_decoder;
590        let time_base = generic_decoder.decoder.as_deref_except().time_base;
591
592        assert_eq!(time_base.num, 48000, "Expected time_base.num to be updated via DerefMut");
593        assert_eq!(time_base.den, 1, "Expected time_base.den to be updated via DerefMut");
594    }
595
596    #[test]
597    fn test_decoder_video() {
598        let valid_file_path = "../../assets/avc_aac_large.mp4";
599        let mut input = Input::open(valid_file_path).expect("Failed to open valid file");
600        let streams = input.streams();
601        let video_stream = streams.best(AVMediaType::Video).expect("No video stream found");
602        let audio_stream = streams.best(AVMediaType::Audio).expect("No audio stream found");
603        let mut video_decoder = Decoder::new(&video_stream)
604            .expect("Failed to create decoder")
605            .video()
606            .expect("Failed to get video decoder");
607        let mut audio_decoder = Decoder::new(&audio_stream)
608            .expect("Failed to create decoder")
609            .audio()
610            .expect("Failed to get audio decoder");
611        let mut video_frames = Vec::new();
612        let mut audio_frames = Vec::new();
613
614        let video_stream_index = video_stream.index();
615        let audio_stream_index = audio_stream.index();
616
617        while let Some(packet) = input.receive_packet().expect("Failed to receive packet") {
618            if packet.stream_index() == video_stream_index {
619                video_decoder.send_packet(&packet).expect("Failed to send packet");
620                while let Some(frame) = video_decoder.receive_frame().expect("Failed to receive frame") {
621                    video_frames.push(frame);
622                }
623            } else if packet.stream_index() == audio_stream_index {
624                audio_decoder.send_packet(&packet).expect("Failed to send packet");
625                while let Some(frame) = audio_decoder.receive_frame().expect("Failed to receive frame") {
626                    audio_frames.push(frame);
627                }
628            }
629        }
630
631        video_decoder.send_eof().expect("Failed to send eof");
632        while let Some(frame) = video_decoder.receive_frame().expect("Failed to receive frame") {
633            video_frames.push(frame);
634        }
635
636        audio_decoder.send_eof().expect("Failed to send eof");
637        while let Some(frame) = audio_decoder.receive_frame().expect("Failed to receive frame") {
638            audio_frames.push(frame);
639        }
640
641        insta::assert_debug_snapshot!("test_decoder_video", video_frames);
642        insta::assert_debug_snapshot!("test_decoder_audio", audio_frames);
643    }
644}