From 045677e56e4260d14b2a2ed81e916bd67fd322d4 Mon Sep 17 00:00:00 2001 From: Andrey Tkachenko Date: Fri, 22 Sep 2023 14:25:53 +0400 Subject: [PATCH 1/4] refactoring: split off Mp4Header from Mp4Reader --- examples/mp4dump.rs | 10 ++-- examples/mp4info.rs | 4 +- src/lib.rs | 6 +- src/reader.rs | 137 ++++++++++++++++++++++++++++++++++---------- 4 files changed, 118 insertions(+), 39 deletions(-) diff --git a/examples/mp4dump.rs b/examples/mp4dump.rs index 6a97d9a..bfbc800 100644 --- a/examples/mp4dump.rs +++ b/examples/mp4dump.rs @@ -46,12 +46,12 @@ fn get_boxes(file: File) -> Result> { // collect known boxes let mut boxes = vec![ - build_box(&mp4.ftyp), - build_box(&mp4.moov), - build_box(&mp4.moov.mvhd), + build_box(&mp4.header.ftyp), + build_box(&mp4.header.moov), + build_box(&mp4.header.moov.mvhd), ]; - if let Some(ref mvex) = &mp4.moov.mvex { + if let Some(ref mvex) = &mp4.header.moov.mvex { boxes.push(build_box(mvex)); if let Some(mehd) = &mvex.mehd { boxes.push(build_box(mehd)); @@ -117,7 +117,7 @@ fn get_boxes(file: File) -> Result> { } // If fragmented, add moof boxes. - for moof in mp4.moofs.iter() { + for moof in mp4.header.moofs.iter() { boxes.push(build_box(moof)); boxes.push(build_box(&moof.mfhd)); for traf in moof.trafs.iter() { diff --git a/examples/mp4info.rs b/examples/mp4info.rs index 00de8ce..84a8366 100644 --- a/examples/mp4info.rs +++ b/examples/mp4info.rs @@ -37,10 +37,10 @@ fn info>(filename: &P) -> Result<()> { println!(" compatible_brands: {}\n", compatible_brands); println!("Movie:"); - println!(" version: {}", mp4.moov.mvhd.version); + println!(" version: {}", mp4.header.moov.mvhd.version); println!( " creation time: {}", - creation_time(mp4.moov.mvhd.creation_time) + creation_time(mp4.header.moov.mvhd.creation_time) ); println!(" duration: {:?}", mp4.duration()); println!(" fragments: {:?}", mp4.is_fragmented()); diff --git a/src/lib.rs b/src/lib.rs index 92319e1..00c08d0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,8 +22,8 @@ //! let mp4 = mp4::Mp4Reader::read_header(reader, size)?; //! //! // Print boxes. -//! println!("major brand: {}", mp4.ftyp.major_brand); -//! println!("timescale: {}", mp4.moov.mvhd.timescale); +//! println!("major brand: {}", mp4.header.ftyp.major_brand); +//! println!("timescale: {}", mp4.header.moov.mvhd.timescale); //! //! // Use available methods. //! println!("size: {}", mp4.size()); @@ -83,7 +83,7 @@ mod track; pub use track::{Mp4Track, TrackConfig}; mod reader; -pub use reader::Mp4Reader; +pub use reader::{Mp4Header, Mp4Reader}; mod writer; pub use writer::{Mp4Config, Mp4Writer}; diff --git a/src/reader.rs b/src/reader.rs index e5ac296..7c2674a 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -6,8 +6,7 @@ use crate::meta::MetaBox; use crate::*; #[derive(Debug)] -pub struct Mp4Reader { - reader: R, +pub struct Mp4Header { pub ftyp: FtypBox, pub moov: MoovBox, pub moofs: Vec, @@ -17,8 +16,8 @@ pub struct Mp4Reader { size: u64, } -impl Mp4Reader { - pub fn read_header(mut reader: R, size: u64) -> Result { +impl Mp4Header { + pub fn read(reader: &mut R, size: u64) -> Result { let start = reader.stream_position()?; let mut ftyp = None; @@ -30,7 +29,7 @@ impl Mp4Reader { let mut current = start; while current < size { // Get box header. - let header = BoxHeader::read(&mut reader)?; + let header = BoxHeader::read(reader)?; let BoxHeader { name, size: s } = header; if s > size { return Err(Error::InvalidData( @@ -46,30 +45,30 @@ impl Mp4Reader { // Match and parse the atom boxes. match name { BoxType::FtypBox => { - ftyp = Some(FtypBox::read_box(&mut reader, s)?); + ftyp = Some(FtypBox::read_box(reader, s)?); } BoxType::FreeBox => { - skip_box(&mut reader, s)?; + skip_box(reader, s)?; } BoxType::MdatBox => { - skip_box(&mut reader, s)?; + skip_box(reader, s)?; } BoxType::MoovBox => { - moov = Some(MoovBox::read_box(&mut reader, s)?); + moov = Some(MoovBox::read_box(reader, s)?); } BoxType::MoofBox => { let moof_offset = reader.stream_position()? - 8; - let moof = MoofBox::read_box(&mut reader, s)?; + let moof = MoofBox::read_box(reader, s)?; moofs.push(moof); moof_offsets.push(moof_offset); } BoxType::EmsgBox => { - let emsg = EmsgBox::read_box(&mut reader, s)?; + let emsg = EmsgBox::read_box(reader, s)?; emsgs.push(emsg); } _ => { // XXX warn!() - skip_box(&mut reader, s)?; + skip_box(reader, s)?; } } current = reader.stream_position()?; @@ -118,8 +117,7 @@ impl Mp4Reader { } } - Ok(Mp4Reader { - reader, + Ok(Mp4Header { ftyp: ftyp.unwrap(), moov: moov.unwrap(), moofs, @@ -129,11 +127,7 @@ impl Mp4Reader { }) } - pub fn read_fragment_header( - &self, - mut reader: FR, - size: u64, - ) -> Result> { + pub fn read_fragment(&self, reader: &mut R, size: u64) -> Result { let start = reader.stream_position()?; let mut moofs = Vec::new(); @@ -142,7 +136,7 @@ impl Mp4Reader { let mut current = start; while current < size { // Get box header. - let header = BoxHeader::read(&mut reader)?; + let header = BoxHeader::read(reader)?; let BoxHeader { name, size: s } = header; if s > size { return Err(Error::InvalidData( @@ -158,17 +152,17 @@ impl Mp4Reader { // Match and parse the atom boxes. match name { BoxType::MdatBox => { - skip_box(&mut reader, s)?; + skip_box(reader, s)?; } BoxType::MoofBox => { let moof_offset = reader.stream_position()? - 8; - let moof = MoofBox::read_box(&mut reader, s)?; + let moof = MoofBox::read_box(reader, s)?; moofs.push(moof); moof_offsets.push(moof_offset); } _ => { // XXX warn!() - skip_box(&mut reader, s)?; + skip_box(reader, s)?; } } current = reader.stream_position()?; @@ -204,8 +198,7 @@ impl Mp4Reader { } } - Ok(Mp4Reader { - reader, + Ok(Mp4Header { ftyp: self.ftyp.clone(), moov: self.moov.clone(), moofs, @@ -215,10 +208,12 @@ impl Mp4Reader { }) } + #[inline] pub fn size(&self) -> u64 { self.size } + #[inline] pub fn major_brand(&self) -> &FourCC { &self.ftyp.major_brand } @@ -255,9 +250,14 @@ impl Mp4Reader { } } - pub fn read_sample(&mut self, track_id: u32, sample_id: u32) -> Result> { + pub fn read_sample( + &mut self, + reader: &mut R, + track_id: u32, + sample_id: u32, + ) -> Result> { if let Some(track) = self.tracks.get(&track_id) { - track.read_sample(&mut self.reader, sample_id) + track.read_sample(reader, sample_id) } else { Err(Error::TrakNotFound(track_id)) } @@ -270,9 +270,7 @@ impl Mp4Reader { Err(Error::TrakNotFound(track_id)) } } -} -impl Mp4Reader { pub fn metadata(&self) -> impl Metadata<'_> { self.moov.udta.as_ref().and_then(|udta| { udta.meta.as_ref().and_then(|meta| match meta { @@ -282,3 +280,84 @@ impl Mp4Reader { }) } } + +#[derive(Debug)] +pub struct Mp4Reader { + reader: R, + pub header: Mp4Header, +} + +impl Mp4Reader { + pub fn from_reader(reader: R, header: Mp4Header) -> Self { + Self { reader, header } + } + + pub fn read_header(mut reader: R, size: u64) -> Result { + Ok(Mp4Reader { + header: Mp4Header::read(&mut reader, size)?, + reader, + }) + } + + pub fn read_fragment_header( + &self, + mut reader: FR, + size: u64, + ) -> Result> { + Ok(Mp4Reader { + header: self.header.read_fragment(&mut reader, size)?, + reader, + }) + } + + pub fn size(&self) -> u64 { + self.header.size() + } + + pub fn major_brand(&self) -> &FourCC { + self.header.major_brand() + } + + pub fn minor_version(&self) -> u32 { + self.header.minor_version() + } + + pub fn compatible_brands(&self) -> &[FourCC] { + self.header.compatible_brands() + } + + pub fn duration(&self) -> Duration { + self.header.duration() + } + + pub fn timescale(&self) -> u32 { + self.header.timescale() + } + + pub fn is_fragmented(&self) -> bool { + self.header.is_fragmented() + } + + pub fn tracks(&self) -> &HashMap { + self.header.tracks() + } + + pub fn sample_count(&self, track_id: u32) -> Result { + self.header.sample_count(track_id) + } + + pub fn read_sample(&mut self, track_id: u32, sample_id: u32) -> Result> { + self.header + .read_sample(&mut self.reader, track_id, sample_id) + } + + pub fn sample_offset(&mut self, track_id: u32, sample_id: u32) -> Result { + self.header.sample_offset(track_id, sample_id) + } +} + +impl Mp4Reader { + pub fn metadata(&self) -> impl Metadata<'_> { + self.header.metadata() + } +} From 1996727b3d96ff68c061d9c28548e1fb3eb36cd9 Mon Sep 17 00:00:00 2001 From: Andrey Tkachenko Date: Thu, 19 Oct 2023 15:40:29 +0400 Subject: [PATCH 2/4] Relax error on box size mismatch --- src/reader.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/reader.rs b/src/reader.rs index 7c2674a..b833d89 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -32,9 +32,7 @@ impl Mp4Header { let header = BoxHeader::read(reader)?; let BoxHeader { name, size: s } = header; if s > size { - return Err(Error::InvalidData( - "file contains a box with a larger size than it", - )); + break; } // Break if size zero BoxHeader, which can result in dead-loop. From 4b49afba6b984e048ddfb3f09e8f442dc95aad66 Mon Sep 17 00:00:00 2001 From: Andrey Tkachenko Date: Mon, 20 Nov 2023 20:03:59 +0400 Subject: [PATCH 3/4] Fix fragmented start_time and is_sync wrong calculation --- src/track.rs | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/src/track.rs b/src/track.rs index 7eada83..fae2c0c 100644 --- a/src/track.rs +++ b/src/track.rs @@ -501,16 +501,14 @@ impl Mp4Track { fn sample_time(&self, sample_id: u32) -> Result<(u64, u32)> { if !self.trafs.is_empty() { - let mut base_start_time = 0; - let mut default_sample_duration = self.default_sample_duration; if let Some((traf_idx, sample_idx)) = self.find_traf_idx_and_sample_idx(sample_id) { let traf = &self.trafs[traf_idx]; - if let Some(tfdt) = &traf.tfdt { - base_start_time = tfdt.base_media_decode_time; - } - if let Some(duration) = traf.tfhd.default_sample_duration { - default_sample_duration = duration; - } + let base_start_time = traf + .tfdt + .as_ref() + .map(|tfdt| tfdt.base_media_decode_time) + .unwrap_or(0); + if let Some(trun) = &traf.trun { if TrunBox::FLAG_SAMPLE_DURATION & trun.flags != 0 { let mut start_offset = 0u64; @@ -523,9 +521,21 @@ impl Mp4Track { return Ok((base_start_time + start_offset, duration)); } } + + let default_sample_duration = traf + .tfhd + .default_sample_duration + .unwrap_or(self.default_sample_duration); + + let start_offset = sample_idx as u64 * default_sample_duration as u64; + + Ok((base_start_time + start_offset, default_sample_duration)) + } else { + Ok(( + ((sample_id - 1) * self.default_sample_duration) as u64, + self.default_sample_duration, + )) } - let start_offset = ((sample_id - 1) * default_sample_duration) as u64; - Ok((base_start_time + start_offset, default_sample_duration)) } else { let stts = &self.trak.mdia.minf.stbl.stts; @@ -579,8 +589,11 @@ impl Mp4Track { fn is_sync_sample(&self, sample_id: u32) -> bool { if !self.trafs.is_empty() { - let sample_sizes_count = self.sample_count() / self.trafs.len() as u32; - return sample_id == 1 || sample_id % sample_sizes_count == 0; + if let Some((_, sample_idx)) = self.find_traf_idx_and_sample_idx(sample_id) { + return sample_idx == 0; + } + + return sample_id == 1; } if let Some(ref stss) = self.trak.mdia.minf.stbl.stss { From deb6d8f0c3137f8b21fbd7c180e1e844ba4832a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=A2=D0=BA=D0=B0?= =?UTF-8?q?=D1=87=D0=B5=D0=BD=D0=BA=D0=BE?= Date: Fri, 12 Apr 2024 20:56:53 +0400 Subject: [PATCH 4/4] Async Mp4Stream --- Cargo.toml | 10 +- benches/bench_main.rs | 11 +- examples/mp4_to_mpeg2ts.rs | 338 ++++++++++++++++++++ examples/mp4copy.rs | 162 +++++----- examples/mp4dump.rs | 250 +++++++-------- examples/mp4info.rs | 255 +++++++-------- examples/mp4sample.rs | 58 ++-- examples/simple.rs | 13 +- src/async_reader.rs | 308 ++++++++++++++++++ src/file.rs | 348 +++++++++++++++++++++ src/header.rs | 139 +++++++++ src/lib.rs | 23 +- src/mp4box/avc1.rs | 152 ++++----- src/mp4box/co64.rs | 46 ++- src/mp4box/ctts.rs | 50 ++- src/mp4box/data.rs | 51 ++- src/mp4box/dinf.rs | 144 ++------- src/mp4box/edts.rs | 41 +-- src/mp4box/elst.rs | 74 ++--- src/mp4box/emsg.rs | 90 ++---- src/mp4box/ftyp.rs | 41 ++- src/mp4box/hdlr.rs | 67 ++-- src/mp4box/hev1.rs | 161 +++++----- src/mp4box/ilst.rs | 180 ++++------- src/mp4box/mdhd.rs | 60 ++-- src/mp4box/mdia.rs | 70 +---- src/mp4box/mehd.rs | 44 ++- src/mp4box/meta.rs | 142 +++------ src/mp4box/mfhd.rs | 36 +-- src/mp4box/minf.rs | 60 +--- src/mp4box/mod.rs | 625 +++++++++++++++++++++++++++++++++---- src/mp4box/moof.rs | 48 +-- src/mp4box/moov.rs | 92 +++--- src/mp4box/mp4a.rs | 236 +++++++------- src/mp4box/mvex.rs | 52 +-- src/mp4box/mvhd.rs | 92 +++--- src/mp4box/smhd.rs | 37 +-- src/mp4box/stbl.rs | 69 ++-- src/mp4box/stco.rs | 47 ++- src/mp4box/stsc.rs | 50 ++- src/mp4box/stsd.rs | 75 +++-- src/mp4box/stss.rs | 47 ++- src/mp4box/stsz.rs | 56 ++-- src/mp4box/stts.rs | 50 ++- src/mp4box/tfdt.rs | 45 ++- src/mp4box/tfhd.rs | 86 ++--- src/mp4box/tkhd.rs | 96 +++--- src/mp4box/traf.rs | 59 +--- src/mp4box/trak.rs | 233 +++++++++++--- src/mp4box/trex.rs | 43 ++- src/mp4box/trun.rs | 73 +++-- src/mp4box/tx3g.rs | 83 +++-- src/mp4box/udta.rs | 66 ++-- src/mp4box/vmhd.rs | 42 ++- src/mp4box/vp09.rs | 71 ++--- src/mp4box/vpcc.rs | 39 ++- src/stream.rs | 19 ++ src/track.rs | 15 +- tests/lib.rs | 3 +- 59 files changed, 3563 insertions(+), 2410 deletions(-) create mode 100644 examples/mp4_to_mpeg2ts.rs create mode 100644 src/async_reader.rs create mode 100644 src/file.rs create mode 100644 src/header.rs create mode 100644 src/stream.rs diff --git a/Cargo.toml b/Cargo.toml index ec35a0d..484a9f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,9 +19,17 @@ bytes = "1.1.0" num-rational = { version = "0.4.0", features = ["serde"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +tokio = { version = "1.37.0", features = ["io-util"] } +futures = "0.3.30" +const_format = "0.2.32" +pin-project-lite = "0.2.14" +async-stream = "0.3.5" [dev-dependencies] -criterion = "0.3" +anyhow = "1.0" +criterion = "0.5.1" +tokio = { version = "1.37.0", features = ["full"] } +tokio-util = "0.7.10" [[bench]] name = "bench_main" diff --git a/benches/bench_main.rs b/benches/bench_main.rs index 99e1ab5..eccb9b2 100644 --- a/benches/bench_main.rs +++ b/benches/bench_main.rs @@ -1,13 +1,14 @@ use criterion::BenchmarkId; use criterion::{criterion_group, criterion_main, Criterion}; -use std::fs::File; +// use std::fs::File; -fn read_mp4(filename: &str) -> u64 { - let f = File::open(filename).unwrap(); - let m = mp4::read_mp4(f).unwrap(); +fn read_mp4(_filename: &str) -> u64 { + // let f = File::open(filename).unwrap(); + // let m = mp4::read_mp4(f).unwrap(); - m.size() + // m.size() + 0 } fn criterion_benchmark(c: &mut Criterion) { diff --git a/examples/mp4_to_mpeg2ts.rs b/examples/mp4_to_mpeg2ts.rs new file mode 100644 index 0000000..1845258 --- /dev/null +++ b/examples/mp4_to_mpeg2ts.rs @@ -0,0 +1,338 @@ +// use std::convert::TryInto; +// use std::env; +// use std::fs::File; +// use std::io::{self, BufReader}; +// use std::path::Path; + +// use anyhow::bail; +// use bytes::{BufMut, Bytes, BytesMut}; +// use futures::SinkExt; +// use mp4::TrackType; +// use std::io::{Cursor, Write}; +// use tokio_util::codec::Encoder; + +// use bytes::Buf; +// use mpeg2ts::{ +// es::{StreamId, StreamType}, +// pes::PesHeader, +// time::{ClockReference, Timestamp}, +// ts::{ +// payload::{self, Pat, Pmt}, +// AdaptationField, ContinuityCounter, EsInfo, Pid, ProgramAssociation, +// TransportScramblingControl, TsHeader, TsPacket, TsPacketWriter, TsPayload, VersionNumber, +// WriteTsPacket, +// }, +// Error as TsError, +// }; + +// const PMT_PID: u16 = 4096; +// const VIDEO_ES_PID: u16 = 256; +// // const AUDIO_ES_PID: u16 = 258; +// const PES_VIDEO_STREAM_ID: u8 = 224; +// // const PES_AUDIO_STREAM_ID: u8 = 192; + +// #[derive(Default)] +// pub struct TsEncoder { +// video_continuity_counter: ContinuityCounter, +// header_sent: bool, +// timestamp: i64, +// } + +// impl TsEncoder { +// fn write_packet( +// &mut self, +// writer: &mut TsPacketWriter, +// pts: Timestamp, +// dts: Timestamp, +// data: &[u8], +// is_keyframe: bool, +// ) -> Result<(), TsError> { +// let mut header = Self::default_ts_header(VIDEO_ES_PID, self.video_continuity_counter)?; +// let mut buf = Cursor::new(data); +// let packet = { +// let data = payload::Bytes::new(&buf.chunk()[..buf.remaining().min(153)])?; +// buf.advance(data.len()); + +// TsPacket { +// header: header.clone(), +// adaptation_field: is_keyframe.then(|| AdaptationField { +// discontinuity_indicator: false, +// random_access_indicator: true, +// es_priority_indicator: false, +// pcr: Some(ClockReference::from(pts)), +// opcr: None, +// splice_countdown: None, +// transport_private_data: Vec::new(), +// extension: None, +// }), +// payload: Some(TsPayload::Pes(payload::Pes { +// header: PesHeader { +// stream_id: StreamId::new(PES_VIDEO_STREAM_ID), +// priority: false, +// data_alignment_indicator: false, +// copyright: false, +// original_or_copy: false, +// pts: Some(pts), +// dts: if pts == dts { None } else { Some(dts) }, +// escr: None, +// }, +// pes_packet_len: 0, +// data, +// })), +// } +// }; + +// writer.write_ts_packet(&packet)?; +// header.continuity_counter.increment(); + +// while buf.has_remaining() { +// let raw_payload = +// payload::Bytes::new(&buf.chunk()[..buf.remaining().min(payload::Bytes::MAX_SIZE)])?; + +// buf.advance(raw_payload.len()); + +// let packet = TsPacket { +// header: header.clone(), +// adaptation_field: None, +// payload: Some(TsPayload::Raw(raw_payload)), +// }; + +// writer.write_ts_packet(&packet)?; +// header.continuity_counter.increment(); +// } + +// self.video_continuity_counter = header.continuity_counter; +// Ok(()) +// } + +// pub fn new(timestamp: i64) -> TsEncoder { +// Self { +// video_continuity_counter: Default::default(), +// header_sent: false, +// timestamp, +// } +// } +// } + +// struct Frame { +// pub pts: i64, +// pub dts: i64, +// pub body: Bytes, +// pub key: bool, +// } + +// impl<'a> Encoder<&'a Frame> for TsEncoder { +// type Error = anyhow::Error; + +// fn encode(&mut self, frame: &'a Frame, dst: &mut BytesMut) -> Result<(), Self::Error> { +// let mut writer = TsPacketWriter::new(dst.writer()); + +// if !self.header_sent { +// self.header_sent = true; +// self.write_header(&mut writer, StreamType::H264)?; +// } + +// let pts = frame.pts - self.timestamp; +// let dts = frame.dts - self.timestamp; +// let p_ts = Timestamp::new((pts as u64 * 9) / 100 + 1).map_err(TsError::from)?; +// let d_ts = Timestamp::new((dts as u64 * 9) / 100 + 1).map_err(TsError::from)?; + +// self.write_packet(&mut writer, p_ts, d_ts, &frame.body, frame.key)?; + +// Ok(()) +// } +// } + +// impl TsEncoder { +// #[inline] +// fn write_header( +// &mut self, +// writer: &mut W, +// stream_type: StreamType, +// ) -> Result<(), TsError> { +// self.write_packets( +// writer, +// [ +// &Self::default_pat_packet(), +// &Self::default_pmt_packet(stream_type), +// ], +// )?; + +// Ok(()) +// } + +// #[inline] +// fn write_packets<'a, W: WriteTsPacket, P: IntoIterator>( +// &mut self, +// writer: &mut W, +// packets: P, +// ) -> Result<(), TsError> { +// packets +// .into_iter() +// .try_for_each(|pak| writer.write_ts_packet(pak))?; + +// Ok(()) +// } + +// fn default_ts_header( +// pid: u16, +// continuity_counter: ContinuityCounter, +// ) -> Result { +// Ok(TsHeader { +// transport_error_indicator: false, +// transport_priority: false, +// pid: Pid::new(pid)?, +// transport_scrambling_control: TransportScramblingControl::NotScrambled, +// continuity_counter, +// }) +// } + +// fn default_pat_packet() -> TsPacket { +// TsPacket { +// header: Self::default_ts_header(0, Default::default()).unwrap(), +// adaptation_field: None, +// payload: Some(TsPayload::Pat(Pat { +// transport_stream_id: 1, +// version_number: VersionNumber::default(), +// table: vec![ProgramAssociation { +// program_num: 1, +// program_map_pid: Pid::new(PMT_PID).unwrap(), +// }], +// })), +// } +// } + +// fn default_pmt_packet(stream_type: StreamType) -> TsPacket { +// TsPacket { +// header: Self::default_ts_header(PMT_PID, Default::default()).unwrap(), +// adaptation_field: None, +// payload: Some(TsPayload::Pmt(Pmt { +// program_num: 1, +// pcr_pid: Some(Pid::new(VIDEO_ES_PID).unwrap()), +// version_number: VersionNumber::default(), +// program_info: vec![], +// es_info: vec![EsInfo { +// stream_type, +// elementary_pid: Pid::new(VIDEO_ES_PID).unwrap(), +// descriptors: vec![], +// }], +// })), +// } +// } +// } + +// #[tokio::main(flavor = "current_thread")] +// async fn main() { +// let args: Vec = env::args().collect(); + +// if args.len() < 2 { +// println!("Usage: mp4sample "); +// std::process::exit(1); +// } + +// if let Err(err) = samples(&args[1]).await { +// let _ = writeln!(io::stderr(), "{}", err); +// } +// } + +// async fn samples>(filename: &P) -> anyhow::Result<()> { +// let mut ts_name = filename.as_ref().parent().unwrap().to_path_buf(); +// ts_name.push(format!( +// "{}.ts", +// filename.as_ref().file_stem().unwrap().to_str().unwrap() +// )); + +// let f = File::open(filename)?; +// let size = f.metadata()?.len(); +// let reader = BufReader::new(f); +// let ts_file = tokio::fs::File::create(ts_name).await.unwrap(); + +// let mut ts = tokio_util::codec::FramedWrite::new(ts_file, TsEncoder::new(-1_400_000)); +// let mut mp4 = mp4::Mp4Reader::read_header(reader, size)?; + +// if let Some(track_id) = mp4.tracks().iter().find_map(|(k, v)| { +// v.track_type() +// .ok() +// .and_then(|x| matches!(x, TrackType::Video).then_some(*k)) +// }) { +// let sample_count = mp4.sample_count(track_id).unwrap(); +// let mut params = BytesMut::new(); +// let track = mp4.tracks().get(&track_id).unwrap(); +// let timescale = track.timescale(); + +// if let Ok(sps) = track.sequence_parameter_set() { +// params.put_slice(&[0, 0, 0, 1]); +// params.put_slice(sps); +// } + +// if let Ok(pps) = track.picture_parameter_set() { +// params.put_slice(&[0, 0, 0, 1]); +// params.put_slice(pps); +// } + +// for sample_idx in 0..sample_count { +// let sample_id = sample_idx + 1; +// let sample = mp4.read_sample(track_id, sample_id); + +// if let Some(samp) = sample.unwrap() { +// let dts = (samp.start_time as i64 * 1_000_000) / timescale as i64; +// let pts = (samp.start_time as i64 + samp.rendering_offset as i64) * 1_000_000 +// / timescale as i64; + +// let mut bytes = BytesMut::from(samp.bytes.as_ref()); +// convert_h264(&mut bytes).unwrap(); + +// let mut body = BytesMut::with_capacity(bytes.len() + 6); + +// if sample_idx == 0 { +// body.put_slice(¶ms); +// } + +// body.put_slice(&[0, 0, 0, 1, 9, 240]); +// body.put_slice(&bytes); + +// ts.send(&Frame { +// pts, +// dts, +// body: body.freeze(), +// key: samp.is_sync, +// }) +// .await?; +// } +// } +// } +// Ok(()) +// } + +// fn convert_h264(data: &mut [u8]) -> anyhow::Result<()> { +// // TODO: +// // * For each IDR frame, copy the SPS and PPS from the stream's +// // parameters, rather than depend on it being present in the frame +// // already. In-band parameters aren't guaranteed. This is awkward +// // with h264_reader v0.5's h264_reader::avcc::AvcDecoderRecord because it +// // strips off the NAL header byte from each parameter. The next major +// // version shouldn't do this. +// // * Copy only the slice data. In particular, don't copy SEI, which confuses +// // Safari: + +// let mut i = 0; +// while i < data.len() - 3 { +// // Replace each NAL's length with the Annex B start code b"\x00\x00\x00\x01". +// let bytes = &mut data[i..i + 4]; +// let nalu_length = u32::from_be_bytes(bytes.try_into().unwrap()) as usize; +// bytes.copy_from_slice(&[0, 0, 0, 1]); + +// i += 4 + nalu_length; + +// if i > data.len() { +// bail!("partial nal body"); +// } +// } + +// if i < data.len() { +// bail!("partial nal body"); +// } + +// Ok(()) +// } +fn main() {} diff --git a/examples/mp4copy.rs b/examples/mp4copy.rs index 98d1ba8..9850cd4 100644 --- a/examples/mp4copy.rs +++ b/examples/mp4copy.rs @@ -1,93 +1,95 @@ -use std::env; -use std::fs::File; -use std::io::prelude::*; -use std::io::{self, BufReader, BufWriter}; -use std::path::Path; +// use std::env; +// use std::fs::File; +// use std::io::prelude::*; +// use std::io::{self, BufReader, BufWriter}; +// use std::path::Path; -use mp4::{ - AacConfig, AvcConfig, HevcConfig, MediaConfig, MediaType, Mp4Config, Result, TrackConfig, - TtxtConfig, Vp9Config, -}; +// use mp4::{ +// AacConfig, AvcConfig, HevcConfig, MediaConfig, MediaType, Mp4Config, Result, TrackConfig, +// TtxtConfig, Vp9Config, +// }; -fn main() { - let args: Vec = env::args().collect(); +// fn main() { +// let args: Vec = env::args().collect(); - if args.len() < 3 { - println!("Usage: mp4copy "); - std::process::exit(1); - } +// if args.len() < 3 { +// println!("Usage: mp4copy "); +// std::process::exit(1); +// } - if let Err(err) = copy(&args[1], &args[2]) { - let _ = writeln!(io::stderr(), "{}", err); - } -} +// if let Err(err) = copy(&args[1], &args[2]) { +// let _ = writeln!(io::stderr(), "{}", err); +// } +// } -fn copy>(src_filename: &P, dst_filename: &P) -> Result<()> { - let src_file = File::open(src_filename)?; - let size = src_file.metadata()?.len(); - let reader = BufReader::new(src_file); +// fn copy>(src_filename: &P, dst_filename: &P) -> Result<()> { +// let src_file = File::open(src_filename)?; +// let size = src_file.metadata()?.len(); +// let reader = BufReader::new(src_file); - let dst_file = File::create(dst_filename)?; - let writer = BufWriter::new(dst_file); +// let dst_file = File::create(dst_filename)?; +// let writer = BufWriter::new(dst_file); - let mut mp4_reader = mp4::Mp4Reader::read_header(reader, size)?; - let mut mp4_writer = mp4::Mp4Writer::write_start( - writer, - &Mp4Config { - major_brand: *mp4_reader.major_brand(), - minor_version: mp4_reader.minor_version(), - compatible_brands: mp4_reader.compatible_brands().to_vec(), - timescale: mp4_reader.timescale(), - }, - )?; +// let mut mp4_reader = mp4::Mp4Reader::read_header(reader, size)?; +// let mut mp4_writer = mp4::Mp4Writer::write_start( +// writer, +// &Mp4Config { +// major_brand: *mp4_reader.major_brand(), +// minor_version: mp4_reader.minor_version(), +// compatible_brands: mp4_reader.compatible_brands().to_vec(), +// timescale: mp4_reader.timescale(), +// }, +// )?; - // TODO interleaving - for track in mp4_reader.tracks().values() { - let media_conf = match track.media_type()? { - MediaType::H264 => MediaConfig::AvcConfig(AvcConfig { - width: track.width(), - height: track.height(), - seq_param_set: track.sequence_parameter_set()?.to_vec(), - pic_param_set: track.picture_parameter_set()?.to_vec(), - }), - MediaType::H265 => MediaConfig::HevcConfig(HevcConfig { - width: track.width(), - height: track.height(), - }), - MediaType::VP9 => MediaConfig::Vp9Config(Vp9Config { - width: track.width(), - height: track.height(), - }), - MediaType::AAC => MediaConfig::AacConfig(AacConfig { - bitrate: track.bitrate(), - profile: track.audio_profile()?, - freq_index: track.sample_freq_index()?, - chan_conf: track.channel_config()?, - }), - MediaType::TTXT => MediaConfig::TtxtConfig(TtxtConfig {}), - }; +// // TODO interleaving +// for track in mp4_reader.tracks().values() { +// let media_conf = match track.media_type()? { +// MediaType::H264 => MediaConfig::AvcConfig(AvcConfig { +// width: track.width(), +// height: track.height(), +// seq_param_set: track.sequence_parameter_set()?.to_vec(), +// pic_param_set: track.picture_parameter_set()?.to_vec(), +// }), +// MediaType::H265 => MediaConfig::HevcConfig(HevcConfig { +// width: track.width(), +// height: track.height(), +// }), +// MediaType::VP9 => MediaConfig::Vp9Config(Vp9Config { +// width: track.width(), +// height: track.height(), +// }), +// MediaType::AAC => MediaConfig::AacConfig(AacConfig { +// bitrate: track.bitrate(), +// profile: track.audio_profile()?, +// freq_index: track.sample_freq_index()?, +// chan_conf: track.channel_config()?, +// }), +// MediaType::TTXT => MediaConfig::TtxtConfig(TtxtConfig {}), +// }; - let track_conf = TrackConfig { - track_type: track.track_type()?, - timescale: track.timescale(), - language: track.language().to_string(), - media_conf, - }; +// let track_conf = TrackConfig { +// track_type: track.track_type()?, +// timescale: track.timescale(), +// language: track.language().to_string(), +// media_conf, +// }; - mp4_writer.add_track(&track_conf)?; - } +// mp4_writer.add_track(&track_conf)?; +// } - for track_id in mp4_reader.tracks().keys().copied().collect::>() { - let sample_count = mp4_reader.sample_count(track_id)?; - for sample_idx in 0..sample_count { - let sample_id = sample_idx + 1; - let sample = mp4_reader.read_sample(track_id, sample_id)?.unwrap(); - mp4_writer.write_sample(track_id, &sample)?; - // println!("copy {}:({})", sample_id, sample); - } - } +// for track_id in mp4_reader.tracks().keys().copied().collect::>() { +// let sample_count = mp4_reader.sample_count(track_id)?; +// for sample_idx in 0..sample_count { +// let sample_id = sample_idx + 1; +// let sample = mp4_reader.read_sample(track_id, sample_id)?.unwrap(); +// mp4_writer.write_sample(track_id, &sample)?; +// // println!("copy {}:({})", sample_id, sample); +// } +// } - mp4_writer.write_end()?; +// mp4_writer.write_end()?; - Ok(()) -} +// Ok(()) +// } + +fn main() {} diff --git a/examples/mp4dump.rs b/examples/mp4dump.rs index bfbc800..f8de8f0 100644 --- a/examples/mp4dump.rs +++ b/examples/mp4dump.rs @@ -1,142 +1,144 @@ -use std::env; -use std::fs::File; -use std::io::prelude::*; -use std::io::{self, BufReader}; -use std::path::Path; +// use std::env; +// use std::fs::File; +// use std::io::prelude::*; +// use std::io::{self, BufReader}; +// use std::path::Path; -use mp4::{Mp4Box, Result}; +// use mp4::{Mp4Box, Result}; -fn main() { - let args: Vec = env::args().collect(); +// fn main() { +// let args: Vec = env::args().collect(); - if args.len() < 2 { - println!("Usage: mp4dump "); - std::process::exit(1); - } +// if args.len() < 2 { +// println!("Usage: mp4dump "); +// std::process::exit(1); +// } - if let Err(err) = dump(&args[1]) { - let _ = writeln!(io::stderr(), "{}", err); - } -} +// if let Err(err) = dump(&args[1]) { +// let _ = writeln!(io::stderr(), "{}", err); +// } +// } -fn dump>(filename: &P) -> Result<()> { - let f = File::open(filename)?; - let boxes = get_boxes(f)?; +// fn dump>(filename: &P) -> Result<()> { +// let f = File::open(filename)?; +// let boxes = get_boxes(f)?; - // print out boxes - for b in boxes.iter() { - println!("[{}] size={} {}", b.name, b.size, b.summary); - } +// // print out boxes +// for b in boxes.iter() { +// println!("[{}] size={} {}", b.name, b.size, b.summary); +// } - Ok(()) -} +// Ok(()) +// } -#[derive(Debug, Clone, PartialEq, Default)] -pub struct Box { - name: String, - size: u64, - summary: String, - indent: u32, -} +// #[derive(Debug, Clone, PartialEq, Default)] +// pub struct Box { +// name: String, +// size: u64, +// summary: String, +// indent: u32, +// } -fn get_boxes(file: File) -> Result> { - let size = file.metadata()?.len(); - let reader = BufReader::new(file); - let mp4 = mp4::Mp4Reader::read_header(reader, size)?; +// fn get_boxes(file: File) -> Result> { +// let size = file.metadata()?.len(); +// let reader = BufReader::new(file); +// let mp4 = mp4::Mp4Reader::read_header(reader, size)?; - // collect known boxes - let mut boxes = vec![ - build_box(&mp4.header.ftyp), - build_box(&mp4.header.moov), - build_box(&mp4.header.moov.mvhd), - ]; +// // collect known boxes +// let mut boxes = vec![ +// build_box(&mp4.header.ftyp), +// build_box(&mp4.header.moov), +// build_box(&mp4.header.moov.mvhd), +// ]; - if let Some(ref mvex) = &mp4.header.moov.mvex { - boxes.push(build_box(mvex)); - if let Some(mehd) = &mvex.mehd { - boxes.push(build_box(mehd)); - } - boxes.push(build_box(&mvex.trex)); - } +// if let Some(ref mvex) = &mp4.header.moov.mvex { +// boxes.push(build_box(mvex)); +// if let Some(mehd) = &mvex.mehd { +// boxes.push(build_box(mehd)); +// } +// boxes.push(build_box(&mvex.trex)); +// } - // trak. - for track in mp4.tracks().values() { - boxes.push(build_box(&track.trak)); - boxes.push(build_box(&track.trak.tkhd)); - if let Some(ref edts) = track.trak.edts { - boxes.push(build_box(edts)); - if let Some(ref elst) = edts.elst { - boxes.push(build_box(elst)); - } - } +// // trak. +// for track in mp4.tracks().values() { +// boxes.push(build_box(&track.trak)); +// boxes.push(build_box(&track.trak.tkhd)); +// if let Some(ref edts) = track.trak.edts { +// boxes.push(build_box(edts)); +// if let Some(ref elst) = edts.elst { +// boxes.push(build_box(elst)); +// } +// } - // trak.mdia - let mdia = &track.trak.mdia; - boxes.push(build_box(mdia)); - boxes.push(build_box(&mdia.mdhd)); - boxes.push(build_box(&mdia.hdlr)); - boxes.push(build_box(&track.trak.mdia.minf)); +// // trak.mdia +// let mdia = &track.trak.mdia; +// boxes.push(build_box(mdia)); +// boxes.push(build_box(&mdia.mdhd)); +// boxes.push(build_box(&mdia.hdlr)); +// boxes.push(build_box(&track.trak.mdia.minf)); - // trak.mdia.minf - let minf = &track.trak.mdia.minf; - if let Some(ref vmhd) = &minf.vmhd { - boxes.push(build_box(vmhd)); - } - if let Some(ref smhd) = &minf.smhd { - boxes.push(build_box(smhd)); - } +// // trak.mdia.minf +// let minf = &track.trak.mdia.minf; +// if let Some(ref vmhd) = &minf.vmhd { +// boxes.push(build_box(vmhd)); +// } +// if let Some(ref smhd) = &minf.smhd { +// boxes.push(build_box(smhd)); +// } - // trak.mdia.minf.stbl - let stbl = &track.trak.mdia.minf.stbl; - boxes.push(build_box(stbl)); - boxes.push(build_box(&stbl.stsd)); - if let Some(ref avc1) = &stbl.stsd.avc1 { - boxes.push(build_box(avc1)); - } - if let Some(ref hev1) = &stbl.stsd.hev1 { - boxes.push(build_box(hev1)); - } - if let Some(ref mp4a) = &stbl.stsd.mp4a { - boxes.push(build_box(mp4a)); - } - boxes.push(build_box(&stbl.stts)); - if let Some(ref ctts) = &stbl.ctts { - boxes.push(build_box(ctts)); - } - if let Some(ref stss) = &stbl.stss { - boxes.push(build_box(stss)); - } - boxes.push(build_box(&stbl.stsc)); - boxes.push(build_box(&stbl.stsz)); - if let Some(ref stco) = &stbl.stco { - boxes.push(build_box(stco)); - } - if let Some(ref co64) = &stbl.co64 { - boxes.push(build_box(co64)); - } - } +// // trak.mdia.minf.stbl +// let stbl = &track.trak.mdia.minf.stbl; +// boxes.push(build_box(stbl)); +// boxes.push(build_box(&stbl.stsd)); +// if let Some(ref avc1) = &stbl.stsd.avc1 { +// boxes.push(build_box(avc1)); +// } +// if let Some(ref hev1) = &stbl.stsd.hev1 { +// boxes.push(build_box(hev1)); +// } +// if let Some(ref mp4a) = &stbl.stsd.mp4a { +// boxes.push(build_box(mp4a)); +// } +// boxes.push(build_box(&stbl.stts)); +// if let Some(ref ctts) = &stbl.ctts { +// boxes.push(build_box(ctts)); +// } +// if let Some(ref stss) = &stbl.stss { +// boxes.push(build_box(stss)); +// } +// boxes.push(build_box(&stbl.stsc)); +// boxes.push(build_box(&stbl.stsz)); +// if let Some(ref stco) = &stbl.stco { +// boxes.push(build_box(stco)); +// } +// if let Some(ref co64) = &stbl.co64 { +// boxes.push(build_box(co64)); +// } +// } - // If fragmented, add moof boxes. - for moof in mp4.header.moofs.iter() { - boxes.push(build_box(moof)); - boxes.push(build_box(&moof.mfhd)); - for traf in moof.trafs.iter() { - boxes.push(build_box(traf)); - boxes.push(build_box(&traf.tfhd)); - if let Some(ref trun) = &traf.trun { - boxes.push(build_box(trun)); - } - } - } +// // If fragmented, add moof boxes. +// for moof in mp4.header.moofs.iter() { +// boxes.push(build_box(moof)); +// boxes.push(build_box(&moof.mfhd)); +// for traf in moof.trafs.iter() { +// boxes.push(build_box(traf)); +// boxes.push(build_box(&traf.tfhd)); +// if let Some(ref trun) = &traf.trun { +// boxes.push(build_box(trun)); +// } +// } +// } - Ok(boxes) -} +// Ok(boxes) +// } -fn build_box(m: &M) -> Box { - Box { - name: m.box_type().to_string(), - size: m.box_size(), - summary: m.summary().unwrap(), - indent: 0, - } -} +// fn build_box(m: &M) -> Box { +// Box { +// name: m.box_type().to_string(), +// size: m.box_size(), +// summary: m.summary().unwrap(), +// indent: 0, +// } +// } + +fn main() {} diff --git a/examples/mp4info.rs b/examples/mp4info.rs index 84a8366..e6b9a7b 100644 --- a/examples/mp4info.rs +++ b/examples/mp4info.rs @@ -1,143 +1,144 @@ -use std::env; -use std::fs::File; -use std::io::prelude::*; -use std::io::{self, BufReader}; -use std::path::Path; +// use std::env; +// use std::fs::File; +// use std::io::prelude::*; +// use std::io::{self, BufReader}; +// use std::path::Path; -use mp4::{Error, Mp4Track, Result, TrackType}; +// use mp4::{Error, Mp4Track, Result, TrackType}; -fn main() { - let args: Vec = env::args().collect(); +// fn main() { +// let args: Vec = env::args().collect(); - if args.len() < 2 { - println!("Usage: mp4info "); - std::process::exit(1); - } +// if args.len() < 2 { +// println!("Usage: mp4info "); +// std::process::exit(1); +// } - if let Err(err) = info(&args[1]) { - let _ = writeln!(io::stderr(), "{}", err); - } -} +// if let Err(err) = info(&args[1]) { +// let _ = writeln!(io::stderr(), "{}", err); +// } +// } -fn info>(filename: &P) -> Result<()> { - let f = File::open(filename)?; - let size = f.metadata()?.len(); - let reader = BufReader::new(f); +// fn info>(filename: &P) -> Result<()> { +// let f = File::open(filename)?; +// let size = f.metadata()?.len(); +// let reader = BufReader::new(f); - let mp4 = mp4::Mp4Reader::read_header(reader, size)?; +// let mp4 = mp4::Mp4Reader::read_header(reader, size)?; - println!("File:"); - println!(" file size: {}", mp4.size()); - println!(" major_brand: {}", mp4.major_brand()); - let mut compatible_brands = String::new(); - for brand in mp4.compatible_brands().iter() { - compatible_brands.push_str(&brand.to_string()); - compatible_brands.push(' '); - } - println!(" compatible_brands: {}\n", compatible_brands); +// println!("File:"); +// println!(" file size: {}", mp4.size()); +// println!(" major_brand: {}", mp4.major_brand()); +// let mut compatible_brands = String::new(); +// for brand in mp4.compatible_brands().iter() { +// compatible_brands.push_str(&brand.to_string()); +// compatible_brands.push(' '); +// } +// println!(" compatible_brands: {}\n", compatible_brands); - println!("Movie:"); - println!(" version: {}", mp4.header.moov.mvhd.version); - println!( - " creation time: {}", - creation_time(mp4.header.moov.mvhd.creation_time) - ); - println!(" duration: {:?}", mp4.duration()); - println!(" fragments: {:?}", mp4.is_fragmented()); - println!(" timescale: {:?}\n", mp4.timescale()); +// println!("Movie:"); +// println!(" version: {}", mp4.header.moov.mvhd.version); +// println!( +// " creation time: {}", +// creation_time(mp4.header.moov.mvhd.creation_time) +// ); +// println!(" duration: {:?}", mp4.duration()); +// println!(" fragments: {:?}", mp4.is_fragmented()); +// println!(" timescale: {:?}\n", mp4.timescale()); - println!("Found {} Tracks", mp4.tracks().len()); - for track in mp4.tracks().values() { - let media_info = match track.track_type()? { - TrackType::Video => video_info(track), - TrackType::Audio => audio_info(track), - TrackType::Subtitle => subtitle_info(track), - }; +// println!("Found {} Tracks", mp4.tracks().len()); +// for track in mp4.tracks().values() { +// let media_info = match track.track_type()? { +// TrackType::Video => video_info(track), +// TrackType::Audio => audio_info(track), +// TrackType::Subtitle => subtitle_info(track), +// }; - println!( - " Track: #{}({}) {}: {}", - track.track_id(), - track.language(), - track.track_type()?, - media_info.unwrap_or_else(|e| e.to_string()) - ); - } - Ok(()) -} +// println!( +// " Track: #{}({}) {}: {}", +// track.track_id(), +// track.language(), +// track.track_type()?, +// media_info.unwrap_or_else(|e| e.to_string()) +// ); +// } +// Ok(()) +// } -fn video_info(track: &Mp4Track) -> Result { - if track.trak.mdia.minf.stbl.stsd.avc1.is_some() { - Ok(format!( - "{} ({}) ({:?}), {}x{}, {} kb/s, {:.2} fps", - track.media_type()?, - track.video_profile()?, - track.box_type()?, - track.width(), - track.height(), - track.bitrate() / 1000, - track.frame_rate() - )) - } else { - Ok(format!( - "{} ({:?}), {}x{}, {} kb/s, {:.2} fps", - track.media_type()?, - track.box_type()?, - track.width(), - track.height(), - track.bitrate() / 1000, - track.frame_rate() - )) - } -} +// fn video_info(track: &Mp4Track) -> Result { +// if track.trak.mdia.minf.stbl.stsd.avc1.is_some() { +// Ok(format!( +// "{} ({}) ({:?}), {}x{}, {} kb/s, {:.2} fps", +// track.media_type()?, +// track.video_profile()?, +// track.box_type()?, +// track.width(), +// track.height(), +// track.bitrate() / 1000, +// track.frame_rate() +// )) +// } else { +// Ok(format!( +// "{} ({:?}), {}x{}, {} kb/s, {:.2} fps", +// track.media_type()?, +// track.box_type()?, +// track.width(), +// track.height(), +// track.bitrate() / 1000, +// track.frame_rate() +// )) +// } +// } -fn audio_info(track: &Mp4Track) -> Result { - if let Some(ref mp4a) = track.trak.mdia.minf.stbl.stsd.mp4a { - if mp4a.esds.is_some() { - let profile = match track.audio_profile() { - Ok(val) => val.to_string(), - _ => "-".to_string(), - }; +// fn audio_info(track: &Mp4Track) -> Result { +// if let Some(ref mp4a) = track.trak.mdia.minf.stbl.stsd.mp4a { +// if mp4a.esds.is_some() { +// let profile = match track.audio_profile() { +// Ok(val) => val.to_string(), +// _ => "-".to_string(), +// }; - let channel_config = match track.channel_config() { - Ok(val) => val.to_string(), - _ => "-".to_string(), - }; +// let channel_config = match track.channel_config() { +// Ok(val) => val.to_string(), +// _ => "-".to_string(), +// }; - Ok(format!( - "{} ({}) ({:?}), {} Hz, {}, {} kb/s", - track.media_type()?, - profile, - track.box_type()?, - track.sample_freq_index()?.freq(), - channel_config, - track.bitrate() / 1000 - )) - } else { - Ok(format!( - "{} ({:?}), {} kb/s", - track.media_type()?, - track.box_type()?, - track.bitrate() / 1000 - )) - } - } else { - Err(Error::InvalidData("mp4a box not found")) - } -} +// Ok(format!( +// "{} ({}) ({:?}), {} Hz, {}, {} kb/s", +// track.media_type()?, +// profile, +// track.box_type()?, +// track.sample_freq_index()?.freq(), +// channel_config, +// track.bitrate() / 1000 +// )) +// } else { +// Ok(format!( +// "{} ({:?}), {} kb/s", +// track.media_type()?, +// track.box_type()?, +// track.bitrate() / 1000 +// )) +// } +// } else { +// Err(Error::InvalidData("mp4a box not found")) +// } +// } -fn subtitle_info(track: &Mp4Track) -> Result { - if track.trak.mdia.minf.stbl.stsd.tx3g.is_some() { - Ok(format!("{} ({:?})", track.media_type()?, track.box_type()?,)) - } else { - Err(Error::InvalidData("tx3g box not found")) - } -} +// fn subtitle_info(track: &Mp4Track) -> Result { +// if track.trak.mdia.minf.stbl.stsd.tx3g.is_some() { +// Ok(format!("{} ({:?})", track.media_type()?, track.box_type()?,)) +// } else { +// Err(Error::InvalidData("tx3g box not found")) +// } +// } -fn creation_time(creation_time: u64) -> u64 { - // convert from MP4 epoch (1904-01-01) to Unix epoch (1970-01-01) - if creation_time >= 2082844800 { - creation_time - 2082844800 - } else { - creation_time - } -} +// fn creation_time(creation_time: u64) -> u64 { +// // convert from MP4 epoch (1904-01-01) to Unix epoch (1970-01-01) +// if creation_time >= 2082844800 { +// creation_time - 2082844800 +// } else { +// creation_time +// } +// } +fn main() {} diff --git a/examples/mp4sample.rs b/examples/mp4sample.rs index 6495daf..9bf403f 100644 --- a/examples/mp4sample.rs +++ b/examples/mp4sample.rs @@ -1,12 +1,14 @@ -use std::env; -use std::fs::File; +use std::fmt::Pointer; use std::io::prelude::*; -use std::io::{self, BufReader}; use std::path::Path; +use std::{env, io}; use mp4::Result; +use tokio::fs::File; +use tokio::io::BufReader; -fn main() { +#[tokio::main] +async fn main() { let args: Vec = env::args().collect(); if args.len() < 2 { @@ -14,37 +16,41 @@ fn main() { std::process::exit(1); } - if let Err(err) = samples(&args[1]) { + if let Err(err) = samples(&args[1]).await { let _ = writeln!(io::stderr(), "{}", err); } } -fn samples>(filename: &P) -> Result<()> { - let f = File::open(filename)?; - let size = f.metadata()?.len(); +async fn samples>(filename: &P) -> Result<()> { + let f = File::open(filename).await?; let reader = BufReader::new(f); - - let mut mp4 = mp4::Mp4Reader::read_header(reader, size)?; + let mp4 = mp4::AsyncMp4Reader::read_header(reader).await?; for track_id in mp4.tracks().keys().copied().collect::>() { - let sample_count = mp4.sample_count(track_id).unwrap(); + let track = mp4.tracks().get(&track_id).unwrap(); - for sample_idx in 0..sample_count { - let sample_id = sample_idx + 1; - let sample = mp4.read_sample(track_id, sample_id); + println!("{:#?}", track.trak.mdia.minf.stbl.stco); - if let Some(ref samp) = sample.unwrap() { - println!( - "[{}] start_time={} duration={} rendering_offset={} size={} is_sync={}", - sample_id, - samp.start_time, - samp.duration, - samp.rendering_offset, - samp.bytes.len(), - samp.is_sync, - ); - } - } + // let sample_count = mp4.sample_count(track_id).unwrap(); + + // println!("sample count {}", sample_count); + + // for sample_idx in 0..sample_count { + // let sample_id = sample_idx + 1; + // let sample = mp4.read_sample(track_id, sample_id); + + // if let Some(ref samp) = sample.unwrap() { + // println!( + // "[{}] start_time={} duration={} rendering_offset={} size={} is_sync={}", + // sample_id, + // samp.start_time, + // samp.duration, + // samp.rendering_offset, + // samp.bytes.len(), + // samp.is_sync, + // ); + // } + // } } Ok(()) } diff --git a/examples/simple.rs b/examples/simple.rs index 0dabedd..adad1c2 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -1,7 +1,9 @@ +use mp4::Mp4Header; use std::env; -use std::fs::File; +use tokio::fs::File; -fn main() { +#[tokio::main] +async fn main() { let args: Vec = env::args().collect(); if args.len() < 2 { @@ -10,10 +12,11 @@ fn main() { } let filename = &args[1]; - let f = File::open(filename).unwrap(); - let mp4 = mp4::read_mp4(f).unwrap(); + let mut f = File::open(filename).await.unwrap(); - println!("Major Brand: {}", mp4.major_brand()); + let mp4 = Mp4Header::read(&mut f, Some(())).await.unwrap(); + + println!("Major Brand: {:?}", mp4.major_brand()); for track in mp4.tracks().values() { println!( diff --git a/src/async_reader.rs b/src/async_reader.rs new file mode 100644 index 0000000..e3f7fe2 --- /dev/null +++ b/src/async_reader.rs @@ -0,0 +1,308 @@ +use std::{ + collections::HashMap, + io::{Read, Seek}, + time::Duration, +}; + +use tokio::io::{AsyncRead, AsyncReadExt}; + +use crate::{ + BlockReader, BoxHeader, BoxType, EmsgBox, Error, FourCC, FtypBox, MetaBox, Metadata, MoofBox, + MoovBox, Mp4Sample, Mp4Track, +}; + +#[derive(Debug, Clone)] +pub struct Mp4Header { + pub ftyp: Option, + pub moov: Option, + pub moofs: Vec, + pub emsgs: Vec, + + tracks: HashMap, +} + +// async fn read + +impl Mp4Header { + pub async fn read(reader: &mut R, _cache: Option) -> Result + where + R: AsyncRead + Unpin, + // C: AsyncRead + AsyncWrite + Unpin, + { + let mut ftyp = None; + let mut moov = None; + let mut moofs = Vec::new(); + // let mut moof_offsets = Vec::new(); + let mut emsgs = Vec::new(); + let mut buff = Vec::with_capacity(1024); + + while let Some(BoxHeader { kind, size: s }) = BoxHeader::read(reader).await? { + if buff.len() < s as usize { + buff.resize(s as usize, 0); + } + + // Match and parse the atom boxes. + match kind { + BoxType::FtypBox => { + reader.read_exact(&mut buff[0..s as usize]).await?; + + ftyp = Some(FtypBox::read_block(&mut &buff[0..s as usize])?); + println!("{:?}", ftyp); + } + + BoxType::MoovBox => { + reader.read_exact(&mut buff[0..s as usize]).await?; + + println!("moov"); + + moov = Some(MoovBox::read_block(&mut &buff[0..s as usize])?); + } + + // BoxType::MoofBox => { + // let moof_offset = reader.stream_position()? - 8; + // let moof = MoofBox::read_box(reader, s)?; + // moofs.push(moof); + // moof_offsets.push(moof_offset); + // } + + // BoxType::EmsgBox => { + // let emsg = EmsgBox::read_box(reader, s)?; + // emsgs.push(emsg); + // } + // BoxType::MdatBox => { + // skip_box(reader, s)?; + // } + + // BoxType::FreeBox => { + // reader.read_exact(buf) + // skip_box(reader, s)?; + // } + bt => { + println!("skip {:?}", bt); + + let mut buff = [0u8; 1024]; + let mut read = 0; + for chunk in (0..s).step_by(1024) { + if chunk == 0 { + continue; + } + + reader.read_exact(&mut buff).await?; + read += buff.len(); + } + + if s as usize - read > 0 { + reader.read_exact(&mut buff[0..s as usize - read]).await?; + } + } + } + } + + if ftyp.is_none() { + return Err(Error::BoxNotFound(BoxType::FtypBox)); + } + + if moov.is_none() { + return Err(Error::BoxNotFound(BoxType::MoovBox)); + } + + let mut tracks = if let Some(ref moov) = moov { + if moov.traks.iter().any(|trak| trak.tkhd.track_id == 0) { + return Err(Error::InvalidData("illegal track id 0")); + } + moov.traks + .iter() + .map(|trak| (trak.tkhd.track_id, Mp4Track::from(trak))) + .collect() + } else { + HashMap::new() + }; + + // Update tracks if any fragmented (moof) boxes are found. + // if !moofs.is_empty() { + // let mut default_sample_duration = 0; + // if let Some(ref moov) = moov { + // if let Some(ref mvex) = &moov.mvex { + // default_sample_duration = mvex.trex.default_sample_duration + // } + // } + + // for (moof, moof_offset) in moofs.iter().zip(moof_offsets) { + // for traf in moof.trafs.iter() { + // let track_id = traf.tfhd.track_id; + // if let Some(track) = tracks.get_mut(&track_id) { + // track.default_sample_duration = default_sample_duration; + // track.moof_offsets.push(moof_offset); + // track.trafs.push(traf.clone()) + // } else { + // return Err(Error::TrakNotFound(track_id)); + // } + // } + // } + // } + + Ok(Mp4Header { + ftyp, + moov, + moofs, + emsgs, + tracks, + }) + } + + #[inline] + pub fn major_brand(&self) -> Option<&FourCC> { + Some(&self.ftyp.as_ref()?.major_brand) + } + + pub fn minor_version(&self) -> Option { + Some(self.ftyp.as_ref()?.minor_version) + } + + pub fn compatible_brands(&self) -> Option<&[FourCC]> { + Some(&self.ftyp.as_ref()?.compatible_brands) + } + + pub fn duration(&self) -> Option { + self.moov.as_ref().map(|moov| { + Duration::from_millis(moov.mvhd.duration * 1000 / moov.mvhd.timescale as u64) + }) + } + + pub fn timescale(&self) -> Option { + Some(self.moov.as_ref()?.mvhd.timescale) + } + + pub fn is_fragmented(&self) -> bool { + !self.moofs.is_empty() + } + + pub fn tracks(&self) -> &HashMap { + &self.tracks + } + + pub fn sample_count(&self, track_id: u32) -> Result { + if let Some(track) = self.tracks.get(&track_id) { + Ok(track.sample_count()) + } else { + Err(Error::TrakNotFound(track_id)) + } + } + + pub fn read_sample( + &mut self, + reader: &mut R, + track_id: u32, + sample_id: u32, + ) -> Result, Error> { + if let Some(track) = self.tracks.get(&track_id) { + track.read_sample(reader, sample_id) + } else { + Err(Error::TrakNotFound(track_id)) + } + } + + pub fn sample_offset(&mut self, track_id: u32, sample_id: u32) -> Result { + if let Some(track) = self.tracks.get(&track_id) { + track.sample_offset(sample_id) + } else { + Err(Error::TrakNotFound(track_id)) + } + } + + pub fn metadata(&self) -> Option> { + self.moov.as_ref()?.udta.as_ref().and_then(|udta| { + udta.meta.as_ref().and_then(|meta| match meta { + MetaBox::Mdir { ilst } => ilst.as_ref(), + _ => None, + }) + }) + } +} + +#[derive(Debug)] +pub struct AsyncMp4Reader { + pub header: Mp4Header, + reader: R, +} + +impl AsyncMp4Reader { + pub fn from_reader(reader: R, header: Mp4Header) -> Self { + Self { reader, header } + } + + pub async fn read_header(mut reader: R) -> Result { + Ok(AsyncMp4Reader { + header: Mp4Header::read(&mut reader, Some(())).await?, + reader, + }) + } + + // pub fn read_fragment_header( + // &self, + // mut reader: FR, + // size: u64, + // ) -> Result> { + // Ok(Mp4Reader { + // header: self.header.read_fragment(&mut reader, size)?, + // reader, + // }) + // } + + // pub fn size(&self) -> u64 { + // self.header.size() + // } + + pub fn major_brand(&self) -> Option<&FourCC> { + self.header.major_brand() + } + + pub fn minor_version(&self) -> Option { + self.header.minor_version() + } + + pub fn compatible_brands(&self) -> Option<&[FourCC]> { + self.header.compatible_brands() + } + + pub fn duration(&self) -> Option { + self.header.duration() + } + + pub fn timescale(&self) -> Option { + self.header.timescale() + } + + pub fn is_fragmented(&self) -> bool { + self.header.is_fragmented() + } + + pub fn tracks(&self) -> &HashMap { + self.header.tracks() + } + + pub fn sample_count(&self, track_id: u32) -> Result { + self.header.sample_count(track_id) + } + + pub fn read_sample( + &mut self, + track_id: u32, + sample_id: u32, + ) -> Result, Error> { + self.header + .read_sample(&mut self.reader, track_id, sample_id) + } + + pub fn sample_offset(&mut self, track_id: u32, sample_id: u32) -> Result { + self.header.sample_offset(track_id, sample_id) + } +} + +pub struct Mp4Track {} + +impl AsyncMp4Reader { + pub fn metadata(&self) -> impl Metadata<'_> { + self.header.metadata() + } +} diff --git a/src/file.rs b/src/file.rs new file mode 100644 index 0000000..81428ea --- /dev/null +++ b/src/file.rs @@ -0,0 +1,348 @@ +use futures::{Future, Stream}; +use std::collections::HashMap; +use std::pin::Pin; +use std::task::{Context, Poll}; +use tokio::io::{AsyncRead, AsyncReadExt, ReadBuf}; + +use crate::{ + BlockReader, BoxHeader, BoxType, EmsgBox, Error, FtypBox, MoofBox, MoovBox, Mp4Sample, +}; + +pub struct MemoryBufferProvider { + buffers: Vec>, +} + +impl BufferProvider for MemoryBufferProvider { + type Error = (); + type Buffer = MemoryBuffer; + + fn create_buffer(&mut self, size: usize) -> Result { + todo!() + } +} + +pub struct MemoryBuffer { + inner: Vec, +} + +impl Buffer for MemoryBuffer { + type Error = std::io::Error; + + async fn write_bytes( + &mut self, + reader: &mut (impl AsyncRead + Unpin), + bytes: usize, + ) -> Result<(), Self::Error> { + self.inner.resize(bytes, 0); + reader.read_exact(&mut self.inner).await?; + Ok(()) + } + + async fn read_bytes(&self, offset: usize, len: usize) -> Result<&[u8], Self::Error> { + Ok(self.inner.get(offset..offset + len).unwrap()) + } +} + +pub trait Buffer { + type Error; + + fn write_bytes( + &mut self, + reader: &mut (impl AsyncRead + Unpin), + bytes: usize, + ) -> impl Future>; + + fn read_bytes( + &self, + offset: usize, + len: usize, + ) -> impl Future>; +} + +pub trait BufferProvider { + type Error; + type Buffer: Buffer; + + fn create_buffer(&mut self, size: usize) -> Result; +} + +struct DataBlock> { + index: usize, + offset: u64, + size: u64, + buffer: B, +} + +pub struct Mp4File<'a, R: AsyncRead + Unpin> { + // provider: P, + // data: Vec>, + ftyp: Option, + emsgs: Vec, + mdat_offset: u64, + mdat_size: u64, + tracks: HashMap, + reader: OffsetWrapper<&'a mut R>, +} + +pin_project_lite::pin_project! { +pub struct OffsetWrapper { + #[pin] + inner: R, + pub offset: u64, +}} + +impl AsyncRead for OffsetWrapper { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut ReadBuf<'_>, + ) -> Poll> { + let mut this = self.project(); + match this.inner.poll_read(cx, buf) { + Poll::Ready(re) => { + *this.offset += buf.filled().len() as u64; + + Poll::Ready(re) + } + Poll::Pending => Poll::Pending, + } + } +} + +impl<'a, R: AsyncRead + Unpin + 'a> Mp4File<'a, R> { + pub fn new(reader: &'a mut R) -> Self { + Self { + ftyp: None, + emsgs: Vec::new(), + mdat_offset: 0, + mdat_size: 0, + tracks: HashMap::new(), + reader: OffsetWrapper { + inner: reader, + offset: 0, + }, + } + } + + pub async fn read_header(&mut self) -> Result { + let mut buff = Vec::with_capacity(8192); + let mut got_moov = false; + + while let Some(BoxHeader { kind, size: s }) = BoxHeader::read(&mut self.reader).await? { + match kind { + BoxType::FtypBox => { + if buff.len() < s as usize { + buff.resize(s as usize, 0); + } + + self.reader.read_exact(&mut buff[0..s as usize]).await?; + self.ftyp = Some(FtypBox::read_block(&mut &buff[0..s as usize])?); + } + + BoxType::MoovBox => { + if buff.len() < s as usize { + buff.resize(s as usize, 0); + } + + self.reader.read_exact(&mut buff[0..s as usize]).await?; + got_moov = true; + self.set_moov(MoovBox::read_block(&mut &buff[0..s as usize])?)?; + } + + BoxType::MoofBox => { + if buff.len() < s as usize { + buff.resize(s as usize, 0); + } + + let offset = self.reader.offset; + self.reader.read_exact(&mut buff[0..s as usize]).await?; + self.add_moof(offset, MoofBox::read_block(&mut &buff[0..s as usize])?)?; + } + + BoxType::EmsgBox => { + if buff.len() < s as usize { + buff.resize(s as usize, 0); + } + + self.reader.read_exact(&mut buff[0..s as usize]).await?; + self.emsgs + .push(EmsgBox::read_block(&mut &buff[0..s as usize])?); + } + + BoxType::MdatBox => { + self.mdat_offset = self.reader.offset; + self.mdat_size = s; + + break; + } + + bt => { + self.skip_box(bt, s).await?; + } + } + } + + Ok(got_moov) + } + + async fn skip_box(&mut self, bt: BoxType, s: u64) -> Result<(), Error> { + println!("skip {:?}", bt); + + let mut buff = [0u8; 1024]; + let mut read = 0; + for chunk in (0..s).step_by(1024) { + if chunk == 0 { + continue; + } + + self.reader.read_exact(&mut buff).await?; + read += buff.len(); + } + + if s as usize - read > 0 { + self.reader + .read_exact(&mut buff[0..s as usize - read]) + .await?; + } + + Ok(()) + } + + fn set_moov(&mut self, moov: MoovBox) -> Result<(), Error> { + for trak in moov.traks { + self.tracks.insert(trak.tkhd.track_id, Mp4Track::new(trak)?); + } + + Ok(()) + } + + fn add_moof(&mut self, offset: u64, moof: MoofBox) -> Result<(), Error> { + for traf in moof.trafs { + let track_id = traf.tfhd.track_id; + + if let Some(track) = self.tracks.get_mut(&track_id) { + track.add_traf(traf) + } else { + return Err(Error::TrakNotFound(track_id)); + } + } + + Ok(()) + } + + // pub fn into_stream(mut self) -> impl Stream> + 'a { + // async_stream::try_stream! { + + // let mut buff = Vec::with_capacity(8192); + // while let Some(BoxHeader { kind, size: s }) = BoxHeader::read(&mut self.reader).await? { + // match kind { + // BoxType::FtypBox => { + // if buff.len() < s as usize { + // buff.resize(s as usize, 0); + // } + + // self.reader.read_exact(&mut buff[0..s as usize]).await?; + // self.ftyp = Some(FtypBox::read_block(&mut &buff[0..s as usize])?); + // } + + // BoxType::MoovBox => { + // if buff.len() < s as usize { + // buff.resize(s as usize, 0); + // } + + // self.reader.read_exact(&mut buff[0..s as usize]).await?; + // self.set_moov(MoovBox::read_block(&mut &buff[0..s as usize])?)?; + // } + + // BoxType::MoofBox => { + // if buff.len() < s as usize { + // buff.resize(s as usize, 0); + // } + + // let offset = self.reader.offset; + // self.reader.read_exact(&mut buff[0..s as usize]).await?; + // self.add_moof(offset, MoofBox::read_block(&mut &buff[0..s as usize])?)?; + // } + + // BoxType::EmsgBox => { + // if buff.len() < s as usize { + // buff.resize(s as usize, 0); + // } + + // self.reader.read_exact(&mut buff[0..s as usize]).await?; + // self.emsgs.push(EmsgBox::read_block(&mut &buff[0..s as usize])?); + // } + + // BoxType::MdatBox => { + + // break; + // } + + // bt => { + // self.skip_box(bt, s).await?; + // } + // } + // } + // } + // } +} + +struct Mp4SampleOffset { + offset: u64, + size: u32, + duration: u32, + start_time: u64, + rendering_offset: i32, + is_sync: bool, +} + +pub struct Mp4Track { + track_id: u32, + duration: u64, + default_sample_duration: u32, + samples: Vec, + tkhd: crate::TkhdBox, +} + +impl Mp4Track { + fn new(trak: crate::TrakBox) -> Result { + let mut samples = Vec::with_capacity(trak.mdia.minf.stbl.stsz.sample_count as _); + + for sample_id in 0..trak.mdia.minf.stbl.stsz.sample_count { + let offset = match trak.sample_offset(sample_id) { + Ok(offset) => offset, + Err(Error::EntryInStblNotFound(_, _, _)) => continue, + Err(err) => return Err(err), + }; + + let size = match trak.sample_size(sample_id) { + Ok(size) => size, + Err(Error::EntryInStblNotFound(_, _, _)) => continue, + Err(err) => return Err(err), + }; + + let (start_time, duration) = trak.sample_time(sample_id)?; + + samples.push(Mp4SampleOffset { + offset, + duration, + size, + start_time, + rendering_offset: trak.sample_rendering_offset(sample_id), + is_sync: trak.sample_is_sync(sample_id), + }); + } + + Ok(Self { + tkhd: trak.tkhd, + track_id: trak.tkhd.track_id, + default_sample_duration: 0, + samples, + duration: 0, + }) + } + + pub(crate) fn add_traf(&self, traf: crate::TrafBox) { + todo!() + } +} diff --git a/src/header.rs b/src/header.rs new file mode 100644 index 0000000..ef545b0 --- /dev/null +++ b/src/header.rs @@ -0,0 +1,139 @@ +use std::collections::HashMap; + +use tokio::io::{AsyncRead, AsyncReadExt}; + +use crate::{BlockReader, BoxHeader, BoxType, EmsgBox, Error, FtypBox, MoofBox, MoovBox, Mp4Track}; + +#[derive(Debug, Clone)] +pub struct Mp4Header { + pub ftyp: Option, + pub moov: Option, + pub moofs: Vec, + pub emsgs: Vec, + pub data: Vec<(u64, u64)>, +} + +impl Mp4Header { + pub async fn read_until_mdat(reader: &mut R) -> Result + where + R: AsyncRead + Unpin, + { + let mut offset = 0; + let mut ftyp = None; + let mut moov = None; + let mut moofs = Vec::new(); + // let mut moof_offsets = Vec::new(); + let mut emsgs = Vec::new(); + let mut buff = Vec::with_capacity(8192); + + while let Some(BoxHeader { kind, size: s }) = BoxHeader::read(reader).await? { + if buff.len() < s as usize { + buff.resize(s as usize, 0); + } + + // Match and parse the atom boxes. + match kind { + BoxType::FtypBox => { + reader.read_exact(&mut buff[0..s as usize]).await?; + ftyp = Some(FtypBox::read_block(&mut &buff[0..s as usize])?); + } + + BoxType::MoovBox => { + reader.read_exact(&mut buff[0..s as usize]).await?; + moov = Some(MoovBox::read_block(&mut &buff[0..s as usize])?); + } + + BoxType::MoofBox => { + let moof_offset = reader.stream_position()? - 8; + let moof = MoofBox::read_box(reader, s)?; + moofs.push(moof); + moof_offsets.push(moof_offset); + } + + BoxType::EmsgBox => { + let emsg = EmsgBox::read_box(reader, s)?; + emsgs.push(emsg); + } + BoxType::MdatBox => {} + + // BoxType::FreeBox => { + // reader.read_exact(buf) + // skip_box(reader, s)?; + // } + bt => { + println!("skip {:?}", bt); + + let mut buff = [0u8; 1024]; + let mut read = 0; + for chunk in (0..s).step_by(1024) { + if chunk == 0 { + continue; + } + + reader.read_exact(&mut buff).await?; + read += buff.len(); + } + + if s as usize - read > 0 { + reader.read_exact(&mut buff[0..s as usize - read]).await?; + } + } + } + } + + if ftyp.is_none() { + return Err(Error::BoxNotFound(BoxType::FtypBox)); + } + + if moov.is_none() { + return Err(Error::BoxNotFound(BoxType::MoovBox)); + } + + let mut tracks = if let Some(ref moov) = moov { + if moov.traks.iter().any(|trak| trak.tkhd.track_id == 0) { + return Err(Error::InvalidData("illegal track id 0")); + } + moov.traks + .iter() + .map(|trak| (trak.tkhd.track_id, Mp4Track::from(trak))) + .collect() + } else { + HashMap::new() + }; + + // Update tracks if any fragmented (moof) boxes are found. + // if !moofs.is_empty() { + // let mut default_sample_duration = 0; + // if let Some(ref moov) = moov { + // if let Some(ref mvex) = &moov.mvex { + // default_sample_duration = mvex.trex.default_sample_duration + // } + // } + + // for (moof, moof_offset) in moofs.iter().zip(moof_offsets) { + // for traf in moof.trafs.iter() { + // let track_id = traf.tfhd.track_id; + // if let Some(track) = tracks.get_mut(&track_id) { + // track.default_sample_duration = default_sample_duration; + // track.moof_offsets.push(moof_offset); + // track.trafs.push(traf.clone()) + // } else { + // return Err(Error::TrakNotFound(track_id)); + // } + // } + // } + // } + + Ok(Mp4Header { + ftyp, + moov, + moofs, + emsgs, + tracks, + }) + } + + pub fn can_be_streamed(&self) -> bool { + self.moov.is_some() + } +} diff --git a/src/lib.rs b/src/lib.rs index 00c08d0..2b6817a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -65,9 +65,6 @@ //! [examples]: https://github.com/alfg/mp4-rust/tree/master/examples #![doc(html_root_url = "https://docs.rs/mp4/*")] -use std::fs::File; -use std::io::BufReader; - mod error; pub use error::Error; @@ -79,18 +76,22 @@ pub use types::*; mod mp4box; pub use mp4box::*; +mod file; +mod header; +mod stream; + mod track; pub use track::{Mp4Track, TrackConfig}; -mod reader; -pub use reader::{Mp4Header, Mp4Reader}; +mod async_reader; +pub use async_reader::{AsyncMp4Reader, Mp4Header}; mod writer; pub use writer::{Mp4Config, Mp4Writer}; -pub fn read_mp4(f: File) -> Result>> { - let size = f.metadata()?.len(); - let reader = BufReader::new(f); - let mp4 = reader::Mp4Reader::read_header(reader, size)?; - Ok(mp4) -} +// pub async fn read_mp4(f: File) -> Result>> { +// let size = f.metadata()?.len(); +// let reader = BufReader::new(f); +// let mp4 = async_reader::Mp4AsyncReader::read_header(reader, size)?; +// Ok(mp4) +// } diff --git a/src/mp4box/avc1.rs b/src/mp4box/avc1.rs index f386f9a..a0227d7 100644 --- a/src/mp4box/avc1.rs +++ b/src/mp4box/avc1.rs @@ -1,6 +1,6 @@ -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{BigEndian, WriteBytesExt}; use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use crate::mp4box::*; @@ -49,19 +49,17 @@ impl Avc1Box { } } - pub fn get_type(&self) -> BoxType { - BoxType::Avc1Box - } - pub fn get_size(&self) -> u64 { HEADER_SIZE + 8 + 70 + self.avcc.box_size() } + + fn box_type(&self) -> BoxType { + BoxType::Avc1Box + } } impl Mp4Box for Avc1Box { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::Avc1Box; fn box_size(&self) -> u64 { self.get_size() @@ -80,59 +78,47 @@ impl Mp4Box for Avc1Box { } } -impl ReadBox<&mut R> for Avc1Box { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; +impl BlockReader for Avc1Box { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + reader.get_u32(); // reserved + reader.get_u16(); // reserved - reader.read_u32::()?; // reserved - reader.read_u16::()?; // reserved - let data_reference_index = reader.read_u16::()?; + let data_reference_index = reader.get_u16(); - reader.read_u32::()?; // pre-defined, reserved - reader.read_u64::()?; // pre-defined - reader.read_u32::()?; // pre-defined - let width = reader.read_u16::()?; - let height = reader.read_u16::()?; - let horizresolution = FixedPointU16::new_raw(reader.read_u32::()?); - let vertresolution = FixedPointU16::new_raw(reader.read_u32::()?); - reader.read_u32::()?; // reserved - let frame_count = reader.read_u16::()?; - skip_bytes(reader, 32)?; // compressorname - let depth = reader.read_u16::()?; - reader.read_i16::()?; // pre-defined + reader.get_u32(); // pre-defined, reserved + reader.get_u64(); // pre-defined + reader.get_u32(); // pre-defined - let end = start + size; - loop { - let current = reader.stream_position()?; - if current >= end { - return Err(Error::InvalidData("avcc not found")); - } - let header = BoxHeader::read(reader)?; - let BoxHeader { name, size: s } = header; - if s > size { - return Err(Error::InvalidData( - "avc1 box contains a box with a larger size than it", - )); - } - if name == BoxType::AvcCBox { - let avcc = AvcCBox::read_box(reader, s)?; + let width = reader.get_u16(); + let height = reader.get_u16(); - skip_bytes_to(reader, start + size)?; + let horizresolution = FixedPointU16::new_raw(reader.get_u32()); + let vertresolution = FixedPointU16::new_raw(reader.get_u32()); - return Ok(Avc1Box { - data_reference_index, - width, - height, - horizresolution, - vertresolution, - frame_count, - depth, - avcc, - }); - } else { - skip_bytes_to(reader, current + s)?; - } - } + reader.get_u32(); // reserved + + let frame_count = reader.get_u16(); + + reader.skip(32); // compressorname + + let depth = reader.get_u16(); + + reader.get_i16(); // pre-defined + + Ok(Avc1Box { + data_reference_index, + width, + height, + horizresolution, + vertresolution, + frame_count, + depth, + avcc: reader.find_box::()?, + }) + } + + fn size_hint() -> usize { + 78 } } @@ -191,9 +177,7 @@ impl AvcCBox { } impl Mp4Box for AvcCBox { - fn box_type(&self) -> BoxType { - BoxType::AvcCBox - } + const TYPE: BoxType = BoxType::AvcCBox; fn box_size(&self) -> u64 { let mut size = HEADER_SIZE + 7; @@ -216,30 +200,29 @@ impl Mp4Box for AvcCBox { } } -impl ReadBox<&mut R> for AvcCBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; +impl BlockReader for AvcCBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let configuration_version = reader.get_u8(); + let avc_profile_indication = reader.get_u8(); + let profile_compatibility = reader.get_u8(); + let avc_level_indication = reader.get_u8(); + let length_size_minus_one = reader.get_u8() & 0x3; + let num_of_spss = reader.get_u8() & 0x1F; - let configuration_version = reader.read_u8()?; - let avc_profile_indication = reader.read_u8()?; - let profile_compatibility = reader.read_u8()?; - let avc_level_indication = reader.read_u8()?; - let length_size_minus_one = reader.read_u8()? & 0x3; - let num_of_spss = reader.read_u8()? & 0x1F; let mut sequence_parameter_sets = Vec::with_capacity(num_of_spss as usize); for _ in 0..num_of_spss { let nal_unit = NalUnit::read(reader)?; sequence_parameter_sets.push(nal_unit); } - let num_of_ppss = reader.read_u8()?; + + let num_of_ppss = reader.get_u8(); + let mut picture_parameter_sets = Vec::with_capacity(num_of_ppss as usize); for _ in 0..num_of_ppss { let nal_unit = NalUnit::read(reader)?; picture_parameter_sets.push(nal_unit); } - skip_bytes_to(reader, start + size)?; - Ok(AvcCBox { configuration_version, avc_profile_indication, @@ -250,12 +233,16 @@ impl ReadBox<&mut R> for AvcCBox { picture_parameter_sets, }) } + + fn size_hint() -> usize { + 7 + } } impl WriteBox<&mut W> for AvcCBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; writer.write_u8(self.configuration_version)?; writer.write_u8(self.avc_profile_indication)?; @@ -292,11 +279,12 @@ impl NalUnit { 2 + self.bytes.len() } - fn read(reader: &mut R) -> Result { - let length = reader.read_u16::()? as usize; - let mut bytes = vec![0u8; length]; - reader.read_exact(&mut bytes)?; - Ok(NalUnit { bytes }) + fn read<'a>(reader: &mut impl Reader<'a>) -> Result { + let length = reader.try_get_u16()? as usize; + + Ok(NalUnit { + bytes: reader.collect(length)?, + }) } fn write(&self, writer: &mut W) -> Result { @@ -310,7 +298,6 @@ impl NalUnit { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_avc1() { @@ -343,12 +330,11 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::Avc1Box); + let header = BoxHeader::read_sync(&mut buf.as_slice()).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::Avc1Box); assert_eq!(src_box.box_size(), header.size); - let dst_box = Avc1Box::read_box(&mut reader, header.size).unwrap(); + let dst_box = Avc1Box::read_block(&mut buf.as_slice()).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/mp4box/co64.rs b/src/mp4box/co64.rs index 978137e..cda4987 100644 --- a/src/mp4box/co64.rs +++ b/src/mp4box/co64.rs @@ -1,6 +1,6 @@ -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{BigEndian, WriteBytesExt}; use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use std::mem::size_of; use crate::mp4box::*; @@ -25,9 +25,7 @@ impl Co64Box { } impl Mp4Box for Co64Box { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::Co64Box; fn box_size(&self) -> u64 { self.get_size() @@ -43,46 +41,40 @@ impl Mp4Box for Co64Box { } } -impl ReadBox<&mut R> for Co64Box { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; +impl BlockReader for Co64Box { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (version, flags) = read_box_header_ext(reader); - let (version, flags) = read_box_header_ext(reader)?; - - let header_size = HEADER_SIZE + HEADER_EXT_SIZE; - let other_size = size_of::(); // entry_count let entry_size = size_of::(); // chunk_offset - let entry_count = reader.read_u32::()?; - if u64::from(entry_count) - > size - .saturating_sub(header_size) - .saturating_sub(other_size as u64) - / entry_size as u64 - { + let entry_count = reader.get_u32(); + if entry_count as usize > reader.remaining() / entry_size { return Err(Error::InvalidData( "co64 entry_count indicates more entries than could fit in the box", )); } + let mut entries = Vec::with_capacity(entry_count as usize); for _i in 0..entry_count { - let chunk_offset = reader.read_u64::()?; + let chunk_offset = reader.get_u64(); entries.push(chunk_offset); } - skip_bytes_to(reader, start + size)?; - Ok(Co64Box { version, flags, entries, }) } + + fn size_hint() -> usize { + 8 + } } impl WriteBox<&mut W> for Co64Box { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; write_box_header_ext(writer, self.version, self.flags)?; @@ -99,7 +91,6 @@ impl WriteBox<&mut W> for Co64Box { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_co64() { @@ -112,12 +103,11 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::Co64Box); + let header = BoxHeader::read_sync(&mut buf.as_slice()).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::Co64Box); assert_eq!(src_box.box_size(), header.size); - let dst_box = Co64Box::read_box(&mut reader, header.size).unwrap(); + let dst_box = Co64Box::read_block(&mut buf.as_slice()).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/mp4box/ctts.rs b/src/mp4box/ctts.rs index 673e8c9..78457be 100644 --- a/src/mp4box/ctts.rs +++ b/src/mp4box/ctts.rs @@ -1,6 +1,6 @@ -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{BigEndian, WriteBytesExt}; use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use std::mem::size_of; use crate::mp4box::*; @@ -31,9 +31,7 @@ pub struct CttsEntry { } impl Mp4Box for CttsBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::CttsBox; fn box_size(&self) -> u64 { self.get_size() @@ -49,50 +47,45 @@ impl Mp4Box for CttsBox { } } -impl ReadBox<&mut R> for CttsBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; +impl BlockReader for CttsBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (version, flags) = read_box_header_ext(reader); - let (version, flags) = read_box_header_ext(reader)?; - - let header_size = HEADER_SIZE + HEADER_EXT_SIZE; - let entry_count = reader.read_u32::()?; + let entry_count = reader.get_u32(); let entry_size = size_of::() + size_of::(); // sample_count + sample_offset // (sample_offset might be a u32, but the size is the same.) - let other_size = size_of::(); // entry_count - if u64::from(entry_count) - > size - .saturating_sub(header_size) - .saturating_sub(other_size as u64) - / entry_size as u64 - { + + if entry_count as usize > reader.remaining() / entry_size { return Err(Error::InvalidData( "ctts entry_count indicates more entries than could fit in the box", )); } + let mut entries = Vec::with_capacity(entry_count as usize); for _ in 0..entry_count { let entry = CttsEntry { - sample_count: reader.read_u32::()?, - sample_offset: reader.read_i32::()?, + sample_count: reader.get_u32(), + sample_offset: reader.get_i32(), }; entries.push(entry); } - skip_bytes_to(reader, start + size)?; - Ok(CttsBox { version, flags, entries, }) } + + fn size_hint() -> usize { + 8 + } } impl WriteBox<&mut W> for CttsBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; write_box_header_ext(writer, self.version, self.flags)?; @@ -110,7 +103,6 @@ impl WriteBox<&mut W> for CttsBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_ctts() { @@ -132,12 +124,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::CttsBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::CttsBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = CttsBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = CttsBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/mp4box/data.rs b/src/mp4box/data.rs index 19b5c77..a4fac3d 100644 --- a/src/mp4box/data.rs +++ b/src/mp4box/data.rs @@ -1,9 +1,5 @@ -use std::{ - convert::TryFrom, - io::{Read, Seek}, -}; - use serde::Serialize; +use std::convert::TryFrom; use crate::mp4box::*; @@ -28,9 +24,7 @@ impl DataBox { } impl Mp4Box for DataBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::DataBox; fn box_size(&self) -> u64 { self.get_size() @@ -46,26 +40,26 @@ impl Mp4Box for DataBox { } } -impl ReadBox<&mut R> for DataBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; +impl BlockReader for DataBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let data_type = DataType::try_from(reader.get_u32())?; + reader.get_u32(); // reserved = 0 - let data_type = DataType::try_from(reader.read_u32::()?)?; + Ok(DataBox { + data: reader.collect(reader.remaining())?, + data_type, + }) + } - reader.read_u32::()?; // reserved = 0 - - let current = reader.stream_position()?; - let mut data = vec![0u8; (start + size - current) as usize]; - reader.read_exact(&mut data)?; - - Ok(DataBox { data, data_type }) + fn size_hint() -> usize { + 8 } } impl WriteBox<&mut W> for DataBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; writer.write_u32::(self.data_type.clone() as u32)?; writer.write_u32::(0)?; // reserved = 0 @@ -79,7 +73,6 @@ impl WriteBox<&mut W> for DataBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_data() { @@ -91,12 +84,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::DataBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::DataBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = DataBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = DataBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } @@ -107,12 +100,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::DataBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::DataBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = DataBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = DataBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/mp4box/dinf.rs b/src/mp4box/dinf.rs index e365e4a..09d7883 100644 --- a/src/mp4box/dinf.rs +++ b/src/mp4box/dinf.rs @@ -1,5 +1,5 @@ use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use crate::mp4box::*; @@ -19,9 +19,7 @@ impl DinfBox { } impl Mp4Box for DinfBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::DinfBox; fn box_size(&self) -> u64 { self.get_size() @@ -37,53 +35,22 @@ impl Mp4Box for DinfBox { } } -impl ReadBox<&mut R> for DinfBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; - - let mut dref = None; - - let mut current = reader.stream_position()?; - let end = start + size; - while current < end { - // Get box header. - let header = BoxHeader::read(reader)?; - let BoxHeader { name, size: s } = header; - if s > size { - return Err(Error::InvalidData( - "dinf box contains a box with a larger size than it", - )); - } - - match name { - BoxType::DrefBox => { - dref = Some(DrefBox::read_box(reader, s)?); - } - _ => { - // XXX warn!() - skip_box(reader, s)?; - } - } - - current = reader.stream_position()?; - } - - if dref.is_none() { - return Err(Error::BoxNotFound(BoxType::DrefBox)); - } - - skip_bytes_to(reader, start + size)?; - +impl BlockReader for DinfBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { Ok(DinfBox { - dref: dref.unwrap(), + dref: reader.find_box::()?, }) } + + fn size_hint() -> usize { + DrefBox::size_hint() + } } impl WriteBox<&mut W> for DinfBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; self.dref.write_box(writer)?; Ok(size) } @@ -109,10 +76,6 @@ impl Default for DrefBox { } impl DrefBox { - pub fn get_type(&self) -> BoxType { - BoxType::DrefBox - } - pub fn get_size(&self) -> u64 { let mut size = HEADER_SIZE + HEADER_EXT_SIZE + 4; if let Some(ref url) = self.url { @@ -123,9 +86,7 @@ impl DrefBox { } impl Mp4Box for DrefBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::DrefBox; fn box_size(&self) -> u64 { self.get_size() @@ -141,58 +102,32 @@ impl Mp4Box for DrefBox { } } -impl ReadBox<&mut R> for DrefBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; - - let mut current = reader.stream_position()?; - - let (version, flags) = read_box_header_ext(reader)?; - let end = start + size; - +impl BlockReader for DrefBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (version, flags) = read_box_header_ext(reader); let mut url = None; + let entry_count = reader.get_u32(); - let entry_count = reader.read_u32::()?; for _i in 0..entry_count { - if current >= end { - break; - } - - // Get box header. - let header = BoxHeader::read(reader)?; - let BoxHeader { name, size: s } = header; - if s > size { - return Err(Error::InvalidData( - "dinf box contains a box with a larger size than it", - )); - } - - match name { - BoxType::UrlBox => { - url = Some(UrlBox::read_box(reader, s)?); - } - _ => { - skip_box(reader, s)?; - } - } - - current = reader.stream_position()?; + url = reader.try_find_box()?; } - skip_bytes_to(reader, start + size)?; - Ok(DrefBox { version, flags, url, }) } + + fn size_hint() -> usize { + 8 + } } impl WriteBox<&mut W> for DrefBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; write_box_header_ext(writer, self.version, self.flags)?; @@ -224,10 +159,6 @@ impl Default for UrlBox { } impl UrlBox { - pub fn get_type(&self) -> BoxType { - BoxType::UrlBox - } - pub fn get_size(&self) -> u64 { let mut size = HEADER_SIZE + HEADER_EXT_SIZE; @@ -240,9 +171,7 @@ impl UrlBox { } impl Mp4Box for UrlBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::UrlBox; fn box_size(&self) -> u64 { self.get_size() @@ -258,37 +187,26 @@ impl Mp4Box for UrlBox { } } -impl ReadBox<&mut R> for UrlBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; - - let (version, flags) = read_box_header_ext(reader)?; - - let buf_size = size - .checked_sub(HEADER_SIZE + HEADER_EXT_SIZE) - .ok_or(Error::InvalidData("url size too small"))?; - - let mut buf = vec![0u8; buf_size as usize]; - reader.read_exact(&mut buf)?; - if let Some(end) = buf.iter().position(|&b| b == b'\0') { - buf.truncate(end); - } - let location = String::from_utf8(buf).unwrap_or_default(); - - skip_bytes_to(reader, start + size)?; +impl BlockReader for UrlBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (version, flags) = read_box_header_ext(reader); Ok(UrlBox { version, flags, - location, + location: reader.get_null_terminated_string(), }) } + + fn size_hint() -> usize { + 4 + } } impl WriteBox<&mut W> for UrlBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; write_box_header_ext(writer, self.version, self.flags)?; diff --git a/src/mp4box/edts.rs b/src/mp4box/edts.rs index 9077bb1..a5d5228 100644 --- a/src/mp4box/edts.rs +++ b/src/mp4box/edts.rs @@ -1,5 +1,5 @@ use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use crate::mp4box::elst::ElstBox; use crate::mp4box::*; @@ -10,10 +10,6 @@ pub struct EdtsBox { } impl EdtsBox { - pub(crate) fn new() -> EdtsBox { - Default::default() - } - pub fn get_type(&self) -> BoxType { BoxType::EdtsBox } @@ -28,9 +24,7 @@ impl EdtsBox { } impl Mp4Box for EdtsBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::EdtsBox; fn box_size(&self) -> u64 { self.get_size() @@ -46,35 +40,22 @@ impl Mp4Box for EdtsBox { } } -impl ReadBox<&mut R> for EdtsBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; +impl BlockReader for EdtsBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + Ok(EdtsBox { + elst: reader.try_find_box::()?, + }) + } - let mut edts = EdtsBox::new(); - - let header = BoxHeader::read(reader)?; - let BoxHeader { name, size: s } = header; - if s > size { - return Err(Error::InvalidData( - "edts box contains a box with a larger size than it", - )); - } - - if let BoxType::ElstBox = name { - let elst = ElstBox::read_box(reader, s)?; - edts.elst = Some(elst); - } - - skip_bytes_to(reader, start + size)?; - - Ok(edts) + fn size_hint() -> usize { + 0 } } impl WriteBox<&mut W> for EdtsBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; if let Some(ref elst) = self.elst { elst.write_box(writer)?; diff --git a/src/mp4box/elst.rs b/src/mp4box/elst.rs index 297fb63..0ab8edc 100644 --- a/src/mp4box/elst.rs +++ b/src/mp4box/elst.rs @@ -1,6 +1,6 @@ -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{BigEndian, WriteBytesExt}; use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use std::mem::size_of; use crate::mp4box::*; @@ -39,9 +39,7 @@ impl ElstBox { } impl Mp4Box for ElstBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::ElstBox; fn box_size(&self) -> u64 { self.get_size() @@ -57,15 +55,11 @@ impl Mp4Box for ElstBox { } } -impl ReadBox<&mut R> for ElstBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; +impl BlockReader for ElstBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (version, flags) = read_box_header_ext(reader); - let (version, flags) = read_box_header_ext(reader)?; - - let header_size = HEADER_SIZE + HEADER_EXT_SIZE; - let entry_count = reader.read_u32::()?; - let other_size = size_of::(); // entry_count + let entry_count = reader.get_u32(); let entry_size = { let mut entry_size = 0; entry_size += if version == 1 { @@ -73,56 +67,49 @@ impl ReadBox<&mut R> for ElstBox { } else { size_of::() + size_of::() // segment_duration + media_time }; + entry_size += size_of::() + size_of::(); // media_rate_integer + media_rate_fraction entry_size }; - if u64::from(entry_count) - > size - .saturating_sub(header_size) - .saturating_sub(other_size as u64) - / entry_size as u64 - { + + if entry_count as usize > reader.remaining() / entry_size { return Err(Error::InvalidData( "elst entry_count indicates more entries than could fit in the box", )); } + let mut entries = Vec::with_capacity(entry_count as usize); for _ in 0..entry_count { let (segment_duration, media_time) = if version == 1 { - ( - reader.read_u64::()?, - reader.read_u64::()?, - ) + (reader.get_u64(), reader.get_u64()) } else { - ( - reader.read_u32::()? as u64, - reader.read_u32::()? as u64, - ) + (reader.get_u32() as u64, reader.get_u32() as u64) }; - let entry = ElstEntry { + entries.push(ElstEntry { segment_duration, media_time, - media_rate: reader.read_u16::()?, - media_rate_fraction: reader.read_u16::()?, - }; - entries.push(entry); + media_rate: reader.get_u16(), + media_rate_fraction: reader.get_u16(), + }); } - skip_bytes_to(reader, start + size)?; - Ok(ElstBox { version, flags, entries, }) } + + fn size_hint() -> usize { + 8 + } } impl WriteBox<&mut W> for ElstBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; write_box_header_ext(writer, self.version, self.flags)?; @@ -147,7 +134,6 @@ impl WriteBox<&mut W> for ElstBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_elst32() { @@ -165,12 +151,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::ElstBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::ElstBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = ElstBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = ElstBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } @@ -190,12 +176,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::ElstBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::ElstBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = ElstBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = ElstBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/mp4box/emsg.rs b/src/mp4box/emsg.rs index f68ba3a..f4a4085 100644 --- a/src/mp4box/emsg.rs +++ b/src/mp4box/emsg.rs @@ -1,7 +1,6 @@ -use std::ffi::CStr; -use std::io::{Read, Seek, Write}; +use std::io::Write; -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{BigEndian, WriteBytesExt}; use serde::Serialize; use crate::mp4box::*; @@ -39,9 +38,7 @@ impl EmsgBox { } impl Mp4Box for EmsgBox { - fn box_type(&self) -> BoxType { - BoxType::EmsgBox - } + const TYPE: BoxType = BoxType::EmsgBox; fn box_size(&self) -> u64 { Self::size_without_message(self.version, &self.scheme_id_uri, &self.value) @@ -58,10 +55,9 @@ impl Mp4Box for EmsgBox { } } -impl ReadBox<&mut R> for EmsgBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; - let (version, flags) = read_box_header_ext(reader)?; +impl BlockReader for EmsgBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (version, flags) = read_box_header_ext(reader); let ( timescale, @@ -73,38 +69,31 @@ impl ReadBox<&mut R> for EmsgBox { value, ) = match version { 0 => { - let scheme_id_uri = read_null_terminated_utf8_string(reader)?; - let value = read_null_terminated_utf8_string(reader)?; + let scheme_id_uri = reader.get_null_terminated_string(); + let value = reader.get_null_terminated_string(); + ( - reader.read_u32::()?, + reader.get_u32(), None, - Some(reader.read_u32::()?), - reader.read_u32::()?, - reader.read_u32::()?, + Some(reader.get_u32()), + reader.get_u32(), + reader.get_u32(), scheme_id_uri, value, ) } 1 => ( - reader.read_u32::()?, - Some(reader.read_u64::()?), + reader.get_u32(), + Some(reader.get_u64()), None, - reader.read_u32::()?, - reader.read_u32::()?, - read_null_terminated_utf8_string(reader)?, - read_null_terminated_utf8_string(reader)?, + reader.get_u32(), + reader.get_u32(), + reader.get_null_terminated_string(), + reader.get_null_terminated_string(), ), _ => return Err(Error::InvalidData("version must be 0 or 1")), }; - let message_size = size - Self::size_without_message(version, &scheme_id_uri, &value); - let mut message_data = Vec::with_capacity(message_size as usize); - for _ in 0..message_size { - message_data.push(reader.read_u8()?); - } - - skip_bytes_to(reader, start + size)?; - Ok(EmsgBox { version, flags, @@ -115,15 +104,19 @@ impl ReadBox<&mut R> for EmsgBox { id, scheme_id_uri, value, - message_data, + message_data: reader.collect(reader.remaining())?, }) } + + fn size_hint() -> usize { + 22 + } } impl WriteBox<&mut W> for EmsgBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; write_box_header_ext(writer, self.version, self.flags)?; match self.version { @@ -154,22 +147,6 @@ impl WriteBox<&mut W> for EmsgBox { } } -fn read_null_terminated_utf8_string(reader: &mut R) -> Result { - let mut bytes = Vec::new(); - loop { - let byte = reader.read_u8()?; - bytes.push(byte); - if byte == 0 { - break; - } - } - if let Ok(str) = unsafe { CStr::from_bytes_with_nul_unchecked(&bytes) }.to_str() { - Ok(str.to_string()) - } else { - Err(Error::InvalidData("invalid utf8")) - } -} - fn write_null_terminated_str(writer: &mut W, string: &str) -> Result<()> { for byte in string.bytes() { writer.write_u8(byte)?; @@ -180,7 +157,6 @@ fn write_null_terminated_str(writer: &mut W, string: &str) -> Result<( #[cfg(test)] mod tests { - use std::io::Cursor; use crate::mp4box::BoxHeader; @@ -204,12 +180,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::EmsgBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::EmsgBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = EmsgBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = EmsgBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } @@ -231,12 +207,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::EmsgBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::EmsgBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = EmsgBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = EmsgBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/mp4box/ftyp.rs b/src/mp4box/ftyp.rs index 789cd4e..87fa6d9 100644 --- a/src/mp4box/ftyp.rs +++ b/src/mp4box/ftyp.rs @@ -1,6 +1,6 @@ -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{BigEndian, WriteBytesExt}; use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use crate::mp4box::*; @@ -22,9 +22,7 @@ impl FtypBox { } impl Mp4Box for FtypBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::FtypBox; fn box_size(&self) -> u64 { self.get_size() @@ -49,37 +47,35 @@ impl Mp4Box for FtypBox { } } -impl ReadBox<&mut R> for FtypBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; +impl BlockReader for FtypBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let brand_count = (reader.remaining() - 16) / 4; // header + major + minor - if size < 16 || size % 4 != 0 { - return Err(Error::InvalidData("ftyp size too small or not aligned")); - } - let brand_count = (size - 16) / 4; // header + major + minor - let major = reader.read_u32::()?; - let minor = reader.read_u32::()?; + let major = reader.get_u32(); + let minor = reader.get_u32(); let mut brands = Vec::new(); for _ in 0..brand_count { - let b = reader.read_u32::()?; + let b = reader.get_u32(); brands.push(From::from(b)); } - skip_bytes_to(reader, start + size)?; - Ok(FtypBox { major_brand: From::from(major), minor_version: minor, compatible_brands: brands, }) } + + fn size_hint() -> usize { + 8 + } } impl WriteBox<&mut W> for FtypBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; writer.write_u32::((&self.major_brand).into())?; writer.write_u32::(self.minor_version)?; @@ -94,7 +90,6 @@ impl WriteBox<&mut W> for FtypBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_ftyp() { @@ -112,12 +107,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::FtypBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::FtypBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = FtypBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = FtypBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/mp4box/hdlr.rs b/src/mp4box/hdlr.rs index b9d86a9..7a0d49c 100644 --- a/src/mp4box/hdlr.rs +++ b/src/mp4box/hdlr.rs @@ -1,6 +1,6 @@ -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{BigEndian, WriteBytesExt}; use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use crate::mp4box::*; @@ -23,9 +23,7 @@ impl HdlrBox { } impl Mp4Box for HdlrBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::HdlrBox; fn box_size(&self) -> u64 { self.get_size() @@ -41,43 +39,33 @@ impl Mp4Box for HdlrBox { } } -impl ReadBox<&mut R> for HdlrBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; +impl BlockReader for HdlrBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (version, flags) = read_box_header_ext(reader); - let (version, flags) = read_box_header_ext(reader)?; + reader.get_u32(); // pre-defined - reader.read_u32::()?; // pre-defined - let handler = reader.read_u32::()?; + let handler = reader.get_u32(); - skip_bytes(reader, 12)?; // reserved - - let buf_size = size - .checked_sub(HEADER_SIZE + HEADER_EXT_SIZE + 20) - .ok_or(Error::InvalidData("hdlr size too small"))?; - - let mut buf = vec![0u8; buf_size as usize]; - reader.read_exact(&mut buf)?; - if let Some(end) = buf.iter().position(|&b| b == b'\0') { - buf.truncate(end); - } - let handler_string = String::from_utf8(buf).unwrap_or_default(); - - skip_bytes_to(reader, start + size)?; + reader.skip(12); Ok(HdlrBox { version, flags, handler_type: From::from(handler), - name: handler_string, + name: reader.get_null_terminated_string(), }) } + + fn size_hint() -> usize { + 24 + } } impl WriteBox<&mut W> for HdlrBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; write_box_header_ext(writer, self.version, self.flags)?; @@ -100,7 +88,6 @@ impl WriteBox<&mut W> for HdlrBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_hdlr() { @@ -114,12 +101,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::HdlrBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::HdlrBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = HdlrBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = HdlrBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } @@ -135,12 +122,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::HdlrBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::HdlrBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = HdlrBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = HdlrBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } @@ -162,12 +149,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::HdlrBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::HdlrBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = HdlrBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = HdlrBox::read_block(&mut reader).unwrap(); assert_eq!(real_src_box, dst_box); } } diff --git a/src/mp4box/hev1.rs b/src/mp4box/hev1.rs index 3070fb8..88821c6 100644 --- a/src/mp4box/hev1.rs +++ b/src/mp4box/hev1.rs @@ -1,6 +1,6 @@ -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{BigEndian, WriteBytesExt}; use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use crate::mp4box::*; @@ -59,9 +59,7 @@ impl Hev1Box { } impl Mp4Box for Hev1Box { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::Hev1Box; fn box_size(&self) -> u64 { self.get_size() @@ -80,59 +78,54 @@ impl Mp4Box for Hev1Box { } } -impl ReadBox<&mut R> for Hev1Box { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; +impl BlockReader for Hev1Box { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + reader.get_u32(); // reserved + reader.get_u16(); // reserved - reader.read_u32::()?; // reserved - reader.read_u16::()?; // reserved - let data_reference_index = reader.read_u16::()?; + let data_reference_index = reader.get_u16(); - reader.read_u32::()?; // pre-defined, reserved - reader.read_u64::()?; // pre-defined - reader.read_u32::()?; // pre-defined - let width = reader.read_u16::()?; - let height = reader.read_u16::()?; - let horizresolution = FixedPointU16::new_raw(reader.read_u32::()?); - let vertresolution = FixedPointU16::new_raw(reader.read_u32::()?); - reader.read_u32::()?; // reserved - let frame_count = reader.read_u16::()?; - skip_bytes(reader, 32)?; // compressorname - let depth = reader.read_u16::()?; - reader.read_i16::()?; // pre-defined + reader.get_u32(); // pre-defined, reserved + reader.get_u64(); // pre-defined + reader.get_u32(); // pre-defined - let header = BoxHeader::read(reader)?; - let BoxHeader { name, size: s } = header; - if s > size { - return Err(Error::InvalidData( - "hev1 box contains a box with a larger size than it", - )); - } - if name == BoxType::HvcCBox { - let hvcc = HvcCBox::read_box(reader, s)?; + let width = reader.get_u16(); + let height = reader.get_u16(); - skip_bytes_to(reader, start + size)?; + let horizresolution = FixedPointU16::new_raw(reader.get_u32()); + let vertresolution = FixedPointU16::new_raw(reader.get_u32()); - Ok(Hev1Box { - data_reference_index, - width, - height, - horizresolution, - vertresolution, - frame_count, - depth, - hvcc, - }) - } else { - Err(Error::InvalidData("hvcc not found")) - } + reader.get_u32(); // reserved + + let frame_count = reader.get_u16(); + + reader.skip(32); // compressorname + + let depth = reader.get_u16(); + + reader.get_i16(); // pre-defined + + Ok(Hev1Box { + data_reference_index, + width, + height, + horizresolution, + vertresolution, + frame_count, + depth, + hvcc: reader.find_box::()?, + }) + } + + fn size_hint() -> usize { + 78 } } impl WriteBox<&mut W> for Hev1Box { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; writer.write_u32::(0)?; // reserved writer.write_u16::(0)?; // reserved @@ -190,9 +183,7 @@ impl HvcCBox { } impl Mp4Box for HvcCBox { - fn box_type(&self) -> BoxType { - BoxType::HvcCBox - } + const TYPE: BoxType = BoxType::HvcCBox; fn box_size(&self) -> u64 { HEADER_SIZE @@ -244,45 +235,56 @@ pub struct HvcCArray { pub nalus: Vec, } -impl ReadBox<&mut R> for HvcCBox { - fn read_box(reader: &mut R, _size: u64) -> Result { - let configuration_version = reader.read_u8()?; - let params = reader.read_u8()?; +impl BlockReader for HvcCBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let configuration_version = reader.get_u8(); + let params = reader.get_u8(); let general_profile_space = params & 0b11000000 >> 6; let general_tier_flag = (params & 0b00100000 >> 5) > 0; let general_profile_idc = params & 0b00011111; - let general_profile_compatibility_flags = reader.read_u32::()?; - let general_constraint_indicator_flag = reader.read_u48::()?; - let general_level_idc = reader.read_u8()?; - let min_spatial_segmentation_idc = reader.read_u16::()? & 0x0FFF; - let parallelism_type = reader.read_u8()? & 0b11; - let chroma_format_idc = reader.read_u8()? & 0b11; - let bit_depth_luma_minus8 = reader.read_u8()? & 0b111; - let bit_depth_chroma_minus8 = reader.read_u8()? & 0b111; - let avg_frame_rate = reader.read_u16::()?; + let general_profile_compatibility_flags = reader.get_u32(); + let general_constraint_indicator_flag = reader.get_u48(); - let params = reader.read_u8()?; + let general_level_idc = reader.get_u8(); + let min_spatial_segmentation_idc = reader.get_u16() & 0x0FFF; + let parallelism_type = reader.get_u8() & 0b11; + let chroma_format_idc = reader.get_u8() & 0b11; + let bit_depth_luma_minus8 = reader.get_u8() & 0b111; + let bit_depth_chroma_minus8 = reader.get_u8() & 0b111; + let avg_frame_rate = reader.get_u16(); + + let params = reader.get_u8(); let constant_frame_rate = params & 0b11000000 >> 6; let num_temporal_layers = params & 0b00111000 >> 3; let temporal_id_nested = (params & 0b00000100 >> 2) > 0; let length_size_minus_one = params & 0b000011; - let num_of_arrays = reader.read_u8()?; + let num_of_arrays = reader.get_u8(); + + if reader.remaining() < num_of_arrays as usize * 3 { + return Err(Error::InvalidData("")); + } let mut arrays = Vec::with_capacity(num_of_arrays as _); + for _ in 0..num_of_arrays { - let params = reader.read_u8()?; - let num_nalus = reader.read_u16::()?; + let params = reader.get_u8(); + let num_nalus = reader.get_u16(); + + if reader.remaining() < num_nalus as usize * 2 { + return Err(Error::InvalidData("")); + } + let mut nalus = Vec::with_capacity(num_nalus as usize); for _ in 0..num_nalus { - let size = reader.read_u16::()?; - let mut data = vec![0; size as usize]; + let size = reader.get_u16(); - reader.read_exact(&mut data)?; - - nalus.push(HvcCArrayNalu { size, data }) + nalus.push(HvcCArrayNalu { + size, + data: reader.collect(size as _)?, + }) } arrays.push(HvcCArray { @@ -313,12 +315,16 @@ impl ReadBox<&mut R> for HvcCBox { arrays, }) } + + fn size_hint() -> usize { + 23 + } } impl WriteBox<&mut W> for HvcCBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; writer.write_u8(self.configuration_version)?; let general_profile_space = (self.general_profile_space & 0b11) << 6; @@ -363,7 +369,6 @@ impl WriteBox<&mut W> for HvcCBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_hev1() { @@ -384,12 +389,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::Hev1Box); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::Hev1Box); assert_eq!(src_box.box_size(), header.size); - let dst_box = Hev1Box::read_box(&mut reader, header.size).unwrap(); + let dst_box = Hev1Box::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/mp4box/ilst.rs b/src/mp4box/ilst.rs index d0292a3..86751af 100644 --- a/src/mp4box/ilst.rs +++ b/src/mp4box/ilst.rs @@ -1,6 +1,5 @@ use std::borrow::Cow; use std::collections::HashMap; -use std::io::{Read, Seek}; use byteorder::ByteOrder; use serde::Serialize; @@ -10,7 +9,7 @@ use crate::mp4box::*; #[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)] pub struct IlstBox { - pub items: HashMap, + pub items: HashMap, } impl IlstBox { @@ -28,9 +27,7 @@ impl IlstBox { } impl Mp4Box for IlstBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::IlstBox; fn box_size(&self) -> u64 { self.get_size() @@ -46,56 +43,52 @@ impl Mp4Box for IlstBox { } } -impl ReadBox<&mut R> for IlstBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; - +impl BlockReader for IlstBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { let mut items = HashMap::new(); - let mut current = reader.stream_position()?; - let end = start + size; - while current < end { - // Get box header. - let header = BoxHeader::read(reader)?; - let BoxHeader { name, size: s } = header; - if s > size { - return Err(Error::InvalidData( - "ilst box contains a box with a larger size than it", - )); - } - - match name { + while let Some(mut bx) = reader.get_box()? { + match bx.kind { BoxType::NameBox => { - items.insert(MetadataKey::Title, IlstItemBox::read_box(reader, s)?); + if let Some(title) = bx.inner.try_find_box::()? { + items.insert(MetadataKey::Title, title); + } } - BoxType::DayBox => { - items.insert(MetadataKey::Year, IlstItemBox::read_box(reader, s)?); - } - BoxType::CovrBox => { - items.insert(MetadataKey::Poster, IlstItemBox::read_box(reader, s)?); - } - BoxType::DescBox => { - items.insert(MetadataKey::Summary, IlstItemBox::read_box(reader, s)?); - } - _ => { - // XXX warn!() - skip_box(reader, s)?; - } - } - current = reader.stream_position()?; + BoxType::DayBox => { + if let Some(day) = bx.inner.try_find_box::()? { + items.insert(MetadataKey::Year, day); + } + } + + BoxType::CovrBox => { + if let Some(cover) = bx.inner.try_find_box::()? { + items.insert(MetadataKey::Poster, cover); + } + } + + BoxType::DescBox => { + if let Some(summary) = bx.inner.try_find_box::()? { + items.insert(MetadataKey::Summary, summary); + } + } + + _ => continue, + } } - skip_bytes_to(reader, start + size)?; - Ok(IlstBox { items }) } + + fn size_hint() -> usize { + 0 + } } impl WriteBox<&mut W> for IlstBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; for (key, value) in &self.items { let name = match key { @@ -104,67 +97,14 @@ impl WriteBox<&mut W> for IlstBox { MetadataKey::Poster => BoxType::CovrBox, MetadataKey::Summary => BoxType::DescBox, }; + BoxHeader::new(name, value.get_size()).write(writer)?; - value.data.write_box(writer)?; + value.write_box(writer)?; } Ok(size) } } -#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)] -pub struct IlstItemBox { - pub data: DataBox, -} - -impl IlstItemBox { - fn get_size(&self) -> u64 { - HEADER_SIZE + self.data.box_size() - } -} - -impl ReadBox<&mut R> for IlstItemBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; - - let mut data = None; - - let mut current = reader.stream_position()?; - let end = start + size; - while current < end { - // Get box header. - let header = BoxHeader::read(reader)?; - let BoxHeader { name, size: s } = header; - if s > size { - return Err(Error::InvalidData( - "ilst item box contains a box with a larger size than it", - )); - } - - match name { - BoxType::DataBox => { - data = Some(DataBox::read_box(reader, s)?); - } - _ => { - // XXX warn!() - skip_box(reader, s)?; - } - } - - current = reader.stream_position()?; - } - - if data.is_none() { - return Err(Error::BoxNotFound(BoxType::DataBox)); - } - - skip_bytes_to(reader, start + size)?; - - Ok(IlstItemBox { - data: data.unwrap(), - }) - } -} - impl<'a> Metadata<'a> for IlstBox { fn title(&self) -> Option> { self.items.get(&MetadataKey::Title).map(item_to_str) @@ -183,18 +123,18 @@ impl<'a> Metadata<'a> for IlstBox { } } -fn item_to_bytes(item: &IlstItemBox) -> &[u8] { - &item.data.data +fn item_to_bytes(item: &DataBox) -> &[u8] { + &item.data } -fn item_to_str(item: &IlstItemBox) -> Cow { - String::from_utf8_lossy(&item.data.data) +fn item_to_str(item: &DataBox) -> Cow { + String::from_utf8_lossy(&item.data) } -fn item_to_u32(item: &IlstItemBox) -> Option { - match item.data.data_type { - DataType::Binary if item.data.data.len() == 4 => Some(BigEndian::read_u32(&item.data.data)), - DataType::Text => String::from_utf8_lossy(&item.data.data).parse::().ok(), +fn item_to_u32(item: &DataBox) -> Option { + match item.data_type { + DataType::Binary if item.data.len() == 4 => Some(BigEndian::read_u32(&item.data)), + DataType::Text => String::from_utf8_lossy(&item.data).parse::().ok(), _ => None, } } @@ -203,22 +143,20 @@ fn item_to_u32(item: &IlstItemBox) -> Option { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_ilst() { - let src_year = IlstItemBox { - data: DataBox { - data_type: DataType::Text, - data: b"test_year".to_vec(), - }, + let src_year = DataBox { + data_type: DataType::Text, + data: b"test_year".to_vec(), }; + let src_box = IlstBox { items: [ - (MetadataKey::Title, IlstItemBox::default()), + (MetadataKey::Title, DataBox::default()), (MetadataKey::Year, src_year), - (MetadataKey::Poster, IlstItemBox::default()), - (MetadataKey::Summary, IlstItemBox::default()), + (MetadataKey::Poster, DataBox::default()), + (MetadataKey::Summary, DataBox::default()), ] .into(), }; @@ -226,12 +164,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::IlstBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::IlstBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = IlstBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = IlstBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } @@ -242,12 +180,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::IlstBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::IlstBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = IlstBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = IlstBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/mp4box/mdhd.rs b/src/mp4box/mdhd.rs index 31c65a8..9bd6f9a 100644 --- a/src/mp4box/mdhd.rs +++ b/src/mp4box/mdhd.rs @@ -1,7 +1,7 @@ -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{BigEndian, WriteBytesExt}; use serde::Serialize; use std::char::{decode_utf16, REPLACEMENT_CHARACTER}; -use std::io::{Read, Seek, Write}; +use std::io::Write; use crate::mp4box::*; @@ -49,9 +49,7 @@ impl Default for MdhdBox { } impl Mp4Box for MdhdBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::MdhdBox; fn box_size(&self) -> u64 { self.get_size() @@ -70,33 +68,30 @@ impl Mp4Box for MdhdBox { } } -impl ReadBox<&mut R> for MdhdBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; - - let (version, flags) = read_box_header_ext(reader)?; +impl BlockReader for MdhdBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (version, flags) = read_box_header_ext(reader); let (creation_time, modification_time, timescale, duration) = if version == 1 { ( - reader.read_u64::()?, - reader.read_u64::()?, - reader.read_u32::()?, - reader.read_u64::()?, + reader.get_u64(), + reader.get_u64(), + reader.get_u32(), + reader.get_u64(), ) } else if version == 0 { ( - reader.read_u32::()? as u64, - reader.read_u32::()? as u64, - reader.read_u32::()?, - reader.read_u32::()? as u64, + reader.get_u32() as u64, + reader.get_u32() as u64, + reader.get_u32(), + reader.get_u32() as u64, ) } else { return Err(Error::InvalidData("version must be 0 or 1")); }; - let language_code = reader.read_u16::()?; - let language = language_string(language_code); - skip_bytes_to(reader, start + size)?; + let language_code = reader.get_u16(); + let language = language_string(language_code); Ok(MdhdBox { version, @@ -108,12 +103,16 @@ impl ReadBox<&mut R> for MdhdBox { language, }) } + + fn size_hint() -> usize { + 22 + } } impl WriteBox<&mut W> for MdhdBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; write_box_header_ext(writer, self.version, self.flags)?; @@ -166,7 +165,6 @@ fn language_code(language: &str) -> u16 { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; fn test_language_code(lang: &str) { let code = language_code(lang); @@ -196,12 +194,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::MdhdBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::MdhdBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = MdhdBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = MdhdBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } @@ -220,12 +218,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::MdhdBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::MdhdBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = MdhdBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = MdhdBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/mp4box/mdia.rs b/src/mp4box/mdia.rs index 423bf72..a87db58 100644 --- a/src/mp4box/mdia.rs +++ b/src/mp4box/mdia.rs @@ -1,5 +1,5 @@ use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use crate::mp4box::*; use crate::mp4box::{hdlr::HdlrBox, mdhd::MdhdBox, minf::MinfBox}; @@ -22,9 +22,7 @@ impl MdiaBox { } impl Mp4Box for MdiaBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::MdiaBox; fn box_size(&self) -> u64 { self.get_size() @@ -40,69 +38,21 @@ impl Mp4Box for MdiaBox { } } -impl ReadBox<&mut R> for MdiaBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; +impl BlockReader for MdiaBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (mdhd, hdlr, minf) = reader.find_box3()?; + Ok(MdiaBox { mdhd, hdlr, minf }) + } - let mut mdhd = None; - let mut hdlr = None; - let mut minf = None; - - let mut current = reader.stream_position()?; - let end = start + size; - while current < end { - // Get box header. - let header = BoxHeader::read(reader)?; - let BoxHeader { name, size: s } = header; - if s > size { - return Err(Error::InvalidData( - "mdia box contains a box with a larger size than it", - )); - } - - match name { - BoxType::MdhdBox => { - mdhd = Some(MdhdBox::read_box(reader, s)?); - } - BoxType::HdlrBox => { - hdlr = Some(HdlrBox::read_box(reader, s)?); - } - BoxType::MinfBox => { - minf = Some(MinfBox::read_box(reader, s)?); - } - _ => { - // XXX warn!() - skip_box(reader, s)?; - } - } - - current = reader.stream_position()?; - } - - if mdhd.is_none() { - return Err(Error::BoxNotFound(BoxType::MdhdBox)); - } - if hdlr.is_none() { - return Err(Error::BoxNotFound(BoxType::HdlrBox)); - } - if minf.is_none() { - return Err(Error::BoxNotFound(BoxType::MinfBox)); - } - - skip_bytes_to(reader, start + size)?; - - Ok(MdiaBox { - mdhd: mdhd.unwrap(), - hdlr: hdlr.unwrap(), - minf: minf.unwrap(), - }) + fn size_hint() -> usize { + 0 } } impl WriteBox<&mut W> for MdiaBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; self.mdhd.write_box(writer)?; self.hdlr.write_box(writer)?; diff --git a/src/mp4box/mehd.rs b/src/mp4box/mehd.rs index 63c0246..4f97948 100644 --- a/src/mp4box/mehd.rs +++ b/src/mp4box/mehd.rs @@ -1,6 +1,6 @@ -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{BigEndian, WriteBytesExt}; use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use crate::mp4box::*; @@ -29,9 +29,7 @@ impl MehdBox { } impl Mp4Box for MehdBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::MehdBox; fn box_size(&self) -> u64 { self.get_size() @@ -47,20 +45,17 @@ impl Mp4Box for MehdBox { } } -impl ReadBox<&mut R> for MehdBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; - - let (version, flags) = read_box_header_ext(reader)?; +impl BlockReader for MehdBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (version, flags) = read_box_header_ext(reader); let fragment_duration = if version == 1 { - reader.read_u64::()? + reader.get_u64() } else if version == 0 { - reader.read_u32::()? as u64 + reader.get_u32() as u64 } else { return Err(Error::InvalidData("version must be 0 or 1")); }; - skip_bytes_to(reader, start + size)?; Ok(MehdBox { version, @@ -68,12 +63,16 @@ impl ReadBox<&mut R> for MehdBox { fragment_duration, }) } + + fn size_hint() -> usize { + 8 + } } impl WriteBox<&mut W> for MehdBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; write_box_header_ext(writer, self.version, self.flags)?; @@ -93,7 +92,6 @@ impl WriteBox<&mut W> for MehdBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_mehd32() { @@ -106,12 +104,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::MehdBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::MehdBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = MehdBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = MehdBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } @@ -126,12 +124,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::MehdBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::MehdBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = MehdBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = MehdBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/mp4box/meta.rs b/src/mp4box/meta.rs index 56ca816..0d3e200 100644 --- a/src/mp4box/meta.rs +++ b/src/mp4box/meta.rs @@ -1,5 +1,3 @@ -use std::io::{Read, Seek}; - use serde::Serialize; use crate::mp4box::hdlr::HdlrBox; @@ -54,9 +52,7 @@ impl MetaBox { } impl Mp4Box for MetaBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::MetaBox; fn box_size(&self) -> u64 { self.get_size() @@ -86,18 +82,18 @@ impl Default for MetaBox { } } -impl ReadBox<&mut R> for MetaBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; +impl BlockReader for MetaBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let extended_header = reader.get_u32(); - let extended_header = reader.read_u32::()?; if extended_header != 0 { // ISO mp4 requires this header (version & flags) to be 0. Some // files skip the extended header and directly start the hdlr box. - let possible_hdlr = BoxType::from(reader.read_u32::()?); + let possible_hdlr = BoxType::from(reader.try_get_u32()?); + if possible_hdlr == BoxType::HdlrBox { // This file skipped the extended header! Go back to start. - reader.seek(SeekFrom::Current(-8))?; + // reader.seek(SeekFrom::Current(-8))?; } else { // Looks like we actually have a bad version number or flags. let v = (extended_header >> 24) as u8; @@ -105,96 +101,34 @@ impl ReadBox<&mut R> for MetaBox { } } - let mut current = reader.stream_position()?; - let end = start + size; - - let content_start = current; - // find the hdlr box - let mut hdlr = None; - while current < end { - // Get box header. - let header = BoxHeader::read(reader)?; - let BoxHeader { name, size: s } = header; + let hdlr = reader.find_box::()?; - match name { - BoxType::HdlrBox => { - hdlr = Some(HdlrBox::read_box(reader, s)?); - } - _ => { - // XXX warn!() - skip_box(reader, s)?; - } - } - - current = reader.stream_position()?; - } - - let Some(hdlr) = hdlr else { - return Err(Error::BoxNotFound(BoxType::HdlrBox)); - }; - - // rewind and handle the other boxes - reader.seek(SeekFrom::Start(content_start))?; - current = reader.stream_position()?; - - let mut ilst = None; - - match hdlr.handler_type { - MDIR => { - while current < end { - // Get box header. - let header = BoxHeader::read(reader)?; - let BoxHeader { name, size: s } = header; - - match name { - BoxType::IlstBox => { - ilst = Some(IlstBox::read_box(reader, s)?); - } - _ => { - // XXX warn!() - skip_box(reader, s)?; - } - } - - current = reader.stream_position()?; - } - - Ok(MetaBox::Mdir { ilst }) - } + Ok(match hdlr.handler_type { + MDIR => MetaBox::Mdir { + ilst: reader.try_find_box::()?, + }, _ => { let mut data = Vec::new(); - while current < end { - // Get box header. - let header = BoxHeader::read(reader)?; - let BoxHeader { name, size: s } = header; - - match name { - BoxType::HdlrBox => { - skip_box(reader, s)?; - } - _ => { - let mut box_data = vec![0; (s - HEADER_SIZE) as usize]; - reader.read_exact(&mut box_data)?; - - data.push((name, box_data)); - } - } - - current = reader.stream_position()?; + while let Some(mut bx) = reader.get_box()? { + data.push((bx.kind, bx.inner.collect_remaining())) } - Ok(MetaBox::Unknown { hdlr, data }) + MetaBox::Unknown { hdlr, data } } - } + }) + } + + fn size_hint() -> usize { + 4 } } impl WriteBox<&mut W> for MetaBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; write_box_header_ext(writer, 0, 0)?; @@ -228,7 +162,6 @@ impl WriteBox<&mut W> for MetaBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_meta_mdir_empty() { @@ -238,12 +171,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::MetaBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::MetaBox); assert_eq!(header.size, src_box.box_size()); - let dst_box = MetaBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = MetaBox::read_block(&mut reader).unwrap(); assert_eq!(dst_box, src_box); } @@ -257,23 +190,24 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::MetaBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::MetaBox); assert_eq!(header.size, src_box.box_size()); - let dst_box = MetaBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = MetaBox::read_block(&mut reader).unwrap(); assert_eq!(dst_box, src_box); } #[test] fn test_meta_hdrl_non_first() { let data = b"\x00\x00\x00\x7fmeta\x00\x00\x00\x00\x00\x00\x00Qilst\x00\x00\x00I\xa9too\x00\x00\x00Adata\x00\x00\x00\x01\x00\x00\x00\x00TMPGEnc Video Mastering Works 7 Version 7.0.15.17\x00\x00\x00\"hdlr\x00\x00\x00\x00\x00\x00\x00\x00mdirappl\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; - let mut reader = Cursor::new(data); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::MetaBox); - let meta_box = MetaBox::read_box(&mut reader, header.size).unwrap(); + let mut reader = data.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::MetaBox); + + let meta_box = MetaBox::read_block(&mut reader).unwrap(); // this contains \xa9too box in the ilst // it designates the tool that created the file, but is not yet supported by this crate @@ -301,12 +235,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::MetaBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::MetaBox); assert_eq!(header.size, src_box.box_size()); - let dst_box = MetaBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = MetaBox::read_block(&mut reader).unwrap(); assert_eq!(dst_box, src_box); } } diff --git a/src/mp4box/mfhd.rs b/src/mp4box/mfhd.rs index 7bc2f72..d1da9de 100644 --- a/src/mp4box/mfhd.rs +++ b/src/mp4box/mfhd.rs @@ -1,6 +1,6 @@ -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{BigEndian, WriteBytesExt}; use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use crate::mp4box::*; @@ -32,9 +32,7 @@ impl MfhdBox { } impl Mp4Box for MfhdBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::MfhdBox; fn box_size(&self) -> u64 { self.get_size() @@ -50,27 +48,26 @@ impl Mp4Box for MfhdBox { } } -impl ReadBox<&mut R> for MfhdBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; - - let (version, flags) = read_box_header_ext(reader)?; - let sequence_number = reader.read_u32::()?; - - skip_bytes_to(reader, start + size)?; +impl BlockReader for MfhdBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (version, flags) = read_box_header_ext(reader); Ok(MfhdBox { version, flags, - sequence_number, + sequence_number: reader.get_u32(), }) } + + fn size_hint() -> usize { + 8 + } } impl WriteBox<&mut W> for MfhdBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; write_box_header_ext(writer, self.version, self.flags)?; writer.write_u32::(self.sequence_number)?; @@ -83,7 +80,6 @@ impl WriteBox<&mut W> for MfhdBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_mfhd() { @@ -96,12 +92,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::MfhdBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::MfhdBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = MfhdBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = MfhdBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/mp4box/minf.rs b/src/mp4box/minf.rs index 5ea853b..b12cd8f 100644 --- a/src/mp4box/minf.rs +++ b/src/mp4box/minf.rs @@ -1,5 +1,5 @@ use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use crate::mp4box::*; use crate::mp4box::{dinf::DinfBox, smhd::SmhdBox, stbl::StblBox, vmhd::VmhdBox}; @@ -36,9 +36,7 @@ impl MinfBox { } impl Mp4Box for MinfBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::MinfBox; fn box_size(&self) -> u64 { self.get_size() @@ -54,58 +52,18 @@ impl Mp4Box for MinfBox { } } -impl ReadBox<&mut R> for MinfBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; - - let mut vmhd = None; - let mut smhd = None; - let mut dinf = None; - let mut stbl = None; - - let mut current = reader.stream_position()?; - let end = start + size; - while current < end { - // Get box header. - let header = BoxHeader::read(reader)?; - let BoxHeader { name, size: s } = header; - if s > size { - return Err(Error::InvalidData( - "minf box contains a box with a larger size than it", - )); - } - - match name { - BoxType::VmhdBox => { - vmhd = Some(VmhdBox::read_box(reader, s)?); - } - BoxType::SmhdBox => { - smhd = Some(SmhdBox::read_box(reader, s)?); - } - BoxType::DinfBox => { - dinf = Some(DinfBox::read_box(reader, s)?); - } - BoxType::StblBox => { - stbl = Some(StblBox::read_box(reader, s)?); - } - _ => { - // XXX warn!() - skip_box(reader, s)?; - } - } - - current = reader.stream_position()?; - } +impl BlockReader for MinfBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (vmhd, smhd, dinf, stbl) = reader.try_find_box4()?; if dinf.is_none() { return Err(Error::BoxNotFound(BoxType::DinfBox)); } + if stbl.is_none() { return Err(Error::BoxNotFound(BoxType::StblBox)); } - skip_bytes_to(reader, start + size)?; - Ok(MinfBox { vmhd, smhd, @@ -113,12 +71,16 @@ impl ReadBox<&mut R> for MinfBox { stbl: stbl.unwrap(), }) } + + fn size_hint() -> usize { + DinfBox::size_hint() + StblBox::size_hint() + } } impl WriteBox<&mut W> for MinfBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; if let Some(ref vmhd) = self.vmhd { vmhd.write_box(writer)?; diff --git a/src/mp4box/mod.rs b/src/mp4box/mod.rs index 4bbdd41..da3b2c2 100644 --- a/src/mp4box/mod.rs +++ b/src/mp4box/mod.rs @@ -56,9 +56,11 @@ //! free //! -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; -use std::convert::TryInto; -use std::io::{Read, Seek, SeekFrom, Write}; +use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; +use bytes::Buf; +use std::io::Write; +use std::{convert::TryInto, marker::PhantomData}; +use tokio::io::{AsyncRead, AsyncReadExt}; use crate::*; @@ -162,6 +164,15 @@ macro_rules! boxtype { UnknownBox(u32), } + impl BoxType { + pub const fn as_str(&self) -> &'static str { + match self { + $( BoxType::$name => stringify!($name), )* + BoxType::UnknownBox(_) => "unknown", + } + } + } + impl From for BoxType { fn from(t: u32) -> BoxType { match t { @@ -242,14 +253,461 @@ boxtype! { } pub trait Mp4Box: Sized { - fn box_type(&self) -> BoxType; + const TYPE: BoxType; + fn box_size(&self) -> u64; fn to_json(&self) -> Result; fn summary(&self) -> Result; } -pub trait ReadBox: Sized { - fn read_box(_: T, size: u64) -> Result; +pub struct BoxReader<'a, R: Reader<'a>> { + kind: BoxType, + inner: R, + m: PhantomData<&'a ()>, +} + +impl<'a, R: Reader<'a>> BoxReader<'a, R> { + #[inline] + pub fn try_read(&mut self) -> Result> { + if T::TYPE == self.kind { + Ok(Some(T::read_block(&mut self.inner)?)) + } else { + Ok(None) + } + } + + #[inline] + pub fn read(&mut self) -> Result { + if T::TYPE == self.kind { + T::read_block(&mut self.inner) + } else { + Err(Error::BoxNotFound(T::TYPE)) + } + } +} + +pub trait Reader<'a> { + fn take(&mut self, size: usize) -> Result + '_>; + fn remaining(&self) -> usize; + fn skip(&mut self, size: usize); + + fn get_u8(&mut self) -> u8; + fn get_u16(&mut self) -> u16; + fn get_u24(&mut self) -> u32; + fn get_u32(&mut self) -> u32; + fn get_u48(&mut self) -> u64; + fn get_u64(&mut self) -> u64; + + fn get_i8(&mut self) -> i8; + fn get_i16(&mut self) -> i16; + fn get_i24(&mut self) -> i32; + fn get_i32(&mut self) -> i32; + fn get_i48(&mut self) -> i64; + fn get_i64(&mut self) -> i64; + + #[inline] + fn try_get_u8(&mut self) -> Result { + if self.remaining() < 1 { + Err(Error::InvalidData("expected at least 1 byte more")) + } else { + Ok(self.get_u8()) + } + } + #[inline] + fn try_get_u16(&mut self) -> Result { + if self.remaining() < 2 { + Err(Error::InvalidData("expected at least 2 byte more")) + } else { + Ok(self.get_u16()) + } + } + #[inline] + fn try_get_u24(&mut self) -> Result { + if self.remaining() < 3 { + Err(Error::InvalidData("expected at least 3 byte more")) + } else { + Ok(self.get_u24()) + } + } + #[inline] + fn try_get_u32(&mut self) -> Result { + if self.remaining() < 4 { + Err(Error::InvalidData("expected at least 4 byte more")) + } else { + Ok(self.get_u32()) + } + } + #[inline] + fn try_get_u48(&mut self) -> Result { + if self.remaining() < 6 { + Err(Error::InvalidData("expected at least 6 byte more")) + } else { + Ok(self.get_u48()) + } + } + #[inline] + fn try_get_u64(&mut self) -> Result { + if self.remaining() < 8 { + Err(Error::InvalidData("expected at least 8 byte more")) + } else { + Ok(self.get_u64()) + } + } + + #[inline] + fn try_get_i8(&mut self) -> Result { + if self.remaining() < 1 { + Err(Error::InvalidData("expected at least 1 byte more")) + } else { + Ok(self.get_i8()) + } + } + #[inline] + fn try_get_i16(&mut self) -> Result { + if self.remaining() < 2 { + Err(Error::InvalidData("expected at least 2 byte more")) + } else { + Ok(self.get_i16()) + } + } + #[inline] + fn try_get_i24(&mut self) -> Result { + if self.remaining() < 3 { + Err(Error::InvalidData("expected at least 3 byte more")) + } else { + Ok(self.get_i24()) + } + } + #[inline] + fn try_get_i32(&mut self) -> Result { + if self.remaining() < 4 { + Err(Error::InvalidData("expected at least 4 byte more")) + } else { + Ok(self.get_i32()) + } + } + #[inline] + fn try_get_i48(&mut self) -> Result { + if self.remaining() < 6 { + Err(Error::InvalidData("expected at least 6 byte more")) + } else { + Ok(self.get_i48()) + } + } + #[inline] + fn try_get_i64(&mut self) -> Result { + if self.remaining() < 8 { + Err(Error::InvalidData("expected at least 8 byte more")) + } else { + Ok(self.get_i64()) + } + } + fn get_null_terminated_string(&mut self) -> String; + fn collect(&mut self, size: usize) -> Result> { + let mut buf = vec![0; size]; + self.copy_to_slice(&mut buf)?; + + Ok(buf) + } + + #[inline] + fn collect_remaining(&mut self) -> Vec { + self.collect(self.remaining()).unwrap() + } + + fn copy_to_slice(&mut self, slice: &mut [u8]) -> Result<()>; + fn get_box(&mut self) -> Result + '_>>>; + + fn find_box(&mut self) -> Result { + self.try_find_box() + .and_then(|x| x.ok_or_else(|| Error::InvalidData("expected box"))) + } + + fn try_find_box2( + &mut self, + ) -> Result<(Option, Option)> { + let mut a = None; + let mut b = None; + + while let Some(mut bx) = self.get_box()? { + if a.is_none() { + if let Some(inner) = bx.try_read::()? { + a = Some(inner); + continue; + } + } + + if b.is_none() { + if let Some(inner) = bx.try_read::()? { + b = Some(inner); + continue; + } + } + + println!(" 1 unknown box {}", bx.kind); + } + + Ok((a, b)) + } + + fn try_find_box3(&mut self) -> Result<(Option, Option, Option)> + where + A: Mp4Box + BlockReader, + B: Mp4Box + BlockReader, + C: Mp4Box + BlockReader, + { + let mut a = None; + let mut b = None; + let mut c = None; + + while let Some(mut bx) = self.get_box()? { + if a.is_none() { + if let Some(inner) = bx.try_read::()? { + a = Some(inner); + continue; + } + } + if b.is_none() { + if let Some(inner) = bx.try_read::()? { + b = Some(inner); + continue; + } + } + + if c.is_none() { + if let Some(inner) = bx.try_read::()? { + c = Some(inner); + continue; + } + } + + println!(" 2 unknown box {}", bx.kind); + } + + Ok((a, b, c)) + } + + #[inline] + fn find_box3(&mut self) -> Result<(A, B, C)> + where + A: Mp4Box + BlockReader, + B: Mp4Box + BlockReader, + C: Mp4Box + BlockReader, + { + let (a, b, c) = self.try_find_box3()?; + + let Some(a) = a else { + return Err(Error::BoxNotFound(A::TYPE)); + }; + + let Some(b) = b else { + return Err(Error::BoxNotFound(B::TYPE)); + }; + + let Some(c) = c else { + return Err(Error::BoxNotFound(C::TYPE)); + }; + + Ok((a, b, c)) + } + + fn try_find_box4(&mut self) -> Result<(Option, Option, Option, Option)> + where + A: Mp4Box + BlockReader, + B: Mp4Box + BlockReader, + C: Mp4Box + BlockReader, + D: Mp4Box + BlockReader, + { + let mut a = None; + let mut b = None; + let mut c = None; + let mut d = None; + + while let Some(mut bx) = self.get_box()? { + if a.is_none() { + if let Some(inner) = bx.try_read::()? { + a = Some(inner); + continue; + } + } + + if b.is_none() { + if let Some(inner) = bx.try_read::()? { + b = Some(inner); + continue; + } + } + + if c.is_none() { + if let Some(inner) = bx.try_read::()? { + c = Some(inner); + continue; + } + } + + if d.is_none() { + if let Some(inner) = bx.try_read::()? { + d = Some(inner); + continue; + } + } + + println!(" 3 unknown box {}", bx.kind); + } + + Ok((a, b, c, d)) + } + + #[inline] + fn try_find_box(&mut self) -> Result> { + while let Some(mut bx) = self.get_box()? { + if let Some(inner) = bx.try_read::()? { + return Ok(Some(inner)); + } + + println!(" 4 unknown box {}", bx.kind); + } + + Ok(None) + } +} + +impl<'a> Reader<'a> for &'a [u8] { + #[inline] + fn take(&mut self, size: usize) -> Result + '_> { + if self.len() < size { + return Err(Error::InvalidData("no bytes left")); + } + + let buff = &(*self)[0..size]; + self.advance(size); + + Ok(buff) + } + + #[inline] + fn skip(&mut self, size: usize) { + Buf::advance(self, size) + } + + #[inline] + fn remaining(&self) -> usize { + Buf::remaining(self) + } + + #[inline] + fn get_u8(&mut self) -> u8 { + Buf::get_u8(self) + } + + #[inline] + fn get_u16(&mut self) -> u16 { + Buf::get_u16(self) + } + + #[inline] + fn get_u24(&mut self) -> u32 { + let val = BigEndian::read_u24(self.chunk()); + self.skip(3); + val + } + + #[inline] + fn get_u32(&mut self) -> u32 { + Buf::get_u32(self) + } + + #[inline] + fn get_u48(&mut self) -> u64 { + let val = BigEndian::read_u48(self.chunk()); + self.skip(6); + val + } + + #[inline] + fn get_u64(&mut self) -> u64 { + Buf::get_u64(self) + } + + #[inline] + fn get_i8(&mut self) -> i8 { + Buf::get_i8(self) + } + + #[inline] + fn get_i16(&mut self) -> i16 { + Buf::get_i16(self) + } + + #[inline] + fn get_i24(&mut self) -> i32 { + todo!() + } + + #[inline] + fn get_i32(&mut self) -> i32 { + Buf::get_i32(self) + } + + #[inline] + fn get_i48(&mut self) -> i64 { + todo!() + } + + #[inline] + fn get_i64(&mut self) -> i64 { + Buf::get_i64(self) + } + + #[inline] + fn copy_to_slice(&mut self, slice: &mut [u8]) -> Result<()> { + if self.len() < slice.len() { + return Err(Error::InvalidData("expected more bytes")); + } + + Buf::copy_to_slice(self, slice); + + Ok(()) + } + + #[inline] + fn get_null_terminated_string(&mut self) -> String { + let rem = self.len(); + + if rem > 0 { + let size = self.iter().position(|&b| b == b'\0'); + + let (size, eat) = if let Some(size) = size { + (size, size + 1) + } else { + (rem, rem) + }; + + let val = String::from_utf8_lossy(&self[0..size]).to_string(); + self.advance(eat); + val + } else { + String::new() + } + } + + #[inline] + fn get_box(&mut self) -> Result + '_>>> { + let Some(BoxHeader { kind, size }) = BoxHeader::read_sync(self)? else { + return Ok(None); + }; + + Ok(Some(BoxReader { + kind, + inner: Reader::take(self, size as _)?, + m: PhantomData, + })) + } +} + +pub trait BlockReader: Sized { + fn read_block<'a>(block: &mut impl Reader<'a>) -> Result; + fn size_hint() -> usize; } pub trait WriteBox: Sized { @@ -258,72 +716,129 @@ pub trait WriteBox: Sized { #[derive(Debug, Clone, Copy)] pub struct BoxHeader { - pub name: BoxType, + pub kind: BoxType, pub size: u64, } impl BoxHeader { pub fn new(name: BoxType, size: u64) -> Self { - Self { name, size } + Self { kind: name, size } + } + + pub fn read_sync<'a>(reader: &mut impl Reader<'a>) -> Result> { + if reader.remaining() < 8 { + return Ok(None); + } + + let sz = reader.get_u32(); + let typ = reader.get_u32(); + + // Get largesize if size is 1 + let size = if sz == 1 { + if reader.remaining() < 8 { + return Err(Error::InvalidData("expected 8 bytes more")); + } + + let largesize = reader.get_u64(); + // Subtract the length of the serialized largesize, as callers assume `size - HEADER_SIZE` is the length + // of the box data. Disallow `largesize < 16`, or else a largesize of 8 will result in a BoxHeader::size + // of 0, incorrectly indicating that the box data extends to the end of the stream. + match largesize { + 0 => 0, + 1..=15 => return Err(Error::InvalidData("64-bit box size too small")), + 16..=u64::MAX => largesize - 8, + } + } else { + sz as _ + }; + + println!( + "{} box {} {}", + if sz == 1 { "big" } else { "small" }, + BoxType::from(typ).as_str(), + size + ); + + Ok(Some(BoxHeader { + kind: BoxType::from(typ), + size: size.saturating_sub(HEADER_SIZE), + })) } // TODO: if size is 0, then this box is the last one in the file - pub fn read(reader: &mut R) -> Result { + pub async fn read(reader: &mut R) -> Result> { // Create and read to buf. let mut buf = [0u8; 8]; // 8 bytes for box header. - reader.read_exact(&mut buf)?; + match reader.read_exact(&mut buf).await { + Ok(_) => (), + Err(err) => match err.kind() { + std::io::ErrorKind::UnexpectedEof => return Ok(None), + _ => return Err(err.into()), + }, + } // Get size. let s = buf[0..4].try_into().unwrap(); - let size = u32::from_be_bytes(s); + let sz = u32::from_be_bytes(s); // Get box type string. let t = buf[4..8].try_into().unwrap(); let typ = u32::from_be_bytes(t); // Get largesize if size is 1 - if size == 1 { - reader.read_exact(&mut buf)?; + let size = if sz == 1 { + match reader.read_exact(&mut buf).await { + Ok(_) => (), + Err(err) => match err.kind() { + std::io::ErrorKind::UnexpectedEof => return Ok(None), + _ => return Err(err.into()), + }, + } + let largesize = u64::from_be_bytes(buf); - Ok(BoxHeader { - name: BoxType::from(typ), - - // Subtract the length of the serialized largesize, as callers assume `size - HEADER_SIZE` is the length - // of the box data. Disallow `largesize < 16`, or else a largesize of 8 will result in a BoxHeader::size - // of 0, incorrectly indicating that the box data extends to the end of the stream. - size: match largesize { - 0 => 0, - 1..=15 => return Err(Error::InvalidData("64-bit box size too small")), - 16..=u64::MAX => largesize - 8, - }, - }) + // Subtract the length of the serialized largesize, as callers assume `size - HEADER_SIZE` is the length + // of the box data. Disallow `largesize < 16`, or else a largesize of 8 will result in a BoxHeader::size + // of 0, incorrectly indicating that the box data extends to the end of the stream. + match largesize { + 0 => 0, + 1..=15 => return Err(Error::InvalidData("64-bit box size too small")), + 16..=u64::MAX => largesize - 8, + } } else { - Ok(BoxHeader { - name: BoxType::from(typ), - size: size as u64, - }) - } + sz as _ + }; + + println!( + "{} box {} {}", + if sz == 1 { "big" } else { "small" }, + BoxType::from(typ).as_str(), + size + ); + + Ok(Some(BoxHeader { + kind: BoxType::from(typ), + size: size.saturating_sub(HEADER_SIZE), + })) } pub fn write(&self, writer: &mut W) -> Result { if self.size > u32::MAX as u64 { writer.write_u32::(1)?; - writer.write_u32::(self.name.into())?; - writer.write_u64::(self.size)?; + writer.write_u32::(self.kind.into())?; + writer.write_u64::(self.size + HEADER_SIZE)?; Ok(16) } else { - writer.write_u32::(self.size as u32)?; - writer.write_u32::(self.name.into())?; + writer.write_u32::((self.size + HEADER_SIZE) as u32)?; + writer.write_u32::(self.kind.into())?; Ok(8) } } } -pub fn read_box_header_ext(reader: &mut R) -> Result<(u8, u32)> { - let version = reader.read_u8()?; - let flags = reader.read_u24::()?; - Ok((version, flags)) +#[inline] +pub fn read_box_header_ext<'a, R: Reader<'a>>(reader: &mut R) -> (u8, u32) { + (reader.get_u8(), reader.get_u24()) } pub fn write_box_header_ext(w: &mut W, v: u8, f: u32) -> Result { @@ -332,26 +847,6 @@ pub fn write_box_header_ext(w: &mut W, v: u8, f: u32) -> Result { Ok(4) } -pub fn box_start(seeker: &mut R) -> Result { - Ok(seeker.stream_position()? - HEADER_SIZE) -} - -pub fn skip_bytes(seeker: &mut S, size: u64) -> Result<()> { - seeker.seek(SeekFrom::Current(size as i64))?; - Ok(()) -} - -pub fn skip_bytes_to(seeker: &mut S, pos: u64) -> Result<()> { - seeker.seek(SeekFrom::Start(pos))?; - Ok(()) -} - -pub fn skip_box(seeker: &mut S, size: u64) -> Result<()> { - let start = box_start(seeker)?; - skip_bytes_to(seeker, start + size)?; - Ok(()) -} - pub fn write_zeros(writer: &mut W, size: u64) -> Result<()> { for _ in 0..size { writer.write_u8(0)?; @@ -410,25 +905,29 @@ mod tests { #[test] fn test_largesize_too_small() { - let error = BoxHeader::read(&mut &[0, 0, 0, 1, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 7][..]); + let error = + BoxHeader::read_sync(&mut &[0, 0, 0, 1, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 7][..]); assert!(matches!(error, Err(Error::InvalidData(_)))); } #[test] fn test_zero_largesize() { - let error = BoxHeader::read(&mut &[0, 0, 0, 1, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 8][..]); + let error = + BoxHeader::read_sync(&mut &[0, 0, 0, 1, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 8][..]); assert!(matches!(error, Err(Error::InvalidData(_)))); } #[test] fn test_nonzero_largesize_too_small() { - let error = BoxHeader::read(&mut &[0, 0, 0, 1, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 15][..]); + let error = + BoxHeader::read_sync(&mut &[0, 0, 0, 1, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 15][..]); assert!(matches!(error, Err(Error::InvalidData(_)))); } #[test] fn test_valid_largesize() { - let header = BoxHeader::read(&mut &[0, 0, 0, 1, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 16][..]); - assert!(matches!(header, Ok(BoxHeader { size: 8, .. }))); + let header = + BoxHeader::read_sync(&mut &[0, 0, 0, 1, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 16][..]); + assert!(matches!(header, Ok(Some(BoxHeader { size: 8, .. })))); } } diff --git a/src/mp4box/moof.rs b/src/mp4box/moof.rs index 20c3565..6206e9a 100644 --- a/src/mp4box/moof.rs +++ b/src/mp4box/moof.rs @@ -1,5 +1,5 @@ use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use crate::mp4box::*; use crate::mp4box::{mfhd::MfhdBox, traf::TrafBox}; @@ -27,9 +27,7 @@ impl MoofBox { } impl Mp4Box for MoofBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::MoofBox; fn box_size(&self) -> u64 { self.get_size() @@ -45,58 +43,44 @@ impl Mp4Box for MoofBox { } } -impl ReadBox<&mut R> for MoofBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; - +impl BlockReader for MoofBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { let mut mfhd = None; let mut trafs = Vec::new(); - let mut current = reader.stream_position()?; - let end = start + size; - while current < end { - // Get box header. - let header = BoxHeader::read(reader)?; - let BoxHeader { name, size: s } = header; - if s > size { - return Err(Error::InvalidData( - "moof box contains a box with a larger size than it", - )); - } - - match name { + while let Some(mut bx) = reader.get_box()? { + match bx.kind { BoxType::MfhdBox => { - mfhd = Some(MfhdBox::read_box(reader, s)?); + mfhd = Some(bx.read()?); } + BoxType::TrafBox => { - let traf = TrafBox::read_box(reader, s)?; - trafs.push(traf); - } - _ => { - // XXX warn!() - skip_box(reader, s)?; + trafs.push(bx.read()?); } + + _ => continue, } - current = reader.stream_position()?; } if mfhd.is_none() { return Err(Error::BoxNotFound(BoxType::MfhdBox)); } - skip_bytes_to(reader, start + size)?; - Ok(MoofBox { mfhd: mfhd.unwrap(), trafs, }) } + + fn size_hint() -> usize { + MfhdBox::size_hint() + } } impl WriteBox<&mut W> for MoofBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; self.mfhd.write_box(writer)?; for traf in self.trafs.iter() { diff --git a/src/mp4box/moov.rs b/src/mp4box/moov.rs index ac19381..1e2ff9b 100644 --- a/src/mp4box/moov.rs +++ b/src/mp4box/moov.rs @@ -1,5 +1,5 @@ use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use crate::meta::MetaBox; use crate::mp4box::*; @@ -43,9 +43,7 @@ impl MoovBox { } impl Mp4Box for MoovBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::MoovBox; fn box_size(&self) -> u64 { self.get_size() @@ -61,9 +59,9 @@ impl Mp4Box for MoovBox { } } -impl ReadBox<&mut R> for MoovBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; +impl BlockReader for MoovBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + println!("begin reading moov"); let mut mvhd = None; let mut meta = None; @@ -71,50 +69,37 @@ impl ReadBox<&mut R> for MoovBox { let mut mvex = None; let mut traks = Vec::new(); - let mut current = reader.stream_position()?; - let end = start + size; - while current < end { - // Get box header. - let header = BoxHeader::read(reader)?; - let BoxHeader { name, size: s } = header; - if s > size { - return Err(Error::InvalidData( - "moov box contains a box with a larger size than it", - )); - } - - match name { + while let Some(mut bx) = reader.get_box()? { + match bx.kind { BoxType::MvhdBox => { - mvhd = Some(MvhdBox::read_box(reader, s)?); + mvhd = Some(bx.read()?); } - BoxType::MetaBox => { - meta = Some(MetaBox::read_box(reader, s)?); - } - BoxType::MvexBox => { - mvex = Some(MvexBox::read_box(reader, s)?); - } - BoxType::TrakBox => { - let trak = TrakBox::read_box(reader, s)?; - traks.push(trak); - } - BoxType::UdtaBox => { - udta = Some(UdtaBox::read_box(reader, s)?); - } - _ => { - // XXX warn!() - skip_box(reader, s)?; - } - } - current = reader.stream_position()?; + BoxType::MetaBox => { + meta = Some(bx.read()?); + } + + BoxType::MvexBox => { + mvex = Some(bx.read()?); + } + + BoxType::TrakBox => { + traks.push(bx.read()?); + } + + BoxType::UdtaBox => { + udta = Some(bx.read()?); + } + + _ => continue, + } } if mvhd.is_none() { return Err(Error::BoxNotFound(BoxType::MvhdBox)); } - skip_bytes_to(reader, start + size)?; - + println!("end reading moov"); Ok(MoovBox { mvhd: mvhd.unwrap(), meta, @@ -123,12 +108,16 @@ impl ReadBox<&mut R> for MoovBox { traks, }) } + + fn size_hint() -> usize { + MvhdBox::size_hint() + } } impl WriteBox<&mut W> for MoovBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; self.mvhd.write_box(writer)?; for trak in self.traks.iter() { @@ -148,7 +137,6 @@ impl WriteBox<&mut W> for MoovBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_moov() { @@ -164,12 +152,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::MoovBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::MoovBox); assert_eq!(header.size, src_box.box_size()); - let dst_box = MoovBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = MoovBox::read_block(&mut reader).unwrap(); assert_eq!(dst_box, src_box); } @@ -181,12 +169,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::MoovBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::MoovBox); assert_eq!(header.size, src_box.box_size()); - let dst_box = MoovBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = MoovBox::read_block(&mut reader).unwrap(); assert_eq!(dst_box, src_box); } } diff --git a/src/mp4box/mp4a.rs b/src/mp4box/mp4a.rs index a80c6c4..0f772f7 100644 --- a/src/mp4box/mp4a.rs +++ b/src/mp4box/mp4a.rs @@ -1,6 +1,6 @@ -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{BigEndian, WriteBytesExt}; use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use crate::mp4box::*; @@ -52,9 +52,7 @@ impl Mp4aBox { } impl Mp4Box for Mp4aBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::Mp4aBox; fn box_size(&self) -> u64 { self.get_size() @@ -75,70 +73,52 @@ impl Mp4Box for Mp4aBox { } } -impl ReadBox<&mut R> for Mp4aBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; +impl BlockReader for Mp4aBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + reader.get_u32(); // reserved + reader.get_u16(); // reserved - reader.read_u32::()?; // reserved - reader.read_u16::()?; // reserved - let data_reference_index = reader.read_u16::()?; - let version = reader.read_u16::()?; - reader.read_u16::()?; // reserved - reader.read_u32::()?; // reserved - let channelcount = reader.read_u16::()?; - let samplesize = reader.read_u16::()?; - reader.read_u32::()?; // pre-defined, reserved - let samplerate = FixedPointU16::new_raw(reader.read_u32::()?); + let data_reference_index = reader.get_u16(); + let version = reader.get_u16(); + + reader.get_u16(); // reserved + reader.get_u32(); // reserved + + let channelcount = reader.get_u16(); + let samplesize = reader.get_u16(); + + reader.get_u32(); // pre-defined, reserved + + let samplerate = FixedPointU16::new_raw(reader.get_u32()); if version == 1 { + if reader.remaining() < 16 { + return Err(Error::InvalidData("expected at least 16 bytes more")); + } + // Skip QTFF - reader.read_u64::()?; - reader.read_u64::()?; + reader.get_u64(); + reader.get_u64(); } - // Find esds in mp4a or wave - let mut esds = None; - let end = start + size; - loop { - let current = reader.stream_position()?; - if current >= end { - break; - } - let header = BoxHeader::read(reader)?; - let BoxHeader { name, size: s } = header; - if s > size { - return Err(Error::InvalidData( - "mp4a box contains a box with a larger size than it", - )); - } - if name == BoxType::EsdsBox { - esds = Some(EsdsBox::read_box(reader, s)?); - break; - } else if name == BoxType::WaveBox { - // Typically contains frma, mp4a, esds, and a terminator atom - } else { - // Skip boxes - let skip_to = current + s; - skip_bytes_to(reader, skip_to)?; - } - } - - skip_bytes_to(reader, end)?; - Ok(Mp4aBox { data_reference_index, channelcount, samplesize, samplerate, - esds, + esds: reader.try_find_box::()?, }) } + + fn size_hint() -> usize { + 28 + } } impl WriteBox<&mut W> for Mp4aBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; writer.write_u32::(0)?; // reserved writer.write_u16::(0)?; // reserved @@ -176,9 +156,7 @@ impl EsdsBox { } impl Mp4Box for EsdsBox { - fn box_type(&self) -> BoxType { - BoxType::EsdsBox - } + const TYPE: BoxType = BoxType::EsdsBox; fn box_size(&self) -> u64 { HEADER_SIZE @@ -197,45 +175,41 @@ impl Mp4Box for EsdsBox { } } -impl ReadBox<&mut R> for EsdsBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; - - let (version, flags) = read_box_header_ext(reader)?; +impl BlockReader for EsdsBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (version, flags) = read_box_header_ext(reader); let mut es_desc = None; - let mut current = reader.stream_position()?; - let end = start + size; - while current < end { - let (desc_tag, desc_size) = read_desc(reader)?; + while let Some((desc_tag, desc_size)) = read_desc(reader) { match desc_tag { 0x03 => { - es_desc = Some(ESDescriptor::read_desc(reader, desc_size)?); + es_desc = Some(ESDescriptor::read_block(&mut reader.take(desc_size as _)?)?); } _ => break, } - current = reader.stream_position()?; } if es_desc.is_none() { return Err(Error::InvalidData("ESDescriptor not found")); } - skip_bytes_to(reader, start + size)?; - Ok(EsdsBox { version, flags, es_desc: es_desc.unwrap(), }) } + + fn size_hint() -> usize { + 4 + ESDescriptor::size_hint() + } } impl WriteBox<&mut W> for EsdsBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; write_box_header_ext(writer, self.version, self.flags)?; @@ -250,27 +224,24 @@ trait Descriptor: Sized { fn desc_size() -> u32; } -trait ReadDesc: Sized { - fn read_desc(_: T, size: u32) -> Result; -} - trait WriteDesc: Sized { fn write_desc(&self, _: T) -> Result; } -fn read_desc(reader: &mut R) -> Result<(u8, u32)> { - let tag = reader.read_u8()?; +fn read_desc<'a, R: Reader<'a>>(reader: &mut R) -> Option<(u8, u32)> { + let tag = reader.try_get_u8().ok()?; let mut size: u32 = 0; for _ in 0..4 { - let b = reader.read_u8()?; + let b = reader.try_get_u8().ok()?; size = (size << 7) | (b & 0x7F) as u32; + if b & 0x80 == 0 { break; } } - Ok((tag, size)) + Some((tag, size)) } fn size_of_length(size: u32) -> u32 { @@ -335,32 +306,28 @@ impl Descriptor for ESDescriptor { } } -impl ReadDesc<&mut R> for ESDescriptor { - fn read_desc(reader: &mut R, size: u32) -> Result { - let start = reader.stream_position()?; - - let es_id = reader.read_u16::()?; - reader.read_u8()?; // XXX flags must be 0 +impl BlockReader for ESDescriptor { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let es_id = reader.get_u16(); + reader.get_u8(); // XXX flags must be 0 let mut dec_config = None; let mut sl_config = None; - let mut current = reader.stream_position()?; - let end = start + size as u64; - while current < end { - let (desc_tag, desc_size) = read_desc(reader)?; + while let Some((desc_tag, desc_size)) = read_desc(reader) { match desc_tag { 0x04 => { - dec_config = Some(DecoderConfigDescriptor::read_desc(reader, desc_size)?); + let mut rdr = reader.take(desc_size as _)?; + dec_config = Some(DecoderConfigDescriptor::read_block(&mut rdr)?); + rdr.skip(rdr.remaining()); } 0x06 => { - sl_config = Some(SLConfigDescriptor::read_desc(reader, desc_size)?); - } - _ => { - skip_bytes(reader, desc_size as u64)?; + let mut rdr = reader.take(desc_size as _)?; + sl_config = Some(SLConfigDescriptor::read_block(&mut rdr)?); + rdr.skip(rdr.remaining()); } + _ => reader.skip(desc_size as _), } - current = reader.stream_position()?; } Ok(ESDescriptor { @@ -369,6 +336,10 @@ impl ReadDesc<&mut R> for ESDescriptor { sl_config: sl_config.unwrap_or_default(), }) } + + fn size_hint() -> usize { + 3 + } } impl WriteDesc<&mut W> for ESDescriptor { @@ -424,33 +395,28 @@ impl Descriptor for DecoderConfigDescriptor { } } -impl ReadDesc<&mut R> for DecoderConfigDescriptor { - fn read_desc(reader: &mut R, size: u32) -> Result { - let start = reader.stream_position()?; - - let object_type_indication = reader.read_u8()?; - let byte_a = reader.read_u8()?; +impl BlockReader for DecoderConfigDescriptor { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let object_type_indication = reader.get_u8(); + let byte_a = reader.get_u8(); let stream_type = (byte_a & 0xFC) >> 2; let up_stream = byte_a & 0x02; - let buffer_size_db = reader.read_u24::()?; - let max_bitrate = reader.read_u32::()?; - let avg_bitrate = reader.read_u32::()?; + let buffer_size_db = reader.get_u24(); + let max_bitrate = reader.get_u32(); + let avg_bitrate = reader.get_u32(); let mut dec_specific = None; - let mut current = reader.stream_position()?; - let end = start + size as u64; - while current < end { - let (desc_tag, desc_size) = read_desc(reader)?; + while let Some((desc_tag, desc_size)) = read_desc(reader) { match desc_tag { 0x05 => { - dec_specific = Some(DecoderSpecificDescriptor::read_desc(reader, desc_size)?); - } - _ => { - skip_bytes(reader, desc_size as u64)?; + let mut rdr = reader.take(desc_size as _)?; + let r = DecoderSpecificDescriptor::read_block(&mut rdr)?; + rdr.skip(rdr.remaining()); + dec_specific = Some(r); } + _ => reader.skip(desc_size as _), } - current = reader.stream_position()?; } Ok(DecoderConfigDescriptor { @@ -463,6 +429,10 @@ impl ReadDesc<&mut R> for DecoderConfigDescriptor { dec_specific: dec_specific.unwrap_or_default(), }) } + + fn size_hint() -> usize { + 13 + } } impl WriteDesc<&mut W> for DecoderConfigDescriptor { @@ -518,7 +488,7 @@ fn get_audio_object_type(byte_a: u8, byte_b: u8) -> u8 { profile } -fn get_chan_conf( +fn get_chan_conf<'a, R: Reader<'a>>( reader: &mut R, byte_b: u8, freq_index: u8, @@ -527,10 +497,10 @@ fn get_chan_conf( let chan_conf; if freq_index == 15 { // Skip the 24 bit sample rate - let sample_rate = reader.read_u24::()?; + let sample_rate = reader.try_get_u24()?; chan_conf = ((sample_rate >> 4) & 0x0F) as u8; } else if extended_profile { - let byte_c = reader.read_u8()?; + let byte_c = reader.try_get_u8()?; chan_conf = (byte_b & 1) | (byte_c & 0xE0); } else { chan_conf = (byte_b >> 3) & 0x0F; @@ -539,11 +509,12 @@ fn get_chan_conf( Ok(chan_conf) } -impl ReadDesc<&mut R> for DecoderSpecificDescriptor { - fn read_desc(reader: &mut R, _size: u32) -> Result { - let byte_a = reader.read_u8()?; - let byte_b = reader.read_u8()?; +impl BlockReader for DecoderSpecificDescriptor { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let byte_a = reader.get_u8(); + let byte_b = reader.get_u8(); let profile = get_audio_object_type(byte_a, byte_b); + let freq_index; let chan_conf; if profile > 31 { @@ -560,6 +531,10 @@ impl ReadDesc<&mut R> for DecoderSpecificDescriptor { chan_conf, }) } + + fn size_hint() -> usize { + 2 + } } impl WriteDesc<&mut W> for DecoderSpecificDescriptor { @@ -593,12 +568,16 @@ impl Descriptor for SLConfigDescriptor { } } -impl ReadDesc<&mut R> for SLConfigDescriptor { - fn read_desc(reader: &mut R, _size: u32) -> Result { - reader.read_u8()?; // pre-defined +impl BlockReader for SLConfigDescriptor { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + reader.get_u8(); // pre-defined Ok(SLConfigDescriptor {}) } + + fn size_hint() -> usize { + 1 + } } impl WriteDesc<&mut W> for SLConfigDescriptor { @@ -615,7 +594,6 @@ impl WriteDesc<&mut W> for SLConfigDescriptor { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_mp4a() { @@ -650,12 +628,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::Mp4aBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::Mp4aBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = Mp4aBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = Mp4aBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } @@ -672,12 +650,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::Mp4aBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::Mp4aBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = Mp4aBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = Mp4aBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/mp4box/mvex.rs b/src/mp4box/mvex.rs index 8be683b..2ab5128 100644 --- a/src/mp4box/mvex.rs +++ b/src/mp4box/mvex.rs @@ -1,5 +1,5 @@ use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use crate::mp4box::*; use crate::mp4box::{mehd::MehdBox, trex::TrexBox}; @@ -21,9 +21,7 @@ impl MvexBox { } impl Mp4Box for MvexBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::MvexBox; fn box_size(&self) -> u64 { self.get_size() @@ -39,62 +37,34 @@ impl Mp4Box for MvexBox { } } -impl ReadBox<&mut R> for MvexBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; - - let mut mehd = None; - let mut trex = None; - - let mut current = reader.stream_position()?; - let end = start + size; - while current < end { - // Get box header. - let header = BoxHeader::read(reader)?; - let BoxHeader { name, size: s } = header; - if s > size { - return Err(Error::InvalidData( - "mvex box contains a box with a larger size than it", - )); - } - - match name { - BoxType::MehdBox => { - mehd = Some(MehdBox::read_box(reader, s)?); - } - BoxType::TrexBox => { - trex = Some(TrexBox::read_box(reader, s)?); - } - _ => { - // XXX warn!() - skip_box(reader, s)?; - } - } - - current = reader.stream_position()?; - } +impl BlockReader for MvexBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (mehd, trex) = reader.try_find_box2::()?; if trex.is_none() { return Err(Error::BoxNotFound(BoxType::TrexBox)); } - skip_bytes_to(reader, start + size)?; - Ok(MvexBox { mehd, trex: trex.unwrap(), }) } + + fn size_hint() -> usize { + TrexBox::size_hint() + } } impl WriteBox<&mut W> for MvexBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; if let Some(mehd) = &self.mehd { mehd.write_box(writer)?; } + self.trex.write_box(writer)?; Ok(size) diff --git a/src/mp4box/mvhd.rs b/src/mp4box/mvhd.rs index 462a29b..59fa45e 100644 --- a/src/mp4box/mvhd.rs +++ b/src/mp4box/mvhd.rs @@ -1,6 +1,6 @@ -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{BigEndian, WriteBytesExt}; use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use crate::mp4box::*; @@ -58,9 +58,7 @@ impl Default for MvhdBox { } impl Mp4Box for MvhdBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::MvhdBox; fn box_size(&self) -> u64 { self.get_size() @@ -85,54 +83,53 @@ impl Mp4Box for MvhdBox { } } -impl ReadBox<&mut R> for MvhdBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; - - let (version, flags) = read_box_header_ext(reader)?; +impl BlockReader for MvhdBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (version, flags) = read_box_header_ext(reader); let (creation_time, modification_time, timescale, duration) = if version == 1 { + if reader.remaining() < Self::size_hint() - 4 + 12 { + return Err(Error::InvalidData("expected more bytes")); + } + ( - reader.read_u64::()?, - reader.read_u64::()?, - reader.read_u32::()?, - reader.read_u64::()?, + reader.get_u64(), + reader.get_u64(), + reader.get_u32(), + reader.get_u64(), ) } else if version == 0 { ( - reader.read_u32::()? as u64, - reader.read_u32::()? as u64, - reader.read_u32::()?, - reader.read_u32::()? as u64, + reader.get_u32() as u64, + reader.get_u32() as u64, + reader.get_u32(), + reader.get_u32() as u64, ) } else { return Err(Error::InvalidData("version must be 0 or 1")); }; - let rate = FixedPointU16::new_raw(reader.read_u32::()?); - let volume = FixedPointU8::new_raw(reader.read_u16::()?); + let rate = FixedPointU16::new_raw(reader.get_u32()); + let volume = FixedPointU8::new_raw(reader.get_u16()); - reader.read_u16::()?; // reserved = 0 - - reader.read_u64::()?; // reserved = 0 + reader.get_u16(); // reserved = 0 + reader.get_u64(); // reserved = 0 let matrix = tkhd::Matrix { - a: reader.read_i32::()?, - b: reader.read_i32::()?, - u: reader.read_i32::()?, - c: reader.read_i32::()?, - d: reader.read_i32::()?, - v: reader.read_i32::()?, - x: reader.read_i32::()?, - y: reader.read_i32::()?, - w: reader.read_i32::()?, + a: reader.get_i32(), + b: reader.get_i32(), + u: reader.get_i32(), + c: reader.get_i32(), + d: reader.get_i32(), + v: reader.get_i32(), + x: reader.get_i32(), + y: reader.get_i32(), + w: reader.get_i32(), }; - skip_bytes(reader, 24)?; // pre_defined = 0 + reader.skip(24); - let next_track_id = reader.read_u32::()?; - - skip_bytes_to(reader, start + size)?; + let next_track_id = reader.get_u32(); Ok(MvhdBox { version, @@ -147,12 +144,16 @@ impl ReadBox<&mut R> for MvhdBox { next_track_id, }) } + + fn size_hint() -> usize { + 100 + } } impl WriteBox<&mut W> for MvhdBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; write_box_header_ext(writer, self.version, self.flags)?; @@ -199,7 +200,6 @@ impl WriteBox<&mut W> for MvhdBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_mvhd32() { @@ -219,12 +219,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::MvhdBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::MvhdBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = MvhdBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = MvhdBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } @@ -246,12 +246,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::MvhdBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::MvhdBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = MvhdBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = MvhdBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/mp4box/smhd.rs b/src/mp4box/smhd.rs index cab7e4b..3f67882 100644 --- a/src/mp4box/smhd.rs +++ b/src/mp4box/smhd.rs @@ -1,6 +1,6 @@ -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{BigEndian, WriteBytesExt}; use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use crate::mp4box::*; @@ -34,9 +34,7 @@ impl Default for SmhdBox { } impl Mp4Box for SmhdBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::SmhdBox; fn box_size(&self) -> u64 { self.get_size() @@ -52,28 +50,26 @@ impl Mp4Box for SmhdBox { } } -impl ReadBox<&mut R> for SmhdBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; - - let (version, flags) = read_box_header_ext(reader)?; - - let balance = FixedPointI8::new_raw(reader.read_i16::()?); - - skip_bytes_to(reader, start + size)?; +impl BlockReader for SmhdBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (version, flags) = read_box_header_ext(reader); Ok(SmhdBox { version, flags, - balance, + balance: FixedPointI8::new_raw(reader.get_i16()), }) } + + fn size_hint() -> usize { + 6 + } } impl WriteBox<&mut W> for SmhdBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; write_box_header_ext(writer, self.version, self.flags)?; @@ -88,7 +84,6 @@ impl WriteBox<&mut W> for SmhdBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_smhd() { @@ -101,12 +96,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::SmhdBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::SmhdBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = SmhdBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = SmhdBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/mp4box/stbl.rs b/src/mp4box/stbl.rs index ef8433b..94ae29b 100644 --- a/src/mp4box/stbl.rs +++ b/src/mp4box/stbl.rs @@ -1,5 +1,5 @@ use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use crate::mp4box::*; use crate::mp4box::{ @@ -55,9 +55,7 @@ impl StblBox { } impl Mp4Box for StblBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::StblBox; fn box_size(&self) -> u64 { self.get_size() @@ -73,10 +71,8 @@ impl Mp4Box for StblBox { } } -impl ReadBox<&mut R> for StblBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; - +impl BlockReader for StblBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { let mut stsd = None; let mut stts = None; let mut ctts = None; @@ -86,69 +82,64 @@ impl ReadBox<&mut R> for StblBox { let mut stco = None; let mut co64 = None; - let mut current = reader.stream_position()?; - let end = start + size; - while current < end { - // Get box header. - let header = BoxHeader::read(reader)?; - let BoxHeader { name, size: s } = header; - if s > size { - return Err(Error::InvalidData( - "stbl box contains a box with a larger size than it", - )); - } - - match name { + while let Some(mut bx) = reader.get_box()? { + match bx.kind { BoxType::StsdBox => { - stsd = Some(StsdBox::read_box(reader, s)?); + stsd = Some(bx.read()?); } + BoxType::SttsBox => { - stts = Some(SttsBox::read_box(reader, s)?); + stts = Some(bx.read()?); } + BoxType::CttsBox => { - ctts = Some(CttsBox::read_box(reader, s)?); + ctts = Some(bx.read()?); } + BoxType::StssBox => { - stss = Some(StssBox::read_box(reader, s)?); + stss = Some(bx.read()?); } + BoxType::StscBox => { - stsc = Some(StscBox::read_box(reader, s)?); + stsc = Some(bx.read()?); } + BoxType::StszBox => { - stsz = Some(StszBox::read_box(reader, s)?); + stsz = Some(bx.read()?); } + BoxType::StcoBox => { - stco = Some(StcoBox::read_box(reader, s)?); + stco = Some(bx.read()?); } + BoxType::Co64Box => { - co64 = Some(Co64Box::read_box(reader, s)?); - } - _ => { - // XXX warn!() - skip_box(reader, s)?; + co64 = Some(bx.read()?); } + + _ => continue, } - current = reader.stream_position()?; } if stsd.is_none() { return Err(Error::BoxNotFound(BoxType::StsdBox)); } + if stts.is_none() { return Err(Error::BoxNotFound(BoxType::SttsBox)); } + if stsc.is_none() { return Err(Error::BoxNotFound(BoxType::StscBox)); } + if stsz.is_none() { return Err(Error::BoxNotFound(BoxType::StszBox)); } + if stco.is_none() && co64.is_none() { return Err(Error::Box2NotFound(BoxType::StcoBox, BoxType::Co64Box)); } - skip_bytes_to(reader, start + size)?; - Ok(StblBox { stsd: stsd.unwrap(), stts: stts.unwrap(), @@ -160,12 +151,16 @@ impl ReadBox<&mut R> for StblBox { co64, }) } + + fn size_hint() -> usize { + StsdBox::size_hint() + SttsBox::size_hint() + StscBox::size_hint() + StszBox::size_hint() + } } impl WriteBox<&mut W> for StblBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; self.stsd.write_box(writer)?; self.stts.write_box(writer)?; diff --git a/src/mp4box/stco.rs b/src/mp4box/stco.rs index a00da8f..ae63803 100644 --- a/src/mp4box/stco.rs +++ b/src/mp4box/stco.rs @@ -1,6 +1,6 @@ -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{BigEndian, WriteBytesExt}; use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use std::mem::size_of; use crate::mp4box::*; @@ -25,9 +25,7 @@ impl StcoBox { } impl Mp4Box for StcoBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::StcoBox; fn box_size(&self) -> u64 { self.get_size() @@ -43,46 +41,40 @@ impl Mp4Box for StcoBox { } } -impl ReadBox<&mut R> for StcoBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; +impl BlockReader for StcoBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (version, flags) = read_box_header_ext(reader); - let (version, flags) = read_box_header_ext(reader)?; - - let header_size = HEADER_SIZE + HEADER_EXT_SIZE; - let other_size = size_of::(); // entry_count let entry_size = size_of::(); // chunk_offset - let entry_count = reader.read_u32::()?; - if u64::from(entry_count) - > size - .saturating_sub(header_size) - .saturating_sub(other_size as u64) - / entry_size as u64 - { + let entry_count = reader.get_u32(); + if entry_count as usize > reader.remaining() / entry_size { return Err(Error::InvalidData( "stco entry_count indicates more entries than could fit in the box", )); } + let mut entries = Vec::with_capacity(entry_count as usize); for _i in 0..entry_count { - let chunk_offset = reader.read_u32::()?; + let chunk_offset = reader.get_u32(); entries.push(chunk_offset); } - skip_bytes_to(reader, start + size)?; - Ok(StcoBox { version, flags, entries, }) } + + fn size_hint() -> usize { + 8 + } } impl WriteBox<&mut W> for StcoBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; write_box_header_ext(writer, self.version, self.flags)?; @@ -117,7 +109,6 @@ impl std::convert::TryFrom<&co64::Co64Box> for StcoBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_stco() { @@ -130,12 +121,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::StcoBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::StcoBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = StcoBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = StcoBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/mp4box/stsc.rs b/src/mp4box/stsc.rs index a2b034b..8c53e88 100644 --- a/src/mp4box/stsc.rs +++ b/src/mp4box/stsc.rs @@ -1,6 +1,6 @@ -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{BigEndian, WriteBytesExt}; use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use std::mem::size_of; use crate::mp4box::*; @@ -33,9 +33,7 @@ pub struct StscEntry { } impl Mp4Box for StscBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::StscBox; fn box_size(&self) -> u64 { self.get_size() @@ -51,22 +49,13 @@ impl Mp4Box for StscBox { } } -impl ReadBox<&mut R> for StscBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; +impl BlockReader for StscBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (version, flags) = read_box_header_ext(reader); - let (version, flags) = read_box_header_ext(reader)?; - - let header_size = HEADER_SIZE + HEADER_EXT_SIZE; - let other_size = size_of::(); // entry_count let entry_size = size_of::() + size_of::() + size_of::(); // first_chunk + samples_per_chunk + sample_description_index - let entry_count = reader.read_u32::()?; - if u64::from(entry_count) - > size - .saturating_sub(header_size) - .saturating_sub(other_size as u64) - / entry_size as u64 - { + let entry_count = reader.get_u32(); + if entry_count as usize > reader.remaining() / entry_size { return Err(Error::InvalidData( "stsc entry_count indicates more entries than could fit in the box", )); @@ -74,9 +63,9 @@ impl ReadBox<&mut R> for StscBox { let mut entries = Vec::with_capacity(entry_count as usize); for _ in 0..entry_count { let entry = StscEntry { - first_chunk: reader.read_u32::()?, - samples_per_chunk: reader.read_u32::()?, - sample_description_index: reader.read_u32::()?, + first_chunk: reader.get_u32(), + samples_per_chunk: reader.get_u32(), + sample_description_index: reader.get_u32(), first_sample: 0, }; entries.push(entry); @@ -102,20 +91,22 @@ impl ReadBox<&mut R> for StscBox { } } - skip_bytes_to(reader, start + size)?; - Ok(StscBox { version, flags, entries, }) } + + fn size_hint() -> usize { + 8 + } } impl WriteBox<&mut W> for StscBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; write_box_header_ext(writer, self.version, self.flags)?; @@ -134,7 +125,6 @@ impl WriteBox<&mut W> for StscBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_stsc() { @@ -160,12 +150,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::StscBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::StscBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = StscBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = StscBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/mp4box/stsd.rs b/src/mp4box/stsd.rs index af947c6..91e0966 100644 --- a/src/mp4box/stsd.rs +++ b/src/mp4box/stsd.rs @@ -1,6 +1,6 @@ -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{BigEndian, WriteBytesExt}; use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use crate::mp4box::vp09::Vp09Box; use crate::mp4box::*; @@ -45,14 +45,13 @@ impl StsdBox { } else if let Some(ref tx3g) = self.tx3g { size += tx3g.box_size(); } + size } } impl Mp4Box for StsdBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::StsdBox; fn box_size(&self) -> u64 { self.get_size() @@ -68,13 +67,11 @@ impl Mp4Box for StsdBox { } } -impl ReadBox<&mut R> for StsdBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; +impl BlockReader for StsdBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (version, flags) = read_box_header_ext(reader); - let (version, flags) = read_box_header_ext(reader)?; - - reader.read_u32::()?; // XXX entry_count + reader.get_u32(); // XXX entry_count let mut avc1 = None; let mut hev1 = None; @@ -82,35 +79,31 @@ impl ReadBox<&mut R> for StsdBox { let mut mp4a = None; let mut tx3g = None; - // Get box header. - let header = BoxHeader::read(reader)?; - let BoxHeader { name, size: s } = header; - if s > size { - return Err(Error::InvalidData( - "stsd box contains a box with a larger size than it", - )); - } + while let Some(mut bx) = reader.get_box()? { + match bx.kind { + BoxType::Avc1Box => { + avc1 = Some(bx.read()?); + } - match name { - BoxType::Avc1Box => { - avc1 = Some(Avc1Box::read_box(reader, s)?); - } - BoxType::Hev1Box => { - hev1 = Some(Hev1Box::read_box(reader, s)?); - } - BoxType::Vp09Box => { - vp09 = Some(Vp09Box::read_box(reader, s)?); - } - BoxType::Mp4aBox => { - mp4a = Some(Mp4aBox::read_box(reader, s)?); - } - BoxType::Tx3gBox => { - tx3g = Some(Tx3gBox::read_box(reader, s)?); - } - _ => {} - } + BoxType::Hev1Box => { + hev1 = Some(bx.read()?); + } - skip_bytes_to(reader, start + size)?; + BoxType::Vp09Box => { + vp09 = Some(bx.read()?); + } + + BoxType::Mp4aBox => { + mp4a = Some(bx.read()?); + } + + BoxType::Tx3gBox => { + tx3g = Some(bx.read()?); + } + + _ => {} + } + } Ok(StsdBox { version, @@ -122,12 +115,16 @@ impl ReadBox<&mut R> for StsdBox { tx3g, }) } + + fn size_hint() -> usize { + 8 + } } impl WriteBox<&mut W> for StsdBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; write_box_header_ext(writer, self.version, self.flags)?; diff --git a/src/mp4box/stss.rs b/src/mp4box/stss.rs index dd9e552..9daa61e 100644 --- a/src/mp4box/stss.rs +++ b/src/mp4box/stss.rs @@ -1,6 +1,6 @@ -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{BigEndian, WriteBytesExt}; use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use std::mem::size_of; use crate::mp4box::*; @@ -25,9 +25,7 @@ impl StssBox { } impl Mp4Box for StssBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::StssBox; fn box_size(&self) -> u64 { self.get_size() @@ -43,46 +41,40 @@ impl Mp4Box for StssBox { } } -impl ReadBox<&mut R> for StssBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; +impl BlockReader for StssBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (version, flags) = read_box_header_ext(reader); - let (version, flags) = read_box_header_ext(reader)?; - - let header_size = HEADER_SIZE + HEADER_EXT_SIZE; - let other_size = size_of::(); // entry_count let entry_size = size_of::(); // sample_number - let entry_count = reader.read_u32::()?; - if u64::from(entry_count) - > size - .saturating_sub(header_size) - .saturating_sub(other_size as u64) - / entry_size as u64 - { + let entry_count = reader.get_u32(); + if entry_count as usize > reader.remaining() / entry_size { return Err(Error::InvalidData( "stss entry_count indicates more entries than could fit in the box", )); } + let mut entries = Vec::with_capacity(entry_count as usize); for _i in 0..entry_count { - let sample_number = reader.read_u32::()?; + let sample_number = reader.get_u32(); entries.push(sample_number); } - skip_bytes_to(reader, start + size)?; - Ok(StssBox { version, flags, entries, }) } + + fn size_hint() -> usize { + 8 + } } impl WriteBox<&mut W> for StssBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; write_box_header_ext(writer, self.version, self.flags)?; @@ -99,7 +91,6 @@ impl WriteBox<&mut W> for StssBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_stss() { @@ -112,12 +103,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::StssBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::StssBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = StssBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = StssBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/mp4box/stsz.rs b/src/mp4box/stsz.rs index b07e765..340a0fb 100644 --- a/src/mp4box/stsz.rs +++ b/src/mp4box/stsz.rs @@ -1,6 +1,6 @@ -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{BigEndian, WriteBytesExt}; use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use std::mem::size_of; use crate::mp4box::*; @@ -27,9 +27,7 @@ impl StszBox { } impl Mp4Box for StszBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::StszBox; fn box_size(&self) -> u64 { self.get_size() @@ -50,42 +48,31 @@ impl Mp4Box for StszBox { } } -impl ReadBox<&mut R> for StszBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; +impl BlockReader for StszBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (version, flags) = read_box_header_ext(reader); - let (version, flags) = read_box_header_ext(reader)?; - - let header_size = HEADER_SIZE + HEADER_EXT_SIZE; - let other_size = size_of::() + size_of::(); // sample_size + sample_count - let sample_size = reader.read_u32::()?; + let sample_size = reader.get_u32(); let stsz_item_size = if sample_size == 0 { size_of::() // entry_size } else { 0 }; - let sample_count = reader.read_u32::()?; + let sample_count = reader.get_u32(); let mut sample_sizes = Vec::new(); if sample_size == 0 { - if u64::from(sample_count) - > size - .saturating_sub(header_size) - .saturating_sub(other_size as u64) - / stsz_item_size as u64 - { + if sample_count as usize > reader.remaining() / stsz_item_size { return Err(Error::InvalidData( "stsz sample_count indicates more values than could fit in the box", )); } sample_sizes.reserve(sample_count as usize); for _ in 0..sample_count { - let sample_number = reader.read_u32::()?; + let sample_number = reader.get_u32(); sample_sizes.push(sample_number); } } - skip_bytes_to(reader, start + size)?; - Ok(StszBox { version, flags, @@ -94,12 +81,16 @@ impl ReadBox<&mut R> for StszBox { sample_sizes, }) } + + fn size_hint() -> usize { + 12 + } } impl WriteBox<&mut W> for StszBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; write_box_header_ext(writer, self.version, self.flags)?; @@ -122,7 +113,6 @@ impl WriteBox<&mut W> for StszBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_stsz_same_size() { @@ -137,12 +127,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::StszBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::StszBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = StszBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = StszBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } @@ -159,12 +149,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::StszBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::StszBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = StszBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = StszBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/mp4box/stts.rs b/src/mp4box/stts.rs index 82de6c5..1432b4f 100644 --- a/src/mp4box/stts.rs +++ b/src/mp4box/stts.rs @@ -1,6 +1,6 @@ -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{BigEndian, WriteBytesExt}; use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use std::mem::size_of; use crate::mp4box::*; @@ -31,9 +31,7 @@ pub struct SttsEntry { } impl Mp4Box for SttsBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::SttsBox; fn box_size(&self) -> u64 { self.get_size() @@ -49,49 +47,44 @@ impl Mp4Box for SttsBox { } } -impl ReadBox<&mut R> for SttsBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; +impl BlockReader for SttsBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (version, flags) = read_box_header_ext(reader); - let (version, flags) = read_box_header_ext(reader)?; - - let header_size = HEADER_SIZE + HEADER_EXT_SIZE; - let other_size = size_of::(); // entry_count let entry_size = size_of::() + size_of::(); // sample_count + sample_delta - let entry_count = reader.read_u32::()?; - if u64::from(entry_count) - > size - .saturating_sub(header_size) - .saturating_sub(other_size as u64) - / entry_size as u64 - { + let entry_count = reader.get_u32(); + + if entry_count as usize > reader.remaining() / entry_size { return Err(Error::InvalidData( "stts entry_count indicates more entries than could fit in the box", )); } + let mut entries = Vec::with_capacity(entry_count as usize); for _i in 0..entry_count { let entry = SttsEntry { - sample_count: reader.read_u32::()?, - sample_delta: reader.read_u32::()?, + sample_count: reader.get_u32(), + sample_delta: reader.get_u32(), }; entries.push(entry); } - skip_bytes_to(reader, start + size)?; - Ok(SttsBox { version, flags, entries, }) } + + fn size_hint() -> usize { + 8 + } } impl WriteBox<&mut W> for SttsBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; write_box_header_ext(writer, self.version, self.flags)?; @@ -109,7 +102,6 @@ impl WriteBox<&mut W> for SttsBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_stts() { @@ -131,12 +123,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::SttsBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::SttsBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = SttsBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = SttsBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/mp4box/tfdt.rs b/src/mp4box/tfdt.rs index ef92889..19dd946 100644 --- a/src/mp4box/tfdt.rs +++ b/src/mp4box/tfdt.rs @@ -1,6 +1,6 @@ -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{BigEndian, WriteBytesExt}; use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use crate::mp4box::*; @@ -28,9 +28,7 @@ impl TfdtBox { } impl Mp4Box for TfdtBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::TfdtBox; fn box_size(&self) -> u64 { self.get_size() @@ -46,34 +44,34 @@ impl Mp4Box for TfdtBox { } } -impl ReadBox<&mut R> for TfdtBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; - - let (version, flags) = read_box_header_ext(reader)?; +impl BlockReader for TfdtBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (version, flags) = read_box_header_ext(reader); let base_media_decode_time = if version == 1 { - reader.read_u64::()? + reader.get_u64() } else if version == 0 { - reader.read_u32::()? as u64 + reader.get_u32() as u64 } else { return Err(Error::InvalidData("version must be 0 or 1")); }; - skip_bytes_to(reader, start + size)?; - Ok(TfdtBox { version, flags, base_media_decode_time, }) } + + fn size_hint() -> usize { + 8 + } } impl WriteBox<&mut W> for TfdtBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; write_box_header_ext(writer, self.version, self.flags)?; @@ -93,7 +91,6 @@ impl WriteBox<&mut W> for TfdtBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_tfdt32() { @@ -106,12 +103,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::TfdtBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::TfdtBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = TfdtBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = TfdtBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } @@ -126,12 +123,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::TfdtBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::TfdtBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = TfdtBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = TfdtBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/mp4box/tfhd.rs b/src/mp4box/tfhd.rs index 5b529e6..b335cbd 100644 --- a/src/mp4box/tfhd.rs +++ b/src/mp4box/tfhd.rs @@ -1,6 +1,6 @@ -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{BigEndian, WriteBytesExt}; use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use crate::mp4box::*; @@ -51,9 +51,7 @@ impl TfhdBox { } impl Mp4Box for TfhdBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::TfhdBox; fn box_size(&self) -> u64 { self.get_size() @@ -69,39 +67,40 @@ impl Mp4Box for TfhdBox { } } -impl ReadBox<&mut R> for TfhdBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; +impl BlockReader for TfhdBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (version, flags) = read_box_header_ext(reader); + let track_id = reader.get_u32(); - let (version, flags) = read_box_header_ext(reader)?; - let track_id = reader.read_u32::()?; let base_data_offset = if TfhdBox::FLAG_BASE_DATA_OFFSET & flags > 0 { - Some(reader.read_u64::()?) - } else { - None - }; - let sample_description_index = if TfhdBox::FLAG_SAMPLE_DESCRIPTION_INDEX & flags > 0 { - Some(reader.read_u32::()?) - } else { - None - }; - let default_sample_duration = if TfhdBox::FLAG_DEFAULT_SAMPLE_DURATION & flags > 0 { - Some(reader.read_u32::()?) - } else { - None - }; - let default_sample_size = if TfhdBox::FLAG_DEFAULT_SAMPLE_SIZE & flags > 0 { - Some(reader.read_u32::()?) - } else { - None - }; - let default_sample_flags = if TfhdBox::FLAG_DEFAULT_SAMPLE_FLAGS & flags > 0 { - Some(reader.read_u32::()?) + Some(reader.get_u64()) } else { None }; - skip_bytes_to(reader, start + size)?; + let sample_description_index = if TfhdBox::FLAG_SAMPLE_DESCRIPTION_INDEX & flags > 0 { + Some(reader.get_u32()) + } else { + None + }; + + let default_sample_duration = if TfhdBox::FLAG_DEFAULT_SAMPLE_DURATION & flags > 0 { + Some(reader.get_u32()) + } else { + None + }; + + let default_sample_size = if TfhdBox::FLAG_DEFAULT_SAMPLE_SIZE & flags > 0 { + Some(reader.get_u32()) + } else { + None + }; + + let default_sample_flags = if TfhdBox::FLAG_DEFAULT_SAMPLE_FLAGS & flags > 0 { + Some(reader.get_u32()) + } else { + None + }; Ok(TfhdBox { version, @@ -114,12 +113,16 @@ impl ReadBox<&mut R> for TfhdBox { default_sample_flags, }) } + + fn size_hint() -> usize { + 8 + } } impl WriteBox<&mut W> for TfhdBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; write_box_header_ext(writer, self.version, self.flags)?; writer.write_u32::(self.track_id)?; @@ -147,7 +150,6 @@ impl WriteBox<&mut W> for TfhdBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_tfhd() { @@ -165,12 +167,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::TfhdBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::TfhdBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = TfhdBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = TfhdBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } @@ -192,12 +194,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::TfhdBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::TfhdBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = TfhdBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = TfhdBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/mp4box/tkhd.rs b/src/mp4box/tkhd.rs index d7bcfbe..cead138 100644 --- a/src/mp4box/tkhd.rs +++ b/src/mp4box/tkhd.rs @@ -1,6 +1,6 @@ -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{BigEndian, WriteBytesExt}; use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use crate::mp4box::*; @@ -117,9 +117,7 @@ impl TkhdBox { } impl Mp4Box for TkhdBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::TkhdBox; fn box_size(&self) -> u64 { self.get_size() @@ -145,53 +143,52 @@ impl Mp4Box for TkhdBox { } } -impl ReadBox<&mut R> for TkhdBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; - - let (version, flags) = read_box_header_ext(reader)?; +impl BlockReader for TkhdBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (version, flags) = read_box_header_ext(reader); let (creation_time, modification_time, track_id, _, duration) = if version == 1 { ( - reader.read_u64::()?, - reader.read_u64::()?, - reader.read_u32::()?, - reader.read_u32::()?, - reader.read_u64::()?, + reader.get_u64(), + reader.get_u64(), + reader.get_u32(), + reader.get_u32(), + reader.get_u64(), ) } else if version == 0 { ( - reader.read_u32::()? as u64, - reader.read_u32::()? as u64, - reader.read_u32::()?, - reader.read_u32::()?, - reader.read_u32::()? as u64, + reader.get_u32() as u64, + reader.get_u32() as u64, + reader.get_u32(), + reader.get_u32(), + reader.get_u32() as u64, ) } else { return Err(Error::InvalidData("version must be 0 or 1")); }; - reader.read_u64::()?; // reserved - let layer = reader.read_u16::()?; - let alternate_group = reader.read_u16::()?; - let volume = FixedPointU8::new_raw(reader.read_u16::()?); - reader.read_u16::()?; // reserved + reader.get_u64(); // reserved + + let layer = reader.get_u16(); + let alternate_group = reader.get_u16(); + let volume = FixedPointU8::new_raw(reader.get_u16()); + + reader.get_u16(); // reserved + let matrix = Matrix { - a: reader.read_i32::()?, - b: reader.read_i32::()?, - u: reader.read_i32::()?, - c: reader.read_i32::()?, - d: reader.read_i32::()?, - v: reader.read_i32::()?, - x: reader.read_i32::()?, - y: reader.read_i32::()?, - w: reader.read_i32::()?, + a: reader.get_i32(), + b: reader.get_i32(), + u: reader.get_i32(), + c: reader.get_i32(), + d: reader.get_i32(), + v: reader.get_i32(), + x: reader.get_i32(), + y: reader.get_i32(), + w: reader.get_i32(), }; - let width = FixedPointU16::new_raw(reader.read_u32::()?); - let height = FixedPointU16::new_raw(reader.read_u32::()?); - - skip_bytes_to(reader, start + size)?; + let width = FixedPointU16::new_raw(reader.get_u32()); + let height = FixedPointU16::new_raw(reader.get_u32()); Ok(TkhdBox { version, @@ -208,12 +205,16 @@ impl ReadBox<&mut R> for TkhdBox { height, }) } + + fn size_hint() -> usize { + 84 + } } impl WriteBox<&mut W> for TkhdBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; write_box_header_ext(writer, self.version, self.flags)?; @@ -261,7 +262,6 @@ impl WriteBox<&mut W> for TkhdBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_tkhd32() { @@ -283,12 +283,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::TkhdBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::TkhdBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = TkhdBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = TkhdBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } @@ -312,12 +312,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::TkhdBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::TkhdBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = TkhdBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = TkhdBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/mp4box/traf.rs b/src/mp4box/traf.rs index d53d713..08769d1 100644 --- a/src/mp4box/traf.rs +++ b/src/mp4box/traf.rs @@ -1,5 +1,5 @@ use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use crate::mp4box::*; use crate::mp4box::{tfdt::TfdtBox, tfhd::TfhdBox, trun::TrunBox}; @@ -12,10 +12,6 @@ pub struct TrafBox { } impl TrafBox { - pub fn get_type(&self) -> BoxType { - BoxType::TrafBox - } - pub fn get_size(&self) -> u64 { let mut size = HEADER_SIZE; size += self.tfhd.box_size(); @@ -30,9 +26,7 @@ impl TrafBox { } impl Mp4Box for TrafBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::TrafBox; fn box_size(&self) -> u64 { self.get_size() @@ -48,63 +42,30 @@ impl Mp4Box for TrafBox { } } -impl ReadBox<&mut R> for TrafBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; - - let mut tfhd = None; - let mut tfdt = None; - let mut trun = None; - - let mut current = reader.stream_position()?; - let end = start + size; - while current < end { - // Get box header. - let header = BoxHeader::read(reader)?; - let BoxHeader { name, size: s } = header; - if s > size { - return Err(Error::InvalidData( - "traf box contains a box with a larger size than it", - )); - } - - match name { - BoxType::TfhdBox => { - tfhd = Some(TfhdBox::read_box(reader, s)?); - } - BoxType::TfdtBox => { - tfdt = Some(TfdtBox::read_box(reader, s)?); - } - BoxType::TrunBox => { - trun = Some(TrunBox::read_box(reader, s)?); - } - _ => { - // XXX warn!() - skip_box(reader, s)?; - } - } - - current = reader.stream_position()?; - } +impl BlockReader for TrafBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (tfhd, tfdt, trun) = reader.try_find_box3()?; if tfhd.is_none() { return Err(Error::BoxNotFound(BoxType::TfhdBox)); } - skip_bytes_to(reader, start + size)?; - Ok(TrafBox { tfhd: tfhd.unwrap(), tfdt, trun, }) } + + fn size_hint() -> usize { + TfhdBox::size_hint() + } } impl WriteBox<&mut W> for TrafBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; self.tfhd.write_box(writer)?; if let Some(ref tfdt) = self.tfdt { diff --git a/src/mp4box/trak.rs b/src/mp4box/trak.rs index e8ae760..502d4a5 100644 --- a/src/mp4box/trak.rs +++ b/src/mp4box/trak.rs @@ -1,5 +1,5 @@ use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use crate::meta::MetaBox; use crate::mp4box::*; @@ -8,14 +8,13 @@ use crate::mp4box::{edts::EdtsBox, mdia::MdiaBox, tkhd::TkhdBox}; #[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)] pub struct TrakBox { pub tkhd: TkhdBox, + pub mdia: MdiaBox, #[serde(skip_serializing_if = "Option::is_none")] pub edts: Option, #[serde(skip_serializing_if = "Option::is_none")] pub meta: Option, - - pub mdia: MdiaBox, } impl TrakBox { @@ -32,12 +31,180 @@ impl TrakBox { size += self.mdia.box_size(); size } + + pub(crate) fn stsc_index(&self, sample_id: u32) -> Result { + if self.mdia.minf.stbl.stsc.entries.is_empty() { + return Err(Error::InvalidData("no stsc entries")); + } + + for (i, entry) in self.mdia.minf.stbl.stsc.entries.iter().enumerate() { + if sample_id < entry.first_sample { + return if i == 0 { + Err(Error::InvalidData("sample not found")) + } else { + Ok(i - 1) + }; + } + } + + Ok(self.mdia.minf.stbl.stsc.entries.len() - 1) + } + + pub(crate) fn chunk_offset(&self, chunk_id: u32) -> Result { + if self.mdia.minf.stbl.stco.is_none() && self.mdia.minf.stbl.co64.is_none() { + return Err(Error::InvalidData("must have either stco or co64 boxes")); + } + + if let Some(ref stco) = self.mdia.minf.stbl.stco { + if let Some(offset) = stco.entries.get(chunk_id as usize - 1) { + return Ok(*offset as u64); + } else { + return Err(Error::EntryInStblNotFound( + self.tkhd.track_id, + BoxType::StcoBox, + chunk_id, + )); + } + } else if let Some(ref co64) = self.mdia.minf.stbl.co64 { + if let Some(offset) = co64.entries.get(chunk_id as usize - 1) { + return Ok(*offset); + } else { + return Err(Error::EntryInStblNotFound( + self.tkhd.track_id, + BoxType::Co64Box, + chunk_id, + )); + } + } + + Err(Error::Box2NotFound(BoxType::StcoBox, BoxType::Co64Box)) + } + + pub(crate) fn sample_size(&self, sample_id: u32) -> Result { + let stsz = &self.mdia.minf.stbl.stsz; + + if stsz.sample_size > 0 { + return Ok(stsz.sample_size); + } + + if let Some(size) = stsz.sample_sizes.get(sample_id as usize - 1) { + Ok(*size) + } else { + Err(Error::EntryInStblNotFound( + self.tkhd.track_id, + BoxType::StszBox, + sample_id, + )) + } + } + + pub(crate) fn sample_offset(&self, sample_id: u32) -> Result { + let stsc_index = self.stsc_index(sample_id)?; + + let stsc = &self.mdia.minf.stbl.stsc; + let stsc_entry = stsc.entries.get(stsc_index).unwrap(); + + let first_chunk = stsc_entry.first_chunk; + let first_sample = stsc_entry.first_sample; + let samples_per_chunk = stsc_entry.samples_per_chunk; + + let chunk_id = sample_id + .checked_sub(first_sample) + .map(|n| n / samples_per_chunk) + .and_then(|n| n.checked_add(first_chunk)) + .ok_or(Error::InvalidData( + "attempt to calculate stsc chunk_id with overflow", + ))?; + + let chunk_offset = self.chunk_offset(chunk_id)?; + + let first_sample_in_chunk = sample_id - (sample_id - first_sample) % samples_per_chunk; + + let mut sample_offset = 0; + for i in first_sample_in_chunk..sample_id { + sample_offset += self.sample_size(i)?; + } + + Ok(chunk_offset + sample_offset as u64) + } + + pub(crate) fn sample_time(&self, sample_id: u32) -> Result<(u64, u32)> { + let stts = self.mdia.minf.stbl.stts; + + let mut sample_count: u32 = 1; + let mut elapsed = 0; + + for entry in stts.entries.iter() { + let new_sample_count = + sample_count + .checked_add(entry.sample_count) + .ok_or(Error::InvalidData( + "attempt to sum stts entries sample_count with overflow", + ))?; + + if sample_id < new_sample_count { + let start_time = + (sample_id - sample_count) as u64 * entry.sample_delta as u64 + elapsed; + return Ok((start_time, entry.sample_delta)); + } + + sample_count = new_sample_count; + elapsed += entry.sample_count as u64 * entry.sample_delta as u64; + } + + Err(Error::EntryInStblNotFound( + self.tkhd.track_id, + BoxType::SttsBox, + sample_id, + )) + } + + pub(crate) fn ctts_index(&self, sample_id: u32) -> Result<(usize, u32)> { + let ctts = self.mdia.minf.stbl.ctts.as_ref().unwrap(); + let mut sample_count: u32 = 1; + for (i, entry) in ctts.entries.iter().enumerate() { + let next_sample_count = + sample_count + .checked_add(entry.sample_count) + .ok_or(Error::InvalidData( + "attempt to sum ctts entries sample_count with overflow", + ))?; + if sample_id < next_sample_count { + return Ok((i, sample_count)); + } + sample_count = next_sample_count; + } + + Err(Error::EntryInStblNotFound( + self.tkhd.track_id, + BoxType::CttsBox, + sample_id, + )) + } + + pub fn sample_rendering_offset(self, sample_id: u32) -> i32 { + if let Some(ref ctts) = self.mdia.minf.stbl.ctts { + if let Ok((ctts_index, _)) = self.ctts_index(sample_id) { + let ctts_entry = ctts.entries.get(ctts_index).unwrap(); + return ctts_entry.sample_offset; + } + } + + 0 + } + + #[inline] + pub fn sample_is_sync(&self, sample_id: u32) -> bool { + if let Some(ref stss) = self.mdia.minf.stbl.stss { + stss.entries.binary_search(&sample_id).is_ok() + } else { + true + } + } } impl Mp4Box for TrakBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::TrakBox; fn box_size(&self) -> u64 { self.get_size() @@ -53,58 +220,18 @@ impl Mp4Box for TrakBox { } } -impl ReadBox<&mut R> for TrakBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; - - let mut tkhd = None; - let mut edts = None; - let mut meta = None; - let mut mdia = None; - - let mut current = reader.stream_position()?; - let end = start + size; - while current < end { - // Get box header. - let header = BoxHeader::read(reader)?; - let BoxHeader { name, size: s } = header; - if s > size { - return Err(Error::InvalidData( - "trak box contains a box with a larger size than it", - )); - } - - match name { - BoxType::TkhdBox => { - tkhd = Some(TkhdBox::read_box(reader, s)?); - } - BoxType::EdtsBox => { - edts = Some(EdtsBox::read_box(reader, s)?); - } - BoxType::MetaBox => { - meta = Some(MetaBox::read_box(reader, s)?); - } - BoxType::MdiaBox => { - mdia = Some(MdiaBox::read_box(reader, s)?); - } - _ => { - // XXX warn!() - skip_box(reader, s)?; - } - } - - current = reader.stream_position()?; - } +impl BlockReader for TrakBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (tkhd, edts, meta, mdia) = reader.try_find_box4()?; if tkhd.is_none() { return Err(Error::BoxNotFound(BoxType::TkhdBox)); } + if mdia.is_none() { return Err(Error::BoxNotFound(BoxType::MdiaBox)); } - skip_bytes_to(reader, start + size)?; - Ok(TrakBox { tkhd: tkhd.unwrap(), edts, @@ -112,12 +239,16 @@ impl ReadBox<&mut R> for TrakBox { mdia: mdia.unwrap(), }) } + + fn size_hint() -> usize { + TkhdBox::size_hint() + MdiaBox::size_hint() + } } impl WriteBox<&mut W> for TrakBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; self.tkhd.write_box(writer)?; if let Some(ref edts) = self.edts { diff --git a/src/mp4box/trex.rs b/src/mp4box/trex.rs index 2694fd6..0aa1922 100644 --- a/src/mp4box/trex.rs +++ b/src/mp4box/trex.rs @@ -1,6 +1,6 @@ -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{BigEndian, WriteBytesExt}; use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use crate::mp4box::*; @@ -26,9 +26,7 @@ impl TrexBox { } impl Mp4Box for TrexBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::TrexBox; fn box_size(&self) -> u64 { self.get_size() @@ -47,19 +45,15 @@ impl Mp4Box for TrexBox { } } -impl ReadBox<&mut R> for TrexBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; +impl BlockReader for TrexBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (version, flags) = read_box_header_ext(reader); - let (version, flags) = read_box_header_ext(reader)?; - - let track_id = reader.read_u32::()?; - let default_sample_description_index = reader.read_u32::()?; - let default_sample_duration = reader.read_u32::()?; - let default_sample_size = reader.read_u32::()?; - let default_sample_flags = reader.read_u32::()?; - - skip_bytes_to(reader, start + size)?; + let track_id = reader.get_u32(); + let default_sample_description_index = reader.get_u32(); + let default_sample_duration = reader.get_u32(); + let default_sample_size = reader.get_u32(); + let default_sample_flags = reader.get_u32(); Ok(TrexBox { version, @@ -71,12 +65,16 @@ impl ReadBox<&mut R> for TrexBox { default_sample_flags, }) } + + fn size_hint() -> usize { + 24 + } } impl WriteBox<&mut W> for TrexBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; write_box_header_ext(writer, self.version, self.flags)?; @@ -94,7 +92,6 @@ impl WriteBox<&mut W> for TrexBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_trex() { @@ -111,12 +108,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::TrexBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::TrexBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = TrexBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = TrexBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/mp4box/trun.rs b/src/mp4box/trun.rs index efbb2b0..ae9c634 100644 --- a/src/mp4box/trun.rs +++ b/src/mp4box/trun.rs @@ -1,6 +1,6 @@ -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{BigEndian, WriteBytesExt}; use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use std::mem::size_of; use crate::mp4box::*; @@ -15,10 +15,13 @@ pub struct TrunBox { #[serde(skip_serializing)] pub sample_durations: Vec, + #[serde(skip_serializing)] pub sample_sizes: Vec, + #[serde(skip_serializing)] pub sample_flags: Vec, + #[serde(skip_serializing)] pub sample_cts: Vec, } @@ -60,9 +63,7 @@ impl TrunBox { } impl Mp4Box for TrunBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::TrunBox; fn box_size(&self) -> u64 { self.get_size() @@ -78,31 +79,25 @@ impl Mp4Box for TrunBox { } } -impl ReadBox<&mut R> for TrunBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; +impl BlockReader for TrunBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (version, flags) = read_box_header_ext(reader); - let (version, flags) = read_box_header_ext(reader)?; - - let header_size = HEADER_SIZE + HEADER_EXT_SIZE; - let other_size = size_of::() // sample_count - + if TrunBox::FLAG_DATA_OFFSET & flags > 0 { size_of::() } else { 0 } // data_offset - + if TrunBox::FLAG_FIRST_SAMPLE_FLAGS & flags > 0 { size_of::() } else { 0 }; // first_sample_flags let sample_size = if TrunBox::FLAG_SAMPLE_DURATION & flags > 0 { size_of::() } else { 0 } // sample_duration + if TrunBox::FLAG_SAMPLE_SIZE & flags > 0 { size_of::() } else { 0 } // sample_size + if TrunBox::FLAG_SAMPLE_FLAGS & flags > 0 { size_of::() } else { 0 } // sample_flags + if TrunBox::FLAG_SAMPLE_CTS & flags > 0 { size_of::() } else { 0 }; // sample_composition_time_offset - let sample_count = reader.read_u32::()?; + let sample_count = reader.get_u32(); let data_offset = if TrunBox::FLAG_DATA_OFFSET & flags > 0 { - Some(reader.read_i32::()?) + Some(reader.try_get_i32()?) } else { None }; let first_sample_flags = if TrunBox::FLAG_FIRST_SAMPLE_FLAGS & flags > 0 { - Some(reader.read_u32::()?) + Some(reader.try_get_u32()?) } else { None }; @@ -111,52 +106,51 @@ impl ReadBox<&mut R> for TrunBox { let mut sample_sizes = Vec::new(); let mut sample_flags = Vec::new(); let mut sample_cts = Vec::new(); - if u64::from(sample_count) * sample_size as u64 - > size - .saturating_sub(header_size) - .saturating_sub(other_size as u64) - { + + if sample_count as usize * sample_size > reader.remaining() { return Err(Error::InvalidData( "trun sample_count indicates more values than could fit in the box", )); } + if TrunBox::FLAG_SAMPLE_DURATION & flags > 0 { sample_durations.reserve(sample_count as usize); } + if TrunBox::FLAG_SAMPLE_SIZE & flags > 0 { sample_sizes.reserve(sample_count as usize); } + if TrunBox::FLAG_SAMPLE_FLAGS & flags > 0 { sample_flags.reserve(sample_count as usize); } + if TrunBox::FLAG_SAMPLE_CTS & flags > 0 { sample_cts.reserve(sample_count as usize); } for _ in 0..sample_count { if TrunBox::FLAG_SAMPLE_DURATION & flags > 0 { - let duration = reader.read_u32::()?; + let duration = reader.get_u32(); sample_durations.push(duration); } if TrunBox::FLAG_SAMPLE_SIZE & flags > 0 { - let sample_size = reader.read_u32::()?; + let sample_size = reader.get_u32(); sample_sizes.push(sample_size); } if TrunBox::FLAG_SAMPLE_FLAGS & flags > 0 { - let sample_flag = reader.read_u32::()?; + let sample_flag = reader.get_u32(); sample_flags.push(sample_flag); } if TrunBox::FLAG_SAMPLE_CTS & flags > 0 { - let cts = reader.read_u32::()?; + let cts = reader.get_u32(); sample_cts.push(cts); } } - skip_bytes_to(reader, start + size)?; - Ok(TrunBox { version, flags, @@ -169,12 +163,16 @@ impl ReadBox<&mut R> for TrunBox { sample_cts, }) } + + fn size_hint() -> usize { + 8 + } } impl WriteBox<&mut W> for TrunBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; write_box_header_ext(writer, self.version, self.flags)?; @@ -211,7 +209,6 @@ impl WriteBox<&mut W> for TrunBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_trun_same_size() { @@ -230,12 +227,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::TrunBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::TrunBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = TrunBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = TrunBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } @@ -259,12 +256,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::TrunBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::TrunBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = TrunBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = TrunBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/mp4box/tx3g.rs b/src/mp4box/tx3g.rs index d696315..01b7533 100644 --- a/src/mp4box/tx3g.rs +++ b/src/mp4box/tx3g.rs @@ -1,6 +1,6 @@ -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{BigEndian, WriteBytesExt}; use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use crate::mp4box::*; @@ -53,9 +53,7 @@ impl Tx3gBox { } impl Mp4Box for Tx3gBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::Tx3gBox; fn box_size(&self) -> u64 { self.get_size() @@ -74,46 +72,42 @@ impl Mp4Box for Tx3gBox { } } -impl ReadBox<&mut R> for Tx3gBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; +impl BlockReader for Tx3gBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + reader.get_u32(); // reserved + reader.get_u16(); // reserved + let data_reference_index = reader.get_u16(); - reader.read_u32::()?; // reserved - reader.read_u16::()?; // reserved - let data_reference_index = reader.read_u16::()?; - - let display_flags = reader.read_u32::()?; - let horizontal_justification = reader.read_i8()?; - let vertical_justification = reader.read_i8()?; + let display_flags = reader.get_u32(); + let horizontal_justification = reader.get_i8(); + let vertical_justification = reader.get_i8(); let bg_color_rgba = RgbaColor { - red: reader.read_u8()?, - green: reader.read_u8()?, - blue: reader.read_u8()?, - alpha: reader.read_u8()?, + red: reader.get_u8(), + green: reader.get_u8(), + blue: reader.get_u8(), + alpha: reader.get_u8(), }; let box_record: [i16; 4] = [ - reader.read_i16::()?, - reader.read_i16::()?, - reader.read_i16::()?, - reader.read_i16::()?, + reader.get_i16(), + reader.get_i16(), + reader.get_i16(), + reader.get_i16(), ]; let style_record: [u8; 12] = [ - reader.read_u8()?, - reader.read_u8()?, - reader.read_u8()?, - reader.read_u8()?, - reader.read_u8()?, - reader.read_u8()?, - reader.read_u8()?, - reader.read_u8()?, - reader.read_u8()?, - reader.read_u8()?, - reader.read_u8()?, - reader.read_u8()?, + reader.get_u8(), + reader.get_u8(), + reader.get_u8(), + reader.get_u8(), + reader.get_u8(), + reader.get_u8(), + reader.get_u8(), + reader.get_u8(), + reader.get_u8(), + reader.get_u8(), + reader.get_u8(), + reader.get_u8(), ]; - skip_bytes_to(reader, start + size)?; - Ok(Tx3gBox { data_reference_index, display_flags, @@ -124,12 +118,16 @@ impl ReadBox<&mut R> for Tx3gBox { style_record, }) } + + fn size_hint() -> usize { + 34 + } } impl WriteBox<&mut W> for Tx3gBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; writer.write_u32::(0)?; // reserved writer.write_u16::(0)?; // reserved @@ -156,7 +154,6 @@ impl WriteBox<&mut W> for Tx3gBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_tx3g() { @@ -178,12 +175,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::Tx3gBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::Tx3gBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = Tx3gBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = Tx3gBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/mp4box/udta.rs b/src/mp4box/udta.rs index 9daec17..1cc97c8 100644 --- a/src/mp4box/udta.rs +++ b/src/mp4box/udta.rs @@ -1,5 +1,3 @@ -use std::io::{Read, Seek}; - use serde::Serialize; use crate::mp4box::meta::MetaBox; @@ -26,9 +24,7 @@ impl UdtaBox { } impl Mp4Box for UdtaBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::UdtaBox; fn box_size(&self) -> u64 { self.get_size() @@ -43,47 +39,22 @@ impl Mp4Box for UdtaBox { } } -impl ReadBox<&mut R> for UdtaBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; +impl BlockReader for UdtaBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + Ok(UdtaBox { + meta: reader.try_find_box()?, + }) + } - let mut meta = None; - - let mut current = reader.stream_position()?; - let end = start + size; - while current < end { - // Get box header. - let header = BoxHeader::read(reader)?; - let BoxHeader { name, size: s } = header; - if s > size { - return Err(Error::InvalidData( - "udta box contains a box with a larger size than it", - )); - } - - match name { - BoxType::MetaBox => { - meta = Some(MetaBox::read_box(reader, s)?); - } - _ => { - // XXX warn!() - skip_box(reader, s)?; - } - } - - current = reader.stream_position()?; - } - - skip_bytes_to(reader, start + size)?; - - Ok(UdtaBox { meta }) + fn size_hint() -> usize { + 0 } } impl WriteBox<&mut W> for UdtaBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; if let Some(meta) = &self.meta { meta.write_box(writer)?; @@ -96,7 +67,6 @@ impl WriteBox<&mut W> for UdtaBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_udta_empty() { @@ -106,12 +76,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::UdtaBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::UdtaBox); assert_eq!(header.size, src_box.box_size()); - let dst_box = UdtaBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = UdtaBox::read_block(&mut reader).unwrap(); assert_eq!(dst_box, src_box); } @@ -125,12 +95,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::UdtaBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::UdtaBox); assert_eq!(header.size, src_box.box_size()); - let dst_box = UdtaBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = UdtaBox::read_block(&mut reader).unwrap(); assert_eq!(dst_box, src_box); } } diff --git a/src/mp4box/vmhd.rs b/src/mp4box/vmhd.rs index 31f24b2..122b8ed 100644 --- a/src/mp4box/vmhd.rs +++ b/src/mp4box/vmhd.rs @@ -1,6 +1,6 @@ -use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use byteorder::{BigEndian, WriteBytesExt}; use serde::Serialize; -use std::io::{Read, Seek, Write}; +use std::io::Write; use crate::mp4box::*; @@ -30,9 +30,7 @@ impl VmhdBox { } impl Mp4Box for VmhdBox { - fn box_type(&self) -> BoxType { - self.get_type() - } + const TYPE: BoxType = BoxType::VmhdBox; fn box_size(&self) -> u64 { self.get_size() @@ -51,21 +49,16 @@ impl Mp4Box for VmhdBox { } } -impl ReadBox<&mut R> for VmhdBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; - - let (version, flags) = read_box_header_ext(reader)?; - - let graphics_mode = reader.read_u16::()?; +impl BlockReader for VmhdBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (version, flags) = read_box_header_ext(reader); + let graphics_mode = reader.get_u16(); let op_color = RgbColor { - red: reader.read_u16::()?, - green: reader.read_u16::()?, - blue: reader.read_u16::()?, + red: reader.get_u16(), + green: reader.get_u16(), + blue: reader.get_u16(), }; - skip_bytes_to(reader, start + size)?; - Ok(VmhdBox { version, flags, @@ -73,12 +66,16 @@ impl ReadBox<&mut R> for VmhdBox { op_color, }) } + + fn size_hint() -> usize { + 12 + } } impl WriteBox<&mut W> for VmhdBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; write_box_header_ext(writer, self.version, self.flags)?; @@ -95,7 +92,6 @@ impl WriteBox<&mut W> for VmhdBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_vmhd() { @@ -113,12 +109,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::VmhdBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::VmhdBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = VmhdBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = VmhdBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/mp4box/vp09.rs b/src/mp4box/vp09.rs index 0f88dd1..04219ed 100644 --- a/src/mp4box/vp09.rs +++ b/src/mp4box/vp09.rs @@ -66,9 +66,7 @@ impl Vp09Box { } impl Mp4Box for Vp09Box { - fn box_type(&self) -> BoxType { - BoxType::Vp09Box - } + const TYPE: BoxType = BoxType::Vp09Box; fn box_size(&self) -> u64 { 0x6A @@ -83,53 +81,37 @@ impl Mp4Box for Vp09Box { } } -impl ReadBox<&mut R> for Vp09Box { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; - let (version, flags) = read_box_header_ext(reader)?; +impl BlockReader for Vp09Box { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (version, flags) = read_box_header_ext(reader); - let start_code: u16 = reader.read_u16::()?; - let data_reference_index: u16 = reader.read_u16::()?; + let start_code: u16 = reader.get_u16(); + let data_reference_index: u16 = reader.get_u16(); let reserved0: [u8; 16] = { let mut buf = [0u8; 16]; - reader.read_exact(&mut buf)?; + reader.copy_to_slice(&mut buf)?; buf }; - let width: u16 = reader.read_u16::()?; - let height: u16 = reader.read_u16::()?; - let horizresolution: (u16, u16) = ( - reader.read_u16::()?, - reader.read_u16::()?, - ); - let vertresolution: (u16, u16) = ( - reader.read_u16::()?, - reader.read_u16::()?, - ); + + let width: u16 = reader.get_u16(); + let height: u16 = reader.get_u16(); + let horizresolution: (u16, u16) = (reader.get_u16(), reader.get_u16()); + let vertresolution: (u16, u16) = (reader.get_u16(), reader.get_u16()); let reserved1: [u8; 4] = { let mut buf = [0u8; 4]; - reader.read_exact(&mut buf)?; + reader.copy_to_slice(&mut buf)?; buf }; - let frame_count: u16 = reader.read_u16::()?; + + let frame_count: u16 = reader.get_u16(); let compressorname: [u8; 32] = { let mut buf = [0u8; 32]; - reader.read_exact(&mut buf)?; + reader.copy_to_slice(&mut buf)?; buf }; - let depth: u16 = reader.read_u16::()?; - let end_code: u16 = reader.read_u16::()?; - let vpcc = { - let header = BoxHeader::read(reader)?; - if header.size > size { - return Err(Error::InvalidData( - "vp09 box contains a box with a larger size than it", - )); - } - VpccBox::read_box(reader, header.size)? - }; - - skip_bytes_to(reader, start + size)?; + let depth: u16 = reader.get_u16(); + let end_code: u16 = reader.get_u16(); Ok(Self { version, @@ -146,15 +128,19 @@ impl ReadBox<&mut R> for Vp09Box { compressorname, depth, end_code, - vpcc, + vpcc: reader.find_box::()?, }) } + + fn size_hint() -> usize { + 78 + } } impl WriteBox<&mut W> for Vp09Box { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; write_box_header_ext(writer, self.version, self.flags)?; @@ -182,7 +168,6 @@ impl WriteBox<&mut W> for Vp09Box { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_vpcc() { @@ -194,12 +179,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::Vp09Box); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::Vp09Box); assert_eq!(src_box.box_size(), header.size); - let dst_box = Vp09Box::read_box(&mut reader, header.size).unwrap(); + let dst_box = Vp09Box::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/mp4box/vpcc.rs b/src/mp4box/vpcc.rs index c9861c6..cc06fc9 100644 --- a/src/mp4box/vpcc.rs +++ b/src/mp4box/vpcc.rs @@ -23,9 +23,7 @@ impl VpccBox { } impl Mp4Box for VpccBox { - fn box_type(&self) -> BoxType { - BoxType::VpccBox - } + const TYPE: BoxType = BoxType::VpccBox; fn box_size(&self) -> u64 { HEADER_SIZE + HEADER_EXT_SIZE + 8 @@ -40,22 +38,20 @@ impl Mp4Box for VpccBox { } } -impl ReadBox<&mut R> for VpccBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; - let (version, flags) = read_box_header_ext(reader)?; +impl BlockReader for VpccBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (version, flags) = read_box_header_ext(reader); - let profile: u8 = reader.read_u8()?; - let level: u8 = reader.read_u8()?; + let profile: u8 = reader.get_u8(); + let level: u8 = reader.get_u8(); let (bit_depth, chroma_subsampling, video_full_range_flag) = { - let b = reader.read_u8()?; + let b = reader.get_u8(); (b >> 4, b << 4 >> 5, b & 0x01 == 1) }; - let transfer_characteristics: u8 = reader.read_u8()?; - let matrix_coefficients: u8 = reader.read_u8()?; - let codec_initialization_data_size: u16 = reader.read_u16::()?; - skip_bytes_to(reader, start + size)?; + let transfer_characteristics: u8 = reader.get_u8(); + let matrix_coefficients: u8 = reader.get_u8(); + let codec_initialization_data_size: u16 = reader.get_u16(); Ok(Self { version, @@ -71,12 +67,16 @@ impl ReadBox<&mut R> for VpccBox { codec_initialization_data_size, }) } + + fn size_hint() -> usize { + 11 + } } impl WriteBox<&mut W> for VpccBox { fn write_box(&self, writer: &mut W) -> Result { let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + BoxHeader::new(Self::TYPE, size).write(writer)?; write_box_header_ext(writer, self.version, self.flags)?; @@ -100,7 +100,6 @@ impl WriteBox<&mut W> for VpccBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; #[test] fn test_vpcc() { @@ -121,12 +120,12 @@ mod tests { src_box.write_box(&mut buf).unwrap(); assert_eq!(buf.len(), src_box.box_size() as usize); - let mut reader = Cursor::new(&buf); - let header = BoxHeader::read(&mut reader).unwrap(); - assert_eq!(header.name, BoxType::VpccBox); + let mut reader = buf.as_slice(); + let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap(); + assert_eq!(header.kind, BoxType::VpccBox); assert_eq!(src_box.box_size(), header.size); - let dst_box = VpccBox::read_box(&mut reader, header.size).unwrap(); + let dst_box = VpccBox::read_block(&mut reader).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/stream.rs b/src/stream.rs new file mode 100644 index 0000000..8a0c1e8 --- /dev/null +++ b/src/stream.rs @@ -0,0 +1,19 @@ +use std::{ + pin::Pin, + task::{Context, Poll}, +}; + +use futures::Stream; + +use crate::Error; + +pub struct Mp4Frame {} + +pub struct Mp4Stream {} +impl Stream for Mp4Stream { + type Item = Result; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + todo!() + } +} diff --git a/src/track.rs b/src/track.rs index fae2c0c..7ecf78b 100644 --- a/src/track.rs +++ b/src/track.rs @@ -89,7 +89,7 @@ impl From for TrackConfig { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Mp4Track { pub trak: TrakBox, pub trafs: Vec, @@ -261,7 +261,7 @@ impl Mp4Track { pub fn sequence_parameter_set(&self) -> Result<&[u8]> { if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 { - match avc1.avcc.sequence_parameter_sets.get(0) { + match avc1.avcc.sequence_parameter_sets.first() { Some(nal) => Ok(nal.bytes.as_ref()), None => Err(Error::EntryInStblNotFound( self.track_id(), @@ -276,7 +276,7 @@ impl Mp4Track { pub fn picture_parameter_set(&self) -> Result<&[u8]> { if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 { - match avc1.avcc.picture_parameter_sets.get(0) { + match avc1.avcc.picture_parameter_sets.first() { Some(nal) => Ok(nal.bytes.as_ref()), None => Err(Error::EntryInStblNotFound( self.track_id(), @@ -386,7 +386,7 @@ impl Mp4Track { None } - fn sample_size(&self, sample_id: u32) -> Result { + pub(crate) fn sample_size(&self, sample_id: u32) -> Result { if !self.trafs.is_empty() { if let Some((traf_idx, sample_idx)) = self.find_traf_idx_and_sample_idx(sample_id) { if let Some(size) = self.trafs[traf_idx] @@ -499,7 +499,7 @@ impl Mp4Track { } } - fn sample_time(&self, sample_id: u32) -> Result<(u64, u32)> { + pub(crate) fn sample_time(&self, sample_id: u32) -> Result<(u64, u32)> { if !self.trafs.is_empty() { if let Some((traf_idx, sample_idx)) = self.find_traf_idx_and_sample_idx(sample_id) { let traf = &self.trafs[traf_idx]; @@ -567,7 +567,7 @@ impl Mp4Track { } } - fn sample_rendering_offset(&self, sample_id: u32) -> i32 { + pub(crate) fn sample_rendering_offset(&self, sample_id: u32) -> i32 { if !self.trafs.is_empty() { if let Some((traf_idx, sample_idx)) = self.find_traf_idx_and_sample_idx(sample_id) { if let Some(cts) = self.trafs[traf_idx] @@ -587,7 +587,7 @@ impl Mp4Track { 0 } - fn is_sync_sample(&self, sample_id: u32) -> bool { + pub(crate) fn is_sync_sample(&self, sample_id: u32) -> bool { if !self.trafs.is_empty() { if let Some((_, sample_idx)) = self.find_traf_idx_and_sample_idx(sample_id) { return sample_idx == 0; @@ -613,6 +613,7 @@ impl Mp4Track { Err(Error::EntryInStblNotFound(_, _, _)) => return Ok(None), Err(err) => return Err(err), }; + let sample_size = match self.sample_size(sample_id) { Ok(size) => size, Err(Error::EntryInStblNotFound(_, _, _)) => return Ok(None), diff --git a/tests/lib.rs b/tests/lib.rs index 7c81f95..ca9fd86 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1,6 +1,5 @@ use mp4::{ - AudioObjectType, AvcProfile, ChannelConfig, MediaType, Metadata, Mp4Reader, SampleFreqIndex, - TrackType, + AudioObjectType, AvcProfile, ChannelConfig, MediaType, Mp4Reader, SampleFreqIndex, TrackType, }; use std::fs::{self, File}; use std::io::BufReader;