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