scuffle_ffmpeg/
decoder.rs

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