mirror of
https://github.com/alfg/mp4-rust.git
synced 2025-02-23 08:16:16 +00:00
Async Mp4Stream
This commit is contained in:
parent
a4bb30dae6
commit
7abff74e90
62 changed files with 4135 additions and 2548 deletions
10
Cargo.toml
10
Cargo.toml
|
@ -19,9 +19,17 @@ bytes = "1.1.0"
|
|||
num-rational = { version = "0.4.0", features = ["serde"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
tokio = { version = "1.37.0", features = ["io-util"] }
|
||||
futures = "0.3.30"
|
||||
const_format = "0.2.32"
|
||||
pin-project-lite = "0.2.14"
|
||||
async-stream = "0.3.5"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.3"
|
||||
anyhow = "1.0"
|
||||
criterion = "0.5.1"
|
||||
tokio = { version = "1.37.0", features = ["full"] }
|
||||
tokio-util = "0.7.10"
|
||||
|
||||
[[bench]]
|
||||
name = "bench_main"
|
||||
|
|
|
@ -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
338
examples/mp4_to_mpeg2ts.rs
Normal 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(¶ms);
|
||||
// }
|
||||
|
||||
// body.put_slice(&[0, 0, 0, 1, 9, 240]);
|
||||
// body.put_slice(&bytes);
|
||||
|
||||
// ts.send(&Frame {
|
||||
// pts,
|
||||
// dts,
|
||||
// body: body.freeze(),
|
||||
// key: samp.is_sync,
|
||||
// })
|
||||
// .await?;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
// fn convert_h264(data: &mut [u8]) -> anyhow::Result<()> {
|
||||
// // TODO:
|
||||
// // * For each IDR frame, copy the SPS and PPS from the stream's
|
||||
// // parameters, rather than depend on it being present in the frame
|
||||
// // already. In-band parameters aren't guaranteed. This is awkward
|
||||
// // with h264_reader v0.5's h264_reader::avcc::AvcDecoderRecord because it
|
||||
// // strips off the NAL header byte from each parameter. The next major
|
||||
// // version shouldn't do this.
|
||||
// // * Copy only the slice data. In particular, don't copy SEI, which confuses
|
||||
// // Safari: <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() {}
|
|
@ -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() {}
|
||||
|
|
|
@ -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() {}
|
||||
|
|
|
@ -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() {}
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::io::{self, BufReader};
|
||||
use std::path::Path;
|
||||
use std::{env, io};
|
||||
|
||||
use mp4::Result;
|
||||
use mp4::{MemoryStorageError, TrackType};
|
||||
use tokio::fs::File;
|
||||
use tokio::io::BufReader;
|
||||
|
||||
fn main() {
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
||||
if args.len() < 2 {
|
||||
|
@ -14,37 +15,45 @@ fn main() {
|
|||
std::process::exit(1);
|
||||
}
|
||||
|
||||
if let Err(err) = samples(&args[1]) {
|
||||
if let Err(err) = samples(&args[1]).await {
|
||||
let _ = writeln!(io::stderr(), "{}", err);
|
||||
}
|
||||
}
|
||||
|
||||
fn samples<P: AsRef<Path>>(filename: &P) -> Result<()> {
|
||||
let f = File::open(filename)?;
|
||||
let size = f.metadata()?.len();
|
||||
let reader = BufReader::new(f);
|
||||
async fn samples<P: AsRef<Path>>(filename: &P) -> Result<(), mp4::Error<MemoryStorageError>> {
|
||||
let f = File::open(filename).await?;
|
||||
let mut reader = BufReader::new(f);
|
||||
|
||||
let mut mp4 = mp4::Mp4Reader::read_header(reader, size)?;
|
||||
let mut mp4_file = mp4::Mp4File::new(&mut reader);
|
||||
println!("streaming possible: {}", mp4_file.read_header().await?);
|
||||
|
||||
for track_id in mp4.tracks().keys().copied().collect::<Vec<u32>>() {
|
||||
let sample_count = mp4.sample_count(track_id).unwrap();
|
||||
let mut keys = mp4_file
|
||||
.tracks
|
||||
.iter()
|
||||
.filter(|&(_, v)| v.track_type() == TrackType::Video)
|
||||
.map(|(k, _)| *k);
|
||||
|
||||
for sample_idx in 0..sample_count {
|
||||
let sample_id = sample_idx + 1;
|
||||
let sample = mp4.read_sample(track_id, sample_id);
|
||||
let track_id = keys.next().unwrap();
|
||||
let track = mp4_file.tracks.get(&track_id).unwrap();
|
||||
|
||||
for (idx, samp) in track.samples.iter().enumerate() {
|
||||
let data = mp4_file
|
||||
.read_sample_data(track_id, idx)
|
||||
.await?
|
||||
.map(|x| x.slice(0..32));
|
||||
|
||||
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(),
|
||||
"[{} {} {}] {} - <{}> {} +{} {:?}",
|
||||
idx + 1,
|
||||
samp.chunk_id,
|
||||
samp.offset,
|
||||
samp.is_sync,
|
||||
samp.size,
|
||||
samp.start_time,
|
||||
samp.rendering_offset,
|
||||
data.as_deref()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -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
308
src/async_reader.rs
Normal 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()
|
||||
}
|
||||
}
|
30
src/error.rs
30
src/error.rs
|
@ -2,28 +2,50 @@ use thiserror::Error;
|
|||
|
||||
use crate::mp4box::BoxType;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum BoxError {
|
||||
#[error("{0}")]
|
||||
IoError(#[from] std::io::Error),
|
||||
|
||||
#[error("{0}")]
|
||||
InvalidData(&'static str),
|
||||
|
||||
#[error("{0} not found")]
|
||||
BoxNotFound(BoxType),
|
||||
|
||||
#[error("{0} and {1} not found")]
|
||||
Box2NotFound(BoxType, BoxType),
|
||||
#[error("trak[{0}] not found")]
|
||||
TrakNotFound(u32),
|
||||
|
||||
#[error("trak[{0}].{1} not found")]
|
||||
BoxInTrakNotFound(u32, BoxType),
|
||||
|
||||
#[error("traf[{0}].{1} not found")]
|
||||
BoxInTrafNotFound(u32, BoxType),
|
||||
|
||||
#[error("trak[{0}].stbl.{1} not found")]
|
||||
BoxInStblNotFound(u32, BoxType),
|
||||
|
||||
#[error("trak[{0}].stbl.{1}.entry[{2}] not found")]
|
||||
EntryInStblNotFound(u32, BoxType, u32),
|
||||
|
||||
#[error("traf[{0}].trun.{1}.entry[{2}] not found")]
|
||||
EntryInTrunNotFound(u32, BoxType, u32),
|
||||
|
||||
#[error("{0} version {1} is not supported")]
|
||||
UnsupportedBoxVersion(BoxType, u8),
|
||||
|
||||
#[error("trak[{0}] not found")]
|
||||
TrakNotFound(u32),
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error<E> {
|
||||
#[error("{0}")]
|
||||
IoError(#[from] std::io::Error),
|
||||
|
||||
#[error("box error: {0}")]
|
||||
BoxError(#[from] BoxError),
|
||||
|
||||
#[error("storage error: {0}")]
|
||||
DataStorageError(E),
|
||||
}
|
||||
|
|
678
src/file.rs
Normal file
678
src/file.rs
Normal file
|
@ -0,0 +1,678 @@
|
|||
use bytes::Bytes;
|
||||
use futures::Future;
|
||||
use std::collections::{BTreeSet, HashMap};
|
||||
use std::ops::Range;
|
||||
use std::sync::Arc;
|
||||
use tokio::io::{AsyncRead, AsyncReadExt};
|
||||
|
||||
use crate::ctts::CttsEntry;
|
||||
use crate::error::BoxError;
|
||||
use crate::stsc::StscEntry;
|
||||
use crate::stts::SttsEntry;
|
||||
use crate::{
|
||||
BlockReader, BoxHeader, BoxType, EmsgBox, Error, FtypBox, MoofBox, MoovBox, Mp4Sample,
|
||||
TrackType,
|
||||
};
|
||||
|
||||
pub trait DataStorage {
|
||||
type Error;
|
||||
type Id;
|
||||
|
||||
fn save_data(
|
||||
&mut self,
|
||||
reader: &mut (impl AsyncRead + Unpin),
|
||||
) -> impl Future<Output = Result<Self::Id, Self::Error>>;
|
||||
|
||||
fn read_data(
|
||||
&self,
|
||||
id: &Self::Id,
|
||||
range: Range<u64>,
|
||||
) -> impl Future<Output = Result<Bytes, Self::Error>>;
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum MemoryStorageError {
|
||||
#[error("IoError: {0}")]
|
||||
IoError(#[from] std::io::Error),
|
||||
|
||||
#[error("data buffer with index {0} not found")]
|
||||
DataBufferNotFound(usize),
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MemoryStorage {
|
||||
pub data: Vec<Bytes>,
|
||||
}
|
||||
|
||||
impl DataStorage for MemoryStorage {
|
||||
type Error = MemoryStorageError;
|
||||
type Id = usize;
|
||||
|
||||
#[inline]
|
||||
async fn save_data(
|
||||
&mut self,
|
||||
reader: &mut (impl AsyncRead + Unpin),
|
||||
) -> Result<Self::Id, Self::Error> {
|
||||
let mut buffer = Vec::new();
|
||||
let index = self.data.len();
|
||||
tokio::io::copy(reader, &mut buffer).await?;
|
||||
self.data.push(buffer.into());
|
||||
|
||||
Ok(index)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
async fn read_data(&self, id: &Self::Id, range: Range<u64>) -> Result<Bytes, Self::Error> {
|
||||
let buff = self
|
||||
.data
|
||||
.get(*id)
|
||||
.ok_or(MemoryStorageError::DataBufferNotFound(*id))?;
|
||||
|
||||
Ok(buff.slice(range.start as usize..range.end as usize))
|
||||
}
|
||||
}
|
||||
|
||||
enum DataBlockBody<I> {
|
||||
Memory(Bytes),
|
||||
Storage(I),
|
||||
Reader,
|
||||
}
|
||||
|
||||
pub struct DataBlock<I> {
|
||||
kind: BoxType,
|
||||
offset: u64,
|
||||
size: u64,
|
||||
buffer: DataBlockBody<I>,
|
||||
}
|
||||
|
||||
pub struct Mp4File<'a, R, S = MemoryStorage>
|
||||
where
|
||||
R: AsyncRead + Unpin,
|
||||
S: DataStorage,
|
||||
{
|
||||
pub ftyp: Option<FtypBox>,
|
||||
pub emsgs: Vec<EmsgBox>,
|
||||
pub tracks: HashMap<u32, Mp4Track>,
|
||||
pub reader: &'a mut R,
|
||||
pub offsets: BTreeSet<u64>,
|
||||
pub data_blocks: Vec<DataBlock<S::Id>>,
|
||||
pub data_storage: S,
|
||||
}
|
||||
|
||||
impl<'a, R> Mp4File<'a, R, MemoryStorage>
|
||||
where
|
||||
R: AsyncRead + Unpin + 'a,
|
||||
{
|
||||
pub fn new(reader: &'a mut R) -> Self {
|
||||
Self {
|
||||
ftyp: None,
|
||||
emsgs: Vec::new(),
|
||||
tracks: HashMap::new(),
|
||||
reader,
|
||||
offsets: BTreeSet::new(),
|
||||
data_blocks: Vec::new(),
|
||||
data_storage: MemoryStorage::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, R, S> Mp4File<'a, R, S>
|
||||
where
|
||||
R: AsyncRead + Unpin + 'a,
|
||||
S: DataStorage,
|
||||
{
|
||||
pub fn with_storage(reader: &'a mut R, data_storage: S) -> Self {
|
||||
Self {
|
||||
ftyp: None,
|
||||
emsgs: Vec::new(),
|
||||
tracks: HashMap::new(),
|
||||
reader,
|
||||
offsets: BTreeSet::new(),
|
||||
data_blocks: Vec::new(),
|
||||
data_storage,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn read_header(&mut self) -> Result<bool, Error<S::Error>> {
|
||||
let mut buff = Vec::with_capacity(8192);
|
||||
let mut got_moov = false;
|
||||
let mut offset = 0u64;
|
||||
|
||||
while let Some(BoxHeader { kind, size: s }) =
|
||||
BoxHeader::read(&mut self.reader, &mut offset).await?
|
||||
{
|
||||
match kind {
|
||||
BoxType::FtypBox => {
|
||||
println!("ftyp");
|
||||
|
||||
if buff.len() < s as usize {
|
||||
buff.resize(s as usize, 0);
|
||||
}
|
||||
|
||||
self.reader.read_exact(&mut buff[0..s as usize]).await?;
|
||||
offset += s;
|
||||
|
||||
self.ftyp = Some(FtypBox::read_block(&mut &buff[0..s as usize])?);
|
||||
}
|
||||
|
||||
BoxType::MoovBox => {
|
||||
println!("moov");
|
||||
if buff.len() < s as usize {
|
||||
buff.resize(s as usize, 0);
|
||||
}
|
||||
|
||||
self.reader.read_exact(&mut buff[0..s as usize]).await?;
|
||||
offset += s;
|
||||
|
||||
got_moov = true;
|
||||
self.set_moov(MoovBox::read_block(&mut &buff[0..s as usize])?)?;
|
||||
}
|
||||
|
||||
BoxType::MoofBox => {
|
||||
println!("moof");
|
||||
|
||||
if buff.len() < s as usize {
|
||||
buff.resize(s as usize, 0);
|
||||
}
|
||||
|
||||
let begin_offset = offset;
|
||||
self.reader.read_exact(&mut buff[0..s as usize]).await?;
|
||||
offset += s;
|
||||
|
||||
self.add_moof(
|
||||
begin_offset,
|
||||
MoofBox::read_block(&mut &buff[0..s as usize])?,
|
||||
)?;
|
||||
}
|
||||
|
||||
BoxType::EmsgBox => {
|
||||
println!("emsg");
|
||||
|
||||
if buff.len() < s as usize {
|
||||
buff.resize(s as usize, 0);
|
||||
}
|
||||
|
||||
self.reader.read_exact(&mut buff[0..s as usize]).await?;
|
||||
offset += s;
|
||||
|
||||
self.emsgs
|
||||
.push(EmsgBox::read_block(&mut &buff[0..s as usize])?);
|
||||
}
|
||||
|
||||
BoxType::MdatBox => {
|
||||
println!("mdat");
|
||||
self.save_box(BoxType::MdatBox, s, offset).await?;
|
||||
offset += s;
|
||||
}
|
||||
|
||||
bt => {
|
||||
println!("{}", bt);
|
||||
|
||||
self.skip_box(bt, s).await?;
|
||||
offset += s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(got_moov)
|
||||
}
|
||||
|
||||
async fn skip_box(&mut self, bt: BoxType, size: u64) -> Result<(), Error<S::Error>> {
|
||||
println!("skip {:?}", bt);
|
||||
tokio::io::copy(&mut (&mut self.reader).take(size), &mut tokio::io::empty()).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn save_box(
|
||||
&mut self,
|
||||
kind: BoxType,
|
||||
size: u64,
|
||||
offset: u64,
|
||||
) -> Result<(), Error<S::Error>> {
|
||||
println!("data_block {:?} {} - {}", kind, offset, offset + size);
|
||||
|
||||
if size < 128 * 1024 * 1024 {
|
||||
let mut buffer = Vec::new();
|
||||
tokio::io::copy(&mut self.reader.take(size), &mut buffer).await?;
|
||||
|
||||
self.data_blocks.push(DataBlock {
|
||||
kind,
|
||||
offset,
|
||||
size,
|
||||
buffer: DataBlockBody::Memory(buffer.into()),
|
||||
});
|
||||
} else {
|
||||
let id = self
|
||||
.data_storage
|
||||
.save_data(&mut self.reader.take(size))
|
||||
.await
|
||||
.map_err(Error::DataStorageError)?;
|
||||
|
||||
self.data_blocks.push(DataBlock {
|
||||
kind,
|
||||
offset,
|
||||
size,
|
||||
buffer: DataBlockBody::Storage(id),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_moov(&mut self, moov: MoovBox) -> Result<(), Error<S::Error>> {
|
||||
for trak in moov.traks {
|
||||
self.tracks
|
||||
.insert(trak.tkhd.track_id, Mp4Track::new(trak, &mut self.offsets)?);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_moof(&mut self, offset: u64, moof: MoofBox) -> Result<(), Error<S::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(offset, moof.mfhd.sequence_number, traf, &mut self.offsets)
|
||||
} else {
|
||||
return Err(BoxError::TrakNotFound(track_id).into());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn read_sample_data(
|
||||
&self,
|
||||
track_id: u32,
|
||||
sample_idx: usize,
|
||||
) -> Result<Option<Bytes>, Error<S::Error>> {
|
||||
let Some(track) = self.tracks.get(&track_id) else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let Some(sample) = track.samples.get(sample_idx) else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
for block in &self.data_blocks {
|
||||
let range = block.offset..block.offset + block.size;
|
||||
|
||||
if range.contains(&sample.offset) {
|
||||
let offset = sample.offset - block.offset;
|
||||
|
||||
return Ok(Some(match &block.buffer {
|
||||
DataBlockBody::Storage(id) => self
|
||||
.data_storage
|
||||
.read_data(id, offset..offset + sample.size as u64)
|
||||
.await
|
||||
.map_err(Error::DataStorageError)?,
|
||||
|
||||
DataBlockBody::Memory(mem) => {
|
||||
mem.slice(offset as usize..offset as usize + sample.size as usize)
|
||||
}
|
||||
DataBlockBody::Reader => todo!(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub fn into_streams<T: AsRef<[u32]>>(
|
||||
self,
|
||||
tracks: T,
|
||||
) -> impl Iterator<
|
||||
Item = (
|
||||
u32,
|
||||
impl futures::Stream<Item = Result<Mp4Sample, Error<S::Error>>> + 'a,
|
||||
),
|
||||
>
|
||||
where
|
||||
S::Error: 'a,
|
||||
{
|
||||
let storage = Arc::new(self.data_storage);
|
||||
let data_blocks = Arc::new(self.data_blocks);
|
||||
|
||||
self.tracks
|
||||
.into_iter()
|
||||
.filter_map(move |(track_id, track)| {
|
||||
if !tracks.as_ref().contains(&track_id) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let storage = storage.clone();
|
||||
let data_blocks = data_blocks.clone();
|
||||
|
||||
Some((
|
||||
track_id,
|
||||
async_stream::stream! {
|
||||
for samp_offset in track.samples {
|
||||
yield Ok(Mp4Sample {
|
||||
start_time: samp_offset.start_time,
|
||||
duration: samp_offset.duration,
|
||||
rendering_offset: samp_offset.rendering_offset,
|
||||
is_sync: samp_offset.is_sync,
|
||||
bytes: Bytes::new(),
|
||||
})
|
||||
}
|
||||
},
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Mp4SampleOffset {
|
||||
pub offset: u64,
|
||||
pub size: u32,
|
||||
pub duration: u32,
|
||||
pub start_time: u64,
|
||||
pub rendering_offset: i32,
|
||||
pub is_sync: bool,
|
||||
pub chunk_id: u32,
|
||||
}
|
||||
|
||||
pub struct Mp4Track {
|
||||
pub track_id: u32,
|
||||
pub duration: u64,
|
||||
pub samples: Vec<Mp4SampleOffset>,
|
||||
pub tkhd: crate::TkhdBox,
|
||||
pub mdia: crate::MdiaBox,
|
||||
}
|
||||
|
||||
impl Mp4Track {
|
||||
fn new(trak: crate::TrakBox, offsets: &mut BTreeSet<u64>) -> Result<Mp4Track, BoxError> {
|
||||
let default_sample_duration = 1024;
|
||||
let mut total_duration = 0;
|
||||
let mut samples = Vec::with_capacity(trak.mdia.minf.stbl.stsz.sample_count as _);
|
||||
let stco = &trak.mdia.minf.stbl.stco;
|
||||
let co64 = &trak.mdia.minf.stbl.co64;
|
||||
|
||||
let mb_iter1 = stco.clone().map(IntoIterator::into_iter);
|
||||
let mb_iter2 = co64.clone().map(IntoIterator::into_iter);
|
||||
|
||||
if let Some(stco) = co64.as_ref().map(IntoIterator::into_iter) {
|
||||
offsets.extend(stco);
|
||||
}
|
||||
|
||||
if let Some(stco) = stco.as_ref().map(IntoIterator::into_iter) {
|
||||
offsets.extend(stco);
|
||||
}
|
||||
|
||||
let chunk_iter = chunk_iter(
|
||||
trak.mdia.minf.stbl.stsc.entries.clone().into_iter(),
|
||||
mb_iter1
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.chain(mb_iter2.into_iter().flatten()),
|
||||
);
|
||||
|
||||
let mut sample_chunk_iter = run_len_iter(chunk_iter);
|
||||
|
||||
let sync_iter_peek = trak
|
||||
.mdia
|
||||
.minf
|
||||
.stbl
|
||||
.stss
|
||||
.as_ref()
|
||||
.map(|x| x.entries.iter().copied().peekable());
|
||||
|
||||
let mut sync_iter =
|
||||
(1..=trak.mdia.minf.stbl.stsz.sample_count).scan(sync_iter_peek, |iter, idx| {
|
||||
let iter = iter.as_mut()?;
|
||||
|
||||
Some(if idx == iter.peek().copied().unwrap_or(u32::MAX) {
|
||||
iter.next();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
})
|
||||
});
|
||||
|
||||
let mut ts_deltas =
|
||||
run_len_iter(trak.mdia.minf.stbl.stts.entries.clone().into_iter().chain(
|
||||
std::iter::once(SttsEntry {
|
||||
sample_count: u32::MAX,
|
||||
sample_delta: default_sample_duration,
|
||||
}),
|
||||
))
|
||||
.scan(0u64, |s, delta| {
|
||||
let out = *s;
|
||||
*s += delta as u64;
|
||||
Some((out, delta))
|
||||
});
|
||||
|
||||
let mut rend_offset_iter = run_len_iter(
|
||||
trak.mdia
|
||||
.minf
|
||||
.stbl
|
||||
.ctts
|
||||
.clone()
|
||||
.into_iter()
|
||||
.flat_map(|x| x.entries.into_iter()),
|
||||
);
|
||||
|
||||
let mut sample_offset = 0;
|
||||
let mut curr_chunk_index = 0;
|
||||
let mut prev_size = 0;
|
||||
|
||||
for sample_idx in 0..trak.mdia.minf.stbl.stsz.sample_count as usize {
|
||||
let (start_time, duration) = ts_deltas.next().unwrap();
|
||||
let chunk = sample_chunk_iter.next().unwrap();
|
||||
let size = *trak
|
||||
.mdia
|
||||
.minf
|
||||
.stbl
|
||||
.stsz
|
||||
.sample_sizes
|
||||
.get(sample_idx)
|
||||
.unwrap_or(&trak.mdia.minf.stbl.stsz.sample_size);
|
||||
|
||||
if curr_chunk_index != chunk.index {
|
||||
curr_chunk_index = chunk.index;
|
||||
sample_offset = 0;
|
||||
} else {
|
||||
sample_offset += prev_size;
|
||||
}
|
||||
|
||||
prev_size = size;
|
||||
total_duration = start_time + duration as u64;
|
||||
samples.push(Mp4SampleOffset {
|
||||
chunk_id: chunk.index,
|
||||
offset: chunk.offset + sample_offset as u64,
|
||||
size,
|
||||
duration,
|
||||
start_time,
|
||||
rendering_offset: rend_offset_iter.next().unwrap_or(0),
|
||||
is_sync: sync_iter.next().unwrap_or(true),
|
||||
})
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
track_id: trak.tkhd.track_id,
|
||||
tkhd: trak.tkhd,
|
||||
mdia: trak.mdia,
|
||||
samples,
|
||||
duration: total_duration,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn track_type(&self) -> TrackType {
|
||||
TrackType::from(&self.mdia.hdlr.handler_type)
|
||||
}
|
||||
|
||||
pub(crate) fn add_traf(
|
||||
&mut self,
|
||||
base_moof_offset: u64,
|
||||
chunk_index: u32,
|
||||
traf: crate::TrafBox,
|
||||
offsets: &mut BTreeSet<u64>,
|
||||
) {
|
||||
let base_data_offset = traf.tfhd.base_data_offset.unwrap_or(base_moof_offset);
|
||||
offsets.insert(base_data_offset);
|
||||
|
||||
let default_sample_size = traf.tfhd.default_sample_size.unwrap_or(0);
|
||||
let default_sample_duration = traf.tfhd.default_sample_duration.unwrap_or(0);
|
||||
let base_start_time = traf
|
||||
.tfdt
|
||||
.map(|x| x.base_media_decode_time)
|
||||
.or_else(|| {
|
||||
self.samples
|
||||
.last()
|
||||
.map(|x| x.start_time + x.duration as u64)
|
||||
})
|
||||
.unwrap_or(0);
|
||||
|
||||
let Some(trun) = traf.trun else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut sample_offset = 0u64;
|
||||
let mut start_time_offset = 0u64;
|
||||
for sample_idx in 0..trun.sample_count as usize {
|
||||
let size = trun
|
||||
.sample_sizes
|
||||
.get(sample_idx)
|
||||
.copied()
|
||||
.unwrap_or(default_sample_size);
|
||||
|
||||
let duration = trun
|
||||
.sample_durations
|
||||
.get(sample_idx)
|
||||
.copied()
|
||||
.unwrap_or(default_sample_duration);
|
||||
|
||||
let rendering_offset = trun.sample_cts.get(sample_idx).copied().unwrap_or(0) as i32;
|
||||
|
||||
self.samples.push(Mp4SampleOffset {
|
||||
chunk_id: chunk_index,
|
||||
offset: (base_data_offset as i64
|
||||
+ trun.data_offset.map(|x| x as i64).unwrap_or(0)
|
||||
+ sample_offset as i64) as u64,
|
||||
size,
|
||||
duration,
|
||||
start_time: base_start_time + start_time_offset,
|
||||
rendering_offset,
|
||||
is_sync: sample_idx == 0,
|
||||
});
|
||||
|
||||
sample_offset += size as u64;
|
||||
start_time_offset += duration as u64;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait RunLenghtItem {
|
||||
type Value: Clone;
|
||||
|
||||
fn count(&self) -> usize;
|
||||
fn value(&self) -> Self::Value;
|
||||
}
|
||||
|
||||
impl<T: Clone> RunLenghtItem for (usize, T) {
|
||||
type Value = T;
|
||||
|
||||
fn count(&self) -> usize {
|
||||
self.0
|
||||
}
|
||||
fn value(&self) -> Self::Value {
|
||||
self.1.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl RunLenghtItem for CttsEntry {
|
||||
type Value = i32;
|
||||
|
||||
fn count(&self) -> usize {
|
||||
self.sample_count as _
|
||||
}
|
||||
|
||||
fn value(&self) -> Self::Value {
|
||||
self.sample_offset
|
||||
}
|
||||
}
|
||||
|
||||
impl RunLenghtItem for SttsEntry {
|
||||
type Value = u32;
|
||||
|
||||
fn count(&self) -> usize {
|
||||
self.sample_count as _
|
||||
}
|
||||
|
||||
fn value(&self) -> Self::Value {
|
||||
self.sample_delta
|
||||
}
|
||||
}
|
||||
|
||||
fn run_len_iter<E: RunLenghtItem, I: IntoIterator<Item = E>>(
|
||||
iter: I,
|
||||
) -> impl Iterator<Item = E::Value> {
|
||||
let mut iter = iter.into_iter();
|
||||
let mut value = None::<E::Value>;
|
||||
let mut repeat = 0;
|
||||
std::iter::from_fn(move || loop {
|
||||
if let Some(val) = &value {
|
||||
if repeat > 0 {
|
||||
repeat -= 1;
|
||||
return Some(val.clone());
|
||||
} else {
|
||||
value = None;
|
||||
}
|
||||
}
|
||||
|
||||
let x = iter.next()?;
|
||||
value = Some(x.value());
|
||||
repeat = x.count();
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Chunk {
|
||||
pub index: u32,
|
||||
pub offset: u64,
|
||||
pub samples_per_chunk: u32,
|
||||
pub sample_description_index: u32,
|
||||
}
|
||||
|
||||
impl RunLenghtItem for Chunk {
|
||||
type Value = Chunk;
|
||||
|
||||
fn count(&self) -> usize {
|
||||
self.samples_per_chunk as _
|
||||
}
|
||||
|
||||
fn value(&self) -> Self::Value {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
fn chunk_iter(
|
||||
mut stsc: impl Iterator<Item = StscEntry>,
|
||||
stco: impl Iterator<Item = u64>,
|
||||
) -> impl Iterator<Item = Chunk> {
|
||||
let mut prev = stsc.next().unwrap_or(StscEntry {
|
||||
first_chunk: 1,
|
||||
samples_per_chunk: u32::MAX,
|
||||
sample_description_index: 1,
|
||||
first_sample: 1,
|
||||
});
|
||||
let mut curr = stsc.next();
|
||||
|
||||
stco.enumerate().map(move |(idx, offset)| {
|
||||
if let Some(c) = &curr {
|
||||
if idx + 1 >= c.first_chunk as usize {
|
||||
prev = *c;
|
||||
curr = stsc.next();
|
||||
}
|
||||
}
|
||||
|
||||
Chunk {
|
||||
index: idx as _,
|
||||
offset,
|
||||
samples_per_chunk: prev.samples_per_chunk,
|
||||
sample_description_index: prev.sample_description_index,
|
||||
}
|
||||
})
|
||||
}
|
139
src/header.rs
Normal file
139
src/header.rs
Normal 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()
|
||||
}
|
||||
}
|
27
src/lib.rs
27
src/lib.rs
|
@ -65,13 +65,11 @@
|
|||
//! [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;
|
||||
use error::BoxError;
|
||||
pub use error::Error;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
pub type Result<T> = std::result::Result<T, BoxError>;
|
||||
|
||||
mod types;
|
||||
pub use types::*;
|
||||
|
@ -79,18 +77,23 @@ 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};
|
||||
pub use file::*;
|
||||
// mod async_reader;
|
||||
// pub use async_reader::{AsyncMp4Reader, Mp4Header};
|
||||
|
||||
mod writer;
|
||||
pub use writer::{Mp4Config, Mp4Writer};
|
||||
|
||||
pub fn read_mp4(f: File) -> Result<Mp4Reader<BufReader<File>>> {
|
||||
let size = f.metadata()?.len();
|
||||
let reader = BufReader::new(f);
|
||||
let mp4 = reader::Mp4Reader::read_header(reader, size)?;
|
||||
Ok(mp4)
|
||||
}
|
||||
// pub async fn read_mp4(f: File) -> Result<Mp4Reader<BufReader<File>>> {
|
||||
// let size = f.metadata()?.len();
|
||||
// let reader = BufReader::new(f);
|
||||
// let mp4 = async_reader::Mp4AsyncReader::read_header(reader, size)?;
|
||||
// Ok(mp4)
|
||||
// }
|
||||
|
|
|
@ -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,46 +78,34 @@ 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 {
|
||||
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,
|
||||
|
@ -127,12 +113,12 @@ impl<R: Read + Seek> ReadBox<&mut R> for Avc1Box {
|
|||
vertresolution,
|
||||
frame_count,
|
||||
depth,
|
||||
avcc,
|
||||
});
|
||||
} else {
|
||||
skip_bytes_to(reader, current + s)?;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
use byteorder::{BigEndian, WriteBytesExt};
|
||||
use serde::Serialize;
|
||||
use std::io::{Read, Seek, Write};
|
||||
use std::io::Write;
|
||||
use std::mem::size_of;
|
||||
|
||||
use crate::mp4box::*;
|
||||
|
@ -14,6 +14,26 @@ pub struct Co64Box {
|
|||
pub entries: Vec<u64>,
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a Co64Box {
|
||||
type Item = u64;
|
||||
type IntoIter = std::iter::Copied<std::slice::Iter<'a, u64>>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.entries.iter().copied()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for Co64Box {
|
||||
type Item = u64;
|
||||
type IntoIter = std::vec::IntoIter<u64>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.entries.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl Co64Box {
|
||||
pub fn get_type(&self) -> BoxType {
|
||||
BoxType::Co64Box
|
||||
|
@ -25,9 +45,7 @@ impl Co64Box {
|
|||
}
|
||||
|
||||
impl Mp4Box for Co64Box {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
const TYPE: BoxType = BoxType::Co64Box;
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
|
@ -43,46 +61,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
|
||||
{
|
||||
return Err(Error::InvalidData(
|
||||
let entry_count = reader.get_u32();
|
||||
if entry_count as usize > reader.remaining() / entry_size {
|
||||
return Err(BoxError::InvalidData(
|
||||
"co64 entry_count indicates more entries than could fit in the box",
|
||||
));
|
||||
}
|
||||
|
||||
let mut entries = Vec::with_capacity(entry_count as usize);
|
||||
for _i in 0..entry_count {
|
||||
let chunk_offset = reader.read_u64::<BigEndian>()?;
|
||||
let chunk_offset = reader.get_u64();
|
||||
entries.push(chunk_offset);
|
||||
}
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
|
||||
Ok(Co64Box {
|
||||
version,
|
||||
flags,
|
||||
entries,
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint() -> usize {
|
||||
8
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for Co64Box {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
BoxHeader::new(Self::TYPE, size).write(writer)?;
|
||||
|
||||
write_box_header_ext(writer, self.version, self.flags)?;
|
||||
|
||||
|
@ -99,7 +111,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 +123,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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
use byteorder::{BigEndian, WriteBytesExt};
|
||||
use serde::Serialize;
|
||||
use std::io::{Read, Seek, Write};
|
||||
use std::io::Write;
|
||||
use std::mem::size_of;
|
||||
|
||||
use crate::mp4box::*;
|
||||
|
@ -24,16 +24,14 @@ impl CttsBox {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize)]
|
||||
pub struct CttsEntry {
|
||||
pub sample_count: u32,
|
||||
pub sample_offset: i32,
|
||||
}
|
||||
|
||||
impl Mp4Box for CttsBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
const TYPE: BoxType = BoxType::CttsBox;
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
|
@ -49,50 +47,45 @@ impl Mp4Box for CttsBox {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for CttsBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
impl BlockReader for CttsBox {
|
||||
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
|
||||
let (version, flags) = read_box_header_ext(reader);
|
||||
|
||||
let (version, flags) = read_box_header_ext(reader)?;
|
||||
|
||||
let header_size = HEADER_SIZE + HEADER_EXT_SIZE;
|
||||
let entry_count = reader.read_u32::<BigEndian>()?;
|
||||
let entry_count = reader.get_u32();
|
||||
let entry_size = size_of::<u32>() + size_of::<i32>(); // sample_count + sample_offset
|
||||
// (sample_offset might be a u32, but the size is the same.)
|
||||
let other_size = size_of::<i32>(); // entry_count
|
||||
if u64::from(entry_count)
|
||||
> size
|
||||
.saturating_sub(header_size)
|
||||
.saturating_sub(other_size as u64)
|
||||
/ entry_size as u64
|
||||
{
|
||||
return Err(Error::InvalidData(
|
||||
|
||||
if entry_count as usize > reader.remaining() / entry_size {
|
||||
return Err(BoxError::InvalidData(
|
||||
"ctts entry_count indicates more entries than could fit in the box",
|
||||
));
|
||||
}
|
||||
|
||||
let mut entries = Vec::with_capacity(entry_count as usize);
|
||||
for _ in 0..entry_count {
|
||||
let entry = CttsEntry {
|
||||
sample_count: reader.read_u32::<BigEndian>()?,
|
||||
sample_offset: reader.read_i32::<BigEndian>()?,
|
||||
sample_count: reader.get_u32(),
|
||||
sample_offset: reader.get_i32(),
|
||||
};
|
||||
entries.push(entry);
|
||||
}
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
|
||||
Ok(CttsBox {
|
||||
version,
|
||||
flags,
|
||||
entries,
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint() -> usize {
|
||||
8
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for CttsBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
BoxHeader::new(Self::TYPE, size).write(writer)?;
|
||||
|
||||
write_box_header_ext(writer, self.version, self.flags)?;
|
||||
|
||||
|
@ -110,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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
url = reader.try_find_box()?;
|
||||
}
|
||||
|
||||
// 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()?;
|
||||
}
|
||||
|
||||
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)?;
|
||||
|
||||
|
|
|
@ -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)?;
|
||||
|
||||
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",
|
||||
));
|
||||
impl BlockReader for EdtsBox {
|
||||
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
|
||||
Ok(EdtsBox {
|
||||
elst: reader.try_find_box::<ElstBox>()?,
|
||||
})
|
||||
}
|
||||
|
||||
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)?;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
use byteorder::{BigEndian, WriteBytesExt};
|
||||
use serde::Serialize;
|
||||
use std::io::{Read, Seek, Write};
|
||||
use std::io::Write;
|
||||
use std::mem::size_of;
|
||||
|
||||
use crate::mp4box::*;
|
||||
|
@ -39,9 +39,7 @@ impl ElstBox {
|
|||
}
|
||||
|
||||
impl Mp4Box for ElstBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
const TYPE: BoxType = BoxType::ElstBox;
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
|
@ -57,15 +55,11 @@ impl Mp4Box for ElstBox {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for ElstBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
impl BlockReader for ElstBox {
|
||||
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
|
||||
let (version, flags) = read_box_header_ext(reader);
|
||||
|
||||
let (version, flags) = read_box_header_ext(reader)?;
|
||||
|
||||
let header_size = HEADER_SIZE + HEADER_EXT_SIZE;
|
||||
let entry_count = reader.read_u32::<BigEndian>()?;
|
||||
let other_size = size_of::<i32>(); // entry_count
|
||||
let entry_count = reader.get_u32();
|
||||
let entry_size = {
|
||||
let mut entry_size = 0;
|
||||
entry_size += if version == 1 {
|
||||
|
@ -73,56 +67,49 @@ impl<R: Read + Seek> ReadBox<&mut R> for ElstBox {
|
|||
} else {
|
||||
size_of::<u32>() + size_of::<i32>() // segment_duration + media_time
|
||||
};
|
||||
|
||||
entry_size += size_of::<i16>() + size_of::<i16>(); // media_rate_integer + media_rate_fraction
|
||||
entry_size
|
||||
};
|
||||
if u64::from(entry_count)
|
||||
> size
|
||||
.saturating_sub(header_size)
|
||||
.saturating_sub(other_size as u64)
|
||||
/ entry_size as u64
|
||||
{
|
||||
return Err(Error::InvalidData(
|
||||
|
||||
if entry_count as usize > reader.remaining() / entry_size {
|
||||
return Err(BoxError::InvalidData(
|
||||
"elst entry_count indicates more entries than could fit in the box",
|
||||
));
|
||||
}
|
||||
|
||||
let mut entries = Vec::with_capacity(entry_count as usize);
|
||||
for _ in 0..entry_count {
|
||||
let (segment_duration, media_time) = if version == 1 {
|
||||
(
|
||||
reader.read_u64::<BigEndian>()?,
|
||||
reader.read_u64::<BigEndian>()?,
|
||||
)
|
||||
(reader.get_u64(), reader.get_u64())
|
||||
} else {
|
||||
(
|
||||
reader.read_u32::<BigEndian>()? as u64,
|
||||
reader.read_u32::<BigEndian>()? as u64,
|
||||
)
|
||||
(reader.get_u32() as u64, reader.get_u32() as u64)
|
||||
};
|
||||
|
||||
let entry = ElstEntry {
|
||||
entries.push(ElstEntry {
|
||||
segment_duration,
|
||||
media_time,
|
||||
media_rate: reader.read_u16::<BigEndian>()?,
|
||||
media_rate_fraction: reader.read_u16::<BigEndian>()?,
|
||||
};
|
||||
entries.push(entry);
|
||||
media_rate: reader.get_u16(),
|
||||
media_rate_fraction: reader.get_u16(),
|
||||
});
|
||||
}
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
|
||||
Ok(ElstBox {
|
||||
version,
|
||||
flags,
|
||||
entries,
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint() -> usize {
|
||||
8
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for ElstBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
BoxHeader::new(Self::TYPE, size).write(writer)?;
|
||||
|
||||
write_box_header_ext(writer, self.version, self.flags)?;
|
||||
|
||||
|
@ -147,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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use std::ffi::CStr;
|
||||
use std::io::{Read, Seek, Write};
|
||||
use std::io::Write;
|
||||
|
||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
use byteorder::{BigEndian, WriteBytesExt};
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::mp4box::*;
|
||||
|
@ -39,9 +38,7 @@ impl EmsgBox {
|
|||
}
|
||||
|
||||
impl Mp4Box for EmsgBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
BoxType::EmsgBox
|
||||
}
|
||||
const TYPE: BoxType = BoxType::EmsgBox;
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
Self::size_without_message(self.version, &self.scheme_id_uri, &self.value)
|
||||
|
@ -58,10 +55,9 @@ impl Mp4Box for EmsgBox {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for EmsgBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
let (version, flags) = read_box_header_ext(reader)?;
|
||||
impl BlockReader for EmsgBox {
|
||||
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
|
||||
let (version, flags) = read_box_header_ext(reader);
|
||||
|
||||
let (
|
||||
timescale,
|
||||
|
@ -73,38 +69,31 @@ impl<R: Read + Seek> ReadBox<&mut R> for EmsgBox {
|
|||
value,
|
||||
) = match version {
|
||||
0 => {
|
||||
let scheme_id_uri = read_null_terminated_utf8_string(reader)?;
|
||||
let value = read_null_terminated_utf8_string(reader)?;
|
||||
let scheme_id_uri = reader.get_null_terminated_string();
|
||||
let value = reader.get_null_terminated_string();
|
||||
|
||||
(
|
||||
reader.read_u32::<BigEndian>()?,
|
||||
reader.get_u32(),
|
||||
None,
|
||||
Some(reader.read_u32::<BigEndian>()?),
|
||||
reader.read_u32::<BigEndian>()?,
|
||||
reader.read_u32::<BigEndian>()?,
|
||||
Some(reader.get_u32()),
|
||||
reader.get_u32(),
|
||||
reader.get_u32(),
|
||||
scheme_id_uri,
|
||||
value,
|
||||
)
|
||||
}
|
||||
1 => (
|
||||
reader.read_u32::<BigEndian>()?,
|
||||
Some(reader.read_u64::<BigEndian>()?),
|
||||
reader.get_u32(),
|
||||
Some(reader.get_u64()),
|
||||
None,
|
||||
reader.read_u32::<BigEndian>()?,
|
||||
reader.read_u32::<BigEndian>()?,
|
||||
read_null_terminated_utf8_string(reader)?,
|
||||
read_null_terminated_utf8_string(reader)?,
|
||||
reader.get_u32(),
|
||||
reader.get_u32(),
|
||||
reader.get_null_terminated_string(),
|
||||
reader.get_null_terminated_string(),
|
||||
),
|
||||
_ => return Err(Error::InvalidData("version must be 0 or 1")),
|
||||
_ => return Err(BoxError::InvalidData("version must be 0 or 1")),
|
||||
};
|
||||
|
||||
let message_size = size - Self::size_without_message(version, &scheme_id_uri, &value);
|
||||
let mut message_data = Vec::with_capacity(message_size as usize);
|
||||
for _ in 0..message_size {
|
||||
message_data.push(reader.read_u8()?);
|
||||
}
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
|
||||
Ok(EmsgBox {
|
||||
version,
|
||||
flags,
|
||||
|
@ -115,15 +104,19 @@ impl<R: Read + Seek> ReadBox<&mut R> for EmsgBox {
|
|||
id,
|
||||
scheme_id_uri,
|
||||
value,
|
||||
message_data,
|
||||
message_data: reader.collect(reader.remaining())?,
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint() -> usize {
|
||||
22
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for EmsgBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
BoxHeader::new(Self::TYPE, size).write(writer)?;
|
||||
|
||||
write_box_header_ext(writer, self.version, self.flags)?;
|
||||
match self.version {
|
||||
|
@ -143,7 +136,7 @@ impl<W: Write> WriteBox<&mut W> for EmsgBox {
|
|||
write_null_terminated_str(writer, &self.scheme_id_uri)?;
|
||||
write_null_terminated_str(writer, &self.value)?;
|
||||
}
|
||||
_ => return Err(Error::InvalidData("version must be 0 or 1")),
|
||||
_ => return Err(BoxError::InvalidData("version must be 0 or 1")),
|
||||
}
|
||||
|
||||
for &byte in &self.message_data {
|
||||
|
@ -154,22 +147,6 @@ impl<W: Write> WriteBox<&mut W> for EmsgBox {
|
|||
}
|
||||
}
|
||||
|
||||
fn read_null_terminated_utf8_string<R: Read + Seek>(reader: &mut R) -> Result<String> {
|
||||
let mut bytes = Vec::new();
|
||||
loop {
|
||||
let byte = reader.read_u8()?;
|
||||
bytes.push(byte);
|
||||
if byte == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if let Ok(str) = unsafe { CStr::from_bytes_with_nul_unchecked(&bytes) }.to_str() {
|
||||
Ok(str.to_string())
|
||||
} else {
|
||||
Err(Error::InvalidData("invalid utf8"))
|
||||
}
|
||||
}
|
||||
|
||||
fn write_null_terminated_str<W: Write>(writer: &mut W, string: &str) -> Result<()> {
|
||||
for byte in string.bytes() {
|
||||
writer.write_u8(byte)?;
|
||||
|
@ -180,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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,38 +78,32 @@ 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());
|
||||
|
||||
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,
|
||||
|
@ -121,18 +113,19 @@ impl<R: Read + Seek> ReadBox<&mut R> for Hev1Box {
|
|||
vertresolution,
|
||||
frame_count,
|
||||
depth,
|
||||
hvcc,
|
||||
hvcc: reader.find_box::<HvcCBox>()?,
|
||||
})
|
||||
} else {
|
||||
Err(Error::InvalidData("hvcc not found"))
|
||||
}
|
||||
|
||||
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(BoxError::InvalidData(""));
|
||||
}
|
||||
|
||||
let mut arrays = Vec::with_capacity(num_of_arrays as _);
|
||||
|
||||
for _ in 0..num_of_arrays {
|
||||
let params = reader.read_u8()?;
|
||||
let num_nalus = reader.read_u16::<BigEndian>()?;
|
||||
let params = reader.get_u8();
|
||||
let num_nalus = reader.get_u16();
|
||||
|
||||
if reader.remaining() < num_nalus as usize * 2 {
|
||||
return Err(BoxError::InvalidData(""));
|
||||
}
|
||||
|
||||
let mut nalus = Vec::with_capacity(num_nalus as usize);
|
||||
|
||||
for _ in 0..num_nalus {
|
||||
let size = reader.read_u16::<BigEndian>()?;
|
||||
let mut data = vec![0; size as usize];
|
||||
let size = reader.get_u16();
|
||||
|
||||
reader.read_exact(&mut data)?;
|
||||
|
||||
nalus.push(HvcCArrayNalu { size, data })
|
||||
nalus.push(HvcCArrayNalu {
|
||||
size,
|
||||
data: reader.collect(size as _)?,
|
||||
})
|
||||
}
|
||||
|
||||
arrays.push(HvcCArray {
|
||||
|
@ -313,12 +315,16 @@ impl<R: Read + Seek> ReadBox<&mut R> for HvcCBox {
|
|||
arrays,
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint() -> usize {
|
||||
23
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for HvcCBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
BoxHeader::new(Self::TYPE, size).write(writer)?;
|
||||
|
||||
writer.write_u8(self.configuration_version)?;
|
||||
let general_profile_space = (self.general_profile_space & 0b11) << 6;
|
||||
|
@ -363,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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)?);
|
||||
if let Some(day) = bx.inner.try_find_box::<DataBox>()? {
|
||||
items.insert(MetadataKey::Year, day);
|
||||
}
|
||||
}
|
||||
|
||||
BoxType::CovrBox => {
|
||||
items.insert(MetadataKey::Poster, IlstItemBox::read_box(reader, s)?);
|
||||
if let Some(cover) = bx.inner.try_find_box::<DataBox>()? {
|
||||
items.insert(MetadataKey::Poster, cover);
|
||||
}
|
||||
}
|
||||
|
||||
BoxType::DescBox => {
|
||||
items.insert(MetadataKey::Summary, IlstItemBox::read_box(reader, s)?);
|
||||
}
|
||||
_ => {
|
||||
// XXX warn!()
|
||||
skip_box(reader, s)?;
|
||||
if let Some(summary) = bx.inner.try_find_box::<DataBox>()? {
|
||||
items.insert(MetadataKey::Summary, summary);
|
||||
}
|
||||
}
|
||||
|
||||
current = reader.stream_position()?;
|
||||
_ => 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 {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
use byteorder::{BigEndian, WriteBytesExt};
|
||||
use serde::Serialize;
|
||||
use std::char::{decode_utf16, REPLACEMENT_CHARACTER};
|
||||
use std::io::{Read, Seek, Write};
|
||||
use std::io::Write;
|
||||
|
||||
use crate::mp4box::*;
|
||||
|
||||
|
@ -49,9 +49,7 @@ impl Default for MdhdBox {
|
|||
}
|
||||
|
||||
impl Mp4Box for MdhdBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
const TYPE: BoxType = BoxType::MdhdBox;
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
|
@ -70,33 +68,30 @@ impl Mp4Box for MdhdBox {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for MdhdBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
|
||||
let (version, flags) = read_box_header_ext(reader)?;
|
||||
impl BlockReader for MdhdBox {
|
||||
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
|
||||
let (version, flags) = read_box_header_ext(reader);
|
||||
|
||||
let (creation_time, modification_time, timescale, duration) = if version == 1 {
|
||||
(
|
||||
reader.read_u64::<BigEndian>()?,
|
||||
reader.read_u64::<BigEndian>()?,
|
||||
reader.read_u32::<BigEndian>()?,
|
||||
reader.read_u64::<BigEndian>()?,
|
||||
reader.get_u64(),
|
||||
reader.get_u64(),
|
||||
reader.get_u32(),
|
||||
reader.get_u64(),
|
||||
)
|
||||
} else if version == 0 {
|
||||
(
|
||||
reader.read_u32::<BigEndian>()? as u64,
|
||||
reader.read_u32::<BigEndian>()? as u64,
|
||||
reader.read_u32::<BigEndian>()?,
|
||||
reader.read_u32::<BigEndian>()? as u64,
|
||||
reader.get_u32() as u64,
|
||||
reader.get_u32() as u64,
|
||||
reader.get_u32(),
|
||||
reader.get_u32() as u64,
|
||||
)
|
||||
} else {
|
||||
return Err(Error::InvalidData("version must be 0 or 1"));
|
||||
return Err(BoxError::InvalidData("version must be 0 or 1"));
|
||||
};
|
||||
let language_code = reader.read_u16::<BigEndian>()?;
|
||||
let language = language_string(language_code);
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
let language_code = reader.get_u16();
|
||||
let language = language_string(language_code);
|
||||
|
||||
Ok(MdhdBox {
|
||||
version,
|
||||
|
@ -108,12 +103,16 @@ impl<R: Read + Seek> ReadBox<&mut R> for MdhdBox {
|
|||
language,
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint() -> usize {
|
||||
22
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for MdhdBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
BoxHeader::new(Self::TYPE, size).write(writer)?;
|
||||
|
||||
write_box_header_ext(writer, self.version, self.flags)?;
|
||||
|
||||
|
@ -128,7 +127,7 @@ impl<W: Write> WriteBox<&mut W> for MdhdBox {
|
|||
writer.write_u32::<BigEndian>(self.timescale)?;
|
||||
writer.write_u32::<BigEndian>(self.duration as u32)?;
|
||||
} else {
|
||||
return Err(Error::InvalidData("version must be 0 or 1"));
|
||||
return Err(BoxError::InvalidData("version must be 0 or 1"));
|
||||
}
|
||||
|
||||
let language_code = language_code(&self.language);
|
||||
|
@ -166,7 +165,6 @@ fn language_code(language: &str) -> u16 {
|
|||
mod tests {
|
||||
use super::*;
|
||||
use crate::mp4box::BoxHeader;
|
||||
use std::io::Cursor;
|
||||
|
||||
fn test_language_code(lang: &str) {
|
||||
let code = language_code(lang);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)?;
|
||||
|
||||
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",
|
||||
));
|
||||
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 })
|
||||
}
|
||||
|
||||
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)?;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
use byteorder::{BigEndian, WriteBytesExt};
|
||||
use serde::Serialize;
|
||||
use std::io::{Read, Seek, Write};
|
||||
use std::io::Write;
|
||||
|
||||
use crate::mp4box::*;
|
||||
|
||||
|
@ -29,9 +29,7 @@ impl MehdBox {
|
|||
}
|
||||
|
||||
impl Mp4Box for MehdBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
const TYPE: BoxType = BoxType::MehdBox;
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
|
@ -47,20 +45,17 @@ impl Mp4Box for MehdBox {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for MehdBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
|
||||
let (version, flags) = read_box_header_ext(reader)?;
|
||||
impl BlockReader for MehdBox {
|
||||
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
|
||||
let (version, flags) = read_box_header_ext(reader);
|
||||
|
||||
let fragment_duration = if version == 1 {
|
||||
reader.read_u64::<BigEndian>()?
|
||||
reader.get_u64()
|
||||
} else if version == 0 {
|
||||
reader.read_u32::<BigEndian>()? as u64
|
||||
reader.get_u32() as u64
|
||||
} else {
|
||||
return Err(Error::InvalidData("version must be 0 or 1"));
|
||||
return Err(BoxError::InvalidData("version must be 0 or 1"));
|
||||
};
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
|
||||
Ok(MehdBox {
|
||||
version,
|
||||
|
@ -68,12 +63,16 @@ impl<R: Read + Seek> ReadBox<&mut R> for MehdBox {
|
|||
fragment_duration,
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint() -> usize {
|
||||
8
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for MehdBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
BoxHeader::new(Self::TYPE, size).write(writer)?;
|
||||
|
||||
write_box_header_ext(writer, self.version, self.flags)?;
|
||||
|
||||
|
@ -82,7 +81,7 @@ impl<W: Write> WriteBox<&mut W> for MehdBox {
|
|||
} else if self.version == 0 {
|
||||
writer.write_u32::<BigEndian>(self.fragment_duration as u32)?;
|
||||
} else {
|
||||
return Err(Error::InvalidData("version must be 0 or 1"));
|
||||
return Err(BoxError::InvalidData("version must be 0 or 1"));
|
||||
}
|
||||
|
||||
Ok(size)
|
||||
|
@ -93,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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
use std::io::{Read, Seek};
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::mp4box::hdlr::HdlrBox;
|
||||
|
@ -26,6 +24,7 @@ pub enum MetaBox {
|
|||
}
|
||||
|
||||
const MDIR: FourCC = FourCC { value: *b"mdir" };
|
||||
const MDTA: FourCC = FourCC { value: *b"mdta" };
|
||||
|
||||
impl MetaBox {
|
||||
pub fn get_type(&self) -> BoxType {
|
||||
|
@ -54,9 +53,7 @@ impl MetaBox {
|
|||
}
|
||||
|
||||
impl Mp4Box for MetaBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
const TYPE: BoxType = BoxType::MetaBox;
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
|
@ -86,115 +83,41 @@ impl Default for MetaBox {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for MetaBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
|
||||
let extended_header = reader.read_u32::<BigEndian>()?;
|
||||
if extended_header != 0 {
|
||||
// ISO mp4 requires this header (version & flags) to be 0. Some
|
||||
// files skip the extended header and directly start the hdlr box.
|
||||
let possible_hdlr = BoxType::from(reader.read_u32::<BigEndian>()?);
|
||||
if possible_hdlr == BoxType::HdlrBox {
|
||||
// This file skipped the extended header! Go back to start.
|
||||
reader.seek(SeekFrom::Current(-8))?;
|
||||
} else {
|
||||
// Looks like we actually have a bad version number or flags.
|
||||
let v = (extended_header >> 24) as u8;
|
||||
return Err(Error::UnsupportedBoxVersion(BoxType::MetaBox, v));
|
||||
impl BlockReader for MetaBox {
|
||||
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
|
||||
let extended_header = reader.peek_u32();
|
||||
if extended_header == 0 {
|
||||
reader.skip(4);
|
||||
}
|
||||
}
|
||||
|
||||
let mut current = reader.stream_position()?;
|
||||
let end = start + size;
|
||||
|
||||
let content_start = current;
|
||||
|
||||
// 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));
|
||||
}
|
||||
while let Some(mut bx) = reader.get_box()? {
|
||||
data.push((bx.kind, bx.inner.collect_remaining()))
|
||||
}
|
||||
|
||||
current = reader.stream_position()?;
|
||||
MetaBox::Unknown { hdlr, data }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Ok(MetaBox::Unknown { hdlr, data })
|
||||
}
|
||||
}
|
||||
fn size_hint() -> usize {
|
||||
8
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for MetaBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
BoxHeader::new(Self::TYPE, size).write(writer)?;
|
||||
|
||||
write_box_header_ext(writer, 0, 0)?;
|
||||
|
||||
|
@ -228,7 +151,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 +160,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 +179,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 +224,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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use serde::Serialize;
|
||||
use std::io::{Read, Seek, Write};
|
||||
use std::io::Write;
|
||||
|
||||
use crate::mp4box::*;
|
||||
use crate::mp4box::{dinf::DinfBox, smhd::SmhdBox, stbl::StblBox, vmhd::VmhdBox};
|
||||
|
@ -36,9 +36,7 @@ impl MinfBox {
|
|||
}
|
||||
|
||||
impl Mp4Box for MinfBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
const TYPE: BoxType = BoxType::MinfBox;
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
|
@ -54,57 +52,17 @@ impl Mp4Box for MinfBox {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for MinfBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
|
||||
let mut vmhd = None;
|
||||
let mut smhd = None;
|
||||
let mut dinf = None;
|
||||
let mut stbl = None;
|
||||
|
||||
let mut current = reader.stream_position()?;
|
||||
let end = start + size;
|
||||
while current < end {
|
||||
// Get box header.
|
||||
let header = BoxHeader::read(reader)?;
|
||||
let BoxHeader { name, size: s } = header;
|
||||
if s > size {
|
||||
return Err(Error::InvalidData(
|
||||
"minf box contains a box with a larger size than it",
|
||||
));
|
||||
}
|
||||
|
||||
match name {
|
||||
BoxType::VmhdBox => {
|
||||
vmhd = Some(VmhdBox::read_box(reader, s)?);
|
||||
}
|
||||
BoxType::SmhdBox => {
|
||||
smhd = Some(SmhdBox::read_box(reader, s)?);
|
||||
}
|
||||
BoxType::DinfBox => {
|
||||
dinf = Some(DinfBox::read_box(reader, s)?);
|
||||
}
|
||||
BoxType::StblBox => {
|
||||
stbl = Some(StblBox::read_box(reader, s)?);
|
||||
}
|
||||
_ => {
|
||||
// XXX warn!()
|
||||
skip_box(reader, s)?;
|
||||
}
|
||||
}
|
||||
|
||||
current = reader.stream_position()?;
|
||||
}
|
||||
impl BlockReader for MinfBox {
|
||||
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
|
||||
let (vmhd, smhd, dinf, stbl) = reader.try_find_box4()?;
|
||||
|
||||
if dinf.is_none() {
|
||||
return Err(Error::BoxNotFound(BoxType::DinfBox));
|
||||
}
|
||||
if stbl.is_none() {
|
||||
return Err(Error::BoxNotFound(BoxType::StblBox));
|
||||
return Err(BoxError::BoxNotFound(BoxType::DinfBox));
|
||||
}
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
if stbl.is_none() {
|
||||
return Err(BoxError::BoxNotFound(BoxType::StblBox));
|
||||
}
|
||||
|
||||
Ok(MinfBox {
|
||||
vmhd,
|
||||
|
@ -113,12 +71,16 @@ impl<R: Read + Seek> ReadBox<&mut R> for MinfBox {
|
|||
stbl: stbl.unwrap(),
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint() -> usize {
|
||||
DinfBox::size_hint() + StblBox::size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for MinfBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
BoxHeader::new(Self::TYPE, size).write(writer)?;
|
||||
|
||||
if let Some(ref vmhd) = self.vmhd {
|
||||
vmhd.write_box(writer)?;
|
||||
|
|
|
@ -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,467 @@ boxtype! {
|
|||
}
|
||||
|
||||
pub trait Mp4Box: Sized {
|
||||
fn box_type(&self) -> BoxType;
|
||||
const TYPE: BoxType;
|
||||
|
||||
fn box_size(&self) -> u64;
|
||||
fn to_json(&self) -> Result<String>;
|
||||
fn summary(&self) -> Result<String>;
|
||||
}
|
||||
|
||||
pub trait ReadBox<T>: Sized {
|
||||
fn read_box(_: T, size: u64) -> Result<Self>;
|
||||
pub struct BoxReader<'a, R: Reader<'a>> {
|
||||
kind: BoxType,
|
||||
inner: R,
|
||||
m: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl<'a, R: Reader<'a>> BoxReader<'a, R> {
|
||||
#[inline]
|
||||
pub fn try_read<T: Mp4Box + BlockReader>(&mut self) -> Result<Option<T>> {
|
||||
if T::TYPE == self.kind {
|
||||
Ok(Some(T::read_block(&mut self.inner)?))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read<T: Mp4Box + BlockReader>(&mut self) -> Result<T> {
|
||||
if T::TYPE == self.kind {
|
||||
T::read_block(&mut self.inner)
|
||||
} else {
|
||||
Err(BoxError::BoxNotFound(T::TYPE))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Reader<'a> {
|
||||
fn take(&mut self, size: usize) -> Result<impl Reader<'a> + '_>;
|
||||
fn remaining(&self) -> usize;
|
||||
fn skip(&mut self, size: usize);
|
||||
|
||||
fn peek_u32(&self) -> u32;
|
||||
|
||||
fn get_u8(&mut self) -> u8;
|
||||
fn get_u16(&mut self) -> u16;
|
||||
fn get_u24(&mut self) -> u32;
|
||||
fn get_u32(&mut self) -> u32;
|
||||
fn get_u48(&mut self) -> u64;
|
||||
fn get_u64(&mut self) -> u64;
|
||||
|
||||
fn get_i8(&mut self) -> i8;
|
||||
fn get_i16(&mut self) -> i16;
|
||||
fn get_i24(&mut self) -> i32;
|
||||
fn get_i32(&mut self) -> i32;
|
||||
fn get_i48(&mut self) -> i64;
|
||||
fn get_i64(&mut self) -> i64;
|
||||
|
||||
#[inline]
|
||||
fn try_get_u8(&mut self) -> Result<u8> {
|
||||
if self.remaining() < 1 {
|
||||
Err(BoxError::InvalidData("expected at least 1 byte more"))
|
||||
} else {
|
||||
Ok(self.get_u8())
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn try_get_u16(&mut self) -> Result<u16> {
|
||||
if self.remaining() < 2 {
|
||||
Err(BoxError::InvalidData("expected at least 2 byte more"))
|
||||
} else {
|
||||
Ok(self.get_u16())
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn try_get_u24(&mut self) -> Result<u32> {
|
||||
if self.remaining() < 3 {
|
||||
Err(BoxError::InvalidData("expected at least 3 byte more"))
|
||||
} else {
|
||||
Ok(self.get_u24())
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn try_get_u32(&mut self) -> Result<u32> {
|
||||
if self.remaining() < 4 {
|
||||
Err(BoxError::InvalidData("expected at least 4 byte more"))
|
||||
} else {
|
||||
Ok(self.get_u32())
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn try_get_u48(&mut self) -> Result<u64> {
|
||||
if self.remaining() < 6 {
|
||||
Err(BoxError::InvalidData("expected at least 6 byte more"))
|
||||
} else {
|
||||
Ok(self.get_u48())
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn try_get_u64(&mut self) -> Result<u64> {
|
||||
if self.remaining() < 8 {
|
||||
Err(BoxError::InvalidData("expected at least 8 byte more"))
|
||||
} else {
|
||||
Ok(self.get_u64())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_get_i8(&mut self) -> Result<i8> {
|
||||
if self.remaining() < 1 {
|
||||
Err(BoxError::InvalidData("expected at least 1 byte more"))
|
||||
} else {
|
||||
Ok(self.get_i8())
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn try_get_i16(&mut self) -> Result<i16> {
|
||||
if self.remaining() < 2 {
|
||||
Err(BoxError::InvalidData("expected at least 2 byte more"))
|
||||
} else {
|
||||
Ok(self.get_i16())
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn try_get_i24(&mut self) -> Result<i32> {
|
||||
if self.remaining() < 3 {
|
||||
Err(BoxError::InvalidData("expected at least 3 byte more"))
|
||||
} else {
|
||||
Ok(self.get_i24())
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn try_get_i32(&mut self) -> Result<i32> {
|
||||
if self.remaining() < 4 {
|
||||
Err(BoxError::InvalidData("expected at least 4 byte more"))
|
||||
} else {
|
||||
Ok(self.get_i32())
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn try_get_i48(&mut self) -> Result<i64> {
|
||||
if self.remaining() < 6 {
|
||||
Err(BoxError::InvalidData("expected at least 6 byte more"))
|
||||
} else {
|
||||
Ok(self.get_i48())
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn try_get_i64(&mut self) -> Result<i64> {
|
||||
if self.remaining() < 8 {
|
||||
Err(BoxError::InvalidData("expected at least 8 byte more"))
|
||||
} else {
|
||||
Ok(self.get_i64())
|
||||
}
|
||||
}
|
||||
fn get_null_terminated_string(&mut self) -> String;
|
||||
fn collect(&mut self, size: usize) -> Result<Vec<u8>> {
|
||||
let mut buf = vec![0; size];
|
||||
self.copy_to_slice(&mut buf)?;
|
||||
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn collect_remaining(&mut self) -> Vec<u8> {
|
||||
self.collect(self.remaining()).unwrap()
|
||||
}
|
||||
|
||||
fn copy_to_slice(&mut self, slice: &mut [u8]) -> Result<()>;
|
||||
fn get_box(&mut self) -> Result<Option<BoxReader<'a, impl Reader<'a> + '_>>>;
|
||||
|
||||
fn find_box<B: Mp4Box + BlockReader>(&mut self) -> Result<B> {
|
||||
self.try_find_box()
|
||||
.and_then(|x| x.ok_or_else(|| BoxError::InvalidData("expected box")))
|
||||
}
|
||||
|
||||
fn try_find_box2<A: Mp4Box + BlockReader, B: Mp4Box + BlockReader>(
|
||||
&mut self,
|
||||
) -> Result<(Option<A>, Option<B>)> {
|
||||
let mut a = None;
|
||||
let mut b = None;
|
||||
|
||||
while let Some(mut bx) = self.get_box()? {
|
||||
if a.is_none() {
|
||||
if let Some(inner) = bx.try_read::<A>()? {
|
||||
a = Some(inner);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if b.is_none() {
|
||||
if let Some(inner) = bx.try_read::<B>()? {
|
||||
b = Some(inner);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
println!(" 1 unknown box {}", bx.kind);
|
||||
}
|
||||
|
||||
Ok((a, b))
|
||||
}
|
||||
|
||||
fn try_find_box3<A, B, C>(&mut self) -> Result<(Option<A>, Option<B>, Option<C>)>
|
||||
where
|
||||
A: Mp4Box + BlockReader,
|
||||
B: Mp4Box + BlockReader,
|
||||
C: Mp4Box + BlockReader,
|
||||
{
|
||||
let mut a = None;
|
||||
let mut b = None;
|
||||
let mut c = None;
|
||||
|
||||
while let Some(mut bx) = self.get_box()? {
|
||||
if a.is_none() {
|
||||
if let Some(inner) = bx.try_read::<A>()? {
|
||||
a = Some(inner);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if b.is_none() {
|
||||
if let Some(inner) = bx.try_read::<B>()? {
|
||||
b = Some(inner);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if c.is_none() {
|
||||
if let Some(inner) = bx.try_read::<C>()? {
|
||||
c = Some(inner);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
println!(" 2 unknown box {}", bx.kind);
|
||||
}
|
||||
|
||||
Ok((a, b, c))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn find_box3<A, B, C>(&mut self) -> Result<(A, B, C)>
|
||||
where
|
||||
A: Mp4Box + BlockReader,
|
||||
B: Mp4Box + BlockReader,
|
||||
C: Mp4Box + BlockReader,
|
||||
{
|
||||
let (a, b, c) = self.try_find_box3()?;
|
||||
|
||||
let Some(a) = a else {
|
||||
return Err(BoxError::BoxNotFound(A::TYPE));
|
||||
};
|
||||
|
||||
let Some(b) = b else {
|
||||
return Err(BoxError::BoxNotFound(B::TYPE));
|
||||
};
|
||||
|
||||
let Some(c) = c else {
|
||||
return Err(BoxError::BoxNotFound(C::TYPE));
|
||||
};
|
||||
|
||||
Ok((a, b, c))
|
||||
}
|
||||
|
||||
fn try_find_box4<A, B, C, D>(&mut self) -> Result<(Option<A>, Option<B>, Option<C>, Option<D>)>
|
||||
where
|
||||
A: Mp4Box + BlockReader,
|
||||
B: Mp4Box + BlockReader,
|
||||
C: Mp4Box + BlockReader,
|
||||
D: Mp4Box + BlockReader,
|
||||
{
|
||||
let mut a = None;
|
||||
let mut b = None;
|
||||
let mut c = None;
|
||||
let mut d = None;
|
||||
|
||||
while let Some(mut bx) = self.get_box()? {
|
||||
if a.is_none() {
|
||||
if let Some(inner) = bx.try_read::<A>()? {
|
||||
a = Some(inner);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if b.is_none() {
|
||||
if let Some(inner) = bx.try_read::<B>()? {
|
||||
b = Some(inner);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if c.is_none() {
|
||||
if let Some(inner) = bx.try_read::<C>()? {
|
||||
c = Some(inner);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if d.is_none() {
|
||||
if let Some(inner) = bx.try_read::<D>()? {
|
||||
d = Some(inner);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
println!(" 3 unknown box {}", bx.kind);
|
||||
}
|
||||
|
||||
Ok((a, b, c, d))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_find_box<B: Mp4Box + BlockReader>(&mut self) -> Result<Option<B>> {
|
||||
while let Some(mut bx) = self.get_box()? {
|
||||
if let Some(inner) = bx.try_read::<B>()? {
|
||||
return Ok(Some(inner));
|
||||
}
|
||||
|
||||
println!(" 4 unknown box {}", bx.kind);
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Reader<'a> for &'a [u8] {
|
||||
#[inline]
|
||||
fn take(&mut self, size: usize) -> Result<impl Reader<'a> + '_> {
|
||||
if self.len() < size {
|
||||
return Err(BoxError::InvalidData("no bytes left"));
|
||||
}
|
||||
|
||||
let buff = &(*self)[0..size];
|
||||
self.advance(size);
|
||||
|
||||
Ok(buff)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn skip(&mut self, size: usize) {
|
||||
Buf::advance(self, size)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn remaining(&self) -> usize {
|
||||
Buf::remaining(self)
|
||||
}
|
||||
|
||||
fn peek_u32(&self) -> u32 {
|
||||
BigEndian::read_u32(self.chunk())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_u8(&mut self) -> u8 {
|
||||
Buf::get_u8(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_u16(&mut self) -> u16 {
|
||||
Buf::get_u16(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_u24(&mut self) -> u32 {
|
||||
let val = BigEndian::read_u24(self.chunk());
|
||||
self.skip(3);
|
||||
val
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_u32(&mut self) -> u32 {
|
||||
Buf::get_u32(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_u48(&mut self) -> u64 {
|
||||
let val = BigEndian::read_u48(self.chunk());
|
||||
self.skip(6);
|
||||
val
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_u64(&mut self) -> u64 {
|
||||
Buf::get_u64(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_i8(&mut self) -> i8 {
|
||||
Buf::get_i8(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_i16(&mut self) -> i16 {
|
||||
Buf::get_i16(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_i24(&mut self) -> i32 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_i32(&mut self) -> i32 {
|
||||
Buf::get_i32(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_i48(&mut self) -> i64 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_i64(&mut self) -> i64 {
|
||||
Buf::get_i64(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn copy_to_slice(&mut self, slice: &mut [u8]) -> Result<()> {
|
||||
if self.len() < slice.len() {
|
||||
return Err(BoxError::InvalidData("expected more bytes"));
|
||||
}
|
||||
|
||||
Buf::copy_to_slice(self, slice);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_null_terminated_string(&mut self) -> String {
|
||||
let rem = self.len();
|
||||
|
||||
if rem > 0 {
|
||||
let size = self.iter().position(|&b| b == b'\0');
|
||||
|
||||
let (size, eat) = if let Some(size) = size {
|
||||
(size, size + 1)
|
||||
} else {
|
||||
(rem, rem)
|
||||
};
|
||||
|
||||
let val = String::from_utf8_lossy(&self[0..size]).to_string();
|
||||
self.advance(eat);
|
||||
val
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_box(&mut self) -> Result<Option<BoxReader<'a, impl Reader<'a> + '_>>> {
|
||||
let 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 +722,134 @@ 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(BoxError::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(BoxError::InvalidData("64-bit box size too small")),
|
||||
16..=u64::MAX => largesize - 8,
|
||||
}
|
||||
} else {
|
||||
sz as _
|
||||
};
|
||||
|
||||
println!(
|
||||
"{} box {} {}",
|
||||
if sz == 1 { "big" } else { "small" },
|
||||
BoxType::from(typ).as_str(),
|
||||
size
|
||||
);
|
||||
|
||||
Ok(Some(BoxHeader {
|
||||
kind: BoxType::from(typ),
|
||||
size: 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,
|
||||
offset: &mut u64,
|
||||
) -> Result<Option<Self>> {
|
||||
// Create and read to buf.
|
||||
let mut buf = [0u8; 8]; // 8 bytes for box header.
|
||||
reader.read_exact(&mut buf)?;
|
||||
match reader.read_exact(&mut buf).await {
|
||||
Ok(_) => (),
|
||||
Err(err) => match err.kind() {
|
||||
std::io::ErrorKind::UnexpectedEof => return Ok(None),
|
||||
_ => return Err(err.into()),
|
||||
},
|
||||
}
|
||||
*offset += 8;
|
||||
|
||||
// Get size.
|
||||
let s = buf[0..4].try_into().unwrap();
|
||||
let size = u32::from_be_bytes(s);
|
||||
let sz = u32::from_be_bytes(s);
|
||||
|
||||
// Get box type string.
|
||||
let t = buf[4..8].try_into().unwrap();
|
||||
let typ = u32::from_be_bytes(t);
|
||||
|
||||
// Get largesize if size is 1
|
||||
if size == 1 {
|
||||
reader.read_exact(&mut buf)?;
|
||||
let largesize = u64::from_be_bytes(buf);
|
||||
let size = if sz == 1 {
|
||||
match reader.read_exact(&mut buf).await {
|
||||
Ok(_) => (),
|
||||
Err(err) => match err.kind() {
|
||||
std::io::ErrorKind::UnexpectedEof => return Ok(None),
|
||||
_ => return Err(err.into()),
|
||||
},
|
||||
}
|
||||
|
||||
Ok(BoxHeader {
|
||||
name: BoxType::from(typ),
|
||||
*offset += 8;
|
||||
let largesize = u64::from_be_bytes(buf);
|
||||
|
||||
// Subtract the length of the serialized largesize, as callers assume `size - HEADER_SIZE` is the length
|
||||
// of the box data. Disallow `largesize < 16`, or else a largesize of 8 will result in a BoxHeader::size
|
||||
// of 0, incorrectly indicating that the box data extends to the end of the stream.
|
||||
size: match largesize {
|
||||
match largesize {
|
||||
0 => 0,
|
||||
1..=15 => return Err(Error::InvalidData("64-bit box size too small")),
|
||||
1..=15 => return Err(BoxError::InvalidData("64-bit box size too small")),
|
||||
16..=u64::MAX => largesize - 8,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
Ok(BoxHeader {
|
||||
name: BoxType::from(typ),
|
||||
size: size as u64,
|
||||
})
|
||||
}
|
||||
} 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),
|
||||
}))
|
||||
}
|
||||
|
||||
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 +858,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 +916,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][..]);
|
||||
assert!(matches!(error, Err(Error::InvalidData(_))));
|
||||
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(BoxError::InvalidData(_))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zero_largesize() {
|
||||
let error = BoxHeader::read(&mut &[0, 0, 0, 1, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 8][..]);
|
||||
assert!(matches!(error, Err(Error::InvalidData(_))));
|
||||
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(BoxError::InvalidData(_))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nonzero_largesize_too_small() {
|
||||
let error = BoxHeader::read(&mut &[0, 0, 0, 1, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 15][..]);
|
||||
assert!(matches!(error, Err(Error::InvalidData(_))));
|
||||
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(BoxError::InvalidData(_))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_valid_largesize() {
|
||||
let header = BoxHeader::read(&mut &[0, 0, 0, 1, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 16][..]);
|
||||
assert!(matches!(header, Ok(BoxHeader { size: 8, .. })));
|
||||
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, .. }))));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
));
|
||||
while let Some(mut bx) = reader.get_box()? {
|
||||
match bx.kind {
|
||||
BoxType::MfhdBox => {
|
||||
mfhd = Some(bx.read()?);
|
||||
}
|
||||
|
||||
match name {
|
||||
BoxType::MfhdBox => {
|
||||
mfhd = Some(MfhdBox::read_box(reader, s)?);
|
||||
}
|
||||
BoxType::TrafBox => {
|
||||
let traf = TrafBox::read_box(reader, s)?;
|
||||
trafs.push(traf);
|
||||
trafs.push(bx.read()?);
|
||||
}
|
||||
_ => {
|
||||
// XXX warn!()
|
||||
skip_box(reader, s)?;
|
||||
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
current = reader.stream_position()?;
|
||||
}
|
||||
|
||||
if mfhd.is_none() {
|
||||
return Err(Error::BoxNotFound(BoxType::MfhdBox));
|
||||
return Err(BoxError::BoxNotFound(BoxType::MfhdBox));
|
||||
}
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
|
||||
Ok(MoofBox {
|
||||
mfhd: mfhd.unwrap(),
|
||||
trafs,
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint() -> usize {
|
||||
MfhdBox::size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for MoofBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
BoxHeader::new(Self::TYPE, size).write(writer)?;
|
||||
|
||||
self.mfhd.write_box(writer)?;
|
||||
for traf in self.trafs.iter() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use serde::Serialize;
|
||||
use std::io::{Read, Seek, Write};
|
||||
use std::io::Write;
|
||||
|
||||
use crate::meta::MetaBox;
|
||||
use crate::mp4box::*;
|
||||
|
@ -43,9 +43,7 @@ impl MoovBox {
|
|||
}
|
||||
|
||||
impl Mp4Box for MoovBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
const TYPE: BoxType = BoxType::MoovBox;
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
|
@ -61,60 +59,44 @@ impl Mp4Box for MoovBox {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for MoovBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
|
||||
impl BlockReader for MoovBox {
|
||||
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
|
||||
let mut mvhd = None;
|
||||
let mut meta = None;
|
||||
let mut udta = None;
|
||||
let mut mvex = None;
|
||||
let mut traks = Vec::new();
|
||||
|
||||
let mut current = reader.stream_position()?;
|
||||
let end = start + size;
|
||||
while current < end {
|
||||
// Get box header.
|
||||
let header = BoxHeader::read(reader)?;
|
||||
let BoxHeader { name, size: s } = header;
|
||||
if s > size {
|
||||
return Err(Error::InvalidData(
|
||||
"moov box contains a box with a larger size than it",
|
||||
));
|
||||
}
|
||||
|
||||
match name {
|
||||
while let Some(mut bx) = reader.get_box()? {
|
||||
match bx.kind {
|
||||
BoxType::MvhdBox => {
|
||||
mvhd = Some(MvhdBox::read_box(reader, s)?);
|
||||
}
|
||||
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)?;
|
||||
}
|
||||
mvhd = Some(bx.read()?);
|
||||
}
|
||||
|
||||
current = reader.stream_position()?;
|
||||
BoxType::MetaBox => {
|
||||
meta = Some(bx.read()?);
|
||||
}
|
||||
|
||||
BoxType::MvexBox => {
|
||||
mvex = Some(bx.read()?);
|
||||
}
|
||||
|
||||
BoxType::TrakBox => {
|
||||
traks.push(bx.read()?);
|
||||
}
|
||||
|
||||
BoxType::UdtaBox => {
|
||||
udta = Some(bx.read()?);
|
||||
}
|
||||
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
|
||||
if mvhd.is_none() {
|
||||
return Err(Error::BoxNotFound(BoxType::MvhdBox));
|
||||
return Err(BoxError::BoxNotFound(BoxType::MvhdBox));
|
||||
}
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
|
||||
Ok(MoovBox {
|
||||
mvhd: mvhd.unwrap(),
|
||||
meta,
|
||||
|
@ -123,12 +105,16 @@ impl<R: Read + Seek> ReadBox<&mut R> for MoovBox {
|
|||
traks,
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint() -> usize {
|
||||
MvhdBox::size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for MoovBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
BoxHeader::new(Self::TYPE, size).write(writer)?;
|
||||
|
||||
self.mvhd.write_box(writer)?;
|
||||
for trak in self.traks.iter() {
|
||||
|
@ -148,7 +134,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 +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::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 +166,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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
use byteorder::{BigEndian, WriteBytesExt};
|
||||
use serde::Serialize;
|
||||
use std::io::{Read, Seek, Write};
|
||||
use std::io::Write;
|
||||
|
||||
use crate::mp4box::*;
|
||||
|
||||
|
@ -52,9 +52,7 @@ impl Mp4aBox {
|
|||
}
|
||||
|
||||
impl Mp4Box for Mp4aBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
const TYPE: BoxType = BoxType::Mp4aBox;
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
|
@ -75,70 +73,52 @@ impl Mp4Box for Mp4aBox {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for Mp4aBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
impl BlockReader for Mp4aBox {
|
||||
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
|
||||
reader.get_u32(); // reserved
|
||||
reader.get_u16(); // reserved
|
||||
|
||||
reader.read_u32::<BigEndian>()?; // reserved
|
||||
reader.read_u16::<BigEndian>()?; // reserved
|
||||
let data_reference_index = reader.read_u16::<BigEndian>()?;
|
||||
let version = reader.read_u16::<BigEndian>()?;
|
||||
reader.read_u16::<BigEndian>()?; // reserved
|
||||
reader.read_u32::<BigEndian>()?; // reserved
|
||||
let channelcount = reader.read_u16::<BigEndian>()?;
|
||||
let samplesize = reader.read_u16::<BigEndian>()?;
|
||||
reader.read_u32::<BigEndian>()?; // pre-defined, reserved
|
||||
let samplerate = FixedPointU16::new_raw(reader.read_u32::<BigEndian>()?);
|
||||
let data_reference_index = reader.get_u16();
|
||||
let version = reader.get_u16();
|
||||
|
||||
reader.get_u16(); // reserved
|
||||
reader.get_u32(); // reserved
|
||||
|
||||
let channelcount = reader.get_u16();
|
||||
let samplesize = reader.get_u16();
|
||||
|
||||
reader.get_u32(); // pre-defined, reserved
|
||||
|
||||
let samplerate = FixedPointU16::new_raw(reader.get_u32());
|
||||
|
||||
if version == 1 {
|
||||
if reader.remaining() < 16 {
|
||||
return Err(BoxError::InvalidData("expected at least 16 bytes more"));
|
||||
}
|
||||
|
||||
// Skip QTFF
|
||||
reader.read_u64::<BigEndian>()?;
|
||||
reader.read_u64::<BigEndian>()?;
|
||||
reader.get_u64();
|
||||
reader.get_u64();
|
||||
}
|
||||
|
||||
// Find esds in mp4a or wave
|
||||
let mut esds = None;
|
||||
let end = start + size;
|
||||
loop {
|
||||
let current = reader.stream_position()?;
|
||||
if current >= end {
|
||||
break;
|
||||
}
|
||||
let header = BoxHeader::read(reader)?;
|
||||
let BoxHeader { name, size: s } = header;
|
||||
if s > size {
|
||||
return Err(Error::InvalidData(
|
||||
"mp4a box contains a box with a larger size than it",
|
||||
));
|
||||
}
|
||||
if name == BoxType::EsdsBox {
|
||||
esds = Some(EsdsBox::read_box(reader, s)?);
|
||||
break;
|
||||
} else if name == BoxType::WaveBox {
|
||||
// Typically contains frma, mp4a, esds, and a terminator atom
|
||||
} else {
|
||||
// Skip boxes
|
||||
let skip_to = current + s;
|
||||
skip_bytes_to(reader, skip_to)?;
|
||||
}
|
||||
}
|
||||
|
||||
skip_bytes_to(reader, end)?;
|
||||
|
||||
Ok(Mp4aBox {
|
||||
data_reference_index,
|
||||
channelcount,
|
||||
samplesize,
|
||||
samplerate,
|
||||
esds,
|
||||
esds: reader.try_find_box::<EsdsBox>()?,
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint() -> usize {
|
||||
28
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for Mp4aBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
BoxHeader::new(Self::TYPE, size).write(writer)?;
|
||||
|
||||
writer.write_u32::<BigEndian>(0)?; // reserved
|
||||
writer.write_u16::<BigEndian>(0)?; // reserved
|
||||
|
@ -176,9 +156,7 @@ impl EsdsBox {
|
|||
}
|
||||
|
||||
impl Mp4Box for EsdsBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
BoxType::EsdsBox
|
||||
}
|
||||
const TYPE: BoxType = BoxType::EsdsBox;
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
HEADER_SIZE
|
||||
|
@ -197,45 +175,41 @@ impl Mp4Box for EsdsBox {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for EsdsBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
|
||||
let (version, flags) = read_box_header_ext(reader)?;
|
||||
impl BlockReader for EsdsBox {
|
||||
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
|
||||
let (version, flags) = read_box_header_ext(reader);
|
||||
|
||||
let mut es_desc = None;
|
||||
|
||||
let mut current = reader.stream_position()?;
|
||||
let end = start + size;
|
||||
while current < end {
|
||||
let (desc_tag, desc_size) = read_desc(reader)?;
|
||||
while let Some((desc_tag, desc_size)) = read_desc(reader) {
|
||||
match desc_tag {
|
||||
0x03 => {
|
||||
es_desc = Some(ESDescriptor::read_desc(reader, desc_size)?);
|
||||
es_desc = Some(ESDescriptor::read_block(&mut reader.take(desc_size as _)?)?);
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
current = reader.stream_position()?;
|
||||
}
|
||||
|
||||
if es_desc.is_none() {
|
||||
return Err(Error::InvalidData("ESDescriptor not found"));
|
||||
return Err(BoxError::InvalidData("ESDescriptor not found"));
|
||||
}
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
|
||||
Ok(EsdsBox {
|
||||
version,
|
||||
flags,
|
||||
es_desc: es_desc.unwrap(),
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint() -> usize {
|
||||
4 + ESDescriptor::size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for EsdsBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
BoxHeader::new(Self::TYPE, size).write(writer)?;
|
||||
|
||||
write_box_header_ext(writer, self.version, self.flags)?;
|
||||
|
||||
|
@ -250,27 +224,24 @@ trait Descriptor: Sized {
|
|||
fn desc_size() -> u32;
|
||||
}
|
||||
|
||||
trait ReadDesc<T>: Sized {
|
||||
fn read_desc(_: T, size: u32) -> Result<Self>;
|
||||
}
|
||||
|
||||
trait WriteDesc<T>: Sized {
|
||||
fn write_desc(&self, _: T) -> Result<u32>;
|
||||
}
|
||||
|
||||
fn read_desc<R: Read>(reader: &mut R) -> Result<(u8, u32)> {
|
||||
let tag = reader.read_u8()?;
|
||||
fn read_desc<'a, R: Reader<'a>>(reader: &mut R) -> Option<(u8, u32)> {
|
||||
let tag = reader.try_get_u8().ok()?;
|
||||
|
||||
let mut size: u32 = 0;
|
||||
for _ in 0..4 {
|
||||
let b = reader.read_u8()?;
|
||||
let b = reader.try_get_u8().ok()?;
|
||||
size = (size << 7) | (b & 0x7F) as u32;
|
||||
|
||||
if b & 0x80 == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok((tag, size))
|
||||
Some((tag, size))
|
||||
}
|
||||
|
||||
fn size_of_length(size: u32) -> u32 {
|
||||
|
@ -286,7 +257,7 @@ fn write_desc<W: Write>(writer: &mut W, tag: u8, size: u32) -> Result<u64> {
|
|||
writer.write_u8(tag)?;
|
||||
|
||||
if size as u64 > std::u32::MAX as u64 {
|
||||
return Err(Error::InvalidData("invalid descriptor length range"));
|
||||
return Err(BoxError::InvalidData("invalid descriptor length range"));
|
||||
}
|
||||
|
||||
let nbytes = size_of_length(size);
|
||||
|
@ -335,33 +306,29 @@ 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)?);
|
||||
let mut rdr = reader.take(desc_size as _)?;
|
||||
sl_config = Some(SLConfigDescriptor::read_block(&mut rdr)?);
|
||||
rdr.skip(rdr.remaining());
|
||||
}
|
||||
_ => {
|
||||
skip_bytes(reader, desc_size as u64)?;
|
||||
_ => reader.skip(desc_size as _),
|
||||
}
|
||||
}
|
||||
current = reader.stream_position()?;
|
||||
}
|
||||
|
||||
Ok(ESDescriptor {
|
||||
es_id,
|
||||
|
@ -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,34 +395,29 @@ 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)?);
|
||||
let mut rdr = reader.take(desc_size as _)?;
|
||||
let r = DecoderSpecificDescriptor::read_block(&mut rdr)?;
|
||||
rdr.skip(rdr.remaining());
|
||||
dec_specific = Some(r);
|
||||
}
|
||||
_ => {
|
||||
skip_bytes(reader, desc_size as u64)?;
|
||||
_ => reader.skip(desc_size as _),
|
||||
}
|
||||
}
|
||||
current = reader.stream_position()?;
|
||||
}
|
||||
|
||||
Ok(DecoderConfigDescriptor {
|
||||
object_type_indication,
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use serde::Serialize;
|
||||
use std::io::{Read, Seek, Write};
|
||||
use std::io::Write;
|
||||
|
||||
use crate::mp4box::*;
|
||||
use crate::mp4box::{mehd::MehdBox, trex::TrexBox};
|
||||
|
@ -21,9 +21,7 @@ impl MvexBox {
|
|||
}
|
||||
|
||||
impl Mp4Box for MvexBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
const TYPE: BoxType = BoxType::MvexBox;
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
|
@ -39,62 +37,34 @@ impl Mp4Box for MvexBox {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for MvexBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
|
||||
let mut mehd = None;
|
||||
let mut trex = None;
|
||||
|
||||
let mut current = reader.stream_position()?;
|
||||
let end = start + size;
|
||||
while current < end {
|
||||
// Get box header.
|
||||
let header = BoxHeader::read(reader)?;
|
||||
let BoxHeader { name, size: s } = header;
|
||||
if s > size {
|
||||
return Err(Error::InvalidData(
|
||||
"mvex box contains a box with a larger size than it",
|
||||
));
|
||||
}
|
||||
|
||||
match name {
|
||||
BoxType::MehdBox => {
|
||||
mehd = Some(MehdBox::read_box(reader, s)?);
|
||||
}
|
||||
BoxType::TrexBox => {
|
||||
trex = Some(TrexBox::read_box(reader, s)?);
|
||||
}
|
||||
_ => {
|
||||
// XXX warn!()
|
||||
skip_box(reader, s)?;
|
||||
}
|
||||
}
|
||||
|
||||
current = reader.stream_position()?;
|
||||
}
|
||||
impl BlockReader for MvexBox {
|
||||
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
|
||||
let (mehd, trex) = reader.try_find_box2::<MehdBox, TrexBox>()?;
|
||||
|
||||
if trex.is_none() {
|
||||
return Err(Error::BoxNotFound(BoxType::TrexBox));
|
||||
return Err(BoxError::BoxNotFound(BoxType::TrexBox));
|
||||
}
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
|
||||
Ok(MvexBox {
|
||||
mehd,
|
||||
trex: trex.unwrap(),
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint() -> usize {
|
||||
TrexBox::size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for MvexBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
BoxHeader::new(Self::TYPE, size).write(writer)?;
|
||||
|
||||
if let Some(mehd) = &self.mehd {
|
||||
mehd.write_box(writer)?;
|
||||
}
|
||||
|
||||
self.trex.write_box(writer)?;
|
||||
|
||||
Ok(size)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
use byteorder::{BigEndian, WriteBytesExt};
|
||||
use serde::Serialize;
|
||||
use std::io::{Read, Seek, Write};
|
||||
use std::io::Write;
|
||||
|
||||
use crate::mp4box::*;
|
||||
|
||||
|
@ -58,9 +58,7 @@ impl Default for MvhdBox {
|
|||
}
|
||||
|
||||
impl Mp4Box for MvhdBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
const TYPE: BoxType = BoxType::MvhdBox;
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
|
@ -85,54 +83,53 @@ impl Mp4Box for MvhdBox {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for MvhdBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
|
||||
let (version, flags) = read_box_header_ext(reader)?;
|
||||
impl BlockReader for MvhdBox {
|
||||
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
|
||||
let (version, flags) = read_box_header_ext(reader);
|
||||
|
||||
let (creation_time, modification_time, timescale, duration) = if version == 1 {
|
||||
if reader.remaining() < Self::size_hint() - 4 + 12 {
|
||||
return Err(BoxError::InvalidData("expected more bytes"));
|
||||
}
|
||||
|
||||
(
|
||||
reader.read_u64::<BigEndian>()?,
|
||||
reader.read_u64::<BigEndian>()?,
|
||||
reader.read_u32::<BigEndian>()?,
|
||||
reader.read_u64::<BigEndian>()?,
|
||||
reader.get_u64(),
|
||||
reader.get_u64(),
|
||||
reader.get_u32(),
|
||||
reader.get_u64(),
|
||||
)
|
||||
} else if version == 0 {
|
||||
(
|
||||
reader.read_u32::<BigEndian>()? as u64,
|
||||
reader.read_u32::<BigEndian>()? as u64,
|
||||
reader.read_u32::<BigEndian>()?,
|
||||
reader.read_u32::<BigEndian>()? as u64,
|
||||
reader.get_u32() as u64,
|
||||
reader.get_u32() as u64,
|
||||
reader.get_u32(),
|
||||
reader.get_u32() as u64,
|
||||
)
|
||||
} else {
|
||||
return Err(Error::InvalidData("version must be 0 or 1"));
|
||||
return Err(BoxError::InvalidData("version must be 0 or 1"));
|
||||
};
|
||||
let rate = FixedPointU16::new_raw(reader.read_u32::<BigEndian>()?);
|
||||
|
||||
let volume = FixedPointU8::new_raw(reader.read_u16::<BigEndian>()?);
|
||||
let rate = FixedPointU16::new_raw(reader.get_u32());
|
||||
let volume = FixedPointU8::new_raw(reader.get_u16());
|
||||
|
||||
reader.read_u16::<BigEndian>()?; // reserved = 0
|
||||
|
||||
reader.read_u64::<BigEndian>()?; // reserved = 0
|
||||
reader.get_u16(); // reserved = 0
|
||||
reader.get_u64(); // reserved = 0
|
||||
|
||||
let matrix = tkhd::Matrix {
|
||||
a: reader.read_i32::<BigEndian>()?,
|
||||
b: reader.read_i32::<BigEndian>()?,
|
||||
u: reader.read_i32::<BigEndian>()?,
|
||||
c: reader.read_i32::<BigEndian>()?,
|
||||
d: reader.read_i32::<BigEndian>()?,
|
||||
v: reader.read_i32::<BigEndian>()?,
|
||||
x: reader.read_i32::<BigEndian>()?,
|
||||
y: reader.read_i32::<BigEndian>()?,
|
||||
w: reader.read_i32::<BigEndian>()?,
|
||||
a: reader.get_i32(),
|
||||
b: reader.get_i32(),
|
||||
u: reader.get_i32(),
|
||||
c: reader.get_i32(),
|
||||
d: reader.get_i32(),
|
||||
v: reader.get_i32(),
|
||||
x: reader.get_i32(),
|
||||
y: reader.get_i32(),
|
||||
w: reader.get_i32(),
|
||||
};
|
||||
|
||||
skip_bytes(reader, 24)?; // pre_defined = 0
|
||||
reader.skip(24);
|
||||
|
||||
let next_track_id = reader.read_u32::<BigEndian>()?;
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
let next_track_id = reader.get_u32();
|
||||
|
||||
Ok(MvhdBox {
|
||||
version,
|
||||
|
@ -147,12 +144,16 @@ impl<R: Read + Seek> ReadBox<&mut R> for MvhdBox {
|
|||
next_track_id,
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint() -> usize {
|
||||
100
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for MvhdBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
BoxHeader::new(Self::TYPE, size).write(writer)?;
|
||||
|
||||
write_box_header_ext(writer, self.version, self.flags)?;
|
||||
|
||||
|
@ -167,7 +168,7 @@ impl<W: Write> WriteBox<&mut W> for MvhdBox {
|
|||
writer.write_u32::<BigEndian>(self.timescale)?;
|
||||
writer.write_u32::<BigEndian>(self.duration as u32)?;
|
||||
} else {
|
||||
return Err(Error::InvalidData("version must be 0 or 1"));
|
||||
return Err(BoxError::InvalidData("version must be 0 or 1"));
|
||||
}
|
||||
writer.write_u32::<BigEndian>(self.rate.raw_value())?;
|
||||
|
||||
|
@ -199,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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use serde::Serialize;
|
||||
use std::io::{Read, Seek, Write};
|
||||
use std::io::Write;
|
||||
|
||||
use crate::mp4box::*;
|
||||
use crate::mp4box::{
|
||||
|
@ -55,9 +55,7 @@ impl StblBox {
|
|||
}
|
||||
|
||||
impl Mp4Box for StblBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
const TYPE: BoxType = BoxType::StblBox;
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
|
@ -73,10 +71,8 @@ impl Mp4Box for StblBox {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for StblBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
|
||||
impl BlockReader for StblBox {
|
||||
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
|
||||
let mut stsd = None;
|
||||
let mut stts = None;
|
||||
let mut ctts = None;
|
||||
|
@ -86,68 +82,63 @@ impl<R: Read + Seek> ReadBox<&mut R> for StblBox {
|
|||
let mut stco = None;
|
||||
let mut co64 = None;
|
||||
|
||||
let mut current = reader.stream_position()?;
|
||||
let end = start + size;
|
||||
while current < end {
|
||||
// Get box header.
|
||||
let header = BoxHeader::read(reader)?;
|
||||
let BoxHeader { name, size: s } = header;
|
||||
if s > size {
|
||||
return Err(Error::InvalidData(
|
||||
"stbl box contains a box with a larger size than it",
|
||||
));
|
||||
while let Some(mut bx) = reader.get_box()? {
|
||||
match bx.kind {
|
||||
BoxType::StsdBox => {
|
||||
stsd = Some(bx.read()?);
|
||||
}
|
||||
|
||||
match name {
|
||||
BoxType::StsdBox => {
|
||||
stsd = Some(StsdBox::read_box(reader, s)?);
|
||||
}
|
||||
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)?);
|
||||
co64 = Some(bx.read()?);
|
||||
}
|
||||
_ => {
|
||||
// XXX warn!()
|
||||
skip_box(reader, s)?;
|
||||
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
current = reader.stream_position()?;
|
||||
}
|
||||
|
||||
if stsd.is_none() {
|
||||
return Err(Error::BoxNotFound(BoxType::StsdBox));
|
||||
}
|
||||
if stts.is_none() {
|
||||
return Err(Error::BoxNotFound(BoxType::SttsBox));
|
||||
}
|
||||
if stsc.is_none() {
|
||||
return Err(Error::BoxNotFound(BoxType::StscBox));
|
||||
}
|
||||
if stsz.is_none() {
|
||||
return Err(Error::BoxNotFound(BoxType::StszBox));
|
||||
}
|
||||
if stco.is_none() && co64.is_none() {
|
||||
return Err(Error::Box2NotFound(BoxType::StcoBox, BoxType::Co64Box));
|
||||
return Err(BoxError::BoxNotFound(BoxType::StsdBox));
|
||||
}
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
if stts.is_none() {
|
||||
return Err(BoxError::BoxNotFound(BoxType::SttsBox));
|
||||
}
|
||||
|
||||
if stsc.is_none() {
|
||||
return Err(BoxError::BoxNotFound(BoxType::StscBox));
|
||||
}
|
||||
|
||||
if stsz.is_none() {
|
||||
return Err(BoxError::BoxNotFound(BoxType::StszBox));
|
||||
}
|
||||
|
||||
if stco.is_none() && co64.is_none() {
|
||||
return Err(BoxError::Box2NotFound(BoxType::StcoBox, BoxType::Co64Box));
|
||||
}
|
||||
|
||||
Ok(StblBox {
|
||||
stsd: stsd.unwrap(),
|
||||
|
@ -160,12 +151,16 @@ impl<R: Read + Seek> ReadBox<&mut R> for StblBox {
|
|||
co64,
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint() -> usize {
|
||||
StsdBox::size_hint() + SttsBox::size_hint() + StscBox::size_hint() + StszBox::size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for StblBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
BoxHeader::new(Self::TYPE, size).write(writer)?;
|
||||
|
||||
self.stsd.write_box(writer)?;
|
||||
self.stts.write_box(writer)?;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
use byteorder::{BigEndian, WriteBytesExt};
|
||||
use serde::Serialize;
|
||||
use std::io::{Read, Seek, Write};
|
||||
use std::io::Write;
|
||||
use std::mem::size_of;
|
||||
|
||||
use crate::mp4box::*;
|
||||
|
@ -14,6 +14,26 @@ pub struct StcoBox {
|
|||
pub entries: Vec<u32>,
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a StcoBox {
|
||||
type Item = u64;
|
||||
type IntoIter = std::iter::Map<std::iter::Copied<std::slice::Iter<'a, u32>>, fn(u32) -> u64>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.entries.iter().copied().map(Into::<u64>::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for StcoBox {
|
||||
type Item = u64;
|
||||
type IntoIter = std::iter::Map<std::vec::IntoIter<u32>, fn(u32) -> u64>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.entries.into_iter().map(Into::<u64>::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl StcoBox {
|
||||
pub fn get_type(&self) -> BoxType {
|
||||
BoxType::StcoBox
|
||||
|
@ -25,9 +45,7 @@ impl StcoBox {
|
|||
}
|
||||
|
||||
impl Mp4Box for StcoBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
const TYPE: BoxType = BoxType::StcoBox;
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
|
@ -43,46 +61,40 @@ impl Mp4Box for StcoBox {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for StcoBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
impl BlockReader for StcoBox {
|
||||
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
|
||||
let (version, flags) = read_box_header_ext(reader);
|
||||
|
||||
let (version, flags) = read_box_header_ext(reader)?;
|
||||
|
||||
let header_size = HEADER_SIZE + HEADER_EXT_SIZE;
|
||||
let other_size = size_of::<u32>(); // entry_count
|
||||
let entry_size = size_of::<u32>(); // chunk_offset
|
||||
let entry_count = reader.read_u32::<BigEndian>()?;
|
||||
if u64::from(entry_count)
|
||||
> size
|
||||
.saturating_sub(header_size)
|
||||
.saturating_sub(other_size as u64)
|
||||
/ entry_size as u64
|
||||
{
|
||||
return Err(Error::InvalidData(
|
||||
let entry_count = reader.get_u32();
|
||||
if entry_count as usize > reader.remaining() / entry_size {
|
||||
return Err(BoxError::InvalidData(
|
||||
"stco entry_count indicates more entries than could fit in the box",
|
||||
));
|
||||
}
|
||||
|
||||
let mut entries = Vec::with_capacity(entry_count as usize);
|
||||
for _i in 0..entry_count {
|
||||
let chunk_offset = reader.read_u32::<BigEndian>()?;
|
||||
let chunk_offset = reader.get_u32();
|
||||
entries.push(chunk_offset);
|
||||
}
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
|
||||
Ok(StcoBox {
|
||||
version,
|
||||
flags,
|
||||
entries,
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint() -> usize {
|
||||
8
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for StcoBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
BoxHeader::new(Self::TYPE, size).write(writer)?;
|
||||
|
||||
write_box_header_ext(writer, self.version, self.flags)?;
|
||||
|
||||
|
@ -117,7 +129,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 +141,12 @@ mod tests {
|
|||
src_box.write_box(&mut buf).unwrap();
|
||||
assert_eq!(buf.len(), src_box.box_size() as usize);
|
||||
|
||||
let mut reader = Cursor::new(&buf);
|
||||
let header = BoxHeader::read(&mut reader).unwrap();
|
||||
assert_eq!(header.name, BoxType::StcoBox);
|
||||
let mut reader = buf.as_slice();
|
||||
let header = BoxHeader::read_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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
use byteorder::{BigEndian, WriteBytesExt};
|
||||
use serde::Serialize;
|
||||
use std::io::{Read, Seek, Write};
|
||||
use std::io::Write;
|
||||
use std::mem::size_of;
|
||||
|
||||
use crate::mp4box::*;
|
||||
|
@ -24,7 +24,7 @@ impl StscBox {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize)]
|
||||
pub struct StscEntry {
|
||||
pub first_chunk: u32,
|
||||
pub samples_per_chunk: u32,
|
||||
|
@ -33,9 +33,7 @@ pub struct StscEntry {
|
|||
}
|
||||
|
||||
impl Mp4Box for StscBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
const TYPE: BoxType = BoxType::StscBox;
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
|
@ -51,32 +49,23 @@ impl Mp4Box for StscBox {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for StscBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
impl BlockReader for StscBox {
|
||||
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
|
||||
let (version, flags) = read_box_header_ext(reader);
|
||||
|
||||
let (version, flags) = read_box_header_ext(reader)?;
|
||||
|
||||
let header_size = HEADER_SIZE + HEADER_EXT_SIZE;
|
||||
let other_size = size_of::<u32>(); // entry_count
|
||||
let entry_size = size_of::<u32>() + size_of::<u32>() + size_of::<u32>(); // first_chunk + samples_per_chunk + sample_description_index
|
||||
let entry_count = reader.read_u32::<BigEndian>()?;
|
||||
if u64::from(entry_count)
|
||||
> size
|
||||
.saturating_sub(header_size)
|
||||
.saturating_sub(other_size as u64)
|
||||
/ entry_size as u64
|
||||
{
|
||||
return Err(Error::InvalidData(
|
||||
let entry_count = reader.get_u32();
|
||||
if entry_count as usize > reader.remaining() / entry_size {
|
||||
return Err(BoxError::InvalidData(
|
||||
"stsc entry_count indicates more entries than could fit in the box",
|
||||
));
|
||||
}
|
||||
let mut entries = Vec::with_capacity(entry_count as usize);
|
||||
for _ in 0..entry_count {
|
||||
let entry = StscEntry {
|
||||
first_chunk: reader.read_u32::<BigEndian>()?,
|
||||
samples_per_chunk: reader.read_u32::<BigEndian>()?,
|
||||
sample_description_index: reader.read_u32::<BigEndian>()?,
|
||||
first_chunk: reader.get_u32(),
|
||||
samples_per_chunk: reader.get_u32(),
|
||||
sample_description_index: reader.get_u32(),
|
||||
first_sample: 0,
|
||||
};
|
||||
entries.push(entry);
|
||||
|
@ -96,26 +85,28 @@ impl<R: Read + Seek> ReadBox<&mut R> for StscBox {
|
|||
.checked_sub(first_chunk)
|
||||
.and_then(|n| n.checked_mul(samples_per_chunk))
|
||||
.and_then(|n| n.checked_add(sample_id))
|
||||
.ok_or(Error::InvalidData(
|
||||
.ok_or(BoxError::InvalidData(
|
||||
"attempt to calculate stsc sample_id with overflow",
|
||||
))?;
|
||||
}
|
||||
}
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
|
||||
Ok(StscBox {
|
||||
version,
|
||||
flags,
|
||||
entries,
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint() -> usize {
|
||||
8
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for StscBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
BoxHeader::new(Self::TYPE, size).write(writer)?;
|
||||
|
||||
write_box_header_ext(writer, self.version, self.flags)?;
|
||||
|
||||
|
@ -134,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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)?);
|
||||
hev1 = Some(bx.read()?);
|
||||
}
|
||||
|
||||
BoxType::Vp09Box => {
|
||||
vp09 = Some(Vp09Box::read_box(reader, s)?);
|
||||
vp09 = Some(bx.read()?);
|
||||
}
|
||||
|
||||
BoxType::Mp4aBox => {
|
||||
mp4a = Some(Mp4aBox::read_box(reader, s)?);
|
||||
mp4a = Some(bx.read()?);
|
||||
}
|
||||
|
||||
BoxType::Tx3gBox => {
|
||||
tx3g = Some(Tx3gBox::read_box(reader, s)?);
|
||||
tx3g = Some(bx.read()?);
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
}
|
||||
|
||||
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)?;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
use byteorder::{BigEndian, WriteBytesExt};
|
||||
use serde::Serialize;
|
||||
use std::io::{Read, Seek, Write};
|
||||
use std::io::Write;
|
||||
use std::mem::size_of;
|
||||
|
||||
use crate::mp4box::*;
|
||||
|
@ -25,9 +25,7 @@ impl StssBox {
|
|||
}
|
||||
|
||||
impl Mp4Box for StssBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
const TYPE: BoxType = BoxType::StssBox;
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
|
@ -43,46 +41,40 @@ impl Mp4Box for StssBox {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for StssBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
impl BlockReader for StssBox {
|
||||
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
|
||||
let (version, flags) = read_box_header_ext(reader);
|
||||
|
||||
let (version, flags) = read_box_header_ext(reader)?;
|
||||
|
||||
let header_size = HEADER_SIZE + HEADER_EXT_SIZE;
|
||||
let other_size = size_of::<u32>(); // entry_count
|
||||
let entry_size = size_of::<u32>(); // sample_number
|
||||
let entry_count = reader.read_u32::<BigEndian>()?;
|
||||
if u64::from(entry_count)
|
||||
> size
|
||||
.saturating_sub(header_size)
|
||||
.saturating_sub(other_size as u64)
|
||||
/ entry_size as u64
|
||||
{
|
||||
return Err(Error::InvalidData(
|
||||
let entry_count = reader.get_u32();
|
||||
if entry_count as usize > reader.remaining() / entry_size {
|
||||
return Err(BoxError::InvalidData(
|
||||
"stss entry_count indicates more entries than could fit in the box",
|
||||
));
|
||||
}
|
||||
|
||||
let mut entries = Vec::with_capacity(entry_count as usize);
|
||||
for _i in 0..entry_count {
|
||||
let sample_number = reader.read_u32::<BigEndian>()?;
|
||||
let sample_number = reader.get_u32();
|
||||
entries.push(sample_number);
|
||||
}
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
|
||||
Ok(StssBox {
|
||||
version,
|
||||
flags,
|
||||
entries,
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint() -> usize {
|
||||
8
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for StssBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
BoxHeader::new(Self::TYPE, size).write(writer)?;
|
||||
|
||||
write_box_header_ext(writer, self.version, self.flags)?;
|
||||
|
||||
|
@ -99,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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
use byteorder::{BigEndian, WriteBytesExt};
|
||||
use serde::Serialize;
|
||||
use std::io::{Read, Seek, Write};
|
||||
use std::io::Write;
|
||||
use std::mem::size_of;
|
||||
|
||||
use crate::mp4box::*;
|
||||
|
@ -27,9 +27,7 @@ impl StszBox {
|
|||
}
|
||||
|
||||
impl Mp4Box for StszBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
const TYPE: BoxType = BoxType::StszBox;
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
|
@ -50,42 +48,31 @@ impl Mp4Box for StszBox {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for StszBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
impl BlockReader for StszBox {
|
||||
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
|
||||
let (version, flags) = read_box_header_ext(reader);
|
||||
|
||||
let (version, flags) = read_box_header_ext(reader)?;
|
||||
|
||||
let header_size = HEADER_SIZE + HEADER_EXT_SIZE;
|
||||
let other_size = size_of::<u32>() + size_of::<u32>(); // sample_size + sample_count
|
||||
let sample_size = reader.read_u32::<BigEndian>()?;
|
||||
let sample_size = reader.get_u32();
|
||||
let stsz_item_size = if sample_size == 0 {
|
||||
size_of::<u32>() // entry_size
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let sample_count = reader.read_u32::<BigEndian>()?;
|
||||
let sample_count = reader.get_u32();
|
||||
let mut sample_sizes = Vec::new();
|
||||
if sample_size == 0 {
|
||||
if u64::from(sample_count)
|
||||
> size
|
||||
.saturating_sub(header_size)
|
||||
.saturating_sub(other_size as u64)
|
||||
/ stsz_item_size as u64
|
||||
{
|
||||
return Err(Error::InvalidData(
|
||||
if sample_count as usize > reader.remaining() / stsz_item_size {
|
||||
return Err(BoxError::InvalidData(
|
||||
"stsz sample_count indicates more values than could fit in the box",
|
||||
));
|
||||
}
|
||||
sample_sizes.reserve(sample_count as usize);
|
||||
for _ in 0..sample_count {
|
||||
let sample_number = reader.read_u32::<BigEndian>()?;
|
||||
let sample_number = reader.get_u32();
|
||||
sample_sizes.push(sample_number);
|
||||
}
|
||||
}
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
|
||||
Ok(StszBox {
|
||||
version,
|
||||
flags,
|
||||
|
@ -94,12 +81,16 @@ impl<R: Read + Seek> ReadBox<&mut R> for StszBox {
|
|||
sample_sizes,
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint() -> usize {
|
||||
12
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for StszBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
BoxHeader::new(Self::TYPE, size).write(writer)?;
|
||||
|
||||
write_box_header_ext(writer, self.version, self.flags)?;
|
||||
|
||||
|
@ -107,7 +98,7 @@ impl<W: Write> WriteBox<&mut W> for StszBox {
|
|||
writer.write_u32::<BigEndian>(self.sample_count)?;
|
||||
if self.sample_size == 0 {
|
||||
if self.sample_count != self.sample_sizes.len() as u32 {
|
||||
return Err(Error::InvalidData("sample count out of sync"));
|
||||
return Err(BoxError::InvalidData("sample count out of sync"));
|
||||
}
|
||||
for sample_number in self.sample_sizes.iter() {
|
||||
writer.write_u32::<BigEndian>(*sample_number)?;
|
||||
|
@ -122,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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
use byteorder::{BigEndian, WriteBytesExt};
|
||||
use serde::Serialize;
|
||||
use std::io::{Read, Seek, Write};
|
||||
use std::io::Write;
|
||||
use std::mem::size_of;
|
||||
|
||||
use crate::mp4box::*;
|
||||
|
@ -31,9 +31,7 @@ pub struct SttsEntry {
|
|||
}
|
||||
|
||||
impl Mp4Box for SttsBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
const TYPE: BoxType = BoxType::SttsBox;
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
|
@ -49,49 +47,44 @@ impl Mp4Box for SttsBox {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for SttsBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
impl BlockReader for SttsBox {
|
||||
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
|
||||
let (version, flags) = read_box_header_ext(reader);
|
||||
|
||||
let (version, flags) = read_box_header_ext(reader)?;
|
||||
|
||||
let header_size = HEADER_SIZE + HEADER_EXT_SIZE;
|
||||
let other_size = size_of::<u32>(); // entry_count
|
||||
let entry_size = size_of::<u32>() + size_of::<u32>(); // sample_count + sample_delta
|
||||
let entry_count = reader.read_u32::<BigEndian>()?;
|
||||
if u64::from(entry_count)
|
||||
> size
|
||||
.saturating_sub(header_size)
|
||||
.saturating_sub(other_size as u64)
|
||||
/ entry_size as u64
|
||||
{
|
||||
return Err(Error::InvalidData(
|
||||
let entry_count = reader.get_u32();
|
||||
|
||||
if entry_count as usize > reader.remaining() / entry_size {
|
||||
return Err(BoxError::InvalidData(
|
||||
"stts entry_count indicates more entries than could fit in the box",
|
||||
));
|
||||
}
|
||||
|
||||
let mut entries = Vec::with_capacity(entry_count as usize);
|
||||
for _i in 0..entry_count {
|
||||
let entry = SttsEntry {
|
||||
sample_count: reader.read_u32::<BigEndian>()?,
|
||||
sample_delta: reader.read_u32::<BigEndian>()?,
|
||||
sample_count: reader.get_u32(),
|
||||
sample_delta: reader.get_u32(),
|
||||
};
|
||||
entries.push(entry);
|
||||
}
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
|
||||
Ok(SttsBox {
|
||||
version,
|
||||
flags,
|
||||
entries,
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint() -> usize {
|
||||
8
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for SttsBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
BoxHeader::new(Self::TYPE, size).write(writer)?;
|
||||
|
||||
write_box_header_ext(writer, self.version, self.flags)?;
|
||||
|
||||
|
@ -109,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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
use byteorder::{BigEndian, WriteBytesExt};
|
||||
use serde::Serialize;
|
||||
use std::io::{Read, Seek, Write};
|
||||
use std::io::Write;
|
||||
|
||||
use crate::mp4box::*;
|
||||
|
||||
|
@ -28,9 +28,7 @@ impl TfdtBox {
|
|||
}
|
||||
|
||||
impl Mp4Box for TfdtBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
const TYPE: BoxType = BoxType::TfdtBox;
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
|
@ -46,34 +44,34 @@ impl Mp4Box for TfdtBox {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for TfdtBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
|
||||
let (version, flags) = read_box_header_ext(reader)?;
|
||||
impl BlockReader for TfdtBox {
|
||||
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
|
||||
let (version, flags) = read_box_header_ext(reader);
|
||||
|
||||
let base_media_decode_time = if version == 1 {
|
||||
reader.read_u64::<BigEndian>()?
|
||||
reader.get_u64()
|
||||
} else if version == 0 {
|
||||
reader.read_u32::<BigEndian>()? as u64
|
||||
reader.get_u32() as u64
|
||||
} else {
|
||||
return Err(Error::InvalidData("version must be 0 or 1"));
|
||||
return Err(BoxError::InvalidData("version must be 0 or 1"));
|
||||
};
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
|
||||
Ok(TfdtBox {
|
||||
version,
|
||||
flags,
|
||||
base_media_decode_time,
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint() -> usize {
|
||||
8
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for TfdtBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
BoxHeader::new(Self::TYPE, size).write(writer)?;
|
||||
|
||||
write_box_header_ext(writer, self.version, self.flags)?;
|
||||
|
||||
|
@ -82,7 +80,7 @@ impl<W: Write> WriteBox<&mut W> for TfdtBox {
|
|||
} else if self.version == 0 {
|
||||
writer.write_u32::<BigEndian>(self.base_media_decode_time as u32)?;
|
||||
} else {
|
||||
return Err(Error::InvalidData("version must be 0 or 1"));
|
||||
return Err(BoxError::InvalidData("version must be 0 or 1"));
|
||||
}
|
||||
|
||||
Ok(size)
|
||||
|
@ -93,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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
use byteorder::{BigEndian, WriteBytesExt};
|
||||
use serde::Serialize;
|
||||
use std::io::{Read, Seek, Write};
|
||||
use std::io::Write;
|
||||
|
||||
use crate::mp4box::*;
|
||||
|
||||
|
@ -117,9 +117,7 @@ impl TkhdBox {
|
|||
}
|
||||
|
||||
impl Mp4Box for TkhdBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
const TYPE: BoxType = BoxType::TkhdBox;
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
|
@ -145,53 +143,52 @@ impl Mp4Box for TkhdBox {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for TkhdBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
|
||||
let (version, flags) = read_box_header_ext(reader)?;
|
||||
impl BlockReader for TkhdBox {
|
||||
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
|
||||
let (version, flags) = read_box_header_ext(reader);
|
||||
|
||||
let (creation_time, modification_time, track_id, _, duration) = if version == 1 {
|
||||
(
|
||||
reader.read_u64::<BigEndian>()?,
|
||||
reader.read_u64::<BigEndian>()?,
|
||||
reader.read_u32::<BigEndian>()?,
|
||||
reader.read_u32::<BigEndian>()?,
|
||||
reader.read_u64::<BigEndian>()?,
|
||||
reader.get_u64(),
|
||||
reader.get_u64(),
|
||||
reader.get_u32(),
|
||||
reader.get_u32(),
|
||||
reader.get_u64(),
|
||||
)
|
||||
} else if version == 0 {
|
||||
(
|
||||
reader.read_u32::<BigEndian>()? as u64,
|
||||
reader.read_u32::<BigEndian>()? as u64,
|
||||
reader.read_u32::<BigEndian>()?,
|
||||
reader.read_u32::<BigEndian>()?,
|
||||
reader.read_u32::<BigEndian>()? as u64,
|
||||
reader.get_u32() as u64,
|
||||
reader.get_u32() as u64,
|
||||
reader.get_u32(),
|
||||
reader.get_u32(),
|
||||
reader.get_u32() as u64,
|
||||
)
|
||||
} else {
|
||||
return Err(Error::InvalidData("version must be 0 or 1"));
|
||||
return Err(BoxError::InvalidData("version must be 0 or 1"));
|
||||
};
|
||||
reader.read_u64::<BigEndian>()?; // reserved
|
||||
let layer = reader.read_u16::<BigEndian>()?;
|
||||
let alternate_group = reader.read_u16::<BigEndian>()?;
|
||||
let volume = FixedPointU8::new_raw(reader.read_u16::<BigEndian>()?);
|
||||
|
||||
reader.read_u16::<BigEndian>()?; // reserved
|
||||
reader.get_u64(); // reserved
|
||||
|
||||
let layer = reader.get_u16();
|
||||
let alternate_group = reader.get_u16();
|
||||
let volume = FixedPointU8::new_raw(reader.get_u16());
|
||||
|
||||
reader.get_u16(); // reserved
|
||||
|
||||
let matrix = Matrix {
|
||||
a: reader.read_i32::<BigEndian>()?,
|
||||
b: reader.read_i32::<BigEndian>()?,
|
||||
u: reader.read_i32::<BigEndian>()?,
|
||||
c: reader.read_i32::<BigEndian>()?,
|
||||
d: reader.read_i32::<BigEndian>()?,
|
||||
v: reader.read_i32::<BigEndian>()?,
|
||||
x: reader.read_i32::<BigEndian>()?,
|
||||
y: reader.read_i32::<BigEndian>()?,
|
||||
w: reader.read_i32::<BigEndian>()?,
|
||||
a: reader.get_i32(),
|
||||
b: reader.get_i32(),
|
||||
u: reader.get_i32(),
|
||||
c: reader.get_i32(),
|
||||
d: reader.get_i32(),
|
||||
v: reader.get_i32(),
|
||||
x: reader.get_i32(),
|
||||
y: reader.get_i32(),
|
||||
w: reader.get_i32(),
|
||||
};
|
||||
|
||||
let width = FixedPointU16::new_raw(reader.read_u32::<BigEndian>()?);
|
||||
let height = FixedPointU16::new_raw(reader.read_u32::<BigEndian>()?);
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
let width = FixedPointU16::new_raw(reader.get_u32());
|
||||
let height = FixedPointU16::new_raw(reader.get_u32());
|
||||
|
||||
Ok(TkhdBox {
|
||||
version,
|
||||
|
@ -208,12 +205,16 @@ impl<R: Read + Seek> ReadBox<&mut R> for TkhdBox {
|
|||
height,
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint() -> usize {
|
||||
84
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for TkhdBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
BoxHeader::new(Self::TYPE, size).write(writer)?;
|
||||
|
||||
write_box_header_ext(writer, self.version, self.flags)?;
|
||||
|
||||
|
@ -230,7 +231,7 @@ impl<W: Write> WriteBox<&mut W> for TkhdBox {
|
|||
writer.write_u32::<BigEndian>(0)?; // reserved
|
||||
writer.write_u32::<BigEndian>(self.duration as u32)?;
|
||||
} else {
|
||||
return Err(Error::InvalidData("version must be 0 or 1"));
|
||||
return Err(BoxError::InvalidData("version must be 0 or 1"));
|
||||
}
|
||||
|
||||
writer.write_u64::<BigEndian>(0)?; // reserved
|
||||
|
@ -261,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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use serde::Serialize;
|
||||
use std::io::{Read, Seek, Write};
|
||||
use std::io::Write;
|
||||
|
||||
use crate::mp4box::*;
|
||||
use crate::mp4box::{tfdt::TfdtBox, tfhd::TfhdBox, trun::TrunBox};
|
||||
|
@ -12,10 +12,6 @@ pub struct TrafBox {
|
|||
}
|
||||
|
||||
impl TrafBox {
|
||||
pub fn get_type(&self) -> BoxType {
|
||||
BoxType::TrafBox
|
||||
}
|
||||
|
||||
pub fn get_size(&self) -> u64 {
|
||||
let mut size = HEADER_SIZE;
|
||||
size += self.tfhd.box_size();
|
||||
|
@ -30,9 +26,7 @@ impl TrafBox {
|
|||
}
|
||||
|
||||
impl Mp4Box for TrafBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
const TYPE: BoxType = BoxType::TrafBox;
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
|
@ -48,63 +42,30 @@ impl Mp4Box for TrafBox {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for TrafBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
|
||||
let mut tfhd = None;
|
||||
let mut tfdt = None;
|
||||
let mut trun = None;
|
||||
|
||||
let mut current = reader.stream_position()?;
|
||||
let end = start + size;
|
||||
while current < end {
|
||||
// Get box header.
|
||||
let header = BoxHeader::read(reader)?;
|
||||
let BoxHeader { name, size: s } = header;
|
||||
if s > size {
|
||||
return Err(Error::InvalidData(
|
||||
"traf box contains a box with a larger size than it",
|
||||
));
|
||||
}
|
||||
|
||||
match name {
|
||||
BoxType::TfhdBox => {
|
||||
tfhd = Some(TfhdBox::read_box(reader, s)?);
|
||||
}
|
||||
BoxType::TfdtBox => {
|
||||
tfdt = Some(TfdtBox::read_box(reader, s)?);
|
||||
}
|
||||
BoxType::TrunBox => {
|
||||
trun = Some(TrunBox::read_box(reader, s)?);
|
||||
}
|
||||
_ => {
|
||||
// XXX warn!()
|
||||
skip_box(reader, s)?;
|
||||
}
|
||||
}
|
||||
|
||||
current = reader.stream_position()?;
|
||||
}
|
||||
impl BlockReader for TrafBox {
|
||||
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
|
||||
let (tfhd, tfdt, trun) = reader.try_find_box3()?;
|
||||
|
||||
if tfhd.is_none() {
|
||||
return Err(Error::BoxNotFound(BoxType::TfhdBox));
|
||||
return Err(BoxError::BoxNotFound(BoxType::TfhdBox));
|
||||
}
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
|
||||
Ok(TrafBox {
|
||||
tfhd: tfhd.unwrap(),
|
||||
tfdt,
|
||||
trun,
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint() -> usize {
|
||||
TfhdBox::size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for TrafBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
BoxHeader::new(Self::TYPE, size).write(writer)?;
|
||||
|
||||
self.tfhd.write_box(writer)?;
|
||||
if let Some(ref tfdt) = self.tfdt {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use serde::Serialize;
|
||||
use std::io::{Read, Seek, Write};
|
||||
use std::io::Write;
|
||||
|
||||
use crate::meta::MetaBox;
|
||||
use crate::mp4box::*;
|
||||
|
@ -8,14 +8,13 @@ use crate::mp4box::{edts::EdtsBox, mdia::MdiaBox, tkhd::TkhdBox};
|
|||
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
|
||||
pub struct TrakBox {
|
||||
pub tkhd: TkhdBox,
|
||||
pub mdia: MdiaBox,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub edts: Option<EdtsBox>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub meta: Option<MetaBox>,
|
||||
|
||||
pub mdia: MdiaBox,
|
||||
}
|
||||
|
||||
impl TrakBox {
|
||||
|
@ -32,12 +31,180 @@ impl TrakBox {
|
|||
size += self.mdia.box_size();
|
||||
size
|
||||
}
|
||||
|
||||
pub(crate) fn stsc_index(&self, sample_id: u32) -> Result<usize> {
|
||||
if self.mdia.minf.stbl.stsc.entries.is_empty() {
|
||||
return Err(BoxError::InvalidData("no stsc entries"));
|
||||
}
|
||||
|
||||
for (i, entry) in self.mdia.minf.stbl.stsc.entries.iter().enumerate() {
|
||||
if sample_id < entry.first_sample {
|
||||
return if i == 0 {
|
||||
Err(BoxError::InvalidData("sample not found"))
|
||||
} else {
|
||||
Ok(i - 1)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Ok(self.mdia.minf.stbl.stsc.entries.len() - 1)
|
||||
}
|
||||
|
||||
pub(crate) fn chunk_offset(&self, chunk_id: u32) -> Result<u64> {
|
||||
if self.mdia.minf.stbl.stco.is_none() && self.mdia.minf.stbl.co64.is_none() {
|
||||
return Err(BoxError::InvalidData("must have either stco or co64 boxes"));
|
||||
}
|
||||
|
||||
if let Some(ref stco) = self.mdia.minf.stbl.stco {
|
||||
if let Some(offset) = stco.entries.get(chunk_id as usize - 1) {
|
||||
return Ok(*offset as u64);
|
||||
} else {
|
||||
return Err(BoxError::EntryInStblNotFound(
|
||||
self.tkhd.track_id,
|
||||
BoxType::StcoBox,
|
||||
chunk_id,
|
||||
));
|
||||
}
|
||||
} else if let Some(ref co64) = self.mdia.minf.stbl.co64 {
|
||||
if let Some(offset) = co64.entries.get(chunk_id as usize - 1) {
|
||||
return Ok(*offset);
|
||||
} else {
|
||||
return Err(BoxError::EntryInStblNotFound(
|
||||
self.tkhd.track_id,
|
||||
BoxType::Co64Box,
|
||||
chunk_id,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Err(BoxError::Box2NotFound(BoxType::StcoBox, BoxType::Co64Box))
|
||||
}
|
||||
|
||||
pub(crate) fn sample_size(&self, sample_id: u32) -> Result<u32> {
|
||||
let stsz = &self.mdia.minf.stbl.stsz;
|
||||
|
||||
if stsz.sample_size > 0 {
|
||||
return Ok(stsz.sample_size);
|
||||
}
|
||||
|
||||
if let Some(size) = stsz.sample_sizes.get(sample_id as usize - 1) {
|
||||
Ok(*size)
|
||||
} else {
|
||||
Err(BoxError::EntryInStblNotFound(
|
||||
self.tkhd.track_id,
|
||||
BoxType::StszBox,
|
||||
sample_id,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn sample_offset(&self, sample_id: u32) -> Result<u64> {
|
||||
let stsc_index = self.stsc_index(sample_id)?;
|
||||
|
||||
let stsc = &self.mdia.minf.stbl.stsc;
|
||||
let stsc_entry = stsc.entries.get(stsc_index).unwrap();
|
||||
|
||||
let first_chunk = stsc_entry.first_chunk;
|
||||
let first_sample = stsc_entry.first_sample;
|
||||
let samples_per_chunk = stsc_entry.samples_per_chunk;
|
||||
|
||||
let chunk_id = sample_id
|
||||
.checked_sub(first_sample)
|
||||
.map(|n| n / samples_per_chunk)
|
||||
.and_then(|n| n.checked_add(first_chunk))
|
||||
.ok_or(BoxError::InvalidData(
|
||||
"attempt to calculate stsc chunk_id with overflow",
|
||||
))?;
|
||||
|
||||
let chunk_offset = self.chunk_offset(chunk_id)?;
|
||||
|
||||
let first_sample_in_chunk = sample_id - (sample_id - first_sample) % samples_per_chunk;
|
||||
|
||||
let mut sample_offset = 0;
|
||||
for i in first_sample_in_chunk..sample_id {
|
||||
sample_offset += self.sample_size(i)?;
|
||||
}
|
||||
|
||||
Ok(chunk_offset + sample_offset as u64)
|
||||
}
|
||||
|
||||
pub(crate) fn sample_time(&self, sample_id: u32) -> Result<(u64, u32)> {
|
||||
let stts = &self.mdia.minf.stbl.stts;
|
||||
|
||||
let mut sample_count: u32 = 1;
|
||||
let mut elapsed = 0;
|
||||
|
||||
for entry in stts.entries.iter() {
|
||||
let new_sample_count =
|
||||
sample_count
|
||||
.checked_add(entry.sample_count)
|
||||
.ok_or(BoxError::InvalidData(
|
||||
"attempt to sum stts entries sample_count with overflow",
|
||||
))?;
|
||||
|
||||
if sample_id < new_sample_count {
|
||||
let start_time =
|
||||
(sample_id - sample_count) as u64 * entry.sample_delta as u64 + elapsed;
|
||||
return Ok((start_time, entry.sample_delta));
|
||||
}
|
||||
|
||||
sample_count = new_sample_count;
|
||||
elapsed += entry.sample_count as u64 * entry.sample_delta as u64;
|
||||
}
|
||||
|
||||
Err(BoxError::EntryInStblNotFound(
|
||||
self.tkhd.track_id,
|
||||
BoxType::SttsBox,
|
||||
sample_id,
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn ctts_index(&self, sample_id: u32) -> Result<(usize, u32)> {
|
||||
let ctts = self.mdia.minf.stbl.ctts.as_ref().unwrap();
|
||||
let mut sample_count: u32 = 1;
|
||||
for (i, entry) in ctts.entries.iter().enumerate() {
|
||||
let next_sample_count =
|
||||
sample_count
|
||||
.checked_add(entry.sample_count)
|
||||
.ok_or(BoxError::InvalidData(
|
||||
"attempt to sum ctts entries sample_count with overflow",
|
||||
))?;
|
||||
if sample_id < next_sample_count {
|
||||
return Ok((i, sample_count));
|
||||
}
|
||||
sample_count = next_sample_count;
|
||||
}
|
||||
|
||||
Err(BoxError::EntryInStblNotFound(
|
||||
self.tkhd.track_id,
|
||||
BoxType::CttsBox,
|
||||
sample_id,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn sample_rendering_offset(&self, sample_id: u32) -> i32 {
|
||||
if let Some(ref ctts) = self.mdia.minf.stbl.ctts {
|
||||
if let Ok((ctts_index, _)) = self.ctts_index(sample_id) {
|
||||
let ctts_entry = ctts.entries.get(ctts_index).unwrap();
|
||||
return ctts_entry.sample_offset;
|
||||
}
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn sample_is_sync(&self, sample_id: u32) -> bool {
|
||||
if let Some(ref stss) = self.mdia.minf.stbl.stss {
|
||||
stss.entries.binary_search(&sample_id).is_ok()
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mp4Box for TrakBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
const TYPE: BoxType = BoxType::TrakBox;
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
|
@ -53,57 +220,17 @@ impl Mp4Box for TrakBox {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for TrakBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
|
||||
let mut tkhd = None;
|
||||
let mut edts = None;
|
||||
let mut meta = None;
|
||||
let mut mdia = None;
|
||||
|
||||
let mut current = reader.stream_position()?;
|
||||
let end = start + size;
|
||||
while current < end {
|
||||
// Get box header.
|
||||
let header = BoxHeader::read(reader)?;
|
||||
let BoxHeader { name, size: s } = header;
|
||||
if s > size {
|
||||
return Err(Error::InvalidData(
|
||||
"trak box contains a box with a larger size than it",
|
||||
));
|
||||
}
|
||||
|
||||
match name {
|
||||
BoxType::TkhdBox => {
|
||||
tkhd = Some(TkhdBox::read_box(reader, s)?);
|
||||
}
|
||||
BoxType::EdtsBox => {
|
||||
edts = Some(EdtsBox::read_box(reader, s)?);
|
||||
}
|
||||
BoxType::MetaBox => {
|
||||
meta = Some(MetaBox::read_box(reader, s)?);
|
||||
}
|
||||
BoxType::MdiaBox => {
|
||||
mdia = Some(MdiaBox::read_box(reader, s)?);
|
||||
}
|
||||
_ => {
|
||||
// XXX warn!()
|
||||
skip_box(reader, s)?;
|
||||
}
|
||||
}
|
||||
|
||||
current = reader.stream_position()?;
|
||||
}
|
||||
impl BlockReader for TrakBox {
|
||||
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
|
||||
let (tkhd, edts, meta, mdia) = reader.try_find_box4()?;
|
||||
|
||||
if tkhd.is_none() {
|
||||
return Err(Error::BoxNotFound(BoxType::TkhdBox));
|
||||
}
|
||||
if mdia.is_none() {
|
||||
return Err(Error::BoxNotFound(BoxType::MdiaBox));
|
||||
return Err(BoxError::BoxNotFound(BoxType::TkhdBox));
|
||||
}
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
if mdia.is_none() {
|
||||
return Err(BoxError::BoxNotFound(BoxType::MdiaBox));
|
||||
}
|
||||
|
||||
Ok(TrakBox {
|
||||
tkhd: tkhd.unwrap(),
|
||||
|
@ -112,12 +239,16 @@ impl<R: Read + Seek> ReadBox<&mut R> for TrakBox {
|
|||
mdia: mdia.unwrap(),
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint() -> usize {
|
||||
TkhdBox::size_hint() + MdiaBox::size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for TrakBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
BoxHeader::new(Self::TYPE, size).write(writer)?;
|
||||
|
||||
self.tkhd.write_box(writer)?;
|
||||
if let Some(ref edts) = self.edts {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||
use byteorder::{BigEndian, WriteBytesExt};
|
||||
use serde::Serialize;
|
||||
use std::io::{Read, Seek, Write};
|
||||
use std::io::Write;
|
||||
use std::mem::size_of;
|
||||
|
||||
use crate::mp4box::*;
|
||||
|
@ -15,10 +15,13 @@ pub struct TrunBox {
|
|||
|
||||
#[serde(skip_serializing)]
|
||||
pub sample_durations: Vec<u32>,
|
||||
|
||||
#[serde(skip_serializing)]
|
||||
pub sample_sizes: Vec<u32>,
|
||||
|
||||
#[serde(skip_serializing)]
|
||||
pub sample_flags: Vec<u32>,
|
||||
|
||||
#[serde(skip_serializing)]
|
||||
pub sample_cts: Vec<u32>,
|
||||
}
|
||||
|
@ -60,9 +63,7 @@ impl TrunBox {
|
|||
}
|
||||
|
||||
impl Mp4Box for TrunBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
const TYPE: BoxType = BoxType::TrunBox;
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
|
@ -78,31 +79,25 @@ impl Mp4Box for TrunBox {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for TrunBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
impl BlockReader for TrunBox {
|
||||
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
|
||||
let (version, flags) = read_box_header_ext(reader);
|
||||
|
||||
let (version, flags) = read_box_header_ext(reader)?;
|
||||
|
||||
let header_size = HEADER_SIZE + HEADER_EXT_SIZE;
|
||||
let other_size = size_of::<u32>() // sample_count
|
||||
+ if TrunBox::FLAG_DATA_OFFSET & flags > 0 { size_of::<i32>() } else { 0 } // data_offset
|
||||
+ if TrunBox::FLAG_FIRST_SAMPLE_FLAGS & flags > 0 { size_of::<u32>() } else { 0 }; // first_sample_flags
|
||||
let sample_size = if TrunBox::FLAG_SAMPLE_DURATION & flags > 0 { size_of::<u32>() } else { 0 } // sample_duration
|
||||
+ if TrunBox::FLAG_SAMPLE_SIZE & flags > 0 { size_of::<u32>() } else { 0 } // sample_size
|
||||
+ if TrunBox::FLAG_SAMPLE_FLAGS & flags > 0 { size_of::<u32>() } else { 0 } // sample_flags
|
||||
+ if TrunBox::FLAG_SAMPLE_CTS & flags > 0 { size_of::<u32>() } else { 0 }; // sample_composition_time_offset
|
||||
|
||||
let sample_count = reader.read_u32::<BigEndian>()?;
|
||||
let sample_count = reader.get_u32();
|
||||
|
||||
let data_offset = if TrunBox::FLAG_DATA_OFFSET & flags > 0 {
|
||||
Some(reader.read_i32::<BigEndian>()?)
|
||||
Some(reader.try_get_i32()?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let first_sample_flags = if TrunBox::FLAG_FIRST_SAMPLE_FLAGS & flags > 0 {
|
||||
Some(reader.read_u32::<BigEndian>()?)
|
||||
Some(reader.try_get_u32()?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
@ -111,52 +106,51 @@ impl<R: Read + Seek> ReadBox<&mut R> for TrunBox {
|
|||
let mut sample_sizes = Vec::new();
|
||||
let mut sample_flags = Vec::new();
|
||||
let mut sample_cts = Vec::new();
|
||||
if u64::from(sample_count) * sample_size as u64
|
||||
> size
|
||||
.saturating_sub(header_size)
|
||||
.saturating_sub(other_size as u64)
|
||||
{
|
||||
return Err(Error::InvalidData(
|
||||
|
||||
if sample_count as usize * sample_size > reader.remaining() {
|
||||
return Err(BoxError::InvalidData(
|
||||
"trun sample_count indicates more values than could fit in the box",
|
||||
));
|
||||
}
|
||||
|
||||
if TrunBox::FLAG_SAMPLE_DURATION & flags > 0 {
|
||||
sample_durations.reserve(sample_count as usize);
|
||||
}
|
||||
|
||||
if TrunBox::FLAG_SAMPLE_SIZE & flags > 0 {
|
||||
sample_sizes.reserve(sample_count as usize);
|
||||
}
|
||||
|
||||
if TrunBox::FLAG_SAMPLE_FLAGS & flags > 0 {
|
||||
sample_flags.reserve(sample_count as usize);
|
||||
}
|
||||
|
||||
if TrunBox::FLAG_SAMPLE_CTS & flags > 0 {
|
||||
sample_cts.reserve(sample_count as usize);
|
||||
}
|
||||
|
||||
for _ in 0..sample_count {
|
||||
if TrunBox::FLAG_SAMPLE_DURATION & flags > 0 {
|
||||
let duration = reader.read_u32::<BigEndian>()?;
|
||||
let duration = reader.get_u32();
|
||||
sample_durations.push(duration);
|
||||
}
|
||||
|
||||
if TrunBox::FLAG_SAMPLE_SIZE & flags > 0 {
|
||||
let sample_size = reader.read_u32::<BigEndian>()?;
|
||||
let sample_size = reader.get_u32();
|
||||
sample_sizes.push(sample_size);
|
||||
}
|
||||
|
||||
if TrunBox::FLAG_SAMPLE_FLAGS & flags > 0 {
|
||||
let sample_flag = reader.read_u32::<BigEndian>()?;
|
||||
let sample_flag = reader.get_u32();
|
||||
sample_flags.push(sample_flag);
|
||||
}
|
||||
|
||||
if TrunBox::FLAG_SAMPLE_CTS & flags > 0 {
|
||||
let cts = reader.read_u32::<BigEndian>()?;
|
||||
let cts = reader.get_u32();
|
||||
sample_cts.push(cts);
|
||||
}
|
||||
}
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
|
||||
Ok(TrunBox {
|
||||
version,
|
||||
flags,
|
||||
|
@ -169,12 +163,16 @@ impl<R: Read + Seek> ReadBox<&mut R> for TrunBox {
|
|||
sample_cts,
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint() -> usize {
|
||||
8
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for TrunBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
BoxHeader::new(Self::TYPE, size).write(writer)?;
|
||||
|
||||
write_box_header_ext(writer, self.version, self.flags)?;
|
||||
|
||||
|
@ -186,7 +184,7 @@ impl<W: Write> WriteBox<&mut W> for TrunBox {
|
|||
writer.write_u32::<BigEndian>(v)?;
|
||||
}
|
||||
if self.sample_count != self.sample_sizes.len() as u32 {
|
||||
return Err(Error::InvalidData("sample count out of sync"));
|
||||
return Err(BoxError::InvalidData("sample count out of sync"));
|
||||
}
|
||||
for i in 0..self.sample_count as usize {
|
||||
if TrunBox::FLAG_SAMPLE_DURATION & self.flags > 0 {
|
||||
|
@ -211,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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)?;
|
||||
|
||||
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",
|
||||
));
|
||||
impl BlockReader for UdtaBox {
|
||||
fn read_block<'a>(reader: &mut impl Reader<'a>) -> Result<Self> {
|
||||
Ok(UdtaBox {
|
||||
meta: reader.try_find_box()?,
|
||||
})
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
19
src/stream.rs
Normal file
19
src/stream.rs
Normal 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!()
|
||||
// }
|
||||
// }
|
122
src/track.rs
122
src/track.rs
|
@ -89,7 +89,7 @@ impl From<Vp9Config> for TrackConfig {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Mp4Track {
|
||||
pub trak: TrakBox,
|
||||
pub trafs: Vec<TrafBox>,
|
||||
|
@ -114,8 +114,8 @@ impl Mp4Track {
|
|||
self.trak.tkhd.track_id
|
||||
}
|
||||
|
||||
pub fn track_type(&self) -> Result<TrackType> {
|
||||
TrackType::try_from(&self.trak.mdia.hdlr.handler_type)
|
||||
pub fn track_type(&self) -> TrackType {
|
||||
TrackType::from(&self.trak.mdia.hdlr.handler_type)
|
||||
}
|
||||
|
||||
pub fn media_type(&self) -> Result<MediaType> {
|
||||
|
@ -130,7 +130,7 @@ impl Mp4Track {
|
|||
} else if self.trak.mdia.minf.stbl.stsd.tx3g.is_some() {
|
||||
Ok(MediaType::TTXT)
|
||||
} else {
|
||||
Err(Error::InvalidData("unsupported media type"))
|
||||
Err(BoxError::InvalidData("unsupported media type"))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,7 +146,7 @@ impl Mp4Track {
|
|||
} else if self.trak.mdia.minf.stbl.stsd.tx3g.is_some() {
|
||||
Ok(FourCC::from(BoxType::Tx3gBox))
|
||||
} else {
|
||||
Err(Error::InvalidData("unsupported sample entry box"))
|
||||
Err(BoxError::InvalidData("unsupported sample entry box"))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -180,10 +180,16 @@ impl Mp4Track {
|
|||
if let Some(ref esds) = mp4a.esds {
|
||||
SampleFreqIndex::try_from(esds.es_desc.dec_config.dec_specific.freq_index)
|
||||
} else {
|
||||
Err(Error::BoxInStblNotFound(self.track_id(), BoxType::EsdsBox))
|
||||
Err(BoxError::BoxInStblNotFound(
|
||||
self.track_id(),
|
||||
BoxType::EsdsBox,
|
||||
))
|
||||
}
|
||||
} else {
|
||||
Err(Error::BoxInStblNotFound(self.track_id(), BoxType::Mp4aBox))
|
||||
Err(BoxError::BoxInStblNotFound(
|
||||
self.track_id(),
|
||||
BoxType::Mp4aBox,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -192,10 +198,16 @@ impl Mp4Track {
|
|||
if let Some(ref esds) = mp4a.esds {
|
||||
ChannelConfig::try_from(esds.es_desc.dec_config.dec_specific.chan_conf)
|
||||
} else {
|
||||
Err(Error::BoxInStblNotFound(self.track_id(), BoxType::EsdsBox))
|
||||
Err(BoxError::BoxInStblNotFound(
|
||||
self.track_id(),
|
||||
BoxType::EsdsBox,
|
||||
))
|
||||
}
|
||||
} else {
|
||||
Err(Error::BoxInStblNotFound(self.track_id(), BoxType::Mp4aBox))
|
||||
Err(BoxError::BoxInStblNotFound(
|
||||
self.track_id(),
|
||||
BoxType::Mp4aBox,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -255,37 +267,46 @@ impl Mp4Track {
|
|||
avc1.avcc.profile_compatibility,
|
||||
))
|
||||
} else {
|
||||
Err(Error::BoxInStblNotFound(self.track_id(), BoxType::Avc1Box))
|
||||
Err(BoxError::BoxInStblNotFound(
|
||||
self.track_id(),
|
||||
BoxType::Avc1Box,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
None => Err(BoxError::EntryInStblNotFound(
|
||||
self.track_id(),
|
||||
BoxType::AvcCBox,
|
||||
0,
|
||||
)),
|
||||
}
|
||||
} else {
|
||||
Err(Error::BoxInStblNotFound(self.track_id(), BoxType::Avc1Box))
|
||||
Err(BoxError::BoxInStblNotFound(
|
||||
self.track_id(),
|
||||
BoxType::Avc1Box,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
None => Err(BoxError::EntryInStblNotFound(
|
||||
self.track_id(),
|
||||
BoxType::AvcCBox,
|
||||
0,
|
||||
)),
|
||||
}
|
||||
} else {
|
||||
Err(Error::BoxInStblNotFound(self.track_id(), BoxType::Avc1Box))
|
||||
Err(BoxError::BoxInStblNotFound(
|
||||
self.track_id(),
|
||||
BoxType::Avc1Box,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -294,21 +315,27 @@ impl Mp4Track {
|
|||
if let Some(ref esds) = mp4a.esds {
|
||||
AudioObjectType::try_from(esds.es_desc.dec_config.dec_specific.profile)
|
||||
} else {
|
||||
Err(Error::BoxInStblNotFound(self.track_id(), BoxType::EsdsBox))
|
||||
Err(BoxError::BoxInStblNotFound(
|
||||
self.track_id(),
|
||||
BoxType::EsdsBox,
|
||||
))
|
||||
}
|
||||
} else {
|
||||
Err(Error::BoxInStblNotFound(self.track_id(), BoxType::Mp4aBox))
|
||||
Err(BoxError::BoxInStblNotFound(
|
||||
self.track_id(),
|
||||
BoxType::Mp4aBox,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn stsc_index(&self, sample_id: u32) -> Result<usize> {
|
||||
if self.trak.mdia.minf.stbl.stsc.entries.is_empty() {
|
||||
return Err(Error::InvalidData("no stsc entries"));
|
||||
return Err(BoxError::InvalidData("no stsc entries"));
|
||||
}
|
||||
for (i, entry) in self.trak.mdia.minf.stbl.stsc.entries.iter().enumerate() {
|
||||
if sample_id < entry.first_sample {
|
||||
return if i == 0 {
|
||||
Err(Error::InvalidData("sample not found"))
|
||||
Err(BoxError::InvalidData("sample not found"))
|
||||
} else {
|
||||
Ok(i - 1)
|
||||
};
|
||||
|
@ -319,13 +346,13 @@ impl Mp4Track {
|
|||
|
||||
fn chunk_offset(&self, chunk_id: u32) -> Result<u64> {
|
||||
if self.trak.mdia.minf.stbl.stco.is_none() && self.trak.mdia.minf.stbl.co64.is_none() {
|
||||
return Err(Error::InvalidData("must have either stco or co64 boxes"));
|
||||
return Err(BoxError::InvalidData("must have either stco or co64 boxes"));
|
||||
}
|
||||
if let Some(ref stco) = self.trak.mdia.minf.stbl.stco {
|
||||
if let Some(offset) = stco.entries.get(chunk_id as usize - 1) {
|
||||
return Ok(*offset as u64);
|
||||
} else {
|
||||
return Err(Error::EntryInStblNotFound(
|
||||
return Err(BoxError::EntryInStblNotFound(
|
||||
self.track_id(),
|
||||
BoxType::StcoBox,
|
||||
chunk_id,
|
||||
|
@ -335,14 +362,14 @@ impl Mp4Track {
|
|||
if let Some(offset) = co64.entries.get(chunk_id as usize - 1) {
|
||||
return Ok(*offset);
|
||||
} else {
|
||||
return Err(Error::EntryInStblNotFound(
|
||||
return Err(BoxError::EntryInStblNotFound(
|
||||
self.track_id(),
|
||||
BoxType::Co64Box,
|
||||
chunk_id,
|
||||
));
|
||||
}
|
||||
}
|
||||
Err(Error::Box2NotFound(BoxType::StcoBox, BoxType::Co64Box))
|
||||
Err(BoxError::Box2NotFound(BoxType::StcoBox, BoxType::Co64Box))
|
||||
}
|
||||
|
||||
fn ctts_index(&self, sample_id: u32) -> Result<(usize, u32)> {
|
||||
|
@ -352,7 +379,7 @@ impl Mp4Track {
|
|||
let next_sample_count =
|
||||
sample_count
|
||||
.checked_add(entry.sample_count)
|
||||
.ok_or(Error::InvalidData(
|
||||
.ok_or(BoxError::InvalidData(
|
||||
"attempt to sum ctts entries sample_count with overflow",
|
||||
))?;
|
||||
if sample_id < next_sample_count {
|
||||
|
@ -361,7 +388,7 @@ impl Mp4Track {
|
|||
sample_count = next_sample_count;
|
||||
}
|
||||
|
||||
Err(Error::EntryInStblNotFound(
|
||||
Err(BoxError::EntryInStblNotFound(
|
||||
self.track_id(),
|
||||
BoxType::CttsBox,
|
||||
sample_id,
|
||||
|
@ -386,7 +413,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]
|
||||
|
@ -398,14 +425,17 @@ impl Mp4Track {
|
|||
{
|
||||
Ok(*size)
|
||||
} else {
|
||||
Err(Error::EntryInTrunNotFound(
|
||||
Err(BoxError::EntryInTrunNotFound(
|
||||
self.track_id(),
|
||||
BoxType::TrunBox,
|
||||
sample_id,
|
||||
))
|
||||
}
|
||||
} else {
|
||||
Err(Error::BoxInTrafNotFound(self.track_id(), BoxType::TrafBox))
|
||||
Err(BoxError::BoxInTrafNotFound(
|
||||
self.track_id(),
|
||||
BoxType::TrafBox,
|
||||
))
|
||||
}
|
||||
} else {
|
||||
let stsz = &self.trak.mdia.minf.stbl.stsz;
|
||||
|
@ -415,7 +445,7 @@ impl Mp4Track {
|
|||
if let Some(size) = stsz.sample_sizes.get(sample_id as usize - 1) {
|
||||
Ok(*size)
|
||||
} else {
|
||||
Err(Error::EntryInStblNotFound(
|
||||
Err(BoxError::EntryInStblNotFound(
|
||||
self.track_id(),
|
||||
BoxType::StszBox,
|
||||
sample_id,
|
||||
|
@ -451,7 +481,9 @@ impl Mp4Track {
|
|||
.and_then(|trun| trun.data_offset)
|
||||
{
|
||||
sample_offset = sample_offset.checked_add_signed(data_offset as i64).ok_or(
|
||||
Error::InvalidData("attempt to calculate trun sample offset with overflow"),
|
||||
BoxError::InvalidData(
|
||||
"attempt to calculate trun sample offset with overflow",
|
||||
),
|
||||
)?;
|
||||
}
|
||||
|
||||
|
@ -459,14 +491,17 @@ impl Mp4Track {
|
|||
for i in first_sample_in_trun..sample_id {
|
||||
sample_offset = sample_offset
|
||||
.checked_add(self.sample_size(i)? as u64)
|
||||
.ok_or(Error::InvalidData(
|
||||
.ok_or(BoxError::InvalidData(
|
||||
"attempt to calculate trun entry sample offset with overflow",
|
||||
))?;
|
||||
}
|
||||
|
||||
Ok(sample_offset)
|
||||
} else {
|
||||
Err(Error::BoxInTrafNotFound(self.track_id(), BoxType::TrafBox))
|
||||
Err(BoxError::BoxInTrafNotFound(
|
||||
self.track_id(),
|
||||
BoxType::TrafBox,
|
||||
))
|
||||
}
|
||||
} else {
|
||||
let stsc_index = self.stsc_index(sample_id)?;
|
||||
|
@ -482,7 +517,7 @@ impl Mp4Track {
|
|||
.checked_sub(first_sample)
|
||||
.map(|n| n / samples_per_chunk)
|
||||
.and_then(|n| n.checked_add(first_chunk))
|
||||
.ok_or(Error::InvalidData(
|
||||
.ok_or(BoxError::InvalidData(
|
||||
"attempt to calculate stsc chunk_id with overflow",
|
||||
))?;
|
||||
|
||||
|
@ -499,7 +534,7 @@ impl Mp4Track {
|
|||
}
|
||||
}
|
||||
|
||||
fn sample_time(&self, sample_id: u32) -> Result<(u64, u32)> {
|
||||
pub(crate) fn sample_time(&self, sample_id: u32) -> Result<(u64, u32)> {
|
||||
if !self.trafs.is_empty() {
|
||||
if let Some((traf_idx, sample_idx)) = self.find_traf_idx_and_sample_idx(sample_id) {
|
||||
let traf = &self.trafs[traf_idx];
|
||||
|
@ -514,7 +549,9 @@ impl Mp4Track {
|
|||
let mut start_offset = 0u64;
|
||||
for duration in &trun.sample_durations[..sample_idx] {
|
||||
start_offset = start_offset.checked_add(*duration as u64).ok_or(
|
||||
Error::InvalidData("attempt to sum sample durations with overflow"),
|
||||
BoxError::InvalidData(
|
||||
"attempt to sum sample durations with overflow",
|
||||
),
|
||||
)?;
|
||||
}
|
||||
let duration = trun.sample_durations[sample_idx];
|
||||
|
@ -546,7 +583,7 @@ impl Mp4Track {
|
|||
let new_sample_count =
|
||||
sample_count
|
||||
.checked_add(entry.sample_count)
|
||||
.ok_or(Error::InvalidData(
|
||||
.ok_or(BoxError::InvalidData(
|
||||
"attempt to sum stts entries sample_count with overflow",
|
||||
))?;
|
||||
if sample_id < new_sample_count {
|
||||
|
@ -559,7 +596,7 @@ impl Mp4Track {
|
|||
elapsed += entry.sample_count as u64 * entry.sample_delta as u64;
|
||||
}
|
||||
|
||||
Err(Error::EntryInStblNotFound(
|
||||
Err(BoxError::EntryInStblNotFound(
|
||||
self.track_id(),
|
||||
BoxType::SttsBox,
|
||||
sample_id,
|
||||
|
@ -567,7 +604,7 @@ impl Mp4Track {
|
|||
}
|
||||
}
|
||||
|
||||
fn sample_rendering_offset(&self, sample_id: u32) -> i32 {
|
||||
pub(crate) fn sample_rendering_offset(&self, sample_id: u32) -> i32 {
|
||||
if !self.trafs.is_empty() {
|
||||
if let Some((traf_idx, sample_idx)) = self.find_traf_idx_and_sample_idx(sample_id) {
|
||||
if let Some(cts) = self.trafs[traf_idx]
|
||||
|
@ -587,7 +624,7 @@ impl Mp4Track {
|
|||
0
|
||||
}
|
||||
|
||||
fn is_sync_sample(&self, sample_id: u32) -> bool {
|
||||
pub(crate) fn is_sync_sample(&self, sample_id: u32) -> bool {
|
||||
if !self.trafs.is_empty() {
|
||||
if let Some((_, sample_idx)) = self.find_traf_idx_and_sample_idx(sample_id) {
|
||||
return sample_idx == 0;
|
||||
|
@ -610,12 +647,13 @@ impl Mp4Track {
|
|||
) -> Result<Option<Mp4Sample>> {
|
||||
let sample_offset = match self.sample_offset(sample_id) {
|
||||
Ok(offset) => offset,
|
||||
Err(Error::EntryInStblNotFound(_, _, _)) => return Ok(None),
|
||||
Err(BoxError::EntryInStblNotFound(_, _, _)) => return Ok(None),
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
|
||||
let sample_size = match self.sample_size(sample_id) {
|
||||
Ok(size) => size,
|
||||
Err(Error::EntryInStblNotFound(_, _, _)) => return Ok(None),
|
||||
Err(BoxError::EntryInStblNotFound(_, _, _)) => return Ok(None),
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
|
||||
|
|
61
src/types.rs
61
src/types.rs
|
@ -92,7 +92,7 @@ pub struct FourCC {
|
|||
}
|
||||
|
||||
impl std::str::FromStr for FourCC {
|
||||
type Err = Error;
|
||||
type Err = BoxError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self> {
|
||||
if let [a, b, c, d] = s.as_bytes() {
|
||||
|
@ -100,7 +100,9 @@ impl std::str::FromStr for FourCC {
|
|||
value: [*a, *b, *c, *d],
|
||||
})
|
||||
} else {
|
||||
Err(Error::InvalidData("expected exactly four bytes in string"))
|
||||
Err(BoxError::InvalidData(
|
||||
"expected exactly four bytes in string",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -170,39 +172,39 @@ pub enum TrackType {
|
|||
Video,
|
||||
Audio,
|
||||
Subtitle,
|
||||
Other(FourCC),
|
||||
}
|
||||
|
||||
impl fmt::Display for TrackType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let s = match self {
|
||||
TrackType::Video => DISPLAY_TYPE_VIDEO,
|
||||
TrackType::Audio => DISPLAY_TYPE_AUDIO,
|
||||
TrackType::Subtitle => DISPLAY_TYPE_SUBTITLE,
|
||||
};
|
||||
write!(f, "{s}")
|
||||
match self {
|
||||
TrackType::Video => write!(f, "{DISPLAY_TYPE_VIDEO}"),
|
||||
TrackType::Audio => write!(f, "{DISPLAY_TYPE_AUDIO}"),
|
||||
TrackType::Subtitle => write!(f, "{DISPLAY_TYPE_SUBTITLE}"),
|
||||
TrackType::Other(other) => write!(f, "Other({})", other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for TrackType {
|
||||
type Error = Error;
|
||||
type Error = BoxError;
|
||||
fn try_from(handler: &str) -> Result<TrackType> {
|
||||
match handler {
|
||||
HANDLER_TYPE_VIDEO => Ok(TrackType::Video),
|
||||
HANDLER_TYPE_AUDIO => Ok(TrackType::Audio),
|
||||
HANDLER_TYPE_SUBTITLE => Ok(TrackType::Subtitle),
|
||||
_ => Err(Error::InvalidData("unsupported handler type")),
|
||||
_ => Err(BoxError::InvalidData("unsupported handler type")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&FourCC> for TrackType {
|
||||
type Error = Error;
|
||||
fn try_from(fourcc: &FourCC) -> Result<TrackType> {
|
||||
impl From<&FourCC> for TrackType {
|
||||
fn from(fourcc: &FourCC) -> TrackType {
|
||||
match fourcc.value {
|
||||
HANDLER_TYPE_VIDEO_FOURCC => Ok(TrackType::Video),
|
||||
HANDLER_TYPE_AUDIO_FOURCC => Ok(TrackType::Audio),
|
||||
HANDLER_TYPE_SUBTITLE_FOURCC => Ok(TrackType::Subtitle),
|
||||
_ => Err(Error::InvalidData("unsupported handler type")),
|
||||
HANDLER_TYPE_VIDEO_FOURCC => TrackType::Video,
|
||||
HANDLER_TYPE_AUDIO_FOURCC => TrackType::Audio,
|
||||
HANDLER_TYPE_SUBTITLE_FOURCC => TrackType::Subtitle,
|
||||
other => TrackType::Other(other.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -213,6 +215,7 @@ impl From<TrackType> for FourCC {
|
|||
TrackType::Video => HANDLER_TYPE_VIDEO_FOURCC.into(),
|
||||
TrackType::Audio => HANDLER_TYPE_AUDIO_FOURCC.into(),
|
||||
TrackType::Subtitle => HANDLER_TYPE_SUBTITLE_FOURCC.into(),
|
||||
TrackType::Other(inner) => inner.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -240,7 +243,7 @@ impl fmt::Display for MediaType {
|
|||
}
|
||||
|
||||
impl TryFrom<&str> for MediaType {
|
||||
type Error = Error;
|
||||
type Error = BoxError;
|
||||
fn try_from(media: &str) -> Result<MediaType> {
|
||||
match media {
|
||||
MEDIA_TYPE_H264 => Ok(MediaType::H264),
|
||||
|
@ -248,7 +251,7 @@ impl TryFrom<&str> for MediaType {
|
|||
MEDIA_TYPE_VP9 => Ok(MediaType::VP9),
|
||||
MEDIA_TYPE_AAC => Ok(MediaType::AAC),
|
||||
MEDIA_TYPE_TTXT => Ok(MediaType::TTXT),
|
||||
_ => Err(Error::InvalidData("unsupported media type")),
|
||||
_ => Err(BoxError::InvalidData("unsupported media type")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -288,7 +291,7 @@ pub enum AvcProfile {
|
|||
}
|
||||
|
||||
impl TryFrom<(u8, u8)> for AvcProfile {
|
||||
type Error = Error;
|
||||
type Error = BoxError;
|
||||
fn try_from(value: (u8, u8)) -> Result<AvcProfile> {
|
||||
let profile = value.0;
|
||||
let constraint_set1_flag = (value.1 & 0x40) >> 7;
|
||||
|
@ -298,7 +301,7 @@ impl TryFrom<(u8, u8)> for AvcProfile {
|
|||
(77, _) => Ok(AvcProfile::AvcMain),
|
||||
(88, _) => Ok(AvcProfile::AvcExtended),
|
||||
(100, _) => Ok(AvcProfile::AvcHigh),
|
||||
_ => Err(Error::InvalidData("unsupported avc profile")),
|
||||
_ => Err(BoxError::InvalidData("unsupported avc profile")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -363,7 +366,7 @@ pub enum AudioObjectType {
|
|||
}
|
||||
|
||||
impl TryFrom<u8> for AudioObjectType {
|
||||
type Error = Error;
|
||||
type Error = BoxError;
|
||||
fn try_from(value: u8) -> Result<AudioObjectType> {
|
||||
match value {
|
||||
1 => Ok(AudioObjectType::AacMain),
|
||||
|
@ -408,7 +411,7 @@ impl TryFrom<u8> for AudioObjectType {
|
|||
44 => Ok(AudioObjectType::LowDelayMpegSurround),
|
||||
45 => Ok(AudioObjectType::SpatialAudioObjectCodingDialogueEnhancement),
|
||||
46 => Ok(AudioObjectType::AudioSync),
|
||||
_ => Err(Error::InvalidData("invalid audio object type")),
|
||||
_ => Err(BoxError::InvalidData("invalid audio object type")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -481,7 +484,7 @@ pub enum SampleFreqIndex {
|
|||
}
|
||||
|
||||
impl TryFrom<u8> for SampleFreqIndex {
|
||||
type Error = Error;
|
||||
type Error = BoxError;
|
||||
fn try_from(value: u8) -> Result<SampleFreqIndex> {
|
||||
match value {
|
||||
0x0 => Ok(SampleFreqIndex::Freq96000),
|
||||
|
@ -497,7 +500,7 @@ impl TryFrom<u8> for SampleFreqIndex {
|
|||
0xa => Ok(SampleFreqIndex::Freq11025),
|
||||
0xb => Ok(SampleFreqIndex::Freq8000),
|
||||
0xc => Ok(SampleFreqIndex::Freq7350),
|
||||
_ => Err(Error::InvalidData("invalid sampling frequency index")),
|
||||
_ => Err(BoxError::InvalidData("invalid sampling frequency index")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -534,7 +537,7 @@ pub enum ChannelConfig {
|
|||
}
|
||||
|
||||
impl TryFrom<u8> for ChannelConfig {
|
||||
type Error = Error;
|
||||
type Error = BoxError;
|
||||
fn try_from(value: u8) -> Result<ChannelConfig> {
|
||||
match value {
|
||||
0x1 => Ok(ChannelConfig::Mono),
|
||||
|
@ -544,7 +547,7 @@ impl TryFrom<u8> for ChannelConfig {
|
|||
0x5 => Ok(ChannelConfig::Five),
|
||||
0x6 => Ok(ChannelConfig::FiveOne),
|
||||
0x7 => Ok(ChannelConfig::SevenOne),
|
||||
_ => Err(Error::InvalidData("invalid channel configuration")),
|
||||
_ => Err(BoxError::InvalidData("invalid channel configuration")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -673,14 +676,14 @@ impl std::default::Default for DataType {
|
|||
}
|
||||
|
||||
impl TryFrom<u32> for DataType {
|
||||
type Error = Error;
|
||||
type Error = BoxError;
|
||||
fn try_from(value: u32) -> Result<DataType> {
|
||||
match value {
|
||||
0x000000 => Ok(DataType::Binary),
|
||||
0x000001 => Ok(DataType::Text),
|
||||
0x00000D => Ok(DataType::Image),
|
||||
0x000015 => Ok(DataType::TempoCpil),
|
||||
_ => Err(Error::InvalidData("invalid data type")),
|
||||
_ => Err(BoxError::InvalidData("invalid data type")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,13 +100,13 @@ impl<W: Write + Seek> Mp4Writer<W> {
|
|||
|
||||
pub fn write_sample(&mut self, track_id: u32, sample: &Mp4Sample) -> Result<()> {
|
||||
if track_id == 0 {
|
||||
return Err(Error::TrakNotFound(track_id));
|
||||
return Err(BoxError::TrakNotFound(track_id));
|
||||
}
|
||||
|
||||
let track_dur = if let Some(ref mut track) = self.tracks.get_mut(track_id as usize - 1) {
|
||||
track.write_sample(&mut self.writer, sample, self.timescale)?
|
||||
} else {
|
||||
return Err(Error::TrakNotFound(track_id));
|
||||
return Err(BoxError::TrakNotFound(track_id));
|
||||
};
|
||||
|
||||
self.update_durations(track_dur);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue