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

Wu, Tong1 tong1.wu at intel.com
Thu Feb 22 12:10:09 EET 2024


>On 18/02/2024 08:45, tong1.wu-at-intel.com at ffmpeg.org wrote:
>> From: Tong Wu <tong1.wu at 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 at 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 --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.
>
>You definitely don't want this absurd wart from libva to leak into the common
>API parts.  Make new fields which have the desired meaning in the common
>parts and let the VAAPI code deal with this stupidity.

Agreed.

>
>> +
>> +            // 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 b836b22e6b..4072b514d3 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;
>
>The set of fields here needs more thought to match up the common parts of
>the APIs.
>
>Just have target rate and maxrate and let VAAPI deal with the percentage stuff
>maybe?  Not sure what to do with window_size which isn't present at all in
>D3D12.  The convergence/accuracy stuff for VAAPI AVBR is also unclear.
>

Could you please explain more about your thoughts on why the percentage stuff should not be in the common part? I think D3D12 can share the same code in terms of percentage stuff and hrd stuff. I can let VAAPI do the AVBR stuff and remove window_size from common part and keep everything else as-is.

>(Do you know why QVBR is missing the VBV parameters in D3D12?  On the face
>of it that doesn't make any sense, but maybe I'm missing something.)
>

It is presented in QVBR1 structure(defined in DirectX-header but not yet in Windows SDK).
I can add this support afterwards maybe along with AV1 implementation.

>> +
>> +
>>   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
>> ...
>
>Thanks,
>
>- Mark

Thanks for your review and I'll reply to your comments regarding D3D12 HEVC encoder patch later since there're indeed a lot of stuff you pointed out that I need to take care of again.

BRs,
Tong 

>_______________________________________________
>ffmpeg-devel mailing list
>ffmpeg-devel at ffmpeg.org
>https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
>To unsubscribe, visit link above, or email
>ffmpeg-devel-request at ffmpeg.org with subject "unsubscribe".


More information about the ffmpeg-devel mailing list