#![forbid(unsafe_code)]
use crate::Demux;
use mediaway_common::{Bytes, CodecKind, Packet, Rational, StreamInfo};
use mpeg_audio::{Demuxer as CoreDemuxer, FrameHeader, MpegVersion, Muxer as CoreMuxer};
pub type Error = mpeg_audio::Error;
#[must_use]
pub const fn samples_per_frame(version: MpegVersion) -> u64 {
match version {
MpegVersion::Mpeg1 => 1152,
_ => 576,
}
}
#[derive(Debug)]
pub struct Muxer {
inner: CoreMuxer,
}
impl Muxer {
pub fn new(header: FrameHeader) -> Result<Self, Error> {
Ok(Self {
inner: CoreMuxer::new(header)?,
})
}
pub fn write_frame(
&self,
frame_body: &[u8],
padding: bool,
out: &mut Vec<u8>,
) -> Result<(), Error> {
self.inner.write_frame(frame_body, padding, 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 header = self.inner.header()?;
if self.streams.is_empty() {
let channels = if matches!(header.channel_mode, mpeg_audio::ChannelMode::Mono) {
1
} else {
2
};
self.streams.push(StreamInfo::Audio {
id: 0,
codec: CodecKind::Mp3,
time_base: Rational::new(1, header.sample_rate),
extra_data: Bytes::new(),
sample_rate: header.sample_rate,
channels,
});
}
let frame_samples = samples_per_frame(header.version);
let pts = i64::try_from(self.samples_seen).unwrap_or(i64::MAX);
self.samples_seen += frame_samples;
Some(Packet {
stream_id: 0,
pts,
dts: pts,
duration: frame_samples,
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 = "mp3_tests.rs"]
mod tests;