gnl: Add the srcpad directly to GnlObject

Starting from now we will not claim that we support GnlObject that have
several source pads as this is
1- Not true at all;
2- the design of priorities in the GnlComposition tree does not allow that;
3- Not very useful in most of the cases and it complexifies quite a lot the code
   in the composition.

Conflicts:
	configure.ac
	tests/check/Makefile.am
This commit is contained in:
Mathieu Duponchelle 2014-06-24 13:44:13 +02:00 committed by Thibault Saunier
parent 38b080deb3
commit 4cb834fa21
14 changed files with 438 additions and 967 deletions

File diff suppressed because it is too large Load diff

View file

@ -35,6 +35,8 @@ struct _GnlPadPrivate
GstPadDirection dir;
GstPadEventFunction eventfunc;
GstPadQueryFunction queryfunc;
GstEvent *pending_seek;
};
static GstEvent *
@ -489,7 +491,18 @@ ghostpad_event_function (GstPad * ghostpad, GstObject * parent,
{
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEEK:
{
GstPad *target;
event = translate_incoming_seek (object, event);
if (!(target = gst_ghost_pad_get_target (GST_GHOST_PAD (ghostpad)))) {
priv->pending_seek = event;
GST_INFO_OBJECT (ghostpad, "No target set yet, "
"Will send the seek event when the target is set");
ret = TRUE;
event = NULL;
}
}
break;
default:
break;
@ -661,7 +674,7 @@ gnl_object_ghost_pad (GnlObject * object, const gchar * name, GstPad * target)
g_return_val_if_fail (target, FALSE);
g_return_val_if_fail ((dir != GST_PAD_UNKNOWN), FALSE);
ghost = gnl_object_ghost_pad_no_target (object, name, dir);
ghost = gnl_object_ghost_pad_no_target (object, name, dir, NULL);
if (!ghost) {
GST_WARNING_OBJECT (object, "Couldn't create ghostpad");
return NULL;
@ -692,17 +705,19 @@ gnl_object_ghost_pad (GnlObject * object, const gchar * name, GstPad * target)
*/
GstPad *
gnl_object_ghost_pad_no_target (GnlObject * object, const gchar * name,
GstPadDirection dir)
GstPadDirection dir, GstPadTemplate * template)
{
GstPad *ghost;
GnlPadPrivate *priv;
/* create a no_target ghostpad */
ghost = gst_ghost_pad_new_no_target (name, dir);
if (template)
ghost = gst_ghost_pad_new_no_target_from_template (name, template);
else
ghost = gst_ghost_pad_new_no_target (name, dir);
if (!ghost)
return NULL;
GST_DEBUG ("grabbing existing pad functions");
/* remember the existing ghostpad event/query/link/unlink functions */
priv = g_slice_new0 (GnlPadPrivate);
@ -726,6 +741,8 @@ gnl_object_ghost_pad_no_target (GnlObject * object, const gchar * name,
return ghost;
}
void
gnl_object_remove_ghost_pad (GnlObject * object, GstPad * ghost)
{
@ -747,16 +764,32 @@ gnl_object_ghost_pad_set_target (GnlObject * object, GstPad * ghost,
GnlPadPrivate *priv = gst_pad_get_element_private (ghost);
g_return_val_if_fail (priv, FALSE);
g_return_val_if_fail (GST_IS_PAD (ghost), FALSE);
if (target)
GST_DEBUG_OBJECT (object, "setting target %s:%s on ghostpad",
GST_DEBUG_PAD_NAME (target));
else
GST_DEBUG_OBJECT (object, "removing target from ghostpad");
if (target) {
GST_DEBUG_OBJECT (object, "setting target %s:%s on %s:%s",
GST_DEBUG_PAD_NAME (target), GST_DEBUG_PAD_NAME (ghost));
} else {
GST_ERROR_OBJECT (object, "removing target from ghostpad");
priv->pending_seek = NULL;
}
/* set target */
if (!(gst_ghost_pad_set_target (GST_GHOST_PAD (ghost), target)))
if (!(gst_ghost_pad_set_target (GST_GHOST_PAD (ghost), target))) {
GST_WARNING_OBJECT (priv->object, "Could not set ghost %s:%s "
"target to: %s:%s", GST_DEBUG_PAD_NAME (ghost),
GST_DEBUG_PAD_NAME (target));
return FALSE;
}
if (target && priv->pending_seek) {
gboolean res = gst_pad_send_event (ghost, priv->pending_seek);
GST_INFO_OBJECT (object, "Sending our pending seek event: %" GST_PTR_FORMAT
" -- Result is %i", priv->pending_seek, res);
priv->pending_seek = NULL;
}
return TRUE;
}

View file

@ -33,7 +33,7 @@ GstPad *gnl_object_ghost_pad (GnlObject * object,
const gchar * name, GstPad * target);
GstPad *gnl_object_ghost_pad_no_target (GnlObject * object,
const gchar * name, GstPadDirection dir);
const gchar * name, GstPadDirection dir, GstPadTemplate *templ);
gboolean gnl_object_ghost_pad_set_target (GnlObject * object,
GstPad * ghost, GstPad * target);

View file

@ -42,10 +42,7 @@
GST_DEBUG_CATEGORY_STATIC (gnlobject_debug);
#define GST_CAT_DEFAULT gnlobject_debug
#define _do_init \
GST_DEBUG_CATEGORY_INIT (gnlobject_debug, "gnlobject", GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin Object base class");
#define gnl_object_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GnlObject, gnl_object, GST_TYPE_BIN, _do_init);
static GObjectClass *parent_class = NULL;
/****************************************************
* Helper macros *
@ -117,6 +114,9 @@ gnl_object_class_init (GnlObjectClass * klass)
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gnlobject_class = (GnlObjectClass *) klass;
GST_DEBUG_CATEGORY_INIT (gnlobject_debug, "gnlobject",
GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin object");
parent_class = g_type_class_ref (GST_TYPE_BIN);
gobject_class->set_property = GST_DEBUG_FUNCPTR (gnl_object_set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR (gnl_object_get_property);
@ -243,7 +243,7 @@ gnl_object_class_init (GnlObjectClass * klass)
}
static void
gnl_object_init (GnlObject * object)
gnl_object_init (GnlObject * object, GnlObjectClass * klass)
{
object->start = object->pending_start = 0;
object->duration = object->pending_duration = 0;
@ -258,6 +258,12 @@ gnl_object_init (GnlObject * object)
object->segment_rate = 1.0;
object->segment_start = -1;
object->segment_stop = -1;
object->srcpad = gnl_object_ghost_pad_no_target (object,
"src", GST_PAD_SRC,
gst_element_class_get_pad_template ((GstElementClass *) klass, "src"));
gst_element_add_pad (GST_ELEMENT (object), object->srcpad);
}
static void
@ -653,3 +659,30 @@ gnl_object_reset (GnlObject * object)
object->priority = 0;
object->active = TRUE;
}
GType
gnl_object_get_type (void)
{
static volatile gsize type = 0;
if (g_once_init_enter (&type)) {
GType _type;
static const GTypeInfo info = {
sizeof (GnlObjectClass),
NULL,
NULL,
(GClassInitFunc) gnl_object_class_init,
NULL,
NULL,
sizeof (GnlObject),
0,
(GInstanceInitFunc) gnl_object_init,
};
_type = g_type_register_static (GST_TYPE_BIN,
"GnlObject", &info, G_TYPE_FLAG_ABSTRACT);
g_once_init_leave (&type, _type);
}
return type;
}

View file

@ -43,6 +43,8 @@ G_BEGIN_DECLS
#define GNL_IS_OBJECT_CLASS(obj) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GNL_TYPE_OBJECT))
#define GNL_OBJECT_SRC(obj) (((GnlObject *) obj)->srcpad)
/**
* GnlObjectFlags:
* @GNL_OBJECT_IS_SOURCE:
@ -84,6 +86,8 @@ struct _GnlObject
{
GstBin parent;
GstPad *srcpad;
/* Time positionning */
GstClockTime start;
GstClockTime inpoint;

View file

@ -40,7 +40,7 @@
static GstStaticPadTemplate gnl_operation_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_SOMETIMES,
GST_PAD_ALWAYS,
GST_STATIC_CAPS_ANY);
static GstStaticPadTemplate gnl_operation_sink_template =
@ -163,10 +163,9 @@ gnl_operation_dispose (GObject * object)
GnlOperation *oper = (GnlOperation *) object;
GST_DEBUG_OBJECT (object, "Disposing of source pad");
if (oper->ghostpad) {
gnl_object_remove_ghost_pad (GNL_OBJECT (oper), oper->ghostpad);
oper->ghostpad = NULL;
}
gnl_object_ghost_pad_set_target (GNL_OBJECT (object),
GNL_OBJECT (object)->srcpad, NULL);
GST_DEBUG_OBJECT (object, "Disposing of sink pad(s)");
while (oper->sinks) {
@ -190,7 +189,6 @@ static void
gnl_operation_init (GnlOperation * operation)
{
gnl_operation_reset (operation);
operation->ghostpad = NULL;
operation->element = NULL;
}
@ -362,13 +360,8 @@ gnl_operation_add_element (GstBin * bin, GstElement * element)
operation->element = element;
operation->dynamicsinks = isdynamic;
/* Source ghostpad */
if (operation->ghostpad)
gnl_object_ghost_pad_set_target (GNL_OBJECT (operation),
operation->ghostpad, srcpad);
else
operation->ghostpad = gnl_object_ghost_pad (GNL_OBJECT (operation),
GST_PAD_NAME (srcpad), srcpad);
gnl_object_ghost_pad_set_target (GNL_OBJECT (operation),
GNL_OBJECT (operation)->srcpad, srcpad);
/* Remove the reference get_src_pad gave us */
gst_object_unref (srcpad);

View file

@ -59,8 +59,6 @@ G_BEGIN_DECLS
/* FIXME : We might need to use a lock to access this list */
GList * sinks; /* The sink ghostpads */
GstPad *ghostpad; /* src ghostpad */
GstElement *element; /* controlled element */
GstClockTime next_base_time;

View file

@ -35,7 +35,7 @@
static GstStaticPadTemplate gnl_source_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_SOMETIMES,
GST_PAD_ALWAYS,
GST_STATIC_CAPS_ANY);
GST_DEBUG_CATEGORY_STATIC (gnlsource);
@ -51,7 +51,6 @@ struct _GnlSourcePrivate
gboolean dispose_has_run;
gboolean dynamicpads; /* TRUE if the controlled element has dynamic pads */
GstPad *ghostpad; /* The source ghostpad */
GstEvent *event; /* queued event */
gulong padremovedid; /* signal handler for element pad-removed signal */
@ -59,9 +58,10 @@ struct _GnlSourcePrivate
gulong probeid; /* source pad probe id */
gboolean pendingblock; /* We have a pending pad_block */
gboolean areblocked; /* We already got blocked */
gboolean is_blocked; /* We already got blocked */
GstPad *ghostedpad; /* Pad (to be) ghosted */
GstPad *staticpad; /* The only pad. We keep an extra ref */
gboolean got_seeked;
};
static gboolean gnl_source_prepare (GnlObject * object);
@ -136,6 +136,7 @@ gnl_source_init (GnlSource * source)
static void
gnl_source_dispose (GObject * object)
{
GnlObject *gnlobject = (GnlObject *) object;
GnlSource *source = (GnlSource *) object;
GnlSourcePrivate *priv = source->priv;
@ -153,9 +154,8 @@ gnl_source_dispose (GObject * object)
if (priv->event)
gst_event_unref (priv->event);
if (priv->ghostpad)
gnl_object_remove_ghost_pad ((GnlObject *) object, priv->ghostpad);
priv->ghostpad = NULL;
if (priv->ghostedpad)
gnl_object_ghost_pad_set_target (gnlobject, gnlobject->srcpad, NULL);
if (priv->staticpad) {
gst_object_unref (priv->staticpad);
@ -171,19 +171,20 @@ element_pad_added_cb (GstElement * element G_GNUC_UNUSED, GstPad * pad,
{
GstCaps *srccaps;
GnlSourcePrivate *priv = source->priv;
GnlObject *gnlobject = (GnlObject *) source;
GST_DEBUG_OBJECT (source, "pad %s:%s", GST_DEBUG_PAD_NAME (pad));
if (priv->ghostpad || priv->pendingblock) {
if (priv->pendingblock) {
GST_WARNING_OBJECT (source,
"We already have (pending) ghost-ed a valid source pad (ghostpad:%s:%s, pendingblock:%d",
GST_DEBUG_PAD_NAME (priv->ghostpad), priv->pendingblock);
"We already have (pending) ghost-ed a valid source pad (srcpad:%s:%s, pendingblock:%d",
GST_DEBUG_PAD_NAME (gnlobject->srcpad), priv->pendingblock);
return;
}
/* FIXME: pass filter caps to query_caps directly */
srccaps = gst_pad_query_caps (pad, NULL);
if (!gst_caps_can_intersect (srccaps, GNL_OBJECT (source)->caps)) {
if (gnlobject->caps && !gst_caps_can_intersect (srccaps, gnlobject->caps)) {
gst_caps_unref (srccaps);
GST_DEBUG_OBJECT (source, "Pad doesn't have valid caps, ignoring");
return;
@ -210,6 +211,7 @@ element_pad_removed_cb (GstElement * element G_GNUC_UNUSED, GstPad * pad,
GnlSource * source)
{
GnlSourcePrivate *priv = source->priv;
GnlObject *gnlobject = (GnlObject *) source;
GST_DEBUG_OBJECT (source, "pad %s:%s (controlled pad %s:%s)",
GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (priv->ghostedpad));
@ -218,19 +220,17 @@ element_pad_removed_cb (GstElement * element G_GNUC_UNUSED, GstPad * pad,
GST_DEBUG_OBJECT (source,
"The removed pad is the controlled pad, clearing up");
if (priv->ghostpad) {
GST_DEBUG_OBJECT (source, "Clearing up ghostpad");
GST_DEBUG_OBJECT (source, "Clearing up ghostpad");
priv->areblocked = FALSE;
if (priv->probeid) {
gst_pad_remove_probe (pad, priv->probeid);
priv->probeid = 0;
}
gnl_object_remove_ghost_pad ((GnlObject *) source, priv->ghostpad);
priv->ghostpad = NULL;
priv->is_blocked = FALSE;
if (priv->probeid) {
gst_pad_remove_probe (pad, priv->probeid);
priv->probeid = 0;
}
gnl_object_ghost_pad_set_target (GNL_OBJECT (source), gnlobject->srcpad,
NULL);
priv->pendingblock = FALSE;
priv->ghostedpad = NULL;
} else {
@ -292,20 +292,24 @@ ghost_seek_pad (GnlSource * source)
{
GnlSourcePrivate *priv = source->priv;
GstPad *pad = priv->ghostedpad;
GnlObject *gnlobject = (GnlObject *) source;
if (priv->ghostpad || !pad)
priv->got_seeked = TRUE;
if (!pad)
goto beach;
GST_DEBUG_OBJECT (source, "ghosting %s:%s", GST_DEBUG_PAD_NAME (pad));
priv->ghostpad = gnl_object_ghost_pad ((GnlObject *) source,
GST_PAD_NAME (pad), pad);
gnl_object_ghost_pad_set_target (gnlobject, gnlobject->srcpad, pad);
GST_DEBUG_OBJECT (source, "emitting no more pads");
gst_pad_set_active (priv->ghostpad, TRUE);
/*FIXME : do that when going to PAUSED */
gst_pad_set_active (gnlobject->srcpad, TRUE);
if (priv->event) {
GST_DEBUG_OBJECT (source, "sending queued seek event");
if (!(gst_pad_send_event (priv->ghostpad, priv->event)))
if (!(gst_pad_send_event (gnlobject->srcpad, priv->event)))
GST_ELEMENT_ERROR (source, RESOURCE, SEEK,
(NULL), ("Sending initial seek to upstream element failed"));
else
@ -314,7 +318,7 @@ ghost_seek_pad (GnlSource * source)
}
GST_DEBUG_OBJECT (source, "about to unblock %s:%s", GST_DEBUG_PAD_NAME (pad));
priv->areblocked = FALSE;
priv->is_blocked = FALSE;
if (priv->probeid) {
gst_pad_remove_probe (pad, priv->probeid);
priv->probeid = 0;
@ -332,10 +336,10 @@ pad_blocked_cb (GstPad * pad, GstPadProbeInfo * info, GnlSource * source)
{
GST_DEBUG_OBJECT (pad, "probe callback");
if (!source->priv->ghostpad && !source->priv->areblocked) {
if (!source->priv->got_seeked && !source->priv->is_blocked) {
GThread *lthread;
source->priv->areblocked = TRUE;
source->priv->is_blocked = TRUE;
GST_DEBUG_OBJECT (pad, "starting thread to call ghost_seek_pad");
lthread =
g_thread_new ("gnlsourceseek", (GThreadFunc) ghost_seek_pad, source);
@ -439,6 +443,7 @@ static gboolean
gnl_source_remove_element (GstBin * bin, GstElement * element)
{
GnlSource *source = (GnlSource *) bin;
GnlObject *gnlobject = (GnlObject *) element;
GnlSourcePrivate *priv = source->priv;
gboolean pret;
@ -452,11 +457,9 @@ gnl_source_remove_element (GstBin * bin, GstElement * element)
}
if (pret) {
/* remove ghostpad */
if (priv->ghostpad) {
gnl_object_remove_ghost_pad ((GnlObject *) bin, priv->ghostpad);
priv->ghostpad = NULL;
}
gnl_object_ghost_pad_set_target (GNL_OBJECT (source), gnlobject->srcpad,
NULL);
priv->got_seeked = FALSE;
/* discard events */
if (priv->event) {
@ -485,12 +488,13 @@ static gboolean
gnl_source_send_event (GstElement * element, GstEvent * event)
{
GnlSource *source = (GnlSource *) element;
GnlObject *gnlobject = (GnlObject *) element;
gboolean res = TRUE;
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEEK:
if (source->priv->ghostpad)
res = gst_pad_send_event (source->priv->ghostpad, event);
if (source->priv->ghostedpad)
res = gst_pad_send_event (gnlobject->srcpad, event);
else {
if (source->priv->event)
gst_event_unref (source->priv->event);
@ -516,13 +520,15 @@ gnl_source_prepare (GnlObject * object)
if (!source->element) {
GST_WARNING_OBJECT (source,
"GnlSource doesn't have an element to control !");
if (parent)
gst_object_unref (parent);
return FALSE;
}
GST_LOG_OBJECT (source, "ghostpad:%p, dynamicpads:%d",
priv->ghostpad, priv->dynamicpads);
GST_LOG_OBJECT (source, "srcpad:%p, dynamicpads:%d",
object->srcpad, priv->dynamicpads);
if (!(priv->ghostpad) && !priv->pendingblock) {
if (!(priv->got_seeked) && !priv->pendingblock) {
GstPad *pad;
GST_LOG_OBJECT (source, "no ghostpad and no dynamic pads");
@ -568,22 +574,22 @@ gnl_source_cleanup (GnlObject * object)
GnlSource *source = GNL_SOURCE (object);
GnlSourcePrivate *priv = source->priv;
if (priv->ghostpad) {
GstPad *target = gst_ghost_pad_get_target ((GstGhostPad *) priv->ghostpad);
/* FIXME : should just be ghostedpad */
GstPad *target = gst_ghost_pad_get_target ((GstGhostPad *) object->srcpad);
if (target) {
if (priv->probeid) {
gst_pad_remove_probe (target, priv->probeid);
priv->probeid = 0;
}
gst_object_unref (target);
if (target) {
if (priv->probeid) {
gst_pad_remove_probe (target, priv->probeid);
priv->probeid = 0;
}
gnl_object_remove_ghost_pad ((GnlObject *) source, priv->ghostpad);
priv->ghostpad = NULL;
priv->ghostedpad = NULL;
priv->areblocked = FALSE;
priv->pendingblock = FALSE;
gst_object_unref (target);
}
gnl_object_ghost_pad_set_target (GNL_OBJECT (source), object->srcpad, NULL);
priv->got_seeked = FALSE;
priv->ghostedpad = NULL;
priv->is_blocked = FALSE;
priv->pendingblock = FALSE;
return TRUE;
}

View file

@ -26,8 +26,7 @@ fill_pipeline_and_check (GstElement * comp, GList * segments,
/* Expected segments */
collect->expected_segments = segments;
g_signal_connect (G_OBJECT (comp), "pad-added",
G_CALLBACK (composition_pad_added_cb), collect);
gst_element_link (comp, sink);
sinkpad = gst_element_get_static_pad (sink, "sink");
gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
@ -562,8 +561,7 @@ GST_START_TEST (test_renegotiation)
segment_new (1.0, GST_FORMAT_TIME,
2 * GST_SECOND, 3 * GST_SECOND, 2 * GST_SECOND));
g_signal_connect (G_OBJECT (comp), "pad-added",
G_CALLBACK (composition_pad_added_cb), collect);
gst_element_link (comp, audioconvert);
sinkpad = gst_element_get_static_pad (sink, "sink");
gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,

View file

@ -24,12 +24,7 @@ typedef struct
GstElement *source3;
} TestClosure;
static int composition_pad_added;
static int composition_pad_removed;
static int seek_events;
static gulong blockprobeid = 0;
static GMutex pad_added_lock;
static GCond pad_added_cond;
static GstPadProbeReturn
on_source1_pad_event_cb (GstPad * pad, GstPadProbeInfo * info,
@ -41,35 +36,9 @@ on_source1_pad_event_cb (GstPad * pad, GstPadProbeInfo * info,
return GST_PAD_PROBE_OK;
}
static void
on_source1_pad_added_cb (GstElement * source, GstPad * pad, gpointer user_data)
{
gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_UPSTREAM,
(GstPadProbeCallback) on_source1_pad_event_cb, NULL, NULL);
}
static void
on_composition_pad_added_cb (GstElement * composition, GstPad * pad,
GstElement * sink)
{
GstPad *s = gst_element_get_static_pad (sink, "sink");
gst_pad_link (pad, s);
++composition_pad_added;
g_mutex_lock (&pad_added_lock);
g_cond_broadcast (&pad_added_cond);
g_mutex_unlock (&pad_added_lock);
gst_object_unref (s);
}
static void
on_composition_pad_removed_cb (GstElement * composition, GstPad * pad,
GstElement * sink)
{
++composition_pad_removed;
}
GST_START_TEST (test_change_object_start_stop_in_current_stack)
{
GstPad *srcpad;
GstElement *pipeline;
GstElement *comp, *source1, *def, *sink;
GstBus *bus;
@ -84,11 +53,7 @@ GST_START_TEST (test_change_object_start_stop_in_current_stack)
sink = gst_element_factory_make_or_warn ("fakesink", "sink");
gst_bin_add_many (GST_BIN (pipeline), comp, sink, NULL);
/* connect to pad-added */
g_object_connect (comp, "signal::pad-added",
on_composition_pad_added_cb, sink, NULL);
g_object_connect (comp, "signal::pad-removed",
on_composition_pad_removed_cb, NULL, NULL);
gst_element_link (comp, sink);
/*
source1
@ -98,8 +63,9 @@ GST_START_TEST (test_change_object_start_stop_in_current_stack)
*/
source1 = videotest_gnl_src ("source1", 0, 2 * GST_SECOND, 2, 2);
g_object_connect (source1, "signal::pad-added",
on_source1_pad_added_cb, NULL, NULL);
srcpad = gst_element_get_static_pad (source1, "src");
gst_pad_add_probe (srcpad, GST_PAD_PROBE_TYPE_EVENT_UPSTREAM,
(GstPadProbeCallback) on_source1_pad_event_cb, NULL, NULL);
/*
def (default source)
@ -161,9 +127,6 @@ GST_START_TEST (test_change_object_start_stop_in_current_stack)
}
}
fail_unless_equals_int (composition_pad_added, 1);
fail_unless_equals_int (composition_pad_removed, 0);
seek_events_before = seek_events;
/* pipeline is paused at this point */
@ -171,15 +134,13 @@ GST_START_TEST (test_change_object_start_stop_in_current_stack)
/* move source1 out of the active segment */
g_object_set (source1, "start", (guint64) 4 * GST_SECOND, NULL);
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
fail_unless (seek_events > seek_events_before);
fail_unless (seek_events > seek_events_before, "%i > %i", seek_events,
seek_events_before);
/* remove source1 from the composition, which will become empty and remove the
* ghostpad */
gst_bin_remove (GST_BIN (comp), source1);
fail_unless_equals_int (composition_pad_added, 1);
fail_unless_equals_int (composition_pad_removed, 1);
g_object_set (source1, "start", (guint64) 0 * GST_SECOND, NULL);
/* add the source again and check that the ghostpad is added again */
gst_bin_add (GST_BIN (comp), source1);
@ -234,177 +195,6 @@ GST_START_TEST (test_remove_invalid_object)
GST_END_TEST;
static GstPadProbeReturn
pad_block (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
{
GstPad *ghost;
GstBin *bin;
bin = GST_BIN (user_data);
GST_DEBUG_OBJECT (pad, "probe type:0x%x", GST_PAD_PROBE_INFO_TYPE (info));
ghost = gst_ghost_pad_new ("src", pad);
gst_pad_set_active (ghost, TRUE);
gst_element_add_pad (GST_ELEMENT (bin), ghost);
return GST_PAD_PROBE_REMOVE;
}
static void
no_more_pads_test_cb (GObject * object, TestClosure * c)
{
gboolean ret;
GST_WARNING ("NO MORE PADS");
gst_bin_add (GST_BIN (c->composition), c->source3);
g_signal_emit_by_name (c->composition, "commit", TRUE, &ret);
}
GST_START_TEST (test_no_more_pads_race)
{
gboolean ret;
GstElement *source1, *source2, *source3;
GstBin *bin;
GstElement *videotestsrc1, *videotestsrc2;
GstElement *operation;
GstElement *composition;
GstElement *videomixer, *fakesink;
GstElement *pipeline;
GstBus *bus;
GstMessage *message;
GstPad *pad;
TestClosure closure;
/* We create a composition with an operation and three sources. The operation
* contains a videomixer instance and the three sources are videotestsrc's.
*
* One of the sources, source2, contains videotestsrc inside a bin. Initially
* the bin doesn't have a source pad. We do this to exercise the dynamic src
* pad code path in gnlcomposition. We block on the videotestsrc srcpad and in
* the pad block callback we ghost the pad and add the ghost to the parent
* bin. This makes gnlsource emit no-more-pads, which is used by
* gnlcomposition to link the source2:src pad to videomixer.
*
* We start with the composition containing operation and source1. We preroll
* and then add source2. Source2 will do what described above and emit
* no-more-pads. We connect to that no-more-pads and from there we add source3 to
* the composition. Adding a new source will make gnlcomposition deactivate
* the old stack and activate a new one. The new one contains operation,
* source1, source2 and source3. Source2 was active in the old stack as well and
* gnlcomposition is *still waiting* for no-more-pads to be emitted on it
* (since the no-more-pads emission is now blocked in our test's no-more-pads
* callback, calling gst_bin_add). In short, here, we're simulating a race between
* no-more-pads and someone modifying the composition.
*
* Activating the new stack, gnlcomposition calls compare_relink_single_node,
* which finds an existing source pad for source2 this time since we have
* already blocked and ghosted. It takes another code path that assumes that
* source2 doesn't have dynamic pads and *BOOM*.
*/
pipeline = GST_ELEMENT (gst_pipeline_new (NULL));
bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
composition = gst_element_factory_make ("gnlcomposition", "composition");
fakesink = gst_element_factory_make ("fakesink", NULL);
fail_unless (fakesink != NULL);
g_object_set (fakesink, "sync", TRUE, NULL);
/* operation */
operation = gst_element_factory_make ("gnloperation", "operation");
videomixer = gst_element_factory_make ("videomixer", "videomixer");
fail_unless (videomixer != NULL);
gst_bin_add (GST_BIN (operation), videomixer);
g_object_set (operation, "start", (guint64) 0 * GST_SECOND,
"duration", (guint64) 10 * GST_SECOND,
"inpoint", (guint64) 0 * GST_SECOND, "priority", 10, NULL);
gst_bin_add (GST_BIN (composition), operation);
/* source 1 */
source1 = gst_element_factory_make ("gnlsource", "source1");
videotestsrc1 = gst_element_factory_make ("videotestsrc", "videotestsrc1");
gst_bin_add (GST_BIN (source1), videotestsrc1);
g_object_set (source1, "start", (guint64) 0 * GST_SECOND, "duration",
(guint64) 5 * GST_SECOND, "inpoint", (guint64) 0 * GST_SECOND, "priority",
20, NULL);
/* source2 */
source2 = gst_element_factory_make ("gnlsource", "source2");
bin = GST_BIN (gst_bin_new (NULL));
videotestsrc2 = gst_element_factory_make ("videotestsrc", "videotestsrc2");
pad = gst_element_get_static_pad (videotestsrc2, "src");
blockprobeid =
gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
(GstPadProbeCallback) pad_block, bin, NULL);
gst_bin_add (bin, videotestsrc2);
gst_bin_add (GST_BIN (source2), GST_ELEMENT (bin));
g_object_set (source2, "start", (guint64) 0 * GST_SECOND, "duration",
(guint64) 5 * GST_SECOND, "inpoint", (guint64) 0 * GST_SECOND, "priority",
20, NULL);
/* source3 */
source3 = gst_element_factory_make ("gnlsource", "source3");
videotestsrc2 = gst_element_factory_make ("videotestsrc", "videotestsrc3");
gst_bin_add (GST_BIN (source3), videotestsrc2);
g_object_set (source3, "start", (guint64) 0 * GST_SECOND, "duration",
(guint64) 5 * GST_SECOND, "inpoint", (guint64) 0 * GST_SECOND, "priority",
20, NULL);
closure.composition = composition;
closure.source3 = source3;
g_object_connect (source2, "signal::no-more-pads",
no_more_pads_test_cb, &closure, NULL);
gst_bin_add (GST_BIN (composition), source1);
g_signal_emit_by_name (composition, "commit", TRUE, &ret);
g_object_connect (composition, "signal::pad-added",
on_composition_pad_added_cb, fakesink, NULL);
g_object_connect (composition, "signal::pad-removed",
on_composition_pad_removed_cb, NULL, NULL);
GST_DEBUG ("Adding composition to pipeline");
gst_bin_add_many (GST_BIN (pipeline), composition, fakesink, NULL);
GST_DEBUG ("Setting pipeline to PAUSED");
fail_if (gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PAUSED)
== GST_STATE_CHANGE_FAILURE);
message = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
GST_MESSAGE_ASYNC_DONE | GST_MESSAGE_ERROR);
if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR) {
fail_error_message (message);
}
gst_message_unref (message);
GST_DEBUG ("Adding second source");
/* FIXME: maybe slow down the videotestsrc steaming thread */
gst_bin_add (GST_BIN (composition), source2);
g_signal_emit_by_name (composition, "commit", TRUE, &ret);
message =
gst_bus_timed_pop_filtered (bus, GST_SECOND / 10, GST_MESSAGE_ERROR);
if (message) {
if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR) {
fail_error_message (message);
} else {
fail_if (TRUE);
}
gst_message_unref (message);
}
gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL);
gst_object_unref (pipeline);
gst_object_unref (bus);
}
GST_END_TEST;
GST_START_TEST (test_simple_adder)
{
GstBus *bus;
@ -452,19 +242,13 @@ GST_START_TEST (test_simple_adder)
g_object_set (gnlsource2, "start", (guint64) 0 * GST_SECOND,
"duration", total_time, "inpoint", (guint64) 0 * GST_SECOND, "priority",
2, NULL);
fail_unless (gst_bin_add (GST_BIN (composition), gnlsource2));
/* Connecting signals */
g_object_connect (composition, "signal::pad-added",
on_composition_pad_added_cb, fakesink, NULL);
g_object_connect (composition, "signal::pad-removed",
on_composition_pad_removed_cb, NULL, NULL);
GST_DEBUG ("Adding composition to pipeline");
gst_bin_add_many (GST_BIN (pipeline), composition, fakesink, NULL);
fail_unless (gst_bin_add (GST_BIN (composition), gnlsource2));
fail_unless (gst_element_link (composition, fakesink) == TRUE);
GST_DEBUG ("Setting pipeline to PAUSED");
g_signal_emit_by_name (composition, "commit", TRUE, &ret);
@ -536,16 +320,8 @@ gnonlin_suite (void)
suite_add_tcase (s, tc_chain);
g_cond_init (&pad_added_cond);
g_mutex_init (&pad_added_lock);
tcase_add_test (tc_chain, test_change_object_start_stop_in_current_stack);
tcase_add_test (tc_chain, test_remove_invalid_object);
if (gst_registry_check_feature_version (gst_registry_get (), "videomixer", 0,
11, 0)) {
tcase_add_test (tc_chain, test_no_more_pads_race);
} else {
GST_WARNING ("videomixer element not available, skipping 1 test");
}
if (gst_registry_check_feature_version (gst_registry_get (), "adder", 1,
0, 0)) {

View file

@ -25,8 +25,7 @@ fill_pipeline_and_check (GstElement * comp, GList * segments)
/* Expected segments */
collect->expected_segments = segments;
g_signal_connect (G_OBJECT (comp), "pad-added",
G_CALLBACK (composition_pad_added_cb), collect);
gst_element_link (comp, sink);
sinkpad = gst_element_get_static_pad (sink, "sink");
gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,

View file

@ -39,8 +39,7 @@ GST_START_TEST (test_simple_videotestsrc)
segment_new (1.0, GST_FORMAT_TIME,
1 * GST_SECOND, 2 * GST_SECOND, 1 * GST_SECOND));
g_signal_connect (G_OBJECT (gnlsource), "pad-added",
G_CALLBACK (composition_pad_added_cb), collect);
gst_element_link (gnlsource, sink);
sinkpad = gst_element_get_static_pad (sink, "sink");
fail_if (sinkpad == NULL);
@ -139,8 +138,7 @@ GST_START_TEST (test_videotestsrc_in_bin)
collect->expected_segments = g_list_append (collect->expected_segments,
segment_new (1.0, GST_FORMAT_TIME, 0, 1 * GST_SECOND, 0));
g_signal_connect (G_OBJECT (gnlsource), "pad-added",
G_CALLBACK (composition_pad_added_cb), collect);
gst_element_link (gnlsource, sink);
sinkpad = gst_element_get_static_pad (sink, "sink");
fail_if (sinkpad == NULL);

View file

@ -1,4 +1,5 @@
#include "common.h"
static const gchar *compositor_element = NULL;
typedef struct _SeekInfo
{
@ -48,8 +49,7 @@ fill_pipeline_and_check (GstElement * comp, GList * segments, GList * seeks)
collect->expected_segments = segments;
collect->keep_expected_segments = TRUE;
g_signal_connect (G_OBJECT (comp), "pad-added",
G_CALLBACK (composition_pad_added_cb), collect);
gst_element_link (comp, sink);
sinkpad = gst_element_get_static_pad (sink, "sink");
gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
@ -521,7 +521,8 @@ GST_START_TEST (test_complex_operations)
*/
oper =
new_operation ("oper", "videomixer", 2 * GST_SECOND, 2 * GST_SECOND, 1);
new_operation ("oper", compositor_element, 2 * GST_SECOND, 2 * GST_SECOND,
1);
fail_if (oper == NULL);
check_start_stop_duration (oper, 2 * GST_SECOND, 4 * GST_SECOND,
2 * GST_SECOND);
@ -639,7 +640,8 @@ GST_START_TEST (test_complex_operations_bis)
*/
oper =
new_operation ("oper", "videomixer", 2 * GST_SECOND, 2 * GST_SECOND, 1);
new_operation ("oper", compositor_element, 2 * GST_SECOND, 2 * GST_SECOND,
1);
fail_if (oper == NULL);
check_start_stop_duration (oper, 2 * GST_SECOND, 4 * GST_SECOND,
2 * GST_SECOND);
@ -751,12 +753,26 @@ gnonlin_suite (void)
suite_add_tcase (s, tc_chain);
if (gst_registry_check_feature_version (gst_registry_get (), "compositor", 1,
0, 0)) {
compositor_element = "compositor";
} else if (gst_registry_check_feature_version (gst_registry_get (),
"videomixer", 1, 0, 0)) {
compositor_element = "videomixer";
}
tcase_add_test (tc_chain, test_simplest);
tcase_add_test (tc_chain, test_one_after_other);
tcase_add_test (tc_chain, test_one_under_another);
tcase_add_test (tc_chain, test_one_bin_after_other);
tcase_add_test (tc_chain, test_complex_operations);
tcase_add_test (tc_chain, test_complex_operations_bis);
if (compositor_element) {
tcase_add_test (tc_chain, test_complex_operations);
tcase_add_test (tc_chain, test_complex_operations_bis);
} else {
GST_WARNING ("No compositor element, can not run operations tests");
}
return s;
}

View file

@ -52,9 +52,7 @@ test_simplest_full (void)
collect->expected_segments = g_list_append (collect->expected_segments,
segment_new (1.0, GST_FORMAT_TIME, 5 * GST_SECOND, 6 * GST_SECOND, 0));
g_signal_connect (G_OBJECT (comp), "pad-added",
G_CALLBACK (composition_pad_added_cb), collect);
gst_element_link (comp, sink);
sinkpad = gst_element_get_static_pad (sink, "sink");
gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
(GstPadProbeCallback) sinkpad_probe, collect, NULL);
@ -282,8 +280,7 @@ test_one_after_other_full (void)
segment_new (1.0, GST_FORMAT_TIME,
2 * GST_SECOND, 3 * GST_SECOND, 1 * GST_SECOND));
g_signal_connect (G_OBJECT (comp), "pad-added",
G_CALLBACK (composition_pad_added_cb), collect);
gst_element_link (comp, sink);
sinkpad = gst_element_get_static_pad (sink, "sink");
gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
@ -485,8 +482,7 @@ test_one_under_another_full (void)
segment_new (1.0, GST_FORMAT_TIME,
2 * GST_SECOND, 3 * GST_SECOND, 2 * GST_SECOND));
g_signal_connect (G_OBJECT (comp), "pad-added",
G_CALLBACK (composition_pad_added_cb), collect);
gst_element_link (comp, sink);
sinkpad = gst_element_get_static_pad (sink, "sink");
gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
@ -634,8 +630,7 @@ test_one_bin_after_other_full (void)
segment_new (1.0, GST_FORMAT_TIME,
1 * GST_SECOND, 2 * GST_SECOND, 1 * GST_SECOND));
g_signal_connect (G_OBJECT (comp), "pad-added",
G_CALLBACK (composition_pad_added_cb), collect);
gst_element_link (comp, sink);
sinkpad = gst_element_get_static_pad (sink, "sink");
gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,