h26xparse: Drop NAL units that can't be parsed using AU alignment

Change so that the handling of NAL unit that can't be parsed when using
AU alignment is the same as when using NAL alignment, ie drop the data
if it can't be parsed.

If the AU contains more than one NAL unit any correctly parsed NAL unit
in the AU is kept.

Fixes #4436

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/8976>
This commit is contained in:
Stefan Andersson 2025-05-09 16:45:53 +02:00 committed by GStreamer Marge Bot
parent a58fd15c91
commit 38f5ab75fd
6 changed files with 467 additions and 30 deletions

View File

@ -1317,7 +1317,6 @@ gst_h264_parse_handle_frame_packetized (GstBaseParse * parse,
* a replacement output buffer is provided anyway. */
gst_h264_parse_parse_frame (parse, &tmp_frame);
ret = gst_base_parse_finish_frame (parse, &tmp_frame, nl + nalu.size);
left -= nl + nalu.size;
/* Bail out if we get a flow error. */
if (ret != GST_FLOW_OK) {
@ -1326,6 +1325,7 @@ gst_h264_parse_handle_frame_packetized (GstBaseParse * parse,
return ret;
}
}
left -= nl + nalu.size;
parse_res = gst_h264_parser_identify_nalu_avc (h264parse->nalparser,
map.data, nalu.offset + nalu.size, map.size, nl, &nalu);
@ -1334,17 +1334,53 @@ gst_h264_parse_handle_frame_packetized (GstBaseParse * parse,
gst_buffer_unmap (buffer, &map);
if (!h264parse->split_packetized) {
h264parse->marker = TRUE;
gst_h264_parse_parse_frame (parse, frame);
ret = gst_base_parse_finish_frame (parse, frame, map.size);
gint parsed = map.size - left;
/* Nothing to do if no NAL unit was parsed, the whole AU will be dropped
* below. */
if (parsed > 0) {
if (G_UNLIKELY (left)) {
/* Only part of the AU could be parsed, split out that part the rest
* will be dropped below. Should not be happening for nice AVC. */
GST_WARNING_OBJECT (parse, "Problem parsing part of AU, keep part that "
"has been correctly parsed (%d bytes).", parsed);
buffer = gst_buffer_copy (frame->buffer);
GstBaseParseFrame tmp_frame;
gst_base_parse_frame_init (&tmp_frame);
tmp_frame.flags |= frame->flags;
tmp_frame.offset = frame->offset;
tmp_frame.overhead = frame->overhead;
tmp_frame.buffer = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL,
0, parsed);
h264parse->marker = TRUE;
gst_h264_parse_parse_frame (parse, &tmp_frame);
ret = gst_base_parse_finish_frame (parse, &tmp_frame, parsed);
gst_buffer_unref (buffer);
/* Bail out if we get a flow error. */
if (ret != GST_FLOW_OK) {
gst_buffer_unmap (buffer, &map);
gst_buffer_unref (buffer);
return ret;
}
} else {
/* The whole AU succesfully parsed. */
h264parse->marker = TRUE;
gst_h264_parse_parse_frame (parse, frame);
ret = gst_base_parse_finish_frame (parse, frame, map.size);
}
}
} else {
gst_buffer_unref (buffer);
if (G_UNLIKELY (left)) {
/* should not be happening for nice AVC */
GST_WARNING_OBJECT (parse, "skipping leftover AVC data %d", left);
frame->flags |= GST_BASE_PARSE_FRAME_FLAG_DROP;
ret = gst_base_parse_finish_frame (parse, frame, left);
}
}
if (G_UNLIKELY (left)) {
/* should not be happening for nice AVC */
GST_WARNING_OBJECT (parse, "skipping leftover AVC data %d", left);
frame->flags |= GST_BASE_PARSE_FRAME_FLAG_DROP;
ret = gst_base_parse_finish_frame (parse, frame, left);
}
if (parse_res == GST_H264_PARSER_NO_NAL_END ||

View File

@ -1113,7 +1113,6 @@ gst_h265_parse_handle_frame_packetized (GstBaseParse * parse,
* a replacement output buffer is provided anyway. */
gst_h265_parse_parse_frame (parse, &tmp_frame);
ret = gst_base_parse_finish_frame (parse, &tmp_frame, nl + nalu.size);
left -= nl + nalu.size;
/* Bail out if we get a flow error. */
if (ret != GST_FLOW_OK) {
@ -1122,6 +1121,7 @@ gst_h265_parse_handle_frame_packetized (GstBaseParse * parse,
return ret;
}
}
left -= nl + nalu.size;
parse_res = gst_h265_parser_identify_nalu_hevc (h265parse->nalparser,
map.data, nalu.offset + nalu.size, map.size, nl, &nalu);
@ -1130,17 +1130,53 @@ gst_h265_parse_handle_frame_packetized (GstBaseParse * parse,
gst_buffer_unmap (buffer, &map);
if (!h265parse->split_packetized) {
h265parse->marker = TRUE;
gst_h265_parse_parse_frame (parse, frame);
ret = gst_base_parse_finish_frame (parse, frame, map.size);
gint parsed = map.size - left;
/* Nothing to do if no NAL unit was parsed, the whole AU will be dropped
* below. */
if (parsed > 0) {
if (G_UNLIKELY (left)) {
/* Only part of the AU could be parsed, split out that part the rest
* will be dropped below. Should not be happening for nice HEVC. */
GST_WARNING_OBJECT (parse, "Problem parsing part of AU, keep part that "
"has been correctly parsed (%d bytes).", parsed);
buffer = gst_buffer_copy (frame->buffer);
GstBaseParseFrame tmp_frame;
gst_base_parse_frame_init (&tmp_frame);
tmp_frame.flags |= frame->flags;
tmp_frame.offset = frame->offset;
tmp_frame.overhead = frame->overhead;
tmp_frame.buffer = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL,
0, parsed);
h265parse->marker = TRUE;
gst_h265_parse_parse_frame (parse, &tmp_frame);
ret = gst_base_parse_finish_frame (parse, &tmp_frame, parsed);
gst_buffer_unref (buffer);
/* Bail out if we get a flow error. */
if (ret != GST_FLOW_OK) {
gst_buffer_unmap (buffer, &map);
gst_buffer_unref (buffer);
return ret;
}
} else {
/* The whole AU succesfully parsed. */
h265parse->marker = TRUE;
gst_h265_parse_parse_frame (parse, frame);
ret = gst_base_parse_finish_frame (parse, frame, map.size);
}
}
} else {
gst_buffer_unref (buffer);
if (G_UNLIKELY (left)) {
/* should not be happening for nice HEVC */
GST_WARNING_OBJECT (parse, "skipping leftover HEVC data %d", left);
frame->flags |= GST_BASE_PARSE_FRAME_FLAG_DROP;
ret = gst_base_parse_finish_frame (parse, frame, left);
}
}
if (G_UNLIKELY (left)) {
/* should not be happening for nice HEVC */
GST_WARNING_OBJECT (parse, "skipping leftover HEVC data %d", left);
frame->flags |= GST_BASE_PARSE_FRAME_FLAG_DROP;
ret = gst_base_parse_finish_frame (parse, frame, left);
}
if (parse_res == GST_H265_PARSER_NO_NAL_END ||

View File

@ -1050,7 +1050,6 @@ gst_h266_parse_handle_frame_packetized (GstBaseParse * parse,
* a replacement output buffer is provided anyway. */
gst_h266_parse_parse_frame (parse, &tmp_frame);
ret = gst_base_parse_finish_frame (parse, &tmp_frame, nl + nalu.size);
left -= nl + nalu.size;
/* Bail out if we get a flow error. */
if (ret != GST_FLOW_OK) {
@ -1059,6 +1058,7 @@ gst_h266_parse_handle_frame_packetized (GstBaseParse * parse,
return ret;
}
}
left -= nl + nalu.size;
parse_res = gst_h266_parser_identify_nalu_vvc (h266parse->nalparser,
map.data, nalu.offset + nalu.size, map.size, nl, &nalu);
@ -1067,17 +1067,53 @@ gst_h266_parse_handle_frame_packetized (GstBaseParse * parse,
gst_buffer_unmap (buffer, &map);
if (!h266parse->split_packetized) {
h266parse->marker = TRUE;
gst_h266_parse_parse_frame (parse, frame);
ret = gst_base_parse_finish_frame (parse, frame, map.size);
gint parsed = map.size - left;
/* Nothing to do if no NAL unit was parsed, the whole AU will be dropped
* below. */
if (parsed > 0) {
if (G_UNLIKELY (left)) {
/* Only part of the AU could be parsed, split out that part the rest
* will be dropped below. Should not be happening for nice VVC. */
GST_WARNING_OBJECT (parse, "Problem parsing part of AU, keep part that "
"has been correctly parsed (%d bytes).", parsed);
buffer = gst_buffer_copy (frame->buffer);
GstBaseParseFrame tmp_frame;
gst_base_parse_frame_init (&tmp_frame);
tmp_frame.flags |= frame->flags;
tmp_frame.offset = frame->offset;
tmp_frame.overhead = frame->overhead;
tmp_frame.buffer = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL,
0, parsed);
h266parse->marker = TRUE;
gst_h266_parse_parse_frame (parse, &tmp_frame);
ret = gst_base_parse_finish_frame (parse, &tmp_frame, parsed);
gst_buffer_unref (buffer);
/* Bail out if we get a flow error. */
if (ret != GST_FLOW_OK) {
gst_buffer_unmap (buffer, &map);
gst_buffer_unref (buffer);
return ret;
}
} else {
/* The whole AU succesfully parsed. */
h266parse->marker = TRUE;
gst_h266_parse_parse_frame (parse, frame);
ret = gst_base_parse_finish_frame (parse, frame, map.size);
}
}
} else {
gst_buffer_unref (buffer);
if (G_UNLIKELY (left)) {
/* should not be happening for nice VVC */
GST_WARNING_OBJECT (parse, "skipping leftover VVC data %d", left);
frame->flags |= GST_BASE_PARSE_FRAME_FLAG_DROP;
ret = gst_base_parse_finish_frame (parse, frame, left);
}
}
if (G_UNLIKELY (left)) {
/* should not be happening for nice VVC */
GST_WARNING_OBJECT (parse, "skipping leftover VVC data %d", left);
frame->flags |= GST_BASE_PARSE_FRAME_FLAG_DROP;
ret = gst_base_parse_finish_frame (parse, frame, left);
}
if (parse_res == GST_H266_PARSER_NO_NAL_END ||

View File

@ -1593,6 +1593,110 @@ GST_START_TEST (test_parse_to_avc3_without_sps)
GST_END_TEST;
GST_START_TEST (test_packetized_avc_drop_corrupt)
{
GstBuffer *cdata;
GstCaps *in_caps, *out_caps;
GstHarness *h = gst_harness_new ("h264parse");
GstBuffer *buf, *bufout;
GstMapInfo mapout;
in_caps = gst_caps_from_string (stream_type_to_caps_str (PACKETIZED_AU));
cdata =
gst_buffer_new_memdup (h264_avc_codec_data, sizeof (h264_avc_codec_data));
gst_caps_set_simple (in_caps, "codec_data", GST_TYPE_BUFFER, cdata,
"stream-format", G_TYPE_STRING, "avc", NULL);
gst_buffer_unref (cdata);
out_caps = gst_caps_from_string (stream_type_to_caps_str (PACKETIZED_AU));
gst_harness_set_caps (h, in_caps, out_caps);
/* avc idr frame nal */
static guint8 *h264_idr_avc;
/* make avc idr frame NAL */
h264_idr_avc = g_malloc (sizeof (h264_idrframe));
GST_WRITE_UINT32_BE (h264_idr_avc, sizeof (h264_idrframe) - 4);
memcpy (h264_idr_avc + 4, h264_idrframe + 4, sizeof (h264_idrframe) - 4);
static guint8 h264_garbage_avc[] = {
0x00, 0x00, 0x00, 0x00, 0x05
};
/* Send all => drop garbage end but keep correct frame. */
buf = composite_buffer (100, 0, 2, h264_idr_avc, sizeof (h264_idrframe),
h264_garbage_avc, sizeof (h264_garbage_avc));
fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK);
/* Send 3 IDR frames => all should be kept. */
buf = composite_buffer (200, 0, 3, h264_idr_avc, sizeof (h264_idrframe),
h264_idr_avc, sizeof (h264_idrframe), h264_idr_avc,
sizeof (h264_idrframe));
fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK);
/* Send 2 IDR and one garbage => keep the first two and drop garabage. */
buf = composite_buffer (300, 0, 3, h264_idr_avc, sizeof (h264_idrframe),
h264_idr_avc, sizeof (h264_idrframe), h264_garbage_avc,
sizeof (h264_garbage_avc));
fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK);
/* Only send part of correct frame => drop everything */
buf = wrap_buffer (h264_idr_avc, sizeof (h264_idrframe) - 10, 400, 0);
fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK);
/* Send garbage frame => drop everything */
buf = wrap_buffer (h264_garbage_avc, sizeof (h264_garbage_avc), 500, 0);
fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK);
/* EOS for pending buffers to be drained if any */
gst_harness_push_event (h, gst_event_new_eos ());
fail_unless_equals_int (gst_harness_buffers_received (h), 3);
/* Verify IDR + garbage. */
bufout = gst_harness_pull (h);
fail_unless (bufout != NULL);
gsize sps_pps_sz = sizeof (h264_sps) + sizeof (h264_pps);
gst_buffer_map (bufout, &mapout, GST_MAP_READ);
fail_unless_equals_int (mapout.size, sps_pps_sz + sizeof (h264_idrframe));
fail_unless (memcmp (mapout.data + sps_pps_sz,
h264_idr_avc, sizeof (h264_idrframe)) == 0);
gst_buffer_unmap (bufout, &mapout);
gst_buffer_unref (bufout);
/* Verify 3 * IDR. */
bufout = gst_harness_pull (h);
fail_unless (bufout != NULL);
gst_buffer_map (bufout, &mapout, GST_MAP_READ);
fail_unless_equals_int (mapout.size, 3 * sizeof (h264_idrframe));
fail_unless (memcmp (mapout.data, h264_idr_avc, sizeof (h264_idrframe)) == 0);
fail_unless (memcmp (mapout.data + sizeof (h264_idrframe), h264_idr_avc,
sizeof (h264_idrframe)) == 0);
fail_unless (memcmp (mapout.data + 2 * sizeof (h264_idrframe), h264_idr_avc,
sizeof (h264_idrframe)) == 0);
gst_buffer_unmap (bufout, &mapout);
gst_buffer_unref (bufout);
/* Verify 2 * IDR + garbage. */
bufout = gst_harness_pull (h);
fail_unless (bufout != NULL);
gst_buffer_map (bufout, &mapout, GST_MAP_READ);
fail_unless_equals_int (mapout.size, 2 * sizeof (h264_idrframe));
fail_unless (memcmp (mapout.data, h264_idr_avc, sizeof (h264_idrframe)) == 0);
fail_unless (memcmp (mapout.data + sizeof (h264_idrframe), h264_idr_avc,
sizeof (h264_idrframe)) == 0);
gst_buffer_unmap (bufout, &mapout);
gst_buffer_unref (bufout);
gst_harness_teardown (h);
}
GST_END_TEST;
/*
* TODO:
* - Both push- and pull-modes need to be tested
@ -1707,6 +1811,7 @@ main (int argc, char **argv)
tcase_add_test (tc_chain, test_parse_aud_insert);
tcase_add_test (tc_chain, test_parse_sei_userdefinedunregistered);
tcase_add_test (tc_chain, test_parse_to_avc3_without_sps);
tcase_add_test (tc_chain, test_packetized_avc_drop_corrupt);
nf += gst_check_run_suite (s, "h264parse", __FILE__);
}

View File

@ -51,6 +51,18 @@ GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
*
*/
static const guint8 h265_hvcc_codec_data[] = {
0x01, 0x04, 0x08, 0x00, 0x00, 0x00, 0x98, 0x08, 0x00, 0x00, 0x00, 0x00, 0x3f,
0xf0, 0x00, 0xfc, 0xff, 0xfc, 0xfc, 0x00, 0x00, 0x0f, 0x03, 0x20, 0x00, 0x01,
0x00, 0x17, 0x40, 0x01, 0x0c, 0x01, 0xff, 0xff, 0x04, 0x08, 0x00, 0x00, 0x03,
0x00, 0x98, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x3f, 0x95, 0x98, 0x09, 0x21,
0x00, 0x01, 0x00, 0x2f, 0x42, 0x01, 0x01, 0x04, 0x08, 0x00, 0x00, 0x03, 0x00,
0x98, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x3f, 0x90, 0x11, 0x08, 0x8a, 0x52,
0xca, 0xcd, 0x57, 0x95, 0xff, 0xe0, 0x00, 0x20, 0x00, 0x2d, 0x41, 0x81, 0x81,
0x81, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x03, 0x00, 0x1e, 0x08, 0x22,
0x00, 0x01, 0x00, 0x06, 0x44, 0x01, 0xc1, 0x73, 0xd0, 0x89
};
static const guint8 h265_vps[] = {
0x00, 0x00, 0x00, 0x01, 0x40, 0x01, 0x0c, 0x01, 0xff, 0xff, 0x01, 0x60, 0x00,
0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x3f, 0x95,
@ -1233,6 +1245,109 @@ GST_START_TEST (test_invalid_sei_in_hvcc)
GST_END_TEST;
GST_START_TEST (test_packetized_hvcc_drop_corrupt)
{
GstBuffer *cdata;
GstCaps *in_caps, *out_caps;
GstHarness *h = gst_harness_new ("h265parse");
GstBuffer *buf, *bufout;
GstMapInfo mapout;
const gchar *in_caps_str =
"video/x-h265, stream-format=(string)hvc1, alignment=(string)au";
const gchar *out_caps_str =
"video/x-h265, stream-format=(string)hvc1, alignment=(string)au";
in_caps = gst_caps_from_string (in_caps_str);
cdata = gst_buffer_new_memdup (h265_hvcc_codec_data,
sizeof (h265_hvcc_codec_data));
gst_caps_set_simple (in_caps, "codec_data", GST_TYPE_BUFFER, cdata, NULL);
gst_buffer_unref (cdata);
out_caps = gst_caps_from_string (out_caps_str);
gst_harness_set_caps (h, in_caps, out_caps);
/* hvcc idr frame nal */
static guint8 *h265_idr_hvcc;
/* make hvcc frame NAL */
h265_idr_hvcc = g_malloc (sizeof (h265_idr));
GST_WRITE_UINT32_BE (h265_idr_hvcc, sizeof (h265_idr) - 4);
memcpy (h265_idr_hvcc + 4, h265_idr + 4, sizeof (h265_idr) - 4);
static guint8 h265_garbage_hvcc[] = {
0x00, 0x00, 0x00, 0x00, 0x05
};
/* Send all => drop garbage end but keep correct frame. */
buf = composite_buffer (100, 0, 2, h265_idr_hvcc, sizeof (h265_idr),
h265_garbage_hvcc, sizeof (h265_garbage_hvcc));
fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK);
/* Send 3 IDR frames => all should be kept. */
buf = composite_buffer (200, 0, 3, h265_idr_hvcc, sizeof (h265_idr),
h265_idr_hvcc, sizeof (h265_idr), h265_idr_hvcc, sizeof (h265_idr));
fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK);
/* Send 2 IDR and one garbage => keep the first two and drop garabage. */
buf = composite_buffer (300, 0, 3, h265_idr_hvcc, sizeof (h265_idr),
h265_idr_hvcc, sizeof (h265_idr), h265_garbage_hvcc,
sizeof (h265_garbage_hvcc));
fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK);
/* Only send part of correct frame => drop everything */
buf = wrap_buffer (h265_idr_hvcc, sizeof (h265_idr) - 10, 400, 0);
fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK);
/* Send garbage frame => drop everything */
buf = wrap_buffer (h265_garbage_hvcc, sizeof (h265_garbage_hvcc), 500, 0);
fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK);
/* EOS for pending buffers to be drained if any */
gst_harness_push_event (h, gst_event_new_eos ());
fail_unless_equals_int (gst_harness_buffers_received (h), 3);
bufout = gst_harness_pull (h);
fail_unless (bufout != NULL);
/* Verify IDR + garbage. */
gst_buffer_map (bufout, &mapout, GST_MAP_READ);
fail_unless_equals_int (mapout.size, sizeof (h265_idr));
fail_unless (memcmp (mapout.data, h265_idr_hvcc, sizeof (h265_idr)) == 0);
gst_buffer_unmap (bufout, &mapout);
gst_buffer_unref (bufout);
/* Verify 3 * IDR. */
bufout = gst_harness_pull (h);
fail_unless (bufout != NULL);
gst_buffer_map (bufout, &mapout, GST_MAP_READ);
fail_unless_equals_int (mapout.size, 3 * sizeof (h265_idr));
fail_unless (memcmp (mapout.data, h265_idr_hvcc, sizeof (h265_idr)) == 0);
fail_unless (memcmp (mapout.data + sizeof (h265_idr), h265_idr_hvcc,
sizeof (h265_idr)) == 0);
fail_unless (memcmp (mapout.data + 2 * sizeof (h265_idr), h265_idr_hvcc,
sizeof (h265_idr)) == 0);
gst_buffer_unmap (bufout, &mapout);
gst_buffer_unref (bufout);
/* Verify 2 * IDR + garbage. */
bufout = gst_harness_pull (h);
fail_unless (bufout != NULL);
gst_buffer_map (bufout, &mapout, GST_MAP_READ);
fail_unless_equals_int (mapout.size, 2 * sizeof (h265_idr));
fail_unless (memcmp (mapout.data, h265_idr_hvcc, sizeof (h265_idr)) == 0);
fail_unless (memcmp (mapout.data + sizeof (h265_idr), h265_idr_hvcc,
sizeof (h265_idr)) == 0);
gst_buffer_unmap (bufout, &mapout);
gst_buffer_unref (bufout);
g_free (h265_idr_hvcc);
gst_harness_teardown (h);
}
GST_END_TEST;
static Suite *
h265parse_harnessed_suite (void)
{
@ -1271,6 +1386,7 @@ h265parse_harnessed_suite (void)
tcase_add_test (tc_chain, test_parse_sei_userdefinedunregistered);
tcase_add_test (tc_chain, test_invalid_sei_in_hvcc);
tcase_add_test (tc_chain, test_packetized_hvcc_drop_corrupt);
return s;
}

View File

@ -1230,6 +1230,112 @@ GST_START_TEST (test_drain)
GST_END_TEST;
GST_START_TEST (test_packetized_vvc1_drop_corrupt)
{
GstBuffer *cdata;
GstCaps *in_caps, *out_caps;
GstHarness *h = gst_harness_new ("h266parse");
GstBuffer *buf, *bufout;
GstMapInfo mapout;
const gchar *in_caps_str =
"video/x-h266, parsed=(boolean)false, stream-format=vvc1, alignment=au";
const gchar *out_caps_str =
"video/x-h266, parsed=(boolean)true, stream-format=vvc1, alignment=au";
in_caps = gst_caps_from_string (in_caps_str);
cdata = gst_buffer_new_memdup (h266_vvc1_codec_data,
sizeof (h266_vvc1_codec_data));
gst_caps_set_simple (in_caps, "codec_data", GST_TYPE_BUFFER, cdata,
"stream-format", G_TYPE_STRING, "vvc1", NULL);
gst_buffer_unref (cdata);
out_caps = gst_caps_from_string (out_caps_str);
gst_harness_set_caps (h, in_caps, out_caps);
/* vvc1 idr frame nal */
static guint8 *h266_idr_vvc1;
/* make vvc1 idr frame NAL */
h266_idr_vvc1 = g_malloc (sizeof (h266_idr));
GST_WRITE_UINT32_BE (h266_idr_vvc1, sizeof (h266_idr) - 4);
memcpy (h266_idr_vvc1 + 4, h266_idr + 4, sizeof (h266_idr) - 4);
static guint8 h266_garbage_vvc1[] = {
0x00, 0x00, 0x00, 0x00, 0x05
};
/* Send all => drop garbage end but keep correct frame. */
buf = composite_buffer (100, 0, 2, h266_idr_vvc1, sizeof (h266_idr),
h266_garbage_vvc1, sizeof (h266_garbage_vvc1));
fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK);
/* Send 3 IDR frames => all should be kept. */
buf = composite_buffer (200, 0, 3, h266_idr_vvc1, sizeof (h266_idr),
h266_idr_vvc1, sizeof (h266_idr), h266_idr_vvc1, sizeof (h266_idr));
fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK);
/* Send 2 IDR and one garbage => keep the first two and drop garabage. */
buf = composite_buffer (300, 0, 3, h266_idr_vvc1, sizeof (h266_idr),
h266_idr_vvc1, sizeof (h266_idr), h266_garbage_vvc1,
sizeof (h266_garbage_vvc1));
fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK);
/* Only send part of correct frame => drop everything */
buf = wrap_buffer (h266_idr_vvc1, sizeof (h266_idr) - 10, 300, 0);
fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK);
/* Send garbage frame => drop everything */
buf = wrap_buffer (h266_garbage_vvc1, sizeof (h266_garbage_vvc1), 400, 0);
fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK);
/* EOS for pending buffers to be drained if any */
gst_harness_push_event (h, gst_event_new_eos ());
fail_unless_equals_int (gst_harness_buffers_received (h), 3);
/* Verify IDR + garbage. */
bufout = gst_harness_pull (h);
fail_unless (bufout != NULL);
gsize vps_sps_pps_sz = sizeof (h266_vps) + sizeof (h266_sps) +
sizeof (h266_pps);
gst_buffer_map (bufout, &mapout, GST_MAP_READ);
fail_unless_equals_int (mapout.size, vps_sps_pps_sz + sizeof (h266_idr));
fail_unless (memcmp (mapout.data + vps_sps_pps_sz,
h266_idr_vvc1, sizeof (h266_idr)) == 0);
gst_buffer_unmap (bufout, &mapout);
gst_buffer_unref (bufout);
/* Verify 3 * IDR. */
bufout = gst_harness_pull (h);
fail_unless (bufout != NULL);
gst_buffer_map (bufout, &mapout, GST_MAP_READ);
fail_unless_equals_int (mapout.size, 3 * sizeof (h266_idr));
fail_unless (memcmp (mapout.data, h266_idr_vvc1, sizeof (h266_idr)) == 0);
fail_unless (memcmp (mapout.data + sizeof (h266_idr), h266_idr_vvc1,
sizeof (h266_idr)) == 0);
fail_unless (memcmp (mapout.data + 2 * sizeof (h266_idr), h266_idr_vvc1,
sizeof (h266_idr)) == 0);
gst_buffer_unmap (bufout, &mapout);
gst_buffer_unref (bufout);
/* Verify 2 * IDR + garbage. */
bufout = gst_harness_pull (h);
fail_unless (bufout != NULL);
gst_buffer_map (bufout, &mapout, GST_MAP_READ);
fail_unless_equals_int (mapout.size, 2 * sizeof (h266_idr));
fail_unless (memcmp (mapout.data, h266_idr_vvc1, sizeof (h266_idr)) == 0);
fail_unless (memcmp (mapout.data + sizeof (h266_idr), h266_idr_vvc1,
sizeof (h266_idr)) == 0);
gst_buffer_unmap (bufout, &mapout);
gst_buffer_unref (bufout);
gst_harness_teardown (h);
}
GST_END_TEST;
static Suite *
h266parse_harnessed_suite (void)
{
@ -1268,6 +1374,8 @@ h266parse_harnessed_suite (void)
tcase_add_test (tc_chain, test_drain);
tcase_add_test (tc_chain, test_packetized_vvc1_drop_corrupt);
return s;
}