Skip to main content

mediadecode_ffmpeg/
audio.rs

1//! `mediadecode::AudioStreamDecoder` impl backed by
2//! `ffmpeg::decoder::Audio`.
3//!
4//! Mirrors the shape of [`crate::FfmpegVideoStreamDecoder`] without
5//! the HW-fallback wrinkle — audio decoders never go through a
6//! hardware backend in the FFmpeg world, so there's no probe, no
7//! state machine, just `send_packet` / `receive_frame` over the
8//! software decoder.
9//!
10//! Frames produced via [`crate::convert::av_frame_to_audio_frame`]
11//! carry zero-copy `FfmpegBuffer` plane views into the source
12//! `AVFrame`'s refcounted buffers; the consumer can hold the frame
13//! across decoder calls without copying.
14
15use ffmpeg_next::{codec::Parameters, frame};
16use mediadecode::{Timebase, decoder::AudioStreamDecoder, frame::AudioFrame, packet::AudioPacket};
17use mediaframe::audio::ChannelLayoutDescription;
18
19use crate::{
20  Error, Ffmpeg, FfmpegBuffer, boundary,
21  convert::{self, ConvertError},
22  decoder::build_codec_context,
23  extras::{AudioFrameExtra, AudioPacketExtra},
24  frame::alloc_av_audio_frame,
25  sample_format::SampleFormat,
26};
27
28/// `mediadecode::AudioStreamDecoder` impl wrapping `ffmpeg::decoder::Audio`.
29pub struct FfmpegAudioStreamDecoder {
30  decoder: ffmpeg_next::decoder::Audio,
31  scratch: frame::Audio,
32  time_base: Timebase,
33}
34
35impl FfmpegAudioStreamDecoder {
36  /// Opens an audio decoder for the given codec parameters.
37  pub fn open(parameters: Parameters, time_base: Timebase) -> Result<Self, AudioDecodeError> {
38    // Use the checked codec-context builder — `Context::from_parameters`
39    // is OOM-UB-prone (see `crate::decoder::build_codec_context`).
40    let ctx = build_codec_context(&parameters).map_err(AudioDecodeError::Decode)?;
41    let decoder = ctx
42      .decoder()
43      .audio()
44      .map_err(|e| AudioDecodeError::Decode(Error::Ffmpeg(e)))?;
45    let scratch = alloc_av_audio_frame().map_err(AudioDecodeError::Decode)?;
46    Ok(Self {
47      decoder,
48      scratch,
49      time_base,
50    })
51  }
52
53  /// Returns the time base associated with the source stream.
54  #[cfg_attr(not(tarpaulin), inline(always))]
55  pub const fn time_base(&self) -> Timebase {
56    self.time_base
57  }
58
59  /// Borrow the wrapped `ffmpeg::decoder::Audio` (e.g. to query
60  /// `channels()` / `rate()` / `format()`).
61  #[cfg_attr(not(tarpaulin), inline(always))]
62  pub const fn inner(&self) -> &ffmpeg_next::decoder::Audio {
63    &self.decoder
64  }
65}
66
67impl AudioStreamDecoder for FfmpegAudioStreamDecoder {
68  type Adapter = Ffmpeg;
69  type Buffer = FfmpegBuffer;
70  type Error = AudioDecodeError;
71
72  fn send_packet(
73    &mut self,
74    packet: &AudioPacket<AudioPacketExtra, Self::Buffer>,
75  ) -> Result<(), Self::Error> {
76    let av_pkt = boundary::ffmpeg_packet_from_audio_packet(packet)
77      .map_err(|e| AudioDecodeError::Decode(Error::Ffmpeg(e)))?;
78    self
79      .decoder
80      .send_packet(&av_pkt)
81      .map_err(|e| AudioDecodeError::Decode(Error::Ffmpeg(e)))
82  }
83
84  fn receive_frame(
85    &mut self,
86    dst: &mut AudioFrame<SampleFormat, ChannelLayoutDescription, AudioFrameExtra, Self::Buffer>,
87  ) -> Result<(), Self::Error> {
88    self
89      .decoder
90      .receive_frame(&mut self.scratch)
91      .map_err(|e| AudioDecodeError::Decode(Error::Ffmpeg(e)))?;
92    // SAFETY: scratch was just filled by receive_frame; convert
93    // refcounts each plane buffer it pulls into the produced
94    // AudioFrame so the scratch can be reused on the next call.
95    let new_frame =
96      unsafe { convert::av_frame_to_audio_frame(self.scratch.as_ptr(), self.time_base) }
97        .map_err(AudioDecodeError::Convert)?;
98    *dst = new_frame;
99    Ok(())
100  }
101
102  fn send_eof(&mut self) -> Result<(), Self::Error> {
103    self
104      .decoder
105      .send_eof()
106      .map_err(|e| AudioDecodeError::Decode(Error::Ffmpeg(e)))
107  }
108
109  fn flush(&mut self) -> Result<(), Self::Error> {
110    self.decoder.flush();
111    Ok(())
112  }
113}
114
115/// Errors from [`FfmpegAudioStreamDecoder`].
116#[derive(thiserror::Error, Debug, Clone)]
117pub enum AudioDecodeError {
118  /// The wrapped `ffmpeg::decoder::Audio` reported an error.
119  #[error(transparent)]
120  Decode(#[from] Error),
121  /// Conversion from FFmpeg's `AVFrame` to mediadecode's `AudioFrame`
122  /// failed.
123  #[error(transparent)]
124  Convert(#[from] ConvertError),
125}