From: Kostya Shishkov Date: Tue, 3 Nov 2020 09:47:39 +0000 (+0100) Subject: mostly working ITU H.264 decoder X-Git-Url: https://git.nihav.org/?a=commitdiff_plain;h=696e4e20bf7167121352f247893370cb83213d5c;p=nihav.git mostly working ITU H.264 decoder --- diff --git a/nihav-allstuff/Cargo.toml b/nihav-allstuff/Cargo.toml index fc78a05..b7398cb 100644 --- a/nihav-allstuff/Cargo.toml +++ b/nihav-allstuff/Cargo.toml @@ -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" } diff --git a/nihav-allstuff/src/lib.rs b/nihav-allstuff/src/lib.rs index 90a7766..af8064c 100644 --- a/nihav-allstuff/src/lib.rs +++ b/nihav-allstuff/src/lib.rs @@ -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 index 0000000..2c5521a --- /dev/null +++ b/nihav-itu/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "nihav_itu" +version = "0.1.0" +authors = ["Kostya Shishkov "] +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 index 0000000..554da2a --- /dev/null +++ b/nihav-itu/src/codecs/h264/cabac.rs @@ -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 index 0000000..453ebc9 --- /dev/null +++ b/nihav-itu/src/codecs/h264/cabac_coder.rs @@ -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 { + 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 index 0000000..4c598a4 --- /dev/null +++ b/nihav-itu/src/codecs/h264/cavlc.rs @@ -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 { + 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 { + 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, tables: &CAVLCTables) -> DecoderResult { + 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, tables: &CAVLCTables) -> DecoderResult { + decode_coeffs(br, coeffs, &ZIGZAG, cb, tables) +} + +fn decode_block_ac(br: &mut BitReader, coeffs: &mut [i16; 16], cb: &Codebook, tables: &CAVLCTables) -> DecoderResult { + decode_coeffs(br, &mut coeffs[1..], &ZIGZAG1, cb, tables) +} + +fn decode_chroma_dc(br: &mut BitReader, coeffs: &mut [i16; 4], cb: &Codebook, tables: &CAVLCTables) -> DecoderResult { + 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; 4], + cdc_coeff_token_cb: Codebook, + total_zeros_cb: [Codebook; 15], + cdc_total_zeros_cb: [Codebook; 3], + run_before_cb: [Codebook; 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 index 0000000..0b3c1e1 --- /dev/null +++ b/nihav-itu/src/codecs/h264/dsp.rs @@ -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, refpic: NAVideoBufferRef, 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, 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, refpic: NAVideoBufferRef, xpos: usize, ypos: usize, w: usize, h: usize, mv: MV, avg_buf: &mut NAVideoBufferRef) { + 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 index 0000000..a5ae123 --- /dev/null +++ b/nihav-itu/src/codecs/h264/loopfilter.rs @@ -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, 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, 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 index 0000000..5ecd05c --- /dev/null +++ b/nihav-itu/src/codecs/h264/mod.rs @@ -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; + fn read_te(&mut self, range: u32) -> DecoderResult; + fn read_ue_lim(&mut self, max_val: u32) -> DecoderResult { + let val = self.read_ue()?; + validate!(val <= max_val); + Ok(val) + } + fn read_se(&mut self) -> DecoderResult { + 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 { + Ok(self.read_code(UintCodeType::GammaP)? - 1) + } + fn read_te(&mut self, range: u32) -> DecoderResult { + 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 { + 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, + cur_sps: usize, + pps: Vec, + cur_pps: usize, + + skip_mode: FrameSkipMode, + deblock_skip: bool, + + is_mbaff: bool, + + cavlc_cb: CAVLCTables, + + sstate: SliceState, + + cur_pic: Option, + 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, + + transform_8x8_mode: bool, +} + +fn unescape_nal(src: &[u8], dst: &mut Vec) -> 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, 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, 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, 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, xpos: usize, ypos: usize, w: usize, h: usize, mv: MV, ref_pic: Option>) { + 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, mode: BMode, xpos: usize, ypos: usize, w: usize, h: usize, mv0: MV, ref_pic0: Option>, mv1: MV, ref_pic1: Option>, avg_buf: &mut NAVideoBufferRef) { + 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 { + 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 { + 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 { + 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 { + 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 { + 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 index 0000000..88d453d --- /dev/null +++ b/nihav-itu/src/codecs/h264/pic_ref.rs @@ -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, + pub cur_mb: usize, + pub is_ref: bool, + pub long_term: Option, + + 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, + 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, + pub ref_list0: Vec>, + pub ref_list1: Vec>, + pub long_term: Vec>, + + 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> { + 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>, ref_pics: &Vec, long_term: &Vec>, 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 index 0000000..8856699 --- /dev/null +++ b/nihav-itu/src/codecs/h264/sets.rs @@ -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 { + 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 { + 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 index 0000000..a4ce4bf --- /dev/null +++ b/nihav-itu/src/codecs/h264/slice.rs @@ -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 { + 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 index 0000000..5ad0cdc --- /dev/null +++ b/nihav-itu/src/codecs/h264/test/conformance.rs @@ -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 index 0000000..3bdbd90 --- /dev/null +++ b/nihav-itu/src/codecs/h264/test/raw_demux.rs @@ -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, +} + +fn read_nal(src: &mut ByteReader, dst: &mut Vec) -> 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, 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 = 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 { + let ts = NATimeInfo::new(Some(self.cur_frame), None, None, 1, 25); + let mut buf: Vec = 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 { 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 + '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 index 0000000..5adeab1 --- /dev/null +++ b/nihav-itu/src/codecs/h264/types.rs @@ -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 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 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 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, + pub blk8: GenericCache, + pub blk4: GenericCache, + + pub deblock: GenericCache, + + 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(&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(&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 index 0000000..d4167b5 --- /dev/null +++ b/nihav-itu/src/codecs/mod.rs @@ -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 index 0000000..4f65576 --- /dev/null +++ b/nihav-itu/src/lib.rs @@ -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; diff --git a/nihav-registry/src/register.rs b/nihav-registry/src/register.rs index 78ccb18..d4e2b02 100644 --- a/nihav-registry/src/register.rs +++ b/nihav-registry/src/register.rs @@ -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)] = &[