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::container::ContainerInfo;
33use ff_format::time::{Rational, Timestamp};
34use ff_format::{AudioFrame, AudioStreamInfo, SampleFormat};
35use ff_sys::{
36 AVCodecContext, AVCodecID, AVFormatContext, AVFrame, AVMediaType_AVMEDIA_TYPE_AUDIO, AVPacket,
37 AVSampleFormat, SwrContext,
38};
39
40use crate::error::DecodeError;
41
42struct AvFormatContextGuard(*mut AVFormatContext);
44
45impl AvFormatContextGuard {
46 unsafe fn new(path: &Path) -> Result<Self, DecodeError> {
52 let format_ctx = unsafe {
54 ff_sys::avformat::open_input(path).map_err(|e| DecodeError::Ffmpeg {
55 code: e,
56 message: format!("Failed to open file: {}", ff_sys::av_error_string(e)),
57 })?
58 };
59 Ok(Self(format_ctx))
60 }
61
62 const fn as_ptr(&self) -> *mut AVFormatContext {
64 self.0
65 }
66
67 fn into_raw(self) -> *mut AVFormatContext {
69 let ptr = self.0;
70 std::mem::forget(self);
71 ptr
72 }
73}
74
75impl Drop for AvFormatContextGuard {
76 fn drop(&mut self) {
77 if !self.0.is_null() {
78 unsafe {
80 ff_sys::avformat::close_input(&mut (self.0 as *mut _));
81 }
82 }
83 }
84}
85
86struct AvCodecContextGuard(*mut AVCodecContext);
88
89impl AvCodecContextGuard {
90 unsafe fn new(codec: *const ff_sys::AVCodec) -> Result<Self, DecodeError> {
96 let codec_ctx = unsafe {
98 ff_sys::avcodec::alloc_context3(codec).map_err(|e| DecodeError::Ffmpeg {
99 code: e,
100 message: format!("Failed to allocate codec context: {e}"),
101 })?
102 };
103 Ok(Self(codec_ctx))
104 }
105
106 const fn as_ptr(&self) -> *mut AVCodecContext {
108 self.0
109 }
110
111 fn into_raw(self) -> *mut AVCodecContext {
113 let ptr = self.0;
114 std::mem::forget(self);
115 ptr
116 }
117}
118
119impl Drop for AvCodecContextGuard {
120 fn drop(&mut self) {
121 if !self.0.is_null() {
122 unsafe {
124 ff_sys::avcodec::free_context(&mut (self.0 as *mut _));
125 }
126 }
127 }
128}
129
130struct AvPacketGuard(*mut AVPacket);
132
133impl AvPacketGuard {
134 unsafe fn new() -> Result<Self, DecodeError> {
140 let packet = unsafe { ff_sys::av_packet_alloc() };
142 if packet.is_null() {
143 return Err(DecodeError::Ffmpeg {
144 code: 0,
145 message: "Failed to allocate packet".to_string(),
146 });
147 }
148 Ok(Self(packet))
149 }
150
151 fn into_raw(self) -> *mut AVPacket {
153 let ptr = self.0;
154 std::mem::forget(self);
155 ptr
156 }
157}
158
159impl Drop for AvPacketGuard {
160 fn drop(&mut self) {
161 if !self.0.is_null() {
162 unsafe {
164 ff_sys::av_packet_free(&mut (self.0 as *mut _));
165 }
166 }
167 }
168}
169
170struct AvFrameGuard(*mut AVFrame);
172
173impl AvFrameGuard {
174 unsafe fn new() -> Result<Self, DecodeError> {
180 let frame = unsafe { ff_sys::av_frame_alloc() };
182 if frame.is_null() {
183 return Err(DecodeError::Ffmpeg {
184 code: 0,
185 message: "Failed to allocate frame".to_string(),
186 });
187 }
188 Ok(Self(frame))
189 }
190
191 fn into_raw(self) -> *mut AVFrame {
193 let ptr = self.0;
194 std::mem::forget(self);
195 ptr
196 }
197}
198
199impl Drop for AvFrameGuard {
200 fn drop(&mut self) {
201 if !self.0.is_null() {
202 unsafe {
204 ff_sys::av_frame_free(&mut (self.0 as *mut _));
205 }
206 }
207 }
208}
209
210struct SwrContextGuard(*mut SwrContext);
212
213impl SwrContextGuard {
214 #[allow(dead_code)]
216 fn into_raw(self) -> *mut SwrContext {
217 let ptr = self.0;
218 std::mem::forget(self);
219 ptr
220 }
221}
222
223impl Drop for SwrContextGuard {
224 fn drop(&mut self) {
225 if !self.0.is_null() {
226 unsafe {
228 ff_sys::swr_free(&mut (self.0 as *mut _));
229 }
230 }
231 }
232}
233
234pub(crate) struct AudioDecoderInner {
239 format_ctx: *mut AVFormatContext,
241 codec_ctx: *mut AVCodecContext,
243 stream_index: i32,
245 swr_ctx: Option<*mut SwrContext>,
247 output_format: Option<SampleFormat>,
249 output_sample_rate: Option<u32>,
251 output_channels: Option<u32>,
253 eof: bool,
255 position: Duration,
257 packet: *mut AVPacket,
259 frame: *mut AVFrame,
261}
262
263impl AudioDecoderInner {
264 pub(crate) fn new(
281 path: &Path,
282 output_format: Option<SampleFormat>,
283 output_sample_rate: Option<u32>,
284 output_channels: Option<u32>,
285 ) -> Result<(Self, AudioStreamInfo, ContainerInfo), DecodeError> {
286 ff_sys::ensure_initialized();
288
289 let format_ctx_guard = unsafe { AvFormatContextGuard::new(path)? };
292 let format_ctx = format_ctx_guard.as_ptr();
293
294 unsafe {
297 ff_sys::avformat::find_stream_info(format_ctx).map_err(|e| DecodeError::Ffmpeg {
298 code: e,
299 message: format!("Failed to find stream info: {}", ff_sys::av_error_string(e)),
300 })?;
301 }
302
303 let (stream_index, codec_id) =
306 unsafe { Self::find_audio_stream(format_ctx) }.ok_or_else(|| {
307 DecodeError::NoAudioStream {
308 path: path.to_path_buf(),
309 }
310 })?;
311
312 let codec_name = unsafe { Self::extract_codec_name(codec_id) };
315 let codec = unsafe {
316 ff_sys::avcodec::find_decoder(codec_id).ok_or_else(|| {
317 DecodeError::UnsupportedCodec {
318 codec: format!("{codec_name} (codec_id={codec_id:?})"),
319 }
320 })?
321 };
322
323 let codec_ctx_guard = unsafe { AvCodecContextGuard::new(codec)? };
326 let codec_ctx = codec_ctx_guard.as_ptr();
327
328 unsafe {
331 let stream = (*format_ctx).streams.add(stream_index as usize);
332 let codecpar = (*(*stream)).codecpar;
333 ff_sys::avcodec::parameters_to_context(codec_ctx, codecpar).map_err(|e| {
334 DecodeError::Ffmpeg {
335 code: e,
336 message: format!(
337 "Failed to copy codec parameters: {}",
338 ff_sys::av_error_string(e)
339 ),
340 }
341 })?;
342 }
343
344 unsafe {
347 ff_sys::avcodec::open2(codec_ctx, codec, ptr::null_mut()).map_err(|e| {
348 DecodeError::Ffmpeg {
349 code: e,
350 message: format!("Failed to open codec: {}", ff_sys::av_error_string(e)),
351 }
352 })?;
353 }
354
355 let stream_info =
358 unsafe { Self::extract_stream_info(format_ctx, stream_index as i32, codec_ctx)? };
359
360 let container_info = unsafe { Self::extract_container_info(format_ctx) };
363
364 let packet_guard = unsafe { AvPacketGuard::new()? };
367 let frame_guard = unsafe { AvFrameGuard::new()? };
368
369 Ok((
371 Self {
372 format_ctx: format_ctx_guard.into_raw(),
373 codec_ctx: codec_ctx_guard.into_raw(),
374 stream_index: stream_index as i32,
375 swr_ctx: None,
376 output_format,
377 output_sample_rate,
378 output_channels,
379 eof: false,
380 position: Duration::ZERO,
381 packet: packet_guard.into_raw(),
382 frame: frame_guard.into_raw(),
383 },
384 stream_info,
385 container_info,
386 ))
387 }
388
389 unsafe fn find_audio_stream(format_ctx: *mut AVFormatContext) -> Option<(usize, AVCodecID)> {
399 unsafe {
401 let nb_streams = (*format_ctx).nb_streams as usize;
402
403 for i in 0..nb_streams {
404 let stream = (*format_ctx).streams.add(i);
405 let codecpar = (*(*stream)).codecpar;
406
407 if (*codecpar).codec_type == AVMediaType_AVMEDIA_TYPE_AUDIO {
408 return Some((i, (*codecpar).codec_id));
409 }
410 }
411
412 None
413 }
414 }
415
416 unsafe fn extract_codec_name(codec_id: ff_sys::AVCodecID) -> String {
418 let name_ptr = unsafe { ff_sys::avcodec_get_name(codec_id) };
420 if name_ptr.is_null() {
421 return String::from("unknown");
422 }
423 unsafe { CStr::from_ptr(name_ptr).to_string_lossy().into_owned() }
425 }
426
427 unsafe fn extract_stream_info(
429 format_ctx: *mut AVFormatContext,
430 stream_index: i32,
431 codec_ctx: *mut AVCodecContext,
432 ) -> Result<AudioStreamInfo, DecodeError> {
433 let (sample_rate, channels, sample_fmt, duration_val, channel_layout, codec_id) = unsafe {
435 let stream = (*format_ctx).streams.add(stream_index as usize);
436 let codecpar = (*(*stream)).codecpar;
437
438 (
439 (*codecpar).sample_rate as u32,
440 (*codecpar).ch_layout.nb_channels as u32,
441 (*codec_ctx).sample_fmt,
442 (*format_ctx).duration,
443 (*codecpar).ch_layout,
444 (*codecpar).codec_id,
445 )
446 };
447
448 let duration = if duration_val > 0 {
450 let duration_secs = duration_val as f64 / 1_000_000.0;
451 Some(Duration::from_secs_f64(duration_secs))
452 } else {
453 None
454 };
455
456 let sample_format = Self::convert_sample_format(sample_fmt);
458
459 let channel_layout_enum = Self::convert_channel_layout(&channel_layout, channels);
461
462 let codec = Self::convert_codec(codec_id);
464 let codec_name = unsafe { Self::extract_codec_name(codec_id) };
465
466 let mut builder = AudioStreamInfo::builder()
468 .index(stream_index as u32)
469 .codec(codec)
470 .codec_name(codec_name)
471 .sample_rate(sample_rate)
472 .channels(channels)
473 .sample_format(sample_format)
474 .channel_layout(channel_layout_enum);
475
476 if let Some(d) = duration {
477 builder = builder.duration(d);
478 }
479
480 Ok(builder.build())
481 }
482
483 unsafe fn extract_container_info(format_ctx: *mut AVFormatContext) -> ContainerInfo {
489 unsafe {
491 let format_name = if (*format_ctx).iformat.is_null() {
492 String::new()
493 } else {
494 let ptr = (*(*format_ctx).iformat).name;
495 if ptr.is_null() {
496 String::new()
497 } else {
498 CStr::from_ptr(ptr).to_string_lossy().into_owned()
499 }
500 };
501
502 let bit_rate = {
503 let br = (*format_ctx).bit_rate;
504 if br > 0 { Some(br as u64) } else { None }
505 };
506
507 let nb_streams = (*format_ctx).nb_streams as u32;
508
509 let mut builder = ContainerInfo::builder()
510 .format_name(format_name)
511 .nb_streams(nb_streams);
512 if let Some(br) = bit_rate {
513 builder = builder.bit_rate(br);
514 }
515 builder.build()
516 }
517 }
518
519 fn convert_sample_format(fmt: AVSampleFormat) -> SampleFormat {
521 if fmt == ff_sys::AVSampleFormat_AV_SAMPLE_FMT_U8 {
522 SampleFormat::U8
523 } else if fmt == ff_sys::AVSampleFormat_AV_SAMPLE_FMT_S16 {
524 SampleFormat::I16
525 } else if fmt == ff_sys::AVSampleFormat_AV_SAMPLE_FMT_S32 {
526 SampleFormat::I32
527 } else if fmt == ff_sys::AVSampleFormat_AV_SAMPLE_FMT_FLT {
528 SampleFormat::F32
529 } else if fmt == ff_sys::AVSampleFormat_AV_SAMPLE_FMT_DBL {
530 SampleFormat::F64
531 } else if fmt == ff_sys::AVSampleFormat_AV_SAMPLE_FMT_U8P {
532 SampleFormat::U8p
533 } else if fmt == ff_sys::AVSampleFormat_AV_SAMPLE_FMT_S16P {
534 SampleFormat::I16p
535 } else if fmt == ff_sys::AVSampleFormat_AV_SAMPLE_FMT_S32P {
536 SampleFormat::I32p
537 } else if fmt == ff_sys::AVSampleFormat_AV_SAMPLE_FMT_FLTP {
538 SampleFormat::F32p
539 } else if fmt == ff_sys::AVSampleFormat_AV_SAMPLE_FMT_DBLP {
540 SampleFormat::F64p
541 } else {
542 log::warn!(
543 "sample_format unsupported, falling back to F32 requested={fmt} fallback=F32"
544 );
545 SampleFormat::F32
546 }
547 }
548
549 fn convert_channel_layout(layout: &ff_sys::AVChannelLayout, channels: u32) -> ChannelLayout {
551 if layout.order == ff_sys::AVChannelOrder_AV_CHANNEL_ORDER_NATIVE {
552 let mask = unsafe { layout.u.mask };
554 match mask {
555 0x4 => ChannelLayout::Mono,
556 0x3 => ChannelLayout::Stereo,
557 0x103 => ChannelLayout::Stereo2_1,
558 0x7 => ChannelLayout::Surround3_0,
559 0x33 => ChannelLayout::Quad,
560 0x37 => ChannelLayout::Surround5_0,
561 0x3F => ChannelLayout::Surround5_1,
562 0x13F => ChannelLayout::Surround6_1,
563 0x63F => ChannelLayout::Surround7_1,
564 _ => {
565 log::warn!(
566 "channel_layout mask has no mapping, deriving from channel count \
567 mask={mask} channels={channels}"
568 );
569 ChannelLayout::from_channels(channels)
570 }
571 }
572 } else {
573 log::warn!(
574 "channel_layout order is not NATIVE, deriving from channel count \
575 order={order} channels={channels}",
576 order = layout.order
577 );
578 ChannelLayout::from_channels(channels)
579 }
580 }
581
582 unsafe fn create_channel_layout(channels: u32) -> ff_sys::AVChannelLayout {
588 let mut layout = unsafe { std::mem::zeroed::<ff_sys::AVChannelLayout>() };
590 unsafe {
592 ff_sys::av_channel_layout_default(&raw mut layout, channels as i32);
593 }
594 layout
595 }
596
597 fn convert_codec(codec_id: AVCodecID) -> AudioCodec {
599 if codec_id == ff_sys::AVCodecID_AV_CODEC_ID_AAC {
600 AudioCodec::Aac
601 } else if codec_id == ff_sys::AVCodecID_AV_CODEC_ID_MP3 {
602 AudioCodec::Mp3
603 } else if codec_id == ff_sys::AVCodecID_AV_CODEC_ID_OPUS {
604 AudioCodec::Opus
605 } else if codec_id == ff_sys::AVCodecID_AV_CODEC_ID_VORBIS {
606 AudioCodec::Vorbis
607 } else if codec_id == ff_sys::AVCodecID_AV_CODEC_ID_FLAC {
608 AudioCodec::Flac
609 } else if codec_id == ff_sys::AVCodecID_AV_CODEC_ID_PCM_S16LE {
610 AudioCodec::Pcm
611 } else {
612 log::warn!(
613 "audio codec unsupported, falling back to Aac codec_id={codec_id} fallback=Aac"
614 );
615 AudioCodec::Aac
616 }
617 }
618
619 pub(crate) fn decode_one(&mut self) -> Result<Option<AudioFrame>, DecodeError> {
627 if self.eof {
628 return Ok(None);
629 }
630
631 unsafe {
632 loop {
633 let ret = ff_sys::avcodec_receive_frame(self.codec_ctx, self.frame);
635
636 if ret == 0 {
637 let audio_frame = self.convert_frame_to_audio_frame()?;
639
640 let pts = (*self.frame).pts;
642 if pts != ff_sys::AV_NOPTS_VALUE {
643 let stream = (*self.format_ctx).streams.add(self.stream_index as usize);
644 let time_base = (*(*stream)).time_base;
645 let timestamp_secs =
646 pts as f64 * time_base.num as f64 / time_base.den as f64;
647 self.position = Duration::from_secs_f64(timestamp_secs);
648 }
649
650 return Ok(Some(audio_frame));
651 } else if ret == ff_sys::error_codes::EAGAIN {
652 let read_ret = ff_sys::av_read_frame(self.format_ctx, self.packet);
655
656 if read_ret == ff_sys::error_codes::EOF {
657 ff_sys::avcodec_send_packet(self.codec_ctx, ptr::null());
659 self.eof = true;
660 continue;
661 } else if read_ret < 0 {
662 return Err(DecodeError::Ffmpeg {
663 code: read_ret,
664 message: format!(
665 "Failed to read frame: {}",
666 ff_sys::av_error_string(read_ret)
667 ),
668 });
669 }
670
671 if (*self.packet).stream_index == self.stream_index {
673 let send_ret = ff_sys::avcodec_send_packet(self.codec_ctx, self.packet);
675 ff_sys::av_packet_unref(self.packet);
676
677 if send_ret < 0 && send_ret != ff_sys::error_codes::EAGAIN {
678 return Err(DecodeError::Ffmpeg {
679 code: send_ret,
680 message: format!(
681 "Failed to send packet: {}",
682 ff_sys::av_error_string(send_ret)
683 ),
684 });
685 }
686 } else {
687 ff_sys::av_packet_unref(self.packet);
689 }
690 } else if ret == ff_sys::error_codes::EOF {
691 self.eof = true;
693 return Ok(None);
694 } else {
695 return Err(DecodeError::DecodingFailed {
696 timestamp: Some(self.position),
697 reason: ff_sys::av_error_string(ret),
698 });
699 }
700 }
701 }
702 }
703
704 unsafe fn convert_frame_to_audio_frame(&mut self) -> Result<AudioFrame, DecodeError> {
706 unsafe {
708 let nb_samples = (*self.frame).nb_samples as usize;
709 let channels = (*self.frame).ch_layout.nb_channels as u32;
710 let sample_rate = (*self.frame).sample_rate as u32;
711 let src_format = (*self.frame).format;
712
713 let needs_conversion = self.output_format.is_some()
715 || self.output_sample_rate.is_some()
716 || self.output_channels.is_some();
717
718 if needs_conversion {
719 self.convert_with_swr(nb_samples, channels, sample_rate, src_format)
720 } else {
721 self.av_frame_to_audio_frame(self.frame)
722 }
723 }
724 }
725
726 unsafe fn convert_with_swr(
728 &mut self,
729 nb_samples: usize,
730 src_channels: u32,
731 src_sample_rate: u32,
732 src_format: i32,
733 ) -> Result<AudioFrame, DecodeError> {
734 let dst_format = self
736 .output_format
737 .map_or(src_format, Self::sample_format_to_av);
738 let dst_sample_rate = self.output_sample_rate.unwrap_or(src_sample_rate);
739 let dst_channels = self.output_channels.unwrap_or(src_channels);
740
741 if src_format == dst_format
743 && src_sample_rate == dst_sample_rate
744 && src_channels == dst_channels
745 {
746 return unsafe { self.av_frame_to_audio_frame(self.frame) };
747 }
748
749 let mut src_ch_layout = unsafe { Self::create_channel_layout(src_channels) };
752 let mut dst_ch_layout = unsafe { Self::create_channel_layout(dst_channels) };
753
754 let mut swr_ctx: *mut SwrContext = ptr::null_mut();
756
757 let ret = unsafe {
759 ff_sys::swr_alloc_set_opts2(
760 &raw mut swr_ctx,
761 &raw const dst_ch_layout,
762 dst_format,
763 dst_sample_rate as i32,
764 &raw const src_ch_layout,
765 src_format,
766 src_sample_rate as i32,
767 0,
768 ptr::null_mut(),
769 )
770 };
771
772 if ret < 0 {
773 unsafe {
775 ff_sys::av_channel_layout_uninit(&raw mut src_ch_layout);
776 ff_sys::av_channel_layout_uninit(&raw mut dst_ch_layout);
777 }
778 return Err(DecodeError::Ffmpeg {
779 code: ret,
780 message: format!(
781 "Failed to allocate SwrContext: {}",
782 ff_sys::av_error_string(ret)
783 ),
784 });
785 }
786
787 let _swr_guard = SwrContextGuard(swr_ctx);
789
790 let ret = unsafe { ff_sys::swr_init(swr_ctx) };
793 if ret < 0 {
794 unsafe {
796 ff_sys::av_channel_layout_uninit(&raw mut src_ch_layout);
797 ff_sys::av_channel_layout_uninit(&raw mut dst_ch_layout);
798 }
799 return Err(DecodeError::Ffmpeg {
800 code: ret,
801 message: format!(
802 "Failed to initialize SwrContext: {}",
803 ff_sys::av_error_string(ret)
804 ),
805 });
806 }
807
808 let out_samples = unsafe { ff_sys::swr_get_out_samples(swr_ctx, nb_samples as i32) };
811
812 if out_samples < 0 {
813 unsafe {
815 ff_sys::av_channel_layout_uninit(&raw mut src_ch_layout);
816 ff_sys::av_channel_layout_uninit(&raw mut dst_ch_layout);
817 }
818 return Err(DecodeError::Ffmpeg {
819 code: 0,
820 message: "Failed to calculate output sample count".to_string(),
821 });
822 }
823
824 let out_samples = out_samples as usize;
825
826 let dst_sample_fmt = Self::convert_sample_format(dst_format);
828 let bytes_per_sample = dst_sample_fmt.bytes_per_sample();
829 let is_planar = dst_sample_fmt.is_planar();
830
831 let buffer_size = if is_planar {
833 out_samples * bytes_per_sample * dst_channels as usize
835 } else {
836 out_samples * bytes_per_sample * dst_channels as usize
838 };
839
840 let mut out_buffer = vec![0u8; buffer_size];
841
842 let mut out_ptrs = if is_planar {
844 let plane_size = out_samples * bytes_per_sample;
846 (0..dst_channels)
847 .map(|i| {
848 let offset = i as usize * plane_size;
849 out_buffer[offset..].as_mut_ptr()
850 })
851 .collect::<Vec<_>>()
852 } else {
853 vec![out_buffer.as_mut_ptr()]
855 };
856
857 let in_ptrs = unsafe { (*self.frame).data };
860
861 let converted_samples = unsafe {
864 ff_sys::swr_convert(
865 swr_ctx,
866 out_ptrs.as_mut_ptr(),
867 out_samples as i32,
868 in_ptrs.as_ptr() as *mut *const u8,
869 nb_samples as i32,
870 )
871 };
872
873 unsafe {
875 ff_sys::av_channel_layout_uninit(&raw mut src_ch_layout);
876 ff_sys::av_channel_layout_uninit(&raw mut dst_ch_layout);
877 }
878
879 if converted_samples < 0 {
880 return Err(DecodeError::Ffmpeg {
881 code: converted_samples,
882 message: format!(
883 "Failed to convert samples: {}",
884 ff_sys::av_error_string(converted_samples)
885 ),
886 });
887 }
888
889 let timestamp = unsafe {
892 let pts = (*self.frame).pts;
893 if pts != ff_sys::AV_NOPTS_VALUE {
894 let stream = (*self.format_ctx).streams.add(self.stream_index as usize);
895 let time_base = (*(*stream)).time_base;
896 Timestamp::new(pts, Rational::new(time_base.num, time_base.den))
897 } else {
898 Timestamp::invalid()
899 }
900 };
901
902 let planes = if is_planar {
904 let plane_size = converted_samples as usize * bytes_per_sample;
905 (0..dst_channels)
906 .map(|i| {
907 let offset = i as usize * plane_size;
908 out_buffer[offset..offset + plane_size].to_vec()
909 })
910 .collect()
911 } else {
912 vec![
914 out_buffer[..converted_samples as usize * bytes_per_sample * dst_channels as usize]
915 .to_vec(),
916 ]
917 };
918
919 AudioFrame::new(
920 planes,
921 converted_samples as usize,
922 dst_channels,
923 dst_sample_rate,
924 dst_sample_fmt,
925 timestamp,
926 )
927 .map_err(|e| DecodeError::Ffmpeg {
928 code: 0,
929 message: format!("Failed to create AudioFrame: {e}"),
930 })
931 }
932
933 unsafe fn av_frame_to_audio_frame(
935 &self,
936 frame: *const AVFrame,
937 ) -> Result<AudioFrame, DecodeError> {
938 unsafe {
940 let nb_samples = (*frame).nb_samples as usize;
941 let channels = (*frame).ch_layout.nb_channels as u32;
942 let sample_rate = (*frame).sample_rate as u32;
943 let format = Self::convert_sample_format((*frame).format);
944
945 let pts = (*frame).pts;
947 let timestamp = if pts != ff_sys::AV_NOPTS_VALUE {
948 let stream = (*self.format_ctx).streams.add(self.stream_index as usize);
949 let time_base = (*(*stream)).time_base;
950 Timestamp::new(
951 pts as i64,
952 Rational::new(time_base.num as i32, time_base.den as i32),
953 )
954 } else {
955 Timestamp::invalid()
956 };
957
958 let planes = Self::extract_planes(frame, nb_samples, channels, format)?;
960
961 AudioFrame::new(planes, nb_samples, channels, sample_rate, format, timestamp).map_err(
962 |e| DecodeError::Ffmpeg {
963 code: 0,
964 message: format!("Failed to create AudioFrame: {e}"),
965 },
966 )
967 }
968 }
969
970 unsafe fn extract_planes(
972 frame: *const AVFrame,
973 nb_samples: usize,
974 channels: u32,
975 format: SampleFormat,
976 ) -> Result<Vec<Vec<u8>>, DecodeError> {
977 unsafe {
979 let mut planes = Vec::new();
980 let bytes_per_sample = format.bytes_per_sample();
981
982 if format.is_planar() {
983 for ch in 0..channels as usize {
985 let plane_size = nb_samples * bytes_per_sample;
986 let mut plane_data = vec![0u8; plane_size];
987
988 let src_ptr = (*frame).data[ch];
989 std::ptr::copy_nonoverlapping(src_ptr, plane_data.as_mut_ptr(), plane_size);
990
991 planes.push(plane_data);
992 }
993 } else {
994 let plane_size = nb_samples * channels as usize * bytes_per_sample;
996 let mut plane_data = vec![0u8; plane_size];
997
998 let src_ptr = (*frame).data[0];
999 std::ptr::copy_nonoverlapping(src_ptr, plane_data.as_mut_ptr(), plane_size);
1000
1001 planes.push(plane_data);
1002 }
1003
1004 Ok(planes)
1005 }
1006 }
1007
1008 fn sample_format_to_av(format: SampleFormat) -> AVSampleFormat {
1010 match format {
1011 SampleFormat::U8 => ff_sys::AVSampleFormat_AV_SAMPLE_FMT_U8,
1012 SampleFormat::I16 => ff_sys::AVSampleFormat_AV_SAMPLE_FMT_S16,
1013 SampleFormat::I32 => ff_sys::AVSampleFormat_AV_SAMPLE_FMT_S32,
1014 SampleFormat::F32 => ff_sys::AVSampleFormat_AV_SAMPLE_FMT_FLT,
1015 SampleFormat::F64 => ff_sys::AVSampleFormat_AV_SAMPLE_FMT_DBL,
1016 SampleFormat::U8p => ff_sys::AVSampleFormat_AV_SAMPLE_FMT_U8P,
1017 SampleFormat::I16p => ff_sys::AVSampleFormat_AV_SAMPLE_FMT_S16P,
1018 SampleFormat::I32p => ff_sys::AVSampleFormat_AV_SAMPLE_FMT_S32P,
1019 SampleFormat::F32p => ff_sys::AVSampleFormat_AV_SAMPLE_FMT_FLTP,
1020 SampleFormat::F64p => ff_sys::AVSampleFormat_AV_SAMPLE_FMT_DBLP,
1021 _ => {
1022 log::warn!(
1023 "sample_format has no AV mapping, falling back to F32 format={format:?} fallback=AV_SAMPLE_FMT_FLT"
1024 );
1025 ff_sys::AVSampleFormat_AV_SAMPLE_FMT_FLT
1026 }
1027 }
1028 }
1029
1030 pub(crate) fn position(&self) -> Duration {
1032 self.position
1033 }
1034
1035 pub(crate) fn is_eof(&self) -> bool {
1037 self.eof
1038 }
1039
1040 fn duration_to_pts(&self, duration: Duration) -> i64 {
1042 let time_base = unsafe {
1044 let stream = (*self.format_ctx).streams.add(self.stream_index as usize);
1045 (*(*stream)).time_base
1046 };
1047
1048 let time_base_f64 = time_base.den as f64 / time_base.num as f64;
1050 (duration.as_secs_f64() * time_base_f64) as i64
1051 }
1052
1053 pub(crate) fn seek(
1064 &mut self,
1065 position: Duration,
1066 mode: crate::SeekMode,
1067 ) -> Result<(), DecodeError> {
1068 use crate::SeekMode;
1069
1070 let timestamp = self.duration_to_pts(position);
1071 let flags = ff_sys::avformat::seek_flags::BACKWARD;
1072
1073 unsafe {
1076 ff_sys::av_packet_unref(self.packet);
1077 ff_sys::av_frame_unref(self.frame);
1078 }
1079
1080 unsafe {
1083 ff_sys::avformat::seek_frame(self.format_ctx, self.stream_index, timestamp, flags)
1084 .map_err(|e| DecodeError::SeekFailed {
1085 target: position,
1086 reason: ff_sys::av_error_string(e),
1087 })?;
1088 }
1089
1090 unsafe {
1093 ff_sys::avcodec::flush_buffers(self.codec_ctx);
1094 }
1095
1096 unsafe {
1099 loop {
1100 let ret = ff_sys::avcodec_receive_frame(self.codec_ctx, self.frame);
1101 if ret == ff_sys::error_codes::EAGAIN || ret == ff_sys::error_codes::EOF {
1102 break;
1103 } else if ret == 0 {
1104 ff_sys::av_frame_unref(self.frame);
1105 } else {
1106 break;
1107 }
1108 }
1109 }
1110
1111 self.eof = false;
1113
1114 if mode == SeekMode::Exact {
1116 self.skip_to_exact(position)?;
1117 }
1118 Ok(())
1121 }
1122
1123 fn skip_to_exact(&mut self, target: Duration) -> Result<(), DecodeError> {
1131 while let Some(frame) = self.decode_one()? {
1133 let frame_time = frame.timestamp().as_duration();
1134 if frame_time >= target {
1135 break;
1137 }
1138 }
1140 Ok(())
1141 }
1142
1143 pub(crate) fn flush(&mut self) {
1145 unsafe {
1147 ff_sys::avcodec::flush_buffers(self.codec_ctx);
1148 }
1149 self.eof = false;
1150 }
1151}
1152
1153impl Drop for AudioDecoderInner {
1154 fn drop(&mut self) {
1155 if let Some(swr_ctx) = self.swr_ctx {
1157 unsafe {
1159 ff_sys::swr_free(&mut (swr_ctx as *mut _));
1161 }
1162 }
1163
1164 if !self.frame.is_null() {
1166 unsafe {
1168 ff_sys::av_frame_free(&mut (self.frame as *mut _));
1169 }
1170 }
1171
1172 if !self.packet.is_null() {
1173 unsafe {
1175 ff_sys::av_packet_free(&mut (self.packet as *mut _));
1176 }
1177 }
1178
1179 if !self.codec_ctx.is_null() {
1181 unsafe {
1183 ff_sys::avcodec::free_context(&mut (self.codec_ctx as *mut _));
1184 }
1185 }
1186
1187 if !self.format_ctx.is_null() {
1189 unsafe {
1191 ff_sys::avformat::close_input(&mut (self.format_ctx as *mut _));
1192 }
1193 }
1194 }
1195}
1196
1197unsafe impl Send for AudioDecoderInner {}
1200
1201#[cfg(test)]
1202#[allow(unsafe_code)]
1203mod tests {
1204 use ff_format::channel::ChannelLayout;
1205
1206 use super::AudioDecoderInner;
1207
1208 fn native_layout(mask: u64, nb_channels: i32) -> ff_sys::AVChannelLayout {
1210 ff_sys::AVChannelLayout {
1211 order: ff_sys::AVChannelOrder_AV_CHANNEL_ORDER_NATIVE,
1212 nb_channels,
1213 u: ff_sys::AVChannelLayout__bindgen_ty_1 { mask },
1214 opaque: std::ptr::null_mut(),
1215 }
1216 }
1217
1218 fn unspec_layout(nb_channels: i32) -> ff_sys::AVChannelLayout {
1220 ff_sys::AVChannelLayout {
1221 order: ff_sys::AVChannelOrder_AV_CHANNEL_ORDER_UNSPEC,
1222 nb_channels,
1223 u: ff_sys::AVChannelLayout__bindgen_ty_1 { mask: 0 },
1224 opaque: std::ptr::null_mut(),
1225 }
1226 }
1227
1228 #[test]
1229 fn native_mask_mono() {
1230 let layout = native_layout(0x4, 1);
1231 assert_eq!(
1232 AudioDecoderInner::convert_channel_layout(&layout, 1),
1233 ChannelLayout::Mono
1234 );
1235 }
1236
1237 #[test]
1238 fn native_mask_stereo() {
1239 let layout = native_layout(0x3, 2);
1240 assert_eq!(
1241 AudioDecoderInner::convert_channel_layout(&layout, 2),
1242 ChannelLayout::Stereo
1243 );
1244 }
1245
1246 #[test]
1247 fn native_mask_stereo2_1() {
1248 let layout = native_layout(0x103, 3);
1249 assert_eq!(
1250 AudioDecoderInner::convert_channel_layout(&layout, 3),
1251 ChannelLayout::Stereo2_1
1252 );
1253 }
1254
1255 #[test]
1256 fn native_mask_surround3_0() {
1257 let layout = native_layout(0x7, 3);
1258 assert_eq!(
1259 AudioDecoderInner::convert_channel_layout(&layout, 3),
1260 ChannelLayout::Surround3_0
1261 );
1262 }
1263
1264 #[test]
1265 fn native_mask_quad() {
1266 let layout = native_layout(0x33, 4);
1267 assert_eq!(
1268 AudioDecoderInner::convert_channel_layout(&layout, 4),
1269 ChannelLayout::Quad
1270 );
1271 }
1272
1273 #[test]
1274 fn native_mask_surround5_0() {
1275 let layout = native_layout(0x37, 5);
1276 assert_eq!(
1277 AudioDecoderInner::convert_channel_layout(&layout, 5),
1278 ChannelLayout::Surround5_0
1279 );
1280 }
1281
1282 #[test]
1283 fn native_mask_surround5_1() {
1284 let layout = native_layout(0x3F, 6);
1285 assert_eq!(
1286 AudioDecoderInner::convert_channel_layout(&layout, 6),
1287 ChannelLayout::Surround5_1
1288 );
1289 }
1290
1291 #[test]
1292 fn native_mask_surround6_1() {
1293 let layout = native_layout(0x13F, 7);
1294 assert_eq!(
1295 AudioDecoderInner::convert_channel_layout(&layout, 7),
1296 ChannelLayout::Surround6_1
1297 );
1298 }
1299
1300 #[test]
1301 fn native_mask_surround7_1() {
1302 let layout = native_layout(0x63F, 8);
1303 assert_eq!(
1304 AudioDecoderInner::convert_channel_layout(&layout, 8),
1305 ChannelLayout::Surround7_1
1306 );
1307 }
1308
1309 #[test]
1310 fn native_mask_unknown_falls_back_to_from_channels() {
1311 let layout = native_layout(0x1, 2);
1313 assert_eq!(
1314 AudioDecoderInner::convert_channel_layout(&layout, 2),
1315 ChannelLayout::from_channels(2)
1316 );
1317 }
1318
1319 #[test]
1320 fn non_native_order_falls_back_to_from_channels() {
1321 let layout = unspec_layout(6);
1322 assert_eq!(
1323 AudioDecoderInner::convert_channel_layout(&layout, 6),
1324 ChannelLayout::from_channels(6)
1325 );
1326 }
1327
1328 #[test]
1333 fn codec_name_should_return_h264_for_h264_codec_id() {
1334 let name =
1335 unsafe { AudioDecoderInner::extract_codec_name(ff_sys::AVCodecID_AV_CODEC_ID_H264) };
1336 assert_eq!(name, "h264");
1337 }
1338
1339 #[test]
1340 fn codec_name_should_return_none_for_none_codec_id() {
1341 let name =
1342 unsafe { AudioDecoderInner::extract_codec_name(ff_sys::AVCodecID_AV_CODEC_ID_NONE) };
1343 assert_eq!(name, "none");
1344 }
1345
1346 #[test]
1347 fn unsupported_codec_error_should_include_codec_name() {
1348 let codec_id = ff_sys::AVCodecID_AV_CODEC_ID_MP3;
1349 let codec_name = unsafe { AudioDecoderInner::extract_codec_name(codec_id) };
1350 let error = crate::error::DecodeError::UnsupportedCodec {
1351 codec: format!("{codec_name} (codec_id={codec_id:?})"),
1352 };
1353 let msg = error.to_string();
1354 assert!(msg.contains("mp3"), "expected codec name in error: {msg}");
1355 assert!(
1356 msg.contains("codec_id="),
1357 "expected codec_id in error: {msg}"
1358 );
1359 }
1360}