diff mbox series

[FFmpeg-devel,2/3,v2] avcodec/encode: restructure the old encode API

Message ID 20200316213002.46612-3-jamrial@gmail.com
State Superseded
Headers show
Series Restructuring the encode API
Related show

Checks

Context Check Description
andriy/ffmpeg-patchwork pending
andriy/ffmpeg-patchwork success Applied patch
andriy/ffmpeg-patchwork success Configure finished
andriy/ffmpeg-patchwork fail Make failed

Commit Message

James Almer March 16, 2020, 9:30 p.m. UTC
Following the same logic as 061a0c14bb, this commit turns the old encode API
into a wrapper for the new one.

Signed-off-by: James Almer <jamrial@gmail.com>
---
This could be squashed with the previous commit, like it was done in 061a0c14bb,
but i figured it would be easier to review this way.

 libavcodec/encode.c   | 364 +++++++++++++-----------------------------
 libavcodec/internal.h |   1 +
 libavcodec/utils.c    |   8 +
 3 files changed, 116 insertions(+), 257 deletions(-)

Comments

Anton Khirnov March 26, 2020, 8:57 a.m. UTC | #1
Quoting James Almer (2020-03-16 22:30:01)
> Following the same logic as 061a0c14bb, this commit turns the old encode API
> into a wrapper for the new one.
> 
> Signed-off-by: James Almer <jamrial@gmail.com>
> ---
> This could be squashed with the previous commit, like it was done in 061a0c14bb,
> but i figured it would be easier to review this way.
> 
>  libavcodec/encode.c   | 364 +++++++++++++-----------------------------
>  libavcodec/internal.h |   1 +
>  libavcodec/utils.c    |   8 +
>  3 files changed, 116 insertions(+), 257 deletions(-)
> 
> diff --git a/libavcodec/encode.c b/libavcodec/encode.c
> index cdea1c6c1e..0fdb9e2df2 100644
> --- a/libavcodec/encode.c
> +++ b/libavcodec/encode.c
> @@ -610,3 +361,102 @@ int attribute_align_arg avcodec_receive_packet(AVCodecContext *avctx, AVPacket *
>  
>      return 0;
>  }
> +
> +static int compat_encode(AVCodecContext *avctx, AVPacket *avpkt,
> +                         int *got_packet, const AVFrame *frame)
> +{
> +    AVCodecInternal *avci = avctx->internal;
> +    AVPacket user_pkt;
> +    int ret;
> +
> +    *got_packet = 0;
> +
> +    if (frame && avctx->codec->type == AVMEDIA_TYPE_VIDEO) {
> +        if (frame->format == AV_PIX_FMT_NONE)
> +            av_log(avctx, AV_LOG_WARNING, "AVFrame.format is not set\n");
> +        if (frame->width == 0 || frame->height == 0)
> +            av_log(avctx, AV_LOG_WARNING, "AVFrame.width or height is not set\n");
> +    }
> +
> +    ret = avcodec_send_frame(avctx, frame);
> +    if (ret == AVERROR_EOF)
> +        ret = 0;
> +    else if (ret == AVERROR(EAGAIN)) {
> +        /* we fully drain all the output in each encode call, so this should not
> +         * ever happen */
> +        return AVERROR_BUG;
> +    } else if (ret < 0)
> +        return ret;
> +
> +    av_packet_move_ref(&user_pkt, avpkt);
> +    while (ret >= 0) {
> +        ret = avcodec_receive_packet(avctx, avpkt);
> +        if (ret < 0) {
> +            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
> +                ret = 0;
> +            goto finish;
> +        }
> +
> +        if (avpkt != avci->compat_encode_packet) {
> +            if (avpkt->data && user_pkt.data) {
> +                if (user_pkt.size >= avpkt->size) {
> +                    memcpy(user_pkt.data, avpkt->data, avpkt->size);
> +                    av_buffer_unref(&avpkt->buf);
> +                    avpkt->buf  = user_pkt.buf;
> +                    avpkt->data = user_pkt.data;
> +                    av_init_packet(&user_pkt);
> +                } else {
> +                    av_log(avctx, AV_LOG_ERROR, "Provided packet is too small, needs to be %d\n", avpkt->size);
> +                    ret = AVERROR(EINVAL);
> +                    goto finish;

Shouldn't the packet be unreffed somewhere in this block?

> +                }
> +            }
> +
> +            *got_packet = 1;
> +            avpkt = avci->compat_encode_packet;
> +        } else {
> +            if (!avci->compat_decode_warned) {
> +                av_log(avctx, AV_LOG_WARNING, "The deprecated avcodec_encode_* "
> +                       "API cannot return all the packets for this encoder. "
> +                       "Some packets will be dropped. Update your code to the "
> +                       "new encoding API to fix this.\n");
> +                avci->compat_decode_warned = 1;
> +            }

Same.

Beyond those two apparent leaks, looks good.
James Almer March 26, 2020, 7:28 p.m. UTC | #2
On 3/26/2020 5:57 AM, Anton Khirnov wrote:
> Quoting James Almer (2020-03-16 22:30:01)
>> Following the same logic as 061a0c14bb, this commit turns the old encode API
>> into a wrapper for the new one.
>>
>> Signed-off-by: James Almer <jamrial@gmail.com>
>> ---
>> This could be squashed with the previous commit, like it was done in 061a0c14bb,
>> but i figured it would be easier to review this way.
>>
>>  libavcodec/encode.c   | 364 +++++++++++++-----------------------------
>>  libavcodec/internal.h |   1 +
>>  libavcodec/utils.c    |   8 +
>>  3 files changed, 116 insertions(+), 257 deletions(-)
>>
>> diff --git a/libavcodec/encode.c b/libavcodec/encode.c
>> index cdea1c6c1e..0fdb9e2df2 100644
>> --- a/libavcodec/encode.c
>> +++ b/libavcodec/encode.c
>> @@ -610,3 +361,102 @@ int attribute_align_arg avcodec_receive_packet(AVCodecContext *avctx, AVPacket *
>>  
>>      return 0;
>>  }
>> +
>> +static int compat_encode(AVCodecContext *avctx, AVPacket *avpkt,
>> +                         int *got_packet, const AVFrame *frame)
>> +{
>> +    AVCodecInternal *avci = avctx->internal;
>> +    AVPacket user_pkt;
>> +    int ret;
>> +
>> +    *got_packet = 0;
>> +
>> +    if (frame && avctx->codec->type == AVMEDIA_TYPE_VIDEO) {
>> +        if (frame->format == AV_PIX_FMT_NONE)
>> +            av_log(avctx, AV_LOG_WARNING, "AVFrame.format is not set\n");
>> +        if (frame->width == 0 || frame->height == 0)
>> +            av_log(avctx, AV_LOG_WARNING, "AVFrame.width or height is not set\n");
>> +    }
>> +
>> +    ret = avcodec_send_frame(avctx, frame);
>> +    if (ret == AVERROR_EOF)
>> +        ret = 0;
>> +    else if (ret == AVERROR(EAGAIN)) {
>> +        /* we fully drain all the output in each encode call, so this should not
>> +         * ever happen */
>> +        return AVERROR_BUG;
>> +    } else if (ret < 0)
>> +        return ret;
>> +
>> +    av_packet_move_ref(&user_pkt, avpkt);
>> +    while (ret >= 0) {
>> +        ret = avcodec_receive_packet(avctx, avpkt);
>> +        if (ret < 0) {
>> +            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
>> +                ret = 0;
>> +            goto finish;
>> +        }
>> +
>> +        if (avpkt != avci->compat_encode_packet) {
>> +            if (avpkt->data && user_pkt.data) {
>> +                if (user_pkt.size >= avpkt->size) {
>> +                    memcpy(user_pkt.data, avpkt->data, avpkt->size);
>> +                    av_buffer_unref(&avpkt->buf);
>> +                    avpkt->buf  = user_pkt.buf;
>> +                    avpkt->data = user_pkt.data;
>> +                    av_init_packet(&user_pkt);
>> +                } else {
>> +                    av_log(avctx, AV_LOG_ERROR, "Provided packet is too small, needs to be %d\n", avpkt->size);
>> +                    ret = AVERROR(EINVAL);
>> +                    goto finish;
> 
> Shouldn't the packet be unreffed somewhere in this block?

Yeah. Fixed locally.

> 
>> +                }
>> +            }
>> +
>> +            *got_packet = 1;
>> +            avpkt = avci->compat_encode_packet;
>> +        } else {
>> +            if (!avci->compat_decode_warned) {
>> +                av_log(avctx, AV_LOG_WARNING, "The deprecated avcodec_encode_* "
>> +                       "API cannot return all the packets for this encoder. "
>> +                       "Some packets will be dropped. Update your code to the "
>> +                       "new encoding API to fix this.\n");
>> +                avci->compat_decode_warned = 1;
>> +            }
> 
> Same.

avpkt is avci->compat_encode_packet in this block. It will either be
unreffed next time avcodec_receive_packet() is called with it, or by
avcodec_close(). No need to unref it here explicitly, but i can do it if
you prefer that.

> 
> Beyond those two apparent leaks, looks good.

I'll send an updated set with the first two patches only. Now we just
need nvenc, amfenc and vaapi_enc ported, and then we can apply everything.
Anton Khirnov March 26, 2020, 7:42 p.m. UTC | #3
Quoting James Almer (2020-03-26 20:28:12)
> On 3/26/2020 5:57 AM, Anton Khirnov wrote:
> > Quoting James Almer (2020-03-16 22:30:01)
> >> Following the same logic as 061a0c14bb, this commit turns the old encode API
> >> into a wrapper for the new one.
> >>
> >> Signed-off-by: James Almer <jamrial@gmail.com>
> >> ---
> >> This could be squashed with the previous commit, like it was done in 061a0c14bb,
> >> but i figured it would be easier to review this way.
> >>
> >>  libavcodec/encode.c   | 364 +++++++++++++-----------------------------
> >>  libavcodec/internal.h |   1 +
> >>  libavcodec/utils.c    |   8 +
> >>  3 files changed, 116 insertions(+), 257 deletions(-)
> >>
> >> diff --git a/libavcodec/encode.c b/libavcodec/encode.c
> >> index cdea1c6c1e..0fdb9e2df2 100644
> >> --- a/libavcodec/encode.c
> >> +++ b/libavcodec/encode.c
> >> @@ -610,3 +361,102 @@ int attribute_align_arg avcodec_receive_packet(AVCodecContext *avctx, AVPacket *
> >>  
> >>      return 0;
> >>  }
> >> +
> >> +static int compat_encode(AVCodecContext *avctx, AVPacket *avpkt,
> >> +                         int *got_packet, const AVFrame *frame)
> >> +{
> >> +    AVCodecInternal *avci = avctx->internal;
> >> +    AVPacket user_pkt;
> >> +    int ret;
> >> +
> >> +    *got_packet = 0;
> >> +
> >> +    if (frame && avctx->codec->type == AVMEDIA_TYPE_VIDEO) {
> >> +        if (frame->format == AV_PIX_FMT_NONE)
> >> +            av_log(avctx, AV_LOG_WARNING, "AVFrame.format is not set\n");
> >> +        if (frame->width == 0 || frame->height == 0)
> >> +            av_log(avctx, AV_LOG_WARNING, "AVFrame.width or height is not set\n");
> >> +    }
> >> +
> >> +    ret = avcodec_send_frame(avctx, frame);
> >> +    if (ret == AVERROR_EOF)
> >> +        ret = 0;
> >> +    else if (ret == AVERROR(EAGAIN)) {
> >> +        /* we fully drain all the output in each encode call, so this should not
> >> +         * ever happen */
> >> +        return AVERROR_BUG;
> >> +    } else if (ret < 0)
> >> +        return ret;
> >> +
> >> +    av_packet_move_ref(&user_pkt, avpkt);
> >> +    while (ret >= 0) {
> >> +        ret = avcodec_receive_packet(avctx, avpkt);
> >> +        if (ret < 0) {
> >> +            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
> >> +                ret = 0;
> >> +            goto finish;
> >> +        }
> >> +
> >> +        if (avpkt != avci->compat_encode_packet) {
> >> +            if (avpkt->data && user_pkt.data) {
> >> +                if (user_pkt.size >= avpkt->size) {
> >> +                    memcpy(user_pkt.data, avpkt->data, avpkt->size);
> >> +                    av_buffer_unref(&avpkt->buf);
> >> +                    avpkt->buf  = user_pkt.buf;
> >> +                    avpkt->data = user_pkt.data;
> >> +                    av_init_packet(&user_pkt);
> >> +                } else {
> >> +                    av_log(avctx, AV_LOG_ERROR, "Provided packet is too small, needs to be %d\n", avpkt->size);
> >> +                    ret = AVERROR(EINVAL);
> >> +                    goto finish;
> > 
> > Shouldn't the packet be unreffed somewhere in this block?
> 
> Yeah. Fixed locally.
> 
> > 
> >> +                }
> >> +            }
> >> +
> >> +            *got_packet = 1;
> >> +            avpkt = avci->compat_encode_packet;
> >> +        } else {
> >> +            if (!avci->compat_decode_warned) {
> >> +                av_log(avctx, AV_LOG_WARNING, "The deprecated avcodec_encode_* "
> >> +                       "API cannot return all the packets for this encoder. "
> >> +                       "Some packets will be dropped. Update your code to the "
> >> +                       "new encoding API to fix this.\n");
> >> +                avci->compat_decode_warned = 1;
> >> +            }
> > 
> > Same.
> 
> avpkt is avci->compat_encode_packet in this block. It will either be
> unreffed next time avcodec_receive_packet() is called with it, or by
> avcodec_close(). No need to unref it here explicitly, but i can do it if
> you prefer that.

I think it's better not to leave data we're not going to use just lying
around for no reason. So yeah, I'd prefer it to be unreffed here.
James Almer March 26, 2020, 8:14 p.m. UTC | #4
On 3/26/2020 4:42 PM, Anton Khirnov wrote:
> Quoting James Almer (2020-03-26 20:28:12)
>> On 3/26/2020 5:57 AM, Anton Khirnov wrote:
>>> Quoting James Almer (2020-03-16 22:30:01)
>>>> Following the same logic as 061a0c14bb, this commit turns the old encode API
>>>> into a wrapper for the new one.
>>>>
>>>> Signed-off-by: James Almer <jamrial@gmail.com>
>>>> ---
>>>> This could be squashed with the previous commit, like it was done in 061a0c14bb,
>>>> but i figured it would be easier to review this way.
>>>>
>>>>  libavcodec/encode.c   | 364 +++++++++++++-----------------------------
>>>>  libavcodec/internal.h |   1 +
>>>>  libavcodec/utils.c    |   8 +
>>>>  3 files changed, 116 insertions(+), 257 deletions(-)
>>>>
>>>> diff --git a/libavcodec/encode.c b/libavcodec/encode.c
>>>> index cdea1c6c1e..0fdb9e2df2 100644
>>>> --- a/libavcodec/encode.c
>>>> +++ b/libavcodec/encode.c
>>>> @@ -610,3 +361,102 @@ int attribute_align_arg avcodec_receive_packet(AVCodecContext *avctx, AVPacket *
>>>>  
>>>>      return 0;
>>>>  }
>>>> +
>>>> +static int compat_encode(AVCodecContext *avctx, AVPacket *avpkt,
>>>> +                         int *got_packet, const AVFrame *frame)
>>>> +{
>>>> +    AVCodecInternal *avci = avctx->internal;
>>>> +    AVPacket user_pkt;
>>>> +    int ret;
>>>> +
>>>> +    *got_packet = 0;
>>>> +
>>>> +    if (frame && avctx->codec->type == AVMEDIA_TYPE_VIDEO) {
>>>> +        if (frame->format == AV_PIX_FMT_NONE)
>>>> +            av_log(avctx, AV_LOG_WARNING, "AVFrame.format is not set\n");
>>>> +        if (frame->width == 0 || frame->height == 0)
>>>> +            av_log(avctx, AV_LOG_WARNING, "AVFrame.width or height is not set\n");
>>>> +    }
>>>> +
>>>> +    ret = avcodec_send_frame(avctx, frame);
>>>> +    if (ret == AVERROR_EOF)
>>>> +        ret = 0;
>>>> +    else if (ret == AVERROR(EAGAIN)) {
>>>> +        /* we fully drain all the output in each encode call, so this should not
>>>> +         * ever happen */
>>>> +        return AVERROR_BUG;
>>>> +    } else if (ret < 0)
>>>> +        return ret;
>>>> +
>>>> +    av_packet_move_ref(&user_pkt, avpkt);
>>>> +    while (ret >= 0) {
>>>> +        ret = avcodec_receive_packet(avctx, avpkt);
>>>> +        if (ret < 0) {
>>>> +            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
>>>> +                ret = 0;
>>>> +            goto finish;
>>>> +        }
>>>> +
>>>> +        if (avpkt != avci->compat_encode_packet) {
>>>> +            if (avpkt->data && user_pkt.data) {
>>>> +                if (user_pkt.size >= avpkt->size) {
>>>> +                    memcpy(user_pkt.data, avpkt->data, avpkt->size);
>>>> +                    av_buffer_unref(&avpkt->buf);
>>>> +                    avpkt->buf  = user_pkt.buf;
>>>> +                    avpkt->data = user_pkt.data;
>>>> +                    av_init_packet(&user_pkt);
>>>> +                } else {
>>>> +                    av_log(avctx, AV_LOG_ERROR, "Provided packet is too small, needs to be %d\n", avpkt->size);
>>>> +                    ret = AVERROR(EINVAL);
>>>> +                    goto finish;
>>>
>>> Shouldn't the packet be unreffed somewhere in this block?
>>
>> Yeah. Fixed locally.
>>
>>>
>>>> +                }
>>>> +            }
>>>> +
>>>> +            *got_packet = 1;
>>>> +            avpkt = avci->compat_encode_packet;
>>>> +        } else {
>>>> +            if (!avci->compat_decode_warned) {
>>>> +                av_log(avctx, AV_LOG_WARNING, "The deprecated avcodec_encode_* "
>>>> +                       "API cannot return all the packets for this encoder. "
>>>> +                       "Some packets will be dropped. Update your code to the "
>>>> +                       "new encoding API to fix this.\n");
>>>> +                avci->compat_decode_warned = 1;
>>>> +            }
>>>
>>> Same.
>>
>> avpkt is avci->compat_encode_packet in this block. It will either be
>> unreffed next time avcodec_receive_packet() is called with it, or by
>> avcodec_close(). No need to unref it here explicitly, but i can do it if
>> you prefer that.
> 
> I think it's better not to leave data we're not going to use just lying
> around for no reason. So yeah, I'd prefer it to be unreffed here.

Ok, added and the updated set sent.
diff mbox series

Patch

diff --git a/libavcodec/encode.c b/libavcodec/encode.c
index cdea1c6c1e..0fdb9e2df2 100644
--- a/libavcodec/encode.c
+++ b/libavcodec/encode.c
@@ -32,43 +32,28 @@ 
 
 int ff_alloc_packet2(AVCodecContext *avctx, AVPacket *avpkt, int64_t size, int64_t min_size)
 {
-    if (avpkt->size < 0) {
-        av_log(avctx, AV_LOG_ERROR, "Invalid negative user packet size %d\n", avpkt->size);
-        return AVERROR(EINVAL);
-    }
     if (size < 0 || size > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE) {
         av_log(avctx, AV_LOG_ERROR, "Invalid minimum required packet size %"PRId64" (max allowed is %d)\n",
                size, INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE);
         return AVERROR(EINVAL);
     }
 
+    av_assert0(!avpkt->data);
+
     if (avctx && 2*min_size < size) { // FIXME The factor needs to be finetuned
-        av_assert0(!avpkt->data || avpkt->data != avctx->internal->byte_buffer);
-        if (!avpkt->data || avpkt->size < size) {
-            av_fast_padded_malloc(&avctx->internal->byte_buffer, &avctx->internal->byte_buffer_size, size);
-            avpkt->data = avctx->internal->byte_buffer;
-            avpkt->size = avctx->internal->byte_buffer_size;
-        }
+        av_fast_padded_malloc(&avctx->internal->byte_buffer, &avctx->internal->byte_buffer_size, size);
+        avpkt->data = avctx->internal->byte_buffer;
+        avpkt->size = size;
     }
 
-    if (avpkt->data) {
-        AVBufferRef *buf = avpkt->buf;
-
-        if (avpkt->size < size) {
-            av_log(avctx, AV_LOG_ERROR, "User packet is too small (%d < %"PRId64")\n", avpkt->size, size);
-            return AVERROR(EINVAL);
-        }
-
-        av_init_packet(avpkt);
-        avpkt->buf      = buf;
-        avpkt->size     = size;
-        return 0;
-    } else {
+    if (!avpkt->data) {
         int ret = av_new_packet(avpkt, size);
         if (ret < 0)
             av_log(avctx, AV_LOG_ERROR, "Failed to allocate packet of size %"PRId64"\n", size);
         return ret;
     }
+
+    return 0;
 }
 
 int ff_alloc_packet(AVPacket *avpkt, int size)
@@ -111,240 +96,6 @@  fail:
     return ret;
 }
 
-int attribute_align_arg avcodec_encode_audio2(AVCodecContext *avctx,
-                                              AVPacket *avpkt,
-                                              const AVFrame *frame,
-                                              int *got_packet_ptr)
-{
-    AVFrame *extended_frame = NULL;
-    AVFrame *padded_frame = NULL;
-    int ret;
-    AVPacket user_pkt = *avpkt;
-    int needs_realloc = !user_pkt.data;
-
-    *got_packet_ptr = 0;
-
-    if (!avctx->codec->encode2) {
-        av_log(avctx, AV_LOG_ERROR, "This encoder requires using the avcodec_send_frame() API.\n");
-        return AVERROR(ENOSYS);
-    }
-
-    if (!(avctx->codec->capabilities & AV_CODEC_CAP_DELAY) && !frame) {
-        av_packet_unref(avpkt);
-        return 0;
-    }
-
-    /* ensure that extended_data is properly set */
-    if (frame && !frame->extended_data) {
-        if (av_sample_fmt_is_planar(avctx->sample_fmt) &&
-            avctx->channels > AV_NUM_DATA_POINTERS) {
-            av_log(avctx, AV_LOG_ERROR, "Encoding to a planar sample format, "
-                                        "with more than %d channels, but extended_data is not set.\n",
-                   AV_NUM_DATA_POINTERS);
-            return AVERROR(EINVAL);
-        }
-        av_log(avctx, AV_LOG_WARNING, "extended_data is not set.\n");
-
-        extended_frame = av_frame_alloc();
-        if (!extended_frame)
-            return AVERROR(ENOMEM);
-
-        memcpy(extended_frame, frame, sizeof(AVFrame));
-        extended_frame->extended_data = extended_frame->data;
-        frame = extended_frame;
-    }
-
-    /* extract audio service type metadata */
-    if (frame) {
-        AVFrameSideData *sd = av_frame_get_side_data(frame, AV_FRAME_DATA_AUDIO_SERVICE_TYPE);
-        if (sd && sd->size >= sizeof(enum AVAudioServiceType))
-            avctx->audio_service_type = *(enum AVAudioServiceType*)sd->data;
-    }
-
-    /* check for valid frame size */
-    if (frame) {
-        if (avctx->codec->capabilities & AV_CODEC_CAP_SMALL_LAST_FRAME) {
-            if (frame->nb_samples > avctx->frame_size) {
-                av_log(avctx, AV_LOG_ERROR, "more samples than frame size (avcodec_encode_audio2)\n");
-                ret = AVERROR(EINVAL);
-                goto end;
-            }
-        } else if (!(avctx->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE)) {
-            /* if we already got an undersized frame, that must have been the last */
-            if (avctx->internal->last_audio_frame) {
-                av_log(avctx, AV_LOG_ERROR, "frame_size (%d) was not respected for a non-last frame (avcodec_encode_audio2)\n", avctx->frame_size);
-                ret = AVERROR(EINVAL);
-                goto end;
-            }
-
-            if (frame->nb_samples < avctx->frame_size) {
-                if (!(padded_frame = av_frame_alloc())) {
-                    ret = AVERROR(ENOMEM);
-                    goto end;
-                }
-                ret = pad_last_frame(avctx, &padded_frame, frame);
-                if (ret < 0)
-                    goto end;
-
-                frame = padded_frame;
-                avctx->internal->last_audio_frame = 1;
-            }
-
-            if (frame->nb_samples != avctx->frame_size) {
-                av_log(avctx, AV_LOG_ERROR, "nb_samples (%d) != frame_size (%d) (avcodec_encode_audio2)\n", frame->nb_samples, avctx->frame_size);
-                ret = AVERROR(EINVAL);
-                goto end;
-            }
-        }
-    }
-
-    av_assert0(avctx->codec->encode2);
-
-    ret = avctx->codec->encode2(avctx, avpkt, frame, got_packet_ptr);
-    if (!ret) {
-        if (*got_packet_ptr) {
-            if (!(avctx->codec->capabilities & AV_CODEC_CAP_DELAY)) {
-                if (avpkt->pts == AV_NOPTS_VALUE)
-                    avpkt->pts = frame->pts;
-                if (!avpkt->duration)
-                    avpkt->duration = ff_samples_to_time_base(avctx,
-                                                              frame->nb_samples);
-            }
-            avpkt->dts = avpkt->pts;
-        } else {
-            avpkt->size = 0;
-        }
-    }
-    if (avpkt->data && avpkt->data == avctx->internal->byte_buffer) {
-        needs_realloc = 0;
-        if (user_pkt.data) {
-            if (user_pkt.size >= avpkt->size) {
-                memcpy(user_pkt.data, avpkt->data, avpkt->size);
-            } else {
-                av_log(avctx, AV_LOG_ERROR, "Provided packet is too small, needs to be %d\n", avpkt->size);
-                avpkt->size = user_pkt.size;
-                ret = -1;
-            }
-            avpkt->buf      = user_pkt.buf;
-            avpkt->data     = user_pkt.data;
-        } else if (!avpkt->buf) {
-            ret = av_packet_make_refcounted(avpkt);
-            if (ret < 0)
-                goto end;
-        }
-    }
-
-    if (!ret) {
-        if (needs_realloc && avpkt->data) {
-            ret = av_buffer_realloc(&avpkt->buf, avpkt->size + AV_INPUT_BUFFER_PADDING_SIZE);
-            if (ret >= 0)
-                avpkt->data = avpkt->buf->data;
-        }
-        if (frame)
-            avctx->frame_number++;
-    }
-
-    if (ret < 0 || !*got_packet_ptr) {
-        av_packet_unref(avpkt);
-        goto end;
-    }
-
-    /* NOTE: if we add any audio encoders which output non-keyframe packets,
-     *       this needs to be moved to the encoders, but for now we can do it
-     *       here to simplify things */
-    avpkt->flags |= AV_PKT_FLAG_KEY;
-
-end:
-    av_frame_free(&padded_frame);
-    av_free(extended_frame);
-
-    return ret;
-}
-
-int attribute_align_arg avcodec_encode_video2(AVCodecContext *avctx,
-                                              AVPacket *avpkt,
-                                              const AVFrame *frame,
-                                              int *got_packet_ptr)
-{
-    int ret;
-    AVPacket user_pkt = *avpkt;
-    int needs_realloc = !user_pkt.data;
-
-    *got_packet_ptr = 0;
-
-    if (!avctx->codec->encode2) {
-        av_log(avctx, AV_LOG_ERROR, "This encoder requires using the avcodec_send_frame() API.\n");
-        return AVERROR(ENOSYS);
-    }
-
-    if(CONFIG_FRAME_THREAD_ENCODER &&
-       avctx->internal->frame_thread_encoder && (avctx->active_thread_type&FF_THREAD_FRAME))
-        return ff_thread_video_encode_frame(avctx, avpkt, frame, got_packet_ptr);
-
-    if ((avctx->flags&AV_CODEC_FLAG_PASS1) && avctx->stats_out)
-        avctx->stats_out[0] = '\0';
-
-    if (!(avctx->codec->capabilities & AV_CODEC_CAP_DELAY) && !frame) {
-        av_packet_unref(avpkt);
-        return 0;
-    }
-
-    if (av_image_check_size2(avctx->width, avctx->height, avctx->max_pixels, AV_PIX_FMT_NONE, 0, avctx))
-        return AVERROR(EINVAL);
-
-    if (frame && frame->format == AV_PIX_FMT_NONE)
-        av_log(avctx, AV_LOG_WARNING, "AVFrame.format is not set\n");
-    if (frame && (frame->width == 0 || frame->height == 0))
-        av_log(avctx, AV_LOG_WARNING, "AVFrame.width or height is not set\n");
-
-    av_assert0(avctx->codec->encode2);
-
-    ret = avctx->codec->encode2(avctx, avpkt, frame, got_packet_ptr);
-    av_assert0(ret <= 0);
-
-    emms_c();
-
-    if (avpkt->data && avpkt->data == avctx->internal->byte_buffer) {
-        needs_realloc = 0;
-        if (user_pkt.data) {
-            if (user_pkt.size >= avpkt->size) {
-                memcpy(user_pkt.data, avpkt->data, avpkt->size);
-            } else {
-                av_log(avctx, AV_LOG_ERROR, "Provided packet is too small, needs to be %d\n", avpkt->size);
-                avpkt->size = user_pkt.size;
-                ret = -1;
-            }
-            avpkt->buf      = user_pkt.buf;
-            avpkt->data     = user_pkt.data;
-        } else if (!avpkt->buf) {
-            ret = av_packet_make_refcounted(avpkt);
-            if (ret < 0)
-                return ret;
-        }
-    }
-
-    if (!ret) {
-        if (!*got_packet_ptr)
-            avpkt->size = 0;
-        else if (!(avctx->codec->capabilities & AV_CODEC_CAP_DELAY))
-            avpkt->pts = avpkt->dts = frame->pts;
-
-        if (needs_realloc && avpkt->data) {
-            ret = av_buffer_realloc(&avpkt->buf, avpkt->size + AV_INPUT_BUFFER_PADDING_SIZE);
-            if (ret >= 0)
-                avpkt->data = avpkt->buf->data;
-        }
-
-        if (frame)
-            avctx->frame_number++;
-    }
-
-    if (ret < 0 || !*got_packet_ptr)
-        av_packet_unref(avpkt);
-
-    return ret;
-}
-
 int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size,
                             const AVSubtitle *sub)
 {
@@ -610,3 +361,102 @@  int attribute_align_arg avcodec_receive_packet(AVCodecContext *avctx, AVPacket *
 
     return 0;
 }
+
+static int compat_encode(AVCodecContext *avctx, AVPacket *avpkt,
+                         int *got_packet, const AVFrame *frame)
+{
+    AVCodecInternal *avci = avctx->internal;
+    AVPacket user_pkt;
+    int ret;
+
+    *got_packet = 0;
+
+    if (frame && avctx->codec->type == AVMEDIA_TYPE_VIDEO) {
+        if (frame->format == AV_PIX_FMT_NONE)
+            av_log(avctx, AV_LOG_WARNING, "AVFrame.format is not set\n");
+        if (frame->width == 0 || frame->height == 0)
+            av_log(avctx, AV_LOG_WARNING, "AVFrame.width or height is not set\n");
+    }
+
+    ret = avcodec_send_frame(avctx, frame);
+    if (ret == AVERROR_EOF)
+        ret = 0;
+    else if (ret == AVERROR(EAGAIN)) {
+        /* we fully drain all the output in each encode call, so this should not
+         * ever happen */
+        return AVERROR_BUG;
+    } else if (ret < 0)
+        return ret;
+
+    av_packet_move_ref(&user_pkt, avpkt);
+    while (ret >= 0) {
+        ret = avcodec_receive_packet(avctx, avpkt);
+        if (ret < 0) {
+            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
+                ret = 0;
+            goto finish;
+        }
+
+        if (avpkt != avci->compat_encode_packet) {
+            if (avpkt->data && user_pkt.data) {
+                if (user_pkt.size >= avpkt->size) {
+                    memcpy(user_pkt.data, avpkt->data, avpkt->size);
+                    av_buffer_unref(&avpkt->buf);
+                    avpkt->buf  = user_pkt.buf;
+                    avpkt->data = user_pkt.data;
+                    av_init_packet(&user_pkt);
+                } else {
+                    av_log(avctx, AV_LOG_ERROR, "Provided packet is too small, needs to be %d\n", avpkt->size);
+                    ret = AVERROR(EINVAL);
+                    goto finish;
+                }
+            }
+
+            *got_packet = 1;
+            avpkt = avci->compat_encode_packet;
+        } else {
+            if (!avci->compat_decode_warned) {
+                av_log(avctx, AV_LOG_WARNING, "The deprecated avcodec_encode_* "
+                       "API cannot return all the packets for this encoder. "
+                       "Some packets will be dropped. Update your code to the "
+                       "new encoding API to fix this.\n");
+                avci->compat_decode_warned = 1;
+            }
+        }
+
+        if (avci->draining)
+            break;
+    }
+
+finish:
+    if (ret < 0)
+        av_packet_unref(&user_pkt);
+
+    return ret;
+}
+
+int attribute_align_arg avcodec_encode_audio2(AVCodecContext *avctx,
+                                              AVPacket *avpkt,
+                                              const AVFrame *frame,
+                                              int *got_packet_ptr)
+{
+    int ret = compat_encode(avctx, avpkt, got_packet_ptr, frame);
+
+    if (ret < 0)
+        av_packet_unref(avpkt);
+
+    return ret;
+}
+
+int attribute_align_arg avcodec_encode_video2(AVCodecContext *avctx,
+                                              AVPacket *avpkt,
+                                              const AVFrame *frame,
+                                              int *got_packet_ptr)
+{
+    int ret = compat_encode(avctx, avpkt, got_packet_ptr, frame);
+
+    if (ret < 0)
+        av_packet_unref(avpkt);
+
+    return ret;
+}
diff --git a/libavcodec/internal.h b/libavcodec/internal.h
index 8b97e08e9f..771b9ec32b 100644
--- a/libavcodec/internal.h
+++ b/libavcodec/internal.h
@@ -222,6 +222,7 @@  typedef struct AVCodecInternal {
      * of the packet (that should be submitted in the next decode call */
     size_t compat_decode_partial_size;
     AVFrame *compat_decode_frame;
+    AVPacket *compat_encode_packet;
 
     int showed_multi_packet_warning;
 
diff --git a/libavcodec/utils.c b/libavcodec/utils.c
index cb39352876..d3c5a8131d 100644
--- a/libavcodec/utils.c
+++ b/libavcodec/utils.c
@@ -613,6 +613,12 @@  int attribute_align_arg avcodec_open2(AVCodecContext *avctx, const AVCodec *code
         goto free_and_end;
     }
 
+    avci->compat_encode_packet = av_packet_alloc();
+    if (!avctx->internal->compat_encode_packet) {
+        ret = AVERROR(ENOMEM);
+        goto free_and_end;
+    }
+
     avci->buffer_pkt = av_packet_alloc();
     if (!avci->buffer_pkt) {
         ret = AVERROR(ENOMEM);
@@ -1076,6 +1082,7 @@  FF_ENABLE_DEPRECATION_WARNINGS
         av_frame_free(&avci->to_free);
         av_frame_free(&avci->compat_decode_frame);
         av_frame_free(&avci->buffer_frame);
+        av_packet_free(&avci->compat_encode_packet);
         av_packet_free(&avci->buffer_pkt);
         av_packet_free(&avci->last_pkt_props);
 
@@ -1132,6 +1139,7 @@  av_cold int avcodec_close(AVCodecContext *avctx)
         av_frame_free(&avctx->internal->to_free);
         av_frame_free(&avctx->internal->compat_decode_frame);
         av_frame_free(&avctx->internal->buffer_frame);
+        av_packet_free(&avctx->internal->compat_encode_packet);
         av_packet_free(&avctx->internal->buffer_pkt);
         av_packet_free(&avctx->internal->last_pkt_props);