| 1 | use nihav_core::io::byteio::{ByteReader,MemoryReader}; |
| 2 | use nihav_core::formats::YUV420_FORMAT; |
| 3 | use nihav_core::codecs::*; |
| 4 | use nihav_codec_support::codecs::HAMShuffler; |
| 5 | |
| 6 | struct CinepakDecoder { |
| 7 | info: NACodecInfoRef, |
| 8 | frmmgr: HAMShuffler, |
| 9 | cb_v1: [[u8; 6]; 256], |
| 10 | cb_v4: [[u8; 6]; 256], |
| 11 | } |
| 12 | |
| 13 | fn put_block(block: &[u8; 24], x: usize, y: usize, frm: &mut NASimpleVideoFrame<u8>) { |
| 14 | let mut yoff = frm.offset[0] + x + y * frm.stride[0]; |
| 15 | for i in 0..4 { |
| 16 | for j in 0..4 { |
| 17 | frm.data[yoff + j] = block[j + i * 4]; |
| 18 | } |
| 19 | yoff += frm.stride[0]; |
| 20 | } |
| 21 | let mut uoff = frm.offset[1] + x / 2 + y / 2 * frm.stride[1]; |
| 22 | for i in 0..2 { |
| 23 | for j in 0..2 { |
| 24 | frm.data[uoff + j] = block[j + i * 2 + 16]; |
| 25 | } |
| 26 | uoff += frm.stride[1]; |
| 27 | } |
| 28 | let mut voff = frm.offset[2] + x / 2 + y / 2 * frm.stride[2]; |
| 29 | for i in 0..2 { |
| 30 | for j in 0..2 { |
| 31 | frm.data[voff + j] = block[j + i * 2 + 20]; |
| 32 | } |
| 33 | voff += frm.stride[2]; |
| 34 | } |
| 35 | } |
| 36 | |
| 37 | impl CinepakDecoder { |
| 38 | fn new() -> Self { |
| 39 | CinepakDecoder { |
| 40 | info: NACodecInfo::new_dummy(), |
| 41 | frmmgr: HAMShuffler::new(), |
| 42 | cb_v1: [[0; 6]; 256], |
| 43 | cb_v4: [[0; 6]; 256], |
| 44 | } |
| 45 | } |
| 46 | fn read_cb(br: &mut ByteReader, size: usize, cb: &mut [[u8; 6]; 256], is_yuv: bool) -> DecoderResult<()> { |
| 47 | let cb_elem = if is_yuv { 6 } else { 4 }; |
| 48 | let cb_size = (size - 4) / cb_elem; |
| 49 | validate!(size - 4 == cb_size * cb_elem); |
| 50 | validate!(cb_size <= 256); |
| 51 | for i in 0..cb_size { |
| 52 | br.read_buf(&mut cb[i][..cb_elem])?; |
| 53 | if !is_yuv { |
| 54 | cb[i][4] = 0x80; |
| 55 | cb[i][5] = 0x80; |
| 56 | } else { |
| 57 | cb[i][4] ^= 0x80; |
| 58 | cb[i][5] ^= 0x80; |
| 59 | } |
| 60 | } |
| 61 | Ok(()) |
| 62 | } |
| 63 | fn read_cb_upd(br: &mut ByteReader, size: usize, cb: &mut [[u8; 6]; 256], is_yuv: bool) -> DecoderResult<()> { |
| 64 | let cb_elem = if is_yuv { 6 } else { 4 }; |
| 65 | let end = br.tell() + (size as u64) - 4; |
| 66 | for i in (0..256).step_by(32) { |
| 67 | if br.tell() >= end { |
| 68 | break; |
| 69 | } |
| 70 | let upd = br.read_u32be()?; |
| 71 | for j in 0..32 { |
| 72 | if ((upd >> (31 - j)) & 1) != 0 { |
| 73 | br.read_buf(&mut cb[i + j][..cb_elem])?; |
| 74 | if !is_yuv { |
| 75 | cb[i + j][4] = 0x80; |
| 76 | cb[i + j][5] = 0x80; |
| 77 | } else { |
| 78 | cb[i + j][4] ^= 0x80; |
| 79 | cb[i + j][5] ^= 0x80; |
| 80 | } |
| 81 | } |
| 82 | } |
| 83 | } |
| 84 | validate!(br.tell() == end); |
| 85 | Ok(()) |
| 86 | } |
| 87 | fn decode_strip(&mut self, src: &[u8], is_intra: bool, is_intra_strip: bool, xoff: usize, yoff: usize, xend: usize, yend: usize, frm: &mut NASimpleVideoFrame<u8>) -> DecoderResult<()> { |
| 88 | let mut mr = MemoryReader::new_read(src); |
| 89 | let mut br = ByteReader::new(&mut mr); |
| 90 | let mut idx_pos = 0; |
| 91 | let mut idx_size = 0; |
| 92 | let mut v1_only = false; |
| 93 | while br.left() > 0 { |
| 94 | let id = br.read_byte()?; |
| 95 | if (id & 0xF0) == 0x20 { |
| 96 | validate!(((id & 1) != 0) ^ is_intra_strip); |
| 97 | } |
| 98 | let size = br.read_u24be()? as usize; |
| 99 | validate!(size >= 4 && (size - 4 <= (br.left() as usize))); |
| 100 | match id { |
| 101 | 0x20 => Self::read_cb (&mut br, size, &mut self.cb_v4, true)?, |
| 102 | 0x21 => Self::read_cb_upd(&mut br, size, &mut self.cb_v4, true)?, |
| 103 | 0x22 => Self::read_cb (&mut br, size, &mut self.cb_v1, true)?, |
| 104 | 0x23 => Self::read_cb_upd(&mut br, size, &mut self.cb_v1, true)?, |
| 105 | 0x24 => Self::read_cb (&mut br, size, &mut self.cb_v4, false)?, |
| 106 | 0x25 => Self::read_cb_upd(&mut br, size, &mut self.cb_v4, false)?, |
| 107 | 0x26 => Self::read_cb (&mut br, size, &mut self.cb_v1, false)?, |
| 108 | 0x27 => Self::read_cb_upd(&mut br, size, &mut self.cb_v1, false)?, |
| 109 | 0x30 => { // intra indices |
| 110 | validate!(idx_pos == 0); |
| 111 | idx_pos = br.tell() as usize; |
| 112 | idx_size = size - 4; |
| 113 | br.read_skip(idx_size)?; |
| 114 | }, |
| 115 | 0x31 => { // inter indices |
| 116 | validate!(!is_intra); |
| 117 | validate!(idx_pos == 0); |
| 118 | idx_pos = br.tell() as usize; |
| 119 | idx_size = size - 4; |
| 120 | br.read_skip(idx_size)?; |
| 121 | }, |
| 122 | 0x32 => { // V1-only blocks |
| 123 | validate!(idx_pos == 0); |
| 124 | idx_pos = br.tell() as usize; |
| 125 | idx_size = size - 4; |
| 126 | v1_only = true; |
| 127 | br.read_skip(idx_size)?; |
| 128 | }, |
| 129 | _ => return Err(DecoderError::InvalidData), |
| 130 | }; |
| 131 | } |
| 132 | validate!(idx_pos != 0); |
| 133 | let mut mr = MemoryReader::new_read(&src[idx_pos..][..idx_size]); |
| 134 | let mut br = ByteReader::new(&mut mr); |
| 135 | |
| 136 | let mut x = xoff; |
| 137 | let mut y = yoff; |
| 138 | let mut block = [0u8; 24]; |
| 139 | while br.left() > 0 { |
| 140 | let flags = if !v1_only { br.read_u32be()? } else { 0xFFFFFFFF }; |
| 141 | let mut mask = 1 << 31; |
| 142 | while mask > 0 { |
| 143 | if !is_intra { |
| 144 | let skip = (flags & mask) == 0; |
| 145 | mask >>= 1; |
| 146 | if skip { |
| 147 | x += 4; |
| 148 | if x >= xend { |
| 149 | x = xoff; |
| 150 | y += 4; |
| 151 | if y == yend { |
| 152 | return Ok(()); |
| 153 | } |
| 154 | } |
| 155 | } |
| 156 | continue; |
| 157 | } |
| 158 | if (flags & mask) == 0 { |
| 159 | let idx = br.read_byte()? as usize; |
| 160 | let cb = &self.cb_v1[idx]; |
| 161 | block[ 0] = cb[0]; block[ 1] = cb[0]; block[ 2] = cb[1]; block[ 3] = cb[1]; |
| 162 | block[ 4] = cb[0]; block[ 5] = cb[0]; block[ 6] = cb[1]; block[ 7] = cb[1]; |
| 163 | block[ 8] = cb[2]; block[ 9] = cb[2]; block[10] = cb[3]; block[11] = cb[3]; |
| 164 | block[12] = cb[2]; block[13] = cb[2]; block[14] = cb[3]; block[15] = cb[3]; |
| 165 | block[16] = cb[4]; block[17] = cb[4]; |
| 166 | block[18] = cb[4]; block[19] = cb[4]; |
| 167 | block[20] = cb[5]; block[21] = cb[5]; |
| 168 | block[22] = cb[5]; block[23] = cb[5]; |
| 169 | } else { |
| 170 | let idx0 = br.read_byte()? as usize; |
| 171 | let cb0 = &self.cb_v4[idx0]; |
| 172 | let idx1 = br.read_byte()? as usize; |
| 173 | let cb1 = &self.cb_v4[idx1]; |
| 174 | let idx2 = br.read_byte()? as usize; |
| 175 | let cb2 = &self.cb_v4[idx2]; |
| 176 | let idx3 = br.read_byte()? as usize; |
| 177 | let cb3 = &self.cb_v4[idx3]; |
| 178 | block[ 0] = cb0[0]; block[ 1] = cb0[1]; block[ 2] = cb1[0]; block[ 3] = cb1[1]; |
| 179 | block[ 4] = cb0[2]; block[ 5] = cb0[3]; block[ 6] = cb1[2]; block[ 7] = cb1[3]; |
| 180 | block[ 8] = cb2[0]; block[ 9] = cb2[1]; block[10] = cb3[0]; block[11] = cb3[1]; |
| 181 | block[12] = cb2[2]; block[13] = cb2[3]; block[14] = cb3[2]; block[15] = cb3[3]; |
| 182 | block[16] = cb0[4]; block[17] = cb1[4]; |
| 183 | block[18] = cb2[4]; block[19] = cb3[4]; |
| 184 | block[20] = cb0[5]; block[21] = cb1[5]; |
| 185 | block[22] = cb2[5]; block[23] = cb3[5]; |
| 186 | } |
| 187 | mask >>= 1; |
| 188 | put_block(&block, x, y, frm); |
| 189 | x += 4; |
| 190 | if x >= xend { |
| 191 | x = xoff; |
| 192 | y += 4; |
| 193 | if y == yend { |
| 194 | return Ok(()); |
| 195 | } |
| 196 | } |
| 197 | } |
| 198 | } |
| 199 | Ok(()) |
| 200 | } |
| 201 | } |
| 202 | |
| 203 | impl NADecoder for CinepakDecoder { |
| 204 | fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> { |
| 205 | if let NACodecTypeInfo::Video(vinfo) = info.get_properties() { |
| 206 | let w = vinfo.get_width(); |
| 207 | let h = vinfo.get_height(); |
| 208 | let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(w, h, false, YUV420_FORMAT)); |
| 209 | self.info = NACodecInfo::new_ref(info.get_name(), myinfo, None).into_ref(); |
| 210 | self.frmmgr.clear(); |
| 211 | Ok(()) |
| 212 | } else { |
| 213 | Err(DecoderError::InvalidData) |
| 214 | } |
| 215 | } |
| 216 | fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> { |
| 217 | let src = pkt.get_buffer(); |
| 218 | if src.len() <= 10 { return Err(DecoderError::ShortData); } |
| 219 | |
| 220 | let mut mr = MemoryReader::new_read(src.as_slice()); |
| 221 | let mut br = ByteReader::new(&mut mr); |
| 222 | |
| 223 | let flags = br.read_byte()?; |
| 224 | let size = br.read_u24be()? as usize; |
| 225 | validate!(src.len() >= size); |
| 226 | let width = br.read_u16be()? as usize; |
| 227 | let height = br.read_u16be()? as usize; |
| 228 | let nstrips = br.read_u16be()? as usize; |
| 229 | |
| 230 | let is_intra = (flags & 1) == 0; |
| 231 | |
| 232 | if let Some(ref vinfo) = self.info.get_properties().get_video_info() { |
| 233 | if vinfo.width != width || vinfo.height != height { |
| 234 | let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(width, height, false, YUV420_FORMAT)); |
| 235 | self.info = NACodecInfo::new_ref(self.info.get_name(), myinfo, None).into_ref(); |
| 236 | self.frmmgr.clear(); |
| 237 | } |
| 238 | } |
| 239 | let mut buf; |
| 240 | if is_intra { |
| 241 | let vinfo = self.info.get_properties().get_video_info().unwrap(); |
| 242 | let bufinfo = alloc_video_buffer(vinfo, 2)?; |
| 243 | buf = bufinfo.get_vbuf().unwrap(); |
| 244 | } else { |
| 245 | let bufret = self.frmmgr.clone_ref(); |
| 246 | if let Some(vbuf) = bufret { |
| 247 | buf = vbuf; |
| 248 | } else { |
| 249 | return Err(DecoderError::MissingReference); |
| 250 | } |
| 251 | } |
| 252 | let mut frm = NASimpleVideoFrame::from_video_buf(&mut buf).unwrap(); |
| 253 | |
| 254 | let mut last_y = 0; |
| 255 | for i in 0..nstrips { |
| 256 | let flags = br.read_byte()?; |
| 257 | validate!(flags == 0x10 || flags == 0x11); |
| 258 | let is_intra_strip = (flags & 1) == 0; |
| 259 | let size = br.read_u24be()? as usize; |
| 260 | validate!(size > 12 && (size - 4) <= (br.left() as usize)); |
| 261 | let yoff = br.read_u16be()? as usize; |
| 262 | let xoff = br.read_u16be()? as usize; |
| 263 | if xoff != 0 || yoff != 0 { |
| 264 | return Err(DecoderError::NotImplemented); |
| 265 | } |
| 266 | let yend = br.read_u16be()? as usize; |
| 267 | let xend = br.read_u16be()? as usize; |
| 268 | if i == 0 && is_intra && !is_intra_strip { |
| 269 | return Err(DecoderError::InvalidData); |
| 270 | } |
| 271 | let start = br.tell() as usize; |
| 272 | let end = start + size - 12; |
| 273 | let strip_data = &src[start..end]; |
| 274 | self.decode_strip(strip_data, is_intra, is_intra_strip, 0, last_y, xend, last_y + yend, &mut frm)?; |
| 275 | br.read_skip(size - 12)?; |
| 276 | last_y += yend; |
| 277 | } |
| 278 | |
| 279 | self.frmmgr.add_frame(buf.clone()); |
| 280 | let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), NABufferType::Video(buf)); |
| 281 | frm.set_keyframe(is_intra); |
| 282 | frm.set_frame_type(if is_intra { FrameType::I } else { FrameType::P }); |
| 283 | Ok(frm.into_ref()) |
| 284 | } |
| 285 | fn flush(&mut self) { |
| 286 | self.frmmgr.clear(); |
| 287 | } |
| 288 | } |
| 289 | |
| 290 | pub fn get_decoder() -> Box<dyn NADecoder + Send> { |
| 291 | Box::new(CinepakDecoder::new()) |
| 292 | } |
| 293 | |
| 294 | #[cfg(test)] |
| 295 | mod test { |
| 296 | use nihav_core::codecs::RegisteredDecoders; |
| 297 | use nihav_core::demuxers::RegisteredDemuxers; |
| 298 | use nihav_codec_support::test::dec_video::*; |
| 299 | use crate::generic_register_all_codecs; |
| 300 | use crate::generic_register_all_demuxers; |
| 301 | #[test] |
| 302 | fn test_cinepak() { |
| 303 | let mut dmx_reg = RegisteredDemuxers::new(); |
| 304 | generic_register_all_demuxers(&mut dmx_reg); |
| 305 | let mut dec_reg = RegisteredDecoders::new(); |
| 306 | generic_register_all_codecs(&mut dec_reg); |
| 307 | test_decoding("avi", "cinepak", "assets/Misc/ot171.avi", Some(10), &dmx_reg, |
| 308 | &dec_reg, ExpectedTestResult::MD5Frames(vec![ |
| 309 | [0xd58326b0, 0xdbfc1dcc, 0x6d66a04c, 0x08a21bbb], |
| 310 | [0x9b2cb5c5, 0x69b5f261, 0xcaccaaaf, 0xff2a807d], |
| 311 | [0x55c322d5, 0xf76f81ce, 0x923ada8c, 0x4925a5c8], |
| 312 | [0x2d1a537a, 0x62233cb6, 0xc1d39c2f, 0xeec9ccf3], |
| 313 | [0xf3cc841d, 0x56603c01, 0x34f521cf, 0x61f8a0c9], |
| 314 | [0xd75c0802, 0x9e786186, 0xc7a05cdf, 0x52ddc59d], |
| 315 | [0xde19733b, 0x29633d17, 0x507e9f82, 0x94c09158], |
| 316 | [0x1ea11919, 0x133a282c, 0x8cee485c, 0x150cb3f4], |
| 317 | [0x55a6d8fb, 0x2ea287c0, 0x36b3083b, 0x954cfc64], |
| 318 | [0xfb8be1fb, 0x84ad10aa, 0xa00ee55c, 0x9e191e5b], |
| 319 | [0x9c090a08, 0x43071726, 0x26236b5a, 0x79595848]])); |
| 320 | } |
| 321 | #[test] |
| 322 | fn test_cinepak_gray() { |
| 323 | let mut dmx_reg = RegisteredDemuxers::new(); |
| 324 | generic_register_all_demuxers(&mut dmx_reg); |
| 325 | let mut dec_reg = RegisteredDecoders::new(); |
| 326 | generic_register_all_codecs(&mut dec_reg); |
| 327 | test_decoding("mov", "cinepak", "assets/Misc/dday.mov", Some(10), &dmx_reg, |
| 328 | &dec_reg, ExpectedTestResult::MD5Frames(vec![ |
| 329 | [0x75d4d701, 0x897b4a37, 0xdc2bfb95, 0x3c8871a5], |
| 330 | [0x75d4d701, 0x897b4a37, 0xdc2bfb95, 0x3c8871a5], |
| 331 | [0x75d4d701, 0x897b4a37, 0xdc2bfb95, 0x3c8871a5], |
| 332 | [0x75d4d701, 0x897b4a37, 0xdc2bfb95, 0x3c8871a5], |
| 333 | [0x75d4d701, 0x897b4a37, 0xdc2bfb95, 0x3c8871a5], |
| 334 | [0x75d4d701, 0x897b4a37, 0xdc2bfb95, 0x3c8871a5], |
| 335 | [0x75d4d701, 0x897b4a37, 0xdc2bfb95, 0x3c8871a5], |
| 336 | [0x75d4d701, 0x897b4a37, 0xdc2bfb95, 0x3c8871a5], |
| 337 | [0x75d4d701, 0x897b4a37, 0xdc2bfb95, 0x3c8871a5], |
| 338 | [0x75d4d701, 0x897b4a37, 0xdc2bfb95, 0x3c8871a5], |
| 339 | [0x4c67ee48, 0xbea36f9c, 0xde61338b, 0xec36cc90]])); |
| 340 | } |
| 341 | } |
| 342 | |