mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 16:50:47 +00:00
h264parser: Add gst_h264_parser_identify_and_split_nalu_avc() method
Equivalent to _split_nalu_hevc() method in h265parser. This method will scan start-code prefix and split into individual NAL units if start-code prefix is detected Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5772>
This commit is contained in:
parent
396c5aef7c
commit
c737799900
3 changed files with 380 additions and 0 deletions
|
@ -1651,6 +1651,179 @@ gst_h264_parser_identify_nalu_avc (GstH264NalParser * nalparser,
|
|||
return GST_H264_PARSER_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_h264_parser_identify_and_split_nalu_avc:
|
||||
* @nalparser: a #GstH264NalParser
|
||||
* @data: The data to parse, containing an AVC coded NAL unit
|
||||
* @offset: the offset in @data from which to parse the NAL unit
|
||||
* @size: the size of @data
|
||||
* @nal_length_size: the size in bytes of the AVC nal length prefix.
|
||||
* @nalus: a caller allocated GArray of #GstH264NalUnit where to store parsed nal headers
|
||||
* @consumed: (out): the size of consumed bytes
|
||||
*
|
||||
* Parses @data for packetized (e.g., avc/avc3) 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 #GstH264ParserResult
|
||||
*
|
||||
* Since: 1.22.9
|
||||
*/
|
||||
GstH264ParserResult
|
||||
gst_h264_parser_identify_and_split_nalu_avc (GstH264NalParser * nalparser,
|
||||
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_H264_PARSER_ERROR);
|
||||
g_return_val_if_fail (nalus != NULL, GST_H264_PARSER_ERROR);
|
||||
g_return_val_if_fail (nal_length_size > 0 && nal_length_size < 5,
|
||||
GST_H264_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_H264_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_H264_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 < 1) {
|
||||
GST_WARNING ("too small nal size %d", nalu_size);
|
||||
return GST_H264_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_H264_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 {
|
||||
GstH264NalUnit nalu;
|
||||
gint sc_offset = -1;
|
||||
guint skip_size = 0;
|
||||
|
||||
memset (&nalu, 0, sizeof (GstH264NalUnit));
|
||||
|
||||
/* startcode 3 bytes + minimum nal size 1 */
|
||||
if (remaining >= 4)
|
||||
sc_offset = scan_for_start_codes (data + off, remaining);
|
||||
|
||||
if (sc_offset < 0) {
|
||||
if (remaining >= 1) {
|
||||
/* CONDITION-A */
|
||||
/* Last chunk */
|
||||
nalu.size = remaining;
|
||||
nalu.sc_offset = off - sc_size;
|
||||
nalu.offset = off;
|
||||
nalu.data = (guint8 *) data;
|
||||
nalu.valid = TRUE;
|
||||
|
||||
gst_h264_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_h264_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 >= 1);
|
||||
|
||||
if (nalus->len > 0)
|
||||
return GST_H264_PARSER_OK;
|
||||
|
||||
GST_WARNING ("No nal found");
|
||||
|
||||
return GST_H264_PARSER_BROKEN_DATA;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_h264_parser_parse_nal:
|
||||
* @nalparser: a #GstH264NalParser
|
||||
|
|
|
@ -1440,6 +1440,15 @@ GstH264ParserResult gst_h264_parser_identify_nalu_avc (GstH264NalParser *nalpars
|
|||
guint offset, gsize size, guint8 nal_length_size,
|
||||
GstH264NalUnit *nalu);
|
||||
|
||||
GST_CODEC_PARSERS_API
|
||||
GstH264ParserResult gst_h264_parser_identify_and_split_nalu_avc (GstH264NalParser *nalparser,
|
||||
const guint8 *data,
|
||||
guint offset,
|
||||
gsize size,
|
||||
guint8 nal_length_size,
|
||||
GArray * nalus,
|
||||
gsize * consumed);
|
||||
|
||||
GST_CODEC_PARSERS_API
|
||||
GstH264ParserResult gst_h264_parser_parse_nal (GstH264NalParser *nalparser,
|
||||
GstH264NalUnit *nalu);
|
||||
|
|
|
@ -878,6 +878,203 @@ GST_START_TEST (test_h264_parse_partial_nal_header)
|
|||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_h264_split_avc)
|
||||
{
|
||||
GstH264NalParser *parser;
|
||||
GArray *array;
|
||||
GstH264NalUnit *nal;
|
||||
static const guint8 aud[] = { 0x09, 0xf0 };
|
||||
static const guint8 seq_end[] = { 0x0a };
|
||||
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;
|
||||
GstH264ParserResult ret;
|
||||
gsize consumed;
|
||||
guint off;
|
||||
|
||||
parser = gst_h264_nal_parser_new ();
|
||||
array = g_array_new (FALSE, FALSE, sizeof (GstH264NalUnit));
|
||||
|
||||
#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_h264_parser_identify_and_split_nalu_avc (parser, data,
|
||||
0, size, nal_length_size, array, &consumed);
|
||||
assert_equals_int (ret, GST_H264_PARSER_OK);
|
||||
assert_equals_int (array->len, 1);
|
||||
assert_equals_int (consumed, size);
|
||||
nal = &g_array_index (array, GstH264NalUnit, 0);
|
||||
assert_equals_int (nal->type, GST_H264_NAL_AU_DELIMITER);
|
||||
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_h264_parser_identify_and_split_nalu_avc (parser, data,
|
||||
0, size, nal_length_size, array, &consumed);
|
||||
assert_equals_int (ret, GST_H264_PARSER_OK);
|
||||
assert_equals_int (array->len, 1);
|
||||
assert_equals_int (consumed, size);
|
||||
nal = &g_array_index (array, GstH264NalUnit, 0);
|
||||
assert_equals_int (nal->type, GST_H264_NAL_AU_DELIMITER);
|
||||
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_h264_parser_identify_and_split_nalu_avc (parser, data,
|
||||
0, size, nal_length_size, array, &consumed);
|
||||
assert_equals_int (ret, GST_H264_PARSER_OK);
|
||||
assert_equals_int (array->len, 1);
|
||||
assert_equals_int (consumed, size);
|
||||
nal = &g_array_index (array, GstH264NalUnit, 0);
|
||||
assert_equals_int (nal->type, GST_H264_NAL_AU_DELIMITER);
|
||||
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_h264_parser_identify_and_split_nalu_avc (parser, data,
|
||||
0, size, nal_length_size, array, &consumed);
|
||||
assert_equals_int (ret, GST_H264_PARSER_OK);
|
||||
assert_equals_int (array->len, 1);
|
||||
assert_equals_int (consumed, size);
|
||||
nal = &g_array_index (array, GstH264NalUnit, 0);
|
||||
assert_equals_int (nal->type, GST_H264_NAL_AU_DELIMITER);
|
||||
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_h264_parser_identify_and_split_nalu_avc (parser, data,
|
||||
0, size, nal_length_size, array, &consumed);
|
||||
assert_equals_int (ret, GST_H264_PARSER_OK);
|
||||
assert_equals_int (array->len, 1);
|
||||
assert_equals_int (consumed, size);
|
||||
nal = &g_array_index (array, GstH264NalUnit, 0);
|
||||
assert_equals_int (nal->type, GST_H264_NAL_AU_DELIMITER);
|
||||
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 (seq_end);
|
||||
off = nal_length_size;
|
||||
GST_WRITE_UINT32_BE (data, sizeof (sc_3bytes) + sizeof (aud) +
|
||||
sizeof (sc_4bytes) + sizeof (seq_end));
|
||||
BUILD_NAL (sc_3bytes);
|
||||
BUILD_NAL (aud);
|
||||
BUILD_NAL (sc_4bytes);
|
||||
BUILD_NAL (seq_end);
|
||||
ret = gst_h264_parser_identify_and_split_nalu_avc (parser, data,
|
||||
0, size, nal_length_size, array, &consumed);
|
||||
assert_equals_int (ret, GST_H264_PARSER_OK);
|
||||
assert_equals_int (array->len, 2);
|
||||
assert_equals_int (consumed, size);
|
||||
nal = &g_array_index (array, GstH264NalUnit, 0);
|
||||
assert_equals_int (nal->type, GST_H264_NAL_AU_DELIMITER);
|
||||
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, GstH264NalUnit, 1);
|
||||
assert_equals_int (nal->type, GST_H264_NAL_SEQ_END);
|
||||
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 (seq_end));
|
||||
|
||||
/* 4-2) SC + nalu + SC + nalu + trailing SC */
|
||||
size = nal_length_size + sizeof (sc_3bytes) + sizeof (aud) +
|
||||
sizeof (sc_4bytes) + sizeof (seq_end) + sizeof (sc_3bytes);
|
||||
off = nal_length_size;
|
||||
GST_WRITE_UINT32_BE (data, sizeof (sc_3bytes) + sizeof (aud) +
|
||||
sizeof (sc_4bytes) + sizeof (seq_end) + sizeof (sc_3bytes));
|
||||
BUILD_NAL (sc_3bytes);
|
||||
BUILD_NAL (aud);
|
||||
BUILD_NAL (sc_4bytes);
|
||||
BUILD_NAL (seq_end);
|
||||
BUILD_NAL (sc_3bytes);
|
||||
ret = gst_h264_parser_identify_and_split_nalu_avc (parser, data,
|
||||
0, size, nal_length_size, array, &consumed);
|
||||
assert_equals_int (ret, GST_H264_PARSER_OK);
|
||||
assert_equals_int (array->len, 2);
|
||||
assert_equals_int (consumed, size);
|
||||
nal = &g_array_index (array, GstH264NalUnit, 0);
|
||||
assert_equals_int (nal->type, GST_H264_NAL_AU_DELIMITER);
|
||||
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, GstH264NalUnit, 1);
|
||||
assert_equals_int (nal->type, GST_H264_NAL_SEQ_END);
|
||||
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 (seq_end));
|
||||
|
||||
/* 4-3) nalu + SC + nalu */
|
||||
size = nal_length_size + sizeof (aud) + sizeof (sc_4bytes) + sizeof (seq_end);
|
||||
off = nal_length_size;
|
||||
GST_WRITE_UINT32_BE (data, sizeof (aud) + sizeof (sc_4bytes) +
|
||||
sizeof (seq_end));
|
||||
BUILD_NAL (aud);
|
||||
BUILD_NAL (sc_4bytes);
|
||||
BUILD_NAL (seq_end);
|
||||
ret = gst_h264_parser_identify_and_split_nalu_avc (parser, data,
|
||||
0, size, nal_length_size, array, &consumed);
|
||||
assert_equals_int (ret, GST_H264_PARSER_OK);
|
||||
assert_equals_int (array->len, 2);
|
||||
assert_equals_int (consumed, size);
|
||||
nal = &g_array_index (array, GstH264NalUnit, 0);
|
||||
assert_equals_int (nal->type, GST_H264_NAL_AU_DELIMITER);
|
||||
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, GstH264NalUnit, 1);
|
||||
assert_equals_int (nal->type, GST_H264_NAL_SEQ_END);
|
||||
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 (seq_end));
|
||||
|
||||
#undef BUILD_NAL
|
||||
|
||||
gst_h264_nal_parser_free (parser);
|
||||
g_array_unref (array);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
static Suite *
|
||||
h264parser_suite (void)
|
||||
|
@ -895,6 +1092,7 @@ h264parser_suite (void)
|
|||
tcase_add_test (tc_chain, test_h264_create_sei);
|
||||
tcase_add_test (tc_chain, test_h264_decoder_config_record);
|
||||
tcase_add_test (tc_chain, test_h264_parse_partial_nal_header);
|
||||
tcase_add_test (tc_chain, test_h264_split_avc);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue