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