use derive_more::Display;
use futures_io::AsyncWrite;
use futures_util::AsyncWriteExt;
use thiserror::Error;
use crate::{atom::FourCC, Atom, AtomData};
#[derive(Debug, Error)]
#[error("{kind}{}", self.source.as_ref().map(|e| format!(" ({e})")).unwrap_or_default())]
pub struct WriteError {
kind: WriteErrorKind,
#[source]
source: Option<Box<dyn std::error::Error + Send + Sync>>,
}
#[derive(Debug, Display)]
pub enum WriteErrorKind {
#[display("I/O error")]
Io,
}
pub trait SerializeAtom: Sized {
fn atom_type(&self) -> FourCC;
fn into_body_bytes(self) -> Vec<u8>;
fn into_bytes(self) -> Vec<u8> {
let atom_type = self.atom_type();
let mut body = self.into_body_bytes();
let mut header = serialize_atom_header(atom_type, body.len() as u64);
header.append(&mut body);
header
}
}
pub struct Mp4Writer<W> {
writer: W,
offset: usize,
}
impl<W: AsyncWrite + Unpin> Mp4Writer<W> {
pub fn new(writer: W) -> Self {
Self { writer, offset: 0 }
}
pub fn current_offset(&self) -> usize {
self.offset
}
pub async fn flush(&mut self) -> Result<(), WriteError> {
self.writer.flush().await.map_err(|e| WriteError {
kind: WriteErrorKind::Io,
source: Some(Box::new(e)),
})
}
pub async fn write_atom_header(
&mut self,
atom_type: FourCC,
data_size: usize,
) -> Result<(), WriteError> {
let header_bytes = serialize_atom_header(atom_type, data_size as u64);
self.writer
.write_all(&header_bytes)
.await
.map_err(|e| WriteError {
kind: WriteErrorKind::Io,
source: Some(Box::new(e)),
})?;
self.offset += header_bytes.len();
Ok(())
}
pub async fn write_leaf_atom(
&mut self,
atom_type: FourCC,
data: AtomData,
) -> Result<(), WriteError> {
let data_bytes: Vec<u8> = data.into_body_bytes();
self.write_atom_header(atom_type, data_bytes.len()).await?;
self.writer
.write_all(&data_bytes)
.await
.map_err(|e| WriteError {
kind: WriteErrorKind::Io,
source: Some(Box::new(e)),
})?;
self.offset += data_bytes.len();
Ok(())
}
pub async fn write_atom(&mut self, atom: Atom) -> Result<(), WriteError> {
let bytes = atom.into_bytes();
self.writer
.write_all(&bytes)
.await
.map_err(|e| WriteError {
kind: WriteErrorKind::Io,
source: Some(Box::new(e)),
})?;
self.offset += bytes.len();
Ok(())
}
pub async fn write_raw(&mut self, data: &[u8]) -> Result<(), WriteError> {
self.writer.write_all(data).await.map_err(|e| WriteError {
kind: WriteErrorKind::Io,
source: Some(Box::new(e)),
})?;
self.offset += data.len();
Ok(())
}
}
fn serialize_atom_header(atom_type: FourCC, data_size: u64) -> Vec<u8> {
let mut result = Vec::new();
let total_size_with_32bit_header = 8u64 + data_size;
let use_64bit = total_size_with_32bit_header > u64::from(u32::MAX);
if use_64bit {
let total_size = 16u64 + data_size;
result.extend_from_slice(&1u32.to_be_bytes());
result.extend_from_slice(&atom_type.0);
result.extend_from_slice(&total_size.to_be_bytes());
} else {
let total_size = total_size_with_32bit_header as u32;
result.extend_from_slice(&total_size.to_be_bytes());
result.extend_from_slice(&atom_type.0);
}
result
}