[PATCH 09/18] avcodec/h264: decode dependent views and inter-view references

Dom Cobley via ffmpeg-devel <[email protected]>
Newsgroups gmane.comp.video.ffmpeg.devel
Message-ID <[email protected]>
Route coded slice extensions (NAL 20) through the normal slice path, switching to
the view named by the MVC NAL unit header when one begins, and build the
inter-view part of the initial reference picture lists as per H.8.2.1: the views
a dependent view may predict from are listed by view_id in the MVC extension of
its subset SPS, separately for anchor and non-anchor pictures, and the picture
used is the one that view holds for the access unit being decoded.

Note that whether a picture may be used for inter-view prediction is signalled by
inter_view_flag, independently of whether it is a temporal reference picture, so
a disposable picture of another view is still available as an inter-view
reference. Its H264Picture.reference is therefore not consulted.

Inter-view references are treated as long term ones for temporal direct
prediction (H.8.2.1); the test for that is hoisted out of the per-macroblock code
into H264SliceContext.col_long_ref so this costs nothing per macroblock.

A dependent view activates the subset SPS with the id its PPS names, and a
dependent view IDR is signalled by non_idr_flag in the MVC NAL unit header rather
than by the NAL unit type, so IDR detection goes through a helper. Reinitialising
the decoding context per view is not supported: a dependent view SPS must agree
with the base view on frame size, bit depth and chroma format.

Output ordering is not yet correct when more than one view is decoded: both views
of an access unit share a POC and currently go through the same reorder buffer,
so they are interleaved arbitrarily. Pairing the views up at output time is done
separately. Decoding only the base view, which remains the default, is
unaffected and bit-exact.

Signed-off-by: Dom Cobley <[email protected]>
---
 libavcodec/h264_direct.c |  15 ++++--
 libavcodec/h264_refs.c   | 107 +++++++++++++++++++++++++++++++++++++--
 libavcodec/h264_slice.c  | 102 +++++++++++++++++++++++++++++++------
 libavcodec/h264dec.c     |  22 ++++++++
 libavcodec/h264dec.h     |  35 +++++++++++++
 5 files changed, 257 insertions(+), 24 deletions(-)

diff --git a/libavcodec/h264_direct.c b/libavcodec/h264_direct.c
index be388ada3a..c783bc9339 100644
--- a/libavcodec/h264_direct.c
+++ b/libavcodec/h264_direct.c
@@ -44,7 +44,8 @@ static int get_scale_factor(const H264SliceContext *sl,
     if (pocdiff != (int)pocdiff)
         avpriv_request_sample(sl->h264->avctx, "pocdiff overflow");
 
-    if (td == 0 || sl->ref_list[0][i].parent->long_ref) {
+    if (td == 0 || sl->ref_list[0][i].parent->long_ref ||
+        sl->ref_list[0][i].inter_view) {
         return 256;
     } else {
         int64_t pocdiff0 = poc - (int64_t)poc0;
@@ -125,6 +126,12 @@ void ff_h264_direct_ref_list_init(const H264Context *const h, H264SliceContext *
     int sidx     = (h->picture_structure & 1) ^ 1;
     int ref1sidx = (ref1->reference      & 1) ^ 1;
 
+    /* Hoisted out of the per-macroblock code below. An inter-view reference is
+     * treated as a long term one for temporal direct prediction, cf. H.8.2.1, so
+     * the colocated motion vectors of such a picture are not used. */
+    sl->col_long_ref = ref1->parent ? ref1->parent->long_ref || ref1->inter_view
+                                    : 0;
+
     /* Updates to cur_pic are not safe once ff_thread_finish_setup() has been
      * called (other threads may already be reading these fields). */
     if (!h->setup_finished) {
@@ -380,7 +387,7 @@ single_col:
                            (uint8_t)ref[0], 1);
             fill_rectangle(&sl->ref_cache[1][scan8[i8 * 4]], 2, 2, 8,
                            (uint8_t)ref[1], 1);
-            if (!IS_INTRA(mb_type_col[y8]) && !sl->ref_list[1][0].parent->long_ref &&
+            if (!IS_INTRA(mb_type_col[y8]) && !sl->col_long_ref &&
                 ((l1ref0[xy8] == 0 &&
                   FFABS(l1mv0[xy4][0]) <= 1 &&
                   FFABS(l1mv0[xy4][1]) <= 1) ||
@@ -411,7 +418,7 @@ single_col:
 
         fill_rectangle(&sl->ref_cache[0][scan8[0]], 4, 4, 8, (uint8_t)ref[0], 1);
         fill_rectangle(&sl->ref_cache[1][scan8[0]], 4, 4, 8, (uint8_t)ref[1], 1);
-        if (!IS_INTRA(mb_type_col[0]) && !sl->ref_list[1][0].parent->long_ref &&
+        if (!IS_INTRA(mb_type_col[0]) && !sl->col_long_ref &&
             ((l1ref0[0] == 0 &&
               FFABS(l1mv0[0][0]) <= 1 &&
               FFABS(l1mv0[0][1]) <= 1) ||
@@ -449,7 +456,7 @@ single_col:
 
             assert(b8_stride == 2);
             /* col_zero_flag */
-            if (!IS_INTRA(mb_type_col[0]) && !sl->ref_list[1][0].parent->long_ref &&
+            if (!IS_INTRA(mb_type_col[0]) && !sl->col_long_ref &&
                 (l1ref0[i8] == 0 ||
                  (l1ref0[i8] < 0 &&
                   l1ref1[i8] == 0 &&
diff --git a/libavcodec/h264_refs.c b/libavcodec/h264_refs.c
index 99f86aa2da..05cdc07c58 100644
--- a/libavcodec/h264_refs.c
+++ b/libavcodec/h264_refs.c
@@ -51,9 +51,10 @@ static void ref_from_h264pic(H264Ref *dst, const H264Picture *src)
 {
     memcpy(dst->data,     src->f->data,     sizeof(dst->data));
     memcpy(dst->linesize, src->f->linesize, sizeof(dst->linesize));
-    dst->reference = src->reference;
-    dst->poc       = src->poc;
-    dst->pic_id    = src->pic_id;
+    dst->reference  = src->reference;
+    dst->poc        = src->poc;
+    dst->pic_id     = src->pic_id;
+    dst->inter_view = 0;
     dst->parent = src;
 }
 
@@ -130,8 +131,76 @@ static int mismatches_ref(const H264Context *h, const H264Picture *pic)
             h->cur_pic_ptr->f->format != f->format);
 }
 
+/**
+ * Append the inter-view reference pictures to an initial reference picture list,
+ * cf. H.8.2.1.
+ *
+ * The dependent views this view may predict from are listed in the MVC extension
+ * of its subset SPS, by view_id, in a separate list for anchor and non-anchor
+ * pictures. The picture used is the one that view holds for the access unit being
+ * decoded, which is exactly what H264ViewContext.cur_pic_ptr records.
+ */
+static int build_inter_view_list(H264Context *h, H264Ref *dst, int dst_len,
+                                 int list, int anchor)
+{
+    const SPS *sps = h->ps.sps;
+    const SPSMVCExt *mvc = &sps->mvc;
+    const unsigned voidx = h->cur_view;
+    int nb_refs, index = 0;
+    const uint16_t *refs;
+
+    if (!sps->is_subset || voidx == 0 || voidx >= mvc->num_views)
+        return 0;
+
+    if (anchor) {
+        nb_refs = mvc->num_anchor_refs[list][voidx];
+        refs    = mvc->anchor_ref[list][voidx];
+    } else {
+        nb_refs = mvc->num_non_anchor_refs[list][voidx];
+        refs    = mvc->non_anchor_ref[list][voidx];
+    }
+
+    for (int i = 0; i < nb_refs && index < dst_len; i++) {
+        int ref_voidx = ff_h264_view_idx(h, refs[i]);
+        H264Picture *pic;
+
+        if (ref_voidx < 0 || ref_voidx >= FF_ARRAY_ELEMS(h->views)) {
+            av_log(h->avctx, AV_LOG_WARNING,
+                   "Inter-view reference view_id %u of view %u is not present\n",
+                   refs[i], voidx);
+            continue;
+        }
+
+        pic = h->views[ref_voidx].cur_pic_ptr;
+        if (!pic || !pic->f->buf[0]) {
+            av_log(h->avctx, AV_LOG_WARNING,
+                   "Inter-view reference for view %u is missing\n", voidx);
+            continue;
+        }
+
+        ref_from_h264pic(&dst[index], pic);
+
+        /* Whether a picture may be used for inter-view prediction is signalled by
+         * inter_view_flag, independently of whether it is a temporal reference
+         * picture; a disposable picture of another view is still available here.
+         * So do not take H264Picture.reference, which may well be 0, and mark the
+         * reference as present for the current picture structure instead. */
+        dst[index].reference = h->picture_structure;
+        if (h->picture_structure != PICT_FRAME)
+            pic_as_field(&dst[index], h->picture_structure);
+
+        /* Inter-view references behave as long term ones for the purposes of
+         * temporal direct prediction, cf. H.8.2.1. */
+        dst[index].inter_view = 1;
+        index++;
+    }
+
+    return index;
+}
+
 static void h264_initialise_ref_list(H264Context *h, H264SliceContext *sl)
 {
+    const int anchor = sl->anchor_pic_flag;
     int len;
 
     if (sl->slice_type_nos == AV_PICTURE_TYPE_B) {
@@ -154,6 +223,9 @@ static void h264_initialise_ref_list(H264Context *h, H264SliceContext *sl)
             len += build_def_list(sl->ref_list[list] + len,
                                   FF_ARRAY_ELEMS(sl->ref_list[0]) - len,
                                   h->long_ref, 16, 1, h->picture_structure);
+            len += build_inter_view_list(h, sl->ref_list[list] + len,
+                                         FF_ARRAY_ELEMS(sl->ref_list[0]) - len,
+                                         list, anchor);
             av_assert0(len <= 32);
 
             memset(&sl->ref_list[list][len], 0, sizeof(H264Ref) * (32 - len));
@@ -175,6 +247,9 @@ static void h264_initialise_ref_list(H264Context *h, H264SliceContext *sl)
         len += build_def_list(sl->ref_list[0] + len,
                               FF_ARRAY_ELEMS(sl->ref_list[0]) - len,
                               h-> long_ref, 16, 1, h->picture_structure);
+        len += build_inter_view_list(h, sl->ref_list[0] + len,
+                                     FF_ARRAY_ELEMS(sl->ref_list[0]) - len,
+                                     0, anchor);
         av_assert0(len <= 32);
 
         memset(&sl->ref_list[0][len], 0, sizeof(H264Ref) * (32 - len));
@@ -585,6 +660,30 @@ void ff_h264_remove_all_refs(H264Context *h)
     memset(h->default_ref, 0, sizeof(h->default_ref));
 }
 
+int ff_h264_view_idx(const H264Context *h, int view_id)
+{
+    const SPS *sps = h->ps.pps ? h->ps.pps->sps_mvc : NULL;
+
+    /* The base view's PPS may not point at a subset SPS, so fall back to any
+     * subset SPS that declares views. */
+    if (!sps || sps->mvc.num_views < 2) {
+        sps = NULL;
+        for (int i = 0; i < MAX_SPS_COUNT; i++)
+            if (h->ps.subset_sps_list[i] && h->ps.subset_sps_list[i]->mvc.num_views > 1) {
+                sps = h->ps.subset_sps_list[i];
+                break;
+            }
+    }
+    if (!sps)
+        return AVERROR_INVALIDDATA;
+
+    for (int i = 0; i < sps->mvc.num_views; i++)
+        if (sps->mvc.view_id[i] == view_id)
+            return i;
+
+    return AVERROR_INVALIDDATA;
+}
+
 void ff_h264_view_switch(H264Context *h, unsigned view)
 {
     H264ViewContext *v;
@@ -880,7 +979,7 @@ int ff_h264_decode_ref_pic_marking(H264SliceContext *sl, GetBitContext *gb,
     MMCO *mmco = sl->mmco;
     int nb_mmco = 0;
 
-    if (nal->type == H264_NAL_IDR_SLICE) { // FIXME fields
+    if (ff_h264_nal_is_idr(nal)) { // FIXME fields
         skip_bits1(gb); // broken_link
         if (get_bits1(gb)) {
             mmco[0].opcode   = MMCO_LONG;
diff --git a/libavcodec/h264_slice.c b/libavcodec/h264_slice.c
index 98d5ebfdb7..33cb20f922 100644
--- a/libavcodec/h264_slice.c
+++ b/libavcodec/h264_slice.c
@@ -1225,6 +1225,27 @@ static int h264_init_ps(H264Context *h, const H264SliceContext *sl, int first_sl
     if (first_slice)
         av_refstruct_replace(&h->ps.pps, h->ps.pps_list[sl->pps_id]);
 
+    if (h->cur_view) {
+        /* A dependent view activates the subset SPS with this id. It has to agree
+         * with the base view on everything the decoding context is sized and
+         * configured from -- reinitialising per view is not supported -- so
+         * validate that and then skip the whole reinit path below. */
+        const SPS *mvc_sps = h->ps.pps->sps_mvc ? h->ps.pps->sps_mvc : h->ps.pps->sps;
+
+        if (mvc_sps->mb_width          != h->mb_width             ||
+            mvc_sps->mb_height         != h->mb_height            ||
+            mvc_sps->bit_depth_luma    != h->cur_bit_depth_luma   ||
+            mvc_sps->chroma_format_idc != h->cur_chroma_format_idc) {
+            av_log(h->avctx, AV_LOG_ERROR,
+                   "Base and dependent view SPS have an unsupported parameter "
+                   "combination\n");
+            return AVERROR(ENOSYS);
+        }
+
+        h->ps.sps = mvc_sps;
+        return 0;
+    }
+
     if (h->ps.sps != h->ps.pps->sps) {
         h->ps.sps = h->ps.pps->sps;
 
@@ -1598,7 +1619,7 @@ static int h264_field_start(H264Context *h, const H264SliceContext *sl,
     h->poc.delta_poc[0]     = sl->delta_poc[0];
     h->poc.delta_poc[1]     = sl->delta_poc[1];
 
-    if (nal->type == H264_NAL_IDR_SLICE)
+    if (ff_h264_nal_is_idr(nal))
         h->poc_offset = sl->idr_pic_id;
     else if (h->picture_intra_only)
         h->poc_offset = 0;
@@ -1827,7 +1848,7 @@ static int h264_field_start(H264Context *h, const H264SliceContext *sl,
     h->nb_mmco = sl->nb_mmco;
     h->explicit_ref_marking = sl->explicit_ref_marking;
 
-    h->picture_idr = nal->type == H264_NAL_IDR_SLICE;
+    h->picture_idr = ff_h264_nal_is_idr(nal);
 
     if (h->sei.recovery_point.recovery_frame_cnt >= 0) {
         const int sei_recovery_frame_cnt = h->sei.recovery_point.recovery_frame_cnt;
@@ -1844,9 +1865,11 @@ static int h264_field_start(H264Context *h, const H264SliceContext *sl,
         }
     }
 
-    h->cur_pic_ptr->f->flags |= AV_FRAME_FLAG_KEY * !!(nal->type == H264_NAL_IDR_SLICE);
+    h->views[h->cur_view].cur_pic_ptr = h->cur_pic_ptr;
 
-    if (nal->type == H264_NAL_IDR_SLICE) {
+    h->cur_pic_ptr->f->flags |= AV_FRAME_FLAG_KEY * !!(ff_h264_nal_is_idr(nal));
+
+    if (ff_h264_nal_is_idr(nal)) {
         h->cur_pic_ptr->recovered |= FRAME_RECOVERED_IDR;
         // If we have an IDR, all frames after it in decoded order are
         // "recovered".
@@ -1881,14 +1904,15 @@ static int h264_field_start(H264Context *h, const H264SliceContext *sl,
 }
 
 static int h264_slice_header_parse(const H264Context *h, H264SliceContext *sl,
-                                   const H2645NAL *nal)
+                                   const H2645NAL *nal, unsigned view)
 {
     const SPS *sps;
     const PPS *pps;
     int ret;
     unsigned int slice_type, tmp, i;
     int field_pic_flag, bottom_field_flag;
-    int first_slice = sl == h->slice_ctx && !h->current_slice;
+    int first_slice = sl == h->slice_ctx &&
+                      (!h->current_slice || view != h->cur_view);
     int picture_structure;
 
     if (first_slice)
@@ -1913,7 +1937,7 @@ static int h264_slice_header_parse(const H264Context *h, H264SliceContext *sl,
     sl->slice_type     = slice_type;
     sl->slice_type_nos = slice_type & 3;
 
-    if (nal->type  == H264_NAL_IDR_SLICE &&
+    if (nal->type == H264_NAL_IDR_SLICE &&
         sl->slice_type_nos != AV_PICTURE_TYPE_I) {
         av_log(h->avctx, AV_LOG_ERROR, "A non-intra slice in an IDR NAL unit.\n");
         return AVERROR_INVALIDDATA;
@@ -1931,7 +1955,18 @@ static int h264_slice_header_parse(const H264Context *h, H264SliceContext *sl,
         return AVERROR_INVALIDDATA;
     }
     pps = h->ps.pps_list[sl->pps_id];
-    sps = pps->sps;
+    /* A coded slice extension activates the subset SPS with this id, an ordinary
+     * slice the plain SPS, cf. H.7.4.1.2.1. */
+    sps = (nal->type == H264_NAL_EXTEN_SLICE && pps->sps_mvc) ? pps->sps_mvc
+                                                              : pps->sps;
+    if (nal->type == H264_NAL_EXTEN_SLICE && !sps->is_subset) {
+        av_log(h->avctx, AV_LOG_ERROR,
+               "PPS %u of a coded slice extension does not refer to a subset SPS\n",
+               sl->pps_id);
+        return AVERROR_INVALIDDATA;
+    }
+
+    sl->anchor_pic_flag = nal->anchor_pic_flag;
 
     sl->frame_num = get_bits(&sl->gb, sps->log2_max_frame_num);
     if (!first_slice) {
@@ -1970,7 +2005,7 @@ static int h264_slice_header_parse(const H264Context *h, H264SliceContext *sl,
         sl->max_pic_num  = 1 << (sps->log2_max_frame_num + 1);
     }
 
-    if (nal->type == H264_NAL_IDR_SLICE) {
+    if (ff_h264_nal_is_idr(nal)) {
         unsigned idr_pic_id = get_ue_golomb_long(&sl->gb);
         if (idr_pic_id < 65536) {
             sl->idr_pic_id = idr_pic_id;
@@ -2104,7 +2139,7 @@ static int h264_slice_init(H264Context *h, H264SliceContext *sl,
 {
     int i, j, ret = 0;
 
-    if (h->picture_idr && nal->type != H264_NAL_IDR_SLICE) {
+    if (h->picture_idr && !ff_h264_nal_is_idr(nal)) {
         av_log(h->avctx, AV_LOG_ERROR, "Invalid mix of IDR and non-IDR slices\n");
         return AVERROR_INVALIDDATA;
     }
@@ -2237,7 +2272,7 @@ static int h264_slice_init(H264Context *h, H264SliceContext *sl,
                sl->mb_y * h->mb_width + sl->mb_x,
                av_get_picture_type_char(sl->slice_type),
                sl->slice_type_fixed ? " fix" : "",
-               nal->type == H264_NAL_IDR_SLICE ? " IDR" : "",
+               ff_h264_nal_is_idr(nal) ? " IDR" : "",
                h->poc.frame_num,
                h->cur_pic_ptr->field_poc[0],
                h->cur_pic_ptr->field_poc[1],
@@ -2256,12 +2291,30 @@ static int h264_slice_init(H264Context *h, H264SliceContext *sl,
 int ff_h264_queue_decode_slice(H264Context *h, const H2645NAL *nal)
 {
     H264SliceContext *sl = h->slice_ctx + h->nb_slice_ctx_queued;
-    int first_slice = sl == h->slice_ctx && !h->current_slice;
+    int first_slice;
+    unsigned view;
     int ret;
 
+    /* Which view does this slice belong to? A slice that is not a coded slice
+     * extension is always the base view, cf. H.7.4.1.1. */
+    if (nal->type == H264_NAL_EXTEN_SLICE) {
+        int voidx = ff_h264_view_idx(h, nal->view_id);
+
+        if (voidx <= 0 || !(h->views_active_decode & (1 << voidx))) {
+            /* not a view we know about or were asked to decode */
+            sl->ref_count[0] = sl->ref_count[1] = 0;
+            return 0;
+        }
+        view = voidx;
+    } else
+        view = 0;
+
+    first_slice = sl == h->slice_ctx &&
+                  (!h->current_slice || view != h->cur_view);
+
     sl->gb = nal->gb;
 
-    ret = h264_slice_header_parse(h, sl, nal);
+    ret = h264_slice_header_parse(h, sl, nal, view);
     if (ret < 0)
         return ret;
 
@@ -2271,7 +2324,7 @@ int ff_h264_queue_decode_slice(H264Context *h, const H2645NAL *nal)
         return 0;
     }
 
-    if (sl->first_mb_addr == 0 || !h->current_slice) {
+    if ((sl->first_mb_addr == 0 || !h->current_slice) && view == h->cur_view) {
         if (h->setup_finished) {
             av_log(h->avctx, AV_LOG_ERROR, "Too many fields\n");
             return AVERROR_INVALIDDATA;
@@ -2295,7 +2348,19 @@ int ff_h264_queue_decode_slice(H264Context *h, const H2645NAL *nal)
                 sl = h->slice_ctx;
             }
 
-            if (h->cur_pic_ptr && FIELD_PICTURE(h) && h->first_field) {
+            if (view != h->cur_view) {
+                /* A new view of the same access unit: finish off the picture of
+                 * the view we were decoding and start a fresh one. */
+                if (h->cur_pic_ptr) {
+                    ret = ff_h264_field_end(h, h->slice_ctx, 1);
+                    if (ret < 0)
+                        return ret;
+                    h->cur_pic_ptr = NULL;
+                }
+                h->current_slice = 0;
+                h->first_field   = 0;
+                ff_h264_view_switch(h, view);
+            } else if (h->cur_pic_ptr && FIELD_PICTURE(h) && h->first_field) {
                 ret = ff_h264_field_end(h, h->slice_ctx, 1);
                 if (ret < 0)
                     return ret;
@@ -2323,6 +2388,11 @@ int ff_h264_queue_decode_slice(H264Context *h, const H2645NAL *nal)
     if (!h->current_slice)
         av_assert0(sl == h->slice_ctx);
 
+    /* Returning to the base view for a new access unit, or starting a view whose
+     * predecessor produced no picture. */
+    if (!h->current_slice)
+        ff_h264_view_switch(h, view);
+
     if (h->current_slice == 0 && !h->first_field) {
         if (
             (h->avctx->skip_frame >= AVDISCARD_NONREF && !h->nal_ref_idc) ||
@@ -2343,7 +2413,7 @@ int ff_h264_queue_decode_slice(H264Context *h, const H2645NAL *nal)
             av_log(h->avctx, AV_LOG_ERROR, "PPS changed between slices\n");
             return AVERROR_INVALIDDATA;
         }
-        if (h->ps.sps != pps->sps) {
+        if (h->ps.sps != pps->sps && !h->cur_view) {
             av_log(h->avctx, AV_LOG_ERROR,
                "SPS changed in the middle of the frame\n");
             return AVERROR_INVALIDDATA;
diff --git a/libavcodec/h264dec.c b/libavcodec/h264dec.c
index 1e7cb96b3b..17baef28d2 100644
--- a/libavcodec/h264dec.c
+++ b/libavcodec/h264dec.c
@@ -555,6 +555,22 @@ static int get_last_needed_nal(H264Context *h)
         case H264_NAL_PPS:
             nals_needed = i;
             break;
+        case H264_NAL_EXTEN_SLICE:
+            if (h->views_active_decode == 1)
+                break;
+            /* the MVC NAL unit header has already been consumed by the NAL parser,
+             * so the slice header starts three bytes further in */
+            ret = init_get_bits8(&gb, nal->data + 4, nal->size - 4);
+            if (ret < 0) {
+                if (h->avctx->err_recognition & AV_EF_EXPLODE)
+                    return ret;
+                break;
+            }
+            if (!get_ue_golomb_long(&gb) || !first_slice ||
+                first_slice != nal->type)
+                nals_needed = i;
+            first_slice = nal->type;
+            break;
         case H264_NAL_DPA:
         case H264_NAL_IDR_SLICE:
         case H264_NAL_SLICE:
@@ -683,6 +699,12 @@ static int decode_nal_units(H264Context *h, AVBufferRef *buf_ref,
             h->has_recovery_point = 1;
             av_fallthrough;
         case H264_NAL_SLICE:
+        case H264_NAL_EXTEN_SLICE:
+            /* A coded slice extension carries a dependent view. Skip it unless we
+             * were asked to decode more than the base view. */
+            if (nal->type == H264_NAL_EXTEN_SLICE && h->views_active_decode == 1)
+                break;
+
             h->has_slice = 1;
 
             if ((err = ff_h264_queue_decode_slice(h, nal))) {
diff --git a/libavcodec/h264dec.h b/libavcodec/h264dec.h
index e814501b2c..dc3cd3f311 100644
--- a/libavcodec/h264dec.h
+++ b/libavcodec/h264dec.h
@@ -171,6 +171,11 @@ typedef struct H264Ref {
     int reference;
     int poc;
     int pic_id;
+    /**
+     * Set for an inter-view reference (MVC). Such a reference behaves as a long
+     * term one for temporal direct prediction, cf. H.8.2.1.
+     */
+    int inter_view;
 
     const H264Picture *parent;
 } H264Ref;
@@ -244,12 +249,21 @@ typedef struct H264SliceContext {
 
     int redundant_pic_count;
 
+    /** anchor_pic_flag of the MVC NAL unit header, 0 for a base view slice */
+    int anchor_pic_flag;
+
     /**
      * number of neighbors (top and/or left) that used 8x8 dct
      */
     int neighbor_transform_size;
 
     int direct_spatial_mv_pred;
+    /**
+     * Whether the colocated picture, ref_list[1][0], is a long term or inter-view
+     * reference, in which case its motion vectors are not used by temporal direct
+     * prediction. Derived once per slice by ff_h264_direct_ref_list_init().
+     */
+    int col_long_ref;
     int col_parity;
     int col_fieldoff;
 
@@ -663,6 +677,19 @@ int ff_h264_alloc_tables(H264Context *h);
 
 int ff_h264_decode_ref_pic_list_reordering(H264SliceContext *sl, void *logctx);
 int ff_h264_build_ref_list(H264Context *h, H264SliceContext *sl);
+/**
+ * Whether a VCL NAL unit starts an IDR picture.
+ *
+ * In a coded slice extension this is signalled by non_idr_flag in the MVC NAL
+ * unit header rather than by the NAL unit type, cf. H.7.4.1.1.
+ */
+static inline int ff_h264_nal_is_idr(const H2645NAL *nal)
+{
+    if (nal->type == H264_NAL_EXTEN_SLICE)
+        return !nal->non_idr_flag;
+    return nal->type == H264_NAL_IDR_SLICE;
+}
+
 void ff_h264_remove_all_refs(H264Context *h);
 
 /**
@@ -675,6 +702,14 @@ void ff_h264_remove_all_refs(H264Context *h);
  */
 void ff_h264_view_switch(H264Context *h, unsigned view);
 
+/**
+ * Map a view_id onto its view order index (VOIdx), i.e. its position in the
+ * view_id[] list of the active subset SPS, cf. H.7.4.1.1.
+ *
+ * @return the VOIdx, or a negative value if the stream declares no such view.
+ */
+int ff_h264_view_idx(const H264Context *h, int view_id);
+
 /**
  * Reset all per-view state, as at an IDR or a flush.
  */
-- 
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.