[FFmpeg-devel] [PATCH v2] avcodec: add a get_encoder_buffer() callback to AVCodecContext

Lynne dev at lynne.ee
Sun Feb 21 19:59:14 EET 2021


Feb 21, 2021, 18:35 by jamrial at gmail.com:

> This callback is functionally the same as get_buffer2() is for decoders, and
> implements for the new encode API the functionality of the old encode API had
> where the user could provide their own buffers.
>
> Signed-off-by: James Almer <jamrial at gmail.com>
> ---
> Used the names Lynne suggested this time, plus a line about how the callback
> must be thread safe.
>
>  libavcodec/avcodec.h | 45 ++++++++++++++++++++++++++++++++++++
>  libavcodec/codec.h   |  8 ++++---
>  libavcodec/encode.c  | 54 +++++++++++++++++++++++++++++++++++++++++++-
>  libavcodec/encode.h  |  8 +++++++
>  libavcodec/options.c |  1 +
>  5 files changed, 112 insertions(+), 4 deletions(-)
>
> diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
> index 7dbf083a24..e60eb16ce1 100644
> --- a/libavcodec/avcodec.h
> +++ b/libavcodec/avcodec.h
> @@ -513,6 +513,11 @@ typedef struct AVProducerReferenceTime {
>  */
>  #define AV_GET_BUFFER_FLAG_REF (1 << 0)
>  
> +/**
> + * The encoder will keep a reference to the packet and may reuse it later.
> + */
> +#define AV_GET_ENCODER_BUFFER_FLAG_REF (1 << 0)
> +
>  struct AVCodecInternal;
>  
>  /**
> @@ -2346,6 +2351,39 @@ typedef struct AVCodecContext {
>  * - encoding: set by user
>  */
>  int export_side_data;
> +
> +    /**
> +     * This callback is called at the beginning of each packet to get a data
> +     * buffer for it.
> +     *
> +     * The following field will be set in the packet before this callback is
> +     * called:
> +     * - size
> +     * This callback must use the above value to calculate the required buffer size,
> +     * which must padded by at least AV_INPUT_BUFFER_PADDING_SIZE bytes.
> +     *
> +     * This callback must fill the following fields in the packet:
> +     * - data
> +     * - buf must contain a pointer to an AVBufferRef structure. The packet's
> +     *   data pointer must be contained in it.
> +     *   See: av_buffer_create(), av_buffer_alloc(), and av_buffer_ref().
> +     *
> +     * If AV_CODEC_CAP_DR1 is not set then get_encoder_buffer() must call
> +     * avcodec_default_get_encoder_buffer() instead of providing a buffer allocated by
> +     * some other means.
> +     *
> +     * If AV_GET_ENCODER_BUFFER_FLAG_REF is set in flags then the packet may be reused
> +     * (read and/or written to if it is writable) later by libavcodec.
> +     *
> +     * This callback must be thread-safe, as when frame multithreading is used, it may
> +     * be called from multiple threads simultaneously.
> +     *
> +     * @see avcodec_default_get_encoder_buffer()
> +     *
> +     * - encoding: Set by libavcodec, user can override.
> +     * - decoding: unused
> +     */
> +    int (*get_encoder_buffer)(struct AVCodecContext *s, AVPacket *pkt, int flags);
>  } AVCodecContext;
>  
>  #if FF_API_CODEC_GET_SET
> @@ -2920,6 +2958,13 @@ void avsubtitle_free(AVSubtitle *sub);
>  */
>  int avcodec_default_get_buffer2(AVCodecContext *s, AVFrame *frame, int flags);
>  
> +/**
> + * The default callback for AVCodecContext.get_encoder_buffer(). It is made public so
> + * it can be called by custom get_encoder_buffer() implementations for encoders without
> + * AV_CODEC_CAP_DR1 set.
> + */
> +int avcodec_default_get_encoder_buffer(AVCodecContext *s, AVPacket *pkt, int flags);
> +
>  /**
>  * Modify width and height values so that they will result in a memory
>  * buffer that is acceptable for the codec if you do not use any horizontal
> diff --git a/libavcodec/codec.h b/libavcodec/codec.h
> index 0ccbf0eb19..a679fdc9e1 100644
> --- a/libavcodec/codec.h
> +++ b/libavcodec/codec.h
> @@ -43,9 +43,11 @@
>  */
>  #define AV_CODEC_CAP_DRAW_HORIZ_BAND     (1 <<  0)
>  /**
> - * Codec uses get_buffer() for allocating buffers and supports custom allocators.
> - * If not set, it might not use get_buffer() at all or use operations that
> - * assume the buffer was allocated by avcodec_default_get_buffer.
> + * Codec uses get_buffer() or get_encoder_buffer() for allocating buffers and
> + * supports custom allocators.
> + * If not set, it might not use get_buffer() or get_encoder_buffer() at all, or
> + * use operations that assume the buffer was allocated by
> + * avcodec_default_get_buffer2 or avcodec_default_get_encoder_buffer.
>  */
>  #define AV_CODEC_CAP_DR1                 (1 <<  1)
>  #define AV_CODEC_CAP_TRUNCATED           (1 <<  3)
> diff --git a/libavcodec/encode.c b/libavcodec/encode.c
> index 282337e453..f39c8d38ce 100644
> --- a/libavcodec/encode.c
> +++ b/libavcodec/encode.c
> @@ -56,6 +56,52 @@ int ff_alloc_packet2(AVCodecContext *avctx, AVPacket *avpkt, int64_t size, int64
>  return 0;
>  }
>  
> +int avcodec_default_get_encoder_buffer(AVCodecContext *avctx, AVPacket *avpkt, int flags)
> +{
> +    int ret;
> +
> +    if (avpkt->data || avpkt->buf) {
> +        av_log(avctx, AV_LOG_ERROR, "avpkt->{data,buf} != NULL in avcodec_default_get_encoder_buffer()\n");
> +        return AVERROR(EINVAL);
> +    }
> +
> +    ret = av_new_packet(avpkt, avpkt->size);
> +    if (ret < 0)
> +        av_log(avctx, AV_LOG_ERROR, "Failed to allocate packet of size %d\n", avpkt->size);
> +
> +    return ret;
> +}
> +
> +int ff_get_encoder_buffer(AVCodecContext *avctx, AVPacket *avpkt, int64_t size, int flags)
> +{
> +    int ret;
> +
> +    if (size < 0 || size > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE)
> +        return AVERROR(EINVAL);
> +
> +    av_assert0(!avpkt->data && !avpkt->buf);
> +
> +    avpkt->size = size;
> +    ret = avctx->get_encoder_buffer(avctx, avpkt, flags);
> +    if (ret < 0)
> +        goto fail;
> +
> +    if (!avpkt->data || !avpkt->buf) {
> +        av_log(avctx, AV_LOG_ERROR, "No buffer returned by get_encoder_buffer()\n");
> +        ret = AVERROR(EINVAL);
> +        goto fail;
> +    }
> +
> +    ret = 0;
> +fail:
> +    if (ret < 0) {
> +        av_log(avctx, AV_LOG_ERROR, "get_encoder_buffer() failed\n");
> +        av_packet_unref(avpkt);
> +    }
> +
> +    return ret;
> +}
> +
>  /**
>  * Pad last frame with silence.
>  */
> @@ -169,7 +215,7 @@ static int encode_simple_internal(AVCodecContext *avctx, AVPacket *avpkt)
>  emms_c();
>  
>  if (!ret && got_packet) {
> -        if (avpkt->data) {
> +        if (avpkt->data && !(avctx->codec->capabilities & AV_CODEC_CAP_DR1)) {
>  ret = av_packet_make_refcounted(avpkt);
>  if (ret < 0)
>  goto end;
> @@ -377,6 +423,12 @@ static int compat_encode(AVCodecContext *avctx, AVPacket *avpkt,
>  av_log(avctx, AV_LOG_WARNING, "AVFrame.width or height is not set\n");
>  }
>  
> +    if (avctx->codec->capabilities & AV_CODEC_CAP_DR1) {
> +        av_log(avctx, AV_LOG_WARNING, "The deprecated avcodec_encode_* API does not support "
> +                                      "AV_CODEC_CAP_DR1 encoders\n");
> +        return AVERROR(ENOSYS);
> +    }
> +
>  ret = avcodec_send_frame(avctx, frame);
>  if (ret == AVERROR_EOF)
>  ret = 0;
> diff --git a/libavcodec/encode.h b/libavcodec/encode.h
> index dfa9cb2d97..3192bd9e38 100644
> --- a/libavcodec/encode.h
> +++ b/libavcodec/encode.h
> @@ -24,6 +24,7 @@
>  #include "libavutil/frame.h"
>  
>  #include "avcodec.h"
> +#include "packet.h"
>  
>  /**
>  * Called by encoders to get the next frame for encoding.
> @@ -36,4 +37,11 @@
>  */
>  int ff_encode_get_frame(AVCodecContext *avctx, AVFrame *frame);
>  
> +/**
> + * Get a buffer for a packet. This is a wrapper around
> + * AVCodecContext.get_encoder_buffer() and should be used instead calling get_encoder_buffer()
> + * directly.
> + */
> +int ff_get_encoder_buffer(AVCodecContext *avctx, AVPacket *avpkt, int64_t size, int flags);
> +
>  #endif /* AVCODEC_ENCODE_H */
> diff --git a/libavcodec/options.c b/libavcodec/options.c
> index 4bbf74ec7f..cd5fa6eb14 100644
> --- a/libavcodec/options.c
> +++ b/libavcodec/options.c
> @@ -130,6 +130,7 @@ static int init_context_defaults(AVCodecContext *s, const AVCodec *codec)
>  s->pkt_timebase        = (AVRational){ 0, 1 };
>  s->get_buffer2         = avcodec_default_get_buffer2;
>  s->get_format          = avcodec_default_get_format;
> +    s->get_encoder_buffer  = avcodec_default_get_encoder_buffer;
>  s->execute             = avcodec_default_execute;
>  s->execute2            = avcodec_default_execute2;
>  s->sample_aspect_ratio = (AVRational){0,1};
> -- 
> 2.30.0
>
> _______________________________________________
> 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".
>

I thought I suggested "encode", but I typed "encoder", sorry :(
"encoder" looks worse. It's a nit, you don't have to send a new patch for it,
but could you change everything to "encode" sometime before you push it?
Thanks.

With that change patch LGTM.


More information about the ffmpeg-devel mailing list