| 1 | use nihav_core::codecs::*; |
| 2 | use nihav_core::compr::deflate::Inflate; |
| 3 | |
| 4 | #[derive(Default)] |
| 5 | struct PicParams { |
| 6 | width: usize, |
| 7 | height: usize, |
| 8 | bw: usize, |
| 9 | bh: usize, |
| 10 | blk_w: usize, |
| 11 | blk_h: usize, |
| 12 | bpp: usize, |
| 13 | bits: u8, |
| 14 | } |
| 15 | |
| 16 | struct ZMBVDecoder { |
| 17 | info: NACodecInfoRef, |
| 18 | comp: u8, |
| 19 | zbuf: Vec<u8>, |
| 20 | frm1: Vec<u8>, |
| 21 | frm2: Vec<u8>, |
| 22 | pparms: PicParams, |
| 23 | pal: [u8; 768], |
| 24 | infl: Inflate, |
| 25 | } |
| 26 | |
| 27 | impl ZMBVDecoder { |
| 28 | fn new() -> Self { |
| 29 | Self { |
| 30 | info: NACodecInfo::new_dummy(), |
| 31 | comp: 0, |
| 32 | zbuf: Vec::new(), |
| 33 | frm1: Vec::new(), |
| 34 | frm2: Vec::new(), |
| 35 | pparms: PicParams::default(), |
| 36 | pal: [0; 768], |
| 37 | infl: Inflate::new(), |
| 38 | } |
| 39 | } |
| 40 | } |
| 41 | |
| 42 | fn decode_intra(frm: &mut [u8], pal: &mut [u8; 768], pparms: &PicParams, src: &[u8]) -> DecoderResult<()> { |
| 43 | let off = if pparms.bits == 8 { |
| 44 | validate!(src.len() > 768); |
| 45 | pal.copy_from_slice(&src[..768]); |
| 46 | pal.len() |
| 47 | } else { 0 }; |
| 48 | let src = &src[off..]; |
| 49 | let size = pparms.width * pparms.height * pparms.bpp; |
| 50 | validate!(src.len() >= size); |
| 51 | frm[..size].copy_from_slice(&src[..size]); |
| 52 | Ok(()) |
| 53 | } |
| 54 | |
| 55 | fn decode_inter(frm: &mut [u8], prev: &[u8], dpal: bool, pal: &mut [u8; 768], pparms: &PicParams, src: &[u8]) -> DecoderResult<()> { |
| 56 | let off = if pparms.bits == 8 && dpal { |
| 57 | validate!(src.len() > 768); |
| 58 | for (dst, &src) in pal.iter_mut().zip(src.iter()) { |
| 59 | *dst ^= src; |
| 60 | } |
| 61 | pal.len() |
| 62 | } else { 0 }; |
| 63 | let mv_len = (pparms.blk_w * pparms.blk_h * 2 + 3) & !3; |
| 64 | validate!(src.len() >= off + mv_len); |
| 65 | let mut mvs = src[off..][..mv_len].chunks_exact(2); |
| 66 | let mut src = &src[off + mv_len..]; |
| 67 | |
| 68 | let mut last_bw = pparms.width % pparms.bw; |
| 69 | if last_bw == 0 { |
| 70 | last_bw = pparms.bw; |
| 71 | } |
| 72 | let mut last_bh = pparms.height % pparms.bh; |
| 73 | if last_bh == 0 { |
| 74 | last_bh = pparms.bh; |
| 75 | } |
| 76 | |
| 77 | let stride = pparms.width * pparms.bpp; |
| 78 | let mut off = 0; |
| 79 | let mut cur_h = pparms.bh; |
| 80 | for y in (0..pparms.height).step_by(pparms.bh) { |
| 81 | if y + pparms.bh >= pparms.height { |
| 82 | cur_h = last_bh; |
| 83 | } |
| 84 | let mut cur_w = pparms.bw; |
| 85 | let mut block_w = cur_w * pparms.bpp; |
| 86 | for x in (0..pparms.width).step_by(pparms.bw) { |
| 87 | if x + pparms.bw >= pparms.width { |
| 88 | cur_w = last_bw; |
| 89 | block_w = cur_w * pparms.bpp; |
| 90 | } |
| 91 | |
| 92 | let mv = mvs.next().unwrap_or(&[0; 2]); |
| 93 | let has_delta = (mv[0] & 1) != 0; |
| 94 | let mv_x = (mv[0] as i8) >> 1; |
| 95 | let mv_y = (mv[1] as i8) >> 1; |
| 96 | |
| 97 | let xoff = (x as isize) + (mv_x as isize); |
| 98 | let yoff = (y as isize) + (mv_y as isize); |
| 99 | if xoff >= 0 && (xoff as usize) + cur_w <= pparms.width && yoff >= 0 && (yoff as usize) + cur_h <= pparms.height { |
| 100 | let src_off = (xoff as usize) * pparms.bpp + (yoff as usize) * stride; |
| 101 | |
| 102 | for (dline, sline) in frm[off..].chunks_mut(stride).zip(prev[src_off..].chunks(stride)).take(cur_h) { |
| 103 | dline[..block_w].copy_from_slice(&sline[..block_w]); |
| 104 | } |
| 105 | } else { |
| 106 | let mut doff = off; |
| 107 | let mut soff = xoff * (pparms.bpp as isize) + yoff * (stride as isize); |
| 108 | for j in 0..cur_h { |
| 109 | let cy = yoff + (j as isize); |
| 110 | if cy >= 0 && (cy as usize) < pparms.height { |
| 111 | for i in 0..cur_w { |
| 112 | let cx = xoff + (i as isize); |
| 113 | if cx >= 0 && (cx as usize) < pparms.width { |
| 114 | for k in 0..pparms.bpp { |
| 115 | frm[doff + i * pparms.bpp + k] = prev[(soff + ((i * pparms.bpp + k) as isize)) as usize] |
| 116 | } |
| 117 | } else { |
| 118 | for k in 0..pparms.bpp { |
| 119 | frm[doff + i * pparms.bpp + k] = 0; |
| 120 | } |
| 121 | } |
| 122 | } |
| 123 | } else { |
| 124 | for p in frm[doff..][..block_w].iter_mut() { |
| 125 | *p = 0; |
| 126 | } |
| 127 | } |
| 128 | doff += stride; |
| 129 | soff += stride as isize; |
| 130 | } |
| 131 | } |
| 132 | if has_delta { |
| 133 | validate!(src.len() >= block_w * cur_h); |
| 134 | for (dline, sline) in frm[off..].chunks_mut(stride).zip(src.chunks(block_w)).take(cur_h) { |
| 135 | for (dst, &src) in dline[..block_w].iter_mut().zip(sline.iter()) { |
| 136 | *dst ^= src; |
| 137 | } |
| 138 | } |
| 139 | src = &src[block_w * cur_h..]; |
| 140 | } |
| 141 | |
| 142 | off += pparms.bw * pparms.bpp; |
| 143 | } |
| 144 | off -= pparms.bw * pparms.blk_w * pparms.bpp; |
| 145 | off += pparms.bh * stride; |
| 146 | } |
| 147 | |
| 148 | Ok(()) |
| 149 | } |
| 150 | |
| 151 | const INTRA_FLAG: u8 = 0x01; |
| 152 | const DELTA_PAL: u8 = 0x02; |
| 153 | |
| 154 | const RGB555_FORMAT: NAPixelFormaton = NAPixelFormaton { model: ColorModel::RGB(RGBSubmodel::RGB), components: 3, |
| 155 | comp_info: [ |
| 156 | Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 10, comp_offs: 0, next_elem: 2 }), |
| 157 | Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 5, comp_offs: 0, next_elem: 2 }), |
| 158 | Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 0, comp_offs: 0, next_elem: 2 }), |
| 159 | None, None], |
| 160 | elem_size: 2, be: false, alpha: false, palette: false }; |
| 161 | const RGB24_0_FORMAT: NAPixelFormaton = NAPixelFormaton { model: ColorModel::RGB(RGBSubmodel::RGB), components: 3, |
| 162 | comp_info: [ |
| 163 | Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 2, next_elem: 4 }), |
| 164 | Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 1, next_elem: 4 }), |
| 165 | Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 0, next_elem: 4 }), |
| 166 | None, None], |
| 167 | elem_size: 4, be: false, alpha: false, palette: false }; |
| 168 | |
| 169 | impl NADecoder for ZMBVDecoder { |
| 170 | #[allow(clippy::or_fun_call)] |
| 171 | fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> { |
| 172 | if let NACodecTypeInfo::Video(vinfo) = info.get_properties() { |
| 173 | self.pparms.width = vinfo.get_width(); |
| 174 | self.pparms.height = vinfo.get_height(); |
| 175 | self.zbuf = vec![0; (self.pparms.width + 255) * (self.pparms.height + 64) * 4]; |
| 176 | self.frm1 = vec![0; self.pparms.width * self.pparms.height * 4]; |
| 177 | self.frm2 = vec![0; self.pparms.width * self.pparms.height * 4]; |
| 178 | Ok(()) |
| 179 | } else { |
| 180 | Err(DecoderError::InvalidData) |
| 181 | } |
| 182 | } |
| 183 | fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> { |
| 184 | let src = pkt.get_buffer(); |
| 185 | if src.is_empty() { return Err(DecoderError::ShortData); } |
| 186 | |
| 187 | let flags = src[0]; |
| 188 | let keyframe = (flags & INTRA_FLAG) != 0; |
| 189 | |
| 190 | if keyframe { |
| 191 | validate!(src.len() > 7); |
| 192 | let hi_ver = src[1]; |
| 193 | let lo_ver = src[2]; |
| 194 | let comp = src[3]; |
| 195 | let fmt = src[4]; |
| 196 | let bw = src[5]; |
| 197 | let bh = src[6]; |
| 198 | validate!(hi_ver == 0 && lo_ver == 1); |
| 199 | validate!(comp == 0 || comp == 1); |
| 200 | validate!(bw > 0 && bh > 0); |
| 201 | self.comp = comp; |
| 202 | self.pparms.bw = bw as usize; |
| 203 | self.pparms.bh = bh as usize; |
| 204 | let (bits, fmt) = match fmt { |
| 205 | 0 => return Err(DecoderError::NotImplemented), //0, |
| 206 | 1 => return Err(DecoderError::NotImplemented), //1, |
| 207 | 2 => return Err(DecoderError::NotImplemented), //2, |
| 208 | 3 => return Err(DecoderError::NotImplemented), //4, |
| 209 | 4 => (8, PAL8_FORMAT), |
| 210 | 5 => (15, RGB555_FORMAT), |
| 211 | 6 => (16, RGB565_FORMAT), |
| 212 | 7 => (24, RGB24_FORMAT), |
| 213 | 8 => (32, RGB24_0_FORMAT), |
| 214 | _ => return Err(DecoderError::NotImplemented), |
| 215 | }; |
| 216 | self.pparms.blk_w = (self.pparms.width + self.pparms.bw - 1) / self.pparms.bw; |
| 217 | self.pparms.blk_h = (self.pparms.height + self.pparms.bh - 1) / self.pparms.bh; |
| 218 | if self.pparms.bits != bits { |
| 219 | self.pparms.bits = bits; |
| 220 | self.pparms.bpp = ((bits + 7) / 8) as usize; |
| 221 | let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(self.pparms.width, self.pparms.height, false, fmt)); |
| 222 | self.info = NACodecInfo::new_ref(self.info.get_name(), myinfo, self.info.get_extradata()).into_ref(); |
| 223 | } |
| 224 | self.infl.reset(); |
| 225 | } else if self.pparms.bits == 0 { |
| 226 | return Err(DecoderError::MissingReference); |
| 227 | } |
| 228 | |
| 229 | let off = if keyframe { 7 } else { 1 }; |
| 230 | let src = match self.comp { |
| 231 | 0 => &src[off..], |
| 232 | 1 => { |
| 233 | let ret = self.infl.decompress_block(&src[off..], &mut self.zbuf); |
| 234 | if ret.is_err() { |
| 235 | return Err(DecoderError::InvalidData); |
| 236 | } |
| 237 | let len = ret.unwrap(); |
| 238 | &self.zbuf[..len] |
| 239 | }, |
| 240 | _ => unreachable!(), |
| 241 | }; |
| 242 | if keyframe { |
| 243 | decode_intra(&mut self.frm1, &mut self.pal, &self.pparms, src)?; |
| 244 | } else { |
| 245 | decode_inter(&mut self.frm1, &self.frm2, (flags & DELTA_PAL) != 0, &mut self.pal, &self.pparms, src)?; |
| 246 | } |
| 247 | |
| 248 | let vinfo = self.info.get_properties().get_video_info().unwrap(); |
| 249 | let mut bufinfo = alloc_video_buffer(vinfo, 0)?; |
| 250 | match (self.pparms.bits, &mut bufinfo) { |
| 251 | (8, NABufferType::Video(ref mut buf)) => { |
| 252 | let stride = buf.get_stride(0); |
| 253 | let offset = buf.get_offset(0); |
| 254 | let paloff = buf.get_offset(1); |
| 255 | let data = buf.get_data_mut().unwrap(); |
| 256 | data[paloff..][..768].copy_from_slice(&self.pal); |
| 257 | for (dline, sline) in data[offset..].chunks_mut(stride).zip(self.frm1.chunks_exact(self.pparms.width)).take(self.pparms.height) { |
| 258 | dline[..self.pparms.width].copy_from_slice(sline); |
| 259 | } |
| 260 | }, |
| 261 | (_, NABufferType::Video16(ref mut buf)) => { |
| 262 | let stride = buf.get_stride(0); |
| 263 | let offset = buf.get_offset(0); |
| 264 | let data = buf.get_data_mut().unwrap(); |
| 265 | for (dline, sline) in data[offset..].chunks_mut(stride).zip(self.frm1.chunks_exact(self.pparms.width * 2)).take(self.pparms.height) { |
| 266 | for (dst, src) in dline[..self.pparms.width].iter_mut().zip(sline.chunks_exact(2)) { |
| 267 | *dst = u16::from(src[0]) | (u16::from(src[1]) << 8); |
| 268 | } |
| 269 | } |
| 270 | }, |
| 271 | (24, NABufferType::VideoPacked(ref mut buf)) => { |
| 272 | let stride = buf.get_stride(0); |
| 273 | let offset = buf.get_offset(0); |
| 274 | let data = buf.get_data_mut().unwrap(); |
| 275 | for (dline, sline) in data[offset..].chunks_mut(stride).zip(self.frm1.chunks_exact(self.pparms.width * 3)).take(self.pparms.height) { |
| 276 | dline[..self.pparms.width * 3].copy_from_slice(sline); |
| 277 | } |
| 278 | }, |
| 279 | (32, NABufferType::VideoPacked(ref mut buf)) => { |
| 280 | let stride = buf.get_stride(0); |
| 281 | let offset = buf.get_offset(0); |
| 282 | let data = buf.get_data_mut().unwrap(); |
| 283 | for (dline, sline) in data[offset..].chunks_mut(stride).zip(self.frm1.chunks_exact(self.pparms.width * 4)).take(self.pparms.height) { |
| 284 | dline[..self.pparms.width * 4].copy_from_slice(sline); |
| 285 | } |
| 286 | }, |
| 287 | _ => return Err(DecoderError::Bug), |
| 288 | }; |
| 289 | |
| 290 | std::mem::swap(&mut self.frm1, &mut self.frm2); |
| 291 | |
| 292 | let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), bufinfo); |
| 293 | frm.set_keyframe(keyframe); |
| 294 | if keyframe { |
| 295 | frm.set_frame_type(FrameType::I); |
| 296 | } else { |
| 297 | frm.set_frame_type(FrameType::P); |
| 298 | } |
| 299 | Ok(frm.into_ref()) |
| 300 | } |
| 301 | fn flush(&mut self) { |
| 302 | self.pparms.bits = 0; |
| 303 | } |
| 304 | } |
| 305 | |
| 306 | impl NAOptionHandler for ZMBVDecoder { |
| 307 | fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] } |
| 308 | fn set_options(&mut self, _options: &[NAOption]) { } |
| 309 | fn query_option_value(&self, _name: &str) -> Option<NAValue> { None } |
| 310 | } |
| 311 | |
| 312 | pub fn get_decoder() -> Box<dyn NADecoder + Send> { |
| 313 | Box::new(ZMBVDecoder::new()) |
| 314 | } |
| 315 | |
| 316 | #[cfg(test)] |
| 317 | mod test { |
| 318 | use nihav_core::codecs::RegisteredDecoders; |
| 319 | use nihav_core::demuxers::RegisteredDemuxers; |
| 320 | use nihav_codec_support::test::dec_video::*; |
| 321 | use crate::generic_register_all_decoders; |
| 322 | use crate::generic_register_all_demuxers; |
| 323 | // samples are from https://samples.mplayerhq.hu/V-codecs/ZMBV/ |
| 324 | #[test] |
| 325 | fn test_zmbv_8() { |
| 326 | let mut dmx_reg = RegisteredDemuxers::new(); |
| 327 | generic_register_all_demuxers(&mut dmx_reg); |
| 328 | let mut dec_reg = RegisteredDecoders::new(); |
| 329 | generic_register_all_decoders(&mut dec_reg); |
| 330 | test_decoding("avi", "zmbv", "assets/Misc/td3_000.avi", Some(10), |
| 331 | &dmx_reg, &dec_reg, ExpectedTestResult::MD5([0x83c57ac3, 0xda325d18, 0x806bd3be, 0x4b108732])); |
| 332 | } |
| 333 | #[test] |
| 334 | fn test_zmbv_15() { |
| 335 | let mut dmx_reg = RegisteredDemuxers::new(); |
| 336 | generic_register_all_demuxers(&mut dmx_reg); |
| 337 | let mut dec_reg = RegisteredDecoders::new(); |
| 338 | generic_register_all_decoders(&mut dec_reg); |
| 339 | test_decoding("avi", "zmbv", "assets/Misc/zmbv_15bit.avi", Some(20), |
| 340 | &dmx_reg, &dec_reg, ExpectedTestResult::MD5([0x9c9d3544, 0x11b437b6, 0x97a47a98, 0xeafb8ec9])); |
| 341 | } |
| 342 | #[test] |
| 343 | fn test_zmbv_16() { |
| 344 | let mut dmx_reg = RegisteredDemuxers::new(); |
| 345 | generic_register_all_demuxers(&mut dmx_reg); |
| 346 | let mut dec_reg = RegisteredDecoders::new(); |
| 347 | generic_register_all_decoders(&mut dec_reg); |
| 348 | test_decoding("avi", "zmbv", "assets/Misc/zmbv_16bit.avi", Some(20), |
| 349 | &dmx_reg, &dec_reg, ExpectedTestResult::MD5([0x5c09564e, 0x000a07c7, 0xf0d8a0d4, 0xa4ef77e6])); |
| 350 | } |
| 351 | #[test] |
| 352 | fn test_zmbv_32() { |
| 353 | let mut dmx_reg = RegisteredDemuxers::new(); |
| 354 | generic_register_all_demuxers(&mut dmx_reg); |
| 355 | let mut dec_reg = RegisteredDecoders::new(); |
| 356 | generic_register_all_decoders(&mut dec_reg); |
| 357 | test_decoding("avi", "zmbv", "assets/Misc/zmbv_32bit.avi", Some(20), |
| 358 | &dmx_reg, &dec_reg, ExpectedTestResult::MD5([0x4ee7b80b, 0xdba2253c, 0x39721ddf, 0x46ed6d53])); |
| 359 | } |
| 360 | } |