aac: clear M/S flags
[nihav.git] / nihav-acorn / src / demuxers / armovie.rs
1 use nihav_core::demuxers::*;
2
3 const VIDEO_CODECS: &[(i32, &str)] = &[
4 ( 1, "movinglines"),
5 ( 2, "arm_rawvideo"),
6 ( 3, "arm_rawvideo"),
7 ( 5, "arm_rawvideo"),
8 ( 7, "movingblocks"),
9 ( 17, "movingblockshq"),
10 ( 19, "supermovingblocks"),
11 (100, "escape100"),
12 (102, "escape102"),
13 (122, "escape122"),
14 (124, "escape124"),
15 (130, "escape130"),
16 (600, "msvideo1"),
17 (601, "msvideo1"),
18 (602, "cinepak"),
19 (800, "linepack"),
20 (802, "movie16_3"),
21 ];
22
23 trait ReadString {
24 fn read_string(&mut self) -> DemuxerResult<Vec<u8>>;
25 }
26
27 impl<'a> ReadString for ByteReader<'a> {
28 fn read_string(&mut self) -> DemuxerResult<Vec<u8>> {
29 let mut res = Vec::new();
30 loop {
31 let c = self.read_byte()?;
32 if c == b'\n' || c == 0 {
33 break;
34 }
35 res.push(c);
36 validate!(res.len() < (1 << 10)); // insanity check
37 }
38 Ok(res)
39 }
40 }
41
42 fn parse_int(src: &[u8]) -> DemuxerResult<i32> {
43 let mut val = 0;
44 let mut parsed = false;
45 let mut sign = false;
46 for &c in src.iter() {
47 match c {
48 b'-' if !parsed => { sign = true; },
49 b'-' => return Err(DemuxerError::InvalidData),
50 b'0'..=b'9' => {
51 val = val * 10 + ((c - b'0') as i32);
52 if val > (1 << 27) {
53 return Err(DemuxerError::InvalidData);
54 }
55 parsed = true;
56 },
57 b' ' | b'\t' if !parsed => {},
58 _ => break,
59 }
60 }
61 if parsed {
62 Ok(if !sign { val } else { -val })
63 } else {
64 Err(DemuxerError::InvalidData)
65 }
66 }
67
68 fn parse_uint(src: &[u8]) -> DemuxerResult<u32> {
69 let val = parse_int(src)?;
70 if val < 0 { return Err(DemuxerError::InvalidData); }
71 Ok(val as u32)
72 }
73
74 fn parse_float(src: &[u8]) -> DemuxerResult<f32> {
75 let mut val = 0.0f32;
76 let mut parsed = false;
77 let mut frac_part = 1.0;
78 for &c in src.iter() {
79 match c {
80 b'0'..=b'9' => {
81 if frac_part == 1.0 {
82 val = val * 10.0 + ((c - b'0') as f32);
83 if val > 1000.0 {
84 return Err(DemuxerError::InvalidData);
85 }
86 } else {
87 val += ((c - b'0') as f32) * frac_part;
88 frac_part *= 0.1;
89 }
90 parsed = true;
91 },
92 b'.' if frac_part != 1.0 => return Err(DemuxerError::InvalidData),
93 b'.' => {
94 frac_part = 0.1;
95 },
96 b' ' | b'\t' => {},
97 _ => break,
98 }
99 }
100 if parsed {
101 Ok(val)
102 } else {
103 Err(DemuxerError::InvalidData)
104 }
105 }
106
107 #[allow(clippy::while_let_on_iterator)]
108 fn split_sound_str(string: &[u8]) -> DemuxerResult<Vec<&[u8]>> {
109 let mut start = 0;
110 let mut ret = Vec::new();
111 let mut ref_trk_id = 2;
112
113 let mut iter = string.iter().enumerate();
114 while let Some((pos, &c)) = iter.next() {
115 if c == b'|' {
116 ret.push(&string[start..pos]);
117
118 validate!(pos + 2 < string.len());
119
120 let mut num_end = pos + 2;
121 while let Some((pos2, c)) = iter.next() {
122 if !c.is_ascii_digit() {
123 num_end = pos2 + 1;
124 break;
125 }
126 }
127 let trk_id = parse_uint(&string[pos + 1..num_end])?;
128 validate!(trk_id == ref_trk_id);
129 ref_trk_id += 1;
130 start = num_end;
131 }
132 }
133 if start < string.len() {
134 ret.push(&string[start..]);
135 }
136 Ok(ret)
137 }
138
139 struct ChunkInfo {
140 offset: u32,
141 vid_size: u32,
142 aud_sizes: Vec<u32>,
143 }
144
145 enum ReadState {
146 None,
147 Video,
148 Audio(usize),
149 }
150
151 struct ARMovieDemuxer<'a> {
152 src: &'a mut ByteReader<'a>,
153 chunk_offs: Vec<ChunkInfo>,
154 cur_chunk: usize,
155 state: ReadState,
156 video_id: Option<usize>,
157 audio_ids: Vec<usize>,
158 }
159
160 impl<'a> ARMovieDemuxer<'a> {
161 fn new(src: &'a mut ByteReader<'a>) -> Self {
162 Self {
163 src,
164 chunk_offs: Vec::new(),
165 cur_chunk: 0,
166 state: ReadState::None,
167 video_id: None,
168 audio_ids: Vec::new(),
169 }
170 }
171 fn parse_catalogue(&mut self, offset: u32, num_chunks: usize, even_csize: usize, odd_csize: usize, aud_tracks: usize) -> DemuxerResult<()> {
172 self.src.seek(SeekFrom::Start(u64::from(offset)))?;
173 self.chunk_offs.clear();
174 for i in 0..num_chunks {
175 let cur_chunk_size = if (i & 1) == 0 { even_csize } else { odd_csize };
176
177 let entry = self.src.read_string()?;
178 let comma_pos = entry.iter().position(|&c| c == b',');
179 let semicolon_pos = entry.iter().position(|&c| c == b';');
180 if let (Some(c_pos), Some(sc_pos)) = (comma_pos, semicolon_pos) {
181 validate!(c_pos > 0 && c_pos + 1 < sc_pos);
182 let offset = parse_uint(&entry[..c_pos])?;
183 let vid_size = parse_uint(&entry[c_pos + 1..sc_pos])?;
184 let astring = &entry[sc_pos + 1..];
185 let asizes = split_sound_str(astring)?;
186
187 let mut aud_sizes = Vec::with_capacity(aud_tracks);
188 if aud_tracks > 0 {
189 let aud_size = parse_uint(asizes[0])?;
190 aud_sizes.push(aud_size);
191 }
192 for &aud_entry in asizes.iter().skip(1) {
193 let aud_size = parse_uint(aud_entry)?;
194 aud_sizes.push(aud_size);
195 }
196
197 let tot_size: u32 = vid_size + aud_sizes.iter().sum::<u32>();
198 validate!((tot_size as usize) <= cur_chunk_size);
199 self.chunk_offs.push(ChunkInfo { offset, vid_size, aud_sizes });
200 } else {
201 return Err(DemuxerError::InvalidData);
202 }
203 }
204
205 Ok(())
206 }
207 }
208
209 impl<'a> RawDemuxCore<'a> for ARMovieDemuxer<'a> {
210 #[allow(clippy::neg_cmp_op_on_partial_ord)]
211 fn open(&mut self, strmgr: &mut StreamManager, _seek_index: &mut SeekIndex) -> DemuxerResult<()> {
212 let magic = self.src.read_string()?;
213 validate!(&magic == b"ARMovie");
214 let _name = self.src.read_string()?;
215 let _date_and_copyright = self.src.read_string()?;
216 let _author = self.src.read_string()?;
217
218 let video_id = self.src.read_string()?;
219 let video_codec = parse_int(&video_id)?;
220 let width = self.src.read_string()?;
221 let width = parse_int(&width)?;
222 let height = self.src.read_string()?;
223 let height = parse_int(&height)?;
224 validate!((video_codec <= 0) || (width > 0 && height > 0));
225 let width = width as usize;
226 let height = height as usize;
227 let vformat = self.src.read_string()?;
228 let fps = self.src.read_string()?;
229 let fps = parse_float(&fps)?;
230
231 let sound_id = self.src.read_string()?;
232 let sound_ids = split_sound_str(&sound_id)?;
233 let mut num_sound = sound_ids.len();
234 if num_sound == 1 {
235 let sound_codec = parse_int(sound_ids[0])?;
236 if sound_codec < 1 {
237 num_sound = 0;
238 }
239 }
240 let srate = self.src.read_string()?;
241 let srates = split_sound_str(&srate)?;
242 let chan = self.src.read_string()?;
243 let channels = split_sound_str(&chan)?;
244 let sndformat = self.src.read_string()?;
245 let sndformats = split_sound_str(&sndformat)?;
246
247 let frm_per_chunk = self.src.read_string()?;
248 let frm_per_chunk = parse_uint(&frm_per_chunk)? as usize;
249 validate!(frm_per_chunk > 0);
250 let num_chunks = self.src.read_string()?;
251 let num_chunks = parse_uint(&num_chunks)? as usize + 1;
252 let even_chunk_size = self.src.read_string()?;
253 let even_chunk_size = parse_uint(&even_chunk_size)? as usize;
254 let odd_chunk_size = self.src.read_string()?;
255 let odd_chunk_size = parse_uint(&odd_chunk_size)? as usize;
256 let cat_offset = self.src.read_string()?;
257 let cat_offset = parse_uint(&cat_offset)?;
258
259 let _sprite_offset = self.src.read_string()?;
260 let _sprite_size = self.src.read_string()?;
261 let _kf_offset_res = self.src.read_string(); // may be not present for older ARMovies
262
263 self.parse_catalogue(cat_offset, num_chunks, even_chunk_size, odd_chunk_size, num_sound)?;
264
265 let mut stream_id = 0;
266 if video_codec > 0 {
267 let codec_name = if let Some(idx) = VIDEO_CODECS.iter().position(|&(id, _)| id == video_codec) {
268 VIDEO_CODECS[idx].1
269 } else {
270 "unknown"
271 };
272 validate!(fps > 1.0e-4);
273 let mut tbase = fps;
274 let mut tb_num = 1;
275 while tbase.fract() > 1.0e-4 {
276 tb_num *= 10;
277 tbase *= 10.0;
278 }
279 let tb_den = tbase as u32;
280
281 let mut edata = vec![video_codec as u8, (video_codec >> 8) as u8];
282 edata.extend_from_slice(&vformat);
283
284 let fmt = match video_codec {
285 600 => PAL8_FORMAT,
286 601 => RGB565_FORMAT,
287 _ => YUV420_FORMAT,
288 };
289
290 let vci = NACodecTypeInfo::Video(NAVideoInfo::new(width, height, false, fmt));
291 let vinfo = NACodecInfo::new(codec_name, vci, Some(edata));
292 let ret = strmgr.add_stream(NAStream::new(StreamType::Video, stream_id, vinfo, tb_num, tb_den, (frm_per_chunk * num_chunks) as u64));
293 if ret.is_some() {
294 stream_id += 1;
295 self.video_id = ret;
296 } else {
297 return Err(DemuxerError::MemoryError);
298 }
299 }
300
301 if num_sound > 0 {
302 validate!(sound_ids.len() == srates.len());
303 validate!(sound_ids.len() == channels.len());
304 validate!(sound_ids.len() == sndformats.len());
305 for ((&id, &sratestr), (&chan, &fmt)) in sound_ids.iter().zip(srates.iter())
306 .zip(channels.iter().zip(sndformats.iter())) {
307 let codec_id = parse_uint(id)?;
308 let codec_name = if codec_id == 1 { "arm_rawaudio" } else { "unknown" };
309 let channels = parse_uint(chan)?;
310 validate!(channels > 0 && channels < 16);
311 let edata = fmt.to_owned();
312 let bits = parse_uint(fmt)?;
313 let mut srate = parse_uint(sratestr)?;
314 if srate > 0 && srate < 1000 { // probably in microseconds instead of Hertz
315 srate = 1000000 / srate;
316 }
317 //println!(" codec id {codec_id} srate {srate} chan {channels} bits {bits}");
318 let fmt = if bits == 8 { SND_U8_FORMAT } else { SND_S16_FORMAT };
319
320 let aci = NACodecTypeInfo::Audio(NAAudioInfo::new(srate, channels as u8, fmt, 0));
321 let ainfo = NACodecInfo::new(codec_name, aci, Some(edata));
322 let ret = strmgr.add_stream(NAStream::new(StreamType::Audio, stream_id, ainfo, 1, srate, 0));
323 if let Some(id) = ret {
324 self.audio_ids.push(id);
325 stream_id += 1;
326 } else {
327 return Err(DemuxerError::MemoryError);
328 }
329 }
330 }
331
332 Ok(())
333 }
334
335 fn get_data(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<NARawData> {
336 while self.cur_chunk < self.chunk_offs.len() {
337 let chunk = &self.chunk_offs[self.cur_chunk];
338 match self.state {
339 ReadState::None => {
340 self.src.seek(SeekFrom::Start(u64::from(chunk.offset)))?;
341 self.state = ReadState::Video;
342 }
343 ReadState::Video => {
344 self.state = ReadState::Audio(0);
345 if chunk.vid_size > 0 {
346 validate!(self.video_id.is_some());
347 if let Some(stream) = strmgr.get_stream(self.video_id.unwrap_or(0)) {
348 let mut buf = vec![0; chunk.vid_size as usize];
349 self.src.read_buf(&mut buf)?;
350 return Ok(NARawData::new(stream, buf));
351 } else {
352 return Err(DemuxerError::InvalidData);
353 }
354 }
355 },
356 ReadState::Audio(idx) => {
357 if idx < chunk.aud_sizes.len() {
358 self.state = ReadState::Audio(idx + 1);
359 if chunk.aud_sizes[idx] > 0 {
360 if let Some(stream) = strmgr.get_stream(self.audio_ids[idx]) {
361 let mut buf = vec![0; chunk.aud_sizes[idx] as usize];
362 self.src.read_buf(&mut buf)?;
363 return Ok(NARawData::new(stream, buf));
364 } else {
365 return Err(DemuxerError::InvalidData);
366 }
367 }
368 } else {
369 self.cur_chunk += 1;
370 self.state = ReadState::None;
371 }
372 },
373 }
374 }
375
376 Err(DemuxerError::EOF)
377 }
378
379 fn seek(&mut self, _time: NATimePoint, _seek_index: &SeekIndex) -> DemuxerResult<()> {
380 Err(DemuxerError::NotPossible)
381 }
382 fn get_duration(&self) -> u64 { 0 }
383 }
384
385 impl<'a> NAOptionHandler for ARMovieDemuxer<'a> {
386 fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
387 fn set_options(&mut self, _options: &[NAOption]) { }
388 fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
389 }
390
391 pub struct ARMovieDemuxerCreator { }
392
393 impl RawDemuxerCreator for ARMovieDemuxerCreator {
394 fn new_demuxer<'a>(&self, br: &'a mut ByteReader<'a>) -> Box<dyn RawDemuxCore<'a> + 'a> {
395 Box::new(ARMovieDemuxer::new(br))
396 }
397 fn get_name(&self) -> &'static str { "armovie" }
398 fn check_format(&self, br: &mut ByteReader) -> bool {
399 let mut hdr = [0; 8];
400 br.read_buf(&mut hdr).is_ok() && &hdr == b"ARMovie\n"
401 }
402 }
403
404 #[cfg(test)]
405 mod test {
406 use super::*;
407 use std::fs::File;
408
409 #[test]
410 fn test_armovie_demux() {
411 // a sample from Acorn Replay Demonstration Disc 2
412 let mut file = File::open("assets/Acorn/CHEMSET2").unwrap();
413 let mut fr = FileReader::new_read(&mut file);
414 let mut br = ByteReader::new(&mut fr);
415 let mut dmx = ARMovieDemuxer::new(&mut br);
416 let mut sm = StreamManager::new();
417 let mut si = SeekIndex::new();
418 dmx.open(&mut sm, &mut si).unwrap();
419
420 loop {
421 let pktres = dmx.get_data(&mut sm);
422 if let Err(e) = pktres {
423 if e == DemuxerError::EOF { break; }
424 panic!("error");
425 }
426 let pkt = pktres.unwrap();
427 println!("Got {}", pkt);
428 }
429 }
430 }