diff --git a/Cargo.toml b/Cargo.toml index ec35a0d4..6f1d13be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,9 +19,19 @@ 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" +glob = "0.3.2" + [[bench]] name = "bench_main" diff --git a/assets/videos/bc9746a24516b39f13f382636791f6081722702__95218.mp4f b/assets/videos/bc9746a24516b39f13f382636791f6081722702__95218.mp4f new file mode 100644 index 00000000..a13d039a Binary files /dev/null and b/assets/videos/bc9746a24516b39f13f382636791f6081722702__95218.mp4f differ diff --git a/assets/videos/cosmoc.mp4f b/assets/videos/cosmoc.mp4f new file mode 100644 index 00000000..d397b81b Binary files /dev/null and b/assets/videos/cosmoc.mp4f differ diff --git a/benches/bench_main.rs b/benches/bench_main.rs index 99e1ab52..eccb9b24 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/mp4copy.rs b/examples/mp4copy.rs deleted file mode 100644 index 98d1ba80..00000000 --- a/examples/mp4copy.rs +++ /dev/null @@ -1,93 +0,0 @@ -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, -}; - -fn main() { - let args: Vec = env::args().collect(); - - 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); - } -} - -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 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 {}), - }; - - 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)?; - } - - 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()?; - - Ok(()) -} diff --git a/examples/mp4dump.rs b/examples/mp4dump.rs deleted file mode 100644 index 6a97d9a0..00000000 --- a/examples/mp4dump.rs +++ /dev/null @@ -1,142 +0,0 @@ -use std::env; -use std::fs::File; -use std::io::prelude::*; -use std::io::{self, BufReader}; -use std::path::Path; - -use mp4::{Mp4Box, Result}; - -fn main() { - let args: Vec = env::args().collect(); - - if args.len() < 2 { - println!("Usage: mp4dump "); - std::process::exit(1); - } - - 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)?; - - // print out boxes - for b in boxes.iter() { - println!("[{}] size={} {}", b.name, b.size, b.summary); - } - - Ok(()) -} - -#[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)?; - - // collect known boxes - let mut boxes = vec![ - build_box(&mp4.ftyp), - build_box(&mp4.moov), - build_box(&mp4.moov.mvhd), - ]; - - if let Some(ref mvex) = &mp4.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.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.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.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) -} - -fn build_box(m: &M) -> Box { - Box { - name: m.box_type().to_string(), - size: m.box_size(), - summary: m.summary().unwrap(), - indent: 0, - } -} diff --git a/examples/mp4info.rs b/examples/mp4info.rs deleted file mode 100644 index 00de8cee..00000000 --- a/examples/mp4info.rs +++ /dev/null @@ -1,143 +0,0 @@ -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}; - -fn main() { - let args: Vec = env::args().collect(); - - if args.len() < 2 { - println!("Usage: mp4info "); - std::process::exit(1); - } - - 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); - - 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!("Movie:"); - println!(" version: {}", mp4.moov.mvhd.version); - println!( - " creation time: {}", - creation_time(mp4.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!( - " 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 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(), - }; - - 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 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 - } -} diff --git a/examples/mp4sample.rs b/examples/mp4sample.rs index 6495daf9..be0f58e8 100644 --- a/examples/mp4sample.rs +++ b/examples/mp4sample.rs @@ -1,12 +1,13 @@ -use std::env; -use std::fs::File; use std::io::prelude::*; -use std::io::{self, BufReader}; use std::path::Path; +use std::{env, io}; -use mp4::Result; +use mp4::{error::MemoryStorageError, TrackType}; +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 +15,47 @@ 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(); - let reader = BufReader::new(f); - - let mut mp4 = mp4::Mp4Reader::read_header(reader, size)?; - - for track_id in mp4.tracks().keys().copied().collect::>() { - let sample_count = mp4.sample_count(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); - - 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, - ); - } - } +async fn samples>(filename: &P) -> Result<(), mp4::Error> { + let f = File::open(filename).await?; + let mut reader = BufReader::new(f); + + let mut mp4_file = mp4::Mp4File::new(&mut reader); + println!("streaming possible: {}", mp4_file.read_header().await?); + + let mut keys = mp4_file + .tracks + .iter() + .filter(|&(_, v)| v.track_type() == TrackType::Video) + .map(|(k, _)| *k); + + let track_id = keys.next().unwrap(); + let samples_len = mp4_file.tracks.get(&track_id).unwrap().samples.len(); + + for idx in 0..samples_len { + let samp = mp4_file.tracks.get(&track_id).unwrap().samples[idx].clone(); + + let data = mp4_file + .read_sample_data(track_id, idx) + .await? + .map(|x| x.slice(0..16)); + + println!( + "[{} {} {}] {} - <{}> {} +{} {:?}", + idx + 1, + samp.chunk_id, + samp.offset, + samp.is_sync, + samp.size, + samp.start_time, + samp.rendering_offset, + data.as_deref() + ); } + Ok(()) } diff --git a/examples/mp4writer.rs b/examples/mp4writer.rs deleted file mode 100644 index 0ab515b9..00000000 --- a/examples/mp4writer.rs +++ /dev/null @@ -1,24 +0,0 @@ -use mp4::{Mp4Config, Mp4Writer}; -use std::io::Cursor; - -fn main() -> mp4::Result<()> { - let config = Mp4Config { - major_brand: str::parse("isom").unwrap(), - minor_version: 512, - compatible_brands: vec![ - str::parse("isom").unwrap(), - str::parse("iso2").unwrap(), - str::parse("avc1").unwrap(), - str::parse("mp41").unwrap(), - ], - timescale: 1000, - }; - - let data = Cursor::new(Vec::::new()); - let mut writer = Mp4Writer::write_start(data, &config)?; - writer.write_end()?; - - let data: Vec = writer.into_writer().into_inner(); - println!("{:?}", data); - Ok(()) -} diff --git a/examples/mpeg_aac_decoder/.gitignore b/examples/mpeg_aac_decoder/.gitignore deleted file mode 100644 index 1b72444a..00000000 --- a/examples/mpeg_aac_decoder/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/Cargo.lock -/target diff --git a/examples/mpeg_aac_decoder/Cargo.toml b/examples/mpeg_aac_decoder/Cargo.toml deleted file mode 100644 index 97426c77..00000000 --- a/examples/mpeg_aac_decoder/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "mpeg_aac_decoder" -version = "0.1.0" -edition = "2018" - -[dependencies] -mp4 = "0.8.1" -fdk-aac = "0.4.0" -rodio = { version = "0.13.0", default-features = false } diff --git a/examples/mpeg_aac_decoder/audio_aac.m4a b/examples/mpeg_aac_decoder/audio_aac.m4a deleted file mode 100644 index 5c3f57cc..00000000 Binary files a/examples/mpeg_aac_decoder/audio_aac.m4a and /dev/null differ diff --git a/examples/mpeg_aac_decoder/src/main.rs b/examples/mpeg_aac_decoder/src/main.rs deleted file mode 100644 index ee35f303..00000000 --- a/examples/mpeg_aac_decoder/src/main.rs +++ /dev/null @@ -1,237 +0,0 @@ -use fdk_aac::dec::{Decoder, DecoderError, Transport}; -use rodio::{OutputStream, Sink, Source}; -use std::fs::File; -use std::io::{BufReader, Read, Seek}; -use std::ops::Range; -use std::time::Duration; - -fn main() { - let path = "audio_aac.m4a"; - let file = File::open(path).expect("Error opening file"); - - let metadata = file.metadata().expect("Error getting file metadata"); - let size = metadata.len(); - let buf = BufReader::new(file); - - let decoder = MpegAacDecoder::new(buf, size).expect("Error creating decoder"); - - let output_stream = OutputStream::try_default(); - let (_stream, handle) = output_stream.expect("Error creating output stream"); - let sink = Sink::try_new(&handle).expect("Error creating sink"); - - sink.append(decoder); - sink.play(); - sink.set_volume(0.5); - sink.sleep_until_end(); -} - -pub struct MpegAacDecoder -where - R: Read + Seek, -{ - mp4_reader: mp4::Mp4Reader, - decoder: Decoder, - current_pcm_index: usize, - current_pcm: Vec, - track_id: u32, - position: u32, -} - -impl MpegAacDecoder -where - R: Read + Seek, -{ - pub fn new(reader: R, size: u64) -> Result, &'static str> { - let decoder = Decoder::new(Transport::Adts); - let mp4 = mp4::Mp4Reader::read_header(reader, size).or(Err("Error reading MPEG header"))?; - let mut track_id: Option = None; - { - for track in mp4.tracks().iter() { - let media_type = track.media_type().or(Err("Error getting media type"))?; - match media_type { - mp4::MediaType::AAC => { - track_id = Some(track.track_id()); - break; - } - _ => {} - } - } - } - match track_id { - Some(track_id) => { - return Ok(MpegAacDecoder { - mp4_reader: mp4, - decoder: decoder, - current_pcm_index: 0, - current_pcm: Vec::new(), - track_id: track_id, - position: 1, - }); - } - None => { - return Err("No aac track found"); - } - } - } -} - -impl Iterator for MpegAacDecoder -where - R: Read + Seek, -{ - type Item = i16; - fn next(&mut self) -> Option { - if self.current_pcm_index == self.current_pcm.len() { - let mut pcm = vec![0; 8192]; - let result = match self.decoder.decode_frame(&mut self.current_pcm) { - Err(DecoderError::NOT_ENOUGH_BITS) => { - let sample_result = self.mp4_reader.read_sample(self.track_id, self.position); - let sample = sample_result.expect("Error reading sample")?; - let tracks = self.mp4_reader.tracks(); - let track = tracks.get(self.track_id as usize - 1).expect("No track ID"); - let adts_header = construct_adts_header(track, &sample).expect("ADTS bytes"); - let adts_bytes = mp4::Bytes::copy_from_slice(&adts_header); - let bytes = [adts_bytes, sample.bytes].concat(); - self.position += 1; - let _bytes_read = match self.decoder.fill(&bytes) { - Ok(bytes_read) => bytes_read, - Err(_) => return None, - }; - self.decoder.decode_frame(&mut pcm) - } - val => val, - }; - if let Err(err) = result { - println!("DecoderError: {}", err); - return None; - } - let decoded_fram_size = self.decoder.decoded_frame_size(); - if decoded_fram_size < pcm.len() { - let _ = pcm.split_off(decoded_fram_size); - } - self.current_pcm = pcm; - self.current_pcm_index = 0; - } - let value = self.current_pcm[self.current_pcm_index]; - self.current_pcm_index += 1; - return Some(value); - } -} - -impl Source for MpegAacDecoder -where - R: Read + Seek, -{ - fn current_frame_len(&self) -> Option { - let frame_size: usize = self.decoder.decoded_frame_size(); - Some(frame_size) - } - fn channels(&self) -> u16 { - let num_channels: i32 = self.decoder.stream_info().numChannels; - num_channels as _ - } - fn sample_rate(&self) -> u32 { - let sample_rate: i32 = self.decoder.stream_info().sampleRate; - sample_rate as _ - } - fn total_duration(&self) -> Option { - return None; - } -} - -fn get_bits(byte: u16, range: Range) -> u16 { - let shaved_left = byte << range.start - 1; - let moved_back = shaved_left >> range.start - 1; - let shave_right = moved_back >> 16 - range.end; - return shave_right; -} - -fn get_bits_u8(byte: u8, range: Range) -> u8 { - let shaved_left = byte << range.start - 1; - let moved_back = shaved_left >> range.start - 1; - let shave_right = moved_back >> 8 - range.end; - return shave_right; -} - -pub fn construct_adts_header(track: &mp4::Mp4Track, sample: &mp4::Mp4Sample) -> Option> { - // B: Only support 0 (MPEG-4) - // D: Only support 1 (without CRC) - // byte7 and byte9 not included without CRC - let adts_header_length = 7; - - // AAAA_AAAA - let byte0 = 0b1111_1111; - - // AAAA_BCCD - let byte1 = 0b1111_0001; - - // EEFF_FFGH - let mut byte2 = 0b0000_0000; - let object_type = match track.audio_profile() { - Ok(mp4::AudioObjectType::AacMain) => 1, - Ok(mp4::AudioObjectType::AacLowComplexity) => 2, - Ok(mp4::AudioObjectType::AacScalableSampleRate) => 3, - Ok(mp4::AudioObjectType::AacLongTermPrediction) => 4, - Err(_) => return None, - }; - let adts_object_type = object_type - 1; - byte2 = (byte2 << 2) | adts_object_type; // EE - - let sample_freq_index = match track.sample_freq_index() { - Ok(mp4::SampleFreqIndex::Freq96000) => 0, - Ok(mp4::SampleFreqIndex::Freq88200) => 1, - Ok(mp4::SampleFreqIndex::Freq64000) => 2, - Ok(mp4::SampleFreqIndex::Freq48000) => 3, - Ok(mp4::SampleFreqIndex::Freq44100) => 4, - Ok(mp4::SampleFreqIndex::Freq32000) => 5, - Ok(mp4::SampleFreqIndex::Freq24000) => 6, - Ok(mp4::SampleFreqIndex::Freq22050) => 7, - Ok(mp4::SampleFreqIndex::Freq16000) => 8, - Ok(mp4::SampleFreqIndex::Freq12000) => 9, - Ok(mp4::SampleFreqIndex::Freq11025) => 10, - Ok(mp4::SampleFreqIndex::Freq8000) => 11, - Ok(mp4::SampleFreqIndex::Freq7350) => 12, - // 13-14 = reserved - // 15 = explicit frequency (forbidden in adts) - Err(_) => return None, - }; - byte2 = (byte2 << 4) | sample_freq_index; // FFFF - byte2 = (byte2 << 1) | 0b1; // G - - let channel_config = match track.channel_config() { - // 0 = for when channel config is sent via an inband PCE - Ok(mp4::ChannelConfig::Mono) => 1, - Ok(mp4::ChannelConfig::Stereo) => 2, - Ok(mp4::ChannelConfig::Three) => 3, - Ok(mp4::ChannelConfig::Four) => 4, - Ok(mp4::ChannelConfig::Five) => 5, - Ok(mp4::ChannelConfig::FiveOne) => 6, - Ok(mp4::ChannelConfig::SevenOne) => 7, - // 8-15 = reserved - Err(_) => return None, - }; - byte2 = (byte2 << 1) | get_bits_u8(channel_config, 6..6); // H - - // HHIJ_KLMM - let mut byte3 = 0b0000_0000; - byte3 = (byte3 << 2) | get_bits_u8(channel_config, 7..8); // HH - byte3 = (byte3 << 4) | 0b1111; // IJKL - - let frame_length = adts_header_length + sample.bytes.len() as u16; - byte3 = (byte3 << 2) | get_bits(frame_length, 3..5) as u8; // MM - - // MMMM_MMMM - let byte4 = get_bits(frame_length, 6..13) as u8; - - // MMMO_OOOO - let mut byte5 = 0b0000_0000; - byte5 = (byte5 << 3) | get_bits(frame_length, 14..16) as u8; - byte5 = (byte5 << 5) | 0b11111; // OOOOO - - // OOOO_OOPP - let mut byte6 = 0b0000_0000; - byte6 = (byte6 << 6) | 0b111111; // OOOOOO - byte6 = (byte6 << 2) | 0b00; // PP - - return Some(vec![byte0, byte1, byte2, byte3, byte4, byte5, byte6]); -} diff --git a/examples/simple.rs b/examples/simple.rs deleted file mode 100644 index 0dabeddd..00000000 --- a/examples/simple.rs +++ /dev/null @@ -1,27 +0,0 @@ -use std::env; -use std::fs::File; - -fn main() { - let args: Vec = env::args().collect(); - - if args.len() < 2 { - println!("Usage: simple "); - std::process::exit(1); - } - - let filename = &args[1]; - let f = File::open(filename).unwrap(); - let mp4 = mp4::read_mp4(f).unwrap(); - - println!("Major Brand: {}", mp4.major_brand()); - - for track in mp4.tracks().values() { - println!( - "Track: #{}({}) {} {}", - track.track_id(), - track.language(), - track.track_type().unwrap(), - track.box_type().unwrap(), - ); - } -} diff --git a/src/error.rs b/src/error.rs index 11690f0c..c281f394 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,28 +2,59 @@ use thiserror::Error; use crate::mp4box::BoxType; -#[derive(Error, Debug)] -pub enum Error { +#[derive(Debug, thiserror::Error)] +pub enum BoxError { #[error("{0}")] IoError(#[from] std::io::Error), + #[error("{0}")] InvalidData(&'static str), + #[error("{0} not found")] BoxNotFound(BoxType), + #[error("{0} and {1} not found")] Box2NotFound(BoxType, BoxType), - #[error("trak[{0}] not found")] - TrakNotFound(u32), + #[error("trak[{0}].{1} not found")] BoxInTrakNotFound(u32, BoxType), + #[error("traf[{0}].{1} not found")] BoxInTrafNotFound(u32, BoxType), + #[error("trak[{0}].stbl.{1} not found")] BoxInStblNotFound(u32, BoxType), + #[error("trak[{0}].stbl.{1}.entry[{2}] not found")] EntryInStblNotFound(u32, BoxType, u32), + #[error("traf[{0}].trun.{1}.entry[{2}] not found")] EntryInTrunNotFound(u32, BoxType, u32), + #[error("{0} version {1} is not supported")] UnsupportedBoxVersion(BoxType, u8), + + #[error("trak[{0}] not found")] + TrakNotFound(u32), +} + +#[derive(thiserror::Error, Debug)] +pub enum MemoryStorageError { + #[error("IoError: {0}")] + IoError(#[from] std::io::Error), + + #[error("data buffer with index {0} not found")] + DataBufferNotFound(usize), +} + +#[derive(Error, Debug)] +pub enum Error { + #[error("{0}")] + IoError(#[from] std::io::Error), + + #[error("box error: {0}")] + BoxError(#[from] BoxError), + + #[error("storage error: {0}")] + DataStorageError(E), } diff --git a/src/file.rs b/src/file.rs new file mode 100644 index 00000000..e877e339 --- /dev/null +++ b/src/file.rs @@ -0,0 +1,282 @@ +use bytes::Bytes; +use futures::Future; +use std::collections::{BTreeSet, HashMap}; +use std::iter::FromIterator; +use std::ops::Range; +use tokio::io::{AsyncRead, AsyncReadExt, AsyncSeek, AsyncSeekExt, SeekFrom}; + +use crate::error::{BoxError, MemoryStorageError}; +use crate::{BlockReader, BoxHeader, BoxType, EmsgBox, FtypBox, MoofBox, MoovBox}; +use crate::{Mp4Track, HEADER_SIZE}; + +pub trait DataStorage { + type Error; + type Id; + + fn save_data( + &mut self, + reader: &mut (impl AsyncRead + Unpin), + ) -> impl Future>; + + fn read_data( + &self, + id: &Self::Id, + range: Range, + ) -> impl Future>; +} + +#[derive(Default)] +pub struct MemoryStorage { + pub data: Vec, +} + +impl DataStorage for MemoryStorage { + type Error = MemoryStorageError; + type Id = usize; + + #[inline] + async fn save_data( + &mut self, + reader: &mut (impl AsyncRead + Unpin), + ) -> Result { + let mut buffer = Vec::new(); + let index = self.data.len(); + tokio::io::copy(reader, &mut buffer).await?; + self.data.push(buffer.into()); + + Ok(index) + } + + #[inline] + async fn read_data(&self, id: &Self::Id, range: Range) -> Result { + let buff = self + .data + .get(*id) + .ok_or(MemoryStorageError::DataBufferNotFound(*id))?; + + Ok(buff.slice(range.start as usize..range.end as usize)) + } +} + +enum DataBlockBody { + Memory(Bytes), + Reader, +} + +pub struct DataBlock { + kind: BoxType, + offset: u64, + size: u64, + buffer: DataBlockBody, +} + +pub struct Mp4File<'a, R> +where + R: AsyncRead + AsyncSeek + Unpin, +{ + pub ftyp: Option, + pub emsgs: Vec, + pub tracks: HashMap, + pub reader: &'a mut R, + pub offsets: BTreeSet, + pub data_blocks: Vec, +} + +impl<'a, R> Mp4File<'a, R> +where + R: AsyncRead + Unpin + AsyncSeek + 'a, +{ + pub fn new(reader: &'a mut R) -> Self { + Self { + ftyp: None, + emsgs: Vec::new(), + tracks: HashMap::new(), + reader, + offsets: BTreeSet::new(), + data_blocks: Vec::new(), + } + } +} + +impl<'a, R> Mp4File<'a, R> +where + R: AsyncRead + Unpin + AsyncSeek + 'a, +{ + pub async fn read_header(&mut self) -> Result { + let mut buff = Vec::with_capacity(8192); + let mut got_moov = false; + let mut offset = 0u64; + + while let Some(BoxHeader { kind, size: mut s }) = + BoxHeader::read(&mut self.reader, &mut offset).await? + { + if s >= HEADER_SIZE { + s -= HEADER_SIZE; // size without header + } + match kind { + BoxType::FtypBox => { + println!("ftyp"); + + if buff.len() < s as usize { + buff.resize(s as usize, 0); + } + self.reader.read_exact(&mut buff[0..s as usize]).await?; + offset += s; + + self.ftyp = Some(FtypBox::read_block(&mut &buff[0..s as usize])?); + } + + BoxType::MoovBox => { + println!("moov"); + + if buff.len() < s as usize { + buff.resize(s as usize, 0); + } + + self.reader.read_exact(&mut buff[0..s as usize]).await?; + offset += s; + + got_moov = true; + self.set_moov(MoovBox::read_block(&mut &buff[0..s as usize])?)?; + } + + BoxType::MoofBox => { + println!("moof"); + + if buff.len() < s as usize { + buff.resize(s as usize, 0); + } + + let begin_offset = offset; + self.reader.read_exact(&mut buff[0..s as usize]).await?; + offset += s; + + self.add_moof( + begin_offset, + MoofBox::read_block(&mut &buff[0..s as usize])?, + )?; + } + + BoxType::EmsgBox => { + println!("emsg"); + + if buff.len() < s as usize { + buff.resize(s as usize, 0); + } + + self.reader.read_exact(&mut buff[0..s as usize]).await?; + offset += s; + + self.emsgs + .push(EmsgBox::read_block(&mut &buff[0..s as usize])?); + } + + BoxType::MdatBox => { + println!("mdat"); + self.save_box(BoxType::MdatBox, s, offset).await?; + offset += s; + } + + bt => { + println!("{}", bt); + + self.skip_box(bt, s).await?; + offset += s; + } + } + println!("\n"); + } + + Ok(got_moov) + } + + async fn skip_box(&mut self, bt: BoxType, size: u64) -> Result<(), BoxError> { + println!("skip {:?}", bt); + self.reader.seek(SeekFrom::Current(size as _)).await?; + Ok(()) + } + + async fn save_box(&mut self, kind: BoxType, size: u64, offset: u64) -> Result<(), BoxError> { + println!("data_block {:?} {} - {}", kind, offset, offset + size); + + if size < 128 * 1024 * 1024 { + let mut buffer = Vec::new(); + tokio::io::copy(&mut self.reader.take(size), &mut buffer).await?; + self.data_blocks.push(DataBlock { + kind, + offset, + size, + buffer: DataBlockBody::Memory(buffer.into()), + }); + } else { + self.skip_box(kind, size).await?; + self.data_blocks.push(DataBlock { + kind, + offset, + size, + buffer: DataBlockBody::Reader, + }); + } + + Ok(()) + } + + fn set_moov(&mut self, moov: MoovBox) -> Result<(), BoxError> { + for trak in moov.traks { + self.tracks + .insert(trak.tkhd.track_id, Mp4Track::new(trak, &mut self.offsets)?); + } + + Ok(()) + } + + fn add_moof(&mut self, offset: u64, moof: MoofBox) -> Result<(), BoxError> { + 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(offset, moof.mfhd.sequence_number, traf, &mut self.offsets) + } else { + return Err(BoxError::TrakNotFound(track_id).into()); + } + } + + Ok(()) + } + + #[inline] + pub async fn read_sample_data( + &mut self, + track_id: u32, + sample_idx: usize, + ) -> Result, BoxError> { + let Some(track) = self.tracks.get(&track_id) else { + return Ok(None); + }; + + let Some(sample) = track.samples.get(sample_idx) else { + return Ok(None); + }; + + for block in &self.data_blocks { + let range = block.offset..block.offset + block.size; + + if range.contains(&sample.offset) { + return Ok(Some(match &block.buffer { + DataBlockBody::Memory(mem) => { + let offset = sample.offset - block.offset; + mem.slice(offset as usize..offset as usize + sample.size as usize) + } + DataBlockBody::Reader => { + let mut buff = vec![0u8; sample.size as _]; + self.reader.seek(SeekFrom::Start(sample.offset)).await?; + self.reader.read_exact(&mut buff).await?; + Bytes::from_iter(buff) + } + })); + } + } + + Ok(None) + } +} diff --git a/src/lib.rs b/src/lib.rs index 92319e18..228cff2a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,77 +1,8 @@ -//! `mp4` is a Rust library to read and write ISO-MP4 files. -//! -//! This package contains MPEG-4 specifications defined in parts: -//! * ISO/IEC 14496-12 - ISO Base Media File Format (QuickTime, MPEG-4, etc) -//! * ISO/IEC 14496-14 - MP4 file format -//! * ISO/IEC 14496-17 - Streaming text format -//! -//! See: [mp4box] for supported MP4 atoms. -//! -//! ### Example -//! -//! ``` -//! use std::fs::File; -//! use std::io::{BufReader}; -//! use mp4::{Result}; -//! -//! fn main() -> Result<()> { -//! let f = File::open("tests/samples/minimal.mp4").unwrap(); -//! let size = f.metadata()?.len(); -//! let reader = BufReader::new(f); -//! -//! let mp4 = mp4::Mp4Reader::read_header(reader, size)?; -//! -//! // Print boxes. -//! println!("major brand: {}", mp4.ftyp.major_brand); -//! println!("timescale: {}", mp4.moov.mvhd.timescale); -//! -//! // Use available methods. -//! println!("size: {}", mp4.size()); -//! -//! let mut compatible_brands = String::new(); -//! for brand in mp4.compatible_brands().iter() { -//! compatible_brands.push_str(&brand.to_string()); -//! compatible_brands.push_str(","); -//! } -//! println!("compatible brands: {}", compatible_brands); -//! println!("duration: {:?}", mp4.duration()); -//! -//! // Track info. -//! for track in mp4.tracks().values() { -//! println!( -//! "track: #{}({}) {} : {}", -//! track.track_id(), -//! track.language(), -//! track.track_type()?, -//! track.box_type()?, -//! ); -//! } -//! Ok(()) -//! } -//! ``` -//! -//! See [examples] for more examples. -//! -//! # Installation -//! -//! Add the following to your `Cargo.toml` file: -//! -//! ```toml -//! [dependencies] -//! mp4 = "0.7.0" -//! ``` -//! -//! [mp4box]: https://github.com/alfg/mp4-rust/blob/master/src/mp4box/mod.rs -//! [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 mod error; +use error::BoxError; pub use error::Error; -pub type Result = std::result::Result; +pub type Result = std::result::Result; mod types; pub use types::*; @@ -79,18 +10,18 @@ pub use types::*; mod mp4box; pub use mp4box::*; -mod track; -pub use track::{Mp4Track, TrackConfig}; - -mod reader; -pub use reader::Mp4Reader; - -mod writer; -pub use writer::{Mp4Config, Mp4Writer}; +mod file; -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) -} +mod track; +pub use track::Mp4Track; + +pub use file::*; +// mod async_reader; +// pub use async_reader::{AsyncMp4Reader, Mp4Header}; + +// 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 f386f9a6..067f3a0e 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() } -} -impl Mp4Box for Avc1Box { fn box_type(&self) -> BoxType { - self.get_type() + BoxType::Avc1Box } +} + +impl Mp4Box for Avc1Box { + 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)?; - - reader.read_u32::()?; // reserved - reader.read_u16::()?; // reserved - let data_reference_index = reader.read_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 - - 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)?; - - skip_bytes_to(reader, start + size)?; - - return Ok(Avc1Box { - data_reference_index, - width, - height, - horizresolution, - vertresolution, - frame_count, - depth, - avcc, - }); - } else { - skip_bytes_to(reader, current + s)?; - } - } +impl BlockReader for Avc1Box { + 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.get_u32(); // pre-defined, reserved + reader.get_u64(); // pre-defined + reader.get_u32(); // pre-defined + + let width = reader.get_u16(); + let height = reader.get_u16(); + + let horizresolution = FixedPointU16::new_raw(reader.get_u32()); + let vertresolution = FixedPointU16::new_raw(reader.get_u32()); + + 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,10 +298,9 @@ impl NalUnit { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; - #[test] - fn test_avc1() { + #[tokio::test] + async fn test_avc1() { let src_box = Avc1Box { data_reference_index: 1, width: 320, @@ -343,12 +330,14 @@ 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(&mut buf.as_slice(), &mut 0) + .await + .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[8..]).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/mp4box/co64.rs b/src/mp4box/co64.rs index 978137e9..4191d197 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::*; @@ -14,6 +14,26 @@ pub struct Co64Box { pub entries: Vec, } +impl<'a> IntoIterator for &'a Co64Box { + type Item = u64; + type IntoIter = std::iter::Copied>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.entries.iter().copied() + } +} + +impl IntoIterator for Co64Box { + type Item = u64; + type IntoIter = std::vec::IntoIter; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.entries.into_iter() + } +} + impl Co64Box { pub fn get_type(&self) -> BoxType { BoxType::Co64Box @@ -25,9 +45,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 +61,42 @@ 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 - { - return Err(Error::InvalidData( + let entry_count = reader.get_u32(); + println!("{}", reader.remaining() / entry_size); + println!("entry_count: {}", entry_count); + if entry_count as usize > reader.remaining() / entry_size { + return Err(BoxError::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,10 +113,9 @@ impl WriteBox<&mut W> for Co64Box { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; - #[test] - fn test_co64() { + #[tokio::test] + async fn test_co64() { let src_box = Co64Box { version: 0, flags: 0, @@ -112,12 +125,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::Co64Box); + let mut reader = buf.as_slice(); + let header = BoxHeader::read(&mut reader, &mut 0).await.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 reader).unwrap(); assert_eq!(src_box, dst_box); } } diff --git a/src/mp4box/ctts.rs b/src/mp4box/ctts.rs index 673e8c92..4a8e04cf 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::*; @@ -24,16 +24,14 @@ impl CttsBox { } } -#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize)] pub struct CttsEntry { pub sample_count: u32, pub sample_offset: i32, } 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 - { - return Err(Error::InvalidData( + + if entry_count as usize > reader.remaining() / entry_size { + return Err(BoxError::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,10 +103,9 @@ impl WriteBox<&mut W> for CttsBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; - #[test] - fn test_ctts() { + #[tokio::test] + async fn test_ctts() { let src_box = CttsBox { version: 0, flags: 0, @@ -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(&mut reader, &mut 0).await.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 19b5c77c..7270aa67 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)?; - - let data_type = DataType::try_from(reader.read_u32::()?)?; +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 - 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: reader.collect(reader.remaining())?, + data_type, + }) + } - 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,10 +73,9 @@ impl WriteBox<&mut W> for DataBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; - #[test] - fn test_data() { + #[tokio::test] + async fn test_data() { let src_box = DataBox { data_type: DataType::Text, data: b"test_data".to_vec(), @@ -91,28 +84,28 @@ 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(&mut reader, &mut 0).await.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); } - #[test] - fn test_data_empty() { + #[tokio::test] + async fn test_data_empty() { let src_box = DataBox::default(); let mut buf = Vec::new(); 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(&mut reader, &mut 0).await.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 e365e4ae..09d7883f 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 9077bb17..a5d5228b 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)?; - - 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)?; +impl BlockReader for EdtsBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + Ok(EdtsBox { + elst: reader.try_find_box::()?, + }) + } - 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 297fb63a..70e302b4 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)?; - - let (version, flags) = read_box_header_ext(reader)?; +impl BlockReader for ElstBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + 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 - { - return Err(Error::InvalidData( + + if entry_count as usize > reader.remaining() / entry_size { + return Err(BoxError::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,10 +134,9 @@ impl WriteBox<&mut W> for ElstBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; - #[test] - fn test_elst32() { + #[tokio::test] + async fn test_elst32() { let src_box = ElstBox { version: 0, flags: 0, @@ -165,17 +151,17 @@ 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(&mut reader, &mut 0).await.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); } - #[test] - fn test_elst64() { + #[tokio::test] + async fn test_elst64() { let src_box = ElstBox { version: 1, flags: 0, @@ -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(&mut reader, &mut 0).await.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 f68ba3af..0ad3f864 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")), + _ => return Err(BoxError::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 { @@ -143,7 +136,7 @@ impl WriteBox<&mut W> for EmsgBox { write_null_terminated_str(writer, &self.scheme_id_uri)?; write_null_terminated_str(writer, &self.value)?; } - _ => return Err(Error::InvalidData("version must be 0 or 1")), + _ => return Err(BoxError::InvalidData("version must be 0 or 1")), } for &byte in &self.message_data { @@ -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,14 +157,13 @@ fn write_null_terminated_str(writer: &mut W, string: &str) -> Result<( #[cfg(test)] mod tests { - use std::io::Cursor; use crate::mp4box::BoxHeader; use super::*; - #[test] - fn test_emsg_version0() { + #[tokio::test] + async fn test_emsg_version0() { let src_box = EmsgBox { version: 0, flags: 0, @@ -204,17 +180,17 @@ 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(&mut reader, &mut 0).await.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); } - #[test] - fn test_emsg_version1() { + #[tokio::test] + async fn test_emsg_version1() { let src_box = EmsgBox { version: 1, flags: 0, @@ -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(&mut reader, &mut 0).await.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 789cd4ef..e10d3587 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() - 8) / 4; // 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,10 +90,9 @@ impl WriteBox<&mut W> for FtypBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; - #[test] - fn test_ftyp() { + #[tokio::test] + async fn test_ftyp() { let src_box = FtypBox { major_brand: str::parse("isom").unwrap(), minor_version: 0, @@ -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(&mut reader, &mut 0).await.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 b9d86a9a..5d55f9ef 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)?; - - let (version, flags) = read_box_header_ext(reader)?; +impl BlockReader for HdlrBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (version, flags) = read_box_header_ext(reader); - reader.read_u32::()?; // pre-defined - let handler = reader.read_u32::()?; + reader.get_u32(); // pre-defined - skip_bytes(reader, 12)?; // reserved + let handler = reader.get_u32(); - 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,10 +88,9 @@ impl WriteBox<&mut W> for HdlrBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; - #[test] - fn test_hdlr() { + #[tokio::test] + async fn test_hdlr() { let src_box = HdlrBox { version: 0, flags: 0, @@ -114,17 +101,17 @@ 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(&mut reader, &mut 0).await.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); } - #[test] - fn test_hdlr_empty() { + #[tokio::test] + async fn test_hdlr_empty() { let src_box = HdlrBox { version: 0, flags: 0, @@ -135,17 +122,17 @@ 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(&mut reader, &mut 0).await.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); } - #[test] - fn test_hdlr_extra() { + #[tokio::test] + async fn test_hdlr_extra() { let real_src_box = HdlrBox { version: 0, flags: 0, @@ -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(&mut reader, &mut 0).await.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 3070fb81..d8e08950 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)?; - - reader.read_u32::()?; // reserved - reader.read_u16::()?; // reserved - let data_reference_index = reader.read_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 - - 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)?; - - skip_bytes_to(reader, start + size)?; - - Ok(Hev1Box { - data_reference_index, - width, - height, - horizresolution, - vertresolution, - frame_count, - depth, - hvcc, - }) - } else { - Err(Error::InvalidData("hvcc not found")) - } +impl BlockReader for Hev1Box { + 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.get_u32(); // pre-defined, reserved + reader.get_u64(); // pre-defined + reader.get_u32(); // pre-defined + + let width = reader.get_u16(); + let height = reader.get_u16(); + + let horizresolution = FixedPointU16::new_raw(reader.get_u32()); + let vertresolution = FixedPointU16::new_raw(reader.get_u32()); + + 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 @@ -209,7 +200,7 @@ impl Mp4Box for HvcCBox { } fn summary(&self) -> Result { - Ok(format!("configuration_version={} general_profile_space={} general_tier_flag={} general_profile_idc={} general_profile_compatibility_flags={} general_constraint_indicator_flag={} general_level_idc={} min_spatial_segmentation_idc={} parallelism_type={} chroma_format_idc={} bit_depth_luma_minus8={} bit_depth_chroma_minus8={} avg_frame_rate={} constant_frame_rate={} num_temporal_layers={} temporal_id_nested={} length_size_minus_one={}", + Ok(format!("configuration_version={} general_profile_space={} general_tier_flag={} general_profile_idc={} general_profile_compatibility_flags={} general_constraint_indicator_flag={} general_level_idc={} min_spatial_segmentation_idc={} parallelism_type={} chroma_format_idc={} bit_depth_luma_minus8={} bit_depth_chroma_minus8={} avg_frame_rate={} constant_frame_rate={} num_temporal_layers={} temporal_id_nested={} length_size_minus_one={}", self.configuration_version, self.general_profile_space, self.general_tier_flag, @@ -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 params = reader.read_u8()?; + let general_profile_compatibility_flags = reader.get_u32(); + let general_constraint_indicator_flag = reader.get_u48(); + + 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(BoxError::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(BoxError::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,10 +369,9 @@ impl WriteBox<&mut W> for HvcCBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; - #[test] - fn test_hev1() { + #[tokio::test] + async fn test_hev1() { let src_box = Hev1Box { data_reference_index: 1, width: 320, @@ -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(&mut reader, &mut 0).await.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 d0292a31..c8c61b70 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 { @@ -20,17 +19,16 @@ impl IlstBox { pub fn get_size(&self) -> u64 { let mut size = HEADER_SIZE; + let ilst_item_header_size = HEADER_SIZE; for item in self.items.values() { - size += item.get_size(); + size += ilst_item_header_size + item.get_size(); } size } } 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 +44,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)?); + if let Some(day) = bx.inner.try_find_box::()? { + items.insert(MetadataKey::Year, day); + } } + BoxType::CovrBox => { - items.insert(MetadataKey::Poster, IlstItemBox::read_box(reader, s)?); + if let Some(cover) = bx.inner.try_find_box::()? { + items.insert(MetadataKey::Poster, cover); + } } + BoxType::DescBox => { - items.insert(MetadataKey::Summary, IlstItemBox::read_box(reader, s)?); + if let Some(summary) = bx.inner.try_find_box::()? { + items.insert(MetadataKey::Summary, summary); + } } - _ => { - // XXX warn!() - skip_box(reader, s)?; - } - } - current = reader.stream_position()?; + _ => continue, + } } - - skip_bytes_to(reader, start + size)?; - + // dbg!(&items); 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,64 +98,13 @@ 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)?; - } - 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)?; - } - } + let size = HEADER_SIZE + value.box_size(); // Size of IlstItem + DataBox - current = reader.stream_position()?; + BoxHeader::new(name, size).write(writer)?; + value.write_box(writer)?; } - - if data.is_none() { - return Err(Error::BoxNotFound(BoxType::DataBox)); - } - - skip_bytes_to(reader, start + size)?; - - Ok(IlstItemBox { - data: data.unwrap(), - }) + Ok(size) } } @@ -183,18 +126,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 +146,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(), - }, + + #[tokio::test] + async fn test_ilst() { + 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,28 +167,28 @@ 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(&mut reader, &mut 0).await.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); } - #[test] - fn test_ilst_empty() { + #[tokio::test] + async fn test_ilst_empty() { let src_box = IlstBox::default(); let mut buf = Vec::new(); 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(&mut reader, &mut 0).await.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 31c65a80..212e8a9f 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")); + return Err(BoxError::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)?; @@ -128,7 +127,7 @@ impl WriteBox<&mut W> for MdhdBox { writer.write_u32::(self.timescale)?; writer.write_u32::(self.duration as u32)?; } else { - return Err(Error::InvalidData("version must be 0 or 1")); + return Err(BoxError::InvalidData("version must be 0 or 1")); } let language_code = language_code(&self.language); @@ -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); @@ -181,8 +179,8 @@ mod tests { test_language_code("kor"); } - #[test] - fn test_mdhd32() { + #[tokio::test] + async fn test_mdhd32() { let src_box = MdhdBox { version: 0, flags: 0, @@ -196,17 +194,17 @@ 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(&mut reader, &mut 0).await.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); } - #[test] - fn test_mdhd64() { + #[tokio::test] + async fn test_mdhd64() { let src_box = MdhdBox { version: 0, flags: 0, @@ -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(&mut reader, &mut 0).await.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 423bf72e..a87db586 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)?; - - 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)?; +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 }) + } - 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 63c0246e..81ec69a0 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")); + return Err(BoxError::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)?; @@ -82,7 +81,7 @@ impl WriteBox<&mut W> for MehdBox { } else if self.version == 0 { writer.write_u32::(self.fragment_duration as u32)?; } else { - return Err(Error::InvalidData("version must be 0 or 1")); + return Err(BoxError::InvalidData("version must be 0 or 1")); } Ok(size) @@ -93,10 +92,9 @@ impl WriteBox<&mut W> for MehdBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; - #[test] - fn test_mehd32() { + #[tokio::test] + async fn test_mehd32() { let src_box = MehdBox { version: 0, flags: 0, @@ -106,17 +104,17 @@ 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(&mut reader, &mut 0).await.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); } - #[test] - fn test_mehd64() { + #[tokio::test] + async fn test_mehd64() { let src_box = MehdBox { version: 0, flags: 0, @@ -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(&mut reader, &mut 0).await.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 56ca8169..d222869f 100644 --- a/src/mp4box/meta.rs +++ b/src/mp4box/meta.rs @@ -1,4 +1,4 @@ -use std::io::{Read, Seek}; +use std::collections::HashMap; use serde::Serialize; @@ -26,6 +26,7 @@ pub enum MetaBox { } const MDIR: FourCC = FourCC { value: *b"mdir" }; +const MDTA: FourCC = FourCC { value: *b"mdta" }; impl MetaBox { pub fn get_type(&self) -> BoxType { @@ -54,9 +55,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,115 +85,48 @@ impl Default for MetaBox { } } -impl ReadBox<&mut R> for MetaBox { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; - - 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::()?); - if possible_hdlr == BoxType::HdlrBox { - // This file skipped the extended header! Go back to start. - reader.seek(SeekFrom::Current(-8))?; - } else { - // Looks like we actually have a bad version number or flags. - let v = (extended_header >> 24) as u8; - return Err(Error::UnsupportedBoxVersion(BoxType::MetaBox, v)); - } +impl BlockReader for MetaBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let extended_header = reader.peek_u32(); + if extended_header == 0 { + reader.skip(4); } - 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; - - match name { - BoxType::HdlrBox => { - hdlr = Some(HdlrBox::read_box(reader, s)?); - } - _ => { - // XXX warn!() - skip_box(reader, s)?; - } - } - - current = reader.stream_position()?; + let mut boxes = HashMap::new(); + while let Some(mut bx) = reader.get_box()? { + boxes.insert(bx.kind, bx.inner.collect_remaining()); } - 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 }) - } - _ => { - 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()?; - } + // find the hdlr box + let hdlr = HdlrBox::read_block(&mut boxes.remove(&BoxType::HdlrBox).unwrap().as_slice())?; + + Ok(match hdlr.handler_type { + MDIR => MetaBox::Mdir { + ilst: if let Some(inner) = boxes.remove(&BoxType::IlstBox) { + Some(IlstBox::read_block(&mut inner.as_slice())?) + } else { + None + }, + }, + _ => MetaBox::Unknown { + hdlr: hdlr.clone(), + data: boxes + .into_iter() + .map(|(k, v)| (k, v)) + .collect::)>>(), + }, + }) + } - Ok(MetaBox::Unknown { hdlr, data }) - } - } + fn size_hint() -> usize { + 8 } } 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,27 +160,26 @@ impl WriteBox<&mut W> for MetaBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; - #[test] - fn test_meta_mdir_empty() { + #[tokio::test] + async fn test_meta_mdir_empty() { let src_box = MetaBox::Mdir { ilst: None }; let mut buf = Vec::new(); 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(&mut reader, &mut 0).await.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_mdir() { + #[tokio::test] + async fn test_meta_mdir() { let src_box = MetaBox::Mdir { ilst: Some(IlstBox::default()), }; @@ -257,23 +188,25 @@ 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(&mut reader, &mut 0).await.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() { + #[tokio::test] + async 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(&mut reader, &mut 0).await.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 @@ -285,8 +218,8 @@ mod tests { ); } - #[test] - fn test_meta_unknown() { + #[tokio::test] + async fn test_meta_unknown() { let src_hdlr = HdlrBox { handler_type: FourCC::from(*b"test"), ..Default::default() @@ -301,12 +234,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(&mut reader, &mut 0).await.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 7bc2f724..5a444215 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,10 +80,9 @@ impl WriteBox<&mut W> for MfhdBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; - #[test] - fn test_mfhd() { + #[tokio::test] + async fn test_mfhd() { let src_box = MfhdBox { version: 0, flags: 0, @@ -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(&mut reader, &mut 0).await.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 5ea853b4..c3b8351b 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)); + return Err(BoxError::BoxNotFound(BoxType::DinfBox)); } + if stbl.is_none() { - return Err(Error::BoxNotFound(BoxType::StblBox)); + return Err(BoxError::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 4bbdd410..16795f6d 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::*; @@ -156,12 +158,21 @@ pub const HEADER_EXT_SIZE: u64 = 4; macro_rules! boxtype { ($( $name:ident => $value:expr ),*) => { - #[derive(Clone, Copy, PartialEq, Eq)] + #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub enum BoxType { $( $name, )* 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,468 @@ 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(BoxError::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 peek_u32(&self) -> u32; + + 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(BoxError::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(BoxError::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(BoxError::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(BoxError::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(BoxError::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(BoxError::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(BoxError::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(BoxError::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(BoxError::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(BoxError::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(BoxError::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(BoxError::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(|| BoxError::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(BoxError::BoxNotFound(A::TYPE)); + }; + + let Some(b) = b else { + return Err(BoxError::BoxNotFound(B::TYPE)); + }; + + let Some(c) = c else { + return Err(BoxError::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(BoxError::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) + } + + fn peek_u32(&self) -> u32 { + BigEndian::read_u32(self.chunk()) + } + + #[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(BoxError::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 mut offset = 0; + let Some(BoxHeader { kind, size }) = BoxHeader::read_sync(self, &mut offset)? else { + return Ok(None); + }; + + Ok(Some(BoxReader { + kind, + inner: Reader::take(self, (size - offset) 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 +723,138 @@ 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>, offset: &mut u64) -> Result> { + if reader.remaining() < 8 { + return Ok(None); + } + + let sz = reader.get_u32(); + let typ = reader.get_u32(); + + *offset += 8; + + // Get largesize if size is 1 + let size = if sz == 1 { + if reader.remaining() < 8 { + return Err(BoxError::InvalidData("expected 8 bytes more")); + } + + *offset += 8; + + 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(BoxError::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, + })) } // 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, + offset: &mut u64, + ) -> 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()), + }, + } + *offset += 8; // 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 largesize = u64::from_be_bytes(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()), + }, + } - Ok(BoxHeader { - name: BoxType::from(typ), + *offset += 8; + let largesize = u64::from_be_bytes(buf); - // 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(BoxError::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, + })) } 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_u32::(self.kind.into())?; writer.write_u64::(self.size)?; Ok(16) } else { writer.write_u32::(self.size as u32)?; - writer.write_u32::(self.name.into())?; + 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 +863,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)?; @@ -408,27 +919,43 @@ mod tests { assert_eq!(ftyp_fcc, ftyp_fcc2); } - #[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][..]); - assert!(matches!(error, Err(Error::InvalidData(_)))); + #[tokio::test] + async 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][..], + &mut 0, + ) + .await; + assert!(matches!(error, Err(BoxError::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][..]); - assert!(matches!(error, Err(Error::InvalidData(_)))); + #[tokio::test] + async 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][..], + &mut 0, + ) + .await; + assert!(matches!(error, Err(BoxError::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][..]); - assert!(matches!(error, Err(Error::InvalidData(_)))); + #[tokio::test] + async 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][..], + &mut 0, + ) + .await; + assert!(matches!(error, Err(BoxError::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, .. }))); + #[tokio::test] + async 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][..], + &mut 0, + ) + .await; + assert!(matches!(header, Ok(Some(BoxHeader { size: 8, .. })))); } } diff --git a/src/mp4box/moof.rs b/src/mp4box/moof.rs index 20c35653..c1cc9bb9 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)); + return Err(BoxError::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 ac19381a..46bd9580 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,60 +59,44 @@ 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 { let mut mvhd = None; let mut meta = None; let mut udta = None; 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)?); + meta = Some(bx.read()?); } + BoxType::MvexBox => { - mvex = Some(MvexBox::read_box(reader, s)?); + mvex = Some(bx.read()?); } + BoxType::TrakBox => { - let trak = TrakBox::read_box(reader, s)?; - traks.push(trak); + traks.push(bx.read()?); } + BoxType::UdtaBox => { - udta = Some(UdtaBox::read_box(reader, s)?); - } - _ => { - // XXX warn!() - skip_box(reader, s)?; + udta = Some(bx.read()?); } - } - current = reader.stream_position()?; + _ => continue, + } } if mvhd.is_none() { - return Err(Error::BoxNotFound(BoxType::MvhdBox)); + return Err(BoxError::BoxNotFound(BoxType::MvhdBox)); } - skip_bytes_to(reader, start + size)?; - Ok(MoovBox { mvhd: mvhd.unwrap(), meta, @@ -123,12 +105,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,10 +134,9 @@ impl WriteBox<&mut W> for MoovBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; - #[test] - fn test_moov() { + #[tokio::test] + async fn test_moov() { let src_box = MoovBox { mvhd: MvhdBox::default(), mvex: None, // XXX mvex is not written currently @@ -164,29 +149,29 @@ 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(&mut reader, &mut 0).await.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); } - #[test] - fn test_moov_empty() { + #[tokio::test] + async fn test_moov_empty() { let src_box = MoovBox::default(); let mut buf = Vec::new(); 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(&mut reader, &mut 0).await.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 a80c6c46..d03737cc 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(); - if version == 1 { - // Skip QTFF - reader.read_u64::()?; - reader.read_u64::()?; - } + reader.get_u16(); // reserved + reader.get_u32(); // reserved - // 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)?; + 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(BoxError::InvalidData("expected at least 16 bytes more")); } - } - skip_bytes_to(reader, end)?; + // Skip QTFF + reader.get_u64(); + reader.get_u64(); + } 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")); + return Err(BoxError::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 { @@ -286,7 +257,7 @@ fn write_desc(writer: &mut W, tag: u8, size: u32) -> Result { writer.write_u8(tag)?; if size as u64 > std::u32::MAX as u64 { - return Err(Error::InvalidData("invalid descriptor length range")); + return Err(BoxError::InvalidData("invalid descriptor length range")); } let nbytes = size_of_length(size); @@ -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,10 +594,9 @@ impl WriteDesc<&mut W> for SLConfigDescriptor { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; - #[test] - fn test_mp4a() { + #[tokio::test] + async fn test_mp4a() { let src_box = Mp4aBox { data_reference_index: 1, channelcount: 2, @@ -650,17 +628,17 @@ 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(&mut reader, &mut 0).await.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); } - #[test] - fn test_mp4a_no_esds() { + #[tokio::test] + async fn test_mp4a_no_esds() { let src_box = Mp4aBox { data_reference_index: 1, channelcount: 2, @@ -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(&mut reader, &mut 0).await.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 8be683b8..43c8cefc 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)); + return Err(BoxError::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 462a29b3..3a8ed470 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(BoxError::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")); + return Err(BoxError::InvalidData("version must be 0 or 1")); }; - let rate = FixedPointU16::new_raw(reader.read_u32::()?); - - let volume = FixedPointU8::new_raw(reader.read_u16::()?); - reader.read_u16::()?; // reserved = 0 + let rate = FixedPointU16::new_raw(reader.get_u32()); + let volume = FixedPointU8::new_raw(reader.get_u16()); - 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)?; @@ -167,7 +168,7 @@ impl WriteBox<&mut W> for MvhdBox { writer.write_u32::(self.timescale)?; writer.write_u32::(self.duration as u32)?; } else { - return Err(Error::InvalidData("version must be 0 or 1")); + return Err(BoxError::InvalidData("version must be 0 or 1")); } writer.write_u32::(self.rate.raw_value())?; @@ -199,10 +200,9 @@ impl WriteBox<&mut W> for MvhdBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; - #[test] - fn test_mvhd32() { + #[tokio::test] + async fn test_mvhd32() { let src_box = MvhdBox { version: 0, flags: 0, @@ -219,17 +219,17 @@ 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(&mut reader, &mut 0).await.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); } - #[test] - fn test_mvhd64() { + #[tokio::test] + async fn test_mvhd64() { let src_box = MvhdBox { version: 1, flags: 0, @@ -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(&mut reader, &mut 0).await.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 cab7e4bb..39210f93 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,10 +84,9 @@ impl WriteBox<&mut W> for SmhdBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; - #[test] - fn test_smhd() { + #[tokio::test] + async fn test_smhd() { let src_box = SmhdBox { version: 0, flags: 0, @@ -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(&mut reader, &mut 0).await.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 ef8433b4..c089e7ff 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)); + return Err(BoxError::BoxNotFound(BoxType::StsdBox)); } + if stts.is_none() { - return Err(Error::BoxNotFound(BoxType::SttsBox)); + return Err(BoxError::BoxNotFound(BoxType::SttsBox)); } + if stsc.is_none() { - return Err(Error::BoxNotFound(BoxType::StscBox)); + return Err(BoxError::BoxNotFound(BoxType::StscBox)); } + if stsz.is_none() { - return Err(Error::BoxNotFound(BoxType::StszBox)); + return Err(BoxError::BoxNotFound(BoxType::StszBox)); } + if stco.is_none() && co64.is_none() { - return Err(Error::Box2NotFound(BoxType::StcoBox, BoxType::Co64Box)); + return Err(BoxError::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 a00da8f9..19e7bc57 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::*; @@ -14,6 +14,26 @@ pub struct StcoBox { pub entries: Vec, } +impl<'a> IntoIterator for &'a StcoBox { + type Item = u64; + type IntoIter = std::iter::Map>, fn(u32) -> u64>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.entries.iter().copied().map(Into::::into) + } +} + +impl IntoIterator for StcoBox { + type Item = u64; + type IntoIter = std::iter::Map, fn(u32) -> u64>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.entries.into_iter().map(Into::::into) + } +} + impl StcoBox { pub fn get_type(&self) -> BoxType { BoxType::StcoBox @@ -25,9 +45,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 +61,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 - { - return Err(Error::InvalidData( + let entry_count = reader.get_u32(); + if entry_count as usize > reader.remaining() / entry_size { + return Err(BoxError::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,10 +129,9 @@ impl std::convert::TryFrom<&co64::Co64Box> for StcoBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; - #[test] - fn test_stco() { + #[tokio::test] + async fn test_stco() { let src_box = StcoBox { version: 0, flags: 0, @@ -130,12 +141,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(&mut reader, &mut 0).await.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 a2b034bb..a7b7ff2d 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::*; @@ -24,7 +24,7 @@ impl StscBox { } } -#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize)] pub struct StscEntry { pub first_chunk: u32, pub samples_per_chunk: u32, @@ -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,32 +49,23 @@ impl Mp4Box for StscBox { } } -impl ReadBox<&mut R> for StscBox { - 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 StscBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + 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 - { - return Err(Error::InvalidData( + let entry_count = reader.get_u32(); + if entry_count as usize > reader.remaining() / entry_size { + return Err(BoxError::InvalidData( "stsc 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 = 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); @@ -96,26 +85,28 @@ impl ReadBox<&mut R> for StscBox { .checked_sub(first_chunk) .and_then(|n| n.checked_mul(samples_per_chunk)) .and_then(|n| n.checked_add(sample_id)) - .ok_or(Error::InvalidData( + .ok_or(BoxError::InvalidData( "attempt to calculate stsc sample_id with overflow", ))?; } } - 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,10 +125,9 @@ impl WriteBox<&mut W> for StscBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; - #[test] - fn test_stsc() { + #[tokio::test] + async fn test_stsc() { let src_box = StscBox { version: 0, flags: 0, @@ -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(&mut reader, &mut 0).await.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 af947c6c..91e0966c 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,36 +79,32 @@ 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()?); + } + + BoxType::Vp09Box => { + vp09 = Some(bx.read()?); + } + + BoxType::Mp4aBox => { + mp4a = Some(bx.read()?); + } + + BoxType::Tx3gBox => { + tx3g = Some(bx.read()?); + } + + _ => {} } - _ => {} } - skip_bytes_to(reader, start + size)?; - Ok(StsdBox { version, flags, @@ -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 dd9e552f..8670728d 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)?; - - let (version, flags) = read_box_header_ext(reader)?; +impl BlockReader for StssBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + 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 - { - return Err(Error::InvalidData( + let entry_count = reader.get_u32(); + if entry_count as usize > reader.remaining() / entry_size { + return Err(BoxError::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,10 +91,9 @@ impl WriteBox<&mut W> for StssBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; - #[test] - fn test_stss() { + #[tokio::test] + async fn test_stss() { let src_box = StssBox { version: 0, flags: 0, @@ -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(&mut reader, &mut 0).await.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 b07e7653..3787a677 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)?; - - let (version, flags) = read_box_header_ext(reader)?; +impl BlockReader for StszBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + 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 - { - return Err(Error::InvalidData( + if sample_count as usize > reader.remaining() / stsz_item_size { + return Err(BoxError::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)?; @@ -107,7 +98,7 @@ impl WriteBox<&mut W> for StszBox { writer.write_u32::(self.sample_count)?; if self.sample_size == 0 { if self.sample_count != self.sample_sizes.len() as u32 { - return Err(Error::InvalidData("sample count out of sync")); + return Err(BoxError::InvalidData("sample count out of sync")); } for sample_number in self.sample_sizes.iter() { writer.write_u32::(*sample_number)?; @@ -122,10 +113,9 @@ impl WriteBox<&mut W> for StszBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; - #[test] - fn test_stsz_same_size() { + #[tokio::test] + async fn test_stsz_same_size() { let src_box = StszBox { version: 0, flags: 0, @@ -137,17 +127,17 @@ 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(&mut reader, &mut 0).await.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); } - #[test] - fn test_stsz_many_sizes() { + #[tokio::test] + async fn test_stsz_many_sizes() { let src_box = StszBox { version: 0, flags: 0, @@ -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(&mut reader, &mut 0).await.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 82de6c50..28e2027a 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)?; - - let (version, flags) = read_box_header_ext(reader)?; +impl BlockReader for SttsBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + 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 - { - return Err(Error::InvalidData( + let entry_count = reader.get_u32(); + + if entry_count as usize > reader.remaining() / entry_size { + return Err(BoxError::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,10 +102,9 @@ impl WriteBox<&mut W> for SttsBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; - #[test] - fn test_stts() { + #[tokio::test] + async fn test_stts() { let src_box = SttsBox { version: 0, flags: 0, @@ -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(&mut reader, &mut 0).await.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 ef928899..e32d7664 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")); + return Err(BoxError::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)?; @@ -82,7 +80,7 @@ impl WriteBox<&mut W> for TfdtBox { } else if self.version == 0 { writer.write_u32::(self.base_media_decode_time as u32)?; } else { - return Err(Error::InvalidData("version must be 0 or 1")); + return Err(BoxError::InvalidData("version must be 0 or 1")); } Ok(size) @@ -93,10 +91,9 @@ impl WriteBox<&mut W> for TfdtBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; - #[test] - fn test_tfdt32() { + #[tokio::test] + async fn test_tfdt32() { let src_box = TfdtBox { version: 0, flags: 0, @@ -106,17 +103,17 @@ 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(&mut reader, &mut 0).await.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); } - #[test] - fn test_tfdt64() { + #[tokio::test] + async fn test_tfdt64() { let src_box = TfdtBox { version: 1, flags: 0, @@ -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(&mut reader, &mut 0).await.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 5b529e6f..5a40c788 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,40 +67,41 @@ 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::()?) + Some(reader.get_u64()) } else { None }; + let sample_description_index = if TfhdBox::FLAG_SAMPLE_DESCRIPTION_INDEX & flags > 0 { - Some(reader.read_u32::()?) + Some(reader.get_u32()) } else { None }; + let default_sample_duration = if TfhdBox::FLAG_DEFAULT_SAMPLE_DURATION & flags > 0 { - Some(reader.read_u32::()?) + Some(reader.get_u32()) } else { None }; + let default_sample_size = if TfhdBox::FLAG_DEFAULT_SAMPLE_SIZE & flags > 0 { - Some(reader.read_u32::()?) + Some(reader.get_u32()) } else { None }; + let default_sample_flags = if TfhdBox::FLAG_DEFAULT_SAMPLE_FLAGS & flags > 0 { - Some(reader.read_u32::()?) + Some(reader.get_u32()) } else { None }; - skip_bytes_to(reader, start + size)?; - Ok(TfhdBox { version, flags, @@ -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,10 +150,9 @@ impl WriteBox<&mut W> for TfhdBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; - #[test] - fn test_tfhd() { + #[tokio::test] + async fn test_tfhd() { let src_box = TfhdBox { version: 0, flags: 0, @@ -165,17 +167,17 @@ 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(&mut reader, &mut 0).await.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); } - #[test] - fn test_tfhd_with_flags() { + #[tokio::test] + async fn test_tfhd_with_flags() { let src_box = TfhdBox { version: 0, flags: TfhdBox::FLAG_SAMPLE_DESCRIPTION_INDEX @@ -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(&mut reader, &mut 0).await.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 d7bcfbe0..1881c54b 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")); + return Err(BoxError::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)?; @@ -230,7 +231,7 @@ impl WriteBox<&mut W> for TkhdBox { writer.write_u32::(0)?; // reserved writer.write_u32::(self.duration as u32)?; } else { - return Err(Error::InvalidData("version must be 0 or 1")); + return Err(BoxError::InvalidData("version must be 0 or 1")); } writer.write_u64::(0)?; // reserved @@ -261,10 +262,9 @@ impl WriteBox<&mut W> for TkhdBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; - #[test] - fn test_tkhd32() { + #[tokio::test] + async fn test_tkhd32() { let src_box = TkhdBox { version: 0, flags: TrackFlag::TrackEnabled as u32, @@ -283,17 +283,17 @@ 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(&mut reader, &mut 0).await.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); } - #[test] - fn test_tkhd64() { + #[tokio::test] + async fn test_tkhd64() { let src_box = TkhdBox { version: 1, flags: TrackFlag::TrackEnabled as u32, @@ -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(&mut reader, &mut 0).await.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 d53d713d..f55bbb3f 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)); + return Err(BoxError::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 e8ae760f..08d0f1de 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(BoxError::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(BoxError::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(BoxError::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(BoxError::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(BoxError::EntryInStblNotFound( + self.tkhd.track_id, + BoxType::Co64Box, + chunk_id, + )); + } + } + + Err(BoxError::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(BoxError::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(BoxError::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(BoxError::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(BoxError::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(BoxError::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(BoxError::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)); + return Err(BoxError::BoxNotFound(BoxType::TkhdBox)); } + if mdia.is_none() { - return Err(Error::BoxNotFound(BoxType::MdiaBox)); + return Err(BoxError::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 2694fd65..4fa025ca 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)?; - - 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::()?; +impl BlockReader for TrexBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + let (version, flags) = read_box_header_ext(reader); - 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,10 +92,9 @@ impl WriteBox<&mut W> for TrexBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; - #[test] - fn test_trex() { + #[tokio::test] + async fn test_trex() { let src_box = TrexBox { version: 0, flags: 0, @@ -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(&mut reader, &mut 0).await.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 efbb2b09..7d58ca65 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) - { - return Err(Error::InvalidData( + + if sample_count as usize * sample_size > reader.remaining() { + return Err(BoxError::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)?; @@ -186,7 +184,7 @@ impl WriteBox<&mut W> for TrunBox { writer.write_u32::(v)?; } if self.sample_count != self.sample_sizes.len() as u32 { - return Err(Error::InvalidData("sample count out of sync")); + return Err(BoxError::InvalidData("sample count out of sync")); } for i in 0..self.sample_count as usize { if TrunBox::FLAG_SAMPLE_DURATION & self.flags > 0 { @@ -211,10 +209,9 @@ impl WriteBox<&mut W> for TrunBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; - #[test] - fn test_trun_same_size() { + #[tokio::test] + async fn test_trun_same_size() { let src_box = TrunBox { version: 0, flags: 0, @@ -230,17 +227,17 @@ 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(&mut reader, &mut 0).await.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); } - #[test] - fn test_trun_many_sizes() { + #[tokio::test] + async fn test_trun_many_sizes() { let src_box = TrunBox { version: 0, flags: TrunBox::FLAG_SAMPLE_DURATION @@ -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(&mut reader, &mut 0).await.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 d6963159..b378e810 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)?; - - reader.read_u32::()?; // reserved - reader.read_u16::()?; // reserved - let data_reference_index = reader.read_u16::()?; +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(); - 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,10 +154,9 @@ impl WriteBox<&mut W> for Tx3gBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; - #[test] - fn test_tx3g() { + #[tokio::test] + async fn test_tx3g() { let src_box = Tx3gBox { data_reference_index: 1, display_flags: 0, @@ -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(&mut reader, &mut 0).await.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 9daec179..a637a6d3 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)?; - - 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)?; +impl BlockReader for UdtaBox { + fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result { + Ok(UdtaBox { + meta: reader.try_find_box()?, + }) + } - 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,27 +67,26 @@ impl WriteBox<&mut W> for UdtaBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; - #[test] - fn test_udta_empty() { + #[tokio::test] + async fn test_udta_empty() { let src_box = UdtaBox { meta: None }; let mut buf = Vec::new(); 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(&mut reader, &mut 0).await.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); } - #[test] - fn test_udta() { + #[tokio::test] + async fn test_udta() { let src_box = UdtaBox { meta: Some(MetaBox::default()), }; @@ -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(&mut reader, &mut 0).await.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 31f24b21..2294c96b 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,10 +92,9 @@ impl WriteBox<&mut W> for VmhdBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; - #[test] - fn test_vmhd() { + #[tokio::test] + async fn test_vmhd() { let src_box = VmhdBox { version: 0, flags: 1, @@ -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(&mut reader, &mut 0).await.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 0f88dd14..5ac461ea 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,10 +168,9 @@ impl WriteBox<&mut W> for Vp09Box { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; - #[test] - fn test_vpcc() { + #[tokio::test] + async fn test_vpcc() { let src_box = Vp09Box::new(&Vp9Config { width: 1920, height: 1080, @@ -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(&mut reader, &mut 0).await.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 c9861c66..2ec0c71e 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,10 +100,9 @@ impl WriteBox<&mut W> for VpccBox { mod tests { use super::*; use crate::mp4box::BoxHeader; - use std::io::Cursor; - #[test] - fn test_vpcc() { + #[tokio::test] + async fn test_vpcc() { let src_box = VpccBox { version: VpccBox::DEFAULT_VERSION, flags: 0, @@ -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(&mut reader, &mut 0).await.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/reader.rs b/src/reader.rs deleted file mode 100644 index e5ac2964..00000000 --- a/src/reader.rs +++ /dev/null @@ -1,284 +0,0 @@ -use std::collections::HashMap; -use std::io::{Read, Seek}; -use std::time::Duration; - -use crate::meta::MetaBox; -use crate::*; - -#[derive(Debug)] -pub struct Mp4Reader { - reader: R, - pub ftyp: FtypBox, - pub moov: MoovBox, - pub moofs: Vec, - pub emsgs: Vec, - - tracks: HashMap, - size: u64, -} - -impl Mp4Reader { - pub fn read_header(mut reader: R, size: u64) -> Result { - let start = reader.stream_position()?; - - 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 current = start; - while current < size { - // Get box header. - let header = BoxHeader::read(&mut 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 if size zero BoxHeader, which can result in dead-loop. - if s == 0 { - break; - } - - // Match and parse the atom boxes. - match name { - BoxType::FtypBox => { - ftyp = Some(FtypBox::read_box(&mut reader, s)?); - } - BoxType::FreeBox => { - skip_box(&mut reader, s)?; - } - BoxType::MdatBox => { - skip_box(&mut reader, s)?; - } - BoxType::MoovBox => { - moov = Some(MoovBox::read_box(&mut reader, s)?); - } - BoxType::MoofBox => { - let moof_offset = reader.stream_position()? - 8; - let moof = MoofBox::read_box(&mut reader, s)?; - moofs.push(moof); - moof_offsets.push(moof_offset); - } - BoxType::EmsgBox => { - let emsg = EmsgBox::read_box(&mut reader, s)?; - emsgs.push(emsg); - } - _ => { - // XXX warn!() - skip_box(&mut reader, s)?; - } - } - current = reader.stream_position()?; - } - - if ftyp.is_none() { - return Err(Error::BoxNotFound(BoxType::FtypBox)); - } - if moov.is_none() { - return Err(Error::BoxNotFound(BoxType::MoovBox)); - } - - let size = current - start; - 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(Mp4Reader { - reader, - ftyp: ftyp.unwrap(), - moov: moov.unwrap(), - moofs, - emsgs, - size, - tracks, - }) - } - - pub fn read_fragment_header( - &self, - mut reader: FR, - size: u64, - ) -> Result> { - let start = reader.stream_position()?; - - let mut moofs = Vec::new(); - let mut moof_offsets = Vec::new(); - - let mut current = start; - while current < size { - // Get box header. - let header = BoxHeader::read(&mut 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 if size zero BoxHeader, which can result in dead-loop. - if s == 0 { - break; - } - - // Match and parse the atom boxes. - match name { - BoxType::MdatBox => { - skip_box(&mut reader, s)?; - } - BoxType::MoofBox => { - let moof_offset = reader.stream_position()? - 8; - let moof = MoofBox::read_box(&mut reader, s)?; - moofs.push(moof); - moof_offsets.push(moof_offset); - } - _ => { - // XXX warn!() - skip_box(&mut reader, s)?; - } - } - current = reader.stream_position()?; - } - - if moofs.is_empty() { - return Err(Error::BoxNotFound(BoxType::MoofBox)); - } - - let size = current - start; - let mut tracks: HashMap = self - .moov - .traks - .iter() - .map(|trak| (trak.tkhd.track_id, Mp4Track::from(trak))) - .collect(); - - let mut default_sample_duration = 0; - if let Some(ref mvex) = &self.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(Mp4Reader { - reader, - ftyp: self.ftyp.clone(), - moov: self.moov.clone(), - moofs, - emsgs: Vec::new(), - tracks, - size, - }) - } - - pub fn size(&self) -> u64 { - self.size - } - - pub fn major_brand(&self) -> &FourCC { - &self.ftyp.major_brand - } - - pub fn minor_version(&self) -> u32 { - self.ftyp.minor_version - } - - pub fn compatible_brands(&self) -> &[FourCC] { - &self.ftyp.compatible_brands - } - - pub fn duration(&self) -> Duration { - Duration::from_millis(self.moov.mvhd.duration * 1000 / self.moov.mvhd.timescale as u64) - } - - pub fn timescale(&self) -> u32 { - self.moov.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, track_id: u32, sample_id: u32) -> Result> { - if let Some(track) = self.tracks.get(&track_id) { - track.read_sample(&mut self.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)) - } - } -} - -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 { - MetaBox::Mdir { ilst } => ilst.as_ref(), - _ => None, - }) - }) - } -} diff --git a/src/track.rs b/src/track.rs index 7eada834..9fd82cd2 100644 --- a/src/track.rs +++ b/src/track.rs @@ -1,918 +1,322 @@ -use bytes::BytesMut; -use std::cmp; -use std::convert::TryFrom; -use std::io::{Read, Seek, SeekFrom, Write}; -use std::time::Duration; - -use crate::mp4box::traf::TrafBox; -use crate::mp4box::trak::TrakBox; -use crate::mp4box::trun::TrunBox; -use crate::mp4box::{ - avc1::Avc1Box, co64::Co64Box, ctts::CttsBox, ctts::CttsEntry, hev1::Hev1Box, mp4a::Mp4aBox, - smhd::SmhdBox, stco::StcoBox, stsc::StscEntry, stss::StssBox, stts::SttsEntry, tx3g::Tx3gBox, - vmhd::VmhdBox, vp09::Vp09Box, -}; -use crate::*; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct TrackConfig { - pub track_type: TrackType, - pub timescale: u32, - pub language: String, - pub media_conf: MediaConfig, +use std::collections::BTreeSet; + +use crate::ctts::CttsEntry; +use crate::error::BoxError; +use crate::stsc::StscEntry; +use crate::stts::SttsEntry; +use crate::TrackType; + +#[derive(Clone)] +pub struct Mp4SampleOffset { + pub offset: u64, + pub size: u32, + pub duration: u32, + pub start_time: u64, + pub rendering_offset: i32, + pub is_sync: bool, + pub chunk_id: u32, } -impl From for TrackConfig { - fn from(media_conf: MediaConfig) -> Self { - match media_conf { - MediaConfig::AvcConfig(avc_conf) => Self::from(avc_conf), - MediaConfig::HevcConfig(hevc_conf) => Self::from(hevc_conf), - MediaConfig::AacConfig(aac_conf) => Self::from(aac_conf), - MediaConfig::TtxtConfig(ttxt_conf) => Self::from(ttxt_conf), - MediaConfig::Vp9Config(vp9_config) => Self::from(vp9_config), - } - } -} - -impl From for TrackConfig { - fn from(avc_conf: AvcConfig) -> Self { - Self { - track_type: TrackType::Video, - timescale: 1000, // XXX - language: String::from("und"), // XXX - media_conf: MediaConfig::AvcConfig(avc_conf), - } - } -} - -impl From for TrackConfig { - fn from(hevc_conf: HevcConfig) -> Self { - Self { - track_type: TrackType::Video, - timescale: 1000, // XXX - language: String::from("und"), // XXX - media_conf: MediaConfig::HevcConfig(hevc_conf), - } - } -} - -impl From for TrackConfig { - fn from(aac_conf: AacConfig) -> Self { - Self { - track_type: TrackType::Audio, - timescale: 1000, // XXX - language: String::from("und"), // XXX - media_conf: MediaConfig::AacConfig(aac_conf), - } - } -} - -impl From for TrackConfig { - fn from(txtt_conf: TtxtConfig) -> Self { - Self { - track_type: TrackType::Subtitle, - timescale: 1000, // XXX - language: String::from("und"), // XXX - media_conf: MediaConfig::TtxtConfig(txtt_conf), - } - } -} - -impl From for TrackConfig { - fn from(vp9_conf: Vp9Config) -> Self { - Self { - track_type: TrackType::Video, - timescale: 1000, // XXX - language: String::from("und"), // XXX - media_conf: MediaConfig::Vp9Config(vp9_conf), - } - } -} - -#[derive(Debug)] pub struct Mp4Track { - pub trak: TrakBox, - pub trafs: Vec, - pub moof_offsets: Vec, - - // Fragmented Tracks Defaults. - pub default_sample_duration: u32, + pub track_id: u32, + pub duration: u64, + pub samples: Vec, + pub tkhd: crate::TkhdBox, + pub mdia: crate::MdiaBox, } impl Mp4Track { - pub(crate) fn from(trak: &TrakBox) -> Self { - let trak = trak.clone(); - Self { - trak, - trafs: Vec::new(), - moof_offsets: Vec::new(), - default_sample_duration: 0, - } - } - - pub fn track_id(&self) -> u32 { - self.trak.tkhd.track_id - } - - pub fn track_type(&self) -> Result { - TrackType::try_from(&self.trak.mdia.hdlr.handler_type) - } - - pub fn media_type(&self) -> Result { - if self.trak.mdia.minf.stbl.stsd.avc1.is_some() { - Ok(MediaType::H264) - } else if self.trak.mdia.minf.stbl.stsd.hev1.is_some() { - Ok(MediaType::H265) - } else if self.trak.mdia.minf.stbl.stsd.vp09.is_some() { - Ok(MediaType::VP9) - } else if self.trak.mdia.minf.stbl.stsd.mp4a.is_some() { - Ok(MediaType::AAC) - } else if self.trak.mdia.minf.stbl.stsd.tx3g.is_some() { - Ok(MediaType::TTXT) - } else { - Err(Error::InvalidData("unsupported media type")) - } - } - - pub fn box_type(&self) -> Result { - if self.trak.mdia.minf.stbl.stsd.avc1.is_some() { - Ok(FourCC::from(BoxType::Avc1Box)) - } else if self.trak.mdia.minf.stbl.stsd.hev1.is_some() { - Ok(FourCC::from(BoxType::Hev1Box)) - } else if self.trak.mdia.minf.stbl.stsd.vp09.is_some() { - Ok(FourCC::from(BoxType::Vp09Box)) - } else if self.trak.mdia.minf.stbl.stsd.mp4a.is_some() { - Ok(FourCC::from(BoxType::Mp4aBox)) - } else if self.trak.mdia.minf.stbl.stsd.tx3g.is_some() { - Ok(FourCC::from(BoxType::Tx3gBox)) - } else { - Err(Error::InvalidData("unsupported sample entry box")) - } - } - - pub fn width(&self) -> u16 { - if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 { - avc1.width - } else { - self.trak.tkhd.width.value() - } - } - - pub fn height(&self) -> u16 { - if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 { - avc1.height - } else { - self.trak.tkhd.height.value() - } - } - - pub fn frame_rate(&self) -> f64 { - let dur = self.duration(); - if dur.is_zero() { - 0.0 - } else { - self.sample_count() as f64 / dur.as_secs_f64() - } - } - - pub fn sample_freq_index(&self) -> Result { - if let Some(ref mp4a) = self.trak.mdia.minf.stbl.stsd.mp4a { - if let Some(ref esds) = mp4a.esds { - SampleFreqIndex::try_from(esds.es_desc.dec_config.dec_specific.freq_index) - } else { - Err(Error::BoxInStblNotFound(self.track_id(), BoxType::EsdsBox)) - } - } else { - Err(Error::BoxInStblNotFound(self.track_id(), BoxType::Mp4aBox)) - } - } - - pub fn channel_config(&self) -> Result { - if let Some(ref mp4a) = self.trak.mdia.minf.stbl.stsd.mp4a { - if let Some(ref esds) = mp4a.esds { - ChannelConfig::try_from(esds.es_desc.dec_config.dec_specific.chan_conf) - } else { - Err(Error::BoxInStblNotFound(self.track_id(), BoxType::EsdsBox)) - } - } else { - Err(Error::BoxInStblNotFound(self.track_id(), BoxType::Mp4aBox)) - } - } - - pub fn language(&self) -> &str { - &self.trak.mdia.mdhd.language - } - - pub fn timescale(&self) -> u32 { - self.trak.mdia.mdhd.timescale - } + pub fn new(trak: crate::TrakBox, offsets: &mut BTreeSet) -> Result { + let default_sample_duration = 1024; + let mut total_duration = 0; + let mut samples = Vec::with_capacity(trak.mdia.minf.stbl.stsz.sample_count as _); + let stco = &trak.mdia.minf.stbl.stco; + let co64 = &trak.mdia.minf.stbl.co64; - pub fn duration(&self) -> Duration { - Duration::from_micros( - self.trak.mdia.mdhd.duration * 1_000_000 / self.trak.mdia.mdhd.timescale as u64, - ) - } + let mb_iter1 = stco.clone().map(IntoIterator::into_iter); + let mb_iter2 = co64.clone().map(IntoIterator::into_iter); - pub fn bitrate(&self) -> u32 { - if let Some(ref mp4a) = self.trak.mdia.minf.stbl.stsd.mp4a { - if let Some(ref esds) = mp4a.esds { - esds.es_desc.dec_config.avg_bitrate - } else { - 0 - } - // mp4a.esds.es_desc.dec_config.avg_bitrate - } else { - let dur = self.duration(); - if dur.is_zero() { - 0 - } else { - let bitrate = self.total_sample_size() as f64 * 8.0 / dur.as_secs_f64(); - bitrate as u32 - } + if let Some(stco) = co64.as_ref().map(IntoIterator::into_iter) { + offsets.extend(stco); } - } - pub fn sample_count(&self) -> u32 { - if !self.trafs.is_empty() { - let mut sample_count = 0u32; - for traf in self.trafs.iter() { - if let Some(ref trun) = traf.trun { - sample_count = sample_count - .checked_add(trun.sample_count) - .expect("attempt to sum trun sample_count with overflow"); - } - } - sample_count - } else { - self.trak.mdia.minf.stbl.stsz.sample_count + if let Some(stco) = stco.as_ref().map(IntoIterator::into_iter) { + offsets.extend(stco); } - } - pub fn video_profile(&self) -> Result { - if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 { - AvcProfile::try_from(( - avc1.avcc.avc_profile_indication, - avc1.avcc.profile_compatibility, - )) - } else { - Err(Error::BoxInStblNotFound(self.track_id(), BoxType::Avc1Box)) - } - } + let chunk_iter = chunk_iter( + trak.mdia.minf.stbl.stsc.entries.clone().into_iter(), + mb_iter1 + .into_iter() + .flatten() + .chain(mb_iter2.into_iter().flatten()), + ); - 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) { - Some(nal) => Ok(nal.bytes.as_ref()), - None => Err(Error::EntryInStblNotFound( - self.track_id(), - BoxType::AvcCBox, - 0, - )), - } - } else { - Err(Error::BoxInStblNotFound(self.track_id(), BoxType::Avc1Box)) - } - } + let mut sample_chunk_iter = run_len_iter(chunk_iter); - 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) { - Some(nal) => Ok(nal.bytes.as_ref()), - None => Err(Error::EntryInStblNotFound( - self.track_id(), - BoxType::AvcCBox, - 0, - )), - } - } else { - Err(Error::BoxInStblNotFound(self.track_id(), BoxType::Avc1Box)) - } - } + let sync_iter_peek = trak + .mdia + .minf + .stbl + .stss + .as_ref() + .map(|x| x.entries.iter().copied().peekable()); - pub fn audio_profile(&self) -> Result { - if let Some(ref mp4a) = self.trak.mdia.minf.stbl.stsd.mp4a { - if let Some(ref esds) = mp4a.esds { - AudioObjectType::try_from(esds.es_desc.dec_config.dec_specific.profile) - } else { - Err(Error::BoxInStblNotFound(self.track_id(), BoxType::EsdsBox)) - } - } else { - Err(Error::BoxInStblNotFound(self.track_id(), BoxType::Mp4aBox)) - } - } - - fn stsc_index(&self, sample_id: u32) -> Result { - if self.trak.mdia.minf.stbl.stsc.entries.is_empty() { - return Err(Error::InvalidData("no stsc entries")); - } - for (i, entry) in self.trak.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.trak.mdia.minf.stbl.stsc.entries.len() - 1) - } - - fn chunk_offset(&self, chunk_id: u32) -> Result { - if self.trak.mdia.minf.stbl.stco.is_none() && self.trak.mdia.minf.stbl.co64.is_none() { - return Err(Error::InvalidData("must have either stco or co64 boxes")); - } - if let Some(ref stco) = self.trak.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.track_id(), - BoxType::StcoBox, - chunk_id, - )); - } - } else if let Some(ref co64) = self.trak.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.track_id(), - BoxType::Co64Box, - chunk_id, - )); - } - } - Err(Error::Box2NotFound(BoxType::StcoBox, BoxType::Co64Box)) - } - - fn ctts_index(&self, sample_id: u32) -> Result<(usize, u32)> { - let ctts = self.trak.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.track_id(), - BoxType::CttsBox, - sample_id, - )) - } + let mut sync_iter = + (1..=trak.mdia.minf.stbl.stsz.sample_count).scan(sync_iter_peek, |iter, idx| { + let iter = iter.as_mut()?; - /// return `(traf_idx, sample_idx_in_trun)` - fn find_traf_idx_and_sample_idx(&self, sample_id: u32) -> Option<(usize, usize)> { - let global_idx = sample_id - 1; - let mut offset = 0; - for traf_idx in 0..self.trafs.len() { - if let Some(trun) = &self.trafs[traf_idx].trun { - let sample_count = trun.sample_count; - if sample_count > (global_idx - offset) { - return Some((traf_idx, (global_idx - offset) as _)); - } - offset = offset - .checked_add(sample_count) - .expect("attempt to sum trun sample_count with overflow"); - } - } - None - } - - 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] - .trun - .as_ref() - .unwrap() - .sample_sizes - .get(sample_idx) - { - Ok(*size) + Some(if idx == iter.peek().copied().unwrap_or(u32::MAX) { + iter.next(); + true } else { - Err(Error::EntryInTrunNotFound( - self.track_id(), - BoxType::TrunBox, - sample_id, - )) - } - } else { - Err(Error::BoxInTrafNotFound(self.track_id(), BoxType::TrafBox)) - } - } else { - let stsz = &self.trak.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.track_id(), - BoxType::StszBox, - sample_id, - )) - } - } - } - - fn total_sample_size(&self) -> u64 { - let stsz = &self.trak.mdia.minf.stbl.stsz; - if stsz.sample_size > 0 { - stsz.sample_size as u64 * self.sample_count() as u64 - } else { - let mut total_size = 0; - for size in stsz.sample_sizes.iter() { - total_size += *size as u64; - } - total_size - } - } - - pub fn sample_offset(&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) { - let mut sample_offset = self.trafs[traf_idx] - .tfhd - .base_data_offset - .unwrap_or(self.moof_offsets[traf_idx]); - - if let Some(data_offset) = self.trafs[traf_idx] - .trun - .as_ref() - .and_then(|trun| trun.data_offset) - { - sample_offset = sample_offset.checked_add_signed(data_offset as i64).ok_or( - Error::InvalidData("attempt to calculate trun sample offset with overflow"), - )?; - } - - let first_sample_in_trun = sample_id - sample_idx as u32; - for i in first_sample_in_trun..sample_id { - sample_offset = sample_offset - .checked_add(self.sample_size(i)? as u64) - .ok_or(Error::InvalidData( - "attempt to calculate trun entry sample offset with overflow", - ))?; - } - - Ok(sample_offset) - } else { - Err(Error::BoxInTrafNotFound(self.track_id(), BoxType::TrafBox)) - } - } else { - let stsc_index = self.stsc_index(sample_id)?; - - let stsc = &self.trak.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) - } - } - - 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; - } - if let Some(trun) = &traf.trun { - if TrunBox::FLAG_SAMPLE_DURATION & trun.flags != 0 { - let mut start_offset = 0u64; - for duration in &trun.sample_durations[..sample_idx] { - start_offset = start_offset.checked_add(*duration as u64).ok_or( - Error::InvalidData("attempt to sum sample durations with overflow"), - )?; - } - let duration = trun.sample_durations[sample_idx]; - return Ok((base_start_time + start_offset, 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; - - 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.track_id(), - BoxType::SttsBox, - sample_id, + false + }) + }); + + let mut ts_deltas = + run_len_iter(trak.mdia.minf.stbl.stts.entries.clone().into_iter().chain( + std::iter::once(SttsEntry { + sample_count: u32::MAX, + sample_delta: default_sample_duration, + }), )) - } - } - - 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] - .trun - .as_ref() - .and_then(|trun| trun.sample_cts.get(sample_idx)) - { - return *cts as i32; - } - } - } else if let Some(ref ctts) = self.trak.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 - } - - 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(ref stss) = self.trak.mdia.minf.stbl.stss { - stss.entries.binary_search(&sample_id).is_ok() - } else { - true - } - } - - pub(crate) fn read_sample( - &self, - reader: &mut R, - sample_id: u32, - ) -> Result> { - let sample_offset = match self.sample_offset(sample_id) { - Ok(offset) => offset, - 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), - Err(err) => return Err(err), - }; - - let mut buffer = vec![0x0u8; sample_size as usize]; - reader.seek(SeekFrom::Start(sample_offset))?; - reader.read_exact(&mut buffer)?; - - let (start_time, duration) = self.sample_time(sample_id).unwrap(); // XXX - let rendering_offset = self.sample_rendering_offset(sample_id); - let is_sync = self.is_sync_sample(sample_id); - - Ok(Some(Mp4Sample { - start_time, - duration, - rendering_offset, - is_sync, - bytes: Bytes::from(buffer), - })) - } -} - -// TODO creation_time, modification_time -#[derive(Debug, Default)] -pub(crate) struct Mp4TrackWriter { - trak: TrakBox, - - sample_id: u32, - fixed_sample_size: u32, - is_fixed_sample_size: bool, - chunk_samples: u32, - chunk_duration: u32, - chunk_buffer: BytesMut, - - samples_per_chunk: u32, - duration_per_chunk: u32, -} - -impl Mp4TrackWriter { - pub(crate) fn new(track_id: u32, config: &TrackConfig) -> Result { - let mut trak = TrakBox::default(); - trak.tkhd.track_id = track_id; - trak.mdia.mdhd.timescale = config.timescale; - trak.mdia.mdhd.language = config.language.to_owned(); - trak.mdia.hdlr.handler_type = config.track_type.into(); - trak.mdia.minf.stbl.co64 = Some(Co64Box::default()); - match config.media_conf { - MediaConfig::AvcConfig(ref avc_config) => { - trak.tkhd.set_width(avc_config.width); - trak.tkhd.set_height(avc_config.height); - - let vmhd = VmhdBox::default(); - trak.mdia.minf.vmhd = Some(vmhd); - - let avc1 = Avc1Box::new(avc_config); - trak.mdia.minf.stbl.stsd.avc1 = Some(avc1); - } - MediaConfig::HevcConfig(ref hevc_config) => { - trak.tkhd.set_width(hevc_config.width); - trak.tkhd.set_height(hevc_config.height); - - let vmhd = VmhdBox::default(); - trak.mdia.minf.vmhd = Some(vmhd); - - let hev1 = Hev1Box::new(hevc_config); - trak.mdia.minf.stbl.stsd.hev1 = Some(hev1); - } - MediaConfig::Vp9Config(ref config) => { - trak.tkhd.set_width(config.width); - trak.tkhd.set_height(config.height); - - trak.mdia.minf.stbl.stsd.vp09 = Some(Vp09Box::new(config)); - } - MediaConfig::AacConfig(ref aac_config) => { - let smhd = SmhdBox::default(); - trak.mdia.minf.smhd = Some(smhd); - - let mp4a = Mp4aBox::new(aac_config); - trak.mdia.minf.stbl.stsd.mp4a = Some(mp4a); - } - MediaConfig::TtxtConfig(ref _ttxt_config) => { - let tx3g = Tx3gBox::default(); - trak.mdia.minf.stbl.stsd.tx3g = Some(tx3g); - } - } - Ok(Mp4TrackWriter { - trak, - chunk_buffer: BytesMut::new(), - sample_id: 1, - duration_per_chunk: config.timescale, // 1 second - ..Self::default() + .scan(0u64, |s, delta| { + let out = *s; + *s += delta as u64; + Some((out, delta)) + }); + + let mut rend_offset_iter = run_len_iter( + trak.mdia + .minf + .stbl + .ctts + .clone() + .into_iter() + .flat_map(|x| x.entries.into_iter()), + ); + + let mut sample_offset = 0; + let mut curr_chunk_index = 0; + let mut prev_size = 0; + + for sample_idx in 0..trak.mdia.minf.stbl.stsz.sample_count as usize { + let (start_time, duration) = ts_deltas.next().unwrap(); + let chunk = sample_chunk_iter.next().unwrap(); + let size = *trak + .mdia + .minf + .stbl + .stsz + .sample_sizes + .get(sample_idx) + .unwrap_or(&trak.mdia.minf.stbl.stsz.sample_size); + + if curr_chunk_index != chunk.index { + curr_chunk_index = chunk.index; + sample_offset = 0; + } else { + sample_offset += prev_size; + } + + prev_size = size; + total_duration = start_time + duration as u64; + samples.push(Mp4SampleOffset { + chunk_id: chunk.index, + offset: chunk.offset + sample_offset as u64, + size, + duration, + start_time, + rendering_offset: rend_offset_iter.next().unwrap_or(0), + is_sync: sync_iter.next().unwrap_or(true), + }) + } + + Ok(Self { + track_id: trak.tkhd.track_id, + tkhd: trak.tkhd, + mdia: trak.mdia, + samples, + duration: total_duration, }) } - fn update_sample_sizes(&mut self, size: u32) { - if self.trak.mdia.minf.stbl.stsz.sample_count == 0 { - if size == 0 { - self.trak.mdia.minf.stbl.stsz.sample_size = 0; - self.is_fixed_sample_size = false; - self.trak.mdia.minf.stbl.stsz.sample_sizes.push(0); - } else { - self.trak.mdia.minf.stbl.stsz.sample_size = size; - self.fixed_sample_size = size; - self.is_fixed_sample_size = true; - } - } else if self.is_fixed_sample_size { - if self.fixed_sample_size != size { - self.is_fixed_sample_size = false; - if self.trak.mdia.minf.stbl.stsz.sample_size > 0 { - self.trak.mdia.minf.stbl.stsz.sample_size = 0; - for _ in 0..self.trak.mdia.minf.stbl.stsz.sample_count { - self.trak - .mdia - .minf - .stbl - .stsz - .sample_sizes - .push(self.fixed_sample_size); - } - } - self.trak.mdia.minf.stbl.stsz.sample_sizes.push(size); - } - } else { - self.trak.mdia.minf.stbl.stsz.sample_sizes.push(size); - } - self.trak.mdia.minf.stbl.stsz.sample_count += 1; + #[inline] + pub fn track_type(&self) -> TrackType { + TrackType::from(&self.mdia.hdlr.handler_type) } - fn update_sample_times(&mut self, dur: u32) { - if let Some(ref mut entry) = self.trak.mdia.minf.stbl.stts.entries.last_mut() { - if entry.sample_delta == dur { - entry.sample_count += 1; - return; - } - } - - let entry = SttsEntry { - sample_count: 1, - sample_delta: dur, - }; - self.trak.mdia.minf.stbl.stts.entries.push(entry); - } - - fn update_rendering_offsets(&mut self, offset: i32) { - let ctts = if let Some(ref mut ctts) = self.trak.mdia.minf.stbl.ctts { - ctts - } else { - if offset == 0 { - return; - } - let mut ctts = CttsBox::default(); - if self.sample_id > 1 { - let entry = CttsEntry { - sample_count: self.sample_id - 1, - sample_offset: 0, - }; - ctts.entries.push(entry); - } - self.trak.mdia.minf.stbl.ctts = Some(ctts); - self.trak.mdia.minf.stbl.ctts.as_mut().unwrap() + pub(crate) fn add_traf( + &mut self, + base_moof_offset: u64, + chunk_index: u32, + traf: crate::TrafBox, + offsets: &mut BTreeSet, + ) { + let base_data_offset = traf.tfhd.base_data_offset.unwrap_or(base_moof_offset); + offsets.insert(base_data_offset); + + let default_sample_size = traf.tfhd.default_sample_size.unwrap_or(0); + let default_sample_duration = traf.tfhd.default_sample_duration.unwrap_or(0); + let base_start_time = traf + .tfdt + .map(|x| x.base_media_decode_time) + .or_else(|| { + self.samples + .last() + .map(|x| x.start_time + x.duration as u64) + }) + .unwrap_or(0); + + let Some(trun) = traf.trun else { + return; }; - if let Some(ref mut entry) = ctts.entries.last_mut() { - if entry.sample_offset == offset { - entry.sample_count += 1; - return; - } + let mut sample_offset = 0u64; + let mut start_time_offset = 0u64; + for sample_idx in 0..trun.sample_count as usize { + let size = trun + .sample_sizes + .get(sample_idx) + .copied() + .unwrap_or(default_sample_size); + + let duration = trun + .sample_durations + .get(sample_idx) + .copied() + .unwrap_or(default_sample_duration); + + let rendering_offset = trun.sample_cts.get(sample_idx).copied().unwrap_or(0) as i32; + + self.samples.push(Mp4SampleOffset { + chunk_id: chunk_index, + offset: (base_data_offset as i64 + + trun.data_offset.map(|x| x as i64).unwrap_or(0) + + sample_offset as i64) as u64, + size, + duration, + start_time: base_start_time + start_time_offset, + rendering_offset, + is_sync: sample_idx == 0, + }); + + sample_offset += size as u64; + start_time_offset += duration as u64; } - - let entry = CttsEntry { - sample_count: 1, - sample_offset: offset, - }; - ctts.entries.push(entry); } +} - fn update_sync_samples(&mut self, is_sync: bool) { - if let Some(ref mut stss) = self.trak.mdia.minf.stbl.stss { - if !is_sync { - return; - } +trait RunLenghtItem { + type Value: Clone; - stss.entries.push(self.sample_id); - } else { - if !is_sync { - return; - } + fn count(&self) -> usize; + fn value(&self) -> Self::Value; +} - // Create the stts box if not found and push the entry. - let mut stss = StssBox::default(); - stss.entries.push(self.sample_id); - self.trak.mdia.minf.stbl.stss = Some(stss); - }; - } +impl RunLenghtItem for (usize, T) { + type Value = T; - fn is_chunk_full(&self) -> bool { - if self.samples_per_chunk > 0 { - self.chunk_samples >= self.samples_per_chunk - } else { - self.chunk_duration >= self.duration_per_chunk - } + fn count(&self) -> usize { + self.0 } - - fn update_durations(&mut self, dur: u32, movie_timescale: u32) { - self.trak.mdia.mdhd.duration += dur as u64; - if self.trak.mdia.mdhd.duration > (u32::MAX as u64) { - self.trak.mdia.mdhd.version = 1 - } - self.trak.tkhd.duration += - dur as u64 * movie_timescale as u64 / self.trak.mdia.mdhd.timescale as u64; - if self.trak.tkhd.duration > (u32::MAX as u64) { - self.trak.tkhd.version = 1 - } + fn value(&self) -> Self::Value { + self.1.clone() } +} - pub(crate) fn write_sample( - &mut self, - writer: &mut W, - sample: &Mp4Sample, - movie_timescale: u32, - ) -> Result { - self.chunk_buffer.extend_from_slice(&sample.bytes); - self.chunk_samples += 1; - self.chunk_duration += sample.duration; - self.update_sample_sizes(sample.bytes.len() as u32); - self.update_sample_times(sample.duration); - self.update_rendering_offsets(sample.rendering_offset); - self.update_sync_samples(sample.is_sync); - if self.is_chunk_full() { - self.write_chunk(writer)?; - } - self.update_durations(sample.duration, movie_timescale); - - self.sample_id += 1; +impl RunLenghtItem for CttsEntry { + type Value = i32; - Ok(self.trak.tkhd.duration) + fn count(&self) -> usize { + self.sample_count as _ } - fn chunk_count(&self) -> u32 { - let co64 = self.trak.mdia.minf.stbl.co64.as_ref().unwrap(); - co64.entries.len() as u32 + fn value(&self) -> Self::Value { + self.sample_offset } +} - fn update_sample_to_chunk(&mut self, chunk_id: u32) { - if let Some(entry) = self.trak.mdia.minf.stbl.stsc.entries.last() { - if entry.samples_per_chunk == self.chunk_samples { - return; - } - } +impl RunLenghtItem for SttsEntry { + type Value = u32; - let entry = StscEntry { - first_chunk: chunk_id, - samples_per_chunk: self.chunk_samples, - sample_description_index: 1, - first_sample: self.sample_id - self.chunk_samples + 1, - }; - self.trak.mdia.minf.stbl.stsc.entries.push(entry); + fn count(&self) -> usize { + self.sample_count as _ } - fn update_chunk_offsets(&mut self, offset: u64) { - let co64 = self.trak.mdia.minf.stbl.co64.as_mut().unwrap(); - co64.entries.push(offset); + fn value(&self) -> Self::Value { + self.sample_delta } +} - fn write_chunk(&mut self, writer: &mut W) -> Result<()> { - if self.chunk_buffer.is_empty() { - return Ok(()); - } - let chunk_offset = writer.stream_position()?; - - writer.write_all(&self.chunk_buffer)?; - - self.update_sample_to_chunk(self.chunk_count() + 1); - self.update_chunk_offsets(chunk_offset); +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Chunk { + pub index: u32, + pub offset: u64, + pub samples_per_chunk: u32, + pub sample_description_index: u32, +} - self.chunk_buffer.clear(); - self.chunk_samples = 0; - self.chunk_duration = 0; +impl RunLenghtItem for Chunk { + type Value = Chunk; - Ok(()) + fn count(&self) -> usize { + self.samples_per_chunk as _ } - fn max_sample_size(&self) -> u32 { - if self.trak.mdia.minf.stbl.stsz.sample_size > 0 { - self.trak.mdia.minf.stbl.stsz.sample_size - } else { - let mut max_size = 0; - for sample_size in self.trak.mdia.minf.stbl.stsz.sample_sizes.iter() { - max_size = cmp::max(max_size, *sample_size); - } - max_size - } + fn value(&self) -> Self::Value { + *self } +} - pub(crate) fn write_end(&mut self, writer: &mut W) -> Result { - self.write_chunk(writer)?; +fn chunk_iter( + mut stsc: impl Iterator, + stco: impl Iterator, +) -> impl Iterator { + let mut prev = stsc.next().unwrap_or(StscEntry { + first_chunk: 1, + samples_per_chunk: u32::MAX, + sample_description_index: 1, + first_sample: 1, + }); + let mut curr = stsc.next(); + + stco.enumerate().map(move |(idx, offset)| { + if let Some(c) = &curr { + if idx + 1 >= c.first_chunk as usize { + prev = *c; + curr = stsc.next(); + } + } + + Chunk { + index: idx as _, + offset, + samples_per_chunk: prev.samples_per_chunk, + sample_description_index: prev.sample_description_index, + } + }) +} - let max_sample_size = self.max_sample_size(); - if let Some(ref mut mp4a) = self.trak.mdia.minf.stbl.stsd.mp4a { - if let Some(ref mut esds) = mp4a.esds { - esds.es_desc.dec_config.buffer_size_db = max_sample_size; +fn run_len_iter>( + iter: I, +) -> impl Iterator { + let mut iter = iter.into_iter(); + let mut value = None::; + let mut repeat = 0; + std::iter::from_fn(move || loop { + if let Some(val) = &value { + if repeat > 0 { + repeat -= 1; + return Some(val.clone()); + } else { + value = None; } - // TODO - // mp4a.esds.es_desc.dec_config.max_bitrate - // mp4a.esds.es_desc.dec_config.avg_bitrate - } - if let Ok(stco) = StcoBox::try_from(self.trak.mdia.minf.stbl.co64.as_ref().unwrap()) { - self.trak.mdia.minf.stbl.stco = Some(stco); - self.trak.mdia.minf.stbl.co64 = None; } - Ok(self.trak.clone()) - } + let x = iter.next()?; + value = Some(x.value()); + repeat = x.count(); + }) } diff --git a/src/types.rs b/src/types.rs index 540f7fb0..0e811f07 100644 --- a/src/types.rs +++ b/src/types.rs @@ -92,7 +92,7 @@ pub struct FourCC { } impl std::str::FromStr for FourCC { - type Err = Error; + type Err = BoxError; fn from_str(s: &str) -> Result { if let [a, b, c, d] = s.as_bytes() { @@ -100,7 +100,9 @@ impl std::str::FromStr for FourCC { value: [*a, *b, *c, *d], }) } else { - Err(Error::InvalidData("expected exactly four bytes in string")) + Err(BoxError::InvalidData( + "expected exactly four bytes in string", + )) } } } @@ -170,39 +172,39 @@ pub enum TrackType { Video, Audio, Subtitle, + Other(FourCC), } impl fmt::Display for TrackType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let s = match self { - TrackType::Video => DISPLAY_TYPE_VIDEO, - TrackType::Audio => DISPLAY_TYPE_AUDIO, - TrackType::Subtitle => DISPLAY_TYPE_SUBTITLE, - }; - write!(f, "{s}") + match self { + TrackType::Video => write!(f, "{DISPLAY_TYPE_VIDEO}"), + TrackType::Audio => write!(f, "{DISPLAY_TYPE_AUDIO}"), + TrackType::Subtitle => write!(f, "{DISPLAY_TYPE_SUBTITLE}"), + TrackType::Other(other) => write!(f, "Other({})", other), + } } } impl TryFrom<&str> for TrackType { - type Error = Error; + type Error = BoxError; fn try_from(handler: &str) -> Result { match handler { HANDLER_TYPE_VIDEO => Ok(TrackType::Video), HANDLER_TYPE_AUDIO => Ok(TrackType::Audio), HANDLER_TYPE_SUBTITLE => Ok(TrackType::Subtitle), - _ => Err(Error::InvalidData("unsupported handler type")), + _ => Err(BoxError::InvalidData("unsupported handler type")), } } } -impl TryFrom<&FourCC> for TrackType { - type Error = Error; - fn try_from(fourcc: &FourCC) -> Result { +impl From<&FourCC> for TrackType { + fn from(fourcc: &FourCC) -> TrackType { match fourcc.value { - HANDLER_TYPE_VIDEO_FOURCC => Ok(TrackType::Video), - HANDLER_TYPE_AUDIO_FOURCC => Ok(TrackType::Audio), - HANDLER_TYPE_SUBTITLE_FOURCC => Ok(TrackType::Subtitle), - _ => Err(Error::InvalidData("unsupported handler type")), + HANDLER_TYPE_VIDEO_FOURCC => TrackType::Video, + HANDLER_TYPE_AUDIO_FOURCC => TrackType::Audio, + HANDLER_TYPE_SUBTITLE_FOURCC => TrackType::Subtitle, + other => TrackType::Other(other.into()), } } } @@ -213,6 +215,7 @@ impl From for FourCC { TrackType::Video => HANDLER_TYPE_VIDEO_FOURCC.into(), TrackType::Audio => HANDLER_TYPE_AUDIO_FOURCC.into(), TrackType::Subtitle => HANDLER_TYPE_SUBTITLE_FOURCC.into(), + TrackType::Other(inner) => inner.into(), } } } @@ -240,7 +243,7 @@ impl fmt::Display for MediaType { } impl TryFrom<&str> for MediaType { - type Error = Error; + type Error = BoxError; fn try_from(media: &str) -> Result { match media { MEDIA_TYPE_H264 => Ok(MediaType::H264), @@ -248,7 +251,7 @@ impl TryFrom<&str> for MediaType { MEDIA_TYPE_VP9 => Ok(MediaType::VP9), MEDIA_TYPE_AAC => Ok(MediaType::AAC), MEDIA_TYPE_TTXT => Ok(MediaType::TTXT), - _ => Err(Error::InvalidData("unsupported media type")), + _ => Err(BoxError::InvalidData("unsupported media type")), } } } @@ -288,7 +291,7 @@ pub enum AvcProfile { } impl TryFrom<(u8, u8)> for AvcProfile { - type Error = Error; + type Error = BoxError; fn try_from(value: (u8, u8)) -> Result { let profile = value.0; let constraint_set1_flag = (value.1 & 0x40) >> 7; @@ -298,7 +301,7 @@ impl TryFrom<(u8, u8)> for AvcProfile { (77, _) => Ok(AvcProfile::AvcMain), (88, _) => Ok(AvcProfile::AvcExtended), (100, _) => Ok(AvcProfile::AvcHigh), - _ => Err(Error::InvalidData("unsupported avc profile")), + _ => Err(BoxError::InvalidData("unsupported avc profile")), } } } @@ -363,7 +366,7 @@ pub enum AudioObjectType { } impl TryFrom for AudioObjectType { - type Error = Error; + type Error = BoxError; fn try_from(value: u8) -> Result { match value { 1 => Ok(AudioObjectType::AacMain), @@ -408,7 +411,7 @@ impl TryFrom for AudioObjectType { 44 => Ok(AudioObjectType::LowDelayMpegSurround), 45 => Ok(AudioObjectType::SpatialAudioObjectCodingDialogueEnhancement), 46 => Ok(AudioObjectType::AudioSync), - _ => Err(Error::InvalidData("invalid audio object type")), + _ => Err(BoxError::InvalidData("invalid audio object type")), } } } @@ -481,7 +484,7 @@ pub enum SampleFreqIndex { } impl TryFrom for SampleFreqIndex { - type Error = Error; + type Error = BoxError; fn try_from(value: u8) -> Result { match value { 0x0 => Ok(SampleFreqIndex::Freq96000), @@ -497,7 +500,7 @@ impl TryFrom for SampleFreqIndex { 0xa => Ok(SampleFreqIndex::Freq11025), 0xb => Ok(SampleFreqIndex::Freq8000), 0xc => Ok(SampleFreqIndex::Freq7350), - _ => Err(Error::InvalidData("invalid sampling frequency index")), + _ => Err(BoxError::InvalidData("invalid sampling frequency index")), } } } @@ -534,7 +537,7 @@ pub enum ChannelConfig { } impl TryFrom for ChannelConfig { - type Error = Error; + type Error = BoxError; fn try_from(value: u8) -> Result { match value { 0x1 => Ok(ChannelConfig::Mono), @@ -544,7 +547,7 @@ impl TryFrom for ChannelConfig { 0x5 => Ok(ChannelConfig::Five), 0x6 => Ok(ChannelConfig::FiveOne), 0x7 => Ok(ChannelConfig::SevenOne), - _ => Err(Error::InvalidData("invalid channel configuration")), + _ => Err(BoxError::InvalidData("invalid channel configuration")), } } } @@ -673,14 +676,14 @@ impl std::default::Default for DataType { } impl TryFrom for DataType { - type Error = Error; + type Error = BoxError; fn try_from(value: u32) -> Result { match value { 0x000000 => Ok(DataType::Binary), 0x000001 => Ok(DataType::Text), 0x00000D => Ok(DataType::Image), 0x000015 => Ok(DataType::TempoCpil), - _ => Err(Error::InvalidData("invalid data type")), + _ => Err(BoxError::InvalidData("invalid data type")), } } } diff --git a/src/writer.rs b/src/writer.rs deleted file mode 100644 index a83a888c..00000000 --- a/src/writer.rs +++ /dev/null @@ -1,149 +0,0 @@ -use byteorder::{BigEndian, WriteBytesExt}; -use std::io::{Seek, SeekFrom, Write}; - -use crate::mp4box::*; -use crate::track::Mp4TrackWriter; -use crate::*; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Mp4Config { - pub major_brand: FourCC, - pub minor_version: u32, - pub compatible_brands: Vec, - pub timescale: u32, -} - -#[derive(Debug)] -pub struct Mp4Writer { - writer: W, - tracks: Vec, - mdat_pos: u64, - timescale: u32, - duration: u64, -} - -impl Mp4Writer { - /// Consume self, returning the inner writer. - /// - /// This can be useful to recover the inner writer after completion in case - /// it's owned by the [Mp4Writer] instance. - /// - /// # Examples - /// - /// ```rust - /// use mp4::{Mp4Writer, Mp4Config}; - /// use std::io::Cursor; - /// - /// # fn main() -> mp4::Result<()> { - /// let config = Mp4Config { - /// major_brand: str::parse("isom").unwrap(), - /// minor_version: 512, - /// compatible_brands: vec![ - /// str::parse("isom").unwrap(), - /// str::parse("iso2").unwrap(), - /// str::parse("avc1").unwrap(), - /// str::parse("mp41").unwrap(), - /// ], - /// timescale: 1000, - /// }; - /// - /// let data = Cursor::new(Vec::::new()); - /// let mut writer = mp4::Mp4Writer::write_start(data, &config)?; - /// writer.write_end()?; - /// - /// let data: Vec = writer.into_writer().into_inner(); - /// # Ok(()) } - /// ``` - pub fn into_writer(self) -> W { - self.writer - } -} - -impl Mp4Writer { - pub fn write_start(mut writer: W, config: &Mp4Config) -> Result { - let ftyp = FtypBox { - major_brand: config.major_brand, - minor_version: config.minor_version, - compatible_brands: config.compatible_brands.clone(), - }; - ftyp.write_box(&mut writer)?; - - // TODO largesize - let mdat_pos = writer.stream_position()?; - BoxHeader::new(BoxType::MdatBox, HEADER_SIZE).write(&mut writer)?; - BoxHeader::new(BoxType::WideBox, HEADER_SIZE).write(&mut writer)?; - - let tracks = Vec::new(); - let timescale = config.timescale; - let duration = 0; - Ok(Self { - writer, - tracks, - mdat_pos, - timescale, - duration, - }) - } - - pub fn add_track(&mut self, config: &TrackConfig) -> Result<()> { - let track_id = self.tracks.len() as u32 + 1; - let track = Mp4TrackWriter::new(track_id, config)?; - self.tracks.push(track); - Ok(()) - } - - fn update_durations(&mut self, track_dur: u64) { - if track_dur > self.duration { - self.duration = track_dur; - } - } - - pub fn write_sample(&mut self, track_id: u32, sample: &Mp4Sample) -> Result<()> { - if track_id == 0 { - return Err(Error::TrakNotFound(track_id)); - } - - let track_dur = if let Some(ref mut track) = self.tracks.get_mut(track_id as usize - 1) { - track.write_sample(&mut self.writer, sample, self.timescale)? - } else { - return Err(Error::TrakNotFound(track_id)); - }; - - self.update_durations(track_dur); - - Ok(()) - } - - fn update_mdat_size(&mut self) -> Result<()> { - let mdat_end = self.writer.stream_position()?; - let mdat_size = mdat_end - self.mdat_pos; - if mdat_size > std::u32::MAX as u64 { - self.writer.seek(SeekFrom::Start(self.mdat_pos))?; - self.writer.write_u32::(1)?; - self.writer.seek(SeekFrom::Start(self.mdat_pos + 8))?; - self.writer.write_u64::(mdat_size)?; - } else { - self.writer.seek(SeekFrom::Start(self.mdat_pos))?; - self.writer.write_u32::(mdat_size as u32)?; - } - self.writer.seek(SeekFrom::Start(mdat_end))?; - Ok(()) - } - - pub fn write_end(&mut self) -> Result<()> { - let mut moov = MoovBox::default(); - - for track in self.tracks.iter_mut() { - moov.traks.push(track.write_end(&mut self.writer)?); - } - self.update_mdat_size()?; - - moov.mvhd.timescale = self.timescale; - moov.mvhd.duration = self.duration; - if moov.mvhd.duration > (u32::MAX as u64) { - moov.mvhd.version = 1 - } - moov.write_box(&mut self.writer)?; - Ok(()) - } -} diff --git a/tests/lib.rs b/tests/lib.rs index 7c81f95f..93d3b73b 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1,211 +1,54 @@ -use mp4::{ - AudioObjectType, AvcProfile, ChannelConfig, MediaType, Metadata, Mp4Reader, SampleFreqIndex, - TrackType, -}; -use std::fs::{self, File}; -use std::io::BufReader; -use std::time::Duration; - -#[test] -fn test_read_mp4() { - let mut mp4 = get_reader("tests/samples/minimal.mp4"); - - assert_eq!(2591, mp4.size()); - - // ftyp. - assert_eq!(4, mp4.compatible_brands().len()); - - // Check compatible_brands. - let brands = vec![ - String::from("isom"), - String::from("iso2"), - String::from("avc1"), - String::from("mp41"), - ]; - - for b in brands { - let t = mp4.compatible_brands().iter().any(|x| x.to_string() == b); - assert!(t); - } - - assert_eq!(mp4.duration(), Duration::from_millis(62)); - assert_eq!(mp4.timescale(), 1000); - assert_eq!(mp4.tracks().len(), 2); - - let sample_count = mp4.sample_count(1).unwrap(); - assert_eq!(sample_count, 1); - let sample_1_1 = mp4.read_sample(1, 1).unwrap().unwrap(); - assert_eq!(sample_1_1.bytes.len(), 751); - assert_eq!( - sample_1_1, - mp4::Mp4Sample { - start_time: 0, - duration: 512, - rendering_offset: 0, - is_sync: true, - bytes: mp4::Bytes::from(vec![0x0u8; 751]), - } - ); - let eos = mp4.read_sample(1, 2).unwrap(); - assert!(eos.is_none()); - - let sample_count = mp4.sample_count(2).unwrap(); - assert_eq!(sample_count, 3); - let sample_2_1 = mp4.read_sample(2, 1).unwrap().unwrap(); - assert_eq!(sample_2_1.bytes.len(), 179); - assert_eq!( - sample_2_1, - mp4::Mp4Sample { - start_time: 0, - duration: 1024, - rendering_offset: 0, - is_sync: true, - bytes: mp4::Bytes::from(vec![0x0u8; 179]), - } - ); - - let sample_2_2 = mp4.read_sample(2, 2).unwrap().unwrap(); - assert_eq!( - sample_2_2, - mp4::Mp4Sample { - start_time: 1024, - duration: 1024, - rendering_offset: 0, - is_sync: true, - bytes: mp4::Bytes::from(vec![0x0u8; 180]), - } - ); - - let sample_2_3 = mp4.read_sample(2, 3).unwrap().unwrap(); - assert_eq!( - sample_2_3, - mp4::Mp4Sample { - start_time: 2048, - duration: 896, - rendering_offset: 0, - is_sync: true, - bytes: mp4::Bytes::from(vec![0x0u8; 160]), - } - ); - - let eos = mp4.read_sample(2, 4).unwrap(); - assert!(eos.is_none()); - - // track #1 - let track1 = mp4.tracks().get(&1).unwrap(); - assert_eq!(track1.track_id(), 1); - assert_eq!(track1.track_type().unwrap(), TrackType::Video); - assert_eq!(track1.media_type().unwrap(), MediaType::H264); - assert_eq!(track1.video_profile().unwrap(), AvcProfile::AvcHigh); - assert_eq!(track1.width(), 320); - assert_eq!(track1.height(), 240); - assert_eq!(track1.bitrate(), 150200); - assert_eq!(track1.frame_rate(), 25.00); - - // track #2 - let track2 = mp4.tracks().get(&2).unwrap(); - assert_eq!(track2.track_type().unwrap(), TrackType::Audio); - assert_eq!(track2.media_type().unwrap(), MediaType::AAC); - assert_eq!( - track2.audio_profile().unwrap(), - AudioObjectType::AacLowComplexity - ); - assert_eq!( - track2.sample_freq_index().unwrap(), - SampleFreqIndex::Freq48000 - ); - assert_eq!(track2.channel_config().unwrap(), ChannelConfig::Mono); - assert_eq!(track2.bitrate(), 67695); -} - -#[test] -fn test_read_extended_audio_object_type() { - // Extended audio object type and sample rate index of 15 - let mp4 = get_reader("tests/samples/extended_audio_object_type.mp4"); - - let track = mp4.tracks().get(&1).unwrap(); - assert_eq!(track.track_type().unwrap(), TrackType::Audio); - assert_eq!(track.media_type().unwrap(), MediaType::AAC); - assert_eq!( - track.audio_profile().unwrap(), - AudioObjectType::AudioLosslessCoding - ); - assert_eq!( - track - .trak - .mdia - .minf - .stbl - .stsd - .mp4a - .as_ref() - .unwrap() - .esds - .as_ref() - .unwrap() - .es_desc - .dec_config - .dec_specific - .freq_index, - 15 - ); - assert_eq!(track.channel_config().unwrap(), ChannelConfig::Stereo); - assert_eq!(track.bitrate(), 839250); -} - -fn get_reader(path: &str) -> Mp4Reader> { - let f = File::open(path).unwrap(); - let f_size = f.metadata().unwrap().len(); - let reader = BufReader::new(f); - - mp4::Mp4Reader::read_header(reader, f_size).unwrap() -} - -#[test] -fn test_read_metadata() { - let want_poster = fs::read("tests/samples/big_buck_bunny.jpg").unwrap(); - let want_summary = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue."; - let mp4 = get_reader("tests/samples/big_buck_bunny_metadata.m4v"); - let metadata = mp4.metadata(); - assert_eq!(metadata.title(), Some("Big Buck Bunny".into())); - assert_eq!(metadata.year(), Some(2008)); - assert_eq!(metadata.summary(), Some(want_summary.into())); - - assert!(metadata.poster().is_some()); - let poster = metadata.poster().unwrap(); - assert_eq!(poster.len(), want_poster.len()); - assert_eq!(poster, want_poster.as_slice()); -} - -#[test] -fn test_read_fragments() { - let mp4 = get_reader("tests/samples/minimal_init.mp4"); - - assert_eq!(692, mp4.size()); - assert_eq!(5, mp4.compatible_brands().len()); - - let sample_count = mp4.sample_count(1).unwrap(); - assert_eq!(sample_count, 0); - - let f = File::open("tests/samples/minimal_fragment.m4s").unwrap(); - let f_size = f.metadata().unwrap().len(); - let frag_reader = BufReader::new(f); - - let mut mp4_fragment = mp4.read_fragment_header(frag_reader, f_size).unwrap(); - let sample_count = mp4_fragment.sample_count(1).unwrap(); - assert_eq!(sample_count, 1); - let sample_1_1 = mp4_fragment.read_sample(1, 1).unwrap().unwrap(); - assert_eq!(sample_1_1.bytes.len(), 751); - assert_eq!( - sample_1_1, - mp4::Mp4Sample { - start_time: 0, - duration: 512, - rendering_offset: 0, - is_sync: true, - bytes: mp4::Bytes::from(vec![0x0u8; 751]), +use glob::glob; +use mp4::TrackType; +use tokio::fs::File; +use tokio::io::BufReader; + +#[tokio::test] +async fn test_read_mp4() { + let paths = glob("./assets/videos/*.mp4").expect("Failed to read glob pattern"); + + for path in paths { + if let Ok(path) = path { + println!("\n{}", path.display()); + let f = File::open(path).await.unwrap(); + let mut reader = BufReader::new(f); + + let mut mp4_file = mp4::Mp4File::new(&mut reader); + println!( + "streaming possible: {}", + mp4_file.read_header().await.unwrap() + ); + + let mut keys = mp4_file + .tracks + .iter() + .filter(|&(_, v)| v.track_type() == TrackType::Video) + .map(|(k, _)| *k); + + let track_id = keys.next().unwrap(); + let samples_len = mp4_file.tracks.get(&track_id).unwrap().samples.len(); + + for idx in 0..samples_len { + let samp = mp4_file.tracks.get(&track_id).unwrap().samples[idx].clone(); + + let data = mp4_file + .read_sample_data(track_id, idx) + .await + .unwrap() + .map(|x| x.slice(0..10)); + + // println!( + // "[{} {} {}] {} - <{}> {} +{} {:?}", + // idx + 1, + // samp.chunk_id, + // samp.offset, + // samp.is_sync, + // samp.size, + // samp.start_time, + // samp.rendering_offset, + // data.as_deref() + // ); + } } - ); - let eos = mp4_fragment.read_sample(1, 2); - assert!(eos.is_err()); + } } diff --git a/tests/samples/big_buck_bunny.jpg b/tests/samples/big_buck_bunny.jpg deleted file mode 100644 index 8c30bc62..00000000 Binary files a/tests/samples/big_buck_bunny.jpg and /dev/null differ diff --git a/tests/samples/big_buck_bunny_metadata.m4v b/tests/samples/big_buck_bunny_metadata.m4v deleted file mode 100644 index 6002e066..00000000 Binary files a/tests/samples/big_buck_bunny_metadata.m4v and /dev/null differ diff --git a/tests/samples/extended_audio_object_type.mp4 b/tests/samples/extended_audio_object_type.mp4 deleted file mode 100644 index 3d1a711a..00000000 Binary files a/tests/samples/extended_audio_object_type.mp4 and /dev/null differ diff --git a/tests/samples/minimal.mp4 b/tests/samples/minimal.mp4 deleted file mode 100644 index 9fe1e672..00000000 Binary files a/tests/samples/minimal.mp4 and /dev/null differ diff --git a/tests/samples/minimal_fragment.m4s b/tests/samples/minimal_fragment.m4s deleted file mode 100644 index 25532bc1..00000000 Binary files a/tests/samples/minimal_fragment.m4s and /dev/null differ diff --git a/tests/samples/minimal_init.mp4 b/tests/samples/minimal_init.mp4 deleted file mode 100644 index fcfe8928..00000000 Binary files a/tests/samples/minimal_init.mp4 and /dev/null differ