[PATCH 08/18] avcodec/h264dec: switch to receive_frame()

Dom Cobley via ffmpeg-devel <[email protected]>
Newsgroups gmane.comp.video.ffmpeg.devel
Message-ID <[email protected]>
A multiview access unit contains one picture per view, so decoding a single
packet can produce more than one frame. Collect finished pictures in an
AVContainerFifo and hand them out one at a time from receive_frame(), as hevcdec
does.

decode_simple_internal() fills AVFrame.pkt_dts in for the decode() callback
only, so a receive_frame() decoder has to do it itself; stamp the DTS of the
packet being decoded onto each frame as it is pushed to the fifo, again as
hevcdec does.

No functional change for single-view streams: exactly one picture per access
unit reaches the fifo, so output is unaffected.

Signed-off-by: Dom Cobley <[email protected]>
---
 libavcodec/h264dec.c | 109 ++++++++++++++++++++++++++++++++-----------
 libavcodec/h264dec.h |  14 ++++++
 2 files changed, 97 insertions(+), 26 deletions(-)

diff --git a/libavcodec/h264dec.c b/libavcodec/h264dec.c
index 608a0c8f67..1e7cb96b3b 100644
--- a/libavcodec/h264dec.c
+++ b/libavcodec/h264dec.c
@@ -31,6 +31,7 @@
 
 #include "libavutil/attributes.h"
 #include "libavutil/avassert.h"
+#include "libavutil/container_fifo.h"
 #include "libavutil/emms.h"
 #include "libavutil/imgutils.h"
 #include "libavutil/mem.h"
@@ -40,6 +41,7 @@
 #include "libavutil/video_enc_params.h"
 
 #include "codec_internal.h"
+#include "decode.h"
 #include "internal.h"
 #include "error_resilience.h"
 #include "avcodec.h"
@@ -312,6 +314,14 @@ static int h264_init_context(AVCodecContext *avctx, H264Context *h)
 
     ff_h264_sei_uninit(&h->sei);
 
+    h->output_fifo = av_container_fifo_alloc_avframe(0);
+    if (!h->output_fifo)
+        return AVERROR(ENOMEM);
+
+    h->output_frame = av_frame_alloc();
+    if (!h->output_frame)
+        return AVERROR(ENOMEM);
+
     if (avctx->active_thread_type & FF_THREAD_FRAME) {
         h->decode_error_flags_pool = av_refstruct_pool_alloc(sizeof(atomic_int), 0);
         if (!h->decode_error_flags_pool)
@@ -382,6 +392,9 @@ static av_cold int h264_decode_end(AVCodecContext *avctx)
     av_freep(&h->view_pos_available);
     h->nb_view_pos_available = 0;
 
+    av_container_fifo_free(&h->output_fifo);
+    av_frame_free(&h->output_frame);
+
     return 0;
 }
 
@@ -500,6 +513,9 @@ static av_cold void h264_decode_flush(AVCodecContext *avctx)
 
     memset(h->delayed_pic, 0, sizeof(h->delayed_pic));
 
+    av_container_fifo_drain(h->output_fifo,
+                            av_container_fifo_can_read(h->output_fifo));
+
     ff_h264_flush_change(h);
     ff_h264_sei_uninit(&h->sei);
 
@@ -945,8 +961,12 @@ static int is_avcc_extradata(const uint8_t *buf, int buf_size)
     return 1;
 }
 
-static int finalize_frame(H264Context *h, AVFrame *dst, H264Picture *out, int *got_frame)
+/**
+ * Make a finished picture available for output by pushing it onto h->output_fifo.
+ */
+static int finalize_frame(H264Context *h, H264Picture *out)
 {
+    AVFrame *dst = h->output_frame;
     int ret;
 
     if (((h->avctx->flags & AV_CODEC_FLAG_OUTPUT_CORRUPT) ||
@@ -986,8 +1006,6 @@ static int finalize_frame(H264Context *h, AVFrame *dst, H264Picture *out, int *g
         if (ret < 0)
             return ret;
 
-        *got_frame = 1;
-
         if (CONFIG_MPEGVIDEODEC) {
             ff_print_debug_info2(h->avctx, dst,
                                  out->mb_type,
@@ -995,13 +1013,23 @@ static int finalize_frame(H264Context *h, AVFrame *dst, H264Picture *out, int *g
                                  out->motion_val,
                                  out->mb_width, out->mb_height, out->mb_stride, 1);
         }
+
+        dst->pkt_dts = h->pkt_dts;
+
+        /* Transfers ownership of dst's references to the fifo. */
+        ret = av_container_fifo_write(h->output_fifo, dst, 0);
+        av_frame_unref(dst);
+        if (ret < 0)
+            return ret;
     }
 
     return 0;
 }
 
-static int send_next_delayed_frame(H264Context *h, AVFrame *dst_frame,
-                                   int *got_frame, int buf_index)
+/**
+ * Push every picture still held for reordering onto the output fifo.
+ */
+static int send_delayed_frames(H264Context *h)
 {
     int ret, i, out_idx;
     H264Picture *out;
@@ -1030,19 +1058,16 @@ static int send_next_delayed_frame(H264Context *h, AVFrame *dst_frame,
             out->recovered |= h->frame_recovered & FRAME_RECOVERED_SEI;
 
             out->reference &= ~DELAYED_PIC_REF;
-            ret = finalize_frame(h, dst_frame, out, got_frame);
+            ret = finalize_frame(h, out);
             if (ret < 0)
                 return ret;
-            if (*got_frame)
-                break;
         }
     }
 
-    return buf_index;
+    return 0;
 }
 
-static int h264_decode_frame(AVCodecContext *avctx, AVFrame *pict,
-                             int *got_frame, AVPacket *avpkt)
+static int h264_decode_packet(AVCodecContext *avctx, AVPacket *avpkt)
 {
     const uint8_t *buf = avpkt->data;
     int buf_size       = avpkt->size;
@@ -1056,10 +1081,6 @@ static int h264_decode_frame(AVCodecContext *avctx, AVFrame *pict,
 
     ff_h264_unref_picture(&h->last_pic_for_ec);
 
-    /* end of stream, output what is still in the buffers */
-    if (buf_size == 0)
-        return send_next_delayed_frame(h, pict, got_frame, 0);
-
     if (av_packet_get_side_data(avpkt, AV_PKT_DATA_NEW_EXTRADATA, NULL)) {
         size_t side_size;
         uint8_t *side = av_packet_get_side_data(avpkt, AV_PKT_DATA_NEW_EXTRADATA, &side_size);
@@ -1068,10 +1089,12 @@ static int h264_decode_frame(AVCodecContext *avctx, AVFrame *pict,
                                  avctx->err_recognition, avctx);
     }
     if (h->is_avc && buf_size >= 9 && buf[0]==1 && buf[2]==0 && (buf[4]&0xFC)==0xFC) {
-        if (is_avcc_extradata(buf, buf_size))
-            return ff_h264_decode_extradata(buf, buf_size,
-                                            &h->ps, &h->is_avc, &h->nal_length_size,
-                                            avctx->err_recognition, avctx);
+        if (is_avcc_extradata(buf, buf_size)) {
+            ret = ff_h264_decode_extradata(buf, buf_size,
+                                           &h->ps, &h->is_avc, &h->nal_length_size,
+                                           avctx->err_recognition, avctx);
+            return ret < 0 ? ret : 0;
+        }
     }
 
     buf_index = decode_nal_units(h, avpkt->buf, buf, buf_size);
@@ -1080,13 +1103,13 @@ static int h264_decode_frame(AVCodecContext *avctx, AVFrame *pict,
 
     if (!h->cur_pic_ptr && h->nal_unit_type == H264_NAL_END_SEQUENCE) {
         av_assert0(buf_index <= buf_size);
-        return send_next_delayed_frame(h, pict, got_frame, buf_index);
+        return send_delayed_frames(h);
     }
 
     if (!(avctx->flags2 & AV_CODEC_FLAG2_CHUNKS) && (!h->cur_pic_ptr || !h->has_slice)) {
         if (avctx->skip_frame >= AVDISCARD_NONREF ||
             buf_size >= 4 && !memcmp("Q264", buf, 4))
-            return buf_size;
+            return 0;
         av_log(avctx, AV_LOG_ERROR, "no frame!\n");
         return AVERROR_INVALIDDATA;
     }
@@ -1098,17 +1121,51 @@ static int h264_decode_frame(AVCodecContext *avctx, AVFrame *pict,
 
         /* Wait for second field. */
         if (h->next_output_pic) {
-            ret = finalize_frame(h, pict, h->next_output_pic, got_frame);
+            ret = finalize_frame(h, h->next_output_pic);
             if (ret < 0)
                 return ret;
         }
     }
 
-    av_assert0(pict->buf[0] || !*got_frame);
-
     ff_h264_unref_picture(&h->last_pic_for_ec);
 
-    return buf_size;
+    return 0;
+}
+
+static int h264_receive_frame(AVCodecContext *avctx, AVFrame *frame)
+{
+    H264Context      *h    = avctx->priv_data;
+    AVCodecInternal  *avci = avctx->internal;
+    AVPacket         *avpkt = avci->in_pkt;
+    int ret;
+
+    h->pkt_dts = AV_NOPTS_VALUE;
+
+    if (av_container_fifo_can_read(h->output_fifo))
+        goto do_output;
+
+    av_packet_unref(avpkt);
+    ret = ff_decode_get_packet(avctx, avpkt);
+    if (ret == AVERROR_EOF) {
+        /* end of stream, output what is still held for reordering */
+        ret = send_delayed_frames(h);
+        if (ret < 0)
+            return ret;
+        goto do_output;
+    } else if (ret < 0)
+        return ret;
+
+    h->pkt_dts = avpkt->dts;
+
+    ret = h264_decode_packet(avctx, avpkt);
+    if (ret < 0)
+        return ret;
+
+do_output:
+    if (av_container_fifo_read(h->output_fifo, frame, 0) >= 0)
+        return 0;
+
+    return avci->draining ? AVERROR_EOF : AVERROR(EAGAIN);
 }
 
 #define OFFSET(x) offsetof(H264Context, x)
@@ -1151,7 +1208,7 @@ const FFCodec ff_h264_decoder = {
     .priv_data_size        = sizeof(H264Context),
     .init                  = h264_decode_init,
     .close                 = h264_decode_end,
-    FF_CODEC_DECODE_CB(h264_decode_frame),
+    FF_CODEC_RECEIVE_FRAME_CB(h264_receive_frame),
     .p.capabilities        = AV_CODEC_CAP_DR1 |
                              AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS |
                              AV_CODEC_CAP_FRAME_THREADS,
diff --git a/libavcodec/h264dec.h b/libavcodec/h264dec.h
index 3d6f133489..e814501b2c 100644
--- a/libavcodec/h264dec.h
+++ b/libavcodec/h264dec.h
@@ -585,6 +585,20 @@ typedef struct H264Context {
      */
     /** Per-view decoding state, indexed by view order index (VOIdx). */
     H264ViewContext views[H264_MAX_MVC_VIEWS];
+
+    /**
+     * Frames ready for output. A multiview access unit produces one frame per
+     * view, so a single packet can yield more than one frame.
+     */
+    struct AVContainerFifo *output_fifo;
+    /** Scratch frame used to hand a finished picture to ::output_fifo. */
+    AVFrame *output_frame;
+    /**
+     * DTS of the packet being decoded, stamped onto every frame pushed to
+     * ::output_fifo. receive_frame() decoders do this themselves; the generic
+     * layer only fills AVFrame.pkt_dts in for the decode() callback.
+     */
+    int64_t pkt_dts;
     /**
      * Number of views in the active subset SPS, or 0 if the stream is not
      * multiview (or its MVC extension is unsupported).
-- 
2.53.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.