2010-05-18 17:58:33 +02:00
|
|
|
/*
|
2010-09-09 14:16:39 +02:00
|
|
|
* Copyright (c) 2010 The WebM project authors. All Rights Reserved.
|
2010-05-18 17:58:33 +02:00
|
|
|
*
|
2010-06-18 18:39:21 +02:00
|
|
|
* Use of this source code is governed by a BSD-style license
|
2010-06-04 22:19:40 +02:00
|
|
|
* that can be found in the LICENSE file in the root of the source
|
|
|
|
* tree. An additional intellectual property rights grant can be found
|
2010-06-18 18:39:21 +02:00
|
|
|
* in the file PATENTS. All contributing project authors may
|
2010-06-04 22:19:40 +02:00
|
|
|
* be found in the AUTHORS file in the root of the source tree.
|
2010-05-18 17:58:33 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include <limits.h>
|
2011-09-15 14:34:12 +02:00
|
|
|
#include "vpx_config.h"
|
2010-05-18 17:58:33 +02:00
|
|
|
#include "onyx_int.h"
|
|
|
|
#include "modecosts.h"
|
|
|
|
#include "encodeintra.h"
|
2014-04-15 23:10:58 +02:00
|
|
|
#include "vp8/common/common.h"
|
2011-02-10 20:41:38 +01:00
|
|
|
#include "vp8/common/entropymode.h"
|
2010-05-18 17:58:33 +02:00
|
|
|
#include "pickinter.h"
|
2011-02-10 20:41:38 +01:00
|
|
|
#include "vp8/common/findnearmv.h"
|
2010-05-18 17:58:33 +02:00
|
|
|
#include "encodemb.h"
|
2011-02-10 20:41:38 +01:00
|
|
|
#include "vp8/common/reconinter.h"
|
|
|
|
#include "vp8/common/reconintra4x4.h"
|
2012-03-06 01:50:33 +01:00
|
|
|
#include "vp8/common/variance.h"
|
2010-05-18 17:58:33 +02:00
|
|
|
#include "mcomp.h"
|
2011-01-28 16:00:20 +01:00
|
|
|
#include "rdopt.h"
|
2010-05-18 17:58:33 +02:00
|
|
|
#include "vpx_mem/vpx_mem.h"
|
2012-03-06 10:48:18 +01:00
|
|
|
#if CONFIG_TEMPORAL_DENOISING
|
|
|
|
#include "denoising.h"
|
|
|
|
#endif
|
2010-05-18 17:58:33 +02:00
|
|
|
|
2012-01-13 01:55:44 +01:00
|
|
|
extern int VP8_UVSSE(MACROBLOCK *x);
|
2010-05-18 17:58:33 +02:00
|
|
|
|
|
|
|
#ifdef SPEEDSTATS
|
|
|
|
extern unsigned int cnt_pm;
|
|
|
|
#endif
|
|
|
|
|
Speed selection support for disabled reference frames
There was an implicit reference frame test order (typically LAST,
GOLD, ARF) in the mode selection logic, but this doesn't provide the
expected results when some reference frames are disabled. For
instance, in real-time mode, the speed selection logic often disables
the ARF modes. So if the user disables the LAST and GOLD frames, the
encoder was always choosing INTRA, when in reality searching the ARF
in this case has the same speed penalty as searching LAST would have
had.
Instead, introduce the notion of a reference frame search order. This
patch preserves the former priorities, so if a frame is disabled, the
other frames bump up a slot to take its place. This patch lays the
groundwork for doing something smarter in the frame test order, for
example considering temporal distance or looking at the frames used by
nearby blocks.
Change-Id: I1199149f8662a408537c653d2c021c7f1d29a700
2011-11-18 21:47:16 +01:00
|
|
|
extern const int vp8_ref_frame_order[MAX_MODES];
|
2010-05-18 17:58:33 +02:00
|
|
|
extern const MB_PREDICTION_MODE vp8_mode_order[MAX_MODES];
|
|
|
|
|
|
|
|
extern int vp8_cost_mv_ref(MB_PREDICTION_MODE m, const int near_mv_ref_ct[4]);
|
|
|
|
|
|
|
|
|
2011-05-12 16:50:16 +02:00
|
|
|
int vp8_skip_fractional_mv_step(MACROBLOCK *mb, BLOCK *b, BLOCKD *d,
|
|
|
|
int_mv *bestmv, int_mv *ref_mv,
|
|
|
|
int error_per_bit,
|
|
|
|
const vp8_variance_fn_ptr_t *vfp,
|
|
|
|
int *mvcost[2], int *distortion,
|
|
|
|
unsigned int *sse)
|
2010-05-18 17:58:33 +02:00
|
|
|
{
|
|
|
|
(void) b;
|
|
|
|
(void) d;
|
|
|
|
(void) ref_mv;
|
|
|
|
(void) error_per_bit;
|
2010-10-26 21:34:16 +02:00
|
|
|
(void) vfp;
|
2010-05-18 17:58:33 +02:00
|
|
|
(void) mvcost;
|
2011-04-14 21:53:33 +02:00
|
|
|
(void) distortion;
|
2011-04-18 21:48:34 +02:00
|
|
|
(void) sse;
|
2011-05-12 16:50:16 +02:00
|
|
|
bestmv->as_mv.row <<= 3;
|
|
|
|
bestmv->as_mv.col <<= 3;
|
2010-05-18 17:58:33 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-05-24 16:44:03 +02:00
|
|
|
int vp8_get_inter_mbpred_error(MACROBLOCK *mb,
|
2011-05-31 20:24:42 +02:00
|
|
|
const vp8_variance_fn_ptr_t *vfp,
|
|
|
|
unsigned int *sse,
|
|
|
|
int_mv this_mv)
|
2010-05-18 17:58:33 +02:00
|
|
|
{
|
|
|
|
|
|
|
|
BLOCK *b = &mb->block[0];
|
|
|
|
BLOCKD *d = &mb->e_mbd.block[0];
|
|
|
|
unsigned char *what = (*(b->base_src) + b->src);
|
|
|
|
int what_stride = b->src_stride;
|
2012-01-20 19:52:16 +01:00
|
|
|
int pre_stride = mb->e_mbd.pre.y_stride;
|
|
|
|
unsigned char *in_what = mb->e_mbd.pre.y_buffer + d->offset ;
|
|
|
|
int in_what_stride = pre_stride;
|
2011-05-31 20:24:42 +02:00
|
|
|
int xoffset = this_mv.as_mv.col & 7;
|
|
|
|
int yoffset = this_mv.as_mv.row & 7;
|
2010-05-18 17:58:33 +02:00
|
|
|
|
2012-01-20 19:52:16 +01:00
|
|
|
in_what += (this_mv.as_mv.row >> 3) * pre_stride + (this_mv.as_mv.col >> 3);
|
2010-05-18 17:58:33 +02:00
|
|
|
|
|
|
|
if (xoffset | yoffset)
|
|
|
|
{
|
2010-10-26 21:34:16 +02:00
|
|
|
return vfp->svf(in_what, in_what_stride, xoffset, yoffset, what, what_stride, sse);
|
2010-05-18 17:58:33 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-10-26 21:34:16 +02:00
|
|
|
return vfp->vf(what, what_stride, in_what, in_what_stride, sse);
|
2010-05-18 17:58:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
unsigned int vp8_get4x4sse_cs_c
|
|
|
|
(
|
2010-10-11 23:01:23 +02:00
|
|
|
const unsigned char *src_ptr,
|
2010-05-18 17:58:33 +02:00
|
|
|
int source_stride,
|
2010-10-11 23:01:23 +02:00
|
|
|
const unsigned char *ref_ptr,
|
2011-05-25 15:26:29 +02:00
|
|
|
int recon_stride
|
2010-05-18 17:58:33 +02:00
|
|
|
)
|
|
|
|
{
|
|
|
|
int distortion = 0;
|
|
|
|
int r, c;
|
|
|
|
|
|
|
|
for (r = 0; r < 4; r++)
|
|
|
|
{
|
|
|
|
for (c = 0; c < 4; c++)
|
|
|
|
{
|
|
|
|
int diff = src_ptr[c] - ref_ptr[c];
|
|
|
|
distortion += diff * diff;
|
|
|
|
}
|
|
|
|
|
|
|
|
src_ptr += source_stride;
|
|
|
|
ref_ptr += recon_stride;
|
|
|
|
}
|
|
|
|
|
|
|
|
return distortion;
|
|
|
|
}
|
|
|
|
|
2012-01-13 01:55:44 +01:00
|
|
|
static int get_prediction_error(BLOCK *be, BLOCKD *b)
|
2010-05-18 17:58:33 +02:00
|
|
|
{
|
|
|
|
unsigned char *sptr;
|
|
|
|
unsigned char *dptr;
|
|
|
|
sptr = (*(be->base_src) + be->src);
|
|
|
|
dptr = b->predictor;
|
|
|
|
|
2012-01-13 01:55:44 +01:00
|
|
|
return vp8_get4x4sse_cs(sptr, be->src_stride, dptr, 16);
|
2010-05-18 17:58:33 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pick_intra4x4block(
|
|
|
|
MACROBLOCK *x,
|
2011-05-20 03:37:13 +02:00
|
|
|
int ib,
|
2010-05-18 17:58:33 +02:00
|
|
|
B_PREDICTION_MODE *best_mode,
|
2012-04-17 09:40:56 +02:00
|
|
|
const int *mode_costs,
|
2010-05-18 17:58:33 +02:00
|
|
|
|
|
|
|
int *bestrate,
|
|
|
|
int *bestdistortion)
|
|
|
|
{
|
2011-05-20 03:37:13 +02:00
|
|
|
|
|
|
|
BLOCKD *b = &x->e_mbd.block[ib];
|
|
|
|
BLOCK *be = &x->block[ib];
|
2012-01-20 19:52:16 +01:00
|
|
|
int dst_stride = x->e_mbd.dst.y_stride;
|
2012-07-31 23:21:55 +02:00
|
|
|
unsigned char *dst = x->e_mbd.dst.y_buffer + b->offset;
|
2010-05-18 17:58:33 +02:00
|
|
|
B_PREDICTION_MODE mode;
|
2012-05-21 23:30:56 +02:00
|
|
|
int best_rd = INT_MAX;
|
2010-05-18 17:58:33 +02:00
|
|
|
int rate;
|
|
|
|
int distortion;
|
|
|
|
|
2012-07-31 23:21:55 +02:00
|
|
|
unsigned char *Above = dst - dst_stride;
|
|
|
|
unsigned char *yleft = dst - 1;
|
|
|
|
unsigned char top_left = Above[-1];
|
|
|
|
|
2012-08-02 23:26:04 +02:00
|
|
|
for (mode = B_DC_PRED; mode <= B_HE_PRED; mode++)
|
2010-05-18 17:58:33 +02:00
|
|
|
{
|
|
|
|
int this_rd;
|
|
|
|
|
|
|
|
rate = mode_costs[mode];
|
2012-07-31 23:21:55 +02:00
|
|
|
|
2012-08-01 20:17:57 +02:00
|
|
|
vp8_intra4x4_predict(Above, yleft, dst_stride, mode,
|
|
|
|
b->predictor, 16, top_left);
|
2012-01-13 01:55:44 +01:00
|
|
|
distortion = get_prediction_error(be, b);
|
2011-04-07 22:57:25 +02:00
|
|
|
this_rd = RDCOST(x->rdmult, x->rddiv, rate, distortion);
|
2010-05-18 17:58:33 +02:00
|
|
|
|
|
|
|
if (this_rd < best_rd)
|
|
|
|
{
|
|
|
|
*bestrate = rate;
|
|
|
|
*bestdistortion = distortion;
|
|
|
|
best_rd = this_rd;
|
|
|
|
*best_mode = mode;
|
|
|
|
}
|
|
|
|
}
|
2011-06-02 19:46:41 +02:00
|
|
|
|
2012-08-02 23:26:04 +02:00
|
|
|
b->bmi.as_mode = *best_mode;
|
2012-01-13 01:55:44 +01:00
|
|
|
vp8_encode_intra4x4block(x, ib);
|
2010-05-18 17:58:33 +02:00
|
|
|
return best_rd;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-06-08 17:24:52 +02:00
|
|
|
static int pick_intra4x4mby_modes
|
2011-04-13 21:56:46 +02:00
|
|
|
(
|
|
|
|
MACROBLOCK *mb,
|
|
|
|
int *Rate,
|
|
|
|
int *best_dist
|
|
|
|
)
|
2010-05-18 17:58:33 +02:00
|
|
|
{
|
|
|
|
MACROBLOCKD *const xd = &mb->e_mbd;
|
|
|
|
int i;
|
|
|
|
int cost = mb->mbmode_cost [xd->frame_type] [B_PRED];
|
2011-02-01 17:26:04 +01:00
|
|
|
int error;
|
2010-05-18 17:58:33 +02:00
|
|
|
int distortion = 0;
|
2012-04-17 09:40:56 +02:00
|
|
|
const int *bmode_costs;
|
2010-05-18 17:58:33 +02:00
|
|
|
|
2012-02-28 20:12:30 +01:00
|
|
|
intra_prediction_down_copy(xd, xd->dst.y_buffer - xd->dst.y_stride + 16);
|
2010-05-18 17:58:33 +02:00
|
|
|
|
2011-06-13 23:14:11 +02:00
|
|
|
bmode_costs = mb->inter_bmode_costs;
|
|
|
|
|
2010-05-18 17:58:33 +02:00
|
|
|
for (i = 0; i < 16; i++)
|
|
|
|
{
|
|
|
|
MODE_INFO *const mic = xd->mode_info_context;
|
|
|
|
const int mis = xd->mode_info_stride;
|
2011-05-24 19:24:52 +02:00
|
|
|
|
2010-05-18 17:58:33 +02:00
|
|
|
B_PREDICTION_MODE UNINITIALIZED_IS_SAFE(best_mode);
|
|
|
|
int UNINITIALIZED_IS_SAFE(r), UNINITIALIZED_IS_SAFE(d);
|
|
|
|
|
2011-06-13 23:14:11 +02:00
|
|
|
if (mb->e_mbd.frame_type == KEY_FRAME)
|
|
|
|
{
|
|
|
|
const B_PREDICTION_MODE A = above_block_mode(mic, i, mis);
|
|
|
|
const B_PREDICTION_MODE L = left_block_mode(mic, i);
|
|
|
|
|
|
|
|
bmode_costs = mb->bmode_costs[A][L];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-01-13 01:55:44 +01:00
|
|
|
pick_intra4x4block(mb, i, &best_mode, bmode_costs, &r, &d);
|
2010-05-18 17:58:33 +02:00
|
|
|
|
|
|
|
cost += r;
|
|
|
|
distortion += d;
|
2011-06-02 19:46:41 +02:00
|
|
|
mic->bmi[i].as_mode = best_mode;
|
2010-05-18 17:58:33 +02:00
|
|
|
|
2012-05-21 23:30:56 +02:00
|
|
|
/* Break out case where we have already exceeded best so far value
|
|
|
|
* that was passed in
|
|
|
|
*/
|
2010-05-18 17:58:33 +02:00
|
|
|
if (distortion > *best_dist)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
*Rate = cost;
|
|
|
|
|
|
|
|
if (i == 16)
|
2011-02-01 17:26:04 +01:00
|
|
|
{
|
2010-05-18 17:58:33 +02:00
|
|
|
*best_dist = distortion;
|
2011-04-07 22:57:25 +02:00
|
|
|
error = RDCOST(mb->rdmult, mb->rddiv, cost, distortion);
|
2011-02-01 17:26:04 +01:00
|
|
|
}
|
2010-05-18 17:58:33 +02:00
|
|
|
else
|
2011-02-01 17:26:04 +01:00
|
|
|
{
|
2010-05-18 17:58:33 +02:00
|
|
|
*best_dist = INT_MAX;
|
2011-02-01 17:26:04 +01:00
|
|
|
error = INT_MAX;
|
|
|
|
}
|
2010-05-18 17:58:33 +02:00
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2011-06-08 17:24:52 +02:00
|
|
|
static void pick_intra_mbuv_mode(MACROBLOCK *mb)
|
2010-05-18 17:58:33 +02:00
|
|
|
{
|
|
|
|
|
|
|
|
MACROBLOCKD *x = &mb->e_mbd;
|
|
|
|
unsigned char *uabove_row = x->dst.u_buffer - x->dst.uv_stride;
|
|
|
|
unsigned char *vabove_row = x->dst.v_buffer - x->dst.uv_stride;
|
|
|
|
unsigned char *usrc_ptr = (mb->block[16].src + *mb->block[16].base_src);
|
|
|
|
unsigned char *vsrc_ptr = (mb->block[20].src + *mb->block[20].base_src);
|
|
|
|
int uvsrc_stride = mb->block[16].src_stride;
|
|
|
|
unsigned char uleft_col[8];
|
|
|
|
unsigned char vleft_col[8];
|
|
|
|
unsigned char utop_left = uabove_row[-1];
|
|
|
|
unsigned char vtop_left = vabove_row[-1];
|
|
|
|
int i, j;
|
|
|
|
int expected_udc;
|
|
|
|
int expected_vdc;
|
|
|
|
int shift;
|
|
|
|
int Uaverage = 0;
|
|
|
|
int Vaverage = 0;
|
|
|
|
int diff;
|
|
|
|
int pred_error[4] = {0, 0, 0, 0}, best_error = INT_MAX;
|
|
|
|
MB_PREDICTION_MODE UNINITIALIZED_IS_SAFE(best_mode);
|
|
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
{
|
|
|
|
uleft_col[i] = x->dst.u_buffer [i* x->dst.uv_stride -1];
|
|
|
|
vleft_col[i] = x->dst.v_buffer [i* x->dst.uv_stride -1];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!x->up_available && !x->left_available)
|
|
|
|
{
|
|
|
|
expected_udc = 128;
|
|
|
|
expected_vdc = 128;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
shift = 2;
|
|
|
|
|
|
|
|
if (x->up_available)
|
|
|
|
{
|
|
|
|
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
{
|
|
|
|
Uaverage += uabove_row[i];
|
|
|
|
Vaverage += vabove_row[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
shift ++;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (x->left_available)
|
|
|
|
{
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
{
|
|
|
|
Uaverage += uleft_col[i];
|
|
|
|
Vaverage += vleft_col[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
shift ++;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
expected_udc = (Uaverage + (1 << (shift - 1))) >> shift;
|
|
|
|
expected_vdc = (Vaverage + (1 << (shift - 1))) >> shift;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
{
|
|
|
|
for (j = 0; j < 8; j++)
|
|
|
|
{
|
|
|
|
|
|
|
|
int predu = uleft_col[i] + uabove_row[j] - utop_left;
|
|
|
|
int predv = vleft_col[i] + vabove_row[j] - vtop_left;
|
|
|
|
int u_p, v_p;
|
|
|
|
|
|
|
|
u_p = usrc_ptr[j];
|
|
|
|
v_p = vsrc_ptr[j];
|
|
|
|
|
|
|
|
if (predu < 0)
|
|
|
|
predu = 0;
|
|
|
|
|
|
|
|
if (predu > 255)
|
|
|
|
predu = 255;
|
|
|
|
|
|
|
|
if (predv < 0)
|
|
|
|
predv = 0;
|
|
|
|
|
|
|
|
if (predv > 255)
|
|
|
|
predv = 255;
|
|
|
|
|
|
|
|
|
|
|
|
diff = u_p - expected_udc;
|
|
|
|
pred_error[DC_PRED] += diff * diff;
|
|
|
|
diff = v_p - expected_vdc;
|
|
|
|
pred_error[DC_PRED] += diff * diff;
|
|
|
|
|
|
|
|
|
|
|
|
diff = u_p - uabove_row[j];
|
|
|
|
pred_error[V_PRED] += diff * diff;
|
|
|
|
diff = v_p - vabove_row[j];
|
|
|
|
pred_error[V_PRED] += diff * diff;
|
|
|
|
|
|
|
|
|
|
|
|
diff = u_p - uleft_col[i];
|
|
|
|
pred_error[H_PRED] += diff * diff;
|
|
|
|
diff = v_p - vleft_col[i];
|
|
|
|
pred_error[H_PRED] += diff * diff;
|
|
|
|
|
|
|
|
|
|
|
|
diff = u_p - predu;
|
|
|
|
pred_error[TM_PRED] += diff * diff;
|
|
|
|
diff = v_p - predv;
|
|
|
|
pred_error[TM_PRED] += diff * diff;
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
usrc_ptr += uvsrc_stride;
|
|
|
|
vsrc_ptr += uvsrc_stride;
|
|
|
|
|
|
|
|
if (i == 3)
|
|
|
|
{
|
|
|
|
usrc_ptr = (mb->block[18].src + *mb->block[18].base_src);
|
|
|
|
vsrc_ptr = (mb->block[22].src + *mb->block[22].base_src);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (i = DC_PRED; i <= TM_PRED; i++)
|
|
|
|
{
|
|
|
|
if (best_error > pred_error[i])
|
|
|
|
{
|
|
|
|
best_error = pred_error[i];
|
|
|
|
best_mode = (MB_PREDICTION_MODE)i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-08-12 22:25:43 +02:00
|
|
|
mb->e_mbd.mode_info_context->mbmi.uv_mode = best_mode;
|
2010-05-18 17:58:33 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2012-12-05 00:43:25 +01:00
|
|
|
static void update_mvcount(MACROBLOCK *x, int_mv *best_ref_mv)
|
2011-05-13 16:56:45 +02:00
|
|
|
{
|
2012-11-05 21:41:24 +01:00
|
|
|
MACROBLOCKD *xd = &x->e_mbd;
|
2011-05-19 21:03:36 +02:00
|
|
|
/* Split MV modes currently not supported when RD is nopt enabled,
|
|
|
|
* therefore, only need to modify MVcount in NEWMV mode. */
|
2011-05-13 16:56:45 +02:00
|
|
|
if (xd->mode_info_context->mbmi.mode == NEWMV)
|
|
|
|
{
|
2012-11-05 21:41:24 +01:00
|
|
|
x->MVcount[0][mv_max+((xd->mode_info_context->mbmi.mv.as_mv.row -
|
2011-05-19 21:03:36 +02:00
|
|
|
best_ref_mv->as_mv.row) >> 1)]++;
|
2012-11-05 21:41:24 +01:00
|
|
|
x->MVcount[1][mv_max+((xd->mode_info_context->mbmi.mv.as_mv.col -
|
2011-05-19 21:03:36 +02:00
|
|
|
best_ref_mv->as_mv.col) >> 1)]++;
|
2011-05-13 16:56:45 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-16 22:50:29 +01:00
|
|
|
|
|
|
|
#if CONFIG_MULTI_RES_ENCODING
|
|
|
|
static
|
|
|
|
void get_lower_res_motion_info(VP8_COMP *cpi, MACROBLOCKD *xd, int *dissim,
|
|
|
|
int *parent_ref_frame,
|
|
|
|
MB_PREDICTION_MODE *parent_mode,
|
|
|
|
int_mv *parent_ref_mv, int mb_row, int mb_col)
|
|
|
|
{
|
2012-05-16 21:06:42 +02:00
|
|
|
LOWER_RES_MB_INFO* store_mode_info
|
|
|
|
= ((LOWER_RES_FRAME_INFO*)cpi->oxcf.mr_low_res_mode_info)->mb_info;
|
2011-12-16 22:50:29 +01:00
|
|
|
unsigned int parent_mb_index;
|
|
|
|
|
|
|
|
/* Consider different down_sampling_factor. */
|
|
|
|
{
|
|
|
|
/* TODO: Removed the loop that supports special down_sampling_factor
|
|
|
|
* such as 2, 4, 8. Will revisit it if needed.
|
|
|
|
* Should also try using a look-up table to see if it helps
|
|
|
|
* performance. */
|
|
|
|
int parent_mb_row, parent_mb_col;
|
|
|
|
|
Allow to skip highest-resolution encoding in multi-resolution encoder
Sometimes, a user doesn't have enough bandwidth to send high-resolution
(i.e. HD) video even though the camera catches HD video. This change
allowed users to skip highest-resolution encoding by setting that level's
target bit rate to 0.
To test it, modify the following line in vp8_multi_resolution_encoder.c.
unsigned int target_bitrate[NUM_ENCODERS]={1400, 500, 100};
To skip the highest-resolution level, change it to
unsigned int target_bitrate[NUM_ENCODERS]={0, 500, 100};
To skip the first and second highest resolution levels, change it to
unsigned int target_bitrate[NUM_ENCODERS]={0, 0, 100};
This change also fixed a small problem in mapping, which slightly helped
quality and performance.
Change-Id: I977bae9a9fbfba85c8be4bd5af01539f2b84bc81
2012-02-02 20:27:04 +01:00
|
|
|
parent_mb_row = mb_row*cpi->oxcf.mr_down_sampling_factor.den
|
2011-12-16 22:50:29 +01:00
|
|
|
/cpi->oxcf.mr_down_sampling_factor.num;
|
Allow to skip highest-resolution encoding in multi-resolution encoder
Sometimes, a user doesn't have enough bandwidth to send high-resolution
(i.e. HD) video even though the camera catches HD video. This change
allowed users to skip highest-resolution encoding by setting that level's
target bit rate to 0.
To test it, modify the following line in vp8_multi_resolution_encoder.c.
unsigned int target_bitrate[NUM_ENCODERS]={1400, 500, 100};
To skip the highest-resolution level, change it to
unsigned int target_bitrate[NUM_ENCODERS]={0, 500, 100};
To skip the first and second highest resolution levels, change it to
unsigned int target_bitrate[NUM_ENCODERS]={0, 0, 100};
This change also fixed a small problem in mapping, which slightly helped
quality and performance.
Change-Id: I977bae9a9fbfba85c8be4bd5af01539f2b84bc81
2012-02-02 20:27:04 +01:00
|
|
|
parent_mb_col = mb_col*cpi->oxcf.mr_down_sampling_factor.den
|
2011-12-16 22:50:29 +01:00
|
|
|
/cpi->oxcf.mr_down_sampling_factor.num;
|
|
|
|
parent_mb_index = parent_mb_row*cpi->mr_low_res_mb_cols + parent_mb_col;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read lower-resolution mode & motion result from memory.*/
|
|
|
|
*parent_ref_frame = store_mode_info[parent_mb_index].ref_frame;
|
|
|
|
*parent_mode = store_mode_info[parent_mb_index].mode;
|
|
|
|
*dissim = store_mode_info[parent_mb_index].dissim;
|
|
|
|
|
|
|
|
/* For highest-resolution encoder, adjust dissim value. Lower its quality
|
|
|
|
* for good performance. */
|
|
|
|
if (cpi->oxcf.mr_encoder_id == (cpi->oxcf.mr_total_resolutions - 1))
|
|
|
|
*dissim>>=1;
|
|
|
|
|
|
|
|
if(*parent_ref_frame != INTRA_FRAME)
|
|
|
|
{
|
|
|
|
/* Consider different down_sampling_factor.
|
|
|
|
* The result can be rounded to be more precise, but it takes more time.
|
|
|
|
*/
|
|
|
|
(*parent_ref_mv).as_mv.row = store_mode_info[parent_mb_index].mv.as_mv.row
|
|
|
|
*cpi->oxcf.mr_down_sampling_factor.num
|
|
|
|
/cpi->oxcf.mr_down_sampling_factor.den;
|
|
|
|
(*parent_ref_mv).as_mv.col = store_mode_info[parent_mb_index].mv.as_mv.col
|
|
|
|
*cpi->oxcf.mr_down_sampling_factor.num
|
|
|
|
/cpi->oxcf.mr_down_sampling_factor.den;
|
|
|
|
|
|
|
|
vp8_clamp_mv2(parent_ref_mv, xd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-03-06 10:48:18 +01:00
|
|
|
static void check_for_encode_breakout(unsigned int sse, MACROBLOCK* x)
|
|
|
|
{
|
2012-08-20 21:58:31 +02:00
|
|
|
MACROBLOCKD *xd = &x->e_mbd;
|
|
|
|
|
|
|
|
unsigned int threshold = (xd->block[0].dequant[1]
|
|
|
|
* xd->block[0].dequant[1] >>4);
|
|
|
|
|
|
|
|
if(threshold < x->encode_breakout)
|
|
|
|
threshold = x->encode_breakout;
|
|
|
|
|
|
|
|
if (sse < threshold )
|
2012-03-06 10:48:18 +01:00
|
|
|
{
|
2012-05-21 23:30:56 +02:00
|
|
|
/* Check u and v to make sure skip is ok */
|
2012-05-08 20:45:35 +02:00
|
|
|
unsigned int sse2 = 0;
|
2012-03-06 10:48:18 +01:00
|
|
|
|
|
|
|
sse2 = VP8_UVSSE(x);
|
|
|
|
|
|
|
|
if (sse2 * 2 < x->encode_breakout)
|
|
|
|
x->skip = 1;
|
|
|
|
else
|
|
|
|
x->skip = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-21 19:52:35 +02:00
|
|
|
static int evaluate_inter_mode(unsigned int* sse, int rate2, int* distortion2,
|
|
|
|
VP8_COMP *cpi, MACROBLOCK *x, int rd_adj)
|
2012-03-06 10:48:18 +01:00
|
|
|
{
|
|
|
|
MB_PREDICTION_MODE this_mode = x->e_mbd.mode_info_context->mbmi.mode;
|
|
|
|
int_mv mv = x->e_mbd.mode_info_context->mbmi.mv;
|
|
|
|
int this_rd;
|
|
|
|
/* Exit early and don't compute the distortion if this macroblock
|
|
|
|
* is marked inactive. */
|
|
|
|
if (cpi->active_map_enabled && x->active_ptr[0] == 0)
|
|
|
|
{
|
|
|
|
*sse = 0;
|
|
|
|
*distortion2 = 0;
|
|
|
|
x->skip = 1;
|
|
|
|
return INT_MAX;
|
|
|
|
}
|
|
|
|
|
|
|
|
if((this_mode != NEWMV) ||
|
|
|
|
!(cpi->sf.half_pixel_search) || cpi->common.full_pixel==1)
|
2012-05-24 16:44:03 +02:00
|
|
|
*distortion2 = vp8_get_inter_mbpred_error(x,
|
2012-03-06 10:48:18 +01:00
|
|
|
&cpi->fn_ptr[BLOCK_16X16],
|
|
|
|
sse, mv);
|
|
|
|
|
|
|
|
this_rd = RDCOST(x->rdmult, x->rddiv, rate2, *distortion2);
|
|
|
|
|
2012-08-21 19:52:35 +02:00
|
|
|
/* Adjust rd to bias to ZEROMV */
|
|
|
|
if(this_mode == ZEROMV)
|
|
|
|
{
|
|
|
|
/* Bias to ZEROMV on LAST_FRAME reference when it is available. */
|
|
|
|
if ((cpi->ref_frame_flags & VP8_LAST_FRAME &
|
|
|
|
cpi->common.refresh_last_frame)
|
|
|
|
&& x->e_mbd.mode_info_context->mbmi.ref_frame != LAST_FRAME)
|
|
|
|
rd_adj = 100;
|
|
|
|
|
2012-10-16 03:41:32 +02:00
|
|
|
// rd_adj <= 100
|
|
|
|
this_rd = ((int64_t)this_rd) * rd_adj / 100;
|
2012-08-21 19:52:35 +02:00
|
|
|
}
|
|
|
|
|
2012-03-06 10:48:18 +01:00
|
|
|
check_for_encode_breakout(*sse, x);
|
|
|
|
return this_rd;
|
|
|
|
}
|
2011-12-16 22:50:29 +01:00
|
|
|
|
2012-08-21 19:52:35 +02:00
|
|
|
static void calculate_zeromv_rd_adjustment(VP8_COMP *cpi, MACROBLOCK *x,
|
|
|
|
int *rd_adjustment)
|
|
|
|
{
|
|
|
|
MODE_INFO *mic = x->e_mbd.mode_info_context;
|
|
|
|
int_mv mv_l, mv_a, mv_al;
|
|
|
|
int local_motion_check = 0;
|
|
|
|
|
|
|
|
if (cpi->lf_zeromv_pct > 40)
|
|
|
|
{
|
|
|
|
/* left mb */
|
|
|
|
mic -= 1;
|
|
|
|
mv_l = mic->mbmi.mv;
|
|
|
|
|
|
|
|
if (mic->mbmi.ref_frame != INTRA_FRAME)
|
|
|
|
if( abs(mv_l.as_mv.row) < 8 && abs(mv_l.as_mv.col) < 8)
|
|
|
|
local_motion_check++;
|
|
|
|
|
|
|
|
/* above-left mb */
|
|
|
|
mic -= x->e_mbd.mode_info_stride;
|
|
|
|
mv_al = mic->mbmi.mv;
|
|
|
|
|
|
|
|
if (mic->mbmi.ref_frame != INTRA_FRAME)
|
|
|
|
if( abs(mv_al.as_mv.row) < 8 && abs(mv_al.as_mv.col) < 8)
|
|
|
|
local_motion_check++;
|
|
|
|
|
|
|
|
/* above mb */
|
|
|
|
mic += 1;
|
|
|
|
mv_a = mic->mbmi.mv;
|
|
|
|
|
|
|
|
if (mic->mbmi.ref_frame != INTRA_FRAME)
|
|
|
|
if( abs(mv_a.as_mv.row) < 8 && abs(mv_a.as_mv.col) < 8)
|
|
|
|
local_motion_check++;
|
|
|
|
|
|
|
|
if (((!x->e_mbd.mb_to_top_edge || !x->e_mbd.mb_to_left_edge)
|
|
|
|
&& local_motion_check >0) || local_motion_check >2 )
|
|
|
|
*rd_adjustment = 80;
|
|
|
|
else if (local_motion_check > 0)
|
|
|
|
*rd_adjustment = 90;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-19 21:03:36 +02:00
|
|
|
void vp8_pick_inter_mode(VP8_COMP *cpi, MACROBLOCK *x, int recon_yoffset,
|
|
|
|
int recon_uvoffset, int *returnrate,
|
2011-12-16 22:50:29 +01:00
|
|
|
int *returndistortion, int *returnintra, int mb_row,
|
|
|
|
int mb_col)
|
2010-05-18 17:58:33 +02:00
|
|
|
{
|
|
|
|
BLOCK *b = &x->block[0];
|
|
|
|
BLOCKD *d = &x->e_mbd.block[0];
|
|
|
|
MACROBLOCKD *xd = &x->e_mbd;
|
|
|
|
MB_MODE_INFO best_mbmode;
|
2011-05-19 21:03:36 +02:00
|
|
|
|
2012-01-25 23:55:49 +01:00
|
|
|
int_mv best_ref_mv_sb[2];
|
|
|
|
int_mv mode_mv_sb[2][MB_MODE_COUNT];
|
2011-05-12 16:50:16 +02:00
|
|
|
int_mv best_ref_mv;
|
2012-01-25 23:55:49 +01:00
|
|
|
int_mv *mode_mv;
|
2010-05-18 17:58:33 +02:00
|
|
|
MB_PREDICTION_MODE this_mode;
|
|
|
|
int num00;
|
|
|
|
int mdcounts[4];
|
2012-05-21 23:30:56 +02:00
|
|
|
int best_rd = INT_MAX;
|
2012-08-21 19:52:35 +02:00
|
|
|
int rd_adjustment = 100;
|
2010-05-18 17:58:33 +02:00
|
|
|
int best_intra_rd = INT_MAX;
|
|
|
|
int mode_index;
|
|
|
|
int rate;
|
|
|
|
int rate2;
|
|
|
|
int distortion2;
|
2011-12-16 22:50:29 +01:00
|
|
|
int bestsme = INT_MAX;
|
2010-05-18 17:58:33 +02:00
|
|
|
int best_mode_index = 0;
|
2014-06-04 00:56:39 +02:00
|
|
|
unsigned int sse = UINT_MAX, best_rd_sse = UINT_MAX;
|
2012-03-06 10:48:18 +01:00
|
|
|
#if CONFIG_TEMPORAL_DENOISING
|
2014-06-04 00:56:39 +02:00
|
|
|
unsigned int zero_mv_sse = UINT_MAX, best_sse = UINT_MAX;
|
2012-03-06 10:48:18 +01:00
|
|
|
#endif
|
2010-05-18 17:58:33 +02:00
|
|
|
|
2013-04-12 03:19:18 +02:00
|
|
|
int sf_improved_mv_pred = cpi->sf.improved_mv_pred;
|
2011-05-12 16:50:16 +02:00
|
|
|
int_mv mvp;
|
2011-12-16 22:50:29 +01:00
|
|
|
|
2011-01-28 16:00:20 +01:00
|
|
|
int near_sadidx[8] = {0, 1, 2, 3, 4, 5, 6, 7};
|
|
|
|
int saddone=0;
|
2012-05-21 23:30:56 +02:00
|
|
|
/* search range got from mv_pred(). It uses step_param levels. (0-7) */
|
|
|
|
int sr=0;
|
2011-01-28 16:00:20 +01:00
|
|
|
|
2012-01-11 23:38:58 +01:00
|
|
|
unsigned char *plane[4][3];
|
Speed selection support for disabled reference frames
There was an implicit reference frame test order (typically LAST,
GOLD, ARF) in the mode selection logic, but this doesn't provide the
expected results when some reference frames are disabled. For
instance, in real-time mode, the speed selection logic often disables
the ARF modes. So if the user disables the LAST and GOLD frames, the
encoder was always choosing INTRA, when in reality searching the ARF
in this case has the same speed penalty as searching LAST would have
had.
Instead, introduce the notion of a reference frame search order. This
patch preserves the former priorities, so if a frame is disabled, the
other frames bump up a slot to take its place. This patch lays the
groundwork for doing something smarter in the frame test order, for
example considering temporal distance or looking at the frames used by
nearby blocks.
Change-Id: I1199149f8662a408537c653d2c021c7f1d29a700
2011-11-18 21:47:16 +01:00
|
|
|
int ref_frame_map[4];
|
2011-12-14 21:35:04 +01:00
|
|
|
int sign_bias = 0;
|
2010-05-18 17:58:33 +02:00
|
|
|
|
2011-12-16 22:50:29 +01:00
|
|
|
#if CONFIG_MULTI_RES_ENCODING
|
|
|
|
int dissim = INT_MAX;
|
|
|
|
int parent_ref_frame = 0;
|
2012-10-09 00:54:24 +02:00
|
|
|
int parent_ref_valid = cpi->oxcf.mr_encoder_id && cpi->mr_low_res_mv_avail;
|
2011-12-16 22:50:29 +01:00
|
|
|
int_mv parent_ref_mv;
|
|
|
|
MB_PREDICTION_MODE parent_mode = 0;
|
|
|
|
|
2012-10-09 00:54:24 +02:00
|
|
|
if (parent_ref_valid)
|
multi-res: work around reference mismatch
In some situations, believed to be an interaction between temporal
scalability and dropped frames, the references available to an
encoder may not be the same references available to its parent.
Previously, the code tried to force the reference frame chosen by
the parent to be used on this frame, even if it was disabled. This
was preventing the pick mode loop from running even once, which led
to a crash.
Attempts to reproduce this bug locally were unsuccessful, so it is
still undetermined what the underlying cause of this issue is. In
the specific case that was failing, the application did not set
any flags which influenced the reference selection on that frame.
ref_frame_flags indicated that the golden frame was disabled,
believed to be because the last frame updated the last and golden
frames, so golden was shut off by default. It's not clear why this
wouldn't have also been true in the lower res encoder, ie, why the
lower res encoder decided to use and/or was allowed to use the
golden frame. We weren't able to debug into the non-crashing
lower res encoder as the crash couldn't be reproduced locally.
Change-Id: Ifb265253d26963ac2afde0e20cf6792788be6af7
2012-10-08 17:39:47 +02:00
|
|
|
{
|
|
|
|
int parent_ref_flag;
|
|
|
|
|
2011-12-16 22:50:29 +01:00
|
|
|
get_lower_res_motion_info(cpi, xd, &dissim, &parent_ref_frame,
|
|
|
|
&parent_mode, &parent_ref_mv, mb_row, mb_col);
|
multi-res: work around reference mismatch
In some situations, believed to be an interaction between temporal
scalability and dropped frames, the references available to an
encoder may not be the same references available to its parent.
Previously, the code tried to force the reference frame chosen by
the parent to be used on this frame, even if it was disabled. This
was preventing the pick mode loop from running even once, which led
to a crash.
Attempts to reproduce this bug locally were unsuccessful, so it is
still undetermined what the underlying cause of this issue is. In
the specific case that was failing, the application did not set
any flags which influenced the reference selection on that frame.
ref_frame_flags indicated that the golden frame was disabled,
believed to be because the last frame updated the last and golden
frames, so golden was shut off by default. It's not clear why this
wouldn't have also been true in the lower res encoder, ie, why the
lower res encoder decided to use and/or was allowed to use the
golden frame. We weren't able to debug into the non-crashing
lower res encoder as the crash couldn't be reproduced locally.
Change-Id: Ifb265253d26963ac2afde0e20cf6792788be6af7
2012-10-08 17:39:47 +02:00
|
|
|
|
|
|
|
/* TODO(jkoleszar): The references available (ref_frame_flags) to the
|
|
|
|
* lower res encoder should match those available to this encoder, but
|
|
|
|
* there seems to be a situation where this mismatch can happen in the
|
|
|
|
* case of frame dropping and temporal layers. For example,
|
|
|
|
* GOLD being disallowed in ref_frame_flags, but being returned as
|
|
|
|
* parent_ref_frame.
|
|
|
|
*
|
|
|
|
* In this event, take the conservative approach of disabling the
|
|
|
|
* lower res info for this MB.
|
|
|
|
*/
|
|
|
|
parent_ref_flag = 0;
|
|
|
|
if (parent_ref_frame == LAST_FRAME)
|
|
|
|
parent_ref_flag = (cpi->ref_frame_flags & VP8_LAST_FRAME);
|
|
|
|
else if (parent_ref_frame == GOLDEN_FRAME)
|
|
|
|
parent_ref_flag = (cpi->ref_frame_flags & VP8_GOLD_FRAME);
|
|
|
|
else if (parent_ref_frame == ALTREF_FRAME)
|
|
|
|
parent_ref_flag = (cpi->ref_frame_flags & VP8_ALTR_FRAME);
|
|
|
|
|
|
|
|
//assert(!parent_ref_frame || parent_ref_flag);
|
2012-10-09 00:54:24 +02:00
|
|
|
if (parent_ref_frame && !parent_ref_flag)
|
|
|
|
parent_ref_valid = 0;
|
multi-res: work around reference mismatch
In some situations, believed to be an interaction between temporal
scalability and dropped frames, the references available to an
encoder may not be the same references available to its parent.
Previously, the code tried to force the reference frame chosen by
the parent to be used on this frame, even if it was disabled. This
was preventing the pick mode loop from running even once, which led
to a crash.
Attempts to reproduce this bug locally were unsuccessful, so it is
still undetermined what the underlying cause of this issue is. In
the specific case that was failing, the application did not set
any flags which influenced the reference selection on that frame.
ref_frame_flags indicated that the golden frame was disabled,
believed to be because the last frame updated the last and golden
frames, so golden was shut off by default. It's not clear why this
wouldn't have also been true in the lower res encoder, ie, why the
lower res encoder decided to use and/or was allowed to use the
golden frame. We weren't able to debug into the non-crashing
lower res encoder as the crash couldn't be reproduced locally.
Change-Id: Ifb265253d26963ac2afde0e20cf6792788be6af7
2012-10-08 17:39:47 +02:00
|
|
|
}
|
2011-12-16 22:50:29 +01:00
|
|
|
#endif
|
2011-04-14 21:53:33 +02:00
|
|
|
|
2012-01-25 23:55:49 +01:00
|
|
|
mode_mv = mode_mv_sb[sign_bias];
|
|
|
|
best_ref_mv.as_int = 0;
|
|
|
|
vpx_memset(mode_mv_sb, 0, sizeof(mode_mv_sb));
|
2010-06-11 20:33:49 +02:00
|
|
|
vpx_memset(&best_mbmode, 0, sizeof(best_mbmode));
|
2010-05-18 17:58:33 +02:00
|
|
|
|
Speed selection support for disabled reference frames
There was an implicit reference frame test order (typically LAST,
GOLD, ARF) in the mode selection logic, but this doesn't provide the
expected results when some reference frames are disabled. For
instance, in real-time mode, the speed selection logic often disables
the ARF modes. So if the user disables the LAST and GOLD frames, the
encoder was always choosing INTRA, when in reality searching the ARF
in this case has the same speed penalty as searching LAST would have
had.
Instead, introduce the notion of a reference frame search order. This
patch preserves the former priorities, so if a frame is disabled, the
other frames bump up a slot to take its place. This patch lays the
groundwork for doing something smarter in the frame test order, for
example considering temporal distance or looking at the frames used by
nearby blocks.
Change-Id: I1199149f8662a408537c653d2c021c7f1d29a700
2011-11-18 21:47:16 +01:00
|
|
|
/* Setup search priorities */
|
multi-res: work around reference mismatch
In some situations, believed to be an interaction between temporal
scalability and dropped frames, the references available to an
encoder may not be the same references available to its parent.
Previously, the code tried to force the reference frame chosen by
the parent to be used on this frame, even if it was disabled. This
was preventing the pick mode loop from running even once, which led
to a crash.
Attempts to reproduce this bug locally were unsuccessful, so it is
still undetermined what the underlying cause of this issue is. In
the specific case that was failing, the application did not set
any flags which influenced the reference selection on that frame.
ref_frame_flags indicated that the golden frame was disabled,
believed to be because the last frame updated the last and golden
frames, so golden was shut off by default. It's not clear why this
wouldn't have also been true in the lower res encoder, ie, why the
lower res encoder decided to use and/or was allowed to use the
golden frame. We weren't able to debug into the non-crashing
lower res encoder as the crash couldn't be reproduced locally.
Change-Id: Ifb265253d26963ac2afde0e20cf6792788be6af7
2012-10-08 17:39:47 +02:00
|
|
|
#if CONFIG_MULTI_RES_ENCODING
|
2012-10-09 00:54:24 +02:00
|
|
|
if (parent_ref_valid && parent_ref_frame && dissim < 8)
|
multi-res: work around reference mismatch
In some situations, believed to be an interaction between temporal
scalability and dropped frames, the references available to an
encoder may not be the same references available to its parent.
Previously, the code tried to force the reference frame chosen by
the parent to be used on this frame, even if it was disabled. This
was preventing the pick mode loop from running even once, which led
to a crash.
Attempts to reproduce this bug locally were unsuccessful, so it is
still undetermined what the underlying cause of this issue is. In
the specific case that was failing, the application did not set
any flags which influenced the reference selection on that frame.
ref_frame_flags indicated that the golden frame was disabled,
believed to be because the last frame updated the last and golden
frames, so golden was shut off by default. It's not clear why this
wouldn't have also been true in the lower res encoder, ie, why the
lower res encoder decided to use and/or was allowed to use the
golden frame. We weren't able to debug into the non-crashing
lower res encoder as the crash couldn't be reproduced locally.
Change-Id: Ifb265253d26963ac2afde0e20cf6792788be6af7
2012-10-08 17:39:47 +02:00
|
|
|
{
|
2012-10-09 00:58:04 +02:00
|
|
|
ref_frame_map[0] = -1;
|
multi-res: work around reference mismatch
In some situations, believed to be an interaction between temporal
scalability and dropped frames, the references available to an
encoder may not be the same references available to its parent.
Previously, the code tried to force the reference frame chosen by
the parent to be used on this frame, even if it was disabled. This
was preventing the pick mode loop from running even once, which led
to a crash.
Attempts to reproduce this bug locally were unsuccessful, so it is
still undetermined what the underlying cause of this issue is. In
the specific case that was failing, the application did not set
any flags which influenced the reference selection on that frame.
ref_frame_flags indicated that the golden frame was disabled,
believed to be because the last frame updated the last and golden
frames, so golden was shut off by default. It's not clear why this
wouldn't have also been true in the lower res encoder, ie, why the
lower res encoder decided to use and/or was allowed to use the
golden frame. We weren't able to debug into the non-crashing
lower res encoder as the crash couldn't be reproduced locally.
Change-Id: Ifb265253d26963ac2afde0e20cf6792788be6af7
2012-10-08 17:39:47 +02:00
|
|
|
ref_frame_map[1] = parent_ref_frame;
|
|
|
|
ref_frame_map[2] = -1;
|
|
|
|
ref_frame_map[3] = -1;
|
|
|
|
} else
|
|
|
|
#endif
|
2012-01-11 23:38:58 +01:00
|
|
|
get_reference_search_order(cpi, ref_frame_map);
|
2010-05-18 17:58:33 +02:00
|
|
|
|
2011-12-14 21:35:04 +01:00
|
|
|
/* Check to see if there is at least 1 valid reference frame that we need
|
|
|
|
* to calculate near_mvs.
|
|
|
|
*/
|
|
|
|
if (ref_frame_map[1] > 0)
|
|
|
|
{
|
2012-01-25 23:55:49 +01:00
|
|
|
sign_bias = vp8_find_near_mvs_bias(&x->e_mbd,
|
|
|
|
x->e_mbd.mode_info_context,
|
|
|
|
mode_mv_sb,
|
|
|
|
best_ref_mv_sb,
|
|
|
|
mdcounts,
|
|
|
|
ref_frame_map[1],
|
|
|
|
cpi->common.ref_frame_sign_bias);
|
|
|
|
|
|
|
|
mode_mv = mode_mv_sb[sign_bias];
|
|
|
|
best_ref_mv.as_int = best_ref_mv_sb[sign_bias].as_int;
|
2011-12-14 21:35:04 +01:00
|
|
|
}
|
|
|
|
|
2012-01-11 23:38:58 +01:00
|
|
|
get_predictor_pointers(cpi, plane, recon_yoffset, recon_uvoffset);
|
2010-05-18 17:58:33 +02:00
|
|
|
|
2012-05-21 23:30:56 +02:00
|
|
|
/* Count of the number of MBs tested so far this frame */
|
2012-12-05 01:52:47 +01:00
|
|
|
x->mbs_tested_so_far++;
|
2010-05-18 17:58:33 +02:00
|
|
|
|
2011-04-08 15:21:36 +02:00
|
|
|
*returnintra = INT_MAX;
|
2010-05-18 17:58:33 +02:00
|
|
|
x->skip = 0;
|
|
|
|
|
2010-08-12 22:25:43 +02:00
|
|
|
x->e_mbd.mode_info_context->mbmi.ref_frame = INTRA_FRAME;
|
2010-05-18 17:58:33 +02:00
|
|
|
|
2012-08-21 19:52:35 +02:00
|
|
|
/* If the frame has big static background and current MB is in low
|
|
|
|
* motion area, its mode decision is biased to ZEROMV mode.
|
|
|
|
*/
|
|
|
|
calculate_zeromv_rd_adjustment(cpi, x, &rd_adjustment);
|
|
|
|
|
2012-05-21 23:30:56 +02:00
|
|
|
/* if we encode a new mv this is important
|
|
|
|
* find the best new motion vector
|
|
|
|
*/
|
2010-05-18 17:58:33 +02:00
|
|
|
for (mode_index = 0; mode_index < MAX_MODES; mode_index++)
|
|
|
|
{
|
|
|
|
int frame_cost;
|
|
|
|
int this_rd = INT_MAX;
|
Speed selection support for disabled reference frames
There was an implicit reference frame test order (typically LAST,
GOLD, ARF) in the mode selection logic, but this doesn't provide the
expected results when some reference frames are disabled. For
instance, in real-time mode, the speed selection logic often disables
the ARF modes. So if the user disables the LAST and GOLD frames, the
encoder was always choosing INTRA, when in reality searching the ARF
in this case has the same speed penalty as searching LAST would have
had.
Instead, introduce the notion of a reference frame search order. This
patch preserves the former priorities, so if a frame is disabled, the
other frames bump up a slot to take its place. This patch lays the
groundwork for doing something smarter in the frame test order, for
example considering temporal distance or looking at the frames used by
nearby blocks.
Change-Id: I1199149f8662a408537c653d2c021c7f1d29a700
2011-11-18 21:47:16 +01:00
|
|
|
int this_ref_frame = ref_frame_map[vp8_ref_frame_order[mode_index]];
|
2010-05-18 17:58:33 +02:00
|
|
|
|
2012-11-07 01:27:00 +01:00
|
|
|
if (best_rd <= x->rd_threshes[mode_index])
|
2010-05-18 17:58:33 +02:00
|
|
|
continue;
|
|
|
|
|
Speed selection support for disabled reference frames
There was an implicit reference frame test order (typically LAST,
GOLD, ARF) in the mode selection logic, but this doesn't provide the
expected results when some reference frames are disabled. For
instance, in real-time mode, the speed selection logic often disables
the ARF modes. So if the user disables the LAST and GOLD frames, the
encoder was always choosing INTRA, when in reality searching the ARF
in this case has the same speed penalty as searching LAST would have
had.
Instead, introduce the notion of a reference frame search order. This
patch preserves the former priorities, so if a frame is disabled, the
other frames bump up a slot to take its place. This patch lays the
groundwork for doing something smarter in the frame test order, for
example considering temporal distance or looking at the frames used by
nearby blocks.
Change-Id: I1199149f8662a408537c653d2c021c7f1d29a700
2011-11-18 21:47:16 +01:00
|
|
|
if (this_ref_frame < 0)
|
2010-05-18 17:58:33 +02:00
|
|
|
continue;
|
|
|
|
|
Speed selection support for disabled reference frames
There was an implicit reference frame test order (typically LAST,
GOLD, ARF) in the mode selection logic, but this doesn't provide the
expected results when some reference frames are disabled. For
instance, in real-time mode, the speed selection logic often disables
the ARF modes. So if the user disables the LAST and GOLD frames, the
encoder was always choosing INTRA, when in reality searching the ARF
in this case has the same speed penalty as searching LAST would have
had.
Instead, introduce the notion of a reference frame search order. This
patch preserves the former priorities, so if a frame is disabled, the
other frames bump up a slot to take its place. This patch lays the
groundwork for doing something smarter in the frame test order, for
example considering temporal distance or looking at the frames used by
nearby blocks.
Change-Id: I1199149f8662a408537c653d2c021c7f1d29a700
2011-11-18 21:47:16 +01:00
|
|
|
x->e_mbd.mode_info_context->mbmi.ref_frame = this_ref_frame;
|
|
|
|
|
2012-05-21 23:30:56 +02:00
|
|
|
/* everything but intra */
|
2010-08-12 22:25:43 +02:00
|
|
|
if (x->e_mbd.mode_info_context->mbmi.ref_frame)
|
2010-05-18 17:58:33 +02:00
|
|
|
{
|
2012-01-11 23:38:58 +01:00
|
|
|
x->e_mbd.pre.y_buffer = plane[this_ref_frame][0];
|
|
|
|
x->e_mbd.pre.u_buffer = plane[this_ref_frame][1];
|
|
|
|
x->e_mbd.pre.v_buffer = plane[this_ref_frame][2];
|
2011-12-14 21:35:04 +01:00
|
|
|
|
2012-01-25 23:55:49 +01:00
|
|
|
if (sign_bias != cpi->common.ref_frame_sign_bias[this_ref_frame])
|
2011-12-14 21:35:04 +01:00
|
|
|
{
|
2012-01-25 23:55:49 +01:00
|
|
|
sign_bias = cpi->common.ref_frame_sign_bias[this_ref_frame];
|
|
|
|
mode_mv = mode_mv_sb[sign_bias];
|
|
|
|
best_ref_mv.as_int = best_ref_mv_sb[sign_bias].as_int;
|
2011-12-14 21:35:04 +01:00
|
|
|
}
|
2011-12-16 22:50:29 +01:00
|
|
|
|
|
|
|
#if CONFIG_MULTI_RES_ENCODING
|
2012-10-09 00:54:24 +02:00
|
|
|
if (parent_ref_valid)
|
2011-12-16 22:50:29 +01:00
|
|
|
{
|
|
|
|
if (vp8_mode_order[mode_index] == NEARESTMV &&
|
|
|
|
mode_mv[NEARESTMV].as_int ==0)
|
|
|
|
continue;
|
|
|
|
if (vp8_mode_order[mode_index] == NEARMV &&
|
|
|
|
mode_mv[NEARMV].as_int ==0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (vp8_mode_order[mode_index] == NEWMV && parent_mode == ZEROMV
|
2012-05-21 23:30:56 +02:00
|
|
|
&& best_ref_mv.as_int==0)
|
2011-12-16 22:50:29 +01:00
|
|
|
continue;
|
|
|
|
else if(vp8_mode_order[mode_index] == NEWMV && dissim==0
|
|
|
|
&& best_ref_mv.as_int==parent_ref_mv.as_int)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check to see if the testing frequency for this mode is at its max
|
|
|
|
* If so then prevent it from being tested and increase the threshold
|
|
|
|
* for its testing */
|
2012-12-05 01:52:47 +01:00
|
|
|
if (x->mode_test_hit_counts[mode_index] &&
|
2011-12-16 22:50:29 +01:00
|
|
|
(cpi->mode_check_freq[mode_index] > 1))
|
|
|
|
{
|
2012-12-05 01:52:47 +01:00
|
|
|
if (x->mbs_tested_so_far <= (cpi->mode_check_freq[mode_index] *
|
|
|
|
x->mode_test_hit_counts[mode_index]))
|
2011-12-16 22:50:29 +01:00
|
|
|
{
|
|
|
|
/* Increase the threshold for coding this mode to make it less
|
|
|
|
* likely to be chosen */
|
2012-11-07 01:27:00 +01:00
|
|
|
x->rd_thresh_mult[mode_index] += 4;
|
2011-12-16 22:50:29 +01:00
|
|
|
|
2012-11-07 01:27:00 +01:00
|
|
|
if (x->rd_thresh_mult[mode_index] > MAX_THRESHMULT)
|
|
|
|
x->rd_thresh_mult[mode_index] = MAX_THRESHMULT;
|
2011-12-16 22:50:29 +01:00
|
|
|
|
2012-11-07 01:27:00 +01:00
|
|
|
x->rd_threshes[mode_index] =
|
2011-12-16 22:50:29 +01:00
|
|
|
(cpi->rd_baseline_thresh[mode_index] >> 7) *
|
2012-11-07 01:27:00 +01:00
|
|
|
x->rd_thresh_mult[mode_index];
|
2011-12-16 22:50:29 +01:00
|
|
|
continue;
|
|
|
|
}
|
2010-05-18 17:58:33 +02:00
|
|
|
}
|
|
|
|
|
2011-12-16 22:50:29 +01:00
|
|
|
/* We have now reached the point where we are going to test the current
|
|
|
|
* mode so increment the counter for the number of times it has been
|
|
|
|
* tested */
|
2012-12-05 01:52:47 +01:00
|
|
|
x->mode_test_hit_counts[mode_index] ++;
|
2011-12-16 22:50:29 +01:00
|
|
|
|
|
|
|
rate2 = 0;
|
|
|
|
distortion2 = 0;
|
|
|
|
|
|
|
|
this_mode = vp8_mode_order[mode_index];
|
|
|
|
|
|
|
|
x->e_mbd.mode_info_context->mbmi.mode = this_mode;
|
|
|
|
x->e_mbd.mode_info_context->mbmi.uv_mode = DC_PRED;
|
|
|
|
|
|
|
|
/* Work out the cost assosciated with selecting the reference frame */
|
|
|
|
frame_cost =
|
2012-02-02 19:40:08 +01:00
|
|
|
x->ref_frame_cost[x->e_mbd.mode_info_context->mbmi.ref_frame];
|
2011-12-16 22:50:29 +01:00
|
|
|
rate2 += frame_cost;
|
|
|
|
|
|
|
|
/* Only consider ZEROMV/ALTREF_FRAME for alt ref frame,
|
|
|
|
* unless ARNR filtering is enabled in which case we want
|
|
|
|
* an unfiltered alternative */
|
2011-01-14 15:52:15 +01:00
|
|
|
if (cpi->is_src_frame_alt_ref && (cpi->oxcf.arnr_max_frames == 0))
|
2010-05-18 17:58:33 +02:00
|
|
|
{
|
2011-12-16 22:50:29 +01:00
|
|
|
if (this_mode != ZEROMV ||
|
|
|
|
x->e_mbd.mode_info_context->mbmi.ref_frame != ALTREF_FRAME)
|
2010-05-18 17:58:33 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (this_mode)
|
|
|
|
{
|
|
|
|
case B_PRED:
|
2011-12-16 22:50:29 +01:00
|
|
|
/* Pass best so far to pick_intra4x4mby_modes to use as breakout */
|
2012-03-06 10:48:18 +01:00
|
|
|
distortion2 = best_rd_sse;
|
2012-01-13 01:55:44 +01:00
|
|
|
pick_intra4x4mby_modes(x, &rate, &distortion2);
|
2010-05-18 17:58:33 +02:00
|
|
|
|
|
|
|
if (distortion2 == INT_MAX)
|
|
|
|
{
|
|
|
|
this_rd = INT_MAX;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-04-14 19:31:56 +02:00
|
|
|
rate2 += rate;
|
2012-01-13 01:55:44 +01:00
|
|
|
distortion2 = vp8_variance16x16(
|
2011-06-23 19:54:02 +02:00
|
|
|
*(b->base_src), b->src_stride,
|
2011-06-08 22:45:29 +02:00
|
|
|
x->e_mbd.predictor, 16, &sse);
|
2011-04-07 22:57:25 +02:00
|
|
|
this_rd = RDCOST(x->rdmult, x->rddiv, rate2, distortion2);
|
2010-05-18 17:58:33 +02:00
|
|
|
|
|
|
|
if (this_rd < best_intra_rd)
|
|
|
|
{
|
|
|
|
best_intra_rd = this_rd;
|
2011-04-08 15:21:36 +02:00
|
|
|
*returnintra = distortion2;
|
2010-05-18 17:58:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SPLITMV:
|
|
|
|
|
2012-05-21 23:30:56 +02:00
|
|
|
/* Split MV modes currently not supported when RD is not enabled. */
|
2010-05-18 17:58:33 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case DC_PRED:
|
|
|
|
case V_PRED:
|
|
|
|
case H_PRED:
|
|
|
|
case TM_PRED:
|
2012-03-20 20:11:42 +01:00
|
|
|
vp8_build_intra_predictors_mby_s(xd,
|
|
|
|
xd->dst.y_buffer - xd->dst.y_stride,
|
|
|
|
xd->dst.y_buffer - 1,
|
|
|
|
xd->dst.y_stride,
|
|
|
|
xd->predictor,
|
|
|
|
16);
|
2012-01-13 01:55:44 +01:00
|
|
|
distortion2 = vp8_variance16x16
|
2011-06-23 19:54:02 +02:00
|
|
|
(*(b->base_src), b->src_stride,
|
2011-06-08 22:45:29 +02:00
|
|
|
x->e_mbd.predictor, 16, &sse);
|
2010-08-12 22:25:43 +02:00
|
|
|
rate2 += x->mbmode_cost[x->e_mbd.frame_type][x->e_mbd.mode_info_context->mbmi.mode];
|
2011-04-07 22:57:25 +02:00
|
|
|
this_rd = RDCOST(x->rdmult, x->rddiv, rate2, distortion2);
|
2010-05-18 17:58:33 +02:00
|
|
|
|
|
|
|
if (this_rd < best_intra_rd)
|
|
|
|
{
|
|
|
|
best_intra_rd = this_rd;
|
2011-04-08 15:21:36 +02:00
|
|
|
*returnintra = distortion2;
|
2010-05-18 17:58:33 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NEWMV:
|
|
|
|
{
|
|
|
|
int thissme;
|
|
|
|
int step_param;
|
|
|
|
int further_steps;
|
|
|
|
int n = 0;
|
2011-06-01 20:41:05 +02:00
|
|
|
int sadpb = x->sadperbit16;
|
2011-07-07 17:21:41 +02:00
|
|
|
int_mv mvp_full;
|
2010-05-18 17:58:33 +02:00
|
|
|
|
2011-12-22 01:11:31 +01:00
|
|
|
int col_min = ((best_ref_mv.as_mv.col+7)>>3) - MAX_FULL_PEL_VAL;
|
|
|
|
int row_min = ((best_ref_mv.as_mv.row+7)>>3) - MAX_FULL_PEL_VAL;
|
2011-12-16 22:50:29 +01:00
|
|
|
int col_max = (best_ref_mv.as_mv.col>>3)
|
|
|
|
+ MAX_FULL_PEL_VAL;
|
|
|
|
int row_max = (best_ref_mv.as_mv.row>>3)
|
|
|
|
+ MAX_FULL_PEL_VAL;
|
2011-01-28 16:00:20 +01:00
|
|
|
|
|
|
|
int tmp_col_min = x->mv_col_min;
|
|
|
|
int tmp_col_max = x->mv_col_max;
|
|
|
|
int tmp_row_min = x->mv_row_min;
|
|
|
|
int tmp_row_max = x->mv_row_max;
|
|
|
|
|
2011-02-11 15:43:37 +01:00
|
|
|
int speed_adjust = (cpi->Speed > 5) ? ((cpi->Speed >= 8)? 3 : 2) : 1;
|
2010-05-18 17:58:33 +02:00
|
|
|
|
2012-05-21 23:30:56 +02:00
|
|
|
/* Further step/diamond searches as necessary */
|
2011-02-11 15:43:37 +01:00
|
|
|
step_param = cpi->sf.first_step + speed_adjust;
|
|
|
|
|
2011-12-16 22:50:29 +01:00
|
|
|
#if CONFIG_MULTI_RES_ENCODING
|
2012-06-08 17:17:50 +02:00
|
|
|
/* If lower-res drops this frame, then higher-res encoder does
|
|
|
|
motion search without any previous knowledge. Also, since
|
|
|
|
last frame motion info is not stored, then we can not
|
|
|
|
use improved_mv_pred. */
|
2012-10-09 00:54:24 +02:00
|
|
|
if (cpi->oxcf.mr_encoder_id && !parent_ref_valid)
|
2013-04-12 03:19:18 +02:00
|
|
|
sf_improved_mv_pred = 0;
|
2012-06-08 17:17:50 +02:00
|
|
|
|
2012-10-09 00:54:24 +02:00
|
|
|
if (parent_ref_valid && parent_ref_frame)
|
2011-12-16 22:50:29 +01:00
|
|
|
{
|
2012-05-21 23:30:56 +02:00
|
|
|
/* Use parent MV as predictor. Adjust search range
|
|
|
|
* accordingly.
|
|
|
|
*/
|
2011-12-16 22:50:29 +01:00
|
|
|
mvp.as_int = parent_ref_mv.as_int;
|
|
|
|
mvp_full.as_mv.col = parent_ref_mv.as_mv.col>>3;
|
|
|
|
mvp_full.as_mv.row = parent_ref_mv.as_mv.row>>3;
|
|
|
|
|
|
|
|
if(dissim <=32) step_param += 3;
|
|
|
|
else if(dissim <=128) step_param += 2;
|
|
|
|
else step_param += 1;
|
|
|
|
}else
|
|
|
|
#endif
|
2010-05-18 17:58:33 +02:00
|
|
|
{
|
2013-04-12 03:19:18 +02:00
|
|
|
if(sf_improved_mv_pred)
|
2011-07-08 20:08:45 +02:00
|
|
|
{
|
2011-12-16 22:50:29 +01:00
|
|
|
if(!saddone)
|
|
|
|
{
|
|
|
|
vp8_cal_sad(cpi,xd,x, recon_yoffset ,&near_sadidx[0] );
|
|
|
|
saddone = 1;
|
|
|
|
}
|
2011-07-08 20:08:45 +02:00
|
|
|
|
2011-12-16 22:50:29 +01:00
|
|
|
vp8_mv_pred(cpi, &x->e_mbd, x->e_mbd.mode_info_context,
|
|
|
|
&mvp,x->e_mbd.mode_info_context->mbmi.ref_frame,
|
|
|
|
cpi->common.ref_frame_sign_bias, &sr,
|
|
|
|
&near_sadidx[0]);
|
2011-07-08 20:08:45 +02:00
|
|
|
|
2011-12-16 22:50:29 +01:00
|
|
|
sr += speed_adjust;
|
2012-05-21 23:30:56 +02:00
|
|
|
/* adjust search range according to sr from mv prediction */
|
2011-12-16 22:50:29 +01:00
|
|
|
if(sr > step_param)
|
|
|
|
step_param = sr;
|
2011-07-07 17:21:41 +02:00
|
|
|
|
2011-12-16 22:50:29 +01:00
|
|
|
mvp_full.as_mv.col = mvp.as_mv.col>>3;
|
|
|
|
mvp_full.as_mv.row = mvp.as_mv.row>>3;
|
|
|
|
}else
|
|
|
|
{
|
|
|
|
mvp.as_int = best_ref_mv.as_int;
|
|
|
|
mvp_full.as_mv.col = best_ref_mv.as_mv.col>>3;
|
|
|
|
mvp_full.as_mv.row = best_ref_mv.as_mv.row>>3;
|
|
|
|
}
|
|
|
|
}
|
2011-07-07 17:21:41 +02:00
|
|
|
|
2011-12-16 22:50:29 +01:00
|
|
|
#if CONFIG_MULTI_RES_ENCODING
|
2012-10-09 00:54:24 +02:00
|
|
|
if (parent_ref_valid && parent_ref_frame && dissim <= 2 &&
|
2011-12-16 22:50:29 +01:00
|
|
|
MAX(abs(best_ref_mv.as_mv.row - parent_ref_mv.as_mv.row),
|
|
|
|
abs(best_ref_mv.as_mv.col - parent_ref_mv.as_mv.col)) <= 4)
|
2011-02-11 15:43:37 +01:00
|
|
|
{
|
2011-12-16 22:50:29 +01:00
|
|
|
d->bmi.mv.as_int = mvp_full.as_int;
|
|
|
|
mode_mv[NEWMV].as_int = mvp_full.as_int;
|
2010-05-18 17:58:33 +02:00
|
|
|
|
2011-12-16 22:50:29 +01:00
|
|
|
cpi->find_fractional_mv_step(x, b, d, &d->bmi.mv, &best_ref_mv,
|
|
|
|
x->errorperbit,
|
|
|
|
&cpi->fn_ptr[BLOCK_16X16],
|
|
|
|
cpi->mb.mvcost,
|
|
|
|
&distortion2,&sse);
|
|
|
|
}else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
/* Get intersection of UMV window and valid MV window to
|
|
|
|
* reduce # of checks in diamond search. */
|
|
|
|
if (x->mv_col_min < col_min )
|
|
|
|
x->mv_col_min = col_min;
|
|
|
|
if (x->mv_col_max > col_max )
|
|
|
|
x->mv_col_max = col_max;
|
|
|
|
if (x->mv_row_min < row_min )
|
|
|
|
x->mv_row_min = row_min;
|
|
|
|
if (x->mv_row_max > row_max )
|
|
|
|
x->mv_row_max = row_max;
|
2011-06-02 23:33:17 +02:00
|
|
|
|
2011-12-16 22:50:29 +01:00
|
|
|
further_steps = (cpi->Speed >= 8)?
|
|
|
|
0: (cpi->sf.max_step_search_steps - 1 - step_param);
|
2011-02-11 15:43:37 +01:00
|
|
|
|
2011-12-16 22:50:29 +01:00
|
|
|
if (cpi->sf.search_method == HEX)
|
|
|
|
{
|
2011-10-25 21:14:16 +02:00
|
|
|
#if CONFIG_MULTI_RES_ENCODING
|
|
|
|
/* TODO: In higher-res pick_inter_mode, step_param is used to
|
|
|
|
* modify hex search range. Here, set step_param to 0 not to
|
|
|
|
* change the behavior in lowest-resolution encoder.
|
|
|
|
* Will improve it later.
|
|
|
|
*/
|
2012-06-08 17:17:50 +02:00
|
|
|
/* Set step_param to 0 to ensure large-range motion search
|
|
|
|
when encoder drops this frame at lower-resolution.
|
|
|
|
*/
|
2012-10-09 00:54:24 +02:00
|
|
|
if (!parent_ref_valid)
|
2011-12-16 22:50:29 +01:00
|
|
|
step_param = 0;
|
2011-10-25 21:14:16 +02:00
|
|
|
#endif
|
2011-12-16 22:50:29 +01:00
|
|
|
bestsme = vp8_hex_search(x, b, d, &mvp_full, &d->bmi.mv,
|
|
|
|
step_param, sadpb,
|
|
|
|
&cpi->fn_ptr[BLOCK_16X16],
|
|
|
|
x->mvsadcost, x->mvcost, &best_ref_mv);
|
|
|
|
mode_mv[NEWMV].as_int = d->bmi.mv.as_int;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bestsme = cpi->diamond_search_sad(x, b, d, &mvp_full,
|
|
|
|
&d->bmi.mv, step_param, sadpb, &num00,
|
|
|
|
&cpi->fn_ptr[BLOCK_16X16],
|
|
|
|
x->mvcost, &best_ref_mv);
|
|
|
|
mode_mv[NEWMV].as_int = d->bmi.mv.as_int;
|
2010-05-18 17:58:33 +02:00
|
|
|
|
2012-05-21 23:30:56 +02:00
|
|
|
/* Further step/diamond searches as necessary */
|
2011-12-16 22:50:29 +01:00
|
|
|
n = num00;
|
|
|
|
num00 = 0;
|
2010-05-18 17:58:33 +02:00
|
|
|
|
2011-12-16 22:50:29 +01:00
|
|
|
while (n < further_steps)
|
2010-05-18 17:58:33 +02:00
|
|
|
{
|
2011-12-16 22:50:29 +01:00
|
|
|
n++;
|
|
|
|
|
|
|
|
if (num00)
|
|
|
|
num00--;
|
2010-05-18 17:58:33 +02:00
|
|
|
else
|
|
|
|
{
|
2011-12-16 22:50:29 +01:00
|
|
|
thissme =
|
|
|
|
cpi->diamond_search_sad(x, b, d, &mvp_full,
|
|
|
|
&d->bmi.mv,
|
|
|
|
step_param + n,
|
|
|
|
sadpb, &num00,
|
|
|
|
&cpi->fn_ptr[BLOCK_16X16],
|
|
|
|
x->mvcost, &best_ref_mv);
|
|
|
|
if (thissme < bestsme)
|
|
|
|
{
|
|
|
|
bestsme = thissme;
|
|
|
|
mode_mv[NEWMV].as_int = d->bmi.mv.as_int;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
d->bmi.mv.as_int = mode_mv[NEWMV].as_int;
|
|
|
|
}
|
2010-05-18 17:58:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-16 22:50:29 +01:00
|
|
|
x->mv_col_min = tmp_col_min;
|
|
|
|
x->mv_col_max = tmp_col_max;
|
|
|
|
x->mv_row_min = tmp_row_min;
|
|
|
|
x->mv_row_max = tmp_row_max;
|
2010-05-18 17:58:33 +02:00
|
|
|
|
2011-12-16 22:50:29 +01:00
|
|
|
if (bestsme < INT_MAX)
|
|
|
|
cpi->find_fractional_mv_step(x, b, d, &d->bmi.mv,
|
|
|
|
&best_ref_mv, x->errorperbit,
|
2011-06-01 20:41:05 +02:00
|
|
|
&cpi->fn_ptr[BLOCK_16X16],
|
|
|
|
cpi->mb.mvcost,
|
|
|
|
&distortion2,&sse);
|
2011-12-16 22:50:29 +01:00
|
|
|
}
|
2010-05-18 17:58:33 +02:00
|
|
|
|
2011-05-12 16:50:16 +02:00
|
|
|
mode_mv[NEWMV].as_int = d->bmi.mv.as_int;
|
2010-05-18 17:58:33 +02:00
|
|
|
|
2012-05-21 23:30:56 +02:00
|
|
|
/* mv cost; */
|
2011-12-16 22:50:29 +01:00
|
|
|
rate2 += vp8_mv_bit_cost(&mode_mv[NEWMV], &best_ref_mv,
|
|
|
|
cpi->mb.mvcost, 128);
|
2011-01-28 16:00:20 +01:00
|
|
|
}
|
2010-05-18 17:58:33 +02:00
|
|
|
|
|
|
|
case NEARESTMV:
|
|
|
|
case NEARMV:
|
|
|
|
|
2011-05-12 16:50:16 +02:00
|
|
|
if (mode_mv[this_mode].as_int == 0)
|
2010-05-18 17:58:33 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
case ZEROMV:
|
|
|
|
|
2011-12-16 22:50:29 +01:00
|
|
|
/* Trap vectors that reach beyond the UMV borders
|
|
|
|
* Note that ALL New MV, Nearest MV Near MV and Zero MV code drops
|
|
|
|
* through to this point because of the lack of break statements
|
|
|
|
* in the previous two cases.
|
|
|
|
*/
|
|
|
|
if (((mode_mv[this_mode].as_mv.row >> 3) < x->mv_row_min) ||
|
|
|
|
((mode_mv[this_mode].as_mv.row >> 3) > x->mv_row_max) ||
|
|
|
|
((mode_mv[this_mode].as_mv.col >> 3) < x->mv_col_min) ||
|
|
|
|
((mode_mv[this_mode].as_mv.col >> 3) > x->mv_col_max))
|
2010-05-18 17:58:33 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
rate2 += vp8_cost_mv_ref(this_mode, mdcounts);
|
2011-05-31 20:24:42 +02:00
|
|
|
x->e_mbd.mode_info_context->mbmi.mv.as_int =
|
|
|
|
mode_mv[this_mode].as_int;
|
2012-08-21 19:52:35 +02:00
|
|
|
this_rd = evaluate_inter_mode(&sse, rate2, &distortion2, cpi, x,
|
|
|
|
rd_adjustment);
|
2010-05-18 17:58:33 +02:00
|
|
|
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-03-06 10:48:18 +01:00
|
|
|
#if CONFIG_TEMPORAL_DENOISING
|
|
|
|
if (cpi->oxcf.noise_sensitivity)
|
|
|
|
{
|
2012-05-24 16:44:03 +02:00
|
|
|
|
2012-05-21 23:30:56 +02:00
|
|
|
/* Store for later use by denoiser. */
|
2012-05-24 16:44:03 +02:00
|
|
|
if (this_mode == ZEROMV && sse < zero_mv_sse )
|
|
|
|
{
|
|
|
|
zero_mv_sse = sse;
|
|
|
|
x->best_zeromv_reference_frame =
|
|
|
|
x->e_mbd.mode_info_context->mbmi.ref_frame;
|
|
|
|
}
|
|
|
|
|
2012-05-21 23:30:56 +02:00
|
|
|
/* Store the best NEWMV in x for later use in the denoiser. */
|
2012-05-24 16:44:03 +02:00
|
|
|
if (x->e_mbd.mode_info_context->mbmi.mode == NEWMV &&
|
|
|
|
sse < best_sse)
|
|
|
|
{
|
|
|
|
best_sse = sse;
|
|
|
|
x->best_sse_inter_mode = NEWMV;
|
|
|
|
x->best_sse_mv = x->e_mbd.mode_info_context->mbmi.mv;
|
|
|
|
x->need_to_clamp_best_mvs =
|
|
|
|
x->e_mbd.mode_info_context->mbmi.need_to_clamp_mvs;
|
|
|
|
x->best_reference_frame =
|
|
|
|
x->e_mbd.mode_info_context->mbmi.ref_frame;
|
|
|
|
}
|
2012-03-06 10:48:18 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2010-05-18 17:58:33 +02:00
|
|
|
if (this_rd < best_rd || x->skip)
|
|
|
|
{
|
2012-05-21 23:30:56 +02:00
|
|
|
/* Note index of best mode */
|
2010-05-18 17:58:33 +02:00
|
|
|
best_mode_index = mode_index;
|
|
|
|
|
|
|
|
*returnrate = rate2;
|
|
|
|
*returndistortion = distortion2;
|
2012-03-06 10:48:18 +01:00
|
|
|
best_rd_sse = sse;
|
2010-05-18 17:58:33 +02:00
|
|
|
best_rd = this_rd;
|
2011-12-16 22:50:29 +01:00
|
|
|
vpx_memcpy(&best_mbmode, &x->e_mbd.mode_info_context->mbmi,
|
|
|
|
sizeof(MB_MODE_INFO));
|
|
|
|
|
|
|
|
/* Testing this mode gave rise to an improvement in best error
|
|
|
|
* score. Lower threshold a bit for next time
|
|
|
|
*/
|
2012-11-07 01:27:00 +01:00
|
|
|
x->rd_thresh_mult[mode_index] =
|
|
|
|
(x->rd_thresh_mult[mode_index] >= (MIN_THRESHMULT + 2)) ?
|
|
|
|
x->rd_thresh_mult[mode_index] - 2 : MIN_THRESHMULT;
|
|
|
|
x->rd_threshes[mode_index] =
|
2011-12-16 22:50:29 +01:00
|
|
|
(cpi->rd_baseline_thresh[mode_index] >> 7) *
|
2012-11-07 01:27:00 +01:00
|
|
|
x->rd_thresh_mult[mode_index];
|
2010-05-18 17:58:33 +02:00
|
|
|
}
|
|
|
|
|
2011-12-16 22:50:29 +01:00
|
|
|
/* If the mode did not help improve the best error case then raise the
|
|
|
|
* threshold for testing that mode next time around.
|
|
|
|
*/
|
2010-05-18 17:58:33 +02:00
|
|
|
else
|
|
|
|
{
|
2012-11-07 01:27:00 +01:00
|
|
|
x->rd_thresh_mult[mode_index] += 4;
|
2010-05-18 17:58:33 +02:00
|
|
|
|
2012-11-07 01:27:00 +01:00
|
|
|
if (x->rd_thresh_mult[mode_index] > MAX_THRESHMULT)
|
|
|
|
x->rd_thresh_mult[mode_index] = MAX_THRESHMULT;
|
2010-05-18 17:58:33 +02:00
|
|
|
|
2012-11-07 01:27:00 +01:00
|
|
|
x->rd_threshes[mode_index] =
|
2011-12-16 22:50:29 +01:00
|
|
|
(cpi->rd_baseline_thresh[mode_index] >> 7) *
|
2012-11-07 01:27:00 +01:00
|
|
|
x->rd_thresh_mult[mode_index];
|
2010-05-18 17:58:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (x->skip)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-05-21 23:30:56 +02:00
|
|
|
/* Reduce the activation RD thresholds for the best choice mode */
|
2010-05-18 17:58:33 +02:00
|
|
|
if ((cpi->rd_baseline_thresh[best_mode_index] > 0) && (cpi->rd_baseline_thresh[best_mode_index] < (INT_MAX >> 2)))
|
|
|
|
{
|
2012-11-07 01:27:00 +01:00
|
|
|
int best_adjustment = (x->rd_thresh_mult[best_mode_index] >> 3);
|
2010-05-18 17:58:33 +02:00
|
|
|
|
2012-11-07 01:27:00 +01:00
|
|
|
x->rd_thresh_mult[best_mode_index] =
|
|
|
|
(x->rd_thresh_mult[best_mode_index]
|
2011-12-16 22:50:29 +01:00
|
|
|
>= (MIN_THRESHMULT + best_adjustment)) ?
|
2012-11-07 01:27:00 +01:00
|
|
|
x->rd_thresh_mult[best_mode_index] - best_adjustment :
|
2011-12-16 22:50:29 +01:00
|
|
|
MIN_THRESHMULT;
|
2012-11-07 01:27:00 +01:00
|
|
|
x->rd_threshes[best_mode_index] =
|
2011-12-16 22:50:29 +01:00
|
|
|
(cpi->rd_baseline_thresh[best_mode_index] >> 7) *
|
2012-11-07 01:27:00 +01:00
|
|
|
x->rd_thresh_mult[best_mode_index];
|
2010-05-18 17:58:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
int this_rdbin = (*returndistortion >> 7);
|
|
|
|
|
|
|
|
if (this_rdbin >= 1024)
|
|
|
|
{
|
|
|
|
this_rdbin = 1023;
|
|
|
|
}
|
|
|
|
|
2012-12-11 02:32:58 +01:00
|
|
|
x->error_bins[this_rdbin] ++;
|
2010-05-18 17:58:33 +02:00
|
|
|
}
|
|
|
|
|
2012-03-06 10:48:18 +01:00
|
|
|
#if CONFIG_TEMPORAL_DENOISING
|
|
|
|
if (cpi->oxcf.noise_sensitivity)
|
|
|
|
{
|
2014-06-13 19:08:09 +02:00
|
|
|
int block_index = mb_row * cpi->common.mb_cols + mb_col;
|
2012-05-24 16:44:03 +02:00
|
|
|
if (x->best_sse_inter_mode == DC_PRED)
|
|
|
|
{
|
2012-05-21 23:30:56 +02:00
|
|
|
/* No best MV found. */
|
2012-05-24 16:44:03 +02:00
|
|
|
x->best_sse_inter_mode = best_mbmode.mode;
|
|
|
|
x->best_sse_mv = best_mbmode.mv;
|
|
|
|
x->need_to_clamp_best_mvs = best_mbmode.need_to_clamp_mvs;
|
|
|
|
x->best_reference_frame = best_mbmode.ref_frame;
|
|
|
|
best_sse = best_rd_sse;
|
|
|
|
}
|
2014-05-16 23:32:04 +02:00
|
|
|
x->increase_denoising = 0;
|
2012-05-24 16:44:03 +02:00
|
|
|
vp8_denoiser_denoise_mb(&cpi->denoiser, x, best_sse, zero_mv_sse,
|
2014-06-13 19:08:09 +02:00
|
|
|
recon_yoffset, recon_uvoffset,
|
|
|
|
&cpi->common.lf_info, mb_row, mb_col,
|
|
|
|
block_index);
|
2012-03-06 10:48:18 +01:00
|
|
|
|
2012-05-24 16:44:03 +02:00
|
|
|
|
2012-05-21 23:30:56 +02:00
|
|
|
/* Reevaluate ZEROMV after denoising. */
|
2012-05-24 16:44:03 +02:00
|
|
|
if (best_mbmode.ref_frame == INTRA_FRAME &&
|
|
|
|
x->best_zeromv_reference_frame != INTRA_FRAME)
|
2012-03-06 10:48:18 +01:00
|
|
|
{
|
2012-05-24 16:44:03 +02:00
|
|
|
int this_rd = 0;
|
|
|
|
int this_ref_frame = x->best_zeromv_reference_frame;
|
|
|
|
rate2 = x->ref_frame_cost[this_ref_frame] +
|
|
|
|
vp8_cost_mv_ref(ZEROMV, mdcounts);
|
|
|
|
distortion2 = 0;
|
|
|
|
|
2012-05-21 23:30:56 +02:00
|
|
|
/* set up the proper prediction buffers for the frame */
|
2012-05-24 16:44:03 +02:00
|
|
|
x->e_mbd.mode_info_context->mbmi.ref_frame = this_ref_frame;
|
|
|
|
x->e_mbd.pre.y_buffer = plane[this_ref_frame][0];
|
|
|
|
x->e_mbd.pre.u_buffer = plane[this_ref_frame][1];
|
|
|
|
x->e_mbd.pre.v_buffer = plane[this_ref_frame][2];
|
|
|
|
|
|
|
|
x->e_mbd.mode_info_context->mbmi.mode = ZEROMV;
|
|
|
|
x->e_mbd.mode_info_context->mbmi.uv_mode = DC_PRED;
|
|
|
|
x->e_mbd.mode_info_context->mbmi.mv.as_int = 0;
|
2012-08-21 19:52:35 +02:00
|
|
|
this_rd = evaluate_inter_mode(&sse, rate2, &distortion2, cpi, x,
|
|
|
|
rd_adjustment);
|
2012-05-24 16:44:03 +02:00
|
|
|
|
|
|
|
if (this_rd < best_rd)
|
|
|
|
{
|
|
|
|
vpx_memcpy(&best_mbmode, &x->e_mbd.mode_info_context->mbmi,
|
|
|
|
sizeof(MB_MODE_INFO));
|
|
|
|
}
|
2012-03-06 10:48:18 +01:00
|
|
|
}
|
2012-05-24 16:44:03 +02:00
|
|
|
|
2012-03-06 10:48:18 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-05-31 20:24:42 +02:00
|
|
|
if (cpi->is_src_frame_alt_ref &&
|
|
|
|
(best_mbmode.mode != ZEROMV || best_mbmode.ref_frame != ALTREF_FRAME))
|
2010-05-18 17:58:33 +02:00
|
|
|
{
|
2011-05-31 20:24:42 +02:00
|
|
|
x->e_mbd.mode_info_context->mbmi.mode = ZEROMV;
|
|
|
|
x->e_mbd.mode_info_context->mbmi.ref_frame = ALTREF_FRAME;
|
2010-08-12 22:25:43 +02:00
|
|
|
x->e_mbd.mode_info_context->mbmi.mv.as_int = 0;
|
2011-05-31 20:24:42 +02:00
|
|
|
x->e_mbd.mode_info_context->mbmi.uv_mode = DC_PRED;
|
|
|
|
x->e_mbd.mode_info_context->mbmi.mb_skip_coeff =
|
2011-12-22 01:11:31 +01:00
|
|
|
(cpi->common.mb_no_coeff_skip);
|
2011-05-31 20:24:42 +02:00
|
|
|
x->e_mbd.mode_info_context->mbmi.partitioning = 0;
|
|
|
|
|
2011-04-11 19:05:08 +02:00
|
|
|
return;
|
2010-05-18 17:58:33 +02:00
|
|
|
}
|
|
|
|
|
2011-12-16 22:50:29 +01:00
|
|
|
/* set to the best mb mode, this copy can be skip if x->skip since it
|
|
|
|
* already has the right content */
|
2011-09-01 18:11:23 +02:00
|
|
|
if (!x->skip)
|
2011-12-16 22:50:29 +01:00
|
|
|
vpx_memcpy(&x->e_mbd.mode_info_context->mbmi, &best_mbmode,
|
|
|
|
sizeof(MB_MODE_INFO));
|
2010-05-18 17:58:33 +02:00
|
|
|
|
2011-05-31 20:24:42 +02:00
|
|
|
if (best_mbmode.mode <= B_PRED)
|
|
|
|
{
|
|
|
|
/* set mode_info_context->mbmi.uv_mode */
|
2011-06-08 17:24:52 +02:00
|
|
|
pick_intra_mbuv_mode(x);
|
2011-05-31 20:24:42 +02:00
|
|
|
}
|
2011-05-24 19:24:52 +02:00
|
|
|
|
2011-12-14 21:35:04 +01:00
|
|
|
if (sign_bias
|
2011-12-16 22:50:29 +01:00
|
|
|
!= cpi->common.ref_frame_sign_bias[xd->mode_info_context->mbmi.ref_frame])
|
2012-01-25 23:55:49 +01:00
|
|
|
best_ref_mv.as_int = best_ref_mv_sb[!sign_bias].as_int;
|
2011-12-14 21:35:04 +01:00
|
|
|
|
2012-12-05 00:43:25 +01:00
|
|
|
update_mvcount(x, &best_ref_mv);
|
2010-05-18 17:58:33 +02:00
|
|
|
}
|
2011-06-08 17:24:52 +02:00
|
|
|
|
|
|
|
|
2012-11-02 20:10:07 +01:00
|
|
|
void vp8_pick_intra_mode(MACROBLOCK *x, int *rate_)
|
2011-06-08 17:24:52 +02:00
|
|
|
{
|
|
|
|
int error4x4, error16x16 = INT_MAX;
|
2011-06-13 17:21:01 +02:00
|
|
|
int rate, best_rate = 0, distortion, best_sse;
|
2011-06-08 17:24:52 +02:00
|
|
|
MB_PREDICTION_MODE mode, best_mode = DC_PRED;
|
|
|
|
int this_rd;
|
2011-06-08 22:45:29 +02:00
|
|
|
unsigned int sse;
|
2011-06-23 19:54:02 +02:00
|
|
|
BLOCK *b = &x->block[0];
|
2012-03-20 20:11:42 +01:00
|
|
|
MACROBLOCKD *xd = &x->e_mbd;
|
2011-06-08 17:24:52 +02:00
|
|
|
|
2012-03-20 20:11:42 +01:00
|
|
|
xd->mode_info_context->mbmi.ref_frame = INTRA_FRAME;
|
2011-06-08 18:05:05 +02:00
|
|
|
|
2011-06-08 17:24:52 +02:00
|
|
|
pick_intra_mbuv_mode(x);
|
|
|
|
|
|
|
|
for (mode = DC_PRED; mode <= TM_PRED; mode ++)
|
|
|
|
{
|
2012-03-20 20:11:42 +01:00
|
|
|
xd->mode_info_context->mbmi.mode = mode;
|
|
|
|
vp8_build_intra_predictors_mby_s(xd,
|
|
|
|
xd->dst.y_buffer - xd->dst.y_stride,
|
|
|
|
xd->dst.y_buffer - 1,
|
|
|
|
xd->dst.y_stride,
|
|
|
|
xd->predictor,
|
|
|
|
16);
|
2012-01-13 01:55:44 +01:00
|
|
|
distortion = vp8_variance16x16
|
2012-03-20 20:11:42 +01:00
|
|
|
(*(b->base_src), b->src_stride, xd->predictor, 16, &sse);
|
|
|
|
rate = x->mbmode_cost[xd->frame_type][mode];
|
2011-06-08 17:24:52 +02:00
|
|
|
this_rd = RDCOST(x->rdmult, x->rddiv, rate, distortion);
|
|
|
|
|
|
|
|
if (error16x16 > this_rd)
|
|
|
|
{
|
|
|
|
error16x16 = this_rd;
|
|
|
|
best_mode = mode;
|
2011-06-13 17:21:01 +02:00
|
|
|
best_sse = sse;
|
2011-06-08 19:08:12 +02:00
|
|
|
best_rate = rate;
|
2011-06-08 17:24:52 +02:00
|
|
|
}
|
|
|
|
}
|
2012-03-20 20:11:42 +01:00
|
|
|
xd->mode_info_context->mbmi.mode = best_mode;
|
2011-06-08 17:24:52 +02:00
|
|
|
|
2012-01-13 01:55:44 +01:00
|
|
|
error4x4 = pick_intra4x4mby_modes(x, &rate,
|
2011-06-13 17:21:01 +02:00
|
|
|
&best_sse);
|
2011-06-08 17:24:52 +02:00
|
|
|
if (error4x4 < error16x16)
|
2011-06-08 19:08:12 +02:00
|
|
|
{
|
2012-03-20 20:11:42 +01:00
|
|
|
xd->mode_info_context->mbmi.mode = B_PRED;
|
2011-06-08 19:08:12 +02:00
|
|
|
best_rate = rate;
|
|
|
|
}
|
2011-06-08 17:24:52 +02:00
|
|
|
|
2011-06-08 19:08:12 +02:00
|
|
|
*rate_ = best_rate;
|
2011-06-08 17:24:52 +02:00
|
|
|
}
|