codecparsers: h264: parse seq_parameter_set_mvc_extension().

https://bugzilla.gnome.org/show_bug.cgi?id=685215

Signed-off-by: Gwenole Beauchesne <gwenole.beauchesne@intel.com>
This commit is contained in:
Gwenole Beauchesne 2012-10-01 13:36:45 +02:00 committed by Jan Schmidt
parent c1deab84e6
commit 8911a80662
2 changed files with 396 additions and 6 deletions

View 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

View 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],