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:
Seungha Yang 2022-05-19 04:25:38 +09:00 committed by GStreamer Marge Bot
parent cf887f1b8e
commit 72975fbd6d
3 changed files with 448 additions and 0 deletions

View file

@ -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
}

View file

@ -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

View file

@ -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;
}