From 7751dbb8e1d548ac6dfdf0f583daeb45594c9e3e Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Wed, 25 May 2011 20:52:09 +0200 Subject: [PATCH] tests: add unit tests for a number of video parsers --- tests/check/Makefile.am | 19 ++ tests/check/elements/h263parse.c | 175 +++++++++++ tests/check/elements/h264parse.c | 393 +++++++++++++++++++++++++ tests/check/elements/mpeg4videoparse.c | 193 ++++++++++++ tests/check/elements/mpegvideoparse.c | 268 +++++++++++++++++ 5 files changed, 1048 insertions(+) create mode 100644 tests/check/elements/h263parse.c create mode 100644 tests/check/elements/h264parse.c create mode 100644 tests/check/elements/mpeg4videoparse.c create mode 100644 tests/check/elements/mpegvideoparse.c diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index 26a52c4922..20a8e2075f 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -169,6 +169,10 @@ check_PROGRAMS = \ $(check_jifmux) \ elements/jpegparse \ $(check_logoinsert) \ + elements/h263parse \ + elements/h264parse \ + elements/mpegvideoparse \ + elements/mpeg4videoparse \ elements/mxfdemux \ elements/mxfmux \ elements/id3mux \ @@ -190,6 +194,21 @@ AM_CFLAGS = $(GST_CHECK_CFLAGS) $(GST_OPTION_CFLAGS) \ -UG_DISABLE_ASSERT -UG_DISABLE_CAST_CHECKS LDADD = $(GST_CHECK_LIBS) +# parser unit test convenience lib +noinst_LTLIBRARIES = libparser.la +libparser_la_SOURCES = elements/parser.c elements/parser.h +libparser_la_CFLAGS = \ + -I$(top_srcdir)/tests/check \ + $(GST_CHECK_CFLAGS) $(GST_OPTION_CFLAGS) + +elements_mpegvideoparse_LDADD = libparser.la $(LDADD) + +elements_mpeg4videoparse_LDADD = libparser.la $(LDADD) + +elements_h263parse_LDADD = libparser.la $(LDADD) + +elements_h264parse_LDADD = libparser.la $(LDADD) + elements_voaacenc_CFLAGS = \ $(GST_PLUGINS_BASE_CFLAGS) \ $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(AM_CFLAGS) diff --git a/tests/check/elements/h263parse.c b/tests/check/elements/h263parse.c new file mode 100644 index 0000000000..4c23c619d0 --- /dev/null +++ b/tests/check/elements/h263parse.c @@ -0,0 +1,175 @@ +/* + * GStreamer + * + * unit test for h263parse + * + * Copyright (C) 2011 Nokia Corporation. All rights reserved. + * + * Contact: Stefan Kost + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "parser.h" + +#define SRC_CAPS_TMPL "video/x-h263, framed=(boolean)false" +#define SINK_CAPS_TMPL "video/x-h263, framed=(boolean)true" + +GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (SINK_CAPS_TMPL) + ); + +GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (SRC_CAPS_TMPL) + ); + +/* some data */ + +#if 0 +static guint8 h263_iframe[] = { + /* keyframes all around */ + 0x00, 0x00, 0x80, 0x02, 0x1c, 0x88, 0x01, 0x00, + 0x11, 0xe0, 0x44, 0xc4, 0x04, 0x04, 0x04, 0x3f, + 0xff, 0xe6, 0x20, 0x20, 0x20, 0x21, 0xff, 0xff, + 0x31, 0x01, 0x01, 0x01, 0x0f, 0xff, 0xf9, 0x88, + 0x08, 0x08, 0x08, 0x7f, 0xff, 0x80 +}; +#endif + +static guint8 h263_iframe[] = { + /* keyframes all around */ + /* actually, this is a truncated keyframe, + * but don't tell anyone or try this at home */ + 0x00, 0x00, 0x80, 0x02, 0x0c, 0x04, 0x26, 0x20, + 0x20, 0x20, 0x21, 0xff, 0xff, 0x31, 0x01, 0x01, + 0x01, 0x0f, 0xff, 0xf9, 0x88, 0x08, 0x08, 0x08, + 0x7f, 0xff, 0xcc, 0x40, 0x40, 0x40, 0x43, 0xff, + 0xfe, 0x62, 0x02, 0x02, 0x02, 0x1f, 0xff, 0xf3, + 0x10, 0x10, 0x10, 0x10, 0xff, 0xff, 0x98, 0x80, + 0x80, 0x80, 0x87, 0xff, 0xfc, 0xc4, 0x04, 0x04, + 0x04, 0x3f, 0xff, 0xe6, 0x20, 0x20, 0x20, 0x21, + 0xff, 0xff, 0x31, 0x01, 0x01, 0x01, 0x0f, 0xff, + 0xf9, 0x88, 0x08, 0x08, 0x08, 0x7f, 0xff, 0xcc, + 0x40, 0x40, 0x40, 0x43, 0xff, 0xfe, 0x62, 0x02, + 0x02, 0x02, 0x1f, 0xff, 0xf3, 0x10, 0x10, 0x10, + 0x10, 0xff, 0xff, 0x98, 0x80, 0x80, 0x80, 0x87, + 0xff, 0xfc, 0xc4, 0x04, 0x04, 0x04, 0x3f, 0xff, + 0xe6, 0x20, 0x20, 0x20, 0x21, 0xff, 0xff, 0x31, + 0x01, 0x01, 0x01, 0x0f, 0xff, 0xf9, 0x88, 0x08 +}; + +GST_START_TEST (test_parse_normal) +{ + gst_parser_test_normal (h263_iframe, sizeof (h263_iframe)); +} + +GST_END_TEST; + + +GST_START_TEST (test_parse_drain_single) +{ + gst_parser_test_drain_single (h263_iframe, sizeof (h263_iframe)); +} + +GST_END_TEST; + + +GST_START_TEST (test_parse_split) +{ + gst_parser_test_split (h263_iframe, sizeof (h263_iframe)); +} + +GST_END_TEST; + + +#define structure_get_int(s,f) \ + (g_value_get_int(gst_structure_get_value(s,f))) +#define fail_unless_structure_field_int_equals(s,field,num) \ + fail_unless_equals_int (structure_get_int(s,field), num) + +GST_START_TEST (test_parse_detect_stream) +{ + GstCaps *caps; + GstStructure *s; + + caps = gst_parser_test_get_output_caps (h263_iframe, sizeof (h263_iframe), + NULL); + fail_unless (caps != NULL); + + /* Check that the negotiated caps are as expected */ + /* When codec_data is present, parser assumes that data is version 4 */ + GST_LOG ("mpegvideo output caps: %" GST_PTR_FORMAT, caps); + s = gst_caps_get_structure (caps, 0); + fail_unless (gst_structure_has_name (s, "video/x-h263")); + fail_unless_structure_field_int_equals (s, "width", 352); + fail_unless_structure_field_int_equals (s, "height", 288); + + gst_caps_unref (caps); +} + +GST_END_TEST; + + +static Suite * +h263parse_suite (void) +{ + Suite *s = suite_create ("h263parse"); + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_parse_normal); + tcase_add_test (tc_chain, test_parse_drain_single); + tcase_add_test (tc_chain, test_parse_split); + tcase_add_test (tc_chain, test_parse_detect_stream); + + return s; +} + + +/* + * TODO: + * - Both push- and pull-modes need to be tested + * * Pull-mode & EOS + */ + +int +main (int argc, char **argv) +{ + int nf; + + Suite *s = h263parse_suite (); + SRunner *sr = srunner_create (s); + + gst_check_init (&argc, &argv); + + /* init test context */ + ctx_factory = "h263parse"; + ctx_sink_template = &sinktemplate; + ctx_src_template = &srctemplate; + /* no timing info to parse */ + ctx_no_metadata = TRUE; + + srunner_run_all (sr, CK_NORMAL); + nf = srunner_ntests_failed (sr); + srunner_free (sr); + + return nf; +} diff --git a/tests/check/elements/h264parse.c b/tests/check/elements/h264parse.c new file mode 100644 index 0000000000..b5423e5bd8 --- /dev/null +++ b/tests/check/elements/h264parse.c @@ -0,0 +1,393 @@ +/* + * GStreamer + * + * unit test for h264parse + * + * Copyright (C) 2011 Nokia Corporation. All rights reserved. + * + * Contact: Stefan Kost + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "parser.h" + +#define SRC_CAPS_TMPL "video/x-h264, parsed=(boolean)false" +#define SINK_CAPS_TMPL "video/x-h264, parsed=(boolean)true" + +GstStaticPadTemplate sinktemplate_bs_nal = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (SINK_CAPS_TMPL + ", stream-format = (string) byte-stream, alignment = (string) nal") + ); + +GstStaticPadTemplate sinktemplate_avc_au = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (SINK_CAPS_TMPL + ", stream-format = (string) avc, alignment = (string) au") + ); + +GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (SRC_CAPS_TMPL) + ); + +/* some data */ + +/* SPS */ +static guint8 h264_sps[] = { + 0x00, 0x00, 0x00, 0x01, 0x67, 0x4d, 0x40, 0x15, + 0xec, 0xa4, 0xbf, 0x2e, 0x02, 0x20, 0x00, 0x00, + 0x03, 0x00, 0x2e, 0xe6, 0xb2, 0x80, 0x01, 0xe2, + 0xc5, 0xb2, 0xc0 +}; + +/* PPS */ +static guint8 h264_pps[] = { + 0x00, 0x00, 0x00, 0x01, 0x68, 0xeb, 0xec, 0xb2 +}; + +/* combines to this codec-data */ +static guint8 h264_codec_data[] = { + 0x01, 0x4d, 0x40, 0x15, 0xff, 0xe1, 0x00, 0x17, + 0x67, 0x4d, 0x40, 0x15, 0xec, 0xa4, 0xbf, 0x2e, + 0x02, 0x20, 0x00, 0x00, 0x03, 0x00, 0x2e, 0xe6, + 0xb2, 0x80, 0x01, 0xe2, 0xc5, 0xb2, 0xc0, 0x01, + 0x00, 0x04, 0x68, 0xeb, 0xec, 0xb2 +}; + +/* keyframes all around */ +static guint8 h264_idrframe[] = { + 0x00, 0x00, 0x00, 0x01, 0x65, 0x88, 0x84, 0x00, + 0x10, 0xff, 0xfe, 0xf6, 0xf0, 0xfe, 0x05, 0x36, + 0x56, 0x04, 0x50, 0x96, 0x7b, 0x3f, 0x53, 0xe1 +}; + +/* truncated nal */ +static guint8 garbage_frame[] = { + 0x00, 0x00, 0x00, 0x01, 0x05 +}; + +/* context to tweak tests */ +static const gchar *ctx_suite; +static gboolean ctx_codec_data; + +static gboolean +verify_buffer (buffer_verify_data_s * vdata, GstBuffer * buffer) +{ + if (vdata->discard) { + /* check separate header NALs */ + gint i = vdata->buffer_counter; + + fail_unless (i <= 1); + fail_unless (GST_BUFFER_SIZE (buffer) == ctx_headers[i].size); + fail_unless (memcmp (GST_BUFFER_DATA (buffer), ctx_headers[i].data, + GST_BUFFER_SIZE (buffer)) == 0); + } else { + fail_unless (GST_BUFFER_SIZE (buffer) > 4); + /* only need to check avc output case */ + if (GST_READ_UINT32_BE (GST_BUFFER_DATA (buffer)) == 0x01) + return FALSE; + /* header is merged in initial frame */ + if (vdata->buffer_counter == 0) { + guint8 *data = GST_BUFFER_DATA (buffer); + + fail_unless (GST_BUFFER_SIZE (buffer) == vdata->data_to_verify_size + + ctx_headers[0].size + ctx_headers[1].size); + fail_unless (GST_READ_UINT32_BE (data) == ctx_headers[0].size - 4); + fail_unless (memcmp (data + 4, ctx_headers[0].data + 4, + ctx_headers[0].size - 4) == 0); + data += ctx_headers[0].size; + fail_unless (GST_READ_UINT32_BE (data) == ctx_headers[1].size - 4); + fail_unless (memcmp (data + 4, ctx_headers[1].data + 4, + ctx_headers[1].size - 4) == 0); + data += ctx_headers[1].size; + fail_unless (GST_READ_UINT32_BE (data) == vdata->data_to_verify_size - 4); + fail_unless (memcmp (data + 4, vdata->data_to_verify + 4, + vdata->data_to_verify_size - 4) == 0); + } else { + fail_unless (GST_READ_UINT32_BE (GST_BUFFER_DATA (buffer)) == + GST_BUFFER_SIZE (buffer) - 4); + fail_unless (GST_BUFFER_SIZE (buffer) == vdata->data_to_verify_size); + fail_unless (memcmp (GST_BUFFER_DATA (buffer) + 4, + vdata->data_to_verify + 4, GST_BUFFER_SIZE (buffer) - 4) == 0); + } + return TRUE; + } + + return FALSE; +} + +GST_START_TEST (test_parse_normal) +{ + gst_parser_test_normal (h264_idrframe, sizeof (h264_idrframe)); +} + +GST_END_TEST; + + +GST_START_TEST (test_parse_drain_single) +{ + gst_parser_test_drain_single (h264_idrframe, sizeof (h264_idrframe)); +} + +GST_END_TEST; + + +GST_START_TEST (test_parse_drain_garbage) +{ + gst_parser_test_drain_garbage (h264_idrframe, sizeof (h264_idrframe), + garbage_frame, sizeof (garbage_frame)); +} + +GST_END_TEST +GST_START_TEST (test_parse_split) +{ + gst_parser_test_split (h264_idrframe, sizeof (h264_idrframe)); +} + +GST_END_TEST; + + +GST_START_TEST (test_parse_skip_garbage) +{ + gst_parser_test_skip_garbage (h264_idrframe, sizeof (h264_idrframe), + garbage_frame, sizeof (garbage_frame)); +} + +GST_END_TEST; + +#define structure_get_int(s,f) \ + (g_value_get_int(gst_structure_get_value(s,f))) +#define fail_unless_structure_field_int_equals(s,field,num) \ + fail_unless_equals_int (structure_get_int(s,field), num) + + +GST_START_TEST (test_parse_detect_stream) +{ + GstCaps *caps; + GstStructure *s; + GstBuffer *buf; + const GValue *val; + + /* parser does not really care that mpeg1 and mpeg2 frame data + * should be a bit different */ + caps = gst_parser_test_get_output_caps (h264_idrframe, sizeof (h264_idrframe), + NULL); + fail_unless (caps != NULL); + + /* Check that the negotiated caps are as expected */ + /* When codec_data is present, parser assumes that data is version 4 */ + GST_LOG ("h264 output caps: %" GST_PTR_FORMAT, caps); + s = gst_caps_get_structure (caps, 0); + fail_unless (gst_structure_has_name (s, "video/x-h264")); + fail_unless_structure_field_int_equals (s, "width", 32); + fail_unless_structure_field_int_equals (s, "height", 24); + + if (ctx_codec_data) { + fail_unless (gst_structure_has_field (s, "codec_data")); + + /* check codec-data in more detail */ + val = gst_structure_get_value (s, "codec_data"); + fail_unless (val != NULL); + buf = gst_value_get_buffer (val); + fail_unless (buf != NULL); + fail_unless (GST_BUFFER_SIZE (buf) == sizeof (h264_codec_data)); + fail_unless (memcmp (GST_BUFFER_DATA (buf), h264_codec_data, + GST_BUFFER_SIZE (buf)) == 0); + } + + gst_caps_unref (caps); +} + +GST_END_TEST; + + +static Suite * +h264parse_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_normal); + tcase_add_test (tc_chain, test_parse_drain_single); + tcase_add_test (tc_chain, test_parse_drain_garbage); + tcase_add_test (tc_chain, test_parse_split); + tcase_add_test (tc_chain, test_parse_skip_garbage); + tcase_add_test (tc_chain, test_parse_detect_stream); + + return s; +} + +static gboolean +verify_buffer_packetized (buffer_verify_data_s * vdata, GstBuffer * buffer) +{ + fail_unless (GST_BUFFER_SIZE (buffer) > 4); + fail_unless (GST_READ_UINT32_BE (GST_BUFFER_DATA (buffer)) == 0x01); + if (vdata->discard) { + /* check separate header NALs */ + guint8 *data; + gint size; + + if (vdata->buffer_counter == 0) { + data = h264_sps; + size = sizeof (h264_sps); + } else { + data = h264_pps; + size = sizeof (h264_pps); + } + + fail_unless (GST_BUFFER_SIZE (buffer) == size); + fail_unless (memcmp (GST_BUFFER_DATA (buffer) + 4, data + 4, + size - 4) == 0); + } else { + fail_unless (GST_BUFFER_SIZE (buffer) == vdata->data_to_verify_size); + fail_unless (memcmp (GST_BUFFER_DATA (buffer) + 4, + vdata->data_to_verify + 4, GST_BUFFER_SIZE (buffer) - 4) == 0); + } + + return TRUE; +} + +GST_START_TEST (test_parse_packetized) +{ + guint8 *frame; + GstCaps *caps; + GstBuffer *cdata; + GstStructure *s; + gchar *desc; + + /* make AVC frame */ + frame = g_malloc (sizeof (h264_idrframe)); + GST_WRITE_UINT32_BE (frame, sizeof (h264_idrframe) - 4); + memcpy (frame + 4, h264_idrframe + 4, sizeof (h264_idrframe) - 4); + + /* some caps messing */ + caps = gst_caps_from_string (SRC_CAPS_TMPL); + cdata = gst_buffer_new (); + GST_BUFFER_DATA (cdata) = h264_codec_data; + GST_BUFFER_SIZE (cdata) = sizeof (h264_codec_data); + gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER, cdata, NULL); + gst_buffer_unref (cdata); + desc = gst_caps_to_string (caps); + gst_caps_unref (caps); + + caps = gst_parser_test_get_output_caps (frame, sizeof (h264_idrframe), desc); + g_free (desc); + g_free (frame); + + /* minor caps checks */ + GST_LOG ("h264 output caps: %" GST_PTR_FORMAT, caps); + s = gst_caps_get_structure (caps, 0); + fail_unless (gst_structure_has_name (s, "video/x-h264")); + fail_unless_structure_field_int_equals (s, "width", 32); + fail_unless_structure_field_int_equals (s, "height", 24); + + gst_caps_unref (caps); +} + +GST_END_TEST; + +static Suite * +h264parse_packetized_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_packetized); + + return s; +} + + +/* + * TODO: + * - Both push- and pull-modes need to be tested + * * Pull-mode & EOS + */ + +int +main (int argc, char **argv) +{ + int nf; + + Suite *s; + SRunner *sr; + + gst_check_init (&argc, &argv); + + /* globabl init test context */ + ctx_factory = "h264parse"; + ctx_sink_template = &sinktemplate_bs_nal; + ctx_src_template = &srctemplate; + ctx_headers[0].data = h264_sps; + ctx_headers[0].size = sizeof (h264_sps); + ctx_headers[1].data = h264_pps; + ctx_headers[1].size = sizeof (h264_pps); + ctx_verify_buffer = verify_buffer; + /* discard initial sps/pps buffers */ + ctx_discard = 2; + /* no timing info to parse */ + ctx_no_metadata = TRUE; + ctx_codec_data = FALSE; + + ctx_suite = "h264parse_to_bs_nal"; + s = h264parse_suite (); + sr = srunner_create (s); + srunner_run_all (sr, CK_NORMAL); + nf = srunner_ntests_failed (sr); + srunner_free (sr); + + /* setup and tweak to handle avc au output */ + ctx_suite = "h264parse_to_avc_au"; + ctx_sink_template = &sinktemplate_avc_au; + ctx_discard = 0; + ctx_codec_data = TRUE; + + s = h264parse_suite (); + sr = srunner_create (s); + srunner_run_all (sr, CK_NORMAL); + nf = srunner_ntests_failed (sr); + srunner_free (sr); + + /* setup and tweak to handle avc packetized input */ + ctx_suite = "h264parse_packetized"; + /* turn into separate byte stream NALs */ + ctx_sink_template = &sinktemplate_bs_nal; + /* and ignore inserted codec-data NALs */ + ctx_discard = 2; + /* no more config headers */ + ctx_headers[0].data = NULL; + ctx_headers[1].data = NULL; + ctx_headers[0].size = 0; + ctx_headers[1].size = 0; + /* and need adapter buffer check */ + ctx_verify_buffer = verify_buffer_packetized; + + s = h264parse_packetized_suite (); + sr = srunner_create (s); + srunner_run_all (sr, CK_NORMAL); + nf = srunner_ntests_failed (sr); + srunner_free (sr); + + return nf; +} diff --git a/tests/check/elements/mpeg4videoparse.c b/tests/check/elements/mpeg4videoparse.c new file mode 100644 index 0000000000..bd4de015ab --- /dev/null +++ b/tests/check/elements/mpeg4videoparse.c @@ -0,0 +1,193 @@ +/* + * GStreamer + * + * unit test for mpeg4videoparse + * + * Copyright (C) 2011 Nokia Corporation. All rights reserved. + * + * Contact: Stefan Kost + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "parser.h" + +#define SRC_CAPS_TMPL "video/mpeg, framed=(boolean)false" +#define SINK_CAPS_TMPL "video/mpeg, framed=(boolean)true" + +GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (SINK_CAPS_TMPL) + ); + +GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (SRC_CAPS_TMPL) + ); + +/* some data */ + +/* codec data; VOS up to and including GOP */ +static guint8 mpeg4_config[] = { + 0x00, 0x00, 0x01, 0xb0, 0x01, 0x00, 0x00, 0x01, + 0xb5, 0x89, 0x13, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x20, 0x00, 0xc4, 0x8d, 0x88, 0x00, + 0xf5, 0x01, 0x04, 0x03, 0x14, 0x63, 0x00, 0x00, + 0x01, 0xb3, 0x00, 0x10, 0x07 +}; + +/* keyframes all around */ +static guint8 mpeg4_iframe[] = { + 0x00, 0x00, 0x01, 0xb6, 0x10, 0x60, 0x91, 0x82, + 0x3d, 0xb7, 0xf1, 0xb6, 0xdf, 0xc6, 0xdb, 0x7f, + 0x1b, 0x6d, 0xfb +}; + +static gboolean +verify_buffer (buffer_verify_data_s * vdata, GstBuffer * buffer) +{ + /* header is merged in initial frame */ + if (vdata->buffer_counter == 0) { + /* the whole sequence header is included */ + fail_unless (GST_BUFFER_SIZE (buffer) == + ctx_headers[0].size + vdata->data_to_verify_size); + fail_unless (memcmp (GST_BUFFER_DATA (buffer), ctx_headers[0].data, + ctx_headers[0].size) == 0); + fail_unless (memcmp (GST_BUFFER_DATA (buffer) + ctx_headers[0].size, + vdata->data_to_verify, vdata->data_to_verify_size) == 0); + return TRUE; + } + + return FALSE; +} + +GST_START_TEST (test_parse_normal) +{ + gst_parser_test_normal (mpeg4_iframe, sizeof (mpeg4_iframe)); +} + +GST_END_TEST; + + +GST_START_TEST (test_parse_drain_single) +{ + gst_parser_test_drain_single (mpeg4_iframe, sizeof (mpeg4_iframe)); +} + +GST_END_TEST; + + +GST_START_TEST (test_parse_split) +{ + gst_parser_test_split (mpeg4_iframe, sizeof (mpeg4_iframe)); +} + +GST_END_TEST; + + +#define structure_get_int(s,f) \ + (g_value_get_int(gst_structure_get_value(s,f))) +#define fail_unless_structure_field_int_equals(s,field,num) \ + fail_unless_equals_int (structure_get_int(s,field), num) + +GST_START_TEST (test_parse_detect_stream) +{ + GstCaps *caps; + GstStructure *s; + GstBuffer *buf; + const GValue *val; + + caps = gst_parser_test_get_output_caps (mpeg4_iframe, sizeof (mpeg4_iframe), + NULL); + fail_unless (caps != NULL); + + /* Check that the negotiated caps are as expected */ + /* When codec_data is present, parser assumes that data is version 4 */ + GST_LOG ("mpeg4video output caps: %" GST_PTR_FORMAT, caps); + s = gst_caps_get_structure (caps, 0); + fail_unless (gst_structure_has_name (s, "video/mpeg")); + fail_unless_structure_field_int_equals (s, "mpegversion", 4); + fail_unless_structure_field_int_equals (s, "width", 32); + fail_unless_structure_field_int_equals (s, "height", 24); + fail_unless (gst_structure_has_field (s, "codec_data")); + + /* check codec-data in more detail */ + val = gst_structure_get_value (s, "codec_data"); + fail_unless (val != NULL); + buf = gst_value_get_buffer (val); + fail_unless (buf != NULL); + /* codec-data == config header - GOP */ + fail_unless (GST_BUFFER_SIZE (buf) == sizeof (mpeg4_config) - 7); + fail_unless (memcmp (GST_BUFFER_DATA (buf), mpeg4_config, + GST_BUFFER_SIZE (buf)) == 0); + + gst_caps_unref (caps); +} + +GST_END_TEST; + + +static Suite * +mpeg4videoparse_suite (void) +{ + Suite *s = suite_create ("mpeg4videoparse"); + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_parse_normal); + tcase_add_test (tc_chain, test_parse_drain_single); + tcase_add_test (tc_chain, test_parse_split); + tcase_add_test (tc_chain, test_parse_detect_stream); + + return s; +} + + +/* + * TODO: + * - Both push- and pull-modes need to be tested + * * Pull-mode & EOS + */ + +int +main (int argc, char **argv) +{ + int nf; + + Suite *s = mpeg4videoparse_suite (); + SRunner *sr = srunner_create (s); + + gst_check_init (&argc, &argv); + + /* init test context */ + ctx_factory = "mpeg4videoparse"; + ctx_sink_template = &sinktemplate; + ctx_src_template = &srctemplate; + ctx_headers[0].data = mpeg4_config; + ctx_headers[0].size = sizeof (mpeg4_config); + ctx_verify_buffer = verify_buffer; + /* no timing info to parse */ + ctx_no_metadata = TRUE; + + srunner_run_all (sr, CK_NORMAL); + nf = srunner_ntests_failed (sr); + srunner_free (sr); + + return nf; +} diff --git a/tests/check/elements/mpegvideoparse.c b/tests/check/elements/mpegvideoparse.c new file mode 100644 index 0000000000..9dac4633d2 --- /dev/null +++ b/tests/check/elements/mpegvideoparse.c @@ -0,0 +1,268 @@ +/* + * GStreamer + * + * unit test for mpegvideoparse + * + * Copyright (C) 2011 Nokia Corporation. All rights reserved. + * + * Contact: Stefan Kost + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "parser.h" + +#define SRC_CAPS_TMPL "video/mpeg, framed=(boolean)false" +#define SINK_CAPS_TMPL "video/mpeg, framed=(boolean)true" + +GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (SINK_CAPS_TMPL) + ); + +GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (SRC_CAPS_TMPL) + ); + +/* some data */ + +/* actually seq + gop */ +static guint8 mpeg2_seq[] = { + 0x00, 0x00, 0x01, 0xb3, 0x02, 0x00, 0x18, 0x15, + 0xff, 0xff, 0xe0, 0x28, 0x00, 0x00, 0x01, 0xb5, + 0x14, 0x8a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xb8, 0x00, 0x08, 0x00, 0x00 +}; + +/* actually seq + gop */ +static guint8 mpeg1_seq[] = { + 0x00, 0x00, 0x01, 0xb3, 0x02, 0x00, 0x18, 0x15, + 0xff, 0xff, 0xe0, 0x28, 0x00, 0x00, 0x01, 0xb8, + 0x00, 0x08, 00, 00 +}; + +/* keyframes all around */ +static guint8 mpeg2_iframe[] = { + 0x00, 0x00, 0x01, 0x00, 0x00, 0x0f, 0xff, 0xf8, + 0x00, 0x00, 0x01, 0xb5, 0x8f, 0xff, 0xf3, 0x41, + 0x80, 0x00, 0x00, 0x01, 0x01, 0x23, 0xf8, 0x7d, + 0x29, 0x48, 0x8b, 0x94, 0xa5, 0x22, 0x20, 0x00, + 0x00, 0x01, 0x02, 0x23, 0xf8, 0x7d, 0x29, 0x48, + 0x8b, 0x94, 0xa5, 0x22, 0x20 +}; + +static gboolean +verify_buffer (buffer_verify_data_s * vdata, GstBuffer * buffer) +{ + /* check initial header special case, otherwise delegate to default */ + if (vdata->discard) { + /* header is separate */ + fail_unless (GST_BUFFER_SIZE (buffer) == ctx_headers[0].size - 8); + fail_unless (memcmp (GST_BUFFER_DATA (buffer), ctx_headers[0].data, + GST_BUFFER_SIZE (buffer)) == 0); + } else { + /* header is merged in initial frame */ + if (vdata->buffer_counter == 0) { + fail_unless (GST_BUFFER_SIZE (buffer) > 4); + if (GST_READ_UINT32_BE (GST_BUFFER_DATA (buffer)) == 0x1b3) { + /* the whole sequence header is included */ + fail_unless (GST_BUFFER_SIZE (buffer) == + ctx_headers[0].size + vdata->data_to_verify_size); + fail_unless (memcmp (GST_BUFFER_DATA (buffer), ctx_headers[0].data, + ctx_headers[0].size) == 0); + fail_unless (memcmp (GST_BUFFER_DATA (buffer) + ctx_headers[0].size, + vdata->data_to_verify, vdata->data_to_verify_size) == 0); + } else { + /* sequence was separate, only gop here */ + fail_unless (GST_BUFFER_SIZE (buffer) == + 8 + vdata->data_to_verify_size); + fail_unless (memcmp (GST_BUFFER_DATA (buffer), + ctx_headers[0].data + ctx_headers[0].size - 8, 8) == 0); + fail_unless (memcmp (GST_BUFFER_DATA (buffer) + 8, + vdata->data_to_verify, vdata->data_to_verify_size) == 0); + } + return TRUE; + } + } + + return FALSE; +} + +#define GOP_SPLIT "gop-split" + +static GstElement * +setup_element (const gchar * desc) +{ + GstElement *element; + + if (strcmp (desc, GOP_SPLIT) == 0) { + element = gst_check_setup_element ("mpegvideoparse"); + g_object_set (G_OBJECT (element), "gop-split", TRUE, NULL); + } else { + element = gst_check_setup_element ("mpegvideoparse"); + } + + return element; +} + +GST_START_TEST (test_parse_normal) +{ + gst_parser_test_normal (mpeg2_iframe, sizeof (mpeg2_iframe)); +} + +GST_END_TEST; + + +GST_START_TEST (test_parse_drain_single) +{ + gst_parser_test_drain_single (mpeg2_iframe, sizeof (mpeg2_iframe)); +} + +GST_END_TEST; + + +GST_START_TEST (test_parse_split) +{ + gst_parser_test_split (mpeg2_iframe, sizeof (mpeg2_iframe)); +} + +GST_END_TEST; + + +#define structure_get_int(s,f) \ + (g_value_get_int(gst_structure_get_value(s,f))) +#define fail_unless_structure_field_int_equals(s,field,num) \ + fail_unless_equals_int (structure_get_int(s,field), num) + +static void +mpeg_video_parse_check_caps (guint version, guint8 * seq, gint size) +{ + GstCaps *caps; + GstStructure *s; + GstBuffer *buf; + const GValue *val; + + ctx_headers[0].data = seq; + ctx_headers[0].size = size; + /* parser does not really care that mpeg1 and mpeg2 frame data + * should be a bit different */ + caps = gst_parser_test_get_output_caps (mpeg2_iframe, sizeof (mpeg2_iframe), + NULL); + fail_unless (caps != NULL); + + /* Check that the negotiated caps are as expected */ + /* When codec_data is present, parser assumes that data is version 4 */ + GST_LOG ("mpegvideo output caps: %" GST_PTR_FORMAT, caps); + s = gst_caps_get_structure (caps, 0); + fail_unless (gst_structure_has_name (s, "video/mpeg")); + fail_unless_structure_field_int_equals (s, "mpegversion", version); + fail_unless_structure_field_int_equals (s, "width", 32); + fail_unless_structure_field_int_equals (s, "height", 24); + fail_unless (gst_structure_has_field (s, "codec_data")); + + /* check codec-data in more detail */ + val = gst_structure_get_value (s, "codec_data"); + fail_unless (val != NULL); + buf = gst_value_get_buffer (val); + fail_unless (buf != NULL); + /* codec-data = header - GOP */ + fail_unless (GST_BUFFER_SIZE (buf) == size - 8); + fail_unless (memcmp (GST_BUFFER_DATA (buf), seq, GST_BUFFER_SIZE (buf)) == 0); + + gst_caps_unref (caps); +} + +GST_START_TEST (test_parse_detect_stream_mpeg2) +{ + mpeg_video_parse_check_caps (2, mpeg2_seq, sizeof (mpeg2_seq)); +} + +GST_END_TEST; + + +GST_START_TEST (test_parse_detect_stream_mpeg1) +{ + mpeg_video_parse_check_caps (1, mpeg1_seq, sizeof (mpeg1_seq)); +} + +GST_END_TEST; + + +GST_START_TEST (test_parse_gop_split) +{ + ctx_factory = GOP_SPLIT; + ctx_discard = 1; + gst_parser_test_normal (mpeg2_iframe, sizeof (mpeg2_iframe)); + ctx_factory = "mpegvideoparse"; + ctx_discard = 0; +} + +GST_END_TEST; + + +static Suite * +mpegvideoparse_suite (void) +{ + Suite *s = suite_create ("mpegvideoparse"); + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_parse_normal); + tcase_add_test (tc_chain, test_parse_drain_single); + tcase_add_test (tc_chain, test_parse_split); + tcase_add_test (tc_chain, test_parse_detect_stream_mpeg1); + tcase_add_test (tc_chain, test_parse_detect_stream_mpeg2); + tcase_add_test (tc_chain, test_parse_gop_split); + + return s; +} + + +/* + * TODO: + * - Both push- and pull-modes need to be tested + * * Pull-mode & EOS + */ + +int +main (int argc, char **argv) +{ + int nf; + + Suite *s = mpegvideoparse_suite (); + SRunner *sr = srunner_create (s); + + gst_check_init (&argc, &argv); + + /* init test context */ + ctx_factory = "mpegvideoparse"; + ctx_sink_template = &sinktemplate; + ctx_src_template = &srctemplate; + ctx_headers[0].data = mpeg2_seq; + ctx_headers[0].size = sizeof (mpeg2_seq); + ctx_verify_buffer = verify_buffer; + ctx_setup = setup_element; + + srunner_run_all (sr, CK_NORMAL); + nf = srunner_ntests_failed (sr); + srunner_free (sr); + + return nf; +}