shithub: libvpx

Download patch

ref: 292d221fed21a84b3a9902bcaecda00cc08e6029
parent: c0180325793e7f1dcd50bf8a30dfe2ce41e6ae75
author: Yury Gitman <[email protected]>
date: Mon Jul 18 11:44:40 EDT 2016

Create interface for the ALT_REF_AQ class

Current commit is just an API template  for the rest of the code, and
I will add inner logic later.

Altref  frames  generate a  lot  of  bitrate  and  at the  same  time
other  frames  refer to  them  a  lot, so  it  makes  sense to  apply
special  compensation-based adaptive  quantization scheme  for altref
frames. E.g.,  for blocks  that are  good predictors  for the  future
apply rate-control  chosen quantizer  while for bad  predictors apply
worse one.

Change-Id: Iba3f8ec349470673b7249f6a125f6859336a47c8

--- /dev/null
+++ b/vp9/encoder/vp9_alt_ref_aq.c
@@ -1,0 +1,61 @@
+/*
+ *  Copyright (c) 2016 The WebM project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed  by a BSD-style license that can be
+ *  found in the LICENSE file in the root of the source tree. An additional
+ *  intellectual property  rights grant can  be found in the  file PATENTS.
+ *  All contributing  project authors may be  found in the AUTHORS  file in
+ *  the root of the source tree.
+ */
+
+#include "vp9/encoder/vp9_encoder.h"
+
+#include "vp9/encoder/vp9_alt_ref_aq_private.h"
+#include "vp9/encoder/vp9_alt_ref_aq.h"
+
+struct ALT_REF_AQ *vp9_alt_ref_aq_create() {
+  return (struct ALT_REF_AQ *)vpx_malloc(sizeof(struct ALT_REF_AQ));
+}
+
+void vp9_alt_ref_aq_destroy(struct ALT_REF_AQ *const self) { vpx_free(self); }
+
+void vp9_alt_ref_aq_upload_map(struct ALT_REF_AQ *const self,
+                               const struct MATX_8U *segmentation_map) {
+  (void)self;
+  (void)segmentation_map;
+}
+
+void vp9_alt_ref_aq_set_nsegments(struct ALT_REF_AQ *const self,
+                                  int nsegments) {
+  (void)self;
+  (void)nsegments;
+}
+
+void vp9_alt_ref_aq_setup_mode(struct ALT_REF_AQ *const self,
+                               struct VP9_COMP *const cpi) {
+  (void)cpi;
+  (void)self;
+}
+
+// set basic segmentation to the altref's one
+void vp9_alt_ref_aq_setup_map(struct ALT_REF_AQ *const self,
+                              struct VP9_COMP *const cpi) {
+  (void)cpi;
+  (void)self;
+}
+
+// restore cpi->aq_mode
+void vp9_alt_ref_aq_unset_all(struct ALT_REF_AQ *const self,
+                              struct VP9_COMP *const cpi) {
+  (void)cpi;
+  (void)self;
+}
+
+int vp9_alt_ref_aq_disable_if(const struct ALT_REF_AQ *self,
+                              int segmentation_overhead, int bandwidth) {
+  (void)bandwidth;
+  (void)self;
+  (void)segmentation_overhead;
+
+  return 0;
+}
--- /dev/null
+++ b/vp9/encoder/vp9_alt_ref_aq.h
@@ -1,0 +1,124 @@
+/*
+ *  Copyright (c) 2016 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.
+ */
+
+/*
+ *  \file vp9_alt_ref_aq.h
+ *
+ *  This file  contains public interface  for setting up  adaptive segmentation
+ *  for altref frames.  Go to alt_ref_aq_private.h for implmentation details.
+ */
+
+#ifndef VP9_ENCODER_VP9_ALT_REF_AQ_H_
+#define VP9_ENCODER_VP9_ALT_REF_AQ_H_
+
+#include "vpx/vpx_integer.h"
+
+// Where to disable segmentation
+#define ALT_REF_AQ_LOW_BITRATE_BOUNDARY 150
+
+// Last frame always has overall quality = 0,
+// so it is questionable if I can process it
+#define ALT_REF_AQ_APPLY_TO_LAST_FRAME 1
+
+// If I should try to compare gain
+// against segmentation overhead
+#define ALT_REF_AQ_PROTECT_GAIN 0
+
+// Threshold to disable segmentation
+#define ALT_REF_AQ_PROTECT_GAIN_THRESH 0.5
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Simple structure for storing images
+struct MATX_8U {
+  int rows;
+  int cols;
+  int stride;
+
+  uint8_t *data;
+};
+
+struct VP9_COMP;
+struct ALT_REF_AQ;
+
+/*!\brief Constructor
+ *
+ * \return Instance of the class
+ */
+struct ALT_REF_AQ *vp9_alt_ref_aq_create();
+
+/*!\brief Upload segmentation_map to self object
+ *
+ * \param    self             Instance of the class
+ * \param    segmentation_map Segmentation map to upload
+ */
+void vp9_alt_ref_aq_upload_map(struct ALT_REF_AQ *const self,
+                               const struct MATX_8U *segmentation_map);
+
+/*!\brief Return pointer to the altref segmentation map
+ *
+ * \param    self                    Instance of the class
+ * \param    segmentation_overhead   Segmentation overhead in bytes
+ * \param    bandwidth               Current frame bandwidth in bytes
+ *
+ * \return  Boolean value to disable segmentation
+ */
+int vp9_alt_ref_aq_disable_if(const struct ALT_REF_AQ *self,
+                              int segmentation_overhead, int bandwidth);
+
+/*!\brief Set number of segments
+ *
+ * It is used for delta quantizer computations
+ * and thus it can be larger than
+ * maximum value of the segmentation map
+ *
+ * \param    self        Instance of the class
+ * \param    nsegments   Maximum number of segments
+ */
+void vp9_alt_ref_aq_set_nsegments(struct ALT_REF_AQ *self, int nsegments);
+
+/*!\brief Set up LOOKAHEAD_AQ segmentation mode
+ *
+ * Set up segmentation mode to LOOKAHEAD_AQ
+ * (expected future frames prediction
+ *  quality refering to the current frame).
+ *
+ * \param    self    Instance of the class
+ * \param    cpi     Encoder context
+ */
+void vp9_alt_ref_aq_setup_mode(struct ALT_REF_AQ *self, struct VP9_COMP *cpi);
+
+/*!\brief Set up LOOKAHEAD_AQ segmentation map and delta quantizers
+ *
+ * \param    self    Instance of the class
+ * \param    cpi     Encoder context
+ */
+void vp9_alt_ref_aq_setup_map(struct ALT_REF_AQ *self, struct VP9_COMP *cpi);
+
+/*!\brief Restore main segmentation map mode and reset the class variables
+ *
+ * \param    self    Instance of the class
+ * \param    cpi     Encoder context
+ */
+void vp9_alt_ref_aq_unset_all(struct ALT_REF_AQ *self, struct VP9_COMP *cpi);
+
+/*!\brief Destructor
+ *
+ * \param    self    Instance of the class
+ */
+void vp9_alt_ref_aq_destroy(struct ALT_REF_AQ *self);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // VP9_ENCODER_VP9_ALT_REF_AQ_H_
--- /dev/null
+++ b/vp9/encoder/vp9_alt_ref_aq_private.h
@@ -1,0 +1,35 @@
+/*
+ *  Copyright (c) 2016 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.
+ */
+
+/*
+ *  \file vp9_alt_ref_aq_private.h
+ *
+ *  This file  describes class  used for  setting up  adaptive segmentation
+ *  for  altref frames.   It  is  private file  and  most  likely you  need
+ *  alt_ref_aq.h instead.
+ */
+
+#ifndef VP9_ENCODER_VP9_ALT_REF_AQ_PRIVATE_H_
+#define VP9_ENCODER_VP9_ALT_REF_AQ_PRIVATE_H_
+
+#include "vp9/encoder/vp9_alt_ref_aq.h"
+
+#ifdef __cplusplus
+
+extern "C" {
+#endif
+
+struct ALT_REF_AQ {};
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // VP9_ENCODER_VP9_ALT_REF_AQ_PRIVATE_H_
--- a/vp9/encoder/vp9_encodeframe.c
+++ b/vp9/encoder/vp9_encodeframe.c
@@ -216,7 +216,7 @@
 
   // Setup segment ID.
   if (seg->enabled) {
-    if (cpi->oxcf.aq_mode != VARIANCE_AQ &&
+    if (cpi->oxcf.aq_mode != VARIANCE_AQ && cpi->oxcf.aq_mode != LOOKAHEAD_AQ &&
         cpi->oxcf.aq_mode != EQUATOR360_AQ) {
       const uint8_t *const map =
           seg->update_map ? cpi->segmentation_map : cm->last_frame_seg_map;
@@ -1354,6 +1354,11 @@
       mi->segment_id = get_segment_id(cm, map, bsize, mi_row, mi_col);
     }
     x->rdmult = set_segment_rdmult(cpi, x, mi->segment_id);
+  } else if (aq_mode == LOOKAHEAD_AQ) {
+    const uint8_t *const map = cpi->segmentation_map;
+
+    // I do not change rdmult here consciously.
+    mi->segment_id = get_segment_id(cm, map, bsize, mi_row, mi_col);
   } else if (aq_mode == EQUATOR360_AQ) {
     if (cm->frame_type == KEY_FRAME || cpi->force_update_segmentation) {
       mi->segment_id = vp9_360aq_segment_id(mi_row, cm->mi_rows);
@@ -2501,7 +2506,8 @@
 
   set_offsets(cpi, tile_info, x, mi_row, mi_col, bsize);
 
-  if (bsize == BLOCK_16X16 && cpi->oxcf.aq_mode != NO_AQ)
+  if (bsize == BLOCK_16X16 && cpi->oxcf.aq_mode != NO_AQ &&
+      cpi->oxcf.aq_mode != LOOKAHEAD_AQ)
     x->mb_energy = vp9_block_energy(cpi, x, bsize);
 
   if (cpi->sf.cb_partition_search && bsize == BLOCK_16X16) {
--- a/vp9/encoder/vp9_encoder.c
+++ b/vp9/encoder/vp9_encoder.c
@@ -36,6 +36,7 @@
 #include "vp9/common/vp9_reconintra.h"
 #include "vp9/common/vp9_tile_common.h"
 
+#include "vp9/encoder/vp9_alt_ref_aq.h"
 #include "vp9/encoder/vp9_aq_360.h"
 #include "vp9/encoder/vp9_aq_complexity.h"
 #include "vp9/encoder/vp9_aq_cyclicrefresh.h"
@@ -1618,6 +1619,8 @@
   cpi->use_skin_detection = 0;
   cpi->common.buffer_pool = pool;
 
+  cpi->force_update_segmentation = 0;
+
   init_config(cpi, oxcf);
   vp9_rc_init(&cpi->oxcf, oxcf->pass, &cpi->rc);
 
@@ -1627,6 +1630,8 @@
 
   realloc_segmentation_maps(cpi);
 
+  CHECK_MEM_ERROR(cm, cpi->alt_ref_aq, vp9_alt_ref_aq_create());
+
   CHECK_MEM_ERROR(
       cm, cpi->consec_zero_mv,
       vpx_calloc(cm->mi_rows * cm->mi_cols, sizeof(*cpi->consec_zero_mv)));
@@ -2024,6 +2029,8 @@
 
   if (cpi->num_workers > 1) vp9_loop_filter_dealloc(&cpi->lf_row_sync);
 
+  vp9_alt_ref_aq_destroy(cpi->alt_ref_aq);
+
   dealloc_compressor_data(cpi);
 
   for (i = 0; i < sizeof(cpi->mbgraph_stats) / sizeof(cpi->mbgraph_stats[0]);
@@ -3138,6 +3145,7 @@
   setup_frame(cpi);
 
   suppress_active_map(cpi);
+
   // Variance adaptive and in frame q adjustment experiments are mutually
   // exclusive.
   if (cpi->oxcf.aq_mode == VARIANCE_AQ) {
@@ -3148,7 +3156,12 @@
     vp9_setup_in_frame_q_adj(cpi);
   } else if (cpi->oxcf.aq_mode == CYCLIC_REFRESH_AQ) {
     vp9_cyclic_refresh_setup(cpi);
+  } else if (cpi->oxcf.aq_mode == LOOKAHEAD_AQ) {
+    // it may be pretty bad for rate-control,
+    // and I should handle it somehow
+    vp9_alt_ref_aq_setup_map(cpi->alt_ref_aq, cpi);
   }
+
   apply_active_map(cpi);
 
   vp9_encode_frame(cpi);
@@ -3286,6 +3299,8 @@
       vp9_360aq_frame_setup(cpi);
     } else if (cpi->oxcf.aq_mode == COMPLEXITY_AQ) {
       vp9_setup_in_frame_q_adj(cpi);
+    } else if (cpi->oxcf.aq_mode == LOOKAHEAD_AQ) {
+      vp9_alt_ref_aq_setup_map(cpi->alt_ref_aq, cpi);
     }
 
     vp9_encode_frame(cpi);
@@ -3748,6 +3763,30 @@
 }
 #endif  // ENABLE_KF_DENOISE
 
+static void vp9_try_disable_lookahead_aq(VP9_COMP *cpi, size_t *size,
+                                         uint8_t *dest) {
+  if (cpi->common.seg.enabled)
+    if (ALT_REF_AQ_PROTECT_GAIN) {
+      size_t nsize = *size;
+      int overhead;
+
+      // TODO(yuryg): optimize this, as
+      // we don't really need to repack
+
+      save_coding_context(cpi);
+      vp9_disable_segmentation(&cpi->common.seg);
+      vp9_pack_bitstream(cpi, dest, &nsize);
+      restore_coding_context(cpi);
+
+      overhead = (int)*size - (int)nsize;
+
+      if (vp9_alt_ref_aq_disable_if(cpi->alt_ref_aq, overhead, (int)*size))
+        vp9_encode_frame(cpi);
+      else
+        vp9_enable_segmentation(&cpi->common.seg);
+    }
+}
+
 static void encode_frame_to_data_rate(VP9_COMP *cpi, size_t *size,
                                       uint8_t *dest,
                                       unsigned int *frame_flags) {
@@ -3873,6 +3912,10 @@
     encode_with_recode_loop(cpi, size, dest);
   }
 
+  // Disable segmentation if it decrease rate/distortion ratio
+  if (cpi->oxcf.aq_mode == LOOKAHEAD_AQ)
+    vp9_try_disable_lookahead_aq(cpi, size, dest);
+
 #if CONFIG_VP9_TEMPORAL_DENOISING
 #ifdef OUTPUT_YUV_DENOISED
   if (oxcf->noise_sensitivity > 0) {
@@ -3996,6 +4039,11 @@
                                cpi->svc.number_temporal_layers +
                            cpi->svc.temporal_layer_id]
         .last_frame_type = cm->frame_type;
+
+  cpi->force_update_segmentation = 0;
+
+  if (cpi->oxcf.aq_mode == LOOKAHEAD_AQ)
+    vp9_alt_ref_aq_unset_all(cpi->alt_ref_aq, cpi);
 }
 
 static void SvcEncode(VP9_COMP *cpi, size_t *size, uint8_t *dest,
@@ -4424,9 +4472,21 @@
 #endif
 
       if ((oxcf->arnr_max_frames > 0) && (oxcf->arnr_strength > 0)) {
+        int bitrate = cpi->rc.avg_frame_bandwidth / 40;
+        int not_low_bitrate = bitrate > ALT_REF_AQ_LOW_BITRATE_BOUNDARY;
+
+        int not_last_frame = (cpi->lookahead->sz - arf_src_index > 1);
+        not_last_frame |= ALT_REF_AQ_APPLY_TO_LAST_FRAME;
+
         // Produce the filtered ARF frame.
         vp9_temporal_filter(cpi, arf_src_index);
         vpx_extend_frame_borders(&cpi->alt_ref_buffer);
+
+        // for small bitrates segmentation overhead usually
+        // eats all bitrate gain from enabling delta quantizers
+        if (cpi->oxcf.alt_ref_aq != 0 && not_low_bitrate && not_last_frame)
+          vp9_alt_ref_aq_setup_mode(cpi->alt_ref_aq, cpi);
+
         force_src_buffer = &cpi->alt_ref_buffer;
       }
 
@@ -4788,6 +4848,7 @@
         cpi->svc.spatial_layer_to_encode = 0;
     }
   }
+
   vpx_clear_system_state();
   return 0;
 }
--- a/vp9/encoder/vp9_encoder.h
+++ b/vp9/encoder/vp9_encoder.h
@@ -29,6 +29,7 @@
 #include "vp9/common/vp9_thread_common.h"
 #include "vp9/common/vp9_onyxc_int.h"
 
+#include "vp9/encoder/vp9_alt_ref_aq.h"
 #include "vp9/encoder/vp9_aq_cyclicrefresh.h"
 #include "vp9/encoder/vp9_context_tree.h"
 #include "vp9/encoder/vp9_encodemb.h"
@@ -487,6 +488,10 @@
   uint8_t force_update_segmentation;
 
   YV12_BUFFER_CONFIG alt_ref_buffer;
+
+  // class responsible for adaptive
+  // quantization of altref frames
+  struct ALT_REF_AQ *alt_ref_aq;
 
 #if CONFIG_INTERNAL_STATS
   unsigned int mode_chosen_counts[MAX_MODES];
--- a/vp9/vp9cx.mk
+++ b/vp9/vp9cx.mk
@@ -81,6 +81,9 @@
 VP9_CX_SRCS-yes += encoder/vp9_aq_cyclicrefresh.h
 VP9_CX_SRCS-yes += encoder/vp9_aq_complexity.c
 VP9_CX_SRCS-yes += encoder/vp9_aq_complexity.h
+VP9_CX_SRCS-yes += encoder/vp9_alt_ref_aq_private.h
+VP9_CX_SRCS-yes += encoder/vp9_alt_ref_aq.h
+VP9_CX_SRCS-yes += encoder/vp9_alt_ref_aq.c
 VP9_CX_SRCS-yes += encoder/vp9_skin_detection.c
 VP9_CX_SRCS-yes += encoder/vp9_skin_detection.h
 VP9_CX_SRCS-yes += encoder/vp9_noise_estimate.c