1
0
Fork 0
mirror of https://github.com/alfg/mp4-rust.git synced 2024-05-19 16:58:04 +00:00

Add boxes needed for common encryption

This commit is contained in:
Jensenn 2023-09-11 12:56:38 -06:00
parent 35560e94f5
commit 943c012d2c
16 changed files with 1387 additions and 2 deletions

View file

@ -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
View 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
View 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
View 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)
}
}

View file

@ -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
View 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
View 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
View 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
View 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
View 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
View 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)
}
}

View file

@ -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
View 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)
}
}

View file

@ -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)
}

View file

@ -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"));

View file

@ -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>,
}