diff --git a/src/mp4box/dinf.rs b/src/mp4box/dinf.rs new file mode 100644 index 0000000..8b10848 --- /dev/null +++ b/src/mp4box/dinf.rs @@ -0,0 +1,296 @@ +use std::io::{Read, Seek, Write}; +use serde::{Serialize}; + +use crate::mp4box::*; + +#[derive(Debug, Clone, PartialEq, Default, Serialize)] +pub struct DinfBox { + dref: DrefBox, +} + +impl DinfBox { + pub fn get_type(&self) -> BoxType { + BoxType::DinfBox + } + + pub fn get_size(&self) -> u64 { + HEADER_SIZE + self.dref.box_size() + } +} + +impl Mp4Box for DinfBox { + fn box_type(&self) -> BoxType { + return self.get_type(); + } + + fn box_size(&self) -> u64 { + return self.get_size(); + } + + fn to_json(&self) -> Result { + Ok(serde_json::to_string(&self).unwrap()) + } + + fn summary(&self) -> Result { + let s = format!(""); + Ok(s) + } +} + +impl ReadBox<&mut R> for DinfBox { + fn read_box(reader: &mut R, size: u64) -> Result { + let start = box_start(reader)?; + + let mut dref = None; + + let mut current = reader.seek(SeekFrom::Current(0))?; + let end = start + size; + while current < end { + // Get box header. + let header = BoxHeader::read(reader)?; + let BoxHeader { name, size: s } = header; + + match name { + BoxType::DrefBox => { + dref = Some(DrefBox::read_box(reader, s)?); + } + _ => { + // XXX warn!() + skip_box(reader, s)?; + } + } + + current = reader.seek(SeekFrom::Current(0))?; + } + + if dref.is_none() { + return Err(Error::BoxNotFound(BoxType::DrefBox)); + } + + skip_bytes_to(reader, start + size)?; + + Ok(DinfBox { + dref: dref.unwrap(), + }) + } +} + +impl WriteBox<&mut W> for DinfBox { + fn write_box(&self, writer: &mut W) -> Result { + let size = self.box_size(); + BoxHeader::new(self.box_type(), size).write(writer)?; + self.dref.write_box(writer)?; + Ok(size) + } +} + +#[derive(Debug, Clone, PartialEq, Serialize)] +pub struct DrefBox { + pub version: u8, + pub flags: u32, + + #[serde(skip_serializing_if = "Option::is_none")] + pub url: Option, +} + +impl Default for DrefBox { + fn default() -> Self { + DrefBox { + version: 0, + flags: 0, + url: Some(UrlBox::default()), + } + } +} + +impl DrefBox { + pub fn get_type(&self) -> BoxType { + BoxType::DrefBox + } + + pub fn get_size(&self) -> u64 { + let mut size = HEADER_SIZE + HEADER_EXT_SIZE + 4; + if let Some(ref url) = self.url { + size += url.box_size(); + } + size + } +} + +impl Mp4Box for DrefBox { + fn box_type(&self) -> BoxType { + return self.get_type(); + } + + fn box_size(&self) -> u64 { + return self.get_size(); + } + + fn to_json(&self) -> Result { + Ok(serde_json::to_string(&self).unwrap()) + } + + fn summary(&self) -> Result { + let s = format!(""); + Ok(s) + } +} + +impl ReadBox<&mut R> for DrefBox { + fn read_box(reader: &mut R, size: u64) -> Result { + let start = box_start(reader)?; + + let mut current = reader.seek(SeekFrom::Current(0))?; + + let (version, flags) = read_box_header_ext(reader)?; + let end = start + size; + + let mut url = None; + + let entry_count = reader.read_u32::()?; + for _i in 0..entry_count { + if current >= end { + break; + } + + // Get box header. + let header = BoxHeader::read(reader)?; + let BoxHeader { name, size: s } = header; + + match name { + BoxType::UrlBox => { + url = Some(UrlBox::read_box(reader, s)?); + } + _ => { + skip_box(reader, s)?; + } + } + + current = reader.seek(SeekFrom::Current(0))?; + } + + skip_bytes_to(reader, start + size)?; + + Ok(DrefBox { + version, + flags, + url, + }) + } +} + +impl WriteBox<&mut W> for DrefBox { + fn write_box(&self, writer: &mut W) -> Result { + 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::(1)?; + + if let Some(ref url) = self.url { + url.write_box(writer)?; + } + + Ok(size) + } +} + +#[derive(Debug, Clone, PartialEq, Serialize)] +pub struct UrlBox { + pub version: u8, + pub flags: u32, + pub location: String, +} + +impl Default for UrlBox { + fn default() -> Self { + UrlBox { + version: 0, + flags: 1, + location: String::default(), + } + } +} + +impl UrlBox { + pub fn get_type(&self) -> BoxType { + BoxType::UrlBox + } + + pub fn get_size(&self) -> u64 { + let mut size = HEADER_SIZE + HEADER_EXT_SIZE; + + if ! self.location.is_empty() { + size += self.location.bytes().len() as u64 + 1; + } + + size + } +} + +impl Mp4Box for UrlBox { + fn box_type(&self) -> BoxType { + return self.get_type(); + } + + fn box_size(&self) -> u64 { + return self.get_size(); + } + + fn to_json(&self) -> Result { + Ok(serde_json::to_string(&self).unwrap()) + } + + fn summary(&self) -> Result { + let s = format!("location={}", self.location); + Ok(s) + } +} + +impl ReadBox<&mut R> for UrlBox { + fn read_box(reader: &mut R, size: u64) -> Result { + let start = box_start(reader)?; + + let (version, flags) = read_box_header_ext(reader)?; + + let location = if size - HEADER_SIZE - HEADER_EXT_SIZE > 0 { + let buf_size = size - HEADER_SIZE - HEADER_EXT_SIZE - 1; + let mut buf = vec![0u8; buf_size as usize]; + reader.read_exact(&mut buf)?; + match String::from_utf8(buf) { + Ok(t) => { + assert_eq!(t.len(), buf_size as usize); + t + } + _ => String::default(), + } + } else { + String::default() + }; + + skip_bytes_to(reader, start + size)?; + + Ok(UrlBox { + version, + flags, + location, + }) + } +} + +impl WriteBox<&mut W> for UrlBox { + fn write_box(&self, writer: &mut W) -> Result { + let size = self.box_size(); + BoxHeader::new(self.box_type(), size).write(writer)?; + + write_box_header_ext(writer, self.version, self.flags)?; + + if ! self.location.is_empty() { + writer.write(self.location.as_bytes())?; + writer.write_u8(0)?; + } + + Ok(size) + } +} diff --git a/src/mp4box/minf.rs b/src/mp4box/minf.rs index fa5422f..fac40de 100644 --- a/src/mp4box/minf.rs +++ b/src/mp4box/minf.rs @@ -2,7 +2,7 @@ use std::io::{Read, Seek, SeekFrom, Write}; use serde::{Serialize}; use crate::mp4box::*; -use crate::mp4box::{smhd::SmhdBox, stbl::StblBox, vmhd::VmhdBox}; +use crate::mp4box::{dinf::DinfBox, smhd::SmhdBox, stbl::StblBox, vmhd::VmhdBox}; #[derive(Debug, Clone, PartialEq, Default, Serialize)] pub struct MinfBox { @@ -12,6 +12,7 @@ pub struct MinfBox { #[serde(skip_serializing_if = "Option::is_none")] pub smhd: Option, + pub dinf: DinfBox, pub stbl: StblBox, } @@ -28,6 +29,7 @@ impl MinfBox { if let Some(ref smhd) = self.smhd { size += smhd.box_size(); } + size += self.dinf.box_size(); size += self.stbl.box_size(); size } @@ -58,6 +60,7 @@ impl ReadBox<&mut R> for MinfBox { let mut vmhd = None; let mut smhd = None; + let mut dinf = None; let mut stbl = None; let mut current = reader.seek(SeekFrom::Current(0))?; @@ -75,8 +78,7 @@ impl ReadBox<&mut R> for MinfBox { smhd = Some(SmhdBox::read_box(reader, s)?); } BoxType::DinfBox => { - // XXX warn!() - skip_box(reader, s)?; + dinf = Some(DinfBox::read_box(reader, s)?); } BoxType::StblBox => { stbl = Some(StblBox::read_box(reader, s)?); @@ -90,6 +92,9 @@ impl ReadBox<&mut R> for MinfBox { current = reader.seek(SeekFrom::Current(0))?; } + if dinf.is_none() { + return Err(Error::BoxNotFound(BoxType::DinfBox)); + } if stbl.is_none() { return Err(Error::BoxNotFound(BoxType::StblBox)); } @@ -99,6 +104,7 @@ impl ReadBox<&mut R> for MinfBox { Ok(MinfBox { vmhd, smhd, + dinf: dinf.unwrap(), stbl: stbl.unwrap(), }) } @@ -115,6 +121,7 @@ impl WriteBox<&mut W> for MinfBox { if let Some(ref smhd) = self.smhd { smhd.write_box(writer)?; } + self.dinf.write_box(writer)?; self.stbl.write_box(writer)?; Ok(size) diff --git a/src/mp4box/mod.rs b/src/mp4box/mod.rs index a5319bd..de660ab 100644 --- a/src/mp4box/mod.rs +++ b/src/mp4box/mod.rs @@ -18,21 +18,23 @@ //! mdhd //! hdlr //! minf -//! stbl -//! stsd -//! avc1 -//! hev1 -//! mp4a -//! tx3g -//! stts -//! stsc -//! stsz -//! stss -//! stco -//! co64 -//! ctts -//! smhd -//! vmhd +//! stbl +//! stsd +//! avc1 +//! hev1 +//! mp4a +//! tx3g +//! stts +//! stsc +//! stsz +//! stss +//! stco +//! co64 +//! ctts +//! dinf +//! dref +//! smhd +//! vmhd //! edts //! elst //! mvex @@ -56,6 +58,7 @@ use crate::*; pub(crate) mod avc1; pub(crate) mod co64; pub(crate) mod ctts; +pub(crate) mod dinf; pub(crate) mod edts; pub(crate) mod elst; pub(crate) mod ftyp; @@ -158,6 +161,8 @@ boxtype! { TrunBox => 0x7472756E, UdtaBox => 0x75647461, DinfBox => 0x64696e66, + DrefBox => 0x64726566, + UrlBox => 0x75726C20, SmhdBox => 0x736d6864, Avc1Box => 0x61766331, AvcCBox => 0x61766343, diff --git a/src/mp4box/mp4a.rs b/src/mp4box/mp4a.rs index df2b35d..fc8ec69 100644 --- a/src/mp4box/mp4a.rs +++ b/src/mp4box/mp4a.rs @@ -150,7 +150,8 @@ impl Mp4Box for EsdsBox { } fn box_size(&self) -> u64 { - HEADER_SIZE + HEADER_EXT_SIZE + ESDescriptor::desc_size() as u64 + HEADER_SIZE + HEADER_EXT_SIZE + + 1 + size_of_length(ESDescriptor::desc_size()) as u64 + ESDescriptor::desc_size() as u64 } fn to_json(&self) -> Result { @@ -224,11 +225,6 @@ trait WriteDesc: Sized { fn write_desc(&self, _: T) -> Result; } -// XXX assert_eq!(size, 1) -fn desc_start(reader: &mut R) -> Result { - Ok(reader.seek(SeekFrom::Current(0))? - 2) -} - fn read_desc(reader: &mut R) -> Result<(u8, u32)> { let tag = reader.read_u8()?; @@ -244,6 +240,15 @@ fn read_desc(reader: &mut R) -> Result<(u8, u32)> { Ok((tag, size)) } +fn size_of_length(size: u32) -> u32 { + match size { + 0x0..=0x7F => 1, + 0x80..=0x3FFF => 2, + 0x4000..=0x1FFFFF => 3, + _ => 4, + } +} + fn write_desc(writer: &mut W, tag: u8, size: u32) -> Result { writer.write_u8(tag)?; @@ -251,12 +256,7 @@ fn write_desc(writer: &mut W, tag: u8, size: u32) -> Result { return Err(Error::InvalidData("invalid descriptor length range")); } - let nbytes = match size { - 0x0..=0x7F => 1, - 0x80..=0x3FFF => 2, - 0x4000..=0x1FFFFF => 3, - _ => 4, - }; + let nbytes = size_of_length(size); for i in 0..nbytes { let mut b = (size >> ((nbytes - i - 1) * 7)) as u8 & 0x7F; @@ -266,7 +266,7 @@ fn write_desc(writer: &mut W, tag: u8, size: u32) -> Result { writer.write_u8(b)?; } - Ok(1 + nbytes) + Ok(1 + nbytes as u64) } #[derive(Debug, Clone, PartialEq, Default, Serialize)] @@ -292,15 +292,16 @@ impl Descriptor for ESDescriptor { 0x03 } - // XXX size > 0x7F fn desc_size() -> u32 { - 2 + 3 + DecoderConfigDescriptor::desc_size() + SLConfigDescriptor::desc_size() + 3 + + 1 + size_of_length(DecoderConfigDescriptor::desc_size()) + DecoderConfigDescriptor::desc_size() + + 1 + size_of_length(SLConfigDescriptor::desc_size()) + SLConfigDescriptor::desc_size() } } impl ReadDesc<&mut R> for ESDescriptor { fn read_desc(reader: &mut R, size: u32) -> Result { - let start = desc_start(reader)?; + let start = reader.seek(SeekFrom::Current(0))?; let es_id = reader.read_u16::()?; reader.read_u8()?; // XXX flags must be 0 @@ -309,7 +310,7 @@ impl ReadDesc<&mut R> for ESDescriptor { let mut sl_config = None; let mut current = reader.seek(SeekFrom::Current(0))?; - let end = start + size as u64 + 1; + let end = start + size as u64; while current < end { let (desc_tag, desc_size) = read_desc(reader)?; match desc_tag { @@ -320,7 +321,7 @@ impl ReadDesc<&mut R> for ESDescriptor { sl_config = Some(SLConfigDescriptor::read_desc(reader, desc_size)?); } _ => { - skip_bytes(reader, desc_size as u64 - 1)?; + skip_bytes(reader, desc_size as u64)?; } } current = reader.seek(SeekFrom::Current(0))?; @@ -337,7 +338,7 @@ impl ReadDesc<&mut R> for ESDescriptor { impl WriteDesc<&mut W> for ESDescriptor { fn write_desc(&self, writer: &mut W) -> Result { let size = Self::desc_size(); - write_desc(writer, Self::desc_tag(), size - 1)?; + write_desc(writer, Self::desc_tag(), size)?; writer.write_u16::(self.es_id)?; writer.write_u8(0)?; @@ -380,15 +381,14 @@ impl Descriptor for DecoderConfigDescriptor { 0x04 } - // XXX size > 0x7F fn desc_size() -> u32 { - 2 + 13 + DecoderSpecificDescriptor::desc_size() + 13 + 1 + size_of_length(DecoderSpecificDescriptor::desc_size()) + DecoderSpecificDescriptor::desc_size() } } impl ReadDesc<&mut R> for DecoderConfigDescriptor { fn read_desc(reader: &mut R, size: u32) -> Result { - let start = desc_start(reader)?; + let start = reader.seek(SeekFrom::Current(0))?; let object_type_indication = reader.read_u8()?; let byte_a = reader.read_u8()?; @@ -401,7 +401,7 @@ impl ReadDesc<&mut R> for DecoderConfigDescriptor { let mut dec_specific = None; let mut current = reader.seek(SeekFrom::Current(0))?; - let end = start + size as u64 + 1; + let end = start + size as u64; while current < end { let (desc_tag, desc_size) = read_desc(reader)?; match desc_tag { @@ -409,7 +409,7 @@ impl ReadDesc<&mut R> for DecoderConfigDescriptor { dec_specific = Some(DecoderSpecificDescriptor::read_desc(reader, desc_size)?); } _ => { - skip_bytes(reader, desc_size as u64 - 1)?; + skip_bytes(reader, desc_size as u64)?; } } current = reader.seek(SeekFrom::Current(0))?; @@ -430,10 +430,10 @@ impl ReadDesc<&mut R> for DecoderConfigDescriptor { impl WriteDesc<&mut W> for DecoderConfigDescriptor { fn write_desc(&self, writer: &mut W) -> Result { let size = Self::desc_size(); - write_desc(writer, Self::desc_tag(), size - 1)?; + write_desc(writer, Self::desc_tag(), size)?; writer.write_u8(self.object_type_indication)?; - writer.write_u8((self.stream_type << 2) + (self.up_stream & 0x02))?; + writer.write_u8((self.stream_type << 2) + (self.up_stream & 0x02) + 1)?; // 1 reserved writer.write_u24::(self.buffer_size_db)?; writer.write_u32::(self.max_bitrate)?; writer.write_u32::(self.avg_bitrate)?; @@ -466,9 +466,8 @@ impl Descriptor for DecoderSpecificDescriptor { 0x05 } - // XXX size > 0x7F fn desc_size() -> u32 { - 2 + 2 + 2 } } @@ -491,7 +490,7 @@ impl ReadDesc<&mut R> for DecoderSpecificDescriptor { impl WriteDesc<&mut W> for DecoderSpecificDescriptor { fn write_desc(&self, writer: &mut W) -> Result { let size = Self::desc_size(); - write_desc(writer, Self::desc_tag(), size - 1)?; + write_desc(writer, Self::desc_tag(), size)?; writer.write_u8((self.profile << 3) + (self.freq_index >> 1))?; writer.write_u8((self.freq_index << 7) + (self.chan_conf << 3))?; @@ -514,9 +513,8 @@ impl Descriptor for SLConfigDescriptor { 0x06 } - // XXX size > 0x7F fn desc_size() -> u32 { - 2 + 1 + 1 } } diff --git a/src/mp4box/tkhd.rs b/src/mp4box/tkhd.rs index b835e5d..75de65d 100644 --- a/src/mp4box/tkhd.rs +++ b/src/mp4box/tkhd.rs @@ -4,6 +4,12 @@ use serde::{Serialize}; use crate::mp4box::*; +pub enum TrackFlag { + TrackEnabled = 0x000001, + // TrackInMovie = 0x000002, + // TrackInPreview = 0x000004, +} + #[derive(Debug, Clone, PartialEq, Serialize)] pub struct TkhdBox { pub version: u8, @@ -30,7 +36,7 @@ impl Default for TkhdBox { fn default() -> Self { TkhdBox { version: 0, - flags: 0, + flags: TrackFlag::TrackEnabled as u32, creation_time: 0, modification_time: 0, track_id: 0, @@ -225,7 +231,7 @@ mod tests { fn test_tkhd32() { let src_box = TkhdBox { version: 0, - flags: 0, + flags: TrackFlag::TrackEnabled as u32, creation_time: 100, modification_time: 200, track_id: 1, @@ -264,7 +270,7 @@ mod tests { fn test_tkhd64() { let src_box = TkhdBox { version: 1, - flags: 0, + flags: TrackFlag::TrackEnabled as u32, creation_time: 100, modification_time: 200, track_id: 1,