mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-27 18:50:48 +00:00
Import GNL from 978332e7c4c3bba1949421d28b492540ab471450 'Release 1.4.0'
This commit is contained in:
parent
a09caa3da2
commit
81a0ee66c1
25 changed files with 11007 additions and 0 deletions
46
gnl/Makefile.am
Normal file
46
gnl/Makefile.am
Normal file
|
@ -0,0 +1,46 @@
|
|||
plugin_LTLIBRARIES = libgnl.la
|
||||
|
||||
libgnl_la_SOURCES = \
|
||||
gnl.c \
|
||||
gnlobject.c \
|
||||
gnlcomposition.c \
|
||||
gnlghostpad.c \
|
||||
gnloperation.c \
|
||||
gnlsource.c \
|
||||
gnlurisource.c
|
||||
libgnl_la_CFLAGS = $(GST_CFLAGS)
|
||||
libgnl_la_LIBADD = $(GST_LIBS)
|
||||
libgnl_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
||||
libgnl_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
|
||||
|
||||
gnl_headers = \
|
||||
gnl.h \
|
||||
gnlobject.h \
|
||||
gnlcomposition.h \
|
||||
gnltypes.h \
|
||||
gnlghostpad.h \
|
||||
gnloperation.h \
|
||||
gnlsource.h \
|
||||
gnltypes.h \
|
||||
gnlurisource.h
|
||||
|
||||
DISTCLEANFILE = $(CLEANFILES)
|
||||
|
||||
#files built on make all/check/instal
|
||||
BUILT_SOURCES = $(built_header_configure)
|
||||
|
||||
noinst_HEADERS = $(gnl_headers) $(built_header_configure)
|
||||
|
||||
Android.mk: Makefile.am $(BUILT_SOURCES)
|
||||
androgenizer \
|
||||
-:PROJECT libgnl -:SHARED libgnl \
|
||||
-:TAGS eng debug \
|
||||
-:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \
|
||||
-:SOURCES $(libgnl_la_SOURCES) \
|
||||
-:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgnl_la_CFLAGS) \
|
||||
-:LDFLAGS $(libgnl_la_LDFLAGS) \
|
||||
$(libgnl_la_LIBADD) \
|
||||
-ldl \
|
||||
-:PASSTHROUGH LOCAL_ARM_MODE:=arm \
|
||||
LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-@GST_API_VERSION@' \
|
||||
> $@
|
60
gnl/gnl.c
Normal file
60
gnl/gnl.c
Normal file
|
@ -0,0 +1,60 @@
|
|||
/* Gnonlin
|
||||
* Copyright (C) <2001> Wim Taymans <wim.taymans@gmail.com>
|
||||
* <2004-2008> Edward Hervey <bilboed@bilboed.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 "gnl.h"
|
||||
|
||||
struct _elements_entry
|
||||
{
|
||||
const gchar *name;
|
||||
GType (*type) (void);
|
||||
};
|
||||
|
||||
static struct _elements_entry _elements[] = {
|
||||
{"gnlsource", gnl_source_get_type},
|
||||
{"gnlcomposition", gnl_composition_get_type},
|
||||
{"gnloperation", gnl_operation_get_type},
|
||||
{"gnlurisource", gnl_urisource_get_type},
|
||||
{NULL, 0}
|
||||
};
|
||||
|
||||
static gboolean
|
||||
plugin_init (GstPlugin * plugin)
|
||||
{
|
||||
gint i = 0;
|
||||
|
||||
for (; _elements[i].name; i++)
|
||||
if (!(gst_element_register (plugin,
|
||||
_elements[i].name, GST_RANK_NONE, (_elements[i].type) ())))
|
||||
return FALSE;
|
||||
|
||||
gnl_init_ghostpad_category ();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
|
||||
GST_VERSION_MINOR,
|
||||
gnonlin,
|
||||
"Standard elements for non-linear multimedia editing",
|
||||
plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
|
36
gnl/gnl.h
Normal file
36
gnl/gnl.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/* Gnonlin
|
||||
* Copyright (C) <2001> Wim Taymans <wim.taymans@gmail.com>
|
||||
* <2004-2008> Edward Hervey <bilboed@bilboed.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.
|
||||
*/
|
||||
|
||||
#ifndef __GNL_H__
|
||||
#define __GNL_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include "gnltypes.h"
|
||||
|
||||
#include "gnlobject.h"
|
||||
#include "gnlghostpad.h"
|
||||
#include "gnlsource.h"
|
||||
#include "gnlcomposition.h"
|
||||
#include "gnloperation.h"
|
||||
|
||||
#include "gnlurisource.h"
|
||||
|
||||
#endif /* __GST_H__ */
|
3074
gnl/gnlcomposition.c
Normal file
3074
gnl/gnlcomposition.c
Normal file
File diff suppressed because it is too large
Load diff
63
gnl/gnlcomposition.h
Normal file
63
gnl/gnlcomposition.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2001 Wim Taymans <wim.taymans@gmail.com>
|
||||
* 2004-2008 Edward Hervey <bilboed@bilboed.com>
|
||||
*
|
||||
* gnlcomposition.h: Header for base GnlComposition
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GNL_COMPOSITION_H__
|
||||
#define __GNL_COMPOSITION_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gnlobject.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
#define GNL_TYPE_COMPOSITION \
|
||||
(gnl_composition_get_type())
|
||||
#define GNL_COMPOSITION(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GNL_TYPE_COMPOSITION,GnlComposition))
|
||||
#define GNL_COMPOSITION_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass),GNL_TYPE_COMPOSITION,GnlCompositionClass))
|
||||
#define GNL_COMPOSITION_GET_CLASS(obj) \
|
||||
(GNL_COMPOSITION_CLASS (G_OBJECT_GET_CLASS (obj)))
|
||||
#define GNL_IS_COMPOSITION(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GNL_TYPE_COMPOSITION))
|
||||
#define GNL_IS_COMPOSITION_CLASS(obj) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GNL_TYPE_COMPOSITION))
|
||||
|
||||
typedef struct _GnlCompositionPrivate GnlCompositionPrivate;
|
||||
|
||||
struct _GnlComposition
|
||||
{
|
||||
GnlObject parent;
|
||||
|
||||
/*< private >*/
|
||||
|
||||
GnlCompositionPrivate *priv;
|
||||
};
|
||||
|
||||
struct _GnlCompositionClass
|
||||
{
|
||||
GnlObjectClass parent_class;
|
||||
};
|
||||
|
||||
GType gnl_composition_get_type (void);
|
||||
|
||||
G_END_DECLS
|
||||
#endif /* __GNL_COMPOSITION_H__ */
|
770
gnl/gnlghostpad.c
Normal file
770
gnl/gnlghostpad.c
Normal file
|
@ -0,0 +1,770 @@
|
|||
/* Gnonlin
|
||||
* Copyright (C) <2009> Edward Hervey <bilboed@bilboed.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 "gnl.h"
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (gnlghostpad);
|
||||
#define GST_CAT_DEFAULT gnlghostpad
|
||||
|
||||
typedef struct _GnlPadPrivate GnlPadPrivate;
|
||||
|
||||
struct _GnlPadPrivate
|
||||
{
|
||||
GnlObject *object;
|
||||
GnlPadPrivate *ghostpriv;
|
||||
GstPadDirection dir;
|
||||
GstPadEventFunction eventfunc;
|
||||
GstPadQueryFunction queryfunc;
|
||||
};
|
||||
|
||||
static GstEvent *
|
||||
translate_incoming_seek (GnlObject * object, GstEvent * event)
|
||||
{
|
||||
GstEvent *event2;
|
||||
GstFormat format;
|
||||
gdouble rate;
|
||||
GstSeekFlags flags;
|
||||
GstSeekType curtype, stoptype;
|
||||
GstSeekType ncurtype;
|
||||
gint64 cur;
|
||||
guint64 ncur;
|
||||
gint64 stop;
|
||||
guint64 nstop;
|
||||
guint32 seqnum = GST_EVENT_SEQNUM (event);
|
||||
|
||||
gst_event_parse_seek (event, &rate, &format, &flags,
|
||||
&curtype, &cur, &stoptype, &stop);
|
||||
|
||||
GST_DEBUG_OBJECT (object,
|
||||
"GOT SEEK rate:%f, format:%d, flags:%d, curtype:%d, stoptype:%d, %"
|
||||
GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, rate, format, flags, curtype,
|
||||
stoptype, GST_TIME_ARGS (cur), GST_TIME_ARGS (stop));
|
||||
|
||||
if (G_UNLIKELY (format != GST_FORMAT_TIME))
|
||||
goto invalid_format;
|
||||
|
||||
/* convert cur */
|
||||
ncurtype = GST_SEEK_TYPE_SET;
|
||||
if (G_LIKELY ((curtype == GST_SEEK_TYPE_SET)
|
||||
&& (gnl_object_to_media_time (object, cur, &ncur)))) {
|
||||
/* cur is TYPE_SET and value is valid */
|
||||
if (ncur > G_MAXINT64)
|
||||
GST_WARNING_OBJECT (object, "return value too big...");
|
||||
GST_LOG_OBJECT (object, "Setting cur to %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (ncur));
|
||||
} else if ((curtype != GST_SEEK_TYPE_NONE)) {
|
||||
GST_DEBUG_OBJECT (object, "Limiting seek start to inpoint");
|
||||
ncur = object->inpoint;
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (object, "leaving GST_SEEK_TYPE_NONE");
|
||||
ncur = cur;
|
||||
ncurtype = GST_SEEK_TYPE_NONE;
|
||||
}
|
||||
|
||||
/* convert stop, we also need to limit it to object->stop */
|
||||
if (G_LIKELY ((stoptype == GST_SEEK_TYPE_SET)
|
||||
&& (gnl_object_to_media_time (object, stop, &nstop)))) {
|
||||
if (nstop > G_MAXINT64)
|
||||
GST_WARNING_OBJECT (object, "return value too big...");
|
||||
GST_LOG_OBJECT (object, "Setting stop to %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (nstop));
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (object, "Limiting end of seek to media_stop");
|
||||
gnl_object_to_media_time (object, object->stop, &nstop);
|
||||
if (nstop > G_MAXINT64)
|
||||
GST_WARNING_OBJECT (object, "return value too big...");
|
||||
GST_LOG_OBJECT (object, "Setting stop to %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (nstop));
|
||||
}
|
||||
|
||||
|
||||
/* add accurate seekflags */
|
||||
if (G_UNLIKELY (!(flags & GST_SEEK_FLAG_ACCURATE))) {
|
||||
GST_DEBUG_OBJECT (object, "Adding GST_SEEK_FLAG_ACCURATE");
|
||||
flags |= GST_SEEK_FLAG_ACCURATE;
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (object,
|
||||
"event already has GST_SEEK_FLAG_ACCURATE : %d", flags);
|
||||
}
|
||||
|
||||
|
||||
|
||||
GST_DEBUG_OBJECT (object,
|
||||
"SENDING SEEK rate:%f, format:TIME, flags:%d, curtype:%d, stoptype:SET, %"
|
||||
GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, rate, flags, ncurtype,
|
||||
GST_TIME_ARGS (ncur), GST_TIME_ARGS (nstop));
|
||||
|
||||
event2 = gst_event_new_seek (rate, GST_FORMAT_TIME, flags,
|
||||
ncurtype, (gint64) ncur, GST_SEEK_TYPE_SET, (gint64) nstop);
|
||||
GST_EVENT_SEQNUM (event2) = seqnum;
|
||||
|
||||
gst_event_unref (event);
|
||||
|
||||
return event2;
|
||||
|
||||
/* ERRORS */
|
||||
invalid_format:
|
||||
{
|
||||
GST_WARNING ("GNonLin time shifting only works with GST_FORMAT_TIME");
|
||||
return event;
|
||||
}
|
||||
}
|
||||
|
||||
static GstEvent *
|
||||
translate_outgoing_seek (GnlObject * object, GstEvent * event)
|
||||
{
|
||||
GstEvent *event2;
|
||||
GstFormat format;
|
||||
gdouble rate;
|
||||
GstSeekFlags flags;
|
||||
GstSeekType curtype, stoptype;
|
||||
GstSeekType ncurtype;
|
||||
gint64 cur;
|
||||
guint64 ncur;
|
||||
gint64 stop;
|
||||
guint64 nstop;
|
||||
guint32 seqnum = GST_EVENT_SEQNUM (event);
|
||||
|
||||
gst_event_parse_seek (event, &rate, &format, &flags,
|
||||
&curtype, &cur, &stoptype, &stop);
|
||||
|
||||
GST_DEBUG_OBJECT (object,
|
||||
"GOT SEEK rate:%f, format:%d, flags:%d, curtype:%d, stoptype:%d, %"
|
||||
GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, rate, format, flags, curtype,
|
||||
stoptype, GST_TIME_ARGS (cur), GST_TIME_ARGS (stop));
|
||||
|
||||
if (G_UNLIKELY (format != GST_FORMAT_TIME))
|
||||
goto invalid_format;
|
||||
|
||||
/* convert cur */
|
||||
ncurtype = GST_SEEK_TYPE_SET;
|
||||
if (G_LIKELY ((curtype == GST_SEEK_TYPE_SET)
|
||||
&& (gnl_media_to_object_time (object, cur, &ncur)))) {
|
||||
/* cur is TYPE_SET and value is valid */
|
||||
if (ncur > G_MAXINT64)
|
||||
GST_WARNING_OBJECT (object, "return value too big...");
|
||||
GST_LOG_OBJECT (object, "Setting cur to %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (ncur));
|
||||
} else if ((curtype != GST_SEEK_TYPE_NONE)) {
|
||||
GST_DEBUG_OBJECT (object, "Limiting seek start to start");
|
||||
ncur = object->start;
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (object, "leaving GST_SEEK_TYPE_NONE");
|
||||
ncur = cur;
|
||||
ncurtype = GST_SEEK_TYPE_NONE;
|
||||
}
|
||||
|
||||
/* convert stop, we also need to limit it to object->stop */
|
||||
if (G_LIKELY ((stoptype == GST_SEEK_TYPE_SET)
|
||||
&& (gnl_media_to_object_time (object, stop, &nstop)))) {
|
||||
if (nstop > G_MAXINT64)
|
||||
GST_WARNING_OBJECT (object, "return value too big...");
|
||||
GST_LOG_OBJECT (object, "Setting stop to %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (nstop));
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (object, "Limiting end of seek to stop");
|
||||
nstop = object->stop;
|
||||
if (nstop > G_MAXINT64)
|
||||
GST_WARNING_OBJECT (object, "return value too big...");
|
||||
GST_LOG_OBJECT (object, "Setting stop to %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (nstop));
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (object,
|
||||
"SENDING SEEK rate:%f, format:TIME, flags:%d, curtype:%d, stoptype:SET, %"
|
||||
GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, rate, flags, ncurtype,
|
||||
GST_TIME_ARGS (ncur), GST_TIME_ARGS (nstop));
|
||||
|
||||
event2 = gst_event_new_seek (rate, GST_FORMAT_TIME, flags,
|
||||
ncurtype, (gint64) ncur, GST_SEEK_TYPE_SET, (gint64) nstop);
|
||||
GST_EVENT_SEQNUM (event2) = seqnum;
|
||||
|
||||
gst_event_unref (event);
|
||||
|
||||
return event2;
|
||||
|
||||
/* ERRORS */
|
||||
invalid_format:
|
||||
{
|
||||
GST_WARNING ("GNonLin time shifting only works with GST_FORMAT_TIME");
|
||||
return event;
|
||||
}
|
||||
}
|
||||
|
||||
static GstEvent *
|
||||
translate_outgoing_segment (GnlObject * object, GstEvent * event)
|
||||
{
|
||||
const GstSegment *orig;
|
||||
GstSegment segment;
|
||||
GstEvent *event2;
|
||||
guint32 seqnum = GST_EVENT_SEQNUM (event);
|
||||
|
||||
/* only modify the streamtime */
|
||||
gst_event_parse_segment (event, &orig);
|
||||
|
||||
GST_DEBUG_OBJECT (object,
|
||||
"Got SEGMENT %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT " // %"
|
||||
GST_TIME_FORMAT, GST_TIME_ARGS (orig->start), GST_TIME_ARGS (orig->stop),
|
||||
GST_TIME_ARGS (orig->time));
|
||||
|
||||
if (G_UNLIKELY (orig->format != GST_FORMAT_TIME)) {
|
||||
GST_WARNING_OBJECT (object,
|
||||
"Can't translate segments with format != GST_FORMAT_TIME");
|
||||
return event;
|
||||
}
|
||||
|
||||
gst_segment_copy_into (orig, &segment);
|
||||
|
||||
gnl_media_to_object_time (object, orig->time, &segment.time);
|
||||
|
||||
if (G_UNLIKELY (segment.time > G_MAXINT64))
|
||||
GST_WARNING_OBJECT (object, "Return value too big...");
|
||||
|
||||
GST_DEBUG_OBJECT (object,
|
||||
"Sending SEGMENT %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT " // %"
|
||||
GST_TIME_FORMAT, GST_TIME_ARGS (segment.start),
|
||||
GST_TIME_ARGS (segment.stop), GST_TIME_ARGS (segment.time));
|
||||
|
||||
event2 = gst_event_new_segment (&segment);
|
||||
GST_EVENT_SEQNUM (event2) = seqnum;
|
||||
gst_event_unref (event);
|
||||
|
||||
return event2;
|
||||
}
|
||||
|
||||
static GstEvent *
|
||||
translate_incoming_segment (GnlObject * object, GstEvent * event)
|
||||
{
|
||||
GstEvent *event2;
|
||||
const GstSegment *orig;
|
||||
GstSegment segment;
|
||||
guint32 seqnum = GST_EVENT_SEQNUM (event);
|
||||
|
||||
/* only modify the streamtime */
|
||||
gst_event_parse_segment (event, &orig);
|
||||
|
||||
GST_DEBUG_OBJECT (object,
|
||||
"Got SEGMENT %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT " // %"
|
||||
GST_TIME_FORMAT, GST_TIME_ARGS (orig->start), GST_TIME_ARGS (orig->stop),
|
||||
GST_TIME_ARGS (orig->time));
|
||||
|
||||
if (G_UNLIKELY (orig->format != GST_FORMAT_TIME)) {
|
||||
GST_WARNING_OBJECT (object,
|
||||
"Can't translate segments with format != GST_FORMAT_TIME");
|
||||
return event;
|
||||
}
|
||||
|
||||
gst_segment_copy_into (orig, &segment);
|
||||
|
||||
if (!gnl_object_to_media_time (object, orig->time, &segment.time)) {
|
||||
GST_DEBUG ("Can't convert media_time, using 0");
|
||||
segment.time = 0;
|
||||
};
|
||||
|
||||
if (GNL_IS_OPERATION (object)) {
|
||||
segment.base = GNL_OPERATION (object)->next_base_time;
|
||||
GST_INFO_OBJECT (object, "Using operation base time %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (GNL_OPERATION (object)->next_base_time));
|
||||
}
|
||||
|
||||
if (G_UNLIKELY (segment.time > G_MAXINT64))
|
||||
GST_WARNING_OBJECT (object, "Return value too big...");
|
||||
|
||||
GST_DEBUG_OBJECT (object,
|
||||
"Sending SEGMENT %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT " // %"
|
||||
GST_TIME_FORMAT, GST_TIME_ARGS (segment.start),
|
||||
GST_TIME_ARGS (segment.stop), GST_TIME_ARGS (segment.time));
|
||||
|
||||
event2 = gst_event_new_segment (&segment);
|
||||
GST_EVENT_SEQNUM (event2) = seqnum;
|
||||
gst_event_unref (event);
|
||||
|
||||
return event2;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
internalpad_event_function (GstPad * internal, GstObject * parent,
|
||||
GstEvent * event)
|
||||
{
|
||||
GnlPadPrivate *priv = gst_pad_get_element_private (internal);
|
||||
GnlObject *object = priv->object;
|
||||
gboolean res;
|
||||
|
||||
GST_DEBUG_OBJECT (internal, "event:%s (seqnum::%d)",
|
||||
GST_EVENT_TYPE_NAME (event), GST_EVENT_SEQNUM (event));
|
||||
|
||||
if (G_UNLIKELY (!(priv->eventfunc))) {
|
||||
GST_WARNING_OBJECT (internal,
|
||||
"priv->eventfunc == NULL !! What is going on ?");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
switch (priv->dir) {
|
||||
case GST_PAD_SRC:{
|
||||
switch (GST_EVENT_TYPE (event)) {
|
||||
case GST_EVENT_SEGMENT:
|
||||
event = translate_outgoing_segment (object, event);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case GST_PAD_SINK:{
|
||||
switch (GST_EVENT_TYPE (event)) {
|
||||
case GST_EVENT_SEEK:
|
||||
event = translate_outgoing_seek (object, event);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
GST_DEBUG_OBJECT (internal, "Calling priv->eventfunc %p", priv->eventfunc);
|
||||
res = priv->eventfunc (internal, parent, event);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
translate_outgoing_position_query
|
||||
|
||||
Should only be called:
|
||||
_ if the query is a GST_QUERY_POSITION
|
||||
_ after the query was sent upstream
|
||||
_ if the upstream query returned TRUE
|
||||
*/
|
||||
|
||||
static gboolean
|
||||
translate_incoming_position_query (GnlObject * object, GstQuery * query)
|
||||
{
|
||||
GstFormat format;
|
||||
gint64 cur, cur2;
|
||||
|
||||
gst_query_parse_position (query, &format, &cur);
|
||||
if (G_UNLIKELY (format != GST_FORMAT_TIME)) {
|
||||
GST_WARNING_OBJECT (object,
|
||||
"position query is in a format different from time, returning without modifying values");
|
||||
goto beach;
|
||||
}
|
||||
|
||||
gnl_media_to_object_time (object, (guint64) cur, (guint64 *) & cur2);
|
||||
|
||||
GST_DEBUG_OBJECT (object,
|
||||
"Adjust position from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (cur), GST_TIME_ARGS (cur2));
|
||||
gst_query_set_position (query, GST_FORMAT_TIME, cur2);
|
||||
|
||||
beach:
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
translate_outgoing_position_query (GnlObject * object, GstQuery * query)
|
||||
{
|
||||
GstFormat format;
|
||||
gint64 cur, cur2;
|
||||
|
||||
gst_query_parse_position (query, &format, &cur);
|
||||
if (G_UNLIKELY (format != GST_FORMAT_TIME)) {
|
||||
GST_WARNING_OBJECT (object,
|
||||
"position query is in a format different from time, returning without modifying values");
|
||||
goto beach;
|
||||
}
|
||||
|
||||
if (G_UNLIKELY (!(gnl_object_to_media_time (object, (guint64) cur,
|
||||
(guint64 *) & cur2)))) {
|
||||
GST_WARNING_OBJECT (object,
|
||||
"Couldn't get media time for %" GST_TIME_FORMAT, GST_TIME_ARGS (cur));
|
||||
goto beach;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (object,
|
||||
"Adjust position from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (cur), GST_TIME_ARGS (cur2));
|
||||
gst_query_set_position (query, GST_FORMAT_TIME, cur2);
|
||||
|
||||
beach:
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
translate_incoming_duration_query (GnlObject * object, GstQuery * query)
|
||||
{
|
||||
GstFormat format;
|
||||
gint64 cur;
|
||||
|
||||
gst_query_parse_duration (query, &format, &cur);
|
||||
if (G_UNLIKELY (format != GST_FORMAT_TIME)) {
|
||||
GST_WARNING_OBJECT (object,
|
||||
"We can only handle duration queries in GST_FORMAT_TIME");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gst_query_set_duration (query, GST_FORMAT_TIME, object->duration);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
internalpad_query_function (GstPad * internal, GstObject * parent,
|
||||
GstQuery * query)
|
||||
{
|
||||
GnlPadPrivate *priv = gst_pad_get_element_private (internal);
|
||||
GnlObject *object = priv->object;
|
||||
gboolean ret;
|
||||
|
||||
GST_DEBUG_OBJECT (internal, "querytype:%s",
|
||||
gst_query_type_get_name (GST_QUERY_TYPE (query)));
|
||||
|
||||
if (!(priv->queryfunc)) {
|
||||
GST_WARNING_OBJECT (internal,
|
||||
"priv->queryfunc == NULL !! What is going on ?");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ((ret = priv->queryfunc (internal, parent, query))) {
|
||||
|
||||
switch (priv->dir) {
|
||||
case GST_PAD_SRC:
|
||||
break;
|
||||
case GST_PAD_SINK:
|
||||
switch (GST_QUERY_TYPE (query)) {
|
||||
case GST_QUERY_POSITION:
|
||||
ret = translate_outgoing_position_query (object, query);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
ghostpad_event_function (GstPad * ghostpad, GstObject * parent,
|
||||
GstEvent * event)
|
||||
{
|
||||
GnlPadPrivate *priv;
|
||||
GnlObject *object;
|
||||
gboolean ret = FALSE;
|
||||
|
||||
priv = gst_pad_get_element_private (ghostpad);
|
||||
object = priv->object;
|
||||
|
||||
GST_DEBUG_OBJECT (ghostpad, "event:%s", GST_EVENT_TYPE_NAME (event));
|
||||
|
||||
if (G_UNLIKELY (priv->eventfunc == NULL))
|
||||
goto no_function;
|
||||
|
||||
switch (priv->dir) {
|
||||
case GST_PAD_SRC:
|
||||
{
|
||||
switch (GST_EVENT_TYPE (event)) {
|
||||
case GST_EVENT_SEEK:
|
||||
event = translate_incoming_seek (object, event);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GST_PAD_SINK:{
|
||||
switch (GST_EVENT_TYPE (event)) {
|
||||
case GST_EVENT_SEGMENT:
|
||||
event = translate_incoming_segment (object, event);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (event) {
|
||||
GST_DEBUG_OBJECT (ghostpad, "Calling priv->eventfunc");
|
||||
ret = priv->eventfunc (ghostpad, parent, event);
|
||||
GST_DEBUG_OBJECT (ghostpad, "Returned from calling priv->eventfunc : %d",
|
||||
ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
/* ERRORS */
|
||||
no_function:
|
||||
{
|
||||
GST_WARNING_OBJECT (ghostpad,
|
||||
"priv->eventfunc == NULL !! What's going on ?");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
ghostpad_query_function (GstPad * ghostpad, GstObject * parent,
|
||||
GstQuery * query)
|
||||
{
|
||||
GnlPadPrivate *priv = gst_pad_get_element_private (ghostpad);
|
||||
GnlObject *object = GNL_OBJECT (parent);
|
||||
gboolean pret = TRUE;
|
||||
|
||||
GST_DEBUG_OBJECT (ghostpad, "querytype:%s", GST_QUERY_TYPE_NAME (query));
|
||||
|
||||
switch (GST_QUERY_TYPE (query)) {
|
||||
case GST_QUERY_DURATION:
|
||||
/* skip duration upstream query, we'll fill it in ourselves */
|
||||
break;
|
||||
default:
|
||||
pret = priv->queryfunc (ghostpad, parent, query);
|
||||
}
|
||||
|
||||
if (pret) {
|
||||
/* translate result */
|
||||
switch (GST_QUERY_TYPE (query)) {
|
||||
case GST_QUERY_POSITION:
|
||||
pret = translate_incoming_position_query (object, query);
|
||||
break;
|
||||
case GST_QUERY_DURATION:
|
||||
pret = translate_incoming_duration_query (object, query);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return pret;
|
||||
}
|
||||
|
||||
/* internal pad going away */
|
||||
static void
|
||||
internal_pad_finalizing (GnlPadPrivate * priv, GObject * pad G_GNUC_UNUSED)
|
||||
{
|
||||
g_slice_free (GnlPadPrivate, priv);
|
||||
}
|
||||
|
||||
static inline GstPad *
|
||||
get_proxy_pad (GstPad * ghostpad)
|
||||
{
|
||||
GValue item = { 0, };
|
||||
GstIterator *it;
|
||||
GstPad *ret = NULL;
|
||||
|
||||
it = gst_pad_iterate_internal_links (ghostpad);
|
||||
g_assert (it);
|
||||
gst_iterator_next (it, &item);
|
||||
ret = g_value_dup_object (&item);
|
||||
g_value_unset (&item);
|
||||
g_assert (ret);
|
||||
gst_iterator_free (it);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
control_internal_pad (GstPad * ghostpad, GnlObject * object)
|
||||
{
|
||||
GnlPadPrivate *priv;
|
||||
GnlPadPrivate *privghost;
|
||||
GstPad *internal;
|
||||
|
||||
if (!ghostpad) {
|
||||
GST_DEBUG_OBJECT (object, "We don't have a valid ghostpad !");
|
||||
return;
|
||||
}
|
||||
privghost = gst_pad_get_element_private (ghostpad);
|
||||
|
||||
GST_LOG_OBJECT (ghostpad, "overriding ghostpad's internal pad function");
|
||||
|
||||
internal = get_proxy_pad (ghostpad);
|
||||
|
||||
if (G_UNLIKELY (!(priv = gst_pad_get_element_private (internal)))) {
|
||||
GST_DEBUG_OBJECT (internal,
|
||||
"Creating a GnlPadPrivate to put in element_private");
|
||||
priv = g_slice_new0 (GnlPadPrivate);
|
||||
|
||||
/* Remember existing pad functions */
|
||||
priv->eventfunc = GST_PAD_EVENTFUNC (internal);
|
||||
priv->queryfunc = GST_PAD_QUERYFUNC (internal);
|
||||
gst_pad_set_element_private (internal, priv);
|
||||
|
||||
g_object_weak_ref ((GObject *) internal,
|
||||
(GWeakNotify) internal_pad_finalizing, priv);
|
||||
|
||||
/* add query/event function overrides on internal pad */
|
||||
gst_pad_set_event_function (internal,
|
||||
GST_DEBUG_FUNCPTR (internalpad_event_function));
|
||||
gst_pad_set_query_function (internal,
|
||||
GST_DEBUG_FUNCPTR (internalpad_query_function));
|
||||
}
|
||||
|
||||
priv->object = object;
|
||||
priv->ghostpriv = privghost;
|
||||
priv->dir = GST_PAD_DIRECTION (ghostpad);
|
||||
gst_object_unref (internal);
|
||||
|
||||
GST_DEBUG_OBJECT (ghostpad, "Done with pad %s:%s",
|
||||
GST_DEBUG_PAD_NAME (ghostpad));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* gnl_object_ghost_pad:
|
||||
* @object: #GnlObject to add the ghostpad to
|
||||
* @name: Name for the new pad
|
||||
* @target: Target #GstPad to ghost
|
||||
*
|
||||
* Adds a #GstGhostPad overridding the correct pad [query|event]_function so
|
||||
* that time shifting is done correctly
|
||||
* The #GstGhostPad is added to the #GnlObject
|
||||
*
|
||||
* /!\ This function doesn't check if the existing [src|sink] pad was removed
|
||||
* first, so you might end up with more pads than wanted
|
||||
*
|
||||
* Returns: The #GstPad if everything went correctly, else NULL.
|
||||
*/
|
||||
GstPad *
|
||||
gnl_object_ghost_pad (GnlObject * object, const gchar * name, GstPad * target)
|
||||
{
|
||||
GstPadDirection dir = GST_PAD_DIRECTION (target);
|
||||
GstPad *ghost;
|
||||
|
||||
GST_DEBUG_OBJECT (object, "name:%s, target:%p", name, 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);
|
||||
if (!ghost) {
|
||||
GST_WARNING_OBJECT (object, "Couldn't create ghostpad");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(gnl_object_ghost_pad_set_target (object, ghost, target))) {
|
||||
GST_WARNING_OBJECT (object,
|
||||
"Couldn't set the target pad... removing ghostpad");
|
||||
gst_object_unref (ghost);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (object, "activating ghostpad");
|
||||
/* activate pad */
|
||||
gst_pad_set_active (ghost, TRUE);
|
||||
/* add it to element */
|
||||
if (!(gst_element_add_pad (GST_ELEMENT (object), ghost))) {
|
||||
GST_WARNING ("couldn't add newly created ghostpad");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ghost;
|
||||
}
|
||||
|
||||
/*
|
||||
* gnl_object_ghost_pad_no_target:
|
||||
* /!\ Doesn't add the pad to the GnlObject....
|
||||
*/
|
||||
GstPad *
|
||||
gnl_object_ghost_pad_no_target (GnlObject * object, const gchar * name,
|
||||
GstPadDirection dir)
|
||||
{
|
||||
GstPad *ghost;
|
||||
GnlPadPrivate *priv;
|
||||
|
||||
/* create a no_target ghostpad */
|
||||
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);
|
||||
priv->dir = dir;
|
||||
priv->object = object;
|
||||
|
||||
/* grab/replace event/query functions */
|
||||
GST_DEBUG_OBJECT (ghost, "Setting priv->eventfunc to %p",
|
||||
GST_PAD_EVENTFUNC (ghost));
|
||||
priv->eventfunc = GST_PAD_EVENTFUNC (ghost);
|
||||
priv->queryfunc = GST_PAD_QUERYFUNC (ghost);
|
||||
|
||||
gst_pad_set_event_function (ghost,
|
||||
GST_DEBUG_FUNCPTR (ghostpad_event_function));
|
||||
gst_pad_set_query_function (ghost,
|
||||
GST_DEBUG_FUNCPTR (ghostpad_query_function));
|
||||
|
||||
gst_pad_set_element_private (ghost, priv);
|
||||
control_internal_pad (ghost, object);
|
||||
|
||||
return ghost;
|
||||
}
|
||||
|
||||
void
|
||||
gnl_object_remove_ghost_pad (GnlObject * object, GstPad * ghost)
|
||||
{
|
||||
GnlPadPrivate *priv;
|
||||
|
||||
GST_DEBUG_OBJECT (object, "ghostpad %s:%s", GST_DEBUG_PAD_NAME (ghost));
|
||||
|
||||
priv = gst_pad_get_element_private (ghost);
|
||||
gst_ghost_pad_set_target (GST_GHOST_PAD (ghost), NULL);
|
||||
gst_element_remove_pad (GST_ELEMENT (object), ghost);
|
||||
if (priv)
|
||||
g_slice_free (GnlPadPrivate, priv);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gnl_object_ghost_pad_set_target (GnlObject * object, GstPad * ghost,
|
||||
GstPad * target)
|
||||
{
|
||||
GnlPadPrivate *priv = gst_pad_get_element_private (ghost);
|
||||
|
||||
g_return_val_if_fail (priv, 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");
|
||||
|
||||
/* set target */
|
||||
if (!(gst_ghost_pad_set_target (GST_GHOST_PAD (ghost), target)))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
gnl_init_ghostpad_category (void)
|
||||
{
|
||||
GST_DEBUG_CATEGORY_INIT (gnlghostpad, "gnlghostpad",
|
||||
GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin GhostPad");
|
||||
|
||||
}
|
47
gnl/gnlghostpad.h
Normal file
47
gnl/gnlghostpad.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2009 Edward Hervey <bilboed@bilboed.com>
|
||||
*
|
||||
* gnlghostpad.h: Header for helper ghostpad
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GNL_GHOSTPAD_H__
|
||||
#define __GNL_GHOSTPAD_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include "gnltypes.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
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);
|
||||
|
||||
gboolean gnl_object_ghost_pad_set_target (GnlObject * object,
|
||||
GstPad * ghost, GstPad * target);
|
||||
|
||||
void gnl_object_remove_ghost_pad (GnlObject * object, GstPad * ghost);
|
||||
|
||||
void gnl_init_ghostpad_category (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GNL_GHOSTPAD_H__ */
|
2
gnl/gnlmarshal.list
Normal file
2
gnl/gnlmarshal.list
Normal file
|
@ -0,0 +1,2 @@
|
|||
VOID:OBJECT,UINT
|
||||
|
656
gnl/gnlobject.c
Normal file
656
gnl/gnlobject.c
Normal file
|
@ -0,0 +1,656 @@
|
|||
/* Gnonlin
|
||||
* Copyright (C) <2001> Wim Taymans <wim.taymans@gmail.com>
|
||||
* <2004-2008> Edward Hervey <bilboed@bilboed.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 <string.h>
|
||||
#include "gnl.h"
|
||||
|
||||
/**
|
||||
* SECTION:gnlobject
|
||||
* @short_description: Base class for GNonLin elements
|
||||
*
|
||||
* <refsect2>
|
||||
* <para>
|
||||
* GnlObject encapsulates default behaviour and implements standard
|
||||
* properties provided by all the GNonLin elements.
|
||||
* </para>
|
||||
* </refsect2>
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
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);
|
||||
|
||||
/****************************************************
|
||||
* Helper macros *
|
||||
****************************************************/
|
||||
#define CHECK_AND_SET(PROPERTY, property, prop_str, print_format) \
|
||||
{ \
|
||||
GstObject *parent = gst_object_get_parent (GST_OBJECT (object)); \
|
||||
if (parent == NULL && !GNL_OBJECT_IS_COMPOSITION (object)) { \
|
||||
GST_INFO_OBJECT (object, "Not in a composition yet, " \
|
||||
"not commiting" prop_str); \
|
||||
} else if (object->pending_##property != object->property) { \
|
||||
object->property = object->pending_##property; \
|
||||
GST_DEBUG_OBJECT(object, "Setting " prop_str " to %" \
|
||||
print_format, object->property); \
|
||||
} else \
|
||||
GST_DEBUG_OBJECT(object, "Nothing to do for " prop_str); \
|
||||
if (parent) \
|
||||
gst_object_unref (parent); \
|
||||
}
|
||||
|
||||
#define SET_PENDING_VALUE(property, property_str, type, print_format) \
|
||||
gnlobject->pending_##property = g_value_get_##type (value); \
|
||||
if (gnlobject->property != gnlobject->pending_##property) { \
|
||||
GST_DEBUG_OBJECT(object, "Setting pending " property_str " to %" \
|
||||
print_format, gnlobject->pending_##property); \
|
||||
gnl_object_set_commit_needed (gnlobject); \
|
||||
} else \
|
||||
GST_DEBUG_OBJECT(object, "Pending " property_str " did not change");
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_START,
|
||||
PROP_DURATION,
|
||||
PROP_STOP,
|
||||
PROP_INPOINT,
|
||||
PROP_PRIORITY,
|
||||
PROP_ACTIVE,
|
||||
PROP_CAPS,
|
||||
PROP_EXPANDABLE,
|
||||
PROP_LAST
|
||||
};
|
||||
|
||||
static GParamSpec *properties[PROP_LAST];
|
||||
|
||||
static void gnl_object_dispose (GObject * object);
|
||||
|
||||
static void gnl_object_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec);
|
||||
static void gnl_object_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec);
|
||||
|
||||
static GstStateChangeReturn gnl_object_change_state (GstElement * element,
|
||||
GstStateChange transition);
|
||||
|
||||
static gboolean gnl_object_prepare_func (GnlObject * object);
|
||||
static gboolean gnl_object_cleanup_func (GnlObject * object);
|
||||
static gboolean gnl_object_commit_func (GnlObject * object, gboolean recurse);
|
||||
|
||||
static GstStateChangeReturn gnl_object_prepare (GnlObject * object);
|
||||
|
||||
static void
|
||||
gnl_object_class_init (GnlObjectClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
GstElementClass *gstelement_class;
|
||||
GnlObjectClass *gnlobject_class;
|
||||
|
||||
gobject_class = (GObjectClass *) klass;
|
||||
gstelement_class = (GstElementClass *) klass;
|
||||
gnlobject_class = (GnlObjectClass *) klass;
|
||||
|
||||
gobject_class->set_property = GST_DEBUG_FUNCPTR (gnl_object_set_property);
|
||||
gobject_class->get_property = GST_DEBUG_FUNCPTR (gnl_object_get_property);
|
||||
gobject_class->dispose = GST_DEBUG_FUNCPTR (gnl_object_dispose);
|
||||
|
||||
gstelement_class->change_state = GST_DEBUG_FUNCPTR (gnl_object_change_state);
|
||||
|
||||
gnlobject_class->prepare = GST_DEBUG_FUNCPTR (gnl_object_prepare_func);
|
||||
gnlobject_class->cleanup = GST_DEBUG_FUNCPTR (gnl_object_cleanup_func);
|
||||
gnlobject_class->commit_signal_handler =
|
||||
GST_DEBUG_FUNCPTR (gnl_object_commit);
|
||||
gnlobject_class->commit = GST_DEBUG_FUNCPTR (gnl_object_commit_func);
|
||||
|
||||
/**
|
||||
* GnlObject:start
|
||||
*
|
||||
* The start position relative to the parent in nanoseconds.
|
||||
*/
|
||||
properties[PROP_START] = g_param_spec_uint64 ("start", "Start",
|
||||
"The start position relative to the parent (in nanoseconds)",
|
||||
0, G_MAXUINT64, 0, G_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_START,
|
||||
properties[PROP_START]);
|
||||
|
||||
/**
|
||||
* GnlObject:duration
|
||||
*
|
||||
* The outgoing duration in nanoseconds.
|
||||
*/
|
||||
properties[PROP_DURATION] = g_param_spec_int64 ("duration", "Duration",
|
||||
"Outgoing duration (in nanoseconds)", 0, G_MAXINT64, 0,
|
||||
G_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_DURATION,
|
||||
properties[PROP_DURATION]);
|
||||
|
||||
/**
|
||||
* GnlObject:stop
|
||||
*
|
||||
* The stop position relative to the parent in nanoseconds.
|
||||
*
|
||||
* This value is computed based on the values of start and duration.
|
||||
*/
|
||||
properties[PROP_STOP] = g_param_spec_uint64 ("stop", "Stop",
|
||||
"The stop position relative to the parent (in nanoseconds)",
|
||||
0, G_MAXUINT64, 0, G_PARAM_READABLE);
|
||||
g_object_class_install_property (gobject_class, PROP_STOP,
|
||||
properties[PROP_STOP]);
|
||||
|
||||
/**
|
||||
* GnlObject:inpoint
|
||||
*
|
||||
* The media start position in nanoseconds.
|
||||
*
|
||||
* Also called 'in-point' in video-editing, this corresponds to
|
||||
* what position in the 'contained' object we should start outputting from.
|
||||
*/
|
||||
properties[PROP_INPOINT] =
|
||||
g_param_spec_uint64 ("inpoint", "Media start",
|
||||
"The media start position (in nanoseconds)", 0, G_MAXUINT64,
|
||||
GST_CLOCK_TIME_NONE, G_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_INPOINT,
|
||||
properties[PROP_INPOINT]);
|
||||
|
||||
/**
|
||||
* GnlObject:priority
|
||||
*
|
||||
* The priority of the object in the container.
|
||||
*
|
||||
* The highest priority is 0, meaning this object will be selected over
|
||||
* any other between start and stop.
|
||||
*
|
||||
* The lowest priority is G_MAXUINT32.
|
||||
*
|
||||
* Objects whose priority is (-1) will be considered as 'default' objects
|
||||
* in GnlComposition and their start/stop values will be modified as to
|
||||
* fit the whole duration of the composition.
|
||||
*/
|
||||
properties[PROP_PRIORITY] = g_param_spec_uint ("priority", "Priority",
|
||||
"The priority of the object (0 = highest priority)", 0, G_MAXUINT, 0,
|
||||
G_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_PRIORITY,
|
||||
properties[PROP_PRIORITY]);
|
||||
|
||||
/**
|
||||
* GnlObject:active
|
||||
*
|
||||
* Indicates whether this object should be used by its container.
|
||||
*
|
||||
* Set to #TRUE to temporarily disable this object in a #GnlComposition.
|
||||
*/
|
||||
properties[PROP_ACTIVE] = g_param_spec_boolean ("active", "Active",
|
||||
"Use this object in the GnlComposition", TRUE, G_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_ACTIVE,
|
||||
properties[PROP_ACTIVE]);
|
||||
|
||||
/**
|
||||
* GnlObject:caps
|
||||
*
|
||||
* Caps used to filter/choose the output stream.
|
||||
*
|
||||
* If the controlled object produces several stream, you can set this
|
||||
* property to choose a specific stream.
|
||||
*
|
||||
* If nothing is specified then a source pad will be chosen at random.
|
||||
*/
|
||||
properties[PROP_CAPS] = g_param_spec_boxed ("caps", "Caps",
|
||||
"Caps used to filter/choose the output stream",
|
||||
GST_TYPE_CAPS, G_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_CAPS,
|
||||
properties[PROP_CAPS]);
|
||||
|
||||
/**
|
||||
* GnlObject:expandable
|
||||
*
|
||||
* Indicates whether this object should expand to the full duration of its
|
||||
* container #GnlComposition.
|
||||
*/
|
||||
properties[PROP_EXPANDABLE] =
|
||||
g_param_spec_boolean ("expandable", "Expandable",
|
||||
"Expand to the full duration of the container composition", FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
g_object_class_install_property (gobject_class, PROP_EXPANDABLE,
|
||||
properties[PROP_EXPANDABLE]);
|
||||
}
|
||||
|
||||
static void
|
||||
gnl_object_init (GnlObject * object)
|
||||
{
|
||||
object->start = object->pending_start = 0;
|
||||
object->duration = object->pending_duration = 0;
|
||||
object->stop = 0;
|
||||
|
||||
object->inpoint = object->pending_inpoint = GST_CLOCK_TIME_NONE;
|
||||
object->priority = object->pending_priority = 0;
|
||||
object->active = object->pending_active = TRUE;
|
||||
|
||||
object->caps = gst_caps_new_any ();
|
||||
|
||||
object->segment_rate = 1.0;
|
||||
object->segment_start = -1;
|
||||
object->segment_stop = -1;
|
||||
}
|
||||
|
||||
static void
|
||||
gnl_object_dispose (GObject * object)
|
||||
{
|
||||
GnlObject *gnl = (GnlObject *) object;
|
||||
|
||||
if (gnl->caps) {
|
||||
gst_caps_unref (gnl->caps);
|
||||
gnl->caps = NULL;
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
/**
|
||||
* gnl_object_to_media_time:
|
||||
* @object: a #GnlObject
|
||||
* @objecttime: The #GstClockTime we want to convert
|
||||
* @mediatime: A pointer on a #GstClockTime to fill
|
||||
*
|
||||
* Converts a #GstClockTime from the object (container) context to the media context
|
||||
*
|
||||
* Returns: TRUE if @objecttime was within the limits of the @object start/stop time,
|
||||
* FALSE otherwise
|
||||
*/
|
||||
gboolean
|
||||
gnl_object_to_media_time (GnlObject * object, GstClockTime otime,
|
||||
GstClockTime * mtime)
|
||||
{
|
||||
g_return_val_if_fail (mtime, FALSE);
|
||||
|
||||
GST_DEBUG_OBJECT (object, "ObjectTime : %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (otime));
|
||||
|
||||
GST_DEBUG_OBJECT (object,
|
||||
"Start/Stop:[%" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT "] "
|
||||
"Media start: %" GST_TIME_FORMAT, GST_TIME_ARGS (object->start),
|
||||
GST_TIME_ARGS (object->stop), GST_TIME_ARGS (object->inpoint));
|
||||
|
||||
/* limit check */
|
||||
if (G_UNLIKELY ((otime < object->start))) {
|
||||
GST_DEBUG_OBJECT (object, "ObjectTime is before start");
|
||||
*mtime = (object->inpoint == GST_CLOCK_TIME_NONE) ? 0 : object->inpoint;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (G_UNLIKELY ((otime >= object->stop))) {
|
||||
GST_DEBUG_OBJECT (object, "ObjectTime is after stop");
|
||||
if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (object->inpoint)))
|
||||
*mtime = object->inpoint + object->duration;
|
||||
else
|
||||
*mtime = object->stop - object->start;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (G_UNLIKELY (object->inpoint == GST_CLOCK_TIME_NONE)) {
|
||||
/* no time shifting, for live sources ? */
|
||||
*mtime = otime - object->start;
|
||||
} else {
|
||||
*mtime = otime - object->start + object->inpoint;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (object, "Returning MediaTime : %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (*mtime));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gnl_media_to_object_time:
|
||||
* @object: The #GnlObject
|
||||
* @mediatime: The #GstClockTime we want to convert
|
||||
* @objecttime: A pointer on a #GstClockTime to fill
|
||||
*
|
||||
* Converts a #GstClockTime from the media context to the object (container) context
|
||||
*
|
||||
* Returns: TRUE if @objecttime was within the limits of the @object media start/stop time,
|
||||
* FALSE otherwise
|
||||
*/
|
||||
|
||||
gboolean
|
||||
gnl_media_to_object_time (GnlObject * object, GstClockTime mtime,
|
||||
GstClockTime * otime)
|
||||
{
|
||||
g_return_val_if_fail (otime, FALSE);
|
||||
|
||||
GST_DEBUG_OBJECT (object, "MediaTime : %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (mtime));
|
||||
|
||||
GST_DEBUG_OBJECT (object,
|
||||
"Start/Stop:[%" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT "] "
|
||||
"inpoint %" GST_TIME_FORMAT, GST_TIME_ARGS (object->start),
|
||||
GST_TIME_ARGS (object->stop), GST_TIME_ARGS (object->inpoint));
|
||||
|
||||
|
||||
/* limit check */
|
||||
if (G_UNLIKELY ((object->inpoint != GST_CLOCK_TIME_NONE)
|
||||
&& (mtime < object->inpoint))) {
|
||||
GST_DEBUG_OBJECT (object, "media time is before inpoint, forcing to start");
|
||||
*otime = object->start;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (G_LIKELY (object->inpoint != GST_CLOCK_TIME_NONE)) {
|
||||
*otime = mtime - object->inpoint + object->start;
|
||||
} else
|
||||
*otime = mtime + object->start;
|
||||
|
||||
GST_DEBUG_OBJECT (object, "Returning ObjectTime : %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (*otime));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gnl_object_prepare_func (GnlObject * object)
|
||||
{
|
||||
GST_DEBUG_OBJECT (object, "default prepare function, returning TRUE");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GstStateChangeReturn
|
||||
gnl_object_prepare (GnlObject * object)
|
||||
{
|
||||
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
|
||||
|
||||
GST_DEBUG_OBJECT (object, "preparing");
|
||||
|
||||
if (!(GNL_OBJECT_GET_CLASS (object)->prepare (object)))
|
||||
ret = GST_STATE_CHANGE_FAILURE;
|
||||
|
||||
GST_DEBUG_OBJECT (object, "finished preparing, returning %d", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gnl_object_cleanup_func (GnlObject * object)
|
||||
{
|
||||
GST_DEBUG_OBJECT (object, "default cleanup function, returning TRUE");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GstStateChangeReturn
|
||||
gnl_object_cleanup (GnlObject * object)
|
||||
{
|
||||
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
|
||||
|
||||
GST_DEBUG_OBJECT (object, "cleaning-up");
|
||||
|
||||
if (!(GNL_OBJECT_GET_CLASS (object)->cleanup (object)))
|
||||
ret = GST_STATE_CHANGE_FAILURE;
|
||||
|
||||
GST_DEBUG_OBJECT (object, "finished preparing, returning %d", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
gnl_object_set_caps (GnlObject * object, const GstCaps * caps)
|
||||
{
|
||||
if (object->caps)
|
||||
gst_caps_unref (object->caps);
|
||||
|
||||
object->caps = gst_caps_copy (caps);
|
||||
}
|
||||
|
||||
static inline void
|
||||
_update_stop (GnlObject * gnlobject)
|
||||
{
|
||||
/* check if start/duration has changed */
|
||||
|
||||
if ((gnlobject->pending_start + gnlobject->pending_duration) !=
|
||||
gnlobject->stop) {
|
||||
gnlobject->stop = gnlobject->pending_start + gnlobject->pending_duration;
|
||||
|
||||
GST_LOG_OBJECT (gnlobject,
|
||||
"Updating stop value : %" GST_TIME_FORMAT " [start:%" GST_TIME_FORMAT
|
||||
", duration:%" GST_TIME_FORMAT "]", GST_TIME_ARGS (gnlobject->stop),
|
||||
GST_TIME_ARGS (gnlobject->pending_start),
|
||||
GST_TIME_ARGS (gnlobject->pending_duration));
|
||||
g_object_notify_by_pspec (G_OBJECT (gnlobject), properties[PROP_STOP]);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
update_values (GnlObject * object)
|
||||
{
|
||||
CHECK_AND_SET (START, start, "start", G_GUINT64_FORMAT);
|
||||
CHECK_AND_SET (INPOINT, inpoint, "inpoint", G_GUINT64_FORMAT);
|
||||
CHECK_AND_SET (DURATION, duration, "duration", G_GINT64_FORMAT);
|
||||
CHECK_AND_SET (PRIORITY, priority, "priority", G_GUINT32_FORMAT);
|
||||
CHECK_AND_SET (ACTIVE, active, "active", G_GUINT32_FORMAT);
|
||||
|
||||
_update_stop (object);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gnl_object_commit_func (GnlObject * object, gboolean recurse)
|
||||
{
|
||||
GST_INFO_OBJECT (object, "Commiting object changed");
|
||||
|
||||
if (object->commit_needed == FALSE) {
|
||||
GST_INFO_OBJECT (object, "No changes to commit");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
update_values (object);
|
||||
|
||||
GST_INFO_OBJECT (object, "Done commiting");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gnl_object_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GnlObject *gnlobject = (GnlObject *) object;
|
||||
|
||||
g_return_if_fail (GNL_IS_OBJECT (object));
|
||||
|
||||
GST_OBJECT_LOCK (object);
|
||||
switch (prop_id) {
|
||||
case PROP_START:
|
||||
SET_PENDING_VALUE (start, "start", uint64, G_GUINT64_FORMAT);
|
||||
break;
|
||||
case PROP_DURATION:
|
||||
SET_PENDING_VALUE (duration, "duration", int64, G_GINT64_FORMAT);
|
||||
break;
|
||||
case PROP_INPOINT:
|
||||
SET_PENDING_VALUE (inpoint, "inpoint", uint64, G_GUINT64_FORMAT);
|
||||
break;
|
||||
case PROP_PRIORITY:
|
||||
SET_PENDING_VALUE (priority, "priority", uint, G_GUINT32_FORMAT);
|
||||
break;
|
||||
case PROP_ACTIVE:
|
||||
SET_PENDING_VALUE (active, "active", boolean, G_GUINT32_FORMAT);
|
||||
break;
|
||||
case PROP_CAPS:
|
||||
gnl_object_set_caps (gnlobject, gst_value_get_caps (value));
|
||||
break;
|
||||
case PROP_EXPANDABLE:
|
||||
if (g_value_get_boolean (value))
|
||||
GST_OBJECT_FLAG_SET (gnlobject, GNL_OBJECT_EXPANDABLE);
|
||||
else
|
||||
GST_OBJECT_FLAG_UNSET (gnlobject, GNL_OBJECT_EXPANDABLE);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
GST_OBJECT_UNLOCK (object);
|
||||
|
||||
_update_stop (gnlobject);
|
||||
}
|
||||
|
||||
static void
|
||||
gnl_object_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GnlObject *gnlobject = (GnlObject *) object;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_START:
|
||||
g_value_set_uint64 (value, gnlobject->pending_start);
|
||||
break;
|
||||
case PROP_DURATION:
|
||||
g_value_set_int64 (value, gnlobject->pending_duration);
|
||||
break;
|
||||
case PROP_STOP:
|
||||
g_value_set_uint64 (value, gnlobject->stop);
|
||||
break;
|
||||
case PROP_INPOINT:
|
||||
g_value_set_uint64 (value, gnlobject->pending_inpoint);
|
||||
break;
|
||||
case PROP_PRIORITY:
|
||||
g_value_set_uint (value, gnlobject->pending_priority);
|
||||
break;
|
||||
case PROP_ACTIVE:
|
||||
g_value_set_boolean (value, gnlobject->pending_active);
|
||||
break;
|
||||
case PROP_CAPS:
|
||||
gst_value_set_caps (value, gnlobject->caps);
|
||||
break;
|
||||
case PROP_EXPANDABLE:
|
||||
g_value_set_boolean (value, GNL_OBJECT_IS_EXPANDABLE (object));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static GstStateChangeReturn
|
||||
gnl_object_change_state (GstElement * element, GstStateChange transition)
|
||||
{
|
||||
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
|
||||
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_NULL_TO_READY:
|
||||
{
|
||||
GstObject *parent = gst_object_get_parent (GST_OBJECT (element));
|
||||
|
||||
/* Going to READY and if we are not in a composition, we need to make
|
||||
* sure that the object positioning state is properly commited */
|
||||
if (parent) {
|
||||
if (!GNL_OBJECT_IS_COMPOSITION (parent) &&
|
||||
!GNL_OBJECT_IS_COMPOSITION (GNL_OBJECT (element))) {
|
||||
GST_DEBUG ("Adding gnlobject to something that is not a composition,"
|
||||
" commiting ourself");
|
||||
gnl_object_commit (GNL_OBJECT (element), FALSE);
|
||||
}
|
||||
|
||||
gst_object_unref (parent);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
||||
gnl_object_commit (GNL_OBJECT (element), FALSE);
|
||||
if (gnl_object_prepare (GNL_OBJECT (element)) == GST_STATE_CHANGE_FAILURE) {
|
||||
ret = GST_STATE_CHANGE_FAILURE;
|
||||
goto beach;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (element, "Calling parent change_state");
|
||||
|
||||
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
||||
|
||||
GST_DEBUG_OBJECT (element, "Return from parent change_state was %d", ret);
|
||||
|
||||
if (ret == GST_STATE_CHANGE_FAILURE)
|
||||
goto beach;
|
||||
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||
/* cleanup gnlobject */
|
||||
if (gnl_object_cleanup (GNL_OBJECT (element)) == GST_STATE_CHANGE_FAILURE)
|
||||
ret = GST_STATE_CHANGE_FAILURE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
beach:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
gnl_object_set_commit_needed (GnlObject * object)
|
||||
{
|
||||
if (G_UNLIKELY (object->commiting)) {
|
||||
GST_WARNING_OBJECT (object,
|
||||
"Trying to set 'commit-needed' while commiting");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (object, "Setting 'commit_needed'");
|
||||
object->commit_needed = TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gnl_object_commit (GnlObject * object, gboolean recurse)
|
||||
{
|
||||
gboolean ret;
|
||||
|
||||
GST_DEBUG_OBJECT (object, "Commiting object state");
|
||||
|
||||
object->commiting = TRUE;
|
||||
ret = GNL_OBJECT_GET_CLASS (object)->commit (object, recurse);
|
||||
object->commiting = FALSE;
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
gnl_object_reset (GnlObject * object)
|
||||
{
|
||||
GST_INFO_OBJECT (object, "Resetting child timing values to default");
|
||||
|
||||
object->start = 0;
|
||||
object->duration = 0;
|
||||
object->stop = 0;
|
||||
object->inpoint = GST_CLOCK_TIME_NONE;
|
||||
object->priority = 0;
|
||||
object->active = TRUE;
|
||||
}
|
160
gnl/gnlobject.h
Normal file
160
gnl/gnlobject.h
Normal file
|
@ -0,0 +1,160 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2001 Wim Taymans <wim.taymans@gmail.com>
|
||||
* 2004-2008 Edward Hervey <bilboed@bilboed.com>
|
||||
*
|
||||
* gnlobject.h: Header for base GnlObject
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GNL_OBJECT_H__
|
||||
#define __GNL_OBJECT_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include "gnltypes.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
#define GNL_TYPE_OBJECT \
|
||||
(gnl_object_get_type())
|
||||
#define GNL_OBJECT(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GNL_TYPE_OBJECT,GnlObject))
|
||||
#define GNL_OBJECT_CAST(obj) ((GnlObject*) (obj))
|
||||
#define GNL_OBJECT_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass),GNL_TYPE_OBJECT,GnlObjectClass))
|
||||
#define GNL_OBJECT_GET_CLASS(obj) \
|
||||
(G_TYPE_INSTANCE_GET_CLASS ((obj), GNL_TYPE_OBJECT, GnlObjectClass))
|
||||
#define GNL_IS_OBJECT(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GNL_TYPE_OBJECT))
|
||||
#define GNL_IS_OBJECT_CLASS(obj) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GNL_TYPE_OBJECT))
|
||||
|
||||
/**
|
||||
* GnlObjectFlags:
|
||||
* @GNL_OBJECT_IS_SOURCE:
|
||||
* @GNL_OBJECT_IS_OPERATION:
|
||||
* @GNL_OBJECT_IS_EXPANDABLE: The #GnlObject start/stop will extend accross the full composition.
|
||||
* @GNL_OBJECT_LAST_FLAG:
|
||||
*/
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GNL_OBJECT_SOURCE = (GST_BIN_FLAG_LAST << 0),
|
||||
GNL_OBJECT_OPERATION = (GST_BIN_FLAG_LAST << 1),
|
||||
GNL_OBJECT_EXPANDABLE = (GST_BIN_FLAG_LAST << 2),
|
||||
GNL_OBJECT_COMPOSITION = (GST_BIN_FLAG_LAST << 3),
|
||||
/* padding */
|
||||
GNL_OBJECT_LAST_FLAG = (GST_BIN_FLAG_LAST << 5)
|
||||
} GnlObjectFlags;
|
||||
|
||||
|
||||
#define GNL_OBJECT_IS_SOURCE(obj) \
|
||||
(GST_OBJECT_FLAG_IS_SET(obj, GNL_OBJECT_SOURCE))
|
||||
#define GNL_OBJECT_IS_OPERATION(obj) \
|
||||
(GST_OBJECT_FLAG_IS_SET(obj, GNL_OBJECT_OPERATION))
|
||||
#define GNL_OBJECT_IS_EXPANDABLE(obj) \
|
||||
(GST_OBJECT_FLAG_IS_SET(obj, GNL_OBJECT_EXPANDABLE))
|
||||
#define GNL_OBJECT_IS_COMPOSITION(obj) \
|
||||
(GST_OBJECT_FLAG_IS_SET(obj, GNL_OBJECT_COMPOSITION))
|
||||
|
||||
/* For internal usage only */
|
||||
#define GNL_OBJECT_START(obj) (GNL_OBJECT_CAST (obj)->start)
|
||||
#define GNL_OBJECT_STOP(obj) (GNL_OBJECT_CAST (obj)->stop)
|
||||
#define GNL_OBJECT_DURATION(obj) (GNL_OBJECT_CAST (obj)->duration)
|
||||
#define GNL_OBJECT_INPOINT(obj) (GNL_OBJECT_CAST (obj)->inpoint)
|
||||
#define GNL_OBJECT_PRIORITY(obj) (GNL_OBJECT_CAST (obj)->priority)
|
||||
|
||||
#define GNL_OBJECT_IS_COMMITING(obj) (GNL_OBJECT_CAST (obj)->commiting)
|
||||
|
||||
struct _GnlObject
|
||||
{
|
||||
GstBin parent;
|
||||
|
||||
/* Time positionning */
|
||||
GstClockTime start;
|
||||
GstClockTime inpoint;
|
||||
GstClockTimeDiff duration;
|
||||
|
||||
/* Pending time positionning
|
||||
* Should be == GST_CLOCK_TIME_NONE when nothing to do
|
||||
*/
|
||||
GstClockTime pending_start;
|
||||
GstClockTime pending_inpoint;
|
||||
GstClockTimeDiff pending_duration;
|
||||
guint32 pending_priority;
|
||||
gboolean pending_active;
|
||||
|
||||
gboolean commit_needed;
|
||||
gboolean commiting; /* Set to TRUE during the commiting time only */
|
||||
|
||||
gboolean expandable;
|
||||
|
||||
/* read-only */
|
||||
GstClockTime stop;
|
||||
|
||||
/* priority in parent */
|
||||
guint32 priority;
|
||||
|
||||
/* active in parent */
|
||||
gboolean active;
|
||||
|
||||
/* Filtering caps */
|
||||
GstCaps *caps;
|
||||
|
||||
/* current segment seek <RO> */
|
||||
gdouble segment_rate;
|
||||
GstSeekFlags segment_flags;
|
||||
gint64 segment_start;
|
||||
gint64 segment_stop;
|
||||
};
|
||||
|
||||
struct _GnlObjectClass
|
||||
{
|
||||
GstBinClass parent_class;
|
||||
|
||||
/* Signal method handler */
|
||||
gboolean (*commit_signal_handler) (GnlObject * object, gboolean recurse);
|
||||
|
||||
/* virtual methods for subclasses */
|
||||
gboolean (*prepare) (GnlObject * object);
|
||||
gboolean (*cleanup) (GnlObject * object);
|
||||
gboolean (*commit) (GnlObject * object, gboolean recurse);
|
||||
};
|
||||
|
||||
GType gnl_object_get_type (void);
|
||||
|
||||
gboolean
|
||||
gnl_object_to_media_time (GnlObject * object, GstClockTime otime,
|
||||
GstClockTime * mtime);
|
||||
|
||||
gboolean
|
||||
gnl_media_to_object_time (GnlObject * object, GstClockTime mtime,
|
||||
GstClockTime * otime);
|
||||
|
||||
void
|
||||
gnl_object_set_caps (GnlObject * object, const GstCaps * caps);
|
||||
|
||||
void
|
||||
gnl_object_set_commit_needed (GnlObject *object);
|
||||
|
||||
gboolean
|
||||
gnl_object_commit (GnlObject *object, gboolean recurse);
|
||||
|
||||
void
|
||||
gnl_object_reset (GnlObject *object);
|
||||
G_END_DECLS
|
||||
#endif /* __GNL_OBJECT_H__ */
|
792
gnl/gnloperation.c
Normal file
792
gnl/gnloperation.c
Normal file
|
@ -0,0 +1,792 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2001 Wim Taymans <wim.taymans@gmail.com>
|
||||
* 2004-2008 Edward Hervey <bilboed@bilboed.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 "gnl.h"
|
||||
|
||||
/**
|
||||
* SECTION:element-gnloperation
|
||||
*
|
||||
* <refsect2>
|
||||
* <para>
|
||||
* A GnlOperation performs a transformation or mixing operation on the
|
||||
* data from one or more #GnlSources, which is used to implement filters or
|
||||
* effects.
|
||||
* </para>
|
||||
* </refsect2>
|
||||
*/
|
||||
|
||||
static GstStaticPadTemplate gnl_operation_src_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("src",
|
||||
GST_PAD_SRC,
|
||||
GST_PAD_SOMETIMES,
|
||||
GST_STATIC_CAPS_ANY);
|
||||
|
||||
static GstStaticPadTemplate gnl_operation_sink_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("sink%d",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_REQUEST,
|
||||
GST_STATIC_CAPS_ANY);
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (gnloperation);
|
||||
#define GST_CAT_DEFAULT gnloperation
|
||||
|
||||
#define _do_init \
|
||||
GST_DEBUG_CATEGORY_INIT (gnloperation, "gnloperation", GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin Operation element");
|
||||
#define gnl_operation_parent_class parent_class
|
||||
G_DEFINE_TYPE_WITH_CODE (GnlOperation, gnl_operation, GNL_TYPE_OBJECT,
|
||||
_do_init);
|
||||
|
||||
enum
|
||||
{
|
||||
ARG_0,
|
||||
ARG_SINKS,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
INPUT_PRIORITY_CHANGED,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint gnl_operation_signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static void gnl_operation_dispose (GObject * object);
|
||||
|
||||
static void gnl_operation_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec);
|
||||
static void gnl_operation_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec);
|
||||
|
||||
static gboolean gnl_operation_prepare (GnlObject * object);
|
||||
static gboolean gnl_operation_cleanup (GnlObject * object);
|
||||
|
||||
static gboolean gnl_operation_add_element (GstBin * bin, GstElement * element);
|
||||
static gboolean gnl_operation_remove_element (GstBin * bin,
|
||||
GstElement * element);
|
||||
|
||||
static GstPad *gnl_operation_request_new_pad (GstElement * element,
|
||||
GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
|
||||
static void gnl_operation_release_pad (GstElement * element, GstPad * pad);
|
||||
|
||||
static void synchronize_sinks (GnlOperation * operation);
|
||||
static gboolean remove_sink_pad (GnlOperation * operation, GstPad * sinkpad);
|
||||
|
||||
static void
|
||||
gnl_operation_class_init (GnlOperationClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = (GObjectClass *) klass;
|
||||
GstBinClass *gstbin_class = (GstBinClass *) klass;
|
||||
|
||||
GstElementClass *gstelement_class = (GstElementClass *) klass;
|
||||
GnlObjectClass *gnlobject_class = (GnlObjectClass *) klass;
|
||||
|
||||
gst_element_class_set_static_metadata (gstelement_class, "GNonLin Operation",
|
||||
"Filter/Editor",
|
||||
"Encapsulates filters/effects for use with GNL Objects",
|
||||
"Wim Taymans <wim.taymans@gmail.com>, Edward Hervey <bilboed@bilboed.com>");
|
||||
|
||||
gobject_class->dispose = GST_DEBUG_FUNCPTR (gnl_operation_dispose);
|
||||
|
||||
gobject_class->set_property = GST_DEBUG_FUNCPTR (gnl_operation_set_property);
|
||||
gobject_class->get_property = GST_DEBUG_FUNCPTR (gnl_operation_get_property);
|
||||
|
||||
/**
|
||||
* GnlOperation:sinks:
|
||||
*
|
||||
* Specifies the number of sink pads the operation should provide.
|
||||
* If the sinks property is -1 (the default) pads are only created as
|
||||
* demanded via get_request_pad() calls on the element.
|
||||
*/
|
||||
g_object_class_install_property (gobject_class, ARG_SINKS,
|
||||
g_param_spec_int ("sinks", "Sinks",
|
||||
"Number of input sinks (-1 for automatic handling)", -1, G_MAXINT, -1,
|
||||
G_PARAM_READWRITE));
|
||||
|
||||
/**
|
||||
* GnlOperation:input-priority-changed:
|
||||
* @pad: The operation's input pad whose priority changed.
|
||||
* @priority: The new priority
|
||||
*
|
||||
* Signals that the @priority of the stream being fed to the given @pad
|
||||
* might have changed.
|
||||
*/
|
||||
gnl_operation_signals[INPUT_PRIORITY_CHANGED] =
|
||||
g_signal_new ("input-priority-changed", G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GnlOperationClass,
|
||||
input_priority_changed), NULL, NULL, g_cclosure_marshal_generic,
|
||||
G_TYPE_NONE, 2, GST_TYPE_PAD, G_TYPE_UINT);
|
||||
|
||||
gstelement_class->request_new_pad =
|
||||
GST_DEBUG_FUNCPTR (gnl_operation_request_new_pad);
|
||||
gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gnl_operation_release_pad);
|
||||
|
||||
gstbin_class->add_element = GST_DEBUG_FUNCPTR (gnl_operation_add_element);
|
||||
gstbin_class->remove_element =
|
||||
GST_DEBUG_FUNCPTR (gnl_operation_remove_element);
|
||||
|
||||
gnlobject_class->prepare = GST_DEBUG_FUNCPTR (gnl_operation_prepare);
|
||||
gnlobject_class->cleanup = GST_DEBUG_FUNCPTR (gnl_operation_cleanup);
|
||||
|
||||
gst_element_class_add_pad_template (gstelement_class,
|
||||
gst_static_pad_template_get (&gnl_operation_src_template));
|
||||
|
||||
gst_element_class_add_pad_template (gstelement_class,
|
||||
gst_static_pad_template_get (&gnl_operation_sink_template));
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
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;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (object, "Disposing of sink pad(s)");
|
||||
while (oper->sinks) {
|
||||
GstPad *ghost = (GstPad *) oper->sinks->data;
|
||||
remove_sink_pad (oper, ghost);
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (object, "Done, calling parent class ::dispose()");
|
||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gnl_operation_reset (GnlOperation * operation)
|
||||
{
|
||||
operation->num_sinks = 1;
|
||||
operation->realsinks = 0;
|
||||
operation->next_base_time = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
gnl_operation_init (GnlOperation * operation)
|
||||
{
|
||||
gnl_operation_reset (operation);
|
||||
operation->ghostpad = NULL;
|
||||
operation->element = NULL;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
element_is_valid_filter (GstElement * element, gboolean * isdynamic)
|
||||
{
|
||||
gboolean havesink = FALSE;
|
||||
gboolean havesrc = FALSE;
|
||||
gboolean done = FALSE;
|
||||
GstIterator *pads;
|
||||
GValue item = { 0, };
|
||||
|
||||
if (isdynamic)
|
||||
*isdynamic = FALSE;
|
||||
|
||||
pads = gst_element_iterate_pads (element);
|
||||
|
||||
while (!done) {
|
||||
switch (gst_iterator_next (pads, &item)) {
|
||||
case GST_ITERATOR_OK:
|
||||
{
|
||||
GstPad *pad = g_value_get_object (&item);
|
||||
|
||||
if (gst_pad_get_direction (pad) == GST_PAD_SRC)
|
||||
havesrc = TRUE;
|
||||
else if (gst_pad_get_direction (pad) == GST_PAD_SINK)
|
||||
havesink = TRUE;
|
||||
|
||||
g_value_reset (&item);
|
||||
break;
|
||||
}
|
||||
case GST_ITERATOR_RESYNC:
|
||||
gst_iterator_resync (pads);
|
||||
havesrc = FALSE;
|
||||
havesink = FALSE;
|
||||
break;
|
||||
default:
|
||||
/* ERROR and DONE */
|
||||
done = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_value_unset (&item);
|
||||
gst_iterator_free (pads);
|
||||
|
||||
/* just look at the element's class, not the factory, since there might
|
||||
* not be a factory (in case of python elements) or the factory is the
|
||||
* wrong one (in case of a GstBin sub-class) and doesn't have complete
|
||||
* information. */
|
||||
{
|
||||
GList *tmp =
|
||||
gst_element_class_get_pad_template_list (GST_ELEMENT_GET_CLASS
|
||||
(element));
|
||||
|
||||
while (tmp) {
|
||||
GstPadTemplate *template = (GstPadTemplate *) tmp->data;
|
||||
|
||||
if (template->direction == GST_PAD_SRC)
|
||||
havesrc = TRUE;
|
||||
else if (template->direction == GST_PAD_SINK) {
|
||||
if (!havesink && (template->presence == GST_PAD_REQUEST) && isdynamic)
|
||||
*isdynamic = TRUE;
|
||||
havesink = TRUE;
|
||||
}
|
||||
tmp = tmp->next;
|
||||
}
|
||||
}
|
||||
return (havesink && havesrc);
|
||||
}
|
||||
|
||||
/*
|
||||
* get_src_pad:
|
||||
* element: a #GstElement
|
||||
*
|
||||
* Returns: The src pad for the given element. A reference was added to the
|
||||
* returned pad, remove it when you don't need that pad anymore.
|
||||
* Returns NULL if there's no source pad.
|
||||
*/
|
||||
|
||||
static GstPad *
|
||||
get_src_pad (GstElement * element)
|
||||
{
|
||||
GstIterator *it;
|
||||
GstIteratorResult itres;
|
||||
GValue item = { 0, };
|
||||
GstPad *srcpad = NULL;
|
||||
|
||||
it = gst_element_iterate_src_pads (element);
|
||||
itres = gst_iterator_next (it, &item);
|
||||
if (itres != GST_ITERATOR_OK) {
|
||||
GST_DEBUG ("%s doesn't have a src pad !", GST_ELEMENT_NAME (element));
|
||||
} else {
|
||||
srcpad = g_value_get_object (&item);
|
||||
gst_object_ref (srcpad);
|
||||
}
|
||||
g_value_reset (&item);
|
||||
gst_iterator_free (it);
|
||||
|
||||
return srcpad;
|
||||
}
|
||||
|
||||
/* get_nb_static_sinks:
|
||||
*
|
||||
* Returns : The number of static sink pads of the controlled element.
|
||||
*/
|
||||
static guint
|
||||
get_nb_static_sinks (GnlOperation * oper)
|
||||
{
|
||||
GstIterator *sinkpads;
|
||||
gboolean done = FALSE;
|
||||
guint nbsinks = 0;
|
||||
GValue item = { 0, };
|
||||
|
||||
sinkpads = gst_element_iterate_sink_pads (oper->element);
|
||||
|
||||
while (!done) {
|
||||
switch (gst_iterator_next (sinkpads, &item)) {
|
||||
case GST_ITERATOR_OK:{
|
||||
nbsinks++;
|
||||
g_value_unset (&item);
|
||||
}
|
||||
break;
|
||||
case GST_ITERATOR_RESYNC:
|
||||
nbsinks = 0;
|
||||
gst_iterator_resync (sinkpads);
|
||||
break;
|
||||
default:
|
||||
/* ERROR and DONE */
|
||||
done = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_value_reset (&item);
|
||||
gst_iterator_free (sinkpads);
|
||||
|
||||
GST_DEBUG ("We found %d static sinks", nbsinks);
|
||||
|
||||
return nbsinks;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gnl_operation_add_element (GstBin * bin, GstElement * element)
|
||||
{
|
||||
GnlOperation *operation = (GnlOperation *) bin;
|
||||
gboolean res = FALSE;
|
||||
gboolean isdynamic;
|
||||
|
||||
GST_DEBUG_OBJECT (bin, "element:%s", GST_ELEMENT_NAME (element));
|
||||
|
||||
if (operation->element) {
|
||||
GST_WARNING_OBJECT (operation,
|
||||
"We already control an element : %s , remove it first",
|
||||
GST_OBJECT_NAME (operation->element));
|
||||
} else {
|
||||
if (!element_is_valid_filter (element, &isdynamic)) {
|
||||
GST_WARNING_OBJECT (operation,
|
||||
"Element %s is not a valid filter element",
|
||||
GST_ELEMENT_NAME (element));
|
||||
} else {
|
||||
if ((res = GST_BIN_CLASS (parent_class)->add_element (bin, element))) {
|
||||
GstPad *srcpad;
|
||||
|
||||
srcpad = get_src_pad (element);
|
||||
if (!srcpad)
|
||||
return FALSE;
|
||||
|
||||
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);
|
||||
|
||||
/* Remove the reference get_src_pad gave us */
|
||||
gst_object_unref (srcpad);
|
||||
|
||||
/* Figure out number of static sink pads */
|
||||
operation->num_sinks = get_nb_static_sinks (operation);
|
||||
|
||||
/* Finally sync the ghostpads with the real pads */
|
||||
synchronize_sinks (operation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gnl_operation_remove_element (GstBin * bin, GstElement * element)
|
||||
{
|
||||
GnlOperation *operation = (GnlOperation *) bin;
|
||||
gboolean res = FALSE;
|
||||
|
||||
if (operation->element) {
|
||||
if ((res = GST_BIN_CLASS (parent_class)->remove_element (bin, element)))
|
||||
operation->element = NULL;
|
||||
} else {
|
||||
GST_WARNING_OBJECT (bin,
|
||||
"Element %s is not the one controlled by this operation",
|
||||
GST_ELEMENT_NAME (element));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void
|
||||
gnl_operation_set_sinks (GnlOperation * operation, guint sinks)
|
||||
{
|
||||
/* FIXME : Check if sinkpad of element is on-demand .... */
|
||||
|
||||
operation->num_sinks = sinks;
|
||||
synchronize_sinks (operation);
|
||||
}
|
||||
|
||||
static void
|
||||
gnl_operation_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GnlOperation *operation = (GnlOperation *) object;
|
||||
|
||||
switch (prop_id) {
|
||||
case ARG_SINKS:
|
||||
gnl_operation_set_sinks (operation, g_value_get_int (value));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gnl_operation_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GnlOperation *operation = (GnlOperation *) object;
|
||||
|
||||
switch (prop_id) {
|
||||
case ARG_SINKS:
|
||||
g_value_set_int (value, operation->num_sinks);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns the first unused sink pad of the controlled element.
|
||||
* Only use with static element. Unref after usage.
|
||||
* Returns NULL if there's no more unused sink pads.
|
||||
*/
|
||||
static GstPad *
|
||||
get_unused_static_sink_pad (GnlOperation * operation)
|
||||
{
|
||||
GstIterator *pads;
|
||||
gboolean done = FALSE;
|
||||
GValue item = { 0, };
|
||||
GstPad *ret = NULL;
|
||||
|
||||
if (!operation->element)
|
||||
return NULL;
|
||||
|
||||
pads = gst_element_iterate_pads (operation->element);
|
||||
|
||||
while (!done) {
|
||||
switch (gst_iterator_next (pads, &item)) {
|
||||
case GST_ITERATOR_OK:
|
||||
{
|
||||
GstPad *pad = g_value_get_object (&item);
|
||||
|
||||
if (gst_pad_get_direction (pad) == GST_PAD_SINK) {
|
||||
GList *tmp;
|
||||
gboolean istaken = FALSE;
|
||||
|
||||
/* 1. figure out if one of our sink ghostpads has this pad as target */
|
||||
for (tmp = operation->sinks; tmp; tmp = tmp->next) {
|
||||
GstGhostPad *gpad = (GstGhostPad *) tmp->data;
|
||||
GstPad *target = gst_ghost_pad_get_target (gpad);
|
||||
|
||||
GST_LOG ("found ghostpad with target %s:%s",
|
||||
GST_DEBUG_PAD_NAME (target));
|
||||
|
||||
if (target) {
|
||||
if (target == pad)
|
||||
istaken = TRUE;
|
||||
gst_object_unref (target);
|
||||
}
|
||||
}
|
||||
|
||||
/* 2. if not taken, return that pad */
|
||||
if (!istaken) {
|
||||
gst_object_ref (pad);
|
||||
ret = pad;
|
||||
done = TRUE;
|
||||
}
|
||||
}
|
||||
g_value_reset (&item);
|
||||
break;
|
||||
}
|
||||
case GST_ITERATOR_RESYNC:
|
||||
gst_iterator_resync (pads);
|
||||
break;
|
||||
default:
|
||||
/* ERROR and DONE */
|
||||
done = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_value_unset (&item);
|
||||
gst_iterator_free (pads);
|
||||
|
||||
if (ret)
|
||||
GST_DEBUG_OBJECT (operation, "found free sink pad %s:%s",
|
||||
GST_DEBUG_PAD_NAME (ret));
|
||||
else
|
||||
GST_DEBUG_OBJECT (operation, "Couldn't find an unused sink pad");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
GstPad *
|
||||
get_unlinked_sink_ghost_pad (GnlOperation * operation)
|
||||
{
|
||||
GstIterator *pads;
|
||||
gboolean done = FALSE;
|
||||
GValue item = { 0, };
|
||||
GstPad *ret = NULL;
|
||||
|
||||
if (!operation->element)
|
||||
return NULL;
|
||||
|
||||
pads = gst_element_iterate_sink_pads ((GstElement *) operation);
|
||||
|
||||
while (!done) {
|
||||
switch (gst_iterator_next (pads, &item)) {
|
||||
case GST_ITERATOR_OK:
|
||||
{
|
||||
GstPad *pad = g_value_get_object (&item);
|
||||
GstPad *peer = gst_pad_get_peer (pad);
|
||||
|
||||
if (peer == NULL) {
|
||||
ret = pad;
|
||||
gst_object_ref (ret);
|
||||
done = TRUE;
|
||||
} else {
|
||||
gst_object_unref (peer);
|
||||
}
|
||||
g_value_reset (&item);
|
||||
break;
|
||||
}
|
||||
case GST_ITERATOR_RESYNC:
|
||||
gst_iterator_resync (pads);
|
||||
break;
|
||||
default:
|
||||
/* ERROR and DONE */
|
||||
done = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_value_unset (&item);
|
||||
gst_iterator_free (pads);
|
||||
|
||||
if (ret)
|
||||
GST_DEBUG_OBJECT (operation, "found unlinked ghost sink pad %s:%s",
|
||||
GST_DEBUG_PAD_NAME (ret));
|
||||
else
|
||||
GST_DEBUG_OBJECT (operation, "Couldn't find an unlinked ghost sink pad");
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static GstPad *
|
||||
get_request_sink_pad (GnlOperation * operation)
|
||||
{
|
||||
GstPad *pad = NULL;
|
||||
GList *templates;
|
||||
|
||||
if (!operation->element)
|
||||
return NULL;
|
||||
|
||||
templates = gst_element_class_get_pad_template_list
|
||||
(GST_ELEMENT_GET_CLASS (operation->element));
|
||||
|
||||
for (; templates; templates = templates->next) {
|
||||
GstPadTemplate *templ = (GstPadTemplate *) templates->data;
|
||||
|
||||
GST_LOG_OBJECT (operation->element, "Trying template %s",
|
||||
GST_PAD_TEMPLATE_NAME_TEMPLATE (templ));
|
||||
|
||||
if ((GST_PAD_TEMPLATE_DIRECTION (templ) == GST_PAD_SINK) &&
|
||||
(GST_PAD_TEMPLATE_PRESENCE (templ) == GST_PAD_REQUEST)) {
|
||||
pad =
|
||||
gst_element_get_request_pad (operation->element,
|
||||
GST_PAD_TEMPLATE_NAME_TEMPLATE (templ));
|
||||
if (pad)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return pad;
|
||||
}
|
||||
|
||||
static GstPad *
|
||||
add_sink_pad (GnlOperation * operation)
|
||||
{
|
||||
GstPad *gpad = NULL;
|
||||
GstPad *ret = NULL;
|
||||
|
||||
if (!operation->element)
|
||||
return NULL;
|
||||
|
||||
/* FIXME : implement */
|
||||
GST_LOG_OBJECT (operation, "element:%s , dynamicsinks:%d",
|
||||
GST_ELEMENT_NAME (operation->element), operation->dynamicsinks);
|
||||
|
||||
|
||||
if (!operation->dynamicsinks) {
|
||||
/* static sink pads */
|
||||
ret = get_unused_static_sink_pad (operation);
|
||||
if (ret) {
|
||||
gpad = gnl_object_ghost_pad ((GnlObject *) operation, GST_PAD_NAME (ret),
|
||||
ret);
|
||||
gst_object_unref (ret);
|
||||
}
|
||||
}
|
||||
|
||||
if (!gpad) {
|
||||
/* request sink pads */
|
||||
ret = get_request_sink_pad (operation);
|
||||
if (ret) {
|
||||
gpad = gnl_object_ghost_pad ((GnlObject *) operation, GST_PAD_NAME (ret),
|
||||
ret);
|
||||
gst_object_unref (ret);
|
||||
}
|
||||
}
|
||||
|
||||
if (gpad) {
|
||||
operation->sinks = g_list_append (operation->sinks, gpad);
|
||||
operation->realsinks++;
|
||||
GST_DEBUG ("Created new pad %s:%s ghosting %s:%s",
|
||||
GST_DEBUG_PAD_NAME (gpad), GST_DEBUG_PAD_NAME (ret));
|
||||
} else {
|
||||
GST_WARNING ("Couldn't find a usable sink pad!");
|
||||
}
|
||||
|
||||
return gpad;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
remove_sink_pad (GnlOperation * operation, GstPad * sinkpad)
|
||||
{
|
||||
gboolean ret = TRUE;
|
||||
|
||||
GST_DEBUG ("sinkpad %s:%s", GST_DEBUG_PAD_NAME (sinkpad));
|
||||
|
||||
/*
|
||||
We can't remove any random pad.
|
||||
We should remove an unused pad ... which is hard to figure out in a
|
||||
thread-safe way.
|
||||
*/
|
||||
|
||||
if ((sinkpad == NULL) && operation->dynamicsinks) {
|
||||
/* Find an unlinked sinkpad */
|
||||
if ((sinkpad = get_unlinked_sink_ghost_pad (operation)) == NULL) {
|
||||
ret = FALSE;
|
||||
goto beach;
|
||||
}
|
||||
}
|
||||
|
||||
if (sinkpad) {
|
||||
GstPad *target = gst_ghost_pad_get_target ((GstGhostPad *) sinkpad);
|
||||
|
||||
if (target) {
|
||||
/* release the target pad */
|
||||
gnl_object_ghost_pad_set_target ((GnlObject *) operation, sinkpad, NULL);
|
||||
if (operation->dynamicsinks)
|
||||
gst_element_release_request_pad (operation->element, target);
|
||||
gst_object_unref (target);
|
||||
}
|
||||
operation->sinks = g_list_remove (operation->sinks, sinkpad);
|
||||
gnl_object_remove_ghost_pad ((GnlObject *) operation, sinkpad);
|
||||
operation->realsinks--;
|
||||
}
|
||||
|
||||
beach:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
synchronize_sinks (GnlOperation * operation)
|
||||
{
|
||||
|
||||
GST_DEBUG_OBJECT (operation, "num_sinks:%d , realsinks:%d, dynamicsinks:%d",
|
||||
operation->num_sinks, operation->realsinks, operation->dynamicsinks);
|
||||
|
||||
if (operation->num_sinks == operation->realsinks)
|
||||
return;
|
||||
|
||||
if (operation->num_sinks > operation->realsinks) {
|
||||
while (operation->num_sinks > operation->realsinks) /* Add pad */
|
||||
if (!(add_sink_pad (operation))) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* Remove pad */
|
||||
/* FIXME, which one do we remove ? :) */
|
||||
while (operation->num_sinks < operation->realsinks)
|
||||
if (!remove_sink_pad (operation, NULL))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gnl_operation_prepare (GnlObject * object)
|
||||
{
|
||||
/* Prepare the pads */
|
||||
synchronize_sinks ((GnlOperation *) object);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gnl_operation_cleanup (GnlObject * object)
|
||||
{
|
||||
GnlOperation *oper = (GnlOperation *) object;
|
||||
|
||||
if (oper->dynamicsinks) {
|
||||
GST_DEBUG ("Resetting dynamic sinks");
|
||||
gnl_operation_set_sinks (oper, 0);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static GstPad *
|
||||
gnl_operation_request_new_pad (GstElement * element, GstPadTemplate * templ,
|
||||
const gchar * name, const GstCaps * caps)
|
||||
{
|
||||
GnlOperation *operation = (GnlOperation *) element;
|
||||
GstPad *ret;
|
||||
|
||||
GST_DEBUG ("template:%s name:%s", templ->name_template, name);
|
||||
|
||||
if (operation->num_sinks == operation->realsinks) {
|
||||
GST_WARNING_OBJECT (element,
|
||||
"We already have the maximum number of pads : %d",
|
||||
operation->num_sinks);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = add_sink_pad ((GnlOperation *) element);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
gnl_operation_release_pad (GstElement * element, GstPad * pad)
|
||||
{
|
||||
GST_DEBUG ("pad %s:%s", GST_DEBUG_PAD_NAME (pad));
|
||||
|
||||
remove_sink_pad ((GnlOperation *) element, pad);
|
||||
}
|
||||
|
||||
void
|
||||
gnl_operation_signal_input_priority_changed (GnlOperation * operation,
|
||||
GstPad * pad, guint32 priority)
|
||||
{
|
||||
GST_DEBUG_OBJECT (operation, "pad:%s:%s, priority:%d",
|
||||
GST_DEBUG_PAD_NAME (pad), priority);
|
||||
g_signal_emit (operation, gnl_operation_signals[INPUT_PRIORITY_CHANGED],
|
||||
0, pad, priority);
|
||||
}
|
||||
|
||||
void
|
||||
gnl_operation_update_base_time (GnlOperation * operation,
|
||||
GstClockTime timestamp)
|
||||
{
|
||||
if (!gnl_object_to_media_time (GNL_OBJECT (operation),
|
||||
timestamp, &operation->next_base_time)) {
|
||||
GST_WARNING_OBJECT (operation, "Trying to set a basetime outside of "
|
||||
"ourself");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
GST_INFO_OBJECT (operation, "Setting next_basetime to %"
|
||||
GST_TIME_FORMAT, GST_TIME_ARGS (operation->next_base_time));
|
||||
}
|
90
gnl/gnloperation.h
Normal file
90
gnl/gnloperation.h
Normal file
|
@ -0,0 +1,90 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2001 Wim Taymans <wim.taymans@chello.be>
|
||||
* 2004 Edward Hervey <bilboed@bilboed.com>
|
||||
*
|
||||
* gnloperation.h: Header for base GnlOperation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GNL_OPERATION_H__
|
||||
#define __GNL_OPERATION_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gnlobject.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
#define GNL_TYPE_OPERATION \
|
||||
(gnl_operation_get_type())
|
||||
#define GNL_OPERATION(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GNL_TYPE_OPERATION,GnlOperation))
|
||||
#define GNL_OPERATION_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass),GNL_TYPE_OPERATION,GnlOperationClass))
|
||||
#define GNL_IS_OPERATION(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GNL_TYPE_OPERATION))
|
||||
#define GNL_IS_OPERATION_CLASS(obj) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GNL_TYPE_OPERATION))
|
||||
struct _GnlOperation
|
||||
{
|
||||
GnlObject parent;
|
||||
|
||||
/* <private> */
|
||||
|
||||
/* num_sinks:
|
||||
* Number of sink inputs of the controlled element.
|
||||
* -1 if the sink pads are dynamic */
|
||||
gint num_sinks;
|
||||
|
||||
/* TRUE if element has request pads */
|
||||
gboolean dynamicsinks;
|
||||
|
||||
/* realsinks:
|
||||
* Number of sink pads currently used on the contolled element. */
|
||||
gint realsinks;
|
||||
|
||||
/* 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;
|
||||
};
|
||||
|
||||
struct _GnlOperationClass
|
||||
{
|
||||
GnlObjectClass parent_class;
|
||||
|
||||
void (*input_priority_changed) (GnlOperation * operation, GstPad *pad, guint32 priority);
|
||||
};
|
||||
|
||||
GstPad * get_unlinked_sink_ghost_pad (GnlOperation * operation);
|
||||
|
||||
void
|
||||
gnl_operation_signal_input_priority_changed(GnlOperation * operation, GstPad *pad,
|
||||
guint32 priority);
|
||||
|
||||
void gnl_operation_update_base_time (GnlOperation *operation,
|
||||
GstClockTime timestamp);
|
||||
|
||||
|
||||
/* normal GOperation stuff */
|
||||
GType gnl_operation_get_type (void);
|
||||
|
||||
G_END_DECLS
|
||||
#endif /* __GNL_OPERATION_H__ */
|
590
gnl/gnlsource.c
Normal file
590
gnl/gnlsource.c
Normal file
|
@ -0,0 +1,590 @@
|
|||
/* Gnonlin
|
||||
* Copyright (C) <2001> Wim Taymans <wim.taymans@gmail.com>
|
||||
* <2004-2008> Edward Hervey <bilboed@bilboed.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 "gnl.h"
|
||||
|
||||
/**
|
||||
* SECTION:element-gnlsource
|
||||
*
|
||||
* The GnlSource encapsulates a pipeline which produces data for processing
|
||||
* in a #GnlComposition.
|
||||
*/
|
||||
|
||||
static GstStaticPadTemplate gnl_source_src_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("src",
|
||||
GST_PAD_SRC,
|
||||
GST_PAD_SOMETIMES,
|
||||
GST_STATIC_CAPS_ANY);
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (gnlsource);
|
||||
#define GST_CAT_DEFAULT gnlsource
|
||||
|
||||
#define _do_init \
|
||||
GST_DEBUG_CATEGORY_INIT (gnlsource, "gnlsource", GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin Source Element");
|
||||
#define gnl_source_parent_class parent_class
|
||||
G_DEFINE_TYPE_WITH_CODE (GnlSource, gnl_source, GNL_TYPE_OBJECT, _do_init);
|
||||
|
||||
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 */
|
||||
gulong padaddedid; /* signal handler for element pad-added signal */
|
||||
gulong probeid; /* source pad probe id */
|
||||
|
||||
gboolean pendingblock; /* We have a pending pad_block */
|
||||
gboolean areblocked; /* We already got blocked */
|
||||
GstPad *ghostedpad; /* Pad (to be) ghosted */
|
||||
GstPad *staticpad; /* The only pad. We keep an extra ref */
|
||||
};
|
||||
|
||||
static gboolean gnl_source_prepare (GnlObject * object);
|
||||
static gboolean gnl_source_cleanup (GnlObject * object);
|
||||
|
||||
static gboolean gnl_source_add_element (GstBin * bin, GstElement * element);
|
||||
|
||||
static gboolean gnl_source_remove_element (GstBin * bin, GstElement * element);
|
||||
|
||||
static void gnl_source_dispose (GObject * object);
|
||||
|
||||
static gboolean gnl_source_send_event (GstElement * element, GstEvent * event);
|
||||
|
||||
static GstPadProbeReturn
|
||||
pad_blocked_cb (GstPad * pad, GstPadProbeInfo * info, GnlSource * source);
|
||||
|
||||
static gboolean
|
||||
gnl_source_control_element_func (GnlSource * source, GstElement * element);
|
||||
|
||||
static void
|
||||
gnl_source_class_init (GnlSourceClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
GstElementClass *gstelement_class;
|
||||
GstBinClass *gstbin_class;
|
||||
GnlObjectClass *gnlobject_class;
|
||||
|
||||
gobject_class = (GObjectClass *) klass;
|
||||
gstelement_class = (GstElementClass *) klass;
|
||||
gstbin_class = (GstBinClass *) klass;
|
||||
gnlobject_class = (GnlObjectClass *) klass;
|
||||
|
||||
g_type_class_add_private (klass, sizeof (GnlSourcePrivate));
|
||||
|
||||
gst_element_class_set_static_metadata (gstelement_class, "GNonLin Source",
|
||||
"Filter/Editor",
|
||||
"Manages source elements",
|
||||
"Wim Taymans <wim.taymans@gmail.com>, Edward Hervey <bilboed@bilboed.com>");
|
||||
|
||||
parent_class = g_type_class_ref (GNL_TYPE_OBJECT);
|
||||
|
||||
klass->controls_one = TRUE;
|
||||
klass->control_element = GST_DEBUG_FUNCPTR (gnl_source_control_element_func);
|
||||
|
||||
gnlobject_class->prepare = GST_DEBUG_FUNCPTR (gnl_source_prepare);
|
||||
gnlobject_class->cleanup = GST_DEBUG_FUNCPTR (gnl_source_cleanup);
|
||||
|
||||
gstbin_class->add_element = GST_DEBUG_FUNCPTR (gnl_source_add_element);
|
||||
gstbin_class->remove_element = GST_DEBUG_FUNCPTR (gnl_source_remove_element);
|
||||
|
||||
gstelement_class->send_event = GST_DEBUG_FUNCPTR (gnl_source_send_event);
|
||||
|
||||
gobject_class->dispose = GST_DEBUG_FUNCPTR (gnl_source_dispose);
|
||||
|
||||
gst_element_class_add_pad_template (gstelement_class,
|
||||
gst_static_pad_template_get (&gnl_source_src_template));
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gnl_source_init (GnlSource * source)
|
||||
{
|
||||
GST_OBJECT_FLAG_SET (source, GNL_OBJECT_SOURCE);
|
||||
source->element = NULL;
|
||||
source->priv =
|
||||
G_TYPE_INSTANCE_GET_PRIVATE (source, GNL_TYPE_SOURCE, GnlSourcePrivate);
|
||||
|
||||
GST_DEBUG_OBJECT (source, "Setting GstBin async-handling to TRUE");
|
||||
g_object_set (G_OBJECT (source), "async-handling", TRUE, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gnl_source_dispose (GObject * object)
|
||||
{
|
||||
GnlSource *source = (GnlSource *) object;
|
||||
GnlSourcePrivate *priv = source->priv;
|
||||
|
||||
GST_DEBUG_OBJECT (object, "dispose");
|
||||
|
||||
if (priv->dispose_has_run)
|
||||
return;
|
||||
|
||||
if (source->element) {
|
||||
gst_object_unref (source->element);
|
||||
source->element = NULL;
|
||||
}
|
||||
|
||||
priv->dispose_has_run = TRUE;
|
||||
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->staticpad) {
|
||||
gst_object_unref (priv->staticpad);
|
||||
priv->staticpad = NULL;
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
element_pad_added_cb (GstElement * element G_GNUC_UNUSED, GstPad * pad,
|
||||
GnlSource * source)
|
||||
{
|
||||
GstCaps *srccaps;
|
||||
GnlSourcePrivate *priv = source->priv;
|
||||
|
||||
GST_DEBUG_OBJECT (source, "pad %s:%s", GST_DEBUG_PAD_NAME (pad));
|
||||
|
||||
if (priv->ghostpad || 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);
|
||||
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)) {
|
||||
gst_caps_unref (srccaps);
|
||||
GST_DEBUG_OBJECT (source, "Pad doesn't have valid caps, ignoring");
|
||||
return;
|
||||
}
|
||||
gst_caps_unref (srccaps);
|
||||
|
||||
GST_DEBUG_OBJECT (pad, "valid pad, about to add event probe and pad block");
|
||||
|
||||
priv->probeid = gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
|
||||
(GstPadProbeCallback) pad_blocked_cb, source, NULL);
|
||||
if (priv->probeid == 0)
|
||||
GST_WARNING_OBJECT (source, "Couldn't set Async pad blocking");
|
||||
else {
|
||||
priv->ghostedpad = pad;
|
||||
priv->pendingblock = TRUE;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (source, "Done handling pad %s:%s",
|
||||
GST_DEBUG_PAD_NAME (pad));
|
||||
}
|
||||
|
||||
static void
|
||||
element_pad_removed_cb (GstElement * element G_GNUC_UNUSED, GstPad * pad,
|
||||
GnlSource * source)
|
||||
{
|
||||
GnlSourcePrivate *priv = source->priv;
|
||||
|
||||
GST_DEBUG_OBJECT (source, "pad %s:%s (controlled pad %s:%s)",
|
||||
GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (priv->ghostedpad));
|
||||
|
||||
if (pad == priv->ghostedpad) {
|
||||
GST_DEBUG_OBJECT (source,
|
||||
"The removed pad is the controlled pad, clearing up");
|
||||
|
||||
if (priv->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->pendingblock = FALSE;
|
||||
priv->ghostedpad = NULL;
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (source, "The removed pad is NOT our controlled pad");
|
||||
}
|
||||
}
|
||||
|
||||
static gint
|
||||
compare_src_pad (GValue * item, GstCaps * caps)
|
||||
{
|
||||
gint ret = 1;
|
||||
GstPad *pad = g_value_get_object (item);
|
||||
GstCaps *padcaps;
|
||||
|
||||
GST_DEBUG_OBJECT (pad, "Trying pad for caps %" GST_PTR_FORMAT, caps);
|
||||
|
||||
/* FIXME: can pass the filter caps right away.. */
|
||||
padcaps = gst_pad_query_caps (pad, NULL);
|
||||
|
||||
if (gst_caps_can_intersect (padcaps, caps))
|
||||
ret = 0;
|
||||
|
||||
gst_caps_unref (padcaps);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
get_valid_src_pad
|
||||
|
||||
Returns True if there's a src pad compatible with the GnlObject caps in the
|
||||
given element. Fills in pad if so. The returned pad has an incremented refcount
|
||||
*/
|
||||
|
||||
static gboolean
|
||||
get_valid_src_pad (GnlSource * source, GstElement * element, GstPad ** pad)
|
||||
{
|
||||
gboolean res = FALSE;
|
||||
GstIterator *srcpads;
|
||||
GValue item = { 0, };
|
||||
|
||||
g_return_val_if_fail (pad, FALSE);
|
||||
|
||||
srcpads = gst_element_iterate_src_pads (element);
|
||||
if (gst_iterator_find_custom (srcpads, (GCompareFunc) compare_src_pad, &item,
|
||||
GNL_OBJECT (source)->caps)) {
|
||||
*pad = g_value_get_object (&item);
|
||||
gst_object_ref (*pad);
|
||||
g_value_reset (&item);
|
||||
res = TRUE;
|
||||
}
|
||||
gst_iterator_free (srcpads);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
ghost_seek_pad (GnlSource * source)
|
||||
{
|
||||
GnlSourcePrivate *priv = source->priv;
|
||||
GstPad *pad = priv->ghostedpad;
|
||||
|
||||
if (priv->ghostpad || !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);
|
||||
GST_DEBUG_OBJECT (source, "emitting no more pads");
|
||||
gst_pad_set_active (priv->ghostpad, TRUE);
|
||||
|
||||
if (priv->event) {
|
||||
GST_DEBUG_OBJECT (source, "sending queued seek event");
|
||||
if (!(gst_pad_send_event (priv->ghostpad, priv->event)))
|
||||
GST_ELEMENT_ERROR (source, RESOURCE, SEEK,
|
||||
(NULL), ("Sending initial seek to upstream element failed"));
|
||||
else
|
||||
GST_DEBUG_OBJECT (source, "queued seek sent");
|
||||
priv->event = NULL;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (source, "about to unblock %s:%s", GST_DEBUG_PAD_NAME (pad));
|
||||
priv->areblocked = FALSE;
|
||||
if (priv->probeid) {
|
||||
gst_pad_remove_probe (pad, priv->probeid);
|
||||
priv->probeid = 0;
|
||||
}
|
||||
gst_element_no_more_pads (GST_ELEMENT (source));
|
||||
|
||||
priv->pendingblock = FALSE;
|
||||
|
||||
beach:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static GstPadProbeReturn
|
||||
pad_blocked_cb (GstPad * pad, GstPadProbeInfo * info, GnlSource * source)
|
||||
{
|
||||
GST_DEBUG_OBJECT (pad, "probe callback");
|
||||
|
||||
if (!source->priv->ghostpad && !source->priv->areblocked) {
|
||||
GThread *lthread;
|
||||
|
||||
source->priv->areblocked = TRUE;
|
||||
GST_DEBUG_OBJECT (pad, "starting thread to call ghost_seek_pad");
|
||||
lthread =
|
||||
g_thread_new ("gnlsourceseek", (GThreadFunc) ghost_seek_pad, source);
|
||||
g_thread_unref (lthread);
|
||||
}
|
||||
|
||||
return GST_PAD_PROBE_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* has_dynamic_pads
|
||||
* Returns TRUE if the element has only dynamic pads.
|
||||
*/
|
||||
|
||||
static gboolean
|
||||
has_dynamic_srcpads (GstElement * element)
|
||||
{
|
||||
gboolean ret = TRUE;
|
||||
GList *templates;
|
||||
GstPadTemplate *template;
|
||||
|
||||
templates =
|
||||
gst_element_class_get_pad_template_list (GST_ELEMENT_GET_CLASS (element));
|
||||
|
||||
while (templates) {
|
||||
template = (GstPadTemplate *) templates->data;
|
||||
|
||||
if ((GST_PAD_TEMPLATE_DIRECTION (template) == GST_PAD_SRC)
|
||||
&& (GST_PAD_TEMPLATE_PRESENCE (template) == GST_PAD_ALWAYS)) {
|
||||
ret = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
templates = templates->next;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gnl_source_control_element_func (GnlSource * source, GstElement * element)
|
||||
{
|
||||
GnlSourcePrivate *priv = source->priv;
|
||||
GstPad *pad = NULL;
|
||||
|
||||
g_return_val_if_fail (source->element == NULL, FALSE);
|
||||
|
||||
GST_DEBUG_OBJECT (source, "element:%s, source->element:%p",
|
||||
GST_ELEMENT_NAME (element), source->element);
|
||||
|
||||
source->element = element;
|
||||
gst_object_ref (element);
|
||||
|
||||
if (get_valid_src_pad (source, source->element, &pad)) {
|
||||
priv->staticpad = pad;
|
||||
GST_DEBUG_OBJECT (source,
|
||||
"There is a valid source pad, we consider the object as NOT having dynamic pads");
|
||||
priv->dynamicpads = FALSE;
|
||||
} else {
|
||||
priv->dynamicpads = has_dynamic_srcpads (element);
|
||||
GST_DEBUG_OBJECT (source, "No valid source pad yet, dynamicpads:%d",
|
||||
priv->dynamicpads);
|
||||
if (priv->dynamicpads) {
|
||||
/* connect to pad-added/removed signals */
|
||||
priv->padremovedid = g_signal_connect
|
||||
(G_OBJECT (element), "pad-removed",
|
||||
G_CALLBACK (element_pad_removed_cb), source);
|
||||
priv->padaddedid =
|
||||
g_signal_connect (G_OBJECT (element), "pad-added",
|
||||
G_CALLBACK (element_pad_added_cb), source);
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gnl_source_add_element (GstBin * bin, GstElement * element)
|
||||
{
|
||||
GnlSource *source = (GnlSource *) bin;
|
||||
gboolean pret;
|
||||
|
||||
GST_DEBUG_OBJECT (source, "Adding element %s", GST_ELEMENT_NAME (element));
|
||||
|
||||
if (GNL_SOURCE_GET_CLASS (source)->controls_one && source->element) {
|
||||
GST_WARNING_OBJECT (bin, "GnlSource can only handle one element at a time");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* call parent add_element */
|
||||
pret = GST_BIN_CLASS (parent_class)->add_element (bin, element);
|
||||
|
||||
if (pret && GNL_SOURCE_GET_CLASS (source)->controls_one) {
|
||||
gnl_source_control_element_func (source, element);
|
||||
}
|
||||
return pret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gnl_source_remove_element (GstBin * bin, GstElement * element)
|
||||
{
|
||||
GnlSource *source = (GnlSource *) bin;
|
||||
GnlSourcePrivate *priv = source->priv;
|
||||
gboolean pret;
|
||||
|
||||
GST_DEBUG_OBJECT (source, "Removing element %s", GST_ELEMENT_NAME (element));
|
||||
|
||||
/* try to remove it */
|
||||
pret = GST_BIN_CLASS (parent_class)->remove_element (bin, element);
|
||||
|
||||
if ((!source->element) || (source->element != element)) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (pret) {
|
||||
/* remove ghostpad */
|
||||
if (priv->ghostpad) {
|
||||
gnl_object_remove_ghost_pad ((GnlObject *) bin, priv->ghostpad);
|
||||
priv->ghostpad = NULL;
|
||||
}
|
||||
|
||||
/* discard events */
|
||||
if (priv->event) {
|
||||
gst_event_unref (priv->event);
|
||||
priv->event = NULL;
|
||||
}
|
||||
|
||||
/* remove signal handlers */
|
||||
if (priv->padremovedid) {
|
||||
g_signal_handler_disconnect (source->element, priv->padremovedid);
|
||||
priv->padremovedid = 0;
|
||||
}
|
||||
if (priv->padaddedid) {
|
||||
g_signal_handler_disconnect (source->element, priv->padaddedid);
|
||||
priv->padaddedid = 0;
|
||||
}
|
||||
|
||||
priv->dynamicpads = FALSE;
|
||||
gst_object_unref (element);
|
||||
source->element = NULL;
|
||||
}
|
||||
return pret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gnl_source_send_event (GstElement * element, GstEvent * event)
|
||||
{
|
||||
GnlSource *source = (GnlSource *) 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);
|
||||
else {
|
||||
if (source->priv->event)
|
||||
gst_event_unref (source->priv->event);
|
||||
source->priv->event = event;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
res = GST_ELEMENT_CLASS (parent_class)->send_event (element, event);
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gnl_source_prepare (GnlObject * object)
|
||||
{
|
||||
GnlSource *source = GNL_SOURCE (object);
|
||||
GnlSourcePrivate *priv = source->priv;
|
||||
GstElement *parent =
|
||||
(GstElement *) gst_element_get_parent ((GstElement *) object);
|
||||
|
||||
if (!source->element) {
|
||||
GST_WARNING_OBJECT (source,
|
||||
"GnlSource doesn't have an element to control !");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (source, "ghostpad:%p, dynamicpads:%d",
|
||||
priv->ghostpad, priv->dynamicpads);
|
||||
|
||||
if (!(priv->ghostpad) && !priv->pendingblock) {
|
||||
GstPad *pad;
|
||||
|
||||
GST_LOG_OBJECT (source, "no ghostpad and no dynamic pads");
|
||||
|
||||
/* Do an async block on valid source pad */
|
||||
|
||||
if (!priv->staticpad
|
||||
&& !(get_valid_src_pad (source, source->element, &pad))) {
|
||||
GST_DEBUG_OBJECT (source, "Couldn't find a valid source pad");
|
||||
} else {
|
||||
if (priv->staticpad)
|
||||
pad = gst_object_ref (priv->staticpad);
|
||||
GST_LOG_OBJECT (source, "Trying to async block source pad %s:%s",
|
||||
GST_DEBUG_PAD_NAME (pad));
|
||||
priv->ghostedpad = pad;
|
||||
priv->probeid = gst_pad_add_probe (pad,
|
||||
GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
|
||||
(GstPadProbeCallback) pad_blocked_cb, source, NULL);
|
||||
gst_object_unref (pad);
|
||||
}
|
||||
}
|
||||
|
||||
if (!GNL_IS_COMPOSITION (parent)) {
|
||||
/* Figure out if we're in a composition */
|
||||
if (source->priv->event)
|
||||
gst_event_unref (source->priv->event);
|
||||
|
||||
GST_DEBUG_OBJECT (object, "Creating initial seek");
|
||||
|
||||
source->priv->event = gst_event_new_seek (1.0, GST_FORMAT_TIME,
|
||||
GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH,
|
||||
GST_SEEK_TYPE_SET, object->start, GST_SEEK_TYPE_SET, object->stop);
|
||||
}
|
||||
|
||||
gst_object_unref (parent);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
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);
|
||||
|
||||
if (target) {
|
||||
if (priv->probeid) {
|
||||
gst_pad_remove_probe (target, priv->probeid);
|
||||
priv->probeid = 0;
|
||||
}
|
||||
gst_object_unref (target);
|
||||
}
|
||||
gnl_object_remove_ghost_pad ((GnlObject *) source, priv->ghostpad);
|
||||
priv->ghostpad = NULL;
|
||||
priv->ghostedpad = NULL;
|
||||
priv->areblocked = FALSE;
|
||||
priv->pendingblock = FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
68
gnl/gnlsource.h
Normal file
68
gnl/gnlsource.h
Normal file
|
@ -0,0 +1,68 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2001 Wim Taymans <wim.taymans@gmail.com>
|
||||
* 2004-2008 Edward Hervey <bilboed@bilboed.com>
|
||||
*
|
||||
* gnlsource.h: Header for base GnlSource
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GNL_SOURCE_H__
|
||||
#define __GNL_SOURCE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gnlobject.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
#define GNL_TYPE_SOURCE \
|
||||
(gnl_source_get_type())
|
||||
#define GNL_SOURCE(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GNL_TYPE_SOURCE,GnlSource))
|
||||
#define GNL_SOURCE_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass),GNL_TYPE_SOURCE,GnlSourceClass))
|
||||
#define GNL_SOURCE_GET_CLASS(obj) \
|
||||
(G_TYPE_INSTANCE_GET_CLASS ((obj), GNL_TYPE_SOURCE, GnlSourceClass))
|
||||
#define GNL_IS_SOURCE(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GNL_TYPE_SOURCE))
|
||||
#define GNL_IS_SOURCE_CLASS(obj) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GNL_TYPE_SOURCE))
|
||||
typedef struct _GnlSourcePrivate GnlSourcePrivate;
|
||||
|
||||
struct _GnlSource
|
||||
{
|
||||
GnlObject parent;
|
||||
|
||||
/* controlled source element, acces with gst_bin_[add|remove]_element */
|
||||
GstElement *element;
|
||||
|
||||
GnlSourcePrivate *priv;
|
||||
};
|
||||
|
||||
struct _GnlSourceClass
|
||||
{
|
||||
GnlObjectClass parent_class;
|
||||
|
||||
/* controls_one is TRUE if the class only controls one element */
|
||||
gboolean controls_one;
|
||||
/* control_element() takes care of controlling the given element */
|
||||
gboolean (*control_element) (GnlSource * source, GstElement * element);
|
||||
};
|
||||
|
||||
GType gnl_source_get_type (void);
|
||||
|
||||
G_END_DECLS
|
||||
#endif /* __GNL_SOURCE_H__ */
|
42
gnl/gnltypes.h
Normal file
42
gnl/gnltypes.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2004 Edward Hervey <bilboed@bilboed.com>
|
||||
*
|
||||
* gnltypes.h: Header for class definition
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef __GNL_TYPES_H__
|
||||
#define __GNL_TYPES_H__
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
typedef struct _GnlObject GnlObject;
|
||||
typedef struct _GnlObjectClass GnlObjectClass;
|
||||
|
||||
typedef struct _GnlComposition GnlComposition;
|
||||
typedef struct _GnlCompositionClass GnlCompositionClass;
|
||||
|
||||
typedef struct _GnlOperation GnlOperation;
|
||||
typedef struct _GnlOperationClass GnlOperationClass;
|
||||
|
||||
typedef struct _GnlSource GnlSource;
|
||||
typedef struct _GnlSourceClass GnlSourceClass;
|
||||
|
||||
typedef struct _GnlURISource GnlURISource;
|
||||
typedef struct _GnlURISourceClass GnlURISourceClass;
|
||||
|
||||
#endif
|
166
gnl/gnlurisource.c
Normal file
166
gnl/gnlurisource.c
Normal file
|
@ -0,0 +1,166 @@
|
|||
/* Gnonlin
|
||||
* Copyright (C) <2005-2008> Edward Hervey <bilboed@bilboed.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 "gnl.h"
|
||||
#include "gnlurisource.h"
|
||||
|
||||
/**
|
||||
* SECTION:element-gnlurisource
|
||||
*
|
||||
* GnlURISource is a #GnlSource which reads and decodes the contents
|
||||
* of a given file. The data in the file is decoded using any available
|
||||
* GStreamer plugins.
|
||||
*/
|
||||
|
||||
static GstStaticPadTemplate gnl_urisource_src_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("src",
|
||||
GST_PAD_SRC,
|
||||
GST_PAD_SOMETIMES,
|
||||
GST_STATIC_CAPS_ANY);
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (gnlurisource);
|
||||
#define GST_CAT_DEFAULT gnlurisource
|
||||
|
||||
#define _do_init \
|
||||
GST_DEBUG_CATEGORY_INIT (gnlurisource, "gnlurisource", GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin URI Source Element");
|
||||
#define gnl_urisource_parent_class parent_class
|
||||
G_DEFINE_TYPE_WITH_CODE (GnlURISource, gnl_urisource, GNL_TYPE_SOURCE,
|
||||
_do_init);
|
||||
|
||||
enum
|
||||
{
|
||||
ARG_0,
|
||||
ARG_URI,
|
||||
};
|
||||
|
||||
static gboolean gnl_urisource_prepare (GnlObject * object);
|
||||
|
||||
static void
|
||||
gnl_urisource_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec);
|
||||
|
||||
static void
|
||||
gnl_urisource_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec);
|
||||
|
||||
static void
|
||||
gnl_urisource_class_init (GnlURISourceClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
GnlObjectClass *gnlobject_class;
|
||||
GstElementClass *gstelement_class;
|
||||
|
||||
gobject_class = (GObjectClass *) klass;
|
||||
gstelement_class = (GstElementClass *) klass;
|
||||
gnlobject_class = (GnlObjectClass *) klass;
|
||||
parent_class = g_type_class_ref (GNL_TYPE_SOURCE);
|
||||
|
||||
gst_element_class_set_static_metadata (gstelement_class, "GNonLin URI Source",
|
||||
"Filter/Editor",
|
||||
"High-level URI Source element", "Edward Hervey <bilboed@bilboed.com>");
|
||||
|
||||
gobject_class->set_property = GST_DEBUG_FUNCPTR (gnl_urisource_set_property);
|
||||
gobject_class->get_property = GST_DEBUG_FUNCPTR (gnl_urisource_get_property);
|
||||
|
||||
g_object_class_install_property (gobject_class, ARG_URI,
|
||||
g_param_spec_string ("uri", "Uri",
|
||||
"Uri of the file to use", NULL, G_PARAM_READWRITE));
|
||||
|
||||
gst_element_class_add_pad_template (gstelement_class,
|
||||
gst_static_pad_template_get (&gnl_urisource_src_template));
|
||||
|
||||
gnlobject_class->prepare = gnl_urisource_prepare;
|
||||
}
|
||||
|
||||
static void
|
||||
gnl_urisource_init (GnlURISource * urisource)
|
||||
{
|
||||
GstElement *decodebin = NULL;
|
||||
|
||||
GST_OBJECT_FLAG_SET (urisource, GNL_OBJECT_SOURCE);
|
||||
|
||||
/* We create a bin with source and decodebin within */
|
||||
decodebin =
|
||||
gst_element_factory_make ("uridecodebin", "internal-uridecodebin");
|
||||
g_object_set (decodebin, "expose-all-streams", FALSE, NULL);
|
||||
|
||||
gst_bin_add (GST_BIN (urisource), decodebin);
|
||||
}
|
||||
|
||||
static inline void
|
||||
gnl_urisource_set_uri (GnlURISource * fs, const gchar * uri)
|
||||
{
|
||||
g_object_set (GNL_SOURCE (fs)->element, "uri", uri, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gnl_urisource_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GnlURISource *fs = (GnlURISource *) object;
|
||||
|
||||
switch (prop_id) {
|
||||
case ARG_URI:
|
||||
gnl_urisource_set_uri (fs, g_value_get_string (value));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gnl_urisource_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GnlURISource *fs = (GnlURISource *) object;
|
||||
|
||||
switch (prop_id) {
|
||||
case ARG_URI:
|
||||
g_object_get_property ((GObject *) GNL_SOURCE (fs)->element, "uri",
|
||||
value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gnl_urisource_prepare (GnlObject * object)
|
||||
{
|
||||
GnlSource *fs = (GnlSource *) object;
|
||||
|
||||
GST_DEBUG ("prepare");
|
||||
|
||||
/* Set the caps on uridecodebin */
|
||||
if (!gst_caps_is_any (object->caps)) {
|
||||
GST_DEBUG_OBJECT (object, "Setting uridecodebin caps to %" GST_PTR_FORMAT,
|
||||
object->caps);
|
||||
g_object_set (fs->element, "caps", object->caps, NULL);
|
||||
}
|
||||
|
||||
return GNL_OBJECT_CLASS (parent_class)->prepare (object);
|
||||
}
|
57
gnl/gnlurisource.h
Normal file
57
gnl/gnlurisource.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2001 Wim Taymans <wim.taymans@gmail.com>
|
||||
* 2004-2008 Edward Hervey <bilboed@bilboed.com>
|
||||
*
|
||||
* gnlurisource.h: Header for GnlURISource
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GNL_URI_SOURCE_H__
|
||||
#define __GNL_URI_SOURCE_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include "gnlsource.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
#define GNL_TYPE_URI_SOURCE \
|
||||
(gnl_urisource_get_type())
|
||||
#define GNL_URI_SOURCE(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GNL_TYPE_URI_SOURCE,GnlURIsource))
|
||||
#define GNL_URI_SOURCE_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass),GNL_TYPE_URI_SOURCE,GnlURIsourceClass))
|
||||
#define GNL_IS_URI_SOURCE(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GNL_TYPE_URI_SOURCE))
|
||||
#define GNL_IS_URI_SOURCE_CLASS(obj) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GNL_TYPE_URI_SOURCE))
|
||||
|
||||
struct _GnlURISource
|
||||
{
|
||||
GnlSource parent;
|
||||
|
||||
gchar *uri;
|
||||
};
|
||||
|
||||
struct _GnlURISourceClass
|
||||
{
|
||||
GnlSourceClass parent_class;
|
||||
};
|
||||
|
||||
GType gnl_urisource_get_type (void);
|
||||
|
||||
G_END_DECLS
|
||||
#endif /* __GNL_URI_SOURCE_H__ */
|
348
tests/check/gnl/common.c
Normal file
348
tests/check/gnl/common.c
Normal file
|
@ -0,0 +1,348 @@
|
|||
#include "common.h"
|
||||
|
||||
void
|
||||
poll_the_bus (GstBus * bus)
|
||||
{
|
||||
GstMessage *message;
|
||||
gboolean carry_on = TRUE;
|
||||
|
||||
while (carry_on) {
|
||||
message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
|
||||
if (message) {
|
||||
switch (GST_MESSAGE_TYPE (message)) {
|
||||
case GST_MESSAGE_EOS:
|
||||
/* we should check if we really finished here */
|
||||
GST_DEBUG ("Got an EOS");
|
||||
carry_on = FALSE;
|
||||
break;
|
||||
case GST_MESSAGE_SEGMENT_START:
|
||||
case GST_MESSAGE_SEGMENT_DONE:
|
||||
/* We shouldn't see any segement messages, since we didn't do a segment seek */
|
||||
GST_WARNING ("Saw a Segment start/stop");
|
||||
fail_if (TRUE);
|
||||
break;
|
||||
case GST_MESSAGE_ERROR:
|
||||
fail_error_message (message);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
gst_mini_object_unref (GST_MINI_OBJECT (message));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GstElement *
|
||||
gst_element_factory_make_or_warn (const gchar * factoryname, const gchar * name)
|
||||
{
|
||||
GstElement *element;
|
||||
|
||||
element = gst_element_factory_make (factoryname, name);
|
||||
fail_unless (element != NULL, "Failed to make element %s", factoryname);
|
||||
return element;
|
||||
}
|
||||
|
||||
void
|
||||
composition_pad_added_cb (GstElement * composition, GstPad * pad,
|
||||
CollectStructure * collect)
|
||||
{
|
||||
fail_if (!(gst_element_link_pads_full (composition, GST_OBJECT_NAME (pad),
|
||||
collect->sink, "sink", GST_PAD_LINK_CHECK_NOTHING)));
|
||||
}
|
||||
|
||||
/* return TRUE to discard the Segment */
|
||||
static gboolean
|
||||
compare_segments (CollectStructure * collect, Segment * segment,
|
||||
GstEvent * event)
|
||||
{
|
||||
const GstSegment *orig;
|
||||
guint64 running_stop, running_start, running_duration;
|
||||
|
||||
gst_event_parse_segment (event, &orig);
|
||||
|
||||
GST_DEBUG ("Got Segment rate:%f, format:%s, start:%" GST_TIME_FORMAT
|
||||
", stop:%" GST_TIME_FORMAT ", time:%" GST_TIME_FORMAT
|
||||
", base:%" GST_TIME_FORMAT ", offset:%" GST_TIME_FORMAT,
|
||||
orig->rate, gst_format_get_name (orig->format),
|
||||
GST_TIME_ARGS (orig->start), GST_TIME_ARGS (orig->stop),
|
||||
GST_TIME_ARGS (orig->time), GST_TIME_ARGS (orig->base),
|
||||
GST_TIME_ARGS (orig->offset));
|
||||
GST_DEBUG ("[RUNNING] start:%" GST_TIME_FORMAT " [STREAM] start:%"
|
||||
GST_TIME_FORMAT, GST_TIME_ARGS (gst_segment_to_running_time (orig,
|
||||
GST_FORMAT_TIME, orig->start)),
|
||||
GST_TIME_ARGS (gst_segment_to_stream_time (orig, GST_FORMAT_TIME,
|
||||
orig->start)));
|
||||
|
||||
GST_DEBUG ("Expecting rate:%f, format:%s, start:%" GST_TIME_FORMAT
|
||||
", stop:%" GST_TIME_FORMAT ", position:%" GST_TIME_FORMAT ", base:%"
|
||||
GST_TIME_FORMAT, segment->rate, gst_format_get_name (segment->format),
|
||||
GST_TIME_ARGS (segment->start), GST_TIME_ARGS (segment->stop),
|
||||
GST_TIME_ARGS (segment->position),
|
||||
GST_TIME_ARGS (collect->expected_base));
|
||||
|
||||
running_start =
|
||||
gst_segment_to_running_time (orig, GST_FORMAT_TIME, orig->start);
|
||||
running_stop =
|
||||
gst_segment_to_running_time (orig, GST_FORMAT_TIME, orig->stop);
|
||||
running_duration = running_stop - running_start;
|
||||
fail_if (orig->rate != segment->rate);
|
||||
fail_if (orig->format != segment->format);
|
||||
fail_unless_equals_int64 (orig->time, segment->position);
|
||||
fail_unless_equals_int64 (orig->base, collect->expected_base);
|
||||
fail_unless_equals_uint64 (orig->stop - orig->start,
|
||||
segment->stop - segment->start);
|
||||
|
||||
collect->expected_base += running_duration;
|
||||
|
||||
GST_DEBUG ("Segment was valid, discarding expected Segment");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GstPadProbeReturn
|
||||
sinkpad_event_probe (GstPad * sinkpad, GstEvent * event,
|
||||
CollectStructure * collect)
|
||||
{
|
||||
Segment *segment;
|
||||
|
||||
GST_DEBUG_OBJECT (sinkpad, "event:%p (%s seqnum:%d) , collect:%p", event,
|
||||
GST_EVENT_TYPE_NAME (event), GST_EVENT_SEQNUM (event), collect);
|
||||
|
||||
if (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT) {
|
||||
fail_if (collect->expected_segments == NULL,
|
||||
"Received unexpected segment on pad: %s:%s",
|
||||
GST_DEBUG_PAD_NAME (sinkpad));
|
||||
|
||||
if (!collect->gotsegment)
|
||||
collect->seen_segments =
|
||||
g_list_append (NULL, GINT_TO_POINTER (GST_EVENT_SEQNUM (event)));
|
||||
else {
|
||||
fail_if (g_list_find (collect->seen_segments,
|
||||
GINT_TO_POINTER (GST_EVENT_SEQNUM (event))),
|
||||
"Got a segment event we already saw before !");
|
||||
collect->seen_segments =
|
||||
g_list_append (collect->seen_segments,
|
||||
GINT_TO_POINTER (GST_EVENT_SEQNUM (event)));
|
||||
}
|
||||
|
||||
segment = (Segment *) collect->expected_segments->data;
|
||||
|
||||
if (compare_segments (collect, segment, event) &&
|
||||
collect->keep_expected_segments == FALSE) {
|
||||
collect->expected_segments =
|
||||
g_list_remove (collect->expected_segments, segment);
|
||||
g_free (segment);
|
||||
}
|
||||
|
||||
collect->gotsegment = TRUE;
|
||||
}
|
||||
|
||||
return GST_PAD_PROBE_OK;
|
||||
}
|
||||
|
||||
static GstPadProbeReturn
|
||||
sinkpad_buffer_probe (GstPad * sinkpad, GstBuffer * buffer,
|
||||
CollectStructure * collect)
|
||||
{
|
||||
GST_DEBUG_OBJECT (sinkpad, "buffer:%p (%" GST_TIME_FORMAT ") , collect:%p",
|
||||
buffer, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)), collect);
|
||||
fail_if (!collect->gotsegment,
|
||||
"Received a buffer without a preceding segment");
|
||||
return GST_PAD_PROBE_OK;
|
||||
}
|
||||
|
||||
GstPadProbeReturn
|
||||
sinkpad_probe (GstPad * sinkpad, GstPadProbeInfo * info,
|
||||
CollectStructure * collect)
|
||||
{
|
||||
if (info->type & GST_PAD_PROBE_TYPE_BUFFER)
|
||||
return sinkpad_buffer_probe (sinkpad, (GstBuffer *) info->data, collect);
|
||||
if (info->type & GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM)
|
||||
return sinkpad_event_probe (sinkpad, (GstEvent *) info->data, collect);
|
||||
return GST_PAD_PROBE_OK;
|
||||
}
|
||||
|
||||
static GstElement *
|
||||
new_gnl_src (const gchar * name, guint64 start, gint64 duration, gint priority)
|
||||
{
|
||||
GstElement *gnlsource = NULL;
|
||||
|
||||
gnlsource = gst_element_factory_make_or_warn ("gnlsource", name);
|
||||
fail_if (gnlsource == NULL);
|
||||
|
||||
g_object_set (G_OBJECT (gnlsource),
|
||||
"start", start,
|
||||
"duration", duration, "inpoint", start, "priority", priority, NULL);
|
||||
|
||||
return gnlsource;
|
||||
}
|
||||
|
||||
GstElement *
|
||||
videotest_gnl_src (const gchar * name, guint64 start, gint64 duration,
|
||||
gint pattern, guint priority)
|
||||
{
|
||||
GstElement *gnlsource = NULL;
|
||||
GstElement *videotestsrc = NULL;
|
||||
GstCaps *caps =
|
||||
gst_caps_from_string
|
||||
("video/x-raw,format=(string)I420,framerate=(fraction)3/2");
|
||||
|
||||
fail_if (caps == NULL);
|
||||
|
||||
videotestsrc = gst_element_factory_make_or_warn ("videotestsrc", NULL);
|
||||
g_object_set (G_OBJECT (videotestsrc), "pattern", pattern, NULL);
|
||||
|
||||
gnlsource = new_gnl_src (name, start, duration, priority);
|
||||
g_object_set (G_OBJECT (gnlsource), "caps", caps, NULL);
|
||||
gst_caps_unref (caps);
|
||||
|
||||
gst_bin_add (GST_BIN (gnlsource), videotestsrc);
|
||||
|
||||
return gnlsource;
|
||||
}
|
||||
|
||||
GstElement *
|
||||
videotest_gnl_src_full (const gchar * name, guint64 start, gint64 duration,
|
||||
guint64 inpoint, gint pattern, guint priority)
|
||||
{
|
||||
GstElement *gnls;
|
||||
|
||||
gnls = videotest_gnl_src (name, start, duration, pattern, priority);
|
||||
if (gnls) {
|
||||
g_object_set (G_OBJECT (gnls), "inpoint", inpoint, NULL);
|
||||
}
|
||||
|
||||
|
||||
return gnls;
|
||||
}
|
||||
|
||||
GstElement *
|
||||
videotest_in_bin_gnl_src (const gchar * name, guint64 start, gint64 duration,
|
||||
gint pattern, guint priority)
|
||||
{
|
||||
GstElement *gnlsource = NULL;
|
||||
GstElement *videotestsrc = NULL;
|
||||
GstElement *bin = NULL;
|
||||
GstElement *alpha = NULL;
|
||||
GstPad *srcpad = NULL;
|
||||
|
||||
alpha = gst_element_factory_make ("alpha", NULL);
|
||||
if (alpha == NULL)
|
||||
return NULL;
|
||||
|
||||
videotestsrc = gst_element_factory_make_or_warn ("videotestsrc", NULL);
|
||||
g_object_set (G_OBJECT (videotestsrc), "pattern", pattern, NULL);
|
||||
bin = gst_bin_new (NULL);
|
||||
|
||||
gnlsource = new_gnl_src (name, start, duration, priority);
|
||||
|
||||
gst_bin_add (GST_BIN (bin), videotestsrc);
|
||||
gst_bin_add (GST_BIN (bin), alpha);
|
||||
|
||||
gst_element_link_pads_full (videotestsrc, "src", alpha, "sink",
|
||||
GST_PAD_LINK_CHECK_NOTHING);
|
||||
|
||||
gst_bin_add (GST_BIN (gnlsource), bin);
|
||||
|
||||
srcpad = gst_element_get_static_pad (alpha, "src");
|
||||
|
||||
gst_element_add_pad (bin, gst_ghost_pad_new ("src", srcpad));
|
||||
|
||||
gst_object_unref (srcpad);
|
||||
|
||||
return gnlsource;
|
||||
}
|
||||
|
||||
GstElement *
|
||||
audiotest_bin_src (const gchar * name, guint64 start,
|
||||
gint64 duration, guint priority, gboolean intaudio)
|
||||
{
|
||||
GstElement *source = NULL;
|
||||
GstElement *identity = NULL;
|
||||
GstElement *audiotestsrc = NULL;
|
||||
GstElement *audioconvert = NULL;
|
||||
GstElement *bin = NULL;
|
||||
GstCaps *caps;
|
||||
GstPad *srcpad = NULL;
|
||||
|
||||
audiotestsrc = gst_element_factory_make_or_warn ("audiotestsrc", NULL);
|
||||
identity = gst_element_factory_make_or_warn ("identity", NULL);
|
||||
bin = gst_bin_new (NULL);
|
||||
source = new_gnl_src (name, start, duration, priority);
|
||||
audioconvert = gst_element_factory_make_or_warn ("audioconvert", NULL);
|
||||
|
||||
if (intaudio)
|
||||
caps = gst_caps_from_string ("audio/x-raw,format=(string)S16LE");
|
||||
else
|
||||
caps = gst_caps_from_string ("audio/x-raw,format=(string)F32LE");
|
||||
|
||||
gst_bin_add_many (GST_BIN (bin), audiotestsrc, audioconvert, identity, NULL);
|
||||
gst_element_link_pads_full (audiotestsrc, "src", audioconvert, "sink",
|
||||
GST_PAD_LINK_CHECK_NOTHING);
|
||||
fail_if ((gst_element_link_filtered (audioconvert, identity, caps)) != TRUE);
|
||||
|
||||
gst_caps_unref (caps);
|
||||
|
||||
gst_bin_add (GST_BIN (source), bin);
|
||||
|
||||
srcpad = gst_element_get_static_pad (identity, "src");
|
||||
|
||||
gst_element_add_pad (bin, gst_ghost_pad_new ("src", srcpad));
|
||||
|
||||
gst_object_unref (srcpad);
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
GstElement *
|
||||
new_operation (const gchar * name, const gchar * factory, guint64 start,
|
||||
gint64 duration, guint priority)
|
||||
{
|
||||
GstElement *gnloperation = NULL;
|
||||
GstElement *operation = NULL;
|
||||
|
||||
operation = gst_element_factory_make_or_warn (factory, NULL);
|
||||
gnloperation = gst_element_factory_make_or_warn ("gnloperation", name);
|
||||
|
||||
g_object_set (G_OBJECT (gnloperation),
|
||||
"start", start, "duration", duration, "priority", priority, NULL);
|
||||
|
||||
gst_bin_add (GST_BIN (gnloperation), operation);
|
||||
|
||||
return gnloperation;
|
||||
}
|
||||
|
||||
|
||||
Segment *
|
||||
segment_new (gdouble rate, GstFormat format, gint64 start, gint64 stop,
|
||||
gint64 position)
|
||||
{
|
||||
Segment *segment;
|
||||
|
||||
segment = g_new0 (Segment, 1);
|
||||
|
||||
segment->rate = rate;
|
||||
segment->format = format;
|
||||
segment->start = start;
|
||||
segment->stop = stop;
|
||||
segment->position = position;
|
||||
|
||||
return segment;
|
||||
}
|
||||
|
||||
GList *
|
||||
copy_segment_list (GList * list)
|
||||
{
|
||||
GList *res = NULL;
|
||||
|
||||
while (list) {
|
||||
Segment *pdata = (Segment *) list->data;
|
||||
|
||||
res =
|
||||
g_list_append (res, segment_new (pdata->rate, pdata->format,
|
||||
pdata->start, pdata->stop, pdata->position));
|
||||
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
73
tests/check/gnl/common.h
Normal file
73
tests/check/gnl/common.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
|
||||
#include <gst/check/gstcheck.h>
|
||||
|
||||
#define fail_error_message(msg) \
|
||||
G_STMT_START { \
|
||||
GError *error; \
|
||||
gst_message_parse_error(msg, &error, NULL); \
|
||||
fail_unless(FALSE, "Error Message from %s : %s", \
|
||||
GST_OBJECT_NAME (GST_MESSAGE_SRC(msg)), error->message); \
|
||||
g_error_free (error); \
|
||||
} G_STMT_END;
|
||||
|
||||
#define check_start_stop_duration(object, startval, stopval, durval) \
|
||||
G_STMT_START { guint64 start, stop; \
|
||||
gint64 duration; \
|
||||
GST_DEBUG_OBJECT (object, "Checking for valid start/stop/duration values"); \
|
||||
g_object_get (object, "start", &start, "stop", &stop, \
|
||||
"duration", &duration, NULL); \
|
||||
fail_unless_equals_uint64(start, startval); \
|
||||
fail_unless_equals_uint64(stop, stopval); \
|
||||
fail_unless_equals_int64(duration, durval); \
|
||||
GST_DEBUG_OBJECT (object, "start/stop/duration values valid"); \
|
||||
} G_STMT_END;
|
||||
|
||||
#define check_state_simple(object, expected_state) \
|
||||
G_STMT_START { \
|
||||
GstStateChangeReturn ret; \
|
||||
GstState state, pending; \
|
||||
ret = gst_element_get_state(GST_ELEMENT_CAST(object), &state, &pending, 5 * GST_SECOND); \
|
||||
fail_if (ret == GST_STATE_CHANGE_FAILURE); \
|
||||
fail_unless (state == expected_state, "Element state (%s) is not the expected one (%s)", \
|
||||
gst_element_state_get_name(state), gst_element_state_get_name(expected_state)); \
|
||||
} G_STMT_END;
|
||||
|
||||
typedef struct _Segment {
|
||||
gdouble rate;
|
||||
GstFormat format;
|
||||
guint64 start, stop, position;
|
||||
} Segment;
|
||||
|
||||
typedef struct _CollectStructure {
|
||||
GstElement *comp;
|
||||
GstElement *sink;
|
||||
guint64 last_time;
|
||||
gboolean gotsegment;
|
||||
GList *seen_segments;
|
||||
GList *expected_segments;
|
||||
guint64 expected_base;
|
||||
|
||||
gboolean keep_expected_segments;
|
||||
} CollectStructure;
|
||||
|
||||
void poll_the_bus(GstBus *bus);
|
||||
void composition_pad_added_cb (GstElement *composition, GstPad *pad, CollectStructure * collect);
|
||||
GstPadProbeReturn sinkpad_probe (GstPad *sinkpad, GstPadProbeInfo * info, CollectStructure * collect);
|
||||
GstElement *videotest_gnl_src (const gchar * name, guint64 start, gint64 duration,
|
||||
gint pattern, guint priority);
|
||||
GstElement * videotest_gnl_src_full (const gchar * name, guint64 start, gint64 duration,
|
||||
guint64 inpoint,
|
||||
gint pattern, guint priority);
|
||||
GstElement *
|
||||
videotest_in_bin_gnl_src (const gchar * name, guint64 start, gint64 duration, gint pattern, guint priority);
|
||||
GstElement *
|
||||
audiotest_bin_src (const gchar * name, guint64 start,
|
||||
gint64 duration, guint priority, gboolean intaudio);
|
||||
GstElement *
|
||||
new_operation (const gchar * name, const gchar * factory, guint64 start, gint64 duration, guint priority);
|
||||
GList *
|
||||
copy_segment_list (GList *list);
|
||||
GstElement *
|
||||
gst_element_factory_make_or_warn (const gchar * factoryname, const gchar * name);
|
||||
Segment *
|
||||
segment_new (gdouble rate, GstFormat format, gint64 start, gint64 stop, gint64 position);
|
834
tests/check/gnl/complex.c
Normal file
834
tests/check/gnl/complex.c
Normal file
|
@ -0,0 +1,834 @@
|
|||
#include "common.h"
|
||||
|
||||
static void
|
||||
fill_pipeline_and_check (GstElement * comp, GList * segments,
|
||||
gint expected_error_domain)
|
||||
{
|
||||
GstElement *pipeline, *sink;
|
||||
CollectStructure *collect;
|
||||
GstBus *bus;
|
||||
GstMessage *message;
|
||||
gboolean carry_on = TRUE;
|
||||
GstPad *sinkpad;
|
||||
GList *listcopy = copy_segment_list (segments);
|
||||
|
||||
pipeline = gst_pipeline_new ("test_pipeline");
|
||||
sink = gst_element_factory_make_or_warn ("fakesink", "sink");
|
||||
fail_if (sink == NULL);
|
||||
|
||||
gst_bin_add_many (GST_BIN (pipeline), comp, sink, NULL);
|
||||
|
||||
/* Shared data */
|
||||
collect = g_new0 (CollectStructure, 1);
|
||||
collect->comp = comp;
|
||||
collect->sink = sink;
|
||||
|
||||
/* Expected segments */
|
||||
collect->expected_segments = segments;
|
||||
|
||||
g_signal_connect (G_OBJECT (comp), "pad-added",
|
||||
G_CALLBACK (composition_pad_added_cb), collect);
|
||||
|
||||
sinkpad = gst_element_get_static_pad (sink, "sink");
|
||||
gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
|
||||
(GstPadProbeCallback) sinkpad_probe, collect, NULL);
|
||||
|
||||
bus = gst_element_get_bus (GST_ELEMENT (pipeline));
|
||||
|
||||
GST_DEBUG ("Setting pipeline to PLAYING");
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
GST_DEBUG ("Let's poll the bus");
|
||||
while (carry_on) {
|
||||
message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
|
||||
if (message) {
|
||||
switch (GST_MESSAGE_TYPE (message)) {
|
||||
case GST_MESSAGE_EOS:
|
||||
/* we should check if we really finished here */
|
||||
GST_WARNING ("Got an EOS");
|
||||
carry_on = FALSE;
|
||||
break;
|
||||
case GST_MESSAGE_SEGMENT_START:
|
||||
case GST_MESSAGE_SEGMENT_DONE:
|
||||
/* We shouldn't see any segement messages, since we didn't do a segment seek */
|
||||
GST_WARNING ("Saw a Segment start/stop");
|
||||
fail_if (TRUE);
|
||||
break;
|
||||
case GST_MESSAGE_ERROR:
|
||||
{
|
||||
GError *error;
|
||||
|
||||
gst_message_parse_error (message, &error, NULL);
|
||||
if (comp == GST_ELEMENT (GST_MESSAGE_SRC (message)) &&
|
||||
expected_error_domain == error->domain) {
|
||||
GST_DEBUG ("Expected Error Message from %s : %s",
|
||||
GST_OBJECT_NAME (GST_MESSAGE_SRC (message)), error->message);
|
||||
|
||||
carry_on = FALSE;
|
||||
} else
|
||||
fail_error_message (message);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
gst_mini_object_unref (GST_MINI_OBJECT (message));
|
||||
}
|
||||
}
|
||||
|
||||
GST_DEBUG ("Setting pipeline to READY");
|
||||
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_READY) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
|
||||
fail_if (collect->expected_segments != NULL);
|
||||
|
||||
GST_DEBUG ("Resetted pipeline to READY");
|
||||
|
||||
collect->expected_segments = listcopy;
|
||||
collect->gotsegment = FALSE;
|
||||
collect->expected_base = 0;
|
||||
|
||||
if (expected_error_domain)
|
||||
goto done;
|
||||
|
||||
GST_DEBUG ("Setting pipeline to PLAYING again");
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
carry_on = TRUE;
|
||||
|
||||
GST_DEBUG ("Let's poll the bus");
|
||||
|
||||
while (carry_on) {
|
||||
message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
|
||||
if (message) {
|
||||
switch (GST_MESSAGE_TYPE (message)) {
|
||||
case GST_MESSAGE_EOS:
|
||||
/* we should check if we really finished here */
|
||||
carry_on = FALSE;
|
||||
break;
|
||||
case GST_MESSAGE_SEGMENT_START:
|
||||
case GST_MESSAGE_SEGMENT_DONE:
|
||||
/* We shouldn't see any segement messages, since we didn't do a segment seek */
|
||||
GST_WARNING ("Saw a Segment start/stop");
|
||||
fail_if (TRUE);
|
||||
break;
|
||||
case GST_MESSAGE_ERROR:
|
||||
fail_error_message (message);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
gst_mini_object_unref (GST_MINI_OBJECT (message));
|
||||
} else {
|
||||
GST_DEBUG ("bus_poll responded, but there wasn't any message...");
|
||||
}
|
||||
}
|
||||
|
||||
fail_if (collect->expected_segments != NULL);
|
||||
|
||||
done:
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
gst_object_unref (GST_OBJECT (sinkpad));
|
||||
ASSERT_OBJECT_REFCOUNT_BETWEEN (pipeline, "main pipeline", 1, 2);
|
||||
gst_object_unref (pipeline);
|
||||
ASSERT_OBJECT_REFCOUNT_BETWEEN (bus, "main bus", 1, 2);
|
||||
gst_object_unref (bus);
|
||||
|
||||
g_free (collect);
|
||||
}
|
||||
|
||||
GST_START_TEST (test_one_space_another)
|
||||
{
|
||||
GstElement *comp, *source1, *source2;
|
||||
gboolean ret = FALSE;
|
||||
GList *segments = NULL;
|
||||
|
||||
comp =
|
||||
gst_element_factory_make_or_warn ("gnlcomposition", "test_composition");
|
||||
fail_if (comp == NULL);
|
||||
|
||||
/* TOPOLOGY
|
||||
*
|
||||
* 0 1 2 3 4 5 | Priority
|
||||
* ----------------------------------------------------------------------------
|
||||
* [-source1--] [-source2--] | 1
|
||||
* */
|
||||
|
||||
/*
|
||||
Source 1
|
||||
Start : 0s
|
||||
Duration : 1s
|
||||
Priority : 1
|
||||
*/
|
||||
source1 = videotest_gnl_src ("source1", 0, 1 * GST_SECOND, 2, 1);
|
||||
fail_if (source1 == NULL);
|
||||
check_start_stop_duration (source1, 0, 1 * GST_SECOND, 1 * GST_SECOND);
|
||||
|
||||
/*
|
||||
Source 2
|
||||
Start : 2s
|
||||
Duration : 1s
|
||||
Priority : 1
|
||||
*/
|
||||
source2 = videotest_gnl_src ("source2", 2 * GST_SECOND, 1 * GST_SECOND, 3, 1);
|
||||
fail_if (source2 == NULL);
|
||||
check_start_stop_duration (source2, 2 * GST_SECOND, 3 * GST_SECOND,
|
||||
1 * GST_SECOND);
|
||||
|
||||
/* Add one source */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source1);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 1 * GST_SECOND, 1 * GST_SECOND);
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
|
||||
/* Second source */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source2);
|
||||
check_start_stop_duration (comp, 0, 1 * GST_SECOND, 1 * GST_SECOND);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 3 * GST_SECOND, 3 * GST_SECOND);
|
||||
ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
|
||||
|
||||
/* Remove first source */
|
||||
|
||||
gst_object_ref (source1);
|
||||
gst_bin_remove (GST_BIN (comp), source1);
|
||||
check_start_stop_duration (comp, 2 * GST_SECOND, 3 * GST_SECOND,
|
||||
1 * GST_SECOND);
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
|
||||
/* Re-add first source */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source1);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 3 * GST_SECOND, 3 * GST_SECOND);
|
||||
gst_object_unref (source1);
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
|
||||
/* Expected segments */
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME, 0, 1 * GST_SECOND, 0));
|
||||
|
||||
fill_pipeline_and_check (comp, segments, GST_STREAM_ERROR);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_one_default_another)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
GstElement *comp, *source1, *source2, *source3, *defaultsrc;
|
||||
GList *segments = NULL;
|
||||
|
||||
comp =
|
||||
gst_element_factory_make_or_warn ("gnlcomposition", "test_composition");
|
||||
fail_if (comp == NULL);
|
||||
|
||||
/* TOPOLOGY
|
||||
*
|
||||
* 0 1 2 3 4 5 | Priority
|
||||
* ----------------------------------------------------------------------------
|
||||
* [-source1--] [-source2--][-source3-] | 1
|
||||
* [--------------------------defaultsource------------------] | MAXUINT32
|
||||
* */
|
||||
|
||||
|
||||
/*
|
||||
defaultsrc source
|
||||
Start : 0s
|
||||
Duration : 5s
|
||||
Priority : 2
|
||||
*/
|
||||
|
||||
defaultsrc =
|
||||
videotest_gnl_src ("defaultsrc", 0, 5 * GST_SECOND, 2, G_MAXUINT32);
|
||||
g_object_set (defaultsrc, "expandable", TRUE, NULL);
|
||||
fail_if (defaultsrc == NULL);
|
||||
check_start_stop_duration (defaultsrc, 0, 5 * GST_SECOND, 5 * GST_SECOND);
|
||||
|
||||
/*
|
||||
Source 1
|
||||
Start : 1s
|
||||
Duration : 1s
|
||||
Priority : 1
|
||||
*/
|
||||
source1 = videotest_gnl_src ("source1", 1 * GST_SECOND, 1 * GST_SECOND, 3, 1);
|
||||
fail_if (source1 == NULL);
|
||||
check_start_stop_duration (source1, GST_SECOND, 2 * GST_SECOND,
|
||||
1 * GST_SECOND);
|
||||
|
||||
/*
|
||||
Source 2
|
||||
Start : 3s
|
||||
Duration : 1s
|
||||
Priority : 1
|
||||
*/
|
||||
source2 = videotest_gnl_src ("source2", 3 * GST_SECOND, 1 * GST_SECOND, 2, 1);
|
||||
fail_if (source2 == NULL);
|
||||
check_start_stop_duration (source2, 3 * GST_SECOND, 4 * GST_SECOND,
|
||||
1 * GST_SECOND);
|
||||
|
||||
/*
|
||||
Source 3
|
||||
Start : 4s
|
||||
Duration : 1s
|
||||
Priority : 1
|
||||
*/
|
||||
source3 = videotest_gnl_src ("source3", 4 * GST_SECOND, 1 * GST_SECOND, 2, 1);
|
||||
fail_if (source3 == NULL);
|
||||
check_start_stop_duration (source3, 4 * GST_SECOND, 5 * GST_SECOND,
|
||||
1 * GST_SECOND);
|
||||
|
||||
|
||||
/* Add one source */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source1);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, GST_SECOND, 2 * GST_SECOND, 1 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
|
||||
/* defaultsrc source */
|
||||
gst_bin_add (GST_BIN (comp), defaultsrc);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 2 * GST_SECOND, 2 * GST_SECOND);
|
||||
check_start_stop_duration (defaultsrc, 0, 2 * GST_SECOND, 2 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (defaultsrc, "defaultsrc", 1);
|
||||
|
||||
/* Second source */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source2);
|
||||
ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
|
||||
/* Third source */
|
||||
gst_bin_add (GST_BIN (comp), source3);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
fail_unless (ret);
|
||||
check_start_stop_duration (comp, 0, 5 * GST_SECOND, 5 * GST_SECOND);
|
||||
check_start_stop_duration (defaultsrc, 0, 5 * GST_SECOND, 5 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source3, "source3", 1);
|
||||
|
||||
/* Expected segments */
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME, 0, 1 * GST_SECOND, 0));
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME,
|
||||
1 * GST_SECOND, 2 * GST_SECOND, 1 * GST_SECOND));
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME,
|
||||
2 * GST_SECOND, 3 * GST_SECOND, 2 * GST_SECOND));
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME,
|
||||
3 * GST_SECOND, 4 * GST_SECOND, 3 * GST_SECOND));
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME,
|
||||
4 * GST_SECOND, 5 * GST_SECOND, 4 * GST_SECOND));
|
||||
|
||||
fill_pipeline_and_check (comp, segments, GST_STREAM_ERROR);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_one_expandable_another)
|
||||
{
|
||||
GstElement *comp, *source1, *source2, *source3, *defaultsrc;
|
||||
GList *segments = NULL;
|
||||
gboolean ret = FALSE;
|
||||
|
||||
comp =
|
||||
gst_element_factory_make_or_warn ("gnlcomposition", "test_composition");
|
||||
fail_if (comp == NULL);
|
||||
|
||||
/* TOPOLOGY
|
||||
*
|
||||
* 0 1 2 3 4 5 | Priority
|
||||
* ----------------------------------------------------------------------------
|
||||
* [ source1 ] [ source2 ][ source3 ] | 1
|
||||
* [--------------------- defaultsrc ------------------------] | 1000 EXPANDABLE
|
||||
* */
|
||||
|
||||
/*
|
||||
defaultsrc source
|
||||
Start : 0s
|
||||
Duration : 5s
|
||||
Priority : 1000
|
||||
*/
|
||||
|
||||
defaultsrc = videotest_gnl_src ("defaultsrc", 0, 5 * GST_SECOND, 2, 1000);
|
||||
g_object_set (defaultsrc, "expandable", TRUE, NULL);
|
||||
fail_if (defaultsrc == NULL);
|
||||
check_start_stop_duration (defaultsrc, 0, 5 * GST_SECOND, 5 * GST_SECOND);
|
||||
|
||||
/*
|
||||
Source 1
|
||||
Start : 1s
|
||||
Duration : 1s
|
||||
Priority : 1
|
||||
*/
|
||||
source1 = videotest_gnl_src ("source1", 1 * GST_SECOND, 1 * GST_SECOND, 3, 1);
|
||||
fail_if (source1 == NULL);
|
||||
check_start_stop_duration (source1, GST_SECOND, 2 * GST_SECOND,
|
||||
1 * GST_SECOND);
|
||||
|
||||
/*
|
||||
Source 2
|
||||
Start : 3s
|
||||
Duration : 1s
|
||||
Priority : 1
|
||||
*/
|
||||
source2 = videotest_gnl_src ("source2", 3 * GST_SECOND, 1 * GST_SECOND, 2, 1);
|
||||
fail_if (source2 == NULL);
|
||||
check_start_stop_duration (source2, 3 * GST_SECOND, 4 * GST_SECOND,
|
||||
1 * GST_SECOND);
|
||||
|
||||
/*
|
||||
Source 3
|
||||
Start : 4s
|
||||
Duration : 1s
|
||||
Priority : 1
|
||||
*/
|
||||
source3 = videotest_gnl_src ("source3", 4 * GST_SECOND, 1 * GST_SECOND, 2, 1);
|
||||
fail_if (source3 == NULL);
|
||||
check_start_stop_duration (source3, 4 * GST_SECOND, 5 * GST_SECOND,
|
||||
1 * GST_SECOND);
|
||||
|
||||
|
||||
/* Add one source */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source1);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, GST_SECOND, 2 * GST_SECOND, 1 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
|
||||
/* defaultsrc source */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), defaultsrc);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 2 * GST_SECOND, 2 * GST_SECOND);
|
||||
check_start_stop_duration (defaultsrc, 0, 2 * GST_SECOND, 2 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (defaultsrc, "defaultsrc", 1);
|
||||
|
||||
/* Second source */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source2);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 4 * GST_SECOND, 4 * GST_SECOND);
|
||||
check_start_stop_duration (defaultsrc, 0, 4 * GST_SECOND, 4 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
|
||||
|
||||
|
||||
/* Third source */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source3);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 5 * GST_SECOND, 5 * GST_SECOND);
|
||||
check_start_stop_duration (defaultsrc, 0, 5 * GST_SECOND, 5 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source3, "source3", 1);
|
||||
|
||||
/* Expected segments */
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME, 0, 1 * GST_SECOND, 0));
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME,
|
||||
1 * GST_SECOND, 2 * GST_SECOND, 1 * GST_SECOND));
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME,
|
||||
2 * GST_SECOND, 3 * GST_SECOND, 2 * GST_SECOND));
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME,
|
||||
3 * GST_SECOND, 4 * GST_SECOND, 3 * GST_SECOND));
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME,
|
||||
4 * GST_SECOND, 5 * GST_SECOND, 4 * GST_SECOND));
|
||||
|
||||
fill_pipeline_and_check (comp, segments, 0);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
|
||||
|
||||
GST_START_TEST (test_renegotiation)
|
||||
{
|
||||
gboolean ret;
|
||||
GstElement *pipeline;
|
||||
GstElement *comp, *sink, *source1, *source2, *source3;
|
||||
GstElement *audioconvert;
|
||||
CollectStructure *collect;
|
||||
GstBus *bus;
|
||||
GstMessage *message;
|
||||
gboolean carry_on = TRUE;
|
||||
GstPad *sinkpad;
|
||||
GstCaps *caps;
|
||||
|
||||
pipeline = gst_pipeline_new ("test_pipeline");
|
||||
comp =
|
||||
gst_element_factory_make_or_warn ("gnlcomposition", "test_composition");
|
||||
fail_if (comp == NULL);
|
||||
|
||||
/*
|
||||
Source 1
|
||||
Start : 1s
|
||||
Duration : 1s
|
||||
Priority : 1
|
||||
*/
|
||||
source1 =
|
||||
audiotest_bin_src ("source1", 0 * GST_SECOND, 1 * GST_SECOND, 1, FALSE);
|
||||
check_start_stop_duration (source1, 0 * GST_SECOND, 1 * GST_SECOND,
|
||||
1 * GST_SECOND);
|
||||
|
||||
/*
|
||||
Source 2
|
||||
Start : 1s
|
||||
Duration : 1s
|
||||
Priority : 1
|
||||
*/
|
||||
source2 =
|
||||
audiotest_bin_src ("source2", 1 * GST_SECOND, 1 * GST_SECOND, 1, TRUE);
|
||||
check_start_stop_duration (source2, 1 * GST_SECOND, 2 * GST_SECOND,
|
||||
1 * GST_SECOND);
|
||||
|
||||
/*
|
||||
Source 3
|
||||
Start : 2s
|
||||
Duration : 1s
|
||||
Priority : 1
|
||||
*/
|
||||
source3 =
|
||||
audiotest_bin_src ("source3", 2 * GST_SECOND, 1 * GST_SECOND, 1, FALSE);
|
||||
check_start_stop_duration (source3, 2 * GST_SECOND, 3 * GST_SECOND,
|
||||
1 * GST_SECOND);
|
||||
|
||||
/* Add one source */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source1);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 1 * GST_SECOND, 1 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
|
||||
/* Second source */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source2);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 2 * GST_SECOND, 2 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
|
||||
|
||||
|
||||
/* Third source */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source3);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 3 * GST_SECOND, 3 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source3, "source3", 1);
|
||||
|
||||
|
||||
sink = gst_element_factory_make_or_warn ("fakesink", "sink");
|
||||
audioconvert = gst_element_factory_make_or_warn ("audioconvert", "aconv");
|
||||
|
||||
gst_bin_add_many (GST_BIN (pipeline), comp, audioconvert, sink, NULL);
|
||||
caps = gst_caps_from_string ("audio/x-raw,format=(string)S16LE");
|
||||
gst_element_link_filtered (audioconvert, sink, caps);
|
||||
gst_caps_unref (caps);
|
||||
|
||||
/* Shared data */
|
||||
collect = g_new0 (CollectStructure, 1);
|
||||
collect->comp = comp;
|
||||
collect->sink = audioconvert;
|
||||
|
||||
/* Expected segments */
|
||||
collect->expected_segments = g_list_append (collect->expected_segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME, 0, 1 * GST_SECOND, 0));
|
||||
collect->expected_segments = g_list_append (collect->expected_segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME,
|
||||
1 * GST_SECOND, 2 * GST_SECOND, 1 * GST_SECOND));
|
||||
collect->expected_segments = g_list_append (collect->expected_segments,
|
||||
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);
|
||||
|
||||
sinkpad = gst_element_get_static_pad (sink, "sink");
|
||||
gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
|
||||
(GstPadProbeCallback) sinkpad_probe, collect, NULL);
|
||||
|
||||
bus = gst_element_get_bus (GST_ELEMENT (pipeline));
|
||||
|
||||
GST_DEBUG ("Setting pipeline to PLAYING");
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
GST_DEBUG ("Let's poll the bus");
|
||||
|
||||
while (carry_on) {
|
||||
message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
|
||||
if (message) {
|
||||
switch (GST_MESSAGE_TYPE (message)) {
|
||||
case GST_MESSAGE_EOS:
|
||||
/* we should check if we really finished here */
|
||||
GST_WARNING ("Got an EOS");
|
||||
carry_on = FALSE;
|
||||
break;
|
||||
case GST_MESSAGE_SEGMENT_START:
|
||||
case GST_MESSAGE_SEGMENT_DONE:
|
||||
/* We shouldn't see any segement messages, since we didn't do a segment seek */
|
||||
GST_WARNING ("Saw a Segment start/stop");
|
||||
fail_if (TRUE);
|
||||
break;
|
||||
case GST_MESSAGE_ERROR:
|
||||
fail_error_message (message);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
gst_mini_object_unref (GST_MINI_OBJECT (message));
|
||||
}
|
||||
}
|
||||
|
||||
GST_DEBUG ("Setting pipeline to NULL");
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_READY) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
fail_if (collect->expected_segments != NULL);
|
||||
|
||||
GST_DEBUG ("Resetted pipeline to READY");
|
||||
|
||||
/* Expected segments */
|
||||
collect->expected_segments = g_list_append (collect->expected_segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME, 0, 1 * GST_SECOND, 0));
|
||||
collect->expected_segments = g_list_append (collect->expected_segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME,
|
||||
1 * GST_SECOND, 2 * GST_SECOND, 1 * GST_SECOND));
|
||||
collect->expected_segments = g_list_append (collect->expected_segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME,
|
||||
2 * GST_SECOND, 3 * GST_SECOND, 2 * GST_SECOND));
|
||||
collect->gotsegment = FALSE;
|
||||
collect->expected_base = 0;
|
||||
|
||||
|
||||
GST_DEBUG ("Setting pipeline to PLAYING again");
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
carry_on = TRUE;
|
||||
|
||||
GST_DEBUG ("Let's poll the bus");
|
||||
|
||||
while (carry_on) {
|
||||
message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
|
||||
if (message) {
|
||||
switch (GST_MESSAGE_TYPE (message)) {
|
||||
case GST_MESSAGE_EOS:
|
||||
/* we should check if we really finished here */
|
||||
carry_on = FALSE;
|
||||
break;
|
||||
case GST_MESSAGE_SEGMENT_START:
|
||||
case GST_MESSAGE_SEGMENT_DONE:
|
||||
/* We shouldn't see any segement messages, since we didn't do a segment seek */
|
||||
GST_WARNING ("Saw a Segment start/stop");
|
||||
fail_if (TRUE);
|
||||
break;
|
||||
case GST_MESSAGE_ERROR:
|
||||
fail_error_message (message);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
gst_mini_object_unref (GST_MINI_OBJECT (message));
|
||||
} else {
|
||||
GST_DEBUG ("bus_poll responded, but there wasn't any message...");
|
||||
}
|
||||
}
|
||||
|
||||
fail_if (collect->expected_segments != NULL);
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
gst_object_unref (GST_OBJECT (sinkpad));
|
||||
ASSERT_OBJECT_REFCOUNT_BETWEEN (pipeline, "main pipeline", 1, 2);
|
||||
gst_object_unref (pipeline);
|
||||
ASSERT_OBJECT_REFCOUNT_BETWEEN (bus, "main bus", 1, 2);
|
||||
gst_object_unref (bus);
|
||||
|
||||
g_free (collect);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
|
||||
GST_START_TEST (test_one_bin_space_another)
|
||||
{
|
||||
GstElement *comp, *source1, *source2;
|
||||
gboolean ret = FALSE;
|
||||
GList *segments = NULL;
|
||||
|
||||
comp =
|
||||
gst_element_factory_make_or_warn ("gnlcomposition", "test_composition");
|
||||
fail_if (comp == NULL);
|
||||
|
||||
/*
|
||||
Source 1
|
||||
Start : 0s
|
||||
Duration : 1s
|
||||
Priority : 1
|
||||
*/
|
||||
source1 = videotest_in_bin_gnl_src ("source1", 0, 1 * GST_SECOND, 3, 1);
|
||||
fail_if (source1 == NULL);
|
||||
check_start_stop_duration (source1, 0, 1 * GST_SECOND, 1 * GST_SECOND);
|
||||
|
||||
/*
|
||||
Source 2
|
||||
Start : 2s
|
||||
Duration : 1s
|
||||
Priority : 1
|
||||
*/
|
||||
source2 =
|
||||
videotest_in_bin_gnl_src ("source2", 2 * GST_SECOND, 1 * GST_SECOND, 2,
|
||||
1);
|
||||
fail_if (source2 == NULL);
|
||||
check_start_stop_duration (source2, 2 * GST_SECOND, 3 * GST_SECOND,
|
||||
1 * GST_SECOND);
|
||||
|
||||
/* Add one source */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source1);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 1 * GST_SECOND, 1 * GST_SECOND);
|
||||
|
||||
/* Second source */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source2);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 3 * GST_SECOND, 3 * GST_SECOND);
|
||||
|
||||
/* Remove second source */
|
||||
|
||||
gst_object_ref (source1);
|
||||
gst_bin_remove (GST_BIN (comp), source1);
|
||||
check_start_stop_duration (comp, 2 * GST_SECOND, 3 * GST_SECOND,
|
||||
1 * GST_SECOND);
|
||||
|
||||
/* Re-add second source */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source1);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 3 * GST_SECOND, 3 * GST_SECOND);
|
||||
gst_object_unref (source1);
|
||||
|
||||
/* Expected segments */
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME, 0, 1 * GST_SECOND, 0));
|
||||
|
||||
fill_pipeline_and_check (comp, segments, GST_STREAM_ERROR);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_one_above_another)
|
||||
{
|
||||
GstElement *comp, *source1, *source2;
|
||||
gboolean ret = FALSE;
|
||||
GList *segments = NULL;
|
||||
|
||||
comp =
|
||||
gst_element_factory_make_or_warn ("gnlcomposition", "test_composition");
|
||||
fail_if (comp == NULL);
|
||||
|
||||
/*
|
||||
Source 1
|
||||
Start : 0s
|
||||
Duration : 2s
|
||||
Priority : 2
|
||||
*/
|
||||
source1 = videotest_gnl_src ("source1", 0, 2 * GST_SECOND, 3, 2);
|
||||
fail_if (source1 == NULL);
|
||||
check_start_stop_duration (source1, 0, 2 * GST_SECOND, 2 * GST_SECOND);
|
||||
|
||||
/*
|
||||
Source 2
|
||||
Start : 2s
|
||||
Duration : 2s
|
||||
Priority : 1
|
||||
*/
|
||||
source2 = videotest_gnl_src ("source2", 1 * GST_SECOND, 2 * GST_SECOND, 2, 1);
|
||||
fail_if (source2 == NULL);
|
||||
check_start_stop_duration (source2, 1 * GST_SECOND, 3 * GST_SECOND,
|
||||
2 * GST_SECOND);
|
||||
|
||||
/* Add one source */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source1);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 2 * GST_SECOND, 2 * GST_SECOND);
|
||||
|
||||
/* Second source */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source2);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 3 * GST_SECOND, 3 * GST_SECOND);
|
||||
|
||||
/* Remove second source */
|
||||
|
||||
gst_object_ref (source1);
|
||||
gst_bin_remove (GST_BIN (comp), source1);
|
||||
check_start_stop_duration (comp, 1 * GST_SECOND, 3 * GST_SECOND,
|
||||
2 * GST_SECOND);
|
||||
|
||||
/* Re-add second source */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source1);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 3 * GST_SECOND, 3 * GST_SECOND);
|
||||
gst_object_unref (source1);
|
||||
|
||||
/* Expected segments */
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME, 0, 1 * GST_SECOND, 0));
|
||||
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME,
|
||||
1 * GST_SECOND, 3 * GST_SECOND, 1 * GST_SECOND));
|
||||
|
||||
fill_pipeline_and_check (comp, segments, 0);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
static Suite *
|
||||
gnonlin_suite (void)
|
||||
{
|
||||
Suite *s = suite_create ("gnonlin-complex");
|
||||
TCase *tc_chain = tcase_create ("complex");
|
||||
|
||||
suite_add_tcase (s, tc_chain);
|
||||
|
||||
tcase_add_test (tc_chain, test_one_space_another);
|
||||
tcase_add_test (tc_chain, test_one_default_another);
|
||||
tcase_add_test (tc_chain, test_one_expandable_another);
|
||||
tcase_add_test (tc_chain, test_renegotiation);
|
||||
tcase_add_test (tc_chain, test_one_bin_space_another);
|
||||
tcase_add_test (tc_chain, test_one_above_another);
|
||||
return s;
|
||||
}
|
||||
|
||||
GST_CHECK_MAIN (gnonlin)
|
560
tests/check/gnl/gnlcomposition.c
Normal file
560
tests/check/gnl/gnlcomposition.c
Normal file
|
@ -0,0 +1,560 @@
|
|||
/* Gnonlin
|
||||
* Copyright (C) <2009> Alessandro Decina <alessandro.decina@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 "common.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GstElement *composition;
|
||||
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,
|
||||
gpointer user_data)
|
||||
{
|
||||
if (GST_EVENT_TYPE (info->data) == GST_EVENT_SEEK)
|
||||
++seek_events;
|
||||
|
||||
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)
|
||||
{
|
||||
GstElement *pipeline;
|
||||
GstElement *comp, *source1, *def, *sink;
|
||||
GstBus *bus;
|
||||
GstMessage *message;
|
||||
gboolean carry_on, ret = FALSE;
|
||||
int seek_events_before;
|
||||
|
||||
pipeline = gst_pipeline_new ("test_pipeline");
|
||||
comp =
|
||||
gst_element_factory_make_or_warn ("gnlcomposition", "test_composition");
|
||||
|
||||
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);
|
||||
|
||||
/*
|
||||
source1
|
||||
Start : 0s
|
||||
Duration : 2s
|
||||
Priority : 2
|
||||
*/
|
||||
|
||||
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);
|
||||
|
||||
/*
|
||||
def (default source)
|
||||
Priority = G_MAXUINT32
|
||||
*/
|
||||
def =
|
||||
videotest_gnl_src ("default", 0 * GST_SECOND, 0 * GST_SECOND, 2,
|
||||
G_MAXUINT32);
|
||||
g_object_set (def, "expandable", TRUE, NULL);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
ASSERT_OBJECT_REFCOUNT (def, "default", 1);
|
||||
|
||||
/* Add source 1 */
|
||||
|
||||
/* keep an extra ref to source1 as we remove it from the bin */
|
||||
gst_object_ref (source1);
|
||||
gst_bin_add (GST_BIN (comp), source1);
|
||||
|
||||
/* Add default */
|
||||
gst_bin_add (GST_BIN (comp), def);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (source1, 0, 2 * GST_SECOND, 2 * GST_SECOND);
|
||||
check_start_stop_duration (comp, 0, 2 * GST_SECOND, 2 * GST_SECOND);
|
||||
|
||||
bus = gst_element_get_bus (GST_ELEMENT (pipeline));
|
||||
|
||||
GST_DEBUG ("Setting pipeline to PLAYING");
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 2);
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
GST_DEBUG ("Let's poll the bus");
|
||||
|
||||
carry_on = TRUE;
|
||||
while (carry_on) {
|
||||
message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
|
||||
if (message) {
|
||||
switch (GST_MESSAGE_TYPE (message)) {
|
||||
case GST_MESSAGE_ASYNC_DONE:
|
||||
{
|
||||
carry_on = FALSE;
|
||||
GST_DEBUG ("Pipeline reached PAUSED, stopping polling");
|
||||
break;
|
||||
}
|
||||
case GST_MESSAGE_EOS:
|
||||
{
|
||||
GST_WARNING ("Saw EOS");
|
||||
|
||||
fail_if (TRUE);
|
||||
}
|
||||
case GST_MESSAGE_ERROR:
|
||||
fail_error_message (message);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
gst_mini_object_unref (GST_MINI_OBJECT (message));
|
||||
}
|
||||
}
|
||||
|
||||
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 */
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
|
||||
g_mutex_lock (&pad_added_lock);
|
||||
g_cond_wait (&pad_added_cond, &pad_added_lock);
|
||||
fail_unless_equals_int (composition_pad_added, 2);
|
||||
fail_unless_equals_int (composition_pad_removed, 1);
|
||||
g_mutex_unlock (&pad_added_lock);
|
||||
|
||||
seek_events_before = seek_events;
|
||||
|
||||
g_object_set (source1, "duration", (guint64) 1 * GST_SECOND, NULL);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
fail_unless (seek_events > seek_events_before);
|
||||
|
||||
GST_DEBUG ("Setting pipeline to NULL");
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE);
|
||||
gst_element_set_state (source1, GST_STATE_NULL);
|
||||
gst_object_unref (source1);
|
||||
|
||||
GST_DEBUG ("Resetted pipeline to READY");
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT_BETWEEN (pipeline, "main pipeline", 1, 2);
|
||||
gst_object_unref (pipeline);
|
||||
ASSERT_OBJECT_REFCOUNT_BETWEEN (bus, "main bus", 1, 2);
|
||||
gst_object_unref (bus);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_remove_invalid_object)
|
||||
{
|
||||
GstBin *composition;
|
||||
GstElement *source1, *source2;
|
||||
|
||||
composition = GST_BIN (gst_element_factory_make ("gnlcomposition",
|
||||
"composition"));
|
||||
source1 = gst_element_factory_make ("gnlsource", "source1");
|
||||
source2 = gst_element_factory_make ("gnlsource", "source2");
|
||||
|
||||
gst_bin_add (composition, source1);
|
||||
fail_if (gst_bin_remove (composition, source2));
|
||||
fail_unless (gst_bin_remove (composition, source1));
|
||||
|
||||
gst_object_unref (composition);
|
||||
gst_object_unref (source2);
|
||||
}
|
||||
|
||||
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;
|
||||
GstMessage *message;
|
||||
GstElement *pipeline;
|
||||
GstElement *gnl_adder;
|
||||
GstElement *composition;
|
||||
GstElement *adder, *fakesink;
|
||||
GstClockTime start_playing_time;
|
||||
GstElement *gnlsource1, *gnlsource2;
|
||||
GstElement *audiotestsrc1, *audiotestsrc2;
|
||||
|
||||
gboolean carry_on = TRUE, ret;
|
||||
GstClockTime total_time = 10 * GST_SECOND;
|
||||
|
||||
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);
|
||||
g_object_set (fakesink, "sync", TRUE, NULL);
|
||||
|
||||
/* gnl_adder */
|
||||
gnl_adder = gst_element_factory_make ("gnloperation", "gnl_adder");
|
||||
adder = gst_element_factory_make ("adder", "adder");
|
||||
fail_unless (adder != NULL);
|
||||
gst_bin_add (GST_BIN (gnl_adder), adder);
|
||||
g_object_set (gnl_adder, "start", (guint64) 0 * GST_SECOND,
|
||||
"duration", total_time, "inpoint", (guint64) 0 * GST_SECOND,
|
||||
"priority", 0, NULL);
|
||||
gst_bin_add (GST_BIN (composition), gnl_adder);
|
||||
|
||||
/* source 1 */
|
||||
gnlsource1 = gst_element_factory_make ("gnlsource", "gnlsource1");
|
||||
audiotestsrc1 = gst_element_factory_make ("audiotestsrc", "audiotestsrc1");
|
||||
gst_bin_add (GST_BIN (gnlsource1), audiotestsrc1);
|
||||
g_object_set (gnlsource1, "start", (guint64) 0 * GST_SECOND,
|
||||
"duration", total_time / 2, "inpoint", (guint64) 0, "priority", 1, NULL);
|
||||
fail_unless (gst_bin_add (GST_BIN (composition), gnlsource1));
|
||||
|
||||
/* gnlsource2 */
|
||||
gnlsource2 = gst_element_factory_make ("gnlsource", "gnlsource2");
|
||||
audiotestsrc2 = gst_element_factory_make ("audiotestsrc", "audiotestsrc2");
|
||||
gst_bin_add (GST_BIN (gnlsource2), GST_ELEMENT (audiotestsrc2));
|
||||
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);
|
||||
|
||||
GST_DEBUG ("Setting pipeline to PAUSED");
|
||||
|
||||
g_signal_emit_by_name (composition, "commit", TRUE, &ret);
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING)
|
||||
== 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_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline),
|
||||
GST_DEBUG_GRAPH_SHOW_ALL, "gnl-simple-adder-test-play");
|
||||
|
||||
/* Now play the 10 second composition */
|
||||
start_playing_time = gst_util_get_timestamp ();
|
||||
while (carry_on) {
|
||||
|
||||
if (GST_CLOCK_DIFF (start_playing_time, gst_util_get_timestamp ()) >
|
||||
total_time + GST_SECOND) {
|
||||
GST_ERROR ("No EOS found after %" GST_TIME_FORMAT " sec",
|
||||
GST_TIME_ARGS ((total_time / GST_SECOND) + 1));
|
||||
GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (pipeline),
|
||||
GST_DEBUG_GRAPH_SHOW_ALL, "gnl-simple-adder-test-fail");
|
||||
|
||||
fail_unless ("No EOS received" == NULL);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
|
||||
GST_LOG ("poll: %" GST_PTR_FORMAT, message);
|
||||
if (message) {
|
||||
switch (GST_MESSAGE_TYPE (message)) {
|
||||
case GST_MESSAGE_EOS:
|
||||
/* we should check if we really finished here */
|
||||
GST_WARNING ("Got an EOS");
|
||||
carry_on = FALSE;
|
||||
break;
|
||||
case GST_MESSAGE_SEGMENT_START:
|
||||
case GST_MESSAGE_SEGMENT_DONE:
|
||||
/* We shouldn't see any segement messages, since we didn't do a segment seek */
|
||||
GST_WARNING ("Saw a Segment start/stop");
|
||||
fail_if (TRUE);
|
||||
carry_on = FALSE;
|
||||
break;
|
||||
case GST_MESSAGE_ERROR:
|
||||
fail_error_message (message);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
gst_mini_object_unref (GST_MINI_OBJECT (message));
|
||||
}
|
||||
}
|
||||
|
||||
gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL);
|
||||
gst_object_unref (pipeline);
|
||||
gst_object_unref (bus);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
static Suite *
|
||||
gnonlin_suite (void)
|
||||
{
|
||||
Suite *s = suite_create ("gnlcomposition");
|
||||
TCase *tc_chain = tcase_create ("gnlcomposition");
|
||||
|
||||
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)) {
|
||||
tcase_add_test (tc_chain, test_simple_adder);
|
||||
} else {
|
||||
GST_WARNING ("adder element not available, skipping 1 test");
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
GST_CHECK_MAIN (gnonlin)
|
698
tests/check/gnl/gnloperation.c
Normal file
698
tests/check/gnl/gnloperation.c
Normal file
|
@ -0,0 +1,698 @@
|
|||
#include "common.h"
|
||||
|
||||
static void
|
||||
fill_pipeline_and_check (GstElement * comp, GList * segments)
|
||||
{
|
||||
GstElement *pipeline, *sink;
|
||||
CollectStructure *collect;
|
||||
GstBus *bus;
|
||||
GstMessage *message;
|
||||
gboolean carry_on = TRUE;
|
||||
GstPad *sinkpad;
|
||||
GList *listcopy = copy_segment_list (segments);
|
||||
|
||||
pipeline = gst_pipeline_new ("test_pipeline");
|
||||
sink = gst_element_factory_make_or_warn ("fakesink", "sink");
|
||||
fail_if (sink == NULL);
|
||||
|
||||
gst_bin_add_many (GST_BIN (pipeline), comp, sink, NULL);
|
||||
|
||||
/* Shared data */
|
||||
collect = g_new0 (CollectStructure, 1);
|
||||
collect->comp = comp;
|
||||
collect->sink = sink;
|
||||
|
||||
/* Expected segments */
|
||||
collect->expected_segments = segments;
|
||||
|
||||
g_signal_connect (G_OBJECT (comp), "pad-added",
|
||||
G_CALLBACK (composition_pad_added_cb), collect);
|
||||
|
||||
sinkpad = gst_element_get_static_pad (sink, "sink");
|
||||
gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
|
||||
(GstPadProbeCallback) sinkpad_probe, collect, NULL);
|
||||
|
||||
bus = gst_element_get_bus (GST_ELEMENT (pipeline));
|
||||
|
||||
GST_DEBUG ("Setting pipeline to PLAYING");
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
GST_DEBUG ("Let's poll the bus");
|
||||
while (carry_on) {
|
||||
message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
|
||||
if (message) {
|
||||
switch (GST_MESSAGE_TYPE (message)) {
|
||||
case GST_MESSAGE_EOS:
|
||||
/* we should check if we really finished here */
|
||||
GST_WARNING ("Got an EOS");
|
||||
carry_on = FALSE;
|
||||
break;
|
||||
case GST_MESSAGE_SEGMENT_START:
|
||||
case GST_MESSAGE_SEGMENT_DONE:
|
||||
/* We shouldn't see any segement messages, since we didn't do a segment seek */
|
||||
GST_WARNING ("Saw a Segment start/stop");
|
||||
fail_if (TRUE);
|
||||
break;
|
||||
case GST_MESSAGE_ERROR:
|
||||
fail_error_message (message);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
gst_mini_object_unref (GST_MINI_OBJECT (message));
|
||||
}
|
||||
}
|
||||
|
||||
GST_DEBUG ("Setting pipeline to READY");
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_READY) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
fail_if (collect->expected_segments != NULL);
|
||||
|
||||
GST_DEBUG ("Resetted pipeline to READY");
|
||||
|
||||
collect->expected_base = 0;
|
||||
collect->expected_segments = listcopy;
|
||||
collect->gotsegment = FALSE;
|
||||
|
||||
GST_DEBUG ("Setting pipeline to PLAYING again");
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
carry_on = TRUE;
|
||||
|
||||
GST_DEBUG ("Let's poll the bus");
|
||||
|
||||
while (carry_on) {
|
||||
message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
|
||||
if (message) {
|
||||
switch (GST_MESSAGE_TYPE (message)) {
|
||||
case GST_MESSAGE_EOS:
|
||||
/* we should check if we really finished here */
|
||||
carry_on = FALSE;
|
||||
break;
|
||||
case GST_MESSAGE_SEGMENT_START:
|
||||
case GST_MESSAGE_SEGMENT_DONE:
|
||||
/* We shouldn't see any segement messages, since we didn't do a segment seek */
|
||||
GST_WARNING ("Saw a Segment start/stop");
|
||||
fail_if (TRUE);
|
||||
break;
|
||||
case GST_MESSAGE_ERROR:
|
||||
fail_error_message (message);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
gst_mini_object_unref (GST_MINI_OBJECT (message));
|
||||
} else {
|
||||
GST_DEBUG ("bus_poll responded, but there wasn't any message...");
|
||||
}
|
||||
}
|
||||
|
||||
fail_if (collect->expected_segments != NULL);
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
gst_object_unref (GST_OBJECT (sinkpad));
|
||||
ASSERT_OBJECT_REFCOUNT_BETWEEN (pipeline, "main pipeline", 1, 2);
|
||||
gst_object_unref (pipeline);
|
||||
ASSERT_OBJECT_REFCOUNT_BETWEEN (bus, "main bus", 1, 2);
|
||||
gst_object_unref (bus);
|
||||
|
||||
g_free (collect);
|
||||
}
|
||||
|
||||
GST_START_TEST (test_simple_operation)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
GstElement *comp, *oper, *source;
|
||||
GList *segments = NULL;
|
||||
|
||||
comp =
|
||||
gst_element_factory_make_or_warn ("gnlcomposition", "test_composition");
|
||||
|
||||
/* TOPOLOGY
|
||||
*
|
||||
* 0 1 2 3 4 5 | Priority
|
||||
* ----------------------------------------------------------------------------
|
||||
* [-- oper --] | 0
|
||||
* [------------- source -------------] | 1
|
||||
* */
|
||||
|
||||
/*
|
||||
source
|
||||
Start : 0s
|
||||
Duration : 3s
|
||||
Priority : 1
|
||||
*/
|
||||
|
||||
source = videotest_gnl_src ("source", 0, 3 * GST_SECOND, 2, 1);
|
||||
fail_if (source == NULL);
|
||||
|
||||
/*
|
||||
operation
|
||||
Start : 1s
|
||||
Duration : 1s
|
||||
Priority : 0
|
||||
*/
|
||||
|
||||
oper = new_operation ("oper", "identity", 1 * GST_SECOND, 1 * GST_SECOND, 0);
|
||||
fail_if (oper == NULL);
|
||||
|
||||
/* Add source */
|
||||
ASSERT_OBJECT_REFCOUNT (source, "source", 1);
|
||||
ASSERT_OBJECT_REFCOUNT (oper, "oper", 1);
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 3 * GST_SECOND, 3 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source, "source", 1);
|
||||
|
||||
/* Add operaton */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), oper);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 3 * GST_SECOND, 3 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (oper, "oper", 1);
|
||||
|
||||
/* remove source */
|
||||
|
||||
gst_object_ref (source);
|
||||
gst_bin_remove (GST_BIN (comp), source);
|
||||
check_start_stop_duration (comp, 1 * GST_SECOND, 2 * GST_SECOND,
|
||||
1 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source, "source", 1);
|
||||
|
||||
/* re-add source */
|
||||
gst_bin_add (GST_BIN (comp), source);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 3 * GST_SECOND, 3 * GST_SECOND);
|
||||
gst_object_unref (source);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source, "source", 1);
|
||||
|
||||
/* Expected segments */
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME, 0, 1 * GST_SECOND, 0));
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME,
|
||||
1 * GST_SECOND, 2 * GST_SECOND, 1 * GST_SECOND));
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME,
|
||||
2 * GST_SECOND, 3 * GST_SECOND, 2 * GST_SECOND));
|
||||
|
||||
fill_pipeline_and_check (comp, segments);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_pyramid_operations)
|
||||
{
|
||||
GstElement *comp, *oper1, *oper2, *source;
|
||||
gboolean ret = FALSE;
|
||||
GList *segments = NULL;
|
||||
|
||||
comp =
|
||||
gst_element_factory_make_or_warn ("gnlcomposition", "test_composition");
|
||||
|
||||
/*
|
||||
source
|
||||
Start : 0s
|
||||
Duration : 10s
|
||||
Priority : 2
|
||||
*/
|
||||
|
||||
source = videotest_gnl_src ("source", 0, 10 * GST_SECOND, 2, 2);
|
||||
|
||||
/*
|
||||
operation1
|
||||
Start : 4s
|
||||
Duration : 2s
|
||||
Priority : 1
|
||||
*/
|
||||
|
||||
oper1 =
|
||||
new_operation ("oper1", "identity", 4 * GST_SECOND, 2 * GST_SECOND, 1);
|
||||
|
||||
/*
|
||||
operation2
|
||||
Start : 2s
|
||||
Duration : 6s
|
||||
Priority : 0
|
||||
*/
|
||||
|
||||
oper2 =
|
||||
new_operation ("oper2", "identity", 2 * GST_SECOND, 6 * GST_SECOND, 0);
|
||||
|
||||
/* Add source */
|
||||
ASSERT_OBJECT_REFCOUNT (source, "source", 1);
|
||||
ASSERT_OBJECT_REFCOUNT (oper1, "oper1", 1);
|
||||
ASSERT_OBJECT_REFCOUNT (oper2, "oper2", 1);
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (source, 0, 10 * GST_SECOND, 10 * GST_SECOND);
|
||||
check_start_stop_duration (comp, 0, 10 * GST_SECOND, 10 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source, "source", 1);
|
||||
|
||||
/* Add operation 1 */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), oper1);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (oper1, 4 * GST_SECOND, 6 * GST_SECOND,
|
||||
2 * GST_SECOND);
|
||||
check_start_stop_duration (comp, 0, 10 * GST_SECOND, 10 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (oper1, "oper1", 1);
|
||||
|
||||
/* Add operation 2 */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), oper2);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (oper2, 2 * GST_SECOND, 8 * GST_SECOND,
|
||||
6 * GST_SECOND);
|
||||
check_start_stop_duration (comp, 0, 10 * GST_SECOND, 10 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (oper1, "oper2", 1);
|
||||
|
||||
/* Expected segments */
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME, 0, 2 * GST_SECOND, 0));
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME,
|
||||
2 * GST_SECOND, 4 * GST_SECOND, 2 * GST_SECOND));
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME,
|
||||
4 * GST_SECOND, 6 * GST_SECOND, 4 * GST_SECOND));
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME,
|
||||
6 * GST_SECOND, 8 * GST_SECOND, 6 * GST_SECOND));
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME,
|
||||
8 * GST_SECOND, 10 * GST_SECOND, 8 * GST_SECOND));
|
||||
|
||||
fill_pipeline_and_check (comp, segments);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_pyramid_operations2)
|
||||
{
|
||||
gboolean ret;
|
||||
GstElement *comp, *oper, *source1, *source2, *def;
|
||||
GList *segments = NULL;
|
||||
|
||||
comp =
|
||||
gst_element_factory_make_or_warn ("gnlcomposition", "test_composition");
|
||||
|
||||
/*
|
||||
source1
|
||||
Start : 0s
|
||||
Duration : 2s
|
||||
Priority : 2
|
||||
*/
|
||||
|
||||
source1 = videotest_gnl_src ("source1", 0, 2 * GST_SECOND, 2, 2);
|
||||
|
||||
/*
|
||||
operation
|
||||
Start : 1s
|
||||
Duration : 4s
|
||||
Priority : 1
|
||||
*/
|
||||
|
||||
oper = new_operation ("oper", "identity", 1 * GST_SECOND, 4 * GST_SECOND, 1);
|
||||
|
||||
/*
|
||||
source2
|
||||
Start : 4s
|
||||
Duration : 2s
|
||||
Priority : 2
|
||||
*/
|
||||
|
||||
source2 = videotest_gnl_src ("source2", 4 * GST_SECOND, 2 * GST_SECOND, 2, 2);
|
||||
|
||||
/*
|
||||
def (default source)
|
||||
Priority = G_MAXUINT32
|
||||
*/
|
||||
def =
|
||||
videotest_gnl_src ("default", 0 * GST_SECOND, 0 * GST_SECOND, 2,
|
||||
G_MAXUINT32);
|
||||
g_object_set (def, "expandable", TRUE, NULL);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
|
||||
ASSERT_OBJECT_REFCOUNT (oper, "oper", 1);
|
||||
ASSERT_OBJECT_REFCOUNT (def, "default", 1);
|
||||
|
||||
/* Add source 1 */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source1);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 2 * GST_SECOND, 2 * GST_SECOND);
|
||||
|
||||
/* Add source 2 */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source2);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 6 * GST_SECOND, 6 * GST_SECOND);
|
||||
|
||||
/* Add operation */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), oper);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 6 * GST_SECOND, 6 * GST_SECOND);
|
||||
|
||||
/* Add default */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), def);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 6 * GST_SECOND, 6 * GST_SECOND);
|
||||
|
||||
|
||||
/* Expected segments */
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME, 0, 1 * GST_SECOND, 0));
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME,
|
||||
1 * GST_SECOND, 2 * GST_SECOND, 1 * GST_SECOND));
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME,
|
||||
2 * GST_SECOND, 4 * GST_SECOND, 2 * GST_SECOND));
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME,
|
||||
4 * GST_SECOND, 5 * GST_SECOND, 4 * GST_SECOND));
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME,
|
||||
5 * GST_SECOND, 6 * GST_SECOND, 5 * GST_SECOND));
|
||||
|
||||
fill_pipeline_and_check (comp, segments);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_pyramid_operations_expandable)
|
||||
{
|
||||
GstElement *comp, *oper, *source1, *source2, *def;
|
||||
gboolean ret = FALSE;
|
||||
GList *segments = NULL;
|
||||
|
||||
comp =
|
||||
gst_element_factory_make_or_warn ("gnlcomposition", "test_composition");
|
||||
|
||||
/*
|
||||
source1
|
||||
Start : 0s
|
||||
Duration : 2s
|
||||
Priority : 2
|
||||
*/
|
||||
|
||||
source1 = videotest_gnl_src ("source1", 0, 2 * GST_SECOND, 2, 2);
|
||||
|
||||
/*
|
||||
operation (expandable)
|
||||
Start : XX
|
||||
Duration : XX
|
||||
Priority : 1
|
||||
*/
|
||||
|
||||
oper = new_operation ("oper", "identity", 1 * GST_SECOND, 4 * GST_SECOND, 1);
|
||||
g_object_set (oper, "expandable", TRUE, NULL);
|
||||
|
||||
/*
|
||||
source2
|
||||
Start : 4s
|
||||
Duration : 2s
|
||||
Priority : 2
|
||||
*/
|
||||
|
||||
source2 = videotest_gnl_src ("source2", 4 * GST_SECOND, 2 * GST_SECOND, 2, 2);
|
||||
|
||||
/*
|
||||
def (default source)
|
||||
Priority = G_MAXUINT32
|
||||
*/
|
||||
def =
|
||||
videotest_gnl_src ("default", 0 * GST_SECOND, 0 * GST_SECOND, 2,
|
||||
G_MAXUINT32);
|
||||
g_object_set (def, "expandable", TRUE, NULL);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
|
||||
ASSERT_OBJECT_REFCOUNT (oper, "oper", 1);
|
||||
ASSERT_OBJECT_REFCOUNT (def, "default", 1);
|
||||
|
||||
/* Add source 1 */
|
||||
gst_bin_add (GST_BIN (comp), source1);
|
||||
/* Add source 2 */
|
||||
gst_bin_add (GST_BIN (comp), source2);
|
||||
/* Add operation */
|
||||
gst_bin_add (GST_BIN (comp), oper);
|
||||
/* Add default */
|
||||
gst_bin_add (GST_BIN (comp), def);
|
||||
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (source1, 0, 2 * GST_SECOND, 2 * GST_SECOND);
|
||||
check_start_stop_duration (oper, 0 * GST_SECOND, 6 * GST_SECOND,
|
||||
6 * GST_SECOND);
|
||||
check_start_stop_duration (source2, 4 * GST_SECOND, 6 * GST_SECOND,
|
||||
2 * GST_SECOND);
|
||||
check_start_stop_duration (comp, 0, 6 * GST_SECOND, 6 * GST_SECOND);
|
||||
|
||||
/* Expected segments */
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME, 0, 2 * GST_SECOND, 0));
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME,
|
||||
2 * GST_SECOND, 4 * GST_SECOND, 2 * GST_SECOND));
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME,
|
||||
4 * GST_SECOND, 6 * GST_SECOND, 4 * GST_SECOND));
|
||||
|
||||
fill_pipeline_and_check (comp, segments);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
|
||||
GST_START_TEST (test_complex_operations)
|
||||
{
|
||||
GstElement *comp, *oper, *source1, *source2;
|
||||
gboolean ret = FALSE;
|
||||
GList *segments = NULL;
|
||||
|
||||
comp =
|
||||
gst_element_factory_make_or_warn ("gnlcomposition", "test_composition");
|
||||
|
||||
/* TOPOLOGY
|
||||
*
|
||||
* 0 1 2 3 4 5 6 | Priority
|
||||
* ----------------------------------------------------------------------------
|
||||
* [ -oper- ] | 1
|
||||
* [ -source2- -] | 2
|
||||
* [ -source1- -] | 3
|
||||
* */
|
||||
|
||||
/*
|
||||
source1
|
||||
Start : 0s
|
||||
Duration : 4s
|
||||
Priority : 3
|
||||
*/
|
||||
|
||||
source1 = videotest_in_bin_gnl_src ("source1", 0, 4 * GST_SECOND, 2, 3);
|
||||
fail_if (source1 == NULL);
|
||||
|
||||
/*
|
||||
source2
|
||||
Start : 2s
|
||||
Duration : 4s
|
||||
Priority : 2
|
||||
*/
|
||||
|
||||
source2 =
|
||||
videotest_in_bin_gnl_src ("source2", 2 * GST_SECOND, 4 * GST_SECOND, 2,
|
||||
2);
|
||||
fail_if (source2 == NULL);
|
||||
|
||||
/*
|
||||
operation
|
||||
Start : 2s
|
||||
Duration : 2s
|
||||
Priority : 1
|
||||
*/
|
||||
|
||||
oper =
|
||||
new_operation ("oper", "videomixer", 2 * GST_SECOND, 2 * GST_SECOND, 1);
|
||||
fail_if (oper == NULL);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
|
||||
ASSERT_OBJECT_REFCOUNT (oper, "oper", 1);
|
||||
|
||||
/* Add source1 */
|
||||
gst_bin_add (GST_BIN (comp), source1);
|
||||
check_start_stop_duration (comp, 0, 0, 0);
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
|
||||
/* Add source2 */
|
||||
gst_bin_add (GST_BIN (comp), source2);
|
||||
check_start_stop_duration (comp, 0, 0, 0);
|
||||
ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
|
||||
|
||||
/* Add operaton */
|
||||
gst_bin_add (GST_BIN (comp), oper);
|
||||
check_start_stop_duration (comp, 0, 0, 0);
|
||||
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 6 * GST_SECOND, 6 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (oper, "oper", 1);
|
||||
|
||||
/* Expected segments */
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME, 0, 2 * GST_SECOND, 0));
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME,
|
||||
0 * GST_SECOND, 2 * GST_SECOND, 2 * GST_SECOND));
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME,
|
||||
4 * GST_SECOND, 6 * GST_SECOND, 4 * GST_SECOND));
|
||||
|
||||
fill_pipeline_and_check (comp, segments);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
|
||||
GST_START_TEST (test_complex_operations_bis)
|
||||
{
|
||||
GstElement *comp, *oper, *source1, *source2;
|
||||
gboolean ret;
|
||||
GList *segments = NULL;
|
||||
|
||||
comp =
|
||||
gst_element_factory_make_or_warn ("gnlcomposition", "test_composition");
|
||||
|
||||
/* TOPOLOGY
|
||||
*
|
||||
* 0 1 2 3 4 .. 6 | Priority
|
||||
* ----------------------------------------------------------------------------
|
||||
* [ ......................[------ oper ----------]..........] | 1 EXPANDABLE
|
||||
* [--------------------- source1 ----------------] | 2
|
||||
* [------------ source2 ------] | 3
|
||||
* */
|
||||
|
||||
|
||||
/*
|
||||
source1
|
||||
Start : 0s
|
||||
Duration : 4s
|
||||
Priority : 2
|
||||
*/
|
||||
|
||||
source1 = videotest_in_bin_gnl_src ("source1", 0, 4 * GST_SECOND, 3, 2);
|
||||
fail_if (source1 == NULL);
|
||||
|
||||
/*
|
||||
source2
|
||||
Start : 2s
|
||||
Duration : 4s
|
||||
Priority : 3
|
||||
*/
|
||||
|
||||
source2 =
|
||||
videotest_in_bin_gnl_src ("source2", 2 * GST_SECOND, 4 * GST_SECOND, 2,
|
||||
3);
|
||||
fail_if (source2 == NULL);
|
||||
|
||||
/*
|
||||
operation
|
||||
Start : 2s
|
||||
Duration : 2s
|
||||
Priority : 1
|
||||
EXPANDABLE
|
||||
*/
|
||||
|
||||
oper =
|
||||
new_operation ("oper", "videomixer", 2 * GST_SECOND, 2 * GST_SECOND, 1);
|
||||
fail_if (oper == NULL);
|
||||
g_object_set (oper, "expandable", TRUE, NULL);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
|
||||
ASSERT_OBJECT_REFCOUNT (oper, "oper", 1);
|
||||
|
||||
/* Add source1 */
|
||||
gst_bin_add (GST_BIN (comp), source1);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 4 * GST_SECOND, 4 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
|
||||
/* Add source2 */
|
||||
gst_bin_add (GST_BIN (comp), source2);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 6 * GST_SECOND, 6 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
|
||||
|
||||
/* Add operaton */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), oper);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 6 * GST_SECOND, 6 * GST_SECOND);
|
||||
/* Since it's expandable, it should have changed to full length */
|
||||
check_start_stop_duration (oper, 0 * GST_SECOND, 6 * GST_SECOND,
|
||||
6 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (oper, "oper", 1);
|
||||
|
||||
/* Expected segments */
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME, 0, 2 * GST_SECOND, 0));
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME,
|
||||
0 * GST_SECOND, 2 * GST_SECOND, 2 * GST_SECOND));
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME,
|
||||
0 * GST_SECOND, 2 * GST_SECOND, 4 * GST_SECOND));
|
||||
|
||||
fill_pipeline_and_check (comp, segments);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
|
||||
|
||||
static Suite *
|
||||
gnonlin_suite (void)
|
||||
{
|
||||
Suite *s = suite_create ("gnloperation");
|
||||
TCase *tc_chain = tcase_create ("gnloperation");
|
||||
|
||||
suite_add_tcase (s, tc_chain);
|
||||
|
||||
tcase_add_test (tc_chain, test_simple_operation);
|
||||
tcase_add_test (tc_chain, test_pyramid_operations);
|
||||
tcase_add_test (tc_chain, test_pyramid_operations2);
|
||||
tcase_add_test (tc_chain, test_pyramid_operations_expandable);
|
||||
if (gst_registry_check_feature_version (gst_registry_get (), "videomixer", 0,
|
||||
11, 0)) {
|
||||
tcase_add_test (tc_chain, test_complex_operations);
|
||||
tcase_add_test (tc_chain, test_complex_operations_bis);
|
||||
} else
|
||||
GST_WARNING ("videomixer element not available, skipping 1 test");
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
GST_CHECK_MAIN (gnonlin)
|
220
tests/check/gnl/gnlsource.c
Normal file
220
tests/check/gnl/gnlsource.c
Normal file
|
@ -0,0 +1,220 @@
|
|||
#include "common.h"
|
||||
|
||||
GST_START_TEST (test_simple_videotestsrc)
|
||||
{
|
||||
GstElement *pipeline;
|
||||
GstElement *gnlsource, *sink;
|
||||
CollectStructure *collect;
|
||||
GstBus *bus;
|
||||
GstMessage *message;
|
||||
gboolean carry_on = TRUE;
|
||||
GstPad *sinkpad;
|
||||
|
||||
pipeline = gst_pipeline_new ("test_pipeline");
|
||||
|
||||
/*
|
||||
Source 1
|
||||
Start : 1s
|
||||
Duration : 1s
|
||||
Priority : 1
|
||||
*/
|
||||
gnlsource =
|
||||
videotest_gnl_src ("source1", 1 * GST_SECOND, 1 * GST_SECOND, 2, 1);
|
||||
fail_if (gnlsource == NULL);
|
||||
check_start_stop_duration (gnlsource, 1 * GST_SECOND, 2 * GST_SECOND,
|
||||
1 * GST_SECOND);
|
||||
|
||||
sink = gst_element_factory_make_or_warn ("fakesink", "sink");
|
||||
fail_if (sink == NULL);
|
||||
|
||||
gst_bin_add_many (GST_BIN (pipeline), gnlsource, sink, NULL);
|
||||
|
||||
/* Shared data */
|
||||
collect = g_new0 (CollectStructure, 1);
|
||||
collect->comp = gnlsource;
|
||||
collect->sink = sink;
|
||||
|
||||
/* Expected segments */
|
||||
collect->expected_segments = g_list_append (collect->expected_segments,
|
||||
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);
|
||||
|
||||
sinkpad = gst_element_get_static_pad (sink, "sink");
|
||||
fail_if (sinkpad == NULL);
|
||||
gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
|
||||
(GstPadProbeCallback) sinkpad_probe, collect, NULL);
|
||||
|
||||
bus = gst_element_get_bus (pipeline);
|
||||
|
||||
GST_DEBUG ("Setting pipeline to PLAYING");
|
||||
ASSERT_OBJECT_REFCOUNT (gnlsource, "gnlsource", 1);
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
GST_DEBUG ("Let's poll the bus");
|
||||
|
||||
while (carry_on) {
|
||||
message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
|
||||
GST_LOG ("poll");
|
||||
if (message) {
|
||||
switch (GST_MESSAGE_TYPE (message)) {
|
||||
case GST_MESSAGE_EOS:
|
||||
/* we should check if we really finished here */
|
||||
GST_WARNING ("Got an EOS");
|
||||
carry_on = FALSE;
|
||||
break;
|
||||
case GST_MESSAGE_SEGMENT_START:
|
||||
case GST_MESSAGE_SEGMENT_DONE:
|
||||
/* We shouldn't see any segement messages, since we didn't do a segment seek */
|
||||
GST_WARNING ("Saw a Segment start/stop");
|
||||
fail_if (FALSE);
|
||||
carry_on = FALSE;
|
||||
break;
|
||||
case GST_MESSAGE_ERROR:
|
||||
fail_error_message (message);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
gst_mini_object_unref (GST_MINI_OBJECT (message));
|
||||
}
|
||||
}
|
||||
|
||||
GST_DEBUG ("Setting pipeline to NULL");
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
fail_if (collect->expected_segments != NULL);
|
||||
|
||||
gst_object_unref (GST_OBJECT (sinkpad));
|
||||
|
||||
GST_DEBUG ("Resetted pipeline to NULL");
|
||||
|
||||
gst_object_unref (pipeline);
|
||||
gst_object_unref (bus);
|
||||
|
||||
g_free (collect);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_videotestsrc_in_bin)
|
||||
{
|
||||
GstElement *pipeline;
|
||||
GstElement *gnlsource, *sink;
|
||||
CollectStructure *collect;
|
||||
GstBus *bus;
|
||||
GstMessage *message;
|
||||
gboolean carry_on = TRUE;
|
||||
GstPad *sinkpad;
|
||||
|
||||
pipeline = gst_pipeline_new ("test_pipeline");
|
||||
|
||||
/*
|
||||
Source 1
|
||||
Start : 1s
|
||||
Duration : 1s
|
||||
Priority : 1
|
||||
*/
|
||||
gnlsource = videotest_in_bin_gnl_src ("source1", 0, 1 * GST_SECOND, 2, 1);
|
||||
/* Handle systems which don't have alpha available */
|
||||
if (gnlsource == NULL)
|
||||
return;
|
||||
|
||||
sink = gst_element_factory_make_or_warn ("fakesink", "sink");
|
||||
fail_if (sink == NULL);
|
||||
|
||||
gst_bin_add_many (GST_BIN (pipeline), gnlsource, sink, NULL);
|
||||
|
||||
/* Shared data */
|
||||
collect = g_new0 (CollectStructure, 1);
|
||||
collect->comp = gnlsource;
|
||||
collect->sink = sink;
|
||||
|
||||
/* Expected segments */
|
||||
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);
|
||||
|
||||
sinkpad = gst_element_get_static_pad (sink, "sink");
|
||||
fail_if (sinkpad == NULL);
|
||||
gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
|
||||
(GstPadProbeCallback) sinkpad_probe, collect, NULL);
|
||||
|
||||
bus = gst_element_get_bus (pipeline);
|
||||
|
||||
GST_DEBUG ("Setting pipeline to PLAYING");
|
||||
ASSERT_OBJECT_REFCOUNT (gnlsource, "gnlsource", 1);
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
GST_DEBUG ("Let's poll the bus");
|
||||
|
||||
while (carry_on) {
|
||||
message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
|
||||
GST_LOG ("poll");
|
||||
if (message) {
|
||||
switch (GST_MESSAGE_TYPE (message)) {
|
||||
case GST_MESSAGE_EOS:
|
||||
/* we should check if we really finished here */
|
||||
GST_WARNING ("Got an EOS");
|
||||
carry_on = FALSE;
|
||||
break;
|
||||
case GST_MESSAGE_SEGMENT_START:
|
||||
case GST_MESSAGE_SEGMENT_DONE:
|
||||
/* We shouldn't see any segement messages, since we didn't do a segment seek */
|
||||
GST_WARNING ("Saw a Segment start/stop");
|
||||
fail_if (FALSE);
|
||||
carry_on = FALSE;
|
||||
break;
|
||||
case GST_MESSAGE_ERROR:
|
||||
fail_error_message (message);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
gst_mini_object_unref (GST_MINI_OBJECT (message));
|
||||
}
|
||||
}
|
||||
|
||||
GST_DEBUG ("Setting pipeline to NULL");
|
||||
|
||||
gst_object_unref (GST_OBJECT (sinkpad));
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
fail_if (collect->expected_segments != NULL);
|
||||
|
||||
GST_DEBUG ("Resetted pipeline to NULL");
|
||||
|
||||
gst_object_unref (pipeline);
|
||||
gst_object_unref (bus);
|
||||
|
||||
g_free (collect);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
static Suite *
|
||||
gnonlin_suite (void)
|
||||
{
|
||||
Suite *s = suite_create ("gnlsource");
|
||||
TCase *tc_chain = tcase_create ("gnlsource");
|
||||
|
||||
suite_add_tcase (s, tc_chain);
|
||||
|
||||
if (0)
|
||||
tcase_add_test (tc_chain, test_simple_videotestsrc);
|
||||
tcase_add_test (tc_chain, test_videotestsrc_in_bin);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
GST_CHECK_MAIN (gnonlin)
|
764
tests/check/gnl/seek.c
Normal file
764
tests/check/gnl/seek.c
Normal file
|
@ -0,0 +1,764 @@
|
|||
#include "common.h"
|
||||
|
||||
typedef struct _SeekInfo
|
||||
{
|
||||
GstClockTime position; /* Seek value and segment position */
|
||||
GstClockTime start; /* Segment start */
|
||||
GstClockTime stop; /* Segment stop */
|
||||
gboolean expect_failure; /* Whether we expect the seek to fail or not */
|
||||
} SeekInfo;
|
||||
|
||||
static SeekInfo *
|
||||
new_seek_info (GstClockTime position, GstClockTime start, GstClockTime stop,
|
||||
gboolean expect_failure)
|
||||
{
|
||||
SeekInfo *info = g_new0 (SeekInfo, 1);
|
||||
|
||||
info->position = position;
|
||||
info->start = start;
|
||||
info->stop = stop;
|
||||
info->expect_failure = expect_failure;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
static void
|
||||
fill_pipeline_and_check (GstElement * comp, GList * segments, GList * seeks)
|
||||
{
|
||||
GstElement *pipeline, *sink;
|
||||
CollectStructure *collect;
|
||||
GstBus *bus;
|
||||
GstMessage *message;
|
||||
gboolean carry_on = TRUE, expected_failure;
|
||||
GstPad *sinkpad;
|
||||
GList *ltofree = seeks;
|
||||
|
||||
pipeline = gst_pipeline_new ("test_pipeline");
|
||||
sink = gst_element_factory_make_or_warn ("fakesink", "sink");
|
||||
fail_if (sink == NULL);
|
||||
|
||||
gst_bin_add_many (GST_BIN (pipeline), comp, sink, NULL);
|
||||
|
||||
/* Shared data */
|
||||
collect = g_new0 (CollectStructure, 1);
|
||||
collect->comp = comp;
|
||||
collect->sink = sink;
|
||||
|
||||
/* Expected segments */
|
||||
collect->expected_segments = segments;
|
||||
collect->keep_expected_segments = TRUE;
|
||||
|
||||
g_signal_connect (G_OBJECT (comp), "pad-added",
|
||||
G_CALLBACK (composition_pad_added_cb), collect);
|
||||
|
||||
sinkpad = gst_element_get_static_pad (sink, "sink");
|
||||
gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
|
||||
(GstPadProbeCallback) sinkpad_probe, collect, NULL);
|
||||
|
||||
bus = gst_element_get_bus (GST_ELEMENT (pipeline));
|
||||
|
||||
GST_DEBUG ("Setting pipeline to PLAYING");
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
GST_DEBUG ("Let's poll the bus");
|
||||
|
||||
while (carry_on) {
|
||||
message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
|
||||
if (message) {
|
||||
switch (GST_MESSAGE_TYPE (message)) {
|
||||
case GST_MESSAGE_EOS:
|
||||
/* we should check if we really finished here */
|
||||
GST_WARNING ("Got an EOS");
|
||||
carry_on = FALSE;
|
||||
break;
|
||||
case GST_MESSAGE_SEGMENT_START:
|
||||
case GST_MESSAGE_SEGMENT_DONE:
|
||||
/* We shouldn't see any segement messages, since we didn't do a segment seek */
|
||||
GST_WARNING ("Saw a Segment start/stop");
|
||||
fail_if (TRUE);
|
||||
break;
|
||||
case GST_MESSAGE_ERROR:
|
||||
fail_error_message (message);
|
||||
break;
|
||||
case GST_MESSAGE_ASYNC_DONE:
|
||||
GST_DEBUG ("prerolling done");
|
||||
|
||||
if (seeks == NULL) {
|
||||
carry_on = FALSE;
|
||||
g_list_free_full (collect->expected_segments, g_free);
|
||||
collect->expected_segments = NULL;
|
||||
GST_DEBUG ("Done seeking");
|
||||
break;
|
||||
}
|
||||
|
||||
g_list_free_full (collect->expected_segments, g_free);
|
||||
collect->expected_segments = NULL;
|
||||
expected_failure = TRUE;
|
||||
while (expected_failure && carry_on) {
|
||||
SeekInfo *sinfo = (SeekInfo *) seeks->data;
|
||||
|
||||
seeks = seeks->next;
|
||||
|
||||
if (!sinfo->expect_failure) {
|
||||
collect->gotsegment = FALSE;
|
||||
collect->expected_base = 0;
|
||||
collect->expected_segments =
|
||||
g_list_append (collect->expected_segments, segment_new (1.0,
|
||||
GST_FORMAT_TIME, sinfo->start, sinfo->stop,
|
||||
sinfo->position));
|
||||
|
||||
expected_failure = FALSE;
|
||||
}
|
||||
|
||||
GST_DEBUG ("Seeking to %" GST_TIME_FORMAT ", Expecting (%"
|
||||
GST_TIME_FORMAT " %" GST_TIME_FORMAT ")",
|
||||
GST_TIME_ARGS (sinfo->position), GST_TIME_ARGS (sinfo->start),
|
||||
GST_TIME_ARGS (sinfo->stop));
|
||||
|
||||
fail_unless_equals_int (gst_element_seek_simple (pipeline,
|
||||
GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, sinfo->position),
|
||||
!sinfo->expect_failure);
|
||||
|
||||
if (!sinfo->expect_failure) {
|
||||
g_free (sinfo);
|
||||
break;
|
||||
}
|
||||
|
||||
if (seeks == NULL)
|
||||
carry_on = FALSE;
|
||||
g_free (sinfo);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
gst_mini_object_unref (GST_MINI_OBJECT (message));
|
||||
}
|
||||
}
|
||||
|
||||
GST_DEBUG ("Setting pipeline to READY");
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_READY) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
fail_if (collect->expected_segments != NULL);
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT_BETWEEN (pipeline, "main pipeline", 1, 2);
|
||||
gst_object_unref (pipeline);
|
||||
ASSERT_OBJECT_REFCOUNT_BETWEEN (bus, "main bus", 1, 2);
|
||||
gst_object_unref (bus);
|
||||
|
||||
g_list_free (ltofree);
|
||||
g_free (collect);
|
||||
}
|
||||
|
||||
static void
|
||||
test_simplest_full (void)
|
||||
{
|
||||
gboolean ret;
|
||||
GstElement *comp, *source1;
|
||||
GList *segments = NULL;
|
||||
GList *seeks = NULL;
|
||||
|
||||
comp =
|
||||
gst_element_factory_make_or_warn ("gnlcomposition", "test_composition");
|
||||
fail_if (comp == NULL);
|
||||
|
||||
/*
|
||||
Source 1
|
||||
Start : 0s
|
||||
Duration : 1s
|
||||
Media start : 5s
|
||||
Priority : 1
|
||||
*/
|
||||
source1 =
|
||||
videotest_gnl_src_full ("source1", 0, 1 * GST_SECOND, 5 * GST_SECOND, 3,
|
||||
1);
|
||||
fail_if (source1 == NULL);
|
||||
check_start_stop_duration (source1, 0, 1 * GST_SECOND, 1 * GST_SECOND);
|
||||
|
||||
/* Add one source */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source1);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 1 * GST_SECOND, 1 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
|
||||
/* Expected segments */
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME, 5 * GST_SECOND, 6 * GST_SECOND, 0));
|
||||
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (0.5 * GST_SECOND, 5.5 * GST_SECOND,
|
||||
6 * GST_SECOND, FALSE));
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (0 * GST_SECOND, 5 * GST_SECOND,
|
||||
6 * GST_SECOND, FALSE));
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (GST_SECOND - 1, 6 * GST_SECOND - 1,
|
||||
6 * GST_SECOND, FALSE));
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (GST_SECOND, 6 * GST_SECOND,
|
||||
6 * GST_SECOND, TRUE));
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (0.5 * GST_SECOND, 5.5 * GST_SECOND,
|
||||
6 * GST_SECOND, FALSE));
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (0 * GST_SECOND, 5 * GST_SECOND,
|
||||
6 * GST_SECOND, FALSE));
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (GST_SECOND - 1, 6 * GST_SECOND - 1,
|
||||
6 * GST_SECOND, FALSE));
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (GST_SECOND, 6 * GST_SECOND,
|
||||
6 * GST_SECOND, TRUE));
|
||||
|
||||
fill_pipeline_and_check (comp, segments, seeks);
|
||||
}
|
||||
|
||||
static void
|
||||
test_one_after_other_full (void)
|
||||
{
|
||||
gboolean ret;
|
||||
GstElement *comp, *source1, *source2;
|
||||
GList *segments = NULL, *seeks = NULL;
|
||||
|
||||
comp =
|
||||
gst_element_factory_make_or_warn ("gnlcomposition", "test_composition");
|
||||
fail_if (comp == NULL);
|
||||
|
||||
/* TOPOLOGY
|
||||
*
|
||||
* 0 1 2 3 4 5 | Priority
|
||||
* ----------------------------------------------------------------------------
|
||||
* [5 source1 ][2 source2 ] | 1
|
||||
*
|
||||
* */
|
||||
|
||||
/*
|
||||
Source 1
|
||||
Start : 0s
|
||||
Duration : 1s
|
||||
Media start : 5s
|
||||
Priority : 1
|
||||
*/
|
||||
source1 =
|
||||
videotest_gnl_src_full ("source1", 0, 1 * GST_SECOND, 5 * GST_SECOND, 3,
|
||||
1);
|
||||
fail_if (source1 == NULL);
|
||||
check_start_stop_duration (source1, 0, 1 * GST_SECOND, 1 * GST_SECOND);
|
||||
|
||||
/*
|
||||
Source 2
|
||||
Start : 1s
|
||||
Duration : 1s
|
||||
Media start : 2s
|
||||
Priority : 1
|
||||
*/
|
||||
source2 = videotest_gnl_src_full ("source2", 1 * GST_SECOND, 1 * GST_SECOND,
|
||||
2 * GST_SECOND, 2, 1);
|
||||
fail_if (source2 == NULL);
|
||||
check_start_stop_duration (source2, 1 * GST_SECOND, 2 * GST_SECOND,
|
||||
1 * GST_SECOND);
|
||||
|
||||
/* Add sources */
|
||||
gst_bin_add (GST_BIN (comp), source1);
|
||||
gst_bin_add (GST_BIN (comp), source2);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (source1, 0, 1 * GST_SECOND, 1 * GST_SECOND);
|
||||
check_start_stop_duration (source2, 1 * GST_SECOND, 2 * GST_SECOND,
|
||||
1 * GST_SECOND);
|
||||
check_start_stop_duration (comp, 0, 2 * GST_SECOND, 2 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
|
||||
|
||||
|
||||
/* Expected segments */
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME, 5 * GST_SECOND, 6 * GST_SECOND, 0));
|
||||
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (0.5 * GST_SECOND, 5.5 * GST_SECOND,
|
||||
6 * GST_SECOND, FALSE));
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (0 * GST_SECOND, 5 * GST_SECOND,
|
||||
6 * GST_SECOND, FALSE));
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (GST_SECOND - 1, 6 * GST_SECOND - 1,
|
||||
6 * GST_SECOND, FALSE));
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (GST_SECOND, 2 * GST_SECOND,
|
||||
3 * GST_SECOND, FALSE));
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (2 * GST_SECOND - 1,
|
||||
3 * GST_SECOND - 1, 3 * GST_SECOND, FALSE));
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (2 * GST_SECOND, 3 * GST_SECOND,
|
||||
3 * GST_SECOND, TRUE));
|
||||
|
||||
|
||||
fill_pipeline_and_check (comp, segments, seeks);
|
||||
}
|
||||
|
||||
static void
|
||||
test_one_under_another_full (void)
|
||||
{
|
||||
gboolean ret;
|
||||
GstElement *comp, *source1, *source2;
|
||||
GList *segments = NULL, *seeks = NULL;
|
||||
|
||||
comp =
|
||||
gst_element_factory_make_or_warn ("gnlcomposition", "test_composition");
|
||||
fail_if (comp == NULL);
|
||||
|
||||
/* TOPOLOGY
|
||||
*
|
||||
* 0 1 2 3 4 5 | Priority
|
||||
* ----------------------------------------------------------------------------
|
||||
* [ source1 ] | 1
|
||||
* [ source2 ] | 2
|
||||
*
|
||||
* */
|
||||
|
||||
/*
|
||||
Source 1
|
||||
Start : 0s
|
||||
Duration : 2s
|
||||
Priority : 1
|
||||
*/
|
||||
source1 = videotest_gnl_src ("source1", 0, 2 * GST_SECOND, 3, 1);
|
||||
fail_if (source1 == NULL);
|
||||
check_start_stop_duration (source1, 0, 2 * GST_SECOND, 2 * GST_SECOND);
|
||||
|
||||
/*
|
||||
Source 2
|
||||
Start : 1s
|
||||
Duration : 2s
|
||||
Priority : 2
|
||||
*/
|
||||
source2 = videotest_gnl_src ("source2", 1 * GST_SECOND, 2 * GST_SECOND, 2, 2);
|
||||
fail_if (source2 == NULL);
|
||||
check_start_stop_duration (source2, 1 * GST_SECOND, 3 * GST_SECOND,
|
||||
2 * GST_SECOND);
|
||||
|
||||
/* Add two sources */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source1);
|
||||
gst_bin_add (GST_BIN (comp), source2);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (source1, 0, 2 * GST_SECOND, 2 * GST_SECOND);
|
||||
check_start_stop_duration (source2, 1 * GST_SECOND, 3 * GST_SECOND,
|
||||
2 * GST_SECOND);
|
||||
check_start_stop_duration (comp, 0, 3 * GST_SECOND, 3 * GST_SECOND);
|
||||
|
||||
/* Expected segments */
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME, 0, GST_SECOND, 0));
|
||||
|
||||
|
||||
/* Hit source1 */
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (0.5 * GST_SECOND, 0.5 * GST_SECOND,
|
||||
1 * GST_SECOND, FALSE));
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (0 * GST_SECOND, 0 * GST_SECOND,
|
||||
1 * GST_SECOND, FALSE));
|
||||
/* Hit source1 over source2 */
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (1 * GST_SECOND, 1 * GST_SECOND,
|
||||
2 * GST_SECOND, FALSE));
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (1.5 * GST_SECOND, 1.5 * GST_SECOND,
|
||||
2 * GST_SECOND, FALSE));
|
||||
/* Hit source2 */
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (2 * GST_SECOND, 2 * GST_SECOND,
|
||||
3 * GST_SECOND, FALSE));
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (2.5 * GST_SECOND, 2.5 * GST_SECOND,
|
||||
3 * GST_SECOND, FALSE));
|
||||
|
||||
fill_pipeline_and_check (comp, segments, seeks);
|
||||
}
|
||||
|
||||
static void
|
||||
test_one_bin_after_other_full (void)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
GstElement *comp, *source1, *source2;
|
||||
GList *segments = NULL, *seeks = NULL;
|
||||
|
||||
comp =
|
||||
gst_element_factory_make_or_warn ("gnlcomposition", "test_composition");
|
||||
fail_if (comp == NULL);
|
||||
|
||||
/*
|
||||
Source 1
|
||||
Start : 0s
|
||||
Duration : 1s
|
||||
Priority : 1
|
||||
*/
|
||||
source1 = videotest_in_bin_gnl_src ("source1", 0, 1 * GST_SECOND, 3, 1);
|
||||
fail_if (source1 == NULL);
|
||||
check_start_stop_duration (source1, 0, 1 * GST_SECOND, 1 * GST_SECOND);
|
||||
|
||||
/*
|
||||
Source 2
|
||||
Start : 1s
|
||||
Duration : 1s
|
||||
Priority : 1
|
||||
*/
|
||||
source2 =
|
||||
videotest_in_bin_gnl_src ("source2", 1 * GST_SECOND, 1 * GST_SECOND, 2,
|
||||
1);
|
||||
fail_if (source2 == NULL);
|
||||
|
||||
/* Add one source */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source1);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
|
||||
/* Second source */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source2);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (source1, 0, 1 * GST_SECOND, 1 * GST_SECOND);
|
||||
check_start_stop_duration (source2, 1 * GST_SECOND, 2 * GST_SECOND,
|
||||
1 * GST_SECOND);
|
||||
check_start_stop_duration (comp, 0, 2 * GST_SECOND, 2 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
|
||||
|
||||
/* Expected segments */
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME, 0, 1 * GST_SECOND, 0));
|
||||
|
||||
/* Hit source1 */
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (0.5 * GST_SECOND, 0.5 * GST_SECOND,
|
||||
GST_SECOND, FALSE));
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (0 * GST_SECOND, 0 * GST_SECOND,
|
||||
GST_SECOND, FALSE));
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (GST_SECOND - 1, GST_SECOND - 1,
|
||||
GST_SECOND, FALSE));
|
||||
/* Hit source2 */
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (1.5 * GST_SECOND, 1.5 * GST_SECOND,
|
||||
2 * GST_SECOND, FALSE));
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (GST_SECOND, GST_SECOND,
|
||||
2 * GST_SECOND, FALSE));
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (2 * GST_SECOND - 1,
|
||||
2 * GST_SECOND - 1, 2 * GST_SECOND, FALSE));
|
||||
/* Should fail */
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (2 * GST_SECOND, GST_SECOND,
|
||||
GST_SECOND, TRUE));
|
||||
|
||||
fill_pipeline_and_check (comp, segments, seeks);
|
||||
}
|
||||
|
||||
|
||||
GST_START_TEST (test_complex_operations)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
GstElement *comp, *oper, *source1, *source2;
|
||||
GList *segments = NULL, *seeks = NULL;
|
||||
|
||||
comp =
|
||||
gst_element_factory_make_or_warn ("gnlcomposition", "test_composition");
|
||||
|
||||
/* TOPOLOGY
|
||||
*
|
||||
* 0 1 2 3 4 .. 6 | Priority
|
||||
* ----------------------------------------------------------------------------
|
||||
* [------ oper ----------] | 1
|
||||
* [--------------------- source1 ----------------] | 2
|
||||
* [------------ source2 ------] | 3
|
||||
* */
|
||||
|
||||
/*
|
||||
source1
|
||||
Start : 0s
|
||||
Duration : 4s
|
||||
Priority : 3
|
||||
*/
|
||||
|
||||
source1 = videotest_in_bin_gnl_src ("source1", 0, 4 * GST_SECOND, 2, 3);
|
||||
fail_if (source1 == NULL);
|
||||
check_start_stop_duration (source1, 0, 4 * GST_SECOND, 4 * GST_SECOND);
|
||||
|
||||
/*
|
||||
source2
|
||||
Start : 2s
|
||||
Duration : 4s
|
||||
Priority : 2
|
||||
*/
|
||||
|
||||
source2 =
|
||||
videotest_in_bin_gnl_src ("source2", 2 * GST_SECOND, 4 * GST_SECOND, 2,
|
||||
2);
|
||||
fail_if (source2 == NULL);
|
||||
check_start_stop_duration (source2, 2 * GST_SECOND, 6 * GST_SECOND,
|
||||
4 * GST_SECOND);
|
||||
|
||||
/*
|
||||
operation
|
||||
Start : 2s
|
||||
Duration : 2s
|
||||
Priority : 1
|
||||
*/
|
||||
|
||||
oper =
|
||||
new_operation ("oper", "videomixer", 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);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
|
||||
ASSERT_OBJECT_REFCOUNT (oper, "oper", 1);
|
||||
|
||||
/* Add source1 */
|
||||
gst_bin_add (GST_BIN (comp), source1);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 4 * GST_SECOND, 4 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
|
||||
/* Add source2 */
|
||||
gst_bin_add (GST_BIN (comp), source2);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 6 * GST_SECOND, 6 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
|
||||
|
||||
/* Add operaton */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), oper);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 6 * GST_SECOND, 6 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (oper, "oper", 1);
|
||||
|
||||
/* Expected segments */
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME, 0, 2 * GST_SECOND, 0));
|
||||
|
||||
/* Seeks */
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (0.5 * GST_SECOND, 0.5 * GST_SECOND,
|
||||
2 * GST_SECOND, FALSE));
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (2.5 * GST_SECOND, 0 * GST_SECOND,
|
||||
1.5 * GST_SECOND, FALSE));
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (4.5 * GST_SECOND, 4.5 * GST_SECOND,
|
||||
6 * GST_SECOND, FALSE));
|
||||
/* and backwards */
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (2.5 * GST_SECOND, 0 * GST_SECOND,
|
||||
1.5 * GST_SECOND, FALSE));
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (0.5 * GST_SECOND, 0.5 * GST_SECOND,
|
||||
2 * GST_SECOND, FALSE));
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (2.5 * GST_SECOND, 0 * GST_SECOND,
|
||||
1.5 * GST_SECOND, FALSE));
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (4.5 * GST_SECOND, 4.5 * GST_SECOND,
|
||||
6 * GST_SECOND, FALSE));
|
||||
|
||||
fill_pipeline_and_check (comp, segments, seeks);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
|
||||
GST_START_TEST (test_complex_operations_bis)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
GstElement *comp, *oper, *source1, *source2;
|
||||
GList *segments = NULL, *seeks = NULL;
|
||||
|
||||
comp =
|
||||
gst_element_factory_make_or_warn ("gnlcomposition", "test_composition");
|
||||
|
||||
/* TOPOLOGY
|
||||
*
|
||||
* 0 1 2 3 4 .. 6 | Priority
|
||||
* ----------------------------------------------------------------------------
|
||||
* [ ......................[------ oper ----------]..........] | 1 EXPANDABLE
|
||||
* [--------------------- source1 ----------------] | 2
|
||||
* [------------ source2 ------] | 3
|
||||
* */
|
||||
|
||||
|
||||
/*
|
||||
source1
|
||||
Start : 0s
|
||||
Duration : 4s
|
||||
Priority : 2
|
||||
*/
|
||||
|
||||
source1 = videotest_in_bin_gnl_src ("source1", 0, 4 * GST_SECOND, 3, 2);
|
||||
fail_if (source1 == NULL);
|
||||
check_start_stop_duration (source1, 0, 4 * GST_SECOND, 4 * GST_SECOND);
|
||||
|
||||
/*
|
||||
source2
|
||||
Start : 2s
|
||||
Duration : 4s
|
||||
Priority : 3
|
||||
*/
|
||||
|
||||
source2 =
|
||||
videotest_in_bin_gnl_src ("source2", 2 * GST_SECOND, 4 * GST_SECOND, 2,
|
||||
3);
|
||||
fail_if (source2 == NULL);
|
||||
check_start_stop_duration (source2, 2 * GST_SECOND, 6 * GST_SECOND,
|
||||
4 * GST_SECOND);
|
||||
|
||||
/*
|
||||
operation
|
||||
Start : 2s
|
||||
Duration : 2s
|
||||
Priority : 1
|
||||
EXPANDABLE
|
||||
*/
|
||||
|
||||
oper =
|
||||
new_operation ("oper", "videomixer", 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);
|
||||
g_object_set (oper, "expandable", TRUE, NULL);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
|
||||
ASSERT_OBJECT_REFCOUNT (oper, "oper", 1);
|
||||
|
||||
/* Add source1 */
|
||||
gst_bin_add (GST_BIN (comp), source1);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 4 * GST_SECOND, 4 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
|
||||
/* Add source2 */
|
||||
gst_bin_add (GST_BIN (comp), source2);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 6 * GST_SECOND, 6 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
|
||||
|
||||
/* Add operaton */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), oper);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (source1, 0, 4 * GST_SECOND, 4 * GST_SECOND);
|
||||
check_start_stop_duration (source2, 2 * GST_SECOND, 6 * GST_SECOND,
|
||||
4 * GST_SECOND);
|
||||
check_start_stop_duration (comp, 0, 6 * GST_SECOND, 6 * GST_SECOND);
|
||||
check_start_stop_duration (oper, 0 * GST_SECOND, 6 * GST_SECOND,
|
||||
6 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (oper, "oper", 1);
|
||||
|
||||
/* Expected segments */
|
||||
segments = g_list_append (segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME, 0, 2 * GST_SECOND, 0));
|
||||
|
||||
/* Seeks */
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (0.5 * GST_SECOND, 0 * GST_SECOND,
|
||||
1.5 * GST_SECOND, FALSE));
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (2.5 * GST_SECOND, 0 * GST_SECOND,
|
||||
1.5 * GST_SECOND, FALSE));
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (4.5 * GST_SECOND, 0 * GST_SECOND,
|
||||
1.5 * GST_SECOND, FALSE));
|
||||
/* and backwards */
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (2.5 * GST_SECOND, 0 * GST_SECOND,
|
||||
1.5 * GST_SECOND, FALSE));
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (0.5 * GST_SECOND, 0 * GST_SECOND,
|
||||
1.5 * GST_SECOND, FALSE));
|
||||
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (2.5 * GST_SECOND, 0 * GST_SECOND,
|
||||
1.5 * GST_SECOND, FALSE));
|
||||
seeks =
|
||||
g_list_append (seeks, new_seek_info (4.5 * GST_SECOND, 0 * GST_SECOND,
|
||||
1.5 * GST_SECOND, FALSE));
|
||||
|
||||
fill_pipeline_and_check (comp, segments, seeks);
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
|
||||
GST_START_TEST (test_simplest)
|
||||
{
|
||||
test_simplest_full ();
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
|
||||
GST_START_TEST (test_one_after_other)
|
||||
{
|
||||
test_one_after_other_full ();
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
|
||||
GST_START_TEST (test_one_under_another)
|
||||
{
|
||||
test_one_under_another_full ();
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
|
||||
GST_START_TEST (test_one_bin_after_other)
|
||||
{
|
||||
test_one_bin_after_other_full ();
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
|
||||
static Suite *
|
||||
gnonlin_suite (void)
|
||||
{
|
||||
Suite *s = suite_create ("gnonlin-seek");
|
||||
TCase *tc_chain = tcase_create ("general");
|
||||
|
||||
suite_add_tcase (s, tc_chain);
|
||||
|
||||
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);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
GST_CHECK_MAIN (gnonlin)
|
791
tests/check/gnl/simple.c
Normal file
791
tests/check/gnl/simple.c
Normal file
|
@ -0,0 +1,791 @@
|
|||
#include "common.h"
|
||||
|
||||
static void
|
||||
test_simplest_full (void)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
GstElement *pipeline;
|
||||
GstElement *comp, *sink, *source1;
|
||||
CollectStructure *collect;
|
||||
GstBus *bus;
|
||||
GstPad *sinkpad;
|
||||
|
||||
pipeline = gst_pipeline_new ("test_pipeline");
|
||||
comp =
|
||||
gst_element_factory_make_or_warn ("gnlcomposition", "test_composition");
|
||||
fail_if (comp == NULL);
|
||||
|
||||
/*
|
||||
Source 1
|
||||
Start : 0s
|
||||
Duration : 1s
|
||||
Media start : 5s
|
||||
Media Duartion : 1s
|
||||
Priority : 1
|
||||
*/
|
||||
source1 =
|
||||
videotest_gnl_src_full ("source1", 0, 1 * GST_SECOND, 5 * GST_SECOND, 3,
|
||||
1);
|
||||
fail_if (source1 == NULL);
|
||||
check_start_stop_duration (source1, 0, 1 * GST_SECOND, 1 * GST_SECOND);
|
||||
|
||||
/* Add one source */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source1);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
fail_unless (ret);
|
||||
check_start_stop_duration (source1, 0, 1 * GST_SECOND, 1 * GST_SECOND);
|
||||
check_start_stop_duration (comp, 0, 1 * GST_SECOND, 1 * GST_SECOND);
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
|
||||
sink = gst_element_factory_make_or_warn ("fakesink", "sink");
|
||||
fail_if (sink == NULL);
|
||||
|
||||
gst_bin_add_many (GST_BIN (pipeline), comp, sink, NULL);
|
||||
|
||||
/* Shared data */
|
||||
collect = g_new0 (CollectStructure, 1);
|
||||
collect->comp = comp;
|
||||
collect->sink = sink;
|
||||
|
||||
/* Expected segments */
|
||||
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);
|
||||
|
||||
sinkpad = gst_element_get_static_pad (sink, "sink");
|
||||
gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
|
||||
(GstPadProbeCallback) sinkpad_probe, collect, NULL);
|
||||
|
||||
bus = gst_element_get_bus (GST_ELEMENT (pipeline));
|
||||
|
||||
GST_DEBUG ("Setting pipeline to PLAYING");
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
GST_DEBUG ("Let's poll the bus");
|
||||
|
||||
poll_the_bus (bus);
|
||||
|
||||
GST_DEBUG ("Setting pipeline to NULL");
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_READY) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
fail_if (collect->expected_segments != NULL);
|
||||
|
||||
GST_DEBUG ("Resetted pipeline to READY");
|
||||
|
||||
/* Expected segments */
|
||||
collect->expected_segments = g_list_append (collect->expected_segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME, 5 * GST_SECOND, 6 * GST_SECOND, 0));
|
||||
collect->expected_base = 0;
|
||||
collect->gotsegment = FALSE;
|
||||
|
||||
GST_DEBUG ("Setting pipeline to PLAYING again");
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
GST_DEBUG ("Let's poll the bus AGAIN");
|
||||
|
||||
poll_the_bus (bus);
|
||||
|
||||
fail_if (collect->expected_segments != NULL);
|
||||
|
||||
gst_object_unref (GST_OBJECT (sinkpad));
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT_BETWEEN (pipeline, "main pipeline", 1, 2);
|
||||
gst_object_unref (pipeline);
|
||||
ASSERT_OBJECT_REFCOUNT_BETWEEN (bus, "main bus", 1, 2);
|
||||
gst_object_unref (bus);
|
||||
|
||||
g_free (collect);
|
||||
}
|
||||
|
||||
static void
|
||||
test_time_duration_full (void)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
GstElement *comp, *source1, *source2;
|
||||
|
||||
comp =
|
||||
gst_element_factory_make_or_warn ("gnlcomposition", "test_composition");
|
||||
|
||||
/*
|
||||
Source 1
|
||||
Start : 0s
|
||||
Duration : 1s
|
||||
Priority : 1
|
||||
*/
|
||||
source1 = videotest_gnl_src ("source1", 0, 1 * GST_SECOND, 3, 1);
|
||||
fail_if (source1 == NULL);
|
||||
check_start_stop_duration (source1, 0, 1 * GST_SECOND, 1 * GST_SECOND);
|
||||
|
||||
/*
|
||||
Source 2
|
||||
Start : 1s
|
||||
Duration : 1s
|
||||
Priority : 1
|
||||
*/
|
||||
source2 = videotest_gnl_src ("source2", 1 * GST_SECOND, 1 * GST_SECOND, 2, 1);
|
||||
fail_if (source2 == NULL);
|
||||
check_start_stop_duration (source2, 1 * GST_SECOND, 2 * GST_SECOND,
|
||||
1 * GST_SECOND);
|
||||
|
||||
/* Add one source */
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source1);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
fail_unless (ret == TRUE);
|
||||
check_start_stop_duration (comp, 0, 1 * GST_SECOND, 1 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
|
||||
/* Second source */
|
||||
|
||||
ret = FALSE;
|
||||
gst_bin_add (GST_BIN (comp), source2);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
fail_unless (ret == TRUE);
|
||||
check_start_stop_duration (comp, 0, 2 * GST_SECOND, 2 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
|
||||
|
||||
/* Remove first source */
|
||||
|
||||
gst_object_ref (source1);
|
||||
gst_bin_remove (GST_BIN (comp), source1);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 1 * GST_SECOND, 2 * GST_SECOND,
|
||||
1 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
|
||||
/* Re-add first source */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source1);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 2 * GST_SECOND, 2 * GST_SECOND);
|
||||
gst_object_unref (source1);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
|
||||
gst_object_unref (comp);
|
||||
}
|
||||
|
||||
static void
|
||||
test_one_after_other_full (void)
|
||||
{
|
||||
GstElement *pipeline;
|
||||
GstElement *comp, *sink, *source1, *source2;
|
||||
CollectStructure *collect;
|
||||
GstBus *bus;
|
||||
GstMessage *message;
|
||||
gboolean carry_on = TRUE;
|
||||
GstPad *sinkpad;
|
||||
|
||||
gboolean ret = FALSE;
|
||||
|
||||
pipeline = gst_pipeline_new ("test_pipeline");
|
||||
comp =
|
||||
gst_element_factory_make_or_warn ("gnlcomposition", "test_composition");
|
||||
fail_if (comp == NULL);
|
||||
|
||||
/*
|
||||
Source 1
|
||||
Start : 0s
|
||||
Duration : 1s
|
||||
Media start : 5s
|
||||
Priority : 1
|
||||
*/
|
||||
source1 =
|
||||
videotest_gnl_src_full ("source1", 0, 1 * GST_SECOND, 5 * GST_SECOND, 3,
|
||||
1);
|
||||
fail_if (source1 == NULL);
|
||||
check_start_stop_duration (source1, 0, 1 * GST_SECOND, 1 * GST_SECOND);
|
||||
|
||||
/*
|
||||
Source 2
|
||||
Start : 1s
|
||||
Duration : 1s
|
||||
Media start : 2s
|
||||
Priority : 1
|
||||
*/
|
||||
source2 = videotest_gnl_src_full ("source2", 1 * GST_SECOND, 1 * GST_SECOND,
|
||||
2 * GST_SECOND, 2, 1);
|
||||
fail_if (source2 == NULL);
|
||||
check_start_stop_duration (source2, 1 * GST_SECOND, 2 * GST_SECOND,
|
||||
1 * GST_SECOND);
|
||||
|
||||
/* Add one source */
|
||||
gst_bin_add (GST_BIN (comp), source1);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 1 * GST_SECOND, 1 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
|
||||
/* Second source */
|
||||
gst_bin_add (GST_BIN (comp), source2);
|
||||
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
fail_unless (ret);
|
||||
check_start_stop_duration (source1, 0 * GST_SECOND, 1 * GST_SECOND,
|
||||
1 * GST_SECOND);
|
||||
check_start_stop_duration (source2, 1 * GST_SECOND, 2 * GST_SECOND,
|
||||
1 * GST_SECOND);
|
||||
check_start_stop_duration (comp, 0, 2 * GST_SECOND, 2 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
|
||||
|
||||
/* Remove first source */
|
||||
|
||||
gst_object_ref (source1);
|
||||
gst_bin_remove (GST_BIN (comp), source1);
|
||||
check_start_stop_duration (comp, 1 * GST_SECOND, 2 * GST_SECOND,
|
||||
1 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
|
||||
/* Re-add first source */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source1);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 2 * GST_SECOND, 2 * GST_SECOND);
|
||||
gst_object_unref (source1);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
|
||||
sink = gst_element_factory_make_or_warn ("fakesink", "sink");
|
||||
fail_if (sink == NULL);
|
||||
|
||||
gst_bin_add_many (GST_BIN (pipeline), comp, sink, NULL);
|
||||
|
||||
/* Shared data */
|
||||
collect = g_new0 (CollectStructure, 1);
|
||||
collect->comp = comp;
|
||||
collect->sink = sink;
|
||||
|
||||
/* Expected segments */
|
||||
collect->expected_segments = g_list_append (collect->expected_segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME, 5 * GST_SECOND, 6 * GST_SECOND, 0));
|
||||
collect->expected_segments = g_list_append (collect->expected_segments,
|
||||
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);
|
||||
|
||||
sinkpad = gst_element_get_static_pad (sink, "sink");
|
||||
gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
|
||||
(GstPadProbeCallback) sinkpad_probe, collect, NULL);
|
||||
|
||||
bus = gst_element_get_bus (GST_ELEMENT (pipeline));
|
||||
|
||||
GST_DEBUG ("Setting pipeline to PLAYING");
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
GST_DEBUG ("Let's poll the bus");
|
||||
|
||||
while (carry_on) {
|
||||
message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
|
||||
if (message) {
|
||||
switch (GST_MESSAGE_TYPE (message)) {
|
||||
case GST_MESSAGE_EOS:
|
||||
/* we should check if we really finished here */
|
||||
GST_WARNING ("Got an EOS");
|
||||
carry_on = FALSE;
|
||||
break;
|
||||
case GST_MESSAGE_SEGMENT_START:
|
||||
case GST_MESSAGE_SEGMENT_DONE:
|
||||
/* We shouldn't see any segement messages, since we didn't do a segment seek */
|
||||
GST_WARNING ("Saw a Segment start/stop");
|
||||
fail_if (TRUE);
|
||||
break;
|
||||
case GST_MESSAGE_ERROR:
|
||||
fail_error_message (message);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
gst_mini_object_unref (GST_MINI_OBJECT (message));
|
||||
}
|
||||
}
|
||||
|
||||
GST_DEBUG ("Setting pipeline to NULL");
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_READY) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
fail_if (collect->expected_segments != NULL);
|
||||
|
||||
GST_DEBUG ("Resetted pipeline to READY");
|
||||
|
||||
/* Expected segments */
|
||||
collect->expected_segments = g_list_append (collect->expected_segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME, 5 * GST_SECOND, 6 * GST_SECOND, 0));
|
||||
collect->expected_segments = g_list_append (collect->expected_segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME,
|
||||
2 * GST_SECOND, 3 * GST_SECOND, 1 * GST_SECOND));
|
||||
collect->gotsegment = FALSE;
|
||||
collect->expected_base = 0;
|
||||
|
||||
|
||||
GST_DEBUG ("Setting pipeline to PLAYING again");
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
carry_on = TRUE;
|
||||
|
||||
GST_DEBUG ("Let's poll the bus AGAIN");
|
||||
|
||||
while (carry_on) {
|
||||
message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
|
||||
if (message) {
|
||||
switch (GST_MESSAGE_TYPE (message)) {
|
||||
case GST_MESSAGE_EOS:
|
||||
/* we should check if we really finished here */
|
||||
carry_on = FALSE;
|
||||
break;
|
||||
case GST_MESSAGE_SEGMENT_START:
|
||||
case GST_MESSAGE_SEGMENT_DONE:
|
||||
/* We shouldn't see any segement messages, since we didn't do a segment seek */
|
||||
GST_WARNING ("Saw a Segment start/stop");
|
||||
fail_if (TRUE);
|
||||
break;
|
||||
case GST_MESSAGE_ERROR:
|
||||
fail_error_message (message);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
gst_mini_object_unref (GST_MINI_OBJECT (message));
|
||||
} else {
|
||||
GST_DEBUG ("bus_poll responded, but there wasn't any message...");
|
||||
}
|
||||
}
|
||||
|
||||
fail_if (collect->expected_segments != NULL);
|
||||
|
||||
gst_object_unref (GST_OBJECT (sinkpad));
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT_BETWEEN (pipeline, "main pipeline", 1, 2);
|
||||
gst_object_unref (pipeline);
|
||||
ASSERT_OBJECT_REFCOUNT_BETWEEN (bus, "main bus", 1, 2);
|
||||
gst_object_unref (bus);
|
||||
|
||||
g_free (collect);
|
||||
}
|
||||
|
||||
static void
|
||||
test_one_under_another_full (void)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
GstElement *pipeline;
|
||||
GstElement *comp, *sink, *source1, *source2;
|
||||
CollectStructure *collect;
|
||||
GstBus *bus;
|
||||
GstMessage *message;
|
||||
gboolean carry_on = TRUE;
|
||||
GstPad *sinkpad;
|
||||
|
||||
pipeline = gst_pipeline_new ("test_pipeline");
|
||||
comp =
|
||||
gst_element_factory_make_or_warn ("gnlcomposition", "test_composition");
|
||||
fail_if (comp == NULL);
|
||||
|
||||
/* TOPOLOGY
|
||||
*
|
||||
* 0 1 2 3 4 5 | Priority
|
||||
* ----------------------------------------------------------------------------
|
||||
* [- source1 -] | 1
|
||||
* [- source2 -] | 2
|
||||
* */
|
||||
|
||||
/*
|
||||
Source 1
|
||||
Start : 0s
|
||||
Duration : 2s
|
||||
Priority : 1
|
||||
*/
|
||||
source1 = videotest_gnl_src ("source1", 0, 2 * GST_SECOND, 3, 1);
|
||||
fail_if (source1 == NULL);
|
||||
check_start_stop_duration (source1, 0, 2 * GST_SECOND, 2 * GST_SECOND);
|
||||
|
||||
/*
|
||||
Source 2
|
||||
Start : 1s
|
||||
Duration : 2s
|
||||
Priority : 2
|
||||
*/
|
||||
source2 = videotest_gnl_src ("source2", 1 * GST_SECOND, 2 * GST_SECOND, 2, 2);
|
||||
fail_if (source2 == NULL);
|
||||
check_start_stop_duration (source2, 1 * GST_SECOND, 3 * GST_SECOND,
|
||||
2 * GST_SECOND);
|
||||
|
||||
/* Add two sources */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source1);
|
||||
gst_bin_add (GST_BIN (comp), source2);
|
||||
check_start_stop_duration (comp, 0, 0 * GST_SECOND, 0 * GST_SECOND);
|
||||
/* Now commiting changes */
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 3 * GST_SECOND, 3 * GST_SECOND);
|
||||
check_start_stop_duration (source1, 0, 2 * GST_SECOND, 2 * GST_SECOND);
|
||||
check_start_stop_duration (source2, 1 * GST_SECOND, 3 * GST_SECOND,
|
||||
2 * GST_SECOND);
|
||||
|
||||
/* Remove second source */
|
||||
|
||||
gst_object_ref (source1);
|
||||
gst_bin_remove (GST_BIN (comp), source1);
|
||||
check_start_stop_duration (comp, 1 * GST_SECOND, 3 * GST_SECOND,
|
||||
2 * GST_SECOND);
|
||||
|
||||
/* Re-add second source */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source1);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 3 * GST_SECOND, 3 * GST_SECOND);
|
||||
gst_object_unref (source1);
|
||||
|
||||
sink = gst_element_factory_make_or_warn ("fakesink", "sink");
|
||||
fail_if (sink == NULL);
|
||||
|
||||
gst_bin_add_many (GST_BIN (pipeline), comp, sink, NULL);
|
||||
|
||||
/* Shared data */
|
||||
collect = g_new0 (CollectStructure, 1);
|
||||
collect->comp = comp;
|
||||
collect->sink = sink;
|
||||
|
||||
/* Expected segments */
|
||||
collect->expected_segments = g_list_append (collect->expected_segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME, 0, GST_SECOND, 0));
|
||||
|
||||
collect->expected_segments = g_list_append (collect->expected_segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME, GST_SECOND, 2 * GST_SECOND,
|
||||
GST_SECOND));
|
||||
|
||||
collect->expected_segments = g_list_append (collect->expected_segments,
|
||||
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);
|
||||
|
||||
sinkpad = gst_element_get_static_pad (sink, "sink");
|
||||
gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
|
||||
(GstPadProbeCallback) sinkpad_probe, collect, NULL);
|
||||
|
||||
bus = gst_element_get_bus (GST_ELEMENT (pipeline));
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
while (carry_on) {
|
||||
message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
|
||||
if (message) {
|
||||
switch (GST_MESSAGE_TYPE (message)) {
|
||||
case GST_MESSAGE_EOS:
|
||||
/* we should check if we really finished here */
|
||||
carry_on = FALSE;
|
||||
break;
|
||||
case GST_MESSAGE_SEGMENT_START:
|
||||
case GST_MESSAGE_SEGMENT_DONE:
|
||||
/* check if the segment is the correct one (0s-4s) */
|
||||
carry_on = FALSE;
|
||||
break;
|
||||
case GST_MESSAGE_ERROR:
|
||||
fail_error_message (message);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
gst_message_unref (message);
|
||||
}
|
||||
}
|
||||
|
||||
fail_if (collect->expected_segments != NULL);
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
|
||||
gst_object_unref (GST_OBJECT (sinkpad));
|
||||
ASSERT_OBJECT_REFCOUNT_BETWEEN (pipeline, "main pipeline", 1, 2);
|
||||
gst_object_unref (pipeline);
|
||||
ASSERT_OBJECT_REFCOUNT_BETWEEN (bus, "main bus", 1, 2);
|
||||
gst_object_unref (bus);
|
||||
|
||||
g_free (collect);
|
||||
}
|
||||
|
||||
static void
|
||||
test_one_bin_after_other_full (void)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
GstElement *pipeline;
|
||||
GstElement *comp, *sink, *source1, *source2;
|
||||
CollectStructure *collect;
|
||||
GstBus *bus;
|
||||
GstMessage *message;
|
||||
gboolean carry_on = TRUE;
|
||||
GstPad *sinkpad;
|
||||
|
||||
pipeline = gst_pipeline_new ("test_pipeline");
|
||||
comp =
|
||||
gst_element_factory_make_or_warn ("gnlcomposition", "test_composition");
|
||||
fail_if (comp == NULL);
|
||||
|
||||
/*
|
||||
Source 1
|
||||
Start : 0s
|
||||
Duration : 1s
|
||||
Priority : 1
|
||||
*/
|
||||
source1 = videotest_in_bin_gnl_src ("source1", 0, 1 * GST_SECOND, 3, 1);
|
||||
if (source1 == NULL) {
|
||||
gst_object_unref (pipeline);
|
||||
gst_object_unref (comp);
|
||||
return;
|
||||
}
|
||||
check_start_stop_duration (source1, 0, 1 * GST_SECOND, 1 * GST_SECOND);
|
||||
|
||||
/*
|
||||
Source 2
|
||||
Start : 1s
|
||||
Duration : 1s
|
||||
Priority : 1
|
||||
*/
|
||||
source2 =
|
||||
videotest_in_bin_gnl_src ("source2", 1 * GST_SECOND, 1 * GST_SECOND, 2,
|
||||
1);
|
||||
fail_if (source2 == NULL);
|
||||
check_start_stop_duration (source2, 1 * GST_SECOND, 2 * GST_SECOND,
|
||||
1 * GST_SECOND);
|
||||
|
||||
/* Add one source */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source1);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
fail_unless (ret);
|
||||
check_start_stop_duration (comp, 0, 1 * GST_SECOND, 1 * GST_SECOND);
|
||||
check_start_stop_duration (source1, 0, 1 * GST_SECOND, 1 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
|
||||
/* Second source */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source2);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 2 * GST_SECOND, 2 * GST_SECOND);
|
||||
check_start_stop_duration (source1, 0, 1 * GST_SECOND, 1 * GST_SECOND);
|
||||
check_start_stop_duration (source2, 1 * GST_SECOND, 2 * GST_SECOND,
|
||||
1 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source2, "source2", 1);
|
||||
|
||||
/* Remove first source */
|
||||
|
||||
gst_object_ref (source1);
|
||||
gst_bin_remove (GST_BIN (comp), source1);
|
||||
check_start_stop_duration (comp, 1 * GST_SECOND, 2 * GST_SECOND,
|
||||
1 * GST_SECOND);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
|
||||
/* Re-add first source */
|
||||
|
||||
gst_bin_add (GST_BIN (comp), source1);
|
||||
g_signal_emit_by_name (comp, "commit", TRUE, &ret);
|
||||
check_start_stop_duration (comp, 0, 2 * GST_SECOND, 2 * GST_SECOND);
|
||||
gst_object_unref (source1);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
|
||||
sink = gst_element_factory_make_or_warn ("fakesink", "sink");
|
||||
fail_if (sink == NULL);
|
||||
|
||||
gst_bin_add_many (GST_BIN (pipeline), comp, sink, NULL);
|
||||
|
||||
/* Shared data */
|
||||
collect = g_new0 (CollectStructure, 1);
|
||||
collect->comp = comp;
|
||||
collect->sink = sink;
|
||||
|
||||
/* Expected segments */
|
||||
collect->expected_segments = g_list_append (collect->expected_segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME, 0, 1 * GST_SECOND, 0));
|
||||
collect->expected_segments = g_list_append (collect->expected_segments,
|
||||
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);
|
||||
|
||||
sinkpad = gst_element_get_static_pad (sink, "sink");
|
||||
gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
|
||||
(GstPadProbeCallback) sinkpad_probe, collect, NULL);
|
||||
|
||||
bus = gst_element_get_bus (GST_ELEMENT (pipeline));
|
||||
|
||||
GST_DEBUG ("Setting pipeline to PLAYING");
|
||||
ASSERT_OBJECT_REFCOUNT (source1, "source1", 1);
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
GST_DEBUG ("Let's poll the bus");
|
||||
|
||||
while (carry_on) {
|
||||
message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
|
||||
if (message) {
|
||||
switch (GST_MESSAGE_TYPE (message)) {
|
||||
case GST_MESSAGE_EOS:
|
||||
/* we should check if we really finished here */
|
||||
GST_WARNING ("Got an EOS");
|
||||
carry_on = FALSE;
|
||||
break;
|
||||
case GST_MESSAGE_SEGMENT_START:
|
||||
case GST_MESSAGE_SEGMENT_DONE:
|
||||
/* We shouldn't see any segement messages, since we didn't do a segment seek */
|
||||
GST_WARNING ("Saw a Segment start/stop");
|
||||
fail_if (FALSE);
|
||||
break;
|
||||
case GST_MESSAGE_ERROR:
|
||||
fail_error_message (message);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
gst_mini_object_unref (GST_MINI_OBJECT (message));
|
||||
}
|
||||
}
|
||||
|
||||
GST_DEBUG ("Setting pipeline to NULL");
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_READY) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
fail_if (collect->expected_segments != NULL);
|
||||
|
||||
GST_DEBUG ("Resetted pipeline to READY");
|
||||
|
||||
/* Expected segments */
|
||||
collect->expected_segments = g_list_append (collect->expected_segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME, 0, 1 * GST_SECOND, 0));
|
||||
collect->expected_segments = g_list_append (collect->expected_segments,
|
||||
segment_new (1.0, GST_FORMAT_TIME,
|
||||
1 * GST_SECOND, 2 * GST_SECOND, 1 * GST_SECOND));
|
||||
collect->gotsegment = FALSE;
|
||||
collect->expected_base = 0;
|
||||
|
||||
GST_DEBUG ("Setting pipeline to PLAYING again");
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
carry_on = TRUE;
|
||||
while (carry_on) {
|
||||
message = gst_bus_poll (bus, GST_MESSAGE_ANY, GST_SECOND / 10);
|
||||
if (message) {
|
||||
switch (GST_MESSAGE_TYPE (message)) {
|
||||
case GST_MESSAGE_EOS:
|
||||
/* we should check if we really finished here */
|
||||
carry_on = FALSE;
|
||||
break;
|
||||
case GST_MESSAGE_SEGMENT_START:
|
||||
case GST_MESSAGE_SEGMENT_DONE:
|
||||
/* We shouldn't see any segement messages, since we didn't do a segment seek */
|
||||
GST_WARNING ("Saw a Segment start/stop");
|
||||
fail_if (FALSE);
|
||||
break;
|
||||
case GST_MESSAGE_ERROR:
|
||||
fail_error_message (message);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
gst_mini_object_unref (GST_MINI_OBJECT (message));
|
||||
}
|
||||
}
|
||||
|
||||
gst_object_unref (GST_OBJECT (sinkpad));
|
||||
|
||||
fail_if (collect->expected_segments != NULL);
|
||||
|
||||
fail_if (gst_element_set_state (GST_ELEMENT (pipeline),
|
||||
GST_STATE_NULL) == GST_STATE_CHANGE_FAILURE);
|
||||
|
||||
ASSERT_OBJECT_REFCOUNT_BETWEEN (pipeline, "main pipeline", 1, 2);
|
||||
gst_object_unref (pipeline);
|
||||
ASSERT_OBJECT_REFCOUNT_BETWEEN (bus, "main bus", 1, 2);
|
||||
gst_object_unref (bus);
|
||||
|
||||
g_free (collect);
|
||||
}
|
||||
|
||||
GST_START_TEST (test_simplest)
|
||||
{
|
||||
test_simplest_full ();
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_time_duration)
|
||||
{
|
||||
test_time_duration_full ();
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_one_after_other)
|
||||
{
|
||||
test_one_after_other_full ();
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_one_under_another)
|
||||
{
|
||||
test_one_under_another_full ();
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
GST_START_TEST (test_one_bin_after_other)
|
||||
{
|
||||
test_one_bin_after_other_full ();
|
||||
}
|
||||
|
||||
GST_END_TEST;
|
||||
|
||||
static Suite *
|
||||
gnonlin_suite (void)
|
||||
{
|
||||
Suite *s = suite_create ("gnonlin-simple");
|
||||
TCase *tc_chain = tcase_create ("general");
|
||||
|
||||
suite_add_tcase (s, tc_chain);
|
||||
|
||||
tcase_add_test (tc_chain, test_time_duration);
|
||||
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);
|
||||
return s;
|
||||
}
|
||||
|
||||
GST_CHECK_MAIN (gnonlin)
|
Loading…
Reference in a new issue