shithub: libvpx

Download patch

ref: 2d60bee1fbfb7e5b6862e4e636151effabae98e3
parent: 3777125ba66d15fef1436ea8871b3901a86c6991
author: Paul Wilkins <paulwilkins@google.com>
date: Fri Aug 24 11:44:01 EDT 2012

New Motion Reference Search

Alternative strategy for finding a list of candidate motion
vectors to use as reference values in mv coding and as
nearest and near.

Sort by sad in vp8_find_best_ref_mvs() rather than just
pick the best. Allow 0,0 as a best ref option but not a
nearest or near unless there are no alternatives.

Encode/Decode verified on at least some clips.

Some commented out experimental and stats code still in place.

Gain over existing code averages about 1% on derf (alll metrics)
with improvement on all clips. Other test results pending.

The entropy coding of the mode (nearest/near etc) still
depends upon and requires the old "findnear" code so
this needs looking at and may provide room for further gains.

Change-Id: I871d7cba1d1c379c4bad9bcccce1fb19c46b8247

--- a/configure
+++ b/configure
@@ -226,6 +226,7 @@
     switchable_interp
     tx16x16
     newbestrefmv
+    new_mvref
 "
 CONFIG_LIST="
     external_build
--- a/vp8/common/blockd.h
+++ b/vp8/common/blockd.h
@@ -44,6 +44,9 @@
 /* Segment Feature Masks */
 #define SEGMENT_DELTADATA   0
 #define SEGMENT_ABSDATA     1
+#if CONFIG_NEW_MVREF
+#define MAX_MV_REFS 10
+#endif
 
 typedef struct {
   int r, c;
@@ -179,6 +182,14 @@
   B_MODE_COUNT
 } B_PREDICTION_MODE;
 
+#if CONFIG_NEW_MVREF
+// Segment level features.
+typedef enum {
+  FIRST_REF = 0,
+  SECOND_REF = 1
+} MV_REF_TYPE;
+#endif
+
 #if CONFIG_HYBRIDTRANSFORM8X8
 // convert MB_PREDICTION_MODE to B_PREDICTION_MODE
 static B_PREDICTION_MODE pred_mode_conv(MB_PREDICTION_MODE mode) {
@@ -268,9 +279,14 @@
   MV_REFERENCE_FRAME ref_frame, second_ref_frame;
   TX_SIZE txfm_size;
   int_mv mv[2]; // for each reference frame used
-#if CONFIG_NEWBESTREFMV
+#if CONFIG_NEWBESTREFMV || CONFIG_NEW_MVREF
   int_mv ref_mv, second_ref_mv;
 #endif
+#if CONFIG_NEW_MVREF
+  int_mv ref_mvs[MAX_REF_FRAMES][MAX_MV_REFS];
+  int mv_ref_index[MAX_REF_FRAMES];
+#endif
+
   unsigned char partitioning;
   unsigned char mb_skip_coeff;                                /* does this mb has coefficients at all, 1=no coefficients, 0=need decode tokens */
   unsigned char need_to_clamp_mvs;
@@ -432,8 +448,13 @@
 #endif
 
   int mb_index;   // Index of the MB in the SB (0..3)
+
 #if CONFIG_NEWBESTREFMV
+#if CONFIG_NEW_MVREF
+  int_mv ref_mv[MAX_MV_REFS];
+#else
   int_mv ref_mv[4];
+#endif
 #endif
 
 #if CONFIG_HYBRIDTRANSFORM
--- a/vp8/common/findnearmv.c
+++ b/vp8/common/findnearmv.c
@@ -200,6 +200,9 @@
  * above and a number cols of pixels in the left to select the one with best
  * score to use as ref motion vector
  */
+
+#if CONFIG_NEW_MVREF
+
 void vp8_find_best_ref_mvs(MACROBLOCKD *xd,
                            unsigned char *ref_y_buffer,
                            int ref_y_stride,
@@ -207,6 +210,136 @@
                            int_mv *nearest,
                            int_mv *near) {
   int_mv *ref_mv = xd->ref_mv;
+  int i, j;
+  unsigned char *above_src;
+  unsigned char *left_src;
+  unsigned char *above_ref;
+  unsigned char *left_ref;
+  int sad;
+  int sad_scores[MAX_MV_REFS];
+  int_mv sorted_mvs[MAX_MV_REFS];
+  int zero_seen = FALSE;
+
+  // Default all to 0,0 if nothing else available
+  best_mv->as_int = nearest->as_int = near->as_int = 0;
+  vpx_memset(sorted_mvs, 0, sizeof(sorted_mvs));
+
+  above_src = xd->dst.y_buffer - xd->dst.y_stride * 2;
+  left_src  = xd->dst.y_buffer - 2;
+  above_ref = ref_y_buffer - ref_y_stride * 2;
+  left_ref  = ref_y_buffer - 2;
+
+  for(i = 0; i < MAX_MV_REFS; ++i) {
+    int_mv this_mv;
+    int offset=0;
+    int row_offset, col_offset;
+
+    this_mv.as_int = ref_mv[i].as_int;
+
+    // If we see a 0,0 vector for a second time we have reached the end of
+    // the list of valid candidate vectors.
+    if (!this_mv.as_int)
+      if (zero_seen)
+        break;
+      else
+        zero_seen = TRUE;
+
+    vp8_clamp_mv(&this_mv,
+                 xd->mb_to_left_edge - LEFT_TOP_MARGIN + 16,
+                 xd->mb_to_right_edge + RIGHT_BOTTOM_MARGIN,
+                 xd->mb_to_top_edge - LEFT_TOP_MARGIN + 16,
+                 xd->mb_to_bottom_edge + RIGHT_BOTTOM_MARGIN);
+
+    row_offset = (this_mv.as_mv.row > 0) ?
+      ((this_mv.as_mv.row + 3) >> 3):((this_mv.as_mv.row + 4) >> 3);
+    col_offset = (this_mv.as_mv.col > 0) ?
+      ((this_mv.as_mv.col + 3) >> 3):((this_mv.as_mv.col + 4) >> 3);
+    offset = ref_y_stride * row_offset + col_offset;
+
+    sad = vp8_sad16x2_c(above_src, xd->dst.y_stride,
+                        above_ref + offset, ref_y_stride, INT_MAX);
+
+    sad += vp8_sad2x16_c(left_src, xd->dst.y_stride,
+                         left_ref + offset, ref_y_stride, INT_MAX);
+
+    // Add the entry to our list and then resort the list on score.
+    sad_scores[i] = sad;
+    sorted_mvs[i].as_int = this_mv.as_int;
+    j = i;
+    while (j > 0) {
+      if (sad_scores[j] < sad_scores[j-1]) {
+        sad_scores[j] = sad_scores[j-1];
+        sorted_mvs[j].as_int = sorted_mvs[j-1].as_int;
+        sad_scores[j-1] = sad;
+        sorted_mvs[j-1].as_int = this_mv.as_int;
+        j--;
+      } else
+        break;
+    }
+  }
+
+  // If not see add 0,0 as a possibility
+  /*if ( (i < MAX_MV_REFS) && !zero_seen ) {
+
+    sad = vp8_sad16x2_c(above_src, xd->dst.y_stride,
+                        above_ref, ref_y_stride,
+                        INT_MAX);
+    sad += vp8_sad2x16_c(left_src, xd->dst.y_stride,
+                         left_ref, ref_y_stride,
+                         INT_MAX);
+    this_mv.as_int = 0;
+
+    // Add the entry to our list and then resort the list on score.
+    sad_scores[i] = sad;
+    sorted_mvs[i].as_int = this_mv.as_int;
+    j = i;
+    while (j > 0) {
+      if (sad_scores[j] < sad_scores[j-1]) {
+        sad_scores[j] = sad_scores[j-1];
+        sorted_mvs[j].as_int = sorted_mvs[j-1].as_int;
+        sad_scores[j-1] = sad;
+        sorted_mvs[j-1].as_int = this_mv.as_int;
+        j--;
+      } else
+        break;
+    }
+  }*/
+
+  // Set the best mv to the first entry in the sorted list
+  best_mv->as_int = sorted_mvs[0].as_int;
+
+  // Provided that there are non zero vectors available there will not
+  // be more than one 0,0 entry in the sorted list.
+  // The best ref mv is always set to the first entry (which gave the best
+  // results. The nearest is set to the first non zero vector if available and
+  // near to the second non zero vector if avaialable.
+  // We do not use 0,0 as a nearest or near as 0,0 has its own mode.
+  if ( sorted_mvs[0].as_int ) {
+    nearest->as_int = sorted_mvs[0].as_int;
+    if ( sorted_mvs[1].as_int )
+      near->as_int = sorted_mvs[1].as_int;
+    else
+      near->as_int = sorted_mvs[2].as_int;
+  } else {
+      nearest->as_int = sorted_mvs[1].as_int;
+      near->as_int = sorted_mvs[2].as_int;
+  }
+
+  if (!xd->allow_high_precision_mv)
+    lower_mv_precision(best_mv);
+
+  vp8_clamp_mv2(best_mv, xd);
+}
+
+#else // !CONFIG_NEW_MVREF
+
+void vp8_find_best_ref_mvs(MACROBLOCKD *xd,
+                           unsigned char *ref_y_buffer,
+                           int ref_y_stride,
+                           int_mv *best_mv,
+                           int_mv *nearest,
+                           int_mv *near) {
+  int_mv *ref_mv = xd->ref_mv;
   int bestsad = INT_MAX;
   int i;
   unsigned char *above_src;
@@ -270,5 +403,5 @@
     nearest->as_int = best_mv->as_int;
   }
 }
-
-#endif
+#endif  // CONFIG_NEW_MVREF
+#endif  // CONFIG_NEWBESTREFMV
--- /dev/null
+++ b/vp8/common/mvref_common.c
@@ -1,0 +1,303 @@
+/*
+ *  Copyright (c) 2012 The WebM project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "mvref_common.h"
+
+#if CONFIG_NEW_MVREF
+
+#define MVREF_NEIGHBOURS 8
+static int mv_ref_search[MVREF_NEIGHBOURS][2] =
+  { {0,-1},{-1,0},{-1,-1},{0,-2},{-2,0},{-1,-2},{-2,-1},{-2,-2} };
+static int ref_distance_weight[MVREF_NEIGHBOURS] =
+  { 3,3,2,1,1,1,1,1 };
+  //{ 4,4,2,1,1,1,1,1 };
+
+// clamp_mv
+#define MV_BORDER (16 << 3) // Allow 16 pels in 1/8th pel units
+static void clamp_mv(const MACROBLOCKD *xd, int_mv *mv) {
+
+  if (mv->as_mv.col < (xd->mb_to_left_edge - MV_BORDER))
+    mv->as_mv.col = xd->mb_to_left_edge - MV_BORDER;
+  else if (mv->as_mv.col > xd->mb_to_right_edge + MV_BORDER)
+    mv->as_mv.col = xd->mb_to_right_edge + MV_BORDER;
+
+  if (mv->as_mv.row < (xd->mb_to_top_edge - MV_BORDER))
+    mv->as_mv.row = xd->mb_to_top_edge - MV_BORDER;
+  else if (mv->as_mv.row > xd->mb_to_bottom_edge + MV_BORDER)
+    mv->as_mv.row = xd->mb_to_bottom_edge + MV_BORDER;
+}
+
+// Code for selecting / building and entropy coding a motion vector reference
+// Returns a seperation value for two vectors.
+// This is taken as the sum of the abs x and y difference.
+unsigned int mv_distance(int_mv *mv1, int_mv *mv2) {
+  return (abs(mv1->as_mv.row - mv2->as_mv.row) +
+          abs(mv1->as_mv.col - mv2->as_mv.col));
+}
+
+// Gets a best matching candidate refenence motion vector
+// from the given mode info structure (if available)
+int get_candidate_mvref(
+  const MODE_INFO *candidate_mi,
+  MV_REFERENCE_FRAME ref_frame,
+  MV_REFERENCE_FRAME *candidate_ref_frame,
+  int_mv *candidate_mv
+) {
+
+  int ret_val = FALSE;
+
+  if (ref_frame == candidate_mi->mbmi.ref_frame) {
+    candidate_mv->as_int = candidate_mi->mbmi.mv[FIRST_REF].as_int;
+    *candidate_ref_frame = ref_frame;
+    ret_val = TRUE;
+
+  } else if (ref_frame == candidate_mi->mbmi.second_ref_frame) {
+    candidate_mv->as_int = candidate_mi->mbmi.mv[SECOND_REF].as_int;
+    *candidate_ref_frame = ref_frame;
+    ret_val = TRUE;
+
+  } else if (candidate_mi->mbmi.ref_frame != INTRA_FRAME) {
+    candidate_mv->as_int = candidate_mi->mbmi.mv[FIRST_REF].as_int;
+    *candidate_ref_frame = candidate_mi->mbmi.ref_frame;
+    ret_val = TRUE;
+
+  } else if (candidate_mi->mbmi.second_ref_frame != INTRA_FRAME) {
+    candidate_mv->as_int = candidate_mi->mbmi.mv[SECOND_REF].as_int;
+    *candidate_ref_frame = candidate_mi->mbmi.second_ref_frame;
+    ret_val = TRUE;
+  }
+
+  return ret_val;
+}
+
+// Performs mv adjustment based on reference frame and clamps the MV
+// if it goes off the edge of the buffer.
+void scale_mv(
+  MACROBLOCKD *xd,
+  MV_REFERENCE_FRAME this_ref_frame,
+  MV_REFERENCE_FRAME candidate_ref_frame,
+  int_mv *candidate_mv,
+  int *ref_sign_bias
+) {
+
+  if (candidate_ref_frame != this_ref_frame) {
+
+    //int frame_distances[MAX_REF_FRAMES];
+    //int last_distance = 1;
+    //int gf_distance = xd->frames_since_golden;
+    //int arf_distance = xd->frames_till_alt_ref_frame;
+
+    // Sign inversion where appropriate.
+    if (ref_sign_bias[candidate_ref_frame] != ref_sign_bias[this_ref_frame]) {
+      candidate_mv->as_mv.row = -candidate_mv->as_mv.row;
+      candidate_mv->as_mv.col = -candidate_mv->as_mv.col;
+    }
+
+    // Scale based on frame distance if the reference frames not the same.
+    /*frame_distances[INTRA_FRAME] = 1;   // should never be used
+    frame_distances[LAST_FRAME] = 1;
+    frame_distances[GOLDEN_FRAME] =
+      (xd->frames_since_golden) ? xd->frames_since_golden : 1;
+    frame_distances[ALTREF_FRAME] =
+      (xd->frames_till_alt_ref_frame) ? xd->frames_till_alt_ref_frame : 1;
+
+    if (frame_distances[this_ref_frame] &&
+        frame_distances[candidate_ref_frame]) {
+      candidate_mv->as_mv.row =
+        (short)(((int)(candidate_mv->as_mv.row) *
+                 frame_distances[this_ref_frame]) /
+                frame_distances[candidate_ref_frame]);
+
+      candidate_mv->as_mv.col =
+        (short)(((int)(candidate_mv->as_mv.col) *
+                 frame_distances[this_ref_frame]) /
+                frame_distances[candidate_ref_frame]);
+    }
+    */
+  }
+
+  // Clamp the MV so it does not point out of the frame buffer
+  clamp_mv(xd, candidate_mv);
+}
+
+// Adds a new candidate reference vector to the list if indeed it is new.
+// If it is not new then the score of the existing candidate that it matches
+// is increased and the list is resorted.
+void addmv_and_shuffle(
+  int_mv *mv_list,
+  int *mv_scores,
+  int *index,
+  int_mv candidate_mv,
+  int weight
+) {
+
+  int i = *index;
+  int duplicate_found = FALSE;
+
+  // Check for duplicates. If there is one increment its score.
+  while (i > 0) {
+    i--;
+    if (candidate_mv.as_int == mv_list[i].as_int) {
+      duplicate_found = TRUE;
+      mv_scores[i] += weight;
+      break;
+    }
+  }
+
+  // If no duplicate was found add the new vector and give it a weight
+  if (!duplicate_found) {
+    mv_list[*index].as_int = candidate_mv.as_int;
+    mv_scores[*index] = weight;
+    i = *index;
+    (*index)++;
+  }
+
+  // Reshuffle the list so that highest scoring mvs at the top.
+  while (i > 0) {
+    if (mv_scores[i] > mv_scores[i-1]) {
+      int tmp_score = mv_scores[i-1];
+      int_mv tmp_mv = mv_list[i-1];
+
+      mv_scores[i-1] = mv_scores[i];
+      mv_list[i-1] = mv_list[i];
+      mv_scores[i] = tmp_score;
+      mv_list[i] = tmp_mv;
+      i--;
+    } else
+      break;
+  }
+}
+
+
+// Measure the distance of each reference candidate from the actual
+// residual vector and return the nearest
+unsigned int pick_best_mv_ref( int_mv target_mv,
+                               int_mv * mv_ref_list,
+                               int_mv * best_ref ) {
+
+  int i;
+  int best_index = 0;
+  unsigned int distance, distance2;
+
+  distance = mv_distance(&target_mv, &mv_ref_list[0]);
+
+  for (i = 1; i < MAX_MV_REFS; ++i ) {
+    distance2 =
+      mv_distance(&target_mv, &mv_ref_list[i]);
+    if (distance2 < distance) {
+      distance = distance2;
+      best_index = i;
+    }
+  }
+
+  (*best_ref).as_int = mv_ref_list[best_index].as_int;
+
+  return best_index;
+}
+
+// This function searches the neighbourhood of a given MB/SB and populates a
+// list of candidate reference vectors.
+//
+void find_mv_refs(
+  MACROBLOCKD *xd,
+  MODE_INFO *here,
+  MODE_INFO *lf_here,
+  MV_REFERENCE_FRAME ref_frame,
+  int_mv *mv_ref_list,
+  int *ref_sign_bias
+) {
+
+  int i;
+  MODE_INFO *candidate_mi;
+  int_mv candidate_mvs[MAX_MV_REFS];
+  int_mv c_refmv;
+  MV_REFERENCE_FRAME c_ref_frame;
+  int candidate_scores[MAX_MV_REFS];
+  int index = 0;
+  int ref_weight = 0;
+  int valid_mv_ref;
+
+  // Blank the reference vector lists and other local structures.
+  vpx_memset(mv_ref_list, 0, sizeof(int_mv) * MAX_MV_REFS);
+  vpx_memset(candidate_mvs, 0, sizeof(int_mv) * MAX_MV_REFS);
+  vpx_memset(candidate_scores, 0, sizeof(candidate_scores));
+
+  // Populate a list with candidate reference vectors from the
+  // spatial neighbours.
+  for (i = 0; i < 2; ++i) {
+    if (((mv_ref_search[i][0] << 7) >= xd->mb_to_left_edge) &&
+        ((mv_ref_search[i][1] << 7) >= xd->mb_to_top_edge)) {
+
+      candidate_mi = here + mv_ref_search[i][0] +
+                     (mv_ref_search[i][1] * xd->mode_info_stride);
+
+      valid_mv_ref = get_candidate_mvref(candidate_mi, ref_frame,
+                                         &c_ref_frame, &c_refmv);
+
+      if (valid_mv_ref) {
+          scale_mv(xd, ref_frame, c_ref_frame, &c_refmv, ref_sign_bias );
+          ref_weight = ref_distance_weight[i] +
+                       ((c_ref_frame == ref_frame) << 3);
+
+          addmv_and_shuffle(candidate_mvs, candidate_scores,
+                            &index, c_refmv, ref_weight);
+      }
+    }
+  }
+
+  // Look at the corresponding vector in the last frame
+  candidate_mi = lf_here;
+  valid_mv_ref = get_candidate_mvref(candidate_mi, ref_frame,
+                                     &c_ref_frame, &c_refmv);
+  if (valid_mv_ref) {
+    scale_mv(xd, ref_frame, c_ref_frame, &c_refmv, ref_sign_bias );
+    ref_weight = 2 + ((c_ref_frame == ref_frame) << 3);
+    addmv_and_shuffle(candidate_mvs, candidate_scores,
+                      &index, c_refmv, ref_weight);
+  }
+
+  // Populate a list with candidate reference vectors from the
+  // spatial neighbours.
+  for (i = 2; i < MVREF_NEIGHBOURS; ++i) {
+    if (((mv_ref_search[i][0] << 7) >= xd->mb_to_left_edge) &&
+        ((mv_ref_search[i][1] << 7) >= xd->mb_to_top_edge)) {
+
+      candidate_mi = here + mv_ref_search[i][0] +
+                     (mv_ref_search[i][1] * xd->mode_info_stride);
+
+      valid_mv_ref = get_candidate_mvref(candidate_mi, ref_frame,
+                                         &c_ref_frame, &c_refmv);
+
+      if (valid_mv_ref) {
+          scale_mv(xd, ref_frame, c_ref_frame, &c_refmv, ref_sign_bias );
+          ref_weight = ref_distance_weight[i] +
+                       ((c_ref_frame == ref_frame) << 3);
+
+          addmv_and_shuffle(candidate_mvs, candidate_scores,
+                            &index, c_refmv, ref_weight);
+      }
+    }
+  }
+
+  // 0,0 is always a valid reference.
+  for (i = 0; i < index; ++i)
+    if (candidate_mvs[i].as_int == 0)
+      break;
+  if (i == index) {
+    c_refmv.as_int = 0;
+    addmv_and_shuffle(candidate_mvs, candidate_scores,
+                      &index, c_refmv, 1);
+  }
+
+  // Copy over the candidate list.
+  vpx_memcpy(mv_ref_list, candidate_mvs, sizeof(candidate_mvs));
+}
+
+#endif
--- /dev/null
+++ b/vp8/common/mvref_common.h
@@ -1,0 +1,37 @@
+/*
+ *  Copyright (c) 2012 The WebM project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "onyxc_int.h"
+#include "blockd.h"
+
+// MR reference entropy header file.
+#if CONFIG_NEW_MVREF
+
+#ifndef __INC_MVREF_COMMON_H
+#define __INC_MVREF_COMMON_H
+
+unsigned int mv_distance(int_mv *mv1, int_mv *mv2);
+
+unsigned int pick_best_mv_ref( int_mv target_mv,
+                      int_mv * mv_ref_list,
+                      int_mv * best_ref );
+
+void find_mv_refs(
+  MACROBLOCKD *xd,
+  MODE_INFO *here,
+  MODE_INFO *lf_here,
+  MV_REFERENCE_FRAME ref_frame,
+  int_mv * mv_ref_list,
+  int *ref_sign_bias
+);
+
+#endif
+
+#endif
--- a/vp8/decoder/decodemv.c
+++ b/vp8/decoder/decodemv.c
@@ -657,6 +657,7 @@
                       prev_mi,
                       &nearest, &nearby, &best_mv, rct,
                       mbmi->ref_frame, cm->ref_frame_sign_bias);
+
 #if CONFIG_NEWBESTREFMV
     {
       int ref_fb_idx;
@@ -679,6 +680,23 @@
       xd->pre.u_buffer = cm->yv12_fb[ref_fb_idx].u_buffer + recon_uvoffset;
       xd->pre.v_buffer = cm->yv12_fb[ref_fb_idx].v_buffer + recon_uvoffset;
 
+#if CONFIG_NEW_MVREF
+      // Update stats on relative distance of chosen vector to the
+      // possible best reference vectors.
+      {
+        int i;
+        MV_REFERENCE_FRAME ref_frame = mbmi->ref_frame;
+
+        find_mv_refs(xd, mi, prev_mi,
+                     ref_frame, mbmi->ref_mvs[ref_frame],
+                     cm->ref_frame_sign_bias );
+
+        // Copy over the candidates.
+        vpx_memcpy(xd->ref_mv, mbmi->ref_mvs[ref_frame],
+                   (MAX_MV_REFS * sizeof(int_mv)) );
+      }
+#endif
+
       vp8_find_best_ref_mvs(xd,
                             xd->pre.y_buffer,
                             recon_y_stride,
@@ -763,6 +781,23 @@
                           rct,
                           mbmi->second_ref_frame,
                           cm->ref_frame_sign_bias);
+
+#if CONFIG_NEW_MVREF
+        // Update stats on relative distance of chosen vector to the
+        // possible best reference vectors.
+        {
+          MV_REFERENCE_FRAME ref_frame = mbmi->second_ref_frame;
+
+          find_mv_refs(xd, mi, prev_mi,
+                       ref_frame, mbmi->ref_mvs[ref_frame],
+                       cm->ref_frame_sign_bias );
+
+          // Copy over the mv candidates
+          vpx_memcpy(xd->ref_mv, mbmi->ref_mvs[ref_frame],
+                    (MAX_MV_REFS * sizeof(int_mv)) );
+        }
+#endif
+
         vp8_find_best_ref_mvs(xd,
                               xd->second_pre.y_buffer,
                               recon_y_stride,
--- a/vp8/encoder/bitstream.c
+++ b/vp8/encoder/bitstream.c
@@ -28,6 +28,10 @@
 #include "vp8/common/pred_common.h"
 #include "vp8/common/entropy.h"
 
+#if CONFIG_NEW_MVREF
+#include "vp8/common/mvref_common.h"
+#endif
+
 #if defined(SECTIONBITS_OUTPUT)
 unsigned __int64 Sectionbits[500];
 #endif
@@ -1043,6 +1047,14 @@
                 active_section = 5;
 #endif
 
+#if 0 //CONFIG_NEW_MVREF
+                find_mv_refs(xd, m, prev_m,
+                             m->mbmi.ref_frame,
+                             mi->ref_mvs[rf],
+                             cpi->common.ref_frame_sign_bias );
+
+                pick_best_mv_ref( mi->mv[0], mi->ref_mvs[rf], &best_mv);
+#endif
                 if (xd->allow_high_precision_mv)
                   write_mv_hp(w, &mi->mv[0].as_mv, &best_mv, mvc_hp);
                 else
@@ -1049,6 +1061,16 @@
                   write_mv(w, &mi->mv[0].as_mv, &best_mv, mvc);
 
                 if (mi->second_ref_frame) {
+#if 0 //CONFIG_NEW_MVREF
+                  find_mv_refs(xd, m, prev_m,
+                               m->mbmi.second_ref_frame,
+                               mi->ref_mvs[mi->second_ref_frame],
+                               cpi->common.ref_frame_sign_bias );
+
+                  pick_best_mv_ref( mi->mv[1],
+                                    mi->ref_mvs[mi->second_ref_frame],
+                                    &best_second_mv);
+#endif
                   if (xd->allow_high_precision_mv)
                     write_mv_hp(w, &mi->mv[1].as_mv, &best_second_mv, mvc_hp);
                   else
--- a/vp8/encoder/encodeframe.c
+++ b/vp8/encoder/encodeframe.c
@@ -34,7 +34,11 @@
 #include "vp8/common/pred_common.h"
 
 #define DBG_PRNT_SEGMAP 0
+#if CONFIG_NEW_MVREF
+#include "vp8/common/mvref_common.h"
+#endif
 
+
 #if CONFIG_RUNTIME_CPU_DETECT
 #define RTCD(x)     &cpi->common.rtcd.x
 #define IF_RTCD(x)  (x)
@@ -1300,6 +1304,12 @@
   // prediction fails. These are based on the current general estimates for
   // this frame which may be updated with each iteration of the recode loop.
   compute_mod_refprobs(cm);
+
+#if CONFIG_NEW_MVREF
+  // temp stats reset
+  vp8_zero( cpi->mv_ref_sum_distance );
+  vp8_zero( cpi->best_ref_index_counts );
+#endif
 
 // debug output
 #if DBG_PRNT_SEGMAP
--- a/vp8/encoder/onyx_if.c
+++ b/vp8/encoder/onyx_if.c
@@ -40,6 +40,10 @@
 #include "bitstream.h"
 #include "ratectrl.h"
 
+#if CONFIG_NEW_MVREF
+#include "vp8/common/mvref_common.h"
+#endif
+
 #if ARCH_ARM
 #include "vpx_ports/arm.h"
 #endif
@@ -3789,6 +3793,36 @@
   // Update the skip mb flag probabilities based on the distribution seen
   // in this frame.
   update_base_skip_probs(cpi);
+
+
+#if CONFIG_NEW_MVREF
+#if 0 && CONFIG_INTERNAL_STATS
+  {
+    FILE *f = fopen("mv_ref_dist.stt", "a");
+    unsigned int i;
+    //fprintf(f, "%10d %10d %10d %10d %10d %10d %10d %10d %10d %10d\n",
+    fprintf(f, "%10d %10d %10d %10d %10d %10d %10d %10d %10d %10d",
+            cpi->common.current_video_frame,
+            cpi->mv_ref_sum_distance[1][0],
+            cpi->mv_ref_sum_distance[1][1],
+            cpi->mv_ref_sum_distance[1][2],
+            cpi->mv_ref_sum_distance[2][0],
+            cpi->mv_ref_sum_distance[2][1],
+            cpi->mv_ref_sum_distance[2][2],
+            cpi->mv_ref_sum_distance[3][0],
+            cpi->mv_ref_sum_distance[3][1],
+            cpi->mv_ref_sum_distance[3][2] );
+
+    for (i = 0; i < MAX_MV_REFS; ++i) {
+      fprintf(f, "%10d", cpi->best_ref_index_counts[i] );
+    }
+    fprintf(f, "\n" );
+
+    fclose(f);
+  }
+#endif
+#endif
+
 
 #if 0// 1 && CONFIG_INTERNAL_STATS
   {
--- a/vp8/encoder/onyx_int.h
+++ b/vp8/encoder/onyx_int.h
@@ -59,6 +59,13 @@
 
 #define VP8_TEMPORAL_ALT_REF 1
 
+#if CONFIG_NEW_MVREF
+// temp. relate to mv_ref_sum_distance stats
+#define CUR_BEST 0
+#define NEW_BEST 1
+#define BEST_SELECTED 2
+#endif
+
 typedef struct {
   MV_CONTEXT mvc[2];
   int mvcosts[2][MVvals + 1];
@@ -750,6 +757,12 @@
 #if CONFIG_SWITCHABLE_INTERP
   unsigned int switchable_interp_count[VP8_SWITCHABLE_FILTERS+1]
                                       [VP8_SWITCHABLE_FILTERS];
+#endif
+
+#if CONFIG_NEW_MVREF
+  // temp stats [REF_FRAME]{REF_METHOD]
+  unsigned int mv_ref_sum_distance[4][3];
+  unsigned int best_ref_index_counts[17];
 #endif
 
 } VP8_COMP;
--- a/vp8/encoder/rdopt.c
+++ b/vp8/encoder/rdopt.c
@@ -41,6 +41,10 @@
 #include "vp8/common/seg_common.h"
 #include "vp8/common/pred_common.h"
 
+#if CONFIG_NEW_MVREF
+#include "vp8/common/mvref_common.h"
+#endif
+
 #if CONFIG_RUNTIME_CPU_DETECT
 #define IF_RTCD(x)  (x)
 #else
@@ -2892,9 +2896,10 @@
                         unsigned char *y_buffer[4], unsigned char *u_buffer[4],
                         unsigned char *v_buffer[4]) {
   YV12_BUFFER_CONFIG *yv12 = &cpi->common.yv12_fb[idx];
+  MACROBLOCKD *xd = &x->e_mbd;
 
-  vp8_find_near_mvs(&x->e_mbd, x->e_mbd.mode_info_context,
-                    x->e_mbd.prev_mode_info_context,
+  vp8_find_near_mvs(xd, xd->mode_info_context,
+                    xd->prev_mode_info_context,
                     &frame_nearest_mv[frame_type], &frame_near_mv[frame_type],
                     &frame_best_ref_mv[frame_type], frame_mdcounts[frame_type],
                     frame_type, cpi->common.ref_frame_sign_bias);
@@ -2902,8 +2907,27 @@
   y_buffer[frame_type] = yv12->y_buffer + recon_yoffset;
   u_buffer[frame_type] = yv12->u_buffer + recon_uvoffset;
   v_buffer[frame_type] = yv12->v_buffer + recon_uvoffset;
+
 #if CONFIG_NEWBESTREFMV
-  vp8_find_best_ref_mvs(&x->e_mbd, y_buffer[frame_type],
+#if CONFIG_NEW_MVREF
+  // Update stats on relative distance of chosen vector to the
+  // possible best reference vectors.
+  {
+    MB_MODE_INFO * mbmi = &xd->mode_info_context->mbmi;
+
+    find_mv_refs(xd, xd->mode_info_context,
+                 xd->prev_mode_info_context,
+                 frame_type,
+                 mbmi->ref_mvs[frame_type],
+                 cpi->common.ref_frame_sign_bias );
+
+    // Copy over the mv candidates
+    vpx_memcpy(xd->ref_mv, mbmi->ref_mvs[frame_type],
+              (MAX_MV_REFS * sizeof(int_mv)) );
+  }
+#endif
+
+  vp8_find_best_ref_mvs(xd, y_buffer[frame_type],
                         yv12->y_stride,
                         &frame_best_ref_mv[frame_type],
                         &frame_nearest_mv[frame_type],
@@ -3406,6 +3430,43 @@
             }
             d->bmi.as_mv.first.as_int = tmp_mv.as_int;
             frame_mv[NEWMV][refs[0]].as_int = d->bmi.as_mv.first.as_int;
+
+#if CONFIG_NEW_MVREF
+            // Update stats on relative distance of chosen vector to the
+            // possible best reference vectors.
+            {
+              unsigned int distance;
+              MV_REFERENCE_FRAME ref = mbmi->ref_frame;
+              int_mv selected_best_ref;
+              unsigned int best_index = 0;
+
+              find_mv_refs(xd, xd->mode_info_context,
+                           xd->prev_mode_info_context,
+                           ref,
+                           mbmi->ref_mvs[ref],
+                           cpi->common.ref_frame_sign_bias );
+
+              distance = mv_distance(&tmp_mv, &best_ref_mv);
+              cpi->mv_ref_sum_distance[ref][CUR_BEST] += distance;
+
+              distance =
+                mv_distance(&tmp_mv,
+                            &mbmi->ref_mvs[ref][0]);
+              cpi->mv_ref_sum_distance[ref][NEW_BEST] += distance;
+
+              best_index = pick_best_mv_ref(tmp_mv, mbmi->ref_mvs[ref],
+                                            &selected_best_ref);
+
+              distance = mv_distance(&tmp_mv, &selected_best_ref);
+              mbmi->mv_ref_index[ref] = best_index;
+              cpi->mv_ref_sum_distance[ref][BEST_SELECTED] += distance;
+              cpi->best_ref_index_counts[best_index]++;
+
+              // Temp
+              //mbmi->mv_ref_index[ref] = 0;
+              //mbmi->ref_mvs[ref][0].as_int = best_ref_mv.as_int;
+            }
+#endif
 
             // Add the new motion vector cost to our rolling cost variable
             rate2 += vp8_mv_bit_cost(&tmp_mv, &best_ref_mv,
--- a/vp8/vp8_common.mk
+++ b/vp8/vp8_common.mk
@@ -67,6 +67,8 @@
 VP8_COMMON_SRCS-yes += common/mbpitch.c
 VP8_COMMON_SRCS-yes += common/modecont.c
 VP8_COMMON_SRCS-yes += common/modecontext.c
+VP8_COMMON_SRCS-yes += common/mvref_common.c
+VP8_COMMON_SRCS-yes += common/mvref_common.h
 VP8_COMMON_SRCS-yes += common/quant_common.c
 VP8_COMMON_SRCS-yes += common/recon.c
 VP8_COMMON_SRCS-yes += common/reconinter.c