[FFmpeg-cvslog] h264: fix decoding multiple fields per packet with slice threads

Anton Khirnov git at videolan.org
Tue Jan 24 17:18:11 EET 2017


ffmpeg | branch: master | Anton Khirnov <anton at khirnov.net> | Sun Jun 12 14:22:50 2016 +0200| [4a9bab3db0d9ec449ebc8b5e823374d1d1df7761] | committer: Anton Khirnov

h264: fix decoding multiple fields per packet with slice threads

Since we only know whether a NAL unit corresponds to a new field after
parsing the slice header, this requires reorganizing the calls to slice
parsing, per-slice/field/frame init and actual decoding.

In the previous code, the function for slice header decoding also
immediately started a new field/frame as necessary, so any slices
already queued for decoding would no longer be decodable.

After this patch, we first parse the slice header, and if we determine
that a new field needs to be started we decode all the queued slices.

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

 libavcodec/h264_slice.c | 129 ++++++++++++++++++++++++++++++------------------
 libavcodec/h264dec.c    |  47 ++++++------------
 libavcodec/h264dec.h    |  12 +++--
 3 files changed, 105 insertions(+), 83 deletions(-)

diff --git a/libavcodec/h264_slice.c b/libavcodec/h264_slice.c
index 8adbe21..8d79740 100644
--- a/libavcodec/h264_slice.c
+++ b/libavcodec/h264_slice.c
@@ -1699,50 +1699,13 @@ static int h264_slice_header_parse(H264SliceContext *sl, const H2645NAL *nal,
     return 0;
 }
 
-/**
- * Decode a slice header.
- * This will (re)initialize the decoder and call h264_frame_start() as needed.
- *
- * @param h h264context
- *
- * @return 0 if okay, <0 if an error occurred
- */
-int ff_h264_decode_slice_header(H264Context *h, H264SliceContext *sl,
-                                const H2645NAL *nal)
+/* do all the per-slice initialization needed before we can start decoding the
+ * actual MBs */
+static int h264_slice_init(H264Context *h, H264SliceContext *sl,
+                           const H2645NAL *nal)
 {
     int i, j, ret = 0;
 
-    ret = h264_slice_header_parse(sl, nal, &h->ps, h->avctx);
-    if (ret < 0)
-        return ret;
-
-    // discard redundant pictures
-    if (sl->redundant_pic_count > 0)
-        return 0;
-
-    if (!h->setup_finished) {
-        if (sl->first_mb_addr == 0) { // FIXME better field boundary detection
-            if (h->current_slice && h->cur_pic_ptr && FIELD_PICTURE(h)) {
-                ff_h264_field_end(h, sl, 1);
-            }
-
-            h->current_slice = 0;
-            if (!h->first_field) {
-                if (h->cur_pic_ptr && !h->droppable) {
-                    ff_thread_report_progress(&h->cur_pic_ptr->tf, INT_MAX,
-                                              h->picture_structure == PICT_BOTTOM_FIELD);
-                }
-                h->cur_pic_ptr = NULL;
-            }
-        }
-
-        if (h->current_slice == 0) {
-            ret = h264_field_start(h, sl, nal);
-            if (ret < 0)
-                return ret;
-        }
-    }
-
     if (h->current_slice > 0) {
         if (h->ps.pps != (const PPS*)h->ps.pps_list[sl->pps_id]->data) {
             av_log(h->avctx, AV_LOG_ERROR, "PPS changed between slices\n");
@@ -1886,6 +1849,75 @@ int ff_h264_decode_slice_header(H264Context *h, H264SliceContext *sl,
     return 0;
 }
 
+int ff_h264_queue_decode_slice(H264Context *h, const H2645NAL *nal)
+{
+    H264SliceContext *sl = h->slice_ctx + h->nb_slice_ctx_queued;
+    int ret;
+
+    sl->gb = nal->gb;
+
+    ret = h264_slice_header_parse(sl, nal, &h->ps, h->avctx);
+    if (ret < 0)
+        return ret;
+
+    // discard redundant pictures
+    if (sl->redundant_pic_count > 0)
+        return 0;
+
+    if (!h->setup_finished) {
+        if (sl->first_mb_addr == 0) { // FIXME better field boundary detection
+            // this slice starts a new field
+            // first decode any pending queued slices
+            if (h->nb_slice_ctx_queued) {
+                H264SliceContext tmp_ctx;
+
+                ret = ff_h264_execute_decode_slices(h);
+                if (ret < 0 && (h->avctx->err_recognition & AV_EF_EXPLODE))
+                    return ret;
+
+                memcpy(&tmp_ctx, h->slice_ctx, sizeof(tmp_ctx));
+                memcpy(h->slice_ctx, sl, sizeof(tmp_ctx));
+                memcpy(sl, &tmp_ctx, sizeof(tmp_ctx));
+                sl = h->slice_ctx;
+            }
+
+            if (h->current_slice && h->cur_pic_ptr && FIELD_PICTURE(h)) {
+                ff_h264_field_end(h, sl, 1);
+            }
+
+            h->current_slice = 0;
+            if (!h->first_field) {
+                if (h->cur_pic_ptr && !h->droppable) {
+                    ff_thread_report_progress(&h->cur_pic_ptr->tf, INT_MAX,
+                                              h->picture_structure == PICT_BOTTOM_FIELD);
+                }
+                h->cur_pic_ptr = NULL;
+            }
+        }
+
+        if (h->current_slice == 0) {
+            ret = h264_field_start(h, sl, nal);
+            if (ret < 0)
+                return ret;
+        }
+    }
+
+    ret = h264_slice_init(h, sl, nal);
+    if (ret < 0)
+        return ret;
+
+    if ((h->avctx->skip_frame < AVDISCARD_NONREF || nal->ref_idc) &&
+        (h->avctx->skip_frame < AVDISCARD_BIDIR  ||
+         sl->slice_type_nos != AV_PICTURE_TYPE_B) &&
+        (h->avctx->skip_frame < AVDISCARD_NONKEY ||
+         h->cur_pic_ptr->f->key_frame) &&
+        h->avctx->skip_frame < AVDISCARD_ALL) {
+        h->nb_slice_ctx_queued++;
+    }
+
+    return 0;
+}
+
 int ff_h264_get_slice_type(const H264SliceContext *sl)
 {
     switch (sl->slice_type) {
@@ -2452,25 +2484,26 @@ finish:
  * Call decode_slice() for each context.
  *
  * @param h h264 master context
- * @param context_count number of contexts to execute
  */
-int ff_h264_execute_decode_slices(H264Context *h, unsigned context_count)
+int ff_h264_execute_decode_slices(H264Context *h)
 {
     AVCodecContext *const avctx = h->avctx;
     H264SliceContext *sl;
+    int context_count = h->nb_slice_ctx_queued;
+    int ret = 0;
     int i, j;
 
-    if (h->avctx->hwaccel)
+    if (h->avctx->hwaccel || context_count < 1)
         return 0;
     if (context_count == 1) {
-        int ret;
 
         h->slice_ctx[0].next_slice_idx = h->mb_width * h->mb_height;
         h->postpone_filter = 0;
 
         ret = decode_slice(avctx, &h->slice_ctx[0]);
         h->mb_y = h->slice_ctx[0].mb_y;
-        return ret;
+        if (ret < 0)
+            goto finish;
     } else {
         for (i = 0; i < context_count; i++) {
             int next_slice_idx = h->mb_width * h->mb_height;
@@ -2520,5 +2553,7 @@ int ff_h264_execute_decode_slices(H264Context *h, unsigned context_count)
         }
     }
 
-    return 0;
+finish:
+    h->nb_slice_ctx_queued = 0;
+    return ret;
 }
diff --git a/libavcodec/h264dec.c b/libavcodec/h264dec.c
index 3ce76ea..1086eab 100644
--- a/libavcodec/h264dec.c
+++ b/libavcodec/h264dec.c
@@ -508,7 +508,6 @@ static int get_last_needed_nal(H264Context *h)
 static int decode_nal_units(H264Context *h, const uint8_t *buf, int buf_size)
 {
     AVCodecContext *const avctx = h->avctx;
-    unsigned context_count = 0;
     int nals_needed = 0; ///< number of NALs that need decoding before the next frame thread starts
     int i, ret = 0;
 
@@ -532,8 +531,7 @@ static int decode_nal_units(H264Context *h, const uint8_t *buf, int buf_size)
 
     for (i = 0; i < h->pkt.nb_nals; i++) {
         H2645NAL *nal = &h->pkt.nals[i];
-        H264SliceContext *sl = &h->slice_ctx[context_count];
-        int err;
+        int max_slice_ctx, err;
 
         if (avctx->skip_frame >= AVDISCARD_NONREF &&
             nal->ref_idc == 0 && nal->type != H264_NAL_SEI)
@@ -548,12 +546,7 @@ static int decode_nal_units(H264Context *h, const uint8_t *buf, int buf_size)
         case H264_NAL_IDR_SLICE:
             idr(h); // FIXME ensure we don't lose some frames if there is reordering
         case H264_NAL_SLICE:
-            sl->gb = nal->gb;
-
-            if ((err = ff_h264_decode_slice_header(h, sl, nal)))
-                break;
-
-            if (sl->redundant_pic_count > 0)
+            if ((err = ff_h264_queue_decode_slice(h, nal)))
                 break;
 
             if (avctx->active_thread_type & FF_THREAD_FRAME && !h->avctx->hwaccel &&
@@ -562,18 +555,14 @@ static int decode_nal_units(H264Context *h, const uint8_t *buf, int buf_size)
                 h->setup_finished = 1;
             }
 
-            if ((avctx->skip_frame < AVDISCARD_NONREF || nal->ref_idc) &&
-                (avctx->skip_frame < AVDISCARD_BIDIR  ||
-                 sl->slice_type_nos != AV_PICTURE_TYPE_B) &&
-                (avctx->skip_frame < AVDISCARD_NONKEY ||
-                 h->cur_pic_ptr->f->key_frame) &&
-                avctx->skip_frame < AVDISCARD_ALL) {
-                if (avctx->hwaccel) {
+            max_slice_ctx = avctx->hwaccel ? 1 : h->nb_slice_ctx;
+            if (h->nb_slice_ctx_queued == max_slice_ctx) {
+                if (avctx->hwaccel)
                     ret = avctx->hwaccel->decode_slice(avctx, nal->raw_data, nal->raw_size);
-                    if (ret < 0)
-                        return ret;
-                } else
-                    context_count++;
+                else
+                    ret = ff_h264_execute_decode_slices(h);
+                if (ret < 0 && (h->avctx->err_recognition & AV_EF_EXPLODE))
+                    goto end;
             }
             break;
         case H264_NAL_DPA:
@@ -611,23 +600,14 @@ static int decode_nal_units(H264Context *h, const uint8_t *buf, int buf_size)
                    nal->type, nal->size_bits);
         }
 
-        if (context_count == h->nb_slice_ctx) {
-            ret = ff_h264_execute_decode_slices(h, context_count);
-            if (ret < 0 && (h->avctx->err_recognition & AV_EF_EXPLODE))
-                goto end;
-            context_count = 0;
-        }
-
         if (err < 0) {
             av_log(h->avctx, AV_LOG_ERROR, "decode_slice_header error\n");
-            sl->ref_count[0] = sl->ref_count[1] = sl->list_count = 0;
         }
     }
-    if (context_count) {
-        ret = ff_h264_execute_decode_slices(h, context_count);
-        if (ret < 0 && (h->avctx->err_recognition & AV_EF_EXPLODE))
-            goto end;
-    }
+
+    ret = ff_h264_execute_decode_slices(h);
+    if (ret < 0 && (h->avctx->err_recognition & AV_EF_EXPLODE))
+        goto end;
 
     ret = 0;
 end:
@@ -687,6 +667,7 @@ static int h264_decode_frame(AVCodecContext *avctx, void *data,
 
     h->flags = avctx->flags;
     h->setup_finished = 0;
+    h->nb_slice_ctx_queued = 0;
 
     /* end of stream, output what is still in the buffers */
 out:
diff --git a/libavcodec/h264dec.h b/libavcodec/h264dec.h
index 6f5ab41..f934fc4 100644
--- a/libavcodec/h264dec.h
+++ b/libavcodec/h264dec.h
@@ -345,6 +345,7 @@ typedef struct H264Context {
 
     H264SliceContext *slice_ctx;
     int            nb_slice_ctx;
+    int            nb_slice_ctx_queued;
 
     H2645Packet pkt;
 
@@ -793,9 +794,14 @@ int ff_h264_slice_context_init(H264Context *h, H264SliceContext *sl);
 
 void ff_h264_draw_horiz_band(const H264Context *h, H264SliceContext *sl, int y, int height);
 
-int ff_h264_decode_slice_header(H264Context *h, H264SliceContext *sl,
-                                const H2645NAL *nal);
-int ff_h264_execute_decode_slices(H264Context *h, unsigned context_count);
+/**
+ * Submit a slice for decoding.
+ *
+ * Parse the slice header, starting a new field/frame if necessary. If any
+ * slices are queued for the previous field, they are decoded.
+ */
+int ff_h264_queue_decode_slice(H264Context *h, const H2645NAL *nal);
+int ff_h264_execute_decode_slices(H264Context *h);
 int ff_h264_update_thread_context(AVCodecContext *dst,
                                   const AVCodecContext *src);
 



More information about the ffmpeg-cvslog mailing list