From 7e52da9e2978c56d38d1bd308372e15324a008e2 Mon Sep 17 00:00:00 2001 From: Kostya Shishkov Date: Thu, 31 Dec 2020 17:11:47 +0100 Subject: [PATCH] clearvideo: support overlapping mode --- nihav-commonfmt/src/codecs/clearvideo.rs | 301 ++++++++++++++++++++++- 1 file changed, 297 insertions(+), 4 deletions(-) diff --git a/nihav-commonfmt/src/codecs/clearvideo.rs b/nihav-commonfmt/src/codecs/clearvideo.rs index 8b22dad..80521b8 100644 --- a/nihav-commonfmt/src/codecs/clearvideo.rs +++ b/nihav-commonfmt/src/codecs/clearvideo.rs @@ -307,6 +307,90 @@ fn copyadd_block(dst: &mut NAVideoBuffer, src: &NAVideoBuffer, } } +fn copy_block_w(dst: &mut WFrame, src: &NAVideoBuffer, + plane: usize, x: usize, y: usize, dx: isize, dy: isize, + size: usize, weights: &[[Vec; 9]]) +{ + let sx: isize = (x as isize) + dx; + let sy: isize = (y as isize) + dy; + + let (w, h) = src.get_dimensions(plane); + let sstride = src.get_stride(plane); + let mut soff = src.get_offset(plane) + (sx as usize) + (sy as usize) * sstride; + let sdta = src.get_data(); + let sbuf: &[u8] = sdta.as_slice(); + let dstride = dst.stride[plane]; + let mut doff = x + y * dstride; + let dbuf = &mut dst.data[plane]; + + let m1 = if x > 0 { if x + size < w { 0 } else { 2 } } else { 1 }; + let m0 = if y > 0 { if y + size < h { 0 } else { 2 } } else { 1 }; + let weight = &weights[size.trailing_zeros() as usize][m0 * 3 + m1]; + let blk_w = if m1 != 0 { size + 1 } else { size + 2 }; + let blk_h = if m0 != 0 { size + 1 } else { size + 2 }; + if m1 != 1 { + soff -= 1; + doff -= 1; + } + if m0 != 1 { + soff -= sstride; + doff -= dstride; + } + + let mut cur_w = weight.iter(); + for _y in 0..blk_h { + for x in 0..blk_w { + let scale = u32::from(*cur_w.next().unwrap()); + let src = u32::from(sbuf[soff + x]); + dbuf[doff + x] += scale | ((src * scale) << 8); + } + soff += sstride; + doff += dstride; + } +} + +fn copyadd_block_w(dst: &mut WFrame, src: &NAVideoBuffer, + plane: usize, x: usize, y: usize, dx: isize, dy: isize, + size: usize, bias: i16, weights: &[[Vec; 9]]) +{ + let sx: isize = (x as isize) + dx; + let sy: isize = (y as isize) + dy; + + let (w, h) = src.get_dimensions(plane); + let sstride = src.get_stride(plane); + let mut soff = src.get_offset(plane) + (sx as usize) + (sy as usize) * sstride; + let sdta = src.get_data(); + let sbuf: &[u8] = sdta.as_slice(); + let dstride = dst.stride[plane]; + let mut doff = x + y * dstride; + let dbuf = &mut dst.data[plane]; + + let m1 = if x > 0 { if x + size < w { 0 } else { 2 } } else { 1 }; + let m0 = if y > 0 { if y + size < h { 0 } else { 2 } } else { 1 }; + let weight = &weights[size.trailing_zeros() as usize][m0 * 3 + m1]; + let blk_w = if m1 != 0 { size + 1 } else { size + 2 }; + let blk_h = if m0 != 0 { size + 1 } else { size + 2 }; + if m1 != 1 { + soff -= 1; + doff -= 1; + } + if m0 != 1 { + soff -= sstride; + doff -= dstride; + } + + let mut cur_w = weight.iter(); + for _y in 0..blk_h { + for x in 0..blk_w { + let scale = u32::from(*cur_w.next().unwrap()); + let val = (i16::from(sbuf[soff + x]) + bias).max(0).min(255) as u32; + dbuf[doff + x] += scale | ((val * scale) << 8); + } + soff += sstride; + doff += dstride; + } +} + fn tile_do_block(dst: &mut NAVideoBuffer, src: &NAVideoBuffer, plane: usize, x: usize, y: usize, dx: i16, dy: i16, size: usize, bias: i16) { @@ -317,6 +401,16 @@ fn tile_do_block(dst: &mut NAVideoBuffer, src: &NAVideoBuffer, } } +fn tile_do_block_w(dst: &mut WFrame, src: &NAVideoBuffer, + plane: usize, x: usize, y: usize, dx: i16, dy: i16, size: usize, bias: i16, weights: &[[Vec; 9]]) +{ + if bias == 0 { + copy_block_w(dst, src, plane, x, y, dx as isize, dy as isize, size, weights); + } else { + copyadd_block_w(dst, src, plane, x, y, dx as isize, dy as isize, size, bias, weights); + } +} + fn restore_tree(dst: &mut NAVideoBuffer, src: &NAVideoBuffer, plane: usize, x: usize, y: usize, size: usize, tile: &TileInfo, root_mv: MV) { let mv = root_mv + tile.mv; @@ -338,6 +432,27 @@ fn restore_tree(dst: &mut NAVideoBuffer, src: &NAVideoBuffer, plane: usi } } +fn restore_tree_weighted(dst: &mut WFrame, src: &NAVideoBuffer, plane: usize, + x: usize, y: usize, size: usize, tile: &TileInfo, root_mv: MV, weights: &[[Vec; 9]]) { + let mv = root_mv + tile.mv; + + if tile.flags == 0 { + tile_do_block_w(dst, src, plane, x, y, mv.x, mv.y, size, tile.bias, weights); + } else { + let hsize = size >> 1; + for i in 0..4 { + let xoff = if (i & 2) == 0 { 0 } else { hsize }; + let yoff = if (i & 1) == 0 { 0 } else { hsize }; + + if let Some(ref subtile) = tile.child[i] { + restore_tree_weighted(dst, src, plane, x + xoff, y + yoff, hsize, subtile, root_mv, weights); + } else { + tile_do_block_w(dst, src, plane, x + xoff, y + yoff, mv.x, mv.y, hsize, tile.bias, weights); + } + } + } +} + fn extend_edges(buf: &mut NAVideoBuffer, tile_size: usize) { for comp in 0..3 { let (w, h) = buf.get_dimensions(comp); @@ -368,6 +483,55 @@ fn extend_edges(buf: &mut NAVideoBuffer, tile_size: usize) { } } +#[derive(Default)] +struct WFrame { + data: [Vec; 3], + stride: [usize; 3], +} + +impl WFrame { + fn new() -> Self { Self::default() } + fn resize(&mut self, w: usize, h: usize, align: u8) { + let mask = (1 << align) - 1; + let wa = (w + mask) & !mask; + let ha = (h + mask) & !mask; + self.data[0].resize(wa * ha, 0); + self.stride[0] = wa; + self.data[1].resize(wa / 2 * ha / 2, 0); + self.stride[1] = wa / 2; + self.data[2].resize(wa / 2 * ha / 2, 0); + self.stride[2] = wa / 2; + } + fn clear(&mut self) { + for plane in self.data.iter_mut() { + for el in plane.iter_mut() { + *el = 0; + } + } + } + fn output(&self, buf: &mut NAVideoBuffer) { + for plane in 0..3 { + let (w, h) = buf.get_dimensions(plane); + let dstride = buf.get_stride(plane); + let doff = buf.get_offset(plane); + let ddta = buf.get_data_mut().unwrap(); + for (dst, src) in ddta[doff..].chunks_mut(dstride).take(h).zip(self.data[plane].chunks(self.stride[plane])) { + for (dst, &src) in dst.iter_mut().take(w).zip(src.iter()) { + let scale = src & 0xFF; + *dst = if scale == 0 { + 0 + } else if scale == 16 { // common case + (src >> 12) as u8 + } else { + let val = src >> 8; + (val / scale) as u8 + }; + } + } + } + } +} + #[allow(dead_code)] struct ClearVideoDecoder { info: NACodecInfoRef, @@ -379,6 +543,10 @@ struct ClearVideoDecoder { ulev: [LevelCodes; 3], vlev: [LevelCodes; 3], tsize: u8, + + weighted: bool, + weights: Vec<[Vec; 9]>, + wframe: WFrame, } fn decode_dct_block(br: &mut BitReader, blk: &mut [i32; 64], ac_quant: i32, has_ac: bool, @@ -518,6 +686,10 @@ impl ClearVideoDecoder { Some((CLV_BIASV_2_BITS, CLV_BIASV_2_CODES, CLV_BIASV_2_SYMS)), CLV_BIAS_ESCAPE), ], tsize: 0, + + weighted: false, + weights: Vec::new(), + wframe: WFrame::new(), } } @@ -585,6 +757,10 @@ impl ClearVideoDecoder { let mut mvi = MVInfo::new(); mvi.reset(mb_w, mb_h, mb_size); + if self.weighted { + self.wframe.clear(); + } + for t_y in 0..mb_h { for t_x in 0..mb_w { if !br.read_bool()? { @@ -593,7 +769,11 @@ impl ClearVideoDecoder { let size = 1 << self.tsize; let tile = decode_tile_info(br, &self.ylev, 0)?; let mv = mvi.predict(t_x, t_y, tile.mv); - restore_tree(buf, prev, 0, x, y, size, &tile, mv); + if !self.weighted { + restore_tree(buf, prev, 0, x, y, size, &tile, mv); + } else { + restore_tree_weighted(&mut self.wframe, prev, 0, x, y, size, &tile, mv, &self.weights); + } let x = t_x << (self.tsize - 1); let y = t_y << (self.tsize - 1); let size = 1 << (self.tsize - 1); @@ -601,9 +781,17 @@ impl ClearVideoDecoder { cmv.x /= 2; cmv.y /= 2; let tile = decode_tile_info(br, &self.ulev, 0)?; - restore_tree(buf, prev, 1, x, y, size, &tile, cmv); + if !self.weighted { + restore_tree(buf, prev, 1, x, y, size, &tile, cmv); + } else { + restore_tree_weighted(&mut self.wframe, prev, 1, x, y, size, &tile, cmv, &self.weights); + } let tile = decode_tile_info(br, &self.vlev, 0)?; - restore_tree(buf, prev, 2, x, y, size, &tile, cmv); + if !self.weighted { + restore_tree(buf, prev, 2, x, y, size, &tile, cmv); + } else { + restore_tree_weighted(&mut self.wframe, prev, 2, x, y, size, &tile, cmv, &self.weights); + } } else { let mv = mvi.predict(t_x, t_y, ZERO_MV); for plane in 0..3 { @@ -612,16 +800,83 @@ impl ClearVideoDecoder { let size = if plane == 0 { 1 << self.tsize } else { 1 << (self.tsize - 1) }; let mx = if plane == 0 { mv.x as isize } else { (mv.x >> 1) as isize }; let my = if plane == 0 { mv.y as isize } else { (mv.y >> 1) as isize }; - copy_block(buf, prev, plane, x, y, mx, my, size); + if !self.weighted { + copy_block(buf, prev, plane, x, y, mx, my, size); + } else { + copy_block_w(&mut self.wframe, prev, plane, x, y, mx, my, size, &self.weights); + } } } } mvi.update_row(); } + if self.weighted { + self.wframe.output(buf); + } Ok(()) } } +fn generate_weights(weights: &mut Vec<[Vec; 9]>, depth: u8) { + const WEIGHT2X2: [u8; 9] = [ 1, 2, 1, 2, 4, 2, 1, 2, 1]; + + for d in 0..=depth as usize { + let size = 1 << d; + let hsize = size >> 1; + let dsize = (size + 2) * (size + 2); + let mut cur_w = [Vec::with_capacity(dsize), Vec::with_capacity(dsize), Vec::with_capacity(dsize), Vec::with_capacity(dsize), Vec::with_capacity(dsize), Vec::with_capacity(dsize), Vec::with_capacity(dsize), Vec::with_capacity(dsize), Vec::with_capacity(dsize)]; + for m0 in 0..3 { + let h = if m0 == 0 { size + 2 } else { size + 1 }; + for m1 in 0..3 { + let w = if m1 == 0 { size + 2 } else { size + 1 }; + let idx = m0 * 3 + m1; + if idx == 0 { + if size == 1 { + cur_w[0].extend_from_slice(&WEIGHT2X2); + } else { + cur_w[0].resize(w * h, 0); + let mut idx0 = 0; + let mut idx1 = (size + 2) * hsize; + let mut ref_iter = weights[d - 1][0].iter(); + for _y in 0..(hsize + 2) { + for _x in 0..(hsize + 2) { + let ref_val = ref_iter.next().unwrap(); + cur_w[0][idx0] += ref_val; + cur_w[0][idx0 + hsize] += ref_val; + cur_w[0][idx1] += ref_val; + cur_w[0][idx1 + hsize] += ref_val; + idx0 += 1; + idx1 += 1; + } + idx0 += hsize; + idx1 += hsize; + } + } + } else { + cur_w[idx].resize(w * h, 0); + for y0 in 0..(size + 2) { + let y = match m0 { + 0 => y0, + 1 => y0.saturating_sub(1), + _ => y0.min(h - 1), + }; + for x0 in 0..(size + 2) { + let x = match m1 { + 0 => x0, + 1 => x0.saturating_sub(1), + _ => x0.min(w - 1), + }; + let ref_val = cur_w[0][x0 + y0 * (size + 2)]; + cur_w[idx][x + y * w] += ref_val; + } + } + } + } + } + weights.push(cur_w); + } +} + impl NADecoder for ClearVideoDecoder { #[allow(clippy::or_fun_call)] fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> { @@ -650,6 +905,8 @@ impl NADecoder for ClearVideoDecoder { let tile_size = br.read_u32be()?; self.tsize = tile_size.trailing_zeros() as u8; } + generate_weights(&mut self.weights, self.tsize); + self.wframe.resize(w, h, self.tsize); Ok(()) } else { Err(DecoderError::InvalidData) @@ -673,6 +930,12 @@ impl NADecoder for ClearVideoDecoder { return Ok(frm.into_ref()); } + if (src[off] & 0xBF) == 5 { + // reset reference frame into grayscale + unimplemented!(); + } + self.weighted = (src[off] & 0x40) != 0; + let is_intra = (src[off] & 2) == 2; let mut br = BitReader::new(&src[(off + 1)..], BitReaderMode::BE); @@ -749,6 +1012,36 @@ mod test { [0xb5b43c22, 0xd9c457fa, 0xcc5390d8, 0x1201ef22], [0x27a206e9, 0x88085556, 0x1114fb62, 0x77f1ebed]])); } + #[test] + fn test_clv_ovl() { + let mut dmx_reg = RegisteredDemuxers::new(); + generic_register_all_demuxers(&mut dmx_reg); + let mut dec_reg = RegisteredDecoders::new(); + generic_register_all_decoders(&mut dec_reg); + test_decoding("avi", "clearvideo", "assets/Misc/Nick Pope.avi", Some(20), &dmx_reg, + &dec_reg, ExpectedTestResult::MD5Frames(vec![ + [0xca265763, 0xd7d40e35, 0x1c27d4fb, 0xbb76b9c6], + [0x42b4728b, 0x8299e532, 0xd9307741, 0xa94003ea], + [0x065f605d, 0x0fcd6be9, 0x7acf00f5, 0xa153298a], + [0x2c1dea59, 0xfca62495, 0x36e87c82, 0xd9e151b5], + [0xa57764f6, 0x0ad30c15, 0x15e65687, 0x1e2b2359], + [0x3d0498ae, 0x3b8a7743, 0x04639817, 0xd7b14313], + [0xf41f3417, 0x8c542118, 0x4c762295, 0xfccc8a28], + [0xdac4a37f, 0x0b42a976, 0xd9b3dc8e, 0xff21062f], + [0x9078fac7, 0x69308953, 0x511942ff, 0x89139f72], + [0xd24c8e85, 0x6c7eda39, 0x07369927, 0x47dc3800], + [0xd5dd7838, 0x572d70d1, 0xae8a6631, 0xce7291b2], + [0x7ce11730, 0x344443c7, 0xa7502185, 0x8940f0c4], + [0x95f93bc1, 0x745b0e69, 0xd69c8208, 0x8bb5bf67], + [0x3b9cf626, 0x33206f48, 0x2314f65b, 0x7833981b], + [0x305578ee, 0x8824d091, 0x17c9510e, 0x2156ef06], + [0xc1efdeaa, 0x79bd6381, 0x5816a81a, 0x902a47d9], + [0xb7724ddb, 0x88dfe176, 0xf1eebfff, 0x16e500db], + [0x86c5972d, 0x9a437142, 0x87189dd7, 0xa92f6bb8], + [0xa60d9732, 0x3a570201, 0x499a5b4c, 0xf234426c], + [0x4f62d7ef, 0x33b3c1cc, 0x6e0fa443, 0x66abe9d0], + [0x7362b65a, 0xc064737b, 0x0e2d199d, 0xead4ca56]])); + } } const CLV_DC_CODES: &[u8] = &[ -- 2.30.2