UNPKG

jsvpx

Version:

Javascript implementation of libvpx

819 lines (591 loc) 23.6 kB
'use strict'; var findenearmv = require('../common/findnearmv.js'); var left_block_mode = findenearmv.left_block_mode; var above_block_mode = findenearmv.above_block_mode; var MotionVector = require('../common/mv.js'); var vp8_entropymodedata = require("../common/vp8_entropymodedata"); var vp8_kf_bmode_prob = vp8_entropymodedata.vp8_kf_bmode_prob; var vp8_coef_update_probs = require('../common/coefupdateprobs.js'); var entropymode = require('../common/entropymode'); var vp8_bmode_tree = entropymode.vp8_bmode_tree; var vp8_kf_ymode_tree = entropymode.vp8_kf_ymode_tree; var vp8_uv_mode_tree = entropymode.vp8_uv_mode_tree; var vp8_kf_uv_mode_prob = entropymode.vp8_kf_uv_mode_prob; var vp8_kf_ymode_prob = entropymode.vp8_kf_ymode_prob; var vp8_sub_mv_ref_tree = entropymode.vp8_sub_mv_ref_tree; var vp8_small_mvtree = entropymode.vp8_small_mvtree; var vp8_mbsplit_tree = entropymode.vp8_mbsplit_tree; var vp8_mbsplits = entropymode.vp8_mbsplits; var vp8_sub_mv_ref_prob2 = entropymode.vp8_sub_mv_ref_prob2; var vp8_bmode_prob = entropymode.vp8_bmode_prob; var vp8_kf_ymode_tree = entropymode.vp8_kf_ymode_tree; var vp8_bmode_tree = entropymode.vp8_bmode_tree; var vp8_ymode_tree = entropymode.vp8_ymode_tree; var vp8_uv_mode_tree = entropymode.vp8_uv_mode_tree; var vp8_mode_contexts = require('../common/modecont.js'); var bitreader = require('../../vpx_dsp/bitreader.js'); var vpx_read = bitreader.vpx_read; var vpx_read_bit = bitreader.vpx_read_bit; var vp8_treed_read = require('./treereader.js'); var entropymv = require('../common/entropymv.js'); var vp8_mv_update_probs = entropymv.vp8_mv_update_probs; var DC_PRED = 0; var V_PRED = 1; var H_PRED = 2; var TM_PRED = 3; var B_PRED = 4; var NEARESTMV = 5; var NEARMV = 6; var ZEROMV = 7; var NEWMV = 8; var SPLITMV = 9; var MB_MODE_COUNT = 10; var CNT_BEST = 0; var CNT_ZEROZERO = 0; var CNT_INTRA = 0; var CNT_NEAREST = 1; var CNT_NEAR = 2; var CNT_SPLITMV = 3; var CURRENT_FRAME = 0; var MV_PROB_CNT = 19; var INTRA_FRAME = 0; var mbsplit_fill_count = new Uint8Array([ 8, 8, 4, 1 ]); /* * read_segment_id * vp8_reader *r, MB_MODE_INFO *mi, MACROBLOCKD *x * passing in segment header for now */ function read_mb_features(r, mi, x) { // Is segmentation enabled if (x.enabled && x.update_map) { // If so then read the segment id. if (vpx_read(r, x.tree_probs[0]) === 1) { mi.mbmi.segment_id = 2 + vpx_read(r, x.tree_probs[2]); } else { mi.mbmi.segment_id = vpx_read(r, x.tree_probs[1]); } } } /** * static MB_PREDICTION_MODE */ function read_kf_ymode(bc, p) { var i = vp8_treed_read(bc, vp8_kf_ymode_tree, p, 0); return i; } /* * static B_PREDICTION_MODE */ function read_bmode(bc, p , p_ptr) { var i = vp8_treed_read(bc, vp8_bmode_tree, p , p_ptr); return i; } /** * static MB_PREDICTION_MODE */ function read_uv_mode(bc, p) { var i = vp8_treed_read(bc, vp8_uv_mode_tree, p , 0); return i; } /** * @param {type} this_ * @param {type} this_off * @param {type} left * @param {type} left_off * @param {type} above * @param {type} above_off * @param {type} bool * @returns {undefined} */ function read_kf_modes(pbi, mi, this_off, bool) { var uv_mode = 0; var mis = pbi.common.mode_info_stride; var bc = bool; var mi_cache = mi[this_off]; //Add split mode dynamically to block info mi_cache.init_split_mode(); var modes_cache = mi_cache.bmi.modes; mi_cache.mbmi.ref_frame = INTRA_FRAME; mi_cache.mbmi.y_mode = read_kf_ymode(bc, vp8_kf_ymode_prob); if (mi_cache.mbmi.y_mode === B_PRED) { var i = 0; mi_cache.mbmi.is_4x4 = 1; do { var A = above_block_mode(mi, this_off, i, mis); var L = left_block_mode(mi, this_off, i); modes_cache[i] = read_bmode(bc, vp8_kf_bmode_prob, (A * 90) + L * 9); } while (++i < 16); } mi_cache.mbmi.uv_mode = read_uv_mode(bc, vp8_kf_uv_mode_prob); } var clamped_best_mv_1 = MotionVector.create(); var LEFT_TOP_MARGIN = (16 << 3); var RIGHT_BOTTOM_MARGIN = (16 << 3); function vp8_clamp_mv2(mv, bounds) { if (mv.as_row_col[0] < (bounds.mb_to_left_edge)) { mv.as_row_col[0] = bounds.mb_to_left_edge ; } else if (mv.as_row_col[0] > bounds.mb_to_right_edge) { mv.as_row_col[0] = bounds.mb_to_right_edge; } if (mv.as_row_col[1] < (bounds.mb_to_top_edge )) { mv.as_row_col[1] = bounds.mb_to_top_edge; } else if (mv.as_row_col[1] > bounds.mb_to_bottom_edge) { mv.as_row_col[1] = bounds.mb_to_bottom_edge ; } } //var mv_clamp_mv = MotionVector.create(); function read_mv(bool, mv, mvc) { mv.as_row_col[1] = read_mv_component(bool, mvc[0]); mv.as_row_col[0] = read_mv_component(bool, mvc[1]); } var blockmv = MotionVector.create(); var left_mv = MotionVector.create(); var above_mv = MotionVector.create(); function decode_split_mv(mi, left_mb, above_mb, hdr, best_mv, bool) { var partition = 0; var j = 0; var k = 0; var s; //split configuration var num_p = 0; s = 3; num_p = 16; if (vpx_read(bool, 110)) { s = 2; num_p = 4; if (vpx_read(bool, 111)) { s = vpx_read(bool, 150); num_p = 2; } } partition = vp8_mbsplits[s]; var mvs = mi.bmi.mvs; do { blockmv.as_int[0] = 0; left_mv.as_int[0] = 0; above_mv.as_int[0] = 0; var subblock_mode;//='prediction_mode' var prob; /* Find the first subblock in this partition. */ for (k = 0; j !== partition[k]; k++) ; /* Decode the next MV */ if (!(k & 3)) { if (left_mb.mbmi.y_mode === SPLITMV){ left_mv.as_int[0] = left_mb.bmi.mvs[k + 3].as_int[0]; }else{ left_mv.as_int[0] = left_mb.mbmi.mv.as_int[0]; } } else { left_mv.as_int[0] = mi.bmi.mvs[k - 1].as_int[0]; } if (!(k >> 2)) { if (above_mb.mbmi.y_mode === SPLITMV) { above_mv.as_int[0] = above_mb.bmi.mvs[k + 12].as_int[0]; } else { above_mv.as_int[0] = above_mb.mbmi.mv.as_int[0]; } } else { above_mv.as_int[0] = mi.bmi.mvs[k - 4].as_int[0]; } prob = get_sub_mv_ref_prob(left_mv.as_int[0], above_mv.as_int[0]); if (vpx_read(bool, prob[0])) { if (vpx_read(bool, prob[1])) { //blockmv.as_int[0] = 0; if (vpx_read(bool, prob[2])) { read_mv(bool, blockmv, hdr.mv_probs); blockmv.as_row_col[0] = (blockmv.as_row_col[0] + best_mv.as_row_col[0]); blockmv.as_row_col[1] = (blockmv.as_row_col[1] + best_mv.as_row_col[1] ); } } else { blockmv.as_int[0] = above_mv.as_int[0]; } } else { blockmv.as_int[0] = left_mv.as_int[0]; } var fill_count = mbsplit_fill_count[s]; /* Fill the MV's for this partition */ for (; k < 16; k++) if (j === partition[k]) { mvs[k].as_int[0] = blockmv.as_int[0]; } } while (++j < num_p); mi.mbmi.partitioning = s; } function get_sub_mv_ref_prob(left, above) { var lez = (left === 0); var aez = (above === 0); var lea = (left === above); var prob; prob = vp8_sub_mv_ref_prob3[(aez << 2) | (lez << 1) | (lea)]; return prob; } var vp8_sub_mv_ref_prob3 = [ new Uint8Array([ 147, 136, 18 ]), /* SUBMVREF_NORMAL */ new Uint8Array([ 223, 1, 34 ]), /* SUBMVREF_LEFT_ABOVE_SAME */ new Uint8Array([ 106, 145, 1 ]), /* SUBMVREF_LEFT_ZED */ new Uint8Array([ 208, 1, 1 ]), /* SUBMVREF_LEFT_ABOVE_ZED */ new Uint8Array([ 179, 121, 1 ]), /* SUBMVREF_ABOVE_ZED */ new Uint8Array([ 223, 1, 34 ]), /* SUBMVREF_LEFT_ABOVE_SAME */ new Uint8Array([ 179, 121, 1 ]), /* SUBMVREF_ABOVE_ZED */ new Uint8Array([ 208, 1, 1 ]) /* SUBMVREF_LEFT_ABOVE_ZED */ ]; //(need_mc_border(this_.mbmi.mv, x, y, 16, w, h)) function need_mc_border(mv, l, t, b_w, w, h) { var b = 0; var r = 0; // Get distance to edge for top-left pixel l += (mv.as_row_col[0] >> 3); t += (mv.as_row_col[1] >> 3); // Get distance to edge for bottom-right pixel r = w - (l + b_w); b = h - (t + b_w); return (l >> 1 < 2 || r >> 1 < 3 || t >> 1 < 2 || b >> 1 < 3); } function mv_bias(mb, sign_bias, ref_frame, mv) { if (sign_bias[mb.mbmi.ref_frame] ^ sign_bias[ref_frame]) { mv.as_row_col[0] *= -1; mv.as_row_col[1] *= -1; } } function read_mv_component(bool, mvc) { var IS_SHORT = 0, SIGN = 1, SHORT = 2, BITS = SHORT + 7, LONG_WIDTH = 10; var x = 0; if (vpx_read(bool, mvc[IS_SHORT])) // Large { var i = 0; for (i = 0; i < 3; i++) x += vpx_read(bool, mvc[BITS + i]) << i; /* Skip bit 3, which is sometimes implicit */ for (i = LONG_WIDTH - 1; i > 3; i--) x += vpx_read(bool, mvc[BITS + i]) << i; if (!(x & 0xFFF0) || vpx_read(bool, mvc[BITS + 3])) x += 8; } else /* small */ x = vp8_treed_read(bool, vp8_small_mvtree, mvc, +SHORT);//todo if (x && vpx_read(bool, mvc[SIGN])) x = -x; return (x << 1) | 0; } //Do not need to redeclare these var near_mvs = [ MotionVector.create(), MotionVector.create(), MotionVector.create(), MotionVector.create() ]; var near_mvs_best = MotionVector.create(); var chroma_mv = [ MotionVector.create(), MotionVector.create(), MotionVector.create(), MotionVector.create() ]; var cnt = new Int32Array(4); var this_mv_tmp = MotionVector.create(); function read_mb_modes_mv(pbi, mi, this_off, bool, bounds) { var mbmi = mi[this_off].mbmi; var hdr = pbi.common.entropy_hdr; var this_ = mi[this_off]; if (vpx_read(bool, hdr.prob_inter)) { //START DECODE_MVS //nearmvs var clamped_best_mv = clamped_best_mv_1; //var probs = new Uint8Array(4); var left_ = mi[this_off - 1]; var BEST = 0; var NEAREST = 1; var NEAR = 2; var mis = pbi.common.mode_info_stride; var above = mi[this_off - mis]; var sign_bias = pbi.common.sign_bias; var x = 0, y = 0, w = 0, h = 0, b = 0; mbmi.ref_frame = vpx_read(bool, hdr.prob_last) ? 2 + vpx_read(bool, hdr.prob_gf) : 1; //START FIND NEAR MVS var aboveleft_off = (this_off - mis) - 1; var nmv = (near_mvs); var mv_off = 0; var cntx = cnt; var cntx_off = 0; /* Zero accumulators */ nmv[0].as_int[0] = nmv[1].as_int[0] = nmv[2].as_int[0] = 0; cnt[0] = cnt[1] = cnt[2] = cnt[3] = 0; var aboveleft_ = mi[aboveleft_off]; /* Process above */ if (above.mbmi.ref_frame !== INTRA_FRAME) { if (above.mbmi.mv.as_int[0]) { nmv[++mv_off].as_int[0] = above.mbmi.mv.as_int[0]; mv_bias(above, sign_bias, this_.mbmi.ref_frame, nmv[mv_off]); ++cntx_off; } cntx[cntx_off] += 2; } /* Process left */ if (left_.mbmi.ref_frame !== INTRA_FRAME) { if (left_.mbmi.mv.as_int[0]) { var this_mv = this_mv_tmp; this_mv.as_int[0] = left_.mbmi.mv.as_int[0]; mv_bias(left_, sign_bias, this_.mbmi.ref_frame, this_mv); if (this_mv.as_int[0] !== nmv[mv_off].as_int[0]) { nmv[++mv_off].as_int[0] = this_mv.as_int[0]; ++cntx_off; } cntx[cntx_off] += 2; } else cnt[CNT_ZEROZERO] += 2; } /* Process above left */ if (aboveleft_.mbmi.ref_frame !== INTRA_FRAME) { if (aboveleft_.mbmi.mv.as_int[0]) { var this_mv = this_mv_tmp; this_mv.as_int[0] = aboveleft_.mbmi.mv.as_int[0]; mv_bias(aboveleft_, sign_bias, this_.mbmi.ref_frame, this_mv); if (this_mv.as_int[0] !== nmv[mv_off].as_int[0]) { nmv[(++mv_off)].as_int[0] = this_mv.as_int[0]; ++cntx_off; } cntx[cntx_off] += 1; } else cnt[CNT_ZEROZERO] += 1; } /* If we have three distinct MV's ... */ if (cnt[CNT_SPLITMV]) { /* See if above-left MV can be merged with NEAREST */ if (nmv[mv_off].as_int[0] === near_mvs[CNT_NEAREST].as_int[0])//.raw cnt[CNT_NEAREST] += 1; } cnt[CNT_SPLITMV] = ((above.mbmi.y_mode === SPLITMV) + (left_.mbmi.y_mode === SPLITMV)) * 2 + (aboveleft_.mbmi.y_mode === SPLITMV); /* Swap near and nearest if necessary */ if (cnt[CNT_NEAR] > cnt[CNT_NEAREST]) { var tmp = 0; tmp = cnt[CNT_NEAREST]; cnt[CNT_NEAREST] = cnt[CNT_NEAR]; cnt[CNT_NEAR] = tmp; tmp = near_mvs[CNT_NEAREST].as_int[0]; near_mvs[CNT_NEAREST].as_int[0] = near_mvs[CNT_NEAR].as_int[0]; near_mvs[CNT_NEAR].as_int[0] = tmp; } var near_index; /* Use near_mvs[CNT_BEST] to store the "best" MV. Note that this * storage shares the same address as near_mvs[CNT_ZEROZERO]. */ if (cnt[CNT_NEAREST] >= cnt[CNT_BEST]) { near_mvs[CNT_BEST].as_int[0] = near_mvs[CNT_NEAREST].as_int[0]; //near_mvs[CNT_BEST].as_row_col[1] = near_mvs[CNT_NEAREST].y; } this_.mbmi.need_mc_border = 0; x = (-bounds.mb_to_left_edge - 128) >> 3; y = (-bounds.mb_to_top_edge - 128) >> 3; w = pbi.mb_cols << 4; h = pbi.mb_rows << 4; if (vpx_read(bool, vp8_mode_contexts[cnt[CNT_INTRA] * 4])) { if (vpx_read(bool, vp8_mode_contexts[cnt[CNT_NEAREST] * 4 + 1])) { if (vpx_read(bool, vp8_mode_contexts[cnt[CNT_NEAR] * 4 + 2])) { if (vpx_read(bool, vp8_mode_contexts[cnt[CNT_SPLITMV] * 4 + 3])) { //splitmv this_.mbmi.y_mode = SPLITMV; //Reset, dont redeclare chroma_mv[0].as_int[0] = 0; chroma_mv[1].as_int[0] = 0; chroma_mv[2].as_int[0] = 0; chroma_mv[3].as_int[0] = 0; //clamped_best_mv = clamp_mv(near_mvs[BEST], bounds); clamped_best_mv = near_mvs[BEST]; vp8_clamp_mv2(clamped_best_mv, bounds); decode_split_mv(this_, left_, above, hdr, clamped_best_mv, bool);//&clamped_best_mv this_.mbmi.mv.as_int[0] = this_.bmi.mvs[15].as_int[0]; var this_mvs = this_.bmi.mvs; for (b = 0; b < 16; b++) { chroma_mv[(b >> 1 & 1) + (b >> 2 & 2)].as_row_col[0] += this_mvs[b].as_row_col[0]; chroma_mv[(b >> 1 & 1) + (b >> 2 & 2)].as_row_col[1] += this_mvs[b].as_row_col[1] ; if (need_mc_border(this_mvs[b], x + (b & 3) * 4, y + (b & ~3), 4, w, h)) { this_.mbmi.need_mc_border = 1; break; } } for (b = 0; b < 4; b++) { chroma_mv[b].as_row_col[0] += 4 + (chroma_mv[b].as_row_col[0] >> 28) | 0;///* + 8 * (chroma_mv[b].as_row_col[0] >> 31)*/; chroma_mv[b].as_row_col[1] += 4 + (chroma_mv[b].as_row_col[1] >> 28) | 0; chroma_mv[b].as_row_col[0] = (chroma_mv[b].as_row_col[0] >> 2); chroma_mv[b].as_row_col[1] = (chroma_mv[b].as_row_col[1] >> 2); //note we're passing in non-subsampled coordinates if (need_mc_border(chroma_mv[b], x + (b & 1) * 8, y + ((b >> 1) << 3), 16, w, h)) { this_.mbmi.need_mc_border = 1; break; } } } else { //new mv clamped_best_mv = near_mvs[BEST]; vp8_clamp_mv2(clamped_best_mv, bounds); read_mv(bool, this_.mbmi.mv, hdr.mv_probs); this_.mbmi.mv.as_row_col[0] += clamped_best_mv.as_row_col[0]; this_.mbmi.mv.as_row_col[1] += clamped_best_mv.as_row_col[1] ; this_.mbmi.y_mode = NEWMV; } } else { //nearmv this_.mbmi.mv.as_int[0] = near_mvs[NEAR].as_int[0]; vp8_clamp_mv2(this_.mbmi.mv, bounds); this_.mbmi.y_mode = NEARMV; } } else { this_.mbmi.y_mode = NEARESTMV; this_.mbmi.mv.as_int[0] = near_mvs[NEAREST].as_int[0]; vp8_clamp_mv2(this_.mbmi.mv, bounds); } } else { this_.mbmi.y_mode = ZEROMV; this_.mbmi.mv.as_int[0] = 0; } if (need_mc_border(this_.mbmi.mv, x, y, 16, w, h)) this_.mbmi.need_mc_border = 1; //END DECODE_MVS } else { //decode_intra_mb_mode(mi[this_off], pbi.common.entropy_hdr, bool); var y_mode = 0, uv_mode = 0; y_mode = vp8_treed_read(bool, vp8_ymode_tree, hdr.y_mode_probs , 0); if (y_mode === B_PRED) { var i = 0; var modes = this_.bmi.modes; var mvs = this_.bmi.mvs; for (i = 0; i < 16; i++) { var b; b = vp8_treed_read(bool, vp8_bmode_tree, vp8_bmode_prob , 0); modes[i] = mvs[i].as_row_col[0] = b; //mvs[i].as_row_col[1] = 0; } } mbmi.y_mode = y_mode; mbmi.uv_mode = vp8_treed_read(bool, vp8_uv_mode_tree, hdr.uv_mode_probs , 0); mbmi.mv.as_row_col[0] = mi[this_off].mbmi.mv.as_row_col[1] = 0; mbmi.ref_frame = CURRENT_FRAME; } } function decode_mb_mode_mvs(pbi, bool, mi, this_off, bounds) { var mi_cache = mi[this_off]; if (pbi.segment_hdr.update_map === 1) { read_mb_features(bool, mi_cache, pbi.segment_hdr); } else if (pbi.common.is_keyframe === true && pbi.segment_hdr.update_map === 0) { mi_cache.mbmi.segment_id = 0; } if (pbi.common.entropy_hdr.coeff_skip_enabled === 1) { mi_cache.mbmi.mb_skip_coeff = vpx_read(bool, pbi.common.entropy_hdr.coeff_skip_prob); } else { mi_cache.mbmi.mb_skip_coeff = 0; } mi_cache.mbmi.is_4x4 = 0; if (pbi.common.is_keyframe === true) { read_kf_modes(pbi, mi, this_off, bool); } else { read_mb_modes_mv(pbi, mi, this_off, bool, bounds); } } function read_mvcontexts(bc, mvc) { var j = 0; for (var i = 0; i < 2; i++) for (j = 0; j < MV_PROB_CNT; j++) if (vpx_read(bc, vp8_mv_update_probs[i][j])) { var x = bc.get_uint(7); if (x > 0) { mvc[i][j] = x << 1; } else { mvc[i][j] = 1; } } } function mb_mode_mv_init(pbi) { var bc = pbi.boolDecoder; var entropy_hdr = pbi.common.entropy_hdr; var bool = bc; var i = 0, j = 0, k = 0, l = 0; var x = 0; var coeff_probs = entropy_hdr.coeff_probs; /* Read coefficient probability updates */ for (var i = 0; i < 1056; i++) { if (vpx_read(bool, vp8_coef_update_probs[i]) === 1) coeff_probs[i] = bool.get_uint(8); } /* Read coefficient skip mode probability */ entropy_hdr.coeff_skip_enabled = vpx_read_bit(bool); if (entropy_hdr.coeff_skip_enabled === 1) entropy_hdr.coeff_skip_prob = bool.get_uint(8); else entropy_hdr.coeff_skip_prob = 0; /* Parse interframe probability updates */ if (pbi.common.is_keyframe === false) { entropy_hdr.prob_inter = bool.get_uint(8); entropy_hdr.prob_last = bool.get_uint(8); entropy_hdr.prob_gf = bool.get_uint(8); if (vpx_read_bit(bool) === 1) { entropy_hdr.y_mode_probs[0] = bool.get_uint(8); entropy_hdr.y_mode_probs[1] = bool.get_uint(8); entropy_hdr.y_mode_probs[2] = bool.get_uint(8); entropy_hdr.y_mode_probs[3] = bool.get_uint(8); } if (vpx_read_bit(bool) === 1) { entropy_hdr.uv_mode_probs[0] = bool.get_uint(8); entropy_hdr.uv_mode_probs[1] = bool.get_uint(8); entropy_hdr.uv_mode_probs[2] = bool.get_uint(8); } read_mvcontexts(bc, entropy_hdr.mv_probs); } } var bounds = { mb_to_left_edge: 0, mb_to_right_edge: 0, mb_to_top_edge: 0, mb_to_bottom_edge: 0 }; function vp8_decode_mode_mvs(pbi, bool) { var mb_row = -1; var mb_rows = pbi.mb_rows; var mb_cols = pbi.mb_cols; var start_col = 0; var mb_mb_to_right_edge_edge_start; bounds.mb_to_left_edge = 0; bounds.mb_to_right_edge = 0; bounds.mb_to_top_edge = 0; bounds.mb_to_bottom_edge = 0; mb_mode_mv_init(pbi); bounds.mb_to_top_edge = 0; while (++mb_row < mb_rows) { var mb_col = -1; var above_off = 0, this_, this_off = 0; this_ = pbi.mb_info_rows; this_off = pbi.mb_info_rows_off[1 + mb_row]; above_off = pbi.mb_info_rows_off[mb_row]; // Calculate the eighth-pel MV bounds using a 1 MB border. bounds.mb_to_left_edge = -((1) << 7); bounds.mb_to_right_edge = (pbi.mb_cols) << 7; bounds.mb_to_top_edge = -((mb_row + 1) << 7); bounds.mb_to_bottom_edge = (pbi.mb_rows - mb_row) << 7; while (++mb_col < mb_cols) { decode_mb_mode_mvs(pbi, bool, this_, this_off, bounds); if (pbi.common.is_keyframe === true) { } else { bounds.mb_to_left_edge -= 16 << 3; bounds.mb_to_right_edge -= 16 << 3; } // Advance to next mb this_off++; // probably mi_ptr; above_off++; } } } module.exports = {}; module.exports.read_mb_features = read_mb_features; module.exports.read_kf_modes = read_kf_modes; module.exports.vp8_decode_mode_mvs = vp8_decode_mode_mvs;