use anyhow::{bail, Result};
use codec::frame::StreamInfo;
use crate::avi::demux_avi;
use crate::ts::demux_ts;
pub mod mp4;
pub mod mkv;
pub(crate) mod audio;
pub(crate) mod hdr;
#[cfg(test)]
mod tests;
pub use mp4::{demux_mp4, Mp4StreamingDemuxer};
pub use mkv::{demux_mkv, probe_mkv_color_info, MkvStreamingDemuxer};
pub(crate) use mkv::demux_mkv_streaming_init;
pub(crate) use mp4::demux_mp4_streaming_init;
pub struct DemuxResult {
pub codec: String,
pub info: StreamInfo,
pub samples: Vec<Vec<u8>>,
pub audio: Option<AudioTrack>,
}
#[derive(Debug, Clone)]
pub struct AudioTrack {
pub codec: String,
pub samples: Vec<Vec<u8>>,
pub sample_rate: u32,
pub channels: u16,
pub asc: Vec<u8>,
pub codec_private: Vec<u8>,
pub timescale: u32,
pub durations: Vec<u32>,
}
pub fn demux(data: &[u8]) -> Result<DemuxResult> {
match detect_container(data) {
"mp4" => demux_mp4(data),
"mkv" => demux_mkv(data),
"avi" => demux_avi(data),
"ts" => demux_ts(data),
other => bail!("unsupported container: {other}"),
}
}
pub(crate) fn detect_container(data: &[u8]) -> &'static str {
if data.len() < 12 {
return "unknown";
}
if &data[4..8] == b"ftyp" || &data[4..8] == b"moov" || &data[4..8] == b"mdat" {
return "mp4";
}
if data[0] == 0x1A && data[1] == 0x45 && data[2] == 0xDF && data[3] == 0xA3 {
return "mkv";
}
if &data[..4] == b"RIFF" && &data[8..12] == b"AVI " {
return "avi";
}
if data[0] == 0x47
&& data.len() > 188
&& data[188] == 0x47
&& (data.len() <= 376 || data[376] == 0x47)
{
return "ts";
}
"unknown"
}
pub(super) fn find_box_body<'a>(data: &'a [u8], path: &[&[u8; 4]]) -> Option<&'a [u8]> {
let mut slice = data;
for (i, target) in path.iter().enumerate() {
let found = find_direct_child(slice, target)?;
if i + 1 == path.len() {
return Some(found);
}
slice = found;
}
None
}
pub(super) fn find_direct_child<'a>(data: &'a [u8], target: &[u8; 4]) -> Option<&'a [u8]> {
let mut pos = 0;
while pos + 8 <= data.len() {
let size =
u32::from_be_bytes([data[pos], data[pos + 1], data[pos + 2], data[pos + 3]]) as usize;
let btype = &data[pos + 4..pos + 8];
if size < 8 || pos.checked_add(size).is_none_or(|end| end > data.len()) {
return None;
}
if btype == target {
return Some(&data[pos + 8..pos + size]);
}
pos += size;
}
None
}