ref: d48be6ad734890e0093d25df61b2943d1f402c0d
parent: 15193ce51f8a18ec0c58a94980657015d50c28fd
parent: 0f8ebddec40c65a8aa0c60b35ab44822745d7027
author: Jerome Jiang <jianj@google.com>
date: Thu Aug 10 20:54:35 EDT 2017
Merge "vp9 SVC: Fix the denoiser frame buffer management."
--- a/vp9/encoder/vp9_denoiser.c
+++ b/vp9/encoder/vp9_denoiser.c
@@ -190,11 +190,12 @@
VP9_COMMON *const cm, VP9_DENOISER *denoiser, MACROBLOCK *mb, BLOCK_SIZE bs,
int increase_denoising, int mi_row, int mi_col, PICK_MODE_CONTEXT *ctx,
int motion_magnitude, int is_skin, int *zeromv_filter, int consec_zeromv,
- int num_spatial_layers, int width) {
+ int num_spatial_layers, int width, int lst_fb_idx, int gld_fb_idx,
+ int use_svc) {
const int sse_diff = (ctx->newmv_sse == UINT_MAX)
? 0
: ((int)ctx->zeromv_sse - (int)ctx->newmv_sse);
- MV_REFERENCE_FRAME frame;
+ int frame;
MACROBLOCKD *filter_mbd = &mb->e_mbd;
MODE_INFO *mi = filter_mbd->mi[0];
MODE_INFO saved_mi;
@@ -202,8 +203,10 @@
struct buf_2d saved_dst[MAX_MB_PLANE];
struct buf_2d saved_pre[MAX_MB_PLANE];
RefBuffer *saved_block_refs[2];
+ MV_REFERENCE_FRAME saved_frame;
frame = ctx->best_reference_frame;
+
saved_mi = *mi;
if (is_skin && (motion_magnitude > 0 || consec_zeromv < 4)) return COPY_BLOCK;
@@ -246,6 +249,15 @@
}
}
+ saved_frame = frame;
+ // When using SVC, we need to map REF_FRAME to the frame buffer index.
+ if (use_svc) {
+ if (frame == LAST_FRAME)
+ frame = lst_fb_idx + 1;
+ else if (frame == GOLDEN_FRAME)
+ frame = gld_fb_idx + 1;
+ }
+
if (ctx->newmv_sse > sse_thresh(bs, increase_denoising)) {
// Restore everything to its original state
*mi = saved_mi;
@@ -292,7 +304,7 @@
denoiser->mc_running_avg_y.uv_stride, mi_row, mi_col);
filter_mbd->plane[2].dst.stride = denoiser->mc_running_avg_y.uv_stride;
- set_ref_ptrs(cm, filter_mbd, frame, NONE);
+ set_ref_ptrs(cm, filter_mbd, saved_frame, NONE);
vp9_build_inter_predictors_sby(filter_mbd, mi_row, mi_col, bs);
// Restore everything to its original state
@@ -370,7 +382,8 @@
decision = perform_motion_compensation(
&cpi->common, denoiser, mb, bs, increase_denoising, mi_row, mi_col, ctx,
motion_magnitude, is_skin, &zeromv_filter, consec_zeromv,
- cpi->svc.number_spatial_layers, cpi->Source->y_width);
+ cpi->svc.number_spatial_layers, cpi->Source->y_width, cpi->lst_fb_idx,
+ cpi->gld_fb_idx, cpi->use_svc);
if (decision == FILTER_BLOCK) {
decision = vp9_denoiser_filter(src.buf, src.stride, mc_avg_start,
@@ -417,23 +430,11 @@
src->y_buffer = tmp_buf;
}
-void vp9_denoise_init_svc(VP9_COMP *cpi) {
- // For fixed pattern SVC, on base temporal layer. Note we only denoise
- // higher spatial layer for SVC.
- if (cpi->svc.temporal_layering_mode != VP9E_TEMPORAL_LAYERING_MODE_BYPASS &&
- cpi->svc.spatial_layer_id == cpi->svc.number_spatial_layers - 1 &&
- cpi->svc.temporal_layer_id == 0) {
- VP9_DENOISER *denoiser = &cpi->denoiser;
- copy_frame(&denoiser->running_avg_y[LAST_FRAME],
- &denoiser->running_avg_y[GOLDEN_FRAME]);
- }
-}
-
void vp9_denoiser_update_frame_info(
VP9_DENOISER *denoiser, YV12_BUFFER_CONFIG src, FRAME_TYPE frame_type,
int refresh_alt_ref_frame, int refresh_golden_frame, int refresh_last_frame,
- int resized, int svc_base_is_key, int svc_fixed_pattern,
- int temporal_layer_id) {
+ int alt_fb_idx, int gld_fb_idx, int lst_fb_idx, int resized,
+ int svc_base_is_key) {
// Copy source into denoised reference buffers on KEY_FRAME or
// if the just encoded frame was resized. For SVC, copy source if the base
// spatial layer was key frame.
@@ -441,48 +442,42 @@
svc_base_is_key) {
int i;
// Start at 1 so as not to overwrite the INTRA_FRAME
- for (i = 1; i < denoiser->num_ref_frames; ++i)
- copy_frame(&denoiser->running_avg_y[i], &src);
+ for (i = 1; i < denoiser->num_ref_frames; ++i) {
+ if (denoiser->running_avg_y[i].buffer_alloc != NULL)
+ copy_frame(&denoiser->running_avg_y[i], &src);
+ }
denoiser->reset = 0;
return;
}
// If more than one refresh occurs, must copy frame buffer.
- if (refresh_golden_frame + refresh_last_frame + refresh_alt_ref_frame > 1) {
+ if ((refresh_alt_ref_frame + refresh_golden_frame + refresh_last_frame) > 1) {
+ if (refresh_alt_ref_frame) {
+ copy_frame(&denoiser->running_avg_y[alt_fb_idx + 1],
+ &denoiser->running_avg_y[INTRA_FRAME]);
+ }
if (refresh_golden_frame) {
- copy_frame(&denoiser->running_avg_y[GOLDEN_FRAME],
+ copy_frame(&denoiser->running_avg_y[gld_fb_idx + 1],
&denoiser->running_avg_y[INTRA_FRAME]);
}
- // For fixed pattern SVC: update denoised last_frame if alt_ref is
- // refreshed, only for non-zero temporal layer.
- if (refresh_last_frame ||
- (refresh_alt_ref_frame && svc_fixed_pattern && temporal_layer_id > 0)) {
- copy_frame(&denoiser->running_avg_y[LAST_FRAME],
+ if (refresh_last_frame) {
+ copy_frame(&denoiser->running_avg_y[lst_fb_idx + 1],
&denoiser->running_avg_y[INTRA_FRAME]);
}
} else {
+ if (refresh_alt_ref_frame) {
+ swap_frame_buffer(&denoiser->running_avg_y[alt_fb_idx + 1],
+ &denoiser->running_avg_y[INTRA_FRAME]);
+ }
if (refresh_golden_frame) {
- swap_frame_buffer(&denoiser->running_avg_y[GOLDEN_FRAME],
+ swap_frame_buffer(&denoiser->running_avg_y[gld_fb_idx + 1],
&denoiser->running_avg_y[INTRA_FRAME]);
}
- // For fixed pattern SVC: update denoised last_frame if alt_ref is
- // refreshed, only for non-zero temporal layer.
- if (refresh_last_frame ||
- (refresh_alt_ref_frame && svc_fixed_pattern && temporal_layer_id > 0)) {
- swap_frame_buffer(&denoiser->running_avg_y[LAST_FRAME],
+ if (refresh_last_frame) {
+ swap_frame_buffer(&denoiser->running_avg_y[lst_fb_idx + 1],
&denoiser->running_avg_y[INTRA_FRAME]);
}
}
- // For fixed pattern SVC we need to keep track of denoised last_frame for base
- // temporal layer (since alt_ref refresh may update denoised last_frame on
- // the upper/middle temporal layers).We do this by copying the current
- // denoised last into the denoised golden_frame, for temporal_layer_id = 0.
- // For the fixed pattern SVC golden is always spatial reference and is never
- // used for denoising, so we can use it to keep track of denoised last_frame.
- if (svc_fixed_pattern && temporal_layer_id == 0) {
- copy_frame(&denoiser->running_avg_y[GOLDEN_FRAME],
- &denoiser->running_avg_y[LAST_FRAME]);
- }
}
void vp9_denoiser_reset_frame_stats(PICK_MODE_CONTEXT *ctx) {
@@ -509,6 +504,46 @@
}
}
+static int vp9_denoiser_realloc_svc_helper(VP9_COMMON *cm,
+ VP9_DENOISER *denoiser, int fb_idx) {
+ int fail = 0;
+ if (denoiser->running_avg_y[fb_idx].buffer_alloc == NULL) {
+ fail =
+ vpx_alloc_frame_buffer(&denoiser->running_avg_y[fb_idx], cm->width,
+ cm->height, cm->subsampling_x, cm->subsampling_y,
+#if CONFIG_VP9_HIGHBITDEPTH
+ cm->use_highbitdepth,
+#endif
+ VP9_ENC_BORDER_IN_PIXELS, 0);
+ if (fail) {
+ vp9_denoiser_free(denoiser);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int vp9_denoiser_realloc_svc(VP9_COMMON *cm, VP9_DENOISER *denoiser,
+ int refresh_alt, int refresh_gld, int refresh_lst,
+ int alt_fb_idx, int gld_fb_idx, int lst_fb_idx) {
+ int fail = 0;
+ if (refresh_alt) {
+ // Increase the frame buffer index by 1 to map it to the buffer index in the
+ // denoiser.
+ fail = vp9_denoiser_realloc_svc_helper(cm, denoiser, alt_fb_idx + 1);
+ if (fail) return 1;
+ }
+ if (refresh_gld) {
+ fail = vp9_denoiser_realloc_svc_helper(cm, denoiser, gld_fb_idx + 1);
+ if (fail) return 1;
+ }
+ if (refresh_lst) {
+ fail = vp9_denoiser_realloc_svc_helper(cm, denoiser, lst_fb_idx + 1);
+ if (fail) return 1;
+ }
+ return 0;
+}
+
int vp9_denoiser_alloc(VP9_COMMON *cm, int use_svc, VP9_DENOISER *denoiser,
int width, int height, int ssx, int ssy,
#if CONFIG_VP9_HIGHBITDEPTH
@@ -515,15 +550,16 @@
int use_highbitdepth,
#endif
int border) {
- int i, fail;
+ int i, fail, init_num_ref_frames;
const int legacy_byte_alignment = 0;
assert(denoiser != NULL);
- denoiser->num_ref_frames = use_svc ? MAX_REF_FRAMES : NONSVC_REF_FRAMES;
+ denoiser->num_ref_frames = use_svc ? SVC_REF_FRAMES : NONSVC_REF_FRAMES;
+ init_num_ref_frames = use_svc ? MAX_REF_FRAMES : NONSVC_REF_FRAMES;
CHECK_MEM_ERROR(
cm, denoiser->running_avg_y,
vpx_calloc(denoiser->num_ref_frames, sizeof(denoiser->running_avg_y[0])));
- for (i = 0; i < denoiser->num_ref_frames; ++i) {
+ for (i = 0; i < init_num_ref_frames; ++i) {
fail = vpx_alloc_frame_buffer(&denoiser->running_avg_y[i], width, height,
ssx, ssy,
#if CONFIG_VP9_HIGHBITDEPTH
@@ -594,7 +630,8 @@
denoiser->prev_denoising_level = denoiser->denoising_level;
}
-// Scale/increase the partition threshold for denoiser speed-up.
+// Scale/increase the partition threshold
+// for denoiser speed-up.
int64_t vp9_scale_part_thresh(int64_t threshold, VP9_DENOISER_LEVEL noise_level,
int content_state, int temporal_layer_id) {
if ((content_state == kLowSadLowSumdiff) ||
@@ -609,7 +646,8 @@
}
}
-// Scale/increase the ac skip threshold for denoiser speed-up.
+// Scale/increase the ac skip threshold for
+// denoiser speed-up.
int64_t vp9_scale_acskip_thresh(int64_t threshold,
VP9_DENOISER_LEVEL noise_level, int abs_sumdiff,
int temporal_layer_id) {
--- a/vp9/encoder/vp9_denoiser.h
+++ b/vp9/encoder/vp9_denoiser.h
@@ -25,6 +25,10 @@
// need to allocate for it, and hence we need MAX_REF_FRAME - 1
#define NONSVC_REF_FRAMES MAX_REF_FRAMES - 1
+// Number of frame buffers when SVC is used. [0] for current denoised buffer and
+// [1..8] for REF_FRAMES
+#define SVC_REF_FRAMES 9
+
typedef enum vp9_denoiser_decision {
COPY_BLOCK,
FILTER_BLOCK,
@@ -63,13 +67,11 @@
struct VP9_COMP;
-void vp9_denoise_init_svc(struct VP9_COMP *cpi);
-
void vp9_denoiser_update_frame_info(
VP9_DENOISER *denoiser, YV12_BUFFER_CONFIG src, FRAME_TYPE frame_type,
int refresh_alt_ref_frame, int refresh_golden_frame, int refresh_last_frame,
- int resized, int svc_base_is_key, int svc_fixed_pattern,
- int temporal_layer_id);
+ int alt_fb_idx, int gld_fb_idx, int lst_fb_idx, int resized,
+ int svc_base_is_key);
void vp9_denoiser_denoise(struct VP9_COMP *cpi, MACROBLOCK *mb, int mi_row,
int mi_col, BLOCK_SIZE bs, PICK_MODE_CONTEXT *ctx,
@@ -80,6 +82,10 @@
void vp9_denoiser_update_frame_stats(MODE_INFO *mi, unsigned int sse,
PREDICTION_MODE mode,
PICK_MODE_CONTEXT *ctx);
+
+int vp9_denoiser_realloc_svc(VP9_COMMON *cm, VP9_DENOISER *denoiser,
+ int refresh_alt, int refresh_gld, int refresh_lst,
+ int alt_fb_idx, int gld_fb_idx, int lst_fb_idx);
int vp9_denoiser_alloc(VP9_COMMON *cm, int use_svc, VP9_DENOISER *denoiser,
int width, int height, int ssx, int ssy,
--- a/vp9/encoder/vp9_encoder.c
+++ b/vp9/encoder/vp9_encoder.c
@@ -2833,21 +2833,29 @@
if (cpi->oxcf.noise_sensitivity > 0 && denoise_svc(cpi) &&
cpi->denoiser.denoising_level > kDenLowLow) {
int svc_base_is_key = 0;
- int svc_fixed_pattern = 0;
if (cpi->use_svc) {
+ int realloc_fail = 0;
int layer = LAYER_IDS_TO_IDX(cpi->svc.spatial_layer_id,
cpi->svc.temporal_layer_id,
cpi->svc.number_temporal_layers);
LAYER_CONTEXT *lc = &cpi->svc.layer_context[layer];
svc_base_is_key = lc->is_key_frame;
- svc_fixed_pattern = (cpi->svc.temporal_layering_mode !=
- VP9E_TEMPORAL_LAYERING_MODE_BYPASS);
+
+ // Check if we need to allocate extra buffers in the denoiser for
+ // refreshed frames.
+ realloc_fail = vp9_denoiser_realloc_svc(
+ cm, &cpi->denoiser, cpi->refresh_alt_ref_frame,
+ cpi->refresh_golden_frame, cpi->refresh_last_frame, cpi->alt_fb_idx,
+ cpi->gld_fb_idx, cpi->lst_fb_idx);
+ if (realloc_fail)
+ vpx_internal_error(&cm->error, VPX_CODEC_MEM_ERROR,
+ "Failed to re-allocate denoiser for SVC");
}
vp9_denoiser_update_frame_info(
&cpi->denoiser, *cpi->Source, cpi->common.frame_type,
cpi->refresh_alt_ref_frame, cpi->refresh_golden_frame,
- cpi->refresh_last_frame, cpi->resize_pending, svc_base_is_key,
- svc_fixed_pattern, cpi->svc.temporal_layer_id);
+ cpi->refresh_last_frame, cpi->alt_fb_idx, cpi->gld_fb_idx,
+ cpi->lst_fb_idx, cpi->resize_pending, svc_base_is_key);
}
#endif
if (is_one_pass_cbr_svc(cpi)) {
@@ -3480,12 +3488,6 @@
}
vp9_update_noise_estimate(cpi);
-
-#if CONFIG_VP9_TEMPORAL_DENOISING
- if (cpi->oxcf.noise_sensitivity > 0 && cpi->use_svc &&
- cpi->denoiser.denoising_level > kDenLowLow)
- vp9_denoise_init_svc(cpi);
-#endif
// Scene detection is always used for VBR mode or screen-content case.
// For other cases (e.g., CBR mode) use it for 5 <= speed < 8 for now