/* * Copyright (c) 2010 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.
*/
// Initialize the segmentation index as 0.
mi->segment_id = 0;
// Skip the rest if AQ mode is disabled. if (!seg->enabled) return;
switch (aq_mode) { case CYCLIC_REFRESH_AQ:
mi->segment_id = get_segment_id(cm, map, bsize, mi_row, mi_col); break; #if !CONFIG_REALTIME_ONLY case VARIANCE_AQ: if (cm->frame_type == KEY_FRAME || cpi->refresh_alt_ref_frame ||
cpi->force_update_segmentation ||
(cpi->refresh_golden_frame && !cpi->rc.is_src_frame_alt_ref)) { int min_energy; int max_energy; // Get sub block energy range if (bsize >= BLOCK_32X32) {
vp9_get_sub_block_energy(cpi, x, mi_row, mi_col, bsize, &min_energy,
&max_energy);
} else {
min_energy = bsize <= BLOCK_16X16 ? x->mb_energy
: vp9_block_energy(cpi, x, bsize);
}
mi->segment_id = vp9_vaq_segment_id(min_energy);
} else {
mi->segment_id = get_segment_id(cm, map, bsize, mi_row, mi_col);
} break; case EQUATOR360_AQ: if (cm->frame_type == KEY_FRAME || cpi->force_update_segmentation)
mi->segment_id = vp9_360aq_segment_id(mi_row, cm->mi_rows); else
mi->segment_id = get_segment_id(cm, map, bsize, mi_row, mi_col); break; #endif case LOOKAHEAD_AQ:
mi->segment_id = get_segment_id(cm, map, bsize, mi_row, mi_col); break; case PSNR_AQ: mi->segment_id = segment_index; break; case PERCEPTUAL_AQ: mi->segment_id = x->segment_id; break; default: // NO_AQ or PSNR_AQ break;
}
// Set segment index if ROI map or active_map is enabled. if (cpi->roi.enabled || cpi->active_map.enabled)
mi->segment_id = get_segment_id(cm, map, bsize, mi_row, mi_col);
vp9_init_plane_quantizers(cpi, x);
}
// Lighter version of set_offsets that only sets the mode info // pointers. staticINLINEvoid set_mode_info_offsets(VP9_COMMON *const cm,
MACROBLOCK *const x,
MACROBLOCKD *const xd, int mi_row, int mi_col) { constint idx_str = xd->mi_stride * mi_row + mi_col;
xd->mi = cm->mi_grid_visible + idx_str;
xd->mi[0] = cm->mi + idx_str;
x->mbmi_ext = x->mbmi_ext_base + (mi_row * cm->mi_cols + mi_col);
}
// Set up destination pointers.
vp9_setup_dst_planes(xd->plane, get_frame_new_buffer(cm), mi_row, mi_col);
// Set up limit values for MV components. // Mv beyond the range do not produce new/different prediction block.
mv_limits->row_min = -(((mi_row + mi_height) * MI_SIZE) + VP9_INTERP_EXTEND);
mv_limits->col_min = -(((mi_col + mi_width) * MI_SIZE) + VP9_INTERP_EXTEND);
mv_limits->row_max = (cm->mi_rows - mi_row) * MI_SIZE + VP9_INTERP_EXTEND;
mv_limits->col_max = (cm->mi_cols - mi_col) * MI_SIZE + VP9_INTERP_EXTEND;
// Set up distance of MB to edge of frame in 1/8th pel units.
assert(!(mi_col & (mi_width - 1)) && !(mi_row & (mi_height - 1)));
set_mi_row_col(xd, tile, mi_row, mi_height, mi_col, mi_width, cm->mi_rows,
cm->mi_cols);
// Set up source buffers.
vp9_setup_src_planes(x, cpi->Source, mi_row, mi_col);
typedefstruct { // This struct is used for computing variance in choose_partitioning(), where // the max number of samples within a superblock is 16x16 (with 4x4 avg). Even // in high bitdepth, uint32_t is enough for sum_square_error (2^12 * 2^12 * 16 // * 16 = 2^32).
uint32_t sum_square_error;
int32_t sum_error; int log2_count; int variance;
} Var;
typedefstruct {
Var none;
Var horz[2];
Var vert[2];
} partition_variance;
typedefstruct {
partition_variance part_variances;
Var split[4];
} v4x4;
// For bsize=bsize_min (16x16/8x8 for 8x8/4x4 downsampling), select if // variance is below threshold, otherwise split will be selected. // No check for vert/horiz split as too few samples for variance. if (bsize == bsize_min) { // Variance already computed to set the force_split. if (frame_is_intra_only(cm)) get_variance(&vt.part_variances->none); if (mi_col + block_width / 2 < cm->mi_cols &&
mi_row + block_height / 2 < cm->mi_rows &&
vt.part_variances->none.variance < threshold) {
set_block_size(cpi, x, xd, mi_row, mi_col, bsize); return 1;
} return 0;
} elseif (bsize > bsize_min) { // Variance already computed to set the force_split. if (frame_is_intra_only(cm)) get_variance(&vt.part_variances->none); // For key frame: take split for bsize above 32X32 or very high variance. if (frame_is_intra_only(cm) &&
(bsize > BLOCK_32X32 ||
vt.part_variances->none.variance > (threshold << 4))) { return 0;
} // If variance is low, take the bsize (no split). if (mi_col + block_width / 2 < cm->mi_cols &&
mi_row + block_height / 2 < cm->mi_rows &&
vt.part_variances->none.variance < threshold) {
set_block_size(cpi, x, xd, mi_row, mi_col, bsize); return 1;
}
// Set the variance split thresholds for following the block sizes: // 0 - threshold_64x64, 1 - threshold_32x32, 2 - threshold_16x16, // 3 - vbp_threshold_8x8. vbp_threshold_8x8 (to split to 4x4 partition) is // currently only used on key frame. staticvoid set_vbp_thresholds(VP9_COMP *cpi, int64_t thresholds[], int q, int content_state) {
VP9_COMMON *const cm = &cpi->common; constint is_key_frame = frame_is_intra_only(cm); constint threshold_multiplier =
is_key_frame ? 20 : cpi->sf.variance_part_thresh_mult;
int64_t threshold_base =
(int64_t)(threshold_multiplier * cpi->y_dequant[q][1]);
// Compute the minmax over the 8x8 subblocks. staticint compute_minmax_8x8(const uint8_t *s, int sp, const uint8_t *d, int dp, int x16_idx, int y16_idx, #if CONFIG_VP9_HIGHBITDEPTH int highbd_flag, #endif int pixels_wide, int pixels_high) { int k; int minmax_max = 0; int minmax_min = 255; // Loop over the 4 8x8 subblocks. for (k = 0; k < 4; k++) { int x8_idx = x16_idx + ((k & 1) << 3); int y8_idx = y16_idx + ((k >> 1) << 3); int min = 0; int max = 0; if (x8_idx < pixels_wide && y8_idx < pixels_high) { #if CONFIG_VP9_HIGHBITDEPTH if (highbd_flag & YV12_FLAG_HIGHBITDEPTH) {
vpx_highbd_minmax_8x8(s + y8_idx * sp + x8_idx, sp,
d + y8_idx * dp + x8_idx, dp, &min, &max);
} else {
vpx_minmax_8x8(s + y8_idx * sp + x8_idx, sp, d + y8_idx * dp + x8_idx,
dp, &min, &max);
} #else
vpx_minmax_8x8(s + y8_idx * sp + x8_idx, sp, d + y8_idx * dp + x8_idx, dp,
&min, &max); #endif if ((max - min) > minmax_max) minmax_max = (max - min); if ((max - min) < minmax_min) minmax_min = (max - min);
}
} return (minmax_max - minmax_min);
}
staticvoid fill_variance_4x4avg(const uint8_t *s, int sp, const uint8_t *d, int dp, int x8_idx, int y8_idx, v8x8 *vst, #if CONFIG_VP9_HIGHBITDEPTH int highbd_flag, #endif int pixels_wide, int pixels_high, int is_key_frame) { int k; for (k = 0; k < 4; k++) { int x4_idx = x8_idx + ((k & 1) << 2); int y4_idx = y8_idx + ((k >> 1) << 2); unsignedint sse = 0; int sum = 0; if (x4_idx < pixels_wide && y4_idx < pixels_high) { int s_avg; int d_avg = 128; #if CONFIG_VP9_HIGHBITDEPTH if (highbd_flag & YV12_FLAG_HIGHBITDEPTH) {
s_avg = vpx_highbd_avg_4x4(s + y4_idx * sp + x4_idx, sp); if (!is_key_frame)
d_avg = vpx_highbd_avg_4x4(d + y4_idx * dp + x4_idx, dp);
} else {
s_avg = vpx_avg_4x4(s + y4_idx * sp + x4_idx, sp); if (!is_key_frame) d_avg = vpx_avg_4x4(d + y4_idx * dp + x4_idx, dp);
} #else
s_avg = vpx_avg_4x4(s + y4_idx * sp + x4_idx, sp); if (!is_key_frame) d_avg = vpx_avg_4x4(d + y4_idx * dp + x4_idx, dp); #endif
sum = s_avg - d_avg;
sse = sum * sum;
}
fill_variance(sse, sum, 0, &vst->split[k].part_variances.none);
}
}
staticvoid fill_variance_8x8avg(const uint8_t *s, int sp, const uint8_t *d, int dp, int x16_idx, int y16_idx, v16x16 *vst, #if CONFIG_VP9_HIGHBITDEPTH int highbd_flag, #endif int pixels_wide, int pixels_high, int is_key_frame) { int k; for (k = 0; k < 4; k++) { int x8_idx = x16_idx + ((k & 1) << 3); int y8_idx = y16_idx + ((k >> 1) << 3); unsignedint sse = 0; int sum = 0; if (x8_idx < pixels_wide && y8_idx < pixels_high) { int s_avg; int d_avg = 128; #if CONFIG_VP9_HIGHBITDEPTH if (highbd_flag & YV12_FLAG_HIGHBITDEPTH) {
s_avg = vpx_highbd_avg_8x8(s + y8_idx * sp + x8_idx, sp); if (!is_key_frame)
d_avg = vpx_highbd_avg_8x8(d + y8_idx * dp + x8_idx, dp);
} else {
s_avg = vpx_avg_8x8(s + y8_idx * sp + x8_idx, sp); if (!is_key_frame) d_avg = vpx_avg_8x8(d + y8_idx * dp + x8_idx, dp);
} #else
s_avg = vpx_avg_8x8(s + y8_idx * sp + x8_idx, sp); if (!is_key_frame) d_avg = vpx_avg_8x8(d + y8_idx * dp + x8_idx, dp); #endif
sum = s_avg - d_avg;
sse = sum * sum;
}
fill_variance(sse, sum, 0, &vst->split[k].part_variances.none);
}
}
// Check if most of the superblock is skin content, and if so, force split to // 32x32, and set x->sb_is_skin for use in mode selection. staticint skin_sb_split(VP9_COMP *cpi, constint low_res, int mi_row, int mi_col, int *force_split) {
VP9_COMMON *const cm = &cpi->common; #if CONFIG_VP9_HIGHBITDEPTH if (cm->use_highbitdepth) return 0; #endif // Avoid checking superblocks on/near boundary and avoid low resolutions. // Note superblock may still pick 64X64 if y_sad is very small // (i.e., y_sad < cpi->vbp_threshold_sad) below. For now leave this as is. if (!low_res && (mi_col >= 8 && mi_col + 8 < cm->mi_cols && mi_row >= 8 &&
mi_row + 8 < cm->mi_rows)) { int num_16x16_skin = 0; int num_16x16_nonskin = 0; constint block_index = mi_row * cm->mi_cols + mi_col; constint bw = num_8x8_blocks_wide_lookup[BLOCK_64X64]; constint bh = num_8x8_blocks_high_lookup[BLOCK_64X64]; constint xmis = VPXMIN(cm->mi_cols - mi_col, bw); constint ymis = VPXMIN(cm->mi_rows - mi_row, bh); // Loop through the 16x16 sub-blocks. int i, j; for (i = 0; i < ymis; i += 2) { for (j = 0; j < xmis; j += 2) { int bl_index = block_index + i * cm->mi_cols + j; int is_skin = cpi->skin_map[bl_index];
num_16x16_skin += is_skin;
num_16x16_nonskin += (1 - is_skin); if (num_16x16_nonskin > 3) { // Exit loop if at least 4 of the 16x16 blocks are not skin.
i = ymis; break;
}
}
} if (num_16x16_skin > 12) {
*force_split = 1; return 1;
}
} return 0;
}
staticvoid set_low_temp_var_flag(VP9_COMP *cpi, MACROBLOCK *x, MACROBLOCKD *xd,
v64x64 *vt, int64_t thresholds[],
MV_REFERENCE_FRAME ref_frame_partition, int mi_col, int mi_row) { int i, j;
VP9_COMMON *const cm = &cpi->common; constint mv_thr = cm->width > 640 ? 8 : 4; // Check temporal variance for bsize >= 16x16, if LAST_FRAME was selected and // int_pro mv is small. If the temporal variance is small set the flag // variance_low for the block. The variance threshold can be adjusted, the // higher the more aggressive. if (ref_frame_partition == LAST_FRAME &&
(cpi->sf.short_circuit_low_temp_var == 1 ||
(xd->mi[0]->mv[0].as_mv.col < mv_thr &&
xd->mi[0]->mv[0].as_mv.col > -mv_thr &&
xd->mi[0]->mv[0].as_mv.row < mv_thr &&
xd->mi[0]->mv[0].as_mv.row > -mv_thr))) { if (xd->mi[0]->sb_type == BLOCK_64X64) { if ((vt->part_variances).none.variance < (thresholds[0] >> 1))
x->variance_low[0] = 1;
} elseif (xd->mi[0]->sb_type == BLOCK_64X32) { for (i = 0; i < 2; i++) { if (vt->part_variances.horz[i].variance < (thresholds[0] >> 2))
x->variance_low[i + 1] = 1;
}
} elseif (xd->mi[0]->sb_type == BLOCK_32X64) { for (i = 0; i < 2; i++) { if (vt->part_variances.vert[i].variance < (thresholds[0] >> 2))
x->variance_low[i + 3] = 1;
}
} else { for (i = 0; i < 4; i++) { constint idx[4][2] = { { 0, 0 }, { 0, 4 }, { 4, 0 }, { 4, 4 } }; constint idx_str =
cm->mi_stride * (mi_row + idx[i][0]) + mi_col + idx[i][1];
MODE_INFO **this_mi = cm->mi_grid_visible + idx_str;
staticint copy_partitioning(VP9_COMP *cpi, MACROBLOCK *x, MACROBLOCKD *xd, int mi_row, int mi_col, int segment_id, int sb_offset) { int svc_copy_allowed = 1; int frames_since_key_thresh = 1; if (cpi->use_svc) { // For SVC, don't allow copy if base spatial layer is key frame, or if // frame is not a temporal enhancement layer frame. int layer = LAYER_IDS_TO_IDX(0, cpi->svc.temporal_layer_id,
cpi->svc.number_temporal_layers); const LAYER_CONTEXT *lc = &cpi->svc.layer_context[layer]; if (lc->is_key_frame || !cpi->svc.non_reference_frame) svc_copy_allowed = 0;
frames_since_key_thresh = cpi->svc.number_spatial_layers << 1;
} if (cpi->rc.frames_since_key > frames_since_key_thresh && svc_copy_allowed &&
!cpi->resize_pending && segment_id == CR_SEGMENT_ID_BASE &&
cpi->prev_segment_id[sb_offset] == CR_SEGMENT_ID_BASE &&
cpi->copied_frame_cnt[sb_offset] < cpi->max_copied_frame) { if (cpi->prev_partition != NULL) {
copy_partitioning_helper(cpi, x, xd, BLOCK_64X64, mi_row, mi_col);
cpi->copied_frame_cnt[sb_offset] += 1;
memcpy(x->variance_low, &(cpi->prev_variance_low[sb_offset * 25]), sizeof(x->variance_low)); return 1;
}
}
return 0;
}
// Set the partition for mi_col/row_high (current resolution) based on // the previous spatial layer (mi_col/row). Returns 0 if partition is set, // returns 1 if no scale partitioning is done. Return 1 means the variance // partitioning will be used. staticint scale_partitioning_svc(VP9_COMP *cpi, MACROBLOCK *x, MACROBLOCKD *xd,
BLOCK_SIZE bsize, int mi_row, int mi_col, int mi_row_high, int mi_col_high) {
VP9_COMMON *const cm = &cpi->common;
SVC *const svc = &cpi->svc;
BLOCK_SIZE *prev_part = svc->prev_partition_svc; // Variables with _high are for higher resolution. int bsize_high = 0; int subsize_high = 0; constint bsl = b_width_log2_lookup[bsize]; constint bs = (1 << bsl) >> 2; constint has_rows = (mi_row_high + bs) < cm->mi_rows; constint has_cols = (mi_col_high + bs) < cm->mi_cols;
int start_pos;
BLOCK_SIZE bsize_low;
PARTITION_TYPE partition_high;
// If the lower layer frame is outside the boundary (this can happen for // odd size resolutions) then do not scale partitioning from the lower // layer. Do variance based partitioning instead (return 1). if (mi_row >= svc->mi_rows[svc->spatial_layer_id - 1] ||
mi_col >= svc->mi_cols[svc->spatial_layer_id - 1]) return 1;
// Do not scale partitioning from lower layers on the boundary. Do // variance based partitioning instead (return 1). if (!has_rows || !has_cols) return 1;
// For reference frames: return 1 (do variance-based partitioning) if the // superblock is not low source sad and lower-resoln bsize is below 32x32. if (!cpi->svc.non_reference_frame && !x->skip_low_source_sad &&
bsize_low < BLOCK_32X32) return 1;
// Scale up block size by 2x2. Force 64x64 for size larger than 32x32. if (bsize_low < BLOCK_32X32) {
bsize_high = bsize_low + 3;
} elseif (bsize_low >= BLOCK_32X32) {
bsize_high = BLOCK_64X64;
}
staticvoid update_prev_partition(VP9_COMP *cpi, MACROBLOCK *x, int segment_id, int mi_row, int mi_col, int sb_offset) {
update_prev_partition_helper(cpi, BLOCK_64X64, mi_row, mi_col);
cpi->prev_segment_id[sb_offset] = segment_id;
memcpy(&(cpi->prev_variance_low[sb_offset * 25]), x->variance_low, sizeof(x->variance_low)); // Reset the counter for copy partitioning
cpi->copied_frame_cnt[sb_offset] = 0;
}
staticvoid chroma_check(VP9_COMP *cpi, MACROBLOCK *x, int bsize, unsignedint y_sad, int is_key_frame, int scene_change_detected) { int i;
MACROBLOCKD *xd = &x->e_mbd; int shift = 2;
if (is_key_frame) return;
// For speed > 8, avoid the chroma check if y_sad is above threshold. if (cpi->oxcf.speed > 8) { if (y_sad > cpi->vbp_thresholds[1] &&
(!cpi->noise_estimate.enabled ||
vp9_noise_estimate_extract_level(&cpi->noise_estimate) < kMedium)) return;
}
if (cpi->oxcf.content == VP9E_CONTENT_SCREEN && scene_change_detected)
shift = 5;
for (i = 1; i <= 2; ++i) { unsignedint uv_sad = UINT_MAX; struct macroblock_plane *p = &x->plane[i]; struct macroblockd_plane *pd = &xd->plane[i]; const BLOCK_SIZE bs = get_plane_block_size(bsize, pd);
if (bs != BLOCK_INVALID)
uv_sad = cpi->fn_ptr[bs].sdf(p->src.buf, p->src.stride, pd->dst.buf,
pd->dst.stride);
// TODO(marpan): Investigate if we should lower this threshold if // superblock is detected as skin.
x->color_sensitivity[i - 1] = uv_sad > (y_sad >> shift);
}
}
if (cpi->content_state_sb_fd != NULL) { if (tmp_sad < avg_source_sad_threshold2) { // Cap the increment to 255. if (cpi->content_state_sb_fd[sb_offset] < 255)
cpi->content_state_sb_fd[sb_offset]++;
} else {
cpi->content_state_sb_fd[sb_offset] = 0;
}
} if (tmp_sad == 0) x->zero_temp_sad_source = 1; return tmp_sad;
}
// This function chooses partitioning based on the variance between source and // reconstructed last, where variance is computed for down-sampled inputs. staticint choose_partitioning(VP9_COMP *cpi, const TileInfo *const tile,
MACROBLOCK *x, int mi_row, int mi_col) {
VP9_COMMON *const cm = &cpi->common;
MACROBLOCKD *xd = &x->e_mbd; int i, j, k, m;
v64x64 vt;
v16x16 *vt2 = NULL; int force_split[21]; int avg_32x32; int max_var_32x32 = 0; int min_var_32x32 = INT_MAX; int var_32x32; int avg_16x16[4]; int maxvar_16x16[4]; int minvar_16x16[4];
int64_t threshold_4x4avg;
NOISE_LEVEL noise_level = kLow; int content_state = 0;
uint8_t *s; const uint8_t *d; int sp; int dp; int compute_minmax_variance = 1; unsignedint y_sad = UINT_MAX;
BLOCK_SIZE bsize = BLOCK_64X64; // Ref frame used in partitioning.
MV_REFERENCE_FRAME ref_frame_partition = LAST_FRAME; int pixels_wide = 64, pixels_high = 64;
int64_t thresholds[4] = { cpi->vbp_thresholds[0], cpi->vbp_thresholds[1],
cpi->vbp_thresholds[2], cpi->vbp_thresholds[3] }; int scene_change_detected =
cpi->rc.high_source_sad ||
(cpi->use_svc && cpi->svc.high_source_sad_superframe); int force_64_split = scene_change_detected ||
(cpi->oxcf.content == VP9E_CONTENT_SCREEN &&
cpi->compute_source_sad_onepass &&
cpi->sf.use_source_sad && !x->zero_temp_sad_source);
// For the variance computation under SVC mode, we treat the frame as key if // the reference (base layer frame) is key frame (i.e., is_key_frame == 1). int is_key_frame =
(frame_is_intra_only(cm) ||
(is_one_pass_svc(cpi) &&
cpi->svc.layer_context[cpi->svc.temporal_layer_id].is_key_frame));
if (!is_key_frame) { if (cm->frame_refs[LAST_FRAME - 1].sf.x_scale_fp == REF_INVALID_SCALE ||
cm->frame_refs[LAST_FRAME - 1].sf.y_scale_fp == REF_INVALID_SCALE)
is_key_frame = 1;
}
// Always use 4x4 partition for key frame. constint use_4x4_partition = frame_is_intra_only(cm); constint low_res = (cm->width <= 352 && cm->height <= 288); int variance4x4downsample[16]; int segment_id; int sb_offset = (cm->mi_stride >> 3) * (mi_row >> 3) + (mi_col >> 3);
// For SVC: check if LAST frame is NULL or if the resolution of LAST is // different than the current frame resolution, and if so, treat this frame // as a key frame, for the purpose of the superblock partitioning. // LAST == NULL can happen in some cases where enhancement spatial layers are // enabled dyanmically in the stream and the only reference is the spatial // reference (GOLDEN). if (cpi->use_svc) { const YV12_BUFFER_CONFIG *const ref = get_ref_frame_buffer(cpi, LAST_FRAME); if (ref == NULL || ref->y_crop_height != cm->height ||
ref->y_crop_width != cm->width)
is_key_frame = 1;
}
// For SVC on top spatial layer: use/scale the partition from // the lower spatial resolution if svc_use_lowres_part is enabled. if (cpi->sf.svc_use_lowres_part &&
cpi->svc.spatial_layer_id == cpi->svc.number_spatial_layers - 1 &&
cpi->svc.prev_partition_svc != NULL && content_state != kVeryHighSad) { if (!scale_partitioning_svc(cpi, x, xd, BLOCK_64X64, mi_row >> 1,
mi_col >> 1, mi_row, mi_col)) { if (cpi->sf.copy_partition_flag) {
update_prev_partition(cpi, x, segment_id, mi_row, mi_col, sb_offset);
} return 0;
}
} // If source_sad is low copy the partition without computing the y_sad. if (x->skip_low_source_sad && cpi->sf.copy_partition_flag &&
!force_64_split &&
copy_partitioning(cpi, x, xd, mi_row, mi_col, segment_id, sb_offset)) {
x->sb_use_mv_part = 1; if (cpi->sf.svc_use_lowres_part &&
cpi->svc.spatial_layer_id == cpi->svc.number_spatial_layers - 2)
update_partition_svc(cpi, BLOCK_64X64, mi_row, mi_col); return 0;
}
}
if (cpi->oxcf.aq_mode == CYCLIC_REFRESH_AQ && cm->seg.enabled &&
cyclic_refresh_segment_id_boosted(segment_id)) { int q = vp9_get_qindex(&cm->seg, segment_id, cm->base_qindex);
set_vbp_thresholds(cpi, thresholds, q, content_state);
} else {
set_vbp_thresholds(cpi, thresholds, cm->base_qindex, content_state);
} // Decrease 32x32 split threshold for screen on base layer, for scene // change/high motion frames. if (cpi->oxcf.content == VP9E_CONTENT_SCREEN &&
cpi->svc.spatial_layer_id == 0 && force_64_split)
thresholds[1] = 3 * thresholds[1] >> 2;
// For non keyframes, disable 4x4 average for low resolution when speed = 8
threshold_4x4avg = (cpi->oxcf.speed < 8) ? thresholds[1] << 1 : INT64_MAX;
if (xd->mb_to_right_edge < 0) pixels_wide += (xd->mb_to_right_edge >> 3); if (xd->mb_to_bottom_edge < 0) pixels_high += (xd->mb_to_bottom_edge >> 3);
s = x->plane[0].src.buf;
sp = x->plane[0].src.stride;
// Index for force_split: 0 for 64x64, 1-4 for 32x32 blocks, // 5-20 for the 16x16 blocks.
force_split[0] = force_64_split;
if (!is_key_frame) { // In the case of spatial/temporal scalable coding, the assumption here is // that the temporal reference frame will always be of type LAST_FRAME. // TODO(marpan): If that assumption is broken, we need to revisit this code.
MODE_INFO *mi = xd->mi[0];
YV12_BUFFER_CONFIG *yv12 = get_ref_frame_buffer(cpi, LAST_FRAME);
if (!(is_one_pass_svc(cpi) && cpi->svc.spatial_layer_id) ||
cpi->svc.use_gf_temporal_ref_current_layer) { // For now, GOLDEN will not be used for non-zero spatial layers, since // it may not be a temporal reference.
yv12_g = get_ref_frame_buffer(cpi, GOLDEN_FRAME);
}
// Only compute y_sad_g (sad for golden reference) for speed < 8. if (cpi->oxcf.speed < 8 && yv12_g && yv12_g != yv12 &&
(cpi->ref_frame_flags & VP9_GOLD_FLAG)) {
vp9_setup_pre_planes(xd, 0, yv12_g, mi_row, mi_col,
&cm->frame_refs[GOLDEN_FRAME - 1].sf);
y_sad_g = cpi->fn_ptr[bsize].sdf(
x->plane[0].src.buf, x->plane[0].src.stride, xd->plane[0].pre[0].buf,
xd->plane[0].pre[0].stride);
} else {
y_sad_g = UINT_MAX;
}
if (cpi->use_skin_detection)
x->sb_is_skin = skin_sb_split(cpi, low_res, mi_row, mi_col, force_split);
d = xd->plane[0].dst.buf;
dp = xd->plane[0].dst.stride;
// If the y_sad is very small, take 64x64 as partition and exit. // Don't check on boosted segment for now, as 64x64 is suppressed there. if (segment_id == CR_SEGMENT_ID_BASE && y_sad < cpi->vbp_threshold_sad) { constint block_width = num_8x8_blocks_wide_lookup[BLOCK_64X64]; constint block_height = num_8x8_blocks_high_lookup[BLOCK_64X64]; if (mi_col + block_width / 2 < cm->mi_cols &&
mi_row + block_height / 2 < cm->mi_rows) {
set_block_size(cpi, x, xd, mi_row, mi_col, BLOCK_64X64);
x->variance_low[0] = 1;
chroma_check(cpi, x, bsize, y_sad, is_key_frame, scene_change_detected); if (cpi->sf.svc_use_lowres_part &&
cpi->svc.spatial_layer_id == cpi->svc.number_spatial_layers - 2)
update_partition_svc(cpi, BLOCK_64X64, mi_row, mi_col); if (cpi->sf.copy_partition_flag) {
update_prev_partition(cpi, x, segment_id, mi_row, mi_col, sb_offset);
} return 0;
}
}
// If the y_sad is small enough, copy the partition of the superblock in the // last frame to current frame only if the last frame is not a keyframe. // Stop the copy every cpi->max_copied_frame to refresh the partition. // TODO(jianj) : tune the threshold. if (cpi->sf.copy_partition_flag && y_sad_last < cpi->vbp_threshold_copy &&
copy_partitioning(cpi, x, xd, mi_row, mi_col, segment_id, sb_offset)) {
chroma_check(cpi, x, bsize, y_sad, is_key_frame, scene_change_detected); if (cpi->sf.svc_use_lowres_part &&
cpi->svc.spatial_layer_id == cpi->svc.number_spatial_layers - 2)
update_partition_svc(cpi, BLOCK_64X64, mi_row, mi_col); return 0;
}
} else {
d = VP9_VAR_OFFS;
dp = 0; #if CONFIG_VP9_HIGHBITDEPTH if (xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH) { switch (xd->bd) { case 10: d = CONVERT_TO_BYTEPTR(VP9_HIGH_VAR_OFFS_10); break; case 12: d = CONVERT_TO_BYTEPTR(VP9_HIGH_VAR_OFFS_12); break; case 8: default: d = CONVERT_TO_BYTEPTR(VP9_HIGH_VAR_OFFS_8); break;
}
} #endif// CONFIG_VP9_HIGHBITDEPTH
}
if (low_res && threshold_4x4avg < INT64_MAX)
CHECK_MEM_ERROR(&cm->error, vt2, vpx_calloc(16, sizeof(*vt2))); // Fill in the entire tree of 8x8 (or 4x4 under some conditions) variances // for splits. for (i = 0; i < 4; i++) { constint x32_idx = ((i & 1) << 5); constint y32_idx = ((i >> 1) << 5); constint i2 = i << 2;
force_split[i + 1] = 0;
avg_16x16[i] = 0;
maxvar_16x16[i] = 0;
minvar_16x16[i] = INT_MAX; for (j = 0; j < 4; j++) { constint x16_idx = x32_idx + ((j & 1) << 4); constint y16_idx = y32_idx + ((j >> 1) << 4); constint split_index = 5 + i2 + j;
v16x16 *vst = &vt.split[i].split[j];
force_split[split_index] = 0;
variance4x4downsample[i2 + j] = 0; if (!is_key_frame) {
fill_variance_8x8avg(s, sp, d, dp, x16_idx, y16_idx, vst, #if CONFIG_VP9_HIGHBITDEPTH
xd->cur_buf->flags, #endif
pixels_wide, pixels_high, is_key_frame);
fill_variance_tree(&vt.split[i].split[j], BLOCK_16X16);
get_variance(&vt.split[i].split[j].part_variances.none);
avg_16x16[i] += vt.split[i].split[j].part_variances.none.variance; if (vt.split[i].split[j].part_variances.none.variance < minvar_16x16[i])
minvar_16x16[i] = vt.split[i].split[j].part_variances.none.variance; if (vt.split[i].split[j].part_variances.none.variance > maxvar_16x16[i])
maxvar_16x16[i] = vt.split[i].split[j].part_variances.none.variance; if (vt.split[i].split[j].part_variances.none.variance > thresholds[2]) { // 16X16 variance is above threshold for split, so force split to 8x8 // for this 16x16 block (this also forces splits for upper levels).
force_split[split_index] = 1;
force_split[i + 1] = 1;
force_split[0] = 1;
} elseif (compute_minmax_variance &&
vt.split[i].split[j].part_variances.none.variance >
thresholds[1] &&
!cyclic_refresh_segment_id_boosted(segment_id)) { // We have some nominal amount of 16x16 variance (based on average), // compute the minmax over the 8x8 sub-blocks, and if above threshold, // force split to 8x8 block for this 16x16 block. int minmax = compute_minmax_8x8(s, sp, d, dp, x16_idx, y16_idx, #if CONFIG_VP9_HIGHBITDEPTH
xd->cur_buf->flags, #endif
pixels_wide, pixels_high); int thresh_minmax = (int)cpi->vbp_threshold_minmax; if (x->content_state_sb == kVeryHighSad)
thresh_minmax = thresh_minmax << 1; if (minmax > thresh_minmax) {
force_split[split_index] = 1;
force_split[i + 1] = 1;
force_split[0] = 1;
}
}
} if (is_key_frame ||
(low_res && vt.split[i].split[j].part_variances.none.variance >
threshold_4x4avg)) {
force_split[split_index] = 0; // Go down to 4x4 down-sampling for variance.
variance4x4downsample[i2 + j] = 1; for (k = 0; k < 4; k++) { int x8_idx = x16_idx + ((k & 1) << 3); int y8_idx = y16_idx + ((k >> 1) << 3);
v8x8 *vst2 = is_key_frame ? &vst->split[k] : &vt2[i2 + j].split[k];
fill_variance_4x4avg(s, sp, d, dp, x8_idx, y8_idx, vst2, #if CONFIG_VP9_HIGHBITDEPTH
xd->cur_buf->flags, #endif
pixels_wide, pixels_high, is_key_frame);
}
}
}
} if (cpi->noise_estimate.enabled)
noise_level = vp9_noise_estimate_extract_level(&cpi->noise_estimate); // Fill the rest of the variance tree by summing split partition values.
avg_32x32 = 0; for (i = 0; i < 4; i++) { constint i2 = i << 2; for (j = 0; j < 4; j++) { if (variance4x4downsample[i2 + j] == 1) {
v16x16 *vtemp = (!is_key_frame) ? &vt2[i2 + j] : &vt.split[i].split[j]; for (m = 0; m < 4; m++) fill_variance_tree(&vtemp->split[m], BLOCK_8X8);
fill_variance_tree(vtemp, BLOCK_16X16); // If variance of this 16x16 block is above the threshold, force block // to split. This also forces a split on the upper levels.
get_variance(&vtemp->part_variances.none); if (vtemp->part_variances.none.variance > thresholds[2]) {
force_split[5 + i2 + j] = 1;
force_split[i + 1] = 1;
force_split[0] = 1;
}
}
}
fill_variance_tree(&vt.split[i], BLOCK_32X32); // If variance of this 32x32 block is above the threshold, or if its above // (some threshold of) the average variance over the sub-16x16 blocks, then // force this block to split. This also forces a split on the upper // (64x64) level. if (!force_split[i + 1]) {
get_variance(&vt.split[i].part_variances.none);
var_32x32 = vt.split[i].part_variances.none.variance;
max_var_32x32 = VPXMAX(var_32x32, max_var_32x32);
min_var_32x32 = VPXMIN(var_32x32, min_var_32x32); if (vt.split[i].part_variances.none.variance > thresholds[1] ||
(!is_key_frame &&
vt.split[i].part_variances.none.variance > (thresholds[1] >> 1) &&
vt.split[i].part_variances.none.variance > (avg_16x16[i] >> 1))) {
force_split[i + 1] = 1;
force_split[0] = 1;
} elseif (!is_key_frame && noise_level < kLow && cm->height <= 360 &&
(maxvar_16x16[i] - minvar_16x16[i]) > (thresholds[1] >> 1) &&
maxvar_16x16[i] > thresholds[1]) {
force_split[i + 1] = 1;
force_split[0] = 1;
}
avg_32x32 += var_32x32;
}
} if (!force_split[0]) {
fill_variance_tree(&vt, BLOCK_64X64);
get_variance(&vt.part_variances.none); // If variance of this 64x64 block is above (some threshold of) the average // variance over the sub-32x32 blocks, then force this block to split. // Only checking this for noise level >= medium for now. if (!is_key_frame && noise_level >= kMedium &&
vt.part_variances.none.variance > (9 * avg_32x32) >> 5)
force_split[0] = 1; // Else if the maximum 32x32 variance minus the miniumum 32x32 variance in // a 64x64 block is greater than threshold and the maximum 32x32 variance is // above a miniumum threshold, then force the split of a 64x64 block // Only check this for low noise. elseif (!is_key_frame && noise_level < kMedium &&
(max_var_32x32 - min_var_32x32) > 3 * (thresholds[0] >> 3) &&
max_var_32x32 > thresholds[0] >> 1)
force_split[0] = 1;
}
// Now go through the entire structure, splitting every block size until // we get to one that's got a variance lower than our threshold. if (mi_col + 8 > cm->mi_cols || mi_row + 8 > cm->mi_rows ||
!set_vt_partitioning(cpi, x, xd, &vt, BLOCK_64X64, mi_row, mi_col,
thresholds[0], BLOCK_16X16, force_split[0])) { for (i = 0; i < 4; ++i) { constint x32_idx = ((i & 1) << 2); constint y32_idx = ((i >> 1) << 2); constint i2 = i << 2; if (!set_vt_partitioning(cpi, x, xd, &vt.split[i], BLOCK_32X32,
(mi_row + y32_idx), (mi_col + x32_idx),
thresholds[1], BLOCK_16X16,
force_split[i + 1])) { for (j = 0; j < 4; ++j) { constint x16_idx = ((j & 1) << 1); constint y16_idx = ((j >> 1) << 1); // For inter frames: if variance4x4downsample[] == 1 for this 16x16 // block, then the variance is based on 4x4 down-sampling, so use vt2 // in set_vt_partitioning(), otherwise use vt.
v16x16 *vtemp = (!is_key_frame && variance4x4downsample[i2 + j] == 1)
? &vt2[i2 + j]
: &vt.split[i].split[j]; if (!set_vt_partitioning(
cpi, x, xd, vtemp, BLOCK_16X16, mi_row + y32_idx + y16_idx,
mi_col + x32_idx + x16_idx, thresholds[2], cpi->vbp_bsize_min,
force_split[5 + i2 + j])) { for (k = 0; k < 4; ++k) { constint x8_idx = (k & 1); constint y8_idx = (k >> 1); if (use_4x4_partition) { if (!set_vt_partitioning(cpi, x, xd, &vtemp->split[k],
BLOCK_8X8,
mi_row + y32_idx + y16_idx + y8_idx,
mi_col + x32_idx + x16_idx + x8_idx,
thresholds[3], BLOCK_8X8, 0)) {
set_block_size(
cpi, x, xd, (mi_row + y32_idx + y16_idx + y8_idx),
(mi_col + x32_idx + x16_idx + x8_idx), BLOCK_4X4);
}
} else {
set_block_size(
cpi, x, xd, (mi_row + y32_idx + y16_idx + y8_idx),
(mi_col + x32_idx + x16_idx + x8_idx), BLOCK_8X8);
}
}
}
}
}
}
}
constint mis = cm->mi_stride; constint mi_width = num_8x8_blocks_wide_lookup[bsize]; constint mi_height = num_8x8_blocks_high_lookup[bsize]; int max_plane;
assert(mi->sb_type == bsize);
*mi_addr = *mi;
*x->mbmi_ext = ctx->mbmi_ext;
// If segmentation in use if (seg->enabled) { // For in frame complexity AQ copy the segment id from the segment map. if (cpi->oxcf.aq_mode == COMPLEXITY_AQ) { const uint8_t *const map =
seg->update_map ? cpi->segmentation_map : cm->last_frame_seg_map;
mi_addr->segment_id = get_segment_id(cm, map, bsize, mi_row, mi_col);
} // Else for cyclic refresh mode update the segment map, set the segment id // and then update the quantizer. if (cpi->oxcf.aq_mode == CYCLIC_REFRESH_AQ &&
cpi->cyclic_refresh->content_mode) {
vp9_cyclic_refresh_update_segment(cpi, xd->mi[0], mi_row, mi_col, bsize,
ctx->rate, ctx->dist, x->skip, p);
}
}
max_plane = is_inter_block(xdmi) ? MAX_MB_PLANE : 1; for (i = 0; i < max_plane; ++i) {
p[i].coeff = ctx->coeff_pbuf[i][1];
p[i].qcoeff = ctx->qcoeff_pbuf[i][1];
pd[i].dqcoeff = ctx->dqcoeff_pbuf[i][1];
p[i].eobs = ctx->eobs_pbuf[i][1];
}
for (i = max_plane; i < MAX_MB_PLANE; ++i) {
p[i].coeff = ctx->coeff_pbuf[i][2];
p[i].qcoeff = ctx->qcoeff_pbuf[i][2];
pd[i].dqcoeff = ctx->dqcoeff_pbuf[i][2];
p[i].eobs = ctx->eobs_pbuf[i][2];
}
// Restore the coding context of the MB to that that was in place // when the mode was picked for it for (y = 0; y < mi_height; y++) for (x_idx = 0; x_idx < mi_width; x_idx++) if ((xd->mb_to_right_edge >> (3 + MI_SIZE_LOG2)) + mi_width > x_idx &&
(xd->mb_to_bottom_edge >> (3 + MI_SIZE_LOG2)) + mi_height > y) {
xd->mi[x_idx + y * mis] = mi_addr;
}
if (cpi->oxcf.aq_mode != NO_AQ) vp9_init_plane_quantizers(cpi, x);
if (!frame_is_intra_only(cm)) {
FRAME_COUNTS *const counts = td->counts; constint inter_block = is_inter_block(mi); constint seg_ref_active =
segfeature_active(&cm->seg, mi->segment_id, SEG_LVL_REF_FRAME); if (!seg_ref_active) {
counts->intra_inter[get_intra_inter_context(xd)][inter_block]++; // If the segment reference feature is enabled we have only a single // reference frame allowed for the segment so exclude it from // the reference frame counts used to work out probabilities. if (inter_block) { const MV_REFERENCE_FRAME ref0 = mi->ref_frame[0]; if (cm->reference_mode == REFERENCE_MODE_SELECT)
counts->comp_inter[vp9_get_reference_mode_context(cm, xd)]
[has_second_ref(mi)]++;
// Check to see if the given partition size is allowed for a specified number // of 8x8 block rows and columns remaining in the image. // If not then return the largest allowed partition size static BLOCK_SIZE find_partition_size(BLOCK_SIZE bsize, int rows_left, int cols_left, int *bh, int *bw) { if (rows_left <= 0 || cols_left <= 0) { return VPXMIN(bsize, BLOCK_8X8);
} else { for (; bsize > 0; bsize -= 3) {
*bh = num_8x8_blocks_high_lookup[bsize];
*bw = num_8x8_blocks_wide_lookup[bsize]; if ((*bh <= rows_left) && (*bw <= cols_left)) { break;
}
}
} return bsize;
}
staticvoid set_partial_b64x64_partition(MODE_INFO *mi, int mis, int bh_in, int bw_in, int row8x8_remaining, int col8x8_remaining, BLOCK_SIZE bsize,
MODE_INFO **mi_8x8) { int bh = bh_in; int r, c; for (r = 0; r < MI_BLOCK_SIZE; r += bh) { int bw = bw_in; for (c = 0; c < MI_BLOCK_SIZE; c += bw) { constint index = r * mis + c;
mi_8x8[index] = mi + index;
mi_8x8[index]->sb_type = find_partition_size(
bsize, row8x8_remaining - r, col8x8_remaining - c, &bh, &bw);
}
}
}
// This function attempts to set all mode info entries in a given SB64 // to the same block partition size. // However, at the bottom and right borders of the image the requested size // may not be allowed in which case this code attempts to choose the largest // allowable partition. staticvoid set_fixed_partitioning(VP9_COMP *cpi, const TileInfo *const tile,
MODE_INFO **mi_8x8, int mi_row, int mi_col,
BLOCK_SIZE bsize) {
VP9_COMMON *const cm = &cpi->common; constint mis = cm->mi_stride; constint row8x8_remaining = tile->mi_row_end - mi_row; constint col8x8_remaining = tile->mi_col_end - mi_col; int block_row, block_col;
MODE_INFO *mi_upper_left = cm->mi + mi_row * mis + mi_col; int bh = num_8x8_blocks_high_lookup[bsize]; int bw = num_8x8_blocks_wide_lookup[bsize];
// Apply the requested partition size to the SB64 if it is all "in image" if ((col8x8_remaining >= MI_BLOCK_SIZE) &&
(row8x8_remaining >= MI_BLOCK_SIZE)) { for (block_row = 0; block_row < MI_BLOCK_SIZE; block_row += bh) { for (block_col = 0; block_col < MI_BLOCK_SIZE; block_col += bw) { int index = block_row * mis + block_col;
mi_8x8[index] = mi_upper_left + index;
mi_8x8[index]->sb_type = bsize;
}
}
} else { // Else this is a partial SB64.
set_partial_b64x64_partition(mi_upper_left, mis, bh, bw, row8x8_remaining,
col8x8_remaining, bsize, mi_8x8);
}
}
if (do_partition_search &&
cpi->sf.partition_search_type == SEARCH_PARTITION &&
cpi->sf.adjust_partitioning_from_last_frame) { // Check if any of the sub blocks are further split. if (partition == PARTITION_SPLIT && subsize > BLOCK_8X8) {
sub_subsize = get_subsize(subsize, PARTITION_SPLIT);
splits_below = 1; for (i = 0; i < 4; i++) { int jj = i >> 1, ii = i & 0x01;
MODE_INFO *this_mi = mi_8x8[jj * bss * mis + ii * bss]; if (this_mi && this_mi->sb_type >= sub_subsize) {
splits_below = 0;
}
}
}
// If partition is not none try none unless each of the 4 splits are split // even further.. if (partition != PARTITION_NONE && !splits_below &&
mi_row + (mi_step >> 1) < cm->mi_rows &&
mi_col + (mi_step >> 1) < cm->mi_cols) {
pc_tree->partitioning = PARTITION_NONE;
rd_pick_sb_modes(cpi, tile_data, x, mi_row, mi_col, &none_rdc, bsize, ctx,
INT_MAX, INT64_MAX);
// If last_part is better set the partitioning to that. if (last_part_rdc.rdcost < chosen_rdc.rdcost) {
mi_8x8[0]->sb_type = bsize; if (bsize >= BLOCK_8X8) pc_tree->partitioning = partition;
chosen_rdc = last_part_rdc;
} // If none was better set the partitioning to that. if (none_rdc.rdcost < chosen_rdc.rdcost) { if (bsize >= BLOCK_8X8) pc_tree->partitioning = PARTITION_NONE;
chosen_rdc = none_rdc;
}
restore_context(x, mi_row, mi_col, a, l, sa, sl, bsize);
// We must have chosen a partitioning and encoding or we'll fail later on. // No other opportunities for success. if (bsize == BLOCK_64X64)
assert(chosen_rdc.rate < INT_MAX && chosen_rdc.dist < INT64_MAX);
if (do_recon) { int output_enabled = (bsize == BLOCK_64X64);
encode_sb(cpi, td, tile_info, tp, mi_row, mi_col, output_enabled, bsize,
pc_tree);
}
// Look at all the mode_info entries for blocks that are part of this // partition and find the min and max values for sb_type. // At the moment this is designed to work on a 64x64 SB but could be // adjusted to use a size parameter. // // The min and max are assumed to have been initialized prior to calling this // function so repeat calls can accumulate a min and max of more than one sb64. staticvoid get_sb_partition_size_range(MACROBLOCKD *xd, MODE_INFO **mi_8x8,
BLOCK_SIZE *min_block_size,
BLOCK_SIZE *max_block_size, int bs_hist[BLOCK_SIZES]) { int sb_width_in_blocks = MI_BLOCK_SIZE; int sb_height_in_blocks = MI_BLOCK_SIZE; int i, j; int index = 0;
// Check the sb_type for each block that belongs to this region. for (i = 0; i < sb_height_in_blocks; ++i) { for (j = 0; j < sb_width_in_blocks; ++j) {
MODE_INFO *mi = mi_8x8[index + j];
BLOCK_SIZE sb_type = mi ? mi->sb_type : 0;
bs_hist[sb_type]++;
*min_block_size = VPXMIN(*min_block_size, sb_type);
*max_block_size = VPXMAX(*max_block_size, sb_type);
}
index += xd->mi_stride;
}
}
// Next square block size less or equal than current block size. staticconst BLOCK_SIZE next_square_size[BLOCK_SIZES] = {
BLOCK_4X4, BLOCK_4X4, BLOCK_4X4, BLOCK_8X8, BLOCK_8X8,
BLOCK_8X8, BLOCK_16X16, BLOCK_16X16, BLOCK_16X16, BLOCK_32X32,
BLOCK_32X32, BLOCK_32X32, BLOCK_64X64
};
// Look at neighboring blocks and set a min and max partition size based on // what they chose. staticvoid rd_auto_partition_range(VP9_COMP *cpi, const TileInfo *const tile,
MACROBLOCKD *const xd, int mi_row, int mi_col, BLOCK_SIZE *min_block_size,
BLOCK_SIZE *max_block_size) {
VP9_COMMON *const cm = &cpi->common;
MODE_INFO **mi = xd->mi; constint left_in_image = !!xd->left_mi; constint above_in_image = !!xd->above_mi; constint row8x8_remaining = tile->mi_row_end - mi_row; constint col8x8_remaining = tile->mi_col_end - mi_col; int bh, bw;
BLOCK_SIZE min_size = BLOCK_4X4;
BLOCK_SIZE max_size = BLOCK_64X64; int bs_hist[BLOCK_SIZES] = { 0 };
// Trap case where we do not have a prediction. if (left_in_image || above_in_image || cm->frame_type != KEY_FRAME) { // Default "min to max" and "max to min"
min_size = BLOCK_64X64;
max_size = BLOCK_4X4;
// NOTE: each call to get_sb_partition_size_range() uses the previous // passed in values for min and max as a starting point. // Find the min and max partition used in previous frame at this location if (cm->frame_type != KEY_FRAME) {
MODE_INFO **prev_mi =
&cm->prev_mi_grid_visible[mi_row * xd->mi_stride + mi_col];
get_sb_partition_size_range(xd, prev_mi, &min_size, &max_size, bs_hist);
} // Find the min and max partition sizes used in the left SB64 if (left_in_image) {
MODE_INFO **left_sb64_mi = &mi[-MI_BLOCK_SIZE];
get_sb_partition_size_range(xd, left_sb64_mi, &min_size, &max_size,
bs_hist);
} // Find the min and max partition sizes used in the above SB64. if (above_in_image) {
MODE_INFO **above_sb64_mi = &mi[-xd->mi_stride * MI_BLOCK_SIZE];
get_sb_partition_size_range(xd, above_sb64_mi, &min_size, &max_size,
bs_hist);
}
// Adjust observed min and max for "relaxed" auto partition case. if (cpi->sf.auto_min_max_partition_size == RELAXED_NEIGHBORING_MIN_MAX) {
min_size = min_partition_size[min_size];
max_size = max_partition_size[max_size];
}
}
// Check border cases where max and min from neighbors may not be legal.
max_size = find_partition_size(max_size, row8x8_remaining, col8x8_remaining,
&bh, &bw); // Test for blocks at the edge of the active image. // This may be the actual edge of the image or where there are formatting // bars. if (vp9_active_edge_sb(cpi, mi_row, mi_col)) {
min_size = BLOCK_4X4;
} else {
min_size =
VPXMIN(cpi->sf.rd_auto_partition_min_limit, VPXMIN(min_size, max_size));
}
// When use_square_partition_only is true, make sure at least one square // partition is allowed by selecting the next smaller square size as // *min_block_size. if (cpi->sf.use_square_partition_only &&
next_square_size[max_size] < min_size) {
min_size = next_square_size[max_size];
}
// Calculate prediction based on the given input features and neural net config. // Assume there are no more than NN_MAX_NODES_PER_LAYER nodes in each hidden // layer. staticvoid nn_predict(constfloat *features, const NN_CONFIG *nn_config, float *output) { int num_input_nodes = nn_config->num_inputs; int buf_index = 0; float buf[2][NN_MAX_NODES_PER_LAYER]; constfloat *input_nodes = features;
// Propagate hidden layers. constint num_layers = nn_config->num_hidden_layers; int layer, node, i;
assert(num_layers <= NN_MAX_HIDDEN_LAYERS); for (layer = 0; layer < num_layers; ++layer) { constfloat *weights = nn_config->weights[layer]; constfloat *bias = nn_config->bias[layer]; float *output_nodes = buf[buf_index]; constint num_output_nodes = nn_config->num_hidden_nodes[layer];
assert(num_output_nodes < NN_MAX_NODES_PER_LAYER); for (node = 0; node < num_output_nodes; ++node) { float val = 0.0f; for (i = 0; i < num_input_nodes; ++i) val += weights[i] * input_nodes[i];
val += bias[node]; // ReLU as activation function.
val = VPXMAX(val, 0.0f);
output_nodes[node] = val;
weights += num_input_nodes;
}
num_input_nodes = num_output_nodes;
input_nodes = output_nodes;
buf_index = 1 - buf_index;
}
// Final output layer.
{ constfloat *weights = nn_config->weights[num_layers]; for (node = 0; node < nn_config->num_outputs; ++node) { constfloat *bias = nn_config->bias[num_layers]; float val = 0.0f; for (i = 0; i < num_input_nodes; ++i) val += weights[i] * input_nodes[i];
output[node] = val + bias[node];
weights += num_input_nodes;
}
}
}
#if !CONFIG_REALTIME_ONLY #define FEATURES 7 // Machine-learning based partition search early termination. // Return 1 to skip split and rect partitions. staticint ml_pruning_partition(VP9_COMMON *const cm, MACROBLOCKD *const xd,
PICK_MODE_CONTEXT *ctx, int mi_row, int mi_col,
BLOCK_SIZE bsize) { constint mag_mv =
abs(ctx->mic.mv[0].as_mv.col) + abs(ctx->mic.mv[0].as_mv.row); constint left_in_image = !!xd->left_mi; constint above_in_image = !!xd->above_mi;
MODE_INFO **prev_mi =
&cm->prev_mi_grid_visible[mi_col + cm->mi_stride * mi_row]; int above_par = 0; // above_partitioning int left_par = 0; // left_partitioning int last_par = 0; // last_partitioning int offset = 0; int i;
BLOCK_SIZE context_size; const NN_CONFIG *nn_config = NULL; constfloat *mean, *sd, *linear_weights; float nn_score, linear_score; float features[FEATURES];
// Predict using linear model.
linear_weights = &vp9_partition_linear_weights[offset];
linear_score = linear_weights[FEATURES]; for (i = 0; i < FEATURES; ++i)
linear_score += linear_weights[i] * features[i]; if (linear_score > 0.1f) return 0;
// Predict using neural net model.
nn_predict(features, nn_config, &nn_score);
if (linear_score < -0.0f && nn_score < 0.1f) return 1; if (nn_score < -0.0f && linear_score < 0.1f) return 1; return 0;
} #undef FEATURES
{ // Calculate the output score. int i;
linear_score = linear_weights[FEATURES]; for (i = 0; i < FEATURES; ++i)
linear_score += linear_weights[i] * features[i];
}
return linear_score >= cpi->sf.rd_ml_partition.search_breakout_thresh[q_ctx];
} #undef FEATURES
#define FEATURES 8 #define LABELS 4 staticvoid ml_prune_rect_partition(VP9_COMP *const cpi, MACROBLOCK *const x,
BLOCK_SIZE bsize, const PC_TREE *const pc_tree, int *allow_horz, int *allow_vert,
int64_t ref_rd) { const NN_CONFIG *nn_config = NULL; float score[LABELS] = {
0.0f,
}; int thresh = -1; int i;
(void)x;
if (ref_rd <= 0 || ref_rd > 1000000000) return;
switch (bsize) { case BLOCK_8X8: break; case BLOCK_16X16:
nn_config = &vp9_rect_part_nnconfig_16;
thresh = cpi->sf.rd_ml_partition.prune_rect_thresh[1]; break; case BLOCK_32X32:
nn_config = &vp9_rect_part_nnconfig_32;
thresh = cpi->sf.rd_ml_partition.prune_rect_thresh[2]; break; case BLOCK_64X64:
nn_config = &vp9_rect_part_nnconfig_64;
thresh = cpi->sf.rd_ml_partition.prune_rect_thresh[3]; break; default: assert(0 && "Unexpected block size."); return;
} if (!nn_config || thresh < 0) return;
// Make decisions based on the model score.
{ int max_score = -1000; int horz = 0, vert = 0; int int_score[LABELS]; for (i = 0; i < LABELS; ++i) {
int_score[i] = (int)(100 * score[i]);
max_score = VPXMAX(int_score[i], max_score);
}
thresh = max_score - thresh; for (i = 0; i < LABELS; ++i) { if (int_score[i] >= thresh) { if ((i >> 0) & 1) horz = 1; if ((i >> 1) & 1) vert = 1;
}
}
*allow_horz = *allow_horz && horz;
*allow_vert = *allow_vert && vert;
}
} #undef FEATURES #undef LABELS
// Perform fast and coarse motion search for the given block. This is a // pre-processing step for the ML based partition search speedup. staticvoid simple_motion_search(const VP9_COMP *const cpi, MACROBLOCK *const x,
BLOCK_SIZE bsize, int mi_row, int mi_col,
MV ref_mv, MV_REFERENCE_FRAME ref,
uint8_t *const pred_buf) { const VP9_COMMON *const cm = &cpi->common;
MACROBLOCKD *const xd = &x->e_mbd;
MODE_INFO *const mi = xd->mi[0];
YV12_BUFFER_CONFIG *yv12;
YV12_BUFFER_CONFIG *scaled_ref_frame = vp9_get_scaled_ref_frame(cpi, ref); constint step_param = 1; const MvLimits tmp_mv_limits = x->mv_limits; const SEARCH_METHODS search_method = NSTEP; constint sadpb = x->sadperbit16;
MV ref_mv_full = { ref_mv.row >> 3, ref_mv.col >> 3 };
MV best_mv = { 0, 0 }; int cost_list[5]; struct buf_2d backup_pre[MAX_MB_PLANE] = { { 0, 0 } };
if (scaled_ref_frame) {
yv12 = scaled_ref_frame; // As reported in b/311294795, the reference buffer pointer needs to be // saved and restored after the search. Otherwise, it causes problems while // the reference frame scaling happens. for (int i = 0; i < MAX_MB_PLANE; i++) backup_pre[i] = xd->plane[i].pre[0];
} else {
yv12 = get_ref_frame_buffer(cpi, ref);
}
// Do a simple single motion search to find a prediction for current block. // The variance of the residue will be used as input features.
{
MV ref_mv; const MV_REFERENCE_FRAME ref =
cpi->rc.is_src_frame_alt_ref ? ALTREF_FRAME : LAST_FRAME; // If bsize is 64x64, use zero MV as reference; otherwise, use MV result // of previous(larger) block as reference. if (bsize == BLOCK_64X64)
ref_mv.row = ref_mv.col = 0; else
ref_mv = pc_tree->mv;
vp9_setup_src_planes(x, cpi->Source, mi_row, mi_col);
simple_motion_search(cpi, x, bsize, mi_row, mi_col, ref_mv, ref, pred_buf);
pc_tree->mv = x->e_mbd.mi[0]->mv[0].as_mv;
}
// Feed the features into the model to get the confidence score.
nn_predict(features, nn_config, &score);
// Higher score means that the model has higher confidence that the split // partition is better than the non-split partition. So if the score is // high enough, we skip the none-split partition search; if the score is // low enough, we skip the split partition search. if (score > thresh) *none = 0; if (score < -thresh) *split = 0;
}
} #undef FEATURES #endif// !CONFIG_REALTIME_ONLY
int partition_none_allowed = !force_horz_split && !force_vert_split; int partition_horz_allowed =
!force_vert_split && yss <= xss && bsize >= BLOCK_8X8; int partition_vert_allowed =
!force_horz_split && xss <= yss && bsize >= BLOCK_8X8;
int64_t dist_breakout_thr = cpi->sf.partition_search_breakout_thr.dist; int rate_breakout_thr = cpi->sf.partition_search_breakout_thr.rate; int must_split = 0; int should_encode_sb = 0;
// Ref frames picked in the [i_th] quarter subblock during square partition // RD search. It may be used to prune ref frame selection of rect partitions.
uint8_t ref_frames_used[4] = { 0, 0, 0, 0 };
// Get sub block energy range if (bsize >= BLOCK_16X16) { int min_energy, max_energy;
vp9_get_sub_block_energy(cpi, x, mi_row, mi_col, bsize, &min_energy,
&max_energy);
must_split = (min_energy < -3) && (max_energy - min_energy > 2);
}
// Determine partition types in search according to the speed features. // The threshold set here has to be of square block size. if (cpi->sf.auto_min_max_partition_size) {
partition_none_allowed &= (bsize <= max_size);
partition_horz_allowed &=
((bsize <= max_size && bsize > min_size) || force_horz_split);
partition_vert_allowed &=
((bsize <= max_size && bsize > min_size) || force_vert_split);
do_split &= bsize > min_size;
}
if (cpi->sf.rd_ml_partition.search_early_termination) { // Currently, the machine-learning based partition search early // termination is only used while bsize is 16x16, 32x32 or 64x64, // VPXMIN(cm->width, cm->height) >= 480, and speed = 0. if (!x->e_mbd.lossless &&
!segfeature_active(&cm->seg, mi->segment_id, SEG_LVL_SKIP) &&
ctx->mic.mode >= INTRA_MODES && bsize >= BLOCK_16X16) { if (ml_pruning_partition(cm, xd, ctx, mi_row, mi_col, bsize)) {
do_split = 0;
do_rect = 0;
}
}
}
// store estimated motion vector
store_pred_mv(x, ctx);
// If the interp_filter is marked as SWITCHABLE_FILTERS, it was for an // intra block and used for context purposes. if (ctx->mic.interp_filter == SWITCHABLE_FILTERS) {
pred_interp_filter = EIGHTTAP;
} else {
pred_interp_filter = ctx->mic.interp_filter;
}
// PARTITION_SPLIT // TODO(jingning): use the motion vectors given by the above search as // the starting point of motion search in the following partition type check.
pc_tree->u.split[0]->none.rdcost = 0;
pc_tree->u.split[1]->none.rdcost = 0;
pc_tree->u.split[2]->none.rdcost = 0;
pc_tree->u.split[3]->none.rdcost = 0; if (do_split || must_split) {
subsize = get_subsize(bsize, PARTITION_SPLIT);
load_pred_mv(x, ctx); if (bsize == BLOCK_8X8) {
i = 4; if (cpi->sf.adaptive_pred_interp_filter && partition_none_allowed)
pc_tree->u.leaf_split[0]->pred_interp_filter = pred_interp_filter;
rd_pick_sb_modes(cpi, tile_data, x, mi_row, mi_col, &sum_rdc, subsize,
pc_tree->u.leaf_split[0], best_rdc.rate, best_rdc.dist); if (sum_rdc.rate == INT_MAX) {
sum_rdc.rdcost = INT64_MAX;
} else { if (cpi->sf.prune_ref_frame_for_rect_partitions) { constint ref1 = pc_tree->u.leaf_split[0]->mic.ref_frame[0]; constint ref2 = pc_tree->u.leaf_split[0]->mic.ref_frame[1]; for (i = 0; i < 4; ++i) {
ref_frames_used[i] |= (1 << ref1); if (ref2 > 0) ref_frames_used[i] |= (1 << ref2);
}
}
}
} else { for (i = 0; (i < 4) && ((sum_rdc.rdcost < best_rdc.rdcost) || must_split);
++i) { constint x_idx = (i & 1) * mi_step; constint y_idx = (i >> 1) * mi_step; int found_best_rd = 0;
RD_COST best_rdc_split;
vp9_rd_cost_reset(&best_rdc_split);
if (best_rdc.rate < INT_MAX && best_rdc.dist < INT64_MAX) { // A must split test here increases the number of sub // partitions but hurts metrics results quite a bit, // so this extra test is commented out pending // further tests on whether it adds much in terms of // visual quality. // (must_split) ? best_rdc.rate // : best_rdc.rate - sum_rdc.rate, // (must_split) ? best_rdc.dist // : best_rdc.dist - sum_rdc.dist,
best_rdc_split.rate = best_rdc.rate - sum_rdc.rate;
best_rdc_split.dist = best_rdc.dist - sum_rdc.dist;
}
staticvoid encode_rd_sb_row(VP9_COMP *cpi, ThreadData *td,
TileDataEnc *tile_data, int mi_row,
TOKENEXTRA **tp) {
VP9_COMMON *const cm = &cpi->common;
TileInfo *const tile_info = &tile_data->tile_info;
MACROBLOCK *const x = &td->mb;
MACROBLOCKD *const xd = &x->e_mbd;
SPEED_FEATURES *const sf = &cpi->sf; constint mi_col_start = tile_info->mi_col_start; constint mi_col_end = tile_info->mi_col_end; int mi_col; constint sb_row = mi_row >> MI_BLOCK_SIZE_LOG2; constint num_sb_cols =
get_num_cols(tile_data->tile_info, MI_BLOCK_SIZE_LOG2); int sb_col_in_tile;
// Initialize the left context for the new SB row
memset(&xd->left_context, 0, sizeof(xd->left_context));
memset(xd->left_seg_context, 0, sizeof(xd->left_seg_context));
// Code each SB in the row for (mi_col = mi_col_start, sb_col_in_tile = 0; mi_col < mi_col_end;
mi_col += MI_BLOCK_SIZE, sb_col_in_tile++) { conststruct segmentation *const seg = &cm->seg; int dummy_rate;
int64_t dummy_dist;
RD_COST dummy_rdc; int i; int seg_skip = 0; int orig_rdmult = cpi->rd.RDMULT;
// Note: this memset assumes above_context[0], [1] and [2] // are allocated as part of the same buffer.
memset(xd->above_context[0], 0, sizeof(*xd->above_context[0]) * 2 * aligned_mi_cols * MAX_MB_PLANE);
memset(xd->above_seg_context, 0, sizeof(*xd->above_seg_context) * aligned_mi_cols);
}
if (bsize > BLOCK_8X8) {
BLOCK_SIZE subsize = get_subsize(bsize, PARTITION_SPLIT); int i; for (i = 0; i < 4; ++i)
pred_pixel_ready_reset(pc_tree->u.split[i], subsize);
}
}
#define FEATURES 6 #define LABELS 2 staticint ml_predict_var_partitioning(VP9_COMP *cpi, MACROBLOCK *x,
BLOCK_SIZE bsize, int mi_row, int mi_col) {
VP9_COMMON *const cm = &cpi->common; const NN_CONFIG *nn_config = NULL;
switch (bsize) { case BLOCK_64X64: nn_config = &vp9_var_part_nnconfig_64; break; case BLOCK_32X32: nn_config = &vp9_var_part_nnconfig_32; break; case BLOCK_16X16: nn_config = &vp9_var_part_nnconfig_16; break; case BLOCK_8X8: break; default: assert(0 && "Unexpected block size."); return -1;
}
// Get a prediction(stored in x->est_pred) for the whole 64x64 superblock. staticvoid get_estimated_pred(VP9_COMP *cpi, const TileInfo *const tile,
MACROBLOCK *x, int mi_row, int mi_col) {
VP9_COMMON *const cm = &cpi->common; constint is_key_frame = frame_is_intra_only(cm);
MACROBLOCKD *xd = &x->e_mbd;
if (!(is_one_pass_svc(cpi) && cpi->svc.spatial_layer_id) ||
cpi->svc.use_gf_temporal_ref_current_layer) { // For now, GOLDEN will not be used for non-zero spatial layers, since // it may not be a temporal reference.
yv12_g = get_ref_frame_buffer(cpi, GOLDEN_FRAME);
}
// Only compute y_sad_g (sad for golden reference) for speed < 8. if (cpi->oxcf.speed < 8 && yv12_g && yv12_g != yv12 &&
(cpi->ref_frame_flags & VP9_GOLD_FLAG)) {
vp9_setup_pre_planes(xd, 0, yv12_g, mi_row, mi_col,
&cm->frame_refs[GOLDEN_FRAME - 1].sf);
y_sad_g = cpi->fn_ptr[bsize].sdf(
x->plane[0].src.buf, x->plane[0].src.stride, xd->plane[0].pre[0].buf,
xd->plane[0].pre[0].stride);
} else {
y_sad_g = UINT_MAX;
}
staticvoid encode_nonrd_sb_row(VP9_COMP *cpi, ThreadData *td,
TileDataEnc *tile_data, int mi_row,
TOKENEXTRA **tp) {
SPEED_FEATURES *const sf = &cpi->sf;
VP9_COMMON *const cm = &cpi->common;
TileInfo *const tile_info = &tile_data->tile_info;
MACROBLOCK *const x = &td->mb;
MACROBLOCKD *const xd = &x->e_mbd; constint mi_col_start = tile_info->mi_col_start; constint mi_col_end = tile_info->mi_col_end; int mi_col; constint sb_row = mi_row >> MI_BLOCK_SIZE_LOG2; constint num_sb_cols =
get_num_cols(tile_data->tile_info, MI_BLOCK_SIZE_LOG2); int sb_col_in_tile;
// Initialize the left context for the new SB row
memset(&xd->left_context, 0, sizeof(xd->left_context));
memset(xd->left_seg_context, 0, sizeof(xd->left_seg_context));
// Code each SB in the row for (mi_col = mi_col_start, sb_col_in_tile = 0; mi_col < mi_col_end;
mi_col += MI_BLOCK_SIZE, ++sb_col_in_tile) { conststruct segmentation *const seg = &cm->seg;
RD_COST dummy_rdc; constint idx_str = cm->mi_stride * mi_row + mi_col;
MODE_INFO **mi = cm->mi_grid_visible + idx_str;
PARTITION_SEARCH_TYPE partition_search_type = sf->partition_search_type;
BLOCK_SIZE bsize = BLOCK_64X64; int seg_skip = 0; int i;
staticvoid compute_boundary_ls(constdouble *ctr_ls, int k, double *boundary_ls) { // boundary_ls[j] is the upper bound of data centered at ctr_ls[j] int j; for (j = 0; j < k - 1; ++j) {
boundary_ls[j] = (ctr_ls[j] + ctr_ls[j + 1]) / 2.;
}
boundary_ls[k - 1] = DBL_MAX;
}
int vp9_get_group_idx(double value, double *boundary_ls, int k) { int group_idx = 0; while (value >= boundary_ls[group_idx]) {
++group_idx; if (group_idx == k - 1) { break;
}
} return group_idx;
}
void vp9_kmeans(double *ctr_ls, double *boundary_ls, int *count_ls, int k,
KMEANS_DATA *arr, int size) { int i, j; int itr; int group_idx; double sum[MAX_KMEANS_GROUPS]; int count[MAX_KMEANS_GROUPS];
// initialize the center points for (j = 0; j < k; ++j) {
ctr_ls[j] = arr[(size * (2 * j + 1)) / (2 * k)].value;
}
for (itr = 0; itr < 10; ++itr) {
compute_boundary_ls(ctr_ls, k, boundary_ls); for (i = 0; i < MAX_KMEANS_GROUPS; ++i) {
sum[i] = 0;
count[i] = 0;
}
// Both the data and centers are sorted in ascending order. // As each data point is processed in order, its corresponding group index // can only increase. So we only need to reset the group index to zero here.
group_idx = 0; for (i = 0; i < size; ++i) { while (arr[i].value >= boundary_ls[group_idx]) { // place samples into clusters
++group_idx; if (group_idx == k - 1) { break;
}
}
sum[group_idx] += arr[i].value;
++count[group_idx];
}
for (group_idx = 0; group_idx < k; ++group_idx) { if (count[group_idx] > 0)
ctr_ls[group_idx] = sum[group_idx] / count[group_idx];
sum[group_idx] = 0;
count[group_idx] = 0;
}
}
// compute group_idx, boundary_ls and count_ls for (j = 0; j < k; ++j) {
count_ls[j] = 0;
}
compute_boundary_ls(ctr_ls, k, boundary_ls);
group_idx = 0; for (i = 0; i < size; ++i) { while (arr[i].value >= boundary_ls[group_idx]) {
++group_idx; if (group_idx == k - 1) { break;
}
}
arr[i].group_idx = group_idx;
++count_ls[group_idx];
}
}
if (!cpi->row_mt) {
cpi->row_mt_sync_read_ptr = vp9_row_mt_sync_read_dummy;
cpi->row_mt_sync_write_ptr = vp9_row_mt_sync_write_dummy; // If allowed, encoding tiles in parallel with one thread handling one // tile when row based multi-threading is disabled. if (VPXMIN(cpi->oxcf.max_threads, 1 << cm->log2_tile_cols) > 1)
vp9_encode_tiles_mt(cpi); else
encode_tiles(cpi);
} else {
cpi->row_mt_sync_read_ptr = vp9_row_mt_sync_read;
cpi->row_mt_sync_write_ptr = vp9_row_mt_sync_write;
vp9_encode_tiles_row_mt(cpi);
}
staticvoid restore_encode_params(VP9_COMP *cpi) {
VP9_COMMON *const cm = &cpi->common; int tile_idx; int i, j;
TileDataEnc *tile_data;
RD_OPT *rd_opt = &cpi->rd; for (i = 0; i < MAX_REF_FRAMES; i++) { for (j = 0; j < REFERENCE_MODES; j++)
rd_opt->prediction_type_threshes[i][j] =
rd_opt->prediction_type_threshes_prev[i][j];
// In the longer term the encoder should be generalized to match the // decoder such that we allow compound where one of the 3 buffers has a // different sign bias and that buffer is then the fixed ref. However, this // requires further work in the rd loop. For now the only supported encoder // side behavior is where the ALT ref buffer has opposite sign bias to // the other two. if (!frame_is_intra_only(cm)) { if (vp9_compound_reference_allowed(cm)) {
cpi->allow_comp_inter_inter = 1;
vp9_setup_compound_reference_mode(cm);
} else {
cpi->allow_comp_inter_inter = 0;
}
}
if (cpi->sf.frame_parameter_update) { int i;
RD_OPT *const rd_opt = &cpi->rd;
FRAME_COUNTS *counts = cpi->td.counts;
RD_COUNTS *const rdc = &cpi->td.rd_counts;
// This code does a single RD pass over the whole frame assuming // either compound, single or hybrid prediction as per whatever has // worked best for that type of frame in the past. // It also predicts whether another coding mode would have worked // better than this coding mode. If that is the case, it remembers // that for subsequent frames. // It also does the same analysis for transform size selection. const MV_REFERENCE_FRAME frame_type = get_frame_type(cpi);
int64_t *const mode_thrs = rd_opt->prediction_type_threshes[frame_type];
int64_t *const filter_thrs = rd_opt->filter_threshes[frame_type]; constint is_alt_ref = frame_type == ALTREF_FRAME;
if (cm->reference_mode == REFERENCE_MODE_SELECT) { int single_count_zero = 0; int comp_count_zero = 0; int i; for (i = 0; i < COMP_INTER_CONTEXTS; i++) {
single_count_zero += counts->comp_inter[i][0];
comp_count_zero += counts->comp_inter[i][1];
} if (comp_count_zero == 0) {
cm->reference_mode = SINGLE_REFERENCE;
vp9_zero(counts->comp_inter);
} elseif (single_count_zero == 0) {
cm->reference_mode = COMPOUND_REFERENCE;
vp9_zero(counts->comp_inter);
}
}
}
// If segmented AQ is enabled compute the average AQ weighting. if (cm->seg.enabled && (cpi->oxcf.aq_mode != NO_AQ) &&
(cm->seg.update_map || cm->seg.update_data)) {
cm->seg.aq_av_offset = compute_frame_aq_offset(cpi);
}
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.163Bemerkung:
(vorverarbeitet am 2026-05-01)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.