#![forbid(unsafe_code)]
use crate::Demux;
use mediaway_common::{Bytes, CodecKind, Packet, Rational, StreamInfo};
use mpeg_ts_core::{Demuxer as CoreDemuxer, Muxer as CoreMuxer};
pub use mpeg_ts_core::{ElementaryStream, StreamType};
pub type Error = mpeg_ts_core::Error;
pub const TS_TIME_BASE: Rational = Rational::new(1, 90_000);
#[derive(Debug)]
pub struct Muxer {
inner: CoreMuxer,
}
impl Muxer {
pub fn new(
program_number: u16,
pmt_pid: u16,
streams: &[ElementaryStream],
) -> Result<Self, Error> {
Ok(Self {
inner: CoreMuxer::new(program_number, pmt_pid, streams)?,
})
}
pub fn write_pat_pmt(&mut self, out: &mut Vec<u8>) {
self.inner.write_pat_pmt(out);
}
pub fn write_access_unit(
&mut self,
pid: u16,
data: &[u8],
pts_90k: u64,
dts_90k: Option<u64>,
random_access: bool,
out: &mut Vec<u8>,
) -> Result<(), Error> {
self.inner
.write_access_unit(pid, data, pts_90k, dts_90k, random_access, out)
}
}
#[derive(Debug, Default)]
pub struct Demuxer {
inner: CoreDemuxer,
streams: Vec<StreamInfo>,
}
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> {
loop {
let Some(unit) = self.inner.poll_access_unit().ok().flatten() else {
self.sync_streams();
return None;
};
self.sync_streams();
if !self.streams.iter().any(|s| s.id() == u32::from(unit.pid)) {
continue; }
let pts = i64::try_from(unit.pts_90k).unwrap_or(i64::MAX);
let dts = unit
.dts_90k
.map_or(pts, |d| i64::try_from(d).unwrap_or(i64::MAX));
return Some(Packet {
stream_id: u32::from(unit.pid),
pts,
dts,
duration: 0, is_keyframe: unit.random_access,
is_discard: false,
payload: unit.data,
});
}
}
pub fn finish(&mut self) -> Vec<Packet> {
self.sync_streams();
self.inner
.finish()
.into_iter()
.filter(|u| self.streams.iter().any(|s| s.id() == u32::from(u.pid)))
.map(|unit| {
let pts = i64::try_from(unit.pts_90k).unwrap_or(i64::MAX);
let dts = unit
.dts_90k
.map_or(pts, |d| i64::try_from(d).unwrap_or(i64::MAX));
Packet {
stream_id: u32::from(unit.pid),
pts,
dts,
duration: 0,
is_keyframe: unit.random_access,
is_discard: false,
payload: unit.data,
}
})
.collect()
}
fn sync_streams(&mut self) {
let inner = self.inner.streams();
if inner.len() == self.streams.len() {
return;
}
self.streams = inner.iter().copied().filter_map(to_stream_info).collect();
}
}
#[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)
}
}
fn to_stream_info(s: ElementaryStream) -> Option<StreamInfo> {
let id = u32::from(s.pid);
match s.stream_type {
StreamType::H264 => Some(StreamInfo::Video {
id,
codec: CodecKind::H264,
time_base: TS_TIME_BASE,
geometry: mediaway_common::VideoGeometry {
width: 0,
height: 0,
},
extra_data: Bytes::new(),
}),
StreamType::Hevc => Some(StreamInfo::Video {
id,
codec: CodecKind::Hevc,
time_base: TS_TIME_BASE,
geometry: mediaway_common::VideoGeometry {
width: 0,
height: 0,
},
extra_data: Bytes::new(),
}),
StreamType::Aac => Some(StreamInfo::Audio {
id,
codec: CodecKind::Aac,
time_base: TS_TIME_BASE,
extra_data: Bytes::new(),
sample_rate: 0,
channels: 0,
}),
StreamType::Mp3 => Some(StreamInfo::Audio {
id,
codec: CodecKind::Mp3,
time_base: TS_TIME_BASE,
extra_data: Bytes::new(),
sample_rate: 0,
channels: 0,
}),
_ => None,
}
}
#[cfg(test)]
#[path = "ts_tests.rs"]
mod tests;