pub mod g711;
#[cfg(feature = "opus")]
pub mod opus;
#[cfg(feature = "opus")]
pub use seam::{Decoder, Encoder};
#[cfg(feature = "opus")]
mod seam {
use super::g711::{G711Codec, G711_FRAME_SAMPLES, G711_SAMPLE_RATE};
use super::opus::{OpusDecoder, OpusEncoder, OPUS_FRAME_SAMPLES, OPUS_PCM_SAMPLE_RATE};
use crate::CoreError;
pub enum Encoder {
G711(G711Codec),
Opus(OpusEncoder),
}
impl Encoder {
pub fn pcm_sample_rate(&self) -> u32 {
match self {
Encoder::G711(_) => G711_SAMPLE_RATE,
Encoder::Opus(_) => OPUS_PCM_SAMPLE_RATE,
}
}
pub fn frame_samples(&self) -> usize {
match self {
Encoder::G711(_) => G711_FRAME_SAMPLES,
Encoder::Opus(_) => OPUS_FRAME_SAMPLES,
}
}
pub fn encode(&mut self, pcm: &[i16], out: &mut Vec<u8>) -> Result<usize, CoreError> {
match self {
Encoder::G711(codec) => {
codec.encode(pcm, out);
Ok(pcm.len())
}
Encoder::Opus(enc) => enc.encode(pcm, out),
}
}
}
pub enum Decoder {
G711(G711Codec),
Opus(OpusDecoder),
}
impl Decoder {
pub fn pcm_sample_rate(&self) -> u32 {
match self {
Decoder::G711(_) => G711_SAMPLE_RATE,
Decoder::Opus(_) => OPUS_PCM_SAMPLE_RATE,
}
}
pub fn decode(&mut self, payload: &[u8], out: &mut Vec<i16>) -> Result<usize, CoreError> {
match self {
Decoder::G711(codec) => {
codec.decode(payload, out);
Ok(payload.len())
}
Decoder::Opus(dec) => dec.decode(payload, out),
}
}
pub fn recover_lost(
&mut self,
next_payload: &[u8],
out: &mut Vec<i16>,
) -> Result<usize, CoreError> {
match self {
Decoder::G711(_) => Ok(0),
Decoder::Opus(dec) => dec.decode_fec(next_payload, out),
}
}
pub fn conceal(&mut self, out: &mut Vec<i16>) -> Result<usize, CoreError> {
match self {
Decoder::G711(_) => Ok(0),
Decoder::Opus(dec) => dec.conceal(out),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn tone(n: usize, rate: u32) -> Vec<i16> {
(0..n)
.map(|i| {
let t = i as f32 / rate as f32;
((t * 440.0 * 2.0 * std::f32::consts::PI).sin() * 8000.0) as i16
})
.collect()
}
#[test]
fn g711_seam_matches_direct_codec() {
let pcm = tone(G711_FRAME_SAMPLES, G711_SAMPLE_RATE);
let mut via_seam = Vec::new();
Encoder::G711(G711Codec::Pcmu)
.encode(&pcm, &mut via_seam)
.unwrap();
let mut direct = Vec::new();
G711Codec::Pcmu.encode(&pcm, &mut direct);
assert_eq!(via_seam, direct);
let mut decoded = Vec::new();
let n = Decoder::G711(G711Codec::Pcmu)
.decode(&via_seam, &mut decoded)
.unwrap();
assert_eq!(n, G711_FRAME_SAMPLES);
}
#[test]
fn seam_reports_per_codec_rates_and_frames() {
let g711 = Encoder::G711(G711Codec::Pcma);
assert_eq!(g711.pcm_sample_rate(), 8_000);
assert_eq!(g711.frame_samples(), 160);
let opus = Encoder::Opus(OpusEncoder::new().unwrap());
assert_eq!(opus.pcm_sample_rate(), 16_000);
assert_eq!(opus.frame_samples(), 320);
assert_eq!(Decoder::G711(G711Codec::Pcmu).pcm_sample_rate(), 8_000);
assert_eq!(
Decoder::Opus(OpusDecoder::new().unwrap()).pcm_sample_rate(),
16_000
);
}
#[test]
fn opus_seam_round_trips() {
let mut enc = Encoder::Opus(OpusEncoder::new().unwrap());
let mut dec = Decoder::Opus(OpusDecoder::new().unwrap());
let pcm = tone(OPUS_FRAME_SAMPLES, OPUS_PCM_SAMPLE_RATE);
let mut packet = Vec::new();
enc.encode(&pcm, &mut packet).unwrap();
let mut out = Vec::new();
let n = dec.decode(&packet, &mut out).unwrap();
assert_eq!(n, OPUS_FRAME_SAMPLES);
}
#[test]
fn g711_recovery_is_a_noop_by_design() {
let mut dec = Decoder::G711(G711Codec::Pcmu);
let mut out = vec![1i16, 2, 3];
assert_eq!(dec.recover_lost(&[0xFF; 20], &mut out).unwrap(), 0);
assert_eq!(dec.conceal(&mut out).unwrap(), 0);
assert_eq!(out, vec![1, 2, 3], "G.711 recovery must not touch out");
}
#[test]
fn opus_seam_recovers_and_conceals() {
let mut enc = Encoder::Opus(OpusEncoder::new().unwrap());
let mut dec = Decoder::Opus(OpusDecoder::new().unwrap());
let mut packets = Vec::new();
for i in 0..4 {
let pcm: Vec<i16> = (0..OPUS_FRAME_SAMPLES)
.map(|j| {
let t = (i * OPUS_FRAME_SAMPLES + j) as f32 / OPUS_PCM_SAMPLE_RATE as f32;
((t * 440.0 * 2.0 * std::f32::consts::PI).sin() * 8000.0) as i16
})
.collect();
let mut packet = Vec::new();
enc.encode(&pcm, &mut packet).unwrap();
packets.push(packet);
}
let mut out = Vec::new();
dec.decode(&packets[0], &mut out).unwrap();
out.clear();
dec.decode(&packets[1], &mut out).unwrap();
out.clear();
assert_eq!(
dec.recover_lost(&packets[3], &mut out).unwrap(),
OPUS_FRAME_SAMPLES
);
assert_eq!(dec.conceal(&mut out).unwrap(), OPUS_FRAME_SAMPLES);
assert_eq!(out.len(), 2 * OPUS_FRAME_SAMPLES);
}
}
}