1#![allow(unsafe_code)]
8#![allow(clippy::similar_names)]
10#![allow(clippy::too_many_lines)]
11#![allow(clippy::cast_sign_loss)]
12#![allow(clippy::cast_possible_truncation)]
13#![allow(clippy::cast_possible_wrap)]
14#![allow(clippy::module_name_repetitions)]
15#![allow(clippy::match_same_arms)]
16#![allow(clippy::ptr_as_ptr)]
17#![allow(clippy::doc_markdown)]
18#![allow(clippy::unnecessary_cast)]
19#![allow(clippy::if_not_else)]
20#![allow(clippy::unnecessary_wraps)]
21#![allow(clippy::cast_precision_loss)]
22#![allow(clippy::if_same_then_else)]
23#![allow(clippy::cast_lossless)]
24
25use std::ffi::CStr;
26use std::path::Path;
27use std::ptr;
28use std::time::Duration;
29
30use ff_format::channel::ChannelLayout;
31use ff_format::codec::AudioCodec;
32use ff_format::time::{Rational, Timestamp};
33use ff_format::{AudioFrame, AudioStreamInfo, SampleFormat};
34use ff_sys::{
35 AVCodecContext, AVCodecID, AVFormatContext, AVFrame, AVMediaType_AVMEDIA_TYPE_AUDIO, AVPacket,
36 AVSampleFormat, SwrContext,
37};
38
39use crate::error::DecodeError;
40
41struct AvFormatContextGuard(*mut AVFormatContext);
43
44impl AvFormatContextGuard {
45 unsafe fn new(path: &Path) -> Result<Self, DecodeError> {
51 let format_ctx = unsafe {
53 ff_sys::avformat::open_input(path).map_err(|e| DecodeError::Ffmpeg {
54 code: e,
55 message: format!("Failed to open file: {}", ff_sys::av_error_string(e)),
56 })?
57 };
58 Ok(Self(format_ctx))
59 }
60
61 const fn as_ptr(&self) -> *mut AVFormatContext {
63 self.0
64 }
65
66 fn into_raw(self) -> *mut AVFormatContext {
68 let ptr = self.0;
69 std::mem::forget(self);
70 ptr
71 }
72}
73
74impl Drop for AvFormatContextGuard {
75 fn drop(&mut self) {
76 if !self.0.is_null() {
77 unsafe {
79 ff_sys::avformat::close_input(&mut (self.0 as *mut _));
80 }
81 }
82 }
83}
84
85struct AvCodecContextGuard(*mut AVCodecContext);
87
88impl AvCodecContextGuard {
89 unsafe fn new(codec: *const ff_sys::AVCodec) -> Result<Self, DecodeError> {
95 let codec_ctx = unsafe {
97 ff_sys::avcodec::alloc_context3(codec).map_err(|e| DecodeError::Ffmpeg {
98 code: e,
99 message: format!("Failed to allocate codec context: {e}"),
100 })?
101 };
102 Ok(Self(codec_ctx))
103 }
104
105 const fn as_ptr(&self) -> *mut AVCodecContext {
107 self.0
108 }
109
110 fn into_raw(self) -> *mut AVCodecContext {
112 let ptr = self.0;
113 std::mem::forget(self);
114 ptr
115 }
116}
117
118impl Drop for AvCodecContextGuard {
119 fn drop(&mut self) {
120 if !self.0.is_null() {
121 unsafe {
123 ff_sys::avcodec::free_context(&mut (self.0 as *mut _));
124 }
125 }
126 }
127}
128
129struct AvPacketGuard(*mut AVPacket);
131
132impl AvPacketGuard {
133 unsafe fn new() -> Result<Self, DecodeError> {
139 let packet = unsafe { ff_sys::av_packet_alloc() };
141 if packet.is_null() {
142 return Err(DecodeError::Ffmpeg {
143 code: 0,
144 message: "Failed to allocate packet".to_string(),
145 });
146 }
147 Ok(Self(packet))
148 }
149
150 fn into_raw(self) -> *mut AVPacket {
152 let ptr = self.0;
153 std::mem::forget(self);
154 ptr
155 }
156}
157
158impl Drop for AvPacketGuard {
159 fn drop(&mut self) {
160 if !self.0.is_null() {
161 unsafe {
163 ff_sys::av_packet_free(&mut (self.0 as *mut _));
164 }
165 }
166 }
167}
168
169struct AvFrameGuard(*mut AVFrame);
171
172impl AvFrameGuard {
173 unsafe fn new() -> Result<Self, DecodeError> {
179 let frame = unsafe { ff_sys::av_frame_alloc() };
181 if frame.is_null() {
182 return Err(DecodeError::Ffmpeg {
183 code: 0,
184 message: "Failed to allocate frame".to_string(),
185 });
186 }
187 Ok(Self(frame))
188 }
189
190 fn into_raw(self) -> *mut AVFrame {
192 let ptr = self.0;
193 std::mem::forget(self);
194 ptr
195 }
196}
197
198impl Drop for AvFrameGuard {
199 fn drop(&mut self) {
200 if !self.0.is_null() {
201 unsafe {
203 ff_sys::av_frame_free(&mut (self.0 as *mut _));
204 }
205 }
206 }
207}
208
209struct SwrContextGuard(*mut SwrContext);
211
212impl SwrContextGuard {
213 #[allow(dead_code)]
215 fn into_raw(self) -> *mut SwrContext {
216 let ptr = self.0;
217 std::mem::forget(self);
218 ptr
219 }
220}
221
222impl Drop for SwrContextGuard {
223 fn drop(&mut self) {
224 if !self.0.is_null() {
225 unsafe {
227 ff_sys::swr_free(&mut (self.0 as *mut _));
228 }
229 }
230 }
231}
232
233pub(crate) struct AudioDecoderInner {
238 format_ctx: *mut AVFormatContext,
240 codec_ctx: *mut AVCodecContext,
242 stream_index: i32,
244 swr_ctx: Option<*mut SwrContext>,
246 output_format: Option<SampleFormat>,
248 output_sample_rate: Option<u32>,
250 output_channels: Option<u32>,
252 eof: bool,
254 position: Duration,
256 packet: *mut AVPacket,
258 frame: *mut AVFrame,
260}
261
262impl AudioDecoderInner {
263 pub(crate) fn new(
280 path: &Path,
281 output_format: Option<SampleFormat>,
282 output_sample_rate: Option<u32>,
283 output_channels: Option<u32>,
284 ) -> Result<(Self, AudioStreamInfo), DecodeError> {
285 ff_sys::ensure_initialized();
287
288 let format_ctx_guard = unsafe { AvFormatContextGuard::new(path)? };
291 let format_ctx = format_ctx_guard.as_ptr();
292
293 unsafe {
296 ff_sys::avformat::find_stream_info(format_ctx).map_err(|e| DecodeError::Ffmpeg {
297 code: e,
298 message: format!("Failed to find stream info: {}", ff_sys::av_error_string(e)),
299 })?;
300 }
301
302 let (stream_index, codec_id) =
305 unsafe { Self::find_audio_stream(format_ctx) }.ok_or_else(|| {
306 DecodeError::NoAudioStream {
307 path: path.to_path_buf(),
308 }
309 })?;
310
311 let codec_name = unsafe { Self::extract_codec_name(codec_id) };
314 let codec = unsafe {
315 ff_sys::avcodec::find_decoder(codec_id).ok_or_else(|| {
316 DecodeError::UnsupportedCodec {
317 codec: format!("{codec_name} (codec_id={codec_id:?})"),
318 }
319 })?
320 };
321
322 let codec_ctx_guard = unsafe { AvCodecContextGuard::new(codec)? };
325 let codec_ctx = codec_ctx_guard.as_ptr();
326
327 unsafe {
330 let stream = (*format_ctx).streams.add(stream_index as usize);
331 let codecpar = (*(*stream)).codecpar;
332 ff_sys::avcodec::parameters_to_context(codec_ctx, codecpar).map_err(|e| {
333 DecodeError::Ffmpeg {
334 code: e,
335 message: format!(
336 "Failed to copy codec parameters: {}",
337 ff_sys::av_error_string(e)
338 ),
339 }
340 })?;
341 }
342
343 unsafe {
346 ff_sys::avcodec::open2(codec_ctx, codec, ptr::null_mut()).map_err(|e| {
347 DecodeError::Ffmpeg {
348 code: e,
349 message: format!("Failed to open codec: {}", ff_sys::av_error_string(e)),
350 }
351 })?;
352 }
353
354 let stream_info =
357 unsafe { Self::extract_stream_info(format_ctx, stream_index as i32, codec_ctx)? };
358
359 let packet_guard = unsafe { AvPacketGuard::new()? };
362 let frame_guard = unsafe { AvFrameGuard::new()? };
363
364 Ok((
366 Self {
367 format_ctx: format_ctx_guard.into_raw(),
368 codec_ctx: codec_ctx_guard.into_raw(),
369 stream_index: stream_index as i32,
370 swr_ctx: None,
371 output_format,
372 output_sample_rate,
373 output_channels,
374 eof: false,
375 position: Duration::ZERO,
376 packet: packet_guard.into_raw(),
377 frame: frame_guard.into_raw(),
378 },
379 stream_info,
380 ))
381 }
382
383 unsafe fn find_audio_stream(format_ctx: *mut AVFormatContext) -> Option<(usize, AVCodecID)> {
393 unsafe {
395 let nb_streams = (*format_ctx).nb_streams as usize;
396
397 for i in 0..nb_streams {
398 let stream = (*format_ctx).streams.add(i);
399 let codecpar = (*(*stream)).codecpar;
400
401 if (*codecpar).codec_type == AVMediaType_AVMEDIA_TYPE_AUDIO {
402 return Some((i, (*codecpar).codec_id));
403 }
404 }
405
406 None
407 }
408 }
409
410 unsafe fn extract_codec_name(codec_id: ff_sys::AVCodecID) -> String {
412 let name_ptr = unsafe { ff_sys::avcodec_get_name(codec_id) };
414 if name_ptr.is_null() {
415 return String::from("unknown");
416 }
417 unsafe { CStr::from_ptr(name_ptr).to_string_lossy().into_owned() }
419 }
420
421 unsafe fn extract_stream_info(
423 format_ctx: *mut AVFormatContext,
424 stream_index: i32,
425 codec_ctx: *mut AVCodecContext,
426 ) -> Result<AudioStreamInfo, DecodeError> {
427 let (sample_rate, channels, sample_fmt, duration_val, channel_layout, codec_id) = unsafe {
429 let stream = (*format_ctx).streams.add(stream_index as usize);
430 let codecpar = (*(*stream)).codecpar;
431
432 (
433 (*codecpar).sample_rate as u32,
434 (*codecpar).ch_layout.nb_channels as u32,
435 (*codec_ctx).sample_fmt,
436 (*format_ctx).duration,
437 (*codecpar).ch_layout,
438 (*codecpar).codec_id,
439 )
440 };
441
442 let duration = if duration_val > 0 {
444 let duration_secs = duration_val as f64 / 1_000_000.0;
445 Some(Duration::from_secs_f64(duration_secs))
446 } else {
447 None
448 };
449
450 let sample_format = Self::convert_sample_format(sample_fmt);
452
453 let channel_layout_enum = Self::convert_channel_layout(&channel_layout, channels);
455
456 let codec = Self::convert_codec(codec_id);
458 let codec_name = unsafe { Self::extract_codec_name(codec_id) };
459
460 let mut builder = AudioStreamInfo::builder()
462 .index(stream_index as u32)
463 .codec(codec)
464 .codec_name(codec_name)
465 .sample_rate(sample_rate)
466 .channels(channels)
467 .sample_format(sample_format)
468 .channel_layout(channel_layout_enum);
469
470 if let Some(d) = duration {
471 builder = builder.duration(d);
472 }
473
474 Ok(builder.build())
475 }
476
477 fn convert_sample_format(fmt: AVSampleFormat) -> SampleFormat {
479 if fmt == ff_sys::AVSampleFormat_AV_SAMPLE_FMT_U8 {
480 SampleFormat::U8
481 } else if fmt == ff_sys::AVSampleFormat_AV_SAMPLE_FMT_S16 {
482 SampleFormat::I16
483 } else if fmt == ff_sys::AVSampleFormat_AV_SAMPLE_FMT_S32 {
484 SampleFormat::I32
485 } else if fmt == ff_sys::AVSampleFormat_AV_SAMPLE_FMT_FLT {
486 SampleFormat::F32
487 } else if fmt == ff_sys::AVSampleFormat_AV_SAMPLE_FMT_DBL {
488 SampleFormat::F64
489 } else if fmt == ff_sys::AVSampleFormat_AV_SAMPLE_FMT_U8P {
490 SampleFormat::U8p
491 } else if fmt == ff_sys::AVSampleFormat_AV_SAMPLE_FMT_S16P {
492 SampleFormat::I16p
493 } else if fmt == ff_sys::AVSampleFormat_AV_SAMPLE_FMT_S32P {
494 SampleFormat::I32p
495 } else if fmt == ff_sys::AVSampleFormat_AV_SAMPLE_FMT_FLTP {
496 SampleFormat::F32p
497 } else if fmt == ff_sys::AVSampleFormat_AV_SAMPLE_FMT_DBLP {
498 SampleFormat::F64p
499 } else {
500 log::warn!(
501 "sample_format unsupported, falling back to F32 requested={fmt} fallback=F32"
502 );
503 SampleFormat::F32
504 }
505 }
506
507 fn convert_channel_layout(layout: &ff_sys::AVChannelLayout, channels: u32) -> ChannelLayout {
509 if layout.order == ff_sys::AVChannelOrder_AV_CHANNEL_ORDER_NATIVE {
510 let mask = unsafe { layout.u.mask };
512 match mask {
513 0x4 => ChannelLayout::Mono,
514 0x3 => ChannelLayout::Stereo,
515 0x103 => ChannelLayout::Stereo2_1,
516 0x7 => ChannelLayout::Surround3_0,
517 0x33 => ChannelLayout::Quad,
518 0x37 => ChannelLayout::Surround5_0,
519 0x3F => ChannelLayout::Surround5_1,
520 0x13F => ChannelLayout::Surround6_1,
521 0x63F => ChannelLayout::Surround7_1,
522 _ => {
523 log::warn!(
524 "channel_layout mask has no mapping, deriving from channel count \
525 mask={mask} channels={channels}"
526 );
527 ChannelLayout::from_channels(channels)
528 }
529 }
530 } else {
531 log::warn!(
532 "channel_layout order is not NATIVE, deriving from channel count \
533 order={order} channels={channels}",
534 order = layout.order
535 );
536 ChannelLayout::from_channels(channels)
537 }
538 }
539
540 unsafe fn create_channel_layout(channels: u32) -> ff_sys::AVChannelLayout {
546 let mut layout = unsafe { std::mem::zeroed::<ff_sys::AVChannelLayout>() };
548 unsafe {
550 ff_sys::av_channel_layout_default(&raw mut layout, channels as i32);
551 }
552 layout
553 }
554
555 fn convert_codec(codec_id: AVCodecID) -> AudioCodec {
557 if codec_id == ff_sys::AVCodecID_AV_CODEC_ID_AAC {
558 AudioCodec::Aac
559 } else if codec_id == ff_sys::AVCodecID_AV_CODEC_ID_MP3 {
560 AudioCodec::Mp3
561 } else if codec_id == ff_sys::AVCodecID_AV_CODEC_ID_OPUS {
562 AudioCodec::Opus
563 } else if codec_id == ff_sys::AVCodecID_AV_CODEC_ID_VORBIS {
564 AudioCodec::Vorbis
565 } else if codec_id == ff_sys::AVCodecID_AV_CODEC_ID_FLAC {
566 AudioCodec::Flac
567 } else if codec_id == ff_sys::AVCodecID_AV_CODEC_ID_PCM_S16LE {
568 AudioCodec::Pcm
569 } else {
570 log::warn!(
571 "audio codec unsupported, falling back to Aac codec_id={codec_id} fallback=Aac"
572 );
573 AudioCodec::Aac
574 }
575 }
576
577 pub(crate) fn decode_one(&mut self) -> Result<Option<AudioFrame>, DecodeError> {
585 if self.eof {
586 return Ok(None);
587 }
588
589 unsafe {
590 loop {
591 let ret = ff_sys::avcodec_receive_frame(self.codec_ctx, self.frame);
593
594 if ret == 0 {
595 let audio_frame = self.convert_frame_to_audio_frame()?;
597
598 let pts = (*self.frame).pts;
600 if pts != ff_sys::AV_NOPTS_VALUE {
601 let stream = (*self.format_ctx).streams.add(self.stream_index as usize);
602 let time_base = (*(*stream)).time_base;
603 let timestamp_secs =
604 pts as f64 * time_base.num as f64 / time_base.den as f64;
605 self.position = Duration::from_secs_f64(timestamp_secs);
606 }
607
608 return Ok(Some(audio_frame));
609 } else if ret == ff_sys::error_codes::EAGAIN {
610 let read_ret = ff_sys::av_read_frame(self.format_ctx, self.packet);
613
614 if read_ret == ff_sys::error_codes::EOF {
615 ff_sys::avcodec_send_packet(self.codec_ctx, ptr::null());
617 self.eof = true;
618 continue;
619 } else if read_ret < 0 {
620 return Err(DecodeError::Ffmpeg {
621 code: read_ret,
622 message: format!(
623 "Failed to read frame: {}",
624 ff_sys::av_error_string(read_ret)
625 ),
626 });
627 }
628
629 if (*self.packet).stream_index == self.stream_index {
631 let send_ret = ff_sys::avcodec_send_packet(self.codec_ctx, self.packet);
633 ff_sys::av_packet_unref(self.packet);
634
635 if send_ret < 0 && send_ret != ff_sys::error_codes::EAGAIN {
636 return Err(DecodeError::Ffmpeg {
637 code: send_ret,
638 message: format!(
639 "Failed to send packet: {}",
640 ff_sys::av_error_string(send_ret)
641 ),
642 });
643 }
644 } else {
645 ff_sys::av_packet_unref(self.packet);
647 }
648 } else if ret == ff_sys::error_codes::EOF {
649 self.eof = true;
651 return Ok(None);
652 } else {
653 return Err(DecodeError::DecodingFailed {
654 timestamp: Some(self.position),
655 reason: ff_sys::av_error_string(ret),
656 });
657 }
658 }
659 }
660 }
661
662 unsafe fn convert_frame_to_audio_frame(&mut self) -> Result<AudioFrame, DecodeError> {
664 unsafe {
666 let nb_samples = (*self.frame).nb_samples as usize;
667 let channels = (*self.frame).ch_layout.nb_channels as u32;
668 let sample_rate = (*self.frame).sample_rate as u32;
669 let src_format = (*self.frame).format;
670
671 let needs_conversion = self.output_format.is_some()
673 || self.output_sample_rate.is_some()
674 || self.output_channels.is_some();
675
676 if needs_conversion {
677 self.convert_with_swr(nb_samples, channels, sample_rate, src_format)
678 } else {
679 self.av_frame_to_audio_frame(self.frame)
680 }
681 }
682 }
683
684 unsafe fn convert_with_swr(
686 &mut self,
687 nb_samples: usize,
688 src_channels: u32,
689 src_sample_rate: u32,
690 src_format: i32,
691 ) -> Result<AudioFrame, DecodeError> {
692 let dst_format = self
694 .output_format
695 .map_or(src_format, Self::sample_format_to_av);
696 let dst_sample_rate = self.output_sample_rate.unwrap_or(src_sample_rate);
697 let dst_channels = self.output_channels.unwrap_or(src_channels);
698
699 if src_format == dst_format
701 && src_sample_rate == dst_sample_rate
702 && src_channels == dst_channels
703 {
704 return unsafe { self.av_frame_to_audio_frame(self.frame) };
705 }
706
707 let mut src_ch_layout = unsafe { Self::create_channel_layout(src_channels) };
710 let mut dst_ch_layout = unsafe { Self::create_channel_layout(dst_channels) };
711
712 let mut swr_ctx: *mut SwrContext = ptr::null_mut();
714
715 let ret = unsafe {
717 ff_sys::swr_alloc_set_opts2(
718 &raw mut swr_ctx,
719 &raw const dst_ch_layout,
720 dst_format,
721 dst_sample_rate as i32,
722 &raw const src_ch_layout,
723 src_format,
724 src_sample_rate as i32,
725 0,
726 ptr::null_mut(),
727 )
728 };
729
730 if ret < 0 {
731 unsafe {
733 ff_sys::av_channel_layout_uninit(&raw mut src_ch_layout);
734 ff_sys::av_channel_layout_uninit(&raw mut dst_ch_layout);
735 }
736 return Err(DecodeError::Ffmpeg {
737 code: ret,
738 message: format!(
739 "Failed to allocate SwrContext: {}",
740 ff_sys::av_error_string(ret)
741 ),
742 });
743 }
744
745 let _swr_guard = SwrContextGuard(swr_ctx);
747
748 let ret = unsafe { ff_sys::swr_init(swr_ctx) };
751 if ret < 0 {
752 unsafe {
754 ff_sys::av_channel_layout_uninit(&raw mut src_ch_layout);
755 ff_sys::av_channel_layout_uninit(&raw mut dst_ch_layout);
756 }
757 return Err(DecodeError::Ffmpeg {
758 code: ret,
759 message: format!(
760 "Failed to initialize SwrContext: {}",
761 ff_sys::av_error_string(ret)
762 ),
763 });
764 }
765
766 let out_samples = unsafe { ff_sys::swr_get_out_samples(swr_ctx, nb_samples as i32) };
769
770 if out_samples < 0 {
771 unsafe {
773 ff_sys::av_channel_layout_uninit(&raw mut src_ch_layout);
774 ff_sys::av_channel_layout_uninit(&raw mut dst_ch_layout);
775 }
776 return Err(DecodeError::Ffmpeg {
777 code: 0,
778 message: "Failed to calculate output sample count".to_string(),
779 });
780 }
781
782 let out_samples = out_samples as usize;
783
784 let dst_sample_fmt = Self::convert_sample_format(dst_format);
786 let bytes_per_sample = dst_sample_fmt.bytes_per_sample();
787 let is_planar = dst_sample_fmt.is_planar();
788
789 let buffer_size = if is_planar {
791 out_samples * bytes_per_sample * dst_channels as usize
793 } else {
794 out_samples * bytes_per_sample * dst_channels as usize
796 };
797
798 let mut out_buffer = vec![0u8; buffer_size];
799
800 let mut out_ptrs = if is_planar {
802 let plane_size = out_samples * bytes_per_sample;
804 (0..dst_channels)
805 .map(|i| {
806 let offset = i as usize * plane_size;
807 out_buffer[offset..].as_mut_ptr()
808 })
809 .collect::<Vec<_>>()
810 } else {
811 vec![out_buffer.as_mut_ptr()]
813 };
814
815 let in_ptrs = unsafe { (*self.frame).data };
818
819 let converted_samples = unsafe {
822 ff_sys::swr_convert(
823 swr_ctx,
824 out_ptrs.as_mut_ptr(),
825 out_samples as i32,
826 in_ptrs.as_ptr() as *mut *const u8,
827 nb_samples as i32,
828 )
829 };
830
831 unsafe {
833 ff_sys::av_channel_layout_uninit(&raw mut src_ch_layout);
834 ff_sys::av_channel_layout_uninit(&raw mut dst_ch_layout);
835 }
836
837 if converted_samples < 0 {
838 return Err(DecodeError::Ffmpeg {
839 code: converted_samples,
840 message: format!(
841 "Failed to convert samples: {}",
842 ff_sys::av_error_string(converted_samples)
843 ),
844 });
845 }
846
847 let timestamp = unsafe {
850 let pts = (*self.frame).pts;
851 if pts != ff_sys::AV_NOPTS_VALUE {
852 let stream = (*self.format_ctx).streams.add(self.stream_index as usize);
853 let time_base = (*(*stream)).time_base;
854 Timestamp::new(pts, Rational::new(time_base.num, time_base.den))
855 } else {
856 let stream = (*self.format_ctx).streams.add(self.stream_index as usize);
857 let time_base = (*(*stream)).time_base;
858 Timestamp::zero(Rational::new(time_base.num, time_base.den))
859 }
860 };
861
862 let planes = if is_planar {
864 let plane_size = converted_samples as usize * bytes_per_sample;
865 (0..dst_channels)
866 .map(|i| {
867 let offset = i as usize * plane_size;
868 out_buffer[offset..offset + plane_size].to_vec()
869 })
870 .collect()
871 } else {
872 vec![
874 out_buffer[..converted_samples as usize * bytes_per_sample * dst_channels as usize]
875 .to_vec(),
876 ]
877 };
878
879 AudioFrame::new(
880 planes,
881 converted_samples as usize,
882 dst_channels,
883 dst_sample_rate,
884 dst_sample_fmt,
885 timestamp,
886 )
887 .map_err(|e| DecodeError::Ffmpeg {
888 code: 0,
889 message: format!("Failed to create AudioFrame: {e}"),
890 })
891 }
892
893 unsafe fn av_frame_to_audio_frame(
895 &self,
896 frame: *const AVFrame,
897 ) -> Result<AudioFrame, DecodeError> {
898 unsafe {
900 let nb_samples = (*frame).nb_samples as usize;
901 let channels = (*frame).ch_layout.nb_channels as u32;
902 let sample_rate = (*frame).sample_rate as u32;
903 let format = Self::convert_sample_format((*frame).format);
904
905 let pts = (*frame).pts;
907 let timestamp = if pts != ff_sys::AV_NOPTS_VALUE {
908 let stream = (*self.format_ctx).streams.add(self.stream_index as usize);
909 let time_base = (*(*stream)).time_base;
910 Timestamp::new(
911 pts as i64,
912 Rational::new(time_base.num as i32, time_base.den as i32),
913 )
914 } else {
915 Timestamp::default()
916 };
917
918 let planes = Self::extract_planes(frame, nb_samples, channels, format)?;
920
921 AudioFrame::new(planes, nb_samples, channels, sample_rate, format, timestamp).map_err(
922 |e| DecodeError::Ffmpeg {
923 code: 0,
924 message: format!("Failed to create AudioFrame: {e}"),
925 },
926 )
927 }
928 }
929
930 unsafe fn extract_planes(
932 frame: *const AVFrame,
933 nb_samples: usize,
934 channels: u32,
935 format: SampleFormat,
936 ) -> Result<Vec<Vec<u8>>, DecodeError> {
937 unsafe {
939 let mut planes = Vec::new();
940 let bytes_per_sample = format.bytes_per_sample();
941
942 if format.is_planar() {
943 for ch in 0..channels as usize {
945 let plane_size = nb_samples * bytes_per_sample;
946 let mut plane_data = vec![0u8; plane_size];
947
948 let src_ptr = (*frame).data[ch];
949 std::ptr::copy_nonoverlapping(src_ptr, plane_data.as_mut_ptr(), plane_size);
950
951 planes.push(plane_data);
952 }
953 } else {
954 let plane_size = nb_samples * channels as usize * bytes_per_sample;
956 let mut plane_data = vec![0u8; plane_size];
957
958 let src_ptr = (*frame).data[0];
959 std::ptr::copy_nonoverlapping(src_ptr, plane_data.as_mut_ptr(), plane_size);
960
961 planes.push(plane_data);
962 }
963
964 Ok(planes)
965 }
966 }
967
968 fn sample_format_to_av(format: SampleFormat) -> AVSampleFormat {
970 match format {
971 SampleFormat::U8 => ff_sys::AVSampleFormat_AV_SAMPLE_FMT_U8,
972 SampleFormat::I16 => ff_sys::AVSampleFormat_AV_SAMPLE_FMT_S16,
973 SampleFormat::I32 => ff_sys::AVSampleFormat_AV_SAMPLE_FMT_S32,
974 SampleFormat::F32 => ff_sys::AVSampleFormat_AV_SAMPLE_FMT_FLT,
975 SampleFormat::F64 => ff_sys::AVSampleFormat_AV_SAMPLE_FMT_DBL,
976 SampleFormat::U8p => ff_sys::AVSampleFormat_AV_SAMPLE_FMT_U8P,
977 SampleFormat::I16p => ff_sys::AVSampleFormat_AV_SAMPLE_FMT_S16P,
978 SampleFormat::I32p => ff_sys::AVSampleFormat_AV_SAMPLE_FMT_S32P,
979 SampleFormat::F32p => ff_sys::AVSampleFormat_AV_SAMPLE_FMT_FLTP,
980 SampleFormat::F64p => ff_sys::AVSampleFormat_AV_SAMPLE_FMT_DBLP,
981 _ => {
982 log::warn!(
983 "sample_format has no AV mapping, falling back to F32 format={format:?} fallback=AV_SAMPLE_FMT_FLT"
984 );
985 ff_sys::AVSampleFormat_AV_SAMPLE_FMT_FLT
986 }
987 }
988 }
989
990 pub(crate) fn position(&self) -> Duration {
992 self.position
993 }
994
995 pub(crate) fn is_eof(&self) -> bool {
997 self.eof
998 }
999
1000 fn duration_to_pts(&self, duration: Duration) -> i64 {
1002 let time_base = unsafe {
1004 let stream = (*self.format_ctx).streams.add(self.stream_index as usize);
1005 (*(*stream)).time_base
1006 };
1007
1008 let time_base_f64 = time_base.den as f64 / time_base.num as f64;
1010 (duration.as_secs_f64() * time_base_f64) as i64
1011 }
1012
1013 pub(crate) fn seek(
1024 &mut self,
1025 position: Duration,
1026 mode: crate::SeekMode,
1027 ) -> Result<(), DecodeError> {
1028 use crate::SeekMode;
1029
1030 let timestamp = self.duration_to_pts(position);
1031 let flags = ff_sys::avformat::seek_flags::BACKWARD;
1032
1033 unsafe {
1036 ff_sys::av_packet_unref(self.packet);
1037 ff_sys::av_frame_unref(self.frame);
1038 }
1039
1040 unsafe {
1043 ff_sys::avformat::seek_frame(self.format_ctx, self.stream_index, timestamp, flags)
1044 .map_err(|e| DecodeError::SeekFailed {
1045 target: position,
1046 reason: ff_sys::av_error_string(e),
1047 })?;
1048 }
1049
1050 unsafe {
1053 ff_sys::avcodec::flush_buffers(self.codec_ctx);
1054 }
1055
1056 unsafe {
1059 loop {
1060 let ret = ff_sys::avcodec_receive_frame(self.codec_ctx, self.frame);
1061 if ret == ff_sys::error_codes::EAGAIN || ret == ff_sys::error_codes::EOF {
1062 break;
1063 } else if ret == 0 {
1064 ff_sys::av_frame_unref(self.frame);
1065 } else {
1066 break;
1067 }
1068 }
1069 }
1070
1071 self.eof = false;
1073
1074 if mode == SeekMode::Exact {
1076 self.skip_to_exact(position)?;
1077 }
1078 Ok(())
1081 }
1082
1083 fn skip_to_exact(&mut self, target: Duration) -> Result<(), DecodeError> {
1091 while let Some(frame) = self.decode_one()? {
1093 let frame_time = frame.timestamp().as_duration();
1094 if frame_time >= target {
1095 break;
1097 }
1098 }
1100 Ok(())
1101 }
1102
1103 pub(crate) fn flush(&mut self) {
1105 unsafe {
1107 ff_sys::avcodec::flush_buffers(self.codec_ctx);
1108 }
1109 self.eof = false;
1110 }
1111}
1112
1113impl Drop for AudioDecoderInner {
1114 fn drop(&mut self) {
1115 if let Some(swr_ctx) = self.swr_ctx {
1117 unsafe {
1119 ff_sys::swr_free(&mut (swr_ctx as *mut _));
1121 }
1122 }
1123
1124 if !self.frame.is_null() {
1126 unsafe {
1128 ff_sys::av_frame_free(&mut (self.frame as *mut _));
1129 }
1130 }
1131
1132 if !self.packet.is_null() {
1133 unsafe {
1135 ff_sys::av_packet_free(&mut (self.packet as *mut _));
1136 }
1137 }
1138
1139 if !self.codec_ctx.is_null() {
1141 unsafe {
1143 ff_sys::avcodec::free_context(&mut (self.codec_ctx as *mut _));
1144 }
1145 }
1146
1147 if !self.format_ctx.is_null() {
1149 unsafe {
1151 ff_sys::avformat::close_input(&mut (self.format_ctx as *mut _));
1152 }
1153 }
1154 }
1155}
1156
1157unsafe impl Send for AudioDecoderInner {}
1160
1161#[cfg(test)]
1162#[allow(unsafe_code)]
1163mod tests {
1164 use ff_format::channel::ChannelLayout;
1165
1166 use super::AudioDecoderInner;
1167
1168 fn native_layout(mask: u64, nb_channels: i32) -> ff_sys::AVChannelLayout {
1170 ff_sys::AVChannelLayout {
1171 order: ff_sys::AVChannelOrder_AV_CHANNEL_ORDER_NATIVE,
1172 nb_channels,
1173 u: ff_sys::AVChannelLayout__bindgen_ty_1 { mask },
1174 opaque: std::ptr::null_mut(),
1175 }
1176 }
1177
1178 fn unspec_layout(nb_channels: i32) -> ff_sys::AVChannelLayout {
1180 ff_sys::AVChannelLayout {
1181 order: ff_sys::AVChannelOrder_AV_CHANNEL_ORDER_UNSPEC,
1182 nb_channels,
1183 u: ff_sys::AVChannelLayout__bindgen_ty_1 { mask: 0 },
1184 opaque: std::ptr::null_mut(),
1185 }
1186 }
1187
1188 #[test]
1189 fn native_mask_mono() {
1190 let layout = native_layout(0x4, 1);
1191 assert_eq!(
1192 AudioDecoderInner::convert_channel_layout(&layout, 1),
1193 ChannelLayout::Mono
1194 );
1195 }
1196
1197 #[test]
1198 fn native_mask_stereo() {
1199 let layout = native_layout(0x3, 2);
1200 assert_eq!(
1201 AudioDecoderInner::convert_channel_layout(&layout, 2),
1202 ChannelLayout::Stereo
1203 );
1204 }
1205
1206 #[test]
1207 fn native_mask_stereo2_1() {
1208 let layout = native_layout(0x103, 3);
1209 assert_eq!(
1210 AudioDecoderInner::convert_channel_layout(&layout, 3),
1211 ChannelLayout::Stereo2_1
1212 );
1213 }
1214
1215 #[test]
1216 fn native_mask_surround3_0() {
1217 let layout = native_layout(0x7, 3);
1218 assert_eq!(
1219 AudioDecoderInner::convert_channel_layout(&layout, 3),
1220 ChannelLayout::Surround3_0
1221 );
1222 }
1223
1224 #[test]
1225 fn native_mask_quad() {
1226 let layout = native_layout(0x33, 4);
1227 assert_eq!(
1228 AudioDecoderInner::convert_channel_layout(&layout, 4),
1229 ChannelLayout::Quad
1230 );
1231 }
1232
1233 #[test]
1234 fn native_mask_surround5_0() {
1235 let layout = native_layout(0x37, 5);
1236 assert_eq!(
1237 AudioDecoderInner::convert_channel_layout(&layout, 5),
1238 ChannelLayout::Surround5_0
1239 );
1240 }
1241
1242 #[test]
1243 fn native_mask_surround5_1() {
1244 let layout = native_layout(0x3F, 6);
1245 assert_eq!(
1246 AudioDecoderInner::convert_channel_layout(&layout, 6),
1247 ChannelLayout::Surround5_1
1248 );
1249 }
1250
1251 #[test]
1252 fn native_mask_surround6_1() {
1253 let layout = native_layout(0x13F, 7);
1254 assert_eq!(
1255 AudioDecoderInner::convert_channel_layout(&layout, 7),
1256 ChannelLayout::Surround6_1
1257 );
1258 }
1259
1260 #[test]
1261 fn native_mask_surround7_1() {
1262 let layout = native_layout(0x63F, 8);
1263 assert_eq!(
1264 AudioDecoderInner::convert_channel_layout(&layout, 8),
1265 ChannelLayout::Surround7_1
1266 );
1267 }
1268
1269 #[test]
1270 fn native_mask_unknown_falls_back_to_from_channels() {
1271 let layout = native_layout(0x1, 2);
1273 assert_eq!(
1274 AudioDecoderInner::convert_channel_layout(&layout, 2),
1275 ChannelLayout::from_channels(2)
1276 );
1277 }
1278
1279 #[test]
1280 fn non_native_order_falls_back_to_from_channels() {
1281 let layout = unspec_layout(6);
1282 assert_eq!(
1283 AudioDecoderInner::convert_channel_layout(&layout, 6),
1284 ChannelLayout::from_channels(6)
1285 );
1286 }
1287
1288 #[test]
1293 fn codec_name_should_return_h264_for_h264_codec_id() {
1294 let name =
1295 unsafe { AudioDecoderInner::extract_codec_name(ff_sys::AVCodecID_AV_CODEC_ID_H264) };
1296 assert_eq!(name, "h264");
1297 }
1298
1299 #[test]
1300 fn codec_name_should_return_none_for_none_codec_id() {
1301 let name =
1302 unsafe { AudioDecoderInner::extract_codec_name(ff_sys::AVCodecID_AV_CODEC_ID_NONE) };
1303 assert_eq!(name, "none");
1304 }
1305
1306 #[test]
1307 fn unsupported_codec_error_should_include_codec_name() {
1308 let codec_id = ff_sys::AVCodecID_AV_CODEC_ID_MP3;
1309 let codec_name = unsafe { AudioDecoderInner::extract_codec_name(codec_id) };
1310 let error = crate::error::DecodeError::UnsupportedCodec {
1311 codec: format!("{codec_name} (codec_id={codec_id:?})"),
1312 };
1313 let msg = error.to_string();
1314 assert!(msg.contains("mp3"), "expected codec name in error: {msg}");
1315 assert!(
1316 msg.contains("codec_id="),
1317 "expected codec_id in error: {msg}"
1318 );
1319 }
1320}