1
0
Fork 0
mirror of https://github.com/alfg/mp4-rust.git synced 2024-05-09 03:52:53 +00:00

Compare commits

...

4 commits

Author SHA1 Message Date
Андрей Ткаченко deb6d8f0c3 Async Mp4Stream 2024-04-12 20:56:53 +04:00
Andrey Tkachenko a4bb30dae6 Merge branch 'fix-fragmeted-wrong-start-time-and-sync' into 'master'
Fix fragmented start_time and is_sync wrong calculation

See merge request oss/rust/mp4-rust!2
2023-11-20 16:06:52 +00:00
Andrey Tkachenko 4b49afba6b Fix fragmented start_time and is_sync wrong calculation 2023-11-20 20:03:59 +04:00
Andrey Tkachenko 1996727b3d Relax error on box size mismatch 2023-10-19 15:40:29 +04:00
60 changed files with 3589 additions and 2425 deletions

View file

@ -19,9 +19,17 @@ bytes = "1.1.0"
num-rational = { version = "0.4.0", features = ["serde"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1.37.0", features = ["io-util"] }
futures = "0.3.30"
const_format = "0.2.32"
pin-project-lite = "0.2.14"
async-stream = "0.3.5"
[dev-dependencies]
criterion = "0.3"
anyhow = "1.0"
criterion = "0.5.1"
tokio = { version = "1.37.0", features = ["full"] }
tokio-util = "0.7.10"
[[bench]]
name = "bench_main"

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

338
examples/mp4_to_mpeg2ts.rs Normal file
View file

@ -0,0 +1,338 @@
// use std::convert::TryInto;
// use std::env;
// use std::fs::File;
// use std::io::{self, BufReader};
// use std::path::Path;
// use anyhow::bail;
// use bytes::{BufMut, Bytes, BytesMut};
// use futures::SinkExt;
// use mp4::TrackType;
// use std::io::{Cursor, Write};
// use tokio_util::codec::Encoder;
// use bytes::Buf;
// use mpeg2ts::{
// es::{StreamId, StreamType},
// pes::PesHeader,
// time::{ClockReference, Timestamp},
// ts::{
// payload::{self, Pat, Pmt},
// AdaptationField, ContinuityCounter, EsInfo, Pid, ProgramAssociation,
// TransportScramblingControl, TsHeader, TsPacket, TsPacketWriter, TsPayload, VersionNumber,
// WriteTsPacket,
// },
// Error as TsError,
// };
// const PMT_PID: u16 = 4096;
// const VIDEO_ES_PID: u16 = 256;
// // const AUDIO_ES_PID: u16 = 258;
// const PES_VIDEO_STREAM_ID: u8 = 224;
// // const PES_AUDIO_STREAM_ID: u8 = 192;
// #[derive(Default)]
// pub struct TsEncoder {
// video_continuity_counter: ContinuityCounter,
// header_sent: bool,
// timestamp: i64,
// }
// impl TsEncoder {
// fn write_packet(
// &mut self,
// writer: &mut TsPacketWriter<impl Write>,
// pts: Timestamp,
// dts: Timestamp,
// data: &[u8],
// is_keyframe: bool,
// ) -> Result<(), TsError> {
// let mut header = Self::default_ts_header(VIDEO_ES_PID, self.video_continuity_counter)?;
// let mut buf = Cursor::new(data);
// let packet = {
// let data = payload::Bytes::new(&buf.chunk()[..buf.remaining().min(153)])?;
// buf.advance(data.len());
// TsPacket {
// header: header.clone(),
// adaptation_field: is_keyframe.then(|| AdaptationField {
// discontinuity_indicator: false,
// random_access_indicator: true,
// es_priority_indicator: false,
// pcr: Some(ClockReference::from(pts)),
// opcr: None,
// splice_countdown: None,
// transport_private_data: Vec::new(),
// extension: None,
// }),
// payload: Some(TsPayload::Pes(payload::Pes {
// header: PesHeader {
// stream_id: StreamId::new(PES_VIDEO_STREAM_ID),
// priority: false,
// data_alignment_indicator: false,
// copyright: false,
// original_or_copy: false,
// pts: Some(pts),
// dts: if pts == dts { None } else { Some(dts) },
// escr: None,
// },
// pes_packet_len: 0,
// data,
// })),
// }
// };
// writer.write_ts_packet(&packet)?;
// header.continuity_counter.increment();
// while buf.has_remaining() {
// let raw_payload =
// payload::Bytes::new(&buf.chunk()[..buf.remaining().min(payload::Bytes::MAX_SIZE)])?;
// buf.advance(raw_payload.len());
// let packet = TsPacket {
// header: header.clone(),
// adaptation_field: None,
// payload: Some(TsPayload::Raw(raw_payload)),
// };
// writer.write_ts_packet(&packet)?;
// header.continuity_counter.increment();
// }
// self.video_continuity_counter = header.continuity_counter;
// Ok(())
// }
// pub fn new(timestamp: i64) -> TsEncoder {
// Self {
// video_continuity_counter: Default::default(),
// header_sent: false,
// timestamp,
// }
// }
// }
// struct Frame {
// pub pts: i64,
// pub dts: i64,
// pub body: Bytes,
// pub key: bool,
// }
// impl<'a> Encoder<&'a Frame> for TsEncoder {
// type Error = anyhow::Error;
// fn encode(&mut self, frame: &'a Frame, dst: &mut BytesMut) -> Result<(), Self::Error> {
// let mut writer = TsPacketWriter::new(dst.writer());
// if !self.header_sent {
// self.header_sent = true;
// self.write_header(&mut writer, StreamType::H264)?;
// }
// let pts = frame.pts - self.timestamp;
// let dts = frame.dts - self.timestamp;
// let p_ts = Timestamp::new((pts as u64 * 9) / 100 + 1).map_err(TsError::from)?;
// let d_ts = Timestamp::new((dts as u64 * 9) / 100 + 1).map_err(TsError::from)?;
// self.write_packet(&mut writer, p_ts, d_ts, &frame.body, frame.key)?;
// Ok(())
// }
// }
// impl TsEncoder {
// #[inline]
// fn write_header<W: WriteTsPacket>(
// &mut self,
// writer: &mut W,
// stream_type: StreamType,
// ) -> Result<(), TsError> {
// self.write_packets(
// writer,
// [
// &Self::default_pat_packet(),
// &Self::default_pmt_packet(stream_type),
// ],
// )?;
// Ok(())
// }
// #[inline]
// fn write_packets<'a, W: WriteTsPacket, P: IntoIterator<Item = &'a TsPacket>>(
// &mut self,
// writer: &mut W,
// packets: P,
// ) -> Result<(), TsError> {
// packets
// .into_iter()
// .try_for_each(|pak| writer.write_ts_packet(pak))?;
// Ok(())
// }
// fn default_ts_header(
// pid: u16,
// continuity_counter: ContinuityCounter,
// ) -> Result<TsHeader, TsError> {
// Ok(TsHeader {
// transport_error_indicator: false,
// transport_priority: false,
// pid: Pid::new(pid)?,
// transport_scrambling_control: TransportScramblingControl::NotScrambled,
// continuity_counter,
// })
// }
// fn default_pat_packet() -> TsPacket {
// TsPacket {
// header: Self::default_ts_header(0, Default::default()).unwrap(),
// adaptation_field: None,
// payload: Some(TsPayload::Pat(Pat {
// transport_stream_id: 1,
// version_number: VersionNumber::default(),
// table: vec![ProgramAssociation {
// program_num: 1,
// program_map_pid: Pid::new(PMT_PID).unwrap(),
// }],
// })),
// }
// }
// fn default_pmt_packet(stream_type: StreamType) -> TsPacket {
// TsPacket {
// header: Self::default_ts_header(PMT_PID, Default::default()).unwrap(),
// adaptation_field: None,
// payload: Some(TsPayload::Pmt(Pmt {
// program_num: 1,
// pcr_pid: Some(Pid::new(VIDEO_ES_PID).unwrap()),
// version_number: VersionNumber::default(),
// program_info: vec![],
// es_info: vec![EsInfo {
// stream_type,
// elementary_pid: Pid::new(VIDEO_ES_PID).unwrap(),
// descriptors: vec![],
// }],
// })),
// }
// }
// }
// #[tokio::main(flavor = "current_thread")]
// async fn main() {
// let args: Vec<String> = env::args().collect();
// if args.len() < 2 {
// println!("Usage: mp4sample <filename>");
// std::process::exit(1);
// }
// if let Err(err) = samples(&args[1]).await {
// let _ = writeln!(io::stderr(), "{}", err);
// }
// }
// async fn samples<P: AsRef<Path>>(filename: &P) -> anyhow::Result<()> {
// let mut ts_name = filename.as_ref().parent().unwrap().to_path_buf();
// ts_name.push(format!(
// "{}.ts",
// filename.as_ref().file_stem().unwrap().to_str().unwrap()
// ));
// let f = File::open(filename)?;
// let size = f.metadata()?.len();
// let reader = BufReader::new(f);
// let ts_file = tokio::fs::File::create(ts_name).await.unwrap();
// let mut ts = tokio_util::codec::FramedWrite::new(ts_file, TsEncoder::new(-1_400_000));
// let mut mp4 = mp4::Mp4Reader::read_header(reader, size)?;
// if let Some(track_id) = mp4.tracks().iter().find_map(|(k, v)| {
// v.track_type()
// .ok()
// .and_then(|x| matches!(x, TrackType::Video).then_some(*k))
// }) {
// let sample_count = mp4.sample_count(track_id).unwrap();
// let mut params = BytesMut::new();
// let track = mp4.tracks().get(&track_id).unwrap();
// let timescale = track.timescale();
// if let Ok(sps) = track.sequence_parameter_set() {
// params.put_slice(&[0, 0, 0, 1]);
// params.put_slice(sps);
// }
// if let Ok(pps) = track.picture_parameter_set() {
// params.put_slice(&[0, 0, 0, 1]);
// params.put_slice(pps);
// }
// for sample_idx in 0..sample_count {
// let sample_id = sample_idx + 1;
// let sample = mp4.read_sample(track_id, sample_id);
// if let Some(samp) = sample.unwrap() {
// let dts = (samp.start_time as i64 * 1_000_000) / timescale as i64;
// let pts = (samp.start_time as i64 + samp.rendering_offset as i64) * 1_000_000
// / timescale as i64;
// let mut bytes = BytesMut::from(samp.bytes.as_ref());
// convert_h264(&mut bytes).unwrap();
// let mut body = BytesMut::with_capacity(bytes.len() + 6);
// if sample_idx == 0 {
// body.put_slice(&params);
// }
// body.put_slice(&[0, 0, 0, 1, 9, 240]);
// body.put_slice(&bytes);
// ts.send(&Frame {
// pts,
// dts,
// body: body.freeze(),
// key: samp.is_sync,
// })
// .await?;
// }
// }
// }
// Ok(())
// }
// fn convert_h264(data: &mut [u8]) -> anyhow::Result<()> {
// // TODO:
// // * For each IDR frame, copy the SPS and PPS from the stream's
// // parameters, rather than depend on it being present in the frame
// // already. In-band parameters aren't guaranteed. This is awkward
// // with h264_reader v0.5's h264_reader::avcc::AvcDecoderRecord because it
// // strips off the NAL header byte from each parameter. The next major
// // version shouldn't do this.
// // * Copy only the slice data. In particular, don't copy SEI, which confuses
// // Safari: <https://github.com/scottlamb/retina/issues/60#issuecomment-1178369955>
// let mut i = 0;
// while i < data.len() - 3 {
// // Replace each NAL's length with the Annex B start code b"\x00\x00\x00\x01".
// let bytes = &mut data[i..i + 4];
// let nalu_length = u32::from_be_bytes(bytes.try_into().unwrap()) as usize;
// bytes.copy_from_slice(&[0, 0, 0, 1]);
// i += 4 + nalu_length;
// if i > data.len() {
// bail!("partial nal body");
// }
// }
// if i < data.len() {
// bail!("partial nal body");
// }
// Ok(())
// }
fn main() {}

View file

@ -1,93 +1,95 @@
use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::io::{self, BufReader, BufWriter};
use std::path::Path;
// use std::env;
// use std::fs::File;
// use std::io::prelude::*;
// use std::io::{self, BufReader, BufWriter};
// use std::path::Path;
use mp4::{
AacConfig, AvcConfig, HevcConfig, MediaConfig, MediaType, Mp4Config, Result, TrackConfig,
TtxtConfig, Vp9Config,
};
// use mp4::{
// AacConfig, AvcConfig, HevcConfig, MediaConfig, MediaType, Mp4Config, Result, TrackConfig,
// TtxtConfig, Vp9Config,
// };
fn main() {
let args: Vec<String> = env::args().collect();
// 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 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);
}
}
// 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);
// 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 dst_file = File::create(dst_filename)?;
// let writer = BufWriter::new(dst_file);
let mut mp4_reader = mp4::Mp4Reader::read_header(reader, size)?;
let mut mp4_writer = mp4::Mp4Writer::write_start(
writer,
&Mp4Config {
major_brand: *mp4_reader.major_brand(),
minor_version: mp4_reader.minor_version(),
compatible_brands: mp4_reader.compatible_brands().to_vec(),
timescale: mp4_reader.timescale(),
},
)?;
// let mut mp4_reader = mp4::Mp4Reader::read_header(reader, size)?;
// let mut mp4_writer = mp4::Mp4Writer::write_start(
// writer,
// &Mp4Config {
// major_brand: *mp4_reader.major_brand(),
// minor_version: mp4_reader.minor_version(),
// compatible_brands: mp4_reader.compatible_brands().to_vec(),
// timescale: mp4_reader.timescale(),
// },
// )?;
// TODO interleaving
for track in mp4_reader.tracks().values() {
let media_conf = match track.media_type()? {
MediaType::H264 => MediaConfig::AvcConfig(AvcConfig {
width: track.width(),
height: track.height(),
seq_param_set: track.sequence_parameter_set()?.to_vec(),
pic_param_set: track.picture_parameter_set()?.to_vec(),
}),
MediaType::H265 => MediaConfig::HevcConfig(HevcConfig {
width: track.width(),
height: track.height(),
}),
MediaType::VP9 => MediaConfig::Vp9Config(Vp9Config {
width: track.width(),
height: track.height(),
}),
MediaType::AAC => MediaConfig::AacConfig(AacConfig {
bitrate: track.bitrate(),
profile: track.audio_profile()?,
freq_index: track.sample_freq_index()?,
chan_conf: track.channel_config()?,
}),
MediaType::TTXT => MediaConfig::TtxtConfig(TtxtConfig {}),
};
// // TODO interleaving
// for track in mp4_reader.tracks().values() {
// let media_conf = match track.media_type()? {
// MediaType::H264 => MediaConfig::AvcConfig(AvcConfig {
// width: track.width(),
// height: track.height(),
// seq_param_set: track.sequence_parameter_set()?.to_vec(),
// pic_param_set: track.picture_parameter_set()?.to_vec(),
// }),
// MediaType::H265 => MediaConfig::HevcConfig(HevcConfig {
// width: track.width(),
// height: track.height(),
// }),
// MediaType::VP9 => MediaConfig::Vp9Config(Vp9Config {
// width: track.width(),
// height: track.height(),
// }),
// MediaType::AAC => MediaConfig::AacConfig(AacConfig {
// bitrate: track.bitrate(),
// profile: track.audio_profile()?,
// freq_index: track.sample_freq_index()?,
// chan_conf: track.channel_config()?,
// }),
// MediaType::TTXT => MediaConfig::TtxtConfig(TtxtConfig {}),
// };
let track_conf = TrackConfig {
track_type: track.track_type()?,
timescale: track.timescale(),
language: track.language().to_string(),
media_conf,
};
// let track_conf = TrackConfig {
// track_type: track.track_type()?,
// timescale: track.timescale(),
// language: track.language().to_string(),
// media_conf,
// };
mp4_writer.add_track(&track_conf)?;
}
// mp4_writer.add_track(&track_conf)?;
// }
for track_id in mp4_reader.tracks().keys().copied().collect::<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);
}
}
// 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()?;
// mp4_writer.write_end()?;
Ok(())
}
// Ok(())
// }
fn main() {}

View file

@ -1,142 +1,144 @@
use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::io::{self, BufReader};
use std::path::Path;
// use std::env;
// use std::fs::File;
// use std::io::prelude::*;
// use std::io::{self, BufReader};
// use std::path::Path;
use mp4::{Mp4Box, Result};
// use mp4::{Mp4Box, Result};
fn main() {
let args: Vec<String> = env::args().collect();
// fn main() {
// let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("Usage: mp4dump <filename>");
std::process::exit(1);
}
// if args.len() < 2 {
// println!("Usage: mp4dump <filename>");
// std::process::exit(1);
// }
if let Err(err) = dump(&args[1]) {
let _ = writeln!(io::stderr(), "{}", err);
}
}
// if let Err(err) = dump(&args[1]) {
// let _ = writeln!(io::stderr(), "{}", err);
// }
// }
fn dump<P: AsRef<Path>>(filename: &P) -> Result<()> {
let f = File::open(filename)?;
let boxes = get_boxes(f)?;
// 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);
}
// // print out boxes
// for b in boxes.iter() {
// println!("[{}] size={} {}", b.name, b.size, b.summary);
// }
Ok(())
}
// Ok(())
// }
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Box {
name: String,
size: u64,
summary: String,
indent: u32,
}
// #[derive(Debug, Clone, PartialEq, Default)]
// pub struct Box {
// name: String,
// size: u64,
// summary: String,
// indent: u32,
// }
fn get_boxes(file: File) -> Result<Vec<Box>> {
let size = file.metadata()?.len();
let reader = BufReader::new(file);
let mp4 = mp4::Mp4Reader::read_header(reader, size)?;
// 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.header.ftyp),
build_box(&mp4.header.moov),
build_box(&mp4.header.moov.mvhd),
];
// // collect known boxes
// let mut boxes = vec![
// build_box(&mp4.header.ftyp),
// build_box(&mp4.header.moov),
// build_box(&mp4.header.moov.mvhd),
// ];
if let Some(ref mvex) = &mp4.header.moov.mvex {
boxes.push(build_box(mvex));
if let Some(mehd) = &mvex.mehd {
boxes.push(build_box(mehd));
}
boxes.push(build_box(&mvex.trex));
}
// if let Some(ref mvex) = &mp4.header.moov.mvex {
// boxes.push(build_box(mvex));
// if let Some(mehd) = &mvex.mehd {
// boxes.push(build_box(mehd));
// }
// boxes.push(build_box(&mvex.trex));
// }
// trak.
for track in mp4.tracks().values() {
boxes.push(build_box(&track.trak));
boxes.push(build_box(&track.trak.tkhd));
if let Some(ref edts) = track.trak.edts {
boxes.push(build_box(edts));
if let Some(ref elst) = edts.elst {
boxes.push(build_box(elst));
}
}
// // trak.
// for track in mp4.tracks().values() {
// boxes.push(build_box(&track.trak));
// boxes.push(build_box(&track.trak.tkhd));
// if let Some(ref edts) = track.trak.edts {
// boxes.push(build_box(edts));
// if let Some(ref elst) = edts.elst {
// boxes.push(build_box(elst));
// }
// }
// trak.mdia
let mdia = &track.trak.mdia;
boxes.push(build_box(mdia));
boxes.push(build_box(&mdia.mdhd));
boxes.push(build_box(&mdia.hdlr));
boxes.push(build_box(&track.trak.mdia.minf));
// // trak.mdia
// let mdia = &track.trak.mdia;
// boxes.push(build_box(mdia));
// boxes.push(build_box(&mdia.mdhd));
// boxes.push(build_box(&mdia.hdlr));
// boxes.push(build_box(&track.trak.mdia.minf));
// trak.mdia.minf
let minf = &track.trak.mdia.minf;
if let Some(ref vmhd) = &minf.vmhd {
boxes.push(build_box(vmhd));
}
if let Some(ref smhd) = &minf.smhd {
boxes.push(build_box(smhd));
}
// // trak.mdia.minf
// let minf = &track.trak.mdia.minf;
// if let Some(ref vmhd) = &minf.vmhd {
// boxes.push(build_box(vmhd));
// }
// if let Some(ref smhd) = &minf.smhd {
// boxes.push(build_box(smhd));
// }
// trak.mdia.minf.stbl
let stbl = &track.trak.mdia.minf.stbl;
boxes.push(build_box(stbl));
boxes.push(build_box(&stbl.stsd));
if let Some(ref avc1) = &stbl.stsd.avc1 {
boxes.push(build_box(avc1));
}
if let Some(ref hev1) = &stbl.stsd.hev1 {
boxes.push(build_box(hev1));
}
if let Some(ref mp4a) = &stbl.stsd.mp4a {
boxes.push(build_box(mp4a));
}
boxes.push(build_box(&stbl.stts));
if let Some(ref ctts) = &stbl.ctts {
boxes.push(build_box(ctts));
}
if let Some(ref stss) = &stbl.stss {
boxes.push(build_box(stss));
}
boxes.push(build_box(&stbl.stsc));
boxes.push(build_box(&stbl.stsz));
if let Some(ref stco) = &stbl.stco {
boxes.push(build_box(stco));
}
if let Some(ref co64) = &stbl.co64 {
boxes.push(build_box(co64));
}
}
// // trak.mdia.minf.stbl
// let stbl = &track.trak.mdia.minf.stbl;
// boxes.push(build_box(stbl));
// boxes.push(build_box(&stbl.stsd));
// if let Some(ref avc1) = &stbl.stsd.avc1 {
// boxes.push(build_box(avc1));
// }
// if let Some(ref hev1) = &stbl.stsd.hev1 {
// boxes.push(build_box(hev1));
// }
// if let Some(ref mp4a) = &stbl.stsd.mp4a {
// boxes.push(build_box(mp4a));
// }
// boxes.push(build_box(&stbl.stts));
// if let Some(ref ctts) = &stbl.ctts {
// boxes.push(build_box(ctts));
// }
// if let Some(ref stss) = &stbl.stss {
// boxes.push(build_box(stss));
// }
// boxes.push(build_box(&stbl.stsc));
// boxes.push(build_box(&stbl.stsz));
// if let Some(ref stco) = &stbl.stco {
// boxes.push(build_box(stco));
// }
// if let Some(ref co64) = &stbl.co64 {
// boxes.push(build_box(co64));
// }
// }
// If fragmented, add moof boxes.
for moof in mp4.header.moofs.iter() {
boxes.push(build_box(moof));
boxes.push(build_box(&moof.mfhd));
for traf in moof.trafs.iter() {
boxes.push(build_box(traf));
boxes.push(build_box(&traf.tfhd));
if let Some(ref trun) = &traf.trun {
boxes.push(build_box(trun));
}
}
}
// // If fragmented, add moof boxes.
// for moof in mp4.header.moofs.iter() {
// boxes.push(build_box(moof));
// boxes.push(build_box(&moof.mfhd));
// for traf in moof.trafs.iter() {
// boxes.push(build_box(traf));
// boxes.push(build_box(&traf.tfhd));
// if let Some(ref trun) = &traf.trun {
// boxes.push(build_box(trun));
// }
// }
// }
Ok(boxes)
}
// Ok(boxes)
// }
fn build_box<M: Mp4Box + std::fmt::Debug>(m: &M) -> Box {
Box {
name: m.box_type().to_string(),
size: m.box_size(),
summary: m.summary().unwrap(),
indent: 0,
}
}
// 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,
// }
// }
fn main() {}

View file

@ -1,143 +1,144 @@
use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::io::{self, BufReader};
use std::path::Path;
// use std::env;
// use std::fs::File;
// use std::io::prelude::*;
// use std::io::{self, BufReader};
// use std::path::Path;
use mp4::{Error, Mp4Track, Result, TrackType};
// use mp4::{Error, Mp4Track, Result, TrackType};
fn main() {
let args: Vec<String> = env::args().collect();
// fn main() {
// let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("Usage: mp4info <filename>");
std::process::exit(1);
}
// if args.len() < 2 {
// println!("Usage: mp4info <filename>");
// std::process::exit(1);
// }
if let Err(err) = info(&args[1]) {
let _ = writeln!(io::stderr(), "{}", err);
}
}
// if let Err(err) = info(&args[1]) {
// let _ = writeln!(io::stderr(), "{}", err);
// }
// }
fn info<P: AsRef<Path>>(filename: &P) -> Result<()> {
let f = File::open(filename)?;
let size = f.metadata()?.len();
let reader = BufReader::new(f);
// 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)?;
// let mp4 = mp4::Mp4Reader::read_header(reader, size)?;
println!("File:");
println!(" file size: {}", mp4.size());
println!(" major_brand: {}", mp4.major_brand());
let mut compatible_brands = String::new();
for brand in mp4.compatible_brands().iter() {
compatible_brands.push_str(&brand.to_string());
compatible_brands.push(' ');
}
println!(" compatible_brands: {}\n", compatible_brands);
// println!("File:");
// println!(" file size: {}", mp4.size());
// println!(" major_brand: {}", mp4.major_brand());
// let mut compatible_brands = String::new();
// for brand in mp4.compatible_brands().iter() {
// compatible_brands.push_str(&brand.to_string());
// compatible_brands.push(' ');
// }
// println!(" compatible_brands: {}\n", compatible_brands);
println!("Movie:");
println!(" version: {}", mp4.header.moov.mvhd.version);
println!(
" creation time: {}",
creation_time(mp4.header.moov.mvhd.creation_time)
);
println!(" duration: {:?}", mp4.duration());
println!(" fragments: {:?}", mp4.is_fragmented());
println!(" timescale: {:?}\n", mp4.timescale());
// println!("Movie:");
// println!(" version: {}", mp4.header.moov.mvhd.version);
// println!(
// " creation time: {}",
// creation_time(mp4.header.moov.mvhd.creation_time)
// );
// println!(" duration: {:?}", mp4.duration());
// println!(" fragments: {:?}", mp4.is_fragmented());
// println!(" timescale: {:?}\n", mp4.timescale());
println!("Found {} Tracks", mp4.tracks().len());
for track in mp4.tracks().values() {
let media_info = match track.track_type()? {
TrackType::Video => video_info(track),
TrackType::Audio => audio_info(track),
TrackType::Subtitle => subtitle_info(track),
};
// println!("Found {} Tracks", mp4.tracks().len());
// for track in mp4.tracks().values() {
// let media_info = match track.track_type()? {
// TrackType::Video => video_info(track),
// TrackType::Audio => audio_info(track),
// TrackType::Subtitle => subtitle_info(track),
// };
println!(
" Track: #{}({}) {}: {}",
track.track_id(),
track.language(),
track.track_type()?,
media_info.unwrap_or_else(|e| e.to_string())
);
}
Ok(())
}
// println!(
// " Track: #{}({}) {}: {}",
// track.track_id(),
// track.language(),
// track.track_type()?,
// media_info.unwrap_or_else(|e| e.to_string())
// );
// }
// Ok(())
// }
fn video_info(track: &Mp4Track) -> Result<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 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(),
};
// 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(),
};
// let channel_config = match track.channel_config() {
// Ok(val) => val.to_string(),
// _ => "-".to_string(),
// };
Ok(format!(
"{} ({}) ({:?}), {} Hz, {}, {} kb/s",
track.media_type()?,
profile,
track.box_type()?,
track.sample_freq_index()?.freq(),
channel_config,
track.bitrate() / 1000
))
} else {
Ok(format!(
"{} ({:?}), {} kb/s",
track.media_type()?,
track.box_type()?,
track.bitrate() / 1000
))
}
} else {
Err(Error::InvalidData("mp4a box not found"))
}
}
// Ok(format!(
// "{} ({}) ({:?}), {} Hz, {}, {} kb/s",
// track.media_type()?,
// profile,
// track.box_type()?,
// track.sample_freq_index()?.freq(),
// channel_config,
// track.bitrate() / 1000
// ))
// } else {
// Ok(format!(
// "{} ({:?}), {} kb/s",
// track.media_type()?,
// track.box_type()?,
// track.bitrate() / 1000
// ))
// }
// } else {
// Err(Error::InvalidData("mp4a box not found"))
// }
// }
fn subtitle_info(track: &Mp4Track) -> Result<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 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
}
}
// fn creation_time(creation_time: u64) -> u64 {
// // convert from MP4 epoch (1904-01-01) to Unix epoch (1970-01-01)
// if creation_time >= 2082844800 {
// creation_time - 2082844800
// } else {
// creation_time
// }
// }
fn main() {}

View file

@ -1,12 +1,14 @@
use std::env;
use std::fs::File;
use std::fmt::Pointer;
use std::io::prelude::*;
use std::io::{self, BufReader};
use std::path::Path;
use std::{env, io};
use mp4::Result;
use tokio::fs::File;
use tokio::io::BufReader;
fn main() {
#[tokio::main]
async fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
@ -14,37 +16,41 @@ fn main() {
std::process::exit(1);
}
if let Err(err) = samples(&args[1]) {
if let Err(err) = samples(&args[1]).await {
let _ = writeln!(io::stderr(), "{}", err);
}
}
fn samples<P: AsRef<Path>>(filename: &P) -> Result<()> {
let f = File::open(filename)?;
let size = f.metadata()?.len();
async fn samples<P: AsRef<Path>>(filename: &P) -> Result<()> {
let f = File::open(filename).await?;
let reader = BufReader::new(f);
let mut mp4 = mp4::Mp4Reader::read_header(reader, size)?;
let mp4 = mp4::AsyncMp4Reader::read_header(reader).await?;
for track_id in mp4.tracks().keys().copied().collect::<Vec<u32>>() {
let sample_count = mp4.sample_count(track_id).unwrap();
let track = mp4.tracks().get(&track_id).unwrap();
for sample_idx in 0..sample_count {
let sample_id = sample_idx + 1;
let sample = mp4.read_sample(track_id, sample_id);
println!("{:#?}", track.trak.mdia.minf.stbl.stco);
if let Some(ref samp) = sample.unwrap() {
println!(
"[{}] start_time={} duration={} rendering_offset={} size={} is_sync={}",
sample_id,
samp.start_time,
samp.duration,
samp.rendering_offset,
samp.bytes.len(),
samp.is_sync,
);
}
}
// let sample_count = mp4.sample_count(track_id).unwrap();
// println!("sample count {}", sample_count);
// for sample_idx in 0..sample_count {
// let sample_id = sample_idx + 1;
// let sample = mp4.read_sample(track_id, sample_id);
// if let Some(ref samp) = sample.unwrap() {
// println!(
// "[{}] start_time={} duration={} rendering_offset={} size={} is_sync={}",
// sample_id,
// samp.start_time,
// samp.duration,
// samp.rendering_offset,
// samp.bytes.len(),
// samp.is_sync,
// );
// }
// }
}
Ok(())
}

View file

@ -1,7 +1,9 @@
use mp4::Mp4Header;
use std::env;
use std::fs::File;
use tokio::fs::File;
fn main() {
#[tokio::main]
async fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
@ -10,10 +12,11 @@ fn main() {
}
let filename = &args[1];
let f = File::open(filename).unwrap();
let mp4 = mp4::read_mp4(f).unwrap();
let mut f = File::open(filename).await.unwrap();
println!("Major Brand: {}", mp4.major_brand());
let mp4 = Mp4Header::read(&mut f, Some(())).await.unwrap();
println!("Major Brand: {:?}", mp4.major_brand());
for track in mp4.tracks().values() {
println!(

308
src/async_reader.rs Normal file
View file

@ -0,0 +1,308 @@
use std::{
collections::HashMap,
io::{Read, Seek},
time::Duration,
};
use tokio::io::{AsyncRead, AsyncReadExt};
use crate::{
BlockReader, BoxHeader, BoxType, EmsgBox, Error, FourCC, FtypBox, MetaBox, Metadata, MoofBox,
MoovBox, Mp4Sample, Mp4Track,
};
#[derive(Debug, Clone)]
pub struct Mp4Header {
pub ftyp: Option<FtypBox>,
pub moov: Option<MoovBox>,
pub moofs: Vec<MoofBox>,
pub emsgs: Vec<EmsgBox>,
tracks: HashMap<u32, Mp4Track>,
}
// async fn read
impl Mp4Header {
pub async fn read<R, C>(reader: &mut R, _cache: Option<C>) -> Result<Self, Error>
where
R: AsyncRead + Unpin,
// C: AsyncRead + AsyncWrite + Unpin,
{
let mut ftyp = None;
let mut moov = None;
let mut moofs = Vec::new();
// let mut moof_offsets = Vec::new();
let mut emsgs = Vec::new();
let mut buff = Vec::with_capacity(1024);
while let Some(BoxHeader { kind, size: s }) = BoxHeader::read(reader).await? {
if buff.len() < s as usize {
buff.resize(s as usize, 0);
}
// Match and parse the atom boxes.
match kind {
BoxType::FtypBox => {
reader.read_exact(&mut buff[0..s as usize]).await?;
ftyp = Some(FtypBox::read_block(&mut &buff[0..s as usize])?);
println!("{:?}", ftyp);
}
BoxType::MoovBox => {
reader.read_exact(&mut buff[0..s as usize]).await?;
println!("moov");
moov = Some(MoovBox::read_block(&mut &buff[0..s as usize])?);
}
// BoxType::MoofBox => {
// let moof_offset = reader.stream_position()? - 8;
// let moof = MoofBox::read_box(reader, s)?;
// moofs.push(moof);
// moof_offsets.push(moof_offset);
// }
// BoxType::EmsgBox => {
// let emsg = EmsgBox::read_box(reader, s)?;
// emsgs.push(emsg);
// }
// BoxType::MdatBox => {
// skip_box(reader, s)?;
// }
// BoxType::FreeBox => {
// reader.read_exact(buf)
// skip_box(reader, s)?;
// }
bt => {
println!("skip {:?}", bt);
let mut buff = [0u8; 1024];
let mut read = 0;
for chunk in (0..s).step_by(1024) {
if chunk == 0 {
continue;
}
reader.read_exact(&mut buff).await?;
read += buff.len();
}
if s as usize - read > 0 {
reader.read_exact(&mut buff[0..s as usize - read]).await?;
}
}
}
}
if ftyp.is_none() {
return Err(Error::BoxNotFound(BoxType::FtypBox));
}
if moov.is_none() {
return Err(Error::BoxNotFound(BoxType::MoovBox));
}
let mut tracks = if let Some(ref moov) = moov {
if moov.traks.iter().any(|trak| trak.tkhd.track_id == 0) {
return Err(Error::InvalidData("illegal track id 0"));
}
moov.traks
.iter()
.map(|trak| (trak.tkhd.track_id, Mp4Track::from(trak)))
.collect()
} else {
HashMap::new()
};
// Update tracks if any fragmented (moof) boxes are found.
// if !moofs.is_empty() {
// let mut default_sample_duration = 0;
// if let Some(ref moov) = moov {
// if let Some(ref mvex) = &moov.mvex {
// default_sample_duration = mvex.trex.default_sample_duration
// }
// }
// for (moof, moof_offset) in moofs.iter().zip(moof_offsets) {
// for traf in moof.trafs.iter() {
// let track_id = traf.tfhd.track_id;
// if let Some(track) = tracks.get_mut(&track_id) {
// track.default_sample_duration = default_sample_duration;
// track.moof_offsets.push(moof_offset);
// track.trafs.push(traf.clone())
// } else {
// return Err(Error::TrakNotFound(track_id));
// }
// }
// }
// }
Ok(Mp4Header {
ftyp,
moov,
moofs,
emsgs,
tracks,
})
}
#[inline]
pub fn major_brand(&self) -> Option<&FourCC> {
Some(&self.ftyp.as_ref()?.major_brand)
}
pub fn minor_version(&self) -> Option<u32> {
Some(self.ftyp.as_ref()?.minor_version)
}
pub fn compatible_brands(&self) -> Option<&[FourCC]> {
Some(&self.ftyp.as_ref()?.compatible_brands)
}
pub fn duration(&self) -> Option<Duration> {
self.moov.as_ref().map(|moov| {
Duration::from_millis(moov.mvhd.duration * 1000 / moov.mvhd.timescale as u64)
})
}
pub fn timescale(&self) -> Option<u32> {
Some(self.moov.as_ref()?.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, Error> {
if let Some(track) = self.tracks.get(&track_id) {
Ok(track.sample_count())
} else {
Err(Error::TrakNotFound(track_id))
}
}
pub fn read_sample<R: Read + Seek>(
&mut self,
reader: &mut R,
track_id: u32,
sample_id: u32,
) -> Result<Option<Mp4Sample>, Error> {
if let Some(track) = self.tracks.get(&track_id) {
track.read_sample(reader, sample_id)
} else {
Err(Error::TrakNotFound(track_id))
}
}
pub fn sample_offset(&mut self, track_id: u32, sample_id: u32) -> Result<u64, Error> {
if let Some(track) = self.tracks.get(&track_id) {
track.sample_offset(sample_id)
} else {
Err(Error::TrakNotFound(track_id))
}
}
pub fn metadata(&self) -> Option<impl Metadata<'_>> {
self.moov.as_ref()?.udta.as_ref().and_then(|udta| {
udta.meta.as_ref().and_then(|meta| match meta {
MetaBox::Mdir { ilst } => ilst.as_ref(),
_ => None,
})
})
}
}
#[derive(Debug)]
pub struct AsyncMp4Reader<R> {
pub header: Mp4Header,
reader: R,
}
impl<R: AsyncRead + Unpin> AsyncMp4Reader<R> {
pub fn from_reader(reader: R, header: Mp4Header) -> Self {
Self { reader, header }
}
pub async fn read_header(mut reader: R) -> Result<Self, Error> {
Ok(AsyncMp4Reader {
header: Mp4Header::read(&mut reader, Some(())).await?,
reader,
})
}
// pub fn read_fragment_header<FR: Read + Seek>(
// &self,
// mut reader: FR,
// size: u64,
// ) -> Result<Mp4Reader<FR>> {
// Ok(Mp4Reader {
// header: self.header.read_fragment(&mut reader, size)?,
// reader,
// })
// }
// pub fn size(&self) -> u64 {
// self.header.size()
// }
pub fn major_brand(&self) -> Option<&FourCC> {
self.header.major_brand()
}
pub fn minor_version(&self) -> Option<u32> {
self.header.minor_version()
}
pub fn compatible_brands(&self) -> Option<&[FourCC]> {
self.header.compatible_brands()
}
pub fn duration(&self) -> Option<Duration> {
self.header.duration()
}
pub fn timescale(&self) -> Option<u32> {
self.header.timescale()
}
pub fn is_fragmented(&self) -> bool {
self.header.is_fragmented()
}
pub fn tracks(&self) -> &HashMap<u32, Mp4Track> {
self.header.tracks()
}
pub fn sample_count(&self, track_id: u32) -> Result<u32, Error> {
self.header.sample_count(track_id)
}
pub fn read_sample(
&mut self,
track_id: u32,
sample_id: u32,
) -> Result<Option<Mp4Sample>, Error> {
self.header
.read_sample(&mut self.reader, track_id, sample_id)
}
pub fn sample_offset(&mut self, track_id: u32, sample_id: u32) -> Result<u64, Error> {
self.header.sample_offset(track_id, sample_id)
}
}
pub struct Mp4Track {}
impl<R> AsyncMp4Reader<R> {
pub fn metadata(&self) -> impl Metadata<'_> {
self.header.metadata()
}
}

348
src/file.rs Normal file
View file

@ -0,0 +1,348 @@
use futures::{Future, Stream};
use std::collections::HashMap;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::{AsyncRead, AsyncReadExt, ReadBuf};
use crate::{
BlockReader, BoxHeader, BoxType, EmsgBox, Error, FtypBox, MoofBox, MoovBox, Mp4Sample,
};
pub struct MemoryBufferProvider {
buffers: Vec<Vec<u8>>,
}
impl BufferProvider for MemoryBufferProvider {
type Error = ();
type Buffer = MemoryBuffer;
fn create_buffer(&mut self, size: usize) -> Result<Self::Buffer, Self::Error> {
todo!()
}
}
pub struct MemoryBuffer {
inner: Vec<u8>,
}
impl Buffer for MemoryBuffer {
type Error = std::io::Error;
async fn write_bytes(
&mut self,
reader: &mut (impl AsyncRead + Unpin),
bytes: usize,
) -> Result<(), Self::Error> {
self.inner.resize(bytes, 0);
reader.read_exact(&mut self.inner).await?;
Ok(())
}
async fn read_bytes(&self, offset: usize, len: usize) -> Result<&[u8], Self::Error> {
Ok(self.inner.get(offset..offset + len).unwrap())
}
}
pub trait Buffer {
type Error;
fn write_bytes(
&mut self,
reader: &mut (impl AsyncRead + Unpin),
bytes: usize,
) -> impl Future<Output = Result<(), Self::Error>>;
fn read_bytes(
&self,
offset: usize,
len: usize,
) -> impl Future<Output = Result<&[u8], Self::Error>>;
}
pub trait BufferProvider {
type Error;
type Buffer: Buffer;
fn create_buffer(&mut self, size: usize) -> Result<Self::Buffer, Self::Error>;
}
struct DataBlock<B: AsRef<[u8]>> {
index: usize,
offset: u64,
size: u64,
buffer: B,
}
pub struct Mp4File<'a, R: AsyncRead + Unpin> {
// provider: P,
// data: Vec<DataBlock<P::Buffer>>,
ftyp: Option<FtypBox>,
emsgs: Vec<EmsgBox>,
mdat_offset: u64,
mdat_size: u64,
tracks: HashMap<u32, Mp4Track>,
reader: OffsetWrapper<&'a mut R>,
}
pin_project_lite::pin_project! {
pub struct OffsetWrapper<R: AsyncRead> {
#[pin]
inner: R,
pub offset: u64,
}}
impl<R: AsyncRead + Unpin> AsyncRead for OffsetWrapper<R> {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<std::io::Result<()>> {
let mut this = self.project();
match this.inner.poll_read(cx, buf) {
Poll::Ready(re) => {
*this.offset += buf.filled().len() as u64;
Poll::Ready(re)
}
Poll::Pending => Poll::Pending,
}
}
}
impl<'a, R: AsyncRead + Unpin + 'a> Mp4File<'a, R> {
pub fn new(reader: &'a mut R) -> Self {
Self {
ftyp: None,
emsgs: Vec::new(),
mdat_offset: 0,
mdat_size: 0,
tracks: HashMap::new(),
reader: OffsetWrapper {
inner: reader,
offset: 0,
},
}
}
pub async fn read_header(&mut self) -> Result<bool, Error> {
let mut buff = Vec::with_capacity(8192);
let mut got_moov = false;
while let Some(BoxHeader { kind, size: s }) = BoxHeader::read(&mut self.reader).await? {
match kind {
BoxType::FtypBox => {
if buff.len() < s as usize {
buff.resize(s as usize, 0);
}
self.reader.read_exact(&mut buff[0..s as usize]).await?;
self.ftyp = Some(FtypBox::read_block(&mut &buff[0..s as usize])?);
}
BoxType::MoovBox => {
if buff.len() < s as usize {
buff.resize(s as usize, 0);
}
self.reader.read_exact(&mut buff[0..s as usize]).await?;
got_moov = true;
self.set_moov(MoovBox::read_block(&mut &buff[0..s as usize])?)?;
}
BoxType::MoofBox => {
if buff.len() < s as usize {
buff.resize(s as usize, 0);
}
let offset = self.reader.offset;
self.reader.read_exact(&mut buff[0..s as usize]).await?;
self.add_moof(offset, MoofBox::read_block(&mut &buff[0..s as usize])?)?;
}
BoxType::EmsgBox => {
if buff.len() < s as usize {
buff.resize(s as usize, 0);
}
self.reader.read_exact(&mut buff[0..s as usize]).await?;
self.emsgs
.push(EmsgBox::read_block(&mut &buff[0..s as usize])?);
}
BoxType::MdatBox => {
self.mdat_offset = self.reader.offset;
self.mdat_size = s;
break;
}
bt => {
self.skip_box(bt, s).await?;
}
}
}
Ok(got_moov)
}
async fn skip_box(&mut self, bt: BoxType, s: u64) -> Result<(), Error> {
println!("skip {:?}", bt);
let mut buff = [0u8; 1024];
let mut read = 0;
for chunk in (0..s).step_by(1024) {
if chunk == 0 {
continue;
}
self.reader.read_exact(&mut buff).await?;
read += buff.len();
}
if s as usize - read > 0 {
self.reader
.read_exact(&mut buff[0..s as usize - read])
.await?;
}
Ok(())
}
fn set_moov(&mut self, moov: MoovBox) -> Result<(), Error> {
for trak in moov.traks {
self.tracks.insert(trak.tkhd.track_id, Mp4Track::new(trak)?);
}
Ok(())
}
fn add_moof(&mut self, offset: u64, moof: MoofBox) -> Result<(), Error> {
for traf in moof.trafs {
let track_id = traf.tfhd.track_id;
if let Some(track) = self.tracks.get_mut(&track_id) {
track.add_traf(traf)
} else {
return Err(Error::TrakNotFound(track_id));
}
}
Ok(())
}
// pub fn into_stream(mut self) -> impl Stream<Item = Result<Mp4Sample, Error>> + 'a {
// async_stream::try_stream! {
// let mut buff = Vec::with_capacity(8192);
// while let Some(BoxHeader { kind, size: s }) = BoxHeader::read(&mut self.reader).await? {
// match kind {
// BoxType::FtypBox => {
// if buff.len() < s as usize {
// buff.resize(s as usize, 0);
// }
// self.reader.read_exact(&mut buff[0..s as usize]).await?;
// self.ftyp = Some(FtypBox::read_block(&mut &buff[0..s as usize])?);
// }
// BoxType::MoovBox => {
// if buff.len() < s as usize {
// buff.resize(s as usize, 0);
// }
// self.reader.read_exact(&mut buff[0..s as usize]).await?;
// self.set_moov(MoovBox::read_block(&mut &buff[0..s as usize])?)?;
// }
// BoxType::MoofBox => {
// if buff.len() < s as usize {
// buff.resize(s as usize, 0);
// }
// let offset = self.reader.offset;
// self.reader.read_exact(&mut buff[0..s as usize]).await?;
// self.add_moof(offset, MoofBox::read_block(&mut &buff[0..s as usize])?)?;
// }
// BoxType::EmsgBox => {
// if buff.len() < s as usize {
// buff.resize(s as usize, 0);
// }
// self.reader.read_exact(&mut buff[0..s as usize]).await?;
// self.emsgs.push(EmsgBox::read_block(&mut &buff[0..s as usize])?);
// }
// BoxType::MdatBox => {
// break;
// }
// bt => {
// self.skip_box(bt, s).await?;
// }
// }
// }
// }
// }
}
struct Mp4SampleOffset {
offset: u64,
size: u32,
duration: u32,
start_time: u64,
rendering_offset: i32,
is_sync: bool,
}
pub struct Mp4Track {
track_id: u32,
duration: u64,
default_sample_duration: u32,
samples: Vec<Mp4SampleOffset>,
tkhd: crate::TkhdBox,
}
impl Mp4Track {
fn new(trak: crate::TrakBox) -> Result<Mp4Track, Error> {
let mut samples = Vec::with_capacity(trak.mdia.minf.stbl.stsz.sample_count as _);
for sample_id in 0..trak.mdia.minf.stbl.stsz.sample_count {
let offset = match trak.sample_offset(sample_id) {
Ok(offset) => offset,
Err(Error::EntryInStblNotFound(_, _, _)) => continue,
Err(err) => return Err(err),
};
let size = match trak.sample_size(sample_id) {
Ok(size) => size,
Err(Error::EntryInStblNotFound(_, _, _)) => continue,
Err(err) => return Err(err),
};
let (start_time, duration) = trak.sample_time(sample_id)?;
samples.push(Mp4SampleOffset {
offset,
duration,
size,
start_time,
rendering_offset: trak.sample_rendering_offset(sample_id),
is_sync: trak.sample_is_sync(sample_id),
});
}
Ok(Self {
tkhd: trak.tkhd,
track_id: trak.tkhd.track_id,
default_sample_duration: 0,
samples,
duration: 0,
})
}
pub(crate) fn add_traf(&self, traf: crate::TrafBox) {
todo!()
}
}

139
src/header.rs Normal file
View file

@ -0,0 +1,139 @@
use std::collections::HashMap;
use tokio::io::{AsyncRead, AsyncReadExt};
use crate::{BlockReader, BoxHeader, BoxType, EmsgBox, Error, FtypBox, MoofBox, MoovBox, Mp4Track};
#[derive(Debug, Clone)]
pub struct Mp4Header {
pub ftyp: Option<FtypBox>,
pub moov: Option<MoovBox>,
pub moofs: Vec<MoofBox>,
pub emsgs: Vec<EmsgBox>,
pub data: Vec<(u64, u64)>,
}
impl Mp4Header {
pub async fn read_until_mdat<R, C>(reader: &mut R) -> Result<Self, Error>
where
R: AsyncRead + Unpin,
{
let mut offset = 0;
let mut ftyp = None;
let mut moov = None;
let mut moofs = Vec::new();
// let mut moof_offsets = Vec::new();
let mut emsgs = Vec::new();
let mut buff = Vec::with_capacity(8192);
while let Some(BoxHeader { kind, size: s }) = BoxHeader::read(reader).await? {
if buff.len() < s as usize {
buff.resize(s as usize, 0);
}
// Match and parse the atom boxes.
match kind {
BoxType::FtypBox => {
reader.read_exact(&mut buff[0..s as usize]).await?;
ftyp = Some(FtypBox::read_block(&mut &buff[0..s as usize])?);
}
BoxType::MoovBox => {
reader.read_exact(&mut buff[0..s as usize]).await?;
moov = Some(MoovBox::read_block(&mut &buff[0..s as usize])?);
}
BoxType::MoofBox => {
let moof_offset = reader.stream_position()? - 8;
let moof = MoofBox::read_box(reader, s)?;
moofs.push(moof);
moof_offsets.push(moof_offset);
}
BoxType::EmsgBox => {
let emsg = EmsgBox::read_box(reader, s)?;
emsgs.push(emsg);
}
BoxType::MdatBox => {}
// BoxType::FreeBox => {
// reader.read_exact(buf)
// skip_box(reader, s)?;
// }
bt => {
println!("skip {:?}", bt);
let mut buff = [0u8; 1024];
let mut read = 0;
for chunk in (0..s).step_by(1024) {
if chunk == 0 {
continue;
}
reader.read_exact(&mut buff).await?;
read += buff.len();
}
if s as usize - read > 0 {
reader.read_exact(&mut buff[0..s as usize - read]).await?;
}
}
}
}
if ftyp.is_none() {
return Err(Error::BoxNotFound(BoxType::FtypBox));
}
if moov.is_none() {
return Err(Error::BoxNotFound(BoxType::MoovBox));
}
let mut tracks = if let Some(ref moov) = moov {
if moov.traks.iter().any(|trak| trak.tkhd.track_id == 0) {
return Err(Error::InvalidData("illegal track id 0"));
}
moov.traks
.iter()
.map(|trak| (trak.tkhd.track_id, Mp4Track::from(trak)))
.collect()
} else {
HashMap::new()
};
// Update tracks if any fragmented (moof) boxes are found.
// if !moofs.is_empty() {
// let mut default_sample_duration = 0;
// if let Some(ref moov) = moov {
// if let Some(ref mvex) = &moov.mvex {
// default_sample_duration = mvex.trex.default_sample_duration
// }
// }
// for (moof, moof_offset) in moofs.iter().zip(moof_offsets) {
// for traf in moof.trafs.iter() {
// let track_id = traf.tfhd.track_id;
// if let Some(track) = tracks.get_mut(&track_id) {
// track.default_sample_duration = default_sample_duration;
// track.moof_offsets.push(moof_offset);
// track.trafs.push(traf.clone())
// } else {
// return Err(Error::TrakNotFound(track_id));
// }
// }
// }
// }
Ok(Mp4Header {
ftyp,
moov,
moofs,
emsgs,
tracks,
})
}
pub fn can_be_streamed(&self) -> bool {
self.moov.is_some()
}
}

View file

@ -65,9 +65,6 @@
//! [examples]: https://github.com/alfg/mp4-rust/tree/master/examples
#![doc(html_root_url = "https://docs.rs/mp4/*")]
use std::fs::File;
use std::io::BufReader;
mod error;
pub use error::Error;
@ -79,18 +76,22 @@ pub use types::*;
mod mp4box;
pub use mp4box::*;
mod file;
mod header;
mod stream;
mod track;
pub use track::{Mp4Track, TrackConfig};
mod reader;
pub use reader::{Mp4Header, Mp4Reader};
mod async_reader;
pub use async_reader::{AsyncMp4Reader, Mp4Header};
mod writer;
pub use writer::{Mp4Config, Mp4Writer};
pub fn read_mp4(f: File) -> Result<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,7 +298,6 @@ impl NalUnit {
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_avc1() {
@ -343,12 +330,11 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::Avc1Box);
let header = BoxHeader::read_sync(&mut buf.as_slice()).unwrap().unwrap();
assert_eq!(header.kind, BoxType::Avc1Box);
assert_eq!(src_box.box_size(), header.size);
let dst_box = Avc1Box::read_box(&mut reader, header.size).unwrap();
let dst_box = Avc1Box::read_block(&mut buf.as_slice()).unwrap();
assert_eq!(src_box, dst_box);
}
}

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 Co64Box {
}
impl Mp4Box for Co64Box {
fn box_type(&self) -> BoxType {
self.get_type()
}
const TYPE: BoxType = BoxType::Co64Box;
fn box_size(&self) -> u64 {
self.get_size()
@ -43,46 +41,40 @@ impl Mp4Box for Co64Box {
}
}
impl<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
{
let entry_count = reader.get_u32();
if entry_count as usize > reader.remaining() / entry_size {
return Err(Error::InvalidData(
"co64 entry_count indicates more entries than could fit in the box",
));
}
let mut entries = Vec::with_capacity(entry_count as usize);
for _i in 0..entry_count {
let chunk_offset = reader.read_u64::<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,7 +91,6 @@ impl<W: Write> WriteBox<&mut W> for Co64Box {
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_co64() {
@ -112,12 +103,11 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::Co64Box);
let header = BoxHeader::read_sync(&mut buf.as_slice()).unwrap().unwrap();
assert_eq!(header.kind, BoxType::Co64Box);
assert_eq!(src_box.box_size(), header.size);
let dst_box = Co64Box::read_box(&mut reader, header.size).unwrap();
let dst_box = Co64Box::read_block(&mut buf.as_slice()).unwrap();
assert_eq!(src_box, dst_box);
}
}

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 CttsEntry {
}
impl Mp4Box for CttsBox {
fn box_type(&self) -> BoxType {
self.get_type()
}
const TYPE: BoxType = BoxType::CttsBox;
fn box_size(&self) -> u64 {
self.get_size()
@ -49,50 +47,45 @@ impl Mp4Box for CttsBox {
}
}
impl<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
{
if entry_count as usize > reader.remaining() / entry_size {
return Err(Error::InvalidData(
"ctts entry_count indicates more entries than could fit in the box",
));
}
let mut entries = Vec::with_capacity(entry_count as usize);
for _ in 0..entry_count {
let entry = CttsEntry {
sample_count: reader.read_u32::<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,7 +103,6 @@ impl<W: Write> WriteBox<&mut W> for CttsBox {
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_ctts() {
@ -132,12 +124,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::CttsBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::CttsBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = CttsBox::read_box(&mut reader, header.size).unwrap();
let dst_box = CttsBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
}

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,7 +73,6 @@ impl<W: Write> WriteBox<&mut W> for DataBox {
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_data() {
@ -91,12 +84,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::DataBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::DataBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = DataBox::read_box(&mut reader, header.size).unwrap();
let dst_box = DataBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
@ -107,12 +100,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::DataBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::DataBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = DataBox::read_box(&mut reader, header.size).unwrap();
let dst_box = DataBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
}

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
{
if entry_count as usize > reader.remaining() / entry_size {
return Err(Error::InvalidData(
"elst entry_count indicates more entries than could fit in the box",
));
}
let mut entries = Vec::with_capacity(entry_count as usize);
for _ in 0..entry_count {
let (segment_duration, media_time) = if version == 1 {
(
reader.read_u64::<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,7 +134,6 @@ impl<W: Write> WriteBox<&mut W> for ElstBox {
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_elst32() {
@ -165,12 +151,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::ElstBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::ElstBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = ElstBox::read_box(&mut reader, header.size).unwrap();
let dst_box = ElstBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
@ -190,12 +176,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::ElstBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::ElstBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = ElstBox::read_box(&mut reader, header.size).unwrap();
let dst_box = ElstBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
}

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")),
};
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 {
@ -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,7 +157,6 @@ 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;
@ -204,12 +180,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::EmsgBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::EmsgBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = EmsgBox::read_box(&mut reader, header.size).unwrap();
let dst_box = EmsgBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
@ -231,12 +207,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::EmsgBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::EmsgBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = EmsgBox::read_box(&mut reader, header.size).unwrap();
let dst_box = EmsgBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
}

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() - 16) / 4; // header + major + minor
if size < 16 || size % 4 != 0 {
return Err(Error::InvalidData("ftyp size too small or not aligned"));
}
let brand_count = (size - 16) / 4; // header + major + minor
let major = reader.read_u32::<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,7 +90,6 @@ impl<W: Write> WriteBox<&mut W> for FtypBox {
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_ftyp() {
@ -112,12 +107,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::FtypBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::FtypBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = FtypBox::read_box(&mut reader, header.size).unwrap();
let dst_box = FtypBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
}

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,7 +88,6 @@ impl<W: Write> WriteBox<&mut W> for HdlrBox {
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_hdlr() {
@ -114,12 +101,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::HdlrBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::HdlrBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = HdlrBox::read_box(&mut reader, header.size).unwrap();
let dst_box = HdlrBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
@ -135,12 +122,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::HdlrBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::HdlrBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = HdlrBox::read_box(&mut reader, header.size).unwrap();
let dst_box = HdlrBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
@ -162,12 +149,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::HdlrBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::HdlrBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = HdlrBox::read_box(&mut reader, header.size).unwrap();
let dst_box = HdlrBox::read_block(&mut reader).unwrap();
assert_eq!(real_src_box, dst_box);
}
}

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
@ -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(Error::InvalidData(""));
}
let mut arrays = Vec::with_capacity(num_of_arrays as _);
for _ in 0..num_of_arrays {
let params = reader.read_u8()?;
let num_nalus = reader.read_u16::<BigEndian>()?;
let params = reader.get_u8();
let num_nalus = reader.get_u16();
if reader.remaining() < num_nalus as usize * 2 {
return Err(Error::InvalidData(""));
}
let mut nalus = Vec::with_capacity(num_nalus as usize);
for _ in 0..num_nalus {
let size = reader.read_u16::<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,7 +369,6 @@ impl<W: Write> WriteBox<&mut W> for HvcCBox {
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_hev1() {
@ -384,12 +389,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::Hev1Box);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::Hev1Box);
assert_eq!(src_box.box_size(), header.size);
let dst_box = Hev1Box::read_box(&mut reader, header.size).unwrap();
let dst_box = Hev1Box::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
}

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 {
@ -28,9 +27,7 @@ impl IlstBox {
}
impl Mp4Box for IlstBox {
fn box_type(&self) -> BoxType {
self.get_type()
}
const TYPE: BoxType = BoxType::IlstBox;
fn box_size(&self) -> u64 {
self.get_size()
@ -46,56 +43,52 @@ impl Mp4Box for IlstBox {
}
}
impl<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)?);
}
BoxType::CovrBox => {
items.insert(MetadataKey::Poster, IlstItemBox::read_box(reader, s)?);
}
BoxType::DescBox => {
items.insert(MetadataKey::Summary, IlstItemBox::read_box(reader, s)?);
}
_ => {
// XXX warn!()
skip_box(reader, s)?;
}
}
current = reader.stream_position()?;
BoxType::DayBox => {
if let Some(day) = bx.inner.try_find_box::<DataBox>()? {
items.insert(MetadataKey::Year, day);
}
}
BoxType::CovrBox => {
if let Some(cover) = bx.inner.try_find_box::<DataBox>()? {
items.insert(MetadataKey::Poster, cover);
}
}
BoxType::DescBox => {
if let Some(summary) = bx.inner.try_find_box::<DataBox>()? {
items.insert(MetadataKey::Summary, summary);
}
}
_ => continue,
}
}
skip_bytes_to(reader, start + size)?;
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 +97,14 @@ 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)?;
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 +123,18 @@ impl<'a> Metadata<'a> for IlstBox {
}
}
fn item_to_bytes(item: &IlstItemBox) -> &[u8] {
&item.data.data
fn item_to_bytes(item: &DataBox) -> &[u8] {
&item.data
}
fn item_to_str(item: &IlstItemBox) -> Cow<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 +143,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(),
},
let src_year = DataBox {
data_type: DataType::Text,
data: b"test_year".to_vec(),
};
let src_box = IlstBox {
items: [
(MetadataKey::Title, IlstItemBox::default()),
(MetadataKey::Title, DataBox::default()),
(MetadataKey::Year, src_year),
(MetadataKey::Poster, IlstItemBox::default()),
(MetadataKey::Summary, IlstItemBox::default()),
(MetadataKey::Poster, DataBox::default()),
(MetadataKey::Summary, DataBox::default()),
]
.into(),
};
@ -226,12 +164,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::IlstBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::IlstBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = IlstBox::read_box(&mut reader, header.size).unwrap();
let dst_box = IlstBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
@ -242,12 +180,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::IlstBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::IlstBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = IlstBox::read_box(&mut reader, header.size).unwrap();
let dst_box = IlstBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
}

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"));
};
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)?;
@ -166,7 +165,6 @@ fn language_code(language: &str) -> u16 {
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
fn test_language_code(lang: &str) {
let code = language_code(lang);
@ -196,12 +194,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::MdhdBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::MdhdBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = MdhdBox::read_box(&mut reader, header.size).unwrap();
let dst_box = MdhdBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
@ -220,12 +218,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::MdhdBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::MdhdBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = MdhdBox::read_box(&mut reader, header.size).unwrap();
let dst_box = MdhdBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
}

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"));
};
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)?;
@ -93,7 +92,6 @@ impl<W: Write> WriteBox<&mut W> for MehdBox {
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_mehd32() {
@ -106,12 +104,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::MehdBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::MehdBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = MehdBox::read_box(&mut reader, header.size).unwrap();
let dst_box = MehdBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
@ -126,12 +124,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::MehdBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::MehdBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = MehdBox::read_box(&mut reader, header.size).unwrap();
let dst_box = MehdBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
}

View file

@ -1,5 +1,3 @@
use std::io::{Read, Seek};
use serde::Serialize;
use crate::mp4box::hdlr::HdlrBox;
@ -54,9 +52,7 @@ impl MetaBox {
}
impl Mp4Box for MetaBox {
fn box_type(&self) -> BoxType {
self.get_type()
}
const TYPE: BoxType = BoxType::MetaBox;
fn box_size(&self) -> u64 {
self.get_size()
@ -86,18 +82,18 @@ impl Default for MetaBox {
}
}
impl<R: Read + Seek> ReadBox<&mut R> for MetaBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
impl BlockReader for MetaBox {
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
let extended_header = reader.get_u32();
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>()?);
let possible_hdlr = BoxType::from(reader.try_get_u32()?);
if possible_hdlr == BoxType::HdlrBox {
// This file skipped the extended header! Go back to start.
reader.seek(SeekFrom::Current(-8))?;
// reader.seek(SeekFrom::Current(-8))?;
} else {
// Looks like we actually have a bad version number or flags.
let v = (extended_header >> 24) as u8;
@ -105,96 +101,34 @@ impl<R: Read + Seek> ReadBox<&mut R> for MetaBox {
}
}
let mut current = reader.stream_position()?;
let end = start + size;
let content_start = current;
// find the hdlr box
let mut hdlr = None;
while current < end {
// Get box header.
let header = BoxHeader::read(reader)?;
let BoxHeader { name, size: s } = header;
let hdlr = reader.find_box::<HdlrBox>()?;
match name {
BoxType::HdlrBox => {
hdlr = Some(HdlrBox::read_box(reader, s)?);
}
_ => {
// XXX warn!()
skip_box(reader, s)?;
}
}
current = reader.stream_position()?;
}
let Some(hdlr) = hdlr else {
return Err(Error::BoxNotFound(BoxType::HdlrBox));
};
// rewind and handle the other boxes
reader.seek(SeekFrom::Start(content_start))?;
current = reader.stream_position()?;
let mut ilst = None;
match hdlr.handler_type {
MDIR => {
while current < end {
// Get box header.
let header = BoxHeader::read(reader)?;
let BoxHeader { name, size: s } = header;
match name {
BoxType::IlstBox => {
ilst = Some(IlstBox::read_box(reader, s)?);
}
_ => {
// XXX warn!()
skip_box(reader, s)?;
}
}
current = reader.stream_position()?;
}
Ok(MetaBox::Mdir { ilst })
}
Ok(match hdlr.handler_type {
MDIR => MetaBox::Mdir {
ilst: reader.try_find_box::<IlstBox>()?,
},
_ => {
let mut data = Vec::new();
while current < end {
// Get box header.
let header = BoxHeader::read(reader)?;
let BoxHeader { name, size: s } = header;
match name {
BoxType::HdlrBox => {
skip_box(reader, s)?;
}
_ => {
let mut box_data = vec![0; (s - HEADER_SIZE) as usize];
reader.read_exact(&mut box_data)?;
data.push((name, box_data));
}
}
current = reader.stream_position()?;
while let Some(mut bx) = reader.get_box()? {
data.push((bx.kind, bx.inner.collect_remaining()))
}
Ok(MetaBox::Unknown { hdlr, data })
MetaBox::Unknown { hdlr, data }
}
}
})
}
fn size_hint() -> usize {
4
}
}
impl<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,7 +162,6 @@ 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() {
@ -238,12 +171,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::MetaBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::MetaBox);
assert_eq!(header.size, src_box.box_size());
let dst_box = MetaBox::read_box(&mut reader, header.size).unwrap();
let dst_box = MetaBox::read_block(&mut reader).unwrap();
assert_eq!(dst_box, src_box);
}
@ -257,23 +190,24 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::MetaBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::MetaBox);
assert_eq!(header.size, src_box.box_size());
let dst_box = MetaBox::read_box(&mut reader, header.size).unwrap();
let dst_box = MetaBox::read_block(&mut reader).unwrap();
assert_eq!(dst_box, src_box);
}
#[test]
fn test_meta_hdrl_non_first() {
let data = b"\x00\x00\x00\x7fmeta\x00\x00\x00\x00\x00\x00\x00Qilst\x00\x00\x00I\xa9too\x00\x00\x00Adata\x00\x00\x00\x01\x00\x00\x00\x00TMPGEnc Video Mastering Works 7 Version 7.0.15.17\x00\x00\x00\"hdlr\x00\x00\x00\x00\x00\x00\x00\x00mdirappl\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
let mut reader = Cursor::new(data);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::MetaBox);
let meta_box = MetaBox::read_box(&mut reader, header.size).unwrap();
let mut reader = data.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::MetaBox);
let meta_box = MetaBox::read_block(&mut reader).unwrap();
// this contains \xa9too box in the ilst
// it designates the tool that created the file, but is not yet supported by this crate
@ -301,12 +235,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::MetaBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::MetaBox);
assert_eq!(header.size, src_box.box_size());
let dst_box = MetaBox::read_box(&mut reader, header.size).unwrap();
let dst_box = MetaBox::read_block(&mut reader).unwrap();
assert_eq!(dst_box, src_box);
}
}

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,7 +80,6 @@ impl<W: Write> WriteBox<&mut W> for MfhdBox {
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_mfhd() {
@ -96,12 +92,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::MfhdBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::MfhdBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = MfhdBox::read_box(&mut reader, header.size).unwrap();
let dst_box = MfhdBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
}

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,58 +52,18 @@ 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));
}
skip_bytes_to(reader, start + size)?;
Ok(MinfBox {
vmhd,
smhd,
@ -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::*;
@ -162,6 +164,15 @@ macro_rules! boxtype {
UnknownBox(u32),
}
impl BoxType {
pub const fn as_str(&self) -> &'static str {
match self {
$( BoxType::$name => stringify!($name), )*
BoxType::UnknownBox(_) => "unknown",
}
}
}
impl From<u32> for BoxType {
fn from(t: u32) -> BoxType {
match t {
@ -242,14 +253,461 @@ boxtype! {
}
pub trait Mp4Box: Sized {
fn box_type(&self) -> BoxType;
const TYPE: BoxType;
fn box_size(&self) -> u64;
fn to_json(&self) -> Result<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(Error::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 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(Error::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(Error::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(Error::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(Error::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(Error::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(Error::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(Error::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(Error::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(Error::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(Error::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(Error::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(Error::InvalidData("expected at least 8 byte more"))
} else {
Ok(self.get_i64())
}
}
fn get_null_terminated_string(&mut self) -> String;
fn collect(&mut self, size: usize) -> Result<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(|| Error::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(Error::BoxNotFound(A::TYPE));
};
let Some(b) = b else {
return Err(Error::BoxNotFound(B::TYPE));
};
let Some(c) = c else {
return Err(Error::BoxNotFound(C::TYPE));
};
Ok((a, b, c))
}
fn try_find_box4<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(Error::InvalidData("no bytes left"));
}
let buff = &(*self)[0..size];
self.advance(size);
Ok(buff)
}
#[inline]
fn skip(&mut self, size: usize) {
Buf::advance(self, size)
}
#[inline]
fn remaining(&self) -> usize {
Buf::remaining(self)
}
#[inline]
fn get_u8(&mut self) -> u8 {
Buf::get_u8(self)
}
#[inline]
fn get_u16(&mut self) -> u16 {
Buf::get_u16(self)
}
#[inline]
fn get_u24(&mut self) -> u32 {
let val = BigEndian::read_u24(self.chunk());
self.skip(3);
val
}
#[inline]
fn get_u32(&mut self) -> u32 {
Buf::get_u32(self)
}
#[inline]
fn get_u48(&mut self) -> u64 {
let val = BigEndian::read_u48(self.chunk());
self.skip(6);
val
}
#[inline]
fn get_u64(&mut self) -> u64 {
Buf::get_u64(self)
}
#[inline]
fn get_i8(&mut self) -> i8 {
Buf::get_i8(self)
}
#[inline]
fn get_i16(&mut self) -> i16 {
Buf::get_i16(self)
}
#[inline]
fn get_i24(&mut self) -> i32 {
todo!()
}
#[inline]
fn get_i32(&mut self) -> i32 {
Buf::get_i32(self)
}
#[inline]
fn get_i48(&mut self) -> i64 {
todo!()
}
#[inline]
fn get_i64(&mut self) -> i64 {
Buf::get_i64(self)
}
#[inline]
fn copy_to_slice(&mut self, slice: &mut [u8]) -> Result<()> {
if self.len() < slice.len() {
return Err(Error::InvalidData("expected more bytes"));
}
Buf::copy_to_slice(self, slice);
Ok(())
}
#[inline]
fn get_null_terminated_string(&mut self) -> String {
let rem = self.len();
if rem > 0 {
let size = self.iter().position(|&b| b == b'\0');
let (size, eat) = if let Some(size) = size {
(size, size + 1)
} else {
(rem, rem)
};
let val = String::from_utf8_lossy(&self[0..size]).to_string();
self.advance(eat);
val
} else {
String::new()
}
}
#[inline]
fn get_box(&mut self) -> Result<Option<BoxReader<'a, impl Reader<'a> + '_>>> {
let Some(BoxHeader { kind, size }) = BoxHeader::read_sync(self)? else {
return Ok(None);
};
Ok(Some(BoxReader {
kind,
inner: Reader::take(self, size as _)?,
m: PhantomData,
}))
}
}
pub trait BlockReader: Sized {
fn read_block<'a>(block: &mut impl Reader<'a>) -> Result<Self>;
fn size_hint() -> usize;
}
pub trait WriteBox<T>: Sized {
@ -258,72 +716,129 @@ 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>) -> Result<Option<Self>> {
if reader.remaining() < 8 {
return Ok(None);
}
let sz = reader.get_u32();
let typ = reader.get_u32();
// Get largesize if size is 1
let size = if sz == 1 {
if reader.remaining() < 8 {
return Err(Error::InvalidData("expected 8 bytes more"));
}
let largesize = reader.get_u64();
// Subtract the length of the serialized largesize, as callers assume `size - HEADER_SIZE` is the length
// of the box data. Disallow `largesize < 16`, or else a largesize of 8 will result in a BoxHeader::size
// of 0, incorrectly indicating that the box data extends to the end of the stream.
match largesize {
0 => 0,
1..=15 => return Err(Error::InvalidData("64-bit box size too small")),
16..=u64::MAX => largesize - 8,
}
} else {
sz as _
};
println!(
"{} box {} {}",
if sz == 1 { "big" } else { "small" },
BoxType::from(typ).as_str(),
size
);
Ok(Some(BoxHeader {
kind: BoxType::from(typ),
size: size.saturating_sub(HEADER_SIZE),
}))
}
// TODO: if size is 0, then this box is the last one in the file
pub fn read<R: Read>(reader: &mut R) -> Result<Self> {
pub async fn read<R: AsyncRead + Unpin>(reader: &mut R) -> 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()),
},
}
// Get size.
let s = buf[0..4].try_into().unwrap();
let size = u32::from_be_bytes(s);
let sz = u32::from_be_bytes(s);
// Get box type string.
let t = buf[4..8].try_into().unwrap();
let typ = u32::from_be_bytes(t);
// Get largesize if size is 1
if size == 1 {
reader.read_exact(&mut buf)?;
let size = if sz == 1 {
match reader.read_exact(&mut buf).await {
Ok(_) => (),
Err(err) => match err.kind() {
std::io::ErrorKind::UnexpectedEof => return Ok(None),
_ => return Err(err.into()),
},
}
let largesize = u64::from_be_bytes(buf);
Ok(BoxHeader {
name: BoxType::from(typ),
// Subtract the length of the serialized largesize, as callers assume `size - HEADER_SIZE` is the length
// of the box data. Disallow `largesize < 16`, or else a largesize of 8 will result in a BoxHeader::size
// of 0, incorrectly indicating that the box data extends to the end of the stream.
size: match largesize {
0 => 0,
1..=15 => return Err(Error::InvalidData("64-bit box size too small")),
16..=u64::MAX => largesize - 8,
},
})
// Subtract the length of the serialized largesize, as callers assume `size - HEADER_SIZE` is the length
// of the box data. Disallow `largesize < 16`, or else a largesize of 8 will result in a BoxHeader::size
// of 0, incorrectly indicating that the box data extends to the end of the stream.
match largesize {
0 => 0,
1..=15 => return Err(Error::InvalidData("64-bit box size too small")),
16..=u64::MAX => largesize - 8,
}
} else {
Ok(BoxHeader {
name: BoxType::from(typ),
size: size as u64,
})
}
sz as _
};
println!(
"{} box {} {}",
if sz == 1 { "big" } else { "small" },
BoxType::from(typ).as_str(),
size
);
Ok(Some(BoxHeader {
kind: BoxType::from(typ),
size: size.saturating_sub(HEADER_SIZE),
}))
}
pub fn write<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_u64::<BigEndian>(self.size)?;
writer.write_u32::<BigEndian>(self.kind.into())?;
writer.write_u64::<BigEndian>(self.size + HEADER_SIZE)?;
Ok(16)
} else {
writer.write_u32::<BigEndian>(self.size as u32)?;
writer.write_u32::<BigEndian>(self.name.into())?;
writer.write_u32::<BigEndian>((self.size + HEADER_SIZE) as u32)?;
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 +847,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)?;
@ -410,25 +905,29 @@ mod tests {
#[test]
fn test_largesize_too_small() {
let error = BoxHeader::read(&mut &[0, 0, 0, 1, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 7][..]);
let error =
BoxHeader::read_sync(&mut &[0, 0, 0, 1, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 7][..]);
assert!(matches!(error, Err(Error::InvalidData(_))));
}
#[test]
fn test_zero_largesize() {
let error = BoxHeader::read(&mut &[0, 0, 0, 1, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 8][..]);
let error =
BoxHeader::read_sync(&mut &[0, 0, 0, 1, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 8][..]);
assert!(matches!(error, Err(Error::InvalidData(_))));
}
#[test]
fn test_nonzero_largesize_too_small() {
let error = BoxHeader::read(&mut &[0, 0, 0, 1, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 15][..]);
let error =
BoxHeader::read_sync(&mut &[0, 0, 0, 1, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 15][..]);
assert!(matches!(error, Err(Error::InvalidData(_))));
}
#[test]
fn test_valid_largesize() {
let header = BoxHeader::read(&mut &[0, 0, 0, 1, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 16][..]);
assert!(matches!(header, Ok(BoxHeader { size: 8, .. })));
let header =
BoxHeader::read_sync(&mut &[0, 0, 0, 1, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 16][..]);
assert!(matches!(header, Ok(Some(BoxHeader { size: 8, .. }))));
}
}

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));
}
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,9 +59,9 @@ 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> {
println!("begin reading moov");
let mut mvhd = None;
let mut meta = None;
@ -71,50 +69,37 @@ impl<R: Read + Seek> ReadBox<&mut R> for MoovBox {
let mut mvex = None;
let mut traks = Vec::new();
let mut current = reader.stream_position()?;
let end = start + size;
while current < end {
// Get box header.
let header = BoxHeader::read(reader)?;
let BoxHeader { name, size: s } = header;
if s > size {
return Err(Error::InvalidData(
"moov box contains a box with a larger size than it",
));
}
match name {
while let Some(mut bx) = reader.get_box()? {
match bx.kind {
BoxType::MvhdBox => {
mvhd = Some(MvhdBox::read_box(reader, s)?);
mvhd = Some(bx.read()?);
}
BoxType::MetaBox => {
meta = Some(MetaBox::read_box(reader, s)?);
}
BoxType::MvexBox => {
mvex = Some(MvexBox::read_box(reader, s)?);
}
BoxType::TrakBox => {
let trak = TrakBox::read_box(reader, s)?;
traks.push(trak);
}
BoxType::UdtaBox => {
udta = Some(UdtaBox::read_box(reader, s)?);
}
_ => {
// XXX warn!()
skip_box(reader, s)?;
}
}
current = reader.stream_position()?;
BoxType::MetaBox => {
meta = Some(bx.read()?);
}
BoxType::MvexBox => {
mvex = Some(bx.read()?);
}
BoxType::TrakBox => {
traks.push(bx.read()?);
}
BoxType::UdtaBox => {
udta = Some(bx.read()?);
}
_ => continue,
}
}
if mvhd.is_none() {
return Err(Error::BoxNotFound(BoxType::MvhdBox));
}
skip_bytes_to(reader, start + size)?;
println!("end reading moov");
Ok(MoovBox {
mvhd: mvhd.unwrap(),
meta,
@ -123,12 +108,16 @@ impl<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,7 +137,6 @@ impl<W: Write> WriteBox<&mut W> for MoovBox {
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_moov() {
@ -164,12 +152,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::MoovBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::MoovBox);
assert_eq!(header.size, src_box.box_size());
let dst_box = MoovBox::read_box(&mut reader, header.size).unwrap();
let dst_box = MoovBox::read_block(&mut reader).unwrap();
assert_eq!(dst_box, src_box);
}
@ -181,12 +169,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::MoovBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::MoovBox);
assert_eq!(header.size, src_box.box_size());
let dst_box = MoovBox::read_box(&mut reader, header.size).unwrap();
let dst_box = MoovBox::read_block(&mut reader).unwrap();
assert_eq!(dst_box, src_box);
}
}

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(Error::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"));
}
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 {
@ -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,7 +594,6 @@ impl<W: Write> WriteDesc<&mut W> for SLConfigDescriptor {
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_mp4a() {
@ -650,12 +628,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::Mp4aBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::Mp4aBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = Mp4aBox::read_box(&mut reader, header.size).unwrap();
let dst_box = Mp4aBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
@ -672,12 +650,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::Mp4aBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::Mp4aBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = Mp4aBox::read_box(&mut reader, header.size).unwrap();
let dst_box = Mp4aBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
}

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));
}
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(Error::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"));
};
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)?;
@ -199,7 +200,6 @@ impl<W: Write> WriteBox<&mut W> for MvhdBox {
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_mvhd32() {
@ -219,12 +219,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::MvhdBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::MvhdBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = MvhdBox::read_box(&mut reader, header.size).unwrap();
let dst_box = MvhdBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
@ -246,12 +246,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::MvhdBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::MvhdBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = MvhdBox::read_box(&mut reader, header.size).unwrap();
let dst_box = MvhdBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
}

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,7 +84,6 @@ impl<W: Write> WriteBox<&mut W> for SmhdBox {
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_smhd() {
@ -101,12 +96,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::SmhdBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::SmhdBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = SmhdBox::read_box(&mut reader, header.size).unwrap();
let dst_box = SmhdBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
}

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,69 +82,64 @@ 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));
}
skip_bytes_to(reader, start + size)?;
Ok(StblBox {
stsd: stsd.unwrap(),
stts: stts.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::*;
@ -25,9 +25,7 @@ impl StcoBox {
}
impl Mp4Box for StcoBox {
fn box_type(&self) -> BoxType {
self.get_type()
}
const TYPE: BoxType = BoxType::StcoBox;
fn box_size(&self) -> u64 {
self.get_size()
@ -43,46 +41,40 @@ impl Mp4Box for StcoBox {
}
}
impl<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
{
let entry_count = reader.get_u32();
if entry_count as usize > reader.remaining() / entry_size {
return Err(Error::InvalidData(
"stco entry_count indicates more entries than could fit in the box",
));
}
let mut entries = Vec::with_capacity(entry_count as usize);
for _i in 0..entry_count {
let chunk_offset = reader.read_u32::<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,7 +109,6 @@ impl std::convert::TryFrom<&co64::Co64Box> for StcoBox {
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_stco() {
@ -130,12 +121,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::StcoBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::StcoBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = StcoBox::read_box(&mut reader, header.size).unwrap();
let dst_box = StcoBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
}

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::*;
@ -33,9 +33,7 @@ pub struct StscEntry {
}
impl Mp4Box for StscBox {
fn box_type(&self) -> BoxType {
self.get_type()
}
const TYPE: BoxType = BoxType::StscBox;
fn box_size(&self) -> u64 {
self.get_size()
@ -51,22 +49,13 @@ impl Mp4Box for StscBox {
}
}
impl<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
{
let entry_count = reader.get_u32();
if entry_count as usize > reader.remaining() / entry_size {
return Err(Error::InvalidData(
"stsc entry_count indicates more entries than could fit in the box",
));
@ -74,9 +63,9 @@ impl<R: Read + Seek> ReadBox<&mut R> for StscBox {
let mut entries = Vec::with_capacity(entry_count as usize);
for _ in 0..entry_count {
let entry = StscEntry {
first_chunk: reader.read_u32::<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);
@ -102,20 +91,22 @@ impl<R: Read + Seek> ReadBox<&mut R> for StscBox {
}
}
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,7 +125,6 @@ impl<W: Write> WriteBox<&mut W> for StscBox {
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_stsc() {
@ -160,12 +150,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::StscBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::StscBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = StscBox::read_box(&mut reader, header.size).unwrap();
let dst_box = StscBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
}

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
{
let entry_count = reader.get_u32();
if entry_count as usize > reader.remaining() / entry_size {
return Err(Error::InvalidData(
"stss entry_count indicates more entries than could fit in the box",
));
}
let mut entries = Vec::with_capacity(entry_count as usize);
for _i in 0..entry_count {
let sample_number = reader.read_u32::<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,7 +91,6 @@ impl<W: Write> WriteBox<&mut W> for StssBox {
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_stss() {
@ -112,12 +103,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::StssBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::StssBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = StssBox::read_box(&mut reader, header.size).unwrap();
let dst_box = StssBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
}

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
{
if sample_count as usize > reader.remaining() / stsz_item_size {
return Err(Error::InvalidData(
"stsz sample_count indicates more values than could fit in the box",
));
}
sample_sizes.reserve(sample_count as usize);
for _ in 0..sample_count {
let sample_number = reader.read_u32::<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)?;
@ -122,7 +113,6 @@ 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() {
@ -137,12 +127,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::StszBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::StszBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = StszBox::read_box(&mut reader, header.size).unwrap();
let dst_box = StszBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
@ -159,12 +149,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::StszBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::StszBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = StszBox::read_box(&mut reader, header.size).unwrap();
let dst_box = StszBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
}

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
{
let entry_count = reader.get_u32();
if entry_count as usize > reader.remaining() / entry_size {
return Err(Error::InvalidData(
"stts entry_count indicates more entries than could fit in the box",
));
}
let mut entries = Vec::with_capacity(entry_count as usize);
for _i in 0..entry_count {
let entry = SttsEntry {
sample_count: reader.read_u32::<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,7 +102,6 @@ impl<W: Write> WriteBox<&mut W> for SttsBox {
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_stts() {
@ -131,12 +123,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::SttsBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::SttsBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = SttsBox::read_box(&mut reader, header.size).unwrap();
let dst_box = SttsBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
}

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"));
};
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)?;
@ -93,7 +91,6 @@ impl<W: Write> WriteBox<&mut W> for TfdtBox {
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_tfdt32() {
@ -106,12 +103,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::TfdtBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::TfdtBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = TfdtBox::read_box(&mut reader, header.size).unwrap();
let dst_box = TfdtBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
@ -126,12 +123,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::TfdtBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::TfdtBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = TfdtBox::read_box(&mut reader, header.size).unwrap();
let dst_box = TfdtBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
}

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,7 +150,6 @@ impl<W: Write> WriteBox<&mut W> for TfhdBox {
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_tfhd() {
@ -165,12 +167,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::TfhdBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::TfhdBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = TfhdBox::read_box(&mut reader, header.size).unwrap();
let dst_box = TfhdBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
@ -192,12 +194,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::TfhdBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::TfhdBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = TfhdBox::read_box(&mut reader, header.size).unwrap();
let dst_box = TfhdBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
}

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"));
};
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)?;
@ -261,7 +262,6 @@ impl<W: Write> WriteBox<&mut W> for TkhdBox {
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_tkhd32() {
@ -283,12 +283,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::TkhdBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::TkhdBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = TkhdBox::read_box(&mut reader, header.size).unwrap();
let dst_box = TkhdBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
@ -312,12 +312,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::TkhdBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::TkhdBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = TkhdBox::read_box(&mut reader, header.size).unwrap();
let dst_box = TkhdBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
}

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));
}
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(Error::InvalidData("no stsc entries"));
}
for (i, entry) in self.mdia.minf.stbl.stsc.entries.iter().enumerate() {
if sample_id < entry.first_sample {
return if i == 0 {
Err(Error::InvalidData("sample not found"))
} else {
Ok(i - 1)
};
}
}
Ok(self.mdia.minf.stbl.stsc.entries.len() - 1)
}
pub(crate) fn chunk_offset(&self, chunk_id: u32) -> Result<u64> {
if self.mdia.minf.stbl.stco.is_none() && self.mdia.minf.stbl.co64.is_none() {
return Err(Error::InvalidData("must have either stco or co64 boxes"));
}
if let Some(ref stco) = self.mdia.minf.stbl.stco {
if let Some(offset) = stco.entries.get(chunk_id as usize - 1) {
return Ok(*offset as u64);
} else {
return Err(Error::EntryInStblNotFound(
self.tkhd.track_id,
BoxType::StcoBox,
chunk_id,
));
}
} else if let Some(ref co64) = self.mdia.minf.stbl.co64 {
if let Some(offset) = co64.entries.get(chunk_id as usize - 1) {
return Ok(*offset);
} else {
return Err(Error::EntryInStblNotFound(
self.tkhd.track_id,
BoxType::Co64Box,
chunk_id,
));
}
}
Err(Error::Box2NotFound(BoxType::StcoBox, BoxType::Co64Box))
}
pub(crate) fn sample_size(&self, sample_id: u32) -> Result<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(Error::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(Error::InvalidData(
"attempt to calculate stsc chunk_id with overflow",
))?;
let chunk_offset = self.chunk_offset(chunk_id)?;
let first_sample_in_chunk = sample_id - (sample_id - first_sample) % samples_per_chunk;
let mut sample_offset = 0;
for i in first_sample_in_chunk..sample_id {
sample_offset += self.sample_size(i)?;
}
Ok(chunk_offset + sample_offset as u64)
}
pub(crate) fn sample_time(&self, sample_id: u32) -> Result<(u64, u32)> {
let stts = self.mdia.minf.stbl.stts;
let mut sample_count: u32 = 1;
let mut elapsed = 0;
for entry in stts.entries.iter() {
let new_sample_count =
sample_count
.checked_add(entry.sample_count)
.ok_or(Error::InvalidData(
"attempt to sum stts entries sample_count with overflow",
))?;
if sample_id < new_sample_count {
let start_time =
(sample_id - sample_count) as u64 * entry.sample_delta as u64 + elapsed;
return Ok((start_time, entry.sample_delta));
}
sample_count = new_sample_count;
elapsed += entry.sample_count as u64 * entry.sample_delta as u64;
}
Err(Error::EntryInStblNotFound(
self.tkhd.track_id,
BoxType::SttsBox,
sample_id,
))
}
pub(crate) fn ctts_index(&self, sample_id: u32) -> Result<(usize, u32)> {
let ctts = self.mdia.minf.stbl.ctts.as_ref().unwrap();
let mut sample_count: u32 = 1;
for (i, entry) in ctts.entries.iter().enumerate() {
let next_sample_count =
sample_count
.checked_add(entry.sample_count)
.ok_or(Error::InvalidData(
"attempt to sum ctts entries sample_count with overflow",
))?;
if sample_id < next_sample_count {
return Ok((i, sample_count));
}
sample_count = next_sample_count;
}
Err(Error::EntryInStblNotFound(
self.tkhd.track_id,
BoxType::CttsBox,
sample_id,
))
}
pub fn sample_rendering_offset(self, sample_id: u32) -> i32 {
if let Some(ref ctts) = self.mdia.minf.stbl.ctts {
if let Ok((ctts_index, _)) = self.ctts_index(sample_id) {
let ctts_entry = ctts.entries.get(ctts_index).unwrap();
return ctts_entry.sample_offset;
}
}
0
}
#[inline]
pub fn sample_is_sync(&self, sample_id: u32) -> bool {
if let Some(ref stss) = self.mdia.minf.stbl.stss {
stss.entries.binary_search(&sample_id).is_ok()
} else {
true
}
}
}
impl Mp4Box for TrakBox {
fn box_type(&self) -> BoxType {
self.get_type()
}
const TYPE: BoxType = BoxType::TrakBox;
fn box_size(&self) -> u64 {
self.get_size()
@ -53,58 +220,18 @@ impl Mp4Box for TrakBox {
}
}
impl<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));
}
skip_bytes_to(reader, start + size)?;
Ok(TrakBox {
tkhd: tkhd.unwrap(),
edts,
@ -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,7 +92,6 @@ impl<W: Write> WriteBox<&mut W> for TrexBox {
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_trex() {
@ -111,12 +108,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::TrexBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::TrexBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = TrexBox::read_box(&mut reader, header.size).unwrap();
let dst_box = TrexBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
}

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)
{
if sample_count as usize * sample_size > reader.remaining() {
return Err(Error::InvalidData(
"trun sample_count indicates more values than could fit in the box",
));
}
if TrunBox::FLAG_SAMPLE_DURATION & flags > 0 {
sample_durations.reserve(sample_count as usize);
}
if TrunBox::FLAG_SAMPLE_SIZE & flags > 0 {
sample_sizes.reserve(sample_count as usize);
}
if TrunBox::FLAG_SAMPLE_FLAGS & flags > 0 {
sample_flags.reserve(sample_count as usize);
}
if TrunBox::FLAG_SAMPLE_CTS & flags > 0 {
sample_cts.reserve(sample_count as usize);
}
for _ in 0..sample_count {
if TrunBox::FLAG_SAMPLE_DURATION & flags > 0 {
let duration = reader.read_u32::<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)?;
@ -211,7 +209,6 @@ 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() {
@ -230,12 +227,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::TrunBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::TrunBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = TrunBox::read_box(&mut reader, header.size).unwrap();
let dst_box = TrunBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
@ -259,12 +256,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::TrunBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::TrunBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = TrunBox::read_box(&mut reader, header.size).unwrap();
let dst_box = TrunBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
}

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,7 +154,6 @@ impl<W: Write> WriteBox<&mut W> for Tx3gBox {
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_tx3g() {
@ -178,12 +175,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::Tx3gBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::Tx3gBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = Tx3gBox::read_box(&mut reader, header.size).unwrap();
let dst_box = Tx3gBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
}

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,7 +67,6 @@ 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() {
@ -106,12 +76,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::UdtaBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::UdtaBox);
assert_eq!(header.size, src_box.box_size());
let dst_box = UdtaBox::read_box(&mut reader, header.size).unwrap();
let dst_box = UdtaBox::read_block(&mut reader).unwrap();
assert_eq!(dst_box, src_box);
}
@ -125,12 +95,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::UdtaBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::UdtaBox);
assert_eq!(header.size, src_box.box_size());
let dst_box = UdtaBox::read_box(&mut reader, header.size).unwrap();
let dst_box = UdtaBox::read_block(&mut reader).unwrap();
assert_eq!(dst_box, src_box);
}
}

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,7 +92,6 @@ impl<W: Write> WriteBox<&mut W> for VmhdBox {
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_vmhd() {
@ -113,12 +109,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::VmhdBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::VmhdBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = VmhdBox::read_box(&mut reader, header.size).unwrap();
let dst_box = VmhdBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
}

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,7 +168,6 @@ impl<W: Write> WriteBox<&mut W> for Vp09Box {
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_vpcc() {
@ -194,12 +179,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::Vp09Box);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::Vp09Box);
assert_eq!(src_box.box_size(), header.size);
let dst_box = Vp09Box::read_box(&mut reader, header.size).unwrap();
let dst_box = Vp09Box::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
}

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,7 +100,6 @@ impl<W: Write> WriteBox<&mut W> for VpccBox {
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_vpcc() {
@ -121,12 +120,12 @@ mod tests {
src_box.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_box.box_size() as usize);
let mut reader = Cursor::new(&buf);
let header = BoxHeader::read(&mut reader).unwrap();
assert_eq!(header.name, BoxType::VpccBox);
let mut reader = buf.as_slice();
let header = BoxHeader::read_sync(&mut reader).unwrap().unwrap();
assert_eq!(header.kind, BoxType::VpccBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = VpccBox::read_box(&mut reader, header.size).unwrap();
let dst_box = VpccBox::read_block(&mut reader).unwrap();
assert_eq!(src_box, dst_box);
}
}

View file

@ -32,9 +32,7 @@ impl Mp4Header {
let header = BoxHeader::read(reader)?;
let BoxHeader { name, size: s } = header;
if s > size {
return Err(Error::InvalidData(
"file contains a box with a larger size than it",
));
break;
}
// Break if size zero BoxHeader, which can result in dead-loop.

19
src/stream.rs Normal file
View file

@ -0,0 +1,19 @@
use std::{
pin::Pin,
task::{Context, Poll},
};
use futures::Stream;
use crate::Error;
pub struct Mp4Frame {}
pub struct Mp4Stream {}
impl Stream for Mp4Stream {
type Item = Result<Mp4Frame, Error>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
todo!()
}
}

View file

@ -89,7 +89,7 @@ impl From<Vp9Config> for TrackConfig {
}
}
#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct Mp4Track {
pub trak: TrakBox,
pub trafs: Vec<TrafBox>,
@ -261,7 +261,7 @@ impl Mp4Track {
pub fn sequence_parameter_set(&self) -> Result<&[u8]> {
if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 {
match avc1.avcc.sequence_parameter_sets.get(0) {
match avc1.avcc.sequence_parameter_sets.first() {
Some(nal) => Ok(nal.bytes.as_ref()),
None => Err(Error::EntryInStblNotFound(
self.track_id(),
@ -276,7 +276,7 @@ impl Mp4Track {
pub fn picture_parameter_set(&self) -> Result<&[u8]> {
if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 {
match avc1.avcc.picture_parameter_sets.get(0) {
match avc1.avcc.picture_parameter_sets.first() {
Some(nal) => Ok(nal.bytes.as_ref()),
None => Err(Error::EntryInStblNotFound(
self.track_id(),
@ -386,7 +386,7 @@ impl Mp4Track {
None
}
fn sample_size(&self, sample_id: u32) -> Result<u32> {
pub(crate) fn sample_size(&self, sample_id: u32) -> Result<u32> {
if !self.trafs.is_empty() {
if let Some((traf_idx, sample_idx)) = self.find_traf_idx_and_sample_idx(sample_id) {
if let Some(size) = self.trafs[traf_idx]
@ -499,18 +499,16 @@ impl Mp4Track {
}
}
fn sample_time(&self, sample_id: u32) -> Result<(u64, u32)> {
pub(crate) fn sample_time(&self, sample_id: u32) -> Result<(u64, u32)> {
if !self.trafs.is_empty() {
let mut base_start_time = 0;
let mut default_sample_duration = self.default_sample_duration;
if let Some((traf_idx, sample_idx)) = self.find_traf_idx_and_sample_idx(sample_id) {
let traf = &self.trafs[traf_idx];
if let Some(tfdt) = &traf.tfdt {
base_start_time = tfdt.base_media_decode_time;
}
if let Some(duration) = traf.tfhd.default_sample_duration {
default_sample_duration = duration;
}
let base_start_time = traf
.tfdt
.as_ref()
.map(|tfdt| tfdt.base_media_decode_time)
.unwrap_or(0);
if let Some(trun) = &traf.trun {
if TrunBox::FLAG_SAMPLE_DURATION & trun.flags != 0 {
let mut start_offset = 0u64;
@ -523,9 +521,21 @@ impl Mp4Track {
return Ok((base_start_time + start_offset, duration));
}
}
let default_sample_duration = traf
.tfhd
.default_sample_duration
.unwrap_or(self.default_sample_duration);
let start_offset = sample_idx as u64 * default_sample_duration as u64;
Ok((base_start_time + start_offset, default_sample_duration))
} else {
Ok((
((sample_id - 1) * self.default_sample_duration) as u64,
self.default_sample_duration,
))
}
let start_offset = ((sample_id - 1) * default_sample_duration) as u64;
Ok((base_start_time + start_offset, default_sample_duration))
} else {
let stts = &self.trak.mdia.minf.stbl.stts;
@ -557,7 +567,7 @@ impl Mp4Track {
}
}
fn sample_rendering_offset(&self, sample_id: u32) -> i32 {
pub(crate) fn sample_rendering_offset(&self, sample_id: u32) -> i32 {
if !self.trafs.is_empty() {
if let Some((traf_idx, sample_idx)) = self.find_traf_idx_and_sample_idx(sample_id) {
if let Some(cts) = self.trafs[traf_idx]
@ -577,10 +587,13 @@ impl Mp4Track {
0
}
fn is_sync_sample(&self, sample_id: u32) -> bool {
pub(crate) fn is_sync_sample(&self, sample_id: u32) -> bool {
if !self.trafs.is_empty() {
let sample_sizes_count = self.sample_count() / self.trafs.len() as u32;
return sample_id == 1 || sample_id % sample_sizes_count == 0;
if let Some((_, sample_idx)) = self.find_traf_idx_and_sample_idx(sample_id) {
return sample_idx == 0;
}
return sample_id == 1;
}
if let Some(ref stss) = self.trak.mdia.minf.stbl.stss {
@ -600,6 +613,7 @@ impl Mp4Track {
Err(Error::EntryInStblNotFound(_, _, _)) => return Ok(None),
Err(err) => return Err(err),
};
let sample_size = match self.sample_size(sample_id) {
Ok(size) => size,
Err(Error::EntryInStblNotFound(_, _, _)) => return Ok(None),

View file

@ -1,6 +1,5 @@
use mp4::{
AudioObjectType, AvcProfile, ChannelConfig, MediaType, Metadata, Mp4Reader, SampleFreqIndex,
TrackType,
AudioObjectType, AvcProfile, ChannelConfig, MediaType, Mp4Reader, SampleFreqIndex, TrackType,
};
use std::fs::{self, File};
use std::io::BufReader;