mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 19:51:11 +00:00
h264parser: Add an API for AVCDecoderConfigurationRecord parsing
Add a method for AVC configuration date parsing Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2449>
This commit is contained in:
parent
cf887f1b8e
commit
72975fbd6d
3 changed files with 448 additions and 0 deletions
|
@ -3463,3 +3463,229 @@ gst_h264_parser_insert_sei_avc (GstH264NalParser * nalparser,
|
|||
return gst_h264_parser_insert_sei_internal (nalparser, nal_length_size, TRUE,
|
||||
au, sei);
|
||||
}
|
||||
|
||||
static GstH264DecoderConfigRecord *
|
||||
gst_h264_decoder_config_record_new (void)
|
||||
{
|
||||
GstH264DecoderConfigRecord *config;
|
||||
|
||||
config = g_new0 (GstH264DecoderConfigRecord, 1);
|
||||
config->sps = g_array_new (FALSE, FALSE, sizeof (GstH264NalUnit));
|
||||
config->pps = g_array_new (FALSE, FALSE, sizeof (GstH264NalUnit));
|
||||
config->sps_ext = g_array_new (FALSE, FALSE, sizeof (GstH264NalUnit));
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_h264_decoder_config_record_free:
|
||||
* @config: (nullable): a #GstH264DecoderConfigRecord data
|
||||
*
|
||||
* Free @config data
|
||||
*
|
||||
* Since: 1.22
|
||||
*/
|
||||
void
|
||||
gst_h264_decoder_config_record_free (GstH264DecoderConfigRecord * config)
|
||||
{
|
||||
if (!config)
|
||||
return;
|
||||
|
||||
if (config->sps)
|
||||
g_array_unref (config->sps);
|
||||
|
||||
if (config->pps)
|
||||
g_array_unref (config->pps);
|
||||
|
||||
if (config->sps_ext)
|
||||
g_array_unref (config->sps_ext);
|
||||
|
||||
g_free (config);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_h264_parser_parse_decoder_config_record:
|
||||
* @nalparser: a #GstH264NalParser
|
||||
* @data: the data to parse
|
||||
* @size: the size of @data
|
||||
* @config: (out): parsed #GstH264DecoderConfigRecord data
|
||||
*
|
||||
* Parses AVCDecoderConfigurationRecord data and fill into @config.
|
||||
* The caller must free @config via gst_h264_decoder_config_record_free()
|
||||
*
|
||||
* This method does not parse SPS and PPS and therefore the caller needs to
|
||||
* parse each NAL unit via appropriate parsing method.
|
||||
*
|
||||
* Returns: a #GstH264ParserResult
|
||||
*
|
||||
* Since: 1.22
|
||||
*/
|
||||
GstH264ParserResult
|
||||
gst_h264_parser_parse_decoder_config_record (GstH264NalParser * nalparser,
|
||||
const guint8 * data, gsize size, GstH264DecoderConfigRecord ** config)
|
||||
{
|
||||
GstH264DecoderConfigRecord *ret;
|
||||
GstBitReader br;
|
||||
GstH264ParserResult result = GST_H264_PARSER_OK;
|
||||
guint8 num_sps, num_pps, i;
|
||||
guint offset;
|
||||
|
||||
g_return_val_if_fail (nalparser != NULL, GST_H264_PARSER_ERROR);
|
||||
g_return_val_if_fail (data != NULL, GST_H264_PARSER_ERROR);
|
||||
g_return_val_if_fail (config != NULL, GST_H264_PARSER_ERROR);
|
||||
|
||||
#define READ_CONFIG_UINT8(val, nbits) G_STMT_START { \
|
||||
if (!gst_bit_reader_get_bits_uint8 (&br, &val, nbits)) { \
|
||||
GST_WARNING ("Failed to read " G_STRINGIFY (val)); \
|
||||
result = GST_H264_PARSER_ERROR; \
|
||||
goto error; \
|
||||
} \
|
||||
} G_STMT_END;
|
||||
|
||||
#define SKIP_CONFIG_BITS(nbits) G_STMT_START { \
|
||||
if (!gst_bit_reader_skip (&br, nbits)) { \
|
||||
GST_WARNING ("Failed to skip %d bits", nbits); \
|
||||
result = GST_H264_PARSER_ERROR; \
|
||||
goto error; \
|
||||
} \
|
||||
} G_STMT_END;
|
||||
|
||||
*config = NULL;
|
||||
|
||||
if (size < 7) {
|
||||
GST_WARNING ("Too small size avcC");
|
||||
return GST_H264_PARSER_ERROR;
|
||||
}
|
||||
|
||||
gst_bit_reader_init (&br, data, size);
|
||||
|
||||
ret = gst_h264_decoder_config_record_new ();
|
||||
|
||||
READ_CONFIG_UINT8 (ret->configuration_version, 8);
|
||||
/* Keep parsing, caller can decide whether this data needs to be discarded
|
||||
* or not */
|
||||
if (ret->configuration_version != 1) {
|
||||
GST_WARNING ("Wrong configurationVersion %d", ret->configuration_version);
|
||||
result = GST_H264_PARSER_ERROR;
|
||||
goto error;
|
||||
}
|
||||
|
||||
READ_CONFIG_UINT8 (ret->profile_indication, 8);
|
||||
READ_CONFIG_UINT8 (ret->profile_compatibility, 8);
|
||||
READ_CONFIG_UINT8 (ret->level_indication, 8);
|
||||
/* reserved 6bits */
|
||||
SKIP_CONFIG_BITS (6);
|
||||
READ_CONFIG_UINT8 (ret->length_size_minus_one, 2);
|
||||
if (ret->length_size_minus_one == 2) {
|
||||
/* "length_size_minus_one + 1" should be 1, 2, or 4 */
|
||||
GST_WARNING ("Wrong nal-length-size");
|
||||
result = GST_H264_PARSER_ERROR;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* reserved 3bits */
|
||||
SKIP_CONFIG_BITS (3);
|
||||
|
||||
READ_CONFIG_UINT8 (num_sps, 5);
|
||||
offset = gst_bit_reader_get_pos (&br);
|
||||
|
||||
g_assert (offset % 8 == 0);
|
||||
offset /= 8;
|
||||
for (i = 0; i < num_sps; i++) {
|
||||
GstH264NalUnit nalu;
|
||||
|
||||
result = gst_h264_parser_identify_nalu_avc (nalparser,
|
||||
data, offset, size, 2, &nalu);
|
||||
if (result != GST_H264_PARSER_OK)
|
||||
goto error;
|
||||
|
||||
g_array_append_val (ret->sps, nalu);
|
||||
offset = nalu.offset + nalu.size;
|
||||
}
|
||||
|
||||
if (!gst_bit_reader_set_pos (&br, offset * 8)) {
|
||||
result = GST_H264_PARSER_ERROR;
|
||||
goto error;
|
||||
}
|
||||
|
||||
READ_CONFIG_UINT8 (num_pps, 8);
|
||||
offset = gst_bit_reader_get_pos (&br);
|
||||
|
||||
g_assert (offset % 8 == 0);
|
||||
offset /= 8;
|
||||
for (i = 0; i < num_pps; i++) {
|
||||
GstH264NalUnit nalu;
|
||||
|
||||
result = gst_h264_parser_identify_nalu_avc (nalparser,
|
||||
data, offset, size, 2, &nalu);
|
||||
if (result != GST_H264_PARSER_OK)
|
||||
goto error;
|
||||
|
||||
g_array_append_val (ret->pps, nalu);
|
||||
offset = nalu.offset + nalu.size;
|
||||
}
|
||||
|
||||
/* Parse chroma format and SPS ext data. We will silently ignore any
|
||||
* error while parsing below data since it's not essential data for
|
||||
* decoding */
|
||||
if (ret->profile_indication == 100 || ret->profile_indication == 110 ||
|
||||
ret->profile_indication == 122 || ret->profile_indication == 144) {
|
||||
guint8 num_sps_ext;
|
||||
|
||||
if (!gst_bit_reader_set_pos (&br, offset * 8))
|
||||
goto out;
|
||||
|
||||
if (!gst_bit_reader_skip (&br, 6))
|
||||
goto out;
|
||||
|
||||
if (!gst_bit_reader_get_bits_uint8 (&br, &ret->chroma_format, 2))
|
||||
goto out;
|
||||
|
||||
if (!gst_bit_reader_skip (&br, 5))
|
||||
goto out;
|
||||
|
||||
if (!gst_bit_reader_get_bits_uint8 (&br, &ret->bit_depth_luma_minus8, 3))
|
||||
goto out;
|
||||
|
||||
if (!gst_bit_reader_skip (&br, 5))
|
||||
goto out;
|
||||
|
||||
if (!gst_bit_reader_get_bits_uint8 (&br, &ret->bit_depth_chroma_minus8, 3))
|
||||
goto out;
|
||||
|
||||
if (!gst_bit_reader_get_bits_uint8 (&br, &num_sps_ext, 8))
|
||||
goto out;
|
||||
|
||||
offset = gst_bit_reader_get_pos (&br);
|
||||
|
||||
g_assert (offset % 8 == 0);
|
||||
offset /= 8;
|
||||
for (i = 0; i < num_sps_ext; i++) {
|
||||
GstH264NalUnit nalu;
|
||||
|
||||
result = gst_h264_parser_identify_nalu_avc (nalparser,
|
||||
data, offset, size, 2, &nalu);
|
||||
if (result != GST_H264_PARSER_OK)
|
||||
goto out;
|
||||
|
||||
g_array_append_val (ret->sps_ext, nalu);
|
||||
offset = nalu.offset + nalu.size;
|
||||
}
|
||||
|
||||
ret->chroma_format_present = TRUE;
|
||||
}
|
||||
|
||||
out:
|
||||
{
|
||||
*config = ret;
|
||||
return GST_H264_PARSER_OK;
|
||||
}
|
||||
error:
|
||||
{
|
||||
gst_h264_decoder_config_record_free (ret);
|
||||
return result;
|
||||
}
|
||||
|
||||
#undef READ_CONFIG_UINT8
|
||||
#undef SKIP_CONFIG_BITS
|
||||
}
|
||||
|
|
|
@ -375,6 +375,7 @@ typedef struct _GstH264MasteringDisplayColourVolume GstH264MasteringDisplayColou
|
|||
typedef struct _GstH264ContentLightLevel GstH264ContentLightLevel;
|
||||
typedef struct _GstH264SEIUnhandledPayload GstH264SEIUnhandledPayload;
|
||||
typedef struct _GstH264SEIMessage GstH264SEIMessage;
|
||||
typedef struct _GstH264DecoderConfigRecord GstH264DecoderConfigRecord;
|
||||
|
||||
/**
|
||||
* GstH264NalUnitExtensionMVC:
|
||||
|
@ -1238,6 +1239,106 @@ struct _GstH264SEIMessage
|
|||
} payload;
|
||||
};
|
||||
|
||||
/**
|
||||
* GstH264DecoderConfigRecord:
|
||||
*
|
||||
* Contains AVCDecoderConfigurationRecord data as defined in ISO/IEC 14496-15
|
||||
*
|
||||
* Since: 1.22
|
||||
*/
|
||||
struct _GstH264DecoderConfigRecord
|
||||
{
|
||||
/**
|
||||
* GstH264DecoderConfigRecord.configuration_version:
|
||||
*
|
||||
* Indicates configurationVersion, must be 1
|
||||
*/
|
||||
guint8 configuration_version;
|
||||
|
||||
/**
|
||||
* GstH264DecoderConfigRecord.profile_indication:
|
||||
*
|
||||
* H.264 profile indication
|
||||
*/
|
||||
guint8 profile_indication;
|
||||
|
||||
/**
|
||||
* GstH264DecoderConfigRecord.profile_compatibility:
|
||||
*
|
||||
* H.264 profile compatibility
|
||||
*/
|
||||
guint8 profile_compatibility;
|
||||
|
||||
/**
|
||||
* GstH264DecoderConfigRecord.level_indication:
|
||||
*
|
||||
* H.264 level indiction
|
||||
*/
|
||||
guint8 level_indication;
|
||||
|
||||
/**
|
||||
* GstH264DecoderConfigRecord.length_size_minus_one:
|
||||
*
|
||||
* Indicates the length in bytes of the NAL unit length field
|
||||
*/
|
||||
guint8 length_size_minus_one;
|
||||
|
||||
/**
|
||||
* GstH264DecoderConfigRecord.sps
|
||||
*
|
||||
* Array of identified #GstH264NalUnit from sequenceParameterSetNALUnit.
|
||||
* This array may contain non-SPS nal units such as SEI message
|
||||
*/
|
||||
GArray *sps;
|
||||
|
||||
/**
|
||||
* GstH264DecoderConfigRecord.pps
|
||||
*
|
||||
* Array of identified #GstH264NalUnit from pictureParameterSetNALUnit.
|
||||
* This array may contain non-PPS nal units such as SEI message
|
||||
*/
|
||||
GArray *pps;
|
||||
|
||||
/**
|
||||
* GstH264DecoderConfigRecord.chroma_format_present
|
||||
*
|
||||
* %TRUE if chroma information is present. Otherwise below values
|
||||
* have no meaning
|
||||
*/
|
||||
gboolean chroma_format_present;
|
||||
|
||||
/**
|
||||
* GstH264DecoderConfigRecord.chroma_format
|
||||
*
|
||||
* chroma_format_idc defined in ISO/IEC 14496-10
|
||||
*/
|
||||
guint8 chroma_format;
|
||||
|
||||
/**
|
||||
* GstH264DecoderConfigRecord.bit_depth_luma_minus8
|
||||
*
|
||||
* Indicates bit depth of luma component
|
||||
*/
|
||||
guint8 bit_depth_luma_minus8;
|
||||
|
||||
/**
|
||||
* GstH264DecoderConfigRecord.bit_depth_chroma_minus8
|
||||
*
|
||||
* Indicates bit depth of chroma component
|
||||
*/
|
||||
guint8 bit_depth_chroma_minus8;
|
||||
|
||||
/**
|
||||
* GstH264DecoderConfigRecord.sps_ext
|
||||
*
|
||||
* Array of identified #GstH264NalUnit from sequenceParameterSetExtNALUnit.
|
||||
*/
|
||||
GArray *sps_ext;
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
/**
|
||||
* GstH264NalParser:
|
||||
*
|
||||
|
@ -1366,6 +1467,15 @@ GstBuffer * gst_h264_parser_insert_sei_avc (GstH264NalParser * nalparser,
|
|||
GstBuffer * au,
|
||||
GstMemory * sei);
|
||||
|
||||
GST_CODEC_PARSERS_API
|
||||
void gst_h264_decoder_config_record_free (GstH264DecoderConfigRecord * config);
|
||||
|
||||
GST_CODEC_PARSERS_API
|
||||
GstH264ParserResult gst_h264_parser_parse_decoder_config_record (GstH264NalParser * nalparser,
|
||||
const guint8 * data,
|
||||
gsize size,
|
||||
GstH264DecoderConfigRecord ** config);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
|
|
|
@ -714,6 +714,117 @@ GST_START_TEST (test_h264_create_sei)
|
|||
|
||||
GST_END_TEST;
|
||||
|
||||
static guint8 h264_avc_codec_data[] = {
|
||||
0x01, 0x4d, 0x40, 0x15, 0xff, 0xe1, 0x00, 0x17,
|
||||
0x67, 0x4d, 0x40, 0x15, 0xec, 0xa4, 0xbf, 0x2e,
|
||||
0x02, 0x20, 0x00, 0x00, 0x03, 0x00, 0x2e, 0xe6,
|
||||
0xb2, 0x80, 0x01, 0xe2, 0xc5, 0xb2, 0xc0, 0x01,
|
||||
0x00, 0x04, 0x68, 0xeb, 0xec, 0xb2
|
||||
};
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
static guint8 h264_avc3_codec_data[] = {
|
||||
0x01, /* config version, always == 1 */
|
||||
0x4d, /* profile */
|
||||
0x40, /* profile compatibility */
|
||||
0x15, /* level */
|
||||
0xff, /* 6 reserved bits, lengthSizeMinusOne */
|
||||
0xe0, /* 3 reserved bits, numSPS */
|
||||
0x00 /* numPPS */
|
||||
};
|
||||
|
||||
static guint8 h264_wrong_version_codec_data[] = {
|
||||
0x00, /* config version, wrong value 0 */
|
||||
0x4d, /* profile */
|
||||
0x40, /* profile compatibility */
|
||||
0x15, /* level */
|
||||
0xff, /* 6 reserved bits, lengthSizeMinusOne */
|
||||
0xe0, /* 3 reserved bits, numSPS */
|
||||
0x00 /* numPPS */
|
||||
};
|
||||
|
||||
static guint8 h264_wrong_length_size_codec_data[] = {
|
||||
0x01, /* config version, always == 1 */
|
||||
0x4d, /* profile */
|
||||
0x40, /* profile compatibility */
|
||||
0x15, /* level */
|
||||
0xfe, /* 6 reserved bits, invalid lengthSizeMinusOne 3 */
|
||||
0xe0, /* 3 reserved bits, numSPS */
|
||||
0x00 /* numPPS */
|
||||
};
|
||||
/* *INDENT-ON* */
|
||||
|
||||
GST_START_TEST (test_h264_decoder_config_record)
|
||||
{
|
||||
GstH264NalParser *parser;
|
||||
GstH264ParserResult ret;
|
||||
GstH264DecoderConfigRecord *config = NULL;
|
||||
GstH264SPS sps;
|
||||
GstH264PPS pps;
|
||||
GstH264NalUnit *nalu;
|
||||
|
||||
parser = gst_h264_nal_parser_new ();
|
||||
|
||||
/* avc */
|
||||
ret = gst_h264_parser_parse_decoder_config_record (parser,
|
||||
h264_avc_codec_data, sizeof (h264_avc_codec_data), &config);
|
||||
assert_equals_int (ret, GST_H264_PARSER_OK);
|
||||
fail_unless (config != NULL);
|
||||
assert_equals_int (config->configuration_version, 1);
|
||||
assert_equals_int (config->length_size_minus_one, 3);
|
||||
|
||||
assert_equals_int (config->sps->len, 1);
|
||||
nalu = &g_array_index (config->sps, GstH264NalUnit, 0);
|
||||
assert_equals_int (nalu->type, GST_H264_NAL_SPS);
|
||||
ret = gst_h264_parser_parse_sps (parser, nalu, &sps);
|
||||
assert_equals_int (ret, GST_H264_PARSER_OK);
|
||||
gst_h264_sps_clear (&sps);
|
||||
|
||||
assert_equals_int (config->pps->len, 1);
|
||||
nalu = &g_array_index (config->pps, GstH264NalUnit, 0);
|
||||
assert_equals_int (nalu->type, GST_H264_NAL_PPS);
|
||||
ret = gst_h264_parser_parse_pps (parser, nalu, &pps);
|
||||
assert_equals_int (ret, GST_H264_PARSER_OK);
|
||||
gst_h264_pps_clear (&pps);
|
||||
g_clear_pointer (&config, gst_h264_decoder_config_record_free);
|
||||
|
||||
/* avc3 */
|
||||
ret = gst_h264_parser_parse_decoder_config_record (parser,
|
||||
h264_avc3_codec_data, sizeof (h264_avc3_codec_data), &config);
|
||||
assert_equals_int (ret, GST_H264_PARSER_OK);
|
||||
fail_unless (config != NULL);
|
||||
|
||||
assert_equals_int (config->configuration_version, 1);
|
||||
assert_equals_int (config->length_size_minus_one, 3);
|
||||
assert_equals_int (config->sps->len, 0);
|
||||
assert_equals_int (config->pps->len, 0);
|
||||
g_clear_pointer (&config, gst_h264_decoder_config_record_free);
|
||||
|
||||
/* avc3 wrong size, return error with null config data */
|
||||
ret = gst_h264_parser_parse_decoder_config_record (parser,
|
||||
h264_avc3_codec_data, sizeof (h264_avc3_codec_data) - 1, &config);
|
||||
assert_equals_int (ret, GST_H264_PARSER_ERROR);
|
||||
fail_unless (config == NULL);
|
||||
|
||||
/* wrong version, return error with null config data */
|
||||
ret = gst_h264_parser_parse_decoder_config_record (parser,
|
||||
h264_wrong_version_codec_data, sizeof (h264_wrong_version_codec_data),
|
||||
&config);
|
||||
assert_equals_int (ret, GST_H264_PARSER_ERROR);
|
||||
fail_unless (config == NULL);
|
||||
|
||||
/* wrong length size, return error with null config data */
|
||||
ret = gst_h264_parser_parse_decoder_config_record (parser,
|
||||
h264_wrong_length_size_codec_data,
|
||||
sizeof (h264_wrong_length_size_codec_data), &config);
|
||||
assert_equals_int (ret, GST_H264_PARSER_ERROR);
|
||||
fail_unless (config == NULL);
|
||||
|
||||
gst_h264_nal_parser_free (parser);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
static Suite *
|
||||
h264parser_suite (void)
|
||||
{
|
||||
|
@ -728,6 +839,7 @@ h264parser_suite (void)
|
|||
tcase_add_test (tc_chain, test_h264_parse_identify_nalu_avc);
|
||||
tcase_add_test (tc_chain, test_h264_parse_invalid_sei);
|
||||
tcase_add_test (tc_chain, test_h264_create_sei);
|
||||
tcase_add_test (tc_chain, test_h264_decoder_config_record);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue