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";