]>
Commit | Line | Data |
---|---|---|
e01d4987 KS |
1 | use nihav_core::demuxers::*; |
2 | ||
3 | struct 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 | ||
17 | impl<'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 | ||
35 | const IVF_GUID_0: [u8; 16] = [0x50, 0xEF, 0x81, 0x19, 0xB3, 0xBD, 0xD0, 0x11, 0xA3, 0xE5, 0x00, 0xA0, 0xC9, 0x24, 0x44, 0x36]; | |
36 | const IVF_GUID_1: [u8; 16] = [0x50, 0xEF, 0x81, 0x19, 0xB3, 0xBD, 0xD0, 0x11, 0xA3, 0xE5, 0x00, 0xA0, 0xC9, 0x24, 0x44, 0x37]; | |
37 | ||
38 | ||
39 | impl<'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)?; | |
43 | let version = match &guid { | |
44 | &IVF_GUID_0 => 0, | |
45 | &IVF_GUID_1 => 1, | |
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 | ||
88 | let mut vhdr = NAVideoInfo::new(width, height.abs() as usize, height < 0, YUV420_FORMAT); | |
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 | ||
236 | const PASSES: &str = "passes"; | |
237 | ||
238 | const 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 | ||
244 | impl<'a> NAOptionHandler for IVFDemuxer<'a> { | |
245 | fn get_supported_options(&self) -> &[NAOptionDefinition] { DEMUXER_OPTS } | |
246 | fn set_options(&mut self, options: &[NAOption]) { | |
247 | for option in options.iter() { | |
248 | for opt_def in DEMUXER_OPTS.iter() { | |
249 | if opt_def.check(option).is_ok() { | |
250 | match option.name { | |
251 | PASSES => { | |
252 | if let NAValue::Int(intval) = option.value { | |
253 | self.passes = intval as u8; | |
254 | } | |
255 | }, | |
256 | _ => {}, | |
257 | } | |
258 | } | |
259 | } | |
260 | } | |
261 | } | |
262 | fn query_option_value(&self, name: &str) -> Option<NAValue> { | |
263 | match name { | |
264 | PASSES => Some(NAValue::Int(i64::from(self.passes))), | |
265 | _ => None, | |
266 | } | |
267 | } | |
268 | } | |
269 | ||
270 | pub struct IVFDemuxerCreator { } | |
271 | ||
272 | impl DemuxerCreator for IVFDemuxerCreator { | |
273 | fn new_demuxer<'a>(&self, br: &'a mut ByteReader<'a>) -> Box<dyn DemuxCore<'a> + 'a> { | |
274 | Box::new(IVFDemuxer::new(br)) | |
275 | } | |
276 | fn get_name(&self) -> &'static str { "ivf" } | |
277 | } | |
278 | ||
279 | #[cfg(test)] | |
280 | mod test { | |
281 | use super::*; | |
282 | use std::fs::File; | |
283 | ||
284 | #[test] | |
285 | fn test_ivf_demux() { | |
286 | // sample is a trailer for Heart of Darkness game | |
287 | let mut file = File::open("assets/Indeo/TRAILERIIE.IVF").unwrap(); | |
288 | let mut fr = FileReader::new_read(&mut file); | |
289 | let mut br = ByteReader::new(&mut fr); | |
290 | let mut dmx = IVFDemuxer::new(&mut br); | |
291 | let mut sm = StreamManager::new(); | |
292 | let mut si = SeekIndex::new(); | |
293 | dmx.open(&mut sm, &mut si).unwrap(); | |
294 | ||
295 | loop { | |
296 | let pktres = dmx.get_frame(&mut sm); | |
297 | if let Err(e) = pktres { | |
298 | if e == DemuxerError::EOF { break; } | |
299 | panic!("error"); | |
300 | } | |
301 | let pkt = pktres.unwrap(); | |
302 | println!("Got {}", pkt); | |
303 | } | |
304 | } | |
305 | } |