1
0
Fork 0
mirror of https://github.com/alfg/mp4-rust.git synced 2025-04-15 08:14:15 +00:00
This commit is contained in:
Andrey Tkachenko 2025-02-06 16:06:11 +00:00 committed by GitHub
commit c796e3892a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
72 changed files with 3003 additions and 4547 deletions

View file

@ -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"

BIN
assets/videos/cosmoc.mp4f Normal file

Binary file not shown.

View file

@ -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) {

View file

@ -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<String> = env::args().collect();
if args.len() < 3 {
println!("Usage: mp4copy <source file> <target file>");
std::process::exit(1);
}
if let Err(err) = copy(&args[1], &args[2]) {
let _ = writeln!(io::stderr(), "{}", err);
}
}
fn copy<P: AsRef<Path>>(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::<Vec<u32>>() {
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(())
}

View file

@ -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<String> = env::args().collect();
if args.len() < 2 {
println!("Usage: mp4dump <filename>");
std::process::exit(1);
}
if let Err(err) = dump(&args[1]) {
let _ = writeln!(io::stderr(), "{}", err);
}
}
fn dump<P: AsRef<Path>>(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<Vec<Box>> {
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: Mp4Box + std::fmt::Debug>(m: &M) -> Box {
Box {
name: m.box_type().to_string(),
size: m.box_size(),
summary: m.summary().unwrap(),
indent: 0,
}
}

View file

@ -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<String> = env::args().collect();
if args.len() < 2 {
println!("Usage: mp4info <filename>");
std::process::exit(1);
}
if let Err(err) = info(&args[1]) {
let _ = writeln!(io::stderr(), "{}", err);
}
}
fn info<P: AsRef<Path>>(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<String> {
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<String> {
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<String> {
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
}
}

View file

@ -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<String> = 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<P: AsRef<Path>>(filename: &P) -> Result<()> {
let f = File::open(filename)?;
let size = f.metadata()?.len();
let reader = BufReader::new(f);
async fn samples<P: AsRef<Path>>(filename: &P) -> Result<(), mp4::Error<MemoryStorageError>> {
let f = File::open(filename).await?;
let mut reader = BufReader::new(f);
let mut mp4 = mp4::Mp4Reader::read_header(reader, size)?;
let mut mp4_file = mp4::Mp4File::new(&mut reader);
println!("streaming possible: {}", mp4_file.read_header().await?);
for track_id in mp4.tracks().keys().copied().collect::<Vec<u32>>() {
let sample_count = mp4.sample_count(track_id).unwrap();
let mut keys = mp4_file
.tracks
.iter()
.filter(|&(_, v)| v.track_type() == TrackType::Video)
.map(|(k, _)| *k);
for sample_idx in 0..sample_count {
let sample_id = sample_idx + 1;
let sample = mp4.read_sample(track_id, sample_id);
let track_id = keys.next().unwrap();
let samples_len = mp4_file.tracks.get(&track_id).unwrap().samples.len();
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,
);
}
}
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(())
}

View file

@ -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::<u8>::new());
let mut writer = Mp4Writer::write_start(data, &config)?;
writer.write_end()?;
let data: Vec<u8> = writer.into_writer().into_inner();
println!("{:?}", data);
Ok(())
}

View file

@ -1,2 +0,0 @@
/Cargo.lock
/target

View file

@ -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 }

View file

@ -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<R>
where
R: Read + Seek,
{
mp4_reader: mp4::Mp4Reader<R>,
decoder: Decoder,
current_pcm_index: usize,
current_pcm: Vec<i16>,
track_id: u32,
position: u32,
}
impl<R> MpegAacDecoder<R>
where
R: Read + Seek,
{
pub fn new(reader: R, size: u64) -> Result<MpegAacDecoder<R>, &'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<u32> = 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<R> Iterator for MpegAacDecoder<R>
where
R: Read + Seek,
{
type Item = i16;
fn next(&mut self) -> Option<i16> {
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<R> Source for MpegAacDecoder<R>
where
R: Read + Seek,
{
fn current_frame_len(&self) -> Option<usize> {
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<Duration> {
return None;
}
}
fn get_bits(byte: u16, range: Range<u16>) -> 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>) -> 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<Vec<u8>> {
// 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]);
}

View file

@ -1,27 +0,0 @@
use std::env;
use std::fs::File;
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("Usage: simple <filename>");
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(),
);
}
}

View file

@ -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<E> {
#[error("{0}")]
IoError(#[from] std::io::Error),
#[error("box error: {0}")]
BoxError(#[from] BoxError),
#[error("storage error: {0}")]
DataStorageError(E),
}

282
src/file.rs Normal file
View file

@ -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<Output = Result<Self::Id, Self::Error>>;
fn read_data(
&self,
id: &Self::Id,
range: Range<u64>,
) -> impl Future<Output = Result<Bytes, Self::Error>>;
}
#[derive(Default)]
pub struct MemoryStorage {
pub data: Vec<Bytes>,
}
impl DataStorage for MemoryStorage {
type Error = MemoryStorageError;
type Id = usize;
#[inline]
async fn save_data(
&mut self,
reader: &mut (impl AsyncRead + Unpin),
) -> Result<Self::Id, Self::Error> {
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<u64>) -> Result<Bytes, Self::Error> {
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<FtypBox>,
pub emsgs: Vec<EmsgBox>,
pub tracks: HashMap<u32, Mp4Track>,
pub reader: &'a mut R,
pub offsets: BTreeSet<u64>,
pub data_blocks: Vec<DataBlock>,
}
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<bool, BoxError> {
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<Option<Bytes>, 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)
}
}

View file

@ -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<T> = std::result::Result<T, Error>;
pub type Result<T> = std::result::Result<T, BoxError>;
mod types;
pub use types::*;
@ -79,18 +10,18 @@ pub use types::*;
mod mp4box;
pub use mp4box::*;
mod file;
mod track;
pub use track::{Mp4Track, TrackConfig};
pub use track::Mp4Track;
mod reader;
pub use reader::Mp4Reader;
pub use file::*;
// mod async_reader;
// pub use async_reader::{AsyncMp4Reader, Mp4Header};
mod writer;
pub use writer::{Mp4Config, Mp4Writer};
pub fn read_mp4(f: File) -> Result<Mp4Reader<BufReader<File>>> {
let size = f.metadata()?.len();
let reader = BufReader::new(f);
let mp4 = reader::Mp4Reader::read_header(reader, size)?;
Ok(mp4)
}
// pub async fn read_mp4(f: File) -> Result<Mp4Reader<BufReader<File>>> {
// let size = f.metadata()?.len();
// let reader = BufReader::new(f);
// let mp4 = async_reader::Mp4AsyncReader::read_header(reader, size)?;
// Ok(mp4)
// }

View file

@ -1,6 +1,6 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use byteorder::{BigEndian, WriteBytesExt};
use serde::Serialize;
use std::io::{Read, Seek, Write};
use std::io::Write;
use crate::mp4box::*;
@ -49,19 +49,17 @@ impl Avc1Box {
}
}
pub fn get_type(&self) -> BoxType {
BoxType::Avc1Box
}
pub fn get_size(&self) -> u64 {
HEADER_SIZE + 8 + 70 + self.avcc.box_size()
}
fn box_type(&self) -> BoxType {
BoxType::Avc1Box
}
}
impl Mp4Box for Avc1Box {
fn box_type(&self) -> BoxType {
self.get_type()
}
const TYPE: BoxType = BoxType::Avc1Box;
fn box_size(&self) -> u64 {
self.get_size()
@ -80,59 +78,47 @@ impl Mp4Box for Avc1Box {
}
}
impl<R: Read + Seek> ReadBox<&mut R> for Avc1Box {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
impl BlockReader for Avc1Box {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
reader.get_u32(); // reserved
reader.get_u16(); // reserved
reader.read_u32::<BigEndian>()?; // reserved
reader.read_u16::<BigEndian>()?; // reserved
let data_reference_index = reader.read_u16::<BigEndian>()?;
let data_reference_index = reader.get_u16();
reader.read_u32::<BigEndian>()?; // pre-defined, reserved
reader.read_u64::<BigEndian>()?; // pre-defined
reader.read_u32::<BigEndian>()?; // pre-defined
let width = reader.read_u16::<BigEndian>()?;
let height = reader.read_u16::<BigEndian>()?;
let horizresolution = FixedPointU16::new_raw(reader.read_u32::<BigEndian>()?);
let vertresolution = FixedPointU16::new_raw(reader.read_u32::<BigEndian>()?);
reader.read_u32::<BigEndian>()?; // reserved
let frame_count = reader.read_u16::<BigEndian>()?;
skip_bytes(reader, 32)?; // compressorname
let depth = reader.read_u16::<BigEndian>()?;
reader.read_i16::<BigEndian>()?; // pre-defined
reader.get_u32(); // pre-defined, reserved
reader.get_u64(); // pre-defined
reader.get_u32(); // pre-defined
let end = start + size;
loop {
let current = reader.stream_position()?;
if current >= end {
return Err(Error::InvalidData("avcc not found"));
}
let header = BoxHeader::read(reader)?;
let BoxHeader { name, size: s } = header;
if s > size {
return Err(Error::InvalidData(
"avc1 box contains a box with a larger size than it",
));
}
if name == BoxType::AvcCBox {
let avcc = AvcCBox::read_box(reader, s)?;
let width = reader.get_u16();
let height = reader.get_u16();
skip_bytes_to(reader, start + size)?;
let horizresolution = FixedPointU16::new_raw(reader.get_u32());
let vertresolution = FixedPointU16::new_raw(reader.get_u32());
return Ok(Avc1Box {
data_reference_index,
width,
height,
horizresolution,
vertresolution,
frame_count,
depth,
avcc,
});
} else {
skip_bytes_to(reader, current + s)?;
}
}
reader.get_u32(); // reserved
let frame_count = reader.get_u16();
reader.skip(32); // compressorname
let depth = reader.get_u16();
reader.get_i16(); // pre-defined
Ok(Avc1Box {
data_reference_index,
width,
height,
horizresolution,
vertresolution,
frame_count,
depth,
avcc: reader.find_box::<AvcCBox>()?,
})
}
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<R: Read + Seek> ReadBox<&mut R> for AvcCBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
impl BlockReader for AvcCBox {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
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<R: Read + Seek> ReadBox<&mut R> for AvcCBox {
picture_parameter_sets,
})
}
fn size_hint() -> usize {
7
}
}
impl<W: Write> WriteBox<&mut W> for AvcCBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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<R: Read + Seek>(reader: &mut R) -> Result<Self> {
let length = reader.read_u16::<BigEndian>()? 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<Self> {
let length = reader.try_get_u16()? as usize;
Ok(NalUnit {
bytes: reader.collect(length)?,
})
}
fn write<W: Write>(&self, writer: &mut W) -> Result<u64> {
@ -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);
}
}

View file

@ -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<u64>,
}
impl<'a> IntoIterator for &'a Co64Box {
type Item = u64;
type IntoIter = std::iter::Copied<std::slice::Iter<'a, u64>>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.entries.iter().copied()
}
}
impl IntoIterator for Co64Box {
type Item = u64;
type IntoIter = std::vec::IntoIter<u64>;
#[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<R: Read + Seek> ReadBox<&mut R> for Co64Box {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
impl BlockReader for Co64Box {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
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::<u32>(); // entry_count
let entry_size = size_of::<u64>(); // chunk_offset
let entry_count = reader.read_u32::<BigEndian>()?;
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::<BigEndian>()?;
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<W: Write> WriteBox<&mut W> for Co64Box {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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<W: Write> 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);
}
}

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for CttsBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
impl BlockReader for CttsBox {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
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::<BigEndian>()?;
let entry_count = reader.get_u32();
let entry_size = size_of::<u32>() + size_of::<i32>(); // sample_count + sample_offset
// (sample_offset might be a u32, but the size is the same.)
let other_size = size_of::<i32>(); // 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::<BigEndian>()?,
sample_offset: reader.read_i32::<BigEndian>()?,
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<W: Write> WriteBox<&mut W> for CttsBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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<W: Write> 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);
}
}

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for DataBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
impl BlockReader for DataBox {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
let data_type = DataType::try_from(reader.get_u32())?;
reader.get_u32(); // reserved = 0
let data_type = DataType::try_from(reader.read_u32::<BigEndian>()?)?;
Ok(DataBox {
data: reader.collect(reader.remaining())?,
data_type,
})
}
reader.read_u32::<BigEndian>()?; // reserved = 0
let current = reader.stream_position()?;
let mut data = vec![0u8; (start + size - current) as usize];
reader.read_exact(&mut data)?;
Ok(DataBox { data, data_type })
fn size_hint() -> usize {
8
}
}
impl<W: Write> WriteBox<&mut W> for DataBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write(writer)?;
BoxHeader::new(Self::TYPE, size).write(writer)?;
writer.write_u32::<BigEndian>(self.data_type.clone() as u32)?;
writer.write_u32::<BigEndian>(0)?; // reserved = 0
@ -79,10 +73,9 @@ impl<W: Write> 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);
}
}

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for DinfBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
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<Self> {
Ok(DinfBox {
dref: dref.unwrap(),
dref: reader.find_box::<DrefBox>()?,
})
}
fn size_hint() -> usize {
DrefBox::size_hint()
}
}
impl<W: Write> WriteBox<&mut W> for DinfBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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<R: Read + Seek> ReadBox<&mut R> for DrefBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
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<Self> {
let (version, flags) = read_box_header_ext(reader);
let mut url = None;
let entry_count = reader.get_u32();
let entry_count = reader.read_u32::<BigEndian>()?;
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<W: Write> WriteBox<&mut W> for DrefBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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<R: Read + Seek> ReadBox<&mut R> for UrlBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
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<Self> {
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<W: Write> WriteBox<&mut W> for UrlBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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)?;

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for EdtsBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
impl BlockReader for EdtsBox {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
Ok(EdtsBox {
elst: reader.try_find_box::<ElstBox>()?,
})
}
let mut edts = EdtsBox::new();
let header = BoxHeader::read(reader)?;
let BoxHeader { name, size: s } = header;
if s > size {
return Err(Error::InvalidData(
"edts box contains a box with a larger size than it",
));
}
if let BoxType::ElstBox = name {
let elst = ElstBox::read_box(reader, s)?;
edts.elst = Some(elst);
}
skip_bytes_to(reader, start + size)?;
Ok(edts)
fn size_hint() -> usize {
0
}
}
impl<W: Write> WriteBox<&mut W> for EdtsBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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)?;

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for ElstBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
impl BlockReader for ElstBox {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
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::<BigEndian>()?;
let other_size = size_of::<i32>(); // 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<R: Read + Seek> ReadBox<&mut R> for ElstBox {
} else {
size_of::<u32>() + size_of::<i32>() // segment_duration + media_time
};
entry_size += size_of::<i16>() + size_of::<i16>(); // 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::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
)
(reader.get_u64(), reader.get_u64())
} else {
(
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()? 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::<BigEndian>()?,
media_rate_fraction: reader.read_u16::<BigEndian>()?,
};
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<W: Write> WriteBox<&mut W> for ElstBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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<W: Write> 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);
}
}

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for EmsgBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
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<Self> {
let (version, flags) = read_box_header_ext(reader);
let (
timescale,
@ -73,38 +69,31 @@ impl<R: Read + Seek> 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::<BigEndian>()?,
reader.get_u32(),
None,
Some(reader.read_u32::<BigEndian>()?),
reader.read_u32::<BigEndian>()?,
reader.read_u32::<BigEndian>()?,
Some(reader.get_u32()),
reader.get_u32(),
reader.get_u32(),
scheme_id_uri,
value,
)
}
1 => (
reader.read_u32::<BigEndian>()?,
Some(reader.read_u64::<BigEndian>()?),
reader.get_u32(),
Some(reader.get_u64()),
None,
reader.read_u32::<BigEndian>()?,
reader.read_u32::<BigEndian>()?,
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<R: Read + Seek> ReadBox<&mut R> for EmsgBox {
id,
scheme_id_uri,
value,
message_data,
message_data: reader.collect(reader.remaining())?,
})
}
fn size_hint() -> usize {
22
}
}
impl<W: Write> WriteBox<&mut W> for EmsgBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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<W: Write> 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<W: Write> WriteBox<&mut W> for EmsgBox {
}
}
fn read_null_terminated_utf8_string<R: Read + Seek>(reader: &mut R) -> Result<String> {
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<W: Write>(writer: &mut W, string: &str) -> Result<()> {
for byte in string.bytes() {
writer.write_u8(byte)?;
@ -180,14 +157,13 @@ fn write_null_terminated_str<W: Write>(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);
}
}

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for FtypBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
impl BlockReader for FtypBox {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
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::<BigEndian>()?;
let minor = reader.read_u32::<BigEndian>()?;
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::<BigEndian>()?;
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<W: Write> WriteBox<&mut W> for FtypBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write(writer)?;
BoxHeader::new(Self::TYPE, size).write(writer)?;
writer.write_u32::<BigEndian>((&self.major_brand).into())?;
writer.write_u32::<BigEndian>(self.minor_version)?;
@ -94,10 +90,9 @@ impl<W: Write> 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);
}
}

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for HdlrBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
impl BlockReader for HdlrBox {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
let (version, flags) = read_box_header_ext(reader);
let (version, flags) = read_box_header_ext(reader)?;
reader.get_u32(); // pre-defined
reader.read_u32::<BigEndian>()?; // pre-defined
let handler = reader.read_u32::<BigEndian>()?;
let handler = reader.get_u32();
skip_bytes(reader, 12)?; // reserved
let buf_size = size
.checked_sub(HEADER_SIZE + HEADER_EXT_SIZE + 20)
.ok_or(Error::InvalidData("hdlr size too small"))?;
let mut buf = vec![0u8; buf_size as usize];
reader.read_exact(&mut buf)?;
if let Some(end) = buf.iter().position(|&b| b == b'\0') {
buf.truncate(end);
}
let handler_string = String::from_utf8(buf).unwrap_or_default();
skip_bytes_to(reader, start + size)?;
reader.skip(12);
Ok(HdlrBox {
version,
flags,
handler_type: From::from(handler),
name: handler_string,
name: reader.get_null_terminated_string(),
})
}
fn size_hint() -> usize {
24
}
}
impl<W: Write> WriteBox<&mut W> for HdlrBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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<W: Write> 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);
}
}

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for Hev1Box {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
impl BlockReader for Hev1Box {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
reader.get_u32(); // reserved
reader.get_u16(); // reserved
reader.read_u32::<BigEndian>()?; // reserved
reader.read_u16::<BigEndian>()?; // reserved
let data_reference_index = reader.read_u16::<BigEndian>()?;
let data_reference_index = reader.get_u16();
reader.read_u32::<BigEndian>()?; // pre-defined, reserved
reader.read_u64::<BigEndian>()?; // pre-defined
reader.read_u32::<BigEndian>()?; // pre-defined
let width = reader.read_u16::<BigEndian>()?;
let height = reader.read_u16::<BigEndian>()?;
let horizresolution = FixedPointU16::new_raw(reader.read_u32::<BigEndian>()?);
let vertresolution = FixedPointU16::new_raw(reader.read_u32::<BigEndian>()?);
reader.read_u32::<BigEndian>()?; // reserved
let frame_count = reader.read_u16::<BigEndian>()?;
skip_bytes(reader, 32)?; // compressorname
let depth = reader.read_u16::<BigEndian>()?;
reader.read_i16::<BigEndian>()?; // pre-defined
reader.get_u32(); // pre-defined, reserved
reader.get_u64(); // pre-defined
reader.get_u32(); // pre-defined
let header = BoxHeader::read(reader)?;
let BoxHeader { name, size: s } = header;
if s > size {
return Err(Error::InvalidData(
"hev1 box contains a box with a larger size than it",
));
}
if name == BoxType::HvcCBox {
let hvcc = HvcCBox::read_box(reader, s)?;
let width = reader.get_u16();
let height = reader.get_u16();
skip_bytes_to(reader, start + size)?;
let horizresolution = FixedPointU16::new_raw(reader.get_u32());
let vertresolution = FixedPointU16::new_raw(reader.get_u32());
Ok(Hev1Box {
data_reference_index,
width,
height,
horizresolution,
vertresolution,
frame_count,
depth,
hvcc,
})
} else {
Err(Error::InvalidData("hvcc not found"))
}
reader.get_u32(); // reserved
let frame_count = reader.get_u16();
reader.skip(32); // compressorname
let depth = reader.get_u16();
reader.get_i16(); // pre-defined
Ok(Hev1Box {
data_reference_index,
width,
height,
horizresolution,
vertresolution,
frame_count,
depth,
hvcc: reader.find_box::<HvcCBox>()?,
})
}
fn size_hint() -> usize {
78
}
}
impl<W: Write> WriteBox<&mut W> for Hev1Box {
fn write_box(&self, writer: &mut W) -> Result<u64> {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write(writer)?;
BoxHeader::new(Self::TYPE, size).write(writer)?;
writer.write_u32::<BigEndian>(0)?; // reserved
writer.write_u16::<BigEndian>(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<String> {
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<HvcCArrayNalu>,
}
impl<R: Read + Seek> ReadBox<&mut R> for HvcCBox {
fn read_box(reader: &mut R, _size: u64) -> Result<Self> {
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<Self> {
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::<BigEndian>()?;
let general_constraint_indicator_flag = reader.read_u48::<BigEndian>()?;
let general_level_idc = reader.read_u8()?;
let min_spatial_segmentation_idc = reader.read_u16::<BigEndian>()? & 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::<BigEndian>()?;
let general_profile_compatibility_flags = reader.get_u32();
let general_constraint_indicator_flag = reader.get_u48();
let params = reader.read_u8()?;
let general_level_idc = reader.get_u8();
let min_spatial_segmentation_idc = reader.get_u16() & 0x0FFF;
let parallelism_type = reader.get_u8() & 0b11;
let chroma_format_idc = reader.get_u8() & 0b11;
let bit_depth_luma_minus8 = reader.get_u8() & 0b111;
let bit_depth_chroma_minus8 = reader.get_u8() & 0b111;
let avg_frame_rate = reader.get_u16();
let params = reader.get_u8();
let constant_frame_rate = params & 0b11000000 >> 6;
let num_temporal_layers = params & 0b00111000 >> 3;
let temporal_id_nested = (params & 0b00000100 >> 2) > 0;
let length_size_minus_one = params & 0b000011;
let num_of_arrays = reader.read_u8()?;
let num_of_arrays = reader.get_u8();
if reader.remaining() < num_of_arrays as usize * 3 {
return Err(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::<BigEndian>()?;
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::<BigEndian>()?;
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<R: Read + Seek> ReadBox<&mut R> for HvcCBox {
arrays,
})
}
fn size_hint() -> usize {
23
}
}
impl<W: Write> WriteBox<&mut W> for HvcCBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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<W: Write> 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);
}
}

View file

@ -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<MetadataKey, IlstItemBox>,
pub items: HashMap<MetadataKey, DataBox>,
}
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<R: Read + Seek> ReadBox<&mut R> for IlstBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
impl BlockReader for IlstBox {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
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::<DataBox>()? {
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::<DataBox>()? {
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::<DataBox>()? {
items.insert(MetadataKey::Poster, cover);
}
}
BoxType::DescBox => {
items.insert(MetadataKey::Summary, IlstItemBox::read_box(reader, s)?);
}
_ => {
// XXX warn!()
skip_box(reader, s)?;
if let Some(summary) = bx.inner.try_find_box::<DataBox>()? {
items.insert(MetadataKey::Summary, summary);
}
}
_ => continue,
}
current = reader.stream_position()?;
}
skip_bytes_to(reader, start + size)?;
// dbg!(&items);
Ok(IlstBox { items })
}
fn size_hint() -> usize {
0
}
}
impl<W: Write> WriteBox<&mut W> for IlstBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write(writer)?;
BoxHeader::new(Self::TYPE, size).write(writer)?;
for (key, value) in &self.items {
let name = match key {
@ -104,67 +98,16 @@ impl<W: Write> 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)?;
let size = HEADER_SIZE + value.box_size(); // Size of IlstItem + DataBox
BoxHeader::new(name, size).write(writer)?;
value.write_box(writer)?;
}
Ok(size)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
pub struct IlstItemBox {
pub data: DataBox,
}
impl IlstItemBox {
fn get_size(&self) -> u64 {
HEADER_SIZE + self.data.box_size()
}
}
impl<R: Read + Seek> ReadBox<&mut R> for IlstItemBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
let mut data = None;
let mut current = reader.stream_position()?;
let end = start + size;
while current < end {
// Get box header.
let header = BoxHeader::read(reader)?;
let BoxHeader { name, size: s } = header;
if s > size {
return Err(Error::InvalidData(
"ilst item box contains a box with a larger size than it",
));
}
match name {
BoxType::DataBox => {
data = Some(DataBox::read_box(reader, s)?);
}
_ => {
// XXX warn!()
skip_box(reader, s)?;
}
}
current = reader.stream_position()?;
}
if data.is_none() {
return Err(Error::BoxNotFound(BoxType::DataBox));
}
skip_bytes_to(reader, start + size)?;
Ok(IlstItemBox {
data: data.unwrap(),
})
}
}
impl<'a> Metadata<'a> for IlstBox {
fn title(&self) -> Option<Cow<str>> {
self.items.get(&MetadataKey::Title).map(item_to_str)
@ -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<str> {
String::from_utf8_lossy(&item.data.data)
fn item_to_str(item: &DataBox) -> Cow<str> {
String::from_utf8_lossy(&item.data)
}
fn item_to_u32(item: &IlstItemBox) -> Option<u32> {
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::<u32>().ok(),
fn item_to_u32(item: &DataBox) -> Option<u32> {
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::<u32>().ok(),
_ => None,
}
}
@ -203,22 +146,20 @@ fn item_to_u32(item: &IlstItemBox) -> Option<u32> {
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);
}
}

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for MdhdBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
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<Self> {
let (version, flags) = read_box_header_ext(reader);
let (creation_time, modification_time, timescale, duration) = if version == 1 {
(
reader.read_u64::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
reader.read_u32::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
reader.get_u64(),
reader.get_u64(),
reader.get_u32(),
reader.get_u64(),
)
} else if version == 0 {
(
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()?,
reader.read_u32::<BigEndian>()? 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::<BigEndian>()?;
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<R: Read + Seek> ReadBox<&mut R> for MdhdBox {
language,
})
}
fn size_hint() -> usize {
22
}
}
impl<W: Write> WriteBox<&mut W> for MdhdBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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<W: Write> WriteBox<&mut W> for MdhdBox {
writer.write_u32::<BigEndian>(self.timescale)?;
writer.write_u32::<BigEndian>(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);
}
}

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for MdiaBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
impl BlockReader for MdiaBox {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
let (mdhd, hdlr, minf) = reader.find_box3()?;
Ok(MdiaBox { mdhd, hdlr, minf })
}
let mut mdhd = None;
let mut hdlr = None;
let mut minf = None;
let mut current = reader.stream_position()?;
let end = start + size;
while current < end {
// Get box header.
let header = BoxHeader::read(reader)?;
let BoxHeader { name, size: s } = header;
if s > size {
return Err(Error::InvalidData(
"mdia box contains a box with a larger size than it",
));
}
match name {
BoxType::MdhdBox => {
mdhd = Some(MdhdBox::read_box(reader, s)?);
}
BoxType::HdlrBox => {
hdlr = Some(HdlrBox::read_box(reader, s)?);
}
BoxType::MinfBox => {
minf = Some(MinfBox::read_box(reader, s)?);
}
_ => {
// XXX warn!()
skip_box(reader, s)?;
}
}
current = reader.stream_position()?;
}
if mdhd.is_none() {
return Err(Error::BoxNotFound(BoxType::MdhdBox));
}
if hdlr.is_none() {
return Err(Error::BoxNotFound(BoxType::HdlrBox));
}
if minf.is_none() {
return Err(Error::BoxNotFound(BoxType::MinfBox));
}
skip_bytes_to(reader, start + size)?;
Ok(MdiaBox {
mdhd: mdhd.unwrap(),
hdlr: hdlr.unwrap(),
minf: minf.unwrap(),
})
fn size_hint() -> usize {
0
}
}
impl<W: Write> WriteBox<&mut W> for MdiaBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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)?;

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for MehdBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
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<Self> {
let (version, flags) = read_box_header_ext(reader);
let fragment_duration = if version == 1 {
reader.read_u64::<BigEndian>()?
reader.get_u64()
} else if version == 0 {
reader.read_u32::<BigEndian>()? 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<R: Read + Seek> ReadBox<&mut R> for MehdBox {
fragment_duration,
})
}
fn size_hint() -> usize {
8
}
}
impl<W: Write> WriteBox<&mut W> for MehdBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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<W: Write> WriteBox<&mut W> for MehdBox {
} else if self.version == 0 {
writer.write_u32::<BigEndian>(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<W: Write> 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);
}
}

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for MetaBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
let extended_header = reader.read_u32::<BigEndian>()?;
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::<BigEndian>()?);
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<Self> {
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;
let mut boxes = HashMap::new();
while let Some(mut bx) = reader.get_box()? {
boxes.insert(bx.kind, bx.inner.collect_remaining());
}
// find the hdlr box
let mut hdlr = None;
while current < end {
// Get box header.
let header = BoxHeader::read(reader)?;
let BoxHeader { name, size: s } = header;
let hdlr = HdlrBox::read_block(&mut boxes.remove(&BoxType::HdlrBox).unwrap().as_slice())?;
match name {
BoxType::HdlrBox => {
hdlr = Some(HdlrBox::read_box(reader, s)?);
}
_ => {
// XXX warn!()
skip_box(reader, s)?;
}
}
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::<Vec<(BoxType, Vec<u8>)>>(),
},
})
}
current = reader.stream_position()?;
}
let Some(hdlr) = hdlr else {
return Err(Error::BoxNotFound(BoxType::HdlrBox));
};
// rewind and handle the other boxes
reader.seek(SeekFrom::Start(content_start))?;
current = reader.stream_position()?;
let mut ilst = None;
match hdlr.handler_type {
MDIR => {
while current < end {
// Get box header.
let header = BoxHeader::read(reader)?;
let BoxHeader { name, size: s } = header;
match name {
BoxType::IlstBox => {
ilst = Some(IlstBox::read_box(reader, s)?);
}
_ => {
// XXX warn!()
skip_box(reader, s)?;
}
}
current = reader.stream_position()?;
}
Ok(MetaBox::Mdir { ilst })
}
_ => {
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()?;
}
Ok(MetaBox::Unknown { hdlr, data })
}
}
fn size_hint() -> usize {
8
}
}
impl<W: Write> WriteBox<&mut W> for MetaBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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<W: Write> 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);
}
}

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for MfhdBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
let (version, flags) = read_box_header_ext(reader)?;
let sequence_number = reader.read_u32::<BigEndian>()?;
skip_bytes_to(reader, start + size)?;
impl BlockReader for MfhdBox {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
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<W: Write> WriteBox<&mut W> for MfhdBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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::<BigEndian>(self.sequence_number)?;
@ -83,10 +80,9 @@ impl<W: Write> 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);
}
}

View file

@ -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,57 +52,17 @@ impl Mp4Box for MinfBox {
}
}
impl<R: Read + Seek> ReadBox<&mut R> for MinfBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
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<Self> {
let (vmhd, smhd, dinf, stbl) = reader.try_find_box4()?;
if dinf.is_none() {
return Err(Error::BoxNotFound(BoxType::DinfBox));
}
if stbl.is_none() {
return Err(Error::BoxNotFound(BoxType::StblBox));
return Err(BoxError::BoxNotFound(BoxType::DinfBox));
}
skip_bytes_to(reader, start + size)?;
if stbl.is_none() {
return Err(BoxError::BoxNotFound(BoxType::StblBox));
}
Ok(MinfBox {
vmhd,
@ -113,12 +71,16 @@ impl<R: Read + Seek> ReadBox<&mut R> for MinfBox {
stbl: stbl.unwrap(),
})
}
fn size_hint() -> usize {
DinfBox::size_hint() + StblBox::size_hint()
}
}
impl<W: Write> WriteBox<&mut W> for MinfBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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)?;

View file

@ -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<u32> 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<String>;
fn summary(&self) -> Result<String>;
}
pub trait ReadBox<T>: Sized {
fn read_box(_: T, size: u64) -> Result<Self>;
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<T: Mp4Box + BlockReader>(&mut self) -> Result<Option<T>> {
if T::TYPE == self.kind {
Ok(Some(T::read_block(&mut self.inner)?))
} else {
Ok(None)
}
}
#[inline]
pub fn read<T: Mp4Box + BlockReader>(&mut self) -> Result<T> {
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<impl Reader<'a> + '_>;
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<u8> {
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<u16> {
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<u32> {
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<u32> {
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<u64> {
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<u64> {
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<i8> {
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<i16> {
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<i32> {
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<i32> {
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<i64> {
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<i64> {
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<Vec<u8>> {
let mut buf = vec![0; size];
self.copy_to_slice(&mut buf)?;
Ok(buf)
}
#[inline]
fn collect_remaining(&mut self) -> Vec<u8> {
self.collect(self.remaining()).unwrap()
}
fn copy_to_slice(&mut self, slice: &mut [u8]) -> Result<()>;
fn get_box(&mut self) -> Result<Option<BoxReader<'a, impl Reader<'a> + '_>>>;
fn find_box<B: Mp4Box + BlockReader>(&mut self) -> Result<B> {
self.try_find_box()
.and_then(|x| x.ok_or_else(|| BoxError::InvalidData("expected box")))
}
fn try_find_box2<A: Mp4Box + BlockReader, B: Mp4Box + BlockReader>(
&mut self,
) -> Result<(Option<A>, Option<B>)> {
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>()? {
a = Some(inner);
continue;
}
}
if b.is_none() {
if let Some(inner) = bx.try_read::<B>()? {
b = Some(inner);
continue;
}
}
println!(" 1 unknown box {}", bx.kind);
}
Ok((a, b))
}
fn try_find_box3<A, B, C>(&mut self) -> Result<(Option<A>, Option<B>, Option<C>)>
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>()? {
a = Some(inner);
continue;
}
}
if b.is_none() {
if let Some(inner) = bx.try_read::<B>()? {
b = Some(inner);
continue;
}
}
if c.is_none() {
if let Some(inner) = bx.try_read::<C>()? {
c = Some(inner);
continue;
}
}
println!(" 2 unknown box {}", bx.kind);
}
Ok((a, b, c))
}
#[inline]
fn find_box3<A, B, C>(&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<A, B, C, D>(&mut self) -> Result<(Option<A>, Option<B>, Option<C>, Option<D>)>
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>()? {
a = Some(inner);
continue;
}
}
if b.is_none() {
if let Some(inner) = bx.try_read::<B>()? {
b = Some(inner);
continue;
}
}
if c.is_none() {
if let Some(inner) = bx.try_read::<C>()? {
c = Some(inner);
continue;
}
}
if d.is_none() {
if let Some(inner) = bx.try_read::<D>()? {
d = Some(inner);
continue;
}
}
println!(" 3 unknown box {}", bx.kind);
}
Ok((a, b, c, d))
}
#[inline]
fn try_find_box<B: Mp4Box + BlockReader>(&mut self) -> Result<Option<B>> {
while let Some(mut bx) = self.get_box()? {
if let Some(inner) = bx.try_read::<B>()? {
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<impl Reader<'a> + '_> {
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<Option<BoxReader<'a, impl Reader<'a> + '_>>> {
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<Self>;
fn size_hint() -> usize;
}
pub trait WriteBox<T>: Sized {
@ -258,72 +723,138 @@ pub trait WriteBox<T>: 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<Option<Self>> {
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<R: Read>(reader: &mut R) -> Result<Self> {
pub async fn read<R: AsyncRead + Unpin>(
reader: &mut R,
offset: &mut u64,
) -> Result<Option<Self>> {
// 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 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()),
},
}
*offset += 8;
let largesize = u64::from_be_bytes(buf);
Ok(BoxHeader {
name: BoxType::from(typ),
// Subtract the length of the serialized largesize, as callers assume `size - HEADER_SIZE` is the length
// of the box data. Disallow `largesize < 16`, or else a largesize of 8 will result in a BoxHeader::size
// of 0, incorrectly indicating that the box data extends to the end of the stream.
size: match largesize {
0 => 0,
1..=15 => return Err(Error::InvalidData("64-bit box size too small")),
16..=u64::MAX => largesize - 8,
},
})
// Subtract the length of the serialized largesize, as callers assume `size - HEADER_SIZE` is the length
// of the box data. Disallow `largesize < 16`, or else a largesize of 8 will result in a BoxHeader::size
// of 0, incorrectly indicating that the box data extends to the end of the stream.
match largesize {
0 => 0,
1..=15 => return Err(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<W: Write>(&self, writer: &mut W) -> Result<u64> {
if self.size > u32::MAX as u64 {
writer.write_u32::<BigEndian>(1)?;
writer.write_u32::<BigEndian>(self.name.into())?;
writer.write_u32::<BigEndian>(self.kind.into())?;
writer.write_u64::<BigEndian>(self.size)?;
Ok(16)
} else {
writer.write_u32::<BigEndian>(self.size as u32)?;
writer.write_u32::<BigEndian>(self.name.into())?;
writer.write_u32::<BigEndian>(self.kind.into())?;
Ok(8)
}
}
}
pub fn read_box_header_ext<R: Read>(reader: &mut R) -> Result<(u8, u32)> {
let version = reader.read_u8()?;
let flags = reader.read_u24::<BigEndian>()?;
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: Write>(w: &mut W, v: u8, f: u32) -> Result<u64> {
@ -332,26 +863,6 @@ pub fn write_box_header_ext<W: Write>(w: &mut W, v: u8, f: u32) -> Result<u64> {
Ok(4)
}
pub fn box_start<R: Seek>(seeker: &mut R) -> Result<u64> {
Ok(seeker.stream_position()? - HEADER_SIZE)
}
pub fn skip_bytes<S: Seek>(seeker: &mut S, size: u64) -> Result<()> {
seeker.seek(SeekFrom::Current(size as i64))?;
Ok(())
}
pub fn skip_bytes_to<S: Seek>(seeker: &mut S, pos: u64) -> Result<()> {
seeker.seek(SeekFrom::Start(pos))?;
Ok(())
}
pub fn skip_box<S: Seek>(seeker: &mut S, size: u64) -> Result<()> {
let start = box_start(seeker)?;
skip_bytes_to(seeker, start + size)?;
Ok(())
}
pub fn write_zeros<W: Write>(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, .. }))));
}
}

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for MoofBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
impl BlockReader for MoofBox {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
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<W: Write> WriteBox<&mut W> for MoofBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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() {

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for MoovBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
impl BlockReader for MoovBox {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
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)?);
}
BoxType::MvexBox => {
mvex = Some(MvexBox::read_box(reader, s)?);
}
BoxType::TrakBox => {
let trak = TrakBox::read_box(reader, s)?;
traks.push(trak);
}
BoxType::UdtaBox => {
udta = Some(UdtaBox::read_box(reader, s)?);
}
_ => {
// XXX warn!()
skip_box(reader, s)?;
}
}
current = reader.stream_position()?;
BoxType::MetaBox => {
meta = Some(bx.read()?);
}
BoxType::MvexBox => {
mvex = Some(bx.read()?);
}
BoxType::TrakBox => {
traks.push(bx.read()?);
}
BoxType::UdtaBox => {
udta = Some(bx.read()?);
}
_ => continue,
}
}
if mvhd.is_none() {
return Err(Error::BoxNotFound(BoxType::MvhdBox));
return Err(BoxError::BoxNotFound(BoxType::MvhdBox));
}
skip_bytes_to(reader, start + size)?;
Ok(MoovBox {
mvhd: mvhd.unwrap(),
meta,
@ -123,12 +105,16 @@ impl<R: Read + Seek> ReadBox<&mut R> for MoovBox {
traks,
})
}
fn size_hint() -> usize {
MvhdBox::size_hint()
}
}
impl<W: Write> WriteBox<&mut W> for MoovBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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<W: Write> 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);
}
}

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for Mp4aBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
impl BlockReader for Mp4aBox {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
reader.get_u32(); // reserved
reader.get_u16(); // reserved
reader.read_u32::<BigEndian>()?; // reserved
reader.read_u16::<BigEndian>()?; // reserved
let data_reference_index = reader.read_u16::<BigEndian>()?;
let version = reader.read_u16::<BigEndian>()?;
reader.read_u16::<BigEndian>()?; // reserved
reader.read_u32::<BigEndian>()?; // reserved
let channelcount = reader.read_u16::<BigEndian>()?;
let samplesize = reader.read_u16::<BigEndian>()?;
reader.read_u32::<BigEndian>()?; // pre-defined, reserved
let samplerate = FixedPointU16::new_raw(reader.read_u32::<BigEndian>()?);
let data_reference_index = reader.get_u16();
let version = reader.get_u16();
reader.get_u16(); // reserved
reader.get_u32(); // reserved
let channelcount = reader.get_u16();
let samplesize = reader.get_u16();
reader.get_u32(); // pre-defined, reserved
let samplerate = FixedPointU16::new_raw(reader.get_u32());
if version == 1 {
if reader.remaining() < 16 {
return Err(BoxError::InvalidData("expected at least 16 bytes more"));
}
// Skip QTFF
reader.read_u64::<BigEndian>()?;
reader.read_u64::<BigEndian>()?;
reader.get_u64();
reader.get_u64();
}
// Find esds in mp4a or wave
let mut esds = None;
let end = start + size;
loop {
let current = reader.stream_position()?;
if current >= end {
break;
}
let header = BoxHeader::read(reader)?;
let BoxHeader { name, size: s } = header;
if s > size {
return Err(Error::InvalidData(
"mp4a box contains a box with a larger size than it",
));
}
if name == BoxType::EsdsBox {
esds = Some(EsdsBox::read_box(reader, s)?);
break;
} else if name == BoxType::WaveBox {
// Typically contains frma, mp4a, esds, and a terminator atom
} else {
// Skip boxes
let skip_to = current + s;
skip_bytes_to(reader, skip_to)?;
}
}
skip_bytes_to(reader, end)?;
Ok(Mp4aBox {
data_reference_index,
channelcount,
samplesize,
samplerate,
esds,
esds: reader.try_find_box::<EsdsBox>()?,
})
}
fn size_hint() -> usize {
28
}
}
impl<W: Write> WriteBox<&mut W> for Mp4aBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write(writer)?;
BoxHeader::new(Self::TYPE, size).write(writer)?;
writer.write_u32::<BigEndian>(0)?; // reserved
writer.write_u16::<BigEndian>(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<R: Read + Seek> ReadBox<&mut R> for EsdsBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
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<Self> {
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<W: Write> WriteBox<&mut W> for EsdsBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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<T>: Sized {
fn read_desc(_: T, size: u32) -> Result<Self>;
}
trait WriteDesc<T>: Sized {
fn write_desc(&self, _: T) -> Result<u32>;
}
fn read_desc<R: Read>(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<W: Write>(writer: &mut W, tag: u8, size: u32) -> Result<u64> {
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<R: Read + Seek> ReadDesc<&mut R> for ESDescriptor {
fn read_desc(reader: &mut R, size: u32) -> Result<Self> {
let start = reader.stream_position()?;
let es_id = reader.read_u16::<BigEndian>()?;
reader.read_u8()?; // XXX flags must be 0
impl BlockReader for ESDescriptor {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
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<R: Read + Seek> ReadDesc<&mut R> for ESDescriptor {
sl_config: sl_config.unwrap_or_default(),
})
}
fn size_hint() -> usize {
3
}
}
impl<W: Write> WriteDesc<&mut W> for ESDescriptor {
@ -424,33 +395,28 @@ impl Descriptor for DecoderConfigDescriptor {
}
}
impl<R: Read + Seek> ReadDesc<&mut R> for DecoderConfigDescriptor {
fn read_desc(reader: &mut R, size: u32) -> Result<Self> {
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<Self> {
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::<BigEndian>()?;
let max_bitrate = reader.read_u32::<BigEndian>()?;
let avg_bitrate = reader.read_u32::<BigEndian>()?;
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<R: Read + Seek> ReadDesc<&mut R> for DecoderConfigDescriptor {
dec_specific: dec_specific.unwrap_or_default(),
})
}
fn size_hint() -> usize {
13
}
}
impl<W: Write> 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<R: Read + Seek>(
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<R: Read + Seek>(
let chan_conf;
if freq_index == 15 {
// Skip the 24 bit sample rate
let sample_rate = reader.read_u24::<BigEndian>()?;
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<R: Read + Seek>(
Ok(chan_conf)
}
impl<R: Read + Seek> ReadDesc<&mut R> for DecoderSpecificDescriptor {
fn read_desc(reader: &mut R, _size: u32) -> Result<Self> {
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<Self> {
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<R: Read + Seek> ReadDesc<&mut R> for DecoderSpecificDescriptor {
chan_conf,
})
}
fn size_hint() -> usize {
2
}
}
impl<W: Write> WriteDesc<&mut W> for DecoderSpecificDescriptor {
@ -593,12 +568,16 @@ impl Descriptor for SLConfigDescriptor {
}
}
impl<R: Read + Seek> ReadDesc<&mut R> for SLConfigDescriptor {
fn read_desc(reader: &mut R, _size: u32) -> Result<Self> {
reader.read_u8()?; // pre-defined
impl BlockReader for SLConfigDescriptor {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
reader.get_u8(); // pre-defined
Ok(SLConfigDescriptor {})
}
fn size_hint() -> usize {
1
}
}
impl<W: Write> WriteDesc<&mut W> for SLConfigDescriptor {
@ -615,10 +594,9 @@ impl<W: Write> 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);
}
}

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for MvexBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
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<Self> {
let (mehd, trex) = reader.try_find_box2::<MehdBox, TrexBox>()?;
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<W: Write> WriteBox<&mut W> for MvexBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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)

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for MvhdBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
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<Self> {
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::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
reader.read_u32::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
reader.get_u64(),
reader.get_u64(),
reader.get_u32(),
reader.get_u64(),
)
} else if version == 0 {
(
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()?,
reader.read_u32::<BigEndian>()? 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::<BigEndian>()?);
let volume = FixedPointU8::new_raw(reader.read_u16::<BigEndian>()?);
let rate = FixedPointU16::new_raw(reader.get_u32());
let volume = FixedPointU8::new_raw(reader.get_u16());
reader.read_u16::<BigEndian>()?; // reserved = 0
reader.read_u64::<BigEndian>()?; // reserved = 0
reader.get_u16(); // reserved = 0
reader.get_u64(); // reserved = 0
let matrix = tkhd::Matrix {
a: reader.read_i32::<BigEndian>()?,
b: reader.read_i32::<BigEndian>()?,
u: reader.read_i32::<BigEndian>()?,
c: reader.read_i32::<BigEndian>()?,
d: reader.read_i32::<BigEndian>()?,
v: reader.read_i32::<BigEndian>()?,
x: reader.read_i32::<BigEndian>()?,
y: reader.read_i32::<BigEndian>()?,
w: reader.read_i32::<BigEndian>()?,
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::<BigEndian>()?;
skip_bytes_to(reader, start + size)?;
let next_track_id = reader.get_u32();
Ok(MvhdBox {
version,
@ -147,12 +144,16 @@ impl<R: Read + Seek> ReadBox<&mut R> for MvhdBox {
next_track_id,
})
}
fn size_hint() -> usize {
100
}
}
impl<W: Write> WriteBox<&mut W> for MvhdBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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<W: Write> WriteBox<&mut W> for MvhdBox {
writer.write_u32::<BigEndian>(self.timescale)?;
writer.write_u32::<BigEndian>(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::<BigEndian>(self.rate.raw_value())?;
@ -199,10 +200,9 @@ impl<W: Write> 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);
}
}

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for SmhdBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
let (version, flags) = read_box_header_ext(reader)?;
let balance = FixedPointI8::new_raw(reader.read_i16::<BigEndian>()?);
skip_bytes_to(reader, start + size)?;
impl BlockReader for SmhdBox {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
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<W: Write> WriteBox<&mut W> for SmhdBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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<W: Write> 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);
}
}

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for StblBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
impl BlockReader for StblBox {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
let mut stsd = None;
let mut stts = None;
let mut ctts = None;
@ -86,68 +82,63 @@ impl<R: Read + Seek> ReadBox<&mut R> for StblBox {
let mut stco = None;
let mut co64 = None;
let mut current = reader.stream_position()?;
let end = start + size;
while current < end {
// Get box header.
let header = BoxHeader::read(reader)?;
let BoxHeader { name, size: s } = header;
if s > size {
return Err(Error::InvalidData(
"stbl box contains a box with a larger size than it",
));
}
match name {
while let Some(mut bx) = reader.get_box()? {
match bx.kind {
BoxType::StsdBox => {
stsd = Some(StsdBox::read_box(reader, s)?);
stsd = Some(bx.read()?);
}
BoxType::SttsBox => {
stts = Some(SttsBox::read_box(reader, s)?);
stts = Some(bx.read()?);
}
BoxType::CttsBox => {
ctts = Some(CttsBox::read_box(reader, s)?);
ctts = Some(bx.read()?);
}
BoxType::StssBox => {
stss = Some(StssBox::read_box(reader, s)?);
stss = Some(bx.read()?);
}
BoxType::StscBox => {
stsc = Some(StscBox::read_box(reader, s)?);
stsc = Some(bx.read()?);
}
BoxType::StszBox => {
stsz = Some(StszBox::read_box(reader, s)?);
stsz = Some(bx.read()?);
}
BoxType::StcoBox => {
stco = Some(StcoBox::read_box(reader, s)?);
stco = Some(bx.read()?);
}
BoxType::Co64Box => {
co64 = Some(Co64Box::read_box(reader, s)?);
}
_ => {
// XXX warn!()
skip_box(reader, s)?;
co64 = Some(bx.read()?);
}
_ => continue,
}
current = reader.stream_position()?;
}
if stsd.is_none() {
return Err(Error::BoxNotFound(BoxType::StsdBox));
}
if stts.is_none() {
return Err(Error::BoxNotFound(BoxType::SttsBox));
}
if stsc.is_none() {
return Err(Error::BoxNotFound(BoxType::StscBox));
}
if stsz.is_none() {
return Err(Error::BoxNotFound(BoxType::StszBox));
}
if stco.is_none() && co64.is_none() {
return Err(Error::Box2NotFound(BoxType::StcoBox, BoxType::Co64Box));
return Err(BoxError::BoxNotFound(BoxType::StsdBox));
}
skip_bytes_to(reader, start + size)?;
if stts.is_none() {
return Err(BoxError::BoxNotFound(BoxType::SttsBox));
}
if stsc.is_none() {
return Err(BoxError::BoxNotFound(BoxType::StscBox));
}
if stsz.is_none() {
return Err(BoxError::BoxNotFound(BoxType::StszBox));
}
if stco.is_none() && co64.is_none() {
return Err(BoxError::Box2NotFound(BoxType::StcoBox, BoxType::Co64Box));
}
Ok(StblBox {
stsd: stsd.unwrap(),
@ -160,12 +151,16 @@ impl<R: Read + Seek> ReadBox<&mut R> for StblBox {
co64,
})
}
fn size_hint() -> usize {
StsdBox::size_hint() + SttsBox::size_hint() + StscBox::size_hint() + StszBox::size_hint()
}
}
impl<W: Write> WriteBox<&mut W> for StblBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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)?;

View file

@ -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<u32>,
}
impl<'a> IntoIterator for &'a StcoBox {
type Item = u64;
type IntoIter = std::iter::Map<std::iter::Copied<std::slice::Iter<'a, u32>>, fn(u32) -> u64>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.entries.iter().copied().map(Into::<u64>::into)
}
}
impl IntoIterator for StcoBox {
type Item = u64;
type IntoIter = std::iter::Map<std::vec::IntoIter<u32>, fn(u32) -> u64>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.entries.into_iter().map(Into::<u64>::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<R: Read + Seek> ReadBox<&mut R> for StcoBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
impl BlockReader for StcoBox {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
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::<u32>(); // entry_count
let entry_size = size_of::<u32>(); // chunk_offset
let entry_count = reader.read_u32::<BigEndian>()?;
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::<BigEndian>()?;
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<W: Write> WriteBox<&mut W> for StcoBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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);
}
}

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for StscBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
impl BlockReader for StscBox {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
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::<u32>(); // entry_count
let entry_size = size_of::<u32>() + size_of::<u32>() + size_of::<u32>(); // first_chunk + samples_per_chunk + sample_description_index
let entry_count = reader.read_u32::<BigEndian>()?;
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::<BigEndian>()?,
samples_per_chunk: reader.read_u32::<BigEndian>()?,
sample_description_index: reader.read_u32::<BigEndian>()?,
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<R: Read + Seek> 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<W: Write> WriteBox<&mut W> for StscBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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<W: Write> 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);
}
}

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for StsdBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
impl BlockReader for StsdBox {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
let (version, flags) = read_box_header_ext(reader);
let (version, flags) = read_box_header_ext(reader)?;
reader.read_u32::<BigEndian>()?; // XXX entry_count
reader.get_u32(); // XXX entry_count
let mut avc1 = None;
let mut hev1 = None;
@ -82,35 +79,31 @@ impl<R: Read + Seek> ReadBox<&mut R> for StsdBox {
let mut mp4a = None;
let mut tx3g = None;
// Get box header.
let header = BoxHeader::read(reader)?;
let BoxHeader { name, size: s } = header;
if s > size {
return Err(Error::InvalidData(
"stsd box contains a box with a larger size than it",
));
}
while let Some(mut bx) = reader.get_box()? {
match bx.kind {
BoxType::Avc1Box => {
avc1 = Some(bx.read()?);
}
match name {
BoxType::Avc1Box => {
avc1 = Some(Avc1Box::read_box(reader, s)?);
}
BoxType::Hev1Box => {
hev1 = Some(Hev1Box::read_box(reader, s)?);
}
BoxType::Vp09Box => {
vp09 = Some(Vp09Box::read_box(reader, s)?);
}
BoxType::Mp4aBox => {
mp4a = Some(Mp4aBox::read_box(reader, s)?);
}
BoxType::Tx3gBox => {
tx3g = Some(Tx3gBox::read_box(reader, s)?);
}
_ => {}
}
BoxType::Hev1Box => {
hev1 = Some(bx.read()?);
}
skip_bytes_to(reader, start + size)?;
BoxType::Vp09Box => {
vp09 = Some(bx.read()?);
}
BoxType::Mp4aBox => {
mp4a = Some(bx.read()?);
}
BoxType::Tx3gBox => {
tx3g = Some(bx.read()?);
}
_ => {}
}
}
Ok(StsdBox {
version,
@ -122,12 +115,16 @@ impl<R: Read + Seek> ReadBox<&mut R> for StsdBox {
tx3g,
})
}
fn size_hint() -> usize {
8
}
}
impl<W: Write> WriteBox<&mut W> for StsdBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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)?;

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for StssBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
impl BlockReader for StssBox {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
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::<u32>(); // entry_count
let entry_size = size_of::<u32>(); // sample_number
let entry_count = reader.read_u32::<BigEndian>()?;
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::<BigEndian>()?;
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<W: Write> WriteBox<&mut W> for StssBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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<W: Write> 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);
}
}

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for StszBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
impl BlockReader for StszBox {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
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::<u32>() + size_of::<u32>(); // sample_size + sample_count
let sample_size = reader.read_u32::<BigEndian>()?;
let sample_size = reader.get_u32();
let stsz_item_size = if sample_size == 0 {
size_of::<u32>() // entry_size
} else {
0
};
let sample_count = reader.read_u32::<BigEndian>()?;
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::<BigEndian>()?;
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<R: Read + Seek> ReadBox<&mut R> for StszBox {
sample_sizes,
})
}
fn size_hint() -> usize {
12
}
}
impl<W: Write> WriteBox<&mut W> for StszBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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<W: Write> WriteBox<&mut W> for StszBox {
writer.write_u32::<BigEndian>(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::<BigEndian>(*sample_number)?;
@ -122,10 +113,9 @@ impl<W: Write> 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);
}
}

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for SttsBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
impl BlockReader for SttsBox {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
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::<u32>(); // entry_count
let entry_size = size_of::<u32>() + size_of::<u32>(); // sample_count + sample_delta
let entry_count = reader.read_u32::<BigEndian>()?;
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::<BigEndian>()?,
sample_delta: reader.read_u32::<BigEndian>()?,
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<W: Write> WriteBox<&mut W> for SttsBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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<W: Write> 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);
}
}

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for TfdtBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
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<Self> {
let (version, flags) = read_box_header_ext(reader);
let base_media_decode_time = if version == 1 {
reader.read_u64::<BigEndian>()?
reader.get_u64()
} else if version == 0 {
reader.read_u32::<BigEndian>()? 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<W: Write> WriteBox<&mut W> for TfdtBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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<W: Write> WriteBox<&mut W> for TfdtBox {
} else if self.version == 0 {
writer.write_u32::<BigEndian>(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<W: Write> 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);
}
}

View file

@ -1,6 +1,6 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use byteorder::{BigEndian, WriteBytesExt};
use serde::Serialize;
use std::io::{Read, Seek, Write};
use std::io::Write;
use crate::mp4box::*;
@ -51,9 +51,7 @@ impl TfhdBox {
}
impl Mp4Box for TfhdBox {
fn box_type(&self) -> BoxType {
self.get_type()
}
const TYPE: BoxType = BoxType::TfhdBox;
fn box_size(&self) -> u64 {
self.get_size()
@ -69,39 +67,40 @@ impl Mp4Box for TfhdBox {
}
}
impl<R: Read + Seek> ReadBox<&mut R> for TfhdBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
impl BlockReader for TfhdBox {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
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::<BigEndian>()?;
let base_data_offset = if TfhdBox::FLAG_BASE_DATA_OFFSET & flags > 0 {
Some(reader.read_u64::<BigEndian>()?)
} else {
None
};
let sample_description_index = if TfhdBox::FLAG_SAMPLE_DESCRIPTION_INDEX & flags > 0 {
Some(reader.read_u32::<BigEndian>()?)
} else {
None
};
let default_sample_duration = if TfhdBox::FLAG_DEFAULT_SAMPLE_DURATION & flags > 0 {
Some(reader.read_u32::<BigEndian>()?)
} else {
None
};
let default_sample_size = if TfhdBox::FLAG_DEFAULT_SAMPLE_SIZE & flags > 0 {
Some(reader.read_u32::<BigEndian>()?)
} else {
None
};
let default_sample_flags = if TfhdBox::FLAG_DEFAULT_SAMPLE_FLAGS & flags > 0 {
Some(reader.read_u32::<BigEndian>()?)
Some(reader.get_u64())
} else {
None
};
skip_bytes_to(reader, start + size)?;
let sample_description_index = if TfhdBox::FLAG_SAMPLE_DESCRIPTION_INDEX & flags > 0 {
Some(reader.get_u32())
} else {
None
};
let default_sample_duration = if TfhdBox::FLAG_DEFAULT_SAMPLE_DURATION & flags > 0 {
Some(reader.get_u32())
} else {
None
};
let default_sample_size = if TfhdBox::FLAG_DEFAULT_SAMPLE_SIZE & flags > 0 {
Some(reader.get_u32())
} else {
None
};
let default_sample_flags = if TfhdBox::FLAG_DEFAULT_SAMPLE_FLAGS & flags > 0 {
Some(reader.get_u32())
} else {
None
};
Ok(TfhdBox {
version,
@ -114,12 +113,16 @@ impl<R: Read + Seek> ReadBox<&mut R> for TfhdBox {
default_sample_flags,
})
}
fn size_hint() -> usize {
8
}
}
impl<W: Write> WriteBox<&mut W> for TfhdBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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::<BigEndian>(self.track_id)?;
@ -147,10 +150,9 @@ impl<W: Write> 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);
}
}

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for TkhdBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
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<Self> {
let (version, flags) = read_box_header_ext(reader);
let (creation_time, modification_time, track_id, _, duration) = if version == 1 {
(
reader.read_u64::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
reader.read_u32::<BigEndian>()?,
reader.read_u32::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
reader.get_u64(),
reader.get_u64(),
reader.get_u32(),
reader.get_u32(),
reader.get_u64(),
)
} else if version == 0 {
(
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()?,
reader.read_u32::<BigEndian>()?,
reader.read_u32::<BigEndian>()? 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::<BigEndian>()?; // reserved
let layer = reader.read_u16::<BigEndian>()?;
let alternate_group = reader.read_u16::<BigEndian>()?;
let volume = FixedPointU8::new_raw(reader.read_u16::<BigEndian>()?);
reader.read_u16::<BigEndian>()?; // 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::<BigEndian>()?,
b: reader.read_i32::<BigEndian>()?,
u: reader.read_i32::<BigEndian>()?,
c: reader.read_i32::<BigEndian>()?,
d: reader.read_i32::<BigEndian>()?,
v: reader.read_i32::<BigEndian>()?,
x: reader.read_i32::<BigEndian>()?,
y: reader.read_i32::<BigEndian>()?,
w: reader.read_i32::<BigEndian>()?,
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::<BigEndian>()?);
let height = FixedPointU16::new_raw(reader.read_u32::<BigEndian>()?);
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<R: Read + Seek> ReadBox<&mut R> for TkhdBox {
height,
})
}
fn size_hint() -> usize {
84
}
}
impl<W: Write> WriteBox<&mut W> for TkhdBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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<W: Write> WriteBox<&mut W> for TkhdBox {
writer.write_u32::<BigEndian>(0)?; // reserved
writer.write_u32::<BigEndian>(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::<BigEndian>(0)?; // reserved
@ -261,10 +262,9 @@ impl<W: Write> 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);
}
}

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for TrafBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
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<Self> {
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<W: Write> WriteBox<&mut W> for TrafBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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 {

View file

@ -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<EdtsBox>,
#[serde(skip_serializing_if = "Option::is_none")]
pub meta: Option<MetaBox>,
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<usize> {
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<u64> {
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<u32> {
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<u64> {
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,57 +220,17 @@ impl Mp4Box for TrakBox {
}
}
impl<R: Read + Seek> ReadBox<&mut R> for TrakBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
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<Self> {
let (tkhd, edts, meta, mdia) = reader.try_find_box4()?;
if tkhd.is_none() {
return Err(Error::BoxNotFound(BoxType::TkhdBox));
}
if mdia.is_none() {
return Err(Error::BoxNotFound(BoxType::MdiaBox));
return Err(BoxError::BoxNotFound(BoxType::TkhdBox));
}
skip_bytes_to(reader, start + size)?;
if mdia.is_none() {
return Err(BoxError::BoxNotFound(BoxType::MdiaBox));
}
Ok(TrakBox {
tkhd: tkhd.unwrap(),
@ -112,12 +239,16 @@ impl<R: Read + Seek> ReadBox<&mut R> for TrakBox {
mdia: mdia.unwrap(),
})
}
fn size_hint() -> usize {
TkhdBox::size_hint() + MdiaBox::size_hint()
}
}
impl<W: Write> WriteBox<&mut W> for TrakBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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 {

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for TrexBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
impl BlockReader for TrexBox {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
let (version, flags) = read_box_header_ext(reader);
let (version, flags) = read_box_header_ext(reader)?;
let track_id = reader.read_u32::<BigEndian>()?;
let default_sample_description_index = reader.read_u32::<BigEndian>()?;
let default_sample_duration = reader.read_u32::<BigEndian>()?;
let default_sample_size = reader.read_u32::<BigEndian>()?;
let default_sample_flags = reader.read_u32::<BigEndian>()?;
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<R: Read + Seek> ReadBox<&mut R> for TrexBox {
default_sample_flags,
})
}
fn size_hint() -> usize {
24
}
}
impl<W: Write> WriteBox<&mut W> for TrexBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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<W: Write> 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);
}
}

View file

@ -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<u32>,
#[serde(skip_serializing)]
pub sample_sizes: Vec<u32>,
#[serde(skip_serializing)]
pub sample_flags: Vec<u32>,
#[serde(skip_serializing)]
pub sample_cts: Vec<u32>,
}
@ -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<R: Read + Seek> ReadBox<&mut R> for TrunBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
impl BlockReader for TrunBox {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
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::<u32>() // sample_count
+ if TrunBox::FLAG_DATA_OFFSET & flags > 0 { size_of::<i32>() } else { 0 } // data_offset
+ if TrunBox::FLAG_FIRST_SAMPLE_FLAGS & flags > 0 { size_of::<u32>() } else { 0 }; // first_sample_flags
let sample_size = if TrunBox::FLAG_SAMPLE_DURATION & flags > 0 { size_of::<u32>() } else { 0 } // sample_duration
+ if TrunBox::FLAG_SAMPLE_SIZE & flags > 0 { size_of::<u32>() } else { 0 } // sample_size
+ if TrunBox::FLAG_SAMPLE_FLAGS & flags > 0 { size_of::<u32>() } else { 0 } // sample_flags
+ if TrunBox::FLAG_SAMPLE_CTS & flags > 0 { size_of::<u32>() } else { 0 }; // sample_composition_time_offset
let sample_count = reader.read_u32::<BigEndian>()?;
let sample_count = reader.get_u32();
let data_offset = if TrunBox::FLAG_DATA_OFFSET & flags > 0 {
Some(reader.read_i32::<BigEndian>()?)
Some(reader.try_get_i32()?)
} else {
None
};
let first_sample_flags = if TrunBox::FLAG_FIRST_SAMPLE_FLAGS & flags > 0 {
Some(reader.read_u32::<BigEndian>()?)
Some(reader.try_get_u32()?)
} else {
None
};
@ -111,52 +106,51 @@ impl<R: Read + Seek> 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::<BigEndian>()?;
let duration = reader.get_u32();
sample_durations.push(duration);
}
if TrunBox::FLAG_SAMPLE_SIZE & flags > 0 {
let sample_size = reader.read_u32::<BigEndian>()?;
let sample_size = reader.get_u32();
sample_sizes.push(sample_size);
}
if TrunBox::FLAG_SAMPLE_FLAGS & flags > 0 {
let sample_flag = reader.read_u32::<BigEndian>()?;
let sample_flag = reader.get_u32();
sample_flags.push(sample_flag);
}
if TrunBox::FLAG_SAMPLE_CTS & flags > 0 {
let cts = reader.read_u32::<BigEndian>()?;
let cts = reader.get_u32();
sample_cts.push(cts);
}
}
skip_bytes_to(reader, start + size)?;
Ok(TrunBox {
version,
flags,
@ -169,12 +163,16 @@ impl<R: Read + Seek> ReadBox<&mut R> for TrunBox {
sample_cts,
})
}
fn size_hint() -> usize {
8
}
}
impl<W: Write> WriteBox<&mut W> for TrunBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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<W: Write> WriteBox<&mut W> for TrunBox {
writer.write_u32::<BigEndian>(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<W: Write> 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);
}
}

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for Tx3gBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
impl BlockReader for Tx3gBox {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
reader.get_u32(); // reserved
reader.get_u16(); // reserved
let data_reference_index = reader.get_u16();
reader.read_u32::<BigEndian>()?; // reserved
reader.read_u16::<BigEndian>()?; // reserved
let data_reference_index = reader.read_u16::<BigEndian>()?;
let display_flags = reader.read_u32::<BigEndian>()?;
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::<BigEndian>()?,
reader.read_i16::<BigEndian>()?,
reader.read_i16::<BigEndian>()?,
reader.read_i16::<BigEndian>()?,
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<R: Read + Seek> ReadBox<&mut R> for Tx3gBox {
style_record,
})
}
fn size_hint() -> usize {
34
}
}
impl<W: Write> WriteBox<&mut W> for Tx3gBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write(writer)?;
BoxHeader::new(Self::TYPE, size).write(writer)?;
writer.write_u32::<BigEndian>(0)?; // reserved
writer.write_u16::<BigEndian>(0)?; // reserved
@ -156,10 +154,9 @@ impl<W: Write> 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);
}
}

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for UdtaBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
impl BlockReader for UdtaBox {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
Ok(UdtaBox {
meta: reader.try_find_box()?,
})
}
let mut meta = None;
let mut current = reader.stream_position()?;
let end = start + size;
while current < end {
// Get box header.
let header = BoxHeader::read(reader)?;
let BoxHeader { name, size: s } = header;
if s > size {
return Err(Error::InvalidData(
"udta box contains a box with a larger size than it",
));
}
match name {
BoxType::MetaBox => {
meta = Some(MetaBox::read_box(reader, s)?);
}
_ => {
// XXX warn!()
skip_box(reader, s)?;
}
}
current = reader.stream_position()?;
}
skip_bytes_to(reader, start + size)?;
Ok(UdtaBox { meta })
fn size_hint() -> usize {
0
}
}
impl<W: Write> WriteBox<&mut W> for UdtaBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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<W: Write> 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);
}
}

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for VmhdBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
let (version, flags) = read_box_header_ext(reader)?;
let graphics_mode = reader.read_u16::<BigEndian>()?;
impl BlockReader for VmhdBox {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
let (version, flags) = read_box_header_ext(reader);
let graphics_mode = reader.get_u16();
let op_color = RgbColor {
red: reader.read_u16::<BigEndian>()?,
green: reader.read_u16::<BigEndian>()?,
blue: reader.read_u16::<BigEndian>()?,
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<R: Read + Seek> ReadBox<&mut R> for VmhdBox {
op_color,
})
}
fn size_hint() -> usize {
12
}
}
impl<W: Write> WriteBox<&mut W> for VmhdBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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<W: Write> 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);
}
}

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for Vp09Box {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
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<Self> {
let (version, flags) = read_box_header_ext(reader);
let start_code: u16 = reader.read_u16::<BigEndian>()?;
let data_reference_index: u16 = reader.read_u16::<BigEndian>()?;
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::<BigEndian>()?;
let height: u16 = reader.read_u16::<BigEndian>()?;
let horizresolution: (u16, u16) = (
reader.read_u16::<BigEndian>()?,
reader.read_u16::<BigEndian>()?,
);
let vertresolution: (u16, u16) = (
reader.read_u16::<BigEndian>()?,
reader.read_u16::<BigEndian>()?,
);
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::<BigEndian>()?;
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::<BigEndian>()?;
let end_code: u16 = reader.read_u16::<BigEndian>()?;
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<R: Read + Seek> ReadBox<&mut R> for Vp09Box {
compressorname,
depth,
end_code,
vpcc,
vpcc: reader.find_box::<VpccBox>()?,
})
}
fn size_hint() -> usize {
78
}
}
impl<W: Write> WriteBox<&mut W> for Vp09Box {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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<W: Write> 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);
}
}

View file

@ -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<R: Read + Seek> ReadBox<&mut R> for VpccBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
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<Self> {
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::<BigEndian>()?;
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<R: Read + Seek> ReadBox<&mut R> for VpccBox {
codec_initialization_data_size,
})
}
fn size_hint() -> usize {
11
}
}
impl<W: Write> WriteBox<&mut W> for VpccBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
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<W: Write> 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);
}
}

View file

@ -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<R> {
reader: R,
pub ftyp: FtypBox,
pub moov: MoovBox,
pub moofs: Vec<MoofBox>,
pub emsgs: Vec<EmsgBox>,
tracks: HashMap<u32, Mp4Track>,
size: u64,
}
impl<R: Read + Seek> Mp4Reader<R> {
pub fn read_header(mut reader: R, size: u64) -> Result<Self> {
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<FR: Read + Seek>(
&self,
mut reader: FR,
size: u64,
) -> Result<Mp4Reader<FR>> {
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<u32, Mp4Track> = 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<u32, Mp4Track> {
&self.tracks
}
pub fn sample_count(&self, track_id: u32) -> Result<u32> {
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<Option<Mp4Sample>> {
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<u64> {
if let Some(track) = self.tracks.get(&track_id) {
track.sample_offset(sample_id)
} else {
Err(Error::TrakNotFound(track_id))
}
}
}
impl<R> Mp4Reader<R> {
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,
})
})
}
}

File diff suppressed because it is too large Load diff

View file

@ -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<Self> {
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<TrackType> {
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<TrackType> {
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<TrackType> 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<MediaType> {
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<AvcProfile> {
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<u8> for AudioObjectType {
type Error = Error;
type Error = BoxError;
fn try_from(value: u8) -> Result<AudioObjectType> {
match value {
1 => Ok(AudioObjectType::AacMain),
@ -408,7 +411,7 @@ impl TryFrom<u8> 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<u8> for SampleFreqIndex {
type Error = Error;
type Error = BoxError;
fn try_from(value: u8) -> Result<SampleFreqIndex> {
match value {
0x0 => Ok(SampleFreqIndex::Freq96000),
@ -497,7 +500,7 @@ impl TryFrom<u8> 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<u8> for ChannelConfig {
type Error = Error;
type Error = BoxError;
fn try_from(value: u8) -> Result<ChannelConfig> {
match value {
0x1 => Ok(ChannelConfig::Mono),
@ -544,7 +547,7 @@ impl TryFrom<u8> 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<u32> for DataType {
type Error = Error;
type Error = BoxError;
fn try_from(value: u32) -> Result<DataType> {
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")),
}
}
}

View file

@ -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<FourCC>,
pub timescale: u32,
}
#[derive(Debug)]
pub struct Mp4Writer<W> {
writer: W,
tracks: Vec<Mp4TrackWriter>,
mdat_pos: u64,
timescale: u32,
duration: u64,
}
impl<W> Mp4Writer<W> {
/// 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::<u8>::new());
/// let mut writer = mp4::Mp4Writer::write_start(data, &config)?;
/// writer.write_end()?;
///
/// let data: Vec<u8> = writer.into_writer().into_inner();
/// # Ok(()) }
/// ```
pub fn into_writer(self) -> W {
self.writer
}
}
impl<W: Write + Seek> Mp4Writer<W> {
pub fn write_start(mut writer: W, config: &Mp4Config) -> Result<Self> {
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::<BigEndian>(1)?;
self.writer.seek(SeekFrom::Start(self.mdat_pos + 8))?;
self.writer.write_u64::<BigEndian>(mdat_size)?;
} else {
self.writer.seek(SeekFrom::Start(self.mdat_pos))?;
self.writer.write_u32::<BigEndian>(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(())
}
}

View file

@ -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;
use glob::glob;
use mp4::TrackType;
use tokio::fs::File;
use tokio::io::BufReader;
#[test]
fn test_read_mp4() {
let mut mp4 = get_reader("tests/samples/minimal.mp4");
#[tokio::test]
async fn test_read_mp4() {
let paths = glob("./assets/videos/*.mp4").expect("Failed to read glob pattern");
assert_eq!(2591, mp4.size());
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);
// ftyp.
assert_eq!(4, mp4.compatible_brands().len());
let mut mp4_file = mp4::Mp4File::new(&mut reader);
println!(
"streaming possible: {}",
mp4_file.read_header().await.unwrap()
);
// Check compatible_brands.
let brands = vec![
String::from("isom"),
String::from("iso2"),
String::from("avc1"),
String::from("mp41"),
];
let mut keys = mp4_file
.tracks
.iter()
.filter(|&(_, v)| v.track_type() == TrackType::Video)
.map(|(k, _)| *k);
for b in brands {
let t = mp4.compatible_brands().iter().any(|x| x.to_string() == b);
assert!(t);
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()
// );
}
}
}
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<BufReader<File>> {
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]),
}
);
let eos = mp4_fragment.read_sample(1, 2);
assert!(eos.is_err());
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.