#![allow(non_snake_case)]
const SIGNED_BIT: u8 = 0x80;
#[derive(Debug,Clone,Copy)]
#[repr(u8)]
pub enum PrimitiveType {
U8 = 1, U16 = 2, U32 = 4, U64 = 8,
I8 = 1 | SIGNED_BIT, I16 = 2 | SIGNED_BIT,
I32 = 4 | SIGNED_BIT, I64 = 8 | SIGNED_BIT,
}
impl PrimitiveType {
#[inline] pub const fn size(self) -> u32 { self as u32 & !SIGNED_BIT as u32 }
}
#[derive(Debug,Clone,Copy)]
pub enum FieldType {
Single(PrimitiveType),
Array(PrimitiveType, u32),
}
impl FieldType {
#[inline]
pub const fn size(self) -> u32 {
match self {
FieldType::Single(t) => t.size(),
FieldType::Array(t, len) => t.size() * len,
}
}
}
#[derive(Debug,Copy,Clone)]
pub struct FieldDefinition {
pub name: &'static str,
pub data_type: FieldType,
}
#[derive(Debug,Copy,Clone)]
pub struct BlockDefinition {
pub start_offset: u32,
pub fields: &'static[FieldDefinition],
}
impl BlockDefinition {
#[inline]
pub const fn field_offset(&self, field_name: &'static str) -> Option<u32> {
let mut off = self.start_offset;
let mut f = 0;
while f < self.fields.len() {
let field = &self.fields[f];
const fn is_same(a: &[u8], b: &[u8]) -> bool {
let mut i = 0;
while i < a.len() { if a[i] != b[i] { return false; } i += 1; }
a.len() == b.len()
}
if is_same(field.name.as_bytes(), field_name.as_bytes()) { return Some(off); }
off += field.data_type.size() as u32;
f += 1;
}
return None;
}
#[inline]
pub const fn size(&self) -> u32 {
let mut size = self.start_offset;
let mut f = 0;
while f < self.fields.len() {
size += self.fields[f].data_type.size();
f += 1;
}
size
}
}
macro_rules! mlv_all_block_def {
($($block_name: ident {
$($name:ident : $ty:tt),* $(,)?
})*) => {
$(pub const $block_name: BlockDefinition = mlv_block_def! {
$($name : $ty),*
};)*
pub const MLV_BLOCKS: &[(&str, &BlockDefinition)] = &[
$((stringify!($block_name), &$block_name),)*
];
};
}
macro_rules! mlv_block_def {
($($name:ident : $ty:tt),* $(,)?) => {
BlockDefinition {
start_offset: 16,
fields: &[
$(field!(stringify!($name), $ty),)*
]
}
};
}
macro_rules! field {
($name:expr, [$elem_ty:tt; $len:expr]) => {
FieldDefinition {
name: $name,
data_type: FieldType::Array(type_to_fdt!($elem_ty), $len),
}
};
($name:expr, $ty:tt) => {
FieldDefinition {
name: $name,
data_type: FieldType::Single(type_to_fdt!($ty)),
}
};
}
macro_rules! type_to_fdt {
(u8) => { PrimitiveType::U8 };
(u16) => { PrimitiveType::U16 };
(u32) => { PrimitiveType::U32 };
(u64) => { PrimitiveType::U64 };
(i8) => { PrimitiveType::I8 };
(i16) => { PrimitiveType::I16 };
(i32) => { PrimitiveType::I32 };
(i64) => { PrimitiveType::I64 };
(_) => { compile_error!("Unknown type") };
}
macro_rules! impl_get_field {
($fun_name:ident, $fun_ty:tt, $ty_size:expr) => {
pub fn $fun_name(data: &[u8], offset: u32) -> Option<$fun_ty> {
let mut buf = [0u8; $ty_size];
buf.copy_from_slice(data.get(offset as usize..(offset + $ty_size) as usize)?);
Some($fun_ty::from_le_bytes(buf))
}
};
}
impl_get_field!(get_u8, u8, 1);
impl_get_field!(get_u16, u16, 2);
impl_get_field!(get_u32, u32, 4);
impl_get_field!(get_u64, u64, 8);
impl_get_field!(get_i8, i8, 1);
impl_get_field!(get_i16, i16, 2);
impl_get_field!(get_i32, i32, 4);
impl_get_field!(get_i64, i64, 8);
pub fn block_get_type<'a>(data: &'a [u8]) -> Option<&'a str> {
str::from_utf8(&data[0..4]).ok()
}
pub fn block_get_timestamp(data: &[u8]) -> Option<u64> {
if block_get_type(data) == Some("MLVI") {
Some(0)
} else {
Some(u64::from_le_bytes(*data[8..16].first_chunk()?))
}
}
pub fn block_get_size(data: &[u8]) -> Option<u32> {
Some(u32::from_le_bytes(*data[4..8].first_chunk()?))
}
mlv_all_block_def! {
MLVI {
fileGuid: u64,
fileNum: u16,
fileCount: u16,
fileFlags: u32,
videoClass: u16,
audioClass: u16,
videoFrameCount: u32,
audioFrameCount: u32,
sourceFpsNom: u32,
sourceFpsDenom: u32,
}
VIDF {
frameNumber: u32,
cropPosX: u16,
cropPosY: u16,
panPosX: u16,
panPosY: u16,
frameSpace: u32,
}
AUDF {
frameNumber: u32,
frameSpace: u32
}
RAWI {
xRes: u16,
yRes: u16,
api_version: u32,
do_not_use_this: u32,
height: i32,
width: i32,
pitch: i32,
frame_size: i32,
bits_per_pixel: i32,
black_level: i32, white_level: i32,
jpeg_x: i32, jpeg_y: i32,
jpeg_width: i32, jpeg_height: i32,
dng_active_area: [i32; 4],
exposure_bias: [i32; 2], cfa_pattern: i32, calibration_illuminant1: i32,
color_matrix1: [i32; 18], dynamic_range: i32 }
WAVI {
format: u16,
channels: u16,
samplingRate: u32,
bytesPerSecond: u32,
blockAlign: u16,
bitsPerSample: u16
}
EXPO {
isoMode: u32,
isoValue: u32,
isoAnalog: u32,
digitalGain: u32,
shutterValue: u64,
}
RAWC {
blockType: [u8; 4],
blockSize: u32,
timestamp: u64,
sensor_res_x: u16,
sensor_res_y: u16,
sensor_crop: u16,
reserved: u16,
binning_x: u8,
skipping_x: u8,
binning_y: u8,
skipping_y: u8,
offset_x: i16,
offset_y: i16,
}
}
pub const MLV_VERSION_STRING: &str = "2.0";