2025-12-28 20:17:56 +08:00
|
|
|
use crate::config::AudioConfig;
|
2025-12-29 22:06:16 +08:00
|
|
|
use anyhow::{Context, Result};
|
|
|
|
|
use opus::{Application, Bitrate, Channels, Decoder, Encoder};
|
2025-12-28 20:17:56 +08:00
|
|
|
|
|
|
|
|
pub struct OpusCodec {
|
|
|
|
|
encoder: Encoder,
|
|
|
|
|
decoder: Decoder,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl OpusCodec {
|
|
|
|
|
pub fn new(config: &AudioConfig) -> Result<Self> {
|
2025-12-29 22:06:16 +08:00
|
|
|
let channels = if config.channels == 1 {
|
|
|
|
|
Channels::Mono
|
|
|
|
|
} else {
|
|
|
|
|
Channels::Stereo
|
|
|
|
|
};
|
2025-12-29 14:28:21 +08:00
|
|
|
let mut encoder = Encoder::new(config.sample_rate, channels, Application::Audio)
|
2025-12-28 20:17:56 +08:00
|
|
|
.context("Failed to create Opus encoder")?;
|
|
|
|
|
encoder.set_bitrate(Bitrate::Bits(config.bitrate))?;
|
|
|
|
|
|
2025-12-29 22:06:16 +08:00
|
|
|
let decoder =
|
|
|
|
|
Decoder::new(config.sample_rate, channels).context("Failed to create Opus decoder")?;
|
2025-12-28 20:17:56 +08:00
|
|
|
|
|
|
|
|
Ok(Self { encoder, decoder })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn encode(&mut self, pcm: &[i16], out: &mut [u8]) -> Result<usize> {
|
2025-12-29 22:06:16 +08:00
|
|
|
self.encoder
|
|
|
|
|
.encode(pcm, out)
|
|
|
|
|
.context("Opus encoding failed")
|
2025-12-28 20:17:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn decode(&mut self, opus: &[u8], out: &mut [i16]) -> Result<usize> {
|
2025-12-29 22:06:16 +08:00
|
|
|
self.decoder
|
|
|
|
|
.decode(opus, out, false)
|
|
|
|
|
.context("Opus decoding failed")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Packet Loss Concealment
|
|
|
|
|
pub fn decode_loss(&mut self, out: &mut [i16]) -> Result<usize> {
|
|
|
|
|
// Fallback to silence as opus crate 0.3 doesn't expose safe PLC
|
|
|
|
|
out.fill(0);
|
|
|
|
|
Ok(out.len())
|
2025-12-28 20:17:56 +08:00
|
|
|
}
|
|
|
|
|
}
|