diff --git a/src/mp4box/avc1.rs b/src/mp4box/avc.rs similarity index 66% rename from src/mp4box/avc1.rs rename to src/mp4box/avc.rs index f386f9a..21e9da0 100644 --- a/src/mp4box/avc1.rs +++ b/src/mp4box/avc.rs @@ -5,7 +5,7 @@ use std::io::{Read, Seek, Write}; use crate::mp4box::*; #[derive(Debug, Clone, PartialEq, Eq, Serialize)] -pub struct Avc1Box { +pub struct Avc1Or3Inner { pub data_reference_index: u16, pub width: u16, pub height: u16, @@ -20,9 +20,9 @@ pub struct Avc1Box { pub avcc: AvcCBox, } -impl Default for Avc1Box { +impl Default for Avc1Or3Inner { fn default() -> Self { - Avc1Box { + Avc1Or3Inner { data_reference_index: 0, width: 0, height: 0, @@ -35,9 +35,9 @@ impl Default for Avc1Box { } } -impl Avc1Box { +impl Avc1Or3Inner { pub fn new(config: &AvcConfig) -> Self { - Avc1Box { + Avc1Or3Inner { data_reference_index: 1, width: config.width, height: config.height, @@ -49,41 +49,13 @@ impl Avc1Box { } } - pub fn get_type(&self) -> BoxType { - BoxType::Avc1Box - } - - pub fn get_size(&self) -> u64 { - HEADER_SIZE + 8 + 70 + self.avcc.box_size() + pub fn size(&self) -> u64 { + 8 + 70 + self.avcc.box_size() } } -impl Mp4Box for Avc1Box { - fn box_type(&self) -> BoxType { - self.get_type() - } - - fn box_size(&self) -> u64 { - self.get_size() - } - - fn to_json(&self) -> Result { - Ok(serde_json::to_string(&self).unwrap()) - } - - fn summary(&self) -> Result { - let s = format!( - "data_reference_index={} width={} height={} frame_count={}", - self.data_reference_index, self.width, self.height, self.frame_count - ); - Ok(s) - } -} - -impl ReadBox<&mut R> for Avc1Box { - fn read_box(reader: &mut R, size: u64) -> Result { - let start = box_start(reader)?; - +impl Avc1Or3Inner { + fn read(reader: &mut R, size: u64, start: u64) -> Result { reader.read_u32::()?; // reserved reader.read_u16::()?; // reserved let data_reference_index = reader.read_u16::()?; @@ -119,7 +91,7 @@ impl ReadBox<&mut R> for Avc1Box { skip_bytes_to(reader, start + size)?; - return Ok(Avc1Box { + return Ok(Avc1Or3Inner { data_reference_index, width, height, @@ -134,13 +106,8 @@ impl ReadBox<&mut R> for Avc1Box { } } } -} - -impl WriteBox<&mut W> for Avc1Box { - fn write_box(&self, writer: &mut W) -> Result { - let size = self.box_size(); - BoxHeader::new(self.box_type(), size).write(writer)?; + fn write(&self, writer: &mut W) -> Result { writer.write_u32::(0)?; // reserved writer.write_u16::(0)?; // reserved writer.write_u16::(self.data_reference_index)?; @@ -161,6 +128,140 @@ impl WriteBox<&mut W> for Avc1Box { self.avcc.write_box(writer)?; + Ok(self.size()) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default)] +pub struct Avc1Box { + pub inner: Avc1Or3Inner, +} + +impl Avc1Box { + pub fn new(config: &AvcConfig) -> Self { + Avc1Box { + inner: Avc1Or3Inner::new(config), + } + } + + pub fn get_type(&self) -> BoxType { + BoxType::Avc1Box + } + + pub fn get_size(&self) -> u64 { + HEADER_SIZE + self.inner.size() + } +} + +impl Mp4Box for Avc1Box { + fn box_type(&self) -> BoxType { + self.get_type() + } + + fn box_size(&self) -> u64 { + self.get_size() + } + + fn to_json(&self) -> Result { + Ok(serde_json::to_string(&self).unwrap()) + } + + fn summary(&self) -> Result { + let s = format!( + "data_reference_index={} width={} height={} frame_count={}", + self.inner.data_reference_index, + self.inner.width, + self.inner.height, + self.inner.frame_count + ); + Ok(s) + } +} + +impl ReadBox<&mut R> for Avc1Box { + fn read_box(reader: &mut R, size: u64) -> Result { + let start = box_start(reader)?; + + let inner = Avc1Or3Inner::read(reader, size, start)?; + + Ok(Avc1Box { inner }) + } +} + +impl WriteBox<&mut W> for Avc1Box { + fn write_box(&self, writer: &mut W) -> Result { + let size = self.box_size(); + BoxHeader::new(self.box_type(), size).write(writer)?; + + self.inner.write(writer)?; + + Ok(size) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default)] +pub struct Avc3Box { + pub inner: Avc1Or3Inner, +} + +impl Avc3Box { + pub fn new(config: &AvcConfig) -> Self { + Avc3Box { + inner: Avc1Or3Inner::new(config), + } + } + + pub fn get_type(&self) -> BoxType { + BoxType::Avc3Box + } + + pub fn get_size(&self) -> u64 { + HEADER_SIZE + self.inner.size() + } +} + +impl Mp4Box for Avc3Box { + fn box_type(&self) -> BoxType { + self.get_type() + } + + fn box_size(&self) -> u64 { + self.get_size() + } + + fn to_json(&self) -> Result { + Ok(serde_json::to_string(&self).unwrap()) + } + + fn summary(&self) -> Result { + let s = format!( + "data_reference_index={} width={} height={} frame_count={}", + self.inner.data_reference_index, + self.inner.width, + self.inner.height, + self.inner.frame_count + ); + Ok(s) + } +} + +impl ReadBox<&mut R> for Avc3Box { + fn read_box(reader: &mut R, size: u64) -> Result { + let start = box_start(reader)?; + + let inner = Avc1Or3Inner::read(reader, size, start)?; + + Ok(Avc3Box { inner }) + } +} + +impl WriteBox<&mut W> for Avc3Box { + fn write_box(&self, writer: &mut W) -> Result { + let size = self.box_size(); + BoxHeader::new(self.box_type(), size).write(writer)?; + + self.inner.write(writer)?; + Ok(size) } } @@ -315,28 +416,30 @@ mod tests { #[test] fn test_avc1() { let src_box = Avc1Box { - data_reference_index: 1, - width: 320, - height: 240, - horizresolution: FixedPointU16::new(0x48), - vertresolution: FixedPointU16::new(0x48), - frame_count: 1, - depth: 24, - avcc: AvcCBox { - configuration_version: 1, - avc_profile_indication: 100, - profile_compatibility: 0, - avc_level_indication: 13, - length_size_minus_one: 3, - sequence_parameter_sets: vec![NalUnit { - bytes: vec![ - 0x67, 0x64, 0x00, 0x0D, 0xAC, 0xD9, 0x41, 0x41, 0xFA, 0x10, 0x00, 0x00, - 0x03, 0x00, 0x10, 0x00, 0x00, 0x03, 0x03, 0x20, 0xF1, 0x42, 0x99, 0x60, - ], - }], - picture_parameter_sets: vec![NalUnit { - bytes: vec![0x68, 0xEB, 0xE3, 0xCB, 0x22, 0xC0], - }], + inner: Avc1Or3Inner { + data_reference_index: 1, + width: 320, + height: 240, + horizresolution: FixedPointU16::new(0x48), + vertresolution: FixedPointU16::new(0x48), + frame_count: 1, + depth: 24, + avcc: AvcCBox { + configuration_version: 1, + avc_profile_indication: 100, + profile_compatibility: 0, + avc_level_indication: 13, + length_size_minus_one: 3, + sequence_parameter_sets: vec![NalUnit { + bytes: vec![ + 0x67, 0x64, 0x00, 0x0D, 0xAC, 0xD9, 0x41, 0x41, 0xFA, 0x10, 0x00, 0x00, + 0x03, 0x00, 0x10, 0x00, 0x00, 0x03, 0x03, 0x20, 0xF1, 0x42, 0x99, 0x60, + ], + }], + picture_parameter_sets: vec![NalUnit { + bytes: vec![0x68, 0xEB, 0xE3, 0xCB, 0x22, 0xC0], + }], + }, }, }; let mut buf = Vec::new(); @@ -351,4 +454,46 @@ mod tests { let dst_box = Avc1Box::read_box(&mut reader, header.size).unwrap(); assert_eq!(src_box, dst_box); } + + #[test] + fn test_avc3() { + let src_box = Avc3Box { + inner: Avc1Or3Inner { + data_reference_index: 1, + width: 320, + height: 240, + horizresolution: FixedPointU16::new(0x48), + vertresolution: FixedPointU16::new(0x48), + frame_count: 1, + depth: 24, + avcc: AvcCBox { + configuration_version: 1, + avc_profile_indication: 100, + profile_compatibility: 0, + avc_level_indication: 13, + length_size_minus_one: 3, + sequence_parameter_sets: vec![NalUnit { + bytes: vec![ + 0x67, 0x64, 0x00, 0x0D, 0xAC, 0xD9, 0x41, 0x41, 0xFA, 0x10, 0x00, 0x00, + 0x03, 0x00, 0x10, 0x00, 0x00, 0x03, 0x03, 0x20, 0xF1, 0x42, 0x99, 0x60, + ], + }], + picture_parameter_sets: vec![NalUnit { + bytes: vec![0x68, 0xEB, 0xE3, 0xCB, 0x22, 0xC0], + }], + }, + }, + }; + let mut buf = Vec::new(); + src_box.write_box(&mut buf).unwrap(); + assert_eq!(buf.len(), src_box.box_size() as usize); + + let mut reader = Cursor::new(&buf); + let header = BoxHeader::read(&mut reader).unwrap(); + assert_eq!(header.name, BoxType::Avc3Box); + assert_eq!(src_box.box_size(), header.size); + + let dst_box = Avc3Box::read_box(&mut reader, header.size).unwrap(); + assert_eq!(src_box, dst_box); + } } diff --git a/src/mp4box/mod.rs b/src/mp4box/mod.rs index 4bbdd41..a9aa782 100644 --- a/src/mp4box/mod.rs +++ b/src/mp4box/mod.rs @@ -26,6 +26,7 @@ //! stbl //! stsd //! avc1 +//! avc3 //! hev1 //! mp4a //! tx3g @@ -62,7 +63,7 @@ use std::io::{Read, Seek, SeekFrom, Write}; use crate::*; -pub(crate) mod avc1; +pub(crate) mod avc; pub(crate) mod co64; pub(crate) mod ctts; pub(crate) mod data; @@ -106,7 +107,7 @@ pub(crate) mod vmhd; pub(crate) mod vp09; pub(crate) mod vpcc; -pub use avc1::Avc1Box; +pub use avc::Avc1Box; pub use co64::Co64Box; pub use ctts::CttsBox; pub use data::DataBox; @@ -223,6 +224,7 @@ boxtype! { UrlBox => 0x75726C20, SmhdBox => 0x736d6864, Avc1Box => 0x61766331, + Avc3Box => 0x61766333, AvcCBox => 0x61766343, Hev1Box => 0x68657631, HvcCBox => 0x68766343, diff --git a/src/mp4box/stsd.rs b/src/mp4box/stsd.rs index af947c6..8a06d72 100644 --- a/src/mp4box/stsd.rs +++ b/src/mp4box/stsd.rs @@ -4,7 +4,12 @@ use std::io::{Read, Seek, Write}; use crate::mp4box::vp09::Vp09Box; use crate::mp4box::*; -use crate::mp4box::{avc1::Avc1Box, hev1::Hev1Box, mp4a::Mp4aBox, tx3g::Tx3gBox}; +use crate::mp4box::{ + avc::{Avc1Box, Avc3Box}, + hev1::Hev1Box, + mp4a::Mp4aBox, + tx3g::Tx3gBox, +}; #[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)] pub struct StsdBox { @@ -14,6 +19,9 @@ pub struct StsdBox { #[serde(skip_serializing_if = "Option::is_none")] pub avc1: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub avc3: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub hev1: Option, @@ -77,6 +85,7 @@ impl ReadBox<&mut R> for StsdBox { reader.read_u32::()?; // XXX entry_count let mut avc1 = None; + let mut avc3 = None; let mut hev1 = None; let mut vp09 = None; let mut mp4a = None; @@ -95,6 +104,9 @@ impl ReadBox<&mut R> for StsdBox { BoxType::Avc1Box => { avc1 = Some(Avc1Box::read_box(reader, s)?); } + BoxType::Avc3Box => { + avc3 = Some(Avc3Box::read_box(reader, s)?); + } BoxType::Hev1Box => { hev1 = Some(Hev1Box::read_box(reader, s)?); } @@ -116,6 +128,7 @@ impl ReadBox<&mut R> for StsdBox { version, flags, avc1, + avc3, hev1, vp09, mp4a, diff --git a/src/track.rs b/src/track.rs index 7eada83..8e361ed 100644 --- a/src/track.rs +++ b/src/track.rs @@ -8,12 +8,14 @@ use crate::mp4box::traf::TrafBox; use crate::mp4box::trak::TrakBox; use crate::mp4box::trun::TrunBox; use crate::mp4box::{ - avc1::Avc1Box, co64::Co64Box, ctts::CttsBox, ctts::CttsEntry, hev1::Hev1Box, mp4a::Mp4aBox, + avc::Avc1Box, co64::Co64Box, ctts::CttsBox, ctts::CttsEntry, hev1::Hev1Box, mp4a::Mp4aBox, smhd::SmhdBox, stco::StcoBox, stsc::StscEntry, stss::StssBox, stts::SttsEntry, tx3g::Tx3gBox, vmhd::VmhdBox, vp09::Vp09Box, }; use crate::*; +use self::avc::Avc1Or3Inner; + #[derive(Debug, Clone, PartialEq, Eq)] pub struct TrackConfig { pub track_type: TrackType, @@ -119,7 +121,7 @@ impl Mp4Track { } pub fn media_type(&self) -> Result { - if self.trak.mdia.minf.stbl.stsd.avc1.is_some() { + if self.avc1_or_3_inner().is_some() { Ok(MediaType::H264) } else if self.trak.mdia.minf.stbl.stsd.hev1.is_some() { Ok(MediaType::H265) @@ -150,17 +152,37 @@ impl Mp4Track { } } + pub fn avc1_or_3_inner(&self) -> Option<&Avc1Or3Inner> { + self.trak + .mdia + .minf + .stbl + .stsd + .avc1 + .as_ref() + .map(|avc1| &avc1.inner) + .or(self + .trak + .mdia + .minf + .stbl + .stsd + .avc3 + .as_ref() + .map(|avc3| &avc3.inner)) + } + pub fn width(&self) -> u16 { - if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 { - avc1.width + if let Some(avc) = self.avc1_or_3_inner() { + avc.width } else { self.trak.tkhd.width.value() } } pub fn height(&self) -> u16 { - if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 { - avc1.height + if let Some(avc) = self.avc1_or_3_inner() { + avc.height } else { self.trak.tkhd.height.value() } @@ -249,10 +271,10 @@ impl Mp4Track { } pub fn video_profile(&self) -> Result { - if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 { + if let Some(avc) = self.avc1_or_3_inner() { AvcProfile::try_from(( - avc1.avcc.avc_profile_indication, - avc1.avcc.profile_compatibility, + avc.avcc.avc_profile_indication, + avc.avcc.profile_compatibility, )) } else { Err(Error::BoxInStblNotFound(self.track_id(), BoxType::Avc1Box)) @@ -260,8 +282,8 @@ impl Mp4Track { } pub fn sequence_parameter_set(&self) -> Result<&[u8]> { - if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 { - match avc1.avcc.sequence_parameter_sets.get(0) { + if let Some(avc) = self.avc1_or_3_inner() { + match avc.avcc.sequence_parameter_sets.first() { Some(nal) => Ok(nal.bytes.as_ref()), None => Err(Error::EntryInStblNotFound( self.track_id(), @@ -275,8 +297,8 @@ impl Mp4Track { } pub fn picture_parameter_set(&self) -> Result<&[u8]> { - if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 { - match avc1.avcc.picture_parameter_sets.get(0) { + if let Some(avc) = self.avc1_or_3_inner() { + match avc.avcc.picture_parameter_sets.first() { Some(nal) => Ok(nal.bytes.as_ref()), None => Err(Error::EntryInStblNotFound( self.track_id(),