michael@0: michael@0: /* michael@0: * Copyright (c) 2012 The WebM project authors. All Rights Reserved. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license michael@0: * that can be found in the LICENSE file in the root of the source michael@0: * tree. An additional intellectual property rights grant can be found michael@0: * in the file PATENTS. All contributing project authors may michael@0: * be found in the AUTHORS file in the root of the source tree. michael@0: */ michael@0: michael@0: #include "vp9/common/vp9_mvref_common.h" michael@0: michael@0: #define MVREF_NEIGHBOURS 8 michael@0: michael@0: typedef enum { michael@0: BOTH_ZERO = 0, michael@0: ZERO_PLUS_PREDICTED = 1, michael@0: BOTH_PREDICTED = 2, michael@0: NEW_PLUS_NON_INTRA = 3, michael@0: BOTH_NEW = 4, michael@0: INTRA_PLUS_NON_INTRA = 5, michael@0: BOTH_INTRA = 6, michael@0: INVALID_CASE = 9 michael@0: } motion_vector_context; michael@0: michael@0: // This is used to figure out a context for the ref blocks. The code flattens michael@0: // an array that would have 3 possible counts (0, 1 & 2) for 3 choices by michael@0: // adding 9 for each intra block, 3 for each zero mv and 1 for each new michael@0: // motion vector. This single number is then converted into a context michael@0: // with a single lookup ( counter_to_context ). michael@0: static const int mode_2_counter[MB_MODE_COUNT] = { michael@0: 9, // DC_PRED michael@0: 9, // V_PRED michael@0: 9, // H_PRED michael@0: 9, // D45_PRED michael@0: 9, // D135_PRED michael@0: 9, // D117_PRED michael@0: 9, // D153_PRED michael@0: 9, // D207_PRED michael@0: 9, // D63_PRED michael@0: 9, // TM_PRED michael@0: 0, // NEARESTMV michael@0: 0, // NEARMV michael@0: 3, // ZEROMV michael@0: 1, // NEWMV michael@0: }; michael@0: michael@0: // There are 3^3 different combinations of 3 counts that can be either 0,1 or michael@0: // 2. However the actual count can never be greater than 2 so the highest michael@0: // counter we need is 18. 9 is an invalid counter that's never used. michael@0: static const int counter_to_context[19] = { michael@0: BOTH_PREDICTED, // 0 michael@0: NEW_PLUS_NON_INTRA, // 1 michael@0: BOTH_NEW, // 2 michael@0: ZERO_PLUS_PREDICTED, // 3 michael@0: NEW_PLUS_NON_INTRA, // 4 michael@0: INVALID_CASE, // 5 michael@0: BOTH_ZERO, // 6 michael@0: INVALID_CASE, // 7 michael@0: INVALID_CASE, // 8 michael@0: INTRA_PLUS_NON_INTRA, // 9 michael@0: INTRA_PLUS_NON_INTRA, // 10 michael@0: INVALID_CASE, // 11 michael@0: INTRA_PLUS_NON_INTRA, // 12 michael@0: INVALID_CASE, // 13 michael@0: INVALID_CASE, // 14 michael@0: INVALID_CASE, // 15 michael@0: INVALID_CASE, // 16 michael@0: INVALID_CASE, // 17 michael@0: BOTH_INTRA // 18 michael@0: }; michael@0: michael@0: static const MV mv_ref_blocks[BLOCK_SIZES][MVREF_NEIGHBOURS] = { michael@0: // 4X4 michael@0: {{-1, 0}, {0, -1}, {-1, -1}, {-2, 0}, {0, -2}, {-2, -1}, {-1, -2}, {-2, -2}}, michael@0: // 4X8 michael@0: {{-1, 0}, {0, -1}, {-1, -1}, {-2, 0}, {0, -2}, {-2, -1}, {-1, -2}, {-2, -2}}, michael@0: // 8X4 michael@0: {{-1, 0}, {0, -1}, {-1, -1}, {-2, 0}, {0, -2}, {-2, -1}, {-1, -2}, {-2, -2}}, michael@0: // 8X8 michael@0: {{-1, 0}, {0, -1}, {-1, -1}, {-2, 0}, {0, -2}, {-2, -1}, {-1, -2}, {-2, -2}}, michael@0: // 8X16 michael@0: {{0, -1}, {-1, 0}, {1, -1}, {-1, -1}, {0, -2}, {-2, 0}, {-2, -1}, {-1, -2}}, michael@0: // 16X8 michael@0: {{-1, 0}, {0, -1}, {-1, 1}, {-1, -1}, {-2, 0}, {0, -2}, {-1, -2}, {-2, -1}}, michael@0: // 16X16 michael@0: {{-1, 0}, {0, -1}, {-1, 1}, {1, -1}, {-1, -1}, {-3, 0}, {0, -3}, {-3, -3}}, michael@0: // 16X32 michael@0: {{0, -1}, {-1, 0}, {2, -1}, {-1, -1}, {-1, 1}, {0, -3}, {-3, 0}, {-3, -3}}, michael@0: // 32X16 michael@0: {{-1, 0}, {0, -1}, {-1, 2}, {-1, -1}, {1, -1}, {-3, 0}, {0, -3}, {-3, -3}}, michael@0: // 32X32 michael@0: {{-1, 1}, {1, -1}, {-1, 2}, {2, -1}, {-1, -1}, {-3, 0}, {0, -3}, {-3, -3}}, michael@0: // 32X64 michael@0: {{0, -1}, {-1, 0}, {4, -1}, {-1, 2}, {-1, -1}, {0, -3}, {-3, 0}, {2, -1}}, michael@0: // 64X32 michael@0: {{-1, 0}, {0, -1}, {-1, 4}, {2, -1}, {-1, -1}, {-3, 0}, {0, -3}, {-1, 2}}, michael@0: // 64X64 michael@0: {{-1, 3}, {3, -1}, {-1, 4}, {4, -1}, {-1, -1}, {-1, 0}, {0, -1}, {-1, 6}} michael@0: }; michael@0: michael@0: static const int idx_n_column_to_subblock[4][2] = { michael@0: {1, 2}, michael@0: {1, 3}, michael@0: {3, 2}, michael@0: {3, 3} michael@0: }; michael@0: michael@0: // clamp_mv_ref michael@0: #define MV_BORDER (16 << 3) // Allow 16 pels in 1/8th pel units michael@0: michael@0: static void clamp_mv_ref(MV *mv, const MACROBLOCKD *xd) { michael@0: clamp_mv(mv, xd->mb_to_left_edge - MV_BORDER, michael@0: xd->mb_to_right_edge + MV_BORDER, michael@0: xd->mb_to_top_edge - MV_BORDER, michael@0: xd->mb_to_bottom_edge + MV_BORDER); michael@0: } michael@0: michael@0: // This function returns either the appropriate sub block or block's mv michael@0: // on whether the block_size < 8x8 and we have check_sub_blocks set. michael@0: static INLINE int_mv get_sub_block_mv(const MODE_INFO *candidate, int which_mv, michael@0: int search_col, int block_idx) { michael@0: return block_idx >= 0 && candidate->mbmi.sb_type < BLOCK_8X8 michael@0: ? candidate->bmi[idx_n_column_to_subblock[block_idx][search_col == 0]] michael@0: .as_mv[which_mv] michael@0: : candidate->mbmi.mv[which_mv]; michael@0: } michael@0: michael@0: michael@0: // Performs mv sign inversion if indicated by the reference frame combination. michael@0: static INLINE int_mv scale_mv(const MB_MODE_INFO *mbmi, int ref, michael@0: const MV_REFERENCE_FRAME this_ref_frame, michael@0: const int *ref_sign_bias) { michael@0: int_mv mv = mbmi->mv[ref]; michael@0: if (ref_sign_bias[mbmi->ref_frame[ref]] != ref_sign_bias[this_ref_frame]) { michael@0: mv.as_mv.row *= -1; michael@0: mv.as_mv.col *= -1; michael@0: } michael@0: return mv; michael@0: } michael@0: michael@0: // This macro is used to add a motion vector mv_ref list if it isn't michael@0: // already in the list. If it's the second motion vector it will also michael@0: // skip all additional processing and jump to done! michael@0: #define ADD_MV_REF_LIST(MV) \ michael@0: do { \ michael@0: if (refmv_count) { \ michael@0: if ((MV).as_int != mv_ref_list[0].as_int) { \ michael@0: mv_ref_list[refmv_count] = (MV); \ michael@0: goto Done; \ michael@0: } \ michael@0: } else { \ michael@0: mv_ref_list[refmv_count++] = (MV); \ michael@0: } \ michael@0: } while (0) michael@0: michael@0: // If either reference frame is different, not INTRA, and they michael@0: // are different from each other scale and add the mv to our list. michael@0: #define IF_DIFF_REF_FRAME_ADD_MV(CANDIDATE) \ michael@0: do { \ michael@0: if ((CANDIDATE)->ref_frame[0] != ref_frame) \ michael@0: ADD_MV_REF_LIST(scale_mv((CANDIDATE), 0, ref_frame, ref_sign_bias)); \ michael@0: if ((CANDIDATE)->ref_frame[1] != ref_frame && \ michael@0: has_second_ref(CANDIDATE) && \ michael@0: (CANDIDATE)->mv[1].as_int != (CANDIDATE)->mv[0].as_int) \ michael@0: ADD_MV_REF_LIST(scale_mv((CANDIDATE), 1, ref_frame, ref_sign_bias)); \ michael@0: } while (0) michael@0: michael@0: michael@0: // Checks that the given mi_row, mi_col and search point michael@0: // are inside the borders of the tile. michael@0: static INLINE int is_inside(const TileInfo *const tile, michael@0: int mi_col, int mi_row, int mi_rows, michael@0: const MV *mv) { michael@0: return !(mi_row + mv->row < 0 || michael@0: mi_col + mv->col < tile->mi_col_start || michael@0: mi_row + mv->row >= mi_rows || michael@0: mi_col + mv->col >= tile->mi_col_end); michael@0: } michael@0: michael@0: // This function searches the neighbourhood of a given MB/SB michael@0: // to try and find candidate reference vectors. michael@0: void vp9_find_mv_refs_idx(const VP9_COMMON *cm, const MACROBLOCKD *xd, michael@0: const TileInfo *const tile, michael@0: MODE_INFO *mi, const MODE_INFO *prev_mi, michael@0: MV_REFERENCE_FRAME ref_frame, michael@0: int_mv *mv_ref_list, michael@0: int block_idx, michael@0: int mi_row, int mi_col) { michael@0: const int *ref_sign_bias = cm->ref_frame_sign_bias; michael@0: int i, refmv_count = 0; michael@0: const MV *const mv_ref_search = mv_ref_blocks[mi->mbmi.sb_type]; michael@0: const MB_MODE_INFO *const prev_mbmi = prev_mi ? &prev_mi->mbmi : NULL; michael@0: int different_ref_found = 0; michael@0: int context_counter = 0; michael@0: michael@0: // Blank the reference vector list michael@0: vpx_memset(mv_ref_list, 0, sizeof(*mv_ref_list) * MAX_MV_REF_CANDIDATES); michael@0: michael@0: // The nearest 2 blocks are treated differently michael@0: // if the size < 8x8 we get the mv from the bmi substructure, michael@0: // and we also need to keep a mode count. michael@0: for (i = 0; i < 2; ++i) { michael@0: const MV *const mv_ref = &mv_ref_search[i]; michael@0: if (is_inside(tile, mi_col, mi_row, cm->mi_rows, mv_ref)) { michael@0: const MODE_INFO *const candidate_mi = xd->mi_8x8[mv_ref->col + mv_ref->row michael@0: * xd->mode_info_stride]; michael@0: const MB_MODE_INFO *const candidate = &candidate_mi->mbmi; michael@0: // Keep counts for entropy encoding. michael@0: context_counter += mode_2_counter[candidate->mode]; michael@0: michael@0: // Check if the candidate comes from the same reference frame. michael@0: if (candidate->ref_frame[0] == ref_frame) { michael@0: ADD_MV_REF_LIST(get_sub_block_mv(candidate_mi, 0, michael@0: mv_ref->col, block_idx)); michael@0: different_ref_found = candidate->ref_frame[1] != ref_frame; michael@0: } else { michael@0: if (candidate->ref_frame[1] == ref_frame) michael@0: // Add second motion vector if it has the same ref_frame. michael@0: ADD_MV_REF_LIST(get_sub_block_mv(candidate_mi, 1, michael@0: mv_ref->col, block_idx)); michael@0: different_ref_found = 1; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Check the rest of the neighbors in much the same way michael@0: // as before except we don't need to keep track of sub blocks or michael@0: // mode counts. michael@0: for (; i < MVREF_NEIGHBOURS; ++i) { michael@0: const MV *const mv_ref = &mv_ref_search[i]; michael@0: if (is_inside(tile, mi_col, mi_row, cm->mi_rows, mv_ref)) { michael@0: const MB_MODE_INFO *const candidate = &xd->mi_8x8[mv_ref->col + michael@0: mv_ref->row michael@0: * xd->mode_info_stride]->mbmi; michael@0: michael@0: if (candidate->ref_frame[0] == ref_frame) { michael@0: ADD_MV_REF_LIST(candidate->mv[0]); michael@0: different_ref_found = candidate->ref_frame[1] != ref_frame; michael@0: } else { michael@0: if (candidate->ref_frame[1] == ref_frame) michael@0: ADD_MV_REF_LIST(candidate->mv[1]); michael@0: different_ref_found = 1; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Check the last frame's mode and mv info. michael@0: if (prev_mbmi) { michael@0: if (prev_mbmi->ref_frame[0] == ref_frame) michael@0: ADD_MV_REF_LIST(prev_mbmi->mv[0]); michael@0: else if (prev_mbmi->ref_frame[1] == ref_frame) michael@0: ADD_MV_REF_LIST(prev_mbmi->mv[1]); michael@0: } michael@0: michael@0: // Since we couldn't find 2 mvs from the same reference frame michael@0: // go back through the neighbors and find motion vectors from michael@0: // different reference frames. michael@0: if (different_ref_found) { michael@0: for (i = 0; i < MVREF_NEIGHBOURS; ++i) { michael@0: const MV *mv_ref = &mv_ref_search[i]; michael@0: if (is_inside(tile, mi_col, mi_row, cm->mi_rows, mv_ref)) { michael@0: const MB_MODE_INFO *const candidate = &xd->mi_8x8[mv_ref->col + michael@0: mv_ref->row michael@0: * xd->mode_info_stride]->mbmi; michael@0: michael@0: // If the candidate is INTRA we don't want to consider its mv. michael@0: if (is_inter_block(candidate)) michael@0: IF_DIFF_REF_FRAME_ADD_MV(candidate); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Since we still don't have a candidate we'll try the last frame. michael@0: if (prev_mbmi && is_inter_block(prev_mbmi)) michael@0: IF_DIFF_REF_FRAME_ADD_MV(prev_mbmi); michael@0: michael@0: Done: michael@0: michael@0: mi->mbmi.mode_context[ref_frame] = counter_to_context[context_counter]; michael@0: michael@0: // Clamp vectors michael@0: for (i = 0; i < MAX_MV_REF_CANDIDATES; ++i) michael@0: clamp_mv_ref(&mv_ref_list[i].as_mv, xd); michael@0: }