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, NetworkOptions, 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 unsafe fn new_url(url: &str, network: &NetworkOptions) -> Result<Self, DecodeError> {
68 let format_ctx = unsafe {
70 ff_sys::avformat::open_input_url(url, network.connect_timeout, network.read_timeout)
71 .map_err(|e| {
72 crate::network::map_network_error(e, crate::network::sanitize_url(url))
73 })?
74 };
75 Ok(Self(format_ctx))
76 }
77
78 const fn as_ptr(&self) -> *mut AVFormatContext {
80 self.0
81 }
82
83 fn into_raw(self) -> *mut AVFormatContext {
85 let ptr = self.0;
86 std::mem::forget(self);
87 ptr
88 }
89}
90
91impl Drop for AvFormatContextGuard {
92 fn drop(&mut self) {
93 if !self.0.is_null() {
94 unsafe {
96 ff_sys::avformat::close_input(&mut (self.0 as *mut _));
97 }
98 }
99 }
100}
101
102struct AvCodecContextGuard(*mut AVCodecContext);
104
105impl AvCodecContextGuard {
106 unsafe fn new(codec: *const ff_sys::AVCodec) -> Result<Self, DecodeError> {
112 let codec_ctx = unsafe {
114 ff_sys::avcodec::alloc_context3(codec).map_err(|e| DecodeError::Ffmpeg {
115 code: e,
116 message: format!("Failed to allocate codec context: {e}"),
117 })?
118 };
119 Ok(Self(codec_ctx))
120 }
121
122 const fn as_ptr(&self) -> *mut AVCodecContext {
124 self.0
125 }
126
127 fn into_raw(self) -> *mut AVCodecContext {
129 let ptr = self.0;
130 std::mem::forget(self);
131 ptr
132 }
133}
134
135impl Drop for AvCodecContextGuard {
136 fn drop(&mut self) {
137 if !self.0.is_null() {
138 unsafe {
140 ff_sys::avcodec::free_context(&mut (self.0 as *mut _));
141 }
142 }
143 }
144}
145
146struct AvPacketGuard(*mut AVPacket);
148
149impl AvPacketGuard {
150 unsafe fn new() -> Result<Self, DecodeError> {
156 let packet = unsafe { ff_sys::av_packet_alloc() };
158 if packet.is_null() {
159 return Err(DecodeError::Ffmpeg {
160 code: 0,
161 message: "Failed to allocate packet".to_string(),
162 });
163 }
164 Ok(Self(packet))
165 }
166
167 fn into_raw(self) -> *mut AVPacket {
169 let ptr = self.0;
170 std::mem::forget(self);
171 ptr
172 }
173}
174
175impl Drop for AvPacketGuard {
176 fn drop(&mut self) {
177 if !self.0.is_null() {
178 unsafe {
180 ff_sys::av_packet_free(&mut (self.0 as *mut _));
181 }
182 }
183 }
184}
185
186struct AvFrameGuard(*mut AVFrame);
188
189impl AvFrameGuard {
190 unsafe fn new() -> Result<Self, DecodeError> {
196 let frame = unsafe { ff_sys::av_frame_alloc() };
198 if frame.is_null() {
199 return Err(DecodeError::Ffmpeg {
200 code: 0,
201 message: "Failed to allocate frame".to_string(),
202 });
203 }
204 Ok(Self(frame))
205 }
206
207 fn into_raw(self) -> *mut AVFrame {
209 let ptr = self.0;
210 std::mem::forget(self);
211 ptr
212 }
213}
214
215impl Drop for AvFrameGuard {
216 fn drop(&mut self) {
217 if !self.0.is_null() {
218 unsafe {
220 ff_sys::av_frame_free(&mut (self.0 as *mut _));
221 }
222 }
223 }
224}
225
226struct SwrContextGuard(*mut SwrContext);
228
229impl SwrContextGuard {
230 #[allow(dead_code)]
232 fn into_raw(self) -> *mut SwrContext {
233 let ptr = self.0;
234 std::mem::forget(self);
235 ptr
236 }
237}
238
239impl Drop for SwrContextGuard {
240 fn drop(&mut self) {
241 if !self.0.is_null() {
242 unsafe {
244 ff_sys::swr_free(&mut (self.0 as *mut _));
245 }
246 }
247 }
248}
249
250pub(crate) struct AudioDecoderInner {
255 format_ctx: *mut AVFormatContext,
257 codec_ctx: *mut AVCodecContext,
259 stream_index: i32,
261 swr_ctx: Option<*mut SwrContext>,
263 output_format: Option<SampleFormat>,
265 output_sample_rate: Option<u32>,
267 output_channels: Option<u32>,
269 is_live: bool,
271 eof: bool,
273 position: Duration,
275 packet: *mut AVPacket,
277 frame: *mut AVFrame,
279 url: Option<String>,
281 network_opts: NetworkOptions,
283 reconnect_count: u32,
285}
286
287impl AudioDecoderInner {
288 #[allow(clippy::too_many_arguments)]
305 pub(crate) fn new(
306 path: &Path,
307 output_format: Option<SampleFormat>,
308 output_sample_rate: Option<u32>,
309 output_channels: Option<u32>,
310 network_opts: Option<NetworkOptions>,
311 ) -> Result<(Self, AudioStreamInfo, ContainerInfo), DecodeError> {
312 ff_sys::ensure_initialized();
314
315 let path_str = path.to_str().unwrap_or("");
316 let is_network_url = crate::network::is_url(path_str);
317
318 let url = if is_network_url {
319 Some(path_str.to_owned())
320 } else {
321 None
322 };
323 let stored_network_opts = network_opts.clone().unwrap_or_default();
324
325 if is_network_url {
327 crate::network::check_srt_url(path_str)?;
328 }
329
330 let format_ctx_guard = unsafe {
333 if is_network_url {
334 let network = network_opts.unwrap_or_default();
335 log::info!(
336 "opening network audio source url={} connect_timeout_ms={} read_timeout_ms={}",
337 crate::network::sanitize_url(path_str),
338 network.connect_timeout.as_millis(),
339 network.read_timeout.as_millis()
340 );
341 AvFormatContextGuard::new_url(path_str, &network)?
342 } else {
343 AvFormatContextGuard::new(path)?
344 }
345 };
346 let format_ctx = format_ctx_guard.as_ptr();
347
348 unsafe {
351 ff_sys::avformat::find_stream_info(format_ctx).map_err(|e| DecodeError::Ffmpeg {
352 code: e,
353 message: format!("Failed to find stream info: {}", ff_sys::av_error_string(e)),
354 })?;
355 }
356
357 let is_live = unsafe {
361 let iformat = (*format_ctx).iformat;
362 !iformat.is_null() && ((*iformat).flags & ff_sys::AVFMT_TS_DISCONT) != 0
363 };
364
365 let (stream_index, codec_id) =
368 unsafe { Self::find_audio_stream(format_ctx) }.ok_or_else(|| {
369 DecodeError::NoAudioStream {
370 path: path.to_path_buf(),
371 }
372 })?;
373
374 let codec_name = unsafe { Self::extract_codec_name(codec_id) };
377 let codec = unsafe {
378 ff_sys::avcodec::find_decoder(codec_id).ok_or_else(|| {
379 DecodeError::UnsupportedCodec {
380 codec: format!("{codec_name} (codec_id={codec_id:?})"),
381 }
382 })?
383 };
384
385 let codec_ctx_guard = unsafe { AvCodecContextGuard::new(codec)? };
388 let codec_ctx = codec_ctx_guard.as_ptr();
389
390 unsafe {
393 let stream = (*format_ctx).streams.add(stream_index as usize);
394 let codecpar = (*(*stream)).codecpar;
395 ff_sys::avcodec::parameters_to_context(codec_ctx, codecpar).map_err(|e| {
396 DecodeError::Ffmpeg {
397 code: e,
398 message: format!(
399 "Failed to copy codec parameters: {}",
400 ff_sys::av_error_string(e)
401 ),
402 }
403 })?;
404 }
405
406 unsafe {
409 ff_sys::avcodec::open2(codec_ctx, codec, ptr::null_mut()).map_err(|e| {
410 DecodeError::Ffmpeg {
411 code: e,
412 message: format!("Failed to open codec: {}", ff_sys::av_error_string(e)),
413 }
414 })?;
415 }
416
417 let stream_info =
420 unsafe { Self::extract_stream_info(format_ctx, stream_index as i32, codec_ctx)? };
421
422 let container_info = unsafe { Self::extract_container_info(format_ctx) };
425
426 let packet_guard = unsafe { AvPacketGuard::new()? };
429 let frame_guard = unsafe { AvFrameGuard::new()? };
430
431 Ok((
433 Self {
434 format_ctx: format_ctx_guard.into_raw(),
435 codec_ctx: codec_ctx_guard.into_raw(),
436 stream_index: stream_index as i32,
437 swr_ctx: None,
438 output_format,
439 output_sample_rate,
440 output_channels,
441 is_live,
442 eof: false,
443 position: Duration::ZERO,
444 packet: packet_guard.into_raw(),
445 frame: frame_guard.into_raw(),
446 url,
447 network_opts: stored_network_opts,
448 reconnect_count: 0,
449 },
450 stream_info,
451 container_info,
452 ))
453 }
454
455 unsafe fn find_audio_stream(format_ctx: *mut AVFormatContext) -> Option<(usize, AVCodecID)> {
465 unsafe {
467 let nb_streams = (*format_ctx).nb_streams as usize;
468
469 for i in 0..nb_streams {
470 let stream = (*format_ctx).streams.add(i);
471 let codecpar = (*(*stream)).codecpar;
472
473 if (*codecpar).codec_type == AVMediaType_AVMEDIA_TYPE_AUDIO {
474 return Some((i, (*codecpar).codec_id));
475 }
476 }
477
478 None
479 }
480 }
481
482 unsafe fn extract_codec_name(codec_id: ff_sys::AVCodecID) -> String {
484 let name_ptr = unsafe { ff_sys::avcodec_get_name(codec_id) };
486 if name_ptr.is_null() {
487 return String::from("unknown");
488 }
489 unsafe { CStr::from_ptr(name_ptr).to_string_lossy().into_owned() }
491 }
492
493 unsafe fn extract_stream_info(
495 format_ctx: *mut AVFormatContext,
496 stream_index: i32,
497 codec_ctx: *mut AVCodecContext,
498 ) -> Result<AudioStreamInfo, DecodeError> {
499 let (sample_rate, channels, sample_fmt, duration_val, channel_layout, codec_id) = unsafe {
501 let stream = (*format_ctx).streams.add(stream_index as usize);
502 let codecpar = (*(*stream)).codecpar;
503
504 (
505 (*codecpar).sample_rate as u32,
506 (*codecpar).ch_layout.nb_channels as u32,
507 (*codec_ctx).sample_fmt,
508 (*format_ctx).duration,
509 (*codecpar).ch_layout,
510 (*codecpar).codec_id,
511 )
512 };
513
514 let duration = if duration_val > 0 {
516 let duration_secs = duration_val as f64 / 1_000_000.0;
517 Some(Duration::from_secs_f64(duration_secs))
518 } else {
519 None
520 };
521
522 let sample_format = Self::convert_sample_format(sample_fmt);
524
525 let channel_layout_enum = Self::convert_channel_layout(&channel_layout, channels);
527
528 let codec = Self::convert_codec(codec_id);
530 let codec_name = unsafe { Self::extract_codec_name(codec_id) };
531
532 let mut builder = AudioStreamInfo::builder()
534 .index(stream_index as u32)
535 .codec(codec)
536 .codec_name(codec_name)
537 .sample_rate(sample_rate)
538 .channels(channels)
539 .sample_format(sample_format)
540 .channel_layout(channel_layout_enum);
541
542 if let Some(d) = duration {
543 builder = builder.duration(d);
544 }
545
546 Ok(builder.build())
547 }
548
549 unsafe fn extract_container_info(format_ctx: *mut AVFormatContext) -> ContainerInfo {
555 unsafe {
557 let format_name = if (*format_ctx).iformat.is_null() {
558 String::new()
559 } else {
560 let ptr = (*(*format_ctx).iformat).name;
561 if ptr.is_null() {
562 String::new()
563 } else {
564 CStr::from_ptr(ptr).to_string_lossy().into_owned()
565 }
566 };
567
568 let bit_rate = {
569 let br = (*format_ctx).bit_rate;
570 if br > 0 { Some(br as u64) } else { None }
571 };
572
573 let nb_streams = (*format_ctx).nb_streams as u32;
574
575 let mut builder = ContainerInfo::builder()
576 .format_name(format_name)
577 .nb_streams(nb_streams);
578 if let Some(br) = bit_rate {
579 builder = builder.bit_rate(br);
580 }
581 builder.build()
582 }
583 }
584
585 fn convert_sample_format(fmt: AVSampleFormat) -> SampleFormat {
587 if fmt == ff_sys::AVSampleFormat_AV_SAMPLE_FMT_U8 {
588 SampleFormat::U8
589 } else if fmt == ff_sys::AVSampleFormat_AV_SAMPLE_FMT_S16 {
590 SampleFormat::I16
591 } else if fmt == ff_sys::AVSampleFormat_AV_SAMPLE_FMT_S32 {
592 SampleFormat::I32
593 } else if fmt == ff_sys::AVSampleFormat_AV_SAMPLE_FMT_FLT {
594 SampleFormat::F32
595 } else if fmt == ff_sys::AVSampleFormat_AV_SAMPLE_FMT_DBL {
596 SampleFormat::F64
597 } else if fmt == ff_sys::AVSampleFormat_AV_SAMPLE_FMT_U8P {
598 SampleFormat::U8p
599 } else if fmt == ff_sys::AVSampleFormat_AV_SAMPLE_FMT_S16P {
600 SampleFormat::I16p
601 } else if fmt == ff_sys::AVSampleFormat_AV_SAMPLE_FMT_S32P {
602 SampleFormat::I32p
603 } else if fmt == ff_sys::AVSampleFormat_AV_SAMPLE_FMT_FLTP {
604 SampleFormat::F32p
605 } else if fmt == ff_sys::AVSampleFormat_AV_SAMPLE_FMT_DBLP {
606 SampleFormat::F64p
607 } else {
608 log::warn!(
609 "sample_format unsupported, falling back to F32 requested={fmt} fallback=F32"
610 );
611 SampleFormat::F32
612 }
613 }
614
615 fn convert_channel_layout(layout: &ff_sys::AVChannelLayout, channels: u32) -> ChannelLayout {
617 if layout.order == ff_sys::AVChannelOrder_AV_CHANNEL_ORDER_NATIVE {
618 let mask = unsafe { layout.u.mask };
620 match mask {
621 0x4 => ChannelLayout::Mono,
622 0x3 => ChannelLayout::Stereo,
623 0x103 => ChannelLayout::Stereo2_1,
624 0x7 => ChannelLayout::Surround3_0,
625 0x33 => ChannelLayout::Quad,
626 0x37 => ChannelLayout::Surround5_0,
627 0x3F => ChannelLayout::Surround5_1,
628 0x13F => ChannelLayout::Surround6_1,
629 0x63F => ChannelLayout::Surround7_1,
630 _ => {
631 log::warn!(
632 "channel_layout mask has no mapping, deriving from channel count \
633 mask={mask} channels={channels}"
634 );
635 ChannelLayout::from_channels(channels)
636 }
637 }
638 } else {
639 log::warn!(
640 "channel_layout order is not NATIVE, deriving from channel count \
641 order={order} channels={channels}",
642 order = layout.order
643 );
644 ChannelLayout::from_channels(channels)
645 }
646 }
647
648 unsafe fn create_channel_layout(channels: u32) -> ff_sys::AVChannelLayout {
654 let mut layout = unsafe { std::mem::zeroed::<ff_sys::AVChannelLayout>() };
656 unsafe {
658 ff_sys::av_channel_layout_default(&raw mut layout, channels as i32);
659 }
660 layout
661 }
662
663 fn convert_codec(codec_id: AVCodecID) -> AudioCodec {
665 if codec_id == ff_sys::AVCodecID_AV_CODEC_ID_AAC {
666 AudioCodec::Aac
667 } else if codec_id == ff_sys::AVCodecID_AV_CODEC_ID_MP3 {
668 AudioCodec::Mp3
669 } else if codec_id == ff_sys::AVCodecID_AV_CODEC_ID_OPUS {
670 AudioCodec::Opus
671 } else if codec_id == ff_sys::AVCodecID_AV_CODEC_ID_VORBIS {
672 AudioCodec::Vorbis
673 } else if codec_id == ff_sys::AVCodecID_AV_CODEC_ID_FLAC {
674 AudioCodec::Flac
675 } else if codec_id == ff_sys::AVCodecID_AV_CODEC_ID_PCM_S16LE {
676 AudioCodec::Pcm
677 } else {
678 log::warn!(
679 "audio codec unsupported, falling back to Aac codec_id={codec_id} fallback=Aac"
680 );
681 AudioCodec::Aac
682 }
683 }
684
685 pub(crate) fn decode_one(&mut self) -> Result<Option<AudioFrame>, DecodeError> {
696 loop {
697 match self.decode_one_inner() {
698 Ok(frame) => return Ok(frame),
699 Err(DecodeError::StreamInterrupted { .. })
700 if self.url.is_some() && self.network_opts.reconnect_on_error =>
701 {
702 self.attempt_reconnect()?;
703 }
704 Err(e) => return Err(e),
705 }
706 }
707 }
708
709 fn decode_one_inner(&mut self) -> Result<Option<AudioFrame>, DecodeError> {
710 if self.eof {
711 return Ok(None);
712 }
713
714 unsafe {
715 loop {
716 let ret = ff_sys::avcodec_receive_frame(self.codec_ctx, self.frame);
718
719 if ret == 0 {
720 let audio_frame = self.convert_frame_to_audio_frame()?;
722
723 let pts = (*self.frame).pts;
725 if pts != ff_sys::AV_NOPTS_VALUE {
726 let stream = (*self.format_ctx).streams.add(self.stream_index as usize);
727 let time_base = (*(*stream)).time_base;
728 let timestamp_secs =
729 pts as f64 * time_base.num as f64 / time_base.den as f64;
730 self.position = Duration::from_secs_f64(timestamp_secs);
731 }
732
733 return Ok(Some(audio_frame));
734 } else if ret == ff_sys::error_codes::EAGAIN {
735 let read_ret = ff_sys::av_read_frame(self.format_ctx, self.packet);
738
739 if read_ret == ff_sys::error_codes::EOF {
740 ff_sys::avcodec_send_packet(self.codec_ctx, ptr::null());
742 self.eof = true;
743 continue;
744 } else if read_ret < 0 {
745 return Err(if let Some(url) = &self.url {
746 crate::network::map_network_error(
748 read_ret,
749 crate::network::sanitize_url(url),
750 )
751 } else {
752 DecodeError::Ffmpeg {
753 code: read_ret,
754 message: format!(
755 "Failed to read frame: {}",
756 ff_sys::av_error_string(read_ret)
757 ),
758 }
759 });
760 }
761
762 if (*self.packet).stream_index == self.stream_index {
764 let send_ret = ff_sys::avcodec_send_packet(self.codec_ctx, self.packet);
766 ff_sys::av_packet_unref(self.packet);
767
768 if send_ret < 0 && send_ret != ff_sys::error_codes::EAGAIN {
769 return Err(DecodeError::Ffmpeg {
770 code: send_ret,
771 message: format!(
772 "Failed to send packet: {}",
773 ff_sys::av_error_string(send_ret)
774 ),
775 });
776 }
777 } else {
778 ff_sys::av_packet_unref(self.packet);
780 }
781 } else if ret == ff_sys::error_codes::EOF {
782 self.eof = true;
784 return Ok(None);
785 } else {
786 return Err(DecodeError::DecodingFailed {
787 timestamp: Some(self.position),
788 reason: ff_sys::av_error_string(ret),
789 });
790 }
791 }
792 }
793 }
794
795 unsafe fn convert_frame_to_audio_frame(&mut self) -> Result<AudioFrame, DecodeError> {
797 unsafe {
799 let nb_samples = (*self.frame).nb_samples as usize;
800 let channels = (*self.frame).ch_layout.nb_channels as u32;
801 let sample_rate = (*self.frame).sample_rate as u32;
802 let src_format = (*self.frame).format;
803
804 let needs_conversion = self.output_format.is_some()
806 || self.output_sample_rate.is_some()
807 || self.output_channels.is_some();
808
809 if needs_conversion {
810 self.convert_with_swr(nb_samples, channels, sample_rate, src_format)
811 } else {
812 self.av_frame_to_audio_frame(self.frame)
813 }
814 }
815 }
816
817 unsafe fn convert_with_swr(
819 &mut self,
820 nb_samples: usize,
821 src_channels: u32,
822 src_sample_rate: u32,
823 src_format: i32,
824 ) -> Result<AudioFrame, DecodeError> {
825 let dst_format = self
827 .output_format
828 .map_or(src_format, Self::sample_format_to_av);
829 let dst_sample_rate = self.output_sample_rate.unwrap_or(src_sample_rate);
830 let dst_channels = self.output_channels.unwrap_or(src_channels);
831
832 if src_format == dst_format
834 && src_sample_rate == dst_sample_rate
835 && src_channels == dst_channels
836 {
837 return unsafe { self.av_frame_to_audio_frame(self.frame) };
838 }
839
840 let mut src_ch_layout = unsafe { Self::create_channel_layout(src_channels) };
843 let mut dst_ch_layout = unsafe { Self::create_channel_layout(dst_channels) };
844
845 let mut swr_ctx: *mut SwrContext = ptr::null_mut();
847
848 let ret = unsafe {
850 ff_sys::swr_alloc_set_opts2(
851 &raw mut swr_ctx,
852 &raw const dst_ch_layout,
853 dst_format,
854 dst_sample_rate as i32,
855 &raw const src_ch_layout,
856 src_format,
857 src_sample_rate as i32,
858 0,
859 ptr::null_mut(),
860 )
861 };
862
863 if ret < 0 {
864 unsafe {
866 ff_sys::av_channel_layout_uninit(&raw mut src_ch_layout);
867 ff_sys::av_channel_layout_uninit(&raw mut dst_ch_layout);
868 }
869 return Err(DecodeError::Ffmpeg {
870 code: ret,
871 message: format!(
872 "Failed to allocate SwrContext: {}",
873 ff_sys::av_error_string(ret)
874 ),
875 });
876 }
877
878 let _swr_guard = SwrContextGuard(swr_ctx);
880
881 let ret = unsafe { ff_sys::swr_init(swr_ctx) };
884 if ret < 0 {
885 unsafe {
887 ff_sys::av_channel_layout_uninit(&raw mut src_ch_layout);
888 ff_sys::av_channel_layout_uninit(&raw mut dst_ch_layout);
889 }
890 return Err(DecodeError::Ffmpeg {
891 code: ret,
892 message: format!(
893 "Failed to initialize SwrContext: {}",
894 ff_sys::av_error_string(ret)
895 ),
896 });
897 }
898
899 let out_samples = unsafe { ff_sys::swr_get_out_samples(swr_ctx, nb_samples as i32) };
902
903 if out_samples < 0 {
904 unsafe {
906 ff_sys::av_channel_layout_uninit(&raw mut src_ch_layout);
907 ff_sys::av_channel_layout_uninit(&raw mut dst_ch_layout);
908 }
909 return Err(DecodeError::Ffmpeg {
910 code: 0,
911 message: "Failed to calculate output sample count".to_string(),
912 });
913 }
914
915 let out_samples = out_samples as usize;
916
917 let dst_sample_fmt = Self::convert_sample_format(dst_format);
919 let bytes_per_sample = dst_sample_fmt.bytes_per_sample();
920 let is_planar = dst_sample_fmt.is_planar();
921
922 let buffer_size = if is_planar {
924 out_samples * bytes_per_sample * dst_channels as usize
926 } else {
927 out_samples * bytes_per_sample * dst_channels as usize
929 };
930
931 let mut out_buffer = vec![0u8; buffer_size];
932
933 let mut out_ptrs = if is_planar {
935 let plane_size = out_samples * bytes_per_sample;
937 (0..dst_channels)
938 .map(|i| {
939 let offset = i as usize * plane_size;
940 out_buffer[offset..].as_mut_ptr()
941 })
942 .collect::<Vec<_>>()
943 } else {
944 vec![out_buffer.as_mut_ptr()]
946 };
947
948 let in_ptrs = unsafe { (*self.frame).data };
951
952 let converted_samples = unsafe {
955 ff_sys::swr_convert(
956 swr_ctx,
957 out_ptrs.as_mut_ptr(),
958 out_samples as i32,
959 in_ptrs.as_ptr() as *mut *const u8,
960 nb_samples as i32,
961 )
962 };
963
964 unsafe {
966 ff_sys::av_channel_layout_uninit(&raw mut src_ch_layout);
967 ff_sys::av_channel_layout_uninit(&raw mut dst_ch_layout);
968 }
969
970 if converted_samples < 0 {
971 return Err(DecodeError::Ffmpeg {
972 code: converted_samples,
973 message: format!(
974 "Failed to convert samples: {}",
975 ff_sys::av_error_string(converted_samples)
976 ),
977 });
978 }
979
980 let timestamp = unsafe {
983 let pts = (*self.frame).pts;
984 if pts != ff_sys::AV_NOPTS_VALUE {
985 let stream = (*self.format_ctx).streams.add(self.stream_index as usize);
986 let time_base = (*(*stream)).time_base;
987 Timestamp::new(pts, Rational::new(time_base.num, time_base.den))
988 } else {
989 Timestamp::invalid()
990 }
991 };
992
993 let planes = if is_planar {
995 let plane_size = converted_samples as usize * bytes_per_sample;
996 (0..dst_channels)
997 .map(|i| {
998 let offset = i as usize * plane_size;
999 out_buffer[offset..offset + plane_size].to_vec()
1000 })
1001 .collect()
1002 } else {
1003 vec![
1005 out_buffer[..converted_samples as usize * bytes_per_sample * dst_channels as usize]
1006 .to_vec(),
1007 ]
1008 };
1009
1010 AudioFrame::new(
1011 planes,
1012 converted_samples as usize,
1013 dst_channels,
1014 dst_sample_rate,
1015 dst_sample_fmt,
1016 timestamp,
1017 )
1018 .map_err(|e| DecodeError::Ffmpeg {
1019 code: 0,
1020 message: format!("Failed to create AudioFrame: {e}"),
1021 })
1022 }
1023
1024 unsafe fn av_frame_to_audio_frame(
1026 &self,
1027 frame: *const AVFrame,
1028 ) -> Result<AudioFrame, DecodeError> {
1029 unsafe {
1031 let nb_samples = (*frame).nb_samples as usize;
1032 let channels = (*frame).ch_layout.nb_channels as u32;
1033 let sample_rate = (*frame).sample_rate as u32;
1034 let format = Self::convert_sample_format((*frame).format);
1035
1036 let pts = (*frame).pts;
1038 let timestamp = if pts != ff_sys::AV_NOPTS_VALUE {
1039 let stream = (*self.format_ctx).streams.add(self.stream_index as usize);
1040 let time_base = (*(*stream)).time_base;
1041 Timestamp::new(
1042 pts as i64,
1043 Rational::new(time_base.num as i32, time_base.den as i32),
1044 )
1045 } else {
1046 Timestamp::invalid()
1047 };
1048
1049 let planes = Self::extract_planes(frame, nb_samples, channels, format)?;
1051
1052 AudioFrame::new(planes, nb_samples, channels, sample_rate, format, timestamp).map_err(
1053 |e| DecodeError::Ffmpeg {
1054 code: 0,
1055 message: format!("Failed to create AudioFrame: {e}"),
1056 },
1057 )
1058 }
1059 }
1060
1061 unsafe fn extract_planes(
1063 frame: *const AVFrame,
1064 nb_samples: usize,
1065 channels: u32,
1066 format: SampleFormat,
1067 ) -> Result<Vec<Vec<u8>>, DecodeError> {
1068 unsafe {
1070 let mut planes = Vec::new();
1071 let bytes_per_sample = format.bytes_per_sample();
1072
1073 if format.is_planar() {
1074 for ch in 0..channels as usize {
1076 let plane_size = nb_samples * bytes_per_sample;
1077 let mut plane_data = vec![0u8; plane_size];
1078
1079 let src_ptr = (*frame).data[ch];
1080 std::ptr::copy_nonoverlapping(src_ptr, plane_data.as_mut_ptr(), plane_size);
1081
1082 planes.push(plane_data);
1083 }
1084 } else {
1085 let plane_size = nb_samples * channels as usize * bytes_per_sample;
1087 let mut plane_data = vec![0u8; plane_size];
1088
1089 let src_ptr = (*frame).data[0];
1090 std::ptr::copy_nonoverlapping(src_ptr, plane_data.as_mut_ptr(), plane_size);
1091
1092 planes.push(plane_data);
1093 }
1094
1095 Ok(planes)
1096 }
1097 }
1098
1099 fn sample_format_to_av(format: SampleFormat) -> AVSampleFormat {
1101 match format {
1102 SampleFormat::U8 => ff_sys::AVSampleFormat_AV_SAMPLE_FMT_U8,
1103 SampleFormat::I16 => ff_sys::AVSampleFormat_AV_SAMPLE_FMT_S16,
1104 SampleFormat::I32 => ff_sys::AVSampleFormat_AV_SAMPLE_FMT_S32,
1105 SampleFormat::F32 => ff_sys::AVSampleFormat_AV_SAMPLE_FMT_FLT,
1106 SampleFormat::F64 => ff_sys::AVSampleFormat_AV_SAMPLE_FMT_DBL,
1107 SampleFormat::U8p => ff_sys::AVSampleFormat_AV_SAMPLE_FMT_U8P,
1108 SampleFormat::I16p => ff_sys::AVSampleFormat_AV_SAMPLE_FMT_S16P,
1109 SampleFormat::I32p => ff_sys::AVSampleFormat_AV_SAMPLE_FMT_S32P,
1110 SampleFormat::F32p => ff_sys::AVSampleFormat_AV_SAMPLE_FMT_FLTP,
1111 SampleFormat::F64p => ff_sys::AVSampleFormat_AV_SAMPLE_FMT_DBLP,
1112 _ => {
1113 log::warn!(
1114 "sample_format has no AV mapping, falling back to F32 format={format:?} fallback=AV_SAMPLE_FMT_FLT"
1115 );
1116 ff_sys::AVSampleFormat_AV_SAMPLE_FMT_FLT
1117 }
1118 }
1119 }
1120
1121 pub(crate) fn position(&self) -> Duration {
1123 self.position
1124 }
1125
1126 pub(crate) fn is_eof(&self) -> bool {
1128 self.eof
1129 }
1130
1131 pub(crate) fn is_live(&self) -> bool {
1136 self.is_live
1137 }
1138
1139 fn duration_to_pts(&self, duration: Duration) -> i64 {
1141 let time_base = unsafe {
1143 let stream = (*self.format_ctx).streams.add(self.stream_index as usize);
1144 (*(*stream)).time_base
1145 };
1146
1147 let time_base_f64 = time_base.den as f64 / time_base.num as f64;
1149 (duration.as_secs_f64() * time_base_f64) as i64
1150 }
1151
1152 pub(crate) fn seek(
1163 &mut self,
1164 position: Duration,
1165 mode: crate::SeekMode,
1166 ) -> Result<(), DecodeError> {
1167 use crate::SeekMode;
1168
1169 let timestamp = self.duration_to_pts(position);
1170 let flags = ff_sys::avformat::seek_flags::BACKWARD;
1171
1172 unsafe {
1175 ff_sys::av_packet_unref(self.packet);
1176 ff_sys::av_frame_unref(self.frame);
1177 }
1178
1179 unsafe {
1182 ff_sys::avformat::seek_frame(self.format_ctx, self.stream_index, timestamp, flags)
1183 .map_err(|e| DecodeError::SeekFailed {
1184 target: position,
1185 reason: ff_sys::av_error_string(e),
1186 })?;
1187 }
1188
1189 unsafe {
1192 ff_sys::avcodec::flush_buffers(self.codec_ctx);
1193 }
1194
1195 unsafe {
1198 loop {
1199 let ret = ff_sys::avcodec_receive_frame(self.codec_ctx, self.frame);
1200 if ret == ff_sys::error_codes::EAGAIN || ret == ff_sys::error_codes::EOF {
1201 break;
1202 } else if ret == 0 {
1203 ff_sys::av_frame_unref(self.frame);
1204 } else {
1205 break;
1206 }
1207 }
1208 }
1209
1210 self.eof = false;
1212
1213 if mode == SeekMode::Exact {
1215 self.skip_to_exact(position)?;
1216 }
1217 Ok(())
1220 }
1221
1222 fn skip_to_exact(&mut self, target: Duration) -> Result<(), DecodeError> {
1230 while let Some(frame) = self.decode_one()? {
1232 let frame_time = frame.timestamp().as_duration();
1233 if frame_time >= target {
1234 break;
1236 }
1237 }
1239 Ok(())
1240 }
1241
1242 pub(crate) fn flush(&mut self) {
1244 unsafe {
1246 ff_sys::avcodec::flush_buffers(self.codec_ctx);
1247 }
1248 self.eof = false;
1249 }
1250
1251 fn attempt_reconnect(&mut self) -> Result<(), DecodeError> {
1259 let url = match self.url.as_deref() {
1260 Some(u) => u.to_owned(),
1261 None => return Ok(()), };
1263 let max = self.network_opts.max_reconnect_attempts;
1264
1265 for attempt in 1..=max {
1266 let backoff_ms = 100u64 * (1u64 << (attempt - 1).min(10));
1267 log::warn!(
1268 "reconnecting attempt={attempt} url={} backoff_ms={backoff_ms}",
1269 crate::network::sanitize_url(&url)
1270 );
1271 std::thread::sleep(Duration::from_millis(backoff_ms));
1272 match self.reopen(&url) {
1273 Ok(()) => {
1274 self.reconnect_count += 1;
1275 log::info!(
1276 "reconnected attempt={attempt} url={} total_reconnects={}",
1277 crate::network::sanitize_url(&url),
1278 self.reconnect_count
1279 );
1280 return Ok(());
1281 }
1282 Err(e) => log::warn!("reconnect attempt={attempt} failed err={e}"),
1283 }
1284 }
1285
1286 Err(DecodeError::StreamInterrupted {
1287 code: 0,
1288 endpoint: crate::network::sanitize_url(&url),
1289 message: format!("stream did not recover after {max} attempts"),
1290 })
1291 }
1292
1293 fn reopen(&mut self, url: &str) -> Result<(), DecodeError> {
1296 unsafe {
1300 ff_sys::avformat::close_input(std::ptr::addr_of_mut!(self.format_ctx));
1301 }
1302
1303 self.format_ctx = unsafe {
1306 ff_sys::avformat::open_input_url(
1307 url,
1308 self.network_opts.connect_timeout,
1309 self.network_opts.read_timeout,
1310 )
1311 .map_err(|e| crate::network::map_network_error(e, crate::network::sanitize_url(url)))?
1312 };
1313
1314 unsafe {
1317 ff_sys::avformat::find_stream_info(self.format_ctx).map_err(|e| {
1318 DecodeError::Ffmpeg {
1319 code: e,
1320 message: format!(
1321 "reconnect find_stream_info failed: {}",
1322 ff_sys::av_error_string(e)
1323 ),
1324 }
1325 })?;
1326 }
1327
1328 let (stream_index, _) = unsafe { Self::find_audio_stream(self.format_ctx) }
1331 .ok_or_else(|| DecodeError::NoAudioStream { path: url.into() })?;
1332 self.stream_index = stream_index as i32;
1333
1334 unsafe {
1337 ff_sys::avcodec::flush_buffers(self.codec_ctx);
1338 }
1339
1340 self.eof = false;
1341 Ok(())
1342 }
1343}
1344
1345impl Drop for AudioDecoderInner {
1346 fn drop(&mut self) {
1347 if let Some(swr_ctx) = self.swr_ctx {
1349 unsafe {
1351 ff_sys::swr_free(&mut (swr_ctx as *mut _));
1353 }
1354 }
1355
1356 if !self.frame.is_null() {
1358 unsafe {
1360 ff_sys::av_frame_free(&mut (self.frame as *mut _));
1361 }
1362 }
1363
1364 if !self.packet.is_null() {
1365 unsafe {
1367 ff_sys::av_packet_free(&mut (self.packet as *mut _));
1368 }
1369 }
1370
1371 if !self.codec_ctx.is_null() {
1373 unsafe {
1375 ff_sys::avcodec::free_context(&mut (self.codec_ctx as *mut _));
1376 }
1377 }
1378
1379 if !self.format_ctx.is_null() {
1381 unsafe {
1383 ff_sys::avformat::close_input(&mut (self.format_ctx as *mut _));
1384 }
1385 }
1386 }
1387}
1388
1389unsafe impl Send for AudioDecoderInner {}
1392
1393#[cfg(test)]
1394#[allow(unsafe_code)]
1395mod tests {
1396 use ff_format::channel::ChannelLayout;
1397
1398 use super::AudioDecoderInner;
1399
1400 fn native_layout(mask: u64, nb_channels: i32) -> ff_sys::AVChannelLayout {
1402 ff_sys::AVChannelLayout {
1403 order: ff_sys::AVChannelOrder_AV_CHANNEL_ORDER_NATIVE,
1404 nb_channels,
1405 u: ff_sys::AVChannelLayout__bindgen_ty_1 { mask },
1406 opaque: std::ptr::null_mut(),
1407 }
1408 }
1409
1410 fn unspec_layout(nb_channels: i32) -> ff_sys::AVChannelLayout {
1412 ff_sys::AVChannelLayout {
1413 order: ff_sys::AVChannelOrder_AV_CHANNEL_ORDER_UNSPEC,
1414 nb_channels,
1415 u: ff_sys::AVChannelLayout__bindgen_ty_1 { mask: 0 },
1416 opaque: std::ptr::null_mut(),
1417 }
1418 }
1419
1420 #[test]
1421 fn native_mask_mono() {
1422 let layout = native_layout(0x4, 1);
1423 assert_eq!(
1424 AudioDecoderInner::convert_channel_layout(&layout, 1),
1425 ChannelLayout::Mono
1426 );
1427 }
1428
1429 #[test]
1430 fn native_mask_stereo() {
1431 let layout = native_layout(0x3, 2);
1432 assert_eq!(
1433 AudioDecoderInner::convert_channel_layout(&layout, 2),
1434 ChannelLayout::Stereo
1435 );
1436 }
1437
1438 #[test]
1439 fn native_mask_stereo2_1() {
1440 let layout = native_layout(0x103, 3);
1441 assert_eq!(
1442 AudioDecoderInner::convert_channel_layout(&layout, 3),
1443 ChannelLayout::Stereo2_1
1444 );
1445 }
1446
1447 #[test]
1448 fn native_mask_surround3_0() {
1449 let layout = native_layout(0x7, 3);
1450 assert_eq!(
1451 AudioDecoderInner::convert_channel_layout(&layout, 3),
1452 ChannelLayout::Surround3_0
1453 );
1454 }
1455
1456 #[test]
1457 fn native_mask_quad() {
1458 let layout = native_layout(0x33, 4);
1459 assert_eq!(
1460 AudioDecoderInner::convert_channel_layout(&layout, 4),
1461 ChannelLayout::Quad
1462 );
1463 }
1464
1465 #[test]
1466 fn native_mask_surround5_0() {
1467 let layout = native_layout(0x37, 5);
1468 assert_eq!(
1469 AudioDecoderInner::convert_channel_layout(&layout, 5),
1470 ChannelLayout::Surround5_0
1471 );
1472 }
1473
1474 #[test]
1475 fn native_mask_surround5_1() {
1476 let layout = native_layout(0x3F, 6);
1477 assert_eq!(
1478 AudioDecoderInner::convert_channel_layout(&layout, 6),
1479 ChannelLayout::Surround5_1
1480 );
1481 }
1482
1483 #[test]
1484 fn native_mask_surround6_1() {
1485 let layout = native_layout(0x13F, 7);
1486 assert_eq!(
1487 AudioDecoderInner::convert_channel_layout(&layout, 7),
1488 ChannelLayout::Surround6_1
1489 );
1490 }
1491
1492 #[test]
1493 fn native_mask_surround7_1() {
1494 let layout = native_layout(0x63F, 8);
1495 assert_eq!(
1496 AudioDecoderInner::convert_channel_layout(&layout, 8),
1497 ChannelLayout::Surround7_1
1498 );
1499 }
1500
1501 #[test]
1502 fn native_mask_unknown_falls_back_to_from_channels() {
1503 let layout = native_layout(0x1, 2);
1505 assert_eq!(
1506 AudioDecoderInner::convert_channel_layout(&layout, 2),
1507 ChannelLayout::from_channels(2)
1508 );
1509 }
1510
1511 #[test]
1512 fn non_native_order_falls_back_to_from_channels() {
1513 let layout = unspec_layout(6);
1514 assert_eq!(
1515 AudioDecoderInner::convert_channel_layout(&layout, 6),
1516 ChannelLayout::from_channels(6)
1517 );
1518 }
1519
1520 #[test]
1525 fn codec_name_should_return_h264_for_h264_codec_id() {
1526 let name =
1527 unsafe { AudioDecoderInner::extract_codec_name(ff_sys::AVCodecID_AV_CODEC_ID_H264) };
1528 assert_eq!(name, "h264");
1529 }
1530
1531 #[test]
1532 fn codec_name_should_return_none_for_none_codec_id() {
1533 let name =
1534 unsafe { AudioDecoderInner::extract_codec_name(ff_sys::AVCodecID_AV_CODEC_ID_NONE) };
1535 assert_eq!(name, "none");
1536 }
1537
1538 #[test]
1539 fn unsupported_codec_error_should_include_codec_name() {
1540 let codec_id = ff_sys::AVCodecID_AV_CODEC_ID_MP3;
1541 let codec_name = unsafe { AudioDecoderInner::extract_codec_name(codec_id) };
1542 let error = crate::error::DecodeError::UnsupportedCodec {
1543 codec: format!("{codec_name} (codec_id={codec_id:?})"),
1544 };
1545 let msg = error.to_string();
1546 assert!(msg.contains("mp3"), "expected codec name in error: {msg}");
1547 assert!(
1548 msg.contains("codec_id="),
1549 "expected codec_id in error: {msg}"
1550 );
1551 }
1552}