summaryrefslogtreecommitdiff
path: root/tests/check/elements
diff options
context:
space:
mode:
authorjk7744.park <jk7744.park@samsung.com>2015-10-24 16:49:08 +0900
committerjk7744.park <jk7744.park@samsung.com>2015-10-24 16:49:08 +0900
commit32e864f0f32b7628d995e84d07646b01dc5bf2d5 (patch)
tree27f97eaa1ca7e171815e4f88b348448b05f02748 /tests/check/elements
parent9093d777f57b3f1dc62010bb8e0c7ed3d72a76d2 (diff)
downloadgst-plugins-good-accepted/tizen_2.4_mobile.tar.gz
gst-plugins-good-accepted/tizen_2.4_mobile.tar.bz2
gst-plugins-good-accepted/tizen_2.4_mobile.zip
Diffstat (limited to 'tests/check/elements')
-rwxr-xr-xtests/check/elements/aacparse.c291
-rw-r--r--tests/check/elements/ac3parse.c163
-rw-r--r--tests/check/elements/alphacolor.c288
-rw-r--r--tests/check/elements/amrparse.c327
-rw-r--r--tests/check/elements/apev2mux.c426
-rw-r--r--tests/check/elements/aspectratiocrop.c190
-rw-r--r--tests/check/elements/audioamplify.c482
-rw-r--r--tests/check/elements/audiochebband.c1685
-rw-r--r--tests/check/elements/audiocheblimit.c1101
-rw-r--r--tests/check/elements/audiodynamic.c457
-rw-r--r--tests/check/elements/audioecho.c240
-rw-r--r--tests/check/elements/audiofirfilter.c192
-rw-r--r--tests/check/elements/audioiirfilter.c189
-rw-r--r--tests/check/elements/audioinvert.c299
-rw-r--r--tests/check/elements/audiopanorama.c818
-rw-r--r--tests/check/elements/audiowsincband.c1152
-rw-r--r--tests/check/elements/audiowsinclimit.c803
-rwxr-xr-xtests/check/elements/autodetect.c229
-rw-r--r--tests/check/elements/avimux.c284
-rw-r--r--tests/check/elements/avisubtitle.c263
-rw-r--r--tests/check/elements/capssetter.c231
-rw-r--r--tests/check/elements/deinterlace.c463
-rw-r--r--tests/check/elements/deinterleave.c637
-rwxr-xr-xtests/check/elements/dtmf.c588
-rw-r--r--tests/check/elements/equalizer.c395
-rw-r--r--tests/check/elements/flacparse.c300
-rw-r--r--tests/check/elements/flvdemux.c182
-rw-r--r--tests/check/elements/flvmux.c175
-rw-r--r--tests/check/elements/gdkpixbufsink.c293
-rw-r--r--tests/check/elements/icydemux.c300
-rw-r--r--tests/check/elements/id3demux.c283
-rw-r--r--tests/check/elements/id3v2mux.c536
-rw-r--r--tests/check/elements/imagefreeze.c586
-rwxr-xr-xtests/check/elements/interleave.c824
-rw-r--r--tests/check/elements/jpegdec.c156
-rw-r--r--tests/check/elements/jpegenc.c254
-rw-r--r--tests/check/elements/level.c604
-rw-r--r--tests/check/elements/matroskamux.c495
-rwxr-xr-xtests/check/elements/matroskaparse.c130
-rw-r--r--tests/check/elements/mpegaudioparse.c170
-rw-r--r--tests/check/elements/mulawdec.c125
-rw-r--r--tests/check/elements/mulawenc.c175
-rw-r--r--tests/check/elements/multifile.c325
-rw-r--r--tests/check/elements/parser.c435
-rw-r--r--tests/check/elements/parser.h95
-rwxr-xr-xtests/check/elements/qtmux.c932
-rw-r--r--tests/check/elements/rganalysis.c1994
-rw-r--r--tests/check/elements/rglimiter.c292
-rw-r--r--tests/check/elements/rgvolume.c687
-rwxr-xr-xtests/check/elements/rtp-payloading.c970
-rwxr-xr-xtests/check/elements/rtpaux.c418
-rwxr-xr-xtests/check/elements/rtpbin.c724
-rw-r--r--tests/check/elements/rtpbin_buffer_list.c335
-rwxr-xr-xtests/check/elements/rtpcollision.c462
-rwxr-xr-xtests/check/elements/rtpjitterbuffer.c1596
-rwxr-xr-xtests/check/elements/rtpmux.c320
-rwxr-xr-xtests/check/elements/rtprtx.c1546
-rwxr-xr-xtests/check/elements/rtpsession.c597
-rwxr-xr-xtests/check/elements/shapewipe.c316
-rwxr-xr-xtests/check/elements/souphttpsrc.c700
-rw-r--r--tests/check/elements/spectrum.c556
-rw-r--r--tests/check/elements/sunaudio.c95
-rwxr-xr-xtests/check/elements/udpsink.c222
-rw-r--r--tests/check/elements/udpsrc.c123
-rwxr-xr-xtests/check/elements/videobox.c238
-rw-r--r--tests/check/elements/videocrop.c834
-rwxr-xr-xtests/check/elements/videofilter.c281
-rwxr-xr-xtests/check/elements/videomixer.c1063
-rw-r--r--tests/check/elements/vp8dec.c178
-rw-r--r--tests/check/elements/vp8enc.c168
-rwxr-xr-xtests/check/elements/wavpackdec.c258
-rw-r--r--tests/check/elements/wavpackenc.c197
-rw-r--r--tests/check/elements/wavpackparse.c243
-rwxr-xr-xtests/check/elements/wavparse.c87
-rw-r--r--tests/check/elements/y4menc.c183
75 files changed, 35251 insertions, 0 deletions
diff --git a/tests/check/elements/aacparse.c b/tests/check/elements/aacparse.c
new file mode 100755
index 0000000..9a50647
--- /dev/null
+++ b/tests/check/elements/aacparse.c
@@ -0,0 +1,291 @@
+/*
+ * GStreamer
+ *
+ * unit test for aacparse
+ *
+ * Copyright (C) 2008 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Stefan Kost <stefan.kost@nokia.com>
+ *
+ * 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.
+ */
+
+#include <gst/check/gstcheck.h>
+#include "parser.h"
+
+#define SRC_CAPS_CDATA "audio/mpeg, mpegversion=(int)4, framed=(boolean)false, codec_data=(buffer)1190"
+#define SRC_CAPS_TMPL "audio/mpeg, framed=(boolean)false, mpegversion=(int){2,4}"
+
+#define SINK_CAPS \
+ "audio/mpeg, framed=(boolean)true"
+#define SINK_CAPS_MPEG2 \
+ "audio/mpeg, framed=(boolean)true, mpegversion=2, rate=48000, channels=2"
+#define SINK_CAPS_MPEG4 \
+ "audio/mpeg, framed=(boolean)true, mpegversion=4, rate=96000, channels=2"
+#define SINK_CAPS_TMPL "audio/mpeg, framed=(boolean)true, mpegversion=(int){2,4}"
+
+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 */
+static guint8 adif_header[] = {
+ 'A', 'D', 'I', 'F'
+};
+
+static guint8 adts_frame_mpeg2[] = {
+ 0xff, 0xf9, 0x4c, 0x80, 0x01, 0xff, 0xfc, 0x21, 0x10, 0xd3, 0x20, 0x0c,
+ 0x32, 0x00, 0xc7
+};
+
+static guint8 adts_frame_mpeg4[] = {
+ 0xff, 0xf1, 0x4c, 0x80, 0x01, 0xff, 0xfc, 0x21, 0x10, 0xd3, 0x20, 0x0c,
+ 0x32, 0x00, 0xc7
+};
+
+static guint8 garbage_frame[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+/*
+ * Test if the parser pushes data with ADIF header properly and detects the
+ * stream to MPEG4 properly.
+ */
+GST_START_TEST (test_parse_adif_normal)
+{
+ GstParserTest ptest;
+
+ /* ADIF header */
+ gst_parser_test_init (&ptest, adif_header, sizeof (adif_header), 1);
+ /* well, no garbage, followed by random data */
+ ptest.series[2].size = 100;
+ ptest.series[2].num = 3;
+ /* and we do not really expect output frames */
+ ptest.framed = FALSE;
+ /* Check that the negotiated caps are as expected */
+ /* For ADIF parser assumes that data is always version 4 */
+ ptest.sink_caps =
+ gst_caps_from_string (SINK_CAPS_MPEG4 ", stream-format=(string)adif");
+
+ gst_parser_test_run (&ptest, NULL);
+
+ gst_caps_unref (ptest.sink_caps);
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_parse_adts_normal)
+{
+ gst_parser_test_normal (adts_frame_mpeg4, sizeof (adts_frame_mpeg4));
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_parse_adts_drain_single)
+{
+ gst_parser_test_drain_single (adts_frame_mpeg4, sizeof (adts_frame_mpeg4));
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_parse_adts_drain_garbage)
+{
+ gst_parser_test_drain_garbage (adts_frame_mpeg4, sizeof (adts_frame_mpeg4),
+ garbage_frame, sizeof (garbage_frame));
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_parse_adts_split)
+{
+ gst_parser_test_split (adts_frame_mpeg4, sizeof (adts_frame_mpeg4));
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_parse_adts_skip_garbage)
+{
+ gst_parser_test_skip_garbage (adts_frame_mpeg4, sizeof (adts_frame_mpeg4),
+ garbage_frame, sizeof (garbage_frame));
+}
+
+GST_END_TEST;
+
+
+/*
+ * Test if the src caps are set according to stream format (MPEG version).
+ */
+GST_START_TEST (test_parse_adts_detect_mpeg_version)
+{
+ gst_parser_test_output_caps (adts_frame_mpeg2, sizeof (adts_frame_mpeg2),
+ NULL,
+ SINK_CAPS_MPEG2
+ ", stream-format=(string)adts, level=(string)2, profile=(string)lc");
+}
+
+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)
+/*
+ * Test if the parser handles raw stream and codec_data info properly.
+ */
+GST_START_TEST (test_parse_handle_codec_data)
+{
+ GstCaps *caps;
+ GstStructure *s;
+ const gchar *stream_format;
+
+ /* Push random data. It should get through since the parser should be
+ * initialized because it got codec_data in the caps */
+ caps = gst_parser_test_get_output_caps (NULL, 100, SRC_CAPS_CDATA);
+ 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 ("aac output caps: %" GST_PTR_FORMAT, caps);
+ s = gst_caps_get_structure (caps, 0);
+ fail_unless (gst_structure_has_name (s, "audio/mpeg"));
+ fail_unless_structure_field_int_equals (s, "mpegversion", 4);
+ fail_unless_structure_field_int_equals (s, "channels", 2);
+ fail_unless_structure_field_int_equals (s, "rate", 48000);
+ fail_unless (gst_structure_has_field (s, "codec_data"));
+ fail_unless (gst_structure_has_field (s, "stream-format"));
+ stream_format = gst_structure_get_string (s, "stream-format");
+ fail_unless (strcmp (stream_format, "raw") == 0);
+
+ gst_caps_unref (caps);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_parse_proxy_constraints)
+{
+ GstCaps *caps;
+ GstElement *parse, *filter;
+ GstPad *sinkpad;
+ GstStructure *s;
+
+ parse = gst_element_factory_make ("aacparse", NULL);
+ filter = gst_element_factory_make ("capsfilter", NULL);
+
+ /* constraint on rate and version */
+ caps = gst_caps_from_string ("audio/mpeg,mpegversion=2,rate=44100");
+ g_object_set (filter, "caps", caps, NULL);
+ gst_caps_unref (caps);
+
+ gst_element_link (parse, filter);
+
+ sinkpad = gst_element_get_static_pad (parse, "sink");
+ caps = gst_pad_query_caps (sinkpad, NULL);
+ GST_LOG ("caps %" GST_PTR_FORMAT, caps);
+
+ fail_unless (gst_caps_get_size (caps) == 1);
+
+ /* getcaps should proxy the rate constraint */
+ s = gst_caps_get_structure (caps, 0);
+ fail_unless (gst_structure_has_name (s, "audio/mpeg"));
+ fail_unless_structure_field_int_equals (s, "rate", 44100);
+ gst_caps_unref (caps);
+
+ /* should accept without the constraint */
+ caps = gst_caps_from_string ("audio/mpeg,mpegversion=2");
+ fail_unless (gst_pad_query_accept_caps (sinkpad, caps));
+ gst_caps_unref (caps);
+
+ /* should not accept with conflicting version */
+ caps = gst_caps_from_string ("audio/mpeg,mpegversion=4");
+ fail_if (gst_pad_query_accept_caps (sinkpad, caps));
+ gst_caps_unref (caps);
+
+ gst_object_unref (sinkpad);
+
+ gst_object_unref (filter);
+ gst_object_unref (parse);
+}
+
+GST_END_TEST;
+
+static Suite *
+aacparse_suite (void)
+{
+ Suite *s = suite_create ("aacparse");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ /* ADIF tests */
+ tcase_add_test (tc_chain, test_parse_adif_normal);
+
+ /* ADTS tests */
+ tcase_add_test (tc_chain, test_parse_adts_normal);
+ tcase_add_test (tc_chain, test_parse_adts_drain_single);
+ tcase_add_test (tc_chain, test_parse_adts_drain_garbage);
+ tcase_add_test (tc_chain, test_parse_adts_split);
+ tcase_add_test (tc_chain, test_parse_adts_skip_garbage);
+ tcase_add_test (tc_chain, test_parse_adts_detect_mpeg_version);
+
+ /* Other tests */
+ tcase_add_test (tc_chain, test_parse_handle_codec_data);
+
+ /* caps tests */
+ tcase_add_test (tc_chain, test_parse_proxy_constraints);
+
+ 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 = aacparse_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ /* init test context */
+ ctx_factory = "aacparse";
+ ctx_sink_template = &sinktemplate;
+ ctx_src_template = &srctemplate;
+
+ srunner_run_all (sr, CK_NORMAL);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}
diff --git a/tests/check/elements/ac3parse.c b/tests/check/elements/ac3parse.c
new file mode 100644
index 0000000..7877cd0
--- /dev/null
+++ b/tests/check/elements/ac3parse.c
@@ -0,0 +1,163 @@
+/*
+ * GStreamer
+ *
+ * unit test for ac3parse
+ *
+ * Copyright (C) 2008 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Stefan Kost <stefan.kost@nokia.com>
+ *
+ * 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.
+ */
+
+#include <gst/check/gstcheck.h>
+#include "parser.h"
+
+#define SRC_CAPS_TMPL "audio/x-ac3, framed=(boolean)false"
+#define SINK_CAPS_TMPL "audio/x-ac3, 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 */
+
+static guint8 ac3_frame[512] = {
+ 0x0b, 0x77, 0xb6, 0xa8, 0x10, 0x40, 0x2f, 0x84,
+ 0x29, 0xcb, 0xfe, 0x75, 0x7c, 0xf9, 0xf3, 0xe7,
+ 0xcf, 0x9f, 0x3e, 0x7c, 0xf9, 0xf3, 0xe7, 0xcf,
+ 0x9f, 0x3e, 0x7c, 0xf9, 0xf3, 0xe7, 0xcf, 0x9f,
+ 0x3e, 0x7c, 0xf9, 0xf3, 0xe7, 0xcf, 0x9f, 0x3e,
+ 0x7c, 0xf9, 0xf3, 0xe7, 0xcf, 0x9f, 0x3e, 0x7c,
+ 0xf9, 0xf3, 0xe7, 0xcf, 0x9f, 0x3e, 0x7c, 0xf9,
+ 0xf3, 0xe7, 0xcf, 0x9f, 0x3e, 0x7c, 0xf9, 0xf3,
+ 0xe7, 0xcf, 0x9f, 0x3e, 0x7c, 0xf9, 0xf3, 0xe7,
+ 0xcf, 0x9f, 0x3e, 0x32, 0xd3, 0xff, 0xc0, 0x06,
+ 0xe9, 0x40, 0x00, 0x6e, 0x94, 0x00, 0x06, 0xe9,
+ 0x40, 0x00, 0x6e, 0x94, 0x00, 0x06, 0xe9, 0x40,
+ 0x00, 0x6e, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static guint8 garbage_frame[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+
+GST_START_TEST (test_parse_normal)
+{
+ gst_parser_test_normal (ac3_frame, sizeof (ac3_frame));
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_parse_drain_single)
+{
+ gst_parser_test_drain_single (ac3_frame, sizeof (ac3_frame));
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_parse_drain_garbage)
+{
+ gst_parser_test_drain_garbage (ac3_frame, sizeof (ac3_frame),
+ garbage_frame, sizeof (garbage_frame));
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_parse_split)
+{
+ gst_parser_test_split (ac3_frame, sizeof (ac3_frame));
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_parse_skip_garbage)
+{
+ gst_parser_test_skip_garbage (ac3_frame, sizeof (ac3_frame),
+ garbage_frame, sizeof (garbage_frame));
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_parse_detect_stream)
+{
+ gst_parser_test_output_caps (ac3_frame, sizeof (ac3_frame),
+ NULL, SINK_CAPS_TMPL ",channels=1,rate=48000,alignment=frame");
+}
+
+GST_END_TEST;
+
+
+static Suite *
+ac3parse_suite (void)
+{
+ Suite *s = suite_create ("ac3parse");
+ 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;
+}
+
+
+/*
+ * TODO:
+ * - Both push- and pull-modes need to be tested
+ * * Pull-mode & EOS
+ */
+
+int
+main (int argc, char **argv)
+{
+ int nf;
+
+ Suite *s = ac3parse_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ /* init test context */
+ ctx_factory = "ac3parse";
+ ctx_sink_template = &sinktemplate;
+ ctx_src_template = &srctemplate;
+
+ srunner_run_all (sr, CK_NORMAL);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}
diff --git a/tests/check/elements/alphacolor.c b/tests/check/elements/alphacolor.c
new file mode 100644
index 0000000..15496d8
--- /dev/null
+++ b/tests/check/elements/alphacolor.c
@@ -0,0 +1,288 @@
+/* GStreamer unit test for the alphacolor element
+ *
+ * Copyright (C) 2007 Tim-Philipp Müller <tim centricular net>
+ *
+ * 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.
+ */
+
+#include <gst/check/gstcheck.h>
+#include <gst/video/video.h>
+
+/* 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 */
+GstPad *mysrcpad, *mysinkpad;
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("AYUV"))
+ );
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ RGBA, RGB }"))
+ );
+
+static GstElement *
+setup_alphacolor (void)
+{
+ GstElement *alphacolor;
+
+ alphacolor = gst_check_setup_element ("alphacolor");
+ mysrcpad = gst_check_setup_src_pad (alphacolor, &srctemplate);
+ mysinkpad = gst_check_setup_sink_pad (alphacolor, &sinktemplate);
+
+ gst_pad_set_active (mysrcpad, TRUE);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ return alphacolor;
+}
+
+static void
+cleanup_alphacolor (GstElement * alphacolor)
+{
+ GST_DEBUG ("cleaning up");
+
+ gst_pad_set_active (mysrcpad, FALSE);
+ gst_pad_set_active (mysinkpad, FALSE);
+ gst_check_teardown_src_pad (alphacolor);
+ gst_check_teardown_sink_pad (alphacolor);
+ gst_check_teardown_element (alphacolor);
+}
+
+#define WIDTH 3
+#define HEIGHT 4
+
+static GstCaps *
+create_caps_rgb24 (void)
+{
+ GstCaps *caps;
+
+ caps = gst_caps_new_simple ("video/x-raw",
+ "width", G_TYPE_INT, 3,
+ "height", G_TYPE_INT, 4,
+ "framerate", GST_TYPE_FRACTION, 0, 1,
+ "format", G_TYPE_STRING, "RGB", NULL);
+
+ return caps;
+}
+
+static GstCaps *
+create_caps_rgba32 (void)
+{
+ GstCaps *caps;
+
+ caps = gst_caps_new_simple ("video/x-raw",
+ "width", G_TYPE_INT, 3,
+ "height", G_TYPE_INT, 4,
+ "framerate", GST_TYPE_FRACTION, 0, 1,
+ "format", G_TYPE_STRING, "RGBA", NULL);
+
+ return caps;
+}
+
+static GstBuffer *
+create_buffer_rgb24_3x4 (void)
+{
+ /* stride is rounded up to multiple of 4, so 3 bytes padding for each row */
+ const guint8 rgb24_3x4_img[HEIGHT * GST_ROUND_UP_4 (WIDTH * 3)] = {
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00
+ };
+ guint rowstride = GST_ROUND_UP_4 (WIDTH * 3);
+ GstBuffer *buf;
+ GstMapInfo info;
+
+ buf = gst_buffer_new_and_alloc (HEIGHT * rowstride);
+ gst_buffer_map (buf, &info, GST_MAP_READWRITE);
+ fail_unless_equals_int (info.size, sizeof (rgb24_3x4_img));
+ memcpy (info.data, rgb24_3x4_img, sizeof (rgb24_3x4_img));
+
+ gst_buffer_unmap (buf, &info);
+
+ return buf;
+}
+
+static GstBuffer *
+create_buffer_rgba32_3x4 (void)
+{
+ /* stride is rounded up to multiple of 4, so 3 bytes padding for each row */
+ /* should be: RED BLUE WHITE where 'nothing' is fully transparent
+ * GREEN RED BLUE and all other colours are fully
+ * NOTHING GREEN RED opaque.
+ * BLACK NOTHING GREEN
+ */
+ const guint8 rgba32_3x4_img[HEIGHT * WIDTH * 4] = {
+ 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff
+ };
+ guint rowstride = WIDTH * 4;
+ GstBuffer *buf;
+ GstMapInfo map;
+
+ buf = gst_buffer_new_and_alloc (HEIGHT * rowstride);
+ gst_buffer_map (buf, &map, GST_MAP_READWRITE);
+ fail_unless_equals_int (map.size, sizeof (rgba32_3x4_img));
+ memcpy (map.data, rgba32_3x4_img, sizeof (rgba32_3x4_img));
+
+ gst_buffer_unmap (buf, &map);
+
+ return buf;
+}
+
+GST_START_TEST (test_rgb24)
+{
+ GstElement *alphacolor;
+ GstBuffer *inbuffer;
+ GstCaps *incaps;
+
+ incaps = create_caps_rgb24 ();
+ alphacolor = setup_alphacolor ();
+
+ fail_unless_equals_int (gst_element_set_state (alphacolor, GST_STATE_PLAYING),
+ GST_STATE_CHANGE_SUCCESS);
+
+ gst_check_setup_events (mysrcpad, alphacolor, incaps, GST_FORMAT_TIME);
+
+ inbuffer = create_buffer_rgb24_3x4 ();
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away reference; this should error out with a not-negotiated
+ * error, alphacolor should only accept RGBA caps, not but plain RGB24 caps */
+ GST_DEBUG ("push it");
+ fail_unless_equals_int (gst_pad_push (mysrcpad, inbuffer),
+ GST_FLOW_NOT_NEGOTIATED);
+ GST_DEBUG ("pushed it");
+
+ fail_unless (g_list_length (buffers) == 0);
+
+ fail_unless_equals_int (gst_element_set_state (alphacolor, GST_STATE_NULL),
+ GST_STATE_CHANGE_SUCCESS);
+
+ /* cleanup */
+ GST_DEBUG ("cleanup alphacolor");
+ cleanup_alphacolor (alphacolor);
+ GST_DEBUG ("cleanup, unref incaps");
+ ASSERT_CAPS_REFCOUNT (incaps, "incaps", 1);
+ gst_caps_unref (incaps);
+}
+
+GST_END_TEST;
+
+/* these macros assume WIDTH and HEIGHT is fixed to what's defined above */
+#define fail_unless_ayuv_pixel_has_alpha(ayuv,x,y,a) \
+ { \
+ guint8 *pixel; \
+ pixel = ((guint8*)(ayuv) + ((WIDTH * 4) * (y)) + ((x) * 4)); \
+ fail_unless_equals_int (pixel[0], a); \
+ }
+
+GST_START_TEST (test_rgba32)
+{
+ GstElement *alphacolor;
+ GstBuffer *inbuffer;
+ GstBuffer *outbuffer;
+ GstCaps *incaps;
+ guint8 *ayuv;
+ guint outlength;
+ GstMapInfo map;
+
+ incaps = create_caps_rgba32 ();
+ alphacolor = setup_alphacolor ();
+
+ fail_unless_equals_int (gst_element_set_state (alphacolor, GST_STATE_PLAYING),
+ GST_STATE_CHANGE_SUCCESS);
+
+ gst_check_setup_events (mysrcpad, alphacolor, incaps, GST_FORMAT_TIME);
+
+ inbuffer = create_buffer_rgba32_3x4 ();
+ GST_DEBUG ("Created buffer of %" G_GSIZE_FORMAT " bytes",
+ gst_buffer_get_size (inbuffer));
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away reference */
+ GST_DEBUG ("push it");
+ fail_unless_equals_int (gst_pad_push (mysrcpad, inbuffer), GST_FLOW_OK);
+ GST_DEBUG ("pushed it");
+
+ /* ... and puts a new buffer on the global list */
+ fail_unless (g_list_length (buffers) == 1);
+ outbuffer = (GstBuffer *) buffers->data;
+ fail_if (outbuffer == NULL);
+ fail_unless (GST_IS_BUFFER (outbuffer));
+
+ ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
+ outlength = WIDTH * HEIGHT * 4; /* output is AYUV */
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ fail_unless_equals_int (map.size, outlength);
+
+ ayuv = map.data;
+
+ /* check alpha values (0x00 = totally transparent, 0xff = totally opaque) */
+ fail_unless_ayuv_pixel_has_alpha (ayuv, 0, 0, 0xff);
+ fail_unless_ayuv_pixel_has_alpha (ayuv, 1, 0, 0xff);
+ fail_unless_ayuv_pixel_has_alpha (ayuv, 2, 0, 0xff);
+ fail_unless_ayuv_pixel_has_alpha (ayuv, 0, 1, 0xff);
+ fail_unless_ayuv_pixel_has_alpha (ayuv, 1, 1, 0xff);
+ fail_unless_ayuv_pixel_has_alpha (ayuv, 2, 1, 0xff);
+ fail_unless_ayuv_pixel_has_alpha (ayuv, 0, 2, 0x00);
+ fail_unless_ayuv_pixel_has_alpha (ayuv, 1, 2, 0xff);
+ fail_unless_ayuv_pixel_has_alpha (ayuv, 2, 2, 0xff);
+ fail_unless_ayuv_pixel_has_alpha (ayuv, 0, 3, 0xff);
+ fail_unless_ayuv_pixel_has_alpha (ayuv, 1, 3, 0x00);
+ fail_unless_ayuv_pixel_has_alpha (ayuv, 2, 3, 0xff);
+
+ /* we don't check the YUV data, because apparently results differ slightly
+ * depending on whether we run in valgrind or not */
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ buffers = g_list_remove (buffers, outbuffer);
+ gst_buffer_unref (outbuffer);
+
+ fail_unless_equals_int (gst_element_set_state (alphacolor, GST_STATE_NULL),
+ GST_STATE_CHANGE_SUCCESS);
+
+ /* cleanup */
+ GST_DEBUG ("cleanup alphacolor");
+ cleanup_alphacolor (alphacolor);
+ GST_DEBUG ("cleanup, unref incaps");
+ ASSERT_CAPS_REFCOUNT (incaps, "incaps", 1);
+ gst_caps_unref (incaps);
+}
+
+GST_END_TEST;
+
+
+static Suite *
+alphacolor_suite (void)
+{
+ Suite *s = suite_create ("alphacolor");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_rgb24);
+ tcase_add_test (tc_chain, test_rgba32);
+
+ return s;
+}
+
+GST_CHECK_MAIN (alphacolor);
diff --git a/tests/check/elements/amrparse.c b/tests/check/elements/amrparse.c
new file mode 100644
index 0000000..ee5b455
--- /dev/null
+++ b/tests/check/elements/amrparse.c
@@ -0,0 +1,327 @@
+/*
+ * GStreamer
+ *
+ * unit test for amrparse
+ *
+ * Copyright (C) 2008 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Stefan Kost <stefan.kost@nokia.com>
+ *
+ * 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.
+ */
+
+#include <gst/check/gstcheck.h>
+#include "parser.h"
+
+#define SRC_CAPS_NB "audio/x-amr-nb-sh"
+#define SRC_CAPS_WB "audio/x-amr-wb-sh"
+#define SRC_CAPS_ANY "ANY"
+
+#define SINK_CAPS_NB "audio/AMR, rate=8000 , channels=1"
+#define SINK_CAPS_WB "audio/AMR-WB, rate=16000 , channels=1"
+#define SINK_CAPS_ANY "ANY"
+
+#define AMR_FRAME_DURATION (GST_SECOND/50)
+
+static GstStaticPadTemplate sinktemplate_nb = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (SINK_CAPS_NB)
+ );
+
+static GstStaticPadTemplate sinktemplate_wb = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (SINK_CAPS_WB)
+ );
+
+static GstStaticPadTemplate srctemplate_nb = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (SRC_CAPS_NB)
+ );
+
+static GstStaticPadTemplate srctemplate_wb = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (SRC_CAPS_WB)
+ );
+
+
+/* some data */
+
+static guint8 frame_data_nb[] = {
+ 0x0c, 0x56, 0x3c, 0x52, 0xe0, 0x61, 0xbc, 0x45,
+ 0x0f, 0x98, 0x2e, 0x01, 0x42, 0x02
+};
+
+static guint8 frame_data_wb[] = {
+ 0x08, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16
+};
+
+static guint8 frame_hdr_nb[] = {
+ '#', '!', 'A', 'M', 'R', '\n'
+};
+
+static guint8 frame_hdr_wb[] = {
+ '#', '!', 'A', 'M', 'R', '-', 'W', 'B', '\n'
+};
+
+static guint8 garbage_frame[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+
+GST_START_TEST (test_parse_nb_normal)
+{
+ gst_parser_test_normal (frame_data_nb, sizeof (frame_data_nb));
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_parse_nb_drain_single)
+{
+ gst_parser_test_drain_single (frame_data_nb, sizeof (frame_data_nb));
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_parse_nb_drain_garbage)
+{
+ gst_parser_test_drain_garbage (frame_data_nb, sizeof (frame_data_nb),
+ garbage_frame, sizeof (garbage_frame));
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_parse_nb_split)
+{
+ gst_parser_test_split (frame_data_nb, sizeof (frame_data_nb));
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_parse_nb_skip_garbage)
+{
+ gst_parser_test_skip_garbage (frame_data_nb, sizeof (frame_data_nb),
+ garbage_frame, sizeof (garbage_frame));
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_parse_nb_detect_stream)
+{
+ GstParserTest ptest;
+ GstCaps *old_ctx_caps;
+
+ /* no input caps, override ctx */
+ old_ctx_caps = ctx_input_caps;
+ ctx_input_caps = NULL;
+
+ /* AMR-NB header */
+ gst_parser_test_init (&ptest, frame_hdr_nb, sizeof (frame_hdr_nb), 1);
+ /* well, no garbage, followed by real data */
+ ptest.series[2].data = frame_data_nb;
+ ptest.series[2].size = sizeof (frame_data_nb);
+ ptest.series[2].num = 10;
+ /* header gets dropped, so ... */
+ /* buffer count will not match */
+ ptest.framed = FALSE;
+ /* total size a bit less */
+ ptest.dropped = sizeof (frame_hdr_nb);
+
+ /* Check that the negotiated caps are as expected */
+ ptest.sink_caps = gst_caps_from_string (SINK_CAPS_NB);
+
+ gst_parser_test_run (&ptest, NULL);
+
+ gst_caps_unref (ptest.sink_caps);
+
+ ctx_input_caps = old_ctx_caps;
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_parse_wb_normal)
+{
+ gst_parser_test_normal (frame_data_wb, sizeof (frame_data_wb));
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_parse_wb_drain_single)
+{
+ gst_parser_test_drain_single (frame_data_wb, sizeof (frame_data_wb));
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_parse_wb_drain_garbage)
+{
+ gst_parser_test_drain_garbage (frame_data_wb, sizeof (frame_data_wb),
+ garbage_frame, sizeof (garbage_frame));
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_parse_wb_split)
+{
+ gst_parser_test_split (frame_data_wb, sizeof (frame_data_wb));
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_parse_wb_skip_garbage)
+{
+ gst_parser_test_skip_garbage (frame_data_wb, sizeof (frame_data_wb),
+ garbage_frame, sizeof (garbage_frame));
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_parse_wb_detect_stream)
+{
+ GstParserTest ptest;
+ GstCaps *old_ctx_caps;
+
+ /* no input caps, override ctx */
+ old_ctx_caps = ctx_input_caps;
+ ctx_input_caps = NULL;
+
+ /* AMR-WB header */
+ gst_parser_test_init (&ptest, frame_hdr_wb, sizeof (frame_hdr_wb), 1);
+ /* well, no garbage, followed by real data */
+ ptest.series[2].data = frame_data_wb;
+ ptest.series[2].size = sizeof (frame_data_wb);
+ ptest.series[2].num = 10;
+ /* header gets dropped, so ... */
+ /* buffer count will not match */
+ ptest.framed = FALSE;
+ /* total size a bit less */
+ ptest.dropped = sizeof (frame_hdr_wb);
+
+ /* Check that the negotiated caps are as expected */
+ ptest.sink_caps = gst_caps_from_string (SINK_CAPS_WB);
+
+ gst_parser_test_run (&ptest, NULL);
+
+ gst_caps_unref (ptest.sink_caps);
+
+ ctx_input_caps = old_ctx_caps;
+}
+
+GST_END_TEST;
+
+
+
+/*
+ * Create test suite.
+ */
+static Suite *
+amrnb_parse_suite (void)
+{
+ Suite *s = suite_create ("amrwb_parse");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ /* AMR-NB tests */
+ tcase_add_test (tc_chain, test_parse_nb_normal);
+ tcase_add_test (tc_chain, test_parse_nb_drain_single);
+ tcase_add_test (tc_chain, test_parse_nb_drain_garbage);
+ tcase_add_test (tc_chain, test_parse_nb_split);
+ tcase_add_test (tc_chain, test_parse_nb_detect_stream);
+ tcase_add_test (tc_chain, test_parse_nb_skip_garbage);
+
+ return s;
+}
+
+static Suite *
+amrwb_parse_suite (void)
+{
+ Suite *s = suite_create ("amrnb_parse");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ /* AMR-WB tests */
+ tcase_add_test (tc_chain, test_parse_wb_normal);
+ tcase_add_test (tc_chain, test_parse_wb_drain_single);
+ tcase_add_test (tc_chain, test_parse_wb_drain_garbage);
+ tcase_add_test (tc_chain, test_parse_wb_split);
+ tcase_add_test (tc_chain, test_parse_wb_detect_stream);
+ tcase_add_test (tc_chain, test_parse_wb_skip_garbage);
+
+ return s;
+}
+
+/*
+ * TODO:
+ * - Both push- and pull-modes need to be tested
+ * * Pull-mode & EOS
+ */
+
+int
+main (int argc, char **argv)
+{
+ int nf;
+ GstCaps *caps;
+
+ Suite *s = amrnb_parse_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ /* init test context */
+ ctx_factory = "amrparse";
+ ctx_sink_template = &sinktemplate_nb;
+ ctx_src_template = &srctemplate_nb;
+ caps = gst_caps_from_string (SRC_CAPS_NB);
+ g_assert (caps);
+ ctx_input_caps = caps;
+
+ srunner_run_all (sr, CK_NORMAL);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+ gst_caps_unref (caps);
+
+ s = amrwb_parse_suite ();
+ sr = srunner_create (s);
+
+ ctx_sink_template = &sinktemplate_wb;
+ ctx_src_template = &srctemplate_wb;
+ caps = gst_caps_from_string (SRC_CAPS_WB);
+ g_assert (caps);
+ ctx_input_caps = caps;
+
+ srunner_run_all (sr, CK_NORMAL);
+ nf += srunner_ntests_failed (sr);
+ srunner_free (sr);
+ gst_caps_unref (caps);
+
+ return nf;
+}
diff --git a/tests/check/elements/apev2mux.c b/tests/check/elements/apev2mux.c
new file mode 100644
index 0000000..88f9e3e
--- /dev/null
+++ b/tests/check/elements/apev2mux.c
@@ -0,0 +1,426 @@
+/* GStreamer
+ *
+ * unit test for the taglib-based apev2mux element
+ *
+ * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
+ * Copyright (C) 2006 Sebastian Dröge <slomo@circular-chaos.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.
+ */
+
+#include <gst/check/gstcheck.h>
+
+#include <gst/gst.h>
+#include <string.h>
+
+#define TEST_ARTIST "Ar T\303\255st"
+#define TEST_TITLE "M\303\274llermilch!"
+#define TEST_ALBUM "Boom"
+#define TEST_DATE g_date_new_dmy(1,1,2006)
+#define TEST_TRACK_NUMBER 7
+#define TEST_TRACK_COUNT 19
+#define TEST_TRACK_GAIN 1.45
+#define TEST_ALBUM_GAIN 0.78
+
+/* for dummy mp3 frame sized MP3_FRAME_SIZE bytes,
+ * start: ff fb b0 44 00 00 08 00 00 4b 00 00 00 00 00 00 */
+static const guint8 mp3_dummyhdr[] = { 0xff, 0xfb, 0xb0, 0x44, 0x00, 0x00,
+ 0x08, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00
+};
+
+#define MP3_FRAME_SIZE 626
+
+static GstTagList *
+test_taglib_apev2mux_create_tags (guint32 mask)
+{
+ GstTagList *tags;
+
+ tags = gst_tag_list_new_empty ();
+
+ if (mask & (1 << 0)) {
+ gst_tag_list_add (tags, GST_TAG_MERGE_KEEP,
+ GST_TAG_ARTIST, TEST_ARTIST, NULL);
+ }
+ if (mask & (1 << 1)) {
+ gst_tag_list_add (tags, GST_TAG_MERGE_KEEP,
+ GST_TAG_TITLE, TEST_TITLE, NULL);
+ }
+ if (mask & (1 << 2)) {
+ gst_tag_list_add (tags, GST_TAG_MERGE_KEEP,
+ GST_TAG_ALBUM, TEST_ALBUM, NULL);
+ }
+ if (mask & (1 << 3)) {
+ GDate *date;
+
+ date = TEST_DATE;
+ gst_tag_list_add (tags, GST_TAG_MERGE_KEEP, GST_TAG_DATE, date, NULL);
+ g_date_free (date);
+ }
+ if (mask & (1 << 4)) {
+ gst_tag_list_add (tags, GST_TAG_MERGE_KEEP,
+ GST_TAG_TRACK_NUMBER, TEST_TRACK_NUMBER, NULL);
+ }
+ if (mask & (1 << 5)) {
+ gst_tag_list_add (tags, GST_TAG_MERGE_KEEP,
+ GST_TAG_TRACK_COUNT, TEST_TRACK_COUNT, NULL);
+ }
+ if (mask & (1 << 6)) {
+ gst_tag_list_add (tags, GST_TAG_MERGE_KEEP,
+ GST_TAG_TRACK_GAIN, TEST_TRACK_GAIN, NULL);
+ }
+ if (mask & (1 << 7)) {
+ gst_tag_list_add (tags, GST_TAG_MERGE_KEEP,
+ GST_TAG_ALBUM_GAIN, TEST_ALBUM_GAIN, NULL);
+ }
+ if (mask & (1 << 8)) {
+ }
+ if (mask & (1 << 9)) {
+ }
+ if (mask & (1 << 10)) {
+ }
+ if (mask & (1 << 11)) {
+ }
+ if (mask & (1 << 12)) {
+ }
+ if (mask & (1 << 13)) {
+ }
+ return tags;
+}
+
+static void
+test_taglib_apev2mux_check_tags (GstTagList * tags, guint32 mask)
+{
+ if (mask & (1 << 0)) {
+ gchar *s = NULL;
+
+ fail_unless (gst_tag_list_get_string (tags, GST_TAG_ARTIST, &s));
+ fail_unless (g_str_equal (s, TEST_ARTIST));
+ g_free (s);
+ }
+ if (mask & (1 << 1)) {
+ gchar *s = NULL;
+
+ fail_unless (gst_tag_list_get_string (tags, GST_TAG_TITLE, &s));
+ fail_unless (g_str_equal (s, TEST_TITLE));
+ g_free (s);
+ }
+ if (mask & (1 << 2)) {
+ gchar *s = NULL;
+
+ fail_unless (gst_tag_list_get_string (tags, GST_TAG_ALBUM, &s));
+ fail_unless (g_str_equal (s, TEST_ALBUM));
+ g_free (s);
+ }
+ if (mask & (1 << 3)) {
+ GDate *shouldbe, *date = NULL;
+
+ shouldbe = TEST_DATE;
+ fail_unless (gst_tag_list_get_date (tags, GST_TAG_DATE, &date));
+ fail_unless (g_date_compare (shouldbe, date) == 0);
+ g_date_free (shouldbe);
+ g_date_free (date);
+ }
+ if (mask & (1 << 4)) {
+ guint num;
+
+ fail_unless (gst_tag_list_get_uint (tags, GST_TAG_TRACK_NUMBER, &num));
+ fail_unless (num == TEST_TRACK_NUMBER);
+ }
+ if (mask & (1 << 5)) {
+ guint count;
+
+ fail_unless (gst_tag_list_get_uint (tags, GST_TAG_TRACK_COUNT, &count));
+ fail_unless (count == TEST_TRACK_COUNT);
+ }
+ if (mask & (1 << 6)) {
+ gdouble gain;
+
+ fail_unless (gst_tag_list_get_double (tags, GST_TAG_TRACK_GAIN, &gain));
+ fail_unless (gain == TEST_TRACK_GAIN);
+ }
+ if (mask & (1 << 7)) {
+ gdouble gain;
+
+ fail_unless (gst_tag_list_get_double (tags, GST_TAG_ALBUM_GAIN, &gain));
+ fail_unless (gain == TEST_ALBUM_GAIN);
+ }
+ if (mask & (1 << 8)) {
+ }
+ if (mask & (1 << 9)) {
+ }
+ if (mask & (1 << 10)) {
+ }
+ if (mask & (1 << 11)) {
+ }
+ if (mask & (1 << 12)) {
+ }
+ if (mask & (1 << 13)) {
+ }
+}
+
+static void
+fill_mp3_buffer (GstElement * fakesrc, GstBuffer * buf, GstPad * pad,
+ guint64 * p_offset)
+{
+ gsize size;
+
+ size = gst_buffer_get_size (buf);
+
+ fail_unless (size == MP3_FRAME_SIZE);
+
+ GST_LOG ("filling buffer with fake mp3 data, offset = %" G_GUINT64_FORMAT,
+ *p_offset);
+
+ gst_buffer_fill (buf, 0, mp3_dummyhdr, sizeof (mp3_dummyhdr));
+
+#if 0
+ /* can't use gst_buffer_set_caps() here because the metadata isn't writable
+ * because of the extra refcounts taken by the signal emission mechanism;
+ * we know it's fine to use GST_BUFFER_CAPS() here though */
+ GST_BUFFER_CAPS (buf) = gst_caps_new_simple ("audio/mpeg", "mpegversion",
+ G_TYPE_INT, 1, "layer", G_TYPE_INT, 3, NULL);
+#endif
+
+ GST_BUFFER_OFFSET (buf) = *p_offset;
+ *p_offset += size;
+}
+
+static void
+got_buffer (GstElement * fakesink, GstBuffer * buf, GstPad * pad,
+ GstBuffer ** p_buf)
+{
+ gint64 off;
+ GstMapInfo map;
+
+ off = GST_BUFFER_OFFSET (buf);
+
+ gst_buffer_map (buf, &map, GST_MAP_READ);
+
+ GST_LOG ("size=%" G_GSIZE_FORMAT ", offset=%" G_GINT64_FORMAT, map.size, off);
+
+ fail_unless (GST_BUFFER_OFFSET_IS_VALID (buf));
+
+ if (*p_buf == NULL || (off + map.size) > gst_buffer_get_size (*p_buf)) {
+ GstBuffer *newbuf;
+
+ /* not very elegant, but who cares */
+ newbuf = gst_buffer_new_and_alloc (off + map.size);
+ if (*p_buf) {
+ GstMapInfo pmap;
+
+ gst_buffer_map (*p_buf, &pmap, GST_MAP_READ);
+ gst_buffer_fill (newbuf, 0, pmap.data, pmap.size);
+ gst_buffer_unmap (*p_buf, &pmap);
+ }
+ gst_buffer_fill (newbuf, off, map.data, map.size);
+ if (*p_buf)
+ gst_buffer_unref (*p_buf);
+ *p_buf = newbuf;
+ } else {
+ gst_buffer_fill (*p_buf, off, map.data, map.size);
+ }
+ gst_buffer_unmap (buf, &map);
+}
+
+static void
+test_taglib_apev2mux_check_output_buffer (GstBuffer * buf)
+{
+ GstMapInfo map;
+ guint off;
+
+ gst_buffer_map (buf, &map, GST_MAP_READ);
+ g_assert (map.size % MP3_FRAME_SIZE == 0);
+
+ for (off = 0; off < map.size; off += MP3_FRAME_SIZE) {
+ fail_unless (memcmp (map.data + off, mp3_dummyhdr,
+ sizeof (mp3_dummyhdr)) == 0);
+ }
+ gst_buffer_unmap (buf, &map);
+}
+
+static void
+test_taglib_apev2mux_with_tags (GstTagList * tags, guint32 mask)
+{
+ GstMessage *msg;
+ GstTagList *tags_read = NULL;
+ GstElement *pipeline, *apev2mux, *apedemux, *fakesrc, *fakesink;
+ GstBus *bus;
+ guint64 offset;
+ GstBuffer *outbuf = NULL;
+
+ pipeline = gst_pipeline_new ("pipeline");
+ g_assert (pipeline != NULL);
+
+ fakesrc = gst_element_factory_make ("fakesrc", "fakesrc");
+ g_assert (fakesrc != NULL);
+
+ apev2mux = gst_element_factory_make ("apev2mux", "apev2mux");
+ g_assert (apev2mux != NULL);
+
+ apedemux = gst_element_factory_make ("apedemux", "apedemux");
+ g_assert (apedemux != NULL);
+
+ fakesink = gst_element_factory_make ("fakesink", "fakesink");
+ g_assert (fakesink != NULL);
+
+ /* set up sink */
+ outbuf = NULL;
+ g_object_set (fakesink, "signal-handoffs", TRUE, NULL);
+ g_signal_connect (fakesink, "handoff", G_CALLBACK (got_buffer), &outbuf);
+
+ gst_bin_add (GST_BIN (pipeline), fakesrc);
+ gst_bin_add (GST_BIN (pipeline), apev2mux);
+ gst_bin_add (GST_BIN (pipeline), apedemux);
+ gst_bin_add (GST_BIN (pipeline), fakesink);
+
+ gst_tag_setter_merge_tags (GST_TAG_SETTER (apev2mux), tags,
+ GST_TAG_MERGE_APPEND);
+
+ gst_element_link_many (fakesrc, apev2mux, apedemux, fakesink, NULL);
+
+ /* set up source */
+ g_object_set (fakesrc, "signal-handoffs", TRUE, "can-activate-pull", FALSE,
+ "filltype", 2, "sizetype", 2, "sizemax", MP3_FRAME_SIZE,
+ "num-buffers", 16, NULL);
+
+ offset = 0;
+ g_signal_connect (fakesrc, "handoff", G_CALLBACK (fill_mp3_buffer), &offset);
+
+ gst_element_set_state (pipeline, GST_STATE_PLAYING);
+ fail_unless (gst_element_get_state (pipeline, NULL, NULL,
+ -1) == GST_STATE_CHANGE_SUCCESS);
+
+ bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
+
+ GST_LOG ("Waiting for tag ...");
+ msg =
+ gst_bus_poll (bus, GST_MESSAGE_TAG | GST_MESSAGE_EOS | GST_MESSAGE_ERROR,
+ -1);
+ if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
+ GError *err;
+ gchar *dbg;
+
+ gst_message_parse_error (msg, &err, &dbg);
+ g_printerr ("ERROR from element %s: %s\n%s\n",
+ GST_OBJECT_NAME (msg->src), err->message, GST_STR_NULL (dbg));
+ g_error_free (err);
+ g_free (dbg);
+ } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS) {
+ g_printerr ("EOS message, but were waiting for TAGS!\n");
+ }
+ fail_unless (msg->type == GST_MESSAGE_TAG);
+
+ gst_message_parse_tag (msg, &tags_read);
+ gst_message_unref (msg);
+
+ GST_LOG ("Got tags: %" GST_PTR_FORMAT, tags_read);
+ test_taglib_apev2mux_check_tags (tags_read, mask);
+ gst_tag_list_unref (tags_read);
+
+ GST_LOG ("Waiting for EOS ...");
+ msg = gst_bus_poll (bus, GST_MESSAGE_EOS | GST_MESSAGE_ERROR, -1);
+ if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
+ GError *err;
+ gchar *dbg;
+
+ gst_message_parse_error (msg, &err, &dbg);
+ g_printerr ("ERROR from element %s: %s\n%s\n",
+ GST_OBJECT_NAME (msg->src), err->message, GST_STR_NULL (dbg));
+ g_error_free (err);
+ g_free (dbg);
+ }
+ fail_unless (msg->type == GST_MESSAGE_EOS);
+ gst_message_unref (msg);
+
+ gst_object_unref (bus);
+
+ GST_LOG ("Got EOS, shutting down ...");
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+ gst_object_unref (pipeline);
+
+ test_taglib_apev2mux_check_output_buffer (outbuf);
+ gst_buffer_unref (outbuf);
+
+ GST_LOG ("Done");
+}
+
+GST_START_TEST (test_apev2mux)
+{
+ GstTagList *tags;
+ gint i;
+
+ g_random_set_seed (247166295);
+
+ /* internal consistency check */
+ tags = test_taglib_apev2mux_create_tags (0xFFFFFFFF);
+ test_taglib_apev2mux_check_tags (tags, 0xFFFFFFFF);
+ gst_tag_list_unref (tags);
+
+ /* now the real tests */
+ for (i = 0; i < 50; ++i) {
+ guint32 mask;
+
+ mask = g_random_int ();
+ GST_LOG ("tag mask = %08x (i=%d)", mask, i);
+
+ if (mask == 0)
+ continue;
+
+ /* create tags */
+ tags = test_taglib_apev2mux_create_tags (mask);
+ GST_LOG ("tags for mask %08x = %" GST_PTR_FORMAT, mask, tags);
+
+ /* double-check for internal consistency */
+ test_taglib_apev2mux_check_tags (tags, mask);
+
+ /* test with pipeline */
+ test_taglib_apev2mux_with_tags (tags, mask);
+
+ /* free tags */
+ gst_tag_list_unref (tags);
+ }
+}
+
+GST_END_TEST;
+
+static Suite *
+apev2mux_suite (void)
+{
+ Suite *s = suite_create ("apev2mux");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_apev2mux);
+
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ int nf;
+
+ Suite *s = apev2mux_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ srunner_run_all (sr, CK_NORMAL);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}
diff --git a/tests/check/elements/aspectratiocrop.c b/tests/check/elements/aspectratiocrop.c
new file mode 100644
index 0000000..1728afd
--- /dev/null
+++ b/tests/check/elements/aspectratiocrop.c
@@ -0,0 +1,190 @@
+/* GStreamer unit test for the aspectratiocrop element
+ * Copyright (C) 2009 Thijs Vermeir <thijsvermeir@gmail.com>
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+#include <gst/check/gstcheck.h>
+
+#define ASPECT_RATIO_CROP_CAPS \
+ GST_VIDEO_CAPS_MAKE ("{ RGBx, xRGB, BGRx, xBGR, " \
+ "RGBA, ARGB, BGRA, ABGR, RGB, BGR, AYUV, " \
+ "YUY2, YVYU, UYVY, GRAY8, I420, YV12, RGB16, " \
+ "RGB15 }")
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (ASPECT_RATIO_CROP_CAPS)
+ );
+
+static void
+check_aspectratiocrop (const gchar * in_string, const gchar * out_string,
+ gint in_size, gint out_size, gint ar_n, gint ar_d)
+{
+ GstElement *element;
+ GstPad *pad_peer;
+ GstPad *sink_pad = NULL;
+ GstPad *src_pad;
+ GstBuffer *new;
+ GstBuffer *buffer;
+ GstBuffer *buffer_out;
+ GstCaps *incaps;
+ GstCaps *outcaps;
+
+ incaps = gst_caps_from_string (in_string);
+ buffer = gst_buffer_new_and_alloc (in_size);
+ outcaps = gst_caps_from_string (out_string);
+ buffer_out = gst_buffer_new_and_alloc (out_size);
+
+ /* check that there are no buffers waiting */
+ gst_check_drop_buffers ();
+
+ /* create the element */
+ element = gst_check_setup_element ("aspectratiocrop");
+
+ /* set the requested aspect ratio */
+ g_object_set (G_OBJECT (element), "aspect-ratio", ar_n, ar_d, NULL);
+
+ /* create the src pad */
+ src_pad = gst_pad_new (NULL, GST_PAD_SRC);
+ gst_pad_set_active (src_pad, TRUE);
+ GST_DEBUG ("setting caps %s %" GST_PTR_FORMAT, in_string, incaps);
+ gst_check_setup_events (src_pad, element, incaps, GST_FORMAT_TIME);
+
+ pad_peer = gst_element_get_static_pad (element, "sink");
+ fail_if (pad_peer == NULL);
+ fail_unless (gst_pad_link (src_pad, pad_peer) == GST_PAD_LINK_OK,
+ "Could not link source and %s sink pads", GST_ELEMENT_NAME (element));
+ gst_object_unref (pad_peer);
+
+ /* create the sink pad */
+ pad_peer = gst_element_get_static_pad (element, "src");
+ sink_pad = gst_pad_new_from_static_template (&sinktemplate, "sink");
+ fail_unless (gst_pad_link (pad_peer, sink_pad) == GST_PAD_LINK_OK,
+ "Could not link sink and %s source pads", GST_ELEMENT_NAME (element));
+ gst_object_unref (pad_peer);
+ gst_pad_set_chain_function (sink_pad, gst_check_chain_func);
+ gst_pad_set_active (sink_pad, TRUE);
+
+ /* configure the sink pad */
+ fail_unless (gst_element_set_state (element,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ fail_unless (gst_pad_push (src_pad, buffer) == GST_FLOW_OK,
+ "Failed to push buffer");
+ fail_unless (gst_element_set_state (element,
+ GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS, "could not set to null");
+
+ /* check the result */
+ fail_unless (g_list_length (buffers) == 1);
+ new = GST_BUFFER (buffers->data);
+ buffers = g_list_remove (buffers, new);
+ fail_unless (gst_buffer_get_size (buffer_out) == gst_buffer_get_size (new),
+ "size of the buffers are not the same");
+ {
+ GstCaps *sinkpad_caps;
+
+ sinkpad_caps = gst_pad_get_current_caps (sink_pad);
+
+ gst_check_caps_equal (sinkpad_caps, outcaps);
+
+ gst_caps_unref (sinkpad_caps);
+ }
+ gst_buffer_unref (new);
+ gst_buffer_unref (buffer_out);
+ gst_caps_unref (outcaps);
+ gst_caps_unref (incaps);
+
+ /* teardown the element and pads */
+ gst_pad_set_active (src_pad, FALSE);
+ gst_check_teardown_src_pad (element);
+ gst_pad_set_active (sink_pad, FALSE);
+ gst_check_teardown_sink_pad (element);
+ gst_check_teardown_element (element);
+}
+
+GST_START_TEST (test_no_cropping)
+{
+ check_aspectratiocrop
+ ("video/x-raw, format=(string)YUY2, width=(int)320, height=(int)240, framerate=(fraction)30/1",
+ "video/x-raw, format=(string)YUY2, width=(int)320, height=(int)240, framerate=(fraction)30/1",
+ 153600, 153600, 4, 3);
+ check_aspectratiocrop
+ ("video/x-raw, format=(string)YUY2, width=(int)320, height=(int)320, framerate=(fraction)30/1, pixel-aspect-ratio=(fraction)4/3",
+ "video/x-raw, format=(string)YUY2, width=(int)320, height=(int)320, framerate=(fraction)30/1, pixel-aspect-ratio=(fraction)4/3",
+ 204800, 204800, 4, 3);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_autocropping)
+{
+ check_aspectratiocrop
+ ("video/x-raw, format=(string)YUY2, width=(int)320, height=(int)240, framerate=(fraction)30/1, pixel-aspect-ratio=(fraction)4/3",
+ "video/x-raw, format=(string)YUY2, width=(int)240, height=(int)240, framerate=(fraction)30/1, pixel-aspect-ratio=(fraction)4/3",
+ 153600, 115200, 4, 3);
+
+ check_aspectratiocrop
+ ("video/x-raw, format=(string)YUY2, width=(int)320, height=(int)240, framerate=(fraction)30/1, pixel-aspect-ratio=(fraction)16/9",
+ "video/x-raw, format=(string)YUY2, width=(int)180, height=(int)240, framerate=(fraction)30/1, pixel-aspect-ratio=(fraction)16/9",
+ 153600, 86400, 4, 3);
+
+ check_aspectratiocrop
+ ("video/x-raw, format=(string)YUY2, width=(int)320, height=(int)240, framerate=(fraction)30/1, pixel-aspect-ratio=(fraction)16/15",
+ "video/x-raw, format=(string)YUY2, width=(int)320, height=(int)192, framerate=(fraction)30/1, pixel-aspect-ratio=(fraction)16/15",
+ 153600, 122880, 16, 9);
+
+}
+
+GST_END_TEST;
+
+static Suite *
+aspectratiocrop_suite (void)
+{
+ Suite *s = suite_create ("aspectratiocrop");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_no_cropping);
+ tcase_add_test (tc_chain, test_autocropping);
+
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ int nf;
+
+ Suite *s = aspectratiocrop_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ srunner_run_all (sr, CK_NORMAL);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}
diff --git a/tests/check/elements/audioamplify.c b/tests/check/elements/audioamplify.c
new file mode 100644
index 0000000..bebb004
--- /dev/null
+++ b/tests/check/elements/audioamplify.c
@@ -0,0 +1,482 @@
+/* GStreamer
+ *
+ * unit test for audioamplify
+ *
+ * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
+ *
+ * Greatly based on the audiopanorama unit test
+ * Copyright (C) 2006 Stefan Kost <ensonic@users.sf.net>
+ *
+ * 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.
+ */
+
+#include <unistd.h>
+
+#include <gst/base/gstbasetransform.h>
+#include <gst/check/gstcheck.h>
+#include <gst/audio/audio.h>
+
+gboolean have_eos = FALSE;
+
+/* 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 */
+GstPad *mysrcpad, *mysinkpad;
+
+
+#define AMPLIFY_CAPS_STRING \
+ "audio/x-raw, " \
+ "channels = (int) 1, " \
+ "rate = (int) 44100, " \
+ "layout = (string) interleaved, " \
+ "format = (string) " GST_AUDIO_NE(S16)
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "channels = (int) 1, "
+ "rate = (int) [ 1, MAX ], "
+ "layout = (string) interleaved, "
+ "format = (string) " GST_AUDIO_NE (S16)));
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "channels = (int) 1, "
+ "rate = (int) [ 1, MAX ], "
+ "layout = (string) interleaved, "
+ "format = (string) " GST_AUDIO_NE (S16)));
+
+static GstElement *
+setup_amplify (void)
+{
+ GstElement *amplify;
+
+ GST_DEBUG ("setup_amplify");
+ amplify = gst_check_setup_element ("audioamplify");
+ mysrcpad = gst_check_setup_src_pad (amplify, &srctemplate);
+ mysinkpad = gst_check_setup_sink_pad (amplify, &sinktemplate);
+ gst_pad_set_active (mysrcpad, TRUE);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ return amplify;
+}
+
+static void
+cleanup_amplify (GstElement * amplify)
+{
+ GST_DEBUG ("cleanup_amplify");
+
+ g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
+ g_list_free (buffers);
+ buffers = NULL;
+
+ gst_pad_set_active (mysrcpad, FALSE);
+ gst_pad_set_active (mysinkpad, FALSE);
+ gst_check_teardown_src_pad (amplify);
+ gst_check_teardown_sink_pad (amplify);
+ gst_check_teardown_element (amplify);
+}
+
+GST_START_TEST (test_passthrough)
+{
+ GstElement *amplify;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gint16 in[6] = { 24576, -16384, 256, -128, 0, -24576 };
+ gint16 res[6];
+
+ amplify = setup_amplify ();
+ fail_unless (gst_element_set_state (amplify,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ caps = gst_caps_from_string (AMPLIFY_CAPS_STRING);
+ gst_check_setup_events (mysrcpad, amplify, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+
+ inbuffer =
+ gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, in, 12, 0, 12,
+ NULL, NULL);
+ fail_unless (gst_buffer_memcmp (inbuffer, 0, in, 12) == 0);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... but it ends up being collected on the global buffer list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ fail_unless (gst_buffer_extract (outbuffer, 0, res, 12) == 12);
+ GST_INFO
+ ("expected %+5d %+5d %+5d %+5d %+5d %+5d real %+5d %+5d %+5d %+5d %+5d %+5d",
+ in[0], in[1], in[2], in[3], in[4], in[5], res[0], res[1], res[2], res[3],
+ res[4], res[5]);
+ fail_unless (gst_buffer_memcmp (outbuffer, 0, in, 12) == 0);
+
+ /* cleanup */
+ cleanup_amplify (amplify);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_zero)
+{
+ GstElement *amplify;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gint16 in[6] = { 24576, -16384, 256, -128, 0, -24576 };
+ gint16 out[6] = { 0, 0, 0, 0, 0, 0 };
+ gint16 res[6];
+
+ amplify = setup_amplify ();
+ g_object_set (G_OBJECT (amplify), "amplification", 0.0, NULL);
+ fail_unless (gst_element_set_state (amplify,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ caps = gst_caps_from_string (AMPLIFY_CAPS_STRING);
+ gst_check_setup_events (mysrcpad, amplify, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+
+ inbuffer =
+ gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, in, 12, 0, 12,
+ NULL, NULL);
+ fail_unless (gst_buffer_memcmp (inbuffer, 0, in, 12) == 0);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ fail_unless (gst_buffer_extract (outbuffer, 0, res, 12) == 12);
+ GST_INFO
+ ("expected %+5d %+5d %+5d %+5d %+5d %+5d real %+5d %+5d %+5d %+5d %+5d %+5d",
+ out[0], out[1], out[2], out[3], out[4], out[5], res[0], res[1], res[2],
+ res[3], res[4], res[5]);
+ fail_unless (gst_buffer_memcmp (outbuffer, 0, out, 12) == 0);
+
+ /* cleanup */
+ cleanup_amplify (amplify);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_050_clip)
+{
+ GstElement *amplify;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gint16 in[6] = { 24576, -16384, 256, -128, 0, -24576 };
+ gint16 out[6] = { 12288, -8192, 128, -64, 0, -12288 };
+ gint16 res[6];
+
+ amplify = setup_amplify ();
+ g_object_set (G_OBJECT (amplify), "amplification", 0.5, NULL);
+ fail_unless (gst_element_set_state (amplify,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ caps = gst_caps_from_string (AMPLIFY_CAPS_STRING);
+ gst_check_setup_events (mysrcpad, amplify, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+
+ inbuffer =
+ gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, in, 12, 0, 12,
+ NULL, NULL);
+ fail_unless (gst_buffer_memcmp (inbuffer, 0, in, 12) == 0);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ fail_unless (gst_buffer_extract (outbuffer, 0, res, 12) == 12);
+ GST_INFO
+ ("expected %+5d %+5d %+5d %+5d %+5d %+5d real %+5d %+5d %+5d %+5d %+5d %+5d",
+ out[0], out[1], out[2], out[3], out[4], out[5], res[0], res[1], res[2],
+ res[3], res[4], res[5]);
+ fail_unless (gst_buffer_memcmp (outbuffer, 0, out, 12) == 0);
+
+ /* cleanup */
+ cleanup_amplify (amplify);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_200_clip)
+{
+ GstElement *amplify;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gint16 in[6] = { 24576, -16384, 256, -128, 0, -24576 };
+ gint16 out[6] = { G_MAXINT16, -32768, 512, -256, 0, G_MININT16 };
+ gint16 res[6];
+
+ amplify = setup_amplify ();
+ g_object_set (G_OBJECT (amplify), "amplification", 2.0, NULL);
+ fail_unless (gst_element_set_state (amplify,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ caps = gst_caps_from_string (AMPLIFY_CAPS_STRING);
+ gst_check_setup_events (mysrcpad, amplify, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+
+ inbuffer =
+ gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, in, 12, 0, 12,
+ NULL, NULL);
+ fail_unless (gst_buffer_memcmp (inbuffer, 0, in, 12) == 0);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ fail_unless (gst_buffer_extract (outbuffer, 0, res, 12) == 12);
+ GST_INFO
+ ("expected %+5d %+5d %+5d %+5d %+5d %+5d real %+5d %+5d %+5d %+5d %+5d %+5d",
+ out[0], out[1], out[2], out[3], out[4], out[5], res[0], res[1], res[2],
+ res[3], res[4], res[5]);
+ fail_unless (gst_buffer_memcmp (outbuffer, 0, out, 12) == 0);
+
+ /* cleanup */
+ cleanup_amplify (amplify);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_050_wrap_negative)
+{
+ GstElement *amplify;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gint16 in[6] = { 24576, -16384, 256, -128, 0, -24576 };
+ gint16 out[6] = { 12288, -8192, 128, -64, 0, -12288 };
+ gint16 res[6];
+
+ amplify = setup_amplify ();
+ g_object_set (G_OBJECT (amplify), "amplification", 0.5, NULL);
+ g_object_set (G_OBJECT (amplify), "clipping-method", 1, NULL);
+ fail_unless (gst_element_set_state (amplify,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ caps = gst_caps_from_string (AMPLIFY_CAPS_STRING);
+ gst_check_setup_events (mysrcpad, amplify, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+
+ inbuffer =
+ gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, in, 12, 0, 12,
+ NULL, NULL);
+ fail_unless (gst_buffer_memcmp (inbuffer, 0, in, 12) == 0);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ fail_unless (gst_buffer_extract (outbuffer, 0, res, 12) == 12);
+ GST_INFO
+ ("expected %+5d %+5d %+5d %+5d %+5d %+5d real %+5d %+5d %+5d %+5d %+5d %+5d",
+ out[0], out[1], out[2], out[3], out[4], out[5], res[0], res[1], res[2],
+ res[3], res[4], res[5]);
+ fail_unless (gst_buffer_memcmp (outbuffer, 0, out, 12) == 0);
+
+ /* cleanup */
+ cleanup_amplify (amplify);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_200_wrap_negative)
+{
+ GstElement *amplify;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gint16 in[6] = { 24576, -16384, 256, -128, 0, -24576 };
+ gint16 out[6] = { -16384, -32768, 512, -256, 0, 16384 };
+ gint16 res[6];
+
+ amplify = setup_amplify ();
+ g_object_set (G_OBJECT (amplify), "amplification", 2.0, NULL);
+ g_object_set (G_OBJECT (amplify), "clipping-method", 1, NULL);
+ fail_unless (gst_element_set_state (amplify,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ caps = gst_caps_from_string (AMPLIFY_CAPS_STRING);
+ gst_check_setup_events (mysrcpad, amplify, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+
+ inbuffer =
+ gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, in, 12, 0, 12,
+ NULL, NULL);
+ fail_unless (gst_buffer_memcmp (inbuffer, 0, in, 12) == 0);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ fail_unless (gst_buffer_extract (outbuffer, 0, res, 12) == 12);
+ GST_INFO
+ ("expected %+5d %+5d %+5d %+5d %+5d %+5d real %+5d %+5d %+5d %+5d %+5d %+5d",
+ out[0], out[1], out[2], out[3], out[4], out[5], res[0], res[1], res[2],
+ res[3], res[4], res[5]);
+ fail_unless (gst_buffer_memcmp (outbuffer, 0, out, 12) == 0);
+
+ /* cleanup */
+ cleanup_amplify (amplify);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_050_wrap_positive)
+{
+ GstElement *amplify;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gint16 in[6] = { 24576, -16384, 256, -128, 0, -24576 };
+ gint16 out[6] = { 12288, -8192, 128, -64, 0, -12288 };
+ gint16 res[6];
+
+ amplify = setup_amplify ();
+ g_object_set (G_OBJECT (amplify), "amplification", 0.5, NULL);
+ g_object_set (G_OBJECT (amplify), "clipping-method", 2, NULL);
+ fail_unless (gst_element_set_state (amplify,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ caps = gst_caps_from_string (AMPLIFY_CAPS_STRING);
+ gst_check_setup_events (mysrcpad, amplify, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+
+ inbuffer =
+ gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, in, 12, 0, 12,
+ NULL, NULL);
+ fail_unless (gst_buffer_memcmp (inbuffer, 0, in, 12) == 0);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ fail_unless (gst_buffer_extract (outbuffer, 0, res, 12) == 12);
+ GST_INFO
+ ("expected %+5d %+5d %+5d %+5d %+5d %+5d real %+5d %+5d %+5d %+5d %+5d %+5d",
+ out[0], out[1], out[2], out[3], out[4], out[5], res[0], res[1], res[2],
+ res[3], res[4], res[5]);
+ fail_unless (gst_buffer_memcmp (outbuffer, 0, out, 12) == 0);
+
+ /* cleanup */
+ cleanup_amplify (amplify);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_200_wrap_positive)
+{
+ GstElement *amplify;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gint16 in[6] = { 24576, -16384, 256, -128, 0, -24576 };
+ gint16 out[6] = { 16382, -32768, 512, -256, 0, -16384 };
+ gint16 res[6];
+
+ amplify = setup_amplify ();
+ g_object_set (G_OBJECT (amplify), "amplification", 2.0, NULL);
+ g_object_set (G_OBJECT (amplify), "clipping-method", 2, NULL);
+ fail_unless (gst_element_set_state (amplify,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ caps = gst_caps_from_string (AMPLIFY_CAPS_STRING);
+ gst_check_setup_events (mysrcpad, amplify, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+
+ inbuffer =
+ gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, in, 12, 0, 12,
+ NULL, NULL);
+ fail_unless (gst_buffer_memcmp (inbuffer, 0, in, 12) == 0);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ fail_unless (gst_buffer_extract (outbuffer, 0, res, 12) == 12);
+ GST_INFO
+ ("expected %+5d %+5d %+5d %+5d %+5d %+5d real %+5d %+5d %+5d %+5d %+5d %+5d",
+ out[0], out[1], out[2], out[3], out[4], out[5], res[0], res[1], res[2],
+ res[3], res[4], res[5]);
+ fail_unless (gst_buffer_memcmp (outbuffer, 0, out, 12) == 0);
+
+ /* cleanup */
+ cleanup_amplify (amplify);
+}
+
+GST_END_TEST;
+
+static Suite *
+amplify_suite (void)
+{
+ Suite *s = suite_create ("amplify");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_passthrough);
+ tcase_add_test (tc_chain, test_zero);
+ tcase_add_test (tc_chain, test_050_clip);
+ tcase_add_test (tc_chain, test_200_clip);
+ tcase_add_test (tc_chain, test_050_wrap_negative);
+ tcase_add_test (tc_chain, test_200_wrap_negative);
+ tcase_add_test (tc_chain, test_050_wrap_positive);
+ tcase_add_test (tc_chain, test_200_wrap_positive);
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ int nf;
+
+ Suite *s = amplify_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ srunner_run_all (sr, CK_NORMAL);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}
diff --git a/tests/check/elements/audiochebband.c b/tests/check/elements/audiochebband.c
new file mode 100644
index 0000000..7021822
--- /dev/null
+++ b/tests/check/elements/audiochebband.c
@@ -0,0 +1,1685 @@
+/* GStreamer
+ *
+ * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
+ *
+ * audiochebband.c: Unit test for the audiochebband element
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <gst/gst.h>
+#include <gst/base/gstbasetransform.h>
+#include <gst/audio/audio.h>
+#include <gst/check/gstcheck.h>
+
+#include <math.h>
+
+/* 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 */
+GstPad *mysrcpad, *mysinkpad;
+
+#define BUFFER_CAPS_STRING_32 \
+ "audio/x-raw, " \
+ "channels = (int) 1, " \
+ "rate = (int) 44100, " \
+ "layout = (string) interleaved, " \
+ "format = (string) " GST_AUDIO_NE(F32)
+
+#define BUFFER_CAPS_STRING_64 \
+ "audio/x-raw, " \
+ "channels = (int) 1, " \
+ "rate = (int) 44100, " \
+ "layout = (string) interleaved, " \
+ "format = (string) " GST_AUDIO_NE(F64)
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "channels = (int) 1, "
+ "rate = (int) 44100, "
+ "layout = (string) interleaved, "
+ "format = (string) { "
+ GST_AUDIO_NE (F32) ", " GST_AUDIO_NE (F64) " }"));
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "channels = (int) 1, "
+ "rate = (int) 44100, "
+ "layout = (string) interleaved, "
+ "format = (string) { "
+ GST_AUDIO_NE (F32) ", " GST_AUDIO_NE (F64) " }"));
+
+static GstElement *
+setup_audiochebband (void)
+{
+ GstElement *audiochebband;
+
+ GST_DEBUG ("setup_audiochebband");
+ audiochebband = gst_check_setup_element ("audiochebband");
+ mysrcpad = gst_check_setup_src_pad (audiochebband, &srctemplate);
+ mysinkpad = gst_check_setup_sink_pad (audiochebband, &sinktemplate);
+ gst_pad_set_active (mysrcpad, TRUE);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ return audiochebband;
+}
+
+static void
+cleanup_audiochebband (GstElement * audiochebband)
+{
+ GST_DEBUG ("cleanup_audiochebband");
+
+ g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
+ g_list_free (buffers);
+ buffers = NULL;
+
+ gst_pad_set_active (mysrcpad, FALSE);
+ gst_pad_set_active (mysinkpad, FALSE);
+ gst_check_teardown_src_pad (audiochebband);
+ gst_check_teardown_sink_pad (audiochebband);
+ gst_check_teardown_element (audiochebband);
+}
+
+/* Test if data containing only one frequency component
+ * at 0 is erased with bandpass mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_type1_32_bp_0hz)
+{
+ GstElement *audiochebband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiochebband = setup_audiochebband ();
+ /* Set to bandpass */
+ g_object_set (G_OBJECT (audiochebband), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiochebband), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiochebband), "type", 1, NULL);
+ g_object_set (G_OBJECT (audiochebband), "ripple", 0.25, NULL);
+
+ fail_unless (gst_element_set_state (audiochebband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiochebband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiochebband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_allocate (NULL, 1024 * sizeof (gfloat), NULL);
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 1024; i++)
+ in[i] = 1.0;
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiochebband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gfloat *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 1024; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 1024.0);
+ fail_unless (rms <= 0.1);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiochebband (audiochebband);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at band center is preserved with bandpass mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_type1_32_bp_11025hz)
+{
+ GstElement *audiochebband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiochebband = setup_audiochebband ();
+ /* Set to bandpass */
+ g_object_set (G_OBJECT (audiochebband), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiochebband), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiochebband), "type", 1, NULL);
+ g_object_set (G_OBJECT (audiochebband), "ripple", 0.25, NULL);
+
+ fail_unless (gst_element_set_state (audiochebband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiochebband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiochebband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_allocate (NULL, 1024 * sizeof (gfloat), NULL);
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 1024; i += 4) {
+ in[i] = 0.0;
+ in[i + 1] = 1.0;
+ in[i + 2] = 0.0;
+ in[i + 3] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiochebband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gfloat *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 1024; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 1024.0);
+ fail_unless (rms >= 0.6);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiochebband (audiochebband);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at rate/2 is erased with bandpass mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_type1_32_bp_22050hz)
+{
+ GstElement *audiochebband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiochebband = setup_audiochebband ();
+ /* Set to bandpass */
+ g_object_set (G_OBJECT (audiochebband), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiochebband), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiochebband), "type", 1, NULL);
+ g_object_set (G_OBJECT (audiochebband), "ripple", 0.25, NULL);
+
+ fail_unless (gst_element_set_state (audiochebband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiochebband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiochebband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_allocate (NULL, 1024 * sizeof (gfloat), NULL);
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 1024; i += 2) {
+ in[i] = 1.0;
+ in[i + 1] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiochebband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gfloat *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 1024; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 1024.0);
+ fail_unless (rms <= 0.1);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiochebband (audiochebband);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at 0 is preserved with bandreject mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_type1_32_br_0hz)
+{
+ GstElement *audiochebband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiochebband = setup_audiochebband ();
+ /* Set to bandreject */
+ g_object_set (G_OBJECT (audiochebband), "mode", 1, NULL);
+ g_object_set (G_OBJECT (audiochebband), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiochebband), "type", 1, NULL);
+ g_object_set (G_OBJECT (audiochebband), "ripple", 0.25, NULL);
+
+ fail_unless (gst_element_set_state (audiochebband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiochebband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiochebband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_allocate (NULL, 1024 * sizeof (gfloat), NULL);
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 1024; i++)
+ in[i] = 1.0;
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiochebband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gfloat *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 1024; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 1024.0);
+ fail_unless (rms >= 0.9);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiochebband (audiochebband);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at band center is erased with bandreject mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_type1_32_br_11025hz)
+{
+ GstElement *audiochebband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiochebband = setup_audiochebband ();
+ /* Set to bandreject */
+ g_object_set (G_OBJECT (audiochebband), "mode", 1, NULL);
+ g_object_set (G_OBJECT (audiochebband), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiochebband), "type", 1, NULL);
+ g_object_set (G_OBJECT (audiochebband), "ripple", 0.25, NULL);
+
+ fail_unless (gst_element_set_state (audiochebband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiochebband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiochebband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_allocate (NULL, 1024 * sizeof (gfloat), NULL);
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 1024; i += 4) {
+ in[i] = 0.0;
+ in[i + 1] = 1.0;
+ in[i + 2] = 0.0;
+ in[i + 3] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiochebband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gfloat *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 1024; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 1024.0);
+ fail_unless (rms <= 0.1);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiochebband (audiochebband);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at rate/2 is preserved with bandreject mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_type1_32_br_22050hz)
+{
+ GstElement *audiochebband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiochebband = setup_audiochebband ();
+ /* Set to bandreject */
+ g_object_set (G_OBJECT (audiochebband), "mode", 1, NULL);
+ g_object_set (G_OBJECT (audiochebband), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiochebband), "type", 1, NULL);
+ g_object_set (G_OBJECT (audiochebband), "ripple", 0.25, NULL);
+
+ fail_unless (gst_element_set_state (audiochebband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiochebband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiochebband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_allocate (NULL, 1024 * sizeof (gfloat), NULL);
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 1024; i += 2) {
+ in[i] = 1.0;
+ in[i + 1] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiochebband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gfloat *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 1024; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 1024.0);
+ fail_unless (rms >= 0.9);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiochebband (audiochebband);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at 0 is erased with bandpass mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_type1_64_bp_0hz)
+{
+ GstElement *audiochebband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiochebband = setup_audiochebband ();
+ /* Set to bandpass */
+ g_object_set (G_OBJECT (audiochebband), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiochebband), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiochebband), "type", 1, NULL);
+ g_object_set (G_OBJECT (audiochebband), "ripple", 0.25, NULL);
+
+ fail_unless (gst_element_set_state (audiochebband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiochebband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiochebband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_and_alloc (1024 * sizeof (gdouble));
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 1024; i++)
+ in[i] = 1.0;
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiochebband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 1024; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 1024.0);
+ fail_unless (rms <= 0.1);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiochebband (audiochebband);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at band center is preserved with bandpass mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_type1_64_bp_11025hz)
+{
+ GstElement *audiochebband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiochebband = setup_audiochebband ();
+ /* Set to bandpass */
+ g_object_set (G_OBJECT (audiochebband), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiochebband), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiochebband), "type", 1, NULL);
+ g_object_set (G_OBJECT (audiochebband), "ripple", 0.25, NULL);
+
+ fail_unless (gst_element_set_state (audiochebband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiochebband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiochebband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_and_alloc (1024 * sizeof (gdouble));
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 1024; i += 4) {
+ in[i] = 0.0;
+ in[i + 1] = 1.0;
+ in[i + 2] = 0.0;
+ in[i + 3] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiochebband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 1024; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 1024.0);
+ fail_unless (rms >= 0.6);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiochebband (audiochebband);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at rate/2 is erased with bandpass mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_type1_64_bp_22050hz)
+{
+ GstElement *audiochebband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiochebband = setup_audiochebband ();
+ /* Set to bandpass */
+ g_object_set (G_OBJECT (audiochebband), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiochebband), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiochebband), "type", 1, NULL);
+ g_object_set (G_OBJECT (audiochebband), "ripple", 0.25, NULL);
+
+ fail_unless (gst_element_set_state (audiochebband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiochebband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiochebband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_and_alloc (1024 * sizeof (gdouble));
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 1024; i += 2) {
+ in[i] = 1.0;
+ in[i + 1] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiochebband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 1024; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 1024.0);
+ fail_unless (rms <= 0.1);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiochebband (audiochebband);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at 0 is preserved with bandreject mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_type1_64_br_0hz)
+{
+ GstElement *audiochebband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiochebband = setup_audiochebband ();
+ /* Set to bandreject */
+ g_object_set (G_OBJECT (audiochebband), "mode", 1, NULL);
+ g_object_set (G_OBJECT (audiochebband), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiochebband), "type", 1, NULL);
+ g_object_set (G_OBJECT (audiochebband), "ripple", 0.25, NULL);
+
+ fail_unless (gst_element_set_state (audiochebband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiochebband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiochebband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_and_alloc (1024 * sizeof (gdouble));
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 1024; i++)
+ in[i] = 1.0;
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiochebband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 1024; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 1024.0);
+ fail_unless (rms >= 0.9);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiochebband (audiochebband);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at band center is erased with bandreject mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_type1_64_br_11025hz)
+{
+ GstElement *audiochebband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiochebband = setup_audiochebband ();
+ /* Set to bandreject */
+ g_object_set (G_OBJECT (audiochebband), "mode", 1, NULL);
+ g_object_set (G_OBJECT (audiochebband), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiochebband), "type", 1, NULL);
+ g_object_set (G_OBJECT (audiochebband), "ripple", 0.25, NULL);
+
+ fail_unless (gst_element_set_state (audiochebband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiochebband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiochebband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_and_alloc (1024 * sizeof (gdouble));
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 1024; i += 4) {
+ in[i] = 0.0;
+ in[i + 1] = 1.0;
+ in[i + 2] = 0.0;
+ in[i + 3] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiochebband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 1024; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 1024.0);
+ fail_unless (rms <= 0.1);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiochebband (audiochebband);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at rate/2 is preserved with bandreject mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_type1_64_br_22050hz)
+{
+ GstElement *audiochebband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiochebband = setup_audiochebband ();
+ /* Set to bandreject */
+ g_object_set (G_OBJECT (audiochebband), "mode", 1, NULL);
+ g_object_set (G_OBJECT (audiochebband), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiochebband), "type", 1, NULL);
+ g_object_set (G_OBJECT (audiochebband), "ripple", 0.25, NULL);
+
+ fail_unless (gst_element_set_state (audiochebband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiochebband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiochebband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_and_alloc (1024 * sizeof (gdouble));
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 1024; i += 2) {
+ in[i] = 1.0;
+ in[i + 1] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiochebband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 1024; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 1024.0);
+ fail_unless (rms >= 0.9);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiochebband (audiochebband);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at 0 is erased with bandpass mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_type2_32_bp_0hz)
+{
+ GstElement *audiochebband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiochebband = setup_audiochebband ();
+ /* Set to bandpass */
+ g_object_set (G_OBJECT (audiochebband), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiochebband), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiochebband), "type", 2, NULL);
+ g_object_set (G_OBJECT (audiochebband), "ripple", 40.0, NULL);
+
+ fail_unless (gst_element_set_state (audiochebband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiochebband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiochebband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_allocate (NULL, 1024 * sizeof (gfloat), NULL);
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 1024; i++)
+ in[i] = 1.0;
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiochebband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gfloat *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 1024; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 1024.0);
+ fail_unless (rms <= 0.1);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiochebband (audiochebband);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at band center is preserved with bandpass mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_type2_32_bp_11025hz)
+{
+ GstElement *audiochebband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiochebband = setup_audiochebband ();
+ /* Set to bandpass */
+ g_object_set (G_OBJECT (audiochebband), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiochebband), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiochebband), "type", 2, NULL);
+ g_object_set (G_OBJECT (audiochebband), "ripple", 40.0, NULL);
+
+ fail_unless (gst_element_set_state (audiochebband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiochebband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiochebband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_allocate (NULL, 1024 * sizeof (gfloat), NULL);
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 1024; i += 4) {
+ in[i] = 0.0;
+ in[i + 1] = 1.0;
+ in[i + 2] = 0.0;
+ in[i + 3] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiochebband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gfloat *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 1024; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 1024.0);
+ fail_unless (rms >= 0.6);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiochebband (audiochebband);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at rate/2 is erased with bandpass mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_type2_32_bp_22050hz)
+{
+ GstElement *audiochebband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiochebband = setup_audiochebband ();
+ /* Set to bandpass */
+ g_object_set (G_OBJECT (audiochebband), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiochebband), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiochebband), "type", 2, NULL);
+ g_object_set (G_OBJECT (audiochebband), "ripple", 40.0, NULL);
+
+ fail_unless (gst_element_set_state (audiochebband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiochebband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiochebband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_allocate (NULL, 1024 * sizeof (gfloat), NULL);
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 1024; i += 2) {
+ in[i] = 1.0;
+ in[i + 1] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiochebband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gfloat *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 1024; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 1024.0);
+ fail_unless (rms <= 0.1);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiochebband (audiochebband);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at 0 is preserved with bandreject mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_type2_32_br_0hz)
+{
+ GstElement *audiochebband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiochebband = setup_audiochebband ();
+ /* Set to bandreject */
+ g_object_set (G_OBJECT (audiochebband), "mode", 1, NULL);
+ g_object_set (G_OBJECT (audiochebband), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiochebband), "type", 2, NULL);
+ g_object_set (G_OBJECT (audiochebband), "ripple", 40.0, NULL);
+
+ fail_unless (gst_element_set_state (audiochebband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiochebband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiochebband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_allocate (NULL, 1024 * sizeof (gfloat), NULL);
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 1024; i++)
+ in[i] = 1.0;
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiochebband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gfloat *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 1024; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 1024.0);
+ fail_unless (rms >= 0.9);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiochebband (audiochebband);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at band center is erased with bandreject mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_type2_32_br_11025hz)
+{
+ GstElement *audiochebband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiochebband = setup_audiochebband ();
+ /* Set to bandreject */
+ g_object_set (G_OBJECT (audiochebband), "mode", 1, NULL);
+ g_object_set (G_OBJECT (audiochebband), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiochebband), "type", 2, NULL);
+ g_object_set (G_OBJECT (audiochebband), "ripple", 40.0, NULL);
+
+ fail_unless (gst_element_set_state (audiochebband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiochebband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiochebband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_allocate (NULL, 1024 * sizeof (gfloat), NULL);
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 1024; i += 4) {
+ in[i] = 0.0;
+ in[i + 1] = 1.0;
+ in[i + 2] = 0.0;
+ in[i + 3] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiochebband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gfloat *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 1024; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 1024.0);
+ fail_unless (rms <= 0.1);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiochebband (audiochebband);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at rate/2 is preserved with bandreject mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_type2_32_br_22050hz)
+{
+ GstElement *audiochebband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiochebband = setup_audiochebband ();
+ /* Set to bandreject */
+ g_object_set (G_OBJECT (audiochebband), "mode", 1, NULL);
+ g_object_set (G_OBJECT (audiochebband), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiochebband), "type", 2, NULL);
+ g_object_set (G_OBJECT (audiochebband), "ripple", 40.0, NULL);
+
+ fail_unless (gst_element_set_state (audiochebband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiochebband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiochebband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_allocate (NULL, 1024 * sizeof (gfloat), NULL);
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 1024; i += 2) {
+ in[i] = 1.0;
+ in[i + 1] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiochebband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gfloat *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 1024; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 1024.0);
+ fail_unless (rms >= 0.9);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiochebband (audiochebband);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at 0 is erased with bandpass mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_type2_64_bp_0hz)
+{
+ GstElement *audiochebband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiochebband = setup_audiochebband ();
+ /* Set to bandpass */
+ g_object_set (G_OBJECT (audiochebband), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiochebband), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiochebband), "type", 2, NULL);
+ g_object_set (G_OBJECT (audiochebband), "ripple", 40.0, NULL);
+
+ fail_unless (gst_element_set_state (audiochebband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiochebband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiochebband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_and_alloc (1024 * sizeof (gdouble));
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 1024; i++)
+ in[i] = 1.0;
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiochebband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 1024; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 1024.0);
+ fail_unless (rms <= 0.1);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiochebband (audiochebband);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at band center is preserved with bandpass mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_type2_64_bp_11025hz)
+{
+ GstElement *audiochebband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiochebband = setup_audiochebband ();
+ /* Set to bandpass */
+ g_object_set (G_OBJECT (audiochebband), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiochebband), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiochebband), "type", 2, NULL);
+ g_object_set (G_OBJECT (audiochebband), "ripple", 40.0, NULL);
+
+ fail_unless (gst_element_set_state (audiochebband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiochebband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiochebband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_and_alloc (1024 * sizeof (gdouble));
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 1024; i += 4) {
+ in[i] = 0.0;
+ in[i + 1] = 1.0;
+ in[i + 2] = 0.0;
+ in[i + 3] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiochebband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 1024; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 1024.0);
+ fail_unless (rms >= 0.6);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiochebband (audiochebband);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at rate/2 is erased with bandpass mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_type2_64_bp_22050hz)
+{
+ GstElement *audiochebband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiochebband = setup_audiochebband ();
+ /* Set to bandpass */
+ g_object_set (G_OBJECT (audiochebband), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiochebband), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiochebband), "type", 2, NULL);
+ g_object_set (G_OBJECT (audiochebband), "ripple", 40.0, NULL);
+
+ fail_unless (gst_element_set_state (audiochebband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiochebband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiochebband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_and_alloc (1024 * sizeof (gdouble));
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 1024; i += 2) {
+ in[i] = 1.0;
+ in[i + 1] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiochebband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 1024; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 1024.0);
+ fail_unless (rms <= 0.1);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiochebband (audiochebband);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at 0 is preserved with bandreject mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_type2_64_br_0hz)
+{
+ GstElement *audiochebband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiochebband = setup_audiochebband ();
+ /* Set to bandreject */
+ g_object_set (G_OBJECT (audiochebband), "mode", 1, NULL);
+ g_object_set (G_OBJECT (audiochebband), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiochebband), "type", 2, NULL);
+ g_object_set (G_OBJECT (audiochebband), "ripple", 40.0, NULL);
+
+ fail_unless (gst_element_set_state (audiochebband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiochebband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiochebband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_and_alloc (1024 * sizeof (gdouble));
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 1024; i++)
+ in[i] = 1.0;
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiochebband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 1024; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 1024.0);
+ fail_unless (rms >= 0.9);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiochebband (audiochebband);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at band center is erased with bandreject mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_type2_64_br_11025hz)
+{
+ GstElement *audiochebband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiochebband = setup_audiochebband ();
+ /* Set to bandreject */
+ g_object_set (G_OBJECT (audiochebband), "mode", 1, NULL);
+ g_object_set (G_OBJECT (audiochebband), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiochebband), "type", 2, NULL);
+ g_object_set (G_OBJECT (audiochebband), "ripple", 40.0, NULL);
+
+ fail_unless (gst_element_set_state (audiochebband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiochebband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiochebband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_and_alloc (1024 * sizeof (gdouble));
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 1024; i += 4) {
+ in[i] = 0.0;
+ in[i + 1] = 1.0;
+ in[i + 2] = 0.0;
+ in[i + 3] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiochebband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 1024; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 1024.0);
+ fail_unless (rms <= 0.1);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiochebband (audiochebband);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at rate/2 is preserved with bandreject mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_type2_64_br_22050hz)
+{
+ GstElement *audiochebband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiochebband = setup_audiochebband ();
+ /* Set to bandreject */
+ g_object_set (G_OBJECT (audiochebband), "mode", 1, NULL);
+ g_object_set (G_OBJECT (audiochebband), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiochebband), "type", 2, NULL);
+ g_object_set (G_OBJECT (audiochebband), "ripple", 40.0, NULL);
+
+ fail_unless (gst_element_set_state (audiochebband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiochebband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiochebband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_and_alloc (1024 * sizeof (gdouble));
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 1024; i += 2) {
+ in[i] = 1.0;
+ in[i + 1] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiochebband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 1024; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 1024.0);
+ fail_unless (rms >= 0.9);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiochebband (audiochebband);
+}
+
+GST_END_TEST;
+
+static Suite *
+audiochebband_suite (void)
+{
+ Suite *s = suite_create ("audiochebband");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_type1_32_bp_0hz);
+ tcase_add_test (tc_chain, test_type1_32_bp_11025hz);
+ tcase_add_test (tc_chain, test_type1_32_bp_22050hz);
+ tcase_add_test (tc_chain, test_type1_32_br_0hz);
+ tcase_add_test (tc_chain, test_type1_32_br_11025hz);
+ tcase_add_test (tc_chain, test_type1_32_br_22050hz);
+ tcase_add_test (tc_chain, test_type1_64_bp_0hz);
+ tcase_add_test (tc_chain, test_type1_64_bp_11025hz);
+ tcase_add_test (tc_chain, test_type1_64_bp_22050hz);
+ tcase_add_test (tc_chain, test_type1_64_br_0hz);
+ tcase_add_test (tc_chain, test_type1_64_br_11025hz);
+ tcase_add_test (tc_chain, test_type1_64_br_22050hz);
+ tcase_add_test (tc_chain, test_type2_32_bp_0hz);
+ tcase_add_test (tc_chain, test_type2_32_bp_11025hz);
+ tcase_add_test (tc_chain, test_type2_32_bp_22050hz);
+ tcase_add_test (tc_chain, test_type2_32_br_0hz);
+ tcase_add_test (tc_chain, test_type2_32_br_11025hz);
+ tcase_add_test (tc_chain, test_type2_32_br_22050hz);
+ tcase_add_test (tc_chain, test_type2_64_bp_0hz);
+ tcase_add_test (tc_chain, test_type2_64_bp_11025hz);
+ tcase_add_test (tc_chain, test_type2_64_bp_22050hz);
+ tcase_add_test (tc_chain, test_type2_64_br_0hz);
+ tcase_add_test (tc_chain, test_type2_64_br_11025hz);
+ tcase_add_test (tc_chain, test_type2_64_br_22050hz);
+
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ int nf;
+
+ Suite *s = audiochebband_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ srunner_run_all (sr, CK_NORMAL);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}
diff --git a/tests/check/elements/audiocheblimit.c b/tests/check/elements/audiocheblimit.c
new file mode 100644
index 0000000..c155b20
--- /dev/null
+++ b/tests/check/elements/audiocheblimit.c
@@ -0,0 +1,1101 @@
+/* GStreamer
+ *
+ * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
+ *
+ * audiocheblimit.c: Unit test for the audiocheblimit element
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <gst/gst.h>
+#include <gst/audio/audio.h>
+#include <gst/base/gstbasetransform.h>
+#include <gst/check/gstcheck.h>
+
+#include <math.h>
+
+/* 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 */
+GstPad *mysrcpad, *mysinkpad;
+
+#define BUFFER_CAPS_STRING_32 \
+ "audio/x-raw, " \
+ "channels = (int) 1, " \
+ "rate = (int) 44100, " \
+ "layout = (string) interleaved, " \
+ "format = (string) " GST_AUDIO_NE(F32)
+
+#define BUFFER_CAPS_STRING_64 \
+ "audio/x-raw, " \
+ "channels = (int) 1, " \
+ "rate = (int) 44100, " \
+ "layout = (string) interleaved, " \
+ "format = (string) " GST_AUDIO_NE(F64)
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "channels = (int) 1, "
+ "rate = (int) 44100, "
+ "layout = (string) interleaved, "
+ "format = (string) { "
+ GST_AUDIO_NE (F32) ", " GST_AUDIO_NE (F64) " }"));
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "channels = (int) 1, "
+ "rate = (int) 44100, "
+ "layout = (string) interleaved, "
+ "format = (string) { "
+ GST_AUDIO_NE (F32) ", " GST_AUDIO_NE (F64) " }"));
+
+static GstElement *
+setup_audiocheblimit (void)
+{
+ GstElement *audiocheblimit;
+
+ GST_DEBUG ("setup_audiocheblimit");
+ audiocheblimit = gst_check_setup_element ("audiocheblimit");
+ mysrcpad = gst_check_setup_src_pad (audiocheblimit, &srctemplate);
+ mysinkpad = gst_check_setup_sink_pad (audiocheblimit, &sinktemplate);
+ gst_pad_set_active (mysrcpad, TRUE);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ return audiocheblimit;
+}
+
+static void
+cleanup_audiocheblimit (GstElement * audiocheblimit)
+{
+ GST_DEBUG ("cleanup_audiocheblimit");
+
+ g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
+ g_list_free (buffers);
+ buffers = NULL;
+
+ gst_pad_set_active (mysrcpad, FALSE);
+ gst_pad_set_active (mysinkpad, FALSE);
+ gst_check_teardown_src_pad (audiocheblimit);
+ gst_check_teardown_sink_pad (audiocheblimit);
+ gst_check_teardown_element (audiocheblimit);
+}
+
+/* Test if data containing only one frequency component
+ * at 0 is preserved with lowpass mode and a cutoff
+ * at rate/4 */
+GST_START_TEST (test_type1_32_lp_0hz)
+{
+ GstElement *audiocheblimit;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiocheblimit = setup_audiocheblimit ();
+ /* Set to lowpass */
+ g_object_set (G_OBJECT (audiocheblimit), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "type", 1, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "ripple", 0.25, NULL);
+
+ fail_unless (gst_element_set_state (audiocheblimit,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiocheblimit), "cutoff", 44100 / 4.0, NULL);
+ inbuffer = gst_buffer_new_allocate (NULL, 128 * sizeof (gfloat), NULL);
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 128; i++)
+ in[i] = 1.0;
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiocheblimit, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gfloat *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 128; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 128.0);
+ fail_unless (rms >= 0.9);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiocheblimit (audiocheblimit);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at rate/2 is erased with lowpass mode and a cutoff
+ * at rate/4 */
+GST_START_TEST (test_type1_32_lp_22050hz)
+{
+ GstElement *audiocheblimit;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiocheblimit = setup_audiocheblimit ();
+ /* Set to lowpass */
+ g_object_set (G_OBJECT (audiocheblimit), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "type", 1, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "ripple", 0.25, NULL);
+
+ fail_unless (gst_element_set_state (audiocheblimit,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiocheblimit), "cutoff", 44100 / 4.0, NULL);
+ inbuffer = gst_buffer_new_allocate (NULL, 128 * sizeof (gfloat), NULL);
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 128; i += 2) {
+ in[i] = 1.0;
+ in[i + 1] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiocheblimit, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gfloat *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 128; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 128.0);
+ fail_unless (rms <= 0.1);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiocheblimit (audiocheblimit);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at 0 is erased with highpass mode and a cutoff
+ * at rate/4 */
+GST_START_TEST (test_type1_32_hp_0hz)
+{
+ GstElement *audiocheblimit;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiocheblimit = setup_audiocheblimit ();
+ /* Set to highpass */
+ g_object_set (G_OBJECT (audiocheblimit), "mode", 1, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "type", 1, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "ripple", 0.25, NULL);
+
+ fail_unless (gst_element_set_state (audiocheblimit,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiocheblimit), "cutoff", 44100 / 4.0, NULL);
+ inbuffer = gst_buffer_new_allocate (NULL, 128 * sizeof (gfloat), NULL);
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 128; i++)
+ in[i] = 1.0;
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiocheblimit, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gfloat *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 128; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 128.0);
+ fail_unless (rms <= 0.1);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiocheblimit (audiocheblimit);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at rate/2 is preserved with highpass mode and a cutoff
+ * at rate/4 */
+GST_START_TEST (test_type1_32_hp_22050hz)
+{
+ GstElement *audiocheblimit;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiocheblimit = setup_audiocheblimit ();
+ /* Set to highpass */
+ g_object_set (G_OBJECT (audiocheblimit), "mode", 1, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "type", 1, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "ripple", 0.25, NULL);
+
+ fail_unless (gst_element_set_state (audiocheblimit,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiocheblimit), "cutoff", 44100 / 4.0, NULL);
+ inbuffer = gst_buffer_new_allocate (NULL, 128 * sizeof (gfloat), NULL);
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 128; i += 2) {
+ in[i] = 1.0;
+ in[i + 1] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiocheblimit, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gfloat *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 128; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 128.0);
+ fail_unless (rms >= 0.9);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiocheblimit (audiocheblimit);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at 0 is preserved with lowpass mode and a cutoff
+ * at rate/4 */
+GST_START_TEST (test_type1_64_lp_0hz)
+{
+ GstElement *audiocheblimit;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiocheblimit = setup_audiocheblimit ();
+ /* Set to lowpass */
+ g_object_set (G_OBJECT (audiocheblimit), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "type", 1, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "ripple", 0.25, NULL);
+
+ fail_unless (gst_element_set_state (audiocheblimit,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiocheblimit), "cutoff", 44100 / 4.0, NULL);
+ inbuffer = gst_buffer_new_allocate (NULL, 128 * sizeof (gdouble), NULL);
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 128; i++)
+ in[i] = 1.0;
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiocheblimit, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 128; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 128.0);
+ fail_unless (rms >= 0.9);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiocheblimit (audiocheblimit);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at rate/2 is erased with lowpass mode and a cutoff
+ * at rate/4 */
+GST_START_TEST (test_type1_64_lp_22050hz)
+{
+ GstElement *audiocheblimit;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiocheblimit = setup_audiocheblimit ();
+ /* Set to lowpass */
+ g_object_set (G_OBJECT (audiocheblimit), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "type", 1, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "ripple", 0.25, NULL);
+
+ fail_unless (gst_element_set_state (audiocheblimit,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiocheblimit), "cutoff", 44100 / 4.0, NULL);
+ inbuffer = gst_buffer_new_allocate (NULL, 128 * sizeof (gdouble), NULL);
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 128; i += 2) {
+ in[i] = 1.0;
+ in[i + 1] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiocheblimit, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 128; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 128.0);
+ fail_unless (rms <= 0.1);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiocheblimit (audiocheblimit);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at 0 is erased with highpass mode and a cutoff
+ * at rate/4 */
+GST_START_TEST (test_type1_64_hp_0hz)
+{
+ GstElement *audiocheblimit;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiocheblimit = setup_audiocheblimit ();
+ /* Set to highpass */
+ g_object_set (G_OBJECT (audiocheblimit), "mode", 1, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "type", 1, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "ripple", 0.25, NULL);
+
+ fail_unless (gst_element_set_state (audiocheblimit,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiocheblimit), "cutoff", 44100 / 4.0, NULL);
+ inbuffer = gst_buffer_new_allocate (NULL, 128 * sizeof (gdouble), NULL);
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 128; i++)
+ in[i] = 1.0;
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiocheblimit, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 128; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 128.0);
+ fail_unless (rms <= 0.1);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiocheblimit (audiocheblimit);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at rate/2 is preserved with highpass mode and a cutoff
+ * at rate/4 */
+GST_START_TEST (test_type1_64_hp_22050hz)
+{
+ GstElement *audiocheblimit;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiocheblimit = setup_audiocheblimit ();
+ /* Set to highpass */
+ g_object_set (G_OBJECT (audiocheblimit), "mode", 1, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "type", 1, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "ripple", 0.25, NULL);
+
+ fail_unless (gst_element_set_state (audiocheblimit,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiocheblimit), "cutoff", 44100 / 4.0, NULL);
+ inbuffer = gst_buffer_new_allocate (NULL, 128 * sizeof (gdouble), NULL);
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 128; i += 2) {
+ in[i] = 1.0;
+ in[i + 1] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiocheblimit, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 128; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 128.0);
+ fail_unless (rms >= 0.9);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiocheblimit (audiocheblimit);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at 0 is preserved with lowpass mode and a cutoff
+ * at rate/4 */
+GST_START_TEST (test_type2_32_lp_0hz)
+{
+ GstElement *audiocheblimit;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiocheblimit = setup_audiocheblimit ();
+ /* Set to lowpass */
+ g_object_set (G_OBJECT (audiocheblimit), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "type", 2, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "ripple", 40.0, NULL);
+
+ fail_unless (gst_element_set_state (audiocheblimit,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiocheblimit), "cutoff", 44100 / 4.0, NULL);
+ inbuffer = gst_buffer_new_allocate (NULL, 128 * sizeof (gfloat), NULL);
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 128; i++)
+ in[i] = 1.0;
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiocheblimit, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gfloat *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 128; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 128.0);
+ fail_unless (rms >= 0.9);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiocheblimit (audiocheblimit);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at rate/2 is erased with lowpass mode and a cutoff
+ * at rate/4 */
+GST_START_TEST (test_type2_32_lp_22050hz)
+{
+ GstElement *audiocheblimit;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiocheblimit = setup_audiocheblimit ();
+ /* Set to lowpass */
+ g_object_set (G_OBJECT (audiocheblimit), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "type", 2, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "ripple", 40.0, NULL);
+
+ fail_unless (gst_element_set_state (audiocheblimit,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiocheblimit), "cutoff", 44100 / 4.0, NULL);
+ inbuffer = gst_buffer_new_allocate (NULL, 128 * sizeof (gfloat), NULL);
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 128; i += 2) {
+ in[i] = 1.0;
+ in[i + 1] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiocheblimit, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gfloat *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 128; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 128.0);
+ fail_unless (rms <= 0.1);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiocheblimit (audiocheblimit);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at 0 is erased with highpass mode and a cutoff
+ * at rate/4 */
+GST_START_TEST (test_type2_32_hp_0hz)
+{
+ GstElement *audiocheblimit;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiocheblimit = setup_audiocheblimit ();
+ /* Set to highpass */
+ g_object_set (G_OBJECT (audiocheblimit), "mode", 1, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "type", 2, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "ripple", 40.0, NULL);
+
+ fail_unless (gst_element_set_state (audiocheblimit,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiocheblimit), "cutoff", 44100 / 4.0, NULL);
+ inbuffer = gst_buffer_new_allocate (NULL, 128 * sizeof (gfloat), NULL);
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 128; i++)
+ in[i] = 1.0;
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiocheblimit, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gfloat *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 128; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 128.0);
+ fail_unless (rms <= 0.1);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiocheblimit (audiocheblimit);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at rate/2 is preserved with highpass mode and a cutoff
+ * at rate/4 */
+GST_START_TEST (test_type2_32_hp_22050hz)
+{
+ GstElement *audiocheblimit;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiocheblimit = setup_audiocheblimit ();
+ /* Set to highpass */
+ g_object_set (G_OBJECT (audiocheblimit), "mode", 1, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "type", 2, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "ripple", 40.0, NULL);
+
+ fail_unless (gst_element_set_state (audiocheblimit,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiocheblimit), "cutoff", 44100 / 4.0, NULL);
+ inbuffer = gst_buffer_new_allocate (NULL, 128 * sizeof (gfloat), NULL);
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 128; i += 2) {
+ in[i] = 1.0;
+ in[i + 1] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiocheblimit, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gfloat *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 128; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 128.0);
+ fail_unless (rms >= 0.9);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiocheblimit (audiocheblimit);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at 0 is preserved with lowpass mode and a cutoff
+ * at rate/4 */
+GST_START_TEST (test_type2_64_lp_0hz)
+{
+ GstElement *audiocheblimit;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiocheblimit = setup_audiocheblimit ();
+ /* Set to lowpass */
+ g_object_set (G_OBJECT (audiocheblimit), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "type", 2, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "ripple", 40.0, NULL);
+
+ fail_unless (gst_element_set_state (audiocheblimit,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiocheblimit), "cutoff", 44100 / 4.0, NULL);
+ inbuffer = gst_buffer_new_and_alloc (128 * sizeof (gdouble));
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 128; i++)
+ in[i] = 1.0;
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiocheblimit, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 128; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 128.0);
+ fail_unless (rms >= 0.9);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiocheblimit (audiocheblimit);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at rate/2 is erased with lowpass mode and a cutoff
+ * at rate/4 */
+GST_START_TEST (test_type2_64_lp_22050hz)
+{
+ GstElement *audiocheblimit;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiocheblimit = setup_audiocheblimit ();
+ /* Set to lowpass */
+ g_object_set (G_OBJECT (audiocheblimit), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "type", 2, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "ripple", 40.0, NULL);
+
+ fail_unless (gst_element_set_state (audiocheblimit,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiocheblimit), "cutoff", 44100 / 4.0, NULL);
+ inbuffer = gst_buffer_new_and_alloc (128 * sizeof (gdouble));
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 128; i += 2) {
+ in[i] = 1.0;
+ in[i + 1] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiocheblimit, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 128; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 128.0);
+ fail_unless (rms <= 0.1);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiocheblimit (audiocheblimit);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at 0 is erased with highpass mode and a cutoff
+ * at rate/4 */
+GST_START_TEST (test_type2_64_hp_0hz)
+{
+ GstElement *audiocheblimit;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiocheblimit = setup_audiocheblimit ();
+ /* Set to highpass */
+ g_object_set (G_OBJECT (audiocheblimit), "mode", 1, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "type", 2, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "ripple", 40.0, NULL);
+
+ fail_unless (gst_element_set_state (audiocheblimit,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiocheblimit), "cutoff", 44100 / 4.0, NULL);
+ inbuffer = gst_buffer_new_and_alloc (128 * sizeof (gdouble));
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 128; i++)
+ in[i] = 1.0;
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiocheblimit, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 128; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 128.0);
+ fail_unless (rms <= 0.1);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiocheblimit (audiocheblimit);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at rate/2 is preserved with highpass mode and a cutoff
+ * at rate/4 */
+GST_START_TEST (test_type2_64_hp_22050hz)
+{
+ GstElement *audiocheblimit;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+
+ audiocheblimit = setup_audiocheblimit ();
+ /* Set to highpass */
+ g_object_set (G_OBJECT (audiocheblimit), "mode", 1, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "poles", 8, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "type", 2, NULL);
+ g_object_set (G_OBJECT (audiocheblimit), "ripple", 40.0, NULL);
+
+ fail_unless (gst_element_set_state (audiocheblimit,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiocheblimit), "cutoff", 44100 / 4.0, NULL);
+ inbuffer = gst_buffer_new_and_alloc (128 * sizeof (gdouble));
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 128; i += 2) {
+ in[i] = 1.0;
+ in[i + 1] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (BUFFER_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiocheblimit, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+
+ rms = 0.0;
+ for (i = 0; i < 128; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / 128.0);
+ fail_unless (rms >= 0.9);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ /* cleanup */
+ cleanup_audiocheblimit (audiocheblimit);
+}
+
+GST_END_TEST;
+
+
+static Suite *
+audiocheblimit_suite (void)
+{
+ Suite *s = suite_create ("audiocheblimit");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_type1_32_lp_0hz);
+ tcase_add_test (tc_chain, test_type1_32_lp_22050hz);
+ tcase_add_test (tc_chain, test_type1_32_hp_0hz);
+ tcase_add_test (tc_chain, test_type1_32_hp_22050hz);
+ tcase_add_test (tc_chain, test_type1_64_lp_0hz);
+ tcase_add_test (tc_chain, test_type1_64_lp_22050hz);
+ tcase_add_test (tc_chain, test_type1_64_hp_0hz);
+ tcase_add_test (tc_chain, test_type1_64_hp_22050hz);
+ tcase_add_test (tc_chain, test_type2_32_lp_0hz);
+ tcase_add_test (tc_chain, test_type2_32_lp_22050hz);
+ tcase_add_test (tc_chain, test_type2_32_hp_0hz);
+ tcase_add_test (tc_chain, test_type2_32_hp_22050hz);
+ tcase_add_test (tc_chain, test_type2_64_lp_0hz);
+ tcase_add_test (tc_chain, test_type2_64_lp_22050hz);
+ tcase_add_test (tc_chain, test_type2_64_hp_0hz);
+ tcase_add_test (tc_chain, test_type2_64_hp_22050hz);
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ int nf;
+
+ Suite *s = audiocheblimit_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ srunner_run_all (sr, CK_NORMAL);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}
diff --git a/tests/check/elements/audiodynamic.c b/tests/check/elements/audiodynamic.c
new file mode 100644
index 0000000..779f8e9
--- /dev/null
+++ b/tests/check/elements/audiodynamic.c
@@ -0,0 +1,457 @@
+/* GStreamer
+ *
+ * unit test for audiodynamic
+ *
+ * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
+ *
+ * Greatly based on the audiopanorama unit test
+ * Copyright (C) 2006 Stefan Kost <ensonic@users.sf.net>
+ *
+ * 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.
+ */
+
+#include <unistd.h>
+
+#include <gst/audio/audio.h>
+#include <gst/base/gstbasetransform.h>
+#include <gst/check/gstcheck.h>
+
+gboolean have_eos = FALSE;
+
+/* 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 */
+GstPad *mysrcpad, *mysinkpad;
+
+
+#define DYNAMIC_CAPS_STRING \
+ "audio/x-raw, " \
+ "channels = (int) 1, " \
+ "rate = (int) 44100, " \
+ "layout = (string) interleaved, " \
+ "format = (string) " GST_AUDIO_NE(S16)
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "channels = (int) 1, "
+ "rate = (int) [ 1, MAX ], "
+ "layout = (string) interleaved, "
+ "format = (string)" GST_AUDIO_NE (S16)));
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "channels = (int) 1, "
+ "rate = (int) [ 1, MAX ], "
+ "layout = (string) interleaved, "
+ "format = (string) " GST_AUDIO_NE (S16)));
+
+static GstElement *
+setup_dynamic (void)
+{
+ GstElement *dynamic;
+
+ GST_DEBUG ("setup_dynamic");
+ dynamic = gst_check_setup_element ("audiodynamic");
+ mysrcpad = gst_check_setup_src_pad (dynamic, &srctemplate);
+ mysinkpad = gst_check_setup_sink_pad (dynamic, &sinktemplate);
+ gst_pad_set_active (mysrcpad, TRUE);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ return dynamic;
+}
+
+static void
+cleanup_dynamic (GstElement * dynamic)
+{
+ GST_DEBUG ("cleanup_dynamic");
+
+ g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
+ g_list_free (buffers);
+ buffers = NULL;
+
+ gst_pad_set_active (mysrcpad, FALSE);
+ gst_pad_set_active (mysinkpad, FALSE);
+ gst_check_teardown_src_pad (dynamic);
+ gst_check_teardown_sink_pad (dynamic);
+ gst_check_teardown_element (dynamic);
+}
+
+GST_START_TEST (test_passthrough)
+{
+ GstElement *dynamic;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gint16 in[6] = { 24576, -16384, 256, -128, 0, -24576 };
+ gint16 res[6];
+
+ dynamic = setup_dynamic ();
+ fail_unless (gst_element_set_state (dynamic,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ inbuffer =
+ gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, in, 12, 0, 12,
+ NULL, NULL);
+ fail_unless (gst_buffer_memcmp (inbuffer, 0, in, 12) == 0);
+ caps = gst_caps_from_string (DYNAMIC_CAPS_STRING);
+ gst_check_setup_events (mysrcpad, dynamic, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... but it ends up being collected on the global buffer list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ fail_unless (gst_buffer_extract (outbuffer, 0, res, 12) == 12);
+ GST_INFO
+ ("expected %+5d %+5d %+5d %+5d %+5d %+5d real %+5d %+5d %+5d %+5d %+5d %+5d",
+ in[0], in[1], in[2], in[3], in[4], in[5], res[0], res[1], res[2], res[3],
+ res[4], res[5]);
+ fail_unless (gst_buffer_memcmp (outbuffer, 0, in, 12) == 0);
+
+ /* cleanup */
+ cleanup_dynamic (dynamic);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_compress_hard_50_50)
+{
+ GstElement *dynamic;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gint16 in[8] = { -30000, 24576, -16384, 256, -128, 0, -24576, 30000 };
+ gint16 res[8];
+
+ dynamic = setup_dynamic ();
+ g_object_set (G_OBJECT (dynamic), "mode", 0, NULL);
+ g_object_set (G_OBJECT (dynamic), "characteristics", 0, NULL);
+ g_object_set (G_OBJECT (dynamic), "ratio", 0.5, NULL);
+ g_object_set (G_OBJECT (dynamic), "threshold", 0.5, NULL);
+ fail_unless (gst_element_set_state (dynamic,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ inbuffer = gst_buffer_new_and_alloc (16);
+ gst_buffer_fill (inbuffer, 0, in, 16);
+ fail_unless (gst_buffer_memcmp (inbuffer, 0, in, 16) == 0);
+ caps = gst_caps_from_string (DYNAMIC_CAPS_STRING);
+ gst_check_setup_events (mysrcpad, dynamic, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ fail_unless (gst_buffer_extract (outbuffer, 0, res, 16) == 16);
+
+ fail_unless (res[0] > in[0]);
+ fail_unless (res[1] < in[1]);
+ fail_unless (res[2] == in[2]);
+ fail_unless (res[3] == in[3]);
+ fail_unless (res[4] == in[4]);
+ fail_unless (res[5] == in[5]);
+ fail_unless (res[6] > in[6]);
+ fail_unless (res[7] < in[7]);
+
+ /* cleanup */
+ cleanup_dynamic (dynamic);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_compress_soft_50_50)
+{
+ GstElement *dynamic;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gint16 in[8] = { -30000, 24576, -16384, 256, -128, 0, -24576, 30000 };
+ gint16 res[8];
+
+ dynamic = setup_dynamic ();
+ g_object_set (G_OBJECT (dynamic), "mode", 0, NULL);
+ g_object_set (G_OBJECT (dynamic), "characteristics", 1, NULL);
+ g_object_set (G_OBJECT (dynamic), "ratio", 0.5, NULL);
+ g_object_set (G_OBJECT (dynamic), "threshold", 0.5, NULL);
+ fail_unless (gst_element_set_state (dynamic,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ inbuffer = gst_buffer_new_and_alloc (16);
+ gst_buffer_fill (inbuffer, 0, in, 16);
+ fail_unless (gst_buffer_memcmp (inbuffer, 0, in, 16) == 0);
+ caps = gst_caps_from_string (DYNAMIC_CAPS_STRING);
+ gst_check_setup_events (mysrcpad, dynamic, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ fail_unless (gst_buffer_extract (outbuffer, 0, res, 16) == 16);
+
+ fail_unless (res[0] > in[0]);
+ fail_unless (res[1] < in[1]);
+ fail_unless (res[2] == in[2]);
+ fail_unless (res[3] == in[3]);
+ fail_unless (res[4] == in[4]);
+ fail_unless (res[5] == in[5]);
+ fail_unless (res[6] > in[6]);
+ fail_unless (res[7] < in[7]);
+
+ /* cleanup */
+ cleanup_dynamic (dynamic);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_compress_hard_100_50)
+{
+ GstElement *dynamic;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gint16 in[8] = { -30000, 24576, -16384, 256, -128, 0, -24576, 30000 };
+ gint16 res[8];
+
+ dynamic = setup_dynamic ();
+ g_object_set (G_OBJECT (dynamic), "mode", 0, NULL);
+ g_object_set (G_OBJECT (dynamic), "characteristics", 0, NULL);
+ g_object_set (G_OBJECT (dynamic), "ratio", 0.5, NULL);
+ g_object_set (G_OBJECT (dynamic), "threshold", 1.0, NULL);
+ fail_unless (gst_element_set_state (dynamic,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ inbuffer = gst_buffer_new_and_alloc (16);
+ gst_buffer_fill (inbuffer, 0, in, 16);
+ fail_unless (gst_buffer_memcmp (inbuffer, 0, in, 16) == 0);
+ caps = gst_caps_from_string (DYNAMIC_CAPS_STRING);
+ gst_check_setup_events (mysrcpad, dynamic, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ fail_unless (gst_buffer_extract (outbuffer, 0, res, 16) == 16);
+
+ fail_unless (res[0] == in[0]);
+ fail_unless (res[1] == in[1]);
+ fail_unless (res[2] == in[2]);
+ fail_unless (res[3] == in[3]);
+ fail_unless (res[4] == in[4]);
+ fail_unless (res[5] == in[5]);
+ fail_unless (res[6] == in[6]);
+ fail_unless (res[7] == in[7]);
+
+ /* cleanup */
+ cleanup_dynamic (dynamic);
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_expand_hard_50_200)
+{
+ GstElement *dynamic;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gint16 in[8] = { -30000, 24576, -16383, 256, -128, 0, -24576, 30000 };
+ gint16 res[8];
+
+ dynamic = setup_dynamic ();
+ g_object_set (G_OBJECT (dynamic), "mode", 1, NULL);
+ g_object_set (G_OBJECT (dynamic), "characteristics", 0, NULL);
+ g_object_set (G_OBJECT (dynamic), "ratio", 2.0, NULL);
+ g_object_set (G_OBJECT (dynamic), "threshold", 0.5, NULL);
+ fail_unless (gst_element_set_state (dynamic,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ inbuffer = gst_buffer_new_and_alloc (16);
+ gst_buffer_fill (inbuffer, 0, in, 16);
+ fail_unless (gst_buffer_memcmp (inbuffer, 0, in, 16) == 0);
+ caps = gst_caps_from_string (DYNAMIC_CAPS_STRING);
+ gst_check_setup_events (mysrcpad, dynamic, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ fail_unless (gst_buffer_extract (outbuffer, 0, res, 16) == 16);
+
+ fail_unless (res[0] == in[0]);
+ fail_unless (res[1] == in[1]);
+ fail_unless (res[2] > in[2]);
+ fail_unless (res[3] < in[3]);
+ fail_unless (res[4] > in[4]);
+ fail_unless (res[5] == in[5]);
+ fail_unless (res[6] == in[6]);
+ fail_unless (res[7] == in[7]);
+
+ /* cleanup */
+ cleanup_dynamic (dynamic);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_expand_soft_50_200)
+{
+ GstElement *dynamic;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gint16 in[8] = { -30000, 24576, -16383, 256, -128, 0, -24576, 30000 };
+ gint16 res[8];
+
+ dynamic = setup_dynamic ();
+ g_object_set (G_OBJECT (dynamic), "mode", 1, NULL);
+ g_object_set (G_OBJECT (dynamic), "characteristics", 1, NULL);
+ g_object_set (G_OBJECT (dynamic), "ratio", 2.0, NULL);
+ g_object_set (G_OBJECT (dynamic), "threshold", 0.5, NULL);
+ fail_unless (gst_element_set_state (dynamic,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ inbuffer = gst_buffer_new_and_alloc (16);
+ gst_buffer_fill (inbuffer, 0, in, 16);
+ fail_unless (gst_buffer_memcmp (inbuffer, 0, in, 16) == 0);
+ caps = gst_caps_from_string (DYNAMIC_CAPS_STRING);
+ gst_check_setup_events (mysrcpad, dynamic, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ fail_unless (gst_buffer_extract (outbuffer, 0, res, 16) == 16);
+
+ fail_unless (res[0] == in[0]);
+ fail_unless (res[1] == in[1]);
+ fail_unless (res[2] > in[2]);
+ fail_unless (res[3] < in[3]);
+ fail_unless (res[4] > in[4]);
+ fail_unless (res[5] == in[5]);
+ fail_unless (res[6] == in[6]);
+ fail_unless (res[7] == in[7]);
+
+ /* cleanup */
+ cleanup_dynamic (dynamic);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_expand_hard_0_200)
+{
+ GstElement *dynamic;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gint16 in[8] = { -30000, 24576, -16383, 256, -128, 0, -24576, 30000 };
+ gint16 res[8];
+
+ dynamic = setup_dynamic ();
+ g_object_set (G_OBJECT (dynamic), "mode", 1, NULL);
+ g_object_set (G_OBJECT (dynamic), "characteristics", 0, NULL);
+ g_object_set (G_OBJECT (dynamic), "ratio", 2.0, NULL);
+ g_object_set (G_OBJECT (dynamic), "threshold", 0.0, NULL);
+ fail_unless (gst_element_set_state (dynamic,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ inbuffer = gst_buffer_new_and_alloc (16);
+ gst_buffer_fill (inbuffer, 0, in, 16);
+ fail_unless (gst_buffer_memcmp (inbuffer, 0, in, 16) == 0);
+ caps = gst_caps_from_string (DYNAMIC_CAPS_STRING);
+ gst_check_setup_events (mysrcpad, dynamic, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ fail_unless (gst_buffer_extract (outbuffer, 0, res, 16) == 16);
+
+ fail_unless (res[0] == in[0]);
+ fail_unless (res[1] == in[1]);
+ fail_unless (res[2] == in[2]);
+ fail_unless (res[3] == in[3]);
+ fail_unless (res[4] == in[4]);
+ fail_unless (res[5] == in[5]);
+ fail_unless (res[6] == in[6]);
+ fail_unless (res[7] == in[7]);
+
+ /* cleanup */
+ cleanup_dynamic (dynamic);
+}
+
+GST_END_TEST;
+
+static Suite *
+dynamic_suite (void)
+{
+ Suite *s = suite_create ("dynamic");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_passthrough);
+ tcase_add_test (tc_chain, test_compress_hard_50_50);
+ tcase_add_test (tc_chain, test_compress_soft_50_50);
+ tcase_add_test (tc_chain, test_compress_hard_100_50);
+ tcase_add_test (tc_chain, test_expand_hard_50_200);
+ tcase_add_test (tc_chain, test_expand_soft_50_200);
+ tcase_add_test (tc_chain, test_expand_hard_0_200);
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ int nf;
+
+ Suite *s = dynamic_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ srunner_run_all (sr, CK_NORMAL);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}
diff --git a/tests/check/elements/audioecho.c b/tests/check/elements/audioecho.c
new file mode 100644
index 0000000..025b710
--- /dev/null
+++ b/tests/check/elements/audioecho.c
@@ -0,0 +1,240 @@
+/* GStreamer
+ *
+ * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ *
+ * 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.
+ */
+
+#include <gst/check/gstcheck.h>
+#include <gst/audio/audio.h>
+
+gboolean have_eos = FALSE;
+
+/* 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 */
+GstPad *mysrcpad, *mysinkpad;
+
+#define ECHO_CAPS_STRING \
+ "audio/x-raw, " \
+ "channels = (int) 2, " \
+ "channel-mask = (bitmask) 3, " \
+ "rate = (int) 100000, " \
+ "layout = (string) interleaved, " \
+ "format = (string) " GST_AUDIO_NE(F64)
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "channels = (int) [ 1, 2 ], "
+ "rate = (int) [ 1, MAX ], "
+ "format = (string) { "
+ GST_AUDIO_NE (F32) ", " GST_AUDIO_NE (F64) " }"));
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "channels = (int) [ 1, 2 ], "
+ "rate = (int) [ 1, MAX ], "
+ "format = (string) { "
+ GST_AUDIO_NE (F32) ", " GST_AUDIO_NE (F64) " }"));
+
+static GstElement *
+setup_echo (void)
+{
+ GstElement *echo;
+
+ GST_DEBUG ("setup_echo");
+ echo = gst_check_setup_element ("audioecho");
+ mysrcpad = gst_check_setup_src_pad (echo, &srctemplate);
+ mysinkpad = gst_check_setup_sink_pad (echo, &sinktemplate);
+ gst_pad_set_active (mysrcpad, TRUE);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ return echo;
+}
+
+static void
+cleanup_echo (GstElement * echo)
+{
+ GST_DEBUG ("cleanup_echo");
+
+ g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
+ g_list_free (buffers);
+ buffers = NULL;
+
+ gst_pad_set_active (mysrcpad, FALSE);
+ gst_pad_set_active (mysinkpad, FALSE);
+ gst_check_teardown_src_pad (echo);
+ gst_check_teardown_sink_pad (echo);
+ gst_check_teardown_element (echo);
+}
+
+GST_START_TEST (test_passthrough)
+{
+ GstElement *echo;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble in[] = { 1.0, -1.0, 0.0, 0.5, -0.5, 0.0 };
+ gdouble res[6];
+
+ echo = setup_echo ();
+ g_object_set (G_OBJECT (echo), "delay", (GstClockTime) 1, "intensity", 0.0,
+ "feedback", 0.0, NULL);
+ fail_unless (gst_element_set_state (echo,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ caps = gst_caps_from_string (ECHO_CAPS_STRING);
+ gst_check_setup_events (mysrcpad, echo, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+
+ inbuffer =
+ gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, in, sizeof (in), 0,
+ sizeof (in), NULL, NULL);
+ fail_unless (gst_buffer_memcmp (inbuffer, 0, in, sizeof (in)) == 0);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... but it ends up being collected on the global buffer list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ fail_unless (gst_buffer_extract (outbuffer, 0, res,
+ sizeof (res)) == sizeof (res));
+ GST_INFO
+ ("expected %+lf %+lf %+lf %+lf %+lf %+lf real %+lf %+lf %+lf %+lf %+lf %+lf",
+ in[0], in[1], in[2], in[3], in[4], in[5], res[0], res[1], res[2], res[3],
+ res[4], res[5]);
+ fail_unless (gst_buffer_memcmp (outbuffer, 0, in, sizeof (in)) == 0);
+
+ /* cleanup */
+ cleanup_echo (echo);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_echo)
+{
+ GstElement *echo;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble in[] = { 1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, };
+ gdouble out[] = { 1.0, -1.0, 0.0, 0.0, 1.0, -1.0, 0.0, 0.0, 0.0, 0.0 };
+ gdouble res[10];
+
+ echo = setup_echo ();
+ g_object_set (G_OBJECT (echo), "delay", (GstClockTime) 20000, "intensity",
+ 1.0, "feedback", 0.0, NULL);
+ fail_unless (gst_element_set_state (echo,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ caps = gst_caps_from_string (ECHO_CAPS_STRING);
+ gst_check_setup_events (mysrcpad, echo, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+
+ inbuffer =
+ gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, in, sizeof (in), 0,
+ sizeof (in), NULL, NULL);
+ fail_unless (gst_buffer_memcmp (inbuffer, 0, in, sizeof (in)) == 0);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... but it ends up being collected on the global buffer list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ fail_unless (gst_buffer_extract (outbuffer, 0, res,
+ sizeof (res)) == sizeof (res));
+ GST_INFO
+ ("expected %+lf %+lf %+lf %+lf %+lf %+lf %+lf %+lf %+lf %+lf real %+lf %+lf %+lf %+lf %+lf %+lf %+lf %+lf %+lf %+lf",
+ out[0], out[1], out[2], out[3], out[4], out[5], out[6], out[7], out[8],
+ out[9], res[0], res[1], res[2], res[3], res[4], res[5], res[6], res[7],
+ res[8], res[9]);
+ fail_unless (gst_buffer_memcmp (outbuffer, 0, out, sizeof (out)) == 0);
+
+ /* cleanup */
+ cleanup_echo (echo);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_feedback)
+{
+ GstElement *echo;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble in[] = { 1.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, };
+ gdouble out[] = { 1.0, -1.0, 0.0, 0.0, 1.0, -1.0, 0.0, 0.0, 1.0, -1.0 };
+ gdouble res[10];
+
+ echo = setup_echo ();
+ g_object_set (G_OBJECT (echo), "delay", (GstClockTime) 20000, "intensity",
+ 1.0, "feedback", 1.0, NULL);
+ fail_unless (gst_element_set_state (echo,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ caps = gst_caps_from_string (ECHO_CAPS_STRING);
+ gst_check_setup_events (mysrcpad, echo, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+
+ inbuffer =
+ gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, in, sizeof (in), 0,
+ sizeof (in), NULL, NULL);
+ fail_unless (gst_buffer_memcmp (inbuffer, 0, in, sizeof (in)) == 0);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... but it ends up being collected on the global buffer list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ fail_unless (gst_buffer_extract (outbuffer, 0, res,
+ sizeof (res)) == sizeof (res));
+ GST_INFO
+ ("expected %+lf %+lf %+lf %+lf %+lf %+lf %+lf %+lf %+lf %+lf real %+lf %+lf %+lf %+lf %+lf %+lf %+lf %+lf %+lf %+lf",
+ out[0], out[1], out[2], out[3], out[4], out[5], out[6], out[7], out[8],
+ out[9], res[0], res[1], res[2], res[3], res[4], res[5], res[6], res[7],
+ res[8], res[9]);
+ fail_unless (gst_buffer_memcmp (outbuffer, 0, out, sizeof (out)) == 0);
+
+ /* cleanup */
+ cleanup_echo (echo);
+}
+
+GST_END_TEST;
+
+static Suite *
+audioecho_suite (void)
+{
+ Suite *s = suite_create ("audioecho");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_passthrough);
+ tcase_add_test (tc_chain, test_echo);
+ tcase_add_test (tc_chain, test_feedback);
+
+ return s;
+}
+
+GST_CHECK_MAIN (audioecho);
diff --git a/tests/check/elements/audiofirfilter.c b/tests/check/elements/audiofirfilter.c
new file mode 100644
index 0000000..dd31e87
--- /dev/null
+++ b/tests/check/elements/audiofirfilter.c
@@ -0,0 +1,192 @@
+/* GStreamer
+ *
+ * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+/* FIXME 0.11: suppress warnings for deprecated API such as GValueArray
+ * with newer GLib versions (>= 2.31.0) */
+#define GLIB_DISABLE_DEPRECATION_WARNINGS
+
+#include <gst/gst.h>
+#include <gst/check/gstcheck.h>
+
+static gboolean have_eos = FALSE;
+
+static gboolean
+on_message (GstBus * bus, GstMessage * message, gpointer user_data)
+{
+ GMainLoop *loop = (GMainLoop *) user_data;
+
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_ERROR:
+ case GST_MESSAGE_WARNING:
+ g_assert_not_reached ();
+ g_main_loop_quit (loop);
+ break;
+
+ case GST_MESSAGE_EOS:
+ have_eos = TRUE;
+ g_main_loop_quit (loop);
+ break;
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+static void
+on_rate_changed (GstElement * element, gint rate, gpointer user_data)
+{
+ GValueArray *va;
+ GValue v = { 0, };
+
+ fail_unless (rate > 0);
+
+ va = g_value_array_new (6);
+
+ g_value_init (&v, G_TYPE_DOUBLE);
+ g_value_set_double (&v, 0.0);
+ g_value_array_append (va, &v);
+ g_value_reset (&v);
+ g_value_set_double (&v, 0.0);
+ g_value_array_append (va, &v);
+ g_value_reset (&v);
+ g_value_set_double (&v, 0.0);
+ g_value_array_append (va, &v);
+ g_value_reset (&v);
+ g_value_set_double (&v, 0.0);
+ g_value_array_append (va, &v);
+ g_value_reset (&v);
+ g_value_set_double (&v, 0.0);
+ g_value_array_append (va, &v);
+ g_value_reset (&v);
+ g_value_set_double (&v, 1.0);
+ g_value_array_append (va, &v);
+ g_value_reset (&v);
+
+ g_object_set (G_OBJECT (element), "kernel", va, NULL);
+
+ g_value_array_free (va);
+}
+
+static gboolean have_data = FALSE;
+
+static void
+on_handoff (GstElement * object, GstBuffer * buffer, GstPad * pad,
+ gpointer user_data)
+{
+ if (!have_data) {
+ GstMapInfo map;
+ gdouble *data;
+
+ gst_buffer_map (buffer, &map, GST_MAP_READ);
+ data = (gdouble *) map.data;
+
+ fail_unless (map.size > 5 * sizeof (gdouble));
+ fail_unless (data[0] == 0.0);
+ fail_unless (data[1] == 0.0);
+ fail_unless (data[2] == 0.0);
+ fail_unless (data[3] == 0.0);
+ fail_unless (data[4] == 0.0);
+ fail_unless (data[5] != 0.0);
+
+ gst_buffer_unmap (buffer, &map);
+ have_data = TRUE;
+ }
+}
+
+GST_START_TEST (test_pipeline)
+{
+ GstElement *pipeline, *src, *cfilter, *filter, *sink;
+ GstCaps *caps;
+ GstBus *bus;
+ GMainLoop *loop;
+
+ have_data = FALSE;
+ have_eos = FALSE;
+
+ pipeline = gst_element_factory_make ("pipeline", NULL);
+ fail_unless (pipeline != NULL);
+
+ src = gst_element_factory_make ("audiotestsrc", NULL);
+ fail_unless (src != NULL);
+ g_object_set (G_OBJECT (src), "num-buffers", 1000, NULL);
+
+ cfilter = gst_element_factory_make ("capsfilter", NULL);
+ fail_unless (cfilter != NULL);
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+ caps = gst_caps_new_simple ("audio/x-raw",
+ "format", G_TYPE_STRING, "F64BE", NULL);
+#else
+ caps = gst_caps_new_simple ("audio/x-raw",
+ "format", G_TYPE_STRING, "F64LE", NULL);
+#endif
+ g_object_set (G_OBJECT (cfilter), "caps", caps, NULL);
+ gst_caps_unref (caps);
+
+ filter = gst_element_factory_make ("audiofirfilter", NULL);
+ fail_unless (filter != NULL);
+ g_signal_connect (G_OBJECT (filter), "rate-changed",
+ G_CALLBACK (on_rate_changed), NULL);
+
+ sink = gst_element_factory_make ("fakesink", NULL);
+ fail_unless (sink != NULL);
+ g_object_set (G_OBJECT (sink), "signal-handoffs", TRUE, NULL);
+ g_signal_connect (G_OBJECT (sink), "handoff", G_CALLBACK (on_handoff), NULL);
+
+ gst_bin_add_many (GST_BIN (pipeline), src, cfilter, filter, sink, NULL);
+ fail_unless (gst_element_link_many (src, cfilter, filter, sink, NULL));
+
+ loop = g_main_loop_new (NULL, FALSE);
+
+ bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
+ gst_bus_add_signal_watch (bus);
+ g_signal_connect (G_OBJECT (bus), "message", G_CALLBACK (on_message), loop);
+ gst_object_unref (GST_OBJECT (bus));
+
+ fail_if (gst_element_set_state (pipeline,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
+
+ g_main_loop_run (loop);
+
+ fail_unless (have_data);
+ fail_unless (have_eos);
+
+ fail_unless (gst_element_set_state (pipeline,
+ GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS);
+
+ g_main_loop_unref (loop);
+ gst_object_unref (pipeline);
+}
+
+GST_END_TEST;
+
+static Suite *
+audiofirfilter_suite (void)
+{
+ Suite *s = suite_create ("audiofirfilter");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_pipeline);
+
+ return s;
+}
+
+GST_CHECK_MAIN (audiofirfilter);
diff --git a/tests/check/elements/audioiirfilter.c b/tests/check/elements/audioiirfilter.c
new file mode 100644
index 0000000..8faae79
--- /dev/null
+++ b/tests/check/elements/audioiirfilter.c
@@ -0,0 +1,189 @@
+/* GStreamer
+ *
+ * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+/* FIXME 0.11: suppress warnings for deprecated API such as GValueArray
+ * with newer GLib versions (>= 2.31.0) */
+#define GLIB_DISABLE_DEPRECATION_WARNINGS
+
+#include <gst/gst.h>
+#include <gst/check/gstcheck.h>
+
+static gboolean have_eos = FALSE;
+
+static gboolean
+on_message (GstBus * bus, GstMessage * message, gpointer user_data)
+{
+ GMainLoop *loop = (GMainLoop *) user_data;
+
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_ERROR:
+ case GST_MESSAGE_WARNING:
+ g_assert_not_reached ();
+ g_main_loop_quit (loop);
+ break;
+
+ case GST_MESSAGE_EOS:
+ have_eos = TRUE;
+ g_main_loop_quit (loop);
+ break;
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+static void
+on_rate_changed (GstElement * element, gint rate, gpointer user_data)
+{
+ GValueArray *va;
+ GValue v = { 0, };
+
+ fail_unless (rate > 0);
+
+ va = g_value_array_new (6);
+
+ g_value_init (&v, G_TYPE_DOUBLE);
+ g_value_set_double (&v, 0.0);
+ g_value_array_append (va, &v);
+ g_value_reset (&v);
+ g_value_set_double (&v, 0.0);
+ g_value_array_append (va, &v);
+ g_value_reset (&v);
+ g_value_set_double (&v, 0.0);
+ g_value_array_append (va, &v);
+ g_value_reset (&v);
+ g_value_set_double (&v, 0.0);
+ g_value_array_append (va, &v);
+ g_value_reset (&v);
+ g_value_set_double (&v, 0.0);
+ g_value_array_append (va, &v);
+ g_value_reset (&v);
+ g_value_set_double (&v, 1.0);
+ g_value_array_append (va, &v);
+ g_value_reset (&v);
+
+ g_object_set (G_OBJECT (element), "b", va, NULL);
+
+ g_value_array_free (va);
+
+ va = g_value_array_new (6);
+
+ g_value_set_double (&v, 1.0);
+ g_value_array_append (va, &v);
+ g_value_reset (&v);
+
+ g_object_set (G_OBJECT (element), "a", va, NULL);
+
+ g_value_array_free (va);
+}
+
+static gboolean have_data = FALSE;
+
+static void
+on_handoff (GstElement * object, GstBuffer * buffer, GstPad * pad,
+ gpointer user_data)
+{
+ if (!have_data) {
+ GstMapInfo map;
+ gfloat *data;
+
+ fail_unless (gst_buffer_map (buffer, &map, GST_MAP_READ));
+ data = (gfloat *) map.data;
+
+ fail_unless (map.size > 5 * sizeof (gdouble));
+ fail_unless (data[0] == 0.0);
+ fail_unless (data[1] == 0.0);
+ fail_unless (data[2] == 0.0);
+ fail_unless (data[3] == 0.0);
+ fail_unless (data[4] == 0.0);
+ fail_unless (data[5] != 0.0);
+
+ gst_buffer_unmap (buffer, &map);
+ have_data = TRUE;
+ }
+}
+
+GST_START_TEST (test_pipeline)
+{
+ GstElement *pipeline, *src, *filter, *sink;
+ GstBus *bus;
+ GMainLoop *loop;
+
+ have_data = FALSE;
+ have_eos = FALSE;
+
+ pipeline = gst_element_factory_make ("pipeline", NULL);
+ fail_unless (pipeline != NULL);
+
+ src = gst_element_factory_make ("audiotestsrc", NULL);
+ fail_unless (src != NULL);
+ g_object_set (G_OBJECT (src), "num-buffers", 1000, NULL);
+
+ filter = gst_element_factory_make ("audioiirfilter", NULL);
+ fail_unless (filter != NULL);
+ g_signal_connect (G_OBJECT (filter), "rate-changed",
+ G_CALLBACK (on_rate_changed), NULL);
+
+ sink = gst_element_factory_make ("fakesink", NULL);
+ fail_unless (sink != NULL);
+ g_object_set (G_OBJECT (sink), "signal-handoffs", TRUE, NULL);
+ g_signal_connect (G_OBJECT (sink), "handoff", G_CALLBACK (on_handoff), NULL);
+
+ gst_bin_add_many (GST_BIN (pipeline), src, filter, sink, NULL);
+ fail_unless (gst_element_link_many (src, filter, sink, NULL));
+
+ loop = g_main_loop_new (NULL, FALSE);
+
+ bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
+ gst_bus_add_signal_watch (bus);
+ g_signal_connect (G_OBJECT (bus), "message", G_CALLBACK (on_message), loop);
+ gst_object_unref (GST_OBJECT (bus));
+
+ fail_if (gst_element_set_state (pipeline,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
+
+ g_main_loop_run (loop);
+
+ fail_unless (have_data);
+ fail_unless (have_eos);
+
+ fail_unless (gst_element_set_state (pipeline,
+ GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS);
+
+ g_main_loop_unref (loop);
+ gst_object_unref (pipeline);
+}
+
+GST_END_TEST;
+
+static Suite *
+audioiirfilter_suite (void)
+{
+ Suite *s = suite_create ("audioiirfilter");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_pipeline);
+
+ return s;
+}
+
+GST_CHECK_MAIN (audioiirfilter);
diff --git a/tests/check/elements/audioinvert.c b/tests/check/elements/audioinvert.c
new file mode 100644
index 0000000..50981d7
--- /dev/null
+++ b/tests/check/elements/audioinvert.c
@@ -0,0 +1,299 @@
+/* GStreamer
+ *
+ * unit test for audioinvert
+ *
+ * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
+ *
+ * Greatly based on the audiopanorama unit test
+ * Copyright (C) 2006 Stefan Kost <ensonic@users.sf.net>
+ *
+ * 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.
+ */
+
+#include <unistd.h>
+
+#include <gst/audio/audio.h>
+#include <gst/base/gstbasetransform.h>
+#include <gst/check/gstcheck.h>
+
+gboolean have_eos = FALSE;
+
+/* 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 */
+GstPad *mysrcpad, *mysinkpad;
+
+
+#define INVERT_CAPS_STRING \
+ "audio/x-raw, " \
+ "format = (string) "GST_AUDIO_NE(S16)", " \
+ "layout = (string) interleaved, " \
+ "channels = (int) 1, " \
+ "rate = (int) 44100"
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "format = (string) " GST_AUDIO_NE (S16) ", "
+ "layout = (string) interleaved, "
+ "channels = (int) 1, " "rate = (int) [ 1, MAX ]")
+ );
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "format = (string) " GST_AUDIO_NE (S16) ", "
+ "layout = (string) interleaved, "
+ "channels = (int) 1, " "rate = (int) [ 1, MAX ]")
+ );
+
+static GstElement *
+setup_invert (void)
+{
+ GstElement *invert;
+
+ GST_DEBUG ("setup_invert");
+ invert = gst_check_setup_element ("audioinvert");
+ mysrcpad = gst_check_setup_src_pad (invert, &srctemplate);
+ mysinkpad = gst_check_setup_sink_pad (invert, &sinktemplate);
+ gst_pad_set_active (mysrcpad, TRUE);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ return invert;
+}
+
+static void
+cleanup_invert (GstElement * invert)
+{
+ GST_DEBUG ("cleanup_invert");
+
+ g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
+ g_list_free (buffers);
+ buffers = NULL;
+
+ gst_pad_set_active (mysrcpad, FALSE);
+ gst_pad_set_active (mysinkpad, FALSE);
+ gst_check_teardown_src_pad (invert);
+ gst_check_teardown_sink_pad (invert);
+ gst_check_teardown_element (invert);
+}
+
+GST_START_TEST (test_passthrough)
+{
+ GstElement *invert;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gint16 in[4] = { 16384, -256, 128, -512 };
+ gint16 *res;
+ GstMapInfo map;
+
+ invert = setup_invert ();
+ fail_unless (gst_element_set_state (invert,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ inbuffer = gst_buffer_new_and_alloc (8);
+ gst_buffer_fill (inbuffer, 0, in, 8);
+ fail_unless (gst_buffer_memcmp (inbuffer, 0, in, 8) == 0);
+ caps = gst_caps_from_string (INVERT_CAPS_STRING);
+ gst_check_setup_events (mysrcpad, invert, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... but it ends up being collected on the global buffer list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gint16 *) map.data;
+ GST_INFO ("expected %+5d %+5d %+5d %+5d real %+5d %+5d %+5d %+5d",
+ in[0], in[1], in[2], in[3], res[0], res[1], res[2], res[3]);
+ gst_buffer_unmap (outbuffer, &map);
+
+ fail_unless (gst_buffer_memcmp (outbuffer, 0, in, 8) == 0);
+
+ /* cleanup */
+ cleanup_invert (invert);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_zero)
+{
+ GstElement *invert;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gint16 in[4] = { 16384, -256, 128, -512 };
+ gint16 out[4] = { 0, 0, 0, 0 };
+ gint16 *res;
+ GstMapInfo map;
+
+ invert = setup_invert ();
+ g_object_set (G_OBJECT (invert), "degree", 0.5, NULL);
+ fail_unless (gst_element_set_state (invert,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ inbuffer = gst_buffer_new_and_alloc (8);
+ gst_buffer_fill (inbuffer, 0, in, 8);
+ fail_unless (gst_buffer_memcmp (inbuffer, 0, in, 8) == 0);
+ caps = gst_caps_from_string (INVERT_CAPS_STRING);
+ gst_check_setup_events (mysrcpad, invert, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gint16 *) map.data;
+ GST_INFO ("expected %+5d %+5d %+5d %+5d real %+5d %+5d %+5d %+5d",
+ out[0], out[1], out[2], out[3], res[0], res[1], res[2], res[3]);
+ gst_buffer_unmap (outbuffer, &map);
+
+ fail_unless (gst_buffer_memcmp (outbuffer, 0, out, 8) == 0);
+
+ /* cleanup */
+ cleanup_invert (invert);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_full_inverse)
+{
+ GstElement *invert;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gint16 in[4] = { 16384, -256, 128, -512 };
+ gint16 out[4] = { -16385, 255, -129, 511 };
+ gint16 *res;
+ GstMapInfo map;
+
+ invert = setup_invert ();
+ g_object_set (G_OBJECT (invert), "degree", 1.0, NULL);
+ fail_unless (gst_element_set_state (invert,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ inbuffer = gst_buffer_new_and_alloc (8);
+ gst_buffer_fill (inbuffer, 0, in, 8);
+ fail_unless (gst_buffer_memcmp (inbuffer, 0, in, 8) == 0);
+ caps = gst_caps_from_string (INVERT_CAPS_STRING);
+ gst_check_setup_events (mysrcpad, invert, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gint16 *) map.data;
+ GST_INFO ("expected %+5d %+5d %+5d %+5d real %+5d %+5d %+5d %+5d",
+ out[0], out[1], out[2], out[3], res[0], res[1], res[2], res[3]);
+ gst_buffer_unmap (outbuffer, &map);
+
+ fail_unless (gst_buffer_memcmp (outbuffer, 0, out, 8) == 0);
+
+ /* cleanup */
+ cleanup_invert (invert);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_25_inverse)
+{
+ GstElement *invert;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gint16 in[4] = { 16384, -256, 128, -512 };
+ gint16 out[4] = { 8191, -128, 63, -256 };
+ gint16 *res;
+ GstMapInfo map;
+
+ invert = setup_invert ();
+ g_object_set (G_OBJECT (invert), "degree", 0.25, NULL);
+ fail_unless (gst_element_set_state (invert,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ inbuffer = gst_buffer_new_and_alloc (8);
+ gst_buffer_fill (inbuffer, 0, in, 8);
+ fail_unless (gst_buffer_memcmp (inbuffer, 0, in, 8) == 0);
+ caps = gst_caps_from_string (INVERT_CAPS_STRING);
+ gst_check_setup_events (mysrcpad, invert, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... and puts a new buffer on the global list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gint16 *) map.data;
+ GST_INFO ("expected %+5d %+5d %+5d %+5d real %+5d %+5d %+5d %+5d",
+ out[0], out[1], out[2], out[3], res[0], res[1], res[2], res[3]);
+ gst_buffer_unmap (outbuffer, &map);
+
+ fail_unless (gst_buffer_memcmp (outbuffer, 0, out, 8) == 0);
+
+ /* cleanup */
+ cleanup_invert (invert);
+}
+
+GST_END_TEST;
+
+static Suite *
+invert_suite (void)
+{
+ Suite *s = suite_create ("invert");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_passthrough);
+ tcase_add_test (tc_chain, test_zero);
+ tcase_add_test (tc_chain, test_full_inverse);
+ tcase_add_test (tc_chain, test_25_inverse);
+
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ int nf;
+
+ Suite *s = invert_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ srunner_run_all (sr, CK_NORMAL);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}
diff --git a/tests/check/elements/audiopanorama.c b/tests/check/elements/audiopanorama.c
new file mode 100644
index 0000000..3857119
--- /dev/null
+++ b/tests/check/elements/audiopanorama.c
@@ -0,0 +1,818 @@
+/* GStreamer
+ *
+ * unit test for audiopanorama
+ *
+ * Copyright (C) 2006 Stefan Kost <ensonic@users.sf.net>
+ *
+ * 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.
+ */
+
+#include <unistd.h>
+
+#include <gst/audio/audio.h>
+#include <gst/base/gstbasetransform.h>
+#include <gst/check/gstcheck.h>
+
+gboolean have_eos = FALSE;
+
+/* 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 */
+GstPad *mysrcpad, *mysinkpad;
+
+
+#define PANORAMA_S16_MONO_CAPS_STRING \
+ "audio/x-raw, " \
+ "channels = (int) 1, " \
+ "rate = (int) 44100, " \
+ "layout = (string) interleaved, " \
+ "format = (string) " GST_AUDIO_NE(S16)
+
+#define PANORAMA_S16_STEREO_CAPS_STRING \
+ "audio/x-raw, " \
+ "channels = (int) 2, " \
+ "channel-mask = (bitmask) 3, " \
+ "rate = (int) 44100, " \
+ "layout = (string) interleaved, " \
+ "format = (string) " GST_AUDIO_NE(S16)
+
+#define PANORAMA_F32_MONO_CAPS_STRING \
+ "audio/x-raw, " \
+ "channels = (int) 1, " \
+ "rate = (int) 44100, " \
+ "layout = (string) interleaved, " \
+ "format = (string) " GST_AUDIO_NE(F32)
+
+#define PANORAMA_F32_STEREO_CAPS_STRING \
+ "audio/x-raw, " \
+ "channels = (int) 2, " \
+ "channel-mask = (bitmask) 3, " \
+ "rate = (int) 44100, " \
+ "layout = (string) interleaved, " \
+ "format = (string) " GST_AUDIO_NE(F32)
+
+#define PANORAMA_WRONG_CAPS_STRING \
+ "audio/x-raw, " \
+ "channels = (int) 5, " \
+ "rate = (int) 44100, " \
+ "layout = (string) interleaved, " \
+ "format = (string) " GST_AUDIO_NE(U16)
+
+
+static GstStaticPadTemplate sinktemplate[2] = {
+ GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "channels = (int) 2, "
+ "rate = (int) [ 1, MAX ], "
+ "layout = (string) interleaved, "
+ "format = (string) " GST_AUDIO_NE (S16))
+ ),
+ GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "channels = (int) 2, "
+ "rate = (int) [ 1, MAX ], "
+ "layout = (string) interleaved, "
+ "format = (string) " GST_AUDIO_NE (F32))
+ ),
+};
+
+static GstStaticPadTemplate msrctemplate[2] = {
+ GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "channels = (int) 1, "
+ "rate = (int) [ 1, MAX ], "
+ "layout = (string) interleaved, "
+ "format = (string) " GST_AUDIO_NE (S16))
+ ),
+ GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "channels = (int) 1, "
+ "rate = (int) [ 1, MAX ], "
+ "layout = (string) interleaved, "
+ "format = (string) " GST_AUDIO_NE (F32))
+ ),
+};
+
+static GstStaticPadTemplate ssrctemplate[2] = {
+ GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "channels = (int) 2, "
+ "rate = (int) [ 1, MAX ], "
+ "layout = (string) interleaved, "
+ "format = (string) " GST_AUDIO_NE (S16))
+ ),
+ GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "channels = (int) 2, "
+ "rate = (int) [ 1, MAX ], "
+ "layout = (string) interleaved, "
+ "format = (string) " GST_AUDIO_NE (F32))
+ ),
+};
+
+static GstElement *
+setup_panorama (GstStaticPadTemplate * srctemplate, gint fmt,
+ const gchar * caps_str)
+{
+ GstElement *panorama;
+
+ GST_DEBUG ("setup_panorama");
+ panorama = gst_check_setup_element ("audiopanorama");
+ mysrcpad = gst_check_setup_src_pad (panorama, &srctemplate[fmt]);
+ mysinkpad = gst_check_setup_sink_pad (panorama, &sinktemplate[fmt]);
+ gst_pad_set_active (mysrcpad, TRUE);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ if (caps_str) {
+ GstCaps *caps = gst_caps_from_string (caps_str);
+ gst_check_setup_events (mysrcpad, panorama, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ }
+ return panorama;
+}
+
+static GstElement *
+setup_panorama_s16_m (gint method, gfloat pan)
+{
+ GstElement *panorama;
+ panorama = setup_panorama (msrctemplate, 0, PANORAMA_S16_MONO_CAPS_STRING);
+ g_object_set (G_OBJECT (panorama), "method", method, "panorama", pan, NULL);
+ gst_element_set_state (panorama, GST_STATE_PLAYING);
+ GST_DEBUG ("panorama(mono) ready");
+
+ return panorama;
+}
+
+static GstElement *
+setup_panorama_f32_m (gint method, gfloat pan)
+{
+ GstElement *panorama;
+ panorama = setup_panorama (msrctemplate, 1, PANORAMA_F32_MONO_CAPS_STRING);
+ g_object_set (G_OBJECT (panorama), "method", method, "panorama", pan, NULL);
+ gst_element_set_state (panorama, GST_STATE_PLAYING);
+ GST_DEBUG ("panorama(mono) ready");
+
+ return panorama;
+}
+
+static GstElement *
+setup_panorama_s16_s (gint method, gfloat pan)
+{
+ GstElement *panorama;
+ panorama = setup_panorama (ssrctemplate, 0, PANORAMA_S16_STEREO_CAPS_STRING);
+ g_object_set (G_OBJECT (panorama), "method", method, "panorama", pan, NULL);
+ gst_element_set_state (panorama, GST_STATE_PLAYING);
+ GST_DEBUG ("panorama(stereo) ready");
+
+ return panorama;
+}
+
+static GstElement *
+setup_panorama_f32_s (gint method, gfloat pan)
+{
+ GstElement *panorama;
+ panorama = setup_panorama (ssrctemplate, 1, PANORAMA_F32_STEREO_CAPS_STRING);
+ g_object_set (G_OBJECT (panorama), "method", method, "panorama", pan, NULL);
+ gst_element_set_state (panorama, GST_STATE_PLAYING);
+ GST_DEBUG ("panorama(stereo) ready");
+
+ return panorama;
+}
+
+static void
+cleanup_panorama (GstElement * panorama)
+{
+ GST_DEBUG ("cleaning up");
+ gst_element_set_state (panorama, GST_STATE_NULL);
+
+ g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
+ g_list_free (buffers);
+ buffers = NULL;
+
+ gst_pad_set_active (mysrcpad, FALSE);
+ gst_pad_set_active (mysinkpad, FALSE);
+ gst_check_teardown_src_pad (panorama);
+ gst_check_teardown_sink_pad (panorama);
+ gst_check_teardown_element (panorama);
+}
+
+static GstBuffer *
+setup_buffer (gpointer data, gsize size)
+{
+ return gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY, data, size, 0,
+ size, NULL, NULL);
+}
+
+static void
+do_panorama (gpointer in_data, gsize in_size, gpointer out_data, gsize out_size)
+{
+ GstBuffer *in, *out;
+
+ in = setup_buffer (in_data, in_size);
+ fail_unless (gst_pad_push (mysrcpad, in) == GST_FLOW_OK);
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((out = (GstBuffer *) buffers->data) == NULL);
+ fail_unless (gst_buffer_extract (out, 0, out_data, out_size) == out_size);
+}
+
+/* the tests */
+
+GST_START_TEST (test_ref_counts)
+{
+ GstElement *panorama;
+ GstBuffer *inbuffer, *outbuffer;
+ gint16 in[2] = { 16384, -256 };
+
+ panorama = setup_panorama (msrctemplate, 0, PANORAMA_S16_MONO_CAPS_STRING);
+ fail_unless (gst_element_set_state (panorama,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ inbuffer = setup_buffer (in, sizeof (in));
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... but it ends up being collected on the global buffer list */
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+ fail_if (inbuffer == outbuffer);
+
+ /* cleanup */
+ fail_unless (gst_element_set_state (panorama,
+ GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS, "could not set to null");
+ ASSERT_OBJECT_REFCOUNT (panorama, "panorama", 1);
+ cleanup_panorama (panorama);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_wrong_caps)
+{
+ GstElement *panorama;
+ GstBuffer *inbuffer;
+ gint16 in[2] = { 16384, -256 };
+ GstBus *bus;
+ GstMessage *message;
+ GstCaps *caps;
+
+ panorama = setup_panorama (msrctemplate, 0, NULL);
+ bus = gst_bus_new ();
+ gst_element_set_state (panorama, GST_STATE_PLAYING);
+
+ inbuffer = setup_buffer (in, sizeof (in));
+ gst_buffer_ref (inbuffer);
+
+ /* set a bus here so we avoid getting state change messages */
+ gst_element_set_bus (panorama, bus);
+
+ caps = gst_caps_from_string (PANORAMA_WRONG_CAPS_STRING);
+ /* this actually succeeds, because the caps event is sticky */
+ gst_check_setup_events (mysrcpad, panorama, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+
+ /* pushing gives an error because it can't negotiate with wrong caps */
+ fail_unless_equals_int (gst_pad_push (mysrcpad, inbuffer),
+ GST_FLOW_NOT_NEGOTIATED);
+ /* ... and the buffer would have been lost if we didn't ref it ourselves */
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+ gst_buffer_unref (inbuffer);
+ fail_unless_equals_int (g_list_length (buffers), 0);
+
+ /* panorama_set_caps should not have been called since basetransform caught
+ * the negotiation problem */
+ fail_if ((message = gst_bus_pop (bus)) != NULL);
+
+ /* cleanup */
+ gst_element_set_bus (panorama, NULL);
+ gst_object_unref (GST_OBJECT (bus));
+ gst_element_set_state (panorama, GST_STATE_NULL);
+ cleanup_panorama (panorama);
+}
+
+GST_END_TEST;
+
+/* processing for method=psy */
+
+GST_START_TEST (test_s16_mono_middle)
+{
+ gint16 in[2] = { 16384, -256 };
+ gint16 out[4] = { 8192, 8192, -128, -128 };
+ gint16 res[4];
+ GstElement *panorama = setup_panorama_s16_m (0, 0.0);
+
+ do_panorama (in, sizeof (in), res, sizeof (res));
+
+ GST_INFO ("exp. %+5d %+5d %+5d %+5d", out[0], out[1], out[2], out[3]);
+ GST_INFO ("real %+5d %+5d %+5d %+5d", res[0], res[1], res[2], res[3]);
+ fail_unless (memcmp (res, out, sizeof (res)) == 0);
+
+ cleanup_panorama (panorama);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_s16_mono_left)
+{
+ gint16 in[2] = { 16384, -256 };
+ gint16 out[4] = { 16384, 0, -256, 0 };
+ gint16 res[4];
+ GstElement *panorama = setup_panorama_s16_m (0, -1.0);
+
+ do_panorama (in, sizeof (in), res, sizeof (res));
+
+ GST_INFO ("exp. %+5d %+5d %+5d %+5d", out[0], out[1], out[2], out[3]);
+ GST_INFO ("real %+5d %+5d %+5d %+5d", res[0], res[1], res[2], res[3]);
+ fail_unless (memcmp (res, out, sizeof (res)) == 0);
+
+ cleanup_panorama (panorama);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_s16_mono_right)
+{
+ gint16 in[2] = { 16384, -256 };
+ gint16 out[4] = { 0, 16384, 0, -256 };
+ gint16 res[4];
+ GstElement *panorama = setup_panorama_s16_m (0, 1.0);
+
+ do_panorama (in, sizeof (in), res, sizeof (res));
+
+ GST_INFO ("exp. %+5d %+5d %+5d %+5d", out[0], out[1], out[2], out[3]);
+ GST_INFO ("real %+5d %+5d %+5d %+5d", res[0], res[1], res[2], res[3]);
+ fail_unless (memcmp (res, out, sizeof (res)) == 0);
+
+ cleanup_panorama (panorama);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_s16_stereo_middle)
+{
+ gint16 in[4] = { 16384, -256, 8192, 128 };
+ gint16 res[4];
+ GstElement *panorama = setup_panorama_s16_s (0, 0.0);
+
+ do_panorama (in, sizeof (in), res, sizeof (res));
+
+ GST_INFO ("exp. %+5d %+5d %+5d %+5d", in[0], in[1], in[2], in[3]);
+ GST_INFO ("real %+5d %+5d %+5d %+5d", res[0], res[1], res[2], res[3]);
+ fail_unless (memcmp (res, in, sizeof (res)) == 0);
+
+ cleanup_panorama (panorama);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_s16_stereo_left)
+{
+ gint16 in[4] = { 16384, -256, 8192, 128 };
+ gint16 out[4] = { 16384 - 256, 0, 8192 + 128, 0 };
+ gint16 res[4];
+ GstElement *panorama = setup_panorama_s16_s (0, -1.0);
+
+ do_panorama (in, sizeof (in), res, sizeof (res));
+
+ GST_INFO ("exp. %+5d %+5d %+5d %+5d", out[0], out[1], out[2], out[3]);
+ GST_INFO ("real %+5d %+5d %+5d %+5d", res[0], res[1], res[2], res[3]);
+ fail_unless (memcmp (res, out, sizeof (res)) == 0);
+
+ cleanup_panorama (panorama);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_s16_stereo_right)
+{
+ gint16 in[4] = { 16384, -256, 8192, 128 };
+ gint16 out[4] = { 0, -256 + 16384, 0, 128 + 8192 };
+ gint16 res[4];
+ GstElement *panorama = setup_panorama_s16_s (0, 1.0);
+
+ do_panorama (in, sizeof (in), res, sizeof (res));
+
+ GST_INFO ("exp. %+5d %+5d %+5d %+5d", out[0], out[1], out[2], out[3]);
+ GST_INFO ("real %+5d %+5d %+5d %+5d", res[0], res[1], res[2], res[3]);
+ fail_unless (memcmp (res, out, sizeof (res)) == 0);
+
+ cleanup_panorama (panorama);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_f32_mono_middle)
+{
+ gfloat in[2] = { 0.5, -0.2 };
+ gfloat out[4] = { 0.25, 0.25, -0.1, -0.1 };
+ gfloat res[4];
+ GstElement *panorama = setup_panorama_f32_m (0, 0.0);
+ gint i;
+
+ do_panorama (in, sizeof (in), res, sizeof (res));
+
+ GST_INFO ("exp. %+4.2f %+4.2f %+4.2f %+4.2f", out[0], out[1], out[2], out[3]);
+ GST_INFO ("real %+4.2f %+4.2f %+4.2f %+4.2f", res[0], res[1], res[2], res[3]);
+ for (i = 0; i < 4; i++)
+ fail_unless (res[i] == out[i], "difference at pos=%d", i);
+
+ cleanup_panorama (panorama);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_f32_mono_left)
+{
+ gfloat in[2] = { 0.5, -0.2 };
+ gfloat out[4] = { 0.5, 0.0, -0.2, 0.0 };
+ gfloat res[4];
+ GstElement *panorama = setup_panorama_f32_m (0, -1.0);
+ gint i;
+
+ do_panorama (in, sizeof (in), res, sizeof (res));
+
+ GST_INFO ("exp. %+4.2f %+4.2f %+4.2f %+4.2f", out[0], out[1], out[2], out[3]);
+ GST_INFO ("real %+4.2f %+4.2f %+4.2f %+4.2f", res[0], res[1], res[2], res[3]);
+ for (i = 0; i < 4; i++)
+ fail_unless (res[i] == out[i], "difference at pos=%d", i);
+
+ cleanup_panorama (panorama);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_f32_mono_right)
+{
+ gfloat in[2] = { 0.5, -0.2 };
+ gfloat out[4] = { 0.0, 0.5, 0.0, -0.2 };
+ gfloat res[4];
+ GstElement *panorama = setup_panorama_f32_m (0, 1.0);
+ gint i;
+
+ do_panorama (in, sizeof (in), res, sizeof (res));
+
+ GST_INFO ("exp. %+4.2f %+4.2f %+4.2f %+4.2f", out[0], out[1], out[2], out[3]);
+ GST_INFO ("real %+4.2f %+4.2f %+4.2f %+4.2f", res[0], res[1], res[2], res[3]);
+ for (i = 0; i < 4; i++)
+ fail_unless (res[i] == out[i], "difference at pos=%d", i);
+
+ cleanup_panorama (panorama);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_f32_stereo_middle)
+{
+ gfloat in[4] = { 0.5, -0.2, 0.25, 0.1 };
+ gfloat res[4];
+ GstElement *panorama = setup_panorama_f32_s (0, 0.0);
+ gint i;
+
+ do_panorama (in, sizeof (in), res, sizeof (res));
+
+ GST_INFO ("exp. %+4.2f %+4.2f %+4.2f %+4.2f", in[0], in[1], in[2], in[3]);
+ GST_INFO ("real %+4.2f %+4.2f %+4.2f %+4.2f", res[0], res[1], res[2], res[3]);
+ for (i = 0; i < 4; i++)
+ fail_unless (res[i] == in[i], "difference at pos=%d", i);
+
+ cleanup_panorama (panorama);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_f32_stereo_left)
+{
+ gfloat in[4] = { 0.5, -0.2, 0.25, 0.1 };
+ gfloat out[4] = { 0.5 - 0.2, 0.0, 0.25 + 0.1, 0.0 };
+ gfloat res[4];
+ GstElement *panorama = setup_panorama_f32_s (0, -1.0);
+ gint i;
+
+ do_panorama (in, sizeof (in), res, sizeof (res));
+
+ GST_INFO ("exp. %+4.2f %+4.2f %+4.2f %+4.2f", out[0], out[1], out[2], out[3]);
+ GST_INFO ("real %+4.2f %+4.2f %+4.2f %+4.2f", res[0], res[1], res[2], res[3]);
+ for (i = 0; i < 4; i++)
+ fail_unless (res[i] == out[i], "difference at pos=%d", i);
+
+ cleanup_panorama (panorama);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_f32_stereo_right)
+{
+ gfloat in[4] = { 0.5, -0.2, 0.25, 0.1 };
+ gfloat out[4] = { 0.0, -0.2 + 0.5, 0.0, 0.1 + 0.25 };
+ gfloat res[4];
+ GstElement *panorama = setup_panorama_f32_s (0, 1.0);
+ gint i;
+
+ do_panorama (in, sizeof (in), res, sizeof (res));
+
+ GST_INFO ("exp. %+4.2f %+4.2f %+4.2f %+4.2f", out[0], out[1], out[2], out[3]);
+ GST_INFO ("real %+4.2f %+4.2f %+4.2f %+4.2f", res[0], res[1], res[2], res[3]);
+ for (i = 0; i < 4; i++)
+ fail_unless (res[i] == out[i], "difference at pos=%d", i);
+
+ cleanup_panorama (panorama);
+}
+
+GST_END_TEST;
+
+/* processing for method=simple */
+
+GST_START_TEST (test_s16_mono_middle_simple)
+{
+ gint16 in[2] = { 16384, -256 };
+ gint16 out[4] = { 16384, 16384, -256, -256 };
+ gint16 res[4];
+ GstElement *panorama = setup_panorama_s16_m (1, 0.0);
+
+ do_panorama (in, sizeof (in), res, sizeof (res));
+
+ GST_INFO ("exp. %+5d %+5d %+5d %+5d", out[0], out[1], out[2], out[3]);
+ GST_INFO ("real %+5d %+5d %+5d %+5d", res[0], res[1], res[2], res[3]);
+ fail_unless (memcmp (res, out, sizeof (res)) == 0);
+
+ cleanup_panorama (panorama);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_s16_mono_left_simple)
+{
+ gint16 in[2] = { 16384, -256 };
+ gint16 out[4] = { 16384, 0, -256, 0 };
+ gint16 res[4];
+ GstElement *panorama = setup_panorama_s16_m (1, -1.0);
+
+ do_panorama (in, sizeof (in), res, sizeof (res));
+
+ GST_INFO ("exp. %+5d %+5d %+5d %+5d", out[0], out[1], out[2], out[3]);
+ GST_INFO ("real %+5d %+5d %+5d %+5d", res[0], res[1], res[2], res[3]);
+ fail_unless (memcmp (res, out, sizeof (res)) == 0);
+
+ cleanup_panorama (panorama);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_s16_mono_right_simple)
+{
+ gint16 in[2] = { 16384, -256 };
+ gint16 out[4] = { 0, 16384, 0, -256 };
+ gint16 res[4];
+ GstElement *panorama = setup_panorama_s16_m (1, 1.0);
+
+ do_panorama (in, sizeof (in), res, sizeof (res));
+
+ GST_INFO ("exp. %+5d %+5d %+5d %+5d", out[0], out[1], out[2], out[3]);
+ GST_INFO ("real %+5d %+5d %+5d %+5d", res[0], res[1], res[2], res[3]);
+ fail_unless (memcmp (res, out, sizeof (res)) == 0);
+
+ cleanup_panorama (panorama);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_s16_stereo_middle_simple)
+{
+ gint16 in[4] = { 16384, -256, 8192, 128 };
+ gint16 res[4];
+ GstElement *panorama = setup_panorama_s16_s (1, 0.0);
+
+ do_panorama (in, sizeof (in), res, sizeof (res));
+
+ GST_INFO ("exp. %+5d %+5d %+5d %+5d", in[0], in[1], in[2], in[3]);
+ GST_INFO ("real %+5d %+5d %+5d %+5d", res[0], res[1], res[2], res[3]);
+ fail_unless (memcmp (res, in, sizeof (res)) == 0);
+
+ cleanup_panorama (panorama);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_s16_stereo_left_simple)
+{
+ gint16 in[4] = { 16384, -256, 8192, 128 };
+ gint16 out[4] = { 16384, 0, 8192, 0 };
+ gint16 res[4];
+ GstElement *panorama = setup_panorama_s16_s (1, -1.0);
+
+ do_panorama (in, sizeof (in), res, sizeof (res));
+
+ GST_INFO ("exp. %+5d %+5d %+5d %+5d", out[0], out[1], out[2], out[3]);
+ GST_INFO ("real %+5d %+5d %+5d %+5d", res[0], res[1], res[2], res[3]);
+ fail_unless (memcmp (res, out, sizeof (res)) == 0);
+
+ cleanup_panorama (panorama);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_s16_stereo_right_simple)
+{
+ gint16 in[4] = { 16384, -256, 8192, 128 };
+ gint16 out[4] = { 0, -256, 0, 128 };
+ gint16 res[4];
+ GstElement *panorama = setup_panorama_s16_s (1, 1.0);
+
+ do_panorama (in, sizeof (in), res, sizeof (res));
+
+ GST_INFO ("exp. %+5d %+5d %+5d %+5d", out[0], out[1], out[2], out[3]);
+ GST_INFO ("real %+5d %+5d %+5d %+5d", res[0], res[1], res[2], res[3]);
+ fail_unless (memcmp (res, out, sizeof (res)) == 0);
+
+ cleanup_panorama (panorama);
+}
+
+GST_END_TEST;
+
+//-----------------------------------------------------------------------------
+
+GST_START_TEST (test_f32_mono_middle_simple)
+{
+ gfloat in[2] = { 0.5, -0.2 };
+ gfloat out[4] = { 0.5, 0.5, -0.2, -0.2 };
+ gfloat res[4];
+ GstElement *panorama = setup_panorama_f32_m (1, 0.0);
+ gint i;
+
+ do_panorama (in, sizeof (in), res, sizeof (res));
+
+ GST_INFO ("exp. %+4.2f %+4.2f %+4.2f %+4.2f", out[0], out[1], out[2], out[3]);
+ GST_INFO ("real %+4.2f %+4.2f %+4.2f %+4.2f", res[0], res[1], res[2], res[3]);
+ for (i = 0; i < 4; i++)
+ fail_unless (res[i] == out[i], "difference at pos=%d", i);
+
+ cleanup_panorama (panorama);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_f32_mono_left_simple)
+{
+ gfloat in[2] = { 0.5, -0.2 };
+ gfloat out[4] = { 0.5, 0.0, -0.2, 0.0 };
+ gfloat res[4];
+ GstElement *panorama = setup_panorama_f32_m (1, -1.0);
+ gint i;
+
+ do_panorama (in, sizeof (in), res, sizeof (res));
+
+ GST_INFO ("exp. %+4.2f %+4.2f %+4.2f %+4.2f", out[0], out[1], out[2], out[3]);
+ GST_INFO ("real %+4.2f %+4.2f %+4.2f %+4.2f", res[0], res[1], res[2], res[3]);
+ for (i = 0; i < 4; i++)
+ fail_unless (res[i] == out[i], "difference at pos=%d", i);
+
+ cleanup_panorama (panorama);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_f32_mono_right_simple)
+{
+ gfloat in[2] = { 0.5, -0.2 };
+ gfloat out[4] = { 0.0, 0.5, 0.0, -0.2 };
+ gfloat res[4];
+ GstElement *panorama = setup_panorama_f32_m (1, 1.0);
+ gint i;
+
+ do_panorama (in, sizeof (in), res, sizeof (res));
+
+ GST_INFO ("exp. %+4.2f %+4.2f %+4.2f %+4.2f", out[0], out[1], out[2], out[3]);
+ GST_INFO ("real %+4.2f %+4.2f %+4.2f %+4.2f", res[0], res[1], res[2], res[3]);
+ for (i = 0; i < 4; i++)
+ fail_unless (res[i] == out[i], "difference at pos=%d", i);
+
+ cleanup_panorama (panorama);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_f32_stereo_middle_simple)
+{
+ gfloat in[4] = { 0.5, -0.2, 0.25, 0.1 };
+ gfloat res[4];
+ GstElement *panorama = setup_panorama_f32_s (1, 0.0);
+ gint i;
+
+ do_panorama (in, sizeof (in), res, sizeof (res));
+
+ GST_INFO ("exp. %+4.2f %+4.2f %+4.2f %+4.2f", in[0], in[1], in[2], in[3]);
+ GST_INFO ("real %+4.2f %+4.2f %+4.2f %+4.2f", res[0], res[1], res[2], res[3]);
+ for (i = 0; i < 4; i++)
+ fail_unless (res[i] == in[i], "difference at pos=%d", i);
+
+ cleanup_panorama (panorama);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_f32_stereo_left_simple)
+{
+ gfloat in[4] = { 0.5, -0.2, 0.25, 0.1 };
+ gfloat out[4] = { 0.5, 0.0, 0.25, 0.0 };
+ gfloat res[4];
+ GstElement *panorama = setup_panorama_f32_s (1, -1.0);
+ gint i;
+
+ do_panorama (in, sizeof (in), res, sizeof (res));
+
+ GST_INFO ("exp. %+4.2f %+4.2f %+4.2f %+4.2f", out[0], out[1], out[2], out[3]);
+ GST_INFO ("real %+4.2f %+4.2f %+4.2f %+4.2f", res[0], res[1], res[2], res[3]);
+ for (i = 0; i < 4; i++)
+ fail_unless (res[i] == out[i], "difference at pos=%d", i);
+
+ cleanup_panorama (panorama);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_f32_stereo_right_simple)
+{
+ gfloat in[4] = { 0.5, -0.2, 0.25, 0.1 };
+ gfloat out[4] = { 0.0, -0.2, 0.0, 0.1 };
+ gfloat res[4];
+ GstElement *panorama = setup_panorama_f32_s (1, 1.0);
+ gint i;
+
+ do_panorama (in, sizeof (in), res, sizeof (res));
+
+ GST_INFO ("exp. %+4.2f %+4.2f %+4.2f %+4.2f", out[0], out[1], out[2], out[3]);
+ GST_INFO ("real %+4.2f %+4.2f %+4.2f %+4.2f", res[0], res[1], res[2], res[3]);
+ for (i = 0; i < 4; i++)
+ fail_unless (res[i] == out[i], "difference at pos=%d", i);
+
+ cleanup_panorama (panorama);
+}
+
+GST_END_TEST;
+
+
+static Suite *
+panorama_suite (void)
+{
+ Suite *s = suite_create ("panorama");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_ref_counts);
+ tcase_add_test (tc_chain, test_wrong_caps);
+ /* processing for method=psy */
+ tcase_add_test (tc_chain, test_s16_mono_middle);
+ tcase_add_test (tc_chain, test_s16_mono_left);
+ tcase_add_test (tc_chain, test_s16_mono_right);
+ tcase_add_test (tc_chain, test_s16_stereo_middle);
+ tcase_add_test (tc_chain, test_s16_stereo_left);
+ tcase_add_test (tc_chain, test_s16_stereo_right);
+ tcase_add_test (tc_chain, test_f32_mono_middle);
+ tcase_add_test (tc_chain, test_f32_mono_left);
+ tcase_add_test (tc_chain, test_f32_mono_right);
+ tcase_add_test (tc_chain, test_f32_stereo_middle);
+ tcase_add_test (tc_chain, test_f32_stereo_left);
+ tcase_add_test (tc_chain, test_f32_stereo_right);
+ /* processing for method=simple */
+ tcase_add_test (tc_chain, test_s16_mono_middle_simple);
+ tcase_add_test (tc_chain, test_s16_mono_left_simple);
+ tcase_add_test (tc_chain, test_s16_mono_right_simple);
+ tcase_add_test (tc_chain, test_s16_stereo_middle_simple);
+ tcase_add_test (tc_chain, test_s16_stereo_left_simple);
+ tcase_add_test (tc_chain, test_s16_stereo_right_simple);
+ tcase_add_test (tc_chain, test_f32_mono_middle_simple);
+ tcase_add_test (tc_chain, test_f32_mono_left_simple);
+ tcase_add_test (tc_chain, test_f32_mono_right_simple);
+ tcase_add_test (tc_chain, test_f32_stereo_middle_simple);
+ tcase_add_test (tc_chain, test_f32_stereo_left_simple);
+ tcase_add_test (tc_chain, test_f32_stereo_right_simple);
+
+ return s;
+}
+
+GST_CHECK_MAIN (panorama);
diff --git a/tests/check/elements/audiowsincband.c b/tests/check/elements/audiowsincband.c
new file mode 100644
index 0000000..0a5b9c1
--- /dev/null
+++ b/tests/check/elements/audiowsincband.c
@@ -0,0 +1,1152 @@
+/* GStreamer
+ *
+ * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
+ *
+ * audiowsincband.c: Unit test for the audiowsincband element
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <gst/gst.h>
+#include <gst/audio/audio.h>
+#include <gst/base/gstbasetransform.h>
+#include <gst/check/gstcheck.h>
+
+#include <math.h>
+
+/* 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 */
+GstPad *mysrcpad, *mysinkpad;
+
+#define AUDIO_WSINC_BAND_CAPS_STRING_32 \
+ "audio/x-raw, " \
+ "format = (string) " GST_AUDIO_NE (F32) ", " \
+ "layout = (string) interleaved, " \
+ "channels = (int) 1, " \
+ "rate = (int) 44100"
+
+#define AUDIO_WSINC_BAND_CAPS_STRING_64 \
+ "audio/x-raw, " \
+ "format = (string) " GST_AUDIO_NE (F64) ", " \
+ "layout = (string) interleaved, " \
+ "channels = (int) 1, " \
+ "rate = (int) 44100"
+
+#define FORMATS "{ "GST_AUDIO_NE (F32)","GST_AUDIO_NE (F64)" }"
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "format = (string) " FORMATS ", "
+ "layout = (string) interleaved, "
+ "channels = (int) 1, " "rate = (int) 44100")
+ );
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "format = (string) " FORMATS ", "
+ "layout = (string) interleaved, "
+ "channels = (int) 1, " "rate = (int) 44100")
+ );
+
+static GstElement *
+setup_audiowsincband (void)
+{
+ GstElement *audiowsincband;
+
+ GST_DEBUG ("setup_audiowsincband");
+ audiowsincband = gst_check_setup_element ("audiowsincband");
+ mysrcpad = gst_check_setup_src_pad (audiowsincband, &srctemplate);
+ mysinkpad = gst_check_setup_sink_pad (audiowsincband, &sinktemplate);
+ gst_pad_set_active (mysrcpad, TRUE);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ return audiowsincband;
+}
+
+static void
+cleanup_audiowsincband (GstElement * audiowsincband)
+{
+ GST_DEBUG ("cleanup_audiowsincband");
+
+ g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
+ g_list_free (buffers);
+ buffers = NULL;
+
+ gst_pad_set_active (mysrcpad, FALSE);
+ gst_pad_set_active (mysinkpad, FALSE);
+ gst_check_teardown_src_pad (audiowsincband);
+ gst_check_teardown_sink_pad (audiowsincband);
+ gst_check_teardown_element (audiowsincband);
+}
+
+/* Test if data containing only one frequency component
+ * at rate/2 is erased with bandpass mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_32_bp_0hz)
+{
+ GstElement *audiowsincband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+ GList *node;
+ GstSegment segment;
+
+ audiowsincband = setup_audiowsincband ();
+ /* Set to bandpass */
+ g_object_set (G_OBJECT (audiowsincband), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiowsincband), "length", 31, NULL);
+
+ fail_unless (gst_element_set_state (audiowsincband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiowsincband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiowsincband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_and_alloc (1024 * sizeof (gfloat));
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 1024; i++)
+ in[i] = 1.0;
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (AUDIO_WSINC_BAND_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiowsincband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* ensure segment (format) properly setup */
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()));
+ /* ... and puts a new buffer on the global list */
+ fail_unless (g_list_length (buffers) >= 1);
+
+ for (node = buffers; node; node = node->next) {
+ gint buffer_length;
+
+ fail_if ((outbuffer = (GstBuffer *) node->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gfloat *) map.data;
+ buffer_length = map.size / sizeof (gfloat);
+
+ rms = 0.0;
+ for (i = 0; i < buffer_length; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / buffer_length);
+ fail_unless (rms <= 0.1);
+
+ gst_buffer_unmap (outbuffer, &map);
+ }
+
+ /* cleanup */
+ cleanup_audiowsincband (audiowsincband);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at the band center is preserved with bandreject mode
+ * and a 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_32_bp_11025hz)
+{
+ GstElement *audiowsincband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+ GList *node;
+ GstSegment segment;
+
+ audiowsincband = setup_audiowsincband ();
+ /* Set to bandpass */
+ g_object_set (G_OBJECT (audiowsincband), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiowsincband), "length", 31, NULL);
+
+ fail_unless (gst_element_set_state (audiowsincband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiowsincband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiowsincband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_and_alloc (1024 * sizeof (gfloat));
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 1024; i += 4) {
+ in[i] = 0.0;
+ in[i + 1] = 1.0;
+ in[i + 2] = 0.0;
+ in[i + 3] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (AUDIO_WSINC_BAND_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiowsincband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* ensure segment (format) properly setup */
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()));
+ /* ... and puts a new buffer on the global list */
+ fail_unless (g_list_length (buffers) >= 1);
+
+ for (node = buffers; node; node = node->next) {
+ gint buffer_length;
+
+ fail_if ((outbuffer = (GstBuffer *) node->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gfloat *) map.data;
+ buffer_length = map.size / sizeof (gfloat);
+ rms = 0.0;
+ for (i = 0; i < buffer_length; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / buffer_length);
+ fail_unless (rms >= 0.4);
+ gst_buffer_unmap (outbuffer, &map);
+ }
+
+ /* cleanup */
+ cleanup_audiowsincband (audiowsincband);
+}
+
+GST_END_TEST;
+
+
+/* Test if data containing only one frequency component
+ * at rate/2 is erased with bandreject mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_32_bp_22050hz)
+{
+ GstElement *audiowsincband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+ GList *node;
+ GstSegment segment;
+
+ audiowsincband = setup_audiowsincband ();
+ /* Set to bandpass */
+ g_object_set (G_OBJECT (audiowsincband), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiowsincband), "length", 31, NULL);
+
+ fail_unless (gst_element_set_state (audiowsincband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiowsincband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiowsincband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_and_alloc (1024 * sizeof (gfloat));
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 1024; i += 2) {
+ in[i] = 1.0;
+ in[i + 1] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (AUDIO_WSINC_BAND_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiowsincband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* ensure segment (format) properly setup */
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()));
+ /* ... and puts a new buffer on the global list */
+ fail_unless (g_list_length (buffers) >= 1);
+
+ for (node = buffers; node; node = node->next) {
+ gint buffer_length;
+
+ fail_if ((outbuffer = (GstBuffer *) node->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gfloat *) map.data;
+ buffer_length = map.size / sizeof (gfloat);
+ rms = 0.0;
+ for (i = 0; i < buffer_length; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / buffer_length);
+ fail_unless (rms <= 0.3);
+ gst_buffer_unmap (outbuffer, &map);
+ }
+
+ /* cleanup */
+ cleanup_audiowsincband (audiowsincband);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at rate/2 is preserved with bandreject mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_32_br_0hz)
+{
+ GstElement *audiowsincband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+ GList *node;
+ GstSegment segment;
+
+ audiowsincband = setup_audiowsincband ();
+ /* Set to bandreject */
+ g_object_set (G_OBJECT (audiowsincband), "mode", 1, NULL);
+ g_object_set (G_OBJECT (audiowsincband), "length", 31, NULL);
+
+ fail_unless (gst_element_set_state (audiowsincband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiowsincband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiowsincband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_and_alloc (1024 * sizeof (gfloat));
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 1024; i++)
+ in[i] = 1.0;
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (AUDIO_WSINC_BAND_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiowsincband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* ensure segment (format) properly setup */
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()));
+ /* ... and puts a new buffer on the global list */
+ fail_unless (g_list_length (buffers) >= 1);
+
+ for (node = buffers; node; node = node->next) {
+ gint buffer_length;
+
+ fail_if ((outbuffer = (GstBuffer *) node->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gfloat *) map.data;
+ buffer_length = map.size / sizeof (gfloat);
+ rms = 0.0;
+ for (i = 0; i < buffer_length; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / buffer_length);
+ fail_unless (rms >= 0.9);
+ gst_buffer_unmap (outbuffer, &map);
+ }
+
+ /* cleanup */
+ cleanup_audiowsincband (audiowsincband);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at the band center is erased with bandreject mode
+ * and a 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_32_br_11025hz)
+{
+ GstElement *audiowsincband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+ GList *node;
+ GstSegment segment;
+
+ audiowsincband = setup_audiowsincband ();
+ /* Set to bandreject */
+ g_object_set (G_OBJECT (audiowsincband), "mode", 1, NULL);
+ g_object_set (G_OBJECT (audiowsincband), "length", 31, NULL);
+
+ fail_unless (gst_element_set_state (audiowsincband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiowsincband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiowsincband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_and_alloc (1024 * sizeof (gfloat));
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+
+ for (i = 0; i < 1024; i += 4) {
+ in[i] = 0.0;
+ in[i + 1] = 1.0;
+ in[i + 2] = 0.0;
+ in[i + 3] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (AUDIO_WSINC_BAND_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiowsincband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* ensure segment (format) properly setup */
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()));
+ /* ... and puts a new buffer on the global list */
+ fail_unless (g_list_length (buffers) >= 1);
+
+ for (node = buffers; node; node = node->next) {
+ gint buffer_length;
+
+ fail_if ((outbuffer = (GstBuffer *) node->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gfloat *) map.data;
+ buffer_length = map.size / sizeof (gfloat);
+ rms = 0.0;
+ for (i = 0; i < buffer_length; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / buffer_length);
+ fail_unless (rms <= 0.35);
+ gst_buffer_unmap (outbuffer, &map);
+ }
+
+ /* cleanup */
+ cleanup_audiowsincband (audiowsincband);
+}
+
+GST_END_TEST;
+
+
+/* Test if data containing only one frequency component
+ * at rate/2 is preserved with bandreject mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_32_br_22050hz)
+{
+ GstElement *audiowsincband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+ GList *node;
+ GstSegment segment;
+
+ audiowsincband = setup_audiowsincband ();
+ /* Set to bandreject */
+ g_object_set (G_OBJECT (audiowsincband), "mode", 1, NULL);
+ g_object_set (G_OBJECT (audiowsincband), "length", 31, NULL);
+
+ fail_unless (gst_element_set_state (audiowsincband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiowsincband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiowsincband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_and_alloc (1024 * sizeof (gfloat));
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 1024; i += 2) {
+ in[i] = 1.0;
+ in[i + 1] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (AUDIO_WSINC_BAND_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiowsincband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* ensure segment (format) properly setup */
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()));
+ /* ... and puts a new buffer on the global list */
+ fail_unless (g_list_length (buffers) >= 1);
+
+ for (node = buffers; node; node = node->next) {
+ gint buffer_length;
+
+ fail_if ((outbuffer = (GstBuffer *) node->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gfloat *) map.data;
+ buffer_length = map.size / sizeof (gfloat);
+ rms = 0.0;
+ for (i = 0; i < buffer_length; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / buffer_length);
+ fail_unless (rms >= 0.9);
+ gst_buffer_unmap (outbuffer, &map);
+ }
+
+ /* cleanup */
+ cleanup_audiowsincband (audiowsincband);
+}
+
+GST_END_TEST;
+
+/* Test if buffers smaller than the kernel size are handled
+ * correctly without accessing wrong memory areas */
+GST_START_TEST (test_32_small_buffer)
+{
+ GstElement *audiowsincband;
+ GstBuffer *inbuffer;
+ GstCaps *caps;
+ gfloat *in;
+ gint i;
+ GstMapInfo map;
+ GstSegment segment;
+
+ audiowsincband = setup_audiowsincband ();
+ /* Set to bandpass */
+ g_object_set (G_OBJECT (audiowsincband), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiowsincband), "length", 101, NULL);
+
+ fail_unless (gst_element_set_state (audiowsincband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiowsincband), "lower-frequency",
+ 44100 / 4.0 - 44100 / 16.0, NULL);
+ g_object_set (G_OBJECT (audiowsincband), "upper-frequency",
+ 44100 / 4.0 + 44100 / 16.0, NULL);
+ inbuffer = gst_buffer_new_and_alloc (20 * sizeof (gfloat));
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 20; i++)
+ in[i] = 1.0;
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (AUDIO_WSINC_BAND_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiowsincband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* ensure segment (format) properly setup */
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()));
+ /* ... and puts a new buffer on the global list */
+ fail_unless (g_list_length (buffers) >= 1);
+
+ /* cleanup */
+ cleanup_audiowsincband (audiowsincband);
+}
+
+GST_END_TEST;
+
+
+
+
+
+
+
+
+
+/* Test if data containing only one frequency component
+ * at rate/2 is erased with bandpass mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_64_bp_0hz)
+{
+ GstElement *audiowsincband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+ GList *node;
+ GstSegment segment;
+
+ audiowsincband = setup_audiowsincband ();
+ /* Set to bandpass */
+ g_object_set (G_OBJECT (audiowsincband), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiowsincband), "length", 31, NULL);
+
+ fail_unless (gst_element_set_state (audiowsincband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiowsincband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiowsincband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_and_alloc (1024 * sizeof (gdouble));
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 1024; i++)
+ in[i] = 1.0;
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (AUDIO_WSINC_BAND_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiowsincband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* ensure segment (format) properly setup */
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()));
+ /* ... and puts a new buffer on the global list */
+ fail_unless (g_list_length (buffers) >= 1);
+
+ for (node = buffers; node; node = node->next) {
+ gint buffer_length;
+
+ fail_if ((outbuffer = (GstBuffer *) node->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+ buffer_length = map.size / sizeof (gdouble);
+ rms = 0.0;
+ for (i = 0; i < buffer_length; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / buffer_length);
+ fail_unless (rms <= 0.1);
+ gst_buffer_unmap (outbuffer, &map);
+ }
+
+ /* cleanup */
+ cleanup_audiowsincband (audiowsincband);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at the band center is preserved with bandreject mode
+ * and a 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_64_bp_11025hz)
+{
+ GstElement *audiowsincband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+ GList *node;
+ GstSegment segment;
+
+ audiowsincband = setup_audiowsincband ();
+ /* Set to bandpass */
+ g_object_set (G_OBJECT (audiowsincband), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiowsincband), "length", 31, NULL);
+
+ fail_unless (gst_element_set_state (audiowsincband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiowsincband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiowsincband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_and_alloc (1024 * sizeof (gdouble));
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 1024; i += 4) {
+ in[i] = 0.0;
+ in[i + 1] = 1.0;
+ in[i + 2] = 0.0;
+ in[i + 3] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (AUDIO_WSINC_BAND_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiowsincband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* ensure segment (format) properly setup */
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()));
+ /* ... and puts a new buffer on the global list */
+ fail_unless (g_list_length (buffers) >= 1);
+
+ for (node = buffers; node; node = node->next) {
+ gint buffer_length;
+
+ fail_if ((outbuffer = (GstBuffer *) node->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+ buffer_length = map.size / sizeof (gdouble);
+ rms = 0.0;
+ for (i = 0; i < buffer_length; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / buffer_length);
+ fail_unless (rms >= 0.4);
+ gst_buffer_unmap (outbuffer, &map);
+ }
+
+ /* cleanup */
+ cleanup_audiowsincband (audiowsincband);
+}
+
+GST_END_TEST;
+
+
+/* Test if data containing only one frequency component
+ * at rate/2 is erased with bandreject mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_64_bp_22050hz)
+{
+ GstElement *audiowsincband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+ GList *node;
+ GstSegment segment;
+
+ audiowsincband = setup_audiowsincband ();
+ /* Set to bandpass */
+ g_object_set (G_OBJECT (audiowsincband), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiowsincband), "length", 31, NULL);
+
+ fail_unless (gst_element_set_state (audiowsincband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiowsincband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiowsincband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_and_alloc (1024 * sizeof (gdouble));
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 1024; i += 2) {
+ in[i] = 1.0;
+ in[i + 1] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (AUDIO_WSINC_BAND_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiowsincband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* ensure segment (format) properly setup */
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()));
+ /* ... and puts a new buffer on the global list */
+ fail_unless (g_list_length (buffers) >= 1);
+
+ for (node = buffers; node; node = node->next) {
+ gint buffer_length;
+
+ fail_if ((outbuffer = (GstBuffer *) node->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+ buffer_length = map.size / sizeof (gdouble);
+ rms = 0.0;
+ for (i = 0; i < buffer_length; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / buffer_length);
+ fail_unless (rms <= 0.3);
+ gst_buffer_unmap (outbuffer, &map);
+ }
+
+ /* cleanup */
+ cleanup_audiowsincband (audiowsincband);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at rate/2 is preserved with bandreject mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_64_br_0hz)
+{
+ GstElement *audiowsincband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+ GList *node;
+ GstSegment segment;
+
+ audiowsincband = setup_audiowsincband ();
+ /* Set to bandreject */
+ g_object_set (G_OBJECT (audiowsincband), "mode", 1, NULL);
+ g_object_set (G_OBJECT (audiowsincband), "length", 31, NULL);
+
+ fail_unless (gst_element_set_state (audiowsincband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiowsincband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiowsincband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_and_alloc (1024 * sizeof (gdouble));
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 1024; i++)
+ in[i] = 1.0;
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (AUDIO_WSINC_BAND_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiowsincband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* ensure segment (format) properly setup */
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()));
+ /* ... and puts a new buffer on the global list */
+ fail_unless (g_list_length (buffers) >= 1);
+
+ for (node = buffers; node; node = node->next) {
+ gint buffer_length;
+
+ fail_if ((outbuffer = (GstBuffer *) node->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+ buffer_length = map.size / sizeof (gdouble);
+ rms = 0.0;
+ for (i = 0; i < buffer_length; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / buffer_length);
+ fail_unless (rms >= 0.9);
+ gst_buffer_unmap (outbuffer, &map);
+ }
+
+ /* cleanup */
+ cleanup_audiowsincband (audiowsincband);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at the band center is erased with bandreject mode
+ * and a 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_64_br_11025hz)
+{
+ GstElement *audiowsincband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+ GList *node;
+ GstSegment segment;
+
+ audiowsincband = setup_audiowsincband ();
+ /* Set to bandreject */
+ g_object_set (G_OBJECT (audiowsincband), "mode", 1, NULL);
+ g_object_set (G_OBJECT (audiowsincband), "length", 31, NULL);
+
+ fail_unless (gst_element_set_state (audiowsincband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiowsincband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiowsincband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_and_alloc (1024 * sizeof (gdouble));
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+
+ for (i = 0; i < 1024; i += 4) {
+ in[i] = 0.0;
+ in[i + 1] = 1.0;
+ in[i + 2] = 0.0;
+ in[i + 3] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (AUDIO_WSINC_BAND_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiowsincband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* ensure segment (format) properly setup */
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()));
+ /* ... and puts a new buffer on the global list */
+ fail_unless (g_list_length (buffers) >= 1);
+
+ for (node = buffers; node; node = node->next) {
+ gint buffer_length;
+
+ fail_if ((outbuffer = (GstBuffer *) node->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+ buffer_length = map.size / sizeof (gdouble);
+ rms = 0.0;
+ for (i = 0; i < buffer_length; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / buffer_length);
+ fail_unless (rms <= 0.35);
+ gst_buffer_unmap (outbuffer, &map);
+ }
+
+ /* cleanup */
+ cleanup_audiowsincband (audiowsincband);
+}
+
+GST_END_TEST;
+
+
+/* Test if data containing only one frequency component
+ * at rate/2 is preserved with bandreject mode and a
+ * 2000Hz frequency band around rate/4 */
+GST_START_TEST (test_64_br_22050hz)
+{
+ GstElement *audiowsincband;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+ GList *node;
+ GstSegment segment;
+
+ audiowsincband = setup_audiowsincband ();
+ /* Set to bandreject */
+ g_object_set (G_OBJECT (audiowsincband), "mode", 1, NULL);
+ g_object_set (G_OBJECT (audiowsincband), "length", 31, NULL);
+
+ fail_unless (gst_element_set_state (audiowsincband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiowsincband), "lower-frequency",
+ 44100 / 4.0 - 1000, NULL);
+ g_object_set (G_OBJECT (audiowsincband), "upper-frequency",
+ 44100 / 4.0 + 1000, NULL);
+ inbuffer = gst_buffer_new_and_alloc (1024 * sizeof (gdouble));
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 1024; i += 2) {
+ in[i] = 1.0;
+ in[i + 1] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (AUDIO_WSINC_BAND_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiowsincband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* ensure segment (format) properly setup */
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()));
+ /* ... and puts a new buffer on the global list */
+ fail_unless (g_list_length (buffers) >= 1);
+
+ for (node = buffers; node; node = node->next) {
+ gint buffer_length;
+
+ fail_if ((outbuffer = (GstBuffer *) node->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+ buffer_length = map.size / sizeof (gdouble);
+ rms = 0.0;
+ for (i = 0; i < buffer_length; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / buffer_length);
+ fail_unless (rms >= 0.9);
+ gst_buffer_unmap (outbuffer, &map);
+ }
+
+ /* cleanup */
+ cleanup_audiowsincband (audiowsincband);
+}
+
+GST_END_TEST;
+
+/* Test if buffers smaller than the kernel size are handled
+ * correctly without accessing wrong memory areas */
+GST_START_TEST (test_64_small_buffer)
+{
+ GstElement *audiowsincband;
+ GstBuffer *inbuffer;
+ GstCaps *caps;
+ gdouble *in;
+ gint i;
+ GstMapInfo map;
+ GstSegment segment;
+
+ audiowsincband = setup_audiowsincband ();
+ /* Set to bandpass */
+ g_object_set (G_OBJECT (audiowsincband), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiowsincband), "length", 101, NULL);
+
+ fail_unless (gst_element_set_state (audiowsincband,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiowsincband), "lower-frequency",
+ 44100 / 4.0 - 44100 / 16.0, NULL);
+ g_object_set (G_OBJECT (audiowsincband), "upper-frequency",
+ 44100 / 4.0 + 44100 / 16.0, NULL);
+ inbuffer = gst_buffer_new_and_alloc (20 * sizeof (gdouble));
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 20; i++)
+ in[i] = 1.0;
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (AUDIO_WSINC_BAND_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiowsincband, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* ensure segment (format) properly setup */
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()));
+ /* ... and puts a new buffer on the global list */
+ fail_unless (g_list_length (buffers) >= 1);
+
+ /* cleanup */
+ cleanup_audiowsincband (audiowsincband);
+}
+
+GST_END_TEST;
+
+static Suite *
+audiowsincband_suite (void)
+{
+ Suite *s = suite_create ("audiowsincband");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_32_bp_0hz);
+ tcase_add_test (tc_chain, test_32_bp_11025hz);
+ tcase_add_test (tc_chain, test_32_bp_22050hz);
+ tcase_add_test (tc_chain, test_32_br_0hz);
+ tcase_add_test (tc_chain, test_32_br_11025hz);
+ tcase_add_test (tc_chain, test_32_br_22050hz);
+ tcase_add_test (tc_chain, test_32_small_buffer);
+ tcase_add_test (tc_chain, test_64_bp_0hz);
+ tcase_add_test (tc_chain, test_64_bp_11025hz);
+ tcase_add_test (tc_chain, test_64_bp_22050hz);
+ tcase_add_test (tc_chain, test_64_br_0hz);
+ tcase_add_test (tc_chain, test_64_br_11025hz);
+ tcase_add_test (tc_chain, test_64_br_22050hz);
+ tcase_add_test (tc_chain, test_64_small_buffer);
+
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ int nf;
+
+ Suite *s = audiowsincband_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ srunner_run_all (sr, CK_NORMAL);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}
diff --git a/tests/check/elements/audiowsinclimit.c b/tests/check/elements/audiowsinclimit.c
new file mode 100644
index 0000000..de80451
--- /dev/null
+++ b/tests/check/elements/audiowsinclimit.c
@@ -0,0 +1,803 @@
+/* GStreamer
+ *
+ * Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
+ *
+ * audiowsinclimit.c: Unit test for the audiowsinclimit element
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <gst/gst.h>
+#include <gst/audio/audio.h>
+#include <gst/base/gstbasetransform.h>
+#include <gst/check/gstcheck.h>
+
+#include <math.h>
+
+/* 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 */
+GstPad *mysrcpad, *mysinkpad;
+
+#define AUDIO_WSINC_LIMIT_CAPS_STRING_32 \
+ "audio/x-raw, " \
+ "format = (string) " GST_AUDIO_NE (F32) ", " \
+ "layout = (string) interleaved, " \
+ "channels = (int) 1, " \
+ "rate = (int) 44100"
+
+#define AUDIO_WSINC_LIMIT_CAPS_STRING_64 \
+ "audio/x-raw, " \
+ "format = (string) " GST_AUDIO_NE (F64) ", " \
+ "layout = (string) interleaved, " \
+ "channels = (int) 1, " \
+ "rate = (int) 44100"
+
+#define FORMATS "{ "GST_AUDIO_NE (F32)","GST_AUDIO_NE (F64)" }"
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "format = (string) " FORMATS ", "
+ "layout = (string) interleaved, "
+ "channels = (int) 1, " "rate = (int) 44100")
+ );
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "format = (string) " FORMATS ", "
+ "layout = (string) interleaved, "
+ "channels = (int) 1, " "rate = (int) 44100")
+ );
+
+static GstElement *
+setup_audiowsinclimit (void)
+{
+ GstElement *audiowsinclimit;
+
+ GST_DEBUG ("setup_audiowsinclimit");
+ audiowsinclimit = gst_check_setup_element ("audiowsinclimit");
+ mysrcpad = gst_check_setup_src_pad (audiowsinclimit, &srctemplate);
+ mysinkpad = gst_check_setup_sink_pad (audiowsinclimit, &sinktemplate);
+ gst_pad_set_active (mysrcpad, TRUE);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ return audiowsinclimit;
+}
+
+static void
+cleanup_audiowsinclimit (GstElement * audiowsinclimit)
+{
+ GST_DEBUG ("cleanup_audiowsinclimit");
+
+ g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
+ g_list_free (buffers);
+ buffers = NULL;
+
+ gst_pad_set_active (mysrcpad, FALSE);
+ gst_pad_set_active (mysinkpad, FALSE);
+ gst_check_teardown_src_pad (audiowsinclimit);
+ gst_check_teardown_sink_pad (audiowsinclimit);
+ gst_check_teardown_element (audiowsinclimit);
+}
+
+/* Test if data containing only one frequency component
+ * at 0 is preserved with lowpass mode and a cutoff
+ * at rate/4 */
+GST_START_TEST (test_32_lp_0hz)
+{
+ GstElement *audiowsinclimit;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+ GList *node;
+ GstSegment segment;
+
+ audiowsinclimit = setup_audiowsinclimit ();
+ /* Set to lowpass */
+ g_object_set (G_OBJECT (audiowsinclimit), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiowsinclimit), "length", 21, NULL);
+
+ fail_unless (gst_element_set_state (audiowsinclimit,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ /* cutoff = sampling rate / 4, data = 0 */
+ g_object_set (G_OBJECT (audiowsinclimit), "cutoff", 44100 / 4.0, NULL);
+ inbuffer = gst_buffer_new_and_alloc (128 * sizeof (gfloat));
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 128; i++)
+ in[i] = 1.0;
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (AUDIO_WSINC_LIMIT_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiowsinclimit, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* ensure segment (format) properly setup */
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()));
+ /* ... and puts a new buffer on the global list */
+ fail_unless (g_list_length (buffers) >= 1);
+
+ for (node = buffers; node; node = node->next) {
+ gint buffer_length;
+
+ fail_if ((outbuffer = (GstBuffer *) node->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gfloat *) map.data;
+ buffer_length = map.size / sizeof (gfloat);
+ rms = 0.0;
+ for (i = 0; i < buffer_length; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / buffer_length);
+ gst_buffer_unmap (outbuffer, &map);
+ fail_unless (rms >= 0.9);
+ }
+
+ /* cleanup */
+ cleanup_audiowsinclimit (audiowsinclimit);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at rate/2 is erased with lowpass mode and a cutoff
+ * at rate/4 */
+GST_START_TEST (test_32_lp_22050hz)
+{
+ GstElement *audiowsinclimit;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+ GList *node;
+ GstSegment segment;
+
+ audiowsinclimit = setup_audiowsinclimit ();
+ /* Set to lowpass */
+ g_object_set (G_OBJECT (audiowsinclimit), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiowsinclimit), "length", 21, NULL);
+
+ fail_unless (gst_element_set_state (audiowsinclimit,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiowsinclimit), "cutoff", 44100 / 4.0, NULL);
+ inbuffer = gst_buffer_new_and_alloc (128 * sizeof (gfloat));
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 128; i += 2) {
+ in[i] = 1.0;
+ in[i + 1] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (AUDIO_WSINC_LIMIT_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiowsinclimit, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* ensure segment (format) properly setup */
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()));
+ /* ... and puts a new buffer on the global list */
+ fail_unless (g_list_length (buffers) >= 1);
+
+ for (node = buffers; node; node = node->next) {
+ gint buffer_length;
+
+ fail_if ((outbuffer = (GstBuffer *) node->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gfloat *) map.data;
+ buffer_length = map.size / sizeof (gfloat);
+ rms = 0.0;
+ for (i = 0; i < buffer_length; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / buffer_length);
+ gst_buffer_unmap (outbuffer, &map);
+ fail_unless (rms <= 0.1);
+ }
+
+ /* cleanup */
+ cleanup_audiowsinclimit (audiowsinclimit);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at 0 is erased with highpass mode and a cutoff
+ * at rate/4 */
+GST_START_TEST (test_32_hp_0hz)
+{
+ GstElement *audiowsinclimit;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+ GList *node;
+ GstSegment segment;
+
+ audiowsinclimit = setup_audiowsinclimit ();
+ /* Set to highpass */
+ g_object_set (G_OBJECT (audiowsinclimit), "mode", 1, NULL);
+ g_object_set (G_OBJECT (audiowsinclimit), "length", 21, NULL);
+
+ fail_unless (gst_element_set_state (audiowsinclimit,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiowsinclimit), "cutoff", 44100 / 4.0, NULL);
+ inbuffer = gst_buffer_new_and_alloc (128 * sizeof (gfloat));
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 128; i++)
+ in[i] = 1.0;
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (AUDIO_WSINC_LIMIT_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiowsinclimit, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* ensure segment (format) properly setup */
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()));
+ /* ... and puts a new buffer on the global list */
+ fail_unless (g_list_length (buffers) >= 1);
+
+ for (node = buffers; node; node = node->next) {
+ gint buffer_length;
+
+ fail_if ((outbuffer = (GstBuffer *) node->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gfloat *) map.data;
+ buffer_length = map.size / sizeof (gfloat);
+ rms = 0.0;
+ for (i = 0; i < buffer_length; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / buffer_length);
+ gst_buffer_unmap (outbuffer, &map);
+ fail_unless (rms <= 0.1);
+ }
+
+ /* cleanup */
+ cleanup_audiowsinclimit (audiowsinclimit);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at rate/2 is preserved with highpass mode and a cutoff
+ * at rate/4 */
+GST_START_TEST (test_32_hp_22050hz)
+{
+ GstElement *audiowsinclimit;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+ GList *node;
+ GstSegment segment;
+
+ audiowsinclimit = setup_audiowsinclimit ();
+ /* Set to highpass */
+ g_object_set (G_OBJECT (audiowsinclimit), "mode", 1, NULL);
+ g_object_set (G_OBJECT (audiowsinclimit), "length", 21, NULL);
+
+ fail_unless (gst_element_set_state (audiowsinclimit,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiowsinclimit), "cutoff", 44100 / 4.0, NULL);
+ inbuffer = gst_buffer_new_and_alloc (128 * sizeof (gfloat));
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 128; i += 2) {
+ in[i] = 1.0;
+ in[i + 1] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (AUDIO_WSINC_LIMIT_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiowsinclimit, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* ensure segment (format) properly setup */
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()));
+ /* ... and puts a new buffer on the global list */
+ fail_unless (g_list_length (buffers) >= 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ for (node = buffers; node; node = node->next) {
+ gint buffer_length;
+
+ fail_if ((outbuffer = (GstBuffer *) node->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gfloat *) map.data;
+ buffer_length = map.size / sizeof (gfloat);
+ rms = 0.0;
+ for (i = 0; i < buffer_length; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / buffer_length);
+ gst_buffer_unmap (outbuffer, &map);
+ fail_unless (rms >= 0.9);
+ }
+
+ /* cleanup */
+ cleanup_audiowsinclimit (audiowsinclimit);
+}
+
+GST_END_TEST;
+
+/* Test if buffers smaller than the kernel size are handled
+ * correctly without accessing wrong memory areas */
+GST_START_TEST (test_32_small_buffer)
+{
+ GstElement *audiowsinclimit;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gfloat *in;
+ gint i;
+ GstMapInfo map;
+ GstSegment segment;
+
+ audiowsinclimit = setup_audiowsinclimit ();
+ /* Set to lowpass */
+ g_object_set (G_OBJECT (audiowsinclimit), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiowsinclimit), "length", 101, NULL);
+
+ fail_unless (gst_element_set_state (audiowsinclimit,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiowsinclimit), "cutoff", 44100 / 4.0, NULL);
+ inbuffer = gst_buffer_new_and_alloc (20 * sizeof (gfloat));
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gfloat *) map.data;
+ for (i = 0; i < 20; i++)
+ in[i] = 1.0;
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (AUDIO_WSINC_LIMIT_CAPS_STRING_32);
+ gst_check_setup_events (mysrcpad, audiowsinclimit, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* ensure segment (format) properly setup */
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()));
+ /* ... and puts a new buffer on the global list */
+ fail_unless (g_list_length (buffers) >= 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ /* cleanup */
+ cleanup_audiowsinclimit (audiowsinclimit);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at 0 is preserved with lowpass mode and a cutoff
+ * at rate/4 */
+GST_START_TEST (test_64_lp_0hz)
+{
+ GstElement *audiowsinclimit;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+ GList *node;
+ GstSegment segment;
+
+ audiowsinclimit = setup_audiowsinclimit ();
+ /* Set to lowpass */
+ g_object_set (G_OBJECT (audiowsinclimit), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiowsinclimit), "length", 21, NULL);
+
+ fail_unless (gst_element_set_state (audiowsinclimit,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ /* cutoff = sampling rate / 4, data = 0 */
+ g_object_set (G_OBJECT (audiowsinclimit), "cutoff", 44100 / 4.0, NULL);
+ inbuffer = gst_buffer_new_and_alloc (128 * sizeof (gdouble));
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 128; i++)
+ in[i] = 1.0;
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (AUDIO_WSINC_LIMIT_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiowsinclimit, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* ensure segment (format) properly setup */
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()));
+ /* ... and puts a new buffer on the global list */
+ fail_unless (g_list_length (buffers) >= 1);
+
+ for (node = buffers; node; node = node->next) {
+ gint buffer_length;
+
+ fail_if ((outbuffer = (GstBuffer *) node->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+ buffer_length = map.size / sizeof (gdouble);
+ rms = 0.0;
+ for (i = 0; i < buffer_length; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / buffer_length);
+ gst_buffer_unmap (outbuffer, &map);
+ fail_unless (rms >= 0.9);
+ }
+
+ /* cleanup */
+ cleanup_audiowsinclimit (audiowsinclimit);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at rate/2 is erased with lowpass mode and a cutoff
+ * at rate/4 */
+GST_START_TEST (test_64_lp_22050hz)
+{
+ GstElement *audiowsinclimit;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+ GList *node;
+ GstSegment segment;
+
+ audiowsinclimit = setup_audiowsinclimit ();
+ /* Set to lowpass */
+ g_object_set (G_OBJECT (audiowsinclimit), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiowsinclimit), "length", 21, NULL);
+
+ fail_unless (gst_element_set_state (audiowsinclimit,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiowsinclimit), "cutoff", 44100 / 4.0, NULL);
+ inbuffer = gst_buffer_new_and_alloc (128 * sizeof (gdouble));
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 128; i += 2) {
+ in[i] = 1.0;
+ in[i + 1] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (AUDIO_WSINC_LIMIT_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiowsinclimit, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* ensure segment (format) properly setup */
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()));
+ /* ... and puts a new buffer on the global list */
+ fail_unless (g_list_length (buffers) >= 1);
+
+ for (node = buffers; node; node = node->next) {
+ gint buffer_length;
+
+ fail_if ((outbuffer = (GstBuffer *) node->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+ buffer_length = map.size / sizeof (gdouble);
+ rms = 0.0;
+ for (i = 0; i < buffer_length; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / buffer_length);
+ gst_buffer_unmap (outbuffer, &map);
+ fail_unless (rms <= 0.1);
+ }
+
+ /* cleanup */
+ cleanup_audiowsinclimit (audiowsinclimit);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at 0 is erased with highpass mode and a cutoff
+ * at rate/4 */
+GST_START_TEST (test_64_hp_0hz)
+{
+ GstElement *audiowsinclimit;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+ GList *node;
+ GstSegment segment;
+
+ audiowsinclimit = setup_audiowsinclimit ();
+ /* Set to highpass */
+ g_object_set (G_OBJECT (audiowsinclimit), "mode", 1, NULL);
+ g_object_set (G_OBJECT (audiowsinclimit), "length", 21, NULL);
+
+ fail_unless (gst_element_set_state (audiowsinclimit,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiowsinclimit), "cutoff", 44100 / 4.0, NULL);
+ inbuffer = gst_buffer_new_and_alloc (128 * sizeof (gdouble));
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 128; i++)
+ in[i] = 1.0;
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (AUDIO_WSINC_LIMIT_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiowsinclimit, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* ensure segment (format) properly setup */
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()));
+ /* ... and puts a new buffer on the global list */
+ fail_unless (g_list_length (buffers) >= 1);
+
+ for (node = buffers; node; node = node->next) {
+ gint buffer_length;
+
+ fail_if ((outbuffer = (GstBuffer *) node->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+ buffer_length = map.size / sizeof (gdouble);
+ rms = 0.0;
+ for (i = 0; i < buffer_length; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / buffer_length);
+ gst_buffer_unmap (outbuffer, &map);
+ fail_unless (rms <= 0.1);
+ }
+
+ /* cleanup */
+ cleanup_audiowsinclimit (audiowsinclimit);
+}
+
+GST_END_TEST;
+
+/* Test if data containing only one frequency component
+ * at rate/2 is preserved with highpass mode and a cutoff
+ * at rate/4 */
+GST_START_TEST (test_64_hp_22050hz)
+{
+ GstElement *audiowsinclimit;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms;
+ gint i;
+ GstMapInfo map;
+ GList *node;
+ GstSegment segment;
+
+ audiowsinclimit = setup_audiowsinclimit ();
+ /* Set to highpass */
+ g_object_set (G_OBJECT (audiowsinclimit), "mode", 1, NULL);
+ g_object_set (G_OBJECT (audiowsinclimit), "length", 21, NULL);
+
+ fail_unless (gst_element_set_state (audiowsinclimit,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiowsinclimit), "cutoff", 44100 / 4.0, NULL);
+ inbuffer = gst_buffer_new_and_alloc (128 * sizeof (gdouble));
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 128; i += 2) {
+ in[i] = 1.0;
+ in[i + 1] = -1.0;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (AUDIO_WSINC_LIMIT_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiowsinclimit, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* ensure segment (format) properly setup */
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()));
+ /* ... and puts a new buffer on the global list */
+ fail_unless (g_list_length (buffers) >= 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ for (node = buffers; node; node = node->next) {
+ gint buffer_length;
+
+ fail_if ((outbuffer = (GstBuffer *) node->data) == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+ buffer_length = map.size / sizeof (gdouble);
+ rms = 0.0;
+ for (i = 0; i < buffer_length; i++)
+ rms += res[i] * res[i];
+ rms = sqrt (rms / buffer_length);
+ gst_buffer_unmap (outbuffer, &map);
+ fail_unless (rms >= 0.9);
+ }
+
+ /* cleanup */
+ cleanup_audiowsinclimit (audiowsinclimit);
+}
+
+GST_END_TEST;
+
+/* Test if buffers smaller than the kernel size are handled
+ * correctly without accessing wrong memory areas */
+GST_START_TEST (test_64_small_buffer)
+{
+ GstElement *audiowsinclimit;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ gdouble *in;
+ gint i;
+ GstMapInfo map;
+ GstSegment segment;
+
+ audiowsinclimit = setup_audiowsinclimit ();
+ /* Set to lowpass */
+ g_object_set (G_OBJECT (audiowsinclimit), "mode", 0, NULL);
+ g_object_set (G_OBJECT (audiowsinclimit), "length", 101, NULL);
+
+ fail_unless (gst_element_set_state (audiowsinclimit,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (G_OBJECT (audiowsinclimit), "cutoff", 44100 / 4.0, NULL);
+ inbuffer = gst_buffer_new_and_alloc (20 * sizeof (gdouble));
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 20; i++)
+ in[i] = 1.0;
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (AUDIO_WSINC_LIMIT_CAPS_STRING_64);
+ gst_check_setup_events (mysrcpad, audiowsinclimit, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* ensure segment (format) properly setup */
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()));
+ /* ... and puts a new buffer on the global list */
+ fail_unless (g_list_length (buffers) >= 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+
+ /* cleanup */
+ cleanup_audiowsinclimit (audiowsinclimit);
+}
+
+GST_END_TEST;
+
+static Suite *
+audiowsinclimit_suite (void)
+{
+ Suite *s = suite_create ("audiowsinclimit");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_32_lp_0hz);
+ tcase_add_test (tc_chain, test_32_lp_22050hz);
+ tcase_add_test (tc_chain, test_32_hp_0hz);
+ tcase_add_test (tc_chain, test_32_hp_22050hz);
+ tcase_add_test (tc_chain, test_32_small_buffer);
+ tcase_add_test (tc_chain, test_64_lp_0hz);
+ tcase_add_test (tc_chain, test_64_lp_22050hz);
+ tcase_add_test (tc_chain, test_64_hp_0hz);
+ tcase_add_test (tc_chain, test_64_hp_22050hz);
+ tcase_add_test (tc_chain, test_64_small_buffer);
+
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ int nf;
+
+ Suite *s = audiowsinclimit_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ srunner_run_all (sr, CK_NORMAL);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}
diff --git a/tests/check/elements/autodetect.c b/tests/check/elements/autodetect.c
new file mode 100755
index 0000000..578627c
--- /dev/null
+++ b/tests/check/elements/autodetect.c
@@ -0,0 +1,229 @@
+/* GStreamer unit test for the autodetect elements
+ *
+ * Copyright (C) <2006> Tim-Philipp Müller <tim centricular net>
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <gst/audio/audio.h>
+#include <gst/base/gstbasesink.h>
+#include <gst/check/gstcheck.h>
+#include <gst/video/video.h>
+
+/* dummy sink elements */
+
+#define GST_TYPE_FAKE_AUDIO_SINK (gst_fake_audio_sink_get_type ())
+typedef GstBaseSink GstFakeAudioSink;
+typedef GstBaseSinkClass GstFakeAudioSinkClass;
+GType gst_fake_audio_sink_get_type (void);
+G_DEFINE_TYPE (GstFakeAudioSink, gst_fake_audio_sink, GST_TYPE_BASE_SINK);
+
+static void
+gst_fake_audio_sink_class_init (GstFakeAudioSinkClass * klass)
+{
+ GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
+ static GstStaticPadTemplate pad_template = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK, GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_FORMATS_ALL) "; "));
+
+ gst_element_class_set_static_metadata (gstelement_class, "Fake Audio Sink",
+ "Sink/Audio", "Audio sink fake for testing", "Stefan Sauer");
+ gst_element_class_add_pad_template (gstelement_class,
+ gst_static_pad_template_get (&pad_template));
+}
+
+static void
+gst_fake_audio_sink_init (GstFakeAudioSink * sink)
+{
+}
+
+#define GST_TYPE_FAKE_VIDEO_SINK (gst_fake_video_sink_get_type ())
+typedef GstBaseSink GstFakeVideoSink;
+typedef GstBaseSinkClass GstFakeVideoSinkClass;
+GType gst_fake_video_sink_get_type (void);
+G_DEFINE_TYPE (GstFakeVideoSink, gst_fake_video_sink, GST_TYPE_BASE_SINK);
+
+static void
+gst_fake_video_sink_class_init (GstFakeVideoSinkClass * klass)
+{
+ GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
+ static GstStaticPadTemplate pad_template = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK, GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL) "; "));
+
+ gst_element_class_set_static_metadata (gstelement_class, "Fake Video Sink",
+ "Sink/Video", "Video sink fake for testing", "Stefan Sauer");
+ gst_element_class_add_pad_template (gstelement_class,
+ gst_static_pad_template_get (&pad_template));
+}
+
+static void
+gst_fake_video_sink_init (GstFakeVideoSink * sink)
+{
+}
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ if (!gst_element_register (plugin, "fakeaudiosink", G_MAXINT,
+ GST_TYPE_FAKE_AUDIO_SINK))
+ return FALSE;
+ if (!gst_element_register (plugin, "fakevideosink", G_MAXINT,
+ GST_TYPE_FAKE_VIDEO_SINK))
+ return FALSE;
+ return TRUE;
+}
+
+/* tests */
+
+static void
+test_plugs_best (GstElement * sink, GType expected)
+{
+ GstStateChangeReturn state_ret;
+ GList *child;
+
+ state_ret = gst_element_set_state (sink, GST_STATE_READY);
+ fail_unless (state_ret == GST_STATE_CHANGE_SUCCESS, NULL);
+
+ child = GST_BIN_CHILDREN (sink);
+ fail_unless (child != NULL, "no elements plugged");
+ ck_assert_int_eq (g_list_length (child), 1);
+ fail_unless (G_OBJECT_TYPE (child), expected);
+
+ /* clean up */
+ gst_element_set_state (sink, GST_STATE_NULL);
+ gst_object_unref (sink);
+}
+
+GST_START_TEST (test_autovideosink_plugs_best)
+{
+ GstElement *sink = gst_element_factory_make ("autovideosink", NULL);
+
+ test_plugs_best (sink, GST_TYPE_FAKE_VIDEO_SINK);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_autoaudiosink_plugs_best)
+{
+ GstElement *sink = gst_element_factory_make ("autoaudiosink", NULL);
+
+ test_plugs_best (sink, GST_TYPE_FAKE_AUDIO_SINK);
+}
+
+GST_END_TEST;
+
+static void
+test_ghostpad_error_case (GstCaps * caps, GstElement * sink,
+ const gchar * fake_sink_name)
+{
+ GstStateChangeReturn state_ret;
+ GstElement *pipeline, *src, *filter, *fakesink;
+
+ pipeline = gst_pipeline_new ("pipeline");
+ src = gst_element_factory_make ("fakesrc", NULL);
+ filter = gst_element_factory_make ("capsfilter", NULL);
+
+ g_object_set (filter, "caps", caps, NULL);
+ gst_caps_unref (caps);
+
+ gst_bin_add_many (GST_BIN (pipeline), src, filter, sink, NULL);
+
+ fail_unless (gst_element_link (src, filter), "Failed to link src to filter");
+ fail_unless (gst_element_link (filter, sink),
+ "Failed to link filter to sink");
+
+ /* this should fail, there's no such format */
+ state_ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
+ if (state_ret == GST_STATE_CHANGE_ASYNC) {
+ /* make sure we wait for the actual success/failure to happen */
+ GstState state;
+ state_ret =
+ gst_element_get_state (pipeline, &state, &state, GST_CLOCK_TIME_NONE);
+ }
+ fakesink = gst_bin_get_by_name (GST_BIN (sink), fake_sink_name);
+ if (fakesink != NULL) {
+ /* no real sink available */
+ fail_unless (state_ret == GST_STATE_CHANGE_SUCCESS,
+ "pipeline _set_state() to PAUSED failed");
+ gst_object_unref (fakesink);
+ } else {
+ /* autovideosink contains a real video sink */
+ fail_unless (state_ret == GST_STATE_CHANGE_FAILURE,
+ "pipeline _set_state() to PAUSED succeeded but should have failed");
+
+ /* so, we hit an error and try to shut down the pipeline; this shouldn't
+ * deadlock or block anywhere when autovideosink resets the ghostpad
+ * targets etc. */
+ }
+ state_ret = gst_element_set_state (pipeline, GST_STATE_NULL);
+ fail_unless (state_ret == GST_STATE_CHANGE_SUCCESS,
+ "State change on pipeline failed");
+
+ /* clean up */
+ gst_object_unref (pipeline);
+}
+
+GST_START_TEST (test_autovideosink_ghostpad_error_case)
+{
+ GstElement *sink = gst_element_factory_make ("autovideosink", NULL);
+ GstCaps *caps = gst_caps_new_simple ("video/x-raw", "format", G_TYPE_STRING,
+ "ABCD", NULL);
+
+ test_ghostpad_error_case (caps, sink, "fake-video-sink");
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_autoaudiosink_ghostpad_error_case)
+{
+ GstElement *sink = gst_element_factory_make ("autoaudiosink", NULL);
+ GstCaps *caps = gst_caps_new_simple ("audio/x-raw", "format", G_TYPE_STRING,
+ "ABCD", NULL);
+
+ test_ghostpad_error_case (caps, sink, "fake-audio-sink");
+}
+
+GST_END_TEST;
+
+static Suite *
+autodetect_suite (void)
+{
+ Suite *s = suite_create ("autodetect");
+ TCase *tc_chain = tcase_create ("general");
+
+ gst_plugin_register_static (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ "autodetect-test",
+ "autodetect test elements",
+ plugin_init,
+ VERSION, "LGPL", PACKAGE, PACKAGE_NAME,
+ "http://gstreamer.freedesktop.org");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_autovideosink_plugs_best);
+ tcase_add_test (tc_chain, test_autoaudiosink_plugs_best);
+ tcase_add_test (tc_chain, test_autovideosink_ghostpad_error_case);
+ tcase_add_test (tc_chain, test_autoaudiosink_ghostpad_error_case);
+
+ return s;
+}
+
+GST_CHECK_MAIN (autodetect);
diff --git a/tests/check/elements/avimux.c b/tests/check/elements/avimux.c
new file mode 100644
index 0000000..47c155f
--- /dev/null
+++ b/tests/check/elements/avimux.c
@@ -0,0 +1,284 @@
+/* GStreamer
+ *
+ * unit test for avimux
+ *
+ * Copyright (C) <2006> Mark Nauwelaerts <manauw@skynet.be>
+ *
+ * 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.
+ */
+
+#include <unistd.h>
+
+#include <gst/check/gstcheck.h>
+
+/* 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;
+
+#define AUDIO_CAPS_STRING "audio/x-ac3, " \
+ "channels = (int) 1, " \
+ "rate = (int) 8000"
+#define VIDEO_CAPS_STRING "video/mpeg, mpegversion = (int) 4, " \
+ "systemstream = (bool) false, " \
+ "width = (int) 384, " \
+ "height = (int) 288, " \
+ "framerate = (fraction) 25/1"
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("video/x-msvideo"));
+static GstStaticPadTemplate srcvideotemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (VIDEO_CAPS_STRING));
+
+static GstStaticPadTemplate srcaudiotemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (AUDIO_CAPS_STRING));
+
+
+/* setup and teardown needs some special handling for muxer */
+static GstPad *
+setup_src_pad (GstElement * element,
+ GstStaticPadTemplate * template, GstCaps * caps, const gchar * sinkname)
+{
+ GstPad *srcpad, *sinkpad;
+
+ GST_DEBUG_OBJECT (element, "setting up sending pad");
+ /* sending pad */
+ srcpad = gst_pad_new_from_static_template (template, "src");
+ fail_if (srcpad == NULL, "Could not create a srcpad");
+ ASSERT_OBJECT_REFCOUNT (srcpad, "srcpad", 1);
+
+ if (!(sinkpad = gst_element_get_static_pad (element, sinkname)))
+ sinkpad = gst_element_get_request_pad (element, sinkname);
+ fail_if (sinkpad == NULL, "Could not get sink pad from %s",
+ GST_ELEMENT_NAME (element));
+ /* references are owned by: 1) us, 2) avimux, 3) collect pads */
+ ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 3);
+ if (caps)
+ fail_unless (gst_pad_set_caps (srcpad, caps));
+ fail_unless (gst_pad_link (srcpad, sinkpad) == GST_PAD_LINK_OK,
+ "Could not link source and %s sink pads", GST_ELEMENT_NAME (element));
+ gst_object_unref (sinkpad); /* because we got it higher up */
+
+ /* references are owned by: 1) avimux, 2) collect pads */
+ ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 2);
+
+ return srcpad;
+}
+
+static void
+teardown_src_pad (GstElement * element, const gchar * sinkname)
+{
+ GstPad *srcpad, *sinkpad;
+ gchar *padname;
+
+ /* clean up floating src pad */
+ padname = g_strdup (sinkname);
+ memcpy (strchr (padname, '%'), "0", 2);
+ if (!(sinkpad = gst_element_get_static_pad (element, padname)))
+ sinkpad = gst_element_get_request_pad (element, padname);
+ g_free (padname);
+ /* pad refs held by 1) avimux 2) collectpads and 3) us (through _get) */
+ ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 3);
+ srcpad = gst_pad_get_peer (sinkpad);
+
+ gst_pad_unlink (srcpad, sinkpad);
+
+ /* after unlinking, pad refs still held by
+ * 1) avimux and 2) collectpads and 3) us (through _get) */
+ ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 3);
+ gst_object_unref (sinkpad);
+ /* one more ref is held by element itself */
+
+ /* pad refs held by both creator and this function (through _get_peer) */
+ ASSERT_OBJECT_REFCOUNT (srcpad, "srcpad", 2);
+ gst_object_unref (srcpad);
+ gst_object_unref (srcpad);
+
+}
+
+static GstElement *
+setup_avimux (GstStaticPadTemplate * srctemplate, const gchar * sinkname)
+{
+ GstElement *avimux;
+
+ GST_DEBUG ("setup_avimux");
+ avimux = gst_check_setup_element ("avimux");
+ mysrcpad = setup_src_pad (avimux, srctemplate, NULL, sinkname);
+ mysinkpad = gst_check_setup_sink_pad (avimux, &sinktemplate);
+ gst_pad_set_active (mysrcpad, TRUE);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ return avimux;
+}
+
+static void
+cleanup_avimux (GstElement * avimux, const gchar * sinkname)
+{
+ GST_DEBUG ("cleanup_avimux");
+ gst_element_set_state (avimux, GST_STATE_NULL);
+
+ gst_pad_set_active (mysrcpad, FALSE);
+ gst_pad_set_active (mysinkpad, FALSE);
+ teardown_src_pad (avimux, sinkname);
+ gst_check_teardown_sink_pad (avimux);
+ gst_check_teardown_element (avimux);
+}
+
+static void
+check_avimux_pad (GstStaticPadTemplate * srctemplate,
+ const gchar * src_caps_string, const gchar * chunk_id,
+ const gchar * sinkname)
+{
+ GstElement *avimux;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ int num_buffers;
+ int i;
+ guint8 data0[4] = "RIFF";
+ guint8 data1[8] = "AVI LIST";
+ guint8 data2[8] = "hdrlavih";
+ guint8 data3[4] = "LIST";
+ guint8 data4[8] = "strlstrh";
+ guint8 data5[4] = "strf";
+ guint8 data6[4] = "LIST";
+ guint8 data7[4] = "movi";
+
+ avimux = setup_avimux (srctemplate, sinkname);
+ fail_unless (gst_element_set_state (avimux,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ inbuffer = gst_buffer_new_and_alloc (1);
+ caps = gst_caps_from_string (src_caps_string);
+ gst_check_setup_events (mysrcpad, avimux, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ num_buffers = g_list_length (buffers);
+ /* at least expect avi header, chunk header, chunk and padding */
+ fail_unless (num_buffers >= 4);
+
+ for (i = 0; i < num_buffers; ++i) {
+ outbuffer = GST_BUFFER (buffers->data);
+ fail_if (outbuffer == NULL);
+ buffers = g_list_remove (buffers, outbuffer);
+
+ switch (i) {
+ case 0:{ /* check riff header */
+ /* avi header */
+ GstMapInfo map;
+ gsize size;
+ guint8 *data;
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ data = map.data;
+ size = map.size;
+
+ fail_unless (memcmp (data, data0, sizeof (data0)) == 0);
+ fail_unless (memcmp (data + 8, data1, sizeof (data1)) == 0);
+ fail_unless (memcmp (data + 20, data2, sizeof (data2)) == 0);
+ /* video or audio header */
+ data += 32 + 56;
+ fail_unless (memcmp (data, data3, sizeof (data3)) == 0);
+ fail_unless (memcmp (data + 8, data4, sizeof (data4)) == 0);
+ fail_unless (memcmp (data + 76, data5, sizeof (data5)) == 0);
+ /* avi data header */
+ data = map.data;
+ data += size - 12;
+ fail_unless (memcmp (data, data6, sizeof (data6)) == 0);
+ data += 8;
+ fail_unless (memcmp (data, data7, sizeof (data7)) == 0);
+ gst_buffer_unmap (outbuffer, &map);
+ break;
+ }
+ case 1: /* chunk header */
+ fail_unless (gst_buffer_get_size (outbuffer) == 8);
+ fail_unless (gst_buffer_memcmp (outbuffer, 0, chunk_id, 4) == 0);
+ break;
+ case 2:
+ fail_unless (gst_buffer_get_size (outbuffer) == 1);
+ break;
+ case 3: /* buffer we put in, must be padded to even size */
+ fail_unless (gst_buffer_get_size (outbuffer) == 1);
+ break;
+ default:
+ break;
+ }
+
+ ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
+ gst_buffer_unref (outbuffer);
+ outbuffer = NULL;
+ }
+
+ cleanup_avimux (avimux, sinkname);
+ g_list_free (buffers);
+ buffers = NULL;
+}
+
+
+GST_START_TEST (test_video_pad)
+{
+ check_avimux_pad (&srcvideotemplate, VIDEO_CAPS_STRING, "00db", "video_%u");
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_audio_pad)
+{
+ check_avimux_pad (&srcaudiotemplate, AUDIO_CAPS_STRING, "00wb", "audio_%u");
+}
+
+GST_END_TEST;
+
+
+static Suite *
+avimux_suite (void)
+{
+ Suite *s = suite_create ("avimux");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_video_pad);
+ tcase_add_test (tc_chain, test_audio_pad);
+
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ int nf;
+
+ Suite *s = avimux_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ srunner_run_all (sr, CK_NORMAL);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}
diff --git a/tests/check/elements/avisubtitle.c b/tests/check/elements/avisubtitle.c
new file mode 100644
index 0000000..1b00b15
--- /dev/null
+++ b/tests/check/elements/avisubtitle.c
@@ -0,0 +1,263 @@
+/* GStreamer
+ *
+ * unit test for avisubtitle
+ *
+ * Copyright (C) <2007> Thijs Vermeir <thijsvermeir@gmail.com>
+ *
+ * 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.
+ */
+/* Element-Checklist-Version: 5 */
+
+#include <unistd.h>
+
+#include <gst/gst.h>
+#include <gst/check/gstcheck.h>
+
+GstPad *mysinkpad;
+GstPad *mysrcpad;
+
+guint8 avisub_utf_8_with_bom[] = {
+ 0x47, 0x41, 0x42, 0x32, 0x00, 0x02, 0x00, 0x10,
+ 0x00, 0x00, 0x00, 0x45, 0x00, 0x6e, 0x00, 0x67,
+ 0x00, 0x6c, 0x00, 0x69, 0x00, 0x73, 0x00, 0x68,
+ 0x00, 0x00, 0x00, 0x04, 0x00, 0x8e, 0x00, 0x00,
+ 0x00, 0xef, 0xbb, 0xbf, 0x31, 0x0d, 0x0a, 0x30,
+ 0x30, 0x3a, 0x30, 0x30, 0x3a, 0x30, 0x30, 0x2c,
+ 0x31, 0x30, 0x30, 0x20, 0x2d, 0x2d, 0x3e, 0x20,
+ 0x30, 0x30, 0x3a, 0x30, 0x30, 0x3a, 0x30, 0x32,
+ 0x2c, 0x30, 0x30, 0x30, 0x0d, 0x0a, 0x3c, 0x62,
+ 0x3e, 0x41, 0x6e, 0x20, 0x55, 0x54, 0x46, 0x38,
+ 0x20, 0x53, 0x75, 0x62, 0x74, 0x69, 0x74, 0x6c,
+ 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x42,
+ 0x4f, 0x4d, 0x3c, 0x2f, 0x62, 0x3e, 0x0d, 0x0a,
+ 0x0d, 0x0a, 0x32, 0x0d, 0x0a, 0x30, 0x30, 0x3a,
+ 0x30, 0x30, 0x3a, 0x30, 0x32, 0x2c, 0x31, 0x30,
+ 0x30, 0x20, 0x2d, 0x2d, 0x3e, 0x20, 0x30, 0x30,
+ 0x3a, 0x30, 0x30, 0x3a, 0x30, 0x34, 0x2c, 0x30,
+ 0x30, 0x30, 0x0d, 0x0a, 0x53, 0x6f, 0x6d, 0x65,
+ 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x6e, 0x6f,
+ 0x6e, 0x41, 0x53, 0x43, 0x49, 0x49, 0x20, 0x2d,
+ 0x20, 0xc2, 0xb5, 0xc3, 0xb6, 0xc3, 0xa4, 0xc3,
+ 0xbc, 0xc3, 0x9f, 0x0d, 0x0a, 0x0d, 0x0a
+};
+
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("application/x-subtitle")
+ );
+
+static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("application/x-subtitle-avi")
+ );
+
+static GstElement *
+setup_avisubtitle (void)
+{
+ GstElement *avisubtitle;
+ GstCaps *srccaps;
+
+ GST_DEBUG ("setup_avisubtitle");
+ avisubtitle = gst_check_setup_element ("avisubtitle");
+ mysinkpad = gst_check_setup_sink_pad (avisubtitle, &sink_template);
+ mysrcpad = gst_check_setup_src_pad (avisubtitle, &src_template);
+ gst_pad_set_active (mysinkpad, TRUE);
+ gst_pad_set_active (mysrcpad, TRUE);
+ srccaps = gst_caps_new_empty_simple ("application/x-subtitle-avi");
+ gst_check_setup_events (mysrcpad, avisubtitle, srccaps, GST_FORMAT_TIME);
+ gst_caps_unref (srccaps);
+ return avisubtitle;
+}
+
+static void
+cleanup_avisubtitle (GstElement * avisubtitle)
+{
+ gst_pad_set_active (mysinkpad, FALSE);
+ gst_pad_set_active (mysrcpad, FALSE);
+ gst_check_teardown_sink_pad (avisubtitle);
+ gst_check_teardown_src_pad (avisubtitle);
+ gst_check_teardown_element (avisubtitle);
+}
+
+static void
+check_wrong_buffer (guint8 * data, guint length)
+{
+ GstBuffer *buffer = gst_buffer_new_allocate (NULL, length, 0);
+ GstElement *avisubtitle = setup_avisubtitle ();
+
+ gst_buffer_fill (buffer, 0, data, length);
+ fail_unless (gst_element_set_state (avisubtitle,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+ gst_buffer_ref (buffer);
+ ASSERT_BUFFER_REFCOUNT (buffer, "inbuffer", 2);
+ /* push the broken buffer */
+ fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_ERROR,
+ "accepted a broken buffer");
+ /* check if we have unreffed this buffer on failure */
+ ASSERT_BUFFER_REFCOUNT (buffer, "inbuffer", 1);
+ gst_buffer_unref (buffer);
+ fail_unless (gst_element_set_state (avisubtitle,
+ GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS, "could not set to null");
+ cleanup_avisubtitle (avisubtitle);
+}
+
+static void
+check_correct_buffer (guint8 * src_data, guint src_size, guint8 * dst_data,
+ guint dst_size)
+{
+ GstBuffer *buffer = gst_buffer_new_allocate (NULL, src_size, 0);
+ GstBuffer *newBuffer;
+ GstElement *avisubtitle = setup_avisubtitle ();
+ GstEvent *event;
+
+ fail_unless (g_list_length (buffers) == 0, "Buffers list needs to be empty");
+ gst_buffer_fill (buffer, 0, src_data, src_size);
+ fail_unless (gst_element_set_state (avisubtitle,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+ ASSERT_BUFFER_REFCOUNT (buffer, "inbuffer", 1);
+ event = gst_event_new_seek (1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
+ GST_SEEK_TYPE_SET, 2 * GST_SECOND, GST_SEEK_TYPE_SET, 5 * GST_SECOND);
+ fail_unless (gst_element_send_event (avisubtitle, event) == FALSE,
+ "Seeking is not possible when there is no buffer yet");
+ fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK,
+ "not accepted a correct buffer");
+ /* we gave away our reference to the buffer, don't assume anything */
+ buffer = NULL;
+ /* a new buffer is created in the list */
+ fail_unless (g_list_length (buffers) == 1,
+ "No new buffer in the buffers list");
+ event = gst_event_new_seek (1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
+ GST_SEEK_TYPE_SET, 2 * GST_SECOND, GST_SEEK_TYPE_SET, 5 * GST_SECOND);
+ fail_unless (gst_element_send_event (avisubtitle, event) == TRUE,
+ "seeking should be working now");
+ fail_unless (g_list_length (buffers) == 2,
+ "After seeking we need another buffer in the buffers");
+ newBuffer = GST_BUFFER (buffers->data);
+ buffers = g_list_remove (buffers, newBuffer);
+ fail_unless (g_list_length (buffers) == 1, "Buffers list needs to be empty");
+ fail_unless (gst_buffer_get_size (newBuffer) == dst_size,
+ "size of the new buffer is wrong ( %d != %d)",
+ gst_buffer_get_size (newBuffer), dst_size);
+ fail_unless (gst_buffer_memcmp (newBuffer, 0, dst_data, dst_size) == 0,
+ "data of the buffer is not correct");
+ gst_buffer_unref (newBuffer);
+ /* free the buffer from seeking */
+ gst_buffer_unref (GST_BUFFER (buffers->data));
+ buffers = g_list_remove (buffers, buffers->data);
+ fail_unless (gst_element_set_state (avisubtitle,
+ GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS, "could not set to null");
+ cleanup_avisubtitle (avisubtitle);
+}
+
+
+GST_START_TEST (test_avisubtitle_negative)
+{
+ guint8 wrong_magic[] =
+ { 0x47, 0x41, 0x41, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00
+ };
+ guint8 wrong_fixed_word_2[] = {
+ 0x47, 0x41, 0x42, 0x32, 0x00, 0x02, 0x01, 0x10,
+ 0x00, 0x00, 0x00, 0x45, 0x00, 0x6e, 0x00, 0x67,
+ 0x00, 0x6c, 0x00, 0x69, 0x00, 0x73, 0x00, 0x68
+ };
+ guint8 wrong_length_after_name[] = {
+ 0x47, 0x41, 0x42, 0x32, 0x00, 0x02, 0x00, 0x10,
+ 0x00, 0x00, 0x00, 0x45, 0x00, 0x6e, 0x00, 0x67,
+ 0x00, 0x6c, 0x00, 0x69, 0x00, 0x73, 0x00, 0x68
+ };
+ guint8 wrong_fixed_word_4[] = {
+ 0x47, 0x41, 0x42, 0x32, 0x00, 0x02, 0x00, 0x10,
+ 0x00, 0x00, 0x00, 0x45, 0x00, 0x6e, 0x00, 0x67,
+ 0x00, 0x6c, 0x00, 0x69, 0x00, 0x73, 0x00, 0x68,
+ 0x00, 0x00, 0x00, 0x04, 0x01, 0x8e, 0x00, 0x00,
+ 0x00, 0xef, 0xbb, 0xbf, 0x31, 0x0d, 0x0a, 0x30
+ };
+ guint8 wrong_total_length[] = {
+ 0x47, 0x41, 0x42, 0x32, 0x00, 0x02, 0x00, 0x10,
+ 0x00, 0x00, 0x00, 0x45, 0x00, 0x6e, 0x00, 0x67,
+ 0x00, 0x6c, 0x00, 0x69, 0x00, 0x73, 0x00, 0x68,
+ 0x00, 0x00, 0x00, 0x04, 0x00, 0x8e, 0x00, 0x00,
+ 0x00, 0xef, 0xbb, 0xbf, 0x31, 0x0d, 0x0a, 0x30
+ };
+ /* size of the buffer must be larger than 11 */
+ check_wrong_buffer (avisub_utf_8_with_bom, 11);
+ /* buffer must start with 'GAB2\0' */
+ check_wrong_buffer (wrong_magic, 14);
+ /* next word must be 2 */
+ check_wrong_buffer (wrong_fixed_word_2, 24);
+ /* length must be larger than the length of the name + 17 */
+ check_wrong_buffer (wrong_length_after_name, 24);
+ /* next word must be 4 */
+ check_wrong_buffer (wrong_fixed_word_4, 36);
+ /* check wrong total length */
+ check_wrong_buffer (wrong_total_length, 36);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_avisubtitle_positive)
+{
+ guint8 avisub_utf_8_without_bom[] = {
+ 0x47, 0x41, 0x42, 0x32, 0x00, 0x02, 0x00, 0x10,
+ 0x00, 0x00, 0x00, 0x45, 0x00, 0x6e, 0x00, 0x67,
+ 0x00, 0x6c, 0x00, 0x69, 0x00, 0x73, 0x00, 0x68,
+ 0x00, 0x00, 0x00, 0x04, 0x00, 0x8b, 0x00, 0x00,
+ 0x00, 0x31, 0x0d, 0x0a, 0x30,
+ 0x30, 0x3a, 0x30, 0x30, 0x3a, 0x30, 0x30, 0x2c,
+ 0x31, 0x30, 0x30, 0x20, 0x2d, 0x2d, 0x3e, 0x20,
+ 0x30, 0x30, 0x3a, 0x30, 0x30, 0x3a, 0x30, 0x32,
+ 0x2c, 0x30, 0x30, 0x30, 0x0d, 0x0a, 0x3c, 0x62,
+ 0x3e, 0x41, 0x6e, 0x20, 0x55, 0x54, 0x46, 0x38,
+ 0x20, 0x53, 0x75, 0x62, 0x74, 0x69, 0x74, 0x6c,
+ 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x42,
+ 0x4f, 0x4d, 0x3c, 0x2f, 0x62, 0x3e, 0x0d, 0x0a,
+ 0x0d, 0x0a, 0x32, 0x0d, 0x0a, 0x30, 0x30, 0x3a,
+ 0x30, 0x30, 0x3a, 0x30, 0x32, 0x2c, 0x31, 0x30,
+ 0x30, 0x20, 0x2d, 0x2d, 0x3e, 0x20, 0x30, 0x30,
+ 0x3a, 0x30, 0x30, 0x3a, 0x30, 0x34, 0x2c, 0x30,
+ 0x30, 0x30, 0x0d, 0x0a, 0x53, 0x6f, 0x6d, 0x65,
+ 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x6e, 0x6f,
+ 0x6e, 0x41, 0x53, 0x43, 0x49, 0x49, 0x20, 0x2d,
+ 0x20, 0xc2, 0xb5, 0xc3, 0xb6, 0xc3, 0xa4, 0xc3,
+ 0xbc, 0xc3, 0x9f, 0x0d, 0x0a, 0x0d, 0x0a
+ };
+ check_correct_buffer (avisub_utf_8_with_bom, 175, avisub_utf_8_with_bom + 36,
+ 139);
+ check_correct_buffer (avisub_utf_8_without_bom, 172,
+ avisub_utf_8_without_bom + 33, 139);
+}
+
+GST_END_TEST;
+
+static Suite *
+avisubtitle_suite (void)
+{
+ Suite *s = suite_create ("avisubtitle");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_avisubtitle_negative);
+ tcase_add_test (tc_chain, test_avisubtitle_positive);
+
+ return s;
+}
+
+GST_CHECK_MAIN (avisubtitle);
diff --git a/tests/check/elements/capssetter.c b/tests/check/elements/capssetter.c
new file mode 100644
index 0000000..17e563b
--- /dev/null
+++ b/tests/check/elements/capssetter.c
@@ -0,0 +1,231 @@
+/* GStreamer
+ *
+ * unit test for capssetter
+ *
+ * Copyright (C) <2009> Mark Nauwelaerts <mnauw@users.sourceforge.net>
+ *
+ * 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.
+ */
+
+#include <unistd.h>
+
+#include <gst/check/gstcheck.h>
+
+
+/* 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;
+
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+static GstElement *
+setup_capssetter (void)
+{
+ GstElement *capssetter;
+
+ GST_DEBUG ("setup_capssetter");
+
+ capssetter = gst_check_setup_element ("capssetter");
+ mysrcpad = gst_check_setup_src_pad (capssetter, &srctemplate);
+ mysinkpad = gst_check_setup_sink_pad (capssetter, &sinktemplate);
+ gst_pad_set_active (mysrcpad, TRUE);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ return capssetter;
+}
+
+static void
+cleanup_capssetter (GstElement * capssetter)
+{
+ GST_DEBUG ("cleanup_capssetter");
+
+ gst_pad_set_active (mysrcpad, FALSE);
+ gst_pad_set_active (mysinkpad, FALSE);
+ gst_check_teardown_src_pad (capssetter);
+ gst_check_teardown_sink_pad (capssetter);
+ gst_check_teardown_element (capssetter);
+}
+
+static void
+push_and_test (GstCaps * prop_caps, gboolean join, gboolean replace,
+ GstCaps * in_caps, GstCaps * out_caps)
+{
+ GstElement *capssetter;
+ GstBuffer *buffer;
+ GstCaps *current_out;
+
+ capssetter = setup_capssetter ();
+ fail_unless (gst_element_set_state (capssetter,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ g_object_set (capssetter, "join", join, NULL);
+ g_object_set (capssetter, "replace", replace, NULL);
+ g_object_set (capssetter, "caps", prop_caps, NULL);
+ gst_caps_unref (prop_caps);
+
+ buffer = gst_buffer_new_and_alloc (4);
+ ASSERT_BUFFER_REFCOUNT (buffer, "buffer", 1);
+ gst_buffer_fill (buffer, 0, "data", 4);
+
+ gst_check_setup_events (mysrcpad, capssetter, in_caps, GST_FORMAT_TIME);
+ gst_caps_unref (in_caps);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK,
+ "Failed pushing buffer to capssetter");
+
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()) == TRUE);
+
+ /* ... but it should end up being collected on the global buffer list */
+ fail_unless (g_list_length (buffers) == 1);
+ buffer = g_list_first (buffers)->data;
+ ASSERT_BUFFER_REFCOUNT (buffer, "buffer", 1);
+
+ current_out = gst_pad_get_current_caps (mysinkpad);
+ fail_unless (gst_caps_is_equal (out_caps, current_out));
+ gst_caps_unref (current_out);
+ gst_caps_unref (out_caps);
+
+ /* cleanup */
+ cleanup_capssetter (capssetter);
+}
+
+#define SRC_WIDTH 8
+#define SRC_HEIGHT 12
+
+static GstCaps *
+make_src_caps (void)
+{
+ return gst_caps_new_simple ("video/x-foo", "width", G_TYPE_INT, SRC_WIDTH,
+ "height", G_TYPE_INT, SRC_HEIGHT, NULL);
+}
+
+/* don't try these caps mutations at home, folks */
+
+GST_START_TEST (test_n_join_n_replace)
+{
+ GstCaps *in_caps, *prop_caps, *out_caps;
+
+ in_caps = make_src_caps ();
+ prop_caps = gst_caps_new_simple ("video/x-bar",
+ "width", G_TYPE_INT, 2 * SRC_WIDTH, NULL);
+ out_caps = gst_caps_new_simple ("video/x-bar",
+ "width", G_TYPE_INT, 2 * SRC_WIDTH,
+ "height", G_TYPE_INT, SRC_HEIGHT, NULL);
+ push_and_test (prop_caps, FALSE, FALSE, in_caps, out_caps);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_n_join_replace)
+{
+ GstCaps *in_caps, *prop_caps, *out_caps;
+
+ in_caps = make_src_caps ();
+ prop_caps = gst_caps_new_simple ("video/x-bar",
+ "width", G_TYPE_INT, 2 * SRC_WIDTH, NULL);
+ out_caps = gst_caps_copy (prop_caps);
+ push_and_test (prop_caps, FALSE, TRUE, in_caps, out_caps);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_join_n_replace_n_match)
+{
+ GstCaps *in_caps, *prop_caps, *out_caps;
+
+ /* non joining caps */
+ in_caps = make_src_caps ();
+ prop_caps = gst_caps_new_simple ("video/x-bar",
+ "width", G_TYPE_INT, 2 * SRC_WIDTH, NULL);
+ out_caps = gst_caps_copy (in_caps);
+ push_and_test (prop_caps, TRUE, FALSE, in_caps, out_caps);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_join_n_replace_match)
+{
+ GstCaps *in_caps, *prop_caps, *out_caps;
+
+ /* joining caps */
+ in_caps = make_src_caps ();
+ prop_caps = gst_caps_new_simple ("video/x-foo",
+ "width", G_TYPE_INT, 2 * SRC_WIDTH, NULL);
+ out_caps = gst_caps_new_simple ("video/x-foo",
+ "width", G_TYPE_INT, 2 * SRC_WIDTH,
+ "height", G_TYPE_INT, SRC_HEIGHT, NULL);
+ push_and_test (prop_caps, TRUE, FALSE, in_caps, out_caps);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_join_replace_n_match)
+{
+ GstCaps *in_caps, *prop_caps, *out_caps;
+
+ /* non joining caps */
+ in_caps = make_src_caps ();
+ prop_caps = gst_caps_new_simple ("video/x-bar",
+ "width", G_TYPE_INT, 2 * SRC_WIDTH, NULL);
+ out_caps = gst_caps_copy (in_caps);
+ push_and_test (prop_caps, TRUE, TRUE, in_caps, out_caps);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_join_replace_match)
+{
+ GstCaps *in_caps, *prop_caps, *out_caps;
+
+ /* joining caps */
+ in_caps = make_src_caps ();
+ prop_caps = gst_caps_new_simple ("video/x-foo",
+ "width", G_TYPE_INT, 2 * SRC_WIDTH, NULL);
+ out_caps = gst_caps_copy (prop_caps);
+ push_and_test (prop_caps, TRUE, TRUE, in_caps, out_caps);
+}
+
+GST_END_TEST;
+
+static Suite *
+capssetter_suite (void)
+{
+ Suite *s = suite_create ("capssetter");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_n_join_n_replace);
+ tcase_add_test (tc_chain, test_n_join_replace);
+ tcase_add_test (tc_chain, test_join_n_replace_n_match);
+ tcase_add_test (tc_chain, test_join_n_replace_match);
+ tcase_add_test (tc_chain, test_join_replace_n_match);
+ tcase_add_test (tc_chain, test_join_replace_match);
+
+ return s;
+}
+
+GST_CHECK_MAIN (capssetter);
diff --git a/tests/check/elements/deinterlace.c b/tests/check/elements/deinterlace.c
new file mode 100644
index 0000000..c9da2dd
--- /dev/null
+++ b/tests/check/elements/deinterlace.c
@@ -0,0 +1,463 @@
+/* GStreamer unit tests for the deinterlace element
+ * Copyright (C) 2010 Thiago Santos <thiago.sousa.santos@collabora.co.uk>
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <gst/check/gstcheck.h>
+#include <gst/video/video.h>
+
+static gboolean
+gst_caps_is_interlaced (GstCaps * caps)
+{
+ GstVideoInfo info;
+
+ fail_unless (gst_caps_is_fixed (caps));
+ fail_unless (gst_video_info_from_caps (&info, caps));
+
+ return GST_VIDEO_INFO_IS_INTERLACED (&info);
+}
+
+GST_START_TEST (test_create_and_unref)
+{
+ GstElement *deinterlace;
+
+ deinterlace = gst_element_factory_make ("deinterlace", NULL);
+ fail_unless (deinterlace != NULL);
+
+ gst_element_set_state (deinterlace, GST_STATE_NULL);
+ gst_object_unref (deinterlace);
+}
+
+GST_END_TEST;
+
+#define CAPS_VIDEO_COMMON \
+ "width=(int)800, height=(int)600, framerate=(fraction)15/1"
+
+#define CAPS_IMAGE_COMMON \
+ "width=(int)3200, height=(int)3400, framerate=(fraction)0/1"
+
+#define CAPS_YUY2 \
+ "video/x-raw, " \
+ CAPS_VIDEO_COMMON ", " \
+ "format=(string)YUY2"
+
+#define CAPS_YUY2_INTERLACED \
+ CAPS_YUY2 ", " \
+ "interlace-mode=interleaved"
+
+#define CAPS_YVYU \
+ "video/x-raw, " \
+ CAPS_VIDEO_COMMON ", " \
+ "format=(string)YVYU"
+
+#define CAPS_YVYU_INTERLACED \
+ CAPS_YVYU ", " \
+ "interlace-mode=interleaved"
+
+#define CAPS_YUY2_IMAGE \
+ "video/x-raw, " \
+ CAPS_IMAGE_COMMON ", " \
+ "format=(string)YUY2"
+
+#define CAPS_YUY2_INTERLACED_IMAGE \
+ CAPS_YUY2_IMAGE ", " \
+ "interlace-mode=interleaved"
+
+#define CAPS_YVYU_IMAGE \
+ "video/x-raw, " \
+ CAPS_IMAGE_COMMON ", " \
+ "format=(string)YVYU"
+
+#define CAPS_YVYU_INTERLACED_IMAGE \
+ CAPS_YVYU_IMAGE ", " \
+ "interlace-mode=interleaved"
+
+static GstElement *deinterlace;
+static GstPad *srcpad;
+static GstPad *sinkpad;
+
+static GstElement *pipeline;
+
+/* sets up deinterlace and shortcut pointers to its pads */
+static void
+setup_deinterlace (void)
+{
+ deinterlace = gst_element_factory_make ("deinterlace", NULL);
+ fail_unless (deinterlace != NULL);
+
+ sinkpad = gst_element_get_static_pad (deinterlace, "sink");
+ fail_unless (sinkpad != NULL);
+ srcpad = gst_element_get_static_pad (deinterlace, "src");
+ fail_unless (srcpad != NULL);
+}
+
+/* sets up a basic test pipeline containing:
+ *
+ * videotestsrc ! capsfilter ! deinterlace ! fakesink
+ *
+ * The parameters set the capsfilter caps and the num-buffers
+ * property of videotestsrc
+ *
+ * It is useful for adding buffer probes to deinterlace pads
+ * and validating inputs/outputs
+ */
+static void
+setup_test_pipeline (gint mode, GstCaps * infiltercaps, GstCaps * outfiltercaps,
+ gint numbuffers)
+{
+ GstElement *src;
+ GstElement *infilter;
+ GstElement *outfilter;
+ GstElement *sink;
+
+ setup_deinterlace ();
+
+ pipeline = gst_pipeline_new ("pipeline");
+ src = gst_element_factory_make ("videotestsrc", NULL);
+ infilter = gst_element_factory_make ("capsfilter", "infilter");
+ outfilter = gst_element_factory_make ("capsfilter", "outfilter");
+ sink = gst_element_factory_make ("fakesink", NULL);
+ fail_if (src == NULL);
+ fail_if (infilter == NULL);
+ fail_if (outfilter == NULL);
+ fail_if (sink == NULL);
+
+ fail_unless (gst_bin_add (GST_BIN (pipeline), src));
+ fail_unless (gst_bin_add (GST_BIN (pipeline), infilter));
+ fail_unless (gst_bin_add (GST_BIN (pipeline), deinterlace));
+ fail_unless (gst_bin_add (GST_BIN (pipeline), outfilter));
+ fail_unless (gst_bin_add (GST_BIN (pipeline), sink));
+
+ /* set the properties */
+ g_object_set (deinterlace, "mode", mode, NULL);
+ if (numbuffers > 0)
+ g_object_set (src, "num-buffers", numbuffers, NULL);
+ if (infiltercaps)
+ g_object_set (infilter, "caps", infiltercaps, NULL);
+ if (outfiltercaps)
+ g_object_set (outfilter, "caps", outfiltercaps, NULL);
+
+ fail_unless (gst_element_link_many (src, infilter, deinterlace, outfilter,
+ sink, NULL));
+ if (infiltercaps)
+ gst_caps_unref (infiltercaps);
+ if (outfiltercaps)
+ gst_caps_unref (outfiltercaps);
+}
+
+/*
+ * Checks if 2 buffers are equal
+ *
+ * Equals means same data
+ */
+static gboolean
+test_buffer_equals (GstBuffer * buf_a, GstBuffer * buf_b)
+{
+ GstMapInfo m1, m2;
+ gboolean res = FALSE;
+
+ gst_buffer_map (buf_a, &m1, GST_MAP_READ);
+ gst_buffer_map (buf_b, &m2, GST_MAP_READ);
+
+ if (m1.size == m2.size) {
+ res = memcmp (m1.data, m2.data, m1.size) == 0;
+ }
+ gst_buffer_unmap (buf_a, &m1);
+ gst_buffer_unmap (buf_b, &m2);
+
+ return res;
+}
+
+static GstPadProbeReturn
+sinkpad_enqueue_buffer (GstPad * pad, GstPadProbeInfo * info, gpointer data)
+{
+ GQueue *queue = (GQueue *) data;
+ GstBuffer *buf = GST_PAD_PROBE_INFO_BUFFER (info);
+
+ /* enqueue a copy for being compared later */
+ g_queue_push_tail (queue, gst_buffer_copy (buf));
+
+ return GST_PAD_PROBE_OK;
+}
+
+/*
+ * pad buffer probe that compares the buffer with the top one
+ * in the GQueue passed as the user data
+ */
+static GstPadProbeReturn
+srcpad_dequeue_and_compare_buffer (GstPad * pad, GstPadProbeInfo * info,
+ gpointer data)
+{
+ GQueue *queue = (GQueue *) data;
+ GstBuffer *buf = GST_PAD_PROBE_INFO_BUFFER (info);
+ GstBuffer *queue_buf;
+
+ queue_buf = (GstBuffer *) g_queue_pop_head (queue);
+ fail_if (queue_buf == NULL);
+
+ fail_unless (test_buffer_equals (buf, queue_buf));
+
+ gst_buffer_unref (queue_buf);
+
+ return GST_PAD_PROBE_OK;
+}
+
+/*
+ * Utility function that sets up a pipeline with deinterlace for
+ * validanting that it operates in passthrough mode when receiving
+ * data with 'infiltercaps' as the input caps and operating in 'mode' mode
+ */
+static void
+deinterlace_check_passthrough (gint mode, const gchar * infiltercaps)
+{
+ GstMessage *msg;
+ GQueue *queue;
+ GstCaps *incaps = NULL;
+
+ if (infiltercaps)
+ incaps = gst_caps_from_string (infiltercaps);
+
+ setup_test_pipeline (mode, incaps, NULL, 20);
+
+ queue = g_queue_new ();
+
+ /* set up probes for testing */
+ gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_BUFFER, sinkpad_enqueue_buffer,
+ queue, NULL);
+ gst_pad_add_probe (srcpad, GST_PAD_PROBE_TYPE_BUFFER,
+ srcpad_dequeue_and_compare_buffer, queue, NULL);
+
+ fail_unless (gst_element_set_state (pipeline, GST_STATE_PLAYING) !=
+ GST_STATE_CHANGE_FAILURE);
+
+ msg = gst_bus_poll (GST_ELEMENT_BUS (pipeline),
+ GST_MESSAGE_ERROR | GST_MESSAGE_EOS, -1);
+ if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
+ GST_ERROR ("ERROR: %" GST_PTR_FORMAT, msg);
+ fail ("Unexpected error message");
+ }
+ gst_message_unref (msg);
+
+ /* queue should be empty */
+ fail_unless (g_queue_is_empty (queue));
+
+ fail_unless (gst_element_set_state (pipeline, GST_STATE_NULL) ==
+ GST_STATE_CHANGE_SUCCESS);
+ gst_object_unref (pipeline);
+ gst_object_unref (sinkpad);
+ gst_object_unref (srcpad);
+ g_queue_free (queue);
+}
+
+/*
+ * Sets the caps on deinterlace sinkpad and validates the
+ * caps that is set on the srcpad
+ */
+static void
+deinterlace_set_caps_and_check (GstCaps * input, gboolean must_deinterlace)
+{
+ GstCaps *othercaps = NULL;
+ GstSegment segment;
+
+ gst_pad_send_event (sinkpad, gst_event_new_stream_start ("test"));
+ fail_unless (gst_pad_set_caps (sinkpad, input));
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ gst_pad_send_event (sinkpad, gst_event_new_segment (&segment));
+
+ g_object_get (srcpad, "caps", &othercaps, NULL);
+
+ if (must_deinterlace) {
+ fail_if (gst_caps_is_interlaced (othercaps));
+ } else {
+ GstStructure *s;
+
+ fail_unless (gst_caps_is_interlaced (input) ==
+ gst_caps_is_interlaced (othercaps));
+
+ othercaps = gst_caps_make_writable (othercaps);
+ s = gst_caps_get_structure (othercaps, 0);
+ gst_structure_remove_field (s, "interlace-mode");
+
+ input = gst_caps_make_writable (input);
+ s = gst_caps_get_structure (input, 0);
+ gst_structure_remove_field (s, "interlace-mode");
+
+ fail_unless (gst_caps_is_equal (input, othercaps));
+ }
+ gst_caps_unref (input);
+ gst_caps_unref (othercaps);
+}
+
+static void
+deinterlace_set_string_caps_and_check (const gchar * input,
+ gboolean must_deinterlace)
+{
+ deinterlace_set_caps_and_check (gst_caps_from_string (input),
+ must_deinterlace);
+}
+
+GST_START_TEST (test_mode_auto_accept_caps)
+{
+ setup_deinterlace ();
+
+ /* auto mode */
+ g_object_set (deinterlace, "mode", 0, NULL);
+ fail_unless (gst_element_set_state (deinterlace, GST_STATE_PLAYING) ==
+ GST_STATE_CHANGE_SUCCESS);
+
+ /* try to set non interlaced caps */
+ deinterlace_set_string_caps_and_check (CAPS_YVYU, FALSE);
+ deinterlace_set_string_caps_and_check (CAPS_YUY2, FALSE);
+ deinterlace_set_string_caps_and_check (CAPS_YVYU_IMAGE, FALSE);
+ deinterlace_set_string_caps_and_check (CAPS_YUY2_IMAGE, FALSE);
+
+ /* now try to set interlaced caps */
+ deinterlace_set_string_caps_and_check (CAPS_YVYU_INTERLACED, TRUE);
+ deinterlace_set_string_caps_and_check (CAPS_YUY2_INTERLACED, TRUE);
+ deinterlace_set_string_caps_and_check (CAPS_YVYU_INTERLACED_IMAGE, TRUE);
+ deinterlace_set_string_caps_and_check (CAPS_YUY2_INTERLACED_IMAGE, TRUE);
+
+ /* cleanup */
+ gst_object_unref (sinkpad);
+ gst_object_unref (srcpad);
+ fail_unless (gst_element_set_state (deinterlace, GST_STATE_NULL) ==
+ GST_STATE_CHANGE_SUCCESS);
+ gst_object_unref (deinterlace);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_mode_forced_accept_caps)
+{
+ setup_deinterlace ();
+
+ /* forced mode */
+ g_object_set (deinterlace, "mode", 1, NULL);
+ fail_unless (gst_element_set_state (deinterlace, GST_STATE_PLAYING) ==
+ GST_STATE_CHANGE_SUCCESS);
+
+ /* try to set non interlaced caps */
+ deinterlace_set_string_caps_and_check (CAPS_YVYU, TRUE);
+ deinterlace_set_string_caps_and_check (CAPS_YUY2, TRUE);
+ deinterlace_set_string_caps_and_check (CAPS_YVYU_IMAGE, TRUE);
+ deinterlace_set_string_caps_and_check (CAPS_YUY2_IMAGE, TRUE);
+
+ /* now try to set interlaced caps */
+ deinterlace_set_string_caps_and_check (CAPS_YVYU_INTERLACED, TRUE);
+ deinterlace_set_string_caps_and_check (CAPS_YUY2_INTERLACED, TRUE);
+ deinterlace_set_string_caps_and_check (CAPS_YVYU_INTERLACED_IMAGE, TRUE);
+ deinterlace_set_string_caps_and_check (CAPS_YUY2_INTERLACED_IMAGE, TRUE);
+
+ /* cleanup */
+ gst_object_unref (sinkpad);
+ gst_object_unref (srcpad);
+ fail_unless (gst_element_set_state (deinterlace, GST_STATE_NULL) ==
+ GST_STATE_CHANGE_SUCCESS);
+ gst_object_unref (deinterlace);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_mode_disabled_accept_caps)
+{
+ setup_deinterlace ();
+
+ /* disabled mode */
+ g_object_set (deinterlace, "mode", 2, NULL);
+ fail_unless (gst_element_set_state (deinterlace, GST_STATE_PLAYING) ==
+ GST_STATE_CHANGE_SUCCESS);
+
+ /* try to set non interlaced caps */
+ deinterlace_set_string_caps_and_check (CAPS_YVYU, FALSE);
+ deinterlace_set_string_caps_and_check (CAPS_YUY2, FALSE);
+ deinterlace_set_string_caps_and_check (CAPS_YVYU_IMAGE, FALSE);
+ deinterlace_set_string_caps_and_check (CAPS_YUY2_IMAGE, FALSE);
+
+ /* now try to set interlaced caps */
+ deinterlace_set_string_caps_and_check (CAPS_YVYU_INTERLACED, FALSE);
+ deinterlace_set_string_caps_and_check (CAPS_YUY2_INTERLACED, FALSE);
+ deinterlace_set_string_caps_and_check (CAPS_YVYU_INTERLACED_IMAGE, FALSE);
+ deinterlace_set_string_caps_and_check (CAPS_YUY2_INTERLACED_IMAGE, FALSE);
+
+ /* cleanup */
+ gst_object_unref (sinkpad);
+ gst_object_unref (srcpad);
+ fail_unless (gst_element_set_state (deinterlace, GST_STATE_NULL) ==
+ GST_STATE_CHANGE_SUCCESS);
+ gst_object_unref (deinterlace);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_mode_disabled_passthrough)
+{
+ /* 2 is disabled mode */
+ deinterlace_check_passthrough (2, CAPS_YUY2_INTERLACED);
+ deinterlace_check_passthrough (2, CAPS_YVYU_INTERLACED);
+ deinterlace_check_passthrough (2, CAPS_YUY2);
+ deinterlace_check_passthrough (2, CAPS_YVYU);
+
+ deinterlace_check_passthrough (2, CAPS_YUY2_INTERLACED_IMAGE);
+ deinterlace_check_passthrough (2, CAPS_YVYU_INTERLACED_IMAGE);
+ deinterlace_check_passthrough (2, CAPS_YUY2_IMAGE);
+ deinterlace_check_passthrough (2, CAPS_YVYU_IMAGE);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_mode_auto_deinterlaced_passthrough)
+{
+ /* 0 is auto mode */
+ deinterlace_check_passthrough (0, CAPS_YUY2);
+ deinterlace_check_passthrough (0, CAPS_YVYU);
+ deinterlace_check_passthrough (0, CAPS_YUY2_IMAGE);
+ deinterlace_check_passthrough (0, CAPS_YVYU_IMAGE);
+}
+
+GST_END_TEST;
+
+static Suite *
+deinterlace_suite (void)
+{
+ Suite *s = suite_create ("deinterlace");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_set_timeout (tc_chain, 180);
+
+ if (!gst_registry_check_feature_version (gst_registry_get (), "deinterlace",
+ GST_VERSION_MAJOR, GST_VERSION_MINOR, GST_VERSION_MICRO)) {
+ GST_ERROR ("FIXME: port deinterlace element");
+ return s;
+ }
+
+ tcase_add_test (tc_chain, test_create_and_unref);
+ tcase_add_test (tc_chain, test_mode_auto_accept_caps);
+ tcase_add_test (tc_chain, test_mode_forced_accept_caps);
+ tcase_add_test (tc_chain, test_mode_disabled_accept_caps);
+ tcase_add_test (tc_chain, test_mode_disabled_passthrough);
+ tcase_add_test (tc_chain, test_mode_auto_deinterlaced_passthrough);
+
+ return s;
+}
+
+GST_CHECK_MAIN (deinterlace);
diff --git a/tests/check/elements/deinterleave.c b/tests/check/elements/deinterleave.c
new file mode 100644
index 0000000..2ad17b3
--- /dev/null
+++ b/tests/check/elements/deinterleave.c
@@ -0,0 +1,637 @@
+/* GStreamer unit tests for the interleave element
+ * Copyright (C) 2008 Sebastian Dröge <slomo@circular-chaos.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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <gst/audio/audio.h>
+#include <gst/check/gstcheck.h>
+#include <gst/audio/audio.h>
+
+GST_START_TEST (test_create_and_unref)
+{
+ GstElement *deinterleave;
+
+ deinterleave = gst_element_factory_make ("deinterleave", NULL);
+ fail_unless (deinterleave != NULL);
+
+ gst_element_set_state (deinterleave, GST_STATE_NULL);
+ gst_object_unref (deinterleave);
+}
+
+GST_END_TEST;
+
+static GstPad *mysrcpad, **mysinkpads;
+static gint nsinkpads;
+static GstBus *bus;
+static GstElement *deinterleave;
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "format = (string) " GST_AUDIO_NE (F32) ", "
+ "channels = (int) 1, layout = (string) {interleaved, non-interleaved}, rate = (int) {32000, 48000}"));
+
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "format = (string) " GST_AUDIO_NE (F32) ", "
+ "channels = (int) { 2, 3 }, layout = (string) interleaved, rate = (int) {32000, 48000}"));
+
+#define CAPS_32khz \
+ "audio/x-raw, " \
+ "format = (string) "GST_AUDIO_NE (F32) ", " \
+ "channels = (int) 2, layout = (string) interleaved, " \
+ "rate = (int) 32000"
+
+#define CAPS_48khz \
+ "audio/x-raw, " \
+ "format = (string) "GST_AUDIO_NE (F32) ", " \
+ "channels = (int) 2, layout = (string) interleaved, " \
+ "rate = (int) 48000"
+
+#define CAPS_48khz_3CH \
+ "audio/x-raw, " \
+ "format = (string) "GST_AUDIO_NE (F32) ", " \
+ "channels = (int) 3, layout = (string) interleaved, " \
+ "rate = (int) 48000"
+
+static GstFlowReturn
+deinterleave_chain_func (GstPad * pad, GstObject * parent, GstBuffer * buffer)
+{
+ gint i;
+ GstMapInfo map;
+ gfloat *indata;
+
+ fail_unless (GST_IS_BUFFER (buffer));
+ gst_buffer_map (buffer, &map, GST_MAP_READ);
+ indata = (gfloat *) map.data;
+ fail_unless_equals_int (map.size, 48000 * sizeof (gfloat));
+ fail_unless (indata != NULL);
+
+ if (strcmp (GST_PAD_NAME (pad), "sink0") == 0) {
+ for (i = 0; i < 48000; i++)
+ fail_unless_equals_float (indata[i], -1.0);
+ } else if (strcmp (GST_PAD_NAME (pad), "sink1") == 0) {
+ for (i = 0; i < 48000; i++)
+ fail_unless_equals_float (indata[i], 1.0);
+ } else {
+ g_assert_not_reached ();
+ }
+ gst_buffer_unmap (buffer, &map);
+ gst_buffer_unref (buffer);
+
+ return GST_FLOW_OK;
+}
+
+static void
+deinterleave_pad_added (GstElement * src, GstPad * pad, gpointer data)
+{
+ gchar *name;
+ gint link = GPOINTER_TO_INT (data);
+
+ if (nsinkpads >= link)
+ return;
+
+ name = g_strdup_printf ("sink%d", nsinkpads);
+
+ mysinkpads[nsinkpads] =
+ gst_pad_new_from_static_template (&sinktemplate, name);
+ g_free (name);
+ fail_if (mysinkpads[nsinkpads] == NULL);
+
+ gst_pad_set_chain_function (mysinkpads[nsinkpads], deinterleave_chain_func);
+ fail_unless (gst_pad_link (pad, mysinkpads[nsinkpads]) == GST_PAD_LINK_OK);
+ gst_pad_set_active (mysinkpads[nsinkpads], TRUE);
+ nsinkpads++;
+}
+
+GST_START_TEST (test_2_channels)
+{
+ GstPad *sinkpad;
+ gint i;
+ GstBuffer *inbuf;
+ GstCaps *caps;
+ gfloat *indata;
+ GstMapInfo map;
+ guint64 channel_mask = 0;
+
+ mysinkpads = g_new0 (GstPad *, 2);
+ nsinkpads = 0;
+
+ deinterleave = gst_element_factory_make ("deinterleave", NULL);
+ fail_unless (deinterleave != NULL);
+
+ mysrcpad = gst_pad_new_from_static_template (&srctemplate, "src");
+ fail_unless (mysrcpad != NULL);
+ gst_pad_set_active (mysrcpad, TRUE);
+
+ caps = gst_caps_from_string (CAPS_48khz);
+ channel_mask |=
+ G_GUINT64_CONSTANT (1) << GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
+ channel_mask |=
+ G_GUINT64_CONSTANT (1) << GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
+ gst_caps_set_simple (caps, "channel-mask", GST_TYPE_BITMASK, channel_mask,
+ NULL);
+
+ gst_check_setup_events (mysrcpad, deinterleave, caps, GST_FORMAT_TIME);
+
+ sinkpad = gst_element_get_static_pad (deinterleave, "sink");
+ fail_unless (sinkpad != NULL);
+ fail_unless (gst_pad_link (mysrcpad, sinkpad) == GST_PAD_LINK_OK);
+ g_object_unref (sinkpad);
+
+ g_signal_connect (deinterleave, "pad-added",
+ G_CALLBACK (deinterleave_pad_added), GINT_TO_POINTER (2));
+
+ bus = gst_bus_new ();
+ gst_element_set_bus (deinterleave, bus);
+
+ fail_unless (gst_element_set_state (deinterleave,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS);
+
+ inbuf = gst_buffer_new_and_alloc (2 * 48000 * sizeof (gfloat));
+ inbuf = gst_buffer_make_writable (inbuf);
+ gst_buffer_map (inbuf, &map, GST_MAP_WRITE);
+ indata = (gfloat *) map.data;
+ for (i = 0; i < 2 * 48000; i += 2) {
+ indata[i] = -1.0;
+ indata[i + 1] = 1.0;
+ }
+ gst_buffer_unmap (inbuf, &map);
+
+ fail_unless (gst_pad_push (mysrcpad, inbuf) == GST_FLOW_OK);
+
+ fail_unless (gst_element_set_state (deinterleave,
+ GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS);
+
+ for (i = 0; i < nsinkpads; i++)
+ g_object_unref (mysinkpads[i]);
+ g_free (mysinkpads);
+ mysinkpads = NULL;
+
+ g_object_unref (deinterleave);
+ g_object_unref (bus);
+ gst_caps_unref (caps);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_2_channels_1_linked)
+{
+ GstPad *sinkpad;
+ gint i;
+ GstBuffer *inbuf;
+ GstCaps *caps;
+ gfloat *indata;
+ GstMapInfo map;
+ guint64 channel_mask = 0;
+
+ nsinkpads = 0;
+ mysinkpads = g_new0 (GstPad *, 2);
+
+ deinterleave = gst_element_factory_make ("deinterleave", NULL);
+ fail_unless (deinterleave != NULL);
+
+ mysrcpad = gst_pad_new_from_static_template (&srctemplate, "src");
+ fail_unless (mysrcpad != NULL);
+ gst_pad_set_active (mysrcpad, TRUE);
+
+ caps = gst_caps_from_string (CAPS_48khz);
+ channel_mask |=
+ G_GUINT64_CONSTANT (1) << GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
+ channel_mask |=
+ G_GUINT64_CONSTANT (1) << GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
+ gst_caps_set_simple (caps, "channel-mask", GST_TYPE_BITMASK, channel_mask,
+ NULL);
+
+ gst_check_setup_events (mysrcpad, deinterleave, caps, GST_FORMAT_TIME);
+
+ sinkpad = gst_element_get_static_pad (deinterleave, "sink");
+ fail_unless (sinkpad != NULL);
+ fail_unless (gst_pad_link (mysrcpad, sinkpad) == GST_PAD_LINK_OK);
+ g_object_unref (sinkpad);
+
+ g_signal_connect (deinterleave, "pad-added",
+ G_CALLBACK (deinterleave_pad_added), GINT_TO_POINTER (1));
+
+ bus = gst_bus_new ();
+ gst_element_set_bus (deinterleave, bus);
+
+ fail_unless (gst_element_set_state (deinterleave,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS);
+
+ inbuf = gst_buffer_new_and_alloc (2 * 48000 * sizeof (gfloat));
+ inbuf = gst_buffer_make_writable (inbuf);
+ gst_buffer_map (inbuf, &map, GST_MAP_WRITE);
+ indata = (gfloat *) map.data;
+ for (i = 0; i < 2 * 48000; i += 2) {
+ indata[i] = -1.0;
+ indata[i + 1] = 1.0;
+ }
+ gst_buffer_unmap (inbuf, &map);
+
+ fail_unless (gst_pad_push (mysrcpad, inbuf) == GST_FLOW_OK);
+
+ fail_unless (gst_element_set_state (deinterleave,
+ GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS);
+
+ for (i = 0; i < nsinkpads; i++)
+ g_object_unref (mysinkpads[i]);
+ g_free (mysinkpads);
+ mysinkpads = NULL;
+
+ g_object_unref (deinterleave);
+ g_object_unref (bus);
+ gst_caps_unref (caps);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_2_channels_caps_change)
+{
+ GstPad *sinkpad;
+ GstCaps *caps, *caps2;
+ gint i;
+ GstBuffer *inbuf;
+ gfloat *indata;
+ GstMapInfo map;
+ guint64 channel_mask;
+
+ nsinkpads = 0;
+ mysinkpads = g_new0 (GstPad *, 2);
+
+ deinterleave = gst_element_factory_make ("deinterleave", NULL);
+ fail_unless (deinterleave != NULL);
+
+ mysrcpad = gst_pad_new_from_static_template (&srctemplate, "src");
+ fail_unless (mysrcpad != NULL);
+
+ gst_pad_set_active (mysrcpad, TRUE);
+
+ caps = gst_caps_from_string (CAPS_48khz);
+ channel_mask = 0;
+ channel_mask |=
+ G_GUINT64_CONSTANT (1) << GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
+ channel_mask |=
+ G_GUINT64_CONSTANT (1) << GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
+ gst_caps_set_simple (caps, "channel-mask", GST_TYPE_BITMASK, channel_mask,
+ NULL);
+ gst_check_setup_events (mysrcpad, deinterleave, caps, GST_FORMAT_TIME);
+
+ sinkpad = gst_element_get_static_pad (deinterleave, "sink");
+ fail_unless (sinkpad != NULL);
+ fail_unless (gst_pad_link (mysrcpad, sinkpad) == GST_PAD_LINK_OK);
+ g_object_unref (sinkpad);
+
+ g_signal_connect (deinterleave, "pad-added",
+ G_CALLBACK (deinterleave_pad_added), GINT_TO_POINTER (2));
+
+ bus = gst_bus_new ();
+ gst_element_set_bus (deinterleave, bus);
+
+ fail_unless (gst_element_set_state (deinterleave,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS);
+
+ inbuf = gst_buffer_new_and_alloc (2 * 48000 * sizeof (gfloat));
+ inbuf = gst_buffer_make_writable (inbuf);
+ gst_buffer_map (inbuf, &map, GST_MAP_WRITE);
+ indata = (gfloat *) map.data;
+ for (i = 0; i < 2 * 48000; i += 2) {
+ indata[i] = -1.0;
+ indata[i + 1] = 1.0;
+ }
+ gst_buffer_unmap (inbuf, &map);
+
+ fail_unless (gst_pad_push (mysrcpad, inbuf) == GST_FLOW_OK);
+
+ caps2 = gst_caps_from_string (CAPS_32khz);
+ channel_mask = 0;
+ channel_mask |=
+ G_GUINT64_CONSTANT (1) << GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
+ channel_mask |=
+ G_GUINT64_CONSTANT (1) << GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
+ gst_caps_set_simple (caps2, "channel-mask", GST_TYPE_BITMASK, channel_mask,
+ NULL);
+ gst_pad_set_caps (mysrcpad, caps2);
+
+ inbuf = gst_buffer_new_and_alloc (2 * 48000 * sizeof (gfloat));
+ inbuf = gst_buffer_make_writable (inbuf);
+ gst_buffer_map (inbuf, &map, GST_MAP_WRITE);
+ indata = (gfloat *) map.data;
+ for (i = 0; i < 2 * 48000; i += 2) {
+ indata[i] = -1.0;
+ indata[i + 1] = 1.0;
+ }
+ gst_buffer_unmap (inbuf, &map);
+
+ /* Should work fine because the caps changed in a compatible way */
+ fail_unless (gst_pad_push (mysrcpad, inbuf) == GST_FLOW_OK);
+
+ gst_caps_unref (caps2);
+
+ caps2 = gst_caps_from_string (CAPS_48khz_3CH);
+ channel_mask = 0;
+ channel_mask |=
+ G_GUINT64_CONSTANT (1) << GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
+ channel_mask |=
+ G_GUINT64_CONSTANT (1) << GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
+ channel_mask |=
+ G_GUINT64_CONSTANT (1) << GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER;
+ gst_caps_set_simple (caps2, "channel-mask", GST_TYPE_BITMASK, channel_mask,
+ NULL);
+ gst_pad_set_caps (mysrcpad, caps2);
+
+ inbuf = gst_buffer_new_and_alloc (3 * 48000 * sizeof (gfloat));
+ inbuf = gst_buffer_make_writable (inbuf);
+ gst_buffer_map (inbuf, &map, GST_MAP_WRITE);
+ indata = (gfloat *) map.data;
+ for (i = 0; i < 3 * 48000; i += 3) {
+ indata[i] = -1.0;
+ indata[i + 1] = 1.0;
+ indata[i + 2] = 0.0;
+ }
+ gst_buffer_unmap (inbuf, &map);
+
+ /* Should break because the caps changed in an incompatible way */
+ fail_if (gst_pad_push (mysrcpad, inbuf) == GST_FLOW_OK);
+
+ fail_unless (gst_element_set_state (deinterleave,
+ GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS);
+
+ for (i = 0; i < nsinkpads; i++)
+ g_object_unref (mysinkpads[i]);
+ g_free (mysinkpads);
+ mysinkpads = NULL;
+
+ g_object_unref (deinterleave);
+ g_object_unref (bus);
+ gst_caps_unref (caps);
+ gst_caps_unref (caps2);
+}
+
+GST_END_TEST;
+
+
+#define SAMPLES_PER_BUFFER 10
+#define NUM_CHANNELS 8
+#define SAMPLE_RATE 44100
+
+static guint pads_created;
+
+static void
+set_channel_positions (GstCaps * caps, int channels,
+ GstAudioChannelPosition * channelpositions)
+{
+ int c;
+ guint64 channel_mask = 0;
+
+ for (c = 0; c < channels; c++)
+ channel_mask |= G_GUINT64_CONSTANT (1) << channelpositions[c];
+
+ gst_caps_set_simple (caps, "channel-mask", GST_TYPE_BITMASK, channel_mask,
+ NULL);
+}
+
+static void
+src_handoff_float32_8ch (GstElement * src, GstBuffer * buf, GstPad * pad,
+ gpointer user_data)
+{
+ gfloat *data, *p;
+ guint size, i, c;
+
+ size = sizeof (gfloat) * SAMPLES_PER_BUFFER * NUM_CHANNELS;
+ data = p = (gfloat *) g_malloc (size);
+
+ for (i = 0; i < SAMPLES_PER_BUFFER; ++i) {
+ for (c = 0; c < NUM_CHANNELS; ++c) {
+ *p = (gfloat) ((i * NUM_CHANNELS) + c);
+ ++p;
+ }
+ }
+
+ if (gst_buffer_n_memory (buf)) {
+ gst_buffer_replace_memory_range (buf, 0, -1,
+ gst_memory_new_wrapped (0, data, size, 0, size, data, g_free));
+ } else {
+ gst_buffer_insert_memory (buf, 0,
+ gst_memory_new_wrapped (0, data, size, 0, size, data, g_free));
+ }
+ GST_BUFFER_OFFSET (buf) = 0;
+ GST_BUFFER_TIMESTAMP (buf) = 0;
+}
+
+static GstPadProbeReturn
+src_event_probe (GstPad * pad, GstPadProbeInfo * info, gpointer userdata)
+{
+ GstAudioChannelPosition layout[NUM_CHANNELS];
+ GstCaps *caps;
+ guint i;
+
+ if ((info->type & GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM)
+ && GST_EVENT_TYPE (info->data) == GST_EVENT_STREAM_START) {
+ gst_pad_remove_probe (pad, info->id);
+
+ caps = gst_caps_new_simple ("audio/x-raw",
+ "format", G_TYPE_STRING, GST_AUDIO_NE (F32),
+ "channels", G_TYPE_INT, NUM_CHANNELS,
+ "layout", G_TYPE_STRING, "interleaved",
+ "rate", G_TYPE_INT, SAMPLE_RATE, NULL);
+
+ for (i = 0; i < NUM_CHANNELS; ++i)
+ layout[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT + i;
+
+ set_channel_positions (caps, NUM_CHANNELS, layout);
+ gst_pad_set_caps (pad, caps);
+ gst_caps_unref (caps);
+ }
+
+ return GST_PAD_PROBE_OK;
+}
+
+static GstPadProbeReturn
+float_buffer_check_probe (GstPad * pad, GstPadProbeInfo * info,
+ gpointer userdata)
+{
+ GstMapInfo map;
+ gfloat *data;
+ guint padnum, numpads;
+ guint num, i;
+ GstCaps *caps;
+ GstStructure *s;
+ GstAudioChannelPosition *pos;
+ gint channels;
+ GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER (info);
+ GstAudioInfo audio_info;
+ guint pad_id = GPOINTER_TO_UINT (userdata);
+
+ fail_unless_equals_int (sscanf (GST_PAD_NAME (pad), "src_%u", &padnum), 1);
+
+ numpads = pads_created;
+
+ /* Check caps */
+ caps = gst_pad_get_current_caps (pad);
+ fail_unless (caps != NULL);
+ s = gst_caps_get_structure (caps, 0);
+ fail_unless (gst_structure_get_int (s, "channels", &channels));
+ fail_unless_equals_int (channels, 1);
+
+ gst_audio_info_init (&audio_info);
+ fail_unless (gst_audio_info_from_caps (&audio_info, caps));
+
+ pos = audio_info.position;
+ fail_unless (pos != NULL
+ && pos[0] == GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT + pad_id);
+ gst_caps_unref (caps);
+
+ gst_buffer_map (buffer, &map, GST_MAP_READ);
+ data = (gfloat *) map.data;
+ num = map.size / sizeof (gfloat);
+
+ /* Check buffer content */
+ for (i = 0; i < num; ++i) {
+ guint val, rest;
+
+ val = (guint) data[i];
+ GST_LOG ("%s[%u]: %8f", GST_PAD_NAME (pad), i, data[i]);
+ /* can't use the modulo operator in the assertion statement, since due to
+ * the way it gets expanded it would be interpreted as a printf operator
+ * in the failure case, which will result in segfaults */
+ rest = val % numpads;
+ /* check that the first channel is on pad src0, the second on src1 etc. */
+ fail_unless_equals_int (rest, padnum);
+ }
+ gst_buffer_unmap (buffer, &map);
+
+ return GST_PAD_PROBE_OK; /* don't drop data */
+}
+
+static void
+pad_added_setup_data_check_float32_8ch_cb (GstElement * deinterleave,
+ GstPad * pad, GstElement * pipeline)
+{
+ GstElement *queue, *sink;
+ GstPad *sinkpad;
+
+ queue = gst_element_factory_make ("queue", NULL);
+ fail_unless (queue != NULL);
+
+ sink = gst_element_factory_make ("fakesink", NULL);
+ fail_unless (sink != NULL);
+
+ gst_bin_add_many (GST_BIN (pipeline), queue, sink, NULL);
+ gst_element_link_pads_full (queue, "src", sink, "sink",
+ GST_PAD_LINK_CHECK_NOTHING);
+
+ sinkpad = gst_element_get_static_pad (queue, "sink");
+
+ fail_unless_equals_int (gst_pad_link (pad, sinkpad), GST_PAD_LINK_OK);
+ gst_object_unref (sinkpad);
+
+ gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BUFFER, float_buffer_check_probe,
+ GUINT_TO_POINTER (pads_created), NULL);
+
+ gst_element_set_state (sink, GST_STATE_PLAYING);
+ gst_element_set_state (queue, GST_STATE_PLAYING);
+
+ GST_LOG ("new pad: %s", GST_PAD_NAME (pad));
+ ++pads_created;
+}
+
+static GstElement *
+make_fake_src_8chans_float32 (void)
+{
+ GstElement *src;
+ GstPad *pad;
+
+ src = gst_element_factory_make ("fakesrc", "src");
+ fail_unless (src != NULL, "failed to create fakesrc element");
+
+ g_object_set (src, "num-buffers", 1, NULL);
+ g_object_set (src, "signal-handoffs", TRUE, NULL);
+
+ g_signal_connect (src, "handoff", G_CALLBACK (src_handoff_float32_8ch), NULL);
+
+ pad = gst_element_get_static_pad (src, "src");
+ gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, src_event_probe,
+ NULL, NULL);
+ gst_object_unref (pad);
+
+ return src;
+}
+
+GST_START_TEST (test_8_channels_float32)
+{
+ GstElement *pipeline, *src, *deinterleave;
+ GstMessage *msg;
+
+ pipeline = (GstElement *) gst_pipeline_new ("pipeline");
+ fail_unless (pipeline != NULL, "failed to create pipeline");
+
+ src = make_fake_src_8chans_float32 ();
+
+ deinterleave = gst_element_factory_make ("deinterleave", "deinterleave");
+ fail_unless (deinterleave != NULL, "failed to create deinterleave element");
+ g_object_set (deinterleave, "keep-positions", TRUE, NULL);
+
+ gst_bin_add_many (GST_BIN (pipeline), src, deinterleave, NULL);
+
+ fail_unless (gst_element_link (src, deinterleave),
+ "failed to link src <=> deinterleave");
+
+ g_signal_connect (deinterleave, "pad-added",
+ G_CALLBACK (pad_added_setup_data_check_float32_8ch_cb), pipeline);
+
+ pads_created = 0;
+
+ gst_element_set_state (pipeline, GST_STATE_PLAYING);
+
+ msg = gst_bus_poll (GST_ELEMENT_BUS (pipeline), GST_MESSAGE_EOS, -1);
+ gst_message_unref (msg);
+
+ fail_unless_equals_int (pads_created, NUM_CHANNELS);
+
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+ gst_object_unref (pipeline);
+}
+
+GST_END_TEST;
+
+static Suite *
+deinterleave_suite (void)
+{
+ Suite *s = suite_create ("deinterleave");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_set_timeout (tc_chain, 180);
+ tcase_add_test (tc_chain, test_create_and_unref);
+ tcase_add_test (tc_chain, test_2_channels);
+ tcase_add_test (tc_chain, test_2_channels_1_linked);
+ tcase_add_test (tc_chain, test_2_channels_caps_change);
+ tcase_add_test (tc_chain, test_8_channels_float32);
+
+ return s;
+}
+
+GST_CHECK_MAIN (deinterleave);
diff --git a/tests/check/elements/dtmf.c b/tests/check/elements/dtmf.c
new file mode 100755
index 0000000..c2ae36d
--- /dev/null
+++ b/tests/check/elements/dtmf.c
@@ -0,0 +1,588 @@
+/* GStreamer
+ *
+ * unit test for dtmf elements
+ * Copyright (C) 2013 Collabora Ltd
+ * @author: Olivier Crete <olivier.crete@collabora.com>
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <gst/audio/audio.h>
+#include <gst/check/gstcheck.h>
+#include <gst/check/gsttestclock.h>
+#include <gst/rtp/gstrtpbuffer.h>
+
+
+/* Include this from the plugin to get the defines */
+
+#include "../../gst/dtmf/gstdtmfcommon.h"
+
+#define END_BIT (1<<7)
+
+static GstStaticPadTemplate audio_sink_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "format = (string) \"" GST_AUDIO_NE (S16) "\", "
+ "rate = " GST_AUDIO_RATE_RANGE ", " "channels = (int) 1")
+ );
+
+static GstStaticPadTemplate rtp_dtmf_src_template =
+GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("application/x-rtp, "
+ "media = (string) \"audio\", "
+ "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
+ "clock-rate = (int) [ 0, MAX ], "
+ "encoding-name = (string) \"TELEPHONE-EVENT\"")
+ );
+
+
+static void
+check_get_dtmf_event_message (GstBus * bus, gint number, gint volume)
+{
+ GstMessage *message;
+ gboolean have_message = FALSE;
+
+ while (!have_message &&
+ (message = gst_bus_pop_filtered (bus, GST_MESSAGE_ELEMENT)) != NULL) {
+ if (gst_message_has_name (message, "dtmf-event")) {
+ const GstStructure *s = gst_message_get_structure (message);
+ gint stype, snumber, smethod, svolume;
+
+ fail_unless (gst_structure_get (s,
+ "type", G_TYPE_INT, &stype,
+ "number", G_TYPE_INT, &snumber,
+ "method", G_TYPE_INT, &smethod,
+ "volume", G_TYPE_INT, &svolume, NULL));
+
+ fail_unless (stype == 1);
+ fail_unless (smethod == 1);
+ fail_unless (snumber == number);
+ fail_unless (svolume == volume);
+ have_message = TRUE;
+ }
+ gst_message_unref (message);
+ }
+
+ fail_unless (have_message);
+}
+
+static void
+check_no_dtmf_event_message (GstBus * bus)
+{
+ GstMessage *message;
+ gboolean have_message = FALSE;
+
+ while (!have_message &&
+ (message = gst_bus_pop_filtered (bus, GST_MESSAGE_ELEMENT)) != NULL) {
+ if (gst_message_has_name (message, "dtmf-event") ||
+ gst_message_has_name (message, "dtmf-event-processed") ||
+ gst_message_has_name (message, "dtmf-event-dropped")) {
+ have_message = TRUE;
+ }
+ gst_message_unref (message);
+ }
+
+ fail_unless (!have_message);
+}
+
+static void
+check_buffers_duration (GstClockTime expected_duration)
+{
+ GstClockTime duration = 0;
+
+ while (buffers) {
+ GstBuffer *buf = buffers->data;
+
+ buffers = g_list_delete_link (buffers, buffers);
+
+ fail_unless (GST_BUFFER_DURATION_IS_VALID (buf));
+ duration += GST_BUFFER_DURATION (buf);
+ gst_buffer_unref (buf);
+ }
+
+ fail_unless (duration == expected_duration);
+}
+
+static void
+send_rtp_packet (GstPad * src, guint timestamp, gboolean marker, gboolean end,
+ guint number, guint volume, guint duration)
+{
+ GstBuffer *buf;
+ GstRTPBuffer rtpbuf = GST_RTP_BUFFER_INIT;
+ gchar *payload;
+ static guint seqnum = 1;
+
+ buf = gst_rtp_buffer_new_allocate (4, 0, 0);
+ fail_unless (gst_rtp_buffer_map (buf, GST_MAP_READWRITE, &rtpbuf));
+ gst_rtp_buffer_set_seq (&rtpbuf, seqnum++);
+ gst_rtp_buffer_set_timestamp (&rtpbuf, timestamp);
+ gst_rtp_buffer_set_marker (&rtpbuf, marker);
+ payload = gst_rtp_buffer_get_payload (&rtpbuf);
+ payload[0] = number;
+ payload[1] = volume | (end ? END_BIT : 0);
+ GST_WRITE_UINT16_BE (payload + 2, duration);
+ gst_rtp_buffer_unmap (&rtpbuf);
+ fail_unless (gst_pad_push (src, buf) == GST_FLOW_OK);
+}
+
+GST_START_TEST (test_rtpdtmfdepay)
+{
+ GstElement *dtmfdepay;
+ GstPad *src, *sink;
+ GstBus *bus;
+ GstCaps *caps_in;
+ GstCaps *expected_caps_out;
+ GstCaps *caps_out;
+
+ dtmfdepay = gst_check_setup_element ("rtpdtmfdepay");
+ sink = gst_check_setup_sink_pad (dtmfdepay, &audio_sink_template);
+ src = gst_check_setup_src_pad (dtmfdepay, &rtp_dtmf_src_template);
+
+ bus = gst_bus_new ();
+ gst_element_set_bus (dtmfdepay, bus);
+
+ gst_pad_set_active (src, TRUE);
+ gst_pad_set_active (sink, TRUE);
+ gst_element_set_state (dtmfdepay, GST_STATE_PLAYING);
+
+
+ caps_in = gst_caps_new_simple ("application/x-rtp",
+ "encoding-name", G_TYPE_STRING, "TELEPHONE-EVENT",
+ "media", G_TYPE_STRING, "audio",
+ "clock-rate", G_TYPE_INT, 1000, "payload", G_TYPE_INT, 99, NULL);
+ gst_check_setup_events (src, dtmfdepay, caps_in, GST_FORMAT_TIME);
+ gst_caps_unref (caps_in);
+
+ caps_out = gst_pad_get_current_caps (sink);
+ expected_caps_out = gst_caps_new_simple ("audio/x-raw",
+ "format", G_TYPE_STRING, GST_AUDIO_NE (S16),
+ "rate", G_TYPE_INT, 1000, "channels", G_TYPE_INT, 1, NULL);
+ fail_unless (gst_caps_is_equal_fixed (caps_out, expected_caps_out));
+ gst_caps_unref (expected_caps_out);
+ gst_caps_unref (caps_out);
+
+ /* Single packet DTMF */
+ send_rtp_packet (src, 200, TRUE, TRUE, 1, 5, 250);
+ check_get_dtmf_event_message (bus, 1, 5);
+ check_buffers_duration (250 * GST_MSECOND);
+
+ /* Two packet DTMF */
+ send_rtp_packet (src, 800, TRUE, FALSE, 1, 5, 200);
+ send_rtp_packet (src, 800, FALSE, TRUE, 1, 5, 400);
+ check_buffers_duration (400 * GST_MSECOND);
+ check_get_dtmf_event_message (bus, 1, 5);
+
+ /* Long DTMF */
+ send_rtp_packet (src, 3000, TRUE, FALSE, 1, 5, 200);
+ check_get_dtmf_event_message (bus, 1, 5);
+ check_buffers_duration (200 * GST_MSECOND);
+ send_rtp_packet (src, 3000, FALSE, FALSE, 1, 5, 400);
+ check_no_dtmf_event_message (bus);
+ check_buffers_duration (200 * GST_MSECOND);
+ send_rtp_packet (src, 3000, FALSE, FALSE, 1, 5, 600);
+ check_no_dtmf_event_message (bus);
+ check_buffers_duration (200 * GST_MSECOND);
+
+ /* New without end to last */
+ send_rtp_packet (src, 4000, TRUE, TRUE, 1, 5, 250);
+ check_get_dtmf_event_message (bus, 1, 5);
+ check_buffers_duration (250 * GST_MSECOND);
+
+ check_no_dtmf_event_message (bus);
+ fail_unless (buffers == NULL);
+ gst_element_set_bus (dtmfdepay, NULL);
+ gst_object_unref (bus);
+
+ gst_pad_set_active (src, FALSE);
+ gst_pad_set_active (sink, FALSE);
+ gst_check_teardown_sink_pad (dtmfdepay);
+ gst_check_teardown_src_pad (dtmfdepay);
+ gst_check_teardown_element (dtmfdepay);
+}
+
+GST_END_TEST;
+
+
+static GstStaticPadTemplate rtp_dtmf_sink_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("application/x-rtp, "
+ "media = (string) \"audio\", "
+ "payload = (int) 99, "
+ "clock-rate = (int) 1000, "
+ "seqnum-offset = (uint) 333, "
+ "timestamp-offset = (uint) 666, "
+ "ssrc = (uint) 999, "
+ "maxptime = (uint) 20, encoding-name = (string) \"TELEPHONE-EVENT\"")
+ );
+
+GstElement *dtmfsrc;
+GstPad *sink;
+GstClock *testclock;
+GstBus *bus;
+
+static void
+check_message_structure (GstStructure * expected_s)
+{
+ GstMessage *message;
+ gboolean have_message = FALSE;
+
+ while (!have_message &&
+ (message = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
+ GST_MESSAGE_ELEMENT)) != NULL) {
+ if (gst_message_has_name (message, gst_structure_get_name (expected_s))) {
+ const GstStructure *s = gst_message_get_structure (message);
+
+ fail_unless (gst_structure_is_equal (s, expected_s));
+ have_message = TRUE;
+ }
+ gst_message_unref (message);
+ }
+
+ fail_unless (have_message);
+
+ gst_structure_free (expected_s);
+}
+
+static void
+check_rtp_buffer (GstClockTime ts, GstClockTime duration, gboolean start,
+ gboolean end, guint rtpts, guint ssrc, guint volume, guint number,
+ guint rtpduration)
+{
+ GstBuffer *buffer;
+ GstRTPBuffer rtpbuffer = GST_RTP_BUFFER_INIT;
+ gchar *payload;
+
+ g_mutex_lock (&check_mutex);
+ while (buffers == NULL)
+ g_cond_wait (&check_cond, &check_mutex);
+ g_mutex_unlock (&check_mutex);
+ fail_unless (buffers != NULL);
+
+ buffer = buffers->data;
+ buffers = g_list_delete_link (buffers, buffers);
+
+ fail_unless (GST_BUFFER_PTS (buffer) == ts);
+ fail_unless (GST_BUFFER_DURATION (buffer) == duration);
+
+ fail_unless (gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtpbuffer));
+ fail_unless (gst_rtp_buffer_get_marker (&rtpbuffer) == start);
+ fail_unless (gst_rtp_buffer_get_timestamp (&rtpbuffer) == rtpts);
+ payload = gst_rtp_buffer_get_payload (&rtpbuffer);
+
+ fail_unless (payload[0] == number);
+ fail_unless ((payload[1] & 0x7F) == volume);
+ fail_unless (! !(payload[1] & 0x80) == end);
+ fail_unless (GST_READ_UINT16_BE (payload + 2) == rtpduration);
+
+ gst_rtp_buffer_unmap (&rtpbuffer);
+ gst_buffer_unref (buffer);
+}
+
+gint method;
+
+static void
+setup_rtpdtmfsrc (void)
+{
+ testclock = gst_test_clock_new ();
+ bus = gst_bus_new ();
+
+ method = 1;
+ dtmfsrc = gst_check_setup_element ("rtpdtmfsrc");
+ sink = gst_check_setup_sink_pad (dtmfsrc, &rtp_dtmf_sink_template);
+ gst_element_set_bus (dtmfsrc, bus);
+ fail_unless (gst_element_set_clock (dtmfsrc, testclock));
+
+ gst_pad_set_active (sink, TRUE);
+ fail_unless (gst_element_set_state (dtmfsrc, GST_STATE_PLAYING) ==
+ GST_STATE_CHANGE_SUCCESS);
+}
+
+static void
+teardown_dtmfsrc (void)
+{
+ gst_object_unref (testclock);
+ gst_pad_set_active (sink, FALSE);
+ gst_element_set_bus (dtmfsrc, NULL);
+ gst_object_unref (bus);
+ gst_check_teardown_sink_pad (dtmfsrc);
+ gst_check_teardown_element (dtmfsrc);
+}
+
+GST_START_TEST (test_dtmfsrc_invalid_events)
+{
+ GstStructure *s;
+
+ /* Missing start */
+ s = gst_structure_new ("dtmf-event",
+ "type", G_TYPE_INT, 1, "number", G_TYPE_INT, 3,
+ "method", G_TYPE_INT, method, "volume", G_TYPE_INT, 8, NULL);
+ fail_unless (gst_pad_push_event (sink,
+ gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, s)) == FALSE);
+
+ /* Missing volume */
+ s = gst_structure_new ("dtmf-event",
+ "type", G_TYPE_INT, 1, "number", G_TYPE_INT, 3,
+ "method", G_TYPE_INT, method, "start", G_TYPE_BOOLEAN, TRUE, NULL);
+ fail_unless (gst_pad_push_event (sink,
+ gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, s)) == FALSE);
+
+ /* Missing number */
+ s = gst_structure_new ("dtmf-event",
+ "type", G_TYPE_INT, 1, "method", G_TYPE_INT, method,
+ "volume", G_TYPE_INT, 8, "start", G_TYPE_BOOLEAN, TRUE, NULL);
+ fail_unless (gst_pad_push_event (sink,
+ gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, s)) == FALSE);
+
+ /* Missing type */
+ s = gst_structure_new ("dtmf-event",
+ "number", G_TYPE_INT, 3, "method", G_TYPE_INT, method,
+ "volume", G_TYPE_INT, 8, "start", G_TYPE_BOOLEAN, TRUE, NULL);
+ fail_unless (gst_pad_push_event (sink,
+ gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, s)) == FALSE);
+
+ /* Stop before start */
+ s = gst_structure_new ("dtmf-event",
+ "type", G_TYPE_INT, 1, "number", G_TYPE_INT, 3,
+ "method", G_TYPE_INT, method, "volume", G_TYPE_INT, 8,
+ "start", G_TYPE_BOOLEAN, FALSE, NULL);
+ fail_unless (gst_pad_push_event (sink,
+ gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, s)) == FALSE);
+
+ gst_element_set_state (dtmfsrc, GST_STATE_NULL);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_rtpdtmfsrc_min_duration)
+{
+ GstStructure *s;
+ GstClockID id;
+ guint timestamp = 0;
+ GstCaps *expected_caps, *caps;
+
+ /* Minimum duration dtmf */
+
+ s = gst_structure_new ("dtmf-event",
+ "type", G_TYPE_INT, 1, "number", G_TYPE_INT, 3,
+ "method", G_TYPE_INT, 1, "volume", G_TYPE_INT, 8,
+ "start", G_TYPE_BOOLEAN, TRUE, NULL);
+ fail_unless (gst_pad_push_event (sink,
+ gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
+ gst_structure_copy (s))));
+ check_no_dtmf_event_message (bus);
+ gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (testclock), NULL);
+ fail_unless (buffers == NULL);
+ id = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (testclock));
+ fail_unless (gst_clock_id_get_time (id) == 0);
+ gst_clock_id_unref (id);
+ gst_structure_set_name (s, "dtmf-event-processed");
+ check_message_structure (s);
+
+ s = gst_structure_new ("dtmf-event",
+ "type", G_TYPE_INT, 1, "method", G_TYPE_INT, 1,
+ "start", G_TYPE_BOOLEAN, FALSE, NULL);
+ fail_unless (gst_pad_push_event (sink,
+ gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
+ gst_structure_copy (s))));
+
+ check_rtp_buffer (0, 20 * GST_MSECOND, TRUE, FALSE, 666, 999, 8, 3, 20);
+
+ for (timestamp = 20; timestamp < MIN_PULSE_DURATION + 20; timestamp += 20) {
+ gst_test_clock_advance_time (GST_TEST_CLOCK (testclock),
+ 20 * GST_MSECOND + 1);
+ gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (testclock), NULL);
+ fail_unless (buffers == NULL);
+ id = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (testclock));
+ fail_unless (gst_clock_id_get_time (id) == timestamp * GST_MSECOND);
+ gst_clock_id_unref (id);
+
+ if (timestamp < MIN_PULSE_DURATION) {
+ check_rtp_buffer (timestamp * GST_MSECOND, 20 * GST_MSECOND, FALSE,
+ FALSE, 666, 999, 8, 3, timestamp + 20);
+ check_no_dtmf_event_message (bus);
+ } else {
+ gst_structure_set_name (s, "dtmf-event-processed");
+ check_message_structure (s);
+ check_rtp_buffer (timestamp * GST_MSECOND,
+ (20 + MIN_INTER_DIGIT_INTERVAL) * GST_MSECOND, FALSE, TRUE, 666,
+ 999, 8, 3, timestamp + 20);
+ }
+
+ fail_unless (buffers == NULL);
+ }
+
+
+ fail_unless (gst_test_clock_peek_id_count (GST_TEST_CLOCK (testclock)) == 0);
+
+ /* caps check */
+
+ expected_caps = gst_caps_new_simple ("application/x-rtp",
+ "encoding-name", G_TYPE_STRING, "TELEPHONE-EVENT",
+ "media", G_TYPE_STRING, "audio",
+ "clock-rate", G_TYPE_INT, 1000, "payload", G_TYPE_INT, 99,
+ "seqnum-offset", G_TYPE_UINT, 333,
+ "timestamp-offset", G_TYPE_UINT, 666,
+ "ssrc", G_TYPE_UINT, 999, "ptime", G_TYPE_UINT, 20, NULL);
+ caps = gst_pad_get_current_caps (sink);
+ fail_unless (gst_caps_can_intersect (caps, expected_caps));
+ gst_caps_unref (caps);
+ gst_caps_unref (expected_caps);
+
+ gst_element_set_state (dtmfsrc, GST_STATE_NULL);
+
+ check_no_dtmf_event_message (bus);
+}
+
+GST_END_TEST;
+
+static GstStaticPadTemplate audio_dtmfsrc_sink_template =
+GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "format = (string) \"" GST_AUDIO_NE (S16) "\", "
+ "rate = (int) 8003, " "channels = (int) 1")
+ );
+static void
+setup_dtmfsrc (void)
+{
+ testclock = gst_test_clock_new ();
+ bus = gst_bus_new ();
+
+ method = 2;
+ dtmfsrc = gst_check_setup_element ("dtmfsrc");
+ sink = gst_check_setup_sink_pad (dtmfsrc, &audio_dtmfsrc_sink_template);
+ gst_element_set_bus (dtmfsrc, bus);
+ fail_unless (gst_element_set_clock (dtmfsrc, testclock));
+
+ gst_pad_set_active (sink, TRUE);
+ fail_unless (gst_element_set_state (dtmfsrc, GST_STATE_PLAYING) ==
+ GST_STATE_CHANGE_SUCCESS);
+}
+
+
+GST_START_TEST (test_dtmfsrc_min_duration)
+{
+ GstStructure *s;
+ GstClockID id;
+ GstClockTime timestamp = 0;
+ GstCaps *expected_caps, *caps;
+ guint interval;
+
+ g_object_get (dtmfsrc, "interval", &interval, NULL);
+ fail_unless (interval == 50);
+
+ /* Minimum duration dtmf */
+ gst_test_clock_set_time (GST_TEST_CLOCK (testclock), 0);
+
+ s = gst_structure_new ("dtmf-event",
+ "type", G_TYPE_INT, 1, "number", G_TYPE_INT, 3,
+ "method", G_TYPE_INT, 2, "volume", G_TYPE_INT, 8,
+ "start", G_TYPE_BOOLEAN, TRUE, NULL);
+ fail_unless (gst_pad_push_event (sink,
+ gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
+ gst_structure_copy (s))));
+
+ gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (testclock), NULL);
+ id = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (testclock));
+ fail_unless (gst_clock_id_get_time (id) == 0);
+ gst_clock_id_unref (id);
+
+ gst_structure_set_name (s, "dtmf-event-processed");
+ check_message_structure (s);
+
+ s = gst_structure_new ("dtmf-event",
+ "type", G_TYPE_INT, 1, "method", G_TYPE_INT, 2,
+ "start", G_TYPE_BOOLEAN, FALSE, NULL);
+ fail_unless (gst_pad_push_event (sink,
+ gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
+ gst_structure_copy (s))));
+
+ for (timestamp = interval * GST_MSECOND;
+ timestamp < (MIN_PULSE_DURATION + MIN_INTER_DIGIT_INTERVAL) *
+ GST_MSECOND; timestamp += GST_MSECOND * interval) {
+ gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (testclock), NULL);
+ gst_test_clock_advance_time (GST_TEST_CLOCK (testclock),
+ interval * GST_MSECOND);
+
+ id = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (testclock));
+ fail_unless (gst_clock_id_get_time (id) == timestamp);
+ gst_clock_id_unref (id);
+ }
+
+ gst_structure_set_name (s, "dtmf-event-processed");
+ check_message_structure (s);
+
+ check_buffers_duration ((MIN_PULSE_DURATION + MIN_INTER_DIGIT_INTERVAL) *
+ GST_MSECOND);
+
+ fail_unless (gst_test_clock_peek_id_count (GST_TEST_CLOCK (testclock)) == 0);
+
+ /* caps check */
+
+ expected_caps = gst_caps_new_simple ("audio/x-raw",
+ "format", G_TYPE_STRING, GST_AUDIO_NE (S16),
+ "rate", G_TYPE_INT, 8003, "channels", G_TYPE_INT, 1, NULL);
+ caps = gst_pad_get_current_caps (sink);
+ fail_unless (gst_caps_can_intersect (caps, expected_caps));
+ gst_caps_unref (caps);
+ gst_caps_unref (expected_caps);
+
+ gst_element_set_state (dtmfsrc, GST_STATE_NULL);
+
+ check_no_dtmf_event_message (bus);
+}
+
+GST_END_TEST;
+
+static Suite *
+dtmf_suite (void)
+{
+ Suite *s = suite_create ("dtmf");
+ TCase *tc;
+
+ tc = tcase_create ("rtpdtmfdepay");
+ tcase_add_test (tc, test_rtpdtmfdepay);
+ suite_add_tcase (s, tc);
+
+ tc = tcase_create ("rtpdtmfsrc");
+ tcase_add_checked_fixture (tc, setup_rtpdtmfsrc, teardown_dtmfsrc);
+ tcase_add_test (tc, test_dtmfsrc_invalid_events);
+ tcase_add_test (tc, test_rtpdtmfsrc_min_duration);
+ suite_add_tcase (s, tc);
+
+ tc = tcase_create ("dtmfsrc");
+ tcase_add_checked_fixture (tc, setup_dtmfsrc, teardown_dtmfsrc);
+ tcase_add_test (tc, test_dtmfsrc_invalid_events);
+ tcase_add_test (tc, test_dtmfsrc_min_duration);
+ suite_add_tcase (s, tc);
+
+ return s;
+}
+
+
+GST_CHECK_MAIN (dtmf);
diff --git a/tests/check/elements/equalizer.c b/tests/check/elements/equalizer.c
new file mode 100644
index 0000000..491e039
--- /dev/null
+++ b/tests/check/elements/equalizer.c
@@ -0,0 +1,395 @@
+/* GStreamer
+ *
+ * Copyright (C) 2008 Sebastian Dröge <slomo@circular-chaos.org>
+ *
+ * equalizer.c: Unit test for the equalizer element
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <gst/gst.h>
+#include <gst/audio/audio.h>
+#include <gst/base/gstbasetransform.h>
+#include <gst/check/gstcheck.h>
+
+#include <math.h>
+
+/* 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 */
+GstPad *mysrcpad, *mysinkpad;
+
+#define EQUALIZER_CAPS_STRING \
+ "audio/x-raw, " \
+ "format = (string) "GST_AUDIO_NE (F64) ", " \
+ "layout = (string) interleaved, " \
+ "channels = (int) 1, " \
+ "rate = (int) 48000"
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "format = (string) " GST_AUDIO_NE (F64) ", "
+ "layout = (string) interleaved, "
+ "channels = (int) 1, " "rate = (int) 48000")
+ );
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "format = (string) " GST_AUDIO_NE (F64) ", "
+ "layout = (string) interleaved, "
+ "channels = (int) 1, " "rate = (int) 48000")
+ );
+
+static GstElement *
+setup_equalizer (void)
+{
+ GstElement *equalizer;
+
+ GST_DEBUG ("setup_equalizer");
+ equalizer = gst_check_setup_element ("equalizer-nbands");
+ mysrcpad = gst_check_setup_src_pad (equalizer, &srctemplate);
+ mysinkpad = gst_check_setup_sink_pad (equalizer, &sinktemplate);
+ gst_pad_set_active (mysrcpad, TRUE);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ return equalizer;
+}
+
+static void
+cleanup_equalizer (GstElement * equalizer)
+{
+ GST_DEBUG ("cleanup_equalizer");
+
+ g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
+ g_list_free (buffers);
+ buffers = NULL;
+
+ gst_pad_set_active (mysrcpad, FALSE);
+ gst_pad_set_active (mysinkpad, FALSE);
+ gst_check_teardown_src_pad (equalizer);
+ gst_check_teardown_sink_pad (equalizer);
+ gst_check_teardown_element (equalizer);
+}
+
+GST_START_TEST (test_equalizer_5bands_passthrough)
+{
+ GstElement *equalizer;
+ GstBuffer *inbuffer;
+ GstCaps *caps;
+ gdouble *in, *res;
+ gint i;
+ GstMapInfo map;
+
+ equalizer = setup_equalizer ();
+ g_object_set (G_OBJECT (equalizer), "num-bands", 5, NULL);
+
+ fail_unless_equals_int (gst_child_proxy_get_children_count (GST_CHILD_PROXY
+ (equalizer)), 5);
+
+ fail_unless (gst_element_set_state (equalizer,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ inbuffer = gst_buffer_new_and_alloc (1024 * sizeof (gdouble));
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 1024; i++)
+ in[i] = g_random_double_range (-1.0, 1.0);
+ gst_buffer_unmap (inbuffer, &map);
+
+ caps = gst_caps_from_string (EQUALIZER_CAPS_STRING);
+ gst_check_setup_events (mysrcpad, equalizer, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()));
+ /* ... and puts a new buffer on the global list */
+ fail_unless (g_list_length (buffers) == 1);
+
+ gst_buffer_map (GST_BUFFER (buffers->data), &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+
+ for (i = 0; i < 1024; i++)
+ fail_unless_equals_float (in[i], res[i]);
+ gst_buffer_unmap (GST_BUFFER (buffers->data), &map);
+
+ /* cleanup */
+ cleanup_equalizer (equalizer);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_equalizer_5bands_minus_24)
+{
+ GstElement *equalizer;
+ GstBuffer *inbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms_in, rms_out;
+ gint i;
+ GstMapInfo map;
+
+ equalizer = setup_equalizer ();
+ g_object_set (G_OBJECT (equalizer), "num-bands", 5, NULL);
+
+ fail_unless_equals_int (gst_child_proxy_get_children_count (GST_CHILD_PROXY
+ (equalizer)), 5);
+
+ for (i = 0; i < 5; i++) {
+ GObject *band =
+ gst_child_proxy_get_child_by_index (GST_CHILD_PROXY (equalizer), i);
+ fail_unless (band != NULL);
+
+ g_object_set (band, "gain", -24.0, NULL);
+ g_object_unref (band);
+ }
+
+ fail_unless (gst_element_set_state (equalizer,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ inbuffer = gst_buffer_new_and_alloc (1024 * sizeof (gdouble));
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 1024; i++)
+ in[i] = g_random_double_range (-1.0, 1.0);
+ gst_buffer_unmap (inbuffer, &map);
+
+ rms_in = 0.0;
+ for (i = 0; i < 1024; i++)
+ rms_in += in[i] * in[i];
+ rms_in = sqrt (rms_in / 1024);
+
+ caps = gst_caps_from_string (EQUALIZER_CAPS_STRING);
+ gst_check_setup_events (mysrcpad, equalizer, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()));
+ /* ... and puts a new buffer on the global list */
+ fail_unless (g_list_length (buffers) == 1);
+
+ gst_buffer_map (GST_BUFFER (buffers->data), &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+
+ rms_out = 0.0;
+ for (i = 0; i < 1024; i++)
+ rms_out += res[i] * res[i];
+ rms_out = sqrt (rms_out / 1024);
+ gst_buffer_unmap (GST_BUFFER (buffers->data), &map);
+
+ fail_unless (rms_in > rms_out);
+
+ /* cleanup */
+ cleanup_equalizer (equalizer);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_equalizer_5bands_plus_12)
+{
+ GstElement *equalizer;
+ GstBuffer *inbuffer;
+ GstCaps *caps;
+ gdouble *in, *res, rms_in, rms_out;
+ gint i;
+ GstMapInfo map;
+
+ equalizer = setup_equalizer ();
+ g_object_set (G_OBJECT (equalizer), "num-bands", 5, NULL);
+
+ fail_unless_equals_int (gst_child_proxy_get_children_count (GST_CHILD_PROXY
+ (equalizer)), 5);
+
+ for (i = 0; i < 5; i++) {
+ GObject *band =
+ gst_child_proxy_get_child_by_index (GST_CHILD_PROXY (equalizer), i);
+ fail_unless (band != NULL);
+
+ g_object_set (band, "gain", 12.0, NULL);
+ g_object_unref (band);
+ }
+
+ fail_unless (gst_element_set_state (equalizer,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ inbuffer = gst_buffer_new_and_alloc (1024 * sizeof (gdouble));
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ in = (gdouble *) map.data;
+ for (i = 0; i < 1024; i++)
+ in[i] = g_random_double_range (-1.0, 1.0);
+ gst_buffer_unmap (inbuffer, &map);
+
+ rms_in = 0.0;
+ for (i = 0; i < 1024; i++)
+ rms_in += in[i] * in[i];
+ rms_in = sqrt (rms_in / 1024);
+
+ caps = gst_caps_from_string (EQUALIZER_CAPS_STRING);
+ gst_check_setup_events (mysrcpad, equalizer, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()));
+ /* ... and puts a new buffer on the global list */
+ fail_unless (g_list_length (buffers) == 1);
+
+ gst_buffer_map (GST_BUFFER (buffers->data), &map, GST_MAP_READ);
+ res = (gdouble *) map.data;
+
+ rms_out = 0.0;
+ for (i = 0; i < 1024; i++)
+ rms_out += res[i] * res[i];
+ rms_out = sqrt (rms_out / 1024);
+ gst_buffer_unmap (GST_BUFFER (buffers->data), &map);
+
+ fail_unless (rms_in < rms_out);
+
+ /* cleanup */
+ cleanup_equalizer (equalizer);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_equalizer_band_number_changing)
+{
+ GstElement *equalizer;
+ gint i;
+
+ equalizer = setup_equalizer ();
+
+ g_object_set (G_OBJECT (equalizer), "num-bands", 5, NULL);
+ fail_unless_equals_int (gst_child_proxy_get_children_count (GST_CHILD_PROXY
+ (equalizer)), 5);
+
+ for (i = 0; i < 5; i++) {
+ GObject *band;
+
+ band = gst_child_proxy_get_child_by_index (GST_CHILD_PROXY (equalizer), i);
+ fail_unless (band != NULL);
+ g_object_unref (band);
+ }
+
+ g_object_set (G_OBJECT (equalizer), "num-bands", 10, NULL);
+ fail_unless_equals_int (gst_child_proxy_get_children_count (GST_CHILD_PROXY
+ (equalizer)), 10);
+
+ for (i = 0; i < 10; i++) {
+ GObject *band;
+
+ band = gst_child_proxy_get_child_by_index (GST_CHILD_PROXY (equalizer), i);
+ fail_unless (band != NULL);
+ g_object_unref (band);
+ }
+
+ /* cleanup */
+ cleanup_equalizer (equalizer);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_equalizer_presets)
+{
+ GstElement *eq1, *eq2;
+ gint type;
+ gdouble gain, freq;
+
+ eq1 = gst_check_setup_element ("equalizer-nbands");
+ g_object_set (G_OBJECT (eq1), "num-bands", 3, NULL);
+
+ /* set properties to non-defaults */
+ gst_child_proxy_set ((GstChildProxy *) eq1,
+ "band0::type", 0, "band0::gain", -3.0, "band0::freq", 100.0,
+ "band1::type", 1, "band1::gain", +3.0, "band1::freq", 1000.0,
+ "band2::type", 2, "band2::gain", +9.0, "band2::freq", 10000.0, NULL);
+
+ /* save preset */
+ gst_preset_save_preset ((GstPreset *) eq1, "_testpreset_");
+ GST_INFO_OBJECT (eq1, "Preset saved");
+
+ eq2 = gst_check_setup_element ("equalizer-nbands");
+ g_object_set (G_OBJECT (eq2), "num-bands", 3, NULL);
+
+ /* load preset */
+ gst_preset_load_preset ((GstPreset *) eq2, "_testpreset_");
+ GST_INFO_OBJECT (eq1, "Preset loaded");
+
+ /* compare properties */
+ gst_child_proxy_get ((GstChildProxy *) eq2,
+ "band0::type", &type, "band0::gain", &gain, "band0::freq", &freq, NULL);
+ ck_assert_int_eq (type, 0);
+ fail_unless (gain == -3.0, NULL);
+ fail_unless (freq == 100.0, NULL);
+ gst_child_proxy_get ((GstChildProxy *) eq2,
+ "band1::type", &type, "band1::gain", &gain, "band1::freq", &freq, NULL);
+ ck_assert_int_eq (type, 1);
+ fail_unless (gain == +3.0, NULL);
+ fail_unless (freq == 1000.0, NULL);
+ gst_child_proxy_get ((GstChildProxy *) eq2,
+ "band2::type", &type, "band2::gain", &gain, "band2::freq", &freq, NULL);
+ ck_assert_int_eq (type, 2);
+ fail_unless (gain == +9.0, NULL);
+ fail_unless (freq == 10000.0, NULL);
+
+ gst_preset_delete_preset ((GstPreset *) eq1, "_testpreset_");
+ gst_check_teardown_element (eq1);
+ gst_check_teardown_element (eq2);
+}
+
+GST_END_TEST;
+
+
+static Suite *
+equalizer_suite (void)
+{
+ Suite *s = suite_create ("equalizer");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_equalizer_5bands_passthrough);
+ tcase_add_test (tc_chain, test_equalizer_5bands_minus_24);
+ tcase_add_test (tc_chain, test_equalizer_5bands_plus_12);
+ tcase_add_test (tc_chain, test_equalizer_band_number_changing);
+ tcase_add_test (tc_chain, test_equalizer_presets);
+
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ int nf;
+
+ Suite *s = equalizer_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ srunner_run_all (sr, CK_NORMAL);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}
diff --git a/tests/check/elements/flacparse.c b/tests/check/elements/flacparse.c
new file mode 100644
index 0000000..306f010
--- /dev/null
+++ b/tests/check/elements/flacparse.c
@@ -0,0 +1,300 @@
+/*
+ * GStreamer
+ *
+ * unit test for flacparse
+ *
+ * Copyright (C) 2010 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Stefan Kost <stefan.kost@nokia.com>
+ *
+ * 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.
+ */
+
+#include <gst/check/gstcheck.h>
+#include "parser.h"
+
+#define SRC_CAPS_TMPL "audio/x-flac, framed=(boolean)false"
+#define SINK_CAPS_TMPL "audio/x-flac, 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 */
+static guint8 streaminfo_header[] = {
+ 0x7f, 0x46, 0x4c, 0x41, 0x43, 0x01, 0x00, 0x00,
+ 0x02, 0x66, 0x4c, 0x61, 0x43, 0x00, 0x00, 0x00,
+ 0x22, 0x12, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0a, 0xc4, 0x40, 0xf0, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00
+};
+
+static guint8 comment_header[] = {
+ 0x84, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+};
+
+static guint8 flac_frame[] = {
+ 0xff, 0xf8, 0xa9, 0x08, 0x00, 0x50, 0x18, 0x06,
+ 0x6a, 0x0c, 0xce, 0x13, 0x24, 0x19, 0x68, 0x00,
+ 0x46, 0x23, 0x08, 0xca, 0xcb, 0x58, 0x9c, 0x26,
+ 0x92, 0x30, 0xa6, 0x29, 0x8a, 0xca, 0xd1, 0x18,
+ 0xae, 0x26, 0x5c, 0x90, 0x60, 0xbf, 0x11, 0xad,
+ 0x43, 0x02, 0x06, 0x26, 0xbd, 0x35, 0xdd, 0xa3,
+ 0x11, 0xa6, 0x4d, 0x18, 0x8c, 0x9a, 0xe4, 0x62,
+ 0xd9, 0x23, 0x11, 0x8b, 0xcb, 0x56, 0x55, 0x45,
+ 0xc2, 0x18, 0x56, 0xa2, 0xe2, 0xe1, 0x18, 0x99,
+ 0x54, 0x98, 0x46, 0x4d, 0x08, 0x70, 0x9a, 0x64,
+ 0xc4, 0x18, 0x4f, 0x27, 0x64, 0x31, 0x66, 0x27,
+ 0x79, 0x19, 0x3c, 0x8c, 0x8c, 0xa3, 0x44, 0x18,
+ 0x23, 0xd2, 0x6b, 0x8b, 0x64, 0x8c, 0x21, 0x84,
+ 0xd6, 0x23, 0x13, 0x13, 0x2d, 0x44, 0xca, 0x5a,
+ 0x23, 0x09, 0x93, 0x25, 0x18, 0x10, 0x61, 0x38,
+ 0xb4, 0x60, 0x8f, 0x2c, 0x8d, 0x26, 0xb4, 0xc9,
+ 0xd9, 0x19, 0x19, 0x34, 0xd7, 0x31, 0x06, 0x10,
+ 0xc4, 0x30, 0x83, 0x17, 0xe2, 0x0c, 0x2c, 0xc4,
+ 0xc8, 0xc9, 0x3c, 0x5e, 0x93, 0x11, 0x8a, 0x62,
+ 0x64, 0x8c, 0x26, 0x23, 0x22, 0x30, 0x9a, 0x58,
+ 0x86, 0x04, 0x18, 0x4c, 0xab, 0x2b, 0x26, 0x5c,
+ 0x46, 0x88, 0xcb, 0xb1, 0x0d, 0x26, 0xbb, 0x5e,
+ 0x8c, 0xa7, 0x64, 0x31, 0x3d, 0x31, 0x06, 0x26,
+ 0x43, 0x17, 0xa3, 0x08, 0x61, 0x06, 0x17, 0xc4,
+ 0x62, 0xec, 0x4d, 0x4b, 0x2e, 0x2d, 0x4a, 0x94,
+ 0xa4, 0xc2, 0x31, 0x4c, 0x4c, 0x20, 0xc0, 0x83,
+ 0x14, 0x8c, 0x27, 0x8b, 0x31, 0x23, 0x2f, 0x23,
+ 0x11, 0x91, 0x94, 0x65, 0x1a, 0x20, 0xc2, 0x18,
+ 0x86, 0x51, 0x88, 0x62, 0x7c, 0x43, 0x2e, 0xa3,
+ 0x04, 0x18, 0x8c, 0x20, 0xc2, 0xf5, 0xaa, 0x94,
+ 0xc2, 0x31, 0x32, 0xd2, 0xb2, 0xa2, 0x30, 0xba,
+ 0x10, 0xc2, 0xb5, 0x89, 0xa5, 0x18, 0x10, 0x62,
+ 0x9a, 0x10, 0x61, 0x19, 0x72, 0x71, 0x1a, 0xb9,
+ 0x0c, 0x23, 0x46, 0x10, 0x62, 0x78, 0x81, 0x82,
+ 0x3d, 0x75, 0xea, 0x6b, 0x51, 0x8b, 0x61, 0x06,
+ 0x08, 0x62, 0x32, 0x5e, 0x84, 0x18, 0x27, 0x25,
+ 0xc2, 0x6a, 0x4b, 0x51, 0x31, 0x34, 0x5e, 0x29,
+ 0xa1, 0x3c, 0x4d, 0x26, 0x23, 0x10, 0xc2, 0x6b,
+ 0xb1, 0x0d, 0x11, 0xae, 0x46, 0x88, 0x31, 0x35,
+ 0xb1, 0x06, 0x08, 0x79, 0x7e, 0x4f, 0x53, 0x23,
+ 0x29, 0xa4, 0x30, 0x20, 0x30, 0x23, 0x5a, 0xb2,
+ 0xc8, 0x60, 0x9c, 0x93, 0x13, 0x17, 0x92, 0x98,
+ 0x46, 0x13, 0x54, 0x53, 0x08, 0xcb, 0x13, 0xa1,
+ 0x1a, 0x89, 0xe5, 0x46, 0x08, 0x18, 0x10, 0x30,
+ 0x9d, 0x68, 0xc2, 0x1c, 0x46, 0x46, 0xae, 0x62,
+ 0x1a, 0x46, 0x4e, 0x4d, 0x34, 0x8c, 0xbd, 0x26,
+ 0xc0, 0x40, 0x62, 0xc9, 0xa9, 0x31, 0x74, 0xa8,
+ 0x99, 0x52, 0xb0, 0x8c, 0xa9, 0x29, 0x84, 0x61,
+ 0x19, 0x54, 0x43, 0x02, 0x06, 0x04, 0x32, 0xe5,
+ 0x18, 0x21, 0x91, 0x8b, 0xf2, 0xcc, 0x10, 0x30,
+ 0x8e, 0x23, 0xc4, 0x76, 0x43, 0x08, 0x30, 0x83,
+ 0x08, 0x62, 0x6c, 0x4e, 0xe2, 0x35, 0x96, 0xd0,
+ 0x8e, 0x89, 0x97, 0x42, 0x18, 0x91, 0x84, 0x61,
+ 0x3c, 0x26, 0xa5, 0x2c, 0x4e, 0x17, 0x94, 0xb8,
+ 0xb5, 0xa4, 0xcb, 0x88, 0xc9, 0x84, 0x18, 0xb9,
+ 0x84, 0x19, 0x23, 0x2d, 0xa4, 0x64, 0x62, 0x18,
+ 0x86, 0x53, 0x93, 0xcb, 0x30, 0x8f, 0x2f, 0x93,
+ 0x55, 0xc4, 0xd7, 0x08, 0x62, 0xb8, 0x46, 0x84,
+ 0x68, 0xa3, 0x02, 0xaf, 0x33
+};
+
+static guint8 garbage_frame[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+
+GST_START_TEST (test_parse_flac_normal)
+{
+ gst_parser_test_normal (flac_frame, sizeof (flac_frame));
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_parse_flac_drain_single)
+{
+ gst_parser_test_drain_single (flac_frame, sizeof (flac_frame));
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_parse_flac_drain_garbage)
+{
+ /* We always output the after frame garbage too because we
+ * have no way of detecting it
+ */
+#if 0
+ gst_parser_test_drain_garbage (flac_frame, sizeof (flac_frame),
+ garbage_frame, sizeof (garbage_frame));
+#endif
+ guint8 frame[sizeof (flac_frame) + sizeof (garbage_frame)];
+
+ memcpy (frame, flac_frame, sizeof (flac_frame));
+ memcpy (frame + sizeof (flac_frame), garbage_frame, sizeof (garbage_frame));
+
+ gst_parser_test_drain_single (frame, sizeof (frame));
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_parse_flac_split)
+{
+ gst_parser_test_split (flac_frame, sizeof (flac_frame));
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_parse_flac_skip_garbage)
+{
+ /* We always include the garbage into the frame because
+ * we have no easy way for finding the real end of the
+ * frame. The decoder will later skip the garbage
+ */
+#if 0
+ gst_parser_test_skip_garbage (flac_frame, sizeof (flac_frame),
+ garbage_frame, sizeof (garbage_frame));
+#endif
+ guint8 frame[sizeof (flac_frame) + sizeof (garbage_frame)];
+
+ memcpy (frame, flac_frame, sizeof (flac_frame));
+ memcpy (frame + sizeof (flac_frame), garbage_frame, sizeof (garbage_frame));
+
+ gst_parser_test_normal (frame, sizeof (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)
+/*
+ * Test if the parser handles raw stream and codec_data info properly.
+ */
+GST_START_TEST (test_parse_flac_detect_stream)
+{
+ GstCaps *caps;
+ GstStructure *s;
+ const GValue *streamheader;
+ GArray *bufarr;
+ gint i;
+
+ /* Push random data. It should get through since the parser should be
+ * initialized because it got codec_data in the caps */
+ caps = gst_parser_test_get_output_caps (flac_frame, sizeof (flac_frame),
+ SRC_CAPS_TMPL);
+ 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 ("flac output caps: %" GST_PTR_FORMAT, caps);
+ s = gst_caps_get_structure (caps, 0);
+ fail_unless (gst_structure_has_name (s, "audio/x-flac"));
+ fail_unless_structure_field_int_equals (s, "channels", 1);
+ fail_unless_structure_field_int_equals (s, "rate", 44100);
+ fail_unless (gst_structure_has_field (s, "streamheader"));
+ streamheader = gst_structure_get_value (s, "streamheader");
+ fail_unless (G_VALUE_TYPE (streamheader) == GST_TYPE_ARRAY);
+ bufarr = g_value_peek_pointer (streamheader);
+ fail_unless (bufarr->len == 2);
+ for (i = 0; i < bufarr->len; i++) {
+ GstBuffer *buf;
+ GValue *bufval = &g_array_index (bufarr, GValue, i);
+
+ fail_unless (G_VALUE_TYPE (bufval) == GST_TYPE_BUFFER);
+ buf = g_value_peek_pointer (bufval);
+ if (i == 0) {
+ fail_unless (gst_buffer_get_size (buf) == sizeof (streaminfo_header));
+ fail_unless (gst_buffer_memcmp (buf, 0, streaminfo_header,
+ sizeof (streaminfo_header)) == 0);
+ } else if (i == 1) {
+ fail_unless (gst_buffer_get_size (buf) == sizeof (comment_header));
+ fail_unless (gst_buffer_memcmp (buf, 0, comment_header,
+ sizeof (comment_header)) == 0);
+ }
+ }
+
+ gst_caps_unref (caps);
+}
+
+GST_END_TEST;
+
+static Suite *
+flacparse_suite (void)
+{
+ Suite *s = suite_create ("flacparse");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_parse_flac_normal);
+ tcase_add_test (tc_chain, test_parse_flac_drain_single);
+ tcase_add_test (tc_chain, test_parse_flac_drain_garbage);
+ tcase_add_test (tc_chain, test_parse_flac_split);
+ tcase_add_test (tc_chain, test_parse_flac_skip_garbage);
+
+ /* Other tests */
+ tcase_add_test (tc_chain, test_parse_flac_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 = flacparse_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ /* init test context */
+ ctx_factory = "flacparse";
+ ctx_sink_template = &sinktemplate;
+ ctx_src_template = &srctemplate;
+ ctx_discard = 3;
+ ctx_headers[0].data = streaminfo_header;
+ ctx_headers[0].size = sizeof (streaminfo_header);
+ ctx_headers[1].data = comment_header;
+ ctx_headers[1].size = sizeof (comment_header);
+ /* custom offsets, and ts always repeatedly 0 */
+ 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/flvdemux.c b/tests/check/elements/flvdemux.c
new file mode 100644
index 0000000..c9d156d
--- /dev/null
+++ b/tests/check/elements/flvdemux.c
@@ -0,0 +1,182 @@
+/* GStreamer unit tests for flvdemux
+ *
+ * Copyright (C) 2009 Tim-Philipp Müller <tim centricular net>
+ *
+ * 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.
+ */
+
+#include <gst/check/gstcheck.h>
+
+#include <gst/gst.h>
+
+static void
+pad_added_cb (GstElement * flvdemux, GstPad * pad, GstBin * pipeline)
+{
+ GstElement *sink;
+
+ sink = gst_bin_get_by_name (pipeline, "fakesink");
+ fail_unless (gst_element_link (flvdemux, sink));
+ gst_object_unref (sink);
+
+ gst_element_set_state (sink, GST_STATE_PAUSED);
+}
+
+static GstBusSyncReply
+error_cb (GstBus * bus, GstMessage * msg, gpointer user_data)
+{
+ if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
+ const gchar *file = (const gchar *) user_data;
+ GError *err = NULL;
+ gchar *dbg = NULL;
+
+ gst_message_parse_error (msg, &err, &dbg);
+ g_error ("ERROR for %s: %s\n%s\n", file, err->message, dbg);
+ }
+
+ return GST_BUS_PASS;
+}
+
+static void
+handoff_cb (GstElement * element, GstBuffer * buf, GstPad * pad,
+ gint * p_counter)
+{
+ *p_counter += 1;
+ GST_LOG ("counter = %d", *p_counter);
+}
+
+static void
+process_file (const gchar * file, gboolean push_mode, gint repeat,
+ gint num_buffers)
+{
+ GstElement *src, *sep, *sink, *flvdemux, *pipeline;
+ GstBus *bus;
+ gchar *path;
+ gint counter;
+
+ pipeline = gst_pipeline_new ("pipeline");
+ fail_unless (pipeline != NULL, "Failed to create pipeline!");
+
+ bus = gst_element_get_bus (pipeline);
+
+ /* kids, don't use a sync handler for this at home, really; we do because
+ * we just want to abort and nothing else */
+ gst_bus_set_sync_handler (bus, error_cb, (gpointer) file, NULL);
+
+ src = gst_element_factory_make ("filesrc", "filesrc");
+ fail_unless (src != NULL, "Failed to create 'filesrc' element!");
+
+ if (push_mode) {
+ sep = gst_element_factory_make ("queue", "queue");
+ fail_unless (sep != NULL, "Failed to create 'queue' element");
+ } else {
+ sep = gst_element_factory_make ("identity", "identity");
+ fail_unless (sep != NULL, "Failed to create 'identity' element");
+ }
+
+ flvdemux = gst_element_factory_make ("flvdemux", "flvdemux");
+ fail_unless (flvdemux != NULL, "Failed to create 'flvdemux' element!");
+
+ sink = gst_element_factory_make ("fakesink", "fakesink");
+ fail_unless (sink != NULL, "Failed to create 'fakesink' element!");
+
+ g_object_set (sink, "signal-handoffs", TRUE, NULL);
+ g_signal_connect (sink, "handoff", G_CALLBACK (handoff_cb), &counter);
+
+ gst_bin_add_many (GST_BIN (pipeline), src, sep, flvdemux, sink, NULL);
+
+ fail_unless (gst_element_link (src, sep));
+ fail_unless (gst_element_link (sep, flvdemux));
+
+ /* can't link flvdemux and sink yet, do that later */
+ g_signal_connect (flvdemux, "pad-added", G_CALLBACK (pad_added_cb), pipeline);
+
+ path = g_build_filename (GST_TEST_FILES_PATH, file, NULL);
+ GST_LOG ("processing file '%s'", path);
+ g_object_set (src, "location", path, NULL);
+
+ do {
+ GstStateChangeReturn state_ret;
+ GstMessage *msg;
+
+ GST_LOG ("repeat=%d", repeat);
+
+ counter = 0;
+
+ state_ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
+ fail_unless (state_ret != GST_STATE_CHANGE_FAILURE);
+
+ if (state_ret == GST_STATE_CHANGE_ASYNC) {
+ GST_LOG ("waiting for pipeline to reach PAUSED state");
+ state_ret = gst_element_get_state (pipeline, NULL, NULL, -1);
+ fail_unless_equals_int (state_ret, GST_STATE_CHANGE_SUCCESS);
+ }
+
+ GST_LOG ("PAUSED, let's read all of it");
+
+ state_ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
+ fail_unless (state_ret != GST_STATE_CHANGE_FAILURE);
+
+ msg = gst_bus_poll (bus, GST_MESSAGE_EOS, -1);
+ fail_unless (msg != NULL, "Expected EOS message on bus! (%s)", file);
+
+ gst_message_unref (msg);
+
+ if (num_buffers >= 0) {
+ fail_unless_equals_int (counter, num_buffers);
+ }
+
+ fail_unless_equals_int (gst_element_set_state (pipeline, GST_STATE_NULL),
+ GST_STATE_CHANGE_SUCCESS);
+
+ --repeat;
+ } while (repeat > 0);
+
+ gst_object_unref (bus);
+ gst_object_unref (pipeline);
+
+ g_free (path);
+}
+
+GST_START_TEST (test_reuse_pull)
+{
+ process_file ("pcm16sine.flv", FALSE, 3, 129);
+ gst_task_cleanup_all ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_reuse_push)
+{
+ process_file ("pcm16sine.flv", TRUE, 3, 129);
+ gst_task_cleanup_all ();
+}
+
+GST_END_TEST;
+
+static Suite *
+flvdemux_suite (void)
+{
+ Suite *s = suite_create ("flvdemux");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_reuse_push);
+ tcase_add_test (tc_chain, test_reuse_pull);
+
+ return s;
+}
+
+GST_CHECK_MAIN (flvdemux)
diff --git a/tests/check/elements/flvmux.c b/tests/check/elements/flvmux.c
new file mode 100644
index 0000000..1df2efe
--- /dev/null
+++ b/tests/check/elements/flvmux.c
@@ -0,0 +1,175 @@
+/* GStreamer unit tests for flvmux
+ *
+ * Copyright (C) 2009 Tim-Philipp Müller <tim centricular net>
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_VALGRIND
+# include <valgrind/valgrind.h>
+#endif
+
+#include <gst/check/gstcheck.h>
+
+#include <gst/gst.h>
+
+static GstBusSyncReply
+error_cb (GstBus * bus, GstMessage * msg, gpointer user_data)
+{
+ if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
+ GError *err = NULL;
+ gchar *dbg = NULL;
+
+ gst_message_parse_error (msg, &err, &dbg);
+ g_error ("ERROR: %s\n%s\n", err->message, dbg);
+ }
+
+ return GST_BUS_PASS;
+}
+
+static void
+handoff_cb (GstElement * element, GstBuffer * buf, GstPad * pad,
+ gint * p_counter)
+{
+ *p_counter += 1;
+ GST_LOG ("counter = %d", *p_counter);
+}
+
+static void
+mux_pcm_audio (guint num_buffers, guint repeat)
+{
+ GstElement *src, *sink, *flvmux, *conv, *pipeline;
+ GstPad *sinkpad, *srcpad;
+ gint counter;
+
+ GST_LOG ("num_buffers = %u", num_buffers);
+
+ pipeline = gst_pipeline_new ("pipeline");
+ fail_unless (pipeline != NULL, "Failed to create pipeline!");
+
+ /* kids, don't use a sync handler for this at home, really; we do because
+ * we just want to abort and nothing else */
+ gst_bus_set_sync_handler (GST_ELEMENT_BUS (pipeline), error_cb, NULL, NULL);
+
+ src = gst_element_factory_make ("audiotestsrc", "audiotestsrc");
+ fail_unless (src != NULL, "Failed to create 'audiotestsrc' element!");
+
+ g_object_set (src, "num-buffers", num_buffers, NULL);
+
+ conv = gst_element_factory_make ("audioconvert", "audioconvert");
+ fail_unless (conv != NULL, "Failed to create 'audioconvert' element!");
+
+ flvmux = gst_element_factory_make ("flvmux", "flvmux");
+ fail_unless (flvmux != NULL, "Failed to create 'flvmux' element!");
+
+ sink = gst_element_factory_make ("fakesink", "fakesink");
+ fail_unless (sink != NULL, "Failed to create 'fakesink' element!");
+
+ g_object_set (sink, "signal-handoffs", TRUE, NULL);
+ g_signal_connect (sink, "handoff", G_CALLBACK (handoff_cb), &counter);
+
+ gst_bin_add_many (GST_BIN (pipeline), src, conv, flvmux, sink, NULL);
+
+ fail_unless (gst_element_link (src, conv));
+ fail_unless (gst_element_link (flvmux, sink));
+
+ /* now link the elements */
+ sinkpad = gst_element_get_request_pad (flvmux, "audio");
+ fail_unless (sinkpad != NULL, "Could not get audio request pad");
+
+ srcpad = gst_element_get_static_pad (conv, "src");
+ fail_unless (srcpad != NULL, "Could not get audioconvert's source pad");
+
+ fail_unless_equals_int (gst_pad_link (srcpad, sinkpad), GST_PAD_LINK_OK);
+
+ gst_object_unref (srcpad);
+ gst_object_unref (sinkpad);
+
+ do {
+ GstStateChangeReturn state_ret;
+ GstMessage *msg;
+
+ GST_LOG ("repeat=%d", repeat);
+
+ counter = 0;
+
+ state_ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
+ fail_unless (state_ret != GST_STATE_CHANGE_FAILURE);
+
+ if (state_ret == GST_STATE_CHANGE_ASYNC) {
+ GST_LOG ("waiting for pipeline to reach PAUSED state");
+ state_ret = gst_element_get_state (pipeline, NULL, NULL, -1);
+ fail_unless_equals_int (state_ret, GST_STATE_CHANGE_SUCCESS);
+ }
+
+ GST_LOG ("PAUSED, let's do the rest of it");
+
+ state_ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
+ fail_unless (state_ret != GST_STATE_CHANGE_FAILURE);
+
+ msg = gst_bus_poll (GST_ELEMENT_BUS (pipeline), GST_MESSAGE_EOS, -1);
+ fail_unless (msg != NULL, "Expected EOS message on bus!");
+
+ GST_LOG ("EOS");
+ gst_message_unref (msg);
+
+ /* should have some output */
+ fail_unless (counter > 2);
+
+ fail_unless_equals_int (gst_element_set_state (pipeline, GST_STATE_NULL),
+ GST_STATE_CHANGE_SUCCESS);
+
+ /* repeat = test re-usability */
+ --repeat;
+ } while (repeat > 0);
+
+ gst_object_unref (pipeline);
+}
+
+GST_START_TEST (test_index_writing)
+{
+ /* note: there's a magic 128 value in flvmux when doing index writing */
+ if ((__i__ % 33) == 1)
+ mux_pcm_audio (__i__, 2);
+}
+
+GST_END_TEST;
+
+static Suite *
+flvmux_suite (void)
+{
+ Suite *s = suite_create ("flvmux");
+ TCase *tc_chain = tcase_create ("general");
+ gint loop = 499;
+
+ suite_add_tcase (s, tc_chain);
+
+#ifdef HAVE_VALGRIND
+ if (RUNNING_ON_VALGRIND) {
+ loop = 140;
+ }
+#endif
+
+ tcase_add_loop_test (tc_chain, test_index_writing, 1, loop);
+
+ return s;
+}
+
+GST_CHECK_MAIN (flvmux)
diff --git a/tests/check/elements/gdkpixbufsink.c b/tests/check/elements/gdkpixbufsink.c
new file mode 100644
index 0000000..1183ccb
--- /dev/null
+++ b/tests/check/elements/gdkpixbufsink.c
@@ -0,0 +1,293 @@
+/* GStreamer unit test for the gdkpixbufsink element
+ * Copyright (C) 2008 Tim-Philipp Müller <tim centricular net>
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gst/check/gstcheck.h>
+#include <gst/video/video.h>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#define CAPS_RGB "video/x-raw, format=RGB"
+#define CAPS_RGBA "video/x-raw, format=RGBA"
+#define WxH ",width=(int)319,height=(int)241"
+
+#define N_BUFFERS 5
+
+typedef struct
+{
+ GstElement *pipe;
+ GstElement *src;
+ GstElement *filter;
+ GstElement *sink;
+} GstGdkPixbufSinkTestContext;
+
+static void
+gdkpixbufsink_init_test_context (GstGdkPixbufSinkTestContext * ctx,
+ const gchar * filter_caps_string, gint num_buffers)
+{
+ GstCaps *caps;
+
+ fail_unless (ctx != NULL);
+
+ ctx->pipe = gst_pipeline_new ("pipeline");
+ fail_unless (ctx->pipe != NULL);
+ ctx->src = gst_element_factory_make ("videotestsrc", "src");
+ fail_unless (ctx->src != NULL, "Failed to create videotestsrc element");
+ ctx->filter = gst_element_factory_make ("capsfilter", "filter");
+ fail_unless (ctx->filter != NULL, "Failed to create capsfilter element");
+ ctx->sink = gst_element_factory_make ("gdkpixbufsink", "sink");
+ fail_unless (ctx->sink != NULL, "Failed to create gdkpixbufsink element");
+
+ caps = gst_caps_from_string (filter_caps_string);
+ fail_unless (caps != NULL);
+ g_object_set (ctx->filter, "caps", caps, NULL);
+ gst_caps_unref (caps);
+
+ if (num_buffers > 0)
+ g_object_set (ctx->src, "num-buffers", num_buffers, NULL);
+
+ fail_unless (gst_bin_add (GST_BIN (ctx->pipe), ctx->src));
+ fail_unless (gst_bin_add (GST_BIN (ctx->pipe), ctx->filter));
+ fail_unless (gst_bin_add (GST_BIN (ctx->pipe), ctx->sink));
+ fail_unless (gst_element_link (ctx->src, ctx->filter));
+ fail_unless (gst_element_link (ctx->filter, ctx->sink));
+}
+
+static void
+gdkpixbufsink_unset_test_context (GstGdkPixbufSinkTestContext * ctx)
+{
+ gst_element_set_state (ctx->pipe, GST_STATE_NULL);
+ gst_object_unref (ctx->pipe);
+ memset (ctx, 0, sizeof (GstGdkPixbufSinkTestContext));
+}
+
+static gboolean
+check_last_pixbuf (GstGdkPixbufSinkTestContext * ctx, gpointer pixbuf)
+{
+ gpointer last_pb = NULL;
+ gboolean ret;
+
+ g_object_get (ctx->sink, "last-pixbuf", &last_pb, NULL);
+
+ ret = (last_pb == pixbuf);
+
+ if (last_pb)
+ g_object_unref (last_pb);
+
+ return ret;
+}
+
+/* doesn't return a ref to the pixbuf */
+static GdkPixbuf *
+check_message_pixbuf (GstMessage * msg, const gchar * name, gint channels,
+ gboolean has_alpha)
+{
+ GdkPixbuf *pixbuf;
+ const GstStructure *s;
+
+ fail_unless (gst_message_get_structure (msg) != NULL);
+
+ s = gst_message_get_structure (msg);
+ fail_unless_equals_string (gst_structure_get_name (s), name);
+
+ fail_unless (gst_structure_has_field (s, "pixbuf"));
+ fail_unless (gst_structure_has_field_typed (s, "pixel-aspect-ratio",
+ GST_TYPE_FRACTION));
+ pixbuf =
+ GDK_PIXBUF (g_value_get_object (gst_structure_get_value (s, "pixbuf")));
+ fail_unless (GDK_IS_PIXBUF (pixbuf));
+ fail_unless_equals_int (gdk_pixbuf_get_n_channels (pixbuf), channels);
+ fail_unless_equals_int (gdk_pixbuf_get_has_alpha (pixbuf), has_alpha);
+ fail_unless_equals_int (gdk_pixbuf_get_width (pixbuf), 319);
+ fail_unless_equals_int (gdk_pixbuf_get_height (pixbuf), 241);
+
+ return pixbuf;
+}
+
+GST_START_TEST (test_rgb)
+{
+ GstGdkPixbufSinkTestContext ctx;
+ GstMessage *msg;
+ GdkPixbuf *pixbuf;
+ GstBus *bus;
+ gint i;
+
+ gdkpixbufsink_init_test_context (&ctx, CAPS_RGB WxH, N_BUFFERS);
+
+ fail_unless (check_last_pixbuf (&ctx, NULL));
+
+ /* start prerolling */
+ fail_unless_equals_int (gst_element_set_state (ctx.pipe, GST_STATE_PAUSED),
+ GST_STATE_CHANGE_ASYNC);
+
+ /* wait until prerolled */
+ fail_unless_equals_int (gst_element_get_state (ctx.pipe, NULL, NULL, -1),
+ GST_STATE_CHANGE_SUCCESS);
+
+ bus = gst_element_get_bus (ctx.pipe);
+
+ /* find element message from our gdkpixbufsink, ignore element messages from
+ * other elemements (which just seems prudent to do, we don't expect any) */
+ while (1) {
+ msg = gst_bus_pop_filtered (bus, GST_MESSAGE_ELEMENT);
+ fail_if (msg == NULL, "Expected element message from gdkpixbufsink");
+
+ if (msg->src == GST_OBJECT (ctx.sink))
+ break;
+ }
+
+ pixbuf = check_message_pixbuf (msg, "preroll-pixbuf", 3, FALSE);
+ fail_unless (check_last_pixbuf (&ctx, pixbuf));
+ gst_message_unref (msg);
+ pixbuf = NULL;
+
+ /* and go! */
+ fail_unless_equals_int (gst_element_set_state (ctx.pipe, GST_STATE_PLAYING),
+ GST_STATE_CHANGE_SUCCESS);
+
+ /* This is racy, supposed to make sure locking and refcounting works at
+ * least to some extent */
+ for (i = 0; i < 10000; ++i) {
+ gpointer obj = NULL;
+
+ g_object_get (ctx.sink, "last-pixbuf", &obj, NULL);
+ fail_unless (obj == NULL || GDK_IS_PIXBUF (obj));
+ if (obj)
+ g_object_unref (obj);
+ }
+
+ /* there should be as many pixbuf messages as buffers */
+ for (i = 0; i < N_BUFFERS; ++i) {
+ /* find element message from our gdkpixbufsink, ignore element messages from
+ * other elemements (which just seems prudent to do, we don't expect any) */
+ while (1) {
+ msg = gst_bus_timed_pop_filtered (bus, -1, GST_MESSAGE_ELEMENT);
+ fail_if (msg == NULL, "Expected element message from gdkpixbufsink");
+ if (msg->src == GST_OBJECT (ctx.sink))
+ break;
+ }
+
+ pixbuf = check_message_pixbuf (msg, "pixbuf", 3, FALSE);
+ gst_message_unref (msg);
+ }
+
+ /* note: we don't hold a ref to pixbuf any longer here, but it should be ok */
+ fail_unless (check_last_pixbuf (&ctx, pixbuf));
+ pixbuf = NULL;
+
+ gdkpixbufsink_unset_test_context (&ctx);
+ gst_object_unref (bus);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_rgba)
+{
+ GstGdkPixbufSinkTestContext ctx;
+ GstMessage *msg;
+ GdkPixbuf *pixbuf;
+ GstBus *bus;
+ gint i;
+
+ gdkpixbufsink_init_test_context (&ctx, CAPS_RGBA WxH, N_BUFFERS);
+
+ fail_unless (check_last_pixbuf (&ctx, NULL));
+
+ /* start prerolling */
+ fail_unless_equals_int (gst_element_set_state (ctx.pipe, GST_STATE_PAUSED),
+ GST_STATE_CHANGE_ASYNC);
+
+ /* wait until prerolled */
+ fail_unless_equals_int (gst_element_get_state (ctx.pipe, NULL, NULL, -1),
+ GST_STATE_CHANGE_SUCCESS);
+
+ bus = gst_element_get_bus (ctx.pipe);
+
+ /* find element message from our gdkpixbufsink, ignore element messages from
+ * other elemements (which just seems prudent to do, we don't expect any) */
+ while (1) {
+ msg = gst_bus_pop_filtered (bus, GST_MESSAGE_ELEMENT);
+ fail_if (msg == NULL, "Expected element message from gdkpixbufsink");
+
+ if (msg->src == GST_OBJECT (ctx.sink))
+ break;
+ }
+
+ pixbuf = check_message_pixbuf (msg, "preroll-pixbuf", 4, TRUE);
+ fail_unless (check_last_pixbuf (&ctx, pixbuf));
+ gst_message_unref (msg);
+ pixbuf = NULL;
+
+ /* and go! */
+ fail_unless_equals_int (gst_element_set_state (ctx.pipe, GST_STATE_PLAYING),
+ GST_STATE_CHANGE_SUCCESS);
+
+ /* This is racy, supposed to make sure locking and refcounting works at
+ * least to some extent */
+ for (i = 0; i < 10000; ++i) {
+ gpointer obj = NULL;
+
+ g_object_get (ctx.sink, "last-pixbuf", &obj, NULL);
+ fail_unless (obj == NULL || GDK_IS_PIXBUF (obj));
+ if (obj)
+ g_object_unref (obj);
+ }
+
+ /* there should be as many pixbuf messages as buffers */
+ for (i = 0; i < N_BUFFERS; ++i) {
+ /* find element message from our gdkpixbufsink, ignore element messages from
+ * other elemements (which just seems prudent to do, we don't expect any) */
+ while (1) {
+ msg = gst_bus_timed_pop_filtered (bus, -1, GST_MESSAGE_ELEMENT);
+ fail_if (msg == NULL, "Expected element message from gdkpixbufsink");
+ if (msg->src == GST_OBJECT (ctx.sink))
+ break;
+ }
+
+ pixbuf = check_message_pixbuf (msg, "pixbuf", 4, TRUE);
+ gst_message_unref (msg);
+ }
+
+ /* note: we don't hold a ref to pixbuf any longer here, but it should be ok */
+ fail_unless (check_last_pixbuf (&ctx, pixbuf));
+ pixbuf = NULL;
+
+ gdkpixbufsink_unset_test_context (&ctx);
+ gst_object_unref (bus);
+}
+
+GST_END_TEST;
+
+static Suite *
+gdkpixbufsink_suite (void)
+{
+ Suite *s = suite_create ("gdkpixbufsink");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_rgb);
+ tcase_add_test (tc_chain, test_rgba);
+
+ return s;
+}
+
+GST_CHECK_MAIN (gdkpixbufsink);
diff --git a/tests/check/elements/icydemux.c b/tests/check/elements/icydemux.c
new file mode 100644
index 0000000..525e309
--- /dev/null
+++ b/tests/check/elements/icydemux.c
@@ -0,0 +1,300 @@
+/*
+ * icydemux.c - Test icydemux element
+ * Copyright (C) 2006 Michael Smith <msmith@fluendo.com>
+ *
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/check/gstcheck.h>
+
+/* Chunk of data: 8 bytes, followed by a metadata-length byte of 2, followed by
+ * some metadata (32 bytes), then some more data.
+ */
+#define TEST_METADATA \
+ "Test metadata"
+#define ICY_METADATA \
+ "StreamTitle='" TEST_METADATA "';\0\0\0\0"
+
+#define ICY_DATA \
+ "aaaaaaaa" \
+ "\x02" \
+ ICY_METADATA \
+ "bbbbbbbb"
+
+#define ICYCAPS "application/x-icy, metadata-interval = (int)8"
+
+#define SRC_CAPS "application/x-icy, metadata-interval = (int)[0, MAX]"
+#define SINK_CAPS "ANY"
+
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (SRC_CAPS)
+ );
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (SINK_CAPS)
+ );
+
+static GstElement *icydemux;
+static GstBus *bus;
+GstPad *srcpad, *sinkpad;
+
+static GstStaticCaps typefind_caps =
+GST_STATIC_CAPS ("application/octet-stream");
+
+static gboolean fake_typefind_caps; /* FALSE */
+
+static void
+typefind_succeed (GstTypeFind * tf, gpointer private)
+{
+ if (fake_typefind_caps) {
+ gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM,
+ gst_static_caps_get (&typefind_caps));
+ }
+}
+
+static gboolean
+test_event_func (GstPad * pad, GstObject * parent, GstEvent * event)
+{
+ GST_LOG_OBJECT (pad, "%s event: %" GST_PTR_FORMAT,
+ GST_EVENT_TYPE_NAME (event), event);
+
+ /* a sink would post tag events as messages, so do the same here,
+ * esp. since we're polling on the bus waiting for TAG messages.. */
+ if (GST_EVENT_TYPE (event) == GST_EVENT_TAG) {
+ GstTagList *taglist;
+
+ gst_event_parse_tag (event, &taglist);
+
+ gst_bus_post (bus, gst_message_new_tag (GST_OBJECT (pad),
+ gst_tag_list_copy (taglist)));
+ }
+
+ gst_event_unref (event);
+ return TRUE;
+}
+
+static void
+icydemux_found_pad (GstElement * src, GstPad * pad, gpointer data)
+{
+ GST_DEBUG ("got new pad %" GST_PTR_FORMAT, pad);
+
+ /* Turns out that this asserts a refcount which is wrong for this
+ * case (adding the pad from a pad-added callback), so just do the same
+ * thing inline... */
+ /* sinkpad = gst_check_setup_sink_pad (icydemux, &sinktemplate, NULL); */
+ sinkpad = gst_pad_new_from_static_template (&sinktemplate, "sink");
+ fail_if (sinkpad == NULL, "Couldn't create sinkpad");
+ srcpad = gst_element_get_static_pad (icydemux, "src");
+ fail_if (srcpad == NULL, "Failed to get srcpad from icydemux");
+ gst_pad_set_chain_function (sinkpad, gst_check_chain_func);
+ gst_pad_set_event_function (sinkpad, test_event_func);
+
+ GST_DEBUG ("checking srcpad %p refcount", srcpad);
+ /* 1 from element, 1 from signal, 1 from us */
+ ASSERT_OBJECT_REFCOUNT (srcpad, "srcpad", 3);
+
+ GST_DEBUG ("linking srcpad");
+ fail_unless (gst_pad_link (srcpad, sinkpad) == GST_PAD_LINK_OK,
+ "Failed to link pads");
+ gst_object_unref (srcpad);
+
+ gst_pad_set_active (sinkpad, TRUE);
+}
+
+static GstElement *
+create_icydemux (void)
+{
+ icydemux = gst_check_setup_element ("icydemux");
+ srcpad = gst_check_setup_src_pad (icydemux, &srctemplate);
+
+ gst_pad_set_active (srcpad, TRUE);
+
+ g_signal_connect (icydemux, "pad-added", G_CALLBACK (icydemux_found_pad),
+ NULL);
+
+ bus = gst_bus_new ();
+ gst_element_set_bus (icydemux, bus);
+
+ fail_unless (gst_element_set_state (icydemux, GST_STATE_PLAYING) !=
+ GST_STATE_CHANGE_FAILURE, "could not set to playing");
+
+ return icydemux;
+}
+
+static void
+cleanup_icydemux (void)
+{
+ gst_bus_set_flushing (bus, TRUE);
+ gst_object_unref (bus);
+ bus = NULL;
+
+ gst_check_teardown_src_pad (icydemux);
+ if (sinkpad)
+ gst_check_teardown_sink_pad (icydemux);
+ gst_check_teardown_element (icydemux);
+
+ srcpad = NULL;
+ sinkpad = NULL;
+ icydemux = NULL;
+}
+
+static void
+push_data (const guint8 * data, int len, gint64 offset)
+{
+ GstFlowReturn res;
+ GstBuffer *buffer = gst_buffer_new_and_alloc (len);
+
+ gst_buffer_fill (buffer, 0, data, len);
+
+ GST_BUFFER_OFFSET (buffer) = offset;
+
+ res = gst_pad_push (srcpad, buffer);
+
+ fail_unless (res == GST_FLOW_OK, "Failed pushing buffer: %d", res);
+}
+
+GST_START_TEST (test_demux)
+{
+ GstMessage *message;
+ GstTagList *tags;
+ const GValue *tag_val;
+ const gchar *tag;
+ GstCaps *caps;
+
+ fail_unless (gst_type_find_register (NULL, "success", GST_RANK_PRIMARY,
+ typefind_succeed, NULL, gst_static_caps_get (&typefind_caps), NULL,
+ NULL));
+
+ fake_typefind_caps = TRUE;
+
+ caps = gst_caps_from_string (ICYCAPS);
+
+ create_icydemux ();
+ gst_check_setup_events (srcpad, icydemux, caps, GST_FORMAT_TIME);
+
+ push_data ((guint8 *) ICY_DATA, sizeof (ICY_DATA), -1);
+
+ message = gst_bus_poll (bus, GST_MESSAGE_TAG, -1);
+ fail_unless (message != NULL);
+
+ gst_message_parse_tag (message, &tags);
+ fail_unless (tags != NULL);
+
+ tag_val = gst_tag_list_get_value_index (tags, GST_TAG_TITLE, 0);
+ fail_unless (tag_val != NULL);
+
+ tag = g_value_get_string (tag_val);
+ fail_unless (tag != NULL);
+
+ fail_unless_equals_string (TEST_METADATA, (char *) tag);
+
+ gst_tag_list_unref (tags);
+ gst_message_unref (message);
+ gst_caps_unref (caps);
+
+ cleanup_icydemux ();
+
+ fake_typefind_caps = FALSE;
+}
+
+GST_END_TEST;
+
+/* run this test first before the custom typefind function is set up */
+GST_START_TEST (test_first_buf_offset_when_merged_for_typefinding)
+{
+ const guint8 buf1[] = { 'M' };
+ const guint8 buf2[] = { 'P', '+', 0xff, 0xfb, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ GstCaps *icy_caps;
+ GstPad *icy_srcpad;
+
+ fake_typefind_caps = FALSE;
+
+ create_icydemux ();
+
+ icy_caps = gst_caps_from_string (ICYCAPS);
+
+ gst_check_setup_events (srcpad, icydemux, icy_caps, GST_FORMAT_TIME);
+
+ push_data (buf1, G_N_ELEMENTS (buf1), 0);
+
+ /* one byte isn't really enough for typefinding, can't have a srcpad yet */
+ fail_unless (gst_element_get_static_pad (icydemux, "src") == NULL);
+
+ push_data (buf2, G_N_ELEMENTS (buf2), -1);
+
+ /* should have been enough to create a audio/x-musepack source pad .. */
+ icy_srcpad = gst_element_get_static_pad (icydemux, "src");
+ fail_unless (icy_srcpad != NULL);
+ gst_object_unref (icy_srcpad);
+
+ fail_unless (g_list_length (buffers) > 0);
+
+ /* first buffer should have offset 0 even after it was merged with 2nd buf */
+ fail_unless (GST_BUFFER_OFFSET (GST_BUFFER_CAST (buffers->data)) == 0);
+
+ gst_caps_unref (icy_caps);
+
+ cleanup_icydemux ();
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_not_negotiated)
+{
+ GstBuffer *buf;
+ GstSegment segment;
+
+ create_icydemux ();
+
+ gst_segment_init (&segment, GST_FORMAT_BYTES);
+ gst_pad_push_event (srcpad, gst_event_new_stream_start ("test"));
+ gst_pad_push_event (srcpad, gst_event_new_segment (&segment));
+
+ buf = gst_buffer_new_and_alloc (0);
+ GST_BUFFER_OFFSET (buf) = 0;
+
+ fail_unless_equals_int (gst_pad_push (srcpad, buf), GST_FLOW_NOT_NEGOTIATED);
+ buf = NULL;
+
+ cleanup_icydemux ();
+}
+
+GST_END_TEST;
+
+static Suite *
+icydemux_suite (void)
+{
+ Suite *s = suite_create ("icydemux");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_demux);
+ tcase_add_test (tc_chain, test_first_buf_offset_when_merged_for_typefinding);
+ tcase_add_test (tc_chain, test_not_negotiated);
+
+ return s;
+}
+
+GST_CHECK_MAIN (icydemux)
diff --git a/tests/check/elements/id3demux.c b/tests/check/elements/id3demux.c
new file mode 100644
index 0000000..17b524d
--- /dev/null
+++ b/tests/check/elements/id3demux.c
@@ -0,0 +1,283 @@
+/* GStreamer unit tests for id3demux
+ *
+ * Copyright (C) 2007 Tim-Philipp Müller <tim centricular net>
+ *
+ * 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.
+ */
+
+#include <gst/check/gstcheck.h>
+
+#include <gst/gst.h>
+
+typedef void (CheckTagsFunc) (const GstTagList * tags, const gchar * file);
+
+static GstBusSyncReply
+error_cb (GstBus * bus, GstMessage * msg, gpointer user_data)
+{
+ if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
+ const gchar *file = (const gchar *) user_data;
+ GError *err = NULL;
+ gchar *dbg = NULL;
+
+ gst_message_parse_error (msg, &err, &dbg);
+ g_error ("ERROR for %s: %s\n%s\n", file, err->message, dbg);
+ }
+
+ return GST_BUS_PASS;
+}
+
+static GstTagList *
+read_tags_from_file (const gchar * file, gboolean push_mode)
+{
+ GstStateChangeReturn state_ret;
+ GstTagList *tags = NULL;
+ GstMessage *msg;
+ GstElement *src, *sep, *sink, *id3demux, *pipeline;
+ GstBus *bus;
+ gchar *path;
+
+ pipeline = gst_pipeline_new ("pipeline");
+ fail_unless (pipeline != NULL, "Failed to create pipeline!");
+
+ bus = gst_element_get_bus (pipeline);
+
+ /* kids, don't use a sync handler for this at home, really; we do because
+ * we just want to abort and nothing else */
+ gst_bus_set_sync_handler (bus, error_cb, (gpointer) file, NULL);
+
+ src = gst_element_factory_make ("filesrc", "filesrc");
+ fail_unless (src != NULL, "Failed to create 'filesrc' element!");
+
+ if (push_mode) {
+ sep = gst_element_factory_make ("queue", "queue");
+ fail_unless (sep != NULL, "Failed to create 'queue' element");
+ } else {
+ sep = gst_element_factory_make ("identity", "identity");
+ fail_unless (sep != NULL, "Failed to create 'identity' element");
+ }
+
+ id3demux = gst_element_factory_make ("id3demux", "id3demux");
+ fail_unless (id3demux != NULL, "Failed to create 'id3demux' element!");
+
+ sink = gst_element_factory_make ("fakesink", "fakesink");
+ fail_unless (sink != NULL, "Failed to create 'fakesink' element!");
+
+ gst_bin_add_many (GST_BIN (pipeline), src, sep, id3demux, sink, NULL);
+
+ fail_unless (gst_element_link (src, sep));
+ fail_unless (gst_element_link (sep, id3demux));
+ fail_unless (gst_element_link (id3demux, sink));
+
+ path = g_build_filename (GST_TEST_FILES_PATH, file, NULL);
+ GST_LOG ("reading file '%s'", path);
+ g_object_set (src, "location", path, NULL);
+
+ state_ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
+ fail_unless (state_ret != GST_STATE_CHANGE_FAILURE);
+
+ if (state_ret == GST_STATE_CHANGE_ASYNC) {
+ GST_LOG ("waiting for pipeline to reach PAUSED state");
+ state_ret = gst_element_get_state (pipeline, NULL, NULL, -1);
+ fail_unless_equals_int (state_ret, GST_STATE_CHANGE_SUCCESS);
+ }
+
+ GST_LOG ("PAUSED, let's retrieve our tags");
+
+ msg = gst_bus_poll (bus, GST_MESSAGE_TAG, -1);
+ fail_unless (msg != NULL, "Expected TAG message on bus! (%s)", file);
+
+ gst_message_parse_tag (msg, &tags);
+ fail_unless (tags != NULL, "TAG message did not contain taglist! (%s)", file);
+
+ gst_message_unref (msg);
+ gst_object_unref (bus);
+
+ fail_unless_equals_int (gst_element_set_state (pipeline, GST_STATE_NULL),
+ GST_STATE_CHANGE_SUCCESS);
+ gst_object_unref (pipeline);
+
+ g_free (path);
+
+ GST_INFO ("%s: tags = %" GST_PTR_FORMAT, file, tags);
+ return tags;
+}
+
+static void
+run_check_for_file (const gchar * filename, CheckTagsFunc * check_func)
+{
+ GstTagList *tags;
+
+ /* first, pull-based */
+ tags = read_tags_from_file (filename, FALSE);
+ fail_unless (tags != NULL, "Failed to extract tags from '%s'", filename);
+ check_func (tags, filename);
+ gst_tag_list_unref (tags);
+
+ /* FIXME: need to fix id3demux for short content in push mode */
+#if 0
+ /* now try push-based */
+ tags = read_tags_from_file (filename, TRUE);
+ fail_unless (tags != NULL, "Failed to extract tags from '%s'", filename);
+ check_func (tags, filename);
+ gst_tag_list_unref (tags);
+#endif
+}
+
+static void
+check_date_1977_06_23 (const GstTagList * tags, const gchar * file)
+{
+ GstDateTime *date = NULL;
+
+ gst_tag_list_get_date_time (tags, GST_TAG_DATE_TIME, &date);
+ fail_unless (date != NULL,
+ "Tags from %s should contain a GST_TAG_DATE_TIME tag");
+ fail_unless_equals_int (gst_date_time_get_year (date), 1977);
+ fail_unless_equals_int (gst_date_time_get_month (date), 6);
+ fail_unless_equals_int (gst_date_time_get_day (date), 23);
+ gst_date_time_unref (date);
+}
+
+GST_START_TEST (test_tdat_tyer)
+{
+ run_check_for_file ("id3-407349-1.tag", check_date_1977_06_23);
+ run_check_for_file ("id3-407349-2.tag", check_date_1977_06_23);
+}
+
+GST_END_TEST;
+
+static void
+check_wcop (const GstTagList * tags, const gchar * file)
+{
+ gchar *copyright = NULL;
+ gchar *uri = NULL;
+
+ fail_unless (gst_tag_list_get_string (tags, GST_TAG_LICENSE_URI, &uri));
+ fail_unless (uri != NULL);
+ fail_unless_equals_string (uri,
+ "http://creativecommons.org/licenses/by/3.0/");
+ g_free (uri);
+
+ fail_unless (gst_tag_list_get_string (tags, GST_TAG_COPYRIGHT, &copyright));
+ fail_unless (copyright != NULL);
+ fail_unless_equals_string (copyright,
+ " Steadman. Licensed to the public under http://creativecommons.org/licenses/by/3.0/ verify at http://test.com");
+ g_free (copyright);
+}
+
+GST_START_TEST (test_wcop)
+{
+ run_check_for_file ("id3-447000-wcop.tag", check_wcop);
+}
+
+GST_END_TEST;
+
+static void
+check_unsync_v23 (const GstTagList * tags, const gchar * file)
+{
+ gchar *album = NULL;
+ gchar *title = NULL;
+ gchar *artist = NULL;
+
+ fail_unless (gst_tag_list_get_string (tags, GST_TAG_TITLE, &title));
+ fail_unless (title != NULL);
+ fail_unless_equals_string (title, "ARTIST"); /* sic */
+ g_free (title);
+
+ fail_unless (gst_tag_list_get_string (tags, GST_TAG_ALBUM, &album));
+ fail_unless (album != NULL);
+ fail_unless_equals_string (album, "Album");
+ g_free (album);
+
+ fail_unless (gst_tag_list_get_string (tags, GST_TAG_ARTIST, &artist));
+ fail_unless (artist != NULL);
+ fail_unless_equals_string (artist, "藝人");
+ g_free (artist);
+}
+
+GST_START_TEST (test_unsync_v23)
+{
+ run_check_for_file ("id3-577468-unsynced-tag.tag", check_unsync_v23);
+}
+
+GST_END_TEST;
+
+static void
+check_unsync_v24 (const GstTagList * tags, const gchar * file)
+{
+ const GValue *val;
+ GstSample *sample;
+ GstBuffer *buf;
+ gchar *album = NULL;
+ gchar *title = NULL;
+ gchar *artist = NULL;
+ GstMapInfo map;
+
+ fail_unless (gst_tag_list_get_string (tags, GST_TAG_TITLE, &title));
+ fail_unless (title != NULL);
+ fail_unless_equals_string (title, "Starlight");
+ g_free (title);
+
+ fail_unless (gst_tag_list_get_string (tags, GST_TAG_ALBUM, &album));
+ fail_unless (album != NULL);
+ fail_unless_equals_string (album, "L'albumRockVol.4 CD1");
+ g_free (album);
+
+ fail_unless (gst_tag_list_get_string (tags, GST_TAG_ARTIST, &artist));
+ fail_unless (artist != NULL);
+ fail_unless_equals_string (artist, "Muse");
+ g_free (artist);
+
+ val = gst_tag_list_get_value_index (tags, GST_TAG_IMAGE, 0);
+ fail_unless (val != NULL);
+ fail_unless (GST_VALUE_HOLDS_SAMPLE (val));
+ sample = gst_value_get_sample (val);
+ fail_unless (sample != NULL);
+ fail_unless (gst_sample_get_caps (sample) != NULL);
+ buf = gst_sample_get_buffer (sample);
+ fail_unless (buf != NULL);
+ gst_buffer_map (buf, &map, GST_MAP_READ);
+ fail_unless_equals_int (map.size, 38022);
+ /* check for jpeg start/end markers */
+ fail_unless_equals_int (map.data[0], 0xff);
+ fail_unless_equals_int (map.data[1], 0xd8);
+ fail_unless_equals_int (map.data[38020], 0xff);
+ fail_unless_equals_int (map.data[38021], 0xd9);
+ gst_buffer_unmap (buf, &map);
+}
+
+GST_START_TEST (test_unsync_v24)
+{
+ run_check_for_file ("id3-588148-unsynced-v24.tag", check_unsync_v24);
+}
+
+GST_END_TEST;
+
+static Suite *
+id3demux_suite (void)
+{
+ Suite *s = suite_create ("id3demux");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_tdat_tyer);
+ tcase_add_test (tc_chain, test_wcop);
+ tcase_add_test (tc_chain, test_unsync_v23);
+ tcase_add_test (tc_chain, test_unsync_v24);
+
+ return s;
+}
+
+GST_CHECK_MAIN (id3demux)
diff --git a/tests/check/elements/id3v2mux.c b/tests/check/elements/id3v2mux.c
new file mode 100644
index 0000000..21869e6
--- /dev/null
+++ b/tests/check/elements/id3v2mux.c
@@ -0,0 +1,536 @@
+/* GStreamer
+ *
+ * unit test for the taglib-based id3v2mux element
+ *
+ * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
+ *
+ * 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.
+ */
+
+#include <gst/check/gstcheck.h>
+
+#include <gst/gst.h>
+#include <string.h>
+
+#define TEST_ARTIST "Ar T\303\255st"
+#define TEST_TITLE "M\303\274llermilch!"
+#define TEST_ALBUM "Boom"
+#define TEST_DATE g_date_new_dmy(1,1,2006)
+#define TEST_TRACK_NUMBER 7
+#define TEST_TRACK_COUNT 19
+#define TEST_VOLUME_NUMBER 2
+#define TEST_VOLUME_COUNT 3
+#define TEST_TRACK_GAIN 1.45
+#define TEST_ALBUM_GAIN 0.78
+#define TEST_TRACK_PEAK 0.83
+#define TEST_ALBUM_PEAK 0.18
+#define TEST_BPM 113.0
+
+/* for dummy mp3 frame sized MP3_FRAME_SIZE bytes,
+ * start: ff fb b0 44 00 00 08 00 00 4b 00 00 00 00 00 00 */
+static const guint8 mp3_dummyhdr[] = { 0xff, 0xfb, 0xb0, 0x44, 0x00, 0x00,
+ 0x08, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00
+};
+
+#define MP3_FRAME_SIZE 626
+
+/* the peak and gain values are stored pretty roughly, so check that they're
+ * within 2% of the expected value.
+ */
+#define fail_unless_sorta_equals_float(a, b) \
+G_STMT_START { \
+ double first = a; \
+ double second = b; \
+ fail_unless(fabs (first - second) < (0.02 * fabs (first)), \
+ "'" #a "' (%g) is not equal to '" #b "' (%g)", first, second); \
+} G_STMT_END;
+
+
+static GstTagList *
+test_taglib_id3mux_create_tags (guint32 mask)
+{
+ GstTagList *tags;
+
+ tags = gst_tag_list_new_empty ();
+
+ if (mask & (1 << 0)) {
+ gst_tag_list_add (tags, GST_TAG_MERGE_KEEP,
+ GST_TAG_ARTIST, TEST_ARTIST, NULL);
+ }
+ if (mask & (1 << 1)) {
+ gst_tag_list_add (tags, GST_TAG_MERGE_KEEP,
+ GST_TAG_TITLE, TEST_TITLE, NULL);
+ }
+ if (mask & (1 << 2)) {
+ gst_tag_list_add (tags, GST_TAG_MERGE_KEEP,
+ GST_TAG_ALBUM, TEST_ALBUM, NULL);
+ }
+ if (mask & (1 << 3)) {
+ GDate *date;
+
+ date = TEST_DATE;
+ gst_tag_list_add (tags, GST_TAG_MERGE_KEEP, GST_TAG_DATE, date, NULL);
+ g_date_free (date);
+ }
+ if (mask & (1 << 4)) {
+ gst_tag_list_add (tags, GST_TAG_MERGE_KEEP,
+ GST_TAG_TRACK_NUMBER, TEST_TRACK_NUMBER, NULL);
+ }
+ if (mask & (1 << 5)) {
+ gst_tag_list_add (tags, GST_TAG_MERGE_KEEP,
+ GST_TAG_TRACK_COUNT, TEST_TRACK_COUNT, NULL);
+ }
+ if (mask & (1 << 6)) {
+ gst_tag_list_add (tags, GST_TAG_MERGE_KEEP,
+ GST_TAG_ALBUM_VOLUME_NUMBER, TEST_VOLUME_NUMBER, NULL);
+ }
+ if (mask & (1 << 7)) {
+ gst_tag_list_add (tags, GST_TAG_MERGE_KEEP,
+ GST_TAG_ALBUM_VOLUME_COUNT, TEST_VOLUME_COUNT, NULL);
+ }
+ if (mask & (1 << 8)) {
+ gst_tag_list_add (tags, GST_TAG_MERGE_KEEP,
+ GST_TAG_TRACK_GAIN, TEST_TRACK_GAIN, NULL);
+ }
+ if (mask & (1 << 9)) {
+ gst_tag_list_add (tags, GST_TAG_MERGE_KEEP,
+ GST_TAG_ALBUM_GAIN, TEST_ALBUM_GAIN, NULL);
+ }
+ if (mask & (1 << 10)) {
+ gst_tag_list_add (tags, GST_TAG_MERGE_KEEP,
+ GST_TAG_TRACK_PEAK, TEST_TRACK_PEAK, NULL);
+ }
+ if (mask & (1 << 11)) {
+ gst_tag_list_add (tags, GST_TAG_MERGE_KEEP,
+ GST_TAG_ALBUM_PEAK, TEST_ALBUM_PEAK, NULL);
+ }
+ if (mask & (1 << 12)) {
+ gst_tag_list_add (tags, GST_TAG_MERGE_KEEP,
+ GST_TAG_BEATS_PER_MINUTE, TEST_BPM, NULL);
+ }
+ if (mask & (1 << 13)) {
+ }
+ return tags;
+}
+
+static gboolean
+utf8_string_in_buf (GstBuffer * buf, const gchar * s)
+{
+ gint i, len;
+ GstMapInfo map;
+
+ len = strlen (s);
+ gst_buffer_map (buf, &map, GST_MAP_READ);
+ for (i = 0; i < (map.size - len); ++i) {
+ if (memcmp (map.data + i, s, len) == 0) {
+ gst_buffer_unmap (buf, &map);
+ return TRUE;
+ }
+ }
+ gst_buffer_unmap (buf, &map);
+
+ return FALSE;
+}
+
+static void
+test_taglib_id3mux_check_tag_buffer (GstBuffer * buf, guint32 mask)
+{
+ /* make sure our UTF-8 string hasn't been put into the tag as ISO-8859-1 */
+ if (mask & (1 << 0)) {
+ fail_unless (utf8_string_in_buf (buf, TEST_ARTIST));
+ }
+ /* make sure our UTF-8 string hasn't been put into the tag as ISO-8859-1 */
+ if (mask & (1 << 1)) {
+ fail_unless (utf8_string_in_buf (buf, TEST_TITLE));
+ }
+ /* make sure our UTF-8 string hasn't been put into the tag as ISO-8859-1 */
+ if (mask & (1 << 2)) {
+ fail_unless (utf8_string_in_buf (buf, TEST_ALBUM));
+ }
+}
+
+static void
+test_taglib_id3mux_check_tags (GstTagList * tags, guint32 mask)
+{
+ if (mask & (1 << 0)) {
+ gchar *s = NULL;
+
+ fail_unless (gst_tag_list_get_string (tags, GST_TAG_ARTIST, &s));
+ fail_unless (g_str_equal (s, TEST_ARTIST));
+ g_free (s);
+ }
+ if (mask & (1 << 1)) {
+ gchar *s = NULL;
+
+ fail_unless (gst_tag_list_get_string (tags, GST_TAG_TITLE, &s));
+ fail_unless (g_str_equal (s, TEST_TITLE));
+ g_free (s);
+ }
+ if (mask & (1 << 2)) {
+ gchar *s = NULL;
+
+ fail_unless (gst_tag_list_get_string (tags, GST_TAG_ALBUM, &s));
+ fail_unless (g_str_equal (s, TEST_ALBUM));
+ g_free (s);
+ }
+ if (mask & (1 << 3)) {
+ GDate *shouldbe, *date = NULL;
+
+ shouldbe = TEST_DATE;
+ fail_unless (gst_tag_list_get_date (tags, GST_TAG_DATE, &date));
+ fail_unless (g_date_compare (shouldbe, date) == 0);
+ g_date_free (shouldbe);
+ g_date_free (date);
+ }
+ if (mask & (1 << 4)) {
+ guint num;
+
+ fail_unless (gst_tag_list_get_uint (tags, GST_TAG_TRACK_NUMBER, &num));
+ fail_unless (num == TEST_TRACK_NUMBER);
+ }
+ if (mask & (1 << 5)) {
+ guint count;
+
+ fail_unless (gst_tag_list_get_uint (tags, GST_TAG_TRACK_COUNT, &count));
+ fail_unless (count == TEST_TRACK_COUNT);
+ }
+ if (mask & (1 << 6)) {
+ guint num;
+
+ fail_unless (gst_tag_list_get_uint (tags, GST_TAG_ALBUM_VOLUME_NUMBER,
+ &num));
+ fail_unless (num == TEST_VOLUME_NUMBER);
+ }
+ if (mask & (1 << 7)) {
+ guint count;
+
+ fail_unless (gst_tag_list_get_uint (tags, GST_TAG_ALBUM_VOLUME_COUNT,
+ &count));
+ fail_unless (count == TEST_VOLUME_COUNT);
+ }
+ if (mask & (1 << 8)) {
+ gdouble gain;
+
+ fail_unless (gst_tag_list_get_double (tags, GST_TAG_TRACK_GAIN, &gain));
+ fail_unless_sorta_equals_float (gain, TEST_TRACK_GAIN);
+ }
+ if (mask & (1 << 9)) {
+ gdouble gain;
+
+ fail_unless (gst_tag_list_get_double (tags, GST_TAG_ALBUM_GAIN, &gain));
+ fail_unless_sorta_equals_float (gain, TEST_ALBUM_GAIN);
+ }
+ if (mask & (1 << 10)) {
+ gdouble peak;
+
+ fail_unless (gst_tag_list_get_double (tags, GST_TAG_TRACK_PEAK, &peak));
+ fail_unless_sorta_equals_float (peak, TEST_TRACK_PEAK);
+ }
+ if (mask & (1 << 11)) {
+ gdouble peak;
+
+ fail_unless (gst_tag_list_get_double (tags, GST_TAG_ALBUM_PEAK, &peak));
+ fail_unless_sorta_equals_float (peak, TEST_ALBUM_PEAK);
+ }
+ if (mask & (1 << 12)) {
+ gdouble bpm;
+
+ fail_unless (gst_tag_list_get_double (tags, GST_TAG_BEATS_PER_MINUTE,
+ &bpm));
+ fail_unless_sorta_equals_float (bpm, TEST_BPM);
+ }
+ if (mask & (1 << 13)) {
+ }
+}
+
+static void
+fill_mp3_buffer (GstElement * fakesrc, GstBuffer * buf, GstPad * pad,
+ guint64 * p_offset)
+{
+ gsize size;
+
+ size = gst_buffer_get_size (buf);
+
+ fail_unless (size == MP3_FRAME_SIZE);
+
+ GST_LOG ("filling buffer with fake mp3 data, offset = %" G_GUINT64_FORMAT,
+ *p_offset);
+
+ gst_buffer_fill (buf, 0, mp3_dummyhdr, sizeof (mp3_dummyhdr));
+
+#if 0
+ /* can't use gst_buffer_set_caps() here because the metadata isn't writable
+ * because of the extra refcounts taken by the signal emission mechanism;
+ * we know it's fine to use GST_BUFFER_CAPS() here though */
+ GST_BUFFER_CAPS (buf) = gst_caps_new_simple ("audio/mpeg", "mpegversion",
+ G_TYPE_INT, 1, "layer", G_TYPE_INT, 3, NULL);
+#endif
+
+ GST_BUFFER_OFFSET (buf) = *p_offset;
+ *p_offset += size;
+}
+
+static void
+got_buffer (GstElement * fakesink, GstBuffer * buf, GstPad * pad,
+ GstBuffer ** p_buf)
+{
+ gint64 off;
+ GstMapInfo map;
+
+ off = GST_BUFFER_OFFSET (buf);
+
+ gst_buffer_map (buf, &map, GST_MAP_READ);
+
+ GST_LOG ("size=%" G_GSIZE_FORMAT ", offset=%" G_GINT64_FORMAT, map.size, off);
+
+ fail_unless (GST_BUFFER_OFFSET_IS_VALID (buf));
+
+ if (*p_buf == NULL || (off + map.size) > gst_buffer_get_size (*p_buf)) {
+ GstBuffer *newbuf;
+
+ /* not very elegant, but who cares */
+ newbuf = gst_buffer_new_and_alloc (off + map.size);
+ if (*p_buf) {
+ GstMapInfo pmap;
+
+ gst_buffer_map (*p_buf, &pmap, GST_MAP_READ);
+ gst_buffer_fill (newbuf, 0, pmap.data, pmap.size);
+ gst_buffer_unmap (*p_buf, &pmap);
+ }
+ gst_buffer_fill (newbuf, off, map.data, map.size);
+
+ if (*p_buf)
+ gst_buffer_unref (*p_buf);
+ *p_buf = newbuf;
+ } else {
+ gst_buffer_fill (*p_buf, off, map.data, map.size);
+ }
+ gst_buffer_unmap (buf, &map);
+}
+
+static void
+test_taglib_id3mux_check_output_buffer (GstBuffer * buf)
+{
+ GstMapInfo map;
+ guint off;
+
+ gst_buffer_map (buf, &map, GST_MAP_READ);
+ g_assert (map.size % MP3_FRAME_SIZE == 0);
+
+ for (off = 0; off < map.size; off += MP3_FRAME_SIZE) {
+ fail_unless (memcmp (map.data + off, mp3_dummyhdr,
+ sizeof (mp3_dummyhdr)) == 0);
+ }
+ gst_buffer_unmap (buf, &map);
+}
+
+static void
+identity_cb (GstElement * identity, GstBuffer * buf, GstBuffer ** p_tagbuf)
+{
+ if (*p_tagbuf == NULL) {
+ *p_tagbuf = gst_buffer_ref (buf);
+ }
+}
+
+static void
+test_taglib_id3mux_with_tags (GstTagList * tags, guint32 mask)
+{
+ GstMessage *msg;
+ GstTagList *tags_read = NULL;
+ GstElement *pipeline, *id3mux, *id3demux, *fakesrc, *identity, *fakesink;
+ GstBus *bus;
+ guint64 offset;
+ GstBuffer *outbuf = NULL;
+ GstBuffer *tagbuf = NULL;
+ GstStateChangeReturn state_result;
+
+ pipeline = gst_pipeline_new ("pipeline");
+ g_assert (pipeline != NULL);
+
+ fakesrc = gst_element_factory_make ("fakesrc", "fakesrc");
+ g_assert (fakesrc != NULL);
+
+ id3mux = gst_element_factory_make ("id3v2mux", "id3v2mux");
+ g_assert (id3mux != NULL);
+
+ identity = gst_element_factory_make ("identity", "identity");
+ g_assert (identity != NULL);
+
+ id3demux = gst_element_factory_make ("id3demux", "id3demux");
+ g_assert (id3demux != NULL);
+
+ fakesink = gst_element_factory_make ("fakesink", "fakesink");
+ g_assert (fakesink != NULL);
+
+ /* set up sink */
+ outbuf = NULL;
+ g_object_set (fakesink, "signal-handoffs", TRUE, NULL);
+ g_signal_connect (fakesink, "handoff", G_CALLBACK (got_buffer), &outbuf);
+
+ gst_bin_add (GST_BIN (pipeline), fakesrc);
+ gst_bin_add (GST_BIN (pipeline), id3mux);
+ gst_bin_add (GST_BIN (pipeline), identity);
+ gst_bin_add (GST_BIN (pipeline), id3demux);
+ gst_bin_add (GST_BIN (pipeline), fakesink);
+
+ gst_tag_setter_merge_tags (GST_TAG_SETTER (id3mux), tags,
+ GST_TAG_MERGE_APPEND);
+
+ gst_element_link_many (fakesrc, id3mux, identity, id3demux, fakesink, NULL);
+
+ /* set up source */
+ g_object_set (fakesrc, "signal-handoffs", TRUE, "can-activate-pull", FALSE,
+ "filltype", 2, "sizetype", 2, "sizemax", MP3_FRAME_SIZE,
+ "num-buffers", 16, NULL);
+
+ offset = 0;
+ g_signal_connect (fakesrc, "handoff", G_CALLBACK (fill_mp3_buffer), &offset);
+
+ /* set up identity to catch tag buffer */
+ g_signal_connect (identity, "handoff", G_CALLBACK (identity_cb), &tagbuf);
+
+ GST_LOG ("setting and getting state ...");
+ gst_element_set_state (pipeline, GST_STATE_PLAYING);
+ state_result = gst_element_get_state (pipeline, NULL, NULL, -1);
+ fail_unless (state_result == GST_STATE_CHANGE_SUCCESS,
+ "Unexpected result from get_state(). Expected success, got %d",
+ state_result);
+
+ bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
+
+ GST_LOG ("Waiting for tag ...");
+ msg =
+ gst_bus_poll (bus, GST_MESSAGE_TAG | GST_MESSAGE_EOS | GST_MESSAGE_ERROR,
+ -1);
+ if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
+ GError *err;
+ gchar *dbg;
+
+ gst_message_parse_error (msg, &err, &dbg);
+ g_printerr ("ERROR from element %s: %s\n%s\n",
+ GST_OBJECT_NAME (msg->src), err->message, GST_STR_NULL (dbg));
+ g_error_free (err);
+ g_free (dbg);
+ } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS) {
+ g_printerr ("EOS message, but were waiting for TAGS!\n");
+ }
+ fail_unless (msg->type == GST_MESSAGE_TAG);
+
+ gst_message_parse_tag (msg, &tags_read);
+ gst_message_unref (msg);
+
+ GST_LOG ("Got tags: %" GST_PTR_FORMAT, tags_read);
+ test_taglib_id3mux_check_tags (tags_read, mask);
+ gst_tag_list_unref (tags_read);
+
+ fail_unless (tagbuf != NULL);
+ test_taglib_id3mux_check_tag_buffer (tagbuf, mask);
+ gst_buffer_unref (tagbuf);
+
+ GST_LOG ("Waiting for EOS ...");
+ msg = gst_bus_poll (bus, GST_MESSAGE_EOS | GST_MESSAGE_ERROR, -1);
+ if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
+ GError *err;
+ gchar *dbg;
+
+ gst_message_parse_error (msg, &err, &dbg);
+ g_printerr ("ERROR from element %s: %s\n%s\n",
+ GST_OBJECT_NAME (msg->src), err->message, GST_STR_NULL (dbg));
+ g_error_free (err);
+ g_free (dbg);
+ }
+ fail_unless (msg->type == GST_MESSAGE_EOS);
+ gst_message_unref (msg);
+
+ gst_object_unref (bus);
+
+ GST_LOG ("Got EOS, shutting down ...");
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+ gst_object_unref (pipeline);
+
+ test_taglib_id3mux_check_output_buffer (outbuf);
+ gst_buffer_unref (outbuf);
+
+ GST_LOG ("Done");
+}
+
+GST_START_TEST (test_id3v2mux)
+{
+ GstTagList *tags;
+ gint i;
+
+ g_random_set_seed (247166295);
+
+ /* internal consistency check */
+ tags = test_taglib_id3mux_create_tags (0xFFFFFFFF);
+ test_taglib_id3mux_check_tags (tags, 0xFFFFFFFF);
+ gst_tag_list_unref (tags);
+
+ /* now the real tests */
+ for (i = 0; i < 50; ++i) {
+ guint32 mask;
+
+ mask = g_random_int ();
+ GST_LOG ("tag mask = %08x (i=%d)", mask, i);
+
+ if (mask == 0)
+ continue;
+
+ /* create tags */
+ tags = test_taglib_id3mux_create_tags (mask);
+ GST_LOG ("tags for mask %08x = %" GST_PTR_FORMAT, mask, tags);
+
+ /* double-check for internal consistency */
+ test_taglib_id3mux_check_tags (tags, mask);
+
+ /* test with pipeline */
+ test_taglib_id3mux_with_tags (tags, mask);
+
+ /* free tags */
+ gst_tag_list_unref (tags);
+ }
+}
+
+GST_END_TEST;
+
+static Suite *
+id3v2mux_suite (void)
+{
+ Suite *s = suite_create ("id3v2mux");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_id3v2mux);
+
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ int nf;
+
+ Suite *s = id3v2mux_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ srunner_run_all (sr, CK_NORMAL);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}
diff --git a/tests/check/elements/imagefreeze.c b/tests/check/elements/imagefreeze.c
new file mode 100644
index 0000000..cfeff37
--- /dev/null
+++ b/tests/check/elements/imagefreeze.c
@@ -0,0 +1,586 @@
+/* GStreamer
+ * Copyright (C) 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ *
+ * 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.
+ */
+
+#include <string.h>
+
+#include <gst/check/gstcheck.h>
+#include <gst/video/video.h>
+
+static gboolean
+bus_handler (GstBus * bus, GstMessage * message, gpointer data)
+{
+ GMainLoop *loop = (GMainLoop *) data;
+
+ switch (message->type) {
+ case GST_MESSAGE_EOS:
+ g_main_loop_quit (loop);
+ break;
+ case GST_MESSAGE_WARNING:
+ case GST_MESSAGE_ERROR:{
+ GError *gerror;
+ gchar *debug;
+
+ if (message->type == GST_MESSAGE_WARNING)
+ gst_message_parse_warning (message, &gerror, &debug);
+ else
+ gst_message_parse_error (message, &gerror, &debug);
+ g_error ("error from %s: %s (%s)\n",
+ GST_ELEMENT_NAME (GST_MESSAGE_SRC (message)), gerror->message,
+ GST_STR_NULL (debug));
+ g_error_free (gerror);
+ g_free (debug);
+ g_main_loop_quit (loop);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+static GstElement *
+setup_imagefreeze (const GstCaps * caps1, const GstCaps * caps2,
+ GCallback sink_handoff, gpointer sink_handoff_data)
+{
+ GstElement *pipeline;
+ GstElement *videotestsrc, *capsfilter1, *imagefreeze, *capsfilter2, *fakesink;
+
+ pipeline = gst_pipeline_new ("pipeline");
+ fail_unless (pipeline != NULL);
+
+ videotestsrc = gst_element_factory_make ("videotestsrc", "src");
+ fail_unless (videotestsrc != NULL);
+ g_object_set (videotestsrc, "num-buffers", 1, NULL);
+
+ capsfilter1 = gst_element_factory_make ("capsfilter", "filter1");
+ fail_unless (capsfilter1 != NULL);
+ g_object_set (capsfilter1, "caps", caps1, NULL);
+
+ imagefreeze = gst_element_factory_make ("imagefreeze", "freeze");
+ fail_unless (imagefreeze != NULL);
+
+ capsfilter2 = gst_element_factory_make ("capsfilter", "filter2");
+ fail_unless (capsfilter2 != NULL);
+ g_object_set (capsfilter2, "caps", caps2, NULL);
+
+ fakesink = gst_element_factory_make ("fakesink", "sink");
+ fail_unless (fakesink != NULL);
+ g_object_set (fakesink, "signal-handoffs", TRUE, "async", FALSE, NULL);
+
+ if (sink_handoff)
+ g_signal_connect (fakesink, "handoff", sink_handoff, sink_handoff_data);
+
+ gst_bin_add_many (GST_BIN (pipeline), videotestsrc, capsfilter1, imagefreeze,
+ capsfilter2, fakesink, NULL);
+
+ fail_unless (gst_element_link_pads (videotestsrc, "src", capsfilter1,
+ "sink"));
+ fail_unless (gst_element_link_pads (capsfilter1, "src", imagefreeze, "sink"));
+ fail_unless (gst_element_link_pads (imagefreeze, "src", capsfilter2, "sink"));
+ fail_unless (gst_element_link_pads (capsfilter2, "src", fakesink, "sink"));
+
+ return pipeline;
+}
+
+static void
+sink_handoff_cb_0_1 (GstElement * object, GstBuffer * buffer, GstPad * pad,
+ gpointer user_data)
+{
+ guint *n_buffers = (guint *) user_data;
+
+ if (*n_buffers == G_MAXUINT)
+ return;
+
+ fail_unless_equals_uint64 (GST_BUFFER_TIMESTAMP (buffer), 0);
+ fail_unless_equals_uint64 (GST_BUFFER_DURATION (buffer), GST_CLOCK_TIME_NONE);
+ fail_unless_equals_uint64 (GST_BUFFER_OFFSET (buffer), 0);
+ fail_unless_equals_uint64 (GST_BUFFER_OFFSET_END (buffer), 1);
+
+ *n_buffers = *n_buffers + 1;
+}
+
+GST_START_TEST (test_imagefreeze_0_1)
+{
+ GstElement *pipeline;
+ GstCaps *caps1, *caps2;
+ GstBus *bus;
+ GMainLoop *loop;
+ guint n_buffers = G_MAXUINT;
+ guint bus_watch = 0;
+ GstVideoInfo i1, i2;
+
+ gst_video_info_init (&i1);
+ gst_video_info_set_format (&i1, GST_VIDEO_FORMAT_xRGB, 640, 480);
+ i1.fps_n = 25;
+ i1.fps_d = 1;
+ caps1 = gst_video_info_to_caps (&i1);
+
+ gst_video_info_init (&i2);
+ gst_video_info_set_format (&i2, GST_VIDEO_FORMAT_xRGB, 640, 480);
+ caps2 = gst_video_info_to_caps (&i2);
+
+ pipeline =
+ setup_imagefreeze (caps1, caps2, G_CALLBACK (sink_handoff_cb_0_1),
+ &n_buffers);
+
+ loop = g_main_loop_new (NULL, TRUE);
+ fail_unless (loop != NULL);
+
+ bus = gst_element_get_bus (pipeline);
+ fail_unless (bus != NULL);
+ bus_watch = gst_bus_add_watch (bus, bus_handler, loop);
+ gst_object_unref (bus);
+
+ n_buffers = 0;
+ fail_unless_equals_int (gst_element_set_state (pipeline, GST_STATE_PLAYING),
+ GST_STATE_CHANGE_SUCCESS);
+
+ g_main_loop_run (loop);
+
+ fail_unless_equals_int (n_buffers, 1);
+
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+
+ gst_object_unref (pipeline);
+ g_main_loop_unref (loop);
+ gst_caps_unref (caps1);
+ gst_caps_unref (caps2);
+ g_source_remove (bus_watch);
+}
+
+GST_END_TEST;
+
+static void
+sink_handoff_cb_25_1_0ms_400ms (GstElement * object, GstBuffer * buffer,
+ GstPad * pad, gpointer user_data)
+{
+ guint *n_buffers = (guint *) user_data;
+
+ if (*n_buffers == G_MAXUINT)
+ return;
+
+ fail_unless_equals_uint64 (GST_BUFFER_TIMESTAMP (buffer),
+ *n_buffers * 40 * GST_MSECOND);
+ fail_unless_equals_uint64 (GST_BUFFER_DURATION (buffer), 40 * GST_MSECOND);
+ fail_unless_equals_uint64 (GST_BUFFER_OFFSET (buffer), *n_buffers);
+ fail_unless_equals_uint64 (GST_BUFFER_OFFSET_END (buffer), *n_buffers + 1);
+
+ *n_buffers = *n_buffers + 1;
+}
+
+GST_START_TEST (test_imagefreeze_25_1_0ms_400ms)
+{
+ GstElement *pipeline;
+ GstCaps *caps1, *caps2;
+ GstBus *bus;
+ GMainLoop *loop;
+ guint n_buffers = G_MAXUINT;
+ guint bus_watch = 0;
+ GstVideoInfo i1, i2;
+
+ gst_video_info_init (&i1);
+ gst_video_info_set_format (&i1, GST_VIDEO_FORMAT_xRGB, 640, 480);
+ i1.fps_n = 25;
+ i1.fps_d = 1;
+ caps1 = gst_video_info_to_caps (&i1);
+
+ gst_video_info_init (&i2);
+ gst_video_info_set_format (&i2, GST_VIDEO_FORMAT_xRGB, 640, 480);
+ i2.fps_n = 25;
+ i2.fps_d = 1;
+ caps2 = gst_video_info_to_caps (&i2);
+
+ pipeline =
+ setup_imagefreeze (caps1, caps2,
+ G_CALLBACK (sink_handoff_cb_25_1_0ms_400ms), &n_buffers);
+
+ loop = g_main_loop_new (NULL, TRUE);
+ fail_unless (loop != NULL);
+
+ bus = gst_element_get_bus (pipeline);
+ fail_unless (bus != NULL);
+ bus_watch = gst_bus_add_watch (bus, bus_handler, loop);
+ gst_object_unref (bus);
+
+ fail_unless_equals_int (gst_element_set_state (pipeline, GST_STATE_PAUSED),
+ GST_STATE_CHANGE_SUCCESS);
+
+ fail_unless (gst_element_seek (pipeline, 1.0, GST_FORMAT_TIME,
+ GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_SET,
+ 400 * GST_MSECOND));
+
+ n_buffers = 0;
+
+ fail_unless_equals_int (gst_element_set_state (pipeline, GST_STATE_PLAYING),
+ GST_STATE_CHANGE_SUCCESS);
+
+ g_main_loop_run (loop);
+
+ fail_unless_equals_int (n_buffers, 10);
+
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+
+ gst_object_unref (pipeline);
+ g_main_loop_unref (loop);
+ gst_caps_unref (caps1);
+ gst_caps_unref (caps2);
+ g_source_remove (bus_watch);
+}
+
+GST_END_TEST;
+
+static void
+sink_handoff_cb_25_1_200ms_400ms (GstElement * object, GstBuffer * buffer,
+ GstPad * pad, gpointer user_data)
+{
+ guint *n_buffers = (guint *) user_data;
+
+ if (*n_buffers == G_MAXUINT)
+ return;
+
+ fail_unless_equals_uint64 (GST_BUFFER_TIMESTAMP (buffer),
+ 200 * GST_MSECOND + *n_buffers * 40 * GST_MSECOND);
+ fail_unless_equals_uint64 (GST_BUFFER_DURATION (buffer), 40 * GST_MSECOND);
+ fail_unless_equals_uint64 (GST_BUFFER_OFFSET (buffer), 5 + *n_buffers);
+ fail_unless_equals_uint64 (GST_BUFFER_OFFSET_END (buffer),
+ 5 + *n_buffers + 1);
+
+ *n_buffers = *n_buffers + 1;
+}
+
+GST_START_TEST (test_imagefreeze_25_1_200ms_400ms)
+{
+ GstElement *pipeline;
+ GstCaps *caps1, *caps2;
+ GstBus *bus;
+ GMainLoop *loop;
+ guint n_buffers = G_MAXUINT;
+ guint bus_watch = 0;
+ GstVideoInfo i1, i2;
+
+ gst_video_info_init (&i1);
+ gst_video_info_set_format (&i1, GST_VIDEO_FORMAT_xRGB, 640, 480);
+ i1.fps_n = 25;
+ i1.fps_d = 1;
+ caps1 = gst_video_info_to_caps (&i1);
+
+ gst_video_info_init (&i2);
+ gst_video_info_set_format (&i2, GST_VIDEO_FORMAT_xRGB, 640, 480);
+ i2.fps_n = 25;
+ i2.fps_d = 1;
+ caps2 = gst_video_info_to_caps (&i2);
+
+ pipeline =
+ setup_imagefreeze (caps1, caps2,
+ G_CALLBACK (sink_handoff_cb_25_1_200ms_400ms), &n_buffers);
+
+ loop = g_main_loop_new (NULL, TRUE);
+ fail_unless (loop != NULL);
+
+ bus = gst_element_get_bus (pipeline);
+ fail_unless (bus != NULL);
+ bus_watch = gst_bus_add_watch (bus, bus_handler, loop);
+ gst_object_unref (bus);
+
+ fail_unless_equals_int (gst_element_set_state (pipeline, GST_STATE_PAUSED),
+ GST_STATE_CHANGE_SUCCESS);
+
+ fail_unless (gst_element_seek (pipeline, 1.0, GST_FORMAT_TIME,
+ GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET, 200 * GST_MSECOND,
+ GST_SEEK_TYPE_SET, 400 * GST_MSECOND));
+
+ n_buffers = 0;
+
+ fail_unless_equals_int (gst_element_set_state (pipeline, GST_STATE_PLAYING),
+ GST_STATE_CHANGE_SUCCESS);
+
+ g_main_loop_run (loop);
+
+ fail_unless_equals_int (n_buffers, 5);
+
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+
+ gst_object_unref (pipeline);
+ g_main_loop_unref (loop);
+ gst_caps_unref (caps1);
+ gst_caps_unref (caps2);
+ g_source_remove (bus_watch);
+}
+
+GST_END_TEST;
+
+static void
+sink_handoff_cb_25_1_400ms_0ms (GstElement * object, GstBuffer * buffer,
+ GstPad * pad, gpointer user_data)
+{
+ guint *n_buffers = (guint *) user_data;
+
+ if (*n_buffers == G_MAXUINT)
+ return;
+
+ fail_unless_equals_uint64 (GST_BUFFER_TIMESTAMP (buffer),
+ 400 * GST_MSECOND - (*n_buffers + 1) * 40 * GST_MSECOND);
+ fail_unless_equals_uint64 (GST_BUFFER_DURATION (buffer), 40 * GST_MSECOND);
+ fail_unless_equals_uint64 (GST_BUFFER_OFFSET (buffer), 10 - (*n_buffers + 1));
+ fail_unless_equals_uint64 (GST_BUFFER_OFFSET_END (buffer),
+ 10 - (*n_buffers + 1) + 1);
+
+ *n_buffers = *n_buffers + 1;
+}
+
+GST_START_TEST (test_imagefreeze_25_1_400ms_0ms)
+{
+ GstElement *pipeline;
+ GstCaps *caps1, *caps2;
+ GstBus *bus;
+ GMainLoop *loop;
+ guint n_buffers = G_MAXUINT;
+ guint bus_watch = 0;
+ GstVideoInfo i1, i2;
+
+ gst_video_info_init (&i1);
+ gst_video_info_set_format (&i1, GST_VIDEO_FORMAT_xRGB, 640, 480);
+ i1.fps_n = 25;
+ i1.fps_d = 1;
+ caps1 = gst_video_info_to_caps (&i1);
+
+ gst_video_info_init (&i2);
+ gst_video_info_set_format (&i2, GST_VIDEO_FORMAT_xRGB, 640, 480);
+ i2.fps_n = 25;
+ i2.fps_d = 1;
+ caps2 = gst_video_info_to_caps (&i2);
+
+ pipeline =
+ setup_imagefreeze (caps1, caps2,
+ G_CALLBACK (sink_handoff_cb_25_1_400ms_0ms), &n_buffers);
+
+ loop = g_main_loop_new (NULL, TRUE);
+ fail_unless (loop != NULL);
+
+ bus = gst_element_get_bus (pipeline);
+ fail_unless (bus != NULL);
+ bus_watch = gst_bus_add_watch (bus, bus_handler, loop);
+ gst_object_unref (bus);
+
+ fail_unless_equals_int (gst_element_set_state (pipeline, GST_STATE_PAUSED),
+ GST_STATE_CHANGE_SUCCESS);
+
+ fail_unless (gst_element_seek (pipeline, -1.0, GST_FORMAT_TIME,
+ GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_SET,
+ 400 * GST_MSECOND));
+
+ n_buffers = 0;
+
+ fail_unless_equals_int (gst_element_set_state (pipeline, GST_STATE_PLAYING),
+ GST_STATE_CHANGE_SUCCESS);
+
+ g_main_loop_run (loop);
+
+ fail_unless_equals_int (n_buffers, 10);
+
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+
+ gst_object_unref (pipeline);
+ g_main_loop_unref (loop);
+ gst_caps_unref (caps1);
+ gst_caps_unref (caps2);
+ g_source_remove (bus_watch);
+}
+
+GST_END_TEST;
+
+static void
+sink_handoff_cb_25_1_220ms_380ms (GstElement * object, GstBuffer * buffer,
+ GstPad * pad, gpointer user_data)
+{
+ guint *n_buffers = (guint *) user_data;
+
+ if (*n_buffers == G_MAXUINT)
+ return;
+
+ if (*n_buffers == 0) {
+ fail_unless_equals_uint64 (GST_BUFFER_TIMESTAMP (buffer),
+ 220 * GST_MSECOND);
+ fail_unless_equals_uint64 (GST_BUFFER_DURATION (buffer), 20 * GST_MSECOND);
+ } else if (*n_buffers == 4) {
+ fail_unless_equals_uint64 (GST_BUFFER_TIMESTAMP (buffer),
+ 360 * GST_MSECOND);
+ fail_unless_equals_uint64 (GST_BUFFER_DURATION (buffer), 20 * GST_MSECOND);
+ } else {
+ fail_unless_equals_uint64 (GST_BUFFER_TIMESTAMP (buffer),
+ 200 * GST_MSECOND + *n_buffers * 40 * GST_MSECOND);
+ fail_unless_equals_uint64 (GST_BUFFER_DURATION (buffer), 40 * GST_MSECOND);
+ }
+
+ fail_unless_equals_uint64 (GST_BUFFER_OFFSET (buffer), 5 + *n_buffers);
+ fail_unless_equals_uint64 (GST_BUFFER_OFFSET_END (buffer),
+ 5 + *n_buffers + 1);
+
+ *n_buffers = *n_buffers + 1;
+}
+
+GST_START_TEST (test_imagefreeze_25_1_220ms_380ms)
+{
+ GstElement *pipeline;
+ GstCaps *caps1, *caps2;
+ GstBus *bus;
+ GMainLoop *loop;
+ guint n_buffers = G_MAXUINT;
+ guint bus_watch = 0;
+ GstVideoInfo i1, i2;
+
+ gst_video_info_init (&i1);
+ gst_video_info_set_format (&i1, GST_VIDEO_FORMAT_xRGB, 640, 480);
+ i1.fps_n = 25;
+ i1.fps_d = 1;
+ caps1 = gst_video_info_to_caps (&i1);
+
+ gst_video_info_init (&i2);
+ gst_video_info_set_format (&i2, GST_VIDEO_FORMAT_xRGB, 640, 480);
+ i2.fps_n = 25;
+ i2.fps_d = 1;
+ caps2 = gst_video_info_to_caps (&i2);
+
+ pipeline =
+ setup_imagefreeze (caps1, caps2,
+ G_CALLBACK (sink_handoff_cb_25_1_220ms_380ms), &n_buffers);
+
+ loop = g_main_loop_new (NULL, TRUE);
+ fail_unless (loop != NULL);
+
+ bus = gst_element_get_bus (pipeline);
+ fail_unless (bus != NULL);
+ bus_watch = gst_bus_add_watch (bus, bus_handler, loop);
+ gst_object_unref (bus);
+
+ fail_unless_equals_int (gst_element_set_state (pipeline, GST_STATE_PAUSED),
+ GST_STATE_CHANGE_SUCCESS);
+
+ fail_unless (gst_element_seek (pipeline, 1.0, GST_FORMAT_TIME,
+ GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET, 220 * GST_MSECOND,
+ GST_SEEK_TYPE_SET, 380 * GST_MSECOND));
+
+ n_buffers = 0;
+
+ fail_unless_equals_int (gst_element_set_state (pipeline, GST_STATE_PLAYING),
+ GST_STATE_CHANGE_SUCCESS);
+
+ g_main_loop_run (loop);
+
+ fail_unless_equals_int (n_buffers, 5);
+
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+
+ gst_object_unref (pipeline);
+ g_main_loop_unref (loop);
+ gst_caps_unref (caps1);
+ gst_caps_unref (caps2);
+ g_source_remove (bus_watch);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_imagefreeze_eos)
+{
+ GstElement *pipeline;
+ GstElement *src;
+ GstCaps *caps1, *caps2;
+ GstBus *bus;
+ GMainLoop *loop;
+ GstFormat fmt = GST_FORMAT_TIME;
+ gint64 position;
+ guint bus_watch = 0;
+ GstVideoInfo i1, i2;
+
+ gst_video_info_init (&i1);
+ gst_video_info_set_format (&i1, GST_VIDEO_FORMAT_xRGB, 640, 480);
+ i1.fps_n = 25;
+ i1.fps_d = 1;
+ caps1 = gst_video_info_to_caps (&i1);
+
+ gst_video_info_init (&i2);
+ gst_video_info_set_format (&i2, GST_VIDEO_FORMAT_xRGB, 640, 480);
+ i2.fps_n = 25;
+ i2.fps_d = 1;
+ caps2 = gst_video_info_to_caps (&i2);
+
+ pipeline = setup_imagefreeze (caps1, caps2, NULL, NULL);
+
+ src = gst_bin_get_by_name (GST_BIN (pipeline), "src");
+ fail_unless (src != NULL);
+ g_object_set (src, "num-buffers", 100, NULL);
+
+ loop = g_main_loop_new (NULL, TRUE);
+ fail_unless (loop != NULL);
+
+ bus = gst_element_get_bus (pipeline);
+ fail_unless (bus != NULL);
+ bus_watch = gst_bus_add_watch (bus, bus_handler, loop);
+ gst_object_unref (bus);
+
+ fail_unless_equals_int (gst_element_set_state (pipeline, GST_STATE_PAUSED),
+ GST_STATE_CHANGE_SUCCESS);
+
+ fail_unless (gst_element_seek (pipeline, 1.0, GST_FORMAT_TIME,
+ GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_SET,
+ 400 * GST_MSECOND));
+
+ fail_unless_equals_int (gst_element_set_state (pipeline, GST_STATE_PLAYING),
+ GST_STATE_CHANGE_SUCCESS);
+
+ g_main_loop_run (loop);
+
+ fail_unless (gst_element_query_position (src, fmt, &position));
+ fail_unless_equals_uint64 (position, 40 * GST_MSECOND);
+
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+
+ gst_object_unref (src);
+ gst_object_unref (pipeline);
+ g_main_loop_unref (loop);
+ gst_caps_unref (caps1);
+ gst_caps_unref (caps2);
+ g_source_remove (bus_watch);
+}
+
+GST_END_TEST;
+
+static Suite *
+imagefreeze_suite (void)
+{
+ Suite *s = suite_create ("imagefreeze");
+ TCase *tc_chain = tcase_create ("linear");
+
+ /* time out after 120s, not the default 3 */
+ tcase_set_timeout (tc_chain, 120);
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_imagefreeze_0_1);
+ tcase_add_test (tc_chain, test_imagefreeze_25_1_0ms_400ms);
+ tcase_add_test (tc_chain, test_imagefreeze_25_1_200ms_400ms);
+ tcase_add_test (tc_chain, test_imagefreeze_25_1_400ms_0ms);
+ tcase_add_test (tc_chain, test_imagefreeze_25_1_220ms_380ms);
+
+ tcase_add_test (tc_chain, test_imagefreeze_eos);
+
+ return s;
+}
+
+GST_CHECK_MAIN (imagefreeze);
diff --git a/tests/check/elements/interleave.c b/tests/check/elements/interleave.c
new file mode 100755
index 0000000..67355e8
--- /dev/null
+++ b/tests/check/elements/interleave.c
@@ -0,0 +1,824 @@
+/* GStreamer unit tests for the interleave element
+ * Copyright (C) 2007 Tim-Philipp Müller <tim centricular net>
+ * Copyright (C) 2008 Sebastian Dröge <slomo@circular-chaos.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 0.11: suppress warnings for deprecated API such as GValueArray
+ * with newer GLib versions (>= 2.31.0) */
+#define GLIB_DISABLE_DEPRECATION_WARNINGS
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_VALGRIND
+# include <valgrind/valgrind.h>
+#endif
+
+#include <gst/check/gstcheck.h>
+#include <gst/audio/audio.h>
+#include <gst/audio/audio-enumtypes.h>
+
+static void
+gst_check_setup_events_interleave (GstPad * srcpad, GstElement * element,
+ GstCaps * caps, GstFormat format, const gchar * stream_id)
+{
+ GstSegment segment;
+
+ gst_segment_init (&segment, format);
+
+ fail_unless (gst_pad_push_event (srcpad,
+ gst_event_new_stream_start (stream_id)));
+ if (caps)
+ fail_unless (gst_pad_push_event (srcpad, gst_event_new_caps (caps)));
+ fail_unless (gst_pad_push_event (srcpad, gst_event_new_segment (&segment)));
+}
+
+GST_START_TEST (test_create_and_unref)
+{
+ GstElement *interleave;
+
+ interleave = gst_element_factory_make ("interleave", NULL);
+ fail_unless (interleave != NULL);
+
+ gst_element_set_state (interleave, GST_STATE_NULL);
+ gst_object_unref (interleave);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_request_pads)
+{
+ GstElement *interleave;
+ GstPad *pad1, *pad2;
+
+ interleave = gst_element_factory_make ("interleave", NULL);
+ fail_unless (interleave != NULL);
+
+ pad1 = gst_element_get_request_pad (interleave, "sink_%u");
+ fail_unless (pad1 != NULL);
+ fail_unless_equals_string (GST_OBJECT_NAME (pad1), "sink_0");
+
+ pad2 = gst_element_get_request_pad (interleave, "sink_%u");
+ fail_unless (pad2 != NULL);
+ fail_unless_equals_string (GST_OBJECT_NAME (pad2), "sink_1");
+
+ gst_element_release_request_pad (interleave, pad2);
+ gst_object_unref (pad2);
+ gst_element_release_request_pad (interleave, pad1);
+ gst_object_unref (pad1);
+
+ gst_element_set_state (interleave, GST_STATE_NULL);
+ gst_object_unref (interleave);
+}
+
+GST_END_TEST;
+
+static GstPad **mysrcpads, *mysinkpad;
+static GstBus *bus;
+static GstElement *interleave;
+static gint have_data;
+static gfloat input[2];
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "format = (string) " GST_AUDIO_NE (F32) ", "
+ "channels = (int) 2, layout = (string) {interleaved, non-interleaved}, rate = (int) 48000"));
+
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "format = (string) " GST_AUDIO_NE (F32) ", "
+ "channels = (int) 1, layout = (string) interleaved, rate = (int) 48000"));
+
+#define CAPS_48khz \
+ "audio/x-raw, " \
+ "format = (string) " GST_AUDIO_NE (F32) ", " \
+ "channels = (int) 1, layout = (string) non-interleaved," \
+ "rate = (int) 48000"
+
+static GstFlowReturn
+interleave_chain_func (GstPad * pad, GstObject * parent, GstBuffer * buffer)
+{
+ GstMapInfo map;
+ gfloat *outdata;
+ gint i;
+
+ fail_unless (GST_IS_BUFFER (buffer));
+ gst_buffer_map (buffer, &map, GST_MAP_READ);
+ outdata = (gfloat *) map.data;
+ fail_unless_equals_int (map.size, 48000 * 2 * sizeof (gfloat));
+ fail_unless (outdata != NULL);
+
+#ifdef HAVE_VALGRIND
+ if (!(RUNNING_ON_VALGRIND))
+#endif
+ for (i = 0; i < 48000 * 2; i += 2) {
+ fail_unless_equals_float (outdata[i], input[0]);
+ fail_unless_equals_float (outdata[i + 1], input[1]);
+ }
+ gst_buffer_unmap (buffer, &map);
+ gst_buffer_unref (buffer);
+
+ have_data++;
+
+ return GST_FLOW_OK;
+}
+
+GST_START_TEST (test_interleave_2ch)
+{
+ GstElement *queue;
+ GstPad *sink0, *sink1, *src, *tmp;
+ GstCaps *caps;
+ gint i;
+ GstBuffer *inbuf;
+ gfloat *indata;
+ GstMapInfo map;
+
+ mysrcpads = g_new0 (GstPad *, 2);
+
+ have_data = 0;
+
+ interleave = gst_element_factory_make ("interleave", NULL);
+ fail_unless (interleave != NULL);
+
+ queue = gst_element_factory_make ("queue", "queue");
+ fail_unless (queue != NULL);
+
+ sink0 = gst_element_get_request_pad (interleave, "sink_%u");
+ fail_unless (sink0 != NULL);
+ fail_unless_equals_string (GST_OBJECT_NAME (sink0), "sink_0");
+
+ sink1 = gst_element_get_request_pad (interleave, "sink_%u");
+ fail_unless (sink1 != NULL);
+ fail_unless_equals_string (GST_OBJECT_NAME (sink1), "sink_1");
+
+ mysrcpads[0] = gst_pad_new_from_static_template (&srctemplate, "src0");
+ fail_unless (mysrcpads[0] != NULL);
+
+ caps = gst_caps_from_string (CAPS_48khz);
+ gst_pad_set_active (mysrcpads[0], TRUE);
+ gst_check_setup_events_interleave (mysrcpads[0], interleave, caps,
+ GST_FORMAT_TIME, "0");
+ gst_pad_use_fixed_caps (mysrcpads[0]);
+
+ mysrcpads[1] = gst_pad_new_from_static_template (&srctemplate, "src1");
+ fail_unless (mysrcpads[1] != NULL);
+
+ gst_pad_set_active (mysrcpads[1], TRUE);
+ gst_check_setup_events_interleave (mysrcpads[1], interleave, caps,
+ GST_FORMAT_TIME, "1");
+ gst_pad_use_fixed_caps (mysrcpads[1]);
+
+ tmp = gst_element_get_static_pad (queue, "sink");
+ fail_unless (gst_pad_link (mysrcpads[0], tmp) == GST_PAD_LINK_OK);
+ gst_object_unref (tmp);
+ tmp = gst_element_get_static_pad (queue, "src");
+ fail_unless (gst_pad_link (tmp, sink0) == GST_PAD_LINK_OK);
+ gst_object_unref (tmp);
+
+ fail_unless (gst_pad_link (mysrcpads[1], sink1) == GST_PAD_LINK_OK);
+
+ mysinkpad = gst_pad_new_from_static_template (&sinktemplate, "sink");
+ fail_unless (mysinkpad != NULL);
+ gst_pad_set_chain_function (mysinkpad, interleave_chain_func);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ src = gst_element_get_static_pad (interleave, "src");
+ fail_unless (src != NULL);
+ fail_unless (gst_pad_link (src, mysinkpad) == GST_PAD_LINK_OK);
+ gst_object_unref (src);
+
+ bus = gst_bus_new ();
+ gst_element_set_bus (interleave, bus);
+
+ fail_unless (gst_element_set_state (interleave,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS);
+ fail_unless (gst_element_set_state (queue,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS);
+
+ input[0] = -1.0;
+ inbuf = gst_buffer_new_and_alloc (48000 * sizeof (gfloat));
+ gst_buffer_map (inbuf, &map, GST_MAP_WRITE);
+ indata = (gfloat *) map.data;
+ for (i = 0; i < 48000; i++)
+ indata[i] = -1.0;
+ gst_buffer_unmap (inbuf, &map);
+ fail_unless (gst_pad_push (mysrcpads[0], inbuf) == GST_FLOW_OK);
+
+ input[1] = 1.0;
+ inbuf = gst_buffer_new_and_alloc (48000 * sizeof (gfloat));
+ gst_buffer_map (inbuf, &map, GST_MAP_WRITE);
+ indata = (gfloat *) map.data;
+ for (i = 0; i < 48000; i++)
+ indata[i] = 1.0;
+ gst_buffer_unmap (inbuf, &map);
+ fail_unless (gst_pad_push (mysrcpads[1], inbuf) == GST_FLOW_OK);
+
+ inbuf = gst_buffer_new_and_alloc (48000 * sizeof (gfloat));
+ gst_buffer_map (inbuf, &map, GST_MAP_WRITE);
+ indata = (gfloat *) map.data;
+ for (i = 0; i < 48000; i++)
+ indata[i] = -1.0;
+ gst_buffer_unmap (inbuf, &map);
+ fail_unless (gst_pad_push (mysrcpads[0], inbuf) == GST_FLOW_OK);
+
+ inbuf = gst_buffer_new_and_alloc (48000 * sizeof (gfloat));
+ gst_buffer_map (inbuf, &map, GST_MAP_WRITE);
+ indata = (gfloat *) map.data;
+ for (i = 0; i < 48000; i++)
+ indata[i] = 1.0;
+ gst_buffer_unmap (inbuf, &map);
+ fail_unless (gst_pad_push (mysrcpads[1], inbuf) == GST_FLOW_OK);
+
+ fail_unless (have_data == 2);
+
+ gst_element_set_state (interleave, GST_STATE_NULL);
+ gst_element_set_state (queue, GST_STATE_NULL);
+
+ gst_object_unref (mysrcpads[0]);
+ gst_object_unref (mysrcpads[1]);
+ gst_object_unref (mysinkpad);
+
+ gst_element_release_request_pad (interleave, sink0);
+ gst_object_unref (sink0);
+ gst_element_release_request_pad (interleave, sink1);
+ gst_object_unref (sink1);
+
+ gst_object_unref (interleave);
+ gst_object_unref (queue);
+ gst_object_unref (bus);
+ gst_caps_unref (caps);
+
+ g_free (mysrcpads);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_interleave_2ch_1eos)
+{
+ GstElement *queue;
+ GstPad *sink0, *sink1, *src, *tmp;
+ GstCaps *caps;
+ gint i;
+ GstBuffer *inbuf;
+ gfloat *indata;
+ GstMapInfo map;
+
+ mysrcpads = g_new0 (GstPad *, 2);
+
+ have_data = 0;
+
+ interleave = gst_element_factory_make ("interleave", NULL);
+ fail_unless (interleave != NULL);
+
+ queue = gst_element_factory_make ("queue", "queue");
+ fail_unless (queue != NULL);
+
+ sink0 = gst_element_get_request_pad (interleave, "sink_%u");
+ fail_unless (sink0 != NULL);
+ fail_unless_equals_string (GST_OBJECT_NAME (sink0), "sink_0");
+
+ sink1 = gst_element_get_request_pad (interleave, "sink_%u");
+ fail_unless (sink1 != NULL);
+ fail_unless_equals_string (GST_OBJECT_NAME (sink1), "sink_1");
+
+ mysrcpads[0] = gst_pad_new_from_static_template (&srctemplate, "src0");
+ fail_unless (mysrcpads[0] != NULL);
+
+ caps = gst_caps_from_string (CAPS_48khz);
+ gst_pad_set_active (mysrcpads[0], TRUE);
+ gst_check_setup_events_interleave (mysrcpads[0], interleave, caps,
+ GST_FORMAT_TIME, "0");
+ gst_pad_use_fixed_caps (mysrcpads[0]);
+
+ mysrcpads[1] = gst_pad_new_from_static_template (&srctemplate, "src1");
+ fail_unless (mysrcpads[1] != NULL);
+
+ gst_pad_set_active (mysrcpads[1], TRUE);
+ gst_check_setup_events_interleave (mysrcpads[1], interleave, caps,
+ GST_FORMAT_TIME, "1");
+ gst_pad_use_fixed_caps (mysrcpads[1]);
+
+ tmp = gst_element_get_static_pad (queue, "sink");
+ fail_unless (gst_pad_link (mysrcpads[0], tmp) == GST_PAD_LINK_OK);
+ gst_object_unref (tmp);
+ tmp = gst_element_get_static_pad (queue, "src");
+ fail_unless (gst_pad_link (tmp, sink0) == GST_PAD_LINK_OK);
+ gst_object_unref (tmp);
+
+ fail_unless (gst_pad_link (mysrcpads[1], sink1) == GST_PAD_LINK_OK);
+
+ mysinkpad = gst_pad_new_from_static_template (&sinktemplate, "sink");
+ fail_unless (mysinkpad != NULL);
+ gst_pad_set_chain_function (mysinkpad, interleave_chain_func);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ src = gst_element_get_static_pad (interleave, "src");
+ fail_unless (src != NULL);
+ fail_unless (gst_pad_link (src, mysinkpad) == GST_PAD_LINK_OK);
+ gst_object_unref (src);
+
+ bus = gst_bus_new ();
+ gst_element_set_bus (interleave, bus);
+
+ fail_unless (gst_element_set_state (interleave,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS);
+ fail_unless (gst_element_set_state (queue,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS);
+
+ input[0] = -1.0;
+ inbuf = gst_buffer_new_and_alloc (48000 * sizeof (gfloat));
+ gst_buffer_map (inbuf, &map, GST_MAP_WRITE);
+ indata = (gfloat *) map.data;
+ for (i = 0; i < 48000; i++)
+ indata[i] = -1.0;
+ gst_buffer_unmap (inbuf, &map);
+ fail_unless (gst_pad_push (mysrcpads[0], inbuf) == GST_FLOW_OK);
+
+ input[1] = 1.0;
+ inbuf = gst_buffer_new_and_alloc (48000 * sizeof (gfloat));
+ gst_buffer_map (inbuf, &map, GST_MAP_WRITE);
+ indata = (gfloat *) map.data;
+ for (i = 0; i < 48000; i++)
+ indata[i] = 1.0;
+ gst_buffer_unmap (inbuf, &map);
+ fail_unless (gst_pad_push (mysrcpads[1], inbuf) == GST_FLOW_OK);
+
+ input[0] = 0.0;
+ gst_pad_push_event (mysrcpads[0], gst_event_new_eos ());
+
+ input[1] = 1.0;
+ inbuf = gst_buffer_new_and_alloc (48000 * sizeof (gfloat));
+ gst_buffer_map (inbuf, &map, GST_MAP_WRITE);
+ indata = (gfloat *) map.data;
+ for (i = 0; i < 48000; i++)
+ indata[i] = 1.0;
+ gst_buffer_unmap (inbuf, &map);
+ fail_unless (gst_pad_push (mysrcpads[1], inbuf) == GST_FLOW_OK);
+
+ fail_unless (have_data == 2);
+
+ gst_element_set_state (interleave, GST_STATE_NULL);
+ gst_element_set_state (queue, GST_STATE_NULL);
+
+ gst_object_unref (mysrcpads[0]);
+ gst_object_unref (mysrcpads[1]);
+ gst_object_unref (mysinkpad);
+
+ gst_element_release_request_pad (interleave, sink0);
+ gst_object_unref (sink0);
+ gst_element_release_request_pad (interleave, sink1);
+ gst_object_unref (sink1);
+
+ gst_object_unref (interleave);
+ gst_object_unref (queue);
+ gst_object_unref (bus);
+ gst_caps_unref (caps);
+
+ g_free (mysrcpads);
+}
+
+GST_END_TEST;
+
+static void
+src_handoff_float32 (GstElement * element, GstBuffer * buffer, GstPad * pad,
+ gboolean interleaved, gpointer user_data)
+{
+ gint n = GPOINTER_TO_INT (user_data);
+ gfloat *data;
+ gint i;
+ gsize size;
+ GstCaps *caps;
+ guint64 mask;
+ GstAudioChannelPosition pos;
+
+ switch (n) {
+ case 0:
+ case 1:
+ case 2:
+ pos = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
+ break;
+ case 3:
+ pos = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
+ break;
+ default:
+ pos = GST_AUDIO_CHANNEL_POSITION_INVALID;
+ break;
+ }
+
+ mask = G_GUINT64_CONSTANT (1) << pos;
+
+ caps = gst_caps_new_simple ("audio/x-raw",
+ "format", G_TYPE_STRING, GST_AUDIO_NE (F32),
+ "channels", G_TYPE_INT, 1,
+ "layout", G_TYPE_STRING, interleaved ? "interleaved" : "non-interleaved",
+ "channel-mask", GST_TYPE_BITMASK, mask, "rate", G_TYPE_INT, 48000, NULL);
+
+ gst_pad_set_caps (pad, caps);
+ gst_caps_unref (caps);
+
+ size = 48000 * sizeof (gfloat);
+ data = g_malloc (size);
+ for (i = 0; i < 48000; i++)
+ data[i] = (n % 2 == 0) ? -1.0 : 1.0;
+
+ gst_buffer_append_memory (buffer, gst_memory_new_wrapped (0, data,
+ size, 0, size, data, g_free));
+
+ GST_BUFFER_OFFSET (buffer) = GST_BUFFER_OFFSET_NONE;
+ GST_BUFFER_TIMESTAMP (buffer) = GST_CLOCK_TIME_NONE;
+ GST_BUFFER_OFFSET_END (buffer) = GST_BUFFER_OFFSET_NONE;
+ GST_BUFFER_DURATION (buffer) = GST_SECOND;
+}
+
+static void
+src_handoff_float32_interleaved (GstElement * element, GstBuffer * buffer,
+ GstPad * pad, gpointer user_data)
+{
+ src_handoff_float32 (element, buffer, pad, TRUE, user_data);
+}
+
+static void
+src_handoff_float32_non_interleaved (GstElement * element, GstBuffer * buffer,
+ GstPad * pad, gpointer user_data)
+{
+ src_handoff_float32 (element, buffer, pad, FALSE, user_data);
+}
+
+static void
+sink_handoff_float32 (GstElement * element, GstBuffer * buffer, GstPad * pad,
+ gpointer user_data)
+{
+ gint i;
+ GstMapInfo map;
+ gfloat *data;
+ GstCaps *caps, *ccaps;
+ gint n = GPOINTER_TO_INT (user_data);
+ guint64 mask;
+
+ fail_unless (GST_IS_BUFFER (buffer));
+ gst_buffer_map (buffer, &map, GST_MAP_READ);
+ data = (gfloat *) map.data;
+
+ fail_unless_equals_int (map.size, 48000 * 2 * sizeof (gfloat));
+ fail_unless_equals_int (GST_BUFFER_DURATION (buffer), GST_SECOND);
+
+ if (n == 0) {
+ GstAudioChannelPosition pos[2] =
+ { GST_AUDIO_CHANNEL_POSITION_NONE, GST_AUDIO_CHANNEL_POSITION_NONE };
+ gst_audio_channel_positions_to_mask (pos, 2, FALSE, &mask);
+ } else if (n == 1) {
+ GstAudioChannelPosition pos[2] = { GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
+ GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT
+ };
+ gst_audio_channel_positions_to_mask (pos, 2, FALSE, &mask);
+ } else if (n == 2) {
+ GstAudioChannelPosition pos[2] = { GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
+ GST_AUDIO_CHANNEL_POSITION_REAR_CENTER
+ };
+ gst_audio_channel_positions_to_mask (pos, 2, FALSE, &mask);
+ } else {
+ g_assert_not_reached ();
+ }
+
+ caps = gst_caps_new_simple ("audio/x-raw",
+ "format", G_TYPE_STRING, GST_AUDIO_NE (F32),
+ "channels", G_TYPE_INT, 2, "rate", G_TYPE_INT, 48000,
+ "layout", G_TYPE_STRING, "interleaved",
+ "channel-mask", GST_TYPE_BITMASK, mask, NULL);
+
+ ccaps = gst_pad_get_current_caps (pad);
+ fail_unless (gst_caps_is_equal (caps, ccaps));
+ gst_caps_unref (ccaps);
+ gst_caps_unref (caps);
+
+#ifdef HAVE_VALGRIND
+ if (!(RUNNING_ON_VALGRIND))
+#endif
+ for (i = 0; i < 48000 * 2; i += 2) {
+ fail_unless_equals_float (data[i], -1.0);
+ fail_unless_equals_float (data[i + 1], 1.0);
+ }
+ gst_buffer_unmap (buffer, &map);
+
+ have_data++;
+}
+
+static void
+test_interleave_2ch_pipeline (gboolean interleaved)
+{
+ GstElement *pipeline, *queue, *src1, *src2, *interleave, *sink;
+ GstPad *sinkpad0, *sinkpad1, *tmp, *tmp2;
+ GstMessage *msg;
+ void *src_handoff_float32 =
+ interleaved ? &src_handoff_float32_interleaved :
+ &src_handoff_float32_non_interleaved;
+
+ have_data = 0;
+
+ pipeline = (GstElement *) gst_pipeline_new ("pipeline");
+ fail_unless (pipeline != NULL);
+
+ src1 = gst_element_factory_make ("fakesrc", "src1");
+ fail_unless (src1 != NULL);
+ g_object_set (src1, "num-buffers", 4, NULL);
+ g_object_set (src1, "signal-handoffs", TRUE, NULL);
+ g_signal_connect (src1, "handoff", G_CALLBACK (src_handoff_float32),
+ GINT_TO_POINTER (0));
+ gst_bin_add (GST_BIN (pipeline), src1);
+
+ src2 = gst_element_factory_make ("fakesrc", "src2");
+ fail_unless (src2 != NULL);
+ g_object_set (src2, "num-buffers", 4, NULL);
+ g_object_set (src2, "signal-handoffs", TRUE, NULL);
+ g_signal_connect (src2, "handoff", G_CALLBACK (src_handoff_float32),
+ GINT_TO_POINTER (1));
+ gst_bin_add (GST_BIN (pipeline), src2);
+
+ queue = gst_element_factory_make ("queue", "queue");
+ fail_unless (queue != NULL);
+ gst_bin_add (GST_BIN (pipeline), queue);
+
+ interleave = gst_element_factory_make ("interleave", "interleave");
+ fail_unless (interleave != NULL);
+ gst_bin_add (GST_BIN (pipeline), gst_object_ref (interleave));
+
+ sinkpad0 = gst_element_get_request_pad (interleave, "sink_%u");
+ fail_unless (sinkpad0 != NULL);
+ tmp = gst_element_get_static_pad (src1, "src");
+ fail_unless (gst_pad_link (tmp, sinkpad0) == GST_PAD_LINK_OK);
+ gst_object_unref (tmp);
+
+ sinkpad1 = gst_element_get_request_pad (interleave, "sink_%u");
+ fail_unless (sinkpad1 != NULL);
+ tmp = gst_element_get_static_pad (src2, "src");
+ tmp2 = gst_element_get_static_pad (queue, "sink");
+ fail_unless (gst_pad_link (tmp, tmp2) == GST_PAD_LINK_OK);
+ gst_object_unref (tmp);
+ gst_object_unref (tmp2);
+ tmp = gst_element_get_static_pad (queue, "src");
+ fail_unless (gst_pad_link (tmp, sinkpad1) == GST_PAD_LINK_OK);
+ gst_object_unref (tmp);
+
+ sink = gst_element_factory_make ("fakesink", "sink");
+ fail_unless (sink != NULL);
+ g_object_set (sink, "signal-handoffs", TRUE, NULL);
+ g_signal_connect (sink, "handoff", G_CALLBACK (sink_handoff_float32),
+ GINT_TO_POINTER (0));
+ gst_bin_add (GST_BIN (pipeline), sink);
+ tmp = gst_element_get_static_pad (interleave, "src");
+ tmp2 = gst_element_get_static_pad (sink, "sink");
+ fail_unless (gst_pad_link (tmp, tmp2) == GST_PAD_LINK_OK);
+ gst_object_unref (tmp);
+ gst_object_unref (tmp2);
+
+ gst_element_set_state (pipeline, GST_STATE_PLAYING);
+
+ msg = gst_bus_poll (GST_ELEMENT_BUS (pipeline), GST_MESSAGE_EOS, -1);
+ gst_message_unref (msg);
+
+ fail_unless (have_data == 4);
+
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+ gst_element_release_request_pad (interleave, sinkpad0);
+ gst_object_unref (sinkpad0);
+ gst_element_release_request_pad (interleave, sinkpad1);
+ gst_object_unref (sinkpad1);
+ gst_object_unref (interleave);
+ gst_object_unref (pipeline);
+}
+
+GST_START_TEST (test_interleave_2ch_pipeline_interleaved)
+{
+ test_interleave_2ch_pipeline (TRUE);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_interleave_2ch_pipeline_non_interleaved)
+{
+ test_interleave_2ch_pipeline (FALSE);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_interleave_2ch_pipeline_input_chanpos)
+{
+ GstElement *pipeline, *queue, *src1, *src2, *interleave, *sink;
+ GstPad *sinkpad0, *sinkpad1, *tmp, *tmp2;
+ GstMessage *msg;
+
+ have_data = 0;
+
+ pipeline = (GstElement *) gst_pipeline_new ("pipeline");
+ fail_unless (pipeline != NULL);
+
+ src1 = gst_element_factory_make ("fakesrc", "src1");
+ fail_unless (src1 != NULL);
+ g_object_set (src1, "num-buffers", 4, NULL);
+ g_object_set (src1, "signal-handoffs", TRUE, NULL);
+ g_signal_connect (src1, "handoff",
+ G_CALLBACK (src_handoff_float32_interleaved), GINT_TO_POINTER (2));
+ gst_bin_add (GST_BIN (pipeline), src1);
+
+ src2 = gst_element_factory_make ("fakesrc", "src2");
+ fail_unless (src2 != NULL);
+ g_object_set (src2, "num-buffers", 4, NULL);
+ g_object_set (src2, "signal-handoffs", TRUE, NULL);
+ g_signal_connect (src2, "handoff",
+ G_CALLBACK (src_handoff_float32_interleaved), GINT_TO_POINTER (3));
+ gst_bin_add (GST_BIN (pipeline), src2);
+
+ queue = gst_element_factory_make ("queue", "queue");
+ fail_unless (queue != NULL);
+ gst_bin_add (GST_BIN (pipeline), queue);
+
+ interleave = gst_element_factory_make ("interleave", "interleave");
+ fail_unless (interleave != NULL);
+ g_object_set (interleave, "channel-positions-from-input", TRUE, NULL);
+ gst_bin_add (GST_BIN (pipeline), gst_object_ref (interleave));
+
+ sinkpad0 = gst_element_get_request_pad (interleave, "sink_%u");
+ fail_unless (sinkpad0 != NULL);
+ tmp = gst_element_get_static_pad (src1, "src");
+ fail_unless (gst_pad_link (tmp, sinkpad0) == GST_PAD_LINK_OK);
+ gst_object_unref (tmp);
+
+ sinkpad1 = gst_element_get_request_pad (interleave, "sink_%u");
+ fail_unless (sinkpad1 != NULL);
+ tmp = gst_element_get_static_pad (src2, "src");
+ tmp2 = gst_element_get_static_pad (queue, "sink");
+ fail_unless (gst_pad_link (tmp, tmp2) == GST_PAD_LINK_OK);
+ gst_object_unref (tmp);
+ gst_object_unref (tmp2);
+ tmp = gst_element_get_static_pad (queue, "src");
+ fail_unless (gst_pad_link (tmp, sinkpad1) == GST_PAD_LINK_OK);
+ gst_object_unref (tmp);
+
+ sink = gst_element_factory_make ("fakesink", "sink");
+ fail_unless (sink != NULL);
+ g_object_set (sink, "signal-handoffs", TRUE, NULL);
+ g_signal_connect (sink, "handoff", G_CALLBACK (sink_handoff_float32),
+ GINT_TO_POINTER (1));
+ gst_bin_add (GST_BIN (pipeline), sink);
+ tmp = gst_element_get_static_pad (interleave, "src");
+ tmp2 = gst_element_get_static_pad (sink, "sink");
+ fail_unless (gst_pad_link (tmp, tmp2) == GST_PAD_LINK_OK);
+ gst_object_unref (tmp);
+ gst_object_unref (tmp2);
+
+ gst_element_set_state (pipeline, GST_STATE_PLAYING);
+
+ msg = gst_bus_poll (GST_ELEMENT_BUS (pipeline), GST_MESSAGE_EOS, -1);
+ gst_message_unref (msg);
+
+ fail_unless (have_data == 4);
+
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+ gst_element_release_request_pad (interleave, sinkpad0);
+ gst_object_unref (sinkpad0);
+ gst_element_release_request_pad (interleave, sinkpad1);
+ gst_object_unref (sinkpad1);
+ gst_object_unref (interleave);
+ gst_object_unref (pipeline);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_interleave_2ch_pipeline_custom_chanpos)
+{
+ GstElement *pipeline, *queue, *src1, *src2, *interleave, *sink;
+ GstPad *sinkpad0, *sinkpad1, *tmp, *tmp2;
+ GstMessage *msg;
+ GValueArray *arr;
+ GValue val = { 0, };
+
+ have_data = 0;
+
+ pipeline = (GstElement *) gst_pipeline_new ("pipeline");
+ fail_unless (pipeline != NULL);
+
+ src1 = gst_element_factory_make ("fakesrc", "src1");
+ fail_unless (src1 != NULL);
+ g_object_set (src1, "num-buffers", 4, NULL);
+ g_object_set (src1, "signal-handoffs", TRUE, NULL);
+ g_signal_connect (src1, "handoff",
+ G_CALLBACK (src_handoff_float32_interleaved), GINT_TO_POINTER (0));
+ gst_bin_add (GST_BIN (pipeline), src1);
+
+ src2 = gst_element_factory_make ("fakesrc", "src2");
+ fail_unless (src2 != NULL);
+ g_object_set (src2, "num-buffers", 4, NULL);
+ g_object_set (src2, "signal-handoffs", TRUE, NULL);
+ g_signal_connect (src2, "handoff",
+ G_CALLBACK (src_handoff_float32_interleaved), GINT_TO_POINTER (1));
+ gst_bin_add (GST_BIN (pipeline), src2);
+
+ queue = gst_element_factory_make ("queue", "queue");
+ fail_unless (queue != NULL);
+ gst_bin_add (GST_BIN (pipeline), queue);
+
+ interleave = gst_element_factory_make ("interleave", "interleave");
+ fail_unless (interleave != NULL);
+ g_object_set (interleave, "channel-positions-from-input", FALSE, NULL);
+ arr = g_value_array_new (2);
+
+ g_value_init (&val, GST_TYPE_AUDIO_CHANNEL_POSITION);
+ g_value_set_enum (&val, GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER);
+ g_value_array_append (arr, &val);
+ g_value_reset (&val);
+ g_value_set_enum (&val, GST_AUDIO_CHANNEL_POSITION_REAR_CENTER);
+ g_value_array_append (arr, &val);
+ g_value_unset (&val);
+
+ g_object_set (interleave, "channel-positions", arr, NULL);
+ g_value_array_free (arr);
+ gst_bin_add (GST_BIN (pipeline), gst_object_ref (interleave));
+
+ sinkpad0 = gst_element_get_request_pad (interleave, "sink_%u");
+ fail_unless (sinkpad0 != NULL);
+ tmp = gst_element_get_static_pad (src1, "src");
+ fail_unless (gst_pad_link (tmp, sinkpad0) == GST_PAD_LINK_OK);
+ gst_object_unref (tmp);
+
+ sinkpad1 = gst_element_get_request_pad (interleave, "sink_%u");
+ fail_unless (sinkpad1 != NULL);
+ tmp = gst_element_get_static_pad (src2, "src");
+ tmp2 = gst_element_get_static_pad (queue, "sink");
+ fail_unless (gst_pad_link (tmp, tmp2) == GST_PAD_LINK_OK);
+ gst_object_unref (tmp);
+ gst_object_unref (tmp2);
+ tmp = gst_element_get_static_pad (queue, "src");
+ fail_unless (gst_pad_link (tmp, sinkpad1) == GST_PAD_LINK_OK);
+ gst_object_unref (tmp);
+
+ sink = gst_element_factory_make ("fakesink", "sink");
+ fail_unless (sink != NULL);
+ g_object_set (sink, "signal-handoffs", TRUE, NULL);
+ g_signal_connect (sink, "handoff", G_CALLBACK (sink_handoff_float32),
+ GINT_TO_POINTER (2));
+ gst_bin_add (GST_BIN (pipeline), sink);
+ tmp = gst_element_get_static_pad (interleave, "src");
+ tmp2 = gst_element_get_static_pad (sink, "sink");
+ fail_unless (gst_pad_link (tmp, tmp2) == GST_PAD_LINK_OK);
+ gst_object_unref (tmp);
+ gst_object_unref (tmp2);
+
+ gst_element_set_state (pipeline, GST_STATE_PLAYING);
+
+ msg = gst_bus_poll (GST_ELEMENT_BUS (pipeline), GST_MESSAGE_EOS, -1);
+ gst_message_unref (msg);
+
+ fail_unless (have_data == 4);
+
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+ gst_element_release_request_pad (interleave, sinkpad0);
+ gst_object_unref (sinkpad0);
+ gst_element_release_request_pad (interleave, sinkpad1);
+ gst_object_unref (sinkpad1);
+ gst_object_unref (interleave);
+ gst_object_unref (pipeline);
+}
+
+GST_END_TEST;
+
+static Suite *
+interleave_suite (void)
+{
+ Suite *s = suite_create ("interleave");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_set_timeout (tc_chain, 180);
+ tcase_add_test (tc_chain, test_create_and_unref);
+ tcase_add_test (tc_chain, test_request_pads);
+ tcase_add_test (tc_chain, test_interleave_2ch);
+ tcase_add_test (tc_chain, test_interleave_2ch_1eos);
+ tcase_add_test (tc_chain, test_interleave_2ch_pipeline_interleaved);
+ tcase_add_test (tc_chain, test_interleave_2ch_pipeline_non_interleaved);
+ tcase_add_test (tc_chain, test_interleave_2ch_pipeline_input_chanpos);
+ tcase_add_test (tc_chain, test_interleave_2ch_pipeline_custom_chanpos);
+
+ return s;
+}
+
+GST_CHECK_MAIN (interleave);
diff --git a/tests/check/elements/jpegdec.c b/tests/check/elements/jpegdec.c
new file mode 100644
index 0000000..81b8b91
--- /dev/null
+++ b/tests/check/elements/jpegdec.c
@@ -0,0 +1,156 @@
+/* GStreamer
+ * unit test for jpegdec
+ * Copyright (C) <2010> Thiago Santos <thiago.sousa.santos@collabora.co.uk>
+ * Copyright (C) <2012> Mathias Hasselmann <mathias@openismus.com>
+ *
+ * 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.
+ */
+
+#include <unistd.h>
+
+#include <gio/gio.h>
+#include <gst/check/gstcheck.h>
+#include <gst/app/gstappsink.h>
+#include <gst/pbutils/gstdiscoverer.h>
+
+/* Verify jpegdec is working when explictly requested by a pipeline. */
+GST_START_TEST (test_jpegdec_explicit)
+{
+ GstElement *pipeline, *source, *dec, *sink;
+ GstSample *sample;
+
+ /* construct a pipeline that explicitly uses jpegdec */
+ pipeline = gst_pipeline_new (NULL);
+ source = gst_element_factory_make ("filesrc", NULL);
+ dec = gst_element_factory_make ("jpegdec", NULL);
+ sink = gst_element_factory_make ("appsink", NULL);
+
+ gst_bin_add_many (GST_BIN (pipeline), source, dec, sink, NULL);
+ gst_element_link_many (source, dec, sink, NULL);
+
+ /* point that pipeline to our test image */
+ {
+ char *filename = g_build_filename (GST_TEST_FILES_PATH, "image.jpg", NULL);
+ g_object_set (G_OBJECT (source), "location", filename, NULL);
+ g_free (filename);
+ }
+
+ gst_element_set_state (pipeline, GST_STATE_PLAYING);
+
+ sample = gst_app_sink_pull_sample (GST_APP_SINK (sink));
+ fail_unless (GST_IS_SAMPLE (sample));
+
+ /* do some basic checks to verify image decoding */
+ {
+ GstCaps *decoded;
+ GstCaps *expected;
+
+ decoded = gst_sample_get_caps (sample);
+ expected = gst_caps_from_string ("video/x-raw, width=120, height=160");
+
+ fail_unless (gst_caps_is_always_compatible (decoded, expected));
+
+ gst_caps_unref (expected);
+ }
+ gst_sample_unref (sample);
+
+ /* wait for EOS */
+ sample = gst_app_sink_pull_sample (GST_APP_SINK (sink));
+ fail_unless (sample == NULL);
+ fail_unless (gst_app_sink_is_eos (GST_APP_SINK (sink)));
+
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+ gst_object_unref (pipeline);
+}
+
+GST_END_TEST;
+
+/* Verify JPEG discovery is working. Right now jpegdec would be used,
+ * but I have no idea how to actually verify this. */
+GST_START_TEST (test_jpegdec_discover)
+{
+ GstDiscoverer *disco;
+ GError *error = NULL;
+ char *uri;
+ GstDiscovererInfo *info;
+ GstDiscovererVideoInfo *video;
+
+ disco = gst_discoverer_new (5 * GST_SECOND, &error);
+
+ fail_unless (GST_IS_DISCOVERER (disco));
+ fail_unless (error == NULL, "%s", (error ? error->message : ""));
+
+ {
+ GFile *testdir = g_file_new_for_path (GST_TEST_FILES_PATH);
+ GFile *testfile = g_file_resolve_relative_path (testdir, "image.jpg");
+ uri = g_file_get_uri (testfile);
+ g_object_unref (testfile);
+ g_object_unref (testdir);
+ }
+
+ info = gst_discoverer_discover_uri (disco, uri, &error);
+ fail_unless (GST_IS_DISCOVERER_INFO (info));
+ fail_unless (error == NULL, "%s: %s", uri, (error ? error->message : ""));
+
+ fail_unless_equals_string (gst_discoverer_info_get_uri (info), uri);
+ fail_unless_equals_int (gst_discoverer_info_get_result (info),
+ GST_DISCOVERER_OK);
+
+ video =
+ GST_DISCOVERER_VIDEO_INFO (gst_discoverer_info_get_stream_info (info));
+ fail_unless (video != NULL);
+
+ fail_unless (gst_discoverer_video_info_is_image (video));
+ fail_unless_equals_int (gst_discoverer_video_info_get_width (video), 120);
+ fail_unless_equals_int (gst_discoverer_video_info_get_height (video), 160);
+
+ gst_discoverer_info_unref (video);
+ gst_discoverer_info_unref (info);
+ g_free (uri);
+ g_object_unref (disco);
+}
+
+GST_END_TEST;
+
+static Suite *
+jpegdec_suite (void)
+{
+ Suite *s = suite_create ("jpegdec");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_jpegdec_explicit);
+ tcase_add_test (tc_chain, test_jpegdec_discover);
+
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ int nf;
+
+ Suite *s = jpegdec_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ srunner_run_all (sr, CK_NORMAL);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}
diff --git a/tests/check/elements/jpegenc.c b/tests/check/elements/jpegenc.c
new file mode 100644
index 0000000..fe55fb2
--- /dev/null
+++ b/tests/check/elements/jpegenc.c
@@ -0,0 +1,254 @@
+/* GStreamer
+ *
+ * unit test for jpegenc
+ *
+ * Copyright (C) <2010> Thiago Santos <thiago.sousa.santos@collabora.co.uk>
+ *
+ * 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.
+ */
+
+#include <unistd.h>
+
+#include <gst/check/gstcheck.h>
+#include <gst/app/gstappsink.h>
+
+/* For ease of programming we use globals to keep refs for our floating
+ * sink pads we create; otherwise we always have to do get_pad,
+ * get_peer, and then remove references in every test function */
+static GstPad *mysinkpad;
+static GstPad *mysrcpad;
+
+#define JPEG_CAPS_STRING "image/jpeg"
+
+#define JPEG_CAPS_RESTRICTIVE "image/jpeg, " \
+ "width = (int) [100, 200], " \
+ "framerate = (fraction) 25/1, " \
+ "extraparameter = (string) { abc, def }"
+
+static GstStaticPadTemplate jpeg_sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (JPEG_CAPS_STRING));
+
+static GstStaticPadTemplate any_sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+static GstStaticPadTemplate jpeg_restrictive_sinktemplate =
+GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (JPEG_CAPS_RESTRICTIVE));
+
+static GstStaticPadTemplate any_srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+
+static GstElement *
+setup_jpegenc (GstStaticPadTemplate * sinktemplate)
+{
+ GstElement *jpegenc;
+
+ GST_DEBUG ("setup_jpegenc");
+ jpegenc = gst_check_setup_element ("jpegenc");
+ mysinkpad = gst_check_setup_sink_pad (jpegenc, sinktemplate);
+ mysrcpad = gst_check_setup_src_pad (jpegenc, &any_srctemplate);
+ gst_pad_set_active (mysrcpad, TRUE);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ return jpegenc;
+}
+
+static void
+cleanup_jpegenc (GstElement * jpegenc)
+{
+ GST_DEBUG ("cleanup_jpegenc");
+ gst_element_set_state (jpegenc, GST_STATE_NULL);
+
+ gst_pad_set_active (mysrcpad, FALSE);
+ gst_pad_set_active (mysinkpad, FALSE);
+ gst_check_teardown_sink_pad (jpegenc);
+ gst_check_teardown_src_pad (jpegenc);
+ gst_check_teardown_element (jpegenc);
+}
+
+static GstBuffer *
+create_video_buffer (GstCaps * caps)
+{
+ GstElement *pipeline;
+ GstElement *cf;
+ GstElement *sink;
+ GstSample *sample;
+ GstBuffer *buffer;
+
+ pipeline =
+ gst_parse_launch
+ ("videotestsrc num-buffers=1 ! capsfilter name=cf ! appsink name=sink",
+ NULL);
+ g_assert (pipeline != NULL);
+
+ cf = gst_bin_get_by_name (GST_BIN (pipeline), "cf");
+ sink = gst_bin_get_by_name (GST_BIN (pipeline), "sink");
+
+ g_object_set (G_OBJECT (cf), "caps", caps, NULL);
+
+ gst_element_set_state (pipeline, GST_STATE_PLAYING);
+
+ sample = gst_app_sink_pull_sample (GST_APP_SINK (sink));
+
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+ gst_object_unref (pipeline);
+ gst_object_unref (sink);
+ gst_object_unref (cf);
+
+ buffer = gst_sample_get_buffer (sample);
+ gst_buffer_ref (buffer);
+
+ gst_sample_unref (sample);
+
+ return buffer;
+}
+
+
+GST_START_TEST (test_jpegenc_getcaps)
+{
+ GstElement *jpegenc;
+ GstPad *sinkpad;
+ GstCaps *caps;
+ GstStructure *structure;
+ gint fps_n;
+ gint fps_d;
+ const GValue *value;
+
+ /* we are going to do some get caps to confirm it doesn't return non-subset
+ * caps */
+
+ jpegenc = setup_jpegenc (&any_sinktemplate);
+ sinkpad = gst_element_get_static_pad (jpegenc, "sink");
+ /* this should assert if non-subset */
+ caps = gst_pad_query_caps (sinkpad, NULL);
+ gst_caps_unref (caps);
+ gst_object_unref (sinkpad);
+ cleanup_jpegenc (jpegenc);
+
+ jpegenc = setup_jpegenc (&jpeg_sinktemplate);
+ sinkpad = gst_element_get_static_pad (jpegenc, "sink");
+ /* this should assert if non-subset */
+ caps = gst_pad_query_caps (sinkpad, NULL);
+ gst_caps_unref (caps);
+ gst_object_unref (sinkpad);
+ cleanup_jpegenc (jpegenc);
+
+ /* now use a more restricted one and check the resulting caps */
+ jpegenc = setup_jpegenc (&jpeg_restrictive_sinktemplate);
+ sinkpad = gst_element_get_static_pad (jpegenc, "sink");
+ /* this should assert if non-subset */
+ caps = gst_pad_query_caps (sinkpad, NULL);
+ structure = gst_caps_get_structure (caps, 0);
+
+ /* check the width */
+ value = gst_structure_get_value (structure, "width");
+ fail_unless (gst_value_get_int_range_min (value) == 100);
+ fail_unless (gst_value_get_int_range_max (value) == 200);
+
+ fail_unless (gst_structure_get_fraction (structure, "framerate", &fps_n,
+ &fps_d));
+ fail_unless (fps_n == 25);
+ fail_unless (fps_d == 1);
+
+ gst_caps_unref (caps);
+ gst_object_unref (sinkpad);
+ cleanup_jpegenc (jpegenc);
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_jpegenc_different_caps)
+{
+ GstElement *jpegenc;
+ GstBuffer *buffer;
+ GstCaps *caps;
+ GstCaps *allowed_caps;
+
+ /* now use a more restricted one and check the resulting caps */
+ jpegenc = setup_jpegenc (&any_sinktemplate);
+ gst_element_set_state (jpegenc, GST_STATE_PLAYING);
+
+ /* push first buffer with 800x600 resolution */
+ caps = gst_caps_new_simple ("video/x-raw", "width", G_TYPE_INT,
+ 800, "height", G_TYPE_INT, 600, "framerate",
+ GST_TYPE_FRACTION, 1, 1, "format", G_TYPE_STRING, "I420", NULL);
+ gst_check_setup_events (mysrcpad, jpegenc, caps, GST_FORMAT_TIME);
+ fail_unless ((buffer = create_video_buffer (caps)) != NULL);
+ gst_caps_unref (caps);
+ fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
+
+ /* check the allowed caps to see if a second buffer with a different
+ * caps could be negotiated */
+ allowed_caps = gst_pad_get_allowed_caps (mysrcpad);
+
+ /* the caps we want to negotiate to */
+ caps = gst_caps_new_simple ("video/x-raw", "width", G_TYPE_INT,
+ 640, "height", G_TYPE_INT, 480, "framerate",
+ GST_TYPE_FRACTION, 1, 1, "format", G_TYPE_STRING, "I420", NULL);
+ fail_unless (gst_caps_can_intersect (allowed_caps, caps));
+ fail_unless (gst_pad_set_caps (mysrcpad, caps));
+
+ /* push second buffer with 640x480 resolution */
+ buffer = create_video_buffer (caps);
+ gst_caps_unref (caps);
+ fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
+
+ gst_caps_unref (allowed_caps);
+ gst_element_set_state (jpegenc, GST_STATE_NULL);
+ cleanup_jpegenc (jpegenc);
+}
+
+GST_END_TEST;
+
+static Suite *
+jpegenc_suite (void)
+{
+ Suite *s = suite_create ("jpegenc");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_jpegenc_getcaps);
+ tcase_add_test (tc_chain, test_jpegenc_different_caps);
+
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ int nf;
+
+ Suite *s = jpegenc_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ srunner_run_all (sr, CK_NORMAL);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}
diff --git a/tests/check/elements/level.c b/tests/check/elements/level.c
new file mode 100644
index 0000000..73f0340
--- /dev/null
+++ b/tests/check/elements/level.c
@@ -0,0 +1,604 @@
+/* GStreamer
+ *
+ * unit test for level
+ *
+ * Copyright (C) <2005> Thomas Vander Stichele <thomas at apestaart 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.
+ */
+
+#include <unistd.h>
+#include <math.h>
+
+/* suppress warnings for deprecated API such as GValueArray
+ * with newer GLib versions (>= 2.31.0) */
+#define GLIB_DISABLE_DEPRECATION_WARNINGS
+
+#include <gst/audio/audio.h>
+#include <gst/check/gstcheck.h>
+
+/* 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;
+
+#define LEVEL_CAPS_TEMPLATE_STRING \
+ "audio/x-raw, " \
+ "format = (string) { "GST_AUDIO_NE(S16)", "GST_AUDIO_NE(F32)" }, " \
+ "layout = (string) interleaved, " \
+ "rate = (int) [ 1, MAX ], " \
+ "channels = (int) [ 1, 8 ]"
+
+/* we use rate = 1000 here for easy buffer size calculations */
+#define LEVEL_S16_CAPS_STRING \
+ "audio/x-raw, " \
+ "format = (string) "GST_AUDIO_NE(S16)", " \
+ "layout = (string) interleaved, " \
+ "rate = (int) 1000, " \
+ "channels = (int) 2, " \
+ "channel-mask = (bitmask) 3"
+
+#define LEVEL_F32_CAPS_STRING \
+ "audio/x-raw, " \
+ "format = (string) "GST_AUDIO_NE(F32)", " \
+ "layout = (string) interleaved, " \
+ "rate = (int) 1000, " \
+ "channels = (int) 2, " \
+ "channel-mask = (bitmask) 3"
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (LEVEL_CAPS_TEMPLATE_STRING)
+ );
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (LEVEL_CAPS_TEMPLATE_STRING)
+ );
+
+/* takes over reference for outcaps */
+static GstElement *
+setup_level (const gchar * caps_str)
+{
+ GstElement *level;
+ GstCaps *caps;
+
+ GST_DEBUG ("setup_level");
+ level = gst_check_setup_element ("level");
+ mysrcpad = gst_check_setup_src_pad (level, &srctemplate);
+ mysinkpad = gst_check_setup_sink_pad (level, &sinktemplate);
+ gst_pad_set_active (mysrcpad, TRUE);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ caps = gst_caps_from_string (caps_str);
+ gst_check_setup_events (mysrcpad, level, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+
+ return level;
+}
+
+static void
+cleanup_level (GstElement * level)
+{
+ GST_DEBUG ("cleanup_level");
+
+ gst_pad_set_active (mysrcpad, FALSE);
+ gst_pad_set_active (mysinkpad, FALSE);
+ gst_check_teardown_src_pad (level);
+ gst_check_teardown_sink_pad (level);
+ gst_check_teardown_element (level);
+}
+
+/* create a 0.1 sec buffer stereo buffer */
+static GstBuffer *
+create_s16_buffer (gint16 val_l, gint16 val_r)
+{
+ GstBuffer *buf = gst_buffer_new_and_alloc (2 * 100 * sizeof (gint16));
+ GstMapInfo map;
+ gint j;
+ gint16 *data;
+
+ gst_buffer_map (buf, &map, GST_MAP_WRITE);
+ data = (gint16 *) map.data;
+ for (j = 0; j < 100; ++j) {
+ *(data++) = val_l;
+ *(data++) = val_r;
+ }
+ gst_buffer_unmap (buf, &map);
+ GST_BUFFER_TIMESTAMP (buf) = G_GUINT64_CONSTANT (0);
+ return buf;
+}
+
+static GstBuffer *
+create_f32_buffer (gfloat val_l, gfloat val_r)
+{
+ GstBuffer *buf = gst_buffer_new_and_alloc (2 * 100 * sizeof (gfloat));
+ GstMapInfo map;
+ gint j;
+ gfloat *data;
+
+ gst_buffer_map (buf, &map, GST_MAP_WRITE);
+ data = (gfloat *) map.data;
+ for (j = 0; j < 100; ++j) {
+ *(data++) = val_l;
+ *(data++) = val_r;
+ }
+ gst_buffer_unmap (buf, &map);
+ GST_BUFFER_TIMESTAMP (buf) = G_GUINT64_CONSTANT (0);
+ return buf;
+}
+
+/* tests */
+
+GST_START_TEST (test_ref_counts)
+{
+ GstElement *level;
+ GstBuffer *inbuffer, *outbuffer;
+ GstBus *bus;
+ GstMessage *message;
+
+ level = setup_level (LEVEL_S16_CAPS_STRING);
+ g_object_set (level, "message", TRUE, "interval", GST_SECOND / 10, NULL);
+ fail_unless (gst_element_set_state (level,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+ /* create a bus to get the level message on */
+ bus = gst_bus_new ();
+ ASSERT_OBJECT_REFCOUNT (bus, "bus", 1);
+ gst_element_set_bus (level, bus);
+ ASSERT_OBJECT_REFCOUNT (bus, "bus", 2);
+
+ /* create a fake 0.1 sec buffer with a half-amplitude block signal */
+ inbuffer = create_s16_buffer (16536, 16536);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... but it ends up being collected on the global buffer list */
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+ fail_unless (inbuffer == outbuffer);
+
+ message = gst_bus_poll (bus, GST_MESSAGE_ELEMENT, -1);
+ ASSERT_OBJECT_REFCOUNT (message, "message", 1);
+
+ fail_unless (message != NULL);
+ fail_unless (GST_MESSAGE_SRC (message) == GST_OBJECT (level));
+ fail_unless (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ELEMENT);
+
+ /* clean up */
+ /* flush current messages,and future state change messages */
+ gst_bus_set_flushing (bus, TRUE);
+
+ /* message has a ref to the element */
+ ASSERT_OBJECT_REFCOUNT (level, "level", 2);
+ gst_message_unref (message);
+ ASSERT_OBJECT_REFCOUNT (level, "level", 1);
+
+ gst_element_set_bus (level, NULL);
+ ASSERT_OBJECT_REFCOUNT (bus, "bus", 1);
+ gst_object_unref (bus);
+ gst_buffer_unref (outbuffer);
+ fail_unless (gst_element_set_state (level,
+ GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS, "could not set to null");
+ ASSERT_OBJECT_REFCOUNT (level, "level", 1);
+ cleanup_level (level);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_message_is_valid)
+{
+ GstElement *level;
+ GstBuffer *inbuffer;
+ GstBus *bus;
+ GstMessage *message;
+ const GstStructure *structure;
+ GstClockTime endtime, ts, duration;
+
+ level = setup_level (LEVEL_S16_CAPS_STRING);
+ g_object_set (level, "message", TRUE, "interval", GST_SECOND / 10, NULL);
+ gst_element_set_state (level, GST_STATE_PLAYING);
+ /* create a bus to get the level message on */
+ bus = gst_bus_new ();
+ gst_element_set_bus (level, bus);
+
+ /* create a fake 0.1 sec buffer with a half-amplitude block signal and push */
+ inbuffer = create_s16_buffer (16536, 16536);
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+
+ message = gst_bus_poll (bus, GST_MESSAGE_ELEMENT, -1);
+ fail_unless (message != NULL);
+ structure = gst_message_get_structure (message);
+ fail_if (structure == NULL);
+ fail_unless_equals_string (gst_structure_get_name (structure), "level");
+ fail_unless (gst_structure_get_clock_time (structure, "endtime", &endtime));
+ fail_unless (gst_structure_get_clock_time (structure, "timestamp", &ts));
+ fail_unless (gst_structure_get_clock_time (structure, "duration", &duration));
+
+ /* clean up */
+ /* flush current messages,and future state change messages */
+ gst_bus_set_flushing (bus, TRUE);
+ gst_message_unref (message);
+ gst_element_set_bus (level, NULL);
+ gst_object_unref (bus);
+ gst_element_set_state (level, GST_STATE_NULL);
+ cleanup_level (level);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_int16)
+{
+ GstElement *level;
+ GstBuffer *inbuffer, *outbuffer;
+ GstBus *bus;
+ GstMessage *message;
+ const GstStructure *structure;
+ gint i, j;
+ const GValue *list, *value;
+ gdouble dB;
+ const gchar *fields[3] = { "rms", "peak", "decay" };
+
+ level = setup_level (LEVEL_S16_CAPS_STRING);
+ g_object_set (level, "message", TRUE, "interval", GST_SECOND / 10, NULL);
+ gst_element_set_state (level, GST_STATE_PLAYING);
+ /* create a bus to get the level message on */
+ bus = gst_bus_new ();
+ gst_element_set_bus (level, bus);
+
+ /* create a fake 0.1 sec buffer with a half-amplitude block signal */
+ inbuffer = create_s16_buffer (16536, 16536);
+
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+ fail_unless (inbuffer == outbuffer);
+
+ message = gst_bus_poll (bus, GST_MESSAGE_ELEMENT, -1);
+ structure = gst_message_get_structure (message);
+
+ /* block wave of half amplitude has -5.94 dB for rms, peak and decay */
+ for (i = 0; i < 2; ++i) {
+ for (j = 0; j < 3; ++j) {
+ GValueArray *arr;
+
+ list = gst_structure_get_value (structure, fields[j]);
+ arr = g_value_get_boxed (list);
+ value = g_value_array_get_nth (arr, i);
+ dB = g_value_get_double (value);
+ GST_DEBUG ("%s is %lf", fields[j], dB);
+ fail_if (dB < -6.1);
+ fail_if (dB > -5.9);
+ }
+ }
+
+ /* clean up */
+ /* flush current messages,and future state change messages */
+ gst_bus_set_flushing (bus, TRUE);
+ gst_message_unref (message);
+ gst_element_set_bus (level, NULL);
+ gst_object_unref (bus);
+ gst_buffer_unref (outbuffer);
+ gst_element_set_state (level, GST_STATE_NULL);
+ cleanup_level (level);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_int16_panned)
+{
+ GstElement *level;
+ GstBuffer *inbuffer, *outbuffer;
+ GstBus *bus;
+ GstMessage *message;
+ const GstStructure *structure;
+ gint j;
+ const GValue *list, *value;
+ gdouble dB;
+ const gchar *fields[3] = { "rms", "peak", "decay" };
+
+ level = setup_level (LEVEL_S16_CAPS_STRING);
+ g_object_set (level, "message", TRUE, "interval", GST_SECOND / 10, NULL);
+ gst_element_set_state (level, GST_STATE_PLAYING);
+ /* create a bus to get the level message on */
+ bus = gst_bus_new ();
+ gst_element_set_bus (level, bus);
+
+ /* create a fake 0.1 sec buffer with a half-amplitude block signal */
+ inbuffer = create_s16_buffer (0, 16536);
+
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+ fail_unless (inbuffer == outbuffer);
+
+ message = gst_bus_poll (bus, GST_MESSAGE_ELEMENT, -1);
+ structure = gst_message_get_structure (message);
+
+ /* silence has 0 dB for rms, peak and decay */
+ for (j = 0; j < 3; ++j) {
+ GValueArray *arr;
+
+ list = gst_structure_get_value (structure, fields[j]);
+ arr = g_value_get_boxed (list);
+ value = g_value_array_get_nth (arr, 0);
+ dB = g_value_get_double (value);
+ GST_DEBUG ("%s[0] is %lf", fields[j], dB);
+#ifdef HAVE_ISINF
+ fail_unless (isinf (dB));
+#elif defined (HAVE_FPCLASS)
+ fail_unless (fpclass (dB) == FP_NINF);
+#endif
+ }
+ /* block wave of half amplitude has -5.94 dB for rms, peak and decay */
+ for (j = 0; j < 3; ++j) {
+ GValueArray *arr;
+
+ list = gst_structure_get_value (structure, fields[j]);
+ arr = g_value_get_boxed (list);
+ value = g_value_array_get_nth (arr, 1);
+ dB = g_value_get_double (value);
+ GST_DEBUG ("%s[1] is %lf", fields[j], dB);
+ fail_if (dB < -6.1);
+ fail_if (dB > -5.9);
+ }
+
+ /* clean up */
+ /* flush current messages,and future state change messages */
+ gst_bus_set_flushing (bus, TRUE);
+ gst_message_unref (message);
+ gst_element_set_bus (level, NULL);
+ gst_object_unref (bus);
+ gst_buffer_unref (outbuffer);
+ gst_element_set_state (level, GST_STATE_NULL);
+ cleanup_level (level);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_float)
+{
+ GstElement *level;
+ GstBuffer *inbuffer, *outbuffer;
+ GstBus *bus;
+ GstMessage *message;
+ const GstStructure *structure;
+ gint i, j;
+ const GValue *list, *value;
+ gdouble dB;
+ const gchar *fields[3] = { "rms", "peak", "decay" };
+
+ level = setup_level (LEVEL_F32_CAPS_STRING);
+ g_object_set (level, "message", TRUE, "interval", GST_SECOND / 10, NULL);
+ gst_element_set_state (level, GST_STATE_PLAYING);
+ /* create a bus to get the level message on */
+ bus = gst_bus_new ();
+ gst_element_set_bus (level, bus);
+
+ /* create a fake 0.1 sec buffer with a half-amplitude block signal */
+ inbuffer = create_f32_buffer (0.5, 0.5);
+
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+ fail_unless (inbuffer == outbuffer);
+
+ message = gst_bus_poll (bus, GST_MESSAGE_ELEMENT, -1);
+ structure = gst_message_get_structure (message);
+
+ /* block wave of half amplitude has -5.94 dB for rms, peak and decay */
+ for (i = 0; i < 2; ++i) {
+ for (j = 0; j < 3; ++j) {
+ GValueArray *arr;
+
+ list = gst_structure_get_value (structure, fields[j]);
+ arr = g_value_get_boxed (list);
+ value = g_value_array_get_nth (arr, i);
+ dB = g_value_get_double (value);
+ GST_DEBUG ("%s is %lf", fields[j], dB);
+ fail_if (dB < -6.1);
+ fail_if (dB > -5.9);
+ }
+ }
+
+ /* clean up */
+ /* flush current messages,and future state change messages */
+ gst_bus_set_flushing (bus, TRUE);
+ gst_message_unref (message);
+ gst_element_set_bus (level, NULL);
+ gst_object_unref (bus);
+ gst_buffer_unref (outbuffer);
+ gst_element_set_state (level, GST_STATE_NULL);
+ cleanup_level (level);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_message_on_eos)
+{
+ GstElement *level;
+ GstBuffer *inbuffer, *outbuffer;
+ GstEvent *event;
+ GstBus *bus;
+ GstMessage *message;
+ const GstStructure *structure;
+ gint i, j;
+ const GValue *list, *value;
+ gdouble dB;
+
+ level = setup_level (LEVEL_S16_CAPS_STRING);
+ g_object_set (level, "message", TRUE, "interval", GST_SECOND / 5, NULL);
+ gst_element_set_state (level, GST_STATE_PLAYING);
+ /* create a bus to get the level message on */
+ bus = gst_bus_new ();
+ gst_element_set_bus (level, bus);
+
+ /* create a fake 0.1 sec buffer with a half-amplitude block signal */
+ inbuffer = create_s16_buffer (16536, 16536);
+
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+ fail_unless (inbuffer == outbuffer);
+
+ message = gst_bus_poll (bus, GST_MESSAGE_ELEMENT, 0);
+
+ event = gst_event_new_eos ();
+ fail_unless (gst_pad_push_event (mysrcpad, event) == TRUE);
+
+ message = gst_bus_poll (bus, GST_MESSAGE_ELEMENT, 0);
+ fail_unless (message != NULL);
+ structure = gst_message_get_structure (message);
+ fail_unless_equals_string (gst_structure_get_name (structure), "level");
+
+ /* block wave of half amplitude has -5.94 dB for rms, peak and decay */
+ for (i = 0; i < 2; ++i) {
+ const gchar *fields[3] = { "rms", "peak", "decay" };
+ for (j = 0; j < 3; ++j) {
+ GValueArray *arr;
+
+ list = gst_structure_get_value (structure, fields[j]);
+ arr = g_value_get_boxed (list);
+ value = g_value_array_get_nth (arr, i);
+ dB = g_value_get_double (value);
+ GST_DEBUG ("%s is %lf", fields[j], dB);
+ fail_if (dB < -6.1);
+ fail_if (dB > -5.9);
+ }
+ }
+
+ /* clean up */
+ /* flush current messages,and future state change messages */
+ gst_bus_set_flushing (bus, TRUE);
+ gst_message_unref (message);
+ gst_element_set_bus (level, NULL);
+ gst_object_unref (bus);
+ gst_buffer_unref (outbuffer);
+ gst_element_set_state (level, GST_STATE_NULL);
+ cleanup_level (level);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_message_count)
+{
+ GstElement *level;
+ GstBuffer *inbuffer, *outbuffer;
+ GstBus *bus;
+ GstMessage *message;
+
+ level = setup_level (LEVEL_S16_CAPS_STRING);
+ g_object_set (level, "message", TRUE, "interval", GST_SECOND / 20, NULL);
+ gst_element_set_state (level, GST_STATE_PLAYING);
+ /* create a bus to get the level message on */
+ bus = gst_bus_new ();
+ gst_element_set_bus (level, bus);
+
+ /* create a fake 0.1 sec buffer with a half-amplitude block signal */
+ inbuffer = create_s16_buffer (16536, 16536);
+
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+ fail_unless (inbuffer == outbuffer);
+
+ /* we should get two messages per buffer */
+ message = gst_bus_poll (bus, GST_MESSAGE_ELEMENT, -1);
+ fail_unless (message != NULL);
+ gst_message_unref (message);
+ message = gst_bus_poll (bus, GST_MESSAGE_ELEMENT, -1);
+ fail_unless (message != NULL);
+ gst_message_unref (message);
+
+ gst_element_set_bus (level, NULL);
+ gst_object_unref (bus);
+ gst_buffer_unref (outbuffer);
+ gst_element_set_state (level, GST_STATE_NULL);
+ cleanup_level (level);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_message_timestamps)
+{
+ GstElement *level;
+ GstBuffer *inbuffer, *outbuffer;
+ GstBus *bus;
+ GstMessage *message;
+ const GstStructure *structure;
+ GstClockTime ts1, dur1, ts2;
+
+ level = setup_level (LEVEL_S16_CAPS_STRING);
+ g_object_set (level, "message", TRUE, "interval", GST_SECOND / 20, NULL);
+ gst_element_set_state (level, GST_STATE_PLAYING);
+ /* create a bus to get the level message on */
+ bus = gst_bus_new ();
+ gst_element_set_bus (level, bus);
+
+ /* create a fake 0.1 sec buffer with a half-amplitude block signal */
+ inbuffer = create_s16_buffer (16536, 16536);
+
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+ fail_unless (inbuffer == outbuffer);
+
+ /* check that timestamp + duration is contigous to the next timestamp */
+ message = gst_bus_poll (bus, GST_MESSAGE_ELEMENT, -1);
+ structure = gst_message_get_structure (message);
+ gst_structure_get_clock_time (structure, "timestamp", &ts1);
+ gst_structure_get_clock_time (structure, "duration", &dur1);
+ gst_message_unref (message);
+
+ message = gst_bus_poll (bus, GST_MESSAGE_ELEMENT, -1);
+ structure = gst_message_get_structure (message);
+ gst_structure_get_clock_time (structure, "timestamp", &ts2);
+ gst_message_unref (message);
+
+ fail_unless_equals_int64 (ts1 + dur1, ts2);
+
+ gst_element_set_bus (level, NULL);
+ gst_object_unref (bus);
+ gst_buffer_unref (outbuffer);
+ gst_element_set_state (level, GST_STATE_NULL);
+ cleanup_level (level);
+}
+
+GST_END_TEST;
+
+static Suite *
+level_suite (void)
+{
+ Suite *s = suite_create ("level");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_ref_counts);
+ tcase_add_test (tc_chain, test_message_is_valid);
+ tcase_add_test (tc_chain, test_int16);
+ tcase_add_test (tc_chain, test_int16_panned);
+ tcase_add_test (tc_chain, test_float);
+ tcase_add_test (tc_chain, test_message_on_eos);
+ tcase_add_test (tc_chain, test_message_count);
+ tcase_add_test (tc_chain, test_message_timestamps);
+
+ return s;
+}
+
+GST_CHECK_MAIN (level);
diff --git a/tests/check/elements/matroskamux.c b/tests/check/elements/matroskamux.c
new file mode 100644
index 0000000..1c18e75
--- /dev/null
+++ b/tests/check/elements/matroskamux.c
@@ -0,0 +1,495 @@
+/* GStreamer
+ *
+ * unit test for matroskamux
+ *
+ * Copyright (C) <2005> Michal Benes <michal.benes@xeris.cz>
+ *
+ * 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.
+ */
+
+#include <unistd.h>
+
+#include <gst/check/gstcheck.h>
+#include <gst/base/gstadapter.h>
+
+/* 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 */
+GstPad *mysrcpad, *mysinkpad;
+
+#define AC3_CAPS_STRING "audio/x-ac3, " \
+ "channels = (int) 1, " \
+ "rate = (int) 8000"
+#define VORBIS_CAPS_STRING "audio/x-vorbis, " \
+ "channels = (int) 1, " \
+ "rate = (int) 8000, " \
+ "streamheader=(buffer)<10, 2020, 303030>"
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("video/x-matroska; audio/x-matroska"));
+static GstStaticPadTemplate srcvorbistemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (VORBIS_CAPS_STRING));
+
+static GstStaticPadTemplate srcac3template = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (AC3_CAPS_STRING));
+
+
+static GstPad *
+setup_src_pad (GstElement * element, GstStaticPadTemplate * template)
+{
+ GstPad *srcpad, *sinkpad;
+
+ GST_DEBUG_OBJECT (element, "setting up sending pad");
+ /* sending pad */
+ srcpad = gst_pad_new_from_static_template (template, "src");
+ fail_if (srcpad == NULL, "Could not create a srcpad");
+ ASSERT_OBJECT_REFCOUNT (srcpad, "srcpad", 1);
+ gst_pad_set_active (srcpad, TRUE);
+
+ if (!(sinkpad = gst_element_get_static_pad (element, "audio_%u")))
+ sinkpad = gst_element_get_request_pad (element, "audio_%u");
+ fail_if (sinkpad == NULL, "Could not get sink pad from %s",
+ GST_ELEMENT_NAME (element));
+ /* references are owned by: 1) us, 2) matroskamux, 3) collect pads */
+ ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 3);
+ fail_unless (gst_pad_link (srcpad, sinkpad) == GST_PAD_LINK_OK,
+ "Could not link source and %s sink pads", GST_ELEMENT_NAME (element));
+ gst_object_unref (sinkpad); /* because we got it higher up */
+
+ /* references are owned by: 1) matroskamux, 2) collect pads */
+ ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 2);
+
+ return srcpad;
+}
+
+static void
+teardown_src_pad (GstElement * element)
+{
+ GstPad *srcpad, *sinkpad;
+
+ /* clean up floating src pad */
+ if (!(sinkpad = gst_element_get_static_pad (element, "audio_0")))
+ sinkpad = gst_element_get_request_pad (element, "audio_0");
+ /* references are owned by: 1) us, 2) matroskamux, 3) collect pads */
+ ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 3);
+ srcpad = gst_pad_get_peer (sinkpad);
+
+ gst_pad_unlink (srcpad, sinkpad);
+
+ /* references are owned by: 1) us, 2) matroskamux, 3) collect pads */
+ ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 3);
+ gst_object_unref (sinkpad);
+ /* one more ref is held by element itself */
+
+ /* pad refs held by both creator and this function (through _get_peer) */
+ ASSERT_OBJECT_REFCOUNT (srcpad, "srcpad", 2);
+ gst_object_unref (srcpad);
+ gst_object_unref (srcpad);
+}
+
+static GstPad *
+setup_sink_pad (GstElement * element, GstStaticPadTemplate * template)
+{
+ GstPad *srcpad, *sinkpad;
+
+ GST_DEBUG_OBJECT (element, "setting up receiving pad");
+ /* receiving pad */
+ sinkpad = gst_pad_new_from_static_template (template, "sink");
+
+ fail_if (sinkpad == NULL, "Could not create a sinkpad");
+ gst_pad_set_active (sinkpad, TRUE);
+
+ srcpad = gst_element_get_static_pad (element, "src");
+ fail_if (srcpad == NULL, "Could not get source pad from %s",
+ GST_ELEMENT_NAME (element));
+ gst_pad_set_chain_function (sinkpad, gst_check_chain_func);
+
+ fail_unless (gst_pad_link (srcpad, sinkpad) == GST_PAD_LINK_OK,
+ "Could not link %s source and sink pads", GST_ELEMENT_NAME (element));
+ gst_object_unref (srcpad); /* because we got it higher up */
+ ASSERT_OBJECT_REFCOUNT (srcpad, "srcpad", 2);
+
+ return sinkpad;
+}
+
+static void
+teardown_sink_pad (GstElement * element)
+{
+ GstPad *srcpad, *sinkpad;
+
+ /* clean up floating sink pad */
+ srcpad = gst_element_get_static_pad (element, "src");
+ sinkpad = gst_pad_get_peer (srcpad);
+ gst_pad_unlink (srcpad, sinkpad);
+
+ /* pad refs held by both creator and this function (through _get_pad) */
+ ASSERT_OBJECT_REFCOUNT (srcpad, "srcpad", 3);
+ gst_object_unref (srcpad);
+ /* one more ref is held by element itself */
+
+ /* pad refs held by both creator and this function (through _get_peer) */
+ ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 2);
+ gst_object_unref (sinkpad);
+ gst_object_unref (sinkpad);
+}
+
+
+static GstElement *
+setup_matroskamux (GstStaticPadTemplate * srctemplate)
+{
+ GstElement *matroskamux;
+
+ GST_DEBUG ("setup_matroskamux");
+ matroskamux = gst_check_setup_element ("matroskamux");
+ g_object_set (matroskamux, "version", 1, NULL);
+ mysrcpad = setup_src_pad (matroskamux, srctemplate);
+ mysinkpad = setup_sink_pad (matroskamux, &sinktemplate);
+
+ fail_unless (gst_element_set_state (matroskamux,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ return matroskamux;
+}
+
+static void
+cleanup_matroskamux (GstElement * matroskamux)
+{
+ GST_DEBUG ("cleanup_matroskamux");
+ gst_element_set_state (matroskamux, GST_STATE_NULL);
+
+ teardown_src_pad (matroskamux);
+ teardown_sink_pad (matroskamux);
+ gst_check_teardown_element (matroskamux);
+}
+
+static void
+check_buffer_data (GstBuffer * buffer, void *data, size_t data_size)
+{
+ fail_unless (gst_buffer_get_size (buffer) == data_size);
+ fail_unless (gst_buffer_memcmp (buffer, 0, data, data_size) == 0);
+}
+
+GST_START_TEST (test_ebml_header)
+{
+ GstElement *matroskamux;
+ GstBuffer *inbuffer, *outbuffer;
+ GstAdapter *adapter;
+ int num_buffers;
+ int i;
+ gint available;
+ GstCaps *caps;
+ guint8 data[] =
+ { 0x1a, 0x45, 0xdf, 0xa3, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14,
+ 0x42, 0x82, 0x89, 0x6d, 0x61, 0x74, 0x72, 0x6f, 0x73, 0x6b, 0x61, 0x00,
+ 0x42, 0x87, 0x81, 0x01,
+ 0x42, 0x85, 0x81, 0x01
+ };
+
+ matroskamux = setup_matroskamux (&srcac3template);
+
+ caps = gst_caps_from_string (srcac3template.static_caps.string);
+ gst_check_setup_events (mysrcpad, matroskamux, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+
+ inbuffer = gst_buffer_new_allocate (NULL, 1, 0);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ num_buffers = g_list_length (buffers);
+ fail_unless (num_buffers >= 1,
+ "expected at least 5 buffers, but got only %d", num_buffers);
+
+ adapter = gst_adapter_new ();
+ for (i = 0; i < num_buffers; ++i) {
+ outbuffer = GST_BUFFER (buffers->data);
+ fail_if (outbuffer == NULL);
+ buffers = g_list_remove (buffers, outbuffer);
+
+ ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
+
+ gst_adapter_push (adapter, outbuffer);
+ }
+
+ available = gst_adapter_available (adapter);
+ fail_unless (available >= sizeof (data));
+ outbuffer = gst_adapter_take_buffer (adapter, sizeof (data));
+ g_object_unref (adapter);
+
+ check_buffer_data (outbuffer, data, sizeof (data));
+ gst_buffer_unref (outbuffer);
+
+ cleanup_matroskamux (matroskamux);
+ g_list_free (buffers);
+ buffers = NULL;
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_vorbis_header)
+{
+ GstElement *matroskamux;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ int num_buffers;
+ int i;
+ gboolean vorbis_header_found = FALSE;
+ guint8 data[12] =
+ { 0x63, 0xa2, 0x89, 0x02, 0x01, 0x02, 0x10, 0x20, 0x20, 0x30, 0x30,
+ 0x30
+ };
+
+ matroskamux = setup_matroskamux (&srcvorbistemplate);
+
+ caps = gst_caps_from_string (VORBIS_CAPS_STRING);
+ gst_check_setup_events (mysrcpad, matroskamux, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+
+ inbuffer = gst_buffer_new_allocate (NULL, 1, 0);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ num_buffers = g_list_length (buffers);
+
+ for (i = 0; i < num_buffers; ++i) {
+ gint j;
+ gsize buffer_size;
+
+ outbuffer = GST_BUFFER (buffers->data);
+ fail_if (outbuffer == NULL);
+ buffer_size = gst_buffer_get_size (outbuffer);
+ buffers = g_list_remove (buffers, outbuffer);
+
+ if (!vorbis_header_found && buffer_size >= sizeof (data)) {
+ for (j = 0; j <= buffer_size - sizeof (data); j++) {
+ if (gst_buffer_memcmp (outbuffer, j, data, sizeof (data)) == 0) {
+ vorbis_header_found = TRUE;
+ break;
+ }
+ }
+ }
+
+ ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
+ gst_buffer_unref (outbuffer);
+ outbuffer = NULL;
+ }
+
+ fail_unless (vorbis_header_found);
+
+ cleanup_matroskamux (matroskamux);
+ g_list_free (buffers);
+ buffers = NULL;
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_block_group)
+{
+ GstElement *matroskamux;
+ GstBuffer *inbuffer, *outbuffer;
+ guint8 *indata;
+ GstCaps *caps;
+ int num_buffers;
+ int i;
+ guint8 data0[] = { 0xa0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
+ 0xa1, 0x85,
+ 0x81, 0x00, 0x01, 0x00
+ };
+ guint8 data1[] = { 0x42 };
+
+ matroskamux = setup_matroskamux (&srcac3template);
+
+ caps = gst_caps_from_string (AC3_CAPS_STRING);
+ gst_check_setup_events (mysrcpad, matroskamux, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+
+ /* Generate the header */
+ inbuffer = gst_buffer_new_allocate (NULL, 1, 0);
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ fail_unless_equals_int (gst_pad_push (mysrcpad, inbuffer), GST_FLOW_OK);
+ num_buffers = g_list_length (buffers);
+
+ for (i = 0; i < num_buffers; ++i) {
+ outbuffer = GST_BUFFER (buffers->data);
+ fail_if (outbuffer == NULL);
+ buffers = g_list_remove (buffers, outbuffer);
+
+ ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
+ gst_buffer_unref (outbuffer);
+ outbuffer = NULL;
+ }
+
+ g_list_free (buffers);
+ buffers = NULL;
+
+ /* Now push a buffer */
+ indata = g_malloc (1);
+ inbuffer = gst_buffer_new_wrapped (indata, 1);
+ indata[0] = 0x42;
+ GST_BUFFER_TIMESTAMP (inbuffer) = 1000000;
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ num_buffers = g_list_length (buffers);
+ fail_unless (num_buffers >= 2);
+
+ for (i = 0; i < num_buffers; ++i) {
+ outbuffer = GST_BUFFER (buffers->data);
+ fail_if (outbuffer == NULL);
+ buffers = g_list_remove (buffers, outbuffer);
+
+ switch (i) {
+ case 0:
+ check_buffer_data (outbuffer, data0, sizeof (data0));
+ break;
+ case 1:
+ check_buffer_data (outbuffer, data1, sizeof (data1));
+ break;
+ default:
+ break;
+ }
+
+ ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
+ gst_buffer_unref (outbuffer);
+ outbuffer = NULL;
+ }
+
+ g_list_free (buffers);
+ buffers = NULL;
+
+ cleanup_matroskamux (matroskamux);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_reset)
+{
+ GstElement *matroskamux;
+ GstBuffer *inbuffer;
+ GstBuffer *outbuffer;
+ int num_buffers;
+ int i;
+ GstCaps *caps;
+
+ matroskamux = setup_matroskamux (&srcac3template);
+
+ caps = gst_caps_from_string (srcac3template.static_caps.string);
+ gst_check_setup_events (mysrcpad, matroskamux, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+
+ inbuffer = gst_buffer_new_allocate (NULL, 1, 0);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ num_buffers = g_list_length (buffers);
+ fail_unless (num_buffers >= 1,
+ "expected at least 1 buffer, but got only %d", num_buffers);
+
+ fail_unless (gst_element_set_state (matroskamux,
+ GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS, "could not set to null");
+
+ fail_unless (gst_element_set_state (matroskamux,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ inbuffer = gst_buffer_new_allocate (NULL, 1, 0);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ num_buffers = g_list_length (buffers);
+ fail_unless (num_buffers >= 2,
+ "expected at least 2 buffers, but got only %d", num_buffers);
+
+ for (i = 0; i < num_buffers; ++i) {
+ outbuffer = GST_BUFFER (buffers->data);
+ fail_if (outbuffer == NULL);
+ buffers = g_list_remove (buffers, outbuffer);
+
+ ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
+ gst_buffer_unref (outbuffer);
+ }
+
+ cleanup_matroskamux (matroskamux);
+ g_list_free (buffers);
+ buffers = NULL;
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_link_webmmux_webm_sink)
+{
+ static GstStaticPadTemplate webm_sinktemplate =
+ GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("video/webm; audio/webm"));
+ GstElement *mux;
+
+ mux = gst_check_setup_element ("webmmux");
+ mysinkpad = setup_sink_pad (mux, &webm_sinktemplate);
+ fail_unless (mysinkpad != NULL);
+
+ fail_unless (gst_element_set_state (mux,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ gst_element_set_state (mux, GST_STATE_NULL);
+
+ teardown_sink_pad (mux);
+ gst_check_teardown_element (mux);
+}
+
+GST_END_TEST;
+
+static Suite *
+matroskamux_suite (void)
+{
+ Suite *s = suite_create ("matroskamux");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_ebml_header);
+ tcase_add_test (tc_chain, test_vorbis_header);
+ tcase_add_test (tc_chain, test_block_group);
+ tcase_add_test (tc_chain, test_reset);
+ tcase_add_test (tc_chain, test_link_webmmux_webm_sink);
+
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ int nf;
+
+ Suite *s = matroskamux_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ srunner_run_all (sr, CK_NORMAL);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}
diff --git a/tests/check/elements/matroskaparse.c b/tests/check/elements/matroskaparse.c
new file mode 100755
index 0000000..13ef8d8
--- /dev/null
+++ b/tests/check/elements/matroskaparse.c
@@ -0,0 +1,130 @@
+/* GStreamer unit tests for matroskaparse
+ * Copyright (C) 2011 Tim-Philipp Müller <tim centricular net>
+ *
+ * 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.
+ */
+
+#include <gst/check/gstcheck.h>
+
+#include <gst/gst.h>
+
+static void
+run_check_for_file (const gchar * file_name, gboolean push_mode)
+{
+ GstStateChangeReturn state_ret;
+ GstMessage *msg;
+ GstElement *src, *sep, *sink, *matroskaparse, *pipeline;
+ GstBus *bus;
+ gchar *path;
+
+ pipeline = gst_pipeline_new ("pipeline");
+ fail_unless (pipeline != NULL, "Failed to create pipeline!");
+
+ bus = gst_element_get_bus (pipeline);
+
+ src = gst_element_factory_make ("filesrc", "filesrc");
+ fail_unless (src != NULL, "Failed to create 'filesrc' element!");
+
+ if (push_mode) {
+ sep = gst_element_factory_make ("queue", "queue");
+ fail_unless (sep != NULL, "Failed to create 'queue' element");
+ } else {
+ sep = gst_element_factory_make ("identity", "identity");
+ fail_unless (sep != NULL, "Failed to create 'identity' element");
+ }
+
+ matroskaparse = gst_element_factory_make ("matroskaparse", "matroskaparse");
+ fail_unless (matroskaparse != NULL, "Failed to create matroskaparse element");
+
+ sink = gst_element_factory_make ("fakesink", "fakesink");
+ fail_unless (sink != NULL, "Failed to create 'fakesink' element!");
+
+ gst_bin_add_many (GST_BIN (pipeline), src, sep, matroskaparse, sink, NULL);
+
+ fail_unless (gst_element_link (src, sep));
+ fail_unless (gst_element_link (sep, matroskaparse));
+ fail_unless (gst_element_link (matroskaparse, sink));
+
+ path = g_build_filename (GST_TEST_FILES_PATH, file_name, NULL);
+ GST_LOG ("reading file '%s'", path);
+ g_object_set (src, "location", path, NULL);
+
+ state_ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
+ fail_unless (state_ret != GST_STATE_CHANGE_FAILURE);
+
+ if (state_ret == GST_STATE_CHANGE_ASYNC) {
+ GST_LOG ("waiting for pipeline to reach PAUSED state");
+ state_ret = gst_element_get_state (pipeline, NULL, NULL, -1);
+ fail_unless_equals_int (state_ret, GST_STATE_CHANGE_SUCCESS);
+ }
+
+ GST_LOG ("PAUSED, let's play a little..");
+ state_ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
+ fail_unless (state_ret != GST_STATE_CHANGE_FAILURE);
+
+ msg = gst_bus_poll (bus, GST_MESSAGE_EOS | GST_MESSAGE_ERROR, -1);
+ if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
+ GError *err;
+ gchar *dbg;
+
+ gst_message_parse_error (msg, &err, &dbg);
+ gst_object_default_error (GST_MESSAGE_SRC (msg), err, dbg);
+ g_error ("%s (%s)", err->message, dbg);
+ g_error_free (err);
+ g_free (dbg);
+ }
+ gst_message_unref (msg);
+ gst_object_unref (bus);
+
+ fail_unless_equals_int (gst_element_set_state (pipeline, GST_STATE_NULL),
+ GST_STATE_CHANGE_SUCCESS);
+ gst_object_unref (pipeline);
+
+ g_free (path);
+}
+
+#if 0
+GST_START_TEST (test_parse_file_pull)
+{
+ run_check_for_file ("pinknoise-vorbis.mkv", TRUE);
+}
+
+GST_END_TEST;
+#endif
+
+GST_START_TEST (test_parse_file_push)
+{
+ run_check_for_file ("pinknoise-vorbis.mkv", FALSE);
+}
+
+GST_END_TEST;
+
+static Suite *
+matroskaparse_suite (void)
+{
+ Suite *s = suite_create ("matroskaparse");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+#if 0
+ tcase_add_test (tc_chain, test_parse_file_pull);
+#endif
+ tcase_add_test (tc_chain, test_parse_file_push);
+
+ return s;
+}
+
+GST_CHECK_MAIN (matroskaparse)
diff --git a/tests/check/elements/mpegaudioparse.c b/tests/check/elements/mpegaudioparse.c
new file mode 100644
index 0000000..dfe4735
--- /dev/null
+++ b/tests/check/elements/mpegaudioparse.c
@@ -0,0 +1,170 @@
+/*
+ * GStreamer
+ *
+ * unit test for aacparse
+ *
+ * Copyright (C) 2008 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Stefan Kost <stefan.kost@nokia.com>
+ *
+ * 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.
+ */
+
+#include <gst/check/gstcheck.h>
+#include "parser.h"
+
+#define SRC_CAPS_TMPL "audio/mpeg, parsed=(boolean)false, mpegversion=(int)1"
+#define SINK_CAPS_TMPL "audio/mpeg, parsed=(boolean)true, mpegversion=(int)1"
+
+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 */
+static guint8 mp3_frame[384] = {
+ 0xff, 0xfb, 0x94, 0xc4, 0xff, 0x83, 0xc0, 0x00,
+ 0x01, 0xa4, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00,
+ 0x34, 0x80, 0x00, 0x00, 0x04, 0x00,
+};
+
+static guint8 garbage_frame[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+
+GST_START_TEST (test_parse_normal)
+{
+ gst_parser_test_normal (mp3_frame, sizeof (mp3_frame));
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_parse_drain_single)
+{
+ gst_parser_test_drain_single (mp3_frame, sizeof (mp3_frame));
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_parse_drain_garbage)
+{
+ gst_parser_test_drain_garbage (mp3_frame, sizeof (mp3_frame),
+ garbage_frame, sizeof (garbage_frame));
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_parse_split)
+{
+ gst_parser_test_split (mp3_frame, sizeof (mp3_frame));
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_parse_skip_garbage)
+{
+ gst_parser_test_skip_garbage (mp3_frame, sizeof (mp3_frame),
+ 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)
+{
+ GstStructure *s;
+ GstCaps *caps;
+
+ caps = gst_parser_test_get_output_caps (mp3_frame, sizeof (mp3_frame), NULL);
+
+ fail_unless (caps != NULL);
+
+ GST_LOG ("mpegaudio output caps: %" GST_PTR_FORMAT, caps);
+ s = gst_caps_get_structure (caps, 0);
+ fail_unless (gst_structure_has_name (s, "audio/mpeg"));
+ fail_unless_structure_field_int_equals (s, "mpegversion", 1);
+ fail_unless_structure_field_int_equals (s, "layer", 3);
+ fail_unless_structure_field_int_equals (s, "channels", 1);
+ fail_unless_structure_field_int_equals (s, "rate", 48000);
+
+ gst_caps_unref (caps);
+}
+
+GST_END_TEST;
+
+
+static Suite *
+mpegaudioparse_suite (void)
+{
+ Suite *s = suite_create ("mpegaudioparse");
+ 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;
+}
+
+
+/*
+ * TODO:
+ * - Both push- and pull-modes need to be tested
+ * * Pull-mode & EOS
+ */
+
+int
+main (int argc, char **argv)
+{
+ int nf;
+
+ Suite *s = mpegaudioparse_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ /* init test context */
+ ctx_factory = "mpegaudioparse";
+ ctx_sink_template = &sinktemplate;
+ ctx_src_template = &srctemplate;
+
+ srunner_run_all (sr, CK_NORMAL);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}
diff --git a/tests/check/elements/mulawdec.c b/tests/check/elements/mulawdec.c
new file mode 100644
index 0000000..94c6339
--- /dev/null
+++ b/tests/check/elements/mulawdec.c
@@ -0,0 +1,125 @@
+/* GStreamer MulawDec unit tests
+ *
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/check/gstcheck.h>
+#include <string.h>
+
+static GstPad *mysrcpad, *mysinkpad;
+static GstElement *mulawdec = NULL;
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw,"
+ "format = (string) S16LE, "
+ "rate = (int) 8000, "
+ "channels = (int) 1, " "layout = (string)interleaved")
+ );
+
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-mulaw," "rate = (int) 8000," "channels = (int) 1")
+ );
+
+static void
+mulawdec_setup (void)
+{
+ GstCaps *src_caps;
+
+ src_caps =
+ gst_caps_from_string ("audio/x-mulaw," "rate = (int) 8000,"
+ "channels = (int) 1");
+
+ GST_DEBUG ("%s", __FUNCTION__);
+
+ mulawdec = gst_check_setup_element ("mulawdec");
+
+ mysrcpad = gst_check_setup_src_pad (mulawdec, &srctemplate);
+ mysinkpad = gst_check_setup_sink_pad (mulawdec, &sinktemplate);
+
+ gst_pad_set_active (mysrcpad, TRUE);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ gst_check_setup_events (mysrcpad, mulawdec, src_caps, GST_FORMAT_TIME);
+
+ gst_caps_unref (src_caps);
+}
+
+static void
+buffer_unref (void *buffer, void *user_data)
+{
+ gst_buffer_unref (GST_BUFFER (buffer));
+}
+
+static void
+mulawdec_teardown (void)
+{
+ /* free decoded buffers */
+ g_list_foreach (buffers, buffer_unref, NULL);
+ g_list_free (buffers);
+ buffers = NULL;
+
+ gst_pad_set_active (mysrcpad, FALSE);
+ gst_pad_set_active (mysinkpad, FALSE);
+ gst_check_teardown_src_pad (mulawdec);
+ gst_check_teardown_sink_pad (mulawdec);
+ gst_check_teardown_element (mulawdec);
+ mulawdec = NULL;
+}
+
+GST_START_TEST (test_one_buffer)
+{
+ GstBuffer *buffer;
+ gint buf_size = 4096;
+ guint8 *dp;
+
+ fail_unless (gst_element_set_state (mulawdec, GST_STATE_PLAYING) ==
+ GST_STATE_CHANGE_SUCCESS, "could not change state to playing");
+
+ buffer = gst_buffer_new ();
+ dp = g_malloc0 (buf_size);
+ gst_buffer_append_memory (buffer,
+ gst_memory_new_wrapped (0, dp, buf_size, 0, buf_size, dp, g_free));
+ ASSERT_BUFFER_REFCOUNT (buffer, "buffer", 1);
+
+ fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
+
+ fail_unless (g_list_length (buffers) == 1);
+ fail_unless (gst_buffer_get_size (GST_BUFFER (g_list_first (buffers)->data)));
+}
+
+GST_END_TEST;
+
+static Suite *
+mulawdec_suite (void)
+{
+ Suite *s = suite_create ("mulawdec");
+ TCase *tc_chain = tcase_create ("mulawdec");
+
+ tcase_add_checked_fixture (tc_chain, mulawdec_setup, mulawdec_teardown);
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_one_buffer);
+ return s;
+}
+
+GST_CHECK_MAIN (mulawdec)
diff --git a/tests/check/elements/mulawenc.c b/tests/check/elements/mulawenc.c
new file mode 100644
index 0000000..3722186
--- /dev/null
+++ b/tests/check/elements/mulawenc.c
@@ -0,0 +1,175 @@
+/* GStreamer MulawEnc unit tests
+ *
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/check/gstcheck.h>
+#include <string.h>
+
+static GstPad *mysrcpad, *mysinkpad;
+static GstElement *mulawenc = NULL;
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-mulaw," "rate = (int) 8000," "channels = (int) 1")
+ );
+
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw,"
+ "format = (string) S16LE, "
+ "rate = (int) 8000, "
+ "channels = (int) 1, " "layout = (string)interleaved")
+ );
+
+static void
+mulawenc_setup (void)
+{
+ GstCaps *src_caps;
+
+ src_caps = gst_caps_from_string ("audio/x-raw,"
+ "format = (string) S16LE, "
+ "rate = (int) 8000, "
+ "channels = (int) 1, " "layout = (string)interleaved");
+
+ GST_DEBUG ("%s", __FUNCTION__);
+
+ mulawenc = gst_check_setup_element ("mulawenc");
+
+ mysrcpad = gst_check_setup_src_pad (mulawenc, &srctemplate);
+ mysinkpad = gst_check_setup_sink_pad (mulawenc, &sinktemplate);
+
+ gst_pad_set_active (mysrcpad, TRUE);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ gst_check_setup_events (mysrcpad, mulawenc, src_caps, GST_FORMAT_TIME);
+ gst_caps_unref (src_caps);
+}
+
+static void
+buffer_unref (void *buffer, void *user_data)
+{
+ gst_buffer_unref (GST_BUFFER (buffer));
+}
+
+static void
+mulawenc_teardown (void)
+{
+ /* free encoded buffers */
+ g_list_foreach (buffers, buffer_unref, NULL);
+ g_list_free (buffers);
+ buffers = NULL;
+
+ gst_pad_set_active (mysrcpad, FALSE);
+ gst_pad_set_active (mysinkpad, FALSE);
+ gst_check_teardown_src_pad (mulawenc);
+ gst_check_teardown_sink_pad (mulawenc);
+ gst_check_teardown_element (mulawenc);
+ mulawenc = NULL;
+}
+
+static gboolean
+check_for_maximum_bitrate (GstPad * pad, GstEvent ** eventp, gpointer user_data)
+{
+ gboolean *found_maximum_bitrate = (gboolean *) user_data;
+ GstEvent *event = *eventp;
+
+ if (event->type == GST_EVENT_TAG) {
+ GstTagList *taglist = NULL;
+ guint value = 0;
+ gst_event_parse_tag (event, &taglist);
+
+ fail_unless (taglist != NULL);
+
+ fail_unless (gst_tag_list_get_uint (taglist, GST_TAG_MAXIMUM_BITRATE,
+ &value));
+
+ /* bitrate needs to be exactly sample rate * channels * 8 */
+ fail_unless (value == 8000 * 1 * 8);
+
+ *found_maximum_bitrate = TRUE;
+ }
+
+ return TRUE;
+}
+
+GST_START_TEST (test_one_buffer)
+{
+ GstBuffer *buffer;
+ gint buf_size = 4096;
+ guint8 *dp;
+
+ fail_unless (gst_element_set_state (mulawenc, GST_STATE_PLAYING) ==
+ GST_STATE_CHANGE_SUCCESS, "could not change state to playing");
+
+ buffer = gst_buffer_new ();
+ dp = g_malloc0 (buf_size);
+ gst_buffer_append_memory (buffer,
+ gst_memory_new_wrapped (0, dp, buf_size, 0, buf_size, dp, g_free));
+ ASSERT_BUFFER_REFCOUNT (buffer, "buffer", 1);
+
+ fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
+
+ fail_unless (g_list_length (buffers) == 1);
+ fail_unless (gst_buffer_get_size (GST_BUFFER (g_list_first (buffers)->data)));
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_tags)
+{
+ GstBuffer *buffer;
+ gint buf_size = 4096;
+ guint8 *dp;
+ gboolean found_maximum_bitrate = FALSE;
+
+ fail_unless (gst_element_set_state (mulawenc, GST_STATE_PLAYING) ==
+ GST_STATE_CHANGE_SUCCESS, "could not change state to playing");
+
+ buffer = gst_buffer_new ();
+ dp = g_malloc0 (buf_size);
+ gst_buffer_append_memory (buffer,
+ gst_memory_new_wrapped (0, dp, buf_size, 0, buf_size, dp, g_free));
+ ASSERT_BUFFER_REFCOUNT (buffer, "buffer", 1);
+
+ fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
+ gst_pad_sticky_events_foreach (mysinkpad, check_for_maximum_bitrate,
+ &found_maximum_bitrate);
+ fail_unless (found_maximum_bitrate);
+}
+
+GST_END_TEST;
+
+static Suite *
+mulawenc_suite (void)
+{
+ Suite *s = suite_create ("mulawenc");
+ TCase *tc_chain = tcase_create ("mulawenc");
+
+ tcase_add_checked_fixture (tc_chain, mulawenc_setup, mulawenc_teardown);
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_one_buffer);
+ tcase_add_test (tc_chain, test_tags);
+ return s;
+}
+
+GST_CHECK_MAIN (mulawenc)
diff --git a/tests/check/elements/multifile.c b/tests/check/elements/multifile.c
new file mode 100644
index 0000000..20770bb
--- /dev/null
+++ b/tests/check/elements/multifile.c
@@ -0,0 +1,325 @@
+/* GStreamer unit test for multifile plugin
+ *
+ * Copyright (C) 2007 David A. Schleef <ds@schleef.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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <glib/gstdio.h>
+#include <unistd.h>
+
+#include <gst/check/gstcheck.h>
+#include <gst/video/video.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static void
+run_pipeline (GstElement * pipeline)
+{
+ gst_element_set_state (pipeline, GST_STATE_PAUSED);
+ gst_element_get_state (pipeline, NULL, NULL, -1);
+ gst_element_set_state (pipeline, GST_STATE_PLAYING);
+ /* FIXME too lazy */
+ g_usleep (1000000);
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+}
+
+GST_START_TEST (test_multifilesink_key_frame)
+{
+ GstElement *pipeline;
+ GstElement *mfs;
+ int i;
+ const gchar *tmpdir;
+ gchar *my_tmpdir;
+ gchar *template;
+ gchar *mfs_pattern;
+
+ tmpdir = g_get_tmp_dir ();
+ template = g_build_filename (tmpdir, "multifile-test-XXXXXX", NULL);
+ my_tmpdir = g_mkdtemp (template);
+ fail_if (my_tmpdir == NULL);
+
+ pipeline =
+ gst_parse_launch
+ ("videotestsrc num-buffers=10 ! video/x-raw,format=(string)I420,width=320,height=240 ! multifilesink name=mfs",
+ NULL);
+ fail_if (pipeline == NULL);
+ mfs = gst_bin_get_by_name (GST_BIN (pipeline), "mfs");
+ fail_if (mfs == NULL);
+ mfs_pattern = g_build_filename (my_tmpdir, "%05d", NULL);
+ g_object_set (G_OBJECT (mfs), "location", mfs_pattern, NULL);
+ g_object_unref (mfs);
+ run_pipeline (pipeline);
+ gst_object_unref (pipeline);
+
+ for (i = 0; i < 10; i++) {
+ char *s;
+
+ s = g_strdup_printf (mfs_pattern, i);
+ fail_if (g_remove (s) != 0);
+ g_free (s);
+ }
+ fail_if (g_remove (my_tmpdir) != 0);
+
+ g_free (mfs_pattern);
+ g_free (my_tmpdir);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_multifilesink_max_files)
+{
+ GstElement *pipeline;
+ GstElement *mfs;
+ int i;
+ const gchar *tmpdir;
+ gchar *my_tmpdir;
+ gchar *template;
+ gchar *mfs_pattern;
+
+ tmpdir = g_get_tmp_dir ();
+ template = g_build_filename (tmpdir, "multifile-test-XXXXXX", NULL);
+ my_tmpdir = g_mkdtemp (template);
+ fail_if (my_tmpdir == NULL);
+
+ pipeline =
+ gst_parse_launch
+ ("videotestsrc num-buffers=10 ! video/x-raw,format=(string)I420,width=320,height=240 ! multifilesink name=mfs",
+ NULL);
+ fail_if (pipeline == NULL);
+ mfs = gst_bin_get_by_name (GST_BIN (pipeline), "mfs");
+ fail_if (mfs == NULL);
+ mfs_pattern = g_build_filename (my_tmpdir, "%05d", NULL);
+ g_object_set (G_OBJECT (mfs), "location", mfs_pattern, "max-files", 3, NULL);
+ g_object_unref (mfs);
+ run_pipeline (pipeline);
+ gst_object_unref (pipeline);
+
+ for (i = 0; i < 7; i++) {
+ char *s;
+
+ s = g_strdup_printf (mfs_pattern, i);
+ fail_unless (g_remove (s) != 0);
+ g_free (s);
+ }
+ for (i = 7; i < 10; i++) {
+ char *s;
+
+ s = g_strdup_printf (mfs_pattern, i);
+ fail_if (g_remove (s) != 0);
+ g_free (s);
+ }
+ fail_if (g_remove (my_tmpdir) != 0);
+
+ g_free (mfs_pattern);
+ g_free (my_tmpdir);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_multifilesink_key_unit)
+{
+ GstElement *mfs;
+ int i;
+ const gchar *tmpdir;
+ gchar *my_tmpdir;
+ gchar *template;
+ gchar *mfs_pattern;
+ GstBuffer *buf;
+ GstPad *sink;
+ GstSegment segment;
+
+ tmpdir = g_get_tmp_dir ();
+ template = g_build_filename (tmpdir, "multifile-test-XXXXXX", NULL);
+ my_tmpdir = g_mkdtemp (template);
+ fail_if (my_tmpdir == NULL);
+
+ mfs = gst_element_factory_make ("multifilesink", NULL);
+ fail_if (mfs == NULL);
+ mfs_pattern = g_build_filename (my_tmpdir, "%05d", NULL);
+ g_object_set (G_OBJECT (mfs), "location", mfs_pattern, "next-file", 3, NULL);
+ fail_if (gst_element_set_state (mfs,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
+
+ sink = gst_element_get_static_pad (mfs, "sink");
+
+ gst_pad_send_event (sink, gst_event_new_stream_start ("test"));
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ gst_pad_send_event (sink, gst_event_new_segment (&segment));
+
+ buf = gst_buffer_new_and_alloc (4);
+
+ gst_buffer_fill (buf, 0, "foo", 4);
+ fail_if (gst_pad_chain (sink, gst_buffer_copy (buf)) != GST_FLOW_OK);
+
+ gst_buffer_fill (buf, 0, "bar", 4);
+ fail_if (gst_pad_chain (sink, gst_buffer_copy (buf)) != GST_FLOW_OK);
+
+ fail_unless (gst_pad_send_event (sink,
+ gst_video_event_new_downstream_force_key_unit (GST_CLOCK_TIME_NONE,
+ GST_CLOCK_TIME_NONE, GST_CLOCK_TIME_NONE, TRUE, 1)));
+
+ gst_buffer_fill (buf, 0, "baz", 4);
+ fail_if (gst_pad_chain (sink, buf) != GST_FLOW_OK);
+
+ fail_if (gst_element_set_state (mfs,
+ GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE);
+
+ for (i = 0; i < 2; i++) {
+ char *s;
+
+ s = g_strdup_printf (mfs_pattern, i);
+ fail_if (g_remove (s) != 0);
+ g_free (s);
+ }
+ fail_if (g_remove (my_tmpdir) != 0);
+
+ g_free (mfs_pattern);
+ g_free (my_tmpdir);
+ gst_object_unref (sink);
+ gst_object_unref (mfs);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_multifilesrc)
+{
+ GstElement *pipeline;
+ GstElement *mfs;
+ int i;
+ const gchar *tmpdir;
+ gchar *my_tmpdir;
+ gchar *template;
+ gchar *mfs_pattern;
+
+ tmpdir = g_get_tmp_dir ();
+ template = g_build_filename (tmpdir, "multifile-test-XXXXXX", NULL);
+ my_tmpdir = g_mkdtemp (template);
+ fail_if (my_tmpdir == NULL);
+
+ pipeline =
+ gst_parse_launch
+ ("videotestsrc num-buffers=10 ! video/x-raw,format=(string)I420,width=320,height=240 ! multifilesink name=mfs",
+ NULL);
+ fail_if (pipeline == NULL);
+ mfs = gst_bin_get_by_name (GST_BIN (pipeline), "mfs");
+ fail_if (mfs == NULL);
+ mfs_pattern = g_build_filename (my_tmpdir, "%05d", NULL);
+ g_object_set (G_OBJECT (mfs), "location", mfs_pattern, NULL);
+ g_free (mfs_pattern);
+ g_object_unref (mfs);
+ run_pipeline (pipeline);
+ gst_object_unref (pipeline);
+
+ pipeline =
+ gst_parse_launch
+ ("multifilesrc ! video/x-raw,format=(string)I420,width=320,height=240,framerate=10/1 ! fakesink",
+ NULL);
+ fail_if (pipeline == NULL);
+ mfs = gst_bin_get_by_name (GST_BIN (pipeline), "multifilesrc0");
+ fail_if (mfs == NULL);
+ mfs_pattern = g_build_filename (my_tmpdir, "%05d", NULL);
+ g_object_set (G_OBJECT (mfs), "location", mfs_pattern, NULL);
+ g_object_unref (mfs);
+ run_pipeline (pipeline);
+ gst_object_unref (pipeline);
+
+ for (i = 0; i < 10; i++) {
+ char *s;
+
+ s = g_strdup_printf (mfs_pattern, i);
+ fail_if (g_remove (s) != 0);
+ g_free (s);
+ }
+ fail_if (g_remove (my_tmpdir) != 0);
+
+ g_free (mfs_pattern);
+ g_free (my_tmpdir);
+}
+
+GST_END_TEST;
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+/* make sure stop_index is honoured even if the next target file exists */
+GST_START_TEST (test_multifilesrc_stop_index)
+{
+ GstElement *src;
+ GstEvent *event;
+ GstPad *sinkpad;
+ gchar *fn;
+
+ src = gst_check_setup_element ("multifilesrc");
+ fail_unless (src != NULL);
+
+ fn = g_build_filename (GST_TEST_FILES_PATH, "image.jpg", NULL);
+ g_object_set (src, "location", fn, NULL);
+ g_free (fn);
+
+ g_object_set (src, "stop-index", 5, NULL);
+
+ sinkpad = gst_check_setup_sink_pad_by_name (src, &sinktemplate, "src");
+ fail_unless (sinkpad != NULL);
+ gst_pad_set_active (sinkpad, TRUE);
+
+ gst_element_set_state (src, GST_STATE_PLAYING);
+
+ gst_element_get_state (src, NULL, NULL, -1);
+
+ /* busy-loop for EOS */
+ do {
+ g_usleep (G_USEC_PER_SEC / 10);
+ event = gst_pad_get_sticky_event (sinkpad, GST_EVENT_EOS, 0);
+ } while (event == NULL);
+ gst_event_unref (event);
+
+ /* Range appears to be [ start, stop ] */
+ fail_unless_equals_int (g_list_length (buffers), 5 + 1);
+
+ gst_element_set_state (src, GST_STATE_NULL);
+
+ gst_check_teardown_pad_by_name (src, "src");
+ gst_check_teardown_element (src);
+}
+
+GST_END_TEST;
+
+
+static Suite *
+multifile_suite (void)
+{
+ Suite *s = suite_create ("multifile");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_multifilesink_key_frame);
+ tcase_add_test (tc_chain, test_multifilesink_max_files);
+ tcase_add_test (tc_chain, test_multifilesink_key_unit);
+ tcase_add_test (tc_chain, test_multifilesrc);
+ tcase_add_test (tc_chain, test_multifilesrc_stop_index);
+
+ return s;
+}
+
+GST_CHECK_MAIN (multifile);
diff --git a/tests/check/elements/parser.c b/tests/check/elements/parser.c
new file mode 100644
index 0000000..52ffed8
--- /dev/null
+++ b/tests/check/elements/parser.c
@@ -0,0 +1,435 @@
+/*
+ * GStreamer
+ *
+ * unit test for (audio) parser
+ *
+ * Copyright (C) 2008 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Stefan Kost <stefan.kost@nokia.com>
+ *
+ * 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.
+ */
+
+#include <gst/check/gstcheck.h>
+#include "elements/parser.h"
+
+
+/* context state variables */
+const gchar *ctx_factory;
+GstStaticPadTemplate *ctx_sink_template;
+GstStaticPadTemplate *ctx_src_template;
+GstCaps *ctx_input_caps;
+GstCaps *ctx_output_caps;
+guint ctx_discard = 0;
+datablob ctx_headers[MAX_HEADERS] = { {NULL, 0}, };
+
+gboolean ctx_no_metadata = FALSE;
+
+/* helper variables */
+GList *current_buf = NULL;
+
+GstPad *srcpad, *sinkpad;
+guint dataoffset = 0;
+GstClockTime ts_counter = 0;
+gint64 offset_counter = 0;
+guint buffer_counter = 0;
+
+typedef struct
+{
+ guint discard;
+ guint buffers_before_offset_skip;
+ guint offset_skip_amount;
+ const guint8 *data_to_verify;
+ guint data_to_verify_size;
+ GstCaps *caps;
+ gboolean no_metadata;
+} buffer_verify_data_s;
+
+/* takes a copy of the passed buffer data */
+static GstBuffer *
+buffer_new (const unsigned char *buffer_data, guint size)
+{
+ GstBuffer *buffer;
+
+ buffer = gst_buffer_new_and_alloc (size);
+ if (buffer_data) {
+ gst_buffer_fill (buffer, 0, buffer_data, size);
+ } else {
+ guint i;
+ GstMapInfo map;
+
+ gst_buffer_map (buffer, &map, GST_MAP_WRITE);
+ /* Create a recognizable pattern (loop 0x00 -> 0xff) in the data block */
+ for (i = 0; i < map.size; i++) {
+ map.data[i] = i % 0x100;
+ }
+ gst_buffer_unmap (buffer, &map);
+ }
+
+ /* gst_buffer_set_caps (buffer, GST_PAD_CAPS (srcpad)); */
+ GST_BUFFER_OFFSET (buffer) = dataoffset;
+ dataoffset += size;
+ return buffer;
+}
+
+/*
+ * Adds buffer sizes together.
+ */
+static void
+buffer_count_size (void *buffer, void *user_data)
+{
+ guint *sum = (guint *) user_data;
+ *sum += gst_buffer_get_size (buffer);
+}
+
+/*
+ * Verify that given buffer contains predefined ADTS frame.
+ */
+static void
+buffer_verify_data (void *buffer, void *user_data)
+{
+ buffer_verify_data_s *vdata;
+
+ if (!user_data) {
+ return;
+ }
+
+ vdata = (buffer_verify_data_s *) user_data;
+
+ GST_DEBUG ("discard: %d", vdata->discard);
+ if (vdata->discard) {
+ buffer_counter++;
+ if (buffer_counter == vdata->discard) {
+ buffer_counter = 0;
+ vdata->discard = 0;
+ }
+ return;
+ }
+
+ fail_unless (gst_buffer_get_size (buffer) == vdata->data_to_verify_size);
+ fail_unless (gst_buffer_memcmp (buffer, 0, vdata->data_to_verify,
+ vdata->data_to_verify_size) == 0);
+
+ if (vdata->buffers_before_offset_skip) {
+ /* This is for skipping the garbage in some test cases */
+ if (buffer_counter == vdata->buffers_before_offset_skip) {
+ offset_counter += vdata->offset_skip_amount;
+ }
+ }
+ if (!vdata->no_metadata) {
+ fail_unless (GST_BUFFER_TIMESTAMP (buffer) == ts_counter);
+ fail_unless (GST_BUFFER_DURATION (buffer) != 0);
+ fail_unless (GST_BUFFER_OFFSET (buffer) == offset_counter);
+ }
+
+ ts_counter += GST_BUFFER_DURATION (buffer);
+ offset_counter += gst_buffer_get_size (buffer);
+ buffer_counter++;
+}
+
+static GstElement *
+setup_element (const gchar * factory, GstStaticPadTemplate * sink_template,
+ GstCaps * sink_caps, GstStaticPadTemplate * src_template,
+ GstCaps * src_caps)
+{
+ GstElement *element;
+ GstBus *bus;
+ gchar *caps_str = NULL;
+
+ element = gst_check_setup_element (factory);
+ srcpad = gst_check_setup_src_pad (element, src_template);
+ if (sink_caps) {
+ caps_str = gst_caps_to_string (sink_caps);
+ sink_template->static_caps.string = caps_str;
+ }
+ sinkpad = gst_check_setup_sink_pad (element, sink_template);
+ gst_pad_set_active (srcpad, TRUE);
+ gst_check_setup_events (srcpad, element, src_caps, GST_FORMAT_BYTES);
+ gst_pad_set_active (sinkpad, TRUE);
+
+ bus = gst_bus_new ();
+ gst_element_set_bus (element, bus);
+
+ fail_unless (gst_element_set_state (element,
+ GST_STATE_PLAYING) != GST_STATE_CHANGE_FAILURE,
+ "could not set to playing");
+
+ ts_counter = offset_counter = buffer_counter = 0;
+ buffers = NULL;
+ g_free (caps_str);
+ return element;
+}
+
+static void
+cleanup_element (GstElement * element)
+{
+ GstBus *bus;
+
+ /* Free parsed buffers */
+ gst_check_drop_buffers ();
+
+ bus = GST_ELEMENT_BUS (element);
+ gst_bus_set_flushing (bus, TRUE);
+ gst_object_unref (bus);
+
+ gst_pad_set_active (srcpad, FALSE);
+ gst_pad_set_active (sinkpad, FALSE);
+ gst_check_teardown_src_pad (element);
+ gst_check_teardown_sink_pad (element);
+ gst_check_teardown_element (element);
+}
+
+/* inits a standard test */
+void
+gst_parser_test_init (GstParserTest * ptest, guint8 * data, guint size,
+ guint num)
+{
+ /* need these */
+ fail_unless (ctx_factory != NULL);
+ fail_unless (ctx_src_template != NULL);
+ fail_unless (ctx_sink_template != NULL);
+
+ /* basics */
+ memset (ptest, 0, sizeof (*ptest));
+ ptest->factory = ctx_factory;
+ ptest->sink_template = ctx_sink_template;
+ ptest->src_template = ctx_src_template;
+ ptest->framed = TRUE;
+ /* could be NULL if not relevant/needed */
+ ptest->src_caps = ctx_input_caps;
+ ptest->sink_caps = ctx_output_caps;
+ memcpy (ptest->headers, ctx_headers, sizeof (ptest->headers));
+ ptest->discard = ctx_discard;
+ /* some data that pleases caller */
+ ptest->series[0].data = data;
+ ptest->series[0].size = size;
+ ptest->series[0].num = num;
+ ptest->series[0].fpb = 1;
+ ptest->series[1].fpb = 1;
+ ptest->series[2].fpb = 1;
+ ptest->no_metadata = ctx_no_metadata;
+}
+
+/*
+ * Test if the parser pushes clean data properly.
+ */
+void
+gst_parser_test_run (GstParserTest * test, GstCaps ** out_caps)
+{
+ buffer_verify_data_s vdata = { 0, 0, 0, NULL, 0, NULL, FALSE };
+ GstElement *element;
+ GstBuffer *buffer = NULL;
+ GstCaps *src_caps;
+ guint i, j, k;
+ guint frames = 0, size = 0;
+
+ element = setup_element (test->factory, test->sink_template, NULL,
+ test->src_template, test->src_caps);
+
+ /* push some setup headers */
+ for (j = 0; j < G_N_ELEMENTS (test->headers) && test->headers[j].data; j++) {
+ buffer = buffer_new (test->headers[j].data, test->headers[j].size);
+ fail_unless_equals_int (gst_pad_push (srcpad, buffer), GST_FLOW_OK);
+ }
+
+ for (j = 0; j < 3; j++) {
+ for (i = 0; i < test->series[j].num; i++) {
+ /* sanity enforcing */
+ for (k = 0; k < MAX (1, test->series[j].fpb); k++) {
+ if (!k)
+ buffer = buffer_new (test->series[j].data, test->series[j].size);
+ else {
+ buffer = gst_buffer_append (buffer,
+ buffer_new (test->series[j].data, test->series[j].size));
+ }
+ }
+ fail_unless_equals_int (gst_pad_push (srcpad, buffer), GST_FLOW_OK);
+ if (j == 0)
+ vdata.buffers_before_offset_skip++;
+ else if (j == 1)
+ vdata.offset_skip_amount += test->series[j].size * test->series[j].fpb;
+ if (j != 1) {
+ frames += test->series[j].fpb;
+ size += test->series[j].size * test->series[j].fpb;
+ }
+ }
+ }
+ gst_pad_push_event (srcpad, gst_event_new_eos ());
+
+ if (G_LIKELY (test->framed))
+ fail_unless_equals_int (g_list_length (buffers) - test->discard, frames);
+
+ /* if all frames are identical, do extended test,
+ * otherwise only verify total data size */
+ if (test->series[0].data && (!test->series[2].size ||
+ (test->series[0].size == test->series[2].size && test->series[2].data
+ && !memcmp (test->series[0].data, test->series[2].data,
+ test->series[0].size)))) {
+ vdata.data_to_verify = test->series[0].data;
+ vdata.data_to_verify_size = test->series[0].size;
+ vdata.caps = test->sink_caps;
+ vdata.discard = test->discard;
+ vdata.no_metadata = test->no_metadata;
+ g_list_foreach (buffers, buffer_verify_data, &vdata);
+ } else {
+ guint datasum = 0;
+
+ g_list_foreach (buffers, buffer_count_size, &datasum);
+ size -= test->dropped;
+ fail_unless_equals_int (datasum, size);
+ }
+
+ src_caps = gst_pad_get_current_caps (sinkpad);
+ GST_LOG ("output caps: %" GST_PTR_FORMAT, src_caps);
+
+ if (test->sink_caps) {
+ GST_LOG ("%" GST_PTR_FORMAT " = %" GST_PTR_FORMAT " ?", src_caps,
+ test->sink_caps);
+ fail_unless (gst_caps_is_equal (src_caps, test->sink_caps));
+ }
+
+ if (out_caps)
+ *out_caps = src_caps;
+ else
+ gst_caps_unref (src_caps);
+
+ cleanup_element (element);
+}
+
+/*
+ * Test if the parser pushes clean data properly.
+ */
+void
+gst_parser_test_normal (guint8 * data, guint size)
+{
+ GstParserTest ptest;
+
+ gst_parser_test_init (&ptest, data, size, 10);
+ gst_parser_test_run (&ptest, NULL);
+}
+
+/*
+ * Test if parser drains its buffers properly. Even one single frame
+ * should be drained and pushed forward when EOS occurs. This single frame
+ * case is special, since normally the parser needs more data to be sure
+ * about stream format. But it should still push the frame forward in EOS.
+ */
+void
+gst_parser_test_drain_single (guint8 * data, guint size)
+{
+ GstParserTest ptest;
+
+ gst_parser_test_init (&ptest, data, size, 1);
+ gst_parser_test_run (&ptest, NULL);
+}
+
+/*
+ * Make sure that parser does not drain garbage when EOS occurs.
+ */
+void
+gst_parser_test_drain_garbage (guint8 * data, guint size, guint8 * garbage,
+ guint gsize)
+{
+ GstParserTest ptest;
+
+ /* provide enough initial frames since it may take some parsers some
+ * time to be convinced of proper sync */
+ gst_parser_test_init (&ptest, data, size, 10);
+ ptest.series[1].data = garbage;
+ ptest.series[1].size = gsize;
+ ptest.series[1].num = 1;
+ gst_parser_test_run (&ptest, NULL);
+}
+
+/*
+ * Test if parser splits a buffer that contains two frames into two
+ * separate buffers properly.
+ */
+void
+gst_parser_test_split (guint8 * data, guint size)
+{
+ GstParserTest ptest;
+
+ gst_parser_test_init (&ptest, data, size, 10);
+ ptest.series[0].fpb = 2;
+ gst_parser_test_run (&ptest, NULL);
+}
+
+/*
+ * Test if the parser skips garbage between frames properly.
+ */
+void
+gst_parser_test_skip_garbage (guint8 * data, guint size, guint8 * garbage,
+ guint gsize)
+{
+ GstParserTest ptest;
+
+ gst_parser_test_init (&ptest, data, size, 10);
+ ptest.series[1].data = garbage;
+ ptest.series[1].size = gsize;
+ ptest.series[1].num = 1;
+ ptest.series[2].data = data;
+ ptest.series[2].size = size;
+ ptest.series[2].num = 10;
+ gst_parser_test_run (&ptest, NULL);
+}
+
+/*
+ * Test if the src caps are set according to stream format.
+ */
+void
+gst_parser_test_output_caps (guint8 * data, guint size,
+ const gchar * input_caps, const gchar * output_caps)
+{
+ GstParserTest ptest;
+
+ gst_parser_test_init (&ptest, data, size, 10);
+ if (input_caps) {
+ ptest.src_caps = gst_caps_from_string (input_caps);
+ fail_unless (ptest.src_caps != NULL);
+ }
+ if (output_caps) {
+ ptest.sink_caps = gst_caps_from_string (output_caps);
+ fail_unless (ptest.sink_caps != NULL);
+ }
+ gst_parser_test_run (&ptest, NULL);
+ if (ptest.sink_caps)
+ gst_caps_unref (ptest.sink_caps);
+ if (ptest.src_caps)
+ gst_caps_unref (ptest.src_caps);
+}
+
+/*
+ * Test if the src caps are set according to stream format.
+ */
+GstCaps *
+gst_parser_test_get_output_caps (guint8 * data, guint size,
+ const gchar * input_caps)
+{
+ GstParserTest ptest;
+ GstCaps *out_caps;
+
+ gst_parser_test_init (&ptest, data, size, 10);
+ if (input_caps) {
+ ptest.src_caps = gst_caps_from_string (input_caps);
+ fail_unless (ptest.src_caps != NULL);
+ }
+ gst_parser_test_run (&ptest, &out_caps);
+ if (ptest.src_caps)
+ gst_caps_unref (ptest.src_caps);
+
+ return out_caps;
+}
diff --git a/tests/check/elements/parser.h b/tests/check/elements/parser.h
new file mode 100644
index 0000000..c4867cd
--- /dev/null
+++ b/tests/check/elements/parser.h
@@ -0,0 +1,95 @@
+/*
+ * GStreamer
+ *
+ * unit test for (audio) parser
+ *
+ * Copyright (C) 2008 Nokia Corporation. All rights reserved.
+ *
+ * Contact: Stefan Kost <stefan.kost@nokia.com>
+ *
+ * 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.
+ */
+
+#include <gst/check/gstcheck.h>
+
+#define MAX_HEADERS 10
+
+typedef struct {
+ guint8 *data;
+ guint size;
+} datablob;
+
+/* context state variables; to be set by test using this helper */
+/* mandatory */
+extern const gchar *ctx_factory;
+extern GstStaticPadTemplate *ctx_sink_template;
+extern GstStaticPadTemplate *ctx_src_template;
+/* optional */
+extern GstCaps *ctx_input_caps;
+extern GstCaps *ctx_output_caps;
+extern guint ctx_discard;
+extern datablob ctx_headers[MAX_HEADERS];
+extern gboolean ctx_no_metadata;
+
+/* no refs taken/kept, all up to caller */
+typedef struct
+{
+ const gchar *factory;
+ GstStaticPadTemplate *sink_template;
+ GstStaticPadTemplate *src_template;
+ /* caps that go into element */
+ GstCaps *src_caps;
+ /* optional: output caps to verify */
+ GstCaps *sink_caps;
+ /* initial headers */
+ datablob headers[MAX_HEADERS];
+ /* initial (header) output to forego checking */
+ guint discard;
+ /* series of buffers; middle series considered garbage */
+ struct {
+ /* data and size */
+ guint8 *data;
+ guint size;
+ /* num of frames with above data per buffer */
+ guint fpb;
+ /* num of buffers */
+ guint num;
+ } series[3];
+ /* sigh, weird cases */
+ gboolean framed;
+ guint dropped;
+ gboolean no_metadata;
+} GstParserTest;
+
+void gst_parser_test_init (GstParserTest * ptest, guint8 * data, guint size, guint num);
+
+void gst_parser_test_run (GstParserTest * test, GstCaps ** out_caps);
+
+void gst_parser_test_normal (guint8 *data, guint size);
+
+void gst_parser_test_drain_single (guint8 *data, guint size);
+
+void gst_parser_test_drain_garbage (guint8 *data, guint size, guint8 *garbage, guint gsize);
+
+void gst_parser_test_split (guint8 *data, guint size);
+
+void gst_parser_test_skip_garbage (guint8 *data, guint size, guint8 *garbage, guint gsize);
+
+void gst_parser_test_output_caps (guint8 *data, guint size, const gchar * input_caps,
+ const gchar * output_caps);
+
+GstCaps *gst_parser_test_get_output_caps (guint8 *data, guint size, const gchar * input_caps);
+
diff --git a/tests/check/elements/qtmux.c b/tests/check/elements/qtmux.c
new file mode 100755
index 0000000..105b1e8
--- /dev/null
+++ b/tests/check/elements/qtmux.c
@@ -0,0 +1,932 @@
+/* GStreamer
+ *
+ * unit test for qtmux
+ *
+ * Copyright (C) <2008> Mark Nauwelaerts <mnauw@users.sf.net>
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <glib/gstdio.h>
+
+#include <gst/check/gstcheck.h>
+#include <gst/pbutils/encoding-profile.h>
+
+/* 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;
+
+#define AUDIO_CAPS_STRING "audio/mpeg, " \
+ "mpegversion = (int) 1, " \
+ "layer = (int) 3, " \
+ "channels = (int) 2, " \
+ "rate = (int) 48000"
+
+#define AUDIO_AAC_CAPS_STRING "audio/mpeg, " \
+ "mpegversion=(int)4, " \
+ "channels=(int)1, " \
+ "rate=(int)44100, " \
+ "stream-format=(string)raw, " \
+ "level=(string)2, " \
+ "base-profile=(string)lc, " \
+ "profile=(string)lc, " \
+ "codec_data=(buffer)1208"
+
+#define VIDEO_CAPS_STRING "video/mpeg, " \
+ "mpegversion = (int) 4, " \
+ "systemstream = (boolean) false, " \
+ "width = (int) 384, " \
+ "height = (int) 288, " \
+ "framerate = (fraction) 25/1"
+
+#define VIDEO_CAPS_H264_STRING "video/x-h264, " \
+ "width=(int)320, " \
+ "height=(int)240, " \
+ "framerate=(fraction)30/1, " \
+ "pixel-aspect-ratio=(fraction)1/1, " \
+ "codec_data=(buffer)01640014ffe1001867640014a" \
+ "cd94141fb0110000003001773594000f14299600" \
+ "1000568ebecb22c, " \
+ "stream-format=(string)avc, " \
+ "alignment=(string)au, " \
+ "level=(string)2, " \
+ "profile=(string)high"
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("video/quicktime"));
+static GstStaticPadTemplate srcvideotemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (VIDEO_CAPS_STRING));
+
+static GstStaticPadTemplate srcvideoh264template =
+GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (VIDEO_CAPS_H264_STRING));
+
+static GstStaticPadTemplate srcaudiotemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (AUDIO_CAPS_STRING));
+
+static GstStaticPadTemplate srcaudioaactemplate =
+GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (AUDIO_AAC_CAPS_STRING));
+
+/* setup and teardown needs some special handling for muxer */
+static GstPad *
+setup_src_pad (GstElement * element,
+ GstStaticPadTemplate * template, const gchar * sinkname)
+{
+ GstPad *srcpad, *sinkpad;
+
+ GST_DEBUG_OBJECT (element, "setting up sending pad");
+ /* sending pad */
+ srcpad = gst_pad_new_from_static_template (template, "src");
+ fail_if (srcpad == NULL, "Could not create a srcpad");
+ ASSERT_OBJECT_REFCOUNT (srcpad, "srcpad", 1);
+
+ if (!(sinkpad = gst_element_get_static_pad (element, sinkname)))
+ sinkpad = gst_element_get_request_pad (element, sinkname);
+ fail_if (sinkpad == NULL, "Could not get sink pad from %s",
+ GST_ELEMENT_NAME (element));
+ /* references are owned by: 1) us, 2) qtmux, 3) collect pads */
+ ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 3);
+ fail_unless (gst_pad_link (srcpad, sinkpad) == GST_PAD_LINK_OK,
+ "Could not link source and %s sink pads", GST_ELEMENT_NAME (element));
+ gst_object_unref (sinkpad); /* because we got it higher up */
+
+ /* references are owned by: 1) qtmux, 2) collect pads */
+ ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 2);
+
+ return srcpad;
+}
+
+static void
+teardown_src_pad (GstPad * srcpad)
+{
+ GstPad *sinkpad;
+
+ /* clean up floating src pad */
+ sinkpad = gst_pad_get_peer (srcpad);
+ fail_if (sinkpad == NULL);
+ /* pad refs held by 1) qtmux 2) collectpads and 3) us (through _get_peer) */
+ ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 3);
+
+ gst_pad_unlink (srcpad, sinkpad);
+
+ /* after unlinking, pad refs still held by
+ * 1) qtmux and 2) collectpads and 3) us (through _get_peer) */
+ ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 3);
+ gst_object_unref (sinkpad);
+ /* one more ref is held by element itself */
+
+ /* pad refs held by creator */
+ ASSERT_OBJECT_REFCOUNT (srcpad, "srcpad", 1);
+ gst_object_unref (srcpad);
+}
+
+static GstElement *
+setup_qtmux (GstStaticPadTemplate * srctemplate, const gchar * sinkname)
+{
+ GstElement *qtmux;
+
+ GST_DEBUG ("setup_qtmux");
+ qtmux = gst_check_setup_element ("qtmux");
+ mysrcpad = setup_src_pad (qtmux, srctemplate, sinkname);
+ mysinkpad = gst_check_setup_sink_pad (qtmux, &sinktemplate);
+ gst_pad_set_active (mysrcpad, TRUE);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ return qtmux;
+}
+
+static void
+cleanup_qtmux (GstElement * qtmux, const gchar * sinkname)
+{
+ GST_DEBUG ("cleanup_qtmux");
+ gst_element_set_state (qtmux, GST_STATE_NULL);
+
+ gst_pad_set_active (mysrcpad, FALSE);
+ gst_pad_set_active (mysinkpad, FALSE);
+ teardown_src_pad (mysrcpad);
+ gst_check_teardown_sink_pad (qtmux);
+ gst_check_teardown_element (qtmux);
+}
+
+static void
+check_qtmux_pad (GstStaticPadTemplate * srctemplate, const gchar * sinkname,
+ guint32 dts_method)
+{
+ GstElement *qtmux;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ int num_buffers;
+ int i;
+ guint8 data0[12] = "\000\000\000\024ftypqt ";
+ guint8 data1[8] = "\000\000\000\001mdat";
+ guint8 data2[4] = "moov";
+ GstSegment segment;
+
+ qtmux = setup_qtmux (srctemplate, sinkname);
+ g_object_set (qtmux, "dts-method", dts_method, NULL);
+ fail_unless (gst_element_set_state (qtmux,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ gst_pad_push_event (mysrcpad, gst_event_new_stream_start ("test"));
+
+ caps = gst_pad_get_pad_template_caps (mysrcpad);
+ gst_pad_set_caps (mysrcpad, caps);
+ gst_caps_unref (caps);
+
+ /* ensure segment (format) properly setup */
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
+
+ inbuffer = gst_buffer_new_and_alloc (1);
+ gst_buffer_memset (inbuffer, 0, 0, 1);
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ GST_BUFFER_DURATION (inbuffer) = 40 * GST_MSECOND;
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+
+ /* send eos to have moov written */
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()) == TRUE);
+
+ num_buffers = g_list_length (buffers);
+ /* at least expect ftyp, mdat header, buffer chunk and moov */
+ fail_unless (num_buffers >= 4);
+
+ /* clean up first to clear any pending refs in sticky caps */
+ cleanup_qtmux (qtmux, sinkname);
+
+ for (i = 0; i < num_buffers; ++i) {
+ outbuffer = GST_BUFFER (buffers->data);
+ fail_if (outbuffer == NULL);
+ buffers = g_list_remove (buffers, outbuffer);
+
+ switch (i) {
+ case 0:
+ {
+ /* ftyp header */
+ fail_unless (gst_buffer_get_size (outbuffer) >= 20);
+ fail_unless (gst_buffer_memcmp (outbuffer, 0, data0,
+ sizeof (data0)) == 0);
+ fail_unless (gst_buffer_memcmp (outbuffer, 16, data0 + 8, 4) == 0);
+ break;
+ }
+ case 1: /* mdat header */
+ fail_unless (gst_buffer_get_size (outbuffer) == 16);
+ fail_unless (gst_buffer_memcmp (outbuffer, 0, data1, sizeof (data1))
+ == 0);
+ break;
+ case 2: /* buffer we put in */
+ fail_unless (gst_buffer_get_size (outbuffer) == 1);
+ break;
+ case 3: /* moov */
+ fail_unless (gst_buffer_get_size (outbuffer) > 8);
+ fail_unless (gst_buffer_memcmp (outbuffer, 4, data2,
+ sizeof (data2)) == 0);
+ break;
+ default:
+ break;
+ }
+
+ ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
+ gst_buffer_unref (outbuffer);
+ outbuffer = NULL;
+ }
+
+ g_list_free (buffers);
+ buffers = NULL;
+}
+
+static void
+check_qtmux_pad_fragmented (GstStaticPadTemplate * srctemplate,
+ const gchar * sinkname, guint32 dts_method, gboolean streamable)
+{
+ GstElement *qtmux;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ int num_buffers;
+ int i;
+ guint8 data0[12] = "\000\000\000\024ftypqt ";
+ guint8 data1[4] = "mdat";
+ guint8 data2[4] = "moov";
+ guint8 data3[4] = "moof";
+ guint8 data4[4] = "mfra";
+ GstSegment segment;
+
+ qtmux = setup_qtmux (srctemplate, sinkname);
+ g_object_set (qtmux, "dts-method", dts_method, NULL);
+ g_object_set (qtmux, "fragment-duration", 2000, NULL);
+ g_object_set (qtmux, "streamable", streamable, NULL);
+ fail_unless (gst_element_set_state (qtmux,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ gst_pad_push_event (mysrcpad, gst_event_new_stream_start ("test"));
+
+ caps = gst_pad_get_pad_template_caps (mysrcpad);
+ gst_pad_set_caps (mysrcpad, caps);
+ gst_caps_unref (caps);
+
+ /* ensure segment (format) properly setup */
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
+
+ inbuffer = gst_buffer_new_and_alloc (1);
+ gst_buffer_memset (inbuffer, 0, 0, 1);
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ GST_BUFFER_DURATION (inbuffer) = 40 * GST_MSECOND;
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+
+ /* send eos to have all written */
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()) == TRUE);
+
+ num_buffers = g_list_length (buffers);
+ /* at least expect ftyp, moov, moof, mdat header, buffer chunk
+ * and optionally mfra */
+ fail_unless (num_buffers >= 5);
+
+ /* clean up first to clear any pending refs in sticky caps */
+ cleanup_qtmux (qtmux, sinkname);
+
+ for (i = 0; i < num_buffers; ++i) {
+ outbuffer = GST_BUFFER (buffers->data);
+ fail_if (outbuffer == NULL);
+ buffers = g_list_remove (buffers, outbuffer);
+
+ switch (i) {
+ case 0:
+ {
+ /* ftyp header */
+ fail_unless (gst_buffer_get_size (outbuffer) >= 20);
+ fail_unless (gst_buffer_memcmp (outbuffer, 0, data0,
+ sizeof (data0)) == 0);
+ fail_unless (gst_buffer_memcmp (outbuffer, 16, data0 + 8, 4) == 0);
+ break;
+ }
+ case 1: /* moov */
+ fail_unless (gst_buffer_get_size (outbuffer) > 8);
+ fail_unless (gst_buffer_memcmp (outbuffer, 4, data2,
+ sizeof (data2)) == 0);
+ break;
+ case 2: /* moof */
+ fail_unless (gst_buffer_get_size (outbuffer) > 8);
+ fail_unless (gst_buffer_memcmp (outbuffer, 4, data3,
+ sizeof (data3)) == 0);
+ break;
+ case 3: /* mdat header */
+ fail_unless (gst_buffer_get_size (outbuffer) == 8);
+ fail_unless (gst_buffer_memcmp (outbuffer, 4, data1,
+ sizeof (data1)) == 0);
+ break;
+ case 4: /* buffer we put in */
+ fail_unless (gst_buffer_get_size (outbuffer) == 1);
+ break;
+ case 5: /* mfra */
+ fail_unless (gst_buffer_get_size (outbuffer) > 8);
+ fail_unless (gst_buffer_memcmp (outbuffer, 4, data4,
+ sizeof (data4)) == 0);
+ break;
+ default:
+ break;
+ }
+
+ ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
+ gst_buffer_unref (outbuffer);
+ outbuffer = NULL;
+ }
+
+ g_list_free (buffers);
+ buffers = NULL;
+}
+
+/* dts-method dd */
+
+GST_START_TEST (test_video_pad_dd)
+{
+ check_qtmux_pad (&srcvideotemplate, "video_%u", 0);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_audio_pad_dd)
+{
+ check_qtmux_pad (&srcaudiotemplate, "audio_%u", 0);
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_video_pad_frag_dd)
+{
+ check_qtmux_pad_fragmented (&srcvideotemplate, "video_%u", 0, FALSE);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_audio_pad_frag_dd)
+{
+ check_qtmux_pad_fragmented (&srcaudiotemplate, "audio_%u", 0, FALSE);
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_video_pad_frag_dd_streamable)
+{
+ check_qtmux_pad_fragmented (&srcvideotemplate, "video_%u", 0, TRUE);
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_audio_pad_frag_dd_streamable)
+{
+ check_qtmux_pad_fragmented (&srcaudiotemplate, "audio_%u", 0, TRUE);
+}
+
+GST_END_TEST;
+
+/* dts-method reorder */
+
+GST_START_TEST (test_video_pad_reorder)
+{
+ check_qtmux_pad (&srcvideotemplate, "video_%u", 1);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_audio_pad_reorder)
+{
+ check_qtmux_pad (&srcaudiotemplate, "audio_%u", 1);
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_video_pad_frag_reorder)
+{
+ check_qtmux_pad_fragmented (&srcvideotemplate, "video_%u", 1, FALSE);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_audio_pad_frag_reorder)
+{
+ check_qtmux_pad_fragmented (&srcaudiotemplate, "audio_%u", 1, FALSE);
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_video_pad_frag_reorder_streamable)
+{
+ check_qtmux_pad_fragmented (&srcvideotemplate, "video_%u", 1, TRUE);
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_audio_pad_frag_reorder_streamable)
+{
+ check_qtmux_pad_fragmented (&srcaudiotemplate, "audio_%u", 1, TRUE);
+}
+
+GST_END_TEST;
+
+/* dts-method asc */
+
+GST_START_TEST (test_video_pad_asc)
+{
+ check_qtmux_pad (&srcvideotemplate, "video_%u", 2);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_audio_pad_asc)
+{
+ check_qtmux_pad (&srcaudiotemplate, "audio_%u", 2);
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_video_pad_frag_asc)
+{
+ check_qtmux_pad_fragmented (&srcvideotemplate, "video_%u", 2, FALSE);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_audio_pad_frag_asc)
+{
+ check_qtmux_pad_fragmented (&srcaudiotemplate, "audio_%u", 2, FALSE);
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_video_pad_frag_asc_streamable)
+{
+ check_qtmux_pad_fragmented (&srcvideotemplate, "video_%u", 2, TRUE);
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_audio_pad_frag_asc_streamable)
+{
+ check_qtmux_pad_fragmented (&srcaudiotemplate, "audio_%u", 2, TRUE);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_reuse)
+{
+ GstElement *qtmux = setup_qtmux (&srcvideotemplate, "video_%u");
+ GstBuffer *inbuffer;
+ GstCaps *caps;
+ GstSegment segment;
+
+ gst_element_set_state (qtmux, GST_STATE_PLAYING);
+ gst_element_set_state (qtmux, GST_STATE_NULL);
+ gst_element_set_state (qtmux, GST_STATE_PLAYING);
+ gst_pad_set_active (mysrcpad, TRUE);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ gst_pad_push_event (mysrcpad, gst_event_new_stream_start ("test"));
+
+ caps = gst_pad_get_pad_template_caps (mysrcpad);
+ gst_pad_set_caps (mysrcpad, caps);
+ gst_caps_unref (caps);
+
+ /* ensure segment (format) properly setup */
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
+
+ inbuffer = gst_buffer_new_and_alloc (1);
+ fail_unless (inbuffer != NULL);
+ gst_buffer_memset (inbuffer, 0, 0, 1);
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ GST_BUFFER_DURATION (inbuffer) = 40 * GST_MSECOND;
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+
+ /* send eos to have all written */
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()) == TRUE);
+
+ cleanup_qtmux (qtmux, "video_%u");
+}
+
+GST_END_TEST;
+
+static GstEncodingContainerProfile *
+create_qtmux_profile (const gchar * variant)
+{
+ GstEncodingContainerProfile *cprof;
+ GstCaps *caps;
+
+ if (variant == NULL) {
+ caps = gst_caps_new_empty_simple ("video/quicktime");
+ } else {
+ caps = gst_caps_new_simple ("video/quicktime",
+ "variant", G_TYPE_STRING, variant, NULL);
+ }
+
+ cprof = gst_encoding_container_profile_new ("Name", "blah", caps, NULL);
+ gst_caps_unref (caps);
+
+ caps = gst_caps_new_simple ("audio/x-raw",
+ "format", G_TYPE_STRING, "S16BE",
+ "channels", G_TYPE_INT, 2, "rate", G_TYPE_INT, 44100, NULL);
+ gst_encoding_container_profile_add_profile (cprof,
+ GST_ENCODING_PROFILE (gst_encoding_audio_profile_new (caps, NULL, NULL,
+ 1)));
+ gst_caps_unref (caps);
+
+ return cprof;
+}
+
+GST_START_TEST (test_encodebin_qtmux)
+{
+ GstEncodingContainerProfile *cprof;
+ GstElement *enc;
+ GstPad *pad;
+
+ enc = gst_element_factory_make ("encodebin", NULL);
+ if (enc == NULL)
+ return;
+
+ /* Make sure encodebin finds a muxer for a profile with a variant field .. */
+ cprof = create_qtmux_profile ("apple");
+ g_object_set (enc, "profile", cprof, NULL);
+ gst_encoding_profile_unref (cprof);
+
+ /* should have created a pad after setting the profile */
+ pad = gst_element_get_static_pad (enc, "audio_0");
+ fail_unless (pad != NULL);
+ gst_object_unref (pad);
+ gst_object_unref (enc);
+
+ /* ... and for a profile without a variant field */
+ enc = gst_element_factory_make ("encodebin", NULL);
+ cprof = create_qtmux_profile (NULL);
+ g_object_set (enc, "profile", cprof, NULL);
+ gst_encoding_profile_unref (cprof);
+
+ /* should have created a pad after setting the profile */
+ pad = gst_element_get_static_pad (enc, "audio_0");
+ fail_unless (pad != NULL);
+ gst_object_unref (pad);
+ gst_object_unref (enc);
+}
+
+GST_END_TEST;
+
+/* Fake mp3 encoder for test */
+typedef GstElement TestMp3Enc;
+typedef GstElementClass TestMp3EncClass;
+
+static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/mpeg, mpegversion=1, layer=[1,3]")
+ );
+
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw")
+ );
+
+static GType test_mp3_enc_get_type (void);
+
+G_DEFINE_TYPE (TestMp3Enc, test_mp3_enc, GST_TYPE_ELEMENT);
+
+static void
+test_mp3_enc_class_init (TestMp3EncClass * klass)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&sink_template));
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&src_template));
+
+ gst_element_class_set_metadata (element_class, "MPEG1 Audio Encoder",
+ "Codec/Encoder/Audio", "Pretends to encode mp3", "Foo Bar <foo@bar.com>");
+}
+
+static void
+test_mp3_enc_init (TestMp3Enc * mp3enc)
+{
+ GstPad *pad;
+
+ pad = gst_pad_new_from_static_template (&sink_template, "sink");
+ gst_element_add_pad (mp3enc, pad);
+
+ pad = gst_pad_new_from_static_template (&src_template, "src");
+ gst_element_add_pad (mp3enc, pad);
+}
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ return gst_element_register (plugin, "testmp3enc", GST_RANK_NONE,
+ test_mp3_enc_get_type ());
+}
+
+static GstEncodingContainerProfile *
+create_mp4mux_profile (void)
+{
+ GstEncodingContainerProfile *cprof;
+ GstCaps *caps;
+
+ caps = gst_caps_new_simple ("video/quicktime",
+ "variant", G_TYPE_STRING, "iso", NULL);
+
+ cprof = gst_encoding_container_profile_new ("Name", "blah", caps, NULL);
+ gst_caps_unref (caps);
+
+ caps = gst_caps_new_simple ("audio/mpeg", "mpegversion", G_TYPE_INT, 1,
+ "layer", G_TYPE_INT, 3, "channels", G_TYPE_INT, 2, "rate", G_TYPE_INT,
+ 44100, NULL);
+ gst_encoding_container_profile_add_profile (cprof,
+ GST_ENCODING_PROFILE (gst_encoding_audio_profile_new (caps, NULL, NULL,
+ 1)));
+ gst_caps_unref (caps);
+
+ return cprof;
+}
+
+GST_START_TEST (test_encodebin_mp4mux)
+{
+ GstEncodingContainerProfile *cprof;
+ GstPluginFeature *feature;
+ GstElement *enc, *mux;
+ GstPad *pad;
+
+ /* need a fake mp3 encoder because mp4 only accepts encoded formats */
+ gst_plugin_register_static (GST_VERSION_MAJOR, GST_VERSION_MINOR,
+ "fakemp3enc", "fakemp3enc", plugin_init, VERSION, "LGPL",
+ "gst-plugins-good", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
+
+ feature = gst_registry_find_feature (gst_registry_get (), "testmp3enc",
+ GST_TYPE_ELEMENT_FACTORY);
+ gst_plugin_feature_set_rank (feature, GST_RANK_PRIMARY + 100);
+
+ enc = gst_element_factory_make ("encodebin", NULL);
+ if (enc == NULL)
+ return;
+
+ /* Make sure encodebin finds mp4mux even though qtmux outputs a superset */
+ cprof = create_mp4mux_profile ();
+ g_object_set (enc, "profile", cprof, NULL);
+ gst_encoding_profile_unref (cprof);
+
+ /* should have created a pad after setting the profile */
+ pad = gst_element_get_static_pad (enc, "audio_0");
+ fail_unless (pad != NULL);
+ gst_object_unref (pad);
+
+ mux = gst_bin_get_by_interface (GST_BIN (enc), GST_TYPE_TAG_SETTER);
+ fail_unless (mux != NULL);
+ {
+ GstElementFactory *f = gst_element_get_factory (mux);
+
+ /* make sure we got mp4mux for variant=iso */
+ GST_INFO ("muxer: %s", G_OBJECT_TYPE_NAME (mux));
+ fail_unless_equals_string (GST_OBJECT_NAME (f), "mp4mux");
+ }
+ gst_object_unref (mux);
+ gst_object_unref (enc);
+
+ gst_plugin_feature_set_rank (feature, GST_RANK_NONE);
+ gst_object_unref (feature);
+}
+
+GST_END_TEST;
+
+static gboolean
+extract_tags (const gchar * location, GstTagList ** taglist)
+{
+ gboolean ret = TRUE;
+ GstElement *src;
+ GstBus *bus;
+ GstElement *pipeline =
+ gst_parse_launch ("filesrc name=src ! qtdemux ! fakesink", NULL);
+
+ src = gst_bin_get_by_name (GST_BIN (pipeline), "src");
+ g_object_set (src, "location", location, NULL);
+
+ bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
+ fail_unless (gst_element_set_state (pipeline, GST_STATE_PLAYING)
+ != GST_STATE_CHANGE_FAILURE);
+
+ if (*taglist == NULL) {
+ *taglist = gst_tag_list_new_empty ();
+ }
+
+ while (1) {
+ GstMessage *msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
+ GST_MESSAGE_TAG | GST_MESSAGE_ERROR | GST_MESSAGE_EOS);
+
+ if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS) {
+ gst_message_unref (msg);
+ break;
+ } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
+ ret = FALSE;
+ gst_message_unref (msg);
+ break;
+ } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_TAG) {
+ GstTagList *tags;
+
+ gst_message_parse_tag (msg, &tags);
+ gst_tag_list_insert (*taglist, tags, GST_TAG_MERGE_REPLACE);
+ gst_tag_list_unref (tags);
+ }
+ gst_message_unref (msg);
+ }
+
+ gst_object_unref (bus);
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+ gst_object_unref (src);
+ gst_object_unref (pipeline);
+ return ret;
+}
+
+static void
+test_average_bitrate_custom (const gchar * elementname,
+ GstStaticPadTemplate * tmpl, const gchar * sinkpadname)
+{
+ gchar *location;
+ GstElement *qtmux;
+ GstElement *filesink;
+ GstBuffer *inbuffer;
+ GstCaps *caps;
+ int i;
+ gint bytes[] = { 16, 22, 12 };
+ gint64 durations[] = { GST_SECOND * 3, GST_SECOND * 5, GST_SECOND * 2 };
+ gint64 total_bytes = 0;
+ GstClockTime total_duration = 0;
+ GstSegment segment;
+
+ location = g_strdup_printf ("%s/%s-%d", g_get_tmp_dir (), "qtmuxtest",
+ g_random_int ());
+ GST_INFO ("Using location %s for bitrate test", location);
+ qtmux = gst_check_setup_element (elementname);
+ filesink = gst_element_factory_make ("filesink", NULL);
+ g_object_set (filesink, "location", location, NULL);
+ gst_element_link (qtmux, filesink);
+ mysrcpad = setup_src_pad (qtmux, tmpl, sinkpadname);
+ fail_unless (mysrcpad != NULL);
+ gst_pad_set_active (mysrcpad, TRUE);
+
+
+ fail_unless (gst_element_set_state (filesink,
+ GST_STATE_PLAYING) != GST_STATE_CHANGE_FAILURE,
+ "could not set filesink to playing");
+ fail_unless (gst_element_set_state (qtmux,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ gst_pad_push_event (mysrcpad, gst_event_new_stream_start ("test"));
+
+ caps = gst_pad_get_pad_template_caps (mysrcpad);
+ gst_pad_set_caps (mysrcpad, caps);
+ gst_caps_unref (caps);
+
+ /* ensure segment (format) properly setup */
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
+
+ for (i = 0; i < 3; i++) {
+ inbuffer = gst_buffer_new_and_alloc (bytes[i]);
+ gst_buffer_memset (inbuffer, 0, 0, bytes[i]);
+ GST_BUFFER_TIMESTAMP (inbuffer) = total_duration;
+ GST_BUFFER_DURATION (inbuffer) = (GstClockTime) durations[i];
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ total_bytes += gst_buffer_get_size (inbuffer);
+ total_duration += GST_BUFFER_DURATION (inbuffer);
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ }
+
+ /* send eos to have moov written */
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()) == TRUE);
+
+ gst_element_set_state (qtmux, GST_STATE_NULL);
+ gst_element_set_state (filesink, GST_STATE_NULL);
+
+ gst_pad_set_active (mysrcpad, FALSE);
+ teardown_src_pad (mysrcpad);
+ gst_object_unref (filesink);
+ gst_check_teardown_element (qtmux);
+
+ /* check the bitrate tag */
+ {
+ GstTagList *taglist = NULL;
+ guint bitrate = 0;
+ guint expected;
+
+ fail_unless (extract_tags (location, &taglist));
+
+ fail_unless (gst_tag_list_get_uint (taglist, GST_TAG_BITRATE, &bitrate));
+
+ expected =
+ (guint) gst_util_uint64_scale_round ((guint64) total_bytes,
+ (guint64) 8 * GST_SECOND, (guint64) total_duration);
+ fail_unless (bitrate == expected);
+ gst_tag_list_unref (taglist);
+ }
+
+ /* delete file */
+ g_unlink (location);
+ g_free (location);
+}
+
+GST_START_TEST (test_average_bitrate)
+{
+ test_average_bitrate_custom ("mp4mux", &srcaudioaactemplate, "audio_%u");
+ test_average_bitrate_custom ("mp4mux", &srcvideoh264template, "video_%u");
+
+ test_average_bitrate_custom ("qtmux", &srcaudioaactemplate, "audio_%u");
+ test_average_bitrate_custom ("qtmux", &srcvideoh264template, "video_%u");
+}
+
+GST_END_TEST;
+
+
+static Suite *
+qtmux_suite (void)
+{
+ Suite *s = suite_create ("qtmux");
+ TCase *tc_chain = tcase_create ("general");
+
+ /* avoid glib warnings when setting deprecated dts-method property */
+ g_setenv ("G_ENABLE_DIAGNOSTIC", "0", TRUE);
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_video_pad_dd);
+ tcase_add_test (tc_chain, test_audio_pad_dd);
+ tcase_add_test (tc_chain, test_video_pad_frag_dd);
+ tcase_add_test (tc_chain, test_audio_pad_frag_dd);
+ tcase_add_test (tc_chain, test_video_pad_frag_dd_streamable);
+ tcase_add_test (tc_chain, test_audio_pad_frag_dd_streamable);
+
+ tcase_add_test (tc_chain, test_video_pad_reorder);
+ tcase_add_test (tc_chain, test_audio_pad_reorder);
+ tcase_add_test (tc_chain, test_video_pad_frag_reorder);
+ tcase_add_test (tc_chain, test_audio_pad_frag_reorder);
+ tcase_add_test (tc_chain, test_video_pad_frag_reorder_streamable);
+ tcase_add_test (tc_chain, test_audio_pad_frag_reorder_streamable);
+
+ tcase_add_test (tc_chain, test_video_pad_asc);
+ tcase_add_test (tc_chain, test_audio_pad_asc);
+ tcase_add_test (tc_chain, test_video_pad_frag_asc);
+ tcase_add_test (tc_chain, test_audio_pad_frag_asc);
+ tcase_add_test (tc_chain, test_video_pad_frag_asc_streamable);
+ tcase_add_test (tc_chain, test_audio_pad_frag_asc_streamable);
+
+ tcase_add_test (tc_chain, test_average_bitrate);
+
+ tcase_add_test (tc_chain, test_reuse);
+ tcase_add_test (tc_chain, test_encodebin_qtmux);
+ tcase_add_test (tc_chain, test_encodebin_mp4mux);
+
+ return s;
+}
+
+GST_CHECK_MAIN (qtmux)
diff --git a/tests/check/elements/rganalysis.c b/tests/check/elements/rganalysis.c
new file mode 100644
index 0000000..1f6f6ee
--- /dev/null
+++ b/tests/check/elements/rganalysis.c
@@ -0,0 +1,1994 @@
+/* GStreamer ReplayGain analysis
+ *
+ * Copyright (C) 2006 Rene Stadler <mail@renestadler.de>
+ *
+ * rganalysis.c: Unit test for the rganalysis element
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+/* Some things to note about the RMS window length of the analysis algorithm and
+ * thus the implementation used in the element: Processing divides input data
+ * into 50ms windows at some point. Some details about this that normally do
+ * not matter:
+ *
+ * 1. At the end of a stream, the remainder of data that did not fill up the
+ * last 50ms window is simply discarded.
+ *
+ * 2. If the sample rate changes during a stream, the currently running window
+ * is discarded and the equal loudness filter gets reset as if a new stream
+ * started.
+ *
+ * 3. For the album gain, it is not entirely correct to think of obtaining it
+ * like "as if all the tracks are analyzed as one track". There isn't a
+ * separate window being tracked for album processing, so at stream (track)
+ * end, the remaining unfilled window does not contribute to the album gain
+ * either.
+ *
+ * 4. If a waveform with a result gain G is concatenated to itself and the
+ * result processed as a track, the gain can be different from G if and only
+ * if the duration of the original waveform is not an integer multiple of
+ * 50ms. If the original waveform gets processed as a single track and then
+ * the same data again as a subsequent track, the album result gain will
+ * always match G (this is implied by 3.).
+ *
+ * 5. A stream shorter than 50ms cannot be analyzed. At 8000 and 48000 Hz,
+ * this corresponds to 400 resp. 2400 frames. If a stream is shorter than
+ * 50ms, the element will not generate tags at EOS (only if an album
+ * finished, but only album tags are generated then). This is not an
+ * erroneous condition, the element should behave normally.
+ *
+ * The limitations outlined in 1.-4. do not apply to the peak values. Every
+ * single sample is accounted for when looking for the peak. Thus the album
+ * peak is guaranteed to be the maximum value of all track peaks.
+ *
+ * In normal day-to-day use, these little facts are unlikely to be relevant, but
+ * they have to be kept in mind for writing the tests here.
+ */
+
+#include <gst/check/gstcheck.h>
+#include <gst/audio/audio.h>
+
+/* 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;
+static GstBus *mybus;
+
+/* Mapping from supported sample rates to the correct result gain for the
+ * following test waveform: 20 * 512 samples with a quarter-full amplitude of
+ * toggling sign, changing every 48 samples and starting with the positive
+ * value.
+ *
+ * Even if we would generate a wave describing a signal with the same frequency
+ * at each sampling rate, the results would vary (slightly). Hence the simple
+ * generation method, since we cannot use a constant value as expected result
+ * anyways. For all sample rates, changing the sign every 48 frames gives a
+ * sane frequency. Buffers containing data that forms such a waveform is
+ * created using the test_buffer_square_{float,int16}_{mono,stereo} functions
+ * below.
+ *
+ * The results have been checked against what the metaflac and wavegain programs
+ * generate for such a stream. If you want to verify these, be sure that the
+ * metaflac program does not produce incorrect results in your environment: I
+ * found a strange bug in the (defacto) reference code for the analysis that
+ * sometimes leads to incorrect RMS window lengths. */
+
+struct rate_test
+{
+ guint sample_rate;
+ gdouble gain;
+};
+
+static const struct rate_test supported_rates[] = {
+ {8000, -0.91},
+ {11025, -2.80},
+ {12000, -3.13},
+ {16000, -4.26},
+ {22050, -5.64},
+ {24000, -5.87},
+ {32000, -6.03},
+ {44100, -6.20},
+ {48000, -6.14}
+};
+
+/* Lookup the correct gain adjustment result in above array. */
+
+static gdouble
+get_expected_gain (guint sample_rate)
+{
+ gint i;
+
+ for (i = G_N_ELEMENTS (supported_rates); i--;)
+ if (supported_rates[i].sample_rate == sample_rate)
+ return supported_rates[i].gain;
+ g_return_val_if_reached (0.0);
+}
+
+#define SILENCE_GAIN 64.82
+
+#define REPLAY_GAIN_CAPS \
+ "channels = (int) { 1, 2 }, " \
+ "rate = (int) { 8000, 11025, 12000, 16000, 22050, " \
+ "24000, 32000, 44100, 48000 }"
+
+#define RG_ANALYSIS_CAPS_TEMPLATE_STRING \
+ "audio/x-raw, " \
+ "format = (string) "GST_AUDIO_NE (F32) ", " \
+ "layout = (string) interleaved, " \
+ REPLAY_GAIN_CAPS \
+ "; " \
+ "audio/x-raw, " \
+ "format = (string) "GST_AUDIO_NE (S16) ", " \
+ "layout = (string) interleaved, " \
+ REPLAY_GAIN_CAPS
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (RG_ANALYSIS_CAPS_TEMPLATE_STRING)
+ );
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (RG_ANALYSIS_CAPS_TEMPLATE_STRING)
+ );
+
+static gboolean
+mysink_event_func (GstPad * pad, GstObject * parent, GstEvent * event)
+{
+ GST_LOG_OBJECT (pad, "%s event: %" GST_PTR_FORMAT,
+ GST_EVENT_TYPE_NAME (event), event);
+
+ /* a sink would post tag events as messages, so do the same here,
+ * esp. since we're polling on the bus waiting for TAG messages.. */
+ if (GST_EVENT_TYPE (event) == GST_EVENT_TAG) {
+ GstTagList *taglist;
+
+ gst_event_parse_tag (event, &taglist);
+
+ gst_bus_post (mybus, gst_message_new_tag (GST_OBJECT (mysinkpad),
+ gst_tag_list_copy (taglist)));
+ }
+
+ gst_event_unref (event);
+ return TRUE;
+}
+
+static GstElement *
+setup_rganalysis (void)
+{
+ GstElement *analysis;
+ GstBus *bus;
+
+ GST_DEBUG ("setup_rganalysis");
+ analysis = gst_check_setup_element ("rganalysis");
+ mysrcpad = gst_check_setup_src_pad (analysis, &srctemplate);
+ mysinkpad = gst_check_setup_sink_pad (analysis, &sinktemplate);
+ gst_pad_set_event_function (mysinkpad, mysink_event_func);
+ gst_pad_set_active (mysrcpad, TRUE);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ bus = gst_bus_new ();
+ gst_element_set_bus (analysis, bus);
+
+ mybus = bus; /* keep ref */
+
+ return analysis;
+}
+
+static void
+cleanup_rganalysis (GstElement * element)
+{
+ GST_DEBUG ("cleanup_rganalysis");
+
+ g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
+ g_list_free (buffers);
+ buffers = NULL;
+
+ gst_object_unref (mybus);
+ mybus = NULL;
+
+ /* The bus owns references to the element: */
+ gst_element_set_bus (element, NULL);
+
+ gst_pad_set_active (mysrcpad, FALSE);
+ gst_pad_set_active (mysinkpad, FALSE);
+ gst_check_teardown_src_pad (element);
+ gst_check_teardown_sink_pad (element);
+ gst_check_teardown_element (element);
+}
+
+static void
+set_playing_state (GstElement * element)
+{
+ fail_unless (gst_element_set_state (element,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "Could not set state to PLAYING");
+}
+
+static void
+send_flush_events (GstElement * element)
+{
+ gboolean res;
+ GstPad *pad;
+
+ pad = mysrcpad;
+ res = gst_pad_push_event (pad, gst_event_new_flush_start ());
+ fail_unless (res, "flush-start even not handledt");
+ res = gst_pad_push_event (pad, gst_event_new_flush_stop (TRUE));
+ fail_unless (res, "flush-stop event not handled");
+}
+
+static void
+send_stream_start_event (GstElement * element)
+{
+ gboolean res;
+ GstPad *pad;
+
+ pad = mysrcpad;
+ res = gst_pad_push_event (pad, gst_event_new_stream_start ("test"));
+ fail_unless (res, "STREAM_START event not handled");
+}
+
+static void
+send_caps_event (const gchar * format, gint sample_rate, gint channels)
+{
+ GstCaps *caps;
+
+ caps = gst_caps_new_simple ("audio/x-raw",
+ "format", G_TYPE_STRING, format,
+ "rate", G_TYPE_INT, sample_rate, "channels", G_TYPE_INT, channels,
+ "layout", G_TYPE_STRING, "interleaved", NULL);
+ if (channels == 2) {
+ gst_caps_set_simple (caps,
+ "channel-mask", GST_TYPE_BITMASK,
+ G_GUINT64_CONSTANT (0x0000000000000003), NULL);
+ }
+ gst_pad_set_caps (mysrcpad, caps);
+ gst_caps_unref (caps);
+}
+
+static void
+send_segment_event (GstElement * element)
+{
+ GstSegment segment;
+ gboolean res;
+ GstPad *pad;
+
+ pad = mysrcpad;
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ res = gst_pad_push_event (pad, gst_event_new_segment (&segment));
+ fail_unless (res, "SEGMENT event not handled");
+}
+
+static void
+send_eos_event (GstElement * element)
+{
+ GstBus *bus = gst_element_get_bus (element);
+ GstPad *pad = mysrcpad;
+ gboolean res;
+
+ res = gst_pad_push_event (pad, gst_event_new_eos ());
+ fail_unless (res, "EOS event not handled");
+
+ /* There is no sink element, so _we_ post the EOS message on the bus here. Of
+ * course we generate any EOS ourselves, but this allows us to poll for the
+ * EOS message in poll_eos if we expect the element to _not_ generate a TAG
+ * message. That's better than waiting for a timeout to lapse. */
+ fail_unless (gst_bus_post (bus, gst_message_new_eos (NULL)));
+
+ gst_object_unref (bus);
+}
+
+static void
+send_tag_event (GstElement * element, GstTagList * tag_list)
+{
+ GstPad *pad = mysrcpad;
+ GstEvent *event = gst_event_new_tag (tag_list);
+
+ fail_unless (gst_pad_push_event (pad, event),
+ "Cannot send TAG event: Not handled.");
+}
+
+static void
+poll_eos (GstElement * element)
+{
+ GstBus *bus = gst_element_get_bus (element);
+ GstMessage *message;
+
+ message = gst_bus_poll (bus, GST_MESSAGE_EOS | GST_MESSAGE_TAG, GST_SECOND);
+ fail_unless (message != NULL, "Could not poll for EOS message: Timed out");
+ fail_unless (message->type == GST_MESSAGE_EOS,
+ "Could not poll for eos message: got message of type %s instead",
+ gst_message_type_get_name (message->type));
+
+ gst_message_unref (message);
+ gst_object_unref (bus);
+}
+
+static GstTagList *
+poll_tags_only (GstElement * element)
+{
+ GstBus *bus = gst_element_get_bus (element);
+ GstTagList *tag_list;
+ GstMessage *message;
+
+ message = gst_bus_poll (bus, GST_MESSAGE_TAG, GST_SECOND);
+ fail_unless (message != NULL, "Could not poll for TAG message: Timed out");
+
+ gst_message_parse_tag (message, &tag_list);
+ gst_message_unref (message);
+ gst_object_unref (bus);
+
+ return tag_list;
+}
+
+/* This also polls for EOS since the TAG message comes right before the end of
+ * streams. */
+
+static GstTagList *
+poll_tags_followed_by_eos (GstElement * element)
+{
+ GstTagList *tag_list = poll_tags_only (element);
+
+ poll_eos (element);
+
+ return tag_list;
+}
+
+#define MATCH_PEAK(p1, p2) ((p1 < p2 + 1e-6) && (p2 < p1 + 1e-6))
+#define MATCH_GAIN(g1, g2) ((g1 < g2 + 1e-13) && (g2 < g1 + 1e-13))
+
+static void
+fail_unless_track_gain (const GstTagList * tag_list, gdouble gain)
+{
+ gdouble result;
+
+ fail_unless (gst_tag_list_get_double (tag_list, GST_TAG_TRACK_GAIN, &result),
+ "Tag list contains no track gain value");
+ fail_unless (MATCH_GAIN (gain, result),
+ "Track gain %+.2f does not match, expected %+.2f", result, gain);
+}
+
+static void
+fail_unless_track_peak (const GstTagList * tag_list, gdouble peak)
+{
+ gdouble result;
+
+ fail_unless (gst_tag_list_get_double (tag_list, GST_TAG_TRACK_PEAK, &result),
+ "Tag list contains no track peak value");
+ fail_unless (MATCH_PEAK (peak, result),
+ "Track peak %f does not match, expected %f", result, peak);
+}
+
+static void
+fail_unless_album_gain (const GstTagList * tag_list, gdouble gain)
+{
+ gdouble result;
+
+ fail_unless (gst_tag_list_get_double (tag_list, GST_TAG_ALBUM_GAIN, &result),
+ "Tag list contains no album gain value");
+ fail_unless (MATCH_GAIN (result, gain),
+ "Album gain %+.2f does not match, expected %+.2f", result, gain);
+}
+
+static void
+fail_unless_album_peak (const GstTagList * tag_list, gdouble peak)
+{
+ gdouble result;
+
+ fail_unless (gst_tag_list_get_double (tag_list, GST_TAG_ALBUM_PEAK, &result),
+ "Tag list contains no album peak value");
+ fail_unless (MATCH_PEAK (peak, result),
+ "Album peak %f does not match, expected %f", result, peak);
+}
+
+static void
+fail_if_track_tags (const GstTagList * tag_list)
+{
+ gdouble result;
+
+ fail_if (gst_tag_list_get_double (tag_list, GST_TAG_TRACK_GAIN, &result),
+ "Tag list contains track gain value (but should not)");
+ fail_if (gst_tag_list_get_double (tag_list, GST_TAG_TRACK_PEAK, &result),
+ "Tag list contains track peak value (but should not)");
+}
+
+static void
+fail_if_album_tags (const GstTagList * tag_list)
+{
+ gdouble result;
+
+ fail_if (gst_tag_list_get_double (tag_list, GST_TAG_ALBUM_GAIN, &result),
+ "Tag list contains album gain value (but should not)");
+ fail_if (gst_tag_list_get_double (tag_list, GST_TAG_ALBUM_PEAK, &result),
+ "Tag list contains album peak value (but should not)");
+}
+
+static void
+fail_unless_num_tracks (GstElement * element, guint num_tracks)
+{
+ guint current;
+
+ g_object_get (element, "num-tracks", &current, NULL);
+ fail_unless (current == num_tracks,
+ "num-tracks property has incorrect value %u, expected %u",
+ current, num_tracks);
+}
+
+/* Functions that create buffers with constant sample values, for peak
+ * tests. */
+
+static GstBuffer *
+test_buffer_const_float_mono (gint sample_rate, gsize n_frames, gfloat value)
+{
+ GstBuffer *buf = gst_buffer_new_and_alloc (n_frames * sizeof (gfloat));
+ GstMapInfo map;
+ gfloat *data;
+ gint i;
+
+ gst_buffer_map (buf, &map, GST_MAP_WRITE);
+ data = (gfloat *) map.data;
+ for (i = n_frames; i--;)
+ *data++ = value;
+ gst_buffer_unmap (buf, &map);
+
+ ASSERT_BUFFER_REFCOUNT (buf, "buf", 1);
+
+ return buf;
+}
+
+static GstBuffer *
+test_buffer_const_float_stereo (gint sample_rate, gsize n_frames,
+ gfloat value_l, gfloat value_r)
+{
+ GstBuffer *buf = gst_buffer_new_and_alloc (n_frames * sizeof (gfloat) * 2);
+ GstMapInfo map;
+ gfloat *data;
+ gint i;
+
+ gst_buffer_map (buf, &map, GST_MAP_WRITE);
+ data = (gfloat *) map.data;
+ for (i = n_frames; i--;) {
+ *data++ = value_l;
+ *data++ = value_r;
+ }
+ gst_buffer_unmap (buf, &map);
+
+ ASSERT_BUFFER_REFCOUNT (buf, "buf", 1);
+
+ return buf;
+}
+
+static GstBuffer *
+test_buffer_const_int16_mono (gint sample_rate, gint depth, gsize n_frames,
+ gint16 value)
+{
+ GstBuffer *buf = gst_buffer_new_and_alloc (n_frames * sizeof (gint16));
+ gint16 *data;
+ GstMapInfo map;
+ gint i;
+
+ gst_buffer_map (buf, &map, GST_MAP_WRITE);
+ data = (gint16 *) map.data;
+ for (i = n_frames; i--;)
+ *data++ = value;
+ gst_buffer_unmap (buf, &map);
+
+ ASSERT_BUFFER_REFCOUNT (buf, "buf", 1);
+
+ return buf;
+}
+
+static GstBuffer *
+test_buffer_const_int16_stereo (gint sample_rate, gint depth, gsize n_frames,
+ gint16 value_l, gint16 value_r)
+{
+ GstBuffer *buf = gst_buffer_new_and_alloc (n_frames * sizeof (gint16) * 2);
+ gint16 *data;
+ GstMapInfo map;
+ gint i;
+
+ gst_buffer_map (buf, &map, GST_MAP_WRITE);
+ data = (gint16 *) map.data;
+ for (i = n_frames; i--;) {
+ *data++ = value_l;
+ *data++ = value_r;
+ }
+ gst_buffer_unmap (buf, &map);
+
+ ASSERT_BUFFER_REFCOUNT (buf, "buf", 1);
+
+ return buf;
+}
+
+/* Functions that create data buffers containing square signal
+ * waveforms. */
+
+static GstBuffer *
+test_buffer_square_float_mono (gint * accumulator, gint sample_rate,
+ gsize n_frames, gfloat value)
+{
+ GstBuffer *buf = gst_buffer_new_and_alloc (n_frames * sizeof (gfloat));
+ gfloat *data;
+ GstMapInfo map;
+ gint i;
+
+ gst_buffer_map (buf, &map, GST_MAP_WRITE);
+ data = (gfloat *) map.data;
+ for (i = n_frames; i--;) {
+ *accumulator += 1;
+ *accumulator %= 96;
+
+ if (*accumulator < 48)
+ *data++ = value;
+ else
+ *data++ = -value;
+ }
+ gst_buffer_unmap (buf, &map);
+
+ ASSERT_BUFFER_REFCOUNT (buf, "buf", 1);
+
+ return buf;
+}
+
+static GstBuffer *
+test_buffer_square_float_stereo (gint * accumulator, gint sample_rate,
+ gsize n_frames, gfloat value_l, gfloat value_r)
+{
+ GstBuffer *buf = gst_buffer_new_and_alloc (n_frames * sizeof (gfloat) * 2);
+ gfloat *data;
+ GstMapInfo map;
+ gint i;
+
+ gst_buffer_map (buf, &map, GST_MAP_WRITE);
+ data = (gfloat *) map.data;
+ for (i = n_frames; i--;) {
+ *accumulator += 1;
+ *accumulator %= 96;
+
+ if (*accumulator < 48) {
+ *data++ = value_l;
+ *data++ = value_r;
+ } else {
+ *data++ = -value_l;
+ *data++ = -value_r;
+ }
+ }
+ gst_buffer_unmap (buf, &map);
+
+ ASSERT_BUFFER_REFCOUNT (buf, "buf", 1);
+
+ return buf;
+}
+
+static GstBuffer *
+test_buffer_square_int16_mono (gint * accumulator, gint sample_rate,
+ gint depth, gsize n_frames, gint16 value)
+{
+ GstBuffer *buf = gst_buffer_new_and_alloc (n_frames * sizeof (gint16));
+ gint16 *data;
+ GstMapInfo map;
+ gint i;
+
+ gst_buffer_map (buf, &map, GST_MAP_WRITE);
+ data = (gint16 *) map.data;
+ for (i = n_frames; i--;) {
+ *accumulator += 1;
+ *accumulator %= 96;
+
+ if (*accumulator < 48)
+ *data++ = value;
+ else
+ *data++ = -MAX (value, -32767);
+ }
+ gst_buffer_unmap (buf, &map);
+
+ ASSERT_BUFFER_REFCOUNT (buf, "buf", 1);
+
+ return buf;
+}
+
+static GstBuffer *
+test_buffer_square_int16_stereo (gint * accumulator, gint sample_rate,
+ gint depth, gsize n_frames, gint16 value_l, gint16 value_r)
+{
+ GstBuffer *buf = gst_buffer_new_and_alloc (n_frames * sizeof (gint16) * 2);
+ gint16 *data;
+ GstMapInfo map;
+ gint i;
+
+ gst_buffer_map (buf, &map, GST_MAP_WRITE);
+ data = (gint16 *) map.data;
+ for (i = n_frames; i--;) {
+ *accumulator += 1;
+ *accumulator %= 96;
+
+ if (*accumulator < 48) {
+ *data++ = value_l;
+ *data++ = value_r;
+ } else {
+ *data++ = -MAX (value_l, -32767);
+ *data++ = -MAX (value_r, -32767);
+ }
+ }
+ gst_buffer_unmap (buf, &map);
+
+ ASSERT_BUFFER_REFCOUNT (buf, "buf", 1);
+
+ return buf;
+}
+
+static void
+push_buffer (GstBuffer * buf)
+{
+ /* gst_pad_push steals a reference. */
+ fail_unless (gst_pad_push (mysrcpad, buf) == GST_FLOW_OK);
+ ASSERT_BUFFER_REFCOUNT (buf, "buf", 1);
+}
+
+/*** Start of the tests. ***/
+
+/* This test looks redundant, but early versions of the element
+ * crashed when doing, well, nothing: */
+
+GST_START_TEST (test_no_buffer)
+{
+ GstElement *element = setup_rganalysis ();
+
+ set_playing_state (element);
+ send_eos_event (element);
+ poll_eos (element);
+
+ cleanup_rganalysis (element);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_no_buffer_album_1)
+{
+ GstElement *element = setup_rganalysis ();
+
+ set_playing_state (element);
+
+ /* Single track: */
+ send_stream_start_event (element);
+ send_segment_event (element);
+ send_eos_event (element);
+ poll_eos (element);
+
+ /* First album: */
+ g_object_set (element, "num-tracks", 3, NULL);
+
+ send_flush_events (element);
+ send_segment_event (element);
+ send_eos_event (element);
+ poll_eos (element);
+ fail_unless_num_tracks (element, 2);
+
+ send_flush_events (element);
+ send_segment_event (element);
+ send_eos_event (element);
+ poll_eos (element);
+ fail_unless_num_tracks (element, 1);
+
+ send_flush_events (element);
+ send_segment_event (element);
+ send_eos_event (element);
+ poll_eos (element);
+ fail_unless_num_tracks (element, 0);
+
+ /* Second album: */
+ g_object_set (element, "num-tracks", 2, NULL);
+
+ send_flush_events (element);
+ send_segment_event (element);
+ send_eos_event (element);
+ poll_eos (element);
+ fail_unless_num_tracks (element, 1);
+
+ send_flush_events (element);
+ send_segment_event (element);
+ send_eos_event (element);
+ poll_eos (element);
+ fail_unless_num_tracks (element, 0);
+
+ /* Single track: */
+ send_flush_events (element);
+ send_segment_event (element);
+ send_eos_event (element);
+ poll_eos (element);
+ fail_unless_num_tracks (element, 0);
+
+ cleanup_rganalysis (element);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_no_buffer_album_2)
+{
+ GstElement *element = setup_rganalysis ();
+ GstTagList *tag_list;
+ gint accumulator = 0;
+ gint i;
+
+ g_object_set (element, "num-tracks", 3, NULL);
+ set_playing_state (element);
+
+ /* No buffer for the first track. */
+ send_stream_start_event (element);
+ send_segment_event (element);
+ send_eos_event (element);
+ /* No tags should be posted, there was nothing to analyze: */
+ poll_eos (element);
+ fail_unless_num_tracks (element, 2);
+
+ /* A test waveform with known gain result as second track: */
+
+ send_flush_events (element);
+ send_caps_event (GST_AUDIO_NE (F32), 44100, 1);
+ send_segment_event (element);
+ for (i = 20; i--;)
+ push_buffer (test_buffer_square_float_mono (&accumulator, 44100, 512,
+ 0.25));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.25);
+ fail_unless_track_gain (tag_list, -6.20);
+ /* Album is not finished yet: */
+ fail_if_album_tags (tag_list);
+ gst_tag_list_unref (tag_list);
+ fail_unless_num_tracks (element, 1);
+
+ /* No buffer for the last track. */
+
+ send_flush_events (element);
+ send_segment_event (element);
+ send_eos_event (element);
+
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_album_peak (tag_list, 0.25);
+ fail_unless_album_gain (tag_list, -6.20);
+ /* No track tags should be posted, as there was no data for it: */
+ fail_if_track_tags (tag_list);
+ gst_tag_list_unref (tag_list);
+ fail_unless_num_tracks (element, 0);
+
+ cleanup_rganalysis (element);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_empty_buffers)
+{
+ GstElement *element = setup_rganalysis ();
+
+ set_playing_state (element);
+
+ /* Single track: */
+ send_stream_start_event (element);
+ send_caps_event (GST_AUDIO_NE (F32), 44100, 2);
+ send_segment_event (element);
+ push_buffer (test_buffer_const_float_stereo (44100, 0, 0.0, 0.0));
+ send_eos_event (element);
+ poll_eos (element);
+
+ /* First album: */
+ g_object_set (element, "num-tracks", 2, NULL);
+
+ send_flush_events (element);
+ send_segment_event (element);
+ push_buffer (test_buffer_const_float_stereo (44100, 0, 0.0, 0.0));
+ send_eos_event (element);
+ poll_eos (element);
+ fail_unless_num_tracks (element, 1);
+
+ send_flush_events (element);
+ send_segment_event (element);
+ push_buffer (test_buffer_const_float_stereo (44100, 0, 0.0, 0.0));
+ send_eos_event (element);
+ poll_eos (element);
+ fail_unless_num_tracks (element, 0);
+
+ /* Second album, with a single track: */
+ g_object_set (element, "num-tracks", 1, NULL);
+ send_flush_events (element);
+ send_segment_event (element);
+ push_buffer (test_buffer_const_float_stereo (44100, 0, 0.0, 0.0));
+ send_eos_event (element);
+ poll_eos (element);
+ fail_unless_num_tracks (element, 0);
+
+ /* Single track: */
+ send_flush_events (element);
+ send_segment_event (element);
+ push_buffer (test_buffer_const_float_stereo (44100, 0, 0.0, 0.0));
+ send_eos_event (element);
+ poll_eos (element);
+
+ cleanup_rganalysis (element);
+}
+
+GST_END_TEST;
+
+/* Tests for correctness of the peak values. */
+
+/* Float peak test. For stereo, one channel has the constant value of -1.369,
+ * the other one 0.0. This tests many things: The result peak value should
+ * occur on any channel. The peak is of course the absolute amplitude, so 1.369
+ * should be the result. This will also detect if the code uses the absolute
+ * value during the comparison. If it is buggy it will return 0.0 since 0.0 >
+ * -1.369. Furthermore, this makes sure that there is no problem with headroom
+ * (exceeding 0dBFS). In the wild you get float samples > 1.0 from stuff like
+ * vorbis. */
+
+GST_START_TEST (test_peak_float)
+{
+ GstElement *element = setup_rganalysis ();
+ GstTagList *tag_list;
+
+ set_playing_state (element);
+ send_stream_start_event (element);
+ send_caps_event (GST_AUDIO_NE (F32), 8000, 2);
+ send_segment_event (element);
+ push_buffer (test_buffer_const_float_stereo (8000, 512, -1.369, 0.0));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 1.369);
+ gst_tag_list_unref (tag_list);
+
+ /* Swapped channels. */
+ send_flush_events (element);
+ send_segment_event (element);
+ push_buffer (test_buffer_const_float_stereo (8000, 512, 0.0, -1.369));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 1.369);
+ gst_tag_list_unref (tag_list);
+
+ /* Mono. */
+ send_flush_events (element);
+ send_caps_event (GST_AUDIO_NE (F32), 8000, 1);
+ send_segment_event (element);
+ push_buffer (test_buffer_const_float_mono (8000, 512, -1.369));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 1.369);
+ gst_tag_list_unref (tag_list);
+
+ cleanup_rganalysis (element);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_peak_int16_16)
+{
+ GstElement *element = setup_rganalysis ();
+ GstTagList *tag_list;
+
+ set_playing_state (element);
+
+ /* Half amplitude. */
+ send_stream_start_event (element);
+ send_caps_event (GST_AUDIO_NE (S16), 8000, 2);
+ send_segment_event (element);
+ push_buffer (test_buffer_const_int16_stereo (8000, 16, 512, 1 << 14, 0));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.5);
+ gst_tag_list_unref (tag_list);
+
+ /* Swapped channels. */
+ send_flush_events (element);
+ send_caps_event (GST_AUDIO_NE (S16), 8000, 2);
+ send_segment_event (element);
+ push_buffer (test_buffer_const_int16_stereo (8000, 16, 512, 0, 1 << 14));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.5);
+ gst_tag_list_unref (tag_list);
+
+ /* Mono. */
+ send_flush_events (element);
+ send_caps_event (GST_AUDIO_NE (S16), 8000, 1);
+ send_segment_event (element);
+ push_buffer (test_buffer_const_int16_mono (8000, 16, 512, 1 << 14));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.5);
+ gst_tag_list_unref (tag_list);
+
+ /* Half amplitude, negative variant. */
+ send_flush_events (element);
+ send_caps_event (GST_AUDIO_NE (S16), 8000, 2);
+ send_segment_event (element);
+ push_buffer (test_buffer_const_int16_stereo (8000, 16, 512, -1 << 14, 0));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.5);
+ gst_tag_list_unref (tag_list);
+
+ /* Swapped channels. */
+ send_flush_events (element);
+ send_caps_event (GST_AUDIO_NE (S16), 8000, 2);
+ send_segment_event (element);
+ push_buffer (test_buffer_const_int16_stereo (8000, 16, 512, 0, -1 << 14));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.5);
+ gst_tag_list_unref (tag_list);
+
+ /* Mono. */
+ send_flush_events (element);
+ send_caps_event (GST_AUDIO_NE (S16), 8000, 1);
+ send_segment_event (element);
+ push_buffer (test_buffer_const_int16_mono (8000, 16, 512, -1 << 14));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.5);
+ gst_tag_list_unref (tag_list);
+
+
+ /* Now check for correct normalization of the peak value: Sample
+ * values of this format range from -32768 to 32767. So for the
+ * highest positive amplitude we do not reach 1.0, only for
+ * -32768! */
+
+ send_flush_events (element);
+ send_caps_event (GST_AUDIO_NE (S16), 8000, 2);
+ send_segment_event (element);
+ push_buffer (test_buffer_const_int16_stereo (8000, 16, 512, 32767, 0));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 32767. / 32768.);
+ gst_tag_list_unref (tag_list);
+
+ /* Swapped channels. */
+ send_flush_events (element);
+ send_caps_event (GST_AUDIO_NE (S16), 8000, 2);
+ send_segment_event (element);
+ push_buffer (test_buffer_const_int16_stereo (8000, 16, 512, 0, 32767));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 32767. / 32768.);
+ gst_tag_list_unref (tag_list);
+
+ /* Mono. */
+ send_flush_events (element);
+ send_caps_event (GST_AUDIO_NE (S16), 8000, 1);
+ send_segment_event (element);
+ push_buffer (test_buffer_const_int16_mono (8000, 16, 512, 32767));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 32767. / 32768.);
+ gst_tag_list_unref (tag_list);
+
+
+ /* Negative variant, reaching 1.0. */
+ send_flush_events (element);
+ send_caps_event (GST_AUDIO_NE (S16), 8000, 2);
+ send_segment_event (element);
+ push_buffer (test_buffer_const_int16_stereo (8000, 16, 512, -32768, 0));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 1.0);
+ gst_tag_list_unref (tag_list);
+
+ /* Swapped channels. */
+ send_flush_events (element);
+ send_caps_event (GST_AUDIO_NE (S16), 8000, 2);
+ send_segment_event (element);
+ push_buffer (test_buffer_const_int16_stereo (8000, 16, 512, 0, -32768));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 1.0);
+ gst_tag_list_unref (tag_list);
+
+ /* Mono. */
+ send_flush_events (element);
+ send_caps_event (GST_AUDIO_NE (S16), 8000, 1);
+ send_segment_event (element);
+ push_buffer (test_buffer_const_int16_mono (8000, 16, 512, -32768));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 1.0);
+ gst_tag_list_unref (tag_list);
+
+ cleanup_rganalysis (element);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_peak_album)
+{
+ GstElement *element = setup_rganalysis ();
+ GstTagList *tag_list;
+
+ g_object_set (element, "num-tracks", 2, NULL);
+ set_playing_state (element);
+
+ send_stream_start_event (element);
+ send_caps_event (GST_AUDIO_NE (F32), 8000, 2);
+ send_segment_event (element);
+ push_buffer (test_buffer_const_float_stereo (8000, 1024, 1.0, 0.0));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 1.0);
+ fail_if_album_tags (tag_list);
+ gst_tag_list_unref (tag_list);
+ fail_unless_num_tracks (element, 1);
+
+ send_flush_events (element);
+ send_segment_event (element);
+ push_buffer (test_buffer_const_float_stereo (8000, 1024, 0.0, 0.5));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.5);
+ fail_unless_album_peak (tag_list, 1.0);
+ gst_tag_list_unref (tag_list);
+ fail_unless_num_tracks (element, 0);
+
+ /* Try a second album: */
+ g_object_set (element, "num-tracks", 3, NULL);
+
+ send_flush_events (element);
+ send_segment_event (element);
+ push_buffer (test_buffer_const_float_stereo (8000, 1024, 0.4, 0.4));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.4);
+ fail_if_album_tags (tag_list);
+ gst_tag_list_unref (tag_list);
+ fail_unless_num_tracks (element, 2);
+
+ send_flush_events (element);
+ send_segment_event (element);
+ push_buffer (test_buffer_const_float_stereo (8000, 1024, 0.45, 0.45));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.45);
+ fail_if_album_tags (tag_list);
+ gst_tag_list_unref (tag_list);
+ fail_unless_num_tracks (element, 1);
+
+ send_flush_events (element);
+ send_segment_event (element);
+ push_buffer (test_buffer_const_float_stereo (8000, 1024, 0.2, 0.2));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.2);
+ fail_unless_album_peak (tag_list, 0.45);
+ gst_tag_list_unref (tag_list);
+ fail_unless_num_tracks (element, 0);
+
+ /* And now a single track, not in album mode (num-tracks is 0
+ * now): */
+ send_flush_events (element);
+ send_segment_event (element);
+ push_buffer (test_buffer_const_float_stereo (8000, 1024, 0.1, 0.1));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.1);
+ fail_if_album_tags (tag_list);
+ gst_tag_list_unref (tag_list);
+
+ cleanup_rganalysis (element);
+}
+
+GST_END_TEST;
+
+/* Switching from track to album mode. */
+
+GST_START_TEST (test_peak_track_album)
+{
+ GstElement *element = setup_rganalysis ();
+ GstTagList *tag_list;
+
+ set_playing_state (element);
+
+ send_stream_start_event (element);
+ send_caps_event (GST_AUDIO_NE (F32), 8000, 1);
+ send_segment_event (element);
+ push_buffer (test_buffer_const_float_mono (8000, 1024, 1.0));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 1.0);
+ fail_if_album_tags (tag_list);
+ gst_tag_list_unref (tag_list);
+
+ g_object_set (element, "num-tracks", 1, NULL);
+ send_flush_events (element);
+ send_segment_event (element);
+ push_buffer (test_buffer_const_float_mono (8000, 1024, 0.5));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.5);
+ fail_unless_album_peak (tag_list, 0.5);
+ gst_tag_list_unref (tag_list);
+ fail_unless_num_tracks (element, 0);
+
+ cleanup_rganalysis (element);
+}
+
+GST_END_TEST;
+
+/* Disabling album processing before the end of the album. Probably a rare edge
+ * case and applications should not rely on this to work. They need to send the
+ * element to the READY state to clear up after an aborted album anyway since
+ * they might need to process another album afterwards. */
+
+GST_START_TEST (test_peak_album_abort_to_track)
+{
+ GstElement *element = setup_rganalysis ();
+ GstTagList *tag_list;
+
+ g_object_set (element, "num-tracks", 2, NULL);
+ set_playing_state (element);
+
+ send_stream_start_event (element);
+ send_caps_event (GST_AUDIO_NE (F32), 8000, 2);
+ send_segment_event (element);
+ push_buffer (test_buffer_const_float_stereo (8000, 1024, 1.0, 0.0));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 1.0);
+ fail_if_album_tags (tag_list);
+ gst_tag_list_unref (tag_list);
+ fail_unless_num_tracks (element, 1);
+
+ g_object_set (element, "num-tracks", 0, NULL);
+
+ send_flush_events (element);
+ send_segment_event (element);
+ push_buffer (test_buffer_const_float_stereo (8000, 1024, 0.0, 0.5));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.5);
+ fail_if_album_tags (tag_list);
+ gst_tag_list_unref (tag_list);
+
+ cleanup_rganalysis (element);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_gain_album)
+{
+ GstElement *element = setup_rganalysis ();
+ GstTagList *tag_list;
+ gint accumulator;
+ gint i;
+
+ g_object_set (element, "num-tracks", 3, NULL);
+ set_playing_state (element);
+
+ /* The three tracks are constructed such that if any of these is in fact
+ * ignored for the album gain, the album gain will differ. */
+ send_stream_start_event (element);
+ send_caps_event (GST_AUDIO_NE (F32), 44100, 2);
+ send_segment_event (element);
+ accumulator = 0;
+ for (i = 8; i--;)
+ push_buffer (test_buffer_square_float_stereo (&accumulator, 44100, 512,
+ 0.75, 0.75));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.75);
+ fail_unless_track_gain (tag_list, -15.70);
+ fail_if_album_tags (tag_list);
+ gst_tag_list_unref (tag_list);
+
+ send_flush_events (element);
+ send_segment_event (element);
+ accumulator = 0;
+ for (i = 12; i--;)
+ push_buffer (test_buffer_square_float_stereo (&accumulator, 44100, 512,
+ 0.5, 0.5));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.5);
+ fail_unless_track_gain (tag_list, -12.22);
+ fail_if_album_tags (tag_list);
+ gst_tag_list_unref (tag_list);
+
+ send_flush_events (element);
+ send_segment_event (element);
+ accumulator = 0;
+ for (i = 180; i--;)
+ push_buffer (test_buffer_square_float_stereo (&accumulator, 44100, 512,
+ 0.25, 0.25));
+ send_eos_event (element);
+
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.25);
+ fail_unless_track_gain (tag_list, -6.20);
+ fail_unless_album_peak (tag_list, 0.75);
+ /* Strangely, wavegain reports -12.17 for the album, but the fixed
+ * metaflac agrees to us. Could be a 32767 vs. 32768 issue. */
+ fail_unless_album_gain (tag_list, -12.18);
+ gst_tag_list_unref (tag_list);
+
+ cleanup_rganalysis (element);
+}
+
+GST_END_TEST;
+
+/* Checks ensuring that the "forced" property works as advertised. */
+
+GST_START_TEST (test_forced)
+{
+ GstElement *element = setup_rganalysis ();
+ GstTagList *tag_list;
+ gint accumulator = 0;
+ gint i;
+
+ g_object_set (element, "forced", FALSE, NULL);
+ set_playing_state (element);
+
+ send_stream_start_event (element);
+ send_caps_event (GST_AUDIO_NE (F32), 44100, 2);
+ send_segment_event (element);
+ tag_list = gst_tag_list_new_empty ();
+ /* Provided values are totally arbitrary. */
+ gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND,
+ GST_TAG_TRACK_PEAK, 1.0, GST_TAG_TRACK_GAIN, 2.21, NULL);
+ send_tag_event (element, tag_list);
+
+ for (i = 20; i--;)
+ push_buffer (test_buffer_const_float_stereo (44100, 512, 0.5, 0.5));
+ send_eos_event (element);
+
+ /* This fails if a tag message is generated: */
+ /* Same values as above */
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_gain (tag_list, 2.21);
+ fail_unless_track_peak (tag_list, 1.0);
+ gst_tag_list_unref (tag_list);
+
+ /* Now back to a track without tags. */
+ send_flush_events (element);
+ send_segment_event (element);
+ for (i = 20; i--;)
+ push_buffer (test_buffer_square_float_stereo (&accumulator, 44100, 512,
+ 0.25, 0.25));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.25);
+ fail_unless_track_gain (tag_list, get_expected_gain (44100));
+ gst_tag_list_unref (tag_list);
+
+ cleanup_rganalysis (element);
+}
+
+GST_END_TEST;
+
+/* Sending track gain and peak in separate tag lists. */
+
+GST_START_TEST (test_forced_separate)
+{
+ GstElement *element = setup_rganalysis ();
+ GstTagList *tag_list;
+ gint accumulator = 0;
+ gint i;
+
+ g_object_set (element, "forced", FALSE, NULL);
+ set_playing_state (element);
+
+ send_stream_start_event (element);
+ send_caps_event (GST_AUDIO_NE (F32), 44100, 2);
+ send_segment_event (element);
+ tag_list = gst_tag_list_new_empty ();
+ gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND, GST_TAG_TRACK_GAIN, 2.21,
+ NULL);
+ send_tag_event (element, tag_list);
+
+ tag_list = gst_tag_list_new_empty ();
+ gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND, GST_TAG_TRACK_PEAK, 1.0,
+ NULL);
+ send_tag_event (element, tag_list);
+
+ for (i = 20; i--;)
+ push_buffer (test_buffer_square_float_stereo (&accumulator, 44100, 512,
+ 0.5, 0.5));
+ send_eos_event (element);
+
+ /* Same values as above */
+ tag_list = poll_tags_only (element);
+ fail_unless_track_gain (tag_list, 2.21);
+ gst_tag_list_unref (tag_list);
+
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 1.0);
+ gst_tag_list_unref (tag_list);
+
+ /* Now a track without tags. */
+ send_flush_events (element);
+ send_segment_event (element);
+ accumulator = 0;
+ for (i = 20; i--;)
+ push_buffer (test_buffer_square_float_stereo (&accumulator, 44100, 512,
+ 0.25, 0.25));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.25);
+ fail_unless_track_gain (tag_list, get_expected_gain (44100));
+ fail_if_album_tags (tag_list);
+ gst_tag_list_unref (tag_list);
+
+ cleanup_rganalysis (element);
+}
+
+GST_END_TEST;
+
+/* A TAG event is sent _after_ data has already been processed. In real
+ * pipelines, this could happen if there is more than one rganalysis element (by
+ * accident). While it would have analyzed all the data prior to receiving the
+ * event, I expect it to not post its results if not forced. This test is
+ * almost equivalent to test_forced. */
+
+GST_START_TEST (test_forced_after_data)
+{
+ GstElement *element = setup_rganalysis ();
+ GstTagList *tag_list;
+ gint accumulator = 0;
+ gint i;
+
+ g_object_set (element, "forced", FALSE, NULL);
+ set_playing_state (element);
+
+ send_stream_start_event (element);
+ send_caps_event (GST_AUDIO_NE (F32), 8000, 2);
+ send_segment_event (element);
+ for (i = 20; i--;)
+ push_buffer (test_buffer_const_float_stereo (8000, 512, 0.5, 0.5));
+
+ tag_list = gst_tag_list_new_empty ();
+ gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND,
+ GST_TAG_TRACK_PEAK, 1.0, GST_TAG_TRACK_GAIN, 2.21, NULL);
+ send_tag_event (element, tag_list);
+
+ send_eos_event (element);
+
+ /* Same values as above */
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_gain (tag_list, 2.21);
+ fail_unless_track_peak (tag_list, 1.0);
+ gst_tag_list_unref (tag_list);
+
+ send_flush_events (element);
+ send_segment_event (element);
+ /* Now back to a normal track, this one has no tags: */
+ for (i = 20; i--;)
+ push_buffer (test_buffer_square_float_stereo (&accumulator, 8000, 512, 0.25,
+ 0.25));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.25);
+ fail_unless_track_gain (tag_list, get_expected_gain (8000));
+ gst_tag_list_unref (tag_list);
+
+ cleanup_rganalysis (element);
+}
+
+GST_END_TEST;
+
+/* Like test_forced, but *analyze* an album afterwards. The two tests following
+ * this one check the *skipping* of albums. */
+
+GST_START_TEST (test_forced_album)
+{
+ GstElement *element = setup_rganalysis ();
+ GstTagList *tag_list;
+ gint accumulator;
+ gint i;
+
+ g_object_set (element, "forced", FALSE, NULL);
+ set_playing_state (element);
+
+ send_stream_start_event (element);
+ send_caps_event (GST_AUDIO_NE (F32), 44100, 2);
+ send_segment_event (element);
+ tag_list = gst_tag_list_new_empty ();
+ /* Provided values are totally arbitrary. */
+ gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND,
+ GST_TAG_TRACK_PEAK, 1.0, GST_TAG_TRACK_GAIN, 2.21, NULL);
+ send_tag_event (element, tag_list);
+
+ accumulator = 0;
+ for (i = 20; i--;)
+ push_buffer (test_buffer_square_float_stereo (&accumulator, 44100, 512,
+ 0.5, 0.5));
+ send_eos_event (element);
+
+ /* Same values as above */
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_gain (tag_list, 2.21);
+ fail_unless_track_peak (tag_list, 1.0);
+ gst_tag_list_unref (tag_list);
+
+ /* Now an album without tags. */
+ g_object_set (element, "num-tracks", 2, NULL);
+
+ send_flush_events (element);
+ send_segment_event (element);
+ accumulator = 0;
+ for (i = 20; i--;)
+ push_buffer (test_buffer_square_float_stereo (&accumulator, 44100, 512,
+ 0.25, 0.25));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.25);
+ fail_unless_track_gain (tag_list, get_expected_gain (44100));
+ fail_if_album_tags (tag_list);
+ gst_tag_list_unref (tag_list);
+ fail_unless_num_tracks (element, 1);
+
+ send_flush_events (element);
+ send_segment_event (element);
+ accumulator = 0;
+ for (i = 20; i--;)
+ push_buffer (test_buffer_square_float_stereo (&accumulator, 44100, 512,
+ 0.25, 0.25));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.25);
+ fail_unless_track_gain (tag_list, get_expected_gain (44100));
+ fail_unless_album_peak (tag_list, 0.25);
+ fail_unless_album_gain (tag_list, get_expected_gain (44100));
+ gst_tag_list_unref (tag_list);
+ fail_unless_num_tracks (element, 0);
+
+ cleanup_rganalysis (element);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_forced_album_skip)
+{
+ GstElement *element = setup_rganalysis ();
+ GstTagList *tag_list;
+ gint accumulator = 0;
+ gint i;
+
+ g_object_set (element, "forced", FALSE, "num-tracks", 2, NULL);
+ set_playing_state (element);
+
+ send_stream_start_event (element);
+ send_caps_event (GST_AUDIO_NE (F32), 8000, 2);
+ send_segment_event (element);
+ tag_list = gst_tag_list_new_empty ();
+ /* Provided values are totally arbitrary. */
+ gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND,
+ GST_TAG_TRACK_PEAK, 0.75, GST_TAG_TRACK_GAIN, 2.21,
+ GST_TAG_ALBUM_PEAK, 0.80, GST_TAG_ALBUM_GAIN, -0.11, NULL);
+ send_tag_event (element, tag_list);
+
+ for (i = 20; i--;)
+ push_buffer (test_buffer_square_float_stereo (&accumulator, 8000, 512, 0.25,
+ 0.25));
+ send_eos_event (element);
+
+ /* Same values as above */
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_gain (tag_list, 2.21);
+ fail_unless_track_peak (tag_list, 0.75);
+ gst_tag_list_unref (tag_list);
+
+ fail_unless_num_tracks (element, 1);
+
+ /* This track has no tags, but needs to be skipped anyways since we
+ * are in album processing mode. */
+ send_flush_events (element);
+ send_segment_event (element);
+ for (i = 20; i--;)
+ push_buffer (test_buffer_const_float_stereo (8000, 512, 0.0, 0.0));
+ send_eos_event (element);
+ poll_eos (element);
+ fail_unless_num_tracks (element, 0);
+
+ /* Normal track after the album. Of course not to be skipped. */
+ send_flush_events (element);
+ send_segment_event (element);
+ accumulator = 0;
+ for (i = 20; i--;)
+ push_buffer (test_buffer_square_float_stereo (&accumulator, 8000, 512, 0.25,
+ 0.25));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.25);
+ fail_unless_track_gain (tag_list, get_expected_gain (8000));
+ fail_if_album_tags (tag_list);
+ gst_tag_list_unref (tag_list);
+
+ cleanup_rganalysis (element);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_forced_album_no_skip)
+{
+ GstElement *element = setup_rganalysis ();
+ GstTagList *tag_list;
+ gint accumulator = 0;
+ gint i;
+
+ g_object_set (element, "forced", FALSE, "num-tracks", 2, NULL);
+ set_playing_state (element);
+
+ send_stream_start_event (element);
+ send_caps_event (GST_AUDIO_NE (F32), 8000, 2);
+ send_segment_event (element);
+ for (i = 20; i--;)
+ push_buffer (test_buffer_square_float_stereo (&accumulator, 8000, 512, 0.25,
+ 0.25));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.25);
+ fail_unless_track_gain (tag_list, get_expected_gain (8000));
+ fail_if_album_tags (tag_list);
+ gst_tag_list_unref (tag_list);
+ fail_unless_num_tracks (element, 1);
+
+ /* The second track has indeed full tags, but although being not forced, this
+ * one has to be processed because album processing is on. */
+ send_flush_events (element);
+ send_segment_event (element);
+ tag_list = gst_tag_list_new_empty ();
+ /* Provided values are totally arbitrary. */
+ gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND,
+ GST_TAG_TRACK_PEAK, 0.75, GST_TAG_TRACK_GAIN, 2.21,
+ GST_TAG_ALBUM_PEAK, 0.80, GST_TAG_ALBUM_GAIN, -0.11, NULL);
+ send_tag_event (element, tag_list);
+ for (i = 20; i--;)
+ push_buffer (test_buffer_const_float_stereo (8000, 512, 0.0, 0.0));
+ send_eos_event (element);
+
+ /* the first batch from the tags */
+ tag_list = poll_tags_only (element);
+ fail_unless_track_peak (tag_list, 0.75);
+ fail_unless_track_gain (tag_list, 2.21);
+ gst_tag_list_unref (tag_list);
+
+ /* the second from the processing */
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.0);
+ fail_unless_track_gain (tag_list, SILENCE_GAIN);
+ /* Second track was just silence so the album peak equals the first
+ * track's peak. */
+ fail_unless_album_peak (tag_list, 0.25);
+ /* Statistical processing leads to the second track being
+ * ignored for the gain (because it is so short): */
+ fail_unless_album_gain (tag_list, get_expected_gain (8000));
+ gst_tag_list_unref (tag_list);
+ fail_unless_num_tracks (element, 0);
+
+ cleanup_rganalysis (element);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_forced_abort_album_no_skip)
+{
+ GstElement *element = setup_rganalysis ();
+ GstTagList *tag_list;
+ gint accumulator = 0;
+ gint i;
+
+ g_object_set (element, "forced", FALSE, "num-tracks", 2, NULL);
+ set_playing_state (element);
+
+ send_stream_start_event (element);
+ send_caps_event (GST_AUDIO_NE (F32), 8000, 2);
+ send_segment_event (element);
+ for (i = 20; i--;)
+ push_buffer (test_buffer_square_float_stereo (&accumulator, 8000, 512, 0.25,
+ 0.25));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.25);
+ fail_unless_track_gain (tag_list, get_expected_gain (8000));
+ fail_if_album_tags (tag_list);
+ gst_tag_list_unref (tag_list);
+ fail_unless_num_tracks (element, 1);
+
+ /* Disabling album processing before end of album: */
+ g_object_set (element, "num-tracks", 0, NULL);
+
+ send_flush_events (element);
+ send_segment_event (element);
+
+ /* Processing a track that has to be skipped. */
+ tag_list = gst_tag_list_new_empty ();
+ /* Provided values are totally arbitrary. */
+ gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND,
+ GST_TAG_TRACK_PEAK, 0.75, GST_TAG_TRACK_GAIN, 2.21,
+ GST_TAG_ALBUM_PEAK, 0.80, GST_TAG_ALBUM_GAIN, -0.11, NULL);
+ send_tag_event (element, tag_list);
+ for (i = 20; i--;)
+ push_buffer (test_buffer_const_float_stereo (8000, 512, 0.0, 0.0));
+ send_eos_event (element);
+
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.75);
+ fail_unless_track_gain (tag_list, 2.21);
+ gst_tag_list_unref (tag_list);
+
+ cleanup_rganalysis (element);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_reference_level)
+{
+ GstElement *element = setup_rganalysis ();
+ GstTagList *tag_list;
+ gdouble ref_level;
+ gint accumulator = 0;
+ gint i;
+
+ set_playing_state (element);
+
+ send_stream_start_event (element);
+ send_caps_event (GST_AUDIO_NE (F32), 44100, 2);
+ send_segment_event (element);
+ for (i = 20; i--;)
+ push_buffer (test_buffer_square_float_stereo (&accumulator, 44100, 512,
+ 0.25, 0.25));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.25);
+ fail_unless_track_gain (tag_list, get_expected_gain (44100));
+ fail_if_album_tags (tag_list);
+ fail_unless (gst_tag_list_get_double (tag_list, GST_TAG_REFERENCE_LEVEL,
+ &ref_level) && MATCH_GAIN (ref_level, 89.),
+ "Incorrect reference level tag");
+ gst_tag_list_unref (tag_list);
+
+ g_object_set (element, "reference-level", 83., "num-tracks", 2, NULL);
+
+ send_flush_events (element);
+ send_segment_event (element);
+ for (i = 20; i--;)
+ push_buffer (test_buffer_square_float_stereo (&accumulator, 44100, 512,
+ 0.25, 0.25));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.25);
+ fail_unless_track_gain (tag_list, get_expected_gain (44100) - 6.);
+ fail_if_album_tags (tag_list);
+ fail_unless (gst_tag_list_get_double (tag_list, GST_TAG_REFERENCE_LEVEL,
+ &ref_level) && MATCH_GAIN (ref_level, 83.),
+ "Incorrect reference level tag");
+ gst_tag_list_unref (tag_list);
+
+ send_flush_events (element);
+ send_segment_event (element);
+ accumulator = 0;
+ for (i = 20; i--;)
+ push_buffer (test_buffer_square_float_stereo (&accumulator, 44100, 512,
+ 0.25, 0.25));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.25);
+ fail_unless_track_gain (tag_list, get_expected_gain (44100) - 6.);
+ fail_unless_album_peak (tag_list, 0.25);
+ /* We provided the same waveform twice, with a reset separating
+ * them. Therefore, the album gain matches the track gain. */
+ fail_unless_album_gain (tag_list, get_expected_gain (44100) - 6.);
+ fail_unless (gst_tag_list_get_double (tag_list, GST_TAG_REFERENCE_LEVEL,
+ &ref_level) && MATCH_GAIN (ref_level, 83.),
+ "Incorrect reference level tag");
+ gst_tag_list_unref (tag_list);
+
+ cleanup_rganalysis (element);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_all_formats)
+{
+ GstElement *element = setup_rganalysis ();
+ GstTagList *tag_list;
+ gint accumulator = 0;
+ gint i, j;
+
+ set_playing_state (element);
+ send_stream_start_event (element);
+ for (i = G_N_ELEMENTS (supported_rates); i--;) {
+ send_flush_events (element);
+ send_caps_event (GST_AUDIO_NE (F32), supported_rates[i].sample_rate, 2);
+ send_segment_event (element);
+ accumulator = 0;
+ for (j = 0; j < 4; j++)
+ push_buffer (test_buffer_square_float_stereo (&accumulator,
+ supported_rates[i].sample_rate, 512, 0.25, 0.25));
+ send_caps_event (GST_AUDIO_NE (F32), supported_rates[i].sample_rate, 1);
+ for (j = 0; j < 3; j++)
+ push_buffer (test_buffer_square_float_mono (&accumulator,
+ supported_rates[i].sample_rate, 512, 0.25));
+ send_caps_event (GST_AUDIO_NE (S16), supported_rates[i].sample_rate, 2);
+ for (j = 0; j < 4; j++)
+ push_buffer (test_buffer_square_int16_stereo (&accumulator,
+ supported_rates[i].sample_rate, 16, 512, 1 << 13, 1 << 13));
+ send_caps_event (GST_AUDIO_NE (S16), supported_rates[i].sample_rate, 1);
+ for (j = 0; j < 3; j++)
+ push_buffer (test_buffer_square_int16_mono (&accumulator,
+ supported_rates[i].sample_rate, 16, 512, 1 << 13));
+ send_eos_event (element);
+ tag_list = poll_tags_followed_by_eos (element);
+ fail_unless_track_peak (tag_list, 0.25);
+ fail_unless_track_gain (tag_list, supported_rates[i].gain);
+ gst_tag_list_unref (tag_list);
+ }
+
+ cleanup_rganalysis (element);
+}
+
+GST_END_TEST;
+
+/* Checks ensuring all advertised supported sample rates are really
+ * accepted, for integer and float, mono and stereo. This also
+ * verifies that the correct gain is computed for all formats (except
+ * odd bit depths). */
+
+#define MAKE_GAIN_TEST_FLOAT_MONO(sample_rate) \
+ GST_START_TEST (test_gain_float_mono_##sample_rate) \
+{ \
+ GstElement *element = setup_rganalysis (); \
+ GstTagList *tag_list; \
+ gint accumulator = 0; \
+ gint i; \
+ \
+ set_playing_state (element); \
+ send_stream_start_event (element); \
+ send_caps_event (GST_AUDIO_NE (F32), sample_rate, 1); \
+ send_segment_event (element); \
+ \
+ for (i = 0; i < 20; i++) \
+ push_buffer (test_buffer_square_float_mono (&accumulator, \
+ sample_rate, 512, 0.25)); \
+ send_eos_event (element); \
+ tag_list = poll_tags_followed_by_eos (element); \
+ fail_unless_track_peak (tag_list, 0.25); \
+ fail_unless_track_gain (tag_list, \
+ get_expected_gain (sample_rate)); \
+ gst_tag_list_unref (tag_list); \
+ \
+ cleanup_rganalysis (element); \
+} \
+ \
+GST_END_TEST;
+
+#define MAKE_GAIN_TEST_FLOAT_STEREO(sample_rate) \
+ GST_START_TEST (test_gain_float_stereo_##sample_rate) \
+{ \
+ GstElement *element = setup_rganalysis (); \
+ GstTagList *tag_list; \
+ gint accumulator = 0; \
+ gint i; \
+ \
+ set_playing_state (element); \
+ send_stream_start_event (element); \
+ send_caps_event (GST_AUDIO_NE (F32), sample_rate, 2); \
+ send_segment_event (element); \
+ \
+ for (i = 0; i < 20; i++) \
+ push_buffer (test_buffer_square_float_stereo (&accumulator, \
+ sample_rate, 512, 0.25, 0.25)); \
+ send_eos_event (element); \
+ tag_list = poll_tags_followed_by_eos (element); \
+ fail_unless_track_peak (tag_list, 0.25); \
+ fail_unless_track_gain (tag_list, \
+ get_expected_gain (sample_rate)); \
+ gst_tag_list_unref (tag_list); \
+ \
+ cleanup_rganalysis (element); \
+} \
+ \
+GST_END_TEST;
+
+#define MAKE_GAIN_TEST_INT16_MONO(sample_rate, depth) \
+ GST_START_TEST (test_gain_int16_##depth##_mono_##sample_rate) \
+{ \
+ GstElement *element = setup_rganalysis (); \
+ GstTagList *tag_list; \
+ gint accumulator = 0; \
+ gint i; \
+ \
+ set_playing_state (element); \
+ send_stream_start_event (element); \
+ send_caps_event (GST_AUDIO_NE (S16), sample_rate, 1); \
+ send_segment_event (element); \
+ \
+ for (i = 0; i < 20; i++) \
+ push_buffer (test_buffer_square_int16_mono (&accumulator, \
+ sample_rate, depth, 512, 1 << (13 + depth - 16))); \
+ \
+ send_eos_event (element); \
+ tag_list = poll_tags_followed_by_eos (element); \
+ fail_unless_track_peak (tag_list, 0.25); \
+ fail_unless_track_gain (tag_list, \
+ get_expected_gain (sample_rate)); \
+ gst_tag_list_unref (tag_list); \
+ \
+ cleanup_rganalysis (element); \
+} \
+ \
+GST_END_TEST;
+
+#define MAKE_GAIN_TEST_INT16_STEREO(sample_rate, depth) \
+ GST_START_TEST (test_gain_int16_##depth##_stereo_##sample_rate) \
+{ \
+ GstElement *element = setup_rganalysis (); \
+ GstTagList *tag_list; \
+ gint accumulator = 0; \
+ gint i; \
+ \
+ set_playing_state (element); \
+ send_stream_start_event (element); \
+ send_caps_event (GST_AUDIO_NE (S16), sample_rate, 2); \
+ send_segment_event (element); \
+ \
+ for (i = 0; i < 20; i++) \
+ push_buffer (test_buffer_square_int16_stereo (&accumulator, \
+ sample_rate, depth, 512, 1 << (13 + depth - 16), \
+ 1 << (13 + depth - 16))); \
+ send_eos_event (element); \
+ tag_list = poll_tags_followed_by_eos (element); \
+ fail_unless_track_peak (tag_list, 0.25); \
+ fail_unless_track_gain (tag_list, \
+ get_expected_gain (sample_rate)); \
+ gst_tag_list_unref (tag_list); \
+ \
+ cleanup_rganalysis (element); \
+} \
+ \
+GST_END_TEST;
+
+MAKE_GAIN_TEST_FLOAT_MONO (8000);
+MAKE_GAIN_TEST_FLOAT_MONO (11025);
+MAKE_GAIN_TEST_FLOAT_MONO (12000);
+MAKE_GAIN_TEST_FLOAT_MONO (16000);
+MAKE_GAIN_TEST_FLOAT_MONO (22050);
+MAKE_GAIN_TEST_FLOAT_MONO (24000);
+MAKE_GAIN_TEST_FLOAT_MONO (32000);
+MAKE_GAIN_TEST_FLOAT_MONO (44100);
+MAKE_GAIN_TEST_FLOAT_MONO (48000);
+
+MAKE_GAIN_TEST_FLOAT_STEREO (8000);
+MAKE_GAIN_TEST_FLOAT_STEREO (11025);
+MAKE_GAIN_TEST_FLOAT_STEREO (12000);
+MAKE_GAIN_TEST_FLOAT_STEREO (16000);
+MAKE_GAIN_TEST_FLOAT_STEREO (22050);
+MAKE_GAIN_TEST_FLOAT_STEREO (24000);
+MAKE_GAIN_TEST_FLOAT_STEREO (32000);
+MAKE_GAIN_TEST_FLOAT_STEREO (44100);
+MAKE_GAIN_TEST_FLOAT_STEREO (48000);
+
+MAKE_GAIN_TEST_INT16_MONO (8000, 16);
+MAKE_GAIN_TEST_INT16_MONO (11025, 16);
+MAKE_GAIN_TEST_INT16_MONO (12000, 16);
+MAKE_GAIN_TEST_INT16_MONO (16000, 16);
+MAKE_GAIN_TEST_INT16_MONO (22050, 16);
+MAKE_GAIN_TEST_INT16_MONO (24000, 16);
+MAKE_GAIN_TEST_INT16_MONO (32000, 16);
+MAKE_GAIN_TEST_INT16_MONO (44100, 16);
+MAKE_GAIN_TEST_INT16_MONO (48000, 16);
+
+MAKE_GAIN_TEST_INT16_STEREO (8000, 16);
+MAKE_GAIN_TEST_INT16_STEREO (11025, 16);
+MAKE_GAIN_TEST_INT16_STEREO (12000, 16);
+MAKE_GAIN_TEST_INT16_STEREO (16000, 16);
+MAKE_GAIN_TEST_INT16_STEREO (22050, 16);
+MAKE_GAIN_TEST_INT16_STEREO (24000, 16);
+MAKE_GAIN_TEST_INT16_STEREO (32000, 16);
+MAKE_GAIN_TEST_INT16_STEREO (44100, 16);
+MAKE_GAIN_TEST_INT16_STEREO (48000, 16);
+
+static Suite *
+rganalysis_suite (void)
+{
+ Suite *s = suite_create ("rganalysis");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_no_buffer);
+ tcase_add_test (tc_chain, test_no_buffer_album_1);
+ tcase_add_test (tc_chain, test_no_buffer_album_2);
+ tcase_add_test (tc_chain, test_empty_buffers);
+
+ tcase_add_test (tc_chain, test_peak_float);
+ tcase_add_test (tc_chain, test_peak_int16_16);
+
+ tcase_add_test (tc_chain, test_peak_album);
+ tcase_add_test (tc_chain, test_peak_track_album);
+ tcase_add_test (tc_chain, test_peak_album_abort_to_track);
+
+ tcase_add_test (tc_chain, test_gain_album);
+
+ tcase_add_test (tc_chain, test_forced);
+ tcase_add_test (tc_chain, test_forced_separate);
+ tcase_add_test (tc_chain, test_forced_after_data);
+ tcase_add_test (tc_chain, test_forced_album);
+ tcase_add_test (tc_chain, test_forced_album_skip);
+ tcase_add_test (tc_chain, test_forced_album_no_skip);
+ tcase_add_test (tc_chain, test_forced_abort_album_no_skip);
+
+ tcase_add_test (tc_chain, test_reference_level);
+
+ tcase_add_test (tc_chain, test_all_formats);
+
+ tcase_add_test (tc_chain, test_gain_float_mono_8000);
+ tcase_add_test (tc_chain, test_gain_float_mono_11025);
+ tcase_add_test (tc_chain, test_gain_float_mono_12000);
+ tcase_add_test (tc_chain, test_gain_float_mono_16000);
+ tcase_add_test (tc_chain, test_gain_float_mono_22050);
+ tcase_add_test (tc_chain, test_gain_float_mono_24000);
+ tcase_add_test (tc_chain, test_gain_float_mono_32000);
+ tcase_add_test (tc_chain, test_gain_float_mono_44100);
+ tcase_add_test (tc_chain, test_gain_float_mono_48000);
+
+ tcase_add_test (tc_chain, test_gain_float_stereo_8000);
+ tcase_add_test (tc_chain, test_gain_float_stereo_11025);
+ tcase_add_test (tc_chain, test_gain_float_stereo_12000);
+ tcase_add_test (tc_chain, test_gain_float_stereo_16000);
+ tcase_add_test (tc_chain, test_gain_float_stereo_22050);
+ tcase_add_test (tc_chain, test_gain_float_stereo_24000);
+ tcase_add_test (tc_chain, test_gain_float_stereo_32000);
+ tcase_add_test (tc_chain, test_gain_float_stereo_44100);
+ tcase_add_test (tc_chain, test_gain_float_stereo_48000);
+
+ tcase_add_test (tc_chain, test_gain_int16_16_mono_8000);
+ tcase_add_test (tc_chain, test_gain_int16_16_mono_11025);
+ tcase_add_test (tc_chain, test_gain_int16_16_mono_12000);
+ tcase_add_test (tc_chain, test_gain_int16_16_mono_16000);
+ tcase_add_test (tc_chain, test_gain_int16_16_mono_22050);
+ tcase_add_test (tc_chain, test_gain_int16_16_mono_24000);
+ tcase_add_test (tc_chain, test_gain_int16_16_mono_32000);
+ tcase_add_test (tc_chain, test_gain_int16_16_mono_44100);
+ tcase_add_test (tc_chain, test_gain_int16_16_mono_48000);
+
+ tcase_add_test (tc_chain, test_gain_int16_16_stereo_8000);
+ tcase_add_test (tc_chain, test_gain_int16_16_stereo_11025);
+ tcase_add_test (tc_chain, test_gain_int16_16_stereo_12000);
+ tcase_add_test (tc_chain, test_gain_int16_16_stereo_16000);
+ tcase_add_test (tc_chain, test_gain_int16_16_stereo_22050);
+ tcase_add_test (tc_chain, test_gain_int16_16_stereo_24000);
+ tcase_add_test (tc_chain, test_gain_int16_16_stereo_32000);
+ tcase_add_test (tc_chain, test_gain_int16_16_stereo_44100);
+ tcase_add_test (tc_chain, test_gain_int16_16_stereo_48000);
+
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ gint nf;
+
+ Suite *s = rganalysis_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ srunner_run_all (sr, CK_ENV);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}
diff --git a/tests/check/elements/rglimiter.c b/tests/check/elements/rglimiter.c
new file mode 100644
index 0000000..926ca33
--- /dev/null
+++ b/tests/check/elements/rglimiter.c
@@ -0,0 +1,292 @@
+/* GStreamer ReplayGain limiter
+ *
+ * Copyright (C) 2007 Rene Stadler <mail@renestadler.de>
+ *
+ * rglimiter.c: Unit test for the rglimiter element
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <gst/check/gstcheck.h>
+#include <gst/audio/audio.h>
+
+#include <math.h>
+
+/* 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;
+
+#define RG_LIMITER_CAPS_TEMPLATE_STRING \
+ "audio/x-raw, " \
+ "format = (string) "GST_AUDIO_NE (F32) ", " \
+ "layout = (string) interleaved, " \
+ "channels = (int) [ 1, MAX ], " \
+ "rate = (int) [ 1, MAX ]"
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (RG_LIMITER_CAPS_TEMPLATE_STRING)
+ );
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (RG_LIMITER_CAPS_TEMPLATE_STRING)
+ );
+
+static GstElement *
+setup_rglimiter (void)
+{
+ GstElement *element;
+
+ GST_DEBUG ("setup_rglimiter");
+ element = gst_check_setup_element ("rglimiter");
+ mysrcpad = gst_check_setup_src_pad (element, &srctemplate);
+ mysinkpad = gst_check_setup_sink_pad (element, &sinktemplate);
+ gst_pad_set_active (mysrcpad, TRUE);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ return element;
+}
+
+static void
+cleanup_rglimiter (GstElement * element)
+{
+ GST_DEBUG ("cleanup_rglimiter");
+
+ g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
+ g_list_free (buffers);
+ buffers = NULL;
+
+ gst_check_teardown_src_pad (element);
+ gst_check_teardown_sink_pad (element);
+ gst_check_teardown_element (element);
+}
+
+static void
+set_playing_state (GstElement * element)
+{
+ fail_unless (gst_element_set_state (element,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "Could not set state to PLAYING");
+}
+
+static const gfloat test_input[] = {
+ -2.0, -1.0, -0.75, -0.5, -0.25, 0.0, 0.25, 0.5, 0.75, 1.0, 2.0
+};
+
+static const gfloat test_output[] = {
+ -0.99752737684336523, /* -2.0 */
+ -0.88079707797788243, /* -1.0 */
+ -0.7310585786300049, /* -0.75 */
+ -0.5, -0.25, 0.0, 0.25, 0.5,
+ 0.7310585786300049, /* 0.75 */
+ 0.88079707797788243, /* 1.0 */
+ 0.99752737684336523, /* 2.0 */
+};
+
+static void
+setup_events (GstElement * element)
+{
+ GstCaps *caps;
+
+ caps = gst_caps_new_simple ("audio/x-raw",
+ "rate", G_TYPE_INT, 44100, "channels", G_TYPE_INT, 1,
+ "format", G_TYPE_STRING, GST_AUDIO_NE (F32),
+ "layout", G_TYPE_STRING, "interleaved", NULL);
+
+ gst_check_setup_events (mysrcpad, element, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+}
+
+static GstBuffer *
+create_test_buffer (void)
+{
+ GstBuffer *buf = gst_buffer_new_and_alloc (sizeof (test_input));
+
+ gst_buffer_fill (buf, 0, test_input, sizeof (test_input));
+
+ ASSERT_BUFFER_REFCOUNT (buf, "buf", 1);
+
+ return buf;
+}
+
+static void
+verify_test_buffer (GstBuffer * buf)
+{
+ GstMapInfo map;
+ gfloat *output;
+ gint i;
+
+ gst_buffer_map (buf, &map, GST_MAP_READ);
+ output = (gfloat *) map.data;
+ fail_unless (map.size == sizeof (test_output));
+
+ for (i = 0; i < G_N_ELEMENTS (test_input); i++)
+ fail_unless (ABS (output[i] - test_output[i]) < 1.e-6,
+ "Incorrect output value %.6f for input %.2f, expected %.6f",
+ output[i], test_input[i], test_output[i]);
+
+ gst_buffer_unmap (buf, &map);
+}
+
+/* Start of tests. */
+
+GST_START_TEST (test_no_buffer)
+{
+ GstElement *element = setup_rglimiter ();
+
+ set_playing_state (element);
+
+ cleanup_rglimiter (element);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_disabled)
+{
+ GstElement *element = setup_rglimiter ();
+ GstBuffer *buf, *out_buf;
+
+ g_object_set (element, "enabled", FALSE, NULL);
+ set_playing_state (element);
+ setup_events (element);
+
+ buf = create_test_buffer ();
+ fail_unless (gst_pad_push (mysrcpad, buf) == GST_FLOW_OK);
+ fail_unless (g_list_length (buffers) == 1);
+ out_buf = buffers->data;
+ fail_if (out_buf == NULL);
+ buffers = g_list_remove (buffers, out_buf);
+ ASSERT_BUFFER_REFCOUNT (out_buf, "out_buf", 1);
+ fail_unless (buf == out_buf);
+ gst_buffer_unref (out_buf);
+
+ cleanup_rglimiter (element);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_limiting)
+{
+ GstElement *element = setup_rglimiter ();
+ GstBuffer *buf, *out_buf;
+
+ set_playing_state (element);
+ setup_events (element);
+
+ /* Mutable variant. */
+ buf = create_test_buffer ();
+ GST_DEBUG ("push mutable buffer");
+ fail_unless (gst_pad_push (mysrcpad, buf) == GST_FLOW_OK);
+ fail_unless (g_list_length (buffers) == 1);
+ out_buf = buffers->data;
+ fail_if (out_buf == NULL);
+ ASSERT_BUFFER_REFCOUNT (out_buf, "out_buf", 1);
+ verify_test_buffer (out_buf);
+
+ /* Immutable variant. */
+ buf = create_test_buffer ();
+ /* Extra ref: */
+ gst_buffer_ref (buf);
+ ASSERT_BUFFER_REFCOUNT (buf, "buf", 2);
+ GST_DEBUG ("push immutable buffer");
+ fail_unless (gst_pad_push (mysrcpad, buf) == GST_FLOW_OK);
+ ASSERT_BUFFER_REFCOUNT (buf, "buf", 1);
+ fail_unless (g_list_length (buffers) == 2);
+ out_buf = g_list_last (buffers)->data;
+ fail_if (out_buf == NULL);
+ ASSERT_BUFFER_REFCOUNT (out_buf, "out_buf", 1);
+ fail_unless (buf != out_buf);
+ /* Drop our extra ref: */
+ gst_buffer_unref (buf);
+ verify_test_buffer (out_buf);
+
+ cleanup_rglimiter (element);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_gap)
+{
+ GstElement *element = setup_rglimiter ();
+ GstBuffer *buf, *out_buf;
+ GstMapInfo m1, m2;
+
+ set_playing_state (element);
+ setup_events (element);
+
+ buf = create_test_buffer ();
+ GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_GAP);
+ fail_unless (gst_pad_push (mysrcpad, buf) == GST_FLOW_OK);
+ fail_unless (g_list_length (buffers) == 1);
+ out_buf = buffers->data;
+ fail_if (out_buf == NULL);
+ ASSERT_BUFFER_REFCOUNT (out_buf, "out_buf", 1);
+
+ /* Verify that the baseclass does not lift the GAP flag: */
+ fail_unless (GST_BUFFER_FLAG_IS_SET (out_buf, GST_BUFFER_FLAG_GAP));
+
+ gst_buffer_map (out_buf, &m1, GST_MAP_READ);
+ gst_buffer_map (buf, &m2, GST_MAP_READ);
+
+ g_assert (m1.size == m2.size);
+ /* We cheated by passing an input buffer with non-silence that has the GAP
+ * flag set. The element cannot know that however and must have skipped
+ * adjusting the buffer because of the flag, which we can easily verify: */
+ fail_if (memcmp (m1.data, m2.data, m1.size) != 0);
+
+ gst_buffer_unmap (out_buf, &m1);
+ gst_buffer_unmap (buf, &m2);
+
+ cleanup_rglimiter (element);
+}
+
+GST_END_TEST;
+
+static Suite *
+rglimiter_suite (void)
+{
+ Suite *s = suite_create ("rglimiter");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_no_buffer);
+ tcase_add_test (tc_chain, test_disabled);
+ tcase_add_test (tc_chain, test_limiting);
+ tcase_add_test (tc_chain, test_gap);
+
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ gint nf;
+
+ Suite *s = rglimiter_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ srunner_run_all (sr, CK_ENV);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}
diff --git a/tests/check/elements/rgvolume.c b/tests/check/elements/rgvolume.c
new file mode 100644
index 0000000..47ed3b3
--- /dev/null
+++ b/tests/check/elements/rgvolume.c
@@ -0,0 +1,687 @@
+/* GStreamer ReplayGain volume adjustment
+ *
+ * Copyright (C) 2007 Rene Stadler <mail@renestadler.de>
+ *
+ * rgvolume.c: Unit test for the rgvolume element
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <gst/check/gstcheck.h>
+#include <gst/audio/audio.h>
+
+#include <math.h>
+
+static GList *events = NULL;
+
+/* 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;
+
+#define RG_VOLUME_CAPS_TEMPLATE_STRING \
+ "audio/x-raw, " \
+ "format = (string) "GST_AUDIO_NE (F32) ", " \
+ "layout = (string) interleaved, " \
+ "channels = (int) [ 1, MAX ], " \
+ "rate = (int) [ 1, MAX ]"
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (RG_VOLUME_CAPS_TEMPLATE_STRING)
+ );
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (RG_VOLUME_CAPS_TEMPLATE_STRING)
+ );
+
+static GstBuffer *test_buffer_new (gfloat value);
+
+/* gstcheck sets up a chain function that appends buffers to a global list.
+ * This is our equivalent of that for event handling. */
+static gboolean
+event_func (GstPad * pad, GstObject * parent, GstEvent * event)
+{
+ GST_DEBUG ("received event %p (%s)", event, GST_EVENT_TYPE_NAME (event));
+ events = g_list_append (events, event);
+
+ return TRUE;
+}
+
+static GstElement *
+setup_rgvolume (void)
+{
+ GstElement *element;
+
+ GST_DEBUG ("setup_rgvolume");
+ element = gst_check_setup_element ("rgvolume");
+ mysrcpad = gst_check_setup_src_pad (element, &srctemplate);
+ mysinkpad = gst_check_setup_sink_pad (element, &sinktemplate);
+
+ /* Capture events, to test tag filtering behavior: */
+ gst_pad_set_event_function (mysinkpad, event_func);
+
+ gst_pad_set_active (mysrcpad, TRUE);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ return element;
+}
+
+static void
+send_empty_buffer (void)
+{
+ GstBuffer *buf;
+
+ buf = test_buffer_new (0.0);
+ gst_buffer_resize (buf, 0, 0);
+ GST_BUFFER_DURATION (buf) = 0;
+ GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_OFFSET (buf);
+ fail_unless (gst_pad_push (mysrcpad, buf) == GST_FLOW_OK);
+
+ fail_unless (g_list_length (buffers) == 1);
+ fail_unless (buffers->data == buf);
+ gst_mini_object_unref ((GstMiniObject *) buffers->data);
+ buffers = g_list_remove (buffers, buf);
+}
+
+static void
+cleanup_rgvolume (GstElement * element)
+{
+ GST_DEBUG ("cleanup_rgvolume");
+
+ g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
+ g_list_free (buffers);
+ buffers = NULL;
+
+ g_list_foreach (events, (GFunc) gst_mini_object_unref, NULL);
+ g_list_free (events);
+ events = NULL;
+
+ gst_pad_set_active (mysrcpad, FALSE);
+ gst_pad_set_active (mysinkpad, FALSE);
+ gst_check_teardown_src_pad (element);
+ gst_check_teardown_sink_pad (element);
+ gst_check_teardown_element (element);
+}
+
+static void
+set_playing_state (GstElement * element)
+{
+ fail_unless (gst_element_set_state (element,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "Could not set state to PLAYING");
+}
+
+static void
+set_null_state (GstElement * element)
+{
+ fail_unless (gst_element_set_state (element,
+ GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS,
+ "Could not set state to NULL");
+}
+
+static void
+send_flush_events (GstElement * element)
+{
+ gboolean res;
+
+ res = gst_pad_push_event (mysrcpad, gst_event_new_flush_start ());
+ fail_unless (res, "flush-start even not handled");
+
+ res = gst_pad_push_event (mysrcpad, gst_event_new_flush_stop (TRUE));
+ fail_unless (res, "flush-stop event not handled");
+}
+
+static void
+send_stream_start_event (GstElement * element)
+{
+ gboolean res;
+
+ res = gst_pad_push_event (mysrcpad, gst_event_new_stream_start ("test"));
+ fail_unless (res, "STREAM_START event not handled");
+}
+
+static void
+send_caps_event (GstElement * element)
+{
+ GstCaps *caps;
+ gboolean res;
+
+ caps = gst_caps_from_string ("audio/x-raw, format = F32LE, "
+ "layout = interleaved, rate = 8000, channels = 1");
+ res = gst_pad_push_event (mysrcpad, gst_event_new_caps (caps));
+ fail_unless (res, "CAPS event not handled");
+ gst_caps_unref (caps);
+}
+
+static void
+send_segment_event (GstElement * element)
+{
+ GstSegment segment;
+ gboolean res;
+
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ res = gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment));
+ fail_unless (res, "SEGMENT event not handled");
+}
+
+static void
+send_eos_event (GstElement * element)
+{
+ GstEvent *event = gst_event_new_eos ();
+
+ fail_unless (gst_pad_push_event (mysrcpad, event),
+ "Pushing EOS event failed");
+}
+
+static GstEvent *
+send_tag_event (GstElement * element, GstEvent * event)
+{
+ GList *l;
+ GstTagList *tag_list;
+ gdouble dummy;
+
+ g_return_val_if_fail (event->type == GST_EVENT_TAG, NULL);
+
+ fail_unless (gst_pad_push_event (mysrcpad, event),
+ "Pushing tag event failed");
+
+ event = NULL;
+
+ for (l = g_list_last (events); l; l = l->prev) {
+ if (GST_EVENT_TYPE (l->data) == GST_EVENT_TAG) {
+ event = l->data;
+ events = g_list_delete_link (events, l);
+ break;
+ }
+ }
+
+ /* Event got filtered out */
+ if (event == NULL)
+ return NULL;
+
+ fail_unless (event->type == GST_EVENT_TAG);
+ gst_event_parse_tag (event, &tag_list);
+
+ /* The element is supposed to filter out ReplayGain related tags. */
+ fail_if (gst_tag_list_get_double (tag_list, GST_TAG_TRACK_GAIN, &dummy),
+ "tag event still contains track gain tag");
+ fail_if (gst_tag_list_get_double (tag_list, GST_TAG_TRACK_PEAK, &dummy),
+ "tag event still contains track peak tag");
+ fail_if (gst_tag_list_get_double (tag_list, GST_TAG_ALBUM_GAIN, &dummy),
+ "tag event still contains album gain tag");
+ fail_if (gst_tag_list_get_double (tag_list, GST_TAG_ALBUM_PEAK, &dummy),
+ "tag event still contains album peak tag");
+
+ return event;
+}
+
+static GstBuffer *
+test_buffer_new (gfloat value)
+{
+ GstBuffer *buf;
+ GstMapInfo map;
+ gfloat *data;
+ gint i;
+
+ buf = gst_buffer_new_and_alloc (8 * sizeof (gfloat));
+ gst_buffer_map (buf, &map, GST_MAP_WRITE);
+ data = (gfloat *) map.data;
+ for (i = 0; i < 8; i++)
+ data[i] = value;
+ gst_buffer_unmap (buf, &map);
+
+
+ ASSERT_BUFFER_REFCOUNT (buf, "buf", 1);
+
+ return buf;
+}
+
+#define MATCH_GAIN(g1, g2) ((g1 < g2 + 1e-6) && (g2 < g1 + 1e-6))
+
+static void
+fail_unless_target_gain (GstElement * element, gdouble expected_gain)
+{
+ gdouble prop_gain;
+
+ g_object_get (element, "target-gain", &prop_gain, NULL);
+
+ fail_unless (MATCH_GAIN (prop_gain, expected_gain),
+ "Target gain is %.2f dB, expected %.2f dB", prop_gain, expected_gain);
+}
+
+static void
+fail_unless_result_gain (GstElement * element, gdouble expected_gain)
+{
+ GstBuffer *input_buf, *output_buf;
+ gfloat *data;
+ gfloat input_sample, output_sample;
+ gdouble gain, prop_gain;
+ gboolean is_passthrough, expect_passthrough;
+ gint i;
+ GstMapInfo map;
+
+ fail_unless (g_list_length (buffers) == 0);
+
+ input_sample = 1.0;
+ input_buf = test_buffer_new (input_sample);
+
+ /* We keep an extra reference to detect passthrough mode. */
+ gst_buffer_ref (input_buf);
+ /* Pushing steals a reference. */
+ fail_unless (gst_pad_push (mysrcpad, input_buf) == GST_FLOW_OK);
+ gst_buffer_unref (input_buf);
+
+ /* The output buffer ends up on the global buffer list. */
+ fail_unless (g_list_length (buffers) == 1);
+ output_buf = buffers->data;
+ fail_if (output_buf == NULL);
+
+ buffers = g_list_remove (buffers, output_buf);
+ ASSERT_BUFFER_REFCOUNT (output_buf, "output_buf", 1);
+
+ fail_unless_equals_int (gst_buffer_get_size (output_buf),
+ 8 * sizeof (gfloat));
+
+ gst_buffer_map (output_buf, &map, GST_MAP_READ);
+ data = (gfloat *) map.data;
+
+ output_sample = *data;
+ fail_if (output_sample == 0.0, "First output sample is zero");
+ for (i = 1; i < 8; i++) {
+ fail_unless (output_sample == data[i], "Output samples not uniform");
+ };
+ gst_buffer_unmap (output_buf, &map);
+
+ gain = 20. * log10 (output_sample / input_sample);
+ fail_unless (MATCH_GAIN (gain, expected_gain),
+ "Applied gain is %.2f dB, expected %.2f dB", gain, expected_gain);
+ g_object_get (element, "result-gain", &prop_gain, NULL);
+ fail_unless (MATCH_GAIN (prop_gain, expected_gain),
+ "Result gain is %.2f dB, expected %.2f dB", prop_gain, expected_gain);
+
+ is_passthrough = (output_buf == input_buf);
+ expect_passthrough = MATCH_GAIN (expected_gain, +0.00);
+ fail_unless (is_passthrough == expect_passthrough,
+ expect_passthrough
+ ? "Expected operation in passthrough mode"
+ : "Incorrect passthrough behaviour");
+
+ gst_buffer_unref (output_buf);
+}
+
+static void
+fail_unless_gain (GstElement * element, gdouble expected_gain)
+{
+ fail_unless_target_gain (element, expected_gain);
+ fail_unless_result_gain (element, expected_gain);
+}
+
+/* Start of tests. */
+
+GST_START_TEST (test_no_buffer)
+{
+ GstElement *element = setup_rgvolume ();
+
+ set_playing_state (element);
+ set_null_state (element);
+ set_playing_state (element);
+ send_eos_event (element);
+
+ cleanup_rgvolume (element);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_events)
+{
+ GstElement *element = setup_rgvolume ();
+ GstEvent *event;
+ GstEvent *new_event;
+ GstTagList *tag_list;
+ gchar *artist;
+
+ set_playing_state (element);
+ send_stream_start_event (element);
+ send_caps_event (element);
+ send_segment_event (element);
+
+ send_empty_buffer ();
+
+ tag_list = gst_tag_list_new_empty ();
+ gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE,
+ GST_TAG_TRACK_GAIN, +4.95, GST_TAG_TRACK_PEAK, 0.59463,
+ GST_TAG_ALBUM_GAIN, -1.54, GST_TAG_ALBUM_PEAK, 0.693415,
+ GST_TAG_ARTIST, "Foobar", NULL);
+ event = gst_event_new_tag (tag_list);
+ new_event = send_tag_event (element, event);
+ gst_event_parse_tag (new_event, &tag_list);
+ fail_unless (gst_tag_list_get_string (tag_list, GST_TAG_ARTIST, &artist));
+ fail_unless (g_str_equal (artist, "Foobar"));
+ g_free (artist);
+ gst_event_unref (new_event);
+
+ /* Same as above, but with a non-writable event. */
+
+ tag_list = gst_tag_list_new_empty ();
+ gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE,
+ GST_TAG_TRACK_GAIN, +4.95, GST_TAG_TRACK_PEAK, 0.59463,
+ GST_TAG_ALBUM_GAIN, -1.54, GST_TAG_ALBUM_PEAK, 0.693415,
+ GST_TAG_ARTIST, "Foobar", NULL);
+ event = gst_event_new_tag (tag_list);
+ new_event = send_tag_event (element, event);
+ gst_event_parse_tag (new_event, &tag_list);
+ fail_unless (gst_tag_list_get_string (tag_list, GST_TAG_ARTIST, &artist));
+ fail_unless (g_str_equal (artist, "Foobar"));
+ g_free (artist);
+ gst_event_unref (new_event);
+
+ cleanup_rgvolume (element);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_simple)
+{
+ GstElement *element = setup_rgvolume ();
+ GstTagList *tag_list;
+
+ g_object_set (element, "album-mode", FALSE, "headroom", +0.00,
+ "pre-amp", -6.00, "fallback-gain", +1.23, NULL);
+ set_playing_state (element);
+ send_stream_start_event (element);
+ send_caps_event (element);
+ send_segment_event (element);
+
+ send_empty_buffer ();
+
+ tag_list = gst_tag_list_new_empty ();
+ gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE,
+ GST_TAG_TRACK_GAIN, -3.45, GST_TAG_TRACK_PEAK, 1.0,
+ GST_TAG_ALBUM_GAIN, +2.09, GST_TAG_ALBUM_PEAK, 1.0, NULL);
+ fail_unless (send_tag_event (element, gst_event_new_tag (tag_list)) == NULL);
+ fail_unless_gain (element, -9.45); /* pre-amp + track gain */
+ send_eos_event (element);
+
+ g_object_set (element, "album-mode", TRUE, NULL);
+
+ send_flush_events (element);
+ send_segment_event (element);
+
+ tag_list = gst_tag_list_new_empty ();
+ gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE,
+ GST_TAG_TRACK_GAIN, -3.45, GST_TAG_TRACK_PEAK, 1.0,
+ GST_TAG_ALBUM_GAIN, +2.09, GST_TAG_ALBUM_PEAK, 1.0, NULL);
+ fail_unless (send_tag_event (element, gst_event_new_tag (tag_list)) == NULL);
+ fail_unless_gain (element, -3.91); /* pre-amp + album gain */
+
+ /* Switching back to track mode in the middle of a stream: */
+ g_object_set (element, "album-mode", FALSE, NULL);
+ fail_unless_gain (element, -9.45); /* pre-amp + track gain */
+ send_eos_event (element);
+
+ cleanup_rgvolume (element);
+}
+
+GST_END_TEST;
+
+/* If there are no gain tags at all, the fallback gain is used. */
+
+GST_START_TEST (test_fallback_gain)
+{
+ GstElement *element = setup_rgvolume ();
+ GstTagList *tag_list;
+
+ /* First some track where fallback does _not_ apply. */
+
+ g_object_set (element, "album-mode", FALSE, "headroom", 10.00,
+ "pre-amp", -6.00, "fallback-gain", -3.00, NULL);
+ set_playing_state (element);
+ send_stream_start_event (element);
+ send_caps_event (element);
+ send_segment_event (element);
+
+ send_empty_buffer ();
+
+ tag_list = gst_tag_list_new_empty ();
+ gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE,
+ GST_TAG_TRACK_GAIN, +3.5, GST_TAG_TRACK_PEAK, 1.0,
+ GST_TAG_ALBUM_GAIN, -0.5, GST_TAG_ALBUM_PEAK, 1.0, NULL);
+ fail_unless (send_tag_event (element, gst_event_new_tag (tag_list)) == NULL);
+ fail_unless_gain (element, -2.50); /* pre-amp + track gain */
+ send_eos_event (element);
+
+ /* Now a track completely missing tags. */
+ send_flush_events (element);
+ send_segment_event (element);
+
+ fail_unless_gain (element, -9.00); /* pre-amp + fallback-gain */
+
+ /* Changing the fallback gain in the middle of a stream, going to pass-through
+ * mode: */
+ g_object_set (element, "fallback-gain", +6.00, NULL);
+ fail_unless_gain (element, +0.00); /* pre-amp + fallback-gain */
+ send_eos_event (element);
+
+ /* Verify that result gain is set to +0.00 with pre-amp + fallback-gain >
+ * +0.00 and no headroom. */
+ send_flush_events (element);
+ send_segment_event (element);
+
+ g_object_set (element, "fallback-gain", +12.00, "headroom", +0.00, NULL);
+ fail_unless_target_gain (element, +6.00); /* pre-amp + fallback-gain */
+ fail_unless_result_gain (element, +0.00);
+ send_eos_event (element);
+
+ cleanup_rgvolume (element);
+}
+
+GST_END_TEST;
+
+/* If album gain is to be preferred but not available, the track gain is to be
+ * taken instead. */
+
+GST_START_TEST (test_fallback_track)
+{
+ GstElement *element = setup_rgvolume ();
+ GstTagList *tag_list;
+
+ g_object_set (element, "album-mode", TRUE, "headroom", +0.00,
+ "pre-amp", -6.00, "fallback-gain", +1.23, NULL);
+ set_playing_state (element);
+ send_stream_start_event (element);
+ send_caps_event (element);
+ send_segment_event (element);
+
+ send_empty_buffer ();
+
+ tag_list = gst_tag_list_new_empty ();
+ gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE,
+ GST_TAG_TRACK_GAIN, +2.11, GST_TAG_TRACK_PEAK, 1.0, NULL);
+ fail_unless (send_tag_event (element, gst_event_new_tag (tag_list)) == NULL);
+ fail_unless_gain (element, -3.89); /* pre-amp + track gain */
+
+ send_eos_event (element);
+
+ cleanup_rgvolume (element);
+}
+
+GST_END_TEST;
+
+/* If track gain is to be preferred but not available, the album gain is to be
+ * taken instead. */
+
+GST_START_TEST (test_fallback_album)
+{
+ GstElement *element = setup_rgvolume ();
+ GstTagList *tag_list;
+
+ g_object_set (element, "album-mode", FALSE, "headroom", +0.00,
+ "pre-amp", -6.00, "fallback-gain", +1.23, NULL);
+ set_playing_state (element);
+ send_stream_start_event (element);
+ send_caps_event (element);
+ send_segment_event (element);
+
+ send_empty_buffer ();
+
+ tag_list = gst_tag_list_new_empty ();
+ gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE,
+ GST_TAG_ALBUM_GAIN, +3.73, GST_TAG_ALBUM_PEAK, 1.0, NULL);
+ fail_unless (send_tag_event (element, gst_event_new_tag (tag_list)) == NULL);
+ fail_unless_gain (element, -2.27); /* pre-amp + album gain */
+
+ send_eos_event (element);
+
+ cleanup_rgvolume (element);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_headroom)
+{
+ GstElement *element = setup_rgvolume ();
+ GstTagList *tag_list;
+
+ g_object_set (element, "album-mode", FALSE, "headroom", +0.00,
+ "pre-amp", +0.00, "fallback-gain", +1.23, NULL);
+ set_playing_state (element);
+ send_stream_start_event (element);
+ send_caps_event (element);
+ send_segment_event (element);
+
+ send_empty_buffer ();
+
+ tag_list = gst_tag_list_new_empty ();
+ gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE,
+ GST_TAG_TRACK_GAIN, +3.50, GST_TAG_TRACK_PEAK, 1.0, NULL);
+ fail_unless (send_tag_event (element, gst_event_new_tag (tag_list)) == NULL);
+ fail_unless_target_gain (element, +3.50); /* pre-amp + track gain */
+ fail_unless_result_gain (element, +0.00);
+ send_eos_event (element);
+
+ send_flush_events (element);
+ send_segment_event (element);
+
+ g_object_set (element, "headroom", +2.00, NULL);
+ tag_list = gst_tag_list_new_empty ();
+ gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE,
+ GST_TAG_TRACK_GAIN, +9.18, GST_TAG_TRACK_PEAK, 0.687149, NULL);
+ fail_unless (send_tag_event (element, gst_event_new_tag (tag_list)) == NULL);
+ fail_unless_target_gain (element, +9.18); /* pre-amp + track gain */
+ /* Result is 20. * log10 (1. / peak) + headroom. */
+ fail_unless_result_gain (element, 5.2589816238303335);
+ send_eos_event (element);
+
+ send_flush_events (element);
+ send_segment_event (element);
+
+ g_object_set (element, "album-mode", TRUE, NULL);
+ tag_list = gst_tag_list_new_empty ();
+ gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE,
+ GST_TAG_ALBUM_GAIN, +5.50, GST_TAG_ALBUM_PEAK, 1.0, NULL);
+ fail_unless (send_tag_event (element, gst_event_new_tag (tag_list)) == NULL);
+ fail_unless_target_gain (element, +5.50); /* pre-amp + album gain */
+ fail_unless_result_gain (element, +2.00); /* headroom */
+ send_eos_event (element);
+
+ cleanup_rgvolume (element);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_reference_level)
+{
+ GstElement *element = setup_rgvolume ();
+ GstTagList *tag_list;
+
+ g_object_set (element,
+ "album-mode", FALSE,
+ "headroom", +0.00, "pre-amp", +0.00, "fallback-gain", +1.23, NULL);
+ set_playing_state (element);
+
+ send_stream_start_event (element);
+ send_caps_event (element);
+ send_segment_event (element);
+
+ send_empty_buffer ();
+
+ tag_list = gst_tag_list_new_empty ();
+ gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE,
+ GST_TAG_TRACK_GAIN, 0.00, GST_TAG_TRACK_PEAK, 0.2,
+ GST_TAG_REFERENCE_LEVEL, 83., NULL);
+ fail_unless (send_tag_event (element, gst_event_new_tag (tag_list)) == NULL);
+ /* Because our authorative reference is 89 dB, we bump it up by +6 dB. */
+ fail_unless_gain (element, +6.00); /* pre-amp + track gain */
+ send_eos_event (element);
+
+ g_object_set (element, "album-mode", TRUE, NULL);
+
+ /* Same as above, but with album gain. */
+ send_flush_events (element);
+ send_segment_event (element);
+
+ tag_list = gst_tag_list_new_empty ();
+ gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE,
+ GST_TAG_TRACK_GAIN, 1.23, GST_TAG_TRACK_PEAK, 0.1,
+ GST_TAG_ALBUM_GAIN, 0.00, GST_TAG_ALBUM_PEAK, 0.2,
+ GST_TAG_REFERENCE_LEVEL, 83., NULL);
+ fail_unless (send_tag_event (element, gst_event_new_tag (tag_list)) == NULL);
+ fail_unless_gain (element, +6.00); /* pre-amp + album gain */
+
+ cleanup_rgvolume (element);
+}
+
+GST_END_TEST;
+
+static Suite *
+rgvolume_suite (void)
+{
+ Suite *s = suite_create ("rgvolume");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_no_buffer);
+ tcase_add_test (tc_chain, test_events);
+ tcase_add_test (tc_chain, test_simple);
+ tcase_add_test (tc_chain, test_fallback_gain);
+ tcase_add_test (tc_chain, test_fallback_track);
+ tcase_add_test (tc_chain, test_fallback_album);
+ tcase_add_test (tc_chain, test_headroom);
+ tcase_add_test (tc_chain, test_reference_level);
+
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ gint nf;
+
+ Suite *s = rgvolume_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ srunner_run_all (sr, CK_ENV);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}
diff --git a/tests/check/elements/rtp-payloading.c b/tests/check/elements/rtp-payloading.c
new file mode 100755
index 0000000..a5db021
--- /dev/null
+++ b/tests/check/elements/rtp-payloading.c
@@ -0,0 +1,970 @@
+/* GStreamer RTP payloader unit tests
+ * Copyright (C) 2008 Nokia Corporation and its subsidary(-ies)
+ * contact: <stefan.kost@nokia.com>
+ *
+ * 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.
+ */
+#include <gst/check/gstcheck.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define RELEASE_ELEMENT(x) if(x) {gst_object_unref(x); x = NULL;}
+
+#define LOOP_COUNT 1
+
+/*
+ * RTP pipeline structure to store the required elements.
+ */
+typedef struct
+{
+ GstElement *pipeline;
+ GstElement *appsrc;
+ GstElement *rtppay;
+ GstElement *rtpdepay;
+ GstElement *fakesink;
+ const guint8 *frame_data;
+ int frame_data_size;
+ int frame_count;
+} rtp_pipeline;
+
+/*
+ * Number of bytes received in the chain list function when using buffer lists
+ */
+static guint chain_list_bytes_received;
+
+/*
+ * Chain list function for testing buffer lists
+ */
+static GstFlowReturn
+rtp_pipeline_chain_list (GstPad * pad, GstObject * parent, GstBufferList * list)
+{
+ guint i, len;
+
+ fail_if (!list);
+ /*
+ * Count the size of the payload in the buffer list.
+ */
+ len = gst_buffer_list_length (list);
+
+ /* Loop through all groups */
+ for (i = 0; i < len; i++) {
+ GstBuffer *paybuf;
+ GstMemory *mem;
+ gint size;
+
+ paybuf = gst_buffer_list_get (list, i);
+ /* only count real data which is expected in last memory block */
+ fail_unless (gst_buffer_n_memory (paybuf) > 1);
+ mem = gst_buffer_get_memory_range (paybuf, gst_buffer_n_memory (paybuf) - 1,
+ 1);
+ size = gst_memory_get_sizes (mem, NULL, NULL);
+ gst_memory_unref (mem);
+ chain_list_bytes_received += size;
+ }
+ gst_buffer_list_unref (list);
+
+ return GST_FLOW_OK;
+}
+
+static GstFlowReturn
+rtp_pipeline_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
+{
+ GstBufferList *list;
+
+ list = gst_buffer_list_new_sized (1);
+ gst_buffer_list_add (list, buf);
+ return rtp_pipeline_chain_list (pad, parent, list);
+}
+
+/*
+ * RTP bus callback.
+ */
+static gboolean
+rtp_bus_callback (GstBus * bus, GstMessage * message, gpointer data)
+{
+ GMainLoop *mainloop = (GMainLoop *) data;
+
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_ERROR:
+ {
+ GError *err;
+
+ gchar *debug;
+
+ gchar *element_name;
+
+ element_name = (message->src) ? gst_object_get_name (message->src) : NULL;
+ gst_message_parse_error (message, &err, &debug);
+ g_print ("\nError from element %s: %s\n%s\n\n",
+ GST_STR_NULL (element_name), err->message, (debug) ? debug : "");
+ g_error_free (err);
+ g_free (debug);
+ g_free (element_name);
+
+ fail_if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR);
+
+ g_main_loop_quit (mainloop);
+ }
+ break;
+
+ case GST_MESSAGE_EOS:
+ {
+ g_main_loop_quit (mainloop);
+ }
+ break;
+ break;
+
+ default:
+ {
+ }
+ break;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Creates a RTP pipeline for one test.
+ * @param frame_data Pointer to the frame data which is used to pass thru pay/depayloaders.
+ * @param frame_data_size Frame data size in bytes.
+ * @param frame_count Frame count.
+ * @param filtercaps Caps filters.
+ * @param pay Payloader name.
+ * @param depay Depayloader name.
+ * @return
+ * Returns pointer to the RTP pipeline.
+ * The user must free the RTP pipeline when it's not used anymore.
+ */
+static rtp_pipeline *
+rtp_pipeline_create (const guint8 * frame_data, int frame_data_size,
+ int frame_count, const char *filtercaps, const char *pay, const char *depay)
+{
+ gchar *pipeline_name;
+ rtp_pipeline *p;
+ GstCaps *caps;
+
+ /* Check parameters. */
+ if (!frame_data || !pay || !depay) {
+ return NULL;
+ }
+
+ /* Allocate memory for the RTP pipeline. */
+ p = (rtp_pipeline *) malloc (sizeof (rtp_pipeline));
+
+ p->frame_data = frame_data;
+ p->frame_data_size = frame_data_size;
+ p->frame_count = frame_count;
+
+ /* Create elements. */
+ pipeline_name = g_strdup_printf ("%s-%s-pipeline", pay, depay);
+ p->pipeline = gst_pipeline_new (pipeline_name);
+ g_free (pipeline_name);
+ p->appsrc = gst_element_factory_make ("appsrc", NULL);
+ p->rtppay = gst_element_factory_make (pay, NULL);
+ p->rtpdepay = gst_element_factory_make (depay, NULL);
+ p->fakesink = gst_element_factory_make ("fakesink", NULL);
+
+ /* One or more elements are not created successfully or failed to create p? */
+ if (!p->pipeline || !p->appsrc || !p->rtppay || !p->rtpdepay || !p->fakesink) {
+ /* Release created elements. */
+ RELEASE_ELEMENT (p->pipeline);
+ RELEASE_ELEMENT (p->appsrc);
+ RELEASE_ELEMENT (p->rtppay);
+ RELEASE_ELEMENT (p->rtpdepay);
+ RELEASE_ELEMENT (p->fakesink);
+
+ /* Release allocated memory. */
+ free (p);
+
+ return NULL;
+ }
+
+ /* Set src properties. */
+ caps = gst_caps_from_string (filtercaps);
+ g_object_set (p->appsrc, "do-timestamp", TRUE, "caps", caps,
+ "format", GST_FORMAT_TIME, NULL);
+ gst_caps_unref (caps);
+
+ /* Add elements to the pipeline. */
+ gst_bin_add (GST_BIN (p->pipeline), p->appsrc);
+ gst_bin_add (GST_BIN (p->pipeline), p->rtppay);
+ gst_bin_add (GST_BIN (p->pipeline), p->rtpdepay);
+ gst_bin_add (GST_BIN (p->pipeline), p->fakesink);
+
+ /* Link elements. */
+ gst_element_link (p->appsrc, p->rtppay);
+ gst_element_link (p->rtppay, p->rtpdepay);
+ gst_element_link (p->rtpdepay, p->fakesink);
+
+ return p;
+}
+
+/*
+ * Destroys the RTP pipeline.
+ * @param p Pointer to the RTP pipeline.
+ */
+static void
+rtp_pipeline_destroy (rtp_pipeline * p)
+{
+ /* Check parameters. */
+ if (p == NULL) {
+ return;
+ }
+
+ /* Release pipeline. */
+ RELEASE_ELEMENT (p->pipeline);
+
+ /* Release allocated memory. */
+ free (p);
+}
+
+/*
+ * Runs the RTP pipeline.
+ * @param p Pointer to the RTP pipeline.
+ */
+static void
+rtp_pipeline_run (rtp_pipeline * p)
+{
+ GstFlowReturn flow_ret;
+ GMainLoop *mainloop = NULL;
+ GstBus *bus;
+ gint i, j;
+
+ /* Check parameters. */
+ if (p == NULL) {
+ return;
+ }
+
+ /* Create mainloop. */
+ mainloop = g_main_loop_new (NULL, FALSE);
+ if (!mainloop) {
+ return;
+ }
+
+ /* Add bus callback. */
+ bus = gst_pipeline_get_bus (GST_PIPELINE (p->pipeline));
+
+ gst_bus_add_watch (bus, rtp_bus_callback, (gpointer) mainloop);
+ gst_object_unref (bus);
+
+ /* Set pipeline to PLAYING. */
+ gst_element_set_state (p->pipeline, GST_STATE_PLAYING);
+
+ /* Push data into the pipeline */
+ for (i = 0; i < LOOP_COUNT; i++) {
+ const guint8 *data = p->frame_data;
+
+ for (j = 0; j < p->frame_count; j++) {
+ GstBuffer *buf;
+
+ buf =
+ gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY,
+ (guint8 *) data, p->frame_data_size, 0, p->frame_data_size, NULL,
+ NULL);
+
+ g_signal_emit_by_name (p->appsrc, "push-buffer", buf, &flow_ret);
+ fail_unless_equals_int (flow_ret, GST_FLOW_OK);
+ data += p->frame_data_size;
+
+ gst_buffer_unref (buf);
+ }
+ }
+
+ g_signal_emit_by_name (p->appsrc, "end-of-stream", &flow_ret);
+
+ /* Run mainloop. */
+ g_main_loop_run (mainloop);
+
+ /* Set pipeline to NULL. */
+ gst_element_set_state (p->pipeline, GST_STATE_NULL);
+
+ /* Release mainloop. */
+ g_main_loop_unref (mainloop);
+}
+
+/*
+ * Enables buffer lists and adds a chain_list_function to the depayloader.
+ * @param p Pointer to the RTP pipeline.
+ */
+static void
+rtp_pipeline_enable_lists (rtp_pipeline * p, guint mtu_size)
+{
+ GstPad *pad;
+
+ /* set mtu size if needed */
+ if (mtu_size) {
+ g_object_set (p->rtppay, "mtu", mtu_size, NULL);
+ }
+
+ /* Add chain list function for the buffer list tests */
+ pad = gst_element_get_static_pad (p->rtpdepay, "sink");
+ gst_pad_set_chain_list_function (pad,
+ GST_DEBUG_FUNCPTR (rtp_pipeline_chain_list));
+ /* .. to satisfy this silly test code in case someone dares push a buffer */
+ gst_pad_set_chain_function (pad, GST_DEBUG_FUNCPTR (rtp_pipeline_chain));
+ gst_object_unref (pad);
+}
+
+/*
+ * Creates the RTP pipeline and runs the test using the pipeline.
+ * @param frame_data Pointer to the frame data which is used to pass thru pay/depayloaders.
+ * @param frame_data_size Frame data size in bytes.
+ * @param frame_count Frame count.
+ * @param filtercaps Caps filters.
+ * @param pay Payloader name.
+ * @param depay Depayloader name.
+ * @bytes_sent bytes that will be sent, used when testing buffer lists
+ * @mtu_size set mtu size when testing lists
+ * @use_lists enable buffer lists
+ */
+static void
+rtp_pipeline_test (const guint8 * frame_data, int frame_data_size,
+ int frame_count, const char *filtercaps, const char *pay, const char *depay,
+ guint bytes_sent, guint mtu_size, gboolean use_lists)
+{
+ /* Create RTP pipeline. */
+ rtp_pipeline *p =
+ rtp_pipeline_create (frame_data, frame_data_size, frame_count, filtercaps,
+ pay, depay);
+
+ if (p == NULL) {
+ return;
+ }
+
+ if (use_lists) {
+ rtp_pipeline_enable_lists (p, mtu_size);
+ chain_list_bytes_received = 0;
+ }
+
+ /* Run RTP pipeline. */
+ rtp_pipeline_run (p);
+
+ /* Destroy RTP pipeline. */
+ rtp_pipeline_destroy (p);
+
+ if (use_lists) {
+ /* 'next NAL' indicator is 4 bytes */
+ fail_if (chain_list_bytes_received != bytes_sent * LOOP_COUNT);
+ }
+}
+
+static const guint8 rtp_ilbc_frame_data[] =
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static int rtp_ilbc_frame_data_size = 20;
+
+static int rtp_ilbc_frame_count = 1;
+
+GST_START_TEST (rtp_ilbc)
+{
+ rtp_pipeline_test (rtp_ilbc_frame_data, rtp_ilbc_frame_data_size,
+ rtp_ilbc_frame_count, "audio/x-iLBC,mode=20", "rtpilbcpay",
+ "rtpilbcdepay", 0, 0, FALSE);
+}
+
+GST_END_TEST;
+static const guint8 rtp_gsm_frame_data[] =
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static int rtp_gsm_frame_data_size = 20;
+
+static int rtp_gsm_frame_count = 1;
+
+GST_START_TEST (rtp_gsm)
+{
+ rtp_pipeline_test (rtp_gsm_frame_data, rtp_gsm_frame_data_size,
+ rtp_gsm_frame_count, "audio/x-gsm,rate=8000,channels=1", "rtpgsmpay",
+ "rtpgsmdepay", 0, 0, FALSE);
+}
+
+GST_END_TEST;
+static const guint8 rtp_amr_frame_data[] =
+ { 0x3c, 0x24, 0x03, 0xb3, 0x48, 0x10, 0x68, 0x46, 0x6c, 0xec, 0x03,
+ 0x7a, 0x37, 0x16, 0x41, 0x41, 0xc0, 0x00, 0x0d, 0xcd, 0x12, 0xed,
+ 0xad, 0x80, 0x00, 0x00, 0x11, 0x31, 0x00, 0x00, 0x0d, 0xa0
+};
+
+static int rtp_amr_frame_data_size = 32;
+
+static int rtp_amr_frame_count = 1;
+
+GST_START_TEST (rtp_amr)
+{
+ rtp_pipeline_test (rtp_amr_frame_data, rtp_amr_frame_data_size,
+ rtp_amr_frame_count, "audio/AMR,channels=1,rate=8000", "rtpamrpay",
+ "rtpamrdepay", 0, 0, FALSE);
+}
+
+GST_END_TEST;
+static const guint8 rtp_pcma_frame_data[] =
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static int rtp_pcma_frame_data_size = 20;
+
+static int rtp_pcma_frame_count = 1;
+
+GST_START_TEST (rtp_pcma)
+{
+ rtp_pipeline_test (rtp_pcma_frame_data, rtp_pcma_frame_data_size,
+ rtp_pcma_frame_count, "audio/x-alaw,channels=1,rate=8000", "rtppcmapay",
+ "rtppcmadepay", 0, 0, FALSE);
+}
+
+GST_END_TEST;
+static const guint8 rtp_pcmu_frame_data[] =
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static int rtp_pcmu_frame_data_size = 20;
+
+static int rtp_pcmu_frame_count = 1;
+
+GST_START_TEST (rtp_pcmu)
+{
+ rtp_pipeline_test (rtp_pcmu_frame_data, rtp_pcmu_frame_data_size,
+ rtp_pcmu_frame_count, "audio/x-mulaw,channels=1,rate=8000", "rtppcmupay",
+ "rtppcmudepay", 0, 0, FALSE);
+}
+
+GST_END_TEST;
+static const guint8 rtp_mpa_frame_data[] =
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static int rtp_mpa_frame_data_size = 20;
+
+static int rtp_mpa_frame_count = 1;
+
+GST_START_TEST (rtp_mpa)
+{
+ rtp_pipeline_test (rtp_mpa_frame_data, rtp_mpa_frame_data_size,
+ rtp_mpa_frame_count, "audio/mpeg,mpegversion=1", "rtpmpapay",
+ "rtpmpadepay", 0, 0, FALSE);
+}
+
+GST_END_TEST;
+static const guint8 rtp_h263_frame_data[] =
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static int rtp_h263_frame_data_size = 20;
+
+static int rtp_h263_frame_count = 1;
+
+GST_START_TEST (rtp_h263)
+{
+ rtp_pipeline_test (rtp_h263_frame_data, rtp_h263_frame_data_size,
+ rtp_h263_frame_count, "video/x-h263,variant=(string)itu,h263version=h263",
+ "rtph263pay", "rtph263depay", 0, 0, FALSE);
+}
+
+GST_END_TEST;
+static const guint8 rtp_h263p_frame_data[] =
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static int rtp_h263p_frame_data_size = 20;
+
+static int rtp_h263p_frame_count = 1;
+
+GST_START_TEST (rtp_h263p)
+{
+ rtp_pipeline_test (rtp_h263p_frame_data, rtp_h263p_frame_data_size,
+ rtp_h263p_frame_count, "video/x-h263,variant=(string)itu,"
+ "h263version=(string)h263", "rtph263ppay", "rtph263pdepay", 0, 0, FALSE);
+
+ /* payloader should accept any input that matches the template caps
+ * if there's just a udpsink or fakesink downstream */
+ rtp_pipeline_test (rtp_h263p_frame_data, rtp_h263p_frame_data_size,
+ rtp_h263p_frame_count, "video/x-h263,variant=(string)itu,"
+ "h263version=(string)h263", "rtph263ppay", "identity", 0, 0, FALSE);
+
+ /* default output of avenc_h263p */
+ rtp_pipeline_test (rtp_h263p_frame_data, rtp_h263p_frame_data_size,
+ rtp_h263p_frame_count, "video/x-h263,variant=(string)itu,"
+ "h263version=(string)h263p, annex-f=(boolean)true, "
+ "annex-j=(boolean)true, annex-i=(boolean)true, annex-t=(boolean)true",
+ "rtph263ppay", "identity", 0, 0, FALSE);
+
+ /* pay ! depay should also work with any input */
+ rtp_pipeline_test (rtp_h263p_frame_data, rtp_h263p_frame_data_size,
+ rtp_h263p_frame_count, "video/x-h263,variant=(string)itu,"
+ "h263version=(string)h263p, annex-f=(boolean)true, "
+ "annex-j=(boolean)true, annex-i=(boolean)true, annex-t=(boolean)true",
+ "rtph263ppay", "rtph263pdepay", 0, 0, FALSE);
+}
+
+GST_END_TEST;
+static const guint8 rtp_h264_frame_data[] =
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static int rtp_h264_frame_data_size = 20;
+
+static int rtp_h264_frame_count = 1;
+
+GST_START_TEST (rtp_h264)
+{
+ /* FIXME 0.11: fully specify h264 caps (and make payloader check) */
+ rtp_pipeline_test (rtp_h264_frame_data, rtp_h264_frame_data_size,
+ rtp_h264_frame_count,
+ "video/x-h264,stream-format=(string)byte-stream,alignment=(string)nal",
+ "rtph264pay", "rtph264depay", 0, 0, FALSE);
+}
+
+GST_END_TEST;
+static const guint8 rtp_h264_list_lt_mtu_frame_data[] =
+ /* not packetized, next NAL starts with 0001 */
+{ 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0xad, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x10
+};
+
+static int rtp_h264_list_lt_mtu_frame_data_size = 16;
+
+static int rtp_h264_list_lt_mtu_frame_count = 2;
+
+/* NAL = 4 bytes */
+/* also 2 bytes FU-A header each time */
+static int rtp_h264_list_lt_mtu_bytes_sent = 2 * (16 - 4);
+
+static int rtp_h264_list_lt_mtu_mtu_size = 1024;
+
+GST_START_TEST (rtp_h264_list_lt_mtu)
+{
+ /* FIXME 0.11: fully specify h264 caps (and make payloader check) */
+ rtp_pipeline_test (rtp_h264_list_lt_mtu_frame_data,
+ rtp_h264_list_lt_mtu_frame_data_size, rtp_h264_list_lt_mtu_frame_count,
+ "video/x-h264,stream-format=(string)byte-stream,alignment=(string)nal",
+ "rtph264pay", "rtph264depay",
+ rtp_h264_list_lt_mtu_bytes_sent, rtp_h264_list_lt_mtu_mtu_size, TRUE);
+}
+
+GST_END_TEST;
+static const guint8 rtp_h264_list_lt_mtu_frame_data_avc[] =
+ /* packetized data */
+{ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
+ 0xad, 0x80, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0d, 0x00
+};
+
+/* NAL = 4 bytes */
+static int rtp_h264_list_lt_mtu_bytes_sent_avc = 2 * (16 - 2 * 4);
+
+//static int rtp_h264_list_lt_mtu_mtu_size = 1024;
+
+GST_START_TEST (rtp_h264_list_lt_mtu_avc)
+{
+ /* FIXME 0.11: fully specify h264 caps (and make payloader check) */
+ rtp_pipeline_test (rtp_h264_list_lt_mtu_frame_data_avc,
+ rtp_h264_list_lt_mtu_frame_data_size, rtp_h264_list_lt_mtu_frame_count,
+ "video/x-h264,stream-format=(string)avc,alignment=(string)au,"
+ "codec_data=(buffer)01640014ffe1001867640014acd94141fb0110000003001773594000f142996001000568ebecb22c",
+ "rtph264pay", "rtph264depay",
+ rtp_h264_list_lt_mtu_bytes_sent_avc, rtp_h264_list_lt_mtu_mtu_size, TRUE);
+}
+
+GST_END_TEST;
+static const guint8 rtp_h264_list_gt_mtu_frame_data[] =
+ /* not packetized, next NAL starts with 0001 */
+{ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10
+};
+
+static int rtp_h264_list_gt_mtu_frame_data_size = 64;
+
+static int rtp_h264_list_gt_mtu_frame_count = 1;
+
+/* NAL = 4 bytes. When data does not fit into 1 mtu, 1 byte will be skipped */
+static int rtp_h264_list_gt_mtu_bytes_sent = 1 * (64 - 4) - 1;
+
+static int rtp_h264_list_gt_mtu_mty_size = 28;
+
+GST_START_TEST (rtp_h264_list_gt_mtu)
+{
+ /* FIXME 0.11: fully specify h264 caps (and make payloader check) */
+ rtp_pipeline_test (rtp_h264_list_gt_mtu_frame_data,
+ rtp_h264_list_gt_mtu_frame_data_size, rtp_h264_list_gt_mtu_frame_count,
+ "video/x-h264,stream-format=(string)byte-stream,alignment=(string)nal",
+ "rtph264pay", "rtph264depay",
+ rtp_h264_list_gt_mtu_bytes_sent, rtp_h264_list_gt_mtu_mty_size, TRUE);
+}
+
+GST_END_TEST;
+static const guint8 rtp_h264_list_gt_mtu_frame_data_avc[] =
+ /* packetized data */
+{ 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/* NAL = 4 bytes. When data does not fit into 1 mtu, 1 byte will be skipped */
+static int rtp_h264_list_gt_mtu_bytes_sent_avc = 1 * (64 - 2 * 4 - 2 * 1);
+
+GST_START_TEST (rtp_h264_list_gt_mtu_avc)
+{
+ /* FIXME 0.11: fully specify h264 caps (and make payloader check) */
+ rtp_pipeline_test (rtp_h264_list_gt_mtu_frame_data_avc,
+ rtp_h264_list_gt_mtu_frame_data_size, rtp_h264_list_gt_mtu_frame_count,
+ "video/x-h264,stream-format=(string)avc,alignment=(string)au,"
+ "codec_data=(buffer)01640014ffe1001867640014acd94141fb0110000003001773594000f142996001000568ebecb22c",
+ "rtph264pay", "rtph264depay",
+ rtp_h264_list_gt_mtu_bytes_sent_avc, rtp_h264_list_gt_mtu_mty_size, TRUE);
+}
+
+GST_END_TEST;
+
+static const guint8 rtp_L16_frame_data[] =
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static int rtp_L16_frame_data_size = 20;
+
+static int rtp_L16_frame_count = 1;
+
+GST_START_TEST (rtp_L16)
+{
+ rtp_pipeline_test (rtp_L16_frame_data, rtp_L16_frame_data_size,
+ rtp_L16_frame_count,
+ "audio/x-raw,format=S16BE,rate=1,channels=1,layout=(string)interleaved",
+ "rtpL16pay", "rtpL16depay", 0, 0, FALSE);
+}
+
+GST_END_TEST;
+
+static const guint8 rtp_L24_frame_data[] =
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static int rtp_L24_frame_data_size = 24;
+
+static int rtp_L24_frame_count = 1;
+
+GST_START_TEST (rtp_L24)
+{
+ rtp_pipeline_test (rtp_L24_frame_data, rtp_L24_frame_data_size,
+ rtp_L24_frame_count,
+ "audio/x-raw,format=S24BE,rate=1,channels=1,layout=(string)interleaved",
+ "rtpL24pay", "rtpL24depay", 0, 0, FALSE);
+}
+
+GST_END_TEST;
+static const guint8 rtp_mp2t_frame_data[] =
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static int rtp_mp2t_frame_data_size = 20;
+
+static int rtp_mp2t_frame_count = 1;
+
+GST_START_TEST (rtp_mp2t)
+{
+ rtp_pipeline_test (rtp_mp2t_frame_data, rtp_mp2t_frame_data_size,
+ rtp_mp2t_frame_count, "video/mpegts,packetsize=188,systemstream=true",
+ "rtpmp2tpay", "rtpmp2tdepay", 0, 0, FALSE);
+}
+
+GST_END_TEST;
+static const guint8 rtp_mp4v_frame_data[] =
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static int rtp_mp4v_frame_data_size = 20;
+
+static int rtp_mp4v_frame_count = 1;
+
+GST_START_TEST (rtp_mp4v)
+{
+ rtp_pipeline_test (rtp_mp4v_frame_data, rtp_mp4v_frame_data_size,
+ rtp_mp4v_frame_count, "video/mpeg,mpegversion=4,systemstream=false",
+ "rtpmp4vpay", "rtpmp4vdepay", 0, 0, FALSE);
+}
+
+GST_END_TEST;
+static const guint8 rtp_mp4v_list_frame_data[] =
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static int rtp_mp4v_list_frame_data_size = 20;
+
+static int rtp_mp4v_list_frame_count = 1;
+
+static int rtp_mp4v_list_bytes_sent = 1 * 20;
+
+GST_START_TEST (rtp_mp4v_list)
+{
+ rtp_pipeline_test (rtp_mp4v_list_frame_data, rtp_mp4v_list_frame_data_size,
+ rtp_mp4v_list_frame_count,
+ "video/mpeg,mpegversion=4,systemstream=false,codec_data=(buffer)000001b001",
+ "rtpmp4vpay", "rtpmp4vdepay", rtp_mp4v_list_bytes_sent, 0, TRUE);
+}
+
+GST_END_TEST;
+static const guint8 rtp_mp4g_frame_data[] =
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static int rtp_mp4g_frame_data_size = 20;
+
+static int rtp_mp4g_frame_count = 1;
+
+GST_START_TEST (rtp_mp4g)
+{
+ rtp_pipeline_test (rtp_mp4g_frame_data, rtp_mp4g_frame_data_size,
+ rtp_mp4g_frame_count,
+ "video/mpeg,mpegversion=4,systemstream=false,codec_data=(buffer)000001b001",
+ "rtpmp4gpay", "rtpmp4gdepay", 0, 0, FALSE);
+}
+
+GST_END_TEST;
+static const guint8 rtp_theora_frame_data[] =
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static int rtp_theora_frame_data_size = 20;
+
+static int rtp_theora_frame_count = 1;
+
+GST_START_TEST (rtp_theora)
+{
+ rtp_pipeline_test (rtp_theora_frame_data, rtp_theora_frame_data_size,
+ rtp_theora_frame_count, "video/x-theora", "rtptheorapay",
+ "rtptheoradepay", 0, 0, FALSE);
+}
+
+GST_END_TEST;
+static const guint8 rtp_vorbis_frame_data[] =
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static int rtp_vorbis_frame_data_size = 20;
+
+static int rtp_vorbis_frame_count = 1;
+
+GST_START_TEST (rtp_vorbis)
+{
+ rtp_pipeline_test (rtp_vorbis_frame_data, rtp_vorbis_frame_data_size,
+ rtp_vorbis_frame_count, "audio/x-vorbis", "rtpvorbispay",
+ "rtpvorbisdepay", 0, 0, FALSE);
+}
+
+GST_END_TEST;
+static const guint8 rtp_jpeg_frame_data[] =
+ { /* SOF */ 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0x08, 0x00, 0x08,
+ 0x03, 0x00, 0x21, 0x08, 0x01, 0x11, 0x08, 0x02, 0x11, 0x08,
+ /* DQT */ 0xFF, 0xDB, 0x00, 0x43, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* DATA */ 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static int rtp_jpeg_frame_data_size = sizeof (rtp_jpeg_frame_data);
+
+static int rtp_jpeg_frame_count = 1;
+
+GST_START_TEST (rtp_jpeg)
+{
+ rtp_pipeline_test (rtp_jpeg_frame_data, rtp_jpeg_frame_data_size,
+ rtp_jpeg_frame_count, "video/x-jpeg,height=640,width=480", "rtpjpegpay",
+ "rtpjpegdepay", 0, 0, FALSE);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (rtp_jpeg_width_greater_than_2040)
+{
+ rtp_pipeline_test (rtp_jpeg_frame_data, rtp_jpeg_frame_data_size,
+ rtp_jpeg_frame_count, "video/x-jpeg,height=2048,width=480", "rtpjpegpay",
+ "rtpjpegdepay", 0, 0, FALSE);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (rtp_jpeg_height_greater_than_2040)
+{
+ rtp_pipeline_test (rtp_jpeg_frame_data, rtp_jpeg_frame_data_size,
+ rtp_jpeg_frame_count, "video/x-jpeg,height=640,width=2048", "rtpjpegpay",
+ "rtpjpegdepay", 0, 0, FALSE);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (rtp_jpeg_width_and_height_greater_than_2040)
+{
+ rtp_pipeline_test (rtp_jpeg_frame_data, rtp_jpeg_frame_data_size,
+ rtp_jpeg_frame_count, "video/x-jpeg,height=2048,width=2048", "rtpjpegpay",
+ "rtpjpegdepay", 0, 0, FALSE);
+}
+
+GST_END_TEST;
+
+static const guint8 rtp_jpeg_list_frame_data[] =
+ { /* SOF */ 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0x08, 0x00, 0x08,
+ 0x03, 0x00, 0x21, 0x08, 0x01, 0x11, 0x08, 0x02, 0x11, 0x08,
+ /* DQT */ 0xFF, 0xDB, 0x00, 0x43, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* DATA */ 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static int rtp_jpeg_list_frame_data_size = sizeof (rtp_jpeg_list_frame_data);
+
+static int rtp_jpeg_list_frame_count = 1;
+
+static int rtp_jpeg_list_bytes_sent = 1 * sizeof (rtp_jpeg_list_frame_data);
+
+GST_START_TEST (rtp_jpeg_list)
+{
+ rtp_pipeline_test (rtp_jpeg_list_frame_data, rtp_jpeg_list_frame_data_size,
+ rtp_jpeg_list_frame_count, "video/x-jpeg,height=640,width=480",
+ "rtpjpegpay", "rtpjpegdepay", rtp_jpeg_list_bytes_sent, 0, TRUE);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (rtp_jpeg_list_width_greater_than_2040)
+{
+ rtp_pipeline_test (rtp_jpeg_list_frame_data, rtp_jpeg_list_frame_data_size,
+ rtp_jpeg_list_frame_count, "video/x-jpeg,height=2048,width=480",
+ "rtpjpegpay", "rtpjpegdepay", rtp_jpeg_list_bytes_sent, 0, TRUE);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (rtp_jpeg_list_height_greater_than_2040)
+{
+ rtp_pipeline_test (rtp_jpeg_list_frame_data, rtp_jpeg_list_frame_data_size,
+ rtp_jpeg_list_frame_count, "video/x-jpeg,height=640,width=2048",
+ "rtpjpegpay", "rtpjpegdepay", rtp_jpeg_list_bytes_sent, 0, TRUE);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (rtp_jpeg_list_width_and_height_greater_than_2040)
+{
+ rtp_pipeline_test (rtp_jpeg_list_frame_data, rtp_jpeg_list_frame_data_size,
+ rtp_jpeg_list_frame_count, "video/x-jpeg,height=2048,width=2048",
+ "rtpjpegpay", "rtpjpegdepay", rtp_jpeg_list_bytes_sent, 0, TRUE);
+}
+
+GST_END_TEST;
+
+static const guint8 rtp_g729_frame_data[] =
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static int rtp_g729_frame_data_size = 22;
+
+static int rtp_g729_frame_count = 1;
+
+GST_START_TEST (rtp_g729)
+{
+ rtp_pipeline_test (rtp_g729_frame_data, rtp_g729_frame_data_size,
+ rtp_g729_frame_count, "audio/G729,rate=8000,channels=1", "rtpg729pay",
+ "rtpg729depay", 0, 0, FALSE);
+}
+
+GST_END_TEST;
+
+/*
+ * Creates the test suite.
+ *
+ * Returns: pointer to the test suite.
+ */
+static Suite *
+rtp_payloading_suite (void)
+{
+ Suite *s = suite_create ("rtp_data_test");
+
+ TCase *tc_chain = tcase_create ("linear");
+
+ /* Set timeout to 60 seconds. */
+ tcase_set_timeout (tc_chain, 60);
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, rtp_ilbc);
+ tcase_add_test (tc_chain, rtp_gsm);
+ tcase_add_test (tc_chain, rtp_amr);
+ tcase_add_test (tc_chain, rtp_pcma);
+ tcase_add_test (tc_chain, rtp_pcmu);
+ tcase_add_test (tc_chain, rtp_mpa);
+ tcase_add_test (tc_chain, rtp_h263);
+ tcase_add_test (tc_chain, rtp_h263p);
+ tcase_add_test (tc_chain, rtp_h264);
+ tcase_add_test (tc_chain, rtp_h264_list_lt_mtu);
+ tcase_add_test (tc_chain, rtp_h264_list_lt_mtu_avc);
+ tcase_add_test (tc_chain, rtp_h264_list_gt_mtu);
+ tcase_add_test (tc_chain, rtp_h264_list_gt_mtu_avc);
+ tcase_add_test (tc_chain, rtp_L16);
+ tcase_add_test (tc_chain, rtp_L24);
+ tcase_add_test (tc_chain, rtp_mp2t);
+ tcase_add_test (tc_chain, rtp_mp4v);
+ tcase_add_test (tc_chain, rtp_mp4v_list);
+ tcase_add_test (tc_chain, rtp_mp4g);
+ tcase_add_test (tc_chain, rtp_theora);
+ tcase_add_test (tc_chain, rtp_vorbis);
+ tcase_add_test (tc_chain, rtp_jpeg);
+ tcase_add_test (tc_chain, rtp_jpeg_width_greater_than_2040);
+ tcase_add_test (tc_chain, rtp_jpeg_height_greater_than_2040);
+ tcase_add_test (tc_chain, rtp_jpeg_width_and_height_greater_than_2040);
+ tcase_add_test (tc_chain, rtp_jpeg_list);
+ tcase_add_test (tc_chain, rtp_jpeg_list_width_greater_than_2040);
+ tcase_add_test (tc_chain, rtp_jpeg_list_height_greater_than_2040);
+ tcase_add_test (tc_chain, rtp_jpeg_list_width_and_height_greater_than_2040);
+ tcase_add_test (tc_chain, rtp_g729);
+ return s;
+}
+
+GST_CHECK_MAIN (rtp_payloading)
diff --git a/tests/check/elements/rtpaux.c b/tests/check/elements/rtpaux.c
new file mode 100755
index 0000000..1f410bf
--- /dev/null
+++ b/tests/check/elements/rtpaux.c
@@ -0,0 +1,418 @@
+/* GStreamer
+ *
+ * Copyright (C) 2013 Collabora Ltd.
+ * @author Julien Isorce <julien.isorce@collabora.co.uk>
+ *
+ * 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.
+ */
+
+#include <gst/check/gstcheck.h>
+#include <gst/check/gstconsistencychecker.h>
+#include <gst/check/gsttestclock.h>
+#include <gst/rtp/gstrtpbuffer.h>
+
+static gboolean send_pipeline_eos = FALSE;
+static gboolean receive_pipeline_eos = FALSE;
+
+static void
+message_received (GstBus * bus, GstMessage * message, GstPipeline * bin)
+{
+ GST_INFO ("bus message from \"%" GST_PTR_FORMAT "\": %" GST_PTR_FORMAT,
+ GST_MESSAGE_SRC (message), message);
+
+ switch (message->type) {
+ case GST_MESSAGE_EOS:
+ if (!strcmp ("pipeline_send",
+ GST_OBJECT_NAME (GST_MESSAGE_SRC (message))))
+ send_pipeline_eos = TRUE;
+ else if (!strcmp ("pipeline_receive",
+ GST_OBJECT_NAME (GST_MESSAGE_SRC (message))))
+ receive_pipeline_eos = TRUE;
+ else
+ fail ("Unknown pipeline: %s",
+ GST_OBJECT_NAME (GST_MESSAGE_SRC (message)));
+ break;
+ case GST_MESSAGE_WARNING:{
+ GError *gerror;
+ gchar *debug;
+
+ gst_message_parse_warning (message, &gerror, &debug);
+ gst_object_default_error (GST_MESSAGE_SRC (message), gerror, debug);
+ g_error_free (gerror);
+ g_free (debug);
+ break;
+ }
+ case GST_MESSAGE_ERROR:{
+ GError *gerror;
+ gchar *debug;
+
+ gst_message_parse_error (message, &gerror, &debug);
+ gst_object_default_error (GST_MESSAGE_SRC (message), gerror, debug);
+ g_error_free (gerror);
+ g_free (debug);
+ fail ("Error!");
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+typedef struct
+{
+ guint count;
+ guint nb_packets;
+ guint drop_every_n_packets;
+} RTXSendData;
+
+static GstPadProbeReturn
+rtprtxsend_srcpad_probe (GstPad * pad, GstPadProbeInfo * info,
+ gpointer user_data)
+{
+ GstPadProbeReturn ret = GST_PAD_PROBE_OK;
+
+ if (info->type == (GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_PUSH)) {
+ GstBuffer *buffer = GST_BUFFER (info->data);
+ RTXSendData *rtxdata = (RTXSendData *) user_data;
+ GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
+ guint payload_type = 0;
+
+ gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp);
+ payload_type = gst_rtp_buffer_get_payload_type (&rtp);
+
+ /* main stream packets */
+ if (payload_type == 96) {
+ /* count packets of the main stream */
+ ++rtxdata->nb_packets;
+ /* drop some packets */
+ if (rtxdata->count < rtxdata->drop_every_n_packets) {
+ ++rtxdata->count;
+ } else {
+ /* drop a packet every 'rtxdata->count' packets */
+ rtxdata->count = 1;
+ ret = GST_PAD_PROBE_DROP;
+ }
+ } else {
+ /* retransmission packets */
+ }
+
+ gst_rtp_buffer_unmap (&rtp);
+ }
+
+ return ret;
+}
+
+static void
+on_rtpbinreceive_pad_added (GstElement * element, GstPad * newPad,
+ gpointer data)
+{
+ GstElement *rtpdepayloader = GST_ELEMENT (data);
+
+ gchar *padName = gst_pad_get_name (newPad);
+ if (g_str_has_prefix (padName, "recv_rtp_src_")) {
+ GstPad *sinkpad = gst_element_get_static_pad (rtpdepayloader, "sink");
+ gst_pad_link (newPad, sinkpad);
+ gst_object_unref (sinkpad);
+ }
+ g_free (padName);
+}
+
+static gboolean
+on_timeout (gpointer data)
+{
+ GstEvent *eos = gst_event_new_eos ();
+ if (!gst_element_send_event (GST_ELEMENT (data), eos)) {
+ GST_ERROR ("failed to send end of stream event");
+ gst_event_unref (eos);
+ }
+
+ return FALSE;
+}
+
+static GstElement *
+request_aux_receive (GstElement * rtpbin, guint sessid, GstElement * receive)
+{
+ GstElement *bin;
+ GstPad *pad;
+
+ GST_INFO ("creating AUX receiver");
+ bin = gst_bin_new (NULL);
+ gst_bin_add (GST_BIN (bin), receive);
+
+ pad = gst_element_get_static_pad (receive, "src");
+ gst_element_add_pad (bin, gst_ghost_pad_new ("src_0", pad));
+ gst_object_unref (pad);
+ pad = gst_element_get_static_pad (receive, "sink");
+ gst_element_add_pad (bin, gst_ghost_pad_new ("sink_0", pad));
+ gst_object_unref (pad);
+
+ return bin;
+}
+
+static GstElement *
+request_aux_send (GstElement * rtpbin, guint sessid, GstElement * send)
+{
+ GstElement *bin;
+ GstPad *pad;
+
+ GST_INFO ("creating AUX sender");
+ bin = gst_bin_new (NULL);
+ gst_bin_add (GST_BIN (bin), send);
+
+ pad = gst_element_get_static_pad (send, "src");
+ gst_element_add_pad (bin, gst_ghost_pad_new ("src_0", pad));
+ gst_object_unref (pad);
+ pad = gst_element_get_static_pad (send, "sink");
+ gst_element_add_pad (bin, gst_ghost_pad_new ("sink_0", pad));
+ gst_object_unref (pad);
+
+ return bin;
+}
+
+
+GST_START_TEST (test_simple_rtpbin_aux)
+{
+ GstElement *binsend, *rtpbinsend, *src, *encoder, *rtppayloader,
+ *rtprtxsend, *sendrtp_udpsink, *sendrtcp_udpsink, *sendrtcp_udpsrc;
+ GstElement *binreceive, *rtpbinreceive, *recvrtp_udpsrc, *recvrtcp_udpsrc,
+ *recvrtcp_udpsink, *rtprtxreceive, *rtpdepayloader, *decoder, *converter,
+ *sink;
+ GstBus *bussend;
+ GstBus *busreceive;
+ gboolean res;
+ GstCaps *rtpcaps = NULL;
+ GstStructure *pt_map;
+ GstStateChangeReturn state_res = GST_STATE_CHANGE_FAILURE;
+ GstPad *srcpad = NULL;
+ guint nb_rtx_send_packets = 0;
+ guint nb_rtx_recv_packets = 0;
+ RTXSendData send_rtxdata;
+ send_rtxdata.count = 1;
+ send_rtxdata.nb_packets = 0;
+ send_rtxdata.drop_every_n_packets = 50;
+
+ GST_INFO ("preparing test");
+
+ /* build pipeline */
+ binsend = gst_pipeline_new ("pipeline_send");
+ bussend = gst_element_get_bus (binsend);
+ gst_bus_add_signal_watch_full (bussend, G_PRIORITY_HIGH);
+
+ binreceive = gst_pipeline_new ("pipeline_receive");
+ busreceive = gst_element_get_bus (binreceive);
+ gst_bus_add_signal_watch_full (busreceive, G_PRIORITY_HIGH);
+
+ rtpbinsend = gst_element_factory_make ("rtpbin", "rtpbinsend");
+ g_object_set (rtpbinsend, "latency", 200, "do-retransmission", TRUE, NULL);
+ src = gst_element_factory_make ("audiotestsrc", "src");
+ encoder = gst_element_factory_make ("speexenc", "encoder");
+ rtppayloader = gst_element_factory_make ("rtpspeexpay", "rtppayloader");
+ rtprtxsend = gst_element_factory_make ("rtprtxsend", "rtprtxsend");
+ sendrtp_udpsink = gst_element_factory_make ("udpsink", "sendrtp_udpsink");
+ g_object_set (sendrtp_udpsink, "host", "127.0.0.1", NULL);
+ g_object_set (sendrtp_udpsink, "port", 5006, NULL);
+ sendrtcp_udpsink = gst_element_factory_make ("udpsink", "sendrtcp_udpsink");
+ g_object_set (sendrtcp_udpsink, "host", "127.0.0.1", NULL);
+ g_object_set (sendrtcp_udpsink, "port", 5007, NULL);
+ g_object_set (sendrtcp_udpsink, "sync", FALSE, NULL);
+ g_object_set (sendrtcp_udpsink, "async", FALSE, NULL);
+ sendrtcp_udpsrc = gst_element_factory_make ("udpsrc", "sendrtcp_udpsrc");
+ g_object_set (sendrtcp_udpsrc, "port", 5009, NULL);
+
+ rtpbinreceive = gst_element_factory_make ("rtpbin", "rtpbinreceive");
+ g_object_set (rtpbinreceive, "latency", 200, "do-retransmission", TRUE, NULL);
+ recvrtp_udpsrc = gst_element_factory_make ("udpsrc", "recvrtp_udpsrc");
+ g_object_set (recvrtp_udpsrc, "port", 5006, NULL);
+ rtpcaps =
+ gst_caps_from_string
+ ("application/x-rtp,media=(string)audio,clock-rate=(int)8000,encoding-name=(string)SPEEX,encoding-params=(string)1,octet-align=(string)1");
+ g_object_set (recvrtp_udpsrc, "caps", rtpcaps, NULL);
+ gst_caps_unref (rtpcaps);
+ recvrtcp_udpsrc = gst_element_factory_make ("udpsrc", "recvrtcp_udpsrc");
+ g_object_set (recvrtcp_udpsrc, "port", 5007, NULL);
+ recvrtcp_udpsink = gst_element_factory_make ("udpsink", "recvrtcp_udpsink");
+ g_object_set (recvrtcp_udpsink, "host", "127.0.0.1", NULL);
+ g_object_set (recvrtcp_udpsink, "port", 5009, NULL);
+ g_object_set (recvrtcp_udpsink, "sync", FALSE, NULL);
+ g_object_set (recvrtcp_udpsink, "async", FALSE, NULL);
+ rtprtxreceive = gst_element_factory_make ("rtprtxreceive", "rtprtxreceive");
+ rtpdepayloader = gst_element_factory_make ("rtpspeexdepay", "rtpdepayloader");
+ decoder = gst_element_factory_make ("speexdec", "decoder");
+ converter = gst_element_factory_make ("identity", "converter");
+ sink = gst_element_factory_make ("fakesink", "sink");
+ g_object_set (sink, "sync", TRUE, NULL);
+
+ gst_bin_add_many (GST_BIN (binsend), rtpbinsend, src, encoder, rtppayloader,
+ sendrtp_udpsink, sendrtcp_udpsink, sendrtcp_udpsrc, NULL);
+
+ gst_bin_add_many (GST_BIN (binreceive), rtpbinreceive,
+ recvrtp_udpsrc, recvrtcp_udpsrc, recvrtcp_udpsink,
+ rtpdepayloader, decoder, converter, sink, NULL);
+
+ g_signal_connect (rtpbinreceive, "pad-added",
+ G_CALLBACK (on_rtpbinreceive_pad_added), rtpdepayloader);
+
+ pt_map = gst_structure_new ("application/x-rtp-pt-map",
+ "96", G_TYPE_UINT, 99, NULL);
+ g_object_set (rtppayloader, "pt", 96, NULL);
+ g_object_set (rtppayloader, "seqnum-offset", 1, NULL);
+ g_object_set (rtprtxsend, "payload-type-map", pt_map, NULL);
+ g_object_set (rtprtxreceive, "payload-type-map", pt_map, NULL);
+ gst_structure_free (pt_map);
+
+ /* set rtp aux receive */
+ g_signal_connect (rtpbinreceive, "request-aux-receiver", (GCallback)
+ request_aux_receive, rtprtxreceive);
+ /* set rtp aux send */
+ g_signal_connect (rtpbinsend, "request-aux-sender", (GCallback)
+ request_aux_send, rtprtxsend);
+
+ /* gst-launch-1.0 rtpbin name=rtpbin audiotestsrc ! amrnbenc ! rtpamrpay ! \
+ * rtpbin.send_rtp_sink_1 rtpbin.send_rtp_src_1 ! udpsink host="127.0.0.1" \
+ * port=5002 rtpbin.send_rtcp_src_1 ! udpsink host="127.0.0.1" port=5003 \
+ * sync=false async=false udpsrc port=5007 ! rtpbin.recv_rtcp_sink_1
+ */
+
+ res = gst_element_link (src, encoder);
+ fail_unless (res == TRUE, NULL);
+ res = gst_element_link (encoder, rtppayloader);
+ fail_unless (res == TRUE, NULL);
+ res =
+ gst_element_link_pads_full (rtppayloader, "src", rtpbinsend,
+ "send_rtp_sink_0", GST_PAD_LINK_CHECK_NOTHING);
+ fail_unless (res == TRUE, NULL);
+ res =
+ gst_element_link_pads_full (rtpbinsend, "send_rtp_src_0", sendrtp_udpsink,
+ "sink", GST_PAD_LINK_CHECK_NOTHING);
+ fail_unless (res == TRUE, NULL);
+ res =
+ gst_element_link_pads_full (rtpbinsend, "send_rtcp_src_0",
+ sendrtcp_udpsink, "sink", GST_PAD_LINK_CHECK_NOTHING);
+ fail_unless (res == TRUE, NULL);
+ res =
+ gst_element_link_pads_full (sendrtcp_udpsrc, "src", rtpbinsend,
+ "recv_rtcp_sink_0", GST_PAD_LINK_CHECK_NOTHING);
+ fail_unless (res == TRUE, NULL);
+
+ srcpad = gst_element_get_static_pad (rtpbinsend, "send_rtp_src_0");
+ gst_pad_add_probe (srcpad,
+ (GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_PUSH),
+ (GstPadProbeCallback) rtprtxsend_srcpad_probe, &send_rtxdata, NULL);
+ gst_object_unref (srcpad);
+
+ /* gst-launch-1.0 rtpbin name=rtpbin udpsrc caps="application/x-rtp,media=(string)audio, \
+ * clock-rate=(int)8000,encoding-name=(string)AMR,encoding-params=(string)1,o
+ * ctet-align=(string)1" port=5002 ! rtpbin.recv_rtp_sink_1 rtpbin. ! rtpamrdepay ! \
+ * amrnbdec ! fakesink sync=True udpsrc port=5003 ! rtpbin.recv_rtcp_sink_1 \
+ * rtpbin.send_rtcp_src_1 ! udpsink host="127.0.0.1" port=5007 sync=false async=false
+ */
+
+ res =
+ gst_element_link_pads_full (recvrtp_udpsrc, "src", rtpbinreceive,
+ "recv_rtp_sink_0", GST_PAD_LINK_CHECK_NOTHING);
+ fail_unless (res == TRUE, NULL);
+ res =
+ gst_element_link_pads_full (rtpdepayloader, "src", decoder, "sink",
+ GST_PAD_LINK_CHECK_NOTHING);
+ fail_unless (res == TRUE, NULL);
+ res = gst_element_link (decoder, converter);
+ fail_unless (res == TRUE, NULL);
+ res =
+ gst_element_link_pads_full (converter, "src", sink, "sink",
+ GST_PAD_LINK_CHECK_NOTHING);
+ fail_unless (res == TRUE, NULL);
+ res =
+ gst_element_link_pads_full (recvrtcp_udpsrc, "src", rtpbinreceive,
+ "recv_rtcp_sink_0", GST_PAD_LINK_CHECK_NOTHING);
+ fail_unless (res == TRUE, NULL);
+ res =
+ gst_element_link_pads_full (rtpbinreceive, "send_rtcp_src_0",
+ recvrtcp_udpsink, "sink", GST_PAD_LINK_CHECK_NOTHING);
+ fail_unless (res == TRUE, NULL);
+
+ g_signal_connect (bussend, "message::error", (GCallback) message_received,
+ binsend);
+ g_signal_connect (bussend, "message::warning", (GCallback) message_received,
+ binsend);
+ g_signal_connect (bussend, "message::eos", (GCallback) message_received,
+ binsend);
+
+ g_signal_connect (busreceive, "message::error", (GCallback) message_received,
+ binreceive);
+ g_signal_connect (busreceive, "message::warning",
+ (GCallback) message_received, binreceive);
+ g_signal_connect (busreceive, "message::eos", (GCallback) message_received,
+ binreceive);
+
+ state_res = gst_element_set_state (binreceive, GST_STATE_PLAYING);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ state_res = gst_element_set_state (binsend, GST_STATE_PLAYING);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ g_timeout_add (5000, on_timeout, binsend);
+ g_timeout_add (5000, on_timeout, binreceive);
+
+ GST_INFO ("enter mainloop");
+ while (!send_pipeline_eos && !receive_pipeline_eos)
+ g_main_context_iteration (NULL, TRUE);
+ GST_INFO ("exit mainloop");
+
+ /* check that FB NACK is working */
+ g_object_get (G_OBJECT (rtprtxsend), "num-rtx-requests", &nb_rtx_send_packets,
+ NULL);
+ g_object_get (G_OBJECT (rtprtxreceive), "num-rtx-requests",
+ &nb_rtx_recv_packets, NULL);
+
+ state_res = gst_element_set_state (binsend, GST_STATE_NULL);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ state_res = gst_element_set_state (binreceive, GST_STATE_NULL);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ GST_INFO ("nb_rtx_send_packets %d", nb_rtx_send_packets);
+ GST_INFO ("nb_rtx_recv_packets %d", nb_rtx_recv_packets);
+ fail_if (nb_rtx_send_packets < 1);
+ fail_if (nb_rtx_recv_packets < 1);
+
+ /* cleanup */
+ gst_bus_remove_signal_watch (bussend);
+ gst_object_unref (bussend);
+ gst_object_unref (binsend);
+
+ gst_bus_remove_signal_watch (busreceive);
+ gst_object_unref (busreceive);
+ gst_object_unref (binreceive);
+}
+
+GST_END_TEST;
+
+static Suite *
+rtpaux_suite (void)
+{
+ Suite *s = suite_create ("rtpaux");
+ TCase *tc_chain = tcase_create ("general");
+
+ tcase_set_timeout (tc_chain, 10000);
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_simple_rtpbin_aux);
+
+ return s;
+}
+
+GST_CHECK_MAIN (rtpaux);
diff --git a/tests/check/elements/rtpbin.c b/tests/check/elements/rtpbin.c
new file mode 100755
index 0000000..fc52b38
--- /dev/null
+++ b/tests/check/elements/rtpbin.c
@@ -0,0 +1,724 @@
+/* GStreamer
+ *
+ * unit test for gstrtpbin
+ *
+ * Copyright (C) <2009> Wim Taymans <wim.taymans@gmail.com>
+ *
+ * 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.
+ */
+
+#include <gst/check/gstcheck.h>
+
+GST_START_TEST (test_pads)
+{
+ GstElement *element;
+ GstPad *pad;
+
+ element = gst_element_factory_make ("rtpsession", NULL);
+
+ pad = gst_element_get_request_pad (element, "recv_rtcp_sink");
+ gst_object_unref (pad);
+ gst_object_unref (element);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_cleanup_send)
+{
+ GstElement *rtpbin;
+ GstPad *rtp_sink, *rtp_src, *rtcp_src;
+ GObject *session;
+ gint count = 2;
+
+ rtpbin = gst_element_factory_make ("rtpbin", "rtpbin");
+
+ while (count--) {
+ /* request session 0 */
+ rtp_sink = gst_element_get_request_pad (rtpbin, "send_rtp_sink_0");
+ fail_unless (rtp_sink != NULL);
+ ASSERT_OBJECT_REFCOUNT (rtp_sink, "rtp_sink", 2);
+
+ /* this static pad should be created automatically now */
+ rtp_src = gst_element_get_static_pad (rtpbin, "send_rtp_src_0");
+ fail_unless (rtp_src != NULL);
+ ASSERT_OBJECT_REFCOUNT (rtp_src, "rtp_src", 2);
+
+ /* we should be able to get an internal session 0 now */
+ g_signal_emit_by_name (rtpbin, "get-internal-session", 0, &session);
+ fail_unless (session != NULL);
+ g_object_unref (session);
+
+ /* get the send RTCP pad too */
+ rtcp_src = gst_element_get_request_pad (rtpbin, "send_rtcp_src_0");
+ fail_unless (rtcp_src != NULL);
+ ASSERT_OBJECT_REFCOUNT (rtcp_src, "rtcp_src", 2);
+
+ gst_element_release_request_pad (rtpbin, rtp_sink);
+ /* we should only have our refs to the pads now */
+ ASSERT_OBJECT_REFCOUNT (rtp_sink, "rtp_sink", 1);
+ ASSERT_OBJECT_REFCOUNT (rtp_src, "rtp_src", 1);
+ ASSERT_OBJECT_REFCOUNT (rtcp_src, "rtp_src", 2);
+
+ /* the other pad should be gone now */
+ fail_unless (gst_element_get_static_pad (rtpbin, "send_rtp_src_0") == NULL);
+
+ /* internal session should still be there */
+ g_signal_emit_by_name (rtpbin, "get-internal-session", 0, &session);
+ fail_unless (session != NULL);
+ g_object_unref (session);
+
+ /* release the RTCP pad */
+ gst_element_release_request_pad (rtpbin, rtcp_src);
+ /* we should only have our refs to the pads now */
+ ASSERT_OBJECT_REFCOUNT (rtp_sink, "rtp_sink", 1);
+ ASSERT_OBJECT_REFCOUNT (rtp_src, "rtp_src", 1);
+ ASSERT_OBJECT_REFCOUNT (rtcp_src, "rtp_src", 1);
+
+ /* the session should be gone now */
+ g_signal_emit_by_name (rtpbin, "get-internal-session", 0, &session);
+ fail_unless (session == NULL);
+
+ /* unref the request pad and the static pad */
+ gst_object_unref (rtp_sink);
+ gst_object_unref (rtp_src);
+ gst_object_unref (rtcp_src);
+ }
+
+ gst_object_unref (rtpbin);
+}
+
+GST_END_TEST;
+
+typedef struct
+{
+ guint16 seqnum;
+ gboolean pad_added;
+ GstPad *pad;
+ GMutex lock;
+ GCond cond;
+ GstPad *sinkpad;
+ GList *pads;
+} CleanupData;
+
+static void
+init_data (CleanupData * data)
+{
+ data->seqnum = 10;
+ data->pad_added = FALSE;
+ g_mutex_init (&data->lock);
+ g_cond_init (&data->cond);
+ data->pads = NULL;
+}
+
+static void
+clean_data (CleanupData * data)
+{
+ g_list_foreach (data->pads, (GFunc) gst_object_unref, NULL);
+ g_list_free (data->pads);
+ g_mutex_clear (&data->lock);
+ g_cond_clear (&data->cond);
+}
+
+static guint8 rtp_packet[] = { 0x80, 0x60, 0x94, 0xbc, 0x8f, 0x37, 0x4e, 0xb8,
+ 0x44, 0xa8, 0xf3, 0x7c, 0x06, 0x6a, 0x0c, 0xce,
+ 0x13, 0x25, 0x19, 0x69, 0x1f, 0x93, 0x25, 0x9d,
+ 0x2b, 0x82, 0x31, 0x3b, 0x36, 0xc1, 0x3c, 0x13
+};
+
+static GstFlowReturn
+chain_rtp_packet (GstPad * pad, CleanupData * data)
+{
+ GstFlowReturn res;
+ static GstCaps *caps = NULL;
+ GstSegment segment;
+ GstBuffer *buffer;
+ GstMapInfo map;
+
+ if (caps == NULL) {
+ caps = gst_caps_from_string ("application/x-rtp,"
+ "media=(string)audio, clock-rate=(int)44100, "
+ "encoding-name=(string)L16, encoding-params=(string)1, channels=(int)1");
+ data->seqnum = 0;
+ }
+
+ gst_pad_send_event (pad, gst_event_new_stream_start (GST_OBJECT_NAME (pad)));
+ gst_pad_send_event (pad, gst_event_new_caps (caps));
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ gst_pad_send_event (pad, gst_event_new_segment (&segment));
+
+ buffer = gst_buffer_new_and_alloc (sizeof (rtp_packet));
+ gst_buffer_map (buffer, &map, GST_MAP_WRITE);
+ memcpy (map.data, rtp_packet, sizeof (rtp_packet));
+
+ map.data[2] = (data->seqnum >> 8) & 0xff;
+ map.data[3] = data->seqnum & 0xff;
+
+ data->seqnum++;
+ gst_buffer_unmap (buffer, &map);
+
+ res = gst_pad_chain (pad, buffer);
+
+ return res;
+}
+
+static GstFlowReturn
+dummy_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
+{
+ gst_buffer_unref (buffer);
+
+ return GST_FLOW_OK;
+}
+
+static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("application/x-rtp"));
+
+
+static GstPad *
+make_sinkpad (CleanupData * data)
+{
+ GstPad *pad;
+
+ pad = gst_pad_new_from_static_template (&sink_factory, "sink");
+
+ gst_pad_set_chain_function (pad, dummy_chain);
+ gst_pad_set_active (pad, TRUE);
+
+ data->pads = g_list_prepend (data->pads, pad);
+
+ return pad;
+}
+
+static void
+pad_added_cb (GstElement * rtpbin, GstPad * pad, CleanupData * data)
+{
+ GstPad *sinkpad;
+
+ GST_DEBUG ("pad added %s:%s\n", GST_DEBUG_PAD_NAME (pad));
+
+ if (GST_PAD_IS_SINK (pad))
+ return;
+
+ fail_unless (data->pad_added == FALSE);
+
+ sinkpad = make_sinkpad (data);
+ fail_unless (gst_pad_link (pad, sinkpad) == GST_PAD_LINK_OK);
+
+ g_mutex_lock (&data->lock);
+ data->pad_added = TRUE;
+ data->pad = pad;
+ g_cond_signal (&data->cond);
+ g_mutex_unlock (&data->lock);
+}
+
+static void
+pad_removed_cb (GstElement * rtpbin, GstPad * pad, CleanupData * data)
+{
+ GST_DEBUG ("pad removed %s:%s\n", GST_DEBUG_PAD_NAME (pad));
+
+ if (data->pad != pad)
+ return;
+
+ fail_unless (data->pad_added == TRUE);
+
+ g_mutex_lock (&data->lock);
+ data->pad_added = FALSE;
+ g_cond_signal (&data->cond);
+ g_mutex_unlock (&data->lock);
+}
+
+GST_START_TEST (test_cleanup_recv)
+{
+ GstElement *rtpbin;
+ GstPad *rtp_sink;
+ CleanupData data;
+ GstStateChangeReturn ret;
+ GstFlowReturn res;
+ gint count = 2;
+
+ init_data (&data);
+
+ rtpbin = gst_element_factory_make ("rtpbin", "rtpbin");
+
+ g_signal_connect (rtpbin, "pad-added", (GCallback) pad_added_cb, &data);
+ g_signal_connect (rtpbin, "pad-removed", (GCallback) pad_removed_cb, &data);
+
+ ret = gst_element_set_state (rtpbin, GST_STATE_PLAYING);
+ fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
+
+ while (count--) {
+ /* request session 0 */
+ rtp_sink = gst_element_get_request_pad (rtpbin, "recv_rtp_sink_0");
+ fail_unless (rtp_sink != NULL);
+ ASSERT_OBJECT_REFCOUNT (rtp_sink, "rtp_sink", 2);
+
+ /* no sourcepads are created yet */
+ fail_unless (rtpbin->numsinkpads == 1);
+ fail_unless (rtpbin->numsrcpads == 0);
+
+ res = chain_rtp_packet (rtp_sink, &data);
+ GST_DEBUG ("res %d, %s\n", res, gst_flow_get_name (res));
+ fail_unless (res == GST_FLOW_OK);
+
+ res = chain_rtp_packet (rtp_sink, &data);
+ GST_DEBUG ("res %d, %s\n", res, gst_flow_get_name (res));
+ fail_unless (res == GST_FLOW_OK);
+
+ /* we wait for the new pad to appear now */
+ g_mutex_lock (&data.lock);
+ while (!data.pad_added)
+ g_cond_wait (&data.cond, &data.lock);
+ g_mutex_unlock (&data.lock);
+
+ /* sourcepad created now */
+ fail_unless (rtpbin->numsinkpads == 1);
+ fail_unless (rtpbin->numsrcpads == 1);
+
+ /* remove the session */
+ gst_element_release_request_pad (rtpbin, rtp_sink);
+ gst_object_unref (rtp_sink);
+
+ /* pad should be gone now */
+ g_mutex_lock (&data.lock);
+ while (data.pad_added)
+ g_cond_wait (&data.cond, &data.lock);
+ g_mutex_unlock (&data.lock);
+
+ /* nothing left anymore now */
+ fail_unless (rtpbin->numsinkpads == 0);
+ fail_unless (rtpbin->numsrcpads == 0);
+ }
+
+ ret = gst_element_set_state (rtpbin, GST_STATE_NULL);
+ fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
+
+ gst_object_unref (rtpbin);
+
+ clean_data (&data);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_cleanup_recv2)
+{
+ GstElement *rtpbin;
+ GstPad *rtp_sink;
+ CleanupData data;
+ GstStateChangeReturn ret;
+ GstFlowReturn res;
+ gint count = 2;
+
+ init_data (&data);
+
+ rtpbin = gst_element_factory_make ("rtpbin", "rtpbin");
+
+ g_signal_connect (rtpbin, "pad-added", (GCallback) pad_added_cb, &data);
+ g_signal_connect (rtpbin, "pad-removed", (GCallback) pad_removed_cb, &data);
+
+ ret = gst_element_set_state (rtpbin, GST_STATE_PLAYING);
+ fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
+
+ /* request session 0 */
+ rtp_sink = gst_element_get_request_pad (rtpbin, "recv_rtp_sink_0");
+ fail_unless (rtp_sink != NULL);
+ ASSERT_OBJECT_REFCOUNT (rtp_sink, "rtp_sink", 2);
+
+ while (count--) {
+ /* no sourcepads are created yet */
+ fail_unless (rtpbin->numsinkpads == 1);
+ fail_unless (rtpbin->numsrcpads == 0);
+
+ res = chain_rtp_packet (rtp_sink, &data);
+ GST_DEBUG ("res %d, %s\n", res, gst_flow_get_name (res));
+ fail_unless (res == GST_FLOW_OK);
+
+ res = chain_rtp_packet (rtp_sink, &data);
+ GST_DEBUG ("res %d, %s\n", res, gst_flow_get_name (res));
+ fail_unless (res == GST_FLOW_OK);
+
+ /* we wait for the new pad to appear now */
+ g_mutex_lock (&data.lock);
+ while (!data.pad_added)
+ g_cond_wait (&data.cond, &data.lock);
+ g_mutex_unlock (&data.lock);
+
+ /* sourcepad created now */
+ fail_unless (rtpbin->numsinkpads == 1);
+ fail_unless (rtpbin->numsrcpads == 1);
+
+ /* change state */
+ ret = gst_element_set_state (rtpbin, GST_STATE_NULL);
+ fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
+
+ /* pad should be gone now */
+ g_mutex_lock (&data.lock);
+ while (data.pad_added)
+ g_cond_wait (&data.cond, &data.lock);
+ g_mutex_unlock (&data.lock);
+
+ /* back to playing for the next round */
+ ret = gst_element_set_state (rtpbin, GST_STATE_PLAYING);
+ fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
+ }
+
+ /* remove the session */
+ gst_element_release_request_pad (rtpbin, rtp_sink);
+ gst_object_unref (rtp_sink);
+
+ /* nothing left anymore now */
+ fail_unless (rtpbin->numsinkpads == 0);
+ fail_unless (rtpbin->numsrcpads == 0);
+
+ ret = gst_element_set_state (rtpbin, GST_STATE_NULL);
+ fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
+
+ gst_object_unref (rtpbin);
+
+ clean_data (&data);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_request_pad_by_template_name)
+{
+ GstElement *rtpbin;
+ GstPad *rtp_sink1, *rtp_sink2, *rtp_sink3;
+
+ rtpbin = gst_element_factory_make ("rtpbin", "rtpbin");
+ rtp_sink1 = gst_element_get_request_pad (rtpbin, "recv_rtp_sink_%u");
+ fail_unless (rtp_sink1 != NULL);
+ fail_unless_equals_string (GST_PAD_NAME (rtp_sink1), "recv_rtp_sink_0");
+ ASSERT_OBJECT_REFCOUNT (rtp_sink1, "rtp_sink1", 2);
+
+ rtp_sink2 = gst_element_get_request_pad (rtpbin, "recv_rtp_sink_%u");
+ fail_unless (rtp_sink2 != NULL);
+ fail_unless_equals_string (GST_PAD_NAME (rtp_sink2), "recv_rtp_sink_1");
+ ASSERT_OBJECT_REFCOUNT (rtp_sink2, "rtp_sink2", 2);
+
+ rtp_sink3 = gst_element_get_request_pad (rtpbin, "recv_rtp_sink_%u");
+ fail_unless (rtp_sink3 != NULL);
+ fail_unless_equals_string (GST_PAD_NAME (rtp_sink3), "recv_rtp_sink_2");
+ ASSERT_OBJECT_REFCOUNT (rtp_sink3, "rtp_sink3", 2);
+
+
+ gst_element_release_request_pad (rtpbin, rtp_sink2);
+ gst_element_release_request_pad (rtpbin, rtp_sink1);
+ gst_element_release_request_pad (rtpbin, rtp_sink3);
+ ASSERT_OBJECT_REFCOUNT (rtp_sink3, "rtp_sink3", 1);
+ ASSERT_OBJECT_REFCOUNT (rtp_sink2, "rtp_sink2", 1);
+ ASSERT_OBJECT_REFCOUNT (rtp_sink1, "rtp_sink", 1);
+ gst_object_unref (rtp_sink1);
+ gst_object_unref (rtp_sink2);
+ gst_object_unref (rtp_sink3);
+
+ gst_object_unref (rtpbin);
+}
+
+GST_END_TEST;
+
+static GstElement *
+encoder_cb (GstElement * rtpbin, guint sessid, GstElement * bin)
+{
+ GstPad *srcpad, *sinkpad;
+
+ fail_unless (sessid == 2);
+
+ GST_DEBUG ("making encoder");
+ sinkpad = gst_ghost_pad_new_no_target ("rtp_sink_2", GST_PAD_SINK);
+ srcpad = gst_ghost_pad_new_no_target ("rtp_src_2", GST_PAD_SRC);
+
+ gst_element_add_pad (bin, sinkpad);
+ gst_element_add_pad (bin, srcpad);
+
+ return gst_object_ref (bin);
+}
+
+static GstElement *
+encoder_cb2 (GstElement * rtpbin, guint sessid, GstElement * bin)
+{
+ GstPad *srcpad, *sinkpad;
+
+ fail_unless (sessid == 3);
+
+ GST_DEBUG ("making encoder");
+ sinkpad = gst_ghost_pad_new_no_target ("rtp_sink_3", GST_PAD_SINK);
+ srcpad = gst_ghost_pad_new_no_target ("rtp_src_3", GST_PAD_SRC);
+
+ gst_element_add_pad (bin, sinkpad);
+ gst_element_add_pad (bin, srcpad);
+
+ return gst_object_ref (bin);
+}
+
+GST_START_TEST (test_encoder)
+{
+ GstElement *rtpbin, *bin;
+ GstPad *rtp_sink1, *rtp_sink2;
+ gulong id;
+
+ bin = gst_bin_new ("rtpenc");
+
+ rtpbin = gst_element_factory_make ("rtpbin", "rtpbin");
+
+ id = g_signal_connect (rtpbin, "request-rtp-encoder", (GCallback) encoder_cb,
+ bin);
+
+ rtp_sink1 = gst_element_get_request_pad (rtpbin, "send_rtp_sink_2");
+ fail_unless (rtp_sink1 != NULL);
+ fail_unless_equals_string (GST_PAD_NAME (rtp_sink1), "send_rtp_sink_2");
+ ASSERT_OBJECT_REFCOUNT (rtp_sink1, "rtp_sink1", 2);
+
+ g_signal_handler_disconnect (rtpbin, id);
+
+ id = g_signal_connect (rtpbin, "request-rtp-encoder", (GCallback) encoder_cb2,
+ bin);
+
+ rtp_sink2 = gst_element_get_request_pad (rtpbin, "send_rtp_sink_3");
+ fail_unless (rtp_sink2 != NULL);
+
+ /* remove the session */
+ gst_element_release_request_pad (rtpbin, rtp_sink1);
+ gst_object_unref (rtp_sink1);
+
+ gst_element_release_request_pad (rtpbin, rtp_sink2);
+ gst_object_unref (rtp_sink2);
+
+ /* nothing left anymore now */
+ fail_unless (rtpbin->numsinkpads == 0);
+ fail_unless (rtpbin->numsrcpads == 0);
+
+ gst_object_unref (rtpbin);
+ gst_object_unref (bin);
+}
+
+GST_END_TEST;
+
+static GstElement *
+decoder_cb (GstElement * rtpbin, guint sessid, gpointer user_data)
+{
+ GstElement *bin;
+ GstPad *srcpad, *sinkpad;
+
+ bin = gst_bin_new (NULL);
+
+ GST_DEBUG ("making decoder");
+ sinkpad = gst_ghost_pad_new_no_target ("rtp_sink", GST_PAD_SINK);
+ srcpad = gst_ghost_pad_new_no_target ("rtp_src", GST_PAD_SRC);
+
+ gst_element_add_pad (bin, sinkpad);
+ gst_element_add_pad (bin, srcpad);
+
+ return bin;
+}
+
+GST_START_TEST (test_decoder)
+{
+ GstElement *rtpbin;
+ GstPad *rtp_sink1, *rtp_sink2;
+ gulong id;
+
+
+ rtpbin = gst_element_factory_make ("rtpbin", "rtpbin");
+
+ id = g_signal_connect (rtpbin, "request-rtp-decoder", (GCallback) decoder_cb,
+ NULL);
+
+ rtp_sink1 = gst_element_get_request_pad (rtpbin, "recv_rtp_sink_2");
+ fail_unless (rtp_sink1 != NULL);
+ fail_unless_equals_string (GST_PAD_NAME (rtp_sink1), "recv_rtp_sink_2");
+ ASSERT_OBJECT_REFCOUNT (rtp_sink1, "rtp_sink1", 2);
+
+ rtp_sink2 = gst_element_get_request_pad (rtpbin, "recv_rtp_sink_3");
+ fail_unless (rtp_sink2 != NULL);
+
+ g_signal_handler_disconnect (rtpbin, id);
+
+ /* remove the session */
+ gst_element_release_request_pad (rtpbin, rtp_sink1);
+ gst_object_unref (rtp_sink1);
+
+ gst_element_release_request_pad (rtpbin, rtp_sink2);
+ gst_object_unref (rtp_sink2);
+
+ /* nothing left anymore now */
+ fail_unless (rtpbin->numsinkpads == 0);
+ fail_unless (rtpbin->numsrcpads == 0);
+
+ gst_object_unref (rtpbin);
+}
+
+GST_END_TEST;
+
+static GstElement *
+aux_sender_cb (GstElement * rtpbin, guint sessid, gpointer user_data)
+{
+ GstElement *bin;
+ GstPad *srcpad, *sinkpad;
+
+ bin = gst_bin_new (NULL);
+
+ GST_DEBUG ("making AUX sender");
+ sinkpad = gst_ghost_pad_new_no_target ("sink_2", GST_PAD_SINK);
+ gst_element_add_pad (bin, sinkpad);
+
+ srcpad = gst_ghost_pad_new_no_target ("src_2", GST_PAD_SRC);
+ gst_element_add_pad (bin, srcpad);
+ srcpad = gst_ghost_pad_new_no_target ("src_1", GST_PAD_SRC);
+ gst_element_add_pad (bin, srcpad);
+ srcpad = gst_ghost_pad_new_no_target ("src_3", GST_PAD_SRC);
+ gst_element_add_pad (bin, srcpad);
+
+ return bin;
+}
+
+GST_START_TEST (test_aux_sender)
+{
+ GstElement *rtpbin;
+ GstPad *rtp_sink1, *rtp_src, *rtcp_src;
+ gulong id;
+
+ rtpbin = gst_element_factory_make ("rtpbin", "rtpbin");
+
+ id = g_signal_connect (rtpbin, "request-aux-sender",
+ (GCallback) aux_sender_cb, NULL);
+
+ rtp_sink1 = gst_element_get_request_pad (rtpbin, "send_rtp_sink_2");
+ fail_unless (rtp_sink1 != NULL);
+ fail_unless_equals_string (GST_PAD_NAME (rtp_sink1), "send_rtp_sink_2");
+ ASSERT_OBJECT_REFCOUNT (rtp_sink1, "rtp_sink1", 2);
+
+ g_signal_handler_disconnect (rtpbin, id);
+
+ rtp_src = gst_element_get_static_pad (rtpbin, "send_rtp_src_2");
+ fail_unless (rtp_src != NULL);
+ gst_object_unref (rtp_src);
+
+ rtp_src = gst_element_get_static_pad (rtpbin, "send_rtp_src_1");
+ fail_unless (rtp_src != NULL);
+ gst_object_unref (rtp_src);
+
+ rtcp_src = gst_element_get_request_pad (rtpbin, "send_rtcp_src_1");
+ fail_unless (rtcp_src != NULL);
+ gst_element_release_request_pad (rtpbin, rtcp_src);
+ gst_object_unref (rtcp_src);
+
+ rtp_src = gst_element_get_static_pad (rtpbin, "send_rtp_src_3");
+ fail_unless (rtp_src != NULL);
+ gst_object_unref (rtp_src);
+
+ /* remove the session */
+ gst_element_release_request_pad (rtpbin, rtp_sink1);
+ gst_object_unref (rtp_sink1);
+
+ gst_object_unref (rtpbin);
+}
+
+GST_END_TEST;
+
+static GstElement *
+aux_receiver_cb (GstElement * rtpbin, guint sessid, gpointer user_data)
+{
+ GstElement *bin;
+ GstPad *srcpad, *sinkpad;
+
+ bin = gst_bin_new (NULL);
+
+ GST_DEBUG ("making AUX receiver");
+ srcpad = gst_ghost_pad_new_no_target ("src_2", GST_PAD_SRC);
+ gst_element_add_pad (bin, srcpad);
+
+ sinkpad = gst_ghost_pad_new_no_target ("sink_2", GST_PAD_SINK);
+ gst_element_add_pad (bin, sinkpad);
+ sinkpad = gst_ghost_pad_new_no_target ("sink_1", GST_PAD_SINK);
+ gst_element_add_pad (bin, sinkpad);
+ sinkpad = gst_ghost_pad_new_no_target ("sink_3", GST_PAD_SINK);
+ gst_element_add_pad (bin, sinkpad);
+
+ return bin;
+}
+
+GST_START_TEST (test_aux_receiver)
+{
+ GstElement *rtpbin;
+ GstPad *rtp_sink1, *rtp_sink2, *rtcp_sink;
+ gulong id;
+
+ rtpbin = gst_element_factory_make ("rtpbin", "rtpbin");
+
+ id = g_signal_connect (rtpbin, "request-aux-receiver",
+ (GCallback) aux_receiver_cb, NULL);
+
+ rtp_sink1 = gst_element_get_request_pad (rtpbin, "recv_rtp_sink_2");
+ fail_unless (rtp_sink1 != NULL);
+
+ rtp_sink2 = gst_element_get_request_pad (rtpbin, "recv_rtp_sink_1");
+ fail_unless (rtp_sink2 != NULL);
+
+ g_signal_handler_disconnect (rtpbin, id);
+
+ rtcp_sink = gst_element_get_request_pad (rtpbin, "recv_rtcp_sink_1");
+ fail_unless (rtcp_sink != NULL);
+ gst_element_release_request_pad (rtpbin, rtcp_sink);
+ gst_object_unref (rtcp_sink);
+
+ /* remove the session */
+ gst_element_release_request_pad (rtpbin, rtp_sink1);
+ gst_object_unref (rtp_sink1);
+ gst_element_release_request_pad (rtpbin, rtp_sink2);
+ gst_object_unref (rtp_sink2);
+
+ gst_object_unref (rtpbin);
+}
+
+GST_END_TEST;
+
+static Suite *
+gstrtpbin_suite (void)
+{
+ Suite *s = suite_create ("rtpbin");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_pads);
+ tcase_add_test (tc_chain, test_cleanup_send);
+ tcase_add_test (tc_chain, test_cleanup_recv);
+ tcase_add_test (tc_chain, test_cleanup_recv2);
+ tcase_add_test (tc_chain, test_request_pad_by_template_name);
+ tcase_add_test (tc_chain, test_encoder);
+ tcase_add_test (tc_chain, test_decoder);
+ tcase_add_test (tc_chain, test_aux_sender);
+ tcase_add_test (tc_chain, test_aux_receiver);
+
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ int nf;
+
+ Suite *s = gstrtpbin_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ srunner_run_all (sr, CK_NORMAL);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}
diff --git a/tests/check/elements/rtpbin_buffer_list.c b/tests/check/elements/rtpbin_buffer_list.c
new file mode 100644
index 0000000..b6a7793
--- /dev/null
+++ b/tests/check/elements/rtpbin_buffer_list.c
@@ -0,0 +1,335 @@
+/* GStreamer
+ *
+ * Unit test for gstrtpbin sending rtp packets using GstBufferList.
+ * Copyright (C) 2009 Branko Subasic <branko dot subasic at axis dot com>
+ *
+ * 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.
+ */
+
+#include <gst/check/gstcheck.h>
+
+#include <gst/rtp/gstrtpbuffer.h>
+
+
+#if 0
+
+/* This test makes sure that RTP packets sent as buffer lists are sent through
+ * the rtpbin as they are supposed to, and not corrupted in any way.
+ */
+
+
+#define TEST_CAPS \
+ "application/x-rtp, " \
+ "media=(string)video, " \
+ "clock-rate=(int)90000, " \
+ "encoding-name=(string)H264, " \
+ "profile-level-id=(string)4d4015, " \
+ "payload=(int)96, " \
+ "ssrc=(guint)2633237432, " \
+ "clock-base=(guint)1868267015, " \
+ "seqnum-base=(guint)54229"
+
+
+/* RTP headers and the first 2 bytes of the payload (FU indicator and FU header)
+ */
+static const guint8 rtp_header[2][14] = {
+ {0x80, 0x60, 0xbb, 0xb7, 0x5c, 0xe9, 0x09,
+ 0x0d, 0xf5, 0x9c, 0x43, 0x55, 0x1c, 0x86},
+ {0x80, 0x60, 0xbb, 0xb8, 0x5c, 0xe9, 0x09,
+ 0x0d, 0xf5, 0x9c, 0x43, 0x55, 0x1c, 0x46}
+};
+
+static const guint rtp_header_len[] = {
+ sizeof rtp_header[0],
+ sizeof rtp_header[1]
+};
+
+static GstBuffer *header_buffer[2] = { NULL, NULL };
+
+
+/* Some payload.
+ */
+static const char *payload =
+ "0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF"
+ "0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF"
+ "0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF"
+ "0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF"
+ "0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF"
+ "0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF"
+ "0123456789ABSDEF0123456";
+
+static const guint payload_offset[] = {
+ 0, 498
+};
+
+static const guint payload_len[] = {
+ 498, 5
+};
+
+
+static GstBuffer *original_buffer = NULL;
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("application/x-rtp"));
+
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("application/x-rtp"));
+
+
+static GstBuffer *
+_create_original_buffer (void)
+{
+ GstCaps *caps;
+
+ if (original_buffer != NULL)
+ return original_buffer;
+
+ original_buffer = gst_buffer_new ();
+ fail_unless (original_buffer != NULL);
+
+ gst_buffer_set_data (original_buffer, (guint8 *) payload, strlen (payload));
+ GST_BUFFER_TIMESTAMP (original_buffer) =
+ gst_clock_get_internal_time (gst_system_clock_obtain ());
+
+ caps = gst_caps_from_string (TEST_CAPS);
+ fail_unless (caps != NULL);
+ gst_buffer_set_caps (original_buffer, caps);
+ gst_caps_unref (caps);
+
+ return original_buffer;
+}
+
+static GstBufferList *
+_create_buffer_list (void)
+{
+ GstBufferList *list;
+ GstBufferListIterator *it;
+ GstBuffer *orig_buffer;
+ GstBuffer *buffer;
+
+ orig_buffer = _create_original_buffer ();
+ fail_if (orig_buffer == NULL);
+
+ list = gst_buffer_list_new ();
+ fail_if (list == NULL);
+
+ it = gst_buffer_list_iterate (list);
+ fail_if (it == NULL);
+
+ /*** First group, i.e. first packet. **/
+ gst_buffer_list_iterator_add_group (it);
+
+ /* Create buffer with RTP header and add it to the 1st group */
+ buffer = gst_buffer_new ();
+ GST_BUFFER_MALLOCDATA (buffer) = g_memdup (&rtp_header[0], rtp_header_len[0]);
+ GST_BUFFER_DATA (buffer) = GST_BUFFER_MALLOCDATA (buffer);
+ GST_BUFFER_SIZE (buffer) = rtp_header_len[0];
+ gst_buffer_copy_metadata (buffer, orig_buffer, GST_BUFFER_COPY_ALL);
+ header_buffer[0] = buffer;
+ gst_buffer_list_iterator_add (it, buffer);
+
+ /* Create the payload buffer and add it to the 1st group
+ */
+ buffer =
+ gst_buffer_create_sub (orig_buffer, payload_offset[0], payload_len[0]);
+ fail_if (buffer == NULL);
+ gst_buffer_list_iterator_add (it, buffer);
+
+
+ /*** Second group, i.e. second packet. ***/
+
+ /* Create a new group to hold the rtp header and the payload */
+ gst_buffer_list_iterator_add_group (it);
+
+ /* Create buffer with RTP header and add it to the 2nd group */
+ buffer = gst_buffer_new ();
+ GST_BUFFER_MALLOCDATA (buffer) = g_memdup (&rtp_header[1], rtp_header_len[1]);
+ GST_BUFFER_DATA (buffer) = GST_BUFFER_MALLOCDATA (buffer);
+ GST_BUFFER_SIZE (buffer) = rtp_header_len[1];
+ gst_buffer_copy_metadata (buffer, orig_buffer, GST_BUFFER_COPY_ALL);
+ header_buffer[1] = buffer;
+
+ /* Add the rtp header to the buffer list */
+ gst_buffer_list_iterator_add (it, buffer);
+
+ /* Create the payload buffer and add it to the 2d group
+ */
+ buffer =
+ gst_buffer_create_sub (orig_buffer, payload_offset[1], payload_len[1]);
+ fail_if (buffer == NULL);
+ gst_buffer_list_iterator_add (it, buffer);
+
+ gst_buffer_list_iterator_free (it);
+
+ return list;
+}
+
+
+static void
+_check_header (GstBuffer * buffer, guint index)
+{
+ guint8 *data;
+
+ fail_if (buffer == NULL);
+ fail_unless (index < 2);
+
+ fail_unless (GST_BUFFER_SIZE (buffer) == rtp_header_len[index]);
+
+ /* Can't do a memcmp() on the whole header, cause the SSRC (bytes 8-11) will
+ * most likely be changed in gstrtpbin.
+ */
+ fail_unless ((data = GST_BUFFER_DATA (buffer)) != NULL);
+ fail_unless_equals_uint64 (*(guint64 *) data, *(guint64 *) rtp_header[index]);
+ fail_unless (*(guint16 *) (data + 12) ==
+ *(guint16 *) (rtp_header[index] + 12));
+}
+
+
+static void
+_check_payload (GstBuffer * buffer, guint index)
+{
+ fail_if (buffer == NULL);
+ fail_unless (index < 2);
+
+ fail_unless (GST_BUFFER_SIZE (buffer) == payload_len[index]);
+ fail_if (GST_BUFFER_DATA (buffer) !=
+ (gpointer) (payload + payload_offset[index]));
+ fail_if (memcmp (GST_BUFFER_DATA (buffer), payload + payload_offset[index],
+ payload_len[index]));
+}
+
+
+static void
+_check_group (GstBufferListIterator * it, guint index, GstCaps * caps)
+{
+ GstBuffer *buffer;
+
+ fail_unless (it != NULL);
+ fail_unless (gst_buffer_list_iterator_n_buffers (it) == 2);
+ fail_unless (caps != NULL);
+
+ fail_unless ((buffer = gst_buffer_list_iterator_next (it)) != NULL);
+
+ fail_unless (GST_BUFFER_TIMESTAMP (buffer) ==
+ GST_BUFFER_TIMESTAMP (original_buffer));
+
+ fail_unless (gst_caps_is_equal (GST_BUFFER_CAPS (original_buffer),
+ GST_BUFFER_CAPS (buffer)));
+
+ _check_header (buffer, index);
+
+ fail_unless ((buffer = gst_buffer_list_iterator_next (it)) != NULL);
+ _check_payload (buffer, index);
+}
+
+
+static GstFlowReturn
+_sink_chain_list (GstPad * pad, GstBufferList * list)
+{
+ GstCaps *caps;
+ GstBufferListIterator *it;
+
+ caps = gst_caps_from_string (TEST_CAPS);
+ fail_unless (caps != NULL);
+
+ fail_unless (GST_IS_BUFFER_LIST (list));
+ fail_unless (gst_buffer_list_n_groups (list) == 2);
+
+ it = gst_buffer_list_iterate (list);
+ fail_if (it == NULL);
+
+ fail_unless (gst_buffer_list_iterator_next_group (it));
+ _check_group (it, 0, caps);
+
+ fail_unless (gst_buffer_list_iterator_next_group (it));
+ _check_group (it, 1, caps);
+
+ gst_caps_unref (caps);
+ gst_buffer_list_iterator_free (it);
+
+ gst_buffer_list_unref (list);
+
+ return GST_FLOW_OK;
+}
+
+
+static void
+_set_chain_functions (GstPad * pad)
+{
+ gst_pad_set_chain_list_function (pad, _sink_chain_list);
+}
+
+
+GST_START_TEST (test_bufferlist)
+{
+ GstElement *rtpbin;
+ GstPad *sinkpad;
+ GstPad *srcpad;
+ GstBufferList *list;
+
+ list = _create_buffer_list ();
+ fail_unless (list != NULL);
+
+ rtpbin = gst_check_setup_element ("gstrtpbin");
+
+ srcpad =
+ gst_check_setup_src_pad_by_name (rtpbin, &srctemplate, "send_rtp_sink_0");
+ fail_if (srcpad == NULL);
+ sinkpad =
+ gst_check_setup_sink_pad_by_name (rtpbin, &sinktemplate,
+ "send_rtp_src_0");
+ fail_if (sinkpad == NULL);
+
+ _set_chain_functions (sinkpad);
+
+ gst_pad_set_active (sinkpad, TRUE);
+ gst_element_set_state (rtpbin, GST_STATE_PLAYING);
+ fail_unless (gst_pad_push_list (srcpad, list) == GST_FLOW_OK);
+ gst_pad_set_active (sinkpad, FALSE);
+
+ gst_check_teardown_pad_by_name (rtpbin, "send_rtp_src_0");
+ gst_check_teardown_pad_by_name (rtpbin, "send_rtp_sink_0");
+ gst_check_teardown_element (rtpbin);
+}
+
+GST_END_TEST;
+
+#endif
+
+
+static Suite *
+bufferlist_suite (void)
+{
+ Suite *s = suite_create ("BufferList");
+
+ TCase *tc_chain = tcase_create ("general");
+
+ /* time out after 30s. */
+ tcase_set_timeout (tc_chain, 10);
+
+ suite_add_tcase (s, tc_chain);
+#if 0
+ tcase_add_test (tc_chain, test_bufferlist);
+#endif
+
+ return s;
+}
+
+GST_CHECK_MAIN (bufferlist);
diff --git a/tests/check/elements/rtpcollision.c b/tests/check/elements/rtpcollision.c
new file mode 100755
index 0000000..e9528f9
--- /dev/null
+++ b/tests/check/elements/rtpcollision.c
@@ -0,0 +1,462 @@
+/* GStreamer
+ *
+ * Copyright (C) 2013 Collabora Ltd.
+ * @author Julien Isorce <julien.isorce@collabora.co.uk>
+ *
+ * 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.
+ */
+
+#include <gst/check/gstcheck.h>
+#include <gst/net/gstnetaddressmeta.h>
+#include <gst/rtp/gstrtpbuffer.h>
+#include <gst/rtp/gstrtcpbuffer.h>
+
+static GMainLoop *main_loop;
+static GstPad *srcpad;
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("application/x-rtcp")
+ );
+
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("application/x-rtcp")
+ );
+
+static void
+message_received (GstBus * bus, GstMessage * message, GstPipeline * bin)
+{
+ GST_INFO ("bus message from \"%" GST_PTR_FORMAT "\": %" GST_PTR_FORMAT,
+ GST_MESSAGE_SRC (message), message);
+
+ switch (message->type) {
+ case GST_MESSAGE_EOS:
+ g_main_loop_quit (main_loop);
+ break;
+ case GST_MESSAGE_WARNING:{
+ GError *gerror;
+ gchar *debug;
+
+ gst_message_parse_warning (message, &gerror, &debug);
+ gst_object_default_error (GST_MESSAGE_SRC (message), gerror, debug);
+ g_error_free (gerror);
+ g_free (debug);
+ break;
+ }
+ case GST_MESSAGE_ERROR:{
+ GError *gerror;
+ gchar *debug;
+
+ gst_message_parse_error (message, &gerror, &debug);
+ gst_object_default_error (GST_MESSAGE_SRC (message), gerror, debug);
+ g_error_free (gerror);
+ g_free (debug);
+ g_main_loop_quit (main_loop);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static GstBuffer *
+create_rtcp_app (guint32 ssrc, guint count)
+{
+ GInetAddress *inet_addr_0;
+ guint16 port = 5678 + count;
+ GSocketAddress *socket_addr_0;
+ GstBuffer *rtcp_buffer;
+ GstRTCPPacket *rtcp_packet = NULL;
+ GstRTCPBuffer rtcp = GST_RTCP_BUFFER_INIT;
+
+ inet_addr_0 = g_inet_address_new_from_string ("192.168.1.1");
+ socket_addr_0 = g_inet_socket_address_new (inet_addr_0, port);
+ g_object_unref (inet_addr_0);
+
+ rtcp_buffer = gst_rtcp_buffer_new (1400);
+ gst_buffer_add_net_address_meta (rtcp_buffer, socket_addr_0);
+ g_object_unref (socket_addr_0);
+
+ /* need to begin with rr */
+ gst_rtcp_buffer_map (rtcp_buffer, GST_MAP_READWRITE, &rtcp);
+ rtcp_packet = g_slice_new0 (GstRTCPPacket);
+ gst_rtcp_buffer_add_packet (&rtcp, GST_RTCP_TYPE_RR, rtcp_packet);
+ gst_rtcp_packet_rr_set_ssrc (rtcp_packet, ssrc);
+ g_slice_free (GstRTCPPacket, rtcp_packet);
+
+ /* useful to make the rtcp buffer valid */
+ rtcp_packet = g_slice_new0 (GstRTCPPacket);
+ gst_rtcp_buffer_add_packet (&rtcp, GST_RTCP_TYPE_APP, rtcp_packet);
+ g_slice_free (GstRTCPPacket, rtcp_packet);
+ gst_rtcp_buffer_unmap (&rtcp);
+
+ return rtcp_buffer;
+}
+
+static guint nb_ssrc_changes;
+static guint ssrc_prev;
+
+static GstPadProbeReturn
+rtpsession_sinkpad_probe (GstPad * pad, GstPadProbeInfo * info,
+ gpointer user_data)
+{
+ GstPadProbeReturn ret = GST_PAD_PROBE_OK;
+
+ if (info->type == (GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_PUSH)) {
+ GstBuffer *buffer = GST_BUFFER (info->data);
+ GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
+ GstBuffer *rtcp_buffer = 0;
+ guint ssrc = 0;
+
+ /* retrieve current ssrc */
+ gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp);
+ ssrc = gst_rtp_buffer_get_ssrc (&rtp);
+ gst_rtp_buffer_unmap (&rtp);
+
+ /* if not first buffer, check that our ssrc has changed */
+ if (ssrc_prev != -1 && ssrc != ssrc_prev)
+ ++nb_ssrc_changes;
+
+ /* update prev ssrc */
+ ssrc_prev = ssrc;
+
+ /* feint a collision on recv_rtcp_sink pad of gstrtpsession
+ * (note that after being marked as collied the rtpsession ignores
+ * all non bye packets)
+ */
+ rtcp_buffer = create_rtcp_app (ssrc, nb_ssrc_changes);
+
+ /* push collied packet on recv_rtcp_sink */
+ gst_pad_push (srcpad, rtcp_buffer);
+ }
+
+ return ret;
+}
+
+static GstFlowReturn
+fake_udp_sink_chain_func (GstPad * pad, GstObject * parent, GstBuffer * buffer)
+{
+ gst_buffer_unref (buffer);
+ return GST_FLOW_OK;
+}
+
+/* This test build the pipeline audiotestsrc ! speexenc ! rtpspeexpay ! \
+ * rtpsession ! fakesink
+ * It manually pushs buffer into rtpsession with same ssrc but different
+ * ip so that collision can be detected
+ * The test checks that the payloader change their ssrc
+ */
+GST_START_TEST (test_master_ssrc_collision)
+{
+ GstElement *bin, *src, *encoder, *rtppayloader, *rtpsession, *sink;
+ GstBus *bus = NULL;
+ gboolean res = FALSE;
+ GstSegment segment;
+ GstPad *sinkpad = NULL;
+ GstPad *rtcp_sinkpad = NULL;
+ GstPad *fake_udp_sinkpad = NULL;
+ GstPad *rtcp_srcpad = NULL;
+ GstStateChangeReturn state_res = GST_STATE_CHANGE_FAILURE;
+
+ GST_INFO ("preparing test");
+
+ nb_ssrc_changes = 0;
+ ssrc_prev = -1;
+
+ /* build pipeline */
+ bin = gst_pipeline_new ("pipeline");
+ bus = gst_element_get_bus (bin);
+ gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);
+
+ src = gst_element_factory_make ("audiotestsrc", "src");
+ g_object_set (src, "num-buffers", 5, NULL);
+ encoder = gst_element_factory_make ("speexenc", NULL);
+ rtppayloader = gst_element_factory_make ("rtpspeexpay", NULL);
+ g_object_set (rtppayloader, "pt", 96, NULL);
+ rtpsession = gst_element_factory_make ("rtpsession", NULL);
+ sink = gst_element_factory_make ("fakesink", "sink");
+ gst_bin_add_many (GST_BIN (bin), src, encoder, rtppayloader,
+ rtpsession, sink, NULL);
+
+ /* link elements */
+ res = gst_element_link (src, encoder);
+ fail_unless (res == TRUE, NULL);
+ res = gst_element_link (encoder, rtppayloader);
+ fail_unless (res == TRUE, NULL);
+ res = gst_element_link_pads_full (rtppayloader, "src",
+ rtpsession, "send_rtp_sink", GST_PAD_LINK_CHECK_NOTHING);
+ fail_unless (res == TRUE, NULL);
+ res = gst_element_link_pads_full (rtpsession, "send_rtp_src",
+ sink, "sink", GST_PAD_LINK_CHECK_NOTHING);
+ fail_unless (res == TRUE, NULL);
+
+ /* add probe on rtpsession sink pad to induce collision */
+ sinkpad = gst_element_get_static_pad (rtpsession, "send_rtp_sink");
+ gst_pad_add_probe (sinkpad,
+ (GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_PUSH),
+ (GstPadProbeCallback) rtpsession_sinkpad_probe, NULL, NULL);
+ gst_object_unref (sinkpad);
+
+ /* setup rtcp link */
+ srcpad = gst_pad_new_from_static_template (&srctemplate, "src");
+ rtcp_sinkpad = gst_element_get_request_pad (rtpsession, "recv_rtcp_sink");
+ fail_unless (gst_pad_link (srcpad, rtcp_sinkpad) == GST_PAD_LINK_OK, NULL);
+ gst_object_unref (rtcp_sinkpad);
+ res = gst_pad_set_active (srcpad, TRUE);
+ fail_if (res == FALSE);
+ res =
+ gst_pad_push_event (srcpad,
+ gst_event_new_stream_start ("my_rtcp_stream_id"));
+ fail_if (res == FALSE);
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ res = gst_pad_push_event (srcpad, gst_event_new_segment (&segment));
+ fail_if (res == FALSE);
+
+ fake_udp_sinkpad = gst_pad_new_from_static_template (&sinktemplate, "sink");
+ gst_pad_set_chain_function (fake_udp_sinkpad, fake_udp_sink_chain_func);
+ rtcp_srcpad = gst_element_get_request_pad (rtpsession, "send_rtcp_src");
+ fail_unless (gst_pad_link (rtcp_srcpad, fake_udp_sinkpad) == GST_PAD_LINK_OK,
+ NULL);
+ gst_object_unref (rtcp_srcpad);
+ res = gst_pad_set_active (fake_udp_sinkpad, TRUE);
+ fail_if (res == FALSE);
+
+ /* connect messages */
+ main_loop = g_main_loop_new (NULL, FALSE);
+ g_signal_connect (bus, "message::error", (GCallback) message_received, bin);
+ g_signal_connect (bus, "message::warning", (GCallback) message_received, bin);
+ g_signal_connect (bus, "message::eos", (GCallback) message_received, bin);
+
+ state_res = gst_element_set_state (bin, GST_STATE_PLAYING);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ GST_INFO ("running main loop");
+ g_main_loop_run (main_loop);
+
+ state_res = gst_element_set_state (bin, GST_STATE_NULL);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ /* cleanup */
+ gst_object_unref (srcpad);
+ gst_object_unref (fake_udp_sinkpad);
+ g_main_loop_unref (main_loop);
+ gst_bus_remove_signal_watch (bus);
+ gst_object_unref (bus);
+ gst_object_unref (bin);
+
+ /* check results */
+ fail_unless_equals_int (nb_ssrc_changes, 7);
+}
+
+GST_END_TEST;
+
+static guint ssrc_before;
+static guint ssrc_after;
+static guint rtx_ssrc_before;
+static guint rtx_ssrc_after;
+
+static GstPadProbeReturn
+rtpsession_sinkpad_probe2 (GstPad * pad, GstPadProbeInfo * info,
+ gpointer user_data)
+{
+ GstPadProbeReturn ret = GST_PAD_PROBE_OK;
+
+ if (info->type == (GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_PUSH)) {
+ GstBuffer *buffer = GST_BUFFER (info->data);
+ GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
+ guint payload_type = 0;
+
+ static gint i = 0;
+
+ /* retrieve current ssrc for retransmission stream only */
+ gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp);
+ payload_type = gst_rtp_buffer_get_payload_type (&rtp);
+ if (payload_type == 99) {
+ if (i < 3)
+ rtx_ssrc_before = gst_rtp_buffer_get_ssrc (&rtp);
+ else
+ rtx_ssrc_after = gst_rtp_buffer_get_ssrc (&rtp);
+ } else {
+ /* ask to retransmit every packet */
+ GstEvent *event = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
+ gst_structure_new ("GstRTPRetransmissionRequest",
+ "seqnum", G_TYPE_UINT, gst_rtp_buffer_get_seq (&rtp),
+ "ssrc", G_TYPE_UINT, gst_rtp_buffer_get_ssrc (&rtp),
+ NULL));
+ gst_pad_push_event (pad, event);
+
+ if (i < 3)
+ ssrc_before = gst_rtp_buffer_get_ssrc (&rtp);
+ else
+ ssrc_after = gst_rtp_buffer_get_ssrc (&rtp);
+ }
+ gst_rtp_buffer_unmap (&rtp);
+
+ /* feint a collision on recv_rtcp_sink pad of gstrtpsession
+ * (note that after being marked as collied the rtpsession ignores
+ * all non bye packets)
+ */
+ if (i == 2) {
+ GstBuffer *rtcp_buffer = create_rtcp_app (rtx_ssrc_before, 0);
+
+ /* push collied packet on recv_rtcp_sink */
+ gst_pad_push (srcpad, rtcp_buffer);
+ }
+
+ ++i;
+ }
+
+ return ret;
+}
+
+/* This test build the pipeline audiotestsrc ! speexenc ! rtpspeexpay ! \
+ * rtprtxsend ! rtpsession ! fakesink
+ * It manually pushs buffer into rtpsession with same ssrc than rtx stream
+ * but different ip so that collision can be detected
+ * The test checks that the rtx elements changes its ssrc whereas
+ * the payloader keeps its master ssrc
+ */
+GST_START_TEST (test_rtx_ssrc_collision)
+{
+ GstElement *bin, *src, *encoder, *rtppayloader, *rtprtxsend, *rtpsession,
+ *sink;
+ GstBus *bus = NULL;
+ gboolean res = FALSE;
+ GstSegment segment;
+ GstPad *sinkpad = NULL;
+ GstPad *rtcp_sinkpad = NULL;
+ GstPad *fake_udp_sinkpad = NULL;
+ GstPad *rtcp_srcpad = NULL;
+ GstStateChangeReturn state_res = GST_STATE_CHANGE_FAILURE;
+ GstStructure *pt_map;
+
+ GST_INFO ("preparing test");
+
+ /* build pipeline */
+ bin = gst_pipeline_new ("pipeline");
+ bus = gst_element_get_bus (bin);
+ gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);
+
+ src = gst_element_factory_make ("audiotestsrc", "src");
+ g_object_set (src, "num-buffers", 5, NULL);
+ encoder = gst_element_factory_make ("speexenc", NULL);
+ rtppayloader = gst_element_factory_make ("rtpspeexpay", NULL);
+ g_object_set (rtppayloader, "pt", 96, NULL);
+ rtprtxsend = gst_element_factory_make ("rtprtxsend", NULL);
+ pt_map = gst_structure_new ("application/x-rtp-pt-map",
+ "96", G_TYPE_UINT, 99, NULL);
+ g_object_set (rtprtxsend, "payload-type-map", pt_map, NULL);
+ gst_structure_free (pt_map);
+ rtpsession = gst_element_factory_make ("rtpsession", NULL);
+ sink = gst_element_factory_make ("fakesink", "sink");
+ gst_bin_add_many (GST_BIN (bin), src, encoder, rtppayloader, rtprtxsend,
+ rtpsession, sink, NULL);
+
+ /* link elements */
+ res = gst_element_link (src, encoder);
+ fail_unless (res == TRUE, NULL);
+ res = gst_element_link (encoder, rtppayloader);
+ fail_unless (res == TRUE, NULL);
+ res = gst_element_link (rtppayloader, rtprtxsend);
+ fail_unless (res == TRUE, NULL);
+ res = gst_element_link_pads_full (rtprtxsend, "src",
+ rtpsession, "send_rtp_sink", GST_PAD_LINK_CHECK_NOTHING);
+ fail_unless (res == TRUE, NULL);
+ res = gst_element_link_pads_full (rtpsession, "send_rtp_src",
+ sink, "sink", GST_PAD_LINK_CHECK_NOTHING);
+ fail_unless (res == TRUE, NULL);
+
+ /* add probe on rtpsession sink pad to induce collision */
+ sinkpad = gst_element_get_static_pad (rtpsession, "send_rtp_sink");
+ gst_pad_add_probe (sinkpad,
+ (GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_PUSH),
+ (GstPadProbeCallback) rtpsession_sinkpad_probe2, NULL, NULL);
+ gst_object_unref (sinkpad);
+
+ /* setup rtcp link */
+ srcpad = gst_pad_new_from_static_template (&srctemplate, "src");
+ rtcp_sinkpad = gst_element_get_request_pad (rtpsession, "recv_rtcp_sink");
+ fail_unless (gst_pad_link (srcpad, rtcp_sinkpad) == GST_PAD_LINK_OK, NULL);
+ gst_object_unref (rtcp_sinkpad);
+ res = gst_pad_set_active (srcpad, TRUE);
+ fail_if (res == FALSE);
+ res =
+ gst_pad_push_event (srcpad,
+ gst_event_new_stream_start ("my_rtcp_stream_id"));
+ fail_if (res == FALSE);
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ res = gst_pad_push_event (srcpad, gst_event_new_segment (&segment));
+ fail_if (res == FALSE);
+
+ fake_udp_sinkpad = gst_pad_new_from_static_template (&sinktemplate, "sink");
+ gst_pad_set_chain_function (fake_udp_sinkpad, fake_udp_sink_chain_func);
+ rtcp_srcpad = gst_element_get_request_pad (rtpsession, "send_rtcp_src");
+ fail_unless (gst_pad_link (rtcp_srcpad, fake_udp_sinkpad) == GST_PAD_LINK_OK,
+ NULL);
+ gst_object_unref (rtcp_srcpad);
+ res = gst_pad_set_active (fake_udp_sinkpad, TRUE);
+ fail_if (res == FALSE);
+
+ /* connect messages */
+ main_loop = g_main_loop_new (NULL, FALSE);
+ g_signal_connect (bus, "message::error", (GCallback) message_received, bin);
+ g_signal_connect (bus, "message::warning", (GCallback) message_received, bin);
+ g_signal_connect (bus, "message::eos", (GCallback) message_received, bin);
+
+ state_res = gst_element_set_state (bin, GST_STATE_PLAYING);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ GST_INFO ("running main loop");
+ g_main_loop_run (main_loop);
+
+ state_res = gst_element_set_state (bin, GST_STATE_NULL);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ /* cleanup */
+ gst_object_unref (srcpad);
+ gst_object_unref (fake_udp_sinkpad);
+ g_main_loop_unref (main_loop);
+ gst_bus_remove_signal_watch (bus);
+ gst_object_unref (bus);
+ gst_object_unref (bin);
+
+ /* check results */
+ fail_if (rtx_ssrc_before == rtx_ssrc_after);
+ fail_if (ssrc_before != ssrc_after);
+}
+
+GST_END_TEST;
+
+static Suite *
+rtpcollision_suite (void)
+{
+ Suite *s = suite_create ("rtpcollision");
+ TCase *tc_chain = tcase_create ("general");
+
+ tcase_set_timeout (tc_chain, 10);
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_master_ssrc_collision);
+ tcase_add_test (tc_chain, test_rtx_ssrc_collision);
+
+ return s;
+}
+
+GST_CHECK_MAIN (rtpcollision);
diff --git a/tests/check/elements/rtpjitterbuffer.c b/tests/check/elements/rtpjitterbuffer.c
new file mode 100755
index 0000000..31999ef
--- /dev/null
+++ b/tests/check/elements/rtpjitterbuffer.c
@@ -0,0 +1,1596 @@
+/* GStreamer
+ *
+ * Copyright (C) 2009 Nokia Corporation and its subsidary(-ies)
+ * contact: <stefan.kost@nokia.com>
+ * Copyright (C) 2012 Cisco Systems, Inc
+ * Authors: Kelley Rogers <kelro@cisco.com>
+ * Havard Graff <hgraff@cisco.com>
+ *
+ * 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.
+ */
+
+#include <gst/check/gstcheck.h>
+#include <gst/check/gsttestclock.h>
+
+#include <gst/rtp/gstrtpbuffer.h>
+
+/* 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;
+/* we also have a list of src buffers */
+static GList *inbuffers = NULL;
+static gint num_dropped = 0;
+
+#define RTP_CAPS_STRING \
+ "application/x-rtp, " \
+ "media = (string)audio, " \
+ "payload = (int) 0, " \
+ "clock-rate = (int) 8000, " \
+ "encoding-name = (string)PCMU"
+
+#define RTP_FRAME_SIZE 20
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("application/x-rtp")
+ );
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("application/x-rtp, "
+ "clock-rate = (int) [ 1, 2147483647 ]")
+ );
+
+static void
+buffer_dropped (gpointer data, GstMiniObject * obj)
+{
+ GST_DEBUG ("dropping buffer %p", obj);
+ num_dropped++;
+}
+
+static GstElement *
+setup_jitterbuffer (gint num_buffers)
+{
+ GstElement *jitterbuffer;
+ GstClock *clock;
+ GstBuffer *buffer;
+ GstCaps *caps;
+ /* a 20 sample audio block (2,5 ms) generated with
+ * gst-launch audiotestsrc wave=silence blocksize=40 num-buffers=3 !
+ * "audio/x-raw,channels=1,rate=8000" ! mulawenc ! rtppcmupay !
+ * fakesink dump=1
+ */
+ guint8 in[] = { /* first 4 bytes are rtp-header, next 4 bytes are timestamp */
+ 0x80, 0x80, 0x1c, 0x24, 0x46, 0xcd, 0xb7, 0x11, 0x3c, 0x3a, 0x7c, 0x5b,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+ };
+ GstClockTime ts = G_GUINT64_CONSTANT (0);
+ GstClockTime tso = gst_util_uint64_scale (RTP_FRAME_SIZE, GST_SECOND, 8000);
+ /*guint latency = GST_TIME_AS_MSECONDS (num_buffers * tso); */
+ gint i;
+
+ GST_DEBUG ("setup_jitterbuffer");
+ jitterbuffer = gst_check_setup_element ("rtpjitterbuffer");
+ /* we need a clock here */
+ clock = gst_system_clock_obtain ();
+ gst_element_set_clock (jitterbuffer, clock);
+ gst_object_unref (clock);
+ /* setup latency */
+ /* latency would be 7 for 3 buffers here, default is 200
+ g_object_set (G_OBJECT (jitterbuffer), "latency", latency, NULL);
+ GST_INFO_OBJECT (jitterbuffer, "set latency to %u ms", latency);
+ */
+
+ mysrcpad = gst_check_setup_src_pad (jitterbuffer, &srctemplate);
+ mysinkpad = gst_check_setup_sink_pad (jitterbuffer, &sinktemplate);
+ gst_pad_set_active (mysrcpad, TRUE);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ /* create n buffers */
+ caps = gst_caps_from_string (RTP_CAPS_STRING);
+ gst_check_setup_events (mysrcpad, jitterbuffer, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+
+ for (i = 0; i < num_buffers; i++) {
+ buffer = gst_buffer_new_and_alloc (sizeof (in));
+ gst_buffer_fill (buffer, 0, in, sizeof (in));
+ GST_BUFFER_DTS (buffer) = ts;
+ GST_BUFFER_PTS (buffer) = ts;
+ GST_BUFFER_DURATION (buffer) = tso;
+ gst_mini_object_weak_ref (GST_MINI_OBJECT (buffer), buffer_dropped, NULL);
+ GST_DEBUG ("created buffer: %p", buffer);
+
+ if (!i)
+ GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
+
+ inbuffers = g_list_append (inbuffers, buffer);
+
+ /* hackish way to update the rtp header */
+ in[1] = 0x00;
+ in[3]++; /* seqnumber */
+ in[7] += RTP_FRAME_SIZE; /* inc. timestamp with framesize */
+ ts += tso;
+ }
+ num_dropped = 0;
+
+ return jitterbuffer;
+}
+
+static GstStateChangeReturn
+start_jitterbuffer (GstElement * jitterbuffer)
+{
+ GstStateChangeReturn ret;
+ GstClockTime now;
+ GstClock *clock;
+
+ clock = gst_element_get_clock (jitterbuffer);
+ now = gst_clock_get_time (clock);
+ gst_object_unref (clock);
+
+ gst_element_set_base_time (jitterbuffer, now);
+ ret = gst_element_set_state (jitterbuffer, GST_STATE_PLAYING);
+
+ return ret;
+}
+
+static void
+cleanup_jitterbuffer (GstElement * jitterbuffer)
+{
+ GST_DEBUG ("cleanup_jitterbuffer");
+
+ g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
+ g_list_free (buffers);
+ buffers = NULL;
+
+ g_list_free (inbuffers);
+ inbuffers = NULL;
+
+ gst_pad_set_active (mysrcpad, FALSE);
+ gst_pad_set_active (mysinkpad, FALSE);
+ gst_check_teardown_src_pad (jitterbuffer);
+ gst_check_teardown_sink_pad (jitterbuffer);
+ gst_check_teardown_element (jitterbuffer);
+}
+
+static void
+check_jitterbuffer_results (GstElement * jitterbuffer, gint num_buffers)
+{
+ GstBuffer *buffer;
+ GList *node;
+ GstClockTime ts = G_GUINT64_CONSTANT (0);
+ GstClockTime tso = gst_util_uint64_scale (RTP_FRAME_SIZE, GST_SECOND, 8000);
+ GstMapInfo map;
+ guint16 prev_sn = 0, cur_sn;
+ guint32 prev_ts = 0, cur_ts;
+
+ /* sleep for twice the latency */
+ g_usleep (400 * 1000);
+
+ GST_INFO ("of %d buffer %d/%d received/dropped", num_buffers,
+ g_list_length (buffers), num_dropped);
+ /* if this fails, not all buffers have been processed */
+ fail_unless_equals_int ((g_list_length (buffers) + num_dropped), num_buffers);
+
+ /* check the buffer list */
+ fail_unless_equals_int (g_list_length (buffers), num_buffers);
+ for (node = buffers; node; node = g_list_next (node)) {
+ fail_if ((buffer = (GstBuffer *) node->data) == NULL);
+ fail_if (GST_BUFFER_PTS (buffer) != ts);
+ fail_if (GST_BUFFER_DTS (buffer) != ts);
+ gst_buffer_map (buffer, &map, GST_MAP_READ);
+ cur_sn = ((guint16) map.data[2] << 8) | map.data[3];
+ cur_ts = ((guint32) map.data[4] << 24) | ((guint32) map.data[5] << 16) |
+ ((guint32) map.data[6] << 8) | map.data[7];
+ gst_buffer_unmap (buffer, &map);
+
+ if (node != buffers) {
+ fail_unless (cur_sn > prev_sn);
+ fail_unless (cur_ts > prev_ts);
+
+ prev_sn = cur_sn;
+ prev_ts = cur_ts;
+ }
+ ts += tso;
+ }
+}
+
+GST_START_TEST (test_push_forward_seq)
+{
+ GstElement *jitterbuffer;
+ const guint num_buffers = 3;
+ GstBuffer *buffer;
+ GList *node;
+
+ jitterbuffer = setup_jitterbuffer (num_buffers);
+ fail_unless (start_jitterbuffer (jitterbuffer)
+ == GST_STATE_CHANGE_SUCCESS, "could not set to playing");
+
+ /* push buffers: 0,1,2, */
+ for (node = inbuffers; node; node = g_list_next (node)) {
+ buffer = (GstBuffer *) node->data;
+ fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
+ }
+
+ /* check the buffer list */
+ check_jitterbuffer_results (jitterbuffer, num_buffers);
+
+ /* cleanup */
+ cleanup_jitterbuffer (jitterbuffer);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_push_backward_seq)
+{
+ GstElement *jitterbuffer;
+ const guint num_buffers = 4;
+ GstBuffer *buffer;
+ GList *node;
+
+ jitterbuffer = setup_jitterbuffer (num_buffers);
+ fail_unless (start_jitterbuffer (jitterbuffer)
+ == GST_STATE_CHANGE_SUCCESS, "could not set to playing");
+
+ /* push buffers: 0,3,2,1 */
+ buffer = (GstBuffer *) inbuffers->data;
+ fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
+ for (node = g_list_last (inbuffers); node != inbuffers;
+ node = g_list_previous (node)) {
+ buffer = (GstBuffer *) node->data;
+ fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
+ }
+
+ /* check the buffer list */
+ check_jitterbuffer_results (jitterbuffer, num_buffers);
+
+ /* cleanup */
+ cleanup_jitterbuffer (jitterbuffer);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_push_unordered)
+{
+ GstElement *jitterbuffer;
+ const guint num_buffers = 4;
+ GstBuffer *buffer;
+
+ jitterbuffer = setup_jitterbuffer (num_buffers);
+ fail_unless (start_jitterbuffer (jitterbuffer)
+ == GST_STATE_CHANGE_SUCCESS, "could not set to playing");
+
+ /* push buffers; 0,2,1,3 */
+ buffer = (GstBuffer *) inbuffers->data;
+ fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
+ buffer = g_list_nth_data (inbuffers, 2);
+ fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
+ buffer = g_list_nth_data (inbuffers, 1);
+ fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
+ buffer = g_list_nth_data (inbuffers, 3);
+ fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
+
+ /* check the buffer list */
+ check_jitterbuffer_results (jitterbuffer, num_buffers);
+
+ /* cleanup */
+ cleanup_jitterbuffer (jitterbuffer);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_basetime)
+{
+ GstElement *jitterbuffer;
+ const guint num_buffers = 3;
+ GstBuffer *buffer;
+ GList *node;
+ GstClockTime tso = gst_util_uint64_scale (RTP_FRAME_SIZE, GST_SECOND, 8000);
+
+ jitterbuffer = setup_jitterbuffer (num_buffers);
+ fail_unless (start_jitterbuffer (jitterbuffer)
+ == GST_STATE_CHANGE_SUCCESS, "could not set to playing");
+
+ /* push buffers: 2,1,0 */
+ for (node = g_list_last (inbuffers); node; node = g_list_previous (node)) {
+ buffer = (GstBuffer *) node->data;
+ fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
+ }
+
+ /* sleep for twice the latency */
+ g_usleep (400 * 1000);
+
+ /* if this fails, not all buffers have been processed */
+ fail_unless_equals_int ((g_list_length (buffers) + num_dropped), num_buffers);
+
+ buffer = (GstBuffer *) buffers->data;
+ fail_unless (GST_BUFFER_DTS (buffer) != (num_buffers * tso));
+ fail_unless (GST_BUFFER_PTS (buffer) != (num_buffers * tso));
+
+ /* cleanup */
+ cleanup_jitterbuffer (jitterbuffer);
+}
+
+GST_END_TEST;
+
+static GstCaps *
+request_pt_map (GstElement * jitterbuffer, guint pt)
+{
+ fail_unless (pt == 0);
+
+ return gst_caps_from_string (RTP_CAPS_STRING);
+}
+
+GST_START_TEST (test_clear_pt_map)
+{
+ GstElement *jitterbuffer;
+ const guint num_buffers = 10;
+ gint i;
+ GstBuffer *buffer;
+ GList *node;
+
+ jitterbuffer = setup_jitterbuffer (num_buffers);
+ fail_unless (start_jitterbuffer (jitterbuffer)
+ == GST_STATE_CHANGE_SUCCESS, "could not set to playing");
+
+ g_signal_connect (jitterbuffer, "request-pt-map", (GCallback)
+ request_pt_map, NULL);
+
+ /* push buffers: 0,1,2, */
+ for (node = inbuffers, i = 0; node && i < 3; node = g_list_next (node), i++) {
+ buffer = (GstBuffer *) node->data;
+ fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
+ }
+
+ g_usleep (400 * 1000);
+
+ g_signal_emit_by_name (jitterbuffer, "clear-pt-map", NULL);
+
+ for (; node && i < 10; node = g_list_next (node), i++) {
+ buffer = (GstBuffer *) node->data;
+ fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
+ }
+
+ /* check the buffer list */
+ check_jitterbuffer_results (jitterbuffer, num_buffers);
+
+ /* cleanup */
+ cleanup_jitterbuffer (jitterbuffer);
+}
+
+GST_END_TEST;
+static const guint payload_size = 160;
+static const guint clock_rate = 8000;
+static const guint pcmu_payload_type = 0;
+static const guint test_ssrc = 0x01BADBAD;
+
+typedef struct
+{
+ GstElement *jitter_buffer;
+ GstPad *test_sink_pad, *test_src_pad;
+ GstClock *clock;
+ GAsyncQueue *buf_queue;
+ GAsyncQueue *sink_event_queue;
+ GAsyncQueue *src_event_queue;
+ gint lost_event_count;
+ gint rtx_event_count;
+} TestData;
+
+static GstCaps *
+generate_caps (void)
+{
+ return gst_caps_new_simple ("application/x-rtp",
+ "media", G_TYPE_STRING, "audio",
+ "clock-rate", G_TYPE_INT, clock_rate,
+ "encoding-name", G_TYPE_STRING, "PCMU",
+ "payload", G_TYPE_INT, pcmu_payload_type,
+ "ssrc", G_TYPE_UINT, test_ssrc, NULL);
+}
+
+static GstBuffer *
+generate_test_buffer (GstClockTime gst_ts,
+ gboolean marker_bit, guint seq_num, guint32 rtp_ts)
+{
+ GstBuffer *buf;
+ guint8 *payload;
+ guint i;
+ GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
+
+ buf = gst_rtp_buffer_new_allocate (payload_size, 0, 0);
+ GST_BUFFER_DTS (buf) = gst_ts;
+ GST_BUFFER_PTS (buf) = gst_ts;
+
+ gst_rtp_buffer_map (buf, GST_MAP_READWRITE, &rtp);
+ gst_rtp_buffer_set_payload_type (&rtp, pcmu_payload_type);
+ gst_rtp_buffer_set_marker (&rtp, marker_bit);
+ gst_rtp_buffer_set_seq (&rtp, seq_num);
+ gst_rtp_buffer_set_timestamp (&rtp, rtp_ts);
+ gst_rtp_buffer_set_ssrc (&rtp, test_ssrc);
+
+ payload = gst_rtp_buffer_get_payload (&rtp);
+ for (i = 0; i < payload_size; i++)
+ payload[i] = 0xff;
+
+ gst_rtp_buffer_unmap (&rtp);
+
+ return buf;
+}
+
+static GstFlowReturn
+test_sink_pad_chain_cb (GstPad * pad, GstObject * parent, GstBuffer * buffer)
+{
+ TestData *data = gst_pad_get_element_private (pad);
+ g_async_queue_push (data->buf_queue, buffer);
+ return GST_FLOW_OK;
+}
+
+static gboolean
+test_sink_pad_event_cb (GstPad * pad, GstObject * parent, GstEvent * event)
+{
+ TestData *data = gst_pad_get_element_private (pad);
+ const GstStructure *structure = gst_event_get_structure (event);
+
+ GST_DEBUG ("got event %" GST_PTR_FORMAT, event);
+
+ if (strcmp (gst_structure_get_name (structure), "GstRTPPacketLost") == 0) {
+ data->lost_event_count++;
+ GST_DEBUG ("lost event count %d", data->lost_event_count);
+ }
+
+ g_async_queue_push (data->sink_event_queue, event);
+ return TRUE;
+}
+
+static gboolean
+test_src_pad_event_cb (GstPad * pad, GstObject * parent, GstEvent * event)
+{
+ TestData *data = gst_pad_get_element_private (pad);
+ const GstStructure *structure = gst_event_get_structure (event);
+
+ GST_DEBUG ("got event %" GST_PTR_FORMAT, event);
+
+ if (structure
+ && strcmp (gst_structure_get_name (structure),
+ "GstRTPRetransmissionRequest") == 0) {
+ data->rtx_event_count++;
+ GST_DEBUG ("rtx event count %d", data->rtx_event_count);
+ }
+
+ g_async_queue_push (data->src_event_queue, event);
+ return TRUE;
+}
+
+static void
+setup_testharness (TestData * data)
+{
+ GstPad *jb_sink_pad, *jb_src_pad;
+ GstSegment seg;
+ GstMiniObject *obj;
+ GstCaps *caps;
+
+ /* create the testclock */
+ data->clock = gst_test_clock_new ();
+ g_assert (data->clock);
+ gst_test_clock_set_time (GST_TEST_CLOCK (data->clock), 0);
+
+ /* rig up the jitter buffer */
+ data->jitter_buffer = gst_element_factory_make ("rtpjitterbuffer", NULL);
+ g_assert (data->jitter_buffer);
+ gst_element_set_clock (data->jitter_buffer, data->clock);
+ g_object_set (data->jitter_buffer, "do-lost", TRUE, NULL);
+ g_assert_cmpint (gst_element_set_state (data->jitter_buffer,
+ GST_STATE_PLAYING), !=, GST_STATE_CHANGE_FAILURE);
+
+ /* set up the buf and event queues */
+ data->buf_queue =
+ g_async_queue_new_full ((GDestroyNotify) gst_mini_object_unref);
+ data->sink_event_queue =
+ g_async_queue_new_full ((GDestroyNotify) gst_mini_object_unref);
+ data->src_event_queue =
+ g_async_queue_new_full ((GDestroyNotify) gst_mini_object_unref);
+
+ data->lost_event_count = 0;
+ data->rtx_event_count = 0;
+
+ /* link in the test source-pad */
+ data->test_src_pad = gst_pad_new ("src", GST_PAD_SRC);
+ gst_pad_set_element_private (data->test_src_pad, data);
+ gst_pad_set_event_function (data->test_src_pad, test_src_pad_event_cb);
+ jb_sink_pad = gst_element_get_static_pad (data->jitter_buffer, "sink");
+ g_assert_cmpint (gst_pad_link (data->test_src_pad, jb_sink_pad), ==,
+ GST_PAD_LINK_OK);
+ gst_object_unref (jb_sink_pad);
+
+ /* link in the test sink-pad */
+ data->test_sink_pad = gst_pad_new ("sink", GST_PAD_SINK);
+ gst_pad_set_element_private (data->test_sink_pad, data);
+ caps = generate_caps ();
+ gst_pad_set_caps (data->test_sink_pad, caps);
+ gst_pad_set_chain_function (data->test_sink_pad, test_sink_pad_chain_cb);
+ gst_pad_set_event_function (data->test_sink_pad, test_sink_pad_event_cb);
+ jb_src_pad = gst_element_get_static_pad (data->jitter_buffer, "src");
+ g_assert_cmpint (gst_pad_link (jb_src_pad, data->test_sink_pad), ==,
+ GST_PAD_LINK_OK);
+ gst_object_unref (jb_src_pad);
+
+ g_assert (gst_pad_set_active (data->test_src_pad, TRUE));
+ g_assert (gst_pad_set_active (data->test_sink_pad, TRUE));
+
+ gst_segment_init (&seg, GST_FORMAT_TIME);
+
+ gst_pad_push_event (data->test_src_pad,
+ gst_event_new_stream_start ("stream0"));
+ gst_pad_set_caps (data->test_src_pad, caps);
+ gst_pad_push_event (data->test_src_pad, gst_event_new_segment (&seg));
+ gst_caps_unref (caps);
+
+ obj = g_async_queue_pop (data->sink_event_queue);
+ gst_mini_object_unref (obj);
+ obj = g_async_queue_pop (data->sink_event_queue);
+ gst_mini_object_unref (obj);
+ obj = g_async_queue_pop (data->sink_event_queue);
+ gst_mini_object_unref (obj);
+}
+
+static void
+destroy_testharness (TestData * data)
+{
+ /* clean up */
+ g_assert_cmpint (gst_element_set_state (data->jitter_buffer, GST_STATE_NULL),
+ ==, GST_STATE_CHANGE_SUCCESS);
+ gst_object_unref (data->jitter_buffer);
+ data->jitter_buffer = NULL;
+
+ gst_object_unref (data->test_src_pad);
+ data->test_src_pad = NULL;
+
+ gst_object_unref (data->test_sink_pad);
+ data->test_sink_pad = NULL;
+
+ gst_object_unref (data->clock);
+ data->clock = NULL;
+
+ g_async_queue_unref (data->buf_queue);
+ data->buf_queue = NULL;
+
+ g_async_queue_unref (data->sink_event_queue);
+ data->sink_event_queue = NULL;
+ g_async_queue_unref (data->src_event_queue);
+ data->src_event_queue = NULL;
+
+ data->lost_event_count = 0;
+}
+
+static void
+verify_lost_event (GstEvent * event, guint32 expected_seqnum,
+ GstClockTime expected_timestamp, GstClockTime expected_duration,
+ gboolean expected_late)
+{
+ const GstStructure *s = gst_event_get_structure (event);
+ const GValue *value;
+ guint32 seqnum;
+ GstClockTime timestamp;
+ GstClockTime duration;
+ gboolean late;
+
+ g_assert (gst_structure_get_uint (s, "seqnum", &seqnum));
+
+ value = gst_structure_get_value (s, "timestamp");
+ g_assert (value && G_VALUE_HOLDS_UINT64 (value));
+ timestamp = g_value_get_uint64 (value);
+
+ value = gst_structure_get_value (s, "duration");
+ g_assert (value && G_VALUE_HOLDS_UINT64 (value));
+ duration = g_value_get_uint64 (value);
+
+ g_assert (gst_structure_get_boolean (s, "late", &late));
+
+ g_assert_cmpint (seqnum, ==, expected_seqnum);
+ g_assert_cmpint (timestamp, ==, expected_timestamp);
+ g_assert_cmpint (duration, ==, expected_duration);
+ g_assert (late == expected_late);
+
+ gst_event_unref (event);
+}
+
+static void
+verify_rtx_event (GstEvent * event, guint32 expected_seqnum,
+ GstClockTime expected_timestamp, guint expected_delay,
+ GstClockTime expected_spacing)
+{
+ const GstStructure *s = gst_event_get_structure (event);
+ const GValue *value;
+ guint32 seqnum;
+ GstClockTime timestamp, spacing;
+ guint delay;
+
+ g_assert (gst_structure_get_uint (s, "seqnum", &seqnum));
+
+ value = gst_structure_get_value (s, "running-time");
+ g_assert (value && G_VALUE_HOLDS_UINT64 (value));
+ timestamp = g_value_get_uint64 (value);
+
+ value = gst_structure_get_value (s, "delay");
+ g_assert (value && G_VALUE_HOLDS_UINT (value));
+ delay = g_value_get_uint (value);
+
+ value = gst_structure_get_value (s, "packet-spacing");
+ g_assert (value && G_VALUE_HOLDS_UINT64 (value));
+ spacing = g_value_get_uint64 (value);
+
+ g_assert_cmpint (seqnum, ==, expected_seqnum);
+ g_assert_cmpint (timestamp, ==, expected_timestamp);
+ g_assert_cmpint (delay, ==, expected_delay);
+ g_assert_cmpint (spacing, ==, expected_spacing);
+
+ gst_event_unref (event);
+}
+
+GST_START_TEST (test_only_one_lost_event_on_large_gaps)
+{
+ TestData data;
+ GstClockID id, test_id;
+ GstBuffer *in_buf, *out_buf;
+ GstEvent *out_event;
+ gint jb_latency_ms = 200;
+ guint buffer_size_ms = (payload_size * 1000) / clock_rate;
+ GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
+
+ setup_testharness (&data);
+
+ g_object_set (data.jitter_buffer, "latency", jb_latency_ms, NULL);
+ /* push the first buffer in */
+ in_buf = generate_test_buffer (0 * GST_MSECOND, TRUE, 0, 0);
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 0);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+
+ /* wait for the first buffer to be synced to timestamp + latency */
+ gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
+
+ /* increase the time to timestamp + latency and release the wait */
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock),
+ jb_latency_ms * GST_MSECOND);
+ test_id = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
+ g_assert (test_id == id);
+ gst_clock_id_unref (test_id);
+ gst_clock_id_unref (id);
+
+ /* check for the buffer coming out that was pushed in */
+ out_buf = g_async_queue_pop (data.buf_queue);
+ g_assert (out_buf != NULL);
+ g_assert_cmpint (GST_BUFFER_DTS (out_buf), ==, 0);
+ g_assert_cmpint (GST_BUFFER_PTS (out_buf), ==, 0);
+ gst_buffer_unref (out_buf);
+
+ /* move time ahead 10 seconds */
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 10 * GST_SECOND);
+
+ /* wait a bit */
+ g_usleep (G_USEC_PER_SEC / 10);
+
+ /* check that no buffers have been pushed out and no pending waits */
+ g_assert_cmpint (g_async_queue_length (data.buf_queue), ==, 0);
+ g_assert (gst_test_clock_peek_next_pending_id (GST_TEST_CLOCK (data.clock),
+ &id) == FALSE);
+
+ /* a buffer now arrives perfectly on time */
+ in_buf = generate_test_buffer (10 * GST_SECOND, FALSE, 500, 500 * 160);
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 10 * GST_SECOND);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+
+ /* release the wait */
+ gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
+ gst_test_clock_advance_time (GST_TEST_CLOCK (data.clock), GST_MSECOND * 20);
+ test_id = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
+ g_assert (id == test_id);
+ gst_clock_id_unref (test_id);
+ gst_clock_id_unref (id);
+
+ /* we should now receive a packet-lost-event for buffers 1 through 489 */
+ out_event = g_async_queue_pop (data.sink_event_queue);
+ g_assert (out_event != NULL);
+ g_assert_cmpint (data.lost_event_count, ==, 1);
+ verify_lost_event (out_event, 1, 1 * GST_MSECOND * 20, GST_MSECOND * 20 * 490,
+ TRUE);
+
+ /* churn through sync_times until the new buffer gets pushed out */
+ while (g_async_queue_length (data.buf_queue) < 1) {
+ if (gst_test_clock_peek_next_pending_id (GST_TEST_CLOCK (data.clock), &id)) {
+ GstClockTime t = gst_clock_id_get_time (id);
+ if (t > gst_clock_get_time (data.clock)) {
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), t);
+ }
+ test_id =
+ gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
+ gst_clock_id_unref (test_id);
+ gst_clock_id_unref (id);
+ }
+ }
+
+ out_buf = g_async_queue_pop (data.buf_queue);
+ g_assert (out_buf != NULL);
+ g_assert (GST_BUFFER_FLAG_IS_SET (out_buf, GST_BUFFER_FLAG_DISCONT));
+ gst_rtp_buffer_map (out_buf, GST_MAP_READ, &rtp);
+ g_assert_cmpint (gst_rtp_buffer_get_seq (&rtp), ==, 500);
+ gst_rtp_buffer_unmap (&rtp);
+ g_assert_cmpint (GST_BUFFER_DTS (out_buf), ==, (10 * GST_SECOND));
+ g_assert_cmpint (GST_BUFFER_PTS (out_buf), ==, (10 * GST_SECOND));
+ gst_buffer_unref (out_buf);
+
+ /* we get as many lost events as the the number of buffers the jitterbuffer
+ * is able to wait for (+ the one we already got) */
+ g_assert_cmpint (data.lost_event_count, ==, jb_latency_ms / buffer_size_ms);
+
+ destroy_testharness (&data);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_two_lost_one_arrives_in_time)
+{
+ TestData data;
+ GstClockID id, test_id;
+ GstBuffer *in_buf, *out_buf;
+ GstEvent *out_event;
+ gint jb_latency_ms = 100;
+ GstClockTime buffer_time, now;
+ gint b;
+ GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
+
+ setup_testharness (&data);
+
+ g_object_set (data.jitter_buffer, "latency", jb_latency_ms, NULL);
+
+ /* push the first buffer in */
+ in_buf = generate_test_buffer (0 * GST_MSECOND, TRUE, 0, 0);
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 0);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+ gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
+ now = jb_latency_ms * GST_MSECOND;
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), now);
+ test_id = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
+ g_assert (test_id == id);
+ gst_clock_id_unref (test_id);
+ gst_clock_id_unref (id);
+ out_buf = g_async_queue_pop (data.buf_queue);
+ g_assert (out_buf != NULL);
+
+ /* push some buffers arriving in perfect time! */
+ for (b = 1; b < 3; b++) {
+ buffer_time = b * GST_MSECOND * 20;
+ in_buf = generate_test_buffer (buffer_time, TRUE, b, b * 160);
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), now + buffer_time);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+ gst_buffer_unref (out_buf);
+
+ /* check for the buffer coming out that was pushed in */
+ out_buf = g_async_queue_pop (data.buf_queue);
+ g_assert (out_buf != NULL);
+ g_assert_cmpint (GST_BUFFER_DTS (out_buf), ==, buffer_time);
+ g_assert_cmpint (GST_BUFFER_PTS (out_buf), ==, buffer_time);
+ }
+ gst_buffer_unref (out_buf);
+
+ /* hop over 2 packets and make another one (gap of 2) */
+ b = 5;
+ buffer_time = b * GST_MSECOND * 20;
+ in_buf = generate_test_buffer (buffer_time, TRUE, b, b * 160);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+
+ /* verify that the jitterbuffer now wait for the latest moment it can push */
+ /* the first lost buffer (buffer 3) out on (buffer-timestamp (60) + latency (10) = 70) */
+ gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
+ g_assert_cmpint (gst_clock_id_get_time (id), ==,
+ (3 * GST_MSECOND * 20) + (jb_latency_ms * GST_MSECOND));
+
+ /* let the time expire... */
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock),
+ gst_clock_id_get_time (id));
+ test_id = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
+ g_assert (test_id == id);
+ gst_clock_id_unref (test_id);
+ gst_clock_id_unref (id);
+
+ /* we should now receive a packet-lost-event for buffer 3 */
+ out_event = g_async_queue_pop (data.sink_event_queue);
+ g_assert (out_event != NULL);
+ g_assert_cmpint (data.lost_event_count, ==, 1);
+ verify_lost_event (out_event, 3, 3 * GST_MSECOND * 20, GST_MSECOND * 20,
+ FALSE);
+
+ /* buffer 4 now arrives just in time (time is 70, buffer 4 expires at 90) */
+ b = 4;
+ buffer_time = b * GST_MSECOND * 20;
+ in_buf = generate_test_buffer (buffer_time, TRUE, b, b * 160);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+
+ /* verify that buffer 4 made it through! */
+ out_buf = g_async_queue_pop (data.buf_queue);
+ g_assert (out_buf != NULL);
+ g_assert (GST_BUFFER_FLAG_IS_SET (out_buf, GST_BUFFER_FLAG_DISCONT));
+ gst_rtp_buffer_map (out_buf, GST_MAP_READ, &rtp);
+ g_assert_cmpint (gst_rtp_buffer_get_seq (&rtp), ==, 4);
+ gst_rtp_buffer_unmap (&rtp);
+ gst_buffer_unref (out_buf);
+
+ /* and see that buffer 5 now arrives in a normal fashion */
+ out_buf = g_async_queue_pop (data.buf_queue);
+ g_assert (out_buf != NULL);
+ g_assert (!GST_BUFFER_FLAG_IS_SET (out_buf, GST_BUFFER_FLAG_DISCONT));
+ gst_rtp_buffer_map (out_buf, GST_MAP_READ, &rtp);
+ g_assert_cmpint (gst_rtp_buffer_get_seq (&rtp), ==, 5);
+ gst_rtp_buffer_unmap (&rtp);
+ gst_buffer_unref (out_buf);
+
+ /* should still have only seen 1 packet lost event */
+ g_assert_cmpint (data.lost_event_count, ==, 1);
+
+ destroy_testharness (&data);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_late_packets_still_makes_lost_events)
+{
+ TestData data;
+ GstClockID id, test_id;
+ GstBuffer *in_buf, *out_buf;
+ GstEvent *out_event;
+ gint jb_latency_ms = 10;
+ GstClockTime buffer_time;
+ gint b;
+ GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
+
+ setup_testharness (&data);
+
+ g_object_set (data.jitter_buffer, "latency", jb_latency_ms, NULL);
+
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 10 * GST_SECOND);
+
+ /* push the first buffer in */
+ in_buf = generate_test_buffer (0 * GST_MSECOND, TRUE, 0, 0);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+
+ gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
+ test_id = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
+ g_assert (test_id == id);
+ gst_clock_id_unref (id);
+ gst_clock_id_unref (test_id);
+ out_buf = g_async_queue_pop (data.buf_queue);
+ g_assert (out_buf != NULL);
+
+ /* push some buffers in! */
+ for (b = 1; b < 3; b++) {
+ buffer_time = b * GST_MSECOND * 20;
+ in_buf = generate_test_buffer (buffer_time, TRUE, b, b * 160);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+ gst_buffer_unref (out_buf);
+
+ /* check for the buffer coming out that was pushed in */
+ out_buf = g_async_queue_pop (data.buf_queue);
+ g_assert (out_buf != NULL);
+ g_assert_cmpint (GST_BUFFER_DTS (out_buf), ==, buffer_time);
+ g_assert_cmpint (GST_BUFFER_PTS (out_buf), ==, buffer_time);
+ }
+ gst_buffer_unref (out_buf);
+
+ /* hop over 2 packets and make another one (gap of 2) */
+ b = 5;
+ buffer_time = b * GST_MSECOND * 20;
+ in_buf = generate_test_buffer (buffer_time, TRUE, b, b * 160);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+
+ /* we should now receive a packet-lost-event for buffer 3 and 4 */
+ out_event = g_async_queue_pop (data.sink_event_queue);
+ g_assert (out_event != NULL);
+ g_assert_cmpint (data.lost_event_count, ==, 1);
+ verify_lost_event (out_event, 3, 3 * GST_MSECOND * 20, GST_MSECOND * 20 * 2,
+ TRUE);
+
+ /* verify that buffer 5 made it through! */
+ out_buf = g_async_queue_pop (data.buf_queue);
+ g_assert (out_buf != NULL);
+ g_assert (GST_BUFFER_FLAG_IS_SET (out_buf, GST_BUFFER_FLAG_DISCONT));
+ gst_rtp_buffer_map (out_buf, GST_MAP_READ, &rtp);
+ g_assert_cmpint (gst_rtp_buffer_get_seq (&rtp), ==, 5);
+ gst_rtp_buffer_unmap (&rtp);
+ gst_buffer_unref (out_buf);
+
+ /* should still have only seen 1 packet lost event */
+ g_assert_cmpint (data.lost_event_count, ==, 1);
+
+ destroy_testharness (&data);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_all_packets_are_timestamped_zero)
+{
+ TestData data;
+ GstClockID id, test_id;
+ GstBuffer *in_buf, *out_buf;
+ GstEvent *out_event;
+ gint jb_latency_ms = 10;
+ gint b;
+ GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
+
+ setup_testharness (&data);
+
+ g_object_set (data.jitter_buffer, "latency", jb_latency_ms, NULL);
+
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 10 * GST_SECOND);
+
+ /* push the first buffer in */
+ in_buf = generate_test_buffer (0 * GST_MSECOND, TRUE, 0, 0);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+
+ gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
+ test_id = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
+ g_assert (test_id == id);
+ gst_clock_id_unref (test_id);
+ gst_clock_id_unref (id);
+ out_buf = g_async_queue_pop (data.buf_queue);
+ g_assert (out_buf != NULL);
+
+ /* push some buffers in! */
+ for (b = 1; b < 3; b++) {
+ in_buf = generate_test_buffer (0, TRUE, b, 0);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+ gst_buffer_unref (out_buf);
+
+ /* check for the buffer coming out that was pushed in */
+ out_buf = g_async_queue_pop (data.buf_queue);
+ g_assert (out_buf != NULL);
+ g_assert_cmpint (GST_BUFFER_DTS (out_buf), ==, 0);
+ g_assert_cmpint (GST_BUFFER_PTS (out_buf), ==, 0);
+ }
+ gst_buffer_unref (out_buf);
+
+ /* hop over 2 packets and make another one (gap of 2) */
+ b = 5;
+ in_buf = generate_test_buffer (0, TRUE, b, 0);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+
+ /* we should now receive a packet-lost-event for buffer 3 and 4 */
+ out_event = g_async_queue_pop (data.sink_event_queue);
+ g_assert (out_event != NULL);
+ verify_lost_event (out_event, 3, 0, 0, FALSE);
+
+ out_event = g_async_queue_pop (data.sink_event_queue);
+ g_assert (out_event != NULL);
+ verify_lost_event (out_event, 4, 0, 0, FALSE);
+
+ g_assert_cmpint (data.lost_event_count, ==, 2);
+
+ /* verify that buffer 5 made it through! */
+ out_buf = g_async_queue_pop (data.buf_queue);
+ g_assert (out_buf != NULL);
+ g_assert (GST_BUFFER_FLAG_IS_SET (out_buf, GST_BUFFER_FLAG_DISCONT));
+ gst_rtp_buffer_map (out_buf, GST_MAP_READ, &rtp);
+ g_assert_cmpint (gst_rtp_buffer_get_seq (&rtp), ==, 5);
+ gst_rtp_buffer_unmap (&rtp);
+ gst_buffer_unref (out_buf);
+
+ /* should still have only seen 2 packet lost events */
+ g_assert_cmpint (data.lost_event_count, ==, 2);
+
+ destroy_testharness (&data);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_rtx_expected_next)
+{
+ TestData data;
+ GstClockID id, tid;
+ GstBuffer *in_buf, *out_buf;
+ GstEvent *out_event;
+ gint jb_latency_ms = 200;
+
+ setup_testharness (&data);
+ g_object_set (data.jitter_buffer, "do-retransmission", TRUE, NULL);
+ g_object_set (data.jitter_buffer, "latency", jb_latency_ms, NULL);
+ g_object_set (data.jitter_buffer, "rtx-retry-period", 120, NULL);
+
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 0);
+
+ /* push the first buffer in */
+ in_buf = generate_test_buffer (0 * GST_MSECOND, TRUE, 0, 0);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 20 * GST_MSECOND);
+
+ gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
+ gst_clock_id_unref (id);
+
+ /* put second buffer, the jitterbuffer should now know that the packet spacing
+ * is 20ms and should ask for retransmission of seqnum 2 in 20ms */
+ in_buf = generate_test_buffer (20 * GST_MSECOND, TRUE, 1, 160);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+
+ gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 60 * GST_MSECOND);
+ tid = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
+ g_assert (tid == id);
+ gst_clock_id_unref (tid);
+ gst_clock_id_unref (id);
+
+ out_event = g_async_queue_pop (data.src_event_queue);
+ g_assert (out_event != NULL);
+ verify_rtx_event (out_event, 2, 40 * GST_MSECOND, 20, 20 * GST_MSECOND);
+
+ /* now we wait for the next timeout */
+ gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 100 * GST_MSECOND);
+ tid = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
+ g_assert (id == tid);
+ gst_clock_id_unref (tid);
+ gst_clock_id_unref (id);
+
+ out_event = g_async_queue_pop (data.src_event_queue);
+ g_assert (out_event != NULL);
+ verify_rtx_event (out_event, 2, 40 * GST_MSECOND, 60, 20 * GST_MSECOND);
+
+ gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 140 * GST_MSECOND);
+ tid = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
+ g_assert (id == tid);
+ gst_clock_id_unref (tid);
+ gst_clock_id_unref (id);
+
+ out_event = g_async_queue_pop (data.src_event_queue);
+ g_assert (out_event != NULL);
+ verify_rtx_event (out_event, 2, 40 * GST_MSECOND, 100, 20 * GST_MSECOND);
+
+ gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 200 * GST_MSECOND);
+ tid = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
+ g_assert (id == tid);
+ gst_clock_id_unref (tid);
+ gst_clock_id_unref (id);
+
+ out_buf = g_async_queue_pop (data.buf_queue);
+ g_assert (out_buf != NULL);
+ gst_buffer_unref (out_buf);
+
+
+ gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 260 * GST_MSECOND);
+ tid = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
+ g_assert (tid == id);
+ gst_clock_id_unref (tid);
+ gst_clock_id_unref (id);
+
+ /* we should now receive a packet-lost-event for buffer 2 */
+ out_event = g_async_queue_pop (data.sink_event_queue);
+ g_assert (out_event != NULL);
+ verify_lost_event (out_event, 2, 40 * GST_MSECOND, 20 * GST_MSECOND, FALSE);
+
+ destroy_testharness (&data);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_rtx_two_missing)
+{
+ TestData data;
+ GstClockID id, tid;
+ GstBuffer *in_buf, *out_buf;
+ GstEvent *out_event;
+ gint jb_latency_ms = 200;
+ gint i;
+ GstStructure *rtx_stats;
+ const GValue *rtx_stat;
+ GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
+
+ setup_testharness (&data);
+ g_object_set (data.jitter_buffer, "do-retransmission", TRUE, NULL);
+ g_object_set (data.jitter_buffer, "latency", jb_latency_ms, NULL);
+ g_object_set (data.jitter_buffer, "rtx-retry-period", 120, NULL);
+
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 0);
+
+ /* push the first buffer in */
+ in_buf = generate_test_buffer (0 * GST_MSECOND, TRUE, 0, 0);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 20 * GST_MSECOND);
+
+ /* put second buffer, the jitterbuffer should now know that the packet spacing
+ * is 20ms and should ask for retransmission of seqnum 2 at 60ms */
+ in_buf = generate_test_buffer (20 * GST_MSECOND, TRUE, 1, 160);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+
+ /* push buffer 4, 2 and 3 are missing now, we should get retransmission events
+ * for 3 at 100ms*/
+ in_buf = generate_test_buffer (80 * GST_MSECOND, TRUE, 4, 4 * 160);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+
+ /* wait for first retransmission request */
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 60 * GST_MSECOND);
+ do {
+ gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
+ tid = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
+ gst_clock_id_unref (id);
+ gst_clock_id_unref (tid);
+ } while (id != tid);
+
+ /* we should have 2 events now, one for 2 and another for 3 */
+ out_event = g_async_queue_pop (data.src_event_queue);
+ g_assert (out_event != NULL);
+ verify_rtx_event (out_event, 2, 40 * GST_MSECOND, 20, 20 * GST_MSECOND);
+ out_event = g_async_queue_pop (data.src_event_queue);
+ g_assert (out_event != NULL);
+ verify_rtx_event (out_event, 3, 60 * GST_MSECOND, 0, 20 * GST_MSECOND);
+
+ /* now we wait for the next timeout */
+ gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 100 * GST_MSECOND);
+ tid = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
+ g_assert (id == tid);
+ gst_clock_id_unref (id);
+ gst_clock_id_unref (tid);
+
+ /* we should have 2 events now, one for 2 and another for 3 */
+ out_event = g_async_queue_pop (data.src_event_queue);
+ g_assert (out_event != NULL);
+ verify_rtx_event (out_event, 2, 40 * GST_MSECOND, 60, 20 * GST_MSECOND);
+ out_event = g_async_queue_pop (data.src_event_queue);
+ g_assert (out_event != NULL);
+ verify_rtx_event (out_event, 3, 60 * GST_MSECOND, 40, 20 * GST_MSECOND);
+
+ /* make buffer 3 */
+ in_buf = generate_test_buffer (60 * GST_MSECOND, TRUE, 3, 3 * 160);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+
+ /* make more buffers */
+ for (i = 5; i < 15; i++) {
+ in_buf = generate_test_buffer (i * 20 * GST_MSECOND, TRUE, i, i * 160);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+ }
+
+ gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 140 * GST_MSECOND);
+ tid = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
+ g_assert (id == tid);
+ gst_clock_id_unref (id);
+ gst_clock_id_unref (tid);
+
+ /* now we only get requests for 2 */
+ out_event = g_async_queue_pop (data.src_event_queue);
+ g_assert (out_event != NULL);
+ verify_rtx_event (out_event, 2, 40 * GST_MSECOND, 100, 20 * GST_MSECOND);
+
+ /* this is when buffer 0 deadline expires */
+ gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 200 * GST_MSECOND);
+ tid = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
+ g_assert (id == tid);
+ gst_clock_id_unref (id);
+ gst_clock_id_unref (tid);
+
+ for (i = 0; i < 2; i++) {
+ GST_DEBUG ("popping %d", i);
+ out_buf = g_async_queue_pop (data.buf_queue);
+ g_assert (out_buf != NULL);
+ gst_rtp_buffer_map (out_buf, GST_MAP_READ, &rtp);
+ g_assert_cmpint (gst_rtp_buffer_get_seq (&rtp), ==, i);
+ gst_rtp_buffer_unmap (&rtp);
+ gst_buffer_unref (out_buf);
+ }
+
+ /* this is when 2 is lost */
+ gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 240 * GST_MSECOND);
+ tid = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
+ g_assert (id == tid);
+ gst_clock_id_unref (id);
+ gst_clock_id_unref (tid);
+
+ /* we should now receive a packet-lost-event for buffer 2 */
+ out_event = g_async_queue_pop (data.sink_event_queue);
+ g_assert (out_event != NULL);
+ verify_lost_event (out_event, 2, 40 * GST_MSECOND, 20 * GST_MSECOND, FALSE);
+
+ /* verify that buffers made it through! */
+ for (i = 3; i < 15; i++) {
+ GST_DEBUG ("popping %d", i);
+ out_buf = g_async_queue_pop (data.buf_queue);
+ g_assert (out_buf != NULL);
+ gst_rtp_buffer_map (out_buf, GST_MAP_READ, &rtp);
+ g_assert_cmpint (gst_rtp_buffer_get_seq (&rtp), ==, i);
+ gst_rtp_buffer_unmap (&rtp);
+ gst_buffer_unref (out_buf);
+ }
+ /* should still have only seen 1 packet lost events */
+ g_assert_cmpint (data.lost_event_count, ==, 1);
+
+ g_object_get (data.jitter_buffer, "stats", &rtx_stats, NULL);
+
+ rtx_stat = gst_structure_get_value (rtx_stats, "rtx-count");
+ g_assert_cmpuint (g_value_get_uint64 (rtx_stat), ==, 5);
+
+ rtx_stat = gst_structure_get_value (rtx_stats, "rtx-success-count");
+ g_assert_cmpuint (g_value_get_uint64 (rtx_stat), ==, 1);
+
+ rtx_stat = gst_structure_get_value (rtx_stats, "rtx-rtt");
+ g_assert_cmpuint (g_value_get_uint64 (rtx_stat), ==, 0);
+ gst_structure_free (rtx_stats);
+
+ destroy_testharness (&data);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_rtx_packet_delay)
+{
+ TestData data;
+ GstClockID id, tid;
+ GstBuffer *in_buf, *out_buf;
+ GstEvent *out_event;
+ gint jb_latency_ms = 200;
+ gint i;
+ GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
+
+ setup_testharness (&data);
+ g_object_set (data.jitter_buffer, "do-retransmission", TRUE, NULL);
+ g_object_set (data.jitter_buffer, "latency", jb_latency_ms, NULL);
+ g_object_set (data.jitter_buffer, "rtx-retry-period", 120, NULL);
+
+ /* push the first buffer in */
+ in_buf = generate_test_buffer (0 * GST_MSECOND, TRUE, 0, 0);
+ GST_BUFFER_FLAG_SET (in_buf, GST_BUFFER_FLAG_DISCONT);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 20 * GST_MSECOND);
+
+ /* put second buffer, the jitterbuffer should now know that the packet spacing
+ * is 20ms and should ask for retransmission of seqnum 2 at 60ms */
+ in_buf = generate_test_buffer (20 * GST_MSECOND, TRUE, 1, 160);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+
+ /* push buffer 8, 2 -> 7 are missing now. note that the rtp time is the same
+ * as packet 1 because it was part of a fragmented payload. This means that
+ * the estimate for 2 could be refined now to 20ms. also packet 2, 3 and 4 are
+ * exceeding the max allowed reorder distance and should request a
+ * retransmission right away */
+ in_buf = generate_test_buffer (20 * GST_MSECOND, TRUE, 8, 8 * 160);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+
+ /* we should now receive retransmission requests for 2 -> 5 */
+ out_event = g_async_queue_pop (data.src_event_queue);
+ g_assert (out_event != NULL);
+ verify_rtx_event (out_event, 2, 20 * GST_MSECOND, 40, 20 * GST_MSECOND);
+
+ for (i = 3; i < 5; i++) {
+ GST_DEBUG ("popping %d", i);
+ out_event = g_async_queue_pop (data.src_event_queue);
+ g_assert (out_event != NULL);
+ verify_rtx_event (out_event, i, 20 * GST_MSECOND, 0, 20 * GST_MSECOND);
+ }
+ g_assert_cmpint (data.rtx_event_count, ==, 3);
+
+ /* push 9, this should immediately request retransmission of 5 */
+ in_buf = generate_test_buffer (20 * GST_MSECOND, TRUE, 9, 9 * 160);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+
+ /* we should now receive retransmission requests for 5 */
+ out_event = g_async_queue_pop (data.src_event_queue);
+ g_assert (out_event != NULL);
+ verify_rtx_event (out_event, 5, 20 * GST_MSECOND, 0, 20 * GST_MSECOND);
+
+ /* wait for timeout for rtx 6 -> 7 */
+ gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
+ tid = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
+ g_assert (id == tid);
+ gst_clock_id_unref (id);
+ gst_clock_id_unref (tid);
+
+ for (i = 6; i < 8; i++) {
+ GST_DEBUG ("popping %d", i);
+ out_event = g_async_queue_pop (data.src_event_queue);
+ g_assert (out_event != NULL);
+ verify_rtx_event (out_event, i, 20 * GST_MSECOND, 0, 20 * GST_MSECOND);
+ }
+
+ /* churn through sync_times until the new buffer gets pushed out */
+ while (g_async_queue_length (data.buf_queue) < 1) {
+ if (gst_test_clock_peek_next_pending_id (GST_TEST_CLOCK (data.clock), &id)) {
+ GstClockTime t = gst_clock_id_get_time (id);
+ if (t >= 240 * GST_MSECOND) {
+ gst_clock_id_unref (id);
+ break;
+ }
+ if (t > gst_clock_get_time (data.clock)) {
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), t);
+ }
+ tid = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
+ gst_clock_id_unref (id);
+ gst_clock_id_unref (tid);
+ }
+ }
+
+ /* verify that buffer 0 and 1 made it through! */
+ for (i = 0; i < 2; i++) {
+ out_buf = g_async_queue_pop (data.buf_queue);
+ g_assert (out_buf != NULL);
+ if (i == 0)
+ g_assert (GST_BUFFER_FLAG_IS_SET (out_buf, GST_BUFFER_FLAG_DISCONT));
+ gst_rtp_buffer_map (out_buf, GST_MAP_READ, &rtp);
+ g_assert_cmpint (gst_rtp_buffer_get_seq (&rtp), ==, i);
+ gst_rtp_buffer_unmap (&rtp);
+ gst_buffer_unref (out_buf);
+ }
+
+ /* churn through sync_times until the next buffer gets pushed out */
+ while (g_async_queue_length (data.buf_queue) < 1) {
+ if (gst_test_clock_peek_next_pending_id (GST_TEST_CLOCK (data.clock), &id)) {
+ GstClockTime t = gst_clock_id_get_time (id);
+ if (t >= 240 * GST_MSECOND) {
+ gst_clock_id_unref (id);
+ break;
+ }
+ if (t > gst_clock_get_time (data.clock)) {
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), t);
+ }
+ tid = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
+ gst_clock_id_unref (id);
+ gst_clock_id_unref (tid);
+ }
+ }
+
+ for (i = 2; i < 8; i++) {
+ GST_DEBUG ("popping lost event %d", i);
+ out_event = g_async_queue_pop (data.sink_event_queue);
+ g_assert (out_event != NULL);
+ verify_lost_event (out_event, i, 20 * GST_MSECOND, 0, FALSE);
+ }
+
+ /* verify that buffer 8 made it through! */
+ for (i = 8; i < 10; i++) {
+ GST_DEBUG ("popping buffer %d", i);
+ out_buf = g_async_queue_pop (data.buf_queue);
+ g_assert (out_buf != NULL);
+ if (i == 8)
+ g_assert (GST_BUFFER_FLAG_IS_SET (out_buf, GST_BUFFER_FLAG_DISCONT));
+ gst_rtp_buffer_map (out_buf, GST_MAP_READ, &rtp);
+ g_assert_cmpint (gst_rtp_buffer_get_seq (&rtp), ==, i);
+ gst_rtp_buffer_unmap (&rtp);
+ gst_buffer_unref (out_buf);
+ }
+
+ GST_DEBUG ("waiting for 240ms");
+ gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 240 * GST_MSECOND);
+ tid = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
+ g_assert (id == tid);
+ gst_clock_id_unref (id);
+ gst_clock_id_unref (tid);
+
+ GST_DEBUG ("popping lost event 10");
+ out_event = g_async_queue_pop (data.sink_event_queue);
+ g_assert (out_event != NULL);
+ verify_lost_event (out_event, 10, 40 * GST_MSECOND, 20 * GST_MSECOND, FALSE);
+
+ /* should have seen 6 packet lost events */
+ g_assert_cmpint (data.lost_event_count, ==, 7);
+ g_assert_cmpint (data.rtx_event_count, ==, 26);
+
+ destroy_testharness (&data);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_gap_exceeds_latency)
+{
+ TestData data;
+ GstBuffer *in_buf, *out_buf;
+ GstClockID id, tid;
+ GstEvent *out_event;
+ guint32 timestamp_ms = 0;
+ guint32 last_ts = 0;
+ gint jb_latency_ms = 200;
+ guint32 rtp_ts = 0;
+ guint32 last_rtp = 0;
+ const GstStructure *s = NULL;
+ guint32 seqnum = 0;
+ gint i;
+
+ setup_testharness (&data);
+ g_object_set (data.jitter_buffer, "do-retransmission", TRUE, NULL);
+ g_object_set (data.jitter_buffer, "latency", jb_latency_ms, NULL);
+ g_object_set (data.jitter_buffer, "rtx-retry-period", 120, NULL);
+
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 0);
+ in_buf = generate_test_buffer (timestamp_ms * GST_MSECOND, TRUE, 0, rtp_ts);
+ GST_BUFFER_FLAG_SET (in_buf, GST_BUFFER_FLAG_DISCONT);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+
+ timestamp_ms += 20;
+ rtp_ts += 160;
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock),
+ timestamp_ms * GST_MSECOND);
+
+ in_buf = generate_test_buffer (timestamp_ms * GST_MSECOND, TRUE, 1, rtp_ts);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+ last_rtp = rtp_ts;
+ last_ts = timestamp_ms;
+
+ /* Allow seqnum 2 to be declared lost */
+ do {
+ out_event = g_async_queue_try_pop (data.sink_event_queue);
+ if (!out_event) {
+ if (gst_test_clock_peek_next_pending_id (GST_TEST_CLOCK (data.clock),
+ &id)) {
+
+ GstClockTime t = gst_clock_id_get_time (id);
+ if (t > gst_clock_get_time (data.clock)) {
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), t);
+ }
+ tid =
+ gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
+ gst_clock_id_unref (id);
+ gst_clock_id_unref (tid);
+ }
+ }
+ } while (!out_event);
+
+ out_buf = g_async_queue_pop (data.buf_queue);
+ gst_buffer_unref (out_buf);
+
+ out_buf = g_async_queue_pop (data.buf_queue);
+ gst_buffer_unref (out_buf);
+
+ timestamp_ms += (20 * 15);
+ s = gst_event_get_structure (out_event);
+ g_assert (gst_structure_get_uint (s, "seqnum", &seqnum));
+ g_assert_cmpint (seqnum, ==, 2);
+ gst_event_unref (out_event);
+
+ /* Now data comes in again, a "bulk" lost packet is created for 3 -> 6 */
+ rtp_ts += (160 * 15);
+ in_buf = generate_test_buffer (timestamp_ms * GST_MSECOND, TRUE, 16, rtp_ts);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+
+ last_ts += 60;
+ last_rtp += 480;
+ in_buf = generate_test_buffer (last_ts * GST_MSECOND, TRUE, 8, last_rtp);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+
+ last_ts += 20;
+ last_rtp += 160;
+ in_buf = generate_test_buffer (last_ts * GST_MSECOND, TRUE, 9, last_rtp);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+
+ last_ts += 20;
+ last_rtp += 160;
+ in_buf = generate_test_buffer (last_ts * GST_MSECOND, TRUE, 10, last_rtp);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+
+ last_ts += 20;
+ last_rtp += 160;
+ in_buf = generate_test_buffer (last_ts * GST_MSECOND, TRUE, 11, last_rtp);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+
+ last_ts += 20;
+ last_rtp += 160;
+ in_buf = generate_test_buffer (last_ts * GST_MSECOND, TRUE, 12, last_rtp);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+
+ last_ts += 20;
+ last_rtp += 160;
+ in_buf = generate_test_buffer (last_ts * GST_MSECOND, TRUE, 13, last_rtp);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+
+ last_ts += 20;
+ last_rtp += 160;
+ in_buf = generate_test_buffer (last_ts * GST_MSECOND, TRUE, 14, last_rtp);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+
+ last_ts += 20;
+ last_rtp += 160;
+ in_buf = generate_test_buffer (last_ts * GST_MSECOND, TRUE, 15, last_rtp);
+ g_assert_cmpint (gst_pad_push (data.test_src_pad, in_buf), ==, GST_FLOW_OK);
+
+ /* Wait for data to be pushed. */
+ while (g_async_queue_length (data.buf_queue) < 1) {
+ if (gst_test_clock_peek_next_pending_id (GST_TEST_CLOCK (data.clock), &id)) {
+ GstClockTime t = gst_clock_id_get_time (id);
+ if (t > gst_clock_get_time (data.clock)) {
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), t);
+ }
+ tid = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
+ gst_clock_id_unref (id);
+ gst_clock_id_unref (tid);
+ }
+ }
+
+ out_event = g_async_queue_pop (data.sink_event_queue);
+ s = gst_event_get_structure (out_event);
+ g_assert (gst_structure_get_uint (s, "seqnum", &seqnum));
+ g_assert_cmpint (seqnum, ==, 3);
+ gst_event_unref (out_event);
+
+ out_event = g_async_queue_pop (data.sink_event_queue);
+ s = gst_event_get_structure (out_event);
+ g_assert (gst_structure_get_uint (s, "seqnum", &seqnum));
+ g_assert_cmpint (seqnum, ==, 7);
+ gst_event_unref (out_event);
+
+ /* 8 */
+ for (i = 8; i <= 16; i++) {
+ out_buf = g_async_queue_pop (data.buf_queue);
+ GST_DEBUG ("pop %d", i);
+ gst_buffer_unref (out_buf);
+ }
+
+ do {
+ out_event = g_async_queue_try_pop (data.sink_event_queue);
+ if (!out_event) {
+ if (gst_test_clock_peek_next_pending_id (GST_TEST_CLOCK (data.clock),
+ &id)) {
+
+ GstClockTime t = gst_clock_id_get_time (id);
+ if (t > gst_clock_get_time (data.clock)) {
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), t);
+ }
+ tid =
+ gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
+ gst_clock_id_unref (id);
+ gst_clock_id_unref (tid);
+ }
+ }
+ } while (!out_event);
+
+ /* and lost of 17 */
+ s = gst_event_get_structure (out_event);
+ g_assert (gst_structure_get_uint (s, "seqnum", &seqnum));
+ g_assert_cmpint (seqnum, ==, 17);
+ gst_event_unref (out_event);
+
+ destroy_testharness (&data);
+}
+
+GST_END_TEST;
+
+
+static Suite *
+rtpjitterbuffer_suite (void)
+{
+ Suite *s = suite_create ("rtpjitterbuffer");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_push_forward_seq);
+ tcase_add_test (tc_chain, test_push_backward_seq);
+ tcase_add_test (tc_chain, test_push_unordered);
+ tcase_add_test (tc_chain, test_basetime);
+ tcase_add_test (tc_chain, test_clear_pt_map);
+ tcase_add_test (tc_chain, test_only_one_lost_event_on_large_gaps);
+ tcase_add_test (tc_chain, test_two_lost_one_arrives_in_time);
+ tcase_add_test (tc_chain, test_late_packets_still_makes_lost_events);
+ tcase_add_test (tc_chain, test_all_packets_are_timestamped_zero);
+ tcase_add_test (tc_chain, test_rtx_expected_next);
+ tcase_add_test (tc_chain, test_rtx_two_missing);
+ tcase_add_test (tc_chain, test_rtx_packet_delay);
+ tcase_add_test (tc_chain, test_gap_exceeds_latency);
+
+ return s;
+}
+
+GST_CHECK_MAIN (rtpjitterbuffer);
diff --git a/tests/check/elements/rtpmux.c b/tests/check/elements/rtpmux.c
new file mode 100755
index 0000000..11ba979
--- /dev/null
+++ b/tests/check/elements/rtpmux.c
@@ -0,0 +1,320 @@
+/* GStreamer
+ *
+ * unit test for rtpmux elements
+ *
+ * Copyright 2009 Collabora Ltd.
+ * @author: Olivier Crete <olivier.crete@collabora.co.uk>
+ * Copyright 2009 Nokia Corp.
+ *
+ * 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.
+ */
+
+#include <gst/check/gstcheck.h>
+#include <gst/rtp/gstrtpbuffer.h>
+#include <gst/gst.h>
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("application/x-rtp"));
+
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("application/x-rtp"));
+
+typedef void (*check_cb) (GstPad * pad, int i);
+
+static gboolean
+query_func (GstPad * pad, GstObject * noparent, GstQuery * query)
+{
+ switch (GST_QUERY_TYPE (query)) {
+ case GST_QUERY_CAPS:
+ {
+ GstCaps **caps = g_object_get_data (G_OBJECT (pad), "caps");
+
+ fail_unless (caps != NULL && *caps != NULL);
+ gst_query_set_caps_result (query, *caps);
+ break;
+ }
+ case GST_QUERY_ACCEPT_CAPS:
+ gst_query_set_accept_caps_result (query, TRUE);
+ break;
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+event_func (GstPad * pad, GstObject * noparent, GstEvent * event)
+{
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_CAPS:
+ {
+ GstCaps *caps;
+ GstCaps **caps2 = g_object_get_data (G_OBJECT (pad), "caps");
+
+ gst_event_parse_caps (event, &caps);
+ fail_unless (caps2 != NULL && *caps2 != NULL);
+ fail_unless (gst_caps_is_fixed (caps));
+ fail_unless (gst_caps_is_fixed (*caps2));
+ fail_unless (gst_caps_is_equal_fixed (caps, *caps2));
+ break;
+ }
+ default:
+ break;
+ }
+
+ gst_event_unref (event);
+
+ return TRUE;
+}
+
+static void
+test_basic (const gchar * elem_name, const gchar * sink2, int count,
+ check_cb cb)
+{
+ GstElement *rtpmux = NULL;
+ GstPad *reqpad1 = NULL;
+ GstPad *reqpad2 = NULL;
+ GstPad *src1 = NULL;
+ GstPad *src2 = NULL;
+ GstPad *sink = NULL;
+ GstBuffer *inbuf = NULL;
+ GstCaps *src1caps = NULL;
+ GstCaps *src2caps = NULL;
+ GstCaps *sinkcaps = NULL;
+ GstCaps *caps;
+ GstSegment segment;
+ int i;
+
+ rtpmux = gst_check_setup_element (elem_name);
+
+ reqpad1 = gst_element_get_request_pad (rtpmux, "sink_1");
+ fail_unless (reqpad1 != NULL);
+ reqpad2 = gst_element_get_request_pad (rtpmux, sink2);
+ fail_unless (reqpad2 != NULL);
+ sink = gst_check_setup_sink_pad_by_name (rtpmux, &sinktemplate, "src");
+
+ src1 = gst_pad_new_from_static_template (&srctemplate, "src");
+ src2 = gst_pad_new_from_static_template (&srctemplate, "src");
+ fail_unless (gst_pad_link (src1, reqpad1) == GST_PAD_LINK_OK);
+ fail_unless (gst_pad_link (src2, reqpad2) == GST_PAD_LINK_OK);
+ gst_pad_set_query_function (src1, query_func);
+ gst_pad_set_query_function (src2, query_func);
+ gst_pad_set_query_function (sink, query_func);
+ gst_pad_set_event_function (sink, event_func);
+ g_object_set_data (G_OBJECT (src1), "caps", &src1caps);
+ g_object_set_data (G_OBJECT (src2), "caps", &src2caps);
+ g_object_set_data (G_OBJECT (sink), "caps", &sinkcaps);
+
+ src1caps = gst_caps_new_simple ("application/x-rtp",
+ "clock-rate", G_TYPE_INT, 1, "ssrc", G_TYPE_UINT, 11, NULL);
+ src2caps = gst_caps_new_simple ("application/x-rtp",
+ "clock-rate", G_TYPE_INT, 2, "ssrc", G_TYPE_UINT, 12, NULL);
+ sinkcaps = gst_caps_new_simple ("application/x-rtp",
+ "clock-rate", G_TYPE_INT, 3, "ssrc", G_TYPE_UINT, 13, NULL);
+
+ caps = gst_pad_peer_query_caps (src1, NULL);
+ fail_unless (gst_caps_is_empty (caps));
+ gst_caps_unref (caps);
+
+ gst_caps_set_simple (src2caps, "clock-rate", G_TYPE_INT, 3, NULL);
+ caps = gst_pad_peer_query_caps (src1, NULL);
+ fail_unless (gst_caps_is_equal (caps, sinkcaps));
+ gst_caps_unref (caps);
+
+ g_object_set (rtpmux, "seqnum-offset", 100, "timestamp-offset", 1000,
+ "ssrc", 55, NULL);
+
+ fail_unless (gst_element_set_state (rtpmux,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS);
+ gst_pad_set_active (sink, TRUE);
+ gst_pad_set_active (src1, TRUE);
+ gst_pad_set_active (src2, TRUE);
+
+ fail_unless (gst_pad_push_event (src1,
+ gst_event_new_stream_start ("stream1")));
+ fail_unless (gst_pad_push_event (src2,
+ gst_event_new_stream_start ("stream2")));
+
+ gst_caps_set_simple (sinkcaps,
+ "payload", G_TYPE_INT, 98, "seqnum-offset", G_TYPE_UINT, 100,
+ "timestamp-offset", G_TYPE_UINT, 1000, "ssrc", G_TYPE_UINT, 66, NULL);
+ caps = gst_caps_new_simple ("application/x-rtp",
+ "payload", G_TYPE_INT, 98, "clock-rate", G_TYPE_INT, 3,
+ "seqnum-offset", G_TYPE_UINT, 56, "timestamp-offset", G_TYPE_UINT, 57,
+ "ssrc", G_TYPE_UINT, 66, NULL);
+ fail_unless (gst_pad_set_caps (src1, caps));
+ gst_caps_unref (caps);
+
+ caps = gst_pad_peer_query_caps (sink, NULL);
+ fail_if (gst_caps_is_empty (caps));
+
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ segment.start = 100000;
+ fail_unless (gst_pad_push_event (src1, gst_event_new_segment (&segment)));
+ segment.start = 0;
+ fail_unless (gst_pad_push_event (src2, gst_event_new_segment (&segment)));
+
+
+ for (i = 0; i < count; i++) {
+ GstRTPBuffer rtpbuffer = GST_RTP_BUFFER_INIT;
+
+ inbuf = gst_rtp_buffer_new_allocate (10, 0, 0);
+ GST_BUFFER_PTS (inbuf) = i * 1000 + 100000;
+ GST_BUFFER_DURATION (inbuf) = 1000;
+
+ gst_rtp_buffer_map (inbuf, GST_MAP_WRITE, &rtpbuffer);
+
+ gst_rtp_buffer_set_version (&rtpbuffer, 2);
+ gst_rtp_buffer_set_payload_type (&rtpbuffer, 98);
+ gst_rtp_buffer_set_ssrc (&rtpbuffer, 44);
+ gst_rtp_buffer_set_timestamp (&rtpbuffer, 200 + i);
+ gst_rtp_buffer_set_seq (&rtpbuffer, 2000 + i);
+ gst_rtp_buffer_unmap (&rtpbuffer);
+ fail_unless (gst_pad_push (src1, inbuf) == GST_FLOW_OK);
+
+ if (buffers)
+ fail_unless (GST_BUFFER_PTS (buffers->data) == i * 1000, "%lld",
+ GST_BUFFER_PTS (buffers->data));
+
+ cb (src2, i);
+
+ g_list_foreach (buffers, (GFunc) gst_buffer_unref, NULL);
+ g_list_free (buffers);
+ buffers = NULL;
+ }
+
+
+ gst_pad_set_active (sink, FALSE);
+ gst_pad_set_active (src1, FALSE);
+ gst_pad_set_active (src2, FALSE);
+ fail_unless (gst_element_set_state (rtpmux,
+ GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS);
+ gst_check_teardown_pad_by_name (rtpmux, "src");
+ gst_object_unref (reqpad1);
+ gst_object_unref (reqpad2);
+ gst_check_teardown_pad_by_name (rtpmux, "sink_1");
+ gst_check_teardown_pad_by_name (rtpmux, sink2);
+ gst_element_release_request_pad (rtpmux, reqpad1);
+ gst_element_release_request_pad (rtpmux, reqpad2);
+
+ gst_caps_unref (caps);
+ gst_caps_replace (&src1caps, NULL);
+ gst_caps_replace (&src2caps, NULL);
+ gst_caps_replace (&sinkcaps, NULL);
+
+ gst_check_teardown_element (rtpmux);
+}
+
+static void
+basic_check_cb (GstPad * pad, int i)
+{
+ GstRTPBuffer rtpbuffer = GST_RTP_BUFFER_INIT;
+ fail_unless (buffers && g_list_length (buffers) == 1);
+
+ gst_rtp_buffer_map (buffers->data, GST_MAP_READ, &rtpbuffer);
+ fail_unless (gst_rtp_buffer_get_ssrc (&rtpbuffer) == 66);
+ fail_unless (gst_rtp_buffer_get_timestamp (&rtpbuffer) ==
+ 200 - 57 + 1000 + i);
+ fail_unless (gst_rtp_buffer_get_seq (&rtpbuffer) == 100 + 1 + i);
+ gst_rtp_buffer_unmap (&rtpbuffer);
+}
+
+
+GST_START_TEST (test_rtpmux_basic)
+{
+ test_basic ("rtpmux", "sink_2", 10, basic_check_cb);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_rtpdtmfmux_basic)
+{
+ test_basic ("rtpdtmfmux", "sink_2", 10, basic_check_cb);
+}
+
+GST_END_TEST;
+
+static void
+lock_check_cb (GstPad * pad, int i)
+{
+ GstBuffer *inbuf;
+
+ if (i % 2) {
+ fail_unless (buffers == NULL);
+ } else {
+ GstRTPBuffer rtpbuffer = GST_RTP_BUFFER_INIT;
+
+ fail_unless (buffers && g_list_length (buffers) == 1);
+ gst_rtp_buffer_map (buffers->data, GST_MAP_READ, &rtpbuffer);
+ fail_unless (gst_rtp_buffer_get_ssrc (&rtpbuffer) == 66);
+ fail_unless (gst_rtp_buffer_get_timestamp (&rtpbuffer) ==
+ 200 - 57 + 1000 + i);
+ fail_unless (gst_rtp_buffer_get_seq (&rtpbuffer) == 100 + 1 + i);
+ gst_rtp_buffer_unmap (&rtpbuffer);
+
+ inbuf = gst_rtp_buffer_new_allocate (10, 0, 0);
+ GST_BUFFER_PTS (inbuf) = i * 1000 + 500;
+ GST_BUFFER_DURATION (inbuf) = 1000;
+ gst_rtp_buffer_map (inbuf, GST_MAP_WRITE, &rtpbuffer);
+ gst_rtp_buffer_set_version (&rtpbuffer, 2);
+ gst_rtp_buffer_set_payload_type (&rtpbuffer, 98);
+ gst_rtp_buffer_set_ssrc (&rtpbuffer, 44);
+ gst_rtp_buffer_set_timestamp (&rtpbuffer, 200 + i);
+ gst_rtp_buffer_set_seq (&rtpbuffer, 2000 + i);
+ gst_rtp_buffer_unmap (&rtpbuffer);
+ fail_unless (gst_pad_push (pad, inbuf) == GST_FLOW_OK);
+
+
+ g_list_foreach (buffers, (GFunc) gst_buffer_unref, NULL);
+ g_list_free (buffers);
+ buffers = NULL;
+ }
+}
+
+GST_START_TEST (test_rtpdtmfmux_lock)
+{
+ test_basic ("rtpdtmfmux", "priority_sink_2", 10, lock_check_cb);
+}
+
+GST_END_TEST;
+
+static Suite *
+rtpmux_suite (void)
+{
+ Suite *s = suite_create ("rtpmux");
+ TCase *tc_chain;
+
+ tc_chain = tcase_create ("rtpmux_basic");
+ tcase_add_test (tc_chain, test_rtpmux_basic);
+ suite_add_tcase (s, tc_chain);
+
+ tc_chain = tcase_create ("rtpdtmfmux_basic");
+ tcase_add_test (tc_chain, test_rtpdtmfmux_basic);
+ suite_add_tcase (s, tc_chain);
+
+ tc_chain = tcase_create ("rtpdtmfmux_lock");
+ tcase_add_test (tc_chain, test_rtpdtmfmux_lock);
+ suite_add_tcase (s, tc_chain);
+
+ return s;
+}
+
+GST_CHECK_MAIN (rtpmux)
diff --git a/tests/check/elements/rtprtx.c b/tests/check/elements/rtprtx.c
new file mode 100755
index 0000000..706f291
--- /dev/null
+++ b/tests/check/elements/rtprtx.c
@@ -0,0 +1,1546 @@
+/* GStreamer
+ *
+ * Copyright (C) 2013 Collabora Ltd.
+ * @author Julien Isorce <julien.isorce@collabora.co.uk>
+ *
+ * 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.
+ */
+
+#include <gst/check/gstcheck.h>
+#include <gst/check/gstconsistencychecker.h>
+
+#include <gst/rtp/gstrtpbuffer.h>
+
+/* 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 *srcpad, *sinkpad;
+/* we also have a list of src buffers */
+static GList *inbuffers = NULL;
+
+#define RTP_CAPS_STRING \
+ "application/x-rtp, " \
+ "media = (string)audio, " \
+ "payload = (int) 0, " \
+ "clock-rate = (int) 8000, " \
+ "ssrc = (uint) 42, " \
+ "encoding-name = (string)PCMU"
+
+#define RTP_FRAME_SIZE 20
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("application/x-rtp")
+ );
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("application/x-rtp")
+ );
+
+static void
+setup_rtprtx (GstElement * rtprtxsend, GstElement * rtprtxreceive,
+ gint num_buffers)
+{
+ GstBuffer *buffer;
+ GstPad *sendsrcpad;
+ GstPad *receivesinkpad;
+ gboolean ret = FALSE;
+
+ /* a 20 sample audio block (2,5 ms) generated with
+ * gst-launch audiotestsrc wave=silence blocksize=40 num-buffers=3 !
+ * "audio/x-raw,channels=1,rate=8000" ! mulawenc ! rtppcmupay !
+ * fakesink dump=1
+ */
+ guint8 in[] = { /* first 4 bytes are rtp-header, next 4 bytes are timestamp */
+ 0x80, 0x80, 0x1c, 0x24, 0x46, 0xcd, 0xb7, 0x11, 0x3c, 0x3a, 0x7c, 0x5b,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+ };
+ GstClockTime ts = G_GUINT64_CONSTANT (0);
+ GstClockTime tso = gst_util_uint64_scale (RTP_FRAME_SIZE, GST_SECOND, 8000);
+ gint i;
+
+ srcpad = gst_check_setup_src_pad (rtprtxsend, &srctemplate);
+ sendsrcpad = gst_element_get_static_pad (rtprtxsend, "src");
+ ret = gst_pad_set_active (srcpad, TRUE);
+ fail_if (ret == FALSE);
+
+ sinkpad = gst_check_setup_sink_pad (rtprtxreceive, &sinktemplate);
+ receivesinkpad = gst_element_get_static_pad (rtprtxreceive, "sink");
+ ret = gst_pad_set_active (sinkpad, TRUE);
+ fail_if (ret == FALSE);
+
+ fail_if (gst_pad_link (sendsrcpad, receivesinkpad) != GST_PAD_LINK_OK);
+
+ ret = gst_pad_set_active (sendsrcpad, TRUE);
+ fail_if (ret == FALSE);
+ ret = gst_pad_set_active (receivesinkpad, TRUE);
+ fail_if (ret == FALSE);
+
+ gst_object_unref (sendsrcpad);
+ gst_object_unref (receivesinkpad);
+
+ for (i = 0; i < num_buffers; i++) {
+ buffer = gst_buffer_new_and_alloc (sizeof (in));
+ gst_buffer_fill (buffer, 0, in, sizeof (in));
+ GST_BUFFER_DTS (buffer) = ts;
+ GST_BUFFER_PTS (buffer) = ts;
+ GST_BUFFER_DURATION (buffer) = tso;
+ GST_DEBUG ("created buffer: %p", buffer);
+
+ /*if (!i)
+ GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT); */
+
+ inbuffers = g_list_append (inbuffers, buffer);
+
+ /* hackish way to update the rtp header */
+ in[1] = 0x00;
+ in[3]++; /* seqnumber */
+ in[7] += RTP_FRAME_SIZE; /* inc. timestamp with framesize */
+ ts += tso;
+ }
+}
+
+static GstStateChangeReturn
+start_rtprtx (GstElement * element)
+{
+ GstStateChangeReturn ret;
+
+ ret = gst_element_set_state (element, GST_STATE_PLAYING);
+ ck_assert_int_ne (ret, GST_STATE_CHANGE_FAILURE);
+
+ ret = gst_element_get_state (element, NULL, NULL, GST_CLOCK_TIME_NONE);
+ ck_assert_int_ne (ret, GST_STATE_CHANGE_FAILURE);
+
+ return ret;
+}
+
+static void
+cleanup_rtprtx (GstElement * rtprtxsend, GstElement * rtprtxreceive)
+{
+ GST_DEBUG ("cleanup_rtprtx");
+
+ g_list_free (inbuffers);
+ inbuffers = NULL;
+
+ gst_pad_set_active (srcpad, FALSE);
+ gst_check_teardown_src_pad (rtprtxsend);
+ gst_check_teardown_element (rtprtxsend);
+
+ gst_pad_set_active (sinkpad, FALSE);
+ gst_check_teardown_sink_pad (rtprtxreceive);
+ gst_check_teardown_element (rtprtxreceive);
+}
+
+static void
+check_rtprtx_results (GstElement * rtprtxsend, GstElement * rtprtxreceive,
+ gint num_buffers)
+{
+ guint nbrtxrequests = 0;
+ guint nbrtxpackets = 0;
+
+ g_object_get (G_OBJECT (rtprtxsend), "num-rtx-requests", &nbrtxrequests,
+ NULL);
+ fail_unless_equals_int (nbrtxrequests, 3);
+
+ g_object_get (G_OBJECT (rtprtxsend), "num-rtx-packets", &nbrtxpackets, NULL);
+ fail_unless_equals_int (nbrtxpackets, 3);
+
+ g_object_get (G_OBJECT (rtprtxreceive), "num-rtx-requests", &nbrtxrequests,
+ NULL);
+ fail_unless_equals_int (nbrtxrequests, 3);
+
+ g_object_get (G_OBJECT (rtprtxreceive), "num-rtx-packets", &nbrtxpackets,
+ NULL);
+ fail_unless_equals_int (nbrtxpackets, 3);
+
+ g_object_get (G_OBJECT (rtprtxreceive), "num-rtx-assoc-packets",
+ &nbrtxpackets, NULL);
+ fail_unless_equals_int (nbrtxpackets, 3);
+}
+
+
+GST_START_TEST (test_push_forward_seq)
+{
+ GstElement *rtprtxsend;
+ GstElement *rtprtxreceive;
+ const guint num_buffers = 4;
+ GList *node;
+ gint i = 0;
+ GstCaps *caps = NULL;
+ GstStructure *pt_map;
+
+ rtprtxsend = gst_check_setup_element ("rtprtxsend");
+ rtprtxreceive = gst_check_setup_element ("rtprtxreceive");
+ setup_rtprtx (rtprtxsend, rtprtxreceive, num_buffers);
+ GST_DEBUG ("setup_rtprtx");
+
+ fail_unless (start_rtprtx (rtprtxsend)
+ == GST_STATE_CHANGE_SUCCESS, "could not set to playing");
+
+ fail_unless (start_rtprtx (rtprtxreceive)
+ == GST_STATE_CHANGE_SUCCESS, "could not set to playing");
+
+ caps = gst_caps_from_string (RTP_CAPS_STRING);
+ gst_check_setup_events (srcpad, rtprtxsend, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+
+ pt_map = gst_structure_new ("application/x-rtp-pt-map",
+ "0", G_TYPE_UINT, 97, NULL);
+ g_object_set (rtprtxsend, "payload-type-map", pt_map, NULL);
+ g_object_set (rtprtxreceive, "payload-type-map", pt_map, NULL);
+ gst_structure_free (pt_map);
+
+ /* push buffers: 0,1,2, */
+ for (node = inbuffers; node; node = g_list_next (node)) {
+ GstEvent *event = NULL;
+ GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
+ GstBuffer *buffer = (GstBuffer *) node->data;
+ GList *last_out_buffer;
+ guint64 end_time;
+ gboolean res;
+
+ gst_buffer_ref (buffer);
+ fail_unless_equals_int (gst_pad_push (srcpad, buffer), GST_FLOW_OK);
+
+ if (i < 3) {
+ gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp);
+ event = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
+ gst_structure_new ("GstRTPRetransmissionRequest",
+ "seqnum", G_TYPE_UINT, (guint) gst_rtp_buffer_get_seq (&rtp),
+ "ssrc", G_TYPE_UINT, (guint) gst_rtp_buffer_get_ssrc (&rtp),
+ "payload-type", G_TYPE_UINT,
+ (guint) gst_rtp_buffer_get_payload_type (&rtp), NULL));
+ gst_rtp_buffer_unmap (&rtp);
+
+ /* synchronize with the chain() function of the "sinkpad"
+ * to make sure that rtxsend has pushed the rtx buffer out
+ * before continuing */
+ last_out_buffer = g_list_last (buffers);
+ g_mutex_lock (&check_mutex);
+ fail_unless (gst_pad_push_event (sinkpad, event));
+ end_time = g_get_monotonic_time () + G_TIME_SPAN_SECOND;
+ do
+ res = g_cond_wait_until (&check_cond, &check_mutex, end_time);
+ while (res == TRUE && last_out_buffer == g_list_last (buffers));
+ g_mutex_unlock (&check_mutex);
+ }
+ gst_buffer_unref (buffer);
+ ++i;
+ }
+
+ /* check the buffer list */
+ check_rtprtx_results (rtprtxsend, rtprtxreceive, num_buffers);
+
+ /* cleanup */
+ cleanup_rtprtx (rtprtxsend, rtprtxreceive);
+}
+
+GST_END_TEST;
+
+static void
+message_received (GstBus * bus, GstMessage * message, gboolean * eos)
+{
+ GST_INFO ("bus message from \"%" GST_PTR_FORMAT "\": %" GST_PTR_FORMAT,
+ GST_MESSAGE_SRC (message), message);
+
+ switch (message->type) {
+ case GST_MESSAGE_EOS:
+ *eos = TRUE;
+ break;
+ case GST_MESSAGE_WARNING:{
+ GError *gerror;
+ gchar *debug;
+
+ gst_message_parse_warning (message, &gerror, &debug);
+ gst_object_default_error (GST_MESSAGE_SRC (message), gerror, debug);
+ g_error_free (gerror);
+ g_free (debug);
+ break;
+ }
+ case GST_MESSAGE_ERROR:{
+ GError *gerror;
+ gchar *debug;
+
+ gst_message_parse_error (message, &gerror, &debug);
+ gst_object_default_error (GST_MESSAGE_SRC (message), gerror, debug);
+ fail ("Error: %s / %s", gerror->message, debug);
+ g_error_free (gerror);
+ g_free (debug);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+typedef struct
+{
+ guint count;
+ guint nb_packets;
+ guint drop_every_n_packets;
+} RTXSendData;
+
+typedef struct
+{
+ guint nb_packets;
+ guint seqnum_offset;
+ guint seqnum_prev;
+} RTXReceiveData;
+
+static GstPadProbeReturn
+rtprtxsend_srcpad_probe (GstPad * pad, GstPadProbeInfo * info,
+ gpointer user_data)
+{
+ GstPadProbeReturn ret = GST_PAD_PROBE_OK;
+
+ if (info->type == (GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_PUSH)) {
+ GstBuffer *buffer = GST_BUFFER (info->data);
+ RTXSendData *rtxdata = (RTXSendData *) user_data;
+ GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
+ guint payload_type = 0;
+
+ gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp);
+ payload_type = gst_rtp_buffer_get_payload_type (&rtp);
+
+ /* main stream packets */
+ if (payload_type == 96) {
+ /* count packets of the main stream */
+ ++rtxdata->nb_packets;
+ /* drop some packets */
+ if (rtxdata->count < rtxdata->drop_every_n_packets) {
+ ++rtxdata->count;
+ } else {
+ /* drop a packet every 'rtxdata->count' packets */
+ rtxdata->count = 1;
+ ret = GST_PAD_PROBE_DROP;
+ }
+ } else {
+ /* retransmission packets */
+ }
+
+ gst_rtp_buffer_unmap (&rtp);
+ }
+
+ return ret;
+}
+
+static GstPadProbeReturn
+rtprtxreceive_srcpad_probe (GstPad * pad, GstPadProbeInfo * info,
+ gpointer user_data)
+{
+ if (info->type == (GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_PUSH)) {
+ GstBuffer *buffer = GST_BUFFER (info->data);
+ RTXReceiveData *rtxdata = (RTXReceiveData *) user_data;
+ GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
+ guint seqnum = 0;
+ guint i = 0;
+
+ gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp);
+ seqnum = gst_rtp_buffer_get_seq (&rtp);
+
+ /* check if there is a dropped packet */
+ if (seqnum > rtxdata->seqnum_prev + rtxdata->seqnum_offset) {
+ GstPad *peerpad = gst_pad_get_peer (pad);
+
+ /* ask retransmission of missing packet */
+ for (i = rtxdata->seqnum_prev + rtxdata->seqnum_offset; i < seqnum;
+ i += rtxdata->seqnum_offset) {
+ GstEvent *event = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
+ gst_structure_new ("GstRTPRetransmissionRequest",
+ "seqnum", G_TYPE_UINT, i,
+ "ssrc", G_TYPE_UINT, gst_rtp_buffer_get_ssrc (&rtp),
+ "payload-type", G_TYPE_UINT,
+ gst_rtp_buffer_get_payload_type (&rtp),
+ NULL));
+ gst_pad_push_event (peerpad, event);
+ }
+ gst_object_unref (peerpad);
+
+ rtxdata->seqnum_prev = seqnum;
+ } else if (seqnum == rtxdata->seqnum_prev + rtxdata->seqnum_offset) {
+ /* also update previous seqnum in this case */
+ rtxdata->seqnum_prev = seqnum;
+ }
+
+ gst_rtp_buffer_unmap (&rtp);
+
+ ++rtxdata->nb_packets;
+ }
+
+ return GST_PAD_PROBE_OK;
+}
+
+static void
+start_test_drop_and_check_results (GstElement * bin, GstElement * rtppayloader,
+ GstElement * rtprtxsend, GstElement * rtprtxreceive,
+ RTXSendData * send_rtxdata, RTXReceiveData * receive_rtxdata,
+ guint drop_every_n_packets, gboolean * eos)
+{
+ GstStateChangeReturn state_res = GST_STATE_CHANGE_FAILURE;
+ guint nbrtxrequests = 0;
+ guint nbrtxpackets = 0;
+ guint nb_expected_requests = 0;
+ GstStructure *pt_map;
+
+ GST_INFO ("starting test");
+
+ pt_map = gst_structure_new ("application/x-rtp-pt-map",
+ "96", G_TYPE_UINT, 99, NULL);
+ g_object_set (rtppayloader, "pt", 96, NULL);
+ g_object_set (rtppayloader, "seqnum-offset", 1, NULL);
+ g_object_set (rtprtxsend, "payload-type-map", pt_map, NULL);
+ g_object_set (rtprtxreceive, "payload-type-map", pt_map, NULL);
+ gst_structure_free (pt_map);
+
+ send_rtxdata->count = 1;
+ send_rtxdata->nb_packets = 0;
+ send_rtxdata->drop_every_n_packets = drop_every_n_packets;
+
+ receive_rtxdata->nb_packets = 0;
+ receive_rtxdata->seqnum_offset = 0;
+ receive_rtxdata->seqnum_prev = 0;
+
+ *eos = FALSE;
+
+ /* retrieve offset before going to paused */
+ g_object_get (G_OBJECT (rtppayloader), "seqnum-offset",
+ &receive_rtxdata->seqnum_offset, NULL);
+
+ /* prepare playing */
+ state_res = gst_element_set_state (bin, GST_STATE_PAUSED);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ /* wait for completion */
+ state_res = gst_element_get_state (bin, NULL, NULL, GST_CLOCK_TIME_NONE);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ /* retrieve seqnum_prev here to make sure it has been reseted */
+ g_object_get (G_OBJECT (rtppayloader), "seqnum",
+ &receive_rtxdata->seqnum_prev, NULL);
+
+ /* run pipeline */
+ state_res = gst_element_set_state (bin, GST_STATE_PLAYING);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ GST_INFO ("running main loop");
+ while (!*eos)
+ g_main_context_iteration (NULL, TRUE);
+
+ /* check results */
+
+ if (send_rtxdata->nb_packets % drop_every_n_packets == 0) {
+ /* special case because the last buffer will be dropped
+ * so the receiver cannot know if it has been dropped (no next packet)
+ */
+ nb_expected_requests = send_rtxdata->nb_packets / drop_every_n_packets - 1;
+ fail_unless_equals_int (send_rtxdata->nb_packets,
+ receive_rtxdata->nb_packets + 1);
+ } else {
+ nb_expected_requests = send_rtxdata->nb_packets / drop_every_n_packets;
+ fail_unless_equals_int (send_rtxdata->nb_packets,
+ receive_rtxdata->nb_packets);
+ }
+
+ g_object_get (G_OBJECT (rtprtxsend), "num-rtx-requests", &nbrtxrequests,
+ NULL);
+ fail_unless_equals_int (nbrtxrequests, nb_expected_requests);
+
+ g_object_get (G_OBJECT (rtprtxsend), "num-rtx-packets", &nbrtxpackets, NULL);
+ fail_unless_equals_int (nbrtxpackets, nb_expected_requests);
+
+ g_object_get (G_OBJECT (rtprtxreceive), "num-rtx-requests", &nbrtxrequests,
+ NULL);
+ fail_unless_equals_int (nbrtxrequests, nb_expected_requests);
+
+ g_object_get (G_OBJECT (rtprtxreceive), "num-rtx-packets", &nbrtxpackets,
+ NULL);
+ fail_unless_equals_int (nbrtxpackets, nb_expected_requests);
+
+ g_object_get (G_OBJECT (rtprtxreceive), "num-rtx-assoc-packets",
+ &nbrtxpackets, NULL);
+ fail_unless_equals_int (nbrtxpackets, nb_expected_requests);
+
+ state_res = gst_element_set_state (bin, GST_STATE_NULL);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+}
+
+/* This test build the pipeline videotestsrc ! rtpvrawpay ! rtprtxsend ! rtprtxreceive ! fakesink
+ * and drop some buffer between rtprtxsend and rtprtxreceive
+ * Then it checks that every dropped packet has been re-sent and it checks that
+ * not too much requests has been sent.
+ */
+GST_START_TEST (test_drop_one_sender)
+{
+ GstElement *bin, *src, *rtppayloader, *rtprtxsend, *rtprtxreceive, *sink;
+ GstBus *bus;
+ gboolean res;
+ GstPad *srcpad, *sinkpad;
+ GstStreamConsistency *chk_1, *chk_2, *chk_3;
+ gint num_buffers = 20;
+ guint drop_every_n_packets = 0;
+ RTXSendData send_rtxdata;
+ RTXReceiveData receive_rtxdata;
+ gboolean eos = FALSE;
+
+ GST_INFO ("preparing test");
+
+ /* build pipeline */
+ bin = gst_pipeline_new ("pipeline");
+ bus = gst_element_get_bus (bin);
+ gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);
+
+ src = gst_element_factory_make ("videotestsrc", "src");
+ g_object_set (src, "num-buffers", num_buffers, NULL);
+ rtppayloader = gst_element_factory_make ("rtpvrawpay", "rtppayloader");
+ rtprtxsend = gst_element_factory_make ("rtprtxsend", "rtprtxsend");
+ rtprtxreceive = gst_element_factory_make ("rtprtxreceive", "rtprtxreceive");
+ sink = gst_element_factory_make ("fakesink", "sink");
+ gst_bin_add_many (GST_BIN (bin), src, rtppayloader, rtprtxsend, rtprtxreceive,
+ sink, NULL);
+
+ res = gst_element_link (src, rtppayloader);
+ fail_unless (res == TRUE, NULL);
+ res = gst_element_link (rtppayloader, rtprtxsend);
+ fail_unless (res == TRUE, NULL);
+ res = gst_element_link (rtprtxsend, rtprtxreceive);
+ fail_unless (res == TRUE, NULL);
+ res = gst_element_link (rtprtxreceive, sink);
+ fail_unless (res == TRUE, NULL);
+
+ /* create consistency checkers for the pads */
+
+ srcpad = gst_element_get_static_pad (rtppayloader, "src");
+ chk_1 = gst_consistency_checker_new (srcpad);
+ gst_object_unref (srcpad);
+
+ srcpad = gst_element_get_static_pad (rtprtxsend, "src");
+ gst_pad_add_probe (srcpad,
+ (GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_PUSH),
+ (GstPadProbeCallback) rtprtxsend_srcpad_probe, &send_rtxdata, NULL);
+ sinkpad = gst_pad_get_peer (srcpad);
+ fail_if (sinkpad == NULL);
+ chk_2 = gst_consistency_checker_new (sinkpad);
+ gst_object_unref (sinkpad);
+ gst_object_unref (srcpad);
+
+ srcpad = gst_element_get_static_pad (rtprtxreceive, "src");
+ gst_pad_add_probe (srcpad,
+ (GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_PUSH),
+ (GstPadProbeCallback) rtprtxreceive_srcpad_probe, &receive_rtxdata, NULL);
+ sinkpad = gst_pad_get_peer (srcpad);
+ fail_if (sinkpad == NULL);
+ chk_3 = gst_consistency_checker_new (sinkpad);
+ gst_object_unref (sinkpad);
+ gst_object_unref (srcpad);
+
+ g_signal_connect (bus, "message::error", (GCallback) message_received, NULL);
+ g_signal_connect (bus, "message::warning", (GCallback) message_received,
+ NULL);
+ g_signal_connect (bus, "message::eos", (GCallback) message_received, &eos);
+
+ for (drop_every_n_packets = 2; drop_every_n_packets < 10;
+ drop_every_n_packets++) {
+ start_test_drop_and_check_results (bin, rtppayloader, rtprtxsend,
+ rtprtxreceive, &send_rtxdata, &receive_rtxdata, drop_every_n_packets,
+ &eos);
+ }
+
+ /* cleanup */
+ gst_consistency_checker_free (chk_1);
+ gst_consistency_checker_free (chk_2);
+ gst_consistency_checker_free (chk_3);
+ gst_bus_remove_signal_watch (bus);
+ gst_object_unref (bus);
+ gst_object_unref (bin);
+}
+
+GST_END_TEST;
+
+static void
+message_received_multiple (GstBus * bus, GstMessage * message, gpointer data)
+{
+ GST_INFO ("bus message from \"%" GST_PTR_FORMAT "\": %" GST_PTR_FORMAT,
+ GST_MESSAGE_SRC (message), message);
+
+ switch (message->type) {
+ case GST_MESSAGE_WARNING:{
+ GError *gerror;
+ gchar *debug;
+
+ gst_message_parse_warning (message, &gerror, &debug);
+ gst_object_default_error (GST_MESSAGE_SRC (message), gerror, debug);
+ g_error_free (gerror);
+ g_free (debug);
+ break;
+ }
+ case GST_MESSAGE_ERROR:{
+ GError *gerror;
+ gchar *debug;
+
+ gst_message_parse_error (message, &gerror, &debug);
+ gst_object_default_error (GST_MESSAGE_SRC (message), gerror, debug);
+ fail ("Error: %s / %s", gerror->message, debug);
+ g_error_free (gerror);
+ g_free (debug);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+typedef struct
+{
+ guint count;
+ guint nb_packets;
+ guint drop_every_n_packets;
+ guint payload_type_master;
+ guint total_packets;
+} RTXSendMultipleData;
+
+/* drop some packets */
+static GstPadProbeReturn
+rtprtxsend_srcpad_probe_multiple (GstPad * pad, GstPadProbeInfo * info,
+ gpointer user_data)
+{
+ GstPadProbeReturn ret = GST_PAD_PROBE_OK;
+
+ if (info->type == (GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_PUSH)) {
+ GstBuffer *buffer = GST_BUFFER (info->data);
+ RTXSendMultipleData *rtxdata = (RTXSendMultipleData *) user_data;
+ GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
+ guint payload_type = 0;
+
+ gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp);
+ payload_type = gst_rtp_buffer_get_payload_type (&rtp);
+
+ /* main stream packets */
+ if (payload_type == rtxdata->payload_type_master) {
+ /* count packets of the main stream */
+ ++rtxdata->nb_packets;
+ /* drop some packets */
+ /* but make sure we never drop the last one, otherwise there
+ * will be nothing to trigger a retransmission.
+ */
+ if (rtxdata->count < rtxdata->drop_every_n_packets ||
+ rtxdata->nb_packets == rtxdata->total_packets) {
+ ++rtxdata->count;
+ } else {
+ /* drop a packet every 'rtxdata->count' packets */
+ rtxdata->count = 1;
+ ret = GST_PAD_PROBE_DROP;
+ }
+ } else {
+ /* retransmission packets */
+ }
+
+ gst_rtp_buffer_unmap (&rtp);
+ }
+
+ return ret;
+}
+
+/* make sure every sources has sent all their buffers */
+static GstPadProbeReturn
+source_srcpad_probe_multiple_drop_eos (GstPad * pad, GstPadProbeInfo * info,
+ gpointer user_data)
+{
+ GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info);
+
+ if (GST_EVENT_TYPE (event) == GST_EVENT_EOS)
+ return GST_PAD_PROBE_DROP;
+ else
+ return GST_PAD_PROBE_OK;
+}
+
+typedef struct
+{
+ GHashTable *ssrc_to_nb_packets_map;
+ GHashTable *ssrc_to_seqnum_offset_map;
+ guint seqnum_offset;
+
+ gint to_send;
+ volatile gint dropped_requests;
+ volatile gint received;
+ gboolean request_passed;
+} RTXReceiveMultipleData;
+
+/* add one branch videotestsrc ! rtpvrawpay ! rtprtxsend ! queue ! funnel. */
+static RTXSendMultipleData *
+add_sender (GstElement * bin, const gchar * src_name,
+ const gchar * payloader_name, guint payload_type_master,
+ guint payload_type_aux, RTXReceiveMultipleData * rtxdata)
+{
+ GstElement *src = NULL;
+ GstCaps *caps;
+ GstElement *rtppayloader = NULL;
+ GstElement *rtprtxsend = NULL;
+ GstElement *queue = NULL;
+ GstElement *funnel = NULL;
+ GstPad *srcpad = NULL;
+ gboolean res = FALSE;
+ RTXSendMultipleData *send_rtxdata = g_slice_new0 (RTXSendMultipleData);
+ gchar *pt_master;
+ GstStructure *pt_map;
+
+ send_rtxdata->count = 1;
+ send_rtxdata->nb_packets = 0;
+ send_rtxdata->drop_every_n_packets = 0;
+ send_rtxdata->payload_type_master = payload_type_master;
+ send_rtxdata->total_packets = 25;
+ rtxdata->to_send += send_rtxdata->total_packets;
+
+ src = gst_element_factory_make (src_name, NULL);
+ rtppayloader = gst_element_factory_make (payloader_name, NULL);
+ rtprtxsend = gst_element_factory_make ("rtprtxsend", NULL);
+ queue = gst_element_factory_make ("queue", NULL);
+ funnel = gst_bin_get_by_name (GST_BIN (bin), "funnel");
+
+ pt_master = g_strdup_printf ("%" G_GUINT32_FORMAT, payload_type_master);
+ pt_map = gst_structure_new ("application/x-rtp-pt-map",
+ pt_master, G_TYPE_UINT, payload_type_aux, NULL);
+ g_free (pt_master);
+
+ g_object_set (src, "num-buffers", send_rtxdata->total_packets, NULL);
+ g_object_set (src, "is-live", TRUE, NULL);
+ g_object_set (rtppayloader, "pt", payload_type_master, NULL);
+ g_object_set (rtppayloader, "seqnum-offset", 1, NULL);
+ g_object_set (rtprtxsend, "payload-type-map", pt_map, NULL);
+ /* we want that every drop packet be resent fast */
+ g_object_set (queue, "max-size-buffers", 1, NULL);
+ g_object_set (queue, "flush-on-eos", FALSE, NULL);
+
+ gst_structure_free (pt_map);
+
+ gst_bin_add_many (GST_BIN (bin), src, rtppayloader, rtprtxsend, queue, NULL);
+
+ /* Make sure we have one buffer per frame, makes it easier to count! */
+ caps =
+ gst_caps_from_string ("video/x-raw, width=20, height=10, framerate=30/1");
+ res = gst_element_link_filtered (src, rtppayloader, caps);
+ gst_caps_unref (caps);
+ fail_unless (res == TRUE, NULL);
+ res = gst_element_link (rtppayloader, rtprtxsend);
+ fail_unless (res == TRUE, NULL);
+ res = gst_element_link (rtprtxsend, queue);
+ fail_unless (res == TRUE, NULL);
+ res = gst_element_link (queue, funnel);
+ fail_unless (res == TRUE, NULL);
+ gst_object_unref (funnel);
+
+ /* to drop some packets */
+ srcpad = gst_element_get_static_pad (rtprtxsend, "src");
+ gst_pad_add_probe (srcpad,
+ (GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_PUSH),
+ (GstPadProbeCallback) rtprtxsend_srcpad_probe_multiple, send_rtxdata,
+ NULL);
+ gst_object_unref (srcpad);
+
+ /* to make sure every sources has sent all their buffers */
+ srcpad = gst_element_get_static_pad (src, "src");
+ gst_pad_add_probe (srcpad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
+ (GstPadProbeCallback) source_srcpad_probe_multiple_drop_eos, NULL, NULL);
+ gst_object_unref (srcpad);
+
+ return send_rtxdata;
+}
+
+static GstPadProbeReturn
+rtprtxreceive_sinkpad_probe_check_drop (GstPad * pad, GstPadProbeInfo * info,
+ gpointer user_data)
+{
+ GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info);
+ RTXReceiveMultipleData *rtxdata = (RTXReceiveMultipleData *) user_data;
+
+ if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_UPSTREAM &&
+ gst_event_get_structure (event) != NULL &&
+ gst_structure_has_name (gst_event_get_structure (event),
+ "GstRTPRetransmissionRequest"))
+ rtxdata->request_passed = TRUE;
+
+ return GST_PAD_PROBE_OK;
+}
+
+static gboolean
+check_finished (RTXReceiveMultipleData * rtxdata)
+{
+ return (g_atomic_int_get (&rtxdata->received) >= (rtxdata->to_send -
+ g_atomic_int_get (&rtxdata->dropped_requests)));
+}
+
+static GstPadProbeReturn
+rtprtxreceive_srcpad_probe_multiple (GstPad * pad, GstPadProbeInfo * info,
+ gpointer user_data)
+{
+ if (info->type == (GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_PUSH)) {
+ GstBuffer *buffer = GST_BUFFER (info->data);
+ RTXReceiveMultipleData *rtxdata = (RTXReceiveMultipleData *) user_data;
+ GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
+ guint ssrc = 0;
+ guint seqnum = 0;
+ gpointer seqnum_prev = 0;
+ guint nb_packets = 0;
+
+ gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp);
+ ssrc = gst_rtp_buffer_get_ssrc (&rtp);
+ seqnum = gst_rtp_buffer_get_seq (&rtp);
+
+ g_atomic_int_inc (&rtxdata->received);
+ if (check_finished (rtxdata))
+ g_main_context_wakeup (NULL);
+
+ if (!g_hash_table_lookup_extended (rtxdata->ssrc_to_seqnum_offset_map,
+ GUINT_TO_POINTER (ssrc), NULL, &seqnum_prev)) {
+ /*In our test we take care to never drop the first buffer */
+ g_hash_table_insert (rtxdata->ssrc_to_seqnum_offset_map,
+ GUINT_TO_POINTER (ssrc), GUINT_TO_POINTER (seqnum));
+ g_hash_table_insert (rtxdata->ssrc_to_nb_packets_map,
+ GUINT_TO_POINTER (ssrc), GUINT_TO_POINTER (1));
+ gst_rtp_buffer_unmap (&rtp);
+ return GST_PAD_PROBE_OK;
+ }
+
+
+ /* check if there is a dropped packet
+ * (in our test every packet arrived in increasing order) */
+ if (seqnum > GPOINTER_TO_UINT (seqnum_prev) + rtxdata->seqnum_offset) {
+ GstPad *peerpad = gst_pad_get_peer (pad);
+ guint i = 0;
+
+ /* ask retransmission of missing packets */
+ for (i = GPOINTER_TO_UINT (seqnum_prev) + rtxdata->seqnum_offset;
+ i < seqnum; i += rtxdata->seqnum_offset) {
+ GstEvent *event = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
+ gst_structure_new ("GstRTPRetransmissionRequest",
+ "seqnum", G_TYPE_UINT, i,
+ "ssrc", G_TYPE_UINT, gst_rtp_buffer_get_ssrc (&rtp),
+ "payload-type", G_TYPE_UINT,
+ gst_rtp_buffer_get_payload_type (&rtp),
+ NULL));
+ rtxdata->request_passed = FALSE;
+ gst_pad_push_event (peerpad, event);
+ if (!rtxdata->request_passed) {
+ g_atomic_int_inc (&rtxdata->dropped_requests);
+ if (check_finished (rtxdata))
+ g_main_context_wakeup (NULL);
+ }
+ }
+ gst_object_unref (peerpad);
+
+ g_hash_table_insert (rtxdata->ssrc_to_seqnum_offset_map,
+ GUINT_TO_POINTER (ssrc), GUINT_TO_POINTER (seqnum));
+ } else if (seqnum ==
+ GPOINTER_TO_UINT (seqnum_prev) + rtxdata->seqnum_offset) {
+ /* also update previous seqnum in this case */
+ g_hash_table_insert (rtxdata->ssrc_to_seqnum_offset_map,
+ GUINT_TO_POINTER (ssrc), GUINT_TO_POINTER (seqnum));
+ } else {
+ /* receive retransmited packet */
+ }
+
+ gst_rtp_buffer_unmap (&rtp);
+
+ nb_packets =
+ GPOINTER_TO_UINT (g_hash_table_lookup (rtxdata->ssrc_to_nb_packets_map,
+ GUINT_TO_POINTER (ssrc)));
+ g_hash_table_insert (rtxdata->ssrc_to_nb_packets_map,
+ GUINT_TO_POINTER (ssrc), GUINT_TO_POINTER (++nb_packets));
+ }
+
+ return GST_PAD_PROBE_OK;
+}
+
+static void
+reset_rtx_send_data (RTXSendMultipleData * send_rtxdata, gpointer data)
+{
+ send_rtxdata->count = 1;
+ send_rtxdata->nb_packets = 0;
+ send_rtxdata->drop_every_n_packets = *(guint *) data;
+}
+
+/* compute number of all packets sent by all sender */
+static void
+compute_total_packets_sent (RTXSendMultipleData * send_rtxdata, gpointer data)
+{
+ guint *sum = (guint *) data;
+ *sum += send_rtxdata->nb_packets;
+}
+
+/* compute number of all packets received by rtprtxreceive::src pad */
+static void
+compute_total_packets_received (gpointer key, gpointer value, gpointer data)
+{
+ guint *sum = (guint *) data;
+ *sum += GPOINTER_TO_UINT (value);
+}
+
+static void
+start_test_drop_multiple_and_check_results (GstElement * bin,
+ GList * send_rtxdata_list, RTXReceiveMultipleData * receive_rtxdata,
+ guint drop_every_n_packets)
+{
+ GstStateChangeReturn state_res = GST_STATE_CHANGE_FAILURE;
+ GstElement *rtprtxreceive =
+ gst_bin_get_by_name (GST_BIN (bin), "rtprtxreceive");
+ guint sum_all_packets_sent = 0;
+ guint sum_rtx_packets_sent = 0;
+ guint sum_all_packets_received = 0;
+ guint sum_rtx_packets_received = 0;
+ guint sum_rtx_assoc_packets_received = 0;
+ guint sum_rtx_dropped_packets_received = 0;
+ gdouble error_sent_recv = 0;
+ GstIterator *itr_elements = NULL;
+ gboolean done = FALSE;
+ GValue item = { 0 };
+ GstElement *element = NULL;
+ gchar *name = NULL;
+
+ GST_INFO ("starting test");
+
+ g_atomic_int_set (&receive_rtxdata->received, 0);
+ g_atomic_int_set (&receive_rtxdata->dropped_requests, 0);
+
+ g_hash_table_remove_all (receive_rtxdata->ssrc_to_nb_packets_map);
+ g_hash_table_remove_all (receive_rtxdata->ssrc_to_seqnum_offset_map);
+
+ g_list_foreach (send_rtxdata_list, (GFunc) reset_rtx_send_data,
+ &drop_every_n_packets);
+
+ /* run pipeline */
+ state_res = gst_element_set_state (bin, GST_STATE_PLAYING);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ state_res = gst_element_get_state (bin, NULL, NULL, GST_CLOCK_TIME_NONE);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ GST_INFO ("running main loop");
+ while (!check_finished (receive_rtxdata))
+ g_main_context_iteration (NULL, TRUE);
+
+ /* check results */
+ itr_elements = gst_bin_iterate_elements (GST_BIN (bin));
+ done = FALSE;
+ while (!done) {
+ switch (gst_iterator_next (itr_elements, &item)) {
+ case GST_ITERATOR_OK:
+ element = GST_ELEMENT (g_value_get_object (&item));
+ name = gst_element_get_name (element);
+ if (g_str_has_prefix (name, "rtprtxsend") > 0) {
+ guint nb_packets = 0;
+ g_object_get (G_OBJECT (element), "num-rtx-packets", &nb_packets,
+ NULL);
+ sum_rtx_packets_sent += nb_packets;
+ }
+ g_free (name);
+ g_value_reset (&item);
+ break;
+ case GST_ITERATOR_RESYNC:
+ gst_iterator_resync (itr_elements);
+ break;
+ case GST_ITERATOR_ERROR:
+ done = TRUE;
+ break;
+ case GST_ITERATOR_DONE:
+ done = TRUE;
+ break;
+ }
+ }
+ g_value_unset (&item);
+ gst_iterator_free (itr_elements);
+
+ /* compute number of all packets sent by all sender */
+ g_list_foreach (send_rtxdata_list, (GFunc) compute_total_packets_sent,
+ &sum_all_packets_sent);
+
+ /* compute number of all packets received by rtprtxreceive::src pad */
+ g_hash_table_foreach (receive_rtxdata->ssrc_to_nb_packets_map,
+ compute_total_packets_received, (gpointer) & sum_all_packets_received);
+
+ sum_all_packets_received +=
+ g_atomic_int_get (&receive_rtxdata->dropped_requests);
+ fail_if (sum_all_packets_sent < sum_all_packets_received);
+
+ /* some packet are not received, I still have to figure out why
+ * but I suspect it comes from pipeline setup/shutdown
+ */
+ if (sum_all_packets_sent != sum_all_packets_received) {
+ error_sent_recv =
+ 1 - sum_all_packets_received / (gdouble) sum_all_packets_sent;
+ fail_if (error_sent_recv > 0.30);
+ /* it should be 0% */
+ }
+
+ /* retrieve number of retransmit packets received by rtprtxreceive */
+ g_object_get (G_OBJECT (rtprtxreceive), "num-rtx-packets",
+ &sum_rtx_packets_received, NULL);
+
+ /* some of rtx packet are not received because the receiver avoids
+ * collision (= requests that have the same seqnum)
+ */
+ fail_if (sum_rtx_packets_sent < sum_rtx_packets_received);
+ g_object_get (G_OBJECT (rtprtxreceive), "num-rtx-assoc-packets",
+ &sum_rtx_assoc_packets_received, NULL);
+ sum_rtx_dropped_packets_received =
+ sum_rtx_packets_received - sum_rtx_assoc_packets_received;
+ fail_unless_equals_int (sum_rtx_packets_sent,
+ sum_rtx_assoc_packets_received + sum_rtx_dropped_packets_received);
+
+ gst_object_unref (rtprtxreceive);
+ state_res = gst_element_set_state (bin, GST_STATE_NULL);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+}
+
+static void
+free_rtx_send_data (gpointer data)
+{
+ g_slice_free (RTXSendMultipleData, data);
+}
+
+/* This test build the pipeline funnel name=funnel
+ * videotestsrc ! rtpvrawpay ! rtprtxsend ! queue ! funnel.
+ * videotestsrc ! rtpvrawpay ! rtprtxsend ! queue ! funnel.
+ * N
+ * funnel. ! rtprtxreceive ! fakesink
+ * and drop some buffer just after each rtprtxsend
+ * Then it checks that every dropped packet has been re-sent and it checks
+ * that not too much requests has been sent.
+ */
+GST_START_TEST (test_drop_multiple_sender)
+{
+ GstElement *bin, *funnel, *rtprtxreceive, *sink;
+ GstBus *bus;
+ gboolean res;
+ GstPad *srcpad, *sinkpad;
+ guint drop_every_n_packets = 0;
+ GList *send_rtxdata_list = NULL;
+ RTXReceiveMultipleData receive_rtxdata = { NULL };
+ GstStructure *pt_map;
+
+ GST_INFO ("preparing test");
+
+ receive_rtxdata.ssrc_to_nb_packets_map =
+ g_hash_table_new (g_direct_hash, g_direct_equal);
+ receive_rtxdata.ssrc_to_seqnum_offset_map =
+ g_hash_table_new (g_direct_hash, g_direct_equal);
+ receive_rtxdata.seqnum_offset = 1;
+
+ /* build pipeline */
+ bin = gst_pipeline_new ("pipeline");
+ bus = gst_element_get_bus (bin);
+ gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);
+
+ funnel = gst_element_factory_make ("funnel", "funnel");
+ rtprtxreceive = gst_element_factory_make ("rtprtxreceive", "rtprtxreceive");
+ sink = gst_element_factory_make ("fakesink", "sink");
+ g_object_set (sink, "sync", TRUE, NULL);
+ g_object_set (sink, "qos", FALSE, NULL);
+ gst_bin_add_many (GST_BIN (bin), funnel, rtprtxreceive, sink, NULL);
+
+ send_rtxdata_list =
+ g_list_append (send_rtxdata_list, add_sender (bin, "videotestsrc",
+ "rtpvrawpay", 96, 121, &receive_rtxdata));
+ send_rtxdata_list =
+ g_list_append (send_rtxdata_list, add_sender (bin, "videotestsrc",
+ "rtpvrawpay", 97, 122, &receive_rtxdata));
+ send_rtxdata_list =
+ g_list_append (send_rtxdata_list, add_sender (bin, "videotestsrc",
+ "rtpvrawpay", 98, 123, &receive_rtxdata));
+ send_rtxdata_list =
+ g_list_append (send_rtxdata_list, add_sender (bin, "videotestsrc",
+ "rtpvrawpay", 99, 124, &receive_rtxdata));
+
+ pt_map = gst_structure_new ("application/x-rtp-pt-map",
+ "96", G_TYPE_UINT, 121, "97", G_TYPE_UINT, 122,
+ "98", G_TYPE_UINT, 123, "99", G_TYPE_UINT, 124, NULL);
+ g_object_set (rtprtxreceive, "payload-type-map", pt_map, NULL);
+ gst_structure_free (pt_map);
+
+ res = gst_element_link (funnel, rtprtxreceive);
+ fail_unless (res == TRUE, NULL);
+ res = gst_element_link (rtprtxreceive, sink);
+ fail_unless (res == TRUE, NULL);
+
+ srcpad = gst_element_get_static_pad (rtprtxreceive, "src");
+ gst_pad_add_probe (srcpad,
+ (GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_PUSH),
+ (GstPadProbeCallback) rtprtxreceive_srcpad_probe_multiple,
+ &receive_rtxdata, NULL);
+ gst_object_unref (srcpad);
+
+ sinkpad = gst_element_get_static_pad (rtprtxreceive, "sink");
+ gst_pad_add_probe (sinkpad,
+ GST_PAD_PROBE_TYPE_EVENT_UPSTREAM,
+ (GstPadProbeCallback) rtprtxreceive_sinkpad_probe_check_drop,
+ &receive_rtxdata, NULL);
+ gst_object_unref (sinkpad);
+
+ g_signal_connect (bus, "message::error",
+ (GCallback) message_received_multiple, NULL);
+ g_signal_connect (bus, "message::warning",
+ (GCallback) message_received_multiple, NULL);
+
+ for (drop_every_n_packets = 2; drop_every_n_packets < 10;
+ drop_every_n_packets++) {
+ start_test_drop_multiple_and_check_results (bin, send_rtxdata_list,
+ &receive_rtxdata, drop_every_n_packets);
+ }
+
+ /* cleanup */
+
+ g_list_free_full (send_rtxdata_list, free_rtx_send_data);
+ g_hash_table_destroy (receive_rtxdata.ssrc_to_nb_packets_map);
+ g_hash_table_destroy (receive_rtxdata.ssrc_to_seqnum_offset_map);
+
+ gst_bus_remove_signal_watch (bus);
+ gst_object_unref (bus);
+ gst_object_unref (bin);
+}
+
+GST_END_TEST;
+
+struct GenerateTestBuffersData
+{
+ GstElement *src, *capsfilter, *payloader, *sink;
+ GMutex mutex;
+ GCond cond;
+ GList *buffers;
+ gint num_buffers;
+ guint last_seqnum;
+};
+
+static void
+fakesink_handoff (GstElement * sink, GstBuffer * buf, GstPad * pad,
+ gpointer user_data)
+{
+ struct GenerateTestBuffersData *data = user_data;
+
+ g_mutex_lock (&data->mutex);
+
+ if (data->num_buffers > 0)
+ data->buffers = g_list_append (data->buffers, gst_buffer_ref (buf));
+
+ /* if we have collected enough buffers, unblock the main thread to stop */
+ if (--data->num_buffers <= 0)
+ g_cond_signal (&data->cond);
+
+ if (data->num_buffers == 0)
+ g_object_get (data->payloader, "seqnum", &data->last_seqnum, NULL);
+
+ g_mutex_unlock (&data->mutex);
+}
+
+static GList *
+generate_test_buffers (const gint num_buffers, guint ssrc, guint * payload_type)
+{
+ GstElement *bin;
+ GstCaps *videotestsrc_caps;
+ gboolean res;
+ struct GenerateTestBuffersData data;
+
+ fail_unless (num_buffers > 0);
+
+ g_mutex_init (&data.mutex);
+ g_cond_init (&data.cond);
+ data.buffers = NULL;
+ data.num_buffers = num_buffers;
+
+ bin = gst_pipeline_new (NULL);
+ data.src = gst_element_factory_make ("videotestsrc", NULL);
+ data.capsfilter = gst_element_factory_make ("capsfilter", NULL);
+ data.payloader = gst_element_factory_make ("rtpvrawpay", NULL);
+ data.sink = gst_element_factory_make ("fakesink", NULL);
+
+ /* small frame size will cause vrawpay to generate exactly one rtp packet
+ * per video frame, which we need for the max-size-time test */
+ videotestsrc_caps =
+ gst_caps_from_string
+ ("video/x-raw,format=I420,width=10,height=10,framerate=30/1");
+
+ g_object_set (data.src, "do-timestamp", TRUE, NULL);
+ g_object_set (data.capsfilter, "caps", videotestsrc_caps, NULL);
+ g_object_set (data.payloader, "seqnum-offset", 1, "ssrc", ssrc, NULL);
+ g_object_set (data.sink, "signal-handoffs", TRUE, NULL);
+ g_signal_connect (data.sink, "handoff", (GCallback) fakesink_handoff, &data);
+
+ gst_caps_unref (videotestsrc_caps);
+
+ gst_bin_add_many (GST_BIN (bin), data.src, data.capsfilter, data.payloader,
+ data.sink, NULL);
+ res = gst_element_link_many (data.src, data.capsfilter, data.payloader,
+ data.sink, NULL);
+ fail_unless_equals_int (res, TRUE);
+
+ g_mutex_lock (&data.mutex);
+ ASSERT_SET_STATE (bin, GST_STATE_PLAYING, GST_STATE_CHANGE_ASYNC);
+ while (data.num_buffers > 0)
+ g_cond_wait (&data.cond, &data.mutex);
+ g_mutex_unlock (&data.mutex);
+
+ g_object_get (data.payloader, "pt", payload_type, NULL);
+
+ ASSERT_SET_STATE (bin, GST_STATE_NULL, GST_STATE_CHANGE_SUCCESS);
+
+ fail_unless_equals_int (g_list_length (data.buffers), num_buffers);
+ fail_unless_equals_int (num_buffers, data.last_seqnum);
+
+ g_mutex_clear (&data.mutex);
+ g_cond_clear (&data.cond);
+ gst_object_unref (bin);
+
+ return data.buffers;
+}
+
+static GstEvent *
+create_rtx_event (guint seqnum, guint ssrc, guint payload_type)
+{
+ return gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
+ gst_structure_new ("GstRTPRetransmissionRequest",
+ "seqnum", G_TYPE_UINT, seqnum,
+ "ssrc", G_TYPE_UINT, ssrc,
+ "payload-type", G_TYPE_UINT, payload_type, NULL));
+}
+
+static void
+test_rtxsender_packet_retention (gboolean test_with_time)
+{
+ const gint num_buffers = test_with_time ? 30 : 10;
+ const gint half_buffers = num_buffers / 2;
+ const guint ssrc = 1234567;
+ const guint rtx_ssrc = 7654321;
+ const guint rtx_payload_type = 99;
+ GstStructure *pt_map;
+ GstStructure *ssrc_map;
+ GList *in_buffers, *node;
+ guint payload_type;
+ GstElement *rtxsend;
+ GstPad *srcpad, *sinkpad;
+ GstCaps *caps;
+ GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
+ gint i, j;
+ gboolean res;
+
+ /* generate test data */
+ in_buffers = generate_test_buffers (num_buffers, ssrc, &payload_type);
+
+ /* clear the global buffers list, which we are going to use later */
+ gst_check_drop_buffers ();
+
+ /* setup element & pads */
+ rtxsend = gst_check_setup_element ("rtprtxsend");
+
+ pt_map = gst_structure_new ("application/x-rtp-pt-map",
+ "96", G_TYPE_UINT, rtx_payload_type, NULL);
+ ssrc_map = gst_structure_new ("application/x-rtp-ssrc-map",
+ "1234567", G_TYPE_UINT, rtx_ssrc, NULL);
+
+ /* in both cases we want the rtxsend queue to store 'half_buffers'
+ * amount of buffers at most. In max-size-packets mode, it's trivial.
+ * In max-size-time mode, we specify almost half a second, which is
+ * the equivalent of 15 frames in a 30fps video stream */
+ g_object_set (rtxsend,
+ "max-size-packets", test_with_time ? 0 : half_buffers,
+ "max-size-time", test_with_time ? 499 : 0,
+ "payload-type-map", pt_map, "ssrc-map", ssrc_map, NULL);
+ gst_structure_free (pt_map);
+ gst_structure_free (ssrc_map);
+
+ srcpad = gst_check_setup_src_pad (rtxsend, &srctemplate);
+ fail_unless_equals_int (gst_pad_set_active (srcpad, TRUE), TRUE);
+
+ sinkpad = gst_check_setup_sink_pad (rtxsend, &sinktemplate);
+ fail_unless_equals_int (gst_pad_set_active (sinkpad, TRUE), TRUE);
+
+ ASSERT_SET_STATE (rtxsend, GST_STATE_PLAYING, GST_STATE_CHANGE_SUCCESS);
+
+ caps = gst_caps_from_string ("application/x-rtp, "
+ "media = (string)video, payload = (int)96, "
+ "ssrc = (uint)1234567, clock-rate = (int)90000, "
+ "encoding-name = (string)RAW");
+ gst_check_setup_events (srcpad, rtxsend, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+
+ /* now push all buffers and request retransmission every time for all of them */
+ node = in_buffers;
+ for (i = 1; i <= num_buffers; i++) {
+ GstBuffer *buffer = GST_BUFFER (node->data);
+
+ /* verify that the original packets are correct */
+ res = gst_rtp_buffer_map (buffer, GST_MAP_READ, &rtp);
+ fail_unless_equals_int (res, TRUE);
+ fail_unless_equals_int (gst_rtp_buffer_get_ssrc (&rtp), ssrc);
+ fail_unless_equals_int (gst_rtp_buffer_get_payload_type (&rtp),
+ payload_type);
+ fail_unless_equals_int (gst_rtp_buffer_get_seq (&rtp), i);
+ gst_rtp_buffer_unmap (&rtp);
+
+ /* retransmit all the previous ones */
+ for (j = 1; j < i; j++) {
+ /* synchronize with the chain() function of the "sinkpad"
+ * to make sure that rtxsend has pushed the rtx buffer out
+ * before continuing */
+ GList *last_out_buffer = g_list_last (buffers);
+ g_mutex_lock (&check_mutex);
+ fail_unless_equals_int (gst_pad_push_event (sinkpad,
+ create_rtx_event (j, ssrc, payload_type)), TRUE);
+ /* wait for the rtx packet only if we expect the element
+ * to actually retransmit something */
+ if (j >= MAX (i - half_buffers, 1)) {
+ guint64 end_time = g_get_monotonic_time () + G_TIME_SPAN_SECOND;
+
+ while (last_out_buffer == g_list_last (buffers))
+ fail_unless (g_cond_wait_until (&check_cond, &check_mutex, end_time));
+ }
+ g_mutex_unlock (&check_mutex);
+ }
+
+ /* push this one */
+ gst_pad_push (srcpad, gst_buffer_ref (buffer));
+ node = g_list_next (node);
+ }
+
+ /* verify the result. buffers should be in this order (numbers are seqnums):
+ * 1, 1rtx, 2, 1rtx, 2rtx, 3, ... , 9, 5rtx, 6rtx, 7rtx, 8rtx, 9rtx, 10 */
+ {
+ GstRTPBuffer orig_rtp = GST_RTP_BUFFER_INIT;
+ gint expected_rtx_requests, expected_rtx_packets;
+ gint real_rtx_requests, real_rtx_packets;
+
+ /* verify statistics first */
+ expected_rtx_packets = half_buffers * half_buffers +
+ ((half_buffers - 1) / 2.0f) * half_buffers;
+ for (i = 1, expected_rtx_requests = 0; i < num_buffers; i++)
+ expected_rtx_requests += i;
+
+ g_object_get (rtxsend, "num-rtx-requests", &real_rtx_requests,
+ "num-rtx-packets", &real_rtx_packets, NULL);
+ fail_unless_equals_int (expected_rtx_requests, real_rtx_requests);
+ fail_unless_equals_int (expected_rtx_packets, real_rtx_packets);
+
+ /* and the number of actual buffers that we were pushed out of rtxsend */
+ fail_unless_equals_int (g_list_length (buffers),
+ num_buffers + expected_rtx_packets);
+
+ node = buffers;
+ for (i = 1; i <= num_buffers; i++) {
+ /* verify the retransmission packets */
+ for (j = MAX (i - half_buffers, 1); j < i; j++) {
+ GST_INFO ("checking %d, %d", i, j);
+
+ res = gst_rtp_buffer_map (GST_BUFFER (node->data), GST_MAP_READ, &rtp);
+ fail_unless_equals_int (res, TRUE);
+
+ fail_if (gst_rtp_buffer_get_ssrc (&rtp) == ssrc);
+ fail_unless_equals_int (gst_rtp_buffer_get_ssrc (&rtp), rtx_ssrc);
+ fail_unless_equals_int (gst_rtp_buffer_get_payload_type (&rtp),
+ rtx_payload_type);
+ fail_unless_equals_int (GST_READ_UINT16_BE (gst_rtp_buffer_get_payload (&rtp)), j); /* j == rtx seqnum */
+
+ /* open the original packet for this rtx packet and verify timestamps */
+ res = gst_rtp_buffer_map (GST_BUFFER (g_list_nth_data (in_buffers,
+ j - 1)), GST_MAP_READ, &orig_rtp);
+ fail_unless_equals_int (res, TRUE);
+ fail_unless_equals_int (gst_rtp_buffer_get_timestamp (&orig_rtp),
+ gst_rtp_buffer_get_timestamp (&rtp));
+ gst_rtp_buffer_unmap (&orig_rtp);
+
+ gst_rtp_buffer_unmap (&rtp);
+ node = g_list_next (node);
+ }
+
+ /* verify the normal rtp flow packet */
+ res = gst_rtp_buffer_map (GST_BUFFER (node->data), GST_MAP_READ, &rtp);
+ fail_unless_equals_int (res, TRUE);
+ fail_unless_equals_int (gst_rtp_buffer_get_ssrc (&rtp), ssrc);
+ fail_unless_equals_int (gst_rtp_buffer_get_payload_type (&rtp),
+ payload_type);
+ fail_unless_equals_int (gst_rtp_buffer_get_seq (&rtp), i);
+ gst_rtp_buffer_unmap (&rtp);
+ node = g_list_next (node);
+ }
+ }
+
+ g_list_free_full (in_buffers, (GDestroyNotify) gst_buffer_unref);
+ gst_check_drop_buffers ();
+
+ gst_check_teardown_src_pad (rtxsend);
+ gst_check_teardown_sink_pad (rtxsend);
+ gst_check_teardown_element (rtxsend);
+}
+
+GST_START_TEST (test_rtxsender_max_size_packets)
+{
+ test_rtxsender_packet_retention (FALSE);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_rtxsender_max_size_time)
+{
+ test_rtxsender_packet_retention (TRUE);
+}
+
+GST_END_TEST;
+
+static void
+compare_rtp_packets (GstBuffer * a, GstBuffer * b)
+{
+ GstRTPBuffer rtp_a = GST_RTP_BUFFER_INIT;
+ GstRTPBuffer rtp_b = GST_RTP_BUFFER_INIT;
+
+ gst_rtp_buffer_map (a, GST_MAP_READ, &rtp_a);
+ gst_rtp_buffer_map (b, GST_MAP_READ, &rtp_b);
+
+ fail_unless_equals_int (gst_rtp_buffer_get_header_len (&rtp_a),
+ gst_rtp_buffer_get_header_len (&rtp_b));
+ fail_unless_equals_int (gst_rtp_buffer_get_version (&rtp_a),
+ gst_rtp_buffer_get_version (&rtp_b));
+ fail_unless_equals_int (gst_rtp_buffer_get_ssrc (&rtp_a),
+ gst_rtp_buffer_get_ssrc (&rtp_b));
+ fail_unless_equals_int (gst_rtp_buffer_get_seq (&rtp_a),
+ gst_rtp_buffer_get_seq (&rtp_b));
+ fail_unless_equals_int (gst_rtp_buffer_get_csrc_count (&rtp_a),
+ gst_rtp_buffer_get_csrc_count (&rtp_b));
+ fail_unless_equals_int (gst_rtp_buffer_get_marker (&rtp_a),
+ gst_rtp_buffer_get_marker (&rtp_b));
+ fail_unless_equals_int (gst_rtp_buffer_get_payload_type (&rtp_a),
+ gst_rtp_buffer_get_payload_type (&rtp_b));
+ fail_unless_equals_int (gst_rtp_buffer_get_timestamp (&rtp_a),
+ gst_rtp_buffer_get_timestamp (&rtp_b));
+ fail_unless_equals_int (gst_rtp_buffer_get_extension (&rtp_a),
+ gst_rtp_buffer_get_extension (&rtp_b));
+
+ fail_unless_equals_int (gst_rtp_buffer_get_payload_len (&rtp_a),
+ gst_rtp_buffer_get_payload_len (&rtp_b));
+ fail_unless_equals_int (memcmp (gst_rtp_buffer_get_payload (&rtp_a),
+ gst_rtp_buffer_get_payload (&rtp_b),
+ gst_rtp_buffer_get_payload_len (&rtp_a)), 0);
+
+ gst_rtp_buffer_unmap (&rtp_a);
+ gst_rtp_buffer_unmap (&rtp_b);
+}
+
+GST_START_TEST (test_rtxreceive_data_reconstruction)
+{
+ const guint ssrc = 1234567;
+ GList *in_buffers;
+ guint payload_type;
+ GstElement *rtxsend, *rtxrecv;
+ GstPad *srcpad, *sinkpad;
+ GstCaps *caps;
+ GstBuffer *buffer;
+ GstStructure *pt_map;
+
+ /* generate test data */
+ in_buffers = generate_test_buffers (1, ssrc, &payload_type);
+
+ /* clear the global buffers list, which we are going to use later */
+ gst_check_drop_buffers ();
+
+ /* setup element & pads */
+ rtxsend = gst_check_setup_element ("rtprtxsend");
+ rtxrecv = gst_check_setup_element ("rtprtxreceive");
+
+ pt_map = gst_structure_new ("application/x-rtp-pt-map",
+ "96", G_TYPE_UINT, 99, NULL);
+ g_object_set (rtxsend, "payload-type-map", pt_map, NULL);
+ g_object_set (rtxrecv, "payload-type-map", pt_map, NULL);
+ gst_structure_free (pt_map);
+
+ fail_unless_equals_int (gst_element_link (rtxsend, rtxrecv), TRUE);
+
+ srcpad = gst_check_setup_src_pad (rtxsend, &srctemplate);
+ fail_unless_equals_int (gst_pad_set_active (srcpad, TRUE), TRUE);
+
+ sinkpad = gst_check_setup_sink_pad (rtxrecv, &sinktemplate);
+ fail_unless_equals_int (gst_pad_set_active (sinkpad, TRUE), TRUE);
+
+ ASSERT_SET_STATE (rtxsend, GST_STATE_PLAYING, GST_STATE_CHANGE_SUCCESS);
+ ASSERT_SET_STATE (rtxrecv, GST_STATE_PLAYING, GST_STATE_CHANGE_SUCCESS);
+
+ caps = gst_caps_from_string ("application/x-rtp, "
+ "media = (string)video, payload = (int)96, "
+ "ssrc = (uint)1234567, clock-rate = (int)90000, "
+ "encoding-name = (string)RAW");
+ gst_check_setup_events (srcpad, rtxsend, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+
+ /* push buffer */
+ buffer = gst_buffer_ref (GST_BUFFER (in_buffers->data));
+ fail_unless_equals_int (gst_pad_push (srcpad, buffer), GST_FLOW_OK);
+
+ /* push retransmission request */
+ {
+ GList *last_out_buffer;
+ guint64 end_time;
+ gboolean res;
+
+ /* synchronize with the chain() function of the "sinkpad"
+ * to make sure that rtxsend has pushed the rtx buffer out
+ * before continuing */
+ last_out_buffer = g_list_last (buffers);
+ g_mutex_lock (&check_mutex);
+ fail_unless_equals_int (gst_pad_push_event (sinkpad,
+ create_rtx_event (1, ssrc, payload_type)), TRUE);
+ end_time = g_get_monotonic_time () + G_TIME_SPAN_SECOND;
+ do
+ res = g_cond_wait_until (&check_cond, &check_mutex, end_time);
+ while (res == TRUE && last_out_buffer == g_list_last (buffers));
+ fail_unless_equals_int (res, TRUE);
+ g_mutex_unlock (&check_mutex);
+ }
+
+ /* verify */
+ fail_unless_equals_int (g_list_length (buffers), 2);
+ compare_rtp_packets (GST_BUFFER (buffers->data),
+ GST_BUFFER (buffers->next->data));
+
+ /* cleanup */
+ g_list_free_full (in_buffers, (GDestroyNotify) gst_buffer_unref);
+ gst_check_drop_buffers ();
+
+ gst_check_teardown_src_pad (rtxsend);
+ gst_check_teardown_sink_pad (rtxrecv);
+ gst_element_unlink (rtxsend, rtxrecv);
+ gst_check_teardown_element (rtxsend);
+ gst_check_teardown_element (rtxrecv);
+}
+
+GST_END_TEST;
+
+static Suite *
+rtprtx_suite (void)
+{
+ Suite *s = suite_create ("rtprtx");
+ TCase *tc_chain = tcase_create ("general");
+
+ tcase_set_timeout (tc_chain, 120);
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_push_forward_seq);
+ tcase_add_test (tc_chain, test_drop_one_sender);
+ tcase_add_test (tc_chain, test_drop_multiple_sender);
+ tcase_add_test (tc_chain, test_rtxsender_max_size_packets);
+ tcase_add_test (tc_chain, test_rtxsender_max_size_time);
+ tcase_add_test (tc_chain, test_rtxreceive_data_reconstruction);
+
+ return s;
+}
+
+GST_CHECK_MAIN (rtprtx);
diff --git a/tests/check/elements/rtpsession.c b/tests/check/elements/rtpsession.c
new file mode 100755
index 0000000..7c7b568
--- /dev/null
+++ b/tests/check/elements/rtpsession.c
@@ -0,0 +1,597 @@
+/* GStreamer
+ *
+ * unit test for gstrtpsession
+ *
+ * Copyright (C) <2009> Wim Taymans <wim.taymans@gmail.com>
+ * Copyright (C) 2013 Collabora Ltd.
+ *
+ * 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.
+ */
+
+#include <gst/check/gstcheck.h>
+#include <gst/check/gsttestclock.h>
+
+#include <gst/rtp/gstrtpbuffer.h>
+#include <gst/rtp/gstrtcpbuffer.h>
+
+static const guint payload_size = 160;
+static const guint clock_rate = 8000;
+static const guint payload_type = 0;
+
+typedef struct
+{
+ GstElement *session;
+ GstPad *src, *rtcp_sink, *rtpsrc;
+ GstClock *clock;
+ GAsyncQueue *rtcp_queue;
+} TestData;
+
+static GstCaps *
+generate_caps (void)
+{
+ return gst_caps_new_simple ("application/x-rtp",
+ "clock-rate", G_TYPE_INT, clock_rate,
+ "payload-type", G_TYPE_INT, payload_type, NULL);
+}
+
+static GstBuffer *
+generate_test_buffer (GstClockTime gst_ts,
+ gboolean marker_bit, guint seq_num, guint32 rtp_ts, guint ssrc)
+{
+ GstBuffer *buf;
+ guint8 *payload;
+ guint i;
+ GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
+
+ buf = gst_rtp_buffer_new_allocate (payload_size, 0, 0);
+ GST_BUFFER_DTS (buf) = gst_ts;
+ GST_BUFFER_PTS (buf) = gst_ts;
+
+ gst_rtp_buffer_map (buf, GST_MAP_READWRITE, &rtp);
+ gst_rtp_buffer_set_payload_type (&rtp, payload_type);
+ gst_rtp_buffer_set_marker (&rtp, marker_bit);
+ gst_rtp_buffer_set_seq (&rtp, seq_num);
+ gst_rtp_buffer_set_timestamp (&rtp, rtp_ts);
+ gst_rtp_buffer_set_ssrc (&rtp, ssrc);
+
+ payload = gst_rtp_buffer_get_payload (&rtp);
+ for (i = 0; i < payload_size; i++)
+ payload[i] = 0xff;
+
+ gst_rtp_buffer_unmap (&rtp);
+
+ return buf;
+}
+
+static GstFlowReturn
+test_sink_pad_chain_cb (GstPad * pad, GstObject * parent, GstBuffer * buffer)
+{
+ TestData *data = gst_pad_get_element_private (pad);
+ g_async_queue_push (data->rtcp_queue, buffer);
+ GST_DEBUG ("chained");
+ return GST_FLOW_OK;
+}
+
+static GstCaps *
+pt_map_requested (GstElement * elemen, guint pt, gpointer data)
+{
+ return generate_caps ();
+}
+
+static void
+destroy_testharness (TestData * data)
+{
+ g_assert_cmpint (gst_element_set_state (data->session, GST_STATE_NULL),
+ ==, GST_STATE_CHANGE_SUCCESS);
+ gst_object_unref (data->session);
+ data->session = NULL;
+
+ gst_object_unref (data->src);
+ data->src = NULL;
+
+ gst_object_unref (data->rtcp_sink);
+ data->rtcp_sink = NULL;
+
+ gst_object_unref (data->rtpsrc);
+ data->rtpsrc = NULL;
+
+ gst_object_unref (data->clock);
+ data->clock = NULL;
+
+ g_async_queue_unref (data->rtcp_queue);
+ data->rtcp_queue = NULL;
+}
+
+static void
+setup_testharness (TestData * data, gboolean session_as_sender)
+{
+ GstPad *rtp_sink_pad, *rtcp_src_pad, *rtp_src_pad;
+ GstSegment seg;
+ GstMiniObject *obj;
+ GstCaps *caps;
+
+ data->clock = gst_test_clock_new ();
+ GST_DEBUG ("Setting default system clock to test clock");
+ gst_system_clock_set_default (data->clock);
+ g_assert (data->clock);
+ gst_test_clock_set_time (GST_TEST_CLOCK (data->clock), 0);
+
+ data->session = gst_element_factory_make ("rtpsession", NULL);
+ g_signal_connect (data->session, "request-pt-map",
+ (GCallback) pt_map_requested, data);
+ g_assert (data->session);
+ gst_element_set_clock (data->session, data->clock);
+ g_assert_cmpint (gst_element_set_state (data->session,
+ GST_STATE_PLAYING), !=, GST_STATE_CHANGE_FAILURE);
+
+ data->rtcp_queue =
+ g_async_queue_new_full ((GDestroyNotify) gst_mini_object_unref);
+
+ /* link in the test source-pad */
+ data->src = gst_pad_new ("src", GST_PAD_SRC);
+ g_assert (data->src);
+ rtp_sink_pad = gst_element_get_request_pad (data->session,
+ session_as_sender ? "send_rtp_sink" : "recv_rtp_sink");
+ g_assert (rtp_sink_pad);
+ g_assert_cmpint (gst_pad_link (data->src, rtp_sink_pad), ==, GST_PAD_LINK_OK);
+ gst_object_unref (rtp_sink_pad);
+
+ data->rtpsrc = gst_pad_new ("sink", GST_PAD_SINK);
+ g_assert (data->rtpsrc);
+ rtp_src_pad = gst_element_get_static_pad (data->session,
+ session_as_sender ? "send_rtp_src" : "recv_rtp_src");
+ g_assert (rtp_src_pad);
+ g_assert_cmpint (gst_pad_link (rtp_src_pad, data->rtpsrc), ==,
+ GST_PAD_LINK_OK);
+ gst_object_unref (rtp_src_pad);
+
+ /* link in the test sink-pad */
+ data->rtcp_sink = gst_pad_new ("sink", GST_PAD_SINK);
+ g_assert (data->rtcp_sink);
+ gst_pad_set_element_private (data->rtcp_sink, data);
+ caps = generate_caps ();
+ gst_pad_set_caps (data->rtcp_sink, caps);
+ gst_pad_set_chain_function (data->rtcp_sink, test_sink_pad_chain_cb);
+ rtcp_src_pad = gst_element_get_request_pad (data->session, "send_rtcp_src");
+ g_assert (rtcp_src_pad);
+ g_assert_cmpint (gst_pad_link (rtcp_src_pad, data->rtcp_sink), ==,
+ GST_PAD_LINK_OK);
+ gst_object_unref (rtcp_src_pad);
+
+ g_assert (gst_pad_set_active (data->src, TRUE));
+ g_assert (gst_pad_set_active (data->rtcp_sink, TRUE));
+
+ gst_segment_init (&seg, GST_FORMAT_TIME);
+ gst_pad_push_event (data->src, gst_event_new_stream_start ("stream0"));
+ gst_pad_set_caps (data->src, caps);
+ gst_pad_push_event (data->src, gst_event_new_segment (&seg));
+ gst_caps_unref (caps);
+
+ while ((obj = g_async_queue_try_pop (data->rtcp_queue)))
+ gst_mini_object_unref (obj);
+}
+
+GST_START_TEST (test_multiple_ssrc_rr)
+{
+ TestData data;
+ GstFlowReturn res;
+ GstClockID id, tid;
+ GstBuffer *in_buf, *out_buf;
+ GstRTCPBuffer rtcp = GST_RTCP_BUFFER_INIT;
+ GstRTCPPacket rtcp_packet;
+ int i;
+ guint32 ssrc, exthighestseq, jitter, lsr, dlsr;
+ gint32 packetslost;
+ guint8 fractionlost;
+
+ setup_testharness (&data, FALSE);
+
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), 10 * GST_MSECOND);
+
+ for (i = 0; i < 5; i++) {
+ GST_DEBUG ("Push %i", i);
+ in_buf =
+ generate_test_buffer (i * 20 * GST_MSECOND, FALSE, i, i * 20,
+ 0x01BADBAD);
+ res = gst_pad_push (data.src, in_buf);
+ fail_unless (res == GST_FLOW_OK || res == GST_FLOW_FLUSHING);
+
+
+ gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
+ tid = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
+ gst_clock_id_unref (id);
+ if (tid)
+ gst_clock_id_unref (tid);
+
+ in_buf =
+ generate_test_buffer (i * 20 * GST_MSECOND, FALSE, i, i * 20,
+ 0xDEADBEEF);
+ res = gst_pad_push (data.src, in_buf);
+ fail_unless (res == GST_FLOW_OK || res == GST_FLOW_FLUSHING);
+
+ gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
+ tid = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
+ GST_DEBUG ("pushed %i", i);
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock),
+ gst_clock_id_get_time (id));
+ gst_clock_id_unref (id);
+ if (tid)
+ gst_clock_id_unref (tid);
+ }
+
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock),
+ gst_clock_id_get_time (id) + (2 * GST_SECOND));
+ gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
+ tid = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
+ gst_clock_id_unref (id);
+ gst_clock_id_unref (tid);
+
+ out_buf = g_async_queue_pop (data.rtcp_queue);
+ g_assert (out_buf != NULL);
+ g_assert (gst_rtcp_buffer_validate (out_buf));
+ gst_rtcp_buffer_map (out_buf, GST_MAP_READ, &rtcp);
+ g_assert (gst_rtcp_buffer_get_first_packet (&rtcp, &rtcp_packet));
+ g_assert (gst_rtcp_packet_get_type (&rtcp_packet) == GST_RTCP_TYPE_RR);
+ g_assert_cmpint (gst_rtcp_packet_get_rb_count (&rtcp_packet), ==, 2);
+
+ gst_rtcp_packet_get_rb (&rtcp_packet, 0, &ssrc, &fractionlost, &packetslost,
+ &exthighestseq, &jitter, &lsr, &dlsr);
+
+ g_assert_cmpint (ssrc, ==, 0x01BADBAD);
+
+ gst_rtcp_packet_get_rb (&rtcp_packet, 1, &ssrc, &fractionlost, &packetslost,
+ &exthighestseq, &jitter, &lsr, &dlsr);
+ g_assert_cmpint (ssrc, ==, 0xDEADBEEF);
+ gst_rtcp_buffer_unmap (&rtcp);
+ gst_buffer_unref (out_buf);
+
+ destroy_testharness (&data);
+}
+
+GST_END_TEST;
+
+/* This verifies that rtpsession will correctly place RBs round-robin
+ * across multiple SRs when there are too many senders that their RBs
+ * do not fit in one SR */
+GST_START_TEST (test_multiple_senders_roundrobin_rbs)
+{
+ TestData data;
+ GstFlowReturn res;
+ GstClockID id, tid;
+ GstBuffer *buf;
+ GstRTCPBuffer rtcp = GST_RTCP_BUFFER_INIT;
+ GstRTCPPacket rtcp_packet;
+ GstClockTime time;
+ gint queue_length;
+ gint i, j, k;
+ guint32 ssrc;
+ GHashTable *sr_ssrcs, *rb_ssrcs, *tmp_set;
+
+ setup_testharness (&data, TRUE);
+
+ /* only the RTCP thread waits on the clock */
+ gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
+
+ for (i = 0; i < 2; i++) { /* cycles between SR reports */
+ for (j = 0; j < 5; j++) { /* packets per ssrc */
+ gint seq = (i * 5) + j;
+ GST_DEBUG ("Push %i", seq);
+
+ gst_test_clock_advance_time (GST_TEST_CLOCK (data.clock),
+ 200 * GST_MSECOND);
+
+ for (k = 0; k < 35; k++) { /* number of ssrcs */
+ buf =
+ generate_test_buffer (seq * 200 * GST_MSECOND, FALSE, seq,
+ seq * 200, 10000 + k);
+ res = gst_pad_push (data.src, buf);
+ fail_unless (res == GST_FLOW_OK || res == GST_FLOW_FLUSHING);
+ }
+
+ GST_DEBUG ("pushed %i", seq);
+ }
+
+ queue_length = g_async_queue_length (data.rtcp_queue);
+
+ do {
+ /* crank the RTCP pad thread */
+ time = gst_clock_id_get_time (id);
+ GST_DEBUG ("Advancing time to %" GST_TIME_FORMAT, GST_TIME_ARGS (time));
+ gst_test_clock_set_time (GST_TEST_CLOCK (data.clock), time);
+ tid = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data.clock));
+ fail_unless_equals_pointer (tid, id);
+ gst_clock_id_unref (id);
+ gst_clock_id_unref (tid);
+
+ /* wait for the RTCP pad thread to output its data
+ * and start waiting on the next timeout */
+ gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock),
+ &id);
+
+ /* and retry as long as there are no new RTCP packets out,
+ * because the RTCP thread may randomly decide to reschedule
+ * the RTCP timeout for later */
+ } while (g_async_queue_length (data.rtcp_queue) == queue_length);
+
+ GST_DEBUG ("RTCP timeout processed");
+ }
+ gst_clock_id_unref (id);
+
+ sr_ssrcs = g_hash_table_new (g_direct_hash, g_direct_equal);
+ rb_ssrcs = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
+ (GDestroyNotify) g_hash_table_unref);
+
+ /* verify the rtcp packets */
+ for (i = 0; i < 2 * 35; i++) {
+ guint expected_rb_count = (i < 35) ? GST_RTCP_MAX_RB_COUNT :
+ (35 - GST_RTCP_MAX_RB_COUNT - 1);
+
+ GST_DEBUG ("pop %d", i);
+
+ buf = g_async_queue_pop (data.rtcp_queue);
+ g_assert (buf != NULL);
+ g_assert (gst_rtcp_buffer_validate (buf));
+
+ gst_rtcp_buffer_map (buf, GST_MAP_READ, &rtcp);
+ g_assert (gst_rtcp_buffer_get_first_packet (&rtcp, &rtcp_packet));
+ g_assert_cmpint (gst_rtcp_packet_get_type (&rtcp_packet), ==,
+ GST_RTCP_TYPE_SR);
+
+ gst_rtcp_packet_sr_get_sender_info (&rtcp_packet, &ssrc, NULL, NULL, NULL,
+ NULL);
+ g_assert_cmpint (ssrc, >=, 10000);
+ g_assert_cmpint (ssrc, <=, 10035);
+ g_hash_table_add (sr_ssrcs, GUINT_TO_POINTER (ssrc));
+
+ /* inspect the RBs */
+ g_assert_cmpint (gst_rtcp_packet_get_rb_count (&rtcp_packet), ==,
+ expected_rb_count);
+
+ if (i < 35) {
+ tmp_set = g_hash_table_new (g_direct_hash, g_direct_equal);
+ g_hash_table_insert (rb_ssrcs, GUINT_TO_POINTER (ssrc), tmp_set);
+ } else {
+ tmp_set = g_hash_table_lookup (rb_ssrcs, GUINT_TO_POINTER (ssrc));
+ g_assert (tmp_set);
+ }
+
+ for (j = 0; j < expected_rb_count; j++) {
+ gst_rtcp_packet_get_rb (&rtcp_packet, j, &ssrc, NULL, NULL,
+ NULL, NULL, NULL, NULL);
+ g_assert_cmpint (ssrc, >=, 10000);
+ g_assert_cmpint (ssrc, <=, 10035);
+ g_hash_table_add (tmp_set, GUINT_TO_POINTER (ssrc));
+ }
+
+ gst_rtcp_buffer_unmap (&rtcp);
+ gst_buffer_unref (buf);
+
+ /* cycle done, verify all ssrcs have issued SR reports */
+ if ((i + 1) == 35 || (i + 1) == (2 * 35)) {
+ g_assert_cmpint (g_hash_table_size (sr_ssrcs), ==, 35);
+ g_hash_table_remove_all (sr_ssrcs);
+ }
+ }
+
+ /* now verify all other ssrcs have been reported on each ssrc's SR */
+ g_assert_cmpint (g_hash_table_size (rb_ssrcs), ==, 35);
+ for (i = 10000; i < 10035; i++) {
+ tmp_set = g_hash_table_lookup (rb_ssrcs, GUINT_TO_POINTER (i));
+ g_assert (tmp_set);
+ /* SR contains RBs for each other ssrc except the ssrc of the SR */
+ g_assert_cmpint (g_hash_table_size (tmp_set), ==, 34);
+ g_assert (!g_hash_table_contains (tmp_set, GUINT_TO_POINTER (i)));
+ }
+
+ g_hash_table_unref (sr_ssrcs);
+ g_hash_table_unref (rb_ssrcs);
+
+ destroy_testharness (&data);
+}
+
+GST_END_TEST;
+
+static void
+crank_rtcp_thread (TestData * data, GstClockTime * time, GstClockID * id)
+{
+ gint queue_length;
+ GstClockID *tid;
+
+ queue_length = g_async_queue_length (data->rtcp_queue);
+ do {
+ *time = gst_clock_id_get_time (*id);
+ GST_DEBUG ("Advancing time to %" GST_TIME_FORMAT, GST_TIME_ARGS (*time));
+ if (*time > gst_clock_get_time (data->clock))
+ gst_test_clock_set_time (GST_TEST_CLOCK (data->clock), *time);
+ tid = gst_test_clock_process_next_clock_id (GST_TEST_CLOCK (data->clock));
+ fail_unless_equals_pointer (tid, *id);
+
+ gst_clock_id_unref (tid);
+ gst_clock_id_unref (*id);
+ *id = NULL;
+
+ /* wait for the RTCP pad thread to output its data
+ * and start waiting on the next timeout */
+ gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data->clock), id);
+
+ /* and retry as long as there are no new RTCP packets out,
+ * because the RTCP thread may randomly decide to reschedule
+ * the RTCP timeout for later */
+ } while (g_async_queue_length (data->rtcp_queue) == queue_length);
+}
+
+GST_START_TEST (test_internal_sources_timeout)
+{
+ TestData data;
+ GstClockID id;
+ GstClockTime time;
+ GObject *internal_session;
+ guint internal_ssrc;
+ guint32 ssrc;
+ GstBuffer *buf;
+ GstRTCPBuffer rtcp = GST_RTCP_BUFFER_INIT;
+ GstRTCPPacket rtcp_packet;
+ GstFlowReturn res;
+ gint i, j;
+
+ setup_testharness (&data, TRUE);
+ g_object_get (data.session, "internal-session", &internal_session, NULL);
+ g_object_set (internal_session, "internal-ssrc", 0xDEADBEEF, NULL);
+
+ /* only the RTCP thread waits on the clock */
+ gst_test_clock_wait_for_next_pending_id (GST_TEST_CLOCK (data.clock), &id);
+
+ /* crank the RTCP pad thread until it creates a RR for its internal-ssrc
+ * source, since we have not pushed any RTP packets and it doesn't have
+ * any other source available */
+ crank_rtcp_thread (&data, &time, &id);
+
+ g_object_get (internal_session, "internal-ssrc", &internal_ssrc, NULL);
+ g_assert_cmpint (internal_ssrc, ==, 0xDEADBEEF);
+
+ /* verify that rtpsession has sent RR for an internally-created
+ * RTPSource that is using the internal-ssrc */
+ buf = g_async_queue_pop (data.rtcp_queue);
+ g_assert (buf != NULL);
+ g_assert (gst_rtcp_buffer_validate (buf));
+ gst_rtcp_buffer_map (buf, GST_MAP_READ, &rtcp);
+ g_assert (gst_rtcp_buffer_get_first_packet (&rtcp, &rtcp_packet));
+ g_assert_cmpint (gst_rtcp_packet_get_type (&rtcp_packet), ==,
+ GST_RTCP_TYPE_RR);
+ ssrc = gst_rtcp_packet_rr_get_ssrc (&rtcp_packet);
+ g_assert_cmpint (ssrc, ==, internal_ssrc);
+ gst_rtcp_buffer_unmap (&rtcp);
+ gst_buffer_unref (buf);
+
+ /* ok, now let's push some RTP packets */
+ for (i = 1; i < 4; i++) {
+ gst_test_clock_advance_time (GST_TEST_CLOCK (data.clock),
+ 200 * GST_MSECOND);
+ buf =
+ generate_test_buffer (time + i * 200 * GST_MSECOND, FALSE, i, i * 200,
+ 0x01BADBAD);
+ res = gst_pad_push (data.src, buf);
+ fail_unless (res == GST_FLOW_OK || res == GST_FLOW_FLUSHING);
+ }
+
+ /* internal ssrc must have changed already */
+ g_object_get (internal_session, "internal-ssrc", &internal_ssrc, NULL);
+ g_assert_cmpint (ssrc, !=, internal_ssrc);
+ g_assert_cmpint (internal_ssrc, ==, 0x01BADBAD);
+
+ /* wait for SR */
+ crank_rtcp_thread (&data, &time, &id);
+
+ /* verify SR and RR */
+ j = 0;
+ for (i = 0; i < 2; i++) {
+ buf = g_async_queue_pop (data.rtcp_queue);
+ g_assert (buf != NULL);
+ g_assert (gst_rtcp_buffer_validate (buf));
+ gst_rtcp_buffer_map (buf, GST_MAP_READ, &rtcp);
+ g_assert (gst_rtcp_buffer_get_first_packet (&rtcp, &rtcp_packet));
+ if (gst_rtcp_packet_get_type (&rtcp_packet) == GST_RTCP_TYPE_SR) {
+ gst_rtcp_packet_sr_get_sender_info (&rtcp_packet, &ssrc, NULL, NULL, NULL,
+ NULL);
+ g_assert_cmpint (ssrc, ==, internal_ssrc);
+ g_assert_cmpint (ssrc, ==, 0x01BADBAD);
+ j |= 0x1;
+ } else if (gst_rtcp_packet_get_type (&rtcp_packet) == GST_RTCP_TYPE_RR) {
+ ssrc = gst_rtcp_packet_rr_get_ssrc (&rtcp_packet);
+ g_assert_cmpint (ssrc, !=, internal_ssrc);
+ g_assert_cmpint (ssrc, ==, 0xDEADBEEF);
+ j |= 0x2;
+ }
+ gst_rtcp_buffer_unmap (&rtcp);
+ gst_buffer_unref (buf);
+ }
+ g_assert_cmpint (j, ==, 0x3); /* verify we got both SR and RR */
+
+ /* go 30 seconds in the future and observe both sources timing out:
+ * 0xDEADBEEF -> BYE, 0x01BADBAD -> becomes receiver only */
+ gst_test_clock_advance_time (GST_TEST_CLOCK (data.clock), 30 * GST_SECOND);
+ crank_rtcp_thread (&data, &time, &id);
+
+ /* verify BYE and RR */
+ j = 0;
+ for (i = 0; i < 2; i++) {
+ buf = g_async_queue_pop (data.rtcp_queue);
+ g_assert (buf != NULL);
+ g_assert (gst_rtcp_buffer_validate (buf));
+ gst_rtcp_buffer_map (buf, GST_MAP_READ, &rtcp);
+
+ g_assert (gst_rtcp_buffer_get_first_packet (&rtcp, &rtcp_packet));
+ g_assert_cmpint (gst_rtcp_packet_get_type (&rtcp_packet), ==,
+ GST_RTCP_TYPE_RR);
+ ssrc = gst_rtcp_packet_rr_get_ssrc (&rtcp_packet);
+ if (ssrc == 0x01BADBAD) {
+ j |= 0x1;
+ g_assert_cmpint (ssrc, ==, internal_ssrc);
+ /* 2 => RR, SDES. There is no BYE here */
+ g_assert_cmpint (gst_rtcp_buffer_get_packet_count (&rtcp), ==, 2);
+ } else if (ssrc == 0xDEADBEEF) {
+ j |= 0x2;
+ g_assert_cmpint (ssrc, !=, internal_ssrc);
+ /* 3 => RR, SDES, BYE */
+ g_assert_cmpint (gst_rtcp_buffer_get_packet_count (&rtcp), ==, 3);
+ g_assert (gst_rtcp_packet_move_to_next (&rtcp_packet));
+ g_assert (gst_rtcp_packet_move_to_next (&rtcp_packet));
+ g_assert_cmpint (gst_rtcp_packet_get_type (&rtcp_packet), ==,
+ GST_RTCP_TYPE_BYE);
+ }
+
+ gst_rtcp_buffer_unmap (&rtcp);
+ gst_buffer_unref (buf);
+ }
+ g_assert_cmpint (j, ==, 0x3); /* verify we got both BYE and RR */
+ gst_clock_id_unref (id);
+
+ g_object_unref (internal_session);
+ destroy_testharness (&data);
+}
+
+GST_END_TEST;
+
+static Suite *
+gstrtpsession_suite (void)
+{
+ Suite *s = suite_create ("rtpsession");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_multiple_ssrc_rr);
+ tcase_add_test (tc_chain, test_multiple_senders_roundrobin_rbs);
+ tcase_add_test (tc_chain, test_internal_sources_timeout);
+
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ int nf;
+
+ Suite *s = gstrtpsession_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ srunner_run_all (sr, CK_NORMAL);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}
diff --git a/tests/check/elements/shapewipe.c b/tests/check/elements/shapewipe.c
new file mode 100755
index 0000000..ab90084
--- /dev/null
+++ b/tests/check/elements/shapewipe.c
@@ -0,0 +1,316 @@
+/* GStreamer
+ *
+ * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ *
+ * 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.
+ */
+
+#include <unistd.h>
+
+#include <gst/check/gstcheck.h>
+
+gboolean have_eos = FALSE;
+
+/* 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 */
+GstPad *myvideosrcpad, *mymasksrcpad, *mysinkpad;
+
+
+#define SHAPEWIPE_VIDEO_CAPS_STRING \
+ "video/x-raw, " \
+ "format = (string)AYUV, " \
+ "width = 400, " \
+ "height = 400, " \
+ "framerate = 0/1"
+
+#define SHAPEWIPE_MASK_CAPS_STRING \
+ "video/x-raw, " \
+ "format = (string)GRAY8, " \
+ "width = 400, " \
+ "height = 400, " \
+ "framerate = 0/1"
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (SHAPEWIPE_VIDEO_CAPS_STRING)
+ );
+static GstStaticPadTemplate videosrctemplate =
+GST_STATIC_PAD_TEMPLATE ("videosrc",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (SHAPEWIPE_VIDEO_CAPS_STRING)
+ );
+static GstStaticPadTemplate masksrctemplate =
+GST_STATIC_PAD_TEMPLATE ("masksrc",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (SHAPEWIPE_MASK_CAPS_STRING)
+ );
+
+
+static GstBuffer *output = NULL;
+
+static GstFlowReturn
+on_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
+{
+ g_return_val_if_fail (output == NULL, GST_FLOW_ERROR);
+
+ output = buffer;
+ return GST_FLOW_OK;
+}
+
+GST_START_TEST (test_general)
+{
+ GstElement *shapewipe, *videosrc, *masksrc, *sink, *bin;
+ GstPad *p;
+ GstCaps *caps;
+ GstBuffer *mask, *input;
+ guint i, j;
+ guint8 *data;
+ GstMapInfo map;
+
+ bin = gst_bin_new ("myshapewipe");
+ videosrc = gst_bin_new ("myvideosrc");
+ masksrc = gst_bin_new ("mymasksrc");
+ sink = gst_bin_new ("mysink");
+ shapewipe = gst_element_factory_make ("shapewipe", NULL);
+ fail_unless (shapewipe != NULL);
+ gst_bin_add_many (GST_BIN (bin), videosrc, masksrc, shapewipe, sink, NULL);
+
+ myvideosrcpad =
+ gst_pad_new_from_static_template (&videosrctemplate, "videosrc");
+ gst_element_add_pad (videosrc, myvideosrcpad);
+
+ mymasksrcpad = gst_pad_new_from_static_template (&masksrctemplate, "masksrc");
+ gst_element_add_pad (masksrc, mymasksrcpad);
+
+ mysinkpad = gst_pad_new_from_static_template (&sinktemplate, "sink");
+ gst_element_add_pad (sink, mysinkpad);
+ gst_pad_set_chain_function (mysinkpad, on_chain);
+
+ p = gst_element_get_static_pad (shapewipe, "video_sink");
+ fail_unless (gst_pad_link (myvideosrcpad, p) == GST_PAD_LINK_OK);
+ gst_object_unref (p);
+ p = gst_element_get_static_pad (shapewipe, "mask_sink");
+ fail_unless (gst_pad_link (mymasksrcpad, p) == GST_PAD_LINK_OK);
+ gst_object_unref (p);
+ p = gst_element_get_static_pad (shapewipe, "src");
+ fail_unless (gst_pad_link (p, mysinkpad) == GST_PAD_LINK_OK);
+ gst_object_unref (p);
+
+ fail_unless (gst_element_set_state (bin,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS);
+
+ caps = gst_caps_from_string (SHAPEWIPE_MASK_CAPS_STRING);
+ gst_check_setup_events (mymasksrcpad, masksrc, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+
+ caps = gst_caps_from_string (SHAPEWIPE_VIDEO_CAPS_STRING);
+ gst_check_setup_events (myvideosrcpad, videosrc, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+
+ mask = gst_buffer_new_and_alloc (400 * 400);
+ gst_buffer_map (mask, &map, GST_MAP_WRITE);
+ data = map.data;
+ for (i = 0; i < 400; i++) {
+ for (j = 0; j < 400; j++) {
+ if (i < 100 && j < 100)
+ data[0] = 0;
+ else if (i < 200 && j < 200)
+ data[0] = 85;
+ else if (i < 300 && j < 300)
+ data[0] = 170;
+ else
+ data[0] = 254;
+ data++;
+ }
+ }
+ gst_buffer_unmap (mask, &map);
+
+ fail_unless (gst_pad_push (mymasksrcpad, mask) == GST_FLOW_OK);
+
+ input = gst_buffer_new_and_alloc (400 * 400 * 4);
+ gst_buffer_map (input, &map, GST_MAP_WRITE);
+ data = map.data;
+ for (i = 0; i < 400; i++) {
+ for (j = 0; j < 400; j++) {
+ /* This is green */
+ data[0] = 255; /* A */
+ data[1] = 173; /* Y */
+ data[2] = 42; /* U */
+ data[3] = 26; /* V */
+ data += 4;
+ }
+ }
+ gst_buffer_unmap (input, &map);
+
+ g_object_set (G_OBJECT (shapewipe), "position", 0.0, NULL);
+ output = NULL;
+ fail_unless (gst_pad_push (myvideosrcpad,
+ gst_buffer_ref (input)) == GST_FLOW_OK);
+ fail_unless (output != NULL);
+ gst_buffer_map (output, &map, GST_MAP_WRITE);
+ data = map.data;
+ for (i = 0; i < 400; i++) {
+ for (j = 0; j < 400; j++) {
+ fail_unless_equals_int (data[0], 255); /* A */
+ fail_unless_equals_int (data[1], 173); /* Y */
+ fail_unless_equals_int (data[2], 42); /* U */
+ fail_unless_equals_int (data[3], 26); /* V */
+ data += 4;
+ }
+ }
+ gst_buffer_unmap (output, &map);
+ gst_buffer_unref (output);
+ output = NULL;
+
+ g_object_set (G_OBJECT (shapewipe), "position", 0.1, NULL);
+ output = NULL;
+ fail_unless (gst_pad_push (myvideosrcpad,
+ gst_buffer_ref (input)) == GST_FLOW_OK);
+ fail_unless (output != NULL);
+ gst_buffer_map (output, &map, GST_MAP_READ);
+ data = map.data;
+ for (i = 0; i < 400; i++) {
+ for (j = 0; j < 400; j++) {
+ if (i < 100 && j < 100) {
+ fail_unless_equals_int (data[0], 0); /* A */
+ fail_unless_equals_int (data[1], 173); /* Y */
+ fail_unless_equals_int (data[2], 42); /* U */
+ fail_unless_equals_int (data[3], 26); /* V */
+ } else {
+ fail_unless_equals_int (data[0], 255); /* A */
+ fail_unless_equals_int (data[1], 173); /* Y */
+ fail_unless_equals_int (data[2], 42); /* U */
+ fail_unless_equals_int (data[3], 26); /* V */
+ }
+ data += 4;
+ }
+ }
+ gst_buffer_unmap (output, &map);
+ gst_buffer_unref (output);
+ output = NULL;
+
+ g_object_set (G_OBJECT (shapewipe), "position", 0.34, NULL);
+ output = NULL;
+ fail_unless (gst_pad_push (myvideosrcpad,
+ gst_buffer_ref (input)) == GST_FLOW_OK);
+ fail_unless (output != NULL);
+ gst_buffer_map (output, &map, GST_MAP_READ);
+ data = map.data;
+ for (i = 0; i < 400; i++) {
+ for (j = 0; j < 400; j++) {
+ if (i < 200 && j < 200) {
+ fail_unless_equals_int (data[0], 0); /* A */
+ fail_unless_equals_int (data[1], 173); /* Y */
+ fail_unless_equals_int (data[2], 42); /* U */
+ fail_unless_equals_int (data[3], 26); /* V */
+ } else {
+ fail_unless_equals_int (data[0], 255); /* A */
+ fail_unless_equals_int (data[1], 173); /* Y */
+ fail_unless_equals_int (data[2], 42); /* U */
+ fail_unless_equals_int (data[3], 26); /* V */
+ }
+ data += 4;
+ }
+ }
+ gst_buffer_unmap (output, &map);
+ gst_buffer_unref (output);
+ output = NULL;
+
+ g_object_set (G_OBJECT (shapewipe), "position", 0.67, NULL);
+ output = NULL;
+ fail_unless (gst_pad_push (myvideosrcpad,
+ gst_buffer_ref (input)) == GST_FLOW_OK);
+ fail_unless (output != NULL);
+ gst_buffer_map (output, &map, GST_MAP_READ);
+ data = map.data;
+ for (i = 0; i < 400; i++) {
+ for (j = 0; j < 400; j++) {
+ if (i < 300 && j < 300) {
+ fail_unless_equals_int (data[0], 0); /* A */
+ fail_unless_equals_int (data[1], 173); /* Y */
+ fail_unless_equals_int (data[2], 42); /* U */
+ fail_unless_equals_int (data[3], 26); /* V */
+ } else {
+ fail_unless_equals_int (data[0], 255); /* A */
+ fail_unless_equals_int (data[1], 173); /* Y */
+ fail_unless_equals_int (data[2], 42); /* U */
+ fail_unless_equals_int (data[3], 26); /* V */
+ }
+ data += 4;
+ }
+ }
+ gst_buffer_unmap (output, &map);
+ gst_buffer_unref (output);
+ output = NULL;
+
+ g_object_set (G_OBJECT (shapewipe), "position", 1.0, NULL);
+ output = NULL;
+ fail_unless (gst_pad_push (myvideosrcpad,
+ gst_buffer_ref (input)) == GST_FLOW_OK);
+ fail_unless (output != NULL);
+ gst_buffer_map (output, &map, GST_MAP_READ);
+ data = map.data;
+ for (i = 0; i < 400; i++) {
+ for (j = 0; j < 400; j++) {
+ fail_unless_equals_int (data[0], 0); /* A */
+ fail_unless_equals_int (data[1], 173); /* Y */
+ fail_unless_equals_int (data[2], 42); /* U */
+ fail_unless_equals_int (data[3], 26); /* V */
+ data += 4;
+ }
+ }
+ gst_buffer_unmap (output, &map);
+ gst_buffer_unref (output);
+ output = NULL;
+
+ gst_buffer_unref (input);
+
+ fail_unless (gst_element_set_state (bin,
+ GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS);
+
+ p = gst_element_get_static_pad (shapewipe, "video_sink");
+ fail_unless (gst_pad_unlink (myvideosrcpad, p));
+ gst_object_unref (p);
+ p = gst_element_get_static_pad (shapewipe, "mask_sink");
+ fail_unless (gst_pad_unlink (mymasksrcpad, p));
+ gst_object_unref (p);
+ p = gst_element_get_static_pad (shapewipe, "src");
+ fail_unless (gst_pad_unlink (p, mysinkpad));
+ gst_object_unref (p);
+
+ gst_object_unref (bin);
+}
+
+GST_END_TEST;
+
+static Suite *
+shapewipe_suite (void)
+{
+ Suite *s = suite_create ("shapewipe");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_set_timeout (tc_chain, 180);
+ tcase_add_test (tc_chain, test_general);
+
+ return s;
+}
+
+GST_CHECK_MAIN (shapewipe);
diff --git a/tests/check/elements/souphttpsrc.c b/tests/check/elements/souphttpsrc.c
new file mode 100755
index 0000000..dfb682b
--- /dev/null
+++ b/tests/check/elements/souphttpsrc.c
@@ -0,0 +1,700 @@
+/* GStreamer unit tests for the souphttpsrc element
+ * Copyright (C) 2006-2007 Tim-Philipp Müller <tim centricular net>
+ * Copyright (C) 2008 Wouter Cloetens <wouter@mind.be>
+ * Copyright (C) 2001-2003, Ximian, Inc.
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+
+#include <glib.h>
+#include <glib/gprintf.h>
+
+#define SOUP_VERSION_MIN_REQUIRED (SOUP_VERSION_2_40)
+#include <libsoup/soup.h>
+#include <gst/check/gstcheck.h>
+
+#if !defined(SOUP_MINOR_VERSION) || SOUP_MINOR_VERSION < 44
+#define SoupStatus SoupKnownStatusCode
+#endif
+
+static guint http_port = 0, https_port = 0;
+
+gboolean redirect = TRUE;
+
+static const char **cookies = NULL;
+
+/* Variables for authentication tests */
+static const char *user_id = NULL;
+static const char *user_pw = NULL;
+static const char *good_user = "good_user";
+static const char *bad_user = "bad_user";
+static const char *good_pw = "good_pw";
+static const char *bad_pw = "bad_pw";
+static const char *realm = "SOUPHTTPSRC_REALM";
+static const char *basic_auth_path = "/basic_auth";
+static const char *digest_auth_path = "/digest_auth";
+
+static gboolean run_server (guint * http_port, guint * https_port);
+static void stop_server (void);
+
+static void
+handoff_cb (GstElement * fakesink, GstBuffer * buf, GstPad * pad,
+ GstBuffer ** p_outbuf)
+{
+ GST_LOG ("handoff, buf = %p", buf);
+ if (*p_outbuf == NULL)
+ *p_outbuf = gst_buffer_ref (buf);
+}
+
+static gboolean
+basic_auth_cb (SoupAuthDomain * domain, SoupMessage * msg,
+ const char *username, const char *password, gpointer user_data)
+{
+ /* There is only one good login for testing */
+ return (strcmp (username, good_user) == 0)
+ && (strcmp (password, good_pw) == 0);
+}
+
+
+static char *
+digest_auth_cb (SoupAuthDomain * domain, SoupMessage * msg,
+ const char *username, gpointer user_data)
+{
+ /* There is only one good login for testing */
+ if (strcmp (username, good_user) == 0)
+ return soup_auth_domain_digest_encode_password (good_user, realm, good_pw);
+ return NULL;
+}
+
+static int
+run_test (const char *format, ...)
+{
+ GstStateChangeReturn ret;
+
+ GstElement *pipe, *src, *sink;
+
+ GstBuffer *buf = NULL;
+
+ GstMessage *msg;
+
+ gchar *url;
+
+ va_list args;
+
+ int rc = -1;
+
+ pipe = gst_pipeline_new (NULL);
+
+ src = gst_element_factory_make ("souphttpsrc", NULL);
+ fail_unless (src != NULL);
+
+ sink = gst_element_factory_make ("fakesink", NULL);
+ fail_unless (sink != NULL);
+
+ gst_bin_add (GST_BIN (pipe), src);
+ gst_bin_add (GST_BIN (pipe), sink);
+ fail_unless (gst_element_link (src, sink));
+
+ if (http_port == 0) {
+ GST_DEBUG ("failed to start soup http server");
+ }
+ fail_unless (http_port != 0);
+ va_start (args, format);
+ g_vasprintf (&url, format, args);
+ va_end (args);
+ fail_unless (url != NULL);
+ g_object_set (src, "location", url, NULL);
+ g_free (url);
+
+ g_object_set (src, "automatic-redirect", redirect, NULL);
+ if (cookies != NULL)
+ g_object_set (src, "cookies", cookies, NULL);
+ g_object_set (sink, "signal-handoffs", TRUE, NULL);
+ g_signal_connect (sink, "preroll-handoff", G_CALLBACK (handoff_cb), &buf);
+
+ if (user_id != NULL)
+ g_object_set (src, "user-id", user_id, NULL);
+ if (user_pw != NULL)
+ g_object_set (src, "user-pw", user_pw, NULL);
+
+ ret = gst_element_set_state (pipe, GST_STATE_PAUSED);
+ if (ret != GST_STATE_CHANGE_ASYNC) {
+ GST_DEBUG ("failed to start up soup http src, ret = %d", ret);
+ goto done;
+ }
+
+ gst_element_set_state (pipe, GST_STATE_PLAYING);
+ msg = gst_bus_poll (GST_ELEMENT_BUS (pipe),
+ GST_MESSAGE_EOS | GST_MESSAGE_ERROR, -1);
+ if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
+ gchar *debug = NULL;
+
+ GError *err = NULL;
+
+ gst_message_parse_error (msg, &err, &debug);
+ GST_INFO ("error: %s", err->message);
+ if (g_str_has_suffix (err->message, "Not Found"))
+ rc = 404;
+ else if (g_str_has_suffix (err->message, "Forbidden"))
+ rc = 403;
+ else if (g_str_has_suffix (err->message, "Unauthorized"))
+ rc = 401;
+ else if (g_str_has_suffix (err->message, "Found"))
+ rc = 302;
+ GST_INFO ("debug: %s", debug);
+ /* should not've gotten any output in case of a 40x error. Wait a bit
+ * to give streaming thread a chance to push out a buffer and triggering
+ * our callback before shutting down the pipeline */
+ g_usleep (G_USEC_PER_SEC / 2);
+ fail_unless (buf == NULL);
+ g_error_free (err);
+ g_free (debug);
+ gst_message_unref (msg);
+ goto done;
+ }
+ gst_message_unref (msg);
+
+ /* don't wait for more than 10 seconds */
+ ret = gst_element_get_state (pipe, NULL, NULL, 10 * GST_SECOND);
+ GST_LOG ("ret = %u", ret);
+
+ if (buf == NULL) {
+ /* we want to test the buffer offset, nothing else; if there's a failure
+ * it might be for lots of reasons (no network connection, whatever), we're
+ * not interested in those */
+ GST_DEBUG ("didn't manage to get data within 10 seconds, skipping test");
+ goto done;
+ }
+
+ GST_DEBUG ("buffer offset = %" G_GUINT64_FORMAT, GST_BUFFER_OFFSET (buf));
+
+ /* first buffer should have a 0 offset */
+ fail_unless (GST_BUFFER_OFFSET (buf) == 0);
+ gst_buffer_unref (buf);
+ rc = 0;
+
+done:
+
+ gst_element_set_state (pipe, GST_STATE_NULL);
+ gst_object_unref (pipe);
+ return rc;
+}
+
+GST_START_TEST (test_first_buffer_has_offset)
+{
+ fail_unless (run_test ("http://127.0.0.1:%u/", http_port) == 0);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_not_found)
+{
+ fail_unless (run_test ("http://127.0.0.1:%u/404", http_port) == 404);
+ fail_unless (run_test ("http://127.0.0.1:%u/404-with-data",
+ http_port) == 404);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_forbidden)
+{
+ fail_unless (run_test ("http://127.0.0.1:%u/403", http_port) == 403);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_redirect_no)
+{
+ redirect = FALSE;
+ fail_unless (run_test ("http://127.0.0.1:%u/302", http_port) == 302);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_redirect_yes)
+{
+ redirect = TRUE;
+ fail_unless (run_test ("http://127.0.0.1:%u/302", http_port) == 0);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_https)
+{
+ if (!https_port)
+ GST_INFO ("Failed to start an HTTPS server; let's just skip this test.");
+ else
+ fail_unless (run_test ("https://127.0.0.1:%u/", https_port) == 0);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_cookies)
+{
+ static const char *biscotti[] = { "delacre=yummie", "koekje=lu", NULL };
+ int rc;
+
+ cookies = biscotti;
+ rc = run_test ("http://127.0.0.1:%u/", http_port);
+ cookies = NULL;
+ fail_unless (rc == 0);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_good_user_basic_auth)
+{
+ int res;
+
+ user_id = good_user;
+ user_pw = good_pw;
+ res = run_test ("http://127.0.0.1:%u%s", http_port, basic_auth_path);
+ GST_DEBUG ("Basic Auth user %s password %s res = %d", user_id, user_pw, res);
+ user_id = user_pw = NULL;
+ fail_unless (res == 0);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_bad_user_basic_auth)
+{
+ int res;
+
+ user_id = bad_user;
+ user_pw = good_pw;
+ res = run_test ("http://127.0.0.1:%u%s", http_port, basic_auth_path);
+ GST_DEBUG ("Basic Auth user %s password %s res = %d", user_id, user_pw, res);
+ user_id = user_pw = NULL;
+ fail_unless (res == 401);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_bad_password_basic_auth)
+{
+ int res;
+
+ user_id = good_user;
+ user_pw = bad_pw;
+ res = run_test ("http://127.0.0.1:%u%s", http_port, basic_auth_path);
+ GST_DEBUG ("Basic Auth user %s password %s res = %d", user_id, user_pw, res);
+ user_id = user_pw = NULL;
+ fail_unless (res == 401);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_good_user_digest_auth)
+{
+ int res;
+
+ user_id = good_user;
+ user_pw = good_pw;
+ res = run_test ("http://127.0.0.1:%u%s", http_port, digest_auth_path);
+ GST_DEBUG ("Digest Auth user %s password %s res = %d", user_id, user_pw, res);
+ user_id = user_pw = NULL;
+ fail_unless (res == 0);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_bad_user_digest_auth)
+{
+ int res;
+
+ user_id = bad_user;
+ user_pw = good_pw;
+ res = run_test ("http://127.0.0.1:%u%s", http_port, digest_auth_path);
+ GST_DEBUG ("Digest Auth user %s password %s res = %d", user_id, user_pw, res);
+ user_id = user_pw = NULL;
+ fail_unless (res == 401);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_bad_password_digest_auth)
+{
+ int res;
+
+ user_id = good_user;
+ user_pw = bad_pw;
+ res = run_test ("http://127.0.0.1:%u%s", http_port, digest_auth_path);
+ GST_DEBUG ("Digest Auth user %s password %s res = %d", user_id, user_pw, res);
+ user_id = user_pw = NULL;
+ fail_unless (res == 401);
+}
+
+GST_END_TEST;
+
+static gboolean icy_caps = FALSE;
+
+static void
+got_buffer (GstElement * fakesink, GstBuffer * buf, GstPad * pad,
+ gpointer user_data)
+{
+ GstStructure *s;
+ GstCaps *caps;
+
+ /* Caps can be anything if we don't except icy caps */
+ if (!icy_caps)
+ return;
+
+ /* Otherwise they _must_ be "application/x-icy" */
+ caps = gst_pad_get_current_caps (pad);
+ fail_unless (caps != NULL);
+ s = gst_caps_get_structure (caps, 0);
+ fail_unless_equals_string (gst_structure_get_name (s), "application/x-icy");
+ gst_caps_unref (caps);
+}
+
+GST_START_TEST (test_icy_stream)
+{
+ GstElement *pipe, *src, *sink;
+
+ GstMessage *msg;
+
+ pipe = gst_pipeline_new (NULL);
+
+ src = gst_element_factory_make ("souphttpsrc", NULL);
+ fail_unless (src != NULL);
+
+ sink = gst_element_factory_make ("fakesink", NULL);
+ fail_unless (sink != NULL);
+ g_object_set (sink, "signal-handoffs", TRUE, NULL);
+ g_signal_connect (sink, "handoff", G_CALLBACK (got_buffer), NULL);
+
+ gst_bin_add (GST_BIN (pipe), src);
+ gst_bin_add (GST_BIN (pipe), sink);
+ fail_unless (gst_element_link (src, sink));
+
+ /* First try Virgin Radio Ogg stream, to see if there's connectivity and all
+ * (which is an attempt to work around the completely horrid error reporting
+ * and that we can't distinguish different types of failures here). */
+
+ g_object_set (src, "location", "http://ogg2.smgradio.com/vr32.ogg", NULL);
+ g_object_set (src, "num-buffers", 1, NULL);
+ icy_caps = FALSE;
+ gst_element_set_state (pipe, GST_STATE_PLAYING);
+
+ msg = gst_bus_poll (GST_ELEMENT_BUS (pipe),
+ GST_MESSAGE_EOS | GST_MESSAGE_ERROR, -1);
+ if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
+ GST_INFO ("looks like there's no net connectivity or sgmradio.com is "
+ "down. In any case, let's just skip this test");
+ gst_message_unref (msg);
+ goto done;
+ }
+ gst_message_unref (msg);
+ msg = NULL;
+ gst_element_set_state (pipe, GST_STATE_NULL);
+
+ /* Now, if the ogg stream works, the mp3 shoutcast stream should work as
+ * well (time will tell if that's true) */
+
+ /* Virgin Radio 32kbps mp3 shoutcast stream */
+ g_object_set (src, "location", "http://mp3-vr-32.smgradio.com:80/", NULL);
+
+
+ /* EOS after the first buffer */
+ g_object_set (src, "num-buffers", 1, NULL);
+ icy_caps = TRUE;
+ gst_element_set_state (pipe, GST_STATE_PLAYING);
+ msg = gst_bus_poll (GST_ELEMENT_BUS (pipe),
+ GST_MESSAGE_EOS | GST_MESSAGE_ERROR, -1);
+
+ if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS) {
+ GST_DEBUG ("success, we're done here");
+ gst_message_unref (msg);
+ goto done;
+ }
+
+ {
+ GError *err = NULL;
+
+ gst_message_parse_error (msg, &err, NULL);
+ gst_message_unref (msg);
+ g_error ("Error with ICY mp3 shoutcast stream: %s", err->message);
+ g_error_free (err);
+ }
+
+done:
+ icy_caps = FALSE;
+
+ gst_element_set_state (pipe, GST_STATE_NULL);
+ gst_object_unref (pipe);
+}
+
+GST_END_TEST;
+
+static SoupServer *server; /* NULL */
+static SoupServer *ssl_server; /* NULL */
+
+static Suite *
+souphttpsrc_suite (void)
+{
+ TCase *tc_chain, *tc_internet;
+ Suite *s;
+
+ /* we don't support exceptions from the proxy, so just unset the environment
+ * variable - in case it's set in the test environment it would otherwise
+ * prevent us from connecting to localhost (like jenkins.qa.ubuntu.com) */
+ g_unsetenv ("http_proxy");
+
+ s = suite_create ("souphttpsrc");
+ tc_chain = tcase_create ("general");
+ tc_internet = tcase_create ("internet");
+
+ suite_add_tcase (s, tc_chain);
+ if (run_server (&http_port, &https_port)) {
+ atexit (stop_server);
+ tcase_add_test (tc_chain, test_first_buffer_has_offset);
+ tcase_add_test (tc_chain, test_redirect_yes);
+ tcase_add_test (tc_chain, test_redirect_no);
+ tcase_add_test (tc_chain, test_not_found);
+ tcase_add_test (tc_chain, test_forbidden);
+ tcase_add_test (tc_chain, test_cookies);
+ tcase_add_test (tc_chain, test_good_user_basic_auth);
+ tcase_add_test (tc_chain, test_bad_user_basic_auth);
+ tcase_add_test (tc_chain, test_bad_password_basic_auth);
+ tcase_add_test (tc_chain, test_good_user_digest_auth);
+ tcase_add_test (tc_chain, test_bad_user_digest_auth);
+ tcase_add_test (tc_chain, test_bad_password_digest_auth);
+
+ if (ssl_server != NULL)
+ tcase_add_test (tc_chain, test_https);
+ } else {
+ g_print ("Skipping 12 souphttpsrc tests, couldn't start or connect to "
+ "local http server\n");
+ }
+
+ suite_add_tcase (s, tc_internet);
+ tcase_set_timeout (tc_internet, 250);
+ tcase_add_test (tc_internet, test_icy_stream);
+
+ return s;
+}
+
+GST_CHECK_MAIN (souphttpsrc);
+
+static void
+do_get (SoupMessage * msg, const char *path)
+{
+ gboolean send_error_doc = FALSE;
+ char *uri;
+
+ int buflen = 4096;
+
+ SoupStatus status = SOUP_STATUS_OK;
+
+ uri = soup_uri_to_string (soup_message_get_uri (msg), FALSE);
+ GST_DEBUG ("request: \"%s\"", uri);
+
+ if (!strcmp (path, "/301"))
+ status = SOUP_STATUS_MOVED_PERMANENTLY;
+ else if (!strcmp (path, "/302"))
+ status = SOUP_STATUS_MOVED_TEMPORARILY;
+ else if (!strcmp (path, "/307"))
+ status = SOUP_STATUS_TEMPORARY_REDIRECT;
+ else if (!strcmp (path, "/403"))
+ status = SOUP_STATUS_FORBIDDEN;
+ else if (!strcmp (path, "/404"))
+ status = SOUP_STATUS_NOT_FOUND;
+ else if (!strcmp (path, "/404-with-data")) {
+ status = SOUP_STATUS_NOT_FOUND;
+ send_error_doc = TRUE;
+ }
+
+ if (SOUP_STATUS_IS_REDIRECTION (status)) {
+ char *redir_uri;
+
+ redir_uri = g_strdup_printf ("%s-redirected", uri);
+ soup_message_headers_append (msg->response_headers, "Location", redir_uri);
+ g_free (redir_uri);
+ }
+ if (status != (SoupStatus) SOUP_STATUS_OK && !send_error_doc)
+ goto leave;
+
+ if (msg->method == SOUP_METHOD_GET) {
+ char *buf;
+
+ buf = g_malloc (buflen);
+ memset (buf, 0, buflen);
+ soup_message_body_append (msg->response_body, SOUP_MEMORY_TAKE,
+ buf, buflen);
+ } else { /* msg->method == SOUP_METHOD_HEAD */
+
+ char *length;
+
+ /* We could just use the same code for both GET and
+ * HEAD. But we'll optimize and avoid the extra
+ * malloc.
+ */
+ length = g_strdup_printf ("%lu", (gulong) buflen);
+ soup_message_headers_append (msg->response_headers,
+ "Content-Length", length);
+ g_free (length);
+ }
+
+leave:
+ soup_message_set_status (msg, status);
+ g_free (uri);
+}
+
+static void
+print_header (const char *name, const char *value, gpointer data)
+{
+ GST_DEBUG ("header: %s: %s", name, value);
+}
+
+static void
+server_callback (SoupServer * server, SoupMessage * msg,
+ const char *path, GHashTable * query,
+ SoupClientContext * context, gpointer data)
+{
+ GST_DEBUG ("%s %s HTTP/1.%d", msg->method, path,
+ soup_message_get_http_version (msg));
+ soup_message_headers_foreach (msg->request_headers, print_header, NULL);
+ if (msg->request_body->length)
+ GST_DEBUG ("%s", msg->request_body->data);
+
+ if (msg->method == SOUP_METHOD_GET || msg->method == SOUP_METHOD_HEAD)
+ do_get (msg, path);
+ else
+ soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+
+ GST_DEBUG (" -> %d %s", msg->status_code, msg->reason_phrase);
+}
+
+static gboolean
+run_server (guint * http_port, guint * https_port)
+{
+ guint port = SOUP_ADDRESS_ANY_PORT;
+ guint ssl_port = SOUP_ADDRESS_ANY_PORT;
+ const char *ssl_cert_file = GST_TEST_FILES_PATH "/test-cert.pem";
+ const char *ssl_key_file = GST_TEST_FILES_PATH "/test-key.pem";
+ static int server_running = 0;
+
+ SoupAuthDomain *domain = NULL;
+
+ if (server_running)
+ return TRUE;
+
+ server_running = 1;
+
+ *http_port = *https_port = 0;
+
+ /* The G_ENABLE_DIAGNOSTIC is temporarily overriden to avoid
+ * property deprecation warnings (for the SOUP_SERVER_PORT
+ * property) */
+ g_setenv ("G_ENABLE_DIAGNOSTIC", "0", TRUE);
+ server = soup_server_new (SOUP_SERVER_PORT, port, NULL);
+ g_setenv ("G_ENABLE_DIAGNOSTIC", "1", TRUE);
+ if (!server) {
+ GST_DEBUG ("Unable to bind to server port %u", port);
+ return FALSE;
+ }
+ *http_port = soup_server_get_port (server);
+ GST_INFO ("HTTP server listening on port %u", *http_port);
+ soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
+ domain = soup_auth_domain_basic_new (SOUP_AUTH_DOMAIN_REALM, realm,
+ SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK, basic_auth_cb,
+ SOUP_AUTH_DOMAIN_ADD_PATH, basic_auth_path, NULL);
+ soup_server_add_auth_domain (server, domain);
+ g_object_unref (domain);
+ domain = soup_auth_domain_digest_new (SOUP_AUTH_DOMAIN_REALM, realm,
+ SOUP_AUTH_DOMAIN_DIGEST_AUTH_CALLBACK, digest_auth_cb,
+ SOUP_AUTH_DOMAIN_ADD_PATH, digest_auth_path, NULL);
+ soup_server_add_auth_domain (server, domain);
+ g_object_unref (domain);
+ soup_server_run_async (server);
+
+ if (ssl_cert_file && ssl_key_file) {
+ GTlsBackend *backend = g_tls_backend_get_default ();
+
+ if (backend != NULL && g_tls_backend_supports_tls (backend)) {
+ ssl_server = soup_server_new (SOUP_SERVER_PORT, ssl_port,
+ SOUP_SERVER_SSL_CERT_FILE, ssl_cert_file,
+ SOUP_SERVER_SSL_KEY_FILE, ssl_key_file, NULL);
+ } else {
+ GST_INFO ("No TLS support");
+ }
+
+ if (ssl_server) {
+ *https_port = soup_server_get_port (ssl_server);
+ GST_INFO ("HTTPS server listening on port %u", *https_port);
+ soup_server_add_handler (ssl_server, NULL, server_callback, NULL, NULL);
+ soup_server_run_async (ssl_server);
+ }
+ }
+
+ /* check if we can connect to our local http server */
+ {
+ GSocketConnection *conn;
+ GSocketClient *client;
+
+ client = g_socket_client_new ();
+ g_socket_client_set_timeout (client, 2);
+ conn = g_socket_client_connect_to_host (client, "127.0.0.1", *http_port,
+ NULL, NULL);
+ if (conn == NULL) {
+ GST_INFO ("Couldn't connect to http server 127.0.0.1:%u", *http_port);
+ g_object_unref (client);
+ stop_server ();
+ return FALSE;
+ }
+ g_object_unref (conn);
+
+ if (ssl_server == NULL)
+ goto skip_https_check;
+
+ conn = g_socket_client_connect_to_host (client, "127.0.0.1", *https_port,
+ NULL, NULL);
+ if (conn == NULL) {
+ GST_INFO ("Couldn't connect to https server 127.0.0.1:%u", *https_port);
+ g_object_unref (client);
+ stop_server ();
+ return FALSE;
+ }
+ g_object_unref (conn);
+
+ skip_https_check:
+
+ g_object_unref (client);
+ }
+
+ return TRUE;
+}
+
+static void
+stop_server (void)
+{
+ GST_INFO ("cleaning up");
+
+ if (server) {
+ g_object_unref (server);
+ server = NULL;
+ }
+ if (ssl_server) {
+ g_object_unref (ssl_server);
+ ssl_server = NULL;
+ }
+}
diff --git a/tests/check/elements/spectrum.c b/tests/check/elements/spectrum.c
new file mode 100644
index 0000000..e562e72
--- /dev/null
+++ b/tests/check/elements/spectrum.c
@@ -0,0 +1,556 @@
+/* GStreamer
+ *
+ * unit test for spectrum
+ *
+ * Copyright (C) <2007> Stefan Kost <ensonic@users.sf.net>
+ *
+ * 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.
+ */
+
+#include <unistd.h>
+
+#include <gst/audio/audio.h>
+#include <gst/check/gstcheck.h>
+
+gboolean have_eos = FALSE;
+
+/* 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 */
+GstPad *mysrcpad, *mysinkpad;
+
+#define SPECT_CAPS_TEMPLATE_STRING \
+ "audio/x-raw, " \
+ " rate = (int) [ 1, MAX ], " \
+ " channels = (int) [ 1, MAX ], " \
+ " layout = (string) interleaved, " \
+ " format = (string) { " \
+ GST_AUDIO_NE(S16) ", " \
+ GST_AUDIO_NE(S32) ", " \
+ GST_AUDIO_NE(F32) ", " \
+ GST_AUDIO_NE(F64) " }"
+
+#define SPECT_CAPS_STRING_S16 \
+ "audio/x-raw, " \
+ "rate = (int) 44100, " \
+ "channels = (int) 1, " \
+ "layout = (string) interleaved, " \
+ "format = (string) " GST_AUDIO_NE(S16)
+
+#define SPECT_CAPS_STRING_S32 \
+ "audio/x-raw, " \
+ "rate = (int) 44100, " \
+ "channels = (int) 1, " \
+ "layout = (string) interleaved, " \
+ "format = (string) " GST_AUDIO_NE(S32)
+
+#define SPECT_CAPS_STRING_F32 \
+ "audio/x-raw, " \
+ " rate = (int) 44100, " \
+ " channels = (int) 1, " \
+ " layout = (string) interleaved, " \
+ " format = (string) " GST_AUDIO_NE(F32)
+
+#define SPECT_CAPS_STRING_F64 \
+ "audio/x-raw, " \
+ " rate = (int) 44100, " \
+ " channels = (int) 1, " \
+ " layout = (string) interleaved, " \
+ " format = (string) " GST_AUDIO_NE(F64)
+
+#define SPECT_BANDS 256
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (SPECT_CAPS_TEMPLATE_STRING)
+ );
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (SPECT_CAPS_TEMPLATE_STRING)
+ );
+
+/* takes over reference for outcaps */
+static GstElement *
+setup_spectrum (const gchar * caps_str)
+{
+ GstElement *spectrum;
+ GstCaps *caps;
+
+ GST_DEBUG ("setup_spectrum");
+ spectrum = gst_check_setup_element ("spectrum");
+ mysrcpad = gst_check_setup_src_pad (spectrum, &srctemplate);
+ mysinkpad = gst_check_setup_sink_pad (spectrum, &sinktemplate);
+ gst_pad_set_active (mysrcpad, TRUE);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ caps = gst_caps_from_string (caps_str);
+ gst_check_setup_events (mysrcpad, spectrum, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+
+ return spectrum;
+}
+
+static void
+cleanup_spectrum (GstElement * spectrum)
+{
+ GST_DEBUG ("cleanup_spectrum");
+
+ gst_pad_set_active (mysrcpad, FALSE);
+ gst_pad_set_active (mysinkpad, FALSE);
+ gst_check_teardown_src_pad (spectrum);
+ gst_check_teardown_sink_pad (spectrum);
+ gst_check_teardown_element (spectrum);
+
+ g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
+ g_list_free (buffers);
+ buffers = NULL;
+}
+
+
+GST_START_TEST (test_int16)
+{
+ GstElement *spectrum;
+ GstBuffer *inbuffer, *outbuffer;
+ GstBus *bus;
+ GstMessage *message;
+ const GstStructure *structure;
+ int i, j;
+ gint16 *data;
+ GstMapInfo map;
+ const GValue *list, *value;
+ GstClockTime endtime;
+ gfloat level;
+
+ spectrum = setup_spectrum (SPECT_CAPS_STRING_S16);
+ g_object_set (spectrum, "post-messages", TRUE, "interval", GST_SECOND / 100,
+ "bands", SPECT_BANDS, "threshold", -80, NULL);
+
+ fail_unless (gst_element_set_state (spectrum,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ /* create a 1 sec buffer with an 11025 Hz sine wave */
+ inbuffer = gst_buffer_new_allocate (NULL, 44100 * sizeof (gint16), 0);
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ data = (gint16 *) map.data;
+ for (j = 0; j < 44100; j += 4) {
+ *data = 0;
+ ++data;
+ *data = 32767;
+ ++data;
+ *data = 0;
+ ++data;
+ *data = -32767;
+ ++data;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* create a bus to get the spectrum message on */
+ bus = gst_bus_new ();
+ ASSERT_OBJECT_REFCOUNT (bus, "bus", 1);
+ gst_element_set_bus (spectrum, bus);
+ ASSERT_OBJECT_REFCOUNT (bus, "bus", 2);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... but it ends up being collected on the global buffer list */
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+ fail_unless (inbuffer == outbuffer);
+
+ message = gst_bus_poll (bus, GST_MESSAGE_ELEMENT, -1);
+ ASSERT_OBJECT_REFCOUNT (message, "message", 1);
+
+ fail_unless (message != NULL);
+ fail_unless (GST_MESSAGE_SRC (message) == GST_OBJECT (spectrum));
+ fail_unless (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ELEMENT);
+ structure = gst_message_get_structure (message);
+ fail_if (structure == NULL);
+ fail_unless_equals_string ((char *) gst_structure_get_name (structure),
+ "spectrum");
+ fail_unless (gst_structure_get_clock_time (structure, "endtime", &endtime));
+
+ list = gst_structure_get_value (structure, "magnitude");
+ for (i = 0; i < SPECT_BANDS; ++i) {
+ value = gst_value_list_get_value (list, i);
+ level = g_value_get_float (value);
+ GST_DEBUG ("band[%3d] is %.2f", i, level);
+ /* Only the bands in the middle should have a level above 60 */
+ fail_if ((i == SPECT_BANDS / 2 || i == SPECT_BANDS / 2 - 1)
+ && level < -20.0);
+ fail_if ((i != SPECT_BANDS / 2 && i != SPECT_BANDS / 2 - 1)
+ && level > -20.0);
+ }
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+ fail_unless (inbuffer == outbuffer);
+
+ /* clean up */
+ /* flush current messages,and future state change messages */
+ gst_bus_set_flushing (bus, TRUE);
+
+ /* message has a ref to the element */
+ ASSERT_OBJECT_REFCOUNT (spectrum, "spectrum", 2);
+ gst_message_unref (message);
+ ASSERT_OBJECT_REFCOUNT (spectrum, "spectrum", 1);
+
+ gst_element_set_bus (spectrum, NULL);
+ ASSERT_OBJECT_REFCOUNT (bus, "bus", 1);
+ gst_object_unref (bus);
+ fail_unless (gst_element_set_state (spectrum,
+ GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS, "could not set to null");
+ ASSERT_OBJECT_REFCOUNT (spectrum, "spectrum", 1);
+ cleanup_spectrum (spectrum);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_int32)
+{
+ GstElement *spectrum;
+ GstBuffer *inbuffer, *outbuffer;
+ GstBus *bus;
+ GstMessage *message;
+ const GstStructure *structure;
+ int i, j;
+ gint32 *data;
+ GstMapInfo map;
+ const GValue *list, *value;
+ GstClockTime endtime;
+ gfloat level;
+
+ spectrum = setup_spectrum (SPECT_CAPS_STRING_S32);
+ g_object_set (spectrum, "post-messages", TRUE, "interval", GST_SECOND / 100,
+ "bands", SPECT_BANDS, "threshold", -80, NULL);
+
+ fail_unless (gst_element_set_state (spectrum,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ /* create a 1 sec buffer with an 11025 Hz sine wave */
+ inbuffer = gst_buffer_new_allocate (NULL, 44100 * sizeof (gint32), 0);
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ data = (gint32 *) map.data;
+ for (j = 0; j < 44100; j += 4) {
+ *data = 0;
+ ++data;
+ *data = 2147483647;
+ ++data;
+ *data = 0;
+ ++data;
+ *data = -2147483647;
+ ++data;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* create a bus to get the spectrum message on */
+ bus = gst_bus_new ();
+ ASSERT_OBJECT_REFCOUNT (bus, "bus", 1);
+ gst_element_set_bus (spectrum, bus);
+ ASSERT_OBJECT_REFCOUNT (bus, "bus", 2);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... but it ends up being collected on the global buffer list */
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+ fail_unless (inbuffer == outbuffer);
+
+ message = gst_bus_poll (bus, GST_MESSAGE_ELEMENT, -1);
+ ASSERT_OBJECT_REFCOUNT (message, "message", 1);
+
+ fail_unless (message != NULL);
+ fail_unless (GST_MESSAGE_SRC (message) == GST_OBJECT (spectrum));
+ fail_unless (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ELEMENT);
+ structure = gst_message_get_structure (message);
+ fail_if (structure == NULL);
+ fail_unless_equals_string ((char *) gst_structure_get_name (structure),
+ "spectrum");
+ fail_unless (gst_structure_get_clock_time (structure, "endtime", &endtime));
+
+ list = gst_structure_get_value (structure, "magnitude");
+ for (i = 0; i < SPECT_BANDS; ++i) {
+ value = gst_value_list_get_value (list, i);
+ level = g_value_get_float (value);
+ GST_DEBUG ("band[%3d] is %.2f", i, level);
+ /* Only the bands in the middle should have a level above 60 */
+ fail_if ((i == SPECT_BANDS / 2 || i == SPECT_BANDS / 2 - 1)
+ && level < -20.0);
+ fail_if ((i != SPECT_BANDS / 2 && i != SPECT_BANDS / 2 - 1)
+ && level > -20.0);
+ }
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+ fail_unless (inbuffer == outbuffer);
+
+ /* clean up */
+ /* flush current messages,and future state change messages */
+ gst_bus_set_flushing (bus, TRUE);
+
+ /* message has a ref to the element */
+ ASSERT_OBJECT_REFCOUNT (spectrum, "spectrum", 2);
+ gst_message_unref (message);
+ ASSERT_OBJECT_REFCOUNT (spectrum, "spectrum", 1);
+
+ gst_element_set_bus (spectrum, NULL);
+ ASSERT_OBJECT_REFCOUNT (bus, "bus", 1);
+ gst_object_unref (bus);
+ fail_unless (gst_element_set_state (spectrum,
+ GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS, "could not set to null");
+ ASSERT_OBJECT_REFCOUNT (spectrum, "spectrum", 1);
+ cleanup_spectrum (spectrum);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_float32)
+{
+ GstElement *spectrum;
+ GstBuffer *inbuffer, *outbuffer;
+ GstBus *bus;
+ GstMessage *message;
+ const GstStructure *structure;
+ int i, j;
+ gfloat *data;
+ GstMapInfo map;
+ const GValue *list, *value;
+ GstClockTime endtime;
+ gfloat level;
+
+ spectrum = setup_spectrum (SPECT_CAPS_STRING_F32);
+ g_object_set (spectrum, "post-messages", TRUE, "interval", GST_SECOND / 100,
+ "bands", SPECT_BANDS, "threshold", -80, NULL);
+
+ fail_unless (gst_element_set_state (spectrum,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ /* create a 1 sec buffer with an 11025 Hz sine wave */
+ inbuffer = gst_buffer_new_allocate (NULL, 44100 * sizeof (gfloat), 0);
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ data = (gfloat *) map.data;
+ for (j = 0; j < 44100; j += 4) {
+ *data = 0.0;
+ ++data;
+ *data = 1.0;
+ ++data;
+ *data = 0.0;
+ ++data;
+ *data = -1.0;
+ ++data;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* create a bus to get the spectrum message on */
+ bus = gst_bus_new ();
+ ASSERT_OBJECT_REFCOUNT (bus, "bus", 1);
+ gst_element_set_bus (spectrum, bus);
+ ASSERT_OBJECT_REFCOUNT (bus, "bus", 2);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... but it ends up being collected on the global buffer list */
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+ fail_unless (inbuffer == outbuffer);
+
+ message = gst_bus_poll (bus, GST_MESSAGE_ELEMENT, -1);
+ ASSERT_OBJECT_REFCOUNT (message, "message", 1);
+
+ fail_unless (message != NULL);
+ fail_unless (GST_MESSAGE_SRC (message) == GST_OBJECT (spectrum));
+ fail_unless (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ELEMENT);
+ structure = gst_message_get_structure (message);
+ fail_if (structure == NULL);
+ fail_unless_equals_string ((char *) gst_structure_get_name (structure),
+ "spectrum");
+ fail_unless (gst_structure_get_clock_time (structure, "endtime", &endtime));
+
+ list = gst_structure_get_value (structure, "magnitude");
+ for (i = 0; i < SPECT_BANDS; ++i) {
+ value = gst_value_list_get_value (list, i);
+ level = g_value_get_float (value);
+ GST_DEBUG ("band[%3d] is %.2f", i, level);
+ /* Only the bands in the middle should have a level above 60 */
+ fail_if ((i == SPECT_BANDS / 2 || i == SPECT_BANDS / 2 - 1)
+ && level < -20.0);
+ fail_if ((i != SPECT_BANDS / 2 && i != SPECT_BANDS / 2 - 1)
+ && level > -20.0);
+ }
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+ fail_unless (inbuffer == outbuffer);
+
+ /* clean up */
+ /* flush current messages,and future state change messages */
+ gst_bus_set_flushing (bus, TRUE);
+
+ /* message has a ref to the element */
+ ASSERT_OBJECT_REFCOUNT (spectrum, "spectrum", 2);
+ gst_message_unref (message);
+ ASSERT_OBJECT_REFCOUNT (spectrum, "spectrum", 1);
+
+ gst_element_set_bus (spectrum, NULL);
+ ASSERT_OBJECT_REFCOUNT (bus, "bus", 1);
+ gst_object_unref (bus);
+ fail_unless (gst_element_set_state (spectrum,
+ GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS, "could not set to null");
+ ASSERT_OBJECT_REFCOUNT (spectrum, "spectrum", 1);
+ cleanup_spectrum (spectrum);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_float64)
+{
+ GstElement *spectrum;
+ GstBuffer *inbuffer, *outbuffer;
+ GstBus *bus;
+ GstMessage *message;
+ const GstStructure *structure;
+ int i, j;
+ gdouble *data;
+ GstMapInfo map;
+ const GValue *list, *value;
+ GstClockTime endtime;
+ gfloat level;
+
+ spectrum = setup_spectrum (SPECT_CAPS_STRING_F64);
+ g_object_set (spectrum, "post-messages", TRUE, "interval", GST_SECOND / 100,
+ "bands", SPECT_BANDS, "threshold", -80, NULL);
+
+ fail_unless (gst_element_set_state (spectrum,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ /* create a 1 sec buffer with an 11025 Hz sine wave */
+ inbuffer = gst_buffer_new_allocate (NULL, 44100 * sizeof (gdouble), 0);
+ gst_buffer_map (inbuffer, &map, GST_MAP_WRITE);
+ data = (gdouble *) map.data;
+ for (j = 0; j < 44100; j += 4) {
+ *data = 0.0;
+ ++data;
+ *data = 1.0;
+ ++data;
+ *data = 0.0;
+ ++data;
+ *data = -1.0;
+ ++data;
+ }
+ gst_buffer_unmap (inbuffer, &map);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ /* create a bus to get the spectrum message on */
+ bus = gst_bus_new ();
+ ASSERT_OBJECT_REFCOUNT (bus, "bus", 1);
+ gst_element_set_bus (spectrum, bus);
+ ASSERT_OBJECT_REFCOUNT (bus, "bus", 2);
+
+ /* pushing gives away my reference ... */
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ /* ... but it ends up being collected on the global buffer list */
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+ fail_unless (inbuffer == outbuffer);
+
+ message = gst_bus_poll (bus, GST_MESSAGE_ELEMENT, -1);
+ ASSERT_OBJECT_REFCOUNT (message, "message", 1);
+
+ fail_unless (message != NULL);
+ fail_unless (GST_MESSAGE_SRC (message) == GST_OBJECT (spectrum));
+ fail_unless (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ELEMENT);
+ structure = gst_message_get_structure (message);
+ fail_if (structure == NULL);
+ fail_unless_equals_string ((char *) gst_structure_get_name (structure),
+ "spectrum");
+ fail_unless (gst_structure_get_clock_time (structure, "endtime", &endtime));
+
+ list = gst_structure_get_value (structure, "magnitude");
+ for (i = 0; i < SPECT_BANDS; ++i) {
+ value = gst_value_list_get_value (list, i);
+ level = g_value_get_float (value);
+ GST_DEBUG ("band[%3d] is %.2f", i, level);
+ /* Only the bands in the middle should have a level above 60 */
+ fail_if ((i == SPECT_BANDS / 2 || i == SPECT_BANDS / 2 - 1)
+ && level < -20.0);
+ fail_if ((i != SPECT_BANDS / 2 && i != SPECT_BANDS / 2 - 1)
+ && level > -20.0);
+ }
+ fail_unless_equals_int (g_list_length (buffers), 1);
+ fail_if ((outbuffer = (GstBuffer *) buffers->data) == NULL);
+ fail_unless (inbuffer == outbuffer);
+
+ /* clean up */
+ /* flush current messages,and future state change messages */
+ gst_bus_set_flushing (bus, TRUE);
+
+ /* message has a ref to the element */
+ ASSERT_OBJECT_REFCOUNT (spectrum, "spectrum", 2);
+ gst_message_unref (message);
+ ASSERT_OBJECT_REFCOUNT (spectrum, "spectrum", 1);
+
+ gst_element_set_bus (spectrum, NULL);
+ ASSERT_OBJECT_REFCOUNT (bus, "bus", 1);
+ gst_object_unref (bus);
+ fail_unless (gst_element_set_state (spectrum,
+ GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS, "could not set to null");
+ ASSERT_OBJECT_REFCOUNT (spectrum, "spectrum", 1);
+ cleanup_spectrum (spectrum);
+}
+
+GST_END_TEST;
+
+
+static Suite *
+spectrum_suite (void)
+{
+ Suite *s = suite_create ("spectrum");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_int16);
+ tcase_add_test (tc_chain, test_int32);
+ tcase_add_test (tc_chain, test_float32);
+ tcase_add_test (tc_chain, test_float64);
+
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ int nf;
+
+ Suite *s = spectrum_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ srunner_run_all (sr, CK_NORMAL);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}
diff --git a/tests/check/elements/sunaudio.c b/tests/check/elements/sunaudio.c
new file mode 100644
index 0000000..834e7cb
--- /dev/null
+++ b/tests/check/elements/sunaudio.c
@@ -0,0 +1,95 @@
+/* GStreamer unit tests for the sun audio elements
+ *
+ * Copyright (C) 2007 Tim-Philipp Müller <tim centricular net>
+ *
+ * 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.
+ */
+
+#include <gst/check/gstcheck.h>
+#include <gst/interfaces/propertyprobe.h>
+#include <gst/interfaces/mixer.h>
+#include <gst/gst.h>
+
+GST_START_TEST (test_sun_audio_mixer_track)
+{
+ GstStateChangeReturn state_ret;
+ GstElement *mixer;
+ GList *tracks, *l;
+
+ mixer = gst_element_factory_make ("sunaudiomixer", "sunaudiomixer");
+ fail_unless (mixer != NULL, "Failed to create 'sunaudiomixer' element!");
+
+ state_ret = gst_element_set_state (mixer, GST_STATE_READY);
+ if (state_ret != GST_STATE_CHANGE_SUCCESS) {
+ gst_object_unref (mixer);
+ return;
+ }
+
+ GST_LOG ("opened sunaudiomixer");
+ fail_unless (GST_IS_MIXER (mixer), "is not a GstMixer?!");
+
+ tracks = (GList *) gst_mixer_list_tracks (GST_MIXER (mixer));
+ for (l = tracks; l != NULL; l = l->next) {
+ GObjectClass *klass;
+ GstMixerTrack *track;
+ gchar *ulabel = NULL, *label = NULL;
+
+ track = GST_MIXER_TRACK (l->data);
+
+ g_object_get (track, "label", &label, NULL);
+ fail_unless (label == NULL || g_utf8_validate (label, -1, NULL));
+
+ /* FIXME: remove this check once we depend on -base >= 0.10.12.1 */
+ klass = G_OBJECT_GET_CLASS (track);
+ if (g_object_class_find_property (klass, "untranslated-label")) {
+ g_object_get (track, "untranslated-label", &ulabel, NULL);
+ }
+
+ if (ulabel != NULL) {
+ gchar *p;
+
+ for (p = ulabel; p != NULL && *p != '\0'; ++p) {
+ fail_unless (g_ascii_isprint (*p),
+ "untranslated label '%s' not printable ASCII", ulabel);
+ }
+ }
+ GST_DEBUG ("%s: %s", GST_STR_NULL (ulabel), GST_STR_NULL (label));
+ g_free (label);
+ g_free (ulabel);
+ }
+
+ fail_unless_equals_int (gst_element_set_state (mixer, GST_STATE_NULL),
+ GST_STATE_CHANGE_SUCCESS);
+
+ gst_object_unref (mixer);
+}
+
+GST_END_TEST;
+
+
+static Suite *
+sunaudio_suite (void)
+{
+ Suite *s = suite_create ("sunaudio");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_sun_audio_mixer_track);
+
+ return s;
+}
+
+GST_CHECK_MAIN (sunaudio)
diff --git a/tests/check/elements/udpsink.c b/tests/check/elements/udpsink.c
new file mode 100755
index 0000000..0e938ee
--- /dev/null
+++ b/tests/check/elements/udpsink.c
@@ -0,0 +1,222 @@
+/* GStreamer RTP payloader unit tests
+ * Copyright (C) 2009 Axis Communications <dev-gstreamer@axis.com>
+ * @author Ognyan Tonchev <ognyan@axis.com>
+ *
+ * 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.
+ */
+#include <gst/check/gstcheck.h>
+#include <gst/base/gstbasesink.h>
+#include <stdlib.h>
+
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+#define RTP_HEADER_SIZE 12
+#define RTP_PAYLOAD_SIZE 1024
+
+/*
+ * Number of bytes received in the render function when using buffer lists
+ */
+static guint render_list_bytes_received;
+
+/*
+ * Render function for testing udpsink with buffer lists
+ */
+static GstFlowReturn
+udpsink_render_list (GstBaseSink * sink, GstBufferList * list)
+{
+ guint i, num;
+
+ num = gst_buffer_list_length (list);
+ for (i = 0; i < num; ++i) {
+ GstBuffer *buf = gst_buffer_list_get (list, i);
+ gsize size = gst_buffer_get_size (buf);
+
+ GST_DEBUG ("rendered %" G_GSIZE_FORMAT " bytes", size);
+ render_list_bytes_received += size;
+ }
+
+ return GST_FLOW_OK;
+}
+
+static void
+set_render_list_function (GstElement * bsink)
+{
+ GstBaseSinkClass *bsclass;
+
+ bsclass = GST_BASE_SINK_GET_CLASS ((GstBaseSink *) bsink);
+
+ /* Add callback function for the buffer list tests */
+ bsclass->render_list = udpsink_render_list;
+}
+
+static GstBufferList *
+create_buffer_list (guint * data_size)
+{
+ GstBufferList *list;
+ GstBuffer *rtp_buffer;
+ GstBuffer *data_buffer;
+
+ list = gst_buffer_list_new ();
+
+ /*** First group, i.e. first packet. **/
+
+ /* Create the RTP header buffer */
+ rtp_buffer = gst_buffer_new_allocate (NULL, RTP_HEADER_SIZE, NULL);
+ gst_buffer_memset (rtp_buffer, 0, 0, RTP_HEADER_SIZE);
+
+ /* Create the buffer that holds the payload */
+ data_buffer = gst_buffer_new_allocate (NULL, RTP_PAYLOAD_SIZE, NULL);
+ gst_buffer_memset (data_buffer, 0, 0, RTP_PAYLOAD_SIZE);
+
+ /* Create a new group to hold the rtp header and the payload */
+ gst_buffer_list_add (list, gst_buffer_append (rtp_buffer, data_buffer));
+
+ /*** Second group, i.e. second packet. ***/
+
+ /* Create the RTP header buffer */
+ rtp_buffer = gst_buffer_new_allocate (NULL, RTP_HEADER_SIZE, NULL);
+ gst_buffer_memset (rtp_buffer, 0, 0, RTP_HEADER_SIZE);
+
+ /* Create the buffer that holds the payload */
+ data_buffer = gst_buffer_new_allocate (NULL, RTP_PAYLOAD_SIZE, NULL);
+ gst_buffer_memset (data_buffer, 0, 0, RTP_PAYLOAD_SIZE);
+
+ /* Create a new group to hold the rtp header and the payload */
+ gst_buffer_list_add (list, gst_buffer_append (rtp_buffer, data_buffer));
+
+ /* Calculate the size of the data */
+ *data_size = 2 * RTP_HEADER_SIZE + 2 * RTP_PAYLOAD_SIZE;
+
+ return list;
+}
+
+static void
+udpsink_test (gboolean use_buffer_lists)
+{
+ GstSegment segment;
+ GstElement *udpsink;
+ GstPad *srcpad;
+ GstBufferList *list;
+ guint data_size;
+
+ list = create_buffer_list (&data_size);
+
+ udpsink = gst_check_setup_element ("udpsink");
+ if (use_buffer_lists)
+ set_render_list_function (udpsink);
+
+ srcpad = gst_check_setup_src_pad_by_name (udpsink, &srctemplate, "sink");
+
+ gst_element_set_state (udpsink, GST_STATE_PLAYING);
+ gst_pad_set_active (srcpad, TRUE);
+
+ gst_pad_push_event (srcpad, gst_event_new_stream_start ("hey there!"));
+
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ gst_pad_push_event (srcpad, gst_event_new_segment (&segment));
+
+ fail_unless_equals_int (gst_pad_push_list (srcpad, list), GST_FLOW_OK);
+
+ gst_check_teardown_pad_by_name (udpsink, "sink");
+ gst_check_teardown_element (udpsink);
+
+ if (use_buffer_lists)
+ fail_unless_equals_int (data_size, render_list_bytes_received);
+}
+
+GST_START_TEST (test_udpsink)
+{
+ udpsink_test (FALSE);
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_udpsink_bufferlist)
+{
+ udpsink_test (TRUE);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_udpsink_client_add_remove)
+{
+ GstElement *udpsink;
+
+ /* Note: keep in mind that these are in addition to the client added by
+ * the host/port properties (by default 'localhost:5004' */
+
+ udpsink = gst_check_setup_element ("udpsink");
+ g_signal_emit_by_name (udpsink, "remove", "localhost", 5004, NULL);
+ gst_object_unref (udpsink);
+
+ udpsink = gst_check_setup_element ("udpsink");
+ g_signal_emit_by_name (udpsink, "add", "127.0.0.1", 5554, NULL);
+ gst_object_unref (udpsink);
+
+ udpsink = gst_check_setup_element ("udpsink");
+ g_signal_emit_by_name (udpsink, "add", "127.0.0.1", 5554, NULL);
+ g_signal_emit_by_name (udpsink, "add", "127.0.0.1", 5554, NULL);
+ gst_object_unref (udpsink);
+
+ udpsink = gst_check_setup_element ("udpsink");
+ g_signal_emit_by_name (udpsink, "add", "127.0.0.1", 5554, NULL);
+ g_signal_emit_by_name (udpsink, "remove", "127.0.0.1", 5554, NULL);
+ gst_object_unref (udpsink);
+
+ udpsink = gst_check_setup_element ("udpsink");
+ g_signal_emit_by_name (udpsink, "add", "127.0.0.1", 5554, NULL);
+ g_signal_emit_by_name (udpsink, "remove", "127.0.0.1", 5555, NULL);
+ gst_object_unref (udpsink);
+
+ udpsink = gst_check_setup_element ("udpsink");
+ g_signal_emit_by_name (udpsink, "add", "127.0.0.1", 5554, NULL);
+ g_signal_emit_by_name (udpsink, "add", "127.0.0.1", 5555, NULL);
+ gst_object_unref (udpsink);
+
+ udpsink = gst_check_setup_element ("udpsink");
+ g_signal_emit_by_name (udpsink, "add", "127.0.0.1", 5554, NULL);
+ g_signal_emit_by_name (udpsink, "add", "10.2.0.1", 5554, NULL);
+ gst_object_unref (udpsink);
+
+ udpsink = gst_check_setup_element ("udpsink");
+ g_signal_emit_by_name (udpsink, "add", "127.0.0.1", 5554, NULL);
+ g_signal_emit_by_name (udpsink, "add", "10.2.0.1", 5554, NULL);
+ g_signal_emit_by_name (udpsink, "remove", "127.0.0.1", 5554, NULL);
+ gst_object_unref (udpsink);
+}
+
+GST_END_TEST;
+
+static Suite *
+udpsink_suite (void)
+{
+ Suite *s = suite_create ("udpsink_test");
+ TCase *tc_chain = tcase_create ("linear");
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_udpsink);
+ tcase_add_test (tc_chain, test_udpsink_bufferlist);
+ tcase_add_test (tc_chain, test_udpsink_client_add_remove);
+
+ return s;
+}
+
+GST_CHECK_MAIN (udpsink)
diff --git a/tests/check/elements/udpsrc.c b/tests/check/elements/udpsrc.c
new file mode 100644
index 0000000..1a0ab3c
--- /dev/null
+++ b/tests/check/elements/udpsrc.c
@@ -0,0 +1,123 @@
+/* GStreamer UDP source unit tests
+ * Copyright (C) 2011 Tim-Philipp Müller <tim centricular net>
+ *
+ * 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.
+ */
+#include <gst/check/gstcheck.h>
+#include <gio/gio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+GST_START_TEST (test_udpsrc_empty_packet)
+{
+ GstElement *udpsrc;
+ GSocket *socket;
+ GstPad *sinkpad;
+ int port = 0;
+
+ udpsrc = gst_check_setup_element ("udpsrc");
+ fail_unless (udpsrc != NULL);
+ g_object_set (udpsrc, "port", 0, NULL);
+
+ sinkpad = gst_check_setup_sink_pad_by_name (udpsrc, &sinktemplate, "src");
+ fail_unless (sinkpad != NULL);
+ gst_pad_set_active (sinkpad, TRUE);
+
+ gst_element_set_state (udpsrc, GST_STATE_PLAYING);
+ g_object_get (udpsrc, "port", &port, NULL);
+ GST_INFO ("udpsrc port = %d", port);
+
+ socket = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_DATAGRAM,
+ G_SOCKET_PROTOCOL_UDP, NULL);
+
+ if (socket != NULL) {
+ GSocketAddress *sa;
+ GInetAddress *ia;
+ gchar *s;
+
+ ia = g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV4);
+ s = g_inet_address_to_string (ia);
+ GST_LOG ("inet address %s", s);
+ g_free (s);
+ sa = g_inet_socket_address_new (ia, port);
+
+ if (g_socket_send_to (socket, sa, "HeLL0", 0, NULL, NULL) == 0) {
+ GST_INFO ("sent 0 bytes");
+ if (g_socket_send_to (socket, sa, "HeLL0", 6, NULL, NULL) == 6) {
+ GstMapInfo map;
+ GstBuffer *buf;
+ guint len;
+
+ GST_INFO ("sent 6 bytes");
+
+ g_usleep (G_USEC_PER_SEC / 2);
+
+ len = g_list_length (buffers);
+ GST_INFO ("%u buffers", len);
+ fail_unless (len == 1 || len == 2);
+
+ /* last buffer should be our HeLL0 string */
+ buf = GST_BUFFER (g_list_nth_data (buffers, len - 1));
+ gst_buffer_map (buf, &map, GST_MAP_READ);
+ fail_unless_equals_int (map.size, 6);
+ fail_unless_equals_string ((gchar *) map.data, "HeLL0");
+ gst_buffer_unmap (buf, &map);
+
+ /* if there's another buffer, it should be 0 bytes */
+ if (len == 2) {
+ buf = GST_BUFFER (g_list_nth_data (buffers, 0));
+ fail_unless_equals_int (gst_buffer_get_size (buf), 0);
+ }
+ } else {
+ GST_WARNING ("send_to(6 bytes) failed");
+ }
+ } else {
+ GST_WARNING ("send_to(0 bytes) failed");
+ }
+
+ g_object_unref (sa);
+ g_object_unref (ia);
+ } else {
+ GST_WARNING ("Could not create IPv4 UDP socket for unit test");
+ }
+
+ gst_element_set_state (udpsrc, GST_STATE_NULL);
+
+ gst_check_teardown_pad_by_name (udpsrc, "src");
+ gst_check_teardown_element (udpsrc);
+
+ g_object_unref (socket);
+}
+
+GST_END_TEST;
+
+static Suite *
+udpsrc_suite (void)
+{
+ Suite *s = suite_create ("udpsrc");
+ TCase *tc_chain = tcase_create ("udpsrc");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_udpsrc_empty_packet);
+ return s;
+}
+
+GST_CHECK_MAIN (udpsrc)
diff --git a/tests/check/elements/videobox.c b/tests/check/elements/videobox.c
new file mode 100755
index 0000000..8953449
--- /dev/null
+++ b/tests/check/elements/videobox.c
@@ -0,0 +1,238 @@
+/* GStreamer
+ * unit test for the videobox element
+ *
+ * Copyright (C) 2006 Ravi Kiran K N <ravi.kiran@samsung.com>
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <unistd.h>
+
+#include <gst/check/gstcheck.h>
+
+typedef struct _GstVideoBoxTestContext
+{
+ GstElement *pipeline;
+ GstElement *src;
+ GstElement *filter;
+ GstElement *box;
+ GstElement *filter2;
+ GstElement *sink;
+} GstVideoBoxTestContext;
+
+typedef struct _FormatConversion
+{
+ const gchar *in_caps;
+ const gchar *out_caps;
+ gboolean expected_result;
+} FormatConversion;
+
+
+/*
+ * Update this table as and when the conversion is supported(or unsupported) in videobox
+ */
+static const FormatConversion conversion_table[] = {
+ {"video/x-raw,format={RGBA}", "video/x-raw,format={AYUV}", TRUE},
+ {"video/x-raw,format={AYUV}", "video/x-raw,format={RGBA}", TRUE},
+ {"video/x-raw,format={I420}", "video/x-raw,format={AYUV}", TRUE},
+ {"video/x-raw,format={AYUV}", "video/x-raw,format={I420}", TRUE},
+ {"video/x-raw,format={I420}", "video/x-raw,format={YV12}", TRUE},
+ {"video/x-raw,format={YV12}", "video/x-raw,format={AYUV}", TRUE},
+ {"video/x-raw,format={YV12}", "video/x-raw,format={I420}", TRUE},
+ {"video/x-raw,format={AYUV}", "video/x-raw,format={YV12}", TRUE},
+ {"video/x-raw,format={AYUV}", "video/x-raw,format={xRGB}", TRUE},
+ {"video/x-raw,format={xRGB}", "video/x-raw,format={xRGB}", TRUE},
+ {"video/x-raw,format={xRGB}", "video/x-raw,format={AYUV}", TRUE},
+ {"video/x-raw,format={GRAY8}", "video/x-raw,format={GRAY16_LE}", FALSE},
+ {"video/x-raw,format={GRAY8}", "video/x-raw,format={GRAY16_BE}", FALSE},
+ {"video/x-raw,format={Y444}", "video/x-raw,format={Y42B}", FALSE},
+ {"video/x-raw,format={Y444}", "video/x-raw,format={Y41B}", FALSE},
+ {"video/x-raw,format={Y42B}", "video/x-raw,format={Y41B}", FALSE}
+};
+
+
+static gboolean
+bus_handler (GstBus * bus, GstMessage * message, gpointer data)
+{
+ GMainLoop *loop = (GMainLoop *) data;
+
+ switch (message->type) {
+ case GST_MESSAGE_EOS:{
+ GST_LOG ("EOS event received");
+ g_main_loop_quit (loop);
+ break;
+ }
+ case GST_MESSAGE_ERROR:{
+ GError *gerror;
+ gchar *debug;
+ gst_message_parse_error (message, &gerror, &debug);
+ g_error ("Error from %s: %s (%s)\n",
+ GST_ELEMENT_NAME (GST_MESSAGE_SRC (message)), gerror->message,
+ GST_STR_NULL (debug));
+ g_error_free (gerror);
+ g_free (debug);
+ g_main_loop_quit (loop);
+ break;
+ }
+ case GST_MESSAGE_WARNING:{
+ g_main_loop_quit (loop);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+static void
+videobox_test_init_context (GstVideoBoxTestContext * ctx)
+{
+ fail_unless (ctx != NULL);
+
+ ctx->pipeline = gst_pipeline_new ("pipeline");
+ fail_unless (ctx->pipeline != NULL);
+ ctx->src = gst_element_factory_make ("videotestsrc", "src");
+ fail_unless (ctx->src != NULL, "Failed to create videotestsrc element");
+ ctx->filter = gst_element_factory_make ("capsfilter", "filter");
+ fail_unless (ctx->filter != NULL, "Failed to create capsfilter element");
+ ctx->box = gst_element_factory_make ("videobox", "box");
+ fail_unless (ctx->box != NULL, "Failed to create videobox element");
+ ctx->filter2 = gst_element_factory_make ("capsfilter", "filter2");
+ fail_unless (ctx->filter2 != NULL,
+ "Failed to create second capsfilter element");
+ ctx->sink = gst_element_factory_make ("fakesink", "sink");
+ fail_unless (ctx->sink != NULL, "Failed to create fakesink element");
+
+ gst_bin_add_many (GST_BIN (ctx->pipeline), ctx->src, ctx->filter,
+ ctx->box, ctx->filter2, ctx->sink, NULL);
+ fail_unless (gst_element_link_many (ctx->src, ctx->filter, ctx->box,
+ ctx->filter2, ctx->sink, NULL) == TRUE, "Can not link elements");
+
+ fail_unless (gst_element_set_state (ctx->pipeline,
+ GST_STATE_READY) != GST_STATE_CHANGE_FAILURE,
+ "couldn't set pipeline to READY state");
+
+ GST_LOG ("videobox context inited");
+}
+
+static void
+videobox_test_deinit_context (GstVideoBoxTestContext * ctx)
+{
+ GST_LOG ("deiniting videobox context");
+
+ gst_element_set_state (ctx->pipeline, GST_STATE_NULL);
+ gst_object_unref (ctx->pipeline);
+ memset (ctx, 0x00, sizeof (GstVideoBoxTestContext));
+}
+
+GST_START_TEST (test_caps_transform)
+{
+ GstStateChangeReturn state_ret;
+ GstVideoBoxTestContext ctx;
+ guint conversions_test_size;
+ guint itr;
+ gboolean link_res;
+ GMainLoop *loop;
+ GstBus *bus;
+
+ videobox_test_init_context (&ctx);
+ gst_util_set_object_arg (G_OBJECT (ctx.src), "num-buffers", "1");
+
+ loop = g_main_loop_new (NULL, TRUE);
+ fail_unless (loop != NULL);
+
+ bus = gst_element_get_bus (ctx.pipeline);
+ fail_unless (bus != NULL);
+
+ gst_bus_add_watch (bus, bus_handler, loop);
+ gst_object_unref (bus);
+
+ conversions_test_size = G_N_ELEMENTS (conversion_table);
+ for (itr = 0; itr < conversions_test_size; itr++) {
+ gst_element_unlink_many (ctx.src, ctx.filter, ctx.box, ctx.filter2,
+ ctx.sink, NULL);
+ gst_util_set_object_arg (G_OBJECT (ctx.filter), "caps",
+ conversion_table[itr].in_caps);
+ gst_util_set_object_arg (G_OBJECT (ctx.filter2), "caps",
+ conversion_table[itr].out_caps);
+
+ /* Link with new input and output format from conversion table */
+ link_res =
+ gst_element_link_many (ctx.src, ctx.filter, ctx.box, ctx.filter2,
+ ctx.sink, NULL);
+
+ /* Check if the specified format conversion is supported or not by videobox */
+ fail_unless (link_res == conversion_table[itr].expected_result,
+ "videobox can not convert from '%s'' to '%s'",
+ conversion_table[itr].in_caps, conversion_table[itr].out_caps);
+
+ if (link_res == FALSE) {
+ GST_LOG ("elements linking failed");
+ continue;
+ }
+
+ state_ret = gst_element_set_state (ctx.pipeline, GST_STATE_PLAYING);
+ fail_unless (state_ret != GST_STATE_CHANGE_FAILURE,
+ "couldn't set pipeline to PLAYING state");
+
+ g_main_loop_run (loop);
+
+ state_ret = gst_element_set_state (ctx.pipeline, GST_STATE_READY);
+ fail_unless (state_ret != GST_STATE_CHANGE_FAILURE,
+ "couldn't set pipeline to READY state");
+ }
+
+ g_main_loop_unref (loop);
+
+ videobox_test_deinit_context (&ctx);
+}
+
+GST_END_TEST;
+
+
+static Suite *
+videobox_suite (void)
+{
+ Suite *s = suite_create ("videobox");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_caps_transform);
+
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ int nf;
+
+ Suite *s = videobox_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ srunner_run_all (sr, CK_NORMAL);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}
diff --git a/tests/check/elements/videocrop.c b/tests/check/elements/videocrop.c
new file mode 100644
index 0000000..a7bb46d
--- /dev/null
+++ b/tests/check/elements/videocrop.c
@@ -0,0 +1,834 @@
+/* GStreamer unit test for the videocrop element
+ * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_VALGRIND
+# include <valgrind/valgrind.h>
+#endif
+
+#include <unistd.h>
+
+#include <gst/check/gstcheck.h>
+#include <gst/video/video.h>
+#include <gst/base/gstbasetransform.h>
+
+/* return a list of caps where we only need to set
+ * width and height to get fixed caps */
+static GList *
+video_crop_get_test_caps (GstElement * videocrop)
+{
+ GstCaps *templ, *allowed_caps;
+ GstPad *srcpad;
+ GList *list = NULL;
+ guint i;
+
+ srcpad = gst_element_get_static_pad (videocrop, "src");
+ fail_unless (srcpad != NULL);
+ templ = gst_pad_get_pad_template_caps (srcpad);
+ fail_unless (templ != NULL);
+
+ allowed_caps = gst_caps_normalize (templ);
+
+ for (i = 0; i < gst_caps_get_size (allowed_caps); ++i) {
+ GstStructure *new_structure;
+ GstCaps *single_caps;
+
+ single_caps = gst_caps_new_empty ();
+ new_structure =
+ gst_structure_copy (gst_caps_get_structure (allowed_caps, i));
+ gst_structure_set (new_structure, "framerate", GST_TYPE_FRACTION,
+ 1, 1, NULL);
+ gst_structure_remove_field (new_structure, "width");
+ gst_structure_remove_field (new_structure, "height");
+ gst_caps_append_structure (single_caps, new_structure);
+
+ GST_DEBUG ("have caps %" GST_PTR_FORMAT, single_caps);
+ /* should be fixed without width/height */
+ fail_unless (gst_caps_is_fixed (single_caps));
+
+ list = g_list_prepend (list, single_caps);
+ }
+
+ gst_caps_unref (allowed_caps);
+ gst_object_unref (srcpad);
+
+ return list;
+}
+
+GST_START_TEST (test_unit_sizes)
+{
+ GstBaseTransformClass *csp_klass, *vcrop_klass;
+ GstElement *videocrop, *csp;
+ GList *caps_list, *l;
+
+ videocrop = gst_element_factory_make ("videocrop", "videocrop");
+ fail_unless (videocrop != NULL, "Failed to create videocrop element");
+ vcrop_klass = GST_BASE_TRANSFORM_GET_CLASS (videocrop);
+
+ csp = gst_element_factory_make ("videoconvert", "csp");
+ fail_unless (csp != NULL, "Failed to create videoconvert element");
+ csp_klass = GST_BASE_TRANSFORM_GET_CLASS (csp);
+
+ caps_list = video_crop_get_test_caps (videocrop);
+
+ for (l = caps_list; l != NULL; l = l->next) {
+ const struct
+ {
+ gint width, height;
+ } sizes_to_try[] = {
+ {
+ 160, 120}, {
+ 161, 120}, {
+ 160, 121}, {
+ 161, 121}, {
+ 159, 120}, {
+ 160, 119}, {
+ 159, 119}, {
+ 159, 121}
+ };
+ GstStructure *s;
+ GstCaps *caps;
+ gint i;
+
+ caps = gst_caps_copy (GST_CAPS (l->data));
+ s = gst_caps_get_structure (caps, 0);
+ fail_unless (s != NULL);
+
+ for (i = 0; i < G_N_ELEMENTS (sizes_to_try); ++i) {
+ gchar *caps_str;
+ gsize csp_size = 0;
+ gsize vc_size = 0;
+
+ gst_structure_set (s, "width", G_TYPE_INT, sizes_to_try[i].width,
+ "height", G_TYPE_INT, sizes_to_try[i].height, NULL);
+
+ caps_str = gst_caps_to_string (caps);
+ GST_INFO ("Testing unit size for %s", caps_str);
+
+ /* skip if videoconvert doesn't support these caps
+ * (only works with gst-plugins-base 0.10.9.1 or later) */
+ if (!csp_klass->get_unit_size ((GstBaseTransform *) csp, caps, &csp_size)) {
+ GST_INFO ("videoconvert does not support format %s", caps_str);
+ g_free (caps_str);
+ continue;
+ }
+
+ fail_unless (vcrop_klass->get_unit_size ((GstBaseTransform *) videocrop,
+ caps, &vc_size));
+
+ fail_unless (vc_size == csp_size,
+ "videocrop and videoconvert return different unit sizes for "
+ "caps %s: vc_size=%d, csp_size=%d", caps_str, vc_size, csp_size);
+
+ g_free (caps_str);
+ }
+
+ gst_caps_unref (caps);
+ }
+
+ g_list_foreach (caps_list, (GFunc) gst_caps_unref, NULL);
+ g_list_free (caps_list);
+
+ gst_object_unref (csp);
+ gst_object_unref (videocrop);
+}
+
+GST_END_TEST;
+
+typedef struct
+{
+ GstElement *pipeline;
+ GstElement *src;
+ GstElement *filter;
+ GstElement *crop;
+ GstElement *filter2;
+ GstElement *sink;
+ GstBuffer *last_buf;
+ GstCaps *last_caps;
+} GstVideoCropTestContext;
+
+static void
+handoff_cb (GstElement * sink, GstBuffer * buf, GstPad * pad,
+ GstVideoCropTestContext * ctx)
+{
+ GstCaps *caps;
+
+ gst_buffer_replace (&ctx->last_buf, buf);
+ caps = gst_pad_get_current_caps (pad);
+ gst_caps_replace (&ctx->last_caps, caps);
+ gst_caps_unref (caps);
+}
+
+static void
+videocrop_test_cropping_init_context (GstVideoCropTestContext * ctx)
+{
+ fail_unless (ctx != NULL);
+
+ ctx->pipeline = gst_pipeline_new ("pipeline");
+ fail_unless (ctx->pipeline != NULL);
+ ctx->src = gst_element_factory_make ("videotestsrc", "src");
+ fail_unless (ctx->src != NULL, "Failed to create videotestsrc element");
+ ctx->filter = gst_element_factory_make ("capsfilter", "filter");
+ fail_unless (ctx->filter != NULL, "Failed to create capsfilter element");
+ ctx->crop = gst_element_factory_make ("videocrop", "crop");
+ fail_unless (ctx->crop != NULL, "Failed to create videocrop element");
+ ctx->filter2 = gst_element_factory_make ("capsfilter", "filter2");
+ fail_unless (ctx->filter2 != NULL,
+ "Failed to create second capsfilter element");
+ ctx->sink = gst_element_factory_make ("fakesink", "sink");
+ fail_unless (ctx->sink != NULL, "Failed to create fakesink element");
+
+ gst_bin_add_many (GST_BIN (ctx->pipeline), ctx->src, ctx->filter,
+ ctx->crop, ctx->filter2, ctx->sink, NULL);
+ gst_element_link_many (ctx->src, ctx->filter, ctx->crop, ctx->filter2,
+ ctx->sink, NULL);
+
+ /* set pattern to 'red' - for our purposes it doesn't matter anyway */
+ g_object_set (ctx->src, "pattern", 4, NULL);
+
+ g_object_set (ctx->sink, "signal-handoffs", TRUE, NULL);
+ g_signal_connect (ctx->sink, "preroll-handoff", G_CALLBACK (handoff_cb), ctx);
+
+ ctx->last_buf = NULL;
+ ctx->last_caps = NULL;
+
+ GST_LOG ("context inited");
+}
+
+static void
+videocrop_test_cropping_deinit_context (GstVideoCropTestContext * ctx)
+{
+ GST_LOG ("deiniting context");
+
+ gst_element_set_state (ctx->pipeline, GST_STATE_NULL);
+ gst_object_unref (ctx->pipeline);
+ gst_buffer_replace (&ctx->last_buf, NULL);
+ gst_caps_replace (&ctx->last_caps, NULL);
+ memset (ctx, 0x00, sizeof (GstVideoCropTestContext));
+}
+
+typedef void (*GstVideoCropTestBufferFunc) (GstBuffer * buffer, GstCaps * caps);
+
+static void
+videocrop_test_cropping (GstVideoCropTestContext * ctx, GstCaps * in_caps,
+ GstCaps * out_caps, gint left, gint right, gint top, gint bottom,
+ GstVideoCropTestBufferFunc func)
+{
+ GST_LOG ("lrtb = %03u %03u %03u %03u, in_caps = %" GST_PTR_FORMAT
+ ", out_caps = %" GST_PTR_FORMAT, left, right, top, bottom, in_caps,
+ out_caps);
+
+ g_object_set (ctx->filter, "caps", in_caps, NULL);
+ g_object_set (ctx->filter2, "caps", out_caps, NULL);
+
+ g_object_set (ctx->crop, "left", left, "right", right, "top", top,
+ "bottom", bottom, NULL);
+
+ /* this will fail if videotestsrc doesn't support our format; we need
+ * videotestsrc from -base CVS 0.10.9.1 with RGBA and AYUV support */
+ fail_unless (gst_element_set_state (ctx->pipeline,
+ GST_STATE_PAUSED) != GST_STATE_CHANGE_FAILURE);
+ fail_unless (gst_element_get_state (ctx->pipeline, NULL, NULL,
+ -1) == GST_STATE_CHANGE_SUCCESS);
+
+ if (func != NULL) {
+ func (ctx->last_buf, ctx->last_caps);
+ }
+
+ gst_element_set_state (ctx->pipeline, GST_STATE_NULL);
+}
+
+static void
+check_1x1_buffer (GstBuffer * buf, GstCaps * caps)
+{
+ GstVideoInfo info;
+ GstVideoFrame frame;
+ /* the exact values we check for come from videotestsrc */
+ static const guint yuv_values[] = { 81, 90, 240, 255 };
+ static const guint rgb_values[] = { 0xff, 0, 0, 255 };
+ static const guint gray8_values[] = { 0x51 };
+ static const guint gray16_values[] = { 0x5151 };
+ const guint *values;
+ guint i;
+ const GstVideoFormatInfo *finfo;
+
+ fail_unless (buf != NULL);
+ fail_unless (caps != NULL);
+
+ fail_unless (gst_video_info_from_caps (&info, caps));
+ fail_unless (gst_video_frame_map (&frame, &info, buf, GST_MAP_READ));
+
+ finfo = info.finfo;
+
+ if (GST_VIDEO_INFO_IS_YUV (&info))
+ values = yuv_values;
+ else if (GST_VIDEO_INFO_IS_GRAY (&info))
+ if (GST_VIDEO_FORMAT_INFO_BITS (finfo) == 8)
+ values = gray8_values;
+ else
+ values = gray16_values;
+ else
+ values = rgb_values;
+
+ GST_MEMDUMP ("buffer", GST_VIDEO_FRAME_PLANE_DATA (&frame, 0), 8);
+
+ for (i = 0; i < GST_VIDEO_FRAME_N_COMPONENTS (&frame); i++) {
+ guint8 *data = GST_VIDEO_FRAME_COMP_DATA (&frame, i);
+
+ GST_DEBUG ("W: %d", GST_VIDEO_FORMAT_INFO_W_SUB (finfo, i));
+ GST_DEBUG ("H: %d", GST_VIDEO_FORMAT_INFO_H_SUB (finfo, i));
+
+ if (GST_VIDEO_FORMAT_INFO_W_SUB (finfo,
+ i) >= GST_VIDEO_FRAME_WIDTH (&frame))
+ continue;
+ if (GST_VIDEO_FORMAT_INFO_H_SUB (finfo,
+ i) >= GST_VIDEO_FRAME_HEIGHT (&frame))
+ continue;
+
+ if (GST_VIDEO_FORMAT_INFO_BITS (finfo) == 8) {
+ fail_unless_equals_int (data[0], values[i]);
+ } else if (GST_VIDEO_FORMAT_INFO_BITS (finfo) == 16) {
+ guint16 pixels, val;
+ gint depth;
+
+ if (GST_VIDEO_FORMAT_INFO_IS_LE (finfo))
+ pixels = GST_READ_UINT16_LE (data);
+ else
+ pixels = GST_READ_UINT16_BE (data);
+
+ depth = GST_VIDEO_FORMAT_INFO_DEPTH (finfo, i);
+ val = pixels >> GST_VIDEO_FORMAT_INFO_SHIFT (finfo, i);
+ val = val & ((1 << depth) - 1);
+
+ GST_DEBUG ("val %08x %d : %d", pixels, i, val);
+ if (depth <= 8) {
+ fail_unless_equals_int (val, values[i] >> (8 - depth));
+ } else {
+ fail_unless_equals_int (val, values[i] >> (16 - depth));
+ }
+ } else {
+ }
+ }
+
+ gst_video_frame_unmap (&frame);
+
+ /*
+ fail_unless_equals_int ((pixel & rmask) >> rshift, 0xff);
+ fail_unless_equals_int ((pixel & gmask) >> gshift, 0x00);
+ fail_unless_equals_int ((pixel & bmask) >> bshift, 0x00);
+ */
+}
+
+GST_START_TEST (test_crop_to_1x1)
+{
+ GstVideoCropTestContext ctx;
+ GList *caps_list, *node;
+
+ videocrop_test_cropping_init_context (&ctx);
+
+ caps_list = video_crop_get_test_caps (ctx.crop);
+
+ for (node = caps_list; node != NULL; node = node->next) {
+ GstStructure *s;
+ GstCaps *caps;
+
+ caps = gst_caps_copy (GST_CAPS (node->data));
+ s = gst_caps_get_structure (caps, 0);
+ fail_unless (s != NULL);
+
+ GST_INFO ("testing format: %" GST_PTR_FORMAT, caps);
+
+ gst_structure_set (s, "width", G_TYPE_INT, 160,
+ "height", G_TYPE_INT, 160, NULL);
+
+ videocrop_test_cropping (&ctx, caps, NULL, 159, 0, 159, 0,
+ check_1x1_buffer);
+ /* commented out because they don't really add anything useful check-wise:
+ videocrop_test_cropping (&ctx, caps, NULL, 0, 159, 0, 159, check_1x1_buffer);
+ videocrop_test_cropping (&ctx, caps, NULL, 159, 0, 0, 159, check_1x1_buffer);
+ videocrop_test_cropping (&ctx, caps, NULL, 0, 159, 159, 0, check_1x1_buffer);
+ */
+ gst_caps_unref (caps);
+ }
+ g_list_foreach (caps_list, (GFunc) gst_caps_unref, NULL);
+ g_list_free (caps_list);
+
+ videocrop_test_cropping_deinit_context (&ctx);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_cropping)
+{
+ GstVideoCropTestContext ctx;
+ struct
+ {
+ gint width, height;
+ } sizes_to_try[] = {
+ {
+ 160, 160}, {
+ 161, 160}, {
+ 160, 161}, {
+ 161, 161}, {
+ 159, 160}, {
+ 160, 159}, {
+ 159, 159}, {
+ 159, 161}
+ };
+ GList *caps_list, *node;
+ gint i;
+
+ videocrop_test_cropping_init_context (&ctx);
+
+ caps_list = video_crop_get_test_caps (ctx.crop);
+ node = g_list_nth (caps_list, __i__);
+
+ if (node != NULL) {
+ GstStructure *s;
+ GstCaps *caps;
+
+ caps = gst_caps_copy (GST_CAPS (node->data));
+ s = gst_caps_get_structure (caps, 0);
+ fail_unless (s != NULL);
+
+ GST_INFO ("testing format: %" GST_PTR_FORMAT, caps);
+
+ for (i = 0; i < G_N_ELEMENTS (sizes_to_try); ++i) {
+ GstCaps *in_caps, *out_caps;
+
+ GST_INFO (" - %d x %d", sizes_to_try[i].width, sizes_to_try[i].height);
+
+ gst_structure_set (s, "width", G_TYPE_INT, sizes_to_try[i].width,
+ "height", G_TYPE_INT, sizes_to_try[i].height, NULL);
+ in_caps = gst_caps_copy (caps);
+
+ gst_structure_set (s, "width", G_TYPE_INT, 1, "height", G_TYPE_INT, 1,
+ NULL);
+ out_caps = gst_caps_copy (caps);
+
+ videocrop_test_cropping (&ctx, in_caps, NULL, 0, 0, 0, 0, NULL);
+ videocrop_test_cropping (&ctx, in_caps, NULL, 1, 0, 0, 0, NULL);
+ videocrop_test_cropping (&ctx, in_caps, NULL, 0, 1, 0, 0, NULL);
+ videocrop_test_cropping (&ctx, in_caps, NULL, 0, 0, 1, 0, NULL);
+ videocrop_test_cropping (&ctx, in_caps, NULL, 0, 0, 0, 1, NULL);
+ videocrop_test_cropping (&ctx, in_caps, NULL, 63, 0, 0, 0, NULL);
+ videocrop_test_cropping (&ctx, in_caps, NULL, 0, 63, 0, 0, NULL);
+ videocrop_test_cropping (&ctx, in_caps, NULL, 0, 0, 63, 0, NULL);
+ videocrop_test_cropping (&ctx, in_caps, NULL, 0, 0, 0, 63, NULL);
+ videocrop_test_cropping (&ctx, in_caps, NULL, 63, 0, 0, 1, NULL);
+ videocrop_test_cropping (&ctx, in_caps, NULL, 0, 63, 1, 0, NULL);
+ videocrop_test_cropping (&ctx, in_caps, NULL, 0, 1, 63, 0, NULL);
+ videocrop_test_cropping (&ctx, in_caps, NULL, 1, 0, 0, 63, NULL);
+ videocrop_test_cropping (&ctx, in_caps, NULL, 0, 0, 0, 0, NULL);
+ videocrop_test_cropping (&ctx, in_caps, NULL, 32, 0, 0, 128, NULL);
+ videocrop_test_cropping (&ctx, in_caps, NULL, 0, 32, 128, 0, NULL);
+ videocrop_test_cropping (&ctx, in_caps, NULL, 0, 128, 32, 0, NULL);
+ videocrop_test_cropping (&ctx, in_caps, NULL, 128, 0, 0, 32, NULL);
+ videocrop_test_cropping (&ctx, in_caps, NULL, 1, 1, 1, 1, NULL);
+ videocrop_test_cropping (&ctx, in_caps, NULL, 63, 63, 63, 63, NULL);
+ videocrop_test_cropping (&ctx, in_caps, NULL, 64, 64, 64, 64, NULL);
+
+ /* Dynamic cropping */
+ videocrop_test_cropping (&ctx, in_caps, out_caps, -1, -1, -1, -1, NULL);
+ videocrop_test_cropping (&ctx, in_caps, out_caps, 0, -1, -1, -1, NULL);
+ videocrop_test_cropping (&ctx, in_caps, out_caps, -1, 0, -1, -1, NULL);
+ videocrop_test_cropping (&ctx, in_caps, out_caps, -1, -1, 0, -1, NULL);
+ videocrop_test_cropping (&ctx, in_caps, out_caps, -1, -1, -1, 0, NULL);
+ videocrop_test_cropping (&ctx, in_caps, out_caps, 10, -1, 10, -1, NULL);
+ videocrop_test_cropping (&ctx, in_caps, out_caps, -1, 10, -1, 10, NULL);
+ videocrop_test_cropping (&ctx, in_caps, out_caps,
+ sizes_to_try[i].width - 1, -1, -1, -1, NULL);
+ videocrop_test_cropping (&ctx, in_caps, out_caps, -1,
+ sizes_to_try[i].width - 1, -1, -1, NULL);
+ videocrop_test_cropping (&ctx, in_caps, out_caps, -1, -1,
+ sizes_to_try[i].height - 1, -1, NULL);
+ videocrop_test_cropping (&ctx, in_caps, out_caps, -1, -1, -1,
+ sizes_to_try[i].height - 1, NULL);
+
+ gst_caps_unref (in_caps);
+ gst_caps_unref (out_caps);
+ }
+
+ gst_caps_unref (caps);
+ } else {
+ GST_INFO ("no caps #%d", __i__);
+ }
+ g_list_foreach (caps_list, (GFunc) gst_caps_unref, NULL);
+ g_list_free (caps_list);
+
+ videocrop_test_cropping_deinit_context (&ctx);
+}
+
+GST_END_TEST;
+
+
+static GstPadProbeReturn
+buffer_probe_cb (GstPad * pad, GstPadProbeInfo * info, gpointer data)
+{
+ GstBuffer **p_buf = data;
+ GstBuffer *buf = GST_PAD_PROBE_INFO_BUFFER (info);
+
+ gst_buffer_replace (p_buf, buf);
+
+ return GST_PAD_PROBE_OK; /* keep data */
+}
+
+GST_START_TEST (test_passthrough)
+{
+ GstStateChangeReturn state_ret;
+ GstVideoCropTestContext ctx;
+ GstPad *srcpad;
+ GstBuffer *gen_buf = NULL; /* buffer generated by videotestsrc */
+
+ videocrop_test_cropping_init_context (&ctx);
+
+ g_object_set (ctx.src, "num-buffers", 1, NULL);
+
+ srcpad = gst_element_get_static_pad (ctx.src, "src");
+ fail_unless (srcpad != NULL);
+ gst_pad_add_probe (srcpad, GST_PAD_PROBE_TYPE_BUFFER, buffer_probe_cb,
+ &gen_buf, NULL);
+ gst_object_unref (srcpad);
+
+ g_object_set (ctx.crop, "left", 0, "right", 0, "top", 0, "bottom", 0, NULL);
+
+ state_ret = gst_element_set_state (ctx.pipeline, GST_STATE_PAUSED);
+ fail_unless (state_ret != GST_STATE_CHANGE_FAILURE,
+ "couldn't set pipeline to PAUSED state");
+
+ state_ret = gst_element_get_state (ctx.pipeline, NULL, NULL, -1);
+ fail_unless (state_ret == GST_STATE_CHANGE_SUCCESS,
+ "pipeline failed to go to PAUSED state");
+
+ fail_unless (gen_buf != NULL);
+ fail_unless (ctx.last_buf != NULL);
+
+ /* pass through should do nothing */
+ fail_unless (gen_buf == ctx.last_buf);
+
+ videocrop_test_cropping_deinit_context (&ctx);
+
+ fail_unless_equals_int (GST_MINI_OBJECT_REFCOUNT_VALUE (gen_buf), 1);
+ gst_buffer_unref (gen_buf);
+}
+
+GST_END_TEST;
+
+static gint
+notgst_value_list_get_nth_int (const GValue * list_val, guint n)
+{
+ const GValue *v;
+
+ fail_unless (GST_VALUE_HOLDS_LIST (list_val));
+ fail_unless (n < gst_value_list_get_size (list_val));
+
+ v = gst_value_list_get_value (list_val, n);
+ fail_unless (G_VALUE_HOLDS_INT (v));
+ return g_value_get_int (v);
+}
+
+GST_START_TEST (test_caps_transform)
+{
+ GstVideoCropTestContext ctx;
+ GstBaseTransformClass *klass;
+ GstBaseTransform *crop;
+ const GValue *w_val;
+ const GValue *h_val;
+ GstCaps *caps, *adj_caps;
+
+ videocrop_test_cropping_init_context (&ctx);
+
+ crop = GST_BASE_TRANSFORM (ctx.crop);
+ klass = GST_BASE_TRANSFORM_GET_CLASS (ctx.crop);
+ fail_unless (klass != NULL);
+
+ caps = gst_caps_new_simple ("video/x-raw",
+ "format", G_TYPE_STRING, "I420",
+ "framerate", GST_TYPE_FRACTION, 1, 1,
+ "width", G_TYPE_INT, 200, "height", G_TYPE_INT, 100, NULL);
+
+ /* by default, it should be no cropping and hence passthrough */
+ adj_caps = klass->transform_caps (crop, GST_PAD_SRC, caps, NULL);
+ fail_unless (adj_caps != NULL);
+ fail_unless (gst_caps_is_equal (adj_caps, caps));
+ gst_caps_unref (adj_caps);
+
+ adj_caps = klass->transform_caps (crop, GST_PAD_SINK, caps, NULL);
+ fail_unless (adj_caps != NULL);
+ fail_unless (gst_caps_is_equal (adj_caps, caps));
+ gst_caps_unref (adj_caps);
+
+ /* make sure that's still true after changing properties back and forth */
+ g_object_set (ctx.crop, "left", 1, "right", 3, "top", 5, "bottom", 7, NULL);
+ g_object_set (ctx.crop, "left", 0, "right", 0, "top", 0, "bottom", 0, NULL);
+
+ adj_caps = klass->transform_caps (crop, GST_PAD_SRC, caps, NULL);
+ fail_unless (adj_caps != NULL);
+ fail_unless (gst_caps_is_equal (adj_caps, caps));
+ gst_caps_unref (adj_caps);
+
+ adj_caps = klass->transform_caps (crop, GST_PAD_SINK, caps, NULL);
+ fail_unless (adj_caps != NULL);
+ fail_unless (gst_caps_is_equal (adj_caps, caps));
+ gst_caps_unref (adj_caps);
+
+ /* now check adjustments made ... */
+ g_object_set (ctx.crop, "left", 1, "right", 3, "top", 5, "bottom", 7, NULL);
+
+ /* ========= (1) fixed value ============================================= */
+
+ /* sink => source, source must be bigger if we crop stuff off */
+ adj_caps = klass->transform_caps (crop, GST_PAD_SRC, caps, NULL);
+ fail_unless (adj_caps != NULL);
+ fail_unless (gst_caps_get_size (adj_caps) == 1);
+ w_val =
+ gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "width");
+ fail_unless (w_val != NULL);
+ fail_unless (G_VALUE_HOLDS_INT (w_val));
+ fail_unless_equals_int (g_value_get_int (w_val), 200 + (1 + 3));
+ h_val =
+ gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "height");
+ fail_unless (h_val != NULL);
+ fail_unless (G_VALUE_HOLDS_INT (h_val));
+ fail_unless_equals_int (g_value_get_int (h_val), 100 + (5 + 7));
+ gst_caps_unref (adj_caps);
+
+ /* source => sink becomes smaller */
+ adj_caps = klass->transform_caps (crop, GST_PAD_SINK, caps, NULL);
+ fail_unless (adj_caps != NULL);
+ fail_unless (gst_caps_get_size (adj_caps) == 1);
+ w_val =
+ gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "width");
+ fail_unless (w_val != NULL);
+ fail_unless (G_VALUE_HOLDS_INT (w_val));
+ fail_unless_equals_int (g_value_get_int (w_val), 200 - (1 + 3));
+ h_val =
+ gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "height");
+ fail_unless (h_val != NULL);
+ fail_unless (G_VALUE_HOLDS_INT (h_val));
+ fail_unless_equals_int (g_value_get_int (h_val), 100 - (5 + 7));
+ gst_caps_unref (adj_caps);
+
+ /* ========= (2) range (simple adjustment) =============================== */
+
+ gst_structure_set (gst_caps_get_structure (caps, 0),
+ "width", GST_TYPE_INT_RANGE, 1000, 2000,
+ "height", GST_TYPE_INT_RANGE, 3000, 4000, NULL);
+
+ /* sink => source, source must be bigger if we crop stuff off */
+ adj_caps = klass->transform_caps (crop, GST_PAD_SRC, caps, NULL);
+ fail_unless (adj_caps != NULL);
+ fail_unless (gst_caps_get_size (adj_caps) == 1);
+ w_val =
+ gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "width");
+ fail_unless (w_val != NULL);
+ fail_unless (GST_VALUE_HOLDS_INT_RANGE (w_val));
+ fail_unless_equals_int (gst_value_get_int_range_min (w_val), 1000 + (1 + 3));
+ fail_unless_equals_int (gst_value_get_int_range_max (w_val), 2000 + (1 + 3));
+ h_val =
+ gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "height");
+ fail_unless (h_val != NULL);
+ fail_unless (GST_VALUE_HOLDS_INT_RANGE (h_val));
+ fail_unless_equals_int (gst_value_get_int_range_min (h_val), 3000 + (5 + 7));
+ fail_unless_equals_int (gst_value_get_int_range_max (h_val), 4000 + (5 + 7));
+ gst_caps_unref (adj_caps);
+
+ /* source => sink becomes smaller */
+ adj_caps = klass->transform_caps (crop, GST_PAD_SINK, caps, NULL);
+ fail_unless (adj_caps != NULL);
+ fail_unless (gst_caps_get_size (adj_caps) == 1);
+ w_val =
+ gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "width");
+ fail_unless (w_val != NULL);
+ fail_unless (GST_VALUE_HOLDS_INT_RANGE (w_val));
+ fail_unless_equals_int (gst_value_get_int_range_min (w_val), 1000 - (1 + 3));
+ fail_unless_equals_int (gst_value_get_int_range_max (w_val), 2000 - (1 + 3));
+ h_val =
+ gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "height");
+ fail_unless (h_val != NULL);
+ fail_unless (GST_VALUE_HOLDS_INT_RANGE (h_val));
+ fail_unless_equals_int (gst_value_get_int_range_min (h_val), 3000 - (5 + 7));
+ fail_unless_equals_int (gst_value_get_int_range_max (h_val), 4000 - (5 + 7));
+ gst_caps_unref (adj_caps);
+
+ /* ========= (3) range (adjustment at boundary) ========================== */
+
+ gst_structure_set (gst_caps_get_structure (caps, 0),
+ "width", GST_TYPE_INT_RANGE, 2, G_MAXINT,
+ "height", GST_TYPE_INT_RANGE, 2, G_MAXINT, NULL);
+
+ /* sink => source, source must be bigger if we crop stuff off */
+ adj_caps = klass->transform_caps (crop, GST_PAD_SRC, caps, NULL);
+ fail_unless (adj_caps != NULL);
+ fail_unless (gst_caps_get_size (adj_caps) == 1);
+ w_val =
+ gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "width");
+ fail_unless (w_val != NULL);
+ fail_unless (GST_VALUE_HOLDS_INT_RANGE (w_val));
+ fail_unless_equals_int (gst_value_get_int_range_min (w_val), 2 + (1 + 3));
+ fail_unless_equals_int (gst_value_get_int_range_max (w_val), G_MAXINT);
+ h_val =
+ gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "height");
+ fail_unless (h_val != NULL);
+ fail_unless (GST_VALUE_HOLDS_INT_RANGE (h_val));
+ fail_unless_equals_int (gst_value_get_int_range_min (h_val), 2 + (5 + 7));
+ fail_unless_equals_int (gst_value_get_int_range_max (h_val), G_MAXINT);
+ gst_caps_unref (adj_caps);
+
+ /* source => sink becomes smaller */
+ adj_caps = klass->transform_caps (crop, GST_PAD_SINK, caps, NULL);
+ fail_unless (adj_caps != NULL);
+ fail_unless (gst_caps_get_size (adj_caps) == 1);
+ w_val =
+ gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "width");
+ fail_unless (w_val != NULL);
+ fail_unless (GST_VALUE_HOLDS_INT_RANGE (w_val));
+ fail_unless_equals_int (gst_value_get_int_range_min (w_val), 1);
+ fail_unless_equals_int (gst_value_get_int_range_max (w_val),
+ G_MAXINT - (1 + 3));
+ h_val =
+ gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "height");
+ fail_unless (h_val != NULL);
+ fail_unless (GST_VALUE_HOLDS_INT_RANGE (h_val));
+ fail_unless_equals_int (gst_value_get_int_range_min (h_val), 1);
+ fail_unless_equals_int (gst_value_get_int_range_max (h_val),
+ G_MAXINT - (5 + 7));
+ gst_caps_unref (adj_caps);
+
+ /* ========= (4) list of values ========================================== */
+
+ {
+ GValue list = { 0, };
+ GValue ival = { 0, };
+
+ g_value_init (&ival, G_TYPE_INT);
+ g_value_init (&list, GST_TYPE_LIST);
+ g_value_set_int (&ival, 2);
+ gst_value_list_append_value (&list, &ival);
+ g_value_set_int (&ival, G_MAXINT);
+ gst_value_list_append_value (&list, &ival);
+ gst_structure_set_value (gst_caps_get_structure (caps, 0), "width", &list);
+ g_value_unset (&list);
+ g_value_unset (&ival);
+
+ g_value_init (&ival, G_TYPE_INT);
+ g_value_init (&list, GST_TYPE_LIST);
+ g_value_set_int (&ival, 5);
+ gst_value_list_append_value (&list, &ival);
+ g_value_set_int (&ival, 1000);
+ gst_value_list_append_value (&list, &ival);
+ gst_structure_set_value (gst_caps_get_structure (caps, 0), "height", &list);
+ g_value_unset (&list);
+ g_value_unset (&ival);
+ }
+
+ /* sink => source, source must be bigger if we crop stuff off */
+ adj_caps = klass->transform_caps (crop, GST_PAD_SRC, caps, NULL);
+ fail_unless (adj_caps != NULL);
+ fail_unless (gst_caps_get_size (adj_caps) == 1);
+ w_val =
+ gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "width");
+ fail_unless (w_val != NULL);
+ fail_unless (GST_VALUE_HOLDS_LIST (w_val));
+ fail_unless_equals_int (notgst_value_list_get_nth_int (w_val, 0),
+ 2 + (1 + 3));
+ fail_unless_equals_int (notgst_value_list_get_nth_int (w_val, 1), G_MAXINT);
+ h_val =
+ gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "height");
+ fail_unless (h_val != NULL);
+ fail_unless (GST_VALUE_HOLDS_LIST (h_val));
+ fail_unless_equals_int (notgst_value_list_get_nth_int (h_val, 0),
+ 5 + (5 + 7));
+ fail_unless_equals_int (notgst_value_list_get_nth_int (h_val, 1),
+ 1000 + (5 + 7));
+ gst_caps_unref (adj_caps);
+
+ /* source => sink becomes smaller */
+ adj_caps = klass->transform_caps (crop, GST_PAD_SINK, caps, NULL);
+ fail_unless (adj_caps != NULL);
+ fail_unless (gst_caps_get_size (adj_caps) == 1);
+ w_val =
+ gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "width");
+ fail_unless (w_val != NULL);
+ fail_unless (GST_VALUE_HOLDS_LIST (w_val));
+ fail_unless_equals_int (notgst_value_list_get_nth_int (w_val, 0), 1);
+ fail_unless_equals_int (notgst_value_list_get_nth_int (w_val, 1),
+ G_MAXINT - (1 + 3));
+ h_val =
+ gst_structure_get_value (gst_caps_get_structure (adj_caps, 0), "height");
+ fail_unless (h_val != NULL);
+ fail_unless (GST_VALUE_HOLDS_LIST (h_val));
+ fail_unless_equals_int (notgst_value_list_get_nth_int (h_val, 0), 1);
+ fail_unless_equals_int (notgst_value_list_get_nth_int (h_val, 1),
+ 1000 - (5 + 7));
+ gst_caps_unref (adj_caps);
+
+ gst_caps_unref (caps);
+ videocrop_test_cropping_deinit_context (&ctx);
+}
+
+GST_END_TEST;
+
+static Suite *
+videocrop_suite (void)
+{
+ Suite *s = suite_create ("videocrop");
+ TCase *tc_chain = tcase_create ("general");
+
+#ifdef HAVE_VALGRIND
+ if (RUNNING_ON_VALGRIND) {
+ /* our tests take quite a long time, so increase
+ * timeout (~25 minutes on my 1.6GHz AMD K7) */
+ tcase_set_timeout (tc_chain, 30 * 60);
+ } else
+#endif
+ {
+ /* increase timeout, these tests take a long time (60 secs here) */
+ tcase_set_timeout (tc_chain, 2 * 60);
+ }
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_crop_to_1x1);
+ tcase_add_test (tc_chain, test_caps_transform);
+ tcase_add_test (tc_chain, test_passthrough);
+ tcase_add_test (tc_chain, test_unit_sizes);
+ tcase_add_loop_test (tc_chain, test_cropping, 0, 25);
+
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ int nf;
+
+ Suite *s = videocrop_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ srunner_run_all (sr, CK_NORMAL);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}
diff --git a/tests/check/elements/videofilter.c b/tests/check/elements/videofilter.c
new file mode 100755
index 0000000..17b67f8
--- /dev/null
+++ b/tests/check/elements/videofilter.c
@@ -0,0 +1,281 @@
+/* GStreamer
+ *
+ * unit test for videofilter elements
+ *
+ * Copyright (C) <2006> Mark Nauwelaerts <manauw@skynet.be>
+ *
+ * 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.
+ */
+
+#include <unistd.h>
+#include <stdarg.h>
+
+#include <gst/video/video.h>
+#include <gst/check/gstcheck.h>
+
+gboolean have_eos = FALSE;
+
+/* 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 */
+GstPad *mysrcpad, *mysinkpad;
+
+#define VIDEO_CAPS_TEMPLATE_STRING \
+ GST_VIDEO_CAPS_MAKE ("{ I420, AYUV, YUY2, UYVY, YVYU, xRGB }")
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (VIDEO_CAPS_TEMPLATE_STRING)
+ );
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (VIDEO_CAPS_TEMPLATE_STRING)
+ );
+
+/* takes over reference for outcaps */
+static GstElement *
+setup_filter (const gchar * name, const gchar * prop, va_list var_args)
+{
+ GstElement *element;
+
+ GST_DEBUG ("setup_element");
+ element = gst_check_setup_element (name);
+ g_object_set_valist (G_OBJECT (element), prop, var_args);
+ mysrcpad = gst_check_setup_src_pad (element, &srctemplate);
+ gst_pad_set_active (mysrcpad, TRUE);
+ mysinkpad = gst_check_setup_sink_pad (element, &sinktemplate);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ return element;
+}
+
+static void
+cleanup_filter (GstElement * filter)
+{
+ GST_DEBUG ("cleanup_element");
+
+ gst_check_teardown_src_pad (filter);
+ gst_check_teardown_sink_pad (filter);
+ gst_check_teardown_element (filter);
+}
+
+static void
+check_filter_caps (const gchar * name, GstEvent * event, GstCaps * caps,
+ gint size, gint num_buffers, const gchar * prop, va_list varargs)
+{
+ GstElement *filter;
+ GstBuffer *inbuffer, *outbuffer;
+ gint i;
+ GstSegment segment;
+
+ filter = setup_filter (name, prop, varargs);
+ fail_unless (gst_element_set_state (filter,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ gst_check_setup_events (mysrcpad, filter, caps, GST_FORMAT_TIME);
+
+ /* ensure segment (format) properly setup */
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
+
+ if (event)
+ fail_unless (gst_pad_push_event (mysrcpad, event));
+
+ for (i = 0; i < num_buffers; ++i) {
+ inbuffer = gst_buffer_new_and_alloc (size);
+ /* makes valgrind's memcheck happier */
+ gst_buffer_memset (inbuffer, 0, 0, size);
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+ }
+
+ fail_unless (g_list_length (buffers) == num_buffers);
+
+ /* clean up buffers */
+ for (i = 0; i < num_buffers; ++i) {
+ outbuffer = GST_BUFFER (buffers->data);
+ fail_if (outbuffer == NULL);
+
+ switch (i) {
+ case 0:
+ fail_unless (gst_buffer_get_size (outbuffer) == size);
+ /* no check on filter operation itself */
+ break;
+ default:
+ break;
+ }
+ buffers = g_list_remove (buffers, outbuffer);
+
+ ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
+ gst_buffer_unref (outbuffer);
+ outbuffer = NULL;
+ }
+
+ cleanup_filter (filter);
+ g_list_free (buffers);
+ buffers = NULL;
+}
+
+static void
+check_filter_varargs (const gchar * name, GstEvent * event, gint num_buffers,
+ const gchar * prop, va_list varargs)
+{
+ static const struct
+ {
+ const int width, height;
+ } resolutions[] = { {
+ 384, 288}, {
+ 385, 289}, {
+ 385, 385}};
+ gint i, n, r;
+ gint size;
+ GstCaps *allcaps, *templ = gst_caps_from_string (VIDEO_CAPS_TEMPLATE_STRING);
+
+ allcaps = gst_caps_normalize (templ);
+
+ n = gst_caps_get_size (allcaps);
+
+ for (i = 0; i < n; i++) {
+ GstStructure *s = gst_caps_get_structure (allcaps, i);
+ GstCaps *caps = gst_caps_new_empty ();
+
+ gst_caps_append_structure (caps, gst_structure_copy (s));
+
+ /* try various resolutions */
+ for (r = 0; r < G_N_ELEMENTS (resolutions); ++r) {
+ GstVideoInfo info;
+ va_list args_cp;
+
+ caps = gst_caps_make_writable (caps);
+ gst_caps_set_simple (caps, "width", G_TYPE_INT, resolutions[r].width,
+ "height", G_TYPE_INT, resolutions[r].height,
+ "framerate", GST_TYPE_FRACTION, 25, 1, NULL);
+
+ GST_DEBUG ("Testing with caps: %" GST_PTR_FORMAT, caps);
+ gst_video_info_from_caps (&info, caps);
+ size = GST_VIDEO_INFO_SIZE (&info);
+
+ if (event)
+ gst_event_ref (event);
+
+ va_copy (args_cp, varargs);
+ check_filter_caps (name, event, caps, size, num_buffers, prop, args_cp);
+ va_end (args_cp);
+ }
+
+ gst_caps_unref (caps);
+ }
+
+ gst_caps_unref (allcaps);
+ if (event)
+ gst_event_unref (event);
+}
+
+static void
+check_filter (const gchar * name, gint num_buffers, const gchar * prop, ...)
+{
+ va_list varargs;
+ va_start (varargs, prop);
+ check_filter_varargs (name, NULL, num_buffers, prop, varargs);
+ va_end (varargs);
+}
+
+static void
+check_filter_with_event (const gchar * name, GstEvent * event,
+ gint num_buffers, const gchar * prop, ...)
+{
+ va_list varargs;
+ va_start (varargs, prop);
+ check_filter_varargs (name, event, num_buffers, prop, varargs);
+ va_end (varargs);
+}
+
+GST_START_TEST (test_videobalance)
+{
+ check_filter ("videobalance", 2, NULL);
+ check_filter ("videobalance", 2, "saturation", 0.5, "hue", 0.8, NULL);
+}
+
+GST_END_TEST;
+
+
+GST_START_TEST (test_videoflip)
+{
+ GstEvent *event;
+
+ /* these we can handle with the caps */
+ check_filter ("videoflip", 2, "method", 0, NULL);
+ check_filter ("videoflip", 2, "method", 2, NULL);
+ check_filter ("videoflip", 2, "method", 4, NULL);
+ check_filter ("videoflip", 2, "method", 5, NULL);
+
+ event = gst_event_new_tag (gst_tag_list_new_empty ());
+ check_filter_with_event ("videoflip", event, 2, "method", 8, NULL);
+
+ event = gst_event_new_tag (gst_tag_list_new (GST_TAG_IMAGE_ORIENTATION,
+ "rotate-180", NULL));
+ check_filter_with_event ("videoflip", event, 2, "method", 8, NULL);
+
+ event = gst_event_new_tag (gst_tag_list_new (GST_TAG_IMAGE_ORIENTATION,
+ "invalid", NULL));
+ check_filter_with_event ("videoflip", event, 2, "method", 8, NULL);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_gamma)
+{
+ check_filter ("gamma", 2, NULL);
+ check_filter ("gamma", 2, "gamma", 2.0, NULL);
+}
+
+GST_END_TEST;
+
+
+static Suite *
+videofilter_suite (void)
+{
+ Suite *s = suite_create ("videofilter");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_videobalance);
+ tcase_add_test (tc_chain, test_videoflip);
+ tcase_add_test (tc_chain, test_gamma);
+
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ int nf;
+
+ Suite *s = videofilter_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ srunner_run_all (sr, CK_NORMAL);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}
diff --git a/tests/check/elements/videomixer.c b/tests/check/elements/videomixer.c
new file mode 100755
index 0000000..e428332
--- /dev/null
+++ b/tests/check/elements/videomixer.c
@@ -0,0 +1,1063 @@
+/* GStreamer
+ *
+ * unit test for videmixer
+ *
+ * Copyright (C) <2005> Thomas Vander Stichele <thomas at apestaart dot org>
+ * Copyright (C) <2013> Thibault Saunier <thibault.saunier@collabora.com>
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef HAVE_VALGRIND
+# include <valgrind/valgrind.h>
+#endif
+
+#include <unistd.h>
+
+#include <gst/check/gstcheck.h>
+#include <gst/check/gstconsistencychecker.h>
+#include <gst/base/gstbasesrc.h>
+
+#define VIDEO_CAPS_STRING \
+ "video/x-raw, " \
+ "width = (int) 320, " \
+ "height = (int) 240, " \
+ "framerate = (fraction) 25/1 , " \
+ "format = (string) I420"
+
+static GMainLoop *main_loop;
+
+/* make sure downstream gets a CAPS event before buffers are sent */
+GST_START_TEST (test_caps)
+{
+ GstElement *pipeline, *src, *videomixer, *sink;
+ GstStateChangeReturn state_res;
+ GstCaps *caps;
+ GstPad *pad;
+
+ /* build pipeline */
+ pipeline = gst_pipeline_new ("pipeline");
+
+ src = gst_element_factory_make ("videotestsrc", "src1");
+ videomixer = gst_element_factory_make ("videomixer", "videomixer");
+ sink = gst_element_factory_make ("fakesink", "sink");
+ gst_bin_add_many (GST_BIN (pipeline), src, videomixer, sink, NULL);
+
+ fail_unless (gst_element_link_many (src, videomixer, sink, NULL));
+
+ /* prepare playing */
+ state_res = gst_element_set_state (pipeline, GST_STATE_PAUSED);
+ fail_unless_equals_int (state_res, GST_STATE_CHANGE_ASYNC);
+
+ /* wait for preroll */
+ state_res = gst_element_get_state (pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
+ fail_unless_equals_int (state_res, GST_STATE_CHANGE_SUCCESS);
+
+ /* check caps on fakesink */
+ pad = gst_element_get_static_pad (sink, "sink");
+ caps = gst_pad_get_current_caps (pad);
+ fail_unless (caps != NULL);
+ gst_caps_unref (caps);
+ gst_object_unref (pad);
+
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+ gst_object_unref (pipeline);
+}
+
+GST_END_TEST;
+
+static void
+message_received (GstBus * bus, GstMessage * message, GstPipeline * bin)
+{
+ GST_INFO ("bus message from \"%" GST_PTR_FORMAT "\": %" GST_PTR_FORMAT,
+ GST_MESSAGE_SRC (message), message);
+
+ switch (message->type) {
+ case GST_MESSAGE_EOS:
+ g_main_loop_quit (main_loop);
+ break;
+ case GST_MESSAGE_WARNING:{
+ GError *gerror;
+ gchar *debug;
+
+ gst_message_parse_warning (message, &gerror, &debug);
+ gst_object_default_error (GST_MESSAGE_SRC (message), gerror, debug);
+ g_error_free (gerror);
+ g_free (debug);
+ break;
+ }
+ case GST_MESSAGE_ERROR:{
+ GError *gerror;
+ gchar *debug;
+
+ gst_message_parse_error (message, &gerror, &debug);
+ gst_object_default_error (GST_MESSAGE_SRC (message), gerror, debug);
+ g_error_free (gerror);
+ g_free (debug);
+ g_main_loop_quit (main_loop);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+
+static GstFormat format = GST_FORMAT_UNDEFINED;
+static gint64 position = -1;
+
+static void
+test_event_message_received (GstBus * bus, GstMessage * message,
+ GstPipeline * bin)
+{
+ GST_INFO ("bus message from \"%" GST_PTR_FORMAT "\": %" GST_PTR_FORMAT,
+ GST_MESSAGE_SRC (message), message);
+
+ switch (message->type) {
+ case GST_MESSAGE_SEGMENT_DONE:
+ gst_message_parse_segment_done (message, &format, &position);
+ GST_INFO ("received segment_done : %" G_GINT64_FORMAT, position);
+ g_main_loop_quit (main_loop);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+
+GST_START_TEST (test_event)
+{
+ GstElement *bin, *src1, *src2, *videomixer, *sink;
+ GstBus *bus;
+ GstEvent *seek_event;
+ GstStateChangeReturn state_res;
+ gboolean res;
+ GstPad *srcpad, *sinkpad;
+ GstStreamConsistency *chk_1, *chk_2, *chk_3;
+
+ GST_INFO ("preparing test");
+
+ /* build pipeline */
+ bin = gst_pipeline_new ("pipeline");
+ bus = gst_element_get_bus (bin);
+ gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);
+
+ src1 = gst_element_factory_make ("videotestsrc", "src1");
+ src2 = gst_element_factory_make ("videotestsrc", "src2");
+ videomixer = gst_element_factory_make ("videomixer", "videomixer");
+ sink = gst_element_factory_make ("fakesink", "sink");
+ gst_bin_add_many (GST_BIN (bin), src1, src2, videomixer, sink, NULL);
+
+ res = gst_element_link (src1, videomixer);
+ fail_unless (res == TRUE, NULL);
+ res = gst_element_link (src2, videomixer);
+ fail_unless (res == TRUE, NULL);
+ res = gst_element_link (videomixer, sink);
+ fail_unless (res == TRUE, NULL);
+
+ srcpad = gst_element_get_static_pad (videomixer, "src");
+ chk_3 = gst_consistency_checker_new (srcpad);
+ gst_object_unref (srcpad);
+
+ /* create consistency checkers for the pads */
+ srcpad = gst_element_get_static_pad (src1, "src");
+ chk_1 = gst_consistency_checker_new (srcpad);
+ sinkpad = gst_pad_get_peer (srcpad);
+ gst_consistency_checker_add_pad (chk_3, sinkpad);
+ gst_object_unref (sinkpad);
+ gst_object_unref (srcpad);
+
+ srcpad = gst_element_get_static_pad (src2, "src");
+ chk_2 = gst_consistency_checker_new (srcpad);
+ sinkpad = gst_pad_get_peer (srcpad);
+ gst_consistency_checker_add_pad (chk_3, sinkpad);
+ gst_object_unref (sinkpad);
+ gst_object_unref (srcpad);
+
+ seek_event = gst_event_new_seek (1.0, GST_FORMAT_TIME,
+ GST_SEEK_FLAG_SEGMENT | GST_SEEK_FLAG_FLUSH,
+ GST_SEEK_TYPE_SET, (GstClockTime) 0,
+ GST_SEEK_TYPE_SET, (GstClockTime) 2 * GST_SECOND);
+
+ format = GST_FORMAT_UNDEFINED;
+ position = -1;
+
+ main_loop = g_main_loop_new (NULL, FALSE);
+ g_signal_connect (bus, "message::segment-done",
+ (GCallback) test_event_message_received, bin);
+ g_signal_connect (bus, "message::error", (GCallback) message_received, bin);
+ g_signal_connect (bus, "message::warning", (GCallback) message_received, bin);
+ g_signal_connect (bus, "message::eos", (GCallback) message_received, bin);
+
+ GST_INFO ("starting test");
+
+ /* prepare playing */
+ state_res = gst_element_set_state (bin, GST_STATE_PAUSED);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ /* wait for completion */
+ state_res = gst_element_get_state (bin, NULL, NULL, GST_CLOCK_TIME_NONE);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ res = gst_element_send_event (bin, seek_event);
+ fail_unless (res == TRUE, NULL);
+
+ /* run pipeline */
+ state_res = gst_element_set_state (bin, GST_STATE_PLAYING);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ GST_INFO ("running main loop");
+ g_main_loop_run (main_loop);
+
+ state_res = gst_element_set_state (bin, GST_STATE_NULL);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ ck_assert_int_eq (position, 2 * GST_SECOND);
+
+ /* cleanup */
+ g_main_loop_unref (main_loop);
+ gst_consistency_checker_free (chk_1);
+ gst_consistency_checker_free (chk_2);
+ gst_consistency_checker_free (chk_3);
+ gst_bus_remove_signal_watch (bus);
+ gst_object_unref (bus);
+ gst_object_unref (bin);
+}
+
+GST_END_TEST;
+
+static guint play_count = 0;
+static GstEvent *play_seek_event = NULL;
+
+static void
+test_play_twice_message_received (GstBus * bus, GstMessage * message,
+ GstPipeline * bin)
+{
+ gboolean res;
+ GstStateChangeReturn state_res;
+
+ GST_INFO ("bus message from \"%" GST_PTR_FORMAT "\": %" GST_PTR_FORMAT,
+ GST_MESSAGE_SRC (message), message);
+
+ switch (message->type) {
+ case GST_MESSAGE_SEGMENT_DONE:
+ play_count++;
+ if (play_count == 1) {
+ state_res = gst_element_set_state (GST_ELEMENT (bin), GST_STATE_READY);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ /* prepare playing again */
+ state_res = gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PAUSED);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ /* wait for completion */
+ state_res =
+ gst_element_get_state (GST_ELEMENT (bin), NULL, NULL,
+ GST_CLOCK_TIME_NONE);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ res = gst_element_send_event (GST_ELEMENT (bin),
+ gst_event_ref (play_seek_event));
+ fail_unless (res == TRUE, NULL);
+
+ state_res =
+ gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PLAYING);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+ } else {
+ g_main_loop_quit (main_loop);
+ }
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+
+GST_START_TEST (test_play_twice)
+{
+ GstElement *bin, *src1, *src2, *videomixer, *sink;
+ GstBus *bus;
+ gboolean res;
+ GstStateChangeReturn state_res;
+ GstPad *srcpad;
+ GstStreamConsistency *consist;
+
+ GST_INFO ("preparing test");
+
+ /* build pipeline */
+ bin = gst_pipeline_new ("pipeline");
+ bus = gst_element_get_bus (bin);
+ gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);
+
+ src1 = gst_element_factory_make ("videotestsrc", "src1");
+ src2 = gst_element_factory_make ("videotestsrc", "src2");
+ videomixer = gst_element_factory_make ("videomixer", "videomixer");
+ sink = gst_element_factory_make ("fakesink", "sink");
+ gst_bin_add_many (GST_BIN (bin), src1, src2, videomixer, sink, NULL);
+
+ res = gst_element_link (src1, videomixer);
+ fail_unless (res == TRUE, NULL);
+ res = gst_element_link (src2, videomixer);
+ fail_unless (res == TRUE, NULL);
+ res = gst_element_link (videomixer, sink);
+ fail_unless (res == TRUE, NULL);
+
+ srcpad = gst_element_get_static_pad (videomixer, "src");
+ consist = gst_consistency_checker_new (srcpad);
+ gst_object_unref (srcpad);
+
+ play_seek_event = gst_event_new_seek (1.0, GST_FORMAT_TIME,
+ GST_SEEK_FLAG_SEGMENT | GST_SEEK_FLAG_FLUSH,
+ GST_SEEK_TYPE_SET, (GstClockTime) 0,
+ GST_SEEK_TYPE_SET, (GstClockTime) 2 * GST_SECOND);
+
+ play_count = 0;
+
+ main_loop = g_main_loop_new (NULL, FALSE);
+ g_signal_connect (bus, "message::segment-done",
+ (GCallback) test_play_twice_message_received, bin);
+ g_signal_connect (bus, "message::error", (GCallback) message_received, bin);
+ g_signal_connect (bus, "message::warning", (GCallback) message_received, bin);
+ g_signal_connect (bus, "message::eos", (GCallback) message_received, bin);
+
+ GST_INFO ("starting test");
+
+ /* prepare playing */
+ state_res = gst_element_set_state (bin, GST_STATE_PAUSED);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ /* wait for completion */
+ state_res =
+ gst_element_get_state (GST_ELEMENT (bin), NULL, NULL,
+ GST_CLOCK_TIME_NONE);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ res = gst_element_send_event (bin, gst_event_ref (play_seek_event));
+ fail_unless (res == TRUE, NULL);
+
+ GST_INFO ("seeked");
+
+ /* run pipeline */
+ state_res = gst_element_set_state (bin, GST_STATE_PLAYING);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ g_main_loop_run (main_loop);
+
+ state_res = gst_element_set_state (bin, GST_STATE_NULL);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ ck_assert_int_eq (play_count, 2);
+
+ /* cleanup */
+ g_main_loop_unref (main_loop);
+ gst_consistency_checker_free (consist);
+ gst_event_ref (play_seek_event);
+ gst_bus_remove_signal_watch (bus);
+ gst_object_unref (bus);
+ gst_object_unref (bin);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_play_twice_then_add_and_play_again)
+{
+ GstElement *bin, *src1, *src2, *src3, *videomixer, *sink;
+ GstBus *bus;
+ gboolean res;
+ GstStateChangeReturn state_res;
+ gint i;
+ GstPad *srcpad;
+ GstStreamConsistency *consist;
+
+ GST_INFO ("preparing test");
+
+ /* build pipeline */
+ bin = gst_pipeline_new ("pipeline");
+ bus = gst_element_get_bus (bin);
+ gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);
+
+ src1 = gst_element_factory_make ("videotestsrc", "src1");
+ src2 = gst_element_factory_make ("videotestsrc", "src2");
+ videomixer = gst_element_factory_make ("videomixer", "videomixer");
+ sink = gst_element_factory_make ("fakesink", "sink");
+ gst_bin_add_many (GST_BIN (bin), src1, src2, videomixer, sink, NULL);
+
+ srcpad = gst_element_get_static_pad (videomixer, "src");
+ consist = gst_consistency_checker_new (srcpad);
+ gst_object_unref (srcpad);
+
+ res = gst_element_link (src1, videomixer);
+ fail_unless (res == TRUE, NULL);
+ res = gst_element_link (src2, videomixer);
+ fail_unless (res == TRUE, NULL);
+ res = gst_element_link (videomixer, sink);
+ fail_unless (res == TRUE, NULL);
+
+ play_seek_event = gst_event_new_seek (1.0, GST_FORMAT_TIME,
+ GST_SEEK_FLAG_SEGMENT | GST_SEEK_FLAG_FLUSH,
+ GST_SEEK_TYPE_SET, (GstClockTime) 0,
+ GST_SEEK_TYPE_SET, (GstClockTime) 2 * GST_SECOND);
+
+ main_loop = g_main_loop_new (NULL, FALSE);
+ g_signal_connect (bus, "message::segment-done",
+ (GCallback) test_play_twice_message_received, bin);
+ g_signal_connect (bus, "message::error", (GCallback) message_received, bin);
+ g_signal_connect (bus, "message::warning", (GCallback) message_received, bin);
+ g_signal_connect (bus, "message::eos", (GCallback) message_received, bin);
+
+ /* run it twice */
+ for (i = 0; i < 2; i++) {
+ play_count = 0;
+
+ GST_INFO ("starting test-loop %d", i);
+
+ /* prepare playing */
+ state_res = gst_element_set_state (bin, GST_STATE_PAUSED);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ /* wait for completion */
+ state_res =
+ gst_element_get_state (GST_ELEMENT (bin), NULL, NULL,
+ GST_CLOCK_TIME_NONE);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ res = gst_element_send_event (bin, gst_event_ref (play_seek_event));
+ fail_unless (res == TRUE, NULL);
+
+ GST_INFO ("seeked");
+
+ /* run pipeline */
+ state_res = gst_element_set_state (bin, GST_STATE_PLAYING);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ g_main_loop_run (main_loop);
+
+ state_res = gst_element_set_state (bin, GST_STATE_READY);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ ck_assert_int_eq (play_count, 2);
+
+ /* plug another source */
+ if (i == 0) {
+ src3 = gst_element_factory_make ("videotestsrc", "src3");
+ gst_bin_add (GST_BIN (bin), src3);
+
+ res = gst_element_link (src3, videomixer);
+ fail_unless (res == TRUE, NULL);
+ }
+
+ gst_consistency_checker_reset (consist);
+ }
+
+ state_res = gst_element_set_state (bin, GST_STATE_NULL);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ /* cleanup */
+ g_main_loop_unref (main_loop);
+ gst_event_ref (play_seek_event);
+ gst_consistency_checker_free (consist);
+ gst_bus_remove_signal_watch (bus);
+ gst_object_unref (bus);
+ gst_object_unref (bin);
+}
+
+GST_END_TEST;
+
+/* check if adding pads work as expected */
+GST_START_TEST (test_add_pad)
+{
+ GstElement *bin, *src1, *src2, *videomixer, *sink;
+ GstBus *bus;
+ GstPad *srcpad;
+ gboolean res;
+ GstStateChangeReturn state_res;
+
+ GST_INFO ("preparing test");
+
+ /* build pipeline */
+ bin = gst_pipeline_new ("pipeline");
+ bus = gst_element_get_bus (bin);
+ gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);
+
+ src1 = gst_element_factory_make ("videotestsrc", "src1");
+ g_object_set (src1, "num-buffers", 4, NULL);
+ src2 = gst_element_factory_make ("videotestsrc", "src2");
+ /* one buffer less, we connect with 1 buffer of delay */
+ g_object_set (src2, "num-buffers", 3, NULL);
+ videomixer = gst_element_factory_make ("videomixer", "videomixer");
+ sink = gst_element_factory_make ("fakesink", "sink");
+ gst_bin_add_many (GST_BIN (bin), src1, videomixer, sink, NULL);
+
+ res = gst_element_link (src1, videomixer);
+ fail_unless (res == TRUE, NULL);
+ res = gst_element_link (videomixer, sink);
+ fail_unless (res == TRUE, NULL);
+
+ srcpad = gst_element_get_static_pad (videomixer, "src");
+ gst_object_unref (srcpad);
+
+ main_loop = g_main_loop_new (NULL, FALSE);
+ g_signal_connect (bus, "message::segment-done", (GCallback) message_received,
+ bin);
+ g_signal_connect (bus, "message::error", (GCallback) message_received, bin);
+ g_signal_connect (bus, "message::warning", (GCallback) message_received, bin);
+ g_signal_connect (bus, "message::eos", (GCallback) message_received, bin);
+
+ GST_INFO ("starting test");
+
+ /* prepare playing */
+ state_res = gst_element_set_state (bin, GST_STATE_PAUSED);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ /* wait for completion */
+ state_res =
+ gst_element_get_state (GST_ELEMENT (bin), NULL, NULL,
+ GST_CLOCK_TIME_NONE);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ /* add other element */
+ gst_bin_add_many (GST_BIN (bin), src2, NULL);
+
+ /* now link the second element */
+ res = gst_element_link (src2, videomixer);
+ fail_unless (res == TRUE, NULL);
+
+ /* set to PAUSED as well */
+ state_res = gst_element_set_state (src2, GST_STATE_PAUSED);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ /* now play all */
+ state_res = gst_element_set_state (bin, GST_STATE_PLAYING);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ g_main_loop_run (main_loop);
+
+ state_res = gst_element_set_state (bin, GST_STATE_NULL);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ /* cleanup */
+ g_main_loop_unref (main_loop);
+ gst_bus_remove_signal_watch (bus);
+ gst_object_unref (bus);
+ gst_object_unref (bin);
+}
+
+GST_END_TEST;
+
+/* check if removing pads work as expected */
+GST_START_TEST (test_remove_pad)
+{
+ GstElement *bin, *src, *videomixer, *sink;
+ GstBus *bus;
+ GstPad *pad, *srcpad;
+ gboolean res;
+ GstStateChangeReturn state_res;
+
+ GST_INFO ("preparing test");
+
+ /* build pipeline */
+ bin = gst_pipeline_new ("pipeline");
+ bus = gst_element_get_bus (bin);
+ gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);
+
+ src = gst_element_factory_make ("videotestsrc", "src");
+ g_object_set (src, "num-buffers", 4, NULL);
+ videomixer = gst_element_factory_make ("videomixer", "videomixer");
+ sink = gst_element_factory_make ("fakesink", "sink");
+ gst_bin_add_many (GST_BIN (bin), src, videomixer, sink, NULL);
+
+ res = gst_element_link (src, videomixer);
+ fail_unless (res == TRUE, NULL);
+ res = gst_element_link (videomixer, sink);
+ fail_unless (res == TRUE, NULL);
+
+ /* create an unconnected sinkpad in videomixer */
+ pad = gst_element_get_request_pad (videomixer, "sink_%u");
+ fail_if (pad == NULL, NULL);
+
+ srcpad = gst_element_get_static_pad (videomixer, "src");
+ gst_object_unref (srcpad);
+
+ main_loop = g_main_loop_new (NULL, FALSE);
+ g_signal_connect (bus, "message::segment-done", (GCallback) message_received,
+ bin);
+ g_signal_connect (bus, "message::error", (GCallback) message_received, bin);
+ g_signal_connect (bus, "message::warning", (GCallback) message_received, bin);
+ g_signal_connect (bus, "message::eos", (GCallback) message_received, bin);
+
+ GST_INFO ("starting test");
+
+ /* prepare playing, this will not preroll as videomixer is waiting
+ * on the unconnected sinkpad. */
+ state_res = gst_element_set_state (bin, GST_STATE_PAUSED);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ /* wait for completion for one second, will return ASYNC */
+ state_res = gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, GST_SECOND);
+ ck_assert_int_eq (state_res, GST_STATE_CHANGE_ASYNC);
+
+ /* get rid of the pad now, videomixer should stop waiting on it and
+ * continue the preroll */
+ gst_element_release_request_pad (videomixer, pad);
+ gst_object_unref (pad);
+
+ /* wait for completion, should work now */
+ state_res =
+ gst_element_get_state (GST_ELEMENT (bin), NULL, NULL,
+ GST_CLOCK_TIME_NONE);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ /* now play all */
+ state_res = gst_element_set_state (bin, GST_STATE_PLAYING);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ g_main_loop_run (main_loop);
+
+ state_res = gst_element_set_state (bin, GST_STATE_NULL);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ /* cleanup */
+ g_main_loop_unref (main_loop);
+ gst_bus_remove_signal_watch (bus);
+ gst_object_unref (G_OBJECT (bus));
+ gst_object_unref (G_OBJECT (bin));
+}
+
+GST_END_TEST;
+
+
+static GstBuffer *handoff_buffer = NULL;
+static void
+handoff_buffer_cb (GstElement * fakesink, GstBuffer * buffer, GstPad * pad,
+ gpointer user_data)
+{
+ GST_DEBUG ("got buffer %p", buffer);
+ gst_buffer_replace (&handoff_buffer, buffer);
+}
+
+/* check if clipping works as expected */
+GST_START_TEST (test_clip)
+{
+ GstSegment segment;
+ GstElement *bin, *videomixer, *sink;
+ GstBus *bus;
+ GstPad *sinkpad;
+ gboolean res;
+ GstStateChangeReturn state_res;
+ GstFlowReturn ret;
+ GstEvent *event;
+ GstBuffer *buffer;
+ GstCaps *caps;
+
+ GST_INFO ("preparing test");
+
+ /* build pipeline */
+ bin = gst_pipeline_new ("pipeline");
+ bus = gst_element_get_bus (bin);
+ gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);
+
+ g_signal_connect (bus, "message::error", (GCallback) message_received, bin);
+ g_signal_connect (bus, "message::warning", (GCallback) message_received, bin);
+ g_signal_connect (bus, "message::eos", (GCallback) message_received, bin);
+
+ /* just an videomixer and a fakesink */
+ videomixer = gst_element_factory_make ("videomixer", "videomixer");
+ sink = gst_element_factory_make ("fakesink", "sink");
+ g_object_set (sink, "signal-handoffs", TRUE, NULL);
+ g_signal_connect (sink, "handoff", (GCallback) handoff_buffer_cb, NULL);
+ gst_bin_add_many (GST_BIN (bin), videomixer, sink, NULL);
+
+ res = gst_element_link (videomixer, sink);
+ fail_unless (res == TRUE, NULL);
+
+ /* set to playing */
+ state_res = gst_element_set_state (bin, GST_STATE_PLAYING);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ /* create an unconnected sinkpad in videomixer, should also automatically activate
+ * the pad */
+ sinkpad = gst_element_get_request_pad (videomixer, "sink_%u");
+ fail_if (sinkpad == NULL, NULL);
+
+ gst_pad_send_event (sinkpad, gst_event_new_stream_start ("test"));
+
+ caps = gst_caps_from_string (VIDEO_CAPS_STRING);
+
+ gst_pad_set_caps (sinkpad, caps);
+ gst_caps_unref (caps);
+
+ /* send segment to videomixer */
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+ segment.start = GST_SECOND;
+ segment.stop = 2 * GST_SECOND;
+ segment.time = 0;
+ event = gst_event_new_segment (&segment);
+ gst_pad_send_event (sinkpad, event);
+
+ /* should be clipped and ok */
+ buffer = gst_buffer_new_and_alloc (115200);
+ GST_BUFFER_TIMESTAMP (buffer) = 0;
+ GST_BUFFER_DURATION (buffer) = 250 * GST_MSECOND;
+ GST_DEBUG ("pushing buffer %p", buffer);
+ ret = gst_pad_chain (sinkpad, buffer);
+ ck_assert_int_eq (ret, GST_FLOW_OK);
+ fail_unless (handoff_buffer == NULL);
+
+ /* should be partially clipped */
+ buffer = gst_buffer_new_and_alloc (115200);
+ GST_BUFFER_TIMESTAMP (buffer) = 900 * GST_MSECOND;
+ GST_BUFFER_DURATION (buffer) = 250 * GST_MSECOND;
+ GST_DEBUG ("pushing buffer %p", buffer);
+ ret = gst_pad_chain (sinkpad, buffer);
+ ck_assert_int_eq (ret, GST_FLOW_OK);
+ fail_unless (handoff_buffer != NULL);
+ gst_buffer_replace (&handoff_buffer, NULL);
+
+ /* should not be clipped */
+ buffer = gst_buffer_new_and_alloc (115200);
+ GST_BUFFER_TIMESTAMP (buffer) = 1 * GST_SECOND;
+ GST_BUFFER_DURATION (buffer) = 250 * GST_MSECOND;
+ GST_DEBUG ("pushing buffer %p", buffer);
+ ret = gst_pad_chain (sinkpad, buffer);
+ ck_assert_int_eq (ret, GST_FLOW_OK);
+ fail_unless (handoff_buffer != NULL);
+ gst_buffer_replace (&handoff_buffer, NULL);
+
+ /* should be clipped and ok */
+ buffer = gst_buffer_new_and_alloc (115200);
+ GST_BUFFER_TIMESTAMP (buffer) = 2 * GST_SECOND;
+ GST_BUFFER_DURATION (buffer) = 250 * GST_MSECOND;
+ GST_DEBUG ("pushing buffer %p", buffer);
+ ret = gst_pad_chain (sinkpad, buffer);
+ ck_assert_int_eq (ret, GST_FLOW_OK);
+ fail_unless (handoff_buffer == NULL);
+
+ gst_object_unref (sinkpad);
+ gst_element_set_state (bin, GST_STATE_NULL);
+ gst_bus_remove_signal_watch (bus);
+ gst_object_unref (bus);
+ gst_object_unref (bin);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_duration_is_max)
+{
+ GstElement *bin, *src[3], *videomixer, *sink;
+ GstStateChangeReturn state_res;
+ GstFormat format = GST_FORMAT_TIME;
+ gboolean res;
+ gint64 duration;
+
+ GST_INFO ("preparing test");
+
+ /* build pipeline */
+ bin = gst_pipeline_new ("pipeline");
+
+ /* 3 sources, an videomixer and a fakesink */
+ src[0] = gst_element_factory_make ("videotestsrc", NULL);
+ src[1] = gst_element_factory_make ("videotestsrc", NULL);
+ src[2] = gst_element_factory_make ("videotestsrc", NULL);
+ videomixer = gst_element_factory_make ("videomixer", "videomixer");
+ sink = gst_element_factory_make ("fakesink", "sink");
+ gst_bin_add_many (GST_BIN (bin), src[0], src[1], src[2], videomixer, sink,
+ NULL);
+
+ gst_element_link (src[0], videomixer);
+ gst_element_link (src[1], videomixer);
+ gst_element_link (src[2], videomixer);
+ gst_element_link (videomixer, sink);
+
+ /* irks, duration is reset on basesrc */
+ state_res = gst_element_set_state (bin, GST_STATE_PAUSED);
+ fail_unless (state_res != GST_STATE_CHANGE_FAILURE, NULL);
+
+ /* set durations on src */
+ GST_BASE_SRC (src[0])->segment.duration = 1000;
+ GST_BASE_SRC (src[1])->segment.duration = 3000;
+ GST_BASE_SRC (src[2])->segment.duration = 2000;
+
+ /* set to playing */
+ state_res = gst_element_set_state (bin, GST_STATE_PLAYING);
+ fail_unless (state_res != GST_STATE_CHANGE_FAILURE, NULL);
+
+ /* wait for completion */
+ state_res =
+ gst_element_get_state (GST_ELEMENT (bin), NULL, NULL,
+ GST_CLOCK_TIME_NONE);
+ fail_unless (state_res != GST_STATE_CHANGE_FAILURE, NULL);
+
+ res = gst_element_query_duration (GST_ELEMENT (bin), format, &duration);
+ fail_unless (res, NULL);
+
+ ck_assert_int_eq (duration, 3000);
+
+ gst_element_set_state (bin, GST_STATE_NULL);
+ gst_object_unref (bin);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_duration_unknown_overrides)
+{
+ GstElement *bin, *src[3], *videomixer, *sink;
+ GstStateChangeReturn state_res;
+ GstFormat format = GST_FORMAT_TIME;
+ gboolean res;
+ gint64 duration;
+
+ GST_INFO ("preparing test");
+
+ /* build pipeline */
+ bin = gst_pipeline_new ("pipeline");
+
+ /* 3 sources, an videomixer and a fakesink */
+ src[0] = gst_element_factory_make ("videotestsrc", NULL);
+ src[1] = gst_element_factory_make ("videotestsrc", NULL);
+ src[2] = gst_element_factory_make ("videotestsrc", NULL);
+ videomixer = gst_element_factory_make ("videomixer", "videomixer");
+ sink = gst_element_factory_make ("fakesink", "sink");
+ gst_bin_add_many (GST_BIN (bin), src[0], src[1], src[2], videomixer, sink,
+ NULL);
+
+ gst_element_link (src[0], videomixer);
+ gst_element_link (src[1], videomixer);
+ gst_element_link (src[2], videomixer);
+ gst_element_link (videomixer, sink);
+
+ /* irks, duration is reset on basesrc */
+ state_res = gst_element_set_state (bin, GST_STATE_PAUSED);
+ fail_unless (state_res != GST_STATE_CHANGE_FAILURE, NULL);
+
+ /* set durations on src */
+ GST_BASE_SRC (src[0])->segment.duration = GST_CLOCK_TIME_NONE;
+ GST_BASE_SRC (src[1])->segment.duration = 3000;
+ GST_BASE_SRC (src[2])->segment.duration = 2000;
+
+ /* set to playing */
+ state_res = gst_element_set_state (bin, GST_STATE_PLAYING);
+ fail_unless (state_res != GST_STATE_CHANGE_FAILURE, NULL);
+
+ /* wait for completion */
+ state_res =
+ gst_element_get_state (GST_ELEMENT (bin), NULL, NULL,
+ GST_CLOCK_TIME_NONE);
+ fail_unless (state_res != GST_STATE_CHANGE_FAILURE, NULL);
+
+ res = gst_element_query_duration (GST_ELEMENT (bin), format, &duration);
+ fail_unless (res, NULL);
+
+ ck_assert_int_eq (duration, GST_CLOCK_TIME_NONE);
+
+ gst_element_set_state (bin, GST_STATE_NULL);
+ gst_object_unref (bin);
+}
+
+GST_END_TEST;
+
+
+static gboolean looped = FALSE;
+
+static void
+loop_segment_done (GstBus * bus, GstMessage * message, GstElement * bin)
+{
+ GST_INFO ("bus message from \"%" GST_PTR_FORMAT "\": %" GST_PTR_FORMAT,
+ GST_MESSAGE_SRC (message), message);
+
+ if (looped) {
+ g_main_loop_quit (main_loop);
+ } else {
+ GstEvent *seek_event;
+ gboolean res;
+
+ seek_event = gst_event_new_seek (1.0, GST_FORMAT_TIME,
+ GST_SEEK_FLAG_SEGMENT,
+ GST_SEEK_TYPE_SET, (GstClockTime) 0,
+ GST_SEEK_TYPE_SET, (GstClockTime) 1 * GST_SECOND);
+
+ res = gst_element_send_event (bin, seek_event);
+ fail_unless (res == TRUE, NULL);
+ looped = TRUE;
+ }
+}
+
+GST_START_TEST (test_loop)
+{
+ GstElement *bin, *src1, *src2, *videomixer, *sink;
+ GstBus *bus;
+ GstEvent *seek_event;
+ GstStateChangeReturn state_res;
+ gboolean res;
+
+ GST_INFO ("preparing test");
+
+ /* build pipeline */
+ bin = gst_pipeline_new ("pipeline");
+ bus = gst_element_get_bus (bin);
+ gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH);
+
+ src1 = gst_element_factory_make ("videotestsrc", "src1");
+ src2 = gst_element_factory_make ("videotestsrc", "src2");
+ videomixer = gst_element_factory_make ("videomixer", "videomixer");
+ sink = gst_element_factory_make ("fakesink", "sink");
+ gst_bin_add_many (GST_BIN (bin), src1, src2, videomixer, sink, NULL);
+
+ res = gst_element_link (src1, videomixer);
+ fail_unless (res == TRUE, NULL);
+ res = gst_element_link (src2, videomixer);
+ fail_unless (res == TRUE, NULL);
+ res = gst_element_link (videomixer, sink);
+ fail_unless (res == TRUE, NULL);
+
+ seek_event = gst_event_new_seek (1.0, GST_FORMAT_TIME,
+ GST_SEEK_FLAG_SEGMENT | GST_SEEK_FLAG_FLUSH,
+ GST_SEEK_TYPE_SET, (GstClockTime) 0, GST_SEEK_TYPE_SET,
+ (GstClockTime) 2 * GST_SECOND);
+
+ main_loop = g_main_loop_new (NULL, FALSE);
+ g_signal_connect (bus, "message::segment-done",
+ (GCallback) loop_segment_done, bin);
+ g_signal_connect (bus, "message::error", (GCallback) message_received, bin);
+ g_signal_connect (bus, "message::warning", (GCallback) message_received, bin);
+ g_signal_connect (bus, "message::eos", (GCallback) message_received, bin);
+
+ GST_INFO ("starting test");
+
+ /* prepare playing */
+ state_res = gst_element_set_state (bin, GST_STATE_PAUSED);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ /* wait for completion */
+ state_res =
+ gst_element_get_state (GST_ELEMENT (bin), NULL, NULL,
+ GST_CLOCK_TIME_NONE);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ res = gst_element_send_event (bin, seek_event);
+ fail_unless (res == TRUE, NULL);
+
+ /* run pipeline */
+ state_res = gst_element_set_state (bin, GST_STATE_PLAYING);
+ ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE);
+
+ GST_INFO ("running main loop");
+ g_main_loop_run (main_loop);
+
+ state_res = gst_element_set_state (bin, GST_STATE_NULL);
+
+ /* cleanup */
+ g_main_loop_unref (main_loop);
+ gst_bus_remove_signal_watch (bus);
+ gst_object_unref (bus);
+ gst_object_unref (bin);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_flush_start_flush_stop)
+{
+ GstPadTemplate *sink_template;
+ GstPad *tmppad, *sinkpad1, *sinkpad2, *videomixer_src;
+ GstElement *pipeline, *src1, *src2, *videomixer, *sink;
+
+ GST_INFO ("preparing test");
+
+ /* build pipeline */
+ pipeline = gst_pipeline_new ("pipeline");
+ src1 = gst_element_factory_make ("videotestsrc", "src1");
+ src2 = gst_element_factory_make ("videotestsrc", "src2");
+ videomixer = gst_element_factory_make ("videomixer", "videomixer");
+ sink = gst_element_factory_make ("fakesink", "sink");
+ gst_bin_add_many (GST_BIN (pipeline), src1, src2, videomixer, sink, NULL);
+
+ sink_template =
+ gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (videomixer),
+ "sink_%u");
+ fail_unless (GST_IS_PAD_TEMPLATE (sink_template));
+ sinkpad1 = gst_element_request_pad (videomixer, sink_template, NULL, NULL);
+ tmppad = gst_element_get_static_pad (src1, "src");
+ gst_pad_link (tmppad, sinkpad1);
+ gst_object_unref (tmppad);
+
+ sinkpad2 = gst_element_request_pad (videomixer, sink_template, NULL, NULL);
+ tmppad = gst_element_get_static_pad (src2, "src");
+ gst_pad_link (tmppad, sinkpad2);
+ gst_object_unref (tmppad);
+
+ gst_element_link (videomixer, sink);
+
+ gst_element_set_state (pipeline, GST_STATE_PLAYING);
+ fail_unless (gst_element_get_state (pipeline, NULL, NULL,
+ GST_CLOCK_TIME_NONE) == GST_STATE_CHANGE_SUCCESS);
+
+ videomixer_src = gst_element_get_static_pad (videomixer, "src");
+ fail_if (GST_PAD_IS_FLUSHING (videomixer_src));
+ gst_pad_send_event (sinkpad1, gst_event_new_flush_start ());
+ fail_unless (GST_PAD_IS_FLUSHING (videomixer_src));
+ gst_pad_send_event (sinkpad1, gst_event_new_flush_stop (TRUE));
+ fail_if (GST_PAD_IS_FLUSHING (videomixer_src));
+ gst_object_unref (videomixer_src);
+
+ /* cleanup */
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+ gst_object_unref (sinkpad1);
+ gst_object_unref (sinkpad2);
+ gst_object_unref (pipeline);
+}
+
+GST_END_TEST;
+
+
+static Suite *
+videomixer_suite (void)
+{
+ Suite *s = suite_create ("videomixer");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_caps);
+ tcase_add_test (tc_chain, test_event);
+ tcase_add_test (tc_chain, test_play_twice);
+ tcase_add_test (tc_chain, test_play_twice_then_add_and_play_again);
+ tcase_add_test (tc_chain, test_add_pad);
+ tcase_add_test (tc_chain, test_remove_pad);
+ tcase_add_test (tc_chain, test_clip);
+ tcase_add_test (tc_chain, test_duration_is_max);
+ tcase_add_test (tc_chain, test_duration_unknown_overrides);
+ tcase_add_test (tc_chain, test_loop);
+ tcase_add_test (tc_chain, test_flush_start_flush_stop);
+
+ /* Use a longer timeout */
+#ifdef HAVE_VALGRIND
+ if (RUNNING_ON_VALGRIND) {
+ tcase_set_timeout (tc_chain, 5 * 60);
+ } else
+#endif
+ {
+ /* this is shorter than the default 60 seconds?! (tpm) */
+ /* tcase_set_timeout (tc_chain, 6); */
+ }
+
+ return s;
+}
+
+GST_CHECK_MAIN (videomixer);
diff --git a/tests/check/elements/vp8dec.c b/tests/check/elements/vp8dec.c
new file mode 100644
index 0000000..786809e
--- /dev/null
+++ b/tests/check/elements/vp8dec.c
@@ -0,0 +1,178 @@
+/* GStreamer
+ *
+ * Copyright (c) 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ *
+ * 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.
+ */
+
+#include <gst/check/gstcheck.h>
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("video/x-raw, "
+ "format = (string) I420, "
+ "width = (int) [1, MAX], "
+ "height = (int) [1, MAX], " "framerate = (fraction) [0, MAX]"));
+
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("video/x-raw, "
+ "format = (string) I420, "
+ "width = (int) [1, MAX], "
+ "height = (int) [1, MAX], " "framerate = (fraction) [0, MAX]"));
+
+static GstPad *sinkpad, *srcpad;
+
+static GstElement *
+setup_vp8dec (const gchar * src_caps_str)
+{
+ GstElement *bin;
+ GstElement *vp8enc, *vp8dec;
+ GstCaps *srccaps = NULL;
+ GstBus *bus;
+ GstPad *ghostpad, *targetpad;
+
+ if (src_caps_str) {
+ srccaps = gst_caps_from_string (src_caps_str);
+ fail_unless (srccaps != NULL);
+ }
+
+ bin = gst_bin_new ("bin");
+
+ vp8enc = gst_check_setup_element ("vp8enc");
+ fail_unless (vp8enc != NULL);
+ vp8dec = gst_check_setup_element ("vp8dec");
+ fail_unless (vp8dec != NULL);
+
+ gst_bin_add_many (GST_BIN (bin), vp8enc, vp8dec, NULL);
+ fail_unless (gst_element_link_pads (vp8enc, "src", vp8dec, "sink"));
+
+ targetpad = gst_element_get_static_pad (vp8enc, "sink");
+ fail_unless (targetpad != NULL);
+ ghostpad = gst_ghost_pad_new ("sink", targetpad);
+ fail_unless (ghostpad != NULL);
+ gst_element_add_pad (bin, ghostpad);
+ gst_object_unref (targetpad);
+
+ targetpad = gst_element_get_static_pad (vp8dec, "src");
+ fail_unless (targetpad != NULL);
+ ghostpad = gst_ghost_pad_new ("src", targetpad);
+ fail_unless (ghostpad != NULL);
+ gst_element_add_pad (bin, ghostpad);
+ gst_object_unref (targetpad);
+
+ srcpad = gst_check_setup_src_pad (bin, &srctemplate);
+ sinkpad = gst_check_setup_sink_pad (bin, &sinktemplate);
+ gst_pad_set_active (srcpad, TRUE);
+ gst_pad_set_active (sinkpad, TRUE);
+ gst_check_setup_events (srcpad, bin, srccaps, GST_FORMAT_TIME);
+
+ bus = gst_bus_new ();
+ gst_element_set_bus (bin, bus);
+
+ fail_unless (gst_element_set_state (bin,
+ GST_STATE_PLAYING) != GST_STATE_CHANGE_FAILURE,
+ "could not set to playing");
+
+ if (srccaps)
+ gst_caps_unref (srccaps);
+
+ buffers = NULL;
+ return bin;
+}
+
+static void
+cleanup_vp8dec (GstElement * bin)
+{
+ GstBus *bus;
+
+ /* Free parsed buffers */
+ gst_check_drop_buffers ();
+
+ bus = GST_ELEMENT_BUS (bin);
+ gst_bus_set_flushing (bus, TRUE);
+ gst_object_unref (bus);
+
+ gst_pad_set_active (srcpad, FALSE);
+ gst_pad_set_active (sinkpad, FALSE);
+
+ gst_check_teardown_src_pad (bin);
+ gst_check_teardown_sink_pad (bin);
+ gst_check_teardown_element (bin);
+}
+
+GST_START_TEST (test_decode_simple)
+{
+ GstElement *bin;
+ GstBuffer *buffer;
+ gint i;
+ GList *l;
+ GstSegment seg;
+
+ bin =
+ setup_vp8dec
+ ("video/x-raw,format=(string)I420,width=(int)320,height=(int)240,framerate=(fraction)25/1");
+
+ gst_segment_init (&seg, GST_FORMAT_TIME);
+ seg.stop = gst_util_uint64_scale (20, GST_SECOND, 25);
+ fail_unless (gst_pad_push_event (srcpad, gst_event_new_segment (&seg)));
+
+ buffer = gst_buffer_new_and_alloc (320 * 240 + 2 * 160 * 120);
+ gst_buffer_memset (buffer, 0, 0, -1);
+
+ for (i = 0; i < 20; i++) {
+ GST_BUFFER_TIMESTAMP (buffer) = gst_util_uint64_scale (i, GST_SECOND, 25);
+ GST_BUFFER_DURATION (buffer) = gst_util_uint64_scale (1, GST_SECOND, 25);
+ fail_unless (gst_pad_push (srcpad, gst_buffer_ref (buffer)) == GST_FLOW_OK);
+ }
+
+ gst_buffer_unref (buffer);
+
+ fail_unless (gst_pad_push_event (srcpad, gst_event_new_eos ()));
+
+ /* All buffers must be there now */
+ fail_unless_equals_int (g_list_length (buffers), 20);
+
+ for (l = buffers, i = 0; l; l = l->next, i++) {
+ buffer = l->data;
+
+ fail_unless_equals_uint64 (GST_BUFFER_TIMESTAMP (buffer),
+ gst_util_uint64_scale (i, GST_SECOND, 25));
+ fail_unless_equals_uint64 (GST_BUFFER_DURATION (buffer),
+ gst_util_uint64_scale (1, GST_SECOND, 25));
+ }
+
+ cleanup_vp8dec (bin);
+}
+
+GST_END_TEST;
+
+static Suite *
+vp8dec_suite (void)
+{
+ Suite *s = suite_create ("vp8dec");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_decode_simple);
+
+ return s;
+}
+
+GST_CHECK_MAIN (vp8dec);
diff --git a/tests/check/elements/vp8enc.c b/tests/check/elements/vp8enc.c
new file mode 100644
index 0000000..86b1d92
--- /dev/null
+++ b/tests/check/elements/vp8enc.c
@@ -0,0 +1,168 @@
+/* GStreamer
+ *
+ * Copyright (c) 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ *
+ * 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.
+ */
+
+#include <gst/check/gstcheck.h>
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("video/x-vp8, "
+ "width = (int) [1, MAX], "
+ "height = (int) [1, MAX], " "framerate = (fraction) [0, MAX]"));
+
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("video/x-raw, "
+ "format = (string) I420, "
+ "width = (int) [1, MAX], "
+ "height = (int) [1, MAX], " "framerate = (fraction) [0, MAX]"));
+
+static GstPad *sinkpad, *srcpad;
+
+static GstElement *
+setup_vp8enc (const gchar * src_caps_str)
+{
+ GstElement *vp8enc;
+ GstCaps *srccaps = NULL;
+ GstBus *bus;
+
+ if (src_caps_str) {
+ srccaps = gst_caps_from_string (src_caps_str);
+ fail_unless (srccaps != NULL);
+ }
+
+ vp8enc = gst_check_setup_element ("vp8enc");
+ fail_unless (vp8enc != NULL);
+ srcpad = gst_check_setup_src_pad (vp8enc, &srctemplate);
+ sinkpad = gst_check_setup_sink_pad (vp8enc, &sinktemplate);
+ gst_pad_set_active (srcpad, TRUE);
+ gst_pad_set_active (sinkpad, TRUE);
+ gst_check_setup_events (srcpad, vp8enc, srccaps, GST_FORMAT_TIME);
+
+ bus = gst_bus_new ();
+ gst_element_set_bus (vp8enc, bus);
+
+ fail_unless (gst_element_set_state (vp8enc,
+ GST_STATE_PLAYING) != GST_STATE_CHANGE_FAILURE,
+ "could not set to playing");
+
+ if (srccaps)
+ gst_caps_unref (srccaps);
+
+ buffers = NULL;
+ return vp8enc;
+}
+
+static void
+cleanup_vp8enc (GstElement * vp8enc)
+{
+ GstBus *bus;
+
+ /* Free parsed buffers */
+ gst_check_drop_buffers ();
+
+ bus = GST_ELEMENT_BUS (vp8enc);
+ gst_bus_set_flushing (bus, TRUE);
+ gst_object_unref (bus);
+
+ gst_pad_set_active (srcpad, FALSE);
+ gst_pad_set_active (sinkpad, FALSE);
+ gst_check_teardown_src_pad (vp8enc);
+ gst_check_teardown_sink_pad (vp8enc);
+ gst_check_teardown_element (vp8enc);
+}
+
+GST_START_TEST (test_encode_simple)
+{
+ GstElement *vp8enc;
+ GstBuffer *buffer;
+ gint i;
+ GList *l;
+ GstCaps *outcaps;
+ GstSegment seg;
+
+ vp8enc =
+ setup_vp8enc
+ ("video/x-raw,format=(string)I420,width=(int)320,height=(int)240,framerate=(fraction)25/1");
+
+ g_object_set (vp8enc, "lag-in-frames", 5, NULL);
+
+ gst_segment_init (&seg, GST_FORMAT_TIME);
+ seg.stop = gst_util_uint64_scale (20, GST_SECOND, 25);
+ fail_unless (gst_pad_push_event (srcpad, gst_event_new_segment (&seg)));
+
+ buffer = gst_buffer_new_and_alloc (320 * 240 + 2 * 160 * 120);
+ gst_buffer_memset (buffer, 0, 0, -1);
+
+ for (i = 0; i < 20; i++) {
+ GST_BUFFER_TIMESTAMP (buffer) = gst_util_uint64_scale (i, GST_SECOND, 25);
+ GST_BUFFER_DURATION (buffer) = gst_util_uint64_scale (1, GST_SECOND, 25);
+ fail_unless (gst_pad_push (srcpad, gst_buffer_ref (buffer)) == GST_FLOW_OK);
+ }
+
+ gst_buffer_unref (buffer);
+
+ /* Only 5 buffers are allowed to be queued now */
+ fail_unless (g_list_length (buffers) > 15);
+
+ fail_unless (gst_pad_push_event (srcpad, gst_event_new_eos ()));
+
+
+ /* All buffers must be there now */
+ fail_unless_equals_int (g_list_length (buffers), 20);
+
+ outcaps =
+ gst_caps_from_string
+ ("video/x-vp8,width=(int)320,height=(int)240,framerate=(fraction)25/1");
+
+ for (l = buffers, i = 0; l; l = l->next, i++) {
+ buffer = l->data;
+
+ if (i == 0)
+ fail_if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT));
+
+ fail_unless_equals_uint64 (GST_BUFFER_TIMESTAMP (buffer),
+ gst_util_uint64_scale (i, GST_SECOND, 25));
+ fail_unless_equals_uint64 (GST_BUFFER_DURATION (buffer),
+ gst_util_uint64_scale (1, GST_SECOND, 25));
+ }
+
+ gst_caps_unref (outcaps);
+
+ cleanup_vp8enc (vp8enc);
+}
+
+GST_END_TEST;
+
+static Suite *
+vp8enc_suite (void)
+{
+ Suite *s = suite_create ("vp8enc");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+
+ tcase_add_test (tc_chain, test_encode_simple);
+
+ return s;
+}
+
+GST_CHECK_MAIN (vp8enc);
diff --git a/tests/check/elements/wavpackdec.c b/tests/check/elements/wavpackdec.c
new file mode 100755
index 0000000..9b30db5
--- /dev/null
+++ b/tests/check/elements/wavpackdec.c
@@ -0,0 +1,258 @@
+/* GStreamer
+ *
+ * unit test for wavpackdec
+ *
+ * Copyright (c) 2006 Sebastian Dröge <slomo@circular-chaos.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.
+ */
+
+#include <unistd.h>
+
+#include <gst/check/gstcheck.h>
+
+/* 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;
+
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+#define AUDIO_FORMAT "S16BE"
+#else
+#define AUDIO_FORMAT "S16LE"
+#endif
+
+guint8 test_frame[] = {
+ 0x77, 0x76, 0x70, 0x6B, /* "wvpk" */
+ 0x2E, 0x00, 0x00, 0x00, /* ckSize */
+ 0x04, 0x04, /* version */
+ 0x00, /* track_no */
+ 0x00, /* index_no */
+ 0x00, 0x64, 0x00, 0x00, /* total_samples */
+ 0x00, 0x00, 0x00, 0x00, /* block_index */
+ 0x00, 0x64, 0x00, 0x00, /* block_samples */
+ 0x05, 0x18, 0x80, 0x04, /* flags */
+ 0xFF, 0xAF, 0x80, 0x60, /* crc */
+ 0x02, 0x00, 0x03, 0x00, /* data */
+ 0x04, 0x00, 0x05, 0x03,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x8A, 0x02,
+ 0x00, 0x00, 0xFF, 0x7F,
+ 0x00, 0xE4,
+};
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "format = (string) " AUDIO_FORMAT ", "
+ "layout = (string) interleaved, "
+ "channels = (int) 1, " "rate = (int) 44100")
+ );
+
+#define WAVPACK_CAPS "audio/x-wavpack, " \
+ "depth = (int) 16, " \
+ "channels = (int) 1, " "rate = (int) 44100, " "framed = (boolean) true"
+
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (WAVPACK_CAPS)
+ );
+
+static GstElement *
+setup_wavpackdec (void)
+{
+ GstElement *wavpackdec;
+ GstCaps *caps;
+
+ GST_DEBUG ("setup_wavpackdec");
+ wavpackdec = gst_check_setup_element ("wavpackdec");
+ mysrcpad = gst_check_setup_src_pad (wavpackdec, &srctemplate);
+ mysinkpad = gst_check_setup_sink_pad (wavpackdec, &sinktemplate);
+ gst_pad_set_active (mysrcpad, TRUE);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ caps = gst_caps_from_string (WAVPACK_CAPS);
+ gst_check_setup_events (mysrcpad, wavpackdec, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+
+ fail_unless (gst_element_set_state (wavpackdec,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ return wavpackdec;
+}
+
+static void
+cleanup_wavpackdec (GstElement * wavpackdec)
+{
+ GST_DEBUG ("cleanup_wavpackdec");
+ gst_element_set_state (wavpackdec, GST_STATE_NULL);
+
+ gst_pad_set_active (mysrcpad, FALSE);
+ gst_pad_set_active (mysinkpad, FALSE);
+ gst_check_teardown_src_pad (wavpackdec);
+ gst_check_teardown_sink_pad (wavpackdec);
+ gst_check_teardown_element (wavpackdec);
+}
+
+GST_START_TEST (test_decode_frame)
+{
+ GstElement *wavpackdec;
+ GstBuffer *inbuffer, *outbuffer;
+ GstBus *bus;
+ int i;
+ GstMapInfo map;
+
+ wavpackdec = setup_wavpackdec ();
+ bus = gst_bus_new ();
+
+ inbuffer = gst_buffer_new_and_alloc (sizeof (test_frame));
+ gst_buffer_fill (inbuffer, 0, test_frame, sizeof (test_frame));
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+
+ gst_element_set_bus (wavpackdec, bus);
+
+ /* should decode the buffer without problems */
+ fail_unless_equals_int (gst_pad_push (mysrcpad, inbuffer), GST_FLOW_OK);
+
+ outbuffer = GST_BUFFER (buffers->data);
+
+ fail_if (outbuffer == NULL);
+
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+
+ /* uncompressed data should be 102400 bytes */
+ fail_unless_equals_int (map.size, 51200);
+
+ /* and all bytes must be 0, i.e. silence */
+ for (i = 0; i < 51200; i++)
+ fail_unless_equals_int (map.data[i], 0);
+
+ gst_buffer_unmap (outbuffer, &map);
+
+ ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
+ gst_buffer_unref (outbuffer);
+ outbuffer = NULL;
+
+ g_list_free (buffers);
+ buffers = NULL;
+
+ gst_bus_set_flushing (bus, TRUE);
+ gst_element_set_bus (wavpackdec, NULL);
+ gst_object_unref (GST_OBJECT (bus));
+ cleanup_wavpackdec (wavpackdec);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_decode_frame_with_broken_header)
+{
+ GstElement *wavpackdec;
+ GstBuffer *inbuffer;
+ GstBus *bus;
+ GstMessage *message;
+
+ wavpackdec = setup_wavpackdec ();
+ bus = gst_bus_new ();
+
+ inbuffer = gst_buffer_new_and_alloc (sizeof (test_frame));
+ gst_buffer_fill (inbuffer, 0, test_frame, sizeof (test_frame));
+ /* break header */
+ gst_buffer_memset (inbuffer, 2, 'e', 1);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+
+ gst_element_set_bus (wavpackdec, bus);
+
+ /* should fail gracefully */
+ fail_unless_equals_int (gst_pad_push (mysrcpad, inbuffer), GST_FLOW_ERROR);
+
+ fail_if ((message = gst_bus_pop (bus)) == NULL);
+ fail_unless_message_error (message, STREAM, DECODE);
+ gst_message_unref (message);
+
+ gst_element_set_bus (wavpackdec, NULL);
+ gst_object_unref (GST_OBJECT (bus));
+ cleanup_wavpackdec (wavpackdec);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_decode_frame_with_incomplete_frame)
+{
+ GstElement *wavpackdec;
+ GstBuffer *inbuffer;
+ GstBus *bus;
+ GstMessage *message;
+
+ wavpackdec = setup_wavpackdec ();
+ bus = gst_bus_new ();
+
+ inbuffer = gst_buffer_new_and_alloc (sizeof (test_frame) - 2);
+ gst_buffer_fill (inbuffer, 0, test_frame, sizeof (test_frame) - 2);
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+
+ gst_element_set_bus (wavpackdec, bus);
+
+ /* should fail gracefully */
+ fail_unless_equals_int (gst_pad_push (mysrcpad, inbuffer), GST_FLOW_ERROR);
+
+ fail_if ((message = gst_bus_pop (bus)) == NULL);
+ fail_unless_message_error (message, STREAM, DECODE);
+ gst_message_unref (message);
+
+
+ gst_element_set_bus (wavpackdec, NULL);
+ gst_object_unref (GST_OBJECT (bus));
+ cleanup_wavpackdec (wavpackdec);
+}
+
+GST_END_TEST;
+
+static Suite *
+wavpackdec_suite (void)
+{
+ Suite *s = suite_create ("wavpackdec");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_decode_frame);
+ tcase_add_test (tc_chain, test_decode_frame_with_broken_header);
+ tcase_add_test (tc_chain, test_decode_frame_with_incomplete_frame);
+
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ int nf;
+
+ Suite *s = wavpackdec_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ srunner_run_all (sr, CK_NORMAL);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}
diff --git a/tests/check/elements/wavpackenc.c b/tests/check/elements/wavpackenc.c
new file mode 100644
index 0000000..22e2e7c
--- /dev/null
+++ b/tests/check/elements/wavpackenc.c
@@ -0,0 +1,197 @@
+/* GStreamer
+ *
+ * unit test for wavpackenc
+ *
+ * Copyright (c) 2006 Sebastian Dröge <slomo@circular-chaos.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.
+ */
+
+#include <unistd.h>
+
+#include <gst/check/gstcheck.h>
+
+/* 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;
+static GstBus *bus;
+
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+#define AUDIO_FORMAT "S32BE"
+#else
+#define AUDIO_FORMAT "S32LE"
+#endif
+
+#define RAW_CAPS_STRING "audio/x-raw, " \
+ "format = (string) " AUDIO_FORMAT ", " \
+ "layout = (string) interleaved, " \
+ "channels = (int) 1, " \
+ "rate = (int) 44100"
+
+#define WAVPACK_CAPS_STRING "audio/x-wavpack, " \
+ "depth = (int) 32, " \
+ "channels = (int) 1, " \
+ "rate = (int) 44100, " \
+ "framed = (boolean) true"
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-wavpack, "
+ "depth = (int) 32, "
+ "channels = (int) 1, "
+ "rate = (int) 44100, " "framed = (boolean) true"));
+
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "format = (string) " AUDIO_FORMAT ", "
+ "layout = (string) interleaved, "
+ "channels = (int) 1, " "rate = (int) 44100"));
+
+static GstElement *
+setup_wavpackenc (void)
+{
+ GstElement *wavpackenc;
+
+ GST_DEBUG ("setup_wavpackenc");
+ wavpackenc = gst_check_setup_element ("wavpackenc");
+ mysrcpad = gst_check_setup_src_pad (wavpackenc, &srctemplate);
+ mysinkpad = gst_check_setup_sink_pad (wavpackenc, &sinktemplate);
+ gst_pad_set_active (mysrcpad, TRUE);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ fail_unless (gst_element_set_state (wavpackenc,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+ bus = gst_bus_new ();
+
+ gst_pad_push_event (mysrcpad, gst_event_new_stream_start ("test-silence"));
+
+ return wavpackenc;
+}
+
+static void
+cleanup_wavpackenc (GstElement * wavpackenc)
+{
+ GST_DEBUG ("cleanup_wavpackenc");
+
+ gst_bus_set_flushing (bus, TRUE);
+ gst_element_set_bus (wavpackenc, NULL);
+ gst_object_unref (GST_OBJECT (bus));
+
+ gst_element_set_state (wavpackenc, GST_STATE_NULL);
+
+ gst_pad_set_active (mysrcpad, FALSE);
+ gst_pad_set_active (mysinkpad, FALSE);
+ gst_check_teardown_src_pad (wavpackenc);
+ gst_check_teardown_sink_pad (wavpackenc);
+ gst_check_teardown_element (wavpackenc);
+}
+
+GST_START_TEST (test_encode_silence)
+{
+ GstSegment segment;
+ GstElement *wavpackenc;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ GstEvent *eos = gst_event_new_eos ();
+ int i, num_buffers;
+
+ wavpackenc = setup_wavpackenc ();
+
+ gst_segment_init (&segment, GST_FORMAT_TIME);
+
+ inbuffer = gst_buffer_new_and_alloc (1000);
+ gst_buffer_memset (inbuffer, 0, 0, 1000);
+
+ caps = gst_caps_from_string (RAW_CAPS_STRING);
+ fail_unless (gst_pad_set_caps (mysrcpad, caps));
+ gst_caps_unref (caps);
+
+ gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment));
+
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+
+ gst_element_set_bus (wavpackenc, bus);
+
+ fail_unless_equals_int (gst_pad_push (mysrcpad, inbuffer), GST_FLOW_OK);
+
+ fail_if (gst_pad_push_event (mysrcpad, eos) != TRUE);
+
+ /* check first buffer */
+ outbuffer = GST_BUFFER (buffers->data);
+
+ fail_if (outbuffer == NULL);
+
+ fail_unless_equals_int (GST_BUFFER_TIMESTAMP (outbuffer), 0);
+ fail_unless_equals_int (GST_BUFFER_DURATION (outbuffer), 5668934);
+
+ fail_unless (gst_buffer_memcmp (outbuffer, 0, "wvpk", 4) == 0,
+ "Failed to encode to valid Wavpack frames");
+
+ /* free all buffers */
+ num_buffers = g_list_length (buffers);
+
+ for (i = 0; i < num_buffers; ++i) {
+ outbuffer = GST_BUFFER (buffers->data);
+ fail_if (outbuffer == NULL);
+
+ buffers = g_list_remove (buffers, outbuffer);
+
+ gst_buffer_unref (outbuffer);
+ outbuffer = NULL;
+ }
+
+ g_list_free (buffers);
+ buffers = NULL;
+
+ cleanup_wavpackenc (wavpackenc);
+}
+
+GST_END_TEST;
+
+static Suite *
+wavpackenc_suite (void)
+{
+ Suite *s = suite_create ("wavpackenc");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_encode_silence);
+
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ int nf;
+
+ Suite *s = wavpackenc_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ srunner_run_all (sr, CK_NORMAL);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}
diff --git a/tests/check/elements/wavpackparse.c b/tests/check/elements/wavpackparse.c
new file mode 100644
index 0000000..ec40909
--- /dev/null
+++ b/tests/check/elements/wavpackparse.c
@@ -0,0 +1,243 @@
+/* GStreamer
+ *
+ * unit test for wavpackparse
+ *
+ * Copyright (c) 2006 Sebastian Dröge <slomo@circular-chaos.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.
+ */
+
+#include <unistd.h>
+
+#include <gst/check/gstcheck.h>
+
+/* 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;
+
+/* Wavpack file with 2 frames of silence */
+guint8 test_file[] = {
+ 0x77, 0x76, 0x70, 0x6B, 0x62, 0x00, 0x00, 0x00, /* first frame */
+ 0x04, 0x04, 0x00, 0x00, 0x00, 0xC8, 0x00, 0x00, /* include RIFF header */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00,
+ 0x05, 0x18, 0x80, 0x04, 0xFF, 0xAF, 0x80, 0x60,
+ 0x21, 0x16, 0x52, 0x49, 0x46, 0x46, 0x24, 0x90,
+ 0x01, 0x00, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6D,
+ 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x44, 0xAC, 0x00, 0x00, 0x88, 0x58,
+ 0x01, 0x00, 0x02, 0x00, 0x10, 0x00, 0x64, 0x61,
+ 0x74, 0x61, 0x00, 0x90, 0x01, 0x00, 0x02, 0x00,
+ 0x03, 0x00, 0x04, 0x00, 0x05, 0x03, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x65, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x8A, 0x02, 0x00, 0x00, 0xFF, 0x7F,
+ 0x00, 0xE4,
+ 0x77, 0x76, 0x70, 0x6B, 0x2E, 0x00, 0x00, 0x00, /* second frame */
+ 0x04, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x64, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00,
+ 0x05, 0x18, 0x80, 0x04, 0xFF, 0xAF, 0x80, 0x60,
+ 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8A, 0x02,
+ 0x00, 0x00, 0xFF, 0x7F, 0x00, 0xE4,
+};
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-wavpack, "
+ "depth = (int) 16, "
+ "channels = (int) 1, "
+ "rate = (int) 44100, " "framed = (boolean) TRUE"));
+
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-wavpack"));
+
+static GstElement *
+setup_wavpackparse (void)
+{
+ GstElement *wavpackparse;
+
+ GST_DEBUG ("setup_wavpackparse");
+
+ wavpackparse = gst_check_setup_element ("wavpackparse");
+ mysrcpad = gst_check_setup_src_pad (wavpackparse, &srctemplate);
+ mysinkpad = gst_check_setup_sink_pad (wavpackparse, &sinktemplate);
+ gst_pad_set_active (mysrcpad, TRUE);
+ gst_pad_set_active (mysinkpad, TRUE);
+ gst_check_setup_events (mysrcpad, wavpackparse, NULL, GST_FORMAT_BYTES);
+
+ return wavpackparse;
+}
+
+static void
+cleanup_wavpackparse (GstElement * wavpackparse)
+{
+ GST_DEBUG ("cleanup_wavpackparse");
+ gst_element_set_state (wavpackparse, GST_STATE_NULL);
+
+ gst_pad_set_active (mysrcpad, FALSE);
+ gst_pad_set_active (mysinkpad, FALSE);
+ gst_check_teardown_src_pad (wavpackparse);
+ gst_check_teardown_sink_pad (wavpackparse);
+ gst_check_teardown_element (wavpackparse);
+}
+
+GST_START_TEST (test_parsing_valid_frames)
+{
+ GstElement *wavpackparse;
+ GstBuffer *inbuffer, *outbuffer;
+ int i, num_buffers;
+ GstFormat format = GST_FORMAT_TIME;
+ gint64 pos;
+
+ wavpackparse = setup_wavpackparse ();
+ fail_unless (gst_element_set_state (wavpackparse,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ inbuffer = gst_buffer_new_and_alloc (sizeof (test_file));
+ gst_buffer_fill (inbuffer, 0, test_file, sizeof (test_file));
+
+ /* should decode the buffer without problems */
+ fail_unless_equals_int (gst_pad_push (mysrcpad, inbuffer), GST_FLOW_OK);
+
+ num_buffers = g_list_length (buffers);
+ /* should get 2 buffers, each one complete wavpack frame */
+ fail_unless_equals_int (num_buffers, 2);
+
+ for (i = 0; i < num_buffers; ++i) {
+ outbuffer = GST_BUFFER (buffers->data);
+ fail_if (outbuffer == NULL);
+
+ fail_unless (gst_buffer_memcmp (outbuffer, 0, "wvpk", 4) == 0,
+ "Buffer contains no Wavpack frame");
+ fail_unless_equals_int (GST_BUFFER_DURATION (outbuffer), 580498866);
+
+ switch (i) {
+ case 0:{
+ fail_unless_equals_int (GST_BUFFER_TIMESTAMP (outbuffer), 0);
+ break;
+ }
+ case 1:{
+ fail_unless_equals_int (GST_BUFFER_TIMESTAMP (outbuffer), 580498866);
+ break;
+ }
+ }
+
+ buffers = g_list_remove (buffers, outbuffer);
+
+ gst_buffer_unref (outbuffer);
+ outbuffer = NULL;
+ }
+
+ fail_unless (gst_element_query_position (wavpackparse, format, &pos),
+ "Position query failed");
+ fail_unless_equals_int64 (pos, 580498866 * 2);
+ fail_unless (gst_element_query_duration (wavpackparse, format, NULL),
+ "Duration query failed");
+
+ g_list_free (buffers);
+ buffers = NULL;
+
+ cleanup_wavpackparse (wavpackparse);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_parsing_invalid_first_header)
+{
+ GstElement *wavpackparse;
+ GstBuffer *inbuffer, *outbuffer;
+ int i, num_buffers;
+
+ wavpackparse = setup_wavpackparse ();
+ fail_unless (gst_element_set_state (wavpackparse,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ inbuffer = gst_buffer_new_and_alloc (sizeof (test_file));
+ gst_buffer_fill (inbuffer, 0, test_file, sizeof (test_file));
+ gst_buffer_memset (inbuffer, 0, 'k', 1);
+
+ /* should decode the buffer without problems */
+ fail_unless_equals_int (gst_pad_push (mysrcpad, inbuffer), GST_FLOW_OK);
+
+ num_buffers = g_list_length (buffers);
+
+ /* should get 1 buffers, the second non-broken one */
+ fail_unless_equals_int (num_buffers, 1);
+
+ for (i = 0; i < num_buffers; ++i) {
+ outbuffer = GST_BUFFER (buffers->data);
+ fail_if (outbuffer == NULL);
+
+ fail_unless (gst_buffer_memcmp (outbuffer, 0, "wvpk", 4) == 0,
+ "Buffer contains no Wavpack frame");
+ fail_unless_equals_int (GST_BUFFER_DURATION (outbuffer), 580498866);
+
+ switch (i) {
+ case 0:{
+ fail_unless_equals_int (GST_BUFFER_TIMESTAMP (outbuffer), 580498866);
+ break;
+ }
+ }
+
+ buffers = g_list_remove (buffers, outbuffer);
+
+ gst_buffer_unref (outbuffer);
+ outbuffer = NULL;
+ }
+
+ g_list_free (buffers);
+ buffers = NULL;
+
+ cleanup_wavpackparse (wavpackparse);
+}
+
+GST_END_TEST;
+
+
+static Suite *
+wavpackparse_suite (void)
+{
+ Suite *s = suite_create ("wavpackparse");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_parsing_valid_frames);
+ tcase_add_test (tc_chain, test_parsing_invalid_first_header);
+
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ int nf;
+
+ Suite *s = wavpackparse_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ srunner_run_all (sr, CK_NORMAL);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}
diff --git a/tests/check/elements/wavparse.c b/tests/check/elements/wavparse.c
new file mode 100755
index 0000000..10e2ea7
--- /dev/null
+++ b/tests/check/elements/wavparse.c
@@ -0,0 +1,87 @@
+/* GStreamer WavParse unit tests
+ *
+ * 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.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/check/gstcheck.h>
+
+static void
+do_test_empty_file (gboolean can_activate_pull)
+{
+ GstStateChangeReturn ret1, ret2;
+ GstElement *pipeline;
+ GstElement *src;
+ GstElement *wavparse;
+ GstElement *fakesink;
+
+ /* Pull mode */
+ pipeline = gst_pipeline_new ("testpipe");
+ src = gst_element_factory_make ("fakesrc", NULL);
+ fail_if (src == NULL);
+ wavparse = gst_element_factory_make ("wavparse", NULL);
+ fail_if (wavparse == NULL);
+ fakesink = gst_element_factory_make ("fakesink", NULL);
+ fail_if (fakesink == NULL);
+
+ gst_bin_add_many (GST_BIN (pipeline), src, wavparse, fakesink, NULL);
+ g_object_set (src, "num-buffers", 0, "can-activate-pull", can_activate_pull,
+ NULL);
+
+ fail_unless (gst_element_link_many (src, wavparse, fakesink, NULL));
+
+ ret1 = gst_element_set_state (pipeline, GST_STATE_PLAYING);
+ if (ret1 == GST_STATE_CHANGE_ASYNC)
+ ret2 = gst_element_get_state (pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
+ else
+ ret2 = ret1;
+
+ /* should have gotten an error on the bus, no output to fakesink */
+ fail_unless_equals_int (ret2, GST_STATE_CHANGE_FAILURE);
+
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+ gst_object_unref (pipeline);
+}
+
+GST_START_TEST (test_empty_file_pull)
+{
+ do_test_empty_file (TRUE);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_empty_file_push)
+{
+ do_test_empty_file (FALSE);
+}
+
+GST_END_TEST;
+
+static Suite *
+wavparse_suite (void)
+{
+ Suite *s = suite_create ("wavparse");
+ TCase *tc_chain = tcase_create ("wavparse");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_empty_file_pull);
+ tcase_add_test (tc_chain, test_empty_file_push);
+ return s;
+}
+
+GST_CHECK_MAIN (wavparse)
diff --git a/tests/check/elements/y4menc.c b/tests/check/elements/y4menc.c
new file mode 100644
index 0000000..2117ada
--- /dev/null
+++ b/tests/check/elements/y4menc.c
@@ -0,0 +1,183 @@
+/* GStreamer
+ *
+ * unit test for y4menc
+ *
+ * Copyright (C) <2006> Mark Nauwelaerts <manauw@skynet.be>
+ *
+ * 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.
+ */
+
+#include <unistd.h>
+
+#include <gst/check/gstcheck.h>
+
+/* 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;
+
+#define VIDEO_CAPS_STRING "video/x-raw, " \
+ "format = (string) I420, "\
+ "width = (int) 384, " \
+ "height = (int) 288, " \
+ "framerate = (fraction) 25/1, " \
+ "pixel-aspect-ratio = (fraction) 1/1"
+
+#define Y4M_CAPS_STRING "application/x-yuv4mpeg, " \
+ "y4mversion = (int) 2"
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (Y4M_CAPS_STRING));
+
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS (VIDEO_CAPS_STRING));
+
+
+static GstElement *
+setup_y4menc (void)
+{
+ GstElement *y4menc;
+
+ GST_DEBUG ("setup_y4menc");
+ y4menc = gst_check_setup_element ("y4menc");
+ mysrcpad = gst_check_setup_src_pad (y4menc, &srctemplate);
+ mysinkpad = gst_check_setup_sink_pad (y4menc, &sinktemplate);
+ gst_pad_set_active (mysrcpad, TRUE);
+ gst_pad_set_active (mysinkpad, TRUE);
+
+ return y4menc;
+}
+
+static void
+cleanup_y4menc (GstElement * y4menc)
+{
+ GST_DEBUG ("cleanup_y4menc");
+ gst_element_set_state (y4menc, GST_STATE_NULL);
+
+ gst_pad_set_active (mysrcpad, FALSE);
+ gst_pad_set_active (mysinkpad, FALSE);
+ gst_check_teardown_src_pad (y4menc);
+ gst_check_teardown_sink_pad (y4menc);
+ gst_check_teardown_element (y4menc);
+}
+
+GST_START_TEST (test_y4m)
+{
+ GstElement *y4menc;
+ GstBuffer *inbuffer, *outbuffer;
+ GstCaps *caps;
+ int i, num_buffers, size;
+ const gchar *data0 = "YUV4MPEG2 W384 H288 Ip F25:1 A1:1\n";
+ const gchar *data1 = "YUV4MPEG2 C420 W384 H288 Ip F25:1 A1:1\n";
+ const gchar *data2 = "FRAME\n";
+
+ y4menc = setup_y4menc ();
+ fail_unless (gst_element_set_state (y4menc,
+ GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
+ "could not set to playing");
+
+ /* corresponds to I420 buffer for the size mentioned in the caps */
+ size = 384 * 288 * 3 / 2;
+ inbuffer = gst_buffer_new_and_alloc (size);
+ /* makes valgrind's memcheck happier */
+ gst_buffer_memset (inbuffer, 0, 0, size);
+ caps = gst_caps_from_string (VIDEO_CAPS_STRING);
+ gst_check_setup_events (mysrcpad, y4menc, caps, GST_FORMAT_TIME);
+ gst_caps_unref (caps);
+ GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+ ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+ fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+
+ num_buffers = g_list_length (buffers);
+ fail_unless (num_buffers == 1);
+
+ /* clean up buffers */
+ for (i = 0; i < num_buffers; ++i) {
+ GstMapInfo map;
+ gchar *data;
+ gsize outsize;
+
+ outbuffer = GST_BUFFER (buffers->data);
+ fail_if (outbuffer == NULL);
+
+ switch (i) {
+ case 0:
+ gst_buffer_map (outbuffer, &map, GST_MAP_READ);
+ outsize = map.size;
+ data = (gchar *) map.data;
+
+ fail_unless (outsize > size);
+ fail_unless (memcmp (data, data0, strlen (data0)) == 0 ||
+ memcmp (data, data1, strlen (data1)) == 0);
+ /* so we know there is a newline */
+ data = strchr (data, '\n');
+ fail_unless (data != NULL);
+ data++;
+ fail_unless (memcmp (data2, data, strlen (data2)) == 0);
+ data += strlen (data2);
+ /* remainder must be frame data */
+ fail_unless (data - (gchar *) map.data + size == outsize);
+ gst_buffer_unmap (outbuffer, &map);
+ break;
+ default:
+ break;
+ }
+ buffers = g_list_remove (buffers, outbuffer);
+
+ ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
+ gst_buffer_unref (outbuffer);
+ outbuffer = NULL;
+ }
+
+ cleanup_y4menc (y4menc);
+ g_list_free (buffers);
+ buffers = NULL;
+}
+
+GST_END_TEST;
+
+static Suite *
+y4menc_suite (void)
+{
+ Suite *s = suite_create ("y4menc");
+ TCase *tc_chain = tcase_create ("general");
+
+ suite_add_tcase (s, tc_chain);
+ tcase_add_test (tc_chain, test_y4m);
+
+ return s;
+}
+
+int
+main (int argc, char **argv)
+{
+ int nf;
+
+ Suite *s = y4menc_suite ();
+ SRunner *sr = srunner_create (s);
+
+ gst_check_init (&argc, &argv);
+
+ srunner_run_all (sr, CK_NORMAL);
+ nf = srunner_ntests_failed (sr);
+ srunner_free (sr);
+
+ return nf;
+}