#![warn(missing_docs)]
pub mod frame;
use frame::*;
use packbytes::{ByteArray, FromBytes, ToBytes};
use std::io;
pub const MAGIC_BYTES: [u8; 4] = *b"MLVI";
#[derive(FromBytes, ToBytes, Clone, Copy, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct FileHeader {
pub version: [u8; 8],
pub guid: u64,
pub num: u16,
pub count: u16,
pub flags: u32,
pub video_class: u16,
pub audio_class: u16,
pub video_frame_count: u32,
pub audio_frame_count: u32,
pub fps: Fraction,
}
#[derive(FromBytes, ToBytes, Clone, Copy, Debug, Default, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Fraction {
pub numerator: i32,
pub denominator: u32,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u32)]
pub enum Camera {
C5d2 = 0x80000218,
C7d = 0x80000250,
C500d = 0x80000252,
C50d = 0x80000261,
C550d = 0x80000270,
C5d3 = 0x80000285,
C600d = 0x80000286,
C60d = 0x80000287,
C1100d = 0x80000288,
C7d2 = 0x80000289,
C650d = 0x80000301,
C6d = 0x80000302,
C70d = 0x80000325,
C700d = 0x80000326,
C1200d = 0x80000327,
M = 0x80000331,
C100d = 0x80000346,
C760d = 0x80000347,
C5d4 = 0x80000349,
C80d = 0x80000350,
M2 = 0x80000355,
C5ds = 0x80000382,
C750d = 0x80000393,
C5dsr = 0x80000401,
C1300d = 0x80000404,
Unknown,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum FileFlag {
OutOfOrderData = 0x1,
DroppedFrames = 0x2,
SingleImageMode = 0x4,
StoppedDueToError = 0x8,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum VideoClass {
None = 0,
Raw = 1,
Yuv = 2,
Jpeg = 3,
H264 = 4,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum VideoClassFlag {
None = 0,
LJ92 = 0x20,
Delta = 0x40,
Lzma = 0x80,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum AudioClass {
None = 0,
Wav,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum AudioClassFlag {
None = 0,
Lzma = 0x80,
}
impl FileHeader {
pub fn read<R: io::Read>(reader: &mut R) -> io::Result<(Self, Vec<u8>)> {
let mut magic = [0; 4];
reader.read_exact(&mut magic)?;
if magic != MAGIC_BYTES {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"MLV header not found",
));
}
let headersize = <FileHeader as FromBytes>::Bytes::SIZE;
let size = u32::read_packed(reader)? as usize;
let size = size.saturating_sub(8);
if size < headersize {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Header size too small",
));
}
let header = FileHeader::read_packed(reader)?;
let mut remaining = vec![0; size - headersize];
reader.read_exact(&mut remaining)?;
Ok((header, remaining))
}
pub fn write_mlv<W, S, I, J>(
mut self,
headers: impl IntoIterator<Item = Frame<S>>,
video_frames: impl IntoIterator<IntoIter = I>,
audio_frames: impl IntoIterator<IntoIter = J>,
writer: &mut W,
) -> io::Result<()>
where
W: io::Write,
S: AsRef<[u8]>,
I: Iterator,
J: Iterator,
I::Item: AsRef<[u8]>,
J::Item: AsRef<[u8]>,
{
let headers = headers.into_iter();
let video_frames = video_frames.into_iter();
let audio_frames = audio_frames.into_iter();
self.video_frame_count = video_frames.size_hint().0 as u32;
self.audio_frame_count = audio_frames.size_hint().0 as u32;
let headersize = (<FileHeader as ToBytes>::Bytes::SIZE + 8) as u32;
writer.write_all(&MAGIC_BYTES)?;
headersize.write_packed(writer)?;
self.write_packed(writer)?;
for frame in headers {
frame.write(writer, 0)?;
}
let video_frame_time = (1_000_000 * self.fps.denominator) / self.fps.numerator as u32;
for (number, payload) in video_frames.enumerate() {
let frameblock = Frame {
header: Video::from_frame_number(number as u32).into(),
payload,
};
frameblock.write(writer, number as u64 * video_frame_time as u64)?;
}
let mut audio_frame_time = 0;
if self.audio_frame_count != 0 {
audio_frame_time = (self.video_frame_count * video_frame_time) / self.audio_frame_count;
}
for (number, payload) in audio_frames.enumerate() {
let frameblock = Frame {
header: Audio {
number: number as u32,
}
.into(),
payload,
};
frameblock.write(writer, number as u64 * audio_frame_time as u64)?;
}
Ok(())
}
}
impl Fraction {
pub const fn new(numerator: i32, denominator: u32) -> Self {
Self {
numerator,
denominator,
}
}
pub const fn in_thousandths(numerator: i32) -> Self {
Self {
numerator,
denominator: 1000,
}
}
pub const fn integral(n: i32) -> Self {
Self {
numerator: n * 1000,
denominator: 1000,
}
}
}
impl From<Fraction> for f32 {
fn from(f: Fraction) -> f32 {
f.numerator as f32 / f.denominator as f32
}
}
impl From<Fraction> for f64 {
fn from(f: Fraction) -> f64 {
f.numerator as f64 / f.denominator as f64
}
}