]> git.nihav.org Git - nihav.git/commitdiff
mostly working ITU H.264 decoder
authorKostya Shishkov <kostya.shishkov@gmail.com>
Tue, 3 Nov 2020 09:47:39 +0000 (10:47 +0100)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Sat, 12 Dec 2020 17:02:07 +0000 (18:02 +0100)
18 files changed:
nihav-allstuff/Cargo.toml
nihav-allstuff/src/lib.rs
nihav-itu/Cargo.toml [new file with mode: 0644]
nihav-itu/src/codecs/h264/cabac.rs [new file with mode: 0644]
nihav-itu/src/codecs/h264/cabac_coder.rs [new file with mode: 0644]
nihav-itu/src/codecs/h264/cavlc.rs [new file with mode: 0644]
nihav-itu/src/codecs/h264/dsp.rs [new file with mode: 0644]
nihav-itu/src/codecs/h264/loopfilter.rs [new file with mode: 0644]
nihav-itu/src/codecs/h264/mod.rs [new file with mode: 0644]
nihav-itu/src/codecs/h264/pic_ref.rs [new file with mode: 0644]
nihav-itu/src/codecs/h264/sets.rs [new file with mode: 0644]
nihav-itu/src/codecs/h264/slice.rs [new file with mode: 0644]
nihav-itu/src/codecs/h264/test/conformance.rs [new file with mode: 0644]
nihav-itu/src/codecs/h264/test/raw_demux.rs [new file with mode: 0644]
nihav-itu/src/codecs/h264/types.rs [new file with mode: 0644]
nihav-itu/src/codecs/mod.rs [new file with mode: 0644]
nihav-itu/src/lib.rs [new file with mode: 0644]
nihav-registry/src/register.rs

index fc78a051fc3ee1edff7d028fd2d3f4f035591391..b7398cbc80d87d6e4855985b1a23b891e034e19f 100644 (file)
@@ -10,6 +10,7 @@ nihav_commonfmt = { path = "../nihav-commonfmt" }
 nihav_duck = { path = "../nihav-duck" }
 nihav_game = { path = "../nihav-game" }
 nihav_indeo = { path = "../nihav-indeo" }
+nihav_itu = { path = "../nihav-itu" }
 nihav_llaudio = { path = "../nihav-llaudio" }
 nihav_ms = { path = "../nihav-ms" }
 nihav_qt = { path = "../nihav-qt" }
index 90a7766bf323109a39f42f20902788db7e6f4978..af8064c2a272d07c3d70e103c1bd350718bd30bc 100644 (file)
@@ -4,6 +4,7 @@ extern crate nihav_commonfmt;
 extern crate nihav_duck;
 extern crate nihav_game;
 extern crate nihav_indeo;
+extern crate nihav_itu;
 extern crate nihav_llaudio;
 extern crate nihav_ms;
 extern crate nihav_rad;
@@ -26,6 +27,8 @@ use nihav_game::game_register_all_demuxers;
 
 use nihav_indeo::indeo_register_all_decoders;
 
+use nihav_itu::itu_register_all_decoders;
+
 use nihav_llaudio::llaudio_register_all_decoders;
 use nihav_llaudio::llaudio_register_all_demuxers;
 
@@ -46,6 +49,7 @@ pub fn nihav_register_all_decoders(rd: &mut RegisteredDecoders) {
     duck_register_all_decoders(rd);
     game_register_all_decoders(rd);
     indeo_register_all_decoders(rd);
+    itu_register_all_decoders(rd);
     llaudio_register_all_decoders(rd);
     ms_register_all_decoders(rd);
     qt_register_all_decoders(rd);
diff --git a/nihav-itu/Cargo.toml b/nihav-itu/Cargo.toml
new file mode 100644 (file)
index 0000000..2c5521a
--- /dev/null
@@ -0,0 +1,23 @@
+[package]
+name = "nihav_itu"
+version = "0.1.0"
+authors = ["Kostya Shishkov <kostya.shishkov@gmail.com>"]
+edition = "2018"
+
+[dependencies.nihav_core]
+path = "../nihav-core"
+
+[dependencies.nihav_codec_support]
+path = "../nihav-codec-support"
+
+[dev-dependencies]
+nihav_commonfmt = { path = "../nihav-commonfmt" }
+
+[features]
+default = ["all_decoders"]
+
+all_decoders = ["all_video_decoders"]
+decoders = []
+
+all_video_decoders = ["decoder_h264"]
+decoder_h264 = ["decoders"]
diff --git a/nihav-itu/src/codecs/h264/cabac.rs b/nihav-itu/src/codecs/h264/cabac.rs
new file mode 100644 (file)
index 0000000..554da2a
--- /dev/null
@@ -0,0 +1,887 @@
+//use nihav_core::codecs::{DecoderResult, DecoderError};
+
+use super::*;
+use super::cabac_coder::*;
+use super::dsp::{CHROMA_DC_SCAN, ZIGZAG, ZIGZAG1, ZIGZAG8X8};
+use super::slice::SliceHeader;
+
+pub fn cabac_decode_mbskip(cabac: &mut CABAC, sstate: &SliceState, slice_hdr: &SliceHeader) -> bool {
+    let skip_idx = if slice_hdr.slice_type.is_p() { 11 } else { 24 };
+    let mut mb_skip_ctx = 0;
+    let left_mbt = sstate.get_left_mb().mb_type;
+    let top_mbt  = sstate.get_top_mb().mb_type;
+    if left_mbt != CompactMBType::None && !left_mbt.is_skip() {
+        mb_skip_ctx += 1;
+    }
+    if top_mbt != CompactMBType::None && !top_mbt.is_skip() {
+        mb_skip_ctx += 1;
+    }
+    if !slice_hdr.slice_type.is_intra() {
+        cabac.decode_bit(skip_idx + mb_skip_ctx)
+    } else {
+        false
+    }
+}
+
+fn decode_i_type(cabac: &mut CABAC, start: usize, ctx: usize) -> MBType {
+    if !cabac.decode_bit(start + ctx) {
+        MBType::Intra4x4
+    } else if !cabac.decode_terminate() {
+        let cbpy = if cabac.decode_bit(start + 3) { 0xF } else { 0x0 };
+        let cbpc = cabac.decode_012(start + 4);
+        let ipred = cabac.decode_bits(start + 6, start + 7, 2);
+
+        MBType::Intra16x16(ipred, cbpy, cbpc)
+    } else {
+        MBType::PCM
+    }
+}
+
+fn decode_i_type_inter(cabac: &mut CABAC, start: usize) -> MBType {
+    if !cabac.decode_bit(start) {
+        MBType::Intra4x4
+    } else if !cabac.decode_terminate() {
+        let cbpy = if cabac.decode_bit(start + 1) { 0xF } else { 0x0 };
+        let cbpc = if !cabac.decode_bit(start + 2) {
+                0
+            } else if !cabac.decode_bit(start + 2) {
+                1
+            } else {
+                2
+            };
+        let ipred = cabac.decode_bits(start + 3, start + 3, 2);
+
+        MBType::Intra16x16(ipred, cbpy, cbpc)
+    } else {
+        MBType::PCM
+    }
+}
+
+fn remap_si_mbtype(mbtype: MBType) -> MBType {
+    match mbtype {
+        MBType::Intra16x16(0, 0, 0) => MBType::Intra4x4,
+        MBType::Intra16x16(imode, cbpy, cbpc) => {
+            let idx = imode + if cbpy != 0 { 12 } else { 0 } + cbpc * 4 - 1;
+            let nimode = idx & 3;
+            let (ncbpy, ncbpc) = if (idx >> 2) >= 3 {
+                    (0xF, (idx >> 2) - 3)
+                } else {
+                    (0x0, idx >> 2)
+                };
+            MBType::Intra16x16(nimode, ncbpy, ncbpc)
+        },
+        MBType::PCM => MBType::Intra16x16(3, 1, 2),
+        _ => mbtype,
+    }
+}
+
+pub fn cabac_decode_mb_type(cabac: &mut CABAC, slice_hdr: &SliceHeader, sstate: &SliceState) -> MBType {
+    match slice_hdr.slice_type {
+        SliceType::I | SliceType::SI => {
+            let mut ctx = 0;
+            if sstate.get_left_mb().mb_type.is_intra16orpcm() {
+                ctx += 1;
+            }
+            if sstate.get_top_mb().mb_type.is_intra16orpcm() {
+                ctx += 1;
+            }
+            let mbtype = decode_i_type(cabac, 3, ctx);
+            if slice_hdr.slice_type == SliceType::I {
+                mbtype
+            } else {
+                remap_si_mbtype(mbtype)
+            }
+        },
+        SliceType::P | SliceType::SP => {
+            if cabac.decode_bit(14) {
+                decode_i_type_inter(cabac, 17)
+            } else if !cabac.decode_bit(15) {
+                if !cabac.decode_bit(16) {
+                    MBType::P16x16
+                } else {
+                    MBType::P8x8
+                }
+            } else {
+                if !cabac.decode_bit(17) {
+                    MBType::P8x16
+                } else {
+                    MBType::P16x8
+                }
+            }
+        },
+        SliceType::B => {
+            let mut ctx = 0;
+            if !sstate.get_left_mb().mb_type.is_direct() {
+                ctx += 1;
+            }
+            if !sstate.get_top_mb().mb_type.is_direct() {
+                ctx += 1;
+            }
+            if !cabac.decode_bit(27 + ctx) {
+                MBType::Direct
+            } else if !cabac.decode_bit(30) {
+                if !cabac.decode_bit(32) {
+                    MBType::B16x16(BMode::L0)
+                } else {
+                    MBType::B16x16(BMode::L1)
+                }
+            } else {
+                let idx = cabac.decode_bits(31, 32, 4);
+                match idx {
+                    0x0 => MBType::B16x16(BMode::Bi),
+                    0x1 => MBType::B16x8(BMode::L0, BMode::L0),
+                    0x2 => MBType::B8x16(BMode::L0, BMode::L0),
+                    0x3 => MBType::B16x8(BMode::L1, BMode::L1),
+                    0x4 => MBType::B8x16(BMode::L1, BMode::L1),
+                    0x5 => MBType::B16x8(BMode::L0, BMode::L1),
+                    0x6 => MBType::B8x16(BMode::L0, BMode::L1),
+                    0x7 => MBType::B16x8(BMode::L1, BMode::L0),
+                    0xE => MBType::B8x16(BMode::L1, BMode::L0),
+                    0xF => MBType::B8x8,
+                    0xD => decode_i_type_inter(cabac, 32),
+                    _ => {
+                        let idx = (idx - 8) * 2 + (cabac.decode_bit(32) as u8);
+                        match idx {
+                            0 => MBType::B16x8(BMode::L0, BMode::Bi),
+                            1 => MBType::B8x16(BMode::L0, BMode::Bi),
+                            2 => MBType::B16x8(BMode::L1, BMode::Bi),
+                            3 => MBType::B8x16(BMode::L1, BMode::Bi),
+                            4 => MBType::B16x8(BMode::Bi, BMode::L0),
+                            5 => MBType::B8x16(BMode::Bi, BMode::L0),
+                            6 => MBType::B16x8(BMode::Bi, BMode::L1),
+                            7 => MBType::B8x16(BMode::Bi, BMode::L1),
+                            8 => MBType::B16x8(BMode::Bi, BMode::Bi),
+                            _ => MBType::B8x16(BMode::Bi, BMode::Bi),
+                        }
+                    },
+                }
+            }
+        },
+    }
+}
+
+fn decode_sub_mb_type_cabac(cabac: &mut CABAC, slice_hdr: &SliceHeader) -> SubMBType {
+    match slice_hdr.slice_type {
+        SliceType::P | SliceType::SP => {
+            if cabac.decode_bit(21) {
+                SubMBType::P8x8
+            } else if !cabac.decode_bit(22) {
+                SubMBType::P8x4
+            } else if cabac.decode_bit(23) {
+                SubMBType::P4x8
+            } else {
+                SubMBType::P4x4
+            }
+        },
+        SliceType::B => {
+            if !cabac.decode_bit(36) {
+                SubMBType::Direct8x8
+            } else if !cabac.decode_bit(37) {
+                if !cabac.decode_bit(39) {
+                    SubMBType::B8x8(BMode::L0)
+                } else {
+                    SubMBType::B8x8(BMode::L1)
+                }
+            } else {
+                let idx = cabac.decode_bits(38, 39, 3);
+                match idx {
+                    0 => SubMBType::B8x8(BMode::Bi),
+                    1 => SubMBType::B8x4(BMode::L0),
+                    2 => SubMBType::B4x8(BMode::L0),
+                    3 => SubMBType::B8x4(BMode::L1),
+                    6 => SubMBType::B4x4(BMode::L1),
+                    7 => SubMBType::B4x4(BMode::Bi),
+                    _ => {
+                        let idx = (idx - 4) * 2 + (cabac.decode_bit(39) as u8);
+                        match idx {
+                            0 => SubMBType::B4x8(BMode::L1),
+                            1 => SubMBType::B8x4(BMode::Bi),
+                            2 => SubMBType::B4x8(BMode::Bi),
+                            _ => SubMBType::B4x4(BMode::L0),
+                        }
+                    },
+                }
+            }
+        },
+        _ => unreachable!(),
+    }
+}
+
+fn decode_ref_idx(cabac: &mut CABAC, num_refs: usize, ctx: usize) -> PicRef {
+    if num_refs == 1 {
+        return ZERO_REF;
+    }
+    if !cabac.decode_bit(54 + ctx) {
+        ZERO_REF
+    } else if !cabac.decode_bit(54 + 4) {
+        PicRef::new(1)
+    } else {
+        let mut idx = 2;
+        while cabac.decode_bit(54 + 5) && idx < 32 {
+            idx += 1;
+        }
+        if idx < num_refs {
+            PicRef::new(idx as u8)
+        } else {
+            INVALID_REF
+        }
+    }
+}
+
+fn decode_mv_component(cabac: &mut CABAC, base: usize, ctx: usize) -> i16 {
+    if !cabac.decode_bit(base + ctx) {
+        0
+    } else {
+        let mut val = 1;
+        while val < 9 && cabac.decode_bit(base + (2 + val).min(6)) {
+            val += 1;
+        }
+        if val >= 9 {
+            let mut pfx = 3;
+            while pfx < 16 && cabac.decode_bypass() {
+                val += 1 << pfx;
+                pfx += 1;
+            }
+            val += cabac.decode_bypass_bits(pfx) as usize;
+        }
+        if val == 0 || !cabac.decode_bypass() {
+            val as i16
+        } else {
+            -(val as i16)
+        }
+    }
+}
+
+fn decode_mv(cabac: &mut CABAC, ctx0: usize, ctx1: usize) -> MV {
+    let x = decode_mv_component(cabac, 40, ctx0);
+    let y = decode_mv_component(cabac, 47, ctx1);
+    MV{ x, y }
+}
+
+pub fn decode_mb_pred_cabac(cabac: &mut CABAC, slice_hdr: &SliceHeader, mb_type: MBType, sstate: &mut SliceState, mb_info: &mut CurrentMBInfo) {
+    mb_info.mb_type = mb_type;
+    let num_l0 = slice_hdr.num_ref_idx_l0_active;
+    let num_l1 = slice_hdr.num_ref_idx_l1_active;
+    sstate.reset_mb_mv();
+    match mb_type {
+        MBType::Intra4x4 => {
+            for &(x, y) in I4X4_SCAN.iter() {
+                let x = x as usize;
+                let y = y as usize;
+                let top_pred  = sstate.get_top_blk4(x + y * 4).ipred;
+                let left_pred = sstate.get_left_blk4(x + y * 4).ipred;
+
+                let top_idx = top_pred.into_pred_idx();
+                let left_idx = left_pred.into_pred_idx();
+                let pred_mode = top_idx.min(left_idx);
+                let mut pred_mode = if pred_mode != -1 { pred_mode as u8 } else { 2 };
+
+                if !cabac.decode_bit(68) {
+                    let m0 = cabac.decode_bit(69) as u8;
+                    let m1 = cabac.decode_bit(69) as u8;
+                    let m2 = cabac.decode_bit(69) as u8;
+                    let new_mode = (m2 << 2) | (m1 << 1) | m0;
+                    pred_mode = if new_mode >= pred_mode {
+                            new_mode + 1
+                        } else { new_mode };
+                }
+                mb_info.ipred[x + y * 4] = pred_mode.into();
+                sstate.get_cur_blk4(x + y * 4).ipred = (pred_mode as u8).into();
+            }
+            let mut ctx = 0;
+            if sstate.get_left_mb().cmode != 0 {
+                ctx += 1;
+            }
+            if sstate.get_top_mb().cmode != 0 {
+                ctx += 1;
+            }
+            mb_info.chroma_ipred = if !cabac.decode_bit(64 + ctx) {
+                    0
+                } else if !cabac.decode_bit(67) {
+                    1
+                } else if !cabac.decode_bit(67) {
+                    2
+                } else {
+                    3
+                };
+        },
+        MBType::Intra8x8 => {
+            for part in 0..4 {
+                let blk4 = (part & 1) * 2 + (part & 2) * 4;
+                let top_pred  = sstate.get_top_blk4(blk4).ipred;
+                let left_pred = sstate.get_left_blk4(blk4).ipred;
+
+                let top_idx = top_pred.into_pred_idx();
+                let left_idx = left_pred.into_pred_idx();
+                let pred_mode = top_idx.min(left_idx);
+                let mut pred_mode = if pred_mode != -1 { pred_mode as u8 } else { 2 };
+                if !cabac.decode_bit(68) {
+                    let m0 = cabac.decode_bit(69) as u8;
+                    let m1 = cabac.decode_bit(69) as u8;
+                    let m2 = cabac.decode_bit(69) as u8;
+                    let new_mode = (m2 << 2) | (m1 << 1) | m0;
+                    pred_mode = if new_mode >= pred_mode {
+                            new_mode + 1
+                        } else { new_mode };
+                }
+                mb_info.ipred[blk4]     = pred_mode.into();
+                mb_info.ipred[blk4 + 1] = pred_mode.into();
+                mb_info.ipred[blk4 + 4] = pred_mode.into();
+                mb_info.ipred[blk4 + 5] = pred_mode.into();
+                sstate.get_cur_blk4(blk4).ipred = (pred_mode as u8).into();
+                sstate.get_cur_blk4(blk4 + 1).ipred = (pred_mode as u8).into();
+                sstate.get_cur_blk4(blk4 + 4).ipred = (pred_mode as u8).into();
+                sstate.get_cur_blk4(blk4 + 5).ipred = (pred_mode as u8).into();
+            }
+            let mut ctx = 0;
+            if sstate.get_left_mb().cmode != 0 {
+                ctx += 1;
+            }
+            if sstate.get_top_mb().cmode != 0 {
+                ctx += 1;
+            }
+            mb_info.chroma_ipred = if !cabac.decode_bit(64 + ctx) {
+                    0
+                } else if !cabac.decode_bit(67) {
+                    1
+                } else if !cabac.decode_bit(67) {
+                    2
+                } else {
+                    3
+                };
+        },
+        MBType::Intra16x16(_ipred, _, _) => {
+            let mut ctx = 0;
+            if sstate.get_left_mb().cmode != 0 {
+                ctx += 1;
+            }
+            if sstate.get_top_mb().cmode != 0 {
+                ctx += 1;
+            }
+            mb_info.chroma_ipred = if !cabac.decode_bit(64 + ctx) {
+                    0
+                } else if !cabac.decode_bit(67) {
+                    1
+                } else if !cabac.decode_bit(67) {
+                    2
+                } else {
+                    3
+                };
+        },
+        MBType::P16x16 | MBType::P16x8 | MBType::P8x16 => {
+            let num_subparts = mb_type.num_parts();
+            let (pw, ph) = mb_type.size();
+            let mut xoff = 0;
+            let mut yoff = 0;
+            for i in 0..num_subparts {
+                let ctx = sstate.get_mv_ref_ctx(xoff, yoff, 0);
+                let ref_idx = decode_ref_idx(cabac, num_l0, ctx);
+                mb_info.ref_l0[i] = ref_idx;
+                sstate.fill_ref(xoff, yoff, pw, ph, 0, ref_idx);
+                xoff += pw;
+                if xoff == 16 {
+                    xoff = 0;
+                    yoff += ph;
+                }
+            }
+            let mut xoff = 0;
+            let mut yoff = 0;
+            for i in 0..num_subparts {
+                let (ctx0, ctx1) = sstate.get_mv_ctx(xoff, yoff, 0);
+                let mv = decode_mv(cabac, ctx0, ctx1);
+                mb_info.mv_l0[i] = mv;
+                sstate.fill_mvd(xoff, yoff, pw, ph, 0, mv);
+                xoff += pw;
+                if xoff == 16 {
+                    xoff = 0;
+                    yoff += ph;
+                }
+            }
+        },
+        MBType::B16x16(mode) => {
+            if mode != BMode::L1 {
+                let ctx = sstate.get_mv_ref_ctx(0, 0, 0);
+                let ref_idx = decode_ref_idx(cabac, num_l0, ctx);
+                mb_info.ref_l0[0] = ref_idx;
+                sstate.fill_ref(0, 0, 16, 16, 0, ref_idx);
+            }
+            if mode != BMode::L0 {
+                let ctx = sstate.get_mv_ref_ctx(0, 0, 1);
+                let ref_idx = decode_ref_idx(cabac, num_l1, ctx);
+                mb_info.ref_l1[0] = ref_idx;
+                sstate.fill_ref(0, 0, 16, 16, 1, ref_idx);
+            }
+            if mode != BMode::L1 {
+                let (ctx0, ctx1) = sstate.get_mv_ctx(0, 0, 0);
+                let mv = decode_mv(cabac, ctx0, ctx1);
+                mb_info.mv_l0[0] = mv;
+                sstate.fill_mvd(0, 0, 16, 16, 0, mv);
+            }
+            if mode != BMode::L0 {
+                let (ctx0, ctx1) = sstate.get_mv_ctx(0, 0, 1);
+                let mv = decode_mv(cabac, ctx0, ctx1);
+                mb_info.mv_l1[0] = mv;
+                sstate.fill_mvd(0, 0, 16, 16, 1, mv);
+            }
+        },
+        MBType::B16x8(mode0, mode1) | MBType::B8x16(mode0, mode1) => {
+            let (pw, ph) = mb_info.mb_type.size();
+            let (px, py) = (pw & 8, ph & 8);
+            if mode0 != BMode::L1 {
+                let ctx = sstate.get_mv_ref_ctx(0, 0, 0);
+                let ref_idx = decode_ref_idx(cabac, num_l0, ctx);
+                mb_info.ref_l0[0] = ref_idx;
+                sstate.fill_ref(0, 0, pw, ph, 0, ref_idx);
+            }
+            if mode1 != BMode::L1 {
+                let ctx = sstate.get_mv_ref_ctx(pw & 8, ph & 8, 0);
+                let ref_idx = decode_ref_idx(cabac, num_l0, ctx);
+                mb_info.ref_l0[1] = ref_idx;
+                sstate.fill_ref(px, py, pw, ph, 0, ref_idx);
+            }
+            if mode0 != BMode::L0 {
+                let ctx = sstate.get_mv_ref_ctx(0, 0, 1);
+                let ref_idx = decode_ref_idx(cabac, num_l1, ctx);
+                mb_info.ref_l1[0] = ref_idx;
+                sstate.fill_ref(0, 0, pw, ph, 1, ref_idx);
+            }
+            if mode1 != BMode::L0 {
+                let ctx = sstate.get_mv_ref_ctx(pw & 8, ph & 8, 1);
+                let ref_idx = decode_ref_idx(cabac, num_l1, ctx);
+                mb_info.ref_l1[1] = ref_idx;
+                sstate.fill_ref(px, py, pw, ph, 1, ref_idx);
+            }
+            if mode0 != BMode::L1 {
+                let (ctx0, ctx1) = sstate.get_mv_ctx(0, 0, 0);
+                let mv = decode_mv(cabac, ctx0, ctx1);
+                mb_info.mv_l0[0] = mv;
+                sstate.fill_mvd(0, 0, pw, ph, 0, mv);
+            }
+            if mode1 != BMode::L1 {
+                let (ctx0, ctx1) = sstate.get_mv_ctx(pw & 8, ph & 8, 0);
+                let mv = decode_mv(cabac, ctx0, ctx1);
+                mb_info.mv_l0[1] = mv;
+                sstate.fill_mvd(px, py, pw, ph, 0, mv);
+            }
+            if mode0 != BMode::L0 {
+                let (ctx0, ctx1) = sstate.get_mv_ctx(0, 0, 1);
+                let mv = decode_mv(cabac, ctx0, ctx1);
+                mb_info.mv_l1[0] = mv;
+                sstate.fill_mvd(0, 0, pw, ph, 1, mv);
+            }
+            if mode1 != BMode::L0 {
+                let (ctx0, ctx1) = sstate.get_mv_ctx(pw & 8, ph & 8, 1);
+                let mv = decode_mv(cabac, ctx0, ctx1);
+                mb_info.mv_l1[1] = mv;
+                sstate.fill_mvd(px, py, pw, ph, 1, mv);
+            }
+        },
+        MBType::P8x8 | MBType::B8x8 => {
+            for sub_type in mb_info.sub_mb_type.iter_mut() {
+                *sub_type = decode_sub_mb_type_cabac(cabac, slice_hdr);
+            }
+            let num_l = [num_l0, num_l1];
+            let dst_ref = [&mut mb_info.ref_l0, &mut mb_info.ref_l1];
+            for ref_l in 0..2 {
+                for spart in 0..4 {
+                    let stype = mb_info.sub_mb_type[spart];
+                    if stype != SubMBType::Direct8x8 && ((ref_l == 0 && !stype.is_l1()) || (ref_l == 1 && !stype.is_l0())) {
+                        let ctx = sstate.get_mv_ref_ctx((spart & 1) * 8, (spart & 2) * 4, ref_l);
+                        let ref_idx = decode_ref_idx(cabac, num_l[ref_l], ctx);
+                        dst_ref[ref_l][spart] = ref_idx;
+                        sstate.get_cur_blk8(spart).ref_idx[ref_l] = ref_idx;
+                    }
+                }
+            }
+            let dst_mv = [&mut mb_info.mv_l0, &mut mb_info.mv_l1];
+            for ref_l in 0..2 {
+                for spart in 0..4 {
+                    let stype = mb_info.sub_mb_type[spart];
+                    if stype == SubMBType::Direct8x8 || (ref_l == 0 && stype.is_l1()) || (ref_l == 1 && stype.is_l0()) {
+                        continue;
+                    }
+                    let (pw, ph) = stype.size();
+                    let mut xoff = (spart & 1) * 8;
+                    let mut yoff = (spart & 2) * 4;
+                    let num_sub = stype.num_parts();
+                    let orig_x = xoff;
+                    for i in 0..num_sub {
+                        let (ctx0, ctx1) = sstate.get_mv_ctx(xoff, yoff, ref_l);
+                        let mv = decode_mv(cabac, ctx0, ctx1);
+                        dst_mv[ref_l][spart * 4 + i] = mv;
+                        sstate.fill_mvd(xoff, yoff, pw, ph, ref_l, mv);
+                        xoff += pw;
+                        if xoff == orig_x + 8 {
+                            xoff -= 8;
+                            yoff += ph;
+                        }
+                    }
+                }
+            }
+        },
+        _ => {},
+    };
+}
+
+pub fn decode_cbp_cabac(cabac: &mut CABAC, sstate: &SliceState) -> (u8, u8) {
+    let mbt_a = sstate.get_left_mb().mb_type;
+    let mbt_b = sstate.get_top_mb().mb_type;
+    let left = if mbt_a == CompactMBType::None || mbt_a == CompactMBType::PCM {
+            0x3F
+        } else if !mbt_a.is_skip() {
+            sstate.get_left_mb().cbp
+        } else {
+            0
+        };
+    let top = if mbt_b == CompactMBType::None || mbt_b == CompactMBType::PCM {
+            0x3F
+        } else if !mbt_b.is_skip() {
+            sstate.get_top_mb().cbp
+        } else {
+            0
+        };
+
+    let cbp_ctx = if (left & 2) != 0 { 0 } else { 1 } + if (top & 4) != 0 { 0 } else { 2 };
+    let mut cbpy = cabac.decode_bit(73 + cbp_ctx) as u8;
+    let cbp_ctx = if cbpy != 0 { 0 } else { 1 } + if (top & 8) != 0 { 0 } else { 2 };
+    cbpy |= (cabac.decode_bit(73 + cbp_ctx) as u8) << 1;
+    let cbp_ctx = if (left & 8) != 0 { 0 } else { 1 } + if (cbpy & 1) != 0 { 0 } else { 2 };
+    cbpy |= (cabac.decode_bit(73 + cbp_ctx) as u8) << 2;
+    let cbp_ctx = if (cbpy & 4) != 0 { 0 } else { 1 } + if (cbpy & 2) != 0 { 0 } else { 2 };
+    cbpy |= (cabac.decode_bit(73 + cbp_ctx) as u8) << 3;
+
+    let left = if mbt_a == CompactMBType::PCM {
+            0x2F
+        } else if mbt_a == CompactMBType::None || !mbt_a.is_skip() {
+            sstate.get_left_mb().cbp
+        } else {
+            0
+        };
+    let top = if mbt_b == CompactMBType::PCM {
+            0x2F
+        } else if mbt_b == CompactMBType::None || !mbt_b.is_skip() {
+            sstate.get_top_mb().cbp
+        } else {
+            0
+        };
+    let cleft = left >> 4;
+    let ctop  = top  >> 4;
+    let cbp_ctx0 = if cleft != 0 { 1 } else { 0 } + if ctop != 0 { 2 } else { 0 };
+    let cbp_ctx1 = if cleft == 2 { 1 } else { 0 } + if ctop == 2 { 2 } else { 0 };
+    let cbpc = if !cabac.decode_bit(77 + cbp_ctx0) {
+            0
+        } else {
+            cabac.decode_bit(81 + cbp_ctx1) as u8 + 1
+        };
+
+    (cbpy, cbpc)
+}
+
+pub fn decode_mb_qp_delta_cabac(cabac: &mut CABAC, ctx: usize) -> i32 {
+    if !cabac.decode_bit(60 + ctx) {
+        0
+    } else if !cabac.decode_bit(62) {
+        1
+    } else {
+        let mut val = 0;
+        while val < 128 && cabac.decode_bit(63) {
+            val += 1;
+        }
+        if (val & 1) != 0 {
+            ((val >> 1) as i32) + 2
+        } else {
+            -((val >> 1) as i32) - 1
+        }
+    }
+}
+
+fn decode_block(cabac: &mut CABAC, coeffs: &mut [i16], cat: usize, ctx_off: usize) -> bool {
+    const CTX_BASE: [(usize, usize); 5] = [
+        (0, 0), (15, 10), (29, 20), (44, 30), (47, 39)
+    ];
+    let (flag_off, coef_off) = CTX_BASE[cat];
+    let scan: &[usize] = match coeffs.len() {
+            4 => &CHROMA_DC_SCAN,
+            15 => &ZIGZAG1,
+            16 => &ZIGZAG,
+            _ => unreachable!(),
+        };
+
+    let coded_block_flag = cabac.decode_bit(85 + ctx_off);
+    let mut coded = [false; 16];
+    if coded_block_flag {
+        let mut last_idx = coeffs.len() - 1;
+        for i in 0..coeffs.len() - 1 {
+            coded[i] = cabac.decode_bit(105 + flag_off + i); // or 277 for interlaced
+            if coded[i] {
+                let last = cabac.decode_bit(166 + flag_off + i); // or 338 for interlaced
+                if last {
+                    last_idx = i;
+                    break;
+                }
+            }
+        }
+        coded[last_idx] = true;
+        let mut coef_ctx = 0;
+        for i in (0..=last_idx).rev() {
+            if coded[i] {
+                let zero_ctx = if coef_ctx < 4 { coef_ctx + 1 } else { 0 };
+                coeffs[scan[i]] = if !cabac.decode_bit(227 + coef_off + zero_ctx) {
+                        if coef_ctx < 3 {
+                            coef_ctx += 1;
+                        }
+                        1
+                    } else {
+                        let cur_ctx = 227 + coef_off + (coef_ctx + 2).max(5);
+                        coef_ctx = (coef_ctx + 1).max(4).min(7);
+
+                        let mut coef = 2;
+                        while coef < 15 && cabac.decode_bit(cur_ctx) {
+                            coef += 1;
+                        }
+                        if coef == 15 {
+                            let mut pfx = 0;
+                            while pfx < 15 && cabac.decode_bypass() {
+                                pfx += 1;
+                            }
+                            let mut tail = 1;
+                            for _ in 0..pfx {
+                                tail = (tail << 1) + (cabac.decode_bypass() as i16);
+                            }
+                            coef + tail - 1
+                        } else {
+                            coef
+                        }
+                    };
+                if cabac.decode_bypass() {
+                    coeffs[scan[i]] = -coeffs[scan[i]];
+                }
+            }
+        }
+    }
+    coded_block_flag
+}
+
+fn decode_block8x8(cabac: &mut CABAC, coeffs: &mut [i16; 64], _cat: usize) {
+    const SIG_FLAG_MAP: [usize; 63] = [
+         0,  1,  2,  3,  4,  5,  5,  4,  4,  3,  3,  4,  4,  4,  5,  5,
+         4,  4,  4,  4,  3,  3,  6,  7,  7,  7,  8,  9, 10,  9,  8,  7,
+         7,  6, 11, 12, 13, 11,  6,  7,  8,  9, 14, 10,  9,  8,  6, 11,
+        12, 13, 11,  6,  9, 14, 10,  9, 11, 12, 13, 11, 14, 10, 12
+    ];
+    const LAST_SIG_FLAG_MAP: [usize; 63] = [
+        0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+        3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
+        5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8
+    ];
+    let (flag_off, coef_off) = (0, 0);
+    let scan = &ZIGZAG8X8;
+
+    let mut coded = [false; 64];
+    let mut last_idx = coeffs.len() - 1;
+    for i in 0..coeffs.len() - 1 {
+        coded[i] = cabac.decode_bit(402 + flag_off + SIG_FLAG_MAP[i]);
+        if coded[i] {
+            let last = cabac.decode_bit(417 + flag_off + LAST_SIG_FLAG_MAP[i]);
+            if last {
+                last_idx = i;
+                break;
+            }
+        }
+    }
+    coded[last_idx] = true;
+    let mut coef_ctx = 0;
+    for i in (0..=last_idx).rev() {
+        if coded[i] {
+            let zero_ctx = if coef_ctx < 4 { coef_ctx + 1 } else { 0 };
+            coeffs[scan[i]] = if !cabac.decode_bit(426 + coef_off + zero_ctx) {
+                    if coef_ctx < 3 {
+                        coef_ctx += 1;
+                    }
+                    1
+                } else {
+                    let cur_ctx = 426 + coef_off + (coef_ctx + 2).max(5);
+                    coef_ctx = (coef_ctx + 1).max(4).min(7);
+
+                    let mut coef = 2;
+                    while coef < 15 && cabac.decode_bit(cur_ctx) {
+                        coef += 1;
+                    }
+                    if coef == 15 {
+                        let mut pfx = 0;
+                        while pfx < 15 && cabac.decode_bypass() {
+                            pfx += 1;
+                        }
+                        let mut tail = 1;
+                        for _ in 0..pfx {
+                            tail = (tail << 1) + (cabac.decode_bypass() as i16);
+                        }
+                        coef + tail - 1
+                    } else {
+                        coef
+                    }
+                };
+            if cabac.decode_bypass() {
+                coeffs[scan[i]] = -coeffs[scan[i]];
+            }
+        }
+    }
+}
+
+fn derive_ctx_off(sstate: &mut SliceState, cat: usize, blk_no: usize) -> usize {
+    let mbt   = sstate.get_cur_mb().mb_type;
+    let mut mbt_a = sstate.get_left_mb().mb_type;
+    let mut mbt_b = sstate.get_top_mb().mb_type;
+    let (trans_a, trans_b, mut cond_term_a, mut cond_term_b) = match cat {
+            0 => {
+                (mbt_a == CompactMBType::Intra16x16,
+                 mbt_b == CompactMBType::Intra16x16,
+                 (sstate.get_left_mb().coded_flags & 1) as usize,
+                 (sstate.get_top_mb().coded_flags  & 1) as usize)
+            },
+            1 | 2 => {
+                if (blk_no & 3) != 0 {
+                    mbt_a = mbt;
+                }
+                if blk_no >= 4 {
+                    mbt_b = mbt;
+                }
+                let nc_left = sstate.get_left_blk4(blk_no).ncoded;
+                let nc_top  = sstate.get_top_blk4(blk_no).ncoded;
+                (nc_left != 0,
+                 nc_top != 0,
+                 (nc_left != 0) as usize,
+                 (nc_top != 0) as usize)
+            },
+            3 => {
+                ((sstate.get_left_mb().cbp & 0x30) != 0,
+                 (sstate.get_top_mb().cbp & 0x30) != 0,
+                 ((sstate.get_left_mb().coded_flags & (1 << (blk_no + 1 + 16))) != 0) as usize,
+                 ((sstate.get_top_mb().coded_flags & (1 << (blk_no + 1 + 16))) != 0) as usize)
+            },
+            4 => {
+                let chroma = blk_no >> 2;
+                if (blk_no & 1) != 0 {
+                    mbt_a = mbt;
+                }
+                if (blk_no & 2) != 0 {
+                    mbt_b = mbt;
+                }
+                ((blk_no & 1) != 0 || (sstate.get_left_mb().cbp & 0x20) != 0,
+                 (blk_no & 2) != 0 || (sstate.get_top_mb().cbp & 0x20) != 0,
+                 (sstate.get_left_blk8(blk_no & 3).ncoded_c[chroma] != 0) as usize,
+                 (sstate.get_top_blk8(blk_no & 3).ncoded_c[chroma] != 0) as usize)
+            },
+            _ => unreachable!(),
+        };
+    /*let coded_no = match cat {
+            0     => 0,
+            1 | 2 => blk_no + 1,
+            3     => 1 + 16 + blk_no,
+            4     => 1 + 16 + 2 + blk_no,
+            _ => unreachable!(),
+        };*/
+
+    if mbt_a == CompactMBType::None && mbt.is_inter() {
+        cond_term_a = 0;
+    }
+    if !trans_a && mbt_a != CompactMBType::PCM {
+        cond_term_a = 0;
+    }
+    /*if mbt.is_intra() && pps.constrained_intra_pred && mbt_a.is_inter() && slice_partitioning {
+        cond_term_a = 0;
+    }*/
+    if (mbt_a == CompactMBType::PCM) || (mbt_a == CompactMBType::None && mbt.is_intra()) {
+        cond_term_a = 1;
+    }
+
+    if mbt_b == CompactMBType::None && mbt.is_inter() {
+        cond_term_b = 0;
+    }
+    if !trans_b && mbt_b != CompactMBType::PCM {
+        cond_term_b = 0;
+    }
+    /*if mbt.is_intra() && pps.constrained_intra_pred && mbt_b.is_inter() && slice_partitioning {
+        cond_term_b = 0;
+    }*/
+    if (mbt_b == CompactMBType::PCM) || (mbt_b == CompactMBType::None && mbt.is_intra()) {
+        cond_term_b = 1;
+    }
+
+    cat * 4 + cond_term_b * 2 + cond_term_a
+}
+
+pub fn decode_residual_cabac(cabac: &mut CABAC, sstate: &mut SliceState, mb_info: &mut CurrentMBInfo) {
+    sstate.get_cur_mb().mb_type = mb_info.mb_type.into();
+    let mut coded_flags = 0;
+    if mb_info.mb_type.is_intra16x16() {
+        let off = derive_ctx_off(sstate, 0, 0);
+        let coded = decode_block(cabac, &mut mb_info.coeffs[24], 0, off);
+        mb_info.coded[24] = coded;
+        if coded {
+            coded_flags |= 1;
+        }
+    }
+    if !mb_info.transform_size_8x8 {
+        for blk8 in 0..4 {
+            if (mb_info.cbpy & (1 << blk8)) != 0 {
+                for blk4 in 0..4 {
+                    let blk_no = (blk8 & 1) * 2 + (blk8 & 2) * 4 + (blk4 & 1) + (blk4 & 2) * 2;
+                    let coded = if mb_info.mb_type.is_intra16x16() {
+                            let off = derive_ctx_off(sstate, 1, blk_no);
+                            decode_block(cabac, &mut mb_info.coeffs[blk_no][1..], 1, off)
+                        } else {
+                            let off = derive_ctx_off(sstate, 2, blk_no);
+                            decode_block(cabac, &mut mb_info.coeffs[blk_no], 2, off)
+                        };
+                    sstate.get_cur_blk4(blk_no).ncoded = coded as u8;
+                    mb_info.coded[blk_no] = coded;
+                    if coded {
+                        coded_flags |= 1 << (1 + blk_no);
+                    }
+                }
+            }
+        }
+    } else {
+        for blk8 in 0..4 {
+            if (mb_info.cbpy & (1 << blk8)) != 0 {
+                let blk4 = (blk8 & 1) * 2 + (blk8 & 2) * 4;
+                decode_block8x8(cabac, &mut mb_info.coeffs8x8[blk8].coeffs, 5);
+                coded_flags |= 0x33 << blk4;
+                mb_info.coded[blk4]     = true;
+                mb_info.coded[blk4 + 1] = true;
+                mb_info.coded[blk4 + 4] = true;
+                mb_info.coded[blk4 + 5] = true;
+                sstate.get_cur_blk4(blk4).ncoded     = 1;
+                sstate.get_cur_blk4(blk4 + 1).ncoded = 1;
+                sstate.get_cur_blk4(blk4 + 4).ncoded = 1;
+                sstate.get_cur_blk4(blk4 + 5).ncoded = 1;
+            }
+        }
+    }
+    for chroma in 0..2 {
+        if (mb_info.cbpc & 3) != 0 {
+            let off = derive_ctx_off(sstate, 3, chroma);
+            let coded = decode_block(cabac, &mut mb_info.chroma_dc[chroma], 3, off);
+            if coded {
+                coded_flags |= 1 << (16 + 1 + chroma);
+            }
+        }
+    }
+    for chroma in 0..2 {
+        if (mb_info.cbpc & 2) != 0 {
+            for blk4 in 0..4 {
+                let blk_no = 16 + chroma * 4 + blk4;
+                let off = derive_ctx_off(sstate, 4, blk_no - 16);
+                let coded = decode_block(cabac, &mut mb_info.coeffs[blk_no][1..], 4, off);
+                sstate.get_cur_blk8(blk4).ncoded_c[chroma] = coded as u8;
+                mb_info.coded[blk_no] = coded;
+                if coded {
+                    coded_flags |= 1 << (1 + 2 + blk_no);
+                }
+            }
+        }
+    }
+    sstate.get_cur_mb().coded_flags = coded_flags;
+}
diff --git a/nihav-itu/src/codecs/h264/cabac_coder.rs b/nihav-itu/src/codecs/h264/cabac_coder.rs
new file mode 100644 (file)
index 0000000..453ebc9
--- /dev/null
@@ -0,0 +1,1307 @@
+use nihav_core::codecs::{DecoderResult, DecoderError};
+use super::slice::SliceType;
+
+const NUM_CABAC_CONTEXTS: usize = 1024;
+
+pub struct CABAC<'a> {
+    pub src:    &'a [u8],
+    pub pos:    usize,
+    states: [u8; NUM_CABAC_CONTEXTS], // top bit - MPS, the rest is state index
+
+    cod_range:  u16,
+    cod_offset: u16,
+    bitbuf:     u16,
+    bits:       u8,
+}
+
+
+impl<'a> CABAC<'a> {
+    fn calc_state(qp: u8, m: i8, n: i8) -> u8 {
+        let pre_ctx_state = (((i16::from(m) * i16::from(qp)) >> 4) + i16::from(n)).max(1).min(126) as u8;
+        if pre_ctx_state < 64 {
+            63 - pre_ctx_state
+        } else {
+            (pre_ctx_state - 64) | 0x80
+        }
+    }
+    fn calc_range(qp: u8, idx: usize, states: &mut [u8], start: usize, end: usize) {
+        for (state, &mn) in states[start..=end].iter_mut().zip(CTX_PARAMS[idx][start..=end].iter()) {
+            *state = Self::calc_state(qp, mn[0], mn[1]);
+        }
+    }
+    pub fn new(src: &'a [u8], slice_type: SliceType, slice_qp: u8, cabac_init_idc: usize) -> DecoderResult<Self> {
+        let mut states = [0; NUM_CABAC_CONTEXTS];
+        let idx = if slice_type.is_intra() { 0 } else { cabac_init_idc + 1 };
+        Self::calc_range(slice_qp, idx, &mut states, 60, 63);
+        Self::calc_range(slice_qp, idx, &mut states, 64, 67);
+        Self::calc_range(slice_qp, idx, &mut states, 68, 68);
+        Self::calc_range(slice_qp, idx, &mut states, 69, 69);
+        Self::calc_range(slice_qp, idx, &mut states, 70, 72);
+        Self::calc_range(slice_qp, idx, &mut states, 73, 76);
+        Self::calc_range(slice_qp, idx, &mut states, 77, 84);
+        Self::calc_range(slice_qp, idx, &mut states, 85, 104);
+        Self::calc_range(slice_qp, idx, &mut states, 105, 165);
+        Self::calc_range(slice_qp, idx, &mut states, 166, 226);
+        Self::calc_range(slice_qp, idx, &mut states, 227, 275);
+        Self::calc_range(slice_qp, idx, &mut states, 277, 337);
+        Self::calc_range(slice_qp, idx, &mut states, 338, 398);
+        Self::calc_range(slice_qp, idx, &mut states, 399, 401);
+        Self::calc_range(slice_qp, idx, &mut states, 402, 416);
+        Self::calc_range(slice_qp, idx, &mut states, 417, 425);
+        Self::calc_range(slice_qp, idx, &mut states, 426, 435);
+        match slice_type {
+            SliceType::I => {
+                Self::calc_range(slice_qp, idx, &mut states, 3, 10);
+            },
+            SliceType::SI => {
+                Self::calc_range(slice_qp, idx, &mut states, 0, 10);
+            },
+            SliceType::P | SliceType::SP => {
+                Self::calc_range(slice_qp, idx, &mut states, 11, 13);
+                Self::calc_range(slice_qp, idx, &mut states, 14, 20);
+                Self::calc_range(slice_qp, idx, &mut states, 21, 23);
+                Self::calc_range(slice_qp, idx, &mut states, 40, 46);
+                Self::calc_range(slice_qp, idx, &mut states, 47, 53);
+                Self::calc_range(slice_qp, idx, &mut states, 54, 59);
+            },
+            SliceType::B => {
+                Self::calc_range(slice_qp, idx, &mut states, 24, 26);
+                Self::calc_range(slice_qp, idx, &mut states, 27, 35);
+                Self::calc_range(slice_qp, idx, &mut states, 36, 39);
+                Self::calc_range(slice_qp, idx, &mut states, 40, 46);
+                Self::calc_range(slice_qp, idx, &mut states, 47, 53);
+                Self::calc_range(slice_qp, idx, &mut states, 54, 59);
+            },
+        }
+
+        let mut ctx = Self {
+                src,
+                pos:        0,
+                states,
+                cod_range:  0,
+                cod_offset: 0,
+                bitbuf:     0,
+                bits:       0,
+            };
+        ctx.reinit()?;
+        Ok(ctx)
+    }
+    pub fn reinit(&mut self) -> DecoderResult<()> {
+        self.bitbuf = 0;
+        self.bits   = 0;
+        self.refill();
+        self.refill();
+        self.cod_range  = 0x1FE;
+        self.cod_offset = self.bitbuf >> 7;
+        validate!(self.cod_offset < self.cod_range);
+        self.bitbuf <<= 9;
+        self.bits    -= 9;
+        Ok(())
+    }
+    pub fn decode_bypass(&mut self) -> bool {
+        if self.bits == 0 {
+            self.refill();
+        }
+        self.cod_offset <<= 1;
+        self.cod_offset |= self.bitbuf >> 15;
+        self.bitbuf <<= 1;
+        self.bits    -= 1;
+        if self.cod_offset >= self.cod_range {
+            self.cod_offset -= self.cod_range;
+            true
+        } else {
+            false
+        }
+    }
+    pub fn decode_bypass_bits(&mut self, nbits: u8) -> u32 {
+        let mut val = 0;
+        for _ in 0..nbits {
+            val = (val << 1) | (self.decode_bypass() as u32)
+        }
+        val
+    }
+    pub fn decode_terminate(&mut self) -> bool {
+        self.cod_range -= 2;
+        if self.cod_offset >= self.cod_range {
+            true
+        } else {
+            self.renorm();
+            false
+        }
+    }
+    pub fn decode_bit(&mut self, idx: usize) -> bool {
+        let mut val_mps = (self.states[idx] & 0x80) != 0;
+        let state_idx = (self.states[idx] & 0x7F) as usize;
+        let range_idx = ((self.cod_range >> 6) & 3) as usize;
+        let range_lps = u16::from(RANGE_TBL_LPS[range_idx + state_idx * 4]);
+        self.cod_range -= range_lps;
+        let bit = if self.cod_offset >= self.cod_range {
+                self.cod_offset -= self.cod_range;
+                self.cod_range   = range_lps;
+                !val_mps
+            } else {
+                val_mps
+            };
+        self.states[idx] = if bit == val_mps {
+                TRANS_IDX_MPS[state_idx]
+            } else {
+                if state_idx == 0 {
+                    val_mps = !val_mps;
+                }
+                TRANS_IDX_LPS[state_idx]
+            } + (if val_mps { 0x80 } else { 0 });
+        self.renorm();
+        bit
+    }
+    pub fn decode_bits(&mut self, mut start: usize, maxidx: usize, len: usize) -> u8 {
+        let mut val = 0;
+        for _ in 0..len {
+            val <<= 1;
+            if self.decode_bit(start) {
+                val |= 1;
+            }
+            if start < maxidx {
+                start += 1;
+            }
+        }
+        val
+    }
+    pub fn decode_012(&mut self, start: usize) -> u8 {
+        if !self.decode_bit(start) {
+            0
+        } else {
+            self.decode_bit(start + 1) as u8 + 1
+        }
+    }
+    fn refill(&mut self) {
+        if self.pos < self.src.len() {
+            self.bitbuf |= u16::from(self.src[self.pos]) << (8 - self.bits);
+            self.pos += 1;
+        }
+        self.bits += 8;
+    }
+    fn renorm(&mut self) {
+        let shift = (self.cod_range.leading_zeros() - 7) as u8;
+        if shift > 0 {
+            if self.bits < shift {
+                self.refill();
+            }
+            self.cod_range  <<= shift;
+            self.cod_offset <<= shift;
+            self.cod_offset  |= u16::from(self.bitbuf >> (16 - shift));
+            self.bitbuf <<= shift;
+            self.bits -= shift;
+        }
+    }
+}
+
+const RANGE_TBL_LPS: [u8; 64 * 4] = [
+    128, 176, 208, 240,
+    128, 167, 197, 227,
+    128, 158, 187, 216,
+    123, 150, 178, 205,
+    116, 142, 169, 195,
+    111, 135, 160, 185,
+    105, 128, 152, 175,
+    100, 122, 144, 166,
+     95, 116, 137, 158,
+     90, 110, 130, 150,
+     85, 104, 123, 142,
+     81,  99, 117, 135,
+     77,  94, 111, 128,
+     73,  89, 105, 122,
+     69,  85, 100, 116,
+     66,  80,  95, 110,
+     62,  76,  90, 104,
+     59,  72,  86,  99,
+     56,  69,  81,  94,
+     53,  65,  77,  89,
+     51,  62,  73,  85,
+     48,  59,  69,  80,
+     46,  56,  66,  76,
+     43,  53,  63,  72,
+     41,  50,  59,  69,
+     39,  48,  56,  65,
+     37,  45,  54,  62,
+     35,  43,  51,  59,
+     33,  41,  48,  56,
+     32,  39,  46,  53,
+     30,  37,  43,  50,
+     29,  35,  41,  48,
+     27,  33,  39,  45,
+     26,  31,  37,  43,
+     24,  30,  35,  41,
+     23,  28,  33,  39,
+     22,  27,  32,  37,
+     21,  26,  30,  35,
+     20,  24,  29,  33,
+     19,  23,  27,  31,
+     18,  22,  26,  30,
+     17,  21,  25,  28,
+     16,  20,  23,  27,
+     15,  19,  22,  25,
+     14,  18,  21,  24,
+     14,  17,  20,  23,
+     13,  16,  19,  22,
+     12,  15,  18,  21,
+     12,  14,  17,  20,
+     11,  14,  16,  19,
+     11,  13,  15,  18,
+     10,  12,  15,  17,
+     10,  12,  14,  16,
+      9,  11,  13,  15,
+      9,  11,  12,  14,
+      8,  10,  12,  14,
+      8,   9,  11,  13,
+      7,   9,  11,  12,
+      7,   9,  10,  12,
+      7,   8,  10,  11,
+      6,   8,   9,  11,
+      6,   7,   9,  10,
+      6,   7,   8,   9,
+      2,   2,   2,   2
+];
+const TRANS_IDX_MPS: [u8; 64] = [
+     1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
+    17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
+    33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+    49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 62, 63
+];
+const TRANS_IDX_LPS: [u8; 64] = [
+     0,  0,  1,  2,  2,  4,  4,  5,  6,  7,  8,  9,  9, 11, 11, 12,
+    13, 13, 15, 15, 16, 16, 18, 18, 19, 19, 21, 21, 22, 22, 23, 24,
+    24, 25, 26, 26, 27, 27, 28, 29, 29, 30, 30, 30, 31, 32, 32, 33,
+    33, 33, 34, 34, 35, 35, 35, 36, 36, 36, 37, 37, 37, 38, 38, 63
+];
+
+const CTX_PARAMS: [[[i8; 2]; NUM_CABAC_CONTEXTS]; 4] = [
+  [
+    [ 20, -15], [  2,  54], [  3,  74], [ 20, -15],
+    [  2,  54], [  3,  74], [-28, 127], [-23, 104],
+    [ -6,  53], [ -1,  54], [  7,  51], [  0,   0],
+    [  0,   0], [  0,   0], [  0,   0], [  0,   0],
+    [  0,   0], [  0,   0], [  0,   0], [  0,   0],
+    [  0,   0], [  0,   0], [  0,   0], [  0,   0],
+    [  0,   0], [  0,   0], [  0,   0], [  0,   0],
+    [  0,   0], [  0,   0], [  0,   0], [  0,   0],
+    [  0,   0], [  0,   0], [  0,   0], [  0,   0],
+    [  0,   0], [  0,   0], [  0,   0], [  0,   0],
+    [  0,   0], [  0,   0], [  0,   0], [  0,   0],
+    [  0,   0], [  0,   0], [  0,   0], [  0,   0],
+    [  0,   0], [  0,   0], [  0,   0], [  0,   0],
+    [  0,   0], [  0,   0], [  0,   0], [  0,   0],
+    [  0,   0], [  0,   0], [  0,   0], [  0,   0],
+    [  0,  41], [  0,  63], [  0,  63], [  0,  63],
+    [ -9,  83], [  4,  86], [  0,  97], [ -7,  72],
+    [ 13,  41], [  3,  62], [  0,  11], [  1,  55],
+    [  0,  69], [-17, 127], [-13, 102], [  0,  82],
+    [ -7,  74], [-21, 107], [-27, 127], [-31, 127],
+    [-24, 127], [-18,  95], [-27, 127], [-21, 114],
+    [-30, 127], [-17, 123], [-12, 115], [-16, 122],
+    [-11, 115], [-12,  63], [ -2,  68], [-15,  84],
+    [-13, 104], [ -3,  70], [ -8,  93], [-10,  90],
+    [-30, 127], [ -1,  74], [ -6,  97], [ -7,  91],
+    [-20, 127], [ -4,  56], [ -5,  82], [ -7,  76],
+    [-22, 125], [ -7,  93], [-11,  87], [ -3,  77],
+    [ -5,  71], [ -4,  63], [ -4,  68], [-12,  84],
+    [ -7,  62], [ -7,  65], [  8,  61], [  5,  56],
+    [ -2,  66], [  1,  64], [  0,  61], [ -2,  78],
+    [  1,  50], [  7,  52], [ 10,  35], [  0,  44],
+    [ 11,  38], [  1,  45], [  0,  46], [  5,  44],
+    [ 31,  17], [  1,  51], [  7,  50], [ 28,  19],
+    [ 16,  33], [ 14,  62], [-13, 108], [-15, 100],
+    [-13, 101], [-13,  91], [-12,  94], [-10,  88],
+    [-16,  84], [-10,  86], [ -7,  83], [-13,  87],
+    [-19,  94], [  1,  70], [  0,  72], [ -5,  74],
+    [ 18,  59], [ -8, 102], [-15, 100], [  0,  95],
+    [ -4,  75], [  2,  72], [-11,  75], [ -3,  71],
+    [ 15,  46], [-13,  69], [  0,  62], [  0,  65],
+    [ 21,  37], [-15,  72], [  9,  57], [ 16,  54],
+    [  0,  62], [ 12,  72], [ 24,   0], [ 15,   9],
+    [  8,  25], [ 13,  18], [ 15,   9], [ 13,  19],
+    [ 10,  37], [ 12,  18], [  6,  29], [ 20,  33],
+    [ 15,  30], [  4,  45], [  1,  58], [  0,  62],
+    [  7,  61], [ 12,  38], [ 11,  45], [ 15,  39],
+    [ 11,  42], [ 13,  44], [ 16,  45], [ 12,  41],
+    [ 10,  49], [ 30,  34], [ 18,  42], [ 10,  55],
+    [ 17,  51], [ 17,  46], [  0,  89], [ 26, -19],
+    [ 22, -17], [ 26, -17], [ 30, -25], [ 28, -20],
+    [ 33, -23], [ 37, -27], [ 33, -23], [ 40, -28],
+    [ 38, -17], [ 33, -11], [ 40, -15], [ 41,  -6],
+    [ 38,   1], [ 41,  17], [ 30,  -6], [ 27,   3],
+    [ 26,  22], [ 37, -16], [ 35,  -4], [ 38,  -8],
+    [ 38,  -3], [ 37,   3], [ 38,   5], [ 42,   0],
+    [ 35,  16], [ 39,  22], [ 14,  48], [ 27,  37],
+    [ 21,  60], [ 12,  68], [  2,  97], [ -3,  71],
+    [ -6,  42], [ -5,  50], [ -3,  54], [ -2,  62],
+    [  0,  58], [  1,  63], [ -2,  72], [ -1,  74],
+    [ -9,  91], [ -5,  67], [ -5,  27], [ -3,  39],
+    [ -2,  44], [  0,  46], [-16,  64], [ -8,  68],
+    [-10,  78], [ -6,  77], [-10,  86], [-12,  92],
+    [-15,  55], [-10,  60], [ -6,  62], [ -4,  65],
+    [-12,  73], [ -8,  76], [ -7,  80], [ -9,  88],
+    [-17, 110], [-11,  97], [-20,  84], [-11,  79],
+    [ -6,  73], [ -4,  74], [-13,  86], [-13,  96],
+    [-11,  97], [-19, 117], [ -8,  78], [ -5,  33],
+    [ -4,  48], [ -2,  53], [ -3,  62], [-13,  71],
+    [-10,  79], [-12,  86], [-13,  90], [-14,  97],
+    [  0,   0], [ -6,  93], [ -6,  84], [ -8,  79],
+    [  0,  66], [ -1,  71], [  0,  62], [ -2,  60],
+    [ -2,  59], [ -5,  75], [ -3,  62], [ -4,  58],
+    [ -9,  66], [ -1,  79], [  0,  71], [  3,  68],
+    [ 10,  44], [ -7,  62], [ 15,  36], [ 14,  40],
+    [ 16,  27], [ 12,  29], [  1,  44], [ 20,  36],
+    [ 18,  32], [  5,  42], [  1,  48], [ 10,  62],
+    [ 17,  46], [  9,  64], [-12, 104], [-11,  97],
+    [-16,  96], [ -7,  88], [ -8,  85], [ -7,  85],
+    [ -9,  85], [-13,  88], [  4,  66], [ -3,  77],
+    [ -3,  76], [ -6,  76], [ 10,  58], [ -1,  76],
+    [ -1,  83], [ -7,  99], [-14,  95], [  2,  95],
+    [  0,  76], [ -5,  74], [  0,  70], [-11,  75],
+    [  1,  68], [  0,  65], [-14,  73], [  3,  62],
+    [  4,  62], [ -1,  68], [-13,  75], [ 11,  55],
+    [  5,  64], [ 12,  70], [ 15,   6], [  6,  19],
+    [  7,  16], [ 12,  14], [ 18,  13], [ 13,  11],
+    [ 13,  15], [ 15,  16], [ 12,  23], [ 13,  23],
+    [ 15,  20], [ 14,  26], [ 14,  44], [ 17,  40],
+    [ 17,  47], [ 24,  17], [ 21,  21], [ 25,  22],
+    [ 31,  27], [ 22,  29], [ 19,  35], [ 14,  50],
+    [ 10,  57], [  7,  63], [ -2,  77], [ -4,  82],
+    [ -3,  94], [  9,  69], [-12, 109], [ 36, -35],
+    [ 36, -34], [ 32, -26], [ 37, -30], [ 44, -32],
+    [ 34, -18], [ 34, -15], [ 40, -15], [ 33,  -7],
+    [ 35,  -5], [ 33,   0], [ 38,   2], [ 33,  13],
+    [ 23,  35], [ 13,  58], [ 29,  -3], [ 26,   0],
+    [ 22,  30], [ 31,  -7], [ 35, -15], [ 34,  -3],
+    [ 34,   3], [ 36,  -1], [ 34,   5], [ 32,  11],
+    [ 35,   5], [ 34,  12], [ 39,  11], [ 30,  29],
+    [ 34,  26], [ 29,  39], [ 19,  66], [ 31,  21],
+    [ 31,  31], [ 25,  50], [-17, 120], [-20, 112],
+    [-18, 114], [-11,  85], [-15,  92], [-14,  89],
+    [-26,  71], [-15,  81], [-14,  80], [  0,  68],
+    [-14,  70], [-24,  56], [-23,  68], [-24,  50],
+    [-11,  74], [ 23, -13], [ 26, -13], [ 40, -15],
+    [ 49, -14], [ 44,   3], [ 45,   6], [ 44,  34],
+    [ 33,  54], [ 19,  82], [ -3,  75], [ -1,  23],
+    [  1,  34], [  1,  43], [  0,  54], [ -2,  55],
+    [  0,  61], [  1,  64], [  0,  68], [ -9,  92],
+    [-14, 106], [-13,  97], [-15,  90], [-12,  90],
+    [-18,  88], [-10,  73], [ -9,  79], [-14,  86],
+    [-10,  73], [-10,  70], [-10,  69], [ -5,  66],
+    [ -9,  64], [ -5,  58], [  2,  59], [ 21, -10],
+    [ 24, -11], [ 28,  -8], [ 28,  -1], [ 29,   3],
+    [ 29,   9], [ 35,  20], [ 29,  36], [ 14,  67],
+    [-17, 123], [-12, 115], [-16, 122], [-11, 115],
+    [-12,  63], [ -2,  68], [-15,  84], [-13, 104],
+    [ -3,  70], [ -8,  93], [-10,  90], [-30, 127],
+    [-17, 123], [-12, 115], [-16, 122], [-11, 115],
+    [-12,  63], [ -2,  68], [-15,  84], [-13, 104],
+    [ -3,  70], [ -8,  93], [-10,  90], [-30, 127],
+    [ -7,  93], [-11,  87], [ -3,  77], [ -5,  71],
+    [ -4,  63], [ -4,  68], [-12,  84], [ -7,  62],
+    [ -7,  65], [  8,  61], [  5,  56], [ -2,  66],
+    [  1,  64], [  0,  61], [ -2,  78], [  1,  50],
+    [  7,  52], [ 10,  35], [  0,  44], [ 11,  38],
+    [  1,  45], [  0,  46], [  5,  44], [ 31,  17],
+    [  1,  51], [  7,  50], [ 28,  19], [ 16,  33],
+    [ 14,  62], [-13, 108], [-15, 100], [-13, 101],
+    [-13,  91], [-12,  94], [-10,  88], [-16,  84],
+    [-10,  86], [ -7,  83], [-13,  87], [-19,  94],
+    [  1,  70], [  0,  72], [ -5,  74], [ 18,  59],
+    [ -7,  93], [-11,  87], [ -3,  77], [ -5,  71],
+    [ -4,  63], [ -4,  68], [-12,  84], [ -7,  62],
+    [ -7,  65], [  8,  61], [  5,  56], [ -2,  66],
+    [  1,  64], [  0,  61], [ -2,  78], [  1,  50],
+    [  7,  52], [ 10,  35], [  0,  44], [ 11,  38],
+    [  1,  45], [  0,  46], [  5,  44], [ 31,  17],
+    [  1,  51], [  7,  50], [ 28,  19], [ 16,  33],
+    [ 14,  62], [-13, 108], [-15, 100], [-13, 101],
+    [-13,  91], [-12,  94], [-10,  88], [-16,  84],
+    [-10,  86], [ -7,  83], [-13,  87], [-19,  94],
+    [  1,  70], [  0,  72], [ -5,  74], [ 18,  59],
+    [ 24,   0], [ 15,   9], [  8,  25], [ 13,  18],
+    [ 15,   9], [ 13,  19], [ 10,  37], [ 12,  18],
+    [  6,  29], [ 20,  33], [ 15,  30], [  4,  45],
+    [  1,  58], [  0,  62], [  7,  61], [ 12,  38],
+    [ 11,  45], [ 15,  39], [ 11,  42], [ 13,  44],
+    [ 16,  45], [ 12,  41], [ 10,  49], [ 30,  34],
+    [ 18,  42], [ 10,  55], [ 17,  51], [ 17,  46],
+    [  0,  89], [ 26, -19], [ 22, -17], [ 26, -17],
+    [ 30, -25], [ 28, -20], [ 33, -23], [ 37, -27],
+    [ 33, -23], [ 40, -28], [ 38, -17], [ 33, -11],
+    [ 40, -15], [ 41,  -6], [ 38,   1], [ 41,  17],
+    [ 24,   0], [ 15,   9], [  8,  25], [ 13,  18],
+    [ 15,   9], [ 13,  19], [ 10,  37], [ 12,  18],
+    [  6,  29], [ 20,  33], [ 15,  30], [  4,  45],
+    [  1,  58], [  0,  62], [  7,  61], [ 12,  38],
+    [ 11,  45], [ 15,  39], [ 11,  42], [ 13,  44],
+    [ 16,  45], [ 12,  41], [ 10,  49], [ 30,  34],
+    [ 18,  42], [ 10,  55], [ 17,  51], [ 17,  46],
+    [  0,  89], [ 26, -19], [ 22, -17], [ 26, -17],
+    [ 30, -25], [ 28, -20], [ 33, -23], [ 37, -27],
+    [ 33, -23], [ 40, -28], [ 38, -17], [ 33, -11],
+    [ 40, -15], [ 41,  -6], [ 38,   1], [ 41,  17],
+    [-17, 120], [-20, 112], [-18, 114], [-11,  85],
+    [-15,  92], [-14,  89], [-26,  71], [-15,  81],
+    [-14,  80], [  0,  68], [-14,  70], [-24,  56],
+    [-23,  68], [-24,  50], [-11,  74], [-14, 106],
+    [-13,  97], [-15,  90], [-12,  90], [-18,  88],
+    [-10,  73], [ -9,  79], [-14,  86], [-10,  73],
+    [-10,  70], [-10,  69], [ -5,  66], [ -9,  64],
+    [ -5,  58], [  2,  59], [ 23, -13], [ 26, -13],
+    [ 40, -15], [ 49, -14], [ 44,   3], [ 45,   6],
+    [ 44,  34], [ 33,  54], [ 19,  82], [ 21, -10],
+    [ 24, -11], [ 28,  -8], [ 28,  -1], [ 29,   3],
+    [ 29,   9], [ 35,  20], [ 29,  36], [ 14,  67],
+    [ -3,  75], [ -1,  23], [  1,  34], [  1,  43],
+    [  0,  54], [ -2,  55], [  0,  61], [  1,  64],
+    [  0,  68], [ -9,  92], [-17, 120], [-20, 112],
+    [-18, 114], [-11,  85], [-15,  92], [-14,  89],
+    [-26,  71], [-15,  81], [-14,  80], [  0,  68],
+    [-14,  70], [-24,  56], [-23,  68], [-24,  50],
+    [-11,  74], [-14, 106], [-13,  97], [-15,  90],
+    [-12,  90], [-18,  88], [-10,  73], [ -9,  79],
+    [-14,  86], [-10,  73], [-10,  70], [-10,  69],
+    [ -5,  66], [ -9,  64], [ -5,  58], [  2,  59],
+    [ 23, -13], [ 26, -13], [ 40, -15], [ 49, -14],
+    [ 44,   3], [ 45,   6], [ 44,  34], [ 33,  54],
+    [ 19,  82], [ 21, -10], [ 24, -11], [ 28,  -8],
+    [ 28,  -1], [ 29,   3], [ 29,   9], [ 35,  20],
+    [ 29,  36], [ 14,  67], [ -3,  75], [ -1,  23],
+    [  1,  34], [  1,  43], [  0,  54], [ -2,  55],
+    [  0,  61], [  1,  64], [  0,  68], [ -9,  92],
+    [ -6,  93], [ -6,  84], [ -8,  79], [  0,  66],
+    [ -1,  71], [  0,  62], [ -2,  60], [ -2,  59],
+    [ -5,  75], [ -3,  62], [ -4,  58], [ -9,  66],
+    [ -1,  79], [  0,  71], [  3,  68], [ 10,  44],
+    [ -7,  62], [ 15,  36], [ 14,  40], [ 16,  27],
+    [ 12,  29], [  1,  44], [ 20,  36], [ 18,  32],
+    [  5,  42], [  1,  48], [ 10,  62], [ 17,  46],
+    [  9,  64], [-12, 104], [-11,  97], [-16,  96],
+    [ -7,  88], [ -8,  85], [ -7,  85], [ -9,  85],
+    [-13,  88], [  4,  66], [ -3,  77], [ -3,  76],
+    [ -6,  76], [ 10,  58], [ -1,  76], [ -1,  83],
+    [ -6,  93], [ -6,  84], [ -8,  79], [  0,  66],
+    [ -1,  71], [  0,  62], [ -2,  60], [ -2,  59],
+    [ -5,  75], [ -3,  62], [ -4,  58], [ -9,  66],
+    [ -1,  79], [  0,  71], [  3,  68], [ 10,  44],
+    [ -7,  62], [ 15,  36], [ 14,  40], [ 16,  27],
+    [ 12,  29], [  1,  44], [ 20,  36], [ 18,  32],
+    [  5,  42], [  1,  48], [ 10,  62], [ 17,  46],
+    [  9,  64], [-12, 104], [-11,  97], [-16,  96],
+    [ -7,  88], [ -8,  85], [ -7,  85], [ -9,  85],
+    [-13,  88], [  4,  66], [ -3,  77], [ -3,  76],
+    [ -6,  76], [ 10,  58], [ -1,  76], [ -1,  83],
+    [ 15,   6], [  6,  19], [  7,  16], [ 12,  14],
+    [ 18,  13], [ 13,  11], [ 13,  15], [ 15,  16],
+    [ 12,  23], [ 13,  23], [ 15,  20], [ 14,  26],
+    [ 14,  44], [ 17,  40], [ 17,  47], [ 24,  17],
+    [ 21,  21], [ 25,  22], [ 31,  27], [ 22,  29],
+    [ 19,  35], [ 14,  50], [ 10,  57], [  7,  63],
+    [ -2,  77], [ -4,  82], [ -3,  94], [  9,  69],
+    [-12, 109], [ 36, -35], [ 36, -34], [ 32, -26],
+    [ 37, -30], [ 44, -32], [ 34, -18], [ 34, -15],
+    [ 40, -15], [ 33,  -7], [ 35,  -5], [ 33,   0],
+    [ 38,   2], [ 33,  13], [ 23,  35], [ 13,  58],
+    [ 15,   6], [  6,  19], [  7,  16], [ 12,  14],
+    [ 18,  13], [ 13,  11], [ 13,  15], [ 15,  16],
+    [ 12,  23], [ 13,  23], [ 15,  20], [ 14,  26],
+    [ 14,  44], [ 17,  40], [ 17,  47], [ 24,  17],
+    [ 21,  21], [ 25,  22], [ 31,  27], [ 22,  29],
+    [ 19,  35], [ 14,  50], [ 10,  57], [  7,  63],
+    [ -2,  77], [ -4,  82], [ -3,  94], [  9,  69],
+    [-12, 109], [ 36, -35], [ 36, -34], [ 32, -26],
+    [ 37, -30], [ 44, -32], [ 34, -18], [ 34, -15],
+    [ 40, -15], [ 33,  -7], [ 35,  -5], [ 33,   0],
+    [ 38,   2], [ 33,  13], [ 23,  35], [ 13,  58],
+    [ -3,  71], [ -6,  42], [ -5,  50], [ -3,  54],
+    [ -2,  62], [  0,  58], [  1,  63], [ -2,  72],
+    [ -1,  74], [ -9,  91], [ -5,  67], [ -5,  27],
+    [ -3,  39], [ -2,  44], [  0,  46], [-16,  64],
+    [ -8,  68], [-10,  78], [ -6,  77], [-10,  86],
+    [-12,  92], [-15,  55], [-10,  60], [ -6,  62],
+    [ -4,  65], [-12,  73], [ -8,  76], [ -7,  80],
+    [ -9,  88], [-17, 110], [ -3,  71], [ -6,  42],
+    [ -5,  50], [ -3,  54], [ -2,  62], [  0,  58],
+    [  1,  63], [ -2,  72], [ -1,  74], [ -9,  91],
+    [ -5,  67], [ -5,  27], [ -3,  39], [ -2,  44],
+    [  0,  46], [-16,  64], [ -8,  68], [-10,  78],
+    [ -6,  77], [-10,  86], [-12,  92], [-15,  55],
+    [-10,  60], [ -6,  62], [ -4,  65], [-12,  73],
+    [ -8,  76], [ -7,  80], [ -9,  88], [-17, 110],
+    [ -3,  70], [ -8,  93], [-10,  90], [-30, 127],
+    [ -3,  70], [ -8,  93], [-10,  90], [-30, 127],
+    [ -3,  70], [ -8,  93], [-10,  90], [-30, 127]
+  ], [
+    [ 20, -15], [  2,  54], [  3,  74], [ 20, -15],
+    [  2,  54], [  3,  74], [-28, 127], [-23, 104],
+    [ -6,  53], [ -1,  54], [  7,  51], [ 23,  33],
+    [ 23,   2], [ 21,   0], [  1,   9], [  0,  49],
+    [-37, 118], [  5,  57], [-13,  78], [-11,  65],
+    [  1,  62], [ 12,  49], [ -4,  73], [ 17,  50],
+    [ 18,  64], [  9,  43], [ 29,   0], [ 26,  67],
+    [ 16,  90], [  9, 104], [-46, 127], [-20, 104],
+    [  1,  67], [-13,  78], [-11,  65], [  1,  62],
+    [ -6,  86], [-17,  95], [ -6,  61], [  9,  45],
+    [ -3,  69], [ -6,  81], [-11,  96], [  6,  55],
+    [  7,  67], [ -5,  86], [  2,  88], [  0,  58],
+    [ -3,  76], [-10,  94], [  5,  54], [  4,  69],
+    [ -3,  81], [  0,  88], [ -7,  67], [ -5,  74],
+    [ -4,  74], [ -5,  80], [ -7,  72], [  1,  58],
+    [  0,  41], [  0,  63], [  0,  63], [  0,  63],
+    [ -9,  83], [  4,  86], [  0,  97], [ -7,  72],
+    [ 13,  41], [  3,  62], [  0,  45], [ -4,  78],
+    [ -3,  96], [-27, 126], [-28,  98], [-25, 101],
+    [-23,  67], [-28,  82], [-20,  94], [-16,  83],
+    [-22, 110], [-21,  91], [-18, 102], [-13,  93],
+    [-29, 127], [ -7,  92], [ -5,  89], [ -7,  96],
+    [-13, 108], [ -3,  46], [ -1,  65], [ -1,  57],
+    [ -9,  93], [ -3,  74], [ -9,  92], [ -8,  87],
+    [-23, 126], [  5,  54], [  6,  60], [  6,  59],
+    [  6,  69], [ -1,  48], [  0,  68], [ -4,  69],
+    [ -8,  88], [ -2,  85], [ -6,  78], [ -1,  75],
+    [ -7,  77], [  2,  54], [  5,  50], [ -3,  68],
+    [  1,  50], [  6,  42], [ -4,  81], [  1,  63],
+    [ -4,  70], [  0,  67], [  2,  57], [ -2,  76],
+    [ 11,  35], [  4,  64], [  1,  61], [ 11,  35],
+    [ 18,  25], [ 12,  24], [ 13,  29], [ 13,  36],
+    [-10,  93], [ -7,  73], [ -2,  73], [ 13,  46],
+    [  9,  49], [ -7, 100], [  9,  53], [  2,  53],
+    [  5,  53], [ -2,  61], [  0,  56], [  0,  56],
+    [-13,  63], [ -5,  60], [ -1,  62], [  4,  57],
+    [ -6,  69], [  4,  57], [ 14,  39], [  4,  51],
+    [ 13,  68], [  3,  64], [  1,  61], [  9,  63],
+    [  7,  50], [ 16,  39], [  5,  44], [  4,  52],
+    [ 11,  48], [ -5,  60], [ -1,  59], [  0,  59],
+    [ 22,  33], [  5,  44], [ 14,  43], [ -1,  78],
+    [  0,  60], [  9,  69], [ 11,  28], [  2,  40],
+    [  3,  44], [  0,  49], [  0,  46], [  2,  44],
+    [  2,  51], [  0,  47], [  4,  39], [  2,  62],
+    [  6,  46], [  0,  54], [  3,  54], [  2,  58],
+    [  4,  63], [  6,  51], [  6,  57], [  7,  53],
+    [  6,  52], [  6,  55], [ 11,  45], [ 14,  36],
+    [  8,  53], [ -1,  82], [  7,  55], [ -3,  78],
+    [ 15,  46], [ 22,  31], [ -1,  84], [ 25,   7],
+    [ 30,  -7], [ 28,   3], [ 28,   4], [ 32,   0],
+    [ 34,  -1], [ 30,   6], [ 30,   6], [ 32,   9],
+    [ 31,  19], [ 26,  27], [ 26,  30], [ 37,  20],
+    [ 28,  34], [ 17,  70], [  1,  67], [  5,  59],
+    [  9,  67], [ 16,  30], [ 18,  32], [ 18,  35],
+    [ 22,  29], [ 24,  31], [ 23,  38], [ 18,  43],
+    [ 20,  41], [ 11,  63], [  9,  59], [  9,  64],
+    [ -1,  94], [ -2,  89], [ -9, 108], [ -6,  76],
+    [ -2,  44], [  0,  45], [  0,  52], [ -3,  64],
+    [ -2,  59], [ -4,  70], [ -4,  75], [ -8,  82],
+    [-17, 102], [ -9,  77], [  3,  24], [  0,  42],
+    [  0,  48], [  0,  55], [ -6,  59], [ -7,  71],
+    [-12,  83], [-11,  87], [-30, 119], [  1,  58],
+    [ -3,  29], [ -1,  36], [  1,  38], [  2,  43],
+    [ -6,  55], [  0,  58], [  0,  64], [ -3,  74],
+    [-10,  90], [  0,  70], [ -4,  29], [  5,  31],
+    [  7,  42], [  1,  59], [ -2,  58], [ -3,  72],
+    [ -3,  81], [-11,  97], [  0,  58], [  8,   5],
+    [ 10,  14], [ 14,  18], [ 13,  27], [  2,  40],
+    [  0,  58], [ -3,  70], [ -6,  79], [ -8,  85],
+    [  0,   0], [-13, 106], [-16, 106], [-10,  87],
+    [-21, 114], [-18, 110], [-14,  98], [-22, 110],
+    [-21, 106], [-18, 103], [-21, 107], [-23, 108],
+    [-26, 112], [-10,  96], [-12,  95], [ -5,  91],
+    [ -9,  93], [-22,  94], [ -5,  86], [  9,  67],
+    [ -4,  80], [-10,  85], [ -1,  70], [  7,  60],
+    [  9,  58], [  5,  61], [ 12,  50], [ 15,  50],
+    [ 18,  49], [ 17,  54], [ 10,  41], [  7,  46],
+    [ -1,  51], [  7,  49], [  8,  52], [  9,  41],
+    [  6,  47], [  2,  55], [ 13,  41], [ 10,  44],
+    [  6,  50], [  5,  53], [ 13,  49], [  4,  63],
+    [  6,  64], [ -2,  69], [ -2,  59], [  6,  70],
+    [ 10,  44], [  9,  31], [ 12,  43], [  3,  53],
+    [ 14,  34], [ 10,  38], [ -3,  52], [ 13,  40],
+    [ 17,  32], [  7,  44], [  7,  38], [ 13,  50],
+    [ 10,  57], [ 26,  43], [ 14,  11], [ 11,  14],
+    [  9,  11], [ 18,  11], [ 21,   9], [ 23,  -2],
+    [ 32, -15], [ 32, -15], [ 34, -21], [ 39, -23],
+    [ 42, -33], [ 41, -31], [ 46, -28], [ 38, -12],
+    [ 21,  29], [ 45, -24], [ 53, -45], [ 48, -26],
+    [ 65, -43], [ 43, -19], [ 39, -10], [ 30,   9],
+    [ 18,  26], [ 20,  27], [  0,  57], [-14,  82],
+    [ -5,  75], [-19,  97], [-35, 125], [ 27,   0],
+    [ 28,   0], [ 31,  -4], [ 27,   6], [ 34,   8],
+    [ 30,  10], [ 24,  22], [ 33,  19], [ 22,  32],
+    [ 26,  31], [ 21,  41], [ 26,  44], [ 23,  47],
+    [ 16,  65], [ 14,  71], [  8,  60], [  6,  63],
+    [ 17,  65], [ 21,  24], [ 23,  20], [ 26,  23],
+    [ 27,  32], [ 28,  23], [ 28,  24], [ 23,  40],
+    [ 24,  32], [ 28,  29], [ 23,  42], [ 19,  57],
+    [ 22,  53], [ 22,  61], [ 11,  86], [ 12,  40],
+    [ 11,  51], [ 14,  59], [ -4,  79], [ -7,  71],
+    [ -5,  69], [ -9,  70], [ -8,  66], [-10,  68],
+    [-19,  73], [-12,  69], [-16,  70], [-15,  67],
+    [-20,  62], [-19,  70], [-16,  66], [-22,  65],
+    [-20,  63], [  9,  -2], [ 26,  -9], [ 33,  -9],
+    [ 39,  -7], [ 41,  -2], [ 45,   3], [ 49,   9],
+    [ 45,  27], [ 36,  59], [ -6,  66], [ -7,  35],
+    [ -7,  42], [ -8,  45], [ -5,  48], [-12,  56],
+    [ -6,  60], [ -5,  62], [ -8,  66], [ -8,  76],
+    [ -5,  85], [ -6,  81], [-10,  77], [ -7,  81],
+    [-17,  80], [-18,  73], [ -4,  74], [-10,  83],
+    [ -9,  71], [ -9,  67], [ -1,  61], [ -8,  66],
+    [-14,  66], [  0,  59], [  2,  59], [ 21, -13],
+    [ 33, -14], [ 39,  -7], [ 46,  -2], [ 51,   2],
+    [ 60,   6], [ 61,  17], [ 55,  34], [ 42,  62],
+    [ -7,  92], [ -5,  89], [ -7,  96], [-13, 108],
+    [ -3,  46], [ -1,  65], [ -1,  57], [ -9,  93],
+    [ -3,  74], [ -9,  92], [ -8,  87], [-23, 126],
+    [ -7,  92], [ -5,  89], [ -7,  96], [-13, 108],
+    [ -3,  46], [ -1,  65], [ -1,  57], [ -9,  93],
+    [ -3,  74], [ -9,  92], [ -8,  87], [-23, 126],
+    [ -2,  85], [ -6,  78], [ -1,  75], [ -7,  77],
+    [  2,  54], [  5,  50], [ -3,  68], [  1,  50],
+    [  6,  42], [ -4,  81], [  1,  63], [ -4,  70],
+    [  0,  67], [  2,  57], [ -2,  76], [ 11,  35],
+    [  4,  64], [  1,  61], [ 11,  35], [ 18,  25],
+    [ 12,  24], [ 13,  29], [ 13,  36], [-10,  93],
+    [ -7,  73], [ -2,  73], [ 13,  46], [  9,  49],
+    [ -7, 100], [  9,  53], [  2,  53], [  5,  53],
+    [ -2,  61], [  0,  56], [  0,  56], [-13,  63],
+    [ -5,  60], [ -1,  62], [  4,  57], [ -6,  69],
+    [  4,  57], [ 14,  39], [  4,  51], [ 13,  68],
+    [ -2,  85], [ -6,  78], [ -1,  75], [ -7,  77],
+    [  2,  54], [  5,  50], [ -3,  68], [  1,  50],
+    [  6,  42], [ -4,  81], [  1,  63], [ -4,  70],
+    [  0,  67], [  2,  57], [ -2,  76], [ 11,  35],
+    [  4,  64], [  1,  61], [ 11,  35], [ 18,  25],
+    [ 12,  24], [ 13,  29], [ 13,  36], [-10,  93],
+    [ -7,  73], [ -2,  73], [ 13,  46], [  9,  49],
+    [ -7, 100], [  9,  53], [  2,  53], [  5,  53],
+    [ -2,  61], [  0,  56], [  0,  56], [-13,  63],
+    [ -5,  60], [ -1,  62], [  4,  57], [ -6,  69],
+    [  4,  57], [ 14,  39], [  4,  51], [ 13,  68],
+    [ 11,  28], [  2,  40], [  3,  44], [  0,  49],
+    [  0,  46], [  2,  44], [  2,  51], [  0,  47],
+    [  4,  39], [  2,  62], [  6,  46], [  0,  54],
+    [  3,  54], [  2,  58], [  4,  63], [  6,  51],
+    [  6,  57], [  7,  53], [  6,  52], [  6,  55],
+    [ 11,  45], [ 14,  36], [  8,  53], [ -1,  82],
+    [  7,  55], [ -3,  78], [ 15,  46], [ 22,  31],
+    [ -1,  84], [ 25,   7], [ 30,  -7], [ 28,   3],
+    [ 28,   4], [ 32,   0], [ 34,  -1], [ 30,   6],
+    [ 30,   6], [ 32,   9], [ 31,  19], [ 26,  27],
+    [ 26,  30], [ 37,  20], [ 28,  34], [ 17,  70],
+    [ 11,  28], [  2,  40], [  3,  44], [  0,  49],
+    [  0,  46], [  2,  44], [  2,  51], [  0,  47],
+    [  4,  39], [  2,  62], [  6,  46], [  0,  54],
+    [  3,  54], [  2,  58], [  4,  63], [  6,  51],
+    [  6,  57], [  7,  53], [  6,  52], [  6,  55],
+    [ 11,  45], [ 14,  36], [  8,  53], [ -1,  82],
+    [  7,  55], [ -3,  78], [ 15,  46], [ 22,  31],
+    [ -1,  84], [ 25,   7], [ 30,  -7], [ 28,   3],
+    [ 28,   4], [ 32,   0], [ 34,  -1], [ 30,   6],
+    [ 30,   6], [ 32,   9], [ 31,  19], [ 26,  27],
+    [ 26,  30], [ 37,  20], [ 28,  34], [ 17,  70],
+    [ -4,  79], [ -7,  71], [ -5,  69], [ -9,  70],
+    [ -8,  66], [-10,  68], [-19,  73], [-12,  69],
+    [-16,  70], [-15,  67], [-20,  62], [-19,  70],
+    [-16,  66], [-22,  65], [-20,  63], [ -5,  85],
+    [ -6,  81], [-10,  77], [ -7,  81], [-17,  80],
+    [-18,  73], [ -4,  74], [-10,  83], [ -9,  71],
+    [ -9,  67], [ -1,  61], [ -8,  66], [-14,  66],
+    [  0,  59], [  2,  59], [  9,  -2], [ 26,  -9],
+    [ 33,  -9], [ 39,  -7], [ 41,  -2], [ 45,   3],
+    [ 49,   9], [ 45,  27], [ 36,  59], [ 21, -13],
+    [ 33, -14], [ 39,  -7], [ 46,  -2], [ 51,   2],
+    [ 60,   6], [ 61,  17], [ 55,  34], [ 42,  62],
+    [ -6,  66], [ -7,  35], [ -7,  42], [ -8,  45],
+    [ -5,  48], [-12,  56], [ -6,  60], [ -5,  62],
+    [ -8,  66], [ -8,  76], [ -4,  79], [ -7,  71],
+    [ -5,  69], [ -9,  70], [ -8,  66], [-10,  68],
+    [-19,  73], [-12,  69], [-16,  70], [-15,  67],
+    [-20,  62], [-19,  70], [-16,  66], [-22,  65],
+    [-20,  63], [ -5,  85], [ -6,  81], [-10,  77],
+    [ -7,  81], [-17,  80], [-18,  73], [ -4,  74],
+    [-10,  83], [ -9,  71], [ -9,  67], [ -1,  61],
+    [ -8,  66], [-14,  66], [  0,  59], [  2,  59],
+    [  9,  -2], [ 26,  -9], [ 33,  -9], [ 39,  -7],
+    [ 41,  -2], [ 45,   3], [ 49,   9], [ 45,  27],
+    [ 36,  59], [ 21, -13], [ 33, -14], [ 39,  -7],
+    [ 46,  -2], [ 51,   2], [ 60,   6], [ 61,  17],
+    [ 55,  34], [ 42,  62], [ -6,  66], [ -7,  35],
+    [ -7,  42], [ -8,  45], [ -5,  48], [-12,  56],
+    [ -6,  60], [ -5,  62], [ -8,  66], [ -8,  76],
+    [-13, 106], [-16, 106], [-10,  87], [-21, 114],
+    [-18, 110], [-14,  98], [-22, 110], [-21, 106],
+    [-18, 103], [-21, 107], [-23, 108], [-26, 112],
+    [-10,  96], [-12,  95], [ -5,  91], [ -9,  93],
+    [-22,  94], [ -5,  86], [  9,  67], [ -4,  80],
+    [-10,  85], [ -1,  70], [  7,  60], [  9,  58],
+    [  5,  61], [ 12,  50], [ 15,  50], [ 18,  49],
+    [ 17,  54], [ 10,  41], [  7,  46], [ -1,  51],
+    [  7,  49], [  8,  52], [  9,  41], [  6,  47],
+    [  2,  55], [ 13,  41], [ 10,  44], [  6,  50],
+    [  5,  53], [ 13,  49], [  4,  63], [  6,  64],
+    [-13, 106], [-16, 106], [-10,  87], [-21, 114],
+    [-18, 110], [-14,  98], [-22, 110], [-21, 106],
+    [-18, 103], [-21, 107], [-23, 108], [-26, 112],
+    [-10,  96], [-12,  95], [ -5,  91], [ -9,  93],
+    [-22,  94], [ -5,  86], [  9,  67], [ -4,  80],
+    [-10,  85], [ -1,  70], [  7,  60], [  9,  58],
+    [  5,  61], [ 12,  50], [ 15,  50], [ 18,  49],
+    [ 17,  54], [ 10,  41], [  7,  46], [ -1,  51],
+    [  7,  49], [  8,  52], [  9,  41], [  6,  47],
+    [  2,  55], [ 13,  41], [ 10,  44], [  6,  50],
+    [  5,  53], [ 13,  49], [  4,  63], [  6,  64],
+    [ 14,  11], [ 11,  14], [  9,  11], [ 18,  11],
+    [ 21,   9], [ 23,  -2], [ 32, -15], [ 32, -15],
+    [ 34, -21], [ 39, -23], [ 42, -33], [ 41, -31],
+    [ 46, -28], [ 38, -12], [ 21,  29], [ 45, -24],
+    [ 53, -45], [ 48, -26], [ 65, -43], [ 43, -19],
+    [ 39, -10], [ 30,   9], [ 18,  26], [ 20,  27],
+    [  0,  57], [-14,  82], [ -5,  75], [-19,  97],
+    [-35, 125], [ 27,   0], [ 28,   0], [ 31,  -4],
+    [ 27,   6], [ 34,   8], [ 30,  10], [ 24,  22],
+    [ 33,  19], [ 22,  32], [ 26,  31], [ 21,  41],
+    [ 26,  44], [ 23,  47], [ 16,  65], [ 14,  71],
+    [ 14,  11], [ 11,  14], [  9,  11], [ 18,  11],
+    [ 21,   9], [ 23,  -2], [ 32, -15], [ 32, -15],
+    [ 34, -21], [ 39, -23], [ 42, -33], [ 41, -31],
+    [ 46, -28], [ 38, -12], [ 21,  29], [ 45, -24],
+    [ 53, -45], [ 48, -26], [ 65, -43], [ 43, -19],
+    [ 39, -10], [ 30,   9], [ 18,  26], [ 20,  27],
+    [  0,  57], [-14,  82], [ -5,  75], [-19,  97],
+    [-35, 125], [ 27,   0], [ 28,   0], [ 31,  -4],
+    [ 27,   6], [ 34,   8], [ 30,  10], [ 24,  22],
+    [ 33,  19], [ 22,  32], [ 26,  31], [ 21,  41],
+    [ 26,  44], [ 23,  47], [ 16,  65], [ 14,  71],
+    [ -6,  76], [ -2,  44], [  0,  45], [  0,  52],
+    [ -3,  64], [ -2,  59], [ -4,  70], [ -4,  75],
+    [ -8,  82], [-17, 102], [ -9,  77], [  3,  24],
+    [  0,  42], [  0,  48], [  0,  55], [ -6,  59],
+    [ -7,  71], [-12,  83], [-11,  87], [-30, 119],
+    [  1,  58], [ -3,  29], [ -1,  36], [  1,  38],
+    [  2,  43], [ -6,  55], [  0,  58], [  0,  64],
+    [ -3,  74], [-10,  90], [ -6,  76], [ -2,  44],
+    [  0,  45], [  0,  52], [ -3,  64], [ -2,  59],
+    [ -4,  70], [ -4,  75], [ -8,  82], [-17, 102],
+    [ -9,  77], [  3,  24], [  0,  42], [  0,  48],
+    [  0,  55], [ -6,  59], [ -7,  71], [-12,  83],
+    [-11,  87], [-30, 119], [  1,  58], [ -3,  29],
+    [ -1,  36], [  1,  38], [  2,  43], [ -6,  55],
+    [  0,  58], [  0,  64], [ -3,  74], [-10,  90],
+    [ -3,  74], [ -9,  92], [ -8,  87], [-23, 126],
+    [ -3,  74], [ -9,  92], [ -8,  87], [-23, 126],
+    [ -3,  74], [ -9,  92], [ -8,  87], [-23, 126]
+  ], [
+    [ 20, -15], [  2,  54], [  3,  74], [ 20, -15],
+    [  2,  54], [  3,  74], [-28, 127], [-23, 104],
+    [ -6,  53], [ -1,  54], [  7,  51], [ 22,  25],
+    [ 34,   0], [ 16,   0], [ -2,   9], [  4,  41],
+    [-29, 118], [  2,  65], [ -6,  71], [-13,  79],
+    [  5,  52], [  9,  50], [ -3,  70], [ 10,  54],
+    [ 26,  34], [ 19,  22], [ 40,   0], [ 57,   2],
+    [ 41,  36], [ 26,  69], [-45, 127], [-15, 101],
+    [ -4,  76], [ -6,  71], [-13,  79], [  5,  52],
+    [  6,  69], [-13,  90], [  0,  52], [  8,  43],
+    [ -2,  69], [ -5,  82], [-10,  96], [  2,  59],
+    [  2,  75], [ -3,  87], [ -3, 100], [  1,  56],
+    [ -3,  74], [ -6,  85], [  0,  59], [ -3,  81],
+    [ -7,  86], [ -5,  95], [ -1,  66], [ -1,  77],
+    [  1,  70], [ -2,  86], [ -5,  72], [  0,  61],
+    [  0,  41], [  0,  63], [  0,  63], [  0,  63],
+    [ -9,  83], [  4,  86], [  0,  97], [ -7,  72],
+    [ 13,  41], [  3,  62], [ 13,  15], [  7,  51],
+    [  2,  80], [-39, 127], [-18,  91], [-17,  96],
+    [-26,  81], [-35,  98], [-24, 102], [-23,  97],
+    [-27, 119], [-24,  99], [-21, 110], [-18, 102],
+    [-36, 127], [  0,  80], [ -5,  89], [ -7,  94],
+    [ -4,  92], [  0,  39], [  0,  65], [-15,  84],
+    [-35, 127], [ -2,  73], [-12, 104], [ -9,  91],
+    [-31, 127], [  3,  55], [  7,  56], [  7,  55],
+    [  8,  61], [ -3,  53], [  0,  68], [ -7,  74],
+    [ -9,  88], [-13, 103], [-13,  91], [ -9,  89],
+    [-14,  92], [ -8,  76], [-12,  87], [-23, 110],
+    [-24, 105], [-10,  78], [-20, 112], [-17,  99],
+    [-78, 127], [-70, 127], [-50, 127], [-46, 127],
+    [ -4,  66], [ -5,  78], [ -4,  71], [ -8,  72],
+    [  2,  59], [ -1,  55], [ -7,  70], [ -6,  75],
+    [ -8,  89], [-34, 119], [ -3,  75], [ 32,  20],
+    [ 30,  22], [-44, 127], [  0,  54], [ -5,  61],
+    [  0,  58], [ -1,  60], [ -3,  61], [ -8,  67],
+    [-25,  84], [-14,  74], [ -5,  65], [  5,  52],
+    [  2,  57], [  0,  61], [ -9,  69], [-11,  70],
+    [ 18,  55], [ -4,  71], [  0,  58], [  7,  61],
+    [  9,  41], [ 18,  25], [  9,  32], [  5,  43],
+    [  9,  47], [  0,  44], [  0,  51], [  2,  46],
+    [ 19,  38], [ -4,  66], [ 15,  38], [ 12,  42],
+    [  9,  34], [  0,  89], [  4,  45], [ 10,  28],
+    [ 10,  31], [ 33, -11], [ 52, -43], [ 18,  15],
+    [ 28,   0], [ 35, -22], [ 38, -25], [ 34,   0],
+    [ 39, -18], [ 32, -12], [102, -94], [  0,   0],
+    [ 56, -15], [ 33,  -4], [ 29,  10], [ 37,  -5],
+    [ 51, -29], [ 39,  -9], [ 52, -34], [ 69, -58],
+    [ 67, -63], [ 44,  -5], [ 32,   7], [ 55, -29],
+    [ 32,   1], [  0,   0], [ 27,  36], [ 33, -25],
+    [ 34, -30], [ 36, -28], [ 38, -28], [ 38, -27],
+    [ 34, -18], [ 35, -16], [ 34, -14], [ 32,  -8],
+    [ 37,  -6], [ 35,   0], [ 30,  10], [ 28,  18],
+    [ 26,  25], [ 29,  41], [  0,  75], [  2,  72],
+    [  8,  77], [ 14,  35], [ 18,  31], [ 17,  35],
+    [ 21,  30], [ 17,  45], [ 20,  42], [ 18,  45],
+    [ 27,  26], [ 16,  54], [  7,  66], [ 16,  56],
+    [ 11,  73], [ 10,  67], [-10, 116], [-23, 112],
+    [-15,  71], [ -7,  61], [  0,  53], [ -5,  66],
+    [-11,  77], [ -9,  80], [ -9,  84], [-10,  87],
+    [-34, 127], [-21, 101], [ -3,  39], [ -5,  53],
+    [ -7,  61], [-11,  75], [-15,  77], [-17,  91],
+    [-25, 107], [-25, 111], [-28, 122], [-11,  76],
+    [-10,  44], [-10,  52], [-10,  57], [ -9,  58],
+    [-16,  72], [ -7,  69], [ -4,  69], [ -5,  74],
+    [ -9,  86], [  2,  66], [ -9,  34], [  1,  32],
+    [ 11,  31], [  5,  52], [ -2,  55], [ -2,  67],
+    [  0,  73], [ -8,  89], [  3,  52], [  7,   4],
+    [ 10,   8], [ 17,   8], [ 16,  19], [  3,  37],
+    [ -1,  61], [ -5,  73], [ -1,  70], [ -4,  78],
+    [  0,   0], [-21, 126], [-23, 124], [-20, 110],
+    [-26, 126], [-25, 124], [-17, 105], [-27, 121],
+    [-27, 117], [-17, 102], [-26, 117], [-27, 116],
+    [-33, 122], [-10,  95], [-14, 100], [ -8,  95],
+    [-17, 111], [-28, 114], [ -6,  89], [ -2,  80],
+    [ -4,  82], [ -9,  85], [ -8,  81], [ -1,  72],
+    [  5,  64], [  1,  67], [  9,  56], [  0,  69],
+    [  1,  69], [  7,  69], [ -7,  69], [ -6,  67],
+    [-16,  77], [ -2,  64], [  2,  61], [ -6,  67],
+    [ -3,  64], [  2,  57], [ -3,  65], [ -3,  66],
+    [  0,  62], [  9,  51], [ -1,  66], [ -2,  71],
+    [ -2,  75], [ -1,  70], [ -9,  72], [ 14,  60],
+    [ 16,  37], [  0,  47], [ 18,  35], [ 11,  37],
+    [ 12,  41], [ 10,  41], [  2,  48], [ 12,  41],
+    [ 13,  41], [  0,  59], [  3,  50], [ 19,  40],
+    [  3,  66], [ 18,  50], [ 19,  -6], [ 18,  -6],
+    [ 14,   0], [ 26, -12], [ 31, -16], [ 33, -25],
+    [ 33, -22], [ 37, -28], [ 39, -30], [ 42, -30],
+    [ 47, -42], [ 45, -36], [ 49, -34], [ 41, -17],
+    [ 32,   9], [ 69, -71], [ 63, -63], [ 66, -64],
+    [ 77, -74], [ 54, -39], [ 52, -35], [ 41, -10],
+    [ 36,   0], [ 40,  -1], [ 30,  14], [ 28,  26],
+    [ 23,  37], [ 12,  55], [ 11,  65], [ 37, -33],
+    [ 39, -36], [ 40, -37], [ 38, -30], [ 46, -33],
+    [ 42, -30], [ 40, -24], [ 49, -29], [ 38, -12],
+    [ 40, -10], [ 38,  -3], [ 46,  -5], [ 31,  20],
+    [ 29,  30], [ 25,  44], [ 12,  48], [ 11,  49],
+    [ 26,  45], [ 22,  22], [ 23,  22], [ 27,  21],
+    [ 33,  20], [ 26,  28], [ 30,  24], [ 27,  34],
+    [ 18,  42], [ 25,  39], [ 18,  50], [ 12,  70],
+    [ 21,  54], [ 14,  71], [ 11,  83], [ 25,  32],
+    [ 21,  49], [ 21,  54], [ -5,  85], [ -6,  81],
+    [-10,  77], [ -7,  81], [-17,  80], [-18,  73],
+    [ -4,  74], [-10,  83], [ -9,  71], [ -9,  67],
+    [ -1,  61], [ -8,  66], [-14,  66], [  0,  59],
+    [  2,  59], [ 17, -10], [ 32, -13], [ 42,  -9],
+    [ 49,  -5], [ 53,   0], [ 64,   3], [ 68,  10],
+    [ 66,  27], [ 47,  57], [ -5,  71], [  0,  24],
+    [ -1,  36], [ -2,  42], [ -2,  52], [ -9,  57],
+    [ -6,  63], [ -4,  65], [ -4,  67], [ -7,  82],
+    [ -3,  81], [ -3,  76], [ -7,  72], [ -6,  78],
+    [-12,  72], [-14,  68], [ -3,  70], [ -6,  76],
+    [ -5,  66], [ -5,  62], [  0,  57], [ -4,  61],
+    [ -9,  60], [  1,  54], [  2,  58], [ 17, -10],
+    [ 32, -13], [ 42,  -9], [ 49,  -5], [ 53,   0],
+    [ 64,   3], [ 68,  10], [ 66,  27], [ 47,  57],
+    [  0,  80], [ -5,  89], [ -7,  94], [ -4,  92],
+    [  0,  39], [  0,  65], [-15,  84], [-35, 127],
+    [ -2,  73], [-12, 104], [ -9,  91], [-31, 127],
+    [  0,  80], [ -5,  89], [ -7,  94], [ -4,  92],
+    [  0,  39], [  0,  65], [-15,  84], [-35, 127],
+    [ -2,  73], [-12, 104], [ -9,  91], [-31, 127],
+    [-13, 103], [-13,  91], [ -9,  89], [-14,  92],
+    [ -8,  76], [-12,  87], [-23, 110], [-24, 105],
+    [-10,  78], [-20, 112], [-17,  99], [-78, 127],
+    [-70, 127], [-50, 127], [-46, 127], [ -4,  66],
+    [ -5,  78], [ -4,  71], [ -8,  72], [  2,  59],
+    [ -1,  55], [ -7,  70], [ -6,  75], [ -8,  89],
+    [-34, 119], [ -3,  75], [ 32,  20], [ 30,  22],
+    [-44, 127], [  0,  54], [ -5,  61], [  0,  58],
+    [ -1,  60], [ -3,  61], [ -8,  67], [-25,  84],
+    [-14,  74], [ -5,  65], [  5,  52], [  2,  57],
+    [  0,  61], [ -9,  69], [-11,  70], [ 18,  55],
+    [-13, 103], [-13,  91], [ -9,  89], [-14,  92],
+    [ -8,  76], [-12,  87], [-23, 110], [-24, 105],
+    [-10,  78], [-20, 112], [-17,  99], [-78, 127],
+    [-70, 127], [-50, 127], [-46, 127], [ -4,  66],
+    [ -5,  78], [ -4,  71], [ -8,  72], [  2,  59],
+    [ -1,  55], [ -7,  70], [ -6,  75], [ -8,  89],
+    [-34, 119], [ -3,  75], [ 32,  20], [ 30,  22],
+    [-44, 127], [  0,  54], [ -5,  61], [  0,  58],
+    [ -1,  60], [ -3,  61], [ -8,  67], [-25,  84],
+    [-14,  74], [ -5,  65], [  5,  52], [  2,  57],
+    [  0,  61], [ -9,  69], [-11,  70], [ 18,  55],
+    [  4,  45], [ 10,  28], [ 10,  31], [ 33, -11],
+    [ 52, -43], [ 18,  15], [ 28,   0], [ 35, -22],
+    [ 38, -25], [ 34,   0], [ 39, -18], [ 32, -12],
+    [102, -94], [  0,   0], [ 56, -15], [ 33,  -4],
+    [ 29,  10], [ 37,  -5], [ 51, -29], [ 39,  -9],
+    [ 52, -34], [ 69, -58], [ 67, -63], [ 44,  -5],
+    [ 32,   7], [ 55, -29], [ 32,   1], [  0,   0],
+    [ 27,  36], [ 33, -25], [ 34, -30], [ 36, -28],
+    [ 38, -28], [ 38, -27], [ 34, -18], [ 35, -16],
+    [ 34, -14], [ 32,  -8], [ 37,  -6], [ 35,   0],
+    [ 30,  10], [ 28,  18], [ 26,  25], [ 29,  41],
+    [  4,  45], [ 10,  28], [ 10,  31], [ 33, -11],
+    [ 52, -43], [ 18,  15], [ 28,   0], [ 35, -22],
+    [ 38, -25], [ 34,   0], [ 39, -18], [ 32, -12],
+    [102, -94], [  0,   0], [ 56, -15], [ 33,  -4],
+    [ 29,  10], [ 37,  -5], [ 51, -29], [ 39,  -9],
+    [ 52, -34], [ 69, -58], [ 67, -63], [ 44,  -5],
+    [ 32,   7], [ 55, -29], [ 32,   1], [  0,   0],
+    [ 27,  36], [ 33, -25], [ 34, -30], [ 36, -28],
+    [ 38, -28], [ 38, -27], [ 34, -18], [ 35, -16],
+    [ 34, -14], [ 32,  -8], [ 37,  -6], [ 35,   0],
+    [ 30,  10], [ 28,  18], [ 26,  25], [ 29,  41],
+    [ -5,  85], [ -6,  81], [-10,  77], [ -7,  81],
+    [-17,  80], [-18,  73], [ -4,  74], [-10,  83],
+    [ -9,  71], [ -9,  67], [ -1,  61], [ -8,  66],
+    [-14,  66], [  0,  59], [  2,  59], [ -3,  81],
+    [ -3,  76], [ -7,  72], [ -6,  78], [-12,  72],
+    [-14,  68], [ -3,  70], [ -6,  76], [ -5,  66],
+    [ -5,  62], [  0,  57], [ -4,  61], [ -9,  60],
+    [  1,  54], [  2,  58], [ 17, -10], [ 32, -13],
+    [ 42,  -9], [ 49,  -5], [ 53,   0], [ 64,   3],
+    [ 68,  10], [ 66,  27], [ 47,  57], [ 17, -10],
+    [ 32, -13], [ 42,  -9], [ 49,  -5], [ 53,   0],
+    [ 64,   3], [ 68,  10], [ 66,  27], [ 47,  57],
+    [ -5,  71], [  0,  24], [ -1,  36], [ -2,  42],
+    [ -2,  52], [ -9,  57], [ -6,  63], [ -4,  65],
+    [ -4,  67], [ -7,  82], [ -5,  85], [ -6,  81],
+    [-10,  77], [ -7,  81], [-17,  80], [-18,  73],
+    [ -4,  74], [-10,  83], [ -9,  71], [ -9,  67],
+    [ -1,  61], [ -8,  66], [-14,  66], [  0,  59],
+    [  2,  59], [ -3,  81], [ -3,  76], [ -7,  72],
+    [ -6,  78], [-12,  72], [-14,  68], [ -3,  70],
+    [ -6,  76], [ -5,  66], [ -5,  62], [  0,  57],
+    [ -4,  61], [ -9,  60], [  1,  54], [  2,  58],
+    [ 17, -10], [ 32, -13], [ 42,  -9], [ 49,  -5],
+    [ 53,   0], [ 64,   3], [ 68,  10], [ 66,  27],
+    [ 47,  57], [ 17, -10], [ 32, -13], [ 42,  -9],
+    [ 49,  -5], [ 53,   0], [ 64,   3], [ 68,  10],
+    [ 66,  27], [ 47,  57], [ -5,  71], [  0,  24],
+    [ -1,  36], [ -2,  42], [ -2,  52], [ -9,  57],
+    [ -6,  63], [ -4,  65], [ -4,  67], [ -7,  82],
+    [-21, 126], [-23, 124], [-20, 110], [-26, 126],
+    [-25, 124], [-17, 105], [-27, 121], [-27, 117],
+    [-17, 102], [-26, 117], [-27, 116], [-33, 122],
+    [-10,  95], [-14, 100], [ -8,  95], [-17, 111],
+    [-28, 114], [ -6,  89], [ -2,  80], [ -4,  82],
+    [ -9,  85], [ -8,  81], [ -1,  72], [  5,  64],
+    [  1,  67], [  9,  56], [  0,  69], [  1,  69],
+    [  7,  69], [ -7,  69], [ -6,  67], [-16,  77],
+    [ -2,  64], [  2,  61], [ -6,  67], [ -3,  64],
+    [  2,  57], [ -3,  65], [ -3,  66], [  0,  62],
+    [  9,  51], [ -1,  66], [ -2,  71], [ -2,  75],
+    [-21, 126], [-23, 124], [-20, 110], [-26, 126],
+    [-25, 124], [-17, 105], [-27, 121], [-27, 117],
+    [-17, 102], [-26, 117], [-27, 116], [-33, 122],
+    [-10,  95], [-14, 100], [ -8,  95], [-17, 111],
+    [-28, 114], [ -6,  89], [ -2,  80], [ -4,  82],
+    [ -9,  85], [ -8,  81], [ -1,  72], [  5,  64],
+    [  1,  67], [  9,  56], [  0,  69], [  1,  69],
+    [  7,  69], [ -7,  69], [ -6,  67], [-16,  77],
+    [ -2,  64], [  2,  61], [ -6,  67], [ -3,  64],
+    [  2,  57], [ -3,  65], [ -3,  66], [  0,  62],
+    [  9,  51], [ -1,  66], [ -2,  71], [ -2,  75],
+    [ 19,  -6], [ 18,  -6], [ 14,   0], [ 26, -12],
+    [ 31, -16], [ 33, -25], [ 33, -22], [ 37, -28],
+    [ 39, -30], [ 42, -30], [ 47, -42], [ 45, -36],
+    [ 49, -34], [ 41, -17], [ 32,   9], [ 69, -71],
+    [ 63, -63], [ 66, -64], [ 77, -74], [ 54, -39],
+    [ 52, -35], [ 41, -10], [ 36,   0], [ 40,  -1],
+    [ 30,  14], [ 28,  26], [ 23,  37], [ 12,  55],
+    [ 11,  65], [ 37, -33], [ 39, -36], [ 40, -37],
+    [ 38, -30], [ 46, -33], [ 42, -30], [ 40, -24],
+    [ 49, -29], [ 38, -12], [ 40, -10], [ 38,  -3],
+    [ 46,  -5], [ 31,  20], [ 29,  30], [ 25,  44],
+    [ 19,  -6], [ 18,  -6], [ 14,   0], [ 26, -12],
+    [ 31, -16], [ 33, -25], [ 33, -22], [ 37, -28],
+    [ 39, -30], [ 42, -30], [ 47, -42], [ 45, -36],
+    [ 49, -34], [ 41, -17], [ 32,   9], [ 69, -71],
+    [ 63, -63], [ 66, -64], [ 77, -74], [ 54, -39],
+    [ 52, -35], [ 41, -10], [ 36,   0], [ 40,  -1],
+    [ 30,  14], [ 28,  26], [ 23,  37], [ 12,  55],
+    [ 11,  65], [ 37, -33], [ 39, -36], [ 40, -37],
+    [ 38, -30], [ 46, -33], [ 42, -30], [ 40, -24],
+    [ 49, -29], [ 38, -12], [ 40, -10], [ 38,  -3],
+    [ 46,  -5], [ 31,  20], [ 29,  30], [ 25,  44],
+    [-23, 112], [-15,  71], [ -7,  61], [  0,  53],
+    [ -5,  66], [-11,  77], [ -9,  80], [ -9,  84],
+    [-10,  87], [-34, 127], [-21, 101], [ -3,  39],
+    [ -5,  53], [ -7,  61], [-11,  75], [-15,  77],
+    [-17,  91], [-25, 107], [-25, 111], [-28, 122],
+    [-11,  76], [-10,  44], [-10,  52], [-10,  57],
+    [ -9,  58], [-16,  72], [ -7,  69], [ -4,  69],
+    [ -5,  74], [ -9,  86], [-23, 112], [-15,  71],
+    [ -7,  61], [  0,  53], [ -5,  66], [-11,  77],
+    [ -9,  80], [ -9,  84], [-10,  87], [-34, 127],
+    [-21, 101], [ -3,  39], [ -5,  53], [ -7,  61],
+    [-11,  75], [-15,  77], [-17,  91], [-25, 107],
+    [-25, 111], [-28, 122], [-11,  76], [-10,  44],
+    [-10,  52], [-10,  57], [ -9,  58], [-16,  72],
+    [ -7,  69], [ -4,  69], [ -5,  74], [ -9,  86],
+    [ -2,  73], [-12, 104], [ -9,  91], [-31, 127],
+    [ -2,  73], [-12, 104], [ -9,  91], [-31, 127],
+    [ -2,  73], [-12, 104], [ -9,  91], [-31, 127]
+  ], [
+    [ 20, -15], [  2,  54], [  3,  74], [ 20, -15],
+    [  2,  54], [  3,  74], [-28, 127], [-23, 104],
+    [ -6,  53], [ -1,  54], [  7,  51], [ 29,  16],
+    [ 25,   0], [ 14,   0], [-10,  51], [ -3,  62],
+    [-27,  99], [ 26,  16], [ -4,  85], [-24, 102],
+    [  5,  57], [  6,  57], [-17,  73], [ 14,  57],
+    [ 20,  40], [ 20,  10], [ 29,   0], [ 54,   0],
+    [ 37,  42], [ 12,  97], [-32, 127], [-22, 117],
+    [ -2,  74], [ -4,  85], [-24, 102], [  5,  57],
+    [ -6,  93], [-14,  88], [ -6,  44], [  4,  55],
+    [-11,  89], [-15, 103], [-21, 116], [ 19,  57],
+    [ 20,  58], [  4,  84], [  6,  96], [  1,  63],
+    [ -5,  85], [-13, 106], [  5,  63], [  6,  75],
+    [ -3,  90], [ -1, 101], [  3,  55], [ -4,  79],
+    [ -2,  75], [-12,  97], [ -7,  50], [  1,  60],
+    [  0,  41], [  0,  63], [  0,  63], [  0,  63],
+    [ -9,  83], [  4,  86], [  0,  97], [ -7,  72],
+    [ 13,  41], [  3,  62], [  7,  34], [ -9,  88],
+    [-20, 127], [-36, 127], [-17,  91], [-14,  95],
+    [-25,  84], [-25,  86], [-12,  89], [-17,  91],
+    [-31, 127], [-14,  76], [-18, 103], [-13,  90],
+    [-37, 127], [ 11,  80], [  5,  76], [  2,  84],
+    [  5,  78], [ -6,  55], [  4,  61], [-14,  83],
+    [-37, 127], [ -5,  79], [-11, 104], [-11,  91],
+    [-30, 127], [  0,  65], [ -2,  79], [  0,  72],
+    [ -4,  92], [ -6,  56], [  3,  68], [ -8,  71],
+    [-13,  98], [ -4,  86], [-12,  88], [ -5,  82],
+    [ -3,  72], [ -4,  67], [ -8,  72], [-16,  89],
+    [ -9,  69], [ -1,  59], [  5,  66], [  4,  57],
+    [ -4,  71], [ -2,  71], [  2,  58], [ -1,  74],
+    [ -4,  44], [ -1,  69], [  0,  62], [ -7,  51],
+    [ -4,  47], [ -6,  42], [ -3,  41], [ -6,  53],
+    [  8,  76], [ -9,  78], [-11,  83], [  9,  52],
+    [  0,  67], [ -5,  90], [  1,  67], [-15,  72],
+    [ -5,  75], [ -8,  80], [-21,  83], [-21,  64],
+    [-13,  31], [-25,  64], [-29,  94], [  9,  75],
+    [ 17,  63], [ -8,  74], [ -5,  35], [ -2,  27],
+    [ 13,  91], [  3,  65], [ -7,  69], [  8,  77],
+    [-10,  66], [  3,  62], [ -3,  68], [-20,  81],
+    [  0,  30], [  1,   7], [ -3,  23], [-21,  74],
+    [ 16,  66], [-23, 124], [ 17,  37], [ 44, -18],
+    [ 50, -34], [-22, 127], [  4,  39], [  0,  42],
+    [  7,  34], [ 11,  29], [  8,  31], [  6,  37],
+    [  7,  42], [  3,  40], [  8,  33], [ 13,  43],
+    [ 13,  36], [  4,  47], [  3,  55], [  2,  58],
+    [  6,  60], [  8,  44], [ 11,  44], [ 14,  42],
+    [  7,  48], [  4,  56], [  4,  52], [ 13,  37],
+    [  9,  49], [ 19,  58], [ 10,  48], [ 12,  45],
+    [  0,  69], [ 20,  33], [  8,  63], [ 35, -18],
+    [ 33, -25], [ 28,  -3], [ 24,  10], [ 27,   0],
+    [ 34, -14], [ 52, -44], [ 39, -24], [ 19,  17],
+    [ 31,  25], [ 36,  29], [ 24,  33], [ 34,  15],
+    [ 30,  20], [ 22,  73], [ 20,  34], [ 19,  31],
+    [ 27,  44], [ 19,  16], [ 15,  36], [ 15,  36],
+    [ 21,  28], [ 25,  21], [ 30,  20], [ 31,  12],
+    [ 27,  16], [ 24,  42], [  0,  93], [ 14,  56],
+    [ 15,  57], [ 26,  38], [-24, 127], [-24, 115],
+    [-22,  82], [ -9,  62], [  0,  53], [  0,  59],
+    [-14,  85], [-13,  89], [-13,  94], [-11,  92],
+    [-29, 127], [-21, 100], [-14,  57], [-12,  67],
+    [-11,  71], [-10,  77], [-21,  85], [-16,  88],
+    [-23, 104], [-15,  98], [-37, 127], [-10,  82],
+    [ -8,  48], [ -8,  61], [ -8,  66], [ -7,  70],
+    [-14,  75], [-10,  79], [ -9,  83], [-12,  92],
+    [-18, 108], [ -4,  79], [-22,  69], [-16,  75],
+    [ -2,  58], [  1,  58], [-13,  78], [ -9,  83],
+    [ -4,  81], [-13,  99], [-13,  81], [ -6,  38],
+    [-13,  62], [ -6,  58], [ -2,  59], [-16,  73],
+    [-10,  76], [-13,  86], [ -9,  83], [-10,  87],
+    [  0,   0], [-22, 127], [-25, 127], [-25, 120],
+    [-27, 127], [-19, 114], [-23, 117], [-25, 118],
+    [-26, 117], [-24, 113], [-28, 118], [-31, 120],
+    [-37, 124], [-10,  94], [-15, 102], [-10,  99],
+    [-13, 106], [-50, 127], [ -5,  92], [ 17,  57],
+    [ -5,  86], [-13,  94], [-12,  91], [ -2,  77],
+    [  0,  71], [ -1,  73], [  4,  64], [ -7,  81],
+    [  5,  64], [ 15,  57], [  1,  67], [  0,  68],
+    [-10,  67], [  1,  68], [  0,  77], [  2,  64],
+    [  0,  68], [ -5,  78], [  7,  55], [  5,  59],
+    [  2,  65], [ 14,  54], [ 15,  44], [  5,  60],
+    [  2,  70], [ -2,  76], [-18,  86], [ 12,  70],
+    [  5,  64], [-12,  70], [ 11,  55], [  5,  56],
+    [  0,  69], [  2,  65], [ -6,  74], [  5,  54],
+    [  7,  54], [ -6,  76], [-11,  82], [ -2,  77],
+    [ -2,  77], [ 25,  42], [ 17, -13], [ 16,  -9],
+    [ 17, -12], [ 27, -21], [ 37, -30], [ 41, -40],
+    [ 42, -41], [ 48, -47], [ 39, -32], [ 46, -40],
+    [ 52, -51], [ 46, -41], [ 52, -39], [ 43, -19],
+    [ 32,  11], [ 61, -55], [ 56, -46], [ 62, -50],
+    [ 81, -67], [ 45, -20], [ 35,  -2], [ 28,  15],
+    [ 34,   1], [ 39,   1], [ 30,  17], [ 20,  38],
+    [ 18,  45], [ 15,  54], [  0,  79], [ 36, -16],
+    [ 37, -14], [ 37, -17], [ 32,   1], [ 34,  15],
+    [ 29,  15], [ 24,  25], [ 34,  22], [ 31,  16],
+    [ 35,  18], [ 31,  28], [ 33,  41], [ 36,  28],
+    [ 27,  47], [ 21,  62], [ 18,  31], [ 19,  26],
+    [ 36,  24], [ 24,  23], [ 27,  16], [ 24,  30],
+    [ 31,  29], [ 22,  41], [ 22,  42], [ 16,  60],
+    [ 15,  52], [ 14,  60], [  3,  78], [-16, 123],
+    [ 21,  53], [ 22,  56], [ 25,  61], [ 21,  33],
+    [ 19,  50], [ 17,  61], [ -3,  78], [ -8,  74],
+    [ -9,  72], [-10,  72], [-18,  75], [-12,  71],
+    [-11,  63], [ -5,  70], [-17,  75], [-14,  72],
+    [-16,  67], [ -8,  53], [-14,  59], [ -9,  52],
+    [-11,  68], [  9,  -2], [ 30, -10], [ 31,  -4],
+    [ 33,  -1], [ 33,   7], [ 31,  12], [ 37,  23],
+    [ 31,  38], [ 20,  64], [ -9,  71], [ -7,  37],
+    [ -8,  44], [-11,  49], [-10,  56], [-12,  59],
+    [ -8,  63], [ -9,  67], [ -6,  68], [-10,  79],
+    [ -3,  78], [ -8,  74], [ -9,  72], [-10,  72],
+    [-18,  75], [-12,  71], [-11,  63], [ -5,  70],
+    [-17,  75], [-14,  72], [-16,  67], [ -8,  53],
+    [-14,  59], [ -9,  52], [-11,  68], [  9,  -2],
+    [ 30, -10], [ 31,  -4], [ 33,  -1], [ 33,   7],
+    [ 31,  12], [ 37,  23], [ 31,  38], [ 20,  64],
+    [ 11,  80], [  5,  76], [  2,  84], [  5,  78],
+    [ -6,  55], [  4,  61], [-14,  83], [-37, 127],
+    [ -5,  79], [-11, 104], [-11,  91], [-30, 127],
+    [ 11,  80], [  5,  76], [  2,  84], [  5,  78],
+    [ -6,  55], [  4,  61], [-14,  83], [-37, 127],
+    [ -5,  79], [-11, 104], [-11,  91], [-30, 127],
+    [ -4,  86], [-12,  88], [ -5,  82], [ -3,  72],
+    [ -4,  67], [ -8,  72], [-16,  89], [ -9,  69],
+    [ -1,  59], [  5,  66], [  4,  57], [ -4,  71],
+    [ -2,  71], [  2,  58], [ -1,  74], [ -4,  44],
+    [ -1,  69], [  0,  62], [ -7,  51], [ -4,  47],
+    [ -6,  42], [ -3,  41], [ -6,  53], [  8,  76],
+    [ -9,  78], [-11,  83], [  9,  52], [  0,  67],
+    [ -5,  90], [  1,  67], [-15,  72], [ -5,  75],
+    [ -8,  80], [-21,  83], [-21,  64], [-13,  31],
+    [-25,  64], [-29,  94], [  9,  75], [ 17,  63],
+    [ -8,  74], [ -5,  35], [ -2,  27], [ 13,  91],
+    [ -4,  86], [-12,  88], [ -5,  82], [ -3,  72],
+    [ -4,  67], [ -8,  72], [-16,  89], [ -9,  69],
+    [ -1,  59], [  5,  66], [  4,  57], [ -4,  71],
+    [ -2,  71], [  2,  58], [ -1,  74], [ -4,  44],
+    [ -1,  69], [  0,  62], [ -7,  51], [ -4,  47],
+    [ -6,  42], [ -3,  41], [ -6,  53], [  8,  76],
+    [ -9,  78], [-11,  83], [  9,  52], [  0,  67],
+    [ -5,  90], [  1,  67], [-15,  72], [ -5,  75],
+    [ -8,  80], [-21,  83], [-21,  64], [-13,  31],
+    [-25,  64], [-29,  94], [  9,  75], [ 17,  63],
+    [ -8,  74], [ -5,  35], [ -2,  27], [ 13,  91],
+    [  4,  39], [  0,  42], [  7,  34], [ 11,  29],
+    [  8,  31], [  6,  37], [  7,  42], [  3,  40],
+    [  8,  33], [ 13,  43], [ 13,  36], [  4,  47],
+    [  3,  55], [  2,  58], [  6,  60], [  8,  44],
+    [ 11,  44], [ 14,  42], [  7,  48], [  4,  56],
+    [  4,  52], [ 13,  37], [  9,  49], [ 19,  58],
+    [ 10,  48], [ 12,  45], [  0,  69], [ 20,  33],
+    [  8,  63], [ 35, -18], [ 33, -25], [ 28,  -3],
+    [ 24,  10], [ 27,   0], [ 34, -14], [ 52, -44],
+    [ 39, -24], [ 19,  17], [ 31,  25], [ 36,  29],
+    [ 24,  33], [ 34,  15], [ 30,  20], [ 22,  73],
+    [  4,  39], [  0,  42], [  7,  34], [ 11,  29],
+    [  8,  31], [  6,  37], [  7,  42], [  3,  40],
+    [  8,  33], [ 13,  43], [ 13,  36], [  4,  47],
+    [  3,  55], [  2,  58], [  6,  60], [  8,  44],
+    [ 11,  44], [ 14,  42], [  7,  48], [  4,  56],
+    [  4,  52], [ 13,  37], [  9,  49], [ 19,  58],
+    [ 10,  48], [ 12,  45], [  0,  69], [ 20,  33],
+    [  8,  63], [ 35, -18], [ 33, -25], [ 28,  -3],
+    [ 24,  10], [ 27,   0], [ 34, -14], [ 52, -44],
+    [ 39, -24], [ 19,  17], [ 31,  25], [ 36,  29],
+    [ 24,  33], [ 34,  15], [ 30,  20], [ 22,  73],
+    [ -3,  78], [ -8,  74], [ -9,  72], [-10,  72],
+    [-18,  75], [-12,  71], [-11,  63], [ -5,  70],
+    [-17,  75], [-14,  72], [-16,  67], [ -8,  53],
+    [-14,  59], [ -9,  52], [-11,  68], [ -3,  78],
+    [ -8,  74], [ -9,  72], [-10,  72], [-18,  75],
+    [-12,  71], [-11,  63], [ -5,  70], [-17,  75],
+    [-14,  72], [-16,  67], [ -8,  53], [-14,  59],
+    [ -9,  52], [-11,  68], [  9,  -2], [ 30, -10],
+    [ 31,  -4], [ 33,  -1], [ 33,   7], [ 31,  12],
+    [ 37,  23], [ 31,  38], [ 20,  64], [  9,  -2],
+    [ 30, -10], [ 31,  -4], [ 33,  -1], [ 33,   7],
+    [ 31,  12], [ 37,  23], [ 31,  38], [ 20,  64],
+    [ -9,  71], [ -7,  37], [ -8,  44], [-11,  49],
+    [-10,  56], [-12,  59], [ -8,  63], [ -9,  67],
+    [ -6,  68], [-10,  79], [ -3,  78], [ -8,  74],
+    [ -9,  72], [-10,  72], [-18,  75], [-12,  71],
+    [-11,  63], [ -5,  70], [-17,  75], [-14,  72],
+    [-16,  67], [ -8,  53], [-14,  59], [ -9,  52],
+    [-11,  68], [ -3,  78], [ -8,  74], [ -9,  72],
+    [-10,  72], [-18,  75], [-12,  71], [-11,  63],
+    [ -5,  70], [-17,  75], [-14,  72], [-16,  67],
+    [ -8,  53], [-14,  59], [ -9,  52], [-11,  68],
+    [  9,  -2], [ 30, -10], [ 31,  -4], [ 33,  -1],
+    [ 33,   7], [ 31,  12], [ 37,  23], [ 31,  38],
+    [ 20,  64], [  9,  -2], [ 30, -10], [ 31,  -4],
+    [ 33,  -1], [ 33,   7], [ 31,  12], [ 37,  23],
+    [ 31,  38], [ 20,  64], [ -9,  71], [ -7,  37],
+    [ -8,  44], [-11,  49], [-10,  56], [-12,  59],
+    [ -8,  63], [ -9,  67], [ -6,  68], [-10,  79],
+    [-22, 127], [-25, 127], [-25, 120], [-27, 127],
+    [-19, 114], [-23, 117], [-25, 118], [-26, 117],
+    [-24, 113], [-28, 118], [-31, 120], [-37, 124],
+    [-10,  94], [-15, 102], [-10,  99], [-13, 106],
+    [-50, 127], [ -5,  92], [ 17,  57], [ -5,  86],
+    [-13,  94], [-12,  91], [ -2,  77], [  0,  71],
+    [ -1,  73], [  4,  64], [ -7,  81], [  5,  64],
+    [ 15,  57], [  1,  67], [  0,  68], [-10,  67],
+    [  1,  68], [  0,  77], [  2,  64], [  0,  68],
+    [ -5,  78], [  7,  55], [  5,  59], [  2,  65],
+    [ 14,  54], [ 15,  44], [  5,  60], [  2,  70],
+    [-22, 127], [-25, 127], [-25, 120], [-27, 127],
+    [-19, 114], [-23, 117], [-25, 118], [-26, 117],
+    [-24, 113], [-28, 118], [-31, 120], [-37, 124],
+    [-10,  94], [-15, 102], [-10,  99], [-13, 106],
+    [-50, 127], [ -5,  92], [ 17,  57], [ -5,  86],
+    [-13,  94], [-12,  91], [ -2,  77], [  0,  71],
+    [ -1,  73], [  4,  64], [ -7,  81], [  5,  64],
+    [ 15,  57], [  1,  67], [  0,  68], [-10,  67],
+    [  1,  68], [  0,  77], [  2,  64], [  0,  68],
+    [ -5,  78], [  7,  55], [  5,  59], [  2,  65],
+    [ 14,  54], [ 15,  44], [  5,  60], [  2,  70],
+    [ 17, -13], [ 16,  -9], [ 17, -12], [ 27, -21],
+    [ 37, -30], [ 41, -40], [ 42, -41], [ 48, -47],
+    [ 39, -32], [ 46, -40], [ 52, -51], [ 46, -41],
+    [ 52, -39], [ 43, -19], [ 32,  11], [ 61, -55],
+    [ 56, -46], [ 62, -50], [ 81, -67], [ 45, -20],
+    [ 35,  -2], [ 28,  15], [ 34,   1], [ 39,   1],
+    [ 30,  17], [ 20,  38], [ 18,  45], [ 15,  54],
+    [  0,  79], [ 36, -16], [ 37, -14], [ 37, -17],
+    [ 32,   1], [ 34,  15], [ 29,  15], [ 24,  25],
+    [ 34,  22], [ 31,  16], [ 35,  18], [ 31,  28],
+    [ 33,  41], [ 36,  28], [ 27,  47], [ 21,  62],
+    [ 17, -13], [ 16,  -9], [ 17, -12], [ 27, -21],
+    [ 37, -30], [ 41, -40], [ 42, -41], [ 48, -47],
+    [ 39, -32], [ 46, -40], [ 52, -51], [ 46, -41],
+    [ 52, -39], [ 43, -19], [ 32,  11], [ 61, -55],
+    [ 56, -46], [ 62, -50], [ 81, -67], [ 45, -20],
+    [ 35,  -2], [ 28,  15], [ 34,   1], [ 39,   1],
+    [ 30,  17], [ 20,  38], [ 18,  45], [ 15,  54],
+    [  0,  79], [ 36, -16], [ 37, -14], [ 37, -17],
+    [ 32,   1], [ 34,  15], [ 29,  15], [ 24,  25],
+    [ 34,  22], [ 31,  16], [ 35,  18], [ 31,  28],
+    [ 33,  41], [ 36,  28], [ 27,  47], [ 21,  62],
+    [-24, 115], [-22,  82], [ -9,  62], [  0,  53],
+    [  0,  59], [-14,  85], [-13,  89], [-13,  94],
+    [-11,  92], [-29, 127], [-21, 100], [-14,  57],
+    [-12,  67], [-11,  71], [-10,  77], [-21,  85],
+    [-16,  88], [-23, 104], [-15,  98], [-37, 127],
+    [-10,  82], [ -8,  48], [ -8,  61], [ -8,  66],
+    [ -7,  70], [-14,  75], [-10,  79], [ -9,  83],
+    [-12,  92], [-18, 108], [-24, 115], [-22,  82],
+    [ -9,  62], [  0,  53], [  0,  59], [-14,  85],
+    [-13,  89], [-13,  94], [-11,  92], [-29, 127],
+    [-21, 100], [-14,  57], [-12,  67], [-11,  71],
+    [-10,  77], [-21,  85], [-16,  88], [-23, 104],
+    [-15,  98], [-37, 127], [-10,  82], [ -8,  48],
+    [ -8,  61], [ -8,  66], [ -7,  70], [-14,  75],
+    [-10,  79], [ -9,  83], [-12,  92], [-18, 108],
+    [ -5,  79], [-11, 104], [-11,  91], [-30, 127],
+    [ -5,  79], [-11, 104], [-11,  91], [-30, 127],
+    [ -5,  79], [-11, 104], [-11,  91], [-30, 127]
+  ]
+];
diff --git a/nihav-itu/src/codecs/h264/cavlc.rs b/nihav-itu/src/codecs/h264/cavlc.rs
new file mode 100644 (file)
index 0000000..4c598a4
--- /dev/null
@@ -0,0 +1,676 @@
+use nihav_core::codecs::{DecoderResult, DecoderError};
+use nihav_core::io::bitreader::*;
+use nihav_core::io::codebook::*;
+use nihav_core::io::intcode::*;
+use super::*;
+use super::dsp::{CHROMA_DC_SCAN, ZIGZAG, ZIGZAG1};
+use super::slice::SliceHeader;
+
+fn map_i_type(idx: usize) -> MBType {
+    if idx == 0 {
+        MBType::Intra4x4
+    } else if idx == 25 {
+        MBType::PCM
+    } else {
+        let imode = ((idx - 1) & 3) as u8;
+        let cbpc  = ((idx - 1) / 4) as u8;
+        let (cbpy, cbpc) = if cbpc >= 3 { (0xF, cbpc - 3) } else { (0x0, cbpc) };
+        MBType::Intra16x16(imode, cbpy, cbpc)
+    }
+}
+
+const NUM_I_TYPES: usize = 26;
+
+const P_TYPES: [MBType; 5] = [
+    MBType::P16x16, MBType::P16x8, MBType::P8x16, MBType::P8x8, MBType::P8x8Ref0
+];
+
+const B_TYPES: [MBType; 23] = [
+    MBType::Direct,
+    MBType::B16x16(BMode::L0),
+    MBType::B16x16(BMode::L1),
+    MBType::B16x16(BMode::Bi),
+    MBType::B16x8(BMode::L0, BMode::L0),
+    MBType::B8x16(BMode::L0, BMode::L0),
+    MBType::B16x8(BMode::L1, BMode::L1),
+    MBType::B8x16(BMode::L1, BMode::L1),
+    MBType::B16x8(BMode::L0, BMode::L1),
+    MBType::B8x16(BMode::L0, BMode::L1),
+    MBType::B16x8(BMode::L1, BMode::L0),
+    MBType::B8x16(BMode::L1, BMode::L0),
+    MBType::B16x8(BMode::L0, BMode::Bi),
+    MBType::B8x16(BMode::L0, BMode::Bi),
+    MBType::B16x8(BMode::L1, BMode::Bi),
+    MBType::B8x16(BMode::L1, BMode::Bi),
+    MBType::B16x8(BMode::Bi, BMode::L0),
+    MBType::B8x16(BMode::Bi, BMode::L0),
+    MBType::B16x8(BMode::Bi, BMode::L1),
+    MBType::B8x16(BMode::Bi, BMode::L1),
+    MBType::B16x8(BMode::Bi, BMode::Bi),
+    MBType::B8x16(BMode::Bi, BMode::Bi),
+    MBType::B8x8,
+];
+
+pub fn decode_mb_type_cavlc(br: &mut BitReader, slice_hdr: &SliceHeader) -> DecoderResult<MBType> {
+    let mb_type_id                      = br.read_ue()? as usize;
+    match slice_hdr.slice_type {
+        SliceType::I => {
+            validate!(mb_type_id < NUM_I_TYPES);
+            Ok(map_i_type(mb_type_id))
+        },
+        SliceType::SI => {
+            validate!(mb_type_id < NUM_I_TYPES + 1);
+            if mb_type_id == 0 {
+                Ok(MBType::Intra4x4) // special SI one
+            } else {
+                Ok(map_i_type(mb_type_id - 1))
+            }
+        },
+        SliceType::P | SliceType::SP => {
+            validate!(mb_type_id < NUM_I_TYPES + P_TYPES.len());
+            if mb_type_id < P_TYPES.len() {
+                Ok(P_TYPES[mb_type_id])
+            } else {
+                Ok(map_i_type(mb_type_id - P_TYPES.len()))
+            }
+        },
+        SliceType::B => {
+            validate!(mb_type_id < NUM_I_TYPES + B_TYPES.len());
+            if mb_type_id < B_TYPES.len() {
+                Ok(B_TYPES[mb_type_id])
+            } else {
+                Ok(map_i_type(mb_type_id - B_TYPES.len()))
+            }
+        },
+    }
+}
+
+fn read_refs(br: &mut BitReader, dst: &mut [PicRef], num_refs: usize) -> DecoderResult<()> {
+    if num_refs > 1 {
+        for pic_ref in dst.iter_mut() {
+            *pic_ref                                = PicRef::new(br.read_te(num_refs as u32 - 1)? as u8);
+        }
+    } else {
+        for pic_ref in dst.iter_mut() {
+            *pic_ref = ZERO_REF;
+        }
+    }
+    Ok(())
+}
+
+fn read_mvs(br: &mut BitReader, mvs: &mut [MV]) -> DecoderResult<()> {
+    for mv in mvs.iter_mut() {
+        mv.x                                        = br.read_se()? as i16;
+        mv.y                                        = br.read_se()? as i16;
+    }
+    Ok(())
+}
+
+pub fn decode_mb_pred_cavlc(br: &mut BitReader, slice_hdr: &SliceHeader, mb_type: MBType, sstate: &mut SliceState, mb_info: &mut CurrentMBInfo) -> DecoderResult<()> {
+    mb_info.mb_type = mb_type;
+    let num_l0 = slice_hdr.num_ref_idx_l0_active;
+    let num_l1 = slice_hdr.num_ref_idx_l1_active;
+    match mb_type {
+        MBType::Intra4x4 => {
+            for &(x, y) in I4X4_SCAN.iter() {
+                let x = x as usize;
+                let y = y as usize;
+                let top_pred  = sstate.get_top_blk4(x + y * 4).ipred;
+                let left_pred = sstate.get_left_blk4(x + y * 4).ipred;
+
+                let top_idx = top_pred.into_pred_idx();
+                let left_idx = left_pred.into_pred_idx();
+                let pred_mode = top_idx.min(left_idx);
+                let mut pred_mode = if pred_mode != -1 { pred_mode as u8 } else { 2 };
+                if !br.read_bool()? {
+                    let new_mode                    = br.read(3)? as u8;
+                    pred_mode = if new_mode >= pred_mode {
+                            new_mode + 1
+                        } else { new_mode };
+                }
+                mb_info.ipred[x + y * 4] = pred_mode.into();
+                sstate.get_cur_blk4(x + y * 4).ipred = (pred_mode as u8).into();
+            }
+            mb_info.chroma_ipred                    = br.read_ue_lim(3)? as u8;
+        },
+        MBType::Intra8x8 => {
+            for part in 0..4 {
+                let blk4 = (part & 1) * 2 + (part & 2) * 4;
+                let top_pred  = sstate.get_top_blk4(blk4).ipred;
+                let left_pred = sstate.get_left_blk4(blk4).ipred;
+
+                let top_idx = top_pred.into_pred_idx();
+                let left_idx = left_pred.into_pred_idx();
+                let pred_mode = top_idx.min(left_idx);
+                let mut pred_mode = if pred_mode != -1 { pred_mode as u8 } else { 2 };
+                if !br.read_bool()? {
+                    let new_mode                    = br.read(3)? as u8;
+                    pred_mode = if new_mode >= pred_mode {
+                            new_mode + 1
+                        } else { new_mode };
+                }
+                mb_info.ipred[blk4]     = pred_mode.into();
+                mb_info.ipred[blk4 + 1] = pred_mode.into();
+                mb_info.ipred[blk4 + 4] = pred_mode.into();
+                mb_info.ipred[blk4 + 5] = pred_mode.into();
+                sstate.get_cur_blk4(blk4).ipred = (pred_mode as u8).into();
+                sstate.get_cur_blk4(blk4 + 1).ipred = (pred_mode as u8).into();
+                sstate.get_cur_blk4(blk4 + 4).ipred = (pred_mode as u8).into();
+                sstate.get_cur_blk4(blk4 + 5).ipred = (pred_mode as u8).into();
+            }
+            mb_info.chroma_ipred                    = br.read_ue_lim(3)? as u8;
+        },
+        MBType::Intra16x16(_ipred, _, _) => {
+            sstate.fill_ipred(IntraPredMode::DC);
+            mb_info.chroma_ipred                    = br.read_ue_lim(3)? as u8;
+        },
+        MBType::P16x16 | MBType::P16x8 | MBType::P8x16 => {
+            let nparts = mb_type.num_parts();
+            read_refs(br, &mut mb_info.ref_l0[..nparts], num_l0)?;
+            read_mvs(br, &mut mb_info.mv_l0[..nparts])?;
+        },
+        MBType::B16x16(mode) => {
+            if mode != BMode::L1 {
+                read_refs(br, &mut mb_info.ref_l0[..1], num_l0)?;
+            }
+            if mode != BMode::L0 {
+                read_refs(br, &mut mb_info.ref_l1[..1], num_l1)?;
+            }
+            if mode != BMode::L1 {
+                read_mvs(br, &mut mb_info.mv_l0[..1])?;
+            }
+            if mode != BMode::L0 {
+                read_mvs(br, &mut mb_info.mv_l1[..1])?;
+            }
+        },
+        MBType::B16x8(mode0, mode1) | MBType::B8x16(mode0, mode1) => {
+            if num_l0 > 1 {
+                if mode0 != BMode::L1 {
+                    read_refs(br, &mut mb_info.ref_l0[0..1], num_l0)?;
+                }
+                if mode1 != BMode::L1 {
+                    read_refs(br, &mut mb_info.ref_l0[1..2], num_l0)?;
+                }
+            }
+            if num_l1 > 1 {
+                if mode0 != BMode::L0 {
+                    read_refs(br, &mut mb_info.ref_l1[0..1], num_l1)?;
+                }
+                if mode1 != BMode::L0 {
+                    read_refs(br, &mut mb_info.ref_l1[1..2], num_l1)?;
+                }
+            }
+            if mode0 != BMode::L1 {
+                read_mvs(br, &mut mb_info.mv_l0[0..1])?;
+            }
+            if mode1 != BMode::L1 {
+                read_mvs(br, &mut mb_info.mv_l0[1..2])?;
+            }
+            if mode0 != BMode::L0 {
+                read_mvs(br, &mut mb_info.mv_l1[0..1])?;
+            }
+            if mode1 != BMode::L0 {
+                read_mvs(br, &mut mb_info.mv_l1[1..2])?;
+            }
+        },
+        MBType::P8x8 | MBType::P8x8Ref0 | MBType::B8x8 => {
+            for sub_mb in mb_info.sub_mb_type.iter_mut() {
+                *sub_mb = decode_sub_mb_type(br, mb_type != MBType::B8x8)?;
+            }
+            for (part, &sub_mb) in mb_info.sub_mb_type.iter().enumerate() {
+                if num_l0 > 1 && mb_type != MBType::P8x8Ref0 && sub_mb != SubMBType::Direct8x8 && !sub_mb.is_l1() {
+                    read_refs(br, &mut mb_info.ref_l0[part..][..1], num_l0)?;
+                }
+            }
+            for (part, &sub_mb) in mb_info.sub_mb_type.iter().enumerate() {
+                if num_l1 > 1 && sub_mb != SubMBType::Direct8x8 && !sub_mb.is_l0() {
+                    read_refs(br, &mut mb_info.ref_l1[part..][..1], num_l1)?;
+                }
+            }
+            for (part, &sub_mb) in mb_info.sub_mb_type.iter().enumerate() {
+                if sub_mb != SubMBType::Direct8x8 && !sub_mb.is_l1() {
+                    let num_subparts = sub_mb.num_parts();
+                    read_mvs(br, &mut mb_info.mv_l0[part * 4..][..num_subparts])?;
+                }
+            }
+            for (part, &sub_mb) in mb_info.sub_mb_type.iter().enumerate() {
+                if sub_mb != SubMBType::Direct8x8 && !sub_mb.is_l0() {
+                    let num_subparts = sub_mb.num_parts();
+                    read_mvs(br, &mut mb_info.mv_l1[part * 4..][..num_subparts])?;
+                }
+            }
+        },
+        _ => {},
+    };
+    Ok(())
+}
+
+fn decode_sub_mb_type(br: &mut BitReader, is_p: bool) -> DecoderResult<SubMBType> {
+    const SUB_MB_P_TYPES: [SubMBType; 4] = [
+        SubMBType::P8x8, SubMBType::P8x4, SubMBType::P4x8, SubMBType::P4x4
+    ];
+    const SUB_MB_B_TYPES: [SubMBType; 13] = [
+        SubMBType::Direct8x8,
+        SubMBType::B8x8(BMode::L0), SubMBType::B8x8(BMode::L1), SubMBType::B8x8(BMode::Bi),
+        SubMBType::B8x4(BMode::L0), SubMBType::B4x8(BMode::L0),
+        SubMBType::B8x4(BMode::L1), SubMBType::B4x8(BMode::L1),
+        SubMBType::B8x4(BMode::Bi), SubMBType::B4x8(BMode::Bi),
+        SubMBType::B4x4(BMode::L0), SubMBType::B4x4(BMode::L1), SubMBType::B4x4(BMode::Bi),
+    ];
+    if is_p {
+        let idx                                     = br.read_ue_lim(SUB_MB_P_TYPES.len() as u32 - 1)? as usize;
+        Ok(SUB_MB_P_TYPES[idx])
+    } else {
+        let idx                                     = br.read_ue_lim(SUB_MB_B_TYPES.len() as u32 - 1)? as usize;
+        Ok(SUB_MB_B_TYPES[idx])
+    }
+}
+
+fn map_coeff_token(val: u8) -> (usize, usize) {
+    const TRAILING_ONES: [u8; 6] = [ 0, 0, 1, 0, 1, 2 ];
+    const TOTAL_COEFF: [u8; 6] = [0, 1, 1, 2, 2, 2];
+
+    if val < 6 {
+        (TRAILING_ONES[val as usize] as usize, TOTAL_COEFF[val as usize] as usize)
+    } else {
+        (((val - 6) & 3) as usize, ((val + 6) >> 2) as usize)
+    }
+}
+
+fn decode_coeffs(br: &mut BitReader, coeffs: &mut [i16], scan: &[usize], cb: &Codebook<u8>, tables: &CAVLCTables) -> DecoderResult<u8> {
+    let coeff_token                                 = br.read_cb(cb)?;
+    let (trail_ones, total_coeff) = map_coeff_token(coeff_token);
+    let mut level = [0i16; 16];
+    let mut run = [0u8; 16];
+    if total_coeff > 0 {
+        let mut suffix_length = (total_coeff > 10 && trail_ones < 3) as u8;
+        for i in 0..total_coeff {
+            if i < trail_ones {
+                if !br.read_bool()? {
+                    level[i] = 1;
+                } else {
+                    level[i] = -1;
+                }
+            } else {
+                let level_prefix                    = br.read_code(UintCodeType::UnaryZeroes)?;
+                validate!(level_prefix <= 19);
+                let mut level_code = level_prefix.min(15) << suffix_length;
+                if suffix_length > 0 || level_prefix >= 14 {
+                    let level_suffix_size = if level_prefix == 14 && suffix_length == 0 {
+                            4
+                        } else if level_prefix >= 15 {
+                            (level_prefix - 3) as u8
+                        } else {
+                            suffix_length
+                        };
+                    let level_suffix                = br.read(level_suffix_size)?;
+                    level_code += level_suffix;
+                }
+                if level_prefix >= 15 && suffix_length == 0 {
+                    level_code += 15;
+                }
+                if level_prefix >= 16 {
+                    level_code += (1 << (level_prefix - 3)) - 4096;
+                }
+                if i == trail_ones && trail_ones < 3 {
+                    level_code += 2;
+                }
+                level[i] = if (level_code & 1) == 0 {
+                        (level_code as i32 + 2) >> 1
+                    } else {
+                        -((level_code as i32 + 1) >> 1)
+                    } as i16;
+                if suffix_length == 0 {
+                    suffix_length = 1;
+                }
+                if level[i].abs() > (3 << (suffix_length - 1)) && suffix_length < 6 {
+                    suffix_length += 1;
+                }
+            }
+        }
+        let mut zeros_left = if total_coeff < coeffs.len() {
+                let cb = if coeffs.len() > 4 {
+                        &tables.total_zeros_cb[total_coeff - 1]
+                    } else {
+                        &tables.cdc_total_zeros_cb[total_coeff - 1]
+                    };
+                                                      br.read_cb(cb)?
+            } else { 0 };
+        for i in 0..total_coeff - 1 {
+            if zeros_left > 0 {
+                let run_before                      = br.read_cb(&tables.run_before_cb[(zeros_left - 1).min(6) as usize])?;
+                run[i] = run_before;
+                zeros_left -= run_before;
+            }
+        }
+        run[total_coeff - 1] = zeros_left;
+        let mut idx = 0;
+        for i in (0..total_coeff).rev() {
+            idx += run[i] as usize;
+            coeffs[scan[idx]] = level[i];
+            idx += 1;
+        }
+    }
+    Ok(total_coeff as u8)
+}
+
+fn decode_block(br: &mut BitReader, coeffs: &mut [i16; 16], cb: &Codebook<u8>, tables: &CAVLCTables) -> DecoderResult<u8> {
+    decode_coeffs(br, coeffs, &ZIGZAG, cb, tables)
+}
+
+fn decode_block_ac(br: &mut BitReader, coeffs: &mut [i16; 16], cb: &Codebook<u8>, tables: &CAVLCTables) -> DecoderResult<u8> {
+    decode_coeffs(br, &mut coeffs[1..], &ZIGZAG1, cb, tables)
+}
+
+fn decode_chroma_dc(br: &mut BitReader, coeffs: &mut [i16; 4], cb: &Codebook<u8>, tables: &CAVLCTables) -> DecoderResult<u8> {
+    decode_coeffs(br, coeffs, &CHROMA_DC_SCAN, cb, tables)
+}
+
+fn get_cb_idx(nc: u8) -> usize {
+    match nc {
+        0 | 1 => 0,
+        2 | 3 => 1,
+        4..=7 => 2,
+        _     => 3,
+    }
+}
+
+pub fn decode_residual_cavlc(br: &mut BitReader, sstate: &mut SliceState, mb_info: &mut CurrentMBInfo, tables: &CAVLCTables) -> DecoderResult<()> {
+    if mb_info.mb_type.is_intra16x16() {
+        let mut top_nc  = sstate.get_top_blk4(0).ncoded;
+        let mut left_nc = sstate.get_left_blk4(0).ncoded;
+        if !sstate.has_left {
+            left_nc = top_nc;
+        } else if !sstate.has_top {
+            top_nc = left_nc;
+        }
+        let cb_idx = get_cb_idx((left_nc + top_nc + 1) >> 1);
+
+        let nc = decode_block(br, &mut mb_info.coeffs[24], &tables.coeff_token_cb[cb_idx], tables)?;
+        mb_info.coded[24] = nc != 0;
+    }
+    for blk8 in 0..4 {
+        if (mb_info.cbpy & (1 << blk8)) != 0 {
+            for blk4 in 0..4 {
+                let bx =  (blk8 & 1) * 2 + (blk4 & 1);
+                let by = ((blk8 & 2) * 2 + (blk4 & 2)) >> 1;
+                let blk_no = bx + by * 4;
+
+                let mut top_nc  = sstate.get_top_blk4(blk_no).ncoded;
+                let mut left_nc = sstate.get_left_blk4(blk_no).ncoded;
+                if bx == 0 && !sstate.has_left {
+                    left_nc = top_nc;
+                } else if by == 0 && !sstate.has_top {
+                    top_nc = left_nc;
+                }
+                let cb_idx = get_cb_idx((left_nc + top_nc + 1) >> 1);
+
+                let nc = if mb_info.mb_type.is_intra16x16() {
+                        decode_block_ac(br, &mut mb_info.coeffs[blk_no], &tables.coeff_token_cb[cb_idx], tables)?
+                    } else {
+                        decode_block(br, &mut mb_info.coeffs[blk_no], &tables.coeff_token_cb[cb_idx], tables)?
+                    };
+                sstate.get_cur_blk4(blk_no).ncoded = nc;
+                mb_info.coded[blk_no] = nc != 0;
+            }
+        }
+    }
+    if mb_info.transform_size_8x8 {
+        for y in 0..2 {
+            for x in 0..2 {
+                let b0 = &mb_info.coeffs[x     + y * 8];
+                let b1 = &mb_info.coeffs[x + 1 + y * 8];
+                let b2 = &mb_info.coeffs[x + 4 + y * 8];
+                let b3 = &mb_info.coeffs[x + 5 + y * 8];
+                let dst = &mut mb_info.coeffs8x8[x + y * 2].coeffs;
+                for (dst, (s0, s1)) in dst.chunks_mut(8).zip(b0.chunks(4).zip(b1.chunks(4))) {
+                    let (d0, d1) = dst.split_at_mut(4);
+                    d0.copy_from_slice(s0);
+                    d1.copy_from_slice(s1);
+                }
+                for (dst, (s0, s1)) in dst.chunks_mut(8).skip(4).zip(b2.chunks(4).zip(b3.chunks(4))) {
+                    let (d0, d1) = dst.split_at_mut(4);
+                    d0.copy_from_slice(s0);
+                    d1.copy_from_slice(s1);
+                }
+            }
+        }
+    }
+    for chroma in 0..2 {
+        if (mb_info.cbpc & 3) != 0 {
+            decode_chroma_dc(br, &mut mb_info.chroma_dc[chroma], &tables.cdc_coeff_token_cb, tables)?;
+        }
+    }
+    for chroma in 0..2 {
+        if (mb_info.cbpc & 2) != 0 {
+            for blk4 in 0..4 {
+                let blk_no = 16 + chroma * 4 + blk4;
+                let bx = blk4 & 1;
+                let by = blk4 >> 1;
+
+                let mut top_nc  = sstate.get_top_blk8(blk4).ncoded_c[chroma];
+                let mut left_nc = sstate.get_left_blk8(blk4).ncoded_c[chroma];
+                if bx == 0 && !sstate.has_left {
+                    left_nc = top_nc;
+                } else if by == 0 && !sstate.has_top {
+                    top_nc = left_nc;
+                }
+                let cb_idx = get_cb_idx((left_nc + top_nc + 1) >> 1);
+
+                let nc = decode_block_ac(br, &mut mb_info.coeffs[blk_no], &tables.coeff_token_cb[cb_idx], tables)?;
+                sstate.get_cur_blk8(blk4).ncoded_c[chroma] = nc;
+                mb_info.coded[blk_no] = nc != 0;
+            }
+        }
+    }
+
+    Ok(())
+}
+
+pub struct CAVLCTables {
+    coeff_token_cb:     [Codebook<u8>; 4],
+    cdc_coeff_token_cb: Codebook<u8>,
+    total_zeros_cb:     [Codebook<u8>; 15],
+    cdc_total_zeros_cb: [Codebook<u8>; 3],
+    run_before_cb:      [Codebook<u8>; 7],
+}
+
+fn map_idx(idx: usize) -> u8 { idx as u8 }
+
+macro_rules! create_cb {
+    ($bits: expr, $lens: expr) => {{
+        let mut reader = TableCodebookDescReader::new($bits, $lens, map_idx);
+        Codebook::new(&mut reader, CodebookMode::MSB).unwrap()
+    }}
+}
+
+impl CAVLCTables {
+    pub fn new() -> Self {
+        /*let mut reader = TableCodebookDescReader::new(&COEFF_TOKEN_BITS[0], &COEFF_TOKEN_LENS[0], map_idx);
+        let coef_tok_cb0 = Codebook::new(&mut reader, CodebookMode::MSB).unwrap();
+        let mut reader = TableCodebookDescReader::new(&COEFF_TOKEN_BITS[1], &COEFF_TOKEN_LENS[1], map_idx);
+        let coef_tok_cb1 = Codebook::new(&mut reader, CodebookMode::MSB).unwrap();
+        let mut reader = TableCodebookDescReader::new(&COEFF_TOKEN_BITS[2], &COEFF_TOKEN_LENS[2], map_idx);
+        let coef_tok_cb2 = Codebook::new(&mut reader, CodebookMode::MSB).unwrap();
+        let mut reader = TableCodebookDescReader::new(&COEFF_TOKEN_BITS[3], &COEFF_TOKEN_LENS[3], map_idx);
+        let coef_tok_cb3 = Codebook::new(&mut reader, CodebookMode::MSB).unwrap();
+
+        let mut reader = TableCodebookDescReader::new(&CHROMA_DC_COEFF_TOKEN_BITS, &CHROMA_DC_COEFF_TOKEN_LENS, map_idx);
+        let cdc_coeff_token_cb = Codebook::new(&mut reader, CodebookMode::MSB).unwrap();*/
+
+        let coef_tok_cb0 = create_cb!(&COEFF_TOKEN_BITS[0], &COEFF_TOKEN_LENS[0]);
+        let coef_tok_cb1 = create_cb!(&COEFF_TOKEN_BITS[1], &COEFF_TOKEN_LENS[1]);
+        let coef_tok_cb2 = create_cb!(&COEFF_TOKEN_BITS[2], &COEFF_TOKEN_LENS[2]);
+        let coef_tok_cb3 = create_cb!(&COEFF_TOKEN_BITS[3], &COEFF_TOKEN_LENS[3]);
+
+        let cdc_coeff_token_cb = create_cb!(&CHROMA_DC_COEFF_TOKEN_BITS, &CHROMA_DC_COEFF_TOKEN_LENS);
+
+        let total_zeros0  = create_cb!(&TOTAL_ZERO_BITS[ 0], &TOTAL_ZERO_LENS[ 0]);
+        let total_zeros1  = create_cb!(&TOTAL_ZERO_BITS[ 1], &TOTAL_ZERO_LENS[ 1]);
+        let total_zeros2  = create_cb!(&TOTAL_ZERO_BITS[ 2], &TOTAL_ZERO_LENS[ 2]);
+        let total_zeros3  = create_cb!(&TOTAL_ZERO_BITS[ 3], &TOTAL_ZERO_LENS[ 3]);
+        let total_zeros4  = create_cb!(&TOTAL_ZERO_BITS[ 4], &TOTAL_ZERO_LENS[ 4]);
+        let total_zeros5  = create_cb!(&TOTAL_ZERO_BITS[ 5], &TOTAL_ZERO_LENS[ 5]);
+        let total_zeros6  = create_cb!(&TOTAL_ZERO_BITS[ 6], &TOTAL_ZERO_LENS[ 6]);
+        let total_zeros7  = create_cb!(&TOTAL_ZERO_BITS[ 7], &TOTAL_ZERO_LENS[ 7]);
+        let total_zeros8  = create_cb!(&TOTAL_ZERO_BITS[ 8], &TOTAL_ZERO_LENS[ 8]);
+        let total_zeros9  = create_cb!(&TOTAL_ZERO_BITS[ 9], &TOTAL_ZERO_LENS[ 9]);
+        let total_zeros10 = create_cb!(&TOTAL_ZERO_BITS[10], &TOTAL_ZERO_LENS[10]);
+        let total_zeros11 = create_cb!(&TOTAL_ZERO_BITS[11], &TOTAL_ZERO_LENS[11]);
+        let total_zeros12 = create_cb!(&TOTAL_ZERO_BITS[12], &TOTAL_ZERO_LENS[12]);
+        let total_zeros13 = create_cb!(&TOTAL_ZERO_BITS[13], &TOTAL_ZERO_LENS[13]);
+        let total_zeros14 = create_cb!(&TOTAL_ZERO_BITS[14], &TOTAL_ZERO_LENS[14]);
+
+        let cdc_total_zeros_cb0 = create_cb!(&CHROMA_DC_TOTAL_ZERO_BITS[0], &CHROMA_DC_TOTAL_ZERO_LENS[0]);
+        let cdc_total_zeros_cb1 = create_cb!(&CHROMA_DC_TOTAL_ZERO_BITS[1], &CHROMA_DC_TOTAL_ZERO_LENS[1]);
+        let cdc_total_zeros_cb2 = create_cb!(&CHROMA_DC_TOTAL_ZERO_BITS[2], &CHROMA_DC_TOTAL_ZERO_LENS[2]);
+
+        let run_before_cb0 = create_cb!(&RUN_BEFORE_BITS[0], &RUN_BEFORE_LENS[0]);
+        let run_before_cb1 = create_cb!(&RUN_BEFORE_BITS[1], &RUN_BEFORE_LENS[1]);
+        let run_before_cb2 = create_cb!(&RUN_BEFORE_BITS[2], &RUN_BEFORE_LENS[2]);
+        let run_before_cb3 = create_cb!(&RUN_BEFORE_BITS[3], &RUN_BEFORE_LENS[3]);
+        let run_before_cb4 = create_cb!(&RUN_BEFORE_BITS[4], &RUN_BEFORE_LENS[4]);
+        let run_before_cb5 = create_cb!(&RUN_BEFORE_BITS[5], &RUN_BEFORE_LENS[5]);
+        let run_before_cb6 = create_cb!(&RUN_BEFORE_BITS[6], &RUN_BEFORE_LENS[6]);
+
+        Self {
+            coeff_token_cb: [coef_tok_cb0, coef_tok_cb1, coef_tok_cb2, coef_tok_cb3],
+            cdc_coeff_token_cb,
+            total_zeros_cb: [total_zeros0,  total_zeros1,  total_zeros2,
+                             total_zeros3,  total_zeros4,  total_zeros5,
+                             total_zeros6,  total_zeros7,  total_zeros8,
+                             total_zeros9,  total_zeros10, total_zeros11,
+                             total_zeros12, total_zeros13, total_zeros14 ],
+            cdc_total_zeros_cb: [cdc_total_zeros_cb0, cdc_total_zeros_cb1, cdc_total_zeros_cb2],
+            run_before_cb:  [ run_before_cb0, run_before_cb1, run_before_cb2,
+                              run_before_cb3, run_before_cb4, run_before_cb5,
+                              run_before_cb6 ],
+        }
+    }
+}
+
+const COEFF_TOKEN_BITS: [[u16; 62]; 4] = [
+  [
+    0x01, 0x05, 0x01, 0x07, 0x04, 0x01, 0x07, 0x06,
+    0x05, 0x03, 0x07, 0x06, 0x05, 0x03, 0x07, 0x06,
+    0x05, 0x04, 0x0F, 0x06, 0x05, 0x04, 0x0B, 0x0E,
+    0x05, 0x04, 0x08, 0x0A, 0x0D, 0x04, 0x0F, 0x0E,
+    0x09, 0x04, 0x0B, 0x0A, 0x0D, 0x0C, 0x0F, 0x0E,
+    0x09, 0x0C, 0x0B, 0x0A, 0x0D, 0x08, 0x0F, 0x01,
+    0x09, 0x0C, 0x0B, 0x0E, 0x0D, 0x08, 0x07, 0x0A,
+    0x09, 0x0C, 0x04, 0x06, 0x05, 0x08
+  ], [
+    0x03, 0x0B, 0x02, 0x07, 0x07, 0x03, 0x07, 0x0A,
+    0x09, 0x05, 0x07, 0x06, 0x05, 0x04, 0x04, 0x06,
+    0x05, 0x06, 0x07, 0x06, 0x05, 0x08, 0x0F, 0x06,
+    0x05, 0x04, 0x0B, 0x0E, 0x0D, 0x04, 0x0F, 0x0A,
+    0x09, 0x04, 0x0B, 0x0E, 0x0D, 0x0C, 0x08, 0x0A,
+    0x09, 0x08, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A,
+    0x09, 0x0C, 0x07, 0x0B, 0x06, 0x08, 0x09, 0x08,
+    0x0A, 0x01, 0x07, 0x06, 0x05, 0x04
+  ], [
+    0x0F, 0x0F, 0x0E, 0x0B, 0x0F, 0x0D, 0x08, 0x0C,
+    0x0E, 0x0C, 0x0F, 0x0A, 0x0B, 0x0B, 0x0B, 0x08,
+    0x09, 0x0A, 0x09, 0x0E, 0x0D, 0x09, 0x08, 0x0A,
+    0x09, 0x08, 0x0F, 0x0E, 0x0D, 0x0D, 0x0B, 0x0E,
+    0x0A, 0x0C, 0x0F, 0x0A, 0x0D, 0x0C, 0x0B, 0x0E,
+    0x09, 0x0C, 0x08, 0x0A, 0x0D, 0x08, 0x0D, 0x07,
+    0x09, 0x0C, 0x09, 0x0C, 0x0B, 0x0A, 0x05, 0x08,
+    0x07, 0x06, 0x01, 0x04, 0x03, 0x02
+  ], [
+    0x03, 0x00, 0x01, 0x04, 0x05, 0x06, 0x08, 0x09,
+    0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11,
+    0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
+    0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21,
+    0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
+    0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31,
+    0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+    0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F
+  ]
+];
+const COEFF_TOKEN_LENS: [[u8; 62]; 4] = [
+  [
+     1,  6,  2,  8,  6,  3,  9,  8,  7,  5, 10,  9,  8,  6, 11, 10,
+     9,  7, 13, 11, 10,  8, 13, 13, 11,  9, 13, 13, 13, 10, 14, 14,
+    13, 11, 14, 14, 14, 13, 15, 15, 14, 14, 15, 15, 15, 14, 16, 15,
+    15, 15, 16, 16, 16, 15, 16, 16, 16, 16, 16, 16, 16, 16
+  ], [
+     2,  6,  2,  6,  5,  3,  7,  6,  6,  4,  8,  6,  6,  4,  8,  7,
+     7,  5,  9,  8,  8,  6, 11,  9,  9,  6, 11, 11, 11,  7, 12, 11,
+    11,  9, 12, 12, 12, 11, 12, 12, 12, 11, 13, 13, 13, 12, 13, 13,
+    13, 13, 13, 14, 13, 13, 14, 14, 14, 13, 14, 14, 14, 14
+  ], [
+     4,  6,  4,  6,  5,  4,  6,  5,  5,  4,  7,  5,  5,  4,  7,  5,
+     5,  4,  7,  6,  6,  4,  7,  6,  6,  4,  8,  7,  7,  5,  8,  8,
+     7,  6,  9,  8,  8,  7,  9,  9,  8,  8,  9,  9,  9,  8, 10,  9,
+     9,  9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10
+  ], [ 6; 62 ]
+];
+
+const CHROMA_DC_COEFF_TOKEN_BITS: [u8; 14] = [
+    1, 7, 1, 4, 6, 1, 3, 3, 2, 5, 2, 3, 2, 0
+];
+const CHROMA_DC_COEFF_TOKEN_LENS: [u8; 14] = [
+    2, 6, 1, 6, 6, 3, 6, 7, 7, 6, 6, 8, 8, 7
+];
+
+const TOTAL_ZERO_BITS: [[u8; 16]; 15] = [
+    [ 1, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 1 ],
+    [ 7, 6, 5, 4, 3, 5, 4, 3, 2, 3, 2, 3, 2, 1, 0, 0 ],
+    [ 5, 7, 6, 5, 4, 3, 4, 3, 2, 3, 2, 1, 1, 0, 0, 0 ],
+    [ 3, 7, 5, 4, 6, 5, 4, 3, 3, 2, 2, 1, 0, 0, 0, 0 ],
+    [ 5, 4, 3, 7, 6, 5, 4, 3, 2, 1, 1, 0, 0, 0, 0, 0 ],
+    [ 1, 1, 7, 6, 5, 4, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0 ],
+    [ 1, 1, 5, 4, 3, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0 ],
+    [ 1, 1, 1, 3, 3, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0 ],
+    [ 1, 0, 1, 3, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 ],
+    [ 1, 0, 1, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
+    [ 0, 1, 1, 2, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
+    [ 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
+    [ 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
+    [ 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
+    [ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
+];
+const TOTAL_ZERO_LENS: [[u8; 16]; 15] = [
+    [ 1, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 9 ],
+    [ 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 6, 6, 6, 6, 0 ],
+    [ 4, 3, 3, 3, 4, 4, 3, 3, 4, 5, 5, 6, 5, 6, 0, 0 ],
+    [ 5, 3, 4, 4, 3, 3, 3, 4, 3, 4, 5, 5, 5, 0, 0, 0 ],
+    [ 4, 4, 4, 3, 3, 3, 3, 3, 4, 5, 4, 5, 0, 0, 0, 0 ],
+    [ 6, 5, 3, 3, 3, 3, 3, 3, 4, 3, 6, 0, 0, 0, 0, 0 ],
+    [ 6, 5, 3, 3, 3, 2, 3, 4, 3, 6, 0, 0, 0, 0, 0, 0 ],
+    [ 6, 4, 5, 3, 2, 2, 3, 3, 6, 0, 0, 0, 0, 0, 0, 0 ],
+    [ 6, 6, 4, 2, 2, 3, 2, 5, 0, 0, 0, 0, 0, 0, 0, 0 ],
+    [ 5, 5, 3, 2, 2, 2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
+    [ 4, 4, 3, 3, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
+    [ 4, 4, 2, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
+    [ 3, 3, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
+    [ 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
+    [ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
+];
+
+const CHROMA_DC_TOTAL_ZERO_BITS: [[u8; 4]; 3] = [
+    [ 1, 1, 1, 0 ], [ 1, 1, 0, 0 ], [ 1, 0, 0, 0 ]
+];
+const CHROMA_DC_TOTAL_ZERO_LENS: [[u8; 4]; 3] = [
+    [ 1, 2, 3, 3 ], [ 1, 2, 2, 0 ], [ 1, 1, 0, 0 ]
+];
+
+const RUN_BEFORE_BITS: [[u8; 15]; 7] = [
+    [ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
+    [ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
+    [ 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
+    [ 3, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
+    [ 3, 2, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
+    [ 3, 0, 1, 3, 2, 5, 4, 0, 0, 0, 0, 0, 0, 0, 0 ],
+    [ 7, 6, 5, 4, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1 ]
+];
+const RUN_BEFORE_LENS: [[u8; 15]; 7] = [
+    [ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  0,  0 ],
+    [ 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  0,  0 ],
+    [ 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0,  0,  0 ],
+    [ 2, 2, 2, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0,  0,  0 ],
+    [ 2, 2, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0,  0,  0 ],
+    [ 2, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0,  0,  0 ],
+    [ 3, 3, 3, 3, 3, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11 ]
+];
diff --git a/nihav-itu/src/codecs/h264/dsp.rs b/nihav-itu/src/codecs/h264/dsp.rs
new file mode 100644 (file)
index 0000000..0b3c1e1
--- /dev/null
@@ -0,0 +1,1326 @@
+use nihav_core::frame::*;
+use nihav_codec_support::codecs::blockdsp::*;
+use nihav_codec_support::codecs::MV;
+
+pub const CHROMA_QUANTS: [u8; 52] = [
+     0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
+    16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 29, 30,
+    31, 32, 32, 33, 34, 34, 35, 35, 36, 36, 37, 37, 37, 38, 38, 38,
+    39, 39, 39, 39
+];
+
+pub const CHROMA_DC_SCAN: [usize; 4] = [ 0, 1, 2, 3];
+pub const ZIGZAG: [usize; 16] = [
+    0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15
+];
+pub const ZIGZAG1: [usize; 15] = [
+    0, 3, 7, 4, 1, 2, 5, 8, 11, 12, 9, 6, 10, 13, 14
+];
+/*pub const IL_SCAN: [usize; 16] = [
+    0, 4, 1, 8, 12, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15
+];*/
+pub const ZIGZAG8X8: [usize; 64] = [
+     0,  1,  8, 16,  9,  2,  3, 10,
+    17, 24, 32, 25, 18, 11,  4,  5,
+    12, 19, 26, 33, 40, 48, 41, 34,
+    27, 20, 13,  6,  7, 14, 21, 28,
+    35, 42, 49, 56, 57, 50, 43, 36,
+    29, 22, 15, 23, 30, 37, 44, 51,
+    58, 59, 52, 45, 38, 31, 39, 46,
+    53, 60, 61, 54, 47, 55, 62, 63
+];
+
+const LEVEL_SCALE: [[i16; 6]; 3] = [
+    [ 10, 11, 13, 14, 16, 18 ],
+    [ 16, 18, 20, 23, 25, 29 ],
+    [ 13, 14, 16, 18, 20, 23 ]
+];
+
+pub fn chroma_dc_transform(blk: &mut [i16; 4], qp: u8) {
+    let t0 = blk[0] + blk[2];
+    let t1 = blk[0] - blk[2];
+    let t2 = blk[1] + blk[3];
+    let t3 = blk[1] - blk[3];
+    blk[0] = t0 + t2;
+    blk[1] = t0 - t2;
+    blk[2] = t1 + t3;
+    blk[3] = t1 - t3;
+    if qp < 6 {
+        let mul = i16::from(LEVEL_SCALE[0][qp as usize]);
+        for el in blk.iter_mut() {
+            *el = el.wrapping_mul(mul) >> 1;
+        }
+    } else {
+        let mul = i16::from(LEVEL_SCALE[0][(qp % 6) as usize]);
+        let shift = qp / 6 - 1;
+        for el in blk.iter_mut() {
+            *el = el.wrapping_mul(mul) << shift;
+        }
+    }
+}
+
+macro_rules! transform {
+    (luma_dc; $a: expr, $b: expr, $c: expr, $d: expr) => ({
+        let t0 = $a.wrapping_add($c);
+        let t1 = $a.wrapping_sub($c);
+        let t2 = $b.wrapping_add($d);
+        let t3 = $b.wrapping_sub($d);
+        $a = t0.wrapping_add(t2);
+        $b = t1.wrapping_add(t3);
+        $c = t1.wrapping_sub(t3);
+        $d = t0.wrapping_sub(t2);
+    });
+    ($a: expr, $b: expr, $c: expr, $d: expr, $shift: expr) => ({
+        let t0 = $a.wrapping_add($c);
+        let t1 = $a.wrapping_sub($c);
+        let t2 = ($b >> 1).wrapping_sub($d);
+        let t3 = $b.wrapping_add($d >> 1);
+        let bias = 1 << $shift >> 1;
+        $a = t0.wrapping_add(t3).wrapping_add(bias) >> $shift;
+        $b = t1.wrapping_add(t2).wrapping_add(bias) >> $shift;
+        $c = t1.wrapping_sub(t2).wrapping_add(bias) >> $shift;
+        $d = t0.wrapping_sub(t3).wrapping_add(bias) >> $shift;
+    });
+    ($a: expr, $b: expr, $c: expr, $d: expr, $e: expr, $f: expr, $g: expr, $h: expr) => {
+        let e0 = $a + $e;
+        let e1 = -$d + $f - $h - ($h >> 1);
+        let e2 = $a - $e;
+        let e3 = $b + $h - $d - ($d >> 1);
+        let e4 = ($c >> 1) - $g;
+        let e5 = -$b + $h + $f + ($f >> 1);
+        let e6 = $c + ($g >> 1);
+        let e7 = $d + $f + $b + ($b >> 1);
+
+        let f0 = e0 + e6;
+        let f1 = e1 + (e7 >> 2);
+        let f2 = e2 + e4;
+        let f3 = e3 + (e5 >> 2);
+        let f4 = e2 - e4;
+        let f5 = (e3 >> 2) - e5;
+        let f6 = e0 - e6;
+        let f7 = e7 - (e1 >> 2);
+
+        $a = f0 + f7;
+        $b = f2 + f5;
+        $c = f4 + f3;
+        $d = f6 + f1;
+        $e = f6 - f1;
+        $f = f4 - f3;
+        $g = f2 - f5;
+        $h = f0 - f7;
+    };
+}
+
+pub fn idct_luma_dc(blk: &mut [i16; 16], qp: u8) {
+    if qp < 12 {
+        let mul = i16::from(LEVEL_SCALE[0][(qp % 6) as usize]);
+        let shift = 2 - qp / 6;
+        let bias = 1 << shift >> 1;
+        for el in blk.iter_mut() {
+            *el = el.wrapping_mul(mul).wrapping_add(bias) >> shift;
+        }
+    } else {
+        let mul = i16::from(LEVEL_SCALE[0][(qp % 6) as usize]);
+        let shift = qp / 6 - 2;
+        for el in blk.iter_mut() {
+            *el = el.wrapping_mul(mul) << shift;
+        }
+    }
+    for i in 0..4 {
+        transform!(luma_dc; blk[i], blk[i + 4], blk[i + 8], blk[i + 12]);
+    }
+    for row in blk.chunks_mut(4) {
+        transform!(luma_dc; row[0], row[1], row[2], row[3]);
+    }
+}
+
+pub fn idct(blk: &mut [i16; 16], qp: u8, quant_dc: bool) {
+    const BLK_INDEX: [usize; 16] = [
+        0, 2, 0, 2,
+        2, 1, 2, 1,
+        0, 2, 0, 2,
+        2, 1, 2, 1
+    ];
+    let qidx = (qp % 6) as usize;
+    let shift = qp / 6;
+    let start = if quant_dc { 0 } else { 1 };
+    for (el, &idx) in blk.iter_mut().zip(BLK_INDEX.iter()).skip(start) {
+        *el = (*el * LEVEL_SCALE[idx][qidx]) << shift;
+    }
+    for i in 0..4 {
+        transform!(blk[i], blk[i + 4], blk[i + 8], blk[i + 12], 0);
+    }
+    for row in blk.chunks_mut(4) {
+        transform!(row[0], row[1], row[2], row[3], 6);
+    }
+}
+
+pub fn idct_dc(blk: &mut [i16; 16], qp: u8, quant_dc: bool) {
+    let dc = if quant_dc {
+            (blk[0] * LEVEL_SCALE[0][(qp % 6) as usize]) << (qp / 6)
+        } else {
+            blk[0]
+        };
+    *blk  = [(dc + 0x20) >> 6; 16];
+}
+
+const QMAT_8X8: [[u8; 16]; 6] = [
+  [
+    20, 19, 25, 24,
+    19, 18, 24, 18,
+    25, 24, 32, 24,
+    24, 18, 24, 18
+  ], [
+    22, 21, 28, 26,
+    21, 19, 26, 19,
+    28, 26, 35, 26,
+    26, 19, 26, 19
+  ], [
+    26, 24, 33, 31,
+    24, 23, 31, 23,
+    33, 31, 42, 31,
+    31, 23, 31, 23
+  ], [
+    28, 26, 35, 33,
+    26, 25, 33, 25,
+    35, 33, 45, 33,
+    33, 25, 33, 25
+  ], [
+    32, 30, 40, 38,
+    30, 28, 38, 28,
+    40, 38, 51, 38,
+    38, 28, 38, 28
+  ], [
+    36, 34, 46, 43,
+    34, 32, 43, 32,
+    46, 43, 58, 43,
+    43, 32, 43, 32
+  ]
+];
+
+pub fn dequant8x8(blk: &mut [i16; 64], slist: &[u8; 64]) {
+    for (el, &scan) in blk.iter_mut().zip(ZIGZAG8X8.iter()) {
+        if *el != 0 {
+            *el = el.wrapping_mul(i16::from(slist[scan]));
+        }
+    }
+}
+
+pub fn idct8x8(blk: &mut [i16; 64], qp: u8) {
+    let mut tmp = [0i32; 64];
+    let qmat = &QMAT_8X8[(qp % 6) as usize];
+    if qp >= 36 {
+        let shift = qp / 6 - 6;
+        for (i, (dst, &src)) in tmp.iter_mut().zip(blk.iter()).enumerate() {
+            let x = i & 7;
+            let y = i >> 3;
+            let idx = (x & 3) + (y & 3) * 4;
+            *dst = i32::from(src).wrapping_mul(i32::from(qmat[idx])) << shift;
+        }
+    } else {
+        let shift = 6 - qp / 6;
+        let bias = (1 << shift) >> 1;
+        for (i, (dst, &src)) in tmp.iter_mut().zip(blk.iter()).enumerate() {
+            let x = i & 7;
+            let y = i >> 3;
+            let idx = (x & 3) + (y & 3) * 4;
+            *dst = i32::from(src).wrapping_mul(i32::from(qmat[idx])).wrapping_add(bias) >> shift;
+        }
+    }
+    for row in tmp.chunks_mut(8) {
+        transform!(row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7]);
+    }
+    for col in 0..8 {
+        transform!(tmp[col], tmp[col + 8], tmp[col + 8 * 2], tmp[col + 8 * 3],
+                   tmp[col + 8 * 4], tmp[col + 8 * 5], tmp[col + 8 * 6], tmp[col + 8 * 7]);
+    }
+    for (dst, &src) in blk.iter_mut().zip(tmp.iter()) {
+        *dst = ((src + 0x20) >> 6) as i16;
+    }
+}
+
+pub fn add_coeffs(dst: &mut [u8], offset: usize, stride: usize, coeffs: &[i16]) {
+    let out = &mut dst[offset..][..stride * 3 + 4];
+    for (line, src) in out.chunks_mut(stride).take(4).zip(coeffs.chunks(4)) {
+        for (dst, src) in line.iter_mut().take(4).zip(src.iter()) {
+            *dst = (i32::from(*dst) + i32::from(*src)).max(0).min(255) as u8;
+        }
+    }
+}
+
+pub fn add_coeffs8(dst: &mut [u8], offset: usize, stride: usize, coeffs: &[i16; 64]) {
+    let out = &mut dst[offset..];
+    for (line, src) in out.chunks_mut(stride).take(8).zip(coeffs.chunks(8)) {
+        for (dst, src) in line.iter_mut().take(8).zip(src.iter()) {
+            *dst = (i32::from(*dst) + i32::from(*src)).max(0).min(255) as u8;
+        }
+    }
+}
+
+pub fn avg(dst: &mut [u8], dstride: usize,
+           src: &[u8], sstride: usize, bw: usize, bh: usize) {
+   for (dline, sline) in dst.chunks_mut(dstride).zip(src.chunks(sstride)).take(bh) {
+        for (dst, src) in dline.iter_mut().zip(sline.iter()).take(bw) {
+            *dst = ((u16::from(*dst) + u16::from(*src) + 1) >> 1) as u8;
+        }
+    }
+}
+
+fn clip8(val: i16) -> u8 { val.max(0).min(255) as u8 }
+
+fn ipred_dc128(buf: &mut [u8], mut idx: usize, stride: usize, bsize: usize) {
+    for _ in 0..bsize {
+        for x in 0..bsize { buf[idx + x] = 128; }
+        idx += stride;
+    }
+}
+fn ipred_ver(buf: &mut [u8], mut idx: usize, stride: usize, bsize: usize) {
+    let oidx = idx - stride;
+    for _ in 0..bsize {
+        for x in 0..bsize { buf[idx + x] = buf[oidx + x]; }
+        idx += stride;
+    }
+}
+fn ipred_hor(buf: &mut [u8], mut idx: usize, stride: usize, bsize: usize) {
+    for _ in 0..bsize {
+        for x in 0..bsize { buf[idx + x] = buf[idx - 1]; }
+        idx += stride;
+    }
+}
+fn ipred_dc(buf: &mut [u8], mut idx: usize, stride: usize, bsize: usize, shift: u8) {
+    let mut adc: u16 = 0;
+    for i in 0..bsize { adc += u16::from(buf[idx - stride + i]); }
+    for i in 0..bsize { adc += u16::from(buf[idx - 1 + i * stride]); }
+    let dc = ((adc + (1 << (shift - 1))) >> shift) as u8;
+
+    for _ in 0..bsize {
+        for x in 0..bsize { buf[idx + x] = dc; }
+        idx += stride;
+    }
+}
+fn ipred_left_dc(buf: &mut [u8], mut idx: usize, stride: usize, bsize: usize, shift: u8) {
+    let mut adc: u16 = 0;
+    for i in 0..bsize { adc += u16::from(buf[idx - 1 + i * stride]); }
+    let dc = ((adc + (1 << (shift - 1))) >> shift) as u8;
+
+    for _ in 0..bsize {
+        for x in 0..bsize { buf[idx + x] = dc; }
+        idx += stride;
+    }
+}
+fn ipred_top_dc(buf: &mut [u8], mut idx: usize, stride: usize, bsize: usize, shift: u8) {
+    let mut adc: u16 = 0;
+    for i in 0..bsize { adc += u16::from(buf[idx - stride + i]); }
+    let dc = ((adc + (1 << (shift - 1))) >> shift) as u8;
+
+    for _ in 0..bsize {
+        for x in 0..bsize { buf[idx + x] = dc; }
+        idx += stride;
+    }
+}
+
+fn load_top(dst: &mut [u16], buf: &mut [u8], idx: usize, stride: usize, len: usize) {
+    for i in 0..len { dst[i] = u16::from(buf[idx - stride + i]); }
+}
+fn load_left(dst: &mut [u16], buf: &mut [u8], idx: usize, stride: usize, len: usize) {
+    for i in 0..len { dst[i] = u16::from(buf[idx - 1 + i * stride]); }
+}
+
+fn ipred_4x4_ver(buf: &mut [u8], idx: usize, stride: usize, _tr: &[u8]) {
+    ipred_ver(buf, idx, stride, 4);
+}
+fn ipred_4x4_hor(buf: &mut [u8], idx: usize, stride: usize, _tr: &[u8]) {
+    ipred_hor(buf, idx, stride, 4);
+}
+fn ipred_4x4_diag_down_left(buf: &mut [u8], idx: usize, stride: usize, tr: &[u8]) {
+    let mut t: [u16; 9] = [0; 9];
+    load_top(&mut t, buf, idx, stride, 4);
+    for i in 0..4 {
+        t[i + 4] = u16::from(tr[i]);
+    }
+    t[8] = t[7];
+
+    let dst = &mut buf[idx..];
+    for i in 0..4 {
+        dst[i] = ((t[i]     + 2 * t[i + 1] + t[i + 2] + 2) >> 2) as u8;
+    }
+    let dst = &mut buf[idx + stride..];
+    for i in 0..4 {
+        dst[i] = ((t[i + 1] + 2 * t[i + 2] + t[i + 3] + 2) >> 2) as u8;
+    }
+    let dst = &mut buf[idx + stride * 2..];
+    for i in 0..4 {
+        dst[i] = ((t[i + 2] + 2 * t[i + 3] + t[i + 4] + 2) >> 2) as u8;
+    }
+    let dst = &mut buf[idx + stride * 3..];
+    for i in 0..4 {
+        dst[i] = ((t[i + 3] + 2 * t[i + 4] + t[i + 5] + 2) >> 2) as u8;
+    }
+}
+fn ipred_4x4_diag_down_right(buf: &mut [u8], idx: usize, stride: usize, _tr: &[u8]) {
+    let mut t: [u16; 5] = [0; 5];
+    let mut l: [u16; 5] = [0; 5];
+    load_top(&mut t, buf, idx - 1, stride, 5);
+    load_left(&mut l, buf, idx - stride, stride, 5);
+    let dst = &mut buf[idx..];
+
+    for j in 0..4 {
+        for i in 0..j {
+            dst[i + j * stride] = ((l[j - i - 1] + 2 * l[j - i] + l[j - i + 1] + 2) >> 2) as u8;
+        }
+        dst[j + j * stride] = ((l[1] + 2 * l[0] + t[1] + 2) >> 2) as u8;
+        for i in (j+1)..4 {
+            dst[i + j * stride] = ((t[i - j - 1] + 2 * t[i - j] + t[i - j + 1] + 2) >> 2) as u8;
+        }
+    }
+}
+fn ipred_4x4_ver_right(buf: &mut [u8], idx: usize, stride: usize, _tr: &[u8]) {
+    let mut t: [u16; 5] = [0; 5];
+    let mut l: [u16; 5] = [0; 5];
+    load_top(&mut t, buf, idx - 1, stride, 5);
+    load_left(&mut l, buf, idx - stride, stride, 5);
+    let dst = &mut buf[idx..];
+
+    for j in 0..4 {
+        for i in 0..4 {
+            let zvr = ((2 * i) as i8) - (j as i8);
+            let pix;
+            if zvr >= 0 {
+                if (zvr & 1) == 0 {
+                    pix = (t[i - (j >> 1)] + t[i - (j >> 1) + 1] + 1) >> 1;
+                } else {
+                    pix = (t[i - (j >> 1) - 1] + 2 * t[i - (j >> 1)] + t[i - (j >> 1) + 1] + 2) >> 2;
+                }
+            } else {
+                if zvr == -1 {
+                    pix = (l[1] + 2 * l[0] + t[1] + 2) >> 2;
+                } else {
+                    pix = (l[j] + 2 * l[j - 1] + l[j - 2] + 2) >> 2;
+                }
+            }
+            dst[i + j * stride] = pix as u8;
+        }
+    }
+}
+fn ipred_4x4_ver_left(buf: &mut [u8], idx: usize, stride: usize, tr: &[u8]) {
+    let mut t: [u16; 8] = [0; 8];
+    load_top(&mut t, buf, idx, stride, 4);
+    for i in 0..4 { t[i + 4] = u16::from(tr[i]); }
+    let dst = &mut buf[idx..];
+
+    dst[0 + 0 * stride] = ((t[0] + t[1] + 1) >> 1) as u8;
+    let pix = ((t[1] + t[2] + 1) >> 1) as u8;
+    dst[1 + 0 * stride] = pix;
+    dst[0 + 2 * stride] = pix;
+    let pix = ((t[2] + t[3] + 1) >> 1) as u8;
+    dst[2 + 0 * stride] = pix;
+    dst[1 + 2 * stride] = pix;
+    let pix = ((t[3] + t[4] + 1) >> 1) as u8;
+    dst[3 + 0 * stride] = pix;
+    dst[2 + 2 * stride] = pix;
+    dst[3 + 2 * stride] = ((t[4] + t[5] + 1) >> 1) as u8;
+    dst[0 + 1 * stride] = ((t[0] + 2*t[1] + t[2] + 2) >> 2) as u8;
+    let pix = ((t[1] + 2*t[2] + t[3] + 2) >> 2) as u8;
+    dst[1 + 1 * stride] = pix;
+    dst[0 + 3 * stride] = pix;
+    let pix = ((t[2] + 2*t[3] + t[4] + 2) >> 2) as u8;
+    dst[2 + 1 * stride] = pix;
+    dst[1 + 3 * stride] = pix;
+    let pix = ((t[3] + 2*t[4] + t[5] + 2) >> 2) as u8;
+    dst[3 + 1 * stride] = pix;
+    dst[2 + 3 * stride] = pix;
+    dst[3 + 3 * stride] = ((t[4] + 2*t[5] + t[6] + 2) >> 2) as u8;
+}
+fn ipred_4x4_hor_down(buf: &mut [u8], idx: usize, stride: usize, _tr: &[u8]) {
+    let mut t: [u16; 5] = [0; 5];
+    let mut l: [u16; 5] = [0; 5];
+    load_top(&mut t, buf, idx - 1, stride, 5);
+    load_left(&mut l, buf, idx - stride, stride, 5);
+    let dst = &mut buf[idx..];
+
+    for j in 0..4 {
+        for i in 0..4 {
+            let zhd = ((2 * j) as i8) - (i as i8);
+            let pix;
+            if zhd >= 0 {
+                if (zhd & 1) == 0 {
+                    pix = (l[j - (i >> 1)] + l[j - (i >> 1) + 1] + 1) >> 1;
+                } else {
+                    pix = (l[j - (i >> 1) - 1] + 2 * l[j - (i >> 1)] + l[j - (i >> 1) + 1] + 2) >> 2;
+                }
+            } else {
+                if zhd == -1 {
+                    pix = (l[1] + 2 * l[0] + t[1] + 2) >> 2;
+                } else {
+                    pix = (t[i - 2] + 2 * t[i - 1] + t[i] + 2) >> 2;
+                }
+            }
+            dst[i + j * stride] = pix as u8;
+        }
+    }
+}
+fn ipred_4x4_hor_up(buf: &mut [u8], idx: usize, stride: usize, _tr: &[u8]) {
+    let mut l: [u16; 8] = [0; 8];
+    load_left(&mut l, buf, idx, stride, 8);
+    let dst = &mut buf[idx..];
+
+    dst[0 + 0 * stride] = ((l[0] + l[1] + 1) >> 1) as u8;
+    dst[1 + 0 * stride] = ((l[0] + 2*l[1] + l[2] + 2) >> 2) as u8;
+    let pix = ((l[1] + l[2] + 1) >> 1) as u8;
+    dst[2 + 0 * stride] = pix;
+    dst[0 + 1 * stride] = pix;
+    let pix = ((l[1] + 2*l[2] + l[3] + 2) >> 2) as u8;
+    dst[3 + 0 * stride] = pix;
+    dst[1 + 1 * stride] = pix;
+    let pix = ((l[2] + l[3] + 1) >> 1) as u8;
+    dst[2 + 1 * stride] = pix;
+    dst[0 + 2 * stride] = pix;
+    let pix = ((l[2] + 3*l[3] + 2) >> 2) as u8;
+    dst[3 + 1 * stride] = pix;
+    dst[1 + 2 * stride] = pix;
+    dst[3 + 2 * stride] = l[3] as u8;
+    dst[1 + 3 * stride] = l[3] as u8;
+    dst[0 + 3 * stride] = l[3] as u8;
+    dst[2 + 2 * stride] = l[3] as u8;
+    dst[2 + 3 * stride] = l[3] as u8;
+    dst[3 + 3 * stride] = l[3] as u8;
+}
+fn ipred_4x4_dc(buf: &mut [u8], idx: usize, stride: usize, _tr: &[u8]) {
+    ipred_dc(buf, idx, stride, 4, 3);
+}
+fn ipred_4x4_left_dc(buf: &mut [u8], idx: usize, stride: usize, _tr: &[u8]) {
+    ipred_left_dc(buf, idx, stride, 4, 2);
+}
+fn ipred_4x4_top_dc(buf: &mut [u8], idx: usize, stride: usize, _tr: &[u8]) {
+    ipred_top_dc(buf, idx, stride, 4, 2);
+}
+fn ipred_4x4_dc128(buf: &mut [u8], idx: usize, stride: usize, _tr: &[u8]) {
+    ipred_dc128(buf, idx, stride, 4);
+}
+
+pub struct IPred8Context {
+    pub t:      [u8; 16],
+    pub l:      [u8; 8],
+    pub tl:     u8,
+}
+
+impl IPred8Context {
+    pub fn new() -> Self {
+        Self {
+            t:      [128; 16],
+            l:      [128; 8],
+            tl:     128,
+        }
+    }
+    pub fn fill(&mut self, buf: &mut [u8], idx: usize, stride: usize, has_t: bool, has_tr: bool, has_l: bool, has_tl: bool) {
+        let mut t = [0x80u8; 19];
+        let mut l = [0x80u8; 11];
+        if has_t {
+            t[1..8 + 1].copy_from_slice(&buf[idx - stride..][..8]);
+        }
+        if has_tr {
+            t[8 + 1..16 + 1].copy_from_slice(&buf[idx - stride + 8..][..8]);
+            t[16 + 1] = t[15 + 1];
+            t[17 + 1] = t[15 + 1];
+        } else {
+            let (t0, t1) = t.split_at_mut(8 + 1);
+            for el in t1.iter_mut() {
+                *el = t0[7 + 1];
+            }
+        }
+        if has_l {
+            for i in 0..8 {
+                l[i + 1] = buf[idx - 1 + stride * i];
+            }
+            l[8 + 1] = l[7 + 1];
+            l[9 + 1] = l[7 + 1];
+        }
+        if has_tl {
+            t[0] = buf[idx - 1 - stride];
+            l[0] = buf[idx - 1 - stride];
+        } else {
+            t[0] = t[1];
+            l[0] = l[1];
+        }
+
+        for i in 0..16 {
+            self.t[i] = ((u16::from(t[i]) + 2 * u16::from(t[i + 1]) + u16::from(t[i + 2]) + 2) >> 2) as u8;
+        }
+        for i in 0..8 {
+            self.l[i] = ((u16::from(l[i]) + 2 * u16::from(l[i + 1]) + u16::from(l[i + 2]) + 2) >> 2) as u8;
+        }
+        self.tl = if has_t && has_l {
+                ((u16::from(t[1]) + 2 * u16::from(t[0]) + u16::from(l[1]) + 2) >> 2) as u8
+            } else if has_t {
+                ((3 * u16::from(t[0]) + u16::from(t[1]) + 2) >> 2) as u8
+            } else if has_l {
+                ((3 * u16::from(l[0]) + u16::from(l[1]) + 2) >> 2) as u8
+            } else {
+                t[0]
+            };
+    }
+}
+
+fn ipred_y_8x8_ver(buf: &mut [u8], stride: usize, ctx: &IPred8Context) {
+    for row in buf.chunks_mut(stride).take(8) {
+        row[..8].copy_from_slice(&ctx.t[..8]);
+    }
+}
+fn ipred_y_8x8_hor(buf: &mut [u8], stride: usize, ctx: &IPred8Context) {
+    for (row, &l) in buf.chunks_mut(stride).zip(ctx.l.iter()).take(8) {
+        row[..8].copy_from_slice(&[l; 8]);
+    }
+}
+fn ipred_y_8x8_diag_down_left(buf: &mut [u8], stride: usize, ctx: &IPred8Context) {
+    let mut t = [0u16; 16];
+    for (dt, &st) in t.iter_mut().zip(ctx.t.iter()) {
+        *dt = u16::from(st);
+    }
+
+    for (y, row) in buf.chunks_mut(stride).take(8).enumerate() {
+        for (x, pix) in row.iter_mut().take(8).enumerate() {
+            *pix = ((if (x != 7) || (y != 7) {
+                    t[x + y] + 2 * t[x + y + 1] + t[x + y + 2]
+                } else {
+                    t[14] + 3 * t[15]
+                } + 2) >> 2) as u8;
+        }
+    }
+}
+fn ipred_y_8x8_diag_down_right(buf: &mut [u8], stride: usize, ctx: &IPred8Context) {
+    let mut t = [0u16; 9];
+    t[0] = u16::from(ctx.tl);
+    for (dt, &st) in t[1..].iter_mut().zip(ctx.t.iter()) {
+        *dt = u16::from(st);
+    }
+    let mut l = [0u16; 9];
+    l[0] = u16::from(ctx.tl);
+    for (dl, &sl) in l[1..].iter_mut().zip(ctx.l.iter()) {
+        *dl = u16::from(sl);
+    }
+    let diag = t[1] + 2 * t[0] + l[1];
+
+    for (y, row) in buf.chunks_mut(stride).take(8).enumerate() {
+        for (x, pix) in row.iter_mut().take(8).enumerate() {
+            *pix = ((if x > y {
+                    t[x - y - 1] + 2 * t[x - y] + t[x - y + 1]
+                } else if x < y {
+                    l[y - x - 1] + 2 * l[y - x] + l[y - x + 1]
+                } else {
+                    diag
+                } + 2) >> 2) as u8;
+        }
+    }
+}
+fn ipred_y_8x8_ver_right(buf: &mut [u8], stride: usize, ctx: &IPred8Context) {
+    let mut t = [0u16; 9];
+    t[0] = u16::from(ctx.tl);
+    for (dt, &st) in t[1..].iter_mut().zip(ctx.t.iter()) {
+        *dt = u16::from(st);
+    }
+    let mut l = [0u16; 9];
+    l[0] = u16::from(ctx.tl);
+    for (dl, &sl) in l[1..].iter_mut().zip(ctx.l.iter()) {
+        *dl = u16::from(sl);
+    }
+
+    for (y, row) in buf.chunks_mut(stride).take(8).enumerate() {
+        for (x, pix) in row.iter_mut().take(8).enumerate() {
+            let zvr = 2 * (x as i8) - (y as i8);
+            *pix = if zvr >= 0 {
+                    let ix = x - (y >> 1);
+                    if (zvr & 1) == 0 {
+                        (t[ix] + t[ix + 1] + 1) >> 1
+                    } else {
+                        (t[ix - 1] + 2 * t[ix] + t[ix + 1] + 2) >> 2
+                    }
+                } else if zvr == -1 {
+                    (l[1] + 2 * l[0] + t[0] + 2) >> 2
+                } else {
+                    let ix = y - 2 * x;
+                    (l[ix] + 2 * l[ix - 1] + l[ix - 2] + 2) >> 2
+                } as u8;
+        }
+    }
+}
+fn ipred_y_8x8_ver_left(buf: &mut [u8], stride: usize, ctx: &IPred8Context) {
+    let mut t = [0u16; 16];
+    for (dt, &st) in t.iter_mut().zip(ctx.t.iter()) {
+        *dt = u16::from(st);
+    }
+
+    for (y, row) in buf.chunks_mut(stride).take(8).enumerate() {
+        for (x, pix) in row.iter_mut().take(8).enumerate() {
+            let ix = x + (y >> 1);
+            *pix = if (y & 1) == 0 {
+                    (t[ix] + t[ix + 1] + 1) >> 1
+                } else {
+                    (t[ix] + 2 * t[ix + 1] + t[ix + 2] + 2) >> 2
+                } as u8;
+        }
+    }
+
+}
+fn ipred_y_8x8_hor_down(buf: &mut [u8], stride: usize, ctx: &IPred8Context) {
+    let mut t = [0u16; 9];
+    t[0] = u16::from(ctx.tl);
+    for (dt, &st) in t[1..].iter_mut().zip(ctx.t.iter()) {
+        *dt = u16::from(st);
+    }
+    let mut l = [0u16; 9];
+    l[0] = u16::from(ctx.tl);
+    for (dl, &sl) in l[1..].iter_mut().zip(ctx.l.iter()) {
+        *dl = u16::from(sl);
+    }
+
+    for (y, row) in buf.chunks_mut(stride).take(8).enumerate() {
+        for (x, pix) in row.iter_mut().take(8).enumerate() {
+            let zhd = 2 * (y as i8) - (x as i8);
+            *pix = if zhd >= 0 {
+                    let ix = y - (x >> 1);
+                    if (zhd & 1) == 0 {
+                        (l[ix] + l[ix + 1] + 1) >> 1
+                    } else {
+                        (l[ix - 1] + 2 * l[ix] + l[ix + 1] + 2) >> 2
+                    }
+                } else if zhd == -1 {
+                    (l[1] + 2 * l[0] + t[0] + 2) >> 2
+                } else {
+                    let ix = x - 2 * y;
+                    (t[ix] + 2 * t[ix - 1] + t[ix - 2] + 2) >> 2
+                } as u8;
+        }
+    }
+}
+fn ipred_y_8x8_hor_up(buf: &mut [u8], stride: usize, ctx: &IPred8Context) {
+    let mut l = [0u16; 8];
+    for (dl, &sl) in l.iter_mut().zip(ctx.l.iter()) {
+        *dl = u16::from(sl);
+    }
+
+    for (y, row) in buf.chunks_mut(stride).take(8).enumerate() {
+        for (x, pix) in row.iter_mut().take(8).enumerate() {
+            let zhu = x + 2 * y;
+            let ix = y + (x >> 1);
+            *pix = if zhu > 13 {
+                    l[7]
+                } else if zhu == 13 {
+                    (l[6] + 3 * l[7] + 2) >> 2
+                } else if (zhu & 1) != 0 {
+                    (l[ix] + 2 * l[ix + 1] + l[ix + 2] + 2) >> 2
+                } else {
+                    (l[ix] + l[ix + 1] + 1) >> 1
+                } as u8;
+        }
+    }
+}
+fn ipred_y_8x8_dc(buf: &mut [u8], stride: usize, ctx: &IPred8Context) {
+    let mut sum = 0u16;
+    for &t in ctx.t[..8].iter() {
+        sum += u16::from(t);
+    }
+    for &l in ctx.l[..8].iter() {
+        sum += u16::from(l);
+    }
+    let dc = ((sum + 8) >> 4) as u8;
+    for row in buf.chunks_mut(stride).take(8) {
+        for pix in row.iter_mut().take(8) {
+            *pix = dc;
+        }
+    }
+}
+fn ipred_y_8x8_left_dc(buf: &mut [u8], stride: usize, ctx: &IPred8Context) {
+    let mut sum = 0u16;
+    for &l in ctx.l[..8].iter() {
+        sum += u16::from(l);
+    }
+    let dc = ((sum + 4) >> 3) as u8;
+    for row in buf.chunks_mut(stride).take(8) {
+        for pix in row.iter_mut().take(8) {
+            *pix = dc;
+        }
+    }
+}
+fn ipred_y_8x8_top_dc(buf: &mut [u8], stride: usize, ctx: &IPred8Context) {
+    let mut sum = 0u16;
+    for &t in ctx.t[..8].iter() {
+        sum += u16::from(t);
+    }
+    let dc = ((sum + 4) >> 3) as u8;
+    for row in buf.chunks_mut(stride).take(8) {
+        for pix in row.iter_mut().take(8) {
+            *pix = dc;
+        }
+    }
+}
+fn ipred_y_8x8_dc128(buf: &mut [u8], stride: usize, _ctx: &IPred8Context) {
+    ipred_dc128(buf, 0, stride, 8);
+}
+
+fn ipred_8x8_ver(buf: &mut [u8], idx: usize, stride: usize) {
+    ipred_ver(buf, idx, stride, 8);
+}
+fn ipred_8x8_hor(buf: &mut [u8], idx: usize, stride: usize) {
+    ipred_hor(buf, idx, stride, 8);
+}
+fn ipred_8x8_dc(buf: &mut [u8], idx: usize, stride: usize) {
+    let mut t: [u16; 8] = [0; 8];
+    load_top(&mut t, buf, idx, stride, 8);
+    let mut l: [u16; 8] = [0; 8];
+    load_left(&mut l, buf, idx, stride, 8);
+
+    let dc0 = ((t[0] + t[1] + t[2] + t[3] + l[0] + l[1] + l[2] + l[3] + 4) >> 3) as u8;
+    let sum1 = t[4] + t[5] + t[6] + t[7];
+    let dc1 = ((sum1 + 2) >> 2) as u8;
+    let sum2 = l[4] + l[5] + l[6] + l[7];
+    let dc2 = ((sum2 + 2) >> 2) as u8;
+    let dc3 = ((sum1 + sum2 + 4) >> 3) as u8;
+
+    let dst = &mut buf[idx..];
+    for row in dst.chunks_mut(stride).take(4) {
+        row[..4].copy_from_slice(&[dc0; 4]);
+        row[4..8].copy_from_slice(&[dc1; 4]);
+    }
+    for row in dst.chunks_mut(stride).skip(4).take(4) {
+        row[..4].copy_from_slice(&[dc2; 4]);
+        row[4..8].copy_from_slice(&[dc3; 4]);
+    }
+}
+fn ipred_8x8_left_dc(buf: &mut [u8], idx: usize, stride: usize) {
+    let mut left_dc0 = 0;
+    let mut left_dc1 = 0;
+    for row in buf[idx - 1..].chunks(stride).take(4) {
+        left_dc0 += u16::from(row[0]);
+    }
+    for row in buf[idx - 1..].chunks(stride).skip(4).take(4) {
+        left_dc1 += u16::from(row[0]);
+    }
+    let dc0 = ((left_dc0 + 2) >> 2) as u8;
+    let dc2 = ((left_dc1 + 2) >> 2) as u8;
+    for row in buf[idx..].chunks_mut(stride).take(4) {
+        row[..8].copy_from_slice(&[dc0; 8]);
+    }
+    for row in buf[idx..].chunks_mut(stride).skip(4).take(4) {
+        row[..8].copy_from_slice(&[dc2; 8]);
+    }
+}
+fn ipred_8x8_top_dc(buf: &mut [u8], idx: usize, stride: usize) {
+    ipred_top_dc(buf, idx, stride, 4, 2);
+    ipred_top_dc(buf, idx + 4, stride, 4, 2);
+    ipred_top_dc(buf, idx + 4 * stride, stride, 4, 2);
+    ipred_top_dc(buf, idx + 4 + 4 * stride, stride, 4, 2);
+}
+fn ipred_8x8_dc128(buf: &mut [u8], idx: usize, stride: usize) {
+    ipred_dc128(buf, idx, stride, 8);
+}
+fn ipred_8x8_plane(buf: &mut [u8], idx: usize, stride: usize) {
+    let mut h: i32 = 0;
+    let mut v: i32 = 0;
+    let     idx0 = idx + 3 - stride;
+    let mut idx1 = idx + 4 * stride - 1;
+    let mut idx2 = idx + 2 * stride - 1;
+    for i in 0..4 {
+        let i1 = (i + 1) as i32;
+        h += i1 * (i32::from(buf[idx0 + i + 1]) - i32::from(buf[idx0 - i - 1]));
+        v += i1 * (i32::from(buf[idx1]) - i32::from(buf[idx2]));
+        idx1 += stride;
+        idx2 -= stride;
+    }
+    let b = (17 * h + 16) >> 5;
+    let c = (17 * v + 16) >> 5;
+    let mut a = 16 * (i32::from(buf[idx - 1 + 7 * stride]) + i32::from(buf[idx + 7 - stride])) - 3 * (b + c) + 16;
+    for line in buf[idx..].chunks_mut(stride).take(8) {
+        let mut acc = a;
+        for el in line.iter_mut().take(8) {
+            *el = clip8((acc >> 5) as i16);
+            acc += b;
+        }
+        a += c;
+    }
+}
+
+fn ipred_16x16_ver(buf: &mut [u8], idx: usize, stride: usize) {
+    ipred_ver(buf, idx, stride, 16);
+}
+fn ipred_16x16_hor(buf: &mut [u8], idx: usize, stride: usize) {
+    ipred_hor(buf, idx, stride, 16);
+}
+fn ipred_16x16_dc(buf: &mut [u8], idx: usize, stride: usize) {
+    ipred_dc(buf, idx, stride, 16, 5);
+}
+fn ipred_16x16_left_dc(buf: &mut [u8], idx: usize, stride: usize) {
+    ipred_left_dc(buf, idx, stride, 16, 4);
+}
+fn ipred_16x16_top_dc(buf: &mut [u8], idx: usize, stride: usize) {
+    ipred_top_dc(buf, idx, stride, 16, 4);
+}
+fn ipred_16x16_dc128(buf: &mut [u8], idx: usize, stride: usize) {
+    ipred_dc128(buf, idx, stride, 16);
+}
+fn ipred_16x16_plane(buf: &mut [u8], idx: usize, stride: usize) {
+    let     idx0 = idx + 7 - stride;
+    let mut idx1 = idx + 8 * stride - 1;
+    let mut idx2 = idx1 - 2 * stride;
+
+    let mut h = i32::from(buf[idx0 + 1]) - i32::from(buf[idx0 - 1]);
+    let mut v = i32::from(buf[idx1])     - i32::from(buf[idx2]);
+
+    for k in 2..9 {
+        idx1 += stride;
+        idx2 -= stride;
+        h += (k as i32) * (i32::from(buf[idx0 + k]) - i32::from(buf[idx0 - k]));
+        v += (k as i32) * (i32::from(buf[idx1])     - i32::from(buf[idx2]));
+    }
+    h = (5 * h + 32) >> 6;
+    v = (5 * v + 32) >> 6;
+
+    let mut a = 16 * (i32::from(buf[idx - 1 + 15 * stride]) + i32::from(buf[idx + 15 - stride]) + 1) - 7 * (v + h);
+
+    for row in buf[idx..].chunks_mut(stride).take(16) {
+        let mut b = a;
+        a += v;
+
+        for dst in row.chunks_exact_mut(4).take(4) {
+            dst[0] = clip8(((b      ) >> 5) as i16);
+            dst[1] = clip8(((b +   h) >> 5) as i16);
+            dst[2] = clip8(((b + 2*h) >> 5) as i16);
+            dst[3] = clip8(((b + 3*h) >> 5) as i16);
+            b += h * 4;
+        }
+    }
+}
+
+pub type IPred4x4Func = fn(buf: &mut [u8], off: usize, stride: usize, tr: &[u8]);
+pub type IPred8x8Func = fn(buf: &mut [u8], off: usize, stride: usize);
+pub type IPred8x8LumaFunc = fn(buf: &mut [u8], stride: usize, ctx: &IPred8Context);
+
+pub const IPRED4_DC128: usize = 11;
+pub const IPRED4_DC_TOP: usize = 10;
+pub const IPRED4_DC_LEFT: usize = 9;
+pub const IPRED8_DC128: usize = 6;
+pub const IPRED8_DC_TOP: usize = 5;
+pub const IPRED8_DC_LEFT: usize = 4;
+
+pub const IPRED_FUNCS4X4: [IPred4x4Func; 12] = [
+    ipred_4x4_ver, ipred_4x4_hor, ipred_4x4_dc,
+    ipred_4x4_diag_down_left, ipred_4x4_diag_down_right,
+    ipred_4x4_ver_right, ipred_4x4_hor_down, ipred_4x4_ver_left, ipred_4x4_hor_up,
+    ipred_4x4_left_dc, ipred_4x4_top_dc, ipred_4x4_dc128
+];
+
+pub const IPRED_FUNCS8X8_LUMA: [IPred8x8LumaFunc; 12] = [
+    ipred_y_8x8_ver, ipred_y_8x8_hor, ipred_y_8x8_dc,
+    ipred_y_8x8_diag_down_left, ipred_y_8x8_diag_down_right,
+    ipred_y_8x8_ver_right, ipred_y_8x8_hor_down,
+    ipred_y_8x8_ver_left, ipred_y_8x8_hor_up,
+    ipred_y_8x8_left_dc, ipred_y_8x8_top_dc, ipred_y_8x8_dc128
+];
+
+pub const IPRED_FUNCS8X8_CHROMA: [IPred8x8Func; 7] = [
+    ipred_8x8_dc, ipred_8x8_hor, ipred_8x8_ver, ipred_8x8_plane,
+    ipred_8x8_left_dc, ipred_8x8_top_dc, ipred_8x8_dc128
+];
+
+pub const IPRED_FUNCS16X16: [IPred8x8Func; 7] = [
+    ipred_16x16_ver, ipred_16x16_hor, ipred_16x16_dc, ipred_16x16_plane,
+    ipred_16x16_left_dc, ipred_16x16_top_dc, ipred_16x16_dc128
+];
+
+fn clip_u8(val: i16) -> u8 { val.max(0).min(255) as u8 }
+
+const TMP_BUF_STRIDE: usize = 32;
+
+fn interp_block1(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, w: usize, h: usize, hor: bool, avg0: bool) {
+    let step = if hor { 1 } else { sstride };
+    let mut idx = 0;
+    let avgidx = if avg0 { step * 2 } else { step * 3 };
+    for dline in dst.chunks_mut(dstride).take(h) {
+        for (x, pix) in dline.iter_mut().take(w).enumerate() {
+            let t = clip_u8((       i16::from(src[idx + x])
+                             - 5  * i16::from(src[idx + x + step])
+                             + 20 * i16::from(src[idx + x + step * 2])
+                             + 20 * i16::from(src[idx + x + step * 3])
+                             - 5  * i16::from(src[idx + x + step * 4])
+                             +      i16::from(src[idx + x + step * 5])
+                             + 16) >> 5);
+            *pix = ((u16::from(t) + u16::from(src[idx + x + avgidx]) + 1) >> 1) as u8;
+        }
+        idx += sstride;
+    }
+}
+
+fn interp_block2(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, w: usize, h: usize, hor: bool) {
+    let step = if hor { 1 } else { sstride };
+    let mut idx = 0;
+    for dline in dst.chunks_mut(dstride).take(h) {
+        for (x, pix) in dline.iter_mut().take(w).enumerate() {
+            *pix = clip_u8((       i16::from(src[idx + x])
+                            - 5  * i16::from(src[idx + x + step])
+                            + 20 * i16::from(src[idx + x + step * 2])
+                            + 20 * i16::from(src[idx + x + step * 3])
+                            - 5  * i16::from(src[idx + x + step * 4])
+                            +      i16::from(src[idx + x + step * 5])
+                            + 16) >> 5);
+        }
+        idx += sstride;
+    }
+}
+
+fn mc_avg_tmp(dst: &mut [u8], dstride: usize, w: usize, h: usize, tmp: &[u8], tmp2: &[u8]) {
+    for (dline, (sline0, sline1)) in dst.chunks_mut(dstride).zip(tmp.chunks(TMP_BUF_STRIDE).zip(tmp2.chunks(TMP_BUF_STRIDE))).take(h) {
+        for (pix, (&a, &b)) in dline.iter_mut().zip(sline0.iter().zip(sline1.iter())).take(w) {
+            *pix = ((u16::from(a) + u16::from(b) + 1) >> 1) as u8;
+        }
+    }
+}
+
+fn h264_mc00(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, w: usize, h: usize) {
+    for (dline, sline) in dst.chunks_mut(dstride).zip(src.chunks(sstride)).take(h) {
+        dline[..w].copy_from_slice(&sline[..w]);
+    }
+}
+
+fn h264_mc01(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, w: usize, h: usize) {
+    interp_block1(dst, dstride, &src[sstride * 2..], sstride, w, h, true, true);
+}
+
+fn h264_mc02(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, w: usize, h: usize) {
+    interp_block2(dst, dstride, &src[sstride * 2..], sstride, w, h, true);
+}
+
+fn h264_mc03(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, w: usize, h: usize) {
+    interp_block1(dst, dstride, &src[sstride * 2..], sstride, w, h, true, false);
+}
+
+fn h264_mc10(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, w: usize, h: usize) {
+    interp_block1(dst, dstride, &src[2..], sstride, w, h, false, true);
+}
+
+fn h264_mc11(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, w: usize, h: usize) {
+    let mut tmp  = [0u8; TMP_BUF_STRIDE * 16];
+    let mut tmp2 = [0u8; TMP_BUF_STRIDE * 16];
+    h264_mc02(&mut tmp,  TMP_BUF_STRIDE, src, sstride, w, h);
+    h264_mc20(&mut tmp2, TMP_BUF_STRIDE, src, sstride, w, h);
+    mc_avg_tmp(dst, dstride, w, h, &tmp, &tmp2);
+}
+
+fn h264_mc12(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, w: usize, h: usize) {
+    let mut tmp  = [0u8; TMP_BUF_STRIDE * 16];
+    let mut tmp2 = [0u8; TMP_BUF_STRIDE * 16];
+    h264_mc02(&mut tmp,  TMP_BUF_STRIDE, src, sstride, w, h);
+    h264_mc22(&mut tmp2, TMP_BUF_STRIDE, src, sstride, w, h);
+    mc_avg_tmp(dst, dstride, w, h, &tmp, &tmp2);
+}
+
+fn h264_mc13(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, w: usize, h: usize) {
+    let mut tmp  = [0u8; TMP_BUF_STRIDE * 16];
+    let mut tmp2 = [0u8; TMP_BUF_STRIDE * 16];
+    h264_mc02(&mut tmp,  TMP_BUF_STRIDE, src, sstride, w, h);
+    h264_mc20(&mut tmp2, TMP_BUF_STRIDE, &src[1..], sstride, w, h);
+    mc_avg_tmp(dst, dstride, w, h, &tmp, &tmp2);
+}
+
+fn h264_mc20(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, w: usize, h: usize) {
+    interp_block2(dst, dstride, &src[2..], sstride, w, h, false);
+}
+
+fn h264_mc21(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, w: usize, h: usize) {
+    let mut tmp  = [0u8; TMP_BUF_STRIDE * 16];
+    let mut tmp2 = [0u8; TMP_BUF_STRIDE * 16];
+    h264_mc22(&mut tmp,  TMP_BUF_STRIDE, src, sstride, w, h);
+    h264_mc20(&mut tmp2, TMP_BUF_STRIDE, src, sstride, w, h);
+    mc_avg_tmp(dst, dstride, w, h, &tmp, &tmp2);
+}
+
+fn h264_mc22(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, w: usize, h: usize) {
+    let mut tmp = [0i32; TMP_BUF_STRIDE * 16];
+    let mut idx = 0;
+    for dline in tmp.chunks_mut(TMP_BUF_STRIDE).take(h) {
+        for (x, pix) in dline.iter_mut().take(w + 5).enumerate() {
+            *pix =        i32::from(src[idx + x])
+                   - 5  * i32::from(src[idx + x + sstride])
+                   + 20 * i32::from(src[idx + x + sstride * 2])
+                   + 20 * i32::from(src[idx + x + sstride * 3])
+                   - 5  * i32::from(src[idx + x + sstride * 4])
+                   +      i32::from(src[idx + x + sstride * 5]);
+        }
+        idx += sstride;
+    }
+    for (dline, sline) in dst.chunks_mut(dstride).zip(tmp.chunks(TMP_BUF_STRIDE)).take(h) {
+        for (x, pix) in dline.iter_mut().take(w).enumerate() {
+            *pix = clip8(((sline[x] - 5 * sline[x + 1] + 20 * sline[x + 2] + 20 * sline[x + 3] - 5 * sline[x + 4] + sline[x + 5] + 512) >> 10) as i16);
+        }
+    }
+}
+
+fn h264_mc23(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, w: usize, h: usize) {
+    let mut tmp  = [0u8; TMP_BUF_STRIDE * 16];
+    let mut tmp2 = [0u8; TMP_BUF_STRIDE * 16];
+    h264_mc22(&mut tmp,  TMP_BUF_STRIDE, src, sstride, w, h);
+    h264_mc20(&mut tmp2, TMP_BUF_STRIDE, &src[1..], sstride, w, h);
+    mc_avg_tmp(dst, dstride, w, h, &tmp, &tmp2);
+}
+
+fn h264_mc30(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, w: usize, h: usize) {
+    interp_block1(dst, dstride, &src[2..], sstride, w, h, false, false);
+}
+
+fn h264_mc31(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, w: usize, h: usize) {
+    let mut tmp  = [0u8; TMP_BUF_STRIDE * 16];
+    let mut tmp2 = [0u8; TMP_BUF_STRIDE * 16];
+    h264_mc20(&mut tmp,  TMP_BUF_STRIDE, src, sstride, w, h);
+    h264_mc02(&mut tmp2, TMP_BUF_STRIDE, &src[sstride..], sstride, w, h);
+    mc_avg_tmp(dst, dstride, w, h, &tmp, &tmp2);
+}
+
+fn h264_mc32(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, w: usize, h: usize) {
+    let mut tmp  = [0u8; TMP_BUF_STRIDE * 16];
+    let mut tmp2 = [0u8; TMP_BUF_STRIDE * 16];
+    h264_mc22(&mut tmp,  TMP_BUF_STRIDE, src, sstride, w, h);
+    h264_mc02(&mut tmp2, TMP_BUF_STRIDE, &src[sstride..], sstride, w, h);
+    mc_avg_tmp(dst, dstride, w, h, &tmp, &tmp2);
+}
+
+fn h264_mc33(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, w: usize, h: usize) {
+    let mut tmp  = [0u8; TMP_BUF_STRIDE * 16];
+    let mut tmp2 = [0u8; TMP_BUF_STRIDE * 16];
+    h264_mc20(&mut tmp,  TMP_BUF_STRIDE, &src[1..], sstride, w, h);
+    h264_mc02(&mut tmp2, TMP_BUF_STRIDE, &src[sstride..], sstride, w, h);
+    mc_avg_tmp(dst, dstride, w, h, &tmp, &tmp2);
+}
+
+
+fn chroma_interp(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, dx: u16, dy: u16, w: usize, h: usize) {
+    let a0 = 8 - dx;
+    let a1 = dx;
+    let b0 = 8 - dy;
+    let b1 = dy;
+
+    let src1 = &src[sstride..];
+    for (drow, (line0, line1)) in dst.chunks_mut(dstride).zip(src.chunks(sstride).zip(src1.chunks(sstride))).take(h) {
+        let mut a = line0[0];
+        let mut c = line1[0];
+        for (pix, (&b, &d)) in drow.iter_mut().take(w).zip(line0[1..].iter().zip(line1[1..].iter())) {
+            *pix = ((u16::from(a) * a0 * b0 + u16::from(b) * a1 * b0 + u16::from(c) * a0 * b1 + u16::from(d) * a1 * b1 + 0x20) >> 6) as u8;
+            a = b;
+            c = d;
+        }
+    }
+}
+
+const H264_LUMA_INTERP: &[BlkInterpFunc] = &[
+    h264_mc00, h264_mc01, h264_mc02, h264_mc03,
+    h264_mc10, h264_mc11, h264_mc12, h264_mc13,
+    h264_mc20, h264_mc21, h264_mc22, h264_mc23,
+    h264_mc30, h264_mc31, h264_mc32, h264_mc33
+];
+
+pub fn do_mc(frm: &mut NASimpleVideoFrame<u8>, refpic: NAVideoBufferRef<u8>, xpos: usize, ypos: usize, w: usize, h: usize, mv: MV) {
+    let mode = ((mv.x & 3) + (mv.y & 3) * 4) as usize;
+    copy_block(frm, refpic.clone(), 0, xpos, ypos, mv.x >> 2, mv.y >> 2, w, h, 2, 3, mode, H264_LUMA_INTERP);
+
+    let (cw, ch) = refpic.get_dimensions(1);
+    let mvx = mv.x >> 3;
+    let mvy = mv.y >> 3;
+    let dx = (mv.x & 7) as u16;
+    let dy = (mv.y & 7) as u16;
+    let mut ebuf = [0u8; 18 * 9];
+    let src_x = ((xpos >> 1) as isize) + (mvx as isize);
+    let src_y = ((ypos >> 1) as isize) + (mvy as isize);
+    let suoff = refpic.get_offset(1);
+    let svoff = refpic.get_offset(2);
+    let sustride = refpic.get_stride(1);
+    let svstride = refpic.get_stride(2);
+    let src = refpic.get_data();
+    let cbw = w / 2;
+    let cbh = h / 2;
+    let (csrc, cstride) = if (src_x < 0) || (src_x + (cbw as isize) + 1 > (cw as isize)) || (src_y < 0) || (src_y + (cbh as isize) + 1 > (ch as isize)) {
+            edge_emu(&refpic, src_x, src_y, cbw+1, cbh+1, &mut ebuf,      18, 1, 4);
+            edge_emu(&refpic, src_x, src_y, cbw+1, cbh+1, &mut ebuf[9..], 18, 2, 4);
+            ([&ebuf, &ebuf[9..]], [18, 18])
+        } else {
+            ([&src[suoff + (src_x as usize) + (src_y as usize) * sustride..],
+             &src[svoff + (src_x as usize) + (src_y as usize) * svstride..]],
+             [sustride, svstride])
+        };
+    for chroma in 1..3 {
+        let off = frm.offset[chroma] + xpos / 2 + (ypos / 2) * frm.stride[chroma];
+        chroma_interp(&mut frm.data[off..], frm.stride[chroma], csrc[chroma - 1], cstride[chroma - 1], dx, dy, cbw, cbh);
+    }
+}
+
+pub fn gray_block(frm: &mut NASimpleVideoFrame<u8>, x: usize, y: usize, w: usize, h: usize) {
+    let yoff = frm.offset[0] + x + y * frm.stride[0];
+    let coff = [frm.offset[1] + x / 2 + y / 2 * frm.stride[1],
+                frm.offset[2] + x / 2 + y / 2 * frm.stride[2]];
+    if w == 16 && h == 16 {
+        IPRED_FUNCS16X16[IPRED8_DC128](frm.data, yoff, frm.stride[0]);
+        for chroma in 1..2 {
+            IPRED_FUNCS8X8_CHROMA[IPRED8_DC128](frm.data, coff[chroma - 1], frm.stride[chroma]);
+        }
+    } else if w == 8 && h == 8 {
+        IPRED_FUNCS8X8_CHROMA[IPRED8_DC128](frm.data, yoff, frm.stride[0]);
+        for chroma in 1..2 {
+            IPRED_FUNCS4X4[IPRED4_DC128](frm.data, coff[chroma - 1], frm.stride[chroma], &[128; 4]);
+        }
+    } else {
+        for row in frm.data[yoff..].chunks_mut(frm.stride[0]).take(h) {
+            for el in row[..w].iter_mut() {
+                *el = 128;
+            }
+        }
+        for chroma in 0..2 {
+            for row in frm.data[coff[chroma]..].chunks_mut(frm.stride[chroma + 1]).take(h / 2) {
+                for el in row[..w / 2].iter_mut() {
+                    *el = 128;
+                }
+            }
+        }
+    }
+}
+
+pub fn do_mc_avg(frm: &mut NASimpleVideoFrame<u8>, refpic: NAVideoBufferRef<u8>, xpos: usize, ypos: usize, w: usize, h: usize, mv: MV, avg_buf: &mut NAVideoBufferRef<u8>) {
+    let mut afrm = NASimpleVideoFrame::from_video_buf(avg_buf).unwrap();
+    let amv = MV { x: mv.x + (xpos as i16) * 4, y: mv.y + (ypos as i16) * 4 };
+    do_mc(&mut afrm, refpic, 0, 0, w, h, amv);
+    for comp in 0..3 {
+        let shift = if comp == 0 { 0 } else { 1 };
+        avg(&mut frm.data[frm.offset[comp] + (xpos >> shift) + (ypos >> shift) * frm.stride[comp]..], frm.stride[comp], &afrm.data[afrm.offset[comp]..], afrm.stride[comp], w >> shift, h >> shift);
+    }
+}
+
+macro_rules! loop_filter {
+    (lumaedge; $buf: expr, $off: expr, $step: expr, $alpha: expr, $beta: expr) => {
+        let p2 = i16::from($buf[$off - $step * 3]);
+        let p1 = i16::from($buf[$off - $step * 2]);
+        let p0 = i16::from($buf[$off - $step]);
+        let q0 = i16::from($buf[$off]);
+        let q1 = i16::from($buf[$off + $step]);
+        let q2 = i16::from($buf[$off + $step * 2]);
+        let a_p = (p2 - p0).abs() < $beta;
+        let a_q = (q2 - q0).abs() < $beta;
+        if a_p && (p0 - q0).abs() < (($alpha >> 2) + 2) {
+            let p3 = i16::from($buf[$off - $step * 4]);
+            $buf[$off - $step * 3] = ((2 * p3 + 3 * p2 + p1 + p0 + q0 + 4) >> 3) as u8;
+            $buf[$off - $step * 2] = ((p2 + p1 + p0 + q0 + 2) >> 2) as u8;
+            $buf[$off - $step] = ((p2 + 2 * p1 + 2 * p0 + 2 * q0 + q1 + 4) >> 3) as u8;
+        } else {
+            $buf[$off - $step] = ((2 * p1 + p0 + q1 + 2) >> 2) as u8;
+        }
+        if a_q && (p0 - q0).abs() < (($alpha >> 2) + 2) {
+            let q3 = i16::from($buf[$off + $step * 3]);
+            $buf[$off]             = ((p1 + 2 * p0 + 2 * q0 + 2 * q1 + q2 + 4) >> 3) as u8;
+            $buf[$off + $step]     = ((p0 + q0 + q1 + q2 + 2) >> 2) as u8;
+            $buf[$off + $step * 2] = ((2 * q3 + 3 * q2 + q1 + q0 + p0 + 4) >> 3) as u8;
+        } else {
+            $buf[$off] = ((2 * q1 + q0 + p1 + 2) >> 2) as u8;
+        }
+    };
+    (chromaedge; $buf: expr, $off: expr, $step: expr) => {
+        let p1 = i16::from($buf[$off - $step * 2]);
+        let p0 = i16::from($buf[$off - $step]);
+        let q0 = i16::from($buf[$off]);
+        let q1 = i16::from($buf[$off + $step]);
+        $buf[$off - $step] = ((2 * p1 + p0 + q1 + 2) >> 2) as u8;
+        $buf[$off]         = ((2 * q1 + q0 + p1 + 2) >> 2) as u8;
+    };
+    (lumanormal; $buf: expr, $off: expr, $step: expr, $tc0: expr, $beta: expr) => {
+        let p2 = i16::from($buf[$off - $step * 3]);
+        let p1 = i16::from($buf[$off - $step * 2]);
+        let p0 = i16::from($buf[$off - $step]);
+        let q0 = i16::from($buf[$off]);
+        let q1 = i16::from($buf[$off + $step]);
+        let q2 = i16::from($buf[$off + $step * 2]);
+        let a_p = (p2 - p0).abs() < $beta;
+        let a_q = (q2 - q0).abs() < $beta;
+        let tc = $tc0 + (a_p as i16) + (a_q as i16);
+        let delta = (((q0 - p0) * 4 + (p1 - q1) + 4) >> 3).max(-tc).min(tc);
+        if a_p && ($tc0 > 0) {
+            $buf[$off - $step * 2] = clip8(p1 + ((p2 + ((p0 + q0 + 1) >> 1) - p1 * 2) >> 1).max(-$tc0).min($tc0));
+        }
+        $buf[$off - $step] = clip8(p0 + delta);
+        $buf[$off]         = clip8(q0 - delta);
+        if a_q && ($tc0 > 0) {
+            $buf[$off + $step] = clip8(q1 + ((q2 + ((p0 + q0 + 1) >> 1) - q1 * 2) >> 1).max(-$tc0).min($tc0));
+        }
+    };
+    (chromanormal; $buf: expr, $off: expr, $step: expr, $tc0: expr) => {
+        let p1 = i16::from($buf[$off - $step * 2]);
+        let p0 = i16::from($buf[$off - $step]);
+        let q0 = i16::from($buf[$off]);
+        let q1 = i16::from($buf[$off + $step]);
+        let tc = $tc0 + 1;
+        let delta = (((q0 - p0) * 4 + (p1 - q1) + 4) >> 3).max(-tc).min(tc);
+        $buf[$off - $step] = clip8(p0 + delta);
+        $buf[$off]         = clip8(q0 - delta);
+    }
+}
+
+fn check_filter(buf: &[u8], off: usize, step: usize, alpha: i16, beta: i16) -> bool {
+    let p1 = i16::from(buf[off - step * 2]);
+    let p0 = i16::from(buf[off - step]);
+    let q0 = i16::from(buf[off]);
+    let q1 = i16::from(buf[off + step]);
+    (p0 - q0).abs() < alpha && (p1 - p0).abs() < beta && (q1 - q0).abs() < beta
+}
+
+pub fn loop_filter_lumaedge_v(dst: &mut [u8], mut off: usize, stride: usize, alpha: i16, beta: i16) {
+    for _ in 0..4 {
+        if check_filter(dst, off, 1, alpha, beta) {
+            loop_filter!(lumaedge; dst, off, 1, alpha, beta);
+        }
+        off += stride;
+    }
+}
+pub fn loop_filter_lumaedge_h(dst: &mut [u8], off: usize, stride: usize, alpha: i16, beta: i16) {
+    for x in 0..4 {
+        if check_filter(dst, off + x, stride, alpha, beta) {
+            loop_filter!(lumaedge; dst, off + x, stride, alpha, beta);
+        }
+    }
+}
+pub fn loop_filter_lumanormal_v(dst: &mut [u8], mut off: usize, stride: usize, alpha: i16, beta: i16, tc0: i16) {
+    for _ in 0..4 {
+        if check_filter(dst, off, 1, alpha, beta) {
+            loop_filter!(lumanormal; dst, off, 1, tc0, beta);
+        }
+        off += stride;
+    }
+}
+pub fn loop_filter_lumanormal_h(dst: &mut [u8], off: usize, stride: usize, alpha: i16, beta: i16, tc0: i16) {
+    for x in 0..4 {
+        if check_filter(dst, off + x, stride, alpha, beta) {
+            loop_filter!(lumanormal; dst, off + x, stride, tc0, beta);
+        }
+    }
+}
+pub fn loop_filter_chromaedge_v(dst: &mut [u8], mut off: usize, stride: usize, alpha: i16, beta: i16) {
+    for _ in 0..4 {
+        if check_filter(dst, off, 1, alpha, beta) {
+            loop_filter!(chromaedge; dst, off, 1);
+        }
+        off += stride;
+    }
+}
+pub fn loop_filter_chromaedge_h(dst: &mut [u8], off: usize, stride: usize, alpha: i16, beta: i16) {
+    for x in 0..4 {
+        if check_filter(dst, off + x, stride, alpha, beta) {
+            loop_filter!(chromaedge; dst, off + x, stride);
+        }
+    }
+}
+pub fn loop_filter_chromanormal_v(dst: &mut [u8], mut off: usize, stride: usize, alpha: i16, beta: i16, tc0: i16) {
+    for _ in 0..4 {
+        if check_filter(dst, off, 1, alpha, beta) {
+            loop_filter!(chromanormal; dst, off, 1, tc0);
+        }
+        off += stride;
+    }
+}
+pub fn loop_filter_chromanormal_h(dst: &mut [u8], off: usize, stride: usize, alpha: i16, beta: i16, tc0: i16) {
+    for x in 0..4 {
+        if check_filter(dst, off + x, stride, alpha, beta) {
+            loop_filter!(chromanormal; dst, off + x, stride, tc0);
+        }
+    }
+}
diff --git a/nihav-itu/src/codecs/h264/loopfilter.rs b/nihav-itu/src/codecs/h264/loopfilter.rs
new file mode 100644 (file)
index 0000000..a5ae123
--- /dev/null
@@ -0,0 +1,210 @@
+use nihav_core::frame::NASimpleVideoFrame;
+use super::types::SliceState;
+use super::dsp::*;
+
+const ALPHA: [i16; 52] = [
+      0,   0,   0,   0,  0,  0,  0,  0,  0,  0,   0,   0,   0,   0,   0,   0,
+      4,   4,   5,   6,  7,  8,  9, 10, 12, 13,  15,  17,  20,  22,  25,  28,
+     32,  36,  40,  45, 50, 56, 63, 71, 80, 90, 100, 113, 127, 144, 162, 182,
+    203, 226, 255, 255
+];
+const BETA: [i16; 52] = [
+     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+     2,  2,  2,  3,  3,  3,  3,  4,  4,  4,  6,  6,  7,  7,  8,  8,
+     9,  9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16,
+    17, 17, 18, 18
+];
+
+const TC0: [[u8; 3]; 52] = [
+    [ 0,  0,  0], [ 0,  0,  0], [ 0,  0,  0], [ 0,  0,  0],
+    [ 0,  0,  0], [ 0,  0,  0], [ 0,  0,  0], [ 0,  0,  0],
+    [ 0,  0,  0], [ 0,  0,  0], [ 0,  0,  0], [ 0,  0,  0],
+    [ 0,  0,  0], [ 0,  0,  0], [ 0,  0,  0], [ 0,  0,  0],
+    [ 0,  0,  0], [ 0,  0,  1], [ 0,  0,  1], [ 0,  0,  1],
+    [ 0,  0,  1], [ 0,  1,  1], [ 0,  1,  1], [ 1,  1,  1],
+    [ 1,  1,  1], [ 1,  1,  1], [ 1,  1,  1], [ 1,  1,  2],
+    [ 1,  1,  2], [ 1,  1,  2], [ 1,  1,  2], [ 1,  2,  3],
+    [ 1,  2,  3], [ 2,  2,  3], [ 2,  2,  4], [ 2,  3,  4],
+    [ 2,  3,  4], [ 3,  3,  5], [ 3,  4,  6], [ 3,  4,  6],
+    [ 4,  5,  7], [ 4,  5,  8], [ 4,  6,  9], [ 5,  7, 10],
+    [ 6,  8, 11], [ 6,  8, 13], [ 7, 10, 14], [ 8, 11, 16],
+    [ 9, 12, 18], [10, 13, 20], [11, 15, 23], [13, 17, 25]
+];
+
+fn get_lf_idx(qp0: u8, qp1: u8, off: i8) -> usize {
+    (i16::from((qp0 + qp1 + 1) >> 1) + i16::from(off)).max(0).min(51) as usize
+}
+
+fn filter_mb_row4_y(dst: &mut [u8], off: usize, stride: usize, dmodes: [u8; 4], quants: [u8; 3], alpha_off: i8, beta_off: i8) {
+    let q = quants[0];
+    let qleft = quants[1];
+    let dmode = dmodes[0] & 0xF;
+    if dmode != 0 {
+        let index_a_y = get_lf_idx(q, qleft, alpha_off);
+        let alpha_y = ALPHA[index_a_y];
+        let beta_y = BETA[get_lf_idx(q, qleft, beta_off)];
+        if dmode == 4 {
+            loop_filter_lumaedge_v(dst, off, stride, alpha_y, beta_y);
+        } else {
+            let tc0 = i16::from(TC0[index_a_y][(dmode - 1) as usize]);
+            loop_filter_lumanormal_v(dst, off, stride, alpha_y, beta_y, tc0);
+        }
+    }
+    let index_a_y = get_lf_idx(q, q, alpha_off);
+    let alpha_y = ALPHA[index_a_y];
+    let beta_y = BETA[get_lf_idx(q, q, beta_off)];
+
+    for i in 1..4 {
+        let dmode = dmodes[i] & 0xF;
+        if dmode != 0 {
+            let tc0 = i16::from(TC0[index_a_y][(dmode - 1) as usize]);
+            loop_filter_lumanormal_v(dst, off + i * 4, stride, alpha_y, beta_y, tc0);
+        }
+    }
+
+    let qtop = quants[2];
+    let index_a_y = get_lf_idx(q, qtop, alpha_off);
+    let alpha_y = ALPHA[index_a_y];
+    let beta_y = BETA[get_lf_idx(q, qtop, beta_off)];
+    for i in 0..4 {
+        let dmode = dmodes[i] >> 4;
+        if dmode == 4 {
+            loop_filter_lumaedge_h(dst, off + i * 4, stride, alpha_y, beta_y);
+        } else if dmode != 0 {
+            let tc0 = i16::from(TC0[index_a_y][(dmode - 1) as usize]);
+            loop_filter_lumanormal_h(dst, off + i * 4, stride, alpha_y, beta_y, tc0);
+        }
+    }
+}
+
+fn filter_mb_row4_c(dst: &mut [u8], off: usize, stride: usize, dmodes: [u8; 4], quants: [u8; 3], alpha_off: i8, beta_off: i8) {
+    let q = quants[0];
+    let qleft = quants[1];
+
+    let dmode = dmodes[0] & 0xF;
+    if dmode != 0 {
+        let index_a_c = get_lf_idx(q, qleft, alpha_off);
+        let alpha_c = ALPHA[index_a_c];
+        let beta_c = BETA[get_lf_idx(q, qleft, beta_off)];
+        if dmode == 4 {
+            loop_filter_chromaedge_v(dst, off, stride, alpha_c, beta_c);
+        } else {
+            let tc0 = i16::from(TC0[index_a_c][(dmode - 1) as usize]);
+            loop_filter_chromanormal_v(dst, off, stride, alpha_c, beta_c, tc0);
+        }
+    }
+    let dmode = dmodes[2] & 0xF;
+    if dmode != 0 {
+        let index_a_c = get_lf_idx(q, q, alpha_off);
+        let alpha_c = ALPHA[index_a_c];
+        let beta_c = BETA[get_lf_idx(q, q, beta_off)];
+        let tc0 = i16::from(TC0[index_a_c][(dmode - 1) as usize]);
+        loop_filter_chromanormal_v(dst, off + 4, stride, alpha_c, beta_c, tc0);
+    }
+
+    let qtop = quants[2];
+    let index_a_c = get_lf_idx(q, qtop, alpha_off);
+    let alpha_c = ALPHA[index_a_c];
+    let beta_c = BETA[get_lf_idx(q, qtop, beta_off)];
+    for i in 0..2 {
+        let dmode = dmodes[i * 2] >> 4;
+        if dmode == 4 {
+            loop_filter_chromaedge_h(dst, off + i * 4, stride, alpha_c, beta_c);
+        } else if dmode != 0 {
+            let tc0 = i16::from(TC0[index_a_c][(dmode - 1) as usize]);
+            loop_filter_chromanormal_h(dst, off + i * 4, stride, alpha_c, beta_c, tc0);
+        }
+    }
+}
+
+pub fn loop_filter_row(frm: &mut NASimpleVideoFrame<u8>, sstate: &SliceState, alpha_off: i8, beta_off: i8) {
+    let mut db_idx = sstate.deblock.xpos - sstate.deblock.stride;
+    let mut yoff = frm.offset[0] + sstate.mb_y * 16 * frm.stride[0];
+    let mut uoff = frm.offset[1] + sstate.mb_y *  8 * frm.stride[1];
+    let mut voff = frm.offset[2] + sstate.mb_y *  8 * frm.stride[2];
+    let mut tlq = [0; 3];
+    let mut lq  = [0; 3];
+    let mut mb_idx = sstate.mb.xpos;
+    for _mb_x in 0..sstate.mb_w {
+        let mut tqy = sstate.mb.data[mb_idx - sstate.mb.stride].qp_y;
+        let     tqu = sstate.mb.data[mb_idx - sstate.mb.stride].qp_u;
+        let     tqv = sstate.mb.data[mb_idx - sstate.mb.stride].qp_v;
+        if sstate.mb_y > 0 {
+            let dmodes = [sstate.deblock.data[db_idx],
+                          sstate.deblock.data[db_idx + 1],
+                          sstate.deblock.data[db_idx + 2],
+                          sstate.deblock.data[db_idx + 3]];
+
+            filter_mb_row4_y(frm.data, yoff - frm.stride[0] * 4, frm.stride[0], dmodes, [tqy, tlq[0], tqy], alpha_off, beta_off);
+            filter_mb_row4_c(frm.data, uoff - frm.stride[1] * 4, frm.stride[1], dmodes, [tqu, tlq[1], tqu], alpha_off, beta_off);
+            filter_mb_row4_c(frm.data, voff - frm.stride[2] * 4, frm.stride[2], dmodes, [tqv, tlq[2], tqv], alpha_off, beta_off);
+
+            tlq = [tqy, tqu, tqv];
+        }
+
+        let qy = sstate.mb.data[mb_idx].qp_y;
+        let qu = sstate.mb.data[mb_idx].qp_u;
+        let qv = sstate.mb.data[mb_idx].qp_v;
+
+        for y in 0..3 {
+            db_idx += sstate.deblock.stride;
+            let dmodes = [sstate.deblock.data[db_idx],
+                          sstate.deblock.data[db_idx + 1],
+                          sstate.deblock.data[db_idx + 2],
+                          sstate.deblock.data[db_idx + 3]];
+
+            filter_mb_row4_y(frm.data, yoff + frm.stride[0] * 4 * y, frm.stride[0], dmodes, [qy, lq[0], tqy], alpha_off, beta_off);
+            if y == 0 {
+                filter_mb_row4_c(frm.data, uoff + frm.stride[1] * 2 * y, frm.stride[1], dmodes, [qu, lq[1], tqu], alpha_off, beta_off);
+                filter_mb_row4_c(frm.data, voff + frm.stride[2] * 2 * y, frm.stride[2], dmodes, [qv, lq[2], tqv], alpha_off, beta_off);
+            }
+            tqy = qy;
+        }
+        db_idx -= sstate.deblock.stride * 3;
+        lq = [qy, qu, qv];
+
+        mb_idx += 1;
+        db_idx += 4;
+        yoff += 16;
+        uoff += 8;
+        voff += 8;
+    }
+}
+pub fn loop_filter_last(frm: &mut NASimpleVideoFrame<u8>, sstate: &SliceState, alpha_off: i8, beta_off: i8) {
+    let mut db_idx = sstate.deblock.xpos + 3 * sstate.deblock.stride;
+    let mut yoff = frm.offset[0] + (sstate.mb_y * 16 + 12) * frm.stride[0];
+    let mut uoff = frm.offset[1] + (sstate.mb_y *  8 +  4) * frm.stride[1];
+    let mut voff = frm.offset[2] + (sstate.mb_y *  8 +  4) * frm.stride[2];
+
+    let mut lq = [0; 3];
+    let mut mb_idx = sstate.mb.xpos;
+    if sstate.mb_y != 0 && sstate.mb_x == 0 {
+        db_idx -= 4 * sstate.deblock.stride;
+        mb_idx -= sstate.mb.stride;
+        yoff -= 16 * frm.stride[0];
+        uoff -=  8 * frm.stride[1];
+        voff -=  8 * frm.stride[2];
+    }
+    for _mb_x in 0..sstate.mb_w {
+        let qy = sstate.mb.data[mb_idx].qp_y;
+        let qu = sstate.mb.data[mb_idx].qp_u;
+        let qv = sstate.mb.data[mb_idx].qp_v;
+
+        let dmodes = [sstate.deblock.data[db_idx],
+                      sstate.deblock.data[db_idx + 1],
+                      sstate.deblock.data[db_idx + 2],
+                      sstate.deblock.data[db_idx + 3]];
+
+        filter_mb_row4_y(frm.data, yoff, frm.stride[0], dmodes, [qy, lq[0], qy], alpha_off, beta_off);
+        filter_mb_row4_c(frm.data, uoff, frm.stride[1], dmodes, [qu, lq[1], qu], alpha_off, beta_off);
+        filter_mb_row4_c(frm.data, voff, frm.stride[2], dmodes, [qv, lq[2], qv], alpha_off, beta_off);
+
+        lq = [qy, qu, qv];
+        mb_idx += 1;
+        db_idx += 4;
+        yoff += 16;
+        uoff += 8;
+        voff += 8;
+    }
+}
+
diff --git a/nihav-itu/src/codecs/h264/mod.rs b/nihav-itu/src/codecs/h264/mod.rs
new file mode 100644 (file)
index 0000000..5ecd05c
--- /dev/null
@@ -0,0 +1,1601 @@
+/*
+ known bugs and limitations:
+  * weighted motion compensation is not implemented
+  * wrong slice boundary filtering
+  * not fully correct deblock strength selection for P/B-macroblocks
+  * scaling lists for 4x4 blocks
+*/
+use nihav_core::codecs::*;
+use nihav_core::io::byteio::*;
+use nihav_core::io::bitreader::*;
+use nihav_core::io::intcode::*;
+use nihav_codec_support::codecs::{MV, ZERO_MV};
+
+mod types;
+pub use types::*;
+mod pic_ref;
+pub use pic_ref::*;
+mod dsp;
+use dsp::*;
+mod cabac;
+use cabac::*;
+mod cabac_coder;
+use cabac_coder::*;
+mod cavlc;
+use cavlc::*;
+mod loopfilter;
+use loopfilter::*;
+mod sets;
+use sets::*;
+mod slice;
+use slice::*;
+
+trait ReadUE {
+    fn read_ue(&mut self) -> DecoderResult<u32>;
+    fn read_te(&mut self, range: u32) -> DecoderResult<u32>;
+    fn read_ue_lim(&mut self, max_val: u32) -> DecoderResult<u32> {
+        let val = self.read_ue()?;
+        validate!(val <= max_val);
+        Ok(val)
+    }
+    fn read_se(&mut self) -> DecoderResult<i32> {
+        let val = self.read_ue()?;
+        if (val & 1) != 0 {
+            Ok (((val >> 1) as i32) + 1)
+        } else {
+            Ok (-((val >> 1) as i32))
+        }
+    }
+}
+
+impl<'a> ReadUE for BitReader<'a> {
+    fn read_ue(&mut self) -> DecoderResult<u32> {
+        Ok(self.read_code(UintCodeType::GammaP)? - 1)
+    }
+    fn read_te(&mut self, range: u32) -> DecoderResult<u32> {
+        if range == 1 {
+            if self.read_bool()? {
+                Ok(0)
+            } else {
+                Ok(1)
+            }
+        } else {
+            let val = self.read_ue()?;
+            validate!(val <= range);
+            Ok(val)
+        }
+    }
+}
+
+#[derive(Clone,Copy)]
+pub struct Coeff8x8 {
+    pub coeffs:     [i16; 64],
+}
+
+impl Coeff8x8 {
+    fn clear(&mut self) {
+        self.coeffs = [0; 64];
+    }
+}
+
+impl Default for Coeff8x8 {
+    fn default() -> Self {
+        Self {
+            coeffs: [0; 64],
+        }
+    }
+}
+
+#[derive(Clone,Copy,Default)]
+pub struct CurrentMBInfo {
+    pub mb_type:        MBType,
+    pub sub_mb_type:    [SubMBType; 4],
+    pub ipred:          [IntraPredMode; 16],
+    pub chroma_ipred:   u8,
+    pub luma_ipred:     [u8; 16],
+    pub mv_l0:          [MV; 16],
+    pub ref_l0:         [PicRef; 4],
+    pub mv_l1:          [MV; 16],
+    pub ref_l1:         [PicRef; 4],
+    pub qp_y:           u8,
+    pub cbpy:           u8,
+    pub cbpc:           u8,
+    pub coeffs:         [[i16; 16]; 25],
+    pub coeffs8x8:      [Coeff8x8; 4],
+    pub chroma_dc:      [[i16; 4]; 2],
+    pub coded:          [bool; 25],
+    pub transform_size_8x8: bool,
+}
+
+impl CurrentMBInfo {
+    fn clear_coeffs8x8(&mut self) {
+        for c in self.coeffs8x8.iter_mut() {
+            c.clear();
+        }
+    }
+    fn can_have_8x8_tx(&self, inference_flag: bool) -> bool {
+        match self.mb_type {
+            MBType::Intra4x4 | MBType::Intra8x8 | MBType::Intra16x16(_, _, _) | MBType::PCM => false,
+            MBType::P8x8 | MBType::P8x8Ref0 | MBType::B8x8 => {
+                for &sub_id in self.sub_mb_type.iter() {
+                    match sub_id {
+                        SubMBType::P8x8 |
+                        SubMBType::B8x8(_)
+                            => {},
+                        SubMBType::Direct8x8
+                            => if !inference_flag { return false; },
+                        _ => return false,
+                    };
+                }
+                true
+            },
+            MBType::Direct => inference_flag,
+            _ => true,
+        }
+    }
+}
+
+fn get_long_term_id(is_idr: bool, slice_hdr: &SliceHeader) -> Option<usize> {
+    if is_idr && !slice_hdr.long_term_reference {
+        None
+    } else {
+        let marking = &slice_hdr.adaptive_ref_pic_marking;
+        for (&op, &arg) in marking.memory_management_control_op.iter().zip(marking.operation_arg.iter()).take(marking.num_ops) {
+            if op == 6 {
+                return Some(arg as usize);
+            }
+        }
+        None
+    }
+}
+
+struct H264Decoder {
+    info:       NACodecInfoRef,
+    width:      usize,
+    height:     usize,
+    num_mbs:    usize,
+    nal_len:    u8,
+    sps:        Vec<SeqParameterSet>,
+    cur_sps:    usize,
+    pps:        Vec<PicParameterSet>,
+    cur_pps:    usize,
+
+    skip_mode:      FrameSkipMode,
+    deblock_skip:   bool,
+
+    is_mbaff:   bool,
+
+    cavlc_cb:   CAVLCTables,
+
+    sstate:     SliceState,
+
+    cur_pic:    Option<PictureInfo>,
+    cur_id:     u16,
+    has_pic:    bool,
+    frame_refs: FrameRefs,
+
+    temporal_mv:    bool,
+    deblock_mode:   u8,
+    lf_alpha:       i8,
+    lf_beta:        i8,
+    is_s:           bool,
+
+    ipcm_buf:   [u8; 256 + 64 + 64],
+
+    avg_buf:    NAVideoBufferRef<u8>,
+
+    transform_8x8_mode: bool,
+}
+
+fn unescape_nal(src: &[u8], dst: &mut Vec<u8>) -> usize {
+    let mut off = 0;
+    let mut zrun = 0;
+    dst.truncate(0);
+    dst.reserve(src.len());
+    while off < src.len() {
+        dst.push(src[off]);
+        if src[off] != 0 {
+            zrun = 0;
+        } else {
+            zrun += 1;
+            if zrun == 2 && off + 1 < src.len() && src[off + 1] == 0x03 {
+                zrun = 0;
+                off += 1;
+            }
+            if zrun >= 3 && off + 1 < src.len() && src[off + 1] == 0x01 {
+                off -= 3;
+                dst.truncate(off);
+                break;
+            }
+        }
+        off += 1;
+    }
+    off
+}
+
+impl H264Decoder {
+    fn new() -> Self {
+        let avg_vi = NAVideoInfo { width: 32, height: 32, flipped: false, format: YUV420_FORMAT, bits: 12 };
+        let avg_buf = alloc_video_buffer(avg_vi, 4).unwrap().get_vbuf().unwrap();
+        H264Decoder{
+            info:       NACodecInfoRef::default(),
+            width:      0,
+            height:     0,
+            num_mbs:    0,
+            nal_len:    0,
+            sps:        Vec::with_capacity(1),
+            cur_sps:    0,
+            pps:        Vec::with_capacity(3),
+            cur_pps:    0,
+
+            skip_mode:      FrameSkipMode::default(),
+            deblock_skip:   false,
+
+            is_mbaff:   false,
+
+            cavlc_cb:   CAVLCTables::new(),
+
+            sstate:     SliceState::new(),
+            cur_pic:    None,
+            cur_id:     0,
+            has_pic:    false,
+            frame_refs: FrameRefs::new(),
+
+            temporal_mv:        false,
+            deblock_mode:       0,
+            lf_alpha:           0,
+            lf_beta:            0,
+            is_s:               false,
+
+            ipcm_buf:   [0; 256 + 64 + 64],
+
+            avg_buf,
+
+            transform_8x8_mode: false,
+        }
+    }
+    fn handle_nal(&mut self, src: &[u8], supp: &mut NADecoderSupport, skip_decoding: bool) -> DecoderResult<()> {
+        validate!(!src.is_empty());
+        validate!((src[0] & 0x80) == 0);
+        let nal_ref_idc   = src[0] >> 5;
+        let nal_unit_type = src[0] & 0x1F;
+
+        let mut full_size = src.len() * 8;
+        for &byte in src.iter().rev() {
+            if byte == 0 {
+                full_size -= 8;
+            } else {
+                full_size -= (byte.trailing_zeros() + 1) as usize;
+                break;
+            }
+        }
+        validate!(full_size > 0);
+        match nal_unit_type {
+             1 | 5 if !skip_decoding => {
+                let is_idr = nal_unit_type == 5;
+                let mut br = BitReader::new(&src[..(full_size + 7)/8], BitReaderMode::BE);
+                                                    br.skip(8)?;
+
+                let slice_hdr = parse_slice_header(&mut br, &self.sps, &self.pps, is_idr, nal_ref_idc)?;
+                validate!(br.tell() < full_size);
+                let full_id;
+                if slice_hdr.first_mb_in_slice == 0 {
+                    validate!(self.cur_pic.is_none());
+                    for (i, pps) in self.pps.iter().enumerate() {
+                        if pps.pic_parameter_set_id == slice_hdr.pic_parameter_set_id {
+                            self.cur_pps = i;
+                            break;
+                        }
+                    }
+                    for (i, sps) in self.sps.iter().enumerate() {
+                        if sps.seq_parameter_set_id == self.pps[self.cur_pps].seq_parameter_set_id {
+                            self.cur_sps = i;
+                            break;
+                        }
+                    }
+
+                    full_id = self.frame_refs.calc_picture_num(&slice_hdr, is_idr, nal_ref_idc, &self.sps[self.cur_sps]);
+
+                    let sps = &self.sps[self.cur_sps];
+                    if sps.chroma_format_idc != 1 || sps.bit_depth_luma != 8 || sps.bit_depth_chroma != 8 {
+println!(" chroma fmt {} bits {}/{}", sps.chroma_format_idc, sps.bit_depth_luma, sps.bit_depth_chroma);
+                        return Err(DecoderError::NotImplemented);
+                    }
+                    //let pps = &self.pps[self.cur_pps];
+
+                    if is_idr {
+                        self.frame_refs.clear_refs();
+                    }
+
+                    self.width  = sps.pic_width_in_mbs  << 4;
+                    self.height = sps.pic_height_in_mbs << 4;
+                    self.num_mbs = sps.pic_width_in_mbs * sps.pic_height_in_mbs;
+
+                    self.is_mbaff = sps.mb_adaptive_frame_field && !slice_hdr.field_pic;
+                    if self.is_mbaff {
+println!("MBAFF");
+                        return Err(DecoderError::NotImplemented);
+                    }
+                    if !sps.frame_mbs_only {
+println!("PAFF?");
+                        return Err(DecoderError::NotImplemented);
+                    }
+
+//if slice_hdr.slice_type.is_b() { return Ok(()); }
+                    self.cur_id = full_id as u16;
+                } else {
+                    if let Some(ref mut pic) = self.cur_pic {
+                        validate!(pic.cur_mb == slice_hdr.first_mb_in_slice);
+                        let new_type = slice_hdr.slice_type.to_frame_type();
+                        pic.pic_type = match (pic.pic_type, new_type) {
+                                (FrameType::I, _) => new_type,
+                                (_, FrameType::B) => FrameType::B,
+                                _ => pic.pic_type,
+                            };
+                        full_id = pic.full_id;
+                    } else {
+                        return Ok(());//Err(DecoderError::InvalidData);
+                    }
+                    validate!(self.cur_pps < self.pps.len() && self.pps[self.cur_pps].pic_parameter_set_id == slice_hdr.pic_parameter_set_id);
+                }
+
+                let sps = &self.sps[self.cur_sps];
+                let pps = &self.pps[self.cur_pps];
+
+                self.temporal_mv = !slice_hdr.direct_spatial_mv_pred;
+                self.is_s = slice_hdr.slice_type == SliceType::SI || slice_hdr.slice_type == SliceType::SP;
+                self.deblock_mode = slice_hdr.disable_deblocking_filter_idc;
+                self.lf_alpha = slice_hdr.slice_alpha_c0_offset;
+                self.lf_beta  = slice_hdr.slice_beta_offset;
+
+                self.frame_refs.select_refs(sps, &slice_hdr, full_id);
+
+                if slice_hdr.adaptive_ref_pic_marking_mode {
+                    self.frame_refs.apply_adaptive_marking(&slice_hdr.adaptive_ref_pic_marking, slice_hdr.frame_num, 1 << self.sps[self.cur_sps].log2_max_frame_num)?;
+                }
+                if slice_hdr.first_mb_in_slice == 0 {
+                    let ret = supp.pool_u8.get_free();
+                    if ret.is_none() {
+                        return Err(DecoderError::AllocError);
+                    }
+                    let tmp_vinfo = NAVideoInfo::new(self.width, self.height, false, YUV420_FORMAT);
+                    let mut buf = ret.unwrap();
+                    if buf.get_info() != tmp_vinfo {
+                        supp.pool_u8.reset();
+                        supp.pool_u8.prealloc_video(tmp_vinfo, 4)?;
+                        let ret = supp.pool_u8.get_free();
+                        if ret.is_none() {
+                            return Err(DecoderError::AllocError);
+                        }
+                        buf = ret.unwrap();
+                    }
+                    self.cur_pic = Some(PictureInfo {
+                            id: slice_hdr.frame_num,
+                            full_id,
+                            pic_type: slice_hdr.slice_type.to_frame_type(),
+                            buf,
+                            cur_mb: 0,
+                            is_ref: nal_ref_idc != 0,
+                            long_term: get_long_term_id(is_idr, &slice_hdr),
+                            mv_info: FrameMV::new(sps.pic_width_in_mbs, sps.pic_height_in_mbs),
+                        });
+                }
+
+                self.transform_8x8_mode = pps.transform_8x8_mode;
+
+                self.sstate.reset(sps.pic_width_in_mbs, sps.pic_height_in_mbs, slice_hdr.first_mb_in_slice);
+                if !pps.entropy_coding_mode {
+                    self.has_pic = self.decode_slice_cavlc(&mut br, &slice_hdr, full_size)?;
+                } else {
+                    br.align();
+                    let start = (br.tell() / 8) as usize;
+                    let csrc = &src[start..];
+                    validate!(csrc.len() >= 2);
+                    let mut cabac = CABAC::new(csrc, slice_hdr.slice_type, slice_hdr.slice_qp, slice_hdr.cabac_init_idc as usize)?;
+                    self.has_pic = self.decode_slice_cabac(&mut cabac, &slice_hdr)?;
+                }
+                if !self.deblock_skip && self.deblock_mode != 1 {
+                    if let Some(ref mut pic) = self.cur_pic {
+                        let mut frm = NASimpleVideoFrame::from_video_buf(&mut pic.buf).unwrap();
+                        if self.sstate.mb_x != 0 {
+                            loop_filter_row(&mut frm, &self.sstate, self.lf_alpha, self.lf_beta);
+                        }
+                        loop_filter_last(&mut frm, &self.sstate, self.lf_alpha, self.lf_beta);
+                    }
+                }
+            },
+             2 => { // slice data partition A
+                //slice header
+                //slice id = read_ue()
+                //cat 2 slice data (all but MB layer residual)
+                return Err(DecoderError::NotImplemented);
+            },
+             3 => { // slice data partition B
+                //slice id = read_ue()
+                //if pps.redundant_pic_cnt_present { redundant_pic_cnt = read_ue() }
+                //cat 3 slice data (MB layer residual)
+                return Err(DecoderError::NotImplemented);
+            },
+             4 => { // slice data partition C
+                //slice id = read_ue()
+                //if pps.redundant_pic_cnt_present { redundant_pic_cnt = read_ue() }
+                //cat 4 slice data (MB layer residual)
+                return Err(DecoderError::NotImplemented);
+            },
+             6 => {}, //SEI
+             7 => {
+                let sps = parse_sps(&src[1..])?;
+                self.sps.push(sps);
+            },
+             8 => {
+                validate!(full_size >= 8 + 16);
+                let pps = parse_pps(&src[1..], &self.sps, full_size - 8)?;
+                let mut found = false;
+                for stored_pps in self.pps.iter_mut() {
+                    if stored_pps.pic_parameter_set_id == pps.pic_parameter_set_id {
+                        *stored_pps = pps.clone();
+                        found = true;
+                        break;
+                    }
+                }
+                if !found {
+                    self.pps.push(pps);
+                }
+            },
+             9 => { // access unit delimiter
+            },
+            10 => {}, //end of sequence
+            11 => {}, //end of stream
+            12 => {}, //filler
+            _  => {},
+        };
+
+        Ok(())
+    }
+    fn pred_intra(frm: &mut NASimpleVideoFrame<u8>, sstate: &SliceState, mb_info: &CurrentMBInfo) {
+        let yoff = frm.offset[0] + sstate.mb_x * 16 + sstate.mb_y * 16 * frm.stride[0];
+        match mb_info.mb_type {
+            MBType::Intra16x16(imode, _, _) => {
+                let id = if imode != 2 || (sstate.has_top && sstate.has_left) {
+                        imode as usize
+                    } else if !sstate.has_top && !sstate.has_left {
+                        IPRED8_DC128
+                    } else if !sstate.has_left {
+                        IPRED8_DC_TOP
+                    } else {
+                        IPRED8_DC_LEFT
+                    };
+                IPRED_FUNCS16X16[id](frm.data, yoff, frm.stride[0]);
+            },
+            MBType::Intra8x8 => {
+                let mut ictx = IPred8Context::new();
+                for part in 0..4 {
+                    let x = (part & 1) * 2;
+                    let y = part & 2;
+                    let blk4 = x + y * 4;
+
+                    let cur_yoff = yoff + x * 4 + y * 4 * frm.stride[0];
+                    let has_top = y > 0 || sstate.has_top;
+                    let has_left = x > 0 || sstate.has_left;
+                    let imode = mb_info.ipred[blk4];
+                    let id = if imode != IntraPredMode::DC || (has_top && has_left) {
+                            let im_id: u8 = imode.into();
+                            im_id as usize
+                        } else if !has_top && !has_left {
+                            IPRED4_DC128
+                        } else if !has_left {
+                            IPRED4_DC_TOP
+                        } else {
+                            IPRED4_DC_LEFT
+                        };
+                    let mb_idx = sstate.mb_x + sstate.mb_y * sstate.mb_w;
+                    let noright = (y == 2 || sstate.mb_x == sstate.mb_w - 1 || mb_idx < sstate.mb_start + sstate.mb_w) && (x == 2);
+                    let has_tl = (has_top && x > 0) || (has_left && y > 0) || (x == 0 && y == 0 && sstate.mb_x > 0 && mb_idx > sstate.mb_start + sstate.mb_w);
+                    if id != IPRED4_DC128 {
+                        ictx.fill(frm.data, cur_yoff, frm.stride[0], has_top, has_top && !noright, has_left, has_tl);
+                    }
+                    IPRED_FUNCS8X8_LUMA[id](&mut frm.data[cur_yoff..], frm.stride[0], &ictx);
+                    if mb_info.coded[blk4] {
+                        add_coeffs8(frm.data, cur_yoff, frm.stride[0], &mb_info.coeffs8x8[part].coeffs);
+                    }
+                }
+            },
+            MBType::Intra4x4 => {
+                for &(x,y) in I4X4_SCAN.iter() {
+                    let x = x as usize;
+                    let y = y as usize;
+                    let cur_yoff = yoff + x * 4 + y * 4 * frm.stride[0];
+                    let has_top = y > 0 || sstate.has_top;
+                    let has_left = x > 0 || sstate.has_left;
+                    let imode = mb_info.ipred[x + y * 4];
+                    let id = if imode != IntraPredMode::DC || (has_top && has_left) {
+                            let im_id: u8 = imode.into();
+                            im_id as usize
+                        } else if !has_top && !has_left {
+                            IPRED4_DC128
+                        } else if !has_left {
+                            IPRED4_DC_TOP
+                        } else {
+                            IPRED4_DC_LEFT
+                        };
+                    let noright = (sstate.mb_x == sstate.mb_w - 1 || sstate.mb_x + sstate.mb_y * sstate.mb_w < sstate.mb_start + sstate.mb_w) && (x == 3);
+                    let tr: [u8; 4] = if y == 0 {
+                            if has_top && !noright {
+                                let i = cur_yoff - frm.stride[0];
+                                [frm.data[i + 4], frm.data[i + 5], frm.data[i + 6], frm.data[i + 7]]
+                            } else if has_top {
+                                let i = cur_yoff - frm.stride[0];
+                                [frm.data[i + 3], frm.data[i + 3], frm.data[i + 3], frm.data[i + 3]]
+                            } else {
+                                [0; 4]
+                            }
+                        } else if (x & 1) == 0 || (x == 1 && y == 2) {
+                            let i = cur_yoff - frm.stride[0];
+                            [frm.data[i + 4], frm.data[i + 5], frm.data[i + 6], frm.data[i + 7]]
+                        } else {
+                            let i = cur_yoff - frm.stride[0];
+                            [frm.data[i + 3], frm.data[i + 3], frm.data[i + 3], frm.data[i + 3]]
+                        };
+                    IPRED_FUNCS4X4[id](frm.data, cur_yoff, frm.stride[0], &tr);
+                    if mb_info.coded[x + y * 4] {
+                        add_coeffs(frm.data, cur_yoff, frm.stride[0], &mb_info.coeffs[x + y * 4]);
+                    }
+                }
+            },
+            _ => unreachable!(),
+        };
+        let id = if mb_info.chroma_ipred != 0 || (sstate.has_top && sstate.has_left) {
+                mb_info.chroma_ipred as usize
+            } else if !sstate.has_top && !sstate.has_left {
+                IPRED8_DC128
+            } else if !sstate.has_left {
+                IPRED8_DC_TOP
+            } else {
+                IPRED8_DC_LEFT
+            };
+        for chroma in 1..3 {
+            let off = frm.offset[chroma] + sstate.mb_x * 8 + sstate.mb_y * 8 * frm.stride[chroma];
+            IPRED_FUNCS8X8_CHROMA[id](frm.data, off, frm.stride[chroma]);
+        }
+    }
+    fn add_luma(frm: &mut NASimpleVideoFrame<u8>, sstate: &SliceState, mb_info: &CurrentMBInfo) {
+        let mut yoff = frm.offset[0] + sstate.mb_x * 16 + sstate.mb_y * 16 * frm.stride[0];
+        if !mb_info.transform_size_8x8 {
+            for y in 0..4 {
+                for x in 0..4 {
+                    if mb_info.coded[x + y * 4] {
+                        add_coeffs(frm.data, yoff + x * 4, frm.stride[0], &mb_info.coeffs[x + y * 4]);
+                    }
+                }
+                yoff += frm.stride[0] * 4;
+            }
+        } else {
+            for y in 0..2 {
+                for x in 0..2 {
+                    if mb_info.coded[x * 2 + y * 2 * 4] {
+                        add_coeffs8(frm.data, yoff + x * 8, frm.stride[0], &mb_info.coeffs8x8[x + y * 2].coeffs);
+                    }
+                }
+                yoff += frm.stride[0] * 8;
+            }
+        }
+    }
+    fn add_chroma(frm: &mut NASimpleVideoFrame<u8>, sstate: &SliceState, mb_info: &CurrentMBInfo) {
+        for chroma in 1..3 {
+            let mut off = frm.offset[chroma] + sstate.mb_x * 8 + sstate.mb_y * 8 * frm.stride[chroma];
+            for y in 0..2 {
+                for x in 0..2 {
+                    let blk_no = 16 + (chroma - 1) * 4 + x + y * 2;
+                    if mb_info.coded[blk_no] || mb_info.coeffs[blk_no][0] != 0 {
+                        add_coeffs(frm.data, off + x * 4, frm.stride[chroma], &mb_info.coeffs[blk_no]);
+                    }
+                }
+                off += frm.stride[chroma] * 4;
+            }
+        }
+    }
+    fn pred_mv(sstate: &mut SliceState, frame_refs: &FrameRefs, mb_info: &mut CurrentMBInfo, cur_id: u16, temporal_mv: bool) {
+        let mb_type = mb_info.mb_type;
+        if !mb_type.is_4x4() {
+            let (pw, ph) = mb_type.size();
+            let mut xoff = 0;
+            let mut yoff = 0;
+            if mb_type == MBType::Direct || mb_type == MBType::BSkip {
+                sstate.predict_direct_mb(frame_refs, temporal_mv, cur_id);
+            }
+            for part in 0..mb_type.num_parts() {
+                if !mb_type.is_l1(part) {
+                    match mb_type {
+                        MBType::PSkip => sstate.predict_pskip(),
+                        MBType::BSkip | MBType::Direct => {
+                        },
+                        _ => {
+                            sstate.predict(xoff, yoff, pw, ph, 0,
+ mb_info.mv_l0[part], mb_info.ref_l0[part]);
+                        },
+                    };
+                }
+                if !mb_type.is_l0(part) && mb_type != MBType::BSkip && mb_type != MBType::Direct {
+                    sstate.predict(xoff, yoff, pw, ph, 1, mb_info.mv_l1[part], mb_info.ref_l1[part]);
+                }
+                if pw != 16 {
+                    xoff += pw;
+                } else {
+                    yoff += ph;
+                }
+            }
+        } else {
+            for part in 0..4 {
+                let sub_type = mb_info.sub_mb_type[part];
+                let mut xoff = (part & 1) * 8;
+                let mut yoff = (part & 2) * 4;
+                let orig_x = xoff;
+                let (pw, ph) = sub_type.size();
+                for subpart in 0..sub_type.num_parts() {
+                    if sub_type != SubMBType::Direct8x8 {
+                        if !sub_type.is_l1() {
+                            sstate.predict(xoff, yoff, pw, ph, 0, mb_info.mv_l0[part * 4 + subpart], mb_info.ref_l0[part]);
+                        }
+                        if !sub_type.is_l0() {
+                            sstate.predict(xoff, yoff, pw, ph, 1, mb_info.mv_l1[part * 4 + subpart], mb_info.ref_l1[part]);
+                        }
+                    } else {
+                        for sblk in 0..4 {
+                            sstate.predict_direct_sub(frame_refs, temporal_mv, cur_id, (xoff / 4) + (sblk & 1) + (yoff / 4) * 4 + (sblk & 2) * 2);
+                        }
+                    }
+                    xoff += pw;
+                    if xoff == orig_x + 8 {
+                        xoff -= 8;
+                        yoff += ph;
+                    }
+                }
+            }
+        }
+    }
+    fn handle_macroblock(&mut self, mb_info: &mut CurrentMBInfo) {
+        let pps = &self.pps[self.cur_pps];
+
+        let qp_y = mb_info.qp_y;
+        let qpr = ((qp_y as i8) + pps.chroma_qp_index_offset).max(0).min(51) as usize;
+        let qp_u = CHROMA_QUANTS[qpr];
+        let qpb = ((qp_y as i8) + pps.second_chroma_qp_index_offset).max(0).min(51) as usize;
+        let qp_v = CHROMA_QUANTS[qpb];
+
+        let tx_bypass = qp_y == 0 && self.sps[self.cur_sps].qpprime_y_zero_transform_bypass;
+
+        self.sstate.get_cur_mb().mb_type = mb_info.mb_type.into();
+        if mb_info.mb_type != MBType::PCM {
+            self.sstate.get_cur_mb().qp_y = qp_y;
+            self.sstate.get_cur_mb().qp_u = qp_u;
+            self.sstate.get_cur_mb().qp_v = qp_v;
+            self.sstate.get_cur_mb().transform_8x8 = mb_info.transform_size_8x8;
+        }
+        let has_dc = mb_info.mb_type.is_intra16x16() && mb_info.coded[24];
+        if has_dc {
+            idct_luma_dc(&mut mb_info.coeffs[24], qp_y);
+            for i in 0..16 {
+                mb_info.coeffs[i][0] = mb_info.coeffs[24][i];
+            }
+        }
+        if !mb_info.transform_size_8x8 {
+            let quant_dc = !mb_info.mb_type.is_intra16x16();
+            for i in 0..16 {
+                if mb_info.coded[i] {
+                    if !tx_bypass {
+                        idct(&mut mb_info.coeffs[i], qp_y, quant_dc);
+                    }
+                } else if has_dc {
+                    if !tx_bypass {
+                        idct_dc(&mut mb_info.coeffs[i], qp_y, quant_dc);
+                    }
+                    mb_info.coded[i] = true;
+                }
+            }
+        } else {
+            for i in 0..4 {
+                if mb_info.coded[(i & 1) * 2 + (i & 2) * 4] && !tx_bypass {
+                    dequant8x8(&mut mb_info.coeffs8x8[i].coeffs, &pps.scaling_list_8x8[!mb_info.mb_type.is_intra() as usize]);
+                    idct8x8(&mut mb_info.coeffs8x8[i].coeffs, qp_y);
+                }
+            }
+        }
+        for chroma in 0..2 {
+            let qp_c = if chroma == 0 { qp_u } else { qp_v };
+            if mb_info.cbpc != 0 {
+                chroma_dc_transform(&mut mb_info.chroma_dc[chroma], qp_c);
+            }
+            for i in 0..4 {
+                let blk_no = 16 + chroma * 4 + i;
+                mb_info.coeffs[blk_no][0] = mb_info.chroma_dc[chroma][i];
+                if mb_info.coded[blk_no] {
+                    idct(&mut mb_info.coeffs[blk_no], qp_c, false);
+                } else if mb_info.coeffs[blk_no][0] != 0 {
+                    idct_dc(&mut mb_info.coeffs[blk_no], qp_c, false);
+                    mb_info.coded[blk_no] = true;
+                }
+            }
+        }
+        if !pps.entropy_coding_mode || mb_info.mb_type.is_skip() || mb_info.mb_type.is_intra() {
+            self.sstate.reset_mb_mv();
+        }
+        if !mb_info.mb_type.is_intra() {
+            Self::pred_mv(&mut self.sstate, &self.frame_refs, mb_info, self.cur_id, self.temporal_mv);
+        }
+        if !pps.constrained_intra_pred && mb_info.mb_type != MBType::Intra4x4 && mb_info.mb_type != MBType::Intra8x8 {
+            self.sstate.fill_ipred(IntraPredMode::DC);
+        }
+
+        let xpos = self.sstate.mb_x * 16;
+        let ypos = self.sstate.mb_y * 16;
+        if let Some(ref mut pic) = self.cur_pic {
+            let mut frm = NASimpleVideoFrame::from_video_buf(&mut pic.buf).unwrap();
+            match mb_info.mb_type {
+                MBType::Intra16x16(_, _, _) => {
+                    Self::pred_intra(&mut frm, &self.sstate, &mb_info);
+                },
+                MBType::Intra4x4 | MBType::Intra8x8 => {
+                    Self::pred_intra(&mut frm, &self.sstate, &mb_info);
+                },
+                MBType::PCM => {},
+                MBType::PSkip => {
+                    let mv = self.sstate.get_cur_blk4(0).mv[0];
+                    let rpic = self.frame_refs.select_ref_pic(0, 0);
+                    Self::do_p_mc(&mut frm, xpos, ypos, 16, 16, mv, rpic);
+                },
+                MBType::P16x16 => {
+                    let mv = self.sstate.get_cur_blk4(0).mv[0];
+                    let rpic = self.frame_refs.select_ref_pic(0, mb_info.ref_l0[0].index());
+                    Self::do_p_mc(&mut frm, xpos, ypos, 16, 16, mv, rpic);
+                },
+                MBType::P16x8 | MBType::P8x16 => {
+                    let (bw, bh, bx, by) = if mb_info.mb_type == MBType::P16x8 {
+                            (16, 8, 0, 8)
+                        } else {
+                            (8, 16, 8, 0)
+                        };
+                    let mv = self.sstate.get_cur_blk4(0).mv[0];
+                    let rpic = self.frame_refs.select_ref_pic(0, mb_info.ref_l0[0].index());
+                    Self::do_p_mc(&mut frm, xpos, ypos, bw, bh, mv, rpic);
+                    let mv = self.sstate.get_cur_blk4(bx / 4 + by).mv[0];
+                    let rpic = self.frame_refs.select_ref_pic(0, mb_info.ref_l0[1].index());
+                    Self::do_p_mc(&mut frm, xpos + bx, ypos + by, bw, bh, mv, rpic);
+                },
+                MBType::P8x8 | MBType::P8x8Ref0 => {
+                    for part in 0..4 {
+                        let bx = (part & 1) * 8;
+                        let by = (part & 2) * 4;
+                        if let Some(buf) = self.frame_refs.select_ref_pic(0, mb_info.ref_l0[part].index()) {
+                            let mv = self.sstate.get_cur_blk4(bx / 4 + by).mv[0];
+
+                            match mb_info.sub_mb_type[part] {
+                                SubMBType::P8x8 => {
+                                    do_mc(&mut frm, buf, xpos + bx, ypos + by, 8, 8, mv);
+                                },
+                                SubMBType::P8x4 => {
+                                    do_mc(&mut frm, buf.clone(), xpos + bx, ypos + by, 8, 4, mv);
+                                    let mv = self.sstate.get_cur_blk4(bx / 4 + by + 4).mv[0];
+                                    do_mc(&mut frm, buf, xpos + bx, ypos + by + 4, 8, 4, mv);
+                                },
+                                SubMBType::P4x8 => {
+                                    do_mc(&mut frm, buf.clone(), xpos + bx, ypos + by, 4, 8, mv);
+                                    let mv = self.sstate.get_cur_blk4(bx / 4 + by + 1).mv[0];
+                                    do_mc(&mut frm, buf, xpos + bx + 4, ypos + by, 4, 8, mv);
+                                },
+                                SubMBType::P4x4 => {
+                                    for sb_no in 0..4 {
+                                        let sxpos = xpos + bx + (sb_no & 1) * 4;
+                                        let sypos = ypos + by + (sb_no & 2) * 2;
+                                        let sblk_no = (bx / 4 + (sb_no & 1)) + ((by / 4) + (sb_no >> 1)) * 4;
+                                        let mv = self.sstate.get_cur_blk4(sblk_no).mv[0];
+                                        do_mc(&mut frm, buf.clone(), sxpos, sypos, 4, 4, mv);
+                                    }
+                                },
+                                _ => unreachable!(),
+                            };
+                        } else {
+                            gray_block(&mut frm, xpos + bx, ypos + by, 8, 8);
+                        }
+                    }
+                },
+                MBType::B16x16(mode) => {
+                    let mv0 = self.sstate.get_cur_blk4(0).mv[0];
+                    let rpic0 = self.frame_refs.select_ref_pic(0, mb_info.ref_l0[0].index());
+                    let mv1 = self.sstate.get_cur_blk4(0).mv[1];
+                    let rpic1 = self.frame_refs.select_ref_pic(1, mb_info.ref_l1[0].index());
+                    Self::do_b_mc(&mut frm, mode, xpos, ypos, 16, 16, mv0, rpic0, mv1, rpic1, &mut self.avg_buf);
+                },
+                MBType::B16x8(mode0, mode1) | MBType::B8x16(mode0, mode1) => {
+                    let (pw, ph) = mb_info.mb_type.size();
+                    let (px, py) = (pw & 8, ph & 8);
+                    let modes = [mode0, mode1];
+                    let (mut bx, mut by) = (0, 0);
+                    for part in 0..2 {
+                        let blk = if part == 0 { 0 } else { (px / 4) + py };
+                        let mv0 = self.sstate.get_cur_blk4(blk).mv[0];
+                        let rpic0 = self.frame_refs.select_ref_pic(0, mb_info.ref_l0[part].index());
+                        let mv1 = self.sstate.get_cur_blk4(blk).mv[1];
+                        let rpic1 = self.frame_refs.select_ref_pic(1, mb_info.ref_l1[part].index());
+                        Self::do_b_mc(&mut frm, modes[part], xpos + bx, ypos + by, pw, ph, mv0, rpic0, mv1, rpic1, &mut self.avg_buf);
+                        bx += px;
+                        by += py;
+                    }
+                },
+                MBType::Direct | MBType::BSkip => {
+                    let is_16x16 = self.frame_refs.get_colocated_info(self.sstate.mb_x, self.sstate.mb_y).0.mb_type.is_16x16();
+                    if is_16x16 || !self.temporal_mv {
+                        let mv = self.sstate.get_cur_blk4(0).mv;
+                        let ref_idx = self.sstate.get_cur_blk8(0).ref_idx;
+                        let rpic0 = self.frame_refs.select_ref_pic(0, ref_idx[0].index());
+                        let rpic1 = self.frame_refs.select_ref_pic(1, ref_idx[1].index());
+                        Self::do_b_mc(&mut frm, BMode::Bi, xpos, ypos, 16, 16, mv[0], rpic0, mv[1], rpic1, &mut self.avg_buf);
+                    } else {
+                        for blk4 in 0..16 {
+                            let mv = self.sstate.get_cur_blk4(blk4).mv;
+                            let ref_idx = self.sstate.get_cur_blk8(blk4_to_blk8(blk4)).ref_idx;
+                            let rpic0 = self.frame_refs.select_ref_pic(0, ref_idx[0].index());
+                            let rpic1 = self.frame_refs.select_ref_pic(1, ref_idx[1].index());
+                            Self::do_b_mc(&mut frm, BMode::Bi, xpos + (blk4 & 3) * 4, ypos + (blk4 >> 2) * 4, 4, 4, mv[0], rpic0, mv[1], rpic1, &mut self.avg_buf);
+                        }
+                    }
+                    self.sstate.apply_to_blk8(|blk8| { blk8.ref_idx[0].set_direct(); blk8.ref_idx[1].set_direct(); });
+                },
+                MBType::B8x8 => {
+                    for part in 0..4 {
+                        let ridx = self.sstate.get_cur_blk8(part).ref_idx;
+                        let rpic0 = self.frame_refs.select_ref_pic(0, ridx[0].index());
+                        let rpic1 = self.frame_refs.select_ref_pic(1, ridx[1].index());
+                        let subtype = mb_info.sub_mb_type[part];
+                        let blk8 = (part & 1) * 2 + (part & 2) * 4;
+                        let mut bx = (part & 1) * 8;
+                        let mut by = (part & 2) * 4;
+                        match subtype {
+                            SubMBType::Direct8x8 => {
+                                for blk in 0..4 {
+                                    let mv = self.sstate.get_cur_blk4(bx / 4 + (by / 4) * 4).mv;
+                                    let ref_idx = self.sstate.get_cur_blk8(bx / 8 + (by / 8) * 2).ref_idx;
+                                    let rpic0 = self.frame_refs.select_ref_pic(0, ref_idx[0].index());
+                                    let rpic1 = self.frame_refs.select_ref_pic(1, ref_idx[1].index());
+                                    Self::do_b_mc(&mut frm, BMode::Bi, xpos + bx, ypos + by, 4, 4, mv[0], rpic0, mv[1], rpic1, &mut self.avg_buf);
+                                    bx += 4;
+                                    if blk == 1 {
+                                        bx -= 8;
+                                        by += 4;
+                                    }
+                                }
+                                self.sstate.get_cur_blk8(part).ref_idx[0].set_direct();
+                                self.sstate.get_cur_blk8(part).ref_idx[1].set_direct();
+                            },
+                            SubMBType::B8x8(mode) => {
+                                let mv = self.sstate.get_cur_blk4(blk8).mv;
+                                Self::do_b_mc(&mut frm, mode, xpos + bx, ypos + by, 8, 8, mv[0], rpic0, mv[1], rpic1, &mut self.avg_buf);
+                            },
+                            SubMBType::B8x4(mode) | SubMBType::B4x8(mode) => {
+                                let (pw, ph) = subtype.size();
+                                let mv = self.sstate.get_cur_blk4(blk8).mv;
+                                Self::do_b_mc(&mut frm, mode, xpos + bx, ypos + by, pw, ph, mv[0], rpic0.clone(), mv[1], rpic1.clone(), &mut self.avg_buf);
+                                let addr2 = blk8 + (pw & 4) / 4 + (ph & 4);
+                                let mv = self.sstate.get_cur_blk4(addr2).mv;
+                                Self::do_b_mc(&mut frm, mode, xpos + bx + (pw & 4), ypos + by + (ph & 4), pw, ph, mv[0], rpic0, mv[1], rpic1, &mut self.avg_buf);
+                            },
+                            SubMBType::B4x4(mode) => {
+                                for i in 0..4 {
+                                    let addr2 = blk8 + (i & 1) + (i & 2) * 2;
+                                    let mv = self.sstate.get_cur_blk4(addr2).mv;
+                                    Self::do_b_mc(&mut frm, mode, xpos + bx, ypos + by, 4, 4, mv[0], rpic0.clone(), mv[1], rpic1.clone(), &mut self.avg_buf);
+                                    bx += 4;
+                                    if i == 1 {
+                                        bx -= 8;
+                                        by += 4;
+                                    }
+                                }
+                            },
+                            _ => unreachable!(),
+                        };
+                    }
+                },
+            };
+            if mb_info.mb_type == MBType::PCM {
+                for (dline, src) in frm.data[frm.offset[0] + xpos + ypos * frm.stride[0]..].chunks_mut(frm.stride[0]).take(16).zip(self.ipcm_buf.chunks(16)) {
+                    dline[..16].copy_from_slice(src);
+                }
+                for (dline, src) in frm.data[frm.offset[1] + xpos/2 + ypos/2 * frm.stride[1]..].chunks_mut(frm.stride[1]).take(8).zip(self.ipcm_buf[256..].chunks(8)) {
+                    dline[..8].copy_from_slice(src);
+                }
+                for (dline, src) in frm.data[frm.offset[2] + xpos/2 + ypos/2 * frm.stride[2]..].chunks_mut(frm.stride[2]).take(8).zip(self.ipcm_buf[256 + 64..].chunks(8)) {
+                    dline[..8].copy_from_slice(src);
+                }
+            } else if !mb_info.mb_type.is_skip() {
+                if mb_info.mb_type != MBType::Intra4x4 && mb_info.mb_type != MBType::Intra8x8 {
+                    Self::add_luma(&mut frm, &self.sstate, &mb_info);
+                }
+                Self::add_chroma(&mut frm, &self.sstate, &mb_info);
+            }
+/*match mb_info.mb_type {
+MBType::BSkip | MBType::Direct | MBType::B16x16(_) | MBType::B16x8(_, _) | MBType::B8x16(_, _) | MBType::B8x8 => {
+ let dstride = frm.stride[0];
+ let dst = &mut frm.data[frm.offset[0] + self.sstate.mb_x * 16 + self.sstate.mb_y * 16 * dstride..];
+ for el in dst[..16].iter_mut() { *el = 255; }
+ for row in dst.chunks_mut(dstride).skip(1).take(15) {
+  row[0] = 255;
+ }
+},
+_ => {},
+};*/
+        }
+        if let Some(ref mut pic) = self.cur_pic {
+            let mv_info = &mut pic.mv_info;
+            let mb_pos = self.sstate.mb_x + self.sstate.mb_y * mv_info.mb_stride;
+            let mut mb = FrameMBInfo::new();
+            mb.mb_type = mb_info.mb_type.into();
+            for blk4 in 0..16 {
+                mb.mv[blk4] = self.sstate.get_cur_blk4(blk4).mv;
+            }
+            for blk8 in 0..4 {
+                mb.ref_poc[blk8] = self.frame_refs.map_refs(self.sstate.get_cur_blk8(blk8).ref_idx);
+                mb.ref_idx[blk8] = self.sstate.get_cur_blk8(blk8).ref_idx;
+            }
+            mv_info.mbs[mb_pos] = mb;
+        }
+        self.sstate.fill_deblock(self.deblock_mode, self.is_s);
+        if !self.deblock_skip && self.sstate.mb_x + 1 == self.sstate.mb_w && self.deblock_mode != 1 {
+            if let Some(ref mut pic) = self.cur_pic {
+                let mut frm = NASimpleVideoFrame::from_video_buf(&mut pic.buf).unwrap();
+                loop_filter_row(&mut frm, &self.sstate, self.lf_alpha, self.lf_beta);
+            }
+        }
+        self.sstate.next_mb();
+    }
+    fn do_p_mc(frm: &mut NASimpleVideoFrame<u8>, xpos: usize, ypos: usize, w: usize, h: usize, mv: MV, ref_pic: Option<NAVideoBufferRef<u8>>) {
+        if let Some(buf) = ref_pic {
+            do_mc(frm, buf, xpos, ypos, w, h, mv);
+        } else {
+            gray_block(frm, xpos, ypos, w, h);
+        }
+    }
+    fn do_b_mc(frm: &mut NASimpleVideoFrame<u8>, mode: BMode, xpos: usize, ypos: usize, w: usize, h: usize, mv0: MV, ref_pic0: Option<NAVideoBufferRef<u8>>, mv1: MV, ref_pic1: Option<NAVideoBufferRef<u8>>, avg_buf: &mut NAVideoBufferRef<u8>) {
+        match mode {
+            BMode::L0 => {
+                if let Some(buf) = ref_pic0 {
+                    do_mc(frm, buf, xpos, ypos, w, h, mv0);
+                } else {
+                    gray_block(frm, xpos, ypos, w, h);
+                }
+            },
+            BMode::L1 => {
+                if let Some(buf) = ref_pic1 {
+                    do_mc(frm, buf, xpos, ypos, w, h, mv1);
+                } else {
+                    gray_block(frm, xpos, ypos, w, h);
+                }
+            },
+            BMode::Bi => {
+                match (ref_pic0, ref_pic1) {
+                    (Some(buf0), Some(buf1)) => {
+                        do_mc(frm, buf0, xpos, ypos, w, h, mv0);
+                        do_mc_avg(frm, buf1, xpos, ypos, w, h, mv1, avg_buf);
+                    },
+                    (Some(buf0), None) => {
+                        do_mc(frm, buf0, xpos, ypos, w, h, mv0);
+                    },
+                    (None, Some(buf1)) => {
+                        do_mc(frm, buf1, xpos, ypos, w, h, mv1);
+                    },
+                    (None, None) => {
+                        gray_block(frm, xpos, ypos, w, h);
+                    },
+                };
+            },
+        };
+    }
+    fn decode_slice_cavlc(&mut self, br: &mut BitReader, slice_hdr: &SliceHeader, full_size: usize) -> DecoderResult<bool> {
+        const INTRA_CBP: [u8; 48] = [
+            47, 31, 15,  0, 23, 27, 29, 30,  7, 11, 13, 14, 39, 43, 45, 46,
+            16,  3,  5, 10, 12, 19, 21, 26, 28, 35, 37, 42, 44,  1,  2,  4,
+             8, 17, 18, 20, 24,  6,  9, 22, 25, 32, 33, 34, 36, 40, 38, 41
+        ];
+        const INTER_CBP: [u8; 48] = [
+             0, 16,  1,  2,  4,  8, 32,  3,  5, 10, 12, 15, 47,  7, 11, 13,
+            14,  6,  9, 31, 35, 37, 42, 44, 33, 34, 36, 40, 39, 43, 45, 46,
+            17, 18, 20, 24, 19, 21, 26, 28, 23, 27, 29, 30, 22, 25, 38, 41
+        ];
+
+        let mut mb_idx = slice_hdr.first_mb_in_slice as usize;
+        let mut mb_info = CurrentMBInfo::default();
+        mb_info.qp_y = slice_hdr.slice_qp;
+        let skip_type = if slice_hdr.slice_type.is_p() { MBType::PSkip } else { MBType::BSkip };
+        while br.tell() < full_size && mb_idx < self.num_mbs {
+            mb_info.coded = [false; 25];
+            mb_info.ref_l0 = [ZERO_REF; 4];
+            mb_info.ref_l1 = [ZERO_REF; 4];
+            mb_info.mv_l0 = [ZERO_MV; 16];
+            mb_info.mv_l1 = [ZERO_MV; 16];
+            mb_info.chroma_dc = [[0; 4]; 2];
+            mb_info.cbpy = 0;
+            mb_info.cbpc = 0;
+
+            if !slice_hdr.slice_type.is_intra() {
+                let mb_skip_run                     = br.read_ue()? as usize;
+                validate!(mb_idx + mb_skip_run <= self.num_mbs);
+                mb_info.mb_type = skip_type;
+                for _ in 0..mb_skip_run {
+                    self.handle_macroblock(&mut mb_info);
+                    mb_idx += 1;
+                }
+                if mb_idx == self.num_mbs || br.tell() >= full_size {
+                    break;
+                }
+            }
+            if br.tell() < full_size {
+                if self.is_mbaff && ((mb_idx & 1) == 0) {
+                    let _mb_field_decoding          = br.read_bool()?;
+                }
+                let mut mb_type = decode_mb_type_cavlc(br, slice_hdr)?;
+                mb_info.mb_type = mb_type;
+                mb_info.transform_size_8x8 = false;
+                if mb_type == MBType::PCM {
+                                                      br.align();
+                    for pix in self.ipcm_buf[..256 + 64 + 64].iter_mut() {
+                        *pix                        = br.read(8)? as u8;
+                    }
+                    self.sstate.fill_ncoded(16);
+                } else {
+                    if self.transform_8x8_mode && mb_type == MBType::Intra4x4 {
+                        mb_info.transform_size_8x8  = br.read_bool()?;
+                        if mb_info.transform_size_8x8 {
+                            mb_type = MBType::Intra8x8;
+                            mb_info.mb_type = MBType::Intra8x8;
+                        }
+                    }
+                    decode_mb_pred_cavlc(br, slice_hdr, mb_type, &mut self.sstate, &mut mb_info)?;
+                    let (cbpy, cbpc) = if let MBType::Intra16x16(_, cbpy, cbpc) = mb_type {
+                            (cbpy, cbpc)
+                        } else {
+                            let cbp_id              = br.read_ue()? as usize;
+                            validate!(cbp_id < INTRA_CBP.len());
+                            let cbp = if mb_type == MBType::Intra4x4 || mb_type == MBType::Intra8x8 {
+                                    INTRA_CBP[cbp_id]
+                                } else {
+                                    INTER_CBP[cbp_id]
+                                };
+                            if self.transform_8x8_mode && (cbp & 0xF) != 0 && mb_info.can_have_8x8_tx(self.sps[self.cur_sps].direct_8x8_inference) {
+                                mb_info.transform_size_8x8 = br.read_bool()?;
+                            }
+                            ((cbp & 0xF), (cbp >> 4))
+                        };
+                    mb_info.cbpy = cbpy;
+                    mb_info.cbpc = cbpc;
+                    self.sstate.get_cur_mb().cbp = (cbpc << 4) | cbpy;
+                    if cbpy != 0 || cbpc != 0 || mb_type.is_intra16x16() {
+                        let mb_qp_delta             = br.read_se()?;
+                        validate!(mb_qp_delta >= -26 && mb_qp_delta <= 25);
+                        let new_qp = mb_qp_delta + i32::from(mb_info.qp_y);
+                        mb_info.qp_y = if new_qp < 0 {
+                                (new_qp + 52) as u8
+                            } else if new_qp >= 52 {
+                                (new_qp - 52) as u8
+                            } else {
+                                new_qp as u8
+                            };
+                        mb_info.coeffs = [[0; 16]; 25];
+                        if self.transform_8x8_mode {
+                            mb_info.clear_coeffs8x8();
+                        }
+                        mb_info.chroma_dc = [[0; 4]; 2];
+                        decode_residual_cavlc(br, &mut self.sstate, &mut mb_info, &self.cavlc_cb)?;
+                    }
+                }
+                self.handle_macroblock(&mut mb_info);
+            }
+            mb_idx += 1;
+        }
+        if let Some(ref mut pic) = self.cur_pic {
+            pic.cur_mb = mb_idx;
+        }
+        Ok(mb_idx == self.num_mbs)
+    }
+    fn decode_slice_cabac(&mut self, cabac: &mut CABAC, slice_hdr: &SliceHeader) -> DecoderResult<bool> {
+        let mut mb_idx = slice_hdr.first_mb_in_slice as usize;
+        let mut prev_mb_skipped = false;
+        let skip_type = if slice_hdr.slice_type.is_p() { MBType::PSkip } else { MBType::BSkip };
+        let mut last_qp_diff = false;
+
+        let mut mb_info = CurrentMBInfo::default();
+        mb_info.qp_y = slice_hdr.slice_qp;
+
+        while mb_idx < self.num_mbs {
+            mb_info.coded = [false; 25];
+            mb_info.ref_l0 = [ZERO_REF; 4];
+            mb_info.ref_l1 = [ZERO_REF; 4];
+            mb_info.mv_l0 = [ZERO_MV; 16];
+            mb_info.mv_l1 = [ZERO_MV; 16];
+            mb_info.chroma_dc = [[0; 4]; 2];
+            mb_info.cbpy = 0;
+            mb_info.cbpc = 0;
+            let mb_skip = cabac_decode_mbskip(cabac, &self.sstate, slice_hdr);
+            if !mb_skip {
+                if self.is_mbaff && (((mb_idx & 1) == 0) || (prev_mb_skipped && ((mb_idx & 1) == 1))) {
+                    let _mb_field_decoding          = cabac.decode_bit(70);
+                }
+                let mut mb_type                     = cabac_decode_mb_type(cabac, &slice_hdr, &self.sstate);
+                mb_info.mb_type = mb_type;
+                mb_info.transform_size_8x8 = false;
+                if mb_type == MBType::PCM {
+                    let ipcm_size = 256 + 64 + 64;
+                    validate!(cabac.pos + ipcm_size <= cabac.src.len());
+                    self.ipcm_buf[..ipcm_size].copy_from_slice(&cabac.src[cabac.pos..][..ipcm_size]);
+                    cabac.pos += ipcm_size;
+                    cabac.reinit()?;
+                    last_qp_diff = false;
+                } else {
+                    if self.transform_8x8_mode && mb_type == MBType::Intra4x4 {
+                        let mut ctx = 0;
+                        if self.sstate.get_top_mb().transform_8x8 {
+                            ctx += 1;
+                        }
+                        if self.sstate.get_left_mb().transform_8x8 {
+                            ctx += 1;
+                        }
+                        mb_info.transform_size_8x8  = cabac.decode_bit(399 + ctx);
+                        if mb_info.transform_size_8x8 {
+                            mb_type = MBType::Intra8x8;
+                            mb_info.mb_type = MBType::Intra8x8;
+                        }
+                    }
+                    decode_mb_pred_cabac(cabac, slice_hdr, mb_type, &mut self.sstate, &mut mb_info);
+                    let (cbpy, cbpc) = if let MBType::Intra16x16(_, cbpy, cbpc) = mb_type {
+                            (cbpy, cbpc)
+                        } else {
+                            decode_cbp_cabac(cabac, &self.sstate)
+                        };
+                    if self.transform_8x8_mode && cbpy != 0 && mb_info.can_have_8x8_tx(self.sps[self.cur_sps].direct_8x8_inference) {
+                        let mut ctx = 0;
+                        if self.sstate.get_top_mb().transform_8x8 {
+                            ctx += 1;
+                        }
+                        if self.sstate.get_left_mb().transform_8x8 {
+                            ctx += 1;
+                        }
+                        mb_info.transform_size_8x8  = cabac.decode_bit(399 + ctx);
+                    }
+                    if mb_type.is_intra() {
+                        self.sstate.get_cur_mb().cmode = mb_info.chroma_ipred;
+                    }
+                    mb_info.cbpy = cbpy;
+                    mb_info.cbpc = cbpc;
+                    self.sstate.get_cur_mb().cbp = (cbpc << 4) | cbpy;
+                    if cbpy != 0 || cbpc != 0 || mb_type.is_intra16x16() {
+                        let mb_qp_delta = decode_mb_qp_delta_cabac(cabac, last_qp_diff as usize);
+                        validate!(mb_qp_delta >= -26 && mb_qp_delta <= 25);
+                        last_qp_diff = mb_qp_delta != 0;
+                        let new_qp = mb_qp_delta + i32::from(mb_info.qp_y);
+                        mb_info.qp_y = if new_qp < 0 {
+                                (new_qp + 52) as u8
+                            } else if new_qp >= 52 {
+                                (new_qp - 52) as u8
+                            } else {
+                                new_qp as u8
+                            };
+                        mb_info.coeffs = [[0; 16]; 25];
+                        if self.transform_8x8_mode {
+                            mb_info.clear_coeffs8x8();
+                        }
+                        mb_info.chroma_dc = [[0; 4]; 2];
+                        decode_residual_cabac(cabac, &mut self.sstate, &mut mb_info);
+                    } else {
+                        last_qp_diff = false;
+                    }
+                }
+            } else {
+                mb_info.mb_type = skip_type;
+                mb_info.transform_size_8x8 = false;
+                last_qp_diff = false;
+            }
+            self.handle_macroblock(&mut mb_info);
+            prev_mb_skipped = mb_skip;
+            if !(self.is_mbaff && ((mb_idx & 1) == 0)) && cabac.decode_terminate() {
+                if let Some(ref mut pic) = self.cur_pic {
+                    pic.cur_mb = mb_idx + 1;
+                }
+                return Ok(mb_idx + 1 == self.num_mbs);
+            }
+            mb_idx += 1;
+        }
+        Err(DecoderError::InvalidData)
+    }
+}
+
+impl NADecoder for H264Decoder {
+    fn init(&mut self, supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
+        if let NACodecTypeInfo::Video(vinfo) = info.get_properties() {
+            let fmt = YUV420_FORMAT;
+            let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, false, fmt));
+            self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref();
+
+            let edata = info.get_extradata().unwrap();
+//print!("edata:"); for &el in edata.iter() { print!(" {:02X}", el); } println!();
+            if edata.len() > 11 && &edata[0..4] == b"avcC" {
+                let mut mr = MemoryReader::new_read(edata.as_slice());
+                let mut br = ByteReader::new(&mut mr);
+                let mut nal_buf = Vec::new();
+
+                                          br.read_skip(4)?;
+                let version             = br.read_byte()?;
+                validate!(version == 1);
+                let profile             = br.read_byte()?;
+                let _compatibility      = br.read_byte()?;
+                let _level              = br.read_byte()?;
+                let b                   = br.read_byte()?;
+                validate!((b & 0xFC) == 0xFC);
+                self.nal_len            = (b & 3) + 1;
+                let b                   = br.read_byte()?;
+                validate!((b & 0xE0) == 0xE0);
+                let num_sps = (b & 0x1F) as usize;
+                for _ in 0..num_sps {
+                    let len             = br.read_u16be()? as usize;
+                    let offset = br.tell() as usize;
+                    validate!((br.peek_byte()? & 0x1F) == 7);
+                    let _size = unescape_nal(&edata[offset..][..len], &mut nal_buf);
+                    self.handle_nal(&nal_buf, supp, true)?;
+                                          br.read_skip(len)?;
+                }
+                let num_pps             = br.read_byte()? as usize;
+                for _ in 0..num_pps {
+                    let len             = br.read_u16be()? as usize;
+                    let offset = br.tell() as usize;
+                    validate!((br.peek_byte()? & 0x1F) == 8);
+                    let _size = unescape_nal(&edata[offset..][..len], &mut nal_buf);
+                    self.handle_nal(&nal_buf, supp, true)?;
+                                          br.read_skip(len)?;
+                }
+                if br.left() > 0 {
+                    match profile {
+                        100 | 110 | 122 | 144 => {
+                            let b       = br.read_byte()?;
+                            validate!((b & 0xFC) == 0xFC);
+                            // b & 3 -> chroma format
+                            let b       = br.read_byte()?;
+                            validate!((b & 0xF8) == 0xF8);
+                            // b & 7 -> luma depth minus 8
+                            let b       = br.read_byte()?;
+                            validate!((b & 0xF8) == 0xF8);
+                            // b & 7 -> chroma depth minus 8
+                            let num_spsext  = br.read_byte()? as usize;
+                            for _ in 0..num_spsext {
+                                let len = br.read_u16be()? as usize;
+                                // parse spsext
+                                          br.read_skip(len)?;
+                            }
+                        },
+                        _ => {},
+                    };
+                }
+            } else {
+                return Err(DecoderError::NotImplemented);
+            }
+
+            self.width  = vinfo.get_width();
+            self.height = vinfo.get_height();
+
+            if (self.width == 0 || self.height == 0) && !self.sps.is_empty() {
+                self.width  = self.sps[0].pic_width_in_mbs  * 16;
+                self.height = self.sps[0].pic_height_in_mbs * 16;
+            }
+
+            let num_bufs = if !self.sps.is_empty() {
+                    self.sps[0].num_ref_frames as usize + 1
+                } else {
+                    3
+                }.max(16 + 1);
+            supp.pool_u8.set_dec_bufs(num_bufs);
+            supp.pool_u8.prealloc_video(NAVideoInfo::new(self.width, self.height, false, fmt), 4)?;
+
+            Ok(())
+        } else {
+println!("???");
+            Err(DecoderError::InvalidData)
+        }
+    }
+    fn decode(&mut self, supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
+        let src = pkt.get_buffer();
+
+        let mut mr = MemoryReader::new_read(&src);
+        let mut br = ByteReader::new(&mut mr);
+        let mut nal_buf = Vec::with_capacity(src.len());
+        if self.nal_len > 0 {
+            let mut skip_decoding = false;
+            if self.skip_mode != FrameSkipMode::None {
+                let mut pic_type = FrameType::I;
+                let mut is_ref = false;
+                while br.left() > 0 {
+                    let size = match self.nal_len {
+                            1 => br.read_byte()? as usize,
+                            2 => br.read_u16be()? as usize,
+                            3 => br.read_u24be()? as usize,
+                            4 => br.read_u32be()? as usize,
+                            _ => unreachable!(),
+                        };
+                    validate!(br.left() >= (size as i64));
+                    let offset = br.tell() as usize;
+                    let size = unescape_nal(&src[offset..][..size], &mut nal_buf);
+                    validate!(size > 0);
+                    let nal_ref_idc   = nal_buf[0] >> 5;
+                    let nal_unit_type = nal_buf[0] & 0x1F;
+                    if nal_unit_type == 1 || nal_unit_type == 5 {
+                        let mut bitr = BitReader::new(&nal_buf[1..], BitReaderMode::BE);
+                        let (first_mb, slice_type) = parse_slice_header_minimal(&mut bitr)?;
+                        if first_mb == 0 && nal_ref_idc != 0 {
+                            is_ref = true;
+                        }
+                        let new_type = slice_type.to_frame_type();
+                        pic_type = match (pic_type, new_type) {
+                                         (FrameType::I, _) => new_type,
+                                         (_, FrameType::B) => FrameType::B,
+                                         _ => pic_type,
+                                     };
+                    }
+                    br.read_skip(size)?;
+                }
+                match self.skip_mode {
+                    FrameSkipMode::IntraOnly => {
+                        skip_decoding = pic_type != FrameType::I;
+                    },
+                    FrameSkipMode::KeyframesOnly => {
+                        if !is_ref {
+                            skip_decoding = true;
+                        }
+                    },
+                    _ => {},
+                };
+                br.seek(SeekFrom::Start(0))?;
+            }
+            while br.left() > 0 {
+                let size = match self.nal_len {
+                        1 => br.read_byte()? as usize,
+                        2 => br.read_u16be()? as usize,
+                        3 => br.read_u24be()? as usize,
+                        4 => br.read_u32be()? as usize,
+                        _ => unreachable!(),
+                    };
+                validate!(br.left() >= (size as i64));
+                let offset = br.tell() as usize;
+                let _size = unescape_nal(&src[offset..][..size], &mut nal_buf);
+                self.handle_nal(nal_buf.as_slice(), supp, skip_decoding)?;
+                br.read_skip(size)?;
+            }
+        } else {
+//todo NAL detection
+            unimplemented!();
+        }
+
+        let (bufinfo, ftype, dts) = if self.has_pic && self.cur_pic.is_some() {
+                let mut npic = None;
+                std::mem::swap(&mut self.cur_pic, &mut npic);
+                let cpic = npic.unwrap();
+                let ret = (NABufferType::Video(cpic.buf.clone()), cpic.pic_type, Some(u64::from(cpic.full_id)));
+                if cpic.is_ref {
+                    self.frame_refs.add_short_term(cpic.clone(), self.sps[self.cur_sps].num_ref_frames);
+                }
+                if let Some(lt_idx) = cpic.long_term {
+                    self.frame_refs.add_long_term(lt_idx, cpic);
+                }
+                ret
+            } else {
+                (NABufferType::None, FrameType::Skip, None)
+            };
+
+        let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), bufinfo);
+        frm.set_keyframe(ftype == FrameType::I);
+        if let (Some(mydts), None) = (dts, frm.get_dts()) {
+            frm.set_dts(Some(mydts));
+        }
+        if let Some(dts) = dts {
+            frm.set_id(dts as i64);
+        }
+        frm.set_frame_type(ftype);
+        Ok(frm.into_ref())
+    }
+    fn flush(&mut self) {
+    }
+}
+
+const DEBLOCK_SKIP_OPTION: &str = "skip_deblock";
+
+const DECODER_OPTIONS: &[NAOptionDefinition] = &[
+    NAOptionDefinition {
+        name: FRAME_SKIP_OPTION, description: FRAME_SKIP_OPTION_DESC,
+        opt_type: NAOptionDefinitionType::Bool },
+    NAOptionDefinition {
+        name: DEBLOCK_SKIP_OPTION, description: "Loop filter skipping mode",
+        opt_type: NAOptionDefinitionType::String(Some(&[
+                FRAME_SKIP_OPTION_VAL_NONE,
+                FRAME_SKIP_OPTION_VAL_KEYFRAME,
+                FRAME_SKIP_OPTION_VAL_INTRA
+            ])) },
+];
+
+impl NAOptionHandler for H264Decoder {
+    fn get_supported_options(&self) -> &[NAOptionDefinition] { DECODER_OPTIONS }
+    fn set_options(&mut self, options: &[NAOption]) {
+        for option in options.iter() {
+            for opt_def in DECODER_OPTIONS.iter() {
+                if opt_def.check(option).is_ok() {
+                    match (option.name, &option.value) {
+                        (FRAME_SKIP_OPTION, NAValue::String(ref str)) => {
+                            if let Ok(smode) = FrameSkipMode::from_str(str) {
+                                self.skip_mode = smode;
+                            }
+                        },
+                        (DEBLOCK_SKIP_OPTION, NAValue::Bool(val)) => {
+                            self.deblock_skip = *val;
+                        },
+                        _ => {},
+                    }
+                }
+            }
+        }
+    }
+    fn query_option_value(&self, name: &str) -> Option<NAValue> {
+        match name {
+            FRAME_SKIP_OPTION => Some(NAValue::String(self.skip_mode.to_string())),
+            DEBLOCK_SKIP_OPTION => Some(NAValue::Bool(self.deblock_skip)),
+            _ => None,
+        }
+    }
+}
+
+pub fn get_decoder() -> Box<dyn NADecoder + Send> {
+    Box::new(H264Decoder::new())
+}
+
+#[cfg(test)]
+mod test {
+    use nihav_core::codecs::RegisteredDecoders;
+    use nihav_core::demuxers::RegisteredDemuxers;
+    use nihav_codec_support::test::dec_video::*;
+    use crate::itu_register_all_decoders;
+    use nihav_commonfmt::generic_register_all_demuxers;
+
+    mod raw_demux;
+    mod conformance;
+    use self::raw_demux::RawH264DemuxerCreator;
+
+    #[test]
+    fn test_h264_perframe() {
+        let mut dmx_reg = RegisteredDemuxers::new();
+        dmx_reg.add_demuxer(&RawH264DemuxerCreator{});
+        generic_register_all_demuxers(&mut dmx_reg);
+        let mut dec_reg = RegisteredDecoders::new();
+        itu_register_all_decoders(&mut dec_reg);
+
+        test_decoding("rawh264", "h264",
+                      "assets/ITU/h264-conformance/CABAST3_Sony_E.jsv",
+                      None, &dmx_reg, &dec_reg, ExpectedTestResult::MD5Frames(vec![
+                        [0x85fc4b44, 0xc9aefdc9, 0x568d0592, 0x2eccf9a0],
+                        [0xbd8d11bc, 0x97acf592, 0x45a3cdbb, 0xa254a882],
+                        [0xbda0e0b9, 0x9fbe1974, 0x1540b244, 0x46a050ca],
+                        [0x471f0057, 0x125ef3b4, 0x4a87515f, 0xba254bbb],
+                        [0x466a7df2, 0xb392c2a4, 0xed66b68b, 0xfdaad2da],
+                        [0x96334b41, 0x41bac7ef, 0xe87154f1, 0xa5fc3551],
+                        [0x0fd4e9b8, 0x4269bbec, 0x00a1978f, 0xe6224851],
+                        [0x68be82af, 0x856615a7, 0x387a253d, 0x8473e6b9],
+                        [0xc4bed119, 0x14ba7fe0, 0x447cb680, 0x555da4c5],
+                        [0x85d127d6, 0x04b85928, 0x26740281, 0x4d848db5],
+                        [0xe44fe461, 0x0d0b64ce, 0xf191179b, 0xabdab686],
+                        [0x347c8edb, 0x847ad11f, 0x8f16b84e, 0xdc915d75],
+                        [0xeb1364a6, 0x91c9d99d, 0x324f5427, 0xcc9f11a2],
+                        [0x7aeb5a3f, 0xebc9c4dd, 0x8f12c8e4, 0x37a2db97],
+                        [0xa11e5c33, 0x656df4c0, 0x1e8b98d8, 0x1736722f],
+                        [0x239f2ef2, 0xe32b0603, 0x448366bb, 0x9331051c],
+                        [0x1815a1b1, 0xfb7e7cf0, 0xd5c7dd5b, 0x0135a8fb],
+                        [0xea3b85dd, 0xa96e7015, 0xa91c576d, 0x5c127ca1],
+                        [0x1c49148f, 0x6d9e7045, 0x093f0b7c, 0x42c2ebaa],
+                        [0x4b4c2863, 0x95709d8c, 0xeb72e251, 0x096632dc],
+                        [0x727418e5, 0x2c015383, 0x59580212, 0x0302dd99],
+                        [0xbe57dfa4, 0xf2aa7d70, 0xa068ee62, 0x77372861],
+                        [0x2faef43a, 0x73da6654, 0xb9d9c22e, 0xc59520bc],
+                        [0x138cff40, 0x3e6c108a, 0xa981e654, 0x903da85b],
+                        [0xa90454f5, 0x7875d5db, 0xbab234bd, 0xe6ce1193]]));
+    }
+
+    #[test]
+    fn test_h264_real1() {
+        let mut dmx_reg = RegisteredDemuxers::new();
+        dmx_reg.add_demuxer(&RawH264DemuxerCreator{});
+        generic_register_all_demuxers(&mut dmx_reg);
+        let mut dec_reg = RegisteredDecoders::new();
+        itu_register_all_decoders(&mut dec_reg);
+
+        test_decoding("mov", "h264", "assets/ITU/1.mp4",
+                      Some(60), &dmx_reg, &dec_reg,
+                      ExpectedTestResult::MD5Frames(vec![
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0x9dbac04a, 0xc49ca8c1, 0x09bb9182, 0xc7928970],
+                            [0xf1c88c12, 0x7da871f5, 0xdaf3153f, 0x66e72d72],
+                            [0x3d4765f1, 0x8ac472f6, 0x7ffd13a6, 0xc7a45dae],
+                            [0x60e5e13a, 0xd2d7f239, 0x1a793d71, 0x19f8c190],
+                            [0xdd80c3e4, 0xb1500149, 0x43925280, 0x9e5f3230],
+                            [0x2adf6e64, 0x39012d45, 0x7a776cb5, 0x3df76e84],
+                            [0x44319007, 0xbc837dd2, 0x486b2703, 0x451d0651],
+                            [0x922386ef, 0xaf101e9b, 0xf2094a40, 0xc8c454c0],
+                            [0x0d81e398, 0x04192a56, 0xa31f39d0, 0x5e0a2deb],
+                            [0xcdd144b3, 0xd1c7743e, 0x5753b0f4, 0xc070efa9],
+                            [0xe1c67e39, 0x6065ddaf, 0x576bf9f1, 0x8e6825aa],
+                            [0xaf817b0d, 0xdc6c345a, 0xf7f289c7, 0x6cc482d8],
+                            [0x81dc4bcb, 0xee4fc5e5, 0x9b87c7ef, 0xdd0fb034],
+                            [0x8b07f523, 0x4168799f, 0x990cb270, 0x858b9f2c],
+                            [0x31855a80, 0xadbcc562, 0xe60628be, 0x5f04be09],
+                            [0x9c614acc, 0xbd08a3f4, 0x91b02c45, 0x41899a83],
+                            [0x89d09064, 0xbff810a3, 0x9c674179, 0x305225a6],
+                            [0xba1fc8d3, 0x15d34fae, 0x565d363b, 0x4f4d0604],
+                            [0x1cb15a1b, 0xa0be111e, 0x45cc801f, 0x01a2c691],
+                            [0xd898be48, 0xd19bf58d, 0xe22fe44f, 0x6a2914fb],
+                            [0xec6712af, 0xa13b55c0, 0x2915a746, 0xb29a5c48],
+                            [0x745798fa, 0x4ef0f882, 0x59335c08, 0xb1d9dbb4],
+                            [0x4045b495, 0xdb3d969c, 0x1f0d9220, 0x5a34067b],
+                            [0x94fee093, 0x78ad89b3, 0xf20e882b, 0x941425db],
+                            [0xc3eb1a78, 0x4b4e098a, 0xcbcf9bb4, 0xfd5b5426]]));
+    }
+    #[test]
+    fn test_h264_real2() {
+        let mut dmx_reg = RegisteredDemuxers::new();
+        dmx_reg.add_demuxer(&RawH264DemuxerCreator{});
+        generic_register_all_demuxers(&mut dmx_reg);
+        let mut dec_reg = RegisteredDecoders::new();
+        itu_register_all_decoders(&mut dec_reg);
+        test_decoding("mov", "h264", "assets/ITU/DimpledSpanishCuckoo-mobile.mp4",
+                      Some(10), &dmx_reg, &dec_reg,
+                      ExpectedTestResult::MD5Frames(vec![
+                            [0x1addcb8e, 0xde58b857, 0x17222c32, 0x75455fa8],
+                            [0xae63141a, 0x79435b2e, 0xfe606c48, 0xf676da66],
+                            [0xfdb80404, 0x6a288e23, 0x45cc4106, 0xdd5eb57c],
+                            [0xd603a3ff, 0x872dcb9b, 0x43f7a71c, 0x2ad4eecc],
+                            [0x639ed6a5, 0xbb1cfec6, 0x0ee5443a, 0x1694772a],
+                            [0xf8ef3f48, 0x152de238, 0xb1995f9a, 0xf82ad1d5],
+                            [0x604f6265, 0xb9d82f56, 0x21f00cf4, 0xc69c18a7],
+                            [0xd932c16e, 0x25cbf060, 0xcb66543b, 0xfe8a5019],
+                            [0xf2a3dac0, 0x0f4678dd, 0xf64c8228, 0x47f14676],
+                            [0x267041ee, 0x3b6b8b64, 0x8bfe1697, 0x1fba508b],
+                            [0x9f917e72, 0x75d882a9, 0xa5e3e684, 0x4ed87eff]]));
+    }
+}
+
+pub const I4X4_SCAN: [(u8, u8); 16] = [
+    (0,0), (1,0), (0,1), (1,1), (2,0), (3,0), (2,1), (3,1),
+    (0,2), (1,2), (0,3), (1,3), (2,2), (3,2), (2,3), (3,3)
+];
diff --git a/nihav-itu/src/codecs/h264/pic_ref.rs b/nihav-itu/src/codecs/h264/pic_ref.rs
new file mode 100644 (file)
index 0000000..88d453d
--- /dev/null
@@ -0,0 +1,440 @@
+use nihav_core::codecs::DecoderResult;
+use nihav_core::frame::{FrameType, NAVideoBufferRef};
+use nihav_codec_support::codecs::MV;
+use super::sets::SeqParameterSet;
+use super::slice::*;
+use super::types::*;
+
+#[derive(Clone)]
+pub struct PictureInfo {
+    pub id:         u16,
+    pub full_id:    u32,
+    pub pic_type:   FrameType,
+    pub buf:        NAVideoBufferRef<u8>,
+    pub cur_mb:     usize,
+    pub is_ref:     bool,
+    pub long_term:  Option<usize>,
+
+    pub mv_info:    FrameMV, //todo replace with refcounted index to a pool
+}
+
+#[derive(Clone,Copy,Default, Debug)]
+pub struct FrameMBInfo {
+    pub mb_type:    CompactMBType,
+    pub ref_poc:    [[u16; 2]; 4],
+    pub ref_idx:    [[PicRef; 2]; 4],
+    pub mv:         [[MV; 2]; 16],
+}
+
+impl FrameMBInfo {
+    pub fn new() -> Self { Self::default() }
+}
+
+#[derive(Clone)]
+pub struct FrameMV {
+    pub mbs:        Vec<FrameMBInfo>,
+    pub mb_stride:  usize,
+}
+
+impl FrameMV {
+    pub fn new(mb_w: usize, mb_h: usize) -> Self {
+        Self {
+            mbs:        vec![FrameMBInfo::default(); mb_w * mb_h],
+            mb_stride:  mb_w,
+        }
+    }
+}
+
+pub struct FrameRefs {
+    pub ref_pics:   Vec<PictureInfo>,
+    pub ref_list0:  Vec<Option<PictureInfo>>,
+    pub ref_list1:  Vec<Option<PictureInfo>>,
+    pub long_term:  Vec<Option<PictureInfo>>,
+
+    prev_poc_msb:       u32,
+    prev_poc_lsb:       u16,
+    prev_ref_poc_lsb:   u16,
+    prev_frame_num:     u16,
+    frame_num_offset:   u32,
+}
+
+impl FrameRefs {
+    pub fn new() -> Self {
+        Self {
+            ref_pics:   Vec::with_capacity(16),
+            ref_list0:  Vec::with_capacity(3),
+            ref_list1:  Vec::with_capacity(3),
+            long_term:  Vec::new(),
+
+            prev_poc_msb:       0,
+            prev_poc_lsb:       0,
+            prev_ref_poc_lsb:   0,
+            prev_frame_num:     0,
+            frame_num_offset:   0,
+        }
+    }
+    pub fn calc_picture_num(&mut self, slice_hdr: &SliceHeader, is_idr: bool, ref_id: u8, sps: &SeqParameterSet) -> u32 {
+        match sps.pic_order_cnt_type {
+            0 => {
+                if is_idr {
+                    //self.prev_poc_msb = 0;
+                    self.prev_poc_lsb = 0;
+                } else {
+                    self.prev_poc_lsb = self.prev_ref_poc_lsb;
+                }
+                let max_poc_lsb = 1 << sps.log2_max_pic_order_cnt_lsb;
+                let half_max_poc_lsb = 1 << (sps.log2_max_pic_order_cnt_lsb - 1);
+                let cur_lsb = slice_hdr.pic_order_cnt_lsb;
+                let poc_msb = if cur_lsb < self.prev_poc_lsb && (self.prev_poc_lsb - cur_lsb >= half_max_poc_lsb) {
+                        self.prev_poc_msb + max_poc_lsb
+                    } else if cur_lsb > self.prev_poc_lsb && (cur_lsb - self.prev_poc_lsb > half_max_poc_lsb) {
+                        self.prev_poc_msb.wrapping_sub(max_poc_lsb)
+                    } else {
+                        self.prev_poc_msb
+                    };
+                let poc = poc_msb + u32::from(cur_lsb);
+                if ref_id != 0 {
+                    self.prev_ref_poc_lsb = slice_hdr.pic_order_cnt_lsb;
+                    self.prev_poc_msb = poc_msb;
+                }
+                poc
+            },
+            1 => {
+                let off = if self.prev_frame_num > slice_hdr.frame_num {
+                        self.frame_num_offset + (1 << sps.log2_max_frame_num)
+                    } else {
+                        self.frame_num_offset
+                    };
+                let mut anum = if sps.num_ref_frames_in_pic_order_cnt_cycle != 0 {
+                        (off as i32) + i32::from(slice_hdr.frame_num)
+                    } else {
+                        0
+                    };
+                if ref_id == 0 && anum > 0 {
+                    anum -= 1;
+                }
+                let (poc_cycle_cnt, fno_in_poc_cycle) = if anum > 0 {
+                        let nrf = sps.num_ref_frames_in_pic_order_cnt_cycle as i32;
+                        ((anum - 1) / nrf, (anum - 1) % nrf)
+                    } else {
+                        (0, 0)
+                    };
+                let mut expected_delta = 0;
+                for &offset in sps.offset_for_ref_frame[..sps.num_ref_frames_in_pic_order_cnt_cycle].iter() {
+                    expected_delta += offset;
+                }
+                let mut expected_poc = if anum > 0 {
+                        let mut sum = poc_cycle_cnt * expected_delta;
+                        for &offset in sps.offset_for_ref_frame[..=fno_in_poc_cycle as usize].iter() {
+                            sum += offset;
+                        }
+                        sum
+                    } else {
+                        0
+                    };
+                if ref_id == 0 {
+                    expected_poc += sps.offset_for_non_ref_pic;
+                }
+                let (top_id, _bottom_id) = if !slice_hdr.field_pic {
+                        let top_id = expected_poc + slice_hdr.delta_pic_order_cnt[0];
+                        let bot_id = top_id + sps.offset_for_top_to_bottom_field + slice_hdr.delta_pic_order_cnt[1];
+                        (top_id, bot_id)
+                    } else if !slice_hdr.bottom_field {
+                        (expected_poc + slice_hdr.delta_pic_order_cnt[0], 0)
+                    } else {
+                        (0, sps.offset_for_top_to_bottom_field + slice_hdr.delta_pic_order_cnt[1])
+                    };
+                self.prev_frame_num = slice_hdr.frame_num;
+                self.frame_num_offset = off;
+                top_id as u32
+            },
+            _ => {
+                if slice_hdr.frame_num < self.prev_frame_num {
+                    self.frame_num_offset   += 1 << sps.log2_max_frame_num;
+                }
+                self.prev_frame_num = slice_hdr.frame_num;
+                self.frame_num_offset + u32::from(slice_hdr.frame_num)
+            },
+        }
+    }
+    pub fn apply_adaptive_marking(&mut self, marking: &AdaptiveMarking, cur_id: u16, max_id: u16) -> DecoderResult<()> {
+        let all_ref_pics = self.ref_pics.clone();
+
+        for (&op, (&arg1, &arg2)) in marking.memory_management_control_op.iter().zip(marking.operation_arg.iter().zip(marking.operation_arg2.iter())).take(marking.num_ops) {
+            match op {
+                1 => {
+                    let src_id = cur_id.wrapping_sub(arg1) & (max_id - 1);
+                    let mut found = false;
+                    let mut idx = 0;
+                    for (i, pic) in self.ref_pics.iter().enumerate() {
+                        if pic.id == src_id {
+                            found = true;
+                            idx = i;
+                            break;
+                        }
+                    }
+                    if found {
+                        self.ref_pics.remove(idx);
+                    }
+                },
+                2 => { // mark long term picture as unused
+                    let idx = arg1 as usize;
+                    if idx < self.long_term.len() {
+                        self.long_term[idx] = None;
+                    }
+                },
+                3 => {
+                    let src_id = cur_id.wrapping_sub(arg1) & (max_id - 1);
+
+                    let didx = arg2 as usize;
+                    for pic in all_ref_pics.iter() {
+                        if pic.id == src_id {
+                            if didx < self.long_term.len() {
+                                self.long_term[didx] = Some(pic.clone());
+                            }
+                            break;
+                        }
+                    }
+                },
+                4 => {
+                    self.long_term.resize(arg1 as usize, None);
+                },
+                5 => {
+                    self.ref_pics.truncate(0);
+                    self.long_term.truncate(0);
+                },
+                6 => {
+                    // assign an long term index to current pic - done elsewhere
+                },
+                _ => {},
+            };
+        }
+        Ok(())
+    }
+    pub fn clear_refs(&mut self) {
+        self.ref_pics.truncate(0);
+        self.long_term.truncate(0);
+    }
+    pub fn select_refs(&mut self, sps: &SeqParameterSet, slice_hdr: &SliceHeader, cur_id: u32) {
+        self.ref_list0.truncate(0);
+        self.ref_list1.truncate(0);
+        let pic_num_mask = if sps.log2_max_frame_num == 16 {
+                0xFFFF
+            } else {
+                (1 << sps.log2_max_frame_num) - 1
+            };
+        if !slice_hdr.slice_type.is_intra() {
+            let has_reordering = slice_hdr.ref_pic_list_reordering_l0;
+            if !has_reordering {
+                let num_ref = slice_hdr.num_ref_idx_l0_active;
+                if slice_hdr.slice_type.is_p() {
+                    if !self.ref_pics.is_empty() {
+                        for pic in self.ref_pics.iter().rev().take(num_ref) {
+                            self.ref_list0.push(Some(pic.clone()));
+                        }
+                    }
+                } else {
+                    let mut pivot = 0;
+                    for (i, pic) in self.ref_pics.iter().enumerate() {
+                        pivot = i;
+                        if pic.full_id > cur_id {
+                            break;
+                        }
+                    }
+                    for pic in self.ref_pics[..pivot].iter().rev() {
+                        if self.ref_list0.len() >= num_ref {
+                            break;
+                        }
+                        self.ref_list0.push(Some(pic.clone()));
+                    }
+                    for pic in self.ref_pics.iter().skip(pivot) {
+                        if self.ref_list0.len() >= num_ref {
+                            break;
+                        }
+                        self.ref_list0.push(Some(pic.clone()));
+                    }
+                }
+                if !self.long_term.is_empty() && self.ref_list0.len() < num_ref {
+                    let copy_size = num_ref - self.ref_list0.len();
+                    for ltpic in self.long_term.iter().take(copy_size) {
+                        self.ref_list0.push(ltpic.clone());
+                    }
+                }
+            } else {
+                form_ref_list(&mut self.ref_list0,
+                              &self.ref_pics, &self.long_term,
+                              &slice_hdr.reordering_list_l0,
+                              slice_hdr.frame_num, pic_num_mask);
+            }
+            if slice_hdr.slice_type.is_b() {
+                let has_reordering = slice_hdr.ref_pic_list_reordering_l1;
+                if !has_reordering {
+                    let num_ref = slice_hdr.num_ref_idx_l1_active;
+                    let mut pivot = 0;
+                    for (i, pic) in self.ref_pics.iter().enumerate() {
+                        pivot = i;
+                        if pic.full_id > cur_id {
+                            break;
+                        }
+                    }
+                    for pic in self.ref_pics.iter().skip(pivot) {
+                        if self.ref_list1.len() >= num_ref {
+                            break;
+                        }
+                        self.ref_list1.push(Some(pic.clone()));
+                    }
+                    for pic in self.ref_pics[..pivot].iter().rev() {
+                        if self.ref_list1.len() >= num_ref {
+                            break;
+                        }
+                        self.ref_list1.push(Some(pic.clone()));
+                    }
+                    if !self.long_term.is_empty() && self.ref_list1.len() < num_ref {
+                        let copy_size = num_ref - self.ref_list1.len();
+                        for ltpic in self.long_term.iter().take(copy_size) {
+                            self.ref_list1.push(ltpic.clone());
+                        }
+                    }
+                    if self.ref_list1.len() > 1 && self.ref_list0.len() == self.ref_list1.len() {
+                        let mut equal = true;
+                        for (pic1, pic2) in self.ref_list0.iter().zip(self.ref_list1.iter()) {
+                            match (pic1, pic2) {
+                                (Some(p1), Some(p2)) => {
+                                    if p1.full_id != p2.full_id {
+                                        equal = false;
+                                        break;
+                                    }
+                                },
+                                (None, None) => {},
+                                _ => {
+                                    equal = false;
+                                    break;
+                                },
+                            };
+                        }
+                        if equal {
+                            self.ref_list1.swap(0, 1);
+                        }
+                    }
+                } else {
+                    form_ref_list(&mut self.ref_list1,
+                                  &self.ref_pics, &self.long_term,
+                                  &slice_hdr.reordering_list_l1,
+                                  slice_hdr.frame_num, pic_num_mask);
+                }
+            }
+        }
+    }
+    pub fn add_short_term(&mut self, cpic: PictureInfo, num_ref_frames: usize) {
+        if !self.ref_pics.is_empty() && self.ref_pics.len() >= num_ref_frames {
+            self.ref_pics.remove(0);
+        }
+        if self.ref_pics.is_empty() || self.ref_pics.last().unwrap().full_id < cpic.full_id {
+            self.ref_pics.push(cpic);
+        } else {
+            let mut idx = 0;
+            for (i, pic) in self.ref_pics.iter().enumerate() {
+                if pic.full_id < cpic.full_id {
+                    idx = i;
+                } else {
+                    break;
+                }
+            }
+            self.ref_pics.insert(idx + 1, cpic);
+        }
+    }
+    pub fn add_long_term(&mut self, lt_idx: usize, cpic: PictureInfo) {
+        if lt_idx < self.long_term.len() {
+            self.long_term[lt_idx] = Some(cpic);
+        }
+    }
+    pub fn select_ref_pic(&self, list_id: u8, ref_id: usize) -> Option<NAVideoBufferRef<u8>> {
+        let ref_list = if list_id == 0 { &self.ref_list0 } else { &self.ref_list1 };
+        if ref_list.len() > ref_id {
+            if let Some(ref pic) = ref_list[ref_id] {
+                Some(pic.buf.clone())
+            } else {
+                None
+            }
+        } else {
+            None
+        }
+    }
+    pub fn get_colocated_info(&self, mb_x: usize, mb_y: usize) -> (FrameMBInfo, u16, bool) {
+        if let Some(ref ref_pic) = &self.ref_list1[0] {
+            let mv_info = &ref_pic.mv_info;
+            let mb = mv_info.mbs[mb_x + mb_y * mv_info.mb_stride];
+            (mb, ref_pic.full_id as u16, ref_pic.long_term.is_some())
+        } else {
+            (FrameMBInfo::default(), 0, false)
+        }
+    }
+    pub fn map_ref0(&self, ref0_id: u16) -> (PicRef, bool) {
+        let mut r0_idx = 0;
+        let mut long = false;
+        for (i, rpic0) in self.ref_list0.iter().enumerate() {
+            if let Some(ref pic) = rpic0 {
+                if (pic.full_id as u16) == ref0_id {
+                    r0_idx = i as u8;
+                    long = pic.long_term.is_some();
+                    break;
+                }
+            }
+        }
+        (PicRef::new(r0_idx), long)
+    }
+    pub fn map_refs(&self, ref_idx: [PicRef; 2]) -> [u16; 2] {
+        let r0 = ref_idx[0].index();
+        let r1 = ref_idx[1].index();
+        let ref0 = if r0 < self.ref_list0.len() {
+                if let Some(ref pic) = self.ref_list0[r0] {
+                    pic.full_id as u16
+                } else {
+                    MISSING_POC
+                }
+            } else {
+                MISSING_POC
+            };
+        let ref1 = if r1 < self.ref_list1.len() {
+                if let Some(ref pic) = self.ref_list1[r1] {
+                    pic.full_id as u16
+                } else {
+                    MISSING_POC
+                }
+            } else {
+                MISSING_POC
+            };
+        [ref0, ref1]
+    }
+}
+
+fn form_ref_list(ref_list: &mut Vec<Option<PictureInfo>>, ref_pics: &Vec<PictureInfo>, long_term: &Vec<Option<PictureInfo>>, reord_info: &ReorderingInfo, cur_id: u16, pic_num_mask: u16) {
+    let mut ref_pic_id = cur_id;
+    for (&op, &num) in reord_info.reordering_of_pic_nums_idc.iter().zip(reord_info.abs_diff_or_num.iter()).take(reord_info.num_ops) {
+        if op < 2 {
+            if op == 0 {
+                ref_pic_id = ref_pic_id.wrapping_sub(num) & pic_num_mask;
+            } else {
+                ref_pic_id = ref_pic_id.wrapping_add(num) & pic_num_mask;
+            }
+            let mut found = false;
+            for pic in ref_pics.iter() {
+                if pic.id == ref_pic_id {
+                    ref_list.push(Some(pic.clone()));
+                    found = true;
+                    break;
+                }
+            }
+            if !found {
+                ref_list.push(None);
+            }
+        } else {
+            let idx = num as usize;
+            if idx < long_term.len() {
+                ref_list.push(long_term[idx].clone());
+            } else {
+                ref_list.push(None);
+            }
+        }
+    }
+}
diff --git a/nihav-itu/src/codecs/h264/sets.rs b/nihav-itu/src/codecs/h264/sets.rs
new file mode 100644 (file)
index 0000000..8856699
--- /dev/null
@@ -0,0 +1,423 @@
+use nihav_core::codecs::{DecoderResult, DecoderError};
+use nihav_core::io::bitreader::*;
+
+use super::ReadUE;
+
+#[derive(Clone)]
+pub struct SeqParameterSet {
+    pub profile_idc:                        u8,
+    pub high_profile:                       bool,
+    pub constraint_set0:                    bool,
+    pub constraint_set1:                    bool,
+    pub constraint_set2:                    bool,
+    pub level_idc:                          u8,
+    pub seq_parameter_set_id:               u32,
+    pub chroma_format_idc:                  u8,
+    pub separate_colour_plane:              bool,
+    pub bit_depth_luma:                     u8,
+    pub bit_depth_chroma:                   u8,
+    pub qpprime_y_zero_transform_bypass:    bool,
+    pub seq_scaling_matrix_present:         bool,
+    pub scaling_list_4x4:                   [[u8; 16]; 6],
+    pub scaling_list_8x8:                   [[u8; 64]; 6],
+    pub log2_max_frame_num:                 u8,
+    pub pic_order_cnt_type:                 u8,
+    pub log2_max_pic_order_cnt_lsb:         u8,
+    pub delta_pic_order_always_zero:        bool,
+    pub offset_for_non_ref_pic:             i32,
+    pub offset_for_top_to_bottom_field:     i32,
+    pub num_ref_frames_in_pic_order_cnt_cycle:  usize,
+    pub offset_for_ref_frame:               [i32; 256],
+    pub num_ref_frames:                     usize,
+    pub gaps_in_frame_num_value_allowed:    bool,
+    pub pic_width_in_mbs:                   usize,
+    pub pic_height_in_mbs:                  usize,
+    pub frame_mbs_only:                     bool,
+    pub mb_adaptive_frame_field:            bool,
+    pub direct_8x8_inference:               bool,
+    pub frame_cropping:                     bool,
+    pub frame_crop_left_offset:             usize,
+    pub frame_crop_right_offset:            usize,
+    pub frame_crop_top_offset:              usize,
+    pub frame_crop_bottom_offset:           usize,
+    pub vui_parameters_present:             bool,
+}
+
+pub fn is_high_profile(profile: u8) -> bool {
+    match profile {
+        100 | 110 | 122 | 244 | 44 | 83 | 86 | 118 | 128 | 138 | 139 | 134 | 125 => true,
+        _ => false,
+    }
+}
+
+pub fn parse_sps(src: &[u8]) -> DecoderResult<SeqParameterSet> {
+    let mut br = BitReader::new(src, BitReaderMode::BE);
+    let mut sps: SeqParameterSet = unsafe { std::mem::zeroed() };
+
+    sps.profile_idc                                 = br.read(8)? as u8;
+    sps.constraint_set0                             = br.read_bool()?;
+    sps.constraint_set1                             = br.read_bool()?;
+    sps.constraint_set2                             = br.read_bool()?;
+    let reserved                                    = br.read(5)?;
+    validate!(reserved == 0);
+    sps.level_idc                                   = br.read(8)? as u8;
+    sps.seq_parameter_set_id                        = br.read_ue()?;
+    sps.high_profile = is_high_profile(sps.profile_idc);
+    if sps.high_profile {
+        sps.chroma_format_idc                       = br.read_ue_lim(3)? as u8;
+        if sps.chroma_format_idc == 3 {
+            sps.separate_colour_plane               = br.read_bool()?;
+        }
+        sps.bit_depth_luma                          = br.read_ue_lim(6)? as u8 + 8;
+        sps.bit_depth_chroma                        = br.read_ue_lim(6)? as u8 + 8;
+        sps.qpprime_y_zero_transform_bypass         = br.read_bool()?;
+        sps.seq_scaling_matrix_present              = br.read_bool()?;
+        if sps.seq_scaling_matrix_present {
+            let mut slist_present = [false; 6];
+            for (i, slist) in sps.scaling_list_4x4.iter_mut().enumerate() {
+                slist_present[i]                    = br.read_bool()?;
+                if slist_present[i] {
+                    parse_scaling_list(&mut br, slist, i < 3)?;
+                }
+            }
+            for i in 1..6 {
+                if i == 3 {
+                    continue;
+                }
+                if !slist_present[i] {
+                    sps.scaling_list_4x4[i] = sps.scaling_list_4x4[i - 1];
+                }
+            }
+
+            let mut slist_present = [false; 6];
+            let num_8x8 = if sps.chroma_format_idc != 3 { 2 } else { 6 };
+            for (i, slist) in sps.scaling_list_8x8.iter_mut().take(num_8x8).enumerate() {
+                slist_present[i]                    = br.read_bool()?;
+                if slist_present[i] {
+                    parse_scaling_list(&mut br, slist, (i & 1) == 0)?;
+                }
+            }
+            if num_8x8 > 2 {
+                for i in 2..6 {
+                    if !slist_present[i] {
+                        sps.scaling_list_8x8[i] = sps.scaling_list_8x8[i - 2];
+                    }
+                }
+            }
+        } else {
+            sps.scaling_list_4x4 = [[16; 16]; 6];
+            sps.scaling_list_8x8 = [[16; 64]; 6];
+        }
+    } else {
+        sps.chroma_format_idc = 1;
+        sps.bit_depth_luma = 8;
+        sps.bit_depth_chroma = 8;
+    }
+    sps.log2_max_frame_num                          = (br.read_ue_lim(12)? + 4) as u8;
+    sps.pic_order_cnt_type                          = br.read_ue_lim(2)? as u8;
+    match sps.pic_order_cnt_type {
+        0 => {
+            sps.log2_max_pic_order_cnt_lsb          = (br.read_ue_lim(12)? + 4) as u8;
+        },
+        1 => {
+            sps.delta_pic_order_always_zero         = br.read_bool()?;
+            sps.offset_for_non_ref_pic              = br.read_se()?;
+            sps.offset_for_top_to_bottom_field      = br.read_se()?;
+            sps.num_ref_frames_in_pic_order_cnt_cycle   = br.read_ue_lim(255)? as usize;
+            for offset in sps.offset_for_ref_frame[..sps.num_ref_frames_in_pic_order_cnt_cycle].iter_mut() {
+                *offset                             = br.read_se()?;
+            }
+        },
+        _ => {},
+    };
+    sps.num_ref_frames                              = br.read_ue()? as usize;
+    validate!(sps.num_ref_frames <= super::slice::MAX_FRAMES);
+    sps.gaps_in_frame_num_value_allowed             = br.read_bool()?;
+    sps.pic_width_in_mbs                            = (br.read_ue()? + 1) as usize;
+    sps.pic_height_in_mbs                           = (br.read_ue()? + 1) as usize;
+    validate!(sps.pic_width_in_mbs <= 1024 && sps.pic_height_in_mbs <= 1024);
+    sps.frame_mbs_only                              = br.read_bool()?;
+    if !sps.frame_mbs_only {
+        sps.mb_adaptive_frame_field                 = br.read_bool()?;
+    }
+    sps.direct_8x8_inference                        = br.read_bool()?;
+    sps.frame_cropping                              = br.read_bool()?;
+    if sps.frame_cropping {
+        sps.frame_crop_left_offset                  = br.read_ue()? as usize;
+        sps.frame_crop_right_offset                 = br.read_ue()? as usize;
+        sps.frame_crop_top_offset                   = br.read_ue()? as usize;
+        sps.frame_crop_bottom_offset                = br.read_ue()? as usize;
+        let l = sps.frame_crop_left_offset * 2;
+        let r = sps.pic_width_in_mbs * 16 - sps.frame_crop_right_offset * 2;
+        let t = sps.frame_crop_top_offset * 2;
+        let d = sps.pic_height_in_mbs * 16 - sps.frame_crop_bottom_offset * 2;
+        validate!(l < r && t < d);
+    }
+    sps.vui_parameters_present                      = br.read_bool()?;
+    if sps.vui_parameters_present {
+        // xxx: vui is ignored for now
+        if br.read_bool()? {
+            let idc = br.read(8)?;
+            if idc == 255 {
+                br.read(16)?;
+                br.read(16)?;
+            }
+        }
+        if br.read_bool()? {
+            br.read_bool()?;
+        }
+        if br.read_bool()? {
+            br.read(3)?;
+            br.read_bool()?;
+            if br.read_bool()? {
+                br.read(8)?;
+                br.read(8)?;
+                br.read(8)?;
+            }
+        }
+        if br.read_bool()? {
+            br.read_ue()?;
+            br.read_ue()?;
+        }
+        if br.read_bool()? {
+            br.read(32)?;
+            br.read(32)?;
+            br.read_bool()?;
+        }
+        let nal_hrd_parameters_present = br.read_bool()?;
+        if nal_hrd_parameters_present {
+            skip_hrd_parameters(&mut br)?;
+        }
+        let vcl_hrd_parameters_present = br.read_bool()?;
+        if vcl_hrd_parameters_present {
+            skip_hrd_parameters(&mut br)?;
+        }
+        if nal_hrd_parameters_present || vcl_hrd_parameters_present {
+            br.read_bool()?;
+        }
+        br.read_bool()?;
+        if br.read_bool()? {
+            br.read_bool()?;
+            br.read_ue()?;
+            br.read_ue()?;
+            br.read_ue()?;
+            br.read_ue()?;
+            br.read_ue()?;
+            br.read_ue()?;
+        }
+    }
+
+    Ok(sps)
+}
+
+fn parse_scaling_list(br: &mut BitReader, slist: &mut[u8], is_intra: bool) -> DecoderResult<()> {
+    const DEFAULT_INTRA_4X4: [u8; 16] = [
+        6, 13, 13, 20, 20, 20, 28, 28, 28, 28, 32, 32, 32, 37, 37, 42
+    ];
+    const DEFAULT_INTER_4X4: [u8; 16] = [
+        10, 14, 14, 20, 20, 20, 24, 24, 24, 24, 27, 27, 27, 30, 30, 34
+    ];
+    const DEFAULT_INTRA_8X8: [u8; 64] = [
+         6, 10, 10, 13, 11, 13, 16, 16, 16, 16, 18, 18, 18, 18, 18, 23,
+        23, 23, 23, 23, 23, 25, 25, 25, 25, 25, 25, 25, 27, 27, 27, 27,
+        27, 27, 27, 27, 29, 29, 29, 29, 29, 29, 29, 31, 31, 31, 31, 31,
+        31, 33, 33, 33, 33, 33, 36, 36, 36, 36, 38, 38, 38, 40, 40, 42
+    ];
+    const DEFAULT_INTER_8X8: [u8; 64] = [
+         9, 13, 13, 15, 13, 15, 17, 17, 17, 17, 19, 19, 19, 19, 19, 21,
+        21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 24, 24, 24, 24,
+        24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 27, 27, 27, 27, 27,
+        27, 28, 28, 28, 28, 28, 30, 30, 30, 30, 32, 32, 32, 33, 33, 35
+    ];
+    let mut last_scale = 8u8;
+    let mut next_scale = 8u8;
+    let mut use_default = false;
+    for (j, elem) in slist.iter_mut().enumerate() {
+        if next_scale != 0 {
+            let delta                   = br.read_se()?;
+            next_scale = last_scale.wrapping_add(delta as u8);
+            if (j == 0) && (next_scale == 0) {
+                use_default = true;
+                break;
+            }
+        }
+        *elem = if next_scale == 0 { last_scale } else { next_scale };
+        last_scale = *elem;
+    }
+    if use_default {
+        match (slist.len(), is_intra) {
+            (16, true)  => slist.copy_from_slice(&DEFAULT_INTRA_4X4),
+            (16, false) => slist.copy_from_slice(&DEFAULT_INTER_4X4),
+            (64, true)  => slist.copy_from_slice(&DEFAULT_INTRA_8X8),
+            (64, false) => slist.copy_from_slice(&DEFAULT_INTER_8X8),
+            _ => unreachable!(),
+        };
+    }
+    Ok(())
+}
+
+fn skip_hrd_parameters(br: &mut BitReader) -> DecoderResult<()> {
+    let cpb_cnt = br.read_ue()? as usize + 1;
+    br.read(4)?;
+    br.read(4)?;
+    for _ in 0..cpb_cnt {
+        br.read_ue()?;
+        br.read_ue()?;
+        br.read_bool()?;
+    }
+    br.read(5)?;
+    br.read(5)?;
+    br.read(5)?;
+    br.read(5)?;
+    Ok(())
+}
+
+const MAX_SLICE_GROUPS: usize = 8;
+
+#[derive(Clone)]
+pub struct PicParameterSet {
+    pub pic_parameter_set_id:               u32,
+    pub seq_parameter_set_id:               u32,
+    pub entropy_coding_mode:                bool,
+    pub pic_order_present:                  bool,
+    pub num_slice_groups:                   usize,
+    pub slice_group_map_type:               u8,
+    pub run_length:                         [u32; MAX_SLICE_GROUPS],
+    pub top_left:                           [u32; MAX_SLICE_GROUPS],
+    pub bottom_right:                       [u32; MAX_SLICE_GROUPS],
+    pub slice_group_change_direction:       bool,
+    pub slice_group_change_rate:            u32,
+    pub pic_size_in_map_units:              u32,
+    pub num_ref_idx_l0_active:              usize,
+    pub num_ref_idx_l1_active:              usize,
+    pub weighted_pred:                      bool,
+    pub weighted_bipred_idc:                u8,
+    pub pic_init_qp:                        u8,
+    pub pic_init_qs:                        u8,
+    pub chroma_qp_index_offset:             i8,
+    pub deblocking_filter_control_present:  bool,
+    pub constrained_intra_pred:             bool,
+    pub redundant_pic_cnt_present:          bool,
+    pub transform_8x8_mode:                 bool,
+    pub pic_scaling_matrix_present:         bool,
+    pub scaling_list_4x4:                   [[u8; 16]; 6],
+    pub scaling_list_8x8:                   [[u8; 64]; 6],
+    pub second_chroma_qp_index_offset:      i8,
+}
+
+pub fn parse_pps(src: &[u8], sps_arr: &[SeqParameterSet], full_size: usize) -> DecoderResult<PicParameterSet> {
+    let mut br = BitReader::new(src, BitReaderMode::BE);
+    let mut pps: PicParameterSet = unsafe { std::mem::zeroed() };
+
+    pps.pic_parameter_set_id                        = br.read_ue()?;
+    pps.seq_parameter_set_id                        = br.read_ue()?;
+    let mut found = false;
+    let mut cur_sps = None;
+    for sps in sps_arr.iter() {
+        if sps.seq_parameter_set_id == pps.seq_parameter_set_id {
+            found = true;
+            cur_sps = Some(sps);
+            break;
+        }
+    }
+    validate!(found);
+    let sps = cur_sps.unwrap();
+    pps.entropy_coding_mode                         = br.read_bool()?;
+    pps.pic_order_present                           = br.read_bool()?;
+    pps.num_slice_groups                            = (br.read_ue()? + 1) as usize;
+    validate!(pps.num_slice_groups <= MAX_SLICE_GROUPS);
+    if pps.num_slice_groups > 1 {
+        let smtype                                  = br.read_ue()?;
+        validate!(smtype <= 6);
+        pps.slice_group_map_type = smtype as u8;
+        match pps.slice_group_map_type {
+            0 => {
+                for elem in pps.run_length[..pps.num_slice_groups].iter_mut() {
+                    *elem                           = br.read_ue()?;
+                }
+            },
+            2 => {
+                for i in 0..pps.num_slice_groups - 1 {
+                    pps.top_left[i]                 = br.read_ue()?;
+                    pps.bottom_right[i]             = br.read_ue()?;
+                }
+            },
+            3 | 4 | 5 => {
+                pps.slice_group_change_direction    = br.read_bool()?;
+                pps.slice_group_change_rate         = br.read_ue()?;
+            },
+            6 => {
+                pps.pic_size_in_map_units           = br.read_ue()? + 1;
+                for _ in 0..pps.pic_size_in_map_units {
+                    let _slice_group_id             = br.read_ue()?;
+                }
+            },
+            _ => {},
+        };
+println!("slice mode!");
+        return Err(DecoderError::NotImplemented);
+    }
+    pps.num_ref_idx_l0_active                       = (br.read_ue()? + 1) as usize;
+    pps.num_ref_idx_l1_active                       = (br.read_ue()? + 1) as usize;
+    pps.weighted_pred                               = br.read_bool()?;
+    pps.weighted_bipred_idc                         = br.read(2)? as u8;
+    let qp                                          = br.read_se()? + 26;
+    validate!(qp > 0 && qp < 52);
+    pps.pic_init_qp = qp as u8;
+    let qs                                          = br.read_se()? + 26;
+    validate!(qs > 0 && qs < 52);
+    pps.pic_init_qs = qs as u8;
+    let off                                         = br.read_se()?;
+    validate!(off >= -12 && off <= 12);
+    pps.chroma_qp_index_offset = off as i8;
+    pps.deblocking_filter_control_present           = br.read_bool()?;
+    pps.constrained_intra_pred                      = br.read_bool()?;
+    pps.redundant_pic_cnt_present                   = br.read_bool()?;
+    if br.tell() < full_size {
+        pps.transform_8x8_mode                      = br.read_bool()?;
+        pps.pic_scaling_matrix_present              = br.read_bool()?;
+        if pps.pic_scaling_matrix_present {
+            let mut slist_present = [false; 6];
+            for (i, slist) in pps.scaling_list_4x4.iter_mut().enumerate() {
+                slist_present[i]                    = br.read_bool()?;
+                if slist_present[i] {
+                    parse_scaling_list(&mut br, slist, i < 3)?;
+                }
+            }
+            for i in 1..6 {
+                if i == 3 {
+                    continue;
+                }
+                if !slist_present[i] {
+                    pps.scaling_list_4x4[i] = pps.scaling_list_4x4[i - 1];
+                }
+            }
+
+            let mut slist_present = [false; 6];
+            let num_8x8 = if !pps.transform_8x8_mode { 0 } else if sps.chroma_format_idc != 3 { 2 } else { 6 };
+            for (i, slist) in pps.scaling_list_8x8.iter_mut().take(num_8x8).enumerate() {
+                slist_present[i]                    = br.read_bool()?;
+                if slist_present[i] {
+                    parse_scaling_list(&mut br, slist, (i & 1) == 0)?;
+                }
+            }
+            if num_8x8 > 2 {
+                for i in 2..6 {
+                    if !slist_present[i] {
+                        pps.scaling_list_8x8[i] = pps.scaling_list_8x8[i - 2];
+                    }
+                }
+            }
+        } else {
+            pps.scaling_list_4x4 = sps.scaling_list_4x4;
+            pps.scaling_list_8x8 = sps.scaling_list_8x8;
+        }
+        let off                                     = br.read_se()?;
+        validate!(off >= -12 && off <= 12);
+        pps.second_chroma_qp_index_offset = off as i8;
+    } else {
+        pps.second_chroma_qp_index_offset = pps.chroma_qp_index_offset;
+    }
+
+    Ok(pps)
+}
diff --git a/nihav-itu/src/codecs/h264/slice.rs b/nihav-itu/src/codecs/h264/slice.rs
new file mode 100644 (file)
index 0000000..a4ce4bf
--- /dev/null
@@ -0,0 +1,384 @@
+use nihav_core::codecs::{DecoderResult, DecoderError};
+use nihav_core::frame::FrameType;
+use nihav_core::io::bitreader::*;
+
+use super::ReadUE;
+use super::sets::*;
+
+pub const MAX_FRAMES: usize = 32;
+
+#[derive(Clone,Copy,Debug,PartialEq)]
+pub enum SliceType {
+    I,
+    P,
+    B,
+    SI,
+    SP,
+}
+
+impl SliceType {
+    pub fn is_intra(self) -> bool {
+        match self {
+            SliceType::I | SliceType::SI => true,
+            _ => false,
+        }
+    }
+    pub fn is_p(self) -> bool {
+        match self {
+            SliceType::P | SliceType::SP => true,
+            _ => false,
+        }
+    }
+    pub fn is_b(self) -> bool { self == SliceType::B }
+    pub fn is_s(self) -> bool {
+        match self {
+            SliceType::SI | SliceType::SP => true,
+            _ => false,
+        }
+    }
+    pub fn to_frame_type(self) -> FrameType {
+        match self {
+            SliceType::I | SliceType::SI => FrameType::I,
+            SliceType::P | SliceType::SP => FrameType::P,
+            SliceType::B                 => FrameType::B,
+        }
+    }
+}
+
+const SLICE_TYPES: [SliceType; 10] = [
+    SliceType::P, SliceType::B, SliceType::I, SliceType::SP, SliceType::SI,
+    SliceType::P, SliceType::B, SliceType::I, SliceType::SP, SliceType::SI,
+];
+
+#[derive(Clone,Copy)]
+pub struct WeightInfo {
+    pub luma_weighted:                      bool,
+    pub luma_weight:                        i8,
+    pub luma_offset:                        i8,
+    pub chroma_weighted:                    bool,
+    pub chroma_weight:                      [i8; 2],
+    pub chroma_offset:                      [i8; 2],
+}
+
+#[derive(Clone,Copy)]
+pub struct ReorderingInfo {
+    pub reordering_of_pic_nums_idc:         [u8; MAX_FRAMES],
+    pub abs_diff_or_num:                    [u16; MAX_FRAMES],
+    pub num_ops:                            usize,
+}
+
+#[derive(Clone,Copy)]
+pub struct AdaptiveMarking {
+    pub memory_management_control_op:       [u8; MAX_FRAMES],
+    pub operation_arg:                      [u16; MAX_FRAMES],
+    pub operation_arg2:                     [u16; MAX_FRAMES],
+    pub num_ops:                            usize,
+}
+
+#[derive(Clone)]
+pub struct SliceHeader {
+    pub first_mb_in_slice:                  usize,
+    pub slice_type:                         SliceType,
+    pub same_slice_type:                    bool,
+    pub pic_parameter_set_id:               u32,
+    pub frame_num:                          u16,
+    pub field_pic:                          bool,
+    pub bottom_field:                       bool,
+    pub idr_pic_id:                         u16,
+    pub pic_order_cnt_lsb:                  u16,
+    pub delta_pic_order_cnt_bottom:         i32,
+    pub delta_pic_order_cnt:                [i32; 2],
+    pub redundant_pic_cnt:                  u8,
+    pub direct_spatial_mv_pred:             bool,
+    pub num_ref_idx_active_override:        bool,
+    pub num_ref_idx_l0_active:              usize,
+    pub num_ref_idx_l1_active:              usize,
+    pub ref_pic_list_reordering_l0:         bool,
+    pub reordering_list_l0:                 ReorderingInfo,
+    pub ref_pic_list_reordering_l1:         bool,
+    pub reordering_list_l1:                 ReorderingInfo,
+    pub luma_log2_weight_denom:             u8,
+    pub chroma_log2_weight_denom:           u8,
+    pub weights_l0:                         [WeightInfo; MAX_FRAMES],
+    pub weights_l1:                         [WeightInfo; MAX_FRAMES],
+    pub no_output_of_prior_pics:            bool,
+    pub long_term_reference:                bool,
+    pub adaptive_ref_pic_marking_mode:      bool,
+    pub adaptive_ref_pic_marking:           AdaptiveMarking,
+    pub cabac_init_idc:                     u8,
+    pub slice_qp_delta:                     i32,
+    pub slice_qp:                           u8,
+    pub sp_for_switch:                      bool,
+    pub slice_qs_delta:                     i32,
+    pub slice_qs:                           u8,
+    pub disable_deblocking_filter_idc:      u8,
+    pub slice_alpha_c0_offset:              i8,
+    pub slice_beta_offset:                  i8,
+    pub slice_group_change_cycle:           u32,
+}
+
+pub fn parse_slice_header_minimal(br: &mut BitReader) -> DecoderResult<(usize, SliceType)> {
+    let first_mb_in_slice                           = br.read_ue()? as usize;
+    let stype                                       = br.read_ue_lim(SLICE_TYPES.len() as u32 - 1)?;
+    let slice_type = SLICE_TYPES[stype as usize];
+    Ok((first_mb_in_slice, slice_type))
+}
+
+pub fn parse_slice_header(br: &mut BitReader, sps_arr: &[SeqParameterSet], pps_arr: &[PicParameterSet], is_idr: bool, nal_ref_idc: u8) -> DecoderResult<SliceHeader> {
+    let mut hdr: SliceHeader = unsafe { std::mem::zeroed() };
+
+    hdr.first_mb_in_slice                           = br.read_ue()? as usize;
+    let stype                                       = br.read_ue_lim(SLICE_TYPES.len() as u32 - 1)?;
+    hdr.slice_type = SLICE_TYPES[stype as usize];
+    hdr.same_slice_type = stype >= 5;
+    hdr.pic_parameter_set_id                        = br.read_ue()?;
+
+    let mut pps_ptr = None;
+    for pps in pps_arr.iter() {
+        if pps.pic_parameter_set_id == hdr.pic_parameter_set_id {
+            pps_ptr = Some(pps);
+            break;
+        }
+    }
+    validate!(pps_ptr.is_some());
+    let pps = pps_ptr.unwrap();
+    let mut sps_ptr = None;
+    for sps in sps_arr.iter() {
+        if sps.seq_parameter_set_id == pps.seq_parameter_set_id {
+            sps_ptr = Some(sps);
+            break;
+        }
+    }
+    validate!(sps_ptr.is_some());
+    let sps = sps_ptr.unwrap();
+
+    hdr.frame_num                                   = br.read(sps.log2_max_frame_num)? as u16;
+    if !sps.frame_mbs_only {
+        hdr.field_pic                               = br.read_bool()?;
+        if hdr.field_pic {
+            hdr.bottom_field                        = br.read_bool()?;
+        }
+    }
+
+    if is_idr {
+        hdr.idr_pic_id                              = br.read_ue_lim(65535)? as u16;
+    }
+    if sps.pic_order_cnt_type == 0 {
+        hdr.pic_order_cnt_lsb                       = br.read(sps.log2_max_pic_order_cnt_lsb)? as u16;
+        if pps.pic_order_present && !hdr.field_pic {
+            hdr.delta_pic_order_cnt_bottom          = br.read_se()?;
+        }
+    } else if sps.pic_order_cnt_type == 1 && !sps.delta_pic_order_always_zero {
+        hdr.delta_pic_order_cnt[0]                  = br.read_se()?;
+        if pps.pic_order_present && !hdr.field_pic {
+            hdr.delta_pic_order_cnt[1]              = br.read_se()?;
+        }
+    }
+    if pps.redundant_pic_cnt_present {
+        hdr.redundant_pic_cnt                       = br.read_ue_lim(127)? as u8;
+    }
+    if hdr.slice_type.is_b() {
+        hdr.direct_spatial_mv_pred                  = br.read_bool()?;
+    }
+    if !hdr.slice_type.is_intra() {
+        hdr.num_ref_idx_active_override             = br.read_bool()?;
+        if hdr.num_ref_idx_active_override {
+            hdr.num_ref_idx_l0_active               = (br.read_ue_lim(15)? + 1) as usize;
+            if hdr.slice_type.is_b() {
+                hdr.num_ref_idx_l1_active           = (br.read_ue_lim(15)? + 1) as usize;
+            }
+        } else {
+            hdr.num_ref_idx_l0_active = pps.num_ref_idx_l0_active;
+            if hdr.slice_type.is_b() {
+                hdr.num_ref_idx_l1_active = pps.num_ref_idx_l1_active;
+            }
+        }
+    }
+    parse_ref_pic_list_reordering(&mut hdr, br)?;
+    if (pps.weighted_pred && hdr.slice_type.is_p()) ||
+        (pps.weighted_bipred_idc == 1 && hdr.slice_type.is_b()) {
+        parse_pred_weight_table(&mut hdr, br)?;
+    }
+    if nal_ref_idc != 0 {
+        if is_idr {
+            hdr.no_output_of_prior_pics             = br.read_bool()?;
+            hdr.long_term_reference                 = br.read_bool()?;
+        } else {
+            hdr.adaptive_ref_pic_marking_mode       = br.read_bool()?;
+            if hdr.adaptive_ref_pic_marking_mode {
+                let mark_info = &mut hdr.adaptive_ref_pic_marking;
+                loop {
+                    let memory_management_control_op = br.read_ue_lim(6)? as u8;
+                    if memory_management_control_op == 0 {
+                        break;
+                    }
+                    if mark_info.num_ops >= mark_info.memory_management_control_op.len() {
+                        return Err(DecoderError::NotImplemented);
+                    }
+                    mark_info.memory_management_control_op[mark_info.num_ops] = memory_management_control_op;
+                    mark_info.operation_arg[mark_info.num_ops] = match memory_management_control_op {
+                            1 | 3 => {
+                                let difference_of_pic_nums = br.read_ue()? + 1;
+                                difference_of_pic_nums as u16
+                            },
+                            2 => {
+                                let long_term_pic_num = br.read_ue_lim(65535)?;
+                                long_term_pic_num as u16
+                            },
+                            6 => {
+                                let long_term_frame_idx = br.read_ue_lim(65536)?;
+                                long_term_frame_idx as u16
+                            },
+                            4 => {
+                                let max_long_term_frame_idx_plus1 = br.read_ue()?;
+                                max_long_term_frame_idx_plus1 as u16
+                            },
+                            _ => 0,
+                        };
+                    mark_info.operation_arg2[mark_info.num_ops] = if memory_management_control_op == 3 {
+                            let long_term_frame_idx = br.read_ue_lim(65536)?;
+                            long_term_frame_idx as u16
+                        } else {
+                            0
+                        };
+                    mark_info.num_ops += 1;
+                }
+            }
+        }
+    }
+    if pps.entropy_coding_mode && !hdr.slice_type.is_intra() {
+        hdr.cabac_init_idc                          = br.read_ue_lim(2)? as u8;
+    }
+    hdr.slice_qp_delta                              = br.read_se()?;
+    let qp = i32::from(pps.pic_init_qp) + hdr.slice_qp_delta;
+    validate!(qp >= 0 && qp <= 51);
+    hdr.slice_qp = qp as u8;
+    if hdr.slice_type.is_s() {
+        if hdr.slice_type == SliceType::SP {
+            hdr.sp_for_switch                       = br.read_bool()?;
+        }
+        hdr.slice_qs_delta                          = br.read_se()?;
+        let qs = i32::from(pps.pic_init_qs) + hdr.slice_qs_delta;
+        validate!(qs >= 0 && qs <= 51);
+        hdr.slice_qs = qs as u8;
+    }
+    if pps.deblocking_filter_control_present {
+        hdr.disable_deblocking_filter_idc           = br.read_ue_lim(2)? as u8;
+        if hdr.disable_deblocking_filter_idc != 1 {
+            let val                                 = br.read_se()?;
+            validate!(val >= -6 && val <= 6);
+            hdr.slice_alpha_c0_offset = val as i8 * 2;
+            let val                                 = br.read_se()?;
+            validate!(val >= -6 && val <= 6);
+            hdr.slice_beta_offset = val as i8 * 2;
+        }
+    }
+    if pps.num_slice_groups > 1 && pps.slice_group_map_type >= 3 && pps.slice_group_map_type <= 5 {
+        hdr.slice_group_change_cycle                = br.read_ue()?;
+    }
+
+    Ok(hdr)
+}
+
+fn parse_ref_pic_list_reordering(hdr: &mut SliceHeader, br: &mut BitReader) -> DecoderResult<()> {
+    if !hdr.slice_type.is_intra() {
+        hdr.ref_pic_list_reordering_l0              = br.read_bool()?;
+        let reord_list = &mut hdr.reordering_list_l0;
+        reord_list.num_ops = 0;
+        if hdr.ref_pic_list_reordering_l0 {
+            loop {
+                let reordering_of_pic_nums_idc      = br.read_ue_lim(3)?;
+                if reordering_of_pic_nums_idc == 3 {
+                    break;
+                }
+                validate!(reord_list.num_ops < MAX_FRAMES);
+                reord_list.reordering_of_pic_nums_idc[reord_list.num_ops] = reordering_of_pic_nums_idc as u8;
+                if reordering_of_pic_nums_idc != 2 {
+                    let abs_diff_pic_num            = br.read_ue()? + 1;
+                    reord_list.abs_diff_or_num[reord_list.num_ops] = abs_diff_pic_num as u16;
+                } else {
+                    let long_term_pic_num           = br.read_ue()?;
+                    reord_list.abs_diff_or_num[reord_list.num_ops] = long_term_pic_num as u16;
+                }
+                reord_list.num_ops += 1;
+            }
+            validate!(reord_list.num_ops > 0);
+        }
+    }
+    if hdr.slice_type.is_b() {
+        hdr.ref_pic_list_reordering_l1              = br.read_bool()?;
+        let reord_list = &mut hdr.reordering_list_l1;
+        reord_list.num_ops = 0;
+        if hdr.ref_pic_list_reordering_l1 {
+            loop {
+                let reordering_of_pic_nums_idc      = br.read_ue_lim(3)?;
+                if reordering_of_pic_nums_idc == 3 {
+                    break;
+                }
+                validate!(reord_list.num_ops < MAX_FRAMES);
+                reord_list.reordering_of_pic_nums_idc[reord_list.num_ops] = reordering_of_pic_nums_idc as u8;
+                if reordering_of_pic_nums_idc != 2 {
+                    let abs_diff_pic_num            = br.read_ue()? + 1;
+                    reord_list.abs_diff_or_num[reord_list.num_ops] = abs_diff_pic_num as u16;
+                } else {
+                    let long_term_pic_num           = br.read_ue()?;
+                    reord_list.abs_diff_or_num[reord_list.num_ops] = long_term_pic_num as u16;
+                }
+                reord_list.num_ops += 1;
+            }
+            validate!(reord_list.num_ops > 0);
+        }
+    }
+    Ok(())
+}
+
+fn parse_pred_weight_table(hdr: &mut SliceHeader, br: &mut BitReader) -> DecoderResult<()> {
+    hdr.luma_log2_weight_denom                      = br.read_ue_lim(7)? as u8;
+    hdr.chroma_log2_weight_denom                    = br.read_ue_lim(7)? as u8;
+    for weight in hdr.weights_l0[..hdr.num_ref_idx_l0_active].iter_mut() {
+        weight.luma_weighted                        = br.read_bool()?;
+        if weight.luma_weighted {
+            let w                                   = br.read_se()?;
+            validate!(w >= -128 && w <= 127);
+            weight.luma_weight = w as i8;
+            let offset                              = br.read_se()?;
+            validate!(offset >= -128 && offset <= 127);
+            weight.luma_offset = offset as i8;
+        }
+        weight.chroma_weighted                      = br.read_bool()?;
+        if weight.chroma_weighted {
+            for i in 0..2 {
+                let w                               = br.read_se()?;
+                validate!(w >= -128 && w <= 127);
+                weight.chroma_weight[i] = w as i8;
+                let offset                          = br.read_se()?;
+                validate!(offset >= -128 && offset <= 127);
+                weight.chroma_offset[i] = offset as i8;
+            }
+        }
+    }
+    for weight in hdr.weights_l1[..hdr.num_ref_idx_l1_active].iter_mut() {
+        weight.luma_weighted                        = br.read_bool()?;
+        if weight.luma_weighted {
+            let w                                   = br.read_se()?;
+            validate!(w >= -128 && w <= 127);
+            weight.luma_weight = w as i8;
+            let offset                              = br.read_se()?;
+            validate!(offset >= -128 && offset <= 127);
+            weight.luma_offset = offset as i8;
+        }
+        weight.chroma_weighted                      = br.read_bool()?;
+        if weight.chroma_weighted {
+            for i in 0..2 {
+                let w                               = br.read_se()?;
+                validate!(w >= -128 && w <= 127);
+                weight.chroma_weight[i] = w as i8;
+                let offset                          = br.read_se()?;
+                validate!(offset >= -128 && offset <= 127);
+                weight.chroma_offset[i] = offset as i8;
+            }
+        }
+    }
+
+    Ok(())
+}
diff --git a/nihav-itu/src/codecs/h264/test/conformance.rs b/nihav-itu/src/codecs/h264/test/conformance.rs
new file mode 100644 (file)
index 0000000..5ad0cdc
--- /dev/null
@@ -0,0 +1,376 @@
+use nihav_core::codecs::RegisteredDecoders;
+use nihav_core::demuxers::RegisteredDemuxers;
+use nihav_codec_support::test::dec_video::*;
+use nihav_commonfmt::generic_register_all_demuxers;
+use crate::itu_register_all_decoders;
+
+use super::raw_demux::RawH264DemuxerCreator;
+
+const PREFIX: &str = "assets/ITU/h264-conformance/";
+
+fn test_files(names: &[(&str, [u32; 4])]) {
+    let mut dmx_reg = RegisteredDemuxers::new();
+    dmx_reg.add_demuxer(&RawH264DemuxerCreator{});
+    generic_register_all_demuxers(&mut dmx_reg);
+    let mut dec_reg = RegisteredDecoders::new();
+    itu_register_all_decoders(&mut dec_reg);
+
+    for (name, hash) in names.iter() {
+        let test_name = format!("{}{}", PREFIX, name);
+        println!("Testing {}", test_name);
+        test_decoding("rawh264", "h264", &test_name, None, &dmx_reg, &dec_reg, ExpectedTestResult::MD5(*hash));
+    }
+}
+
+const GENERAL_TEST_STREAMS: &[(&str, [u32; 4])] = &[
+    ("NL1_Sony_D.jsv", [0xD4BB8D98, 0x0C1377EE, 0x45515763, 0xAE7989FD]),
+    ("SVA_NL1_B.264", [0xB5626983, 0xAC087749, 0x7FFF9A4B, 0x10D2F1D4]),
+    ("NL2_Sony_H.jsv", [0x48D8380C, 0xDB7EFF52, 0x116C1AAD, 0xDBC583F5]),
+    ("SVA_NL2_E.264", [0xB47E932D, 0x43628801, 0x3B8453D9, 0xA1D0F60D]),
+    ("BA1_Sony_D.jsv", [0xFDE8F7A6, 0x34A7B7CE, 0xA7F1317D, 0xAAF5EB7C]),
+    ("SVA_BA1_B.264", [0x627288C4, 0xE4D4E7A6, 0xA13F187C, 0x4A7A9A4D]),
+    ("BA2_Sony_F.jsv", [0xB4C1B35F, 0xDC25B520, 0x5E842E64, 0x19C0E81A]),
+    ("SVA_BA2_D.264", [0x18B60729, 0x98CDA04B, 0x278B1436, 0x27FC9D4A]),
+    ("BA_MW_D.264", [0xC42C2D96, 0xC49254A6, 0xE980B174, 0xDB1CE2D8]),
+    ("BANM_MW_D.264", [0x6572ACB5, 0xE65EA0BC, 0x4A7ECBE7, 0xE436E654]),
+    ("BA1_FT_C.264", [0x355A737E, 0xE9FBDE6E, 0xAA47ACFD, 0xED7D2475]),
+    ("NLMQ1_JVC_C.264", [0xB5DE2480, 0xBD391286, 0x7FE69D65, 0x7AADDD6E]),
+    ("NLMQ2_JVC_C.264", [0x35635990, 0xBE9CB3E5, 0x1000CBB1, 0xC8322D5B]),
+    ("BAMQ1_JVC_C.264", [0x04B40C4A, 0xF5A4B4C0, 0x94D77821, 0x79D12A88]),
+    ("BAMQ2_JVC_C.264", [0xDAB08F3D, 0x5E304802, 0xC91AC830, 0x71BFB9DE]),
+    ("SVA_Base_B.264", [0x4B5BB06C, 0x8C698DA3, 0xABFAD6B9, 0xA28852D2]),
+    ("SVA_FM1_E.264", [0x5A20AF6C, 0xDBE9B632, 0x5D752096, 0xC587A7F1]),
+    ("BASQP1_Sony_C.jsv", [0xB49014B2, 0xDC04FE5A, 0x6138C083, 0x387A9A9B]),
+    /*"FM1_BT_B.h264",
+    "FM2_SVA_C.264",
+    "FM1_FT_E.264",*/ //special slice modes
+    ("CI_MW_D.264", [0x4571A884, 0xA6C7856F, 0x4377928C, 0x830246E3]),
+    ("SVA_CL1_E.264", [0x5723A151, 0x8DE9FADC, 0xA7499C5B, 0xA34DA7C4]),
+    ("CI1_FT_B.264", [0x411ECE62, 0xFDD3791E, 0xE3E90B82, 0x1B79CF77]),
+    ("CVFC1_Sony_C.jsv", [0x78E5AAA2, 0x48CC85CC, 0x68DD1D56, 0x535F6ED0]),
+    ("AUD_MW_E.264", [0xE96FE505, 0x4DE0329A, 0x8868D060, 0x03375CDB]),
+    ("MIDR_MW_D.264", [0x527E7207, 0x584DFE19, 0x3346316F, 0xCBAB1516]),
+    ("NRF_MW_E.264", [0x22F2011C, 0x44661F4D, 0xABBBD4A2, 0x423AB9B8]),
+    ("MPS_MW_A.264", [0x3159BB10, 0xD656899D, 0xD13D89E2, 0x44F6F5BD]),
+    ("CVBS3_Sony_C.jsv", [0xFF57F1A4, 0xD03A6599, 0x8CDC4EFE, 0x19DC4ADB]),
+    ("BA3_SVA_C.264", [0xe35fe99a, 0xd8ebef51, 0x017e2169, 0xe48e3ad5]),
+    ("SL1_SVA_B.264", [0x738E8AAD, 0x711E58FE, 0x76C5E366, 0x432BBB90]),
+    ("NL3_SVA_E.264", [0x428B0604, 0xFF02E0A0, 0x0DA08577, 0xDA0EEB76]),
+    ("cvmp_mot_frm0_full_B.26l", [0xb8baed20, 0x7e57efcb, 0x22ba5538, 0x849a573f]),
+    // no direct mention
+    //"FM2_SVA_B.264", //special slice mode
+];
+#[test]
+fn test_h264_general() {
+    test_files(GENERAL_TEST_STREAMS);
+}
+
+const I_PCM_TEST_STREAMS: &[(&str, [u32; 4])] = &[
+    ("CVPCMNL1_SVA_C.264", [0x5C1FD0F6, 0x8E875200, 0x711FEBF1, 0xD683E58F]),
+    ("CVPCMNL2_SVA_C.264", [0xAF1F1DBE, 0x1DD6569C, 0xB02271F0, 0x53217D88]),
+];
+#[test]
+fn test_h264_ipcm() {
+    test_files(I_PCM_TEST_STREAMS);
+}
+
+const MMCO_TEST_STREAMS: &[(&str, [u32; 4])] = &[
+    ("MR1_BT_A.h264", [0x6c9c7a22, 0x3c5d0f04, 0x73d7e777, 0x46d8e1c9]),
+    ("MR2_TANDBERG_E.264", [0x225aff94, 0x8c079867, 0xf7f0af24, 0xc4093834]),
+    ("MR3_TANDBERG_B.264", [0x49728ec3, 0x3d6247de, 0x72dd49ae, 0x22c11930]),
+    ("MR4_TANDBERG_C.264", [0x98aaed22, 0x5bf63437, 0x8209bc05, 0x58ad5782]),
+    ("MR5_TANDBERG_C.264", [0xa1c5e24a, 0xf96e4801, 0x2f2ac7f6, 0xb61e2779]),
+    ("MR1_MW_A.264", [0x6e6ba67d, 0x1829c4e1, 0x639e9ec3, 0x68b72208]),
+    ("MR2_MW_A.264", [0x08499fbb, 0x2a566e46, 0x72e5685e, 0xcacb802c]),
+    /*"MR6_BT_B.h264",
+    "MR7_BT_B.h264",
+    "MR8_BT_B.h264",*/ // interlaced coding
+    ("HCBP1_HHI_A.264", [0x69F61D9D, 0x050F777D, 0x894C3191, 0x76A33A13]),
+    ("HCBP2_HHI_A.264", [0xEAE95099, 0x1F6CD60B, 0xE2435713, 0x5E4661CA]),
+];
+#[test]
+fn test_h264_mmco() {
+    test_files(MMCO_TEST_STREAMS);
+}
+
+const WP_TEST_STREAMS: &[(&str, [u32; 4])] = &[
+    ("CVWP5_TOSHIBA_E.264", [0xB6C61135, 0x9A6F86DE, 0xC46445A3, 0x350A75B2]),
+    ("CVWP1_TOSHIBA_E.264", [0xA3F64FC4, 0xC18AA1A1, 0x622C6D25, 0x289930B2]),
+    ("CVWP2_TOSHIBA_E.264", [0x42c18fb8, 0x9062f091, 0xa06c9ac1, 0x00d3bc80]),
+    ("CVWP3_TOSHIBA_E.264", [0x76e164a1, 0x26ff7073, 0x655f1fe9, 0xac40a0fd]),
+];
+#[test]
+fn test_h264_wp() {
+    test_files(WP_TEST_STREAMS);
+}
+
+/*const FIELD_CODING_TEST_STREAMS: &[(&str, [u32; 4])] = &[
+    "CVNLFI1_Sony_C.jsv",
+    "CVNLFI2_Sony_H.jsv",
+    "Sharp_MP_Field_1_B.jvt",
+    "Sharp_MP_Field_2_B.jvt",
+    "Sharp_MP_Field_3_B.jvt",
+    "CVFI1_Sony_D.jsv",
+    "CVFI2_Sony_H.jsv",
+    "FI1_Sony_E.jsv",
+    "CVFI1_SVA_C.264",
+    "CVFI2_SVA_C.264",
+    "cvmp_mot_fld0_full_B.26l",
+    "CVMP_MOT_FLD_L30_B.26l",
+];
+#[test]
+fn test_h264_field() {
+    test_files(FIELD_CODING_TEST_STREAMS);
+}*/
+
+/*const FRAME_FIELD_CODING_TEST_STREAMS: &[(&str, [u32; 4])] = &[
+    "Sharp_MP_PAFF_1r2.jvt",
+    "CVPA1_TOSHIBA_B.264",
+    "cvmp_mot_picaff0_full_B.26l",
+];
+#[test]
+fn test_h264_frame_field() {
+    test_files(FRAME_FIELD_CODING_TEST_STREAMS);
+}*/
+
+/*const MBAFF_TEST_STREAMS: &[(&str, [u32; 4])] = &[
+    "CVMANL1_TOSHIBA_B.264",
+    "CVMANL2_TOSHIBA_B.264",
+    "CVMA1_Sony_D.jsv",
+    "CVMA1_TOSHIBA_B.264",
+    "CVMAQP2_Sony_G.jsv",
+    "CVMAQP3_Sony_D.jsv",
+    "CVMAPAQP3_Sony_E.jsv",
+    "cvmp_mot_mbaff0_full_B.26l",
+    "CVMP_MOT_FRM_L31_B.26l",
+];
+#[test]
+fn test_h264_mbaff() {
+    test_files(MBAFF_CODING_TEST_STREAMS);
+}*/
+
+/*const S_PICTURE_TEST_STREAMS: &[(&str, [u32; 4])] = &[
+    "sp1_bt_a.h264",
+    "sp2_bt_b.h264",
+];
+#[test]
+fn test_h264_s_picture() {
+    test_files(S_PICTURE_TEST_STREAMS);
+}*/
+
+const LONG_SEQUENCE_TEST_STREAMS: &[(&str, [u32; 4])] = &[
+    ("LS_SVA_D.264", [0xA1F4C1CC, 0x701AF32F, 0x985CDE87, 0xA0785B4D]),
+];
+#[test]
+fn test_h264_long_sequence() {
+    test_files(LONG_SEQUENCE_TEST_STREAMS);
+}
+
+const SEI_VUI_TEST_STREAMS: &[(&str, [u32; 4])] = &[
+    ("CVSE2_Sony_B.jsv", [0xDC811E9A, 0xD11D06A0, 0x00F55FF3, 0x2179433E]),
+    ("CVSE3_Sony_H.jsv", [0x30CCF52E, 0x2B0DCE8F, 0x98384A84, 0x51BD4F89]),
+    ("CVSEFDFT3_Sony_E.jsv", [0x1EA2228B, 0xBDD88D50, 0x95C452C4, 0xC75A5229]),
+];
+#[test]
+fn test_h264_sei_vui() {
+    test_files(SEI_VUI_TEST_STREAMS);
+}
+
+const CABAC_TEST_STREAMS: &[(&str, [u32; 4])] = &[
+    ("CANL1_TOSHIBA_G.264", [0xAFA07274, 0x6B16BD96, 0xF3152B45, 0xE2F2881E]),
+    ("CANL1_Sony_E.jsv", [0x27F1D5D3, 0x89E110FC, 0x320788BF, 0x78006DB0]),
+    ("CANL2_Sony_E.jsv", [0x3A28438E, 0x3E0795DE, 0xAED795FC, 0xFEFBC833]),
+    ("CANL3_Sony_C.jsv", [0xFE2DC3CB, 0xA055044C, 0x739911B0, 0xE6AA66BA]),
+    ("CANL1_SVA_B.264", [0xB02DEFCB, 0x741C0E98, 0x2313C574, 0x9F2008ED]),
+    ("CANL2_SVA_B.264", [0xB02DEFCB, 0x741C0E98, 0x2313C574, 0x9F2008ED]),
+    ("CANL3_SVA_B.264", [0x04A6DE98, 0x4EF88D1B, 0x8C1B26FC, 0x8F33A425]),
+    ("CANL4_SVA_B.264", [0x19cee0ac, 0xcfbebacc, 0x57aa4cf0, 0x3e4ef26d]),
+    ("CABA1_Sony_D.jsv", [0x5EB23E95, 0xD9908DBD, 0x68AAA5BF, 0x775071DE]),
+    ("CABA2_Sony_E.jsv", [0xB60EE63C, 0xB7A969DA, 0x88C9120D, 0xEB6752F6]),
+    ("CABA3_Sony_C.jsv", [0xC74CA8A2, 0x509C153C, 0xFE7ABF23, 0xABF8F8F0]),
+    ("CABA3_TOSHIBA_E.264", [0xC559BBDC, 0x2939EBD9, 0xD09CAA95, 0x63DF81DD]),
+    ("CABA1_SVA_B.264", [0x466A59AE, 0x3968AADD, 0x529FEDFB, 0x87539141]),
+    ("CABA2_SVA_B.264", [0xEF495A1D, 0x8F02E1E7, 0xCA128ACC, 0xC4086CFE]),
+    ("CABA3_SVA_B.264", [0x09F84428, 0xE29B6602, 0x87EF56CF, 0x6093B54F]),
+    ("camp_mot_frm0_full.26l", [0x0CA9541B, 0xCEF163D0, 0x75FC5817, 0x45132421]),
+];
+#[test]
+fn test_h264_cabac() {
+    test_files(CABAC_TEST_STREAMS);
+}
+
+const CABAC_INIT_TEST_STREAMS: &[(&str, [u32; 4])] = &[
+    ("CABACI3_Sony_B.jsv", [0xD74CBB99, 0x81FE3018, 0x0F4A15CD, 0x4C9B490D]),
+];
+#[test]
+fn test_h264_cabac_init() {
+    test_files(CABAC_INIT_TEST_STREAMS);
+}
+
+const CABAC_MB_QPTEST_STREAMS: &[(&str, [u32; 4])] = &[
+    ("CAQP1_Sony_B.jsv", [0x3C60A84F, 0xA2A2F0CB, 0x6FEB91AE, 0xD97E36C5]),
+    ("CACQP3_Sony_D.jsv", [0x296FAD20, 0x0369FF53, 0x042FE3A3, 0xDE6BB6C3]),
+];
+#[test]
+fn test_h264_cabac_mb_qp() {
+    test_files(CABAC_MB_QPTEST_STREAMS);
+}
+
+const CABAC_SLICE_TEST_STREAMS: &[(&str, [u32; 4])] = &[
+    ("CABAST3_Sony_E.jsv", [0xF97789B5, 0x85A499DF, 0xAED8B05F, 0xA5024D66]),
+    ("CABASTBR3_Sony_B.jsv", [0xbc738fb9, 0x946298c0, 0x3f3f894e, 0x3a10c6bc]),
+];
+#[test]
+fn test_h264_cabac_slice() {
+    test_files(CABAC_SLICE_TEST_STREAMS);
+}
+
+const CABAC_I_PCM_TEST_STREAMS: &[(&str, [u32; 4])] = &[
+    ("CAPCMNL1_Sand_E.264", [0xEE9968EE, 0xEFE935F0, 0x45C6B70B, 0xE51691EB]),
+    ("CAPCM1_Sand_E.264", [0x318680B7, 0x85FA5499, 0xB4C4B2A4, 0xD43AA656]),
+    ("CAPM3_Sony_D.jsv", [0xB515EEDB, 0xF1E4C5A6, 0xD217B1C8, 0xFBEC1DB9]),
+];
+#[test]
+fn test_h264_cabac_ipcm() {
+    test_files(CABAC_I_PCM_TEST_STREAMS);
+}
+
+const CABAC_MMCO_TEST_STREAMS: &[(&str, [u32; 4])] = &[
+    /*"MR9_BT_B.h264",*/ //MBAFF
+    ("HCMP1_HHI_A.264", [0x4ec3788f, 0x2bec7e4c, 0xade27eee, 0xda17b05d]),
+];
+#[test]
+fn test_h264_cabac_mmco() {
+    test_files(CABAC_MMCO_TEST_STREAMS);
+}
+
+const CABAC_WP_TEST_STREAMS: &[(&str, [u32; 4])] = &[
+    ("CAWP1_TOSHIBA_E.264", [0x305937E4, 0x30B50003, 0xDEC317BD, 0x3A0CDB9C]),
+    ("CAWP5_TOSHIBA_E.264", [0xB6C61135, 0x9A6F86DE, 0xC46445A3, 0x350A75B2]),
+];
+#[test]
+fn test_h264_cabac_wp() {
+    test_files(CABAC_WP_TEST_STREAMS);
+}
+
+/*const CABAC_FIELD_TEST_STREAMS: &[(&str, [u32; 4])] = &[
+    "CABREF3_Sand_D.264",
+    "CAFI1_SVA_C.264",
+    "camp_mot_fld0_full.26l",
+];
+#[test]
+fn test_h264_cabac_field_() {
+    test_files(CABAC_FIELD_TEST_STREAMS);
+}*/
+
+/*const CABAC_FIELD_FRAME_TEST_STREAMS: &[(&str, [u32; 4])] = &[
+    "Sharp_MP_PAFF_2.jvt",
+    "CAPA1_TOSHIBA_B.264",
+    "camp_mot_picaff0_full.26l",
+];
+#[test]
+fn test_h264_cabac_field_frame() {
+    test_files(CABAC_FIELD_FRAMETEST_STREAMS);
+}*/
+
+/*const CABAC_MBAFF_TEST_STREAMS: &[(&str, [u32; 4])] = &[
+    "CAMANL1_TOSHIBA_B.264",
+    "CAMANL2_TOSHIBA_B.264",
+    "CANLMA2_Sony_C.jsv",
+    "CANLMA3_Sony_C.jsv",
+    "CAMA1_Sony_C.jsv",
+    "CAMA1_TOSHIBA_B.264",
+    "CAMANL3_Sand_E.264",
+    "CAMA3_Sand_E.264",
+    "CAMASL3_Sony_B.jsv",
+    "CAMACI3_Sony_C.jsv",
+    "camp_mot_mbaff0_full.26l",
+    "CAMP_MOT_MBAFF_L30.26l",
+    "CAMP_MOT_MBAFF_L31.26l",
+    "CAPAMA3_Sand_F.264",
+    "cama1_vtc_c.avc",
+    "cama2_vtc_b.avc",
+    "cama3_vtc_b.avc",
+];
+#[test]
+fn test_h264_cabac_mbaff() {
+    test_files(CABAC_MBAFF_TEST_STREAMS);
+}*/
+
+/*const CABAC_CAVLC_TEST_STREAMS: &[(&str, [u32; 4])] = &[
+    "CVCANLMA2_Sony_C.jsv",
+];
+#[test]
+fn test_h264_cabac_cavlc() {
+    test_files(CABAC_CAVLC_TEST_STREAMS);
+}*/ // contains MBAFF
+
+const CABAC_PRED_BW_TEST_STREAMS: &[(&str, [u32; 4])] = &[
+    ("src19td.IBP.264", [0xEE7F2F8E, 0x722B297A, 0x532DFA94, 0xDEE55779]),
+];
+#[test]
+fn test_h264_cabac_pred_bw() {
+    test_files(CABAC_PRED_BW_TEST_STREAMS);
+}
+
+const FREXT_420_8_TEST_STREAMS: &[(&str, [u32; 4])] = &[
+    ("FRext/FRExt1_Panasonic.avc", [0xBD8EA0B1, 0x9668C25E, 0xFBB50D85, 0xABAFFE7C]),
+    ("FRext/FRExt3_Panasonic.avc", [0x39772F7C, 0xC227DCE3, 0x80732096, 0xEB970937]),
+    ("FRext/HCAFR1_HHI.264", [0x4C1C4214, 0x7190D5B8, 0x6650E6B9, 0xD86BCB03]),
+    //("FRext/HCAFF1_HHI.264", [0;4]), //PAFF
+    //("FRext/HCAMFF1_HHI.264", [0;4]), //MBAFF
+    //("FRext/FRExt2_Panasonic.avc", [0;4]), //PAFF
+    //("FRext/FRExt4_Panasonic.avc", [0;4]), //MBAFF
+    ("FRext/HPCANL_BRCM_C.264", [0x24BB8150, 0xC03A9FBC, 0x304A427C, 0x5C11B5D7]),
+    ("FRext/HPCA_BRCM_C.264", [0x46AF80A6, 0x8CAA5AD0, 0x42F65E88, 0x0EEE65E4]),
+    /*("FRext/HPCAFLNL_BRCM_C.264", [0;4]), //PAFF
+    ("FRext/HPCAFL_BRCM_C.264", [0;4]),*/
+    ("FRext/HCAFR2_HHI.264", [0x79CC14EA, 0xBD39DDFF, 0x82D49538, 0xF3D9AE1A]),
+    ("FRext/HCAFR3_HHI.264", [0x280AF93D, 0x551539E1, 0xA3F1979D, 0xC1CF64DF]),
+    ("FRext/HCAFR4_HHI.264", [0x6E80B189, 0xAAE83055, 0x6F51F4EE, 0xC3BEE5C8]),
+    ("FRext/HPCADQ_BRCM_B.264", [0xCAB10745, 0xB7CB657A, 0xB51600CE, 0x7C7E7A19]),
+    ("FRext/HPCALQ_BRCM_B.264", [0xCAB10745, 0xB7CB657A, 0xB51600CE, 0x7C7E7A19]),
+    //("FRext/HPCAMAPALQ_BRCM_B.264", [0;4]), //MBAFF
+    ("FRext/HPCV_BRCM_A.264", [0x9B2D963E, 0x953DE431, 0x8A4385F8, 0x41D7C42C]),
+    ("FRext/HPCVNL_BRCM_A.264", [0x45E2D980, 0xFAB71BA7, 0xC2DFD63B, 0x80AC89E7]),
+    /*("FRext/HPCVFL_BRCM_A.264", [0;4]), //PAFF
+    ("FRext/HPCVFLNL_BRCM_A.264", [0;4]),*/
+    //("FRext/HPCVMOLQ_BRCM_B.264", [0;4]), //grayscale
+    //("FRext/HPCAMOLQ_BRCM_B.264", [0;4]), //grayscale
+    ("FRext/HPCAQ2LQ_BRCM_B.264", [0x04101005, 0x61E5ED27, 0xBBD135FF, 0x7E35F162]),
+    ("FRext/Freh1_B.264", [0xC9FB3A23, 0x59564945, 0x659E23DB, 0x2D61DE13]),
+    ("FRext/Freh2_B.264", [0x3E1853A5, 0x7B36CA1A, 0xDEDA7FB6, 0xFF60A2E7]),
+    ("FRext/freh3.264", [0x482BA0B8, 0x388252D8, 0x0B7095C9, 0x07D32939]),
+    //("FRext/freh4.264", [0;4]), //PAFF
+    //("FRext/freh5.264", [0;4]), //MBAFF
+    //("FRext/freh6.264", [0;4]), //PAFF
+    //("FRext/Freh7_B.264", [0;4]), //PAFF
+    ("FRext/freh8.264", [0xFC3BC8E0, 0xF6728372, 0x448C0E26, 0xE7472E6F]),
+    ("FRext/freh9.264", [0xA118CCC1, 0xBDFFDFF0, 0xAD0FD32F, 0x9A3821A3]),
+    //("FRext/freh10.264", [0;4]), //PAFF
+    //("FRext/freh11.264", [0;4]), //PAFF
+    ("FRext/Freh12_B.264", [0xE474287F, 0xCB9CCD28, 0xFD24CD02, 0x02E97603]),
+    /*("FRext/FREXT01_JVC_D.264", [0;4]), //MBAFF
+    ("FRext/FREXT02_JVC_C.264", [0;4]),*/
+    ("FRext/FRExt_MMCO4_Sony_B.264", [0x3B226B30, 0x42AC899B, 0x9FE1EB2C, 0x4B6ED90C]),
+
+    ("FRext/test8b43.264", [0x81A43E33, 0x6811D40D, 0x2DEAAC38, 0xBCC4F535]),
+];
+#[test]
+fn test_h264_frext_420_8() {
+    test_files(FREXT_420_8_TEST_STREAMS);
+}
+
+/*const FREXT_420_10I_TEST_STREAMS: &[(&str, [u32; 4])] = &[
+    "FRext/PPH10I1_Panasonic_A.264",
+    "FRext/PPH10I2_Panasonic_A.264",
+    "FRext/PPH10I3_Panasonic_A.264",
+    "FRext/PPH10I4_Panasonic_A.264",
+    "FRext/PPH10I5_Panasonic_A.264",
+    "FRext/PPH10I6_Panasonic_A.264",
+    "FRext/PPH10I7_Panasonic_A.264",
+];
+#[test]
+fn test_h264_frext_420_10i() {
+    test_files(FREXT_420_10I_TEST_STREAMS);
+}*/
diff --git a/nihav-itu/src/codecs/h264/test/raw_demux.rs b/nihav-itu/src/codecs/h264/test/raw_demux.rs
new file mode 100644 (file)
index 0000000..3bdbd90
--- /dev/null
@@ -0,0 +1,206 @@
+use nihav_core::frame::*;
+use nihav_core::io::byteio::*;
+use nihav_core::demuxers::*;
+use nihav_core::formats::YUV420_FORMAT;
+
+struct RawH264Demuxer<'a> {
+    src:            &'a mut ByteReader<'a>,
+    cur_frame:      u64,
+    frame_buf:      Vec<u8>,
+}
+
+fn read_nal(src: &mut ByteReader, dst: &mut Vec<u8>) -> DemuxerResult<()> {
+    dst.truncate(0);
+    loop {
+        let b                           = src.read_byte()?;
+        if b == 0 {
+            let b2                      = src.read_byte()?;
+            if b2 == 0 {
+                let b3                  = src.read_byte()?;
+                if b3 == 0 {
+                    while src.read_byte()? != 1 { }
+                    break;
+                } else if b3 == 1 {
+                    break;
+                } else {
+                    dst.push(b);
+                    dst.push(b2);
+                    dst.push(b3);
+                }
+            } else {
+                dst.push(b);
+                dst.push(b2);
+            }
+        } else {
+            dst.push(b);
+        }
+    }
+    Ok(())
+}
+
+fn put_nal(dst: &mut Vec<u8>, src: &[u8]) {
+    let len = src.len();
+    dst.push((len >> 24) as u8);
+    dst.push((len >> 16) as u8);
+    dst.push((len >>  8) as u8);
+    dst.push( len        as u8);
+    dst.extend_from_slice(src);
+}
+
+impl<'a> DemuxCore<'a> for RawH264Demuxer<'a> {
+    #[allow(clippy::unreadable_literal)]
+    fn open(&mut self, strmgr: &mut StreamManager, _seek_idx: &mut SeekIndex) -> DemuxerResult<()> {
+        let src = &mut self.src;
+
+        while src.read_byte()? != 1 { }
+
+        let mut edata: Vec<u8> = Vec::with_capacity(64);
+        let mut num_sps = 0;
+        let mut sps_buf = Vec::new();
+        let mut num_pps = 0;
+        let mut pps_buf = Vec::new();
+        let mut profile = 0;
+        let mut level = 0;
+
+        let mut nal_buf = Vec::with_capacity(65536);
+        loop {
+            read_nal(src, &mut nal_buf)?;
+            if !nal_buf.is_empty() {
+                let nal_type = nal_buf[0] & 0x1F;
+                match nal_type {
+                    7 => {
+                        if nal_buf.len() < 4 {
+                            return Err(DemuxerError::InvalidData);
+                        }
+                        profile = nal_buf[1];
+                        level   = nal_buf[3];
+                        sps_buf.push((nal_buf.len() >> 8) as u8);
+                        sps_buf.push(nal_buf.len() as u8);
+                        sps_buf.extend_from_slice(&nal_buf);
+                        num_sps += 1;
+                    },
+                    8 => {
+                        pps_buf.push((nal_buf.len() >> 8) as u8);
+                        pps_buf.push(nal_buf.len() as u8);
+                        pps_buf.extend_from_slice(&nal_buf);
+                        num_pps += 1;
+                    },
+                    1 | 5 => {
+                        self.frame_buf = nal_buf;
+                        break;
+                    },
+                    _ => {},
+                };
+            }
+        }
+        if num_sps == 0 || num_pps == 0 {
+            return Err(DemuxerError::InvalidData);
+        }
+        edata.extend_from_slice(b"avcC");
+        edata.push(1);
+        edata.push(profile);
+        edata.push(0);
+        edata.push(level);
+        edata.push(0xFF);
+        edata.push(0xE0 | num_sps);
+        edata.extend_from_slice(&sps_buf);
+        edata.push(num_pps);
+        edata.extend_from_slice(&pps_buf);
+
+        let width = 16;
+        let height = 16;
+
+        let vhdr = NAVideoInfo::new(width, height, false, YUV420_FORMAT);
+        let vinfo = NACodecInfo::new("h264", NACodecTypeInfo::Video(vhdr), Some(edata));
+        if let None = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 1, 25, 0)) {
+            return Err(DemuxerError::InvalidData);
+        }
+        self.cur_frame = 0;
+
+        Ok(())
+    }
+    fn get_frame(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<NAPacket> {
+        let ts = NATimeInfo::new(Some(self.cur_frame), None, None, 1, 25);
+        let mut buf: Vec<u8> = Vec::with_capacity(65536);
+        if !self.frame_buf.is_empty() {
+            put_nal(&mut buf, &self.frame_buf);
+            self.frame_buf.truncate(0);
+        }
+        let strres = strmgr.get_stream(0);
+        if strres.is_none() {
+            return Err(DemuxerError::InvalidData);
+        }
+        let stream = strres.unwrap();
+
+        let mut keyframe = false;
+
+        let mut nal_buf = Vec::with_capacity(65536);
+        let mut is_eof = false;
+        while !is_eof {
+            match read_nal(&mut self.src, &mut nal_buf) {
+                Ok(()) => {},
+                Err(DemuxerError::IOError) => { is_eof = true; },
+                Err(err) => return Err(err),
+            };
+            if !nal_buf.is_empty() {
+                let nal_type = nal_buf[0] & 0x1F;
+                keyframe = nal_type == 5;
+                match nal_type {
+                    1 | 5 => {
+                        let first_slice = (nal_buf[1] & 0x80) != 0;
+                        if first_slice && !buf.is_empty() {
+                            self.frame_buf.extend_from_slice(&nal_buf);
+                            break;
+                        } else {
+                            put_nal(&mut buf, &nal_buf);
+                        }
+                    },
+                    _ => {
+                        //println!("non-slice NAL {} @ {:X}", nal_type, self.src.tell());
+                        put_nal(&mut buf, &nal_buf);
+                    },
+                };
+            }
+        }
+
+        if is_eof && buf.is_empty() {
+            return Err(DemuxerError::EOF);
+        }
+
+        let pkt = NAPacket::new(stream, ts, keyframe, buf);
+
+        self.cur_frame += 1;
+
+        Ok(pkt)
+    }
+    fn seek(&mut self, _time: NATimePoint, _seek_idx: &SeekIndex) -> DemuxerResult<()> {
+        Err(DemuxerError::NotImplemented)
+    }
+    fn get_duration(&self) -> u64 { 0 }
+}
+
+impl<'a> NAOptionHandler for RawH264Demuxer<'a> {
+    fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
+    fn set_options(&mut self, _options: &[NAOption]) { }
+    fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
+}
+
+impl<'a> RawH264Demuxer<'a> {
+    fn new(io: &'a mut ByteReader<'a>) -> Self {
+        RawH264Demuxer {
+            src:            io,
+            cur_frame:      0,
+            frame_buf:      Vec::new(),
+        }
+    }
+}
+
+pub struct RawH264DemuxerCreator { }
+
+impl DemuxerCreator for RawH264DemuxerCreator {
+    fn new_demuxer<'a>(&self, br: &'a mut ByteReader<'a>) -> Box<dyn DemuxCore<'a> + 'a> {
+        Box::new(RawH264Demuxer::new(br))
+    }
+    fn get_name(&self) -> &'static str { "rawh264" }
+}
+
diff --git a/nihav-itu/src/codecs/h264/types.rs b/nihav-itu/src/codecs/h264/types.rs
new file mode 100644 (file)
index 0000000..5adeab1
--- /dev/null
@@ -0,0 +1,880 @@
+use nihav_codec_support::codecs::{MV, ZERO_MV};
+use nihav_codec_support::data::GenericCache;
+use super::FrameRefs;
+
+#[repr(u8)]
+#[derive(Clone,Copy,Debug,PartialEq)]
+pub enum BMode {
+    L0,
+    L1,
+    Bi,
+}
+
+#[derive(Clone,Copy,Debug,PartialEq)]
+pub enum MBType {
+    Intra4x4,
+    Intra8x8,
+    Intra16x16(u8, u8, u8),
+    PCM,
+
+    P16x16,
+    P16x8,
+    P8x16,
+    P8x8,
+    P8x8Ref0,
+    PSkip,
+
+    Direct,
+    B16x16(BMode),
+    B16x8(BMode, BMode),
+    B8x16(BMode, BMode),
+    B8x8,
+    BSkip,
+}
+
+impl MBType {
+    pub fn is_intra(self) -> bool {
+        match self {
+            MBType::Intra4x4 | MBType::Intra8x8 | MBType::Intra16x16(_, _, _) | MBType::PCM => true,
+            _ => false,
+        }
+    }
+    pub fn is_intra16x16(self) -> bool {
+        if let MBType::Intra16x16(_, _, _) = self {
+            true
+        } else {
+            false
+        }
+    }
+    pub fn is_skip(self) -> bool {
+        match self {
+            MBType::PSkip | MBType::BSkip => true,
+            _ => false,
+        }
+    }
+    pub fn is_4x4(self) -> bool { self.num_parts() == 4 }
+    pub fn is_l0(self, part: usize) -> bool {
+        match self {
+            MBType::B16x16(mode) => mode == BMode::L0,
+            MBType::B16x8(mode0, mode1) | MBType::B8x16(mode0, mode1) => {
+                if part == 0 {
+                    mode0 == BMode::L0
+                } else {
+                    mode1 == BMode::L0
+                }
+            },
+            MBType::Direct | MBType::BSkip => false,
+            _ => true,
+        }
+    }
+    pub fn is_l1(self, part: usize) -> bool {
+        match self {
+            MBType::B16x16(mode) => mode == BMode::L1,
+            MBType::B16x8(mode0, mode1) | MBType::B8x16(mode0, mode1) => {
+                if part == 0 {
+                    mode0 == BMode::L1
+                } else {
+                    mode1 == BMode::L1
+                }
+            },
+            _ => false,
+        }
+    }
+    pub fn num_parts(self) -> usize {
+        match self {
+            MBType::Intra4x4 | MBType::Intra8x8 | MBType::Intra16x16(_, _, _) | MBType::PCM |
+            MBType::PSkip |
+            MBType::Direct | MBType::BSkip
+                => 1,
+            MBType::P16x16 |
+            MBType::B16x16(_)
+                => 1,
+            MBType::P16x8 | MBType::P8x16 |
+            MBType::B16x8(_, _) | MBType::B8x16(_, _)
+                => 2,
+            _ => 4,
+        }
+    }
+    pub fn size(self) -> (usize, usize) {
+        match self {
+            MBType::Intra4x4 |
+            MBType::Intra8x8 |
+            MBType::Intra16x16(_, _, _) |
+            MBType::PCM |
+            MBType::P16x16 |
+            MBType::PSkip |
+            MBType::Direct |
+            MBType::B16x16(_) |
+            MBType::BSkip
+                => (16, 16),
+            MBType::P16x8 | MBType::B16x8(_, _) => (16, 8),
+            MBType::P8x16 | MBType::B8x16(_, _) => (8, 16),
+            _ => (8, 8),
+        }
+    }
+}
+
+impl Default for MBType {
+    fn default() -> Self { MBType::Intra4x4 }
+}
+
+#[derive(Clone,Copy,Debug,PartialEq)]
+pub enum SubMBType {
+    P8x8,
+    P8x4,
+    P4x8,
+    P4x4,
+    Direct8x8,
+    B8x8(BMode),
+    B8x4(BMode),
+    B4x8(BMode),
+    B4x4(BMode),
+}
+
+impl SubMBType {
+    pub fn num_parts(self) -> usize {
+        match self {
+            SubMBType::P8x8 | SubMBType::Direct8x8 | SubMBType::B8x8(_) => 1,
+            SubMBType::P4x4 | SubMBType::B4x4(_) => 4,
+            _ => 2,
+        }
+    }
+    pub fn size(self) -> (usize, usize) {
+        match self {
+            SubMBType::P8x8 | SubMBType::Direct8x8 | SubMBType::B8x8(_) => (8, 8),
+            SubMBType::P8x4 | SubMBType::B8x4(_) => (8, 4),
+            SubMBType::P4x8 | SubMBType::B4x8(_) => (4, 8),
+            SubMBType::P4x4 | SubMBType::B4x4(_) => (4, 4),
+        }
+    }
+    pub fn is_l0(self) -> bool {
+        match self {
+            SubMBType::B8x8(mode) | SubMBType::B8x4(mode) |
+            SubMBType::B4x8(mode) | SubMBType::B4x4(mode) => {
+                mode == BMode::L0
+            },
+            _ => true,
+        }
+    }
+    pub fn is_l1(self) -> bool {
+        match self {
+            SubMBType::B8x8(mode) | SubMBType::B8x4(mode) |
+            SubMBType::B4x8(mode) | SubMBType::B4x4(mode) => {
+                mode == BMode::L1
+            },
+            _ => false,
+        }
+    }
+}
+
+impl Default for SubMBType {
+    fn default() -> Self { SubMBType::Direct8x8 }
+}
+
+#[repr(u8)]
+#[derive(Clone,Copy,Debug,PartialEq)]
+pub enum CompactMBType {
+    Intra4x4,
+    Intra8x8,
+    Intra16x16,
+    PCM,
+
+    P16x16,
+    P16x8,
+    P8x16,
+    P8x8,
+    P8x8Ref0,
+    PSkip,
+
+    Direct,
+    B16x16,
+    B16x8,
+    B8x16,
+    B8x8,
+    BSkip,
+
+    None,
+}
+
+impl CompactMBType {
+    pub fn is_intra(self) -> bool {
+        match self {
+            CompactMBType::Intra4x4 | CompactMBType::Intra8x8 | CompactMBType::Intra16x16 => true,
+            _ => false,
+        }
+    }
+    pub fn is_intra16orpcm(self) -> bool {
+        match self {
+            CompactMBType::Intra16x16 | CompactMBType::PCM => true,
+            _ => false,
+        }
+    }
+    pub fn is_skip(self) -> bool {
+        match self {
+            CompactMBType::PSkip | CompactMBType::BSkip => true,
+            _ => false,
+        }
+    }
+    pub fn is_direct(self) -> bool {
+        match self {
+            CompactMBType::BSkip | CompactMBType::Direct | CompactMBType::None => true,
+            _ => false,
+        }
+    }
+    pub fn is_inter(self) -> bool {
+        !self.is_intra() && !self.is_skip() && self != CompactMBType::PCM
+    }
+    pub fn is_16x16(self) -> bool {
+        match self {
+            CompactMBType::P16x8 | CompactMBType::P8x16 |
+            CompactMBType::P8x8 | CompactMBType::P8x8Ref0 |
+            CompactMBType::B16x8 | CompactMBType::B8x16 |
+            CompactMBType::B8x8 => false,
+            _ => true,
+        }
+    }
+}
+
+impl Default for CompactMBType {
+    fn default() -> Self { CompactMBType::None }
+}
+
+impl From<MBType> for CompactMBType {
+    fn from(mbtype: MBType) -> Self {
+        match mbtype {
+            MBType::Intra4x4 => CompactMBType::Intra4x4,
+            MBType::Intra8x8 => CompactMBType::Intra8x8,
+            MBType::Intra16x16(_, _, _) => CompactMBType::Intra16x16,
+            MBType::PCM => CompactMBType::PCM,
+            MBType::P16x16 => CompactMBType::P16x16,
+            MBType::P16x8 => CompactMBType::P16x8,
+            MBType::P8x16 => CompactMBType::P8x16,
+            MBType::P8x8 => CompactMBType::P8x8,
+            MBType::P8x8Ref0 => CompactMBType::P8x8Ref0,
+            MBType::PSkip => CompactMBType::PSkip,
+            MBType::Direct => CompactMBType::Direct,
+            MBType::B16x16(_) => CompactMBType::B16x16,
+            MBType::B16x8(_, _) => CompactMBType::B16x8,
+            MBType::B8x16(_, _) => CompactMBType::B8x16,
+            MBType::B8x8 => CompactMBType::B8x8,
+            MBType::BSkip => CompactMBType::BSkip,
+        }
+    }
+}
+
+#[repr(u8)]
+#[derive(Clone,Copy,Debug,PartialEq)]
+pub enum IntraPredMode {
+    Vertical,
+    Horizontal,
+    DC,
+    DiagDownLeft,
+    DiagDownRight,
+    VerRight,
+    HorDown,
+    VerLeft,
+    HorUp,
+    None,
+}
+
+impl IntraPredMode {
+    pub fn is_none(self) -> bool { self == IntraPredMode::None }
+    pub fn into_pred_idx(self) -> i8 {
+        if !self.is_none() {
+            self as u8 as i8
+        } else {
+            -1
+        }
+    }
+}
+
+impl Default for IntraPredMode {
+    fn default() -> Self { IntraPredMode::None }
+}
+
+impl From<u8> for IntraPredMode {
+    fn from(val: u8) -> Self {
+        match val {
+            0 => IntraPredMode::Vertical,
+            1 => IntraPredMode::Horizontal,
+            2 => IntraPredMode::DC,
+            3 => IntraPredMode::DiagDownLeft,
+            4 => IntraPredMode::DiagDownRight,
+            5 => IntraPredMode::VerRight,
+            6 => IntraPredMode::HorDown,
+            7 => IntraPredMode::VerLeft,
+            8 => IntraPredMode::HorUp,
+            _ => IntraPredMode::None,
+        }
+    }
+}
+
+impl Into<u8> for IntraPredMode {
+    fn into(self) -> u8 {
+        match self {
+            IntraPredMode::Vertical      => 0,
+            IntraPredMode::Horizontal    => 1,
+            IntraPredMode::DC            => 2,
+            IntraPredMode::DiagDownLeft  => 3,
+            IntraPredMode::DiagDownRight => 4,
+            IntraPredMode::VerRight      => 5,
+            IntraPredMode::HorDown       => 6,
+            IntraPredMode::VerLeft       => 7,
+            IntraPredMode::HorUp         => 8,
+            _ => 9,
+        }
+    }
+}
+
+pub const MISSING_POC: u16 = 0xFFFF;
+
+#[derive(Clone,Copy,Debug)]
+pub struct PicRef {
+    ref_idx: u8
+}
+
+pub const MISSING_REF: PicRef = PicRef { ref_idx: 0xFF };
+pub const INVALID_REF: PicRef = PicRef { ref_idx: 0xFE };
+pub const ZERO_REF: PicRef = PicRef { ref_idx: 0 };
+const DIRECT_FLAG: u8 = 0x40;
+
+impl PicRef {
+    pub fn new(ref_idx: u8) -> Self {
+        Self { ref_idx }
+    }
+    pub fn not_avail(self) -> bool {
+        self == MISSING_REF || self == INVALID_REF
+    }
+    pub fn index(self) -> usize { (self.ref_idx & !DIRECT_FLAG) as usize }
+    pub fn is_direct(self) -> bool { (self.ref_idx & DIRECT_FLAG) != 0 }
+    pub fn set_direct(&mut self) { self.ref_idx |= DIRECT_FLAG; }
+    fn min_pos(self, other: Self) -> Self {
+        match (self.not_avail(), other.not_avail()) {
+            (true,  true)   => self,
+            (false, true)   => self,
+            (true,  false)  => other,
+            (false, false)  => PicRef::new((self.ref_idx & !DIRECT_FLAG).min(other.ref_idx & !DIRECT_FLAG)),
+        }
+    }
+}
+
+impl Default for PicRef {
+    fn default() -> Self { MISSING_REF }
+}
+
+impl PartialEq for PicRef {
+    fn eq(&self, other: &Self) -> bool {
+        (self.ref_idx | DIRECT_FLAG) == (other.ref_idx | DIRECT_FLAG)
+    }
+}
+
+impl std::fmt::Display for PicRef {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        if *self == MISSING_REF {
+            write!(f, "-1")
+        } else if *self == INVALID_REF {
+            write!(f, "-2")
+        } else {
+            write!(f, "{}", self.ref_idx & !DIRECT_FLAG)
+        }
+    }
+}
+
+#[derive(Clone,Copy,Default)]
+pub struct MBData {
+    pub mb_type:        CompactMBType,
+    pub cbp:            u8,
+    pub coded_flags:    u32,
+    pub cmode:          u8,
+    pub qp_y:           u8,
+    pub qp_u:           u8,
+    pub qp_v:           u8,
+    pub transform_8x8:  bool,
+}
+
+pub fn blk4_to_blk8(blk4: usize) -> usize {
+    const MAP: [usize; 16] = [ 0, 0, 1, 1, 0, 0, 1, 1, 2, 2, 3, 3, 2, 2, 3, 3 ];
+    MAP[blk4 & 0xF]
+}
+
+#[derive(Clone,Copy)]
+pub struct Blk8Data {
+    pub ref_idx:    [PicRef; 2],
+    pub ncoded_c:   [u8; 2],
+}
+
+impl Default for Blk8Data {
+    fn default() -> Self {
+        Self {
+            ref_idx:        [MISSING_REF; 2],
+            ncoded_c:       [0; 2],
+        }
+    }
+}
+
+#[derive(Clone,Copy,Default)]
+pub struct Blk4Data {
+    pub ncoded:     u8,
+    pub ipred:      IntraPredMode,
+    pub mv:         [MV; 2],
+    pub mvd:        [MV; 2],
+}
+
+pub struct SliceState {
+    pub mb_x:           usize,
+    pub mb_y:           usize,
+    pub mb_w:           usize,
+    pub mb_h:           usize,
+    pub mb_start:       usize,
+
+    pub mb:             GenericCache<MBData>,
+    pub blk8:           GenericCache<Blk8Data>,
+    pub blk4:           GenericCache<Blk4Data>,
+
+    pub deblock:        GenericCache<u8>,
+
+    pub has_top:        bool,
+    pub has_left:       bool,
+}
+
+impl SliceState {
+    pub fn new() -> Self {
+        Self {
+            mb_x:       0,
+            mb_y:       0,
+            mb_w:       0,
+            mb_h:       0,
+            mb_start:   0,
+            mb:         GenericCache::new(0, 0, MBData::default()),
+            blk8:       GenericCache::new(0, 0, Blk8Data::default()),
+            blk4:       GenericCache::new(0, 0, Blk4Data::default()),
+
+            deblock:    GenericCache::new(0, 0, 0),
+
+            has_top:    false,
+            has_left:   false,
+        }
+    }
+    pub fn reset(&mut self, mb_w: usize, mb_h: usize, mb_pos: usize) {
+        self.mb_w     = mb_w;
+        self.mb_h     = mb_h;
+        self.mb_start = mb_pos;
+        if mb_w > 0 {
+            self.mb_x = mb_pos % mb_w;
+            self.mb_y = mb_pos / mb_w;
+        } else {
+            self.mb_x = 0;
+            self.mb_y = 0;
+        }
+        self.mb    = GenericCache::new(1, mb_w     + 2, MBData::default());
+        self.blk8  = GenericCache::new(2, mb_w * 2 + 2, Blk8Data::default());
+        self.blk4  = GenericCache::new(4, mb_w * 4 + 2, Blk4Data::default());
+
+        self.deblock  = GenericCache::new(4, mb_w * 4 + 1, 0);
+
+        self.has_top  = false;
+        self.has_left = false;
+    }
+    pub fn fill_deblock(&mut self, deblock_mode: u8, is_s: bool) {
+        if deblock_mode == 1 {
+            return;
+        }
+
+        let tx8x8 = self.get_cur_mb().transform_8x8;
+
+        let mut idx = self.deblock.xpos + self.mb_x * 4;
+        let cur_mbt     = self.get_cur_mb().mb_type;
+        let left_mbt    = self.get_left_mb().mb_type;
+        let mut top_mbt = self.get_top_mb().mb_type;
+        for y in 0..4 {
+            if tx8x8 && (y & 1) != 0 {
+                continue;
+            }
+            let can_do_top = y != 0 || (self.mb_y != 0 && (self.has_top || deblock_mode != 2));
+            if can_do_top {
+                if is_s || cur_mbt.is_intra() || top_mbt.is_intra() {
+                    let val = if y == 0 { 0x40 } else { 0x30 };
+                    for el in self.deblock.data[idx..][..4].iter_mut() { *el |= val; }
+                } else {
+                    for x in 0..4 {
+                        if self.get_cur_blk4(x).ncoded != 0 || self.get_top_blk4(x).ncoded != 0 {
+                            self.deblock.data[idx + x] |= 0x20;
+                        } else {
+                            let cur_mv = self.get_cur_blk4(x).mv;
+                            let top_mv = self.get_top_blk4(x).mv;
+                            let cur_ref = self.get_cur_blk8(x / 2).ref_idx;
+                            let top_ref = self.get_top_blk8(x / 2).ref_idx;
+                            if mvdiff4(cur_mv[0], top_mv[0]) || mvdiff4(cur_mv[1], top_mv[1]) || cur_ref != top_ref {
+                                self.deblock.data[idx + x] |= 0x10;
+                            }
+                        }
+                    }
+                }
+            }
+            let mut lleft_mbt = left_mbt;
+            for x in 0..4 {
+                if tx8x8 && (x & 1) != 0 {
+                    continue;
+                }
+                let can_do_left = x > 0 || self.has_left || (self.mb_x != 0 && deblock_mode != 2);
+                if !can_do_left {
+                    continue;
+                }
+                let blk4 = x + y * 4;
+                let blk8 = x / 2 + (y / 2) * 2;
+                if is_s || cur_mbt.is_intra() || lleft_mbt.is_intra() {
+                    self.deblock.data[idx + x] |= if x == 0 { 4 } else { 3 };
+                } else if self.get_cur_blk4(blk4).ncoded != 0 || self.get_top_blk4(blk4).ncoded != 0 {
+                    self.deblock.data[idx + x] |= 2;
+                } else {
+                    let cur_mv  = self.get_cur_blk4(blk4).mv;
+                    let left_mv = self.get_left_blk4(blk4).mv;
+                    let cur_ref  = self.get_cur_blk8(blk8).ref_idx;
+                    let left_ref = self.get_left_blk8(blk8).ref_idx;
+                    if mvdiff4(cur_mv[0], left_mv[0]) || mvdiff4(cur_mv[1], left_mv[1]) || cur_ref != left_ref {
+                        self.deblock.data[idx + x] |= 1;
+                    }
+                }
+                lleft_mbt = cur_mbt;
+            }
+            top_mbt = cur_mbt;
+            idx += self.deblock.stride;
+        }
+    }
+    pub fn next_mb(&mut self) {
+        self.mb_x += 1;
+        self.has_left = true;
+        if self.mb_x == self.mb_w {
+            self.mb_x = 0;
+            self.mb_y += 1;
+            self.mb.update_row();
+            self.blk8.update_row();
+            self.blk4.update_row();
+
+            self.deblock.update_row();
+
+            self.has_left = false;
+        }
+        self.has_top = self.mb_x + self.mb_y * self.mb_w >= self.mb_start + self.mb_w;
+    }
+    pub fn get_cur_mb_idx(&self) -> usize { self.mb.xpos + self.mb_x }
+    pub fn get_cur_blk8_idx(&self, blk_no: usize) -> usize {
+        self.blk8.xpos + self.mb_x * 2 + (blk_no & 1) + (blk_no >> 1) * self.blk8.stride
+    }
+    pub fn get_cur_blk4_idx(&self, blk_no: usize) -> usize {
+        self.blk4.xpos + self.mb_x * 4 + (blk_no & 3) + (blk_no >> 2) * self.blk4.stride
+    }
+    pub fn get_cur_mb(&mut self) -> &mut MBData {
+        let idx = self.get_cur_mb_idx();
+        &mut self.mb.data[idx]
+    }
+    pub fn get_left_mb(&self) -> &MBData {
+        &self.mb.data[self.get_cur_mb_idx() - 1]
+    }
+    pub fn get_top_mb(&self) -> &MBData {
+        &self.mb.data[self.get_cur_mb_idx() - self.mb.stride]
+    }
+    pub fn get_cur_blk8(&mut self, blk_no: usize) -> &mut Blk8Data {
+        let idx = self.get_cur_blk8_idx(blk_no);
+        &mut self.blk8.data[idx]
+    }
+    pub fn get_left_blk8(&self, blk_no: usize) -> &Blk8Data {
+        &self.blk8.data[self.get_cur_blk8_idx(blk_no) - 1]
+    }
+    pub fn get_top_blk8(&self, blk_no: usize) -> &Blk8Data {
+        &self.blk8.data[self.get_cur_blk8_idx(blk_no) - self.blk8.stride]
+    }
+    pub fn get_cur_blk4(&mut self, blk_no: usize) -> &mut Blk4Data {
+        let idx = self.get_cur_blk4_idx(blk_no);
+        &mut self.blk4.data[idx]
+    }
+    pub fn get_left_blk4(&self, blk_no: usize) -> &Blk4Data {
+        &self.blk4.data[self.get_cur_blk4_idx(blk_no) - 1]
+    }
+    pub fn get_top_blk4(&self, blk_no: usize) -> &Blk4Data {
+        &self.blk4.data[self.get_cur_blk4_idx(blk_no) - self.blk4.stride]
+    }
+
+    pub fn apply_to_blk8<F: (Fn(&mut Blk8Data))>(&mut self, f: F) {
+        let start = self.get_cur_blk8_idx(0);
+        for row in self.blk8.data[start..].chunks_mut(self.blk8.stride).take(2) {
+            for el in row[..2].iter_mut() {
+                f(el);
+            }
+        }
+    }
+    pub fn apply_to_blk4<F: (Fn(&mut Blk4Data))>(&mut self, f: F) {
+        let start = self.get_cur_blk4_idx(0);
+        for row in self.blk4.data[start..].chunks_mut(self.blk4.stride).take(4) {
+            for el in row[..4].iter_mut() {
+                f(el);
+            }
+        }
+    }
+
+    pub fn fill_ipred(&mut self, imode: IntraPredMode) {
+        self.apply_to_blk4(|blk| blk.ipred = imode);
+    }
+    pub fn fill_ncoded(&mut self, nc: u8) {
+        self.apply_to_blk4(|blk| blk.ncoded = nc);
+        self.apply_to_blk8(|blk| blk.ncoded_c = [nc; 2]);
+    }
+    pub fn reset_mb_mv(&mut self) {
+        self.apply_to_blk8(|blk| blk.ref_idx = [INVALID_REF; 2]);
+    }
+
+    pub fn get_mv_ctx(&self, xoff: usize, yoff: usize, ref_l: usize) -> (usize, usize) {
+        let blk_no = xoff / 4 + yoff;
+        let mv_a = self.get_left_blk4(blk_no).mvd[ref_l];
+        let mv_b = self.get_top_blk4(blk_no).mvd[ref_l];
+        let mv = mv_a + mv_b;
+        let ctx0 = if mv.x < 3 { 0 } else if mv.x <= 32 { 1 } else { 2 };
+        let ctx1 = if mv.y < 3 { 0 } else if mv.y <= 32 { 1 } else { 2 };
+        (ctx0, ctx1)
+    }
+    pub fn get_mv_ref_ctx(&self, xoff: usize, yoff: usize, ref_l: usize) -> usize {
+        let blk_no = xoff / 8 + (yoff / 8) * 2;
+        let mut ctx = 0;
+        let left_ref = self.get_left_blk8(blk_no).ref_idx[ref_l];
+        let top_ref = self.get_top_blk8(blk_no).ref_idx[ref_l];
+        if !left_ref.not_avail() && !left_ref.is_direct() && left_ref.index() > 0 {
+            ctx += 1;
+        }
+        if !top_ref.not_avail() && !top_ref.is_direct() && top_ref.index() > 0 {
+            ctx += 2;
+        }
+        ctx
+    }
+    pub fn predict(&mut self, xpos: usize, ypos: usize, bw: usize, bh: usize, ref_l: usize, diff_mv: MV, ref_idx: PicRef) {
+        let midx = self.get_cur_blk4_idx(0) + xpos / 4 + ypos / 4 * self.blk4.stride;
+        let ridx = self.get_cur_blk8_idx(0) + xpos / 8 + ypos / 8 * self.blk8.stride;
+        let ridx_c = self.get_cur_blk8_idx(0) + (xpos + bw) / 8 + ypos / 8 * self.blk8.stride - if (ypos & 4) == 0 { self.blk8.stride } else { 0 };
+
+        let mv_a = self.blk4.data[midx - 1].mv[ref_l];
+        let mv_b = self.blk4.data[midx - self.blk4.stride].mv[ref_l];
+        let mut mv_c = self.blk4.data[midx - self.blk4.stride + bw / 4].mv[ref_l];
+
+        let rx = if (xpos & 4) != 0 { 0 } else { 1 };
+        let ry = if (ypos & 4) != 0 { 0 } else { self.blk8.stride };
+        let ref_a = self.blk8.data[ridx - rx].ref_idx[ref_l];
+        let ref_b = self.blk8.data[ridx - ry].ref_idx[ref_l];
+        let mut ref_c = self.blk8.data[ridx_c].ref_idx[ref_l];
+
+        if ref_c == MISSING_REF || (((xpos + bw) & 4) == 0 && (ypos & 4) != 0) {
+            mv_c = self.blk4.data[midx - self.blk4.stride - 1].mv[ref_l];
+            ref_c = self.blk8.data[ridx - rx - ry].ref_idx[ref_l];
+        }
+
+        let pred_mv = if bw == 16 && bh == 8 && ypos == 0 && ref_b == ref_idx {
+                mv_b
+            } else if bw == 16 && bh == 8 && ypos != 0 && ref_a == ref_idx {
+                mv_a
+            } else if bw == 8 && bh == 16 && xpos == 0 && ref_a == ref_idx {
+                mv_a
+            } else if bw == 8 && bh == 16 && xpos != 0 && ref_c == ref_idx {
+                mv_c
+            } else if ref_b == MISSING_REF && ref_c == MISSING_REF {
+                mv_a
+            } else {
+                let count = ((ref_a == ref_idx) as u8) + ((ref_b == ref_idx) as u8) + ((ref_c == ref_idx) as u8);
+                if count == 1 {
+                    if ref_a == ref_idx {
+                        mv_a
+                    } else if ref_b == ref_idx {
+                        mv_b
+                    } else {
+                        mv_c
+                    }
+                } else {
+                    MV::pred(mv_a, mv_b, mv_c)
+                }
+            };
+
+        let mv = pred_mv + diff_mv;
+        self.fill_mv (xpos, ypos, bw, bh, ref_l, mv);
+        self.fill_ref(xpos, ypos, bw, bh, ref_l, ref_idx);
+    }
+    pub fn predict_pskip(&mut self) {
+        let midx = self.get_cur_blk4_idx(0);
+        let ridx = self.get_cur_blk8_idx(0);
+
+        let mv_a = self.blk4.data[midx - 1].mv[0];
+        let mv_b = self.blk4.data[midx - self.blk4.stride].mv[0];
+        let mut mv_c = self.blk4.data[midx - self.blk4.stride + 4].mv[0];
+
+        let ref_a = self.blk8.data[ridx - 1].ref_idx[0];
+        let ref_b = self.blk8.data[ridx - self.blk8.stride].ref_idx[0];
+        let mut ref_c = self.blk8.data[ridx - self.blk8.stride + 2].ref_idx[0];
+
+        if ref_c == MISSING_REF {
+            mv_c = self.blk4.data[midx - self.blk4.stride - 1].mv[0];
+            ref_c = self.blk8.data[ridx - self.blk8.stride - 1].ref_idx[0];
+        }
+
+        let ref_idx = ZERO_REF;
+        let mv = if ref_a == MISSING_REF || ref_b == MISSING_REF || (ref_a == ZERO_REF && mv_a == ZERO_MV) || (ref_b == ZERO_REF && mv_b == ZERO_MV) {
+                ZERO_MV
+            } else {
+                let count = ((ref_a == ref_idx) as u8) + ((ref_b == ref_idx) as u8) + ((ref_c == ref_idx) as u8);
+                if count == 1 {
+                    if ref_a == ref_idx {
+                        mv_a
+                    } else if ref_b == ref_idx {
+                        mv_b
+                    } else {
+                        mv_c
+                    }
+                } else {
+                    MV::pred(mv_a, mv_b, mv_c)
+                }
+            };
+
+        self.fill_mv (0, 0, 16, 16, 0, mv);
+        self.fill_ref(0, 0, 16, 16, 0, ref_idx);
+    }
+    pub fn predict_direct_mb(&mut self, frame_refs: &FrameRefs, temporal_mv: bool, cur_id: u16) {
+        let (col_mb, _, _) = frame_refs.get_colocated_info(self.mb_x, self.mb_y);
+        if col_mb.mb_type.is_16x16() || !temporal_mv {
+            let (mv0, ref0, mv1, ref1) = self.get_direct_mv(frame_refs, temporal_mv, cur_id, 0);
+            self.apply_to_blk4(|blk4| blk4.mv = [mv0, mv1]);
+            self.apply_to_blk8(|blk8| blk8.ref_idx = [ref0, ref1]);
+        } else {
+            for blk4 in 0..16 {
+                let (mv0, ref0, mv1, ref1) = self.get_direct_mv(frame_refs, temporal_mv, cur_id, blk4);
+                self.get_cur_blk4(blk4).mv = [mv0, mv1];
+                self.get_cur_blk8(blk4_to_blk8(blk4)).ref_idx = [ref0, ref1];
+            }
+        }
+    }
+    pub fn predict_direct_sub(&mut self, frame_refs: &FrameRefs, temporal_mv: bool, cur_id: u16, blk4: usize) {
+        if temporal_mv {
+            let (mv0, ref0, mv1, ref1) = self.get_direct_mv(frame_refs, temporal_mv, cur_id, blk4);
+            self.get_cur_blk4(blk4).mv = [mv0, mv1];
+            self.get_cur_blk8(blk4_to_blk8(blk4)).ref_idx = [ref0, ref1];
+        } else {
+            let (mv0, ref0, mv1, ref1) = self.get_direct_mv(frame_refs, temporal_mv, cur_id, blk4);
+            self.get_cur_blk4(blk4).mv = [mv0, mv1];
+            self.get_cur_blk8(blk4_to_blk8(blk4)).ref_idx = [ref0, ref1];
+        }
+    }
+    pub fn get_direct_mv(&self, frame_refs: &FrameRefs, temporal_mv: bool, cur_id: u16, blk4: usize) -> (MV, PicRef, MV, PicRef) {
+        let (mbi, r1_poc, r1_long) = frame_refs.get_colocated_info(self.mb_x, self.mb_y);
+        let blk8 = blk4_to_blk8(blk4);
+        let (col_mv, r0_poc, col_idx) = if mbi.ref_poc[blk8] == [MISSING_POC; 2] {
+                (ZERO_MV, MISSING_POC, MISSING_REF)
+            } else if mbi.ref_poc[blk8][0] != MISSING_POC {
+                (mbi.mv[blk4][0], mbi.ref_poc[blk8][0], mbi.ref_idx[blk8][0])
+            } else {
+                (mbi.mv[blk4][1], mbi.ref_poc[blk8][1], mbi.ref_idx[blk8][1])
+            };
+        let (col_ref, r0_long) = frame_refs.map_ref0(r0_poc);
+        if temporal_mv {
+            let td = (i32::from(r1_poc) - i32::from(r0_poc)).max(-128).min(127);
+            if r0_long || td == 0 {
+                (col_mv, col_ref, ZERO_MV, ZERO_REF)
+            } else {
+                let tx = (16384 + (td / 2).abs()) / td;
+                let tb = (i32::from(cur_id) - i32::from(r0_poc)).max(-128).min(127);
+                let scale = ((tb * tx + 32) >> 6).max(-1024).min(1023);
+                let mv0 = MV {
+                        x: ((i32::from(col_mv.x) * scale + 128) >> 8) as i16,
+                        y: ((i32::from(col_mv.y) * scale + 128) >> 8) as i16,
+                    };
+                let mv1 = mv0 - col_mv;
+                (mv0, col_ref, mv1, ZERO_REF)
+            }
+        } else {
+            let blk4 = 0; // we generate the same MV prediction for the whole MB
+            let blk8 = blk4_to_blk8(blk4);
+            let midx = self.get_cur_blk4_idx(blk4);
+            let ridx = self.get_cur_blk8_idx(blk8);
+            let ridx_c = self.get_cur_blk8_idx(blk8) + 16 / 8 - self.blk8.stride;
+
+            let mv_a = self.blk4.data[midx - 1].mv;
+            let mv_b = self.blk4.data[midx - self.blk4.stride].mv;
+            let mut mv_c = self.blk4.data[midx - self.blk4.stride + 16 / 4].mv;
+
+            let ref_a = self.blk8.data[ridx - 1].ref_idx;
+            let ref_b = self.blk8.data[ridx - self.blk8.stride].ref_idx;
+            let mut ref_c = self.blk8.data[ridx_c].ref_idx;
+
+            if ref_c == [MISSING_REF; 2] {
+                mv_c = self.blk4.data[midx - self.blk4.stride - 1].mv;
+                ref_c = self.blk8.data[ridx - self.blk8.stride - 1].ref_idx;
+            }
+            let mut refs = [INVALID_REF; 2];
+            for cur_ref in [ref_a, ref_b, ref_c].iter() {
+                refs[0] = refs[0].min_pos(cur_ref[0]);
+                refs[1] = refs[1].min_pos(cur_ref[1]);
+            }
+            if refs == [INVALID_REF; 2] {
+                return (ZERO_MV, ZERO_REF, ZERO_MV, ZERO_REF);
+            }
+
+            let mut col_zero = true;
+            if r1_long || col_idx != ZERO_REF {
+                col_zero = false;
+            }
+            if col_mv.x.abs() > 1 || col_mv.y.abs() > 1 {
+                col_zero = false;
+            }
+            let mut mvs = [ZERO_MV; 2];
+            for ref_l in 0..2 {
+                if mbi.mb_type.is_intra() || (!refs[ref_l].not_avail() && !(refs[ref_l] == ZERO_REF && col_zero)) {
+                    let ref_idx = refs[ref_l];
+                    mvs[ref_l] = if ref_b[ref_l] == MISSING_REF && ref_c[ref_l] == MISSING_REF {
+                            mv_a[ref_l]
+                        } else {
+                            let count = ((ref_a[ref_l] == ref_idx) as u8) + ((ref_b[ref_l] == ref_idx) as u8) + ((ref_c[ref_l] == ref_idx) as u8);
+                            if count == 1 {
+                                if ref_a[ref_l] == ref_idx {
+                                    mv_a[ref_l]
+                                } else if ref_b[ref_l] == ref_idx {
+                                    mv_b[ref_l]
+                                } else {
+                                    mv_c[ref_l]
+                                }
+                            } else {
+                                MV::pred(mv_a[ref_l], mv_b[ref_l], mv_c[ref_l])
+                            }
+                        };
+                }
+            }
+            (mvs[0], refs[0], mvs[1], refs[1])
+        }
+    }
+    pub fn fill_mv(&mut self, xpos: usize, ypos: usize, bw: usize, bh: usize, ref_l: usize, mv: MV) {
+        let start = self.get_cur_blk4_idx(0) + xpos / 4 + ypos / 4 * self.blk4.stride;
+        for row in self.blk4.data[start..].chunks_mut(self.blk4.stride).take(bh / 4) {
+            for blk in row[..bw / 4].iter_mut() {
+                blk.mv[ref_l] = mv;
+            }
+        }
+    }
+    pub fn fill_mvd(&mut self, xpos: usize, ypos: usize, bw: usize, bh: usize, ref_l: usize, mv: MV) {
+        let mvd = MV{ x: mv.x.abs().min(128), y: mv.y.abs().min(128) };
+        let start = self.get_cur_blk4_idx(0) + xpos / 4 + ypos / 4 * self.blk4.stride;
+        for row in self.blk4.data[start..].chunks_mut(self.blk4.stride).take(bh / 4) {
+            for blk in row[..bw / 4].iter_mut() {
+                blk.mvd[ref_l] = mvd;
+            }
+        }
+    }
+    pub fn fill_ref(&mut self, xpos: usize, ypos: usize, bw: usize, bh: usize, ref_l: usize, ref_idx: PicRef) {
+        let start = self.get_cur_blk8_idx(0) + xpos / 8 + ypos / 8 * self.blk8.stride;
+        if bw < 8 || bh < 8 {
+            self.blk8.data[start].ref_idx[ref_l] = ref_idx;
+        } else {
+            for row in self.blk8.data[start..].chunks_mut(self.blk8.stride).take(bh / 8) {
+                for blk in row[..bw / 8].iter_mut() {
+                    blk.ref_idx[ref_l] = ref_idx;
+                }
+            }
+        }
+    }
+}
+
+fn mvdiff4(mv1: MV, mv2: MV) -> bool {
+    let mv = mv1 - mv2;
+    (mv.x.abs() >= 4) || (mv.y.abs() >= 4)
+}
diff --git a/nihav-itu/src/codecs/mod.rs b/nihav-itu/src/codecs/mod.rs
new file mode 100644 (file)
index 0000000..d4167b5
--- /dev/null
@@ -0,0 +1,20 @@
+use nihav_core::codecs::*;
+
+macro_rules! validate {
+    ($a:expr) => { if !$a { println!("check failed at {}:{}", file!(), line!()); return Err(DecoderError::InvalidData); } };
+}
+
+#[cfg(feature="decoder_h264")]
+mod h264;
+
+const ITU_CODECS: &[DecoderInfo] = &[
+#[cfg(feature="decoder_h264")]
+    DecoderInfo { name: "h264", get_decoder: h264::get_decoder },
+];
+
+/// Registers all available codecs provided by this crate.
+pub fn itu_register_all_decoders(rd: &mut RegisteredDecoders) {
+    for decoder in ITU_CODECS.iter() {
+        rd.add_decoder(decoder.clone());
+    }
+}
diff --git a/nihav-itu/src/lib.rs b/nihav-itu/src/lib.rs
new file mode 100644 (file)
index 0000000..4f65576
--- /dev/null
@@ -0,0 +1,8 @@
+extern crate nihav_core;
+extern crate nihav_codec_support;
+
+mod codecs;
+pub use crate::codecs::itu_register_all_decoders;
+
+#[cfg(test)]
+extern crate nihav_commonfmt;
index 78ccb18b9c2fbd36f0e5fce71ab9018bf3e4ded6..d4e2b028097d7c753533663b2ec31bc09f4afdb9 100644 (file)
@@ -256,6 +256,8 @@ static CODEC_REGISTER: &'static [CodecDescription] = &[
     desc!(audio-ll;  "flac",         "Free Lossless Audio Codec"),
     desc!(audio-ll;  "tta",          "True Audio codec"),
     desc!(audio-hyb; "wavpack",      "WavPack"),
+
+    desc!(video;    "h264",          "ITU H.264", CODEC_CAP_COMPLEX_REORDER | CODEC_CAP_HYBRID),
 ];
 
 static AVI_VIDEO_CODEC_REGISTER: &'static [(&[u8;4], &str)] = &[
@@ -335,6 +337,8 @@ static MOV_VIDEO_CODEC_REGISTER: &'static [(&[u8;4], &str)] = &[
 
     (b"VP30", "vp3"),
     (b"VP31", "vp3"),
+
+    (b"avc1", "h264"),
 ];
 
 static MOV_AUDIO_CODEC_REGISTER: &'static [(&[u8;4], &str)] = &[