#include "vp9/encoder/vp9_firstpass.h"
#include "vp9/encoder/vp9_mcomp.h"
#include "vp9/encoder/vp9_quantize.h"
-#include "vp9/encoder/vp9_rdopt.h"
+#include "vp9/encoder/vp9_rd.h"
#include "vp9/encoder/vp9_variance.h"
#define OUTPUT_FPF 0
// Read frame stats at an offset from the current position.
-static int read_frame_stats(const TWO_PASS *p,
- FIRSTPASS_STATS *frame_stats, int offset) {
- const FIRSTPASS_STATS *fps_ptr = p->stats_in;
-
- // Check legality of offset.
- if (offset >= 0) {
- if (&fps_ptr[offset] >= p->stats_in_end)
- return EOF;
- } else if (offset < 0) {
- if (&fps_ptr[offset] < p->stats_in_start)
- return EOF;
+static const FIRSTPASS_STATS *read_frame_stats(const TWO_PASS *p, int offset) {
+ if ((offset >= 0 && p->stats_in + offset >= p->stats_in_end) ||
+ (offset < 0 && p->stats_in + offset < p->stats_in_start)) {
+ return NULL;
}
- *frame_stats = fps_ptr[offset];
- return 1;
+ return &p->stats_in[offset];
}
static int input_stats(TWO_PASS *p, FIRSTPASS_STATS *fps) {
#endif
}
+#if CONFIG_FP_MB_STATS
+static void output_fpmb_stats(uint8_t *this_frame_mb_stats, VP9_COMMON *cm,
+ struct vpx_codec_pkt_list *pktlist) {
+ struct vpx_codec_cx_pkt pkt;
+ pkt.kind = VPX_CODEC_FPMB_STATS_PKT;
+ pkt.data.firstpass_mb_stats.buf = this_frame_mb_stats;
+ pkt.data.firstpass_mb_stats.sz = cm->MBs * sizeof(uint8_t);
+ vpx_codec_pkt_list_add(pktlist, &pkt);
+}
+#endif
+
static void zero_stats(FIRSTPASS_STATS *section) {
section->frame = 0.0;
section->intra_error = 0.0;
const MV zero_mv = {0, 0};
const YV12_BUFFER_CONFIG *first_ref_buf = lst_yv12;
+#if CONFIG_FP_MB_STATS
+ if (cpi->use_fp_mb_stats) {
+ vp9_zero_array(cpi->twopass.frame_mb_stats_buf, cm->MBs);
+ }
+#endif
+
vp9_clear_system_state();
set_first_pass_params(cpi);
const YV12_BUFFER_CONFIG *scaled_ref_buf = NULL;
twopass = &cpi->svc.layer_context[cpi->svc.spatial_layer_id].twopass;
+ if (cpi->common.current_video_frame == 0) {
+ cpi->ref_frame_flags = 0;
+ } else {
+ LAYER_CONTEXT *lc = &cpi->svc.layer_context[cpi->svc.spatial_layer_id];
+ if (lc->current_video_frame_in_layer == 0)
+ cpi->ref_frame_flags = VP9_GOLD_FLAG;
+ else
+ cpi->ref_frame_flags = VP9_LAST_FLAG | VP9_GOLD_FLAG;
+ }
+
vp9_scale_references(cpi);
// Use either last frame or alt frame for motion search.
if (cpi->ref_frame_flags & VP9_LAST_FLAG) {
scaled_ref_buf = vp9_get_scaled_ref_frame(cpi, LAST_FRAME);
ref_frame = LAST_FRAME;
- } else if (cpi->ref_frame_flags & VP9_ALT_FLAG) {
- scaled_ref_buf = vp9_get_scaled_ref_frame(cpi, ALTREF_FRAME);
- ref_frame = ALTREF_FRAME;
+ } else if (cpi->ref_frame_flags & VP9_GOLD_FLAG) {
+ scaled_ref_buf = vp9_get_scaled_ref_frame(cpi, GOLDEN_FRAME);
+ ref_frame = GOLDEN_FRAME;
}
- if (scaled_ref_buf != NULL) {
- // Update the stride since we are using scaled reference buffer
+ if (scaled_ref_buf != NULL)
first_ref_buf = scaled_ref_buf;
- recon_y_stride = first_ref_buf->y_stride;
- recon_uv_stride = first_ref_buf->uv_stride;
- uv_mb_height = 16 >> (first_ref_buf->y_height > first_ref_buf->uv_height);
- }
+
+ recon_y_stride = new_yv12->y_stride;
+ recon_uv_stride = new_yv12->uv_stride;
+ uv_mb_height = 16 >> (new_yv12->y_height > new_yv12->uv_height);
// Disable golden frame for svc first pass for now.
gld_yv12 = NULL;
const int use_dc_pred = (mb_col || mb_row) && (!mb_col || !mb_row);
double error_weight = 1.0;
const BLOCK_SIZE bsize = get_bsize(cm, mb_row, mb_col);
+#if CONFIG_FP_MB_STATS
+ const int mb_index = mb_row * cm->mb_cols + mb_col;
+#endif
vp9_clear_system_state();
// Accumulate the intra error.
intra_error += (int64_t)this_error;
+#if CONFIG_FP_MB_STATS
+ if (cpi->use_fp_mb_stats) {
+ // initialization
+ cpi->twopass.frame_mb_stats_buf[mb_index] = 0;
+ }
+#endif
+
// Set up limit values for motion vectors to prevent them extending
// outside the UMV borders.
x->mv_col_min = -((mb_col * 16) + BORDER_MV_PIXELS_B16);
&unscaled_last_source_buf_2d);
// TODO(pengchong): Replace the hard-coded threshold
- if (raw_motion_error > 25) {
+ if (raw_motion_error > 25 ||
+ (cpi->use_svc && cpi->svc.number_temporal_layers == 1)) {
// Test last reference frame using the previous best mv as the
// starting point (best reference) for the search.
first_pass_motion_search(cpi, x, &best_ref_mv.as_mv, &mv.as_mv,
// Start by assuming that intra mode is best.
best_ref_mv.as_int = 0;
+#if CONFIG_FP_MB_STATS
+ if (cpi->use_fp_mb_stats) {
+ // intra predication statistics
+ cpi->twopass.frame_mb_stats_buf[mb_index] = 0;
+ cpi->twopass.frame_mb_stats_buf[mb_index] |= FPMB_DCINTRA_MASK;
+ cpi->twopass.frame_mb_stats_buf[mb_index] &=
+ ~FPMB_NONZERO_MOTION_MASK;
+ if (this_error > FPMB_ERROR_LEVEL4_TH) {
+ cpi->twopass.frame_mb_stats_buf[mb_index] |= FPMB_ERROR_LEVEL4_MASK;
+ } else if (this_error > FPMB_ERROR_LEVEL3_TH) {
+ cpi->twopass.frame_mb_stats_buf[mb_index] |= FPMB_ERROR_LEVEL3_MASK;
+ } else if (this_error > FPMB_ERROR_LEVEL2_TH) {
+ cpi->twopass.frame_mb_stats_buf[mb_index] |= FPMB_ERROR_LEVEL2_MASK;
+ } else if (this_error > FPMB_ERROR_LEVEL1_TH) {
+ cpi->twopass.frame_mb_stats_buf[mb_index] |= FPMB_ERROR_LEVEL1_MASK;
+ } else {
+ cpi->twopass.frame_mb_stats_buf[mb_index] |= FPMB_ERROR_LEVEL0_MASK;
+ }
+ }
+#endif
+
if (motion_error <= this_error) {
// Keep a count of cases where the inter and intra were very close
// and very low. This helps with scene cut detection for example in
best_ref_mv.as_int = mv.as_int;
+#if CONFIG_FP_MB_STATS
+ if (cpi->use_fp_mb_stats) {
+ // inter predication statistics
+ cpi->twopass.frame_mb_stats_buf[mb_index] = 0;
+ cpi->twopass.frame_mb_stats_buf[mb_index] &= ~FPMB_DCINTRA_MASK;
+ cpi->twopass.frame_mb_stats_buf[mb_index] &=
+ ~FPMB_NONZERO_MOTION_MASK;
+ if (this_error > FPMB_ERROR_LEVEL4_TH) {
+ cpi->twopass.frame_mb_stats_buf[mb_index] |=
+ FPMB_ERROR_LEVEL4_MASK;
+ } else if (this_error > FPMB_ERROR_LEVEL3_TH) {
+ cpi->twopass.frame_mb_stats_buf[mb_index] |=
+ FPMB_ERROR_LEVEL3_MASK;
+ } else if (this_error > FPMB_ERROR_LEVEL2_TH) {
+ cpi->twopass.frame_mb_stats_buf[mb_index] |=
+ FPMB_ERROR_LEVEL2_MASK;
+ } else if (this_error > FPMB_ERROR_LEVEL1_TH) {
+ cpi->twopass.frame_mb_stats_buf[mb_index] |=
+ FPMB_ERROR_LEVEL1_MASK;
+ } else {
+ cpi->twopass.frame_mb_stats_buf[mb_index] |=
+ FPMB_ERROR_LEVEL0_MASK;
+ }
+ }
+#endif
+
if (mv.as_int) {
++mvcount;
+#if CONFIG_FP_MB_STATS
+ if (cpi->use_fp_mb_stats) {
+ cpi->twopass.frame_mb_stats_buf[mb_index] |=
+ FPMB_NONZERO_MOTION_MASK;
+ }
+#endif
+
// Non-zero vector, was it different from the last non zero vector?
if (mv.as_int != lastmv_as_int)
++new_mv_count;
twopass->this_frame_stats = fps;
output_stats(&twopass->this_frame_stats, cpi->output_pkt_list);
accumulate_stats(&twopass->total_stats, &fps);
+
+#if CONFIG_FP_MB_STATS
+ if (cpi->use_fp_mb_stats) {
+ output_fpmb_stats(twopass->frame_mb_stats_buf, cm, cpi->output_pkt_list);
+ }
+#endif
}
// Copy the previous Last Frame back into gf and and arf buffers if
}
++cm->current_video_frame;
+ if (cpi->use_svc)
+ vp9_inc_frame_in_layer(&cpi->svc);
}
static double calc_correction_factor(double err_per_mb,
// score in the frame following a flash frame. The offset passed in should
// reflect this.
static int detect_flash(const TWO_PASS *twopass, int offset) {
- FIRSTPASS_STATS next_frame;
-
- int flash_detected = 0;
-
- // Read the frame data.
- // The return is FALSE (no flash detected) if not a valid frame
- if (read_frame_stats(twopass, &next_frame, offset) != EOF) {
- // What we are looking for here is a situation where there is a
- // brief break in prediction (such as a flash) but subsequent frames
- // are reasonably well predicted by an earlier (pre flash) frame.
- // The recovery after a flash is indicated by a high pcnt_second_ref
- // compared to pcnt_inter.
- if (next_frame.pcnt_second_ref > next_frame.pcnt_inter &&
- next_frame.pcnt_second_ref >= 0.5)
- flash_detected = 1;
- }
-
- return flash_detected;
+ const FIRSTPASS_STATS *const next_frame = read_frame_stats(twopass, offset);
+
+ // What we are looking for here is a situation where there is a
+ // brief break in prediction (such as a flash) but subsequent frames
+ // are reasonably well predicted by an earlier (pre flash) frame.
+ // The recovery after a flash is indicated by a high pcnt_second_ref
+ // compared to pcnt_inter.
+ return next_frame != NULL &&
+ next_frame->pcnt_second_ref > next_frame->pcnt_inter &&
+ next_frame->pcnt_second_ref >= 0.5;
}
// Update the motion related elements to the GF arf boost calculation.
static int calc_arf_boost(VP9_COMP *cpi, int offset,
int f_frames, int b_frames,
int *f_boost, int *b_boost) {
- FIRSTPASS_STATS this_frame;
TWO_PASS *const twopass = &cpi->twopass;
int i;
double boost_score = 0.0;
// Search forward from the proposed arf/next gf position.
for (i = 0; i < f_frames; ++i) {
- if (read_frame_stats(twopass, &this_frame, (i + offset)) == EOF)
+ const FIRSTPASS_STATS *this_frame = read_frame_stats(twopass, i + offset);
+ if (this_frame == NULL)
break;
// Update the motion related elements to the boost calculation.
- accumulate_frame_motion_stats(&this_frame,
+ accumulate_frame_motion_stats(this_frame,
&this_frame_mv_in_out, &mv_in_out_accumulator,
&abs_mv_in_out_accumulator,
&mv_ratio_accumulator);
// Accumulate the effect of prediction quality decay.
if (!flash_detected) {
- decay_accumulator *= get_prediction_decay_rate(&cpi->common, &this_frame);
+ decay_accumulator *= get_prediction_decay_rate(&cpi->common, this_frame);
decay_accumulator = decay_accumulator < MIN_DECAY_FACTOR
? MIN_DECAY_FACTOR : decay_accumulator;
}
- boost_score += decay_accumulator * calc_frame_boost(twopass, &this_frame,
+ boost_score += decay_accumulator * calc_frame_boost(twopass, this_frame,
this_frame_mv_in_out);
}
// Search backward towards last gf position.
for (i = -1; i >= -b_frames; --i) {
- if (read_frame_stats(twopass, &this_frame, (i + offset)) == EOF)
+ const FIRSTPASS_STATS *this_frame = read_frame_stats(twopass, i + offset);
+ if (this_frame == NULL)
break;
// Update the motion related elements to the boost calculation.
- accumulate_frame_motion_stats(&this_frame,
+ accumulate_frame_motion_stats(this_frame,
&this_frame_mv_in_out, &mv_in_out_accumulator,
&abs_mv_in_out_accumulator,
&mv_ratio_accumulator);
// Cumulative effect of prediction quality decay.
if (!flash_detected) {
- decay_accumulator *= get_prediction_decay_rate(&cpi->common, &this_frame);
+ decay_accumulator *= get_prediction_decay_rate(&cpi->common, this_frame);
decay_accumulator = decay_accumulator < MIN_DECAY_FACTOR
? MIN_DECAY_FACTOR : decay_accumulator;
}
- boost_score += decay_accumulator * calc_frame_boost(twopass, &this_frame,
+ boost_score += decay_accumulator * calc_frame_boost(twopass, this_frame,
this_frame_mv_in_out);
}
*b_boost = (int)boost_score;
double modified_err = 0.0;
double err_fraction;
int mid_boost_bits = 0;
- int middle_frame_idx;
+ int mid_frame_idx;
unsigned char arf_buffer_indices[MAX_ACTIVE_ARFS];
key_frame = cpi->common.frame_type == KEY_FRAME ||
// Store the bits to spend on the ARF if there is one.
if (rc->source_alt_ref_pending) {
- if (cpi->multi_arf_enabled) {
- // A portion of the gf / arf extra bits are set asside for lower level
- // boosted frames in the middle of the group.
- mid_boost_bits += gf_arf_bits >> 5;
- gf_arf_bits -= (gf_arf_bits >> 5);
- }
-
twopass->gf_group.update_type[frame_index] = ARF_UPDATE;
twopass->gf_group.rf_level[frame_index] = GF_ARF_STD;
twopass->gf_group.bit_allocation[frame_index] = gf_arf_bits;
twopass->gf_group.arf_src_offset[frame_index] =
(unsigned char)(rc->baseline_gf_interval - 1);
twopass->gf_group.arf_update_idx[frame_index] = arf_buffer_indices[0];
- twopass->gf_group.arf_ref_idx[frame_index] = arf_buffer_indices[0];
+ twopass->gf_group.arf_ref_idx[frame_index] =
+ arf_buffer_indices[cpi->multi_arf_last_grp_enabled &&
+ rc->source_alt_ref_active];
++frame_index;
if (cpi->multi_arf_enabled) {
}
// Define middle frame
- middle_frame_idx = frame_index + (rc->baseline_gf_interval >> 1) - 1;
+ mid_frame_idx = frame_index + (rc->baseline_gf_interval >> 1) - 1;
// Allocate bits to the other frames in the group.
for (i = 0; i < rc->baseline_gf_interval - 1; ++i) {
mid_boost_bits += (target_frame_size >> 4);
target_frame_size -= (target_frame_size >> 4);
- if (frame_index <= middle_frame_idx)
+ if (frame_index <= mid_frame_idx)
arf_idx = 1;
}
twopass->gf_group.arf_update_idx[frame_index] = arf_buffer_indices[arf_idx];
// Final setup for second arf and its overlay.
if (cpi->multi_arf_enabled) {
twopass->gf_group.bit_allocation[2] =
- twopass->gf_group.bit_allocation[middle_frame_idx] + mid_boost_bits;
- twopass->gf_group.update_type[middle_frame_idx] = OVERLAY_UPDATE;
- twopass->gf_group.bit_allocation[middle_frame_idx] = 0;
+ twopass->gf_group.bit_allocation[mid_frame_idx] + mid_boost_bits;
+ twopass->gf_group.update_type[mid_frame_idx] = OVERLAY_UPDATE;
+ twopass->gf_group.bit_allocation[mid_frame_idx] = 0;
}
} else {
twopass->gf_group.update_type[frame_index] = GF_UPDATE;
twopass->gf_group.rf_level[frame_index] = GF_ARF_STD;
}
+
+ // Note whether multi-arf was enabled this group for next time.
+ cpi->multi_arf_last_grp_enabled = cpi->multi_arf_enabled;
}
// Analyse and define a gf/arf group.
double mv_in_out_accumulator = 0.0;
double abs_mv_in_out_accumulator = 0.0;
double mv_ratio_accumulator_thresh;
- unsigned int allow_alt_ref = is_altref_enabled(oxcf);
+ unsigned int allow_alt_ref = is_altref_enabled(cpi);
int f_boost = 0;
int b_boost = 0;
// Motion breakout threshold for loop below depends on image size.
mv_ratio_accumulator_thresh = (cpi->common.width + cpi->common.height) / 10.0;
- // Work out a maximum interval for the GF.
- // If the image appears completely static we can extend beyond this.
- // The value chosen depends on the active Q range. At low Q we have
- // bits to spare and are better with a smaller interval and smaller boost.
- // At high Q when there are few bits to spare we are better with a longer
- // interval to spread the cost of the GF.
- //
- active_max_gf_interval =
- 12 + ((int)vp9_convert_qindex_to_q(rc->last_q[INTER_FRAME]) >> 5);
-
- if (active_max_gf_interval > rc->max_gf_interval)
+ // Work out a maximum interval for the GF group.
+ // If the image appears almost completely static we can extend beyond this.
+ if (cpi->multi_arf_allowed) {
active_max_gf_interval = rc->max_gf_interval;
+ } else {
+ // The value chosen depends on the active Q range. At low Q we have
+ // bits to spare and are better with a smaller interval and smaller boost.
+ // At high Q when there are few bits to spare we are better with a longer
+ // interval to spread the cost of the GF.
+ active_max_gf_interval =
+ 12 + ((int)vp9_convert_qindex_to_q(rc->last_q[INTER_FRAME]) >> 5);
+
+ if (active_max_gf_interval > rc->max_gf_interval)
+ active_max_gf_interval = rc->max_gf_interval;
+ }
i = 0;
while (i < rc->static_scene_max_gf_interval && i < rc->frames_to_key) {
&b_boost);
rc->source_alt_ref_pending = 1;
+ // Test to see if multi arf is appropriate.
+ cpi->multi_arf_enabled =
+ (cpi->multi_arf_allowed && (rc->baseline_gf_interval >= 6) &&
+ (zero_motion_accumulator < 0.995)) ? 1 : 0;
} else {
rc->gfu_boost = (int)boost_score;
rc->source_alt_ref_pending = 0;
// Is this a forced key frame by interval.
rc->this_key_frame_forced = rc->next_key_frame_forced;
- // Clear the alt ref active flag as this can never be active on a key frame.
+ // Clear the alt ref active flag and last group multi arf flags as they
+ // can never be set for a key frame.
rc->source_alt_ref_active = 0;
+ cpi->multi_arf_last_grp_enabled = 0;
// KF is always a GF so clear frames till next gf counter.
rc->frames_till_gf_update_due = 0;
break;
default:
assert(0);
+ break;
+ }
+ if (cpi->use_svc && cpi->svc.number_temporal_layers == 1) {
+ cpi->refresh_golden_frame = 0;
+ if (cpi->alt_ref_source == NULL)
+ cpi->refresh_alt_ref_frame = 0;
}
}
#endif
vp9_rc_set_frame_target(cpi, target_rate);
cm->frame_type = INTER_FRAME;
+
+ if (is_spatial_svc) {
+ if (cpi->svc.spatial_layer_id == 0) {
+ lc->is_key_frame = 0;
+ } else {
+ lc->is_key_frame = cpi->svc.layer_context[0].is_key_frame;
+
+ if (lc->is_key_frame)
+ cpi->ref_frame_flags &= (~VP9_LAST_FLAG);
+ }
+ }
+
return;
}
}
rc->frames_till_gf_update_due = rc->baseline_gf_interval;
- cpi->refresh_golden_frame = 1;
- }
-
- {
- FIRSTPASS_STATS next_frame;
- if (lookup_next_frame_stats(twopass, &next_frame) != EOF) {
- twopass->next_iiratio = (int)(next_frame.intra_error /
- DOUBLE_DIVIDE_CHECK(next_frame.coded_error));
- }
+ if (!is_spatial_svc)
+ cpi->refresh_golden_frame = 1;
}
configure_buffer_updates(cpi);