Skip to main content

mlv/
blocks.rs

1#![allow(non_snake_case)]
2// #![allow(non_camel_case_types)]
3
4
5/********************** Type defiintions and mactos **********************/
6
7/* Why did i invent my own reflection */
8
9const SIGNED_BIT: u8 = 0x80;
10
11#[derive(Debug,Clone,Copy)]
12#[repr(u8)]
13pub enum PrimitiveType {
14    U8 = 1, U16 = 2, U32 = 4, U64 = 8,
15    I8 = 1 | SIGNED_BIT, I16 = 2 | SIGNED_BIT,
16    I32 = 4 | SIGNED_BIT, I64 = 8 | SIGNED_BIT,
17}
18
19impl PrimitiveType {
20    #[inline] pub const fn size(self) -> u32 { self as u32 & !SIGNED_BIT as u32 }
21}
22
23#[derive(Debug,Clone,Copy)]
24pub enum FieldType {
25    Single(PrimitiveType),
26    Array(PrimitiveType, u32),
27}
28
29impl FieldType {
30    #[inline]
31    pub const fn size(self) -> u32 {
32        match self {
33            FieldType::Single(t) => t.size(),
34            FieldType::Array(t, len) => t.size() * len,
35        }
36    }
37}
38
39#[derive(Debug,Copy,Clone)]
40pub struct FieldDefinition {
41    pub name: &'static str,
42    pub data_type: FieldType,
43}
44
45#[derive(Debug,Copy,Clone)]
46pub struct BlockDefinition {
47    pub start_offset: u32,
48    pub fields: &'static[FieldDefinition],
49}
50
51impl BlockDefinition {
52    #[inline]
53    pub const fn field_offset(&self, field_name: &'static str) -> Option<u32> {
54        let mut off = self.start_offset;
55        let mut f = 0;
56        while f < self.fields.len() {
57            let field = &self.fields[f];
58            const fn is_same(a: &[u8], b: &[u8]) -> bool {
59                let mut i = 0;
60                while i < a.len() { if a[i] != b[i] { return false; } i += 1; }
61                a.len() == b.len()
62            }
63            if is_same(field.name.as_bytes(), field_name.as_bytes()) { return Some(off); }
64            off += field.data_type.size() as u32;
65            f += 1;
66        }
67        return None;
68    }
69
70    #[inline]
71    pub const fn size(&self) -> u32 {
72        let mut size = self.start_offset;
73        let mut f = 0;
74        while f < self.fields.len() {
75            size += self.fields[f].data_type.size();
76            f += 1;
77        }
78        size
79    }
80}
81
82macro_rules! mlv_all_block_def {
83    ($($block_name: ident {
84        $($name:ident : $ty:tt),* $(,)?
85    })*) => {
86        $(pub const $block_name: BlockDefinition = mlv_block_def! {
87            $($name : $ty),*
88        };)*
89
90        pub const MLV_BLOCKS: &[(&str, &BlockDefinition)] = &[
91            $((stringify!($block_name), &$block_name),)*
92        ];
93    };
94}
95
96macro_rules! mlv_block_def {
97    ($($name:ident : $ty:tt),* $(,)?) => {
98        BlockDefinition {
99            start_offset: 16,
100            fields: &[
101                $(field!(stringify!($name), $ty),)*
102            ]
103        }
104    };
105}
106
107macro_rules! field {
108    ($name:expr, [$elem_ty:tt; $len:expr]) => {
109        FieldDefinition {
110            name: $name,
111            data_type: FieldType::Array(type_to_fdt!($elem_ty), $len),
112        }
113    };
114    ($name:expr, $ty:tt) => {
115        FieldDefinition {
116            name: $name,
117            data_type: FieldType::Single(type_to_fdt!($ty)),
118        }
119    };
120}
121
122macro_rules! type_to_fdt {
123    (u8) => { PrimitiveType::U8 };
124    (u16) => { PrimitiveType::U16 };
125    (u32) => { PrimitiveType::U32 };
126    (u64) => { PrimitiveType::U64 };
127    (i8) => { PrimitiveType::I8 };
128    (i16) => { PrimitiveType::I16 };
129    (i32) => { PrimitiveType::I32 };
130    (i64) => { PrimitiveType::I64 };
131    (_) => { compile_error!("Unknown type") };
132}
133
134macro_rules! impl_get_field {
135    ($fun_name:ident, $fun_ty:tt, $ty_size:expr) => {
136        pub fn $fun_name(data: &[u8], offset: u32) -> Option<$fun_ty> {
137            let mut buf = [0u8; $ty_size];
138            buf.copy_from_slice(data.get(offset as usize..(offset + $ty_size) as usize)?);
139            Some($fun_ty::from_le_bytes(buf))
140        }
141    };
142}
143
144impl_get_field!(get_u8, u8, 1);
145impl_get_field!(get_u16, u16, 2);
146impl_get_field!(get_u32, u32, 4);
147impl_get_field!(get_u64, u64, 8);
148
149impl_get_field!(get_i8, i8, 1);
150impl_get_field!(get_i16, i16, 2);
151impl_get_field!(get_i32, i32, 4);
152impl_get_field!(get_i64, i64, 8);
153
154/* Block functions for working directly with blocks as bytes */
155pub fn block_get_type<'a>(data: &'a [u8]) -> Option<&'a str> {
156    str::from_utf8(&data[0..4]).ok()
157}
158
159pub fn block_get_timestamp(data: &[u8]) -> Option<u64> {
160    if block_get_type(data) == Some("MLVI") {
161        Some(0)
162    } else {
163        Some(u64::from_le_bytes(*data[8..16].first_chunk()?))
164    }
165}
166
167pub fn block_get_size(data: &[u8]) -> Option<u32> {
168    Some(u32::from_le_bytes(*data[4..8].first_chunk()?))
169}
170
171/*********************************************************************************
172                                 BLOCK TYPE DEFINITIONS
173*********************************************************************************/
174
175mlv_all_block_def! {
176    MLVI {
177        // fileMagic: [u8; 4],         /* Magic Lantern Video file header "MLVI" */
178        // blockSize: u32,           /* size of the whole header */
179        // versionString: [u8; 8],     /* null-terminated C-string of the exact revision of this format */
180        fileGuid: u64,            /* UID of the file (group) generated using hw counter, time of day and PRNG */
181        fileNum: u16,             /* the ID within fileCount this file has (0 to fileCount-1) */
182        fileCount: u16,           /* how many files belong to this group (splitting or parallel) */
183        fileFlags: u32,           /* 1=out-of-order data, 2=dropped frames, 4=single image mode, 8=stopped due to error */
184        videoClass: u16,          /* 0=none, 1=RAW, 2=YUV, 3=JPEG, 4=H.264 */
185        audioClass: u16,          /* 0=none, 1=WAV */
186        videoFrameCount: u32,     /* number of video frames in this file. set to 0 on start, updated when finished. */
187        audioFrameCount: u32,     /* number of audio frames in this file. set to 0 on start, updated when finished. */
188        sourceFpsNom: u32,        /* configured fps in 1/s multiplied by sourceFpsDenom */
189        sourceFpsDenom: u32,      /* denominator for fps. usually set to 1000, but may be 1001 for NTSC */
190    }
191
192    VIDF {
193        frameNumber: u32,         /* unique video frame number */
194        cropPosX: u16,            /* specifies from which sensor row/col the video frame was copied (8x2 blocks) */
195        cropPosY: u16,            /* (can be used to process dead/hot pixels) */
196        panPosX: u16,             /* specifies the panning offset which is cropPos, but with higher resolution (1x1 blocks) */
197        panPosY: u16,             /* (it's the frame area from sensor the user wants to see) */
198        frameSpace: u32,          /* size of dummy data before frameData starts, necessary for EDMAC alignment */
199    }
200
201    AUDF {
202        frameNumber: u32,         /* unique audio frame number */
203        frameSpace: u32           /* size of dummy data before frameData starts, necessary for EDMAC alignment */
204        /* uint8_t     frameData[variable]; */
205    }
206
207    RAWI {
208        xRes: u16,                /* Configured video resolution, may differ from payload resolution */
209        yRes: u16,                /* Configured video resolution, may differ from payload resolution */
210        // raw_info: RawInfo,          /* the raw_info structure delivered by raw.h of ML Core */
211
212        api_version: u32,
213        do_not_use_this: u32,
214
215        height: i32,
216        width: i32,
217        pitch: i32,
218        frame_size: i32,
219        bits_per_pixel: i32,              // 14
220
221        black_level: i32,                 // autodetected
222        white_level: i32,                 // somewhere around 13000 - 16000, varies with camera, settings etc
223                                            // would be best to autodetect it, but we can't do this reliably yet
224
225        // "DNG JPEG info"
226        jpeg_x: i32, jpeg_y: i32,
227        jpeg_width: i32, jpeg_height: i32,
228
229        // DNG active sensor area (Y1, X1, Y2, X2)
230        dng_active_area: [i32; 4],
231
232        exposure_bias: [i32; 2],        // DNG Exposure Bias (idk what's that)
233        cfa_pattern: i32,               // stick to 0x02010100 (RGBG) if you can
234        calibration_illuminant1: i32,
235        color_matrix1: [i32; 18],       // DNG Color Matrix
236        dynamic_range: i32              // EV x100, from analyzing black level and noise (very close to DxO)
237    }
238
239    WAVI {
240        format: u16,            /* 1=Integer PCM, 6=alaw, 7=mulaw */
241        channels: u16,          /* audio channel count: 1=mono, 2=stereo */
242        samplingRate: u32,      /* audio sampling rate in 1/s */
243        bytesPerSecond: u32,    /* audio data rate */
244        blockAlign: u16,        /* see RIFF WAV hdr description */
245        bitsPerSample: u16      /* audio ADC resolution */
246    }
247
248    EXPO {
249        isoMode: u32,            /* 0=manual, 1=auto */
250        isoValue: u32,           /* camera delivered ISO value */
251        isoAnalog: u32,          /* ISO obtained by hardware amplification (most full-stop ISOs, except extreme values) */
252        digitalGain: u32,        /* digital ISO gain (1024 = 1 EV) - it's not baked in the raw data, so you may want to scale it or adjust the white level */
253        shutterValue: u64,       /* exposure time in microseconds */
254    }
255
256    RAWC {
257        blockType: [u8; 4],         /* RAWC - raw image capture information */
258        blockSize: u32,           /* sizeof(mlv_rawc_hdr_t) */
259        timestamp: u64,           /* hardware counter timestamp */
260
261        /* see struct raw_capture_info from raw.h */
262
263        /* sensor attributes: resolution, crop factor */
264        sensor_res_x: u16,        /* sensor resolution */
265        sensor_res_y: u16,        /* 2-3 GPixel cameras anytime soon? (to overflow this) */
266        sensor_crop: u16,         /* sensor crop factor x100 */
267        reserved: u16,            /* reserved for future use */
268
269        /* video mode attributes */
270        /* (how the sensor is configured for image capture) */
271        /* subsampling factor: (binning_x+skipping_x) x (binning_y+skipping_y) */
272        binning_x: u8,              /* 3 (1080p and 720p); 1 (crop, zoom) */
273        skipping_x: u8,             /* so far, 0 everywhere */
274        binning_y: u8,              /* 1 (most cameras in 1080/720p; also all crop modes); 3 (5D3 1080p); 5 (5D3 720p) */
275        skipping_y: u8,             /* 2 (most cameras in 1080p); 4 (most cameras in 720p); 0 (5D3) */
276        offset_x: i16,            /* crop offset (top-left active pixel) - optional (SHRT_MIN if unknown) */
277        offset_y: i16,            /* relative to top-left active pixel from a full-res image (FRSP or CR2) */
278
279        /* The captured *active* area (raw_info.active_area) will be mapped
280         * on a full-res image (which does not use subsampling) as follows:
281         *   active_width  = raw_info.active_area.x2 - raw_info.active_area.x1
282         *   active_height = raw_info.active_area.y2 - raw_info.active_area.y1
283         *   .x1 (left)  : offset_x + full_res.active_area.x1
284         *   .y1 (top)   : offset_y + full_res.active_area.y1
285         *   .x2 (right) : offset_x + active_width  * (binning_x+skipping_x) + full_res.active_area.x1
286         *   .y2 (bottom): offset_y + active_height * (binning_y+skipping_y) + full_res.active_area.y1
287         */
288    }
289}
290
291pub const MLV_VERSION_STRING: &str = "2.0";