[FFmpeg-devel] [PATCH v8 05/15] avcodec/vaapi_encode: move the dpb logic from VAAPI to base layer

Wu, Tong1 tong1.wu at intel.com
Wed May 15 13:14:10 EEST 2024


>-----Original Message-----
>From: ffmpeg-devel <ffmpeg-devel-bounces at ffmpeg.org> On Behalf Of Mark
>Thompson
>Sent: Wednesday, May 15, 2024 4:47 AM
>To: ffmpeg-devel at ffmpeg.org
>Subject: Re: [FFmpeg-devel] [PATCH v8 05/15] avcodec/vaapi_encode: move
>the dpb logic from VAAPI to base layer
>
>On 18/04/2024 09:58, tong1.wu-at-intel.com at ffmpeg.org wrote:
>> From: Tong Wu <tong1.wu at intel.com>
>>
>> Move receive_packet function to base. This requires adding *alloc,
>> *issue, *output, *free as hardware callbacks. HWBaseEncodePicture is
>> introduced as the base layer structure. The related parameters in
>> VAAPIEncodeContext are also extracted to HWBaseEncodeContext. Then DPB
>> management logic can be fully extracted to base layer as-is.
>>
>> Signed-off-by: Tong Wu <tong1.wu at intel.com>
>> ---
>>  libavcodec/Makefile             |   2 +-
>>  libavcodec/hw_base_encode.c     | 600 ++++++++++++++++++++++++
>>  libavcodec/hw_base_encode.h     | 123 +++++
>>  libavcodec/vaapi_encode.c       | 793 +++++---------------------------
>>  libavcodec/vaapi_encode.h       | 102 +---
>>  libavcodec/vaapi_encode_av1.c   |  51 +-
>>  libavcodec/vaapi_encode_h264.c  | 176 +++----
>>  libavcodec/vaapi_encode_h265.c  | 121 ++---
>>  libavcodec/vaapi_encode_mjpeg.c |   7 +-
>>  libavcodec/vaapi_encode_mpeg2.c |  47 +-
>>  libavcodec/vaapi_encode_vp8.c   |  18 +-
>>  libavcodec/vaapi_encode_vp9.c   |  54 +--
>>  12 files changed, 1097 insertions(+), 997 deletions(-)
>>  create mode 100644 libavcodec/hw_base_encode.c
>>
>> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
>> index 7f6de4470e..a2174dcb2f 100644
>> --- a/libavcodec/Makefile
>> +++ b/libavcodec/Makefile
>> @@ -162,7 +162,7 @@ OBJS-$(CONFIG_STARTCODE)               += startcode.o
>>  OBJS-$(CONFIG_TEXTUREDSP)              += texturedsp.o
>>  OBJS-$(CONFIG_TEXTUREDSPENC)           += texturedspenc.o
>>  OBJS-$(CONFIG_TPELDSP)                 += tpeldsp.o
>> -OBJS-$(CONFIG_VAAPI_ENCODE)            += vaapi_encode.o
>> +OBJS-$(CONFIG_VAAPI_ENCODE)            += vaapi_encode.o
>hw_base_encode.o
>>  OBJS-$(CONFIG_AV1_AMF_ENCODER)         += amfenc_av1.o
>>  OBJS-$(CONFIG_VC1DSP)                  += vc1dsp.o
>>  OBJS-$(CONFIG_VIDEODSP)                += videodsp.o
>> diff --git a/libavcodec/hw_base_encode.c b/libavcodec/hw_base_encode.c
>> new file mode 100644
>> index 0000000000..1d9a255f69
>> --- /dev/null
>> +++ b/libavcodec/hw_base_encode.c
>> @@ -0,0 +1,600 @@
>> +/*
>> + * This file is part of FFmpeg.
>> + *
>> + * FFmpeg is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU Lesser General Public
>> + * License as published by the Free Software Foundation; either
>> + * version 2.1 of the License, or (at your option) any later version.
>> + *
>> + * FFmpeg is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>GNU
>> + * Lesser General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU Lesser General Public
>> + * License along with FFmpeg; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
>USA
>> + */
>> +
>> +#include "libavutil/avassert.h"
>> +#include "libavutil/common.h"
>> +#include "libavutil/internal.h"
>> +#include "libavutil/log.h"
>> +#include "libavutil/mem.h"
>> +#include "libavutil/pixdesc.h"
>> +
>> +#include "encode.h"
>> +#include "avcodec.h"
>> +#include "hw_base_encode.h"
>> +
>> ...
>
>Everything above here looks like a copy of the VAAPI code with the names
>changed, good if so.  (If not then please highlight any differences.)
>
>> +
>> +static int hw_base_encode_send_frame(AVCodecContext *avctx, AVFrame
>*frame)
>> +{
>> +    HWBaseEncodeContext *ctx = avctx->priv_data;
>> +    HWBaseEncodePicture *pic;
>> +    int err;
>> +
>> +    if (frame) {
>> +        av_log(avctx, AV_LOG_DEBUG, "Input frame: %ux%u (%"PRId64").\n",
>> +               frame->width, frame->height, frame->pts);
>> +
>> +        err = hw_base_encode_check_frame(avctx, frame);
>> +        if (err < 0)
>> +            return err;
>> +
>> +        pic = ctx->op->alloc(avctx, frame);
>> +        if (!pic)
>> +            return AVERROR(ENOMEM);
>
>Can you push the allocation of this picture out into the base layer?
>vaapi_encode_alloc() and d3d12va_encode_alloc() are identical except for the
>types and the input_surface setting.

You still need to know the API-specific picture type size for allocation right? I could add a hw_encode_alloc() which calls API-specific alloc() to alloc with the specific picture type size and move priv_data to the base. Or the picture type size has to be part of the callback structure.

>
>> +
>> +        pic->input_image = av_frame_alloc();
>> +        if (!pic->input_image) {
>> +            err = AVERROR(ENOMEM);
>> +            goto fail;
>> +        }
>> +
>> +        pic->recon_image = av_frame_alloc();
>> +        if (!pic->recon_image) {
>> +            err = AVERROR(ENOMEM);
>> +            goto fail;
>> +        }
>> +
>> +        if (ctx->input_order == 0 || frame->pict_type == AV_PICTURE_TYPE_I)
>> +            pic->force_idr = 1;
>> +
>> +        pic->pts = frame->pts;
>> +        pic->duration = frame->duration;
>> +
>> +        if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) {
>> +            err = av_buffer_replace(&pic->opaque_ref, frame->opaque_ref);
>> +            if (err < 0)
>> +                goto fail;
>> +
>> +            pic->opaque = frame->opaque;
>> +        }
>> +
>> +        av_frame_move_ref(pic->input_image, frame);
>> +
>> +        if (ctx->input_order == 0)
>> +            ctx->first_pts = pic->pts;
>> +        if (ctx->input_order == ctx->decode_delay)
>> +            ctx->dts_pts_diff = pic->pts - ctx->first_pts;
>> +        if (ctx->output_delay > 0)
>> +            ctx->ts_ring[ctx->input_order %
>> +                        (3 * ctx->output_delay + ctx->async_depth)] = pic->pts;
>> +
>> +        pic->display_order = ctx->input_order;
>> +        ++ctx->input_order;
>> +
>> +        if (ctx->pic_start) {
>> +            ctx->pic_end->next = pic;
>> +            ctx->pic_end       = pic;
>> +        } else {
>> +            ctx->pic_start     = pic;
>> +            ctx->pic_end       = pic;
>> +        }
>> +
>> +    } else {
>> +        ctx->end_of_stream = 1;
>> +
>> +        // Fix timestamps if we hit end-of-stream before the initial decode
>> +        // delay has elapsed.
>> +        if (ctx->input_order < ctx->decode_delay)
>> +            ctx->dts_pts_diff = ctx->pic_end->pts - ctx->first_pts;
>> +    }
>> +
>> +    return 0;
>> +
>> +fail:
>> +    ctx->op->free(avctx, pic);
>
>Maybe same with free, since it mixes the two layers?  hw_base_encode_free()
>in this file calling the API-specific free might be cleaner.

ff_hw_base_encode_free was extracted in a following patch. Current design is let vaapi_encode_free call ff_hw_base_encode_free. Ok if so the idea is the opposite. I could let ff_hw_base_encode_free call API-specific free here. Also in ff_vaapi_encode_close, vaapi_encode_free should be replaced with ff_hw_base_encode_free.

>
>> +    return err;
>> +}
>> +
>> +int ff_hw_base_encode_receive_packet(AVCodecContext *avctx, AVPacket
>*pkt)
>> +{
>> +    HWBaseEncodeContext *ctx = avctx->priv_data;
>> +    HWBaseEncodePicture *pic = NULL;
>> +    AVFrame *frame = ctx->frame;
>> +    int err;
>> +
>> +start:
>> +    /** if no B frame before repeat P frame, sent repeat P frame out. */
>> +    if (ctx->tail_pkt->size) {
>> +        for (HWBaseEncodePicture *tmp = ctx->pic_start; tmp; tmp = tmp-
>>next) {
>> +            if (tmp->type == PICTURE_TYPE_B && tmp->pts < ctx->tail_pkt->pts)
>> +                break;
>> +            else if (!tmp->next) {
>> +                av_packet_move_ref(pkt, ctx->tail_pkt);
>> +                goto end;
>> +            }
>> +        }
>> +    }
>> +
>> +    err = ff_encode_get_frame(avctx, frame);
>> +    if (err < 0 && err != AVERROR_EOF)
>> +        return err;
>> +
>> +    if (err == AVERROR_EOF)
>> +        frame = NULL;
>> +
>> +    if (!(ctx->op && ctx->op->alloc && ctx->op->issue &&
>> +          ctx->op->output && ctx->op->free)) {
>> +        err = AVERROR(EINVAL);
>> +        return err;
>> +    }
>
>This check feels like it should have been done earlier?
>
>Also, it should probably be an assert - EINVAL is indicating that the caller has
>got something wrong, while this would be a bug in this case.

Got it.

>
>> +
>> +    err = hw_base_encode_send_frame(avctx, frame);
>> +    if (err < 0)
>> +        return err;
>> +
>> +    if (!ctx->pic_start) {
>> +        if (ctx->end_of_stream)
>> +            return AVERROR_EOF;
>> +        else
>> +            return AVERROR(EAGAIN);
>> +    }
>> +
>> +    if (ctx->async_encode) {
>
>Is async_encode false tested after this series?  (I see that D3D12 has it set
>unconditionally.)

Yes. Async_encode is only used for vaapi check and the logic for vaapi is never changed.

>
>> +        if (av_fifo_can_write(ctx->encode_fifo)) {
>> +            err = hw_base_encode_pick_next(avctx, &pic);
>> +            if (!err) {
>> +                av_assert0(pic);
>> +                pic->encode_order = ctx->encode_order +
>> +                    av_fifo_can_read(ctx->encode_fifo);
>> +                err = ctx->op->issue(avctx, pic);
>> +                if (err < 0) {
>> +                    av_log(avctx, AV_LOG_ERROR, "Encode failed: %d.\n", err);
>> +                    return err;
>> +                }
>> +                pic->encode_issued = 1;
>> +                av_fifo_write(ctx->encode_fifo, &pic, 1);
>> +            }
>> +        }
>> +
>> +        if (!av_fifo_can_read(ctx->encode_fifo))
>> +            return err;
>> +
>> +        // More frames can be buffered
>> +        if (av_fifo_can_write(ctx->encode_fifo) && !ctx->end_of_stream)
>> +            return AVERROR(EAGAIN);
>> +
>> +        av_fifo_read(ctx->encode_fifo, &pic, 1);
>> +        ctx->encode_order = pic->encode_order + 1;
>> +    } else {
>> +        err = hw_base_encode_pick_next(avctx, &pic);
>> +        if (err < 0)
>> +            return err;
>> +        av_assert0(pic);
>> +
>> +        pic->encode_order = ctx->encode_order++;
>> +
>> +        err = ctx->op->issue(avctx, pic);
>> +        if (err < 0) {
>> +            av_log(avctx, AV_LOG_ERROR, "Encode failed: %d.\n", err);
>> +            return err;
>> +        }
>> +
>> +        pic->encode_issued = 1;
>> +    }
>> +
>> +    err = ctx->op->output(avctx, pic, pkt);
>> +    if (err < 0) {
>> +        av_log(avctx, AV_LOG_ERROR, "Output failed: %d.\n", err);
>> +        return err;
>> +    }
>> +
>> +    ctx->output_order = pic->encode_order;
>> +    hw_base_encode_clear_old(avctx);
>> +
>> +    /** loop to get an available pkt in encoder flushing. */
>> +    if (ctx->end_of_stream && !pkt->size)
>> +        goto start;
>> +
>> +end:
>> +    if (pkt->size)
>> +        av_log(avctx, AV_LOG_DEBUG, "Output packet: pts %"PRId64",
>dts %"PRId64", "
>> +               "size %d bytes.\n", pkt->pts, pkt->dts, pkt->size);
>> +
>> +    return 0;
>> +}
>> diff --git a/libavcodec/hw_base_encode.h b/libavcodec/hw_base_encode.h
>> index a578db8c06..b5b676b9a8 100644
>> --- a/libavcodec/hw_base_encode.h
>> +++ b/libavcodec/hw_base_encode.h
>> @@ -19,6 +19,8 @@
>>  #ifndef AVCODEC_HW_BASE_ENCODE_H
>>  #define AVCODEC_HW_BASE_ENCODE_H
>>
>> +#include "libavutil/fifo.h"
>> +
>>  #define MAX_DPB_SIZE 16
>>  #define MAX_PICTURE_REFERENCES 2
>>  #define MAX_REORDER_DELAY 16
>> @@ -53,13 +55,134 @@ enum {
>>      FLAG_NON_IDR_KEY_PICTURES  = 1 << 5,
>>  };
>>
>> +typedef struct HWBaseEncodePicture {
>> +    struct HWBaseEncodePicture *next;
>
>"next" does not seem like the right name for this field.

The field is copied from VAAPI and if we rename it more churn is brought.

>
>> +
>> +    int64_t         display_order;
>> +    int64_t         encode_order;
>> +    int64_t         pts;
>> +    int64_t         duration;
>> +    int             force_idr;
>> +
>> +    void           *opaque;
>> +    AVBufferRef    *opaque_ref;
>> +
>> +    int             type;
>> +    int             b_depth;
>> +    int             encode_issued;
>> +    int             encode_complete;
>> +
>> +    AVFrame        *input_image;
>> +    AVFrame        *recon_image;
>> +
>> +    void           *priv_data;
>> +
>> +    // Whether this picture is a reference picture.
>> +    int             is_reference;
>> +
>> +    // The contents of the DPB after this picture has been decoded.
>> +    // This will contain the picture itself if it is a reference picture,
>> +    // but not if it isn't.
>> +    int                     nb_dpb_pics;
>> +    struct HWBaseEncodePicture *dpb[MAX_DPB_SIZE];
>> +    // The reference pictures used in decoding this picture. If they are
>> +    // used by later pictures they will also appear in the DPB. ref[0][] for
>> +    // previous reference frames. ref[1][] for future reference frames.
>> +    int                     nb_refs[MAX_REFERENCE_LIST_NUM];
>> +    struct HWBaseEncodePicture
>*refs[MAX_REFERENCE_LIST_NUM][MAX_PICTURE_REFERENCES];
>> +    // The previous reference picture in encode order.  Must be in at least
>> +    // one of the reference list and DPB list.
>> +    struct HWBaseEncodePicture *prev;
>> +    // Reference count for other pictures referring to this one through
>> +    // the above pointers, directly from incomplete pictures and indirectly
>> +    // through completed pictures.
>> +    int             ref_count[2];
>> +    int             ref_removed[2];
>> +} HWBaseEncodePicture;
>> +
>> +typedef struct HWEncodePictureOperation {
>> +    // Alloc memory for the picture structure.
>
>Maybe something like "Allocate API-specific internals of a new picture structure
>based on the given frame."?
>
>(Assuming you move the allocation of the structure itself out as suggested
>above.)
>
>> +    HWBaseEncodePicture * (*alloc)(AVCodecContext *avctx, const AVFrame
>*frame);
>> +    // Issue the picture structure, which will send the frame surface to HW
>Encode API.
>> +    int (*issue)(AVCodecContext *avctx, const HWBaseEncodePicture
>*base_pic);
>> +    // Get the output AVPacket.
>> +    int (*output)(AVCodecContext *avctx, const HWBaseEncodePicture
>*base_pic, AVPacket *pkt);
>> +    // Free the picture structure.
>> +    int (*free)(AVCodecContext *avctx, HWBaseEncodePicture *base_pic);
>> +}  HWEncodePictureOperation;
>> +
>>  typedef struct HWBaseEncodeContext {
>>      const AVClass *class;
>>
>> +    // Hardware-specific hooks.
>> +    const struct HWEncodePictureOperation *op;
>> +
>> +    // Current encoding window, in display (input) order.
>> +    HWBaseEncodePicture *pic_start, *pic_end;
>> +    // The next picture to use as the previous reference picture in
>> +    // encoding order. Order from small to large in encoding order.
>> +    HWBaseEncodePicture *next_prev[MAX_PICTURE_REFERENCES];
>> +    int                  nb_next_prev;
>> +
>> +    // Next input order index (display order).
>> +    int64_t         input_order;
>> +    // Number of frames that output is behind input.
>> +    int64_t         output_delay;
>> +    // Next encode order index.
>> +    int64_t         encode_order;
>> +    // Number of frames decode output will need to be delayed.
>> +    int64_t         decode_delay;
>> +    // Next output order index (in encode order).
>> +    int64_t         output_order;
>> +
>> +    // Timestamp handling.
>> +    int64_t         first_pts;
>> +    int64_t         dts_pts_diff;
>> +    int64_t         ts_ring[MAX_REORDER_DELAY * 3 +
>> +                            MAX_ASYNC_DEPTH];
>> +
>> +    // Frame type decision.
>> +    int gop_size;
>> +    int closed_gop;
>> +    int gop_per_idr;
>> +    int p_per_i;
>> +    int max_b_depth;
>> +    int b_per_p;
>> +    int force_idr;
>> +    int idr_counter;
>> +    int gop_counter;
>> +    int end_of_stream;
>> +    int p_to_gpb;
>> +
>> +    // Whether the driver supports ROI at all.
>> +    int             roi_allowed;
>> +
>> +    // The encoder does not support cropping information, so warn about
>> +    // it the first time we encounter any nonzero crop fields.
>> +    int             crop_warned;
>> +    // If the driver does not support ROI then warn the first time we
>> +    // encounter a frame with ROI side data.
>> +    int             roi_warned;
>> +
>> +    // The frame to be filled with data.
>> +    AVFrame         *frame;
>> +
>> +    // Whether the HW supports sync buffer function.
>> +    // If supported, encode_fifo/async_depth will be used together.
>> +    // Used for output buffer synchronization.
>> +    int             async_encode;
>> +
>> +    // Store buffered pic.
>> +    AVFifo          *encode_fifo;
>>      // Max number of frame buffered in encoder.
>>      int             async_depth;
>> +
>> +    /** Tail data of a pic, now only used for av1 repeat frame header. */
>> +    AVPacket        *tail_pkt;
>>  } HWBaseEncodeContext;
>>
>> +int ff_hw_base_encode_receive_packet(AVCodecContext *avctx, AVPacket
>*pkt);
>> +
>>  #define HW_BASE_ENCODE_COMMON_OPTIONS \
>>      { "async_depth", "Maximum processing parallelism. " \
>>        "Increase this to improve single channel performance.", \
>> diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
>> index 227cccae64..18966596e1 100644
>> --- a/libavcodec/vaapi_encode.c
>> +++ b/libavcodec/vaapi_encode.c
>> @@ -138,22 +138,26 @@ static int
>vaapi_encode_make_misc_param_buffer(AVCodecContext *avctx,
>>  static int vaapi_encode_wait(AVCodecContext *avctx,
>>                               VAAPIEncodePicture *pic)
>>  {
>> +#if VA_CHECK_VERSION(1, 9, 0)
>> +    HWBaseEncodeContext *base_ctx = avctx->priv_data;
>> +#endif
>>      VAAPIEncodeContext *ctx = avctx->priv_data;
>> +    HWBaseEncodePicture *base_pic = (HWBaseEncodePicture*)pic;
>
>Use "base_foo = &foo->base_field" instead to avoid overriding the type-
>checking.
>
>(Also in move places below.)

OK.

>
>>      VAStatus vas;
>>
>> -    av_assert0(pic->encode_issued);
>> +    av_assert0(base_pic->encode_issued);
>>
>> -    if (pic->encode_complete) {
>> +    if (base_pic->encode_complete) {
>>          // Already waited for this picture.
>>          return 0;
>>      }
>>
>>      av_log(avctx, AV_LOG_DEBUG, "Sync to pic %"PRId64"/%"PRId64" "
>> -           "(input surface %#x).\n", pic->display_order,
>> -           pic->encode_order, pic->input_surface);
>> +           "(input surface %#x).\n", base_pic->display_order,
>> +           base_pic->encode_order, pic->input_surface);
>>
>>  #if VA_CHECK_VERSION(1, 9, 0)
>> -    if (ctx->has_sync_buffer_func) {
>> +    if (base_ctx->async_encode) {
>>          vas = vaSyncBuffer(ctx->hwctx->display,
>>                             pic->output_buffer,
>>                             VA_TIMEOUT_INFINITE);
>> @@ -174,9 +178,9 @@ static int vaapi_encode_wait(AVCodecContext *avctx,
>>      }
>>
>>      // Input is definitely finished with now.
>> -    av_frame_free(&pic->input_image);
>> +    av_frame_free(&base_pic->input_image);
>>
>> -    pic->encode_complete = 1;
>> +    base_pic->encode_complete = 1;
>>      return 0;
>>  }
>>
>> @@ -263,9 +267,11 @@ static int
>vaapi_encode_make_tile_slice(AVCodecContext *avctx,
>>  }
>>
>>  static int vaapi_encode_issue(AVCodecContext *avctx,
>> -                              VAAPIEncodePicture *pic)
>> +                              const HWBaseEncodePicture *base_pic)
>>  {
>> -    VAAPIEncodeContext *ctx = avctx->priv_data;
>> +    HWBaseEncodeContext *base_ctx = avctx->priv_data;
>> +    VAAPIEncodeContext       *ctx = avctx->priv_data;
>> +    VAAPIEncodePicture *pic = (VAAPIEncodePicture*)base_pic;
>>      VAAPIEncodeSlice *slice;
>>      VAStatus vas;
>>      int err, i;
>> @@ -274,52 +280,46 @@ static int vaapi_encode_issue(AVCodecContext
>*avctx,
>>      av_unused AVFrameSideData *sd;
>>
>>      av_log(avctx, AV_LOG_DEBUG, "Issuing encode for
>pic %"PRId64"/%"PRId64" "
>> -           "as type %s.\n", pic->display_order, pic->encode_order,
>> -           ff_hw_base_encode_get_pictype_name(pic->type));
>> -    if (pic->nb_refs[0] == 0 && pic->nb_refs[1] == 0) {
>> +           "as type %s.\n", base_pic->display_order, base_pic->encode_order,
>> +           ff_hw_base_encode_get_pictype_name(base_pic->type));
>> +    if (base_pic->nb_refs[0] == 0 && base_pic->nb_refs[1] == 0) {
>>          av_log(avctx, AV_LOG_DEBUG, "No reference pictures.\n");
>>      } else {
>>          av_log(avctx, AV_LOG_DEBUG, "L0 refers to");
>> -        for (i = 0; i < pic->nb_refs[0]; i++) {
>> +        for (i = 0; i < base_pic->nb_refs[0]; i++) {
>>              av_log(avctx, AV_LOG_DEBUG, " %"PRId64"/%"PRId64,
>> -                   pic->refs[0][i]->display_order, pic->refs[0][i]->encode_order);
>> +                   base_pic->refs[0][i]->display_order, base_pic->refs[0][i]-
>>encode_order);
>>          }
>>          av_log(avctx, AV_LOG_DEBUG, ".\n");
>>
>> -        if (pic->nb_refs[1]) {
>> +        if (base_pic->nb_refs[1]) {
>>              av_log(avctx, AV_LOG_DEBUG, "L1 refers to");
>> -            for (i = 0; i < pic->nb_refs[1]; i++) {
>> +            for (i = 0; i < base_pic->nb_refs[1]; i++) {
>>                  av_log(avctx, AV_LOG_DEBUG, " %"PRId64"/%"PRId64,
>> -                       pic->refs[1][i]->display_order, pic->refs[1][i]->encode_order);
>> +                       base_pic->refs[1][i]->display_order, base_pic->refs[1][i]-
>>encode_order);
>>              }
>>              av_log(avctx, AV_LOG_DEBUG, ".\n");
>>          }
>>      }
>>
>> -    av_assert0(!pic->encode_issued);
>> -    for (i = 0; i < pic->nb_refs[0]; i++) {
>> -        av_assert0(pic->refs[0][i]);
>> -        av_assert0(pic->refs[0][i]->encode_issued);
>> +    av_assert0(!base_pic->encode_issued);
>> +    for (i = 0; i < base_pic->nb_refs[0]; i++) {
>> +        av_assert0(base_pic->refs[0][i]);
>> +        av_assert0(base_pic->refs[0][i]->encode_issued);
>>      }
>> -    for (i = 0; i < pic->nb_refs[1]; i++) {
>> -        av_assert0(pic->refs[1][i]);
>> -        av_assert0(pic->refs[1][i]->encode_issued);
>> +    for (i = 0; i < base_pic->nb_refs[1]; i++) {
>> +        av_assert0(base_pic->refs[1][i]);
>> +        av_assert0(base_pic->refs[1][i]->encode_issued);
>>      }
>>
>>      av_log(avctx, AV_LOG_DEBUG, "Input surface is %#x.\n", pic-
>>input_surface);
>>
>> -    pic->recon_image = av_frame_alloc();
>> -    if (!pic->recon_image) {
>> -        err = AVERROR(ENOMEM);
>> -        goto fail;
>> -    }
>> -
>> -    err = av_hwframe_get_buffer(ctx->recon_frames_ref, pic->recon_image,
>0);
>> +    err = av_hwframe_get_buffer(ctx->recon_frames_ref, base_pic-
>>recon_image, 0);
>>      if (err < 0) {
>>          err = AVERROR(ENOMEM);
>>          goto fail;
>>      }
>> -    pic->recon_surface = (VASurfaceID)(uintptr_t)pic->recon_image->data[3];
>> +    pic->recon_surface = (VASurfaceID)(uintptr_t)base_pic->recon_image-
>>data[3];
>>      av_log(avctx, AV_LOG_DEBUG, "Recon surface is %#x.\n", pic-
>>recon_surface);
>>
>>      pic->output_buffer_ref = ff_refstruct_pool_get(ctx->output_buffer_pool);
>> @@ -343,7 +343,7 @@ static int vaapi_encode_issue(AVCodecContext
>*avctx,
>>
>>      pic->nb_param_buffers = 0;
>>
>> -    if (pic->type == PICTURE_TYPE_IDR && ctx->codec-
>>init_sequence_params) {
>> +    if (base_pic->type == PICTURE_TYPE_IDR && ctx->codec-
>>init_sequence_params) {
>>          err = vaapi_encode_make_param_buffer(avctx, pic,
>>                                               VAEncSequenceParameterBufferType,
>>                                               ctx->codec_sequence_params,
>> @@ -352,7 +352,7 @@ static int vaapi_encode_issue(AVCodecContext
>*avctx,
>>              goto fail;
>>      }
>>
>> -    if (pic->type == PICTURE_TYPE_IDR) {
>> +    if (base_pic->type == PICTURE_TYPE_IDR) {
>>          for (i = 0; i < ctx->nb_global_params; i++) {
>>              err = vaapi_encode_make_misc_param_buffer(avctx, pic,
>>                                                        ctx->global_params_type[i],
>> @@ -389,7 +389,7 @@ static int vaapi_encode_issue(AVCodecContext
>*avctx,
>>      }
>>  #endif
>>
>> -    if (pic->type == PICTURE_TYPE_IDR) {
>> +    if (base_pic->type == PICTURE_TYPE_IDR) {
>>          if (ctx->va_packed_headers & VA_ENC_PACKED_HEADER_SEQUENCE
>&&
>>              ctx->codec->write_sequence_header) {
>>              bit_len = 8 * sizeof(data);
>> @@ -529,9 +529,9 @@ static int vaapi_encode_issue(AVCodecContext
>*avctx,
>>      }
>>
>>  #if VA_CHECK_VERSION(1, 0, 0)
>> -    sd = av_frame_get_side_data(pic->input_image,
>> +    sd = av_frame_get_side_data(base_pic->input_image,
>>                                  AV_FRAME_DATA_REGIONS_OF_INTEREST);
>> -    if (sd && ctx->roi_allowed) {
>> +    if (sd && base_ctx->roi_allowed) {
>>          const AVRegionOfInterest *roi;
>>          uint32_t roi_size;
>>          VAEncMiscParameterBufferROI param_roi;
>> @@ -542,11 +542,11 @@ static int vaapi_encode_issue(AVCodecContext
>*avctx,
>>          av_assert0(roi_size && sd->size % roi_size == 0);
>>          nb_roi = sd->size / roi_size;
>>          if (nb_roi > ctx->roi_max_regions) {
>> -            if (!ctx->roi_warned) {
>> +            if (!base_ctx->roi_warned) {
>>                  av_log(avctx, AV_LOG_WARNING, "More ROIs set than "
>>                         "supported by driver (%d > %d).\n",
>>                         nb_roi, ctx->roi_max_regions);
>> -                ctx->roi_warned = 1;
>> +                base_ctx->roi_warned = 1;
>>              }
>>              nb_roi = ctx->roi_max_regions;
>>          }
>> @@ -639,8 +639,6 @@ static int vaapi_encode_issue(AVCodecContext
>*avctx,
>>          }
>>      }
>>
>> -    pic->encode_issued = 1;
>> -
>>      return 0;
>>
>>  fail_with_picture:
>> @@ -657,14 +655,13 @@ fail_at_end:
>>      av_freep(&pic->param_buffers);
>>      av_freep(&pic->slices);
>>      av_freep(&pic->roi);
>> -    av_frame_free(&pic->recon_image);
>>      ff_refstruct_unref(&pic->output_buffer_ref);
>>      pic->output_buffer = VA_INVALID_ID;
>>      return err;
>>  }
>
>All the churn in this function makes me wonder whether it would be better to
>have VAAPIEncodePicture *vaapi_pic and HWBaseEncodePicture *pic to avoid
>it?  (Perhaps not given that it uses both.)

If so it will also bring renaming in a different way and seems not any better. ☹ I think it's not avoidable.

>
>> ...
>> diff --git a/libavcodec/vaapi_encode_av1.c b/libavcodec/vaapi_encode_av1.c
>> index f4b4586583..393b479f99 100644
>> --- a/libavcodec/vaapi_encode_av1.c
>> +++ b/libavcodec/vaapi_encode_av1.c
>> @@ -357,6 +357,7 @@ static int
>vaapi_encode_av1_write_sequence_header(AVCodecContext *avctx,
>>
>>  static int vaapi_encode_av1_init_sequence_params(AVCodecContext *avctx)
>>  {
>> +    HWBaseEncodeContext         *base_ctx = avctx->priv_data;
>>      VAAPIEncodeContext               *ctx = avctx->priv_data;
>>      VAAPIEncodeAV1Context           *priv = avctx->priv_data;
>>      AV1RawOBU                     *sh_obu = &priv->sh;
>> @@ -438,8 +439,8 @@ static int
>vaapi_encode_av1_init_sequence_params(AVCodecContext *avctx)
>>      vseq->seq_level_idx           = sh->seq_level_idx[0];
>>      vseq->seq_tier                = sh->seq_tier[0];
>>      vseq->order_hint_bits_minus_1 = sh->order_hint_bits_minus_1;
>> -    vseq->intra_period            = ctx->gop_size;
>> -    vseq->ip_period               = ctx->b_per_p + 1;
>> +    vseq->intra_period            = base_ctx->gop_size;
>> +    vseq->ip_period               = base_ctx->b_per_p + 1;
>>
>>      vseq->seq_fields.bits.enable_order_hint = sh->enable_order_hint;
>>
>> @@ -466,12 +467,13 @@ static int
>vaapi_encode_av1_init_picture_params(AVCodecContext *avctx,
>>  {
>>      VAAPIEncodeContext              *ctx = avctx->priv_data;
>>      VAAPIEncodeAV1Context          *priv = avctx->priv_data;
>> -    VAAPIEncodeAV1Picture          *hpic = pic->priv_data;
>> +    HWBaseEncodePicture        *base_pic = (HWBaseEncodePicture *)pic;
>
>base_pic is const here?

OK.

>
>Also consider whether this naming is the best way around.
>
>> +    VAAPIEncodeAV1Picture          *hpic = base_pic->priv_data;
>>      AV1RawOBU                    *fh_obu = &priv->fh;
>>      AV1RawFrameHeader                *fh = &fh_obu->obu.frame.header;
>>      VAEncPictureParameterBufferAV1 *vpic = pic->codec_picture_params;
>>      CodedBitstreamFragment          *obu = &priv->current_obu;
>> -    VAAPIEncodePicture    *ref;
>> +    HWBaseEncodePicture    *ref;
>>      VAAPIEncodeAV1Picture *href;
>>      int slot, i;
>>      int ret;
>> @@ -480,24 +482,24 @@ static int
>vaapi_encode_av1_init_picture_params(AVCodecContext *avctx,
>>
>>      memset(fh_obu, 0, sizeof(*fh_obu));
>>      pic->nb_slices = priv->tile_groups;
>> -    pic->non_independent_frame = pic->encode_order < pic->display_order;
>> +    pic->non_independent_frame = base_pic->encode_order < base_pic-
>>display_order;
>>      fh_obu->header.obu_type = AV1_OBU_FRAME_HEADER;
>>      fh_obu->header.obu_has_size_field = 1;
>>
>> -    switch (pic->type) {
>> +    switch (base_pic->type) {
>>      case PICTURE_TYPE_IDR:
>> -        av_assert0(pic->nb_refs[0] == 0 || pic->nb_refs[1]);
>> +        av_assert0(base_pic->nb_refs[0] == 0 || base_pic->nb_refs[1]);
>>          fh->frame_type = AV1_FRAME_KEY;
>>          fh->refresh_frame_flags = 0xFF;
>>          fh->base_q_idx = priv->q_idx_idr;
>>          hpic->slot = 0;
>> -        hpic->last_idr_frame = pic->display_order;
>> +        hpic->last_idr_frame = base_pic->display_order;
>>          break;
>>      case PICTURE_TYPE_P:
>> -        av_assert0(pic->nb_refs[0]);
>> +        av_assert0(base_pic->nb_refs[0]);
>>          fh->frame_type = AV1_FRAME_INTER;
>>          fh->base_q_idx = priv->q_idx_p;
>> -        ref = pic->refs[0][pic->nb_refs[0] - 1];
>> +        ref = base_pic->refs[0][base_pic->nb_refs[0] - 1];
>>          href = ref->priv_data;
>>          hpic->slot = !href->slot;
>>          hpic->last_idr_frame = href->last_idr_frame;
>> @@ -512,8 +514,8 @@ static int
>vaapi_encode_av1_init_picture_params(AVCodecContext *avctx,
>>          vpic->ref_frame_ctrl_l0.fields.search_idx0 = AV1_REF_FRAME_LAST;
>>
>>          /** set the 2nd nearest frame in L0 as Golden frame. */
>> -        if (pic->nb_refs[0] > 1) {
>> -            ref = pic->refs[0][pic->nb_refs[0] - 2];
>> +        if (base_pic->nb_refs[0] > 1) {
>> +            ref = base_pic->refs[0][base_pic->nb_refs[0] - 2];
>>              href = ref->priv_data;
>>              fh->ref_frame_idx[3] = href->slot;
>>              fh->ref_order_hint[href->slot] = ref->display_order - href-
>>last_idr_frame;
>> @@ -521,7 +523,7 @@ static int
>vaapi_encode_av1_init_picture_params(AVCodecContext *avctx,
>>          }
>>          break;
>>      case PICTURE_TYPE_B:
>> -        av_assert0(pic->nb_refs[0] && pic->nb_refs[1]);
>> +        av_assert0(base_pic->nb_refs[0] && base_pic->nb_refs[1]);
>>          fh->frame_type = AV1_FRAME_INTER;
>>          fh->base_q_idx = priv->q_idx_b;
>>          fh->refresh_frame_flags = 0x0;
>> @@ -534,7 +536,7 @@ static int
>vaapi_encode_av1_init_picture_params(AVCodecContext *avctx,
>>          vpic->ref_frame_ctrl_l0.fields.search_idx0 = AV1_REF_FRAME_LAST;
>>          vpic->ref_frame_ctrl_l1.fields.search_idx0 = AV1_REF_FRAME_BWDREF;
>>
>> -        ref                            = pic->refs[0][pic->nb_refs[0] - 1];
>> +        ref                            = base_pic->refs[0][base_pic->nb_refs[0] - 1];
>>          href                           = ref->priv_data;
>>          hpic->last_idr_frame           = href->last_idr_frame;
>>          fh->primary_ref_frame          = href->slot;
>> @@ -543,7 +545,7 @@ static int
>vaapi_encode_av1_init_picture_params(AVCodecContext *avctx,
>>              fh->ref_frame_idx[i] = href->slot;
>>          }
>>
>> -        ref                            = pic->refs[1][pic->nb_refs[1] - 1];
>> +        ref                            = base_pic->refs[1][base_pic->nb_refs[1] - 1];
>>          href                           = ref->priv_data;
>>          fh->ref_order_hint[href->slot] = ref->display_order - href-
>>last_idr_frame;
>>          for (i = AV1_REF_FRAME_GOLDEN; i < AV1_REFS_PER_FRAME; i++) {
>> @@ -554,13 +556,13 @@ static int
>vaapi_encode_av1_init_picture_params(AVCodecContext *avctx,
>>          av_assert0(0 && "invalid picture type");
>>      }
>>
>> -    fh->show_frame                = pic->display_order <= pic->encode_order;
>> +    fh->show_frame                = base_pic->display_order <= base_pic-
>>encode_order;
>>      fh->showable_frame            = fh->frame_type != AV1_FRAME_KEY;
>>      fh->frame_width_minus_1       = avctx->width - 1;
>>      fh->frame_height_minus_1      = avctx->height - 1;
>>      fh->render_width_minus_1      = fh->frame_width_minus_1;
>>      fh->render_height_minus_1     = fh->frame_height_minus_1;
>> -    fh->order_hint                = pic->display_order - hpic->last_idr_frame;
>> +    fh->order_hint                = base_pic->display_order - hpic->last_idr_frame;
>>      fh->tile_cols                 = priv->tile_cols;
>>      fh->tile_rows                 = priv->tile_rows;
>>      fh->tile_cols_log2            = priv->tile_cols_log2;
>> @@ -626,13 +628,13 @@ static int
>vaapi_encode_av1_init_picture_params(AVCodecContext *avctx,
>>          vpic->reference_frames[i] = VA_INVALID_SURFACE;
>>
>>      for (i = 0; i < MAX_REFERENCE_LIST_NUM; i++) {
>> -        for (int j = 0; j < pic->nb_refs[i]; j++) {
>> -            VAAPIEncodePicture *ref_pic = pic->refs[i][j];
>> +        for (int j = 0; j < base_pic->nb_refs[i]; j++) {
>> +            HWBaseEncodePicture *ref_pic = base_pic->refs[i][j];
>>
>>              slot = ((VAAPIEncodeAV1Picture*)ref_pic->priv_data)->slot;
>>              av_assert0(vpic->reference_frames[slot] == VA_INVALID_SURFACE);
>>
>> -            vpic->reference_frames[slot] = ref_pic->recon_surface;
>> +            vpic->reference_frames[slot] = ((VAAPIEncodePicture *)ref_pic)-
>>recon_surface;
>>          }
>>      }
>>
>> @@ -653,7 +655,7 @@ static int
>vaapi_encode_av1_init_picture_params(AVCodecContext *avctx,
>>          vpic->bit_offset_cdef_params         = priv->cdef_start_offset;
>>          vpic->size_in_bits_cdef_params       = priv->cdef_param_size;
>>          vpic->size_in_bits_frame_hdr_obu     = priv->fh_data_len;
>> -        vpic->byte_offset_frame_hdr_obu_size = (((pic->type ==
>PICTURE_TYPE_IDR) ?
>> +        vpic->byte_offset_frame_hdr_obu_size = (((base_pic->type ==
>PICTURE_TYPE_IDR) ?
>>                                                 priv->sh_data_len / 8 : 0) +
>>                                                 (fh_obu->header.obu_extension_flag ?
>>                                                 2 : 1));
>> @@ -695,14 +697,15 @@ static int
>vaapi_encode_av1_write_picture_header(AVCodecContext *avctx,
>>      CodedBitstreamAV1Context *cbctx = priv->cbc->priv_data;
>>      AV1RawOBU               *fh_obu = &priv->fh;
>>      AV1RawFrameHeader       *rep_fh = &fh_obu->obu.frame_header;
>> +    HWBaseEncodePicture   *base_pic = (HWBaseEncodePicture *)pic;
>
>Also const.
>
>>      VAAPIEncodeAV1Picture *href;
>>      int ret = 0;
>>
>>      pic->tail_size = 0;
>>      /** Pack repeat frame header. */
>> -    if (pic->display_order > pic->encode_order) {
>> +    if (base_pic->display_order > base_pic->encode_order) {
>>          memset(fh_obu, 0, sizeof(*fh_obu));
>> -        href = pic->refs[0][pic->nb_refs[0] - 1]->priv_data;
>> +        href = base_pic->refs[0][base_pic->nb_refs[0] - 1]->priv_data;
>>          fh_obu->header.obu_type = AV1_OBU_FRAME_HEADER;
>>          fh_obu->header.obu_has_size_field = 1;
>>
>> @@ -937,7 +940,7 @@ const FFCodec ff_av1_vaapi_encoder = {
>>      .p.id           = AV_CODEC_ID_AV1,
>>      .priv_data_size = sizeof(VAAPIEncodeAV1Context),
>>      .init           = &vaapi_encode_av1_init,
>> -    FF_CODEC_RECEIVE_PACKET_CB(&ff_vaapi_encode_receive_packet),
>> +    FF_CODEC_RECEIVE_PACKET_CB(&ff_hw_base_encode_receive_packet),
>>      .close          = &vaapi_encode_av1_close,
>>      .p.priv_class   = &vaapi_encode_av1_class,
>>      .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE |
>> ...
>
>I didn't look carefully through the other codecs here.

Thanks for the review.

-Tong


More information about the ffmpeg-devel mailing list