[FFmpeg-devel] [PATCH v2] avcodec: add a get_encoder_buffer() callback to AVCodecContext
James Almer
jamrial at gmail.com
Sun Feb 21 19:35:12 EET 2021
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
More information about the ffmpeg-devel
mailing list