mirror of
https://github.com/alfg/mp4-rust.git
synced 2024-06-24 06:50:33 +00:00
Merge 94b9bff82f
into 35560e94f5
This commit is contained in:
commit
44bc45f162
|
@ -99,6 +99,27 @@ fn get_boxes(file: File) -> Result<Vec<Box>> {
|
||||||
if let Some(ref mp4a) = &stbl.stsd.mp4a {
|
if let Some(ref mp4a) = &stbl.stsd.mp4a {
|
||||||
boxes.push(build_box(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));
|
boxes.push(build_box(&stbl.stts));
|
||||||
if let Some(ref ctts) = &stbl.ctts {
|
if let Some(ref ctts) = &stbl.ctts {
|
||||||
boxes.push(build_box(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
|
//! hev1
|
||||||
//! mp4a
|
//! mp4a
|
||||||
//! tx3g
|
//! tx3g
|
||||||
|
//! enca
|
||||||
|
//! encv
|
||||||
|
//! sinf
|
||||||
|
//! frma
|
||||||
|
//! schm
|
||||||
|
//! schi
|
||||||
|
//! tenc
|
||||||
//! stts
|
//! stts
|
||||||
//! stsc
|
//! stsc
|
||||||
//! stsz
|
//! stsz
|
||||||
|
@ -45,6 +52,7 @@
|
||||||
//! mvex
|
//! mvex
|
||||||
//! mehd
|
//! mehd
|
||||||
//! trex
|
//! trex
|
||||||
|
//! pssh
|
||||||
//! emsg
|
//! emsg
|
||||||
//! moof
|
//! moof
|
||||||
//! mfhd
|
//! mfhd
|
||||||
|
@ -52,6 +60,9 @@
|
||||||
//! tfhd
|
//! tfhd
|
||||||
//! tfdt
|
//! tfdt
|
||||||
//! trun
|
//! trun
|
||||||
|
//! saiz
|
||||||
|
//! saio
|
||||||
|
//! senc
|
||||||
//! mdat
|
//! mdat
|
||||||
//! free
|
//! free
|
||||||
//!
|
//!
|
||||||
|
@ -70,6 +81,9 @@ pub(crate) mod dinf;
|
||||||
pub(crate) mod edts;
|
pub(crate) mod edts;
|
||||||
pub(crate) mod elst;
|
pub(crate) mod elst;
|
||||||
pub(crate) mod emsg;
|
pub(crate) mod emsg;
|
||||||
|
pub(crate) mod enca;
|
||||||
|
pub(crate) mod encv;
|
||||||
|
pub(crate) mod frma;
|
||||||
pub(crate) mod ftyp;
|
pub(crate) mod ftyp;
|
||||||
pub(crate) mod hdlr;
|
pub(crate) mod hdlr;
|
||||||
pub(crate) mod hev1;
|
pub(crate) mod hev1;
|
||||||
|
@ -85,6 +99,13 @@ 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 pssh;
|
||||||
|
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 smhd;
|
||||||
pub(crate) mod stbl;
|
pub(crate) mod stbl;
|
||||||
pub(crate) mod stco;
|
pub(crate) mod stco;
|
||||||
|
@ -93,6 +114,7 @@ pub(crate) mod stsd;
|
||||||
pub(crate) mod stss;
|
pub(crate) mod stss;
|
||||||
pub(crate) mod stsz;
|
pub(crate) mod stsz;
|
||||||
pub(crate) mod stts;
|
pub(crate) mod stts;
|
||||||
|
pub(crate) mod tenc;
|
||||||
pub(crate) mod tfdt;
|
pub(crate) mod tfdt;
|
||||||
pub(crate) mod tfhd;
|
pub(crate) mod tfhd;
|
||||||
pub(crate) mod tkhd;
|
pub(crate) mod tkhd;
|
||||||
|
@ -114,6 +136,9 @@ pub use dinf::DinfBox;
|
||||||
pub use edts::EdtsBox;
|
pub use edts::EdtsBox;
|
||||||
pub use elst::ElstBox;
|
pub use elst::ElstBox;
|
||||||
pub use emsg::EmsgBox;
|
pub use emsg::EmsgBox;
|
||||||
|
pub use enca::EncaBox;
|
||||||
|
pub use encv::EncvBox;
|
||||||
|
pub use frma::FrmaBox;
|
||||||
pub use ftyp::FtypBox;
|
pub use ftyp::FtypBox;
|
||||||
pub use hdlr::HdlrBox;
|
pub use hdlr::HdlrBox;
|
||||||
pub use hev1::Hev1Box;
|
pub use hev1::Hev1Box;
|
||||||
|
@ -129,6 +154,13 @@ 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 pssh::PsshBox;
|
||||||
|
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 smhd::SmhdBox;
|
||||||
pub use stbl::StblBox;
|
pub use stbl::StblBox;
|
||||||
pub use stco::StcoBox;
|
pub use stco::StcoBox;
|
||||||
|
@ -137,6 +169,7 @@ pub use stsd::StsdBox;
|
||||||
pub use stss::StssBox;
|
pub use stss::StssBox;
|
||||||
pub use stsz::StszBox;
|
pub use stsz::StszBox;
|
||||||
pub use stts::SttsBox;
|
pub use stts::SttsBox;
|
||||||
|
pub use tenc::TencBox;
|
||||||
pub use tfdt::TfdtBox;
|
pub use tfdt::TfdtBox;
|
||||||
pub use tfhd::TfhdBox;
|
pub use tfhd::TfhdBox;
|
||||||
pub use tkhd::TkhdBox;
|
pub use tkhd::TkhdBox;
|
||||||
|
@ -238,7 +271,18 @@ boxtype! {
|
||||||
CovrBox => 0x636f7672,
|
CovrBox => 0x636f7672,
|
||||||
DescBox => 0x64657363,
|
DescBox => 0x64657363,
|
||||||
WideBox => 0x77696465,
|
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,
|
||||||
|
PsshBox => 0x70737368
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Mp4Box: Sized {
|
pub trait Mp4Box: Sized {
|
||||||
|
|
|
@ -20,6 +20,9 @@ pub struct MoovBox {
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub udta: Option<UdtaBox>,
|
pub udta: Option<UdtaBox>,
|
||||||
|
|
||||||
|
#[serde(rename = "pssh")]
|
||||||
|
pub psshs: Vec<PsshBox>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MoovBox {
|
impl MoovBox {
|
||||||
|
@ -38,6 +41,9 @@ impl MoovBox {
|
||||||
if let Some(udta) = &self.udta {
|
if let Some(udta) = &self.udta {
|
||||||
size += udta.box_size();
|
size += udta.box_size();
|
||||||
}
|
}
|
||||||
|
for pssh in &self.psshs {
|
||||||
|
size += pssh.box_size();
|
||||||
|
}
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,6 +76,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for MoovBox {
|
||||||
let mut udta = None;
|
let mut udta = None;
|
||||||
let mut mvex = None;
|
let mut mvex = None;
|
||||||
let mut traks = Vec::new();
|
let mut traks = Vec::new();
|
||||||
|
let mut psshs = Vec::new();
|
||||||
|
|
||||||
let mut current = reader.stream_position()?;
|
let mut current = reader.stream_position()?;
|
||||||
let end = start + size;
|
let end = start + size;
|
||||||
|
@ -100,6 +107,10 @@ impl<R: Read + Seek> ReadBox<&mut R> for MoovBox {
|
||||||
BoxType::UdtaBox => {
|
BoxType::UdtaBox => {
|
||||||
udta = Some(UdtaBox::read_box(reader, s)?);
|
udta = Some(UdtaBox::read_box(reader, s)?);
|
||||||
}
|
}
|
||||||
|
BoxType::PsshBox => {
|
||||||
|
let pssh = PsshBox::read_box(reader, s)?;
|
||||||
|
psshs.push(pssh);
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// XXX warn!()
|
// XXX warn!()
|
||||||
skip_box(reader, s)?;
|
skip_box(reader, s)?;
|
||||||
|
@ -121,6 +132,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for MoovBox {
|
||||||
udta,
|
udta,
|
||||||
mvex,
|
mvex,
|
||||||
traks,
|
traks,
|
||||||
|
psshs,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,6 +152,9 @@ impl<W: Write> WriteBox<&mut W> for MoovBox {
|
||||||
if let Some(udta) = &self.udta {
|
if let Some(udta) = &self.udta {
|
||||||
udta.write_box(writer)?;
|
udta.write_box(writer)?;
|
||||||
}
|
}
|
||||||
|
for pssh in &self.psshs {
|
||||||
|
pssh.write_box(writer)?;
|
||||||
|
}
|
||||||
Ok(0)
|
Ok(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,6 +173,7 @@ mod tests {
|
||||||
traks: vec![],
|
traks: vec![],
|
||||||
meta: Some(MetaBox::default()),
|
meta: Some(MetaBox::default()),
|
||||||
udta: Some(UdtaBox::default()),
|
udta: Some(UdtaBox::default()),
|
||||||
|
psshs: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
|
|
109
src/mp4box/pssh.rs
Normal file
109
src/mp4box/pssh.rs
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::io::{Read, Seek, Write};
|
||||||
|
|
||||||
|
use crate::mp4box::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
|
||||||
|
pub struct PsshBox {
|
||||||
|
pub version: u8,
|
||||||
|
pub system_id: [u8; 16],
|
||||||
|
pub kids: Vec<[u8; 16]>,
|
||||||
|
pub data: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PsshBox {
|
||||||
|
pub fn get_type(&self) -> BoxType {
|
||||||
|
BoxType::PsshBox
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_size(&self) -> u64 {
|
||||||
|
let mut s = HEADER_SIZE + HEADER_EXT_SIZE + 16;
|
||||||
|
if self.version > 0 {
|
||||||
|
s += 4 + (16 * self.kids.len() as u64);
|
||||||
|
}
|
||||||
|
s += 4 + self.data.len() as u64;
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mp4Box for PsshBox {
|
||||||
|
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!(
|
||||||
|
"system_id={:02x?} data_size={}",
|
||||||
|
self.system_id,
|
||||||
|
self.data.len(),
|
||||||
|
);
|
||||||
|
Ok(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read + Seek> ReadBox<&mut R> for PsshBox {
|
||||||
|
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 system_id = [0; 16];
|
||||||
|
reader.read_exact(&mut system_id)?;
|
||||||
|
|
||||||
|
let mut kids = Vec::new();
|
||||||
|
if version > 0 {
|
||||||
|
let kid_count = reader.read_u32::<BigEndian>()?;
|
||||||
|
kids.reserve(kid_count as usize);
|
||||||
|
for _ in 0..kid_count {
|
||||||
|
let mut kid = [0; 16];
|
||||||
|
reader.read_exact(&mut kid)?;
|
||||||
|
kids.push(kid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let data_size = reader.read_u32::<BigEndian>()?;
|
||||||
|
|
||||||
|
let mut data = vec![0; data_size as usize];
|
||||||
|
reader.read_exact(&mut data)?;
|
||||||
|
|
||||||
|
skip_bytes_to(reader, start + size)?;
|
||||||
|
|
||||||
|
Ok(PsshBox {
|
||||||
|
version,
|
||||||
|
system_id,
|
||||||
|
kids,
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: Write> WriteBox<&mut W> for PsshBox {
|
||||||
|
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_all(&self.system_id)?;
|
||||||
|
|
||||||
|
if self.version > 0 {
|
||||||
|
writer.write_u32::<BigEndian>(self.kids.len() as u32)?;
|
||||||
|
for kid in &self.kids {
|
||||||
|
writer.write_all(kid)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.write_u32::<BigEndian>(self.data.len() as u32)?;
|
||||||
|
writer.write_all(&self.data)?;
|
||||||
|
|
||||||
|
Ok(size)
|
||||||
|
}
|
||||||
|
}
|
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)
|
||||||
|
}
|
||||||
|
}
|
136
src/mp4box/senc.rs
Normal file
136
src/mp4box/senc.rs
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
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)
|
||||||
|
.checked_sub(reader.stream_position()?)
|
||||||
|
.ok_or(Error::InvalidData("senc size too small"))?;
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
120
src/mp4box/sinf.rs
Normal file
120
src/mp4box/sinf.rs
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
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();
|
||||||
|
|
||||||
|
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")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub tx3g: Option<Tx3gBox>,
|
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 {
|
impl StsdBox {
|
||||||
|
@ -44,6 +50,10 @@ 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 enca) = self.enca {
|
||||||
|
size += enca.box_size();
|
||||||
|
} else if let Some(ref encv) = self.encv {
|
||||||
|
size += encv.box_size();
|
||||||
}
|
}
|
||||||
size
|
size
|
||||||
}
|
}
|
||||||
|
@ -81,6 +91,8 @@ 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 enca = None;
|
||||||
|
let mut encv = None;
|
||||||
|
|
||||||
// Get box header.
|
// Get box header.
|
||||||
let header = BoxHeader::read(reader)?;
|
let header = BoxHeader::read(reader)?;
|
||||||
|
@ -107,6 +119,12 @@ 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::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,
|
vp09,
|
||||||
mp4a,
|
mp4a,
|
||||||
tx3g,
|
tx3g,
|
||||||
|
enca,
|
||||||
|
encv,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,6 +163,10 @@ 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 enca) = self.enca {
|
||||||
|
enca.write_box(writer)?;
|
||||||
|
} else if let Some(ref encv) = self.encv {
|
||||||
|
encv.write_box(writer)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(size)
|
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 std::io::{Read, Seek, Write};
|
||||||
|
|
||||||
use crate::mp4box::*;
|
use crate::mp4box::*;
|
||||||
use crate::mp4box::{tfdt::TfdtBox, tfhd::TfhdBox, trun::TrunBox};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
|
||||||
pub struct TrafBox {
|
pub struct TrafBox {
|
||||||
pub tfhd: TfhdBox,
|
pub tfhd: TfhdBox,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub tfdt: Option<TfdtBox>,
|
pub tfdt: Option<TfdtBox>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub trun: Option<TrunBox>,
|
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 {
|
impl TrafBox {
|
||||||
|
@ -25,6 +37,15 @@ impl TrafBox {
|
||||||
if let Some(ref trun) = self.trun {
|
if let Some(ref trun) = self.trun {
|
||||||
size += trun.box_size();
|
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
|
size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,6 +76,9 @@ impl<R: Read + Seek> ReadBox<&mut R> for TrafBox {
|
||||||
let mut tfhd = None;
|
let mut tfhd = None;
|
||||||
let mut tfdt = None;
|
let mut tfdt = None;
|
||||||
let mut trun = None;
|
let mut trun = None;
|
||||||
|
let mut saiz = None;
|
||||||
|
let mut saio = None;
|
||||||
|
let mut senc = None;
|
||||||
|
|
||||||
let mut current = reader.stream_position()?;
|
let mut current = reader.stream_position()?;
|
||||||
let end = start + size;
|
let end = start + size;
|
||||||
|
@ -78,6 +102,15 @@ impl<R: Read + Seek> ReadBox<&mut R> for TrafBox {
|
||||||
BoxType::TrunBox => {
|
BoxType::TrunBox => {
|
||||||
trun = Some(TrunBox::read_box(reader, s)?);
|
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!()
|
// XXX warn!()
|
||||||
skip_box(reader, s)?;
|
skip_box(reader, s)?;
|
||||||
|
@ -97,6 +130,9 @@ impl<R: Read + Seek> ReadBox<&mut R> for TrafBox {
|
||||||
tfhd: tfhd.unwrap(),
|
tfhd: tfhd.unwrap(),
|
||||||
tfdt,
|
tfdt,
|
||||||
trun,
|
trun,
|
||||||
|
saiz,
|
||||||
|
saio,
|
||||||
|
senc,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,6 +149,15 @@ impl<W: Write> WriteBox<&mut W> for TrafBox {
|
||||||
if let Some(ref trun) = self.trun {
|
if let Some(ref trun) = self.trun {
|
||||||
trun.write_box(writer)?;
|
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)
|
Ok(size)
|
||||||
}
|
}
|
||||||
|
|
127
src/track.rs
127
src/track.rs
|
@ -129,6 +129,22 @@ impl Mp4Track {
|
||||||
Ok(MediaType::AAC)
|
Ok(MediaType::AAC)
|
||||||
} else if self.trak.mdia.minf.stbl.stsd.tx3g.is_some() {
|
} else if self.trak.mdia.minf.stbl.stsd.tx3g.is_some() {
|
||||||
Ok(MediaType::TTXT)
|
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 {
|
} else {
|
||||||
Err(Error::InvalidData("unsupported media type"))
|
Err(Error::InvalidData("unsupported media type"))
|
||||||
}
|
}
|
||||||
|
@ -145,6 +161,22 @@ impl Mp4Track {
|
||||||
Ok(FourCC::from(BoxType::Mp4aBox))
|
Ok(FourCC::from(BoxType::Mp4aBox))
|
||||||
} else if self.trak.mdia.minf.stbl.stsd.tx3g.is_some() {
|
} else if self.trak.mdia.minf.stbl.stsd.tx3g.is_some() {
|
||||||
Ok(FourCC::from(BoxType::Tx3gBox))
|
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 {
|
} else {
|
||||||
Err(Error::InvalidData("unsupported sample entry box"))
|
Err(Error::InvalidData("unsupported sample entry box"))
|
||||||
}
|
}
|
||||||
|
@ -176,7 +208,15 @@ impl Mp4Track {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sample_freq_index(&self) -> Result<SampleFreqIndex> {
|
pub fn sample_freq_index(&self) -> Result<SampleFreqIndex> {
|
||||||
|
let mut mp4a_opt: Option<&Mp4aBox> = None;
|
||||||
if let Some(ref mp4a) = self.trak.mdia.minf.stbl.stsd.mp4a {
|
if let Some(ref mp4a) = self.trak.mdia.minf.stbl.stsd.mp4a {
|
||||||
|
mp4a_opt = Some(mp4a);
|
||||||
|
} else if let Some(ref enca) = self.trak.mdia.minf.stbl.stsd.enca {
|
||||||
|
if let Some(ref mp4a) = enca.mp4a {
|
||||||
|
mp4a_opt = Some(mp4a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(mp4a) = mp4a_opt {
|
||||||
if let Some(ref esds) = mp4a.esds {
|
if let Some(ref esds) = mp4a.esds {
|
||||||
SampleFreqIndex::try_from(esds.es_desc.dec_config.dec_specific.freq_index)
|
SampleFreqIndex::try_from(esds.es_desc.dec_config.dec_specific.freq_index)
|
||||||
} else {
|
} else {
|
||||||
|
@ -188,7 +228,15 @@ impl Mp4Track {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn channel_config(&self) -> Result<ChannelConfig> {
|
pub fn channel_config(&self) -> Result<ChannelConfig> {
|
||||||
|
let mut mp4a_opt: Option<&Mp4aBox> = None;
|
||||||
if let Some(ref mp4a) = self.trak.mdia.minf.stbl.stsd.mp4a {
|
if let Some(ref mp4a) = self.trak.mdia.minf.stbl.stsd.mp4a {
|
||||||
|
mp4a_opt = Some(mp4a);
|
||||||
|
} else if let Some(ref enca) = self.trak.mdia.minf.stbl.stsd.enca {
|
||||||
|
if let Some(ref mp4a) = enca.mp4a {
|
||||||
|
mp4a_opt = Some(mp4a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(mp4a) = mp4a_opt {
|
||||||
if let Some(ref esds) = mp4a.esds {
|
if let Some(ref esds) = mp4a.esds {
|
||||||
ChannelConfig::try_from(esds.es_desc.dec_config.dec_specific.chan_conf)
|
ChannelConfig::try_from(esds.es_desc.dec_config.dec_specific.chan_conf)
|
||||||
} else {
|
} else {
|
||||||
|
@ -214,7 +262,15 @@ impl Mp4Track {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bitrate(&self) -> u32 {
|
pub fn bitrate(&self) -> u32 {
|
||||||
|
let mut mp4a_opt: Option<&Mp4aBox> = None;
|
||||||
if let Some(ref mp4a) = self.trak.mdia.minf.stbl.stsd.mp4a {
|
if let Some(ref mp4a) = self.trak.mdia.minf.stbl.stsd.mp4a {
|
||||||
|
mp4a_opt = Some(mp4a);
|
||||||
|
} else if let Some(ref enca) = self.trak.mdia.minf.stbl.stsd.enca {
|
||||||
|
if let Some(ref mp4a) = enca.mp4a {
|
||||||
|
mp4a_opt = Some(mp4a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(mp4a) = mp4a_opt {
|
||||||
if let Some(ref esds) = mp4a.esds {
|
if let Some(ref esds) = mp4a.esds {
|
||||||
esds.es_desc.dec_config.avg_bitrate
|
esds.es_desc.dec_config.avg_bitrate
|
||||||
} else {
|
} else {
|
||||||
|
@ -249,7 +305,15 @@ impl Mp4Track {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn video_profile(&self) -> Result<AvcProfile> {
|
pub fn video_profile(&self) -> Result<AvcProfile> {
|
||||||
|
let mut avc1_opt: Option<&Avc1Box> = None;
|
||||||
if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 {
|
if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 {
|
||||||
|
avc1_opt = Some(avc1);
|
||||||
|
} else if let Some(ref encv) = self.trak.mdia.minf.stbl.stsd.encv {
|
||||||
|
if let Some(ref avc1) = encv.avc1 {
|
||||||
|
avc1_opt = Some(avc1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(avc1) = avc1_opt {
|
||||||
AvcProfile::try_from((
|
AvcProfile::try_from((
|
||||||
avc1.avcc.avc_profile_indication,
|
avc1.avcc.avc_profile_indication,
|
||||||
avc1.avcc.profile_compatibility,
|
avc1.avcc.profile_compatibility,
|
||||||
|
@ -260,8 +324,16 @@ impl Mp4Track {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sequence_parameter_set(&self) -> Result<&[u8]> {
|
pub fn sequence_parameter_set(&self) -> Result<&[u8]> {
|
||||||
|
let mut avc1_opt: Option<&Avc1Box> = None;
|
||||||
if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 {
|
if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 {
|
||||||
match avc1.avcc.sequence_parameter_sets.get(0) {
|
avc1_opt = Some(avc1);
|
||||||
|
} else if let Some(ref encv) = self.trak.mdia.minf.stbl.stsd.encv {
|
||||||
|
if let Some(ref avc1) = encv.avc1 {
|
||||||
|
avc1_opt = Some(avc1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(avc1) = avc1_opt {
|
||||||
|
match avc1.avcc.sequence_parameter_sets.first() {
|
||||||
Some(nal) => Ok(nal.bytes.as_ref()),
|
Some(nal) => Ok(nal.bytes.as_ref()),
|
||||||
None => Err(Error::EntryInStblNotFound(
|
None => Err(Error::EntryInStblNotFound(
|
||||||
self.track_id(),
|
self.track_id(),
|
||||||
|
@ -275,8 +347,16 @@ impl Mp4Track {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn picture_parameter_set(&self) -> Result<&[u8]> {
|
pub fn picture_parameter_set(&self) -> Result<&[u8]> {
|
||||||
|
let mut avc1_opt: Option<&Avc1Box> = None;
|
||||||
if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 {
|
if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 {
|
||||||
match avc1.avcc.picture_parameter_sets.get(0) {
|
avc1_opt = Some(avc1);
|
||||||
|
} else if let Some(ref encv) = self.trak.mdia.minf.stbl.stsd.encv {
|
||||||
|
if let Some(ref avc1) = encv.avc1 {
|
||||||
|
avc1_opt = Some(avc1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(avc1) = avc1_opt {
|
||||||
|
match avc1.avcc.picture_parameter_sets.first() {
|
||||||
Some(nal) => Ok(nal.bytes.as_ref()),
|
Some(nal) => Ok(nal.bytes.as_ref()),
|
||||||
None => Err(Error::EntryInStblNotFound(
|
None => Err(Error::EntryInStblNotFound(
|
||||||
self.track_id(),
|
self.track_id(),
|
||||||
|
@ -290,7 +370,15 @@ impl Mp4Track {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn audio_profile(&self) -> Result<AudioObjectType> {
|
pub fn audio_profile(&self) -> Result<AudioObjectType> {
|
||||||
|
let mut mp4a_opt: Option<&Mp4aBox> = None;
|
||||||
if let Some(ref mp4a) = self.trak.mdia.minf.stbl.stsd.mp4a {
|
if let Some(ref mp4a) = self.trak.mdia.minf.stbl.stsd.mp4a {
|
||||||
|
mp4a_opt = Some(mp4a);
|
||||||
|
} else if let Some(ref enca) = self.trak.mdia.minf.stbl.stsd.enca {
|
||||||
|
if let Some(ref mp4a) = enca.mp4a {
|
||||||
|
mp4a_opt = Some(mp4a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(mp4a) = mp4a_opt {
|
||||||
if let Some(ref esds) = mp4a.esds {
|
if let Some(ref esds) = mp4a.esds {
|
||||||
AudioObjectType::try_from(esds.es_desc.dec_config.dec_specific.profile)
|
AudioObjectType::try_from(esds.es_desc.dec_config.dec_specific.profile)
|
||||||
} else {
|
} else {
|
||||||
|
@ -301,6 +389,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> {
|
fn stsc_index(&self, sample_id: u32) -> Result<usize> {
|
||||||
if self.trak.mdia.minf.stbl.stsc.entries.is_empty() {
|
if self.trak.mdia.minf.stbl.stsc.entries.is_empty() {
|
||||||
return Err(Error::InvalidData("no stsc entries"));
|
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 {
|
impl fmt::Debug for FourCC {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let code: u32 = self.into();
|
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())
|
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