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 `FfmpegBytes` planes copied out of the source `AVFrame` — the
12//! [D-seat amputation contract][law]. The consumer can hold the frame
13//! across decoder calls, send it to another thread, and outlive the
14//! decoder that made it.
15//!
16//! [law]: mediadecode::adapter#the-d-seat-amputation-contract
17
18use derive_more::{IsVariant, TryUnwrap, Unwrap};
19use ffmpeg_next::{codec::Parameters, frame};
20use mediadecode::{
21  Received, Sent, Timebase, decoder::AudioStreamDecoder, frame::AudioFrame, packet::AudioPacket,
22};
23use mediaframe::audio::ChannelLayoutDescription;
24
25use crate::{
26  DecoderLimits, Error, Ffmpeg, boundary,
27  convert::{self, ConvertError},
28  decoder::build_codec_context,
29  extras::{AudioFrameExtra, AudioPacketExtra},
30  frame::alloc_av_audio_frame,
31  sample_format::SampleFormat,
32};
33
34/// `mediadecode::AudioStreamDecoder` impl wrapping `ffmpeg::decoder::Audio`.
35pub struct CarrierAudioStreamDecoder<C: crate::FfmpegCarrier> {
36  decoder: ffmpeg_next::decoder::Audio,
37  scratch: frame::Audio,
38  time_base: Timebase,
39  limits: DecoderLimits,
40  /// Keeps the [`CallbackState`](crate::ffi::CallbackState) alive for as
41  /// long as the codec context that points at it.
42  ///
43  /// Declared **after** the decoder on purpose: struct fields drop in
44  /// declaration order, so the `AVCodecContext` is freed first and the
45  /// state it references outlives it.
46  _callback_state: Box<crate::ffi::CallbackState>,
47  /// The lane this decoder captures into. A marker: the carrier
48  /// appears in the frames it produces, not in its own state.
49  /// `true` when [`Self::scratch`] holds a decoded frame whose
50  /// conversion has **not committed**.
51  ///
52  /// `receive_frame` advances libavcodec: the frame it fills the
53  /// scratch with is out of the codec's queue and nothing re-offers it.
54  /// A conversion that then failed on an *allocation* used to leave
55  /// that frame in a scratch the next call overwrites — a decoded frame
56  /// lost to memory pressure, silently. So the receive and the
57  /// conversion are one transaction: while this is set, the next call
58  /// converts the scratch it already has instead of asking libavcodec
59  /// for another.
60  ///
61  /// The same seat the demux session keeps for a packet, and the same
62  /// discipline `flush` clears.
63  scratch_pending: bool,
64  /// `true` once [`Self::send_eof_impl`] has been accepted, until
65  /// [`Self::flush_impl`].
66  ///
67  /// **This road kept no end of its own before, and that was the gap.**
68  /// It leans on libavcodec to refuse a post-EOF submission — which
69  /// libavcodec does — but leaning is not knowing: asked "where is this
70  /// session", it could only answer `Streaming`, which is false the
71  /// moment a caller signals the end. A phase that can be wrong is the
72  /// thing [`SessionPhase`](crate::decoder::SessionPhase) exists to
73  /// abolish, so the latch is here to make the answer true rather than
74  /// to add a gate. The substrate is still the one that refuses.
75  eof: bool,
76  _carrier: core::marker::PhantomData<C>,
77}
78
79impl<C: crate::FfmpegCarrier + crate::CarrierOps> CarrierAudioStreamDecoder<C> {
80  /// Opens an audio decoder for the given codec parameters.
81  ///
82  /// `limits` bounds what one decoded frame may cost and is taken here
83  /// rather than through a builder: half of it is written straight into
84  /// the `AVCodecContext` this call opens, and a context's ceiling
85  /// cannot be moved after `avcodec_open2`. See [`DecoderLimits`].
86  pub(crate) fn open_impl(
87    parameters: Parameters,
88    time_base: Timebase,
89    limits: DecoderLimits,
90  ) -> Result<Self, AudioDecodeError> {
91    // Use the checked codec-context builder — `Context::from_parameters`
92    // is OOM-UB-prone (see `crate::decoder::build_codec_context`).
93    let (ctx, callback_state) = build_codec_context(&parameters, limits, Some(time_base))
94      .map_err(AudioDecodeError::Decode)?;
95    // Opened without forming a bindgen enum from FFmpeg memory: the codec
96    // is resolved off a raw `codec_id`, and the medium is proved off a raw
97    // `codec_type`. See `crate::decoder::ensure_codec_type`.
98    let codec = crate::decoder::find_decoder(&parameters).map_err(AudioDecodeError::Decode)?;
99    let opened = ctx
100      .decoder()
101      .open_as(codec)
102      .map_err(|e| AudioDecodeError::Decode(Error::Ffmpeg(e)))?;
103    crate::decoder::ensure_codec_type(&opened, ffmpeg_next::ffi::AVMediaType::AVMEDIA_TYPE_AUDIO)
104      .map_err(AudioDecodeError::Decode)?;
105    let decoder = ffmpeg_next::decoder::Audio(opened);
106    let scratch = alloc_av_audio_frame().map_err(AudioDecodeError::Decode)?;
107    Ok(Self {
108      decoder,
109      scratch,
110      time_base,
111      limits,
112      _callback_state: callback_state,
113      scratch_pending: false,
114      eof: false,
115      _carrier: core::marker::PhantomData,
116    })
117  }
118
119  /// Returns the time base associated with the source stream.
120  #[cfg_attr(not(tarpaulin), inline(always))]
121  pub(crate) const fn time_base_impl(&self) -> Timebase {
122    self.time_base
123  }
124
125  /// The frame ceilings this decoder was opened with.
126  #[cfg_attr(not(tarpaulin), inline(always))]
127  pub(crate) const fn limits_impl(&self) -> DecoderLimits {
128    self.limits
129  }
130
131  /// Borrow the wrapped `ffmpeg::decoder::Audio` (e.g. to query
132  /// `channels()` / `rate()` / `format()`).
133  #[cfg_attr(not(tarpaulin), inline(always))]
134  pub(crate) const fn inner_impl(&self) -> &ffmpeg_next::decoder::Audio {
135    &self.decoder
136  }
137}
138
139impl<C: crate::FfmpegCarrier + crate::CarrierOps> CarrierAudioStreamDecoder<C> {
140  /// Where this session is. See
141  /// [`SessionPhase`](crate::decoder::SessionPhase) — one derivation,
142  /// no road reading the latch for itself. There is no probe on this
143  /// road, so only the committed pair is reachable.
144  const fn phase(&self) -> crate::decoder::SessionPhase {
145    if self.eof {
146      crate::decoder::SessionPhase::Draining
147    } else {
148      crate::decoder::SessionPhase::Streaming
149    }
150  }
151
152  /// **This road never refuses for a parked frame, and that asymmetry
153  /// is kept rather than papered over.** The video and subtitle
154  /// decoders answer [`Sent::MustDrain`] while their scratch holds an
155  /// undelivered frame; this one has a single scratch and cannot change
156  /// which is current, so a submission under a park loses nothing and
157  /// is simply taken. See
158  /// [`CarrierVideoStreamDecoder::scratch_pending`](crate::video::CarrierVideoStreamDecoder)
159  /// for the property that makes the refusal necessary over there.
160  ///
161  /// What it *does* answer `MustDrain` to is libavcodec's own back
162  /// pressure: `avcodec_send_packet` returning `EAGAIN` because its
163  /// output queue is full. That used to arrive as
164  /// `Decode(Ffmpeg(Other { errno: EAGAIN }))` — a fault-shaped value a
165  /// generic consumer could not recognise, which is what made the
166  /// two-offer rule necessary in the first place.
167  pub(crate) fn send_packet_impl(
168    &mut self,
169    packet: &AudioPacket<AudioPacketExtra, C::Buffer>,
170  ) -> Result<Sent, AudioDecodeError> {
171    // Scoped submission: the rebuilt `AVPacket` lives only inside this
172    // call, which is what lets the view lane hand libavcodec its own
173    // buffer instead of a copy. See `boundary::with_ffmpeg_audio_packet`.
174    let state: *const crate::ffi::CallbackState = &*self._callback_state;
175    let phase = self.phase();
176    let decoder = &mut self.decoder;
177    boundary::with_ffmpeg_audio_packet::<C, _>(
178      packet,
179      self.limits.packet_limits(),
180      // Nothing on this road records what it is sent, so the
181      // packet really does die inside the call and its body may
182      // be shared.
183      crate::carrier::BodyRoute::Submission,
184      |av_pkt| {
185        // Funnel, then gate — the same two steps the receive road
186        // takes. A frame the allocator judge refused surfaces named,
187        // not as the `EINVAL` a corrupt file also produces; whatever
188        // survives the funnel is read for back pressure.
189        match decoder.send_packet(av_pkt) {
190          Ok(()) => Ok(Sent::Accepted),
191          Err(e) => {
192            crate::decoder::software_send(state, e, phase).map_err(AudioDecodeError::Decode)
193          }
194        }
195      },
196    )
197    .map_err(|e| AudioDecodeError::Decode(Error::PacketBuild(e)))?
198  }
199
200  pub(crate) fn receive_frame_impl(
201    &mut self,
202    dst: &mut AudioFrame<SampleFormat, ChannelLayoutDescription, AudioFrameExtra, C::Buffer>,
203  ) -> Result<Received, AudioDecodeError> {
204    let state: *const crate::ffi::CallbackState = &*self._callback_state;
205    let phase = self.phase();
206    // A frame whose conversion did not commit is converted again before
207    // another is asked for — see [`Self::scratch_pending`].
208    if !self.scratch_pending {
209      // Funnel first, classify second: the funnel is what turns a
210      // recorded budget refusal into its own name, and only what
211      // survives it is read as a drain state. `EAGAIN` and `Eof` are
212      // the protocol talking, so they leave through the `Ok` arm and
213      // the errno never reaches the caller.
214      if let Err(e) = self.decoder.receive_frame(&mut self.scratch) {
215        return crate::decoder::software_receive(state, e, phase).map_err(AudioDecodeError::Decode);
216      }
217    }
218    // SAFETY: the scratch holds a frame — either one `receive_frame`
219    // just filled it with, or one it was left holding by a conversion
220    // that did not commit. Convert takes what it needs out of it, so
221    // the scratch can be reused once this has committed.
222    let converted = unsafe {
223      convert::av_frame_to_audio_frame_as::<C>(
224        self.scratch.as_ptr(),
225        self.time_base,
226        self.limits.frame(),
227      )
228    };
229    match converted {
230      Ok(new_frame) => {
231        self.scratch_pending = false;
232        *dst = new_frame;
233        Ok(Received::Frame)
234      }
235      Err(e) => {
236        // Park only what another attempt could survive; a frame nothing
237        // can carry is let go, or every later receive answers with the
238        // same error.
239        self.scratch_pending = e.parks_in_decode();
240        Err(AudioDecodeError::Convert(e))
241      }
242    }
243  }
244
245  pub(crate) fn send_eof_impl(&mut self) -> Result<Sent, AudioDecodeError> {
246    let state: *const crate::ffi::CallbackState = &*self._callback_state;
247    let phase = self.phase();
248    match self.decoder.send_eof() {
249      Ok(()) => {
250        self.eof = true;
251        Ok(Sent::Accepted)
252      }
253      Err(e) => crate::decoder::software_send(state, e, phase).map_err(AudioDecodeError::Decode),
254    }
255  }
256
257  pub(crate) fn flush_impl(&mut self) -> Result<(), AudioDecodeError> {
258    // A parked frame belongs to the stream position being abandoned.
259    self.scratch_pending = false;
260    // And so does the end it was told about.
261    self.eof = false;
262    self.decoder.flush();
263    Ok(())
264  }
265}
266
267macro_rules! audio_lane_face {
268  ($($lane:ty),+ $(,)?) => { $(
269    impl CarrierAudioStreamDecoder<$lane> {
270      /// Opens an audio decoder for `parameters`.
271      pub fn open(
272        parameters: Parameters,
273        time_base: Timebase,
274        limits: DecoderLimits,
275      ) -> Result<Self, AudioDecodeError> {
276        Self::open_impl(parameters, time_base, limits)
277      }
278
279      /// The stream timebase every produced timestamp is stamped with.
280      pub const fn time_base(&self) -> Timebase {
281        self.time_base_impl()
282      }
283
284      /// The budgets this decoder was opened with.
285      pub const fn limits(&self) -> DecoderLimits {
286        self.limits_impl()
287      }
288
289      /// The wrapped decoder context.
290      pub const fn inner(&self) -> &ffmpeg_next::decoder::Audio {
291        self.inner_impl()
292      }
293    }
294
295    impl AudioStreamDecoder for CarrierAudioStreamDecoder<$lane> {
296      type Adapter = Ffmpeg;
297      type Buffer = <$lane as crate::FfmpegCarrier>::Buffer;
298      type Error = AudioDecodeError;
299
300      fn send_packet(
301        &mut self,
302        packet: &AudioPacket<AudioPacketExtra, Self::Buffer>,
303      ) -> Result<Sent, Self::Error> {
304        self.send_packet_impl(packet)
305      }
306
307      fn receive_frame(
308        &mut self,
309        dst: &mut AudioFrame<
310          SampleFormat,
311          ChannelLayoutDescription,
312          AudioFrameExtra,
313          Self::Buffer,
314        >,
315      ) -> Result<Received, Self::Error> {
316        self.receive_frame_impl(dst)
317      }
318
319      fn send_eof(&mut self) -> Result<Sent, Self::Error> {
320        self.send_eof_impl()
321      }
322
323      fn flush(&mut self) -> Result<(), Self::Error> {
324        self.flush_impl()
325      }
326    }
327  )+ };
328}
329
330audio_lane_face!(crate::View, crate::Owned);
331
332/// Errors from [`FfmpegAudioStreamDecoder`] — **faults only**.
333///
334/// Both arms are real failures. The drain's other two answers never
335/// reached this type even before they had names: they rode in as
336/// `Decode(Ffmpeg(Other { errno: EAGAIN }))` and
337/// `Decode(Ffmpeg(Eof))`, which a generic consumer had no way to
338/// recognise. They are [`Received`] states now.
339///
340/// **Open fault taxonomy, so it is `#[non_exhaustive]`.** New ways to
341/// fail are discovered — a backend, a ceiling, a corruption a codec
342/// learns to report — and a consumer that meets one it has never heard
343/// of should take its generic-fault path. That is exactly what the
344/// wildcard arm this attribute forces is for. The two status
345/// vocabularies opposite it,
346/// [`Sent`](mediadecode::Sent) and [`Received`](mediadecode::Received),
347/// are exhaustive for the mirror-image reason: their arms are the
348/// substrate's fixed state set, and there the wildcard would be dead
349/// weight hiding a state a consumer forgot.
350#[derive(thiserror::Error, Debug, IsVariant, Unwrap, TryUnwrap)]
351#[unwrap(ref, ref_mut)]
352#[try_unwrap(ref, ref_mut)]
353#[non_exhaustive]
354pub enum AudioDecodeError {
355  /// The wrapped `ffmpeg::decoder::Audio` reported an error.
356  #[error(transparent)]
357  Decode(#[from] Error),
358  /// Conversion from FFmpeg's `AVFrame` to mediadecode's `AudioFrame`
359  /// failed.
360  #[error(transparent)]
361  Convert(#[from] ConvertError),
362}