mirror of
https://github.com/alfg/mp4-rust.git
synced 2025-01-05 09:58:40 +00:00
Feature/mp4reader (#13)
* Add ReadBox trait * Add boxtype macro * Remove offset in BoxHeader * Fix parsing error when box has largesize * Remove duplicated codes reading version and flags * Add avc1 box * Add mp4a box * Add mp4a box * Add DecoderSpecificDescriptor in esds box * Add necessary sub-boxes to stbl box * Improve ReadBox::read_box() * Add smhd box * Refactor BoxHeader * Refactor BMFF * Refactor * Add some functions to get offset and size of sample * Add Mp4Reader::read_sample() that read media samples Co-authored-by: Byungwan Jun <unipro.kr@gmail.com>
This commit is contained in:
parent
b755db3fa0
commit
0df82aec5f
31 changed files with 2484 additions and 619 deletions
|
@ -20,4 +20,5 @@ license = "MIT"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
thiserror = "^1.0"
|
thiserror = "^1.0"
|
||||||
byteorder = "1"
|
byteorder = "1"
|
||||||
|
bytes = "0.5"
|
||||||
num-rational = "0.3"
|
num-rational = "0.3"
|
41
examples/mp4copy.rs
Normal file
41
examples/mp4copy.rs
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
use std::env;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::prelude::*;
|
||||||
|
use std::io::{self, BufReader};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use mp4::Result;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args: Vec<String> = env::args().collect();
|
||||||
|
|
||||||
|
if args.len() < 3 {
|
||||||
|
println!("Usage: mp4copy <source file> <target file>");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(err) = copy(&args[1], &args[2]) {
|
||||||
|
let _ = writeln!(io::stderr(), "{}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn copy<P: AsRef<Path>>(src_filename: &P, _dst_filename: &P) -> Result<()> {
|
||||||
|
let src_file = File::open(src_filename)?;
|
||||||
|
let size = src_file.metadata()?.len();
|
||||||
|
let reader = BufReader::new(src_file);
|
||||||
|
|
||||||
|
let mut mp4 = mp4::Mp4Reader::new(reader);
|
||||||
|
mp4.read(size)?;
|
||||||
|
|
||||||
|
for tix in 0..mp4.track_count()? {
|
||||||
|
let track_id = tix + 1;
|
||||||
|
let sample_count = mp4.sample_count(track_id)?;
|
||||||
|
for six in 0..sample_count {
|
||||||
|
let sample_id = six + 1;
|
||||||
|
let sample = mp4.read_sample(track_id, sample_id)?.unwrap();
|
||||||
|
println!("sample_id: {}, {}", sample_id, sample);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -1,32 +1,42 @@
|
||||||
use mp4;
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
use std::io::prelude::*;
|
||||||
|
use std::io::{self, BufReader};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use mp4::{Result, Mp4Reader, TrackType};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
|
|
||||||
match args.len() {
|
if args.len() < 2 {
|
||||||
2 => {
|
println!("Usage: mp4info <filename>");
|
||||||
let filename = &args[1];
|
std::process::exit(1);
|
||||||
let f = File::open(filename).unwrap();
|
}
|
||||||
|
|
||||||
let bmff = mp4::read_mp4(f).unwrap();
|
if let Err(err) = info(&args[1]) {
|
||||||
let moov = bmff.moov.unwrap();
|
let _ = writeln!(io::stderr(), "{}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn info<P: AsRef<Path>>(filename: &P) -> Result<()> {
|
||||||
|
let f = File::open(filename)?;
|
||||||
|
let size = f.metadata()?.len();
|
||||||
|
let reader = BufReader::new(f);
|
||||||
|
|
||||||
|
let mut mp4 = Mp4Reader::new(reader);
|
||||||
|
mp4.read(size)?;
|
||||||
|
|
||||||
// Print results.
|
|
||||||
println!("File:");
|
println!("File:");
|
||||||
println!(" file size: {}", bmff.size);
|
println!(" size: {}", mp4.size());
|
||||||
println!(
|
println!(" brands: {:?} {:?}\n",
|
||||||
" brands: {:?} {:?}\n",
|
mp4.ftyp.major_brand, mp4.ftyp.compatible_brands);
|
||||||
bmff.ftyp.major_brand, bmff.ftyp.compatible_brands
|
|
||||||
);
|
|
||||||
|
|
||||||
|
if let Some(ref moov) = mp4.moov {
|
||||||
println!("Movie:");
|
println!("Movie:");
|
||||||
println!(" version: {:?}", moov.mvhd.version);
|
println!(" version: {:?}", moov.mvhd.version);
|
||||||
println!(
|
println!(" creation time: {}",
|
||||||
" creation time: {}",
|
creation_time(moov.mvhd.creation_time));
|
||||||
creation_time(moov.mvhd.creation_time)
|
|
||||||
);
|
|
||||||
println!(" duration: {:?}", moov.mvhd.duration);
|
println!(" duration: {:?}", moov.mvhd.duration);
|
||||||
println!(" timescale: {:?}\n", moov.mvhd.timescale);
|
println!(" timescale: {:?}\n", moov.mvhd.timescale);
|
||||||
|
|
||||||
|
@ -50,10 +60,8 @@ fn main() {
|
||||||
.map(|m| m.stbl.as_ref().map(|s| s.stts.as_ref()).flatten())
|
.map(|m| m.stbl.as_ref().map(|s| s.stts.as_ref()).flatten())
|
||||||
.flatten();
|
.flatten();
|
||||||
|
|
||||||
println!(
|
println!(" type: {:?}",
|
||||||
" type: {:?}",
|
get_handler_type(hdlr.handler_type.value.as_ref()));
|
||||||
get_handler_type(hdlr.handler_type.value.as_ref())
|
|
||||||
);
|
|
||||||
println!(" language: {:?}", mdhd.language);
|
println!(" language: {:?}", mdhd.language);
|
||||||
|
|
||||||
println!(" media:");
|
println!(" media:");
|
||||||
|
@ -61,37 +69,30 @@ fn main() {
|
||||||
println!(" sample count: {:?}", s.entries[0].sample_count);
|
println!(" sample count: {:?}", s.entries[0].sample_count);
|
||||||
}
|
}
|
||||||
println!(" timescale: {:?}", mdhd.timescale);
|
println!(" timescale: {:?}", mdhd.timescale);
|
||||||
println!(
|
println!(" duration: {:?} (media timescale units)",
|
||||||
" duration: {:?} (media timescale units)",
|
mdhd.duration);
|
||||||
mdhd.duration
|
println!(" duration: {:?} (ms)",
|
||||||
);
|
get_duration_ms(mdhd.duration, mdhd.timescale));
|
||||||
println!(
|
if get_handler_type(hdlr.handler_type.value.as_ref()) == TrackType::Video {
|
||||||
" duration: {:?} (ms)",
|
|
||||||
get_duration_ms(mdhd.duration, mdhd.timescale)
|
|
||||||
);
|
|
||||||
if get_handler_type(hdlr.handler_type.value.as_ref()) == mp4::TrackType::Video {
|
|
||||||
if let Some(ref s) = stts {
|
if let Some(ref s) = stts {
|
||||||
println!(
|
println!(" frame rate: (computed): {:?}",
|
||||||
" frame rate: (computed): {:?}",
|
get_framerate(s.entries[0].sample_count,
|
||||||
get_framerate(s.entries[0].sample_count, mdhd.duration, mdhd.timescale)
|
mdhd.duration, mdhd.timescale));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
|
||||||
println!("Usage: mp4info <filename>");
|
Ok(())
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_handler_type(handler: &str) -> mp4::TrackType {
|
fn get_handler_type(handler: &str) -> TrackType {
|
||||||
let mut typ: mp4::TrackType = mp4::TrackType::Unknown;
|
let mut typ: TrackType = TrackType::Unknown;
|
||||||
match handler {
|
match handler {
|
||||||
"vide" => typ = mp4::TrackType::Video,
|
"vide" => typ = TrackType::Video,
|
||||||
"soun" => typ = mp4::TrackType::Audio,
|
"soun" => typ = TrackType::Audio,
|
||||||
"meta" => typ = mp4::TrackType::Unknown,
|
"meta" => typ = TrackType::Unknown,
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
return typ;
|
return typ;
|
||||||
|
|
235
src/atoms/avc.rs
Normal file
235
src/atoms/avc.rs
Normal file
|
@ -0,0 +1,235 @@
|
||||||
|
use std::io::{Seek, Read, Write};
|
||||||
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
use num_rational::Ratio;
|
||||||
|
|
||||||
|
use crate::*;
|
||||||
|
use crate::atoms::*;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct Avc1Box {
|
||||||
|
pub data_reference_index: u16,
|
||||||
|
pub width: u16,
|
||||||
|
pub height: u16,
|
||||||
|
pub horizresolution: Ratio<u32>,
|
||||||
|
pub vertresolution: Ratio<u32>,
|
||||||
|
pub frame_count: u16,
|
||||||
|
pub depth: u16,
|
||||||
|
pub avcc: AvcCBox,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Avc1Box {
|
||||||
|
fn default() -> Self {
|
||||||
|
Avc1Box {
|
||||||
|
data_reference_index: 0,
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
horizresolution: Ratio::new_raw(0x00480000, 0x10000),
|
||||||
|
vertresolution: Ratio::new_raw(0x00480000, 0x10000),
|
||||||
|
frame_count: 1,
|
||||||
|
depth: 0x0018,
|
||||||
|
avcc: AvcCBox::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mp4Box for Avc1Box {
|
||||||
|
fn box_type() -> BoxType {
|
||||||
|
BoxType::Avc1Box
|
||||||
|
}
|
||||||
|
|
||||||
|
fn box_size(&self) -> u64 {
|
||||||
|
HEADER_SIZE + 8 + 74 + self.avcc.box_size()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read + Seek> ReadBox<&mut R> for Avc1Box {
|
||||||
|
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||||
|
let start = get_box_start(reader)?;
|
||||||
|
|
||||||
|
reader.read_u32::<BigEndian>()?; // reserved
|
||||||
|
reader.read_u16::<BigEndian>()?; // reserved
|
||||||
|
let data_reference_index = reader.read_u16::<BigEndian>()?;
|
||||||
|
|
||||||
|
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 horiznumer = reader.read_u32::<BigEndian>()?;
|
||||||
|
let horizresolution = Ratio::new_raw(horiznumer, 0x10000);
|
||||||
|
let vertnumer = reader.read_u32::<BigEndian>()?;
|
||||||
|
let vertresolution = Ratio::new_raw(vertnumer, 0x10000);
|
||||||
|
reader.read_u32::<BigEndian>()?; // reserved
|
||||||
|
let frame_count = reader.read_u16::<BigEndian>()?;
|
||||||
|
skip_read(reader, 32)?; // compressorname
|
||||||
|
let depth = reader.read_u16::<BigEndian>()?;
|
||||||
|
reader.read_i16::<BigEndian>()?; // pre-defined
|
||||||
|
|
||||||
|
let header = BoxHeader::read(reader)?;
|
||||||
|
let BoxHeader{ name, size: s } = header;
|
||||||
|
if name == BoxType::AvcCBox {
|
||||||
|
let avcc = AvcCBox::read_box(reader, s)?;
|
||||||
|
|
||||||
|
skip_read_to(reader, start + size)?;
|
||||||
|
|
||||||
|
Ok(Avc1Box {
|
||||||
|
data_reference_index,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
horizresolution,
|
||||||
|
vertresolution,
|
||||||
|
frame_count,
|
||||||
|
depth,
|
||||||
|
avcc,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(Error::InvalidData("avcc not found"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: Write> WriteBox<&mut W> for Avc1Box {
|
||||||
|
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||||
|
let size = self.box_size();
|
||||||
|
BoxHeader::new(Self::box_type(), size).write(writer)?;
|
||||||
|
|
||||||
|
writer.write_u32::<BigEndian>(0)?; // reserved
|
||||||
|
writer.write_u16::<BigEndian>(0)?; // reserved
|
||||||
|
writer.write_u16::<BigEndian>(self.data_reference_index)?;
|
||||||
|
|
||||||
|
writer.write_u32::<BigEndian>(0)?; // pre-defined, reserved
|
||||||
|
writer.write_u64::<BigEndian>(0)?; // pre-defined
|
||||||
|
writer.write_u32::<BigEndian>(0)?; // pre-defined
|
||||||
|
writer.write_u16::<BigEndian>(self.width)?;
|
||||||
|
writer.write_u16::<BigEndian>(self.height)?;
|
||||||
|
writer.write_u32::<BigEndian>(*self.horizresolution.numer())?;
|
||||||
|
writer.write_u32::<BigEndian>(*self.vertresolution.numer())?;
|
||||||
|
writer.write_u32::<BigEndian>(0)?; // reserved
|
||||||
|
writer.write_u16::<BigEndian>(self.frame_count)?;
|
||||||
|
// skip compressorname
|
||||||
|
for _ in 0..4 {
|
||||||
|
writer.write_u64::<BigEndian>(0)?;
|
||||||
|
}
|
||||||
|
writer.write_u16::<BigEndian>(self.depth)?;
|
||||||
|
writer.write_i16::<BigEndian>(-1)?; // pre-defined
|
||||||
|
|
||||||
|
self.avcc.write_box(writer)?;
|
||||||
|
|
||||||
|
Ok(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq)]
|
||||||
|
pub struct AvcCBox {
|
||||||
|
pub configuration_version: u8,
|
||||||
|
pub avc_profile_indication: u8,
|
||||||
|
pub profile_compatibility: u8,
|
||||||
|
pub avc_level_indication: u8,
|
||||||
|
pub length_size_minus_one: u8,
|
||||||
|
pub sequence_parameter_sets: Vec<NalUnit>,
|
||||||
|
pub picture_parameter_sets: Vec<NalUnit>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mp4Box for AvcCBox {
|
||||||
|
fn box_type() -> BoxType {
|
||||||
|
BoxType::AvcCBox
|
||||||
|
}
|
||||||
|
|
||||||
|
fn box_size(&self) -> u64 {
|
||||||
|
let mut size = HEADER_SIZE + 7;
|
||||||
|
for sps in self.sequence_parameter_sets.iter() {
|
||||||
|
size += sps.size() as u64;
|
||||||
|
}
|
||||||
|
for pps in self.picture_parameter_sets.iter() {
|
||||||
|
size += pps.size() as u64;
|
||||||
|
}
|
||||||
|
size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read + Seek> ReadBox<&mut R> for AvcCBox {
|
||||||
|
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||||
|
let start = get_box_start(reader)?;
|
||||||
|
|
||||||
|
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 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_read_to(reader, start + size)?;
|
||||||
|
|
||||||
|
Ok(AvcCBox {
|
||||||
|
configuration_version,
|
||||||
|
avc_profile_indication,
|
||||||
|
profile_compatibility,
|
||||||
|
avc_level_indication,
|
||||||
|
length_size_minus_one,
|
||||||
|
sequence_parameter_sets,
|
||||||
|
picture_parameter_sets,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)?;
|
||||||
|
|
||||||
|
writer.write_u8(self.configuration_version)?;
|
||||||
|
writer.write_u8(self.avc_profile_indication)?;
|
||||||
|
writer.write_u8(self.profile_compatibility)?;
|
||||||
|
writer.write_u8(self.avc_level_indication)?;
|
||||||
|
writer.write_u8(self.length_size_minus_one | 0xFC)?;
|
||||||
|
writer.write_u8(self.sequence_parameter_sets.len() as u8 | 0xE0)?;
|
||||||
|
for sps in self.sequence_parameter_sets.iter() {
|
||||||
|
sps.write(writer)?;
|
||||||
|
}
|
||||||
|
writer.write_u8(self.picture_parameter_sets.len() as u8)?;
|
||||||
|
for pps in self.picture_parameter_sets.iter() {
|
||||||
|
pps.write(writer)?;
|
||||||
|
}
|
||||||
|
Ok(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq)]
|
||||||
|
pub struct NalUnit {
|
||||||
|
pub bytes: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NalUnit {
|
||||||
|
pub fn size(&self) -> usize {
|
||||||
|
2 + self.bytes.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub 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(&mut bytes)?;
|
||||||
|
Ok(NalUnit {
|
||||||
|
bytes,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write<W: Write>(&self, writer: &mut W) -> Result<u64> {
|
||||||
|
writer.write_u16::<BigEndian>(self.bytes.len() as u16)?;
|
||||||
|
writer.write(&self.bytes)?;
|
||||||
|
Ok(self.size() as u64)
|
||||||
|
}
|
||||||
|
}
|
89
src/atoms/co64.rs
Normal file
89
src/atoms/co64.rs
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
use std::io::{Seek, Read, Write};
|
||||||
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
|
use crate::*;
|
||||||
|
use crate::atoms::*;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq)]
|
||||||
|
pub struct Co64Box {
|
||||||
|
pub version: u8,
|
||||||
|
pub flags: u32,
|
||||||
|
pub entries: Vec<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mp4Box for Co64Box {
|
||||||
|
fn box_type() -> BoxType {
|
||||||
|
BoxType::Co64Box
|
||||||
|
}
|
||||||
|
|
||||||
|
fn box_size(&self) -> u64 {
|
||||||
|
HEADER_SIZE + HEADER_EXT_SIZE + 4 + (8 * self.entries.len() as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read + Seek> ReadBox<&mut R> for Co64Box {
|
||||||
|
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||||
|
let start = get_box_start(reader)?;
|
||||||
|
|
||||||
|
let (version, flags) = read_box_header_ext(reader)?;
|
||||||
|
|
||||||
|
let entry_count = reader.read_u32::<BigEndian>()?;
|
||||||
|
let mut entries = Vec::with_capacity(entry_count as usize);
|
||||||
|
for _i in 0..entry_count {
|
||||||
|
let chunk_offset = reader.read_u64::<BigEndian>()?;
|
||||||
|
entries.push(chunk_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_read_to(reader, start + size)?;
|
||||||
|
|
||||||
|
Ok(Co64Box {
|
||||||
|
version,
|
||||||
|
flags,
|
||||||
|
entries,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)?;
|
||||||
|
|
||||||
|
write_box_header_ext(writer, self.version, self.flags)?;
|
||||||
|
|
||||||
|
writer.write_u32::<BigEndian>(self.entries.len() as u32)?;
|
||||||
|
for chunk_offset in self.entries.iter() {
|
||||||
|
writer.write_u64::<BigEndian>(*chunk_offset)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::atoms::BoxHeader;
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_co64() {
|
||||||
|
let src_box = Co64Box {
|
||||||
|
version: 0,
|
||||||
|
flags: 0,
|
||||||
|
entries: vec![267, 1970, 2535, 2803, 11843, 22223, 33584],
|
||||||
|
};
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
src_box.write_box(&mut buf).unwrap();
|
||||||
|
assert_eq!(buf.len(), src_box.box_size() as usize);
|
||||||
|
|
||||||
|
let mut reader = Cursor::new(&buf);
|
||||||
|
let header = BoxHeader::read(&mut reader).unwrap();
|
||||||
|
assert_eq!(header.name, BoxType::Co64Box);
|
||||||
|
assert_eq!(src_box.box_size(), header.size);
|
||||||
|
|
||||||
|
let dst_box = Co64Box::read_box(&mut reader, header.size).unwrap();
|
||||||
|
assert_eq!(src_box, dst_box);
|
||||||
|
}
|
||||||
|
}
|
102
src/atoms/ctts.rs
Normal file
102
src/atoms/ctts.rs
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
use std::io::{Seek, Read, Write};
|
||||||
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
|
use crate::*;
|
||||||
|
use crate::atoms::*;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq)]
|
||||||
|
pub struct CttsBox {
|
||||||
|
pub version: u8,
|
||||||
|
pub flags: u32,
|
||||||
|
pub entries: Vec<CttsEntry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq)]
|
||||||
|
pub struct CttsEntry {
|
||||||
|
pub sample_count: u32,
|
||||||
|
pub sample_offset: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mp4Box for CttsBox {
|
||||||
|
fn box_type() -> BoxType {
|
||||||
|
BoxType::CttsBox
|
||||||
|
}
|
||||||
|
|
||||||
|
fn box_size(&self) -> u64 {
|
||||||
|
HEADER_SIZE + HEADER_EXT_SIZE + 4 + (8 * self.entries.len() as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read + Seek> ReadBox<&mut R> for CttsBox {
|
||||||
|
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||||
|
let start = get_box_start(reader)?;
|
||||||
|
|
||||||
|
let (version, flags) = read_box_header_ext(reader)?;
|
||||||
|
|
||||||
|
let entry_count = reader.read_u32::<BigEndian>()?;
|
||||||
|
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>()?,
|
||||||
|
};
|
||||||
|
entries.push(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_read_to(reader, start + size)?;
|
||||||
|
|
||||||
|
Ok(CttsBox {
|
||||||
|
version,
|
||||||
|
flags,
|
||||||
|
entries,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)?;
|
||||||
|
|
||||||
|
write_box_header_ext(writer, self.version, self.flags)?;
|
||||||
|
|
||||||
|
writer.write_u32::<BigEndian>(self.entries.len() as u32)?;
|
||||||
|
for entry in self.entries.iter() {
|
||||||
|
writer.write_u32::<BigEndian>(entry.sample_count)?;
|
||||||
|
writer.write_i32::<BigEndian>(entry.sample_offset)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::atoms::BoxHeader;
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ctts() {
|
||||||
|
let src_box = CttsBox {
|
||||||
|
version: 0,
|
||||||
|
flags: 0,
|
||||||
|
entries: vec![
|
||||||
|
CttsEntry {sample_count: 1, sample_offset: 200},
|
||||||
|
CttsEntry {sample_count: 2, sample_offset: -100},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
src_box.write_box(&mut buf).unwrap();
|
||||||
|
assert_eq!(buf.len(), src_box.box_size() as usize);
|
||||||
|
|
||||||
|
let mut reader = Cursor::new(&buf);
|
||||||
|
let header = BoxHeader::read(&mut reader).unwrap();
|
||||||
|
assert_eq!(header.name, BoxType::CttsBox);
|
||||||
|
assert_eq!(src_box.box_size(), header.size);
|
||||||
|
|
||||||
|
let dst_box = CttsBox::read_box(&mut reader, header.size).unwrap();
|
||||||
|
assert_eq!(src_box, dst_box);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
use std::io::{BufReader, SeekFrom, Seek, Read, BufWriter, Write};
|
use std::io::{Seek, Read, Write};
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
use crate::atoms::*;
|
||||||
use crate::atoms::elst::ElstBox;
|
use crate::atoms::elst::ElstBox;
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,28 +17,26 @@ impl EdtsBox {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mp4Box for EdtsBox {
|
impl Mp4Box for EdtsBox {
|
||||||
fn box_type(&self) -> BoxType {
|
fn box_type() -> BoxType {
|
||||||
BoxType::EdtsBox
|
BoxType::EdtsBox
|
||||||
}
|
}
|
||||||
|
|
||||||
fn box_size(&self) -> u64 {
|
fn box_size(&self) -> u64 {
|
||||||
let mut size = HEADER_SIZE;
|
let mut size = HEADER_SIZE;
|
||||||
if let Some(elst) = &self.elst {
|
if let Some(ref elst) = self.elst {
|
||||||
size += elst.box_size();
|
size += elst.box_size();
|
||||||
}
|
}
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for EdtsBox {
|
impl<R: Read + Seek> ReadBox<&mut R> for EdtsBox {
|
||||||
fn read_box(reader: &mut BufReader<R>, size: u64) -> Result<Self> {
|
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||||
let current = reader.seek(SeekFrom::Current(0))?; // Current cursor position.
|
let start = get_box_start(reader)?;
|
||||||
|
|
||||||
let mut edts = EdtsBox::new();
|
let mut edts = EdtsBox::new();
|
||||||
|
|
||||||
let start = 0u64;
|
let header = BoxHeader::read(reader)?;
|
||||||
while start < size {
|
|
||||||
// Get box header.
|
|
||||||
let header = read_box_header(reader, start)?;
|
|
||||||
let BoxHeader{ name, size: s } = header;
|
let BoxHeader{ name, size: s } = header;
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
|
@ -45,21 +44,21 @@ impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for EdtsBox {
|
||||||
let elst = ElstBox::read_box(reader, s)?;
|
let elst = ElstBox::read_box(reader, s)?;
|
||||||
edts.elst = Some(elst);
|
edts.elst = Some(elst);
|
||||||
}
|
}
|
||||||
_ => break
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
skip_read(reader, current, size)?;
|
skip_read_to(reader, start + size)?;
|
||||||
|
|
||||||
Ok(edts)
|
Ok(edts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: Write> WriteBox<&mut BufWriter<W>> for EdtsBox {
|
impl<W: Write> WriteBox<&mut W> for EdtsBox {
|
||||||
fn write_box(&self, writer: &mut BufWriter<W>) -> Result<u64> {
|
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||||
let size = self.box_size();
|
let size = self.box_size();
|
||||||
BoxHeader::new(self.box_type(), size).write_box(writer)?;
|
BoxHeader::new(Self::box_type(), size).write(writer)?;
|
||||||
|
|
||||||
if let Some(elst) = &self.elst {
|
if let Some(ref elst) = self.elst {
|
||||||
elst.write_box(writer)?;
|
elst.write_box(writer)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use std::io::{BufReader, SeekFrom, Seek, Read, BufWriter, Write};
|
use std::io::{Seek, Read, Write};
|
||||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
use crate::atoms::*;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq)]
|
#[derive(Debug, Default, PartialEq)]
|
||||||
|
@ -20,7 +21,7 @@ pub struct ElstEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mp4Box for ElstBox {
|
impl Mp4Box for ElstBox {
|
||||||
fn box_type(&self) -> BoxType {
|
fn box_type() -> BoxType {
|
||||||
BoxType::ElstBox
|
BoxType::ElstBox
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,9 +37,9 @@ impl Mp4Box for ElstBox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for ElstBox {
|
impl<R: Read + Seek> ReadBox<&mut R> for ElstBox {
|
||||||
fn read_box(reader: &mut BufReader<R>, size: u64) -> Result<Self> {
|
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||||
let current = reader.seek(SeekFrom::Current(0))?; // Current cursor position.
|
let start = get_box_start(reader)?;
|
||||||
|
|
||||||
let (version, flags) = read_box_header_ext(reader)?;
|
let (version, flags) = read_box_header_ext(reader)?;
|
||||||
|
|
||||||
|
@ -66,7 +67,8 @@ impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for ElstBox {
|
||||||
};
|
};
|
||||||
entries.push(entry);
|
entries.push(entry);
|
||||||
}
|
}
|
||||||
skip_read(reader, current, size)?;
|
|
||||||
|
skip_read_to(reader, start + size)?;
|
||||||
|
|
||||||
Ok(ElstBox {
|
Ok(ElstBox {
|
||||||
version,
|
version,
|
||||||
|
@ -76,10 +78,10 @@ impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for ElstBox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: Write> WriteBox<&mut BufWriter<W>> for ElstBox {
|
impl<W: Write> WriteBox<&mut W> for ElstBox {
|
||||||
fn write_box(&self, writer: &mut BufWriter<W>) -> Result<u64> {
|
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||||
let size = self.box_size();
|
let size = self.box_size();
|
||||||
BoxHeader::new(self.box_type(), size).write_box(writer)?;
|
BoxHeader::new(Self::box_type(), size).write(writer)?;
|
||||||
|
|
||||||
write_box_header_ext(writer, self.version, self.flags)?;
|
write_box_header_ext(writer, self.version, self.flags)?;
|
||||||
|
|
||||||
|
@ -103,7 +105,7 @@ impl<W: Write> WriteBox<&mut BufWriter<W>> for ElstBox {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::read_box_header;
|
use crate::atoms::BoxHeader;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -119,23 +121,17 @@ mod tests {
|
||||||
}],
|
}],
|
||||||
};
|
};
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
{
|
src_box.write_box(&mut buf).unwrap();
|
||||||
let mut writer = BufWriter::new(&mut buf);
|
|
||||||
src_box.write_box(&mut writer).unwrap();
|
|
||||||
}
|
|
||||||
assert_eq!(buf.len(), src_box.box_size() as usize);
|
assert_eq!(buf.len(), src_box.box_size() as usize);
|
||||||
|
|
||||||
{
|
let mut reader = Cursor::new(&buf);
|
||||||
let mut reader = BufReader::new(Cursor::new(&buf));
|
let header = BoxHeader::read(&mut reader).unwrap();
|
||||||
let header = read_box_header(&mut reader, 0).unwrap();
|
|
||||||
assert_eq!(header.name, BoxType::ElstBox);
|
assert_eq!(header.name, BoxType::ElstBox);
|
||||||
assert_eq!(src_box.box_size(), header.size);
|
assert_eq!(src_box.box_size(), header.size);
|
||||||
|
|
||||||
let dst_box = ElstBox::read_box(&mut reader, header.size).unwrap();
|
let dst_box = ElstBox::read_box(&mut reader, header.size).unwrap();
|
||||||
|
|
||||||
assert_eq!(src_box, dst_box);
|
assert_eq!(src_box, dst_box);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_elst64() {
|
fn test_elst64() {
|
||||||
|
@ -150,21 +146,15 @@ mod tests {
|
||||||
}],
|
}],
|
||||||
};
|
};
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
{
|
src_box.write_box(&mut buf).unwrap();
|
||||||
let mut writer = BufWriter::new(&mut buf);
|
|
||||||
src_box.write_box(&mut writer).unwrap();
|
|
||||||
}
|
|
||||||
assert_eq!(buf.len(), src_box.box_size() as usize);
|
assert_eq!(buf.len(), src_box.box_size() as usize);
|
||||||
|
|
||||||
{
|
let mut reader = Cursor::new(&buf);
|
||||||
let mut reader = BufReader::new(Cursor::new(&buf));
|
let header = BoxHeader::read(&mut reader).unwrap();
|
||||||
let header = read_box_header(&mut reader, 0).unwrap();
|
|
||||||
assert_eq!(header.name, BoxType::ElstBox);
|
assert_eq!(header.name, BoxType::ElstBox);
|
||||||
assert_eq!(src_box.box_size(), header.size);
|
assert_eq!(src_box.box_size(), header.size);
|
||||||
|
|
||||||
let dst_box = ElstBox::read_box(&mut reader, header.size).unwrap();
|
let dst_box = ElstBox::read_box(&mut reader, header.size).unwrap();
|
||||||
|
|
||||||
assert_eq!(src_box, dst_box);
|
assert_eq!(src_box, dst_box);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use std::io::{BufReader, Seek, Read, BufWriter, Write};
|
use std::io::{Seek, Read, Write};
|
||||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
use crate::atoms::*;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq)]
|
#[derive(Debug, Default, PartialEq)]
|
||||||
|
@ -12,7 +13,7 @@ pub struct FtypBox {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mp4Box for FtypBox {
|
impl Mp4Box for FtypBox {
|
||||||
fn box_type(&self) -> BoxType {
|
fn box_type() -> BoxType {
|
||||||
BoxType::FtypBox
|
BoxType::FtypBox
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,8 +22,10 @@ impl Mp4Box for FtypBox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for FtypBox {
|
impl<R: Read + Seek> ReadBox<&mut R> for FtypBox {
|
||||||
fn read_box(reader: &mut BufReader<R>, size: u64) -> Result<Self> {
|
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||||
|
let start = get_box_start(reader)?;
|
||||||
|
|
||||||
let major = reader.read_u32::<BigEndian>()?;
|
let major = reader.read_u32::<BigEndian>()?;
|
||||||
let minor = reader.read_u32::<BigEndian>()?;
|
let minor = reader.read_u32::<BigEndian>()?;
|
||||||
if size % 4 != 0 {
|
if size % 4 != 0 {
|
||||||
|
@ -36,6 +39,8 @@ impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for FtypBox {
|
||||||
brands.push(From::from(b));
|
brands.push(From::from(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
skip_read_to(reader, start + size)?;
|
||||||
|
|
||||||
Ok(FtypBox {
|
Ok(FtypBox {
|
||||||
major_brand: From::from(major),
|
major_brand: From::from(major),
|
||||||
minor_version: minor,
|
minor_version: minor,
|
||||||
|
@ -44,10 +49,10 @@ impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for FtypBox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: Write> WriteBox<&mut BufWriter<W>> for FtypBox {
|
impl<W: Write> WriteBox<&mut W> for FtypBox {
|
||||||
fn write_box(&self, writer: &mut BufWriter<W>) -> Result<u64> {
|
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||||
let size = self.box_size();
|
let size = self.box_size();
|
||||||
BoxHeader::new(self.box_type(), size).write_box(writer)?;
|
BoxHeader::new(Self::box_type(), size).write(writer)?;
|
||||||
|
|
||||||
writer.write_u32::<BigEndian>((&self.major_brand).into())?;
|
writer.write_u32::<BigEndian>((&self.major_brand).into())?;
|
||||||
writer.write_u32::<BigEndian>(self.minor_version)?;
|
writer.write_u32::<BigEndian>(self.minor_version)?;
|
||||||
|
@ -61,7 +66,7 @@ impl<W: Write> WriteBox<&mut BufWriter<W>> for FtypBox {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::read_box_header;
|
use crate::atoms::BoxHeader;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -77,21 +82,15 @@ mod tests {
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
{
|
src_box.write_box(&mut buf).unwrap();
|
||||||
let mut writer = BufWriter::new(&mut buf);
|
|
||||||
src_box.write_box(&mut writer).unwrap();
|
|
||||||
}
|
|
||||||
assert_eq!(buf.len(), src_box.box_size() as usize);
|
assert_eq!(buf.len(), src_box.box_size() as usize);
|
||||||
|
|
||||||
{
|
let mut reader = Cursor::new(&buf);
|
||||||
let mut reader = BufReader::new(Cursor::new(&buf));
|
let header = BoxHeader::read(&mut reader).unwrap();
|
||||||
let header = read_box_header(&mut reader, 0).unwrap();
|
|
||||||
assert_eq!(header.name, BoxType::FtypBox);
|
assert_eq!(header.name, BoxType::FtypBox);
|
||||||
assert_eq!(src_box.box_size(), header.size);
|
assert_eq!(src_box.box_size(), header.size);
|
||||||
|
|
||||||
let dst_box = FtypBox::read_box(&mut reader, header.size).unwrap();
|
let dst_box = FtypBox::read_box(&mut reader, header.size).unwrap();
|
||||||
|
|
||||||
assert_eq!(src_box, dst_box);
|
assert_eq!(src_box, dst_box);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use std::io::{BufReader, SeekFrom, Seek, Read, BufWriter, Write};
|
use std::io::{Seek, Read, Write};
|
||||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
use crate::atoms::*;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq)]
|
#[derive(Debug, Default, PartialEq)]
|
||||||
|
@ -13,7 +14,7 @@ pub struct HdlrBox {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mp4Box for HdlrBox {
|
impl Mp4Box for HdlrBox {
|
||||||
fn box_type(&self) -> BoxType {
|
fn box_type() -> BoxType {
|
||||||
BoxType::HdlrBox
|
BoxType::HdlrBox
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,18 +23,18 @@ impl Mp4Box for HdlrBox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for HdlrBox {
|
impl<R: Read + Seek> ReadBox<&mut R> for HdlrBox {
|
||||||
fn read_box(reader: &mut BufReader<R>, size: u64) -> Result<Self> {
|
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||||
let current = reader.seek(SeekFrom::Current(0))?; // Current cursor position.
|
let start = get_box_start(reader)?;
|
||||||
|
|
||||||
let (version, flags) = read_box_header_ext(reader)?;
|
let (version, flags) = read_box_header_ext(reader)?;
|
||||||
|
|
||||||
reader.read_u32::<BigEndian>()?; // pre-defined
|
reader.read_u32::<BigEndian>()?; // pre-defined
|
||||||
let handler = reader.read_u32::<BigEndian>()?;
|
let handler = reader.read_u32::<BigEndian>()?;
|
||||||
|
|
||||||
let n = reader.seek(SeekFrom::Current(12))?; // 12 bytes reserved.
|
skip_read(reader, 12)?; // reserved
|
||||||
|
|
||||||
let buf_size = (size - (n - current)) - HEADER_SIZE - 1;
|
let buf_size = size - HEADER_SIZE - HEADER_EXT_SIZE - 20 - 1;
|
||||||
let mut buf = vec![0u8; buf_size as usize];
|
let mut buf = vec![0u8; buf_size as usize];
|
||||||
reader.read_exact(&mut buf)?;
|
reader.read_exact(&mut buf)?;
|
||||||
|
|
||||||
|
@ -45,7 +46,7 @@ impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for HdlrBox {
|
||||||
_ => String::from("null"),
|
_ => String::from("null"),
|
||||||
};
|
};
|
||||||
|
|
||||||
skip_read(reader, current, size)?;
|
skip_read_to(reader, start + size)?;
|
||||||
|
|
||||||
Ok(HdlrBox {
|
Ok(HdlrBox {
|
||||||
version,
|
version,
|
||||||
|
@ -56,10 +57,10 @@ impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for HdlrBox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: Write> WriteBox<&mut BufWriter<W>> for HdlrBox {
|
impl<W: Write> WriteBox<&mut W> for HdlrBox {
|
||||||
fn write_box(&self, writer: &mut BufWriter<W>) -> Result<u64> {
|
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||||
let size = self.box_size();
|
let size = self.box_size();
|
||||||
BoxHeader::new(self.box_type(), size).write_box(writer)?;
|
BoxHeader::new(Self::box_type(), size).write(writer)?;
|
||||||
|
|
||||||
write_box_header_ext(writer, self.version, self.flags)?;
|
write_box_header_ext(writer, self.version, self.flags)?;
|
||||||
|
|
||||||
|
@ -81,7 +82,7 @@ impl<W: Write> WriteBox<&mut BufWriter<W>> for HdlrBox {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::read_box_header;
|
use crate::atoms::BoxHeader;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -93,21 +94,15 @@ mod tests {
|
||||||
name: String::from("VideoHandler"),
|
name: String::from("VideoHandler"),
|
||||||
};
|
};
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
{
|
src_box.write_box(&mut buf).unwrap();
|
||||||
let mut writer = BufWriter::new(&mut buf);
|
|
||||||
src_box.write_box(&mut writer).unwrap();
|
|
||||||
}
|
|
||||||
assert_eq!(buf.len(), src_box.box_size() as usize);
|
assert_eq!(buf.len(), src_box.box_size() as usize);
|
||||||
|
|
||||||
{
|
let mut reader = Cursor::new(&buf);
|
||||||
let mut reader = BufReader::new(Cursor::new(&buf));
|
let header = BoxHeader::read(&mut reader).unwrap();
|
||||||
let header = read_box_header(&mut reader, 0).unwrap();
|
|
||||||
assert_eq!(header.name, BoxType::HdlrBox);
|
assert_eq!(header.name, BoxType::HdlrBox);
|
||||||
assert_eq!(src_box.box_size(), header.size);
|
assert_eq!(src_box.box_size(), header.size);
|
||||||
|
|
||||||
let dst_box = HdlrBox::read_box(&mut reader, header.size).unwrap();
|
let dst_box = HdlrBox::read_box(&mut reader, header.size).unwrap();
|
||||||
|
|
||||||
assert_eq!(src_box, dst_box);
|
assert_eq!(src_box, dst_box);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use std::io::{BufReader, SeekFrom, Seek, Read, BufWriter, Write};
|
use std::io::{Seek, Read, Write};
|
||||||
use std::char::{decode_utf16, REPLACEMENT_CHARACTER};
|
use std::char::{decode_utf16, REPLACEMENT_CHARACTER};
|
||||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
use crate::atoms::*;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
|
@ -31,7 +32,7 @@ impl Default for MdhdBox {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mp4Box for MdhdBox {
|
impl Mp4Box for MdhdBox {
|
||||||
fn box_type(&self) -> BoxType {
|
fn box_type() -> BoxType {
|
||||||
BoxType::MdhdBox
|
BoxType::MdhdBox
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,9 +50,9 @@ impl Mp4Box for MdhdBox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for MdhdBox {
|
impl<R: Read + Seek> ReadBox<&mut R> for MdhdBox {
|
||||||
fn read_box(reader: &mut BufReader<R>, size: u64) -> Result<Self> {
|
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||||
let current = reader.seek(SeekFrom::Current(0))?; // Current cursor position.
|
let start = get_box_start(reader)?;
|
||||||
|
|
||||||
let (version, flags) = read_box_header_ext(reader)?;
|
let (version, flags) = read_box_header_ext(reader)?;
|
||||||
|
|
||||||
|
@ -74,7 +75,8 @@ impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for MdhdBox {
|
||||||
};
|
};
|
||||||
let language_code = reader.read_u16::<BigEndian>()?;
|
let language_code = reader.read_u16::<BigEndian>()?;
|
||||||
let language = get_language_string(language_code);
|
let language = get_language_string(language_code);
|
||||||
skip_read(reader, current, size)?;
|
|
||||||
|
skip_read_to(reader, start + size)?;
|
||||||
|
|
||||||
Ok(MdhdBox {
|
Ok(MdhdBox {
|
||||||
version,
|
version,
|
||||||
|
@ -88,10 +90,10 @@ impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for MdhdBox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: Write> WriteBox<&mut BufWriter<W>> for MdhdBox {
|
impl<W: Write> WriteBox<&mut W> for MdhdBox {
|
||||||
fn write_box(&self, writer: &mut BufWriter<W>) -> Result<u64> {
|
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||||
let size = self.box_size();
|
let size = self.box_size();
|
||||||
BoxHeader::new(self.box_type(), size).write_box(writer)?;
|
BoxHeader::new(Self::box_type(), size).write(writer)?;
|
||||||
|
|
||||||
write_box_header_ext(writer, self.version, self.flags)?;
|
write_box_header_ext(writer, self.version, self.flags)?;
|
||||||
|
|
||||||
|
@ -142,7 +144,7 @@ fn get_language_code(language: &str) -> u16 {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::read_box_header;
|
use crate::atoms::BoxHeader;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
||||||
fn test_language_code(lang: &str) {
|
fn test_language_code(lang: &str) {
|
||||||
|
@ -170,23 +172,17 @@ mod tests {
|
||||||
language: String::from("und"),
|
language: String::from("und"),
|
||||||
};
|
};
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
{
|
src_box.write_box(&mut buf).unwrap();
|
||||||
let mut writer = BufWriter::new(&mut buf);
|
|
||||||
src_box.write_box(&mut writer).unwrap();
|
|
||||||
}
|
|
||||||
assert_eq!(buf.len(), src_box.box_size() as usize);
|
assert_eq!(buf.len(), src_box.box_size() as usize);
|
||||||
|
|
||||||
{
|
let mut reader = Cursor::new(&buf);
|
||||||
let mut reader = BufReader::new(Cursor::new(&buf));
|
let header = BoxHeader::read(&mut reader).unwrap();
|
||||||
let header = read_box_header(&mut reader, 0).unwrap();
|
|
||||||
assert_eq!(header.name, BoxType::MdhdBox);
|
assert_eq!(header.name, BoxType::MdhdBox);
|
||||||
assert_eq!(src_box.box_size(), header.size);
|
assert_eq!(src_box.box_size(), header.size);
|
||||||
|
|
||||||
let dst_box = MdhdBox::read_box(&mut reader, header.size).unwrap();
|
let dst_box = MdhdBox::read_box(&mut reader, header.size).unwrap();
|
||||||
|
|
||||||
assert_eq!(src_box, dst_box);
|
assert_eq!(src_box, dst_box);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mdhd64() {
|
fn test_mdhd64() {
|
||||||
|
@ -200,21 +196,15 @@ mod tests {
|
||||||
language: String::from("eng"),
|
language: String::from("eng"),
|
||||||
};
|
};
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
{
|
src_box.write_box(&mut buf).unwrap();
|
||||||
let mut writer = BufWriter::new(&mut buf);
|
|
||||||
src_box.write_box(&mut writer).unwrap();
|
|
||||||
}
|
|
||||||
assert_eq!(buf.len(), src_box.box_size() as usize);
|
assert_eq!(buf.len(), src_box.box_size() as usize);
|
||||||
|
|
||||||
{
|
let mut reader = Cursor::new(&buf);
|
||||||
let mut reader = BufReader::new(Cursor::new(&buf));
|
let header = BoxHeader::read(&mut reader).unwrap();
|
||||||
let header = read_box_header(&mut reader, 0).unwrap();
|
|
||||||
assert_eq!(header.name, BoxType::MdhdBox);
|
assert_eq!(header.name, BoxType::MdhdBox);
|
||||||
assert_eq!(src_box.box_size(), header.size);
|
assert_eq!(src_box.box_size(), header.size);
|
||||||
|
|
||||||
let dst_box = MdhdBox::read_box(&mut reader, header.size).unwrap();
|
let dst_box = MdhdBox::read_box(&mut reader, header.size).unwrap();
|
||||||
|
|
||||||
assert_eq!(src_box, dst_box);
|
assert_eq!(src_box, dst_box);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::io::{BufReader, SeekFrom, Seek, Read, BufWriter, Write};
|
use std::io::{Seek, SeekFrom, Read, Write};
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
use crate::atoms::*;
|
||||||
use crate::atoms::{mdhd::MdhdBox, hdlr::HdlrBox, minf::MinfBox};
|
use crate::atoms::{mdhd::MdhdBox, hdlr::HdlrBox, minf::MinfBox};
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,34 +19,36 @@ impl MdiaBox {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mp4Box for MdiaBox {
|
impl Mp4Box for MdiaBox {
|
||||||
fn box_type(&self) -> BoxType {
|
fn box_type() -> BoxType {
|
||||||
BoxType::MdiaBox
|
BoxType::MdiaBox
|
||||||
}
|
}
|
||||||
|
|
||||||
fn box_size(&self) -> u64 {
|
fn box_size(&self) -> u64 {
|
||||||
let mut size = HEADER_SIZE;
|
let mut size = HEADER_SIZE;
|
||||||
if let Some(mdhd) = &self.mdhd {
|
if let Some(ref mdhd) = self.mdhd {
|
||||||
size += mdhd.box_size();
|
size += mdhd.box_size();
|
||||||
}
|
}
|
||||||
if let Some(hdlr) = &self.hdlr {
|
if let Some(ref hdlr) = self.hdlr {
|
||||||
size += hdlr.box_size();
|
size += hdlr.box_size();
|
||||||
}
|
}
|
||||||
if let Some(minf) = &self.minf {
|
if let Some(ref minf) = self.minf {
|
||||||
size += minf.box_size();
|
size += minf.box_size();
|
||||||
}
|
}
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for MdiaBox {
|
impl<R: Read + Seek> ReadBox<&mut R> for MdiaBox {
|
||||||
fn read_box(reader: &mut BufReader<R>, size: u64) -> Result<Self> {
|
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||||
let current = reader.seek(SeekFrom::Current(0))?; // Current cursor position.
|
let start = get_box_start(reader)?;
|
||||||
|
|
||||||
let mut mdia = MdiaBox::new();
|
let mut mdia = MdiaBox::new();
|
||||||
|
|
||||||
let start = 0u64;
|
let mut current = reader.seek(SeekFrom::Current(0))?;
|
||||||
while start < size {
|
let end = start + size;
|
||||||
|
while current < end {
|
||||||
// Get box header.
|
// Get box header.
|
||||||
let header = read_box_header(reader, start)?;
|
let header = BoxHeader::read(reader)?;
|
||||||
let BoxHeader{ name, size: s } = header;
|
let BoxHeader{ name, size: s } = header;
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
|
@ -61,27 +64,33 @@ impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for MdiaBox {
|
||||||
let minf = MinfBox::read_box(reader, s)?;
|
let minf = MinfBox::read_box(reader, s)?;
|
||||||
mdia.minf = Some(minf);
|
mdia.minf = Some(minf);
|
||||||
}
|
}
|
||||||
_ => break
|
_ => {
|
||||||
|
// XXX warn!()
|
||||||
|
skip_box(reader, s)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
skip_read(reader, current, size)?;
|
|
||||||
|
current = reader.seek(SeekFrom::Current(0))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_read_to(reader, start + size)?;
|
||||||
|
|
||||||
Ok(mdia)
|
Ok(mdia)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: Write> WriteBox<&mut BufWriter<W>> for MdiaBox {
|
impl<W: Write> WriteBox<&mut W> for MdiaBox {
|
||||||
fn write_box(&self, writer: &mut BufWriter<W>) -> Result<u64> {
|
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||||
let size = self.box_size();
|
let size = self.box_size();
|
||||||
BoxHeader::new(self.box_type(), size).write_box(writer)?;
|
BoxHeader::new(Self::box_type(), size).write(writer)?;
|
||||||
|
|
||||||
if let Some(mdhd) = &self.mdhd {
|
if let Some(ref mdhd) = self.mdhd {
|
||||||
mdhd.write_box(writer)?;
|
mdhd.write_box(writer)?;
|
||||||
}
|
}
|
||||||
if let Some(hdlr) = &self.hdlr {
|
if let Some(ref hdlr) = self.hdlr {
|
||||||
hdlr.write_box(writer)?;
|
hdlr.write_box(writer)?;
|
||||||
}
|
}
|
||||||
if let Some(minf) = &self.minf {
|
if let Some(ref minf) = self.minf {
|
||||||
minf.write_box(writer)?;
|
minf.write_box(writer)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
use std::io::{BufReader, SeekFrom, Seek, Read, BufWriter, Write};
|
use std::io::{Seek, SeekFrom, Read, Write};
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use crate::atoms::{vmhd::VmhdBox, stbl::StblBox};
|
use crate::atoms::*;
|
||||||
|
use crate::atoms::{vmhd::VmhdBox, smhd::SmhdBox, stbl::StblBox};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct MinfBox {
|
pub struct MinfBox {
|
||||||
pub vmhd: Option<VmhdBox>,
|
pub vmhd: Option<VmhdBox>,
|
||||||
|
pub smhd: Option<SmhdBox>,
|
||||||
pub stbl: Option<StblBox>,
|
pub stbl: Option<StblBox>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,31 +19,36 @@ impl MinfBox {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mp4Box for MinfBox {
|
impl Mp4Box for MinfBox {
|
||||||
fn box_type(&self) -> BoxType {
|
fn box_type() -> BoxType {
|
||||||
BoxType::MinfBox
|
BoxType::MinfBox
|
||||||
}
|
}
|
||||||
|
|
||||||
fn box_size(&self) -> u64 {
|
fn box_size(&self) -> u64 {
|
||||||
let mut size = HEADER_SIZE;
|
let mut size = HEADER_SIZE;
|
||||||
if let Some(vmhd) = &self.vmhd {
|
if let Some(ref vmhd) = self.vmhd {
|
||||||
size += vmhd.box_size();
|
size += vmhd.box_size();
|
||||||
}
|
}
|
||||||
if let Some(stbl) = &self.stbl {
|
if let Some(ref smhd) = self.smhd {
|
||||||
|
size += smhd.box_size();
|
||||||
|
}
|
||||||
|
if let Some(ref stbl) = self.stbl {
|
||||||
size += stbl.box_size();
|
size += stbl.box_size();
|
||||||
}
|
}
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for MinfBox {
|
impl<R: Read + Seek> ReadBox<&mut R> for MinfBox {
|
||||||
fn read_box(reader: &mut BufReader<R>, size: u64) -> Result<Self> {
|
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||||
let current = reader.seek(SeekFrom::Current(0))?; // Current cursor position.
|
let start = get_box_start(reader)?;
|
||||||
|
|
||||||
let mut minf = MinfBox::new();
|
let mut minf = MinfBox::new();
|
||||||
|
|
||||||
let mut start = 0u64;
|
let mut current = reader.seek(SeekFrom::Current(0))?;
|
||||||
while start < size {
|
let end = start + size;
|
||||||
|
while current < end {
|
||||||
// Get box header.
|
// Get box header.
|
||||||
let header = read_box_header(reader, start)?;
|
let header = BoxHeader::read(reader)?;
|
||||||
let BoxHeader{ name, size: s } = header;
|
let BoxHeader{ name, size: s } = header;
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
|
@ -50,33 +57,43 @@ impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for MinfBox {
|
||||||
minf.vmhd = Some(vmhd);
|
minf.vmhd = Some(vmhd);
|
||||||
}
|
}
|
||||||
BoxType::SmhdBox => {
|
BoxType::SmhdBox => {
|
||||||
start = s - HEADER_SIZE;
|
let smhd = SmhdBox::read_box(reader, s)?;
|
||||||
|
minf.smhd = Some(smhd);
|
||||||
}
|
}
|
||||||
BoxType::DinfBox => {
|
BoxType::DinfBox => {// XXX warn!()
|
||||||
start = s - HEADER_SIZE;
|
skip_box(reader, s)?;
|
||||||
}
|
}
|
||||||
BoxType::StblBox => {
|
BoxType::StblBox => {
|
||||||
let stbl = StblBox::read_box(reader, s)?;
|
let stbl = StblBox::read_box(reader, s)?;
|
||||||
minf.stbl = Some(stbl);
|
minf.stbl = Some(stbl);
|
||||||
}
|
}
|
||||||
_ => break
|
_ => {
|
||||||
|
// XXX warn!()
|
||||||
|
skip_box(reader, s)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
skip_read(reader, current, size)?;
|
|
||||||
|
current = reader.seek(SeekFrom::Current(0))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_read_to(reader, start + size)?;
|
||||||
|
|
||||||
Ok(minf)
|
Ok(minf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: Write> WriteBox<&mut BufWriter<W>> for MinfBox {
|
impl<W: Write> WriteBox<&mut W> for MinfBox {
|
||||||
fn write_box(&self, writer: &mut BufWriter<W>) -> Result<u64> {
|
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||||
let size = self.box_size();
|
let size = self.box_size();
|
||||||
BoxHeader::new(self.box_type(), size).write_box(writer)?;
|
BoxHeader::new(Self::box_type(), size).write(writer)?;
|
||||||
|
|
||||||
if let Some(vmhd) = &self.vmhd {
|
if let Some(ref vmhd) = self.vmhd {
|
||||||
vmhd.write_box(writer)?;
|
vmhd.write_box(writer)?;
|
||||||
}
|
}
|
||||||
if let Some(stbl) = &self.stbl {
|
if let Some(ref smhd) = self.smhd {
|
||||||
|
smhd.write_box(writer)?;
|
||||||
|
}
|
||||||
|
if let Some(ref stbl) = self.stbl {
|
||||||
stbl.write_box(writer)?;
|
stbl.write_box(writer)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
130
src/atoms/mod.rs
130
src/atoms/mod.rs
|
@ -1,5 +1,5 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io::{BufReader, SeekFrom, Seek, Read, BufWriter, Write};
|
use std::io::{Seek, SeekFrom, Read, Write};
|
||||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
@ -16,13 +16,24 @@ mod mdhd;
|
||||||
mod hdlr;
|
mod hdlr;
|
||||||
mod minf;
|
mod minf;
|
||||||
mod vmhd;
|
mod vmhd;
|
||||||
|
mod smhd;
|
||||||
mod stbl;
|
mod stbl;
|
||||||
mod stts;
|
|
||||||
mod stsd;
|
mod stsd;
|
||||||
|
mod stts;
|
||||||
|
mod ctts;
|
||||||
|
mod stss;
|
||||||
|
mod stsc;
|
||||||
|
mod stsz;
|
||||||
|
mod stco;
|
||||||
|
mod co64;
|
||||||
|
mod avc;
|
||||||
|
mod mp4a;
|
||||||
|
|
||||||
pub use ftyp::FtypBox;
|
pub use ftyp::FtypBox;
|
||||||
pub use moov::MoovBox;
|
pub use moov::MoovBox;
|
||||||
|
|
||||||
|
const HEADER_SIZE: u64 = 8;
|
||||||
|
// const HEADER_LARGE_SIZE: u64 = 16;
|
||||||
pub const HEADER_EXT_SIZE: u64 = 4;
|
pub const HEADER_EXT_SIZE: u64 = 4;
|
||||||
|
|
||||||
macro_rules! boxtype {
|
macro_rules! boxtype {
|
||||||
|
@ -71,12 +82,20 @@ boxtype!{
|
||||||
StblBox => 0x7374626c,
|
StblBox => 0x7374626c,
|
||||||
StsdBox => 0x73747364,
|
StsdBox => 0x73747364,
|
||||||
SttsBox => 0x73747473,
|
SttsBox => 0x73747473,
|
||||||
|
CttsBox => 0x63747473,
|
||||||
|
StssBox => 0x73747373,
|
||||||
|
StscBox => 0x73747363,
|
||||||
|
StszBox => 0x7374737A,
|
||||||
|
StcoBox => 0x7374636F,
|
||||||
|
Co64Box => 0x636F3634,
|
||||||
TrakBox => 0x7472616b,
|
TrakBox => 0x7472616b,
|
||||||
UdtaBox => 0x75647461,
|
UdtaBox => 0x75647461,
|
||||||
DinfBox => 0x64696e66,
|
DinfBox => 0x64696e66,
|
||||||
SmhdBox => 0x736d6864,
|
SmhdBox => 0x736d6864,
|
||||||
Avc1Box => 0x61766331,
|
Avc1Box => 0x61766331,
|
||||||
Mp4aBox => 0x6d703461
|
AvcCBox => 0x61766343,
|
||||||
|
Mp4aBox => 0x6d703461,
|
||||||
|
EsdsBox => 0x65736473
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for BoxType {
|
impl fmt::Debug for BoxType {
|
||||||
|
@ -86,6 +105,13 @@ impl fmt::Debug for BoxType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for BoxType {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let fourcc: FourCC = From::from(self.clone());
|
||||||
|
write!(f, "{}", fourcc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, PartialEq, Clone)]
|
#[derive(Default, PartialEq, Clone)]
|
||||||
pub struct FourCC {
|
pub struct FourCC {
|
||||||
pub value: String
|
pub value: String
|
||||||
|
@ -167,7 +193,7 @@ impl fmt::Display for FourCC {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Mp4Box: Sized {
|
pub trait Mp4Box: Sized {
|
||||||
fn box_type(&self) -> BoxType;
|
fn box_type() -> BoxType;
|
||||||
fn box_size(&self) -> u64;
|
fn box_size(&self) -> u64;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,23 +205,50 @@ pub trait WriteBox<T>: Sized {
|
||||||
fn write_box(&self, _: T) -> Result<u64>;
|
fn write_box(&self, _: T) -> Result<u64>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_box_header_ext<R: Read>(reader: &mut BufReader<R>) -> Result<(u8, u32)> {
|
#[derive(Debug, Clone, Copy)]
|
||||||
let version = reader.read_u8()?;
|
pub struct BoxHeader {
|
||||||
let flags_a = reader.read_u8()?;
|
pub name: BoxType,
|
||||||
let flags_b = reader.read_u8()?;
|
pub size: u64,
|
||||||
let flags_c = reader.read_u8()?;
|
|
||||||
let flags = u32::from(flags_a) << 16 | u32::from(flags_b) << 8 | u32::from(flags_c);
|
|
||||||
Ok((version, flags))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_box_header_ext<W: Write>(w: &mut BufWriter<W>, v: u8, f: u32) -> Result<u64> {
|
impl BoxHeader {
|
||||||
let d = u32::from(v) << 24 | f;
|
fn new(name: BoxType, size: u64) -> Self {
|
||||||
w.write_u32::<BigEndian>(d)?;
|
Self { name, size }
|
||||||
Ok(4)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<W: Write> WriteBox<&mut BufWriter<W>> for BoxHeader {
|
// TODO: if size is 0, then this box is the last one in the file
|
||||||
fn write_box(&self, writer: &mut BufWriter<W>) -> Result<u64> {
|
pub fn read<R: Read>(reader: &mut R) -> Result<Self> {
|
||||||
|
// Create and read to buf.
|
||||||
|
let mut buf = [0u8;8]; // 8 bytes for box header.
|
||||||
|
reader.read(&mut buf)?;
|
||||||
|
|
||||||
|
// Get size.
|
||||||
|
let s = buf[0..4].try_into().unwrap();
|
||||||
|
let size = 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(&mut buf)?;
|
||||||
|
let s = buf.try_into().unwrap();
|
||||||
|
let largesize = u64::from_be_bytes(s);
|
||||||
|
|
||||||
|
Ok(BoxHeader {
|
||||||
|
name: BoxType::from(typ),
|
||||||
|
size: largesize,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Ok(BoxHeader {
|
||||||
|
name: BoxType::from(typ),
|
||||||
|
size: size as u64,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write<W: Write>(&self, writer: &mut W) -> Result<u64> {
|
||||||
if self.size > u32::MAX as u64 {
|
if self.size > u32::MAX as u64 {
|
||||||
writer.write_u32::<BigEndian>(1)?;
|
writer.write_u32::<BigEndian>(1)?;
|
||||||
writer.write_u32::<BigEndian>(self.name.into())?;
|
writer.write_u32::<BigEndian>(self.name.into())?;
|
||||||
|
@ -209,19 +262,44 @@ impl<W: Write> WriteBox<&mut BufWriter<W>> for BoxHeader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn skip_read<R: Read + Seek>(reader: &mut BufReader<R>, current: u64, size: u64) -> Result<i64> {
|
pub fn read_box_header_ext<R: Read>(reader: &mut R) -> Result<(u8, u32)> {
|
||||||
let after = reader.seek(SeekFrom::Current(0))?;
|
let version = reader.read_u8()?;
|
||||||
let remaining_bytes = (size - (after - current)) as i64;
|
let flags = reader.read_u24::<BigEndian>()?;
|
||||||
let size = remaining_bytes - HEADER_SIZE as i64;
|
Ok((version, flags))
|
||||||
reader.seek(SeekFrom::Current(size))?;
|
|
||||||
Ok(size)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn skip_write<W: Write>(writer: &mut BufWriter<W>, size: u64) -> Result<u64> {
|
pub fn write_box_header_ext<W: Write>(w: &mut W, v: u8, f: u32) -> Result<u64> {
|
||||||
|
w.write_u8(v)?;
|
||||||
|
w.write_u24::<BigEndian>(f)?;
|
||||||
|
Ok(4)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_box_start<R: Seek>(reader: &mut R) -> Result<u64> {
|
||||||
|
Ok(reader.seek(SeekFrom::Current(0))? - HEADER_SIZE)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn skip_read<R: Read + Seek>(reader: &mut R, size: i64) -> Result<()> {
|
||||||
|
assert!(size >= 0);
|
||||||
|
reader.seek(SeekFrom::Current(size))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn skip_read_to<R: Read + Seek>(reader: &mut R, pos: u64) -> Result<()> {
|
||||||
|
reader.seek(SeekFrom::Start(pos))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn skip_box<R: Read + Seek>(reader: &mut R, size: u64) -> Result<()> {
|
||||||
|
let start = get_box_start(reader)?;
|
||||||
|
skip_read_to(reader, start + size)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn skip_write<W: Write>(writer: &mut W, size: u64) -> Result<()> {
|
||||||
for _ in 0..size {
|
for _ in 0..size {
|
||||||
writer.write_u8(0)?;
|
writer.write_u8(0)?;
|
||||||
}
|
}
|
||||||
Ok(size)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::io::{BufReader, Seek, Read, BufWriter, Write};
|
use std::io::{Seek, SeekFrom, Read, Write};
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
use crate::atoms::*;
|
||||||
use crate::atoms::{mvhd::MvhdBox, trak::TrakBox};
|
use crate::atoms::{mvhd::MvhdBox, trak::TrakBox};
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,7 +18,7 @@ impl MoovBox {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mp4Box for MoovBox {
|
impl Mp4Box for MoovBox {
|
||||||
fn box_type(&self) -> BoxType {
|
fn box_type() -> BoxType {
|
||||||
BoxType::MoovBox
|
BoxType::MoovBox
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,15 +31,17 @@ impl Mp4Box for MoovBox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for MoovBox {
|
impl<R: Read + Seek> ReadBox<&mut R> for MoovBox {
|
||||||
fn read_box(reader: &mut BufReader<R>, size: u64) -> Result<Self> {
|
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||||
|
let start = get_box_start(reader)?;
|
||||||
|
|
||||||
let mut moov = MoovBox::new();
|
let mut moov = MoovBox::new();
|
||||||
|
|
||||||
let mut start = 0u64;
|
let mut current = reader.seek(SeekFrom::Current(0))?;
|
||||||
while start < size {
|
let end = start + size;
|
||||||
|
while current < end {
|
||||||
// Get box header.
|
// Get box header.
|
||||||
let header = read_box_header(reader, start)?;
|
let header = BoxHeader::read(reader)?;
|
||||||
let BoxHeader{ name, size: s } = header;
|
let BoxHeader{ name, size: s } = header;
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
|
@ -46,23 +49,33 @@ impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for MoovBox {
|
||||||
moov.mvhd = MvhdBox::read_box(reader, s)?;
|
moov.mvhd = MvhdBox::read_box(reader, s)?;
|
||||||
}
|
}
|
||||||
BoxType::TrakBox => {
|
BoxType::TrakBox => {
|
||||||
let trak = TrakBox::read_box(reader, s)?;
|
let mut trak = TrakBox::read_box(reader, s)?;
|
||||||
|
trak.id = moov.traks.len() as u32 + 1;
|
||||||
moov.traks.push(trak);
|
moov.traks.push(trak);
|
||||||
}
|
}
|
||||||
BoxType::UdtaBox => {
|
BoxType::UdtaBox => {
|
||||||
start = s - HEADER_SIZE;
|
// XXX warn!()
|
||||||
|
skip_box(reader, s)?;
|
||||||
}
|
}
|
||||||
_ => break
|
_ => {
|
||||||
|
// XXX warn!()
|
||||||
|
skip_box(reader, s)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
current = reader.seek(SeekFrom::Current(0))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_read_to(reader, start + size)?;
|
||||||
|
|
||||||
Ok(moov)
|
Ok(moov)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: Write> WriteBox<&mut BufWriter<W>> for MoovBox {
|
impl<W: Write> WriteBox<&mut W> for MoovBox {
|
||||||
fn write_box(&self, writer: &mut BufWriter<W>) -> Result<u64> {
|
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||||
let size = self.box_size();
|
let size = self.box_size();
|
||||||
BoxHeader::new(self.box_type(), size).write_box(writer)?;
|
BoxHeader::new(Self::box_type(), size).write(writer)?;
|
||||||
|
|
||||||
self.mvhd.write_box(writer)?;
|
self.mvhd.write_box(writer)?;
|
||||||
for trak in self.traks.iter() {
|
for trak in self.traks.iter() {
|
||||||
|
|
411
src/atoms/mp4a.rs
Normal file
411
src/atoms/mp4a.rs
Normal file
|
@ -0,0 +1,411 @@
|
||||||
|
use std::io::{Seek, Read, Write};
|
||||||
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
|
use crate::*;
|
||||||
|
use crate::atoms::*;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct Mp4aBox {
|
||||||
|
pub data_reference_index: u16,
|
||||||
|
pub channel_count: u16,
|
||||||
|
pub samplesize: u16,
|
||||||
|
pub samplerate: u32,
|
||||||
|
pub esds: EsdsBox,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Mp4aBox {
|
||||||
|
fn default() -> Self {
|
||||||
|
Mp4aBox {
|
||||||
|
data_reference_index: 0,
|
||||||
|
channel_count: 2,
|
||||||
|
samplesize: 16,
|
||||||
|
samplerate: 0, // XXX
|
||||||
|
esds: EsdsBox::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mp4Box for Mp4aBox {
|
||||||
|
fn box_type() -> BoxType {
|
||||||
|
BoxType::Mp4aBox
|
||||||
|
}
|
||||||
|
|
||||||
|
fn box_size(&self) -> u64 {
|
||||||
|
HEADER_SIZE + 8 + 74 + self.esds.box_size()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read + Seek> ReadBox<&mut R> for Mp4aBox {
|
||||||
|
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||||
|
let start = get_box_start(reader)?;
|
||||||
|
|
||||||
|
reader.read_u32::<BigEndian>()?; // reserved
|
||||||
|
reader.read_u16::<BigEndian>()?; // reserved
|
||||||
|
let data_reference_index = reader.read_u16::<BigEndian>()?;
|
||||||
|
|
||||||
|
reader.read_u64::<BigEndian>()?; // reserved
|
||||||
|
let channel_count = reader.read_u16::<BigEndian>()?;
|
||||||
|
let samplesize = reader.read_u16::<BigEndian>()?;
|
||||||
|
reader.read_u32::<BigEndian>()?; // pre-defined, reserved
|
||||||
|
let samplerate = reader.read_u32::<BigEndian>()?;
|
||||||
|
|
||||||
|
let header = BoxHeader::read(reader)?;
|
||||||
|
let BoxHeader{ name, size: s } = header;
|
||||||
|
if name == BoxType::EsdsBox {
|
||||||
|
let esds = EsdsBox::read_box(reader, s)?;
|
||||||
|
|
||||||
|
skip_read_to(reader, start + size)?;
|
||||||
|
|
||||||
|
Ok(Mp4aBox {
|
||||||
|
data_reference_index,
|
||||||
|
channel_count,
|
||||||
|
samplesize,
|
||||||
|
samplerate,
|
||||||
|
esds,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(Error::InvalidData("esds not found"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)?;
|
||||||
|
|
||||||
|
writer.write_u32::<BigEndian>(0)?; // reserved
|
||||||
|
writer.write_u16::<BigEndian>(0)?; // reserved
|
||||||
|
writer.write_u16::<BigEndian>(self.data_reference_index)?;
|
||||||
|
|
||||||
|
writer.write_u64::<BigEndian>(0)?; // reserved
|
||||||
|
writer.write_u16::<BigEndian>(self.channel_count)?;
|
||||||
|
writer.write_u16::<BigEndian>(self.samplesize)?;
|
||||||
|
writer.write_u32::<BigEndian>(0)?; // reserved
|
||||||
|
writer.write_u32::<BigEndian>(self.samplerate)?;
|
||||||
|
|
||||||
|
self.esds.write_box(writer)?;
|
||||||
|
|
||||||
|
Ok(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq)]
|
||||||
|
pub struct EsdsBox {
|
||||||
|
pub version: u8,
|
||||||
|
pub flags: u32,
|
||||||
|
pub es_desc: ESDescriptor,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mp4Box for EsdsBox {
|
||||||
|
fn box_type() -> BoxType {
|
||||||
|
BoxType::EsdsBox
|
||||||
|
}
|
||||||
|
|
||||||
|
fn box_size(&self) -> u64 {
|
||||||
|
HEADER_SIZE + HEADER_EXT_SIZE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read + Seek> ReadBox<&mut R> for EsdsBox {
|
||||||
|
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||||
|
let start = get_box_start(reader)?;
|
||||||
|
|
||||||
|
let (version, flags) = read_box_header_ext(reader)?;
|
||||||
|
|
||||||
|
let es_desc = ESDescriptor::read_desc(reader)?;
|
||||||
|
|
||||||
|
skip_read_to(reader, start + size)?;
|
||||||
|
|
||||||
|
Ok(EsdsBox {
|
||||||
|
version,
|
||||||
|
flags,
|
||||||
|
es_desc,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)?;
|
||||||
|
|
||||||
|
write_box_header_ext(writer, self.version, self.flags)?;
|
||||||
|
|
||||||
|
Ok(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
trait Descriptor: Sized {
|
||||||
|
fn desc_tag() -> u8;
|
||||||
|
fn desc_size() -> u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
trait ReadDesc<T>: Sized {
|
||||||
|
fn read_desc(_: T) -> 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()?;
|
||||||
|
|
||||||
|
let mut size: u32 = 0;
|
||||||
|
for _ in 0..4 {
|
||||||
|
let b = reader.read_u8()?;
|
||||||
|
size = (size << 7) | (b & 0x7F) as u32;
|
||||||
|
if b & 0x80 == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((tag, size))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_desc<W: Write>(writer: &mut W, tag: u8, size: u32) -> Result<u64> {
|
||||||
|
writer.write_u8(tag)?;
|
||||||
|
|
||||||
|
if size > 0x0FFFFFFF {
|
||||||
|
return Err(Error::InvalidData("invalid descriptor length range"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let nbytes = match size {
|
||||||
|
0x0..=0x7F => 1,
|
||||||
|
0x80..=0x3FFF => 2,
|
||||||
|
0x4000..=0x1FFFFF => 3,
|
||||||
|
_ => 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
for i in 0..nbytes {
|
||||||
|
let mut b = (size >> ((3 - i) * 7)) as u8 & 0x7F;
|
||||||
|
if i < nbytes - 1 {
|
||||||
|
b |= 0x80;
|
||||||
|
}
|
||||||
|
writer.write_u8(b)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(1 + nbytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq)]
|
||||||
|
pub struct ESDescriptor {
|
||||||
|
pub tag: u8,
|
||||||
|
pub size: u32,
|
||||||
|
|
||||||
|
pub es_id: u16,
|
||||||
|
|
||||||
|
pub dec_config: DecoderConfigDescriptor,
|
||||||
|
pub sl_config: SLConfigDescriptor,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Descriptor for ESDescriptor {
|
||||||
|
fn desc_tag() -> u8 {
|
||||||
|
0x03
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX size > 0x7F
|
||||||
|
fn desc_size() -> u32 {
|
||||||
|
2 + 3
|
||||||
|
+ DecoderConfigDescriptor::desc_size()
|
||||||
|
+ SLConfigDescriptor::desc_size()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read + Seek> ReadDesc<&mut R> for ESDescriptor {
|
||||||
|
fn read_desc(reader: &mut R) -> Result<Self> {
|
||||||
|
let (tag, size) = read_desc(reader)?;
|
||||||
|
if tag != Self::desc_tag() {
|
||||||
|
return Err(Error::InvalidData("ESDescriptor not found"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let es_id = reader.read_u16::<BigEndian>()?;
|
||||||
|
reader.read_u8()?; // XXX flags must be 0
|
||||||
|
|
||||||
|
let dec_config = DecoderConfigDescriptor::read_desc(reader)?;
|
||||||
|
let sl_config = SLConfigDescriptor::read_desc(reader)?;
|
||||||
|
|
||||||
|
Ok(ESDescriptor {
|
||||||
|
tag,
|
||||||
|
size,
|
||||||
|
es_id,
|
||||||
|
dec_config,
|
||||||
|
sl_config,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: Write> WriteDesc<&mut W> for ESDescriptor {
|
||||||
|
fn write_desc(&self, writer: &mut W) -> Result<u32> {
|
||||||
|
write_desc(writer, self.tag, self.size)?;
|
||||||
|
|
||||||
|
Ok(self.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq)]
|
||||||
|
pub struct DecoderConfigDescriptor {
|
||||||
|
pub tag: u8,
|
||||||
|
pub size: u32,
|
||||||
|
|
||||||
|
pub object_type_indication: u8,
|
||||||
|
pub stream_type: u8,
|
||||||
|
pub up_stream: u8,
|
||||||
|
pub buffer_size_db: u32,
|
||||||
|
pub max_bitrate: u32,
|
||||||
|
pub avg_bitrate: u32,
|
||||||
|
|
||||||
|
pub dec_specific: DecoderSpecificDescriptor,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Descriptor for DecoderConfigDescriptor {
|
||||||
|
fn desc_tag() -> u8 {
|
||||||
|
0x04
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX size > 0x7F
|
||||||
|
fn desc_size() -> u32 {
|
||||||
|
2 + 13 + DecoderSpecificDescriptor::desc_size()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read + Seek> ReadDesc<&mut R> for DecoderConfigDescriptor {
|
||||||
|
fn read_desc(reader: &mut R) -> Result<Self> {
|
||||||
|
let (tag, size) = read_desc(reader)?;
|
||||||
|
if tag != Self::desc_tag() {
|
||||||
|
return Err(Error::InvalidData("DecoderConfigDescriptor not found"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let object_type_indication = reader.read_u8()?;
|
||||||
|
let byte_a = reader.read_u8()?;
|
||||||
|
let stream_type = byte_a & 0xFC;
|
||||||
|
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 dec_specific = DecoderSpecificDescriptor::read_desc(reader)?;
|
||||||
|
|
||||||
|
// XXX skip_read
|
||||||
|
for _ in DecoderConfigDescriptor::desc_size()..size-1 {
|
||||||
|
reader.read_u8()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(DecoderConfigDescriptor {
|
||||||
|
tag,
|
||||||
|
size,
|
||||||
|
object_type_indication,
|
||||||
|
stream_type,
|
||||||
|
up_stream,
|
||||||
|
buffer_size_db,
|
||||||
|
max_bitrate,
|
||||||
|
avg_bitrate,
|
||||||
|
dec_specific,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: Write> WriteDesc<&mut W> for DecoderConfigDescriptor {
|
||||||
|
fn write_desc(&self, writer: &mut W) -> Result<u32> {
|
||||||
|
write_desc(writer, self.tag, self.size)?;
|
||||||
|
|
||||||
|
Ok(self.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq)]
|
||||||
|
pub struct DecoderSpecificDescriptor {
|
||||||
|
pub tag: u8,
|
||||||
|
pub size: u32,
|
||||||
|
pub profile: u8,
|
||||||
|
pub freq_index: u8,
|
||||||
|
pub chan_conf: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Descriptor for DecoderSpecificDescriptor {
|
||||||
|
fn desc_tag() -> u8 {
|
||||||
|
0x05
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX size > 0x7F
|
||||||
|
fn desc_size() -> u32 {
|
||||||
|
2 + 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read + Seek> ReadDesc<&mut R> for DecoderSpecificDescriptor {
|
||||||
|
fn read_desc(reader: &mut R) -> Result<Self> {
|
||||||
|
let (tag, size) = read_desc(reader)?;
|
||||||
|
if tag != Self::desc_tag() {
|
||||||
|
return Err(Error::InvalidData("DecoderSpecificDescriptor not found"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let byte_a = reader.read_u8()?;
|
||||||
|
let byte_b = reader.read_u8()?;
|
||||||
|
let profile = byte_a >> 3;
|
||||||
|
let freq_index = ((byte_a & 0x07) << 1) + (byte_b >> 7);
|
||||||
|
let chan_conf = (byte_b >> 3) & 0x0F;
|
||||||
|
|
||||||
|
Ok(DecoderSpecificDescriptor {
|
||||||
|
tag,
|
||||||
|
size,
|
||||||
|
profile,
|
||||||
|
freq_index,
|
||||||
|
chan_conf,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: Write> WriteDesc<&mut W> for DecoderSpecificDescriptor {
|
||||||
|
fn write_desc(&self, writer: &mut W) -> Result<u32> {
|
||||||
|
write_desc(writer, self.tag, self.size)?;
|
||||||
|
|
||||||
|
Ok(self.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq)]
|
||||||
|
pub struct SLConfigDescriptor {
|
||||||
|
pub tag: u8,
|
||||||
|
pub size: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Descriptor for SLConfigDescriptor {
|
||||||
|
fn desc_tag() -> u8 {
|
||||||
|
0x06
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX size > 0x7F
|
||||||
|
fn desc_size() -> u32 {
|
||||||
|
2 + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read + Seek> ReadDesc<&mut R> for SLConfigDescriptor {
|
||||||
|
fn read_desc(reader: &mut R) -> Result<Self> {
|
||||||
|
let (tag, size) = read_desc(reader)?;
|
||||||
|
if tag != Self::desc_tag() {
|
||||||
|
return Err(Error::InvalidData("SLConfigDescriptor not found"));
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.read_u8()?; // pre-defined
|
||||||
|
|
||||||
|
Ok(SLConfigDescriptor {
|
||||||
|
tag,
|
||||||
|
size,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: Write> WriteDesc<&mut W> for SLConfigDescriptor {
|
||||||
|
fn write_desc(&self, writer: &mut W) -> Result<u32> {
|
||||||
|
write_desc(writer, self.tag, self.size)?;
|
||||||
|
|
||||||
|
Ok(self.size)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,9 @@
|
||||||
use std::io::{BufReader, SeekFrom, Seek, Read, BufWriter, Write};
|
use std::io::{Seek, Read, Write};
|
||||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use num_rational::Ratio;
|
use num_rational::Ratio;
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
use crate::atoms::*;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
|
@ -31,7 +32,7 @@ impl Default for MvhdBox {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mp4Box for MvhdBox {
|
impl Mp4Box for MvhdBox {
|
||||||
fn box_type(&self) -> BoxType {
|
fn box_type() -> BoxType {
|
||||||
BoxType::MvhdBox
|
BoxType::MvhdBox
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,9 +49,9 @@ impl Mp4Box for MvhdBox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for MvhdBox {
|
impl<R: Read + Seek> ReadBox<&mut R> for MvhdBox {
|
||||||
fn read_box(reader: &mut BufReader<R>, size: u64) -> Result<Self> {
|
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||||
let current = reader.seek(SeekFrom::Current(0))?; // Current cursor position.
|
let start = get_box_start(reader)?;
|
||||||
|
|
||||||
let (version, flags) = read_box_header_ext(reader)?;
|
let (version, flags) = read_box_header_ext(reader)?;
|
||||||
|
|
||||||
|
@ -73,7 +74,8 @@ impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for MvhdBox {
|
||||||
};
|
};
|
||||||
let numer = reader.read_u32::<BigEndian>()?;
|
let numer = reader.read_u32::<BigEndian>()?;
|
||||||
let rate = Ratio::new_raw(numer, 0x10000);
|
let rate = Ratio::new_raw(numer, 0x10000);
|
||||||
skip_read(reader, current, size)?;
|
|
||||||
|
skip_read_to(reader, start + size)?;
|
||||||
|
|
||||||
Ok(MvhdBox{
|
Ok(MvhdBox{
|
||||||
version,
|
version,
|
||||||
|
@ -87,10 +89,10 @@ impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for MvhdBox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: Write> WriteBox<&mut BufWriter<W>> for MvhdBox {
|
impl<W: Write> WriteBox<&mut W> for MvhdBox {
|
||||||
fn write_box(&self, writer: &mut BufWriter<W>) -> Result<u64> {
|
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||||
let size = self.box_size();
|
let size = self.box_size();
|
||||||
BoxHeader::new(self.box_type(), size).write_box(writer)?;
|
BoxHeader::new(Self::box_type(), size).write(writer)?;
|
||||||
|
|
||||||
write_box_header_ext(writer, self.version, self.flags)?;
|
write_box_header_ext(writer, self.version, self.flags)?;
|
||||||
|
|
||||||
|
@ -118,7 +120,7 @@ impl<W: Write> WriteBox<&mut BufWriter<W>> for MvhdBox {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::read_box_header;
|
use crate::atoms::BoxHeader;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -133,23 +135,17 @@ mod tests {
|
||||||
rate: Ratio::new_raw(0x00010000, 0x10000),
|
rate: Ratio::new_raw(0x00010000, 0x10000),
|
||||||
};
|
};
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
{
|
src_box.write_box(&mut buf).unwrap();
|
||||||
let mut writer = BufWriter::new(&mut buf);
|
|
||||||
src_box.write_box(&mut writer).unwrap();
|
|
||||||
}
|
|
||||||
assert_eq!(buf.len(), src_box.box_size() as usize);
|
assert_eq!(buf.len(), src_box.box_size() as usize);
|
||||||
|
|
||||||
{
|
let mut reader = Cursor::new(&buf);
|
||||||
let mut reader = BufReader::new(Cursor::new(&buf));
|
let header = BoxHeader::read(&mut reader).unwrap();
|
||||||
let header = read_box_header(&mut reader, 0).unwrap();
|
|
||||||
assert_eq!(header.name, BoxType::MvhdBox);
|
assert_eq!(header.name, BoxType::MvhdBox);
|
||||||
assert_eq!(src_box.box_size(), header.size);
|
assert_eq!(src_box.box_size(), header.size);
|
||||||
|
|
||||||
let dst_box = MvhdBox::read_box(&mut reader, header.size).unwrap();
|
let dst_box = MvhdBox::read_box(&mut reader, header.size).unwrap();
|
||||||
|
|
||||||
assert_eq!(src_box, dst_box);
|
assert_eq!(src_box, dst_box);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mvhd64() {
|
fn test_mvhd64() {
|
||||||
|
@ -163,21 +159,15 @@ mod tests {
|
||||||
rate: Ratio::new_raw(0x00010000, 0x10000),
|
rate: Ratio::new_raw(0x00010000, 0x10000),
|
||||||
};
|
};
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
{
|
src_box.write_box(&mut buf).unwrap();
|
||||||
let mut writer = BufWriter::new(&mut buf);
|
|
||||||
src_box.write_box(&mut writer).unwrap();
|
|
||||||
}
|
|
||||||
assert_eq!(buf.len(), src_box.box_size() as usize);
|
assert_eq!(buf.len(), src_box.box_size() as usize);
|
||||||
|
|
||||||
{
|
let mut reader = Cursor::new(&buf);
|
||||||
let mut reader = BufReader::new(Cursor::new(&buf));
|
let header = BoxHeader::read(&mut reader).unwrap();
|
||||||
let header = read_box_header(&mut reader, 0).unwrap();
|
|
||||||
assert_eq!(header.name, BoxType::MvhdBox);
|
assert_eq!(header.name, BoxType::MvhdBox);
|
||||||
assert_eq!(src_box.box_size(), header.size);
|
assert_eq!(src_box.box_size(), header.size);
|
||||||
|
|
||||||
let dst_box = MvhdBox::read_box(&mut reader, header.size).unwrap();
|
let dst_box = MvhdBox::read_box(&mut reader, header.size).unwrap();
|
||||||
|
|
||||||
assert_eq!(src_box, dst_box);
|
assert_eq!(src_box, dst_box);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
95
src/atoms/smhd.rs
Normal file
95
src/atoms/smhd.rs
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
use std::io::{Seek, Read, Write};
|
||||||
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
use num_rational::Ratio;
|
||||||
|
|
||||||
|
use crate::*;
|
||||||
|
use crate::atoms::*;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct SmhdBox {
|
||||||
|
pub version: u8,
|
||||||
|
pub flags: u32,
|
||||||
|
pub balance: Ratio<i16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for SmhdBox {
|
||||||
|
fn default() -> Self {
|
||||||
|
SmhdBox {
|
||||||
|
version: 0,
|
||||||
|
flags: 0,
|
||||||
|
balance: Ratio::new_raw(0, 0x100),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mp4Box for SmhdBox {
|
||||||
|
fn box_type() -> BoxType {
|
||||||
|
BoxType::SmhdBox
|
||||||
|
}
|
||||||
|
|
||||||
|
fn box_size(&self) -> u64 {
|
||||||
|
HEADER_SIZE + HEADER_EXT_SIZE + 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read + Seek> ReadBox<&mut R> for SmhdBox {
|
||||||
|
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||||
|
let start = get_box_start(reader)?;
|
||||||
|
|
||||||
|
let (version, flags) = read_box_header_ext(reader)?;
|
||||||
|
|
||||||
|
let balance_numer = reader.read_i16::<BigEndian>()?;
|
||||||
|
let balance = Ratio::new_raw(balance_numer, 0x100);
|
||||||
|
|
||||||
|
skip_read_to(reader, start + size)?;
|
||||||
|
|
||||||
|
Ok(SmhdBox {
|
||||||
|
version,
|
||||||
|
flags,
|
||||||
|
balance,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)?;
|
||||||
|
|
||||||
|
write_box_header_ext(writer, self.version, self.flags)?;
|
||||||
|
|
||||||
|
writer.write_i16::<BigEndian>(*self.balance.numer())?;
|
||||||
|
writer.write_u16::<BigEndian>(0)?; // reserved
|
||||||
|
|
||||||
|
Ok(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::atoms::BoxHeader;
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_smhd() {
|
||||||
|
let src_box = SmhdBox {
|
||||||
|
version: 0,
|
||||||
|
flags: 0,
|
||||||
|
balance: Ratio::new_raw(-0x100, 0x100),
|
||||||
|
};
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
src_box.write_box(&mut buf).unwrap();
|
||||||
|
assert_eq!(buf.len(), src_box.box_size() as usize);
|
||||||
|
|
||||||
|
let mut reader = Cursor::new(&buf);
|
||||||
|
let header = BoxHeader::read(&mut reader).unwrap();
|
||||||
|
assert_eq!(header.name, BoxType::SmhdBox);
|
||||||
|
assert_eq!(src_box.box_size(), header.size);
|
||||||
|
|
||||||
|
let dst_box = SmhdBox::read_box(&mut reader, header.size).unwrap();
|
||||||
|
assert_eq!(src_box, dst_box);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,13 +1,29 @@
|
||||||
use std::io::{BufReader, Seek, Read, BufWriter, Write};
|
use std::io::{Seek, SeekFrom, Read, Write};
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use crate::atoms::{stts::SttsBox, stsd::StsdBox};
|
use crate::atoms::*;
|
||||||
|
use crate::atoms::{
|
||||||
|
stsd::StsdBox,
|
||||||
|
stts::SttsBox,
|
||||||
|
ctts::CttsBox,
|
||||||
|
stss::StssBox,
|
||||||
|
stsc::StscBox,
|
||||||
|
stsz::StszBox,
|
||||||
|
stco::StcoBox,
|
||||||
|
co64::Co64Box,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct StblBox {
|
pub struct StblBox {
|
||||||
pub stts: Option<SttsBox>,
|
|
||||||
pub stsd: Option<StsdBox>,
|
pub stsd: Option<StsdBox>,
|
||||||
|
pub stts: Option<SttsBox>,
|
||||||
|
pub ctts: Option<CttsBox>,
|
||||||
|
pub stss: Option<StssBox>,
|
||||||
|
pub stsc: Option<StscBox>,
|
||||||
|
pub stsz: Option<StszBox>,
|
||||||
|
pub stco: Option<StcoBox>,
|
||||||
|
pub co64: Option<Co64Box>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StblBox {
|
impl StblBox {
|
||||||
|
@ -17,58 +33,128 @@ impl StblBox {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mp4Box for StblBox {
|
impl Mp4Box for StblBox {
|
||||||
fn box_type(&self) -> BoxType {
|
fn box_type() -> BoxType {
|
||||||
BoxType::StblBox
|
BoxType::StblBox
|
||||||
}
|
}
|
||||||
|
|
||||||
fn box_size(&self) -> u64 {
|
fn box_size(&self) -> u64 {
|
||||||
let mut size = HEADER_SIZE;
|
let mut size = HEADER_SIZE;
|
||||||
if let Some(stts) = &self.stts {
|
if let Some(ref stsd) = self.stsd {
|
||||||
|
size += stsd.box_size();
|
||||||
|
}
|
||||||
|
if let Some(ref stts) = self.stts {
|
||||||
size += stts.box_size();
|
size += stts.box_size();
|
||||||
}
|
}
|
||||||
if let Some(stsd) = &self.stsd {
|
if let Some(ref ctts) = self.ctts {
|
||||||
size += stsd.box_size();
|
size += ctts.box_size();
|
||||||
|
}
|
||||||
|
if let Some(ref stss) = self.stss {
|
||||||
|
size += stss.box_size();
|
||||||
|
}
|
||||||
|
if let Some(ref stsc) = self.stsc {
|
||||||
|
size += stsc.box_size();
|
||||||
|
}
|
||||||
|
if let Some(ref stsz) = self.stsz {
|
||||||
|
size += stsz.box_size();
|
||||||
|
}
|
||||||
|
if let Some(ref stco) = self.stco {
|
||||||
|
size += stco.box_size();
|
||||||
|
}
|
||||||
|
if let Some(ref co64) = self.co64 {
|
||||||
|
size += co64.box_size();
|
||||||
}
|
}
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for StblBox {
|
impl<R: Read + Seek> ReadBox<&mut R> for StblBox {
|
||||||
fn read_box(reader: &mut BufReader<R>, size: u64) -> Result<Self> {
|
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||||
|
let start = get_box_start(reader)?;
|
||||||
|
|
||||||
let mut stbl = StblBox::new();
|
let mut stbl = StblBox::new();
|
||||||
|
|
||||||
let start = 0u64;
|
let mut current = reader.seek(SeekFrom::Current(0))?;
|
||||||
while start < size {
|
let end = start + size;
|
||||||
|
while current < end {
|
||||||
// Get box header.
|
// Get box header.
|
||||||
let header = read_box_header(reader, start)?;
|
let header = BoxHeader::read(reader)?;
|
||||||
let BoxHeader{ name, size: s } = header;
|
let BoxHeader{ name, size: s } = header;
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
BoxType::SttsBox => {
|
|
||||||
let stts = SttsBox::read_box(reader, s)?;
|
|
||||||
stbl.stts = Some(stts);
|
|
||||||
}
|
|
||||||
BoxType::StsdBox => {
|
BoxType::StsdBox => {
|
||||||
let stsd = StsdBox::read_box(reader, s)?;
|
let stsd = StsdBox::read_box(reader, s)?;
|
||||||
stbl.stsd = Some(stsd);
|
stbl.stsd = Some(stsd);
|
||||||
}
|
}
|
||||||
_ => break
|
BoxType::SttsBox => {
|
||||||
|
let stts = SttsBox::read_box(reader, s)?;
|
||||||
|
stbl.stts = Some(stts);
|
||||||
|
}
|
||||||
|
BoxType::CttsBox => {
|
||||||
|
let ctts = CttsBox::read_box(reader, s)?;
|
||||||
|
stbl.ctts = Some(ctts);
|
||||||
|
}
|
||||||
|
BoxType::StssBox => {
|
||||||
|
let stss = StssBox::read_box(reader, s)?;
|
||||||
|
stbl.stss = Some(stss);
|
||||||
|
}
|
||||||
|
BoxType::StscBox => {
|
||||||
|
let stsc = StscBox::read_box(reader, s)?;
|
||||||
|
stbl.stsc = Some(stsc);
|
||||||
|
}
|
||||||
|
BoxType::StszBox => {
|
||||||
|
let stsz = StszBox::read_box(reader, s)?;
|
||||||
|
stbl.stsz = Some(stsz);
|
||||||
|
}
|
||||||
|
BoxType::StcoBox => {
|
||||||
|
let stco = StcoBox::read_box(reader, s)?;
|
||||||
|
stbl.stco = Some(stco);
|
||||||
|
}
|
||||||
|
BoxType::Co64Box => {
|
||||||
|
let co64 = Co64Box::read_box(reader, s)?;
|
||||||
|
stbl.co64 = Some(co64);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// XXX warn!()
|
||||||
|
skip_box(reader, s)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
current = reader.seek(SeekFrom::Current(0))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_read_to(reader, start + size)?;
|
||||||
|
|
||||||
Ok(stbl)
|
Ok(stbl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: Write> WriteBox<&mut BufWriter<W>> for StblBox {
|
impl<W: Write> WriteBox<&mut W> for StblBox {
|
||||||
fn write_box(&self, writer: &mut BufWriter<W>) -> Result<u64> {
|
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||||
let size = self.box_size();
|
let size = self.box_size();
|
||||||
BoxHeader::new(self.box_type(), size).write_box(writer)?;
|
BoxHeader::new(Self::box_type(), size).write(writer)?;
|
||||||
|
|
||||||
if let Some(stts) = &self.stts {
|
if let Some(ref stsd) = self.stsd {
|
||||||
|
stsd.write_box(writer)?;
|
||||||
|
}
|
||||||
|
if let Some(ref stts) = self.stts {
|
||||||
stts.write_box(writer)?;
|
stts.write_box(writer)?;
|
||||||
}
|
}
|
||||||
if let Some(stsd) = &self.stsd {
|
if let Some(ref ctts) = self.ctts {
|
||||||
stsd.write_box(writer)?;
|
ctts.write_box(writer)?;
|
||||||
|
}
|
||||||
|
if let Some(ref stss) = self.stss {
|
||||||
|
stss.write_box(writer)?;
|
||||||
|
}
|
||||||
|
if let Some(ref stsc) = self.stsc {
|
||||||
|
stsc.write_box(writer)?;
|
||||||
|
}
|
||||||
|
if let Some(ref stsz) = self.stsz {
|
||||||
|
stsz.write_box(writer)?;
|
||||||
|
}
|
||||||
|
if let Some(ref stco) = self.stco {
|
||||||
|
stco.write_box(writer)?;
|
||||||
|
}
|
||||||
|
if let Some(ref co64) = self.co64 {
|
||||||
|
co64.write_box(writer)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(size)
|
Ok(size)
|
||||||
|
|
89
src/atoms/stco.rs
Normal file
89
src/atoms/stco.rs
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
use std::io::{Seek, Read, Write};
|
||||||
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
|
use crate::*;
|
||||||
|
use crate::atoms::*;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq)]
|
||||||
|
pub struct StcoBox {
|
||||||
|
pub version: u8,
|
||||||
|
pub flags: u32,
|
||||||
|
pub entries: Vec<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mp4Box for StcoBox {
|
||||||
|
fn box_type() -> BoxType {
|
||||||
|
BoxType::StcoBox
|
||||||
|
}
|
||||||
|
|
||||||
|
fn box_size(&self) -> u64 {
|
||||||
|
HEADER_SIZE + HEADER_EXT_SIZE + 4 + (4 * self.entries.len() as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read + Seek> ReadBox<&mut R> for StcoBox {
|
||||||
|
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||||
|
let start = get_box_start(reader)?;
|
||||||
|
|
||||||
|
let (version, flags) = read_box_header_ext(reader)?;
|
||||||
|
|
||||||
|
let entry_count = reader.read_u32::<BigEndian>()?;
|
||||||
|
let mut entries = Vec::with_capacity(entry_count as usize);
|
||||||
|
for _i in 0..entry_count {
|
||||||
|
let chunk_offset = reader.read_u32::<BigEndian>()?;
|
||||||
|
entries.push(chunk_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_read_to(reader, start + size)?;
|
||||||
|
|
||||||
|
Ok(StcoBox {
|
||||||
|
version,
|
||||||
|
flags,
|
||||||
|
entries,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)?;
|
||||||
|
|
||||||
|
write_box_header_ext(writer, self.version, self.flags)?;
|
||||||
|
|
||||||
|
writer.write_u32::<BigEndian>(self.entries.len() as u32)?;
|
||||||
|
for chunk_offset in self.entries.iter() {
|
||||||
|
writer.write_u32::<BigEndian>(*chunk_offset)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::atoms::BoxHeader;
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_stco() {
|
||||||
|
let src_box = StcoBox {
|
||||||
|
version: 0,
|
||||||
|
flags: 0,
|
||||||
|
entries: vec![267, 1970, 2535, 2803, 11843, 22223, 33584],
|
||||||
|
};
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
src_box.write_box(&mut buf).unwrap();
|
||||||
|
assert_eq!(buf.len(), src_box.box_size() as usize);
|
||||||
|
|
||||||
|
let mut reader = Cursor::new(&buf);
|
||||||
|
let header = BoxHeader::read(&mut reader).unwrap();
|
||||||
|
assert_eq!(header.name, BoxType::StcoBox);
|
||||||
|
assert_eq!(src_box.box_size(), header.size);
|
||||||
|
|
||||||
|
let dst_box = StcoBox::read_box(&mut reader, header.size).unwrap();
|
||||||
|
assert_eq!(src_box, dst_box);
|
||||||
|
}
|
||||||
|
}
|
130
src/atoms/stsc.rs
Normal file
130
src/atoms/stsc.rs
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
use std::io::{Seek, Read, Write};
|
||||||
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
|
use crate::*;
|
||||||
|
use crate::atoms::*;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq)]
|
||||||
|
pub struct StscBox {
|
||||||
|
pub version: u8,
|
||||||
|
pub flags: u32,
|
||||||
|
pub entries: Vec<StscEntry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq)]
|
||||||
|
pub struct StscEntry {
|
||||||
|
pub first_chunk: u32,
|
||||||
|
pub samples_per_chunk: u32,
|
||||||
|
pub sample_description_index: u32,
|
||||||
|
pub first_sample: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mp4Box for StscBox {
|
||||||
|
fn box_type() -> BoxType {
|
||||||
|
BoxType::StscBox
|
||||||
|
}
|
||||||
|
|
||||||
|
fn box_size(&self) -> u64 {
|
||||||
|
HEADER_SIZE + HEADER_EXT_SIZE + 4 + (12 * self.entries.len() as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read + Seek> ReadBox<&mut R> for StscBox {
|
||||||
|
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||||
|
let start = get_box_start(reader)?;
|
||||||
|
|
||||||
|
let (version, flags) = read_box_header_ext(reader)?;
|
||||||
|
|
||||||
|
let entry_count = reader.read_u32::<BigEndian>()?;
|
||||||
|
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_sample: 0,
|
||||||
|
};
|
||||||
|
entries.push(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut sample_id = 1;
|
||||||
|
for i in 0..entry_count {
|
||||||
|
let (first_chunk, samples_per_chunk) = {
|
||||||
|
let mut entry = entries.get_mut(i as usize).unwrap();
|
||||||
|
entry.first_sample = sample_id;
|
||||||
|
(entry.first_chunk, entry.samples_per_chunk)
|
||||||
|
};
|
||||||
|
if i < entry_count - 1 {
|
||||||
|
let next_entry = entries.get(i as usize + 1).unwrap();
|
||||||
|
sample_id += (next_entry.first_chunk - first_chunk) * samples_per_chunk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_read_to(reader, start + size)?;
|
||||||
|
|
||||||
|
Ok(StscBox {
|
||||||
|
version,
|
||||||
|
flags,
|
||||||
|
entries,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)?;
|
||||||
|
|
||||||
|
write_box_header_ext(writer, self.version, self.flags)?;
|
||||||
|
|
||||||
|
writer.write_u32::<BigEndian>(self.entries.len() as u32)?;
|
||||||
|
for entry in self.entries.iter() {
|
||||||
|
writer.write_u32::<BigEndian>(entry.first_chunk)?;
|
||||||
|
writer.write_u32::<BigEndian>(entry.samples_per_chunk)?;
|
||||||
|
writer.write_u32::<BigEndian>(entry.sample_description_index)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::atoms::BoxHeader;
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_stsc() {
|
||||||
|
let src_box = StscBox {
|
||||||
|
version: 0,
|
||||||
|
flags: 0,
|
||||||
|
entries: vec![
|
||||||
|
StscEntry {
|
||||||
|
first_chunk: 1,
|
||||||
|
samples_per_chunk: 1,
|
||||||
|
sample_description_index: 1,
|
||||||
|
first_sample: 1,
|
||||||
|
},
|
||||||
|
StscEntry {
|
||||||
|
first_chunk: 19026,
|
||||||
|
samples_per_chunk: 14,
|
||||||
|
sample_description_index: 1,
|
||||||
|
first_sample: 19026,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
src_box.write_box(&mut buf).unwrap();
|
||||||
|
assert_eq!(buf.len(), src_box.box_size() as usize);
|
||||||
|
|
||||||
|
let mut reader = Cursor::new(&buf);
|
||||||
|
let header = BoxHeader::read(&mut reader).unwrap();
|
||||||
|
assert_eq!(header.name, BoxType::StscBox);
|
||||||
|
assert_eq!(src_box.box_size(), header.size);
|
||||||
|
|
||||||
|
let dst_box = StscBox::read_box(&mut reader, header.size).unwrap();
|
||||||
|
assert_eq!(src_box, dst_box);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,59 +1,86 @@
|
||||||
use std::io::{BufReader, SeekFrom, Seek, Read, BufWriter, Write};
|
use std::io::{Seek, Read, Write};
|
||||||
use byteorder::{BigEndian, ReadBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
use crate::atoms::*;
|
||||||
|
use crate::atoms::{avc::Avc1Box, mp4a::Mp4aBox};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct StsdBox {
|
pub struct StsdBox {
|
||||||
pub version: u8,
|
pub version: u8,
|
||||||
pub flags: u32,
|
pub flags: u32,
|
||||||
|
pub avc1: Option<Avc1Box>,
|
||||||
|
pub mp4a: Option<Mp4aBox>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mp4Box for StsdBox {
|
impl Mp4Box for StsdBox {
|
||||||
fn box_type(&self) -> BoxType {
|
fn box_type() -> BoxType {
|
||||||
BoxType::StsdBox
|
BoxType::StsdBox
|
||||||
}
|
}
|
||||||
|
|
||||||
fn box_size(&self) -> u64 {
|
fn box_size(&self) -> u64 {
|
||||||
// TODO
|
let mut size = HEADER_SIZE + HEADER_EXT_SIZE;
|
||||||
0
|
if let Some(ref avc1) = self.avc1 {
|
||||||
|
size += avc1.box_size();
|
||||||
|
} else if let Some(ref mp4a) = self.mp4a {
|
||||||
|
size += mp4a.box_size();
|
||||||
|
}
|
||||||
|
size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for StsdBox {
|
impl<R: Read + Seek> ReadBox<&mut R> for StsdBox {
|
||||||
fn read_box(reader: &mut BufReader<R>, size: u64) -> Result<Self> {
|
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||||
let current = reader.seek(SeekFrom::Current(0))?; // Current cursor position.
|
let start = get_box_start(reader)?;
|
||||||
|
|
||||||
let (version, flags) = read_box_header_ext(reader)?;
|
let (version, flags) = read_box_header_ext(reader)?;
|
||||||
|
|
||||||
let _entry_count = reader.read_u32::<BigEndian>()?;
|
reader.read_u32::<BigEndian>()?; // XXX entry_count
|
||||||
|
|
||||||
|
let mut avc1 = None;
|
||||||
|
let mut mp4a = None;
|
||||||
|
|
||||||
let mut start = 0u64;
|
|
||||||
while start < size {
|
|
||||||
// Get box header.
|
// Get box header.
|
||||||
let header = read_box_header(reader, start)?;
|
let header = BoxHeader::read(reader)?;
|
||||||
let BoxHeader{ name, size: s } = header;
|
let BoxHeader{ name, size: s } = header;
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
BoxType::Avc1Box => {}
|
BoxType::Avc1Box => {
|
||||||
BoxType::Mp4aBox => {}
|
avc1 = Some(Avc1Box::read_box(reader, s)?);
|
||||||
_ => break
|
|
||||||
}
|
}
|
||||||
start += s - HEADER_SIZE;
|
BoxType::Mp4aBox => {
|
||||||
|
mp4a = Some(Mp4aBox::read_box(reader, s)?);
|
||||||
}
|
}
|
||||||
skip_read(reader, current, size)?;
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_read_to(reader, start + size)?;
|
||||||
|
|
||||||
Ok(StsdBox {
|
Ok(StsdBox {
|
||||||
version,
|
version,
|
||||||
flags,
|
flags,
|
||||||
|
avc1,
|
||||||
|
mp4a,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: Write> WriteBox<&mut BufWriter<W>> for StsdBox {
|
impl<W: Write> WriteBox<&mut W> for StsdBox {
|
||||||
fn write_box(&self, _writer: &mut BufWriter<W>) -> Result<u64> {
|
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||||
// TODO
|
let size = self.box_size();
|
||||||
Ok(0)
|
BoxHeader::new(Self::box_type(), size).write(writer)?;
|
||||||
|
|
||||||
|
write_box_header_ext(writer, self.version, self.flags)?;
|
||||||
|
|
||||||
|
writer.write_u32::<BigEndian>(1)?; // entry_count
|
||||||
|
|
||||||
|
if let Some(ref avc1) = self.avc1 {
|
||||||
|
avc1.write_box(writer)?;
|
||||||
|
} else if let Some(ref mp4a) = self.mp4a {
|
||||||
|
mp4a.write_box(writer)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
89
src/atoms/stss.rs
Normal file
89
src/atoms/stss.rs
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
use std::io::{Seek, Read, Write};
|
||||||
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
|
use crate::*;
|
||||||
|
use crate::atoms::*;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq)]
|
||||||
|
pub struct StssBox {
|
||||||
|
pub version: u8,
|
||||||
|
pub flags: u32,
|
||||||
|
pub entries: Vec<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mp4Box for StssBox {
|
||||||
|
fn box_type() -> BoxType {
|
||||||
|
BoxType::StssBox
|
||||||
|
}
|
||||||
|
|
||||||
|
fn box_size(&self) -> u64 {
|
||||||
|
HEADER_SIZE + HEADER_EXT_SIZE + 4 + (4 * self.entries.len() as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read + Seek> ReadBox<&mut R> for StssBox {
|
||||||
|
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||||
|
let start = get_box_start(reader)?;
|
||||||
|
|
||||||
|
let (version, flags) = read_box_header_ext(reader)?;
|
||||||
|
|
||||||
|
let entry_count = reader.read_u32::<BigEndian>()?;
|
||||||
|
let mut entries = Vec::with_capacity(entry_count as usize);
|
||||||
|
for _i in 0..entry_count {
|
||||||
|
let sample_number = reader.read_u32::<BigEndian>()?;
|
||||||
|
entries.push(sample_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_read_to(reader, start + size)?;
|
||||||
|
|
||||||
|
Ok(StssBox {
|
||||||
|
version,
|
||||||
|
flags,
|
||||||
|
entries,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)?;
|
||||||
|
|
||||||
|
write_box_header_ext(writer, self.version, self.flags)?;
|
||||||
|
|
||||||
|
writer.write_u32::<BigEndian>(self.entries.len() as u32)?;
|
||||||
|
for sample_number in self.entries.iter() {
|
||||||
|
writer.write_u32::<BigEndian>(*sample_number)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::atoms::BoxHeader;
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_stss() {
|
||||||
|
let src_box = StssBox {
|
||||||
|
version: 0,
|
||||||
|
flags: 0,
|
||||||
|
entries: vec![1, 61, 121, 181, 241, 301, 361, 421, 481],
|
||||||
|
};
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
src_box.write_box(&mut buf).unwrap();
|
||||||
|
assert_eq!(buf.len(), src_box.box_size() as usize);
|
||||||
|
|
||||||
|
let mut reader = Cursor::new(&buf);
|
||||||
|
let header = BoxHeader::read(&mut reader).unwrap();
|
||||||
|
assert_eq!(header.name, BoxType::StssBox);
|
||||||
|
assert_eq!(src_box.box_size(), header.size);
|
||||||
|
|
||||||
|
let dst_box = StssBox::read_box(&mut reader, header.size).unwrap();
|
||||||
|
assert_eq!(src_box, dst_box);
|
||||||
|
}
|
||||||
|
}
|
119
src/atoms/stsz.rs
Normal file
119
src/atoms/stsz.rs
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
use std::io::{Seek, Read, Write};
|
||||||
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
|
use crate::*;
|
||||||
|
use crate::atoms::*;
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Default, PartialEq)]
|
||||||
|
pub struct StszBox {
|
||||||
|
pub version: u8,
|
||||||
|
pub flags: u32,
|
||||||
|
pub sample_size: u32,
|
||||||
|
pub sample_sizes: Vec<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mp4Box for StszBox {
|
||||||
|
fn box_type() -> BoxType {
|
||||||
|
BoxType::StszBox
|
||||||
|
}
|
||||||
|
|
||||||
|
fn box_size(&self) -> u64 {
|
||||||
|
HEADER_SIZE + HEADER_EXT_SIZE + 8 + (4 * self.sample_sizes.len() as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read + Seek> ReadBox<&mut R> for StszBox {
|
||||||
|
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||||
|
let start = get_box_start(reader)?;
|
||||||
|
|
||||||
|
let (version, flags) = read_box_header_ext(reader)?;
|
||||||
|
|
||||||
|
let sample_size = reader.read_u32::<BigEndian>()?;
|
||||||
|
let sample_count = reader.read_u32::<BigEndian>()?;
|
||||||
|
let mut sample_sizes = Vec::with_capacity(sample_count as usize);
|
||||||
|
if sample_size == 0 {
|
||||||
|
for _i in 0..sample_count {
|
||||||
|
let sample_number = reader.read_u32::<BigEndian>()?;
|
||||||
|
sample_sizes.push(sample_number);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_read_to(reader, start + size)?;
|
||||||
|
|
||||||
|
Ok(StszBox {
|
||||||
|
version,
|
||||||
|
flags,
|
||||||
|
sample_size,
|
||||||
|
sample_sizes,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)?;
|
||||||
|
|
||||||
|
write_box_header_ext(writer, self.version, self.flags)?;
|
||||||
|
|
||||||
|
writer.write_u32::<BigEndian>(self.sample_size)?;
|
||||||
|
writer.write_u32::<BigEndian>(self.sample_sizes.len() as u32)?;
|
||||||
|
if self.sample_size == 0 {
|
||||||
|
for sample_number in self.sample_sizes.iter() {
|
||||||
|
writer.write_u32::<BigEndian>(*sample_number)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::atoms::BoxHeader;
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_stsz_same_size() {
|
||||||
|
let src_box = StszBox {
|
||||||
|
version: 0,
|
||||||
|
flags: 0,
|
||||||
|
sample_size: 1165,
|
||||||
|
sample_sizes: vec![],
|
||||||
|
};
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
src_box.write_box(&mut buf).unwrap();
|
||||||
|
assert_eq!(buf.len(), src_box.box_size() as usize);
|
||||||
|
|
||||||
|
let mut reader = Cursor::new(&buf);
|
||||||
|
let header = BoxHeader::read(&mut reader).unwrap();
|
||||||
|
assert_eq!(header.name, BoxType::StszBox);
|
||||||
|
assert_eq!(src_box.box_size(), header.size);
|
||||||
|
|
||||||
|
let dst_box = StszBox::read_box(&mut reader, header.size).unwrap();
|
||||||
|
assert_eq!(src_box, dst_box);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_stsz_many_sizes() {
|
||||||
|
let src_box = StszBox {
|
||||||
|
version: 0,
|
||||||
|
flags: 0,
|
||||||
|
sample_size: 0,
|
||||||
|
sample_sizes: vec![1165, 11, 11, 8545, 10126, 10866, 9643, 9351, 7730],
|
||||||
|
};
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
src_box.write_box(&mut buf).unwrap();
|
||||||
|
assert_eq!(buf.len(), src_box.box_size() as usize);
|
||||||
|
|
||||||
|
let mut reader = Cursor::new(&buf);
|
||||||
|
let header = BoxHeader::read(&mut reader).unwrap();
|
||||||
|
assert_eq!(header.name, BoxType::StszBox);
|
||||||
|
assert_eq!(src_box.box_size(), header.size);
|
||||||
|
|
||||||
|
let dst_box = StszBox::read_box(&mut reader, header.size).unwrap();
|
||||||
|
assert_eq!(src_box, dst_box);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,8 @@
|
||||||
use std::io::{BufReader, SeekFrom, Seek, Read, BufWriter, Write};
|
use std::io::{Seek, Read, Write};
|
||||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
use crate::atoms::*;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq)]
|
#[derive(Debug, Default, PartialEq)]
|
||||||
|
@ -18,7 +19,7 @@ pub struct SttsEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mp4Box for SttsBox {
|
impl Mp4Box for SttsBox {
|
||||||
fn box_type(&self) -> BoxType {
|
fn box_type() -> BoxType {
|
||||||
BoxType::SttsBox
|
BoxType::SttsBox
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,9 +28,9 @@ impl Mp4Box for SttsBox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for SttsBox {
|
impl<R: Read + Seek> ReadBox<&mut R> for SttsBox {
|
||||||
fn read_box(reader: &mut BufReader<R>, size: u64) -> Result<Self> {
|
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||||
let current = reader.seek(SeekFrom::Current(0))?; // Current cursor position.
|
let start = get_box_start(reader)?;
|
||||||
|
|
||||||
let (version, flags) = read_box_header_ext(reader)?;
|
let (version, flags) = read_box_header_ext(reader)?;
|
||||||
|
|
||||||
|
@ -42,7 +43,8 @@ impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for SttsBox {
|
||||||
};
|
};
|
||||||
entries.push(entry);
|
entries.push(entry);
|
||||||
}
|
}
|
||||||
skip_read(reader, current, size)?;
|
|
||||||
|
skip_read_to(reader, start + size)?;
|
||||||
|
|
||||||
Ok(SttsBox {
|
Ok(SttsBox {
|
||||||
version,
|
version,
|
||||||
|
@ -52,10 +54,10 @@ impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for SttsBox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: Write> WriteBox<&mut BufWriter<W>> for SttsBox {
|
impl<W: Write> WriteBox<&mut W> for SttsBox {
|
||||||
fn write_box(&self, writer: &mut BufWriter<W>) -> Result<u64> {
|
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||||
let size = self.box_size();
|
let size = self.box_size();
|
||||||
BoxHeader::new(self.box_type(), size).write_box(writer)?;
|
BoxHeader::new(Self::box_type(), size).write(writer)?;
|
||||||
|
|
||||||
write_box_header_ext(writer, self.version, self.flags)?;
|
write_box_header_ext(writer, self.version, self.flags)?;
|
||||||
|
|
||||||
|
@ -72,11 +74,11 @@ impl<W: Write> WriteBox<&mut BufWriter<W>> for SttsBox {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::read_box_header;
|
use crate::atoms::BoxHeader;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_stts32() {
|
fn test_stts() {
|
||||||
let src_box = SttsBox {
|
let src_box = SttsBox {
|
||||||
version: 0,
|
version: 0,
|
||||||
flags: 0,
|
flags: 0,
|
||||||
|
@ -86,50 +88,15 @@ mod tests {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
{
|
src_box.write_box(&mut buf).unwrap();
|
||||||
let mut writer = BufWriter::new(&mut buf);
|
|
||||||
src_box.write_box(&mut writer).unwrap();
|
|
||||||
}
|
|
||||||
assert_eq!(buf.len(), src_box.box_size() as usize);
|
assert_eq!(buf.len(), src_box.box_size() as usize);
|
||||||
|
|
||||||
{
|
let mut reader = Cursor::new(&buf);
|
||||||
let mut reader = BufReader::new(Cursor::new(&buf));
|
let header = BoxHeader::read(&mut reader).unwrap();
|
||||||
let header = read_box_header(&mut reader, 0).unwrap();
|
|
||||||
assert_eq!(header.name, BoxType::SttsBox);
|
assert_eq!(header.name, BoxType::SttsBox);
|
||||||
assert_eq!(src_box.box_size(), header.size);
|
assert_eq!(src_box.box_size(), header.size);
|
||||||
|
|
||||||
let dst_box = SttsBox::read_box(&mut reader, header.size).unwrap();
|
let dst_box = SttsBox::read_box(&mut reader, header.size).unwrap();
|
||||||
|
|
||||||
assert_eq!(src_box, dst_box);
|
assert_eq!(src_box, dst_box);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_stts64() {
|
|
||||||
let src_box = SttsBox {
|
|
||||||
version: 1,
|
|
||||||
flags: 0,
|
|
||||||
entries: vec![
|
|
||||||
SttsEntry {sample_count: 29726, sample_delta: 1024},
|
|
||||||
SttsEntry {sample_count: 1, sample_delta: 512},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
let mut buf = Vec::new();
|
|
||||||
{
|
|
||||||
let mut writer = BufWriter::new(&mut buf);
|
|
||||||
src_box.write_box(&mut writer).unwrap();
|
|
||||||
}
|
|
||||||
assert_eq!(buf.len(), src_box.box_size() as usize);
|
|
||||||
|
|
||||||
{
|
|
||||||
let mut reader = BufReader::new(Cursor::new(&buf));
|
|
||||||
let header = read_box_header(&mut reader, 0).unwrap();
|
|
||||||
assert_eq!(header.name, BoxType::SttsBox);
|
|
||||||
assert_eq!(src_box.box_size(), header.size);
|
|
||||||
|
|
||||||
let dst_box = SttsBox::read_box(&mut reader, header.size).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(src_box, dst_box);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use std::io::{BufReader, Seek, SeekFrom, Read, BufWriter, Write};
|
use std::io::{Seek, Read, Write};
|
||||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use num_rational::Ratio;
|
use num_rational::Ratio;
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
use crate::atoms::*;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
|
@ -54,7 +55,7 @@ pub struct Matrix {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mp4Box for TkhdBox {
|
impl Mp4Box for TkhdBox {
|
||||||
fn box_type(&self) -> BoxType {
|
fn box_type() -> BoxType {
|
||||||
BoxType::TkhdBox
|
BoxType::TkhdBox
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,9 +72,9 @@ impl Mp4Box for TkhdBox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for TkhdBox {
|
impl<R: Read + Seek> ReadBox<&mut R> for TkhdBox {
|
||||||
fn read_box(reader: &mut BufReader<R>, size: u64) -> Result<Self> {
|
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||||
let current = reader.seek(SeekFrom::Current(0))?; // Current cursor position.
|
let start = get_box_start(reader)?;
|
||||||
|
|
||||||
let (version, flags) = read_box_header_ext(reader)?;
|
let (version, flags) = read_box_header_ext(reader)?;
|
||||||
|
|
||||||
|
@ -118,7 +119,7 @@ impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for TkhdBox {
|
||||||
let width = reader.read_u32::<BigEndian>()? >> 16;
|
let width = reader.read_u32::<BigEndian>()? >> 16;
|
||||||
let height = reader.read_u32::<BigEndian>()? >> 16;
|
let height = reader.read_u32::<BigEndian>()? >> 16;
|
||||||
|
|
||||||
skip_read(reader, current, size)?;
|
skip_read_to(reader, start + size)?;
|
||||||
|
|
||||||
Ok(TkhdBox {
|
Ok(TkhdBox {
|
||||||
version,
|
version,
|
||||||
|
@ -137,10 +138,10 @@ impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for TkhdBox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: Write> WriteBox<&mut BufWriter<W>> for TkhdBox {
|
impl<W: Write> WriteBox<&mut W> for TkhdBox {
|
||||||
fn write_box(&self, writer: &mut BufWriter<W>) -> Result<u64> {
|
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||||
let size = self.box_size();
|
let size = self.box_size();
|
||||||
BoxHeader::new(self.box_type(), size).write_box(writer)?;
|
BoxHeader::new(Self::box_type(), size).write(writer)?;
|
||||||
|
|
||||||
write_box_header_ext(writer, self.version, self.flags)?;
|
write_box_header_ext(writer, self.version, self.flags)?;
|
||||||
|
|
||||||
|
@ -186,7 +187,7 @@ impl<W: Write> WriteBox<&mut BufWriter<W>> for TkhdBox {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::read_box_header;
|
use crate::atoms::BoxHeader;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -216,23 +217,17 @@ mod tests {
|
||||||
height: 288,
|
height: 288,
|
||||||
};
|
};
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
{
|
src_box.write_box(&mut buf).unwrap();
|
||||||
let mut writer = BufWriter::new(&mut buf);
|
|
||||||
src_box.write_box(&mut writer).unwrap();
|
|
||||||
}
|
|
||||||
assert_eq!(buf.len(), src_box.box_size() as usize);
|
assert_eq!(buf.len(), src_box.box_size() as usize);
|
||||||
|
|
||||||
{
|
let mut reader = Cursor::new(&buf);
|
||||||
let mut reader = BufReader::new(Cursor::new(&buf));
|
let header = BoxHeader::read(&mut reader).unwrap();
|
||||||
let header = read_box_header(&mut reader, 0).unwrap();
|
|
||||||
assert_eq!(header.name, BoxType::TkhdBox);
|
assert_eq!(header.name, BoxType::TkhdBox);
|
||||||
assert_eq!(src_box.box_size(), header.size);
|
assert_eq!(src_box.box_size(), header.size);
|
||||||
|
|
||||||
let dst_box = TkhdBox::read_box(&mut reader, header.size).unwrap();
|
let dst_box = TkhdBox::read_box(&mut reader, header.size).unwrap();
|
||||||
|
|
||||||
assert_eq!(src_box, dst_box);
|
assert_eq!(src_box, dst_box);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tkhd64() {
|
fn test_tkhd64() {
|
||||||
|
@ -261,21 +256,15 @@ mod tests {
|
||||||
height: 288,
|
height: 288,
|
||||||
};
|
};
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
{
|
src_box.write_box(&mut buf).unwrap();
|
||||||
let mut writer = BufWriter::new(&mut buf);
|
|
||||||
src_box.write_box(&mut writer).unwrap();
|
|
||||||
}
|
|
||||||
assert_eq!(buf.len(), src_box.box_size() as usize);
|
assert_eq!(buf.len(), src_box.box_size() as usize);
|
||||||
|
|
||||||
{
|
let mut reader = Cursor::new(&buf);
|
||||||
let mut reader = BufReader::new(Cursor::new(&buf));
|
let header = BoxHeader::read(&mut reader).unwrap();
|
||||||
let header = read_box_header(&mut reader, 0).unwrap();
|
|
||||||
assert_eq!(header.name, BoxType::TkhdBox);
|
assert_eq!(header.name, BoxType::TkhdBox);
|
||||||
assert_eq!(src_box.box_size(), header.size);
|
assert_eq!(src_box.box_size(), header.size);
|
||||||
|
|
||||||
let dst_box = TkhdBox::read_box(&mut reader, header.size).unwrap();
|
let dst_box = TkhdBox::read_box(&mut reader, header.size).unwrap();
|
||||||
|
|
||||||
assert_eq!(src_box, dst_box);
|
assert_eq!(src_box, dst_box);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,22 @@
|
||||||
use std::io::{BufReader, SeekFrom, Seek, Read, BufWriter, Write};
|
use std::io::{Seek, SeekFrom, Read, Write};
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use crate::atoms::{tkhd::TkhdBox, edts::EdtsBox, mdia::MdiaBox};
|
use crate::atoms::*;
|
||||||
|
use crate::atoms::{
|
||||||
|
tkhd::TkhdBox,
|
||||||
|
edts::EdtsBox,
|
||||||
|
mdia::MdiaBox,
|
||||||
|
stbl::StblBox,
|
||||||
|
stts::SttsBox,
|
||||||
|
stsc::StscBox,
|
||||||
|
stsz::StszBox,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct TrakBox {
|
pub struct TrakBox {
|
||||||
|
pub id: u32,
|
||||||
|
|
||||||
pub tkhd: Option<TkhdBox>,
|
pub tkhd: Option<TkhdBox>,
|
||||||
pub edts: Option<EdtsBox>,
|
pub edts: Option<EdtsBox>,
|
||||||
pub mdia: Option<MdiaBox>,
|
pub mdia: Option<MdiaBox>,
|
||||||
|
@ -15,37 +26,260 @@ impl TrakBox {
|
||||||
pub(crate) fn new() -> TrakBox {
|
pub(crate) fn new() -> TrakBox {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn stbl(&self) -> Result<&StblBox> {
|
||||||
|
if let Some(ref mdia) = self.mdia {
|
||||||
|
if let Some(ref minf) = mdia.minf {
|
||||||
|
if let Some(ref stbl) = minf.stbl {
|
||||||
|
Ok(stbl)
|
||||||
|
} else {
|
||||||
|
Err(Error::BoxInTrakNotFound(self.id, BoxType::StblBox))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(Error::BoxInTrakNotFound(self.id, BoxType::MinfBox))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(Error::BoxInTrakNotFound(self.id, BoxType::MdiaBox))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stts(&self) -> Result<&SttsBox> {
|
||||||
|
let stbl = self.stbl()?;
|
||||||
|
|
||||||
|
if let Some(ref stts) = stbl.stts {
|
||||||
|
Ok(stts)
|
||||||
|
} else {
|
||||||
|
Err(Error::BoxInStblNotFound(self.id, BoxType::SttsBox))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stsc(&self) -> Result<&StscBox> {
|
||||||
|
let stbl = self.stbl()?;
|
||||||
|
|
||||||
|
if let Some(ref stsc) = stbl.stsc {
|
||||||
|
Ok(stsc)
|
||||||
|
} else {
|
||||||
|
Err(Error::BoxInStblNotFound(self.id, BoxType::StscBox))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stsz(&self) -> Result<&StszBox> {
|
||||||
|
let stbl = self.stbl()?;
|
||||||
|
|
||||||
|
if let Some(ref stsz) = stbl.stsz {
|
||||||
|
Ok(stsz)
|
||||||
|
} else {
|
||||||
|
Err(Error::BoxInStblNotFound(self.id, BoxType::StszBox))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stsc_index(&self, sample_id: u32) -> Result<usize> {
|
||||||
|
let stsc = self.stsc()?;
|
||||||
|
|
||||||
|
for (i, entry) in stsc.entries.iter().enumerate() {
|
||||||
|
if sample_id < entry.first_sample {
|
||||||
|
assert_ne!(i, 0);
|
||||||
|
return Ok(i - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_ne!(stsc.entries.len(), 0);
|
||||||
|
Ok(stsc.entries.len() - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn chunk_offset(&self, chunk_id: u32) -> Result<u64> {
|
||||||
|
let stbl = self.stbl()?;
|
||||||
|
|
||||||
|
if let Some(ref stco) = stbl.stco {
|
||||||
|
if let Some(offset) = stco.entries.get(chunk_id as usize - 1) {
|
||||||
|
return Ok(*offset as u64);
|
||||||
|
} else {
|
||||||
|
return Err(Error::EntryInStblNotFound(self.id, BoxType::StcoBox,
|
||||||
|
chunk_id));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let Some(ref co64) = stbl.co64 {
|
||||||
|
if let Some(offset) = co64.entries.get(chunk_id as usize - 1) {
|
||||||
|
return Ok(*offset);
|
||||||
|
} else {
|
||||||
|
return Err(Error::EntryInStblNotFound(self.id, BoxType::Co64Box,
|
||||||
|
chunk_id));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// XXX BoxType::StcoBox & BoxType::Co64Box
|
||||||
|
Err(Error::BoxInStblNotFound(self.id, BoxType::Co64Box))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ctts_index(&self, sample_id: u32) -> Result<(usize, u32)> {
|
||||||
|
let stbl = self.stbl()?;
|
||||||
|
|
||||||
|
let ctts = if let Some(ref ctts) = stbl.ctts {
|
||||||
|
ctts
|
||||||
|
} else {
|
||||||
|
return Err(Error::BoxInStblNotFound(self.id, BoxType::CttsBox));
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut sample_count = 1;
|
||||||
|
for (i, entry) in ctts.entries.iter().enumerate() {
|
||||||
|
if sample_id <= sample_count + entry.sample_count -1 {
|
||||||
|
return Ok((i, sample_count))
|
||||||
|
}
|
||||||
|
sample_count += entry.sample_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(Error::EntryInStblNotFound(self.id, BoxType::CttsBox, sample_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sample_count(&self) -> Result<u32> {
|
||||||
|
let stsz = self.stsz()?;
|
||||||
|
Ok(stsz.sample_sizes.len() as u32)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sample_size(&self, sample_id: u32) -> Result<u32> {
|
||||||
|
let stsz = self.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 {
|
||||||
|
return Err(Error::EntryInStblNotFound(self.id, BoxType::StszBox, sample_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sample_offset(&self, sample_id: u32) -> Result<u64> {
|
||||||
|
let stsc_index = self.stsc_index(sample_id)?;
|
||||||
|
|
||||||
|
let stsc = self.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 = first_chunk + (sample_id - first_sample) / samples_per_chunk;
|
||||||
|
|
||||||
|
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 fn sample_time(&self, sample_id: u32) -> Result<(u64, u32)> {
|
||||||
|
let stts = self.stts()?;
|
||||||
|
|
||||||
|
let mut sample_count = 1;
|
||||||
|
let mut elapsed = 0;
|
||||||
|
|
||||||
|
for entry in stts.entries.iter() {
|
||||||
|
if sample_id <= sample_count + entry.sample_count - 1 {
|
||||||
|
let start_time = (sample_id - sample_count) as u64
|
||||||
|
* entry.sample_delta as u64 + elapsed;
|
||||||
|
return Ok((start_time, entry.sample_delta));
|
||||||
|
}
|
||||||
|
|
||||||
|
sample_count += entry.sample_count;
|
||||||
|
elapsed += entry.sample_count as u64 * entry.sample_delta as u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(Error::EntryInStblNotFound(self.id, BoxType::SttsBox, sample_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sample_rendering_offset(&self, sample_id: u32) -> Result<i32> {
|
||||||
|
let stbl = self.stbl()?;
|
||||||
|
|
||||||
|
if let Some(ref ctts) = stbl.ctts {
|
||||||
|
let (ctts_index, _) = self.ctts_index(sample_id)?;
|
||||||
|
let ctts_entry = ctts.entries.get(ctts_index).unwrap();
|
||||||
|
Ok(ctts_entry.sample_offset)
|
||||||
|
} else {
|
||||||
|
Ok(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_sync_sample(&self, sample_id: u32) -> Result<bool> {
|
||||||
|
let stbl = self.stbl()?;
|
||||||
|
|
||||||
|
if let Some(ref stss) = stbl.stss {
|
||||||
|
match stss.entries.binary_search(&sample_id) {
|
||||||
|
Ok(_) => Ok(true),
|
||||||
|
Err(_) => Ok(false)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_sample<R: Read + Seek>(
|
||||||
|
&self,
|
||||||
|
reader: &mut R,
|
||||||
|
sample_id: u32,
|
||||||
|
) -> Result<Option<Mp4Sample>> {
|
||||||
|
let sample_offset = match self.sample_offset(sample_id) {
|
||||||
|
Ok(offset) => offset,
|
||||||
|
Err(Error::EntryInStblNotFound(_,_,_)) => return Ok(None),
|
||||||
|
Err(err) => return Err(err)
|
||||||
|
};
|
||||||
|
let sample_size = self.sample_size(sample_id)?;
|
||||||
|
|
||||||
|
let mut buffer = vec![0x0u8; sample_size as usize];
|
||||||
|
reader.seek(SeekFrom::Start(sample_offset))?;
|
||||||
|
reader.read_exact(&mut buffer)?;
|
||||||
|
|
||||||
|
let (start_time, duration) = self.sample_time(sample_id)?;
|
||||||
|
let rendering_offset = self.sample_rendering_offset(sample_id)?;
|
||||||
|
let is_sync = self.is_sync_sample(sample_id)?;
|
||||||
|
|
||||||
|
Ok(Some(Mp4Sample {
|
||||||
|
start_time,
|
||||||
|
duration,
|
||||||
|
rendering_offset,
|
||||||
|
is_sync,
|
||||||
|
bytes: Bytes::from(buffer),
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mp4Box for TrakBox {
|
impl Mp4Box for TrakBox {
|
||||||
fn box_type(&self) -> BoxType {
|
fn box_type() -> BoxType {
|
||||||
BoxType::TrakBox
|
BoxType::TrakBox
|
||||||
}
|
}
|
||||||
|
|
||||||
fn box_size(&self) -> u64 {
|
fn box_size(&self) -> u64 {
|
||||||
let mut size = HEADER_SIZE;
|
let mut size = HEADER_SIZE;
|
||||||
if let Some(tkhd) = &self.tkhd {
|
if let Some(ref tkhd) = self.tkhd {
|
||||||
size += tkhd.box_size();
|
size += tkhd.box_size();
|
||||||
}
|
}
|
||||||
if let Some(edts) = &self.edts {
|
if let Some(ref edts) = self.edts {
|
||||||
size += edts.box_size();
|
size += edts.box_size();
|
||||||
}
|
}
|
||||||
if let Some(mdia) = &self.mdia {
|
if let Some(ref mdia) = self.mdia {
|
||||||
size += mdia.box_size();
|
size += mdia.box_size();
|
||||||
}
|
}
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for TrakBox {
|
impl<R: Read + Seek> ReadBox<&mut R> for TrakBox {
|
||||||
fn read_box(reader: &mut BufReader<R>, size: u64) -> Result<Self> {
|
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||||
let current = reader.seek(SeekFrom::Current(0))?; // Current cursor position.
|
let start = get_box_start(reader)?;
|
||||||
|
|
||||||
let mut trak = TrakBox::new();
|
let mut trak = TrakBox::new();
|
||||||
|
|
||||||
let start = 0u64;
|
let mut current = reader.seek(SeekFrom::Current(0))?;
|
||||||
while start < size {
|
let end = start + size;
|
||||||
|
while current < end {
|
||||||
// Get box header.
|
// Get box header.
|
||||||
let header = read_box_header(reader, start)?;
|
let header = BoxHeader::read(reader)?;
|
||||||
let BoxHeader{ name, size: s } = header;
|
let BoxHeader{ name, size: s } = header;
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
|
@ -61,27 +295,33 @@ impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for TrakBox {
|
||||||
let mdia = MdiaBox::read_box(reader, s)?;
|
let mdia = MdiaBox::read_box(reader, s)?;
|
||||||
trak.mdia = Some(mdia);
|
trak.mdia = Some(mdia);
|
||||||
}
|
}
|
||||||
_ => break
|
_ => {
|
||||||
|
// XXX warn!()
|
||||||
|
skip_box(reader, s)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
skip_read(reader, current, size)?;
|
|
||||||
|
current = reader.seek(SeekFrom::Current(0))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_read_to(reader, start + size)?;
|
||||||
|
|
||||||
Ok(trak)
|
Ok(trak)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: Write> WriteBox<&mut BufWriter<W>> for TrakBox {
|
impl<W: Write> WriteBox<&mut W> for TrakBox {
|
||||||
fn write_box(&self, writer: &mut BufWriter<W>) -> Result<u64> {
|
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||||
let size = self.box_size();
|
let size = self.box_size();
|
||||||
BoxHeader::new(self.box_type(), size).write_box(writer)?;
|
BoxHeader::new(Self::box_type(), size).write(writer)?;
|
||||||
|
|
||||||
if let Some(tkhd) = &self.tkhd {
|
if let Some(ref tkhd) = self.tkhd {
|
||||||
tkhd.write_box(writer)?;
|
tkhd.write_box(writer)?;
|
||||||
}
|
}
|
||||||
if let Some(edts) = &self.edts {
|
if let Some(ref edts) = self.edts {
|
||||||
edts.write_box(writer)?;
|
edts.write_box(writer)?;
|
||||||
}
|
}
|
||||||
if let Some(mdia) = &self.mdia {
|
if let Some(ref mdia) = self.mdia {
|
||||||
mdia.write_box(writer)?;
|
mdia.write_box(writer)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use std::io::{BufReader, SeekFrom, Seek, Read, BufWriter, Write};
|
use std::io::{Seek, Read, Write};
|
||||||
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
use crate::atoms::*;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq)]
|
#[derive(Debug, Default, PartialEq)]
|
||||||
|
@ -20,7 +21,7 @@ pub struct RgbColor {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mp4Box for VmhdBox {
|
impl Mp4Box for VmhdBox {
|
||||||
fn box_type(&self) -> BoxType {
|
fn box_type() -> BoxType {
|
||||||
BoxType::VmhdBox
|
BoxType::VmhdBox
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,9 +30,9 @@ impl Mp4Box for VmhdBox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for VmhdBox {
|
impl<R: Read + Seek> ReadBox<&mut R> for VmhdBox {
|
||||||
fn read_box(reader: &mut BufReader<R>, size: u64) -> Result<Self> {
|
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||||
let current = reader.seek(SeekFrom::Current(0))?; // Current cursor position.
|
let start = get_box_start(reader)?;
|
||||||
|
|
||||||
let (version, flags) = read_box_header_ext(reader)?;
|
let (version, flags) = read_box_header_ext(reader)?;
|
||||||
|
|
||||||
|
@ -41,7 +42,8 @@ impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for VmhdBox {
|
||||||
green: reader.read_u16::<BigEndian>()?,
|
green: reader.read_u16::<BigEndian>()?,
|
||||||
blue: reader.read_u16::<BigEndian>()?,
|
blue: reader.read_u16::<BigEndian>()?,
|
||||||
};
|
};
|
||||||
skip_read(reader, current, size)?;
|
|
||||||
|
skip_read_to(reader, start + size)?;
|
||||||
|
|
||||||
Ok(VmhdBox {
|
Ok(VmhdBox {
|
||||||
version,
|
version,
|
||||||
|
@ -52,10 +54,10 @@ impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for VmhdBox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: Write> WriteBox<&mut BufWriter<W>> for VmhdBox {
|
impl<W: Write> WriteBox<&mut W> for VmhdBox {
|
||||||
fn write_box(&self, writer: &mut BufWriter<W>) -> Result<u64> {
|
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||||
let size = self.box_size();
|
let size = self.box_size();
|
||||||
BoxHeader::new(self.box_type(), size).write_box(writer)?;
|
BoxHeader::new(Self::box_type(), size).write(writer)?;
|
||||||
|
|
||||||
write_box_header_ext(writer, self.version, self.flags)?;
|
write_box_header_ext(writer, self.version, self.flags)?;
|
||||||
|
|
||||||
|
@ -72,7 +74,7 @@ impl<W: Write> WriteBox<&mut BufWriter<W>> for VmhdBox {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::read_box_header;
|
use crate::atoms::BoxHeader;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -84,21 +86,15 @@ mod tests {
|
||||||
op_color: RgbColor { red: 0, green: 0, blue: 0},
|
op_color: RgbColor { red: 0, green: 0, blue: 0},
|
||||||
};
|
};
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
{
|
src_box.write_box(&mut buf).unwrap();
|
||||||
let mut writer = BufWriter::new(&mut buf);
|
|
||||||
src_box.write_box(&mut writer).unwrap();
|
|
||||||
}
|
|
||||||
assert_eq!(buf.len(), src_box.box_size() as usize);
|
assert_eq!(buf.len(), src_box.box_size() as usize);
|
||||||
|
|
||||||
{
|
let mut reader = Cursor::new(&buf);
|
||||||
let mut reader = BufReader::new(Cursor::new(&buf));
|
let header = BoxHeader::read(&mut reader).unwrap();
|
||||||
let header = read_box_header(&mut reader, 0).unwrap();
|
|
||||||
assert_eq!(header.name, BoxType::VmhdBox);
|
assert_eq!(header.name, BoxType::VmhdBox);
|
||||||
assert_eq!(src_box.box_size(), header.size);
|
assert_eq!(src_box.box_size(), header.size);
|
||||||
|
|
||||||
let dst_box = VmhdBox::read_box(&mut reader, header.size).unwrap();
|
let dst_box = VmhdBox::read_box(&mut reader, header.size).unwrap();
|
||||||
|
|
||||||
assert_eq!(src_box, dst_box);
|
assert_eq!(src_box, dst_box);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
12
src/error.rs
12
src/error.rs
|
@ -1,9 +1,21 @@
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::atoms::BoxType;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
IoError(#[from] std::io::Error),
|
IoError(#[from] std::io::Error),
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
InvalidData(&'static str),
|
InvalidData(&'static str),
|
||||||
|
#[error("{0} not found")]
|
||||||
|
BoxNotFound(BoxType),
|
||||||
|
#[error("trak[{0}] not found")]
|
||||||
|
TrakNotFound(u32),
|
||||||
|
#[error("trak[{0}].{1} not found")]
|
||||||
|
BoxInTrakNotFound(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),
|
||||||
}
|
}
|
||||||
|
|
196
src/lib.rs
196
src/lib.rs
|
@ -1,7 +1,9 @@
|
||||||
use std::io::{BufReader, Read, SeekFrom, Seek};
|
use std::fmt;
|
||||||
use std::fs::File;
|
use std::io::{Seek, SeekFrom, Read};
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
pub use bytes::Bytes;
|
||||||
|
|
||||||
mod atoms;
|
mod atoms;
|
||||||
use crate::atoms::*;
|
use crate::atoms::*;
|
||||||
|
|
||||||
|
@ -10,9 +12,6 @@ pub use error::Error;
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
// XXX if box has largesize
|
|
||||||
const HEADER_SIZE: u64 = 8;
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum TrackType {
|
pub enum TrackType {
|
||||||
Audio,
|
Audio,
|
||||||
|
@ -21,118 +20,143 @@ pub enum TrackType {
|
||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug)]
|
||||||
pub struct BMFF {
|
pub struct Mp4Sample {
|
||||||
pub ftyp: FtypBox,
|
pub start_time: u64,
|
||||||
pub moov: Option<MoovBox>,
|
pub duration: u32,
|
||||||
pub size: u64,
|
pub rendering_offset: i32,
|
||||||
|
pub is_sync: bool,
|
||||||
|
pub bytes: Bytes,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BMFF {
|
impl PartialEq for Mp4Sample {
|
||||||
fn new() -> BMFF {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
Default::default()
|
self.start_time == other.start_time
|
||||||
|
&& self.duration == other.duration
|
||||||
|
&& self.rendering_offset == other.rendering_offset
|
||||||
|
&& self.is_sync == other.is_sync
|
||||||
|
&& self.bytes.len() == other.bytes.len() // XXX for easy check
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
impl fmt::Display for Mp4Sample {
|
||||||
struct BoxHeader {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
name: BoxType,
|
write!(f,
|
||||||
|
"start_time {}, duration {}, rendering_offset {}, is_sync {}, length {}",
|
||||||
|
self.start_time, self.duration, self.rendering_offset, self.is_sync,
|
||||||
|
self.bytes.len())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Mp4Reader<R> {
|
||||||
|
reader: R,
|
||||||
|
pub ftyp: FtypBox,
|
||||||
|
pub moov: Option<MoovBox>,
|
||||||
size: u64,
|
size: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BoxHeader {
|
impl<R: Read + Seek> Mp4Reader<R> {
|
||||||
fn new(name: BoxType, size: u64) -> Self {
|
pub fn new(reader: R) -> Self {
|
||||||
Self { name, size }
|
Mp4Reader {
|
||||||
|
reader,
|
||||||
|
ftyp: FtypBox::default(),
|
||||||
|
moov: None,
|
||||||
|
size: 0,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_mp4(f: File) -> Result<BMFF> {
|
pub fn size(&self) -> u64 {
|
||||||
|
self.size
|
||||||
// Open file and read boxes.
|
}
|
||||||
let bmff = read_boxes(f)?;
|
|
||||||
|
|
||||||
Ok(bmff)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_boxes(f: File) -> Result<BMFF> {
|
|
||||||
let filesize = f.metadata()?.len();
|
|
||||||
let mut reader = BufReader::new(f);
|
|
||||||
let mut bmff = BMFF::new();
|
|
||||||
bmff.size = filesize;
|
|
||||||
|
|
||||||
let mut start = 0u64;
|
|
||||||
while start < filesize {
|
|
||||||
|
|
||||||
|
pub fn read(&mut self, size: u64) -> Result<()> {
|
||||||
|
let start = self.reader.seek(SeekFrom::Current(0))?;
|
||||||
|
let mut current = start;
|
||||||
|
while current < size {
|
||||||
// Get box header.
|
// Get box header.
|
||||||
let header = read_box_header(&mut reader, start)?;
|
let header = BoxHeader::read(&mut self.reader)?;
|
||||||
let BoxHeader{ name, size } = header;
|
let BoxHeader{ name, size: s } = header;
|
||||||
|
|
||||||
// Match and parse the atom boxes.
|
// Match and parse the atom boxes.
|
||||||
match name {
|
match name {
|
||||||
BoxType::FtypBox => {
|
BoxType::FtypBox => {
|
||||||
let ftyp = FtypBox::read_box(&mut reader, size)?;
|
let ftyp = FtypBox::read_box(&mut self.reader, s)?;
|
||||||
bmff.ftyp = ftyp;
|
self.ftyp = ftyp;
|
||||||
}
|
}
|
||||||
BoxType::FreeBox => {
|
BoxType::FreeBox => {
|
||||||
start = 0;
|
skip_box(&mut self.reader, s)?;
|
||||||
}
|
}
|
||||||
BoxType::MdatBox => {
|
BoxType::MdatBox => {
|
||||||
start = size - HEADER_SIZE;
|
skip_box(&mut self.reader, s)?;
|
||||||
}
|
}
|
||||||
BoxType::MoovBox => {
|
BoxType::MoovBox => {
|
||||||
let moov = MoovBox::read_box(&mut reader, size)?;
|
let moov = MoovBox::read_box(&mut self.reader, s)?;
|
||||||
bmff.moov = Some(moov);
|
self.moov = Some(moov);
|
||||||
}
|
}
|
||||||
BoxType::MoofBox => {
|
BoxType::MoofBox => {
|
||||||
start = size - HEADER_SIZE;
|
skip_box(&mut self.reader, s)?;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// Skip over unsupported boxes, but stop if the size is zero,
|
// XXX warn!()
|
||||||
// meaning the last box has been reached.
|
skip_box(&mut self.reader, s)?;
|
||||||
if size == 0 {
|
}
|
||||||
break;
|
}
|
||||||
|
current = self.reader.seek(SeekFrom::Current(0))?;
|
||||||
|
}
|
||||||
|
self.size = current - start;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn track_count(&self) -> Result<u32> {
|
||||||
|
if let Some(ref moov) = self.moov {
|
||||||
|
Ok(moov.traks.len() as u32)
|
||||||
} else {
|
} else {
|
||||||
start = size - HEADER_SIZE;
|
Err(Error::BoxNotFound(MoovBox::box_type()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn sample_count(&self, track_id: u32) -> Result<u32> {
|
||||||
|
if track_id == 0 {
|
||||||
|
return Err(Error::TrakNotFound(track_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
let moov = if let Some(ref moov) = self.moov {
|
||||||
|
moov
|
||||||
|
} else {
|
||||||
|
return Err(Error::BoxNotFound(MoovBox::box_type()));
|
||||||
};
|
};
|
||||||
}
|
|
||||||
Ok(bmff)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: if size is 0, then this box is the last one in the file
|
let trak = if let Some(trak) = moov.traks.get(track_id as usize - 1) {
|
||||||
fn read_box_header<R: Read + Seek>(reader: &mut BufReader<R>, start: u64) -> Result<BoxHeader> {
|
trak
|
||||||
// Seek to offset.
|
|
||||||
let _r = reader.seek(SeekFrom::Current(start as i64));
|
|
||||||
|
|
||||||
// Create and read to buf.
|
|
||||||
let mut buf = [0u8;8]; // 8 bytes for box header.
|
|
||||||
reader.read(&mut buf)?;
|
|
||||||
|
|
||||||
// Get size.
|
|
||||||
let s = buf[0..4].try_into().unwrap();
|
|
||||||
let size = 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(&mut buf)?;
|
|
||||||
let s = buf.try_into().unwrap();
|
|
||||||
let largesize = u64::from_be_bytes(s);
|
|
||||||
|
|
||||||
Ok(BoxHeader {
|
|
||||||
name: BoxType::from(typ),
|
|
||||||
size: largesize,
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
Ok(BoxHeader {
|
return Err(Error::TrakNotFound(track_id));
|
||||||
name: BoxType::from(typ),
|
};
|
||||||
size: size as u64,
|
|
||||||
})
|
trak.sample_count()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_sample(
|
||||||
|
&mut self,
|
||||||
|
track_id: u32,
|
||||||
|
sample_id: u32,
|
||||||
|
) -> Result<Option<Mp4Sample>> {
|
||||||
|
if track_id == 0 {
|
||||||
|
return Err(Error::TrakNotFound(track_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
let moov = if let Some(ref moov) = self.moov {
|
||||||
|
moov
|
||||||
|
} else {
|
||||||
|
return Err(Error::BoxNotFound(MoovBox::box_type()));
|
||||||
|
};
|
||||||
|
|
||||||
|
let trak = if let Some(trak) = moov.traks.get(track_id as usize - 1) {
|
||||||
|
trak
|
||||||
|
} else {
|
||||||
|
return Err(Error::TrakNotFound(track_id));
|
||||||
|
};
|
||||||
|
|
||||||
|
trak.read_sample(&mut self.reader, sample_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
54
tests/lib.rs
54
tests/lib.rs
|
@ -1,18 +1,23 @@
|
||||||
use mp4;
|
use mp4;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
use std::io::BufReader;
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_read_mp4() {
|
fn test_read_mp4() {
|
||||||
let filename = "tests/samples/minimal.mp4";
|
let filename = "tests/samples/minimal.mp4";
|
||||||
let f = File::open(filename).unwrap();
|
let f = File::open(filename).unwrap();
|
||||||
let bmff = mp4::read_mp4(f).unwrap();
|
let size = f.metadata().unwrap().len();
|
||||||
|
let reader = BufReader::new(f);
|
||||||
|
|
||||||
assert_eq!(2591, bmff.size);
|
let mut mp4 = mp4::Mp4Reader::new(reader);
|
||||||
|
mp4.read(size).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(2591, mp4.size());
|
||||||
|
|
||||||
// ftyp.
|
// ftyp.
|
||||||
println!("{:?}", bmff.ftyp.compatible_brands);
|
println!("{:?}", mp4.ftyp.compatible_brands);
|
||||||
assert_eq!(4, bmff.ftyp.compatible_brands.len());
|
assert_eq!(4, mp4.ftyp.compatible_brands.len());
|
||||||
|
|
||||||
// Check compatible_brands.
|
// Check compatible_brands.
|
||||||
let brands = vec![
|
let brands = vec![
|
||||||
|
@ -23,16 +28,53 @@ fn test_read_mp4() {
|
||||||
];
|
];
|
||||||
|
|
||||||
for b in brands {
|
for b in brands {
|
||||||
let t = bmff.ftyp.compatible_brands.iter().any(|x| x.to_string() == b);
|
let t = mp4.ftyp.compatible_brands.iter().any(|x| x.to_string() == b);
|
||||||
assert_eq!(t, true);
|
assert_eq!(t, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// moov.
|
// moov.
|
||||||
let moov = bmff.moov.unwrap();
|
assert!(mp4.moov.is_some());
|
||||||
|
if let Some(ref moov) = mp4.moov {
|
||||||
assert_eq!(moov.mvhd.version, 0);
|
assert_eq!(moov.mvhd.version, 0);
|
||||||
assert_eq!(moov.mvhd.creation_time, 0);
|
assert_eq!(moov.mvhd.creation_time, 0);
|
||||||
assert_eq!(moov.mvhd.duration, 62);
|
assert_eq!(moov.mvhd.duration, 62);
|
||||||
assert_eq!(moov.mvhd.timescale, 1000);
|
assert_eq!(moov.mvhd.timescale, 1000);
|
||||||
assert_eq!(moov.traks.len(), 2);
|
assert_eq!(moov.traks.len(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
let sample_count = mp4.sample_count(1).unwrap();
|
||||||
|
assert_eq!(sample_count, 0);
|
||||||
|
|
||||||
|
let sample_count = mp4.sample_count(2).unwrap();
|
||||||
|
assert_eq!(sample_count, 3);
|
||||||
|
let sample1 = mp4.read_sample(2, 1).unwrap().unwrap();
|
||||||
|
assert_eq!(sample1.bytes.len(), 179);
|
||||||
|
assert_eq!(sample1, mp4::Mp4Sample {
|
||||||
|
start_time: 0,
|
||||||
|
duration: 1024,
|
||||||
|
rendering_offset: 0,
|
||||||
|
is_sync: true,
|
||||||
|
bytes: mp4::Bytes::from(vec![0x0u8; 179]),
|
||||||
|
});
|
||||||
|
|
||||||
|
let sample2 = mp4.read_sample(2, 2).unwrap().unwrap();
|
||||||
|
assert_eq!(sample2, mp4::Mp4Sample {
|
||||||
|
start_time: 1024,
|
||||||
|
duration: 1024,
|
||||||
|
rendering_offset: 0,
|
||||||
|
is_sync: true,
|
||||||
|
bytes: mp4::Bytes::from(vec![0x0u8; 180]),
|
||||||
|
});
|
||||||
|
|
||||||
|
let sample3 = mp4.read_sample(2, 3).unwrap().unwrap();
|
||||||
|
assert_eq!(sample3, mp4::Mp4Sample {
|
||||||
|
start_time: 2048,
|
||||||
|
duration: 896,
|
||||||
|
rendering_offset: 0,
|
||||||
|
is_sync: true,
|
||||||
|
bytes: mp4::Bytes::from(vec![0x0u8; 160]),
|
||||||
|
});
|
||||||
|
|
||||||
|
let eos = mp4.read_sample(2, 4).unwrap();
|
||||||
|
assert!(eos.is_none());
|
||||||
}
|
}
|
Loading…
Reference in a new issue