1
0
Fork 0
mirror of https://github.com/alfg/mp4-rust.git synced 2024-06-02 13:39:54 +00:00

write meta and udta

This commit is contained in:
Jessa 2023-01-16 13:06:42 -08:00
parent 5f61fd2b68
commit 669edcf7ad
5 changed files with 452 additions and 32 deletions

View file

@ -13,6 +13,39 @@ pub struct DataBox {
pub data_type: DataType,
}
impl DataBox {
pub fn get_type(&self) -> BoxType {
BoxType::DataBox
}
pub fn get_size(&self) -> u64 {
let mut size = HEADER_SIZE;
size += 4; // data_type
size += 4; // reserved
size += self.data.len() as u64;
size
}
}
impl Mp4Box for DataBox {
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!("type={:?} len={}", self.data_type, self.data.len());
Ok(s)
}
}
impl<R: Read + Seek> ReadBox<&mut R> for DataBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
@ -28,3 +61,58 @@ impl<R: Read + Seek> ReadBox<&mut R> for DataBox {
Ok(DataBox { data, data_type })
}
}
impl<W: Write> WriteBox<&mut W> for DataBox {
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.data_type.clone() as u32)?;
writer.write_u32::<BigEndian>(0)?; // reserved = 0
writer.write_all(&self.data)?;
Ok(size)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_data() {
let src_box = DataBox {
data_type: DataType::Text,
data: b"test_data".to_vec(),
};
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::DataBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = DataBox::read_box(&mut reader, header.size).unwrap();
assert_eq!(src_box, dst_box);
}
#[test]
fn test_data_empty() {
let src_box = DataBox::default();
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::DataBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = DataBox::read_box(&mut reader, header.size).unwrap();
assert_eq!(src_box, dst_box);
}
}

View file

@ -13,6 +13,39 @@ pub struct IlstBox {
pub items: HashMap<MetadataKey, IlstItemBox>,
}
impl IlstBox {
pub fn get_type(&self) -> BoxType {
BoxType::IlstBox
}
pub fn get_size(&self) -> u64 {
let mut size = HEADER_SIZE;
for item in self.items.values() {
size += item.get_size();
}
size
}
}
impl Mp4Box for IlstBox {
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!("item_count={}", self.items.len());
Ok(s)
}
}
impl<R: Read + Seek> ReadBox<&mut R> for IlstBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
@ -54,11 +87,36 @@ impl<R: Read + Seek> ReadBox<&mut R> for IlstBox {
}
}
impl<W: Write> WriteBox<&mut W> for IlstBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write(writer)?;
for (key, value) in &self.items {
let name = match key {
MetadataKey::Title => BoxType::NameBox,
MetadataKey::Year => BoxType::DayBox,
MetadataKey::Poster => BoxType::CovrBox,
MetadataKey::Summary => BoxType::DescBox,
};
BoxHeader::new(name, value.get_size()).write(writer)?;
value.data.write_box(writer)?;
}
Ok(size)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
pub struct IlstItemBox {
pub data: DataBox,
}
impl IlstItemBox {
fn get_size(&self) -> u64 {
HEADER_SIZE + self.data.box_size()
}
}
impl<R: Read + Seek> ReadBox<&mut R> for IlstItemBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
@ -130,3 +188,56 @@ fn item_to_u32(item: &IlstItemBox) -> Option<u32> {
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_ilst() {
let src_year = IlstItemBox {
data: DataBox {
data_type: DataType::Text,
data: b"test_year".to_vec(),
},
};
let src_box = IlstBox {
items: [
(MetadataKey::Title, IlstItemBox::default()),
(MetadataKey::Year, src_year),
(MetadataKey::Poster, IlstItemBox::default()),
(MetadataKey::Summary, IlstItemBox::default()),
]
.into(),
};
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::IlstBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = IlstBox::read_box(&mut reader, header.size).unwrap();
assert_eq!(src_box, dst_box);
}
#[test]
fn test_ilst_empty() {
let src_box = IlstBox::default();
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::IlstBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = IlstBox::read_box(&mut reader, header.size).unwrap();
assert_eq!(src_box, dst_box);
}
}

View file

@ -27,6 +27,50 @@ pub enum MetaBox {
const MDIR: FourCC = FourCC { value: *b"mdir" };
impl MetaBox {
pub fn get_type(&self) -> BoxType {
BoxType::MetaBox
}
pub fn get_size(&self) -> u64 {
let mut size = HEADER_SIZE + HEADER_EXT_SIZE;
match self {
Self::Mdir { ilst } => {
size += HdlrBox::default().box_size();
if let Some(ilst) = ilst {
size += ilst.box_size();
}
}
Self::Unknown { hdlr, data } => size += hdlr.box_size() + data.len() as u64,
}
size
}
}
impl Mp4Box for MetaBox {
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 = match self {
Self::Mdir { .. } => format!("hdlr=ilst"),
Self::Unknown { hdlr, data } => {
format!("hdlr={} data_len={}", hdlr.handler_type, data.len())
}
};
Ok(s)
}
}
impl Default for MetaBox {
fn default() -> Self {
Self::Unknown {
@ -88,6 +132,34 @@ impl<R: Read + Seek> ReadBox<&mut R> for MetaBox {
}
}
impl<W: Write> WriteBox<&mut W> for MetaBox {
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, 0, 0)?;
let hdlr = match self {
Self::Mdir { .. } => HdlrBox {
handler_type: MDIR,
..Default::default()
},
Self::Unknown { hdlr, .. } => hdlr.clone(),
};
hdlr.write_box(writer)?;
match self {
Self::Mdir { ilst } => {
if let Some(ilst) = ilst {
ilst.write_box(writer)?;
}
}
Self::Unknown { data, .. } => writer.write_all(data)?,
}
Ok(size)
}
}
#[cfg(test)]
mod tests {
use super::*;
@ -95,29 +167,39 @@ mod tests {
use std::io::Cursor;
#[test]
fn test_meta_mdir() {
let src_hdlr = HdlrBox {
handler_type: MDIR,
..Default::default()
};
let src_header = BoxHeader::new(
BoxType::MetaBox,
HEADER_SIZE + HEADER_EXT_SIZE + src_hdlr.box_size(),
);
fn test_meta_mdir_empty() {
let src_box = MetaBox::Mdir { ilst: None };
let mut buf = Vec::new();
src_header.write(&mut buf).unwrap();
write_box_header_ext(&mut buf, 0, 0).unwrap();
src_hdlr.write_box(&mut buf).unwrap();
assert_eq!(buf.len(), src_header.size as usize);
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, src_header.name);
assert_eq!(header.size, src_header.size);
assert_eq!(header.name, BoxType::MetaBox);
assert_eq!(header.size, src_box.box_size());
let dst_box = MetaBox::read_box(&mut reader, header.size).unwrap();
assert!(matches!(dst_box, MetaBox::Mdir { ilst: None }));
assert_eq!(dst_box, src_box);
}
#[test]
fn test_meta_mdir() {
let src_box = MetaBox::Mdir {
ilst: Some(IlstBox::default()),
};
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::MetaBox);
assert_eq!(header.size, src_box.box_size());
let dst_box = MetaBox::read_box(&mut reader, header.size).unwrap();
assert_eq!(dst_box, src_box);
}
#[test]
@ -127,28 +209,21 @@ mod tests {
..Default::default()
};
let src_data = b"123";
let src_header = BoxHeader::new(
BoxType::MetaBox,
HEADER_SIZE + HEADER_EXT_SIZE + src_hdlr.box_size() + src_data.len() as u64,
);
let src_box = MetaBox::Unknown {
hdlr: src_hdlr,
data: src_data.to_vec(),
};
let mut buf = Vec::new();
src_header.write(&mut buf).unwrap();
write_box_header_ext(&mut buf, 0, 0).unwrap();
src_hdlr.write_box(&mut buf).unwrap();
buf.extend(src_data);
assert_eq!(buf.len(), src_header.size as usize);
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, src_header.name);
assert_eq!(header.size, src_header.size);
assert_eq!(header.name, BoxType::MetaBox);
assert_eq!(header.size, src_box.box_size());
let dst_box = MetaBox::read_box(&mut reader, header.size).unwrap();
assert!(
matches!(dst_box, MetaBox::Unknown { hdlr, data } if data == src_data && hdlr == src_hdlr)
);
assert_eq!(dst_box, src_box);
}
}

View file

@ -32,6 +32,12 @@ impl MoovBox {
for trak in self.traks.iter() {
size += trak.box_size();
}
if let Some(meta) = &self.meta {
size += meta.box_size();
}
if let Some(udta) = &self.udta {
size += udta.box_size();
}
size
}
}
@ -123,6 +129,59 @@ impl<W: Write> WriteBox<&mut W> for MoovBox {
for trak in self.traks.iter() {
trak.write_box(writer)?;
}
if let Some(meta) = &self.meta {
meta.write_box(writer)?;
}
if let Some(udta) = &self.udta {
udta.write_box(writer)?;
}
Ok(0)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_moov() {
let src_box = MoovBox {
mvhd: MvhdBox::default(),
mvex: None, // XXX mvex is not written currently
traks: vec![],
meta: Some(MetaBox::default()),
udta: Some(UdtaBox::default()),
};
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::MoovBox);
assert_eq!(header.size, src_box.box_size());
let dst_box = MoovBox::read_box(&mut reader, header.size).unwrap();
assert_eq!(dst_box, src_box);
}
#[test]
fn test_moov_empty() {
let src_box = MoovBox::default();
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::MoovBox);
assert_eq!(header.size, src_box.box_size());
let dst_box = MoovBox::read_box(&mut reader, header.size).unwrap();
assert_eq!(dst_box, src_box);
}
}

View file

@ -11,6 +11,38 @@ pub struct UdtaBox {
pub meta: Option<MetaBox>,
}
impl UdtaBox {
pub fn get_type(&self) -> BoxType {
BoxType::UdtaBox
}
pub fn get_size(&self) -> u64 {
let mut size = HEADER_SIZE;
if let Some(meta) = &self.meta {
size += meta.box_size();
}
size
}
}
impl Mp4Box for UdtaBox {
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> {
Ok(String::new())
}
}
impl<R: Read + Seek> ReadBox<&mut R> for UdtaBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
@ -42,3 +74,58 @@ impl<R: Read + Seek> ReadBox<&mut R> for UdtaBox {
Ok(UdtaBox { meta })
}
}
impl<W: Write> WriteBox<&mut W> for UdtaBox {
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(meta) = &self.meta {
meta.write_box(writer)?;
}
Ok(size)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_udta_empty() {
let src_box = UdtaBox { meta: None };
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::UdtaBox);
assert_eq!(header.size, src_box.box_size());
let dst_box = UdtaBox::read_box(&mut reader, header.size).unwrap();
assert_eq!(dst_box, src_box);
}
#[test]
fn test_udta() {
let src_box = UdtaBox {
meta: Some(MetaBox::default()),
};
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::UdtaBox);
assert_eq!(header.size, src_box.box_size());
let dst_box = UdtaBox::read_box(&mut reader, header.size).unwrap();
assert_eq!(dst_box, src_box);
}
}