use crate::Result;
#[repr(Rust, packed)]
#[derive(Debug, Clone)]
pub struct OpusHead {
pub magic_signature: [u8; 8],
pub version: u8,
pub channel_count: u8,
pub pre_skip: u16,
pub sample_rate: u32,
pub output_gain: i16,
pub mapping_family: u8,
}
impl OpusHead {
pub fn from_slice(data: &[u8]) -> Result<Self> {
let l = std::mem::size_of::<OpusHead>();
if data.len() != l {
return Err(crate::Error::OggUnexpectedLenForOpusHead(data.len()));
}
let head: Self = unsafe { std::ptr::read_unaligned(data.as_ptr() as *const Self) };
if &head.magic_signature != b"OpusHead" {
return Err(crate::Error::OggUnexpectedSignature(head.magic_signature));
}
Ok(head)
}
}
const OPUS_ENCODER_FRAME_SIZE: usize = 960;
pub struct Encoder {
pw: ogg::PacketWriter<'static, Vec<u8>>,
encoder: opus2::Encoder,
total_data: usize,
sample_rate: usize,
header_data: Vec<u8>,
out_pcm: std::collections::VecDeque<f32>,
opus_buf: Vec<u8>,
}
fn write_opus_header<W: std::io::Write>(w: &mut W) -> std::io::Result<()> {
use byteorder::WriteBytesExt;
w.write_all(b"OpusHead")?;
w.write_u8(1)?; w.write_u8(1)?; w.write_u16::<byteorder::LittleEndian>(3840)?; w.write_u32::<byteorder::LittleEndian>(48000)?; w.write_i16::<byteorder::LittleEndian>(0)?; w.write_u8(0)?; Ok(())
}
fn write_opus_tags<W: std::io::Write>(w: &mut W) -> std::io::Result<()> {
use byteorder::WriteBytesExt;
let vendor = "KyutaiMoshi";
w.write_all(b"OpusTags")?;
w.write_u32::<byteorder::LittleEndian>(vendor.len() as u32)?; w.write_all(vendor.as_bytes())?; w.write_u32::<byteorder::LittleEndian>(0u32)?; Ok(())
}
impl Encoder {
pub fn new(sample_rate: usize) -> Result<Self> {
let encoder = opus2::Encoder::new(
sample_rate as u32,
opus2::Channels::Mono,
opus2::Application::Voip,
)?;
let all_data = Vec::new();
let mut pw = ogg::PacketWriter::new(all_data);
let mut head = Vec::new();
write_opus_header(&mut head)?;
pw.write_packet(head, 42, ogg::PacketWriteEndInfo::EndPage, 0)?;
let mut tags = Vec::new();
write_opus_tags(&mut tags)?;
pw.write_packet(tags, 42, ogg::PacketWriteEndInfo::EndPage, 0)?;
let header_data = {
let inner = pw.inner_mut();
let data = inner.to_vec();
inner.clear();
data
};
let out_pcm = std::collections::VecDeque::with_capacity(2 * OPUS_ENCODER_FRAME_SIZE);
let opus_buf = vec![0u8; 50_000];
Ok(Self { encoder, pw, header_data, total_data: 0, out_pcm, opus_buf, sample_rate })
}
pub fn header_data(&self) -> &[u8] {
self.header_data.as_slice()
}
pub fn encode_page(&mut self, pcm: &[f32]) -> Result<Vec<u8>> {
let mut encoded = vec![];
self.out_pcm.extend(pcm.iter());
let nchunks = self.out_pcm.len() / OPUS_ENCODER_FRAME_SIZE;
for _chunk_id in 0..nchunks {
let mut chunk = Vec::with_capacity(OPUS_ENCODER_FRAME_SIZE);
for _i in 0..OPUS_ENCODER_FRAME_SIZE {
let v = match self.out_pcm.pop_front() {
None => return Err(crate::Error::OpusMissingPcm),
Some(v) => v,
};
chunk.push(v)
}
self.total_data += chunk.len();
let size = self.encoder.encode_float(&chunk, &mut self.opus_buf)?;
let absgp = self.total_data as u64 * 48_000 / self.sample_rate as u64;
if size > 0 {
self.pw.write_packet(
self.opus_buf[..size].to_vec(),
42,
ogg::PacketWriteEndInfo::EndPage,
absgp,
)?;
let data = self.pw.inner_mut();
if !data.is_empty() {
encoded.extend_from_slice(data);
data.clear()
}
}
}
Ok(encoded)
}
}
pub struct AsyncDecoder {
pr_ogg: ogg::reading::async_api::PacketReader<tokio::io::DuplexStream>,
decoder: opus2::Decoder,
pcm_buf: Vec<f32>,
size_in_buf: usize,
flush_every_n_samples: usize,
}
pub type Sender = tokio::sync::mpsc::UnboundedSender<Vec<u8>>;
impl AsyncDecoder {
pub fn new(sample_rate: usize, flush_every_n_samples: usize) -> Result<(Self, Sender)> {
use tokio::io::AsyncWriteExt;
let pcm_buf = vec![0f32; flush_every_n_samples + sample_rate * 5];
let (mut tx_tokio, rx_tokio) = tokio::io::duplex(100_000);
let (tx_sync, mut rx_sync) = tokio::sync::mpsc::unbounded_channel::<Vec<u8>>();
let pr_ogg = ogg::reading::async_api::PacketReader::new(rx_tokio);
let decoder = opus2::Decoder::new(sample_rate as u32, opus2::Channels::Mono)?;
tokio::task::spawn(async move {
while let Some(data) = rx_sync.recv().await {
tx_tokio.write_all(&data).await?;
}
Ok::<_, crate::Error>(())
});
let s = Self { pr_ogg, decoder, pcm_buf, size_in_buf: 0, flush_every_n_samples };
Ok((s, tx_sync))
}
pub async fn read(&mut self) -> Result<Option<&[f32]>> {
use futures_util::StreamExt;
loop {
let packet = match self.pr_ogg.next().await {
None => return Ok(None),
Some(v) => v?,
};
if packet.data.starts_with(b"OpusHead") || packet.data.starts_with(b"OpusTags") {
continue;
}
let read_size = self.decoder.decode_float(
&packet.data,
&mut self.pcm_buf[self.size_in_buf..],
false,
)?;
self.size_in_buf += read_size;
if self.size_in_buf >= self.flush_every_n_samples {
let size_in_buf = self.size_in_buf;
self.size_in_buf = 0;
return Ok(Some(&self.pcm_buf[..size_in_buf]));
}
}
}
}
pub struct Decoder {
pr_ogg: crate::ogg_pager::PacketReader,
decoder: opus2::Decoder,
pcm_buf: Vec<f32>,
size_in_buf: usize,
flush_every_n_samples: usize,
}
impl Decoder {
pub fn new(sample_rate: usize, flush_every_n_samples: usize) -> Result<Self> {
let pcm_buf = vec![0f32; flush_every_n_samples + sample_rate * 5];
let pr_ogg = crate::ogg_pager::PacketReader::new();
let decoder = opus2::Decoder::new(sample_rate as u32, opus2::Channels::Mono)?;
let s = Self { pr_ogg, decoder, pcm_buf, size_in_buf: 0, flush_every_n_samples };
Ok(s)
}
pub fn decode(&mut self, data: &[u8]) -> Result<Option<&[f32]>> {
self.pr_ogg.append_bytes(data);
while let Some(packet) = self.pr_ogg.next()? {
if packet.starts_with(b"OpusHead") || packet.starts_with(b"OpusTags") {
continue;
}
let read_size = self.decoder.decode_float(
&packet,
&mut self.pcm_buf[self.size_in_buf..],
false,
)?;
self.size_in_buf += read_size;
}
let pcm = if self.size_in_buf >= self.flush_every_n_samples {
let size_in_buf = self.size_in_buf;
self.size_in_buf = 0;
Some(&self.pcm_buf[..size_in_buf])
} else {
None
};
Ok(pcm)
}
}