--- /dev/null
+use nihav_core::demuxers::*;
+
+#[allow(clippy::neg_cmp_op_on_partial_ord)]
+mod parse;
+use parse::*;
+
+const TIMEBASE: u32 = 1_000_000_000;
+
+struct MSeekInfo {
+ time: u64,
+ tracks: Vec<(u32, u64, u64)>,
+}
+
+#[allow(dead_code)]
+struct Track {
+ id: u32,
+ codec: String,
+ lacing: bool,
+}
+
+struct Segment {
+ start: u64,
+ end: u64,
+ clu_start: u64,
+ duration: f64,
+ tscale: u32,
+ tracks: Vec<Track>,
+ seek_tab: Vec<MSeekInfo>,
+}
+
+impl std::convert::From<SegmentInfo> for Segment {
+ fn from(mut seg: SegmentInfo) -> Self {
+ let tracks = seg.tracks.drain(..).map(|x| x.into()).collect();
+
+ Self {
+ start: seg.start,
+ end: seg.end,
+ clu_start: seg.clu_start,
+ duration: seg.duration,
+ tscale: seg.tscale,
+ tracks,
+ seek_tab: Vec::new(),
+ }
+ }
+}
+
+impl std::convert::From<TrackInfo> for Track {
+ fn from(trk: TrackInfo) -> Self {
+ Self {
+ id: trk.number.unwrap() as u32,
+ lacing: trk.lacing.unwrap_or(false),
+ codec: trk.codec_id.unwrap(),
+ }
+ }
+}
+
+impl std::convert::From<CueEntry> for MSeekInfo {
+ fn from(cue: CueEntry) -> Self {
+ Self {
+ time: cue.time.unwrap(),
+ tracks: cue.tracks,
+ }
+ }
+}
+
+struct MatroskaDemuxer<'a> {
+ src: &'a mut dyn ByteIO,
+ segments: Vec<Segment>,
+
+ cur_seg: usize,
+ seg_end: Option<u64>,
+ clu_end: Option<u64>,
+ clu_ts: Option<u64>,
+
+ print_chunks: bool,
+}
+
+impl<'a> DemuxCore<'a> for MatroskaDemuxer<'a> {
+ fn open(&mut self, strmgr: &mut StreamManager, seek_index: &mut SeekIndex) -> DemuxerResult<()> {
+ let master_hdr = self.src.read_ebml_id()?;
+ validate!(master_hdr == 0x1A45DFA3);
+ let size = self.src.read_vint()?;
+ let hdr_end = self.src.tell() + size;
+ if self.print_chunks {
+ println!("master header size {size:X} @ 0");
+ }
+
+ let debuglevel = if self.print_chunks { Some(1) } else { None };
+ let mut hdr = Header::default();
+ parse_entries(&mut hdr, debuglevel, self.src, PARSE_MASTER_HEADER, hdr_end, ParseMode::Ignore)?;
+ validate!(hdr.version == Some(1) && hdr.read_ver.unwrap_or(1) == 1);
+
+ self.segments = Vec::new();
+ while let Ok(segment_id) = self.src.read_ebml_id() {
+ let seg_start = self.src.tell() - 4;
+ validate!(segment_id == 0x18538067);
+ let size = self.src.read_vint()?;
+ let seg_end = self.src.tell() + size;
+ if self.print_chunks {
+ println!("segment start @ {seg_start:X}");
+ }
+
+ let mut segment = SegmentInfo::new(self.print_chunks, self.src.tell(), seg_end);
+ parse_entries(&mut segment, debuglevel, self.src, PARSE_SEGMENT, seg_end, ParseMode::StopAt(CLUSTER_ID))?;
+ segment.clu_start = self.src.tell();
+
+ let mut cues = Vec::new();
+ if let Some(entry) = segment.seek_entries.iter().find(|x| x.0 == CUES_ID) {
+ if self.print_chunks {
+ println!("cues at @{:X}", entry.1 + segment.start);
+ }
+ let _ = parse_cues(self.src, entry.1 + segment.start, seg_end, self.print_chunks, &mut cues);
+ }
+
+ self.src.seek(SeekFrom::Start(seg_end))?;
+ segment.validate()?;
+ segment.fill_streams(strmgr)?;
+ let mut new_seg = Segment::from(segment);
+ new_seg.seek_tab = cues.drain(..).map(|x| x.into()).collect();
+ let mut prev_time = None;
+ for cue in new_seg.seek_tab.iter() {
+ if let Some(pval) = prev_time {
+ validate!(pval < cue.time);
+ }
+ prev_time = Some(cue.time);
+ }
+ self.segments.push(new_seg);
+ }
+ validate!(!self.segments.is_empty());
+ self.cur_seg = 0;
+ self.clu_end = None;
+ self.clu_ts = None;
+ seek_index.mode = SeekIndexMode::Automatic;
+ Ok(())
+ }
+
+ fn get_frame(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<NAPacket> {
+ 'main_loop: loop {
+ if let Some(clu_end) = self.clu_end {
+ while self.src.tell() < clu_end {
+ let off = self.src.tell();
+ let id = self.src.read_ebml_id()?;
+ let size = self.src.read_vint()?;
+ let end = self.src.tell() + size;
+ if self.print_chunks {
+ print_entry(2, off, id, size);
+ }
+ validate!(end <= clu_end);
+ match id {
+ CLUSTER_TIMESTAMP_ID => {
+ validate!(self.clu_ts.is_none());
+ let ts = self.src.read_uint(size)?;
+ if self.print_chunks {
+ println!(" cluster timestamp {ts}");
+ }
+ self.clu_ts = Some(ts);
+ },
+ 0xA0 => return Err(DemuxerError::NotImplemented), // BlockGroup
+ 0xA3 => {
+ validate!(self.clu_ts.is_some());
+ let trk_id = self.src.read_vint()?;
+ let timestamp = self.src.read_u16be()?;
+ let flags = self.src.read_byte()?;
+ let full_ts = self.clu_ts.unwrap() + u64::from(timestamp);
+
+ let keyframe = (flags & 0x80) != 0;
+ let invisible = (flags & 0x08) != 0;
+ let lacing = (flags >> 1) & 3;
+ let disposable = (flags & 0x01) != 0;
+ if self.print_chunks {
+ println!(" simple block - track {trk_id} ts {full_ts} ({timestamp}) {}{}{}{}", if keyframe { " key" } else {""}, if invisible { " invisible" } else {""}, if lacing != 0 { " laced" } else {""}, if disposable { " disposable" } else {""});
+ }
+ if trk_id > U32_MAX { return Err(DemuxerError::NotImplemented); }
+ let trk_id = trk_id as u32;
+ validate!(self.src.tell() < end);
+ if lacing != 0 {
+ if let Some(trk) = self.segments[self.cur_seg].tracks.iter().find(|x| x.id == trk_id) {
+ validate!(trk.lacing);
+ }
+ return Err(DemuxerError::NotImplemented);
+ }
+ if let Some(stream) = strmgr.get_stream_by_id(trk_id) {
+ let pkt_size = (end - self.src.tell()) as usize;
+ let ts = NATimeInfo::new(Some(full_ts), None, None, stream.tb_num, stream.tb_den);
+ return self.src.read_packet(stream, ts, keyframe, pkt_size);
+ }
+ },
+ _ => {},
+ }
+ validate!(self.src.tell() <= end);
+ self.src.seek(SeekFrom::Start(end))?;
+ }
+ self.clu_end = None;
+ }
+ if self.seg_end.is_none() {
+ if self.cur_seg >= self.segments.len() {
+ return Err(DemuxerError::EOF);
+ }
+ self.seg_end = Some(self.segments[self.cur_seg].end);
+ self.src.seek(SeekFrom::Start(self.segments[self.cur_seg].clu_start))?;
+ }
+ let seg_end = self.seg_end.unwrap();
+ while self.src.tell() < seg_end {
+ let off = self.src.tell();
+ let id = self.src.read_ebml_id()?;
+ let size = self.src.read_vint()?;
+ let end = self.src.tell() + size;
+ if self.print_chunks {
+ print_entry(1, off, id, size);
+ if id == CLUSTER_ID {
+ println!(" cluster");
+ }
+ }
+ validate!(end <= seg_end);
+ if id == CLUSTER_ID {
+ self.clu_end = Some(end);
+ self.clu_ts = None;
+ continue 'main_loop;
+ }
+ self.src.seek(SeekFrom::Start(end))?;
+ }
+ self.seg_end = None;
+ self.cur_seg += 1;
+ }
+ }
+
+ fn seek(&mut self, time: NATimePoint, _seek_index: &SeekIndex) -> DemuxerResult<()> {
+ let mut tgt_seg = None;
+ let mut entry_no = None;
+ for (seg_no, seg) in self.segments.iter().enumerate() {
+ let seek_time = match time {
+ NATimePoint::Milliseconds(ms) => NATimeInfo::rescale_ts(ms, 1, 1000, seg.tscale, TIMEBASE),
+ NATimePoint::PTS(ts) => ts,
+ _ => return Err(DemuxerError::SeekError),
+ };
+
+ for (i, entry) in seg.seek_tab.iter().enumerate() {
+ if seek_time >= entry.time {
+ tgt_seg = Some(seg_no);
+ entry_no = Some(i);
+ } else {
+ break;
+ }
+ }
+ }
+ if let (Some(seg_no), Some(entry_no)) = (tgt_seg, entry_no) {
+ self.seg_end = None;
+ self.clu_end = None;
+ self.clu_ts = None;
+ self.cur_seg = self.segments.len();
+
+ let tgt_seg = &self.segments[seg_no];
+
+ let (_, clu_pos, clu_off) = tgt_seg.seek_tab[entry_no].tracks[0];
+ self.src.seek(SeekFrom::Start(tgt_seg.start + clu_pos))?;
+ let id = self.src.read_ebml_id()?;
+ let size = self.src.read_vint()?;
+ let clu_start = self.src.tell();
+ let clu_end = clu_start + size;
+ validate!(id == CLUSTER_ID && clu_end <= tgt_seg.end && clu_off < size);
+ let mut ts = None;
+ while self.src.tell() < clu_end {
+ let id = self.src.read_ebml_id()?;
+ let size = self.src.read_vint()?;
+ let end = self.src.tell() + size;
+ validate!(end <= clu_end);
+ if id == CLUSTER_TIMESTAMP_ID {
+ ts = Some(self.src.read_uint(size)?);
+ break;
+ }
+ self.src.seek(SeekFrom::Start(end))?;
+ }
+ validate!(ts.is_some());
+ self.src.seek(SeekFrom::Start(clu_start + clu_off))?;
+ self.cur_seg = seg_no;
+ self.seg_end = Some(tgt_seg.end);
+ self.clu_end = Some(clu_end);
+ self.clu_ts = ts;
+
+ Ok(())
+ } else {
+ Err(DemuxerError::SeekError)
+ }
+ }
+ fn get_duration(&self) -> u64 {
+ self.segments.iter().fold(0u64,
+ |acc, seg| acc + ((seg.duration * f64::from(seg.tscale) / f64::from(TIMEBASE / 1000)) as u64))
+ }
+}
+
+const PRINT_CHUNKS: &str = "print_chunks";
+
+const DEMUXER_OPTIONS: &[NAOptionDefinition] = &[
+ NAOptionDefinition {
+ name: PRINT_CHUNKS,
+ description: "Print parsed file structure",
+ opt_type: NAOptionDefinitionType::Bool },
+];
+
+impl<'a> NAOptionHandler for MatroskaDemuxer<'a> {
+ fn get_supported_options(&self) -> &[NAOptionDefinition] { DEMUXER_OPTIONS }
+ #[allow(clippy::single_match)]
+ fn set_options(&mut self, options: &[NAOption]) {
+ for option in options.iter() {
+ for opt_def in DEMUXER_OPTIONS.iter() {
+ if opt_def.check(option).is_ok() {
+ match (option.name, &option.value) {
+ (PRINT_CHUNKS, NAValue::Bool(val)) => {
+ self.print_chunks = *val;
+ },
+ _ => {},
+ }
+ }
+ }
+ }
+ }
+ fn query_option_value(&self, name: &str) -> Option<NAValue> {
+ match name {
+ PRINT_CHUNKS => Some(NAValue::Bool(self.print_chunks)),
+ _ => None,
+ }
+ }
+}
+
+impl<'a> MatroskaDemuxer<'a> {
+ fn new(io: &'a mut dyn ByteIO) -> Self {
+ MatroskaDemuxer {
+ src: io,
+ print_chunks: false,
+ segments: Vec::new(),
+ cur_seg: 0,
+ seg_end: None,
+ clu_end: None,
+ clu_ts: None,
+ }
+ }
+}
+
+pub struct MatroskaDemuxerCreator { }
+
+impl DemuxerCreator for MatroskaDemuxerCreator {
+ fn new_demuxer<'a>(&self, br: &'a mut dyn ByteIO) -> Box<dyn DemuxCore<'a> + 'a> {
+ Box::new(MatroskaDemuxer::new(br))
+ }
+ fn get_name(&self) -> &'static str { "matroska" }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use std::fs::File;
+ use nihav_codec_support::test::dmx_test::*;
+
+ fn test_core(name: &str, nframes: usize, test_mode: DemuxerTestMode) {
+ let mut file = File::open(name).unwrap();
+ let mut br = FileReader::new_read(&mut file);
+ let mut dmx = MatroskaDemuxer::new(&mut br);
+ let mut sm = StreamManager::new();
+ let mut si = SeekIndex::new();
+ dmx.open(&mut sm, &mut si).unwrap();
+ test_demuxer(&mut dmx, &mut sm, nframes, test_mode);
+ }
+
+ #[test]
+ // sample from https://www.webmfiles.org/demo-files/
+ fn test_webm() {
+ test_core("assets/Misc/big-buck-bunny_trailer.webm", 10, DemuxerTestMode::Test(vec![
+ DPacket::new(1, Some( 0), None, 431, true),
+ DPacket::new(2, Some( 0), None, 22, false),
+ DPacket::new(2, Some( 13), None, 151, false),
+ DPacket::new(2, Some( 36), None, 144, false),
+ DPacket::new(1, Some( 40), None, 66, false),
+ DPacket::new(2, Some( 59), None, 155, false),
+ DPacket::new(1, Some( 80), None, 1339, false),
+ DPacket::new(2, Some( 82), None, 156, false),
+ DPacket::new(2, Some(105), None, 152, false),
+ DPacket::new(1, Some(120), None, 1415, false)]));
+ }
+}
--- /dev/null
+use super::*;
+
+pub const CLUSTER_ID: u32 = 0x1F43B675;
+pub const CLUSTER_TIMESTAMP_ID: u32 = 0xE7;
+pub const CUES_ID: u32 = 0x1C53BB6B;
+
+pub const U32_MAX: u64 = u32::MAX as u64;
+
+fn gcd(mut a: u32, mut b: u32) -> u32 {
+ while a != 0 && b != 0 && a != b {
+ let minab = a.min(b);
+ a = a.max(b) - minab;
+ b = minab;
+ }
+ a.max(b)
+}
+
+pub trait ReadEBML {
+ fn read_ebml_id(&mut self) -> DemuxerResult<u32>;
+ fn read_vint(&mut self) -> DemuxerResult<u64>;
+ fn read_uint(&mut self, size: u64) -> DemuxerResult<u64>;
+ fn read_float(&mut self, size: u64) -> DemuxerResult<f64>;
+}
+
+impl<T: ?Sized + ByteIO> ReadEBML for T {
+ fn read_ebml_id(&mut self) -> DemuxerResult<u32> {
+ let b = self.read_byte()?;
+ validate!(b != 0xFF);
+ let (len, id) = match b >> 3 {
+ 0 => return Err(DemuxerError::InvalidData),
+ 1 => { // 0000 1 + 37 bits
+ let low = self.read_u32be()?;
+ (37, (u64::from(b & 0x7) << 32) | u64::from(low))
+ },
+ 2 | 3 => { // 0001 + 28 bits
+ let low = self.read_u24be()?;
+ (28, (u64::from(b & 0xF) << 24) | u64::from(low))
+ },
+ 4..=7 => { // 001 + 21 bit
+ let low = self.read_u16be()?;
+ (21, (u64::from(b & 0x1F) << 16) | u64::from(low))
+ },
+ 8..=15 => { // 01 + 14 bits
+ let low = self.read_byte()?;
+ (14, (u64::from(b & 0x3F) << 8) | u64::from(low))
+ },
+ _ => { // 1 + 7 bits
+ (7, u64::from(b & 0x7F))
+ }
+ };
+ validate!(id != 0);
+ validate!((id >> 32) == 0);
+ Ok((1 << len) | (id as u32))
+ }
+ fn read_vint(&mut self) -> DemuxerResult<u64> {
+ let b = self.read_byte()?;
+ match b {
+ 0 => Err(DemuxerError::InvalidData),
+ 0x80..=0xFF => { // 1 + 7 bits
+ Ok(u64::from(b & 0x7F))
+ },
+ 0x40..=0x7F => { // 01 + 14 bits
+ let low = self.read_byte()?;
+ Ok((u64::from(b & 0x3F) << 8) | u64::from(low))
+ },
+ 0x20..=0x3F => { // 001 + 21 bit
+ let low = self.read_u16be()?;
+ Ok((u64::from(b & 0x1F) << 16) | u64::from(low))
+ },
+ 0x10..=0x1F => { // 0001 + 28 bits
+ let low = self.read_u24be()?;
+ Ok((u64::from(b & 0xF) << 24) | u64::from(low))
+ },
+ 0x08..=0x0F => { // 0000 1 + 37 bits
+ let low = self.read_u32be()?;
+ Ok((u64::from(b & 0x7) << 32) | u64::from(low))
+ },
+ _ => {
+ let len = b.leading_zeros();
+ let mut val = if len < 7 { u64::from((b << (len + 1)) >> (len + 1)) } else { 0 };
+ for _ in 0..len {
+ val <<= 8;
+ val |= u64::from(self.read_byte()?);
+ }
+ Ok(val)
+ },
+ }
+ }
+ fn read_uint(&mut self, size: u64) -> DemuxerResult<u64> {
+ match size {
+ 1 => Ok(u64::from(self.read_byte()?)),
+ 2 => Ok(u64::from(self.read_u16be()?)),
+ 3 => Ok(u64::from(self.read_u24be()?)),
+ 4 => Ok(u64::from(self.read_u32be()?)),
+ 5..=7 => Err(DemuxerError::NotImplemented),
+ 8 => Ok( self.read_u64be()?),
+ _ => Err(DemuxerError::InvalidData),
+ }
+ }
+ fn read_float(&mut self, size: u64) -> DemuxerResult<f64> {
+ match size {
+ 4 => Ok(f64::from(self.read_f32be()?)),
+ 8 => Ok( self.read_f64be()?),
+ _ => Err(DemuxerError::InvalidData),
+ }
+ }
+}
+
+macro_rules! read_field {
+ (uint; $name:ident, $field:ident) => {
+ fn $name(&mut self, br: &mut dyn ByteIO, size: u64) -> DemuxerResult<()> {
+ validate!(self.$field.is_none());
+ self.$field = Some(br.read_uint(size)?);
+ Ok(())
+ }
+ };
+ (float; $name:ident, $field:ident) => {
+ fn $name(&mut self, br: &mut dyn ByteIO, size: u64) -> DemuxerResult<()> {
+ validate!(self.$field.is_none());
+ self.$field = Some(br.read_float(size)?);
+ Ok(())
+ }
+ };
+ (bool; $name:ident, $field:ident) => {
+ fn $name(&mut self, br: &mut dyn ByteIO, size: u64) -> DemuxerResult<()> {
+ validate!(self.$field.is_none());
+ let val = br.read_uint(size)?;
+ validate!(val == 0 || val == 1);
+ self.$field = Some(val == 1);
+ Ok(())
+ }
+ };
+ (byte; $name:ident, $field:ident) => {
+ fn $name(&mut self, br: &mut dyn ByteIO, size: u64) -> DemuxerResult<()> {
+ validate!(self.$field.is_none());
+ let val = br.read_uint(size)?;
+ validate!((0..=255).contains(&val));
+ self.$field = Some(val as u8);
+ Ok(())
+ }
+ };
+}
+
+#[derive(Clone,Copy,Debug,PartialEq)]
+#[allow(dead_code)]
+pub enum ParseMode {
+ Ignore,
+ Stop,
+ StopAt(u32),
+ StopAfter(u32),
+}
+
+pub struct ParseEntry<T> {
+ id: u32,
+ parse: fn (&mut T, br: &mut dyn ByteIO, size: u64) -> DemuxerResult<()>,
+}
+
+pub fn print_entry(depth: u8, off: u64, id: u32, size: u64) {
+ for _ in 0..depth {
+ print!(" ");
+ }
+ println!("id {id:X} size {size:X} @ {off:X}");
+}
+
+pub fn parse_entries<T>(obj: &mut T, debug: Option<u8>, br: &mut dyn ByteIO, entries: &[ParseEntry<T>], end: u64, mode: ParseMode) -> DemuxerResult<()> {
+ while br.tell() < end {
+ let off = br.tell();
+ let id = br.read_ebml_id()?;
+ let size = br.read_vint()?;
+ if let Some(dlev) = debug {
+ print_entry(dlev, off, id, size);
+ }
+ if mode == ParseMode::StopAt(id) {
+ br.seek(SeekFrom::Start(off))?;
+ return Ok(());
+ }
+ let cur_end = br.tell() + size;
+ validate!(cur_end <= end);
+ let mut found = false;
+ for pe in entries.iter() {
+ if id == pe.id {
+ (pe.parse)(obj, br, size)?;
+ found = true;
+ break;
+ }
+ }
+ validate!(br.tell() <= cur_end);
+ if !found {
+ match id {
+ 0xBF => { // CRC
+ validate!(size == 4);
+ br.read_u32be()?;
+ found = true;
+ },
+ 0xEC => { // void
+ found = true;
+ },
+ _ => {},
+ }
+ }
+ br.seek(SeekFrom::Start(cur_end))?;
+ match mode {
+ ParseMode::Stop if !found => return Ok(()),
+ ParseMode::StopAfter(pid) if pid == id => return Ok(()),
+ _ => {},
+ }
+ }
+ validate!(br.tell() == end);
+ Ok(())
+}
+
+#[derive(Default)]
+pub struct Header {
+ pub version: Option<u8>,
+ pub read_ver: Option<u8>,
+}
+
+pub static PARSE_MASTER_HEADER: &[ParseEntry<Header>] = &[
+ ParseEntry { id: 0x4286, parse: Header::parse_version },
+ ParseEntry { id: 0x42F7, parse: Header::parse_read_version },
+ ParseEntry { id: 0x42F2, parse: Header::parse_max_id },
+ ParseEntry { id: 0x42F3, parse: Header::parse_max_size },
+];
+
+impl Header {
+ fn parse_version(&mut self, br: &mut dyn ByteIO, size: u64) -> DemuxerResult<()> {
+ validate!(size == 1 && self.version.is_none());
+ self.version = Some(br.read_byte()?);
+ Ok(())
+ }
+ fn parse_read_version(&mut self, br: &mut dyn ByteIO, size: u64) -> DemuxerResult<()> {
+ validate!(size == 1 && self.read_ver.is_none());
+ self.read_ver = Some(br.read_byte()?);
+ Ok(())
+ }
+ fn parse_max_id(&mut self, br: &mut dyn ByteIO, size: u64) -> DemuxerResult<()> {
+ validate!(size == 1);
+ let len = br.read_byte()?;
+ if len == 4 { Ok(()) } else { Err(DemuxerError::NotImplemented) }
+ }
+ fn parse_max_size(&mut self, br: &mut dyn ByteIO, size: u64) -> DemuxerResult<()> {
+ validate!(size == 1);
+ let len = br.read_byte()?;
+ match len {
+ 0 => Err(DemuxerError::InvalidData),
+ 1..=8 => Ok(()),
+ _ => Err(DemuxerError::NotImplemented)
+ }
+ }
+}
+
+#[derive(Default)]
+pub struct SeekHead {
+ pub id: Option<u32>,
+ pub pos: Option<u64>,
+}
+
+static PARSE_SEEK_HEAD: &[ParseEntry<SeekHead>] = &[
+ ParseEntry { id: 0x53AB, parse: SeekHead::parse_id },
+ ParseEntry { id: 0x53AC, parse: SeekHead::parse_pos },
+];
+
+impl SeekHead {
+ fn parse_id(&mut self, br: &mut dyn ByteIO, size: u64) -> DemuxerResult<()> {
+ validate!(size == 4 && self.id.is_none());
+ self.id = Some(br.read_u32be()?);
+ Ok(())
+ }
+ read_field!(uint; parse_pos, pos);
+}
+
+#[derive(Default)]
+pub struct Info {
+ pub tscale: u32,
+ pub uuid: [u8; 16],
+ pub duration: f64,
+}
+
+static PARSE_INFO: &[ParseEntry<Info>] = &[
+ ParseEntry { id: 0x2AD7B1, parse: Info::parse_timescale },
+ ParseEntry { id: 0x73A4, parse: Info::parse_uuid },
+ ParseEntry { id: 0x4489, parse: Info::parse_duration },
+];
+
+impl Info {
+ pub fn new() -> Self {
+ Self { tscale: 1000000, ..Default::default() }
+ }
+ fn parse_timescale(&mut self, br: &mut dyn ByteIO, size: u64) -> DemuxerResult<()> {
+ let ts = br.read_uint(size)?;
+ validate!((1..=U32_MAX).contains(&ts));
+ self.tscale = ts as u32;
+ Ok(())
+ }
+ fn parse_uuid(&mut self, br: &mut dyn ByteIO, size: u64) -> DemuxerResult<()> {
+ validate!(size == 16);
+ br.read_buf(&mut self.uuid)?;
+ Ok(())
+ }
+ fn parse_duration(&mut self, br: &mut dyn ByteIO, size: u64) -> DemuxerResult<()> {
+ self.duration = br.read_float(size)?;
+ Ok(())
+ }
+}
+
+#[derive(Default)]
+pub struct TrackInfo {
+ pub debug: bool,
+ pub number: Option<u64>,
+ pub trktype: Option<StreamType>,
+ pub lacing: Option<bool>,
+ pub duration: Option<u64>,
+ pub tscale: Option<f64>,
+ pub codec_id: Option<String>,
+ pub edata: Option<Vec<u8>>,
+ pub width: Option<u64>,
+ pub height: Option<u64>,
+ pub rate: Option<f64>,
+ pub real_rate: Option<f64>,
+ pub channels: Option<u8>,
+ pub adepth: Option<u8>,
+}
+
+static PARSE_TRACK: &[ParseEntry<TrackInfo>] = &[
+ ParseEntry { id: 0xD7, parse: TrackInfo::parse_number },
+ //ParseEntry { id: 0x73C5, parse: TrackInfo::parse_uid },
+ ParseEntry { id: 0x83, parse: TrackInfo::parse_type },
+ ParseEntry { id: 0x9C, parse: TrackInfo::parse_lacing },
+ ParseEntry { id: 0x23E383, parse: TrackInfo::parse_duration },
+ ParseEntry { id: 0x23314F, parse: TrackInfo::parse_tscale },
+ ParseEntry { id: 0x86, parse: TrackInfo::parse_codec_id },
+ ParseEntry { id: 0x63A2, parse: TrackInfo::parse_edata },
+ ParseEntry { id: 0xE0, parse: TrackInfo::parse_video_elem },
+ ParseEntry { id: 0xE1, parse: TrackInfo::parse_audio_elem },
+];
+
+static PARSE_TRACK_VIDEO_ELEM: &[ParseEntry<TrackInfo>] = &[
+ ParseEntry { id: 0xB0, parse: TrackInfo::parse_width },
+ ParseEntry { id: 0xBA, parse: TrackInfo::parse_height },
+];
+
+static PARSE_TRACK_AUDIO_ELEM: &[ParseEntry<TrackInfo>] = &[
+ ParseEntry { id: 0xB5, parse: TrackInfo::parse_sample_rate },
+ ParseEntry { id: 0x78B5, parse: TrackInfo::parse_real_sample_rate },
+ ParseEntry { id: 0x9F, parse: TrackInfo::parse_channels },
+ ParseEntry { id: 0x6264, parse: TrackInfo::parse_adepth },
+];
+
+impl TrackInfo {
+ pub fn validate(&self) -> DemuxerResult<()> {
+ validate!(self.number.is_some());
+ if let Some(id) = self.number {
+ validate!(id > 0);
+ }
+ validate!(self.codec_id.is_some() && self.trktype.is_some());
+ if let Some(ts) = self.tscale {
+ validate!(ts > 0.0);
+ }
+ if let Some(dur) = self.duration {
+ validate!(dur > 0);
+ }
+ match self.trktype {
+ Some(StreamType::Video) => {
+ validate!(self.width.is_some() && self.height.is_some());
+ if let (Some(w), Some(h)) = (self.width, self.height) {
+ validate!((1..=16384).contains(&w) && (1..=16384).contains(&h));
+ }
+ },
+ Some(StreamType::Audio) => {
+ validate!(self.rate.is_some());
+ if let Some(rate) = self.rate {
+ validate!((4000.0..=512000.0).contains(&rate));
+ }
+ if let Some(rrate) = self.real_rate {
+ validate!((4000.0..=512000.0).contains(&rrate));
+ }
+ if let Some(chan) = self.channels {
+ validate!((1..=128).contains(&chan));
+ }
+ if let Some(depth) = self.adepth {
+ validate!((1..=64).contains(&depth));
+ }
+ },
+ _ => {},
+ }
+ Ok(())
+ }
+
+ read_field!(uint; parse_number, number);
+ read_field!(uint; parse_duration, duration);
+ read_field!(float; parse_tscale, tscale);
+ read_field!(bool; parse_lacing, lacing);
+ read_field!(uint; parse_width, width);
+ read_field!(uint; parse_height, height);
+ read_field!(float; parse_sample_rate, rate);
+ read_field!(float; parse_real_sample_rate, real_rate);
+ read_field!(byte; parse_channels, channels);
+ read_field!(byte; parse_adepth, adepth);
+
+ fn parse_type(&mut self, br: &mut dyn ByteIO, size: u64) -> DemuxerResult<()> {
+ validate!(self.trktype.is_none());
+ let id = br.read_uint(size)?;
+ self.trktype = Some(match id {
+ 1 => StreamType::Video,
+ 2 => StreamType::Audio,
+ 17 => StreamType::Subtitles,
+ _ => StreamType::Data,
+ });
+ Ok(())
+ }
+ fn parse_codec_id(&mut self, br: &mut dyn ByteIO, size: u64) -> DemuxerResult<()> {
+ validate!(self.codec_id.is_none() && size > 0);
+ let mut c_id = String::with_capacity(size as usize);
+ for _ in 0..size {
+ let b = br.read_byte()?;
+ validate!((0x20..=0x7F).contains(&b));
+ c_id.push(b as char);
+ }
+ self.codec_id = Some(c_id);
+ Ok(())
+ }
+ fn parse_edata(&mut self, br: &mut dyn ByteIO, size: u64) -> DemuxerResult<()> {
+ validate!(self.edata.is_none() && (1..=(1 << 24)).contains(&size));
+ let mut edata = vec![0; size as usize];
+ br.read_buf(&mut edata)?;
+ self.edata = Some(edata);
+ Ok(())
+ }
+ fn parse_video_elem(&mut self, br: &mut dyn ByteIO, size: u64) -> DemuxerResult<()> {
+ let cur_end = br.tell() + size;
+ parse_entries(self, if self.debug { Some(3) } else { None }, br, PARSE_TRACK_VIDEO_ELEM, cur_end, ParseMode::Ignore)?;
+ Ok(())
+ }
+ fn parse_audio_elem(&mut self, br: &mut dyn ByteIO, size: u64) -> DemuxerResult<()> {
+ let cur_end = br.tell() + size;
+ parse_entries(self, if self.debug { Some(3) } else { None }, br, PARSE_TRACK_AUDIO_ELEM, cur_end, ParseMode::Ignore)?;
+ Ok(())
+ }
+}
+
+
+#[derive(Default)]
+pub struct CueEntry {
+ pub time: Option<u64>,
+ pub tracks: Vec<(u32, u64, u64)>,
+}
+
+pub static PARSE_CUES: &[ParseEntry<CueEntry>] = &[
+ ParseEntry { id: 0xB3, parse: CueEntry::parse_time },
+ ParseEntry { id: 0xB7, parse: CueEntry::parse_track_pos },
+];
+
+impl CueEntry {
+ pub fn validate(&mut self) -> DemuxerResult<()> {
+ if self.time.is_some() && !self.tracks.is_empty() {
+ Ok(())
+ } else {
+ Err(DemuxerError::InvalidData)
+ }
+ }
+
+ read_field!(uint; parse_time, time);
+ #[allow(clippy::unnecessary_unwrap)]
+ fn parse_track_pos(&mut self, br: &mut dyn ByteIO, size: u64) -> DemuxerResult<()> {
+ let end = br.tell() + size;
+
+ let mut tid = None;
+ let mut pos = None;
+ let mut rpos = None;
+ while br.tell() + 2 <= end {
+ let id = br.read_ebml_id()?;
+ let size = br.read_vint()?;
+ let cur_end = br.tell() + size;
+ validate!(cur_end <= end);
+ match id {
+ 0xF7 => {
+ if tid.is_some() {
+ validate!(pos.is_some() && rpos.is_some());
+ self.tracks.push((tid.unwrap(), pos.unwrap(), rpos.unwrap()));
+ }
+ let ttid = br.read_uint(size)?;
+ validate!((1..=U32_MAX).contains(&ttid));
+ tid = Some(ttid as u32);
+ },
+ 0xF1 => {
+ validate!(tid.is_some() && pos.is_none());
+ pos = Some(br.read_uint(size)?);
+ },
+ 0xF0 => {
+ validate!(tid.is_some() && rpos.is_none());
+ rpos = Some(br.read_uint(size)?);
+ },
+ _ => {},
+ }
+ validate!(br.tell() <= cur_end);
+ br.seek(SeekFrom::Start(cur_end))?;
+ }
+ if tid.is_some() && pos.is_some() && rpos.is_some() {
+ self.tracks.push((tid.unwrap(), pos.unwrap(), rpos.unwrap()));
+ }
+ Ok(())
+ }
+}
+
+#[derive(Default)]
+pub struct SegmentInfo {
+ pub debug: bool,
+ pub start: u64,
+ pub end: u64,
+ pub clu_start: u64,
+ pub tracks: Vec<TrackInfo>,
+ pub seek_entries: Vec<(u32, u64)>,
+ pub tscale: u32,
+ pub duration: f64,
+}
+
+pub static PARSE_SEGMENT: &[ParseEntry<SegmentInfo>] = &[
+ ParseEntry { id: 0x114D9B74, parse: SegmentInfo::parse_seek_table },
+ ParseEntry { id: 0x1549A966, parse: SegmentInfo::parse_info },
+ ParseEntry { id: 0x1654AE6B, parse: SegmentInfo::parse_tracks },
+ ParseEntry { id: 0x1254C367, parse: SegmentInfo::skip }, // tags
+];
+
+impl SegmentInfo {
+ pub fn new(debug: bool, start: u64, end: u64) -> Self {
+ Self { debug, start, end, ..Default::default() }
+ }
+ pub fn validate(&self) -> DemuxerResult<()> {
+ validate!(!self.tracks.is_empty());
+ Ok(())
+ }
+ pub fn fill_streams(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<()> {
+ for trk in self.tracks.iter_mut() {
+ let id = trk.number.unwrap();
+ if id > U32_MAX { return Err(DemuxerError::NotImplemented); }
+ let id = id as u32;
+ let ttype = trk.trktype.unwrap();
+ let duration = trk.duration.unwrap_or_default();
+ //let tscale = trk.tscale.unwrap_or(1.0);
+ let cid = trk.codec_id.as_ref().unwrap();
+ let edata = trk.edata.take();
+ let (cname, ctinfo) = match ttype {
+ StreamType::Video => {
+ let cname = match cid.as_str() {
+ "V_AV1" => "av1",
+ "V_AVS2" => "avs2",
+ "V_AVS3" => "avs3",
+ "V_CAVS" => "avs1",
+ "V_DIRAC" => "dirac",
+ "V_FFV1" => "ffv1",
+ "V_JPEG2000" => "jpeg2k",
+ "V_MJPEG" => "jpeg",
+ "V_MPEGH/ISO/HEVC" => "h265",
+ "V_MPEGI/ISO?VVC" => "h266",
+ "V_MPEG1" => "mpeg1",
+ "V_MPEG2" => "mpeg2",
+ "V_MPEG4/ISO/AVC" => "h264",
+ "V_MPEG4/ISO/AP" => "mpeg4asp",
+ "V_MPEG4/ISO/ASP" => "mpeg4asp",
+ "V_MPEG4/ISO/SP" => "mpeg4asp",
+ "V_MPEG4/MS/V3" => "msmpeg4v3",
+ "V_PRORES" => "prores",
+ "V_REAL/RV10" => "rv10",
+ "V_REAL/RV20" => "rv20",
+ "V_REAL/RV30" => "rv30",
+ "V_REAL/RV40" => "rv40",
+ "V_THEORA" => "theora",
+ "V_UNCOMPRESSED" => "rawvideo",
+ "V_VC1" => "vc1",
+ "V_VP8" => "vp8",
+ "V_VP9" => "vp9",
+ _ => "unknown",
+ };
+ let width = trk.width.unwrap() as usize;
+ let height = trk.height.unwrap() as usize;
+ let vinfo = NAVideoInfo::new(width, height, false, RGB24_FORMAT);
+ (cname, NACodecTypeInfo::Video(vinfo))
+ },
+ StreamType::Audio => {
+ let cname = match cid.as_str() {
+ "A_AAC" |
+ "A_AAC/MPEG2/LC" |
+ "A_AAC/MPEG2/LC/SBR" |
+ "A_AAC/MPEG2/MAIN" |
+ "A_AAC/MPEG2/SSR" |
+ "A_AAC/MPEG4/LC" |
+ "A_AAC/MPEG4/LC/SBR" |
+ "A_AAC/MPEG4/LTP" |
+ "A_AAC/MPEG4/MAIN" |
+ "A_AAC/MPEG4/SSR" => "aac",
+ "A_AC3" |
+ "A_AC3/BSID9" |
+ "A_AC3/BSID10" => "ac3",
+ "A_ALAC" => "alac",
+ "A_ATRAC/AT1" => "atrac1",
+ "A_DTS" |
+ "A_DTS/EXPRESS" |
+ "A_DTS/LOSSLESS" => "dts",
+ "A_EAC3" => "eac3-multi",
+ "A_FLAC" => "flac",
+ "A_MLP" => "mlp",
+ "A_MPEG/L1" => "mp1",
+ "A_MPEG/L2" => "mp2",
+ "A_MPEG/L3" => "mp3",
+ "A_REAL/14_4" => "ra14.4",
+ "A_REAL/28_8" => "ra28.8",
+ "A_REAL/ATRC" => "atrac3",
+ "A_REAL/COOK" => "cook",
+ "A_REAL/RALF" => "ralf",
+ "A_REAL/SIPR" => "sipro",
+ "A_OPUS" => "opus",
+ "A_PCM/FLOAT/IEEE" |
+ "A_PCM/INT/BIG" |
+ "A_PCM/INT/LIT" => "pcm",
+ "A_QUICKTIME/QDMC" => "qdesign-music",
+ "A_QUICKTIME/QDM2" => "qdesign-music2",
+ "A_TRUEHD" => "mlp",
+ "A_TTA1" => "tta",
+ "A_VORBIS" => "vorbis",
+ "A_WAVPACK4" => "wavpack",
+ _ => "unknown",
+ };
+ let rate = trk.rate.unwrap() as u32;
+ let channels = trk.channels.unwrap_or(1);
+ let adepth = trk.adepth.unwrap_or(32);
+ let mut fmt = match adepth {
+ 8 => SND_U8_FORMAT,
+ 16 => SND_S16_FORMAT,
+ _ => SND_F32P_FORMAT,
+ };
+ match cid.as_str() {
+ "A_PCM/FLOAT/IEEE" => {
+ validate!(trk.adepth.is_some() && (adepth == 32 || adepth == 64));
+ fmt.bits = adepth;
+ fmt.float = true;
+ fmt.be = false;
+ },
+ "A_PCM/INT/BIG" => {
+ validate!(trk.adepth.is_some());
+ fmt.bits = adepth;
+ fmt.be = true;
+ },
+ "A_PCM/INT/LIT" => {
+ validate!(trk.adepth.is_some());
+ fmt.bits = adepth;
+ fmt.be = false;
+ },
+ _ => {}
+ }
+ let ainfo = NAAudioInfo::new(rate, channels, fmt, 1);
+ (cname, NACodecTypeInfo::Audio(ainfo))
+ },
+ _ => {
+ ("unknown", NACodecTypeInfo::None)
+ }
+ };
+ let info = NACodecInfo::new(cname, ctinfo, edata);
+
+ let div = gcd(self.tscale, TIMEBASE);
+ let (tb_num, tb_den) = (self.tscale / div, TIMEBASE / div);
+ let stream = NAStream::new(ttype, id, info, tb_num, tb_den, duration);
+ if strmgr.add_stream(stream).is_none() {
+ return Err(DemuxerError::InvalidData);
+ }
+ }
+ Ok(())
+ }
+
+ fn skip(&mut self, _br: &mut dyn ByteIO, _size: u64) -> DemuxerResult<()> { Ok(()) }
+ fn parse_seek_table(&mut self, br: &mut dyn ByteIO, size: u64) -> DemuxerResult<()> {
+ let end = br.tell() + size;
+ let mut has_crc = false;
+ if self.debug {
+ println!(" seek table");
+ }
+ while br.tell() + 2 <= end {
+ let id = br.read_ebml_id()?;
+ let size = br.read_vint()?;
+ let cur_end = br.tell() + size;
+ validate!(cur_end <= end);
+ match id {
+ 0xBF => {
+ validate!(!has_crc);
+ validate!(size == 4);
+ has_crc = true;
+ },
+ 0x4DBB => {
+ let mut seekhead = SeekHead::default();
+ parse_entries(&mut seekhead, if self.debug { Some(2) } else { None }, br, PARSE_SEEK_HEAD, cur_end, ParseMode::Ignore)?;
+ validate!(seekhead.id.is_some() && seekhead.pos.is_some());
+ if self.debug {
+ println!(" seek entry id {:X?} pos {:X?}", seekhead.id, seekhead.pos);
+ }
+ self.seek_entries.push((seekhead.id.unwrap(), seekhead.pos.unwrap()));
+ },
+ _ => {
+ },
+ }
+ validate!(br.tell() <= cur_end);
+ br.seek(SeekFrom::Start(cur_end))?;
+ }
+ Ok(())
+ }
+ fn parse_info(&mut self, br: &mut dyn ByteIO, size: u64) -> DemuxerResult<()> {
+ let cur_end = br.tell() + size;
+ if self.debug {
+ println!(" info block");
+ }
+ let mut info = Info::new();
+ parse_entries(&mut info, if self.debug { Some(2) } else { None }, br, PARSE_INFO, cur_end, ParseMode::Ignore)?;
+ self.duration = info.duration;
+ self.tscale = info.tscale;
+
+ Ok(())
+ }
+ fn parse_tracks(&mut self, br: &mut dyn ByteIO, size: u64) -> DemuxerResult<()> {
+ let end = br.tell() + size;
+ validate!(self.tracks.is_empty());
+ while br.tell() < end {
+ let id = br.read_ebml_id()?;
+ let size = br.read_vint()?;
+ let cur_end = br.tell() + size;
+ validate!(cur_end <= end);
+ if id == 0xAE {
+ if self.debug {
+ println!(" track");
+ }
+ let mut trk = TrackInfo { debug: self.debug, ..Default::default() };
+ parse_entries(&mut trk, if self.debug { Some(2) } else { None }, br, PARSE_TRACK, cur_end, ParseMode::Ignore)?;
+ trk.validate()?;
+ self.tracks.push(trk);
+ }
+ validate!(br.tell() <= cur_end);
+ br.seek(SeekFrom::Start(cur_end))?;
+ }
+ Ok(())
+ }
+}
+
+pub fn parse_cues(br: &mut dyn ByteIO, offset: u64, seg_end: u64, print_chunks: bool, cues: &mut Vec<CueEntry>) -> DemuxerResult<()> {
+ br.seek(SeekFrom::Start(offset))?;
+ let id = br.read_ebml_id()?;
+ let size = br.read_vint()?;
+ let cues_end = br.tell() + size;
+ validate!(id == CUES_ID && cues_end <= seg_end);
+ while br.tell() + 2 <= cues_end {
+ let id = br.read_ebml_id()?;
+ let size = br.read_vint()?;
+ let loc_end = br.tell() + size;
+ validate!(loc_end <= cues_end);
+ if id == 0xBB {
+ let mut cue = CueEntry::default();
+ parse_entries(&mut cue, if print_chunks { Some(2) } else { None }, br, PARSE_CUES, loc_end, ParseMode::Ignore)?;
+ cue.validate()?;
+ cues.push(cue);
+ }
+ br.seek(SeekFrom::Start(loc_end))?;
+ }
+ Ok(())
+}