[PATCH 04/18] avcodec/h264: parse subset SPS (NAL 15) and its MVC extension

Dom Cobley via ffmpeg-devel <[email protected]>
Newsgroups gmane.comp.video.ffmpeg.devel
Message-ID <[email protected]>
Add parsing of subset_seq_parameter_set_rbsp() (H.7.3.2.1.3) and the
seq_parameter_set_mvc_extension() it carries (H.7.3.2.1.4), which is where an
MVC stream declares its views and the inter-view dependency lists later used to
build the inter-view reference picture lists.

Subset SPSs live in an id space of their own, separate from that of ordinary
SPSs (H.7.4.1.2.1), so they get a separate list. A PPS referenced by a coded
slice extension refers to a subset SPS, but nothing in the PPS itself says which
space is meant, so resolve it against both and let the slice header pick by NAL
type. This also stops a PPS pointing at a subset SPS from being rejected
outright, which previously spammed "sps_id N out of range" on every MVC stream.

Only the MVC extension is handled; SVC (Annex G) and MVCD/3D-AVC subset SPSs are
stored but their extensions are not parsed. Streams declaring more views than
are supported are likewise stored without a usable view list, so that base view
decoding is unaffected either way.

Also add H264_NAL_SUB_SPS to the extract_extradata bsf, which would otherwise
drop it and leave the decoder unable to resolve a dependent view PPS arriving
via extradata.

Signed-off-by: Dom Cobley <[email protected]>
---
 libavcodec/bsf/extract_extradata.c |   2 +-
 libavcodec/h264_parse.c            |   4 +
 libavcodec/h264_parser.c           |   3 +
 libavcodec/h264_ps.c               | 157 +++++++++++++++++++++++++++--
 libavcodec/h264_ps.h               |  57 +++++++++++
 libavcodec/h264_slice.c            |   4 +-
 libavcodec/h264dec.c               |   5 +
 7 files changed, 222 insertions(+), 10 deletions(-)

diff --git a/libavcodec/bsf/extract_extradata.c b/libavcodec/bsf/extract_extradata.c
index 3c66f392b2..c6220bd7a2 100644
--- a/libavcodec/bsf/extract_extradata.c
+++ b/libavcodec/bsf/extract_extradata.c
@@ -173,7 +173,7 @@ static int extract_extradata_h2645(AVBSFContext *ctx, AVPacket *pkt,
         HEVC_NAL_VPS, HEVC_NAL_SPS, HEVC_NAL_PPS,
     };
     static const int extradata_nal_types_h264[] = {
-        H264_NAL_SPS, H264_NAL_PPS,
+        H264_NAL_SPS, H264_NAL_SUB_SPS, H264_NAL_PPS,
     };
 
     ExtractExtradataContext *s = ctx->priv_data;
diff --git a/libavcodec/h264_parse.c b/libavcodec/h264_parse.c
index c6f9b3cea7..d472d9a86c 100644
--- a/libavcodec/h264_parse.c
+++ b/libavcodec/h264_parse.c
@@ -396,6 +396,10 @@ static int decode_extradata_ps(const uint8_t *data, int size, H264ParamSets *ps,
                 goto fail;
             break;
         }
+        case H264_NAL_SUB_SPS:
+            /* Only affects multiview decoding, so a failure is not fatal. */
+            ff_h264_decode_subset_seq_parameter_set(&nal->gb, logctx, ps);
+            break;
         case H264_NAL_PPS:
             ret = ff_h264_decode_picture_parameter_set(&nal->gb, logctx, ps,
                                                        nal->size_bits);
diff --git a/libavcodec/h264_parser.c b/libavcodec/h264_parser.c
index af43cad609..1b9cf9fe36 100644
--- a/libavcodec/h264_parser.c
+++ b/libavcodec/h264_parser.c
@@ -344,6 +344,9 @@ static inline int parse_nal_units(AVCodecParserContext *s,
         case H264_NAL_SPS:
             ff_h264_decode_seq_parameter_set(&nal.gb, avctx, &p->ps, 0);
             break;
+        case H264_NAL_SUB_SPS:
+            ff_h264_decode_subset_seq_parameter_set(&nal.gb, avctx, &p->ps);
+            break;
         case H264_NAL_PPS:
             ff_h264_decode_picture_parameter_set(&nal.gb, avctx, &p->ps,
                                                  nal.size_bits);
diff --git a/libavcodec/h264_ps.c b/libavcodec/h264_ps.c
index d59fd2667e..269702c0bc 100644
--- a/libavcodec/h264_ps.c
+++ b/libavcodec/h264_ps.c
@@ -271,8 +271,10 @@ void ff_h264_ps_uninit(H264ParamSets *ps)
 {
     int i;
 
-    for (i = 0; i < MAX_SPS_COUNT; i++)
+    for (i = 0; i < MAX_SPS_COUNT; i++) {
         av_refstruct_unref(&ps->sps_list[i]);
+        av_refstruct_unref(&ps->subset_sps_list[i]);
+    }
 
     for (i = 0; i < MAX_PPS_COUNT; i++)
         av_refstruct_unref(&ps->pps_list[i]);
@@ -281,8 +283,103 @@ void ff_h264_ps_uninit(H264ParamSets *ps)
     ps->sps = NULL;
 }
 
-int ff_h264_decode_seq_parameter_set(GetBitContext *gb, AVCodecContext *avctx,
-                                     H264ParamSets *ps, int ignore_truncation)
+/**
+ * Parse seq_parameter_set_mvc_extension(), H.7.3.2.1.4.
+ *
+ * On success sps->mvc.num_views is the number of views; on unsupported input it
+ * is left at 0 and 0 is still returned, so that the subset SPS remains usable
+ * for base-view-only decoding.
+ */
+static int decode_sps_mvc_extension(GetBitContext *gb, AVCodecContext *avctx,
+                                    SPS *sps)
+{
+    SPSMVCExt *mvc = &sps->mvc;
+    unsigned num_views, num_level_values;
+
+    num_views = get_ue_golomb_long(gb) + 1;
+    if (num_views > H264_MAX_MVC_VIEWS) {
+        avpriv_report_missing_feature(avctx, "MVC with %u views", num_views);
+        return 0;
+    }
+
+    for (int i = 0; i < num_views; i++) {
+        unsigned view_id = get_ue_golomb_long(gb);
+        if (view_id > 1023) {
+            av_log(avctx, AV_LOG_ERROR, "Invalid view_id %u\n", view_id);
+            return AVERROR_INVALIDDATA;
+        }
+        mvc->view_id[i] = view_id;
+    }
+
+    /* The anchor and non-anchor inter-view dependency lists. Note l0 and l1 are
+     * interleaved within each iteration of i. */
+    for (int i = 1; i < num_views; i++) {
+        for (int list = 0; list < 2; list++) {
+            unsigned n = get_ue_golomb_31(gb);
+            if (n >= H264_MAX_MVC_REFS) {
+                av_log(avctx, AV_LOG_ERROR,
+                       "Invalid num_anchor_refs_l%d[%d] %u\n", list, i, n);
+                return AVERROR_INVALIDDATA;
+            }
+            mvc->num_anchor_refs[list][i] = n;
+            for (int j = 0; j < n; j++)
+                mvc->anchor_ref[list][i][j] = get_ue_golomb_long(gb);
+        }
+    }
+    for (int i = 1; i < num_views; i++) {
+        for (int list = 0; list < 2; list++) {
+            unsigned n = get_ue_golomb_31(gb);
+            if (n >= H264_MAX_MVC_REFS) {
+                av_log(avctx, AV_LOG_ERROR,
+                       "Invalid num_non_anchor_refs_l%d[%d] %u\n", list, i, n);
+                return AVERROR_INVALIDDATA;
+            }
+            mvc->num_non_anchor_refs[list][i] = n;
+            for (int j = 0; j < n; j++)
+                mvc->non_anchor_ref[list][i][j] = get_ue_golomb_long(gb);
+        }
+    }
+
+    /* The level / operation point table is parsed only to consume it; we have no
+     * use for the operation points. */
+    num_level_values = get_ue_golomb_31(gb) + 1;
+    for (int i = 0; i < num_level_values; i++) {
+        unsigned num_ops;
+
+        skip_bits(gb, 8);                        // level_idc
+        num_ops = get_ue_golomb_long(gb) + 1;
+        for (int j = 0; j < num_ops; j++) {
+            unsigned num_target_views;
+
+            skip_bits(gb, 3);                    // applicable_op_temporal_id
+            num_target_views = get_ue_golomb_long(gb) + 1;
+            for (int k = 0; k < num_target_views; k++)
+                get_ue_golomb_long(gb);          // applicable_op_target_view_id
+            get_ue_golomb_long(gb);              // applicable_op_num_views_minus1
+        }
+        if (get_bits_left(gb) < 0) {
+            av_log(avctx, AV_LOG_ERROR, "Overread MVC SPS extension\n");
+            return AVERROR_INVALIDDATA;
+        }
+    }
+
+    mvc->num_views = num_views;
+
+    if (avctx->debug & FF_DEBUG_PICT_INFO) {
+        for (int i = 0; i < num_views; i++)
+            av_log(avctx, AV_LOG_DEBUG,
+                   "mvc: voidx:%d view_id:%u anchor_refs:%d/%d non_anchor_refs:%d/%d\n",
+                   i, mvc->view_id[i],
+                   mvc->num_anchor_refs[0][i],     mvc->num_anchor_refs[1][i],
+                   mvc->num_non_anchor_refs[0][i], mvc->num_non_anchor_refs[1][i]);
+    }
+
+    return 0;
+}
+
+static int decode_seq_parameter_set(GetBitContext *gb, AVCodecContext *avctx,
+                                    H264ParamSets *ps, int ignore_truncation,
+                                    int subset)
 {
     int profile_idc, level_idc, constraint_set_flags = 0;
     unsigned int sps_id;
@@ -532,6 +629,21 @@ int ff_h264_decode_seq_parameter_set(GetBitContext *gb, AVCodecContext *avctx,
             goto fail;
     }
 
+    /* The remainder of subset_seq_parameter_set_rbsp(), H.7.3.2.1.3. Only the
+     * MVC extension is handled; SVC (profile 83/86) and MVCD/3D-AVC (138) are
+     * parsed no further, which just leaves mvc.num_views at 0. */
+    sps->is_subset = subset;
+    if (subset && (sps->profile_idc == 118 || sps->profile_idc == 128)) {
+        if (!get_bits1(gb)) {   // bit_equal_to_one
+            av_log(avctx, AV_LOG_ERROR,
+                   "Invalid subset SPS: bit_equal_to_one is 0\n");
+            goto fail;
+        }
+        ret = decode_sps_mvc_extension(gb, avctx, sps);
+        if (ret < 0)
+            goto fail;
+    }
+
     if (get_bits_left(gb) < 0) {
         av_log_once(avctx, ignore_truncation ? AV_LOG_WARNING : AV_LOG_ERROR, AV_LOG_DEBUG,
                     &ps->overread_warning_printed[sps->vui_parameters_present_flag],
@@ -578,8 +690,16 @@ int ff_h264_decode_seq_parameter_set(GetBitContext *gb, AVCodecContext *avctx,
     /* check if this is a repeat of an already parsed SPS, then keep the
      * original one.
      * otherwise drop all PPSes that depend on it */
-    if (ps->sps_list[sps_id] &&
-        !memcmp(ps->sps_list[sps_id], sps, sizeof(*sps))) {
+    if (subset) {
+        if (ps->subset_sps_list[sps_id] &&
+            !memcmp(ps->subset_sps_list[sps_id], sps, sizeof(*sps))) {
+            av_refstruct_unref(&sps);
+        } else {
+            av_refstruct_unref(&ps->subset_sps_list[sps_id]);
+            ps->subset_sps_list[sps_id] = sps;
+        }
+    } else if (ps->sps_list[sps_id] &&
+               !memcmp(ps->sps_list[sps_id], sps, sizeof(*sps))) {
         av_refstruct_unref(&sps);
     } else {
         remove_sps(ps, sps_id);
@@ -593,6 +713,18 @@ fail:
     return AVERROR_INVALIDDATA;
 }
 
+int ff_h264_decode_seq_parameter_set(GetBitContext *gb, AVCodecContext *avctx,
+                                     H264ParamSets *ps, int ignore_truncation)
+{
+    return decode_seq_parameter_set(gb, avctx, ps, ignore_truncation, 0);
+}
+
+int ff_h264_decode_subset_seq_parameter_set(GetBitContext *gb, AVCodecContext *avctx,
+                                            H264ParamSets *ps)
+{
+    return decode_seq_parameter_set(gb, avctx, ps, 0, 1);
+}
+
 static void init_dequant8_coeff_table(PPS *pps, const SPS *sps)
 {
     int i, j, q, x;
@@ -693,6 +825,7 @@ static void pps_free(AVRefStructOpaque unused, void *obj)
     PPS *pps = obj;
 
     av_refstruct_unref(&pps->sps);
+    av_refstruct_unref(&pps->sps_mvc);
 }
 
 int ff_h264_decode_picture_parameter_set(GetBitContext *gb, AVCodecContext *avctx,
@@ -729,14 +862,22 @@ int ff_h264_decode_picture_parameter_set(GetBitContext *gb, AVCodecContext *avct
 
     pps->pps_id = pps_id;
     pps->sps_id = get_ue_golomb_31(gb);
+    /* A PPS referenced by a coded slice extension (NAL 20) refers to a subset
+     * SPS, which lives in its own id space. As the two spaces are disjoint in
+     * practice we cannot tell from the PPS alone which one is meant, so resolve
+     * against both and let the slice header pick by NAL type. */
     if ((unsigned)pps->sps_id >= MAX_SPS_COUNT ||
-        !ps->sps_list[pps->sps_id]) {
+        (!ps->sps_list[pps->sps_id] && !ps->subset_sps_list[pps->sps_id])) {
         av_log(avctx, AV_LOG_ERROR, "sps_id %u out of range\n", pps->sps_id);
         ret = AVERROR_INVALIDDATA;
         goto fail;
     }
-    pps->sps = av_refstruct_ref_c(ps->sps_list[pps->sps_id]);
-    sps      = pps->sps;
+    /* av_refstruct_replace() rather than av_refstruct_ref_c(), as either list
+     * entry may be NULL here. */
+    av_refstruct_replace(&pps->sps_mvc, ps->subset_sps_list[pps->sps_id]);
+    av_refstruct_replace(&pps->sps, ps->sps_list[pps->sps_id] ?
+                                    ps->sps_list[pps->sps_id] : pps->sps_mvc);
+    sps = pps->sps;
 
     if (sps->bit_depth_luma > 14) {
         av_log(avctx, AV_LOG_ERROR,
diff --git a/libavcodec/h264_ps.h b/libavcodec/h264_ps.h
index f216e4989f..41d776a3a6 100644
--- a/libavcodec/h264_ps.h
+++ b/libavcodec/h264_ps.h
@@ -38,6 +38,36 @@
 #define MAX_PPS_COUNT         256
 #define MAX_LOG2_MAX_FRAME_NUM    (12 + 4)
 
+/**
+ * Maximum number of MVC views we support decoding. The syntax allows up to
+ * 1024; as for MV-HEVC only stereo is implemented.
+ */
+#define H264_MAX_MVC_VIEWS      2
+/** num_anchor_refs_lX / num_non_anchor_refs_lX are in [0, 15], cf. H.7.4.2.1.4 */
+#define H264_MAX_MVC_REFS      16
+
+/**
+ * MVC extension of the subset sequence parameter set, cf. H.7.3.2.1.4
+ * seq_parameter_set_mvc_extension().
+ *
+ * Arrays documented as [voidx] are indexed by view order index (VOIdx), i.e.
+ * the position of a view in view_id[]. The inter-view reference arrays hold
+ * view_id values, not VOIdx values, and are only meaningful for voidx >= 1.
+ */
+typedef struct SPSMVCExt {
+    /**
+     * num_views_minus1 + 1, or 0 if this subset SPS carries no usable MVC
+     * extension (absent, or more views than we support).
+     */
+    int      num_views;
+    uint16_t view_id[H264_MAX_MVC_VIEWS];
+
+    uint8_t  num_anchor_refs    [2][H264_MAX_MVC_VIEWS];
+    uint16_t anchor_ref         [2][H264_MAX_MVC_VIEWS][H264_MAX_MVC_REFS];
+    uint8_t  num_non_anchor_refs[2][H264_MAX_MVC_VIEWS];
+    uint16_t non_anchor_ref     [2][H264_MAX_MVC_VIEWS][H264_MAX_MVC_REFS];
+} SPSMVCExt;
+
 /**
  * Sequence parameter set
  */
@@ -102,6 +132,9 @@ typedef struct SPS {
     int constraint_set_flags;             ///< constraint_set[0-3]_flag
     uint8_t data[4096];
     size_t data_size;
+
+    int is_subset;                        ///< came from a NAL_SUB_SPS (H.7.3.2.1.3)
+    SPSMVCExt mvc;                        ///< only valid when is_subset is set
 } SPS;
 
 /**
@@ -139,10 +172,24 @@ typedef struct PPS {
     uint32_t(*dequant8_coeff[6])[64];
 
     const SPS   *sps; ///< RefStruct reference
+
+    /**
+     * RefStruct reference to the subset SPS with the same seq_parameter_set_id,
+     * if one exists, else NULL. Slices in a coded slice extension (NAL 20)
+     * activate this instead of ::sps.
+     *
+     * The derived tables above (chroma_qp_table, dequant*_coeff) are built from
+     * ::sps; a subset SPS is required to agree with the base SPS on bit depth
+     * and chroma format for multiview decoding to be attempted, so they are
+     * valid for both.
+     */
+    const SPS   *sps_mvc; ///< RefStruct reference
 } PPS;
 
 typedef struct H264ParamSets {
     const SPS *sps_list[MAX_SPS_COUNT]; ///< RefStruct references
+    /** subset SPSs (NAL 15); a separate id space from sps_list, cf. H.7.4.1.2.1 */
+    const SPS *subset_sps_list[MAX_SPS_COUNT]; ///< RefStruct references
     const PPS *pps_list[MAX_PPS_COUNT]; ///< RefStruct references
 
     /* currently active parameters sets */
@@ -163,6 +210,16 @@ int ff_h264_get_profile(const SPS *sps);
 int ff_h264_decode_seq_parameter_set(GetBitContext *gb, AVCodecContext *avctx,
                                      H264ParamSets *ps, int ignore_truncation);
 
+/**
+ * Decode a subset SPS (NAL_SUB_SPS), cf. H.7.3.2.1.3
+ * subset_seq_parameter_set_rbsp().
+ *
+ * The result is stored in ps->subset_sps_list, which is a separate id space
+ * from ps->sps_list.
+ */
+int ff_h264_decode_subset_seq_parameter_set(GetBitContext *gb, AVCodecContext *avctx,
+                                            H264ParamSets *ps);
+
 /**
  * Decode PPS
  */
diff --git a/libavcodec/h264_slice.c b/libavcodec/h264_slice.c
index c96d99d242..aeddf49db4 100644
--- a/libavcodec/h264_slice.c
+++ b/libavcodec/h264_slice.c
@@ -365,8 +365,10 @@ int ff_h264_update_thread_context(AVCodecContext *dst,
     memcpy(h->block_offset, h1->block_offset, sizeof(h->block_offset));
 
     // SPS/PPS
-    for (int i = 0; i < FF_ARRAY_ELEMS(h->ps.sps_list); i++)
+    for (int i = 0; i < FF_ARRAY_ELEMS(h->ps.sps_list); i++) {
         av_refstruct_replace(&h->ps.sps_list[i], h1->ps.sps_list[i]);
+        av_refstruct_replace(&h->ps.subset_sps_list[i], h1->ps.subset_sps_list[i]);
+    }
     for (int i = 0; i < FF_ARRAY_ELEMS(h->ps.pps_list); i++)
         av_refstruct_replace(&h->ps.pps_list[i], h1->ps.pps_list[i]);
 
diff --git a/libavcodec/h264dec.c b/libavcodec/h264dec.c
index b78b7989ea..8636001270 100644
--- a/libavcodec/h264dec.c
+++ b/libavcodec/h264dec.c
@@ -714,6 +714,11 @@ static int decode_nal_units(H264Context *h, AVBufferRef *buf_ref,
             ff_h264_decode_seq_parameter_set(&nal->gb, avctx, &h->ps, 1);
             break;
         }
+        case H264_NAL_SUB_SPS:
+            /* Failure here only means multiview decoding will not be offered;
+             * the base view is unaffected, so do not propagate the error. */
+            ff_h264_decode_subset_seq_parameter_set(&nal->gb, avctx, &h->ps);
+            break;
         case H264_NAL_PPS:
             if (FF_HW_HAS_CB(avctx, decode_params)) {
                 ret = FF_HW_CALL(avctx, decode_params,
-- 
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.