mirror of
https://github.com/alfg/mp4-rust.git
synced 2024-06-02 13:39:54 +00:00
Merge b3ba12433e
into 35560e94f5
This commit is contained in:
commit
313f1b23eb
|
@ -56,7 +56,9 @@ fn get_boxes(file: File) -> Result<Vec<Box>> {
|
||||||
if let Some(mehd) = &mvex.mehd {
|
if let Some(mehd) = &mvex.mehd {
|
||||||
boxes.push(build_box(mehd));
|
boxes.push(build_box(mehd));
|
||||||
}
|
}
|
||||||
boxes.push(build_box(&mvex.trex));
|
for trex in &mvex.trex {
|
||||||
|
boxes.push(build_box(trex));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// trak.
|
// trak.
|
||||||
|
|
|
@ -85,6 +85,7 @@ pub(crate) mod moov;
|
||||||
pub(crate) mod mp4a;
|
pub(crate) mod mp4a;
|
||||||
pub(crate) mod mvex;
|
pub(crate) mod mvex;
|
||||||
pub(crate) mod mvhd;
|
pub(crate) mod mvhd;
|
||||||
|
pub(crate) mod opus;
|
||||||
pub(crate) mod smhd;
|
pub(crate) mod smhd;
|
||||||
pub(crate) mod stbl;
|
pub(crate) mod stbl;
|
||||||
pub(crate) mod stco;
|
pub(crate) mod stco;
|
||||||
|
@ -110,7 +111,7 @@ pub use avc1::Avc1Box;
|
||||||
pub use co64::Co64Box;
|
pub use co64::Co64Box;
|
||||||
pub use ctts::CttsBox;
|
pub use ctts::CttsBox;
|
||||||
pub use data::DataBox;
|
pub use data::DataBox;
|
||||||
pub use dinf::DinfBox;
|
pub use dinf::*;
|
||||||
pub use edts::EdtsBox;
|
pub use edts::EdtsBox;
|
||||||
pub use elst::ElstBox;
|
pub use elst::ElstBox;
|
||||||
pub use emsg::EmsgBox;
|
pub use emsg::EmsgBox;
|
||||||
|
@ -129,6 +130,7 @@ pub use moov::MoovBox;
|
||||||
pub use mp4a::Mp4aBox;
|
pub use mp4a::Mp4aBox;
|
||||||
pub use mvex::MvexBox;
|
pub use mvex::MvexBox;
|
||||||
pub use mvhd::MvhdBox;
|
pub use mvhd::MvhdBox;
|
||||||
|
pub use opus::*;
|
||||||
pub use smhd::SmhdBox;
|
pub use smhd::SmhdBox;
|
||||||
pub use stbl::StblBox;
|
pub use stbl::StblBox;
|
||||||
pub use stco::StcoBox;
|
pub use stco::StcoBox;
|
||||||
|
@ -139,7 +141,7 @@ pub use stsz::StszBox;
|
||||||
pub use stts::SttsBox;
|
pub use stts::SttsBox;
|
||||||
pub use tfdt::TfdtBox;
|
pub use tfdt::TfdtBox;
|
||||||
pub use tfhd::TfhdBox;
|
pub use tfhd::TfhdBox;
|
||||||
pub use tkhd::TkhdBox;
|
pub use tkhd::*;
|
||||||
pub use traf::TrafBox;
|
pub use traf::TrafBox;
|
||||||
pub use trak::TrakBox;
|
pub use trak::TrakBox;
|
||||||
pub use trex::TrexBox;
|
pub use trex::TrexBox;
|
||||||
|
@ -238,7 +240,9 @@ boxtype! {
|
||||||
CovrBox => 0x636f7672,
|
CovrBox => 0x636f7672,
|
||||||
DescBox => 0x64657363,
|
DescBox => 0x64657363,
|
||||||
WideBox => 0x77696465,
|
WideBox => 0x77696465,
|
||||||
WaveBox => 0x77617665
|
WaveBox => 0x77617665,
|
||||||
|
DopsBox => 0x644F7073,
|
||||||
|
OpusBox => 0x4F707573
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Mp4Box: Sized {
|
pub trait Mp4Box: Sized {
|
||||||
|
|
|
@ -102,6 +102,6 @@ impl<W: Write> WriteBox<&mut W> for MoofBox {
|
||||||
for traf in self.trafs.iter() {
|
for traf in self.trafs.iter() {
|
||||||
traf.write_box(writer)?;
|
traf.write_box(writer)?;
|
||||||
}
|
}
|
||||||
Ok(0)
|
Ok(size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,15 +29,14 @@ impl MoovBox {
|
||||||
|
|
||||||
pub fn get_size(&self) -> u64 {
|
pub fn get_size(&self) -> u64 {
|
||||||
let mut size = HEADER_SIZE + self.mvhd.box_size();
|
let mut size = HEADER_SIZE + self.mvhd.box_size();
|
||||||
|
size += self.meta.as_ref().map(|x| x.box_size()).unwrap_or(0);
|
||||||
|
size += self.mvex.as_ref().map(|x| x.box_size()).unwrap_or(0);
|
||||||
|
|
||||||
for trak in self.traks.iter() {
|
for trak in self.traks.iter() {
|
||||||
size += trak.box_size();
|
size += trak.box_size();
|
||||||
}
|
}
|
||||||
if let Some(meta) = &self.meta {
|
|
||||||
size += meta.box_size();
|
size += self.udta.as_ref().map(|x| x.box_size()).unwrap_or(0);
|
||||||
}
|
|
||||||
if let Some(udta) = &self.udta {
|
|
||||||
size += udta.box_size();
|
|
||||||
}
|
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,16 +130,19 @@ impl<W: Write> WriteBox<&mut W> for MoovBox {
|
||||||
BoxHeader::new(self.box_type(), size).write(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() {
|
|
||||||
trak.write_box(writer)?;
|
|
||||||
}
|
|
||||||
if let Some(meta) = &self.meta {
|
if let Some(meta) = &self.meta {
|
||||||
meta.write_box(writer)?;
|
meta.write_box(writer)?;
|
||||||
}
|
}
|
||||||
|
if let Some(mvex) = &self.mvex {
|
||||||
|
mvex.write_box(writer)?;
|
||||||
|
}
|
||||||
|
for trak in self.traks.iter() {
|
||||||
|
trak.write_box(writer)?;
|
||||||
|
}
|
||||||
if let Some(udta) = &self.udta {
|
if let Some(udta) = &self.udta {
|
||||||
udta.write_box(writer)?;
|
udta.write_box(writer)?;
|
||||||
}
|
}
|
||||||
Ok(0)
|
Ok(size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,16 +7,17 @@ use crate::mp4box::{mehd::MehdBox, trex::TrexBox};
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
|
||||||
pub struct MvexBox {
|
pub struct MvexBox {
|
||||||
pub mehd: Option<MehdBox>,
|
pub mehd: Option<MehdBox>,
|
||||||
pub trex: TrexBox,
|
pub trex: Vec<TrexBox>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MvexBox {
|
impl MvexBox {
|
||||||
pub fn get_type(&self) -> BoxType {
|
pub fn get_type(&self) -> BoxType {
|
||||||
BoxType::MdiaBox
|
BoxType::MvexBox
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_size(&self) -> u64 {
|
pub fn get_size(&self) -> u64 {
|
||||||
HEADER_SIZE + self.mehd.as_ref().map(|x| x.box_size()).unwrap_or(0) + self.trex.box_size()
|
let trex_sizes = self.trex.iter().fold(0, |acc, x| acc + x.box_size());
|
||||||
|
HEADER_SIZE + self.mehd.as_ref().map(|x| x.box_size()).unwrap_or(0) + trex_sizes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +45,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for MvexBox {
|
||||||
let start = box_start(reader)?;
|
let start = box_start(reader)?;
|
||||||
|
|
||||||
let mut mehd = None;
|
let mut mehd = None;
|
||||||
let mut trex = None;
|
let mut trex: Vec<TrexBox> = Vec::new();
|
||||||
|
|
||||||
let mut current = reader.stream_position()?;
|
let mut current = reader.stream_position()?;
|
||||||
let end = start + size;
|
let end = start + size;
|
||||||
|
@ -63,7 +64,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for MvexBox {
|
||||||
mehd = Some(MehdBox::read_box(reader, s)?);
|
mehd = Some(MehdBox::read_box(reader, s)?);
|
||||||
}
|
}
|
||||||
BoxType::TrexBox => {
|
BoxType::TrexBox => {
|
||||||
trex = Some(TrexBox::read_box(reader, s)?);
|
trex.push(TrexBox::read_box(reader, s)?);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// XXX warn!()
|
// XXX warn!()
|
||||||
|
@ -74,16 +75,13 @@ impl<R: Read + Seek> ReadBox<&mut R> for MvexBox {
|
||||||
current = reader.stream_position()?;
|
current = reader.stream_position()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if trex.is_none() {
|
if trex.is_empty() {
|
||||||
return Err(Error::BoxNotFound(BoxType::TrexBox));
|
return Err(Error::BoxNotFound(BoxType::TrexBox));
|
||||||
}
|
}
|
||||||
|
|
||||||
skip_bytes_to(reader, start + size)?;
|
skip_bytes_to(reader, start + size)?;
|
||||||
|
|
||||||
Ok(MvexBox {
|
Ok(MvexBox { mehd, trex })
|
||||||
mehd,
|
|
||||||
trex: trex.unwrap(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +93,9 @@ impl<W: Write> WriteBox<&mut W> for MvexBox {
|
||||||
if let Some(mehd) = &self.mehd {
|
if let Some(mehd) = &self.mehd {
|
||||||
mehd.write_box(writer)?;
|
mehd.write_box(writer)?;
|
||||||
}
|
}
|
||||||
self.trex.write_box(writer)?;
|
for trex in &self.trex {
|
||||||
|
trex.write_box(writer)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(size)
|
Ok(size)
|
||||||
}
|
}
|
||||||
|
|
228
src/mp4box/opus.rs
Normal file
228
src/mp4box/opus.rs
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
use crate::mp4box::*;
|
||||||
|
use crate::Mp4Box;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
// taken from the following sources
|
||||||
|
// - https://opus-codec.org/docs/opus_in_isobmff.html
|
||||||
|
// - chromium source code: box_definitions.h - OpusSpecificBox
|
||||||
|
// - async-mp4 crate: https://github.com/Wicpar/async-mp4/blob/master/src/mp4box/dops.rs
|
||||||
|
|
||||||
|
// this OpusBox is a combination of the AudioSampleEntry box and OpusSpecificBox
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
||||||
|
pub struct OpusBox {
|
||||||
|
pub data_reference_index: u16,
|
||||||
|
pub channelcount: u16,
|
||||||
|
pub samplesize: u16,
|
||||||
|
|
||||||
|
#[serde(with = "value_u32")]
|
||||||
|
pub samplerate: FixedPointU16,
|
||||||
|
pub dops: DopsBox,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mp4Box for OpusBox {
|
||||||
|
fn box_type(&self) -> BoxType {
|
||||||
|
BoxType::OpusBox
|
||||||
|
}
|
||||||
|
|
||||||
|
fn box_size(&self) -> u64 {
|
||||||
|
// the +19 is for DopsBox
|
||||||
|
36 + 19
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_json(&self) -> Result<String> {
|
||||||
|
Ok(serde_json::to_string(&self).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn summary(&self) -> Result<String> {
|
||||||
|
Ok(format!("{self:?}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read + Seek> ReadBox<&mut R> for OpusBox {
|
||||||
|
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||||
|
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 channelcount = reader.read_u16::<BigEndian>()?;
|
||||||
|
let samplesize = reader.read_u16::<BigEndian>()?;
|
||||||
|
reader.read_u32::<BigEndian>()?; // reserved
|
||||||
|
let samplerate = reader.read_u32::<BigEndian>()?;
|
||||||
|
|
||||||
|
let num_read = 36;
|
||||||
|
if num_read > size {
|
||||||
|
return Err(Error::InvalidData("opus box is too small"));
|
||||||
|
}
|
||||||
|
let remaining = num_read.saturating_sub(size);
|
||||||
|
|
||||||
|
// todo: if the size of DopsBox becomes variable, then the size of
|
||||||
|
// OpusBox will be needed to determine what size to pass to DopsBox::read_box()
|
||||||
|
let dops = DopsBox::read_box(reader, remaining)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
data_reference_index,
|
||||||
|
channelcount,
|
||||||
|
samplesize,
|
||||||
|
samplerate: FixedPointU16::new_raw(samplerate),
|
||||||
|
dops,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: Write> WriteBox<&mut W> for OpusBox {
|
||||||
|
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||||
|
let mut written = 0;
|
||||||
|
written += BoxHeader::new(self.box_type(), self.box_size()).write(writer)?;
|
||||||
|
|
||||||
|
writer.write_u32::<BigEndian>(0)?; // reserved
|
||||||
|
written += 4;
|
||||||
|
writer.write_u16::<BigEndian>(0)?; // reserved
|
||||||
|
written += 2;
|
||||||
|
writer.write_u16::<BigEndian>(self.data_reference_index)?;
|
||||||
|
written += 2;
|
||||||
|
|
||||||
|
writer.write_u64::<BigEndian>(0)?; // reserved
|
||||||
|
written += 8;
|
||||||
|
writer.write_u16::<BigEndian>(self.channelcount)?;
|
||||||
|
written += 2;
|
||||||
|
writer.write_u16::<BigEndian>(self.samplesize)?;
|
||||||
|
written += 2;
|
||||||
|
writer.write_u32::<BigEndian>(0)?; // reserved
|
||||||
|
written += 4;
|
||||||
|
writer.write_u32::<BigEndian>(self.samplerate.raw_value())?;
|
||||||
|
written += 4;
|
||||||
|
|
||||||
|
written += self.dops.write_box(writer)?;
|
||||||
|
|
||||||
|
if written != self.box_size() {
|
||||||
|
Err(Error::InvalidData("written != box_size for OpusBox"))
|
||||||
|
} else {
|
||||||
|
Ok(written)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/Wicpar/async-mp4/blob/master/src/mp4box/dops.rs
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
||||||
|
pub struct DopsBox {
|
||||||
|
pub version: u8,
|
||||||
|
pub pre_skip: u16,
|
||||||
|
pub input_sample_rate: u32,
|
||||||
|
pub output_gain: i16,
|
||||||
|
pub channel_mapping_family: ChannelMappingFamily,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mp4Box for DopsBox {
|
||||||
|
fn box_type(&self) -> BoxType {
|
||||||
|
BoxType::DopsBox
|
||||||
|
}
|
||||||
|
|
||||||
|
fn box_size(&self) -> u64 {
|
||||||
|
// if channel_mapping_family is updates to support more than 2 channels,
|
||||||
|
// box_size could change, depending on the channel mapping.
|
||||||
|
19
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_json(&self) -> Result<String> {
|
||||||
|
Ok(serde_json::to_string(&self).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn summary(&self) -> Result<String> {
|
||||||
|
Ok(format!("{self:?}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read + Seek> ReadBox<&mut R> for DopsBox {
|
||||||
|
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||||
|
if size < 19 {
|
||||||
|
return Err(Error::InvalidData("Dops box size too small"));
|
||||||
|
}
|
||||||
|
box_start(reader)?;
|
||||||
|
let version = reader.read_u8()?;
|
||||||
|
let num_channels = reader.read_u8()?;
|
||||||
|
let pre_skip = reader.read_u16::<BigEndian>()?;
|
||||||
|
let input_sample_rate = reader.read_u32::<BigEndian>()?;
|
||||||
|
let output_gain = reader.read_i16::<BigEndian>()?;
|
||||||
|
|
||||||
|
// todo: should this be used?
|
||||||
|
let _channel_mapping_family = reader.read_u8()?;
|
||||||
|
|
||||||
|
Ok(DopsBox {
|
||||||
|
version,
|
||||||
|
pre_skip,
|
||||||
|
input_sample_rate,
|
||||||
|
output_gain,
|
||||||
|
channel_mapping_family: ChannelMappingFamily::Family0 {
|
||||||
|
stereo: num_channels == 2,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/Wicpar/async-mp4/blob/master/src/mp4box/dops.rs
|
||||||
|
impl<W: Write> WriteBox<&mut W> for DopsBox {
|
||||||
|
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||||
|
let mut written = 0;
|
||||||
|
written += BoxHeader::new(self.box_type(), self.box_size()).write(writer)?;
|
||||||
|
writer.write_u8(self.version)?;
|
||||||
|
written += 1;
|
||||||
|
|
||||||
|
let num_channels = match self.channel_mapping_family {
|
||||||
|
ChannelMappingFamily::Family0 { stereo } => match stereo {
|
||||||
|
true => 2,
|
||||||
|
false => 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
writer.write_u8(num_channels)?;
|
||||||
|
written += 1;
|
||||||
|
writer.write_u16::<BigEndian>(self.pre_skip)?;
|
||||||
|
written += 2;
|
||||||
|
writer.write_u32::<BigEndian>(self.input_sample_rate)?;
|
||||||
|
written += 4;
|
||||||
|
writer.write_i16::<BigEndian>(self.output_gain)?;
|
||||||
|
written += 2;
|
||||||
|
|
||||||
|
// channel mapping family 0
|
||||||
|
writer.write_u8(0)?;
|
||||||
|
written += 1;
|
||||||
|
|
||||||
|
// todo: StreamCount? CoupledCount? ChannelMapping?
|
||||||
|
|
||||||
|
assert_eq!(written, self.box_size());
|
||||||
|
Ok(written)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
||||||
|
pub enum ChannelMappingFamily {
|
||||||
|
Family0 { stereo: bool },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_opus_writer() {
|
||||||
|
let dops = DopsBox {
|
||||||
|
version: 0,
|
||||||
|
pre_skip: 1,
|
||||||
|
input_sample_rate: 2,
|
||||||
|
output_gain: 3,
|
||||||
|
channel_mapping_family: ChannelMappingFamily::Family0 { stereo: false },
|
||||||
|
};
|
||||||
|
|
||||||
|
let opus = OpusBox {
|
||||||
|
data_reference_index: 1,
|
||||||
|
channelcount: 1,
|
||||||
|
samplesize: 2,
|
||||||
|
samplerate: FixedPointU16::new(48000),
|
||||||
|
dops,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut buffer = Vec::<u8>::new();
|
||||||
|
opus.write_box(&mut buffer).expect("write_box failed");
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,8 +25,21 @@ pub struct StsdBox {
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub tx3g: Option<Tx3gBox>,
|
pub tx3g: Option<Tx3gBox>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub opus: Option<OpusBox>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo: all the Options in StsdBox may be better as an enumeration
|
||||||
|
// pub enum StsdChildBox {
|
||||||
|
// Avc1(Avc1Box),
|
||||||
|
// Hev1(Hev1Box),
|
||||||
|
// Vp09(Vp09Box),
|
||||||
|
// Mp4a(Mp4aBox),
|
||||||
|
// Tx3g(Tx3gBox),
|
||||||
|
// Opus(OpusBox),
|
||||||
|
// }
|
||||||
|
|
||||||
impl StsdBox {
|
impl StsdBox {
|
||||||
pub fn get_type(&self) -> BoxType {
|
pub fn get_type(&self) -> BoxType {
|
||||||
BoxType::StsdBox
|
BoxType::StsdBox
|
||||||
|
@ -44,6 +57,8 @@ impl StsdBox {
|
||||||
size += mp4a.box_size();
|
size += mp4a.box_size();
|
||||||
} else if let Some(ref tx3g) = self.tx3g {
|
} else if let Some(ref tx3g) = self.tx3g {
|
||||||
size += tx3g.box_size();
|
size += tx3g.box_size();
|
||||||
|
} else if let Some(ref opus) = self.opus {
|
||||||
|
size += opus.box_size();
|
||||||
}
|
}
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
|
@ -81,6 +96,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for StsdBox {
|
||||||
let mut vp09 = None;
|
let mut vp09 = None;
|
||||||
let mut mp4a = None;
|
let mut mp4a = None;
|
||||||
let mut tx3g = None;
|
let mut tx3g = None;
|
||||||
|
let mut opus = None;
|
||||||
|
|
||||||
// Get box header.
|
// Get box header.
|
||||||
let header = BoxHeader::read(reader)?;
|
let header = BoxHeader::read(reader)?;
|
||||||
|
@ -107,6 +123,9 @@ impl<R: Read + Seek> ReadBox<&mut R> for StsdBox {
|
||||||
BoxType::Tx3gBox => {
|
BoxType::Tx3gBox => {
|
||||||
tx3g = Some(Tx3gBox::read_box(reader, s)?);
|
tx3g = Some(Tx3gBox::read_box(reader, s)?);
|
||||||
}
|
}
|
||||||
|
BoxType::OpusBox => {
|
||||||
|
opus = Some(OpusBox::read_box(reader, s)?);
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,6 +139,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for StsdBox {
|
||||||
vp09,
|
vp09,
|
||||||
mp4a,
|
mp4a,
|
||||||
tx3g,
|
tx3g,
|
||||||
|
opus,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,6 +163,8 @@ impl<W: Write> WriteBox<&mut W> for StsdBox {
|
||||||
mp4a.write_box(writer)?;
|
mp4a.write_box(writer)?;
|
||||||
} else if let Some(ref tx3g) = self.tx3g {
|
} else if let Some(ref tx3g) = self.tx3g {
|
||||||
tx3g.write_box(writer)?;
|
tx3g.write_box(writer)?;
|
||||||
|
} else if let Some(ref opus) = self.opus {
|
||||||
|
opus.write_box(writer)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(size)
|
Ok(size)
|
||||||
|
|
|
@ -19,12 +19,8 @@ impl TrafBox {
|
||||||
pub fn get_size(&self) -> u64 {
|
pub fn get_size(&self) -> u64 {
|
||||||
let mut size = HEADER_SIZE;
|
let mut size = HEADER_SIZE;
|
||||||
size += self.tfhd.box_size();
|
size += self.tfhd.box_size();
|
||||||
if let Some(ref tfdt) = self.tfdt {
|
size += self.tfdt.as_ref().map(|x| x.box_size()).unwrap_or(0);
|
||||||
size += tfdt.box_size();
|
size += self.trun.as_ref().map(|x| x.box_size()).unwrap_or(0);
|
||||||
}
|
|
||||||
if let Some(ref trun) = self.trun {
|
|
||||||
size += trun.box_size();
|
|
||||||
}
|
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,17 +44,18 @@ impl TrunBox {
|
||||||
sum += 4;
|
sum += 4;
|
||||||
}
|
}
|
||||||
if TrunBox::FLAG_SAMPLE_DURATION & self.flags > 0 {
|
if TrunBox::FLAG_SAMPLE_DURATION & self.flags > 0 {
|
||||||
sum += 4 * self.sample_count as u64;
|
sum += (4 * self.sample_durations.len()) as u64;
|
||||||
}
|
}
|
||||||
if TrunBox::FLAG_SAMPLE_SIZE & self.flags > 0 {
|
if TrunBox::FLAG_SAMPLE_SIZE & self.flags > 0 {
|
||||||
sum += 4 * self.sample_count as u64;
|
sum += (4 * self.sample_sizes.len()) as u64;
|
||||||
}
|
}
|
||||||
if TrunBox::FLAG_SAMPLE_FLAGS & self.flags > 0 {
|
if TrunBox::FLAG_SAMPLE_FLAGS & self.flags > 0 {
|
||||||
sum += 4 * self.sample_count as u64;
|
sum += (4 * self.sample_flags.len()) as u64;
|
||||||
}
|
}
|
||||||
if TrunBox::FLAG_SAMPLE_CTS & self.flags > 0 {
|
if TrunBox::FLAG_SAMPLE_CTS & self.flags > 0 {
|
||||||
sum += 4 * self.sample_count as u64;
|
sum += (4 * self.sample_cts.len()) as u64;
|
||||||
}
|
}
|
||||||
|
|
||||||
sum
|
sum
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -185,9 +186,7 @@ impl<W: Write> WriteBox<&mut W> for TrunBox {
|
||||||
if let Some(v) = self.first_sample_flags {
|
if let Some(v) = self.first_sample_flags {
|
||||||
writer.write_u32::<BigEndian>(v)?;
|
writer.write_u32::<BigEndian>(v)?;
|
||||||
}
|
}
|
||||||
if self.sample_count != self.sample_sizes.len() as u32 {
|
|
||||||
return Err(Error::InvalidData("sample count out of sync"));
|
|
||||||
}
|
|
||||||
for i in 0..self.sample_count as usize {
|
for i in 0..self.sample_count as usize {
|
||||||
if TrunBox::FLAG_SAMPLE_DURATION & self.flags > 0 {
|
if TrunBox::FLAG_SAMPLE_DURATION & self.flags > 0 {
|
||||||
writer.write_u32::<BigEndian>(self.sample_durations[i])?;
|
writer.write_u32::<BigEndian>(self.sample_durations[i])?;
|
||||||
|
|
|
@ -97,18 +97,21 @@ impl<R: Read + Seek> Mp4Reader<R> {
|
||||||
|
|
||||||
// Update tracks if any fragmented (moof) boxes are found.
|
// Update tracks if any fragmented (moof) boxes are found.
|
||||||
if !moofs.is_empty() {
|
if !moofs.is_empty() {
|
||||||
let mut default_sample_duration = 0;
|
|
||||||
if let Some(ref moov) = moov {
|
|
||||||
if let Some(ref mvex) = &moov.mvex {
|
|
||||||
default_sample_duration = mvex.trex.default_sample_duration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (moof, moof_offset) in moofs.iter().zip(moof_offsets) {
|
for (moof, moof_offset) in moofs.iter().zip(moof_offsets) {
|
||||||
for traf in moof.trafs.iter() {
|
for traf in moof.trafs.iter() {
|
||||||
let track_id = traf.tfhd.track_id;
|
let track_id = traf.tfhd.track_id;
|
||||||
if let Some(track) = tracks.get_mut(&track_id) {
|
if let Some(track) = tracks.get_mut(&track_id) {
|
||||||
track.default_sample_duration = default_sample_duration;
|
track.default_sample_duration = moov
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|moov| {
|
||||||
|
moov.mvex.as_ref().and_then(|mvex| {
|
||||||
|
mvex.trex
|
||||||
|
.iter()
|
||||||
|
.find(|trex| trex.track_id == track.track_id())
|
||||||
|
.map(|trex| trex.default_sample_duration)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.unwrap_or(0);
|
||||||
track.moof_offsets.push(moof_offset);
|
track.moof_offsets.push(moof_offset);
|
||||||
track.trafs.push(traf.clone())
|
track.trafs.push(traf.clone())
|
||||||
} else {
|
} else {
|
||||||
|
@ -186,16 +189,21 @@ impl<R: Read + Seek> Mp4Reader<R> {
|
||||||
.map(|trak| (trak.tkhd.track_id, Mp4Track::from(trak)))
|
.map(|trak| (trak.tkhd.track_id, Mp4Track::from(trak)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut default_sample_duration = 0;
|
|
||||||
if let Some(ref mvex) = &self.moov.mvex {
|
|
||||||
default_sample_duration = mvex.trex.default_sample_duration
|
|
||||||
}
|
|
||||||
|
|
||||||
for (moof, moof_offset) in moofs.iter().zip(moof_offsets) {
|
for (moof, moof_offset) in moofs.iter().zip(moof_offsets) {
|
||||||
for traf in moof.trafs.iter() {
|
for traf in moof.trafs.iter() {
|
||||||
let track_id = traf.tfhd.track_id;
|
let track_id = traf.tfhd.track_id;
|
||||||
if let Some(track) = tracks.get_mut(&track_id) {
|
if let Some(track) = tracks.get_mut(&track_id) {
|
||||||
track.default_sample_duration = default_sample_duration;
|
track.default_sample_duration = self
|
||||||
|
.moov
|
||||||
|
.mvex
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|mvex| {
|
||||||
|
mvex.trex
|
||||||
|
.iter()
|
||||||
|
.find(|trex| trex.track_id == track.track_id())
|
||||||
|
.map(|trex| trex.default_sample_duration)
|
||||||
|
})
|
||||||
|
.unwrap_or(0);
|
||||||
track.moof_offsets.push(moof_offset);
|
track.moof_offsets.push(moof_offset);
|
||||||
track.trafs.push(traf.clone())
|
track.trafs.push(traf.clone())
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in a new issue