gstreamer/tests/check/gst/gstghostpad.c
Wim Taymans ba3028aeeb pad: Add GstPadProbeInfo
Make a new GstPadProbeInfo structure and pass this in the probe callback. This
allows us to add more things later and also allow the callback to replace or
modify the passed object.
2011-11-08 11:04:19 +01:00

1138 lines
35 KiB
C

/* GStreamer
* Copyright (C) 2005 Wim Taymans <wim@fluendo.com>
*
* gstghostpad.c: Unit test for GstGhostPad
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <gst/check/gstcheck.h>
/* test if removing a bin also cleans up the ghostpads
*/
GST_START_TEST (test_remove1)
{
GstElement *b1, *b2, *src, *sink;
GstPad *srcpad, *sinkpad;
GstPadLinkReturn ret;
b1 = gst_element_factory_make ("pipeline", NULL);
b2 = gst_element_factory_make ("bin", NULL);
src = gst_element_factory_make ("fakesrc", NULL);
sink = gst_element_factory_make ("fakesink", NULL);
ASSERT_OBJECT_REFCOUNT (b1, "pipeline", 1);
ASSERT_OBJECT_REFCOUNT (b2, "bin", 1);
fail_unless (gst_bin_add (GST_BIN (b2), sink));
fail_unless (gst_bin_add (GST_BIN (b1), src));
ASSERT_OBJECT_REFCOUNT (b1, "pipeline", 1);
ASSERT_OBJECT_REFCOUNT (b2, "bin", 1);
fail_unless (gst_bin_add (GST_BIN (b1), b2));
ASSERT_OBJECT_REFCOUNT (b1, "pipeline", 1);
ASSERT_OBJECT_REFCOUNT (b2, "bin", 1);
sinkpad = gst_element_get_static_pad (sink, "sink");
gst_element_add_pad (b2, gst_ghost_pad_new ("sink", sinkpad));
gst_object_unref (sinkpad);
srcpad = gst_element_get_static_pad (src, "src");
/* get the ghostpad */
sinkpad = gst_element_get_static_pad (b2, "sink");
ret = gst_pad_link (srcpad, sinkpad);
fail_unless (ret == GST_PAD_LINK_OK);
gst_object_unref (srcpad);
gst_object_unref (sinkpad);
/* now remove the bin with the ghostpad, b2 is disposed now. */
ASSERT_OBJECT_REFCOUNT (b1, "pipeline", 1);
ASSERT_OBJECT_REFCOUNT (b2, "bin", 1);
gst_bin_remove (GST_BIN (b1), b2);
srcpad = gst_element_get_static_pad (src, "src");
/* pad cannot be linked now */
fail_if (gst_pad_is_linked (srcpad));
gst_object_unref (srcpad);
ASSERT_OBJECT_REFCOUNT (b1, "pipeline", 1);
gst_object_unref (b1);
}
GST_END_TEST;
/* test if removing a bin also cleans up the ghostpads
*/
GST_START_TEST (test_remove2)
{
GstElement *b1, *b2, *src, *sink;
GstPad *srcpad, *sinkpad;
GstPadLinkReturn ret;
b1 = gst_element_factory_make ("pipeline", NULL);
b2 = gst_element_factory_make ("bin", NULL);
src = gst_element_factory_make ("fakesrc", NULL);
sink = gst_element_factory_make ("fakesink", NULL);
ASSERT_OBJECT_REFCOUNT (src, "src", 1);
fail_unless (gst_bin_add (GST_BIN (b2), sink));
fail_unless (gst_bin_add (GST_BIN (b1), src));
fail_unless (gst_bin_add (GST_BIN (b1), b2));
ASSERT_OBJECT_REFCOUNT (src, "src", 1);
sinkpad = gst_element_get_static_pad (sink, "sink");
gst_element_add_pad (b2, gst_ghost_pad_new ("sink", sinkpad));
gst_object_unref (sinkpad);
srcpad = gst_element_get_static_pad (src, "src");
ASSERT_OBJECT_REFCOUNT (srcpad, "srcpad", 2); /* since we got one */
/* get the ghostpad */
sinkpad = gst_element_get_static_pad (b2, "sink");
ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 2); /* since we got one */
GST_DEBUG ("linking srcpad and sinkpad");
ret = gst_pad_link (srcpad, sinkpad);
GST_DEBUG ("linked srcpad and sinkpad");
fail_unless (ret == GST_PAD_LINK_OK);
/* Refcount should be unchanged, targets are now decuced using peer pad */
ASSERT_OBJECT_REFCOUNT (srcpad, "srcpad", 2);
ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 2);
gst_object_unref (srcpad);
gst_object_unref (sinkpad);
/* now remove the sink from the bin */
gst_bin_remove (GST_BIN (b2), sink);
srcpad = gst_element_get_static_pad (src, "src");
/* pad is still linked to ghostpad */
fail_if (!gst_pad_is_linked (srcpad));
ASSERT_OBJECT_REFCOUNT (src, "src", 1);
ASSERT_OBJECT_REFCOUNT (srcpad, "srcpad", 2);
gst_object_unref (srcpad);
ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 1);
/* cleanup */
/* now unlink the pads */
gst_pad_unlink (srcpad, sinkpad);
ASSERT_OBJECT_REFCOUNT (srcpad, "srcpad", 1); /* we dropped our ref */
ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 1);
ASSERT_OBJECT_REFCOUNT (src, "src", 1);
ASSERT_OBJECT_REFCOUNT (b2, "bin", 1);
/* remove b2 from b1 */
gst_bin_remove (GST_BIN (b1), b2);
/* flush the message, dropping the b1 refcount to 1 */
gst_element_set_state (b1, GST_STATE_READY);
gst_element_set_state (b1, GST_STATE_NULL);
ASSERT_OBJECT_REFCOUNT (b1, "pipeline", 1);
gst_object_unref (b1);
}
GST_END_TEST;
/* test if a ghost pad without a target can be linked and
* unlinked. An untargeted ghostpad has a default ANY caps unless there
* is a padtemplate that says something else.
*/
GST_START_TEST (test_ghost_pads_notarget)
{
GstElement *b1, *b2, *sink;
GstPad *srcpad, *sinkpad, *peer;
GstPadLinkReturn ret;
gboolean bret;
GstBus *bus;
GstCaps *caps;
b1 = gst_element_factory_make ("pipeline", NULL);
/* make sure all messages are discarded */
bus = gst_pipeline_get_bus (GST_PIPELINE (b1));
gst_bus_set_flushing (bus, TRUE);
gst_object_unref (bus);
b2 = gst_element_factory_make ("bin", NULL);
sink = gst_element_factory_make ("fakesink", NULL);
fail_unless (gst_bin_add (GST_BIN (b1), sink));
fail_unless (gst_bin_add (GST_BIN (b1), b2));
srcpad = gst_ghost_pad_new_no_target ("src", GST_PAD_SRC);
fail_unless (srcpad != NULL);
sinkpad = gst_element_get_static_pad (sink, "sink");
fail_unless (sinkpad != NULL);
ret = gst_pad_link (srcpad, sinkpad);
fail_unless (ret == GST_PAD_LINK_OK);
/* check if the peers are ok */
peer = gst_pad_get_peer (srcpad);
fail_unless (peer == sinkpad);
gst_object_unref (peer);
peer = gst_pad_get_peer (sinkpad);
fail_unless (peer == srcpad);
gst_object_unref (peer);
/* check caps, untargetted pad should return ANY or the padtemplate caps
* when it was created from a template */
caps = gst_pad_get_caps (srcpad, NULL);
fail_unless (gst_caps_is_any (caps));
gst_caps_unref (caps);
/* unlink */
bret = gst_pad_unlink (srcpad, sinkpad);
fail_unless (bret == TRUE);
/* cleanup */
gst_object_unref (srcpad);
gst_object_unref (sinkpad);
gst_object_unref (b1);
}
GST_END_TEST;
/* Test that removing the target of a ghostpad properly sets the target of the
* ghostpad to NULL */
GST_START_TEST (test_remove_target)
{
GstElement *b1, *b2, *src, *sink;
GstPad *sinkpad, *ghost, *target;
b1 = gst_element_factory_make ("pipeline", NULL);
b2 = gst_element_factory_make ("bin", NULL);
src = gst_element_factory_make ("fakesrc", NULL);
sink = gst_element_factory_make ("fakesink", NULL);
ASSERT_OBJECT_REFCOUNT (src, "src", 1);
fail_unless (gst_bin_add (GST_BIN (b2), sink));
fail_unless (gst_bin_add (GST_BIN (b1), src));
fail_unless (gst_bin_add (GST_BIN (b1), b2));
ASSERT_OBJECT_REFCOUNT (src, "src", 1);
sinkpad = gst_element_get_static_pad (sink, "sink");
gst_element_add_pad (b2, gst_ghost_pad_new ("sink", sinkpad));
ghost = gst_element_get_static_pad (b2, "sink");
target = gst_ghost_pad_get_target (GST_GHOST_PAD (ghost));
fail_unless (target == sinkpad);
gst_object_unref (target);
gst_object_unref (sinkpad);
gst_bin_remove (GST_BIN (b2), sink);
target = gst_ghost_pad_get_target (GST_GHOST_PAD (ghost));
fail_unless (target == NULL);
gst_object_unref (b1);
gst_object_unref (ghost);
}
GST_END_TEST;
/* test if linking fails over different bins using a pipeline
* like this:
*
* fakesrc num_buffers=10 ! ( fakesink )
*
*/
GST_START_TEST (test_link)
{
GstElement *b1, *b2, *src, *sink;
GstPad *srcpad, *sinkpad, *gpad, *ppad, *tmp;
GstPadLinkReturn ret;
b1 = gst_element_factory_make ("pipeline", NULL);
b2 = gst_element_factory_make ("bin", NULL);
src = gst_element_factory_make ("fakesrc", NULL);
sink = gst_element_factory_make ("fakesink", NULL);
fail_unless (gst_bin_add (GST_BIN (b2), sink));
fail_unless (gst_bin_add (GST_BIN (b1), src));
fail_unless (gst_bin_add (GST_BIN (b1), b2));
srcpad = gst_element_get_static_pad (src, "src");
fail_unless (srcpad != NULL);
sinkpad = gst_element_get_static_pad (sink, "sink");
fail_unless (sinkpad != NULL);
/* linking in different hierarchies should fail */
ret = gst_pad_link (srcpad, sinkpad);
fail_unless (ret == GST_PAD_LINK_WRONG_HIERARCHY);
/* now setup a ghostpad */
gpad = gst_ghost_pad_new ("sink", sinkpad);
/* Check if the internal pads are set correctly */
ppad = GST_PAD (gst_proxy_pad_get_internal (GST_PROXY_PAD (gpad)));
fail_unless (ppad == GST_PAD_PEER (sinkpad));
tmp = GST_PAD (gst_proxy_pad_get_internal (GST_PROXY_PAD (ppad)));
fail_unless (tmp == gpad);
gst_object_unref (tmp);
gst_object_unref (ppad);
gst_object_unref (sinkpad);
/* need to ref as _add_pad takes ownership */
gst_object_ref (gpad);
gst_element_add_pad (b2, gpad);
/* our new sinkpad */
sinkpad = gpad;
/* and linking should work now */
ret = gst_pad_link (srcpad, sinkpad);
fail_unless (ret == GST_PAD_LINK_OK);
/* flush the message, dropping the b1 refcount to 1 */
gst_element_set_state (b1, GST_STATE_READY);
gst_element_set_state (b1, GST_STATE_NULL);
ASSERT_OBJECT_REFCOUNT (b1, "pipeline", 1);
gst_object_unref (srcpad);
gst_object_unref (sinkpad);
gst_object_unref (b1);
}
GST_END_TEST;
/* test if ghostpads are created automagically when using
* gst_element_link_pads.
*
* fakesrc num_buffers=10 ! ( identity ) ! fakesink
*/
GST_START_TEST (test_ghost_pads)
{
GstElement *b1, *b2, *src, *i1, *sink;
GstPad *gsink, *gsrc, *gisrc, *gisink, *isink, *isrc, *fsrc, *fsink;
GstStateChangeReturn ret;
b1 = gst_element_factory_make ("pipeline", NULL);
b2 = gst_element_factory_make ("bin", NULL);
src = gst_element_factory_make ("fakesrc", NULL);
g_object_set (src, "num-buffers", (int) 10, NULL);
i1 = gst_element_factory_make ("identity", NULL);
sink = gst_element_factory_make ("fakesink", NULL);
fail_unless (gst_bin_add (GST_BIN (b2), i1));
fail_unless (gst_bin_add (GST_BIN (b1), src));
fail_unless (gst_bin_add (GST_BIN (b1), b2));
fail_unless (gst_bin_add (GST_BIN (b1), sink));
fail_unless (gst_element_link_pads (src, NULL, i1, NULL));
fail_unless (gst_element_link_pads (i1, NULL, sink, NULL));
GST_OBJECT_LOCK (b2);
fail_unless (b2->numsinkpads == 1);
fail_unless (GST_IS_GHOST_PAD (b2->sinkpads->data));
fail_unless (b2->numsrcpads == 1);
fail_unless (GST_IS_GHOST_PAD (b2->srcpads->data));
GST_OBJECT_UNLOCK (b2);
fsrc = gst_element_get_static_pad (src, "src");
fail_unless (fsrc != NULL);
gsink = GST_PAD (gst_object_ref (b2->sinkpads->data));
fail_unless (gsink != NULL);
gsrc = GST_PAD (gst_object_ref (b2->srcpads->data));
fail_unless (gsrc != NULL);
fsink = gst_element_get_static_pad (sink, "sink");
fail_unless (fsink != NULL);
isink = gst_element_get_static_pad (i1, "sink");
fail_unless (isink != NULL);
isrc = gst_element_get_static_pad (i1, "src");
fail_unless (isrc != NULL);
gisrc = gst_pad_get_peer (isink);
fail_unless (gisrc != NULL);
gisink = gst_pad_get_peer (isrc);
fail_unless (gisink != NULL);
/* all objects above have one refcount owned by us as well */
ASSERT_OBJECT_REFCOUNT (fsrc, "fsrc", 2); /* parent */
ASSERT_OBJECT_REFCOUNT (gsink, "gsink", 2); /* parent */
ASSERT_OBJECT_REFCOUNT (gsrc, "gsrc", 2); /* parent */
ASSERT_OBJECT_REFCOUNT (fsink, "fsink", 2); /* parent */
ASSERT_OBJECT_REFCOUNT (gisrc, "gisrc", 2); /* parent */
ASSERT_OBJECT_REFCOUNT (isink, "isink", 2); /* parent */
ASSERT_OBJECT_REFCOUNT (gisink, "gisink", 2); /* parent */
ASSERT_OBJECT_REFCOUNT (isrc, "isrc", 2); /* parent */
ret = gst_element_set_state (b1, GST_STATE_PLAYING);
ret = gst_element_get_state (b1, NULL, NULL, GST_CLOCK_TIME_NONE);
fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
ret = gst_element_set_state (b1, GST_STATE_NULL);
ret = gst_element_get_state (b1, NULL, NULL, GST_CLOCK_TIME_NONE);
fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
gst_object_unref (b1);
/* unreffing the bin will unref all elements, which will unlink and unparent
* all pads */
/* wait for thread to settle down */
while (GST_OBJECT_REFCOUNT_VALUE (fsrc) > 1)
THREAD_SWITCH ();
ASSERT_OBJECT_REFCOUNT (fsrc, "fsrc", 1);
ASSERT_OBJECT_REFCOUNT (gsink, "gsink", 1);
ASSERT_OBJECT_REFCOUNT (gsrc, "gsink", 1);
ASSERT_OBJECT_REFCOUNT (fsink, "fsink", 1);
ASSERT_OBJECT_REFCOUNT (gisrc, "gisrc", 2); /* gsink */
ASSERT_OBJECT_REFCOUNT (isink, "isink", 1); /* gsink */
ASSERT_OBJECT_REFCOUNT (gisink, "gisink", 2); /* gsrc */
ASSERT_OBJECT_REFCOUNT (isrc, "isrc", 1); /* gsrc */
gst_object_unref (gsink);
ASSERT_OBJECT_REFCOUNT (isink, "isink", 1);
ASSERT_OBJECT_REFCOUNT (gisrc, "gisrc", 1);
ASSERT_OBJECT_REFCOUNT (fsrc, "fsrc", 1);
gst_object_unref (gisrc);
ASSERT_OBJECT_REFCOUNT (fsrc, "fsrc", 1);
gst_object_unref (gsrc);
ASSERT_OBJECT_REFCOUNT (isrc, "isrc", 1);
ASSERT_OBJECT_REFCOUNT (gisink, "gisink", 1);
ASSERT_OBJECT_REFCOUNT (fsink, "fsink", 1);
gst_object_unref (gisink);
ASSERT_OBJECT_REFCOUNT (fsink, "fsink", 1);
gst_object_unref (fsrc);
gst_object_unref (isrc);
gst_object_unref (isink);
gst_object_unref (fsink);
}
GST_END_TEST;
GST_START_TEST (test_ghost_pads_bin)
{
GstBin *pipeline;
GstBin *srcbin;
GstBin *sinkbin;
GstElement *src;
GstElement *sink;
GstPad *srcpad, *srcghost, *target;
GstPad *sinkpad, *sinkghost;
pipeline = GST_BIN (gst_pipeline_new ("pipe"));
ASSERT_OBJECT_REFCOUNT (pipeline, "pipeline", 1);
srcbin = GST_BIN (gst_bin_new ("srcbin"));
gst_bin_add (pipeline, GST_ELEMENT (srcbin));
ASSERT_OBJECT_REFCOUNT (pipeline, "pipeline", 1);
sinkbin = GST_BIN (gst_bin_new ("sinkbin"));
gst_bin_add (pipeline, GST_ELEMENT (sinkbin));
ASSERT_OBJECT_REFCOUNT (pipeline, "pipeline", 1);
src = gst_element_factory_make ("fakesrc", "src");
gst_bin_add (srcbin, src);
srcpad = gst_element_get_static_pad (src, "src");
srcghost = gst_ghost_pad_new ("src", srcpad);
gst_object_unref (srcpad);
gst_element_add_pad (GST_ELEMENT (srcbin), srcghost);
sink = gst_element_factory_make ("fakesink", "sink");
gst_bin_add (sinkbin, sink);
sinkpad = gst_element_get_static_pad (sink, "sink");
sinkghost = gst_ghost_pad_new ("sink", sinkpad);
gst_object_unref (sinkpad);
gst_element_add_pad (GST_ELEMENT (sinkbin), sinkghost);
gst_element_link (GST_ELEMENT (srcbin), GST_ELEMENT (sinkbin));
fail_unless (GST_PAD_PEER (srcghost) != NULL);
fail_unless (GST_PAD_PEER (sinkghost) != NULL);
target = gst_ghost_pad_get_target (GST_GHOST_PAD (srcghost));
fail_unless (GST_PAD_PEER (target) != NULL);
gst_object_unref (target);
target = gst_ghost_pad_get_target (GST_GHOST_PAD (sinkghost));
fail_unless (GST_PAD_PEER (target) != NULL);
gst_object_unref (target);
ASSERT_OBJECT_REFCOUNT (pipeline, "pipeline", 1);
gst_object_unref (pipeline);
}
GST_END_TEST;
typedef struct
{
GMutex *mutex;
GCond *cond;
} BlockData;
static GstPadProbeReturn
block_callback (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
{
BlockData *block_data = (BlockData *) user_data;
g_mutex_lock (block_data->mutex);
GST_DEBUG ("blocked\n");
g_cond_signal (block_data->cond);
g_mutex_unlock (block_data->mutex);
return GST_PAD_PROBE_OK;
}
GST_START_TEST (test_ghost_pads_block)
{
GstBin *pipeline;
GstBin *srcbin;
GstElement *src;
GstPad *srcpad;
GstPad *srcghost;
BlockData block_data;
pipeline = GST_BIN (gst_pipeline_new ("pipeline"));
srcbin = GST_BIN (gst_bin_new ("srcbin"));
gst_bin_add (pipeline, GST_ELEMENT (srcbin));
src = gst_element_factory_make ("fakesrc", "src");
gst_bin_add (srcbin, src);
srcpad = gst_element_get_static_pad (src, "src");
srcghost = gst_ghost_pad_new ("src", srcpad);
gst_element_add_pad (GST_ELEMENT (srcbin), srcghost);
gst_object_unref (srcpad);
block_data.mutex = g_mutex_new ();
block_data.cond = g_cond_new ();
g_mutex_lock (block_data.mutex);
gst_pad_add_probe (srcghost, GST_PAD_PROBE_TYPE_BLOCK, block_callback,
&block_data, NULL);
gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
/* and wait now */
g_cond_wait (block_data.cond, block_data.mutex);
gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL);
g_mutex_unlock (block_data.mutex);
g_mutex_free (block_data.mutex);
g_cond_free (block_data.cond);
ASSERT_OBJECT_REFCOUNT (pipeline, "pipeline", 1);
gst_object_unref (pipeline);
}
GST_END_TEST;
GST_START_TEST (test_ghost_pads_probes)
{
GstBin *pipeline;
GstBin *srcbin;
GstElement *src;
GstPad *srcpad;
GstPad *srcghost;
BlockData block_data;
pipeline = GST_BIN (gst_pipeline_new ("pipeline"));
srcbin = GST_BIN (gst_bin_new ("srcbin"));
gst_bin_add (pipeline, GST_ELEMENT (srcbin));
src = gst_element_factory_make ("fakesrc", "src");
gst_bin_add (srcbin, src);
srcpad = gst_element_get_static_pad (src, "src");
srcghost = gst_ghost_pad_new ("src", srcpad);
gst_element_add_pad (GST_ELEMENT (srcbin), srcghost);
gst_object_unref (srcpad);
block_data.mutex = g_mutex_new ();
block_data.cond = g_cond_new ();
g_mutex_lock (block_data.mutex);
gst_pad_add_probe (srcghost, GST_PAD_PROBE_TYPE_BLOCK, block_callback,
&block_data, NULL);
gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
/* and wait now */
g_cond_wait (block_data.cond, block_data.mutex);
gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL);
g_mutex_unlock (block_data.mutex);
g_mutex_free (block_data.mutex);
g_cond_free (block_data.cond);
ASSERT_OBJECT_REFCOUNT (pipeline, "pipeline", 1);
gst_object_unref (pipeline);
}
GST_END_TEST;
GST_START_TEST (test_ghost_pads_new_from_template)
{
GstPad *sinkpad, *ghostpad;
GstPadTemplate *padtempl, *ghosttempl;
GstCaps *padcaps, *ghostcaps, *newcaps;
padcaps = gst_caps_from_string ("some/caps");
fail_unless (padcaps != NULL);
ghostcaps = gst_caps_from_string ("some/caps;some/other-caps");
fail_unless (ghostcaps != NULL);
padtempl = gst_pad_template_new ("padtempl", GST_PAD_SINK,
GST_PAD_ALWAYS, padcaps);
fail_unless (padtempl != NULL);
ghosttempl = gst_pad_template_new ("ghosttempl", GST_PAD_SINK,
GST_PAD_ALWAYS, ghostcaps);
sinkpad = gst_pad_new_from_template (padtempl, "sinkpad");
fail_unless (sinkpad != NULL);
ghostpad = gst_ghost_pad_new_from_template ("ghostpad", sinkpad, ghosttempl);
fail_unless (ghostpad != NULL);
/* check template is properly set */
fail_unless (GST_PAD_PAD_TEMPLATE (ghostpad) == ghosttempl);
/* check ghostpad caps are from the sinkpad */
newcaps = gst_pad_get_caps (ghostpad, NULL);
fail_unless (newcaps != NULL);
fail_unless (gst_caps_is_equal (newcaps, padcaps));
gst_caps_unref (newcaps);
gst_caps_unref (padcaps);
gst_caps_unref (ghostcaps);
gst_object_unref (sinkpad);
gst_object_unref (ghostpad);
gst_object_unref (padtempl);
gst_object_unref (ghosttempl);
}
GST_END_TEST;
GST_START_TEST (test_ghost_pads_new_no_target_from_template)
{
GstPad *sinkpad, *ghostpad;
GstPadTemplate *padtempl, *ghosttempl;
GstCaps *padcaps, *ghostcaps, *newcaps;
padcaps = gst_caps_from_string ("some/caps");
fail_unless (padcaps != NULL);
ghostcaps = gst_caps_from_string ("some/caps;some/other-caps");
fail_unless (ghostcaps != NULL);
padtempl = gst_pad_template_new ("padtempl", GST_PAD_SINK,
GST_PAD_ALWAYS, padcaps);
fail_unless (padtempl != NULL);
ghosttempl = gst_pad_template_new ("ghosttempl", GST_PAD_SINK,
GST_PAD_ALWAYS, ghostcaps);
sinkpad = gst_pad_new_from_template (padtempl, "sinkpad");
fail_unless (sinkpad != NULL);
ghostpad = gst_ghost_pad_new_no_target_from_template ("ghostpad", ghosttempl);
fail_unless (ghostpad != NULL);
/* check template is properly set */
fail_unless (GST_PAD_PAD_TEMPLATE (ghostpad) == ghosttempl);
/* check ghostpad caps are from the ghostpad template */
newcaps = gst_pad_get_caps (ghostpad, NULL);
fail_unless (newcaps != NULL);
fail_unless (gst_caps_is_equal (newcaps, ghostcaps));
gst_caps_unref (newcaps);
fail_unless (gst_ghost_pad_set_target ((GstGhostPad *) ghostpad, sinkpad));
/* check ghostpad caps are now from the target pad */
newcaps = gst_pad_get_caps (ghostpad, NULL);
fail_unless (newcaps != NULL);
fail_unless (gst_caps_is_equal (newcaps, padcaps));
gst_caps_unref (newcaps);
gst_object_unref (sinkpad);
gst_object_unref (ghostpad);
gst_object_unref (padtempl);
gst_object_unref (ghosttempl);
gst_caps_unref (padcaps);
gst_caps_unref (ghostcaps);
}
GST_END_TEST;
static void
ghost_notify_caps (GObject * object, GParamSpec * pspec, gpointer * user_data)
{
GST_DEBUG ("caps notify called");
(*(gint *) user_data)++;
}
GST_START_TEST (test_ghost_pads_forward_setcaps)
{
GstCaps *templ_caps, *caps1, *caps2;
GstPadTemplate *src_template, *sink_template;
GstPad *src, *ghost, *sink;
gint notify_counter = 0;
templ_caps = gst_caps_from_string ("meh; muh");
src_template = gst_pad_template_new ("src", GST_PAD_SRC,
GST_PAD_ALWAYS, templ_caps);
gst_caps_unref (templ_caps);
templ_caps = gst_caps_from_string ("muh; meh");
sink_template = gst_pad_template_new ("sink", GST_PAD_SINK,
GST_PAD_ALWAYS, templ_caps);
gst_caps_unref (templ_caps);
src = gst_pad_new_from_template (src_template, "src");
sink = gst_pad_new_from_template (sink_template, "sink");
/* ghost source pad, setting caps on the source influences the caps of the
* ghostpad. */
ghost = gst_ghost_pad_new ("ghostsrc", src);
g_signal_connect (ghost, "notify::caps",
G_CALLBACK (ghost_notify_caps), &notify_counter);
fail_unless (gst_pad_link (ghost, sink) == GST_PAD_LINK_OK);
/* Activate pads for caps forwarding/setting to work */
gst_pad_set_active (src, TRUE);
gst_pad_set_active (ghost, TRUE);
caps1 = gst_caps_from_string ("meh");
fail_unless (gst_pad_set_caps (src, caps1));
caps2 = gst_pad_get_current_caps (ghost);
fail_unless (gst_caps_is_equal (caps1, caps2));
fail_unless_equals_int (notify_counter, 1);
gst_object_unref (ghost);
gst_caps_unref (caps1);
gst_caps_unref (caps2);
/* source 2, setting the caps on the ghostpad does not influence the caps of
* the target */
notify_counter = 0;
ghost = gst_ghost_pad_new ("ghostsrc", src);
g_signal_connect (ghost, "notify::caps",
G_CALLBACK (ghost_notify_caps), &notify_counter);
fail_unless (gst_pad_link (ghost, sink) == GST_PAD_LINK_OK);
gst_pad_set_active (ghost, TRUE);
gst_pad_set_active (sink, TRUE);
caps1 = gst_caps_from_string ("meh");
fail_unless (gst_pad_set_caps (ghost, caps1));
#if 0
caps2 = gst_pad_get_current_caps (src);
fail_unless (caps2 == NULL);
#endif
fail_unless_equals_int (notify_counter, 1);
gst_object_unref (ghost);
gst_caps_unref (caps1);
/* ghost sink pad. Setting caps on the ghostpad will also set those caps on
* the target pad. */
notify_counter = 0;
ghost = gst_ghost_pad_new ("ghostsink", sink);
g_signal_connect (ghost, "notify::caps",
G_CALLBACK (ghost_notify_caps), &notify_counter);
fail_unless (gst_pad_link (src, ghost) == GST_PAD_LINK_OK);
gst_pad_set_active (src, TRUE);
gst_pad_set_active (ghost, TRUE);
caps1 = gst_caps_from_string ("muh");
fail_unless (gst_pad_set_caps (ghost, caps1));
caps2 = gst_pad_get_current_caps (sink);
fail_unless (gst_caps_is_equal (caps1, caps2));
fail_unless_equals_int (notify_counter, 1);
gst_object_unref (ghost);
gst_caps_unref (caps1);
gst_caps_unref (caps2);
/* clear caps on pads */
gst_pad_set_active (src, FALSE);
gst_pad_set_active (src, TRUE);
gst_pad_set_active (sink, FALSE);
gst_pad_set_active (sink, TRUE);
/* sink pad 2, setting caps just on the target pad should not influence the caps
* on the ghostpad. */
notify_counter = 0;
ghost = gst_ghost_pad_new ("ghostsink", sink);
fail_unless (gst_pad_get_current_caps (ghost) == NULL);
g_signal_connect (ghost, "notify::caps",
G_CALLBACK (ghost_notify_caps), &notify_counter);
fail_unless (gst_pad_link (src, ghost) == GST_PAD_LINK_OK);
gst_pad_set_active (ghost, TRUE);
caps1 = gst_caps_from_string ("muh");
fail_unless (gst_pad_set_caps (sink, caps1));
caps2 = gst_pad_get_current_caps (ghost);
fail_unless (caps2 == NULL);
fail_unless_equals_int (notify_counter, 0);
gst_object_unref (ghost);
gst_caps_unref (caps1);
gst_object_unref (src);
gst_object_unref (sink);
gst_object_unref (src_template);
gst_object_unref (sink_template);
}
GST_END_TEST;
static gint linked_count1;
static gint unlinked_count1;
static gint linked_count2;
static gint unlinked_count2;
static GstPadLinkReturn
pad_linked1 (GstPad * pad, GstPad * peer)
{
linked_count1++;
return GST_PAD_LINK_OK;
}
static void
pad_unlinked1 (GstPad * pad)
{
unlinked_count1++;
}
static GstPadLinkReturn
pad_linked2 (GstPad * pad, GstPad * peer)
{
linked_count2++;
return GST_PAD_LINK_OK;
}
static void
pad_unlinked2 (GstPad * pad)
{
unlinked_count2++;
}
GST_START_TEST (test_ghost_pads_sink_link_unlink)
{
GstCaps *padcaps;
GstPad *srcpad, *sinkpad, *ghostpad;
GstPadTemplate *srctempl, *sinktempl;
GstPadLinkReturn ret;
gboolean res;
padcaps = gst_caps_from_string ("some/caps");
fail_unless (padcaps != NULL);
srctempl = gst_pad_template_new ("srctempl", GST_PAD_SRC,
GST_PAD_ALWAYS, padcaps);
gst_caps_unref (padcaps);
padcaps = gst_caps_from_string ("some/caps");
fail_unless (padcaps != NULL);
sinktempl = gst_pad_template_new ("sinktempl", GST_PAD_SINK,
GST_PAD_ALWAYS, padcaps);
gst_caps_unref (padcaps);
srcpad = gst_pad_new_from_template (srctempl, "src");
fail_unless (srcpad != NULL);
sinkpad = gst_pad_new_from_template (sinktempl, "sink");
fail_unless (sinkpad != NULL);
/* set up link/unlink functions for the pad */
linked_count1 = unlinked_count1 = 0;
gst_pad_set_link_function (sinkpad, pad_linked1);
gst_pad_set_unlink_function (sinkpad, pad_unlinked1);
linked_count2 = unlinked_count2 = 0;
gst_pad_set_link_function (srcpad, pad_linked2);
gst_pad_set_unlink_function (srcpad, pad_unlinked2);
/* this should trigger a link from the internal pad to the sinkpad */
ghostpad = gst_ghost_pad_new ("ghostpad", sinkpad);
fail_unless (ghostpad != NULL);
fail_unless (linked_count1 == 1);
fail_unless (unlinked_count1 == 0);
fail_unless (linked_count2 == 0);
fail_unless (unlinked_count2 == 0);
/* this should not trigger anything because we are not directly
* linking/unlinking the sink pad. */
ret = gst_pad_link (srcpad, ghostpad);
fail_unless (ret == GST_PAD_LINK_OK);
fail_unless (linked_count1 == 1);
fail_unless (unlinked_count1 == 0);
fail_unless (linked_count2 == 1);
fail_unless (unlinked_count2 == 0);
res = gst_pad_unlink (srcpad, ghostpad);
fail_unless (res == TRUE);
fail_unless (linked_count1 == 1);
fail_unless (unlinked_count1 == 0);
fail_unless (linked_count2 == 1);
fail_unless (unlinked_count2 == 1);
/* this should trigger the unlink */
res = gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (ghostpad), NULL);
fail_unless (res == TRUE);
fail_unless (linked_count1 == 1);
fail_unless (unlinked_count1 == 1);
fail_unless (linked_count2 == 1);
fail_unless (unlinked_count2 == 1);
gst_object_unref (ghostpad);
gst_object_unref (sinkpad);
gst_object_unref (srcpad);
gst_object_unref (srctempl);
gst_object_unref (sinktempl);
}
GST_END_TEST;
GST_START_TEST (test_ghost_pads_src_link_unlink)
{
GstCaps *padcaps;
GstPad *srcpad, *sinkpad, *ghostpad, *dummy;
GstPadTemplate *srctempl, *sinktempl;
GstPadLinkReturn ret;
gboolean res;
padcaps = gst_caps_from_string ("some/caps");
fail_unless (padcaps != NULL);
srctempl = gst_pad_template_new ("srctempl", GST_PAD_SRC,
GST_PAD_ALWAYS, padcaps);
gst_caps_unref (padcaps);
padcaps = gst_caps_from_string ("some/caps");
fail_unless (padcaps != NULL);
sinktempl = gst_pad_template_new ("sinktempl", GST_PAD_SINK,
GST_PAD_ALWAYS, padcaps);
gst_caps_unref (padcaps);
srcpad = gst_pad_new_from_template (srctempl, "src");
fail_unless (srcpad != NULL);
sinkpad = gst_pad_new_from_template (sinktempl, "sink");
fail_unless (sinkpad != NULL);
/* set up link/unlink functions for the pad */
linked_count1 = unlinked_count1 = 0;
gst_pad_set_link_function (srcpad, pad_linked1);
gst_pad_set_unlink_function (srcpad, pad_unlinked1);
linked_count2 = unlinked_count2 = 0;
gst_pad_set_link_function (sinkpad, pad_linked2);
gst_pad_set_unlink_function (sinkpad, pad_unlinked2);
/* this should trigger a link from the internal pad to the srcpad */
ghostpad = gst_ghost_pad_new ("ghostpad", srcpad);
fail_unless (ghostpad != NULL);
fail_unless (linked_count1 == 1);
fail_unless (unlinked_count1 == 0);
fail_unless (linked_count2 == 0);
fail_unless (unlinked_count2 == 0);
/* this should fail with a critial */
ASSERT_CRITICAL (dummy = gst_ghost_pad_new ("ghostpad", srcpad));
fail_unless (dummy == NULL);
fail_unless (linked_count1 == 1);
fail_unless (unlinked_count1 == 0);
fail_unless (linked_count2 == 0);
fail_unless (unlinked_count2 == 0);
/* this should not trigger anything because we are not directly
* linking/unlinking the src pad. */
ret = gst_pad_link (ghostpad, sinkpad);
fail_unless (ret == GST_PAD_LINK_OK);
fail_unless (linked_count1 == 1);
fail_unless (unlinked_count1 == 0);
fail_unless (linked_count2 == 1);
fail_unless (unlinked_count2 == 0);
/* this link should fail because we are already linked. Let's make sure the
* link functions are not called */
ret = gst_pad_link (ghostpad, sinkpad);
fail_unless (ret == GST_PAD_LINK_WAS_LINKED);
fail_unless (linked_count1 == 1);
fail_unless (unlinked_count1 == 0);
fail_unless (linked_count2 == 1);
fail_unless (unlinked_count2 == 0);
res = gst_pad_unlink (ghostpad, sinkpad);
fail_unless (res == TRUE);
fail_unless (linked_count1 == 1);
fail_unless (unlinked_count1 == 0);
fail_unless (linked_count2 == 1);
fail_unless (unlinked_count2 == 1);
res = gst_pad_unlink (ghostpad, sinkpad);
fail_unless (res == FALSE);
fail_unless (linked_count1 == 1);
fail_unless (unlinked_count1 == 0);
fail_unless (linked_count2 == 1);
fail_unless (unlinked_count2 == 1);
/* this should trigger the unlink function */
res = gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (ghostpad), NULL);
fail_unless (res == TRUE);
fail_unless (linked_count1 == 1);
fail_unless (unlinked_count1 == 1);
fail_unless (linked_count2 == 1);
fail_unless (unlinked_count2 == 1);
/* and this the link function again */
res = gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (ghostpad), srcpad);
fail_unless (res == TRUE);
fail_unless (linked_count1 == 2);
fail_unless (unlinked_count1 == 1);
fail_unless (linked_count2 == 1);
fail_unless (unlinked_count2 == 1);
gst_object_unref (ghostpad);
gst_object_unref (sinkpad);
gst_object_unref (srcpad);
gst_object_unref (srctempl);
gst_object_unref (sinktempl);
}
GST_END_TEST;
GST_START_TEST (test_ghost_pads_change_when_linked)
{
GstElement *b1, *b2, *src, *fmt, *sink1, *sink2;
GstPad *sinkpad, *ghostpad;
GstCaps *caps;
b1 = gst_element_factory_make ("pipeline", NULL);
b2 = gst_element_factory_make ("bin", NULL);
src = gst_element_factory_make ("fakesrc", NULL);
fmt = gst_element_factory_make ("capsfilter", NULL);
sink1 = gst_element_factory_make ("fakesink", NULL);
sink2 = gst_element_factory_make ("fakesink", NULL);
gst_bin_add (GST_BIN (b2), sink1);
gst_bin_add (GST_BIN (b2), sink2);
gst_bin_add (GST_BIN (b1), src);
gst_bin_add (GST_BIN (b1), fmt);
gst_bin_add (GST_BIN (b1), b2);
caps = gst_caps_from_string ("audio/x-raw-int, width=16, channels=1");
g_object_set (fmt, "caps", caps, NULL);
gst_caps_unref (caps);
/* create the ghostpad as a sink-pad for bin 2 */
ghostpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
gst_element_add_pad (b2, ghostpad);
sinkpad = gst_element_get_static_pad (sink1, "sink");
fail_unless (gst_ghost_pad_set_target ((GstGhostPad *) ghostpad, sinkpad));
gst_object_unref (sinkpad);
fail_unless (gst_element_link_many (src, fmt, b2, NULL));
/* set different target after ghostpad is linked */
sinkpad = gst_element_get_static_pad (sink2, "sink");
fail_unless (gst_ghost_pad_set_target ((GstGhostPad *) ghostpad, sinkpad));
gst_object_unref (sinkpad);
/* clean up */
gst_object_unref (b1);
}
GST_END_TEST;
/* test that setting a ghostpad proxy pad as ghostpad target automatically set
* both ghostpad targets.
*
* fakesrc ! ( ) ! fakesink
*/
GST_START_TEST (test_ghost_pads_internal_link)
{
GstElement *pipeline, *src, *bin, *sink;
GstPad *sinkpad, *srcpad, *target;
GstProxyPad *proxypad;
pipeline = gst_element_factory_make ("pipeline", NULL);
bin = gst_element_factory_make ("bin", NULL);
src = gst_element_factory_make ("fakesrc", NULL);
sink = gst_element_factory_make ("fakesink", NULL);
gst_bin_add (GST_BIN (pipeline), src);
gst_bin_add (GST_BIN (pipeline), bin);
gst_bin_add (GST_BIN (pipeline), sink);
/* create the sink ghostpad */
sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
proxypad = gst_proxy_pad_get_internal (GST_PROXY_PAD (sinkpad));
gst_element_add_pad (bin, sinkpad);
/* create the src ghostpad and link it to sink proxypad */
srcpad = gst_ghost_pad_new ("src", GST_PAD (proxypad));
gst_object_unref (proxypad);
gst_element_add_pad (bin, srcpad);
fail_unless (gst_element_link_many (src, bin, sink, NULL));
/* Check that both targets are set, and point to each other */
target = gst_ghost_pad_get_target (GST_GHOST_PAD (sinkpad));
fail_if (target == NULL);
proxypad = gst_proxy_pad_get_internal (GST_PROXY_PAD (srcpad));
fail_unless (target == GST_PAD (proxypad));
gst_object_unref (target);
gst_object_unref (proxypad);
target = gst_ghost_pad_get_target (GST_GHOST_PAD (srcpad));
fail_if (target == NULL);
proxypad = gst_proxy_pad_get_internal (GST_PROXY_PAD (sinkpad));
fail_unless (target == GST_PAD (proxypad));
gst_object_unref (target);
gst_object_unref (proxypad);
/* clean up */
gst_object_unref (pipeline);
}
GST_END_TEST;
static Suite *
gst_ghost_pad_suite (void)
{
Suite *s = suite_create ("GstGhostPad");
TCase *tc_chain = tcase_create ("ghost pad tests");
suite_add_tcase (s, tc_chain);
tcase_add_test (tc_chain, test_remove1);
tcase_add_test (tc_chain, test_remove2);
tcase_add_test (tc_chain, test_remove_target);
tcase_add_test (tc_chain, test_link);
tcase_add_test (tc_chain, test_ghost_pads);
tcase_add_test (tc_chain, test_ghost_pads_bin);
tcase_add_test (tc_chain, test_ghost_pads_notarget);
tcase_add_test (tc_chain, test_ghost_pads_block);
tcase_add_test (tc_chain, test_ghost_pads_probes);
tcase_add_test (tc_chain, test_ghost_pads_new_from_template);
tcase_add_test (tc_chain, test_ghost_pads_new_no_target_from_template);
tcase_add_test (tc_chain, test_ghost_pads_forward_setcaps);
tcase_add_test (tc_chain, test_ghost_pads_sink_link_unlink);
tcase_add_test (tc_chain, test_ghost_pads_src_link_unlink);
tcase_add_test (tc_chain, test_ghost_pads_change_when_linked);
tcase_add_test (tc_chain, test_ghost_pads_internal_link);
return s;
}
GST_CHECK_MAIN (gst_ghost_pad);