From 04a4eae13b7be538fcc28af524d922ab477358cd Mon Sep 17 00:00:00 2001 From: George Kiagiadakis Date: Thu, 28 Mar 2019 17:23:30 +0200 Subject: tests: h264parse: unit tests for sliced data processing --- tests/check/elements/h264parse.c | 309 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 308 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/check/elements/h264parse.c b/tests/check/elements/h264parse.c index ac41d9a99..2079e2f0c 100644 --- a/tests/check/elements/h264parse.c +++ b/tests/check/elements/h264parse.c @@ -379,7 +379,8 @@ GST_START_TEST (test_parse_drain_garbage) garbage_frame, sizeof (garbage_frame)); } -GST_END_TEST +GST_END_TEST; + GST_START_TEST (test_parse_split) { gst_parser_test_split (h264_idrframe, sizeof (h264_idrframe)); @@ -801,6 +802,308 @@ h264parse_packetized_suite (void) return s; } +/* These were generated using pipeline: + * gst-launch-1.0 videotestsrc num-buffers=1 pattern=green \ + * ! video/x-raw,width=128,height=128 \ + * ! openh264enc num-slices=2 \ + * ! fakesink dump=1 + */ + +/* SPS */ +static guint8 h264_slicing_sps[] = { + 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0xc0, 0x0b, + 0x8c, 0x8d, 0x41, 0x02, 0x24, 0x03, 0xc2, 0x21, + 0x1a, 0x80 +}; + +/* PPS */ +static guint8 h264_slicing_pps[] = { + 0x00, 0x00, 0x00, 0x01, 0x68, 0xce, 0x3c, 0x80 +}; + +/* IDR Slice 1 */ +static guint8 h264_idr_slice_1[] = { + 0x00, 0x00, 0x00, 0x01, 0x65, 0xb8, 0x00, 0x04, + 0x00, 0x00, 0x11, 0xff, 0xff, 0xf8, 0x22, 0x8a, + 0x1f, 0x1c, 0x00, 0x04, 0x0a, 0x63, 0x80, 0x00, + 0x81, 0xec, 0x9a, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0xad, 0x57, 0x5d, 0x75, 0xd7, 0x5d, 0x75, + 0xd7, 0x5d, 0x75, 0xd7, 0x5d, 0x75, 0xd7, 0x5d, + 0x75, 0xd7, 0x5d, 0x78 +}; + +/* IDR Slice 2 */ +static guint8 h264_idr_slice_2[] = { + 0x00, 0x00, 0x00, 0x01, 0x65, 0x04, 0x2e, 0x00, + 0x01, 0x00, 0x00, 0x04, 0x7f, 0xff, 0xfe, 0x08, + 0xa2, 0x87, 0xc7, 0x00, 0x01, 0x02, 0x98, 0xe0, + 0x00, 0x20, 0x7b, 0x26, 0xa4, 0xe4, 0xe4, 0xe4, + 0xe4, 0xe4, 0xeb, 0x55, 0xd7, 0x5d, 0x75, 0xd7, + 0x5d, 0x75, 0xd7, 0x5d, 0x75, 0xd7, 0x5d, 0x75, + 0xd7, 0x5d, 0x75, 0xd7, 0x5e +}; + +static inline GstBuffer * +wrap_buffer (guint8 * buf, gsize size, GstClockTime pts, GstBufferFlags flags) +{ + GstBuffer *buffer; + + buffer = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, + buf, size, 0, size, NULL, NULL); + GST_BUFFER_PTS (buffer) = pts; + GST_BUFFER_FLAGS (buffer) |= flags; + + return buffer; +} + +static inline GstBuffer * +composite_buffer (GstClockTime pts, GstBufferFlags flags, gint count, ...) +{ + va_list vl; + gint i; + guint8 *data; + gsize size; + GstBuffer *buffer; + + va_start (vl, count); + + buffer = gst_buffer_new (); + for (i = 0; i < count; i++) { + data = va_arg (vl, guint8 *); + size = va_arg (vl, gsize); + + buffer = gst_buffer_append (buffer, wrap_buffer (data, size, 0, 0)); + } + GST_BUFFER_PTS (buffer) = pts; + GST_BUFFER_FLAGS (buffer) |= flags; + + va_end (vl); + + return buffer; +} + +#define pull_and_check_full(h, data, size, pts, flags) \ +{ \ + GstBuffer *b = gst_harness_pull (h); \ + gst_check_buffer_data (b, data, size); \ + fail_unless_equals_clocktime (GST_BUFFER_PTS (b), pts); \ + if (flags) \ + fail_unless (GST_BUFFER_FLAG_IS_SET (b, flags)); \ + gst_buffer_unref (b); \ +} + +#define pull_and_check(h, data, pts, flags) \ + pull_and_check_full (h, data, sizeof (data), pts, flags) + +/* used to check NALs for which the parser removes the first 0x00 byte; + * this parser behavior is a bit broken, so we may remove that in the future */ +#define pull_and_check_skip1byte(h, data, pts, flags) \ + pull_and_check_full (h, data + 1, sizeof (data) - 1, pts, flags) + +#define pull_and_drop(h) \ + G_STMT_START { \ + GstBuffer *b = gst_harness_pull (h); \ + gst_buffer_unref (b); \ + } G_STMT_END; + +GST_START_TEST (test_parse_sliced_nal_nal) +{ + GstHarness *h = gst_harness_new ("h264parse"); + GstBuffer *buf; + + gst_harness_set_caps_str (h, + "video/x-h264,stream-format=byte-stream,alignment=nal,parsed=false,framerate=30/1", + "video/x-h264,stream-format=byte-stream,alignment=nal,parsed=true"); + + buf = wrap_buffer (h264_slicing_sps, sizeof (h264_slicing_sps), 10, 0); + fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK); + + buf = wrap_buffer (h264_slicing_pps, sizeof (h264_slicing_pps), 10, 0); + fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK); + + /* parser must have inserted AUD before the headers, with the same PTS */ + pull_and_check (h, h264_aud, 10, 0); + + /* drop the header buffers */ + while ((buf = gst_harness_try_pull (h))) + gst_buffer_unref (buf); + + /* reported latency must be zero */ + fail_unless_equals_clocktime (gst_harness_query_latency (h), 0); + + /* test some flow with 2 slices. + * 1st slice gets the input PTS, second gets NONE */ + buf = wrap_buffer (h264_idr_slice_1, sizeof (h264_idr_slice_1), 100, 0); + fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK); + fail_unless_equals_int (gst_harness_buffers_in_queue (h), 1); + pull_and_check_skip1byte (h, h264_idr_slice_1, 100, 0); + + buf = wrap_buffer (h264_idr_slice_2, sizeof (h264_idr_slice_2), 100, 0); + fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK); + fail_unless_equals_int (gst_harness_buffers_in_queue (h), 1); + pull_and_check_skip1byte (h, h264_idr_slice_2, -1, 0); + + buf = wrap_buffer (h264_idr_slice_1, sizeof (h264_idr_slice_1), 200, 0); + fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK); + fail_unless_equals_int (gst_harness_buffers_in_queue (h), 2); + pull_and_check (h, h264_aud, 200, 0); + pull_and_check_skip1byte (h, h264_idr_slice_1, 200, 0); + + buf = wrap_buffer (h264_idr_slice_2, sizeof (h264_idr_slice_2), 200, 0); + fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK); + fail_unless_equals_int (gst_harness_buffers_in_queue (h), 1); + pull_and_check_skip1byte (h, h264_idr_slice_2, -1, 0); + + buf = wrap_buffer (h264_idr_slice_1, sizeof (h264_idr_slice_1), 250, 0); + fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK); + fail_unless_equals_int (gst_harness_buffers_in_queue (h), 2); + pull_and_check (h, h264_aud, 250, 0); + pull_and_check_skip1byte (h, h264_idr_slice_1, 250, 0); + + /* 1st slice starts a new AU, even though the previous one is incomplete. + * DISCONT must also be propagated */ + buf = wrap_buffer (h264_idr_slice_1, sizeof (h264_idr_slice_1), 400, + GST_BUFFER_FLAG_DISCONT); + fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK); + fail_unless_equals_int (gst_harness_buffers_in_queue (h), 2); + pull_and_check (h, h264_aud, 400, 0); + pull_and_check_skip1byte (h, h264_idr_slice_1, 400, GST_BUFFER_FLAG_DISCONT); + + gst_harness_teardown (h); +} + +GST_END_TEST; + +GST_START_TEST (test_parse_sliced_au_nal) +{ + GstHarness *h = gst_harness_new ("h264parse"); + GstBuffer *buf; + + gst_harness_set_caps_str (h, + "video/x-h264,stream-format=byte-stream,alignment=au,parsed=false,framerate=30/1", + "video/x-h264,stream-format=byte-stream,alignment=nal,parsed=true"); + + /* push the whole AU in a single buffer */ + buf = composite_buffer (100, 0, 4, + h264_slicing_sps, sizeof (h264_slicing_sps), + h264_slicing_pps, sizeof (h264_slicing_pps), + h264_idr_slice_1, sizeof (h264_idr_slice_1), + h264_idr_slice_2, sizeof (h264_idr_slice_2)); + fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK); + + /* parser must have inserted AUD before the headers, with the same PTS */ + pull_and_check (h, h264_aud, 100, 0); + + /* drop the headers */ + fail_unless (gst_harness_buffers_in_queue (h) > 2); + while (gst_harness_buffers_in_queue (h) > 2) + pull_and_drop (h); + + /* reported latency must be zero */ + fail_unless_equals_clocktime (gst_harness_query_latency (h), 0); + + /* 1st slice here doens't have a PTS + * because it was present in the first header NAL */ + pull_and_check_skip1byte (h, h264_idr_slice_1, -1, 0); + pull_and_check_skip1byte (h, h264_idr_slice_2, -1, 0); + + /* new AU. we expect AUD to be inserted and 1st slice to have the same PTS */ + buf = composite_buffer (200, 0, 2, + h264_idr_slice_1, sizeof (h264_idr_slice_1), + h264_idr_slice_2, sizeof (h264_idr_slice_2)); + fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK); + fail_unless_equals_int (gst_harness_buffers_in_queue (h), 3); + pull_and_check (h, h264_aud, 200, 0); + pull_and_check_skip1byte (h, h264_idr_slice_1, 200, 0); + pull_and_check_skip1byte (h, h264_idr_slice_2, -1, 0); + + /* DISCONT must be propagated */ + buf = composite_buffer (400, GST_BUFFER_FLAG_DISCONT, 2, + h264_idr_slice_1, sizeof (h264_idr_slice_1), + h264_idr_slice_2, sizeof (h264_idr_slice_2)); + fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK); + fail_unless_equals_int (gst_harness_buffers_in_queue (h), 3); + pull_and_check (h, h264_aud, 400, 0); + pull_and_check_skip1byte (h, h264_idr_slice_1, 400, GST_BUFFER_FLAG_DISCONT); + pull_and_check_skip1byte (h, h264_idr_slice_2, -1, 0); + + gst_harness_teardown (h); +} + +GST_END_TEST; + +GST_START_TEST (test_parse_sliced_nal_au) +{ + GstHarness *h = gst_harness_new ("h264parse"); + GstBuffer *buf; + + gst_harness_set_caps_str (h, + "video/x-h264,stream-format=byte-stream,alignment=nal,parsed=false,framerate=30/1", + "video/x-h264,stream-format=byte-stream,alignment=au,parsed=true"); + + buf = wrap_buffer (h264_slicing_sps, sizeof (h264_slicing_sps), 100, 0); + fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK); + + buf = wrap_buffer (h264_slicing_pps, sizeof (h264_slicing_pps), 100, 0); + fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK); + + buf = wrap_buffer (h264_idr_slice_1, sizeof (h264_idr_slice_1), 100, 0); + fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK); + + buf = wrap_buffer (h264_idr_slice_2, sizeof (h264_idr_slice_2), 100, 0); + fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK); + + /* no output yet, it will be pushed as soon as + * the parser recognizes the new AU */ + fail_unless_equals_int (gst_harness_buffers_in_queue (h), 0); + + buf = wrap_buffer (h264_idr_slice_1, sizeof (h264_idr_slice_1), 200, 0); + fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK); + + fail_unless_equals_int (gst_harness_buffers_in_queue (h), 1); + + { + GstMapInfo info; + + buf = composite_buffer (100, 0, 5, + h264_aud, sizeof (h264_aud), + h264_slicing_sps, sizeof (h264_slicing_sps), + h264_slicing_pps, sizeof (h264_slicing_pps), + h264_idr_slice_1, sizeof (h264_idr_slice_1), + h264_idr_slice_2, sizeof (h264_idr_slice_2)); + gst_buffer_map (buf, &info, GST_MAP_READ); + + pull_and_check_full (h, info.data, info.size, 100, 0); + + gst_buffer_unmap (buf, &info); + gst_buffer_unref (buf); + } + + /* reported latency must be 1 frame (@ 30fps because of sink pad caps) */ + fail_unless_equals_clocktime (gst_harness_query_latency (h), + gst_util_uint64_scale (GST_SECOND, 1, 30)); + + gst_harness_teardown (h); +} + +GST_END_TEST; + + +static Suite * +h264parse_sliced_suite (void) +{ + Suite *s = suite_create (ctx_suite); + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_parse_sliced_nal_nal); + tcase_add_test (tc_chain, test_parse_sliced_au_nal); + tcase_add_test (tc_chain, test_parse_sliced_nal_au); + + return s; +} + + GST_START_TEST (test_parse_sei_closedcaptions) { GstVideoCaptionMeta *cc; @@ -950,6 +1253,10 @@ main (int argc, char **argv) s = h264parse_packetized_suite (); nf += gst_check_run_suite (s, ctx_suite, __FILE__ "_packetized.c"); + ctx_suite = "h264parse_sliced"; + s = h264parse_sliced_suite (); + nf += gst_check_run_suite (s, ctx_suite, __FILE__ "_sliced.c"); + { TCase *tc_chain = tcase_create ("general"); -- cgit v1.2.1