[FFmpeg-cvslog] decode: restructure the core decoding code

Anton Khirnov git at videolan.org
Sun Apr 23 02:32:24 EEST 2017


ffmpeg | branch: master | Anton Khirnov <anton at khirnov.net> | Wed Oct 26 13:59:15 2016 +0200| [061a0c14bb5767bca72e3a7227ca400de439ba09] | committer: Anton Khirnov

decode: restructure the core decoding code

Currently, the new decoding API is pretty much just a wrapper around the
old deprecated one. This is problematic, since it interferes with making
full use of the flexibility added by the new API. The old API should
also be removed at some future point.

Reorganize the code so that the new send_packet/receive_frame functions
call the actual decoding directly and change the old deprecated
avcodec_decode_* functions into wrappers around the new API.

The new internal API for decoders is now changing as well. Before this
commit, it mirrors the public API, so the decoders need to implement
send_packet() and receive_frame() callbacks. This turns out to require
awkward constructs in both the decoders and the generic code. After this
commit, the decoders only implement the receive_frame() callback and
call a new internal function, ff_decode_get_packet() to obtain input
data, in the same manner to how the bitstream filters now work.

avcodec will now always make a reference to the input packet, which means
that non-refcounted input packets will be copied. Keeping the previous
behaviour, where this copy could sometimes be avoided, would make the
code significantly more complex and fragile for only dubious gains,
since packets are typically small and everyone who cares about
performance should use refcounted packets anyway.

> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=061a0c14bb5767bca72e3a7227ca400de439ba09
---

 libavcodec/avcodec.h  |  18 +--
 libavcodec/decode.c   | 391 +++++++++++++++++++++++++++-----------------------
 libavcodec/decode.h   |  35 +++++
 libavcodec/internal.h |  17 +++
 libavcodec/utils.c    |  22 ++-
 5 files changed, 294 insertions(+), 189 deletions(-)

diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h
index 6e1ed74e08..1827711b50 100644
--- a/libavcodec/avcodec.h
+++ b/libavcodec/avcodec.h
@@ -3217,20 +3217,22 @@ typedef struct AVCodec {
     int (*decode)(AVCodecContext *, void *outdata, int *outdata_size, AVPacket *avpkt);
     int (*close)(AVCodecContext *);
     /**
-     * Decode/encode API with decoupled packet/frame dataflow. The API is the
+     * Encode API with decoupled packet/frame dataflow. The API is the
      * same as the avcodec_ prefixed APIs (avcodec_send_frame() etc.), except
      * that:
      * - never called if the codec is closed or the wrong type,
-     * - AVPacket parameter change side data is applied right before calling
-     *   AVCodec->send_packet,
-     * - if AV_CODEC_CAP_DELAY is not set, drain packets or frames are never sent,
-     * - only one drain packet is ever passed down (until the next flush()),
-     * - a drain AVPacket is always NULL (no need to check for avpkt->size).
+     * - if AV_CODEC_CAP_DELAY is not set, drain frames are never sent,
+     * - only one drain frame is ever passed down,
      */
     int (*send_frame)(AVCodecContext *avctx, const AVFrame *frame);
-    int (*send_packet)(AVCodecContext *avctx, const AVPacket *avpkt);
-    int (*receive_frame)(AVCodecContext *avctx, AVFrame *frame);
     int (*receive_packet)(AVCodecContext *avctx, AVPacket *avpkt);
+
+    /**
+     * Decode API with decoupled packet/frame dataflow. This function is called
+     * to get one output frame. It should call ff_decode_get_packet() to obtain
+     * input data.
+     */
+    int (*receive_frame)(AVCodecContext *avctx, AVFrame *frame);
     /**
      * Flush buffers.
      * Will be called when seeking
diff --git a/libavcodec/decode.c b/libavcodec/decode.c
index b02278d3a6..a1908ecf4b 100644
--- a/libavcodec/decode.c
+++ b/libavcodec/decode.c
@@ -24,6 +24,7 @@
 #include "config.h"
 
 #include "libavutil/avassert.h"
+#include "libavutil/avstring.h"
 #include "libavutil/common.h"
 #include "libavutil/frame.h"
 #include "libavutil/hwcontext.h"
@@ -31,6 +32,7 @@
 
 #include "avcodec.h"
 #include "bytestream.h"
+#include "decode.h"
 #include "internal.h"
 #include "thread.h"
 
@@ -152,109 +154,188 @@ static int unrefcount_frame(AVCodecInternal *avci, AVFrame *frame)
     return 0;
 }
 
-static int do_decode(AVCodecContext *avctx, AVPacket *pkt)
+int ff_decode_get_packet(AVCodecContext *avctx, AVPacket *pkt)
 {
-    int got_frame;
+    AVCodecInternal *avci = avctx->internal;
     int ret;
 
-    av_assert0(!avctx->internal->buffer_frame->buf[0]);
+    if (avci->draining)
+        return AVERROR_EOF;
+
+    if (!avci->buffer_pkt->data && !avci->buffer_pkt->side_data_elems)
+        return AVERROR(EAGAIN);
+
+    av_packet_move_ref(pkt, avci->buffer_pkt);
+
+    ret = extract_packet_props(avctx->internal, pkt);
+    if (ret < 0)
+        goto finish;
+
+    ret = apply_param_change(avctx, pkt);
+    if (ret < 0)
+        goto finish;
+
+    if (avctx->codec->receive_frame)
+        avci->compat_decode_consumed += pkt->size;
 
-    if (!pkt)
-        pkt = avctx->internal->buffer_pkt;
+    return 0;
+finish:
+    av_packet_unref(pkt);
+    return ret;
+}
+
+/*
+ * The core of the receive_frame_wrapper for the decoders implementing
+ * the simple API. Certain decoders might consume partial packets without
+ * returning any output, so this function needs to be called in a loop until it
+ * returns EAGAIN.
+ **/
+static int decode_simple_internal(AVCodecContext *avctx, AVFrame *frame)
+{
+    AVCodecInternal   *avci = avctx->internal;
+    DecodeSimpleContext *ds = &avci->ds;
+    AVPacket           *pkt = ds->in_pkt;
+    int got_frame;
+    int ret;
 
-    // This is the lesser evil. The field is for compatibility with legacy users
-    // of the legacy API, and users using the new API should not be forced to
-    // even know about this field.
-    avctx->refcounted_frames = 1;
+    if (!pkt->data && !avci->draining) {
+        av_packet_unref(pkt);
+        ret = ff_decode_get_packet(avctx, pkt);
+        if (ret < 0 && ret != AVERROR_EOF)
+            return ret;
+    }
 
     // Some codecs (at least wma lossless) will crash when feeding drain packets
     // after EOF was signaled.
-    if (avctx->internal->draining_done)
+    if (avci->draining_done)
+        return AVERROR_EOF;
+
+    if (!pkt->data &&
+        !(avctx->codec->capabilities & AV_CODEC_CAP_DELAY ||
+          avctx->active_thread_type & FF_THREAD_FRAME))
         return AVERROR_EOF;
 
-    if (avctx->codec_type == AVMEDIA_TYPE_VIDEO) {
-        ret = avcodec_decode_video2(avctx, avctx->internal->buffer_frame,
-                                    &got_frame, pkt);
-        if (ret >= 0)
-            ret = pkt->size;
-    } else if (avctx->codec_type == AVMEDIA_TYPE_AUDIO) {
-        ret = avcodec_decode_audio4(avctx, avctx->internal->buffer_frame,
-                                    &got_frame, pkt);
+    got_frame = 0;
+
+    if (HAVE_THREADS && avctx->active_thread_type & FF_THREAD_FRAME) {
+        ret = ff_thread_decode_frame(avctx, frame, &got_frame, pkt);
     } else {
-        ret = AVERROR(EINVAL);
+        ret = avctx->codec->decode(avctx, frame, &got_frame, pkt);
+
+        if (!(avctx->codec->caps_internal & FF_CODEC_CAP_SETS_PKT_DTS))
+            frame->pkt_dts = pkt->dts;
+        /* get_buffer is supposed to set frame parameters */
+        if (!(avctx->codec->capabilities & AV_CODEC_CAP_DR1)) {
+            frame->sample_aspect_ratio = avctx->sample_aspect_ratio;
+            frame->width               = avctx->width;
+            frame->height              = avctx->height;
+            frame->format              = avctx->codec->type == AVMEDIA_TYPE_VIDEO ?
+                                         avctx->pix_fmt : avctx->sample_fmt;
+        }
     }
 
-    if (ret < 0)
-        return ret;
+    emms_c();
+
+    if (!got_frame)
+        av_frame_unref(frame);
+
+    if (ret >= 0 && avctx->codec->type == AVMEDIA_TYPE_VIDEO)
+        ret = pkt->size;
+
+#if FF_API_AVCTX_TIMEBASE
+    if (avctx->framerate.num > 0 && avctx->framerate.den > 0)
+        avctx->time_base = av_inv_q(avctx->framerate);
+#endif
 
     if (avctx->internal->draining && !got_frame)
-        avctx->internal->draining_done = 1;
+        avci->draining_done = 1;
+
+    avci->compat_decode_consumed += ret;
 
-    if (ret >= pkt->size) {
-        av_packet_unref(avctx->internal->buffer_pkt);
+    if (ret >= pkt->size || ret < 0) {
+        av_packet_unref(pkt);
     } else {
         int consumed = ret;
 
-        if (pkt != avctx->internal->buffer_pkt) {
-            av_packet_unref(avctx->internal->buffer_pkt);
-            if ((ret = av_packet_ref(avctx->internal->buffer_pkt, pkt)) < 0)
-                return ret;
-        }
-
-        avctx->internal->buffer_pkt->data += consumed;
-        avctx->internal->buffer_pkt->size -= consumed;
-        avctx->internal->buffer_pkt->pts   = AV_NOPTS_VALUE;
-        avctx->internal->buffer_pkt->dts   = AV_NOPTS_VALUE;
+        pkt->data                += consumed;
+        pkt->size                -= consumed;
+        pkt->pts                  = AV_NOPTS_VALUE;
+        pkt->dts                  = AV_NOPTS_VALUE;
+        avci->last_pkt_props->pts = AV_NOPTS_VALUE;
+        avci->last_pkt_props->dts = AV_NOPTS_VALUE;
     }
 
     if (got_frame)
-        av_assert0(avctx->internal->buffer_frame->buf[0]);
+        av_assert0(frame->buf[0]);
+
+    return ret < 0 ? ret : 0;
+}
+
+static int decode_simple_receive_frame(AVCodecContext *avctx, AVFrame *frame)
+{
+    int ret;
+
+    while (!frame->buf[0]) {
+        ret = decode_simple_internal(avctx, frame);
+        if (ret < 0)
+            return ret;
+    }
 
     return 0;
 }
 
-int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt)
+static int decode_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame)
 {
+    AVCodecInternal *avci = avctx->internal;
     int ret;
 
+    av_assert0(!frame->buf[0]);
+
+    if (avctx->codec->receive_frame)
+        ret = avctx->codec->receive_frame(avctx, frame);
+    else
+        ret = decode_simple_receive_frame(avctx, frame);
+
+    if (ret == AVERROR_EOF)
+        avci->draining_done = 1;
+
+    return ret;
+}
+
+int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt)
+{
+    AVCodecInternal *avci = avctx->internal;
+    int ret = 0;
+
     if (!avcodec_is_open(avctx) || !av_codec_is_decoder(avctx->codec))
         return AVERROR(EINVAL);
 
     if (avctx->internal->draining)
         return AVERROR_EOF;
 
+    if (avci->buffer_pkt->data || avci->buffer_pkt->side_data_elems)
+        return AVERROR(EAGAIN);
+
     if (!avpkt || !avpkt->size) {
         avctx->internal->draining = 1;
-        avpkt = NULL;
-
-        if (!(avctx->codec->capabilities & AV_CODEC_CAP_DELAY))
-            return 0;
+    } else {
+        ret = av_packet_ref(avci->buffer_pkt, avpkt);
+        if (ret < 0)
+            return ret;
     }
 
-    if (avctx->codec->send_packet) {
-        if (avpkt) {
-            ret = apply_param_change(avctx, (AVPacket *)avpkt);
-            if (ret < 0)
-                return ret;
-        }
-        return avctx->codec->send_packet(avctx, avpkt);
+    if (!avci->buffer_frame->buf[0]) {
+        ret = decode_receive_frame_internal(avctx, avci->buffer_frame);
+        if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
+            return ret;
     }
 
-    // Emulation via old API. Assume avpkt is likely not refcounted, while
-    // decoder output is always refcounted, and avoid copying.
-
-    if (avctx->internal->buffer_pkt->size || avctx->internal->buffer_frame->buf[0])
-        return AVERROR(EAGAIN);
-
-    // The goal is decoding the first frame of the packet without using memcpy,
-    // because the common case is having only 1 frame per packet (especially
-    // with video, but audio too). In other cases, it can't be avoided, unless
-    // the user is feeding refcounted packets.
-    return do_decode(avctx, (AVPacket *)avpkt);
+    return 0;
 }
 
 int attribute_align_arg avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame)
 {
+    AVCodecInternal *avci = avctx->internal;
     int ret;
 
     av_frame_unref(frame);
@@ -262,157 +343,104 @@ int attribute_align_arg avcodec_receive_frame(AVCodecContext *avctx, AVFrame *fr
     if (!avcodec_is_open(avctx) || !av_codec_is_decoder(avctx->codec))
         return AVERROR(EINVAL);
 
-    if (avctx->codec->receive_frame) {
-        if (avctx->internal->draining && !(avctx->codec->capabilities & AV_CODEC_CAP_DELAY))
-            return AVERROR_EOF;
-        return avctx->codec->receive_frame(avctx, frame);
-    }
-
-    // Emulation via old API.
-
-    if (!avctx->internal->buffer_frame->buf[0]) {
-        if (!avctx->internal->buffer_pkt->size && !avctx->internal->draining)
-            return AVERROR(EAGAIN);
-
-        while (1) {
-            if ((ret = do_decode(avctx, avctx->internal->buffer_pkt)) < 0) {
-                av_packet_unref(avctx->internal->buffer_pkt);
-                return ret;
-            }
-            // Some audio decoders may consume partial data without returning
-            // a frame (fate-wmapro-2ch). There is no way to make the caller
-            // call avcodec_receive_frame() again without returning a frame,
-            // so try to decode more in these cases.
-            if (avctx->internal->buffer_frame->buf[0] ||
-                !avctx->internal->buffer_pkt->size)
-                break;
-        }
+    if (avci->buffer_frame->buf[0]) {
+        av_frame_move_ref(frame, avci->buffer_frame);
+    } else {
+        ret = decode_receive_frame_internal(avctx, frame);
+        if (ret < 0)
+            return ret;
     }
 
-    if (!avctx->internal->buffer_frame->buf[0])
-        return avctx->internal->draining ? AVERROR_EOF : AVERROR(EAGAIN);
+    avctx->frame_number++;
 
-    av_frame_move_ref(frame, avctx->internal->buffer_frame);
     return 0;
 }
 
-int attribute_align_arg avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture,
-                                              int *got_picture_ptr,
-                                              AVPacket *avpkt)
+static int compat_decode(AVCodecContext *avctx, AVFrame *frame,
+                         int *got_frame, AVPacket *pkt)
 {
     AVCodecInternal *avci = avctx->internal;
     int ret;
 
-    *got_picture_ptr = 0;
-    if ((avctx->coded_width || avctx->coded_height) && av_image_check_size(avctx->coded_width, avctx->coded_height, 0, avctx))
-        return -1;
+    av_assert0(avci->compat_decode_consumed == 0);
 
-    if (!avctx->codec->decode) {
-        av_log(avctx, AV_LOG_ERROR, "This decoder requires using the avcodec_send_packet() API.\n");
-        return AVERROR(ENOSYS);
-    }
+    *got_frame = 0;
+    avci->compat_decode = 1;
 
-    ret = extract_packet_props(avci, avpkt);
-    if (ret < 0)
-        return ret;
+    if (avci->compat_decode_partial_size > 0 &&
+        avci->compat_decode_partial_size != pkt->size) {
+        av_log(avctx, AV_LOG_ERROR,
+               "Got unexpected packet size after a partial decode\n");
+        ret = AVERROR(EINVAL);
+        goto finish;
+    }
 
-    ret = apply_param_change(avctx, avpkt);
-    if (ret < 0)
-        return ret;
+    if (!avci->compat_decode_partial_size) {
+        ret = avcodec_send_packet(avctx, pkt);
+        if (ret == AVERROR_EOF)
+            ret = 0;
+        else if (ret == AVERROR(EAGAIN)) {
+            /* we fully drain all the output in each decode call, so this should not
+             * ever happen */
+            ret = AVERROR_BUG;
+            goto finish;
+        } else if (ret < 0)
+            goto finish;
+    }
 
-    av_frame_unref(picture);
-
-    if ((avctx->codec->capabilities & AV_CODEC_CAP_DELAY) || avpkt->size ||
-        (avctx->active_thread_type & FF_THREAD_FRAME)) {
-        if (HAVE_THREADS && avctx->active_thread_type & FF_THREAD_FRAME)
-            ret = ff_thread_decode_frame(avctx, picture, got_picture_ptr,
-                                         avpkt);
-        else {
-            ret = avctx->codec->decode(avctx, picture, got_picture_ptr,
-                                       avpkt);
-            if (!(avctx->codec->caps_internal & FF_CODEC_CAP_SETS_PKT_DTS))
-                picture->pkt_dts = avpkt->dts;
-            /* get_buffer is supposed to set frame parameters */
-            if (!(avctx->codec->capabilities & AV_CODEC_CAP_DR1)) {
-                picture->sample_aspect_ratio = avctx->sample_aspect_ratio;
-                picture->width               = avctx->width;
-                picture->height              = avctx->height;
-                picture->format              = avctx->pix_fmt;
-            }
+    while (ret >= 0) {
+        ret = avcodec_receive_frame(avctx, frame);
+        if (ret < 0) {
+            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
+                ret = 0;
+            goto finish;
         }
 
-        emms_c(); //needed to avoid an emms_c() call before every return;
-
-        if (*got_picture_ptr) {
+        if (frame != avci->compat_decode_frame) {
             if (!avctx->refcounted_frames) {
-                int err = unrefcount_frame(avci, picture);
-                if (err < 0)
-                    return err;
+                ret = unrefcount_frame(avci, frame);
+                if (ret < 0)
+                    goto finish;
             }
 
-            avctx->frame_number++;
-        } else
-            av_frame_unref(picture);
-    } else
-        ret = 0;
+            *got_frame = 1;
+            frame = avci->compat_decode_frame;
+        } else {
+            if (!avci->compat_decode_warned) {
+                av_log(avctx, AV_LOG_WARNING, "The deprecated avcodec_decode_* "
+                       "API cannot return all the frames for this decoder. "
+                       "Some frames will be dropped. Update your code to the "
+                       "new decoding API to fix this.\n");
+                avci->compat_decode_warned = 1;
+            }
+        }
 
-#if FF_API_AVCTX_TIMEBASE
-    if (avctx->framerate.num > 0 && avctx->framerate.den > 0)
-        avctx->time_base = av_inv_q(avctx->framerate);
-#endif
+        if (avci->draining || avci->compat_decode_consumed < pkt->size)
+            break;
+    }
+
+finish:
+    if (ret == 0)
+        ret = FFMIN(avci->compat_decode_consumed, pkt->size);
+    avci->compat_decode_consumed = 0;
+    avci->compat_decode_partial_size = (ret >= 0) ? pkt->size - ret : 0;
 
     return ret;
 }
 
+int attribute_align_arg avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture,
+                                              int *got_picture_ptr,
+                                              AVPacket *avpkt)
+{
+    return compat_decode(avctx, picture, got_picture_ptr, avpkt);
+}
+
 int attribute_align_arg avcodec_decode_audio4(AVCodecContext *avctx,
                                               AVFrame *frame,
                                               int *got_frame_ptr,
                                               AVPacket *avpkt)
 {
-    AVCodecInternal *avci = avctx->internal;
-    int ret = 0;
-
-    *got_frame_ptr = 0;
-
-    if (!avctx->codec->decode) {
-        av_log(avctx, AV_LOG_ERROR, "This decoder requires using the avcodec_send_packet() API.\n");
-        return AVERROR(ENOSYS);
-    }
-
-    ret = extract_packet_props(avci, avpkt);
-    if (ret < 0)
-        return ret;
-
-    if (!avpkt->data && avpkt->size) {
-        av_log(avctx, AV_LOG_ERROR, "invalid packet: NULL data, size != 0\n");
-        return AVERROR(EINVAL);
-    }
-
-    ret = apply_param_change(avctx, avpkt);
-    if (ret < 0)
-        return ret;
-
-    av_frame_unref(frame);
-
-    if ((avctx->codec->capabilities & AV_CODEC_CAP_DELAY) || avpkt->size) {
-        ret = avctx->codec->decode(avctx, frame, got_frame_ptr, avpkt);
-        if (ret >= 0 && *got_frame_ptr) {
-            avctx->frame_number++;
-            frame->pkt_dts = avpkt->dts;
-            if (frame->format == AV_SAMPLE_FMT_NONE)
-                frame->format = avctx->sample_fmt;
-
-            if (!avctx->refcounted_frames) {
-                int err = unrefcount_frame(avci, frame);
-                if (err < 0)
-                    return err;
-            }
-        } else
-            av_frame_unref(frame);
-    }
-
-
-    return ret;
+    return compat_decode(avctx, frame, got_frame_ptr, avpkt);
 }
 
 int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub,
@@ -919,9 +947,12 @@ void avcodec_flush_buffers(AVCodecContext *avctx)
     avctx->internal->draining      = 0;
     avctx->internal->draining_done = 0;
     av_frame_unref(avctx->internal->buffer_frame);
+    av_frame_unref(avctx->internal->compat_decode_frame);
     av_packet_unref(avctx->internal->buffer_pkt);
     avctx->internal->buffer_pkt_valid = 0;
 
+    av_packet_unref(avctx->internal->ds.in_pkt);
+
     if (HAVE_THREADS && avctx->active_thread_type & FF_THREAD_FRAME)
         ff_thread_flush(avctx);
     else if (avctx->codec->flush)
diff --git a/libavcodec/decode.h b/libavcodec/decode.h
new file mode 100644
index 0000000000..21c7c3e07a
--- /dev/null
+++ b/libavcodec/decode.h
@@ -0,0 +1,35 @@
+/*
+ * generic decoding-related code
+ *
+ * This file is part of Libav.
+ *
+ * Libav 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.
+ *
+ * Libav 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 Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_DECODE_H
+#define AVCODEC_DECODE_H
+
+/**
+ * Called by decoders to get the next packet for decoding.
+ *
+ * @param pkt An empty packet to be filled with data.
+ * @return 0 if a new reference has been successfully written to pkt
+ *         AVERROR(EAGAIN) if no data is currently available
+ *         AVERROR_EOF if and end of stream has been reached, so no more data
+ *                     will be available
+ */
+int ff_decode_get_packet(AVCodecContext *avctx, AVPacket *pkt);
+
+#endif /* AVCODEC_DECODE_H */
diff --git a/libavcodec/internal.h b/libavcodec/internal.h
index 2ca7a45e81..dc24e8f764 100644
--- a/libavcodec/internal.h
+++ b/libavcodec/internal.h
@@ -94,6 +94,11 @@ typedef struct FramePool {
     int samples;
 } FramePool;
 
+typedef struct DecodeSimpleContext {
+    AVPacket *in_pkt;
+    AVFrame  *out_frame;
+} DecodeSimpleContext;
+
 typedef struct AVCodecInternal {
     /**
      * Whether the parent AVCodecContext is a copy of the context which had
@@ -130,6 +135,8 @@ typedef struct AVCodecInternal {
 
     void *thread_ctx;
 
+    DecodeSimpleContext ds;
+
     /**
      * Properties (timestamps+side data) extracted from the last packet passed
      * for decoding.
@@ -153,6 +160,16 @@ typedef struct AVCodecInternal {
     int buffer_pkt_valid; // encoding: packet without data can be valid
     AVFrame *buffer_frame;
     int draining_done;
+    /* set to 1 when the caller is using the old decoding API */
+    int compat_decode;
+    int compat_decode_warned;
+    /* this variable is set by the decoder internals to signal to the old
+     * API compat wrappers the amount of data consumed from the last packet */
+    size_t compat_decode_consumed;
+    /* when a partial packet has been consumed, this stores the remaining size
+     * of the packet (that should be submitted in the next decode call */
+    size_t compat_decode_partial_size;
+    AVFrame *compat_decode_frame;
 } AVCodecInternal;
 
 struct AVCodecDefault {
diff --git a/libavcodec/utils.c b/libavcodec/utils.c
index b569b48f7a..8a422d7669 100644
--- a/libavcodec/utils.c
+++ b/libavcodec/utils.c
@@ -100,7 +100,7 @@ int av_codec_is_encoder(const AVCodec *codec)
 
 int av_codec_is_decoder(const AVCodec *codec)
 {
-    return codec && (codec->decode || codec->send_packet);
+    return codec && (codec->decode || codec->receive_frame);
 }
 
 av_cold void avcodec_register(AVCodec *codec)
@@ -421,6 +421,12 @@ int attribute_align_arg avcodec_open2(AVCodecContext *avctx, const AVCodec *code
         goto free_and_end;
     }
 
+    avctx->internal->compat_decode_frame = av_frame_alloc();
+    if (!avctx->internal->compat_decode_frame) {
+        ret = AVERROR(ENOMEM);
+        goto free_and_end;
+    }
+
     avctx->internal->buffer_frame = av_frame_alloc();
     if (!avctx->internal->buffer_frame) {
         ret = AVERROR(ENOMEM);
@@ -433,6 +439,12 @@ int attribute_align_arg avcodec_open2(AVCodecContext *avctx, const AVCodec *code
         goto free_and_end;
     }
 
+    avctx->internal->ds.in_pkt = av_packet_alloc();
+    if (!avctx->internal->ds.in_pkt) {
+        ret = AVERROR(ENOMEM);
+        goto free_and_end;
+    }
+
     avctx->internal->last_pkt_props = av_packet_alloc();
     if (!avctx->internal->last_pkt_props) {
         ret = AVERROR(ENOMEM);
@@ -717,9 +729,13 @@ FF_ENABLE_DEPRECATION_WARNINGS
     av_freep(&avctx->priv_data);
     if (avctx->internal) {
         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->buffer_pkt);
         av_packet_free(&avctx->internal->last_pkt_props);
+
+        av_packet_free(&avctx->internal->ds.in_pkt);
+
         av_freep(&avctx->internal->pool);
     }
     av_freep(&avctx->internal);
@@ -758,9 +774,13 @@ av_cold int avcodec_close(AVCodecContext *avctx)
         if (avctx->codec && avctx->codec->close)
             avctx->codec->close(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->buffer_pkt);
         av_packet_free(&avctx->internal->last_pkt_props);
+
+        av_packet_free(&avctx->internal->ds.in_pkt);
+
         for (i = 0; i < FF_ARRAY_ELEMS(pool->pools); i++)
             av_buffer_pool_uninit(&pool->pools[i]);
         av_freep(&avctx->internal->pool);



More information about the ffmpeg-cvslog mailing list