#![forbid(unsafe_code)]
use crate::{Demux, Mux};
use adts_core::{
AacProfile as CoreAacProfile, AdtsConfig, Demuxer as CoreDemuxer, Muxer as CoreMuxer,
};
use mediaway_common::{CodecKind, Packet, Rational, StreamInfo};
const SAMPLES_PER_FRAME: u64 = 1024;
pub type Error = adts_core::Error;
#[derive(Debug)]
pub struct Muxer {
inner: CoreMuxer,
output: Vec<u8>,
}
impl Muxer {
pub fn new(sample_rate: u32, channels: u8) -> Result<Self, Error> {
let inner = CoreMuxer::new(AdtsConfig {
profile: CoreAacProfile::Lc,
sample_rate,
channels,
})?;
Ok(Self {
inner,
output: Vec::new(),
})
}
pub fn push_packet(&mut self, packet: &Packet) -> Result<(), Error> {
self.inner.write_frame(&packet.payload, &mut self.output)
}
pub const fn flush(&self) {}
pub fn poll_bytes(&mut self, out: &mut Vec<u8>) -> usize {
let n = self.output.len();
out.extend_from_slice(&self.output);
self.output.clear();
n
}
}
#[allow(clippy::use_self)]
impl Mux for Muxer {
type Error = Error;
fn push_packet(&mut self, packet: &Packet) -> Result<(), Self::Error> {
Muxer::push_packet(self, packet)
}
fn flush(&mut self) {
Muxer::flush(self);
}
fn poll_bytes(&mut self, out: &mut Vec<u8>) -> usize {
Muxer::poll_bytes(self, out)
}
}
#[derive(Debug, Default)]
pub struct Demuxer {
inner: CoreDemuxer,
streams: Vec<StreamInfo>,
samples_seen: u64,
}
impl Demuxer {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn push_bytes(&mut self, chunk: &[u8]) {
self.inner.push_bytes(chunk);
}
#[must_use]
pub fn streams(&self) -> &[StreamInfo] {
&self.streams
}
pub fn poll_packet(&mut self) -> Option<Packet> {
let payload = self.inner.poll_frame().ok().flatten()?;
let config = self.inner.config()?;
if self.streams.is_empty() {
self.streams.push(StreamInfo::Audio {
id: 0,
codec: CodecKind::Aac,
time_base: Rational::new(1, config.sample_rate),
extra_data: mediaway_common::Bytes::new(),
sample_rate: config.sample_rate,
channels: u16::from(config.channels),
});
}
let pts = i64::try_from(self.samples_seen).unwrap_or(i64::MAX);
self.samples_seen += SAMPLES_PER_FRAME;
Some(Packet {
stream_id: 0,
pts,
dts: pts,
duration: SAMPLES_PER_FRAME,
is_keyframe: true,
is_discard: false,
payload,
})
}
}
#[allow(clippy::use_self)]
impl Demux for Demuxer {
fn push_bytes(&mut self, chunk: &[u8]) {
Demuxer::push_bytes(self, chunk);
}
fn streams(&self) -> &[StreamInfo] {
Demuxer::streams(self)
}
fn poll_packet(&mut self) -> Option<Packet> {
Demuxer::poll_packet(self)
}
}
#[cfg(test)]
#[path = "adts_tests.rs"]
mod tests;