aom: rtc: Convert the max_consec_drop to time units

From 9d756a1117005c064862d53134bfc6d78663c5b4 Mon Sep 17 00:00:00 2001
From: Marco Paniconi <[EMAIL REDACTED]>
Date: Tue, 6 Aug 2024 11:09:49 -0700
Subject: [PATCH] rtc: Convert the max_consec_drop to time units

This is a more natural unit to use for rtc applications.
At the start of av1_get_one_pass_rt_params() the time units
is converted to frame number (via framerate), which is the
unit used internally in the frame dropper.

Change-Id: I27f5573e6bcb3831f9159798919eab3cc9ab6991
---
 aom/aomcx.h                 | 16 +++++++++++++---
 av1/av1_cx_iface.c          | 16 ++++++++++++++++
 av1/encoder/encoder.h       |  7 +++++++
 av1/encoder/ratectrl.c      |  5 +++++
 av1/ratectrl_rtc.cc         | 14 +++++++++++---
 av1/ratectrl_rtc.h          |  2 +-
 examples/svc_encoder_rtc.cc |  2 +-
 test/ratectrl_rtc_test.cc   | 11 ++++++-----
 8 files changed, 60 insertions(+), 13 deletions(-)

diff --git a/aom/aomcx.h b/aom/aomcx.h
index 3f3eb643aa..3cf3991213 100644
--- a/aom/aomcx.h
+++ b/aom/aomcx.h
@@ -1527,9 +1527,10 @@ enum aome_enc_control_id {
    */
   AV1E_SET_BITRATE_ONE_PASS_CBR = 163,
 
-  /*!\brief Codec control to set the maximum number of consecutive frame drops
-   * allowed for the frame dropper in 1 pass CBR mode, int parameter. Value of
-   * zero has no effect.
+  /*!\brief Codec control to set the maximum number of consecutive frame drops,
+   * in units of frames, allowed for the frame dropper in 1 pass
+   * CBR mode, int parameter. Value of zero has no effect.
+   * Deprecated: use the new control AV1E_SET_MAX_CONSEC_FRAME_DROP_MS_CBR.
    */
   AV1E_SET_MAX_CONSEC_FRAME_DROP_CBR = 164,
 
@@ -1563,6 +1564,12 @@ enum aome_enc_control_id {
    */
   AV1E_SET_POSTENCODE_DROP_RTC = 168,
 
+  /*!\brief Codec control to set the maximum number of consecutive frame drops,
+   * in units of time (milliseconds), allowed for the frame dropper in 1 pass
+   * CBR mode, int parameter. Value of zero has no effect.
+   */
+  AV1E_SET_MAX_CONSEC_FRAME_DROP_MS_CBR = 169,
+
   // Any new encoder control IDs should be added above.
   // Maximum allowed encoder control ID is 229.
   // No encoder control ID should be added below.
@@ -2229,6 +2236,9 @@ AOM_CTRL_USE_TYPE(AV1E_GET_HIGH_MOTION_CONTENT_SCREEN_RTC, int *)
 AOM_CTRL_USE_TYPE(AV1E_SET_POSTENCODE_DROP_RTC, int)
 #define AOM_CTRL_AV1E_SET_POSTENCODE_DROP_RTC
 
+AOM_CTRL_USE_TYPE(AV1E_SET_MAX_CONSEC_FRAME_DROP_MS_CBR, int)
+#define AOM_CTRL_AV1E_SET_MAX_CONSEC_FRAME_DROP_MS_CBR
+
 /*!\endcond */
 /*! @} - end defgroup aom_encoder */
 #ifdef __cplusplus
diff --git a/av1/av1_cx_iface.c b/av1/av1_cx_iface.c
index 92cd7f40da..ce6bbe9618 100644
--- a/av1/av1_cx_iface.c
+++ b/av1/av1_cx_iface.c
@@ -2680,6 +2680,20 @@ static aom_codec_err_t ctrl_set_max_consec_frame_drop_cbr(
   return AOM_CODEC_OK;
 }
 
+static aom_codec_err_t ctrl_set_max_consec_frame_drop_ms_cbr(
+    aom_codec_alg_priv_t *ctx, va_list args) {
+  AV1_PRIMARY *const ppi = ctx->ppi;
+  AV1_COMP *const cpi = ppi->cpi;
+  const int max_consec_drop_ms =
+      CAST(AV1E_SET_MAX_CONSEC_FRAME_DROP_MS_CBR, args);
+  if (max_consec_drop_ms < 0) return AOM_CODEC_INVALID_PARAM;
+  // max_consec_drop_ms will be converted to frame units inside encoder
+  // based on framerate (which can change dynamically).
+  ctx->oxcf.rc_cfg.max_consec_drop_ms = max_consec_drop_ms;
+  cpi->rc.drop_count_consec = 0;
+  return AOM_CODEC_OK;
+}
+
 static aom_codec_err_t ctrl_set_svc_frame_drop_mode(aom_codec_alg_priv_t *ctx,
                                                     va_list args) {
   AV1_PRIMARY *const ppi = ctx->ppi;
@@ -4610,6 +4624,8 @@ static aom_codec_ctrl_fn_map_t encoder_ctrl_maps[] = {
   { AV1E_SET_SVC_FRAME_DROP_MODE, ctrl_set_svc_frame_drop_mode },
   { AV1E_SET_AUTO_TILES, ctrl_set_auto_tiles },
   { AV1E_SET_POSTENCODE_DROP_RTC, ctrl_set_postencode_drop_rtc },
+  { AV1E_SET_MAX_CONSEC_FRAME_DROP_MS_CBR,
+    ctrl_set_max_consec_frame_drop_ms_cbr },
 
   // Getters
   { AOME_GET_LAST_QUANTIZER, ctrl_get_quantizer },
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index 8b3ddb5776..e071b0496b 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -620,6 +620,13 @@ typedef struct {
    * of the target bitrate.
    */
   int vbrmax_section;
+
+  /*!
+   * Indicates the maximum consecutive amount of frame drops, in units of time
+   * (milliseconds). This is converted to frame units internally. Only used in
+   * CBR mode.
+   */
+  int max_consec_drop_ms;
 } RateControlCfg;
 
 /*!\cond */
diff --git a/av1/encoder/ratectrl.c b/av1/encoder/ratectrl.c
index ea402201f1..320d984832 100644
--- a/av1/encoder/ratectrl.c
+++ b/av1/encoder/ratectrl.c
@@ -3679,6 +3679,11 @@ void av1_get_one_pass_rt_params(AV1_COMP *cpi, FRAME_TYPE *const frame_type,
   const int layer =
       LAYER_IDS_TO_IDX(svc->spatial_layer_id, svc->temporal_layer_id,
                        svc->number_temporal_layers);
+  if (cpi->oxcf.rc_cfg.max_consec_drop_ms > 0) {
+    rc->max_consec_drop = (int)AOMMIN(
+        ceil(cpi->oxcf.rc_cfg.max_consec_drop_ms * cpi->framerate / 1000),
+        INT_MAX);
+  }
   if (cpi->ppi->use_svc) {
     av1_update_temporal_layer_framerate(cpi);
     av1_restore_layer_context(cpi);
diff --git a/av1/ratectrl_rtc.cc b/av1/ratectrl_rtc.cc
index 7f1640a77c..e0d896c81b 100644
--- a/av1/ratectrl_rtc.cc
+++ b/av1/ratectrl_rtc.cc
@@ -11,12 +11,14 @@
 
 #include "av1/ratectrl_rtc.h"
 
+#include <climits>
 #include <memory>
 #include <new>
 
 #include "aom/aomcx.h"
 #include "aom/aom_encoder.h"
 #include "aom_mem/aom_mem.h"
+#include "aom_dsp/aom_dsp_common.h"
 #include "av1/encoder/encoder.h"
 #include "av1/encoder/encoder_utils.h"
 #include "av1/encoder/pickcdef.h"
@@ -40,7 +42,7 @@ AV1RateControlRtcConfig::AV1RateControlRtcConfig() {
   max_intra_bitrate_pct = 50;
   max_inter_bitrate_pct = 0;
   frame_drop_thresh = 0;
-  max_consec_drop = 0;
+  max_consec_drop_ms = 0;
   framerate = 30.0;
   ss_number_layers = 1;
   ts_number_layers = 1;
@@ -127,7 +129,10 @@ bool AV1RateControlRTC::InitRateControl(const AV1RateControlRtcConfig &rc_cfg) {
   oxcf->q_cfg.aq_mode = rc_cfg.aq_mode ? CYCLIC_REFRESH_AQ : NO_AQ;
   oxcf->tune_cfg.content = AOM_CONTENT_DEFAULT;
   oxcf->rc_cfg.drop_frames_water_mark = rc_cfg.frame_drop_thresh;
-  rc->max_consec_drop = rc_cfg.max_consec_drop;
+  if (rc_cfg.max_consec_drop_ms > 0) {
+    rc->max_consec_drop = (int)AOMMIN(
+        ceil(cpi_->framerate * rc_cfg.max_consec_drop_ms / 1000), INT_MAX);
+  }
   cpi_->svc.framedrop_mode = AOM_FULL_SUPERFRAME_DROP;
   oxcf->tool_cfg.bit_depth = AOM_BITS_8;
   oxcf->tool_cfg.superblock_size = AOM_SUPERBLOCK_SIZE_DYNAMIC;
@@ -190,7 +195,10 @@ bool AV1RateControlRTC::UpdateRateControl(
   oxcf->rc_cfg.under_shoot_pct = rc_cfg.undershoot_pct;
   oxcf->rc_cfg.over_shoot_pct = rc_cfg.overshoot_pct;
   oxcf->rc_cfg.drop_frames_water_mark = rc_cfg.frame_drop_thresh;
-  rc->max_consec_drop = rc_cfg.max_consec_drop;
+  if (rc_cfg.max_consec_drop_ms > 0) {
+    rc->max_consec_drop = (int)AOMMIN(
+        ceil(cpi_->framerate * rc_cfg.max_consec_drop_ms / 1000), INT_MAX);
+  }
   oxcf->rc_cfg.max_intra_bitrate_pct = rc_cfg.max_intra_bitrate_pct;
   oxcf->rc_cfg.max_inter_bitrate_pct = rc_cfg.max_inter_bitrate_pct;
   cpi_->framerate = rc_cfg.framerate;
diff --git a/av1/ratectrl_rtc.h b/av1/ratectrl_rtc.h
index a4b33039cd..e4da5d3b35 100644
--- a/av1/ratectrl_rtc.h
+++ b/av1/ratectrl_rtc.h
@@ -46,7 +46,7 @@ struct AV1RateControlRtcConfig {
   int max_intra_bitrate_pct;
   int max_inter_bitrate_pct;
   int frame_drop_thresh;
-  int max_consec_drop;
+  int max_consec_drop_ms;
   double framerate;
   int layer_target_bitrate[kAV1MaxLayers];
   int ts_rate_decimator[kAV1MaxTemporalLayers];
diff --git a/examples/svc_encoder_rtc.cc b/examples/svc_encoder_rtc.cc
index d034b1806b..4fb160e6e0 100644
--- a/examples/svc_encoder_rtc.cc
+++ b/examples/svc_encoder_rtc.cc
@@ -1703,7 +1703,7 @@ int main(int argc, const char **argv) {
     aom_codec_control(&codec, AV1E_SET_RTC_EXTERNAL_RC, 1);
   }
 
-  aom_codec_control(&codec, AV1E_SET_MAX_CONSEC_FRAME_DROP_CBR, INT_MAX);
+  aom_codec_control(&codec, AV1E_SET_MAX_CONSEC_FRAME_DROP_MS_CBR, INT_MAX);
 
   aom_codec_control(&codec, AV1E_SET_SVC_FRAME_DROP_MODE,
                     AOM_FULL_SUPERFRAME_DROP);
diff --git a/test/ratectrl_rtc_test.cc b/test/ratectrl_rtc_test.cc
index 31ae4509a4..20c4fbb178 100644
--- a/test/ratectrl_rtc_test.cc
+++ b/test/ratectrl_rtc_test.cc
@@ -37,7 +37,7 @@ class RcInterfaceTest : public ::libaom_test::EncoderTest,
       : EncoderTest(GET_PARAM(0)), aq_mode_(GET_PARAM(1)), key_interval_(3000),
         encoder_exit_(false), layer_frame_cnt_(0), superframe_cnt_(0),
         frame_cnt_(0), dynamic_temporal_layers_(false),
-        dynamic_spatial_layers_(false), num_drops_(0), max_consec_drop_(0),
+        dynamic_spatial_layers_(false), num_drops_(0), max_consec_drop_ms_(0),
         frame_drop_thresh_(0) {
     memset(&svc_params_, 0, sizeof(svc_params_));
     memset(&layer_id_, 0, sizeof(layer_id_));
@@ -67,7 +67,8 @@ class RcInterfaceTest : public ::libaom_test::EncoderTest,
       encoder->Control(AOME_SET_MAX_INTRA_BITRATE_PCT,
                        rc_cfg_.max_intra_bitrate_pct);
       if (use_svc) encoder->Control(AV1E_SET_SVC_PARAMS, &svc_params_);
-      encoder->Control(AV1E_SET_MAX_CONSEC_FRAME_DROP_CBR, max_consec_drop_);
+      encoder->Control(AV1E_SET_MAX_CONSEC_FRAME_DROP_MS_CBR,
+                       max_consec_drop_ms_);
     }
     // SVC specific settings
     if (use_svc) {
@@ -210,7 +211,7 @@ class RcInterfaceTest : public ::libaom_test::EncoderTest,
 
   void RunOneLayerDropFramesCBR() {
     key_interval_ = 10000;
-    max_consec_drop_ = 8;
+    max_consec_drop_ms_ = 250;
     frame_drop_thresh_ = 30;
     SetConfig();
     rc_cfg_.target_bandwidth = 100;
@@ -319,7 +320,7 @@ class RcInterfaceTest : public ::libaom_test::EncoderTest,
     rc_cfg_.min_quantizers[0] = 2;
     rc_cfg_.aq_mode = aq_mode_;
     rc_cfg_.frame_drop_thresh = frame_drop_thresh_;
-    rc_cfg_.max_consec_drop = max_consec_drop_;
+    rc_cfg_.max_consec_drop_ms = max_consec_drop_ms_;
 
     // Encoder settings for ground truth.
     cfg_.g_w = 640;
@@ -480,7 +481,7 @@ class RcInterfaceTest : public ::libaom_test::EncoderTest,
   bool dynamic_temporal_layers_;
   bool dynamic_spatial_layers_;
   int num_drops_;
-  int max_consec_drop_;
+  int max_consec_drop_ms_;
   int frame_drop_thresh_;
 };