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