fix clippy warnings
[nihav.git] / nihav-indeo / src / demuxers / ivf.rs
CommitLineData
e01d4987
KS
1use nihav_core::demuxers::*;
2
3struct IVFDemuxer<'a> {
4 src: &'a mut ByteReader<'a>,
5 nframes: u32,
6 vframe: u32,
7 aframe: u32,
8 size: u64,
9 vframes: Vec<Vec<u8>>,
10 vsizes: Vec<u32>,
11 aframes: Vec<Vec<u8>>,
12 do_v: bool,
13
14 passes: u8,
15}
16
17impl<'a> IVFDemuxer<'a> {
18 fn new(src: &'a mut ByteReader<'a>) -> Self {
19 IVFDemuxer {
20 src,
21 nframes: 0,
22 vframe: 0,
23 aframe: 0,
24 size: 0,
25 vframes: Vec::new(),
26 aframes: Vec::new(),
27 vsizes: Vec::new(),
28 do_v: false,
29
30 passes: 0,
31 }
32 }
33}
34
35const IVF_GUID_0: [u8; 16] = [0x50, 0xEF, 0x81, 0x19, 0xB3, 0xBD, 0xD0, 0x11, 0xA3, 0xE5, 0x00, 0xA0, 0xC9, 0x24, 0x44, 0x36];
36const IVF_GUID_1: [u8; 16] = [0x50, 0xEF, 0x81, 0x19, 0xB3, 0xBD, 0xD0, 0x11, 0xA3, 0xE5, 0x00, 0xA0, 0xC9, 0x24, 0x44, 0x37];
37
38
39impl<'a> DemuxCore<'a> for IVFDemuxer<'a> {
40 fn open(&mut self, strmgr: &mut StreamManager, _seek_index: &mut SeekIndex) -> DemuxerResult<()> {
41 let mut guid = [0; 16];
42 self.src.read_buf(&mut guid)?;
e6aaad5c
KS
43 let version = match guid {
44 IVF_GUID_0 => 0,
45 IVF_GUID_1 => 1,
e01d4987
KS
46 _ => return Err(DemuxerError::InvalidData),
47 };
48 let flags = self.src.read_u32le()?;
49 // file header - 0x9C bytes
50 let aframes = self.src.read_u32le()? as usize;
51 self.src.read_skip(12)?;
52 self.size = u64::from(self.src.read_u32le()?);
53 self.src.read_skip(136)?;
54 // video stream header - 0x8C bytes
55 let tag = self.src.read_tag()?;
56 validate!(&tag == b"vids");
57 self.src.read_skip(16)?;
58 let tb_num = self.src.read_u32le()?;
59 let tb_den = self.src.read_u32le()?;
60 self.src.read_skip(4)?;
61 self.nframes = self.src.read_u32le()?;
62 self.src.read_skip(104)?;
63
64 let (atb_num, atb_den, aduration) = if (flags & 1) != 0 {
65 // audio stream header - 0x8C bytes
66 let tag = self.src.read_tag()?;
67 validate!(&tag == b"auds");
68 self.src.read_skip(16)?;
69 let tb_num = self.src.read_u32le()?;
70 let tb_den = self.src.read_u32le()?;
71 self.src.read_skip(4)?;
72 let duration = self.src.read_u32le()?;
73 self.src.read_skip(104)?;
74 (tb_num, tb_den, duration)
75 } else { (0, 0, 0) };
76
77 let vhdr_size = self.src.read_u32le()? as usize;
78 validate!(vhdr_size >= 40);
79 let bmpi_size = self.src.read_u32le()? as usize;
80 validate!(bmpi_size == vhdr_size);
81 let width = self.src.read_u32le()? as usize;
82 let height = self.src.read_u32le()? as i32;
83 let planes = self.src.read_u16le()?;
84 let bitcount = self.src.read_u16le()?;
85 let fcc = self.src.read_tag()?;
86 self.src.read_skip(20)?;
87
e6aaad5c 88 let mut vhdr = NAVideoInfo::new(width, height.unsigned_abs() as usize, height < 0, YUV420_FORMAT);
e01d4987
KS
89 vhdr.bits = (planes as u8) * (bitcount as u8);
90 let cname = match &fcc {
91 b"IV31" | b"IV32" => "indeo3",
92 b"IV41" => "indeo4",
f7686373 93 b"IV50" => "indeo5s",
e01d4987
KS
94 _ => "unknown",
95 };
96 let edata = if vhdr_size > 40 {
97 let mut buf = vec![0; vhdr_size - 40];
98 self.src.read_buf(&mut buf)?;
99 Some(buf)
100 } else {
101 None
102 };
103 let vinfo = NACodecInfo::new(cname, NACodecTypeInfo::Video(vhdr), edata);
104 let res = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, tb_num, tb_den, u64::from(self.nframes)));
105 if res.is_none() { return Err(DemuxerError::MemoryError); }
106
107 if (flags & 1) != 0 {
108 let ahdr_size = self.src.read_u32le()? as usize;
109 validate!(ahdr_size >= 16);
110 let w_format_tag = self.src.read_u16le()?;
111 let channels = self.src.read_u16le()?;
112 let samplespersec = self.src.read_u32le()?;
113 let _avgbytespersec = self.src.read_u32le()?;
114 let block_align = self.src.read_u16le()?;
115 let bits_per_sample = self.src.read_u16le()?;
116
117 let signed = bits_per_sample > 8;
118 let soniton = NASoniton::new(bits_per_sample as u8, if signed { SONITON_FLAG_SIGNED } else { 0 });
119 let ahdr = NAAudioInfo::new(samplespersec, channels as u8, soniton, block_align as usize);
120 let edata = if ahdr_size > 16 {
121 let edata_size = self.src.read_u16le()? as usize;
122 validate!(edata_size + 18 == ahdr_size);
123 if edata_size > 0 {
124 let mut buf = vec![0; edata_size];
125 self.src.read_buf(&mut buf)?;
126 Some(buf)
127 } else {
128 None
129 }
130 } else {
131 None
132 };
133
134 let cname = match w_format_tag {
135 0x401 => "iac",
136 0x402 => "imc",
137 _ => "unknown",
138 };
139
140 let ainfo = NACodecInfo::new(cname, NACodecTypeInfo::Audio(ahdr), edata);
141 let res = strmgr.add_stream(NAStream::new(StreamType::Audio, 1, ainfo, atb_num, atb_den, u64::from(aduration)));
142 if res.is_none() { return Err(DemuxerError::MemoryError); }
143 }
144
145 // video frame table
146 self.vsizes.reserve(self.nframes as usize);
147 for _ in 0..self.nframes {
148 let size = self.src.read_u32le()?;
149 self.vsizes.push(size);
150 }
151
152 if version == 1 {
153 self.src.read_skip(128)?;
154 }
155
156 let comment_len = self.src.read_u32le()? as usize;
157 self.src.read_skip(comment_len)?;
158
159 self.vframe = 0;
160 self.aframe = 0;
161
162 self.vframes = Vec::with_capacity(self.nframes as usize);
163 self.aframes = Vec::with_capacity(aframes);
164 for _ in 0..self.nframes {
165 self.vframes.push(Vec::new());
166 }
167 for _ in 0..aframes {
168 self.aframes.push(Vec::new());
169 }
170
171 let mut last_ts = 1 << 31;
172 let mut pass = 0;
173 while self.src.tell() < self.size {
174 let flg = self.src.read_u32le()?;
175 let fsize = self.src.read_u32le()? as usize;
176
177 let tstamp = (flg >> 1) as usize;
178
179 if (flg & 1) != 0 {
180 if last_ts > tstamp {
181 pass += 1;
182 if self.passes != 0 && pass > self.passes {
183 break;
184 }
185 }
186 last_ts = tstamp;
187 }
188
189 let dst = if (flg & 1) != 0 { &mut self.vframes[tstamp] } else { &mut self.aframes[tstamp] };
190 let cur_size = dst.len();
191 dst.resize(cur_size + fsize, 0);
192 self.src.read_buf(&mut dst[cur_size..])?;
193 }
194
195 // remove provisionary code for drop frames if real data is present
196 for frm in self.vframes.iter_mut() {
197 if frm.len() > 2 && frm[0] == 0x9F && frm[1] == 0x00 {
198 frm.remove(0);
199 frm.remove(0);
200 }
201 }
202
203 Ok(())
204 }
205
206 fn get_frame(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<NAPacket> {
207 let has_next = if self.do_v { self.vframe < self.nframes } else { self.aframe < self.nframes };
208 if has_next {
209 let (stream_id, tstamp, buf) = if self.do_v {
210 self.vframe += 1;
211 (0, self.vframe - 1, self.vframes[self.vframe as usize - 1].clone())
212 } else {
213 self.aframe += 1;
214 (1, self.aframe - 1, self.aframes[self.aframe as usize - 1].clone())
215 };
216 if !self.do_v || (self.aframe as usize) < self.aframes.len() {
217 self.do_v = !self.do_v;
218 }
219
220 if let Some(stream) = strmgr.get_stream(stream_id) {
8e14efb0 221 let ts = stream.make_ts(Some(tstamp as u64), None, None);
e01d4987
KS
222 return Ok(NAPacket::new_from_refbuf(stream, ts, false, NABufferRef::new(buf)));
223 } else {
224 return Err(DemuxerError::InvalidData);
225 }
226 }
227 Err(DemuxerError::EOF)
228 }
229
230 fn seek(&mut self, _time: NATimePoint, _seek_index: &SeekIndex) -> DemuxerResult<()> {
231 Err(DemuxerError::NotPossible)
232 }
233 fn get_duration(&self) -> u64 { 0 }
234}
235
236const PASSES: &str = "passes";
237
238const DEMUXER_OPTS: &[NAOptionDefinition] = &[
239 NAOptionDefinition {
240 name: PASSES, description: "Number of passes to assemble data (0 = all)",
241 opt_type: NAOptionDefinitionType::Int(Some(0), Some(128)) },
242];
243
244impl<'a> NAOptionHandler for IVFDemuxer<'a> {
245 fn get_supported_options(&self) -> &[NAOptionDefinition] { DEMUXER_OPTS }
e6aaad5c 246 #[allow(clippy::single_match)]
e01d4987
KS
247 fn set_options(&mut self, options: &[NAOption]) {
248 for option in options.iter() {
249 for opt_def in DEMUXER_OPTS.iter() {
250 if opt_def.check(option).is_ok() {
251 match option.name {
252 PASSES => {
253 if let NAValue::Int(intval) = option.value {
254 self.passes = intval as u8;
255 }
256 },
257 _ => {},
258 }
259 }
260 }
261 }
262 }
e6aaad5c 263 #[allow(clippy::single_match)]
e01d4987
KS
264 fn query_option_value(&self, name: &str) -> Option<NAValue> {
265 match name {
266 PASSES => Some(NAValue::Int(i64::from(self.passes))),
267 _ => None,
268 }
269 }
270}
271
272pub struct IVFDemuxerCreator { }
273
274impl DemuxerCreator for IVFDemuxerCreator {
275 fn new_demuxer<'a>(&self, br: &'a mut ByteReader<'a>) -> Box<dyn DemuxCore<'a> + 'a> {
276 Box::new(IVFDemuxer::new(br))
277 }
278 fn get_name(&self) -> &'static str { "ivf" }
279}
280
281#[cfg(test)]
282mod test {
283 use super::*;
284 use std::fs::File;
285
286 #[test]
287 fn test_ivf_demux() {
288 // sample is a trailer for Heart of Darkness game
289 let mut file = File::open("assets/Indeo/TRAILERIIE.IVF").unwrap();
290 let mut fr = FileReader::new_read(&mut file);
291 let mut br = ByteReader::new(&mut fr);
292 let mut dmx = IVFDemuxer::new(&mut br);
293 let mut sm = StreamManager::new();
294 let mut si = SeekIndex::new();
295 dmx.open(&mut sm, &mut si).unwrap();
296
297 loop {
298 let pktres = dmx.get_frame(&mut sm);
299 if let Err(e) = pktres {
300 if e == DemuxerError::EOF { break; }
301 panic!("error");
302 }
303 let pkt = pktres.unwrap();
304 println!("Got {}", pkt);
305 }
306 }
307}