From a2c4cddd96db08ce4af8def7d46a3a757addb433 Mon Sep 17 00:00:00 2001 From: Kostya Shishkov Date: Thu, 2 Apr 2026 18:18:03 +0200 Subject: [PATCH] mov: split code into smaller components --- nihav-commonfmt/src/demuxers/mov.rs | 2324 ------------------- nihav-commonfmt/src/demuxers/mov/mod.rs | 910 ++++++++ nihav-commonfmt/src/demuxers/mov/pktread.rs | 483 ++++ nihav-commonfmt/src/demuxers/mov/track.rs | 967 ++++++++ 4 files changed, 2360 insertions(+), 2324 deletions(-) delete mode 100644 nihav-commonfmt/src/demuxers/mov.rs create mode 100644 nihav-commonfmt/src/demuxers/mov/mod.rs create mode 100644 nihav-commonfmt/src/demuxers/mov/pktread.rs create mode 100644 nihav-commonfmt/src/demuxers/mov/track.rs diff --git a/nihav-commonfmt/src/demuxers/mov.rs b/nihav-commonfmt/src/demuxers/mov.rs deleted file mode 100644 index eb16b45..0000000 --- a/nihav-commonfmt/src/demuxers/mov.rs +++ /dev/null @@ -1,2324 +0,0 @@ -use nihav_core::demuxers::*; -use nihav_registry::register::*; -use nihav_core::compr::deflate::*; -use nihav_codec_support::codecs::qt_pal::*; - -macro_rules! mktag { - ($a:expr, $b:expr, $c:expr, $d:expr) => ({ - (($a as u32) << 24) | (($b as u32) << 16) | (($c as u32) << 8) | ($d as u32) - }); - ($arr:expr) => ({ - (($arr[0] as u32) << 24) | (($arr[1] as u32) << 16) | (($arr[2] as u32) << 8) | ($arr[3] as u32) - }); -} - -const QT_RGB555_FORMAT: NAPixelFormaton = NAPixelFormaton::make_rgb16_fmt(5, 5, 5, true, true); - -pub const ARGB_FORMAT: NAPixelFormaton = NAPixelFormaton { - model: ColorModel::RGB(RGBSubmodel::RGB), components: 4, - comp_info: [ - Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 1, next_elem: 4 }), - Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 2, next_elem: 4 }), - Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 3, next_elem: 4 }), - Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 0, next_elem: 4 }), - None ], - elem_size: 4, be: false, alpha: true, palette: false }; - -#[derive(Clone,Copy,Debug,PartialEq)] -enum DemuxMode { - Normal, - MacBin, - ResFork, -} - -trait Skip64 { - fn skip64(&mut self, size: u64) -> ByteIOResult<()>; -} - -impl Skip64 for T { - fn skip64(&mut self, size: u64) -> ByteIOResult<()> { - if (size as usize as u64) != size { - self.seek(SeekFrom::Current(size as i64))?; - } else { - self.read_skip(size as usize)?; - } - Ok(()) - } -} - -fn read_chunk_header(br: &mut dyn ByteIO) -> DemuxerResult<(u32, u64)> { - let size = br.read_u32be()?; - let ctype = br.read_u32be()?; - if size == 0 { - Ok((ctype, br.left() as u64)) - } else if size == 1 { - let size64 = br.read_u64be()?; - validate!(size64 >= 16); - Ok((ctype, size64 - 16)) - } else { - validate!(size >= 8); - Ok((ctype, (size as u64) - 8)) - } -} - -fn read_palette(br: &mut dyn ByteIO, size: u64, pal: &mut [u8; 1024]) -> DemuxerResult { - let _seed = br.read_u32be()?; - let flags = br.read_u16be()?; - let palsize = (br.read_u16be()? as usize) + 1; - validate!(palsize <= 256); - validate!(flags == 0 || flags == 0x4000 || flags == 0x8000); - validate!((palsize as u64) * 8 + 8 <= size); - for i in 0..palsize { - let a = br.read_u16be()?; - let r = br.read_u16be()?; - let g = br.read_u16be()?; - let b = br.read_u16be()?; - pal[i * 4] = (r >> 8) as u8; - pal[i * 4 + 1] = (g >> 8) as u8; - pal[i * 4 + 2] = (b >> 8) as u8; - if flags == 0x8000 { - pal[i * 4 + 3] = (a >> 8) as u8; - } - } - if flags == 0x4000 { - br.read_skip(8)?; - } - Ok(size) -} - -struct RootChunkHandler { - ctype: u32, - parse: fn(dmx: &mut MOVDemuxer, strmgr: &mut StreamManager, size: u64) -> DemuxerResult, -} - -struct TrackChunkHandler { - ctype: u32, - parse: fn(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult, -} - -const IGNORED_CHUNKS: &[u32] = &[ - mktag!(b"free"), mktag!(b"skip"), mktag!(b"udta"), mktag!(b"wide") -]; - -const ROOT_CHUNK_HANDLERS: &[RootChunkHandler] = &[ - RootChunkHandler { ctype: mktag!(b"ftyp"), parse: read_ftyp }, - RootChunkHandler { ctype: mktag!(b"mdat"), parse: read_mdat }, - RootChunkHandler { ctype: mktag!(b"moov"), parse: read_moov }, - RootChunkHandler { ctype: mktag!(b"moof"), parse: read_moof }, - RootChunkHandler { ctype: mktag!(b"sidx"), parse: read_sidx }, -]; - -fn print_cname(ctype: u32, size: u64, off: u64, depth: u8) { - for _ in 0..depth { print!(" "); } - let tag = [(ctype >> 24) as u8, (ctype >> 16) as u8, (ctype >> 8) as u8, ctype as u8]; - let mut printable = true; - for ch in tag.iter() { - if !(0x20..=0x7F).contains(ch) { - printable = false; - break; - } - } - if printable { - print!(" '{}{}{}{}'", tag[0] as char, tag[1] as char, tag[2] as char, tag[3] as char); - } else { - print!(" {:08X}", ctype); - } - println!(" size {} @ {:X}", size, off); -} - -macro_rules! read_chunk_list { - (root; $name: expr, $fname: ident, $handlers: ident) => { - fn $fname(&mut self, strmgr: &mut StreamManager, size: u64) -> DemuxerResult<()> { - self.depth += 1; - validate!(self.depth < 32); - let list_end = self.src.tell() + size; - while self.src.tell() < list_end { - let ret = read_chunk_header(self.src); - if ret.is_err() { break; } - let (ctype, size) = ret.unwrap(); - if self.print_chunks { - print_cname(ctype, size, self.src.tell(), self.depth as u8); - } - if self.src.tell() + size > list_end { - break; - } - if IGNORED_CHUNKS.contains(&ctype) { - self.src.skip64(size)?; - continue; - } - let handler = $handlers.iter().find(|x| x.ctype == ctype); - let read_size; - if let Some(ref handler) = handler { - read_size = (handler.parse)(self, strmgr, size)?; - } else { - println!("skipping unknown chunk {:08X} size {}", ctype, size); - read_size = 0; - } - validate!(read_size <= size); - self.src.skip64(size - read_size)?; - } - self.depth -= 1; - validate!(self.src.tell() == list_end); - Ok(()) - } - }; - (track; $name: expr, $fname: ident, $handlers: ident) => { - fn $fname(&mut self, br: &mut dyn ByteIO, size: u64) -> DemuxerResult<()> { - self.depth += 1; - validate!(self.depth < 32); - let list_end = br.tell() + size; - while br.tell() < list_end { - let ret = read_chunk_header(br); - if ret.is_err() { break; } - let (ctype, size) = ret.unwrap(); - if self.print_chunks { - print_cname(ctype, size, br.tell(), self.depth + 1); - } - if br.tell() + size > list_end { - break; - } - if IGNORED_CHUNKS.contains(&ctype) { - br.skip64(size)?; - continue; - } - let handler = $handlers.iter().find(|x| x.ctype == ctype); - let read_size; - if let Some(ref handler) = handler { - read_size = (handler.parse)(self, br, size)?; - } else { - read_size = 0; - } - validate!(read_size <= size); - br.skip64(size - read_size)?; - } - self.depth -= 1; - validate!(br.tell() == list_end); - Ok(()) - } - } -} - -fn skip_chunk(_track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { - br.skip64(size)?; - Ok(size) -} - -fn read_ftyp(dmx: &mut MOVDemuxer, _strmgr: &mut StreamManager, size: u64) -> DemuxerResult { - dmx.src.skip64(size)?; - Ok(size) -} - -fn read_mdat(dmx: &mut MOVDemuxer, _strmgr: &mut StreamManager, size: u64) -> DemuxerResult { - dmx.mdat_pos = dmx.src.tell(); - dmx.mdat_size = size; - dmx.src.skip64(size)?; - Ok(size) -} - -fn read_sidx(_dmx: &mut MOVDemuxer, _strmgr: &mut StreamManager, _size: u64) -> DemuxerResult { - Ok(0) -} - -fn read_moov(dmx: &mut MOVDemuxer, strmgr: &mut StreamManager, size: u64) -> DemuxerResult { - dmx.read_moov(strmgr, size)?; - Ok(size) -} - -const MOOV_CHUNK_HANDLERS: &[RootChunkHandler] = &[ - RootChunkHandler { ctype: mktag!(b"mvhd"), parse: read_mvhd }, - RootChunkHandler { ctype: mktag!(b"cmov"), parse: read_cmov }, - RootChunkHandler { ctype: mktag!(b"ctab"), parse: read_ctab }, - RootChunkHandler { ctype: mktag!(b"trak"), parse: read_trak }, - RootChunkHandler { ctype: mktag!(b"meta"), parse: read_meta }, - RootChunkHandler { ctype: mktag!(b"mvex"), parse: read_mvex }, - RootChunkHandler { ctype: mktag!(b"iods"), parse: skip_chunk_mov }, -]; - -fn read_mvhd(dmx: &mut MOVDemuxer, _strmgr: &mut StreamManager, size: u64) -> DemuxerResult { - const KNOWN_MVHD_SIZE: u64 = 100; - let br = &mut dmx.src; - validate!(size >= KNOWN_MVHD_SIZE); - let version = br.read_byte()?; - validate!(version == 0 || version == 0xFF); - dmx.ver_m1 = version == 0xFF; - let _flags = br.read_u24be()?; - let _ctime = br.read_u32be()?; - let _mtime = br.read_u32be()?; - let tscale = br.read_u32be()?; - let duration = br.read_u32be()?; - let _pref_rate = br.read_u32be()?; - let _pref_volume = br.read_u16be()?; - br.read_skip(10)?; - br.read_skip(36)?; // matrix - let _preview_time = br.read_u32be()?; - let _preview_duration = br.read_u32be()?; - let _poster_time = br.read_u32be()?; - let _sel_time = br.read_u32be()?; - let _sel_duration = br.read_u32be()?; - let _cur_time = br.read_u32be()?; - let _next_track_id = br.read_u32be()?; - dmx.duration = duration; - dmx.tb_den = tscale; - - Ok(KNOWN_MVHD_SIZE) -} - -fn read_cmov(dmx: &mut MOVDemuxer, strmgr: &mut StreamManager, size: u64) -> DemuxerResult { - let br = &mut dmx.src; - validate!(size > 24); - let dcom_size = br.read_u32be()?; - let dcom_tag = br.read_tag()?; - let compr_type = br.read_tag()?; - validate!(&dcom_tag == b"dcom" && dcom_size == 12); - if &compr_type != b"zlib" { - return Err(DemuxerError::NotImplemented); - } - let cmvd_size = u64::from(br.read_u32be()?); - let cmvd_tag = br.read_tag()?; - validate!(&cmvd_tag == b"cmvd" && cmvd_size > 14 && cmvd_size == size - 12); - let comp_size = (cmvd_size - 12) as usize; - let uncomp_size = br.read_u32be()? as usize; - validate!(uncomp_size > 8); - let mut sbuf = vec![0; comp_size]; - let mut dbuf = vec![0; uncomp_size]; - br.read_buf(sbuf.as_mut_slice())?; - let ret = Inflate::uncompress(sbuf.as_slice(), dbuf.as_mut_slice()); - if ret.is_err() { - return Err(DemuxerError::InvalidData); - } - let len = ret.unwrap(); - validate!(len == uncomp_size); - let mut br = MemoryReader::new_read(dbuf.as_slice()); - let (ctype, csize) = read_chunk_header(&mut br)?; - validate!(ctype == mktag!(b"moov")); - let mut ddmx = MOVDemuxer::new(&mut br); - ddmx.print_chunks = dmx.print_chunks; - ddmx.read_moov(strmgr, csize)?; - std::mem::swap(&mut dmx.tracks, &mut ddmx.tracks); - dmx.duration = ddmx.duration; - dmx.tb_den = ddmx.tb_den; - std::mem::swap(&mut dmx.pal, &mut ddmx.pal); - - Ok(size) -} - -fn read_ctab(dmx: &mut MOVDemuxer, _strmgr: &mut StreamManager, size: u64) -> DemuxerResult { - let mut pal = [0; 1024]; - let size = read_palette(dmx.src, size, &mut pal)?; - dmx.pal = Some(Arc::new(pal)); - Ok(size) -} - -fn read_meta(dmx: &mut MOVDemuxer, _strmgr: &mut StreamManager, size: u64) -> DemuxerResult { - dmx.src.skip64(size)?; - Ok(size) -} - -fn read_mvex(_dmx: &mut MOVDemuxer, _strmgr: &mut StreamManager, _size: u64) -> DemuxerResult { - Ok(0) -} - -fn skip_chunk_mov(_dmx: &mut MOVDemuxer, _strmgr: &mut StreamManager, _size: u64) -> DemuxerResult { - Ok(0) -} - -fn read_trak(dmx: &mut MOVDemuxer, strmgr: &mut StreamManager, size: u64) -> DemuxerResult { - let mut track = Track::new(dmx.cur_track as u32, dmx.tb_den); - track.print_chunks = dmx.print_chunks; - track.ver_m1 = dmx.ver_m1; - track.read_trak(dmx.src, size)?; - validate!(track.tkhd_found); - if !track.stsd_found { - track.stream = Some(NAStream::new(StreamType::Data, track.track_no, DUMMY_CODEC_INFO, track.tb_num, track.tb_den, 0)); - } - // invent keyframes for video stream if none were reported - if !track.stss_found && track.stream_type == StreamType::Video { - track.keyframes.reserve(track.time_to_sample.len()); - if !track.time_to_sample.is_empty() { - let mut abs_csamp = 1u64; - for &(count, _scount) in track.time_to_sample.iter() { - let count = u64::from(count); - track.keyframes.push(abs_csamp as u32); - abs_csamp += count; - } - } else if let Some(ref stream) = track.stream { - if let Some(desc) = get_codec_description(stream.get_info().get_name()) { - if (desc.caps & CODEC_CAP_INTRAONLY) != 0 && !track.chunk_sizes.is_empty() { - for samp in 0..track.chunk_sizes.len() { - track.keyframes.push((samp + 1) as u32); - } - } - } - } - } - validate!(strmgr.get_stream_by_id(track.track_id).is_none()); - dmx.cur_track += 1; - dmx.tracks.push(track); - Ok(size) -} - -fn read_moof(dmx: &mut MOVDemuxer, strmgr: &mut StreamManager, size: u64) -> DemuxerResult { - dmx.moof_off = dmx.src.tell() - 8; - dmx.read_moof(strmgr, size)?; - Ok(size) -} - -const MOOF_CHUNK_HANDLERS: &[RootChunkHandler] = &[ - RootChunkHandler { ctype: mktag!(b"mfhd"), parse: read_mfhd }, - RootChunkHandler { ctype: mktag!(b"traf"), parse: read_traf }, - RootChunkHandler { ctype: mktag!(b"meta"), parse: read_meta }, -]; - -fn read_mfhd(dmx: &mut MOVDemuxer, _strmgr: &mut StreamManager, size: u64) -> DemuxerResult { - const KNOWN_MFHD_SIZE: u64 = 8; - validate!(size >= KNOWN_MFHD_SIZE); - let version = dmx.src.read_byte()?; - validate!(version == 0); - let flags = dmx.src.read_u24be()?; - validate!(flags == 0); - let _seq_no = dmx.src.read_u32be()?; - - Ok(KNOWN_MFHD_SIZE) -} - -fn read_traf(dmx: &mut MOVDemuxer, _strmgr: &mut StreamManager, size: u64) -> DemuxerResult { - let mut buf = [0u8; 16]; - dmx.src.peek_buf(&mut buf)?; - validate!(&buf[4..8] == b"tfhd"); - let track_id = read_u32be(&buf[12..16])?; - let mut track = None; - for trk in dmx.tracks.iter_mut() { - if trk.track_id == track_id { - track = Some(trk); - break; - } - } - if let Some(track) = track { - track.moof_off = dmx.moof_off; - track.read_traf(dmx.src, size)?; - Ok(size) - } else { - Ok(0) - } -} - -const TRAK_CHUNK_HANDLERS: &[TrackChunkHandler] = &[ - TrackChunkHandler { ctype: mktag!(b"clip"), parse: skip_chunk }, - TrackChunkHandler { ctype: mktag!(b"matt"), parse: skip_chunk }, - TrackChunkHandler { ctype: mktag!(b"edts"), parse: skip_chunk }, - TrackChunkHandler { ctype: mktag!(b"tref"), parse: skip_chunk }, - TrackChunkHandler { ctype: mktag!(b"load"), parse: skip_chunk }, - TrackChunkHandler { ctype: mktag!(b"imap"), parse: skip_chunk }, - TrackChunkHandler { ctype: mktag!(b"tkhd"), parse: read_tkhd }, - TrackChunkHandler { ctype: mktag!(b"mdia"), parse: read_mdia }, -]; - -fn read_tkhd(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { - const KNOWN_TKHD_SIZE: u64 = 84; - validate!(size >= KNOWN_TKHD_SIZE); - let version = br.read_byte()?; - validate!(version == 0); - let _flags = br.read_u24be()?; - let _ctime = br.read_u32be()?; - let _mtime = br.read_u32be()?; - let track_id = br.read_u32be()?; - br.read_skip(4)?; - let duration = br.read_u32be()?; - br.read_skip(8)?; - let _layer = br.read_u16be()?; - let _alt_group = br.read_u16be()?; - let _volume = br.read_u16be()?; - br.read_skip(2)?; - br.read_skip(36)?; // matrix - let width = br.read_u32be()? as usize; - let height = br.read_u32be()? as usize; - track.width = width >> 16; - track.height = height >> 16; - track.track_id = track_id; - track.duration = duration; - - track.tkhd_found = true; - Ok(KNOWN_TKHD_SIZE) -} - -fn read_mdia(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { - track.stream_type = StreamType::None; - track.read_mdia(br, size)?; - Ok(size) -} - -const MDIA_CHUNK_HANDLERS: &[TrackChunkHandler] = &[ - TrackChunkHandler { ctype: mktag!(b"mdhd"), parse: read_mdhd }, - TrackChunkHandler { ctype: mktag!(b"hdlr"), parse: read_hdlr }, - TrackChunkHandler { ctype: mktag!(b"minf"), parse: read_minf }, -]; - -fn read_mdhd(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { - const KNOWN_MDHD_SIZE: u64 = 24; - validate!(size >= KNOWN_MDHD_SIZE); - let version = br.read_byte()?; - validate!(version == 0); - let flags = br.read_u24be()?; - validate!(flags == 0); - let _ctime = br.read_u32be()?; - let _mtime = br.read_u32be()?; - track.tb_den = br.read_u32be()?; - validate!(track.tb_den != 0); - track.duration = br.read_u32be()?; - let _language = br.read_u16be()?; - let _quality = br.read_u16be()?; - - Ok(KNOWN_MDHD_SIZE) -} - -fn read_hdlr(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { - const KNOWN_HDLR_SIZE: u64 = 24; - validate!(size >= KNOWN_HDLR_SIZE); - let version = br.read_byte()?; - validate!(version == 0); - let flags = br.read_u24be()?; - validate!(flags == 0); - let comp_type = br.read_u32be()?; - let comp_subtype = br.read_u32be()?; - let _comp_manufacturer = br.read_u32be()?; - let _comp_flags = br.read_u32be()?; - let _comp_flags_mask = br.read_u32be()?; - - if comp_type == mktag!(b"mhlr") || comp_type == 0 { - if comp_subtype == mktag!(b"vide") { - track.stream_type = StreamType::Video; - } else if comp_subtype == mktag!(b"soun") { - track.stream_type = StreamType::Audio; - } else { - track.stream_type = StreamType::Data; - } - } else if comp_type == mktag!(b"dhlr") { - track.stream_type = StreamType::Data; - } else { - println!("Unknown stream type"); - track.stream_type = StreamType::Data; - } - - Ok(KNOWN_HDLR_SIZE) -} - -fn read_minf(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { - track.read_minf(br, size)?; - Ok(size) -} - -const MINF_CHUNK_HANDLERS: &[TrackChunkHandler] = &[ - TrackChunkHandler { ctype: mktag!(b"hdlr"), parse: skip_chunk }, - TrackChunkHandler { ctype: mktag!(b"dinf"), parse: skip_chunk }, - TrackChunkHandler { ctype: mktag!(b"vmhd"), parse: read_vmhd }, - TrackChunkHandler { ctype: mktag!(b"smhd"), parse: read_smhd }, - TrackChunkHandler { ctype: mktag!(b"gmhd"), parse: read_gmhd }, - TrackChunkHandler { ctype: mktag!(b"gmin"), parse: read_gmin }, - TrackChunkHandler { ctype: mktag!(b"stbl"), parse: read_stbl }, -]; - -fn read_vmhd(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { - const KNOWN_VMHD_SIZE: u64 = 12; - validate!(track.stream_type == StreamType::Video); - validate!(size >= KNOWN_VMHD_SIZE); - let version = br.read_byte()?; - validate!(version == 0); - let _flags = br.read_u24be()?; - br.read_skip(2)?; // graphics mode - br.read_skip(6)?; // opcolor - Ok(KNOWN_VMHD_SIZE) -} - -fn read_smhd(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { - const KNOWN_SMHD_SIZE: u64 = 8; - validate!(track.stream_type == StreamType::Audio); - validate!(size >= KNOWN_SMHD_SIZE); - let version = br.read_byte()?; - validate!(version == 0); - let _flags = br.read_u24be()?; - br.read_skip(2)?; // balance - br.read_skip(2)?; - Ok(KNOWN_SMHD_SIZE) -} - -fn read_gmhd(track: &mut Track, _br: &mut dyn ByteIO, _size: u64) -> DemuxerResult { - validate!(track.stream_type == StreamType::Data); - Ok(0) -} - -fn read_gmin(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { - validate!(track.stream_type == StreamType::Data); - const KNOWN_GMIN_SIZE: u64 = 16; - validate!(size >= KNOWN_GMIN_SIZE); - let version = br.read_byte()?; - validate!(version == 0); - let _flags = br.read_u24be()?; - br.read_skip(2)?; // graphics mode - br.read_skip(6)?; // opcolor - br.read_skip(2)?; // balance - br.read_skip(2)?; - Ok(KNOWN_GMIN_SIZE) -} - -fn read_stbl(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { - track.read_stbl(br, size)?; - Ok(size) -} - -const STBL_CHUNK_HANDLERS: &[TrackChunkHandler] = &[ - TrackChunkHandler { ctype: mktag!(b"stsd"), parse: read_stsd }, - TrackChunkHandler { ctype: mktag!(b"stts"), parse: read_stts }, - TrackChunkHandler { ctype: mktag!(b"stgs"), parse: read_stgs }, - TrackChunkHandler { ctype: mktag!(b"stss"), parse: read_stss }, - TrackChunkHandler { ctype: mktag!(b"stsc"), parse: read_stsc }, - TrackChunkHandler { ctype: mktag!(b"stsz"), parse: read_stsz }, - TrackChunkHandler { ctype: mktag!(b"stco"), parse: read_stco }, - TrackChunkHandler { ctype: mktag!(b"co64"), parse: read_co64 }, - TrackChunkHandler { ctype: mktag!(b"stsh"), parse: skip_chunk }, - TrackChunkHandler { ctype: mktag!(b"ctts"), parse: read_ctts }, -]; - -fn parse_audio_edata(br: &mut dyn ByteIO, start_pos: u64, size: u64) -> DemuxerResult>> { - let read_part = br.tell() - start_pos; - if read_part + 8 < size { - let mut buf = [0; 8]; - br.peek_buf(&mut buf)?; - if &buf[4..8] != b"wave" { - let mut buf = vec![0; (size - read_part) as usize]; - br.read_buf(&mut buf)?; - return Ok(Some(buf)); - } - - let csize = br.read_u32be()? as u64; - let ctag = br.read_u32be()?; - validate!(read_part + csize <= size); - validate!(ctag == mktag!(b"wave")); - if csize == 8 { - return Ok(None); - } - let mut buf = [0; 8]; - br.peek_buf(&mut buf)?; - if &buf[4..8] == b"frma" { - br.read_skip(12)?; - if csize > 20 { - let mut buf = vec![0; (csize - 20) as usize]; - br.read_buf(&mut buf)?; - Ok(Some(buf)) - } else { - Ok(None) - } - } else if csize > 8 { - let mut buf = vec![0; (csize as usize) - 8]; - br.read_buf(&mut buf)?; - Ok(Some(buf)) - } else { - Ok(None) - } - } else { - Ok(None) - } -} - -#[allow(clippy::neg_cmp_op_on_partial_ord)] -fn read_stsd(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { - const KNOWN_STSD_SIZE: u64 = 24; - validate!(size >= KNOWN_STSD_SIZE); - let start_pos = br.tell(); - let version = br.read_byte()?; - validate!(version == 0); - let _flags = br.read_u24be()?; - let entries = br.read_u32be()?; - validate!(entries > 0); - let esize = u64::from(br.read_u32be()?); - validate!(esize + 8 <= size); - let mut fcc = [0u8; 4]; - br.read_buf(&mut fcc)?; - br.read_skip(6)?; - let _data_ref = br.read_u16be()?; - - track.fcc = fcc; - - let codec_info; - match track.stream_type { - StreamType::Video => { - let _ver = br.read_u16be()?; - let _revision = br.read_u16le()?; - let _vendor = br.read_u32be()?; - let _temp_quality = br.read_u32be()?; - let _spat_quality = br.read_u32be()?; - let width = br.read_u16be()? as usize; - let height = br.read_u16be()? as usize; - let _hor_res = br.read_u32be()?; - let _vert_res = br.read_u32be()?; - let data_size = br.read_u32be()?; - if !track.ver_m1 { - validate!(data_size == 0); - } - let _frame_count = br.read_u16be()? as usize; - let _cname_len = br.read_byte()? as usize; - br.read_skip(31)?; // actual compressor name - let depth = br.read_u16be()?; - let ctable_id = br.read_u16be()?; - let grayscale = depth > 0x20 || depth == 1; - let depth = if grayscale { depth & 0x1F } else { depth }; - if ctable_id == 0 { - let max_pal_size = start_pos + size - br.tell(); - if depth <= 8 { - let mut pal = [0; 1024]; - read_palette(br, max_pal_size, &mut pal)?; - track.pal = Some(Arc::new(pal)); - } else { - br.read_skip(max_pal_size as usize)?; - } - } else if (depth <= 8) && !grayscale { - match depth & 0x1F { - 2 => { - let mut pal = [0; 1024]; - pal[..4 * 4].copy_from_slice(&MOV_DEFAULT_PAL_2BIT); - track.pal = Some(Arc::new(pal)); - }, - 4 => { - let mut pal = [0; 1024]; - pal[..16 * 4].copy_from_slice(&MOV_DEFAULT_PAL_4BIT); - track.pal = Some(Arc::new(pal)); - }, - 8 => { - track.pal = Some(Arc::new(MOV_DEFAULT_PAL_8BIT)); - }, - _ => {}, - }; - } else if grayscale && ctable_id != 0xFFFF { - let mut pal = [0; 1024]; - let cdepth = depth & 0x1F; - let size = 1 << cdepth; - for i in 0..size { - let mut clr = ((size - 1 - i) as u8) << (8 - cdepth); - let mut off = 8 - cdepth; - while off >= cdepth { - clr |= clr >> (8 - off); - off -= cdepth; - } - if off > 0 { - clr |= clr >> (8 - off); - } - pal[i * 4] = clr; - pal[i * 4 + 1] = clr; - pal[i * 4 + 2] = clr; - } - track.pal = Some(Arc::new(pal)); - } -// todo other atoms, put as extradata - let cname = if let Some(name) = find_codec_from_mov_video_fourcc(&fcc) { - name - } else if let Some(name) = find_codec_from_avi_fourcc(&fcc) { - name - } else { - "unknown" - }; - let format = if cname == "rawvideo" { - if &fcc == b"j420" { - validate!(depth == 12); - YUV420_FORMAT - } else { - match depth { - 1..=8 | 33..=40 => PAL8_FORMAT, - 15 | 16 => QT_RGB555_FORMAT, - 24 => RGB24_FORMAT, - 32 => ARGB_FORMAT, - _ => { - println!("unknown depth {depth}"); - return Err(DemuxerError::NotImplemented); - } - } - } - } else if depth > 8 && depth <= 32 { - RGB24_FORMAT - } else { - PAL8_FORMAT - }; - let mut vhdr = NAVideoInfo::new(width, height, false, format); - vhdr.bits = depth as u8; - if track.ver_m1 { // skip the rest for the beta version of QT - br.seek(SeekFrom::Start(start_pos + size))?; - } - //skip various common atoms - while br.tell() - start_pos + 8 < size { - let mut buf = [0u8; 8]; - br.peek_buf(&mut buf)?; - let tsize = read_u32be(&buf).unwrap() as usize; - let tag = &buf[4..8]; - validate!(tsize >= 8); - match tag { - b"pasp" | b"clap" | b"gama" => { - br.read_skip(tsize)?; - }, - _ => break, - }; - } - let edata = if br.tell() - start_pos + 4 < size { - let edata_size = br.read_u32be()? as usize; - validate!(edata_size >= 4); - let mut buf = vec![0; edata_size - 4]; - br.read_buf(buf.as_mut_slice())?; - Some(buf) - } else { - None - }; - codec_info = NACodecInfo::new(cname, NACodecTypeInfo::Video(vhdr), edata); - }, - StreamType::Audio if track.ver_m1 => { - br.read_skip(8)?; - let nchannels = br.read_u16be()?; - validate!(nchannels == 1 || nchannels == 2); - let sample_size = br.read_u16be()?; - validate!(sample_size == 8 || sample_size == 16); - br.read_u32be()?; - let sample_rate = br.read_u32be()? >> 16; - let cname = if fcc == [0; 4] { "pcm" } else { "unknown" }; - let mut soniton = NASoniton::new(sample_size as u8, SONITON_FLAG_SIGNED | SONITON_FLAG_BE); - if sample_size == 8 { - soniton.signed = false; - } - let ahdr = NAAudioInfo::new(sample_rate, nchannels as u8, soniton, 1); - let edata = parse_audio_edata(br, start_pos, size)?; - codec_info = NACodecInfo::new(cname, NACodecTypeInfo::Audio(ahdr), edata); - track.raw_audio = cname == "pcm"; - track.bsize = (sample_size / 8) as usize; - track.channels = nchannels as usize; - track.bits = sample_size as usize; - if track.tb_den <= 1 { - track.tb_den = sample_rate; - } - }, - StreamType::Audio => { - let sver = br.read_u16be()?; - let _revision = br.read_u16le()?; - let _vendor = br.read_u32be()?; - let mut nchannels = br.read_u16be()?; - if sver != 2 { - validate!(nchannels <= 64); - } - let sample_size = br.read_u16be()?; - validate!(sample_size <= 128); - let _compr_id = br.read_u16be()?; - let packet_size = br.read_u16be()? as usize; - validate!(packet_size == 0); - let mut sample_rate = br.read_u32be()? >> 16; - if sver != 2 { - validate!(sample_rate > 0); - } - let cname = if let Some(name) = find_codec_from_mov_audio_fourcc(&fcc) { - name - } else if let (true, Some(name)) = ((fcc[0] == b'm' && fcc[1] == b's'), find_codec_from_wav_twocc(u16::from(fcc[2]) * 256 + u16::from(fcc[3]))) { - name - } else { - "unknown" - }; - let mut soniton = NASoniton::new(sample_size as u8, SONITON_FLAG_SIGNED | SONITON_FLAG_BE); - if &fcc == b"raw " && sample_size == 8 { - soniton.signed = false; - } - if &fcc == b"sowt" { - soniton.be = false; - } - let mut block_align = 1; - match sver { - 1 => { - let samples_per_packet = br.read_u32be()?; - let _bytes_per_packet = br.read_u32be()?; - let bytes_per_frame = br.read_u32be()?; - let _bytes_per_sample = br.read_u32be()?; - track.bsize = bytes_per_frame as usize; - track.frame_samples = samples_per_packet as usize; - track.tb_num = samples_per_packet.max(1); - block_align = bytes_per_frame as usize; - }, - 2 => { - br.read_u32be()?; // some size - let srate = br.read_f64be()?; - validate!(srate > 1.0); - sample_rate = srate as u32; - let channels = br.read_u32be()?; - validate!(channels > 0 && channels < 255); - nchannels = channels as u16; - br.read_u32be()?; // always 0x7F000000 - let _bits_per_csample = br.read_u32be()?; - let _codec_flags = br.read_u32be()?; - let bytes_per_frame = br.read_u32be()?; - let samples_per_packet = br.read_u32be()?; - track.bsize = bytes_per_frame as usize; - track.frame_samples = samples_per_packet as usize; - track.tb_num = samples_per_packet; - }, - _ => { - track.bsize = (sample_size / 8) as usize; - }, - }; - if track.tb_den <= 1 { - track.tb_den = sample_rate; - } - track.raw_audio = matches!(&fcc, - b"NONE" | b"raw " | b"twos" | b"sowt" | - b"in24" | b"in32" | b"fl32" | b"fl64" | - b"ima4" | b"ms\x00\x02" | b"ms\x00\x11" | - b"alaw" | b"ulaw" | - b"MAC3" | b"MAC6"); - let ahdr = NAAudioInfo::new(sample_rate, nchannels as u8, soniton, block_align); - let edata = parse_audio_edata(br, start_pos, size)?; - codec_info = NACodecInfo::new(cname, NACodecTypeInfo::Audio(ahdr), edata); - track.channels = nchannels as usize; - track.bits = sample_size as usize; - }, - StreamType::None => { - return Err(DemuxerError::InvalidData); - }, - _ => { -//todo put it all into extradata - let edata = None; - codec_info = NACodecInfo::new("unknown", NACodecTypeInfo::None, edata); - }, - }; - let read_size = br.tell() - start_pos; - validate!(read_size <= size); - track.stream = Some(NAStream::new(track.stream_type, track.track_no, codec_info, track.tb_num, track.tb_den, u64::from(track.duration))); - track.stsd_found = true; - Ok(read_size) -} - -fn read_stts(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { - validate!(size >= 8); - let start_pos = br.tell(); - let version = br.read_byte()?; - validate!(version == 0); - let _flags = br.read_u24be()?; - let entries = br.read_u32be()? as usize; - validate!(entries as u64 <= (size - 8) / 8); - if entries == 0 { - } else if entries == 1 { - let _count = br.read_u32be()?; - let tb_num = br.read_u32be()?; - validate!(tb_num != 0); - track.rescale(tb_num); - } else { - track.time_to_sample.clear(); - track.time_to_sample.reserve(entries); - for _ in 0..entries { - let count = br.read_u32be()?; - let mult = br.read_u32be()?; - track.time_to_sample.push((count, mult)); - } - } - let read_size = br.tell() - start_pos; - validate!(read_size <= size); - Ok(read_size) -} - -fn read_stgs(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { - validate!(size >= 8); - validate!(track.ver_m1); - let start_pos = br.tell(); - let version = br.read_byte()?; - validate!(version == 0); - let _flags = br.read_u24be()?; - let entries = br.read_u32be()? as usize; - validate!(entries as u64 <= (size - 8) / 8); - if entries == 0 { - } else if entries == 1 { - let _count = br.read_u32be()?; - let tb_num = br.read_u32be()?; - validate!(tb_num != 0); - track.rescale(tb_num); - } else { - track.time_to_sample.clear(); - track.time_to_sample.reserve(entries); - for _ in 0..entries { - let count = br.read_u32be()?; - let mult = br.read_u32be()?; - track.time_to_sample.push((count, mult)); - } - } - let read_size = br.tell() - start_pos; - validate!(read_size <= size); - Ok(read_size) -} - -fn read_stss(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { - let version = br.read_byte()?; - validate!(version == 0); - let _flags = br.read_u24be()?; - let entries = br.read_u32be()? as usize; - validate!(entries < ((u32::MAX >> 2) - 8) as usize); - validate!((entries * 4 + 8) as u64 == size); - track.keyframes = Vec::with_capacity(entries); - let mut last_sample_no = 0; - for _ in 0..entries { - let sample_no = br.read_u32be()?; - validate!(sample_no > last_sample_no); - track.keyframes.push(sample_no); - last_sample_no = sample_no; - } - track.stss_found = true; - Ok(size) -} - -fn read_stsc(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { - let version = br.read_byte()?; - validate!(version == 0); - let _flags = br.read_u24be()?; - let entries = br.read_u32be()? as usize; - if track.ver_m1 { - if entries != 1 || size != 24 { - return Err(DemuxerError::NotImplemented); - } - br.read_u32be()?; - let sample_no = br.read_u32be()?; - validate!(sample_no == 1); - let nsamples = br.read_u32be()?; - br.read_u32be()?; // maybe sample descriptor - track.sample_map.push((sample_no, nsamples)); - return Ok(size); - } - validate!(entries < ((u32::MAX / 12) - 8) as usize); - validate!((entries * 12 + 8) as u64 == size); - track.sample_map = Vec::with_capacity(entries); - let mut last_sample_no = 0; - for _i in 0..entries { - let sample_no = br.read_u32be()?; - validate!(sample_no > last_sample_no); - let nsamples = br.read_u32be()?; - let _sample_desc = br.read_u32be()?; - track.sample_map.push((sample_no, nsamples)); - last_sample_no = sample_no; - } - Ok(size) -} - -fn read_stsz(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { - let version = br.read_byte()?; - validate!(version == 0); - let _flags = br.read_u24be()?; - let sample_size = br.read_u32be()?; - if sample_size != 0 { - track.sample_size = sample_size; - if track.sample_size != 1 || track.bsize == 0 { - track.bsize = sample_size as usize; - } - Ok(8) - } else { - let entries = br.read_u32be()? as usize; - validate!((entries * 4 + 12) as u64 == size); - track.chunk_sizes = vec![0; entries]; - br.read_u32be_arr(&mut track.chunk_sizes)?; - Ok(size) - } -} - -fn read_stco(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { - let version = br.read_byte()?; - validate!(version == 0); - let _flags = br.read_u24be()?; - let entries = br.read_u32be()? as usize; - validate!((entries * 4 + 8) as u64 == size); - if entries < 1024 { - track.chunk_offsets = Vec::with_capacity(entries); - for _i in 0..entries { - let sample_offset = br.read_u32be()?; - track.chunk_offsets.push(u64::from(sample_offset)); - } - } else { - // reading whole array at once and converting it later is much faster - track.chunk_offsets = vec![0; entries]; - let mut tmp = vec![0u32; entries]; - unsafe { - let data = tmp.as_mut_ptr(); - let ptr = std::slice::from_raw_parts_mut(data as *mut u8, entries * 4); - br.read_buf(ptr)?; - } - for (dst, &src) in track.chunk_offsets.iter_mut().zip(tmp.iter()) { - *dst = u64::from(u32::from_be(src)); - } - } - Ok(size) -} - -fn read_co64(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { - let version = br.read_byte()?; - validate!(version == 0); - let _flags = br.read_u24be()?; - let entries = br.read_u32be()? as usize; - validate!(entries > 0 && (entries * 8 + 8) as u64 == size); - track.chunk_offsets = vec![0; entries]; - br.read_u64be_arr(&mut track.chunk_offsets)?; - Ok(size) -} - -fn read_ctts(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { - validate!(size >= 8); - let version = br.read_byte()?; - let _flags = br.read_u24be()?; - if version > 1 { - return Err(DemuxerError::NotImplemented); - } - let entries = br.read_u32be()? as usize; - track.ctts_version = version; - track.ctts_map.resize(entries); - match version { - 0 | 1 => { - validate!(size == (entries as u64) * 8 + 8); - if entries < 1024 { - for _ in 0..entries { - let samp_count = br.read_u32be()?; - let samp_offset = br.read_u32be()?; - track.ctts_map.add(samp_count, samp_offset / track.tb_div); - } - } else { - // reading whole array at once and converting it later is much faster - track.ctts_map.array.resize(entries, RLEPair(0, 0)); - unsafe { - let data = track.ctts_map.array.as_mut_ptr(); - let ptr = std::slice::from_raw_parts_mut(data as *mut u8, entries * 8); - br.read_buf(ptr)?; - } - for RLEPair(count, offset) in track.ctts_map.array.iter_mut() { - *count = u32::from_be(*count); - *offset = u32::from_be(*offset) / track.tb_div; - } - } - }, - _ => unreachable!(), - }; - track.ctts_map.reset(); - - Ok(size) -} - -const TRAF_CHUNK_HANDLERS: &[TrackChunkHandler] = &[ - TrackChunkHandler { ctype: mktag!(b"tfhd"), parse: read_tfhd }, - TrackChunkHandler { ctype: mktag!(b"trun"), parse: read_trun }, - TrackChunkHandler { ctype: mktag!(b"sbgp"), parse: skip_chunk }, - TrackChunkHandler { ctype: mktag!(b"sgpd"), parse: skip_chunk }, - TrackChunkHandler { ctype: mktag!(b"subs"), parse: skip_chunk }, - TrackChunkHandler { ctype: mktag!(b"saiz"), parse: skip_chunk }, - TrackChunkHandler { ctype: mktag!(b"saio"), parse: skip_chunk }, - TrackChunkHandler { ctype: mktag!(b"tfdt"), parse: skip_chunk }, - TrackChunkHandler { ctype: mktag!(b"meta"), parse: skip_chunk }, -]; - -fn read_tfhd(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { - validate!(size >= 8); - let start = br.tell(); - let _version = br.read_byte()?; - let flags = br.read_u24be()?; - let _track_id = br.read_u32be()?; - if (flags & 0x000001) != 0 { - let base_offset = br.read_u64be()?; - track.moof_off = base_offset; - } - if (flags & 0x000002) != 0 { - let _sample_description_index = br.read_u32be()?; - } - if (flags & 0x000008) != 0 { - let default_sample_duration = br.read_u32be()?; - if track.tb_div == 1 { - track.rescale(default_sample_duration); - } - } - if (flags & 0x000010) != 0 { - let _default_sample_size = br.read_u32be()?; - } - if (flags & 0x000020) != 0 { - let _default_sample_flags = br.read_u32be()?; - } - /*if (flags & 0x010000) != 0 { // duration is empty - }*/ - /*if (flags & 0x020000) != 0 { // base offset is moof start - }*/ - Ok(br.tell() - start) -} - -fn read_trun(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { - validate!(size >= 8); - let version = br.read_byte()?; - let flags = br.read_u24be()?; - let data_off_present = (flags & 0x000001) != 0; - let first_sample_flags = (flags & 0x000004) != 0; - let sample_duration_present = (flags & 0x000100) != 0; - let sample_size_present = (flags & 0x000200) != 0; - let sample_flags_present = (flags & 0x000400) != 0; - let sample_ct_off_present = (flags & 0x000800) != 0; - - let sample_count = br.read_u32be()? as usize; - - let mut hdr_size = 8; - let mut arr_size = 0; - if data_off_present { - hdr_size += 4; - } - if first_sample_flags { - hdr_size += 4; - } - if sample_duration_present { - arr_size += 4; - } - if sample_size_present { - arr_size += 4; - } - if sample_flags_present { - arr_size += 4; - } - if sample_ct_off_present { - arr_size += 4; - } - validate!(size == hdr_size + arr_size * (sample_count as u64)); - - let mut data_off = if data_off_present { - let off = br.read_u32be()? as i32; - let new_off = (track.moof_off as i64) + i64::from(off); - validate!(new_off > 0); - new_off as u64 - } else { - track.moof_off - }; - if first_sample_flags { - let _flags = br.read_u32be()?; - } - - if sample_size_present { - track.chunk_sizes.reserve(sample_count); - track.chunk_offsets.reserve(sample_count); - } - - if sample_ct_off_present { - if track.ctts_version != version { - track.ctts_version = version; - } - track.ctts_map.reserve(sample_count); - } - - if track.chunk_offsets.len() < (u32::MAX as usize) { - track.keyframes.push((track.chunk_offsets.len() + 1) as u32); - } - for _ in 0..sample_count { - if sample_duration_present { - let _duration = br.read_u32be()?; - } - if sample_size_present { - let ssize = br.read_u32be()?; - track.chunk_sizes.push(ssize); - track.chunk_offsets.push(data_off); - data_off += u64::from(ssize); - } - if sample_flags_present { - let _flags = br.read_u32be()?; - } - if sample_ct_off_present { - let samp_offset = br.read_u32be()?; - if version == 0 { - track.ctts_map.add(1, samp_offset / track.tb_div); - } else { - track.ctts_map.add(1, ((samp_offset as i32) / (track.tb_div as i32)) as u32); - } - } - } - track.ctts_map.reset(); - - Ok(size) -} - -struct MOVDemuxer<'a> { - src: &'a mut dyn ByteIO, - depth: usize, - mdat_pos: u64, - mdat_size: u64, - tracks: Vec, - cur_track: usize, - tb_den: u32, - duration: u32, - pal: Option>, - ver_m1: bool, - - moof_off: u64, - - print_chunks: bool, - - demux_mode: DemuxMode, - - force_seek: bool, -} - -struct Track { - ver_m1: bool, - track_id: u32, - track_str_id: usize, - track_no: u32, - tb_num: u32, - tb_den: u32, - tb_div: u32, - raw_audio: bool, - raw_apos: u64, - duration: u32, - depth: u8, - tkhd_found: bool, - stsd_found: bool, - stss_found: bool, - stream_type: StreamType, - width: usize, - height: usize, - channels: usize, - bits: usize, - bsize: usize, - fcc: [u8; 4], - keyframes: Vec, - chunk_sizes: Vec, - chunk_offsets: Vec, - time_to_sample: Vec<(u32, u32)>, - sample_map: Vec<(u32, u32)>, - sample_size: u32, - frame_samples: usize, - ctts_map: RLESearcher, - ctts_version: u8, - stream: Option, - cur_chunk: usize, - cur_sample: usize, - cur_ts: Option, - samples_left: usize, - last_offset: u64, - pal: Option>, - timesearch: TimeSearcher, - - moof_off: u64, - - print_chunks: bool, -} - -#[derive(Default)] -struct TimeSearcher { - idx: usize, - base: u64, - sbase: u32, - cur_len: u32, - cur_mul: u32, -} - -impl TimeSearcher { - fn new() -> Self { Self::default() } - fn reset(&mut self) { - *self = Self::default(); - } - fn map_time(&mut self, sample: u32, tts: &[(u32, u32)]) -> u64 { - if tts.is_empty() { - u64::from(sample) - } else if sample >= self.sbase { - let mut sample = sample - self.sbase; - if self.idx == 0 { - let (cur_len, cur_mul) = tts[0]; - self.cur_len = cur_len; - self.cur_mul = cur_mul; - self.idx += 1; - } - while self.idx < tts.len() && sample > self.cur_len { - sample -= self.cur_len; - self.sbase += self.cur_len; - self.base += u64::from(self.cur_len) * u64::from(self.cur_mul); - self.cur_len = tts[self.idx].0; - self.cur_mul = tts[self.idx].1; - self.idx += 1; - } - self.base + u64::from(sample) * u64::from(self.cur_mul) - } else { - self.reset(); - self.map_time(sample, tts) - } - } -} - -#[repr(C)] -#[derive(Clone,Copy,Default)] -struct RLEPair(u32, T); - -#[derive(Default)] -struct RLESearcher { - array: Vec>, - idx: usize, - start: u64, - next: u64, -} - -impl RLESearcher { - fn new() -> Self { Self::default() } - fn resize(&mut self, size: usize) { - self.array.clear(); - self.array.reserve(size); - } - fn reserve(&mut self, size: usize) { - self.array.reserve(size); - } - fn add(&mut self, len: u32, val: T) { - self.array.push(RLEPair(len, val)); - } - fn reset(&mut self) { - self.start = 0; - if !self.array.is_empty() { - self.idx = 0; - self.next = u64::from(self.array[0].0); - } else { - self.idx = self.array.len(); - self.next = 0; - } - } - fn map(&mut self, sample: u64) -> Option { - if sample < self.start { - self.reset(); - } - if self.idx < self.array.len() { - if sample < self.next { - Some(self.array[self.idx].1) - } else { - while (self.idx < self.array.len()) && (sample >= self.next) { - self.start = self.next; - self.idx += 1; - if self.idx < self.array.len() { - self.next += u64::from(self.array[self.idx].0); - } - } - if self.idx < self.array.len() { - Some(self.array[self.idx].1) - } else { - None - } - } - } else { - None - } - } -} - -impl Track { - fn new(track_no: u32, tb_den: u32) -> Self { - Self { - tkhd_found: false, - stsd_found: false, - stss_found: false, - track_id: 0, - track_str_id: 0, - track_no, - tb_num: 1, - tb_den, - tb_div: 1, - raw_audio: false, - raw_apos: 0, - duration: 0, - stream_type: StreamType::None, - width: 0, - height: 0, - channels: 0, - bits: 0, - bsize: 0, - fcc: [0; 4], - keyframes: Vec::new(), - chunk_sizes: Vec::new(), - chunk_offsets: Vec::new(), - time_to_sample: Vec::new(), - sample_map: Vec::new(), - sample_size: 0, - frame_samples: 0, - ctts_map: RLESearcher::new(), - ctts_version: 0, - stream: None, - depth: 0, - cur_chunk: 0, - cur_sample: 0, - cur_ts: None, - samples_left: 0, - last_offset: 0, - pal: None, - timesearch: TimeSearcher::new(), - ver_m1: false, - - moof_off: 0, - - print_chunks: false, - } - } - read_chunk_list!(track; "trak", read_trak, TRAK_CHUNK_HANDLERS); - read_chunk_list!(track; "mdia", read_mdia, MDIA_CHUNK_HANDLERS); - read_chunk_list!(track; "minf", read_minf, MINF_CHUNK_HANDLERS); - read_chunk_list!(track; "stbl", read_stbl, STBL_CHUNK_HANDLERS); - read_chunk_list!(track; "traf", read_traf, TRAF_CHUNK_HANDLERS); - fn rescale(&mut self, tb_num: u32) { - self.tb_div = tb_num; - if let Some(ref mut stream) = self.stream { - let tb_den = stream.tb_den; - let (tb_num, tb_den) = reduce_timebase(tb_num * stream.tb_num, tb_den); - stream.duration /= u64::from(self.tb_div); - stream.tb_num = tb_num; - stream.tb_den = tb_den; - self.tb_num = tb_num; - self.tb_den = tb_den; - self.duration /= self.tb_div; - } - } - fn fill_seek_index(&self, seek_index: &mut SeekIndex) { - if !self.keyframes.is_empty() { - seek_index.mode = SeekIndexMode::Present; - } - let mut tsearch = TimeSearcher::new(); - for kf_time in self.keyframes.iter() { - let pts = tsearch.map_time(*kf_time - 1, &self.time_to_sample); - let time = NATimeInfo::rescale_ts(pts, self.tb_num, self.tb_den, 1, 1000); - seek_index.add_entry(self.track_no, SeekEntry { time, pts: u64::from(*kf_time - 1), pos: 0 }); - } - } - fn calculate_chunk_size(&self, nsamp: usize) -> usize { - if nsamp == 0 { - self.bsize - } else { - match &self.fcc { - b"NONE" | b"raw " | b"twos" | b"sowt" | &[0, 0, 0, 0] => { - (nsamp * self.bits * self.channels + 7) >> 3 - }, - b"ima4" => { - let nblocks = (nsamp + 63) >> 6; - nblocks * 34 * self.channels - }, - b"MAC3" => { - (nsamp + 5) / 6 * 2 * self.channels - }, - b"MAC6" => { - (nsamp + 5) / 6 * self.channels - }, - b"in24" => nsamp * 3 * self.channels, - b"in32" | b"fl32" => nsamp * 4 * self.channels, - b"fl64" => nsamp * 8 * self.channels, - b"ulaw" | b"alaw" => nsamp, - b"ms\x00\x02" => { //MS ADPCM - ((nsamp - 1) / 2 + 7) * self.channels - }, - b"ms\x00\x11" => { //IMA ADPCM - (nsamp / 2 + 4) * self.channels - }, - _ => self.bsize, - } - } - } - fn reset(&mut self) { - self.cur_chunk = self.chunk_offsets.len(); - self.samples_left = 0; - self.cur_sample = self.chunk_sizes.len(); - } - fn get_next_chunk(&mut self) -> Option<(NATimeInfo, u64, usize, bool)> { - let dts_val = self.timesearch.map_time(self.cur_sample as u32, &self.time_to_sample); - let pts = if let Some(dts_corr) = self.ctts_map.map(self.cur_sample as u64) { - let pts = match self.ctts_version { - 0 => dts_val.wrapping_add(u64::from(dts_corr)), - 1 => dts_val.wrapping_add(i64::from(dts_corr as i32) as u64), - _ => unimplemented!(), - }; - if (pts as i64) < 0 { - None - } else { - Some(pts) - } - } else { - Some(dts_val) - }; - let mut ts = NATimeInfo::new(pts, Some(dts_val), None, self.tb_num, self.tb_den); - if self.chunk_offsets.len() == self.chunk_sizes.len() { // simple one-to-one mapping - if self.cur_sample >= self.chunk_sizes.len() { - return None; - } - let offset = self.chunk_offsets[self.cur_sample]; - let size = self.chunk_sizes[self.cur_sample] as usize; - self.cur_sample += 1; - let is_kf = self.keyframes.contains(&(self.cur_sample as u32)); - Some((ts, offset, size, is_kf)) - } else { - let chunk_start = self.samples_left == 0; - if self.samples_left == 0 { - if self.cur_chunk >= self.chunk_offsets.len() { - return None; - } - for (idx, samples) in self.sample_map.iter() { - if *idx as usize <= self.cur_chunk + 1 { - self.samples_left = *samples as usize; - } else { - break; - } - } - self.last_offset = self.chunk_offsets[self.cur_chunk]; - self.cur_chunk += 1; - } - let offset = self.last_offset; - let size = self.get_size(self.cur_sample); - self.last_offset += size as u64; - let is_kf = chunk_start && (self.stream_type == StreamType::Audio || self.keyframes.contains(&(self.cur_chunk as u32))); - if self.stream_type == StreamType::Video { - self.samples_left -= 1; - } else if self.frame_samples != 0 && self.bsize != 0 { - let nblocks = size / self.bsize; - if self.raw_audio { - ts.pts = Some(self.raw_apos); - ts.duration = Some(nblocks as u64); - self.raw_apos += nblocks as u64; - } - if nblocks > 0 { - let consumed = (nblocks * self.frame_samples).min(self.samples_left); - self.samples_left -= consumed; - } else { - self.samples_left = 0; - } - } else if !self.raw_audio { - self.samples_left -= 1; - } else { - const BLOCK_SAMPLES: usize = 1024 * 6; // should be multiple of 64 and 6 to fit both IMA ADPCM and MACE 6:1 blocks - self.last_offset -= size as u64; - let samples = self.samples_left.min(BLOCK_SAMPLES); - let cur_size = self.calculate_chunk_size(samples); - self.cur_sample += samples; - self.samples_left -= samples; - self.last_offset += cur_size as u64; - return Some((ts, offset, cur_size, true)); - } - self.cur_sample += 1; - Some((ts, offset, size, is_kf)) - } - } - fn get_size(&self, sample_no: usize) -> usize { - if !self.chunk_sizes.is_empty() { - self.chunk_sizes[sample_no] as usize - } else if self.stream_type != StreamType::Audio && self.sample_map.len() <= 1 && self.bsize > 0 { - self.bsize - } else if !self.sample_map.is_empty() && self.sample_size == 0 { - let mut nsamp = 0; - for (idx, samples) in self.sample_map.iter() { - if *idx as usize <= self.cur_chunk { - nsamp = *samples; - } else { - break; - } - } - self.calculate_chunk_size(nsamp as usize) - } else { - self.bsize - } - } - fn seek(&mut self, pts: u64, tpoint: NATimePoint, forced: bool) -> DemuxerResult { - let tgt_pts = match tpoint { - NATimePoint::Milliseconds(ms) => NATimeInfo::rescale_ts(ms, 1, 1000, self.tb_num, self.tb_den), - NATimePoint::PTS(ts) => ts, - _ => 0, - }; - self.cur_sample = pts as usize; - self.samples_left = 0; - self.cur_ts = None; - if forced && pts.abs_diff(tgt_pts) > 5 * u64::from(self.tb_den) { - self.cur_sample = tgt_pts as usize; - } - if self.stream_type == StreamType::Audio { - if let NATimePoint::Milliseconds(ms) = tpoint { - let exp_pts = NATimeInfo::rescale_ts(ms, 1, 1000, self.tb_num, self.tb_den); - if self.raw_audio { - if self.frame_samples != 0 { - self.raw_apos = exp_pts / (self.frame_samples as u64); - let mut apos = 0; - self.cur_sample = 0; - self.cur_chunk = 0; - let mut cmap = self.sample_map.iter(); - let mut cur_samps = 0; - let (mut next_idx, mut next_samples) = cmap.next().unwrap(); - loop { - if self.cur_chunk + 1 == next_idx as usize { - self.samples_left = cur_samps; - cur_samps = next_samples as usize; - if let Some((new_idx, new_samples)) = cmap.next() { - next_idx = *new_idx; - next_samples = *new_samples; - } - } - self.raw_apos = apos; - apos += (cur_samps / self.frame_samples) as u64; - if apos > exp_pts && (cur_samps == self.frame_samples || apos > exp_pts + 1) { - if self.cur_chunk >= self.chunk_offsets.len() { - return Err(DemuxerError::SeekError); - } - self.last_offset = self.chunk_offsets[self.cur_chunk]; - break; - } - self.cur_chunk += 1; - } - self.samples_left = cur_samps; - self.cur_chunk += 1; - } else { - self.raw_apos = exp_pts; - self.cur_sample = exp_pts as usize; - let mut csamp = 0; - self.cur_chunk = 0; - let mut cmap = self.sample_map.iter(); - let mut cur_samps = 0; - let (mut next_idx, mut next_samples) = cmap.next().unwrap(); - loop { - if self.cur_chunk + 1 == next_idx as usize { - self.samples_left = cur_samps; - cur_samps = next_samples as usize; - if let Some((new_idx, new_samples)) = cmap.next() { - next_idx = *new_idx; - next_samples = *new_samples; - } - } - csamp += cur_samps; - if csamp > self.cur_sample { - if self.cur_chunk >= self.chunk_offsets.len() { - return Err(DemuxerError::SeekError); - } - self.last_offset = self.chunk_offsets[self.cur_chunk]; - break; - } - self.cur_chunk += 1; - } - self.samples_left = csamp - self.cur_sample; - self.cur_chunk += 1; - } - } else if self.chunk_offsets.len() == self.chunk_sizes.len() && self.duration != 0 { - let new_sample = (self.chunk_sizes.len() as u64 * exp_pts / u64::from(self.duration)) as usize; - self.cur_sample = new_sample; - self.cur_chunk = self.cur_sample; - } else { - if !self.time_to_sample.is_empty() { - let mut remaining = exp_pts; - let mut abs_csamp = 0; - for &(count, scount) in self.time_to_sample.iter() { - let count = u64::from(count); - let scount = u64::from(scount); - let nblk = remaining / scount; - if nblk < count { - abs_csamp += nblk; - break; - } - remaining -= count * scount; - abs_csamp += count; - } - self.cur_sample = abs_csamp as usize; - } else { - self.cur_sample = exp_pts as usize; - } - let tgt_sample = self.cur_sample; - let mut csamp = 0; - self.cur_chunk = 0; - let mut cmap = self.sample_map.iter(); - let mut cur_samps = 0; - let (mut next_idx, mut next_samples) = cmap.next().unwrap(); - loop { - if self.cur_chunk + 1 == next_idx as usize { - self.samples_left = cur_samps; - cur_samps = next_samples as usize; - if let Some((new_idx, new_samples)) = cmap.next() { - next_idx = *new_idx; - next_samples = *new_samples; - } - } - csamp += cur_samps; - if csamp > self.cur_sample { - if self.cur_chunk >= self.chunk_offsets.len() { - self.cur_sample = csamp - cur_samps; - self.samples_left = 0; - self.cur_sample = csamp; - return Err(DemuxerError::SeekError); - } - self.last_offset = self.chunk_offsets[self.cur_chunk]; - break; - } - self.cur_chunk += 1; - } - self.cur_sample = csamp - cur_samps; - self.samples_left = cur_samps; - self.last_offset = self.chunk_offsets[self.cur_chunk]; - self.cur_chunk += 1; - - // try to refine sample position - if self.chunk_sizes.len() > self.chunk_offsets.len() { - for i in self.cur_sample..tgt_sample { - self.cur_sample += 1; - self.samples_left -= 1; - self.last_offset += u64::from(self.chunk_sizes[i]); - } - } - } - } else { - self.cur_chunk = self.cur_sample; - } - } else if self.chunk_offsets.len() != self.chunk_sizes.len() && !self.sample_map.is_empty() { - let mut csamp = 0; - self.cur_chunk = 0; - let mut cmap = self.sample_map.iter(); - let mut cur_samps = 0; - let (mut next_idx, mut next_samples) = cmap.next().unwrap(); - loop { - if self.cur_chunk + 1 == next_idx as usize { - self.samples_left = cur_samps; - cur_samps = next_samples as usize; - if let Some((new_idx, new_samples)) = cmap.next() { - next_idx = *new_idx; - next_samples = *new_samples; - } - } - csamp += cur_samps; - if csamp >= self.cur_sample { - if self.cur_chunk >= self.chunk_offsets.len() { - if self.stream_type == StreamType::Data { - self.cur_chunk = self.chunk_offsets.len(); - self.samples_left = 0; - return Ok(NATimeInfo::rescale_ts(tgt_pts, self.tb_num, self.tb_den, 1, 1000)); - } - return Err(DemuxerError::SeekError); - } - self.last_offset = self.chunk_offsets[self.cur_chunk]; - break; - } - self.cur_chunk += 1; - } - csamp -= cur_samps; - for sample_no in csamp..self.cur_sample { - self.last_offset += self.get_size(sample_no) as u64; - } - self.samples_left = csamp + cur_samps - self.cur_sample; - self.cur_chunk += 1; - } - let cur_pts = self.timesearch.map_time(self.cur_sample as u32, &self.time_to_sample); - let cur_time = NATimeInfo::rescale_ts(cur_pts, self.tb_num, self.tb_den, 1, 1000); - Ok(cur_time) - } -} - -#[allow(clippy::too_many_arguments)] -fn process_packet(src: &mut dyn ByteIO, strmgr: &StreamManager, track: &mut Track, ts: NATimeInfo, offset: u64, size: usize, first: bool, is_kf: bool) -> DemuxerResult { - if let Some(cpts) = ts.get_pts() { - let cts = NATimeInfo::rescale_ts(cpts, ts.tb_num, ts.tb_den, 1, 1000); - track.cur_ts = Some(cts); - } else { - track.cur_ts = None; - } - let stream = strmgr.get_stream(track.track_str_id); - if stream.is_none() { return Err(DemuxerError::InvalidData); } - let stream = stream.unwrap(); - src.seek(SeekFrom::Start(offset))?; - let mut pkt = src.read_packet(stream, ts, first | is_kf, size)?; - if let Some(ref pal) = track.pal { - let side_data = NASideData::Palette(first, pal.clone()); - pkt.add_side_data(side_data); - } - Ok(pkt) -} - -impl<'a> DemuxCore<'a> for MOVDemuxer<'a> { - fn open(&mut self, strmgr: &mut StreamManager, seek_index: &mut SeekIndex) -> DemuxerResult<()> { - let mut data_start = 0; - match self.demux_mode { - DemuxMode::Normal => self.read_root(strmgr)?, - DemuxMode::MacBin => { - let ver = self.src.read_byte()?; - validate!(ver == 0); - self.src.read_skip(64)?; - let tag = self.src.read_tag()?; - validate!(&tag == b"MooV"); - self.src.read_skip(14)?; - let data_length = self.src.read_u32be()?; - validate!(data_length > 8); - let rsrc_length = self.src.read_u32be()?; - validate!(rsrc_length > 0); - self.src.read_skip(31)?; - let ver = self.src.read_byte()?; - validate!(ver == 0x81); - let ver = self.src.read_byte()?; - validate!(ver == 0x81); - //xxx: maybe check header CRC - - let rsrc_start = 0x80 + ((data_length + 0x7F) & !0x7F); - self.src.seek(SeekFrom::Start(rsrc_start.into()))?; - let rsrc_off = self.src.read_u32be()?; - let rsrc_map_off = self.src.read_u32be()?; - let rsrc_size = self.src.read_u32be()?; - let _rsrc_map_size = self.src.read_u32be()?; - validate!(rsrc_off >= 0x10); - validate!(rsrc_map_off >= rsrc_off + rsrc_size); - self.src.seek(SeekFrom::Current(i64::from(rsrc_off - 16)))?; - // I'm too lazy to parse resource map, so let's just iterate over resources for movie header - let end_pos = u64::from(rsrc_start + rsrc_off + rsrc_size); - let mut peek_buf = [0u8; 8]; - while self.src.tell() < end_pos { - let cur_size = self.src.read_u32be()?; - validate!(self.src.tell() + u64::from(cur_size) <= end_pos); - if cur_size > 8 { - let rsize = self.src.peek_u32be()?; - if rsize == cur_size { - self.src.peek_buf(&mut peek_buf)?; - if &peek_buf[4..] == b"moov" { - self.src.read_skip(8)?; - self.src.peek_buf(&mut peek_buf)?; - if peek_buf == [0; 8] { - println!("This looks like an alpha version of the format, try warhol demuxer instead"); - return Err(DemuxerError::InvalidData); - } - self.read_moov(strmgr, u64::from(rsize - 8))?; - self.mdat_pos = 8; - break; - } - } - } - self.src.read_skip(cur_size as usize)?; - } - // if moov/mdat is not found, it may be in data part - if self.tracks.is_empty() || self.mdat_pos == 0 { - self.src.seek(SeekFrom::Start(0x80))?; - let end_pos = u64::from(data_length) + 0x80; - while self.src.tell() < end_pos && (self.mdat_pos == 0 || self.tracks.is_empty()) { - let size = self.src.read_u32be()?; - let tag = self.src.read_tag()?; - validate!(size >= 8); - match &tag { - b"moov" => { - self.src.peek_buf(&mut peek_buf)?; - if peek_buf == [0; 8] { - println!("This looks like an alpha version of the format, try warhol demuxer instead"); - return Err(DemuxerError::InvalidData); - } - self.read_moov(strmgr, u64::from(size - 8))?; - }, - b"mdat" => { - self.mdat_pos = self.src.tell() - 0x80; - self.src.read_skip(size as usize - 8)?; - }, - _ => { - self.src.read_skip(size as usize - 8)?; - }, - } - } - } - }, - DemuxMode::ResFork => { - let res_data_offset = self.src.read_u32be()?; - let res_map_offset = self.src.read_u32be()?; - let res_map_data_len = self.src.read_u32be()?; - let res_map_length = self.src.read_u32be()?; - validate!(res_data_offset == 0x100 && res_map_length > 16); - self.src.seek(SeekFrom::Start(u64::from(res_map_offset)))?; - let res_data_offset2 = self.src.read_u32be()?; - let res_map_offset2 = self.src.read_u32be()?; - let res_map_data_len2 = self.src.read_u32be()?; - let res_map_length2 = self.src.read_u32be()?; - validate!(res_data_offset == res_data_offset2 - && res_map_offset == res_map_offset2 - && res_map_data_len == res_map_data_len2 - && res_map_length == res_map_length2); - self.src.read_skip(res_map_length as usize - 16)?; - data_start = self.src.tell(); - self.read_root(strmgr)?; - }, - }; - validate!(self.mdat_pos > 0); - validate!(!self.tracks.is_empty()); - for track in self.tracks.iter_mut() { - let mut strm = None; - std::mem::swap(&mut track.stream, &mut strm); - if let Some(stream) = strm { - let str_id = strmgr.add_stream(stream).unwrap(); - track.track_str_id = str_id; - } - } - match self.demux_mode { - DemuxMode::MacBin => { - // patch data offsets - for track in self.tracks.iter_mut() { - for offset in track.chunk_offsets.iter_mut() { - *offset += 0x80; - } - } - }, - DemuxMode::ResFork => { - // patch data offsets - for track in self.tracks.iter_mut() { - for offset in track.chunk_offsets.iter_mut() { - *offset += data_start; - } - } - }, - _ => {}, - } - for track in self.tracks.iter() { - track.fill_seek_index(seek_index); - } - self.src.seek(SeekFrom::Start(self.mdat_pos))?; - self.cur_track = 0; - Ok(()) - } - - fn get_frame(&mut self, strmgr: &mut StreamManager) -> DemuxerResult { - if self.tracks.is_empty() { - return Err(DemuxerError::EOF); - } - let mut has_all_time = true; - let mut min_ts = u64::MAX; - for (trk_no, trk) in self.tracks.iter().enumerate() { - if strmgr.is_ignored(trk_no) { - continue; - } - if let Some(ts) = trk.cur_ts { - min_ts = min_ts.min(ts); - } else { - has_all_time = false; - break; - } - } - if has_all_time { - for (trk_no, track) in self.tracks.iter_mut().enumerate() { - if let Some(ts) = track.cur_ts { - if ts == min_ts { - let first = track.cur_sample == 0; - if let Some((cts, offset, size, is_kf)) = track.get_next_chunk() { - self.cur_track = trk_no + 1; - return process_packet(self.src, strmgr, track, cts, offset, size, first, is_kf); - } - } - } - } - } - - for _ in 0..self.tracks.len() { - if self.cur_track >= self.tracks.len() { - self.cur_track = 0; - } - let track = &mut self.tracks[self.cur_track]; - self.cur_track += 1; - if strmgr.is_ignored(self.cur_track - 1) { - continue; - } - let first = track.cur_sample == 0; - if let Some((cts, offset, size, is_kf)) = track.get_next_chunk() { - return process_packet(self.src, strmgr, track, cts, offset, size, first, is_kf); - } - } - Err(DemuxerError::EOF) - } - - fn seek(&mut self, time: NATimePoint, seek_index: &SeekIndex) -> DemuxerResult<()> { - let ret = seek_index.find_pos(time); - if ret.is_none() { - if let NATimePoint::Milliseconds(_) = time { - let mut aonly = true; - for track in self.tracks.iter() { - if track.stream_type != StreamType::Audio { - aonly = false; - break; - } - } - if aonly { - for track in self.tracks.iter_mut() { - track.seek(0, time, false)?; - } - return Ok(()); - } - } - if self.force_seek { - let mut ret = Ok(()); - for track in self.tracks.iter_mut() { - if let Err(err) = track.seek(0, time, true) { - ret = Err(err); - break; - } - } - if ret.is_err() { - for track in self.tracks.iter_mut() { - track.reset(); - } - } - return ret; - } - return Err(DemuxerError::SeekError); - } - let seek_info = ret.unwrap(); - let tbn = self.tracks[seek_info.str_id as usize].tb_num; - let tbd = self.tracks[seek_info.str_id as usize].tb_den; - let mut vpts = None; - let mut apts = None; - for track in self.tracks.iter_mut() { - let cur_pts = if track.track_id == seek_info.str_id { - seek_info.pts - } else { - seek_info.pts * u64::from(tbn) * u64::from(track.tb_den) / (u64::from(tbd) * u64::from(track.tb_num)) - }; - let actual_time = track.seek(cur_pts, time, false)?; - match track.stream_type { - StreamType::Video => vpts = Some(actual_time), - StreamType::Audio => apts = Some(actual_time), - _ => {}, - }; - } - /* For audio+video stream case when the post-seek actual times differ - by more than half a second try to seek audio to a closer position - to video. - */ - if let (Some(vtime), Some(atime)) = (vpts, apts) { - let acount = self.tracks.iter().filter(|trk| trk.stream_type == StreamType::Audio).count(); - let vcount = self.tracks.iter().filter(|trk| trk.stream_type == StreamType::Video).count(); - if (vcount == 1 && acount == 1) && (vtime.max(atime) - vtime.min(atime) > 500) && (atime != 0) { - for track in self.tracks.iter_mut() { - if track.stream_type == StreamType::Audio { - let new_pts = NATimeInfo::rescale_ts(vtime, 1, 1000, track.tb_num, track.tb_den); - track.seek(new_pts, NATimePoint::Milliseconds(vtime), false)?; - } - } - } - } - - Ok(()) - } - fn get_duration(&self) -> u64 { - if self.tb_den != 0 { - u64::from(self.duration) * 1000 / u64::from(self.tb_den) - } else { - 0 - } - } -} - -const PRINT_CHUNKS: &str = "print_chunks"; - -const DEMUXER_OPTIONS: &[NAOptionDefinition] = &[ - NAOptionDefinition { - name: PRINT_CHUNKS, - description: "Print parsed file structure", - opt_type: NAOptionDefinitionType::Bool }, - NAOptionDefinition { - name: FORCE_SEEK_OPTION, - description: FORCE_SEEK_OPTION_DESC, - opt_type: NAOptionDefinitionType::Bool }, -]; - -impl<'a> NAOptionHandler for MOVDemuxer<'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; - }, - (FORCE_SEEK_OPTION, NAValue::Bool(val)) => { - self.force_seek = *val; - }, - _ => {}, - } - } - } - } - } - fn query_option_value(&self, name: &str) -> Option { - match name { - PRINT_CHUNKS => Some(NAValue::Bool(self.print_chunks)), - FORCE_SEEK_OPTION => Some(NAValue::Bool(self.force_seek)), - _ => None, - } - } -} - -impl<'a> MOVDemuxer<'a> { - fn new(io: &'a mut dyn ByteIO) -> Self { - Self::new_common(io, DemuxMode::Normal) - } - fn new_macbinary(io: &'a mut dyn ByteIO) -> Self { - Self::new_common(io, DemuxMode::MacBin) - } - fn new_resfork(io: &'a mut dyn ByteIO) -> Self { - Self::new_common(io, DemuxMode::ResFork) - } - fn new_common(io: &'a mut dyn ByteIO, demux_mode: DemuxMode,) -> Self { - MOVDemuxer { - src: io, - depth: 0, - mdat_pos: 0, - mdat_size: 0, - tracks: Vec::with_capacity(2), - cur_track: 0, - tb_den: 0, - duration: 0, - pal: None, - ver_m1: false, - - moof_off: 0, - - print_chunks: false, - - demux_mode, - force_seek: false, - } - } - fn read_root(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<()> { - self.depth = 0; - while self.src.left() != 0 { - let ret = read_chunk_header(self.src); - if ret.is_err() { break; } - let (ctype, size) = ret.unwrap(); - if self.print_chunks { - print_cname(ctype, size, self.src.tell(), 0); - } - if IGNORED_CHUNKS.contains(&ctype) { - self.src.skip64(size)?; - continue; - } - let handler = ROOT_CHUNK_HANDLERS.iter().find(|x| x.ctype == ctype); - let read_size; - if let Some(handler) = handler { - read_size = (handler.parse)(self, strmgr, size)?; - } else { - println!("skipping unknown chunk {:08X} size {}", ctype, size); - read_size = 0; - } - validate!(read_size <= size); - self.src.skip64(size - read_size)?; - } -//todo check if all needed chunks are found - Ok(()) - } - read_chunk_list!(root; "moov", read_moov, MOOV_CHUNK_HANDLERS); - read_chunk_list!(root; "moof", read_moof, MOOF_CHUNK_HANDLERS); -} - -pub struct MOVDemuxerCreator { } - -impl DemuxerCreator for MOVDemuxerCreator { - fn new_demuxer<'a>(&self, br: &'a mut dyn ByteIO) -> Box + 'a> { - Box::new(MOVDemuxer::new(br)) - } - fn get_name(&self) -> &'static str { "mov" } -} - -pub struct MacBinaryMOVDemuxerCreator { } - -impl DemuxerCreator for MacBinaryMOVDemuxerCreator { - fn new_demuxer<'a>(&self, br: &'a mut dyn ByteIO) -> Box + 'a> { - Box::new(MOVDemuxer::new_macbinary(br)) - } - fn get_name(&self) -> &'static str { "mov-macbin" } -} - -pub struct MacResForkMOVDemuxerCreator { } - -impl DemuxerCreator for MacResForkMOVDemuxerCreator { - fn new_demuxer<'a>(&self, br: &'a mut dyn ByteIO) -> Box + 'a> { - Box::new(MOVDemuxer::new_resfork(br)) - } - fn get_name(&self) -> &'static str { "mov-resfork" } -} - -#[cfg(test)] -mod test { - use super::*; - use std::fs::File; - - #[test] - fn test_mov_demux() { - // sample: https://samples.mplayerhq.hu/V-codecs/IV32/cubes.mov - let mut file = File::open("assets/Indeo/cubes.mov").unwrap(); - let mut br = FileReader::new_read(&mut file); - let mut dmx = MOVDemuxer::new(&mut br); - let mut sm = StreamManager::new(); - let mut si = SeekIndex::new(); - dmx.open(&mut sm, &mut si).unwrap(); - - loop { - let pktres = dmx.get_frame(&mut sm); - if let Err(e) = pktres { - if e == DemuxerError::EOF { break; } - panic!("error"); - } - let pkt = pktres.unwrap(); - println!("Got {}", pkt); - } - } - - #[test] - fn test_dash_demux() { - // sample: a stream downloaded with youtube-dl - let mut file = File::open("assets/ITU/dash.m4a").unwrap(); - let mut br = FileReader::new_read(&mut file); - let mut dmx = MOVDemuxer::new(&mut br); - let mut sm = StreamManager::new(); - let mut si = SeekIndex::new(); - dmx.open(&mut sm, &mut si).unwrap(); - - loop { - let pktres = dmx.get_frame(&mut sm); - if let Err(e) = pktres { - if e == DemuxerError::EOF { break; } - panic!("error"); - } - let pkt = pktres.unwrap(); - println!("Got {}", pkt); - } - } - - #[test] - fn test_macbinary_demux() { - // sample from King's Quest VI Macintosh edition - let mut file = File::open("assets/QT/Halfdome.bin").unwrap(); - let mut br = FileReader::new_read(&mut file); - let mut dmx = MOVDemuxer::new_macbinary(&mut br); - let mut sm = StreamManager::new(); - let mut si = SeekIndex::new(); - dmx.open(&mut sm, &mut si).unwrap(); - - loop { - let pktres = dmx.get_frame(&mut sm); - if let Err(e) = pktres { - if e == DemuxerError::EOF { break; } - panic!("error"); - } - let pkt = pktres.unwrap(); - println!("Got {}", pkt); - } - } - - - #[test] - fn test_beta_qt() { - // sample from Apple Reference & Presentation Library 8 - let mut file = File::open("assets/QT/Smallwhale2").unwrap(); - let mut br = FileReader::new_read(&mut file); - let mut dmx = MOVDemuxer::new_macbinary(&mut br); - let mut sm = StreamManager::new(); - let mut si = SeekIndex::new(); - dmx.open(&mut sm, &mut si).unwrap(); - - loop { - let pktres = dmx.get_frame(&mut sm); - if let Err(e) = pktres { - if e == DemuxerError::EOF { break; } - panic!("error"); - } - let pkt = pktres.unwrap(); - println!("Got {}", pkt); - } - } - - #[test] - fn test_resfork_demux() { - // sample from The Wonders of Electricity: An Adventure in Safety - let mut file = File::open("assets/QT/car.mov").unwrap(); - let mut br = FileReader::new_read(&mut file); - let mut dmx = MOVDemuxer::new_resfork(&mut br); - let mut sm = StreamManager::new(); - let mut si = SeekIndex::new(); - dmx.open(&mut sm, &mut si).unwrap(); - - loop { - let pktres = dmx.get_frame(&mut sm); - if let Err(e) = pktres { - if e == DemuxerError::EOF { break; } - panic!("error"); - } - let pkt = pktres.unwrap(); - println!("Got {}", pkt); - } - } -} diff --git a/nihav-commonfmt/src/demuxers/mov/mod.rs b/nihav-commonfmt/src/demuxers/mov/mod.rs new file mode 100644 index 0000000..ad95837 --- /dev/null +++ b/nihav-commonfmt/src/demuxers/mov/mod.rs @@ -0,0 +1,910 @@ +use nihav_core::demuxers::*; +use nihav_registry::register::*; +use nihav_core::compr::deflate::*; + +macro_rules! mktag { + ($a:expr, $b:expr, $c:expr, $d:expr) => ({ + (($a as u32) << 24) | (($b as u32) << 16) | (($c as u32) << 8) | ($d as u32) + }); + ($arr:expr) => ({ + (($arr[0] as u32) << 24) | (($arr[1] as u32) << 16) | (($arr[2] as u32) << 8) | ($arr[3] as u32) + }); +} + +#[derive(Clone,Copy,Debug,PartialEq)] +enum DemuxMode { + Normal, + MacBin, + ResFork, +} + +trait Skip64 { + fn skip64(&mut self, size: u64) -> ByteIOResult<()>; +} + +impl Skip64 for T { + fn skip64(&mut self, size: u64) -> ByteIOResult<()> { + if (size as usize as u64) != size { + self.seek(SeekFrom::Current(size as i64))?; + } else { + self.read_skip(size as usize)?; + } + Ok(()) + } +} + +fn read_chunk_header(br: &mut dyn ByteIO) -> DemuxerResult<(u32, u64)> { + let size = br.read_u32be()?; + let ctype = br.read_u32be()?; + if size == 0 { + Ok((ctype, br.left() as u64)) + } else if size == 1 { + let size64 = br.read_u64be()?; + validate!(size64 >= 16); + Ok((ctype, size64 - 16)) + } else { + validate!(size >= 8); + Ok((ctype, (size as u64) - 8)) + } +} + +fn read_palette(br: &mut dyn ByteIO, size: u64, pal: &mut [u8; 1024]) -> DemuxerResult { + let _seed = br.read_u32be()?; + let flags = br.read_u16be()?; + let palsize = (br.read_u16be()? as usize) + 1; + validate!(palsize <= 256); + validate!(flags == 0 || flags == 0x4000 || flags == 0x8000); + validate!((palsize as u64) * 8 + 8 <= size); + for i in 0..palsize { + let a = br.read_u16be()?; + let r = br.read_u16be()?; + let g = br.read_u16be()?; + let b = br.read_u16be()?; + pal[i * 4] = (r >> 8) as u8; + pal[i * 4 + 1] = (g >> 8) as u8; + pal[i * 4 + 2] = (b >> 8) as u8; + if flags == 0x8000 { + pal[i * 4 + 3] = (a >> 8) as u8; + } + } + if flags == 0x4000 { + br.read_skip(8)?; + } + Ok(size) +} + +struct RootChunkHandler { + ctype: u32, + parse: fn(dmx: &mut MOVDemuxer, strmgr: &mut StreamManager, size: u64) -> DemuxerResult, +} + +const IGNORED_CHUNKS: &[u32] = &[ + mktag!(b"free"), mktag!(b"skip"), mktag!(b"udta"), mktag!(b"wide") +]; + +const ROOT_CHUNK_HANDLERS: &[RootChunkHandler] = &[ + RootChunkHandler { ctype: mktag!(b"ftyp"), parse: read_ftyp }, + RootChunkHandler { ctype: mktag!(b"mdat"), parse: read_mdat }, + RootChunkHandler { ctype: mktag!(b"moov"), parse: read_moov }, + RootChunkHandler { ctype: mktag!(b"moof"), parse: read_moof }, + RootChunkHandler { ctype: mktag!(b"sidx"), parse: read_sidx }, +]; + +fn print_cname(ctype: u32, size: u64, off: u64, depth: u8) { + for _ in 0..depth { print!(" "); } + let tag = [(ctype >> 24) as u8, (ctype >> 16) as u8, (ctype >> 8) as u8, ctype as u8]; + let mut printable = true; + for ch in tag.iter() { + if !(0x20..=0x7F).contains(ch) { + printable = false; + break; + } + } + if printable { + print!(" '{}{}{}{}'", tag[0] as char, tag[1] as char, tag[2] as char, tag[3] as char); + } else { + print!(" {:08X}", ctype); + } + println!(" size {} @ {:X}", size, off); +} + +macro_rules! read_chunk_list { + (root; $name: expr, $fname: ident, $handlers: ident) => { + fn $fname(&mut self, strmgr: &mut StreamManager, size: u64) -> DemuxerResult<()> { + self.depth += 1; + validate!(self.depth < 32); + let list_end = self.src.tell() + size; + while self.src.tell() < list_end { + let ret = read_chunk_header(self.src); + if ret.is_err() { break; } + let (ctype, size) = ret.unwrap(); + if self.print_chunks { + print_cname(ctype, size, self.src.tell(), self.depth as u8); + } + if self.src.tell() + size > list_end { + break; + } + if IGNORED_CHUNKS.contains(&ctype) { + self.src.skip64(size)?; + continue; + } + let handler = $handlers.iter().find(|x| x.ctype == ctype); + let read_size; + if let Some(ref handler) = handler { + read_size = (handler.parse)(self, strmgr, size)?; + } else { + println!("skipping unknown chunk {:08X} size {}", ctype, size); + read_size = 0; + } + validate!(read_size <= size); + self.src.skip64(size - read_size)?; + } + self.depth -= 1; + validate!(self.src.tell() == list_end); + Ok(()) + } + }; + (track; $name: expr, $fname: ident, $handlers: ident) => { + fn $fname(&mut self, br: &mut dyn ByteIO, size: u64) -> DemuxerResult<()> { + self.depth += 1; + validate!(self.depth < 32); + let list_end = br.tell() + size; + while br.tell() < list_end { + let ret = read_chunk_header(br); + if ret.is_err() { break; } + let (ctype, size) = ret.unwrap(); + if self.print_chunks { + print_cname(ctype, size, br.tell(), self.depth + 1); + } + if br.tell() + size > list_end { + break; + } + if IGNORED_CHUNKS.contains(&ctype) { + br.skip64(size)?; + continue; + } + let handler = $handlers.iter().find(|x| x.ctype == ctype); + let read_size; + if let Some(ref handler) = handler { + read_size = (handler.parse)(self, br, size)?; + } else { + read_size = 0; + } + validate!(read_size <= size); + br.skip64(size - read_size)?; + } + self.depth -= 1; + validate!(br.tell() == list_end); + Ok(()) + } + } +} + +mod track; +use track::*; +mod pktread; + +fn read_ftyp(dmx: &mut MOVDemuxer, _strmgr: &mut StreamManager, size: u64) -> DemuxerResult { + dmx.src.skip64(size)?; + Ok(size) +} + +fn read_mdat(dmx: &mut MOVDemuxer, _strmgr: &mut StreamManager, size: u64) -> DemuxerResult { + dmx.mdat_pos = dmx.src.tell(); + dmx.mdat_size = size; + dmx.src.skip64(size)?; + Ok(size) +} + +fn read_sidx(_dmx: &mut MOVDemuxer, _strmgr: &mut StreamManager, _size: u64) -> DemuxerResult { + Ok(0) +} + +fn read_moov(dmx: &mut MOVDemuxer, strmgr: &mut StreamManager, size: u64) -> DemuxerResult { + dmx.read_moov(strmgr, size)?; + Ok(size) +} + +const MOOV_CHUNK_HANDLERS: &[RootChunkHandler] = &[ + RootChunkHandler { ctype: mktag!(b"mvhd"), parse: read_mvhd }, + RootChunkHandler { ctype: mktag!(b"cmov"), parse: read_cmov }, + RootChunkHandler { ctype: mktag!(b"ctab"), parse: read_ctab }, + RootChunkHandler { ctype: mktag!(b"trak"), parse: read_trak }, + RootChunkHandler { ctype: mktag!(b"meta"), parse: read_meta }, + RootChunkHandler { ctype: mktag!(b"mvex"), parse: read_mvex }, + RootChunkHandler { ctype: mktag!(b"iods"), parse: skip_chunk_mov }, +]; + +fn read_mvhd(dmx: &mut MOVDemuxer, _strmgr: &mut StreamManager, size: u64) -> DemuxerResult { + const KNOWN_MVHD_SIZE: u64 = 100; + let br = &mut dmx.src; + validate!(size >= KNOWN_MVHD_SIZE); + let version = br.read_byte()?; + validate!(version == 0 || version == 0xFF); + dmx.ver_m1 = version == 0xFF; + let _flags = br.read_u24be()?; + let _ctime = br.read_u32be()?; + let _mtime = br.read_u32be()?; + let tscale = br.read_u32be()?; + let duration = br.read_u32be()?; + let _pref_rate = br.read_u32be()?; + let _pref_volume = br.read_u16be()?; + br.read_skip(10)?; + br.read_skip(36)?; // matrix + let _preview_time = br.read_u32be()?; + let _preview_duration = br.read_u32be()?; + let _poster_time = br.read_u32be()?; + let _sel_time = br.read_u32be()?; + let _sel_duration = br.read_u32be()?; + let _cur_time = br.read_u32be()?; + let _next_track_id = br.read_u32be()?; + dmx.duration = duration; + dmx.tb_den = tscale; + + Ok(KNOWN_MVHD_SIZE) +} + +fn read_cmov(dmx: &mut MOVDemuxer, strmgr: &mut StreamManager, size: u64) -> DemuxerResult { + let br = &mut dmx.src; + validate!(size > 24); + let dcom_size = br.read_u32be()?; + let dcom_tag = br.read_tag()?; + let compr_type = br.read_tag()?; + validate!(&dcom_tag == b"dcom" && dcom_size == 12); + if &compr_type != b"zlib" { + return Err(DemuxerError::NotImplemented); + } + let cmvd_size = u64::from(br.read_u32be()?); + let cmvd_tag = br.read_tag()?; + validate!(&cmvd_tag == b"cmvd" && cmvd_size > 14 && cmvd_size == size - 12); + let comp_size = (cmvd_size - 12) as usize; + let uncomp_size = br.read_u32be()? as usize; + validate!(uncomp_size > 8); + let mut sbuf = vec![0; comp_size]; + let mut dbuf = vec![0; uncomp_size]; + br.read_buf(sbuf.as_mut_slice())?; + let ret = Inflate::uncompress(sbuf.as_slice(), dbuf.as_mut_slice()); + if ret.is_err() { + return Err(DemuxerError::InvalidData); + } + let len = ret.unwrap(); + validate!(len == uncomp_size); + let mut br = MemoryReader::new_read(dbuf.as_slice()); + let (ctype, csize) = read_chunk_header(&mut br)?; + validate!(ctype == mktag!(b"moov")); + let mut ddmx = MOVDemuxer::new(&mut br); + ddmx.print_chunks = dmx.print_chunks; + ddmx.read_moov(strmgr, csize)?; + std::mem::swap(&mut dmx.tracks, &mut ddmx.tracks); + dmx.duration = ddmx.duration; + dmx.tb_den = ddmx.tb_den; + std::mem::swap(&mut dmx.pal, &mut ddmx.pal); + + Ok(size) +} + +fn read_ctab(dmx: &mut MOVDemuxer, _strmgr: &mut StreamManager, size: u64) -> DemuxerResult { + let mut pal = [0; 1024]; + let size = read_palette(dmx.src, size, &mut pal)?; + dmx.pal = Some(Arc::new(pal)); + Ok(size) +} + +fn read_meta(dmx: &mut MOVDemuxer, _strmgr: &mut StreamManager, size: u64) -> DemuxerResult { + dmx.src.skip64(size)?; + Ok(size) +} + +fn read_mvex(_dmx: &mut MOVDemuxer, _strmgr: &mut StreamManager, _size: u64) -> DemuxerResult { + Ok(0) +} + +fn skip_chunk_mov(_dmx: &mut MOVDemuxer, _strmgr: &mut StreamManager, _size: u64) -> DemuxerResult { + Ok(0) +} + +fn read_trak(dmx: &mut MOVDemuxer, strmgr: &mut StreamManager, size: u64) -> DemuxerResult { + let mut track = Track::new(dmx.cur_track as u32, dmx.tb_den); + track.print_chunks = dmx.print_chunks; + track.ver_m1 = dmx.ver_m1; + track.call_read_trak(dmx.src, size)?; + validate!(track.tkhd_found); + if !track.stsd_found { + track.stream = Some(NAStream::new(StreamType::Data, track.track_no, DUMMY_CODEC_INFO, track.tb_num, track.tb_den, 0)); + } + // invent keyframes for video stream if none were reported + if !track.stss_found && track.stream_type == StreamType::Video { + let mut intraonly = false; + if let Some(ref stream) = track.stream { + if let Some(desc) = get_codec_description(stream.get_info().get_name()) { + intraonly = (desc.caps & CODEC_CAP_INTRAONLY) != 0; + } + } + + track.pkt_demux.invent_keyframes(intraonly); + } + validate!(strmgr.get_stream_by_id(track.track_id).is_none()); + dmx.cur_track += 1; + dmx.tracks.push(track); + Ok(size) +} + +fn read_moof(dmx: &mut MOVDemuxer, strmgr: &mut StreamManager, size: u64) -> DemuxerResult { + dmx.moof_off = dmx.src.tell() - 8; + dmx.read_moof(strmgr, size)?; + Ok(size) +} + +const MOOF_CHUNK_HANDLERS: &[RootChunkHandler] = &[ + RootChunkHandler { ctype: mktag!(b"mfhd"), parse: read_mfhd }, + RootChunkHandler { ctype: mktag!(b"traf"), parse: read_traf }, + RootChunkHandler { ctype: mktag!(b"meta"), parse: read_meta }, +]; + +fn read_mfhd(dmx: &mut MOVDemuxer, _strmgr: &mut StreamManager, size: u64) -> DemuxerResult { + const KNOWN_MFHD_SIZE: u64 = 8; + validate!(size >= KNOWN_MFHD_SIZE); + let version = dmx.src.read_byte()?; + validate!(version == 0); + let flags = dmx.src.read_u24be()?; + validate!(flags == 0); + let _seq_no = dmx.src.read_u32be()?; + + Ok(KNOWN_MFHD_SIZE) +} + +fn read_traf(dmx: &mut MOVDemuxer, _strmgr: &mut StreamManager, size: u64) -> DemuxerResult { + let mut buf = [0u8; 16]; + dmx.src.peek_buf(&mut buf)?; + validate!(&buf[4..8] == b"tfhd"); + let track_id = read_u32be(&buf[12..16])?; + let mut track = None; + for trk in dmx.tracks.iter_mut() { + if trk.track_id == track_id { + track = Some(trk); + break; + } + } + if let Some(track) = track { + track.moof_off = dmx.moof_off; + track.call_read_traf(dmx.src, size)?; + Ok(size) + } else { + Ok(0) + } +} + +struct MOVDemuxer<'a> { + src: &'a mut dyn ByteIO, + depth: usize, + mdat_pos: u64, + mdat_size: u64, + tracks: Vec, + cur_track: usize, + tb_den: u32, + duration: u32, + pal: Option>, + ver_m1: bool, + + moof_off: u64, + + print_chunks: bool, + + demux_mode: DemuxMode, + + force_seek: bool, +} + +impl<'a> DemuxCore<'a> for MOVDemuxer<'a> { + fn open(&mut self, strmgr: &mut StreamManager, seek_index: &mut SeekIndex) -> DemuxerResult<()> { + let mut data_start = 0; + match self.demux_mode { + DemuxMode::Normal => self.read_root(strmgr)?, + DemuxMode::MacBin => { + let ver = self.src.read_byte()?; + validate!(ver == 0); + self.src.read_skip(64)?; + let tag = self.src.read_tag()?; + validate!(&tag == b"MooV"); + self.src.read_skip(14)?; + let data_length = self.src.read_u32be()?; + validate!(data_length > 8); + let rsrc_length = self.src.read_u32be()?; + validate!(rsrc_length > 0); + self.src.read_skip(31)?; + let ver = self.src.read_byte()?; + validate!(ver == 0x81); + let ver = self.src.read_byte()?; + validate!(ver == 0x81); + //xxx: maybe check header CRC + + let rsrc_start = 0x80 + ((data_length + 0x7F) & !0x7F); + self.src.seek(SeekFrom::Start(rsrc_start.into()))?; + let rsrc_off = self.src.read_u32be()?; + let rsrc_map_off = self.src.read_u32be()?; + let rsrc_size = self.src.read_u32be()?; + let _rsrc_map_size = self.src.read_u32be()?; + validate!(rsrc_off >= 0x10); + validate!(rsrc_map_off >= rsrc_off + rsrc_size); + self.src.seek(SeekFrom::Current(i64::from(rsrc_off - 16)))?; + // I'm too lazy to parse resource map, so let's just iterate over resources for movie header + let end_pos = u64::from(rsrc_start + rsrc_off + rsrc_size); + let mut peek_buf = [0u8; 8]; + while self.src.tell() < end_pos { + let cur_size = self.src.read_u32be()?; + validate!(self.src.tell() + u64::from(cur_size) <= end_pos); + if cur_size > 8 { + let rsize = self.src.peek_u32be()?; + if rsize == cur_size { + self.src.peek_buf(&mut peek_buf)?; + if &peek_buf[4..] == b"moov" { + self.src.read_skip(8)?; + self.src.peek_buf(&mut peek_buf)?; + if peek_buf == [0; 8] { + println!("This looks like an alpha version of the format, try warhol demuxer instead"); + return Err(DemuxerError::InvalidData); + } + self.read_moov(strmgr, u64::from(rsize - 8))?; + self.mdat_pos = 8; + break; + } + } + } + self.src.read_skip(cur_size as usize)?; + } + // if moov/mdat is not found, it may be in data part + if self.tracks.is_empty() || self.mdat_pos == 0 { + self.src.seek(SeekFrom::Start(0x80))?; + let end_pos = u64::from(data_length) + 0x80; + while self.src.tell() < end_pos && (self.mdat_pos == 0 || self.tracks.is_empty()) { + let size = self.src.read_u32be()?; + let tag = self.src.read_tag()?; + validate!(size >= 8); + match &tag { + b"moov" => { + self.src.peek_buf(&mut peek_buf)?; + if peek_buf == [0; 8] { + println!("This looks like an alpha version of the format, try warhol demuxer instead"); + return Err(DemuxerError::InvalidData); + } + self.read_moov(strmgr, u64::from(size - 8))?; + }, + b"mdat" => { + self.mdat_pos = self.src.tell() - 0x80; + self.src.read_skip(size as usize - 8)?; + }, + _ => { + self.src.read_skip(size as usize - 8)?; + }, + } + } + } + }, + DemuxMode::ResFork => { + let res_data_offset = self.src.read_u32be()?; + let res_map_offset = self.src.read_u32be()?; + let res_map_data_len = self.src.read_u32be()?; + let res_map_length = self.src.read_u32be()?; + validate!(res_data_offset == 0x100 && res_map_length > 16); + self.src.seek(SeekFrom::Start(u64::from(res_map_offset)))?; + let res_data_offset2 = self.src.read_u32be()?; + let res_map_offset2 = self.src.read_u32be()?; + let res_map_data_len2 = self.src.read_u32be()?; + let res_map_length2 = self.src.read_u32be()?; + validate!(res_data_offset == res_data_offset2 + && res_map_offset == res_map_offset2 + && res_map_data_len == res_map_data_len2 + && res_map_length == res_map_length2); + self.src.read_skip(res_map_length as usize - 16)?; + data_start = self.src.tell(); + self.read_root(strmgr)?; + }, + }; + validate!(self.mdat_pos > 0); + validate!(!self.tracks.is_empty()); + for track in self.tracks.iter_mut() { + let mut strm = None; + std::mem::swap(&mut track.stream, &mut strm); + if let Some(stream) = strm { + let str_id = strmgr.add_stream(stream).unwrap(); + track.track_str_id = str_id; + } + } + match self.demux_mode { + DemuxMode::MacBin => { + // patch data offsets + for track in self.tracks.iter_mut() { + for offset in track.pkt_demux.chunk_offsets.iter_mut() { + *offset += 0x80; + } + } + }, + DemuxMode::ResFork => { + // patch data offsets + for track in self.tracks.iter_mut() { + for offset in track.pkt_demux.chunk_offsets.iter_mut() { + *offset += data_start; + } + } + }, + _ => {}, + } + for track in self.tracks.iter() { + track.pkt_demux.fill_seek_index(track.track_no, seek_index); + } + self.src.seek(SeekFrom::Start(self.mdat_pos))?; + self.cur_track = 0; + Ok(()) + } + + fn get_frame(&mut self, strmgr: &mut StreamManager) -> DemuxerResult { + if self.tracks.is_empty() { + return Err(DemuxerError::EOF); + } + let mut has_all_time = true; + let mut min_ts = u64::MAX; + for (trk_no, trk) in self.tracks.iter().enumerate() { + if strmgr.is_ignored(trk_no) { + continue; + } + if let Some(ts) = trk.pkt_demux.cur_ts { + min_ts = min_ts.min(ts); + } else { + has_all_time = false; + break; + } + } + if has_all_time { + for (trk_no, track) in self.tracks.iter_mut().enumerate() { + if let Some(ts) = track.pkt_demux.cur_ts { + if ts == min_ts { + let first = track.pkt_demux.is_first(); + if let Some((cts, offset, size, is_kf)) = track.pkt_demux.get_next_chunk() { + self.cur_track = trk_no + 1; + return process_packet(self.src, strmgr, track, cts, offset, size, first, is_kf); + } + } + } + } + } + + for _ in 0..self.tracks.len() { + if self.cur_track >= self.tracks.len() { + self.cur_track = 0; + } + let track = &mut self.tracks[self.cur_track]; + self.cur_track += 1; + if strmgr.is_ignored(self.cur_track - 1) { + continue; + } + let first = track.pkt_demux.is_first(); + if let Some((cts, offset, size, is_kf)) = track.pkt_demux.get_next_chunk() { + return process_packet(self.src, strmgr, track, cts, offset, size, first, is_kf); + } + } + Err(DemuxerError::EOF) + } + + fn seek(&mut self, time: NATimePoint, seek_index: &SeekIndex) -> DemuxerResult<()> { + let ret = seek_index.find_pos(time); + if ret.is_none() { + if let NATimePoint::Milliseconds(_) = time { + let mut aonly = true; + for track in self.tracks.iter() { + if track.stream_type != StreamType::Audio { + aonly = false; + break; + } + } + if aonly { + for track in self.tracks.iter_mut() { + track.pkt_demux.seek(0, time, false)?; + } + return Ok(()); + } + } + if self.force_seek { + let mut ret = Ok(()); + for track in self.tracks.iter_mut() { + if let Err(err) = track.pkt_demux.seek(0, time, true) { + ret = Err(err); + break; + } + } + if ret.is_err() { + for track in self.tracks.iter_mut() { + track.pkt_demux.reset(); + } + } + return ret; + } + return Err(DemuxerError::SeekError); + } + let seek_info = ret.unwrap(); + let tbn = self.tracks[seek_info.str_id as usize].tb_num; + let tbd = self.tracks[seek_info.str_id as usize].tb_den; + let mut vpts = None; + let mut apts = None; + for track in self.tracks.iter_mut() { + let cur_pts = if track.track_id == seek_info.str_id { + seek_info.pts + } else { + seek_info.pts * u64::from(tbn) * u64::from(track.tb_den) / (u64::from(tbd) * u64::from(track.tb_num)) + }; + let actual_time = track.pkt_demux.seek(cur_pts, time, false)?; + match track.stream_type { + StreamType::Video => vpts = Some(actual_time), + StreamType::Audio => apts = Some(actual_time), + _ => {}, + }; + } + /* For audio+video stream case when the post-seek actual times differ + by more than half a second try to seek audio to a closer position + to video. + */ + if let (Some(vtime), Some(atime)) = (vpts, apts) { + let acount = self.tracks.iter().filter(|trk| trk.stream_type == StreamType::Audio).count(); + let vcount = self.tracks.iter().filter(|trk| trk.stream_type == StreamType::Video).count(); + if (vcount == 1 && acount == 1) && (vtime.max(atime) - vtime.min(atime) > 500) && (atime != 0) { + for track in self.tracks.iter_mut() { + if track.stream_type == StreamType::Audio { + let new_pts = NATimeInfo::rescale_ts(vtime, 1, 1000, track.tb_num, track.tb_den); + track.pkt_demux.seek(new_pts, NATimePoint::Milliseconds(vtime), false)?; + } + } + } + } + + Ok(()) + } + fn get_duration(&self) -> u64 { + if self.tb_den != 0 { + u64::from(self.duration) * 1000 / u64::from(self.tb_den) + } else { + 0 + } + } +} + +const PRINT_CHUNKS: &str = "print_chunks"; + +const DEMUXER_OPTIONS: &[NAOptionDefinition] = &[ + NAOptionDefinition { + name: PRINT_CHUNKS, + description: "Print parsed file structure", + opt_type: NAOptionDefinitionType::Bool }, + NAOptionDefinition { + name: FORCE_SEEK_OPTION, + description: FORCE_SEEK_OPTION_DESC, + opt_type: NAOptionDefinitionType::Bool }, +]; + +impl<'a> NAOptionHandler for MOVDemuxer<'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; + }, + (FORCE_SEEK_OPTION, NAValue::Bool(val)) => { + self.force_seek = *val; + }, + _ => {}, + } + } + } + } + } + fn query_option_value(&self, name: &str) -> Option { + match name { + PRINT_CHUNKS => Some(NAValue::Bool(self.print_chunks)), + FORCE_SEEK_OPTION => Some(NAValue::Bool(self.force_seek)), + _ => None, + } + } +} + +impl<'a> MOVDemuxer<'a> { + fn new(io: &'a mut dyn ByteIO) -> Self { + Self::new_common(io, DemuxMode::Normal) + } + fn new_macbinary(io: &'a mut dyn ByteIO) -> Self { + Self::new_common(io, DemuxMode::MacBin) + } + fn new_resfork(io: &'a mut dyn ByteIO) -> Self { + Self::new_common(io, DemuxMode::ResFork) + } + fn new_common(io: &'a mut dyn ByteIO, demux_mode: DemuxMode,) -> Self { + MOVDemuxer { + src: io, + depth: 0, + mdat_pos: 0, + mdat_size: 0, + tracks: Vec::with_capacity(2), + cur_track: 0, + tb_den: 0, + duration: 0, + pal: None, + ver_m1: false, + + moof_off: 0, + + print_chunks: false, + + demux_mode, + force_seek: false, + } + } + fn read_root(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<()> { + self.depth = 0; + while self.src.left() != 0 { + let ret = read_chunk_header(self.src); + if ret.is_err() { break; } + let (ctype, size) = ret.unwrap(); + if self.print_chunks { + print_cname(ctype, size, self.src.tell(), 0); + } + if IGNORED_CHUNKS.contains(&ctype) { + self.src.skip64(size)?; + continue; + } + let handler = ROOT_CHUNK_HANDLERS.iter().find(|x| x.ctype == ctype); + let read_size; + if let Some(handler) = handler { + read_size = (handler.parse)(self, strmgr, size)?; + } else { + println!("skipping unknown chunk {:08X} size {}", ctype, size); + read_size = 0; + } + validate!(read_size <= size); + self.src.skip64(size - read_size)?; + } +//todo check if all needed chunks are found + Ok(()) + } + read_chunk_list!(root; "moov", read_moov, MOOV_CHUNK_HANDLERS); + read_chunk_list!(root; "moof", read_moof, MOOF_CHUNK_HANDLERS); +} + +pub struct MOVDemuxerCreator { } + +impl DemuxerCreator for MOVDemuxerCreator { + fn new_demuxer<'a>(&self, br: &'a mut dyn ByteIO) -> Box + 'a> { + Box::new(MOVDemuxer::new(br)) + } + fn get_name(&self) -> &'static str { "mov" } +} + +pub struct MacBinaryMOVDemuxerCreator { } + +impl DemuxerCreator for MacBinaryMOVDemuxerCreator { + fn new_demuxer<'a>(&self, br: &'a mut dyn ByteIO) -> Box + 'a> { + Box::new(MOVDemuxer::new_macbinary(br)) + } + fn get_name(&self) -> &'static str { "mov-macbin" } +} + +pub struct MacResForkMOVDemuxerCreator { } + +impl DemuxerCreator for MacResForkMOVDemuxerCreator { + fn new_demuxer<'a>(&self, br: &'a mut dyn ByteIO) -> Box + 'a> { + Box::new(MOVDemuxer::new_resfork(br)) + } + fn get_name(&self) -> &'static str { "mov-resfork" } +} + +#[cfg(test)] +mod test { + use super::*; + use std::fs::File; + + #[test] + fn test_mov_demux() { + // sample: https://samples.mplayerhq.hu/V-codecs/IV32/cubes.mov + let mut file = File::open("assets/Indeo/cubes.mov").unwrap(); + let mut br = FileReader::new_read(&mut file); + let mut dmx = MOVDemuxer::new(&mut br); + let mut sm = StreamManager::new(); + let mut si = SeekIndex::new(); + dmx.open(&mut sm, &mut si).unwrap(); + + loop { + let pktres = dmx.get_frame(&mut sm); + if let Err(e) = pktres { + if e == DemuxerError::EOF { break; } + panic!("error"); + } + let pkt = pktres.unwrap(); + println!("Got {}", pkt); + } + } + + #[test] + fn test_dash_demux() { + // sample: a stream downloaded with youtube-dl + let mut file = File::open("assets/ITU/dash.m4a").unwrap(); + let mut br = FileReader::new_read(&mut file); + let mut dmx = MOVDemuxer::new(&mut br); + let mut sm = StreamManager::new(); + let mut si = SeekIndex::new(); + dmx.open(&mut sm, &mut si).unwrap(); + + loop { + let pktres = dmx.get_frame(&mut sm); + if let Err(e) = pktres { + if e == DemuxerError::EOF { break; } + panic!("error"); + } + let pkt = pktres.unwrap(); + println!("Got {}", pkt); + } + } + + #[test] + fn test_macbinary_demux() { + // sample from King's Quest VI Macintosh edition + let mut file = File::open("assets/QT/Halfdome.bin").unwrap(); + let mut br = FileReader::new_read(&mut file); + let mut dmx = MOVDemuxer::new_macbinary(&mut br); + let mut sm = StreamManager::new(); + let mut si = SeekIndex::new(); + dmx.open(&mut sm, &mut si).unwrap(); + + loop { + let pktres = dmx.get_frame(&mut sm); + if let Err(e) = pktres { + if e == DemuxerError::EOF { break; } + panic!("error"); + } + let pkt = pktres.unwrap(); + println!("Got {}", pkt); + } + } + + + #[test] + fn test_beta_qt() { + // sample from Apple Reference & Presentation Library 8 + let mut file = File::open("assets/QT/Smallwhale2").unwrap(); + let mut br = FileReader::new_read(&mut file); + let mut dmx = MOVDemuxer::new_macbinary(&mut br); + let mut sm = StreamManager::new(); + let mut si = SeekIndex::new(); + dmx.open(&mut sm, &mut si).unwrap(); + + loop { + let pktres = dmx.get_frame(&mut sm); + if let Err(e) = pktres { + if e == DemuxerError::EOF { break; } + panic!("error"); + } + let pkt = pktres.unwrap(); + println!("Got {}", pkt); + } + } + + #[test] + fn test_resfork_demux() { + // sample from The Wonders of Electricity: An Adventure in Safety + let mut file = File::open("assets/QT/car.mov").unwrap(); + let mut br = FileReader::new_read(&mut file); + let mut dmx = MOVDemuxer::new_resfork(&mut br); + let mut sm = StreamManager::new(); + let mut si = SeekIndex::new(); + dmx.open(&mut sm, &mut si).unwrap(); + + loop { + let pktres = dmx.get_frame(&mut sm); + if let Err(e) = pktres { + if e == DemuxerError::EOF { break; } + panic!("error"); + } + let pkt = pktres.unwrap(); + println!("Got {}", pkt); + } + } +} diff --git a/nihav-commonfmt/src/demuxers/mov/pktread.rs b/nihav-commonfmt/src/demuxers/mov/pktread.rs new file mode 100644 index 0000000..4d656f3 --- /dev/null +++ b/nihav-commonfmt/src/demuxers/mov/pktread.rs @@ -0,0 +1,483 @@ +use nihav_core::demuxers::*; + +#[derive(Default)] +pub struct TimeSearcher { + pub idx: usize, + pub base: u64, + pub sbase: u32, + pub cur_len: u32, + pub cur_mul: u32, +} + +impl TimeSearcher { + pub fn new() -> Self { Self::default() } + pub fn reset(&mut self) { + *self = Self::default(); + } + pub fn map_time(&mut self, sample: u32, tts: &[(u32, u32)]) -> u64 { + if tts.is_empty() { + u64::from(sample) + } else if sample >= self.sbase { + let mut sample = sample - self.sbase; + if self.idx == 0 { + let (cur_len, cur_mul) = tts[0]; + self.cur_len = cur_len; + self.cur_mul = cur_mul; + self.idx += 1; + } + while self.idx < tts.len() && sample > self.cur_len { + sample -= self.cur_len; + self.sbase += self.cur_len; + self.base += u64::from(self.cur_len) * u64::from(self.cur_mul); + self.cur_len = tts[self.idx].0; + self.cur_mul = tts[self.idx].1; + self.idx += 1; + } + self.base + u64::from(sample) * u64::from(self.cur_mul) + } else { + self.reset(); + self.map_time(sample, tts) + } + } +} + +#[repr(C)] +#[derive(Clone,Copy,Default)] +pub struct RLEPair(pub u32, pub T); + +#[derive(Default)] +pub struct RLESearcher { + pub array: Vec>, + pub idx: usize, + pub start: u64, + pub next: u64, +} + +impl RLESearcher { + //pub fn new() -> Self { Self::default() } + pub fn resize(&mut self, size: usize) { + self.array.clear(); + self.array.reserve(size); + } + pub fn reserve(&mut self, size: usize) { + self.array.reserve(size); + } + pub fn add(&mut self, len: u32, val: T) { + self.array.push(RLEPair(len, val)); + } + pub fn reset(&mut self) { + self.start = 0; + if !self.array.is_empty() { + self.idx = 0; + self.next = u64::from(self.array[0].0); + } else { + self.idx = self.array.len(); + self.next = 0; + } + } + pub fn map(&mut self, sample: u64) -> Option { + if sample < self.start { + self.reset(); + } + if self.idx < self.array.len() { + if sample < self.next { + Some(self.array[self.idx].1) + } else { + while (self.idx < self.array.len()) && (sample >= self.next) { + self.start = self.next; + self.idx += 1; + if self.idx < self.array.len() { + self.next += u64::from(self.array[self.idx].0); + } + } + if self.idx < self.array.len() { + Some(self.array[self.idx].1) + } else { + None + } + } + } else { + None + } + } +} + +#[derive(Default)] +pub struct QTPacketDemuxer { + pub stream_type: StreamType, + + pub tb_num: u32, + pub tb_den: u32, + pub cur_chunk: usize, + pub cur_sample: usize, + pub cur_ts: Option, + pub samples_left: usize, + pub last_offset: u64, + pub raw_audio: bool, + pub raw_apos: u64, + pub duration: u32, + pub channels: usize, + pub bits: usize, + pub bsize: usize, + pub fcc: [u8; 4], + + pub keyframes: Vec, + pub chunk_sizes: Vec, + pub chunk_offsets: Vec, + pub time_to_sample: Vec<(u32, u32)>, + pub sample_map: Vec<(u32, u32)>, + pub sample_size: u32, + pub frame_samples: usize, + pub ctts_map: RLESearcher, + pub ctts_version: u8, + pub timesearch: TimeSearcher, +} + +impl QTPacketDemuxer { + pub fn new() -> Self { Self::default() } + pub fn is_first(&self) -> bool { self.cur_sample == 0 } + pub fn fill_seek_index(&self, track_no: u32, seek_index: &mut SeekIndex) { + if !self.keyframes.is_empty() { + seek_index.mode = SeekIndexMode::Present; + } + let mut tsearch = TimeSearcher::new(); + for kf_time in self.keyframes.iter() { + let pts = tsearch.map_time(*kf_time - 1, &self.time_to_sample); + let time = NATimeInfo::rescale_ts(pts, self.tb_num, self.tb_den, 1, 1000); + seek_index.add_entry(track_no, SeekEntry { time, pts: u64::from(*kf_time - 1), pos: 0 }); + } + } + pub fn invent_keyframes(&mut self, intraonly: bool) { + self.keyframes.reserve(self.time_to_sample.len()); + if !self.time_to_sample.is_empty() { + let mut abs_csamp = 1u64; + for &(count, _scount) in self.time_to_sample.iter() { + let count = u64::from(count); + self.keyframes.push(abs_csamp as u32); + abs_csamp += count; + } + } else if intraonly && !self.chunk_sizes.is_empty() { + for samp in 0..self.chunk_sizes.len() { + self.keyframes.push((samp + 1) as u32); + } + } + } + fn calculate_chunk_size(&self, nsamp: usize) -> usize { + if nsamp == 0 { + self.bsize + } else { + match &self.fcc { + b"NONE" | b"raw " | b"twos" | b"sowt" | &[0, 0, 0, 0] => { + (nsamp * self.bits * self.channels + 7) >> 3 + }, + b"ima4" => { + let nblocks = (nsamp + 63) >> 6; + nblocks * 34 * self.channels + }, + b"MAC3" => { + (nsamp + 5) / 6 * 2 * self.channels + }, + b"MAC6" => { + (nsamp + 5) / 6 * self.channels + }, + b"in24" => nsamp * 3 * self.channels, + b"in32" | b"fl32" => nsamp * 4 * self.channels, + b"fl64" => nsamp * 8 * self.channels, + b"ulaw" | b"alaw" => nsamp, + b"ms\x00\x02" => { //MS ADPCM + ((nsamp - 1) / 2 + 7) * self.channels + }, + b"ms\x00\x11" => { //IMA ADPCM + (nsamp / 2 + 4) * self.channels + }, + _ => self.bsize, + } + } + } + pub fn reset(&mut self) { + self.cur_chunk = self.chunk_offsets.len(); + self.samples_left = 0; + self.cur_sample = self.chunk_sizes.len(); + } + pub fn get_next_chunk(&mut self) -> Option<(NATimeInfo, u64, usize, bool)> { + let dts_val = self.timesearch.map_time(self.cur_sample as u32, &self.time_to_sample); + let pts = if let Some(dts_corr) = self.ctts_map.map(self.cur_sample as u64) { + let pts = match self.ctts_version { + 0 => dts_val.wrapping_add(u64::from(dts_corr)), + 1 => dts_val.wrapping_add(i64::from(dts_corr as i32) as u64), + _ => unimplemented!(), + }; + if (pts as i64) < 0 { + None + } else { + Some(pts) + } + } else { + Some(dts_val) + }; + let mut ts = NATimeInfo::new(pts, Some(dts_val), None, self.tb_num, self.tb_den); + if self.chunk_offsets.len() == self.chunk_sizes.len() { // simple one-to-one mapping + if self.cur_sample >= self.chunk_sizes.len() { + return None; + } + let offset = self.chunk_offsets[self.cur_sample]; + let size = self.chunk_sizes[self.cur_sample] as usize; + self.cur_sample += 1; + let is_kf = self.keyframes.contains(&(self.cur_sample as u32)); + Some((ts, offset, size, is_kf)) + } else { + let chunk_start = self.samples_left == 0; + if self.samples_left == 0 { + if self.cur_chunk >= self.chunk_offsets.len() { + return None; + } + for (idx, samples) in self.sample_map.iter() { + if *idx as usize <= self.cur_chunk + 1 { + self.samples_left = *samples as usize; + } else { + break; + } + } + self.last_offset = self.chunk_offsets[self.cur_chunk]; + self.cur_chunk += 1; + } + let offset = self.last_offset; + let size = self.get_size(self.cur_sample); + self.last_offset += size as u64; + let is_kf = chunk_start && (self.stream_type == StreamType::Audio || self.keyframes.contains(&(self.cur_chunk as u32))); + if self.stream_type == StreamType::Video { + self.samples_left -= 1; + } else if self.frame_samples != 0 && self.bsize != 0 { + let nblocks = size / self.bsize; + if self.raw_audio { + ts.pts = Some(self.raw_apos); + ts.duration = Some(nblocks as u64); + self.raw_apos += nblocks as u64; + } + if nblocks > 0 { + let consumed = (nblocks * self.frame_samples).min(self.samples_left); + self.samples_left -= consumed; + } else { + self.samples_left = 0; + } + } else if !self.raw_audio { + self.samples_left -= 1; + } else { + const BLOCK_SAMPLES: usize = 1024 * 6; // should be multiple of 64 and 6 to fit both IMA ADPCM and MACE 6:1 blocks + self.last_offset -= size as u64; + let samples = self.samples_left.min(BLOCK_SAMPLES); + let cur_size = self.calculate_chunk_size(samples); + self.cur_sample += samples; + self.samples_left -= samples; + self.last_offset += cur_size as u64; + return Some((ts, offset, cur_size, true)); + } + self.cur_sample += 1; + Some((ts, offset, size, is_kf)) + } + } + fn get_size(&self, sample_no: usize) -> usize { + if !self.chunk_sizes.is_empty() { + self.chunk_sizes[sample_no] as usize + } else if self.stream_type != StreamType::Audio && self.sample_map.len() <= 1 && self.bsize > 0 { + self.bsize + } else if !self.sample_map.is_empty() && self.sample_size == 0 { + let mut nsamp = 0; + for (idx, samples) in self.sample_map.iter() { + if *idx as usize <= self.cur_chunk { + nsamp = *samples; + } else { + break; + } + } + self.calculate_chunk_size(nsamp as usize) + } else { + self.bsize + } + } + pub fn seek(&mut self, pts: u64, tpoint: NATimePoint, forced: bool) -> DemuxerResult { + let tgt_pts = match tpoint { + NATimePoint::Milliseconds(ms) => NATimeInfo::rescale_ts(ms, 1, 1000, self.tb_num, self.tb_den), + NATimePoint::PTS(ts) => ts, + _ => 0, + }; + self.cur_sample = pts as usize; + self.samples_left = 0; + self.cur_ts = None; + if forced && pts.abs_diff(tgt_pts) > 5 * u64::from(self.tb_den) { + self.cur_sample = tgt_pts as usize; + } + if self.stream_type == StreamType::Audio { + if let NATimePoint::Milliseconds(ms) = tpoint { + let exp_pts = NATimeInfo::rescale_ts(ms, 1, 1000, self.tb_num, self.tb_den); + if self.raw_audio { + if self.frame_samples != 0 { + self.raw_apos = exp_pts / (self.frame_samples as u64); + let mut apos = 0; + self.cur_sample = 0; + self.cur_chunk = 0; + let mut cmap = self.sample_map.iter(); + let mut cur_samps = 0; + let (mut next_idx, mut next_samples) = cmap.next().unwrap(); + loop { + if self.cur_chunk + 1 == next_idx as usize { + self.samples_left = cur_samps; + cur_samps = next_samples as usize; + if let Some((new_idx, new_samples)) = cmap.next() { + next_idx = *new_idx; + next_samples = *new_samples; + } + } + self.raw_apos = apos; + apos += (cur_samps / self.frame_samples) as u64; + if apos > exp_pts && (cur_samps == self.frame_samples || apos > exp_pts + 1) { + if self.cur_chunk >= self.chunk_offsets.len() { + return Err(DemuxerError::SeekError); + } + self.last_offset = self.chunk_offsets[self.cur_chunk]; + break; + } + self.cur_chunk += 1; + } + self.samples_left = cur_samps; + self.cur_chunk += 1; + } else { + self.raw_apos = exp_pts; + self.cur_sample = exp_pts as usize; + let mut csamp = 0; + self.cur_chunk = 0; + let mut cmap = self.sample_map.iter(); + let mut cur_samps = 0; + let (mut next_idx, mut next_samples) = cmap.next().unwrap(); + loop { + if self.cur_chunk + 1 == next_idx as usize { + self.samples_left = cur_samps; + cur_samps = next_samples as usize; + if let Some((new_idx, new_samples)) = cmap.next() { + next_idx = *new_idx; + next_samples = *new_samples; + } + } + csamp += cur_samps; + if csamp > self.cur_sample { + if self.cur_chunk >= self.chunk_offsets.len() { + return Err(DemuxerError::SeekError); + } + self.last_offset = self.chunk_offsets[self.cur_chunk]; + break; + } + self.cur_chunk += 1; + } + self.samples_left = csamp - self.cur_sample; + self.cur_chunk += 1; + } + } else if self.chunk_offsets.len() == self.chunk_sizes.len() && self.duration != 0 { + let new_sample = (self.chunk_sizes.len() as u64 * exp_pts / u64::from(self.duration)) as usize; + self.cur_sample = new_sample; + self.cur_chunk = self.cur_sample; + } else { + if !self.time_to_sample.is_empty() { + let mut remaining = exp_pts; + let mut abs_csamp = 0; + for &(count, scount) in self.time_to_sample.iter() { + let count = u64::from(count); + let scount = u64::from(scount); + let nblk = remaining / scount; + if nblk < count { + abs_csamp += nblk; + break; + } + remaining -= count * scount; + abs_csamp += count; + } + self.cur_sample = abs_csamp as usize; + } else { + self.cur_sample = exp_pts as usize; + } + let tgt_sample = self.cur_sample; + let mut csamp = 0; + self.cur_chunk = 0; + let mut cmap = self.sample_map.iter(); + let mut cur_samps = 0; + let (mut next_idx, mut next_samples) = cmap.next().unwrap(); + loop { + if self.cur_chunk + 1 == next_idx as usize { + self.samples_left = cur_samps; + cur_samps = next_samples as usize; + if let Some((new_idx, new_samples)) = cmap.next() { + next_idx = *new_idx; + next_samples = *new_samples; + } + } + csamp += cur_samps; + if csamp > self.cur_sample { + if self.cur_chunk >= self.chunk_offsets.len() { + self.cur_sample = csamp - cur_samps; + self.samples_left = 0; + self.cur_sample = csamp; + return Err(DemuxerError::SeekError); + } + self.last_offset = self.chunk_offsets[self.cur_chunk]; + break; + } + self.cur_chunk += 1; + } + self.cur_sample = csamp - cur_samps; + self.samples_left = cur_samps; + self.last_offset = self.chunk_offsets[self.cur_chunk]; + self.cur_chunk += 1; + + // try to refine sample position + if self.chunk_sizes.len() > self.chunk_offsets.len() { + for i in self.cur_sample..tgt_sample { + self.cur_sample += 1; + self.samples_left -= 1; + self.last_offset += u64::from(self.chunk_sizes[i]); + } + } + } + } else { + self.cur_chunk = self.cur_sample; + } + } else if self.chunk_offsets.len() != self.chunk_sizes.len() && !self.sample_map.is_empty() { + let mut csamp = 0; + self.cur_chunk = 0; + let mut cmap = self.sample_map.iter(); + let mut cur_samps = 0; + let (mut next_idx, mut next_samples) = cmap.next().unwrap(); + loop { + if self.cur_chunk + 1 == next_idx as usize { + self.samples_left = cur_samps; + cur_samps = next_samples as usize; + if let Some((new_idx, new_samples)) = cmap.next() { + next_idx = *new_idx; + next_samples = *new_samples; + } + } + csamp += cur_samps; + if csamp >= self.cur_sample { + if self.cur_chunk >= self.chunk_offsets.len() { + if self.stream_type == StreamType::Data { + self.cur_chunk = self.chunk_offsets.len(); + self.samples_left = 0; + return Ok(NATimeInfo::rescale_ts(tgt_pts, self.tb_num, self.tb_den, 1, 1000)); + } + return Err(DemuxerError::SeekError); + } + self.last_offset = self.chunk_offsets[self.cur_chunk]; + break; + } + self.cur_chunk += 1; + } + csamp -= cur_samps; + for sample_no in csamp..self.cur_sample { + self.last_offset += self.get_size(sample_no) as u64; + } + self.samples_left = csamp + cur_samps - self.cur_sample; + self.cur_chunk += 1; + } + let cur_pts = self.timesearch.map_time(self.cur_sample as u32, &self.time_to_sample); + let cur_time = NATimeInfo::rescale_ts(cur_pts, self.tb_num, self.tb_den, 1, 1000); + Ok(cur_time) + } +} diff --git a/nihav-commonfmt/src/demuxers/mov/track.rs b/nihav-commonfmt/src/demuxers/mov/track.rs new file mode 100644 index 0000000..581f331 --- /dev/null +++ b/nihav-commonfmt/src/demuxers/mov/track.rs @@ -0,0 +1,967 @@ +use nihav_core::demuxers::*; +use nihav_registry::register::*; +use nihav_codec_support::codecs::qt_pal::*; + +use super::*; +use super::pktread::*; + +macro_rules! mktag { + ($a:expr, $b:expr, $c:expr, $d:expr) => ({ + (($a as u32) << 24) | (($b as u32) << 16) | (($c as u32) << 8) | ($d as u32) + }); + ($arr:expr) => ({ + (($arr[0] as u32) << 24) | (($arr[1] as u32) << 16) | (($arr[2] as u32) << 8) | ($arr[3] as u32) + }); +} + +const QT_RGB555_FORMAT: NAPixelFormaton = NAPixelFormaton::make_rgb16_fmt(5, 5, 5, true, true); + +pub const ARGB_FORMAT: NAPixelFormaton = NAPixelFormaton { + model: ColorModel::RGB(RGBSubmodel::RGB), components: 4, + comp_info: [ + Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 1, next_elem: 4 }), + Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 2, next_elem: 4 }), + Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 3, next_elem: 4 }), + Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 0, next_elem: 4 }), + None ], + elem_size: 4, be: false, alpha: true, palette: false }; + +struct TrackChunkHandler { + ctype: u32, + parse: fn(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult, +} + +const IGNORED_CHUNKS: &[u32] = &[ + mktag!(b"free"), mktag!(b"skip"), mktag!(b"udta"), mktag!(b"wide") +]; + +fn skip_chunk(_track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { + br.skip64(size)?; + Ok(size) +} + +const TRAK_CHUNK_HANDLERS: &[TrackChunkHandler] = &[ + TrackChunkHandler { ctype: mktag!(b"clip"), parse: skip_chunk }, + TrackChunkHandler { ctype: mktag!(b"matt"), parse: skip_chunk }, + TrackChunkHandler { ctype: mktag!(b"edts"), parse: skip_chunk }, + TrackChunkHandler { ctype: mktag!(b"tref"), parse: skip_chunk }, + TrackChunkHandler { ctype: mktag!(b"load"), parse: skip_chunk }, + TrackChunkHandler { ctype: mktag!(b"imap"), parse: skip_chunk }, + TrackChunkHandler { ctype: mktag!(b"tkhd"), parse: read_tkhd }, + TrackChunkHandler { ctype: mktag!(b"mdia"), parse: read_mdia }, +]; + +fn read_tkhd(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { + const KNOWN_TKHD_SIZE: u64 = 84; + validate!(size >= KNOWN_TKHD_SIZE); + let version = br.read_byte()?; + validate!(version == 0); + let _flags = br.read_u24be()?; + let _ctime = br.read_u32be()?; + let _mtime = br.read_u32be()?; + let track_id = br.read_u32be()?; + br.read_skip(4)?; + let duration = br.read_u32be()?; + br.read_skip(8)?; + let _layer = br.read_u16be()?; + let _alt_group = br.read_u16be()?; + let _volume = br.read_u16be()?; + br.read_skip(2)?; + br.read_skip(36)?; // matrix + let width = br.read_u32be()? as usize; + let height = br.read_u32be()? as usize; + track.width = width >> 16; + track.height = height >> 16; + track.track_id = track_id; + track.pkt_demux.duration = duration; + + track.tkhd_found = true; + Ok(KNOWN_TKHD_SIZE) +} + +fn read_mdia(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { + track.stream_type = StreamType::None; + track.read_mdia(br, size)?; + Ok(size) +} + +const MDIA_CHUNK_HANDLERS: &[TrackChunkHandler] = &[ + TrackChunkHandler { ctype: mktag!(b"mdhd"), parse: read_mdhd }, + TrackChunkHandler { ctype: mktag!(b"hdlr"), parse: read_hdlr }, + TrackChunkHandler { ctype: mktag!(b"minf"), parse: read_minf }, +]; + +fn read_mdhd(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { + const KNOWN_MDHD_SIZE: u64 = 24; + validate!(size >= KNOWN_MDHD_SIZE); + let version = br.read_byte()?; + validate!(version == 0); + let flags = br.read_u24be()?; + validate!(flags == 0); + let _ctime = br.read_u32be()?; + let _mtime = br.read_u32be()?; + track.tb_den = br.read_u32be()?; + validate!(track.tb_den != 0); + track.pkt_demux.tb_den = track.tb_den; + track.pkt_demux.duration = br.read_u32be()?; + let _language = br.read_u16be()?; + let _quality = br.read_u16be()?; + + Ok(KNOWN_MDHD_SIZE) +} + +fn read_hdlr(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { + const KNOWN_HDLR_SIZE: u64 = 24; + validate!(size >= KNOWN_HDLR_SIZE); + let version = br.read_byte()?; + validate!(version == 0); + let flags = br.read_u24be()?; + validate!(flags == 0); + let comp_type = br.read_u32be()?; + let comp_subtype = br.read_u32be()?; + let _comp_manufacturer = br.read_u32be()?; + let _comp_flags = br.read_u32be()?; + let _comp_flags_mask = br.read_u32be()?; + + if comp_type == mktag!(b"mhlr") || comp_type == 0 { + if comp_subtype == mktag!(b"vide") { + track.stream_type = StreamType::Video; + } else if comp_subtype == mktag!(b"soun") { + track.stream_type = StreamType::Audio; + } else { + track.stream_type = StreamType::Data; + } + } else if comp_type == mktag!(b"dhlr") { + track.stream_type = StreamType::Data; + } else { + println!("Unknown stream type"); + track.stream_type = StreamType::Data; + } + + Ok(KNOWN_HDLR_SIZE) +} + +fn read_minf(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { + track.read_minf(br, size)?; + Ok(size) +} + +const MINF_CHUNK_HANDLERS: &[TrackChunkHandler] = &[ + TrackChunkHandler { ctype: mktag!(b"hdlr"), parse: skip_chunk }, + TrackChunkHandler { ctype: mktag!(b"dinf"), parse: skip_chunk }, + TrackChunkHandler { ctype: mktag!(b"vmhd"), parse: read_vmhd }, + TrackChunkHandler { ctype: mktag!(b"smhd"), parse: read_smhd }, + TrackChunkHandler { ctype: mktag!(b"gmhd"), parse: read_gmhd }, + TrackChunkHandler { ctype: mktag!(b"gmin"), parse: read_gmin }, + TrackChunkHandler { ctype: mktag!(b"stbl"), parse: read_stbl }, +]; + +fn read_vmhd(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { + const KNOWN_VMHD_SIZE: u64 = 12; + validate!(track.stream_type == StreamType::Video); + validate!(size >= KNOWN_VMHD_SIZE); + let version = br.read_byte()?; + validate!(version == 0); + let _flags = br.read_u24be()?; + br.read_skip(2)?; // graphics mode + br.read_skip(6)?; // opcolor + Ok(KNOWN_VMHD_SIZE) +} + +fn read_smhd(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { + const KNOWN_SMHD_SIZE: u64 = 8; + validate!(track.stream_type == StreamType::Audio); + validate!(size >= KNOWN_SMHD_SIZE); + let version = br.read_byte()?; + validate!(version == 0); + let _flags = br.read_u24be()?; + br.read_skip(2)?; // balance + br.read_skip(2)?; + Ok(KNOWN_SMHD_SIZE) +} + +fn read_gmhd(track: &mut Track, _br: &mut dyn ByteIO, _size: u64) -> DemuxerResult { + validate!(track.stream_type == StreamType::Data); + Ok(0) +} + +fn read_gmin(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { + validate!(track.stream_type == StreamType::Data); + const KNOWN_GMIN_SIZE: u64 = 16; + validate!(size >= KNOWN_GMIN_SIZE); + let version = br.read_byte()?; + validate!(version == 0); + let _flags = br.read_u24be()?; + br.read_skip(2)?; // graphics mode + br.read_skip(6)?; // opcolor + br.read_skip(2)?; // balance + br.read_skip(2)?; + Ok(KNOWN_GMIN_SIZE) +} + +fn read_stbl(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { + track.read_stbl(br, size)?; + Ok(size) +} + +const STBL_CHUNK_HANDLERS: &[TrackChunkHandler] = &[ + TrackChunkHandler { ctype: mktag!(b"stsd"), parse: read_stsd }, + TrackChunkHandler { ctype: mktag!(b"stts"), parse: read_stts }, + TrackChunkHandler { ctype: mktag!(b"stgs"), parse: read_stgs }, + TrackChunkHandler { ctype: mktag!(b"stss"), parse: read_stss }, + TrackChunkHandler { ctype: mktag!(b"stsc"), parse: read_stsc }, + TrackChunkHandler { ctype: mktag!(b"stsz"), parse: read_stsz }, + TrackChunkHandler { ctype: mktag!(b"stco"), parse: read_stco }, + TrackChunkHandler { ctype: mktag!(b"co64"), parse: read_co64 }, + TrackChunkHandler { ctype: mktag!(b"stsh"), parse: skip_chunk }, + TrackChunkHandler { ctype: mktag!(b"ctts"), parse: read_ctts }, +]; + +fn parse_audio_edata(br: &mut dyn ByteIO, start_pos: u64, size: u64) -> DemuxerResult>> { + let read_part = br.tell() - start_pos; + if read_part + 8 < size { + let mut buf = [0; 8]; + br.peek_buf(&mut buf)?; + if &buf[4..8] != b"wave" { + let mut buf = vec![0; (size - read_part) as usize]; + br.read_buf(&mut buf)?; + return Ok(Some(buf)); + } + + let csize = br.read_u32be()? as u64; + let ctag = br.read_u32be()?; + validate!(read_part + csize <= size); + validate!(ctag == mktag!(b"wave")); + if csize == 8 { + return Ok(None); + } + let mut buf = [0; 8]; + br.peek_buf(&mut buf)?; + if &buf[4..8] == b"frma" { + br.read_skip(12)?; + if csize > 20 { + let mut buf = vec![0; (csize - 20) as usize]; + br.read_buf(&mut buf)?; + Ok(Some(buf)) + } else { + Ok(None) + } + } else if csize > 8 { + let mut buf = vec![0; (csize as usize) - 8]; + br.read_buf(&mut buf)?; + Ok(Some(buf)) + } else { + Ok(None) + } + } else { + Ok(None) + } +} + +#[allow(clippy::neg_cmp_op_on_partial_ord)] +fn read_stsd(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { + const KNOWN_STSD_SIZE: u64 = 24; + validate!(size >= KNOWN_STSD_SIZE); + let start_pos = br.tell(); + let version = br.read_byte()?; + validate!(version == 0); + let _flags = br.read_u24be()?; + let entries = br.read_u32be()?; + validate!(entries > 0); + let esize = u64::from(br.read_u32be()?); + validate!(esize + 8 <= size); + let mut fcc = [0u8; 4]; + br.read_buf(&mut fcc)?; + br.read_skip(6)?; + let _data_ref = br.read_u16be()?; + + track.pkt_demux.fcc = fcc; + + let codec_info; + match track.stream_type { + StreamType::Video => { + let _ver = br.read_u16be()?; + let _revision = br.read_u16le()?; + let _vendor = br.read_u32be()?; + let _temp_quality = br.read_u32be()?; + let _spat_quality = br.read_u32be()?; + let width = br.read_u16be()? as usize; + let height = br.read_u16be()? as usize; + let _hor_res = br.read_u32be()?; + let _vert_res = br.read_u32be()?; + let data_size = br.read_u32be()?; + if !track.ver_m1 { + validate!(data_size == 0); + } + let _frame_count = br.read_u16be()? as usize; + let _cname_len = br.read_byte()? as usize; + br.read_skip(31)?; // actual compressor name + let depth = br.read_u16be()?; + let ctable_id = br.read_u16be()?; + let grayscale = depth > 0x20 || depth == 1; + let depth = if grayscale { depth & 0x1F } else { depth }; + if ctable_id == 0 { + let max_pal_size = start_pos + size - br.tell(); + if depth <= 8 { + let mut pal = [0; 1024]; + read_palette(br, max_pal_size, &mut pal)?; + track.pal = Some(Arc::new(pal)); + } else { + br.read_skip(max_pal_size as usize)?; + } + } else if (depth <= 8) && !grayscale { + match depth & 0x1F { + 2 => { + let mut pal = [0; 1024]; + pal[..4 * 4].copy_from_slice(&MOV_DEFAULT_PAL_2BIT); + track.pal = Some(Arc::new(pal)); + }, + 4 => { + let mut pal = [0; 1024]; + pal[..16 * 4].copy_from_slice(&MOV_DEFAULT_PAL_4BIT); + track.pal = Some(Arc::new(pal)); + }, + 8 => { + track.pal = Some(Arc::new(MOV_DEFAULT_PAL_8BIT)); + }, + _ => {}, + }; + } else if grayscale && ctable_id != 0xFFFF { + let mut pal = [0; 1024]; + let cdepth = depth & 0x1F; + let size = 1 << cdepth; + for i in 0..size { + let mut clr = ((size - 1 - i) as u8) << (8 - cdepth); + let mut off = 8 - cdepth; + while off >= cdepth { + clr |= clr >> (8 - off); + off -= cdepth; + } + if off > 0 { + clr |= clr >> (8 - off); + } + pal[i * 4] = clr; + pal[i * 4 + 1] = clr; + pal[i * 4 + 2] = clr; + } + track.pal = Some(Arc::new(pal)); + } +// todo other atoms, put as extradata + let cname = if let Some(name) = find_codec_from_mov_video_fourcc(&fcc) { + name + } else if let Some(name) = find_codec_from_avi_fourcc(&fcc) { + name + } else { + "unknown" + }; + let format = if cname == "rawvideo" { + if &fcc == b"j420" { + validate!(depth == 12); + YUV420_FORMAT + } else { + match depth { + 1..=8 | 33..=40 => PAL8_FORMAT, + 15 | 16 => QT_RGB555_FORMAT, + 24 => RGB24_FORMAT, + 32 => ARGB_FORMAT, + _ => { + println!("unknown depth {depth}"); + return Err(DemuxerError::NotImplemented); + } + } + } + } else if depth > 8 && depth <= 32 { + RGB24_FORMAT + } else { + PAL8_FORMAT + }; + let mut vhdr = NAVideoInfo::new(width, height, false, format); + vhdr.bits = depth as u8; + if track.ver_m1 { // skip the rest for the beta version of QT + br.seek(SeekFrom::Start(start_pos + size))?; + } + //skip various common atoms + while br.tell() - start_pos + 8 < size { + let mut buf = [0u8; 8]; + br.peek_buf(&mut buf)?; + let tsize = read_u32be(&buf).unwrap() as usize; + let tag = &buf[4..8]; + validate!(tsize >= 8); + match tag { + b"pasp" | b"clap" | b"gama" => { + br.read_skip(tsize)?; + }, + _ => break, + }; + } + let edata = if br.tell() - start_pos + 4 < size { + let edata_size = br.read_u32be()? as usize; + validate!(edata_size >= 4); + let mut buf = vec![0; edata_size - 4]; + br.read_buf(buf.as_mut_slice())?; + Some(buf) + } else { + None + }; + codec_info = NACodecInfo::new(cname, NACodecTypeInfo::Video(vhdr), edata); + }, + StreamType::Audio if track.ver_m1 => { + br.read_skip(8)?; + let nchannels = br.read_u16be()?; + validate!(nchannels == 1 || nchannels == 2); + let sample_size = br.read_u16be()?; + validate!(sample_size == 8 || sample_size == 16); + br.read_u32be()?; + let sample_rate = br.read_u32be()? >> 16; + let cname = if fcc == [0; 4] { "pcm" } else { "unknown" }; + let mut soniton = NASoniton::new(sample_size as u8, SONITON_FLAG_SIGNED | SONITON_FLAG_BE); + if sample_size == 8 { + soniton.signed = false; + } + let ahdr = NAAudioInfo::new(sample_rate, nchannels as u8, soniton, 1); + let edata = parse_audio_edata(br, start_pos, size)?; + codec_info = NACodecInfo::new(cname, NACodecTypeInfo::Audio(ahdr), edata); + track.pkt_demux.raw_audio = cname == "pcm"; + track.pkt_demux.bsize = (sample_size / 8) as usize; + track.pkt_demux.channels = nchannels as usize; + track.pkt_demux.bits = sample_size as usize; + if track.tb_den <= 1 { + track.tb_den = sample_rate; + } + }, + StreamType::Audio => { + let sver = br.read_u16be()?; + let _revision = br.read_u16le()?; + let _vendor = br.read_u32be()?; + let mut nchannels = br.read_u16be()?; + if sver != 2 { + validate!(nchannels <= 64); + } + let sample_size = br.read_u16be()?; + validate!(sample_size <= 128); + let _compr_id = br.read_u16be()?; + let packet_size = br.read_u16be()? as usize; + validate!(packet_size == 0); + let mut sample_rate = br.read_u32be()? >> 16; + if sver != 2 { + validate!(sample_rate > 0); + } + let cname = if let Some(name) = find_codec_from_mov_audio_fourcc(&fcc) { + name + } else if let (true, Some(name)) = ((fcc[0] == b'm' && fcc[1] == b's'), find_codec_from_wav_twocc(u16::from(fcc[2]) * 256 + u16::from(fcc[3]))) { + name + } else { + "unknown" + }; + let mut soniton = NASoniton::new(sample_size as u8, SONITON_FLAG_SIGNED | SONITON_FLAG_BE); + if &fcc == b"raw " && sample_size == 8 { + soniton.signed = false; + } + if &fcc == b"sowt" { + soniton.be = false; + } + let mut block_align = 1; + match sver { + 1 => { + let samples_per_packet = br.read_u32be()?; + let _bytes_per_packet = br.read_u32be()?; + let bytes_per_frame = br.read_u32be()?; + let _bytes_per_sample = br.read_u32be()?; + track.pkt_demux.bsize = bytes_per_frame as usize; + track.pkt_demux.frame_samples = samples_per_packet as usize; + track.tb_num = samples_per_packet.max(1); + block_align = bytes_per_frame as usize; + }, + 2 => { + br.read_u32be()?; // some size + let srate = br.read_f64be()?; + validate!(srate > 1.0); + sample_rate = srate as u32; + let channels = br.read_u32be()?; + validate!(channels > 0 && channels < 255); + nchannels = channels as u16; + br.read_u32be()?; // always 0x7F000000 + let _bits_per_csample = br.read_u32be()?; + let _codec_flags = br.read_u32be()?; + let bytes_per_frame = br.read_u32be()?; + let samples_per_packet = br.read_u32be()?; + track.pkt_demux.bsize = bytes_per_frame as usize; + track.pkt_demux.frame_samples = samples_per_packet as usize; + track.tb_num = samples_per_packet; + }, + _ => { + track.pkt_demux.bsize = (sample_size / 8) as usize; + }, + }; + if track.tb_den <= 1 { + track.tb_den = sample_rate; + track.pkt_demux.tb_den = sample_rate; + } + track.pkt_demux.raw_audio = matches!(&fcc, + b"NONE" | b"raw " | b"twos" | b"sowt" | + b"in24" | b"in32" | b"fl32" | b"fl64" | + b"ima4" | b"ms\x00\x02" | b"ms\x00\x11" | + b"alaw" | b"ulaw" | + b"MAC3" | b"MAC6"); + let ahdr = NAAudioInfo::new(sample_rate, nchannels as u8, soniton, block_align); + let edata = parse_audio_edata(br, start_pos, size)?; + codec_info = NACodecInfo::new(cname, NACodecTypeInfo::Audio(ahdr), edata); + track.pkt_demux.channels = nchannels as usize; + track.pkt_demux.bits = sample_size as usize; + }, + StreamType::None => { + return Err(DemuxerError::InvalidData); + }, + _ => { +//todo put it all into extradata + let edata = None; + codec_info = NACodecInfo::new("unknown", NACodecTypeInfo::None, edata); + }, + }; + track.pkt_demux.tb_num = track.tb_num; + track.pkt_demux.tb_den = track.tb_den; + track.pkt_demux.stream_type = track.stream_type; + let read_size = br.tell() - start_pos; + validate!(read_size <= size); + track.stream = Some(NAStream::new(track.stream_type, track.track_no, codec_info, track.tb_num, track.tb_den, u64::from(track.pkt_demux.duration))); + track.stsd_found = true; + Ok(read_size) +} + +fn read_stts(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { + validate!(size >= 8); + let start_pos = br.tell(); + let version = br.read_byte()?; + validate!(version == 0); + let _flags = br.read_u24be()?; + let entries = br.read_u32be()? as usize; + validate!(entries as u64 <= (size - 8) / 8); + if entries == 0 { + } else if entries == 1 { + let _count = br.read_u32be()?; + let tb_num = br.read_u32be()?; + validate!(tb_num != 0); + track.rescale(tb_num); + } else { + track.pkt_demux.time_to_sample.clear(); + track.pkt_demux.time_to_sample.reserve(entries); + for _ in 0..entries { + let count = br.read_u32be()?; + let mult = br.read_u32be()?; + track.pkt_demux.time_to_sample.push((count, mult)); + } + } + let read_size = br.tell() - start_pos; + validate!(read_size <= size); + Ok(read_size) +} + +fn read_stgs(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { + validate!(size >= 8); + validate!(track.ver_m1); + let start_pos = br.tell(); + let version = br.read_byte()?; + validate!(version == 0); + let _flags = br.read_u24be()?; + let entries = br.read_u32be()? as usize; + validate!(entries as u64 <= (size - 8) / 8); + if entries == 0 { + } else if entries == 1 { + let _count = br.read_u32be()?; + let tb_num = br.read_u32be()?; + validate!(tb_num != 0); + track.rescale(tb_num); + } else { + track.pkt_demux.time_to_sample.clear(); + track.pkt_demux.time_to_sample.reserve(entries); + for _ in 0..entries { + let count = br.read_u32be()?; + let mult = br.read_u32be()?; + track.pkt_demux.time_to_sample.push((count, mult)); + } + } + let read_size = br.tell() - start_pos; + validate!(read_size <= size); + Ok(read_size) +} + +fn read_stss(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { + let version = br.read_byte()?; + validate!(version == 0); + let _flags = br.read_u24be()?; + let entries = br.read_u32be()? as usize; + validate!(entries < ((u32::MAX >> 2) - 8) as usize); + validate!((entries * 4 + 8) as u64 == size); + track.pkt_demux.keyframes = Vec::with_capacity(entries); + let mut last_sample_no = 0; + for _ in 0..entries { + let sample_no = br.read_u32be()?; + validate!(sample_no > last_sample_no); + track.pkt_demux.keyframes.push(sample_no); + last_sample_no = sample_no; + } + track.stss_found = true; + Ok(size) +} + +fn read_stsc(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { + let version = br.read_byte()?; + validate!(version == 0); + let _flags = br.read_u24be()?; + let entries = br.read_u32be()? as usize; + if track.ver_m1 { + if entries != 1 || size != 24 { + return Err(DemuxerError::NotImplemented); + } + br.read_u32be()?; + let sample_no = br.read_u32be()?; + validate!(sample_no == 1); + let nsamples = br.read_u32be()?; + br.read_u32be()?; // maybe sample descriptor + track.pkt_demux.sample_map.push((sample_no, nsamples)); + return Ok(size); + } + validate!(entries < ((u32::MAX / 12) - 8) as usize); + validate!((entries * 12 + 8) as u64 == size); + track.pkt_demux.sample_map = Vec::with_capacity(entries); + let mut last_sample_no = 0; + for _i in 0..entries { + let sample_no = br.read_u32be()?; + validate!(sample_no > last_sample_no); + let nsamples = br.read_u32be()?; + let _sample_desc = br.read_u32be()?; + track.pkt_demux.sample_map.push((sample_no, nsamples)); + last_sample_no = sample_no; + } + Ok(size) +} + +fn read_stsz(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { + let version = br.read_byte()?; + validate!(version == 0); + let _flags = br.read_u24be()?; + let sample_size = br.read_u32be()?; + if sample_size != 0 { + track.pkt_demux.sample_size = sample_size; + if track.pkt_demux.sample_size != 1 || track.pkt_demux.bsize == 0 { + track.pkt_demux.bsize = sample_size as usize; + } + Ok(8) + } else { + let entries = br.read_u32be()? as usize; + validate!((entries * 4 + 12) as u64 == size); + track.pkt_demux.chunk_sizes = vec![0; entries]; + br.read_u32be_arr(&mut track.pkt_demux.chunk_sizes)?; + Ok(size) + } +} + +fn read_stco(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { + let version = br.read_byte()?; + validate!(version == 0); + let _flags = br.read_u24be()?; + let entries = br.read_u32be()? as usize; + validate!((entries * 4 + 8) as u64 == size); + if entries < 1024 { + track.pkt_demux.chunk_offsets = Vec::with_capacity(entries); + for _i in 0..entries { + let sample_offset = br.read_u32be()?; + track.pkt_demux.chunk_offsets.push(u64::from(sample_offset)); + } + } else { + // reading whole array at once and converting it later is much faster + track.pkt_demux.chunk_offsets = vec![0; entries]; + let mut tmp = vec![0u32; entries]; + unsafe { + let data = tmp.as_mut_ptr(); + let ptr = std::slice::from_raw_parts_mut(data as *mut u8, entries * 4); + br.read_buf(ptr)?; + } + for (dst, &src) in track.pkt_demux.chunk_offsets.iter_mut().zip(tmp.iter()) { + *dst = u64::from(u32::from_be(src)); + } + } + Ok(size) +} + +fn read_co64(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { + let version = br.read_byte()?; + validate!(version == 0); + let _flags = br.read_u24be()?; + let entries = br.read_u32be()? as usize; + validate!(entries > 0 && (entries * 8 + 8) as u64 == size); + track.pkt_demux.chunk_offsets = vec![0; entries]; + br.read_u64be_arr(&mut track.pkt_demux.chunk_offsets)?; + Ok(size) +} + +fn read_ctts(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { + validate!(size >= 8); + let version = br.read_byte()?; + let _flags = br.read_u24be()?; + if version > 1 { + return Err(DemuxerError::NotImplemented); + } + let entries = br.read_u32be()? as usize; + track.pkt_demux.ctts_version = version; + track.pkt_demux.ctts_map.resize(entries); + match version { + 0 | 1 => { + validate!(size == (entries as u64) * 8 + 8); + if entries < 1024 { + for _ in 0..entries { + let samp_count = br.read_u32be()?; + let samp_offset = br.read_u32be()?; + track.pkt_demux.ctts_map.add(samp_count, samp_offset / track.tb_div); + } + } else { + // reading whole array at once and converting it later is much faster + track.pkt_demux.ctts_map.array.resize(entries, RLEPair(0, 0)); + unsafe { + let data = track.pkt_demux.ctts_map.array.as_mut_ptr(); + let ptr = std::slice::from_raw_parts_mut(data as *mut u8, entries * 8); + br.read_buf(ptr)?; + } + for RLEPair(count, offset) in track.pkt_demux.ctts_map.array.iter_mut() { + *count = u32::from_be(*count); + *offset = u32::from_be(*offset) / track.tb_div; + } + } + }, + _ => unreachable!(), + }; + track.pkt_demux.ctts_map.reset(); + + Ok(size) +} + +const TRAF_CHUNK_HANDLERS: &[TrackChunkHandler] = &[ + TrackChunkHandler { ctype: mktag!(b"tfhd"), parse: read_tfhd }, + TrackChunkHandler { ctype: mktag!(b"trun"), parse: read_trun }, + TrackChunkHandler { ctype: mktag!(b"sbgp"), parse: skip_chunk }, + TrackChunkHandler { ctype: mktag!(b"sgpd"), parse: skip_chunk }, + TrackChunkHandler { ctype: mktag!(b"subs"), parse: skip_chunk }, + TrackChunkHandler { ctype: mktag!(b"saiz"), parse: skip_chunk }, + TrackChunkHandler { ctype: mktag!(b"saio"), parse: skip_chunk }, + TrackChunkHandler { ctype: mktag!(b"tfdt"), parse: skip_chunk }, + TrackChunkHandler { ctype: mktag!(b"meta"), parse: skip_chunk }, +]; + +fn read_tfhd(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { + validate!(size >= 8); + let start = br.tell(); + let _version = br.read_byte()?; + let flags = br.read_u24be()?; + let _track_id = br.read_u32be()?; + if (flags & 0x000001) != 0 { + let base_offset = br.read_u64be()?; + track.moof_off = base_offset; + } + if (flags & 0x000002) != 0 { + let _sample_description_index = br.read_u32be()?; + } + if (flags & 0x000008) != 0 { + let default_sample_duration = br.read_u32be()?; + if track.tb_div == 1 { + track.rescale(default_sample_duration); + } + } + if (flags & 0x000010) != 0 { + let _default_sample_size = br.read_u32be()?; + } + if (flags & 0x000020) != 0 { + let _default_sample_flags = br.read_u32be()?; + } + /*if (flags & 0x010000) != 0 { // duration is empty + }*/ + /*if (flags & 0x020000) != 0 { // base offset is moof start + }*/ + Ok(br.tell() - start) +} + +fn read_trun(track: &mut Track, br: &mut dyn ByteIO, size: u64) -> DemuxerResult { + validate!(size >= 8); + let version = br.read_byte()?; + let flags = br.read_u24be()?; + let data_off_present = (flags & 0x000001) != 0; + let first_sample_flags = (flags & 0x000004) != 0; + let sample_duration_present = (flags & 0x000100) != 0; + let sample_size_present = (flags & 0x000200) != 0; + let sample_flags_present = (flags & 0x000400) != 0; + let sample_ct_off_present = (flags & 0x000800) != 0; + + let sample_count = br.read_u32be()? as usize; + + let mut hdr_size = 8; + let mut arr_size = 0; + if data_off_present { + hdr_size += 4; + } + if first_sample_flags { + hdr_size += 4; + } + if sample_duration_present { + arr_size += 4; + } + if sample_size_present { + arr_size += 4; + } + if sample_flags_present { + arr_size += 4; + } + if sample_ct_off_present { + arr_size += 4; + } + validate!(size == hdr_size + arr_size * (sample_count as u64)); + + let mut data_off = if data_off_present { + let off = br.read_u32be()? as i32; + let new_off = (track.moof_off as i64) + i64::from(off); + validate!(new_off > 0); + new_off as u64 + } else { + track.moof_off + }; + if first_sample_flags { + let _flags = br.read_u32be()?; + } + + if sample_size_present { + track.pkt_demux.chunk_sizes.reserve(sample_count); + track.pkt_demux.chunk_offsets.reserve(sample_count); + } + + if sample_ct_off_present { + if track.pkt_demux.ctts_version != version { + track.pkt_demux.ctts_version = version; + } + track.pkt_demux.ctts_map.reserve(sample_count); + } + + if track.pkt_demux.chunk_offsets.len() < (u32::MAX as usize) { + track.pkt_demux.keyframes.push((track.pkt_demux.chunk_offsets.len() + 1) as u32); + } + for _ in 0..sample_count { + if sample_duration_present { + let _duration = br.read_u32be()?; + } + if sample_size_present { + let ssize = br.read_u32be()?; + track.pkt_demux.chunk_sizes.push(ssize); + track.pkt_demux.chunk_offsets.push(data_off); + data_off += u64::from(ssize); + } + if sample_flags_present { + let _flags = br.read_u32be()?; + } + if sample_ct_off_present { + let samp_offset = br.read_u32be()?; + if version == 0 { + track.pkt_demux.ctts_map.add(1, samp_offset / track.tb_div); + } else { + track.pkt_demux.ctts_map.add(1, ((samp_offset as i32) / (track.tb_div as i32)) as u32); + } + } + } + track.pkt_demux.ctts_map.reset(); + + Ok(size) +} + +pub struct Track { + pub ver_m1: bool, + pub track_id: u32, + pub track_str_id: usize, + pub track_no: u32, + pub tb_num: u32, + pub tb_den: u32, + pub tb_div: u32, + pub depth: u8, + pub tkhd_found: bool, + pub stsd_found: bool, + pub stss_found: bool, + pub stream_type: StreamType, + pub width: usize, + pub height: usize, + pub stream: Option, + pub pal: Option>, + pub pkt_demux: QTPacketDemuxer, + + pub moof_off: u64, + + pub print_chunks: bool, +} + +impl Track { + pub fn new(track_no: u32, tb_den: u32) -> Self { + Self { + tkhd_found: false, + stsd_found: false, + stss_found: false, + track_id: 0, + track_str_id: 0, + track_no, + tb_num: 1, + tb_den, + tb_div: 1, + stream_type: StreamType::None, + width: 0, + height: 0, + stream: None, + depth: 0, + pal: None, + ver_m1: false, + pkt_demux: QTPacketDemuxer::new(), + + moof_off: 0, + + print_chunks: false, + } + } + read_chunk_list!(track; "trak", read_trak, TRAK_CHUNK_HANDLERS); + read_chunk_list!(track; "mdia", read_mdia, MDIA_CHUNK_HANDLERS); + read_chunk_list!(track; "minf", read_minf, MINF_CHUNK_HANDLERS); + read_chunk_list!(track; "stbl", read_stbl, STBL_CHUNK_HANDLERS); + read_chunk_list!(track; "traf", read_traf, TRAF_CHUNK_HANDLERS); + pub fn call_read_trak(&mut self, br: &mut dyn ByteIO, size: u64) -> DemuxerResult<()> { + self.read_trak(br, size) + } + pub fn call_read_traf(&mut self, br: &mut dyn ByteIO, size: u64) -> DemuxerResult<()> { + self.read_traf(br, size) + } + fn rescale(&mut self, tb_num: u32) { + self.tb_div = tb_num; + if let Some(ref mut stream) = self.stream { + let tb_den = stream.tb_den; + let (tb_num, tb_den) = reduce_timebase(tb_num * stream.tb_num, tb_den); + stream.duration /= u64::from(self.tb_div); + stream.tb_num = tb_num; + stream.tb_den = tb_den; + self.tb_num = tb_num; + self.tb_den = tb_den; + self.pkt_demux.tb_num = tb_num; + self.pkt_demux.tb_den = tb_den; + self.pkt_demux.duration /= self.tb_div; + } + } +} + +#[allow(clippy::too_many_arguments)] +pub fn process_packet(src: &mut dyn ByteIO, strmgr: &StreamManager, track: &mut Track, ts: NATimeInfo, offset: u64, size: usize, first: bool, is_kf: bool) -> DemuxerResult { + if let Some(cpts) = ts.get_pts() { + let cts = NATimeInfo::rescale_ts(cpts, ts.tb_num, ts.tb_den, 1, 1000); + track.pkt_demux.cur_ts = Some(cts); + } else { + track.pkt_demux.cur_ts = None; + } + let stream = strmgr.get_stream(track.track_str_id); + if stream.is_none() { return Err(DemuxerError::InvalidData); } + let stream = stream.unwrap(); + src.seek(SeekFrom::Start(offset))?; + let mut pkt = src.read_packet(stream, ts, first | is_kf, size)?; + if let Some(ref pal) = track.pal { + let side_data = NASideData::Palette(first, pal.clone()); + pkt.add_side_data(side_data); + } + Ok(pkt) +} -- 2.39.5