1use std::collections::VecDeque;
14
15use rff_codec::{Codec, CodecParams, CodecRegistry, Decoder, Encoder};
16use rff_core::{AudioFrame, Dictionary, Error, Frame, MediaType, Packet, Result, SampleFormat};
17use rusty_opus::parallel::{encode_parallel, ParallelConfig};
18use rusty_opus::{Application, OpusDecoder, OpusEncoder};
19
20const FRAME_MS: usize = 20;
22const RATES: [u32; 5] = [8000, 12000, 16000, 24000, 48000];
24
25pub fn register(registry: &mut CodecRegistry) {
27 registry.register(Codec {
28 id: rff_core::CodecId::Opus,
29 name: "opus",
30 long_name: "Opus (Opus Interactive Audio Codec)",
31 media_type: MediaType::Audio,
32 decoder: Some(|| Box::new(OpusDec::default())),
33 encoder: Some(|| Box::new(OpusEnc::default())),
34 });
35}
36
37fn check_rate(rate: u32) -> Result<()> {
38 if RATES.contains(&rate) {
39 Ok(())
40 } else {
41 Err(Error::unsupported(format!(
42 "opus: sample rate {rate} (must be 8000/12000/16000/24000/48000)"
43 )))
44 }
45}
46
47fn frame_to_f32(af: &AudioFrame) -> Result<Vec<f32>> {
49 match af.format {
50 SampleFormat::F32 => Ok(af.planes[0]
51 .chunks_exact(4)
52 .map(|b| f32::from_le_bytes([b[0], b[1], b[2], b[3]]))
53 .collect()),
54 SampleFormat::S16 => Ok(af.planes[0]
55 .chunks_exact(2)
56 .map(|b| i16::from_le_bytes([b[0], b[1]]) as f32 / 32768.0)
57 .collect()),
58 other => Err(Error::unsupported(format!(
59 "opus encode: sample format `{}` (need interleaved s16/f32)",
60 other.name()
61 ))),
62 }
63}
64
65#[derive(Default)]
70struct OpusDec {
71 dec: Option<OpusDecoder>,
72 sample_rate: u32,
73 channels: u16,
74 queue: VecDeque<Frame>,
75 eof: bool,
76}
77
78impl Decoder for OpusDec {
79 fn configure(&mut self, params: &CodecParams) -> Result<()> {
80 let rate = if params.sample_rate == 0 {
82 48_000
83 } else {
84 params.sample_rate
85 };
86 check_rate(rate)?;
87 let channels = params.channels.max(1);
88 self.dec = Some(
89 OpusDecoder::new(rate as i32, channels as usize)
90 .map_err(|e| Error::invalid(format!("opus decode init: {e}")))?,
91 );
92 self.sample_rate = rate;
93 self.channels = channels;
94 Ok(())
95 }
96
97 fn send_packet(&mut self, packet: &Packet) -> Result<()> {
98 let channels = self.channels as usize;
99 let frame_size = self.sample_rate as usize / 1000 * FRAME_MS;
102 let dec = self
103 .dec
104 .as_mut()
105 .ok_or_else(|| Error::invalid("opus decode: not configured"))?;
106
107 let mut out = vec![0f32; frame_size * channels];
108 let samples = dec
109 .decode(&packet.data, frame_size, &mut out)
110 .map_err(|e| Error::invalid(format!("opus decode: {e}")))?;
111 out.truncate(samples * channels);
112
113 let bytes: Vec<u8> = out.iter().flat_map(|s| s.to_le_bytes()).collect();
114 self.queue.push_back(Frame::Audio(AudioFrame {
115 sample_rate: self.sample_rate,
116 channels: self.channels,
117 format: SampleFormat::F32,
118 planes: vec![bytes],
119 samples,
120 pts: packet.pts,
121 }));
122 Ok(())
123 }
124
125 fn receive_frame(&mut self) -> Result<Frame> {
126 if let Some(frame) = self.queue.pop_front() {
127 return Ok(frame);
128 }
129 if self.eof {
130 Err(Error::Eof)
131 } else {
132 Err(Error::Again)
133 }
134 }
135
136 fn flush(&mut self) {
137 self.eof = true;
138 }
139}
140
141struct OpusEnc {
146 enc: Option<OpusEncoder>,
147 sample_rate: u32,
148 channels: u16,
149 frame_size: usize,
151 buffer: Vec<f32>,
153 next_pts: i64,
155 queue: VecDeque<Packet>,
156 eof: bool,
157 bitrate: i32,
160 complexity: i32,
162 use_cbr: bool,
164 voip: bool,
166 parallel: bool,
171 warmup: usize,
174 threads: usize,
176}
177
178impl Default for OpusEnc {
179 fn default() -> Self {
180 OpusEnc {
181 enc: None,
182 sample_rate: 0,
183 channels: 0,
184 frame_size: 0,
185 buffer: Vec::new(),
186 next_pts: 0,
187 queue: VecDeque::new(),
188 eof: false,
189 bitrate: 64_000,
190 complexity: 9,
191 use_cbr: false,
192 voip: false,
193 parallel: true,
194 warmup: 8,
195 threads: 0,
196 }
197 }
198}
199
200impl OpusEnc {
201 fn init(&mut self, af: &AudioFrame) -> Result<()> {
202 check_rate(af.sample_rate)?;
203 let channels = af.channels.max(1);
204 let app = if self.voip {
205 Application::Voip
206 } else {
207 Application::Audio
208 };
209 let mut enc = OpusEncoder::new(af.sample_rate as i32, channels as usize, app)
210 .map_err(|e| Error::invalid(format!("opus encode init: {e}")))?;
211 enc.bitrate_bps = self.bitrate;
212 enc.complexity = self.complexity.clamp(0, 10);
213 enc.use_cbr = self.use_cbr;
214 self.enc = Some(enc);
215 self.sample_rate = af.sample_rate;
216 self.channels = channels;
217 self.frame_size = af.sample_rate as usize / 1000 * FRAME_MS;
218 Ok(())
219 }
220
221 fn drain_frames(&mut self) -> Result<()> {
223 let frame_samples = self.frame_size * self.channels as usize;
224 if frame_samples == 0 || self.buffer.len() < frame_samples {
225 return Ok(());
226 }
227 let OpusEnc {
234 enc,
235 buffer,
236 queue,
237 next_pts,
238 frame_size,
239 ..
240 } = self;
241 let enc = enc.as_mut().expect("encoder initialized");
242 let frame_size = *frame_size;
243 let n_full = buffer.len() / frame_samples;
244 for i in 0..n_full {
245 let chunk = &buffer[i * frame_samples..(i + 1) * frame_samples];
246 let mut out = vec![0u8; 4000]; let n = enc
248 .encode(chunk, frame_size, &mut out)
249 .map_err(|e| Error::invalid(format!("opus encode: {e}")))?;
250 out.truncate(n);
251 let mut packet = Packet::from_data(0, out);
253 packet.pts = Some(*next_pts);
254 *next_pts += frame_size as i64;
255 queue.push_back(packet);
256 }
257 buffer.drain(..n_full * frame_samples);
258 Ok(())
259 }
260
261 fn drain_parallel(&mut self) {
264 let frame_samples = self.frame_size * self.channels as usize;
265 if frame_samples == 0 || self.buffer.is_empty() {
266 return;
267 }
268 let rem = self.buffer.len() % frame_samples;
269 if rem != 0 {
270 self.buffer
271 .extend(std::iter::repeat(0.0).take(frame_samples - rem));
272 }
273 let app = if self.voip {
274 Application::Voip
275 } else {
276 Application::Audio
277 };
278 let mut cfg = ParallelConfig::new(self.sample_rate as i32, self.channels as usize, app);
279 cfg.bitrate_bps = self.bitrate;
280 cfg.complexity = self.complexity;
281 cfg.use_cbr = self.use_cbr;
282 cfg.warmup = self.warmup;
283 cfg.threads = self.threads;
284 let packets = encode_parallel(&cfg, &self.buffer, self.frame_size);
285 self.buffer.clear();
286 for data in packets {
287 let mut packet = Packet::from_data(0, data);
288 packet.pts = Some(self.next_pts);
289 self.next_pts += self.frame_size as i64;
290 self.queue.push_back(packet);
291 }
292 }
293}
294
295impl Encoder for OpusEnc {
296 fn configure(&mut self, options: &Dictionary) -> Result<()> {
297 if let Some(b) = options.get_bitrate("b") {
298 if b > 0 {
299 self.bitrate = b as i32;
300 }
301 }
302 if let Some(c) = options.get_int("compression_level") {
304 self.complexity = (c as i32).clamp(0, 10);
305 }
306 if let Some(app) = options.get("application") {
307 self.voip = app.eq_ignore_ascii_case("voip") || app.eq_ignore_ascii_case("lowdelay");
308 }
309 if let Some(v) = options.get("vbr") {
311 self.use_cbr = v == "off" || v == "0" || v.eq_ignore_ascii_case("false");
312 }
313 if let Some(p) = options.get("opus_parallel") {
315 self.parallel = !(p == "0" || p == "off" || p.eq_ignore_ascii_case("false"));
316 }
317 if let Some(w) = options.get_int("opus_warmup") {
318 self.warmup = w.max(0) as usize;
319 }
320 if let Some(t) = options.get_int("threads") {
321 self.threads = t.max(0) as usize;
322 }
323 Ok(())
324 }
325
326 fn accepted_sample_rates(&self) -> Option<Vec<u32>> {
327 Some(RATES.to_vec())
328 }
329
330 fn send_frame(&mut self, frame: &Frame) -> Result<()> {
331 let af = match frame {
332 Frame::Audio(a) => a,
333 Frame::Video(_) => {
334 return Err(Error::unsupported(
335 "opus encode: video frame on an audio codec",
336 ))
337 }
338 };
339 if self.enc.is_none() {
340 self.init(af)?;
341 self.next_pts = af.pts.unwrap_or(0);
343 } else if af.sample_rate != self.sample_rate || af.channels != self.channels {
344 return Err(Error::unsupported(
345 "opus encode: stream layout changed mid-stream",
346 ));
347 }
348 self.buffer.extend(frame_to_f32(af)?);
349 if self.parallel {
352 Ok(())
353 } else {
354 self.drain_frames()
355 }
356 }
357
358 fn receive_packet(&mut self) -> Result<Packet> {
359 if let Some(packet) = self.queue.pop_front() {
360 return Ok(packet);
361 }
362 if self.eof {
363 Err(Error::Eof)
364 } else {
365 Err(Error::Again)
366 }
367 }
368
369 fn flush(&mut self) {
370 if self.frame_size != 0 && !self.buffer.is_empty() {
372 if self.parallel {
373 self.drain_parallel();
374 } else {
375 let frame_samples = self.frame_size * self.channels as usize;
377 let rem = self.buffer.len() % frame_samples;
378 if rem != 0 {
379 self.buffer
380 .extend(std::iter::repeat(0.0).take(frame_samples - rem));
381 }
382 let _ = self.drain_frames();
383 }
384 }
385 self.eof = true;
386 }
387}
388
389#[cfg(test)]
390mod tests {
391 use super::*;
392
393 #[test]
396 fn opus_encode_then_decode() {
397 let (rate, channels) = (48_000u32, 1u16);
398 let frame = rate as usize / 1000 * FRAME_MS; let mut samples = Vec::new();
400 for i in 0..frame * 10 {
401 samples.push(((i as f32 * 0.05).sin() * 0.3) as f32);
402 }
403 let bytes: Vec<u8> = samples.iter().flat_map(|s| s.to_le_bytes()).collect();
404
405 let mut enc = OpusEnc::default();
406 enc.send_frame(&Frame::Audio(AudioFrame {
407 sample_rate: rate,
408 channels,
409 format: SampleFormat::F32,
410 planes: vec![bytes],
411 samples: samples.len(),
412 pts: Some(0),
413 }))
414 .unwrap();
415 enc.flush();
416
417 let mut packets = Vec::new();
418 loop {
419 match enc.receive_packet() {
420 Ok(p) => {
421 assert!(!p.data.is_empty());
422 packets.push(p);
423 }
424 Err(Error::Eof) | Err(Error::Again) => break,
425 Err(e) => panic!("encode: {e}"),
426 }
427 }
428 assert!(
429 packets.len() >= 8,
430 "expected ~10 frames, got {}",
431 packets.len()
432 );
433
434 let mut dec = OpusDec::default();
435 dec.configure(&CodecParams {
436 sample_rate: rate,
437 channels,
438 ..Default::default()
439 })
440 .unwrap();
441 let mut decoded = 0usize;
442 let mut energy = 0f64;
443 for p in &packets {
444 dec.send_packet(p).unwrap();
445 if let Ok(Frame::Audio(af)) = dec.receive_frame() {
446 assert_eq!(af.format, SampleFormat::F32);
447 decoded += af.samples;
448 for b in af.planes[0].chunks_exact(4) {
449 let s = f32::from_le_bytes([b[0], b[1], b[2], b[3]]) as f64;
450 energy += s * s;
451 }
452 }
453 }
454 assert!(decoded >= frame * 8, "decoded too few samples: {decoded}");
455 let rms = (energy / decoded.max(1) as f64).sqrt();
456 assert!(rms > 0.02, "decoded audio is silent: rms {rms:.4}");
457 }
458}