Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,8 @@ license = "MIT"
include = ["src", "benches", "Cargo.toml", "README", "LICENSE"]

[dependencies]
thiserror = "^1.0"
byteorder = "1"
bytes = "1.1.0"
num-rational = { version = "0.4.0", features = ["serde"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

[dev-dependencies]
Expand Down
57 changes: 42 additions & 15 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,56 @@
use thiserror::Error;

use std::fmt;
use crate::mp4box::BoxType;

#[derive(Error, Debug)]
#[derive(Debug)]
pub enum Error {
#[error("{0}")]
IoError(#[from] std::io::Error),
#[error("{0}")]
IoError(std::io::Error),
InvalidData(&'static str),
#[error("{0} not found")]
BoxNotFound(BoxType),
#[error("{0} and {1} not found")]
Box2NotFound(BoxType, BoxType),
#[error("trak[{0}] not found")]
TrakNotFound(u32),
#[error("trak[{0}].{1} not found")]
BoxInTrakNotFound(u32, BoxType),
#[error("traf[{0}].{1} not found")]
BoxInTrafNotFound(u32, BoxType),
#[error("trak[{0}].stbl.{1} not found")]
BoxInStblNotFound(u32, BoxType),
#[error("trak[{0}].stbl.{1}.entry[{2}] not found")]
EntryInStblNotFound(u32, BoxType, u32),
#[error("traf[{0}].trun.{1}.entry[{2}] not found")]
EntryInTrunNotFound(u32, BoxType, u32),
#[error("{0} version {1} is not supported")]
UnsupportedBoxVersion(BoxType, u8),
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::IoError(e) => write!(f, "{}", e),
Error::InvalidData(s) => write!(f, "{}", s),
Error::BoxNotFound(t) => write!(f, "{} not found", t),
Error::Box2NotFound(t1, t2) => write!(f, "{} and {} not found", t1, t2),
Error::TrakNotFound(id) => write!(f, "trak[{}] not found", id),
Error::BoxInTrakNotFound(id, t) => write!(f, "trak[{}].{} not found", id, t),
Error::BoxInTrafNotFound(id, t) => write!(f, "traf[{}].{} not found", id, t),
Error::BoxInStblNotFound(id, t) => write!(f, "trak[{}].stbl.{} not found", id, t),
Error::EntryInStblNotFound(id, t, entry) => {
write!(f, "trak[{}].stbl.{}.entry[{}] not found", id, t, entry)
}
Error::EntryInTrunNotFound(id, t, entry) => {
write!(f, "traf[{}].trun.{}.entry[{}] not found", id, t, entry)
}
Error::UnsupportedBoxVersion(t, v) => {
write!(f, "{} version {} is not supported", t, v)
}
}
}
}

impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::IoError(e) => Some(e),
_ => None,
}
}
}

impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
Error::IoError(err)
}
}
54 changes: 31 additions & 23 deletions src/mp4box/avc1.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use serde::Serialize;
use std::io::{Read, Seek, Write};

use crate::mp4box::*;

#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Avc1Box {
pub data_reference_index: u16,
pub width: u16,
pub height: u16,

#[serde(with = "value_u32")]
pub horizresolution: FixedPointU16,

#[serde(with = "value_u32")]
pub vertresolution: FixedPointU16,
pub frame_count: u16,
pub depth: u16,
Expand Down Expand Up @@ -68,7 +63,17 @@ impl Mp4Box for Avc1Box {
}

fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
let j = serde_json::json!({
"data_reference_index": self.data_reference_index,
"width": self.width,
"height": self.height,
"horizresolution": self.horizresolution.raw_value(),
"vertresolution": self.vertresolution.raw_value(),
"frame_count": self.frame_count,
"depth": self.depth,
"avcc": serde_json::from_str::<serde_json::Value>(&self.avcc.to_json()?).unwrap_or(serde_json::Value::Null),
});
Ok(j.to_string())
}

fn summary(&self) -> Result<String> {
Expand Down Expand Up @@ -116,9 +121,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for Avc1Box {
}
if name == BoxType::AvcCBox {
let avcc = AvcCBox::read_box(reader, s)?;

skip_bytes_to(reader, start + size)?;

return Ok(Avc1Box {
data_reference_index,
width,
Expand Down Expand Up @@ -154,18 +157,16 @@ impl<W: Write> WriteBox<&mut W> for Avc1Box {
writer.write_u32::<BigEndian>(self.vertresolution.raw_value())?;
writer.write_u32::<BigEndian>(0)?; // reserved
writer.write_u16::<BigEndian>(self.frame_count)?;
// skip compressorname
write_zeros(writer, 32)?;
writer.write_u16::<BigEndian>(self.depth)?;
writer.write_i16::<BigEndian>(-1)?; // pre-defined

self.avcc.write_box(writer)?;

Ok(size)
}
}

#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct AvcCBox {
pub configuration_version: u8,
pub avc_profile_indication: u8,
Expand Down Expand Up @@ -207,12 +208,23 @@ impl Mp4Box for AvcCBox {
}

fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
let sps: Vec<Vec<u8>> = self.sequence_parameter_sets.iter().map(|n| n.bytes.clone()).collect();
let pps: Vec<Vec<u8>> = self.picture_parameter_sets.iter().map(|n| n.bytes.clone()).collect();

let j = serde_json::json!({
"configuration_version": self.configuration_version,
"avc_profile_indication": self.avc_profile_indication,
"profile_compatibility": self.profile_compatibility,
"avc_level_indication": self.avc_level_indication,
"length_size_minus_one": self.length_size_minus_one,
"sequence_parameter_sets": sps,
"picture_parameter_sets": pps,
});
Ok(j.to_string())
}

fn summary(&self) -> Result<String> {
let s = format!("avc_profile_indication={}", self.avc_profile_indication);
Ok(s)
Ok(format!("avc_profile_indication={}", self.avc_profile_indication))
}
}

Expand All @@ -228,14 +240,12 @@ impl<R: Read + Seek> ReadBox<&mut R> for AvcCBox {
let num_of_spss = reader.read_u8()? & 0x1F;
let mut sequence_parameter_sets = Vec::with_capacity(num_of_spss as usize);
for _ in 0..num_of_spss {
let nal_unit = NalUnit::read(reader)?;
sequence_parameter_sets.push(nal_unit);
sequence_parameter_sets.push(NalUnit::read(reader)?);
}
let num_of_ppss = reader.read_u8()?;
let mut picture_parameter_sets = Vec::with_capacity(num_of_ppss as usize);
for _ in 0..num_of_ppss {
let nal_unit = NalUnit::read(reader)?;
picture_parameter_sets.push(nal_unit);
picture_parameter_sets.push(NalUnit::read(reader)?);
}

skip_bytes_to(reader, start + size)?;
Expand Down Expand Up @@ -274,16 +284,14 @@ impl<W: Write> WriteBox<&mut W> for AvcCBox {
}
}

#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct NalUnit {
pub bytes: Vec<u8>,
}

impl From<&[u8]> for NalUnit {
fn from(bytes: &[u8]) -> Self {
Self {
bytes: bytes.to_vec(),
}
Self { bytes: bytes.to_vec() }
}
}

Expand Down
13 changes: 8 additions & 5 deletions src/mp4box/co64.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use serde::Serialize;
use std::io::{Read, Seek, Write};
use std::mem::size_of;

use crate::mp4box::*;

#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Eq, Default)] // Removed Serialize
pub struct Co64Box {
pub version: u8,
pub flags: u32,

#[serde(skip_serializing)]
pub entries: Vec<u64>,
}

Expand All @@ -34,7 +31,11 @@ impl Mp4Box for Co64Box {
}

fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
let j = serde_json::json!({
"version": self.version,
"flags": self.flags,
});
Ok(j.to_string())
}

fn summary(&self) -> Result<String> {
Expand All @@ -53,6 +54,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for Co64Box {
let other_size = size_of::<u32>(); // entry_count
let entry_size = size_of::<u64>(); // chunk_offset
let entry_count = reader.read_u32::<BigEndian>()?;

if u64::from(entry_count)
> size
.saturating_sub(header_size)
Expand All @@ -63,6 +65,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for Co64Box {
"co64 entry_count indicates more entries than could fit in the box",
));
}

let mut entries = Vec::with_capacity(entry_count as usize);
for _i in 0..entry_count {
let chunk_offset = reader.read_u64::<BigEndian>()?;
Expand Down
21 changes: 12 additions & 9 deletions src/mp4box/ctts.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use serde::Serialize;
use std::io::{Read, Seek, Write};
use std::mem::size_of;

use crate::mp4box::*;

#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Eq, Default)] // Removed Serialize
pub struct CttsBox {
pub version: u8,
pub flags: u32,

#[serde(skip_serializing)]
pub entries: Vec<CttsEntry>,
}

Expand All @@ -24,7 +21,7 @@ impl CttsBox {
}
}

#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Eq, Default)] // Removed Serialize
pub struct CttsEntry {
pub sample_count: u32,
pub sample_offset: i32,
Expand All @@ -40,7 +37,12 @@ impl Mp4Box for CttsBox {
}

fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
// Manually construct JSON to skip 'entries'
let j = serde_json::json!({
"version": self.version,
"flags": self.flags,
});
Ok(j.to_string())
}

fn summary(&self) -> Result<String> {
Expand All @@ -57,9 +59,9 @@ impl<R: Read + Seek> ReadBox<&mut R> for CttsBox {

let header_size = HEADER_SIZE + HEADER_EXT_SIZE;
let entry_count = reader.read_u32::<BigEndian>()?;
let entry_size = size_of::<u32>() + size_of::<i32>(); // sample_count + sample_offset
// (sample_offset might be a u32, but the size is the same.)
let other_size = size_of::<i32>(); // entry_count
let entry_size = size_of::<u32>() + size_of::<i32>();
let other_size = size_of::<u32>(); // entry_count is u32

if u64::from(entry_count)
> size
.saturating_sub(header_size)
Expand All @@ -70,6 +72,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for CttsBox {
"ctts entry_count indicates more entries than could fit in the box",
));
}

let mut entries = Vec::with_capacity(entry_count as usize);
for _ in 0..entry_count {
let entry = CttsEntry {
Expand Down
17 changes: 11 additions & 6 deletions src/mp4box/data.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::{
convert::TryFrom,
io::{Read, Seek},
io::{Read, Seek, Write},
};

use serde::Serialize;

use crate::mp4box::*;

#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Eq, Default)] // Removed Serialize
pub struct DataBox {
pub data: Vec<u8>,
pub data_type: DataType,
Expand Down Expand Up @@ -37,7 +36,12 @@ impl Mp4Box for DataBox {
}

fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
// Manually construct JSON to avoid needing Serialize trait on the struct
let j = serde_json::json!({
"data_type": format!("{:?}", self.data_type),
"data_len": self.data.len(),
});
Ok(j.to_string())
}

fn summary(&self) -> Result<String> {
Expand All @@ -55,7 +59,8 @@ impl<R: Read + Seek> ReadBox<&mut R> for DataBox {
reader.read_u32::<BigEndian>()?; // reserved = 0

let current = reader.stream_position()?;
let mut data = vec![0u8; (start + size - current) as usize];
let remaining = (start + size).saturating_sub(current);
let mut data = vec![0u8; remaining as usize];
reader.read_exact(&mut data)?;

Ok(DataBox { data, data_type })
Expand Down
Loading
Loading