Commit | Line | Data |
---|---|---|
9895bd7b KS |
1 | use nihav_core::frame::*; |
2 | use nihav_core::demuxers::*; | |
3 | use std::io::SeekFrom; | |
4 | ||
5563dfce KS |
5 | const OLD_HEADER_SIZE: usize = 0x330; |
6 | const NEW_HEADER_SIZE: usize = 0x34; | |
7 | const HEADER1_SIZE: usize = 28; | |
8 | const HEADER2_OFF: usize = HEADER1_SIZE + 768; | |
9895bd7b KS |
9 | const FRAME_HDR_SIZE: usize = 10; |
10 | ||
11 | const CHTYPE_VIDEO: u8 = 0x02; | |
12 | const CHTYPE_AUDIO: u8 = 0x01; | |
13 | ||
14 | #[derive(Clone,Copy)] | |
15 | struct FrameRec { | |
16 | chtype: u8, | |
17 | size: u32, | |
18 | off: u32, | |
19 | hdr: [u8; FRAME_HDR_SIZE], | |
20 | ts: u32, | |
21 | } | |
22 | ||
23 | struct VMDDemuxer<'a> { | |
24 | src: &'a mut ByteReader<'a>, | |
25 | vid_id: usize, | |
26 | aud_id: usize, | |
27 | fno: usize, | |
28 | is_indeo: bool, | |
29 | frames: Vec<FrameRec>, | |
30 | } | |
31 | ||
32 | impl<'a> DemuxCore<'a> for VMDDemuxer<'a> { | |
33 | #[allow(unused_variables)] | |
33b5a8f0 | 34 | fn open(&mut self, strmgr: &mut StreamManager, _seek_index: &mut SeekIndex) -> DemuxerResult<()> { |
9895bd7b KS |
35 | let src = &mut self.src; |
36 | ||
5563dfce KS |
37 | let mut header: [u8; OLD_HEADER_SIZE] = [0; OLD_HEADER_SIZE]; |
38 | src.read_buf(&mut header[..HEADER1_SIZE])?; | |
39 | let hdr_size = read_u16le(&header)? as usize + 2; | |
40 | validate!(hdr_size == OLD_HEADER_SIZE || hdr_size == NEW_HEADER_SIZE); | |
41 | if hdr_size == OLD_HEADER_SIZE { | |
42 | src.read_buf(&mut header[HEADER1_SIZE..][..768])?; | |
43 | } | |
44 | src.read_buf(&mut header[HEADER2_OFF..])?; | |
9895bd7b KS |
45 | |
46 | let mut width = read_u16le(&header[12..])? as usize; | |
47 | let mut height = read_u16le(&header[14..])? as usize; | |
48 | self.is_indeo = &header[24..27] == b"iv3"; | |
49 | if self.is_indeo && width > 320 { | |
50 | width >>= 1; | |
51 | height >>= 1; | |
52 | } | |
53 | ||
54 | let nframes = read_u16le(&header[6..])? as usize; | |
55 | let fpb = read_u16le(&header[18..])? as usize; | |
56 | validate!(nframes > 0 && fpb > 0); | |
d24468d9 | 57 | |
5563dfce | 58 | let mut edata: Vec<u8> = Vec::with_capacity(OLD_HEADER_SIZE); |
9895bd7b KS |
59 | edata.extend_from_slice(&header); |
60 | let vhdr = NAVideoInfo::new(width, height, false, PAL8_FORMAT); | |
61 | let vci = NACodecTypeInfo::Video(vhdr); | |
62 | let vinfo = NACodecInfo::new(if !self.is_indeo { "vmd-video" } else { "indeo3" }, vci, Some(edata)); | |
63 | self.vid_id = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 1, 12)).unwrap(); | |
64 | ||
5563dfce KS |
65 | let is_ext_audio = (hdr_size & 0xF) == 4; |
66 | let ext_audio_id = if is_ext_audio { | |
67 | src.read_u16le()? | |
68 | } else { 0 }; | |
69 | if is_ext_audio { | |
70 | validate!(ext_audio_id >= 3 && ext_audio_id <= 6); | |
71 | } | |
e69b1148 | 72 | let srate = u32::from(read_u16le(&header[804..])?); |
aa7e65d2 | 73 | let block_size; |
9895bd7b KS |
74 | if srate > 0 { |
75 | let bsize = read_u16le(&header[806..])? as usize; | |
aa7e65d2 | 76 | let channels = if (header[811] & 0x8F) != 0 { 2 } else { 1 }; |
9895bd7b | 77 | let is16bit; |
9895bd7b KS |
78 | if (bsize & 0x8000) != 0 { |
79 | is16bit = true; | |
80 | block_size = 0x10000 - bsize; | |
81 | } else { | |
82 | is16bit = false; | |
83 | block_size = bsize; | |
84 | } | |
85 | ||
f45dfcf7 KS |
86 | let mut aedata: Vec<u8> = Vec::with_capacity(2); |
87 | aedata.extend_from_slice(&header[810..][..2]); | |
9895bd7b | 88 | let ahdr = NAAudioInfo::new(srate, channels, if is16bit { SND_S16P_FORMAT } else { SND_U8_FORMAT }, block_size); |
5563dfce KS |
89 | let ac_name = if !is_ext_audio { |
90 | "vmd-audio" | |
91 | } else { | |
92 | "unknown" | |
93 | }; | |
94 | let ainfo = NACodecInfo::new(ac_name, NACodecTypeInfo::Audio(ahdr), Some(aedata)); | |
9895bd7b | 95 | self.aud_id = strmgr.add_stream(NAStream::new(StreamType::Audio, 1, ainfo, 1, srate)).unwrap(); |
aa7e65d2 KS |
96 | } else { |
97 | block_size = 0; | |
9895bd7b KS |
98 | } |
99 | ||
e69b1148 KS |
100 | let adelay = u32::from(read_u16le(&header[808..])?); |
101 | let idx_off = u64::from(read_u32le(&header[812..])?); | |
9895bd7b KS |
102 | src.seek(SeekFrom::Start(idx_off))?; |
103 | let mut offs: Vec<u32> = Vec::with_capacity(nframes); | |
104 | for i in 0..nframes { | |
105 | let _flags = src.read_u16le()?; | |
106 | let off = src.read_u32le()?; | |
107 | offs.push(off); | |
108 | } | |
109 | self.frames.reserve(nframes * fpb); | |
110 | let mut ats = adelay; | |
111 | for i in 0..nframes { | |
112 | let mut off = offs[i]; | |
113 | for _ in 0..fpb { | |
114 | let chtype = src.read_byte()?; | |
115 | src.read_skip(1)?; | |
aa7e65d2 | 116 | let mut size = src.read_u32le()?; |
9895bd7b KS |
117 | let mut hdr: [u8; FRAME_HDR_SIZE] = [0; FRAME_HDR_SIZE]; |
118 | src.read_buf(&mut hdr)?; | |
aa7e65d2 KS |
119 | if (i == 0) && (chtype == CHTYPE_AUDIO) && (size > 4) && ((size as usize) < block_size/2) { |
120 | size += 0x10000; | |
121 | } | |
9895bd7b KS |
122 | if (chtype == CHTYPE_VIDEO || chtype == CHTYPE_AUDIO) && (size > 0) { |
123 | let ts = if (i == 0) || (chtype != CHTYPE_AUDIO) { | |
124 | i as u32 | |
125 | } else { | |
126 | ats | |
127 | }; | |
128 | self.frames.push(FrameRec { chtype, size, hdr, off, ts }); | |
129 | } | |
130 | if i > 0 && chtype == CHTYPE_AUDIO { | |
131 | ats += 1; | |
132 | } | |
133 | if chtype != 0 { | |
134 | validate!(off.checked_add(size).is_some()); | |
135 | off += size; | |
136 | } | |
137 | } | |
138 | } | |
139 | ||
140 | self.fno = 0; | |
141 | Ok(()) | |
142 | } | |
143 | ||
144 | fn get_frame(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<NAPacket> { | |
145 | if self.fno >= self.frames.len() { return Err(DemuxerError::EOF); } | |
146 | let cur_frame = &self.frames[self.fno]; | |
147 | //println!("fno {} -> type {} size {} @ {:X} ts {}", self.fno, cur_frame.chtype, cur_frame.size, cur_frame.off, cur_frame.ts); | |
e69b1148 | 148 | let next_pos = u64::from(cur_frame.off); |
9895bd7b KS |
149 | if self.src.tell() != next_pos { |
150 | self.src.seek(SeekFrom::Start(next_pos))?; | |
151 | } | |
152 | ||
153 | let is_video = cur_frame.chtype == CHTYPE_VIDEO; | |
154 | let mut buf: Vec<u8> = Vec::with_capacity(FRAME_HDR_SIZE + (cur_frame.size as usize)); | |
155 | if !self.is_indeo || !is_video { | |
156 | buf.extend_from_slice(&cur_frame.hdr); | |
157 | buf.resize(FRAME_HDR_SIZE + (cur_frame.size as usize), 0); | |
158 | self.src.read_buf(&mut buf[FRAME_HDR_SIZE..])?; | |
159 | } else { | |
160 | buf.resize(cur_frame.size as usize, 0); | |
161 | self.src.read_buf(&mut buf)?; | |
162 | } | |
163 | ||
164 | self.fno += 1; | |
165 | ||
166 | let str_id = if is_video { self.vid_id } else { self.aud_id }; | |
167 | let str = strmgr.get_stream(str_id).unwrap(); | |
168 | let (tb_num, tb_den) = str.get_timebase(); | |
e69b1148 | 169 | let ts = NATimeInfo::new(Some(u64::from(cur_frame.ts)), None, None, tb_num, tb_den); |
9895bd7b KS |
170 | let pkt = NAPacket::new(str, ts, false, buf); |
171 | ||
172 | Ok(pkt) | |
173 | } | |
174 | ||
33b5a8f0 KS |
175 | fn seek(&mut self, _time: u64, _seek_index: &SeekIndex) -> DemuxerResult<()> { |
176 | Err(DemuxerError::NotPossible) | |
9895bd7b KS |
177 | } |
178 | } | |
179 | ||
180 | impl<'a> VMDDemuxer<'a> { | |
181 | fn new(io: &'a mut ByteReader<'a>) -> Self { | |
182 | Self { | |
183 | src: io, | |
184 | vid_id: 0, | |
185 | aud_id: 0, | |
186 | fno: 0, | |
187 | is_indeo: false, | |
188 | frames: Vec::new(), | |
189 | } | |
190 | } | |
191 | } | |
192 | ||
193 | pub struct VMDDemuxerCreator { } | |
194 | ||
195 | impl DemuxerCreator for VMDDemuxerCreator { | |
6011e201 | 196 | fn new_demuxer<'a>(&self, br: &'a mut ByteReader<'a>) -> Box<dyn DemuxCore<'a> + 'a> { |
9895bd7b KS |
197 | Box::new(VMDDemuxer::new(br)) |
198 | } | |
199 | fn get_name(&self) -> &'static str { "vmd" } | |
200 | } | |
201 | ||
202 | #[cfg(test)] | |
203 | mod test { | |
204 | use super::*; | |
205 | use std::fs::File; | |
206 | ||
207 | #[test] | |
208 | fn test_vmd_demux() { | |
1678d59a KS |
209 | let mut file = File::open("assets/Game/128.vmd").unwrap(); |
210 | //let mut file = File::open("assets/Game/1491.VMD").unwrap(); | |
9895bd7b KS |
211 | let mut fr = FileReader::new_read(&mut file); |
212 | let mut br = ByteReader::new(&mut fr); | |
213 | let mut dmx = VMDDemuxer::new(&mut br); | |
214 | let mut sm = StreamManager::new(); | |
caf0f37e KS |
215 | let mut si = SeekIndex::new(); |
216 | dmx.open(&mut sm, &mut si).unwrap(); | |
9895bd7b KS |
217 | loop { |
218 | let pktres = dmx.get_frame(&mut sm); | |
219 | if let Err(e) = pktres { | |
220 | if (e as i32) == (DemuxerError::EOF as i32) { break; } | |
221 | panic!("error"); | |
222 | } | |
223 | let pkt = pktres.unwrap(); | |
224 | println!("Got {}", pkt); | |
225 | } | |
226 | } | |
227 | } |