diff options
author | Kostya Shishkov <kostya.shishkov@gmail.com> | 2021-05-30 17:21:18 +0200 |
---|---|---|
committer | Kostya Shishkov <kostya.shishkov@gmail.com> | 2021-05-30 17:21:18 +0200 |
commit | afe1e5babec1591d397725fbb7d37285e5b7d70c (patch) | |
tree | 4b550666126be920c18a4c0553eae99b62167625 /nihav-game/src/demuxers/q.rs | |
parent | f17870054cf46115cd1eb95176b1767bd874d95c (diff) | |
download | nihav-afe1e5babec1591d397725fbb7d37285e5b7d70c.tar.gz |
Legend Entertainment Q format demuxer and decoder
Diffstat (limited to 'nihav-game/src/demuxers/q.rs')
-rw-r--r-- | nihav-game/src/demuxers/q.rs | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/nihav-game/src/demuxers/q.rs b/nihav-game/src/demuxers/q.rs new file mode 100644 index 0000000..3f38ef7 --- /dev/null +++ b/nihav-game/src/demuxers/q.rs @@ -0,0 +1,249 @@ +use nihav_core::frame::*; +use nihav_core::demuxers::*; + +#[allow(dead_code)] +struct QDemuxer<'a> { + src: &'a mut ByteReader<'a>, + vpts: u64, + apts: u64, + bps: usize, + a_id: Option<usize>, + v_id: Option<usize>, + nframes: usize, + duration: u64, + side_data: Vec<u8>, +} + +impl<'a> DemuxCore<'a> for QDemuxer<'a> { + #[allow(unused_variables)] + fn open(&mut self, strmgr: &mut StreamManager, _seek_index: &mut SeekIndex) -> DemuxerResult<()> { + let src = &mut self.src; + + let mut hdr = [0; 22]; + src.read_buf(&mut hdr)?; + validate!(hdr[0] == 0x39); + validate!(hdr[1] == 0x68); + let version = hdr[2]; + validate!(version >= 3 && version <= 5); + let mut width = read_u16le(&hdr[4..])? as usize; + let mut height = read_u16le(&hdr[6..])? as usize; + if version > 3 { + width *= hdr[8] as usize; + height *= hdr[9] as usize; + } + validate!(width > 0 && width <= 800); + validate!(height > 0 && height <= 600); + + self.nframes = read_u16le(&hdr[10..])? as usize; + validate!(self.nframes > 0); + let fps = if hdr[16] == 0 { 5 } else { hdr[16] as u32 }; + self.duration = (self.nframes as u64) * 1000 / u64::from(fps); + let asize = if version > 3 { + src.read_u32le()? + } else { 0 }; + + let vhdr = NAVideoInfo::new(width, height, false, PAL8_FORMAT); + let vci = NACodecTypeInfo::Video(vhdr); + let vinfo = NACodecInfo::new("legend-q-video", vci, Some(hdr.to_vec())); + self.v_id = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 1, fps, self.nframes as u64)); + if asize != 0 { + let ntype = self.src.peek_byte()?; + if ntype == 8 { + let _ = self.src.read_u16le()?; + let size = self.src.read_u32le()? as usize; + validate!(size >= 44); + let mut buf = vec![0; size]; + self.src.read_buf(&mut buf)?; + let arate = read_u32le(&buf[24..])?; + let channels = buf[22]; + let abits = buf[34] as usize; + validate!(abits == 8 || abits == 16); + self.bps = (channels as usize) * abits / 8; + let bsize = read_u16le(&buf[32..])? as usize; + let ahdr = NAAudioInfo::new(arate, channels as u8, if abits == 16 { SND_S16_FORMAT } else { SND_U8_FORMAT }, bsize); + let ainfo = NACodecInfo::new("pcm", NACodecTypeInfo::Audio(ahdr), None); + self.a_id = strmgr.add_stream(NAStream::new(StreamType::Audio, 1, ainfo, 1, arate, 2)); + } + } + self.apts = 0; + self.vpts = 0; + self.side_data.truncate(0); + Ok(()) + } + + fn get_frame(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<NAPacket> { + loop { + let ctype = self.src.read_u16le()?; + let size = self.src.read_u32le()? as usize; + match ctype { + 0xFFFF => return Err(DemuxerError::EOF), + 0 => { + if let Some(a_id) = self.a_id { + let str = strmgr.get_stream(a_id).unwrap(); + let (tb_num, tb_den) = str.get_timebase(); + let ts = NATimeInfo::new(Some(self.apts), None, None, tb_num, tb_den); + self.apts += (size / self.bps) as u64; + return self.src.read_packet(str, ts, true, size); + } else { + return Err(DemuxerError::InvalidData); + } + }, + 1 => { + validate!(size <= 768); + let cur_len = self.side_data.len(); + self.side_data.resize(cur_len + size + 6, 0); + self.side_data[cur_len] = ctype as u8; + write_u32le(&mut self.side_data[cur_len + 2..], size as u32)?; + self.src.read_buf(&mut self.side_data[cur_len + 6..])?; + }, + 2 | 3 | 4 | 11 => { + validate!(self.v_id.is_some()); + let str = strmgr.get_stream(self.v_id.unwrap_or(0)).unwrap(); + let (tb_num, tb_den) = str.get_timebase(); + let ts = NATimeInfo::new(Some(self.vpts), None, None, tb_num, tb_den); + self.vpts += 1; + + let cur_len = self.side_data.len(); + self.side_data.resize(cur_len + size + 6, 0); + self.side_data[cur_len] = ctype as u8; + self.side_data[cur_len] = ctype as u8; + write_u32le(&mut self.side_data[cur_len + 2..], size as u32)?; + if let Err(err) = self.src.read_buf(&mut self.side_data[cur_len + 6..]) { + self.side_data.truncate(cur_len); + return Err(err.into()); + } + let mut buf = Vec::new(); + std::mem::swap(&mut buf, &mut self.side_data); + return Ok(NAPacket::new(str, ts, self.vpts == 1, buf)); + }, + 5 => { + validate!(size <= 256); + let cur_len = self.side_data.len(); + self.side_data.resize(cur_len + size + 6, 0); + self.side_data[cur_len] = ctype as u8; + write_u32le(&mut self.side_data[cur_len + 2..], size as u32)?; + self.src.read_buf(&mut self.side_data[cur_len + 6..])?; + }, + 6 | 7 => { + self.side_data.push(ctype as u8); + self.side_data.push(0); + self.side_data.push(0); + self.side_data.push(0); + self.side_data.push(0); + self.side_data.push(0); + }, + 8 => return Err(DemuxerError::InvalidData), //should be handled before main loop + 9 => { // first part of interlaced frame + let cur_len = self.side_data.len(); + self.side_data.resize(cur_len + size + 6, 0); + self.side_data[cur_len] = ctype as u8; + write_u32le(&mut self.side_data[cur_len + 2..], size as u32)?; + self.src.read_buf(&mut self.side_data[cur_len + 6..])?; + }, + _ => { + self.src.read_skip(size)?; + }, + }; + } + } + + fn seek(&mut self, _time: NATimePoint, _seek_index: &SeekIndex) -> DemuxerResult<()> { + Err(DemuxerError::NotPossible) + } + fn get_duration(&self) -> u64 { self.duration } +} +impl<'a> NAOptionHandler for QDemuxer<'a> { + fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] } + fn set_options(&mut self, _options: &[NAOption]) { } + fn query_option_value(&self, _name: &str) -> Option<NAValue> { None } +} +impl<'a> QDemuxer<'a> { + fn new(io: &'a mut ByteReader<'a>) -> Self { + QDemuxer { + src: io, + vpts: 0, + apts: 0, + bps: 0, + a_id: None, + v_id: None, + nframes: 0, + duration: 0, + side_data: Vec::with_capacity(256 + 6), + } + } +} + +pub struct QDemuxerCreator { } + +impl DemuxerCreator for QDemuxerCreator { + fn new_demuxer<'a>(&self, br: &'a mut ByteReader<'a>) -> Box<dyn DemuxCore<'a> + 'a> { + Box::new(QDemuxer::new(br)) + } + fn get_name(&self) -> &'static str { "legend-q" } +} + +#[cfg(test)] +mod test { + use super::*; + use std::fs::File; + + #[test] + fn test_q_demux_v3() { + let mut file = File::open("assets/Game/dgate101.q").unwrap(); + let mut fr = FileReader::new_read(&mut file); + let mut br = ByteReader::new(&mut fr); + let mut dmx = QDemuxer::new(&mut br); + let mut sm = StreamManager::new(); + let mut si = SeekIndex::new(); + dmx.open(&mut sm, &mut si).unwrap(); + loop { + let pktres = dmx.get_frame(&mut sm); + if let Err(e) = pktres { + if (e as i32) == (DemuxerError::EOF as i32) { break; } + panic!("error"); + } + let pkt = pktres.unwrap(); + println!("Got {}", pkt); + } + } + + #[test] + fn test_q_demux_v4() { + let mut file = File::open("assets/Game/1425A5.Q").unwrap(); + let mut fr = FileReader::new_read(&mut file); + let mut br = ByteReader::new(&mut fr); + let mut dmx = QDemuxer::new(&mut br); + let mut sm = StreamManager::new(); + let mut si = SeekIndex::new(); + dmx.open(&mut sm, &mut si).unwrap(); + loop { + let pktres = dmx.get_frame(&mut sm); + if let Err(e) = pktres { + if (e as i32) == (DemuxerError::EOF as i32) { break; } + panic!("error"); + } + let pkt = pktres.unwrap(); + println!("Got {}", pkt); + } + } + + #[test] + fn test_q_demux_v5() { + let mut file = File::open("assets/Game/mc703.q").unwrap(); + let mut fr = FileReader::new_read(&mut file); + let mut br = ByteReader::new(&mut fr); + let mut dmx = QDemuxer::new(&mut br); + let mut sm = StreamManager::new(); + let mut si = SeekIndex::new(); + dmx.open(&mut sm, &mut si).unwrap(); + loop { + let pktres = dmx.get_frame(&mut sm); + if let Err(e) = pktres { + if (e as i32) == (DemuxerError::EOF as i32) { break; } + panic!("error"); + } + let pkt = pktres.unwrap(); + println!("Got {}", pkt); + } + } +} |