]> git.nihav.org Git - nihav.git/commitdiff
semi-working VP5/6 decoder
authorKostya Shishkov <kostya.shishkov@gmail.com>
Sun, 14 Jul 2019 16:35:04 +0000 (18:35 +0200)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Sat, 3 Aug 2019 09:53:05 +0000 (11:53 +0200)
nihav-duck/Cargo.toml
nihav-duck/src/codecs/mod.rs
nihav-duck/src/codecs/vp3.rs
nihav-duck/src/codecs/vp5.rs [new file with mode: 0644]
nihav-duck/src/codecs/vp56.rs [new file with mode: 0644]
nihav-duck/src/codecs/vp6.rs [new file with mode: 0644]
nihav-duck/src/codecs/vpcommon.rs

index cecf251f3bf349450deeece78103dc8d2c1f3e5d..8817fa85cf0cbaf8a92cc933aa399938e5a01f78 100644 (file)
@@ -14,7 +14,7 @@ nihav_commonfmt = { path = "../nihav-commonfmt" }
 [features]
 default = ["all_decoders"]
 
-all_decoders = ["decoder_truemotion1", "decoder_truemotionrt", "decoder_dk3_adpcm", "decoder_dk4_adpcm", "decoder_truemotion2", "decoder_truemotion2x", "decoder_vp3", "decoder_vp4"]#, "decoder_vp5", "decoder_vp6", "decoder_vp7", "decoder_on2avc"]
+all_decoders = ["decoder_truemotion1", "decoder_truemotionrt", "decoder_dk3_adpcm", "decoder_dk4_adpcm", "decoder_truemotion2", "decoder_truemotion2x", "decoder_vp3", "decoder_vp4", "decoder_vp5", "decoder_vp6"]#, "decoder_vp7", "decoder_on2avc"]
 decoders = []
 
 decoder_truemotion1 = ["decoders"]
index 6a00e0f21715678277fa0c6d6b55a30b97424f10..2c934f18a828e0e8ec9925dcee42285f5560e79e 100644 (file)
@@ -12,14 +12,19 @@ mod truemotionrt;
 mod truemotion2;
 #[cfg(feature="decoder_truemotion2x")]
 mod truemotion2x;
+#[cfg(any(feature="decoder_vp3", feature="decoder_vp4", feature="decoder_vp5", feature="decoder_vp6", feature="decoder_vp7"))]
+#[macro_use]
+mod vpcommon;
 #[cfg(any(feature="decoder_vp3", feature="decoder_vp4"))]
 mod vp3;
 #[cfg(any(feature="decoder_vp5", feature="decoder_vp6"))]
 mod vp56;
+#[cfg(feature="decoder_vp5")]
+mod vp5;
+#[cfg(feature="decoder_vp6")]
+mod vp6;
 #[cfg(feature="decoder_vp7")]
 mod vp7;
-#[cfg(any(feature="decoder_vp3", feature="decoder_vp4", feature="decoder_vp5", feature="decoder_vp6", feature="decoder_vp7"))]
-mod vpcommon;
 
 #[cfg(any(feature="decoder_dk3_adpcm", feature="decoder_dk4_adpcm"))]
 mod dkadpcm;
@@ -40,9 +45,11 @@ const DUCK_CODECS: &[DecoderInfo] = &[
 #[cfg(feature="decoder_vp4")]
     DecoderInfo { name: "vp4", get_decoder: vp3::get_decoder_vp4 },
 #[cfg(feature="decoder_vp5")]
-    DecoderInfo { name: "vp5", get_decoder: vp56::get_decoder_vp5 },
+    DecoderInfo { name: "vp5", get_decoder: vp5::get_decoder },
+#[cfg(feature="decoder_vp6")]
+    DecoderInfo { name: "vp6", get_decoder: vp6::get_decoder_vp6 },
 #[cfg(feature="decoder_vp6")]
-    DecoderInfo { name: "vp6", get_decoder: vp56::get_decoder_vp6 },
+    DecoderInfo { name: "vp6a", get_decoder: vp6::get_decoder_vp6_alpha },
 #[cfg(feature="decoder_vp7")]
     DecoderInfo { name: "vp7", get_decoder: vp7::get_decoder },
 
index 487c671dd00bfdab01867cccd644dfbab2f51dd6..d25d27f9480d2e2d626e1620ab7c2410167140c2 100644 (file)
@@ -542,55 +542,6 @@ macro_rules! fill_dc_pred {
     };
 }
 
-fn vp3_interp00(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, bw: usize, bh: usize)
-{
-    let mut didx = 0;
-    let mut sidx = 0;
-    for _ in 0..bh {
-        for x in 0..bw { dst[didx + x] = src[sidx + x]; }
-        didx += dstride;
-        sidx += sstride;
-    }
-}
-
-fn vp3_interp01(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, bw: usize, bh: usize)
-{
-    let mut didx = 0;
-    let mut sidx = 0;
-    for _ in 0..bh {
-        for x in 0..bw { dst[didx + x] = (((src[sidx + x] as u16) + (src[sidx + x + 1] as u16)) >> 1) as u8; }
-        didx += dstride;
-        sidx += sstride;
-    }
-}
-
-fn vp3_interp10(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, bw: usize, bh: usize)
-{
-    let mut didx = 0;
-    let mut sidx = 0;
-    for _ in 0..bh {
-        for x in 0..bw { dst[didx + x] = (((src[sidx + x] as u16) + (src[sidx + x + sstride] as u16)) >> 1) as u8; }
-        didx += dstride;
-        sidx += sstride;
-    }
-}
-
-fn vp3_interp11(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, bw: usize, bh: usize)
-{
-    let mut didx = 0;
-    let mut sidx = 0;
-    for _ in 0..bh {
-        for x in 0..bw {
-            dst[didx + x] = (((src[sidx + x] as u16) +
-                              (src[sidx + x + 1] as u16) +
-                              (src[sidx + x + sstride] as u16) +
-                              (src[sidx + x + sstride + 1] as u16)) >> 2) as u8;
-        }
-        didx += dstride;
-        sidx += sstride;
-    }
-}
-
 fn vp31_loop_filter_v(frm: &mut NASimpleVideoFrame<u8>, x: usize, y: usize, plane: usize, loop_str: i16) {
     let off = frm.offset[plane] + x + y * frm.stride[plane];
     vp31_loop_filter(frm.data, off, 1, frm.stride[plane], 8, loop_str);
@@ -601,8 +552,6 @@ fn vp31_loop_filter_h(frm: &mut NASimpleVideoFrame<u8>, x: usize, y: usize, plan
     vp31_loop_filter(frm.data, off, frm.stride[plane], 1, 8, loop_str);
 }
 
-pub const VP3_INTERP_FUNCS: &[blockdsp::BlkInterpFunc] = &[ vp3_interp00, vp3_interp01, vp3_interp10, vp3_interp11 ];
-
 impl VP34Decoder {
     fn new(version: u8) -> Self {
         let vt = alloc_video_buffer(NAVideoInfo::new(24, 24, false, YUV420_FORMAT), 4).unwrap();
diff --git a/nihav-duck/src/codecs/vp5.rs b/nihav-duck/src/codecs/vp5.rs
new file mode 100644 (file)
index 0000000..abfd4d0
--- /dev/null
@@ -0,0 +1,451 @@
+use nihav_core::codecs::*;
+use nihav_core::io::bitreader::*;
+use super::vpcommon::*;
+use super::vp56::*;
+
+struct VP5BR {}
+
+impl VP5BR {
+    fn new() -> Self {
+        Self {}
+    }
+}
+
+impl VP56Parser for VP5BR {
+    fn parse_header(&mut self, bc: &mut BoolCoder) -> DecoderResult<VP56Header> {
+        let mut hdr = VP56Header::default();
+        hdr.is_intra                            = !bc.read_bool();
+        hdr.is_golden = hdr.is_intra;
+                                                  bc.read_bool();
+        hdr.quant                               = bc.read_bits(6) as u8;
+        if hdr.is_intra {
+            hdr.version                         = bc.read_bits(13) as u8;
+            validate!(hdr.version == 5);
+            hdr.profile                         = bc.read_bits(2) as u8;
+            hdr.interlaced                      = bc.read_bool();
+            validate!(!hdr.interlaced);
+            hdr.mb_h                            = bc.read_bits(8) as u8;
+            hdr.mb_w                            = bc.read_bits(8) as u8;
+            hdr.disp_h                          = bc.read_bits(8) as u8;
+            hdr.disp_w                          = bc.read_bits(8) as u8;
+            validate!((hdr.mb_h > 0) && (hdr.mb_w > 0) && (hdr.disp_w > 0) && (hdr.disp_h > 0));
+            validate!((hdr.disp_w <= hdr.mb_w) && (hdr.disp_h <= hdr.mb_h));
+            hdr.scale                           = bc.read_bits(2) as u8;
+        }
+        
+        Ok(hdr)
+    }
+    fn decode_mv(&self, bc: &mut BoolCoder, model: &VP56MVModel) -> i16 {
+        if bc.read_prob(model.nz_prob) {
+            let sign                            = bc.read_prob(model.sign_prob);
+            let b0                              = bc.read_prob(model.raw_probs[0]) as i16;
+            let b1                              = bc.read_prob(model.raw_probs[1]) as i16;
+            let top: i16 = vp_tree!(bc, model.tree_probs[0],
+                                vp_tree!(bc, model.tree_probs[1],
+                                    vp_tree!(bc, model.tree_probs[2], 0, 1),
+                                    vp_tree!(bc, model.tree_probs[3], 2, 3)
+                                ),
+                                vp_tree!(bc, model.tree_probs[4],
+                                    vp_tree!(bc, model.tree_probs[5], 4, 5),
+                                    vp_tree!(bc, model.tree_probs[6], 6, 7)
+                                )
+                           );
+            let val = (top << 2) | (b1 << 1) | b0;
+            if !sign {
+                val
+            } else {
+                -val
+            }
+        } else {
+            0
+        }
+    }
+    fn reset_models(&self, models: &mut VP56Models) {
+        for mdl in models.mv_models.iter_mut() {
+            mdl.nz_prob         = 128;
+            mdl.sign_prob       = 128;
+            mdl.raw_probs[0]    = 85;
+            mdl.raw_probs[1]    = 128;
+            mdl.tree_probs      = [128; 7];
+        }
+    }
+    fn decode_mv_models(&self, bc: &mut BoolCoder, models: &mut [VP56MVModel; 2]) -> DecoderResult<()> {
+        const HAS_NZ_PROB:   [u8; 2] = [ 243, 235 ];
+        const HAS_SIGN_PROB: [u8; 2] = [ 220, 211 ];
+        const HAS_RAW0_PROB: [u8; 2] = [ 251, 246 ];
+        const HAS_RAW1_PROB: [u8; 2] = [ 253, 249 ];
+        const HAS_TREE_PROB: [[u8; 7]; 2] = [
+            [ 237, 232, 241, 245, 247, 251, 253 ],
+            [ 234, 231, 248, 249, 252, 252, 254 ]
+        ];
+        for comp in 0..2 {
+            if bc.read_prob(HAS_NZ_PROB[comp]) {
+                models[comp].nz_prob            = bc.read_probability();
+            }
+            if bc.read_prob(HAS_SIGN_PROB[comp]) {
+                models[comp].sign_prob          = bc.read_probability();
+            }
+            if bc.read_prob(HAS_RAW0_PROB[comp]) {
+                models[comp].raw_probs[0]       = bc.read_probability();
+            }
+            if bc.read_prob(HAS_RAW1_PROB[comp]) {
+                models[comp].raw_probs[1]       = bc.read_probability();
+            }
+        }
+        for comp in 0..2 {
+            for i in 0..7 {
+                if bc.read_prob(HAS_TREE_PROB[comp][i]) {
+                    models[comp].tree_probs[i]  = bc.read_probability();
+                }
+            }
+        }
+        Ok(())
+    }
+    fn decode_coeff_models(&self, bc: &mut BoolCoder, models: &mut VP56Models, is_intra: bool) -> DecoderResult<()> {
+        const COEF_PROBS: [[u8; 11]; 2] = [
+            [ 146, 197, 181, 207, 232, 243, 238, 251, 244, 250, 249 ],
+            [ 179, 219, 214, 240, 250, 254, 244, 254, 254, 254, 254 ]
+        ];
+
+        let mut def_prob = [128u8; 11];
+        for plane in 0..2 {
+            for i in 0..11 {
+                if bc.read_prob(COEF_PROBS[plane][i]) {
+                    def_prob[i]                 = bc.read_probability();
+                    models.coeff_models[plane].dc_value_probs[i] = def_prob[i];
+                } else if is_intra {
+                    models.coeff_models[plane].dc_value_probs[i] = def_prob[i];
+                }
+            }
+        }
+        for ctype in 0..3 {
+            for plane in 0..2 {
+                for group in 0..6 {
+                    for i in 0..11 {
+                        if bc.read_prob(VP5_AC_PROBS[ctype][plane][group][i]) {
+                            def_prob[i]         = bc.read_probability();
+                            models.coeff_models[plane].ac_val_probs[ctype][group][i] = def_prob[i];
+                        } else if is_intra {
+                            models.coeff_models[plane].ac_val_probs[ctype][group][i] = def_prob[i];
+                        }
+                    }
+                }
+            }
+        }
+        for plane in 0..2 {
+            let mdl = &mut models.coeff_models[plane];
+            for i in 0..6 {
+                for j in 0..6 {
+                    for k in 0..5 {
+                        mdl.dc_token_probs[i][j][k] = rescale_prob(mdl.dc_value_probs[k], &VP5_DC_WEIGHTS[k][i][j], 254);
+                    }
+                }
+            }
+            for ctype in 0..3 {
+                for group in 0..3 {
+                    for i in 0..6 {
+                        for j in 0..5 {
+                            mdl.ac_type_probs[ctype][group][i][j] = rescale_prob(mdl.ac_val_probs[ctype][group][j], &VP5_AC_WEIGHTS[ctype][group][j][i], 254);
+                        }
+                    }
+                }
+            }
+        }
+        Ok(())
+    }
+    fn decode_block(&self, bc: &mut BoolCoder, coeffs: &mut [i16; 64], model: &VP56CoeffModel, _vp6model: &VP6Models, fstate: &mut FrameState) -> DecoderResult<()> {
+        const COEF_GROUPS: [u8; 64] = [
+            0, 0, 1, 1, 2, 1, 1, 2,
+            2, 1, 1, 2, 2, 2, 1, 2,
+            2, 2, 2, 2, 1, 1, 2, 2,
+            3, 3, 4, 3, 4, 4, 4, 3,
+            3, 3, 3, 3, 4, 3, 3, 3,
+            4, 4, 4, 4, 4, 3, 3, 4,
+            4, 4, 3, 4, 4, 4, 4, 4,
+            4, 4, 5, 5, 5, 5, 5, 5
+        ];
+
+        let mut ctype = 1;
+        let left_ctx = fstate.coeff_cat[fstate.ctx_idx][0] as usize;
+        let top_ctx = fstate.top_ctx as usize;
+
+        let mut val_probs: &[u8; 11] = &model.dc_value_probs;
+        let mut tok_probs: &[u8] = &model.dc_token_probs[left_ctx][top_ctx];
+        let mut idx = 0;
+        loop {
+            let token = vp_tree!(bc, tok_probs[0],
+                            if ctype != 0 { vp_tree!(bc, tok_probs[1], break, 0) } else { 0 },
+                            vp_tree!(bc, tok_probs[2],
+                                1,
+                                vp_tree!(bc, tok_probs[3],
+                                    vp_tree!(bc, tok_probs[4], 2,
+                                             vp_tree!(bc, val_probs[5], 3, 4)),
+                                    TOKEN_LARGE)));
+            let val = expand_token_bc(bc, val_probs, token, 5);
+            ctype = token.min(2) as usize;
+            if token < TOKEN_LARGE {
+                fstate.coeff_cat[fstate.ctx_idx][idx] = token.min(3);
+            } else {
+                fstate.coeff_cat[fstate.ctx_idx][idx] = 4;
+            }
+            coeffs[ZIGZAG[idx]] = val;
+            if idx > 0 {
+                coeffs[ZIGZAG[idx]] *= fstate.ac_quant;
+            }
+            
+            idx += 1;
+            if idx >= 64 {
+                break;
+            }
+            let group = COEF_GROUPS[idx] as usize;
+            val_probs = &model.ac_val_probs[ctype][group];
+            tok_probs = if group > 2 { val_probs
+                        } else {
+                            let ctx = fstate.coeff_cat[fstate.ctx_idx][idx] as usize;
+                            &model.ac_type_probs[ctype][group][ctx]
+                        };
+        }
+        let end = fstate.last_idx[fstate.ctx_idx].min(24);
+        fstate.last_idx[fstate.ctx_idx] = idx;
+        for i in idx..end {
+            fstate.coeff_cat[fstate.ctx_idx][i] = 5;
+        }
+        fstate.top_ctx = fstate.coeff_cat[fstate.ctx_idx][0];
+
+        Ok(())
+    }
+    fn decode_block_huff(&self, _br: &mut BitReader, _coeffs: &mut [i16; 64], _vp6model: &VP6Models, _model: &VP6HuffModels, _fstate: &mut FrameState) -> DecoderResult<()> {
+        unreachable!();
+    }
+    fn mc_block(&self, dst: &mut NASimpleVideoFrame<u8>, mc_buf: NAVideoBufferRef<u8>, src: NAVideoBufferRef<u8>, plane: usize, x: usize, y: usize, mv: MV, loop_str: i16) {
+        let (sx, sy, mx, my) = if (plane != 1) && (plane != 2) {
+                (mv.x / 2, mv.y / 2, mv.x & 1, mv.y & 1)
+            } else {
+                (mv.x / 4, mv.y / 4, (mv.x >> 1) & 1, (mv.y >> 1) & 1)
+            };
+        let mode = (mx as usize) + (my as usize) * 2;
+        vp_copy_block(dst, src, plane, x, y, sx, sy, 0, 1, loop_str,
+                      mode, VP3_INTERP_FUNCS, mc_buf);
+    }
+}
+
+struct VP5Decoder {
+    dec:        VP56Decoder,
+    info:       NACodecInfoRef,
+    br:         VP5BR,
+}
+
+impl VP5Decoder {
+    fn new() -> Self {
+        Self {
+            dec:        VP56Decoder::new(5, false, true),
+            info:       NACodecInfoRef::default(),
+            br:         VP5BR::new(),
+        }
+    }
+}
+
+impl NADecoder for VP5Decoder {
+    fn init(&mut self, supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
+        if let NACodecTypeInfo::Video(vinfo) = info.get_properties() {
+            let myvinfo = NAVideoInfo::new(vinfo.get_width(), vinfo.get_height(), false, YUV420_FORMAT);
+            let myinfo = NACodecTypeInfo::Video(myvinfo.clone());
+            self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref();
+            self.dec.init(supp, myvinfo)?;
+            Ok(())
+        } else {
+            Err(DecoderError::InvalidData)
+        }
+    }
+    fn decode(&mut self, supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
+        let src = pkt.get_buffer();
+
+        let (bufinfo, ftype) = self.dec.decode_frame(supp, src.as_slice(), &mut self.br)?;
+
+        let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), bufinfo);
+        frm.set_keyframe(ftype == FrameType::I);
+        frm.set_frame_type(ftype);
+        Ok(frm.into_ref())
+    }
+}
+
+pub fn get_decoder() -> Box<NADecoder> {
+    Box::new(VP5Decoder::new())
+}
+
+#[cfg(test)]
+mod test {
+    use nihav_core::codecs::RegisteredDecoders;
+    use nihav_core::demuxers::RegisteredDemuxers;
+    use nihav_core::test::dec_video::*;
+    use crate::codecs::duck_register_all_codecs;
+    use nihav_commonfmt::demuxers::generic_register_all_demuxers;
+
+    #[test]
+    fn test_vp5() {
+        let mut dmx_reg = RegisteredDemuxers::new();
+        generic_register_all_demuxers(&mut dmx_reg);
+        let mut dec_reg = RegisteredDecoders::new();
+        duck_register_all_codecs(&mut dec_reg);
+
+        let file = "assets/Duck/Cell-140.vp5";
+        //let file = "assets/Duck/Chocolat-500.vp5";
+        test_file_decoding("avi", file, Some(13), true, false, None/*Some("vp5")*/, &dmx_reg, &dec_reg);
+//panic!("end");
+    }
+}
+
+const VP5_AC_PROBS: [[[[u8; 11]; 6]; 2]; 3] = [
+  [
+    [
+      [ 227, 246, 230, 247, 244, 254, 254, 254, 254, 254, 254 ],
+      [ 202, 254, 209, 231, 231, 249, 249, 253, 254, 254, 254 ],
+      [ 206, 254, 225, 242, 241, 251, 253, 254, 254, 254, 254 ],
+      [ 235, 254, 241, 253, 252, 254, 254, 254, 254, 254, 254 ],
+      [ 234, 254, 248, 254, 254, 254, 254, 254, 254, 254, 254 ],
+      [ 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 ]
+    ], [
+      [ 240, 254, 248, 254, 254, 254, 254, 254, 254, 254, 254 ],
+      [ 238, 254, 240, 253, 254, 254, 254, 254, 254, 254, 254 ],
+      [ 244, 254, 251, 254, 254, 254, 254, 254, 254, 254, 254 ],
+      [ 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 ],
+      [ 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 ],
+      [ 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 ]
+    ]
+  ], [
+    [
+      [ 206, 203, 227, 239, 247, 254, 253, 254, 254, 254, 254 ],
+      [ 207, 199, 220, 236, 243, 252, 252, 254, 254, 254, 254 ],
+      [ 212, 219, 230, 243, 244, 253, 252, 254, 254, 254, 254 ],
+      [ 236, 237, 247, 252, 253, 254, 254, 254, 254, 254, 254 ],
+      [ 240, 240, 248, 254, 254, 254, 254, 254, 254, 254, 254 ],
+      [ 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 ]
+    ], [
+      [ 230, 233, 249, 254, 254, 254, 254, 254, 254, 254, 254 ],
+      [ 238, 238, 250, 254, 254, 254, 254, 254, 254, 254, 254 ],
+      [ 248, 251, 254, 254, 254, 254, 254, 254, 254, 254, 254 ],
+      [ 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 ],
+      [ 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 ],
+      [ 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 ]
+    ]
+  ], [
+    [
+      [ 225, 239, 227, 231, 244, 253, 243, 254, 254, 253, 254 ],
+      [ 232, 234, 224, 228, 242, 249, 242, 252, 251, 251, 254 ],
+      [ 235, 249, 238, 240, 251, 254, 249, 254, 253, 253, 254 ],
+      [ 249, 253, 251, 250, 254, 254, 254, 254, 254, 254, 254 ],
+      [ 251, 250, 249, 254, 254, 254, 254, 254, 254, 254, 254 ],
+      [ 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 ]
+    ], [
+      [ 243, 244, 250, 250, 254, 254, 254, 254, 254, 254, 254 ],
+      [ 249, 248, 250, 253, 254, 254, 254, 254, 254, 254, 254 ],
+      [ 253, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 ],
+      [ 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 ],
+      [ 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 ],
+      [ 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254 ]
+    ]
+  ]
+];
+
+const VP5_DC_WEIGHTS: [[[[i16; 2]; 6]; 6]; 5] = [
+  [
+    [ [154,  61], [141,  54], [ 90,  45], [ 54,  34], [ 54,  13], [128, 109] ],
+    [ [136,  54], [148,  45], [ 92,  41], [ 54,  33], [ 51,  15], [ 87, 113] ],
+    [ [ 87,  44], [ 97,  40], [ 67,  36], [ 46,  29], [ 41,  15], [ 64,  80] ],
+    [ [ 59,  33], [ 61,  31], [ 51,  28], [ 44,  22], [ 33,  12], [ 49,  63] ],
+    [ [ 69,  12], [ 59,  16], [ 46,  14], [ 31,  13], [ 26,   6], [ 92,  26] ],
+    [ [128, 108], [ 77, 119], [ 54,  84], [ 26,  71], [ 87,  19], [ 95, 155] ]
+  ], [
+    [ [154,   4], [182,   0], [159,  -8], [128,  -5], [143,  -5], [187,  55] ],
+    [ [182,   0], [228,  -3], [187,  -7], [174,  -9], [189, -11], [169,  79] ],
+    [ [161,  -9], [192,  -8], [187,  -9], [169, -10], [136,  -9], [184,  40] ],
+    [ [164, -11], [179, -10], [174, -10], [161, -10], [115,  -7], [197,  20] ],
+    [ [195, -11], [195, -11], [146, -10], [110,  -6], [ 95,  -4], [195,  39] ],
+    [ [182,  55], [172,  77], [177,  37], [169,  29], [172,  52], [ 92, 162] ]
+  ], [
+    [ [174,  80], [164,  80], [ 95,  80], [ 46,  66], [ 56,  24], [ 36, 193] ],
+    [ [164,  80], [166,  77], [105,  76], [ 49,  68], [ 46,  31], [ 49, 186] ],
+    [ [ 97,  78], [110,  74], [ 72,  72], [ 44,  60], [ 33,  30], [ 69, 131] ],
+    [ [ 61,  61], [ 69,  63], [ 51,  57], [ 31,  48], [ 26,  27], [ 64,  89] ],
+    [ [ 67,  23], [ 51,  32], [ 36,  33], [ 26,  28], [ 20,  12], [ 44,  68] ],
+    [ [ 26, 197], [ 41, 189], [ 61, 129], [ 28, 103], [ 49,  52], [-12, 245] ]
+  ], [
+    [ [102, 141], [ 79, 166], [ 72, 162], [ 97, 125], [179,   4], [307,   0] ],
+    [ [ 72, 168], [ 69, 175], [ 84, 160], [105, 127], [148,  34], [310,   0] ],
+    [ [ 84, 151], [ 82, 161], [ 87, 153], [ 87, 135], [115,  51], [317,   0] ],
+    [ [ 97, 125], [102, 131], [105, 125], [ 87, 122], [ 84,  64], [ 54, 184] ],
+    [ [166,  18], [146,  43], [125,  51], [ 90,  64], [ 95,   7], [ 38, 154] ],
+    [ [294,   0], [ 13, 225], [ 10, 225], [ 67, 168], [  0, 167], [161,  94] ]
+  ], [
+    [ [172,  76], [172,  75], [136,  80], [ 64,  98], [ 74,  67], [315,   0] ],
+    [ [169,  76], [207,  56], [164,  66], [ 97,  80], [ 67,  72], [328,   0] ],
+    [ [136,  80], [187,  53], [154,  62], [ 72,  85], [ -2, 105], [305,   0] ],
+    [ [ 74,  91], [128,  64], [113,  64], [ 61,  77], [ 41,  75], [259,   0] ],
+    [ [ 46,  84], [ 51,  81], [ 28,  89], [ 31,  78], [ 23,  77], [202,   0] ],
+    [ [323,   0], [323,   0], [300,   0], [236,   0], [195,   0], [328,   0] ]
+  ]
+];
+
+const VP5_AC_WEIGHTS: [[[[[i16; 2]; 6]; 5]; 3]; 3] = [
+  [
+    [
+      [ [276,  0], [238,  0], [195,  0], [156,  0], [113,  0], [274,  0] ],
+      [ [  0,  1], [  0,  1], [  0,  1], [  0,  1], [  0,  1], [  0,  1] ],
+      [ [192, 59], [182, 50], [141, 48], [110, 40], [ 92, 19], [125,128] ],
+      [ [169, 87], [169, 83], [184, 62], [220, 16], [184,  0], [264,  0] ],
+      [ [212, 40], [212, 36], [169, 49], [174, 27], [  8,120], [182, 71] ]
+    ], [
+      [ [259, 10], [197, 19], [143, 22], [123, 16], [110,  8], [133, 88] ],
+      [ [  0,  1], [256,  0], [  0,  1], [  0,  1], [  0,  1], [  0,  1] ],
+      [ [207, 46], [187, 50], [ 97, 83], [ 23,100], [ 41, 56], [ 56,188] ],
+      [ [166, 90], [146,108], [161, 88], [136, 95], [174,  0], [266,  0] ],
+      [ [264,  7], [243, 18], [184, 43], [-14,154], [ 20,112], [ 20,199] ]
+    ], [
+      [ [230, 26], [197, 22], [159, 20], [146, 12], [136,  4], [ 54,162] ],
+      [ [  0,  1], [  0,  1], [  0,  1], [  0,  1], [  0,  1], [  0,  1] ],
+      [ [192, 59], [156, 72], [ 84,101], [ 49,101], [ 79, 47], [ 79,167] ],
+      [ [138,115], [136,116], [166, 80], [238,  0], [195,  0], [261,  0] ],
+      [ [225, 33], [205, 42], [159, 61], [ 79, 96], [ 92, 66], [ 28,195] ]
+    ]
+  ], [
+    [
+      [ [200, 37], [197, 18], [159, 13], [143,  7], [102,  5], [123,126] ],
+      [ [197,  3], [220, -9], [210,-12], [187, -6], [151, -2], [174, 80] ],
+      [ [200, 53], [187, 47], [159, 40], [118, 38], [100, 18], [141,111] ],
+      [ [179, 78], [166, 86], [197, 50], [207, 27], [187,  0], [115,139] ],
+      [ [218, 34], [220, 29], [174, 46], [128, 61], [ 54, 89], [187, 65] ]
+    ], [
+      [ [238, 14], [197, 18], [125, 26], [ 90, 25], [ 82, 13], [161, 86] ],
+      [ [189,  1], [205, -2], [156, -4], [143, -4], [146, -4], [172, 72] ],
+      [ [230, 31], [192, 45], [102, 76], [ 38, 85], [ 56, 41], [ 64,173] ],
+      [ [166, 91], [141,111], [128,116], [118,109], [177,  0], [ 23,222] ],
+      [ [253, 14], [236, 21], [174, 49], [ 33,118], [ 44, 93], [ 23,187] ]
+    ], [
+      [ [218, 28], [179, 28], [118, 35], [ 95, 30], [ 72, 24], [128,108] ],
+      [ [187,  1], [174, -1], [125, -1], [110, -1], [108, -1], [202, 52] ],
+      [ [197, 53], [146, 75], [ 46,118], [ 33,103], [ 64, 50], [118,126] ],
+      [ [138,114], [128,122], [161, 86], [243, -6], [195,  0], [ 38,210] ],
+      [ [215, 39], [179, 58], [ 97,101], [ 95, 85], [ 87, 70], [ 69,152] ]
+    ]
+  ], [
+    [
+      [ [236, 24], [205, 18], [172, 12], [154,  6], [125,  1], [169, 75] ],
+      [ [187,  4], [230, -2], [228, -4], [236, -4], [241, -2], [192, 66] ],
+      [ [200, 46], [187, 42], [159, 34], [136, 25], [105, 10], [179, 62] ],
+      [ [207, 55], [192, 63], [192, 54], [195, 36], [177,  1], [143, 98] ],
+      [ [225, 27], [207, 34], [200, 30], [131, 57], [ 97, 60], [197, 45] ]
+    ], [
+      [ [271,  8], [218, 13], [133, 19], [ 90, 19], [ 72,  7], [182, 51] ],
+      [ [179,  1], [225, -1], [154, -2], [110, -1], [ 92,  0], [195, 41] ],
+      [ [241, 26], [189, 40], [ 82, 64], [ 33, 60], [ 67, 17], [120, 94] ],
+      [ [192, 68], [151, 94], [146, 90], [143, 72], [161,  0], [113,128] ],
+      [ [256, 12], [218, 29], [166, 48], [ 44, 99], [ 31, 87], [148, 78] ]
+    ], [
+      [ [238, 20], [184, 22], [113, 27], [ 90, 22], [ 74,  9], [192, 37] ],
+      [ [184,  0], [215, -1], [141, -1], [ 97,  0], [ 49,  0], [264, 13] ],
+      [ [182, 51], [138, 61], [ 95, 63], [ 54, 59], [ 64, 25], [200, 45] ],
+      [ [179, 75], [156, 87], [174, 65], [177, 44], [174,  0], [164, 85] ],
+      [ [195, 45], [148, 65], [105, 79], [ 95, 72], [ 87, 60], [169, 63] ]
+    ]
+  ]
+];
diff --git a/nihav-duck/src/codecs/vp56.rs b/nihav-duck/src/codecs/vp56.rs
new file mode 100644 (file)
index 0000000..54c84a0
--- /dev/null
@@ -0,0 +1,1128 @@
+use nihav_core::codecs::*;
+use nihav_core::io::bitreader::*;
+use super::vpcommon::*;
+
+pub const TOKEN_LARGE: u8 = 5;
+pub const TOKEN_EOB: u8 = 42;
+
+#[derive(Clone,Copy,Debug,Default)]
+#[allow(dead_code)]
+pub struct VP56Header {
+    pub is_intra:       bool,
+    pub is_golden:      bool,
+    pub quant:          u8,
+    pub multistream:    bool,
+    pub use_huffman:    bool,
+    pub version:        u8,
+    pub profile:        u8,
+    pub interlaced:     bool,
+    pub offset:         u16,
+    pub mb_w:           u8,
+    pub mb_h:           u8,
+    pub disp_w:         u8,
+    pub disp_h:         u8,
+    pub scale:          u8,
+}
+
+#[derive(Clone,Copy,Default)]
+pub struct VP56MVModel {
+    pub nz_prob:        u8,
+    pub sign_prob:      u8,
+    pub raw_probs:      [u8; 8],
+    pub tree_probs:     [u8; 7],
+}
+
+#[derive(Clone,Copy,Default)]
+pub struct VP56MBTypeModel {
+    pub probs:          [u8; 10],
+}
+
+#[derive(Clone,Copy,Default)]
+pub struct VP56CoeffModel {
+    pub dc_token_probs: [[[u8; 5]; 6]; 6],
+    pub dc_value_probs: [u8; 11],
+    pub ac_ctype_probs: [[[[u8; 5]; 5]; 6]; 3],
+    pub ac_type_probs:  [[[[u8; 5]; 6]; 3]; 3],
+    pub ac_val_probs:   [[[u8; 11]; 6]; 3],
+}
+
+pub struct VP6Models {
+    pub scan_order:         [usize; 64],
+    pub scan:               [usize; 64],
+    pub zigzag:             [usize; 64],
+    pub zero_run_probs:     [[u8; 14]; 2],
+}
+
+const MAX_HUFF_ELEMS: usize = 12;
+#[derive(Clone,Copy,Default)]
+pub struct VP6Huff {
+    pub codes:  [u16; MAX_HUFF_ELEMS],
+    pub bits:   [u8; MAX_HUFF_ELEMS],
+}
+
+#[derive(Clone,Copy,Default)]
+struct Node {
+    weight:     u16,
+    sym:        i8,
+    ch0:        usize,
+    ch1:        usize,
+}
+
+fn prob2weight(a: u8, b: u8) -> u8 {
+    let w = (((a as u16) * (b as u16)) >> 8) as u8;
+    if w == 0 {
+        1
+    } else {
+        w
+    }
+}
+
+impl VP6Huff {
+    fn build_codes(&mut self, probs: &[u8; 11]) {
+        let mut weights = [0u8; 12];
+
+        weights[11] = prob2weight( probs[0],  probs[ 1]);
+        weights[ 0] = prob2weight( probs[0], !probs[ 1]);
+        weights[ 1] = prob2weight(!probs[0],  probs[ 2]);
+        let lvroot  = prob2weight(!probs[0], !probs[ 2]);
+        let tworoot = prob2weight( lvroot,    probs[ 3]);
+        let hlroot  = prob2weight( lvroot,   !probs[ 3]);
+        weights[ 2] = prob2weight( tworoot,   probs[ 4]);
+        let root34  = prob2weight( tworoot,  !probs[ 4]);
+        weights[ 3] = prob2weight( root34,    probs[ 5]);
+        weights[ 4] = prob2weight( root34,   !probs[ 5]);
+        let c1root  = prob2weight( hlroot,    probs[ 6]);
+        let c34root = prob2weight( hlroot,   !probs[ 6]);
+        weights[ 5] = prob2weight( c1root,    probs[ 7]);
+        weights[ 6] = prob2weight( c1root,   !probs[ 7]);
+        let c3root  = prob2weight( c34root,   probs[ 8]);
+        let c4root  = prob2weight( c34root,  !probs[ 8]);
+        weights[ 7] = prob2weight( c3root,    probs[ 9]);
+        weights[ 8] = prob2weight( c3root,   !probs[ 9]);
+        weights[ 9] = prob2weight( c4root,    probs[10]);
+        weights[10] = prob2weight( c4root,   !probs[10]);
+
+        self.build(&weights);
+    }
+    fn build_codes_zero_run(&mut self, probs: &[u8; 14]) {
+        let mut weights = [0u8; 9];
+
+        let root   = prob2weight( probs[0],  probs[1]);
+        weights[0] = prob2weight( root,      probs[2]);
+        weights[1] = prob2weight( root,     !probs[2]);
+
+        let root   = prob2weight( probs[0], !probs[1]);
+        weights[2] = prob2weight( root,      probs[3]);
+        weights[3] = prob2weight( root,     !probs[3]);
+
+        let root   = prob2weight(!probs[0],  probs[4]);
+        weights[8] = prob2weight(!probs[0], !probs[4]);
+        let root1  = prob2weight( root,      probs[5]);
+        let root2  = prob2weight( root,     !probs[5]);
+        weights[4] = prob2weight( root1,     probs[6]);
+        weights[5] = prob2weight( root1,    !probs[6]);
+        weights[6] = prob2weight( root2,     probs[7]);
+        weights[7] = prob2weight( root2,    !probs[7]);
+
+        self.build(&weights);
+    }
+    fn build(&mut self, weights: &[u8]) {
+        let mut nodes = [Node::default(); MAX_HUFF_ELEMS * 2];
+        let mut nlen = 0;
+
+        for w in weights.iter().rev() {
+            let weight = *w as u16;
+            let mut pos = nlen;
+            for i in 0..nlen {
+                if nodes[i].weight > weight {
+                    pos = i;
+                    break;
+                }
+            }
+            for j in (pos..nlen).rev() {
+                nodes[j + 1] = nodes[j];
+            }
+            nodes[pos].weight = weight;
+            nodes[pos].sym    = (weights.len() - nlen - 1) as i8;
+            nlen += 1;
+        }
+
+        let mut low = 0;
+        for _ in 0..nlen-1 {
+            let nnode = Node {
+                    weight: nodes[low + 0].weight + nodes[low + 1].weight,
+                    sym:    -1,
+                    ch0:    low + 0,
+                    ch1:    low + 1,
+                };
+            low += 2;
+            let mut pos = low;
+            while (pos < nlen) && (nodes[pos].weight < nnode.weight) {
+                pos += 1;
+            }
+            for j in (pos..nlen).rev() {
+                nodes[j + 1] = nodes[j];
+            }
+            nodes[pos] = nnode;
+            nlen += 1;
+        }
+        self.get_codes(&nodes, nlen - 1, 0, 0);
+        for i in nlen..self.codes.len() {
+            self.codes[i]   = self.codes[0];
+            self.bits[i]    = self.bits[0];
+        }
+    }
+    fn get_codes(&mut self, nodes: &[Node], pos: usize, code: u16, len: u8) {
+        if nodes[pos].sym >= 0 {
+            self.codes[nodes[pos].sym as usize] = code;
+            self.bits [nodes[pos].sym as usize] = len;
+        } else {
+            self.get_codes(nodes, nodes[pos].ch0, (code << 1) | 0, len + 1);
+            self.get_codes(nodes, nodes[pos].ch1, (code << 1) | 1, len + 1);
+        }
+    }
+}
+
+pub trait ReadHuff {
+    fn read_huff(&mut self, huff: &VP6Huff) -> DecoderResult<u8>;
+}
+
+impl<'a> ReadHuff for BitReader<'a> {
+    fn read_huff(&mut self, huff: &VP6Huff) -> DecoderResult<u8> {
+        let peekval                             = self.peek(16);
+        for (i, (code, bit)) in huff.codes.iter().zip(huff.bits.iter()).enumerate() {
+            if (peekval >> (16 - *bit)) == (*code as u32) {
+                self.skip(*bit as u32)?;
+                return Ok(i as u8);
+            }
+        }
+        Err(DecoderError::InvalidData)
+    }
+}
+
+#[derive(Clone,Copy,Default)]
+pub struct VP6HuffModels {
+    pub dc_token_tree:      [VP6Huff; 2],
+    pub ac_token_tree:      [[[VP6Huff; 6]; 3]; 2],
+    pub zero_run_tree:      [VP6Huff; 2],
+}
+
+impl VP6Models {
+    fn new() -> Self {
+        Self {
+            scan_order:         [0; 64],
+            scan:               [0; 64],
+            zigzag:             [0; 64],
+            zero_run_probs:     [[0; 14]; 2],
+        }
+    }
+}
+
+pub struct VP56Models {
+    pub mv_models:          [VP56MVModel; 2],
+    pub mbtype_models:      [[VP56MBTypeModel; 10]; 3],
+    pub coeff_models:       [VP56CoeffModel; 2],
+    pub prob_xmitted:       [[u8; 20]; 3],
+    pub vp6models:          VP6Models,
+    pub vp6huff:            VP6HuffModels,
+}
+
+impl VP56Models {
+    fn new() -> Self {
+        Self {
+            mv_models:      [VP56MVModel::default(); 2],
+            mbtype_models:  [[VP56MBTypeModel::default(); 10]; 3],
+            coeff_models:   [VP56CoeffModel::default(); 2],
+            prob_xmitted:   [[0; 20]; 3],
+            vp6models:      VP6Models::new(),
+            vp6huff:        VP6HuffModels::default(),
+        }
+    }
+}
+
+pub trait VP56Parser {
+    fn parse_header(&mut self, bc: &mut BoolCoder) -> DecoderResult<VP56Header>;
+    fn reset_models(&self, models: &mut VP56Models);
+    fn decode_mv(&self, bc: &mut BoolCoder, model: &VP56MVModel) -> i16;
+    fn decode_mv_models(&self, bc: &mut BoolCoder, models: &mut [VP56MVModel; 2]) -> DecoderResult<()>;
+    fn decode_coeff_models(&self, bc: &mut BoolCoder, models: &mut VP56Models, is_intra: bool) -> DecoderResult<()>;
+    fn decode_block(&self, bc: &mut BoolCoder, coeffs: &mut [i16; 64], model: &VP56CoeffModel, vp6model: &VP6Models, fstate: &mut FrameState) -> DecoderResult<()>;
+    fn decode_block_huff(&self, br: &mut BitReader, coeffs: &mut [i16; 64], vp6model: &VP6Models, model: &VP6HuffModels, fstate: &mut FrameState) -> DecoderResult<()>;
+    fn mc_block(&self, dst: &mut NASimpleVideoFrame<u8>, mc_buf: NAVideoBufferRef<u8>, src: NAVideoBufferRef<u8>, plane: usize, x: usize, y: usize, mv: MV, loop_thr: i16);
+}
+
+enum CoeffReader<'a> {
+    None,
+    Bool(BoolCoder<'a>),
+    Huff(BitReader<'a>),
+}
+
+#[derive(Clone,Copy,Default)]
+struct MBInfo {
+    mb_type:    VPMBType,
+    mv:         MV,
+}
+
+pub struct FrameState {
+    pub mb_x:           usize,
+    pub mb_y:           usize,
+    pub plane:          usize,
+    pub coeff_cat:      [[u8; 64]; 4],
+    pub last_idx:       [usize; 4],
+    pub top_ctx:        u8,
+    pub ctx_idx:        usize,
+    pub dc_quant:       i16,
+    pub ac_quant:       i16,
+    pub dc_zero_run:    [usize; 2],
+    pub ac_zero_run:    [usize; 2],
+}
+
+impl FrameState {
+    fn new() -> Self {
+        Self {
+            mb_x:           0,
+            mb_y:           0,
+            plane:          0,
+            coeff_cat:      [[0; 64]; 4],
+            last_idx:       [0; 4],
+            top_ctx:        0,
+            ctx_idx:        0,
+            dc_quant:       0,
+            ac_quant:       0,
+            dc_zero_run:    [0; 2],
+            ac_zero_run:    [0; 2],
+        }
+    }
+}
+
+pub struct GenericCache<T: Copy> {
+    pub height: usize,
+    pub stride: usize,
+    pub xpos:   usize,
+    pub data:   Vec<T>,
+    pub default: T,
+}
+
+impl<T:Copy> GenericCache<T> {
+    fn new(height: usize, stride: usize, default: T) -> Self {
+        let mut ret = Self {
+                stride,
+                height,
+                xpos:   0,
+                data:   Vec::with_capacity((height + 1) * stride),
+                default,
+            };
+        ret.reset();
+        ret
+    }
+    fn full_size(&self) -> usize { self.stride * (self.height + 1) }
+    fn reset(&mut self) {
+        self.data.truncate(0);
+        let size = self.full_size();
+        self.data.resize(size, self.default);
+        self.xpos = self.stride + 1;
+    }
+    fn update_row(&mut self) {
+        for i in 0..self.stride {
+            self.data[i] = self.data[self.height * self.stride + i];
+        }
+        self.data.truncate(self.stride);
+        let size = self.full_size();
+        self.data.resize(size, self.default);
+        self.xpos = self.stride + 1;
+    }
+}
+
+pub struct VP56Decoder {
+    version:    u8,
+    has_alpha:  bool,
+    flip:       bool,
+    shuf:       VPShuffler,
+    width:      usize,
+    height:     usize,
+    mb_w:       usize,
+    mb_h:       usize,
+    models:     VP56Models,
+    coeffs:     [[i16; 64]; 6],
+    last_mbt:   VPMBType,
+
+    loop_thr:   i16,
+    ilace_prob: u8,
+    ilace_mb:   bool,
+
+    mb_info:    Vec<MBInfo>,
+    fstate:     FrameState,
+    dc_y:       GenericCache<i16>,
+    dc_u:       GenericCache<i16>,
+    dc_v:       GenericCache<i16>,
+    dc_a:       GenericCache<i16>,
+    last_dc:    [[i16; 4]; 3],
+    top_ctx:    [Vec<u8>; 4],
+
+    mc_buf:     NAVideoBufferRef<u8>,
+}
+
+fn rescale_mb_mode_prob(prob: u32, total: u32) -> u8 {
+    (255 * prob / (1 + total)) as u8
+}
+
+fn map_mb_type(mbtype: VPMBType) -> usize {
+    match mbtype {
+        VPMBType::InterNoMV     => 0,
+        VPMBType::Intra         => 1,
+        VPMBType::InterMV       => 2,
+        VPMBType::InterNearest  => 3,
+        VPMBType::InterNear     => 4,
+        VPMBType::GoldenNoMV    => 5,
+        VPMBType::GoldenMV      => 6,
+        VPMBType::InterFourMV   => 7,
+        VPMBType::GoldenNearest => 8,
+        VPMBType::GoldenNear    => 9,
+    }
+}
+
+pub const VP56_COEF_BASE: [i16; 6] = [ 5, 7, 11, 19, 35, 67 ];
+pub fn expand_token_bc(bc: &mut BoolCoder, val_probs: &[u8; 11], token: u8, version: u8) -> i16 {
+    const COEF_ADD_PROBS: [[u8; 12]; 6] = [
+        [ 159, 128,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0 ],
+        [ 165, 145, 128,   0,   0,   0,   0,   0,   0,   0,   0,   0 ],
+        [ 173, 148, 140, 128,   0,   0,   0,   0,   0,   0,   0,   0 ],
+        [ 176, 155, 140, 135, 128,   0,   0,   0,   0,   0,   0,   0 ],
+        [ 180, 157, 141, 134, 130, 128,   0,   0,   0,   0,   0,   0 ],
+        [ 254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129, 128 ],
+    ];
+
+    let mut sign = false;
+    let level;
+    if token < TOKEN_LARGE {
+        if token != 0 {
+            sign                                = bc.read_bool();
+        }
+        level = token as i16;
+    } else {
+        let cat: usize = vp_tree!(bc, val_probs[6],
+                                  vp_tree!(bc, val_probs[7], 0, 1),
+                                  vp_tree!(bc, val_probs[8],
+                                           vp_tree!(bc, val_probs[9],  2, 3),
+                                           vp_tree!(bc, val_probs[10], 4, 5)));
+        if version == 5 {
+            sign                                = bc.read_bool();
+        }
+        let mut add = 0i16;
+        let add_probs = &COEF_ADD_PROBS[cat];
+        for prob in add_probs.iter() {
+            if *prob == 128 { break; }
+            add                                 = (add << 1) | (bc.read_prob(*prob) as i16);
+        }
+        if version != 5 {
+            sign                                = bc.read_bool();
+        }
+        level = VP56_COEF_BASE[cat] + add;
+    }
+    if !sign {
+        level
+    } else {
+        -level
+    }
+}
+
+impl VP56Decoder {
+    pub fn new(version: u8, has_alpha: bool, flip: bool) -> Self {
+        let vt = alloc_video_buffer(NAVideoInfo::new(24, 24, false, YUV420_FORMAT), 4).unwrap();
+        let mc_buf = vt.get_vbuf().unwrap();
+        Self {
+            version, has_alpha, flip,
+            shuf:       VPShuffler::new(),
+            width:      0,
+            height:     0,
+            mb_w:       0,
+            mb_h:       0,
+            models:     VP56Models::new(),
+            coeffs:     [[0; 64]; 6],
+            last_mbt:   VPMBType::InterNoMV,
+
+            loop_thr:   0,
+            ilace_prob: 0,
+            ilace_mb:   false,
+
+            mb_info:    Vec::new(),
+            fstate:     FrameState::new(),
+            dc_y:       GenericCache::new(0, 0, 0),
+            dc_u:       GenericCache::new(0, 0, 0),
+            dc_v:       GenericCache::new(0, 0, 0),
+            dc_a:       GenericCache::new(0, 0, 0),
+            last_dc:    [[0; 4]; 3],
+            top_ctx:    [Vec::new(), Vec::new(), Vec::new(), Vec::new()],
+
+            mc_buf,
+        }
+    }
+    fn set_dimensions(&mut self, width: usize, height: usize) {
+        self.width  = width;
+        self.height = height;
+        self.mb_w   = (self.width  + 15) >> 4;
+        self.mb_h   = (self.height + 15) >> 4;
+        self.mb_info.resize(self.mb_w * self.mb_h, MBInfo::default());
+        self.dc_y   = GenericCache::new(2, 1 + self.mb_w * 2, 0);
+        self.dc_u   = GenericCache::new(1, 1 + self.mb_w,     0);
+        self.dc_v   = GenericCache::new(1, 1 + self.mb_w,     0);
+        self.dc_a   = GenericCache::new(2, 1 + self.mb_w * 2, 0);
+        self.top_ctx = [vec![0; self.mb_w * 2], vec![0; self.mb_w], vec![0; self.mb_w], vec![0; self.mb_w * 2]];
+    }
+    pub fn init(&mut self, supp: &mut NADecoderSupport, vinfo: NAVideoInfo) -> DecoderResult<()> {
+        supp.pool_u8.set_dec_bufs(3);
+        supp.pool_u8.prealloc_video(NAVideoInfo::new(vinfo.get_width(), vinfo.get_height(), false, vinfo.get_format()), 4)?;
+        self.set_dimensions(vinfo.get_width(), vinfo.get_height());
+        Ok(())
+    }
+    pub fn decode_frame(&mut self, supp: &mut NADecoderSupport, src: &[u8], br: &mut dyn VP56Parser) -> DecoderResult<(NABufferType, FrameType)> {
+        let aoffset;
+        let mut bc;
+        if self.has_alpha {
+            validate!(src.len() >= 7);
+            aoffset = ((src[0] as usize) << 16) | ((src[1] as usize) << 8) | (src[2] as usize);
+            validate!((aoffset > 0) && (aoffset < src.len() - 3));
+            bc = BoolCoder::new(&src[3..])?;
+        } else {
+            validate!(src.len() >= 4);
+            aoffset = src.len();
+            bc = BoolCoder::new(src)?;
+        }
+        let hdr = br.parse_header(&mut bc)?;
+        validate!((hdr.offset as usize) < aoffset); //XXX: take alpha 3 byte offset into account?
+
+        if hdr.mb_w != 0 {
+            self.set_dimensions((hdr.mb_w as usize) * 16, (hdr.mb_h as usize) * 16);
+        }
+        let vinfo = NAVideoInfo::new(self.width, self.height, self.flip, YUV420_FORMAT);
+        let ret = supp.pool_u8.get_free();
+        if ret.is_none() {
+            return Err(DecoderError::AllocError);
+        }
+        let mut buf = ret.unwrap();
+        if buf.get_info() != vinfo {
+            self.shuf.clear();
+            supp.pool_u8.reset();
+            supp.pool_u8.prealloc_video(vinfo, 4)?;
+            let ret = supp.pool_u8.get_free();
+            if ret.is_none() {
+                return Err(DecoderError::AllocError);
+            }
+            buf = ret.unwrap();
+        }
+        let mut dframe = NASimpleVideoFrame::from_video_buf(&mut buf).unwrap();
+
+        if hdr.is_intra {
+            self.shuf.clear();
+        }
+
+        let mut cr;
+        if hdr.multistream {
+            let off = (if self.has_alpha { 3 } else { 0 }) + (hdr.offset as usize);
+            if !hdr.use_huffman {
+                let bc2 = BoolCoder::new(&src[off..])?;
+                cr = CoeffReader::Bool(bc2);
+            } else {
+                let br = BitReader::new(&src[off..], aoffset - off, BitReaderMode::BE);
+                cr = CoeffReader::Huff(br);
+            }
+        } else {
+            cr = CoeffReader::None;
+        }
+
+        if hdr.is_intra {
+            br.reset_models(&mut self.models);
+            self.reset_mbtype_models();
+        } else {
+            self.decode_mode_prob_models(&mut bc)?;
+            br.decode_mv_models(&mut bc, &mut self.models.mv_models)?;
+        }
+        br.decode_coeff_models(&mut bc, &mut self.models, hdr.is_intra)?;
+        if hdr.use_huffman {
+            for i in 0..2 {
+                self.models.vp6huff.dc_token_tree[i].build_codes(&self.models.coeff_models[i].dc_value_probs);
+            }
+            for i in 0..2 {
+                for mode in 0..3 {
+                    for band in 0..6 {
+                        self.models.vp6huff.ac_token_tree[i][mode][band].build_codes(&self.models.coeff_models[i].ac_val_probs[mode][band]);
+                    }
+                }
+            }
+            for i in 0..2 {
+                self.models.vp6huff.zero_run_tree[i].build_codes_zero_run(&self.models.vp6models.zero_run_probs[i]);
+            }
+        }
+
+        if hdr.interlaced {
+            self.ilace_prob                     = bc.read_bits(8) as u8;
+        }
+
+        self.fstate = FrameState::new();
+        self.fstate.dc_quant = VP56_DC_QUANTS[hdr.quant as usize] * 4;
+        self.fstate.ac_quant = VP56_AC_QUANTS[hdr.quant as usize] * 4;
+        self.loop_thr = VP56_FILTER_LIMITS[hdr.quant as usize] as i16;
+
+        self.last_mbt = VPMBType::InterNoMV;
+        self.dc_y.reset();
+        self.dc_u.reset();
+        self.dc_v.reset();
+        self.dc_a.reset();
+        for vec in self.top_ctx.iter_mut() {
+            for el in vec.iter_mut() {
+                *el = 0;
+            }
+        }
+        self.last_dc = [[0; 4]; 3];
+        self.last_dc[0][1] = 0x80;
+        self.last_dc[0][2] = 0x80;
+
+        self.ilace_mb = false;
+        for mb_y in 0..self.mb_h {
+            self.fstate.mb_y = mb_y;
+            self.fstate.coeff_cat = [[0; 64]; 4];
+            self.fstate.last_idx = [24; 4];
+            for mb_x in 0..self.mb_w {
+                self.fstate.mb_x = mb_x;
+                self.decode_mb(&mut dframe, &mut bc, &mut cr, br, &hdr, false)?;
+            }
+            self.dc_y.update_row();
+            self.dc_u.update_row();
+            self.dc_v.update_row();
+            self.dc_a.update_row();
+        }
+
+        if self.has_alpha {
+            let asrc = &src[aoffset + 3..];
+            let mut bc = BoolCoder::new(asrc)?;
+            let ahdr = br.parse_header(&mut bc)?;
+            validate!(ahdr.mb_w == hdr.mb_w && ahdr.mb_h == hdr.mb_h);
+        }
+
+        if hdr.is_golden {
+            self.shuf.add_golden_frame(buf.clone());
+        }
+        self.shuf.add_frame(buf.clone());
+
+        Ok((NABufferType::Video(buf), if hdr.is_intra { FrameType::I } else { FrameType::P }))
+    }
+    fn reset_mbtype_models(&mut self) {
+        const DEFAULT_XMITTED_PROBS: [[u8; 20]; 3] = [
+            [ 42,  69, 2, 1, 7, 1, 42, 44, 22, 6, 3, 1, 2, 0, 5, 1, 1, 0, 0, 0 ],
+            [  8, 229, 1, 1, 8, 0,  0,  0,  0, 0, 2, 1, 1, 0, 0, 0, 1, 1, 0, 0 ],
+            [ 35, 122, 1, 1, 6, 1, 34, 46,  0, 0, 2, 1, 1, 0, 1, 0, 1, 1, 0, 0 ]
+        ];
+        self.models.prob_xmitted.copy_from_slice(&DEFAULT_XMITTED_PROBS);
+    }
+    fn decode_mode_prob_models(&mut self, bc: &mut BoolCoder) -> DecoderResult<()> {
+        for ctx in 0..3 {
+            if bc.read_prob(174) {
+                let idx                         = bc.read_bits(4) as usize;
+                for i in 0..20 {
+                    self.models.prob_xmitted[ctx][i ^ 1] = VP56_MODE_VQ[ctx][idx][i];
+                }
+            }
+            if bc.read_prob(254) {
+                for set in 0..20 {
+                    if bc.read_prob(205) {
+                        let sign                = bc.read_bool();
+                        let diff = vp_tree!(bc, 171,
+                                        vp_tree!(bc, 83, 2, 1),
+                                        vp_tree!(bc, 199,
+                                            vp_tree!(bc, 140,
+                                                vp_tree!(bc, 125,
+                                                    vp_tree!(bc, 104, 6, 5),
+                                                    4
+                                                ),
+                                                3
+                                            ),
+                                            bc.read_bits(7)
+                                        )) * 4;
+                        validate!(diff < 256);
+                        let diff = diff as u8;
+                        if !sign {
+                            validate!(self.models.prob_xmitted[ctx][set ^ 1] <= 255 - diff);
+                            self.models.prob_xmitted[ctx][set ^ 1] += diff;
+                        } else {
+                            validate!(self.models.prob_xmitted[ctx][set ^ 1] >= diff);
+                            self.models.prob_xmitted[ctx][set ^ 1] -= diff;
+                        }
+                    }
+                }
+            }
+        }
+        for ctx in 0..3 {
+            let prob_xmitted = &self.models.prob_xmitted[ctx];
+            for mode in 0..10 {
+                let mdl = &mut self.models.mbtype_models[ctx][mode];
+                let mut cnt = [0u32; 10];
+                let mut total = 0;
+                for i in 0..10 {
+                    if i == mode { continue; }
+                    cnt[i] = 100 * (prob_xmitted[i * 2] as u32);
+                    total += cnt[i];
+                }
+                let sum = (prob_xmitted[mode * 2] as u32) + (prob_xmitted[mode * 2 + 1] as u32);
+                mdl.probs[9] = 255 - rescale_mb_mode_prob(prob_xmitted[mode * 2 + 1] as u32, sum);
+
+                let inter_mv0_weight = (cnt[0] as u32) + (cnt[2] as u32);
+                let inter_mv1_weight = (cnt[3] as u32) + (cnt[4] as u32);
+                let gold_mv0_weight = (cnt[5] as u32) + (cnt[6] as u32);
+                let gold_mv1_weight = (cnt[8] as u32) + (cnt[9] as u32);
+                let mix_weight = (cnt[1] as u32) + (cnt[7] as u32);
+                mdl.probs[0] = 1 + rescale_mb_mode_prob(inter_mv0_weight + inter_mv1_weight, total);
+                mdl.probs[1] = 1 + rescale_mb_mode_prob(inter_mv0_weight, inter_mv0_weight + inter_mv1_weight);
+                mdl.probs[2] = 1 + rescale_mb_mode_prob(mix_weight, mix_weight + gold_mv0_weight + gold_mv1_weight);
+                mdl.probs[3] = 1 + rescale_mb_mode_prob(cnt[0] as u32, inter_mv0_weight);
+                mdl.probs[4] = 1 + rescale_mb_mode_prob(cnt[3] as u32, inter_mv1_weight);
+                mdl.probs[5] = 1 + rescale_mb_mode_prob(cnt[1], mix_weight);
+                mdl.probs[6] = 1 + rescale_mb_mode_prob(gold_mv0_weight, gold_mv0_weight + gold_mv1_weight);
+                mdl.probs[7] = 1 + rescale_mb_mode_prob(cnt[5], gold_mv0_weight);
+                mdl.probs[8] = 1 + rescale_mb_mode_prob(cnt[8], gold_mv1_weight);
+            }
+        }
+        Ok(())
+    }
+    fn find_mv_pred(&self, ref_id: u8) -> (usize, MV, MV, MV) {
+        const CAND_POS: [(i8, i8); 12] = [
+            (-1,  0), ( 0, -1),
+            (-1, -1), (-1,  1),
+            (-2,  0), ( 0, -2),
+            (-1, -2), (-2, -1),
+            (-2,  1), (-1,  2),
+            (-2, -2), (-2,  2)
+        ];
+
+        let mut nearest_mv = ZERO_MV;
+        let mut near_mv = ZERO_MV;
+        let mut pred_mv = ZERO_MV;
+        let mut num_mv: usize = 0;
+
+        for (i, (yoff, xoff)) in CAND_POS.iter().enumerate() {
+            let cx = (self.fstate.mb_x as isize) + (*xoff as isize);
+            let cy = (self.fstate.mb_y as isize) + (*yoff as isize);
+            if (cx < 0) || (cy < 0) {
+                continue;
+            }
+            let cx = cx as usize;
+            let cy = cy as usize;
+            if (cx >= self.mb_w) || (cy >= self.mb_h) {
+                continue;
+            }
+            let mb_pos = cx + cy * self.mb_w;
+            let mv = self.mb_info[mb_pos].mv;
+            if (self.mb_info[mb_pos].mb_type.get_ref_id() != ref_id) || (mv == ZERO_MV) {
+                continue;
+            }
+            if num_mv == 0 {
+                nearest_mv = mv;
+                num_mv += 1;
+                if (self.version > 5) && (i < 2) {
+                    pred_mv = mv;
+                }
+            } else if mv != nearest_mv {
+                near_mv = mv;
+                num_mv += 1;
+                break;
+            }
+        }
+
+        (num_mv, nearest_mv, near_mv, pred_mv)
+    }
+    fn decode_mv(&self, bc: &mut BoolCoder, br: &mut dyn VP56Parser) -> MV {
+        let x = br.decode_mv(bc, &self.models.mv_models[0]);
+        let y = br.decode_mv(bc, &self.models.mv_models[1]);
+        MV{ x, y }
+    }
+    fn decode_mb_type(&mut self, bc: &mut BoolCoder, ctx: usize) -> DecoderResult<VPMBType> {
+        let probs = &self.models.mbtype_models[ctx][map_mb_type(self.last_mbt)].probs;
+        if !bc.read_prob(probs[9]) {
+            self.last_mbt = vp_tree!(
+                    bc, probs[0],
+                    vp_tree!(bc, probs[1],
+                        vp_tree!(bc, probs[3], VPMBType::InterNoMV, VPMBType::InterMV),
+                        vp_tree!(bc, probs[4], VPMBType::InterNearest, VPMBType::InterNear)
+                    ),
+                    vp_tree!(bc, probs[2],
+                        vp_tree!(bc, probs[5], VPMBType::Intra, VPMBType::InterFourMV),
+                        vp_tree!(bc, probs[6],
+                            vp_tree!(bc, probs[7], VPMBType::GoldenNoMV, VPMBType::GoldenMV),
+                            vp_tree!(bc, probs[8], VPMBType::InterNearest, VPMBType::InterNear)
+                        )
+                    )
+                );
+        }
+        Ok(self.last_mbt)
+    }
+    fn decode_mb(&mut self, frm: &mut NASimpleVideoFrame<u8>, bc: &mut BoolCoder, cr: &mut CoeffReader, br: &mut dyn VP56Parser, hdr: &VP56Header, alpha: bool) -> DecoderResult<()> {
+        const FOURMV_SUB_TYPE: [VPMBType; 4] = [ VPMBType::InterNoMV, VPMBType::InterMV, VPMBType::InterNearest, VPMBType::InterNear ];
+
+        let mb_x = self.fstate.mb_x;
+        let mb_y = self.fstate.mb_y;
+        self.coeffs = [[0; 64]; 6];
+        let mb_pos = mb_x + mb_y * self.mb_w;
+        let mut four_mv = [ZERO_MV; 4];
+        let mut four_mbt = [VPMBType::Intra; 4];
+
+        if hdr.interlaced {
+            let iprob = self.ilace_prob;
+            let prob = if mb_x == 0 {
+                    iprob
+                } else if !self.ilace_mb {
+                    iprob + (((256 - (iprob as u16)) >> 1) as u8)
+                } else {
+                    iprob - (iprob >> 1)
+                };
+            self.ilace_mb                       = bc.read_prob(prob);
+        }
+
+        let (num_mv, nearest_mv, near_mv, pred_mv) = if hdr.is_intra {
+                (0, ZERO_MV, ZERO_MV, ZERO_MV)
+            } else { self.find_mv_pred(VP_REF_INTER) };
+        let mb_type = if hdr.is_intra {
+                VPMBType::Intra
+            } else {
+                self.decode_mb_type(bc, (num_mv + 1) % 3)?
+            };
+        self.mb_info[mb_pos].mb_type = mb_type;
+        if mb_type.get_ref_id() != VP_REF_GOLDEN {
+            match mb_type {
+                VPMBType::Intra |
+                VPMBType::InterNoMV         => {
+                    self.mb_info[mb_pos].mv = ZERO_MV;
+                },
+                VPMBType::InterMV           => {
+                    let diff_mv = self.decode_mv(bc, br);
+                    self.mb_info[mb_pos].mv = pred_mv + diff_mv;
+                },
+                VPMBType::InterNearest      => {
+                    self.mb_info[mb_pos].mv = nearest_mv;
+                },
+                VPMBType::InterNear         => {
+                    self.mb_info[mb_pos].mv = near_mv;
+                },
+                VPMBType::InterFourMV       => {
+                    for i in 0..4 {
+                        four_mbt[i]         = FOURMV_SUB_TYPE[bc.read_bits(2) as usize];
+                    }
+                    for i in 0..4 {
+                        match four_mbt[i] {
+                            VPMBType::InterNoMV => {},
+                            VPMBType::InterMV   => {
+                                let diff_mv = self.decode_mv(bc, br);
+                                four_mv[i] = pred_mv + diff_mv;
+                            },
+                            VPMBType::InterNearest => {
+                                four_mv[i] = nearest_mv;
+                            },
+                            VPMBType::InterNear => {
+                                four_mv[i] = near_mv;
+                            },
+                            _ => unreachable!(),
+                        };
+                    }
+                    self.mb_info[mb_pos].mv = four_mv[3];
+                },
+                _ => unreachable!(),
+            };
+        } else {
+            let (_num_mv, nearest_mv, near_mv, pred_mv) = self.find_mv_pred(VP_REF_GOLDEN);
+            match mb_type {
+                VPMBType::GoldenNoMV        => {
+                    self.mb_info[mb_pos].mv = ZERO_MV;
+                },
+                VPMBType::GoldenMV          => {
+                    let diff_mv = self.decode_mv(bc, br);
+                    self.mb_info[mb_pos].mv = pred_mv + diff_mv;
+                },
+                VPMBType::GoldenNearest     => {
+                    self.mb_info[mb_pos].mv = nearest_mv;
+                },
+                VPMBType::GoldenNear        => {
+                    self.mb_info[mb_pos].mv = near_mv;
+                },
+                _ => unreachable!(),
+            };
+        }
+        if !mb_type.is_intra() && (mb_type != VPMBType::InterFourMV) {
+            self.do_mc(br, frm, mb_type, self.mb_info[mb_pos].mv, alpha);
+        } else if mb_type == VPMBType::InterFourMV {
+            self.do_fourmv(br, frm, &four_mv, alpha);
+        }
+
+        for blk_no in 0..4 {
+            self.fstate.plane = if !alpha { 0 } else { 3 };
+            self.fstate.ctx_idx = blk_no >> 1;
+            self.fstate.top_ctx = self.top_ctx[self.fstate.plane][mb_x * 2 + (blk_no & 1)];
+            match cr {
+                CoeffReader::None              => {
+                    br.decode_block(bc, &mut self.coeffs[blk_no], &self.models.coeff_models[0], &self.models.vp6models, &mut self.fstate)?;
+                },
+                CoeffReader::Bool(ref mut bcc) => {
+                    br.decode_block(bcc, &mut self.coeffs[blk_no], &self.models.coeff_models[0], &self.models.vp6models, &mut self.fstate)?;
+                },
+                CoeffReader::Huff(ref mut brc) => {
+                    br.decode_block_huff(brc, &mut self.coeffs[blk_no], &self.models.vp6models, &self.models.vp6huff, &mut self.fstate)?;
+                },
+            };
+            self.top_ctx[self.fstate.plane][mb_x * 2 + (blk_no & 1)] = self.fstate.top_ctx;
+            self.predict_dc(mb_type, mb_pos, blk_no, alpha);
+
+            let bx = mb_x * 2 + (blk_no & 1);
+            let by = mb_y * 2 + (blk_no >> 1);
+            let has_ac = self.fstate.last_idx[self.fstate.ctx_idx] > 0;
+            if mb_type.is_intra() {
+                if !self.ilace_mb {
+                    if has_ac {
+                        vp_put_block(&mut self.coeffs[blk_no], bx, by, self.fstate.plane, frm);
+                    } else {
+                        vp_put_block_dc(&mut self.coeffs[blk_no], bx, by, self.fstate.plane, frm);
+                    }
+                } else {
+                    vp_put_block_ilace(&mut self.coeffs[blk_no], bx, by, self.fstate.plane, frm);
+                }
+            } else {
+                if !self.ilace_mb {
+                    if has_ac {
+                        vp_add_block(&mut self.coeffs[blk_no], bx, by, self.fstate.plane, frm);
+                    } else {
+                        vp_add_block_dc(&mut self.coeffs[blk_no], bx, by, self.fstate.plane, frm);
+                    }
+                } else {
+                    vp_add_block_ilace(&mut self.coeffs[blk_no], bx, by, self.fstate.plane, frm);
+                }
+            }
+        }
+        if !alpha {
+            for blk_no in 4..6 {
+                self.fstate.plane = blk_no - 3;
+                self.fstate.ctx_idx = blk_no - 2;
+                self.fstate.top_ctx = self.top_ctx[self.fstate.plane][mb_x];
+                match cr {
+                    CoeffReader::None              => {
+                        br.decode_block(bc, &mut self.coeffs[blk_no], &self.models.coeff_models[1], &self.models.vp6models, &mut self.fstate)?;
+                    },
+                    CoeffReader::Bool(ref mut bcc) => {
+                        br.decode_block(bcc, &mut self.coeffs[blk_no], &self.models.coeff_models[1], &self.models.vp6models, &mut self.fstate)?;
+                    },
+                    CoeffReader::Huff(ref mut brc) => {
+                        br.decode_block_huff(brc, &mut self.coeffs[blk_no], &self.models.vp6models, &self.models.vp6huff, &mut self.fstate)?;
+                    },
+                };
+                self.top_ctx[self.fstate.plane][mb_x] = self.fstate.top_ctx;
+                self.predict_dc(mb_type, mb_pos, blk_no, alpha);
+
+                let has_ac = self.fstate.last_idx[self.fstate.ctx_idx] > 0;
+                if mb_type.is_intra() {
+                    if has_ac {
+                        vp_put_block(&mut self.coeffs[blk_no], mb_x, mb_y, self.fstate.plane, frm);
+                    } else {
+                        vp_put_block_dc(&mut self.coeffs[blk_no], mb_x, mb_y, self.fstate.plane, frm);
+                    }
+                } else {
+                    if has_ac {
+                        vp_add_block(&mut self.coeffs[blk_no], mb_x, mb_y, self.fstate.plane, frm);
+                    } else {
+                        vp_add_block_dc(&mut self.coeffs[blk_no], mb_x, mb_y, self.fstate.plane, frm);
+                    }
+                }
+            }
+        }
+        Ok(())
+    }
+    fn do_mc(&mut self, br: &dyn VP56Parser, frm: &mut NASimpleVideoFrame<u8>, mb_type: VPMBType, mv: MV, alpha: bool) {
+        let x = self.fstate.mb_x * 16;
+        let y = self.fstate.mb_y * 16;
+        let plane = if !alpha { 0 } else { 3 };
+        let src = if mb_type.get_ref_id() == VP_REF_INTER {
+                self.shuf.get_last().unwrap()
+            } else {
+                self.shuf.get_golden().unwrap()
+            };
+
+        br.mc_block(frm, self.mc_buf.clone(), src.clone(), plane, x + 0, y + 0, mv, self.loop_thr);
+        br.mc_block(frm, self.mc_buf.clone(), src.clone(), plane, x + 8, y + 0, mv, self.loop_thr);
+        br.mc_block(frm, self.mc_buf.clone(), src.clone(), plane, x + 0, y + 8, mv, self.loop_thr);
+        br.mc_block(frm, self.mc_buf.clone(), src.clone(), plane, x + 8, y + 8, mv, self.loop_thr);
+        if !alpha {
+            let x = self.fstate.mb_x * 8;
+            let y = self.fstate.mb_y * 8;
+            br.mc_block(frm, self.mc_buf.clone(), src.clone(), 1, x, y, mv, self.loop_thr);
+            br.mc_block(frm, self.mc_buf.clone(), src.clone(), 2, x, y, mv, self.loop_thr);
+        }
+    }
+    fn do_fourmv(&mut self, br: &dyn VP56Parser, frm: &mut NASimpleVideoFrame<u8>, mvs: &[MV; 4], alpha: bool) {
+        let x = self.fstate.mb_x * 16;
+        let y = self.fstate.mb_y * 16;
+        let plane = if !alpha { 0 } else { 3 };
+        let src = self.shuf.get_last().unwrap();
+        for blk_no in 0..4 {
+            br.mc_block(frm, self.mc_buf.clone(), src.clone(),
+                        plane, x + (blk_no & 1) * 8, y + (blk_no & 2) * 4,
+                        mvs[blk_no], self.loop_thr);
+        }
+        if !alpha {
+            let x = self.fstate.mb_x * 8;
+            let y = self.fstate.mb_y * 8;
+            let sum = mvs[0] + mvs[1] + mvs[2] + mvs[3];
+            let mv = MV { x: sum.x / 4, y: sum.y / 4 };
+            br.mc_block(frm, self.mc_buf.clone(), src.clone(), 1, x, y, mv, self.loop_thr);
+            br.mc_block(frm, self.mc_buf.clone(), src.clone(), 2, x, y, mv, self.loop_thr);
+        }
+    }
+    fn predict_dc(&mut self, mb_type: VPMBType, mb_pos: usize, blk_no: usize, alpha: bool) {
+        let mb_x = self.fstate.mb_x;
+        let is_luma = blk_no < 4;
+        let (plane, dcs) = if alpha { (0, &mut self.dc_a) } else {
+                match blk_no {
+                    4 => (1, &mut self.dc_u),
+                    5 => (2, &mut self.dc_v),
+                    _ => (0, &mut self.dc_y),
+                }
+             };
+        let dc_pos = if is_luma {
+                dcs.xpos + mb_x * 2 + (blk_no & 1) + (blk_no >> 1) * dcs.stride
+            } else {
+                dcs.xpos + mb_x
+            };
+        let ref_id = mb_type.get_ref_id();
+        let has_left_blk = is_luma && ((blk_no & 1) != 0);
+        let has_top_blk = is_luma && ((blk_no & 2) != 0);
+        let mut dc_pred = 0;
+        let mut count = 0;
+        if has_left_blk || ((mb_x > 0) && (self.mb_info[mb_pos - 1].mb_type.get_ref_id() == ref_id)) {
+            dc_pred += dcs.data[dc_pos - 1];
+            count += 1;
+        }
+        if has_top_blk || ((mb_pos >= self.mb_w) && (self.mb_info[mb_pos - self.mb_w].mb_type.get_ref_id() == ref_id)) {
+            dc_pred += dcs.data[dc_pos - dcs.stride];
+            count += 1;
+        }
+        if self.version == 5 {
+            if (count < 2) && has_left_blk {
+                dc_pred += dc_pred;
+                count += 1;
+            }
+            if (count < 2) && !has_left_blk && has_top_blk && (mb_x > 0) && (self.mb_info[mb_pos - 1].mb_type.get_ref_id() == ref_id) {
+                dc_pred += dc_pred;
+                count += 1;
+            }
+            if (count < 2) && mb_pos == 0 && !is_luma {
+                count += 1;
+            }
+            if (count < 2) && !has_left_blk && !has_top_blk && is_luma && (mb_x > 0) && (self.mb_info[mb_pos - 1].mb_type.get_ref_id() == ref_id) {
+                dc_pred += dcs.data[dc_pos + dcs.stride - 1];
+                count += 1;
+            }
+            if (count < 2) && blk_no == 2 {
+                dc_pred += dcs.data[dc_pos - dcs.stride + 1];
+                count += 1;
+            }
+            if (count < 2) && !has_left_blk && (mb_pos >= self.mb_w) && (self.mb_info[mb_pos - self.mb_w].mb_type.get_ref_id() == ref_id) {
+                dc_pred += dcs.data[dc_pos - dcs.stride + 1];
+                count += 1;
+            }
+            if (count < 2) && has_left_blk && (mb_pos > self.mb_w) && (mb_x < self.mb_w - 1) && (self.mb_info[mb_pos - self.mb_w + 1].mb_type.get_ref_id() == ref_id) {
+                dc_pred += dcs.data[dc_pos - dcs.stride + 1];
+                count += 1;
+            }
+        }
+        if count == 0 {
+            dc_pred = self.last_dc[ref_id as usize][plane];
+        } else if count == 2 {
+            dc_pred /= 2;
+        }
+        self.coeffs[blk_no][0] += dc_pred;
+        self.last_dc[ref_id as usize][plane] = self.coeffs[blk_no][0];
+        dcs.data[dc_pos] = self.coeffs[blk_no][0];
+        self.coeffs[blk_no][0] = self.coeffs[blk_no][0].wrapping_mul(self.fstate.dc_quant);
+    }
+}
+
+const VP56_DC_QUANTS: [i16; 64] = [
+    47, 47, 47, 47, 45, 43, 43, 43,
+    43, 43, 42, 41, 41, 40, 40, 40,
+    40, 35, 35, 35, 35, 33, 33, 33,
+    33, 32, 32, 32, 27, 27, 26, 26,
+    25, 25, 24, 24, 23, 23, 19, 19,
+    19, 19, 18, 18, 17, 16, 16, 16,
+    16, 16, 15, 11, 11, 11, 10, 10,
+     9,  8,  7,  5,  3,  3,  2,  2
+];
+const VP56_AC_QUANTS: [i16; 64] = [
+    94, 92, 90, 88, 86, 82, 78, 74,
+    70, 66, 62, 58, 54, 53, 52, 51,
+    50, 49, 48, 47, 46, 45, 44, 43,
+    42, 40, 39, 37, 36, 35, 34, 33,
+    32, 31, 30, 29, 28, 27, 26, 25,
+    24, 23, 22, 21, 20, 19, 18, 17,
+    16, 15, 14, 13, 12, 11, 10,  9,
+     8,  7,  6,  5,  4,  3,  2,  1
+];
+
+const VP56_FILTER_LIMITS: [u8; 64] = [
+    14, 14, 13, 13, 12, 12, 10, 10,
+    10, 10,  8,  8,  8,  8,  8,  8,
+     8,  8,  8,  8,  8,  8,  8,  8,
+     8,  8,  8,  8,  8,  8,  8,  8,
+     8,  8,  8,  8,  7,  7,  7,  7,
+     7,  7,  6,  6,  6,  6,  6,  6,
+     5,  5,  5,  5,  4,  4,  4,  4,
+     4,  4,  4,  3,  3,  3,  3,  2
+];
+
+const VP56_MODE_VQ: [[[u8; 20]; 16]; 3] = [
+  [
+    [   9,  15,  32,  25,   7,  19,   9,  21,   1,  12,  14,  12,   3,  18,  14,  23,   3,  10,   0,   4 ],
+    [  48,  39,   1,   2,  11,  27,  29,  44,   7,  27,   1,   4,   0,   3,   1,   6,   1,   2,   0,   0 ],
+    [  21,  32,   1,   2,   4,  10,  32,  43,   6,  23,   2,   3,   1,  19,   1,   6,  12,  21,   0,   7 ],
+    [  69,  83,   0,   0,   0,   2,  10,  29,   3,  12,   0,   1,   0,   3,   0,   3,   2,   2,   0,   0 ],
+    [  11,  20,   1,   4,  18,  36,  43,  48,  13,  35,   0,   2,   0,   5,   3,  12,   1,   2,   0,   0 ],
+    [  70,  44,   0,   1,   2,  10,  37,  46,   8,  26,   0,   2,   0,   2,   0,   2,   0,   1,   0,   0 ],
+    [   8,  15,   0,   1,   8,  21,  74,  53,  22,  42,   0,   1,   0,   2,   0,   3,   1,   2,   0,   0 ],
+    [ 141,  42,   0,   0,   1,   4,  11,  24,   1,  11,   0,   1,   0,   1,   0,   2,   0,   0,   0,   0 ],
+    [   8,  19,   4,  10,  24,  45,  21,  37,   9,  29,   0,   3,   1,   7,  11,  25,   0,   2,   0,   1 ],
+    [  46,  42,   0,   1,   2,  10,  54,  51,  10,  30,   0,   2,   0,   2,   0,   1,   0,   1,   0,   0 ],
+    [  28,  32,   0,   0,   3,  10,  75,  51,  14,  33,   0,   1,   0,   2,   0,   1,   1,   2,   0,   0 ],
+    [ 100,  46,   0,   1,   3,   9,  21,  37,   5,  20,   0,   1,   0,   2,   1,   2,   0,   1,   0,   0 ],
+    [  27,  29,   0,   1,   9,  25,  53,  51,  12,  34,   0,   1,   0,   3,   1,   5,   0,   2,   0,   0 ],
+    [  80,  38,   0,   0,   1,   4,  69,  33,   5,  16,   0,   1,   0,   1,   0,   0,   0,   1,   0,   0 ],
+    [  16,  20,   0,   0,   2,   8, 104,  49,  15,  33,   0,   1,   0,   1,   0,   1,   1,   1,   0,   0 ],
+    [ 194,  16,   0,   0,   1,   1,   1,   9,   1,   3,   0,   0,   0,   1,   0,   1,   0,   0,   0,   0 ],
+  ], [
+    [  41,  22,   1,   0,   1,  31,   0,   0,   0,   0,   0,   1,   1,   7,   0,   1,  98,  25,   4,  10 ],
+    [ 123,  37,   6,   4,   1,  27,   0,   0,   0,   0,   5,   8,   1,   7,   0,   1,  12,  10,   0,   2 ],
+    [  26,  14,  14,  12,   0,  24,   0,   0,   0,   0,  55,  17,   1,   9,   0,  36,   5,   7,   1,   3 ],
+    [ 209,   5,   0,   0,   0,  27,   0,   0,   0,   0,   0,   1,   0,   1,   0,   1,   0,   0,   0,   0 ],
+    [   2,   5,   4,   5,   0, 121,   0,   0,   0,   0,   0,   3,   2,   4,   1,   4,   2,   2,   0,   1 ],
+    [ 175,   5,   0,   1,   0,  48,   0,   0,   0,   0,   0,   2,   0,   1,   0,   2,   0,   1,   0,   0 ],
+    [  83,   5,   2,   3,   0, 102,   0,   0,   0,   0,   1,   3,   0,   2,   0,   1,   0,   0,   0,   0 ],
+    [ 233,   6,   0,   0,   0,   8,   0,   0,   0,   0,   0,   1,   0,   1,   0,   0,   0,   1,   0,   0 ],
+    [  34,  16, 112,  21,   1,  28,   0,   0,   0,   0,   6,   8,   1,   7,   0,   3,   2,   5,   0,   2 ],
+    [ 159,  35,   2,   2,   0,  25,   0,   0,   0,   0,   3,   6,   0,   5,   0,   1,   4,   4,   0,   1 ],
+    [  75,  39,   5,   7,   2,  48,   0,   0,   0,   0,   3,  11,   2,  16,   1,   4,   7,  10,   0,   2 ],
+    [ 212,  21,   0,   1,   0,   9,   0,   0,   0,   0,   1,   2,   0,   2,   0,   0,   2,   2,   0,   0 ],
+    [   4,   2,   0,   0,   0, 172,   0,   0,   0,   0,   0,   1,   0,   2,   0,   0,   2,   0,   0,   0 ],
+    [ 187,  22,   1,   1,   0,  17,   0,   0,   0,   0,   3,   6,   0,   4,   0,   1,   4,   4,   0,   1 ],
+    [ 133,   6,   1,   2,   1,  70,   0,   0,   0,   0,   0,   2,   0,   4,   0,   3,   1,   1,   0,   0 ],
+    [ 251,   1,   0,   0,   0,   2,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0 ],
+  ], [
+    [   2,   3,   2,   3,   0,   2,   0,   2,   0,   0,  11,   4,   1,   4,   0,   2,   3,   2,   0,   4 ],
+    [  49,  46,   3,   4,   7,  31,  42,  41,   0,   0,   2,   6,   1,   7,   1,   4,   2,   4,   0,   1 ],
+    [  26,  25,   1,   1,   2,  10,  67,  39,   0,   0,   1,   1,   0,  14,   0,   2,  31,  26,   1,   6 ],
+    [ 103,  46,   1,   2,   2,  10,  33,  42,   0,   0,   1,   4,   0,   3,   0,   1,   1,   3,   0,   0 ],
+    [  14,  31,   9,  13,  14,  54,  22,  29,   0,   0,   2,   6,   4,  18,   6,  13,   1,   5,   0,   1 ],
+    [  85,  39,   0,   0,   1,   9,  69,  40,   0,   0,   0,   1,   0,   3,   0,   1,   2,   3,   0,   0 ],
+    [  31,  28,   0,   0,   3,  14, 130,  34,   0,   0,   0,   1,   0,   3,   0,   1,   3,   3,   0,   1 ],
+    [ 171,  25,   0,   0,   1,   5,  25,  21,   0,   0,   0,   1,   0,   1,   0,   0,   0,   0,   0,   0 ],
+    [  17,  21,  68,  29,   6,  15,  13,  22,   0,   0,   6,  12,   3,  14,   4,  10,   1,   7,   0,   3 ],
+    [  51,  39,   0,   1,   2,  12,  91,  44,   0,   0,   0,   2,   0,   3,   0,   1,   2,   3,   0,   1 ],
+    [  81,  25,   0,   0,   2,   9, 106,  26,   0,   0,   0,   1,   0,   1,   0,   1,   1,   1,   0,   0 ],
+    [ 140,  37,   0,   1,   1,   8,  24,  33,   0,   0,   1,   2,   0,   2,   0,   1,   1,   2,   0,   0 ],
+    [  14,  23,   1,   3,  11,  53,  90,  31,   0,   0,   0,   3,   1,   5,   2,   6,   1,   2,   0,   0 ],
+    [ 123,  29,   0,   0,   1,   7,  57,  30,   0,   0,   0,   1,   0,   1,   0,   1,   0,   1,   0,   0 ],
+    [  13,  14,   0,   0,   4,  20, 175,  20,   0,   0,   0,   1,   0,   1,   0,   1,   1,   1,   0,   0 ],
+    [ 202,  23,   0,   0,   1,   3,   2,   9,   0,   0,   0,   1,   0,   1,   0,   1,   0,   0,   0,   0 ],
+  ]
+];
+
diff --git a/nihav-duck/src/codecs/vp6.rs b/nihav-duck/src/codecs/vp6.rs
new file mode 100644 (file)
index 0000000..1cf6a78
--- /dev/null
@@ -0,0 +1,967 @@
+use nihav_core::codecs::*;
+use nihav_core::io::bitreader::*;
+use nihav_core::codecs::blockdsp::edge_emu;
+use super::vpcommon::*;
+use super::vp56::*;
+
+const VERSION_VP60: u8 = 6;
+//const VERSION_VP61: u8 = 7;
+const VERSION_VP62: u8 = 8;
+
+const VP6_SIMPLE_PROFILE: u8    = 0;
+const VP6_ADVANCED_PROFILE: u8  = 3;
+
+#[derive(Default)]
+struct VP6BR {
+    vpversion:      u8,
+    profile:        u8,
+    interlaced:     bool,
+    do_pm:          bool,
+    loop_mode:      u8,
+    autosel_pm:     bool,
+    var_thresh:     u16,
+    mv_thresh:      u8,
+    bicubic:        bool,
+    filter_alpha:   usize,
+}
+
+impl VP6BR {
+    fn new() -> Self {
+        Self::default()
+    }
+}
+
+impl VP56Parser for VP6BR {
+    fn parse_header(&mut self, bc: &mut BoolCoder) -> DecoderResult<VP56Header> {
+        let mut hdr = VP56Header::default();
+// horrible hack to match VP6 header parsing
+        let src = bc.src;
+        let mut br = BitReader::new(src, src.len(), BitReaderMode::BE);
+
+        hdr.is_intra                            = !br.read_bool()?;
+        hdr.is_golden = hdr.is_intra;
+        hdr.quant                               = br.read(6)? as u8;
+        hdr.multistream                         = br.read_bool()?;
+        if hdr.is_intra {
+            hdr.version                         = br.read(5)? as u8;
+            validate!((hdr.version >= VERSION_VP60) && (hdr.version <= VERSION_VP62));
+            hdr.profile                         = br.read(2)? as u8;
+            validate!((hdr.profile == VP6_SIMPLE_PROFILE) || (hdr.profile == VP6_ADVANCED_PROFILE));
+            hdr.interlaced                      = br.read_bool()?;
+        } else {
+            hdr.version = self.vpversion;
+            hdr.profile = self.profile;
+        }
+        if hdr.multistream || (hdr.profile == VP6_SIMPLE_PROFILE) {
+            hdr.offset                          = br.read(16)? as u16;
+            validate!(hdr.offset > if hdr.is_intra { 6 } else { 2 });
+        }
+        let bytes = br.tell() >> 3;
+        std::mem::drop(br);
+        bc.skip_bytes(bytes);
+        self.loop_mode = 0;
+        if hdr.is_intra {
+            hdr.mb_h                            = bc.read_bits(8) as u8;
+            hdr.mb_w                            = bc.read_bits(8) as u8;
+            hdr.disp_h                          = bc.read_bits(8) as u8;
+            hdr.disp_w                          = bc.read_bits(8) as u8;
+            validate!((hdr.mb_h > 0) && (hdr.mb_w > 0) && (hdr.disp_w > 0) && (hdr.disp_h > 0));
+            validate!((hdr.disp_w <= hdr.mb_w) && (hdr.disp_h <= hdr.mb_h));
+            hdr.scale                           = bc.read_bits(2) as u8;
+        } else {
+            hdr.is_golden                       = bc.read_bool();
+            if hdr.profile == VP6_ADVANCED_PROFILE {
+                self.loop_mode                  = bc.read_bool() as u8;
+                if self.loop_mode != 0 {
+                    self.loop_mode             += bc.read_bool() as u8;
+                    validate!(self.loop_mode <= 1);
+                }
+                if hdr.version == VERSION_VP62 {
+                    self.do_pm                  = bc.read_bool();
+                }
+            }
+        }
+
+        if (hdr.profile == VP6_ADVANCED_PROFILE) && (hdr.is_intra || self.do_pm) {
+            self.autosel_pm                     = bc.read_bool();
+            if self.autosel_pm {
+                self.var_thresh                 = bc.read_bits(5) as u16;
+                if hdr.version != VERSION_VP62 {
+                    self.var_thresh <<= 5;
+                }
+                self.mv_thresh                  = bc.read_bits(3) as u8;
+            } else {
+                self.bicubic                    = bc.read_bool();
+            }
+            if hdr.version == VERSION_VP62 {
+                self.filter_alpha               = bc.read_bits(4) as usize;
+            } else {
+                self.filter_alpha = 16;
+            }
+        }
+
+        hdr.use_huffman                         = bc.read_bool();
+
+        self.vpversion  = hdr.version;
+        self.profile    = hdr.profile;
+        self.interlaced = hdr.interlaced;
+        Ok(hdr)
+    }
+    fn decode_mv(&self, bc: &mut BoolCoder, model: &VP56MVModel) -> i16 {
+        const LONG_VECTOR_ORDER: [usize; 7] = [ 0, 1, 2, 7, 6, 5, 4 ];
+
+        let val = if !bc.read_prob(model.nz_prob) { // short vector
+                vp_tree!(bc, model.tree_probs[0],
+                         vp_tree!(bc, model.tree_probs[1],
+                                  vp_tree!(bc, model.tree_probs[2], 0, 1),
+                                  vp_tree!(bc, model.tree_probs[3], 2, 3)),
+                         vp_tree!(bc, model.tree_probs[4],
+                                  vp_tree!(bc, model.tree_probs[5], 4, 5),
+                                  vp_tree!(bc, model.tree_probs[6], 6, 7)))
+            } else {
+                let mut raw = 0;
+                for ord in LONG_VECTOR_ORDER.iter() {
+                    raw                         |= (bc.read_prob(model.raw_probs[*ord]) as i16) << *ord;
+                }
+                if (raw & 0xF0) != 0 {
+                    raw                         |= (bc.read_prob(model.raw_probs[3]) as i16) << 3;
+                } else {
+                    raw |= 1 << 3;
+                }
+                raw
+            };
+        if (val != 0) && bc.read_prob(model.sign_prob) {
+            -val
+        } else {
+            val
+        }
+    }
+    fn reset_models(&self, models: &mut VP56Models) {
+        const NZ_PROBS: [u8; 2] = [ 162, 164 ];
+        const RAW_PROBS: [[u8; 8]; 2] = [
+            [ 247, 210, 135, 68, 138, 220, 239, 246 ],
+            [ 244, 184, 201, 44, 173, 221, 239, 253 ]
+        ];
+        const TREE_PROBS: [[u8; 7]; 2] = [
+            [ 225, 146, 172, 147, 214,  39, 156 ],
+            [ 204, 170, 119, 235, 140, 230, 228 ]
+        ];
+        const ZERO_RUN_PROBS: [[u8; 14]; 2] = [
+            [ 198, 197, 196, 146, 198, 204, 169, 142, 130, 136, 149, 149, 191, 249 ],
+            [ 135, 201, 181, 154,  98, 117, 132, 126, 146, 169, 184, 240, 246, 254 ]
+        ];
+
+        for (i, mdl) in models.mv_models.iter_mut().enumerate() {
+            mdl.nz_prob         = NZ_PROBS[i];
+            mdl.sign_prob       = 128;
+            mdl.raw_probs.copy_from_slice(&RAW_PROBS[i]);
+            mdl.tree_probs.copy_from_slice(&TREE_PROBS[i]);
+        }
+        models.vp6models.zero_run_probs.copy_from_slice(&ZERO_RUN_PROBS);
+        reset_scan(&mut models.vp6models, self.interlaced);
+    }
+    fn decode_mv_models(&self, bc: &mut BoolCoder, models: &mut [VP56MVModel; 2]) -> DecoderResult<()> {
+        const HAS_NZ_PROB: [u8; 2] = [ 237, 231 ];
+        const HAS_SIGN_PROB: [u8; 2] = [ 246, 243 ];
+        const HAS_TREE_PROB: [[u8; 7]; 2] = [
+            [ 253, 253, 254, 254, 254, 254, 254 ],
+            [ 245, 253, 254, 254, 254, 254, 254 ]
+        ];
+        const HAS_RAW_PROB: [[u8; 8]; 2] = [
+            [ 254, 254, 254, 254, 254, 250, 250, 252 ],
+            [ 254, 254, 254, 254, 254, 251, 251, 254 ]
+        ];
+
+        for comp in 0..2 {
+            if bc.read_prob(HAS_NZ_PROB[comp]) {
+                models[comp].nz_prob            = bc.read_probability();
+            }
+            if bc.read_prob(HAS_SIGN_PROB[comp]) {
+                models[comp].sign_prob          = bc.read_probability();
+            }
+        }
+        for comp in 0..2 {
+            for (i, prob) in HAS_TREE_PROB[comp].iter().enumerate() {
+                if bc.read_prob(*prob) {
+                    models[comp].tree_probs[i]  = bc.read_probability();
+                }
+            }
+        }
+        for comp in 0..2 {
+            for (i, prob) in HAS_RAW_PROB[comp].iter().enumerate() {
+                if bc.read_prob(*prob) {
+                    models[comp].raw_probs[i]   = bc.read_probability();
+                }
+            }
+        }
+        Ok(())
+    }
+    fn decode_coeff_models(&self, bc: &mut BoolCoder, models: &mut VP56Models, is_intra: bool) -> DecoderResult<()> {
+        const COEF_PROBS: [[u8; 11]; 2] = [
+            [ 146, 255, 181, 207, 232, 243, 238, 251, 244, 250, 249 ],
+            [ 179, 255, 214, 240, 250, 255, 244, 255, 255, 255, 255 ]
+        ];
+        const SCAN_UPD_PROBS: [u8; 64] = [
+              0, 132, 132, 159, 153, 151, 161, 170,
+            164, 162, 136, 110, 103, 114, 129, 118,
+            124, 125, 132, 136, 114, 110, 142, 135,
+            134, 123, 143, 126, 153, 183, 166, 161,
+            171, 180, 179, 164, 203, 218, 225, 217,
+            215, 206, 203, 217, 229, 241, 248, 243,
+            253, 255, 253, 255, 255, 255, 255, 255,
+            255, 255, 255, 255, 255, 255, 255, 255
+        ];
+        const ZERO_RUN_PROBS: [[u8; 14]; 2] = [
+            [ 219, 246, 238, 249, 232, 239, 249, 255, 248, 253, 239, 244, 241, 248 ],
+            [ 198, 232, 251, 253, 219, 241, 253, 255, 248, 249, 244, 238, 251, 255 ]
+        ];
+
+        let mut def_prob = [128u8; 11];
+        for plane in 0..2 {
+            for i in 0..11 {
+                if bc.read_prob(COEF_PROBS[plane][i]) {
+                    def_prob[i]                 = bc.read_probability();
+                    models.coeff_models[plane].dc_value_probs[i] = def_prob[i];
+                } else if is_intra {
+                    models.coeff_models[plane].dc_value_probs[i] = def_prob[i];
+                }
+            }
+        }
+
+        if bc.read_bool() {
+            for i in 1..64 {
+                if bc.read_prob(SCAN_UPD_PROBS[i]) {
+                    models.vp6models.scan_order[i]  = bc.read_bits(4) as usize;
+                }
+            }
+            update_scan(&mut models.vp6models);
+        } else {
+            reset_scan(&mut models.vp6models, self.interlaced);
+        }
+
+        for comp in 0..2 {
+            for i in 0..14 {
+                if bc.read_prob(ZERO_RUN_PROBS[comp][i]) {
+                    models.vp6models.zero_run_probs[comp][i] = bc.read_probability();
+                }
+            }
+        }
+
+        for ctype in 0..3 {
+            for plane in 0..2 {
+                for group in 0..6 {
+                    for i in 0..11 {
+                        if bc.read_prob(VP6_AC_PROBS[ctype][plane][group][i]) {
+                            def_prob[i]         = bc.read_probability();
+                            models.coeff_models[plane].ac_val_probs[ctype][group][i] = def_prob[i];
+                        } else if is_intra {
+                            models.coeff_models[plane].ac_val_probs[ctype][group][i] = def_prob[i];
+                        }
+                    }
+                }
+            }
+        }
+        for plane in 0..2 {
+            let mdl = &mut models.coeff_models[plane];
+            for i in 0..3 {
+                for k in 0..5 {
+                    mdl.dc_token_probs[0][i][k] = rescale_prob(mdl.dc_value_probs[k], &VP6_DC_WEIGHTS[k][i], 255);
+                }
+            }
+        }
+        Ok(())
+    }
+    fn decode_block(&self, bc: &mut BoolCoder, coeffs: &mut [i16; 64], model: &VP56CoeffModel, vp6model: &VP6Models, fstate: &mut FrameState) -> DecoderResult<()> {
+        let left_ctx = fstate.coeff_cat[fstate.ctx_idx][0] as usize;
+        let top_ctx = fstate.top_ctx as usize;
+        let dc_mode = top_ctx + left_ctx;
+        let token = decode_token_bc(bc, &model.dc_token_probs[0][dc_mode], model.dc_value_probs[5], true, true);
+        let val = expand_token_bc(bc, &model.dc_value_probs, token, 6);
+        coeffs[0] = val;
+        fstate.last_idx[fstate.ctx_idx] = 0;
+
+        let mut idx = 1;
+        let mut last_val = val;
+        while idx < 64 {
+            let ac_band = VP6_IDX_TO_AC_BAND[idx];
+            let ac_mode = last_val.abs().min(2) as usize;
+            let has_nnz = (idx == 1) || (last_val != 0);
+            let token = decode_token_bc(bc, &model.ac_val_probs[ac_mode][ac_band], model.ac_val_probs[ac_mode][ac_band][5], false, has_nnz);
+            if token == 42 { break; }
+            let val = expand_token_bc(bc, &model.ac_val_probs[ac_mode][ac_band], token, 6);
+            coeffs[vp6model.zigzag[idx]] = val.wrapping_mul(fstate.ac_quant);
+            idx += 1;
+            last_val = val;
+            if val == 0 {
+                idx += decode_zero_run_bc(bc, &vp6model.zero_run_probs[if idx >= 7 { 1 } else { 0 }]);
+                validate!(idx <= 64);
+            }
+        }
+        fstate.coeff_cat[fstate.ctx_idx][0] = if coeffs[0] != 0 { 1 } else { 0 };
+        fstate.top_ctx = fstate.coeff_cat[fstate.ctx_idx][0];
+        fstate.last_idx[fstate.ctx_idx] = idx;
+        Ok(())
+    }
+    fn decode_block_huff(&self, br: &mut BitReader, coeffs: &mut [i16; 64], vp6model: &VP6Models, model: &VP6HuffModels, fstate: &mut FrameState) -> DecoderResult<()> {
+        let plane = if (fstate.plane == 0) || (fstate.plane == 3) { 0 } else { 1 };
+        let mut last_val;
+
+        if fstate.dc_zero_run[plane] == 0 {
+            let (val, eob) = decode_token_huff(br, &model.dc_token_tree[plane])?;
+            if eob {
+                return Ok(());
+            }
+            last_val = val;
+            coeffs[0] = val;
+            if val == 0 {
+                fstate.dc_zero_run[plane] = decode_eob_run_huff(br)?;
+            }
+        } else {
+            last_val = 0;
+            fstate.dc_zero_run[plane] -= 1;
+        }
+
+        if fstate.ac_zero_run[plane] > 0 {
+            fstate.ac_zero_run[plane] -= 1;
+            fstate.last_idx[fstate.ctx_idx] = 0;
+            return Ok(());
+        }
+
+        let mut idx = 1;
+        while idx < 64 {
+            let ac_band = VP6_IDX_TO_AC_BAND[idx].min(3);
+            let ac_mode = last_val.abs().min(2) as usize;
+            let (val, eob) = decode_token_huff(br, &model.ac_token_tree[plane][ac_mode][ac_band])?;
+            if eob {
+                if idx == 1 {
+                    fstate.ac_zero_run[plane] = decode_eob_run_huff(br)?;
+                }
+                break;
+            }
+            coeffs[vp6model.zigzag[idx]] = val.wrapping_mul(fstate.ac_quant);
+            idx += 1;
+            last_val = val;
+            if val == 0 {
+                idx += decode_zero_run_huff(br, &model.zero_run_tree[if idx >= 7 { 1 } else { 0 }])?;
+                validate!(idx <= 64);
+            }
+        }
+
+        fstate.last_idx[fstate.ctx_idx] = idx;
+
+        Ok(())
+    }
+    fn mc_block(&self, dst: &mut NASimpleVideoFrame<u8>, mut mc_buf: NAVideoBufferRef<u8>, src: NAVideoBufferRef<u8>, plane: usize, x: usize, y: usize, mv: MV, _loop_str: i16) {
+        let is_luma = (plane != 1) && (plane != 2);
+        let (sx, sy, mx, my) = if is_luma {
+                (mv.x / 4, mv.y / 4, mv.x & 3, mv.y & 3)
+            } else {
+                (mv.x / 8, mv.y / 8, mv.x & 7, mv.y & 7)
+            };
+        let tmp_blk = mc_buf.get_data_mut().unwrap();
+        get_block(tmp_blk, 16, src.clone(), plane, x, y, sx, sy);
+        // todo filtering
+        let mut bicubic = self.bicubic;
+        if is_luma && (self.profile == VP6_ADVANCED_PROFILE) {
+            if !self.autosel_pm {
+                bicubic = true;
+            } else {
+                let mv_limit = 1 << (self.mv_thresh + 1);
+                if (mv.x.abs() <= mv_limit) && (mv.y.abs() <= mv_limit) {
+                    let var = calc_variance(&tmp_blk[16 * 2 + 2..], 16);
+                    if var > self.var_thresh {
+                        bicubic = false;
+                    }
+                }
+            }
+        }
+        let dstride = dst.stride[plane];
+        let dbuf = &mut dst.data[dst.offset[plane] + x + y * dstride..];
+        if mx == 0 && my == 0 {
+            let src = &tmp_blk[2 * 16 + 2..];
+            for (dline, sline) in dbuf.chunks_mut(dst.stride[plane]).zip(src.chunks(16)).take(8) {
+                for i in 0..8 { dline[i] = sline[i]; }
+            }
+        } else if is_luma && bicubic {
+            let coeff_h = &VP6_BICUBIC_COEFFS[self.filter_alpha][mx as usize];
+            let coeff_v = &VP6_BICUBIC_COEFFS[self.filter_alpha][my as usize];
+            mc_bicubic(dbuf, dstride, tmp_blk, 16 * 2 + 2, 16, coeff_h, coeff_v);
+        } else {
+            mc_bilinear(dbuf, dstride, tmp_blk, 16 * 2 + 2, 16, mx as u16, my as u16);
+        }
+    }
+}
+
+fn update_scan(model: &mut VP6Models) {
+    let mut idx = 1;
+    for band in 0..16 {
+        for i in 1..64 {
+            if model.scan_order[i] == band {
+                model.scan[idx] = i;
+                idx += 1;
+            }
+        }
+    }
+    for i in 1..64 {
+        model.zigzag[i] = ZIGZAG[model.scan[i]];
+    }
+}
+
+fn reset_scan(model: &mut VP6Models, interlaced: bool) {
+    const VP6_DEFAULT_SCAN_ORDER: [usize; 64] = [
+         0,  0,  1,  1,  1,  2,  2,  2,
+         2,  2,  2,  3,  3,  4,  4,  4,
+         5,  5,  5,  5,  6,  6,  7,  7,
+         7,  7,  7,  8,  8,  9,  9,  9,
+         9,  9,  9, 10, 10, 11, 11, 11,
+        11, 11, 11, 12, 12, 12, 12, 12,
+        12, 13, 13, 13, 13, 13, 14, 14,
+        14, 14, 15, 15, 15, 15, 15, 15
+    ];
+    const VP6_INTERLACED_SCAN_ORDER: [usize; 64] = [
+         0,  1,  0,  1,  1,  2,  5,  3,
+         2,  2,  2,  2,  4,  7,  8, 10,
+         9,  7,  5,  4,  2,  3,  5,  6,
+         8,  9, 11, 12, 13, 12, 11, 10,
+         9,  7,  5,  4,  6,  7,  9, 11,
+        12, 12, 13, 13, 14, 12, 11,  9,
+         7,  9, 11, 12, 14, 14, 14, 15,
+        13, 11, 13, 15, 15, 15, 15, 15
+    ];
+
+    if !interlaced {
+        model.scan_order.copy_from_slice(&VP6_DEFAULT_SCAN_ORDER);
+    } else {
+        model.scan_order.copy_from_slice(&VP6_INTERLACED_SCAN_ORDER);
+    }
+    for i in 0..64 { model.scan[i] = i; }
+    model.zigzag.copy_from_slice(&ZIGZAG);
+}
+
+fn decode_token_bc(bc: &mut BoolCoder, probs: &[u8], prob34: u8, is_dc: bool, has_nnz: bool) -> u8 {
+    if has_nnz && !bc.read_prob(probs[0]) {
+        if is_dc || bc.read_prob(probs[1]) {
+            0
+        } else {
+            TOKEN_EOB
+        }
+    } else {
+        vp_tree!(bc, probs[2],
+                 1,
+                 vp_tree!(bc, probs[3],
+                          vp_tree!(bc, probs[4],
+                                   2,
+                                   vp_tree!(bc, prob34, 3, 4)),
+                          TOKEN_LARGE))
+    }
+}
+
+fn decode_zero_run_bc(bc: &mut BoolCoder, probs: &[u8; 14]) -> usize {
+    let val = vp_tree!(bc, probs[0],
+                    vp_tree!(bc, probs[1],
+                        vp_tree!(bc, probs[2], 0, 1),
+                        vp_tree!(bc, probs[3], 2, 3)),
+                    vp_tree!(bc, probs[4],
+                        vp_tree!(bc, probs[5],
+                            vp_tree!(bc, probs[6], 4, 5),
+                            vp_tree!(bc, probs[7], 6, 7)),
+                        42));
+    if val != 42 {
+        val
+    } else {
+        let mut nval = 8;
+        for i in 0..6 {
+            nval                                += (bc.read_prob(probs[i + 8]) as usize) << i;
+        }
+        nval
+    }
+}
+
+fn decode_token_huff(br: &mut BitReader, huff: &VP6Huff) -> DecoderResult<(i16, bool)> {
+    const COEF_ADD_BITS: [u8; 6] = [ 1, 2, 3, 4, 5, 11 ];
+    let tok                                     = br.read_huff(huff)?;
+    match tok {
+        0   => Ok((0, false)),
+        1 | 2 | 3 | 4 => {
+            if !br.read_bool()? {
+                Ok((tok as i16, false))
+            } else {
+                Ok((-(tok as i16), false))
+            }
+        },
+        5 | 6 | 7 | 8 | 9 | 10 => {
+            let base = (tok - 5) as usize;
+            let add_bits                        = br.read(COEF_ADD_BITS[base])? as i16;
+            let val = VP56_COEF_BASE[base] + add_bits;
+            if !br.read_bool()? {
+                Ok((val, false))
+            } else {
+                Ok((-val, false))
+            }
+        },
+        _   => Ok((0, true)),
+    }
+}
+
+fn decode_eob_run_huff(br: &mut BitReader) -> DecoderResult<usize> {
+    let val                                     = br.read(2)?;
+    match val {
+        0 => Ok(0),
+        1 => Ok(1),
+        2 => {
+            let val                             = br.read(2)?;
+            Ok((val as usize) + 2)
+        },
+        _ => {
+            if br.read_bool()? {
+                Ok((br.read(6)? as usize) + 10)
+            } else {
+                Ok((br.read(2)? as usize) + 6)
+            }
+        },
+    }
+}
+
+fn decode_zero_run_huff(br: &mut BitReader, huff: &VP6Huff) -> DecoderResult<usize> {
+    let val                                     = br.read_huff(huff)?;
+    if val < 8 {
+        Ok(val as usize)
+    } else {
+        Ok((br.read(6)? as usize) + 8)
+    }
+}
+
+
+fn get_block(dst: &mut [u8], dstride: usize, src: NAVideoBufferRef<u8>, comp: usize,
+             dx: usize, dy: usize, mv_x: i16, mv_y: i16)
+{
+    let (w, h) = src.get_dimensions(comp);
+    let sx = (dx as isize) + (mv_x as isize);
+    let sy = (dy as isize) + (mv_y as isize);
+
+    if (sx - 2 < 0) || (sx + 8 + 2 > (w as isize)) ||
+       (sy - 2 < 0) || (sy + 8 + 2 > (h as isize)) {
+        edge_emu(&src, sx - 2, sy - 2, 8 + 2 + 2, 8 + 2 + 2,
+                 dst, dstride, comp);
+    } else {
+        let sstride = src.get_stride(comp);
+        let soff    = src.get_offset(comp);
+        let sdta    = src.get_data();
+        let sbuf: &[u8] = sdta.as_slice();
+        let saddr = soff + ((sx - 2) as usize) + ((sy - 2) as usize) * sstride;
+        let src = &sbuf[saddr..];
+        for (dline, sline) in dst.chunks_mut(dstride).zip(src.chunks(sstride)).take(12) {
+            for i in 0..12 {
+                dline[i] = sline[i];
+            }
+        }
+    }
+}
+
+fn calc_variance(src: &[u8], stride: usize) -> u16 {
+    let mut sum = 0;
+    let mut ssum = 0;
+    for line in src.chunks(stride * 2).take(8) {
+        for el in line.iter().take(8).step_by(2) {
+            let pix = *el as u32;
+            sum += pix;
+            ssum += pix * pix;
+        }
+    }
+    ((ssum * 16 - sum * sum) >> 8) as u16
+}
+
+macro_rules! mc_filter {
+    (bilinear; $a: expr, $b: expr, $c: expr) => {
+        ((($a as u16) * (8 - $c) + ($b as u16) * $c + 3) >> 3) as u8
+    };
+    (bicubic; $src: expr, $off: expr, $step: expr, $coeffs: expr) => {
+        ((($src[$off - $step]     as i32) * ($coeffs[0] as i32) +
+          ($src[$off]             as i32) * ($coeffs[1] as i32) +
+          ($src[$off + $step]     as i32) * ($coeffs[2] as i32) +
+          ($src[$off + $step * 2] as i32) * ($coeffs[3] as i32) + 64) >> 7).min(255).max(0) as u8
+    }
+}
+
+//#[allow(snake_case)]
+fn mc_bilinear(dst: &mut [u8], dstride: usize, src: &[u8], mut soff: usize, sstride: usize, mx: u16, my: u16) {
+    if my == 0 {
+        for dline in dst.chunks_mut(dstride).take(8) {
+            for i in 0..8 {
+                dline[i] = mc_filter!(bilinear; src[soff + i], src[soff + i + 1], mx);
+            }
+            soff += sstride;
+        }
+    } else if mx == 0 {
+        for dline in dst.chunks_mut(dstride).take(8) {
+            for i in 0..8 {
+                dline[i] = mc_filter!(bilinear; src[soff + i], src[soff + i + sstride], my);
+            }
+            soff += sstride;
+        }
+    } else {
+        let mut tmp = [0u8; 8];
+        for i in 0..8 {
+            tmp[i] = mc_filter!(bilinear; src[soff + i], src[soff + i + 1], mx);
+        }
+        soff += sstride;
+        for dline in dst.chunks_mut(dstride).take(8) {
+            for i in 0..8 {
+                let cur = mc_filter!(bilinear; src[soff + i], src[soff + i + 1], mx);
+                dline[i] = mc_filter!(bilinear; tmp[i], cur, my);
+                tmp[i] = cur;
+            }
+            soff += sstride;
+        }
+    }
+}
+
+fn mc_bicubic(dst: &mut [u8], dstride: usize, src: &[u8], mut soff: usize, sstride: usize, coeffs_w: &[i16; 4], coeffs_h: &[i16; 4]) {
+    if coeffs_h[1] == 128 {
+        for dline in dst.chunks_mut(dstride).take(8) {
+            for i in 0..8 {
+                dline[i] = mc_filter!(bicubic; src, soff + i, 1, coeffs_w);
+            }
+            soff += sstride;
+        }
+    } else if coeffs_w[1] == 128 { // horizontal-only interpolation
+        for dline in dst.chunks_mut(dstride).take(8) {
+            for i in 0..8 {
+                dline[i] = mc_filter!(bicubic; src, soff + i, sstride, coeffs_h);
+            }
+            soff += sstride;
+        }
+    } else {
+        let mut buf = [0u8; 16 * 11];
+        soff -= sstride;
+        for dline in buf.chunks_mut(16) {
+            for i in 0..8 {
+                dline[i] = mc_filter!(bicubic; src, soff + i, 1, coeffs_w);
+            }
+            soff += sstride;
+        }
+        let mut soff = 16;
+        for dline in dst.chunks_mut(dstride).take(8) {
+            for i in 0..8 {
+                dline[i] = mc_filter!(bicubic; buf, soff + i, 16, coeffs_h);
+            }
+            soff += 16;
+        }
+    }
+}
+
+struct VP6Decoder {
+    dec:        VP56Decoder,
+    info:       NACodecInfoRef,
+    br:         VP6BR,
+    has_alpha:  bool,
+}
+
+impl VP6Decoder {
+    fn new(has_alpha: bool) -> Self {
+        Self {
+            dec:        VP56Decoder::new(6, has_alpha, true),
+            info:       NACodecInfoRef::default(),
+            br:         VP6BR::new(),
+            has_alpha,
+        }
+    }
+}
+
+impl NADecoder for VP6Decoder {
+    fn init(&mut self, supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
+        if let NACodecTypeInfo::Video(vinfo) = info.get_properties() {
+            let fmt = if !self.has_alpha {
+                    YUV420_FORMAT
+                } else {
+                    NAPixelFormaton::new(ColorModel::YUV(YUVSubmodel::YUVJ),
+                                         Some(NAPixelChromaton::new(0, 0, false, 8, 0, 0, 1)),
+                                         Some(NAPixelChromaton::new(1, 1, false, 8, 0, 1, 1)),
+                                         Some(NAPixelChromaton::new(1, 1, false, 8, 0, 2, 1)),
+                                         Some(NAPixelChromaton::new(0, 0, false, 8, 0, 3, 1)),
+                                         None,
+                                         0, 4)
+                };
+            let myvinfo = NAVideoInfo::new(vinfo.get_width(), vinfo.get_height(), false, fmt);
+            let myinfo = NACodecTypeInfo::Video(myvinfo.clone());
+            self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref();
+            self.dec.init(supp, myvinfo)?;
+            Ok(())
+        } else {
+            Err(DecoderError::InvalidData)
+        }
+    }
+    fn decode(&mut self, supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
+        let src = pkt.get_buffer();
+
+        let (bufinfo, ftype) = self.dec.decode_frame(supp, src.as_slice(), &mut self.br)?;
+
+        let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), bufinfo);
+        frm.set_keyframe(ftype == FrameType::I);
+        frm.set_frame_type(ftype);
+        Ok(frm.into_ref())
+    }
+}
+
+pub fn get_decoder_vp6() -> Box<NADecoder> {
+    Box::new(VP6Decoder::new(false))
+}
+
+pub fn get_decoder_vp6_alpha() -> Box<NADecoder> {
+    Box::new(VP6Decoder::new(true))
+}
+
+#[cfg(test)]
+mod test {
+    use nihav_core::codecs::RegisteredDecoders;
+    use nihav_core::demuxers::RegisteredDemuxers;
+    use nihav_core::test::dec_video::*;
+    use crate::codecs::duck_register_all_codecs;
+    use nihav_commonfmt::demuxers::generic_register_all_demuxers;
+
+    #[test]
+    fn test_vp6() {
+        let mut dmx_reg = RegisteredDemuxers::new();
+        generic_register_all_demuxers(&mut dmx_reg);
+        let mut dec_reg = RegisteredDecoders::new();
+        duck_register_all_codecs(&mut dec_reg);
+
+        //let file = "assets/Duck/predator2_vp60.avi";
+        //let file = "assets/Duck/predator2_vp61.avi";
+        //let file = "assets/Duck/vp6_crash.avi";
+        let file = "assets/Duck/vp6_interlaced.avi";
+        //let file = "assets/Duck/vp6_vid.avi";
+        //let file = "assets/Duck/selection_720x576_300kBit_vp60i.avi";
+        //let file = "assets/Duck/selection_720x576_300kBit_flipped_vp60i.avi";
+        test_file_decoding("avi", file, Some(17), true, false, None/*Some("vp6")*/, &dmx_reg, &dec_reg);
+//panic!("end");
+    }
+}
+
+const VP6_AC_PROBS: [[[[u8; 11]; 6]; 2]; 3] = [
+  [
+    [
+      [ 227, 246, 230, 247, 244, 255, 255, 255, 255, 255, 255 ],
+      [ 255, 255, 209, 231, 231, 249, 249, 253, 255, 255, 255 ],
+      [ 255, 255, 225, 242, 241, 251, 253, 255, 255, 255, 255 ],
+      [ 255, 255, 241, 253, 252, 255, 255, 255, 255, 255, 255 ],
+      [ 255, 255, 248, 255, 255, 255, 255, 255, 255, 255, 255 ],
+      [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
+    ], [
+      [ 240, 255, 248, 255, 255, 255, 255, 255, 255, 255, 255 ],
+      [ 255, 255, 240, 253, 255, 255, 255, 255, 255, 255, 255 ],
+      [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+      [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+      [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+      [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
+    ]
+  ], [
+    [
+      [ 206, 203, 227, 239, 247, 255, 253, 255, 255, 255, 255 ],
+      [ 207, 199, 220, 236, 243, 252, 252, 255, 255, 255, 255 ],
+      [ 212, 219, 230, 243, 244, 253, 252, 255, 255, 255, 255 ],
+      [ 236, 237, 247, 252, 253, 255, 255, 255, 255, 255, 255 ],
+      [ 240, 240, 248, 255, 255, 255, 255, 255, 255, 255, 255 ],
+      [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
+    ], [
+      [ 230, 233, 249, 255, 255, 255, 255, 255, 255, 255, 255 ],
+      [ 238, 238, 250, 255, 255, 255, 255, 255, 255, 255, 255 ],
+      [ 248, 251, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+      [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+      [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+      [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
+    ]
+  ], [
+    [
+      [ 225, 239, 227, 231, 244, 253, 243, 255, 255, 253, 255 ],
+      [ 232, 234, 224, 228, 242, 249, 242, 252, 251, 251, 255 ],
+      [ 235, 249, 238, 240, 251, 255, 249, 255, 253, 253, 255 ],
+      [ 249, 253, 251, 250, 255, 255, 255, 255, 255, 255, 255 ],
+      [ 251, 250, 249, 255, 255, 255, 255, 255, 255, 255, 255 ],
+      [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
+    ], [
+      [ 243, 244, 250, 250, 255, 255, 255, 255, 255, 255, 255 ],
+      [ 249, 248, 250, 253, 255, 255, 255, 255, 255, 255, 255 ],
+      [ 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+      [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+      [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ],
+      [ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 ]
+    ]
+  ]
+];
+
+const VP6_DC_WEIGHTS: [[[i16; 2]; 3]; 5] = [
+    [ [ 122, 133 ], [ 133,  51 ], [ 142, -16 ] ],
+    [ [   0,   1 ], [   0,   1 ], [   0,   1 ] ],
+    [ [  78, 171 ], [ 169,  71 ], [ 221, -30 ] ],
+    [ [ 139, 117 ], [ 214,  44 ], [ 246,  -3 ] ],
+    [ [ 168,  79 ], [ 210,  38 ], [ 203,  17 ] ]
+];
+
+const VP6_IDX_TO_AC_BAND: [usize; 64] = [
+    0, 0, 1, 1, 1, 2, 2, 2,
+    2, 2, 2, 3, 3, 3, 3, 3,
+    3, 3, 3, 3, 3, 3, 4, 4,
+    4, 4, 4, 4, 4, 4, 4, 4,
+    4, 4, 4, 4, 4, 5, 5, 5,
+    5, 5, 5, 5, 5, 5, 5, 5,
+    5, 5, 5, 5, 5, 5, 5, 5,
+    5, 5, 5, 5, 5, 5, 5, 5
+];
+
+const VP6_BICUBIC_COEFFS: [[[i16; 4]; 8]; 17] = [
+  [
+    [   0, 128,   0,   0 ],
+    [  -3, 122,   9,   0 ],
+    [  -4, 109,  24,  -1 ],
+    [  -5,  91,  45,  -3 ],
+    [  -4,  68,  68,  -4 ],
+    [  -3,  45,  91,  -5 ],
+    [  -1,  24, 109,  -4 ],
+    [   0,   9, 122,  -3 ]
+  ], [
+    [   0, 128,   0,   0 ],
+    [  -4, 124,   9,  -1 ],
+    [  -5, 110,  25,  -2 ],
+    [  -6,  91,  46,  -3 ],
+    [  -5,  69,  69,  -5 ],
+    [  -3,  46,  91,  -6 ],
+    [  -2,  25, 110,  -5 ],
+    [  -1,   9, 124,  -4 ]
+  ], [
+    [   0, 128,   0,   0 ],
+    [  -4, 123,  10,  -1 ],
+    [  -6, 110,  26,  -2 ],
+    [  -7,  92,  47,  -4 ],
+    [  -6,  70,  70,  -6 ],
+    [  -4,  47,  92,  -7 ],
+    [  -2,  26, 110,  -6 ],
+    [  -1,  10, 123,  -4 ]
+  ], [
+    [   0, 128,   0,   0 ],
+    [  -5, 124,  10,  -1 ],
+    [  -7, 110,  27,  -2 ],
+    [  -7,  91,  48,  -4 ],
+    [  -6,  70,  70,  -6 ],
+    [  -4,  48,  92,  -8 ],
+    [  -2,  27, 110,  -7 ],
+    [  -1,  10, 124,  -5 ]
+  ], [
+    [   0, 128,   0,   0 ],
+    [  -6, 124,  11,  -1 ],
+    [  -8, 111,  28,  -3 ],
+    [  -8,  92,  49,  -5 ],
+    [  -7,  71,  71,  -7 ],
+    [  -5,  49,  92,  -8 ],
+    [  -3,  28, 111,  -8 ],
+    [  -1,  11, 124,  -6 ]
+  ], [
+    [   0, 128,   0,   0 ],
+    [  -6, 123,  12,  -1 ],
+    [  -9, 111,  29,  -3 ],
+    [  -9,  93,  50,  -6 ],
+    [  -8,  72,  72,  -8 ],
+    [  -6,  50,  93,  -9 ],
+    [  -3,  29, 111,  -9 ],
+    [  -1,  12, 123,  -6 ]
+  ], [
+    [   0, 128,   0,   0 ],
+    [  -7, 124,  12,  -1 ],
+    [ -10, 111,  30,  -3 ],
+    [ -10,  93,  51,  -6 ],
+    [  -9,  73,  73,  -9 ],
+    [  -6,  51,  93, -10 ],
+    [  -3,  30, 111, -10 ],
+    [  -1,  12, 124,  -7 ]
+  ], [
+    [   0, 128,   0,   0 ],
+    [  -7, 123,  13,  -1 ],
+    [ -11, 112,  31,  -4 ],
+    [ -11,  94,  52,  -7 ],
+    [ -10,  74,  74, -10 ],
+    [  -7,  52,  94, -11 ],
+    [  -4,  31, 112, -11 ],
+    [  -1,  13, 123,  -7 ]
+  ], [
+    [   0, 128,   0,   0 ],
+    [  -8, 124,  13,  -1 ],
+    [ -12, 112,  32,  -4 ],
+    [ -12,  94,  53,  -7 ],
+    [ -10,  74,  74, -10 ],
+    [  -7,  53,  94, -12 ],
+    [  -4,  32, 112, -12 ],
+    [  -1,  13, 124,  -8 ]
+  ], [
+    [   0, 128,   0,   0 ],
+    [  -9, 124,  14,  -1 ],
+    [ -13, 112,  33,  -4 ],
+    [ -13,  95,  54,  -8 ],
+    [ -11,  75,  75, -11 ],
+    [  -8,  54,  95, -13 ],
+    [  -4,  33, 112, -13 ],
+    [  -1,  14, 124,  -9 ]
+  ], [
+    [   0, 128,   0,   0 ],
+    [  -9, 123,  15,  -1 ],
+    [ -14, 113,  34,  -5 ],
+    [ -14,  95,  55,  -8 ],
+    [ -12,  76,  76, -12 ],
+    [  -8,  55,  95, -14 ],
+    [  -5,  34, 112, -13 ],
+    [  -1,  15, 123,  -9 ]
+  ], [
+    [   0, 128,   0,   0 ],
+    [ -10, 124,  15,  -1 ],
+    [ -14, 113,  34,  -5 ],
+    [ -15,  96,  56,  -9 ],
+    [ -13,  77,  77, -13 ],
+    [  -9,  56,  96, -15 ],
+    [  -5,  34, 113, -14 ],
+    [  -1,  15, 124, -10 ]
+  ], [
+    [   0, 128,   0,   0 ],
+    [ -10, 123,  16,  -1 ],
+    [ -15, 113,  35,  -5 ],
+    [ -16,  98,  56, -10 ],
+    [ -14,  78,  78, -14 ],
+    [ -10,  56,  98, -16 ],
+    [  -5,  35, 113, -15 ],
+    [  -1,  16, 123, -10 ]
+  ], [
+    [   0, 128,   0,   0 ],
+    [ -11, 124,  17,  -2 ],
+    [ -16, 113,  36,  -5 ],
+    [ -17,  98,  57, -10 ],
+    [ -14,  78,  78, -14 ],
+    [ -10,  57,  98, -17 ],
+    [  -5,  36, 113, -16 ],
+    [  -2,  17, 124, -11 ]
+  ], [
+    [   0, 128,   0,   0 ],
+    [ -12, 125,  17,  -2 ],
+    [ -17, 114,  37,  -6 ],
+    [ -18,  99,  58, -11 ],
+    [ -15,  79,  79, -15 ],
+    [ -11,  58,  99, -18 ],
+    [  -6,  37, 114, -17 ],
+    [  -2,  17, 125, -12 ]
+  ], [
+    [   0, 128,   0,   0 ],
+    [ -12, 124,  18,  -2 ],
+    [ -18, 114,  38,  -6 ],
+    [ -19,  99,  59, -11 ],
+    [ -16,  80,  80, -16 ],
+    [ -11,  59,  99, -19 ],
+    [  -6,  38, 114, -18 ],
+    [  -2,  18, 124, -12 ]
+  ], [
+    [   0, 128,   0,   0 ],
+    [  -4, 118,  16,  -2 ],
+    [  -7, 106,  34,  -5 ],
+    [  -8,  90,  53,  -7 ],
+    [  -8,  72,  72,  -8 ],
+    [  -7,  53,  90,  -8 ],
+    [  -5,  34, 106,  -7 ],
+    [  -2,  16, 118,  -4 ]
+  ]
+];
index 05b07d19eadda854fef417aa8a4c267c387cfcac..0392a383b5248a4647e3b734b43534668e07c4f2 100644 (file)
@@ -16,6 +16,9 @@ pub enum VPMBType {
     GoldenNear,
 }
 
+pub const VP_REF_INTER: u8 = 1;
+pub const VP_REF_GOLDEN: u8 = 2;
+
 #[allow(dead_code)]
 impl VPMBType {
     pub fn is_intra(self) -> bool { self == VPMBType::Intra }
@@ -26,8 +29,8 @@ impl VPMBType {
             VPMBType::InterMV       |
             VPMBType::InterNearest  |
             VPMBType::InterNear     |
-            VPMBType::InterFourMV   => 1,
-            _                       => 2,
+            VPMBType::InterFourMV   => VP_REF_INTER,
+            _                       => VP_REF_GOLDEN,
         }
     }
 }
@@ -67,6 +70,103 @@ impl VPShuffler {
     }
 }
 
+#[allow(dead_code)]
+pub struct BoolCoder<'a> {
+    pub src:    &'a [u8],
+    pos:    usize,
+    value:  u32,
+    range:  u32,
+    bits:   i32,
+}
+
+#[allow(dead_code)]
+impl<'a> BoolCoder<'a> {
+    pub fn new(src: &'a [u8]) -> DecoderResult<Self> {
+        if src.len() < 3 { return Err(DecoderError::ShortData); }
+        let value = ((src[0] as u32) << 24) | ((src[1] as u32) << 16) | ((src[2] as u32) << 8) | (src[3] as u32);
+        Ok(Self { src, pos: 4, value, range: 255, bits: 8 })
+    }
+    pub fn read_bool(&mut self) -> bool {
+        self.read_prob(128)
+    }
+    pub fn read_prob(&mut self, prob: u8) -> bool {
+        self.renorm();
+        let split = 1 + (((self.range - 1) * (prob as u32)) >> 8);
+        let bit;
+        if self.value < (split << 24) {
+            self.range = split;
+            bit = false;
+        } else {
+            self.range -= split;
+            self.value -= split << 24;
+            bit = true;
+        }
+        bit
+    }
+    pub fn read_bits(&mut self, bits: u8) -> u32 {
+        let mut val = 0u32;
+        for _ in 0..bits {
+            val = (val << 1) | (self.read_prob(128) as u32);
+        }
+        val
+    }
+    pub fn read_probability(&mut self) -> u8 {
+        let val = self.read_bits(7) as u8;
+        if val == 0 {
+            1
+        } else {
+            val << 1
+        }
+    }
+    fn renorm(&mut self) {
+        let shift = self.range.leading_zeros() & 7;
+        self.range <<= shift;
+        self.value <<= shift;
+        self.bits   -= shift as i32;
+        if (self.bits <= 0) && (self.pos < self.src.len()) {
+            self.value |= (self.src[self.pos] as u32) << (-self.bits as u8);
+            self.pos += 1;
+            self.bits += 8;
+        }
+/*        while self.range < 0x80 {
+            self.range <<= 1;
+            self.value <<= 1;
+            self.bits   -= 1;
+            if (self.bits <= 0) && (self.pos < self.src.len()) {
+                self.value |= self.src[self.pos] as u32;
+                self.pos += 1;
+                self.bits = 8;
+            }
+        }*/
+    }
+    pub fn skip_bytes(&mut self, nbytes: usize) {
+        for _ in 0..nbytes {
+            self.value <<= 8;
+            if self.pos < self.src.len() {
+                self.value |= self.src[self.pos] as u32;
+                self.pos += 1;
+            }
+        }
+    }
+}
+
+#[allow(dead_code)]
+pub fn rescale_prob(prob: u8, weights: &[i16; 2], maxval: i32) -> u8 {
+    ((((prob as i32) * (weights[0] as i32) + 128) >> 8) + (weights[1] as i32)).min(maxval).max(1) as u8
+}
+
+#[macro_export]
+macro_rules! vp_tree {
+    ($bc: expr, $prob: expr, $node1: expr, $node2: expr) => {
+        if !$bc.read_prob($prob) {
+            $node1
+        } else {
+            $node2
+        }
+    };
+    ($leaf: expr) => { $leaf }
+}
+
 const C1S7: i32 = 64277;
 const C2S6: i32 = 60547;
 const C3S5: i32 = 54491;
@@ -153,6 +253,17 @@ pub fn vp_put_block(coeffs: &mut [i16; 64], bx: usize, by: usize, plane: usize,
     }
 }
 
+pub fn vp_put_block_ilace(coeffs: &mut [i16; 64], bx: usize, by: usize, plane: usize, frm: &mut NASimpleVideoFrame<u8>) {
+    vp_idct(coeffs);
+    let mut off = frm.offset[plane] + bx * 8 + ((by & !1) * 8 + (by & 1)) * frm.stride[plane];
+    for y in 0..8 {
+        for x in 0..8 {
+            frm.data[off + x] = (coeffs[x + y * 8] + 128).min(255).max(0) as u8;
+        }
+        off += frm.stride[plane] * 2;
+    }
+}
+
 pub fn vp_put_block_dc(coeffs: &mut [i16; 64], bx: usize, by: usize, plane: usize, frm: &mut NASimpleVideoFrame<u8>) {
     vp_idct_dc(coeffs);
     let dc = (coeffs[0] + 128).min(255).max(0) as u8;
@@ -176,6 +287,17 @@ pub fn vp_add_block(coeffs: &mut [i16; 64], bx: usize, by: usize, plane: usize,
     }
 }
 
+pub fn vp_add_block_ilace(coeffs: &mut [i16; 64], bx: usize, by: usize, plane: usize, frm: &mut NASimpleVideoFrame<u8>) {
+    vp_idct(coeffs);
+    let mut off = frm.offset[plane] + bx * 8 + ((by & !1) * 8 + (by & 1)) * frm.stride[plane];
+    for y in 0..8 {
+        for x in 0..8 {
+            frm.data[off + x] = (coeffs[x + y * 8] + (frm.data[off + x] as i16)).min(255).max(0) as u8;
+        }
+        off += frm.stride[plane] * 2;
+    }
+}
+
 pub fn vp_add_block_dc(coeffs: &mut [i16; 64], bx: usize, by: usize, plane: usize, frm: &mut NASimpleVideoFrame<u8>) {
     vp_idct_dc(coeffs);
     let dc = coeffs[0];
@@ -249,3 +371,55 @@ pub fn vp_copy_block(dst: &mut NASimpleVideoFrame<u8>, src: NAVideoBufferRef<u8>
     let dyoff = (pre as i16) - (dy as i16);
     copy_block(dst, mc_buf, comp, dx, dy, dxoff, dyoff, 8, 8, preborder, postborder, 0/* mode*/, interp);
 }
+
+fn vp3_interp00(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, bw: usize, bh: usize)
+{
+    let mut didx = 0;
+    let mut sidx = 0;
+    for _ in 0..bh {
+        for x in 0..bw { dst[didx + x] = src[sidx + x]; }
+        didx += dstride;
+        sidx += sstride;
+    }
+}
+
+fn vp3_interp01(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, bw: usize, bh: usize)
+{
+    let mut didx = 0;
+    let mut sidx = 0;
+    for _ in 0..bh {
+        for x in 0..bw { dst[didx + x] = (((src[sidx + x] as u16) + (src[sidx + x + 1] as u16)) >> 1) as u8; }
+        didx += dstride;
+        sidx += sstride;
+    }
+}
+
+fn vp3_interp10(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, bw: usize, bh: usize)
+{
+    let mut didx = 0;
+    let mut sidx = 0;
+    for _ in 0..bh {
+        for x in 0..bw { dst[didx + x] = (((src[sidx + x] as u16) + (src[sidx + x + sstride] as u16)) >> 1) as u8; }
+        didx += dstride;
+        sidx += sstride;
+    }
+}
+
+fn vp3_interp11(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, bw: usize, bh: usize)
+{
+    let mut didx = 0;
+    let mut sidx = 0;
+    for _ in 0..bh {
+        for x in 0..bw {
+            dst[didx + x] = (((src[sidx + x] as u16) +
+                              (src[sidx + x + 1] as u16) +
+                              (src[sidx + x + sstride] as u16) +
+                              (src[sidx + x + sstride + 1] as u16)) >> 2) as u8;
+        }
+        didx += dstride;
+        sidx += sstride;
+    }
+}
+
+pub const VP3_INTERP_FUNCS: &[blockdsp::BlkInterpFunc] = &[ vp3_interp00, vp3_interp01, vp3_interp10, vp3_interp11 ];
+