rv20: validate slice start
[nihav.git] / nihav-realmedia / src / codecs / rv20.rs
index d5415b6989c2651bc9bc7d0d017c16eda12beb11..0e743969d306795bfe5ebeb4aaec9833229ca282 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 });
+
+        let mut dst = NASimpleVideoFrame::from_video_buf(dst).unwrap();
 
-        blockdsp::copy_blocks(dst, src, xpos, ypos, srcx, srcy, w, h, 0, 1, mode, H263_INTERP_AVG_FUNCS);
+        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)
@@ -209,8 +253,8 @@ impl<'a> RealVideo20BR<'a> {
                 };
 
         let rl_cb = if sstate.is_iframe { &self.tables.aic_rl_cb } else { &self.tables.rl_cb };
-        let q_add = if quant == 0 || sstate.is_iframe { 0i16 } else { ((quant - 1) | 1) as i16 };
-        let q = if plane_no == 0 { (quant * 2) as i16 } else { H263_CHROMA_QUANT[quant as usize] as i16 };
+        let q = if plane_no == 0 { (quant * 2) as i16 } else { (H263_CHROMA_QUANT[quant as usize] * 2) as i16 };
+        let q_add = if q == 0 || sstate.is_iframe { 0i16 } else { (((q >> 1) - 1) | 1) as i16 };
         while idx < 64 {
             let code = br.read_cb(rl_cb)?;
             let run;
@@ -221,7 +265,11 @@ impl<'a> RealVideo20BR<'a> {
                 level = code.get_level();
                 last  = code.is_last();
                 if br.read_bool()? { level = -level; }
-                level = (level * q) + q_add;
+                if level >= 0 {
+                    level = (level * q) + q_add;
+                } else {
+                    level = (level * q) - q_add;
+                }
             } else {
                 last  = br.read_bool()?;
                 run   = br.read(6)? as u8;
@@ -231,7 +279,11 @@ impl<'a> RealVideo20BR<'a> {
                     let top = br.read_s(6)? as i16;
                     level = (top << 5) | low;
                 }
-                level = (level * q) + q_add;
+                if level >= 0 {
+                    level = (level * q) + q_add;
+                } else {
+                    level = (level * q) - q_add;
+                }
                 if level < -2048 { level = -2048; }
                 if level >  2047 { level =  2047; }
             }
@@ -301,6 +353,7 @@ impl<'a> BlockDecoder for RealVideo20BR<'a> {
         if self.slice_no < self.num_slices {
             let pos = self.br.tell();
             let shdr2 = self.read_slice_header()?;
+            validate!(shdr2.mb_pos > shdr.mb_pos);
             mb_count = shdr2.mb_pos - shdr.mb_pos;
             self.br.seek(pos as u32)?;
         } else {
@@ -632,7 +685,29 @@ mod test {
         let mut dec_reg = RegisteredDecoders::new();
         realmedia_register_all_codecs(&mut dec_reg);
 
-        test_file_decoding("realmedia", "assets/RV/rv20_svt_atrc_640x352_realproducer_plus_8.51.rm", /*None*/Some(3000), true, false, None/*Some("rv20")*/, &dmx_reg, &dec_reg);
-//        test_file_decoding("realmedia", "assets/RV/rv20_cook_640x352_realproducer_plus_8.51.rm", /*None*/Some(1000), true, false, Some("rv20"));
+        test_decoding("realmedia", "realvideo2",
+                      "assets/RV/rv20_svt_atrc_640x352_realproducer_plus_8.51.rm",
+                      Some(1000), &dmx_reg, &dec_reg, ExpectedTestResult::MD5Frames(vec![
+                        [0x319d142d, 0x607a7c28, 0x526a2794, 0xa6e7864f],
+                        [0x319d142d, 0x607a7c28, 0x526a2794, 0xa6e7864f],
+                        [0x319d142d, 0x607a7c28, 0x526a2794, 0xa6e7864f],
+                        [0x319d142d, 0x607a7c28, 0x526a2794, 0xa6e7864f],
+                        [0x319d142d, 0x607a7c28, 0x526a2794, 0xa6e7864f],
+                        [0x319d142d, 0x607a7c28, 0x526a2794, 0xa6e7864f],
+                        [0x319d142d, 0x607a7c28, 0x526a2794, 0xa6e7864f],
+                        [0x319d142d, 0x607a7c28, 0x526a2794, 0xa6e7864f],
+                        [0x319d142d, 0x607a7c28, 0x526a2794, 0xa6e7864f],
+                        [0x8ce88686, 0x03ca3bb9, 0x0d18347b, 0xccdb0bc5],
+                        [0x319d142d, 0x607a7c28, 0x526a2794, 0xa6e7864f],
+                        [0x319d142d, 0x607a7c28, 0x526a2794, 0xa6e7864f],
+                        [0x319d142d, 0x607a7c28, 0x526a2794, 0xa6e7864f],
+                        [0xa2008d4c, 0xf4684b3a, 0xecd0526c, 0xf0742a77],
+                        [0xafe0df5b, 0x29cd2418, 0x29a265c7, 0x9b4f2218],
+                        [0x0e0529df, 0xf1cc3f03, 0x03986b0d, 0xd2033c08],
+                        [0x4662b5ab, 0xaca5ca35, 0x4d089fb2, 0xc6a3df1e],
+                        [0x22c978cf, 0x6887a9ba, 0xe74c9316, 0x8cbdd29b],
+                        [0x12d8b88f, 0x59ebe632, 0xbcfaa336, 0xadbdd9ad],
+                        [0x0d99c67b, 0x3231302f, 0x3612b0d0, 0x38b5414d],
+                        [0x2f65b75e, 0x1239d563, 0x832ce096, 0x568a9bc2]]));
     }
 }