]> git.nihav.org Git - nihav.git/commitdiff
h263-based codecs: use proper motion compensation
authorKostya Shishkov <kostya.shishkov@gmail.com>
Fri, 3 Apr 2020 13:23:11 +0000 (15:23 +0200)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Fri, 3 Apr 2020 13:23:11 +0000 (15:23 +0200)
nihav-codec-support/src/codecs/h263/code.rs
nihav-codec-support/src/codecs/h263/data.rs
nihav-codec-support/src/codecs/h263/decoder.rs
nihav-codec-support/src/codecs/h263/mod.rs
nihav-realmedia/src/codecs/rv20.rs

index 9b9033a9c2dac5c476af87645a815b8ea3270c8e..c2100bb6ec0d33b8968faaacb05e31905381ace5 100644 (file)
@@ -1,7 +1,7 @@
-use nihav_core::frame::NAVideoBuffer;
+use nihav_core::frame::{NAVideoBuffer, NAVideoBufferRef, NASimpleVideoFrame};
 use super::{BlockDSP, CBPInfo, MV};
 use super::super::blockdsp;
-//use super::h263data::*;
+use super::data::H263_CHROMA_ROUND;
 
 /*const W1: i32 = 22725;
 const W2: i32 = 21407;
@@ -478,19 +478,63 @@ impl BlockDSP for H263BlockDSP {
     fn idct(&self, blk: &mut [i16; 64]) {
         h263_idct(blk)
     }
-    fn copy_blocks(&self, dst: &mut NAVideoBuffer<u8>, src: &NAVideoBuffer<u8>, xpos: usize, ypos: usize, w: usize, h: usize, mv: MV) {
-        let srcx = ((mv.x >> 1) as isize) + (xpos as isize);
-        let srcy = ((mv.y >> 1) as isize) + (ypos as isize);
+    fn copy_blocks(&self, dst: &mut NAVideoBuffer<u8>, src: NAVideoBufferRef<u8>, xpos: usize, ypos: usize, mv: MV) {
         let mode = ((mv.x & 1) + (mv.y & 1) * 2) as usize;
+        let cmode = (if (mv.x & 3) != 0 { 1 } else { 0 }) + (if (mv.y & 3) != 0 { 2 } else { 0 });
 
-        blockdsp::copy_blocks(dst, src, xpos, ypos, srcx, srcy, w, h, 0, 1, mode, H263_INTERP_FUNCS);
+        let mut dst = NASimpleVideoFrame::from_video_buf(dst).unwrap();
+
+        blockdsp::copy_block(&mut dst, src.clone(), 0, xpos, ypos, mv.x >> 1, mv.y >> 1, 16, 16, 0, 1, mode, H263_INTERP_FUNCS);
+        blockdsp::copy_block(&mut dst, src.clone(), 1, xpos >> 1, ypos >> 1, mv.x >> 2, mv.y >> 2, 8, 8, 0, 1, cmode, H263_INTERP_FUNCS);
+        blockdsp::copy_block(&mut dst, src.clone(), 2, xpos >> 1, ypos >> 1, mv.x >> 2, mv.y >> 2, 8, 8, 0, 1, cmode, H263_INTERP_FUNCS);
+    }
+    fn copy_blocks8x8(&self, dst: &mut NAVideoBuffer<u8>, src: NAVideoBufferRef<u8>, xpos: usize, ypos: usize, mvs: &[MV; 4]) {
+        let mut dst = NASimpleVideoFrame::from_video_buf(dst).unwrap();
+
+        for i in 0..4 {
+            let xadd = (i & 1) * 8;
+            let yadd = (i & 2) * 4;
+            let mode = ((mvs[i].x & 1) + (mvs[i].y & 1) * 2) as usize;
+
+            blockdsp::copy_block(&mut dst, src.clone(), 0, xpos + xadd, ypos + yadd, mvs[i].x >> 1, mvs[i].y >> 1, 8, 8, 0, 1, mode, H263_INTERP_FUNCS);
+        }
+
+        let sum_mv = mvs[0] + mvs[1] + mvs[2] + mvs[3];
+        let cmx = (sum_mv.x >> 3) + H263_CHROMA_ROUND[(sum_mv.x & 0xF) as usize];
+        let cmy = (sum_mv.y >> 3) + H263_CHROMA_ROUND[(sum_mv.y & 0xF) as usize];
+        let mode = ((cmx & 1) + (cmy & 1) * 2) as usize;
+        for plane in 1..3 {
+            blockdsp::copy_block(&mut dst, src.clone(), plane, xpos >> 1, ypos >> 1, cmx >> 1, cmy >> 1, 8, 8, 0, 1, mode, H263_INTERP_FUNCS);
+        }
     }
-    fn avg_blocks(&self, dst: &mut NAVideoBuffer<u8>, src: &NAVideoBuffer<u8>, xpos: usize, ypos: usize, w: usize, h: usize, mv: MV) {
-        let srcx = ((mv.x >> 1) as isize) + (xpos as isize);
-        let srcy = ((mv.y >> 1) as isize) + (ypos as isize);
+    fn avg_blocks(&self, dst: &mut NAVideoBuffer<u8>, src: NAVideoBufferRef<u8>, xpos: usize, ypos: usize, mv: MV) {
         let mode = ((mv.x & 1) + (mv.y & 1) * 2) as usize;
+        let cmode = (if (mv.x & 3) != 0 { 1 } else { 0 }) + (if (mv.y & 3) != 0 { 2 } else { 0 });
 
-        blockdsp::copy_blocks(dst, src, xpos, ypos, srcx, srcy, w, h, 0, 1, mode, H263_INTERP_AVG_FUNCS);
+        let mut dst = NASimpleVideoFrame::from_video_buf(dst).unwrap();
+
+        blockdsp::copy_block(&mut dst, src.clone(), 0, xpos, ypos, mv.x >> 1, mv.y >> 1, 16, 16, 0, 1, mode, H263_INTERP_AVG_FUNCS);
+        blockdsp::copy_block(&mut dst, src.clone(), 1, xpos >> 1, ypos >> 1, mv.x >> 2, mv.y >> 2, 8, 8, 0, 1, cmode, H263_INTERP_AVG_FUNCS);
+        blockdsp::copy_block(&mut dst, src.clone(), 2, xpos >> 1, ypos >> 1, mv.x >> 2, mv.y >> 2, 8, 8, 0, 1, cmode, H263_INTERP_AVG_FUNCS);
+    }
+    fn avg_blocks8x8(&self, dst: &mut NAVideoBuffer<u8>, src: NAVideoBufferRef<u8>, xpos: usize, ypos: usize, mvs: &[MV; 4]) {
+        let mut dst = NASimpleVideoFrame::from_video_buf(dst).unwrap();
+
+        for i in 0..4 {
+            let xadd = (i & 1) * 8;
+            let yadd = (i & 2) * 4;
+            let mode = ((mvs[i].x & 1) + (mvs[i].y & 1) * 2) as usize;
+
+            blockdsp::copy_block(&mut dst, src.clone(), 0, xpos + xadd, ypos + yadd, mvs[i].x >> 1, mvs[i].y >> 1, 8, 8, 0, 1, mode, H263_INTERP_AVG_FUNCS);
+        }
+
+        let sum_mv = mvs[0] + mvs[1] + mvs[2] + mvs[3];
+        let cmx = (sum_mv.x >> 3) + H263_CHROMA_ROUND[(sum_mv.x & 0xF) as usize];
+        let cmy = (sum_mv.y >> 3) + H263_CHROMA_ROUND[(sum_mv.y & 0xF) as usize];
+        let mode = ((cmx & 1) + (cmy & 1) * 2) as usize;
+        for plane in 1..3 {
+            blockdsp::copy_block(&mut dst, src.clone(), plane, xpos >> 1, ypos >> 1, cmx >> 1, cmy >> 1, 8, 8, 0, 1, mode, H263_INTERP_AVG_FUNCS);
+        }
     }
     fn filter_row(&self, buf: &mut NAVideoBuffer<u8>, mb_y: usize, mb_w: usize, cbpi: &CBPInfo) {
         h263_filter_row(buf, mb_y, mb_w, cbpi)
index 9a2d4a226edf0ea64313011c2f6075d305508708..40d5cc8c08bcea02228c7495558bba0098db4482 100644 (file)
@@ -63,6 +63,8 @@ pub const H263_MBTYPE_B: &[(u8, u8)] = &[
     (7,  6), (4,  6), (5,  6), (1,  6), (1,  7), (1,  8), (1, 10)
 ];
 
+pub const H263_CHROMA_ROUND: [i16; 16] = [ 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1 ];
+
 // 0x1 - direct, 0x2 - has quant, 0x4 - has CBP, 0x8 - has dquant, 0x10 - has fwd, 0x20 - has bwd, 0x40 - intra
 
 pub const H263_MBB_CAP_CODED:    u8 = 0x2;
index a78bf4f5a7a003354bfbf8f848f67503bd694862..7a888821576d1ade73f60bce064ebc1debeb1fab 100644 (file)
@@ -353,7 +353,7 @@ impl H263BaseDecoder {
                             self.mv_data.push(BlockMVInfo::Inter_1MV(mv));
                         }
                         if let Some(ref srcbuf) = self.ipbs.get_lastref() {
-                            bdsp.copy_blocks(&mut buf, srcbuf, mb_x * 16, mb_y * 16, 16, 16, mv);
+                            bdsp.copy_blocks(&mut buf, srcbuf.clone(), mb_x * 16, mb_y * 16, mv);
                         }
                         if pinfo.is_pb() {
                             mvi2.predict(mb_x, 0, false, binfo.get_mv(0), sstate.first_line, sstate.first_mb);
@@ -362,11 +362,9 @@ impl H263BaseDecoder {
                         let mut mv: [MV; 4] = [ZERO_MV, ZERO_MV, ZERO_MV, ZERO_MV];
                         for blk_no in 0..4 {
                             mv[blk_no] = mvi.predict(mb_x, blk_no, true, binfo.get_mv(blk_no), sstate.first_line, sstate.first_mb);
-                            if let Some(ref srcbuf) = self.ipbs.get_lastref() {
-                                bdsp.copy_blocks(&mut buf, srcbuf,
-                                                 mb_x * 16 + (blk_no & 1) * 8,
-                                                 mb_y * 16 + (blk_no & 2) * 4, 8, 8, mv[blk_no]);
-                            }
+                        }
+                        if let Some(ref srcbuf) = self.ipbs.get_lastref() {
+                            bdsp.copy_blocks8x8(&mut buf, srcbuf.clone(), mb_x * 16, mb_y * 16, &mv);
                         }
                         if pinfo.is_pb() {
                             for blk_no in 0..4 {
@@ -392,7 +390,7 @@ impl H263BaseDecoder {
                         mvi2.set_zero_mv(mb_x);
                     }
                     if let Some(ref srcbuf) = self.ipbs.get_lastref() {
-                        bdsp.copy_blocks(&mut buf, srcbuf, mb_x * 16, mb_y * 16, 16, 16, ZERO_MV);
+                        bdsp.copy_blocks(&mut buf, srcbuf.clone(), mb_x * 16, mb_y * 16, ZERO_MV);
                     }
                 } else {
                     let ref_mv_info = self.mv_data[mb_pos];
@@ -416,26 +414,28 @@ impl H263BaseDecoder {
                         }
                         if let (Some(ref fwd_buf), Some(ref bck_buf)) = (self.ipbs.get_nextref(), self.ipbs.get_lastref()) {
                             if has_fwd && has_bwd {
-                                bdsp.copy_blocks(&mut buf, fwd_buf, mb_x * 16, mb_y * 16, 16, 16, fwd_mv);
-                                bdsp.avg_blocks (&mut buf, bck_buf, mb_x * 16, mb_y * 16, 16, 16, bwd_mv);
+                                bdsp.copy_blocks(&mut buf, fwd_buf.clone(), mb_x * 16, mb_y * 16, fwd_mv);
+                                bdsp.avg_blocks (&mut buf, bck_buf.clone(), mb_x * 16, mb_y * 16, bwd_mv);
                             } else if has_fwd {
-                                bdsp.copy_blocks(&mut buf, fwd_buf, mb_x * 16, mb_y * 16, 16, 16, fwd_mv);
+                                bdsp.copy_blocks(&mut buf, fwd_buf.clone(), mb_x * 16, mb_y * 16, fwd_mv);
                             } else {
-                                bdsp.copy_blocks(&mut buf, bck_buf, mb_x * 16, mb_y * 16, 16, 16, bwd_mv);
+                                bdsp.copy_blocks(&mut buf, bck_buf.clone(), mb_x * 16, mb_y * 16, bwd_mv);
                             }
                         }
                     } else {
                         if let BlockMVInfo::Inter_4MV(mvs) = ref_mv_info {
+                            let mut mv_f = [ZERO_MV; 4];
+                            let mut mv_b = [ZERO_MV; 4];
                             for blk_no in 0..4 {
                                 let ref_mv = mvs[blk_no];
                                 let ref_mv_fwd = ref_mv.scale(bsdiff, tsdiff);
                                 let ref_mv_bwd = ref_mv - ref_mv_fwd;
-                                let xoff = mb_x * 16 + (blk_no & 1) * 8;
-                                let yoff = mb_y * 16 + (blk_no & 2) * 4;
-                                if let (Some(ref fwd_buf), Some(ref bck_buf)) = (self.ipbs.get_nextref(), self.ipbs.get_lastref()) {
-                                    bdsp.copy_blocks(&mut buf, fwd_buf, xoff, yoff, 8, 8, ref_mv_fwd);
-                                    bdsp.avg_blocks (&mut buf, bck_buf, xoff, yoff, 8, 8, ref_mv_bwd);
-                                }
+                                mv_f[blk_no] = ref_mv_fwd;
+                                mv_b[blk_no] = ref_mv_bwd;
+                            }
+                            if let (Some(ref fwd_buf), Some(ref bck_buf)) = (self.ipbs.get_nextref(), self.ipbs.get_lastref()) {
+                                bdsp.copy_blocks8x8(&mut buf, fwd_buf.clone(), mb_x * 16, mb_y * 16, &mv_f);
+                                bdsp.avg_blocks8x8 (&mut buf, bck_buf.clone(), mb_x * 16, mb_y * 16, &mv_b);
                             }
                         } else {
                             let ref_mv = if let BlockMVInfo::Inter_1MV(mv_) = ref_mv_info { mv_ } else { ZERO_MV };
@@ -443,8 +443,8 @@ impl H263BaseDecoder {
                             let ref_mv_bwd = MV::b_sub(ref_mv, ref_mv_fwd, ZERO_MV, bsdiff, tsdiff);
 
                             if let (Some(ref fwd_buf), Some(ref bck_buf)) = (self.ipbs.get_nextref(), self.ipbs.get_lastref()) {
-                                bdsp.copy_blocks(&mut buf, fwd_buf, mb_x * 16, mb_y * 16, 16, 16, ref_mv_fwd);
-                                bdsp.avg_blocks (&mut buf, bck_buf, mb_x * 16, mb_y * 16, 16, 16, ref_mv_bwd);
+                                bdsp.copy_blocks(&mut buf, fwd_buf.clone(), mb_x * 16, mb_y * 16, ref_mv_fwd);
+                                bdsp.avg_blocks (&mut buf, bck_buf.clone(), mb_x * 16, mb_y * 16, ref_mv_bwd);
                             }
                         }
                         mvi.set_zero_mv(mb_x);
@@ -534,10 +534,10 @@ impl H263BaseDecoder {
         let fmt = formats::YUV420_FORMAT;
         let vinfo = NAVideoInfo::new(self.w, self.h, false, fmt);
         let bufinfo = alloc_video_buffer(vinfo, 4)?;
-        let mut b_buf = bufinfo.get_vbuf().unwrap();
+        let b_buf = bufinfo.get_vbuf().unwrap();
 
         if let (Some(ref bck_buf), Some(ref fwd_buf)) = (self.ipbs.get_nextref(), self.ipbs.get_lastref()) {
-            recon_b_frame(&mut b_buf, fwd_buf, bck_buf, self.mb_w, self.mb_h, self.b_data.as_slice(), bdsp);
+            recon_b_frame(b_buf, fwd_buf.clone(), bck_buf.clone(), self.mb_w, self.mb_h, self.b_data.as_slice(), bdsp);
         }
 
         self.b_data.truncate(0);
@@ -545,7 +545,7 @@ impl H263BaseDecoder {
     }
 }
 
-fn recon_b_frame(b_buf: &mut NAVideoBuffer<u8>, bck_buf: &NAVideoBuffer<u8>, fwd_buf: &NAVideoBuffer<u8>,
+fn recon_b_frame(mut b_buf: NAVideoBufferRef<u8>, bck_buf: NAVideoBufferRef<u8>, fwd_buf: NAVideoBufferRef<u8>,
                  mb_w: usize, mb_h: usize, b_data: &[BMB], bdsp: &BlockDSP) {
     let mut cbpi = CBPInfo::new();
     let mut cur_mb = 0;
@@ -557,22 +557,18 @@ fn recon_b_frame(b_buf: &mut NAVideoBuffer<u8>, bck_buf: &NAVideoBuffer<u8>, fwd
             let cbp    = b_data[cur_mb].cbp;
             cbpi.set_cbp(mb_x, cbp);
             if num_mv == 1 {
-                bdsp.copy_blocks(b_buf, fwd_buf, mb_x * 16, mb_y * 16, 16, 16, b_data[cur_mb].mv_b[0]);
+                bdsp.copy_blocks(&mut b_buf, fwd_buf.clone(), mb_x * 16, mb_y * 16, b_data[cur_mb].mv_b[0]);
                 if !is_fwd {
-                    bdsp.avg_blocks(b_buf, bck_buf, mb_x * 16, mb_y * 16, 16, 16, b_data[cur_mb].mv_f[0]);
+                    bdsp.avg_blocks(&mut b_buf, bck_buf.clone(), mb_x * 16, mb_y * 16, b_data[cur_mb].mv_f[0]);
                 }
             } else {
-                for blk_no in 0..4 {
-                    let xpos = mb_x * 16 + (blk_no & 1) * 8;
-                    let ypos = mb_y * 16 + (blk_no & 2) * 4;
-                    bdsp.copy_blocks(b_buf, fwd_buf, xpos, ypos, 8, 8, b_data[cur_mb].mv_b[blk_no]);
-                    if !is_fwd {
-                        bdsp.avg_blocks(b_buf, bck_buf, xpos, ypos, 8, 8, b_data[cur_mb].mv_f[blk_no]);
-                    }
+                bdsp.copy_blocks8x8(&mut b_buf, fwd_buf.clone(), mb_x * 16, mb_y * 16, &b_data[cur_mb].mv_b);
+                if !is_fwd {
+                    bdsp.avg_blocks8x8(&mut b_buf, bck_buf.clone(), mb_x * 16, mb_y * 16, &b_data[cur_mb].mv_f);
                 }
             }
             if cbp != 0 {
-                blockdsp::add_blocks(b_buf, mb_x, mb_y, &b_data[cur_mb].blk);
+                blockdsp::add_blocks(&mut b_buf, mb_x, mb_y, &b_data[cur_mb].blk);
             }
             cur_mb += 1;
         }
index 33d71baa580a97d56ee9349ac6ed9d4ce7b5f8e2..8c3ca655088008480e30e64f32c7d63d66123770 100644 (file)
@@ -1,6 +1,6 @@
 use nihav_core::codecs::DecoderResult;
 use super::{MV, ZERO_MV};
-use nihav_core::frame::NAVideoBuffer;
+use nihav_core::frame::{NAVideoBuffer, NAVideoBufferRef};
 
 #[allow(clippy::many_single_char_names)]
 pub mod code;
@@ -19,8 +19,10 @@ pub trait BlockDecoder {
 
 pub trait BlockDSP {
     fn idct(&self, blk: &mut [i16; 64]);
-    fn copy_blocks(&self, dst: &mut NAVideoBuffer<u8>, src: &NAVideoBuffer<u8>, xpos: usize, ypos: usize, w: usize, h: usize, mv: MV);
-    fn avg_blocks(&self, dst: &mut NAVideoBuffer<u8>, src: &NAVideoBuffer<u8>, xpos: usize, ypos: usize, w: usize, h: usize, mv: MV);
+    fn copy_blocks(&self, dst: &mut NAVideoBuffer<u8>, src: NAVideoBufferRef<u8>, xpos: usize, ypos: usize, mv: MV);
+    fn copy_blocks8x8(&self, dst: &mut NAVideoBuffer<u8>, src: NAVideoBufferRef<u8>, xpos: usize, ypos: usize, mvs: &[MV; 4]);
+    fn avg_blocks(&self, dst: &mut NAVideoBuffer<u8>, src: NAVideoBufferRef<u8>, xpos: usize, ypos: usize, mv: MV);
+    fn avg_blocks8x8(&self, dst: &mut NAVideoBuffer<u8>, src: NAVideoBufferRef<u8>, xpos: usize, ypos: usize, mvs: &[MV; 4]);
     fn filter_row(&self, buf: &mut NAVideoBuffer<u8>, mb_y: usize, mb_w: usize, cbpi: &CBPInfo);
 }
 
index 7f24650298c68ad701393a3d5b352f47c8338f08..0d24b28cc3dcaa1e588837c951d843f1a9ae94ea 100644 (file)
@@ -131,19 +131,63 @@ impl BlockDSP for RV20BlockDSP {
             idct!(&tmp[i..], 8, &mut blk[i..], 8, 1 << 19, 20, i16);
         }
     }
-    fn copy_blocks(&self, dst: &mut NAVideoBuffer<u8>, src: &NAVideoBuffer<u8>, xpos: usize, ypos: usize, w: usize, h: usize, mv: MV) {
-        let srcx = ((mv.x >> 1) as isize) + (xpos as isize);
-        let srcy = ((mv.y >> 1) as isize) + (ypos as isize);
+    fn copy_blocks(&self, dst: &mut NAVideoBuffer<u8>, src: NAVideoBufferRef<u8>, xpos: usize, ypos: usize, mv: MV) {
         let mode = ((mv.x & 1) + (mv.y & 1) * 2) as usize;
+        let cmode = (if (mv.x & 3) != 0 { 1 } else { 0 }) + (if (mv.y & 3) != 0 { 2 } else { 0 });
 
-        blockdsp::copy_blocks(dst, src, xpos, ypos, srcx, srcy, w, h, 0, 1, mode, H263_INTERP_FUNCS);
+        let mut dst = NASimpleVideoFrame::from_video_buf(dst).unwrap();
+
+        blockdsp::copy_block(&mut dst, src.clone(), 0, xpos, ypos, mv.x >> 1, mv.y >> 1, 16, 16, 0, 1, mode, H263_INTERP_FUNCS);
+        blockdsp::copy_block(&mut dst, src.clone(), 1, xpos >> 1, ypos >> 1, mv.x >> 2, mv.y >> 2, 8, 8, 0, 1, cmode, H263_INTERP_FUNCS);
+        blockdsp::copy_block(&mut dst, src.clone(), 2, xpos >> 1, ypos >> 1, mv.x >> 2, mv.y >> 2, 8, 8, 0, 1, cmode, H263_INTERP_FUNCS);
+    }
+    fn copy_blocks8x8(&self, dst: &mut NAVideoBuffer<u8>, src: NAVideoBufferRef<u8>, xpos: usize, ypos: usize, mvs: &[MV; 4]) {
+        let mut dst = NASimpleVideoFrame::from_video_buf(dst).unwrap();
+
+        for i in 0..4 {
+            let xadd = (i & 1) * 8;
+            let yadd = (i & 2) * 4;
+            let mode = ((mvs[i].x & 1) + (mvs[i].y & 1) * 2) as usize;
+
+            blockdsp::copy_block(&mut dst, src.clone(), 0, xpos + xadd, ypos + yadd, mvs[i].x >> 1, mvs[i].y >> 1, 8, 8, 0, 1, mode, H263_INTERP_FUNCS);
+        }
+
+        let sum_mv = mvs[0] + mvs[1] + mvs[2] + mvs[3];
+        let cmx = (sum_mv.x >> 3) + H263_CHROMA_ROUND[(sum_mv.x & 0xF) as usize];
+        let cmy = (sum_mv.y >> 3) + H263_CHROMA_ROUND[(sum_mv.y & 0xF) as usize];
+        let mode = ((cmx & 1) + (cmy & 1) * 2) as usize;
+        for plane in 1..3 {
+            blockdsp::copy_block(&mut dst, src.clone(), plane, xpos >> 1, ypos >> 1, cmx >> 1, cmy >> 1, 8, 8, 0, 1, mode, H263_INTERP_FUNCS);
+        }
     }
-    fn avg_blocks(&self, dst: &mut NAVideoBuffer<u8>, src: &NAVideoBuffer<u8>, xpos: usize, ypos: usize, w: usize, h: usize, mv: MV) {
-        let srcx = ((mv.x >> 1) as isize) + (xpos as isize);
-        let srcy = ((mv.y >> 1) as isize) + (ypos as isize);
+    fn avg_blocks(&self, dst: &mut NAVideoBuffer<u8>, src: NAVideoBufferRef<u8>, xpos: usize, ypos: usize, mv: MV) {
         let mode = ((mv.x & 1) + (mv.y & 1) * 2) as usize;
+        let cmode = (if (mv.x & 3) != 0 { 1 } else { 0 }) + (if (mv.y & 3) != 0 { 2 } else { 0 });
 
-        blockdsp::copy_blocks(dst, src, xpos, ypos, srcx, srcy, w, h, 0, 1, mode, H263_INTERP_AVG_FUNCS);
+        let mut dst = NASimpleVideoFrame::from_video_buf(dst).unwrap();
+
+        blockdsp::copy_block(&mut dst, src.clone(), 0, xpos, ypos, mv.x >> 1, mv.y >> 1, 16, 16, 0, 1, mode, H263_INTERP_AVG_FUNCS);
+        blockdsp::copy_block(&mut dst, src.clone(), 1, xpos >> 1, ypos >> 1, mv.x >> 2, mv.y >> 2, 8, 8, 0, 1, cmode, H263_INTERP_AVG_FUNCS);
+        blockdsp::copy_block(&mut dst, src.clone(), 2, xpos >> 1, ypos >> 1, mv.x >> 2, mv.y >> 2, 8, 8, 0, 1, cmode, H263_INTERP_AVG_FUNCS);
+    }
+    fn avg_blocks8x8(&self, dst: &mut NAVideoBuffer<u8>, src: NAVideoBufferRef<u8>, xpos: usize, ypos: usize, mvs: &[MV; 4]) {
+        let mut dst = NASimpleVideoFrame::from_video_buf(dst).unwrap();
+
+        for i in 0..4 {
+            let xadd = (i & 1) * 8;
+            let yadd = (i & 2) * 4;
+            let mode = ((mvs[i].x & 1) + (mvs[i].y & 1) * 2) as usize;
+
+            blockdsp::copy_block(&mut dst, src.clone(), 0, xpos + xadd, ypos + yadd, mvs[i].x >> 1, mvs[i].y >> 1, 8, 8, 0, 1, mode, H263_INTERP_AVG_FUNCS);
+        }
+
+        let sum_mv = mvs[0] + mvs[1] + mvs[2] + mvs[3];
+        let cmx = (sum_mv.x >> 3) + H263_CHROMA_ROUND[(sum_mv.x & 0xF) as usize];
+        let cmy = (sum_mv.y >> 3) + H263_CHROMA_ROUND[(sum_mv.y & 0xF) as usize];
+        let mode = ((cmx & 1) + (cmy & 1) * 2) as usize;
+        for plane in 1..3 {
+            blockdsp::copy_block(&mut dst, src.clone(), plane, xpos >> 1, ypos >> 1, cmx >> 1, cmy >> 1, 8, 8, 0, 1, mode, H263_INTERP_AVG_FUNCS);
+        }
     }
     fn filter_row(&self, buf: &mut NAVideoBuffer<u8>, mb_y: usize, mb_w: usize, cbpi: &CBPInfo) {
         h263_filter_row(buf, mb_y, mb_w, cbpi)