Skip to main content

mediadecode_ffmpeg/
resampler.rs

1//! [`mediadecode::resampler::AudioResampler`] impl backed by
2//! `libswresample`.
3//!
4//! Converts rate, sample format and channel layout between two specs
5//! fixed at construction — [`FfmpegResampler::new`] takes both, because
6//! neither end is discoverable and neither is a constant. The source is
7//! whatever the file holds; the target is whatever the consumer wants,
8//! and consumers disagree (16 kHz mono for a speech model, 48 kHz for
9//! an audio-event one, from the same track at the same time).
10//!
11//! # Output timestamps
12//!
13//! `swr` is a delay line: it needs future input to produce present
14//! output, so at any moment a filter's worth of samples is inside it.
15//! Timestamps are therefore *counted*, not computed per call — the
16//! output timeline is anchored on the first input timestamp and
17//! advanced by the number of samples actually produced. The frames
18//! drained after EOF continue that same line rather than restarting it,
19//! and no arithmetic anywhere depends on how many samples a given
20//! `swr_convert_frame` happened to yield.
21
22use std::{
23  collections::VecDeque,
24  ptr::{addr_of, read_unaligned},
25};
26
27use derive_more::{IsVariant, TryUnwrap, Unwrap};
28use ffmpeg_next::{
29  ChannelLayout,
30  codec::Parameters,
31  ffi::{
32    AV_NOPTS_VALUE, AVChannelOrder, AVMatrixEncoding, AVSampleFormat, av_channel_layout_from_mask,
33    av_frame_get_buffer, swr_build_matrix2,
34  },
35  format::Sample,
36  frame,
37  software::resampling,
38};
39use mediadecode::{
40  Received, Sent, Timebase, Timestamp,
41  frame::{AudioFrame, Plane},
42  resampler::AudioResampler,
43};
44use mediaframe::audio::ChannelLayoutDescription;
45
46use crate::{
47  Error, Ffmpeg, extras::AudioFrameExtra, limits::FrameLimits, sample_format::SampleFormat,
48};
49
50/// The frame type a resampler accepts and produces, on lane `C`.
51///
52/// Written as a projection rather than a bounded alias so the bound
53/// lives on the items that need it — `type_alias_bounds` is not
54/// enforced anyway, and a bound written where it is not enforced reads
55/// like a promise the compiler is keeping.
56type Frame<C> = AudioFrame<
57  SampleFormat,
58  ChannelLayoutDescription,
59  AudioFrameExtra,
60  <C as crate::FfmpegCarrier>::Buffer,
61>;
62
63/// One end of a conversion: sample rate, sample format, channel layout.
64///
65/// Spelled in FFmpeg's own vocabulary because construction is off the
66/// [`AudioResampler`] trait and this is the backend that has to be
67/// handed to `swr_alloc_set_opts2`. [`FfmpegResampler`] restates the
68/// source spec in the vocabulary a decoded frame carries, so the
69/// mid-stream check compares like with like without the caller ever
70/// seeing two dialects.
71#[derive(Copy, Clone, Debug, PartialEq, Eq)]
72pub struct ResampleSpec {
73  rate: u32,
74  format: Sample,
75  layout: ChannelLayout,
76}
77
78impl ResampleSpec {
79  /// Constructs a spec from its three parts.
80  ///
81  /// Deliberately total and `const`: a spec is a description, and
82  /// describing something `swr` cannot convert is not itself an error.
83  /// [`FfmpegResampler::new`] is the choke point every construction
84  /// route passes through, and it is what refuses a rate, a format or a
85  /// channel layout this backend cannot honour — see
86  /// [`FfmpegResampler::new`] for the roster and
87  /// [`ResampleError::UnsupportedLayout`] for why a layout can be
88  /// refused at all.
89  #[inline]
90  pub const fn new(rate: u32, format: Sample, layout: ChannelLayout) -> Self {
91    Self {
92      rate,
93      format,
94      layout,
95    }
96  }
97
98  /// The spec a track *declares*, read off the codec parameters a
99  /// [`crate::FfmpegDemuxer`] track row carries
100  /// (`track.extra().parameters()`) — the "source from `TrackInfo`"
101  /// path.
102  ///
103  /// Returns `None` for a non-audio track, for one whose declared
104  /// sample format is `AV_SAMPLE_FMT_NONE` (a codec whose format is
105  /// only known once its decoder opens), and for a custom or ambisonic
106  /// channel layout — see [`Self::from_decoder`] for the first case and
107  /// the note on [`unspecified_layout`] for the last.
108  pub fn from_parameters(parameters: &Parameters) -> Option<Self> {
109    // Before `medium()`, which dereferences the pointer inside
110    // ffmpeg-next. `Parameters`' safe constructors hand back a
111    // null-backed value when FFmpeg's allocation failed and report
112    // nothing, so a caller can arrive here holding one without ever
113    // having been told. Parameters that were never allocated describe
114    // no audio, which this function already has a word for.
115    // SAFETY: reading the pointer without dereferencing it.
116    if unsafe { parameters.as_ptr() }.is_null() {
117      return None;
118    }
119    if !crate::boundary::media_kind_of(parameters).is_audio() {
120      return None;
121    }
122    // SAFETY: `parameters` keeps the `AVCodecParameters` live; every
123    // read below goes through the raw pointer and none of them
124    // materialises a bindgen enum out of foreign memory.
125    let par = unsafe { parameters.as_ptr() };
126    let rate = unsafe { (*par).sample_rate }.max(0) as u32;
127    if rate == 0 {
128      return None;
129    }
130    let format = SampleFormat::from_raw(unsafe { (*par).format }).to_ffmpeg()?;
131    let layout = unsafe { layout_from_raw(addr_of!((*par).ch_layout)) }?;
132    Some(Self::new(rate, format, layout))
133  }
134
135  /// The spec an opened decoder will actually produce — its rate,
136  /// sample format and channel layout, straight off the codec context.
137  ///
138  /// Reach it through
139  /// [`FfmpegAudioStreamDecoder::inner`](crate::FfmpegAudioStreamDecoder::inner).
140  /// `None` on a custom or ambisonic layout, and on a context whose
141  /// sample format is still unset (a decoder that has not been opened).
142  pub fn from_decoder(decoder: &ffmpeg_next::decoder::Audio) -> Option<Self> {
143    // SAFETY: `decoder` keeps the `AVCodecContext` live. `sample_fmt`
144    // is read as the raw integer it is rather than through
145    // `decoder.format()`, which would construct an `AVSampleFormat`
146    // out of foreign memory.
147    let ctx = unsafe { decoder.as_ptr() };
148    // Same reason as `from_parameters`: `codec::Context::new()` is a
149    // safe constructor over an unchecked `avcodec_alloc_context3`, so a
150    // decoder can be null-backed without anyone having been told.
151    if ctx.is_null() {
152      return None;
153    }
154    let format =
155      SampleFormat::from_raw(unsafe { read_unaligned(addr_of!((*ctx).sample_fmt).cast::<i32>()) })
156        .to_ffmpeg()?;
157    let rate = unsafe { (*ctx).sample_rate }.max(0) as u32;
158    if rate == 0 {
159      return None;
160    }
161    let layout = unsafe { layout_from_raw(addr_of!((*ctx).ch_layout)) }?;
162    Some(Self::new(rate, format, layout))
163  }
164
165  /// A layout that names a channel *count* and nothing else —
166  /// `AV_CHANNEL_ORDER_UNSPEC`.
167  ///
168  /// Not a degenerate case: a WAV file without a `WAVE_FORMAT_EXTENSIBLE`
169  /// channel mask genuinely declares no layout, and FFmpeg faithfully
170  /// reports it as unspecified in the codec parameters, in the codec
171  /// context, and on every decoded frame. Substituting a default layout
172  /// would make the source spec disagree with the frames it is supposed
173  /// to describe, and every `send_frame` would be refused as a
174  /// mid-stream change. `swr` accepts an unspecified layout at either
175  /// end and maps the channels positionally.
176  #[inline]
177  pub fn unspecified_layout(channels: i32) -> ChannelLayout {
178    // SAFETY: a zeroed `AVChannelLayout` is a valid value — `order`
179    // reads as `AV_CHANNEL_ORDER_UNSPEC`, the zero discriminant, and
180    // the union is documented as unused for that order.
181    unsafe {
182      let mut layout: ffmpeg_next::ffi::AVChannelLayout = std::mem::zeroed();
183      layout.nb_channels = channels.max(0);
184      ChannelLayout(layout)
185    }
186  }
187
188  /// Sample rate in Hz.
189  #[inline]
190  pub const fn rate(&self) -> u32 {
191    self.rate
192  }
193  /// Sample format.
194  #[inline]
195  pub const fn format(&self) -> Sample {
196    self.format
197  }
198  /// Channel layout.
199  #[inline]
200  pub const fn layout(&self) -> ChannelLayout {
201    self.layout
202  }
203  /// Channel count, from the layout.
204  #[inline]
205  pub fn channels(&self) -> i32 {
206    self.layout.channels()
207  }
208
209  /// The timebase output frames carry — one tick per output sample.
210  fn timebase(&self) -> Timebase {
211    Timebase::new(
212      1,
213      std::num::NonZeroI32::new(self.rate.min(i32::MAX as u32) as i32).unwrap_or(
214        // A zero-rate spec never reaches here: `new` is the only way in
215        // and every caller of it names a real rate. Falling back to
216        // one tick per second keeps the arithmetic total rather than
217        // panicking on a value that cannot occur.
218        std::num::NonZeroI32::new(1).expect("1 is non-zero"),
219      ),
220    )
221  }
222}
223
224/// `mediadecode::resampler::AudioResampler` impl wrapping
225/// `swresample`.
226///
227/// Construction is [`Self::new`], off the trait, taking both specs —
228/// see the trait's own documentation for why the target can never be a
229/// constant.
230pub struct CarrierResampler<C: crate::FfmpegCarrier> {
231  ctx: resampling::Context,
232  source: ResampleSpec,
233  target: ResampleSpec,
234  /// The source spec restated in the vocabulary a decoded `AudioFrame`
235  /// carries. The mid-stream check compares against these, not against
236  /// FFmpeg's dialect, so it never has to translate a frame.
237  source_format: SampleFormat,
238  source_layout: ChannelLayoutDescription,
239  /// The target spec in the vocabulary an output frame carries,
240  /// computed once at construction. Assembling a converted frame after
241  /// `swr` has run must not have to ask FFmpeg anything, because asking
242  /// can fail — see [`FfmpegResampler::prepare_output`].
243  target_format: SampleFormat,
244  target_layout: ChannelLayoutDescription,
245  /// The layouts `swr` is really configured with — see
246  /// [`initialized_layout`]. Every `AVFrame` this type stages or
247  /// allocates carries these, not the declared ones.
248  staged_source_layout: ChannelLayout,
249  staged_target_layout: ChannelLayout,
250  target_timebase: Timebase,
251  ready: VecDeque<Frame<C>>,
252  /// Next output timestamp, in target-rate ticks. `None` until the
253  /// first input frame anchors it.
254  next_pts: Option<i64>,
255  eof: bool,
256  /// `true` once the post-EOF tail has been drained to its end.
257  ///
258  /// **The terminal answer has to be terminal.** Without this latch,
259  /// every poll past the end re-enters the flush road — allocating an
260  /// output frame and asking `swr` again — because `swr_get_delay` can
261  /// keep reporting residual samples that `swr_convert_frame` will
262  /// never emit (observed: 16 samples left standing after the tail is
263  /// genuinely exhausted). That is harmless while allocation succeeds
264  /// and wrong when it does not: a session that has already answered
265  /// `Ended` would start answering with an allocation error instead,
266  /// turning a settled protocol state back into a fault. The latch
267  /// makes the end cheap and unconditional. `flush` clears it with the
268  /// rest of the session.
269  drained: bool,
270  /// What one converted frame may cost. See
271  /// [`Self::check_output_bytes`] for why a resampler needs a ceiling
272  /// of its own even when its input already had one.
273  limits: FrameLimits,
274  /// The lane. Zero-sized: it selects how a produced plane is carried
275  /// — shared out of the output `AVFrame` or copied out of it — and
276  /// nothing else about the conversion.
277  _carrier: core::marker::PhantomData<C>,
278}
279
280impl<C: crate::FfmpegCarrier + crate::CarrierOps> CarrierResampler<C> {
281  /// Opens a resampler between two explicit specs.
282  ///
283  /// Both are required and neither is inferred. The source is what the
284  /// decoder will hand over — read it off the track
285  /// ([`ResampleSpec::from_parameters`]) or off the opened decoder
286  /// ([`ResampleSpec::from_decoder`]). The target is the caller's, and
287  /// is options: 16 kHz mono for a speech model, 48 kHz for an
288  /// audio-event one, both from the same track.
289  ///
290  /// # The choke point
291  ///
292  /// [`ResampleSpec::new`] is `const` and total, so this is where both
293  /// ends are checked — every construction route (`from_parameters`,
294  /// `from_decoder`, the public constructor) passes through here, and
295  /// nothing hazardous reaches `swr` or a staged `AVFrame` behind it:
296  ///
297  /// - a rate of zero, or one past `c_int`
298  ///   ([`ResampleError::UnsupportedRate`]);
299  /// - `AV_SAMPLE_FMT_NONE` ([`ResampleError::UnsupportedFormat`]);
300  /// - a channel layout that is neither native nor unspecified, or one
301  ///   naming no channels ([`ResampleError::UnsupportedLayout`]).
302  ///
303  /// `limits` bounds what one **converted** frame may cost.
304  ///
305  /// [`FrameLimits`] rather than a seat of this seam's own: the
306  /// quantity is bytes of one produced audio frame, which is exactly
307  /// what [`FrameLimits::max_frame_bytes`] means everywhere else in
308  /// this crate, and one number for "what a frame may cost" is worth
309  /// more than a second vocabulary. [`FrameLimits::max_pixels`] is
310  /// unused here, as it is on the audio decode path, and for the same
311  /// reason: audio has no pixels.
312  pub(crate) fn new_impl(
313    source: ResampleSpec,
314    target: ResampleSpec,
315    limits: FrameLimits,
316  ) -> Result<Self, ResampleError> {
317    check_spec(&source, SpecEnd::Source)?;
318    check_spec(&target, SpecEnd::Target)?;
319
320    // The layouts `swr` is really configured with, resolved *before*
321    // the pair is judged — because the conversion that will run is
322    // between these two, not between the two that were declared. An
323    // unspecified layout becomes FFmpeg's default for its channel count
324    // (twenty-four unspecified channels are 22.2), so judging the
325    // declared pair let exactly the routing the explicit 22.2 refusal
326    // blocks walk in through the unspecified door.
327    let staged_source_layout = initialized_layout(source.layout);
328    let staged_target_layout = initialized_layout(target.layout);
329    check_pair(&staged_source_layout, &staged_target_layout)?;
330    let ctx = open_context(&source, &target, staged_source_layout, staged_target_layout)?;
331
332    let source_format = SampleFormat::from_ffmpeg(source.format);
333    let target_format = SampleFormat::from_ffmpeg(target.format);
334    // SAFETY: the layout is a live `ChannelLayout` owned by this scope.
335    let target_layout =
336      crate::channel_layout::channel_layout_description_from_ffmpeg(&staged_target_layout);
337    // SAFETY: the layout is a live `ChannelLayout` owned by `source`
338    // for the duration of this call.
339    let source_layout =
340      crate::channel_layout::channel_layout_description_from_ffmpeg(&source.layout);
341    let target_timebase = target.timebase();
342
343    Ok(Self {
344      ctx,
345      source,
346      target,
347      source_format,
348      source_layout,
349      target_format,
350      target_layout,
351      staged_source_layout,
352      staged_target_layout,
353      target_timebase,
354      ready: VecDeque::new(),
355      next_pts: None,
356      eof: false,
357      drained: false,
358      limits,
359      _carrier: core::marker::PhantomData,
360    })
361  }
362
363  /// The spec frames must arrive in.
364  #[inline]
365  pub(crate) const fn source_impl(&self) -> &ResampleSpec {
366    &self.source
367  }
368
369  /// The spec frames leave in.
370  #[inline]
371  pub(crate) const fn target_impl(&self) -> &ResampleSpec {
372    &self.target
373  }
374
375  /// Borrows the wrapped `swr` context.
376  #[inline]
377  pub(crate) const fn inner_impl(&self) -> &resampling::Context {
378    &self.ctx
379  }
380
381  /// Samples still inside the delay line, counted at the output rate.
382  #[inline]
383  pub(crate) fn delay_impl(&self) -> i64 {
384    self.ctx.delay().map_or(0, |d| d.output.max(0))
385  }
386
387  /// Refuses a frame whose shape is not the source spec.
388  fn check_source(&self, frame: &Frame<C>) -> Result<(), ResampleError> {
389    if frame.sample_rate() != self.source.rate
390      || *frame.sample_format() != self.source_format
391      || *frame.channel_layout() != self.source_layout
392    {
393      return Err(ResampleError::SourceChanged(SourceChanged::new(
394        self.source.rate,
395        self.source_format,
396        frame.sample_rate(),
397        *frame.sample_format(),
398      )));
399    }
400    Ok(())
401  }
402
403  /// Where a frame's timestamp lands on the output timeline, or `None`
404  /// when it carries none.
405  ///
406  /// Rescaled with the **checked** rung, and before anything is staged.
407  /// `Timestamp::rescale_to` saturates, and both ends of that clamp are
408  /// wrong here: a positive one reaches the counted timeline's checked
409  /// addition only after `swr` has consumed the input, leaving a
410  /// session no caller can retry; a negative one lands on `i64::MIN`,
411  /// which *is* `AV_NOPTS_VALUE`, so the conversion back reads the
412  /// frame as carrying no timestamp at all and an extreme timestamp is
413  /// silently erased. A timestamp that does not fit the output timeline
414  /// is refused by name, with the resampler untouched.
415  fn anchor_of(&self, frame: &Frame<C>) -> Result<Option<i64>, ResampleError> {
416    let Some(timestamp) = frame.pts() else {
417      return Ok(None);
418    };
419    let ticks = timestamp.pts();
420    let out_of_range = || ResampleError::TimestampOutOfRange(TimestampOutOfRange::new(ticks));
421    // `AV_NOPTS_VALUE` is a sentinel, not a time. A frame carrying it
422    // as a value says something contradictory, and anchoring on it
423    // would produce output frames that report no timestamp.
424    if ticks == AV_NOPTS_VALUE {
425      return Err(out_of_range());
426    }
427    let rescaled = timestamp
428      .timebase()
429      .checked_rescale(ticks, self.target_timebase)
430      .ok_or_else(out_of_range)?;
431    if rescaled == AV_NOPTS_VALUE {
432      return Err(out_of_range());
433    }
434    Ok(Some(rescaled))
435  }
436
437  /// Stages a decoded frame as an `AVFrame` swr can read.
438  ///
439  /// Geometry is settled **before** anything is allocated. A frame's
440  /// header is a claim, not a fact: `nb_samples` comes from the same
441  /// foreign memory as the planes it describes, and sizing an
442  /// allocation off it first would let a forged frame with a
443  /// twelve-byte plane ask for tens of gigabytes on its way to being
444  /// refused.
445  fn stage_input(&self, frame: &Frame<C>) -> Result<frame::Audio, ResampleError> {
446    let samples = frame.nb_samples() as usize;
447    let channels = self.source.channels();
448
449    // What the *format* requires, not what the allocated frame reports
450    // — the frame does not exist yet.
451    let planes = if self.source.format.is_planar() {
452      // `check_spec` proved this positive at construction.
453      channels as usize
454    } else {
455      1
456    };
457    let found = frame.plane_count() as usize;
458    if planes > found {
459      return Err(ResampleError::PlaneCount(PlaneCount::new(planes, found)));
460    }
461    let bytes = plane_bytes(self.source.format, samples, channels)
462      .ok_or(ResampleError::SampleCount(SampleCount::new(samples)))?;
463    for plane in frame.planes().iter().take(planes) {
464      let src = plane.data_ref().as_ref();
465      if src.len() < bytes {
466        return Err(ResampleError::PlaneCount(PlaneCount::new(bytes, src.len())));
467      }
468    }
469
470    // Only now, with every plane proved long enough for the sample
471    // count that sizes this allocation.
472    let mut input = new_audio_frame(
473      self.source.format,
474      samples,
475      self.source.rate,
476      self.staged_source_layout,
477    )?;
478    // What the allocation really produced. `data_mut` panics past its
479    // own plane count, and this crate does not put a panic on a path
480    // that reads foreign geometry.
481    let staged = input.planes();
482    if staged < planes {
483      return Err(ResampleError::PlaneCount(PlaneCount::new(planes, staged)));
484    }
485    for (index, plane) in frame.planes().iter().take(planes).enumerate() {
486      let src = plane.data_ref().as_ref();
487      let dst = input.data_mut(index);
488      if dst.len() < bytes {
489        return Err(ResampleError::PlaneCount(PlaneCount::new(bytes, dst.len())));
490      }
491      dst[..bytes].copy_from_slice(&src[..bytes]);
492    }
493    Ok(input)
494  }
495
496  /// The most samples the next conversion could produce: the delay
497  /// line's contents plus `in_samples` of new input, rescaled to the
498  /// output rate and rounded up.
499  ///
500  /// Separate from the allocation because it is also the preflight the
501  /// output timeline is checked against — *before* `swr` consumes
502  /// anything, so a refusal leaves the session where a caller can retry
503  /// it.
504  fn output_capacity(&self, in_samples: i64) -> Result<usize, ResampleError> {
505    let delay_in = self.ctx.delay().map_or(0, |d| d.input.max(0));
506    let total = delay_in.saturating_add(in_samples).max(0) as i128;
507    let scaled = (total * i128::from(self.target.rate) + i128::from(self.source.rate) - 1)
508      / i128::from(self.source.rate).max(1);
509    // One extra sample of headroom: swr rounds its own accounting, and
510    // an output frame one short would silently push the remainder into
511    // the internal FIFO where the pts accounting cannot see it until
512    // the next call.
513    let samples = scaled + 1;
514    // `av_frame_get_buffer` takes the count as a `c_int`. A request
515    // past that is refused by name rather than clamped: a silently
516    // shortened output frame is a stream that loses samples.
517    if samples > i128::from(i32::MAX) {
518      // Saturating only for a count past `usize` itself, which no
519      // machine could hold either way.
520      return Err(ResampleError::SampleCount(SampleCount::new(
521        usize::try_from(samples).unwrap_or(usize::MAX),
522      )));
523    }
524    Ok(samples.max(1) as usize)
525  }
526
527  /// Refuses a conversion whose output would not fit the frame ceiling.
528  ///
529  /// # Why a resampler needs one even though its input had one
530  ///
531  /// [`Self::output_capacity`] bounds the output **sample count**, and
532  /// only at `i32::MAX` — the structural limit of
533  /// `av_frame_get_buffer`. Nothing in it bounds the *bytes*, and the
534  /// two are related by a ratio the caller does not control: the
535  /// capacity is `input_samples × target_rate / source_rate`, and a
536  /// source spec read off an untrusted container can say 1 Hz. One
537  /// second of 1 Hz mono input converted to 48 kHz stereo `f32` is
538  /// 384 KiB from 4 bytes — and the same input against a 1 Hz source
539  /// claim and a 192 kHz target is multi-gigabyte. The frame that
540  /// arrived was within *its* ceiling; the frame that leaves need not
541  /// be, so it gets its own judgement.
542  ///
543  /// Both allocations are covered: `av_frame_get_buffer`'s, and the
544  /// [`FfmpegBytes`] copy [`Self::finish_output`] makes from it.
545  fn check_output_bytes(&self, capacity: usize, channels: i32) -> Result<(), ResampleError> {
546    // **Priced as the allocator prices it, not as the samples weigh.**
547    // This used to multiply the tight plane length by the plane count,
548    // which is the payload's arithmetic and not
549    // `av_frame_get_buffer`'s: a one-sample eight-channel planar `s16`
550    // frame is 16 bytes of samples and a **768-byte** allocation,
551    // because every plane is aligned and padded on its own. A 16-byte
552    // ceiling admitted it.
553    //
554    // The overhead is under one percent on any frame big enough to
555    // care about, which is exactly why this went unseen — see
556    // [`crate::footprint`] for the measured table and for the rule the
557    // whole crate now keeps: a judge must dominate the allocator's
558    // arithmetic, not the payload's.
559    let bytes = crate::footprint::audio_frame_bytes(
560      ffmpeg_next::ffi::AVSampleFormat::from(self.target.format) as libc::c_int,
561      capacity,
562      channels.max(0) as usize,
563    )
564    .ok_or(ResampleError::OutputTooLarge(OutputTooLarge::new(
565      usize::MAX,
566      self.limits.max_frame_bytes(),
567    )))?;
568    if bytes > self.limits.max_frame_bytes() {
569      return Err(ResampleError::OutputTooLarge(OutputTooLarge::new(
570        bytes,
571        self.limits.max_frame_bytes(),
572      )));
573    }
574    Ok(())
575  }
576
577  /// Refuses a conversion whose output could not be labelled: the
578  /// timeline plus everything this call might produce has to stay
579  /// inside `i64`.
580  ///
581  /// Asked before `swr` sees a sample, like everything else that can
582  /// fail. [`Self::finish_output`] performs the same addition against
583  /// the count actually produced, which cannot exceed the capacity
584  /// checked here — so once this passes, that one cannot fail.
585  fn check_timeline(&self, anchor: Option<i64>, capacity: usize) -> Result<(), ResampleError> {
586    let pts = self.next_pts.or(anchor).unwrap_or(0);
587    let samples = capacity as i64;
588    if pts.checked_add(samples).is_none() {
589      return Err(ResampleError::TimestampOverflow(TimestampOverflow::new(
590        pts, samples,
591      )));
592    }
593    Ok(())
594  }
595
596  /// Allocates the output frame **and proves every plane the converted
597  /// frame will be read out of**, before `swr` is allowed to touch a
598  /// sample.
599  ///
600  /// This is the shape the whole seam is built around. Anything
601  /// fallible that runs *after* `swr_convert_frame` has consumed input
602  /// leaves a session no caller can act on: retrying feeds the same
603  /// samples twice, continuing loses them, and the delay line has moved
604  /// either way. The failure kept relocating — the timestamp addition,
605  /// the tail drain, then the output wrapping — so the fix is not
606  /// another check in another place but an ordering that leaves nothing
607  /// on the far side: the frame, every plane pointer it will be read
608  /// through, and the queue slot are all taken here, where failing
609  /// costs nothing but an error.
610  ///
611  /// **What 0.9 changed, and what it did not.** Through 0.8 this
612  /// function also *acquired* one `AVBufferRef` view per plane, because
613  /// wrapping a plane could fail and so had to happen on this side of
614  /// the conversion; [`Self::finish_output`] then narrowed each view to
615  /// what `swr` produced. The amputation removes the views — the output
616  /// planes are copied out afterwards instead — and with them the
617  /// failure that forced the acquisition to be early. What stays early
618  /// is the *proof*: every plane pointer is checked non-null and
619  /// checked to address `plane_len` bytes inside one of the frame's own
620  /// buffers here, so the copy on the far side has nothing left to
621  /// judge and [`Self::finish_output`] remains infallible.
622  fn prepare_output(&self, capacity: usize) -> Result<PreparedOutput<C>, ResampleError> {
623    let channels = self.target.channels();
624    let plane_count = if self.target.format.is_planar() {
625      // `check_spec` proved this positive at construction.
626      channels as usize
627    } else {
628      1
629    };
630    let plane_len = plane_bytes(self.target.format, capacity, channels)
631      .ok_or(ResampleError::SampleCount(SampleCount::new(capacity)))?;
632    // Linear in the sample count, which is what lets the post-run
633    // trim be a multiplication rather than another fallible call.
634    let per_sample = plane_bytes(self.target.format, 1, channels)
635      .ok_or(ResampleError::SampleCount(SampleCount::new(1)))?;
636
637    // **The byte ceiling, before `av_frame_get_buffer` and before the
638    // carrier copy that follows it.** Measured first, allocated second:
639    // the three quantities above are arithmetic over the target spec
640    // and cost nothing, so there is no reason for the frame to exist
641    // before the answer does.
642    self.check_output_bytes(capacity, channels)?;
643
644    let frame = new_audio_frame(
645      self.target.format,
646      capacity,
647      self.target.rate,
648      self.staged_target_layout,
649    )?;
650    if frame.planes() < plane_count {
651      return Err(ResampleError::PlaneCount(PlaneCount::new(
652        plane_count,
653        frame.planes(),
654      )));
655    }
656
657    let mut reserved: [Option<C::Reserved>; 8] = core::array::from_fn(|_| None);
658    for (index, slot) in reserved.iter_mut().enumerate().take(plane_count) {
659      // SAFETY: `frame` owns a live `AVFrame` this call just
660      // allocated; `data` is a public field and `plane_count` is
661      // within the eight slots `data` has.
662      let data_ptr = unsafe { (*frame.as_ptr()).data[index] };
663      if data_ptr.is_null() {
664        return Err(ResampleError::OutputBuffer(OutputBuffer::new(index)));
665      }
666      // SAFETY: the frame is live, and the helper only reads `buf[]`'s
667      // ranges to find the one containing `data_ptr`. Proving it here
668      // is what lets the carry on the far side of the conversion be
669      // unconditional — a copy on the owned lane, a view on the other,
670      // and neither has a proof left to make.
671      let backing =
672        unsafe { crate::convert::find_audio_backing_buffer(frame.as_ptr(), data_ptr, plane_len) }
673          .ok_or(ResampleError::OutputBuffer(OutputBuffer::new(index)))?;
674      // The carrier's claim on the plane, taken **here** — before `swr`
675      // consumes anything. See [`FfmpegCarrier::reserve`].
676      //
677      // SAFETY: `backing` is a live buffer of this frame's, proved just
678      // above to cover `plane_len` bytes from `data_ptr`; the frame is
679      // moved into `PreparedOutput` below and so outlives the commit.
680      let offset = unsafe { (data_ptr as usize).wrapping_sub((*backing).data as usize) };
681      // SAFETY: as above — the extent lies inside `backing`.
682      *slot = Some(
683        unsafe { C::reserve(backing, offset, plane_len) }
684          .ok_or(ResampleError::OutputBuffer(OutputBuffer::new(index)))?,
685      );
686    }
687
688    Ok(PreparedOutput {
689      frame,
690      reserved,
691      plane_count,
692      plane_len,
693      per_sample,
694    })
695  }
696
697  /// Turns a converted frame into a `mediadecode` one. **Infallible**,
698  /// by construction: every check it could have made was made in
699  /// [`Self::prepare_output`], and everything left here is arithmetic
700  /// over values this type owns plus a copy that cannot be refused.
701  ///
702  /// `None` when the conversion produced nothing — the delay line
703  /// swallowed the input, which is ordinary and not a failure.
704  fn finish_output(&mut self, mut prepared: PreparedOutput<C>) -> Option<Frame<C>> {
705    let produced = prepared.frame.samples();
706    if produced == 0 {
707      return None;
708    }
709    let pts = self.next_pts.unwrap_or(0);
710    // `check_timeline` ran before `swr` did, against a capacity that is
711    // never smaller than what came out, so this addition cannot leave
712    // `i64`. It is stated rather than checked because a check here
713    // would be an error path on the wrong side of the conversion —
714    // exactly what this design exists to remove.
715    debug_assert!(
716      pts.checked_add(produced as i64).is_some(),
717      "the timeline was preflighted against a capacity >= produced",
718    );
719    self.next_pts = Some(pts.saturating_add(produced as i64));
720
721    let bytes = prepared
722      .per_sample
723      .saturating_mul(produced)
724      .min(prepared.plane_len);
725    let plane_count = prepared.plane_count;
726    let mut planes: [Plane<C::Buffer>; 8] = core::array::from_fn(|_| Plane::new(C::empty(), 0));
727    for (index, slot) in planes.iter_mut().enumerate().take(plane_count) {
728      // Present for every index below `plane_count`: `prepare_output`
729      // fills exactly that many and returns an error otherwise.
730      let Some(reserved) = prepared.reserved[index].take() else {
731        continue;
732      };
733      // **The valid prefix, not the plane.** `plane_len` is what
734      // capacity was allocated for; `bytes` is what `swr` produced.
735      // Both lanes stop at the latter, for the same reason the decode
736      // road does: the tail is allocator memory nothing wrote, and a
737      // carrier's span is a span a consumer may read.
738      //
739      // SAFETY: the reservation covers `plane_len` bytes inside one of
740      // `frame`'s own buffers, and `bytes <= plane_len` by the `min`
741      // above. `swr_convert_frame` has written those bytes and does not
742      // replace the buffers; `frame` has been owned by `prepared` — and
743      // so kept alive — across the whole conversion. A view committed
744      // here outlives `prepared` by refcount, and the writing is over
745      // before the sharing begins: this resampler allocates a fresh
746      // output frame per conversion, so nothing ever writes into a
747      // buffer a delivered frame is reading.
748      *slot = Plane::new(unsafe { C::commit(reserved, bytes) }, bytes as u32);
749    }
750
751    Some(
752      AudioFrame::new(
753        self.target.rate,
754        produced as u32,
755        // Exact, not clipped: `check_spec` refused every spec outside
756        // `1..=MAX_FRAME_CHANNELS` before this resampler existed.
757        self.target.channels() as u8,
758        self.target_format,
759        self.target_layout.clone(),
760        planes,
761        plane_count as u8,
762        AudioFrameExtra::default(),
763      )
764      .with_pts(Some(Timestamp::new(pts, self.target_timebase)))
765      .with_duration(Some(Timestamp::new(produced as i64, self.target_timebase))),
766    )
767  }
768}
769
770/// Everything a converted frame needs, acquired before the conversion
771/// runs. See [`FfmpegResampler::prepare_output`].
772struct PreparedOutput<C: crate::FfmpegCarrier + crate::CarrierOps> {
773  /// The output `AVFrame`. Owning it here is what keeps the plane
774  /// pointers below valid across the conversion — moving this struct
775  /// moves a pointer to the `AVFrame`, never the `AVFrame` or the
776  /// buffers it addresses.
777  frame: frame::Audio,
778  /// One carrier claim per populated plane, taken before the
779  /// conversion ran and settled at its true length after — which is
780  /// what keeps this struct's whole reason for existing intact on the
781  /// view lane too. `None` for every unpopulated slot.
782  reserved: [Option<C::Reserved>; 8],
783  plane_count: usize,
784  /// Bytes one plane holds at full capacity — the ceiling every trim
785  /// stays under.
786  plane_len: usize,
787  /// Bytes one plane holds per sample.
788  per_sample: usize,
789}
790
791impl<C: crate::FfmpegCarrier + crate::CarrierOps> CarrierResampler<C> {
792  /// **Always [`Sent::Accepted`] when it accepts at all.** The
793  /// converted-frame queue this type keeps is unbounded — every frame a
794  /// conversion produces is built before `swr` is asked and pushed
795  /// straight onto it — so there is no state in which draining first
796  /// would let a submission through that is refused now. A bounded
797  /// implementation of the same face would answer
798  /// [`Sent::MustDrain`] here; this one has nothing to say it about.
799  ///
800  /// [`ResampleError::AfterEof`] stays an error rather than becoming
801  /// that arm, and the line is the same one the decoders draw: a
802  /// resampler that has been told the stream ended will refuse this
803  /// frame however much is drained first, so sending the caller into a
804  /// drain loop would be sending it nowhere. `flush` is the way back,
805  /// and the message says so.
806  pub(crate) fn send_frame_impl(&mut self, frame: &Frame<C>) -> Result<Sent, ResampleError> {
807    if self.eof {
808      return Err(ResampleError::AfterEof);
809    }
810    self.check_source(frame)?;
811    // A frame carrying no samples is a header and nothing else. There
812    // is nothing to convert and nothing to stage: `av_frame_get_buffer`
813    // refuses a zero-sample allocation, so staging one would hand `swr`
814    // an unbacked `AVFrame` for no gain.
815    if frame.nb_samples() == 0 {
816      return Ok(Sent::Accepted);
817    }
818
819    // Nothing below touches the session's state until the conversion
820    // has succeeded. A refused frame must leave the timeline exactly
821    // where it was, or the next good frame inherits the rejected one's
822    // timestamp.
823    let anchor = self.anchor_of(frame)?;
824    let input = self.stage_input(frame)?;
825    let capacity = self.output_capacity(frame.nb_samples() as i64)?;
826    self.check_timeline(anchor, capacity)?;
827    let mut prepared = self.prepare_output(capacity)?;
828    // The last fallible thing before the conversion: room for the frame
829    // it will produce. `push_back` on a full queue allocates, and an
830    // allocation failure there aborts the process rather than
831    // unwinding — so the growth happens here, where it can be an error.
832    self
833      .ready
834      .try_reserve(1)
835      .map_err(|_| ResampleError::QueueAlloc)?;
836
837    // The only mutation. Everything above could fail and cost nothing;
838    // nothing below can fail at all.
839    self
840      .ctx
841      .run(&input, &mut prepared.frame)
842      .map_err(|e| ResampleError::Resample(Error::Ffmpeg(e)))?;
843
844    // The frame is inside the filter now, so the timeline may be
845    // anchored on it. Anchored on *input* rather than on the first
846    // output, because a call that produces nothing but fills the delay
847    // line still fixes where the stream starts.
848    if self.next_pts.is_none() {
849      self.next_pts = anchor;
850    }
851    if let Some(converted) = self.finish_output(prepared) {
852      self.ready.push_back(converted);
853    }
854    Ok(Sent::Accepted)
855  }
856
857  /// **No parked-frame seat here, and none is needed.** The queue holds
858  /// frames that are already built: every fallible step of a
859  /// conversion — the carrier's claim included — happens in
860  /// [`Self::prepare_output`], before `swr` consumes anything, and
861  /// `finish_output` is infallible by construction. There is no
862  /// conversion left to fail after a frame has been taken out of the
863  /// queue, so nothing can be lost between the two. That is the
864  /// property the reserve-then-commit seam was built for, stated where
865  /// the sibling roads state their seats.
866  pub(crate) fn receive_frame_impl(
867    &mut self,
868    dst: &mut Frame<C>,
869  ) -> Result<Received, ResampleError> {
870    if let Some(frame) = self.ready.pop_front() {
871      *dst = frame;
872      return Ok(Received::Frame);
873    }
874    if !self.eof {
875      return Ok(Received::NeedsInput);
876    }
877    if self.drained {
878      return Ok(Received::Ended);
879    }
880    // EOF: drain the conversion tail. Without this every file loses the
881    // tens of milliseconds sitting inside the filter.
882    //
883    // **This is where the two answers used to be one.** Pre-EOF nothing
884    // ready and post-EOF tail exhausted both returned `Again`, so a
885    // caller that did not itself remember whether it had called
886    // `send_eof` could not tell "send more" from "there is no more" —
887    // and a drain loop written against the seam alone spun forever on
888    // a resampler that was already finished.
889    let remaining = self.delay_impl();
890    if remaining <= 0 {
891      self.drained = true;
892      return Ok(Received::Ended);
893    }
894    let capacity = remaining.min(i64::from(i32::MAX)) as usize;
895    // Same discipline as `send_frame`, and for the same reason: the
896    // tail is drained only once the timeline can hold it and every
897    // reference the converted frame needs is already in hand, so a
898    // failure leaves the delay line untouched instead of turning
899    // samples into an error.
900    self.check_timeline(None, capacity)?;
901    let mut prepared = self.prepare_output(capacity)?;
902    self
903      .ctx
904      .flush(&mut prepared.frame)
905      .map_err(|e| ResampleError::Resample(Error::Ffmpeg(e)))?;
906    match self.finish_output(prepared) {
907      Some(frame) => {
908        *dst = frame;
909        Ok(Received::Frame)
910      }
911      // The delay line reported samples and the flush produced none.
912      // Another flush would report the same and produce the same — this
913      // really happens, `swr_get_delay` standing at a residue the
914      // converter will not emit — so this is the end of the tail and
915      // not a pause in it. Saying otherwise is the spin this reform
916      // removes.
917      None => {
918        self.drained = true;
919        Ok(Received::Ended)
920      }
921    }
922  }
923
924  pub(crate) fn send_eof_impl(&mut self) -> Result<Sent, ResampleError> {
925    self.eof = true;
926    Ok(Sent::Accepted)
927  }
928
929  /// Resets the resampler for another stream on the same two specs.
930  ///
931  /// The `swr` context is **rebuilt**, not drained. `swresample` has no
932  /// reset call, and draining it dry cannot be verified from outside: a
933  /// `swr_convert_frame` that makes no progress reports no error, so a
934  /// drain loop that gives up and a drain loop that finished are
935  /// indistinguishable — and a flush that returned `Ok` with the old
936  /// delay line still inside would let one stream's tail contaminate
937  /// the next. A fresh context is the only reset whose success is a
938  /// fact.
939  ///
940  /// The new context is built before the old one is dropped, so a
941  /// failure leaves the resampler exactly as it was: this call either
942  /// resets everything or changes nothing.
943  pub(crate) fn flush_impl(&mut self) -> Result<(), ResampleError> {
944    let ctx = open_context(
945      &self.source,
946      &self.target,
947      self.staged_source_layout,
948      self.staged_target_layout,
949    )?;
950    self.ctx = ctx;
951    self.ready.clear();
952    self.next_pts = None;
953    self.eof = false;
954    self.drained = false;
955    debug_assert_eq!(self.delay_impl(), 0, "a fresh swr context holds nothing");
956    Ok(())
957  }
958}
959
960macro_rules! resampler_lane_face {
961  ($($lane:ty),+ $(,)?) => { $(
962    impl CarrierResampler<$lane> {
963      /// Opens a resampler between two explicit specs. See
964      /// [`CarrierResampler::new_impl`] for the full contract.
965      pub fn new(
966        source: ResampleSpec,
967        target: ResampleSpec,
968        limits: FrameLimits,
969      ) -> Result<Self, ResampleError> {
970        Self::new_impl(source, target, limits)
971      }
972
973      /// The spec frames must arrive in.
974      pub const fn source(&self) -> &ResampleSpec {
975        self.source_impl()
976      }
977
978      /// The spec frames leave in.
979      pub const fn target(&self) -> &ResampleSpec {
980        self.target_impl()
981      }
982
983      /// The wrapped `swr` context.
984      pub const fn inner(&self) -> &resampling::Context {
985        self.inner_impl()
986      }
987
988      /// Samples still inside the delay line, counted at the output
989      /// rate.
990      pub fn delay(&self) -> i64 {
991        self.delay_impl()
992      }
993    }
994
995    impl AudioResampler for CarrierResampler<$lane> {
996      type Adapter = Ffmpeg;
997      type Buffer = <$lane as crate::FfmpegCarrier>::Buffer;
998      type Error = ResampleError;
999
1000      fn send_frame(&mut self, frame: &Frame<$lane>) -> Result<Sent, ResampleError> {
1001        self.send_frame_impl(frame)
1002      }
1003
1004      fn receive_frame(&mut self, dst: &mut Frame<$lane>) -> Result<Received, ResampleError> {
1005        self.receive_frame_impl(dst)
1006      }
1007
1008      fn send_eof(&mut self) -> Result<Sent, ResampleError> {
1009        self.send_eof_impl()
1010      }
1011
1012      fn flush(&mut self) -> Result<(), ResampleError> {
1013        self.flush_impl()
1014      }
1015    }
1016  )+ };
1017}
1018
1019resampler_lane_face!(crate::View, crate::Owned);
1020
1021/// Payload for [`ResampleError::SourceChanged`].
1022///
1023/// A frame arrived whose shape is not the source spec this resampler
1024/// was built with — the mid-stream refusal.
1025///
1026/// The face never silently reconfigures: doing so would resample the
1027/// two halves of a stream on different terms and hand back a single
1028/// unbroken timeline built out of them. Build a new resampler for the
1029/// new source spec.
1030#[derive(thiserror::Error, Debug, Clone)]
1031#[error(
1032  "source format changed mid-stream: expected {expected_rate} Hz {expected_format:?}, \
1033   got {found_rate} Hz {found_format:?}"
1034)]
1035pub struct SourceChanged {
1036  expected_rate: u32,
1037  expected_format: SampleFormat,
1038  found_rate: u32,
1039  found_format: SampleFormat,
1040}
1041
1042impl SourceChanged {
1043  /// Constructs a `SourceChanged` payload.
1044  #[inline]
1045  pub const fn new(
1046    expected_rate: u32,
1047    expected_format: SampleFormat,
1048    found_rate: u32,
1049    found_format: SampleFormat,
1050  ) -> Self {
1051    Self {
1052      expected_rate,
1053      expected_format,
1054      found_rate,
1055      found_format,
1056    }
1057  }
1058  /// Rate the resampler was built for.
1059  #[inline]
1060  pub const fn expected_rate(&self) -> u32 {
1061    self.expected_rate
1062  }
1063  /// Sample format the resampler was built for.
1064  #[inline]
1065  pub const fn expected_format(&self) -> SampleFormat {
1066    self.expected_format
1067  }
1068  /// Rate the offending frame carried.
1069  #[inline]
1070  pub const fn found_rate(&self) -> u32 {
1071    self.found_rate
1072  }
1073  /// Sample format the offending frame carried.
1074  #[inline]
1075  pub const fn found_format(&self) -> SampleFormat {
1076    self.found_format
1077  }
1078}
1079
1080/// Payload for [`ResampleError::PlaneCount`].
1081///
1082/// A frame's planes do not hold what its header claims — too few
1083/// planes for the format, or a plane shorter than its sample count
1084/// requires.
1085#[derive(thiserror::Error, Debug, Clone)]
1086#[error("frame plane geometry mismatch: expected {expected}, found {found}")]
1087pub struct PlaneCount {
1088  expected: usize,
1089  found: usize,
1090}
1091
1092impl PlaneCount {
1093  /// Constructs a `PlaneCount` payload.
1094  #[inline]
1095  pub const fn new(expected: usize, found: usize) -> Self {
1096    Self { expected, found }
1097  }
1098  /// What the format and sample count require.
1099  #[inline]
1100  pub const fn expected(&self) -> usize {
1101    self.expected
1102  }
1103  /// What the frame carries.
1104  #[inline]
1105  pub const fn found(&self) -> usize {
1106    self.found
1107  }
1108}
1109
1110/// Payload for [`ResampleError::OutputTooLarge`].
1111///
1112/// The converted frame would be larger than
1113/// [`FrameLimits::max_frame_bytes`] allows.
1114///
1115/// Distinct from [`SampleCount`], which is about a count no `AVFrame`
1116/// can express at all. This one is about a frame FFmpeg would happily
1117/// allocate and the caller has said it does not want: the amplification
1118/// a hostile source rate buys — one second at a declared 1 Hz becomes
1119/// gigabytes at 192 kHz — lands here.
1120#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq)]
1121#[error("a converted frame of {bytes} bytes exceeds the {limit}-byte ceiling")]
1122pub struct OutputTooLarge {
1123  bytes: usize,
1124  limit: usize,
1125}
1126
1127impl OutputTooLarge {
1128  /// Constructs an `OutputTooLarge` payload.
1129  #[cfg_attr(not(tarpaulin), inline(always))]
1130  pub const fn new(bytes: usize, limit: usize) -> Self {
1131    Self { bytes, limit }
1132  }
1133  /// The bytes the conversion would have produced.
1134  #[cfg_attr(not(tarpaulin), inline(always))]
1135  pub const fn bytes(&self) -> usize {
1136    self.bytes
1137  }
1138  /// The ceiling in force.
1139  #[cfg_attr(not(tarpaulin), inline(always))]
1140  pub const fn limit(&self) -> usize {
1141    self.limit
1142  }
1143}
1144
1145/// Payload for [`ResampleError::SampleCount`].
1146///
1147/// A sample count no frame can hold: one whose byte size overflows, or
1148/// one past the `c_int` `av_frame_get_buffer` takes.
1149#[derive(thiserror::Error, Debug, Clone)]
1150#[error("{requested} samples is not a frame size")]
1151pub struct SampleCount {
1152  requested: usize,
1153}
1154
1155impl SampleCount {
1156  /// Constructs a `SampleCount` payload.
1157  #[inline]
1158  pub const fn new(requested: usize) -> Self {
1159    Self { requested }
1160  }
1161  /// The count that was asked for.
1162  #[inline]
1163  pub const fn requested(&self) -> usize {
1164    self.requested
1165  }
1166}
1167
1168/// Payload for [`ResampleError::UnsupportedRate`].
1169///
1170/// One end of the conversion declares a sample rate `swr` cannot be
1171/// driven with — zero, or past `c_int`.
1172#[derive(thiserror::Error, Debug, Clone)]
1173#[error("the {end} rate {rate} is not a sample rate swr can use")]
1174pub struct UnsupportedRate {
1175  end: SpecEnd,
1176  rate: u32,
1177}
1178
1179impl UnsupportedRate {
1180  /// Constructs an `UnsupportedRate` payload.
1181  #[inline]
1182  pub const fn new(end: SpecEnd, rate: u32) -> Self {
1183    Self { end, rate }
1184  }
1185  /// Which end of the conversion.
1186  #[inline]
1187  pub const fn end(&self) -> SpecEnd {
1188    self.end
1189  }
1190  /// The rate that was declared.
1191  #[inline]
1192  pub const fn rate(&self) -> u32 {
1193    self.rate
1194  }
1195}
1196
1197/// Payload for [`ResampleError::UnsupportedFormat`].
1198///
1199/// One end of the conversion declares no sample format
1200/// (`AV_SAMPLE_FMT_NONE`) — the state a codec context is in before its
1201/// decoder opens.
1202#[derive(thiserror::Error, Debug, Clone)]
1203#[error("the {end} spec names no sample format")]
1204pub struct UnsupportedFormat {
1205  end: SpecEnd,
1206}
1207
1208impl UnsupportedFormat {
1209  /// Constructs an `UnsupportedFormat` payload.
1210  #[inline]
1211  pub const fn new(end: SpecEnd) -> Self {
1212    Self { end }
1213  }
1214  /// Which end of the conversion.
1215  #[inline]
1216  pub const fn end(&self) -> SpecEnd {
1217    self.end
1218  }
1219}
1220
1221/// Payload for [`ResampleError::UnsupportedLayout`].
1222///
1223/// One end of the conversion declares a channel layout this backend
1224/// will not carry.
1225///
1226/// Native and unspecified layouts are the two it does. A **custom** or
1227/// **ambisonic** `AVChannelLayout` owns a heap-allocated channel map,
1228/// and FFmpeg documents that such a layout must be copied with
1229/// `av_channel_layout_copy` rather than assigned — while
1230/// `ffmpeg_next::ChannelLayout` is a `Copy` wrapper with no destructor.
1231/// Every `AVFrame` this type stages or allocates receives the layout by
1232/// assignment, and `av_frame_free` runs `av_channel_layout_uninit` on
1233/// it: the first staged frame to be dropped would free a map the spec,
1234/// the decoder and every later frame still point at. Refusing at
1235/// construction is what keeps that use-after-free unreachable; a
1236/// resampler over those layouts is a separate design, not a silent
1237/// approximation.
1238#[derive(thiserror::Error, Debug, Clone)]
1239#[error("the {end} channel layout is not supported: order {order}, {channels} channels")]
1240pub struct UnsupportedLayout {
1241  end: SpecEnd,
1242  order: i32,
1243  channels: i32,
1244}
1245
1246impl UnsupportedLayout {
1247  /// Constructs an `UnsupportedLayout` payload.
1248  #[inline]
1249  pub const fn new(end: SpecEnd, order: i32, channels: i32) -> Self {
1250    Self {
1251      end,
1252      order,
1253      channels,
1254    }
1255  }
1256  /// Which end of the conversion.
1257  #[inline]
1258  pub const fn end(&self) -> SpecEnd {
1259    self.end
1260  }
1261  /// `AVChannelOrder` as the raw integer it is on the wire.
1262  #[inline]
1263  pub const fn order(&self) -> i32 {
1264    self.order
1265  }
1266  /// The channel count the layout declares.
1267  #[inline]
1268  pub const fn channels(&self) -> i32 {
1269    self.channels
1270  }
1271}
1272
1273/// Payload for [`ResampleError::TooManyPlanes`].
1274///
1275/// A planar spec with more channels than a decoded frame has plane
1276/// slots.
1277///
1278/// `mediadecode`'s `AudioFrame` carries a fixed eight planes
1279/// (`AV_NUM_DATA_POINTERS`); planar audio past that lives in
1280/// `AVFrame.extended_data[]`, which this crate does not plumb through.
1281/// As a **source** no valid frame could ever arrive; as a **target**
1282/// `swr` would produce one this crate cannot hand back — and it would
1283/// fail only after the input had been consumed, leaving a session that
1284/// cannot be retried. Both are refused at construction, where nothing
1285/// has happened yet.
1286#[derive(thiserror::Error, Debug, Clone)]
1287#[error("the {end} spec is planar with {channels} channels; a frame carries {limit} planes")]
1288pub struct TooManyPlanes {
1289  end: SpecEnd,
1290  channels: i32,
1291  limit: i32,
1292}
1293
1294impl TooManyPlanes {
1295  /// Constructs a `TooManyPlanes` payload.
1296  #[inline]
1297  pub const fn new(end: SpecEnd, channels: i32, limit: i32) -> Self {
1298    Self {
1299      end,
1300      channels,
1301      limit,
1302    }
1303  }
1304  /// Which end of the conversion.
1305  #[inline]
1306  pub const fn end(&self) -> SpecEnd {
1307    self.end
1308  }
1309  /// The channel count the layout declares.
1310  #[inline]
1311  pub const fn channels(&self) -> i32 {
1312    self.channels
1313  }
1314  /// Plane slots a frame has.
1315  #[inline]
1316  pub const fn limit(&self) -> i32 {
1317    self.limit
1318  }
1319}
1320
1321/// Payload for [`ResampleError::UnsupportedChannelCount`].
1322///
1323/// A spec declaring more channels than a frame's channel seat can
1324/// carry. `mediadecode`'s `AudioFrame` states its channel count in a
1325/// `u8`, so 255 is the ceiling in both directions.
1326///
1327/// This is the **packed** sibling of [`TooManyPlanes`], which only ever
1328/// caught planar specs: packed audio declares one plane whatever its
1329/// channel count, so a 256-channel packed spec sailed past that check
1330/// and had its count clipped on the way into the frame — a frame whose
1331/// bytes were computed from 256 channels while advertising 255. Refused
1332/// here, at the same choke point, for both ends.
1333#[derive(thiserror::Error, Debug, Clone)]
1334#[error("the {end} spec declares {channels} channels; a frame carries at most {limit}")]
1335pub struct UnsupportedChannelCount {
1336  end: SpecEnd,
1337  channels: i32,
1338  limit: i32,
1339}
1340
1341impl UnsupportedChannelCount {
1342  /// Constructs an `UnsupportedChannelCount` payload.
1343  #[inline]
1344  pub const fn new(end: SpecEnd, channels: i32, limit: i32) -> Self {
1345    Self {
1346      end,
1347      channels,
1348      limit,
1349    }
1350  }
1351  /// Which end of the conversion.
1352  #[inline]
1353  pub const fn end(&self) -> SpecEnd {
1354    self.end
1355  }
1356  /// The channel count the layout declares.
1357  #[inline]
1358  pub const fn channels(&self) -> i32 {
1359    self.channels
1360  }
1361  /// Channels a frame can state.
1362  #[inline]
1363  pub const fn limit(&self) -> i32 {
1364    self.limit
1365  }
1366}
1367
1368/// Payload for [`ResampleError::TimestampOutOfRange`].
1369///
1370/// A frame's timestamp does not land on the output timeline: it does
1371/// not survive the rescale as an `i64`, or it is `AV_NOPTS_VALUE`,
1372/// which is a sentinel rather than a time.
1373///
1374/// Raised before anything is staged, so a refused frame leaves the
1375/// resampler exactly as it was.
1376#[derive(thiserror::Error, Debug, Clone)]
1377#[error("the frame timestamp {pts} does not land on the output timeline")]
1378pub struct TimestampOutOfRange {
1379  pts: i64,
1380}
1381
1382impl TimestampOutOfRange {
1383  /// Constructs a `TimestampOutOfRange` payload.
1384  #[inline]
1385  pub const fn new(pts: i64) -> Self {
1386    Self { pts }
1387  }
1388  /// The timestamp the frame carried, in its own timebase.
1389  #[inline]
1390  pub const fn pts(&self) -> i64 {
1391    self.pts
1392  }
1393}
1394
1395/// Payload for [`ResampleError::ChannelDropped`].
1396///
1397/// The conversion between these two layouts would silently drop a
1398/// source channel: FFmpeg's own mixing matrix routes it to no output.
1399///
1400/// `swr` mixes the channel positions its rematrix table knows and
1401/// processes the rest of the input as though it were absent — a log
1402/// line at most. Measured against FFmpeg 9, packed 22.2 → mono loses
1403/// fifteen of twenty-four channels and `cube` → stereo loses two of
1404/// eight, so this is not a matter of channel count. Installing an
1405/// explicit mix matrix is how such a conversion would be accepted
1406/// deliberately; until this crate has a seat for one, the pair is
1407/// refused.
1408#[derive(thiserror::Error, Debug, Clone)]
1409#[error(
1410  "converting {source_channels} channels to {target_channels} would drop source channel \
1411   {channel}: FFmpeg's mixing matrix routes it to no output"
1412)]
1413pub struct ChannelDropped {
1414  source_channels: i32,
1415  target_channels: i32,
1416  channel: i32,
1417}
1418
1419impl ChannelDropped {
1420  /// Constructs a `ChannelDropped` payload.
1421  #[inline]
1422  pub const fn new(source_channels: i32, target_channels: i32, channel: i32) -> Self {
1423    Self {
1424      source_channels,
1425      target_channels,
1426      channel,
1427    }
1428  }
1429  /// Channels the source layout declares.
1430  #[inline]
1431  pub const fn source_channels(&self) -> i32 {
1432    self.source_channels
1433  }
1434  /// Channels the target layout declares.
1435  #[inline]
1436  pub const fn target_channels(&self) -> i32 {
1437    self.target_channels
1438  }
1439  /// The first source channel that reaches no output channel.
1440  #[inline]
1441  pub const fn channel(&self) -> i32 {
1442    self.channel
1443  }
1444}
1445
1446/// Payload for [`ResampleError::RematrixUnsupported`].
1447///
1448/// FFmpeg will not build a mixing matrix between these two layouts at
1449/// all.
1450#[derive(thiserror::Error, Debug, Clone)]
1451#[error("FFmpeg builds no mixing matrix from {source_channels} channels to {target_channels}")]
1452pub struct RematrixUnsupported {
1453  source_channels: i32,
1454  target_channels: i32,
1455}
1456
1457impl RematrixUnsupported {
1458  /// Constructs a `RematrixUnsupported` payload.
1459  #[inline]
1460  pub const fn new(source_channels: i32, target_channels: i32) -> Self {
1461    Self {
1462      source_channels,
1463      target_channels,
1464    }
1465  }
1466  /// Channels the source layout declares.
1467  #[inline]
1468  pub const fn source_channels(&self) -> i32 {
1469    self.source_channels
1470  }
1471  /// Channels the target layout declares.
1472  #[inline]
1473  pub const fn target_channels(&self) -> i32 {
1474    self.target_channels
1475  }
1476}
1477
1478/// Payload for [`ResampleError::TimestampOverflow`].
1479///
1480/// The output timeline would leave `i64`. Counted timestamps are exact
1481/// or they are nothing, so this is named rather than saturated.
1482#[derive(thiserror::Error, Debug, Clone)]
1483#[error("the output timeline overflows: {pts} + {samples} samples")]
1484pub struct TimestampOverflow {
1485  pts: i64,
1486  samples: i64,
1487}
1488
1489impl TimestampOverflow {
1490  /// Constructs a `TimestampOverflow` payload.
1491  #[inline]
1492  pub const fn new(pts: i64, samples: i64) -> Self {
1493    Self { pts, samples }
1494  }
1495  /// Where the timeline stood.
1496  #[inline]
1497  pub const fn pts(&self) -> i64 {
1498    self.pts
1499  }
1500  /// How many samples were produced.
1501  #[inline]
1502  pub const fn samples(&self) -> i64 {
1503    self.samples
1504  }
1505}
1506
1507/// Payload for [`ResampleError::OutputBuffer`].
1508///
1509/// A reference to one of the output frame's planes could not be taken.
1510///
1511/// Raised while preparing the conversion, never after it: that is the
1512/// point of preparing.
1513#[derive(thiserror::Error, Debug, Clone)]
1514#[error("the output frame's plane {plane} could not be referenced")]
1515pub struct OutputBuffer {
1516  plane: usize,
1517}
1518
1519impl OutputBuffer {
1520  /// Constructs an `OutputBuffer` payload.
1521  #[inline]
1522  pub const fn new(plane: usize) -> Self {
1523    Self { plane }
1524  }
1525  /// Which plane slot.
1526  #[inline]
1527  pub const fn plane(&self) -> usize {
1528    self.plane
1529  }
1530}
1531
1532/// Errors from [`FfmpegResampler`] — **faults and the two send-side
1533/// refusals** ([`Self::SourceChanged`], [`Self::AfterEof`]).
1534///
1535/// `Again` used to be here and meant two opposite things: pre-EOF
1536/// "nothing ready, send more" and post-EOF "the tail is exhausted".
1537/// A caller polling the seam could tell them apart only by remembering
1538/// whether it had itself called `send_eof`; one that did not spun
1539/// forever. Both are [`Received`] states now, and they are distinct.
1540///
1541/// **Open fault taxonomy, so it is `#[non_exhaustive]`.** New ways to
1542/// fail are discovered — a backend, a ceiling, a corruption a codec
1543/// learns to report — and a consumer that meets one it has never heard
1544/// of should take its generic-fault path. That is exactly what the
1545/// wildcard arm this attribute forces is for. The two status
1546/// vocabularies opposite it,
1547/// [`Sent`](mediadecode::Sent) and [`Received`](mediadecode::Received),
1548/// are exhaustive for the mirror-image reason: their arms are the
1549/// substrate's fixed state set, and there the wildcard would be dead
1550/// weight hiding a state a consumer forgot.
1551#[derive(thiserror::Error, Debug, Clone, IsVariant, Unwrap, TryUnwrap)]
1552#[unwrap(ref, ref_mut)]
1553#[try_unwrap(ref, ref_mut)]
1554#[non_exhaustive]
1555pub enum ResampleError {
1556  /// The conversion would produce a frame larger than the ceiling
1557  /// allows. Refused **before** the output frame is allocated.
1558  #[error(transparent)]
1559  OutputTooLarge(#[from] OutputTooLarge),
1560
1561  /// A frame arrived whose shape is not the source spec this resampler
1562  /// was built with — the mid-stream refusal.
1563  #[error(transparent)]
1564  SourceChanged(#[from] SourceChanged),
1565
1566  /// [`send_frame`](AudioResampler::send_frame) was called after
1567  /// [`send_eof`](AudioResampler::send_eof). Call
1568  /// [`flush`](AudioResampler::flush) first to reuse the resampler for
1569  /// another stream.
1570  #[error("send_frame after send_eof; flush() first to start another stream")]
1571  AfterEof,
1572
1573  /// A frame's planes do not hold what its header claims — too few
1574  /// planes for the format, or a plane shorter than its sample count
1575  /// requires.
1576  #[error(transparent)]
1577  PlaneCount(#[from] PlaneCount),
1578
1579  /// A sample count no frame can hold: one whose byte size overflows,
1580  /// or one past the `c_int` `av_frame_get_buffer` takes.
1581  #[error(transparent)]
1582  SampleCount(#[from] SampleCount),
1583
1584  /// One end of the conversion declares a sample rate `swr` cannot be
1585  /// driven with — zero, or past `c_int`.
1586  #[error(transparent)]
1587  UnsupportedRate(#[from] UnsupportedRate),
1588
1589  /// One end of the conversion declares no sample format
1590  /// (`AV_SAMPLE_FMT_NONE`) — the state a codec context is in before
1591  /// its decoder opens.
1592  #[error(transparent)]
1593  UnsupportedFormat(#[from] UnsupportedFormat),
1594
1595  /// One end of the conversion declares a channel layout this backend
1596  /// will not carry.
1597  #[error(transparent)]
1598  UnsupportedLayout(#[from] UnsupportedLayout),
1599
1600  /// A planar spec with more channels than a decoded frame has plane
1601  /// slots.
1602  #[error(transparent)]
1603  TooManyPlanes(#[from] TooManyPlanes),
1604
1605  /// A spec with more channels than a frame's channel seat can state.
1606  #[error(transparent)]
1607  UnsupportedChannelCount(#[from] UnsupportedChannelCount),
1608
1609  /// A frame's timestamp does not land on the output timeline.
1610  #[error(transparent)]
1611  TimestampOutOfRange(#[from] TimestampOutOfRange),
1612
1613  /// The conversion between these two layouts would silently drop a
1614  /// source channel.
1615  #[error(transparent)]
1616  ChannelDropped(#[from] ChannelDropped),
1617
1618  /// FFmpeg will not build a mixing matrix between these two layouts at
1619  /// all.
1620  #[error(transparent)]
1621  RematrixUnsupported(#[from] RematrixUnsupported),
1622
1623  /// The output timeline would leave `i64`. Counted timestamps are
1624  /// exact or they are nothing, so this is named rather than saturated.
1625  #[error(transparent)]
1626  TimestampOverflow(#[from] TimestampOverflow),
1627
1628  /// The wrapped `swresample` call reported an error.
1629  #[error(transparent)]
1630  Resample(#[from] Error),
1631
1632  /// A reference to one of the output frame's planes could not be
1633  /// taken.
1634  #[error(transparent)]
1635  OutputBuffer(#[from] OutputBuffer),
1636
1637  /// The queue of converted frames could not be grown to hold one more.
1638  #[error("out of memory reserving room for a converted frame")]
1639  QueueAlloc,
1640}
1641
1642/// Which end of a conversion a refusal is about.
1643#[derive(Copy, Clone, Debug, PartialEq, Eq, IsVariant)]
1644pub enum SpecEnd {
1645  /// The spec frames must arrive in.
1646  Source,
1647  /// The spec frames leave in.
1648  Target,
1649}
1650
1651impl core::fmt::Display for SpecEnd {
1652  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1653    f.write_str(match self {
1654      Self::Source => "source",
1655      Self::Target => "target",
1656    })
1657  }
1658}
1659
1660/// Plane slots a `mediadecode::frame::AudioFrame` has — the fixed array
1661/// matching `AV_NUM_DATA_POINTERS`. Planar audio past this many
1662/// channels lives in `AVFrame.extended_data[]` / `extended_buf[]`,
1663/// which this crate does not plumb through: `convert` refuses such a
1664/// frame and `AudioFrame::new` will not build one.
1665const MAX_AUDIO_PLANES: i32 = 8;
1666
1667/// Channels a `mediadecode::frame::AudioFrame` can state — its channel
1668/// seat is a `u8`. A spec past this is refused rather than clipped.
1669const MAX_FRAME_CHANNELS: i32 = u8::MAX as i32;
1670
1671/// Refuses a spec `swr` cannot be driven with, or whose channel layout
1672/// cannot be carried by value — see [`ResampleError::UnsupportedLayout`]
1673/// for that one, which is the whole reason this check exists at the
1674/// choke point rather than in the `const` constructor.
1675fn check_spec(spec: &ResampleSpec, end: SpecEnd) -> Result<(), ResampleError> {
1676  if spec.rate == 0 || spec.rate > i32::MAX as u32 {
1677    return Err(ResampleError::UnsupportedRate(UnsupportedRate::new(
1678      end, spec.rate,
1679    )));
1680  }
1681  if spec.format == Sample::None {
1682    return Err(ResampleError::UnsupportedFormat(UnsupportedFormat::new(
1683      end,
1684    )));
1685  }
1686  let order = layout_order(&spec.layout);
1687  let channels = spec.layout.channels();
1688  let carried = order == AVChannelOrder::AV_CHANNEL_ORDER_NATIVE as i32
1689    || order == AVChannelOrder::AV_CHANNEL_ORDER_UNSPEC as i32;
1690  if !carried || channels <= 0 {
1691    return Err(ResampleError::UnsupportedLayout(UnsupportedLayout::new(
1692      end, order, channels,
1693    )));
1694  }
1695  // A planar spec with more channels than the frame model has plane
1696  // slots is a resampler that cannot work in either direction, and
1697  // saying so here is the difference between a refusal at construction
1698  // and a refusal on every frame — the target one arriving *after*
1699  // `swr` has already consumed the input, which is not a state a caller
1700  // can retry from.
1701  if spec.format.is_planar() && channels > MAX_AUDIO_PLANES {
1702    return Err(ResampleError::TooManyPlanes(TooManyPlanes::new(
1703      end,
1704      channels,
1705      MAX_AUDIO_PLANES,
1706    )));
1707  }
1708  // And the packed sibling, which the plane check above cannot see: a
1709  // packed spec declares one plane at any channel count, so it reached
1710  // the frame with its count clipped to 255 instead of refused. That is
1711  // the same silent truncation the decode path was carrying, on the
1712  // other audio road — closed here, at the same choke point, so that
1713  // every channel count downstream is exact by construction.
1714  if channels > MAX_FRAME_CHANNELS {
1715    return Err(ResampleError::UnsupportedChannelCount(
1716      UnsupportedChannelCount::new(end, channels, MAX_FRAME_CHANNELS),
1717    ));
1718  }
1719  Ok(())
1720}
1721
1722/// A layout's `AVChannelOrder` as the integer it is on the wire.
1723///
1724/// Read raw rather than matched as an `AVChannelOrder`, the discipline
1725/// this crate keeps everywhere it touches a bindgen enum: a value
1726/// outside this build's discriminant set would be undefined behaviour
1727/// the moment it existed as one.
1728fn layout_order(layout: &ChannelLayout) -> i32 {
1729  // SAFETY: `layout` is a live `ChannelLayout` for the duration of this
1730  // call; `addr_of!` reaches its `order` field without forming a
1731  // reference to the enum.
1732  unsafe { read_unaligned(addr_of!(layout.0.order).cast::<i32>()) }
1733}
1734
1735/// FFmpeg's `SWR_CH_MAX`: the square its own matrix builder writes,
1736/// whatever the two layouts' channel counts are.
1737///
1738/// Not a convenience. `swr_build_matrix2` copies its internal
1739/// `[SWR_CH_MAX][SWR_CH_MAX]` block out at the caller's stride, so a
1740/// buffer sized to the actual channel counts is written far past its
1741/// end — measured, and the measurement is a killed process.
1742const SWR_CH_MAX: usize = 64;
1743
1744/// Refuses an **effective pair** whose rematrixing would silently drop
1745/// input channels.
1746///
1747/// Takes the layouts `swr` is configured with, not the ones the caller
1748/// declared. The two differ exactly where it matters: an unspecified
1749/// layout is resolved to FFmpeg's default for its channel count before
1750/// the context is opened, and twenty-four unspecified channels resolve
1751/// to 22.2 — so the declared pair says "unspecified, nothing to
1752/// rematrix" while the conversion that runs is the lossy one.
1753///
1754/// This is the second half of the crate's two-layout bookkeeping, and
1755/// the halves answer different questions. The **declared** layout is
1756/// what decoded frames carry (a WAV without a channel mask hands out
1757/// unspecified frames forever) and stays the yardstick for the
1758/// mid-stream refusal: *is this frame the stream I was built for?* The
1759/// **effective** layout is what `swr` and every staged `AVFrame` use,
1760/// and it is the one judged here: *what will `swr` actually do?*
1761///
1762/// Each end can be perfectly valid on its own and the conversion
1763/// between them still lose whole channels: `swr` mixes only the channel
1764/// positions its rematrix table knows, and quietly processes the rest
1765/// of the input as though it were not there. Measured against the
1766/// linked FFmpeg 9 with a tone isolated in each source channel: packed
1767/// 22.2 → mono drops fifteen of twenty-four (`swr` says as much in a
1768/// log line and converts anyway), `cube` → stereo drops two of *eight*
1769/// — so a channel-count threshold is both too strict and too loose to
1770/// be the rule.
1771///
1772/// The rule is asked of FFmpeg instead: build the mixing matrix its own
1773/// builder would use, and refuse when any input channel reaches no
1774/// output at all. `lfe_mix_level` is deliberately non-zero, so the
1775/// question is "can this channel reach the output" rather than "does
1776/// FFmpeg's default downmix policy include it" — the default leaves LFE
1777/// out of a downmix on purpose, and refusing an everyday 5.1 → stereo
1778/// over that would be absurd. The predicate matched the tone sweep
1779/// exactly on every pair measured.
1780///
1781/// A pair `swr` cannot matrix at all is refused too. Accepting these
1782/// deliberately is a *mix matrix* seat on the spec — a real design, not
1783/// something to mint in passing; until it exists, refusal is the honest
1784/// answer.
1785fn check_pair(source: &ChannelLayout, target: &ChannelLayout) -> Result<(), ResampleError> {
1786  let native = AVChannelOrder::AV_CHANNEL_ORDER_NATIVE as i32;
1787  // A layout still unspecified *after* resolution — a channel count
1788  // FFmpeg has no default for — is mapped positionally by `swr` with no
1789  // rematrixing at all, and identical layouts need no matrix: neither
1790  // can drop a channel, and neither is what the builder describes.
1791  if layout_order(source) != native || layout_order(target) != native || source == target {
1792    return Ok(());
1793  }
1794  let source_channels = source.channels();
1795  let target_channels = target.channels();
1796
1797  let mut matrix = vec![0f64; SWR_CH_MAX * SWR_CH_MAX];
1798  // SAFETY: both layouts are live for the call; `matrix` is the full
1799  // `SWR_CH_MAX` square the builder writes, passed with the matching
1800  // stride; the encoding is a compile-time constant of this build; and
1801  // a null log context is documented as allowed.
1802  let rc = unsafe {
1803    swr_build_matrix2(
1804      &source.0,
1805      &target.0,
1806      core::f64::consts::FRAC_1_SQRT_2,
1807      core::f64::consts::FRAC_1_SQRT_2,
1808      1.0,
1809      1.0,
1810      1.0,
1811      matrix.as_mut_ptr(),
1812      SWR_CH_MAX as isize,
1813      AVMatrixEncoding::AV_MATRIX_ENCODING_NONE,
1814      core::ptr::null_mut(),
1815    )
1816  };
1817  if rc < 0 {
1818    return Err(ResampleError::RematrixUnsupported(
1819      RematrixUnsupported::new(source_channels, target_channels),
1820    ));
1821  }
1822  for channel in 0..source_channels.min(SWR_CH_MAX as i32) {
1823    let index = channel as usize;
1824    if (0..target_channels.min(SWR_CH_MAX as i32) as usize)
1825      .all(|out| matrix[index + SWR_CH_MAX * out] == 0.0)
1826    {
1827      return Err(ResampleError::ChannelDropped(ChannelDropped::new(
1828        source_channels,
1829        target_channels,
1830        channel,
1831      )));
1832    }
1833  }
1834  Ok(())
1835}
1836
1837/// Opens a `swr` context for the two specs. Shared by
1838/// [`FfmpegResampler::new`] and the rebuild
1839/// [`AudioResampler::flush`] performs.
1840fn open_context(
1841  source: &ResampleSpec,
1842  target: &ResampleSpec,
1843  staged_source_layout: ChannelLayout,
1844  staged_target_layout: ChannelLayout,
1845) -> Result<resampling::Context, ResampleError> {
1846  resampling::Context::get(
1847    source.format,
1848    staged_source_layout,
1849    source.rate,
1850    target.format,
1851    staged_target_layout,
1852    target.rate,
1853  )
1854  .map_err(|e| ResampleError::Resample(Error::Ffmpeg(e)))
1855}
1856
1857/// Allocates an audio `AVFrame`, checking every step the dependency's
1858/// own `frame::Audio::new` does not.
1859///
1860/// `ffmpeg_next`'s constructor dereferences `av_frame_alloc`'s result
1861/// without a null check and discards `av_frame_get_buffer`'s return
1862/// value, so an allocation failure there yields a frame whose planes
1863/// are not backed — which is then handed to FFI. Both are checked here;
1864/// on failure the caller gets a named error and no frame at all. The
1865/// null check is the crate's existing one
1866/// ([`crate::frame::alloc_av_audio_frame`], which the decoders already
1867/// allocate through), so there is one answer to `av_frame_alloc`
1868/// returning null rather than two.
1869fn new_audio_frame(
1870  format: Sample,
1871  samples: usize,
1872  rate: u32,
1873  layout: ChannelLayout,
1874) -> Result<frame::Audio, ResampleError> {
1875  if samples == 0 || samples > i32::MAX as usize {
1876    return Err(ResampleError::SampleCount(SampleCount::new(samples)));
1877  }
1878  let mut out = crate::frame::alloc_av_audio_frame()?;
1879  out.set_format(format);
1880  out.set_samples(samples);
1881  // The layout is assigned by value, which is sound only because
1882  // `check_spec` refused every layout that owns a heap channel map.
1883  out.set_channel_layout(layout);
1884  out.set_rate(rate);
1885  // SAFETY: `out` is a live `AVFrame` whose format, sample count and
1886  // layout were just set; `av_frame_get_buffer` allocates its planes
1887  // and reports failure in its return value, which is checked.
1888  let rc = unsafe { av_frame_get_buffer(out.as_mut_ptr(), 0) };
1889  if rc < 0 {
1890    return Err(ResampleError::Resample(Error::Ffmpeg(
1891      ffmpeg_next::Error::from(rc),
1892    )));
1893  }
1894  Ok(out)
1895}
1896
1897/// Bytes one plane holds for `samples` samples of `format`, or `None`
1898/// when that product does not fit a `usize`. Packed formats keep every
1899/// channel in the single plane; planar formats give each channel its
1900/// own.
1901fn plane_bytes(format: Sample, samples: usize, channels: i32) -> Option<usize> {
1902  // Refused, not floored. `check_spec` already proves the count is in
1903  // `1..=MAX_FRAME_CHANNELS` before any resampler exists, so this is a
1904  // restatement of an invariant rather than a live branch — but it is
1905  // stated as a refusal because the alternative was a substituted `1`,
1906  // which invents a channel the caller never declared and makes the
1907  // byte product disagree with the frame it sizes.
1908  let channels = usize::try_from(channels).ok().filter(|count| *count > 0)?;
1909  let bytes = samples.checked_mul(format.bytes())?;
1910  if format.is_planar() {
1911    Some(bytes)
1912  } else {
1913    bytes.checked_mul(channels)
1914  }
1915}
1916
1917/// Builds a native-order [`ChannelLayout`] from a channel bitmask,
1918/// without ever forming an `AVChannelLayout` out of foreign memory:
1919/// the struct starts zeroed (`AV_CHANNEL_ORDER_UNSPEC` is `0`, a valid
1920/// discriminant) and FFmpeg fills it.
1921fn layout_from_mask(mask: u64) -> ChannelLayout {
1922  // SAFETY: a zeroed `AVChannelLayout` is a valid value — its `order`
1923  // field reads as `AV_CHANNEL_ORDER_UNSPEC`, the zero discriminant —
1924  // and `av_channel_layout_from_mask` overwrites it wholesale.
1925  unsafe {
1926    let mut layout = std::mem::zeroed();
1927    if av_channel_layout_from_mask(&mut layout, mask) < 0 {
1928      return ChannelLayout::default(mask.count_ones() as i32);
1929    }
1930    ChannelLayout(layout)
1931  }
1932}
1933
1934/// The layout `swr` will actually be configured with.
1935///
1936/// `swr_init` replaces an unspecified input or output layout with
1937/// FFmpeg's default for that channel count, and from then on compares
1938/// every frame handed to it against *that* layout — a staged frame
1939/// still carrying the unspecified one is refused with
1940/// `AVERROR_INPUT_CHANGED`. Applying the same rule here, once, keeps
1941/// the frames this type builds in step with the context it built.
1942///
1943/// The declared layout is kept separately and is what the mid-stream
1944/// check compares against, because it is what decoded frames really
1945/// carry: a WAV without a channel mask hands out unspecified frames
1946/// forever, whatever `swr` decided internally.
1947fn initialized_layout(layout: ChannelLayout) -> ChannelLayout {
1948  if layout.is_empty() {
1949    ChannelLayout::default(layout.channels())
1950  } else {
1951    layout
1952  }
1953}
1954
1955/// Reads an `AVChannelLayout` out of FFmpeg memory into a layout this
1956/// spec can own, or `None` for one it does not represent.
1957///
1958/// The `order` field is read as the integer it is on the wire: an
1959/// out-of-range value would be undefined behaviour the instant it
1960/// existed as an `AVChannelOrder`, which is the hazard this crate
1961/// keeps out everywhere it touches a bindgen enum.
1962///
1963/// A **custom** or **ambisonic** layout returns `None`. Both keep a
1964/// heap-allocated channel map inside the layout, and `ChannelLayout` is
1965/// a plain `Copy` wrapper with no destructor: owning one here would
1966/// either alias a map the decoder still frees or leak the copy. A
1967/// resampler over one of those layouts is a separate design, not a
1968/// silent approximation.
1969///
1970/// # Safety
1971///
1972/// `ptr` must be a live `*const AVChannelLayout` for the duration of
1973/// this call.
1974unsafe fn layout_from_raw(ptr: *const ffmpeg_next::ffi::AVChannelLayout) -> Option<ChannelLayout> {
1975  let order = unsafe { read_unaligned(addr_of!((*ptr).order).cast::<i32>()) };
1976  let channels = unsafe { (*ptr).nb_channels };
1977  if channels <= 0 {
1978    return None;
1979  }
1980  if order == AVChannelOrder::AV_CHANNEL_ORDER_NATIVE as i32 {
1981    // SAFETY: `u.mask` is the union's variant for NATIVE, and the
1982    // order was checked against our own constant before the read.
1983    let mask = unsafe { (*ptr).u.mask };
1984    if mask != 0 {
1985      return Some(layout_from_mask(mask));
1986    }
1987    // Native in name with no channels named: unspecified in substance.
1988    return Some(ResampleSpec::unspecified_layout(channels));
1989  }
1990  if order == AVChannelOrder::AV_CHANNEL_ORDER_UNSPEC as i32 {
1991    return Some(ResampleSpec::unspecified_layout(channels));
1992  }
1993  None
1994}
1995
1996/// Compile-time assurance that `SampleFormat`'s round trip through
1997/// FFmpeg's vocabulary is the identity on the closed set. Both
1998/// directions are hand-written tables, and a table that disagreed with
1999/// its inverse would silently mislabel every sample.
2000const _: () = {
2001  assert!(
2002    SampleFormat::from_raw(AVSampleFormat::AV_SAMPLE_FMT_NONE as i32)
2003      .to_ffmpeg()
2004      .is_none()
2005  );
2006};
2007
2008#[cfg(test)]
2009mod tests {
2010  use super::*;
2011
2012  use mediadecode::resampler::AudioResampler;
2013
2014  // These exercise the conversion arithmetic — rates, layouts, the
2015  // counted timeline, the byte ceiling — which is lane-independent, so
2016  // they run on the owned lane exactly as they did before the second
2017  // lane existed. The view lane's own road through this type (reserve
2018  // before `swr`, commit after) is proved in `tests/view_carriers.rs`,
2019  // where a produced plane can be shown to point into the output
2020  // frame's buffer.
2021  use crate::{FfmpegBytes, FfmpegOwnedResampler as FfmpegResampler};
2022
2023  type Frame = super::Frame<crate::Owned>;
2024
2025  /// A 48 kHz packed-s16 stereo frame of silence, with the plane its
2026  /// header claims.
2027  fn stereo_frame(samples: u32) -> Frame {
2028    let plane = FfmpegBytes::copy_from_slice(&vec![0u8; samples as usize * 2 * 2]);
2029    let planes = std::array::from_fn(|index| {
2030      Plane::new(
2031        if index == 0 {
2032          plane.clone()
2033        } else {
2034          FfmpegBytes::empty()
2035        },
2036        0,
2037      )
2038    });
2039    AudioFrame::new(
2040      48_000,
2041      samples,
2042      2,
2043      SampleFormat::S16,
2044      crate::channel_layout::channel_layout_description_from_ffmpeg(&ChannelLayout::STEREO),
2045      planes,
2046      1,
2047      AudioFrameExtra::default(),
2048    )
2049    .with_pts(Some(Timestamp::new(
2050      0,
2051      Timebase::new(1, std::num::NonZeroI32::new(48_000).expect("a real rate")),
2052    )))
2053  }
2054
2055  /// Takes every frame that is ready *right now* and stops — the
2056  /// pre-EOF half of a drain, written as the exhaustive match the face
2057  /// now requires. A `.is_ok()` loop here would never end: "needs
2058  /// input" is a success.
2059  fn drain_ready(resampler: &mut FfmpegResampler, dst: &mut Frame) {
2060    loop {
2061      match resampler.receive_frame(dst).expect("a fault-free drain") {
2062        Received::Frame => {}
2063        Received::NeedsInput | Received::Ended => return,
2064      }
2065    }
2066  }
2067
2068  fn stereo_to_mono() -> FfmpegResampler {
2069    FfmpegResampler::new(
2070      ResampleSpec::new(
2071        48_000,
2072        Sample::I16(ffmpeg_next::format::sample::Type::Packed),
2073        ChannelLayout::STEREO,
2074      ),
2075      ResampleSpec::new(
2076        16_000,
2077        Sample::I16(ffmpeg_next::format::sample::Type::Packed),
2078        ChannelLayout::MONO,
2079      ),
2080      FrameLimits::default(),
2081    )
2082    .expect("open resampler")
2083  }
2084
2085  #[test]
2086  fn resample_error_carries_the_derived_accessor_face() {
2087    // `IsVariant` / `Unwrap` / `TryUnwrap` — one arm per derive family,
2088    // mirroring the mediadecode-side proof for this crate's own
2089    // newly-wired `derive_more` dependency.
2090    let err = ResampleError::OutputBuffer(OutputBuffer::new(2));
2091    assert!(err.is_output_buffer());
2092    assert!(!err.is_queue_alloc());
2093    assert_eq!(err.unwrap_output_buffer_ref().plane(), 2);
2094    assert!(err.try_unwrap_queue_alloc().is_err());
2095  }
2096
2097  #[test]
2098  fn an_allocation_fault_while_sending_leaves_the_session_untouched() {
2099    // The class this design exists to end: a failure on the far side of
2100    // `swr_convert_frame` leaves a session no caller can act on —
2101    // retrying feeds the same samples twice, continuing loses them, and
2102    // the delay line has moved either way. Every allocation the
2103    // conversion needs is taken before `swr` runs, so an allocator that
2104    // refuses everything can only produce an error that cost nothing.
2105    crate::fault_subprocess::in_subprocess(
2106      "resampler::tests::an_allocation_fault_while_sending_leaves_the_session_untouched",
2107      || {
2108        let mut resampler = stereo_to_mono();
2109        let frame = stereo_frame(4_800);
2110        let mut dst = crate::boundary::empty_owned_audio_frame();
2111        crate::accepted(resampler.send_frame(&frame), "a first frame");
2112        drain_ready(&mut resampler, &mut dst);
2113        let delay = resampler.delay();
2114        assert!(delay > 0, "the filter has to be holding something");
2115
2116        crate::fault_subprocess::cap_ffmpeg_allocations(1);
2117        let refused = resampler.send_frame(&frame);
2118        crate::fault_subprocess::uncap_ffmpeg_allocations();
2119
2120        assert!(
2121          refused.is_err(),
2122          "an allocator that refuses everything must not look like success",
2123        );
2124        assert_eq!(
2125          resampler.delay(),
2126          delay,
2127          "the frame went into the filter anyway",
2128        );
2129        assert!(
2130          matches!(resampler.receive_frame(&mut dst), Ok(Received::NeedsInput)),
2131          "a failed send left output ready",
2132        );
2133
2134        // And the session is still a session: the same frame converts.
2135        crate::accepted(resampler.send_frame(&frame), "the failure cost nothing");
2136        assert!(matches!(
2137          resampler.receive_frame(&mut dst),
2138          Ok(Received::Frame)
2139        ));
2140      },
2141    );
2142  }
2143
2144  #[test]
2145  fn an_allocation_fault_while_draining_keeps_the_tail() {
2146    // The same property one call along, where the samples at risk are
2147    // the ones already inside the filter: a drain that fails must leave
2148    // the tail where it was, not turn it into an error.
2149    crate::fault_subprocess::in_subprocess(
2150      "resampler::tests::an_allocation_fault_while_draining_keeps_the_tail",
2151      || {
2152        let mut resampler = stereo_to_mono();
2153        let frame = stereo_frame(4_800);
2154        let mut dst = crate::boundary::empty_owned_audio_frame();
2155        for _ in 0..3 {
2156          crate::accepted(resampler.send_frame(&frame), "send_frame");
2157          drain_ready(&mut resampler, &mut dst);
2158        }
2159        crate::accepted(resampler.send_eof(), "eof");
2160        let tail = resampler.delay();
2161        assert!(tail > 0, "there has to be a tail to lose");
2162
2163        crate::fault_subprocess::cap_ffmpeg_allocations(1);
2164        let refused = resampler.receive_frame(&mut dst);
2165        crate::fault_subprocess::uncap_ffmpeg_allocations();
2166
2167        let refused = refused.expect_err("the drain cannot have succeeded");
2168        // No arm of this enum can say "send me more input" any more —
2169        // that answer left the error type — so an allocation failure has
2170        // nowhere to be mistaken for one.
2171        assert!(
2172          matches!(
2173            refused,
2174            ResampleError::Resample(_) | ResampleError::OutputBuffer(_)
2175          ),
2176          "an allocation failure surfaced as something else: {refused:?}",
2177        );
2178        assert_eq!(
2179          resampler.delay(),
2180          tail,
2181          "the tail was consumed by a drain that failed",
2182        );
2183
2184        // And it is still drainable, which is the whole point.
2185        assert_eq!(
2186          resampler
2187            .receive_frame(&mut dst)
2188            .expect("the tail survived the failure"),
2189          Received::Frame,
2190        );
2191      },
2192    );
2193  }
2194
2195  #[test]
2196  fn the_sample_format_table_round_trips() {
2197    for format in [
2198      SampleFormat::U8,
2199      SampleFormat::S16,
2200      SampleFormat::S32,
2201      SampleFormat::S64,
2202      SampleFormat::FLT,
2203      SampleFormat::DBL,
2204      SampleFormat::U8P,
2205      SampleFormat::S16P,
2206      SampleFormat::S32P,
2207      SampleFormat::S64P,
2208      SampleFormat::FLTP,
2209      SampleFormat::DBLP,
2210    ] {
2211      let ffmpeg = format.to_ffmpeg().expect("a named format");
2212      assert_eq!(
2213        SampleFormat::from_ffmpeg(ffmpeg),
2214        format,
2215        "{format:?} does not survive the round trip",
2216      );
2217      assert_eq!(ffmpeg.is_planar(), format.is_planar());
2218    }
2219    assert!(SampleFormat::NONE.to_ffmpeg().is_none());
2220    assert!(SampleFormat::from_raw(9999).to_ffmpeg().is_none());
2221  }
2222
2223  #[test]
2224  fn a_mask_rebuilds_the_layout_it_names() {
2225    let stereo = layout_from_mask(ChannelLayout::STEREO.bits());
2226    assert_eq!(stereo.channels(), 2);
2227    assert_eq!(stereo.bits(), ChannelLayout::STEREO.bits());
2228
2229    let five_one = layout_from_mask(ChannelLayout::_5POINT1.bits());
2230    assert_eq!(five_one.channels(), 6);
2231    assert_eq!(
2232      five_one.bits(),
2233      ChannelLayout::_5POINT1.bits(),
2234      "the side-vs-back distinction is exactly what a default layout would lose",
2235    );
2236  }
2237
2238  #[test]
2239  fn plane_geometry_follows_packed_versus_planar() {
2240    use ffmpeg_next::format::sample::Type;
2241    // Packed: one plane holding every channel.
2242    assert_eq!(
2243      plane_bytes(Sample::I16(Type::Packed), 1024, 2),
2244      Some(1024 * 2 * 2)
2245    );
2246    // Planar: one plane per channel, so the count does not multiply in.
2247    assert_eq!(
2248      plane_bytes(Sample::I16(Type::Planar), 1024, 2),
2249      Some(1024 * 2)
2250    );
2251    assert_eq!(
2252      plane_bytes(Sample::F32(Type::Planar), 1024, 6),
2253      Some(1024 * 4)
2254    );
2255    // A sample count whose byte size does not fit is not a size. This
2256    // is the arithmetic that used to run before the allocation it
2257    // feeds, and it wrapped.
2258    assert_eq!(
2259      plane_bytes(Sample::F32(Type::Packed), usize::MAX / 2, 8),
2260      None,
2261      "an overflowing plane size is refused, not wrapped",
2262    );
2263  }
2264
2265  #[test]
2266  fn the_target_timebase_is_one_tick_per_output_sample() {
2267    let spec = ResampleSpec::new(
2268      16_000,
2269      Sample::I16(ffmpeg_next::format::sample::Type::Packed),
2270      ChannelLayout::MONO,
2271    );
2272    let tb = spec.timebase();
2273    assert_eq!((tb.num(), tb.den().get()), (1, 16_000));
2274  }
2275}