]>
Commit | Line | Data |
---|---|---|
606c448e | 1 | use std::io::SeekFrom; |
3687b8b3 | 2 | use std::cmp::Ordering; |
606c448e KS |
3 | use nihav_core::demuxers::*; |
4 | ||
5 | const SMK_FLAG_LOOP_FRAME: u32 = 0x01; | |
6 | ||
7 | const SMK_FRAME_FLAG_PALETTE: u8 = 0x01; | |
8 | ||
9 | const SMK_AUD_FLAG_PACKED: u8 = 0x80; | |
10 | const SMK_AUD_FLAG_PRESENT: u8 = 0x40; | |
11 | const SMK_AUD_FLAG_16BIT: u8 = 0x20; | |
12 | const SMK_AUD_FLAG_STEREO: u8 = 0x10; | |
13 | const SMK_AUD_FLAG_BINKAUD_RDFT: u8 = 0x08; | |
14 | const SMK_AUD_FLAG_BINKAUD_DCT: u8 = 0x04; | |
15 | ||
16 | const NUM_AUDIO_TRACKS: usize = 7; | |
17 | ||
18 | const PAL_SIZE: usize = 768; | |
19 | ||
20 | const SMK_DEFAULT_PAL: [u8; 64] = [ | |
21 | 0x00, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x18, 0x1C, | |
22 | 0x20, 0x24, 0x28, 0x2C, 0x30, 0x34, 0x38, 0x3C, | |
23 | 0x41, 0x45, 0x49, 0x4D, 0x51, 0x55, 0x59, 0x5D, | |
24 | 0x61, 0x65, 0x69, 0x6D, 0x71, 0x75, 0x79, 0x7D, | |
25 | 0x82, 0x86, 0x8A, 0x8E, 0x92, 0x96, 0x9A, 0x9E, | |
26 | 0xA2, 0xA6, 0xAA, 0xAE, 0xB2, 0xB6, 0xBA, 0xBE, | |
27 | 0xC3, 0xC7, 0xCB, 0xCF, 0xD3, 0xD7, 0xDB, 0xDF, | |
28 | 0xE3, 0xE7, 0xEB, 0xEF, 0xF3, 0xF7, 0xFB, 0xFF | |
29 | ]; | |
30 | ||
31 | #[derive(Clone,Copy)] | |
32 | struct AudioTrack { | |
33 | size: u32, | |
34 | flags: u8, | |
35 | srate: u32, | |
36 | id: usize, | |
37 | } | |
38 | ||
39 | impl AudioTrack { | |
40 | fn new() -> Self { | |
41 | Self { size: 0, flags: 0, srate: 0, id: 0 } | |
42 | } | |
43 | fn is_present(&self) -> bool { | |
44 | (self.flags & SMK_AUD_FLAG_PRESENT) != 0 | |
45 | } | |
46 | fn add_stream(&mut self, strmgr: &mut StreamManager, str_id: usize) -> DemuxerResult<()> { | |
47 | let channels = if (self.flags & SMK_AUD_FLAG_STEREO) != 0 { 2 } else { 1 }; | |
48 | let codecname = if (self.flags & SMK_AUD_FLAG_BINKAUD_RDFT) != 0 { | |
49 | "bink-audio-rdft" | |
50 | } else if (self.flags & SMK_AUD_FLAG_BINKAUD_DCT) != 0 { | |
51 | "bink-audio-dct" | |
52 | } else if (self.flags & SMK_AUD_FLAG_PACKED) != 0 { | |
53 | "smacker-audio" | |
54 | } else { | |
55 | "pcm" | |
56 | }; | |
57 | let soniton = if (self.flags & SMK_AUD_FLAG_16BIT) != 0 { SND_S16_FORMAT } else { SND_U8_FORMAT }; | |
58 | let ahdr = NAAudioInfo::new(self.srate, channels, soniton, 1); | |
59 | let ainfo = NACodecInfo::new(codecname, NACodecTypeInfo::Audio(ahdr), None); | |
a480a0de | 60 | let res = strmgr.add_stream(NAStream::new(StreamType::Audio, (str_id + 1) as u32, ainfo, 1, self.srate, 0)); |
606c448e KS |
61 | validate!(res.is_some()); |
62 | self.id = res.unwrap(); | |
63 | ||
64 | Ok(()) | |
65 | } | |
66 | } | |
67 | ||
68 | struct SmackerVideoDemuxer<'a> { | |
69 | src: &'a mut ByteReader<'a>, | |
70 | frames: usize, | |
71 | pts_inc: u64, | |
72 | cur_pts: u64, | |
73 | ainfo: [AudioTrack; NUM_AUDIO_TRACKS], | |
74 | frame_sizes: Vec<u32>, | |
75 | frame_flags: Vec<u8>, | |
76 | video_id: usize, | |
77 | start: u64, | |
78 | cur_frame: usize, | |
79 | queued_packets: Vec<NAPacket>, | |
80 | pal: [u8; PAL_SIZE], | |
81 | } | |
82 | ||
83 | /*macro_rules! mktag { | |
84 | ($a:expr, $b:expr, $c:expr, $d:expr) => ({ | |
85 | (($a as u32) << 24) | (($b as u32) << 16) | (($c as u32) << 8) | ($d as u32) | |
86 | }); | |
87 | ($arr:expr) => ({ | |
88 | (($arr[0] as u32) << 24) | (($arr[1] as u32) << 16) | (($arr[2] as u32) << 8) | ($arr[3] as u32) | |
89 | }); | |
90 | }*/ | |
91 | ||
92 | fn get_pts_inc(val: i32) -> u64 { | |
3687b8b3 KS |
93 | match val.cmp(&0) { |
94 | Ordering::Greater => (val as u64) * 100, | |
95 | Ordering::Less => -val as u64, | |
96 | Ordering::Equal => 1, | |
97 | } | |
606c448e KS |
98 | } |
99 | ||
100 | impl<'a> DemuxCore<'a> for SmackerVideoDemuxer<'a> { | |
33b5a8f0 | 101 | fn open(&mut self, strmgr: &mut StreamManager, _seek_idx: &mut SeekIndex) -> DemuxerResult<()> { |
606c448e KS |
102 | let src = &mut self.src; |
103 | let mut magic: [u8; 4] = [0; 4]; | |
104 | src.read_buf(&mut magic)?; | |
105 | validate!((&magic == b"SMK2") || (&magic == b"SMK4")); | |
106 | let width = src.read_u32le()? as usize; | |
107 | let height = src.read_u32le()? as usize; | |
108 | self.frames = src.read_u32le()? as usize; | |
109 | let pts_inc = src.read_u32le()? as i32; | |
110 | let flags = src.read_u32le()?; | |
111 | validate!((width > 0) && (height > 0) && (self.frames > 0)); | |
112 | self.pts_inc = get_pts_inc(pts_inc); | |
113 | ||
114 | if (flags & SMK_FLAG_LOOP_FRAME) != 0 { | |
115 | self.frames += 1; | |
116 | } | |
117 | ||
118 | for i in 0..NUM_AUDIO_TRACKS { | |
119 | self.ainfo[i].size = src.read_u32le()?; | |
120 | } | |
121 | let treesize = src.read_u32le()? as usize; | |
122 | ||
123 | let mut treedata: Vec<u8> = Vec::with_capacity(treesize + 20); | |
124 | treedata.resize(treesize + 24, 0); | |
125 | let hdr = &mut treedata[0..4]; | |
126 | hdr.copy_from_slice(&magic); | |
127 | let flg = &mut treedata[4..8]; | |
128 | flg[0] = (flags & 0xFF) as u8; | |
129 | flg[1] = (flags >> 8) as u8; | |
130 | flg[2] = (flags >> 16) as u8; | |
131 | flg[3] = (flags >> 24) as u8; | |
132 | src.read_buf(&mut treedata[8..][..16])?; | |
133 | ||
134 | for i in 0..NUM_AUDIO_TRACKS { | |
135 | self.ainfo[i].srate = src.read_u24le()?; | |
136 | self.ainfo[i].flags = src.read_byte()?; | |
137 | } | |
138 | ||
139 | src.read_skip(4)?; | |
140 | ||
141 | self.frame_sizes.resize(self.frames, 0); | |
142 | self.frame_flags.resize(self.frames, 0); | |
143 | for i in 0..self.frames { | |
144 | self.frame_sizes[i] = src.read_u32le()?; | |
145 | } | |
146 | for i in 0..self.frames { | |
147 | self.frame_flags[i] = src.read_byte()?; | |
148 | } | |
149 | ||
150 | src.read_buf(&mut treedata[24..])?; | |
151 | ||
152 | let vhdr = NAVideoInfo::new(width, height, false, PAL8_FORMAT); | |
153 | let vinfo = NACodecInfo::new("smacker-video", NACodecTypeInfo::Video(vhdr), Some(treedata)); | |
a480a0de | 154 | let res = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 1, 100000, 0)); |
606c448e KS |
155 | validate!(res.is_some()); |
156 | self.video_id = res.unwrap(); | |
157 | ||
158 | for i in 0..NUM_AUDIO_TRACKS { | |
159 | if self.ainfo[i].is_present() { | |
160 | self.ainfo[i].add_stream(strmgr, i)?; | |
161 | } | |
162 | } | |
163 | ||
164 | self.start = src.tell(); | |
165 | self.cur_frame = 0; | |
166 | self.reset_state(); | |
d24468d9 | 167 | |
606c448e KS |
168 | Ok(()) |
169 | } | |
bf109afe | 170 | #[allow(clippy::identity_op)] |
606c448e KS |
171 | fn get_frame(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<NAPacket> { |
172 | if !self.queued_packets.is_empty() { | |
173 | let pkt = self.queued_packets.pop().unwrap(); | |
174 | return Ok(pkt); | |
175 | } | |
176 | if self.cur_frame >= self.frames { return Err(DemuxerError::EOF); } | |
177 | ||
178 | let mut payload_size = (self.frame_sizes[self.cur_frame] & !3) as usize; | |
179 | let frame_flags = self.frame_flags[self.cur_frame]; | |
180 | if (frame_flags & SMK_FRAME_FLAG_PALETTE) != 0 { | |
181 | let chunk_size = (self.src.read_byte()? as usize) * 4; | |
182 | validate!(chunk_size > 0); | |
183 | validate!(payload_size >= chunk_size); | |
184 | payload_size -= chunk_size; | |
7e6086e5 | 185 | let oldpal = self.pal; |
606c448e KS |
186 | let mut idx = 0; |
187 | let endpos = self.src.tell() + (chunk_size as u64) - 1; | |
188 | while idx < 256 { | |
189 | let op = self.src.read_byte()?; | |
190 | if (op & 0x80) != 0 { | |
191 | idx += ((op & 0x7F) as usize) + 1; | |
192 | } else if (op & 0x40) != 0 { | |
193 | let start = self.src.read_byte()? as usize; | |
194 | let len = ((op & 0x3F) as usize) + 1; | |
195 | validate!(idx + len <= 256); | |
196 | for i in 0..len*3 { | |
197 | self.pal[idx * 3 + i] = oldpal[start * 3 + i]; | |
198 | } | |
199 | idx += len; | |
200 | } else { | |
201 | let ix0 = op as usize; | |
202 | let ix1 = (self.src.read_byte()? & 0x3F) as usize; | |
203 | let ix2 = (self.src.read_byte()? & 0x3F) as usize; | |
204 | self.pal[idx * 3 + 0] = SMK_DEFAULT_PAL[ix0]; | |
205 | self.pal[idx * 3 + 1] = SMK_DEFAULT_PAL[ix1]; | |
206 | self.pal[idx * 3 + 2] = SMK_DEFAULT_PAL[ix2]; | |
207 | idx += 1; | |
208 | } | |
209 | } | |
210 | validate!(self.src.tell() <= endpos); | |
211 | if self.src.tell() < endpos { | |
212 | let skip_size = endpos - self.src.tell(); | |
213 | self.src.read_skip(skip_size as usize)?; | |
214 | } | |
215 | } | |
216 | ||
217 | let ts = NATimeInfo::new(Some(self.cur_pts), None, None, 1, 100000); | |
218 | for i in 0..NUM_AUDIO_TRACKS { | |
219 | if ((frame_flags >> (i + 1)) & 1) == 0 { continue; } | |
606c448e KS |
220 | let size = self.src.read_u32le()? as usize; |
221 | validate!(size > 4); | |
222 | validate!(payload_size >= size); | |
223 | payload_size -= size; | |
dc19613e KS |
224 | if !self.ainfo[i].is_present() { |
225 | self.src.read_skip(size - 4)?; | |
226 | continue; | |
227 | } | |
606c448e KS |
228 | let strres = strmgr.get_stream(self.ainfo[i].id); |
229 | validate!(strres.is_some()); | |
230 | let stream = strres.unwrap(); | |
231 | let pkt = self.src.read_packet(stream.clone(), ts, true, size - 4)?; | |
232 | self.queued_packets.push(pkt); | |
233 | } | |
234 | self.queued_packets.reverse(); | |
235 | ||
236 | let mut buf: Vec<u8> = Vec::with_capacity(PAL_SIZE + payload_size); | |
237 | buf.resize(PAL_SIZE, 0); | |
238 | buf.copy_from_slice(&self.pal[0..]); | |
239 | if payload_size > 0 { | |
240 | buf.resize(PAL_SIZE + payload_size, 0); | |
241 | self.src.read_buf(&mut buf[PAL_SIZE..])?; | |
242 | } | |
243 | ||
244 | let strres = strmgr.get_stream(self.video_id); | |
245 | validate!(strres.is_some()); | |
246 | let stream = strres.unwrap(); | |
247 | let keyframe = (self.frame_sizes[self.cur_frame] & 1) != 0; | |
248 | let pkt = NAPacket::new(stream, ts, keyframe, buf); | |
249 | ||
250 | self.cur_frame += 1; | |
251 | self.cur_pts += self.pts_inc; | |
252 | ||
253 | Ok(pkt) | |
254 | } | |
24d99894 | 255 | fn seek(&mut self, time: NATimePoint, _seek_idx: &SeekIndex) -> DemuxerResult<()> { |
e6aaad5c | 256 | let seek_to_start = matches!(time, NATimePoint::Milliseconds(0) | NATimePoint::PTS(0)); |
24d99894 | 257 | if seek_to_start { |
606c448e KS |
258 | let start = self.start; |
259 | self.src.seek(SeekFrom::Start(start))?; | |
260 | self.cur_frame = 0; | |
261 | self.cur_pts = 0; | |
262 | self.reset_state(); | |
263 | return Ok(()); | |
264 | } | |
265 | Err(DemuxerError::NotImplemented) | |
266 | } | |
a480a0de | 267 | fn get_duration(&self) -> u64 { self.frames as u64 * self.pts_inc / 100 } |
606c448e KS |
268 | } |
269 | ||
787b8d03 KS |
270 | impl<'a> NAOptionHandler for SmackerVideoDemuxer<'a> { |
271 | fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] } | |
272 | fn set_options(&mut self, _options: &[NAOption]) { } | |
273 | fn query_option_value(&self, _name: &str) -> Option<NAValue> { None } | |
274 | } | |
275 | ||
606c448e KS |
276 | impl<'a> SmackerVideoDemuxer<'a> { |
277 | fn new(io: &'a mut ByteReader<'a>) -> Self { | |
278 | SmackerVideoDemuxer { | |
279 | src: io, | |
280 | frames: 0, | |
281 | pts_inc: 0, | |
282 | cur_pts: 0, | |
283 | ainfo: [AudioTrack::new(); NUM_AUDIO_TRACKS], | |
284 | frame_sizes: Vec::new(), | |
285 | frame_flags: Vec::new(), | |
286 | video_id: 0, | |
287 | start: 0, | |
288 | cur_frame: 0, | |
289 | queued_packets: Vec::new(), | |
290 | pal: [0; PAL_SIZE], | |
291 | } | |
292 | } | |
293 | fn reset_state(&mut self) { | |
37952415 | 294 | self.queued_packets.clear(); |
606c448e KS |
295 | } |
296 | } | |
297 | ||
298 | pub struct SMKDemuxerCreator { } | |
299 | ||
300 | impl DemuxerCreator for SMKDemuxerCreator { | |
6011e201 | 301 | fn new_demuxer<'a>(&self, br: &'a mut ByteReader<'a>) -> Box<dyn DemuxCore<'a> + 'a> { |
606c448e KS |
302 | Box::new(SmackerVideoDemuxer::new(br)) |
303 | } | |
304 | fn get_name(&self) -> &'static str { "smacker" } | |
305 | } | |
306 | ||
307 | #[cfg(test)] | |
308 | mod test { | |
309 | use super::*; | |
310 | use std::fs::File; | |
311 | ||
312 | #[test] | |
313 | fn test_smk_demux() { | |
886cde48 | 314 | // sample: https://samples.mplayerhq.hu/game-formats/smacker/20130507_audio-distortion.smk |
606c448e KS |
315 | let mut file = File::open("assets/RAD/20130507_audio-distortion.smk").unwrap(); |
316 | // let mut file = File::open("assets/RAD/ajfstr1.smk").unwrap(); | |
317 | // let mut file = File::open("assets/RAD/credits.smk").unwrap(); | |
318 | // let mut file = File::open("assets/RAD/wetlogo.smk").unwrap(); | |
319 | let mut fr = FileReader::new_read(&mut file); | |
320 | let mut br = ByteReader::new(&mut fr); | |
321 | let mut dmx = SmackerVideoDemuxer::new(&mut br); | |
322 | let mut sm = StreamManager::new(); | |
caf0f37e KS |
323 | let mut si = SeekIndex::new(); |
324 | dmx.open(&mut sm, &mut si).unwrap(); | |
606c448e KS |
325 | loop { |
326 | let pktres = dmx.get_frame(&mut sm); | |
327 | if let Err(e) = pktres { | |
328 | if (e as i32) == (DemuxerError::EOF as i32) { break; } | |
329 | panic!("error"); | |
330 | } | |
331 | let pkt = pktres.unwrap(); | |
332 | println!("Got {}", pkt); | |
333 | } | |
334 | } | |
335 | } |