mirror of
https://github.com/alfg/mp4-rust.git
synced 2025-01-21 17:18:06 +00:00
Add boxes needed for common encryption
This commit is contained in:
parent
35560e94f5
commit
943c012d2c
16 changed files with 1387 additions and 2 deletions
|
@ -99,6 +99,27 @@ fn get_boxes(file: File) -> Result<Vec<Box>> {
|
|||
if let Some(ref mp4a) = &stbl.stsd.mp4a {
|
||||
boxes.push(build_box(mp4a));
|
||||
}
|
||||
let mut sinf = None;
|
||||
if let Some(ref encv) = &stbl.stsd.encv {
|
||||
boxes.push(build_box(encv));
|
||||
sinf = Some(&encv.sinf)
|
||||
}
|
||||
if let Some(ref enca) = &stbl.stsd.enca {
|
||||
boxes.push(build_box(enca));
|
||||
sinf = Some(&enca.sinf)
|
||||
}
|
||||
if let Some(sinf) = sinf {
|
||||
boxes.push(build_box(sinf));
|
||||
if let Some(ref schm) = sinf.schm {
|
||||
boxes.push(build_box(schm));
|
||||
}
|
||||
if let Some(ref schi) = sinf.schi {
|
||||
boxes.push(build_box(schi));
|
||||
if let Some(ref tenc) = schi.tenc {
|
||||
boxes.push(build_box(tenc));
|
||||
}
|
||||
}
|
||||
}
|
||||
boxes.push(build_box(&stbl.stts));
|
||||
if let Some(ref ctts) = &stbl.ctts {
|
||||
boxes.push(build_box(ctts));
|
||||
|
|
131
src/mp4box/enca.rs
Normal file
131
src/mp4box/enca.rs
Normal file
|
@ -0,0 +1,131 @@
|
|||
use serde::Serialize;
|
||||
use std::io::{Read, Seek, Write};
|
||||
|
||||
use crate::mp4box::*;
|
||||
|
||||
const RESERVED_DATA_SIZE: u64 = 28;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
|
||||
pub struct EncaBox {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub mp4a: Option<Mp4aBox>,
|
||||
|
||||
pub sinf: SinfBox,
|
||||
}
|
||||
|
||||
impl EncaBox {
|
||||
pub fn get_type(&self) -> BoxType {
|
||||
BoxType::EncaBox
|
||||
}
|
||||
|
||||
pub fn get_size(&self) -> u64 {
|
||||
let mut size = 0;
|
||||
if let Some(ref mp4a) = self.mp4a {
|
||||
// HEADER_SIZE intentionally omitted
|
||||
size += mp4a.box_size();
|
||||
} else {
|
||||
size += HEADER_SIZE + RESERVED_DATA_SIZE;
|
||||
}
|
||||
size += self.sinf.box_size();
|
||||
size
|
||||
}
|
||||
}
|
||||
|
||||
impl Mp4Box for EncaBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
}
|
||||
|
||||
fn to_json(&self) -> Result<String> {
|
||||
Ok(serde_json::to_string(&self).unwrap())
|
||||
}
|
||||
|
||||
fn summary(&self) -> Result<String> {
|
||||
let child_summary = if let Some(ref mp4a) = self.mp4a {
|
||||
mp4a.summary()
|
||||
} else {
|
||||
Err(Error::InvalidData(""))
|
||||
};
|
||||
let mut s = format!("original_format={}", &self.sinf.frma.original_format);
|
||||
if let Ok(summary) = child_summary {
|
||||
s.push(' ');
|
||||
s.push_str(&summary);
|
||||
}
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for EncaBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
|
||||
let mut mp4a = None;
|
||||
let mut sinf = None;
|
||||
|
||||
// skip current container items
|
||||
skip_bytes(reader, RESERVED_DATA_SIZE)?;
|
||||
|
||||
let mut current = reader.stream_position()?;
|
||||
let end = start + size;
|
||||
while current < end {
|
||||
// Get box header.
|
||||
let header = BoxHeader::read(reader)?;
|
||||
let BoxHeader { name, size: s } = header;
|
||||
if s > size {
|
||||
return Err(Error::InvalidData(
|
||||
"enca box contains a box with a larger size than it",
|
||||
));
|
||||
}
|
||||
|
||||
match name {
|
||||
BoxType::SinfBox => {
|
||||
sinf = Some(SinfBox::read_box(reader, s)?);
|
||||
break;
|
||||
}
|
||||
_ => {
|
||||
skip_box(reader, s)?;
|
||||
}
|
||||
}
|
||||
|
||||
current = reader.stream_position()?;
|
||||
}
|
||||
|
||||
let sinf = sinf.ok_or(Error::BoxNotFound(BoxType::SinfBox))?;
|
||||
|
||||
reader.seek(SeekFrom::Start(start + HEADER_SIZE))?;
|
||||
|
||||
let original_format: BoxType = sinf.frma.original_format.into();
|
||||
if original_format == BoxType::Mp4aBox {
|
||||
mp4a = Some(Mp4aBox::read_box(reader, size)?);
|
||||
}
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
|
||||
Ok(EncaBox { mp4a, sinf })
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for EncaBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
|
||||
if let Some(ref mp4a) = self.mp4a {
|
||||
// the enca box header is used, so the header from this box
|
||||
// must be removed
|
||||
let mut buf = Vec::with_capacity(mp4a.box_size() as usize);
|
||||
mp4a.write_box(&mut buf)?;
|
||||
writer.write_all(&buf[HEADER_SIZE as usize..])?;
|
||||
} else {
|
||||
writer.write_all(&[0; RESERVED_DATA_SIZE as usize])?;
|
||||
}
|
||||
|
||||
self.sinf.write_box(writer)?;
|
||||
|
||||
Ok(size)
|
||||
}
|
||||
}
|
175
src/mp4box/encv.rs
Normal file
175
src/mp4box/encv.rs
Normal file
|
@ -0,0 +1,175 @@
|
|||
use serde::Serialize;
|
||||
use std::io::{Read, Seek, Write};
|
||||
|
||||
use crate::mp4box::*;
|
||||
|
||||
const RESERVED_DATA_SIZE: u64 = 78;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
|
||||
pub struct EncvBox {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub avc1: Option<Avc1Box>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub hev1: Option<Hev1Box>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub vp09: Option<Vp09Box>,
|
||||
|
||||
pub sinf: SinfBox,
|
||||
}
|
||||
|
||||
impl EncvBox {
|
||||
pub fn get_type(&self) -> BoxType {
|
||||
BoxType::EncvBox
|
||||
}
|
||||
|
||||
pub fn get_size(&self) -> u64 {
|
||||
let mut size = 0;
|
||||
if let Some(ref avc1) = self.avc1 {
|
||||
// HEADER_SIZE intentionally omitted
|
||||
size += avc1.box_size();
|
||||
} else if let Some(ref hev1) = self.hev1 {
|
||||
// HEADER_SIZE intentionally omitted
|
||||
size += hev1.box_size();
|
||||
} else if let Some(ref vp09) = self.vp09 {
|
||||
// HEADER_SIZE intentionally omitted
|
||||
size += vp09.box_size();
|
||||
} else {
|
||||
size += HEADER_SIZE + RESERVED_DATA_SIZE;
|
||||
}
|
||||
size += self.sinf.box_size();
|
||||
size
|
||||
}
|
||||
}
|
||||
|
||||
impl Mp4Box for EncvBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
}
|
||||
|
||||
fn to_json(&self) -> Result<String> {
|
||||
Ok(serde_json::to_string(&self).unwrap())
|
||||
}
|
||||
|
||||
fn summary(&self) -> Result<String> {
|
||||
let child_summary = if let Some(ref avc1) = self.avc1 {
|
||||
avc1.summary()
|
||||
} else if let Some(ref hev1) = self.hev1 {
|
||||
hev1.summary()
|
||||
} else if let Some(ref vp09) = self.vp09 {
|
||||
vp09.summary()
|
||||
} else {
|
||||
Err(Error::InvalidData(""))
|
||||
};
|
||||
let mut s = format!("original_format={}", &self.sinf.frma.original_format);
|
||||
if let Ok(summary) = child_summary {
|
||||
s.push(' ');
|
||||
s.push_str(&summary);
|
||||
}
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for EncvBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
|
||||
let mut avc1 = None;
|
||||
let mut hev1 = None;
|
||||
let mut vp09 = None;
|
||||
let mut sinf = None;
|
||||
|
||||
// skip current container items
|
||||
skip_bytes(reader, RESERVED_DATA_SIZE)?;
|
||||
|
||||
let mut current = reader.stream_position()?;
|
||||
let end = start + size;
|
||||
while current < end {
|
||||
// Get box header.
|
||||
let header = BoxHeader::read(reader)?;
|
||||
let BoxHeader { name, size: s } = header;
|
||||
if s > size {
|
||||
return Err(Error::InvalidData(
|
||||
"encv box contains a box with a larger size than it",
|
||||
));
|
||||
}
|
||||
|
||||
match name {
|
||||
BoxType::SinfBox => {
|
||||
sinf = Some(SinfBox::read_box(reader, s)?);
|
||||
break;
|
||||
}
|
||||
_ => {
|
||||
skip_box(reader, s)?;
|
||||
}
|
||||
}
|
||||
|
||||
current = reader.stream_position()?;
|
||||
}
|
||||
|
||||
let sinf = sinf.ok_or(Error::BoxNotFound(BoxType::SinfBox))?;
|
||||
|
||||
reader.seek(SeekFrom::Start(start + HEADER_SIZE))?;
|
||||
|
||||
let original_format: BoxType = sinf.frma.original_format.into();
|
||||
match original_format {
|
||||
BoxType::Avc1Box => {
|
||||
avc1 = Some(Avc1Box::read_box(reader, size)?);
|
||||
}
|
||||
BoxType::Hev1Box => {
|
||||
hev1 = Some(Hev1Box::read_box(reader, size)?);
|
||||
}
|
||||
BoxType::Vp09Box => {
|
||||
vp09 = Some(Vp09Box::read_box(reader, size)?);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
|
||||
Ok(EncvBox {
|
||||
avc1,
|
||||
hev1,
|
||||
vp09,
|
||||
sinf,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for EncvBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
|
||||
if let Some(ref avc1) = self.avc1 {
|
||||
// the encv box header is used, so the header from this box
|
||||
// must be removed
|
||||
let mut buf = Vec::with_capacity(avc1.box_size() as usize);
|
||||
avc1.write_box(&mut buf)?;
|
||||
writer.write_all(&buf[HEADER_SIZE as usize..])?;
|
||||
} else if let Some(ref hev1) = self.hev1 {
|
||||
// the encv box header is used, so the header from this box
|
||||
// must be removed
|
||||
let mut buf = Vec::with_capacity(hev1.box_size() as usize);
|
||||
hev1.write_box(&mut buf)?;
|
||||
writer.write_all(&buf[HEADER_SIZE as usize..])?;
|
||||
} else if let Some(ref vp09) = self.vp09 {
|
||||
// the encv box header is used, so the header from this box
|
||||
// must be removed
|
||||
let mut buf = Vec::with_capacity(vp09.box_size() as usize);
|
||||
vp09.write_box(&mut buf)?;
|
||||
writer.write_all(&buf[HEADER_SIZE as usize..])?;
|
||||
} else {
|
||||
writer.write_all(&[0; RESERVED_DATA_SIZE as usize])?;
|
||||
}
|
||||
|
||||
self.sinf.write_box(writer)?;
|
||||
|
||||
Ok(size)
|
||||
}
|
||||
}
|
63
src/mp4box/frma.rs
Normal file
63
src/mp4box/frma.rs
Normal file
|
@ -0,0 +1,63 @@
|
|||
use serde::Serialize;
|
||||
use std::io::{Read, Seek, Write};
|
||||
|
||||
use crate::mp4box::*;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
|
||||
pub struct FrmaBox {
|
||||
pub original_format: FourCC,
|
||||
}
|
||||
|
||||
impl FrmaBox {
|
||||
pub fn get_type(&self) -> BoxType {
|
||||
BoxType::FrmaBox
|
||||
}
|
||||
|
||||
pub fn get_size(&self) -> u64 {
|
||||
HEADER_SIZE + 4
|
||||
}
|
||||
}
|
||||
|
||||
impl Mp4Box for FrmaBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
}
|
||||
|
||||
fn to_json(&self) -> Result<String> {
|
||||
Ok(serde_json::to_string(&self).unwrap())
|
||||
}
|
||||
|
||||
fn summary(&self) -> Result<String> {
|
||||
let s = format!("original_format={}", self.original_format,);
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for FrmaBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
|
||||
let original_format = reader.read_u32::<BigEndian>()?;
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
|
||||
Ok(FrmaBox {
|
||||
original_format: original_format.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for FrmaBox {
|
||||
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>(self.original_format.into())?;
|
||||
|
||||
Ok(size)
|
||||
}
|
||||
}
|
|
@ -29,6 +29,13 @@
|
|||
//! hev1
|
||||
//! mp4a
|
||||
//! tx3g
|
||||
//! enca
|
||||
//! encv
|
||||
//! sinf
|
||||
//! frma
|
||||
//! schm
|
||||
//! schi
|
||||
//! tenc
|
||||
//! stts
|
||||
//! stsc
|
||||
//! stsz
|
||||
|
@ -52,6 +59,9 @@
|
|||
//! tfhd
|
||||
//! tfdt
|
||||
//! trun
|
||||
//! saiz
|
||||
//! saio
|
||||
//! senc
|
||||
//! mdat
|
||||
//! free
|
||||
//!
|
||||
|
@ -69,7 +79,10 @@ pub(crate) mod data;
|
|||
pub(crate) mod dinf;
|
||||
pub(crate) mod edts;
|
||||
pub(crate) mod elst;
|
||||
pub(crate) mod enca;
|
||||
pub(crate) mod encv;
|
||||
pub(crate) mod emsg;
|
||||
pub(crate) mod frma;
|
||||
pub(crate) mod ftyp;
|
||||
pub(crate) mod hdlr;
|
||||
pub(crate) mod hev1;
|
||||
|
@ -85,6 +98,12 @@ pub(crate) mod moov;
|
|||
pub(crate) mod mp4a;
|
||||
pub(crate) mod mvex;
|
||||
pub(crate) mod mvhd;
|
||||
pub(crate) mod saio;
|
||||
pub(crate) mod saiz;
|
||||
pub(crate) mod schi;
|
||||
pub(crate) mod schm;
|
||||
pub(crate) mod senc;
|
||||
pub(crate) mod sinf;
|
||||
pub(crate) mod smhd;
|
||||
pub(crate) mod stbl;
|
||||
pub(crate) mod stco;
|
||||
|
@ -93,6 +112,7 @@ pub(crate) mod stsd;
|
|||
pub(crate) mod stss;
|
||||
pub(crate) mod stsz;
|
||||
pub(crate) mod stts;
|
||||
pub(crate) mod tenc;
|
||||
pub(crate) mod tfdt;
|
||||
pub(crate) mod tfhd;
|
||||
pub(crate) mod tkhd;
|
||||
|
@ -113,7 +133,10 @@ pub use data::DataBox;
|
|||
pub use dinf::DinfBox;
|
||||
pub use edts::EdtsBox;
|
||||
pub use elst::ElstBox;
|
||||
pub use enca::EncaBox;
|
||||
pub use encv::EncvBox;
|
||||
pub use emsg::EmsgBox;
|
||||
pub use frma::FrmaBox;
|
||||
pub use ftyp::FtypBox;
|
||||
pub use hdlr::HdlrBox;
|
||||
pub use hev1::Hev1Box;
|
||||
|
@ -129,6 +152,12 @@ pub use moov::MoovBox;
|
|||
pub use mp4a::Mp4aBox;
|
||||
pub use mvex::MvexBox;
|
||||
pub use mvhd::MvhdBox;
|
||||
pub use saio::SaioBox;
|
||||
pub use saiz::SaizBox;
|
||||
pub use schi::SchiBox;
|
||||
pub use schm::SchmBox;
|
||||
pub use senc::SencBox;
|
||||
pub use sinf::SinfBox;
|
||||
pub use smhd::SmhdBox;
|
||||
pub use stbl::StblBox;
|
||||
pub use stco::StcoBox;
|
||||
|
@ -137,6 +166,7 @@ pub use stsd::StsdBox;
|
|||
pub use stss::StssBox;
|
||||
pub use stsz::StszBox;
|
||||
pub use stts::SttsBox;
|
||||
pub use tenc::TencBox;
|
||||
pub use tfdt::TfdtBox;
|
||||
pub use tfhd::TfhdBox;
|
||||
pub use tkhd::TkhdBox;
|
||||
|
@ -238,7 +268,17 @@ boxtype! {
|
|||
CovrBox => 0x636f7672,
|
||||
DescBox => 0x64657363,
|
||||
WideBox => 0x77696465,
|
||||
WaveBox => 0x77617665
|
||||
WaveBox => 0x77617665,
|
||||
EncaBox => 0x656e6361,
|
||||
EncvBox => 0x656e6376,
|
||||
SinfBox => 0x73696e66,
|
||||
FrmaBox => 0x66726d61,
|
||||
SchmBox => 0x7363686d,
|
||||
SchiBox => 0x73636869,
|
||||
TencBox => 0x74656e63,
|
||||
SaizBox => 0x7361697a,
|
||||
SaioBox => 0x7361696f,
|
||||
SencBox => 0x73656e63
|
||||
}
|
||||
|
||||
pub trait Mp4Box: Sized {
|
||||
|
|
119
src/mp4box/saio.rs
Normal file
119
src/mp4box/saio.rs
Normal file
|
@ -0,0 +1,119 @@
|
|||
use serde::Serialize;
|
||||
use std::io::{Read, Seek, Write};
|
||||
|
||||
use crate::mp4box::*;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
|
||||
pub struct SaioBox {
|
||||
pub version: u8,
|
||||
pub aux_info: Option<AuxiliaryInfoType>,
|
||||
pub entry_count: u32,
|
||||
pub offsets: Vec<u64>,
|
||||
}
|
||||
|
||||
impl SaioBox {
|
||||
pub const FLAG_AUX_INFO_TYPE: u32 = 0x01;
|
||||
|
||||
pub fn get_type(&self) -> BoxType {
|
||||
BoxType::SaioBox
|
||||
}
|
||||
|
||||
pub fn get_size(&self) -> u64 {
|
||||
let mut size = HEADER_SIZE + HEADER_EXT_SIZE + 4;
|
||||
if self.aux_info.is_some() {
|
||||
size += 8;
|
||||
}
|
||||
if self.version == 0 {
|
||||
size += 4 * self.entry_count as u64;
|
||||
} else {
|
||||
size += 8 * self.entry_count as u64;
|
||||
}
|
||||
size
|
||||
}
|
||||
}
|
||||
|
||||
impl Mp4Box for SaioBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
}
|
||||
|
||||
fn to_json(&self) -> Result<String> {
|
||||
Ok(serde_json::to_string(&self).unwrap())
|
||||
}
|
||||
|
||||
fn summary(&self) -> Result<String> {
|
||||
let s = format!("entry_count={}", self.entry_count);
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for SaioBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
|
||||
let (version, flags) = read_box_header_ext(reader)?;
|
||||
|
||||
let mut aux_info = None;
|
||||
if SaioBox::FLAG_AUX_INFO_TYPE & flags != 0 {
|
||||
let aux_info_type = reader.read_u32::<BigEndian>()?;
|
||||
let aux_info_type_parameter = reader.read_u32::<BigEndian>()?;
|
||||
aux_info = Some(AuxiliaryInfoType {
|
||||
aux_info_type,
|
||||
aux_info_type_parameter,
|
||||
});
|
||||
}
|
||||
|
||||
let sample_count = reader.read_u32::<BigEndian>()?;
|
||||
|
||||
let mut offsets = Vec::with_capacity(sample_count as usize);
|
||||
for _ in 0..sample_count as usize {
|
||||
let offset = if version == 0 {
|
||||
reader.read_u32::<BigEndian>()? as u64
|
||||
} else {
|
||||
reader.read_u64::<BigEndian>()?
|
||||
};
|
||||
offsets.push(offset);
|
||||
}
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
|
||||
Ok(SaioBox {
|
||||
version,
|
||||
aux_info,
|
||||
entry_count: sample_count,
|
||||
offsets,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for SaioBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
|
||||
if let Some(ref aux_info) = self.aux_info {
|
||||
write_box_header_ext(writer, self.version, SaioBox::FLAG_AUX_INFO_TYPE)?;
|
||||
writer.write_u32::<BigEndian>(aux_info.aux_info_type)?;
|
||||
writer.write_u32::<BigEndian>(aux_info.aux_info_type_parameter)?;
|
||||
} else {
|
||||
write_box_header_ext(writer, self.version, 0)?;
|
||||
}
|
||||
|
||||
writer.write_u32::<BigEndian>(self.entry_count)?;
|
||||
|
||||
for i in 0..self.entry_count as usize {
|
||||
let offset = self.offsets.get(i).copied().unwrap_or(0);
|
||||
if self.version == 0 {
|
||||
writer.write_u32::<BigEndian>(offset as u32)?;
|
||||
} else {
|
||||
writer.write_u64::<BigEndian>(offset)?;
|
||||
};
|
||||
}
|
||||
|
||||
Ok(size)
|
||||
}
|
||||
}
|
117
src/mp4box/saiz.rs
Normal file
117
src/mp4box/saiz.rs
Normal file
|
@ -0,0 +1,117 @@
|
|||
use serde::Serialize;
|
||||
use std::io::{Read, Seek, Write};
|
||||
|
||||
use crate::mp4box::*;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
|
||||
pub struct SaizBox {
|
||||
pub aux_info: Option<AuxiliaryInfoType>,
|
||||
pub default_sample_info_size: u8,
|
||||
pub sample_count: u32,
|
||||
pub sample_info_sizes: Vec<u8>,
|
||||
}
|
||||
|
||||
impl SaizBox {
|
||||
pub const FLAG_AUX_INFO_TYPE: u32 = 0x01;
|
||||
|
||||
pub fn get_type(&self) -> BoxType {
|
||||
BoxType::SaizBox
|
||||
}
|
||||
|
||||
pub fn get_size(&self) -> u64 {
|
||||
let mut size = HEADER_SIZE + HEADER_EXT_SIZE + 5;
|
||||
if self.aux_info.is_some() {
|
||||
size += 8;
|
||||
}
|
||||
if self.default_sample_info_size == 0 {
|
||||
size += self.sample_count as u64;
|
||||
}
|
||||
size
|
||||
}
|
||||
}
|
||||
|
||||
impl Mp4Box for SaizBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
}
|
||||
|
||||
fn to_json(&self) -> Result<String> {
|
||||
Ok(serde_json::to_string(&self).unwrap())
|
||||
}
|
||||
|
||||
fn summary(&self) -> Result<String> {
|
||||
let s = format!(
|
||||
"sample_info_size={} sample_count={}",
|
||||
self.default_sample_info_size, self.sample_count
|
||||
);
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for SaizBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
|
||||
let (_version, flags) = read_box_header_ext(reader)?;
|
||||
|
||||
let mut aux_info = None;
|
||||
if SaizBox::FLAG_AUX_INFO_TYPE & flags != 0 {
|
||||
let aux_info_type = reader.read_u32::<BigEndian>()?;
|
||||
let aux_info_type_parameter = reader.read_u32::<BigEndian>()?;
|
||||
aux_info = Some(AuxiliaryInfoType {
|
||||
aux_info_type,
|
||||
aux_info_type_parameter,
|
||||
});
|
||||
}
|
||||
|
||||
let default_sample_info_size = reader.read_u8()?;
|
||||
let sample_count = reader.read_u32::<BigEndian>()?;
|
||||
|
||||
let mut sample_info_sizes = Vec::new();
|
||||
if default_sample_info_size == 0 {
|
||||
sample_info_sizes = Vec::with_capacity(sample_count as usize);
|
||||
for _ in 0..sample_count as usize {
|
||||
sample_info_sizes.push(reader.read_u8()?);
|
||||
}
|
||||
};
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
|
||||
Ok(SaizBox {
|
||||
aux_info,
|
||||
default_sample_info_size,
|
||||
sample_count,
|
||||
sample_info_sizes,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for SaizBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
|
||||
if let Some(ref aux_info) = self.aux_info {
|
||||
write_box_header_ext(writer, 0, SaizBox::FLAG_AUX_INFO_TYPE)?;
|
||||
writer.write_u32::<BigEndian>(aux_info.aux_info_type)?;
|
||||
writer.write_u32::<BigEndian>(aux_info.aux_info_type_parameter)?;
|
||||
} else {
|
||||
write_box_header_ext(writer, 0, 0)?;
|
||||
}
|
||||
|
||||
writer.write_u8(self.default_sample_info_size)?;
|
||||
writer.write_u32::<BigEndian>(self.sample_count)?;
|
||||
|
||||
if self.default_sample_info_size == 0 {
|
||||
for i in 0..self.sample_count as usize {
|
||||
let sample_info_size = self.sample_info_sizes.get(i).copied().unwrap_or(0);
|
||||
writer.write_u8(sample_info_size)?;
|
||||
}
|
||||
}
|
||||
Ok(size)
|
||||
}
|
||||
}
|
92
src/mp4box/schi.rs
Normal file
92
src/mp4box/schi.rs
Normal file
|
@ -0,0 +1,92 @@
|
|||
use serde::Serialize;
|
||||
use std::io::{Read, Seek, Write};
|
||||
|
||||
use crate::mp4box::*;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
|
||||
pub struct SchiBox {
|
||||
pub tenc: Option<TencBox>,
|
||||
}
|
||||
|
||||
impl SchiBox {
|
||||
pub fn get_type(&self) -> BoxType {
|
||||
BoxType::SchiBox
|
||||
}
|
||||
|
||||
pub fn get_size(&self) -> u64 {
|
||||
let mut size = HEADER_SIZE;
|
||||
if let Some(ref tenc) = self.tenc {
|
||||
size += tenc.get_size();
|
||||
}
|
||||
size
|
||||
}
|
||||
}
|
||||
|
||||
impl Mp4Box for SchiBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
}
|
||||
|
||||
fn to_json(&self) -> Result<String> {
|
||||
Ok(serde_json::to_string(&self).unwrap())
|
||||
}
|
||||
|
||||
fn summary(&self) -> Result<String> {
|
||||
let s = String::new();
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for SchiBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
|
||||
let mut tenc = None;
|
||||
|
||||
let mut current = reader.stream_position()?;
|
||||
let end = start + size;
|
||||
while current < end {
|
||||
// Get box header.
|
||||
let header = BoxHeader::read(reader)?;
|
||||
let BoxHeader { name, size: s } = header;
|
||||
if s > size {
|
||||
return Err(Error::InvalidData(
|
||||
"schi box contains a box with a larger size than it",
|
||||
));
|
||||
}
|
||||
|
||||
match name {
|
||||
BoxType::TencBox => {
|
||||
tenc = Some(TencBox::read_box(reader, s)?);
|
||||
}
|
||||
_ => {
|
||||
// XXX warn!()
|
||||
skip_box(reader, s)?;
|
||||
}
|
||||
}
|
||||
|
||||
current = reader.stream_position()?;
|
||||
}
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
|
||||
Ok(SchiBox { tenc })
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for SchiBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
|
||||
if let Some(ref tenc) = self.tenc {
|
||||
tenc.write_box(writer)?;
|
||||
}
|
||||
|
||||
Ok(size)
|
||||
}
|
||||
}
|
82
src/mp4box/schm.rs
Normal file
82
src/mp4box/schm.rs
Normal file
|
@ -0,0 +1,82 @@
|
|||
use serde::Serialize;
|
||||
use std::io::{Read, Seek, Write};
|
||||
|
||||
use crate::mp4box::*;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
|
||||
pub struct SchmBox {
|
||||
pub version: u8,
|
||||
pub scheme_type: FourCC,
|
||||
pub scheme_version: u32,
|
||||
}
|
||||
|
||||
impl SchmBox {
|
||||
pub const FLAG_SCHEME_URI: u32 = 0x01;
|
||||
|
||||
pub fn get_type(&self) -> BoxType {
|
||||
BoxType::SchmBox
|
||||
}
|
||||
|
||||
pub fn get_size(&self) -> u64 {
|
||||
HEADER_SIZE + HEADER_EXT_SIZE + 8
|
||||
}
|
||||
}
|
||||
|
||||
impl Mp4Box for SchmBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
}
|
||||
|
||||
fn to_json(&self) -> Result<String> {
|
||||
Ok(serde_json::to_string(&self).unwrap())
|
||||
}
|
||||
|
||||
fn summary(&self) -> Result<String> {
|
||||
let s = format!(
|
||||
"scheme_type={} scheme_version={}",
|
||||
self.scheme_type, self.scheme_version
|
||||
);
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for SchmBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
|
||||
let (version, flags) = read_box_header_ext(reader)?;
|
||||
|
||||
let scheme_type = reader.read_u32::<BigEndian>()?;
|
||||
let scheme_version = reader.read_u32::<BigEndian>()?;
|
||||
|
||||
if SchmBox::FLAG_SCHEME_URI & flags != 0 {
|
||||
// todo
|
||||
}
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
|
||||
Ok(SchmBox {
|
||||
version,
|
||||
scheme_type: scheme_type.into(),
|
||||
scheme_version,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for SchmBox {
|
||||
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, 0)?;
|
||||
|
||||
writer.write_u32::<BigEndian>(self.scheme_type.into())?;
|
||||
writer.write_u32::<BigEndian>(self.scheme_version)?;
|
||||
|
||||
Ok(size)
|
||||
}
|
||||
}
|
134
src/mp4box/senc.rs
Normal file
134
src/mp4box/senc.rs
Normal file
|
@ -0,0 +1,134 @@
|
|||
use serde::Serialize;
|
||||
use std::io::{Read, Seek, Write};
|
||||
|
||||
use crate::mp4box::*;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
|
||||
pub struct SencBox {
|
||||
pub version: u8,
|
||||
pub flags: u32,
|
||||
pub sample_count: u32,
|
||||
pub sample_data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl SencBox {
|
||||
pub const FLAG_USE_SUBSAMPLE_ENCRYPTION: u32 = 0x02;
|
||||
|
||||
pub fn get_type(&self) -> BoxType {
|
||||
BoxType::SencBox
|
||||
}
|
||||
|
||||
pub fn get_size(&self) -> u64 {
|
||||
HEADER_SIZE + HEADER_EXT_SIZE + 4 + (self.sample_data.len() as u64)
|
||||
}
|
||||
|
||||
pub fn get_sample_info(&self, iv_size: u8) -> Result<Vec<SampleInfo>> {
|
||||
if iv_size != 16 && iv_size != 8 && iv_size != 0 {
|
||||
return Err(Error::InvalidData("invalid iv_size"));
|
||||
}
|
||||
let mut reader = &self.sample_data[..];
|
||||
let mut infos = Vec::with_capacity(self.sample_count as usize);
|
||||
for _ in 0..self.sample_count {
|
||||
let mut iv = vec![0; iv_size as usize];
|
||||
if iv_size != 0 {
|
||||
reader.read_exact(&mut iv)?;
|
||||
}
|
||||
let mut subsamples = Vec::new();
|
||||
if SencBox::FLAG_USE_SUBSAMPLE_ENCRYPTION & self.flags != 0 {
|
||||
let subsample_count = reader.read_u16::<BigEndian>()?;
|
||||
subsamples = Vec::with_capacity(subsample_count as usize);
|
||||
for _ in 0..subsample_count {
|
||||
let bytes_of_clear_data = reader.read_u16::<BigEndian>()?;
|
||||
let bytes_of_encrypted_data = reader.read_u32::<BigEndian>()?;
|
||||
subsamples.push(SubSampleInfo {
|
||||
bytes_of_clear_data,
|
||||
bytes_of_encrypted_data,
|
||||
});
|
||||
}
|
||||
}
|
||||
infos.push(SampleInfo { iv, subsamples });
|
||||
}
|
||||
Ok(infos)
|
||||
}
|
||||
|
||||
pub fn set_sample_info(&mut self, infos: &[SampleInfo], iv_size: u8) -> Result<()> {
|
||||
if iv_size != 16 && iv_size != 8 && iv_size != 0 {
|
||||
return Err(Error::InvalidData("invalid iv_size"));
|
||||
}
|
||||
let mut buf = Vec::new();
|
||||
for info in infos {
|
||||
if iv_size != 0 {
|
||||
buf.write_all(&info.iv[..iv_size as usize])?;
|
||||
}
|
||||
if SencBox::FLAG_USE_SUBSAMPLE_ENCRYPTION & self.flags != 0 {
|
||||
buf.write_u16::<BigEndian>(info.subsamples.len() as u16)?;
|
||||
for subsample in &info.subsamples {
|
||||
buf.write_u16::<BigEndian>(subsample.bytes_of_clear_data)?;
|
||||
buf.write_u32::<BigEndian>(subsample.bytes_of_encrypted_data)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.sample_data = buf;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Mp4Box for SencBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
}
|
||||
|
||||
fn to_json(&self) -> Result<String> {
|
||||
Ok(serde_json::to_string(&self).unwrap())
|
||||
}
|
||||
|
||||
fn summary(&self) -> Result<String> {
|
||||
let s = format!("sample_count={}", self.sample_count);
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for SencBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
|
||||
let (version, flags) = read_box_header_ext(reader)?;
|
||||
|
||||
let sample_count = reader.read_u32::<BigEndian>()?;
|
||||
|
||||
// the senc box cannot be properly parsed without IV_size
|
||||
// which is only available from other boxes. Store the raw
|
||||
// data for parsing with member functions later
|
||||
let data_size = start + size - reader.stream_position()?;
|
||||
let mut sample_data = vec![0; data_size as usize];
|
||||
reader.read_exact(&mut sample_data)?;
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
|
||||
Ok(SencBox {
|
||||
version,
|
||||
flags,
|
||||
sample_count,
|
||||
sample_data,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for SencBox {
|
||||
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_count)?;
|
||||
|
||||
writer.write_all(&self.sample_data)?;
|
||||
|
||||
Ok(size)
|
||||
}
|
||||
}
|
121
src/mp4box/sinf.rs
Normal file
121
src/mp4box/sinf.rs
Normal file
|
@ -0,0 +1,121 @@
|
|||
use serde::Serialize;
|
||||
use std::io::{Read, Seek, Write};
|
||||
|
||||
use crate::mp4box::*;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
|
||||
pub struct SinfBox {
|
||||
pub frma: FrmaBox,
|
||||
pub schm: Option<SchmBox>,
|
||||
pub schi: Option<SchiBox>,
|
||||
}
|
||||
|
||||
impl SinfBox {
|
||||
pub fn get_type(&self) -> BoxType {
|
||||
BoxType::SinfBox
|
||||
}
|
||||
|
||||
pub fn get_size(&self) -> u64 {
|
||||
let mut size = HEADER_SIZE + self.frma.get_size();
|
||||
if let Some(ref schm) = self.schm {
|
||||
size += schm.get_size();
|
||||
}
|
||||
if let Some(ref schi) = self.schi {
|
||||
size += schi.get_size();
|
||||
}
|
||||
size
|
||||
}
|
||||
}
|
||||
|
||||
impl Mp4Box for SinfBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
}
|
||||
|
||||
fn to_json(&self) -> Result<String> {
|
||||
Ok(serde_json::to_string(&self).unwrap())
|
||||
}
|
||||
|
||||
fn summary(&self) -> Result<String> {
|
||||
let s = String::new();
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for SinfBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
|
||||
let mut frma = None;
|
||||
let mut schm = None;
|
||||
let mut schi = None;
|
||||
|
||||
let mut current = reader.stream_position()?;
|
||||
let end = start + size;
|
||||
while current < end {
|
||||
// Get box header.
|
||||
let header = BoxHeader::read(reader)?;
|
||||
let BoxHeader { name, size: s } = header;
|
||||
if s > size {
|
||||
return Err(Error::InvalidData(
|
||||
"sinf box contains a box with a larger size than it",
|
||||
));
|
||||
}
|
||||
|
||||
match name {
|
||||
BoxType::FrmaBox => {
|
||||
frma = Some(FrmaBox::read_box(reader, s)?);
|
||||
}
|
||||
BoxType::SchmBox => {
|
||||
schm = Some(SchmBox::read_box(reader, s)?);
|
||||
}
|
||||
BoxType::SchiBox => {
|
||||
schi = Some(SchiBox::read_box(reader, s)?);
|
||||
}
|
||||
_ => {
|
||||
// XXX warn!()
|
||||
skip_box(reader, s)?;
|
||||
}
|
||||
}
|
||||
|
||||
current = reader.stream_position()?;
|
||||
}
|
||||
|
||||
if frma.is_none() {
|
||||
return Err(Error::BoxNotFound(BoxType::FrmaBox));
|
||||
}
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
|
||||
Ok(SinfBox {
|
||||
frma: frma.unwrap(),
|
||||
schm,
|
||||
schi,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for SinfBox {
|
||||
fn write_box(&self, writer: &mut W) -> Result<u64> {
|
||||
let size = self.box_size();
|
||||
println!("sinf size: {}", size);
|
||||
|
||||
BoxHeader::new(self.box_type(), size).write(writer)?;
|
||||
|
||||
self.frma.write_box(writer)?;
|
||||
|
||||
if let Some(schm) = &self.schm {
|
||||
schm.write_box(writer)?;
|
||||
}
|
||||
|
||||
if let Some(schi) = &self.schi {
|
||||
schi.write_box(writer)?;
|
||||
}
|
||||
|
||||
Ok(size)
|
||||
}
|
||||
}
|
|
@ -25,6 +25,12 @@ pub struct StsdBox {
|
|||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub tx3g: Option<Tx3gBox>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub enca: Option<EncaBox>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub encv: Option<EncvBox>,
|
||||
}
|
||||
|
||||
impl StsdBox {
|
||||
|
@ -44,6 +50,10 @@ impl StsdBox {
|
|||
size += mp4a.box_size();
|
||||
} else if let Some(ref tx3g) = self.tx3g {
|
||||
size += tx3g.box_size();
|
||||
} else if let Some(ref enca) = self.enca {
|
||||
size += enca.box_size();
|
||||
} else if let Some(ref encv) = self.encv {
|
||||
size += encv.box_size();
|
||||
}
|
||||
size
|
||||
}
|
||||
|
@ -81,6 +91,8 @@ impl<R: Read + Seek> ReadBox<&mut R> for StsdBox {
|
|||
let mut vp09 = None;
|
||||
let mut mp4a = None;
|
||||
let mut tx3g = None;
|
||||
let mut enca = None;
|
||||
let mut encv = None;
|
||||
|
||||
// Get box header.
|
||||
let header = BoxHeader::read(reader)?;
|
||||
|
@ -107,6 +119,12 @@ impl<R: Read + Seek> ReadBox<&mut R> for StsdBox {
|
|||
BoxType::Tx3gBox => {
|
||||
tx3g = Some(Tx3gBox::read_box(reader, s)?);
|
||||
}
|
||||
BoxType::EncaBox => {
|
||||
enca = Some(EncaBox::read_box(reader, s)?);
|
||||
}
|
||||
BoxType::EncvBox => {
|
||||
encv = Some(EncvBox::read_box(reader, s)?);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
@ -120,6 +138,8 @@ impl<R: Read + Seek> ReadBox<&mut R> for StsdBox {
|
|||
vp09,
|
||||
mp4a,
|
||||
tx3g,
|
||||
enca,
|
||||
encv,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -143,6 +163,10 @@ impl<W: Write> WriteBox<&mut W> for StsdBox {
|
|||
mp4a.write_box(writer)?;
|
||||
} else if let Some(ref tx3g) = self.tx3g {
|
||||
tx3g.write_box(writer)?;
|
||||
} else if let Some(ref enca) = self.enca {
|
||||
enca.write_box(writer)?;
|
||||
} else if let Some(ref encv) = self.encv {
|
||||
encv.write_box(writer)?;
|
||||
}
|
||||
|
||||
Ok(size)
|
||||
|
|
129
src/mp4box/tenc.rs
Normal file
129
src/mp4box/tenc.rs
Normal file
|
@ -0,0 +1,129 @@
|
|||
use serde::Serialize;
|
||||
use std::io::{Read, Seek, Write};
|
||||
|
||||
use crate::mp4box::*;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
|
||||
pub struct TencBox {
|
||||
pub version: u8,
|
||||
pub default_crypt_byte_block: u8,
|
||||
pub default_skip_byte_block: u8,
|
||||
pub default_is_protected: bool,
|
||||
pub default_per_sample_iv_size: u8,
|
||||
pub default_kid: [u8; 16],
|
||||
pub default_constant_iv: Vec<u8>,
|
||||
}
|
||||
|
||||
impl TencBox {
|
||||
pub fn get_type(&self) -> BoxType {
|
||||
BoxType::TencBox
|
||||
}
|
||||
|
||||
pub fn get_size(&self) -> u64 {
|
||||
let mut size = HEADER_SIZE + HEADER_EXT_SIZE + 20;
|
||||
if self.default_is_protected && self.default_per_sample_iv_size == 0 {
|
||||
size += 1 + (self.default_constant_iv.len() & 0xff) as u64;
|
||||
}
|
||||
size
|
||||
}
|
||||
}
|
||||
|
||||
impl Mp4Box for TencBox {
|
||||
fn box_type(&self) -> BoxType {
|
||||
self.get_type()
|
||||
}
|
||||
|
||||
fn box_size(&self) -> u64 {
|
||||
self.get_size()
|
||||
}
|
||||
|
||||
fn to_json(&self) -> Result<String> {
|
||||
Ok(serde_json::to_string(&self).unwrap())
|
||||
}
|
||||
|
||||
fn summary(&self) -> Result<String> {
|
||||
let mut s = format!(
|
||||
"crypt_byte_block={} skip_byte_block={} protected={} iv_size={} kid={:x?}",
|
||||
self.default_crypt_byte_block,
|
||||
self.default_skip_byte_block,
|
||||
self.default_is_protected,
|
||||
self.default_per_sample_iv_size,
|
||||
self.default_kid,
|
||||
);
|
||||
if !self.default_constant_iv.is_empty() {
|
||||
s.push_str(&format!(" constant_iv={:x?}", self.default_constant_iv));
|
||||
}
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Seek> ReadBox<&mut R> for TencBox {
|
||||
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
|
||||
let start = box_start(reader)?;
|
||||
|
||||
let mut default_crypt_byte_block = 0;
|
||||
let mut default_skip_byte_block = 0;
|
||||
|
||||
let (version, _flags) = read_box_header_ext(reader)?;
|
||||
|
||||
let _reserved = reader.read_u8()?;
|
||||
let val = reader.read_u8()?;
|
||||
if version > 0 {
|
||||
default_crypt_byte_block = val >> 4;
|
||||
default_skip_byte_block = val & 0xf;
|
||||
}
|
||||
let default_is_protected = reader.read_u8()? != 0;
|
||||
let default_per_sample_iv_size = reader.read_u8()?;
|
||||
|
||||
let mut default_kid = [0; 16];
|
||||
reader.read_exact(&mut default_kid)?;
|
||||
|
||||
let mut default_constant_iv = Vec::new();
|
||||
if default_is_protected && default_per_sample_iv_size == 0 {
|
||||
let default_constant_iv_size = reader.read_u8()?;
|
||||
if default_constant_iv_size > 0 {
|
||||
default_constant_iv = vec![0; default_constant_iv_size as usize];
|
||||
reader.read_exact(&mut default_constant_iv[..])?;
|
||||
}
|
||||
}
|
||||
|
||||
skip_bytes_to(reader, start + size)?;
|
||||
|
||||
Ok(TencBox {
|
||||
version,
|
||||
default_crypt_byte_block,
|
||||
default_skip_byte_block,
|
||||
default_is_protected,
|
||||
default_per_sample_iv_size,
|
||||
default_kid,
|
||||
default_constant_iv,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> WriteBox<&mut W> for TencBox {
|
||||
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, 0)?;
|
||||
|
||||
writer.write_u8(0)?; // reserved
|
||||
let val = if self.version > 0 {
|
||||
(self.default_crypt_byte_block << 4) | (self.default_skip_byte_block & 0xf)
|
||||
} else {
|
||||
0
|
||||
};
|
||||
writer.write_u8(val)?;
|
||||
writer.write_u8(self.default_is_protected as u8)?;
|
||||
writer.write_u8(self.default_per_sample_iv_size)?;
|
||||
writer.write_all(&self.default_kid)?;
|
||||
if self.default_is_protected && self.default_per_sample_iv_size == 0 {
|
||||
let default_constant_iv_size = (self.default_constant_iv.len() & 0xff) as u8;
|
||||
writer.write_u8(default_constant_iv_size)?;
|
||||
writer.write_all(&self.default_constant_iv[0..default_constant_iv_size as usize])?;
|
||||
}
|
||||
|
||||
Ok(size)
|
||||
}
|
||||
}
|
|
@ -2,13 +2,25 @@ use serde::Serialize;
|
|||
use std::io::{Read, Seek, Write};
|
||||
|
||||
use crate::mp4box::*;
|
||||
use crate::mp4box::{tfdt::TfdtBox, tfhd::TfhdBox, trun::TrunBox};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
|
||||
pub struct TrafBox {
|
||||
pub tfhd: TfhdBox,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub tfdt: Option<TfdtBox>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub trun: Option<TrunBox>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub saiz: Option<SaizBox>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub saio: Option<SaioBox>,
|
||||
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub senc: Option<SencBox>,
|
||||
}
|
||||
|
||||
impl TrafBox {
|
||||
|
@ -25,6 +37,15 @@ impl TrafBox {
|
|||
if let Some(ref trun) = self.trun {
|
||||
size += trun.box_size();
|
||||
}
|
||||
if let Some(ref saiz) = self.saiz {
|
||||
size += saiz.box_size();
|
||||
}
|
||||
if let Some(ref saio) = self.saio {
|
||||
size += saio.box_size();
|
||||
}
|
||||
if let Some(ref senc) = self.senc {
|
||||
size += senc.box_size();
|
||||
}
|
||||
size
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +76,9 @@ impl<R: Read + Seek> ReadBox<&mut R> for TrafBox {
|
|||
let mut tfhd = None;
|
||||
let mut tfdt = None;
|
||||
let mut trun = None;
|
||||
let mut saiz = None;
|
||||
let mut saio = None;
|
||||
let mut senc = None;
|
||||
|
||||
let mut current = reader.stream_position()?;
|
||||
let end = start + size;
|
||||
|
@ -78,6 +102,15 @@ impl<R: Read + Seek> ReadBox<&mut R> for TrafBox {
|
|||
BoxType::TrunBox => {
|
||||
trun = Some(TrunBox::read_box(reader, s)?);
|
||||
}
|
||||
BoxType::SaizBox => {
|
||||
saiz = Some(SaizBox::read_box(reader, s)?);
|
||||
}
|
||||
BoxType::SaioBox => {
|
||||
saio = Some(SaioBox::read_box(reader, s)?);
|
||||
}
|
||||
BoxType::SencBox => {
|
||||
senc = Some(SencBox::read_box(reader, s)?);
|
||||
}
|
||||
_ => {
|
||||
// XXX warn!()
|
||||
skip_box(reader, s)?;
|
||||
|
@ -97,6 +130,9 @@ impl<R: Read + Seek> ReadBox<&mut R> for TrafBox {
|
|||
tfhd: tfhd.unwrap(),
|
||||
tfdt,
|
||||
trun,
|
||||
saiz,
|
||||
saio,
|
||||
senc,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -113,6 +149,15 @@ impl<W: Write> WriteBox<&mut W> for TrafBox {
|
|||
if let Some(ref trun) = self.trun {
|
||||
trun.write_box(writer)?;
|
||||
}
|
||||
if let Some(ref saiz) = self.saiz {
|
||||
saiz.write_box(writer)?;
|
||||
}
|
||||
if let Some(ref saio) = self.saio {
|
||||
saio.write_box(writer)?;
|
||||
}
|
||||
if let Some(ref senc) = self.senc {
|
||||
senc.write_box(writer)?;
|
||||
}
|
||||
|
||||
Ok(size)
|
||||
}
|
||||
|
|
67
src/track.rs
67
src/track.rs
|
@ -129,6 +129,22 @@ impl Mp4Track {
|
|||
Ok(MediaType::AAC)
|
||||
} else if self.trak.mdia.minf.stbl.stsd.tx3g.is_some() {
|
||||
Ok(MediaType::TTXT)
|
||||
} else if let Some(ref enca) = self.trak.mdia.minf.stbl.stsd.enca {
|
||||
if enca.mp4a.is_some() {
|
||||
Ok(MediaType::AAC)
|
||||
} else {
|
||||
Err(Error::InvalidData("unsupported media type"))
|
||||
}
|
||||
} else if let Some(ref encv) = self.trak.mdia.minf.stbl.stsd.encv {
|
||||
if encv.avc1.is_some() {
|
||||
Ok(MediaType::H264)
|
||||
} else if encv.hev1.is_some() {
|
||||
Ok(MediaType::H265)
|
||||
} else if encv.vp09.is_some() {
|
||||
Ok(MediaType::VP9)
|
||||
} else {
|
||||
Err(Error::InvalidData("unsupported media type"))
|
||||
}
|
||||
} else {
|
||||
Err(Error::InvalidData("unsupported media type"))
|
||||
}
|
||||
|
@ -145,6 +161,22 @@ impl Mp4Track {
|
|||
Ok(FourCC::from(BoxType::Mp4aBox))
|
||||
} else if self.trak.mdia.minf.stbl.stsd.tx3g.is_some() {
|
||||
Ok(FourCC::from(BoxType::Tx3gBox))
|
||||
} else if let Some(ref enca) = self.trak.mdia.minf.stbl.stsd.enca {
|
||||
if enca.mp4a.is_some() {
|
||||
Ok(FourCC::from(BoxType::Mp4aBox))
|
||||
} else {
|
||||
Err(Error::InvalidData("unsupported sample entry box"))
|
||||
}
|
||||
} else if let Some(ref encv) = self.trak.mdia.minf.stbl.stsd.encv {
|
||||
if encv.avc1.is_some() {
|
||||
Ok(FourCC::from(BoxType::Avc1Box))
|
||||
} else if encv.hev1.is_some() {
|
||||
Ok(FourCC::from(BoxType::Hev1Box))
|
||||
} else if encv.vp09.is_some() {
|
||||
Ok(FourCC::from(BoxType::Vp09Box))
|
||||
} else {
|
||||
Err(Error::InvalidData("unsupported sample entry box"))
|
||||
}
|
||||
} else {
|
||||
Err(Error::InvalidData("unsupported sample entry box"))
|
||||
}
|
||||
|
@ -301,6 +333,41 @@ impl Mp4Track {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn protection_scheme_info(&self) -> Option<&SinfBox> {
|
||||
if let Some(ref enca) = self.trak.mdia.minf.stbl.stsd.enca {
|
||||
Some(&enca.sinf)
|
||||
} else if let Some(ref encv) = self.trak.mdia.minf.stbl.stsd.encv {
|
||||
Some(&encv.sinf)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn protection_sample_info(&self) -> Result<Vec<SampleInfo>> {
|
||||
let mut sample_infos = Vec::new();
|
||||
let sinf = self
|
||||
.protection_scheme_info()
|
||||
.ok_or(Error::InvalidData("missing protection info"))?;
|
||||
let iv_size = sinf
|
||||
.schi
|
||||
.as_ref()
|
||||
.and_then(|schi| {
|
||||
schi.tenc
|
||||
.as_ref()
|
||||
.map(|tenc| tenc.default_per_sample_iv_size)
|
||||
})
|
||||
.unwrap_or(16);
|
||||
for traf in &self.trafs {
|
||||
if let Some(ref senc) = traf.senc {
|
||||
let si = senc.get_sample_info(iv_size)?;
|
||||
sample_infos.extend(si);
|
||||
} else {
|
||||
// TODO - read protection info based on the saiz and saio boxes
|
||||
}
|
||||
}
|
||||
Ok(sample_infos)
|
||||
}
|
||||
|
||||
fn stsc_index(&self, sample_id: u32) -> Result<usize> {
|
||||
if self.trak.mdia.minf.stbl.stsc.entries.is_empty() {
|
||||
return Err(Error::InvalidData("no stsc entries"));
|
||||
|
|
25
src/types.rs
25
src/types.rs
|
@ -138,6 +138,13 @@ impl From<BoxType> for FourCC {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<FourCC> for BoxType {
|
||||
fn from(fourcc: FourCC) -> BoxType {
|
||||
let int_val: u32 = fourcc.into();
|
||||
BoxType::from(int_val)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for FourCC {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let code: u32 = self.into();
|
||||
|
@ -739,3 +746,21 @@ impl<'a, T: Metadata<'a>> Metadata<'a> for Option<T> {
|
|||
self.as_ref().and_then(|t| t.summary())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
|
||||
pub struct AuxiliaryInfoType {
|
||||
pub aux_info_type: u32,
|
||||
pub aux_info_type_parameter: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
|
||||
pub struct SubSampleInfo {
|
||||
pub bytes_of_clear_data: u16,
|
||||
pub bytes_of_encrypted_data: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
|
||||
pub struct SampleInfo {
|
||||
pub iv: Vec<u8>,
|
||||
pub subsamples: Vec<SubSampleInfo>,
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue