}
/**
+ * 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
* @nalu: The #GstH265NalUnit to parse
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)
{
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;
}