diff mbox series

[FFmpeg-devel,v3,4/9] avcodec/vaapi_encode: extract rc parameter configuration to base layer

Message ID 20240202101615.382-4-tong1.wu@intel.com
State New
Headers show
Series [FFmpeg-devel,v3,1/9] avcodec/vaapi_encode: move pic->input_surface initialization to encode_alloc | expand

Checks

Context Check Description
yinshiyou/make_loongarch64 success Make finished
yinshiyou/make_fate_loongarch64 success Make fate finished
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished

Commit Message

Wu, Tong1 Feb. 2, 2024, 10:16 a.m. UTC
From: Tong Wu <tong1.wu@intel.com>

VAAPI and D3D12VA can share rate control configuration codes. Hence, it
can be moved to base layer for simplification.

Signed-off-by: Tong Wu <tong1.wu@intel.com>
---
 libavcodec/hw_base_encode.c    | 151 ++++++++++++++++++++++++
 libavcodec/hw_base_encode.h    |  34 ++++++
 libavcodec/vaapi_encode.c      | 210 ++++++---------------------------
 libavcodec/vaapi_encode.h      |  14 +--
 libavcodec/vaapi_encode_av1.c  |   2 +-
 libavcodec/vaapi_encode_h264.c |   2 +-
 libavcodec/vaapi_encode_vp9.c  |   2 +-
 7 files changed, 227 insertions(+), 188 deletions(-)
diff mbox series

Patch

diff --git a/libavcodec/hw_base_encode.c b/libavcodec/hw_base_encode.c
index f0e4ef9655..c20c47bf55 100644
--- a/libavcodec/hw_base_encode.c
+++ b/libavcodec/hw_base_encode.c
@@ -631,6 +631,157 @@  end:
     return 0;
 }
 
+int ff_hw_base_rc_mode_configure(AVCodecContext *avctx, const HWBaseEncodeRCMode *rc_mode,
+                                 int default_quality, HWBaseEncodeRCConfigure *rc_conf)
+{
+    HWBaseEncodeContext *ctx = avctx->priv_data;
+
+    if (!rc_mode || !rc_conf)
+        return -1;
+
+    if (rc_mode->bitrate) {
+        if (avctx->bit_rate <= 0) {
+            av_log(avctx, AV_LOG_ERROR, "Bitrate must be set for %s "
+                   "RC mode.\n", rc_mode->name);
+            return AVERROR(EINVAL);
+        }
+
+        if (rc_mode->mode == RC_MODE_AVBR) {
+            // For maximum confusion AVBR is hacked into the existing API
+            // by overloading some of the fields with completely different
+            // meanings.
+
+            // Target percentage does not apply in AVBR mode.
+            rc_conf->rc_bits_per_second = avctx->bit_rate;
+
+            // Accuracy tolerance range for meeting the specified target
+            // bitrate.  It's very unclear how this is actually intended
+            // to work - since we do want to get the specified bitrate,
+            // set the accuracy to 100% for now.
+            rc_conf->rc_target_percentage = 100;
+
+            // Convergence period in frames.  The GOP size reflects the
+            // user's intended block size for cutting, so reusing that
+            // as the convergence period seems a reasonable default.
+            rc_conf->rc_window_size = avctx->gop_size > 0 ? avctx->gop_size : 60;
+
+        } else if (rc_mode->maxrate) {
+            if (avctx->rc_max_rate > 0) {
+                if (avctx->rc_max_rate < avctx->bit_rate) {
+                    av_log(avctx, AV_LOG_ERROR, "Invalid bitrate settings: "
+                           "bitrate (%"PRId64") must not be greater than "
+                           "maxrate (%"PRId64").\n", avctx->bit_rate,
+                           avctx->rc_max_rate);
+                    return AVERROR(EINVAL);
+                }
+                rc_conf->rc_bits_per_second   = avctx->rc_max_rate;
+                rc_conf->rc_target_percentage = (avctx->bit_rate * 100) /
+                                                 avctx->rc_max_rate;
+            } else {
+                // We only have a target bitrate, but this mode requires
+                // that a maximum rate be supplied as well.  Since the
+                // user does not want this to be a constraint, arbitrarily
+                // pick a maximum rate of double the target rate.
+                rc_conf->rc_bits_per_second   = 2 * avctx->bit_rate;
+                rc_conf->rc_target_percentage = 50;
+            }
+        } else {
+            if (avctx->rc_max_rate > avctx->bit_rate) {
+                av_log(avctx, AV_LOG_WARNING, "Max bitrate is ignored "
+                       "in %s RC mode.\n", rc_mode->name);
+            }
+            rc_conf->rc_bits_per_second   = avctx->bit_rate;
+            rc_conf->rc_target_percentage = 100;
+        }
+    } else {
+        rc_conf->rc_bits_per_second   = 0;
+        rc_conf->rc_target_percentage = 100;
+    }
+
+    if (rc_mode->quality) {
+        if (ctx->explicit_qp) {
+            rc_conf->rc_quality = ctx->explicit_qp;
+        } else if (avctx->global_quality > 0) {
+            rc_conf->rc_quality = avctx->global_quality;
+        } else {
+            rc_conf->rc_quality = default_quality;
+            av_log(avctx, AV_LOG_WARNING, "No quality level set; "
+                   "using default (%d).\n", rc_conf->rc_quality);
+        }
+    } else {
+        rc_conf->rc_quality = 0;
+    }
+
+    if (rc_mode->hrd) {
+        if (avctx->rc_buffer_size)
+            rc_conf->hrd_buffer_size = avctx->rc_buffer_size;
+        else if (avctx->rc_max_rate > 0)
+            rc_conf->hrd_buffer_size = avctx->rc_max_rate;
+        else
+            rc_conf->hrd_buffer_size = avctx->bit_rate;
+        if (avctx->rc_initial_buffer_occupancy) {
+            if (avctx->rc_initial_buffer_occupancy > rc_conf->hrd_buffer_size) {
+                av_log(avctx, AV_LOG_ERROR, "Invalid RC buffer settings: "
+                       "must have initial buffer size (%d) <= "
+                       "buffer size (%"PRId64").\n",
+                       avctx->rc_initial_buffer_occupancy, rc_conf->hrd_buffer_size);
+                return AVERROR(EINVAL);
+            }
+            rc_conf->hrd_initial_buffer_fullness = avctx->rc_initial_buffer_occupancy;
+        } else {
+            rc_conf->hrd_initial_buffer_fullness = rc_conf->hrd_buffer_size * 3 / 4;
+        }
+
+        rc_conf->rc_window_size = (rc_conf->hrd_buffer_size * 1000) / rc_conf->rc_bits_per_second;
+    } else {
+        if (avctx->rc_buffer_size || avctx->rc_initial_buffer_occupancy) {
+            av_log(avctx, AV_LOG_WARNING, "Buffering settings are ignored "
+                   "in %s RC mode.\n", rc_mode->name);
+        }
+
+        rc_conf->hrd_buffer_size             = 0;
+        rc_conf->hrd_initial_buffer_fullness = 0;
+
+        if (rc_mode->mode != RC_MODE_AVBR) {
+            // Already set (with completely different meaning) for AVBR.
+            rc_conf->rc_window_size = 1000;
+        }
+    }
+
+    if (rc_conf->rc_bits_per_second          > UINT32_MAX ||
+        rc_conf->hrd_buffer_size             > UINT32_MAX ||
+        rc_conf->hrd_initial_buffer_fullness > UINT32_MAX) {
+        av_log(avctx, AV_LOG_ERROR, "RC parameters of 2^32 or "
+               "greater are not supported by hardware.\n");
+        return AVERROR(EINVAL);
+    }
+
+    av_log(avctx, AV_LOG_VERBOSE, "RC mode: %s.\n", rc_mode->name);
+
+    if (rc_mode->quality)
+        av_log(avctx, AV_LOG_VERBOSE, "RC quality: %d.\n", rc_conf->rc_quality);
+
+    if (rc_mode->hrd) {
+        av_log(avctx, AV_LOG_VERBOSE, "RC buffer: %"PRId64" bits, "
+               "initial fullness %"PRId64" bits.\n",
+               rc_conf->hrd_buffer_size, rc_conf->hrd_initial_buffer_fullness);
+    }
+
+    if (avctx->framerate.num > 0 && avctx->framerate.den > 0)
+        av_reduce(&rc_conf->fr_num, &rc_conf->fr_den,
+                  avctx->framerate.num, avctx->framerate.den, 65535);
+    else
+        av_reduce(&rc_conf->fr_num, &rc_conf->fr_den,
+                  avctx->time_base.den, avctx->time_base.num, 65535);
+
+    av_log(avctx, AV_LOG_VERBOSE, "RC framerate: %d/%d (%.2f fps).\n",
+           rc_conf->fr_num, rc_conf->fr_den, (double)rc_conf->fr_num / rc_conf->fr_den);
+
+    ctx->rc_quality  = rc_conf->rc_quality;
+
+    return 0;
+}
+
 int ff_hw_base_encode_init(AVCodecContext *avctx)
 {
     HWBaseEncodeContext *ctx = avctx->priv_data;
diff --git a/libavcodec/hw_base_encode.h b/libavcodec/hw_base_encode.h
index d215d6a32b..57cfa12e73 100644
--- a/libavcodec/hw_base_encode.h
+++ b/libavcodec/hw_base_encode.h
@@ -72,6 +72,37 @@  enum {
     RC_MODE_MAX = RC_MODE_AVBR,
 };
 
+typedef struct HWBaseEncodeRCMode {
+    // Mode from above enum (RC_MODE_*).
+    int mode;
+    // Name.
+    const char *name;
+    // Uses bitrate parameters.
+    int bitrate;
+    // Supports maxrate distinct from bitrate.
+    int maxrate;
+    // Uses quality value.
+    int quality;
+    // Supports HRD/VBV parameters.
+    int hrd;
+} HWBaseEncodeRCMode;
+
+typedef struct HWBaseEncodeRCConfigure
+{
+    int64_t rc_bits_per_second;
+    int rc_target_percentage;
+    int rc_window_size;
+
+    int rc_quality;
+
+    int64_t hrd_buffer_size;
+    int64_t hrd_initial_buffer_fullness;
+
+    int fr_num;
+    int fr_den;
+} HWBaseEncodeRCConfigure;
+
+
 typedef struct HWBaseEncodePicture {
     struct HWBaseEncodePicture *next;
 
@@ -242,6 +273,9 @@  int ff_hw_base_encode_set_output_property(AVCodecContext *avctx, HWBaseEncodePic
 
 int ff_hw_base_encode_receive_packet(AVCodecContext *avctx, AVPacket *pkt);
 
+int ff_hw_base_rc_mode_configure(AVCodecContext *avctx, const HWBaseEncodeRCMode *rc_mode,
+                                 int default_quality, HWBaseEncodeRCConfigure *rc_conf);
+
 int ff_hw_base_encode_init(AVCodecContext *avctx);
 
 int ff_hw_base_encode_close(AVCodecContext *avctx);
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index 2d839a1202..30e5deac08 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
@@ -1164,23 +1164,23 @@  fail:
 }
 
 static const VAAPIEncodeRCMode vaapi_encode_rc_modes[] = {
-    //                                  Bitrate   Quality
-    //                                     | Maxrate | HRD/VBV
-    { 0 }, //                              |    |    |    |
-    { RC_MODE_CQP,  "CQP",  1, VA_RC_CQP,  0,   0,   1,   0 },
-    { RC_MODE_CBR,  "CBR",  1, VA_RC_CBR,  1,   0,   0,   1 },
-    { RC_MODE_VBR,  "VBR",  1, VA_RC_VBR,  1,   1,   0,   1 },
+    //                     Bitrate   Quality
+    //                        | Maxrate | HRD/VBV
+    { { 0 } }, //             |    |    |    |
+    { { RC_MODE_CQP,  "CQP",  0,   0,   1,   0 }, 1, VA_RC_CQP },
+    { { RC_MODE_CBR,  "CBR",  1,   0,   0,   1 }, 1, VA_RC_CBR },
+    { { RC_MODE_VBR,  "VBR",  1,   1,   0,   1 }, 1, VA_RC_VBR },
 #if VA_CHECK_VERSION(1, 1, 0)
-    { RC_MODE_ICQ,  "ICQ",  1, VA_RC_ICQ,  0,   0,   1,   0 },
+    { { RC_MODE_ICQ,  "ICQ",  0,   0,   1,   0 }, 1, VA_RC_ICQ },
 #else
-    { RC_MODE_ICQ,  "ICQ",  0 },
+    { { RC_MODE_ICQ,  "ICQ",  0,   0,   1,   0 }, 0 },
 #endif
 #if VA_CHECK_VERSION(1, 3, 0)
-    { RC_MODE_QVBR, "QVBR", 1, VA_RC_QVBR, 1,   1,   1,   1 },
-    { RC_MODE_AVBR, "AVBR", 0, VA_RC_AVBR, 1,   0,   0,   0 },
+    { { RC_MODE_QVBR, "QVBR", 1,   1,   1,   1 }, 1, VA_RC_QVBR },
+    { { RC_MODE_AVBR, "AVBR", 1,   0,   0,   0 }, 0, VA_RC_AVBR },
 #else
-    { RC_MODE_QVBR, "QVBR", 0 },
-    { RC_MODE_AVBR, "AVBR", 0 },
+    { { RC_MODE_QVBR, "QVBR", 1,   1,   1,   1 }, 0 },
+    { { RC_MODE_AVBR, "AVBR", 1,   0,   0,   0 }, 0 },
 #endif
 };
 
@@ -1190,13 +1190,8 @@  static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx)
     VAAPIEncodeContext       *ctx = avctx->priv_data;
     uint32_t supported_va_rc_modes;
     const VAAPIEncodeRCMode *rc_mode;
-    int64_t rc_bits_per_second;
-    int     rc_target_percentage;
-    int     rc_window_size;
-    int     rc_quality;
-    int64_t hrd_buffer_size;
-    int64_t hrd_initial_buffer_fullness;
-    int fr_num, fr_den;
+    HWBaseEncodeRCConfigure rc_conf = { 0 };
+    int err;
     VAConfigAttrib rc_attr = { VAConfigAttribRateControl };
     VAStatus vas;
     char supported_rc_modes_string[64];
@@ -1224,7 +1219,7 @@  static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx)
             rc_mode = &vaapi_encode_rc_modes[i];
             if (supported_va_rc_modes & rc_mode->va_mode) {
                 res = snprintf(str, len, "%s%s",
-                               first ? "" : ", ", rc_mode->name);
+                               first ? "" : ", ", rc_mode->base.name);
                 first = 0;
                 if (res < 0) {
                     *str = 0;
@@ -1258,12 +1253,12 @@  static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx)
         if (!(rc_mode->va_mode & supported_va_rc_modes)) { \
             if (fail) { \
                 av_log(avctx, AV_LOG_ERROR, "Driver does not support %s " \
-                       "RC mode (supported modes: %s).\n", rc_mode->name, \
+                       "RC mode (supported modes: %s).\n", rc_mode->base.name, \
                        supported_rc_modes_string); \
                 return AVERROR(EINVAL); \
             } \
             av_log(avctx, AV_LOG_DEBUG, "Driver does not support %s " \
-                   "RC mode.\n", rc_mode->name); \
+                   "RC mode.\n", rc_mode->base.name); \
             rc_mode = NULL; \
         } else { \
             goto rc_mode_found; \
@@ -1308,129 +1303,15 @@  static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx)
     return AVERROR(EINVAL);
 
 rc_mode_found:
-    if (rc_mode->bitrate) {
-        if (avctx->bit_rate <= 0) {
-            av_log(avctx, AV_LOG_ERROR, "Bitrate must be set for %s "
-                   "RC mode.\n", rc_mode->name);
-            return AVERROR(EINVAL);
-        }
-
-        if (rc_mode->mode == RC_MODE_AVBR) {
-            // For maximum confusion AVBR is hacked into the existing API
-            // by overloading some of the fields with completely different
-            // meanings.
-
-            // Target percentage does not apply in AVBR mode.
-            rc_bits_per_second = avctx->bit_rate;
-
-            // Accuracy tolerance range for meeting the specified target
-            // bitrate.  It's very unclear how this is actually intended
-            // to work - since we do want to get the specified bitrate,
-            // set the accuracy to 100% for now.
-            rc_target_percentage = 100;
-
-            // Convergence period in frames.  The GOP size reflects the
-            // user's intended block size for cutting, so reusing that
-            // as the convergence period seems a reasonable default.
-            rc_window_size = avctx->gop_size > 0 ? avctx->gop_size : 60;
-
-        } else if (rc_mode->maxrate) {
-            if (avctx->rc_max_rate > 0) {
-                if (avctx->rc_max_rate < avctx->bit_rate) {
-                    av_log(avctx, AV_LOG_ERROR, "Invalid bitrate settings: "
-                           "bitrate (%"PRId64") must not be greater than "
-                           "maxrate (%"PRId64").\n", avctx->bit_rate,
-                           avctx->rc_max_rate);
-                    return AVERROR(EINVAL);
-                }
-                rc_bits_per_second   = avctx->rc_max_rate;
-                rc_target_percentage = (avctx->bit_rate * 100) /
-                                       avctx->rc_max_rate;
-            } else {
-                // We only have a target bitrate, but this mode requires
-                // that a maximum rate be supplied as well.  Since the
-                // user does not want this to be a constraint, arbitrarily
-                // pick a maximum rate of double the target rate.
-                rc_bits_per_second   = 2 * avctx->bit_rate;
-                rc_target_percentage = 50;
-            }
-        } else {
-            if (avctx->rc_max_rate > avctx->bit_rate) {
-                av_log(avctx, AV_LOG_WARNING, "Max bitrate is ignored "
-                       "in %s RC mode.\n", rc_mode->name);
-            }
-            rc_bits_per_second   = avctx->bit_rate;
-            rc_target_percentage = 100;
-        }
-    } else {
-        rc_bits_per_second   = 0;
-        rc_target_percentage = 100;
-    }
-
-    if (rc_mode->quality) {
-        if (base_ctx->explicit_qp) {
-            rc_quality = base_ctx->explicit_qp;
-        } else if (avctx->global_quality > 0) {
-            rc_quality = avctx->global_quality;
-        } else {
-            rc_quality = ctx->codec->default_quality;
-            av_log(avctx, AV_LOG_WARNING, "No quality level set; "
-                   "using default (%d).\n", rc_quality);
-        }
-    } else {
-        rc_quality = 0;
-    }
-
-    if (rc_mode->hrd) {
-        if (avctx->rc_buffer_size)
-            hrd_buffer_size = avctx->rc_buffer_size;
-        else if (avctx->rc_max_rate > 0)
-            hrd_buffer_size = avctx->rc_max_rate;
-        else
-            hrd_buffer_size = avctx->bit_rate;
-        if (avctx->rc_initial_buffer_occupancy) {
-            if (avctx->rc_initial_buffer_occupancy > hrd_buffer_size) {
-                av_log(avctx, AV_LOG_ERROR, "Invalid RC buffer settings: "
-                       "must have initial buffer size (%d) <= "
-                       "buffer size (%"PRId64").\n",
-                       avctx->rc_initial_buffer_occupancy, hrd_buffer_size);
-                return AVERROR(EINVAL);
-            }
-            hrd_initial_buffer_fullness = avctx->rc_initial_buffer_occupancy;
-        } else {
-            hrd_initial_buffer_fullness = hrd_buffer_size * 3 / 4;
-        }
-
-        rc_window_size = (hrd_buffer_size * 1000) / rc_bits_per_second;
-    } else {
-        if (avctx->rc_buffer_size || avctx->rc_initial_buffer_occupancy) {
-            av_log(avctx, AV_LOG_WARNING, "Buffering settings are ignored "
-                   "in %s RC mode.\n", rc_mode->name);
-        }
-
-        hrd_buffer_size             = 0;
-        hrd_initial_buffer_fullness = 0;
-
-        if (rc_mode->mode != RC_MODE_AVBR) {
-            // Already set (with completely different meaning) for AVBR.
-            rc_window_size = 1000;
-        }
-    }
-
-    if (rc_bits_per_second          > UINT32_MAX ||
-        hrd_buffer_size             > UINT32_MAX ||
-        hrd_initial_buffer_fullness > UINT32_MAX) {
-        av_log(avctx, AV_LOG_ERROR, "RC parameters of 2^32 or "
-               "greater are not supported by VAAPI.\n");
-        return AVERROR(EINVAL);
-    }
+    err = ff_hw_base_rc_mode_configure(avctx, (const HWBaseEncodeRCMode*)rc_mode,
+                                       ctx->codec->default_quality, &rc_conf);
+    if (err < 0)
+        return err;
 
     ctx->rc_mode          = rc_mode;
-    base_ctx->rc_quality  = rc_quality;
     ctx->va_rc_mode       = rc_mode->va_mode;
-    ctx->va_bit_rate      = rc_bits_per_second;
+    ctx->va_bit_rate      = rc_conf.rc_bits_per_second;
 
-    av_log(avctx, AV_LOG_VERBOSE, "RC mode: %s.\n", rc_mode->name);
     if (rc_attr.value == VA_ATTRIB_NOT_SUPPORTED) {
         // This driver does not want the RC mode attribute to be set.
     } else {
@@ -1441,34 +1322,31 @@  rc_mode_found:
         };
     }
 
-    if (rc_mode->quality)
-        av_log(avctx, AV_LOG_VERBOSE, "RC quality: %d.\n", rc_quality);
-
     if (rc_mode->va_mode != VA_RC_CQP) {
-        if (rc_mode->mode == RC_MODE_AVBR) {
+        if (rc_mode->base.mode == RC_MODE_AVBR) {
             av_log(avctx, AV_LOG_VERBOSE, "RC target: %"PRId64" bps "
                    "converging in %d frames with %d%% accuracy.\n",
-                   rc_bits_per_second, rc_window_size,
-                   rc_target_percentage);
-        } else if (rc_mode->bitrate) {
+                   rc_conf.rc_bits_per_second, rc_conf.rc_window_size,
+                   rc_conf.rc_target_percentage);
+        } else if (rc_mode->base.bitrate) {
             av_log(avctx, AV_LOG_VERBOSE, "RC target: %d%% of "
-                   "%"PRId64" bps over %d ms.\n", rc_target_percentage,
-                   rc_bits_per_second, rc_window_size);
+                   "%"PRId64" bps over %d ms.\n", rc_conf.rc_target_percentage,
+                   rc_conf.rc_bits_per_second, rc_conf.rc_window_size);
         }
 
         ctx->rc_params = (VAEncMiscParameterRateControl) {
-            .bits_per_second    = rc_bits_per_second,
-            .target_percentage  = rc_target_percentage,
-            .window_size        = rc_window_size,
+            .bits_per_second    = rc_conf.rc_bits_per_second,
+            .target_percentage  = rc_conf.rc_target_percentage,
+            .window_size        = rc_conf.rc_window_size,
             .initial_qp         = 0,
             .min_qp             = (avctx->qmin > 0 ? avctx->qmin : 0),
             .basic_unit_size    = 0,
 #if VA_CHECK_VERSION(1, 1, 0)
-            .ICQ_quality_factor = av_clip(rc_quality, 1, 51),
+            .ICQ_quality_factor = av_clip(rc_conf.rc_quality, 1, 51),
             .max_qp             = (avctx->qmax > 0 ? avctx->qmax : 0),
 #endif
 #if VA_CHECK_VERSION(1, 3, 0)
-            .quality_factor     = rc_quality,
+            .quality_factor     = rc_conf.rc_quality,
 #endif
         };
         vaapi_encode_add_global_param(avctx,
@@ -1477,14 +1355,10 @@  rc_mode_found:
                                       sizeof(ctx->rc_params));
     }
 
-    if (rc_mode->hrd) {
-        av_log(avctx, AV_LOG_VERBOSE, "RC buffer: %"PRId64" bits, "
-               "initial fullness %"PRId64" bits.\n",
-               hrd_buffer_size, hrd_initial_buffer_fullness);
-
+    if (rc_mode->base.hrd) {
         ctx->hrd_params = (VAEncMiscParameterHRD) {
-            .initial_buffer_fullness = hrd_initial_buffer_fullness,
-            .buffer_size             = hrd_buffer_size,
+            .initial_buffer_fullness = rc_conf.hrd_initial_buffer_fullness,
+            .buffer_size             = rc_conf.hrd_buffer_size,
         };
         vaapi_encode_add_global_param(avctx,
                                       VAEncMiscParameterTypeHRD,
@@ -1492,18 +1366,8 @@  rc_mode_found:
                                       sizeof(ctx->hrd_params));
     }
 
-    if (avctx->framerate.num > 0 && avctx->framerate.den > 0)
-        av_reduce(&fr_num, &fr_den,
-                  avctx->framerate.num, avctx->framerate.den, 65535);
-    else
-        av_reduce(&fr_num, &fr_den,
-                  avctx->time_base.den, avctx->time_base.num, 65535);
-
-    av_log(avctx, AV_LOG_VERBOSE, "RC framerate: %d/%d (%.2f fps).\n",
-           fr_num, fr_den, (double)fr_num / fr_den);
-
     ctx->fr_params = (VAEncMiscParameterFrameRate) {
-        .framerate = (unsigned int)fr_den << 16 | fr_num,
+        .framerate = (unsigned int)rc_conf.fr_den << 16 | rc_conf.fr_num,
     };
 #if VA_CHECK_VERSION(0, 40, 0)
     vaapi_encode_add_global_param(avctx,
diff --git a/libavcodec/vaapi_encode.h b/libavcodec/vaapi_encode.h
index 772fb83f5b..c947aca52b 100644
--- a/libavcodec/vaapi_encode.h
+++ b/libavcodec/vaapi_encode.h
@@ -110,22 +110,12 @@  typedef struct VAAPIEncodeProfile {
 } VAAPIEncodeProfile;
 
 typedef struct VAAPIEncodeRCMode {
-    // Mode from above enum (RC_MODE_*).
-    int mode;
-    // Name.
-    const char *name;
+    // Base.
+    HWBaseEncodeRCMode base;
     // Supported in the compile-time VAAPI version.
     int supported;
     // VA mode value (VA_RC_*).
     uint32_t va_mode;
-    // Uses bitrate parameters.
-    int bitrate;
-    // Supports maxrate distinct from bitrate.
-    int maxrate;
-    // Uses quality value.
-    int quality;
-    // Supports HRD/VBV parameters.
-    int hrd;
 } VAAPIEncodeRCMode;
 
 typedef struct VAAPIEncodeContext {
diff --git a/libavcodec/vaapi_encode_av1.c b/libavcodec/vaapi_encode_av1.c
index d3785e133c..10fc79783a 100644
--- a/libavcodec/vaapi_encode_av1.c
+++ b/libavcodec/vaapi_encode_av1.c
@@ -134,7 +134,7 @@  static av_cold int vaapi_encode_av1_configure(AVCodecContext *avctx)
     priv->cbc->trace_context = ctx;
     priv->cbc->trace_write_callback = vaapi_encode_av1_trace_write_log;
 
-    if (ctx->rc_mode->quality) {
+    if (ctx->rc_mode->base.quality) {
         priv->q_idx_p = av_clip(base_ctx->rc_quality, 0, AV1_MAX_QUANT);
         if (fabs(avctx->i_quant_factor) > 0.0)
             priv->q_idx_idr =
diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c
index fe02f75f85..5a1db27690 100644
--- a/libavcodec/vaapi_encode_h264.c
+++ b/libavcodec/vaapi_encode_h264.c
@@ -1124,7 +1124,7 @@  static av_cold int vaapi_encode_h264_configure(AVCodecContext *avctx)
         priv->fixed_qp_b   = 26;
     }
 
-    if (!ctx->rc_mode->hrd) {
+    if (!ctx->rc_mode->base.hrd) {
         // Timing SEI requires a mode respecting HRD parameters.
         priv->sei &= ~SEI_TIMING;
     }
diff --git a/libavcodec/vaapi_encode_vp9.c b/libavcodec/vaapi_encode_vp9.c
index 85621d7471..a1c0f45e5a 100644
--- a/libavcodec/vaapi_encode_vp9.c
+++ b/libavcodec/vaapi_encode_vp9.c
@@ -205,7 +205,7 @@  static av_cold int vaapi_encode_vp9_configure(AVCodecContext *avctx)
     VAAPIEncodeContext       *ctx = avctx->priv_data;
     VAAPIEncodeVP9Context   *priv = avctx->priv_data;
 
-    if (ctx->rc_mode->quality) {
+    if (ctx->rc_mode->base.quality) {
         priv->q_idx_p = av_clip(base_ctx->rc_quality, 0, VP9_MAX_QUANT);
         if (avctx->i_quant_factor > 0.0)
             priv->q_idx_idr =