summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/check/Makefile.am8
-rw-r--r--tests/check/elements/.gitignore1
-rw-r--r--tests/check/elements/rawaudioparse.c339
-rw-r--r--tests/check/elements/rawvideoparse.c459
4 files changed, 807 insertions, 0 deletions
diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am
index b16c96890..04230573f 100644
--- a/tests/check/Makefile.am
+++ b/tests/check/Makefile.am
@@ -274,6 +274,8 @@ check_PROGRAMS = \
elements/netsim \
elements/pcapparse \
elements/pnm \
+ elements/rawaudioparse \
+ elements/rawvideoparse \
elements/rtponvifparse \
elements/rtponviftimestamp \
elements/id3mux \
@@ -340,6 +342,12 @@ elements_h264parse_LDADD = libparser.la $(LDADD)
elements_pcapparse_LDADD = libparser.la $(LDADD)
+elements_rawaudioparse_LDADD = $(GST_BASE_LIBS) -lgstbase-@GST_API_VERSION@ -lgstaudio-@GST_API_VERSION@ $(LDADD)
+elements_rawaudioparse_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(AM_CFLAGS)
+
+elements_rawvideoparse_LDADD = $(GST_BASE_LIBS) -lgstbase-@GST_API_VERSION@ -lgstvideo-@GST_API_VERSION@ $(LDADD)
+elements_rawvideoparse_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(AM_CFLAGS)
+
libs_mpegvideoparser_CFLAGS = \
$(GST_PLUGINS_BAD_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) \
-DGST_USE_UNSTABLE_API \
diff --git a/tests/check/elements/.gitignore b/tests/check/elements/.gitignore
index 5910320a7..05552cce9 100644
--- a/tests/check/elements/.gitignore
+++ b/tests/check/elements/.gitignore
@@ -46,6 +46,7 @@ neonhttpsrc
netsim
ofa
pcapparse
+rawaudioparse
rtponvif
rganalysis
rglimiter
diff --git a/tests/check/elements/rawaudioparse.c b/tests/check/elements/rawaudioparse.c
new file mode 100644
index 000000000..62ab975e8
--- /dev/null
+++ b/tests/check/elements/rawaudioparse.c
@@ -0,0 +1,339 @@
+/* GStreamer
+ *
+ * unit test for rawaudioparse
+ *
+ * Copyright (C) <2016> Carlos Rafael Giani <dv at pseudoterminal dot org>
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/* FIXME: GValueArray is deprecated, but there is currently no viabla alternative
+ * See https://bugzilla.gnome.org/show_bug.cgi?id=667228 */
+#define GLIB_DISABLE_DEPRECATION_WARNINGS
+
+#include <gst/check/gstcheck.h>
+#include <gst/audio/audio.h>
+
+/* Checks are hardcoded to expect stereo 16-bit data. The sample rate
+ * however varies from the default of 40 kHz in some tests to see the
+ * differences in calculated buffer durations. */
+#define NUM_TEST_SAMPLES 512
+#define NUM_TEST_CHANNELS 2
+#define TEST_SAMPLE_RATE 40000
+#define TEST_SAMPLE_FORMAT GST_AUDIO_FORMAT_S16
+
+/* For ease of programming we use globals to keep refs for our floating
+ * src and sink pads we create; otherwise we always have to do get_pad,
+ * get_peer, and then remove references in every test function */
+static GstPad *mysrcpad, *mysinkpad;
+
+typedef struct
+{
+ GstElement *rawaudioparse;
+ GstAdapter *test_data_adapter;
+}
+RawAudParseTestCtx;
+
+/* Sets up a rawaudioparse element and a GstAdapter that contains 512 test
+ * audio samples. The samples a monotonically increasing set from the values
+ * 0 to 511 for the left and 512 to 1023 for the right channel. The result
+ * is a GstAdapter that contains the interleaved 16-bit integer values:
+ * 0,512,1,513,2,514, ... 511,1023 . This set is used in the checks to see
+ * if rawaudioparse's output buffers contain valid data. */
+static void
+setup_rawaudioparse (RawAudParseTestCtx * testctx, gboolean use_sink_caps,
+ gboolean set_properties, GstCaps * incaps, GstFormat format)
+{
+ GstElement *rawaudioparse;
+ GstAdapter *test_data_adapter;
+ GstBuffer *buffer;
+ guint i;
+ guint16 samples[NUM_TEST_SAMPLES * NUM_TEST_CHANNELS];
+
+
+ /* Setup the rawaudioparse element and the pads */
+
+ static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_FORMATS_ALL))
+ );
+ static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+ rawaudioparse = gst_check_setup_element ("rawaudioparse");
+
+ g_object_set (G_OBJECT (rawaudioparse), "use-sink-caps", use_sink_caps, NULL);
+ if (set_properties)
+ g_object_set (G_OBJECT (rawaudioparse), "sample-rate", TEST_SAMPLE_RATE,
+ "num-channels", NUM_TEST_CHANNELS, "pcm-format", TEST_SAMPLE_FORMAT,
+ NULL);
+
+ fail_unless (gst_element_set_state (rawaudioparse,
+ GST_STATE_PAUSED) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to paused");
+
+ mysrcpad = gst_check_setup_src_pad (rawaudioparse, &srctemplate);
+ mysinkpad = gst_check_setup_sink_pad (rawaudioparse, &sinktemplate);
+
+ gst_pad_set_active (mysrcpad, TRUE);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ gst_check_setup_events (mysrcpad, rawaudioparse, incaps, format);
+ if (incaps)
+ gst_caps_unref (incaps);
+
+
+ /* Fill the adapter with the interleaved 0..511 and
+ * 512..1023 samples */
+ for (i = 0; i < NUM_TEST_SAMPLES; ++i) {
+ guint c;
+ for (c = 0; c < NUM_TEST_CHANNELS; ++c)
+ samples[i * NUM_TEST_CHANNELS + c] = c * NUM_TEST_SAMPLES + i;
+ }
+
+ test_data_adapter = gst_adapter_new ();
+ buffer = gst_buffer_new_allocate (NULL, sizeof (samples), NULL);
+ gst_buffer_fill (buffer, 0, samples, sizeof (samples));
+ gst_adapter_push (test_data_adapter, buffer);
+
+
+ testctx->rawaudioparse = rawaudioparse;
+ testctx->test_data_adapter = test_data_adapter;
+}
+
+static void
+cleanup_rawaudioparse (RawAudParseTestCtx * testctx)
+{
+ gst_pad_set_active (mysrcpad, FALSE);
+ gst_pad_set_active (mysinkpad, FALSE);
+ gst_check_teardown_src_pad (testctx->rawaudioparse);
+ gst_check_teardown_sink_pad (testctx->rawaudioparse);
+ gst_check_teardown_element (testctx->rawaudioparse);
+
+ g_object_unref (G_OBJECT (testctx->test_data_adapter));
+}
+
+
+static void
+push_data_and_check_output (RawAudParseTestCtx * testctx, gsize num_in_bytes,
+ gsize expected_num_out_bytes, gint64 expected_pts, gint64 expected_dur,
+ guint expected_num_buffers_in_list, guint bpf, guint16 channel0_start,
+ guint16 channel1_start)
+{
+ GstBuffer *inbuf, *outbuf;
+ guint num_buffers;
+
+ /* Simulate upstream input by taking num_in_bytes bytes from the adapter */
+ inbuf = gst_adapter_take_buffer (testctx->test_data_adapter, num_in_bytes);
+ fail_unless (inbuf != NULL);
+
+ /* Push the input data and check that the output buffers list grew as
+ * expected */
+ fail_unless (gst_pad_push (mysrcpad, inbuf) == GST_FLOW_OK);
+ num_buffers = g_list_length (buffers);
+ fail_unless_equals_int (num_buffers, expected_num_buffers_in_list);
+
+ /* Take the latest output buffer */
+ outbuf = g_list_nth_data (buffers, num_buffers - 1);
+ fail_unless (outbuf != NULL);
+
+ /* Verify size, PTS, duration of the output buffer */
+ fail_unless_equals_uint64 (expected_num_out_bytes,
+ gst_buffer_get_size (outbuf));
+ fail_unless_equals_uint64 (expected_pts, GST_BUFFER_PTS (outbuf));
+ fail_unless_equals_uint64 (expected_dur, GST_BUFFER_DURATION (outbuf));
+
+ /* Go through all of the samples in the output buffer and check that they are
+ * valid. The samples are interleaved. The offsets specified by channel0_start
+ * and channel1_start are the expected values of the first sample for each
+ * channel in the buffer. So, if channel0_start is 512, then sample #0 in the
+ * buffer must have value 512, and if channel1_start is 700, then sample #1
+ * in the buffer must have value 700 etc. */
+ {
+ guint i, num_frames;
+ guint16 *s;
+ GstMapInfo map_info;
+ guint channel_starts[2] = { channel0_start, channel1_start };
+
+ gst_buffer_map (outbuf, &map_info, GST_MAP_READ);
+ num_frames = map_info.size / bpf;
+ s = (guint16 *) (map_info.data);
+
+ for (i = 0; i < num_frames; ++i) {
+ guint c;
+
+ for (c = 0; i < NUM_TEST_CHANNELS; ++i) {
+ guint16 expected = channel_starts[c] + i;
+ guint16 actual = s[i * NUM_TEST_CHANNELS + c];
+
+ fail_unless_equals_int (expected, actual);
+ }
+ }
+
+ gst_buffer_unmap (outbuf, &map_info);
+ }
+}
+
+
+GST_START_TEST (test_push_unaligned_data_properties_config)
+{
+ RawAudParseTestCtx testctx;
+
+ setup_rawaudioparse (&testctx, FALSE, TRUE, NULL, GST_FORMAT_BYTES);
+
+ /* Send in data buffers that are not aligned to multiples of the
+ * frame size (= sample size * num_channels). This tests if rawaudioparse
+ * aligns output data properly.
+ *
+ * The second line sends in 99 bytes, and expects 100 bytes in the
+ * output buffer. This is because the first buffer contains 45 bytes,
+ * and rawaudioparse is expected to output 44 bytes (which is an integer
+ * multiple of the frame size). The leftover 1 byte then gets prepended
+ * to the input buffer with 99 bytes, resulting in 100 bytes, which is
+ * an integer multiple of the frame size.
+ */
+
+ push_data_and_check_output (&testctx, 45, 44, GST_USECOND * 0,
+ GST_USECOND * 275, 1, 4, 0, 512);
+ push_data_and_check_output (&testctx, 99, 100, GST_USECOND * 275,
+ GST_USECOND * 625, 2, 4, 11, 523);
+ push_data_and_check_output (&testctx, 18, 16, GST_USECOND * 900,
+ GST_USECOND * 100, 3, 4, 36, 548);
+
+ cleanup_rawaudioparse (&testctx);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_push_unaligned_data_sink_caps_config)
+{
+ RawAudParseTestCtx testctx;
+ GstAudioInfo ainfo;
+ GstCaps *caps;
+
+ /* This test is essentially the same as test_push_unaligned_data_properties_config,
+ * except that rawaudioparse uses the sink caps config instead of the property config. */
+
+ gst_audio_info_set_format (&ainfo, TEST_SAMPLE_FORMAT, TEST_SAMPLE_RATE,
+ NUM_TEST_CHANNELS, NULL);
+ caps = gst_audio_info_to_caps (&ainfo);
+
+ setup_rawaudioparse (&testctx, TRUE, FALSE, caps, GST_FORMAT_BYTES);
+
+ push_data_and_check_output (&testctx, 45, 44, GST_USECOND * 0,
+ GST_USECOND * 275, 1, 4, 0, 512);
+ push_data_and_check_output (&testctx, 99, 100, GST_USECOND * 275,
+ GST_USECOND * 625, 2, 4, 11, 523);
+ push_data_and_check_output (&testctx, 18, 16, GST_USECOND * 900,
+ GST_USECOND * 100, 3, 4, 36, 548);
+
+ cleanup_rawaudioparse (&testctx);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_push_swapped_channels)
+{
+ RawAudParseTestCtx testctx;
+ GValueArray *valarray;
+ GValue val = G_VALUE_INIT;
+
+ /* Send in 40 bytes and use a nonstandard channel order (left and right channels
+ * swapped). Expected behavior is for rawaudioparse to reorder the samples inside
+ * output buffers to conform to the GStreamer channel order. For this reason,
+ * channel0 offset is 512 and channel1 offset is 0 in the check below. */
+
+ setup_rawaudioparse (&testctx, FALSE, TRUE, NULL, GST_FORMAT_BYTES);
+
+ valarray = g_value_array_new (2);
+ g_value_init (&val, GST_TYPE_AUDIO_CHANNEL_POSITION);
+ g_value_set_enum (&val, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT);
+ g_value_array_insert (valarray, 0, &val);
+ g_value_set_enum (&val, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT);
+ g_value_array_insert (valarray, 1, &val);
+ g_object_set (G_OBJECT (testctx.rawaudioparse), "channel-positions",
+ valarray, NULL);
+ g_value_array_free (valarray);
+ g_value_unset (&val);
+
+ push_data_and_check_output (&testctx, 40, 40, GST_USECOND * 0,
+ GST_USECOND * 250, 1, 4, 512, 0);
+
+ cleanup_rawaudioparse (&testctx);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_config_switch)
+{
+ RawAudParseTestCtx testctx;
+ GstAudioInfo ainfo;
+ GstCaps *caps;
+
+ /* Start processing with the properties config active, then mid-stream switch to
+ * the sink caps config. The properties config is altered to have a different
+ * sample rate than the sink caps to be able to detect the switch. The net effect
+ * is that output buffer durations are altered. For example, 40 bytes equal
+ * 10 samples, and this equals 500 us with 20 kHz or 250 us with 40 kHz. */
+
+ gst_audio_info_set_format (&ainfo, TEST_SAMPLE_FORMAT, TEST_SAMPLE_RATE,
+ NUM_TEST_CHANNELS, NULL);
+ caps = gst_audio_info_to_caps (&ainfo);
+
+ setup_rawaudioparse (&testctx, FALSE, TRUE, caps, GST_FORMAT_BYTES);
+
+ g_object_set (G_OBJECT (testctx.rawaudioparse), "sample-rate", 20000, NULL);
+
+ /* Push in data with properties config active, expecting duration calculations
+ * to be based on the 20 kHz sample rate */
+ push_data_and_check_output (&testctx, 40, 40, GST_USECOND * 0,
+ GST_USECOND * 500, 1, 4, 0, 512);
+ push_data_and_check_output (&testctx, 20, 20, GST_USECOND * 500,
+ GST_USECOND * 250, 2, 4, 10, 522);
+
+ /* Perform the switch */
+ g_object_set (G_OBJECT (testctx.rawaudioparse), "use-sink-caps", TRUE, NULL);
+
+ /* Push in data with sink caps config active, expecting duration calculations
+ * to be based on the 40 kHz sample rate */
+ push_data_and_check_output (&testctx, 40, 40, GST_USECOND * 750,
+ GST_USECOND * 250, 3, 4, 15, 527);
+
+ cleanup_rawaudioparse (&testctx);
+}
+
+GST_END_TEST;
+
+
+static Suite *
+rawaudioparse_suite (void)
+{
+ Suite *s = suite_create ("rawaudioparse");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_push_unaligned_data_properties_config);
+ tcase_add_test (tc_chain, test_push_unaligned_data_sink_caps_config);
+ tcase_add_test (tc_chain, test_push_swapped_channels);
+ tcase_add_test (tc_chain, test_config_switch);
+
+ return s;
+}
+
+GST_CHECK_MAIN (rawaudioparse);
diff --git a/tests/check/elements/rawvideoparse.c b/tests/check/elements/rawvideoparse.c
new file mode 100644
index 000000000..b071370db
--- /dev/null
+++ b/tests/check/elements/rawvideoparse.c
@@ -0,0 +1,459 @@
+/* GStreamer
+ *
+ * unit test for rawvideoparse
+ *
+ * Copyright (C) <2016> Carlos Rafael Giani <dv at pseudoterminal dot org>
+ *
+ * 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., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/* FIXME: GValueArray is deprecated, but there is currently no viabla alternative
+ * See https://bugzilla.gnome.org/show_bug.cgi?id=667228 */
+#define GLIB_DISABLE_DEPRECATION_WARNINGS
+
+#include <gst/check/gstcheck.h>
+#include <gst/video/video.h>
+
+/* The checks use as test data an 8x8 Y444 image, with 25 Hz framerate. In the
+ * sink caps configuration, the stride is 8 bytes, and the frames are tightly
+ * packed together. In the properties configuration, the stride is 10 bytes, the
+ * planes aren't tightly packed (there are 20 bytes between the planes), and the
+ * frames overall have padding between them (the overall frame size is
+ * stride (10) * height (8) * num-planes (3) + bytes-between-planes (20) * 2
+ * = 280 bytes, and the frame stride is 500 bytes, so there are 220 bytes of
+ * extra padding between frames).
+ *
+ * In the test 8x8 frame, the pixels are all set to #000000, except for two
+ * pixels: (xofs+1 yofs+0) is set to #8899AA, (xofs+0 yofs+1) is set to #112233.
+ * The first frame uses the offsets xofs=0 yofs=0. The second frame uses
+ * xofs=1 yofs=0 etc. For each configuration, there is a separate set of frames,
+ * each stored in the GstAdapter in the Context struct.
+ *
+ * During the tests, as part of the checks, the pixels are verified to have the
+ * right values. The pattern of the pixels was chosen to easily detect stride
+ * errors, incorrect plane offsets etc.
+ */
+
+#define TEST_WIDTH 8
+#define TEST_HEIGHT 8
+#define TEST_FRAMERATE_N 25
+#define TEST_FRAMERATE_D 1
+#define TEST_FRAME_FORMAT GST_VIDEO_FORMAT_Y444
+#define NUM_TEST_PLANES 3
+
+#define PROP_CTX_PLANE_STRIDE 10
+#define PROP_CTX_FRAME_STRIDE 500
+#define PROP_CTX_PLANE_PADDING 20
+#define PROP_CTX_PLANE_SIZE (PROP_CTX_PLANE_STRIDE * TEST_HEIGHT + PROP_CTX_PLANE_PADDING)
+
+GstElement *rawvideoparse;
+
+/* For ease of programming we use globals to keep refs for our floating
+ * src and sink pads we create; otherwise we always have to do get_pad,
+ * get_peer, and then remove references in every test function */
+static GstPad *mysrcpad, *mysinkpad;
+
+typedef struct
+{
+ GstAdapter *data;
+ guint plane_stride;
+ guint plane_size;
+}
+Context;
+
+static Context properties_ctx, sinkcaps_ctx;
+
+static void
+set_pixel (Context const *ctx, guint8 * pixels, guint x, guint y, guint32 color)
+{
+ guint i;
+ guint ofs = y * ctx->plane_stride + x;
+ for (i = 0; i < NUM_TEST_PLANES; ++i)
+ pixels[ctx->plane_size * i + ofs] =
+ (color >> ((NUM_TEST_PLANES - 1 - i) * 8)) & 0xFF;
+}
+
+static guint32
+get_pixel (Context const *ctx, const guint8 * pixels, guint x, guint y)
+{
+ guint i;
+ guint ofs = y * ctx->plane_stride + x;
+ guint32 color = 0;
+ for (i = 0; i < NUM_TEST_PLANES; ++i)
+ color |=
+ ((guint32) (pixels[ctx->plane_size * i + ofs])) << ((NUM_TEST_PLANES -
+ 1 - i) * 8);
+ return color;
+}
+
+static void
+fill_test_pattern (Context const *ctx, GstBuffer * buffer, guint xofs,
+ guint yofs)
+{
+ guint8 *pixels;
+ GstMapInfo map_info;
+
+ gst_buffer_map (buffer, &map_info, GST_MAP_WRITE);
+ pixels = map_info.data;
+
+ memset (pixels, 0, ctx->plane_size * NUM_TEST_PLANES);
+ set_pixel (ctx, pixels, 1 + xofs, 0 + yofs, 0x8899AA);
+ set_pixel (ctx, pixels, 0 + xofs, 1 + yofs, 0x112233);
+
+ gst_buffer_unmap (buffer, &map_info);
+}
+
+static void
+check_test_pattern (Context const *ctx, GstBuffer * buffer, guint xofs,
+ guint yofs)
+{
+ guint x, y;
+ guint8 *pixels;
+ GstMapInfo map_info;
+
+ gst_buffer_map (buffer, &map_info, GST_MAP_READ);
+ pixels = map_info.data;
+
+ fail_unless_equals_uint64_hex (get_pixel (ctx, pixels, 1 + xofs, 0 + yofs),
+ 0x8899AA);
+ fail_unless_equals_uint64_hex (get_pixel (ctx, pixels, 0 + xofs, 1 + yofs),
+ 0x112233);
+
+ for (y = 0; y < TEST_HEIGHT; ++y) {
+ for (x = 0; x < TEST_WIDTH; ++x) {
+ if ((x == (1 + xofs) && y == (0 + yofs)) || (x == (0 + xofs)
+ && y == (1 + yofs)))
+ continue;
+
+ fail_unless_equals_uint64_hex (get_pixel (ctx, pixels, x, y), 0x000000);
+ }
+ }
+
+ gst_buffer_unmap (buffer, &map_info);
+}
+
+
+static void
+setup_rawvideoparse (gboolean use_sink_caps,
+ gboolean set_properties, GstCaps * incaps, GstFormat format)
+{
+ guint i;
+
+
+ /* Setup the rawvideoparse element and the pads */
+
+ static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL))
+ );
+ static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+ rawvideoparse = gst_check_setup_element ("rawvideoparse");
+
+ properties_ctx.plane_stride = PROP_CTX_PLANE_STRIDE;
+ properties_ctx.plane_size = PROP_CTX_PLANE_SIZE;
+ properties_ctx.data = gst_adapter_new ();
+
+ sinkcaps_ctx.plane_stride = TEST_WIDTH;
+ sinkcaps_ctx.plane_size = TEST_WIDTH * TEST_HEIGHT;
+ sinkcaps_ctx.data = gst_adapter_new ();
+
+ g_object_set (G_OBJECT (rawvideoparse), "use-sink-caps", use_sink_caps, NULL);
+ if (set_properties) {
+ GValueArray *plane_offsets, *plane_strides;
+ GValue val = G_VALUE_INIT;
+
+ g_value_init (&val, G_TYPE_UINT);
+
+ plane_offsets = g_value_array_new (NUM_TEST_PLANES);
+ for (i = 0; i < NUM_TEST_PLANES; ++i) {
+ g_value_set_uint (&val, properties_ctx.plane_size * i);
+ g_value_array_insert (plane_offsets, i, &val);
+ }
+
+ plane_strides = g_value_array_new (NUM_TEST_PLANES);
+ for (i = 0; i < NUM_TEST_PLANES; ++i) {
+ g_value_set_uint (&val, properties_ctx.plane_stride);
+ g_value_array_insert (plane_strides, i, &val);
+ }
+
+ g_value_unset (&val);
+
+ g_object_set (G_OBJECT (rawvideoparse), "width", TEST_WIDTH, "height",
+ TEST_HEIGHT, "frame-stride", PROP_CTX_FRAME_STRIDE, "framerate",
+ TEST_FRAMERATE_N, TEST_FRAMERATE_D, "plane-offsets", plane_offsets,
+ "plane-strides", plane_strides, "format", TEST_FRAME_FORMAT, NULL);
+
+ g_value_array_free (plane_offsets);
+ g_value_array_free (plane_strides);
+ }
+
+ /* Check that the plane stride/offset values are correct */
+ {
+ GValueArray *plane_offsets_array;
+ GValueArray *plane_strides_array;
+ /* By default, 320x240 i420 is used as format */
+ guint plane_offsets[3] = { 0, 76800, 96000 };
+ guint plane_strides[3] = { 320, 160, 160 };
+
+ if (set_properties) {
+ /* When properties are explicitely set, we use Y444 as video format,
+ * so in that case, plane stride values are all the same */
+ plane_offsets[0] = properties_ctx.plane_size * 0;
+ plane_offsets[1] = properties_ctx.plane_size * 1;
+ plane_offsets[2] = properties_ctx.plane_size * 2;
+ plane_strides[0] = plane_strides[1] = plane_strides[2] =
+ properties_ctx.plane_stride;
+ }
+
+ g_object_get (G_OBJECT (rawvideoparse), "plane-offsets",
+ &plane_offsets_array, "plane-strides", &plane_strides_array, NULL);
+ fail_unless (plane_offsets_array != NULL);
+ fail_unless (plane_strides_array != NULL);
+ fail_unless (plane_offsets_array->n_values ==
+ plane_strides_array->n_values);
+
+ for (i = 0; i < plane_offsets_array->n_values; ++i) {
+ GValue *gvalue;
+
+ gvalue = g_value_array_get_nth (plane_offsets_array, i);
+ fail_unless (gvalue != NULL);
+ fail_unless_equals_uint64 (plane_offsets[i], g_value_get_uint (gvalue));
+
+ gvalue = g_value_array_get_nth (plane_strides_array, i);
+ fail_unless (gvalue != NULL);
+ fail_unless_equals_uint64 (plane_strides[i], g_value_get_uint (gvalue));
+ }
+
+ g_value_array_free (plane_offsets_array);
+ g_value_array_free (plane_strides_array);
+ }
+
+ fail_unless (gst_element_set_state (rawvideoparse,
+ GST_STATE_PAUSED) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to paused");
+
+ mysrcpad = gst_check_setup_src_pad (rawvideoparse, &srctemplate);
+ mysinkpad = gst_check_setup_sink_pad (rawvideoparse, &sinktemplate);
+
+ gst_pad_set_active (mysrcpad, TRUE);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ gst_check_setup_events (mysrcpad, rawvideoparse, incaps, format);
+ if (incaps)
+ gst_caps_unref (incaps);
+
+
+ /* Fill the adapters with test frames */
+
+ for (i = 0; i < 10; ++i) {
+ GstBuffer *buffer =
+ gst_buffer_new_allocate (NULL, PROP_CTX_FRAME_STRIDE, NULL);
+ gst_buffer_memset (buffer, 0, 0xCC, gst_buffer_get_size (buffer));
+ fill_test_pattern (&properties_ctx, buffer, i, 0);
+ gst_adapter_push (properties_ctx.data, buffer);
+ }
+
+ for (i = 0; i < 10; ++i) {
+ GstBuffer *buffer =
+ gst_buffer_new_allocate (NULL, sinkcaps_ctx.plane_size * 3, NULL);
+ gst_buffer_memset (buffer, 0, 0xCC, gst_buffer_get_size (buffer));
+ fill_test_pattern (&sinkcaps_ctx, buffer, i, 0);
+ gst_adapter_push (sinkcaps_ctx.data, buffer);
+ }
+}
+
+static void
+cleanup_rawvideoparse (void)
+{
+ gst_pad_set_active (mysrcpad, FALSE);
+ gst_pad_set_active (mysinkpad, FALSE);
+ gst_check_teardown_src_pad (rawvideoparse);
+ gst_check_teardown_sink_pad (rawvideoparse);
+ gst_check_teardown_element (rawvideoparse);
+
+ g_object_unref (G_OBJECT (properties_ctx.data));
+ g_object_unref (G_OBJECT (sinkcaps_ctx.data));
+}
+
+static void
+push_data_and_check_output (Context * ctx, gsize num_in_bytes,
+ gsize expected_num_out_bytes, gint64 expected_pts, gint64 expected_dur,
+ guint expected_num_buffers_in_list, guint buf_idx, guint xofs, guint yofs)
+{
+ GstBuffer *inbuf, *outbuf;
+ guint num_buffers;
+
+ /* Simulate upstream input by taking num_in_bytes bytes from the adapter */
+ inbuf = gst_adapter_take_buffer (ctx->data, num_in_bytes);
+ fail_unless (inbuf != NULL);
+
+ /* Push the input data and check that the output buffers list grew as
+ * expected */
+ fail_unless (gst_pad_push (mysrcpad, inbuf) == GST_FLOW_OK);
+ num_buffers = g_list_length (buffers);
+ fail_unless_equals_int (num_buffers, expected_num_buffers_in_list);
+
+ /* Take the output buffer */
+ outbuf = g_list_nth_data (buffers, buf_idx);
+ fail_unless (outbuf != NULL);
+
+ /* Verify size, PTS, duration of the output buffer */
+ fail_unless_equals_uint64 (expected_num_out_bytes,
+ gst_buffer_get_size (outbuf));
+ fail_unless_equals_uint64 (expected_pts, GST_BUFFER_PTS (outbuf));
+ fail_unless_equals_uint64 (expected_dur, GST_BUFFER_DURATION (outbuf));
+
+ /* Check that the pixels have the correct values */
+ check_test_pattern (ctx, outbuf, xofs, yofs);
+}
+
+
+GST_START_TEST (test_push_unaligned_data_properties_config)
+{
+ setup_rawvideoparse (FALSE, TRUE, NULL, GST_FORMAT_BYTES);
+
+ /* Send in data buffers that are not aligned to multiples of the
+ * frame size (= sample size * num_channels). This tests if rawvideoparse
+ * aligns output data properly.
+ *
+ * The second line sends a buffer with multiple frames inside.
+ * rawvideoparse will then parse this buffer repeatedly (and prepend
+ * leftover data from the earlier parse iteration), explaining why
+ * all of a sudden there are 4 output buffers, compared to just one
+ * earlier. The output data is expected to be 280 bytes large, since this
+ * is the size of the actual frame, without extra padding at the end.
+ */
+ push_data_and_check_output (&properties_ctx, 511, 280, GST_MSECOND * 0,
+ GST_MSECOND * 40, 1, 0, 0, 0);
+ push_data_and_check_output (&properties_ctx, 1940, 280, GST_MSECOND * 40,
+ GST_MSECOND * 40, 4, 1, 1, 0);
+ push_data_and_check_output (&properties_ctx, 10, 280, GST_MSECOND * 80,
+ GST_MSECOND * 40, 4, 2, 2, 0);
+
+ cleanup_rawvideoparse ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_push_unaligned_data_sink_caps_config)
+{
+ GstVideoInfo vinfo;
+ GstCaps *caps;
+
+ /* This test is essentially the same as test_push_unaligned_data_properties_config,
+ * except that rawvideoparse uses the sink caps config instead of the property config.
+ * Also, the input sizes are different, since the sink caps config does not use extra
+ * padding between planes and does use a stride that directly corresponds to the width,
+ * resulting in smaller frame size (192 bytes vs 280 bytes). */
+
+ gst_video_info_set_format (&vinfo, TEST_FRAME_FORMAT, TEST_WIDTH,
+ TEST_HEIGHT);
+ GST_VIDEO_INFO_FPS_N (&vinfo) = 25;
+ GST_VIDEO_INFO_FPS_D (&vinfo) = 1;
+ caps = gst_video_info_to_caps (&vinfo);
+
+ setup_rawvideoparse (TRUE, FALSE, caps, GST_FORMAT_BYTES);
+
+ push_data_and_check_output (&sinkcaps_ctx, 250, 192, GST_MSECOND * 0,
+ GST_MSECOND * 40, 1, 0, 0, 0);
+ push_data_and_check_output (&sinkcaps_ctx, 811, 192, GST_MSECOND * 40,
+ GST_MSECOND * 40, 5, 1, 1, 0);
+ push_data_and_check_output (&sinkcaps_ctx, 10, 192, GST_MSECOND * 80,
+ GST_MSECOND * 40, 5, 2, 2, 0);
+
+ cleanup_rawvideoparse ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_config_switch)
+{
+ GstVideoInfo vinfo;
+ GstCaps *caps;
+
+ /* Start processing with the properties config active, then mid-stream switch to
+ * the sink caps config. Since the sink caps config does not use padding, its
+ * frame size is smaller. The buffer duration stays the same (since it only depends
+ * on the framerate), but the expected output buffer size is different). */
+
+ gst_video_info_set_format (&vinfo, TEST_FRAME_FORMAT, TEST_WIDTH,
+ TEST_HEIGHT);
+ GST_VIDEO_INFO_FPS_N (&vinfo) = 25;
+ GST_VIDEO_INFO_FPS_D (&vinfo) = 1;
+ caps = gst_video_info_to_caps (&vinfo);
+
+ setup_rawvideoparse (FALSE, TRUE, caps, GST_FORMAT_BYTES);
+
+ /* Push in data with properties config active */
+ push_data_and_check_output (&properties_ctx, 500, 280, GST_MSECOND * 0,
+ GST_MSECOND * 40, 1, 0, 0, 0);
+ push_data_and_check_output (&properties_ctx, 500, 280, GST_MSECOND * 40,
+ GST_MSECOND * 40, 2, 1, 1, 0);
+
+ /* Perform the switch */
+ g_object_set (G_OBJECT (rawvideoparse), "use-sink-caps", TRUE, NULL);
+
+ /* Push in data with sink caps config active, expecting a different frame size */
+ push_data_and_check_output (&sinkcaps_ctx, 192, 192, GST_MSECOND * 80,
+ GST_MSECOND * 40, 3, 2, 0, 0);
+
+ cleanup_rawvideoparse ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_push_with_no_framerate)
+{
+ /* Test the special case when no framerate is set. The parser is expected to
+ * still work then, but without setting duration or PTS/DTS (it cannot do that,
+ * because these require a nonzero framerate). The output buffers have PTS 0,
+ * all subsequent ones have no set PTS. */
+
+ setup_rawvideoparse (FALSE, TRUE, NULL, GST_FORMAT_BYTES);
+ g_object_set (G_OBJECT (rawvideoparse), "framerate", 0, 1, NULL);
+
+ push_data_and_check_output (&properties_ctx, 500, 280, 0, GST_CLOCK_TIME_NONE,
+ 1, 0, 0, 0);
+ push_data_and_check_output (&properties_ctx, 500, 280, GST_CLOCK_TIME_NONE,
+ GST_CLOCK_TIME_NONE, 2, 1, 1, 0);
+
+ cleanup_rawvideoparse ();
+}
+
+GST_END_TEST;
+
+
+static Suite *
+rawvideoparse_suite (void)
+{
+ Suite *s = suite_create ("rawvideoparse");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_push_unaligned_data_properties_config);
+ tcase_add_test (tc_chain, test_push_unaligned_data_sink_caps_config);
+ tcase_add_test (tc_chain, test_config_switch);
+ tcase_add_test (tc_chain, test_push_with_no_framerate);
+
+ return s;
+}
+
+GST_CHECK_MAIN (rawvideoparse);