shithub: libvpx

Download patch

ref: 557368a8fa9f839afd7a6ded6a95f18829ff3365
parent: 61edec1efbea1c02d71857e2aff9426d9cd2df4e
author: Cheng Chen <chengchen@google.com>
date: Mon Feb 1 11:57:09 EST 2021

L2E: let external rate control pass in a max frame size

And allow the frame to recode when the frame size is larger
than the input max frame size.

If the max frame size is not specified, let vp9 decide whether
to recode.  The recode follows the vp9's current recoding mechanism.

The rate control api will return the new qindex back to the
external model.

Change-Id: I796fbf713ad50a5b413b0e2501583b565ed2343f

--- a/test/vp9_ext_ratectrl_test.cc
+++ b/test/vp9_ext_ratectrl_test.cc
@@ -128,6 +128,7 @@
   } else {
     frame_decision->q_index = 100;
   }
+  frame_decision->max_frame_size = 0;
   return VPX_RC_OK;
 }
 
@@ -142,6 +143,11 @@
   EXPECT_EQ(encode_frame_result->pixel_count, ref_pixel_count);
   if (toy_rate_ctrl->coding_index == kLosslessCodingIndex) {
     EXPECT_EQ(encode_frame_result->sse, 0);
+  }
+  if (toy_rate_ctrl->coding_index == kLosslessCodingIndex) {
+    EXPECT_EQ(encode_frame_result->actual_encoding_qindex, 0);
+  } else {
+    EXPECT_EQ(encode_frame_result->actual_encoding_qindex, 100);
   }
   return VPX_RC_OK;
 }
--- a/vp9/encoder/vp9_encoder.c
+++ b/vp9/encoder/vp9_encoder.c
@@ -4402,6 +4402,17 @@
   int qrange_adj = 1;
 #endif
 
+  // A flag which indicates whether we are recoding the current frame
+  // when the current frame size is larger than the max frame size in the
+  // external rate control model.
+  // This flag doesn't have any impact when external rate control is not used.
+  int ext_rc_recode = 0;
+  // Maximal frame size allowed by the external rate control.
+  // case: 0, we ignore the max frame size limit, and encode with the qindex
+  // passed in by the external rate control model.
+  // case: -1, we take VP9's decision for the max frame size.
+  int ext_rc_max_frame_size = 0;
+
 #if CONFIG_RATE_CTRL
   const FRAME_UPDATE_TYPE update_type =
       cpi->twopass.gf_group.update_type[cpi->twopass.gf_group.index];
@@ -4507,7 +4518,7 @@
       q = cpi->encode_command.external_quantize_index;
     }
 #endif
-    if (cpi->ext_ratectrl.ready) {
+    if (cpi->ext_ratectrl.ready && !ext_rc_recode) {
       vpx_codec_err_t codec_status;
       const GF_GROUP *gf_group = &cpi->twopass.gf_group;
       vpx_rc_encodeframe_decision_t encode_frame_decision;
@@ -4526,6 +4537,7 @@
                            "vp9_extrc_get_encodeframe_decision() failed");
       }
       q = encode_frame_decision.q_index;
+      ext_rc_max_frame_size = encode_frame_decision.max_frame_size;
     }
 
     vp9_set_quantizer(cpi, q);
@@ -4567,7 +4579,24 @@
     }
 
     if (cpi->ext_ratectrl.ready) {
-      break;
+      // In general, for the external rate control, we take the qindex provided
+      // as input and encode the frame with this qindex faithfully. However,
+      // in some extreme scenarios, the provided qindex leads to a massive
+      // overshoot of frame size. In this case, we fall back to VP9's decision
+      // to pick a new qindex and recode the frame. We return the new qindex
+      // through the API to the external model.
+      if (ext_rc_max_frame_size == 0) {
+        break;
+      } else if (ext_rc_max_frame_size == -1) {
+        if (rc->projected_frame_size < rc->max_frame_bandwidth) {
+          break;
+        }
+      } else {
+        if (rc->projected_frame_size < ext_rc_max_frame_size) {
+          break;
+        }
+      }
+      ext_rc_recode = 1;
     }
 #if CONFIG_RATE_CTRL
     // This part needs to be after save_coding_context() because
@@ -5501,7 +5530,7 @@
         get_ref_cnt_buffer(cm, cm->new_fb_idx);
     vpx_codec_err_t codec_status = vp9_extrc_update_encodeframe_result(
         &cpi->ext_ratectrl, (*size) << 3, cpi->Source, &coded_frame_buf->buf,
-        cm->bit_depth, cpi->oxcf.input_bit_depth);
+        cm->bit_depth, cpi->oxcf.input_bit_depth, cm->base_qindex);
     if (codec_status != VPX_CODEC_OK) {
       vpx_internal_error(&cm->error, codec_status,
                          "vp9_extrc_update_encodeframe_result() failed");
--- a/vp9/encoder/vp9_ext_ratectrl.c
+++ b/vp9/encoder/vp9_ext_ratectrl.c
@@ -168,7 +168,7 @@
     EXT_RATECTRL *ext_ratectrl, int64_t bit_count,
     const YV12_BUFFER_CONFIG *source_frame,
     const YV12_BUFFER_CONFIG *coded_frame, uint32_t bit_depth,
-    uint32_t input_bit_depth) {
+    uint32_t input_bit_depth, const int actual_encoding_qindex) {
   if (ext_ratectrl == NULL) {
     return VPX_CODEC_INVALID_PARAM;
   }
@@ -180,6 +180,7 @@
     encode_frame_result.pixel_count =
         source_frame->y_crop_width * source_frame->y_crop_height +
         2 * source_frame->uv_crop_width * source_frame->uv_crop_height;
+    encode_frame_result.actual_encoding_qindex = actual_encoding_qindex;
 #if CONFIG_VP9_HIGHBITDEPTH
     vpx_calc_highbd_psnr(source_frame, coded_frame, &psnr, bit_depth,
                          input_bit_depth);
--- a/vp9/encoder/vp9_ext_ratectrl.h
+++ b/vp9/encoder/vp9_ext_ratectrl.h
@@ -43,6 +43,6 @@
     EXT_RATECTRL *ext_ratectrl, int64_t bit_count,
     const YV12_BUFFER_CONFIG *source_frame,
     const YV12_BUFFER_CONFIG *coded_frame, uint32_t bit_depth,
-    uint32_t input_bit_depth);
+    uint32_t input_bit_depth, int actual_encoding_qindex);
 
 #endif  // VPX_VP9_ENCODER_VP9_EXT_RATECTRL_H_
--- a/vpx/vpx_ext_ratectrl.h
+++ b/vpx/vpx_ext_ratectrl.h
@@ -38,9 +38,15 @@
  *
  * The encoder will receive the decision from the external rate control model
  * through get_encodeframe_decision() defined in vpx_rc_funcs_t.
+ *
+ * If max_frame_size = 0, the encoding ignores max frame size limit.
+ * If max_frame_size = -1, the encoding uses VP9's max frame size as the limit.
+ * If the encoded frame size is larger than max_frame_size, the frame is
+ * recoded to meet the size limit, following VP9's recoding principles.
  */
 typedef struct vpx_rc_encodeframe_decision {
-  int q_index; /**< Quantizer step index [0..255]*/
+  int q_index;        /**< Quantizer step index [0..255]*/
+  int max_frame_size; /**< Maximal frame size allowed to encode a frame*/
 } vpx_rc_encodeframe_decision_t;
 
 /*!\brief Information for the frame to be encoded.
@@ -82,6 +88,7 @@
   int64_t sse;         /**< sum of squared error of the reconstructed frame */
   int64_t bit_count;   /**< number of bits spent on coding the frame*/
   int64_t pixel_count; /**< number of pixels in YUV planes of the frame*/
+  int actual_encoding_qindex; /**< the actual qindex used to encode the frame*/
 } vpx_rc_encodeframe_result_t;
 
 /*!\brief Status returned by rate control callback functions.