| 1 | use nihav_core::codecs::*; |
| 2 | use nihav_core::io::byteio::*; |
| 3 | |
| 4 | const FRAME_W: usize = 320; |
| 5 | const FRAME_H: usize = 160; |
| 6 | |
| 7 | struct IMAXDecoder { |
| 8 | info: NACodecInfoRef, |
| 9 | pal: [u8; 768], |
| 10 | frame: [u8; FRAME_W * FRAME_H], |
| 11 | hist: [u8; 32768], |
| 12 | hist_pos: usize, |
| 13 | } |
| 14 | |
| 15 | impl IMAXDecoder { |
| 16 | fn new() -> Self { |
| 17 | Self { |
| 18 | info: NACodecInfoRef::default(), |
| 19 | pal: [0; 768], |
| 20 | frame: [0; FRAME_W * FRAME_H], |
| 21 | hist: [0; 32768], |
| 22 | hist_pos: 0, |
| 23 | } |
| 24 | } |
| 25 | } |
| 26 | |
| 27 | impl NADecoder for IMAXDecoder { |
| 28 | fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> { |
| 29 | if let NACodecTypeInfo::Video(_vinfo) = info.get_properties() { |
| 30 | /*let fmt = NAPixelFormaton::new(ColorModel::RGB(RGBSubmodel::RGB), |
| 31 | Some(NAPixelChromaton::new(0, 0, true, 8, 0, 0, 3)), |
| 32 | Some(NAPixelChromaton::new(0, 0, true, 8, 0, 1, 3)), |
| 33 | Some(NAPixelChromaton::new(0, 0, true, 8, 0, 2, 3)), |
| 34 | None, None, |
| 35 | FORMATON_FLAG_PALETTE, 3);*/ |
| 36 | let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(FRAME_W, FRAME_H, false, PAL8_FORMAT)); |
| 37 | self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref(); |
| 38 | |
| 39 | Ok(()) |
| 40 | } else { |
| 41 | Err(DecoderError::InvalidData) |
| 42 | } |
| 43 | } |
| 44 | fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> { |
| 45 | let src = pkt.get_buffer(); |
| 46 | validate!(src.len() > 0); |
| 47 | |
| 48 | for sd in pkt.side_data.iter() { |
| 49 | if let NASideData::Palette(true, ref pal) = sd { |
| 50 | for (dst, src) in self.pal.chunks_mut(3).zip(pal.chunks(4)) { |
| 51 | dst[0] = src[0]; |
| 52 | dst[1] = src[1]; |
| 53 | dst[2] = src[2]; |
| 54 | } |
| 55 | break; |
| 56 | } |
| 57 | } |
| 58 | |
| 59 | let mut mr = MemoryReader::new_read(&src); |
| 60 | let mut br = ByteReader::new(&mut mr); |
| 61 | |
| 62 | let mut is_intra = true; |
| 63 | let mut is_skip = true; |
| 64 | let mut idx = 0; |
| 65 | while idx < self.frame.len() { |
| 66 | let v = br.read_byte()?; |
| 67 | let op = v >> 6; |
| 68 | let len = (v & 0x3F) as usize; |
| 69 | match op { |
| 70 | 0 => { |
| 71 | validate!(idx + len <= self.frame.len()); |
| 72 | idx += len; |
| 73 | is_intra = false; |
| 74 | }, |
| 75 | 1 => { |
| 76 | if len == 0 { |
| 77 | let off = br.read_u16le()? as usize; |
| 78 | let len = br.read_byte()? as usize; |
| 79 | validate!(idx + len <= self.frame.len()); |
| 80 | validate!(off + len <= self.hist.len()); |
| 81 | self.frame[idx..][..len].copy_from_slice(&self.hist[off..][..len]); |
| 82 | } else { |
| 83 | validate!(idx + len <= self.frame.len()); |
| 84 | br.read_buf(&mut self.frame[idx..][..len])?; |
| 85 | if self.hist_pos + len <= self.hist.len() { |
| 86 | self.hist[self.hist_pos..][..len].copy_from_slice(&self.frame[idx..][..len]); |
| 87 | self.hist_pos += len; |
| 88 | } |
| 89 | idx += len; |
| 90 | } |
| 91 | is_skip = false; |
| 92 | }, |
| 93 | 2 => { |
| 94 | let pix = br.read_byte()?; |
| 95 | validate!(idx + len <= self.frame.len()); |
| 96 | for _ in 0..len { |
| 97 | self.frame[idx] = pix; |
| 98 | idx += 1; |
| 99 | } |
| 100 | is_skip = false; |
| 101 | }, |
| 102 | _ => { |
| 103 | let len2 = br.read_byte()? as usize; |
| 104 | let skip_len = len * 64 + len2; |
| 105 | validate!(idx + skip_len <= self.frame.len()); |
| 106 | idx += skip_len; |
| 107 | is_intra = false; |
| 108 | }, |
| 109 | }; |
| 110 | } |
| 111 | |
| 112 | let bufinfo = if !is_skip { |
| 113 | let binfo = alloc_video_buffer(self.info.get_properties().get_video_info().unwrap(), 0)?; |
| 114 | let mut vbuf = binfo.get_vbuf().unwrap(); |
| 115 | let paloff = vbuf.get_offset(1); |
| 116 | let stride = vbuf.get_stride(0); |
| 117 | let data = vbuf.get_data_mut().unwrap(); |
| 118 | for (drow, srow) in data.chunks_mut(stride).zip(self.frame.chunks(FRAME_W)) { |
| 119 | drow[..FRAME_W].copy_from_slice(srow); |
| 120 | } |
| 121 | data[paloff..][..768].copy_from_slice(&self.pal); |
| 122 | binfo |
| 123 | } else { |
| 124 | NABufferType::None |
| 125 | }; |
| 126 | |
| 127 | let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), bufinfo); |
| 128 | frm.set_keyframe(is_intra); |
| 129 | let ftype = if is_skip { FrameType::Skip } else if is_intra { FrameType::I } else { FrameType::P }; |
| 130 | frm.set_frame_type(ftype); |
| 131 | Ok(frm.into_ref()) |
| 132 | } |
| 133 | fn flush(&mut self) { |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | impl NAOptionHandler for IMAXDecoder { |
| 138 | fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] } |
| 139 | fn set_options(&mut self, _options: &[NAOption]) { } |
| 140 | fn query_option_value(&self, _name: &str) -> Option<NAValue> { None } |
| 141 | } |
| 142 | |
| 143 | pub fn get_decoder() -> Box<dyn NADecoder + Send> { |
| 144 | Box::new(IMAXDecoder::new()) |
| 145 | } |
| 146 | |
| 147 | #[cfg(test)] |
| 148 | mod test { |
| 149 | use nihav_core::codecs::RegisteredDecoders; |
| 150 | use nihav_core::demuxers::RegisteredDemuxers; |
| 151 | use nihav_codec_support::test::dec_video::*; |
| 152 | use crate::game_register_all_decoders; |
| 153 | use crate::game_register_all_demuxers; |
| 154 | #[test] |
| 155 | fn test_imax_video() { |
| 156 | let mut dmx_reg = RegisteredDemuxers::new(); |
| 157 | game_register_all_demuxers(&mut dmx_reg); |
| 158 | let mut dec_reg = RegisteredDecoders::new(); |
| 159 | game_register_all_decoders(&mut dec_reg); |
| 160 | |
| 161 | // sample from Fable game |
| 162 | test_decoding("fable-imax", "fable-imax", "assets/Game/present.imx", |
| 163 | Some(64), &dmx_reg, &dec_reg, |
| 164 | ExpectedTestResult::MD5([0x775e1326, 0x7aa63674, 0x9b8aec54, 0x5caee2e3])); |
| 165 | } |
| 166 | } |