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};
310
311    #[test]
312    fn test_generic_decoder_debug() {
313        let valid_file_path = "../../assets/avc_aac_large.mp4";
314        let input = Input::open(valid_file_path).expect("Failed to open valid file");
315        let streams = input.streams();
316        let stream = streams
317            .iter()
318            .find(|s| {
319                s.codec_parameters()
320                    .map(|p| AVMediaType(p.codec_type) == AVMediaType::Video)
321                    .unwrap_or(false)
322            })
323            .expect("No video stream found");
324        let codec_params = stream.codec_parameters().expect("Missing codec parameters");
325        assert_eq!(
326            AVMediaType(codec_params.codec_type),
327            AVMediaType::Video,
328            "Expected the stream to be a video stream"
329        );
330        let decoder_options = DecoderOptions {
331            codec: Some(DecoderCodec::new(AVCodecID::H264).expect("Failed to find H264 codec")),
332            thread_count: 2,
333        };
334        let decoder = Decoder::with_options(&stream, decoder_options).expect("Failed to create Decoder");
335        let generic_decoder = match decoder {
336            Decoder::Video(video_decoder) => video_decoder.0,
337            Decoder::Audio(audio_decoder) => audio_decoder.0,
338        };
339
340        insta::assert_debug_snapshot!(generic_decoder, @r"
341        Decoder {
342            time_base: Some(
343                Rational {
344                    numerator: 1,
345                    denominator: 15360,
346                },
347            ),
348            codec_type: AVMediaType::Video,
349        }
350        ");
351    }
352
353    #[test]
354    fn test_video_decoder_debug() {
355        let valid_file_path = "../../assets/avc_aac_large.mp4";
356        let input = Input::open(valid_file_path).expect("Failed to open valid file");
357        let streams = input.streams();
358        let stream = streams
359            .iter()
360            .find(|s| {
361                s.codec_parameters()
362                    .map(|p| AVMediaType(p.codec_type) == AVMediaType::Video)
363                    .unwrap_or(false)
364            })
365            .expect("No video stream found");
366        let codec_params = stream.codec_parameters().expect("Missing codec parameters");
367        assert_eq!(
368            AVMediaType(codec_params.codec_type),
369            AVMediaType::Video,
370            "Expected the stream to be a video stream"
371        );
372
373        let decoder_options = DecoderOptions {
374            codec: Some(DecoderCodec::new(AVCodecID::H264).expect("Failed to find H264 codec")),
375            thread_count: 2,
376        };
377        let decoder = Decoder::with_options(&stream, decoder_options).expect("Failed to create Decoder");
378
379        let generic_decoder = match decoder {
380            Decoder::Video(video_decoder) => video_decoder,
381            _ => panic!("Expected a video decoder, got something else"),
382        };
383
384        insta::assert_debug_snapshot!(generic_decoder, @r"
385        VideoDecoder {
386            time_base: Some(
387                Rational {
388                    numerator: 1,
389                    denominator: 15360,
390                },
391            ),
392            width: 3840,
393            height: 2160,
394            pixel_format: AVPixelFormat::Yuv420p,
395            frame_rate: Rational {
396                numerator: 60,
397                denominator: 1,
398            },
399            sample_aspect_ratio: Rational {
400                numerator: 1,
401                denominator: 1,
402            },
403        }
404        ");
405    }
406
407    #[test]
408    fn test_audio_decoder_debug() {
409        let valid_file_path = "../../assets/avc_aac_large.mp4";
410        let input = Input::open(valid_file_path).expect("Failed to open valid file");
411        let streams = input.streams();
412        let stream = streams
413            .iter()
414            .find(|s| {
415                s.codec_parameters()
416                    .map(|p| AVMediaType(p.codec_type) == AVMediaType::Audio)
417                    .unwrap_or(false)
418            })
419            .expect("No audio stream found");
420        let codec_params = stream.codec_parameters().expect("Missing codec parameters");
421        assert_eq!(
422            AVMediaType(codec_params.codec_type),
423            AVMediaType::Audio,
424            "Expected the stream to be an audio stream"
425        );
426        let decoder_options = DecoderOptions {
427            codec: Some(DecoderCodec::new(AVCodecID::Aac).expect("Failed to find AAC codec")),
428            thread_count: 2,
429        };
430        let decoder = Decoder::with_options(&stream, decoder_options).expect("Failed to create Decoder");
431        let audio_decoder = match decoder {
432            Decoder::Audio(audio_decoder) => audio_decoder,
433            _ => panic!("Expected an audio decoder, got something else"),
434        };
435
436        insta::assert_debug_snapshot!(audio_decoder, @r"
437        AudioDecoder {
438            time_base: Some(
439                Rational {
440                    numerator: 1,
441                    denominator: 48000,
442                },
443            ),
444            sample_rate: 48000,
445            channels: 2,
446            sample_fmt: AVSampleFormat::Fltp,
447        }
448        ");
449    }
450
451    #[test]
452    fn test_decoder_options_default() {
453        let default_options = DecoderOptions::default();
454
455        assert!(default_options.codec.is_none(), "Expected default codec to be None");
456        assert_eq!(default_options.thread_count, 1, "Expected default thread_count to be 1");
457    }
458
459    #[test]
460    fn test_decoder_new() {
461        let valid_file_path = "../../assets/avc_aac_large.mp4";
462        let input = Input::open(valid_file_path).expect("Failed to open valid file");
463        let streams = input.streams();
464        let stream = streams
465            .iter()
466            .find(|s| {
467                s.codec_parameters()
468                    .map(|p| AVMediaType(p.codec_type) == AVMediaType::Video)
469                    .unwrap_or(false)
470            })
471            .expect("No video stream found");
472
473        let decoder_result = Decoder::new(&stream);
474        assert!(decoder_result.is_ok(), "Expected Decoder::new to succeed, but it failed");
475
476        let decoder = decoder_result.unwrap();
477        if let Decoder::Video(video_decoder) = decoder {
478            assert_eq!(video_decoder.width(), 3840, "Expected valid width for video stream");
479            assert_eq!(video_decoder.height(), 2160, "Expected valid height for video stream");
480        } else {
481            panic!("Expected a video decoder, but got a different type");
482        }
483    }
484
485    #[test]
486    fn test_decoder_with_options_missing_codec_parameters() {
487        let valid_file_path = "../../assets/avc_aac_large.mp4";
488        let mut input = Input::open(valid_file_path).expect("Failed to open valid file");
489        let mut streams = input.streams_mut();
490        let mut stream = streams.get(0).expect("Expected a valid stream");
491        // Safety: Stream is a valid pointer.
492        let codecpar = unsafe { (*stream.as_mut_ptr()).codecpar };
493        // Safety: We are setting the `codecpar` to `null` to simulate a missing codec parameters.
494        unsafe {
495            (*stream.as_mut_ptr()).codecpar = std::ptr::null_mut();
496        }
497        let decoder_result = Decoder::with_options(&stream, DecoderOptions::default());
498        // Safety: Stream is a valid pointer.
499        unsafe {
500            (*stream.as_mut_ptr()).codecpar = codecpar;
501        }
502
503        assert!(decoder_result.is_err(), "Expected Decoder creation to fail");
504        if let Err(err) = decoder_result {
505            match err {
506                crate::error::FfmpegError::NoDecoder => (),
507                _ => panic!("Unexpected error type: {err:?}"),
508            }
509        }
510    }
511
512    #[test]
513    fn test_decoder_with_options_non_video_audio_codec_type() {
514        let valid_file_path = "../../assets/avc_aac_large.mp4";
515        let mut input = Input::open(valid_file_path).expect("Failed to open valid file");
516        let mut streams = input.streams_mut();
517        let mut stream = streams.get(0).expect("Expected a valid stream");
518        // Safety: We are setting the `codecpar` to `null` to simulate a missing codec parameters.
519        let codecpar = unsafe { (*stream.as_mut_ptr()).codecpar };
520        // Safety: We are setting the `codec_type` to `AVMEDIA_TYPE_SUBTITLE` to simulate a non-video/audio codec type.
521        unsafe {
522            (*codecpar).codec_type = AVMediaType::Subtitle.into();
523        }
524        let decoder_result = Decoder::with_options(&stream, DecoderOptions::default());
525
526        assert!(
527            decoder_result.is_err(),
528            "Expected Decoder creation to fail for non-video/audio codec type"
529        );
530        if let Err(err) = decoder_result {
531            match err {
532                crate::error::FfmpegError::NoDecoder => (),
533                _ => panic!("Unexpected error type: {err:?}"),
534            }
535        }
536    }
537
538    #[test]
539    fn test_video_decoder_deref_mut_safe() {
540        let valid_file_path = "../../assets/avc_aac_large.mp4";
541        let input = Input::open(valid_file_path).expect("Failed to open valid file");
542        let streams = input.streams();
543        let stream = streams
544            .iter()
545            .find(|s| {
546                s.codec_parameters()
547                    .map(|p| AVMediaType(p.codec_type) == AVMediaType::Video)
548                    .unwrap_or(false)
549            })
550            .expect("No video stream found");
551        let decoder_options = DecoderOptions {
552            codec: None,
553            thread_count: 2,
554        };
555        let decoder = Decoder::with_options(&stream, decoder_options).expect("Failed to create Decoder");
556        let mut video_decoder = match decoder {
557            Decoder::Video(video_decoder) => video_decoder,
558            _ => panic!("Expected a VideoDecoder, got something else"),
559        };
560        {
561            let generic_decoder = &mut *video_decoder;
562            let mut time_base = generic_decoder.time_base().expect("Failed to get time base of decoder");
563            time_base.numerator = 1000;
564            time_base.denominator = NonZero::new(1).unwrap();
565            generic_decoder.decoder.as_deref_mut_except().time_base = time_base.into();
566        }
567        let generic_decoder = &*video_decoder;
568        let time_base = generic_decoder.decoder.as_deref_except().time_base;
569
570        assert_eq!(time_base.num, 1000, "Expected time_base.num to be updated via DerefMut");
571        assert_eq!(time_base.den, 1, "Expected time_base.den to be updated via DerefMut");
572    }
573
574    #[test]
575    fn test_audio_decoder_deref_mut() {
576        let valid_file_path = "../../assets/avc_aac_large.mp4";
577        let input = Input::open(valid_file_path).expect("Failed to open valid file");
578        let streams = input.streams();
579        let stream = streams
580            .iter()
581            .find(|s| {
582                s.codec_parameters()
583                    .map(|p| AVMediaType(p.codec_type) == AVMediaType::Audio)
584                    .unwrap_or(false)
585            })
586            .expect("No audio stream found");
587        let decoder_options = DecoderOptions {
588            codec: None,
589            thread_count: 2,
590        };
591        let decoder = Decoder::with_options(&stream, decoder_options).expect("Failed to create Decoder");
592        let mut audio_decoder = match decoder {
593            Decoder::Audio(audio_decoder) => audio_decoder,
594            _ => panic!("Expected an AudioDecoder, got something else"),
595        };
596        {
597            let generic_decoder = &mut *audio_decoder;
598            let mut time_base = generic_decoder.time_base().expect("Failed to get time base of decoder");
599            time_base.numerator = 48000;
600            time_base.denominator = NonZero::new(1).unwrap();
601            generic_decoder.decoder.as_deref_mut_except().time_base = time_base.into();
602        }
603        let generic_decoder = &*audio_decoder;
604        let time_base = generic_decoder.decoder.as_deref_except().time_base;
605
606        assert_eq!(time_base.num, 48000, "Expected time_base.num to be updated via DerefMut");
607        assert_eq!(time_base.den, 1, "Expected time_base.den to be updated via DerefMut");
608    }
609
610    #[test]
611    fn test_decoder_video() {
612        let valid_file_path = "../../assets/avc_aac_large.mp4";
613        let mut input = Input::open(valid_file_path).expect("Failed to open valid file");
614        let streams = input.streams();
615        let video_stream = streams.best(AVMediaType::Video).expect("No video stream found");
616        let audio_stream = streams.best(AVMediaType::Audio).expect("No audio stream found");
617        let mut video_decoder = Decoder::new(&video_stream)
618            .expect("Failed to create decoder")
619            .video()
620            .expect("Failed to get video decoder");
621        let mut audio_decoder = Decoder::new(&audio_stream)
622            .expect("Failed to create decoder")
623            .audio()
624            .expect("Failed to get audio decoder");
625        let mut video_frames = Vec::new();
626        let mut audio_frames = Vec::new();
627
628        let video_stream_index = video_stream.index();
629        let audio_stream_index = audio_stream.index();
630
631        while let Some(packet) = input.receive_packet().expect("Failed to receive packet") {
632            if packet.stream_index() == video_stream_index {
633                video_decoder.send_packet(&packet).expect("Failed to send packet");
634                while let Some(frame) = video_decoder.receive_frame().expect("Failed to receive frame") {
635                    video_frames.push(frame);
636                }
637            } else if packet.stream_index() == audio_stream_index {
638                audio_decoder.send_packet(&packet).expect("Failed to send packet");
639                while let Some(frame) = audio_decoder.receive_frame().expect("Failed to receive frame") {
640                    audio_frames.push(frame);
641                }
642            }
643        }
644
645        video_decoder.send_eof().expect("Failed to send eof");
646        while let Some(frame) = video_decoder.receive_frame().expect("Failed to receive frame") {
647            video_frames.push(frame);
648        }
649
650        audio_decoder.send_eof().expect("Failed to send eof");
651        while let Some(frame) = audio_decoder.receive_frame().expect("Failed to receive frame") {
652            audio_frames.push(frame);
653        }
654
655        insta::assert_debug_snapshot!("test_decoder_video", video_frames);
656        insta::assert_debug_snapshot!("test_decoder_audio", audio_frames);
657    }
658}