1
0
Fork 0
mirror of https://github.com/alfg/mp4-rust.git synced 2024-06-11 09:29:21 +00:00

Add mandatory check when reading boxes

Add some methods to Mp4Reader, TrackReader
Format codes
This commit is contained in:
Ian Jun 2020-07-31 19:42:05 +09:00
parent c0fdbcf688
commit 4b82165efc
31 changed files with 721 additions and 768 deletions

View file

@ -27,7 +27,7 @@ fn copy<P: AsRef<Path>>(src_filename: &P, _dst_filename: &P) -> Result<()> {
let mut mp4 = mp4::Mp4Reader::new(reader);
mp4.read(size)?;
for tix in 0..mp4.track_count()? {
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 {

View file

@ -4,7 +4,7 @@ use std::io::prelude::*;
use std::io::{self, BufReader};
use std::path::Path;
use mp4::{Result, Mp4Reader, TrackType};
use mp4::{Mp4Reader, Result, TrackType};
fn main() {
let args: Vec<String> = env::args().collect();
@ -27,22 +27,26 @@ fn info<P: AsRef<Path>>(filename: &P) -> Result<()> {
let mut mp4 = Mp4Reader::new(reader);
mp4.read(size)?;
println!("File:");
println!(" size: {}", mp4.size());
println!(" brands: {:?} {:?}\n",
mp4.ftyp.major_brand, mp4.ftyp.compatible_brands);
println!("Metadata:");
println!(" size: {}", mp4.size());
println!(
" brands: {:?} {:?}\n",
mp4.major_brand(),
mp4.compatible_brands()
);
println!(
"Duration: {}, timescale: {}",
mp4.duration()?,
mp4.timescale()?
);
for track in mp4.tracks().iter() {
println!(" Track: {}", track.track_id());
}
if let Some(ref moov) = mp4.moov {
println!("Movie:");
println!(" version: {:?}", moov.mvhd.version);
println!(" creation time: {}",
creation_time(moov.mvhd.creation_time));
println!(" duration: {:?}", moov.mvhd.duration);
println!(" timescale: {:?}\n", moov.mvhd.timescale);
println!("Found {} Tracks", moov.traks.len());
for trak in moov.traks.iter() {
let tkhd = trak.tkhd.as_ref().unwrap();
let tkhd = &trak.tkhd;
println!("Track: {:?}", tkhd.track_id);
println!(" flags: {:?}", tkhd.flags);
println!(" id: {:?}", tkhd.track_id);
@ -51,35 +55,34 @@ fn info<P: AsRef<Path>>(filename: &P) -> Result<()> {
println!(" width: {:?}", tkhd.width);
println!(" height: {:?}", tkhd.height);
}
if let Some(ref mdia) = trak.mdia {
let hdlr = mdia.hdlr.as_ref().unwrap();
let mdhd = mdia.mdhd.as_ref().unwrap();
let stts = mdia
.minf
.as_ref()
.map(|m| m.stbl.as_ref().map(|s| s.stts.as_ref()).flatten())
.flatten();
println!(" type: {:?}",
get_handler_type(hdlr.handler_type.value.as_ref()));
println!(" language: {:?}", mdhd.language);
let mdia = &trak.mdia;
let hdlr = &mdia.hdlr;
let mdhd = &mdia.mdhd;
let stts = &mdia.minf.stbl.stts;
println!(" media:");
if let Some(ref s) = stts {
println!(" sample count: {:?}", s.entries[0].sample_count);
}
println!(" timescale: {:?}", mdhd.timescale);
println!(" duration: {:?} (media timescale units)",
mdhd.duration);
println!(" duration: {:?} (ms)",
get_duration_ms(mdhd.duration, mdhd.timescale));
if get_handler_type(hdlr.handler_type.value.as_ref()) == TrackType::Video {
if let Some(ref s) = stts {
println!(" frame rate: (computed): {:?}",
get_framerate(s.entries[0].sample_count,
mdhd.duration, mdhd.timescale));
}
}
println!(
" type: {:?}",
get_handler_type(hdlr.handler_type.value.as_ref())
);
println!(" language: {:?}", mdhd.language);
println!(" media:");
println!(" sample count: {:?}", stts.entries[0].sample_count);
println!(" timescale: {:?}", mdhd.timescale);
println!(
" duration: {:?} (media timescale units)",
mdhd.duration
);
println!(
" duration: {:?} (ms)",
get_duration_ms(mdhd.duration, mdhd.timescale)
);
if get_handler_type(hdlr.handler_type.value.as_ref()) == TrackType::Video {
println!(
" frame rate: (computed): {:?}",
get_framerate(stts.entries[0].sample_count, mdhd.duration, mdhd.timescale)
);
}
}
}
@ -109,11 +112,11 @@ fn get_framerate(sample_count: u32, duration: u64, timescale: u32) -> String {
return format!("{:.2}", sc / ms.floor());
}
fn creation_time(creation_time: u64) -> u64 {
// convert from MP4 epoch (1904-01-01) to Unix epoch (1970-01-01)
if creation_time >= 2082844800 {
creation_time - 2082844800
} else {
creation_time
}
}
// fn creation_time(creation_time: u64) -> u64 {
// // convert from MP4 epoch (1904-01-01) to Unix epoch (1970-01-01)
// if creation_time >= 2082844800 {
// creation_time - 2082844800
// } else {
// creation_time
// }
// }

View file

@ -1,12 +1,10 @@
use std::io::{Seek, Read, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use num_rational::Ratio;
use std::io::{Read, Seek, Write};
use crate::*;
use crate::atoms::*;
#[derive(Debug, PartialEq)]
#[derive(Debug, Clone, PartialEq)]
pub struct Avc1Box {
pub data_reference_index: u16,
pub width: u16,
@ -67,7 +65,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for Avc1Box {
reader.read_i16::<BigEndian>()?; // pre-defined
let header = BoxHeader::read(reader)?;
let BoxHeader{ name, size: s } = header;
let BoxHeader { name, size: s } = header;
if name == BoxType::AvcCBox {
let avcc = AvcCBox::read_box(reader, s)?;
@ -120,8 +118,7 @@ impl<W: Write> WriteBox<&mut W> for Avc1Box {
}
}
#[derive(Debug, Default, PartialEq)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct AvcCBox {
pub configuration_version: u8,
pub avc_profile_indication: u8,
@ -207,8 +204,7 @@ impl<W: Write> WriteBox<&mut W> for AvcCBox {
}
}
#[derive(Debug, Default, PartialEq)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct NalUnit {
pub bytes: Vec<u8>,
}
@ -222,9 +218,7 @@ impl NalUnit {
let length = reader.read_u16::<BigEndian>()? as usize;
let mut bytes = vec![0u8; length];
reader.read(&mut bytes)?;
Ok(NalUnit {
bytes,
})
Ok(NalUnit { bytes })
}
pub fn write<W: Write>(&self, writer: &mut W) -> Result<u64> {

View file

@ -1,11 +1,9 @@
use std::io::{Seek, Read, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::io::{Read, Seek, Write};
use crate::*;
use crate::atoms::*;
#[derive(Debug, Default, PartialEq)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Co64Box {
pub version: u8,
pub flags: u32,

View file

@ -1,18 +1,16 @@
use std::io::{Seek, Read, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::io::{Read, Seek, Write};
use crate::*;
use crate::atoms::*;
#[derive(Debug, Default, PartialEq)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct CttsBox {
pub version: u8,
pub flags: u32,
pub entries: Vec<CttsEntry>,
}
#[derive(Debug, Default, PartialEq)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct CttsEntry {
pub sample_count: u32,
pub sample_offset: i32,
@ -83,8 +81,14 @@ mod tests {
version: 0,
flags: 0,
entries: vec![
CttsEntry {sample_count: 1, sample_offset: 200},
CttsEntry {sample_count: 2, sample_offset: -100},
CttsEntry {
sample_count: 1,
sample_offset: 200,
},
CttsEntry {
sample_count: 2,
sample_offset: -100,
},
],
};
let mut buf = Vec::new();

View file

@ -1,11 +1,9 @@
use std::io::{Seek, Read, Write};
use std::io::{Read, Seek, Write};
use crate::*;
use crate::atoms::*;
use crate::atoms::elst::ElstBox;
use crate::atoms::*;
#[derive(Debug, Default)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct EdtsBox {
pub elst: Option<ElstBox>,
}
@ -37,7 +35,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for EdtsBox {
let mut edts = EdtsBox::new();
let header = BoxHeader::read(reader)?;
let BoxHeader{ name, size: s } = header;
let BoxHeader { name, size: s } = header;
match name {
BoxType::ElstBox => {

View file

@ -1,18 +1,16 @@
use std::io::{Seek, Read, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::io::{Read, Seek, Write};
use crate::*;
use crate::atoms::*;
#[derive(Debug, Default, PartialEq)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct ElstBox {
pub version: u8,
pub flags: u32,
pub entries: Vec<ElstEntry>,
}
#[derive(Debug, Default, PartialEq)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct ElstEntry {
pub segment_duration: u64,
pub media_time: u64,
@ -46,20 +44,19 @@ impl<R: Read + Seek> ReadBox<&mut R> for ElstBox {
let entry_count = reader.read_u32::<BigEndian>()?;
let mut entries = Vec::with_capacity(entry_count as usize);
for _ in 0..entry_count {
let (segment_duration, media_time)
= if version == 1 {
(
reader.read_u64::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
)
} else {
(
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()? as u64,
)
};
let (segment_duration, media_time) = if version == 1 {
(
reader.read_u64::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
)
} else {
(
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()? as u64,
)
};
let entry = ElstEntry{
let entry = ElstEntry {
segment_duration,
media_time,
media_rate: reader.read_u16::<BigEndian>()?,

View file

@ -1,11 +1,9 @@
use std::io::{Seek, Read, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::io::{Read, Seek, Write};
use crate::*;
use crate::atoms::*;
#[derive(Debug, Default, PartialEq)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct FtypBox {
pub major_brand: FourCC,
pub minor_version: u32,
@ -72,14 +70,24 @@ mod tests {
#[test]
fn test_ftyp() {
let src_box = FtypBox {
major_brand: FourCC { value: String::from("isom") },
major_brand: FourCC {
value: String::from("isom"),
},
minor_version: 0,
compatible_brands: vec![
FourCC { value: String::from("isom") },
FourCC { value: String::from("iso2") },
FourCC { value: String::from("avc1") },
FourCC { value: String::from("mp41") },
]
FourCC {
value: String::from("isom"),
},
FourCC {
value: String::from("iso2"),
},
FourCC {
value: String::from("avc1"),
},
FourCC {
value: String::from("mp41"),
},
],
};
let mut buf = Vec::new();
src_box.write_box(&mut buf).unwrap();

View file

@ -1,11 +1,9 @@
use std::io::{Seek, Read, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::io::{Read, Seek, Write};
use crate::*;
use crate::atoms::*;
#[derive(Debug, Default, PartialEq)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct HdlrBox {
pub version: u8,
pub flags: u32,
@ -42,7 +40,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for HdlrBox {
Ok(t) => {
assert_eq!(t.len(), buf_size as usize);
t
},
}
_ => String::from("null"),
};

View file

@ -1,12 +1,10 @@
use std::io::{Seek, Read, Write};
use std::char::{decode_utf16, REPLACEMENT_CHARACTER};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::char::{decode_utf16, REPLACEMENT_CHARACTER};
use std::io::{Read, Seek, Write};
use crate::*;
use crate::atoms::*;
#[derive(Debug, PartialEq)]
#[derive(Debug, Clone, PartialEq)]
pub struct MdhdBox {
pub version: u8,
pub flags: u32,
@ -56,23 +54,22 @@ impl<R: Read + Seek> ReadBox<&mut R> for MdhdBox {
let (version, flags) = read_box_header_ext(reader)?;
let (creation_time, modification_time, timescale, duration)
= if version == 1 {
(
reader.read_u64::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
reader.read_u32::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
)
} else {
assert_eq!(version, 0);
(
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()?,
reader.read_u32::<BigEndian>()? as u64,
)
};
let (creation_time, modification_time, timescale, duration) = if version == 1 {
(
reader.read_u64::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
reader.read_u32::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
)
} else {
assert_eq!(version, 0);
(
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()?,
reader.read_u32::<BigEndian>()? as u64,
)
};
let language_code = reader.read_u16::<BigEndian>()?;
let language = get_language_string(language_code);

View file

@ -1,21 +1,13 @@
use std::io::{Seek, SeekFrom, Read, Write};
use std::io::{Read, Seek, SeekFrom, Write};
use crate::*;
use crate::atoms::*;
use crate::atoms::{mdhd::MdhdBox, hdlr::HdlrBox, minf::MinfBox};
use crate::atoms::{hdlr::HdlrBox, mdhd::MdhdBox, minf::MinfBox};
#[derive(Debug, Default)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct MdiaBox {
pub mdhd: Option<MdhdBox>,
pub hdlr: Option<HdlrBox>,
pub minf: Option<MinfBox>,
}
impl MdiaBox {
pub(crate) fn new() -> MdiaBox {
Default::default()
}
pub mdhd: MdhdBox,
pub hdlr: HdlrBox,
pub minf: MinfBox,
}
impl Mp4Box for MdiaBox {
@ -24,17 +16,7 @@ impl Mp4Box for MdiaBox {
}
fn box_size(&self) -> u64 {
let mut size = HEADER_SIZE;
if let Some(ref mdhd) = self.mdhd {
size += mdhd.box_size();
}
if let Some(ref hdlr) = self.hdlr {
size += hdlr.box_size();
}
if let Some(ref minf) = self.minf {
size += minf.box_size();
}
size
HEADER_SIZE + self.mdhd.box_size() + self.hdlr.box_size() + self.minf.box_size()
}
}
@ -42,27 +24,26 @@ impl<R: Read + Seek> ReadBox<&mut R> for MdiaBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = get_box_start(reader)?;
let mut mdia = MdiaBox::new();
let mut mdhd = None;
let mut hdlr = None;
let mut minf = None;
let mut current = reader.seek(SeekFrom::Current(0))?;
let end = start + size;
while current < end {
// Get box header.
let header = BoxHeader::read(reader)?;
let BoxHeader{ name, size: s } = header;
let BoxHeader { name, size: s } = header;
match name {
BoxType::MdhdBox => {
let mdhd = MdhdBox::read_box(reader, s)?;
mdia.mdhd = Some(mdhd);
mdhd = Some(MdhdBox::read_box(reader, s)?);
}
BoxType::HdlrBox => {
let hdlr = HdlrBox::read_box(reader, s)?;
mdia.hdlr = Some(hdlr);
hdlr = Some(HdlrBox::read_box(reader, s)?);
}
BoxType::MinfBox => {
let minf = MinfBox::read_box(reader, s)?;
mdia.minf = Some(minf);
minf = Some(MinfBox::read_box(reader, s)?);
}
_ => {
// XXX warn!()
@ -73,9 +54,23 @@ impl<R: Read + Seek> ReadBox<&mut R> for MdiaBox {
current = reader.seek(SeekFrom::Current(0))?;
}
if mdhd.is_none() {
return Err(Error::BoxNotFound(BoxType::MdhdBox));
}
if hdlr.is_none() {
return Err(Error::BoxNotFound(BoxType::HdlrBox));
}
if minf.is_none() {
return Err(Error::BoxNotFound(BoxType::MinfBox));
}
skip_read_to(reader, start + size)?;
Ok(mdia)
Ok(MdiaBox {
mdhd: mdhd.unwrap(),
hdlr: hdlr.unwrap(),
minf: minf.unwrap(),
})
}
}
@ -84,15 +79,9 @@ impl<W: Write> WriteBox<&mut W> for MdiaBox {
let size = self.box_size();
BoxHeader::new(Self::box_type(), size).write(writer)?;
if let Some(ref mdhd) = self.mdhd {
mdhd.write_box(writer)?;
}
if let Some(ref hdlr) = self.hdlr {
hdlr.write_box(writer)?;
}
if let Some(ref minf) = self.minf {
minf.write_box(writer)?;
}
self.mdhd.write_box(writer)?;
self.hdlr.write_box(writer)?;
self.minf.write_box(writer)?;
Ok(size)
}

View file

@ -1,21 +1,13 @@
use std::io::{Seek, SeekFrom, Read, Write};
use std::io::{Read, Seek, SeekFrom, Write};
use crate::*;
use crate::atoms::*;
use crate::atoms::{vmhd::VmhdBox, smhd::SmhdBox, stbl::StblBox};
use crate::atoms::{smhd::SmhdBox, stbl::StblBox, vmhd::VmhdBox};
#[derive(Debug, Default)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct MinfBox {
pub vmhd: Option<VmhdBox>,
pub smhd: Option<SmhdBox>,
pub stbl: Option<StblBox>,
}
impl MinfBox {
pub(crate) fn new() -> MinfBox {
Default::default()
}
pub stbl: StblBox,
}
impl Mp4Box for MinfBox {
@ -31,9 +23,7 @@ impl Mp4Box for MinfBox {
if let Some(ref smhd) = self.smhd {
size += smhd.box_size();
}
if let Some(ref stbl) = self.stbl {
size += stbl.box_size();
}
size += self.stbl.box_size();
size
}
}
@ -42,30 +32,30 @@ impl<R: Read + Seek> ReadBox<&mut R> for MinfBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = get_box_start(reader)?;
let mut minf = MinfBox::new();
let mut vmhd = None;
let mut smhd = None;
let mut stbl = None;
let mut current = reader.seek(SeekFrom::Current(0))?;
let end = start + size;
while current < end {
// Get box header.
let header = BoxHeader::read(reader)?;
let BoxHeader{ name, size: s } = header;
let BoxHeader { name, size: s } = header;
match name {
BoxType::VmhdBox => {
let vmhd = VmhdBox::read_box(reader, s)?;
minf.vmhd = Some(vmhd);
vmhd = Some(VmhdBox::read_box(reader, s)?);
}
BoxType::SmhdBox => {
let smhd = SmhdBox::read_box(reader, s)?;
minf.smhd = Some(smhd);
smhd = Some(SmhdBox::read_box(reader, s)?);
}
BoxType::DinfBox => {// XXX warn!()
BoxType::DinfBox => {
// XXX warn!()
skip_box(reader, s)?;
}
BoxType::StblBox => {
let stbl = StblBox::read_box(reader, s)?;
minf.stbl = Some(stbl);
stbl = Some(StblBox::read_box(reader, s)?);
}
_ => {
// XXX warn!()
@ -76,9 +66,17 @@ impl<R: Read + Seek> ReadBox<&mut R> for MinfBox {
current = reader.seek(SeekFrom::Current(0))?;
}
if stbl.is_none() {
return Err(Error::BoxNotFound(BoxType::StblBox));
}
skip_read_to(reader, start + size)?;
Ok(minf)
Ok(MinfBox {
vmhd,
smhd,
stbl: stbl.unwrap(),
})
}
}
@ -93,9 +91,7 @@ impl<W: Write> WriteBox<&mut W> for MinfBox {
if let Some(ref smhd) = self.smhd {
smhd.write_box(writer)?;
}
if let Some(ref stbl) = self.stbl {
stbl.write_box(writer)?;
}
self.stbl.write_box(writer)?;
Ok(size)
}

View file

@ -1,33 +1,33 @@
use std::fmt;
use std::io::{Seek, SeekFrom, Read, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::fmt;
use std::io::{Read, Seek, SeekFrom, Write};
use crate::*;
mod ftyp;
mod moov;
mod mvhd;
mod trak;
mod tkhd;
mod edts;
mod elst;
mod mdia;
mod mdhd;
mod hdlr;
mod minf;
mod vmhd;
mod smhd;
mod stbl;
mod stsd;
mod stts;
mod ctts;
mod stss;
mod stsc;
mod stsz;
mod stco;
mod co64;
mod avc;
mod mp4a;
pub(crate) mod avc;
pub(crate) mod co64;
pub(crate) mod ctts;
pub(crate) mod edts;
pub(crate) mod elst;
pub(crate) mod ftyp;
pub(crate) mod hdlr;
pub(crate) mod mdhd;
pub(crate) mod mdia;
pub(crate) mod minf;
pub(crate) mod moov;
pub(crate) mod mp4a;
pub(crate) mod mvhd;
pub(crate) mod smhd;
pub(crate) mod stbl;
pub(crate) mod stco;
pub(crate) mod stsc;
pub(crate) mod stsd;
pub(crate) mod stss;
pub(crate) mod stsz;
pub(crate) mod stts;
pub(crate) mod tkhd;
pub(crate) mod trak;
pub(crate) mod vmhd;
pub use ftyp::FtypBox;
pub use moov::MoovBox;
@ -64,7 +64,7 @@ macro_rules! boxtype {
}
}
boxtype!{
boxtype! {
FtypBox => 0x66747970,
MvhdBox => 0x6d766864,
FreeBox => 0x66726565,
@ -114,7 +114,7 @@ impl fmt::Display for BoxType {
#[derive(Default, PartialEq, Clone)]
pub struct FourCC {
pub value: String
pub value: String,
}
impl From<u32> for FourCC {
@ -131,9 +131,7 @@ impl From<u32> for FourCC {
_ => String::from("null"), // error to retrieve fourcc
};
FourCC {
value: box_string
}
FourCC { value: box_string }
}
}
@ -158,7 +156,7 @@ impl From<String> for FourCC {
} else {
fourcc
};
FourCC {value}
FourCC { value }
}
}
@ -169,7 +167,7 @@ impl From<&str> for FourCC {
} else {
fourcc.to_string()
};
FourCC {value}
FourCC { value }
}
}
@ -219,7 +217,7 @@ impl BoxHeader {
// TODO: if size is 0, then this box is the last one in the file
pub fn read<R: Read>(reader: &mut R) -> Result<Self> {
// Create and read to buf.
let mut buf = [0u8;8]; // 8 bytes for box header.
let mut buf = [0u8; 8]; // 8 bytes for box header.
reader.read(&mut buf)?;
// Get size.

View file

@ -1,22 +1,14 @@
use std::io::{Seek, SeekFrom, Read, Write};
use std::io::{Read, Seek, SeekFrom, Write};
use crate::*;
use crate::atoms::*;
use crate::atoms::{mvhd::MvhdBox, trak::TrakBox};
#[derive(Debug, Default)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct MoovBox {
pub mvhd: MvhdBox,
pub traks: Vec<TrakBox>,
}
impl MoovBox {
pub(crate) fn new() -> MoovBox {
Default::default()
}
}
impl Mp4Box for MoovBox {
fn box_type() -> BoxType {
BoxType::MoovBox
@ -35,23 +27,23 @@ impl<R: Read + Seek> ReadBox<&mut R> for MoovBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = get_box_start(reader)?;
let mut moov = MoovBox::new();
let mut mvhd = None;
let mut traks = Vec::new();
let mut current = reader.seek(SeekFrom::Current(0))?;
let end = start + size;
while current < end {
// Get box header.
let header = BoxHeader::read(reader)?;
let BoxHeader{ name, size: s } = header;
let BoxHeader { name, size: s } = header;
match name {
BoxType::MvhdBox => {
moov.mvhd = MvhdBox::read_box(reader, s)?;
mvhd = Some(MvhdBox::read_box(reader, s)?);
}
BoxType::TrakBox => {
let mut trak = TrakBox::read_box(reader, s)?;
trak.id = moov.traks.len() as u32 + 1;
moov.traks.push(trak);
let trak = TrakBox::read_box(reader, s)?;
traks.push(trak);
}
BoxType::UdtaBox => {
// XXX warn!()
@ -66,9 +58,16 @@ impl<R: Read + Seek> ReadBox<&mut R> for MoovBox {
current = reader.seek(SeekFrom::Current(0))?;
}
if mvhd.is_none() {
return Err(Error::BoxNotFound(BoxType::MvhdBox));
}
skip_read_to(reader, start + size)?;
Ok(moov)
Ok(MoovBox {
mvhd: mvhd.unwrap(),
traks,
})
}
}

View file

@ -1,11 +1,9 @@
use std::io::{Seek, Read, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::io::{Read, Seek, Write};
use crate::*;
use crate::atoms::*;
#[derive(Debug, PartialEq)]
#[derive(Debug, Clone, PartialEq)]
pub struct Mp4aBox {
pub data_reference_index: u16,
pub channel_count: u16,
@ -51,7 +49,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for Mp4aBox {
let samplerate = reader.read_u32::<BigEndian>()?;
let header = BoxHeader::read(reader)?;
let BoxHeader{ name, size: s } = header;
let BoxHeader { name, size: s } = header;
if name == BoxType::EsdsBox {
let esds = EsdsBox::read_box(reader, s)?;
@ -91,8 +89,7 @@ impl<W: Write> WriteBox<&mut W> for Mp4aBox {
}
}
#[derive(Debug, Default, PartialEq)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct EsdsBox {
pub version: u8,
pub flags: u32,
@ -138,7 +135,6 @@ impl<W: Write> WriteBox<&mut W> for EsdsBox {
}
}
trait Descriptor: Sized {
fn desc_tag() -> u8;
fn desc_size() -> u32;
@ -192,8 +188,7 @@ fn write_desc<W: Write>(writer: &mut W, tag: u8, size: u32) -> Result<u64> {
Ok(1 + nbytes)
}
#[derive(Debug, Default, PartialEq)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct ESDescriptor {
pub tag: u8,
pub size: u32,
@ -211,9 +206,7 @@ impl Descriptor for ESDescriptor {
// XXX size > 0x7F
fn desc_size() -> u32 {
2 + 3
+ DecoderConfigDescriptor::desc_size()
+ SLConfigDescriptor::desc_size()
2 + 3 + DecoderConfigDescriptor::desc_size() + SLConfigDescriptor::desc_size()
}
}
@ -248,7 +241,7 @@ impl<W: Write> WriteDesc<&mut W> for ESDescriptor {
}
}
#[derive(Debug, Default, PartialEq)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct DecoderConfigDescriptor {
pub tag: u8,
pub size: u32,
@ -292,7 +285,7 @@ impl<R: Read + Seek> ReadDesc<&mut R> for DecoderConfigDescriptor {
let dec_specific = DecoderSpecificDescriptor::read_desc(reader)?;
// XXX skip_read
for _ in DecoderConfigDescriptor::desc_size()..size-1 {
for _ in DecoderConfigDescriptor::desc_size()..size - 1 {
reader.read_u8()?;
}
@ -318,7 +311,7 @@ impl<W: Write> WriteDesc<&mut W> for DecoderConfigDescriptor {
}
}
#[derive(Debug, Default, PartialEq)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct DecoderSpecificDescriptor {
pub tag: u8,
pub size: u32,
@ -369,7 +362,7 @@ impl<W: Write> WriteDesc<&mut W> for DecoderSpecificDescriptor {
}
}
#[derive(Debug, Default, PartialEq)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct SLConfigDescriptor {
pub tag: u8,
pub size: u32,
@ -395,10 +388,7 @@ impl<R: Read + Seek> ReadDesc<&mut R> for SLConfigDescriptor {
reader.read_u8()?; // pre-defined
Ok(SLConfigDescriptor {
tag,
size,
})
Ok(SLConfigDescriptor { tag, size })
}
}

View file

@ -1,12 +1,10 @@
use std::io::{Seek, Read, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use num_rational::Ratio;
use std::io::{Read, Seek, Write};
use crate::*;
use crate::atoms::*;
#[derive(Debug, PartialEq)]
#[derive(Debug, Clone, PartialEq)]
pub struct MvhdBox {
pub version: u8,
pub flags: u32,
@ -55,29 +53,28 @@ impl<R: Read + Seek> ReadBox<&mut R> for MvhdBox {
let (version, flags) = read_box_header_ext(reader)?;
let (creation_time, modification_time, timescale, duration)
= if version == 1 {
(
reader.read_u64::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
reader.read_u32::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
)
} else {
assert_eq!(version, 0);
(
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()?,
reader.read_u32::<BigEndian>()? as u64,
)
};
let (creation_time, modification_time, timescale, duration) = if version == 1 {
(
reader.read_u64::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
reader.read_u32::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
)
} else {
assert_eq!(version, 0);
(
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()?,
reader.read_u32::<BigEndian>()? as u64,
)
};
let numer = reader.read_u32::<BigEndian>()?;
let rate = Ratio::new_raw(numer, 0x10000);
skip_read_to(reader, start + size)?;
Ok(MvhdBox{
Ok(MvhdBox {
version,
flags,
creation_time,

View file

@ -1,12 +1,10 @@
use std::io::{Seek, Read, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use num_rational::Ratio;
use std::io::{Read, Seek, Write};
use crate::*;
use crate::atoms::*;
#[derive(Debug, PartialEq)]
#[derive(Debug, Clone, PartialEq)]
pub struct SmhdBox {
pub version: u8,
pub flags: u32,
@ -66,7 +64,6 @@ impl<W: Write> WriteBox<&mut W> for SmhdBox {
}
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -1,37 +1,23 @@
use std::io::{Seek, SeekFrom, Read, Write};
use std::io::{Read, Seek, SeekFrom, Write};
use crate::*;
use crate::atoms::*;
use crate::atoms::{
stsd::StsdBox,
stts::SttsBox,
ctts::CttsBox,
stss::StssBox,
stsc::StscBox,
stsz::StszBox,
stco::StcoBox,
co64::Co64Box,
co64::Co64Box, ctts::CttsBox, stco::StcoBox, stsc::StscBox, stsd::StsdBox, stss::StssBox,
stsz::StszBox, stts::SttsBox,
};
#[derive(Debug, Default)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct StblBox {
pub stsd: Option<StsdBox>,
pub stts: Option<SttsBox>,
pub stsd: StsdBox,
pub stts: SttsBox,
pub ctts: Option<CttsBox>,
pub stss: Option<StssBox>,
pub stsc: Option<StscBox>,
pub stsz: Option<StszBox>,
pub stsc: StscBox,
pub stsz: StszBox,
pub stco: Option<StcoBox>,
pub co64: Option<Co64Box>,
}
impl StblBox {
pub(crate) fn new() -> StblBox {
Default::default()
}
}
impl Mp4Box for StblBox {
fn box_type() -> BoxType {
BoxType::StblBox
@ -39,24 +25,16 @@ impl Mp4Box for StblBox {
fn box_size(&self) -> u64 {
let mut size = HEADER_SIZE;
if let Some(ref stsd) = self.stsd {
size += stsd.box_size();
}
if let Some(ref stts) = self.stts {
size += stts.box_size();
}
size += self.stsd.box_size();
size += self.stts.box_size();
if let Some(ref ctts) = self.ctts {
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();
}
size += self.stsc.box_size();
size += self.stsz.box_size();
if let Some(ref stco) = self.stco {
size += stco.box_size();
}
@ -71,47 +49,46 @@ impl<R: Read + Seek> ReadBox<&mut R> for StblBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = get_box_start(reader)?;
let mut stbl = StblBox::new();
let mut stsd = None;
let mut stts = None;
let mut ctts = None;
let mut stss = None;
let mut stsc = None;
let mut stsz = None;
let mut stco = None;
let mut co64 = None;
let mut current = reader.seek(SeekFrom::Current(0))?;
let end = start + size;
while current < end {
// Get box header.
let header = BoxHeader::read(reader)?;
let BoxHeader{ name, size: s } = header;
let BoxHeader { name, size: s } = header;
match name {
BoxType::StsdBox => {
let stsd = StsdBox::read_box(reader, s)?;
stbl.stsd = Some(stsd);
stsd = Some(StsdBox::read_box(reader, s)?);
}
BoxType::SttsBox => {
let stts = SttsBox::read_box(reader, s)?;
stbl.stts = Some(stts);
stts = Some(SttsBox::read_box(reader, s)?);
}
BoxType::CttsBox => {
let ctts = CttsBox::read_box(reader, s)?;
stbl.ctts = Some(ctts);
ctts = Some(CttsBox::read_box(reader, s)?);
}
BoxType::StssBox => {
let stss = StssBox::read_box(reader, s)?;
stbl.stss = Some(stss);
stss = Some(StssBox::read_box(reader, s)?);
}
BoxType::StscBox => {
let stsc = StscBox::read_box(reader, s)?;
stbl.stsc = Some(stsc);
stsc = Some(StscBox::read_box(reader, s)?);
}
BoxType::StszBox => {
let stsz = StszBox::read_box(reader, s)?;
stbl.stsz = Some(stsz);
stsz = Some(StszBox::read_box(reader, s)?);
}
BoxType::StcoBox => {
let stco = StcoBox::read_box(reader, s)?;
stbl.stco = Some(stco);
stco = Some(StcoBox::read_box(reader, s)?);
}
BoxType::Co64Box => {
let co64 = Co64Box::read_box(reader, s)?;
stbl.co64 = Some(co64);
co64 = Some(Co64Box::read_box(reader, s)?);
}
_ => {
// XXX warn!()
@ -121,9 +98,34 @@ impl<R: Read + Seek> ReadBox<&mut R> for StblBox {
current = reader.seek(SeekFrom::Current(0))?;
}
if stsd.is_none() {
return Err(Error::BoxNotFound(BoxType::StsdBox));
}
if stts.is_none() {
return Err(Error::BoxNotFound(BoxType::SttsBox));
}
if stsc.is_none() {
return Err(Error::BoxNotFound(BoxType::StscBox));
}
if stsz.is_none() {
return Err(Error::BoxNotFound(BoxType::StszBox));
}
if stco.is_none() && co64.is_none() {
return Err(Error::Box2NotFound(BoxType::StcoBox, BoxType::Co64Box));
}
skip_read_to(reader, start + size)?;
Ok(stbl)
Ok(StblBox {
stsd: stsd.unwrap(),
stts: stts.unwrap(),
ctts: ctts,
stss: stss,
stsc: stsc.unwrap(),
stsz: stsz.unwrap(),
stco: stco,
co64: co64,
})
}
}
@ -132,24 +134,16 @@ impl<W: Write> WriteBox<&mut W> for StblBox {
let size = self.box_size();
BoxHeader::new(Self::box_type(), size).write(writer)?;
if let Some(ref stsd) = self.stsd {
stsd.write_box(writer)?;
}
if let Some(ref stts) = self.stts {
stts.write_box(writer)?;
}
self.stsd.write_box(writer)?;
self.stts.write_box(writer)?;
if let Some(ref ctts) = self.ctts {
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)?;
}
self.stsc.write_box(writer)?;
self.stsz.write_box(writer)?;
if let Some(ref stco) = self.stco {
stco.write_box(writer)?;
}

View file

@ -1,11 +1,9 @@
use std::io::{Seek, Read, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::io::{Read, Seek, Write};
use crate::*;
use crate::atoms::*;
#[derive(Debug, Default, PartialEq)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct StcoBox {
pub version: u8,
pub flags: u32,

View file

@ -1,18 +1,16 @@
use std::io::{Seek, Read, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::io::{Read, Seek, Write};
use crate::*;
use crate::atoms::*;
#[derive(Debug, Default, PartialEq)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct StscBox {
pub version: u8,
pub flags: u32,
pub entries: Vec<StscEntry>,
}
#[derive(Debug, Default, PartialEq)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct StscEntry {
pub first_chunk: u32,
pub samples_per_chunk: u32,

View file

@ -1,12 +1,10 @@
use std::io::{Seek, Read, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::io::{Read, Seek, Write};
use crate::*;
use crate::atoms::*;
use crate::atoms::{avc::Avc1Box, mp4a::Mp4aBox};
#[derive(Debug)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct StsdBox {
pub version: u8,
pub flags: u32,
@ -43,7 +41,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for StsdBox {
// Get box header.
let header = BoxHeader::read(reader)?;
let BoxHeader{ name, size: s } = header;
let BoxHeader { name, size: s } = header;
match name {
BoxType::Avc1Box => {

View file

@ -1,11 +1,9 @@
use std::io::{Seek, Read, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::io::{Read, Seek, Write};
use crate::*;
use crate::atoms::*;
#[derive(Debug, Default, PartialEq)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct StssBox {
pub version: u8,
pub flags: u32,

View file

@ -1,11 +1,9 @@
use std::io::{Seek, Read, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::io::{Read, Seek, Write};
use crate::*;
use crate::atoms::*;
#[derive(Debug, Default, PartialEq)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct StszBox {
pub version: u8,
pub flags: u32,

View file

@ -1,18 +1,16 @@
use std::io::{Seek, Read, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::io::{Read, Seek, Write};
use crate::*;
use crate::atoms::*;
#[derive(Debug, Default, PartialEq)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct SttsBox {
pub version: u8,
pub flags: u32,
pub entries: Vec<SttsEntry>,
}
#[derive(Debug, Default, PartialEq)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct SttsEntry {
pub sample_count: u32,
pub sample_delta: u32,
@ -83,8 +81,14 @@ mod tests {
version: 0,
flags: 0,
entries: vec![
SttsEntry {sample_count: 29726, sample_delta: 1024},
SttsEntry {sample_count: 1, sample_delta: 512},
SttsEntry {
sample_count: 29726,
sample_delta: 1024,
},
SttsEntry {
sample_count: 1,
sample_delta: 512,
},
],
};
let mut buf = Vec::new();

View file

@ -1,12 +1,10 @@
use std::io::{Seek, Read, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use num_rational::Ratio;
use std::io::{Read, Seek, Write};
use crate::*;
use crate::atoms::*;
#[derive(Debug, PartialEq)]
#[derive(Debug, Clone, PartialEq)]
pub struct TkhdBox {
pub version: u8,
pub flags: u32,
@ -14,7 +12,7 @@ pub struct TkhdBox {
pub modification_time: u64,
pub track_id: u32,
pub duration: u64,
pub layer: u16,
pub layer: u16,
pub alternate_group: u16,
pub volume: Ratio<u16>,
pub matrix: Matrix,
@ -41,7 +39,7 @@ impl Default for TkhdBox {
}
}
#[derive(Debug, Default, PartialEq)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Matrix {
pub a: i32,
pub b: i32,
@ -78,24 +76,23 @@ impl<R: Read + Seek> ReadBox<&mut R> for TkhdBox {
let (version, flags) = read_box_header_ext(reader)?;
let (creation_time, modification_time, track_id, _, duration)
= if version == 1 {
(
reader.read_u64::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
reader.read_u32::<BigEndian>()?,
reader.read_u32::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
)
let (creation_time, modification_time, track_id, _, duration) = if version == 1 {
(
reader.read_u64::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
reader.read_u32::<BigEndian>()?,
reader.read_u32::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
)
} else {
assert_eq!(version, 0);
(
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()?,
reader.read_u32::<BigEndian>()?,
reader.read_u32::<BigEndian>()? as u64,
)
assert_eq!(version, 0);
(
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()?,
reader.read_u32::<BigEndian>()?,
reader.read_u32::<BigEndian>()? as u64,
)
};
reader.read_u64::<BigEndian>()?; // reserved
let layer = reader.read_u16::<BigEndian>()?;
@ -104,7 +101,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for TkhdBox {
let volume = Ratio::new_raw(volume_numer, 0x100);
reader.read_u16::<BigEndian>()?; // reserved
let matrix = Matrix{
let matrix = Matrix {
a: reader.read_i32::<byteorder::LittleEndian>()?,
b: reader.read_i32::<BigEndian>()?,
u: reader.read_i32::<BigEndian>()?,

View file

@ -1,252 +1,13 @@
use std::io::{Seek, SeekFrom, Read, Write};
use std::io::{Read, Seek, SeekFrom, Write};
use crate::*;
use crate::atoms::*;
use crate::atoms::{
tkhd::TkhdBox,
edts::EdtsBox,
mdia::MdiaBox,
stbl::StblBox,
stts::SttsBox,
stsc::StscBox,
stsz::StszBox,
};
use crate::atoms::{edts::EdtsBox, mdia::MdiaBox, tkhd::TkhdBox};
#[derive(Debug, Default)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct TrakBox {
pub id: u32,
pub tkhd: Option<TkhdBox>,
pub tkhd: TkhdBox,
pub edts: Option<EdtsBox>,
pub mdia: Option<MdiaBox>,
}
impl TrakBox {
pub(crate) fn new() -> TrakBox {
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),
}))
}
pub mdia: MdiaBox,
}
impl Mp4Box for TrakBox {
@ -256,15 +17,11 @@ impl Mp4Box for TrakBox {
fn box_size(&self) -> u64 {
let mut size = HEADER_SIZE;
if let Some(ref tkhd) = self.tkhd {
size += tkhd.box_size();
}
size += self.tkhd.box_size();
if let Some(ref edts) = self.edts {
size += edts.box_size();
}
if let Some(ref mdia) = self.mdia {
size += mdia.box_size();
}
size += self.mdia.box_size();
size
}
}
@ -273,27 +30,26 @@ impl<R: Read + Seek> ReadBox<&mut R> for TrakBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = get_box_start(reader)?;
let mut trak = TrakBox::new();
let mut tkhd = None;
let mut edts = None;
let mut mdia = None;
let mut current = reader.seek(SeekFrom::Current(0))?;
let end = start + size;
while current < end {
// Get box header.
let header = BoxHeader::read(reader)?;
let BoxHeader{ name, size: s } = header;
let BoxHeader { name, size: s } = header;
match name {
BoxType::TkhdBox => {
let tkhd = TkhdBox::read_box(reader, s)?;
trak.tkhd = Some(tkhd);
tkhd = Some(TkhdBox::read_box(reader, s)?);
}
BoxType::EdtsBox => {
let edts = EdtsBox::read_box(reader, s)?;
trak.edts = Some(edts);
edts = Some(EdtsBox::read_box(reader, s)?);
}
BoxType::MdiaBox => {
let mdia = MdiaBox::read_box(reader, s)?;
trak.mdia = Some(mdia);
mdia = Some(MdiaBox::read_box(reader, s)?);
}
_ => {
// XXX warn!()
@ -304,9 +60,20 @@ impl<R: Read + Seek> ReadBox<&mut R> for TrakBox {
current = reader.seek(SeekFrom::Current(0))?;
}
if tkhd.is_none() {
return Err(Error::BoxNotFound(BoxType::TkhdBox));
}
if mdia.is_none() {
return Err(Error::BoxNotFound(BoxType::MdiaBox));
}
skip_read_to(reader, start + size)?;
Ok(trak)
Ok(TrakBox {
tkhd: tkhd.unwrap(),
edts,
mdia: mdia.unwrap(),
})
}
}
@ -315,15 +82,11 @@ impl<W: Write> WriteBox<&mut W> for TrakBox {
let size = self.box_size();
BoxHeader::new(Self::box_type(), size).write(writer)?;
if let Some(ref tkhd) = self.tkhd {
tkhd.write_box(writer)?;
}
self.tkhd.write_box(writer)?;
if let Some(ref edts) = self.edts {
edts.write_box(writer)?;
}
if let Some(ref mdia) = self.mdia {
mdia.write_box(writer)?;
}
self.mdia.write_box(writer)?;
Ok(size)
}

View file

@ -1,11 +1,9 @@
use std::io::{Seek, Read, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::io::{Read, Seek, Write};
use crate::*;
use crate::atoms::*;
#[derive(Debug, Default, PartialEq)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct VmhdBox {
pub version: u8,
pub flags: u32,
@ -13,7 +11,7 @@ pub struct VmhdBox {
pub op_color: RgbColor,
}
#[derive(Debug, Default, PartialEq)]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct RgbColor {
pub red: u16,
pub green: u16,
@ -70,7 +68,6 @@ impl<W: Write> WriteBox<&mut W> for VmhdBox {
}
}
#[cfg(test)]
mod tests {
use super::*;
@ -83,7 +80,11 @@ mod tests {
version: 0,
flags: 1,
graphics_mode: 0,
op_color: RgbColor { red: 0, green: 0, blue: 0},
op_color: RgbColor {
red: 0,
green: 0,
blue: 0,
},
};
let mut buf = Vec::new();
src_box.write_box(&mut buf).unwrap();

View file

@ -10,6 +10,8 @@ pub enum Error {
InvalidData(&'static str),
#[error("{0} not found")]
BoxNotFound(BoxType),
#[error("{0} and {1} not found")]
Box2NotFound(BoxType, BoxType),
#[error("trak[{0}] not found")]
TrakNotFound(u32),
#[error("trak[{0}].{1} not found")]

View file

@ -1,5 +1,5 @@
use std::fmt;
use std::convert::TryInto;
use std::fmt;
pub use bytes::Bytes;
@ -42,9 +42,14 @@ impl PartialEq for Mp4Sample {
impl fmt::Display for Mp4Sample {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f,
"start_time {}, duration {}, rendering_offset {}, is_sync {}, length {}",
self.start_time, self.duration, self.rendering_offset, self.is_sync,
self.bytes.len())
write!(
f,
"start_time {}, duration {}, rendering_offset {}, is_sync {}, length {}",
self.start_time,
self.duration,
self.rendering_offset,
self.is_sync,
self.bytes.len()
)
}
}

View file

@ -1,7 +1,8 @@
use std::io::{Seek, SeekFrom, Read};
use std::io::{Read, Seek, SeekFrom};
use crate::{Result, Error, Mp4Sample};
use crate::atoms::*;
use crate::atoms::{mvhd::MvhdBox, stbl::StblBox, trak::TrakBox};
use crate::{Bytes, Error, Mp4Sample, Result};
#[derive(Debug)]
pub struct Mp4Reader<R> {
@ -9,6 +10,8 @@ pub struct Mp4Reader<R> {
pub ftyp: FtypBox,
pub moov: Option<MoovBox>,
size: u64,
tracks: Vec<TrackReader>,
}
impl<R: Read + Seek> Mp4Reader<R> {
@ -18,6 +21,7 @@ impl<R: Read + Seek> Mp4Reader<R> {
ftyp: FtypBox::default(),
moov: None,
size: 0,
tracks: Vec::new(),
}
}
@ -31,7 +35,7 @@ impl<R: Read + Seek> Mp4Reader<R> {
while current < size {
// Get box header.
let header = BoxHeader::read(&mut self.reader)?;
let BoxHeader{ name, size: s } = header;
let BoxHeader { name, size: s } = header;
// Match and parse the atom boxes.
match name {
@ -59,59 +63,288 @@ impl<R: Read + Seek> Mp4Reader<R> {
}
current = self.reader.seek(SeekFrom::Current(0))?;
}
if let Some(ref moov) = self.moov {
for (i, trak) in moov.traks.iter().enumerate() {
self.tracks.push(TrackReader::new(i as u32 + 1, trak));
}
}
self.size = current - start;
Ok(())
}
pub fn track_count(&self) -> Result<u32> {
pub fn major_brand(&self) -> &FourCC {
&self.ftyp.major_brand
}
pub fn minor_version(&self) -> u32 {
self.ftyp.minor_version
}
pub fn compatible_brands(&self) -> &[FourCC] {
&self.ftyp.compatible_brands
}
fn mvhd(&self) -> Result<&MvhdBox> {
if let Some(ref moov) = self.moov {
Ok(moov.traks.len() as u32)
Ok(&moov.mvhd)
} else {
Err(Error::BoxNotFound(MoovBox::box_type()))
Err(Error::BoxNotFound(BoxType::VmhdBox))
}
}
pub fn duration(&self) -> Result<u64> {
let mvhd = self.mvhd()?;
Ok(mvhd.duration)
}
pub fn timescale(&self) -> Result<u32> {
let mvhd = self.mvhd()?;
Ok(mvhd.timescale)
}
pub fn track_count(&self) -> u32 {
self.tracks.len() as u32
}
pub fn tracks(&self) -> &[TrackReader] {
&self.tracks
}
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
if let Some(track) = self.tracks.get(track_id as usize - 1) {
Ok(track.sample_count())
} 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.sample_count()
Err(Error::TrakNotFound(track_id))
}
}
pub fn read_sample(
&mut self,
track_id: u32,
sample_id: u32,
) -> Result<Option<Mp4Sample>> {
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
if let Some(track) = self.tracks.get(track_id as usize - 1) {
track.read_sample(&mut self.reader, sample_id)
} 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)
Err(Error::TrakNotFound(track_id))
}
}
}
#[derive(Debug)]
pub struct TrackReader {
track_id: u32,
trak: TrakBox,
}
impl TrackReader {
pub(crate) fn new(track_id: u32, trak: &TrakBox) -> Self {
let trak = trak.clone();
Self { track_id, trak }
}
pub fn track_id(&self) -> u32 {
self.track_id
}
fn stbl(&self) -> &StblBox {
&self.trak.mdia.minf.stbl
}
fn stsc_index(&self, sample_id: u32) -> usize {
let stsc = &self.stbl().stsc;
for (i, entry) in stsc.entries.iter().enumerate() {
if sample_id < entry.first_sample {
assert_ne!(i, 0);
return i - 1;
}
}
assert_ne!(stsc.entries.len(), 0);
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.track_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.track_id,
BoxType::Co64Box,
chunk_id,
));
}
}
}
assert!(stbl.stco.is_some() || stbl.co64.is_some());
return Err(Error::Box2NotFound(BoxType::StcoBox, BoxType::Co64Box));
}
fn ctts_index(&self, sample_id: u32) -> Result<(usize, u32)> {
let stbl = self.stbl();
assert!(stbl.ctts.is_some());
let ctts = if let Some(ref ctts) = stbl.ctts {
ctts
} else {
return Err(Error::BoxInStblNotFound(self.track_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.track_id,
BoxType::CttsBox,
sample_id,
));
}
pub fn sample_count(&self) -> u32 {
let stsz = &self.stbl().stsz;
stsz.sample_sizes.len() as u32
}
pub fn sample_size(&self, sample_id: u32) -> Result<u32> {
let stsz = &self.stbl().stsz;
if stsz.sample_size > 0 {
return Ok(stsz.sample_size);
}
if let Some(size) = stsz.sample_sizes.get(sample_id as usize - 1) {
Ok(*size)
} else {
return Err(Error::EntryInStblNotFound(
self.track_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.stbl().stsc;
let stsc_entry = stsc.entries.get(stsc_index).unwrap();
let first_chunk = stsc_entry.first_chunk;
let first_sample = stsc_entry.first_sample;
let samples_per_chunk = stsc_entry.samples_per_chunk;
let chunk_id = 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.stbl().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.track_id,
BoxType::SttsBox,
sample_id,
));
}
pub fn sample_rendering_offset(&self, sample_id: u32) -> i32 {
let stbl = self.stbl();
if let Some(ref ctts) = stbl.ctts {
if let Ok((ctts_index, _)) = self.ctts_index(sample_id) {
let ctts_entry = ctts.entries.get(ctts_index).unwrap();
return ctts_entry.sample_offset;
}
}
0
}
pub fn is_sync_sample(&self, sample_id: u32) -> bool {
let stbl = self.stbl();
if let Some(ref stss) = stbl.stss {
match stss.entries.binary_search(&sample_id) {
Ok(_) => true,
Err(_) => false,
}
} else {
true
}
}
pub fn read_sample<R: Read + Seek>(
&self,
reader: &mut R,
sample_id: u32,
) -> Result<Option<Mp4Sample>> {
let sample_size = match self.sample_size(sample_id) {
Ok(size) => size,
Err(Error::EntryInStblNotFound(_, _, _)) => return Ok(None),
Err(err) => return Err(err),
};
let sample_offset = self.sample_offset(sample_id).unwrap(); // XXX
let mut buffer = vec![0x0u8; sample_size as usize];
reader.seek(SeekFrom::Start(sample_offset))?;
reader.read_exact(&mut buffer)?;
let (start_time, duration) = self.sample_time(sample_id).unwrap(); // XXX
let rendering_offset = self.sample_rendering_offset(sample_id);
let is_sync = self.is_sync_sample(sample_id);
Ok(Some(Mp4Sample {
start_time,
duration,
rendering_offset,
is_sync,
bytes: Bytes::from(buffer),
}))
}
}

View file

@ -2,7 +2,6 @@ use mp4;
use std::fs::File;
use std::io::BufReader;
#[test]
fn test_read_mp4() {
let filename = "tests/samples/minimal.mp4";
@ -16,31 +15,24 @@ fn test_read_mp4() {
assert_eq!(2591, mp4.size());
// ftyp.
println!("{:?}", mp4.ftyp.compatible_brands);
assert_eq!(4, mp4.ftyp.compatible_brands.len());
assert_eq!(4, mp4.compatible_brands().len());
// Check compatible_brands.
let brands = vec![
String::from("isom"),
String::from("iso2"),
String::from("avc1"),
String::from("mp41")
String::from("mp41"),
];
for b in brands {
let t = mp4.ftyp.compatible_brands.iter().any(|x| x.to_string() == b);
let t = mp4.compatible_brands().iter().any(|x| x.to_string() == b);
assert_eq!(t, true);
}
// moov.
assert!(mp4.moov.is_some());
if let Some(ref moov) = mp4.moov {
assert_eq!(moov.mvhd.version, 0);
assert_eq!(moov.mvhd.creation_time, 0);
assert_eq!(moov.mvhd.duration, 62);
assert_eq!(moov.mvhd.timescale, 1000);
assert_eq!(moov.traks.len(), 2);
}
assert_eq!(mp4.duration().unwrap(), 62);
assert_eq!(mp4.timescale().unwrap(), 1000);
assert_eq!(mp4.track_count(), 2);
let sample_count = mp4.sample_count(1).unwrap();
assert_eq!(sample_count, 0);
@ -49,31 +41,40 @@ fn test_read_mp4() {
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]),
});
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]),
});
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]),
});
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());