Skip to main content

lumen_ffmpeg/
audio.rs

1use std::ptr;
2
3use crate::{
4    FfmpegError, Result,
5    ffi::{self, AvFrame, sys},
6    format::{InputContext, Packet, Rational},
7};
8use sys::{
9    AVRounding::AV_ROUND_UP,
10    AVSampleFormat::{AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_NONE, AV_SAMPLE_FMT_S16},
11};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum SampleFormat {
15    F32,
16    I16,
17    Unknown,
18}
19
20#[derive(Debug, Clone)]
21pub struct AudioFrame {
22    pub sample_rate: u32,
23    pub channels: u16,
24    pub sample_format: SampleFormat,
25    pub pts: Option<i64>,
26    pub samples: usize,
27    pub interleaved_f32: Vec<f32>,
28}
29
30#[derive(Debug, Clone, Copy)]
31pub struct AudioResamplerConfig {
32    pub sample_rate: u32,
33    pub channels: u16,
34    pub sample_format: SampleFormat,
35}
36
37impl Default for AudioResamplerConfig {
38    fn default() -> Self {
39        Self {
40            sample_rate: 48_000,
41            channels: 2,
42            sample_format: SampleFormat::F32,
43        }
44    }
45}
46
47pub struct AudioDecoder {
48    stream_index: usize,
49    time_base: Rational,
50    context: *mut sys::AVCodecContext,
51}
52
53unsafe impl Send for AudioDecoder {}
54
55impl AudioDecoder {
56    pub fn open(input: &InputContext, stream_index: usize) -> Result<Self> {
57        let parameters = input.stream_parameters(stream_index)?;
58        let codec = unsafe { sys::avcodec_find_decoder((*parameters).codec_id) };
59        if codec.is_null() {
60            return Err(FfmpegError::new(
61                "avcodec_find_decoder",
62                "no audio decoder found",
63            ));
64        }
65        let context = unsafe { sys::avcodec_alloc_context3(codec) };
66        if context.is_null() {
67            return Err(FfmpegError::new(
68                "avcodec_alloc_context3",
69                "failed to allocate audio decoder context",
70            ));
71        }
72        unsafe {
73            ffi::check(
74                sys::avcodec_parameters_to_context(context, parameters),
75                "avcodec_parameters_to_context",
76            )?;
77            ffi::check(
78                sys::avcodec_open2(context, codec, ptr::null_mut()),
79                "avcodec_open2",
80            )?;
81        }
82        Ok(Self {
83            stream_index,
84            time_base: input.stream_time_base(stream_index)?.into(),
85            context,
86        })
87    }
88
89    pub fn stream_index(&self) -> usize {
90        self.stream_index
91    }
92
93    pub fn time_base(&self) -> Rational {
94        self.time_base
95    }
96
97    pub fn send_packet(&mut self, packet: &Packet) -> Result<()> {
98        if packet.stream_index() != self.stream_index {
99            return Ok(());
100        }
101        unsafe {
102            ffi::check(
103                sys::avcodec_send_packet(self.context, packet.inner.as_ptr()),
104                "avcodec_send_packet",
105            )
106        }
107    }
108
109    pub fn send_eof(&mut self) -> Result<()> {
110        unsafe {
111            ffi::check(
112                sys::avcodec_send_packet(self.context, ptr::null()),
113                "avcodec_send_packet",
114            )
115        }
116    }
117
118    pub fn flush(&mut self) {
119        unsafe { sys::avcodec_flush_buffers(self.context) };
120    }
121
122    pub fn receive_frame(&mut self) -> Result<Option<DecodedAudioFrame>> {
123        let mut frame = AvFrame::new()?;
124        let result = unsafe { sys::avcodec_receive_frame(self.context, frame.as_mut_ptr()) };
125        if result == sys::AVERROR(libc::EAGAIN) || result == sys::AVERROR_EOF {
126            return Ok(None);
127        }
128        if result < 0 {
129            return Err(ffi::error_from_code("avcodec_receive_frame", result));
130        }
131        Ok(Some(DecodedAudioFrame { frame }))
132    }
133}
134
135impl Drop for AudioDecoder {
136    fn drop(&mut self) {
137        unsafe { sys::avcodec_free_context(&mut self.context) };
138    }
139}
140
141pub struct DecodedAudioFrame {
142    frame: AvFrame,
143}
144
145impl DecodedAudioFrame {
146    pub fn samples(&self) -> usize {
147        self.frame.nb_samples()
148    }
149
150    pub fn pts(&self) -> Option<i64> {
151        self.frame.pts()
152    }
153}
154
155pub struct AudioResampler {
156    ptr: *mut sys::SwrContext,
157    config: AudioResamplerConfig,
158}
159
160unsafe impl Send for AudioResampler {}
161
162impl AudioResampler {
163    pub fn new(source: &DecodedAudioFrame, config: AudioResamplerConfig) -> Result<Self> {
164        let mut dst_layout = unsafe { std::mem::zeroed() };
165        let mut src_layout = unsafe { (*source.frame.as_ptr()).ch_layout };
166        unsafe {
167            sys::av_channel_layout_default(&mut dst_layout, config.channels as i32);
168            if src_layout.nb_channels == 0 {
169                sys::av_channel_layout_default(
170                    &mut src_layout,
171                    (*source.frame.as_ptr()).ch_layout.nb_channels,
172                );
173            }
174        }
175        let mut ptr = ptr::null_mut();
176        unsafe {
177            ffi::check(
178                sys::swr_alloc_set_opts2(
179                    &mut ptr,
180                    ptr::addr_of_mut!(dst_layout),
181                    config.sample_format.to_av_sample_format(),
182                    config.sample_rate as i32,
183                    ptr::addr_of_mut!(src_layout),
184                    source.frame.sample_format(),
185                    (*source.frame.as_ptr()).sample_rate,
186                    0,
187                    ptr::null_mut(),
188                ),
189                "swr_alloc_set_opts2",
190            )?;
191            ffi::check(sys::swr_init(ptr), "swr_init")?;
192        }
193        Ok(Self { ptr, config })
194    }
195
196    pub fn convert(&mut self, source: &DecodedAudioFrame) -> Result<AudioFrame> {
197        if self.config.sample_format != SampleFormat::F32 {
198            return Err(FfmpegError::new(
199                "AudioResampler::convert",
200                "only interleaved f32 output is currently exposed",
201            ));
202        }
203        let input_samples = source.frame.nb_samples() as i64;
204        let delay =
205            unsafe { sys::swr_get_delay(self.ptr, (*source.frame.as_ptr()).sample_rate as i64) };
206        let output_capacity = unsafe {
207            sys::av_rescale_rnd(
208                delay + input_samples,
209                self.config.sample_rate as i64,
210                (*source.frame.as_ptr()).sample_rate as i64,
211                AV_ROUND_UP,
212            )
213        } as usize;
214        let channels = self.config.channels as usize;
215        let mut output = vec![0_f32; output_capacity.saturating_mul(channels)];
216        let mut output_ptr = output.as_mut_ptr() as *mut u8;
217        let converted = unsafe {
218            sys::swr_convert(
219                self.ptr,
220                ptr::addr_of_mut!(output_ptr),
221                output_capacity as i32,
222                (*source.frame.as_ptr()).data.as_ptr() as *mut *const u8,
223                source.frame.nb_samples() as i32,
224            )
225        };
226        if converted < 0 {
227            return Err(ffi::error_from_code("swr_convert", converted));
228        }
229        let samples = converted as usize;
230        output.truncate(samples.saturating_mul(channels));
231        Ok(AudioFrame {
232            sample_rate: self.config.sample_rate,
233            channels: self.config.channels,
234            sample_format: self.config.sample_format,
235            pts: source.pts(),
236            samples,
237            interleaved_f32: output,
238        })
239    }
240}
241
242impl Drop for AudioResampler {
243    fn drop(&mut self) {
244        unsafe { sys::swr_free(&mut self.ptr) };
245    }
246}
247
248impl SampleFormat {
249    fn to_av_sample_format(self) -> sys::AVSampleFormat {
250        match self {
251            Self::F32 => AV_SAMPLE_FMT_FLT,
252            Self::I16 => AV_SAMPLE_FMT_S16,
253            Self::Unknown => AV_SAMPLE_FMT_NONE,
254        }
255    }
256}