1use rff_codec::{Codec, CodecRegistry, Decoder, Encoder};
14use rff_core::{
15 AudioFrame, CodecId, Dictionary, Error, Frame, MediaType, Packet, Result, SampleFormat,
16};
17
18pub use rusty_mp3;
20
21pub fn register(registry: &mut CodecRegistry) {
23 registry.register(Codec {
24 id: CodecId::Mp3,
25 name: "mp3",
26 long_name: "MP3 (MPEG-1/2 Audio Layer III)",
27 media_type: MediaType::Audio,
28 decoder: Some(|| Box::new(Mp3Decoder::default())),
29 encoder: Some(|| Box::new(Mp3Encoder::default())),
30 });
31}
32
33fn map_err(e: rusty_mp3::Error) -> Error {
37 match e {
38 rusty_mp3::Error::Unimplemented(what) => Error::Unimplemented(what),
39 rusty_mp3::Error::Eof => Error::Eof,
40 rusty_mp3::Error::Again => Error::Again,
41 rusty_mp3::Error::InvalidData(msg) => Error::InvalidData(msg),
42 rusty_mp3::Error::Unsupported(msg) => Error::Unsupported(msg),
43 other => Error::InvalidData(format!("rusty_mp3: {other}")),
46 }
47}
48
49#[derive(Default)]
50struct Mp3Decoder {
51 inner: rusty_mp3::Mp3Decoder,
52}
53
54impl Decoder for Mp3Decoder {
55 fn send_packet(&mut self, packet: &Packet) -> Result<()> {
56 self.inner.push(&packet.data);
57 Ok(())
58 }
59
60 fn receive_frame(&mut self) -> Result<Frame> {
61 let audio = self.inner.next_frame().map_err(map_err)?;
62 let channels = audio.channels.max(1);
63 let mut bytes = Vec::with_capacity(audio.samples.len() * 4);
64 for s in &audio.samples {
65 bytes.extend_from_slice(&s.to_le_bytes());
66 }
67 Ok(Frame::Audio(AudioFrame {
68 sample_rate: audio.sample_rate,
69 channels,
70 format: SampleFormat::F32,
71 samples: audio.samples.len() / channels as usize,
72 planes: vec![bytes],
73 pts: None,
74 }))
75 }
76
77 fn flush(&mut self) {
78 self.inner.flush();
79 }
80}
81
82fn parse_bitrate(s: &str) -> Option<u32> {
84 let s = s.trim();
85 if let Some(n) = s.strip_suffix(['k', 'K']) {
86 n.trim().parse::<f32>().ok().map(|x| x as u32)
87 } else if let Some(n) = s.strip_suffix(['m', 'M']) {
88 n.trim().parse::<f32>().ok().map(|x| (x * 1000.0) as u32)
89 } else {
90 s.parse::<u32>()
91 .ok()
92 .map(|x| if x >= 1000 { x / 1000 } else { x })
93 }
94}
95
96#[derive(Default)]
100struct Mp3Encoder {
101 inner: Option<rusty_mp3::Mp3Encoder>,
102 cbr_kbps: u32,
104 quality: Option<f32>,
106 eof: bool,
107}
108
109impl Mp3Encoder {
110 fn inner(&mut self) -> &mut rusty_mp3::Mp3Encoder {
111 if self.inner.is_none() {
112 self.inner = Some(rusty_mp3::Mp3Encoder::new(rusty_mp3::Mp3EncoderConfig {
113 bitrate_kbps: self.cbr_kbps,
114 vbr_quality: self.quality,
115 }));
116 }
117 self.inner.as_mut().unwrap()
118 }
119}
120
121impl Encoder for Mp3Encoder {
122 fn configure(&mut self, options: &Dictionary) -> Result<()> {
123 if let Some(b) = options.get("b").and_then(parse_bitrate) {
124 self.cbr_kbps = b;
125 }
126 for key in ["q", "qp", "crf", "qscale"] {
129 if let Some(q) = options.get(key).and_then(|v| v.trim().parse::<f32>().ok()) {
130 self.quality = Some(rusty_mp3::vbr_quality_index(q));
131 break;
132 }
133 }
134 Ok(())
135 }
136
137 fn send_frame(&mut self, frame: &Frame) -> Result<()> {
138 let Frame::Audio(af) = frame else {
139 return Ok(());
140 };
141 let in_ch = af.channels.max(1);
142 let data = &af.planes[0];
143 match af.format {
149 SampleFormat::F32 => {
150 let mut pcm = Vec::with_capacity(af.samples * in_ch as usize);
151 for s in 0..af.samples {
152 for c in 0..in_ch as usize {
153 let off = (s * in_ch as usize + c) * 4;
154 pcm.push(if off + 4 <= data.len() {
155 f32::from_le_bytes([
156 data[off],
157 data[off + 1],
158 data[off + 2],
159 data[off + 3],
160 ])
161 } else {
162 0.0
163 });
164 }
165 }
166 let sr = af.sample_rate;
167 self.inner()
168 .push_pcm_f32(&pcm, in_ch, sr)
169 .map_err(map_err)?;
170 }
171 SampleFormat::S16 => {
172 let mut pcm = Vec::with_capacity(af.samples * in_ch as usize);
173 for s in 0..af.samples {
174 for c in 0..in_ch as usize {
175 let off = (s * in_ch as usize + c) * 2;
176 pcm.push(if off + 2 <= data.len() {
177 i16::from_le_bytes([data[off], data[off + 1]])
178 } else {
179 0
180 });
181 }
182 }
183 let sr = af.sample_rate;
184 self.inner()
185 .push_pcm_s16(&pcm, in_ch, sr)
186 .map_err(map_err)?;
187 }
188 other => {
189 return Err(Error::unsupported(format!(
190 "mp3 encode: sample format `{}` (need interleaved s16/f32)",
191 other.name()
192 )))
193 }
194 }
195 Ok(())
196 }
197
198 fn receive_packet(&mut self) -> Result<Packet> {
199 match self.inner.as_mut() {
203 Some(inner) => inner
204 .next_packet()
205 .map(|bytes| Packet::from_data(0, bytes))
206 .map_err(map_err),
207 None if self.eof => Err(Error::Eof),
208 None => Err(Error::Again),
209 }
210 }
211
212 fn flush(&mut self) {
213 if let Some(inner) = self.inner.as_mut() {
214 inner.finish();
215 }
216 self.eof = true;
217 }
218}
219
220#[cfg(test)]
221mod tests {
222 use super::*;
223
224 fn encode_mono_s16(input: &[i16], sample_rate: u32) -> Vec<u8> {
227 let bytes: Vec<u8> = input.iter().flat_map(|s| s.to_le_bytes()).collect();
228 let mut enc = Mp3Encoder::default();
229 enc.send_frame(&Frame::Audio(AudioFrame {
230 sample_rate,
231 channels: 1,
232 format: SampleFormat::S16,
233 planes: vec![bytes],
234 samples: input.len(),
235 pts: None,
236 }))
237 .unwrap();
238 enc.flush();
239 let mut mp3 = Vec::new();
240 while let Ok(p) = enc.receive_packet() {
241 mp3.extend_from_slice(&p.data);
242 }
243 mp3
244 }
245
246 fn decode_mono(mp3: Vec<u8>) -> Vec<f32> {
247 let mut dec = Mp3Decoder::default();
248 dec.send_packet(&Packet::from_data(0, mp3)).unwrap();
249 dec.flush();
250 let mut out = Vec::new();
251 while let Ok(Frame::Audio(af)) = dec.receive_frame() {
252 for c in af.planes[0].chunks_exact(4) {
253 out.push(f32::from_le_bytes([c[0], c[1], c[2], c[3]]));
254 }
255 }
256 out
257 }
258
259 #[test]
265 fn s16_input_encodes_to_audible_output() {
266 let sr = 44100u32;
267 let n = sr as usize; let s16: Vec<i16> = (0..n)
269 .map(|i| {
270 let t = i as f32 / sr as f32;
271 (0.5 * (2.0 * std::f32::consts::PI * 440.0 * t).sin() * 32767.0) as i16
272 })
273 .collect();
274 let out = decode_mono(encode_mono_s16(&s16, sr));
275 let rms = (out.iter().map(|x| x * x).sum::<f32>() / out.len().max(1) as f32).sqrt();
276 assert!(
277 rms > 0.1,
278 "S16 input produced near-silent output (rms={rms}); encoder ignored af.format"
279 );
280 }
281
282 #[test]
283 fn registers_as_audio_codec() {
284 let mut reg = CodecRegistry::new();
285 register(&mut reg);
286 let codec = reg.by_name("mp3").expect("mp3 registered");
287 assert_eq!(codec.id, CodecId::Mp3);
288 assert_eq!(codec.media_type, MediaType::Audio);
289 assert!(codec.can_decode() && codec.can_encode());
290 }
291}