1use std::{fs::File, io::Read, path::Path};
5
6use byteorder::{LittleEndian, ReadBytesExt};
7
8use chrono::{DateTime, Utc};
9use formats::PixFmt;
10use strand_dynamic_frame::DynamicFrameOwned;
11
12use crate::{FMFError, FMFResult, pixel_formats};
13
14const TIMESTAMP_SIZE: usize = 8;
15
16fn open_buffered<P: AsRef<Path>>(p: &P) -> std::io::Result<std::io::BufReader<File>> {
17 Ok(std::io::BufReader::new(File::open(p.as_ref())?))
18}
19
20pub struct FMFReader {
21 f: Box<dyn Read>,
23 pixel_format: PixFmt,
24 height: u32,
25 width: u32,
26 image_data_size: usize,
27 n_frames: usize,
30 count: usize,
31 file_pos: usize,
32 did_error: bool,
33}
34
35impl FMFReader {
36 pub fn new<P: AsRef<Path>>(path: P) -> FMFResult<FMFReader> {
37 let extension = path.as_ref().extension().and_then(|x| x.to_str());
38 let mut f: Box<dyn Read> = if extension == Some("gz") {
39 let gz_fd = open_buffered(&path).map_err(|e| FMFError::IoPath {
40 source: e,
41 path: path.as_ref().display().to_string(),
42 })?;
43 let decoder = libflate::gzip::Decoder::new(gz_fd)?;
44 Box::new(decoder)
45 } else {
46 Box::new(open_buffered(&path).map_err(|e| FMFError::IoPath {
47 source: e,
48 path: path.as_ref().display().to_string(),
49 })?)
50 };
51
52 let mut pos = 0;
54 let version = f.read_u32::<LittleEndian>()?;
55 pos += 4;
56 if version != 3 {
57 return Err(FMFError::UnimplementedVersion(version));
58 }
59
60 let expected_format_len = f.read_u32::<LittleEndian>()? as usize;
62 pos += 4;
63 let mut format: Vec<u8> = vec![0; expected_format_len];
64 let actual_format_len = f.read(&mut format)?;
65 pos += actual_format_len;
66 if expected_format_len != actual_format_len {
67 return Err(FMFError::PrematureFileEnd);
68 }
69 let pixel_format = pixel_formats::get_pixel_format(&format)?;
70
71 let _bpp = f.read_u32::<LittleEndian>()?;
72 pos += 4;
73 let height = f.read_u32::<LittleEndian>()?;
74 pos += 4;
75 let width = f.read_u32::<LittleEndian>()?;
76 pos += 4;
77 let chunksize: usize = f.read_u64::<LittleEndian>()?.try_into().unwrap();
78 assert!(chunksize > TIMESTAMP_SIZE);
79 let image_data_size = chunksize - TIMESTAMP_SIZE;
80 pos += 8;
81 let n_frames = f.read_u64::<LittleEndian>()?.try_into().unwrap();
82 pos += 8;
83 let count = 0;
84
85 Ok(Self {
86 f,
87 pixel_format,
88 height,
89 width,
90 image_data_size,
91 n_frames,
92 count,
93 file_pos: pos,
94 did_error: false,
95 })
96 }
97
98 #[inline]
99 pub fn width(&self) -> u32 {
100 self.width
101 }
102
103 #[inline]
104 pub fn height(&self) -> u32 {
105 self.height
106 }
107
108 #[inline]
109 pub fn format(&self) -> PixFmt {
110 self.pixel_format
111 }
112
113 pub fn file_pos(&self) -> usize {
114 self.file_pos
115 }
116
117 pub fn n_frames(&self) -> usize {
119 self.n_frames
120 }
121
122 fn next_frame(&mut self) -> FMFResult<(DynamicFrameOwned, DateTime<Utc>)> {
123 if self.count >= self.n_frames {
125 return Err(FMFError::ReadingPastEnd);
126 }
127
128 let mut timestamp_data: Vec<u8> = vec![0; TIMESTAMP_SIZE];
129 self.f.read_exact(&mut timestamp_data)?;
130 self.file_pos += TIMESTAMP_SIZE;
131
132 let mut image_data: Vec<u8> = vec![0; self.image_data_size];
133 self.f.read_exact(&mut image_data)?;
134 self.file_pos += self.image_data_size;
135
136 let timestamp_f64 = timestamp_data.as_slice().read_f64::<LittleEndian>()?;
137 let dt = strand_datetime_conversion::f64_to_datetime(timestamp_f64);
138
139 let width = self.width;
140 let height = self.height;
141 let pixel_format = self.pixel_format;
142 let bpp = self.pixel_format.bits_per_pixel() as u32;
143 let stride = (width * bpp) / 8;
144 self.count += 1;
145
146 if let Some(dframe) =
147 DynamicFrameOwned::from_buf(width, height, stride as usize, image_data, pixel_format)
148 {
149 Ok((dframe, dt))
150 } else {
151 Err(FMFError::UnexpectedSize)
152 }
153 }
154}
155
156impl Iterator for FMFReader {
157 type Item = FMFResult<(DynamicFrameOwned, DateTime<Utc>)>;
158 fn next(&mut self) -> Option<Self::Item> {
159 if self.did_error {
160 return None;
162 }
163
164 if self.count >= self.n_frames {
165 return None;
167 }
168
169 let frame = self.next_frame();
170 if frame.is_err() {
171 self.did_error = true;
172 }
173 Some(frame)
174 }
175}