mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 11:41:09 +00:00
3e8e0a7065
This property avoids not linked error when all the pads are unlinked or when there are no source pads. This is useful in dynamic pipelines where it can happen that for a short time there are no pads at all or all downstream pads are not linked yet. https://bugzilla.gnome.org/show_bug.cgi?id=746436
723 lines
22 KiB
C
723 lines
22 KiB
C
/* GStreamer
|
|
*
|
|
* unit test for tee
|
|
*
|
|
* Copyright (C) <2007> Wim Taymans <wim dot taymans at gmail dot com>
|
|
* Copyright (C) <2008> Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
|
|
* Copyright (C) <2008> Christian Berentsen <christian.berentsen@tandberg.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 <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
|
|
#include <gst/check/gstcheck.h>
|
|
|
|
static void
|
|
handoff (GstElement * fakesink, GstBuffer * buf, GstPad * pad, guint * count)
|
|
{
|
|
*count = *count + 1;
|
|
}
|
|
|
|
/* construct fakesrc num-buffers=3 ! tee name=t ! queue ! fakesink t. ! queue !
|
|
* fakesink. Each fakesink should exactly receive 3 buffers.
|
|
*/
|
|
GST_START_TEST (test_num_buffers)
|
|
{
|
|
#define NUM_SUBSTREAMS 15
|
|
#define NUM_BUFFERS 3
|
|
GstElement *pipeline, *src, *tee;
|
|
GstElement *queues[NUM_SUBSTREAMS];
|
|
GstElement *sinks[NUM_SUBSTREAMS];
|
|
GstPad *req_pads[NUM_SUBSTREAMS];
|
|
guint counts[NUM_SUBSTREAMS];
|
|
GstBus *bus;
|
|
GstMessage *msg;
|
|
gint i;
|
|
|
|
pipeline = gst_pipeline_new ("pipeline");
|
|
src = gst_check_setup_element ("fakesrc");
|
|
g_object_set (src, "num-buffers", NUM_BUFFERS, NULL);
|
|
tee = gst_check_setup_element ("tee");
|
|
fail_unless (gst_bin_add (GST_BIN (pipeline), src));
|
|
fail_unless (gst_bin_add (GST_BIN (pipeline), tee));
|
|
fail_unless (gst_element_link (src, tee));
|
|
|
|
for (i = 0; i < NUM_SUBSTREAMS; ++i) {
|
|
GstPad *qpad;
|
|
gchar name[32];
|
|
|
|
counts[i] = 0;
|
|
|
|
queues[i] = gst_check_setup_element ("queue");
|
|
g_snprintf (name, 32, "queue%d", i);
|
|
gst_object_set_name (GST_OBJECT (queues[i]), name);
|
|
fail_unless (gst_bin_add (GST_BIN (pipeline), queues[i]));
|
|
|
|
sinks[i] = gst_check_setup_element ("fakesink");
|
|
g_snprintf (name, 32, "sink%d", i);
|
|
gst_object_set_name (GST_OBJECT (sinks[i]), name);
|
|
fail_unless (gst_bin_add (GST_BIN (pipeline), sinks[i]));
|
|
fail_unless (gst_element_link (queues[i], sinks[i]));
|
|
g_object_set (sinks[i], "signal-handoffs", TRUE, NULL);
|
|
g_signal_connect (sinks[i], "handoff", (GCallback) handoff, &counts[i]);
|
|
|
|
req_pads[i] = gst_element_get_request_pad (tee, "src_%u");
|
|
fail_unless (req_pads[i] != NULL);
|
|
|
|
qpad = gst_element_get_static_pad (queues[i], "sink");
|
|
fail_unless_equals_int (gst_pad_link (req_pads[i], qpad), GST_PAD_LINK_OK);
|
|
gst_object_unref (qpad);
|
|
}
|
|
|
|
bus = gst_element_get_bus (pipeline);
|
|
fail_if (bus == NULL);
|
|
gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
|
|
|
msg = gst_bus_poll (bus, GST_MESSAGE_EOS | GST_MESSAGE_ERROR, -1);
|
|
fail_if (GST_MESSAGE_TYPE (msg) != GST_MESSAGE_EOS);
|
|
gst_message_unref (msg);
|
|
|
|
for (i = 0; i < NUM_SUBSTREAMS; ++i) {
|
|
fail_unless_equals_int (counts[i], NUM_BUFFERS);
|
|
}
|
|
|
|
gst_element_set_state (pipeline, GST_STATE_NULL);
|
|
gst_object_unref (bus);
|
|
|
|
for (i = 0; i < NUM_SUBSTREAMS; ++i) {
|
|
gst_element_release_request_pad (tee, req_pads[i]);
|
|
gst_object_unref (req_pads[i]);
|
|
}
|
|
gst_object_unref (pipeline);
|
|
}
|
|
|
|
GST_END_TEST;
|
|
|
|
/* we use fakesrc ! tee ! fakesink and then randomly request/release and link
|
|
* some pads from tee. This should happily run without any errors. */
|
|
GST_START_TEST (test_stress)
|
|
{
|
|
GstElement *pipeline;
|
|
GstElement *tee;
|
|
const gchar *desc;
|
|
GstBus *bus;
|
|
GstMessage *msg;
|
|
gint i;
|
|
|
|
/* Pump 1000 buffers (10 bytes each) per second through tee for 5 secs */
|
|
desc = "fakesrc datarate=10000 sizemin=10 sizemax=10 num-buffers=5000 ! "
|
|
"video/x-raw,framerate=25/1 ! tee name=t ! "
|
|
"queue max-size-buffers=2 ! fakesink sync=true";
|
|
|
|
pipeline = gst_parse_launch (desc, NULL);
|
|
fail_if (pipeline == NULL);
|
|
|
|
tee = gst_bin_get_by_name (GST_BIN (pipeline), "t");
|
|
fail_if (tee == NULL);
|
|
|
|
/* bring the pipeline to PLAYING, then start switching */
|
|
bus = gst_element_get_bus (pipeline);
|
|
fail_if (bus == NULL);
|
|
gst_element_set_state (pipeline, GST_STATE_PLAYING);
|
|
/* Wait for the pipeline to hit playing so that parse_launch can do the
|
|
* initial link, otherwise we perform linking from multiple threads and cause
|
|
* trouble */
|
|
gst_element_get_state (pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
|
|
|
|
for (i = 0; i < 50000; i++) {
|
|
GstPad *pad;
|
|
|
|
pad = gst_element_get_request_pad (tee, "src_%u");
|
|
gst_element_release_request_pad (tee, pad);
|
|
gst_object_unref (pad);
|
|
|
|
if ((msg = gst_bus_poll (bus, GST_MESSAGE_EOS | GST_MESSAGE_ERROR, 0)))
|
|
break;
|
|
}
|
|
|
|
/* now wait for completion or error */
|
|
if (msg == NULL)
|
|
msg = gst_bus_poll (bus, GST_MESSAGE_EOS | GST_MESSAGE_ERROR, -1);
|
|
fail_if (GST_MESSAGE_TYPE (msg) != GST_MESSAGE_EOS);
|
|
gst_message_unref (msg);
|
|
|
|
gst_element_set_state (pipeline, GST_STATE_NULL);
|
|
gst_object_unref (tee);
|
|
gst_object_unref (bus);
|
|
gst_object_unref (pipeline);
|
|
}
|
|
|
|
GST_END_TEST;
|
|
|
|
typedef struct
|
|
{
|
|
GstElement *tee;
|
|
GstCaps *caps;
|
|
GstPad *start_srcpad;
|
|
GstPad *tee_sinkpad;
|
|
GstPad *tee_srcpad;
|
|
GstPad *final_sinkpad;
|
|
GThread *app_thread;
|
|
gint countdown;
|
|
gboolean app_thread_prepped;
|
|
gboolean bufferalloc_blocked;
|
|
} BufferAllocHarness;
|
|
|
|
static void
|
|
buffer_alloc_harness_setup (BufferAllocHarness * h, gint countdown)
|
|
{
|
|
h->app_thread = NULL;
|
|
|
|
h->tee = gst_check_setup_element ("tee");
|
|
fail_if (h->tee == NULL);
|
|
|
|
h->countdown = countdown;
|
|
|
|
fail_unless_equals_int (gst_element_set_state (h->tee, GST_STATE_PLAYING),
|
|
TRUE);
|
|
|
|
h->caps = gst_caps_new_empty_simple ("video/x-raw");
|
|
|
|
h->start_srcpad = gst_pad_new ("src", GST_PAD_SRC);
|
|
fail_if (h->start_srcpad == NULL);
|
|
fail_unless (gst_pad_set_active (h->start_srcpad, TRUE) == TRUE);
|
|
fail_unless (gst_pad_set_caps (h->start_srcpad, h->caps) == TRUE);
|
|
|
|
h->tee_sinkpad = gst_element_get_static_pad (h->tee, "sink");
|
|
fail_if (h->tee_sinkpad == NULL);
|
|
|
|
h->tee_srcpad = gst_element_get_request_pad (h->tee, "src_%u");
|
|
fail_if (h->tee_srcpad == NULL);
|
|
|
|
h->final_sinkpad = gst_pad_new ("sink", GST_PAD_SINK);
|
|
fail_if (h->final_sinkpad == NULL);
|
|
fail_unless (gst_pad_set_active (h->final_sinkpad, TRUE) == TRUE);
|
|
fail_unless (gst_pad_set_caps (h->final_sinkpad, h->caps) == TRUE);
|
|
g_object_set_qdata (G_OBJECT (h->final_sinkpad),
|
|
g_quark_from_static_string ("buffer-alloc-harness"), h);
|
|
|
|
fail_unless_equals_int (gst_pad_link (h->start_srcpad, h->tee_sinkpad),
|
|
GST_PAD_LINK_OK);
|
|
fail_unless_equals_int (gst_pad_link (h->tee_srcpad, h->final_sinkpad),
|
|
GST_PAD_LINK_OK);
|
|
}
|
|
|
|
static void
|
|
buffer_alloc_harness_teardown (BufferAllocHarness * h)
|
|
{
|
|
if (h->app_thread)
|
|
g_thread_join (h->app_thread);
|
|
|
|
gst_pad_set_active (h->final_sinkpad, FALSE);
|
|
gst_object_unref (h->final_sinkpad);
|
|
gst_object_unref (h->tee_srcpad);
|
|
gst_object_unref (h->tee_sinkpad);
|
|
gst_pad_set_active (h->start_srcpad, FALSE);
|
|
gst_object_unref (h->start_srcpad);
|
|
gst_caps_unref (h->caps);
|
|
gst_check_teardown_element (h->tee);
|
|
}
|
|
|
|
#if 0
|
|
static gpointer
|
|
app_thread_func (gpointer data)
|
|
{
|
|
BufferAllocHarness *h = data;
|
|
|
|
/* Signal that we are about to call release_request_pad(). */
|
|
g_mutex_lock (&check_mutex);
|
|
h->app_thread_prepped = TRUE;
|
|
g_cond_signal (&check_cond);
|
|
g_mutex_unlock (&check_mutex);
|
|
|
|
/* Simulate that the app releases the pad while the streaming thread is in
|
|
* buffer_alloc below. */
|
|
gst_element_release_request_pad (h->tee, h->tee_srcpad);
|
|
|
|
/* Signal the bufferalloc function below if it's still waiting. */
|
|
g_mutex_lock (&check_mutex);
|
|
h->bufferalloc_blocked = FALSE;
|
|
g_cond_signal (&check_cond);
|
|
g_mutex_unlock (&check_mutex);
|
|
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
static GstFlowReturn
|
|
final_sinkpad_bufferalloc (GstPad * pad, guint64 offset, guint size,
|
|
GstCaps * caps, GstBuffer ** buf)
|
|
{
|
|
BufferAllocHarness *h;
|
|
GTimeVal deadline;
|
|
|
|
h = g_object_get_qdata (G_OBJECT (pad),
|
|
g_quark_from_static_string ("buffer-alloc-harness"));
|
|
g_assert (h != NULL);
|
|
|
|
if (--(h->countdown) == 0) {
|
|
/* Time to make the app release the pad. */
|
|
h->app_thread_prepped = FALSE;
|
|
h->bufferalloc_blocked = TRUE;
|
|
|
|
h->app_thread = g_thread_try_new ("gst-check", app_thread_func, h, NULL);
|
|
fail_if (h->app_thread == NULL);
|
|
|
|
/* Wait for the app thread to get ready to call release_request_pad(). */
|
|
g_mutex_lock (&check_mutex);
|
|
while (!h->app_thread_prepped)
|
|
g_cond_wait (&check_cond, &check_mutex);
|
|
g_mutex_unlock (&check_mutex);
|
|
|
|
/* Now wait for it to do that within a second, to avoid deadlocking
|
|
* in the event of future changes to the locking semantics. */
|
|
g_mutex_lock (&check_mutex);
|
|
g_get_current_time (&deadline);
|
|
deadline.tv_sec += 1;
|
|
while (h->bufferalloc_blocked) {
|
|
if (!g_cond_timed_wait (&check_cond, &check_mutex, &deadline))
|
|
break;
|
|
}
|
|
g_mutex_unlock (&check_mutex);
|
|
}
|
|
|
|
*buf = gst_buffer_new_and_alloc (size);
|
|
gst_buffer_set_caps (*buf, caps);
|
|
|
|
return GST_FLOW_OK;
|
|
}
|
|
#endif
|
|
|
|
/* Simulate an app releasing the pad while the first alloc_buffer() is in
|
|
* progress. */
|
|
GST_START_TEST (test_release_while_buffer_alloc)
|
|
{
|
|
BufferAllocHarness h;
|
|
|
|
buffer_alloc_harness_setup (&h, 1);
|
|
|
|
buffer_alloc_harness_teardown (&h);
|
|
}
|
|
|
|
GST_END_TEST;
|
|
|
|
/* Simulate an app releasing the pad while the second alloc_buffer() is in
|
|
* progress. */
|
|
GST_START_TEST (test_release_while_second_buffer_alloc)
|
|
{
|
|
BufferAllocHarness h;
|
|
|
|
buffer_alloc_harness_setup (&h, 2);
|
|
|
|
buffer_alloc_harness_teardown (&h);
|
|
}
|
|
|
|
GST_END_TEST;
|
|
|
|
/* Check the internal pads of tee */
|
|
GST_START_TEST (test_internal_links)
|
|
{
|
|
GstElement *tee;
|
|
GstPad *sinkpad, *srcpad1, *srcpad2;
|
|
GstIterator *it;
|
|
GstIteratorResult res;
|
|
GValue val1 = { 0, }
|
|
, val2 = {
|
|
0,};
|
|
|
|
tee = gst_check_setup_element ("tee");
|
|
|
|
sinkpad = gst_element_get_static_pad (tee, "sink");
|
|
fail_unless (sinkpad != NULL);
|
|
it = gst_pad_iterate_internal_links (sinkpad);
|
|
fail_unless (it != NULL);
|
|
|
|
/* iterator should not return anything */
|
|
res = gst_iterator_next (it, &val1);
|
|
fail_unless (res == GST_ITERATOR_DONE);
|
|
fail_unless (g_value_get_object (&val1) == NULL);
|
|
|
|
srcpad1 = gst_element_get_request_pad (tee, "src_%u");
|
|
fail_unless (srcpad1 != NULL);
|
|
|
|
/* iterator should resync */
|
|
res = gst_iterator_next (it, &val1);
|
|
fail_unless (res == GST_ITERATOR_RESYNC);
|
|
fail_unless (g_value_get_object (&val1) == NULL);
|
|
gst_iterator_resync (it);
|
|
|
|
/* we should get something now */
|
|
res = gst_iterator_next (it, &val1);
|
|
fail_unless (res == GST_ITERATOR_OK);
|
|
fail_unless (GST_PAD_CAST (g_value_get_object (&val1)) == srcpad1);
|
|
|
|
g_value_reset (&val1);
|
|
|
|
res = gst_iterator_next (it, &val1);
|
|
fail_unless (res == GST_ITERATOR_DONE);
|
|
fail_unless (g_value_get_object (&val1) == NULL);
|
|
|
|
srcpad2 = gst_element_get_request_pad (tee, "src_%u");
|
|
fail_unless (srcpad2 != NULL);
|
|
|
|
/* iterator should resync */
|
|
res = gst_iterator_next (it, &val1);
|
|
fail_unless (res == GST_ITERATOR_RESYNC);
|
|
fail_unless (g_value_get_object (&val1) == NULL);
|
|
gst_iterator_resync (it);
|
|
|
|
/* we should get one of the 2 pads now */
|
|
res = gst_iterator_next (it, &val1);
|
|
fail_unless (res == GST_ITERATOR_OK);
|
|
fail_unless (GST_PAD_CAST (g_value_get_object (&val1)) == srcpad1
|
|
|| GST_PAD_CAST (g_value_get_object (&val1)) == srcpad2);
|
|
|
|
/* and the other */
|
|
res = gst_iterator_next (it, &val2);
|
|
fail_unless (res == GST_ITERATOR_OK);
|
|
fail_unless (GST_PAD_CAST (g_value_get_object (&val2)) == srcpad1
|
|
|| GST_PAD_CAST (g_value_get_object (&val2)) == srcpad2);
|
|
fail_unless (g_value_get_object (&val1) != g_value_get_object (&val2));
|
|
g_value_reset (&val1);
|
|
g_value_reset (&val2);
|
|
|
|
res = gst_iterator_next (it, &val1);
|
|
fail_unless (res == GST_ITERATOR_DONE);
|
|
fail_unless (g_value_get_object (&val1) == NULL);
|
|
|
|
gst_iterator_free (it);
|
|
|
|
/* get an iterator for the other direction */
|
|
it = gst_pad_iterate_internal_links (srcpad1);
|
|
fail_unless (it != NULL);
|
|
|
|
res = gst_iterator_next (it, &val1);
|
|
fail_unless (res == GST_ITERATOR_OK);
|
|
fail_unless (GST_PAD_CAST (g_value_get_object (&val1)) == sinkpad);
|
|
g_value_reset (&val1);
|
|
|
|
res = gst_iterator_next (it, &val1);
|
|
fail_unless (res == GST_ITERATOR_DONE);
|
|
gst_iterator_free (it);
|
|
|
|
it = gst_pad_iterate_internal_links (srcpad2);
|
|
fail_unless (it != NULL);
|
|
|
|
res = gst_iterator_next (it, &val1);
|
|
fail_unless (res == GST_ITERATOR_OK);
|
|
fail_unless (GST_PAD_CAST (g_value_get_object (&val1)) == sinkpad);
|
|
g_value_reset (&val1);
|
|
|
|
res = gst_iterator_next (it, &val1);
|
|
fail_unless (res == GST_ITERATOR_DONE);
|
|
|
|
g_value_unset (&val1);
|
|
g_value_unset (&val2);
|
|
gst_iterator_free (it);
|
|
gst_object_unref (srcpad1);
|
|
gst_object_unref (srcpad2);
|
|
gst_object_unref (sinkpad);
|
|
gst_object_unref (tee);
|
|
}
|
|
|
|
GST_END_TEST;
|
|
|
|
static GstFlowReturn
|
|
_fake_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
|
|
{
|
|
gst_buffer_unref (buffer);
|
|
return GST_FLOW_OK;
|
|
}
|
|
|
|
static GstFlowReturn
|
|
_fake_chain_error (GstPad * pad, GstObject * parent, GstBuffer * buffer)
|
|
{
|
|
gst_buffer_unref (buffer);
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
GST_START_TEST (test_flow_aggregation)
|
|
{
|
|
GstPad *mysrc, *mysink1, *mysink2;
|
|
GstPad *teesink, *teesrc1, *teesrc2;
|
|
GstElement *tee;
|
|
GstBuffer *buffer;
|
|
GstSegment segment;
|
|
GstCaps *caps;
|
|
|
|
caps = gst_caps_new_empty_simple ("test/test");
|
|
|
|
tee = gst_element_factory_make ("tee", NULL);
|
|
fail_unless (tee != NULL);
|
|
teesink = gst_element_get_static_pad (tee, "sink");
|
|
fail_unless (teesink != NULL);
|
|
teesrc1 = gst_element_get_request_pad (tee, "src_%u");
|
|
fail_unless (teesrc1 != NULL);
|
|
teesrc2 = gst_element_get_request_pad (tee, "src_%u");
|
|
fail_unless (teesrc2 != NULL);
|
|
|
|
GST_DEBUG ("Creating mysink1");
|
|
mysink1 = gst_pad_new ("mysink1", GST_PAD_SINK);
|
|
gst_pad_set_chain_function (mysink1, _fake_chain);
|
|
gst_pad_set_active (mysink1, TRUE);
|
|
|
|
GST_DEBUG ("Creating mysink2");
|
|
mysink2 = gst_pad_new ("mysink2", GST_PAD_SINK);
|
|
gst_pad_set_chain_function (mysink2, _fake_chain);
|
|
gst_pad_set_active (mysink2, TRUE);
|
|
|
|
GST_DEBUG ("Creating mysrc");
|
|
mysrc = gst_pad_new ("mysrc", GST_PAD_SRC);
|
|
gst_pad_set_active (mysrc, TRUE);
|
|
|
|
gst_segment_init (&segment, GST_FORMAT_BYTES);
|
|
gst_pad_push_event (mysrc, gst_event_new_stream_start ("test"));
|
|
gst_pad_set_caps (mysrc, caps);
|
|
gst_pad_push_event (mysrc, gst_event_new_segment (&segment));
|
|
|
|
fail_unless (gst_pad_link (mysrc, teesink) == GST_PAD_LINK_OK);
|
|
fail_unless (gst_pad_link (teesrc1, mysink1) == GST_PAD_LINK_OK);
|
|
fail_unless (gst_pad_link (teesrc2, mysink2) == GST_PAD_LINK_OK);
|
|
|
|
fail_unless (gst_element_set_state (tee,
|
|
GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS);
|
|
|
|
buffer = gst_buffer_new ();
|
|
#if 0
|
|
gst_buffer_set_caps (buffer, caps);
|
|
#endif
|
|
|
|
GST_DEBUG ("Try to push a buffer");
|
|
/* First check if everything works in normal state */
|
|
fail_unless (gst_pad_push (mysrc, gst_buffer_ref (buffer)) == GST_FLOW_OK);
|
|
|
|
/* One pad being in wrong state must result in wrong state */
|
|
GST_DEBUG ("Trying to push with mysink2 disabled");
|
|
gst_pad_set_active (mysink2, FALSE);
|
|
fail_unless (gst_pad_push (mysrc,
|
|
gst_buffer_ref (buffer)) == GST_FLOW_FLUSHING);
|
|
|
|
GST_DEBUG ("Trying to push with mysink2 disabled");
|
|
gst_pad_set_active (mysink1, FALSE);
|
|
gst_pad_set_active (mysink2, TRUE);
|
|
fail_unless (gst_pad_push (mysrc,
|
|
gst_buffer_ref (buffer)) == GST_FLOW_FLUSHING);
|
|
|
|
GST_DEBUG ("Trying to push with mysink2 and mysink1 disabled");
|
|
gst_pad_set_active (mysink2, FALSE);
|
|
fail_unless (gst_pad_push (mysrc,
|
|
gst_buffer_ref (buffer)) == GST_FLOW_FLUSHING);
|
|
|
|
/* Test if everything still works in normal state */
|
|
GST_DEBUG ("Reactivate both pads and try pushing");
|
|
gst_pad_set_active (mysink1, TRUE);
|
|
gst_pad_set_active (mysink2, TRUE);
|
|
fail_unless (gst_pad_push (mysrc, gst_buffer_ref (buffer)) == GST_FLOW_OK);
|
|
|
|
/* One unlinked pad must return OK, two unlinked pads must return NOT_LINKED */
|
|
GST_DEBUG ("Pushing with mysink1 unlinked");
|
|
fail_unless (gst_pad_unlink (teesrc1, mysink1) == TRUE);
|
|
fail_unless (gst_pad_push (mysrc, gst_buffer_ref (buffer)) == GST_FLOW_OK);
|
|
|
|
GST_DEBUG ("Pushing with mysink2 unlinked");
|
|
fail_unless (gst_pad_link (teesrc1, mysink1) == GST_PAD_LINK_OK);
|
|
fail_unless (gst_pad_unlink (teesrc2, mysink2) == TRUE);
|
|
fail_unless (gst_pad_push (mysrc, gst_buffer_ref (buffer)) == GST_FLOW_OK);
|
|
|
|
GST_DEBUG ("Pushing with mysink1 AND mysink2 unlinked");
|
|
fail_unless (gst_pad_unlink (teesrc1, mysink1) == TRUE);
|
|
fail_unless (gst_pad_push (mysrc,
|
|
gst_buffer_ref (buffer)) == GST_FLOW_NOT_LINKED);
|
|
|
|
/* Test if everything still works in normal state */
|
|
GST_DEBUG ("Relink both pads and try pushing");
|
|
fail_unless (gst_pad_link (teesrc1, mysink1) == GST_PAD_LINK_OK);
|
|
fail_unless (gst_pad_link (teesrc2, mysink2) == GST_PAD_LINK_OK);
|
|
fail_unless (gst_pad_push (mysrc, gst_buffer_ref (buffer)) == GST_FLOW_OK);
|
|
|
|
/* One pad returning ERROR should result in ERROR */
|
|
GST_DEBUG ("Pushing with mysink1 returning GST_FLOW_ERROR");
|
|
gst_pad_set_chain_function (mysink1, _fake_chain_error);
|
|
fail_unless (gst_pad_push (mysrc, gst_buffer_ref (buffer)) == GST_FLOW_ERROR);
|
|
|
|
GST_DEBUG ("Pushing with mysink2 returning GST_FLOW_ERROR");
|
|
gst_pad_set_chain_function (mysink1, _fake_chain);
|
|
gst_pad_set_chain_function (mysink2, _fake_chain_error);
|
|
fail_unless (gst_pad_push (mysrc, gst_buffer_ref (buffer)) == GST_FLOW_ERROR);
|
|
|
|
GST_DEBUG ("Pushing with mysink1 AND mysink2 returning GST_FLOW_ERROR");
|
|
gst_pad_set_chain_function (mysink1, _fake_chain_error);
|
|
fail_unless (gst_pad_push (mysrc, gst_buffer_ref (buffer)) == GST_FLOW_ERROR);
|
|
|
|
/* And now everything still needs to work */
|
|
GST_DEBUG ("Try pushing with everything ok");
|
|
gst_pad_set_chain_function (mysink1, _fake_chain);
|
|
gst_pad_set_chain_function (mysink2, _fake_chain);
|
|
fail_unless (gst_pad_push (mysrc, gst_buffer_ref (buffer)) == GST_FLOW_OK);
|
|
|
|
fail_unless (gst_element_set_state (tee,
|
|
GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS);
|
|
|
|
fail_unless (gst_pad_unlink (mysrc, teesink) == TRUE);
|
|
fail_unless (gst_pad_unlink (teesrc1, mysink1) == TRUE);
|
|
fail_unless (gst_pad_unlink (teesrc2, mysink2) == TRUE);
|
|
|
|
|
|
gst_object_unref (teesink);
|
|
gst_object_unref (teesrc1);
|
|
gst_object_unref (teesrc2);
|
|
gst_element_release_request_pad (tee, teesrc1);
|
|
gst_element_release_request_pad (tee, teesrc2);
|
|
gst_object_unref (tee);
|
|
|
|
gst_object_unref (mysink1);
|
|
gst_object_unref (mysink2);
|
|
gst_object_unref (mysrc);
|
|
gst_caps_unref (caps);
|
|
gst_buffer_unref (buffer);
|
|
}
|
|
|
|
GST_END_TEST;
|
|
|
|
GST_START_TEST (test_request_pads)
|
|
{
|
|
GstElement *tee;
|
|
GstPad *srcpad1, *srcpad2, *srcpad3, *srcpad4;
|
|
|
|
tee = gst_check_setup_element ("tee");
|
|
|
|
srcpad1 = gst_element_get_request_pad (tee, "src_%u");
|
|
fail_unless (srcpad1 != NULL);
|
|
fail_unless_equals_string (GST_OBJECT_NAME (srcpad1), "src_0");
|
|
srcpad2 = gst_element_get_request_pad (tee, "src_100");
|
|
fail_unless (srcpad2 != NULL);
|
|
fail_unless_equals_string (GST_OBJECT_NAME (srcpad2), "src_100");
|
|
srcpad3 = gst_element_get_request_pad (tee, "src_10");
|
|
fail_unless (srcpad3 != NULL);
|
|
fail_unless_equals_string (GST_OBJECT_NAME (srcpad3), "src_10");
|
|
srcpad4 = gst_element_get_request_pad (tee, "src_%u");
|
|
fail_unless (srcpad4 != NULL);
|
|
|
|
gst_object_unref (srcpad1);
|
|
gst_object_unref (srcpad2);
|
|
gst_object_unref (srcpad3);
|
|
gst_object_unref (srcpad4);
|
|
gst_object_unref (tee);
|
|
}
|
|
|
|
GST_END_TEST;
|
|
|
|
GST_START_TEST (test_allow_not_linked)
|
|
{
|
|
GstElement *tee;
|
|
GstPad *src1, *src2;
|
|
GstBuffer *buffer;
|
|
GstPad *srcpad;
|
|
GstCaps *caps;
|
|
GstSegment segment;
|
|
|
|
static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
|
|
GST_PAD_SRC,
|
|
GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS_ANY);
|
|
|
|
caps = gst_caps_new_empty_simple ("test/test");
|
|
|
|
tee = gst_check_setup_element ("tee");
|
|
fail_unless (tee);
|
|
g_object_set (tee, "allow-not-linked", TRUE, NULL);
|
|
|
|
srcpad = gst_check_setup_src_pad (tee, &srctemplate);
|
|
gst_pad_set_active (srcpad, TRUE);
|
|
|
|
gst_pad_push_event (srcpad, gst_event_new_stream_start ("test"));
|
|
gst_segment_init (&segment, GST_FORMAT_BYTES);
|
|
gst_pad_push_event (srcpad, gst_event_new_stream_start ("test"));
|
|
gst_pad_set_caps (srcpad, caps);
|
|
gst_caps_unref (caps);
|
|
gst_pad_push_event (srcpad, gst_event_new_segment (&segment));
|
|
|
|
fail_unless (gst_element_set_state (tee,
|
|
GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS);
|
|
|
|
buffer = gst_buffer_new ();
|
|
fail_unless (buffer);
|
|
|
|
fail_unless (gst_pad_push (srcpad, gst_buffer_ref (buffer)) == GST_FLOW_OK);
|
|
|
|
src1 = gst_element_get_request_pad (tee, "src_%u");
|
|
|
|
fail_unless (gst_pad_push (srcpad, gst_buffer_ref (buffer)) == GST_FLOW_OK);
|
|
|
|
src2 = gst_element_get_request_pad (tee, "src_%u");
|
|
|
|
fail_unless (gst_pad_push (srcpad, gst_buffer_ref (buffer)) == GST_FLOW_OK);
|
|
|
|
g_object_set (tee, "allow-not-linked", FALSE, NULL);
|
|
|
|
fail_unless (gst_pad_push (srcpad,
|
|
gst_buffer_ref (buffer)) == GST_FLOW_NOT_LINKED);
|
|
|
|
gst_element_release_request_pad (tee, src1);
|
|
|
|
fail_unless (gst_pad_push (srcpad,
|
|
gst_buffer_ref (buffer)) == GST_FLOW_NOT_LINKED);
|
|
|
|
gst_element_release_request_pad (tee, src2);
|
|
g_object_unref (src1);
|
|
g_object_unref (src2);
|
|
|
|
fail_unless (gst_pad_push (srcpad,
|
|
gst_buffer_ref (buffer)) == GST_FLOW_NOT_LINKED);
|
|
|
|
gst_pad_set_active (srcpad, FALSE);
|
|
gst_check_teardown_src_pad (tee);
|
|
gst_check_teardown_element (tee);
|
|
|
|
fail_if (buffer->mini_object.refcount != 1);
|
|
gst_buffer_unref (buffer);
|
|
}
|
|
|
|
GST_END_TEST;
|
|
|
|
static Suite *
|
|
tee_suite (void)
|
|
{
|
|
Suite *s = suite_create ("tee");
|
|
TCase *tc_chain = tcase_create ("general");
|
|
|
|
/* Set the timeout to a much larger time - 3 minutes */
|
|
tcase_set_timeout (tc_chain, 180);
|
|
|
|
suite_add_tcase (s, tc_chain);
|
|
tcase_add_test (tc_chain, test_num_buffers);
|
|
tcase_add_test (tc_chain, test_stress);
|
|
tcase_add_test (tc_chain, test_release_while_buffer_alloc);
|
|
tcase_add_test (tc_chain, test_release_while_second_buffer_alloc);
|
|
tcase_add_test (tc_chain, test_internal_links);
|
|
tcase_add_test (tc_chain, test_flow_aggregation);
|
|
tcase_add_test (tc_chain, test_request_pads);
|
|
tcase_add_test (tc_chain, test_allow_not_linked);
|
|
|
|
return s;
|
|
}
|
|
|
|
GST_CHECK_MAIN (tee);
|