use std::sync::Arc;
use std::time::{Duration, Instant};
use bytes::Bytes;
use rtc::media::Sample;
use tokio::sync::Mutex;
use tracing::{debug, warn};
use webrtc::media_stream::Track;
use webrtc::media_stream::track_local::static_sample::TrackLocalStaticSample;
use webrtc::media_stream::track_remote::{TrackRemote, TrackRemoteEvent};
use webrtc::rtp_transceiver::RtpSender;
use crate::audio::{AudioSink, AudioSource, FRAME, check_rate, frame_samples};
use crate::body::Body;
use crate::error::{Error, Result};
pub(crate) struct Encoder {
opus: opus::Encoder,
frame: usize,
pending: Vec<i16>,
out: Vec<u8>,
}
impl Encoder {
pub(crate) fn new(sample_rate: u32) -> Result<Self> {
let rate = check_rate(sample_rate)?;
let opus = opus::Encoder::new(rate, opus::Channels::Mono, opus::Application::Voip)?;
Ok(Self { opus, frame: frame_samples(rate), pending: Vec::new(), out: vec![0; 4000] })
}
pub(crate) fn push(&mut self, pcm: &[i16]) -> Result<Vec<Bytes>> {
self.pending.extend_from_slice(pcm);
let mut packets = Vec::new();
while self.pending.len() >= self.frame {
let size = self.opus.encode(&self.pending[..self.frame], &mut self.out)?;
packets.push(Bytes::copy_from_slice(&self.out[..size]));
self.pending.drain(..self.frame);
}
Ok(packets)
}
}
pub(crate) struct Decoder {
opus: opus::Decoder,
out: Vec<i16>,
}
impl Decoder {
pub(crate) fn new(sample_rate: u32) -> Result<Self> {
let rate = check_rate(sample_rate)?;
Ok(Self {
opus: opus::Decoder::new(rate, opus::Channels::Mono)?,
out: vec![0; (rate / 1000 * 120) as usize], })
}
pub(crate) fn decode(&mut self, packet: &[u8]) -> Result<&[i16]> {
let samples = self.opus.decode(packet, &mut self.out, false)?;
Ok(&self.out[..samples])
}
}
pub(crate) async fn send(
source: Arc<Mutex<Box<dyn AudioSource>>>,
track: Arc<TrackLocalStaticSample>,
sender: Arc<dyn RtpSender>,
) -> Result<()> {
let payload_type = sender
.get_parameters()
.await?
.rtp_parameters
.codecs
.first()
.map(|codec| codec.payload_type)
.ok_or(Error::Link("no audio codec negotiated"))?;
let ssrc = *track.ssrcs().await.first().ok_or(Error::Link("the audio track has no SSRC"))?;
let mut source = source.lock().await;
let rate = source.sample_rate();
let mut encoder = Encoder::new(rate)?;
let started = Instant::now();
let mut sent: u64 = 0;
loop {
let due = started + Duration::from_secs_f64(sent as f64 / f64::from(rate));
tokio::time::sleep_until(due.into()).await;
let pcm = source.read().await?;
for packet in encoder.push(&pcm)? {
track
.sample_writer(ssrc, payload_type)
.write_sample(&Sample { data: packet, duration: FRAME, ..Sample::new(Instant::now()) })
.await?;
}
sent += pcm.len() as u64;
}
}
pub(crate) async fn play(
track: Arc<dyn TrackRemote>,
sink: Arc<Mutex<Box<dyn AudioSink>>>,
body: Arc<dyn Body>,
) -> Result<()> {
let mut sink = sink.lock().await;
let mut decoder = Decoder::new(sink.sample_rate())?;
while let Some(event) = track.poll().await {
match event {
TrackRemoteEvent::OnRtpPacket(packet) => {
if packet.payload.is_empty() {
continue;
}
body.heartbeat();
match decoder.decode(&packet.payload) {
Ok(pcm) => sink.write(pcm).await?,
Err(error) => warn!(%error, "an undecodable audio packet from the core"),
}
}
TrackRemoteEvent::OnEnded => break,
_ => {}
}
}
debug!("the core's audio track ended");
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn audio_is_cut_in_20_ms_opus_packets_whatever_the_chunks() {
let mut encoder = Encoder::new(48_000).unwrap();
assert!(encoder.push(&[0; 500]).unwrap().is_empty());
assert_eq!(encoder.push(&[0; 1420]).unwrap().len(), 2);
}
#[test]
fn what_is_encoded_decodes_to_the_same_length() {
let mut encoder = Encoder::new(16_000).unwrap();
let tone: Vec<i16> = (0..320).map(|i| ((f64::from(i) * 0.3).sin() * 8000.0) as i16).collect();
let packet = encoder.push(&tone).unwrap().remove(0);
let mut decoder = Decoder::new(16_000).unwrap();
assert_eq!(decoder.decode(&packet).unwrap().len(), 320);
}
#[test]
fn a_rate_opus_does_not_know_is_refused() {
assert!(Encoder::new(44_100).is_err());
assert!(Decoder::new(44_100).is_err());
}
}