From: Gwenole Beauchesne Date: Mon, 1 Oct 2012 11:36:45 +0000 (+0200) Subject: codecparsers: h264: parse seq_parameter_set_mvc_extension(). X-Git-Tag: 1.19.3~507^2~10053 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=8911a80662a21978cfc032c193f9e8b70874ce54;p=platform%2Fupstream%2Fgstreamer.git codecparsers: h264: parse seq_parameter_set_mvc_extension(). https://bugzilla.gnome.org/show_bug.cgi?id=685215 Signed-off-by: Gwenole Beauchesne --- diff --git a/gst-libs/gst/codecparsers/gsth264parser.c b/gst-libs/gst/codecparsers/gsth264parser.c index 5cafa1c3fa..0aab994308 100644 --- a/gst-libs/gst/codecparsers/gsth264parser.c +++ b/gst-libs/gst/codecparsers/gsth264parser.c @@ -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 diff --git a/gst-libs/gst/codecparsers/gsth264parser.h b/gst-libs/gst/codecparsers/gsth264parser.h index a78f3075c8..3028a8f40f 100644 --- a/gst-libs/gst/codecparsers/gsth264parser.h +++ b/gst-libs/gst/codecparsers/gsth264parser.h @@ -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],