use crate::catalog::hang::CatalogExt;
use crate::container::Frame;
use crate::container::Timestamp;
#[derive(Debug, Clone, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("AC-3 header needs 7 bytes")]
Ac3HeaderTooShort,
#[error("missing AC-3 sync word")]
Ac3MissingSyncWord,
#[error("invalid AC-3 frame size code")]
Ac3InvalidFrameSizeCode,
#[error("unsupported AC-3 bsid {0}")]
Ac3UnsupportedBsid(u8),
#[error("reserved AC-3 sample-rate code")]
Ac3ReservedSampleRate,
#[error("E-AC-3 header needs 6 bytes")]
Eac3HeaderTooShort,
#[error("missing E-AC-3 sync word")]
Eac3MissingSyncWord,
#[error("not an E-AC-3 bitstream (bsid {0})")]
Eac3NotEac3Bsid(u8),
#[error("reserved E-AC-3 stream type")]
Eac3ReservedStreamType,
#[error("E-AC-3 dependent substream (7.1+ layout) is not supported; only a single independent substream")]
Eac3DependentSubstream,
#[error("E-AC-3 additional substream {0} is not supported; only a single independent substream")]
Eac3AdditionalSubstream(u8),
#[error("E-AC-3 frame length {0} shorter than its header")]
Eac3FrameShorterThanHeader(usize),
#[error("reserved E-AC-3 sample-rate code")]
Eac3ReservedSampleRate,
#[error("MP2 header needs 4 bytes")]
Mp2HeaderTooShort,
#[error("missing MP2 frame sync")]
Mp2MissingSync,
#[error("reserved or MPEG-2.5 audio version")]
Mp2ReservedVersion,
#[error("not MPEG Layer II")]
Mp2NotLayerII,
#[error("reserved MP2 sample-rate index")]
Mp2ReservedSampleRate,
#[error("free-format or invalid MP2 bitrate")]
Mp2InvalidBitrate,
}
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub(crate) struct Header {
pub len: usize,
pub sample_rate: u32,
pub channel_count: u32,
pub samples: u64,
}
pub(crate) struct Descriptor {
pub track_suffix: &'static str,
pub codec: hang::catalog::AudioCodec,
pub min_header_len: usize,
pub parse: fn(&[u8]) -> Result<Header>,
}
pub(crate) struct Config {
pub sample_rate: u32,
pub channel_count: u32,
}
pub(crate) struct Import<E: CatalogExt = ()> {
track: crate::container::Producer<crate::catalog::hang::Container>,
rendition: crate::catalog::AudioTrack<E>,
}
impl<E: CatalogExt> Import<E> {
pub fn new(
descriptor: &'static Descriptor,
track: moq_net::TrackProducer,
catalog: crate::catalog::Producer<E>,
config: Config,
) -> Self {
let mut audio_config =
hang::catalog::AudioConfig::new(descriptor.codec.clone(), config.sample_rate, config.channel_count);
audio_config.container = hang::catalog::Container::Legacy;
tracing::debug!(name = ?track.name(), config = ?audio_config, "starting track");
let mut rendition = catalog.audio_track(track.name());
rendition.set(audio_config);
Self {
track: catalog.media_producer(track, crate::catalog::hang::Container::Legacy),
rendition,
}
}
pub fn name(&self) -> &str {
self.track.name()
}
pub fn finish(&mut self) -> crate::Result<()> {
self.track.finish()?;
Ok(())
}
pub fn seek(&mut self, sequence: u64) -> crate::Result<()> {
self.track.seek(sequence)?;
Ok(())
}
pub fn decode(&mut self, frame: &[u8], pts: Option<Timestamp>) -> crate::Result<()> {
let timestamp = self.rendition.timestamp(pts)?;
self.track.write(Frame {
timestamp,
duration: None,
payload: bytes::Bytes::copy_from_slice(frame),
keyframe: true,
})?;
self.track.finish_group()?;
Ok(())
}
}