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