diff options
| author | Felix Kaaman <tmtu@tmtu.ee> | 2021-01-07 20:32:49 +0100 |
|---|---|---|
| committer | Felix Kaaman <tmtu@tmtu.ee> | 2021-01-07 20:32:49 +0100 |
| commit | 9e6e4962f2346a3fbd96ab3e6c331858ef6ec0d1 (patch) | |
| tree | 14b580295f71243def9c8df9c242c33f9f2c66a8 /src/lib.rs | |
| download | fmp4-trunk.tar.gz fmp4-trunk.zip | |
Diffstat (limited to 'src/lib.rs')
| -rw-r--r-- | src/lib.rs | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..0630a15 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,129 @@ +#![allow(dead_code)] + +use std::io; + +use byteorder::{BigEndian, ByteOrder}; +use bytes::{BufMut, BytesMut}; +pub use four_cc::FourCC; + +mod boxes; + +pub use boxes::*; + +fn get_total_box_size<B: Mp4Box + ?Sized>(boks: &B) -> u64 { + let size = boks.content_size(); + + if boks.get_full_box_header().is_some() { + size + FullBoxHeader::SIZE + 8 + } else { + size + 8 + } +} + +fn write_box_header<B: Mp4Box + ?Sized>(header: &mut [u8], size: u64) -> usize { + if size > u32::MAX as _ { + BigEndian::write_u32(&mut header[..], 1); + header[4..8].copy_from_slice(&B::NAME.0); + BigEndian::write_u64(&mut header[8..], size); + + 16 + } else { + BigEndian::write_u32(&mut header[..], size as u32); + header[4..8].copy_from_slice(&B::NAME.0); + + 8 + } +} + +fn write_full_box_header(header: &mut [u8], box_header: FullBoxHeader) -> usize { + header[0] = box_header.version; + BigEndian::write_u24(&mut header[1..], box_header.flags); + + 4 +} + +#[derive(Copy, Clone)] +pub struct FullBoxHeader { + version: u8, + flags: u32, +} + +impl FullBoxHeader { + pub const SIZE: u64 = 4; + + pub fn new(version: u8, flags: u32) -> Self { + FullBoxHeader { version, flags } + } +} + +#[derive(Debug, thiserror::Error)] +pub enum Mp4BoxError { + #[error("Failed to write box: {0}")] + IoError(#[from] io::Error), +} + +/// A trait interface for a MP4 box. +pub trait Mp4Box { + const NAME: FourCC; + + fn get_full_box_header(&self) -> Option<FullBoxHeader> { + None + } + + fn flags(&self) -> Option<u32> { + self.get_full_box_header().map(|h| h.flags) + } + + /// The size of the contents of the box. + fn content_size(&self) -> u64; + + fn size(&self) -> u64 { + get_total_box_size::<Self>(&self) + } + + fn write_box_contents(&self, writer: &mut BytesMut) -> Result<(), Mp4BoxError>; + + fn write(&self, writer: &mut BytesMut) -> Result<(), Mp4BoxError> { + let mut header = [0u8; 20]; + + /* println!( + "{}: {}/{}", + std::any::type_name::<Self>(), + self.size(), + self.content_size() + ); */ + + let mut size = write_box_header::<Self>(&mut header, self.size()); + if let Some(box_header) = self.get_full_box_header() { + size += write_full_box_header(&mut header[size..], box_header); + } + + writer.put_slice(&header[..size]); + + self.write_box_contents(writer)?; + + Ok(()) + } +} + +pub struct MediaDataBox<'a> { + pub data: &'a [u8], +} + +impl<'a> Mp4Box for MediaDataBox<'a> { + const NAME: FourCC = FourCC(*b"mdat"); + + fn get_full_box_header(&self) -> Option<FullBoxHeader> { + Some(FullBoxHeader::new(0, 0)) + } + + fn content_size(&self) -> u64 { + self.data.len() as _ + } + + fn write_box_contents(&self, writer: &mut BytesMut) -> Result<(), Mp4BoxError> { + writer.put_slice(&self.data); + + Ok(()) + } +} |
