codecparsers: h264: parse seq_parameter_set_mvc_extension().
authorGwenole Beauchesne <gwenole.beauchesne@intel.com>
Mon, 1 Oct 2012 11:36:45 +0000 (13:36 +0200)
committerJan Schmidt <jan@centricular.com>
Wed, 29 Oct 2014 12:09:41 +0000 (23:09 +1100)
https://bugzilla.gnome.org/show_bug.cgi?id=685215

Signed-off-by: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
gst-libs/gst/codecparsers/gsth264parser.c
gst-libs/gst/codecparsers/gsth264parser.h

index 5cafa1c3fa7d0cee8692b75ce65d100b44d832ca..0aab994308fc6d8b3064c057e1dc0f17fdff9cef 100644 (file)
@@ -275,6 +275,114 @@ gst_h264_pps_copy (GstH264PPS * dst_pps, const GstH264PPS * src_pps)
   return TRUE;
 }
 
+/* Copy MVC-specific data for subset SPS header */
+static gboolean
+gst_h264_sps_mvc_copy (GstH264SPS * dst_sps, const GstH264SPS * src_sps)
+{
+  GstH264SPSExtMVC *const dst_mvc = &dst_sps->extension.mvc;
+  const GstH264SPSExtMVC *const src_mvc = &src_sps->extension.mvc;
+  guint i, j, k;
+
+  g_assert (dst_sps->extension_type == GST_H264_NAL_EXTENSION_MVC);
+
+  dst_mvc->num_views_minus1 = src_mvc->num_views_minus1;
+  dst_mvc->view = g_new0 (GstH264SPSExtMVCView, dst_mvc->num_views_minus1 + 1);
+  if (!dst_mvc->view)
+    return FALSE;
+
+  dst_mvc->view[0].view_id = src_mvc->view[0].view_id;
+
+  for (i = 1; i <= dst_mvc->num_views_minus1; i++) {
+    GstH264SPSExtMVCView *const dst_view = &dst_mvc->view[i];
+    const GstH264SPSExtMVCView *const src_view = &src_mvc->view[i];
+
+    dst_view->view_id = src_view->view_id;
+
+    dst_view->num_anchor_refs_l0 = src_view->num_anchor_refs_l1;
+    for (j = 0; j < dst_view->num_anchor_refs_l0; j++)
+      dst_view->anchor_ref_l0[j] = src_view->anchor_ref_l0[j];
+
+    dst_view->num_anchor_refs_l1 = src_view->num_anchor_refs_l1;
+    for (j = 0; j < dst_view->num_anchor_refs_l1; j++)
+      dst_view->anchor_ref_l1[j] = src_view->anchor_ref_l1[j];
+
+    dst_view->num_non_anchor_refs_l0 = src_view->num_non_anchor_refs_l1;
+    for (j = 0; j < dst_view->num_non_anchor_refs_l0; j++)
+      dst_view->non_anchor_ref_l0[j] = src_view->non_anchor_ref_l0[j];
+
+    dst_view->num_non_anchor_refs_l1 = src_view->num_non_anchor_refs_l1;
+    for (j = 0; j < dst_view->num_non_anchor_refs_l1; j++)
+      dst_view->non_anchor_ref_l1[j] = src_view->non_anchor_ref_l1[j];
+  }
+
+  dst_mvc->num_level_values_signalled_minus1 =
+      src_mvc->num_level_values_signalled_minus1;
+  dst_mvc->level_value = g_new0 (GstH264SPSExtMVCLevelValue,
+      dst_mvc->num_level_values_signalled_minus1 + 1);
+  if (!dst_mvc->level_value)
+    return FALSE;
+
+  for (i = 0; i <= dst_mvc->num_level_values_signalled_minus1; i++) {
+    GstH264SPSExtMVCLevelValue *const dst_value = &dst_mvc->level_value[i];
+    const GstH264SPSExtMVCLevelValue *const src_value =
+        &src_mvc->level_value[i];
+
+    dst_value->level_idc = src_value->level_idc;
+
+    dst_value->num_applicable_ops_minus1 = src_value->num_applicable_ops_minus1;
+    dst_value->applicable_op = g_new0 (GstH264SPSExtMVCLevelValueOp,
+        dst_value->num_applicable_ops_minus1 + 1);
+    if (!dst_value->applicable_op)
+      return FALSE;
+
+    for (j = 0; j <= dst_value->num_applicable_ops_minus1; j++) {
+      GstH264SPSExtMVCLevelValueOp *const dst_op = &dst_value->applicable_op[j];
+      const GstH264SPSExtMVCLevelValueOp *const src_op =
+          &src_value->applicable_op[j];
+
+      dst_op->temporal_id = src_op->temporal_id;
+      dst_op->num_target_views_minus1 = src_op->num_target_views_minus1;
+      dst_op->target_view_id =
+          g_new (guint16, dst_op->num_target_views_minus1 + 1);
+      if (!dst_op->target_view_id)
+        return FALSE;
+
+      for (k = 0; k <= dst_op->num_target_views_minus1; k++)
+        dst_op->target_view_id[k] = src_op->target_view_id[k];
+      dst_op->num_views_minus1 = src_op->num_views_minus1;
+    }
+  }
+  return TRUE;
+}
+
+/*
+ * gst_h264_sps_copy:
+ * @dst_sps: The destination #GstH264SPS to copy into
+ * @src_sps: The source #GstH264SPS to copy from
+ *
+ * Copies @src_sps into @dst_sps.
+ *
+ * Returns: %TRUE if everything went fine, %FALSE otherwise
+ */
+static gboolean
+gst_h264_sps_copy (GstH264SPS * dst_sps, const GstH264SPS * src_sps)
+{
+  g_return_val_if_fail (dst_sps != NULL, FALSE);
+  g_return_val_if_fail (src_sps != NULL, FALSE);
+
+  gst_h264_sps_clear (dst_sps);
+
+  *dst_sps = *src_sps;
+
+  switch (dst_sps->extension_type) {
+    case GST_H264_NAL_EXTENSION_MVC:
+      if (!gst_h264_sps_mvc_copy (dst_sps, src_sps))
+        return FALSE;
+      break;
+  }
+  return TRUE;
+}
+
 /****** Parsing functions *****/
 
 static gboolean
@@ -1054,6 +1162,8 @@ gst_h264_nal_parser_free (GstH264NalParser * nalparser)
 {
   guint i;
 
+  for (i = 0; i < GST_H264_MAX_SPS_COUNT; i++)
+    gst_h264_sps_clear (&nalparser->sps[i]);
   for (i = 0; i < GST_H264_MAX_PPS_COUNT; i++)
     gst_h264_pps_clear (&nalparser->pps[i]);
   g_slice_free (GstH264NalParser, nalparser);
@@ -1286,12 +1396,10 @@ gst_h264_parser_parse_sps (GstH264NalParser * nalparser, GstH264NalUnit * nalu,
   if (res == GST_H264_PARSER_OK) {
     GST_DEBUG ("adding sequence parameter set with id: %d to array", sps->id);
 
-    nalparser->sps[sps->id] = *sps;
+    if (!gst_h264_sps_copy (&nalparser->sps[sps->id], sps))
+      return GST_H264_PARSER_ERROR;
     nalparser->last_sps = &nalparser->sps[sps->id];
   }
-
-
-
   return res;
 }
 
@@ -1307,6 +1415,7 @@ gst_h264_parse_sps_data (NalReader * nr, GstH264SPS * sps,
 
   /* set default values for fields that might not be present in the bitstream
      and have valid defaults */
+  sps->extension_type = GST_H264_NAL_EXTENSION_NONE;
   sps->chroma_format_idc = 1;
   sps->separate_colour_plane_flag = 0;
   sps->bit_depth_luma_minus8 = 0;
@@ -1471,6 +1580,109 @@ error:
   return FALSE;
 }
 
+/* Parse subset_seq_parameter_set() data for MVC */
+static gboolean
+gst_h264_parse_sps_mvc_data (NalReader * nr, GstH264SPS * sps,
+    gboolean parse_vui_params)
+{
+  GstH264SPSExtMVC *const mvc = &sps->extension.mvc;
+  guint8 bit_equal_to_one;
+  guint i, j, k;
+
+  READ_UINT8 (nr, bit_equal_to_one, 1);
+  if (!bit_equal_to_one)
+    return FALSE;
+
+  sps->extension_type = GST_H264_NAL_EXTENSION_MVC;
+
+  READ_UE_ALLOWED (nr, mvc->num_views_minus1, 0, GST_H264_MAX_VIEW_COUNT - 1);
+
+  mvc->view = g_new0 (GstH264SPSExtMVCView, mvc->num_views_minus1 + 1);
+  if (!mvc->view)
+    goto error_allocation_failed;
+
+  for (i = 0; i <= mvc->num_views_minus1; i++)
+    READ_UE_ALLOWED (nr, mvc->view[i].view_id, 0, GST_H264_MAX_VIEW_ID);
+
+  for (i = 1; i <= mvc->num_views_minus1; i++) {
+    /* for RefPicList0 */
+    READ_UE_ALLOWED (nr, mvc->view[i].num_anchor_refs_l0, 0, 15);
+    for (j = 0; j < mvc->view[i].num_anchor_refs_l0; j++) {
+      READ_UE_ALLOWED (nr, mvc->view[i].anchor_ref_l0[j], 0,
+          GST_H264_MAX_VIEW_ID);
+    }
+
+    /* for RefPicList1 */
+    READ_UE_ALLOWED (nr, mvc->view[i].num_anchor_refs_l1, 0, 15);
+    for (j = 0; j < mvc->view[i].num_anchor_refs_l1; j++) {
+      READ_UE_ALLOWED (nr, mvc->view[i].anchor_ref_l1[j], 0,
+          GST_H264_MAX_VIEW_ID);
+    }
+  }
+
+  for (i = 1; i <= mvc->num_views_minus1; i++) {
+    /* for RefPicList0 */
+    READ_UE_ALLOWED (nr, mvc->view[i].num_non_anchor_refs_l0, 0, 15);
+    for (j = 0; j < mvc->view[i].num_non_anchor_refs_l0; j++) {
+      READ_UE_ALLOWED (nr, mvc->view[i].non_anchor_ref_l0[j], 0,
+          GST_H264_MAX_VIEW_ID);
+    }
+
+    /* for RefPicList1 */
+    READ_UE_ALLOWED (nr, mvc->view[i].num_non_anchor_refs_l1, 0, 15);
+    for (j = 0; j < mvc->view[i].num_non_anchor_refs_l1; j++) {
+      READ_UE_ALLOWED (nr, mvc->view[i].non_anchor_ref_l1[j], 0,
+          GST_H264_MAX_VIEW_ID);
+    }
+  }
+
+  READ_UE_ALLOWED (nr, mvc->num_level_values_signalled_minus1, 0, 63);
+
+  mvc->level_value =
+      g_new0 (GstH264SPSExtMVCLevelValue,
+      mvc->num_level_values_signalled_minus1 + 1);
+  if (!mvc->level_value)
+    goto error_allocation_failed;
+
+  for (i = 0; i <= mvc->num_level_values_signalled_minus1; i++) {
+    GstH264SPSExtMVCLevelValue *const level_value = &mvc->level_value[i];
+
+    READ_UINT8 (nr, level_value->level_idc, 8);
+
+    READ_UE_ALLOWED (nr, level_value->num_applicable_ops_minus1, 0, 1023);
+    level_value->applicable_op =
+        g_new0 (GstH264SPSExtMVCLevelValueOp,
+        level_value->num_applicable_ops_minus1 + 1);
+    if (!level_value->applicable_op)
+      goto error_allocation_failed;
+
+    for (j = 0; j <= level_value->num_applicable_ops_minus1; j++) {
+      GstH264SPSExtMVCLevelValueOp *const op = &level_value->applicable_op[j];
+
+      READ_UINT8 (nr, op->temporal_id, 3);
+
+      READ_UE_ALLOWED (nr, op->num_target_views_minus1, 0, 1023);
+      op->target_view_id = g_new (guint16, op->num_target_views_minus1 + 1);
+      if (!op->target_view_id)
+        goto error_allocation_failed;
+
+      for (k = 0; k <= op->num_target_views_minus1; k++)
+        READ_UE_ALLOWED (nr, op->target_view_id[k], 0, GST_H264_MAX_VIEW_ID);
+      READ_UE_ALLOWED (nr, op->num_views_minus1, 0, 1023);
+    }
+  }
+  return TRUE;
+
+error_allocation_failed:
+  GST_WARNING ("failed to allocate memory");
+  gst_h264_sps_clear (sps);
+  return FALSE;
+
+error:
+  gst_h264_sps_clear (sps);
+  return FALSE;
+}
+
 /**
  * gst_h264_parse_sps:
  * @nalu: The #GST_H264_NAL_SPS #GstH264NalUnit to parse
@@ -1515,6 +1727,15 @@ error:
  *
  * Parses @data, and fills in the @sps structure.
  *
+ * This function fully parses @data and allocates all the necessary
+ * data structures needed for MVC extensions. The resulting @sps
+ * structure shall be deallocated with gst_h264_sps_clear() when it is
+ * no longer needed.
+ *
+ * Note: if the caller doesn't need any of the MVC-specific data, then
+ * gst_h264_parser_parse_sps() is more efficient because those extra
+ * syntax elements are not parsed and no extra memory is allocated.
+ *
  * Returns: a #GstH264ParserResult
  */
 GstH264ParserResult
@@ -1527,7 +1748,8 @@ gst_h264_parser_parse_subset_sps (GstH264NalParser * nalparser,
   if (res == GST_H264_PARSER_OK) {
     GST_DEBUG ("adding sequence parameter set with id: %d to array", sps->id);
 
-    nalparser->sps[sps->id] = *sps;
+    if (!gst_h264_sps_copy (&nalparser->sps[sps->id], sps))
+      return GST_H264_PARSER_ERROR;
     nalparser->last_sps = &nalparser->sps[sps->id];
   }
   return res;
@@ -1541,6 +1763,15 @@ gst_h264_parser_parse_subset_sps (GstH264NalParser * nalparser,
  *
  * Parses @data, and fills in the @sps structure.
  *
+ * This function fully parses @data and allocates all the necessary
+ * data structures needed for MVC extensions. The resulting @sps
+ * structure shall be deallocated with gst_h264_sps_clear() when it is
+ * no longer needed.
+ *
+ * Note: if the caller doesn't need any of the MVC-specific data, then
+ * gst_h264_parser_parse_sps() is more efficient because those extra
+ * syntax elements are not parsed and no extra memory is allocated.
+ *
  * Returns: a #GstH264ParserResult
  */
 GstH264ParserResult
@@ -1555,9 +1786,15 @@ gst_h264_parse_subset_sps (GstH264NalUnit * nalu, GstH264SPS * sps,
   nal_reader_init (&nr, nalu->data + nalu->offset + nalu->header_bytes,
       nalu->size - nalu->header_bytes);
 
-  if (!gst_h264_parse_sps_data (&nr, sps, parse_vui_params))
+  if (!gst_h264_parse_sps_data (&nr, sps, TRUE))
     goto error;
 
+  if (sps->profile_idc == GST_H264_PROFILE_MULTIVIEW_HIGH ||
+      sps->profile_idc == GST_H264_PROFILE_STEREO_HIGH) {
+    if (!gst_h264_parse_sps_mvc_data (&nr, sps, parse_vui_params))
+      goto error;
+  }
+
   sps->valid = TRUE;
   return GST_H264_PARSER_OK;
 
@@ -1933,6 +2170,54 @@ error:
   return GST_H264_PARSER_ERROR;
 }
 
+/* Free MVC-specific data from subset SPS header */
+static void
+gst_h264_sps_mvc_clear (GstH264SPS * sps)
+{
+  GstH264SPSExtMVC *const mvc = &sps->extension.mvc;
+  guint i, j;
+
+  g_assert (sps->extension_type == GST_H264_NAL_EXTENSION_MVC);
+
+  g_free (mvc->view);
+  mvc->view = NULL;
+
+  for (i = 0; i <= mvc->num_level_values_signalled_minus1; i++) {
+    GstH264SPSExtMVCLevelValue *const level_value = &mvc->level_value[i];
+
+    for (j = 0; j <= level_value->num_applicable_ops_minus1; j++) {
+      g_free (level_value->applicable_op[j].target_view_id);
+      level_value->applicable_op[j].target_view_id = NULL;
+    }
+    g_free (level_value->applicable_op);
+    level_value->applicable_op = NULL;
+  }
+  g_free (mvc->level_value);
+  mvc->level_value = NULL;
+
+  /* All meaningful MVC info are now gone, just pretend to be a
+   * standard AVC struct now */
+  sps->extension_type = GST_H264_NAL_EXTENSION_NONE;
+}
+
+/**
+ * gst_h264_sps_clear:
+ * @sps: The #GstH264SPS to free
+ *
+ * Clears all @sps internal resources.
+ */
+void
+gst_h264_sps_clear (GstH264SPS * sps)
+{
+  g_return_if_fail (sps != NULL);
+
+  switch (sps->extension_type) {
+    case GST_H264_NAL_EXTENSION_MVC:
+      gst_h264_sps_mvc_clear (sps);
+      break;
+  }
+}
+
 /**
  * gst_h264_parser_parse_sei:
  * @nalparser: a #GstH264NalParser
index a78f3075c80f26c2059f036cdc516bbe4ed99208..3028a8f40fb3db036e742fa4c56e5e757d5279af 100644 (file)
@@ -41,6 +41,8 @@ G_BEGIN_DECLS
 
 #define GST_H264_MAX_SPS_COUNT   32
 #define GST_H264_MAX_PPS_COUNT   256
+#define GST_H264_MAX_VIEW_COUNT  1024
+#define GST_H264_MAX_VIEW_ID     (GST_H264_MAX_VIEW_COUNT - 1)
 
 #define GST_H264_IS_P_SLICE(slice)  (((slice)->type % 5) == GST_H264_P_SLICE)
 #define GST_H264_IS_B_SLICE(slice)  (((slice)->type % 5) == GST_H264_B_SLICE)
@@ -239,6 +241,11 @@ typedef struct _GstH264NalParser              GstH264NalParser;
 typedef struct _GstH264NalUnit                GstH264NalUnit;
 typedef struct _GstH264NalUnitExtensionMVC    GstH264NalUnitExtensionMVC;
 
+typedef struct _GstH264SPSExtMVCView          GstH264SPSExtMVCView;
+typedef struct _GstH264SPSExtMVCLevelValue    GstH264SPSExtMVCLevelValue;
+typedef struct _GstH264SPSExtMVCLevelValueOp  GstH264SPSExtMVCLevelValueOp;
+typedef struct _GstH264SPSExtMVC              GstH264SPSExtMVC;
+
 typedef struct _GstH264SPS                    GstH264SPS;
 typedef struct _GstH264PPS                    GstH264PPS;
 typedef struct _GstH264HRDParams              GstH264HRDParams;
@@ -470,6 +477,97 @@ struct _GstH264VUIParams
   guint par_d;
 };
 
+/**
+ * GstH264SPSExtMVCView:
+ * @num_anchor_refs_l0: specifies the number of view components for
+ *   inter-view prediction in the initialized RefPicList0 in decoding
+ *   anchor view components.
+ * @anchor_ref_l0: specifies the view_id for inter-view prediction in
+ *   the initialized RefPicList0 in decoding anchor view components.
+ * @num_anchor_refs_l1: specifies the number of view components for
+ *   inter-view prediction in the initialized RefPicList1 in decoding
+ *   anchor view components.
+ * @anchor_ref_l1: specifies the view_id for inter-view prediction in
+ *   the initialized RefPicList1 in decoding anchor view components.
+ * @num_non_anchor_refs_l0: specifies the number of view components
+ *   for inter-view prediction in the initialized RefPicList0 in
+ *   decoding non-anchor view components.
+ * @non_anchor_ref_l0: specifies the view_id for inter-view prediction
+ *   in the initialized RefPicList0 in decoding non-anchor view
+ *   components.
+ * @num_non_anchor_refs_l1: specifies the number of view components
+ *   for inter-view prediction in the initialized RefPicList1 in
+ *   decoding non-anchor view components.
+ * @non_anchor_ref_l1: specifies the view_id for inter-view prediction
+ *   in the initialized RefPicList1 in decoding non-anchor view
+ *   components.
+ *
+ * Represents inter-view dependency relationships for the coded video
+ * sequence.
+ */
+struct _GstH264SPSExtMVCView
+{
+  guint16 view_id;
+  guint8 num_anchor_refs_l0;
+  guint16 anchor_ref_l0[15];
+  guint8 num_anchor_refs_l1;
+  guint16 anchor_ref_l1[15];
+  guint8 num_non_anchor_refs_l0;
+  guint16 non_anchor_ref_l0[15];
+  guint8 num_non_anchor_refs_l1;
+  guint16 non_anchor_ref_l1[15];
+};
+
+/**
+ * GstH264SPSExtMVCLevelValueOp:
+ *
+ * Represents an operation point for the coded video sequence.
+ */
+struct _GstH264SPSExtMVCLevelValueOp
+{
+  guint8 temporal_id;
+  guint16 num_target_views_minus1;
+  guint16 *target_view_id;
+  guint16 num_views_minus1;
+};
+
+/**
+ * GstH264SPSExtMVCLevelValue:
+ * @level_idc: specifies the level value signalled for the coded video
+ *   sequence
+ * @num_applicable_ops_minus1: plus 1 specifies the number of
+ *   operation points to which the level indicated by level_idc applies
+ * @applicable_op: specifies the applicable operation point
+ *
+ * Represents level values for a subset of the operation points for
+ * the coded video sequence.
+ */
+struct _GstH264SPSExtMVCLevelValue
+{
+  guint8 level_idc;
+  guint16 num_applicable_ops_minus1;
+  GstH264SPSExtMVCLevelValueOp *applicable_op;
+};
+
+/**
+ * GstH264SPSExtMVC:
+ * @num_views_minus1: plus 1 specifies the maximum number of coded
+ *   views in the coded video sequence
+ * @view: array of #GstH264SPSExtMVCView
+ * @num_level_values_signalled_minus1: plus 1 specifies the number of
+ *   level values signalled for the coded video sequence.
+ * @level_value: array of #GstH264SPSExtMVCLevelValue
+ *
+ * Represents the parsed seq_parameter_set_mvc_extension().
+ */
+struct _GstH264SPSExtMVC
+{
+  guint16 num_views_minus1;
+  GstH264SPSExtMVCView *view;
+  guint8 num_level_values_signalled_minus1;
+  GstH264SPSExtMVCLevelValue *level_value;
+};
+
 /**
  * GstH264SPS:
  * @id: The ID of the sequence parameter set
@@ -543,6 +641,12 @@ struct _GstH264SPS
   gint crop_rect_x, crop_rect_y;
   gint fps_num, fps_den;
   gboolean valid;
+
+  /* Subset SPS extensions */
+  guint8 extension_type;
+  union {
+    GstH264SPSExtMVC mvc;
+  } extension;
 };
 
 /**
@@ -842,6 +946,7 @@ GstH264ParserResult gst_h264_parse_sps                (GstH264NalUnit *nalu,
 GstH264ParserResult gst_h264_parse_pps                (GstH264NalParser *nalparser,
                                                        GstH264NalUnit *nalu, GstH264PPS *pps);
 
+void                gst_h264_sps_clear                (GstH264SPS *sps);
 void                gst_h264_pps_clear                (GstH264PPS *pps);
 
 void    gst_h264_quant_matrix_8x8_get_zigzag_from_raster (guint8 out_quant[64],