1use crate::error::{FfmpegError, FfmpegErrorCode};
2use crate::ffi::*;
3use crate::rational::Rational;
4use crate::smart_object::{SmartObject, SmartPtr};
5use crate::utils::{check_i64, or_nopts};
6use crate::{AVPictureType, AVPixelFormat};
7
8pub struct GenericFrame(SmartPtr<AVFrame>);
10
11impl Clone for GenericFrame {
12 fn clone(&self) -> Self {
13 let clone = unsafe { av_frame_clone(self.0.as_ptr()) };
15
16 unsafe { Self::wrap(clone).expect("failed to clone frame") }
18 }
19}
20
21unsafe impl Send for GenericFrame {}
23
24unsafe impl Sync for GenericFrame {}
26
27#[derive(Clone)]
29pub struct VideoFrame(GenericFrame);
30
31#[derive(Clone)]
33pub struct AudioFrame(GenericFrame);
34
35impl GenericFrame {
36 pub(crate) fn new() -> Result<Self, FfmpegError> {
38 let frame = unsafe { av_frame_alloc() };
40
41 unsafe { Self::wrap(frame).ok_or(FfmpegError::Alloc) }
43 }
44
45 pub(crate) unsafe fn wrap(ptr: *mut AVFrame) -> Option<Self> {
50 SmartPtr::wrap_non_null(ptr, |ptr| av_frame_free(ptr)).map(Self)
51 }
52
53 pub(crate) unsafe fn alloc_frame_buffer(&mut self, alignment: Option<i32>) -> Result<(), FfmpegError> {
60 FfmpegErrorCode(unsafe { av_frame_get_buffer(self.as_mut_ptr(), alignment.unwrap_or(0)) }).result()?;
66 Ok(())
67 }
68
69 pub(crate) const fn as_ptr(&self) -> *const AVFrame {
71 self.0.as_ptr()
72 }
73
74 pub(crate) const fn as_mut_ptr(&mut self) -> *mut AVFrame {
76 self.0.as_mut_ptr()
77 }
78
79 pub(crate) const fn video(self) -> VideoFrame {
81 VideoFrame(self)
82 }
83
84 pub(crate) const fn audio(self) -> AudioFrame {
86 AudioFrame(self)
87 }
88
89 pub(crate) const fn pts(&self) -> Option<i64> {
91 check_i64(self.0.as_deref_except().pts)
92 }
93
94 pub(crate) const fn set_pts(&mut self, pts: Option<i64>) {
96 self.0.as_deref_mut_except().pts = or_nopts(pts);
97 self.0.as_deref_mut_except().best_effort_timestamp = or_nopts(pts);
98 }
99
100 pub(crate) const fn duration(&self) -> Option<i64> {
102 check_i64(self.0.as_deref_except().duration)
103 }
104
105 pub(crate) const fn set_duration(&mut self, duration: Option<i64>) {
107 self.0.as_deref_mut_except().duration = or_nopts(duration);
108 }
109
110 pub(crate) const fn best_effort_timestamp(&self) -> Option<i64> {
112 check_i64(self.0.as_deref_except().best_effort_timestamp)
113 }
114
115 pub(crate) const fn dts(&self) -> Option<i64> {
117 check_i64(self.0.as_deref_except().pkt_dts)
118 }
119
120 pub(crate) const fn set_dts(&mut self, dts: Option<i64>) {
122 self.0.as_deref_mut_except().pkt_dts = or_nopts(dts);
123 }
124
125 pub(crate) fn time_base(&self) -> Rational {
127 self.0.as_deref_except().time_base.into()
128 }
129
130 pub(crate) fn set_time_base(&mut self, time_base: impl Into<Rational>) {
132 self.0.as_deref_mut_except().time_base = time_base.into().into();
133 }
134
135 pub(crate) const fn format(&self) -> i32 {
137 self.0.as_deref_except().format
138 }
139
140 pub(crate) const fn set_format(&mut self, format: i32) {
142 self.0.as_deref_mut_except().format = format;
143 }
144
145 pub(crate) const fn is_audio(&self) -> bool {
147 self.0.as_deref_except().ch_layout.nb_channels != 0
148 }
149
150 pub(crate) const fn is_video(&self) -> bool {
152 self.0.as_deref_except().width != 0
153 }
154
155 pub(crate) const fn linesize(&self, index: usize) -> Option<i32> {
157 if index >= self.0.as_deref_except().linesize.len() {
158 return None;
159 }
160 Some(self.0.as_deref_except().linesize[index])
161 }
162}
163
164impl std::fmt::Debug for GenericFrame {
165 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166 f.debug_struct("GenericFrame")
167 .field("pts", &self.pts())
168 .field("dts", &self.dts())
169 .field("duration", &self.duration())
170 .field("best_effort_timestamp", &self.best_effort_timestamp())
171 .field("time_base", &self.time_base())
172 .field("format", &self.format())
173 .field("is_audio", &self.is_audio())
174 .field("is_video", &self.is_video())
175 .finish()
176 }
177}
178
179impl VideoFrame {
180 pub fn new() -> Result<Self, FfmpegError> {
182 let frame = GenericFrame::new()?;
183 Ok(VideoFrame(frame))
184 }
185
186 pub const fn width(&self) -> usize {
188 self.0 .0.as_deref_except().width as usize
189 }
190
191 pub const fn height(&self) -> usize {
193 self.0 .0.as_deref_except().height as usize
194 }
195
196 pub fn sample_aspect_ratio(&self) -> Rational {
198 self.0 .0.as_deref_except().sample_aspect_ratio.into()
199 }
200
201 pub fn set_sample_aspect_ratio(&mut self, sample_aspect_ratio: impl Into<Rational>) {
203 self.0 .0.as_deref_mut_except().sample_aspect_ratio = sample_aspect_ratio.into().into();
204 }
205
206 pub const fn set_width(&mut self, width: usize) {
208 self.0 .0.as_deref_mut_except().width = width as i32;
209 }
210
211 pub const fn set_height(&mut self, height: usize) {
213 self.0 .0.as_deref_mut_except().height = height as i32;
214 }
215
216 pub const fn is_keyframe(&self) -> bool {
218 self.0 .0.as_deref_except().key_frame != 0
219 }
220
221 pub const fn pict_type(&self) -> AVPictureType {
223 AVPictureType(self.0 .0.as_deref_except().pict_type as i32)
224 }
225
226 pub const fn set_pict_type(&mut self, pict_type: AVPictureType) {
228 self.0 .0.as_deref_mut_except().pict_type = pict_type.0 as u32;
229 }
230
231 pub fn data(&self, index: usize) -> Option<&[u8]> {
233 let line = self.linesize(index)? as usize;
234 let height = self.height();
235 let raw = *self.0 .0.as_deref_except().data.get(index)?;
236
237 unsafe { Some(std::slice::from_raw_parts(raw, line * height)) }
239 }
240
241 pub fn data_mut(&mut self, index: usize) -> Option<&mut [u8]> {
243 let line = self.linesize(index)? as usize;
244 let height = self.height();
245 let raw = *self.0 .0.as_deref_mut_except().data.get(index)?;
246
247 unsafe { Some(std::slice::from_raw_parts_mut(raw, line * height)) }
249 }
250
251 pub const fn format(&self) -> AVPixelFormat {
253 AVPixelFormat(self.0 .0.as_deref_except().format)
254 }
255}
256
257impl std::fmt::Debug for VideoFrame {
258 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
259 f.debug_struct("VideoFrame")
260 .field("width", &self.width())
261 .field("height", &self.height())
262 .field("sample_aspect_ratio", &self.sample_aspect_ratio())
263 .field("pts", &self.pts())
264 .field("dts", &self.dts())
265 .field("duration", &self.duration())
266 .field("best_effort_timestamp", &self.best_effort_timestamp())
267 .field("time_base", &self.time_base())
268 .field("format", &self.format())
269 .field("is_audio", &self.is_audio())
270 .field("is_video", &self.is_video())
271 .field("is_keyframe", &self.is_keyframe())
272 .finish()
273 }
274}
275
276impl std::ops::Deref for VideoFrame {
277 type Target = GenericFrame;
278
279 fn deref(&self) -> &Self::Target {
280 &self.0
281 }
282}
283
284impl std::ops::DerefMut for VideoFrame {
285 fn deref_mut(&mut self) -> &mut Self::Target {
286 &mut self.0
287 }
288}
289
290pub struct AudioChannelLayout(SmartObject<AVChannelLayout>);
292
293impl Default for AudioChannelLayout {
294 fn default() -> Self {
295 let zeroed_layout = unsafe { std::mem::zeroed() };
297
298 Self(SmartObject::new(zeroed_layout, Self::destructor))
299 }
300}
301
302impl AudioChannelLayout {
303 #[doc(hidden)]
304 fn destructor(ptr: &mut AVChannelLayout) {
305 unsafe { av_channel_layout_uninit(ptr) };
307 }
308
309 pub fn new(channels: i32) -> Result<Self, FfmpegError> {
311 let mut layout = Self::default();
312
313 unsafe { av_channel_layout_default(layout.0.as_mut(), channels) };
315
316 layout.validate()?;
317
318 Ok(layout)
319 }
320
321 pub fn validate(&self) -> Result<(), FfmpegError> {
323 if unsafe { av_channel_layout_check(self.0.as_ref()) } == 0 {
325 return Err(FfmpegError::Arguments("invalid channel layout"));
326 }
327
328 Ok(())
329 }
330
331 pub unsafe fn wrap(layout: AVChannelLayout) -> Self {
336 Self(SmartObject::new(layout, Self::destructor))
337 }
338
339 pub fn channel_count(&self) -> i32 {
341 self.0.as_ref().nb_channels
342 }
343
344 pub fn into_inner(self) -> AVChannelLayout {
347 self.0.into_inner()
348 }
349
350 pub(crate) fn apply(mut self, layout: &mut AVChannelLayout) {
351 std::mem::swap(layout, self.0.as_mut());
352 }
353}
354
355impl AudioFrame {
356 pub fn set_channel_layout_default(&mut self, channel_count: usize) -> Result<(), FfmpegError> {
358 let layout = AudioChannelLayout::new(channel_count as i32)?;
359
360 let av_frame = unsafe { self.as_mut_ptr().as_mut() }.ok_or(FfmpegError::Alloc)?;
362
363 layout.apply(&mut av_frame.ch_layout);
364
365 Ok(())
366 }
367
368 pub fn set_channel_layout_custom(&mut self, custom_layout: AudioChannelLayout) -> Result<(), FfmpegError> {
371 custom_layout.validate()?;
372
373 let av_frame = unsafe { self.as_mut_ptr().as_mut() }.ok_or(FfmpegError::Alloc)?;
375
376 custom_layout.apply(&mut av_frame.ch_layout);
377
378 Ok(())
379 }
380
381 pub const fn channel_layout(&self) -> AVChannelLayout {
383 self.0 .0.as_deref_except().ch_layout
384 }
385
386 pub const fn channel_count(&self) -> usize {
388 self.0 .0.as_deref_except().ch_layout.nb_channels as usize
389 }
390
391 pub const fn nb_samples(&self) -> i32 {
393 self.0 .0.as_deref_except().nb_samples
394 }
395
396 pub const fn set_nb_samples(&mut self, nb_samples: usize) {
398 self.0 .0.as_deref_mut_except().nb_samples = nb_samples as i32;
399 }
400
401 pub const fn sample_rate(&self) -> i32 {
403 self.0 .0.as_deref_except().sample_rate
404 }
405
406 pub const fn set_sample_rate(&mut self, sample_rate: usize) {
408 self.0 .0.as_deref_mut_except().sample_rate = sample_rate as i32;
409 }
410}
411
412impl std::fmt::Debug for AudioFrame {
413 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
414 f.debug_struct("AudioFrame")
415 .field("channel_count", &self.channel_count())
416 .field("nb_samples", &self.nb_samples())
417 .field("sample_rate", &self.sample_rate())
418 .field("pts", &self.pts())
419 .field("dts", &self.dts())
420 .field("duration", &self.duration())
421 .field("best_effort_timestamp", &self.best_effort_timestamp())
422 .field("time_base", &self.time_base())
423 .field("format", &self.format())
424 .field("is_audio", &self.is_audio())
425 .field("is_video", &self.is_video())
426 .finish()
427 }
428}
429
430impl std::ops::Deref for AudioFrame {
431 type Target = GenericFrame;
432
433 fn deref(&self) -> &Self::Target {
434 &self.0
435 }
436}
437
438impl std::ops::DerefMut for AudioFrame {
439 fn deref_mut(&mut self) -> &mut Self::Target {
440 &mut self.0
441 }
442}
443
444#[cfg(test)]
445#[cfg_attr(all(test, coverage_nightly), coverage(off))]
446mod tests {
447 use insta::assert_debug_snapshot;
448 use rand::Rng;
449 use rusty_ffmpeg::ffi::AVRational;
450
451 use crate::ffi::av_frame_get_buffer;
452 use crate::frame::{AudioChannelLayout, GenericFrame, VideoFrame};
453 use crate::rational::Rational;
454 use crate::{AVChannelOrder, AVPictureType, AVPixelFormat, AVSampleFormat};
455
456 #[test]
457 fn test_frame_clone() {
458 let mut frame = VideoFrame::new().expect("Failed to create frame");
459 frame.set_format(AVPixelFormat::Yuv420p.into());
460
461 frame.set_width(16);
463 frame.set_height(16);
464
465 unsafe { frame.alloc_frame_buffer(Some(32)) }.expect("Failed to allocate frame buffer");
467
468 frame.set_pts(Some(12));
469 frame.set_dts(Some(34));
470 frame.set_duration(Some(5));
471 frame.set_time_base(Rational::static_new::<1, 30>());
472 frame.set_format(AVPixelFormat::Yuv420p.into());
473
474 let cloned_frame = frame.clone();
475
476 assert_eq!(
477 format!("{:?}", frame),
478 format!("{:?}", cloned_frame),
479 "Cloned frame should be equal to the original frame."
480 );
481 }
482
483 #[test]
484 fn test_audio_conversion() {
485 let frame = GenericFrame::new().expect("Failed to create frame");
486 let mut audio_frame = frame.audio();
487
488 assert!(audio_frame.set_channel_layout_default(2).is_ok());
489 assert!(audio_frame.is_audio(), "The frame should be identified as audio.");
490 assert!(!audio_frame.is_video(), "The frame should not be identified as video.");
491 }
492
493 #[test]
494 fn test_set_format() {
495 let mut frame = GenericFrame::new().expect("Failed to create frame");
496 frame.set_format(AVPixelFormat::Yuv420p.into());
497 assert_eq!(
498 frame.format(),
499 AVPixelFormat::Yuv420p.0,
500 "The format should match the set value."
501 );
502
503 frame.set_format(AVPixelFormat::Rgb24.into());
504 assert_eq!(
505 frame.format(),
506 AVPixelFormat::Rgb24.0,
507 "The format should match the updated value."
508 );
509 }
510
511 #[test]
512 fn test_linesize() {
513 let mut frame = VideoFrame::new().expect("Failed to create frame");
514 frame.set_format(AVPixelFormat::Yuv420p.into());
515 frame.set_width(1920);
516 frame.set_height(1080);
517
518 unsafe { frame.alloc_frame_buffer(Some(32)) }.expect("Failed to allocate frame buffer");
520
521 assert!(
522 frame.linesize(0).unwrap_or(0) > 0,
523 "Linesize should be greater than zero for valid index."
524 );
525
526 assert!(
527 frame.linesize(100).is_none(),
528 "Linesize at an invalid index should return None."
529 );
530 }
531
532 #[test]
533 fn test_frame_debug() {
534 let mut frame = GenericFrame::new().expect("Failed to create frame");
535 frame.set_pts(Some(12345));
536 frame.set_dts(Some(67890));
537 frame.set_duration(Some(1000));
538 frame.set_time_base(Rational::static_new::<1, 30>());
539 frame.set_format(AVPixelFormat::Yuv420p.into());
540
541 assert_debug_snapshot!(frame, @r"
542 GenericFrame {
543 pts: Some(
544 12345,
545 ),
546 dts: Some(
547 67890,
548 ),
549 duration: Some(
550 1000,
551 ),
552 best_effort_timestamp: Some(
553 12345,
554 ),
555 time_base: Rational {
556 numerator: 1,
557 denominator: 30,
558 },
559 format: 0,
560 is_audio: false,
561 is_video: false,
562 }
563 ");
564 }
565
566 #[test]
567 fn test_sample_aspect_ratio() {
568 let frame = GenericFrame::new().expect("Failed to create frame");
569 let mut video_frame = frame.video();
570 let sample_aspect_ratio = Rational::static_new::<16, 9>();
571 video_frame.set_sample_aspect_ratio(sample_aspect_ratio);
572
573 assert_eq!(
574 video_frame.sample_aspect_ratio(),
575 sample_aspect_ratio,
576 "Sample aspect ratio should match the set value."
577 );
578 }
579
580 #[test]
581 fn test_pict_type() {
582 let frame = GenericFrame::new().expect("Failed to create frame");
583 let mut video_frame = frame.video();
584 video_frame.set_pict_type(AVPictureType::Intra);
585
586 assert_eq!(
587 video_frame.pict_type(),
588 AVPictureType::Intra,
589 "Picture type should match the set value."
590 );
591 }
592
593 #[test]
594 fn test_data_allocation_and_access() {
595 let mut frame = GenericFrame::new().expect("Failed to create frame");
596 frame.set_format(AVPixelFormat::Yuv420p.into());
597 let mut video_frame = frame.video();
598 video_frame.set_width(16);
599 video_frame.set_height(16);
600
601 let av_frame = unsafe { video_frame.as_mut_ptr().as_mut() }.expect("Failed to get mutable pointer");
603
604 unsafe {
606 assert!(av_frame_get_buffer(av_frame, 32) >= 0, "Failed to allocate buffer for frame.");
607 }
608
609 let linesize = av_frame.linesize[0] as usize; let height = av_frame.height as usize; let data_ptr = av_frame.data[0]; let randomized_data = if !data_ptr.is_null() {
615 let data_slice = unsafe { std::slice::from_raw_parts_mut(data_ptr, linesize * height) };
617 let mut rng = rand::rng();
618 rng.fill(data_slice);
619 data_slice.to_vec()
620 } else {
621 panic!("Failed to get valid data pointer for Y-plane.");
622 };
623
624 if let Some(data) = video_frame.data(0) {
625 assert_eq!(data, randomized_data.as_slice(), "Data does not match randomized content.");
626 } else {
627 panic!("Data at index 0 should not be None.");
628 }
629 }
630
631 #[test]
632 fn test_video_frame_debug() {
633 let mut frame = GenericFrame::new().expect("Failed to create frame");
634 frame.set_pts(Some(12345));
635 frame.set_dts(Some(67890));
636 frame.set_duration(Some(1000));
637 frame.set_time_base(AVRational { num: 1, den: 30 });
638 frame.set_format(AVPixelFormat::Yuv420p.into());
639 let mut video_frame = frame.video();
640 video_frame.set_width(1920);
641 video_frame.set_height(1080);
642 video_frame.set_sample_aspect_ratio(AVRational { num: 16, den: 9 });
643
644 assert_debug_snapshot!(video_frame, @r"
645 VideoFrame {
646 width: 1920,
647 height: 1080,
648 sample_aspect_ratio: Rational {
649 numerator: 16,
650 denominator: 9,
651 },
652 pts: Some(
653 12345,
654 ),
655 dts: Some(
656 67890,
657 ),
658 duration: Some(
659 1000,
660 ),
661 best_effort_timestamp: Some(
662 12345,
663 ),
664 time_base: Rational {
665 numerator: 1,
666 denominator: 30,
667 },
668 format: AVPixelFormat::Yuv420p,
669 is_audio: false,
670 is_video: true,
671 is_keyframe: false,
672 }
673 ");
674 }
675
676 #[test]
677 fn test_set_channel_layout_default_invalid_count_error() {
678 let frame = GenericFrame::new().expect("Failed to create frame");
679 let mut audio_frame = frame.audio();
680
681 assert!(
682 audio_frame.set_channel_layout_default(usize::MAX).is_err(),
683 "Expected error for invalid channel count."
684 );
685 }
686
687 #[test]
688 fn test_set_channel_layout_custom_invalid_layout_error() {
689 let frame = GenericFrame::new().expect("Failed to create frame");
690 let mut audio_frame = frame.audio();
691 let custom_layout = unsafe {
693 AudioChannelLayout::wrap(crate::ffi::AVChannelLayout {
694 order: AVChannelOrder::Native.into(),
695 nb_channels: -1,
696 u: crate::ffi::AVChannelLayout__bindgen_ty_1 { mask: 2 },
697 opaque: std::ptr::null_mut(),
698 })
699 };
700
701 assert!(
702 audio_frame.set_channel_layout_custom(custom_layout).is_err(),
703 "Expected error for invalid custom channel layout"
704 );
705 }
706
707 #[test]
708 fn test_set_channel_layout_custom() {
709 let frame = GenericFrame::new().expect("Failed to create frame");
710 let mut audio_frame = frame.audio();
711 let custom_layout = unsafe {
713 AudioChannelLayout::wrap(crate::ffi::AVChannelLayout {
714 order: AVChannelOrder::Native.into(),
715 nb_channels: 2,
716 u: crate::ffi::AVChannelLayout__bindgen_ty_1 { mask: 3 },
717 opaque: std::ptr::null_mut(),
718 })
719 };
720
721 assert!(
722 audio_frame.set_channel_layout_custom(custom_layout).is_ok(),
723 "Failed to set custom channel layout"
724 );
725
726 let layout = audio_frame.channel_layout();
727 assert_eq!(layout.nb_channels, 2, "Expected channel layout to have 2 channels (stereo).");
728 assert_eq!(
729 unsafe { layout.u.mask },
731 3,
732 "Expected channel mask to match AV_CH_LAYOUT_STEREO."
733 );
734 assert_eq!(
735 AVChannelOrder(layout.order),
736 AVChannelOrder::Native,
737 "Expected channel order to be AV_CHANNEL_ORDER_NATIVE."
738 );
739 }
740
741 #[test]
742 fn test_alloc_frame_buffer() {
743 let cases = [(None, true), (Some(0), true), (Some(32), true), (Some(-1), false)];
744
745 for alignment in cases {
746 let mut frame = GenericFrame::new().expect("Failed to create frame");
747 frame.set_format(AVSampleFormat::S16.into());
748 let mut audio_frame = frame.audio();
749 audio_frame.set_nb_samples(1024);
750 audio_frame.set_sample_rate(44100);
751
752 assert!(
753 audio_frame.set_channel_layout_default(2).is_ok(),
754 "Failed to set default channel layout"
755 );
756
757 assert_eq!(
758 unsafe { audio_frame.alloc_frame_buffer(alignment.0).is_ok() },
760 alignment.1,
761 "Failed to allocate buffer with alignment {:?}",
762 alignment
763 );
764 }
765 }
766
767 #[test]
768 fn test_alloc_frame_buffer_error() {
769 let cases = [None, Some(0), Some(32), Some(-1)];
770
771 for alignment in cases {
772 let mut frame = GenericFrame::new().expect("Failed to create frame");
773 frame.set_format(AVSampleFormat::S16.into());
774 let mut audio_frame = frame.audio();
775 audio_frame.set_nb_samples(1024);
776
777 assert!(
778 unsafe { audio_frame.alloc_frame_buffer(alignment).is_err() },
780 "Should fail to allocate buffer with invalid frame and alignment {:?}",
781 alignment
782 );
783 }
784 }
785
786 #[test]
787 fn test_nb_samples() {
788 let mut frame = GenericFrame::new().expect("Failed to create frame");
789 frame.set_format(AVSampleFormat::S16.into());
790 let mut audio_frame = frame.audio();
791 audio_frame.set_nb_samples(1024);
792
793 assert_eq!(
794 audio_frame.nb_samples(),
795 1024,
796 "The number of samples should match the set value."
797 );
798 }
799
800 #[test]
801 fn test_sample_rate() {
802 let mut frame = GenericFrame::new().expect("Failed to create frame");
803 frame.set_format(AVSampleFormat::S16.into());
804 let mut audio_frame = frame.audio();
805 audio_frame.set_sample_rate(44100);
806
807 assert_eq!(
808 audio_frame.sample_rate(),
809 44100,
810 "The sample rate should match the set value."
811 );
812 }
813
814 #[test]
815 fn test_audio_frame_debug() {
816 let mut frame = GenericFrame::new().expect("Failed to create frame");
817 frame.set_format(AVSampleFormat::S16.into());
818 let mut audio_frame = frame.audio();
819 audio_frame.set_nb_samples(1024);
820 audio_frame.set_sample_rate(44100);
821 audio_frame.set_pts(Some(12345));
822 audio_frame.set_dts(Some(67890));
823 audio_frame.set_duration(Some(512));
824 audio_frame.set_time_base(AVRational { num: 1, den: 44100 });
825
826 assert!(
827 audio_frame.set_channel_layout_default(2).is_ok(),
828 "Failed to set default channel layout"
829 );
830 assert_debug_snapshot!(audio_frame, @r"
831 AudioFrame {
832 channel_count: 2,
833 nb_samples: 1024,
834 sample_rate: 44100,
835 pts: Some(
836 12345,
837 ),
838 dts: Some(
839 67890,
840 ),
841 duration: Some(
842 512,
843 ),
844 best_effort_timestamp: Some(
845 12345,
846 ),
847 time_base: Rational {
848 numerator: 1,
849 denominator: 44100,
850 },
851 format: 1,
852 is_audio: true,
853 is_video: false,
854 }
855 ");
856 }
857}