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:
parent
5f61fd2b68
commit
669edcf7ad
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue