mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-24 17:20:36 +00:00
tests: h264parse: unit tests for sliced data processing
This commit is contained in:
parent
95134cfda8
commit
04a4eae13b
1 changed files with 308 additions and 1 deletions
|
@ -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");
|
||||
|
||||
|
|
Loading…
Reference in a new issue