use std::fs::File;
use std::io::{Read, Seek, Write};
use std::path::Path;
use std::time::Duration;
use crate::color::ColorSpace;
use crate::error::{Error, Result};
use crate::identify::{Container, Encoding};
use crate::media::{Packet, Rational};
mod mp4;
mod webm;
pub trait Storage: Read + Write + Seek + Send {}
impl<T: Read + Write + Seek + Send + ?Sized> Storage for T {}
#[derive(Clone, Debug)]
pub struct VideoTrack {
pub encoding: Encoding,
pub width: u32,
pub height: u32,
pub frame_rate: Rational,
pub color: ColorSpace,
pub config_record: Vec<u8>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Written {
pub samples: u64,
pub duration: Duration,
pub bytes: u64,
}
pub trait Muxer: Send {
fn write(&mut self, packet: Packet) -> Result<()>;
fn finish(self: Box<Self>) -> Result<Written>;
}
pub fn container_for(encoding: Encoding) -> Result<Container> {
match encoding {
Encoding::H264 => Ok(Container::Mp4),
Encoding::Av1 => Ok(Container::WebM),
other => Err(Error::NoEncoder {
encoding: other,
remedy: "vtome writes H.264 and AV1 only".to_string(),
}),
}
}
pub fn create(path: &Path, track: VideoTrack) -> Result<Box<dyn Muxer>> {
let open = || File::create(path).map_err(|error| Error::io(path, error));
match container_for(track.encoding)? {
Container::Mp4 => Ok(Box::new(mp4::Mp4Muxer::create(open, path, false, track)?)),
_ => Ok(Box::new(webm::WebmMuxer::create(open, path, |file| file.sync_all(), track)?)),
}
}
pub fn create_in<'a>(storage: &'a mut dyn Storage, track: VideoTrack) -> Result<Box<dyn Muxer + 'a>> {
let label = Path::new(STORAGE_LABEL);
let open = move || Ok(storage);
match container_for(track.encoding)? {
Container::Mp4 => Ok(Box::new(mp4::Mp4Muxer::create(open, label, true, track)?)),
_ => Ok(Box::new(webm::WebmMuxer::create(open, label, |_| Ok(()), track)?)),
}
}
const STORAGE_LABEL: &str = "the output";
fn require_start(storage: &mut impl Seek, label: &Path) -> Result<()> {
let position = storage.stream_position().map_err(|error| Error::io(label, error))?;
if position == 0 {
Ok(())
} else {
Err(mux_error(format!(
"{}: a file is written from its start, and this is {position} bytes in",
label.display()
)))
}
}
fn frame_duration(rate: Rational) -> Duration {
let duration = rate.frame_duration();
if duration.is_zero() {
Duration::from_millis(40)
} else {
duration
}
}
fn mux_error(reason: impl Into<String>) -> Error {
Error::Encode {
reason: reason.into(),
}
}