scuffle_ffmpeg/
resampler.rs1use rusty_ffmpeg::ffi::{SwrContext, swr_alloc_set_opts2, swr_convert_frame, swr_free, swr_init};
2
3use crate::enums::AVSampleFormat;
4use crate::error::{FfmpegError, FfmpegErrorCode};
5use crate::frame::{AudioChannelLayout, AudioFrame, GenericFrame};
6use crate::smart_object::SmartPtr;
7
8pub struct Resampler {
10 ptr: SmartPtr<SwrContext>,
11 channel_layout: AudioChannelLayout,
12 sample_fmt: AVSampleFormat,
13 sample_rate: i32,
14}
15
16impl Resampler {
17 pub fn new(
19 input_ch_layout: AudioChannelLayout,
20 input_sample_fmt: AVSampleFormat,
21 input_sample_rate: i32,
22 output_ch_layout: AudioChannelLayout,
23 output_sample_fmt: AVSampleFormat,
24 output_sample_rate: i32,
25 ) -> Result<Self, FfmpegError> {
26 let mut ptr = core::ptr::null_mut::<SwrContext>();
27
28 FfmpegErrorCode(unsafe {
30 swr_alloc_set_opts2(
31 &mut ptr,
32 output_ch_layout.as_ptr(),
33 output_sample_fmt.into(),
34 output_sample_rate,
35 input_ch_layout.as_ptr(),
36 input_sample_fmt.into(),
37 input_sample_rate,
38 0,
39 core::ptr::null::<core::ffi::c_void>() as _,
40 )
41 })
42 .result()?;
43
44 let destructor = |ctx: &mut *mut SwrContext| {
45 unsafe { swr_free(ctx) };
47 };
48
49 let mut ptr = unsafe { SmartPtr::wrap_non_null(ptr, destructor).ok_or(FfmpegError::Alloc) }?;
51
52 FfmpegErrorCode(unsafe { swr_init(ptr.as_mut_ptr()) }).result()?;
54
55 Ok(Self {
56 ptr,
57 channel_layout: output_ch_layout,
58 sample_fmt: output_sample_fmt,
59 sample_rate: output_sample_rate,
60 })
61 }
62
63 pub fn process(&mut self, input: &AudioFrame) -> Result<AudioFrame, FfmpegError> {
65 let mut out = GenericFrame::new()?;
66
67 let inner = unsafe { out.as_mut_ptr().as_mut() }.expect("inner pointer of GenericFrame was invalid");
69 inner.ch_layout = self.channel_layout().copy()?.into_inner();
70 inner.format = self.sample_format().into();
71 inner.sample_rate = self.sample_rate();
72
73 FfmpegErrorCode(unsafe { swr_convert_frame(self.ptr.as_mut_ptr(), out.as_mut_ptr(), input.as_ptr()) }).result()?;
75
76 Ok(out.audio())
78 }
79
80 pub const fn channel_layout(&self) -> &AudioChannelLayout {
82 &self.channel_layout
83 }
84
85 pub const fn sample_format(&self) -> AVSampleFormat {
87 self.sample_fmt
88 }
89
90 pub const fn sample_rate(&self) -> i32 {
92 self.sample_rate
93 }
94}
95
96#[cfg(test)]
97#[cfg_attr(all(test, coverage_nightly), coverage(off))]
98mod tests {
99 use rand::{Rng, rng};
100 use rusty_ffmpeg::ffi::swr_is_initialized;
101
102 use super::Resampler;
103 use crate::AVSampleFormat;
104 use crate::frame::{AudioChannelLayout, AudioFrame};
105
106 #[test]
107 fn test_resampler_new() {
108 let input_layout = AudioChannelLayout::new(1).expect("Failed to create new AudioChannelLayout");
109 let input_format = AVSampleFormat::S16;
110 let input_sample_rate = 44100;
111
112 let output_layout = AudioChannelLayout::new(2).expect("Failed to create new AudioChannelLayout");
113 let output_format = AVSampleFormat::S16p;
114 let output_sample_rate = 48000;
115
116 let mut resampler = Resampler::new(
117 input_layout,
118 input_format,
119 input_sample_rate,
120 output_layout,
121 output_format,
122 output_sample_rate,
123 )
124 .expect("Failed to create new Resampler");
125
126 let is_init = unsafe { swr_is_initialized(resampler.ptr.as_mut_ptr()) };
128
129 assert!(is_init.is_positive() && is_init != 0, "Resampler is not initialized")
130 }
131
132 #[test]
133 fn test_resampler_process() {
134 let input_layout = AudioChannelLayout::new(1).expect("Failed to create new AudioChannelLayout");
135 let input_format = AVSampleFormat::S16;
136 let input_sample_rate = 44100;
137
138 let output_layout = AudioChannelLayout::new(2).expect("Failed to create new AudioChannelLayout");
139 let output_format = AVSampleFormat::S16p;
140 let output_sample_rate = 48000;
141
142 let mut resampler = Resampler::new(
143 input_layout.copy().unwrap(),
144 input_format,
145 input_sample_rate,
146 output_layout,
147 output_format,
148 output_sample_rate,
149 )
150 .expect("Failed to create new Resampler");
151
152 let mut input_frame = AudioFrame::builder()
153 .nb_samples(1024)
154 .channel_layout(input_layout)
155 .sample_fmt(input_format)
156 .sample_rate(44100)
157 .build()
158 .expect("Failed to create input AudioFrame");
159
160 let input_data = input_frame.data_mut(0).expect("Data buffer of input frame was invalid");
161 rng().fill(input_data);
162
163 let output = resampler.process(&input_frame).expect("Failed to process frame");
164
165 assert_eq!(output.channel_count(), 2, "Output channel count should be 2");
166 assert!(output.data(0).is_some(), "First data buffer of output frame is None");
167 assert!(output.data(1).is_some(), "Second data buffer of output frame is None");
168 assert_eq!(output.sample_rate(), 48000, "Output sample rate was not 48000");
169 }
170}