X-Git-Url: https://git.nihav.org/?a=blobdiff_plain;f=nihav-acorn%2Fsrc%2Fcodecs%2Fmovinglines.rs;fp=nihav-acorn%2Fsrc%2Fcodecs%2Fmovinglines.rs;h=ad159af608861e9e9d20a81007675cab0b1f1d6b;hb=e981a888dc75b454113445f643bd34a84652832c;hp=0000000000000000000000000000000000000000;hpb=1dd1e5060f391b4228bce5278951177ff32a026f;p=nihav.git diff --git a/nihav-acorn/src/codecs/movinglines.rs b/nihav-acorn/src/codecs/movinglines.rs new file mode 100644 index 0000000..ad159af --- /dev/null +++ b/nihav-acorn/src/codecs/movinglines.rs @@ -0,0 +1,295 @@ +use nihav_core::codecs::*; +use nihav_core::io::byteio::*; + +use super::RGB555_FORMAT; +use super::yuvtab::YUV2RGB; + +const END_CODE: u16 = 0x7300; + +#[derive(Default)] +struct MLDecoder { + info: NACodecInfoRef, + cur_frm: Vec, + prev_frm: Vec, + width: usize, + is_yuv: bool, +} + +impl MLDecoder { + fn new() -> Self { Self::default() } +} + +impl NADecoder for MLDecoder { + fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> { + if let NACodecTypeInfo::Video(vinfo) = info.get_properties() { + let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(vinfo.get_width(), vinfo.get_height(), false, RGB555_FORMAT)); + self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref(); + self.cur_frm = vec![0; vinfo.get_width() * vinfo.get_height()]; + self.prev_frm = vec![0; vinfo.get_width() * vinfo.get_height()]; + self.width = vinfo.get_width(); + if let Some(edata) = info.get_extradata() { + for triplet in edata.windows(3) { + if triplet == b"YUV" { + self.is_yuv = true; + break; + } + } + } + Ok(()) + } else { + Err(DecoderError::InvalidData) + } + } + fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult { + let src = pkt.get_buffer(); + validate!(src.len() > 2 && (src.len() & 1) == 0); + let mut mr = MemoryReader::new_read(&src); + let mut br = ByteReader::new(&mut mr); + + let mut is_intra = true; + let mut dpos = 0; + while dpos < self.cur_frm.len() { + let op = br.read_u16le()?; + let raw_flag = (op & 1) == 0; + let val = op >> 1; + if raw_flag { + self.cur_frm[dpos] = val; + dpos += 1; + } else { + match val { + 0..=0x47FF => { // copy prev + let len = ((val & 0x3F) + 2) as usize; + validate!(dpos + len <= self.cur_frm.len()); + + let mut idx = (val >> 6) as isize; + if idx >= 144 { // skip (0,0) + idx += 1; + } + let dy = idx / 17 - 8; + let dx = idx % 17 - 8; + let spos = dpos as isize + dx + dy * (self.width as isize); + validate!(spos >= 0); + let mut spos = spos as usize; + validate!(spos + len <= self.prev_frm.len()); + + for _ in 0..len { + self.cur_frm[dpos] = self.prev_frm[spos]; + dpos += 1; + spos += 1; + } + + is_intra = false; + }, + 0x4800..=0x72FF => { // copy cur + let len = ((val & 0x3F) + 2) as usize; + validate!(dpos + len <= self.cur_frm.len()); + + let idx = ((val >> 6) as usize) - 0x120; + let dx = idx % 19; + let dy = 9 - (idx / 19); + validate!(dpos + dx >= dy * self.width + 9); + let mut spos = dpos + dx - 9 - dy * self.width; + + for _ in 0..len { + self.cur_frm[dpos] = self.cur_frm[spos]; + dpos += 1; + spos += 1; + } + }, + END_CODE => break, // end of frame + 0x7301..=0x77FF => { // run + let len = ((val & 0x3F) + 2) as usize; + let pix = br.read_u16le()?; + validate!(dpos + len <= self.cur_frm.len()); + for _ in 0..len { + self.cur_frm[dpos] = pix; + dpos += 1; + } + }, + 0x7800..=0x7BFF => { // skip + let len = ((val & 0x3FF) + 1) as usize; + validate!(dpos + len <= self.cur_frm.len()); + for _ in 0..len { + self.cur_frm[dpos] = self.prev_frm[dpos]; + dpos += 1; + } + is_intra = false; + }, + 0x7C00.. => { // raw + let len = ((val & 0x3FF) + 1) as usize; + validate!(dpos + len <= self.cur_frm.len()); + let mut bitbuf = u32::from(br.read_u16le()?); + let mut bits = 16; + for _ in 0..len { + if bits < 15 { + bitbuf |= u32::from(br.read_u16le()?) << bits; + bits += 16; + } + self.cur_frm[dpos] = (bitbuf & 0x7FFF) as u16; + bitbuf >>= 15; + bits -= 15; + dpos += 1; + } + }, + } + } + } + validate!(br.left() == 2 && br.read_u16le()? == (END_CODE * 2 + 1)); + + let bufinfo = alloc_video_buffer(self.info.get_properties().get_video_info().unwrap(), 0)?; + let mut buf = bufinfo.get_vbuf16().unwrap(); + let stride = buf.get_stride(0); + let data = buf.get_data_mut().unwrap(); + + for (dline, sline) in data.chunks_exact_mut(stride) + .zip(self.cur_frm.chunks_exact(self.width)) { + dline[..self.width].copy_from_slice(sline); + } + if self.is_yuv { + for el in data.iter_mut() { + *el = YUV2RGB[(*el as usize) & 0x7FFF]; + } + } + + std::mem::swap(&mut self.cur_frm, &mut self.prev_frm); + + let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), bufinfo); + frm.set_keyframe(is_intra); + frm.set_frame_type(if is_intra { FrameType::I } else { FrameType::P }); + Ok(frm.into_ref()) + } + fn flush(&mut self) { + for el in self.cur_frm.iter_mut() { + *el = 0; + } + for el in self.prev_frm.iter_mut() { + *el = 0; + } + } +} + +impl NAOptionHandler for MLDecoder { + fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] } + fn set_options(&mut self, _options: &[NAOption]) { } + fn query_option_value(&self, _name: &str) -> Option { None } +} + +pub fn get_decoder() -> Box { + Box::new(MLDecoder::new()) +} + +#[derive(Default)] +struct MLPacketiser { + stream: Option, + buf: Vec, + end: usize, + frameno: u32, + intra: bool, +} + +impl MLPacketiser { + fn new() -> Self { Self::default() } +} + +impl NAPacketiser for MLPacketiser { + fn attach_stream(&mut self, stream: NAStreamRef) { + self.stream = Some(stream); + } + fn add_data(&mut self, src: &[u8]) -> bool { + self.buf.extend_from_slice(src); + self.buf.len() < (1 << 10) + } + fn parse_stream(&mut self, id: u32) -> DecoderResult { + if let Some(ref stream) = self.stream { + let mut stream = NAStream::clone(stream); + stream.id = id; + Ok(stream.into_ref()) + } else { + Err(DecoderError::MissingReference) + } + } + fn skip_junk(&mut self) -> DecoderResult { + Err(DecoderError::NotImplemented) + } + fn get_packet(&mut self, stream: NAStreamRef) -> DecoderResult> { + if self.buf.len() < self.end { + return Ok(None); + } + + if self.end == 0 { + self.intra = true; + } + + let mut found = false; + while self.end + 2 <= self.buf.len() { + let op = u16::from(self.buf[self.end + 1]) * 256 + u16::from(self.buf[self.end]); + self.end += 2; + + if op == (END_CODE * 2 + 1) { + found = true; + break; + } + // run + if ((op & 1) == 1) && (0xE603..=0xEFFF).contains(&op) { + self.end += 2; + } + // raw data + if ((op & 1) == 1) && (op > 0xF800) { + let raw_size = (((op >> 1) & 0x3FF) + 1) as usize; + self.end += ((raw_size * 15 + 15) & !15) >> 3; + } + // copy from previous frame + if ((op & 1) == 1) && ((op < 0x9000) || (0xF001..=0xF7FF).contains(&op)) { + self.intra = false; + } + } + + if found { + let mut data = Vec::with_capacity(self.end); + data.extend_from_slice(&self.buf[..self.end]); + self.buf.drain(..self.end); + let ts = NATimeInfo::new(Some(u64::from(self.frameno)), None, None, stream.tb_num, stream.tb_den); + self.end = 0; + self.frameno += 1; + + return Ok(Some(NAPacket::new(stream, ts, self.intra, data))); + } + + Ok(None) + } + fn reset(&mut self) { + self.buf.clear(); + self.end = 0; + } + fn bytes_left(&self) -> usize { self.buf.len() } +} + +pub fn get_packetiser() -> Box { + Box::new(MLPacketiser::new()) +} + +#[cfg(test)] +mod test { + use nihav_core::codecs::{RegisteredDecoders, RegisteredPacketisers}; + use nihav_core::demuxers::RegisteredRawDemuxers; + use nihav_codec_support::test::dec_video::*; + use crate::*; + #[test] + fn test_movinglines() { + let mut dmx_reg = RegisteredRawDemuxers::new(); + acorn_register_all_raw_demuxers(&mut dmx_reg); + let mut pkt_reg = RegisteredPacketisers::new(); + acorn_register_all_packetisers(&mut pkt_reg); + let mut dec_reg = RegisteredDecoders::new(); + acorn_register_all_decoders(&mut dec_reg); + + // a sample from Acorn Replay Demonstration Disc 2 + test_decoding_raw("armovie", "movinglines", "assets/Acorn/CHEMSET2", Some(3), + &dmx_reg, &pkt_reg, &dec_reg, + ExpectedTestResult::MD5Frames(vec![ + [0x2ba85570, 0x339ddc8f, 0x2e9ea4ba, 0xec6fa25c], + [0x17a5dd38, 0xab99b869, 0x63936887, 0x0cb05673], + [0x81d920cf, 0x57155044, 0xe13d1b8b, 0xb029e645], + [0x7dc1a826, 0xea3c8e29, 0x13398b07, 0xa9a647de]])); + } +}