mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-18 14:26:43 +00:00
h265parser: Add a new NAL parsing API to handle malformed packets
Add gst_h265_parser_identify_and_split_nalu_hevc() method to handle a case where packetized stream contains start-code prefix. This new method behaves similar to exisiting gst_h265_parser_identify_nalu_hevc() but it will scan start-code prefix to split given data into NAL units. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2394>
This commit is contained in:
parent
c47255d148
commit
be84fc23ca
3 changed files with 380 additions and 0 deletions
|
@ -1581,6 +1581,179 @@ gst_h265_parser_identify_nalu_hevc (GstH265Parser * parser,
|
|||
return GST_H265_PARSER_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_h265_parser_identify_and_split_nalu_hevc:
|
||||
* @parser: a #GstH265Parser
|
||||
* @data: The data to parse, must be the beging of the Nal unit
|
||||
* @offset: the offset from which to parse @data
|
||||
* @size: the size of @data
|
||||
* @nal_length_size: the size in bytes of the HEVC nal length prefix.
|
||||
* @nalus: a caller allocated GArray of #GstH265NalUnit where to store parsed nal headers
|
||||
* @consumed: the size of consumed bytes
|
||||
*
|
||||
* Parses @data for packetized (e.g., hvc1/hev1) bitstream and
|
||||
* sets @nalus. In addition to nal identifying process,
|
||||
* this method scans start-code prefix to split malformed packet into
|
||||
* actual nal chunks.
|
||||
*
|
||||
* Returns: a #GstH265ParserResult
|
||||
*
|
||||
* Since: 1.22
|
||||
*/
|
||||
GstH265ParserResult
|
||||
gst_h265_parser_identify_and_split_nalu_hevc (GstH265Parser * parser,
|
||||
const guint8 * data, guint offset, gsize size, guint8 nal_length_size,
|
||||
GArray * nalus, gsize * consumed)
|
||||
{
|
||||
GstBitReader br;
|
||||
guint nalu_size;
|
||||
guint remaining;
|
||||
guint off;
|
||||
guint sc_size;
|
||||
|
||||
g_return_val_if_fail (data != NULL, GST_H265_PARSER_ERROR);
|
||||
g_return_val_if_fail (nalus != NULL, GST_H265_PARSER_ERROR);
|
||||
g_return_val_if_fail (nal_length_size > 0 && nal_length_size < 5,
|
||||
GST_H265_PARSER_ERROR);
|
||||
|
||||
g_array_set_size (nalus, 0);
|
||||
|
||||
if (consumed)
|
||||
*consumed = 0;
|
||||
|
||||
/* Would overflow guint below otherwise: the callers needs to ensure that
|
||||
* this never happens */
|
||||
if (offset > G_MAXUINT32 - nal_length_size) {
|
||||
GST_WARNING ("offset + nal_length_size overflow");
|
||||
return GST_H265_PARSER_BROKEN_DATA;
|
||||
}
|
||||
|
||||
if (size < offset + nal_length_size) {
|
||||
GST_DEBUG ("Can't parse, buffer has too small size %" G_GSIZE_FORMAT
|
||||
", offset %u", size, offset);
|
||||
return GST_H265_PARSER_ERROR;
|
||||
}
|
||||
|
||||
/* Read nal unit size and unwrap the size field */
|
||||
gst_bit_reader_init (&br, data + offset, size - offset);
|
||||
nalu_size = gst_bit_reader_get_bits_uint32_unchecked (&br,
|
||||
nal_length_size * 8);
|
||||
|
||||
if (nalu_size < 2) {
|
||||
GST_WARNING ("too small nal size %d", nalu_size);
|
||||
return GST_H265_PARSER_BROKEN_DATA;
|
||||
}
|
||||
|
||||
if (size < (gsize) nalu_size + nal_length_size) {
|
||||
GST_WARNING ("larger nalu size %d than data size %" G_GSIZE_FORMAT,
|
||||
nalu_size + nal_length_size, size);
|
||||
return GST_H265_PARSER_BROKEN_DATA;
|
||||
}
|
||||
|
||||
if (consumed)
|
||||
*consumed = nalu_size + nal_length_size;
|
||||
|
||||
off = offset + nal_length_size;
|
||||
remaining = nalu_size;
|
||||
sc_size = nal_length_size;
|
||||
|
||||
/* Drop trailing start-code since it will not be scanned */
|
||||
if (remaining >= 3) {
|
||||
if (data[off + remaining - 1] == 0x01 && data[off + remaining - 2] == 0x00
|
||||
&& data[off + remaining - 3] == 0x00) {
|
||||
remaining -= 3;
|
||||
|
||||
/* 4 bytes start-code */
|
||||
if (remaining > 0 && data[off + remaining - 1] == 0x00)
|
||||
remaining--;
|
||||
}
|
||||
}
|
||||
|
||||
/* Looping to split malformed nal units. nal-length field was dropped above
|
||||
* so expected bitstream structure are:
|
||||
*
|
||||
* <complete nalu>
|
||||
* | nalu |
|
||||
* sc scan result will be -1 and handled in CONDITION-A
|
||||
*
|
||||
* <nalu with startcode prefix>
|
||||
* | SC | nalu |
|
||||
* Hit CONDITION-C first then terminated in CONDITION-A
|
||||
*
|
||||
* <first nal has no startcode but others have>
|
||||
* | nalu | SC | nalu | ...
|
||||
* CONDITION-B handles those cases
|
||||
*/
|
||||
do {
|
||||
GstH265NalUnit nalu;
|
||||
gint sc_offset = -1;
|
||||
guint skip_size = 0;
|
||||
|
||||
memset (&nalu, 0, sizeof (GstH265NalUnit));
|
||||
|
||||
/* startcode 3 bytes + minimum nal size 2 */
|
||||
if (remaining >= 5)
|
||||
sc_offset = scan_for_start_codes (data + off, remaining);
|
||||
|
||||
if (sc_offset < 0) {
|
||||
if (remaining >= 2) {
|
||||
/* CONDITION-A */
|
||||
/* Last chunk */
|
||||
nalu.size = remaining;
|
||||
nalu.sc_offset = off - sc_size;
|
||||
nalu.offset = off;
|
||||
nalu.data = (guint8 *) data;
|
||||
nalu.valid = TRUE;
|
||||
|
||||
gst_h265_parse_nalu_header (&nalu);
|
||||
g_array_append_val (nalus, nalu);
|
||||
}
|
||||
break;
|
||||
} else if ((sc_offset == 2 && data[off + sc_offset - 1] != 0)
|
||||
|| sc_offset > 2) {
|
||||
/* CONDITION-B */
|
||||
/* Found trailing startcode prefix */
|
||||
|
||||
nalu.size = sc_offset;
|
||||
if (data[off + sc_offset - 1] == 0) {
|
||||
/* 4 bytes start code */
|
||||
nalu.size--;
|
||||
}
|
||||
|
||||
nalu.sc_offset = off - sc_size;
|
||||
nalu.offset = off;
|
||||
nalu.data = (guint8 *) data;
|
||||
nalu.valid = TRUE;
|
||||
|
||||
gst_h265_parse_nalu_header (&nalu);
|
||||
g_array_append_val (nalus, nalu);
|
||||
} else {
|
||||
/* CONDITION-C */
|
||||
/* startcode located at beginning of this chunk without actual nal data.
|
||||
* skip this start code */
|
||||
}
|
||||
|
||||
skip_size = sc_offset + 3;
|
||||
if (skip_size >= remaining)
|
||||
break;
|
||||
|
||||
/* no more nal-length bytes but 3bytes startcode */
|
||||
sc_size = 3;
|
||||
if (sc_offset > 0 && data[off + sc_offset - 1] == 0)
|
||||
sc_size++;
|
||||
|
||||
remaining -= skip_size;
|
||||
off += skip_size;
|
||||
} while (remaining >= 2);
|
||||
|
||||
if (nalus->len > 0)
|
||||
return GST_H265_PARSER_OK;
|
||||
|
||||
GST_WARNING ("No nal found");
|
||||
|
||||
return GST_H265_PARSER_BROKEN_DATA;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_h265_parser_parse_nal:
|
||||
* @parser: a #GstH265Parser
|
||||
|
|
|
@ -1653,6 +1653,15 @@ GstH265ParserResult gst_h265_parser_identify_nalu_hevc (GstH265Parser * parser,
|
|||
guint8 nal_length_size,
|
||||
GstH265NalUnit * nalu);
|
||||
|
||||
GST_CODEC_PARSERS_API
|
||||
GstH265ParserResult gst_h265_parser_identify_and_split_nalu_hevc (GstH265Parser * parser,
|
||||
const guint8 * data,
|
||||
guint offset,
|
||||
gsize size,
|
||||
guint8 nal_length_size,
|
||||
GArray * nalus,
|
||||
gsize * consumed);
|
||||
|
||||
GST_CODEC_PARSERS_API
|
||||
GstH265ParserResult gst_h265_parser_parse_nal (GstH265Parser * parser,
|
||||
GstH265NalUnit * nalu);
|
||||
|
|
|
@ -1175,6 +1175,203 @@ GST_START_TEST (test_h265_create_sei)
|
|||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_h265_split_hevc)
|
||||
{
|
||||
GstH265Parser *parser;
|
||||
GArray *array;
|
||||
GstH265NalUnit *nal;
|
||||
static const guint8 aud[] = { 0x46, 0x01, 0x10 };
|
||||
static const guint8 eos[] = { 0x48, 0x01 };
|
||||
static const guint8 sc_3bytes[] = { 0x00, 0x00, 0x01 };
|
||||
static const guint8 sc_4bytes[] = { 0x00, 0x00, 0x00, 0x01 };
|
||||
const guint8 nal_length_size = 4;
|
||||
guint8 data[128];
|
||||
gsize size;
|
||||
GstH265ParserResult ret;
|
||||
gsize consumed;
|
||||
guint off;
|
||||
|
||||
parser = gst_h265_parser_new ();
|
||||
array = g_array_new (FALSE, FALSE, sizeof (GstH265NalUnit));
|
||||
|
||||
#define BUILD_NAL(arr) G_STMT_START { \
|
||||
memcpy (data + off, arr, sizeof (arr)); \
|
||||
off += sizeof (arr); \
|
||||
} G_STMT_END
|
||||
|
||||
/* 1) Complete packetized nalu */
|
||||
size = nal_length_size + sizeof (aud);
|
||||
off = nal_length_size;
|
||||
GST_WRITE_UINT32_BE (data, sizeof (aud));
|
||||
BUILD_NAL (aud);
|
||||
ret = gst_h265_parser_identify_and_split_nalu_hevc (parser, data,
|
||||
0, size, nal_length_size, array, &consumed);
|
||||
assert_equals_int (ret, GST_H265_PARSER_OK);
|
||||
assert_equals_int (array->len, 1);
|
||||
assert_equals_int (consumed, size);
|
||||
nal = &g_array_index (array, GstH265NalUnit, 0);
|
||||
assert_equals_int (nal->type, GST_H265_NAL_AUD);
|
||||
assert_equals_int (nal->sc_offset, 0);
|
||||
assert_equals_int (nal->offset, nal_length_size);
|
||||
assert_equals_int (nal->size, sizeof (aud));
|
||||
|
||||
/* 2-1) SC (3 bytes) + nalu */
|
||||
size = nal_length_size + sizeof (sc_3bytes) + sizeof (aud);
|
||||
off = nal_length_size;
|
||||
GST_WRITE_UINT32_BE (data, sizeof (sc_3bytes) + sizeof (aud));
|
||||
BUILD_NAL (sc_3bytes);
|
||||
BUILD_NAL (aud);
|
||||
ret = gst_h265_parser_identify_and_split_nalu_hevc (parser, data,
|
||||
0, size, nal_length_size, array, &consumed);
|
||||
assert_equals_int (ret, GST_H265_PARSER_OK);
|
||||
assert_equals_int (array->len, 1);
|
||||
assert_equals_int (consumed, size);
|
||||
nal = &g_array_index (array, GstH265NalUnit, 0);
|
||||
assert_equals_int (nal->type, GST_H265_NAL_AUD);
|
||||
assert_equals_int (nal->sc_offset, nal_length_size);
|
||||
assert_equals_int (nal->offset, nal_length_size + sizeof (sc_3bytes));
|
||||
assert_equals_int (nal->size, sizeof (aud));
|
||||
|
||||
/* 2-2) SC (4 bytes) + nalu */
|
||||
size = nal_length_size + sizeof (sc_4bytes) + sizeof (aud);
|
||||
off = nal_length_size;
|
||||
GST_WRITE_UINT32_BE (data, sizeof (sc_4bytes) + sizeof (aud));
|
||||
BUILD_NAL (sc_4bytes);
|
||||
BUILD_NAL (aud);
|
||||
ret = gst_h265_parser_identify_and_split_nalu_hevc (parser, data,
|
||||
0, size, nal_length_size, array, &consumed);
|
||||
assert_equals_int (ret, GST_H265_PARSER_OK);
|
||||
assert_equals_int (array->len, 1);
|
||||
assert_equals_int (consumed, size);
|
||||
nal = &g_array_index (array, GstH265NalUnit, 0);
|
||||
assert_equals_int (nal->type, GST_H265_NAL_AUD);
|
||||
assert_equals_int (nal->sc_offset, nal_length_size);
|
||||
assert_equals_int (nal->offset, nal_length_size + sizeof (sc_4bytes));
|
||||
assert_equals_int (nal->size, sizeof (aud));
|
||||
|
||||
/* 3-1) nalu + trailing SC (3 bytes) */
|
||||
size = nal_length_size + sizeof (aud) + sizeof (sc_3bytes);
|
||||
off = nal_length_size;
|
||||
GST_WRITE_UINT32_BE (data, sizeof (aud) + sizeof (sc_3bytes));
|
||||
BUILD_NAL (aud);
|
||||
BUILD_NAL (sc_3bytes);
|
||||
ret = gst_h265_parser_identify_and_split_nalu_hevc (parser, data,
|
||||
0, size, nal_length_size, array, &consumed);
|
||||
assert_equals_int (ret, GST_H265_PARSER_OK);
|
||||
assert_equals_int (array->len, 1);
|
||||
assert_equals_int (consumed, size);
|
||||
nal = &g_array_index (array, GstH265NalUnit, 0);
|
||||
assert_equals_int (nal->type, GST_H265_NAL_AUD);
|
||||
assert_equals_int (nal->sc_offset, 0);
|
||||
assert_equals_int (nal->offset, nal_length_size);
|
||||
assert_equals_int (nal->size, sizeof (aud));
|
||||
|
||||
/* 3-2) nalu + trailing SC (4 bytes) */
|
||||
size = nal_length_size + sizeof (aud) + sizeof (sc_4bytes);
|
||||
off = nal_length_size;
|
||||
GST_WRITE_UINT32_BE (data, sizeof (aud) + sizeof (sc_4bytes));
|
||||
BUILD_NAL (aud);
|
||||
BUILD_NAL (sc_4bytes);
|
||||
ret = gst_h265_parser_identify_and_split_nalu_hevc (parser, data,
|
||||
0, size, nal_length_size, array, &consumed);
|
||||
assert_equals_int (ret, GST_H265_PARSER_OK);
|
||||
assert_equals_int (array->len, 1);
|
||||
assert_equals_int (consumed, size);
|
||||
nal = &g_array_index (array, GstH265NalUnit, 0);
|
||||
assert_equals_int (nal->type, GST_H265_NAL_AUD);
|
||||
assert_equals_int (nal->sc_offset, 0);
|
||||
assert_equals_int (nal->offset, nal_length_size);
|
||||
assert_equals_int (nal->size, sizeof (aud));
|
||||
|
||||
/* 4-1) SC + nalu + SC + nalu */
|
||||
size = nal_length_size + sizeof (sc_3bytes) + sizeof (aud) +
|
||||
sizeof (sc_4bytes) + sizeof (eos);
|
||||
off = nal_length_size;
|
||||
GST_WRITE_UINT32_BE (data, sizeof (sc_3bytes) + sizeof (aud) +
|
||||
sizeof (sc_4bytes) + sizeof (eos));
|
||||
BUILD_NAL (sc_3bytes);
|
||||
BUILD_NAL (aud);
|
||||
BUILD_NAL (sc_4bytes);
|
||||
BUILD_NAL (eos);
|
||||
ret = gst_h265_parser_identify_and_split_nalu_hevc (parser, data,
|
||||
0, size, nal_length_size, array, &consumed);
|
||||
assert_equals_int (ret, GST_H265_PARSER_OK);
|
||||
assert_equals_int (array->len, 2);
|
||||
assert_equals_int (consumed, size);
|
||||
nal = &g_array_index (array, GstH265NalUnit, 0);
|
||||
assert_equals_int (nal->type, GST_H265_NAL_AUD);
|
||||
assert_equals_int (nal->sc_offset, nal_length_size);
|
||||
assert_equals_int (nal->offset, nal_length_size + sizeof (sc_3bytes));
|
||||
assert_equals_int (nal->size, sizeof (aud));
|
||||
nal = &g_array_index (array, GstH265NalUnit, 1);
|
||||
assert_equals_int (nal->type, GST_H265_NAL_EOS);
|
||||
assert_equals_int (nal->sc_offset, nal_length_size + sizeof (sc_3bytes)
|
||||
+ sizeof (aud));
|
||||
assert_equals_int (nal->offset, nal_length_size + sizeof (sc_3bytes)
|
||||
+ sizeof (aud) + sizeof (sc_4bytes));
|
||||
assert_equals_int (nal->size, sizeof (eos));
|
||||
|
||||
/* 4-2) SC + nalu + SC + nalu + trailing SC */
|
||||
size = nal_length_size + sizeof (sc_3bytes) + sizeof (aud) +
|
||||
sizeof (sc_4bytes) + sizeof (eos) + sizeof (sc_3bytes);
|
||||
off = nal_length_size;
|
||||
GST_WRITE_UINT32_BE (data, sizeof (sc_3bytes) + sizeof (aud) +
|
||||
sizeof (sc_4bytes) + sizeof (eos) + sizeof (sc_3bytes));
|
||||
BUILD_NAL (sc_3bytes);
|
||||
BUILD_NAL (aud);
|
||||
BUILD_NAL (sc_4bytes);
|
||||
BUILD_NAL (eos);
|
||||
BUILD_NAL (sc_3bytes);
|
||||
ret = gst_h265_parser_identify_and_split_nalu_hevc (parser, data,
|
||||
0, size, nal_length_size, array, &consumed);
|
||||
assert_equals_int (ret, GST_H265_PARSER_OK);
|
||||
assert_equals_int (array->len, 2);
|
||||
assert_equals_int (consumed, size);
|
||||
nal = &g_array_index (array, GstH265NalUnit, 0);
|
||||
assert_equals_int (nal->type, GST_H265_NAL_AUD);
|
||||
assert_equals_int (nal->sc_offset, nal_length_size);
|
||||
assert_equals_int (nal->offset, nal_length_size + sizeof (sc_3bytes));
|
||||
assert_equals_int (nal->size, sizeof (aud));
|
||||
nal = &g_array_index (array, GstH265NalUnit, 1);
|
||||
assert_equals_int (nal->type, GST_H265_NAL_EOS);
|
||||
assert_equals_int (nal->sc_offset, nal_length_size + sizeof (sc_3bytes)
|
||||
+ sizeof (aud));
|
||||
assert_equals_int (nal->offset, nal_length_size + sizeof (sc_3bytes)
|
||||
+ sizeof (aud) + sizeof (sc_4bytes));
|
||||
assert_equals_int (nal->size, sizeof (eos));
|
||||
|
||||
/* 4-3) nalu + SC + nalu */
|
||||
size = nal_length_size + sizeof (aud) + sizeof (sc_4bytes) + sizeof (eos);
|
||||
off = nal_length_size;
|
||||
GST_WRITE_UINT32_BE (data, sizeof (aud) + sizeof (sc_4bytes) + sizeof (eos));
|
||||
BUILD_NAL (aud);
|
||||
BUILD_NAL (sc_4bytes);
|
||||
BUILD_NAL (eos);
|
||||
ret = gst_h265_parser_identify_and_split_nalu_hevc (parser, data,
|
||||
0, size, nal_length_size, array, &consumed);
|
||||
assert_equals_int (ret, GST_H265_PARSER_OK);
|
||||
assert_equals_int (array->len, 2);
|
||||
assert_equals_int (consumed, size);
|
||||
nal = &g_array_index (array, GstH265NalUnit, 0);
|
||||
assert_equals_int (nal->type, GST_H265_NAL_AUD);
|
||||
assert_equals_int (nal->sc_offset, 0);
|
||||
assert_equals_int (nal->offset, nal_length_size);
|
||||
assert_equals_int (nal->size, sizeof (aud));
|
||||
nal = &g_array_index (array, GstH265NalUnit, 1);
|
||||
assert_equals_int (nal->type, GST_H265_NAL_EOS);
|
||||
assert_equals_int (nal->sc_offset, nal_length_size + sizeof (aud));
|
||||
assert_equals_int (nal->offset,
|
||||
nal_length_size + sizeof (aud) + sizeof (sc_4bytes));
|
||||
assert_equals_int (nal->size, sizeof (eos));
|
||||
|
||||
#undef BUILD_NAL
|
||||
|
||||
gst_h265_parser_free (parser);
|
||||
g_array_unref (array);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
static Suite *
|
||||
h265parser_suite (void)
|
||||
{
|
||||
|
@ -1197,6 +1394,7 @@ h265parser_suite (void)
|
|||
tcase_add_test (tc_chain, test_h265_nal_type_classification);
|
||||
tcase_add_test (tc_chain, test_h265_sei_registered_user_data);
|
||||
tcase_add_test (tc_chain, test_h265_create_sei);
|
||||
tcase_add_test (tc_chain, test_h265_split_hevc);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue