| 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", |
| 93 | b"IV50" => "indeo5s", |
| 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) { |
| 221 | let ts = stream.make_ts(Some(tstamp as u64), None, None); |
| 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 | } |