commiting wim's preliminary threaded work to a branch

Original commit message from CVS:
commiting wim's preliminary threaded work to a branch
This commit is contained in:
Thomas Vander Stichele 2004-12-08 17:40:37 +00:00
parent 047c0a3e9b
commit 3ffce00efc
91 changed files with 7047 additions and 7866 deletions

View file

@ -1,41 +1,7 @@
2004-12-08 Thomas Vander Stichele <thomas at apestaart dot org>
* autogen.sh:
remove patch if autopoint fails
2004-12-08 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
* docs/gst/gstreamer-sections.txt:
Document Thomas' addition, fix build, make Luis the sheriff happy.
2004-12-07 Thomas Vander Stichele <thomas at apestaart dot org>
* gst/gstplugin.c:
* gst/gstplugin.h:
add accessor for version field
2004-12-06 Thomas Vander Stichele <thomas at apestaart dot org>
submitted by: Luca Ferretti <elle.uca@infinito.it>
* po/LINGUAS:
* po/it.po:
New tranlation added: Italian
2004-12-03 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
* gst/gstpad.c: (gst_pad_is_negotiated),
(gst_pad_get_negotiated_caps):
GST_RPAD_* will only operate on a RealPad (it casts the pointer,
it doesn't actually check the contents), so be sure to hand it
a RealPad else we'll crash.
2004-12-03 Wim Taymans <wim@fluendo.com>
* gst/gstqueue.c: (gst_queue_init), (gst_queue_getcaps),
(gst_queue_link), (gst_queue_handle_src_query):
Reverted to 1.110 until this makes the testsuite and various
apps work.
* commited Wim's preliminary work according to his design to
BRANCH-THREADED
2004-12-01 Christian Fredrik Kalager Schaller christian@fluendo.com
@ -594,7 +560,6 @@
* gst/elements/gstidentity.h:
Added datarate properties to limit the datarate.
>>>>>>> 1.808
2004-08-25 Benjamin Otte <in7y118@public.uni-hamburg.de>
* gst/autoplug/gstspider.c: (plugin_init):

View file

@ -8,13 +8,17 @@ SUBDIRS_DOCS =
endif
if BUILD_TESTS
SUBDIRS_TESTS = tests testsuite
## SUBDIRS_TESTS = tests testsuite
## FIXME: write tests from scratch
SUBDIRS_TESTS =
else
SUBDIRS_TESTS =
endif
if BUILD_EXAMPLES
SUBDIRS_EXAMPLES = examples
## FIXME: write examples from scratch
# SUBDIRS_EXAMPLES = examples
SUBDIRS_EXAMPLES =
else
SUBDIRS_EXAMPLES =
endif

View file

@ -67,7 +67,7 @@ if test -f po/Makefile.in.in;
then
patch -p0 -R < common/gettext.patch
fi
tool_run "$autopoint" "--force" "patch -p0 < common/gettext.patch"
tool_run "$autopoint --force"
patch -p0 < common/gettext.patch
# aclocal

View file

@ -1186,7 +1186,6 @@ gst_plugin_get_license
gst_plugin_get_package
gst_plugin_get_origin
gst_plugin_get_module
gst_plugin_get_version
gst_plugin_is_loaded
gst_plugin_feature_filter
gst_plugin_list_feature_filter

View file

@ -348,10 +348,28 @@ If the refcount is 1, this function just returns the original buffer.
</para>
<<<<<<< gstbuffer.sgml
@GST_BUFFER_READONLY: the buffer is read-only.
@GST_BUFFER_SUBBUFFER: the buffer is a subbuffer, the parent buffer can be
found with the GST_BUFFER_POOL_PRIVATE() macro.
@GST_BUFFER_ORIGINAL: buffer is not a copy of another buffer.
@GST_BUFFER_DONTFREE: do not try to free the data when this buffer is
unreferenced.
@GST_BUFFER_KEY_UNIT: the buffer holds a key unit, a unit that can be
decoded independently of other buffers.
@GST_BUFFER_DONTKEEP:
<<<<<<< gstbuffer.sgml
@GST_BUFFER_IN_CAPS:
=======
@GST_BUFFER_IN_CAPS: the buffer has been added as a field in a #GstCaps.
>>>>>>> 1.44
@GST_BUFFER_FLAG_LAST: additional flags can be added starting from this flag.
=======
@buf1:
@buf2:
@Returns:
>>>>>>> 1.48
<!-- ##### FUNCTION gst_buffer_default_free ##### -->
<para>

View file

@ -2,37 +2,20 @@
gstcontrol
<!-- ##### SECTION Short_Description ##### -->
dynamic parameter functionality.
<!-- ##### SECTION Long_Description ##### -->
<para>
This library provides a Manager that maintains a list of dynamically
controlable parameters for a GstElement.
Just think of a volume slider in a mixer.
</para>
<para>
To use this library one needs to add some code to initialize it.
<example>
<title>Adding the control library to a project</title>
<programlisting>
...
&hash;include &lt;gst/gst.h&gt;
&hash;include &lt;gst/control/control.h&gt;
...
gst_init(&amp;argc,&amp;argv);
gst_control_init(&amp;argc,&amp;argv);
...
</programlisting>
</example>
The next step is to get hold of the GstDParamManager instance of a GstElement.
</para>
<!-- ##### SECTION See_Also ##### -->
<para>
</para>
<!-- ##### FUNCTION gst_control_init ##### -->
<para>
@ -124,10 +107,8 @@ The next step is to get hold of the GstDParamManager instance of a GstElement.
</para>
@dpman:
@dparam_name:
@Returns:
<!-- # Unused Parameters # -->
@name:
@Returns:
<!-- ##### FUNCTION gst_dpman_get_dparam_type ##### -->
@ -136,40 +117,10 @@ The next step is to get hold of the GstDParamManager instance of a GstElement.
</para>
@dpman:
@dparam_name:
@Returns:
<!-- # Unused Parameters # -->
@name:
<!-- ##### FUNCTION gst_dpman_list_dparam_specs ##### -->
<para>
</para>
@dpman:
@Returns:
<!-- ##### FUNCTION gst_dpman_get_param_spec ##### -->
<para>
</para>
@dpman:
@dparam_name:
@Returns:
<!-- ##### FUNCTION gst_dpman_set_rate ##### -->
<para>
</para>
@dpman:
@rate:
<!-- ##### FUNCTION gst_dpman_register_mode ##### -->
<para>
@ -259,12 +210,3 @@ The next step is to get hold of the GstDParamManager instance of a GstElement.
@update_info:
<!-- ##### FUNCTION gst_dpsmooth_new ##### -->
<para>
</para>
@type:
@Returns:

View file

@ -2,18 +2,20 @@
gstgetbits
<!-- ##### SECTION Short_Description ##### -->
accelerated routines for getting bits from a data stream.
<!-- ##### SECTION Long_Description ##### -->
<para>
</para>
<!-- ##### SECTION See_Also ##### -->
<para>
</para>
<!-- ##### STRUCT gst_getbits_t ##### -->
<para>
@ -130,8 +132,6 @@ accelerated routines for getting bits from a data stream.
@gb:
@n:
<!-- # Unused Parameters # -->
@num:
<!-- ##### MACRO gst_getbits1 ##### -->
@ -325,8 +325,6 @@ accelerated routines for getting bits from a data stream.
@gb:
@n:
<!-- # Unused Parameters # -->
@num:
<!-- ##### MACRO gst_showbits1 ##### -->
@ -536,8 +534,6 @@ accelerated routines for getting bits from a data stream.
@gb:
@n:
<!-- # Unused Parameters # -->
@num:
<!-- ##### MACRO gst_flushbits32 ##### -->
@ -555,8 +551,6 @@ accelerated routines for getting bits from a data stream.
@gb:
@n:
<!-- # Unused Parameters # -->
@num:
<!-- ##### MACRO gst_backbits24 ##### -->

View file

@ -0,0 +1,257 @@
<chapter id="chapter-factories">
<title>More on factories</title>
<para>
The small application we created in the previous chapter used the
concept of a factory to create the elements. In this chapter we will
show you how to use the factory concepts to create elements based
on what they do instead of what they are called.
</para>
<para>
We will first explain the concepts involved before we move on
to the reworked helloworld example using autoplugging.
</para>
<sect1 id="section-factories-helloworld-problems">
<title>The problems with the helloworld example</title>
<para>
If we take a look at how the elements were created in the previous
example we used a rather crude mechanism:
</para>
<programlisting>
...
/* now it's time to get the parser */
decoder = gst_element_factory_make ("mad", "decoder");
...
</programlisting>
<para>
While this mechanism is quite effective it also has some big problems:
The elements are created based on their name. Indeed, we create an
element, mad, by explicitly stating the mad element's name. Our little
program therefore always uses the mad decoder element to decode
the MP3 audio stream, even if there are three other MP3 decoders in the
system. We will see how we can use a more general way to create an
MP3 decoder element.
</para>
<para>
We have to introduce the concept of MIME types and capabilities
added to the source and sink pads.
</para>
</sect1>
<sect1 id="section-factories-mime">
<title>More on MIME Types</title>
<para>
GStreamer uses MIME types to identify the different types of data
that can be handled by the elements. They are the high level
mechanisms to make sure that everyone is talking about the right
kind of data.
</para>
<para>
A MIME (Multipurpose Internet Mail Extension) type is a pair of
strings that denote a certain type of data. Examples include:
<itemizedlist>
<listitem>
<para>
audio/x-raw-int : raw audio samples
</para>
</listitem>
<listitem>
<para>
audio/mpeg : MPEG audio
</para>
</listitem>
<listitem>
<para>
video/mpeg : MPEG video
</para>
</listitem>
</itemizedlist>
</para>
<para>
An element must associate a MIME type to its source and sink pads
when it is loaded into the system. GStreamer knows about the
different elements and what type of data they expect and emit.
This allows for very dynamic and extensible element creation as we
will see.
</para>
<para>
As we have seen in the previous chapter, MIME types are added
to the Capability structure of a pad.
</para>
<para>
<xref linkend="section-mime-img"/> shows the MIME types associated with
each pad from the "hello world" example.
</para>
<figure float="1" id="section-mime-img">
<title>The Hello world pipeline with MIME types</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/mime-world.&image;" format="&IMAGE;" />
</imageobject>
</mediaobject>
</figure>
<para>
We will see how you can create an element based on the MIME types
of its source and sink pads. This way the end-user will have the
ability to choose his/her favorite audio/mpeg decoder without
you even having to care about it.
</para>
<para>
The typing of the source and sink pads also makes it possible to
'autoplug' a pipeline. We will have the ability to say: "construct
a pipeline that does an audio/mpeg to audio/x-raw-int conversion".
</para>
<note>
<para>
The basic GStreamer library does not try to solve all of your
autoplug problems. It leaves the hard decisions to the application
programmer, where they belong.
</para>
</note>
</sect1>
<sect1 id="section-factories-gstreamer-types">
<title>GStreamer types</title>
<para>
GStreamer assigns a unique number to all registered MIME types.
GStreamer also keeps a reference to
a function that can be used to determine if a given buffer is of
the given MIME type.
</para>
<para>
There is also an association between a MIME type and a file extension,
but the use of typefind functions (similar to file(1)) is preferred.
</para>
<para>
The type information is maintained in a list of
<classname>GstType</classname>. The definition of a
<classname>GstType</classname> is like:
</para>
<para>
<programlisting>
typedef GstCaps (*GstTypeFindFunc) (GstBuffer *buf,gpointer *priv);
typedef struct _GstType GstType;
struct _GstType {
guint16 id; /* type id (assigned) */
gchar *mime; /* MIME type */
gchar *exts; /* space-delimited list of extensions */
GstTypeFindFunc typefindfunc; /* typefind function */
};
</programlisting>
</para>
<para>
All operations on <classname>GstType</classname> occur
via their <classname>guint16 id</classname> numbers, with
the <classname>GstType</classname> structure private to the GStreamer
library.
</para>
<sect2>
<title>MIME type to id conversion</title>
<para>
We can obtain the id for a given MIME type
with the following piece of code:
</para>
<programlisting>
guint16 id;
id = gst_type_find_by_mime ("audio/mpeg");
</programlisting>
<para>
This function will return 0 if the type was not known.
</para>
</sect2>
<sect2>
<title>id to <classname>GstType</classname> conversion</title>
<para>
We can obtain the <classname>GstType</classname> for a given id
with the following piece of code:
</para>
<programlisting>
GstType *type;
type = gst_type_find_by_id (id);
</programlisting>
<para>
This function will return NULL if the id was not associated with
any known <classname>GstType</classname>
</para>
</sect2>
<sect2>
<title>extension to id conversion</title>
<para>
We can obtain the id for a given file extension
with the following piece of code:
</para>
<programlisting>
guint16 id;
id = gst_type_find_by_ext (".mp3");
</programlisting>
<para>
This function will return 0 if the extension was not known.
</para>
<para>
For more information, see <xref linkend="chapter-autoplug"/>.
</para>
</sect2>
</sect1>
<sect1 id="section-factories-create">
<title>Creating elements with the factory</title>
<para>
In the previous section we described how you could obtain
an element factory using MIME types. One the factory has been
obtained, you can create an element using:
</para>
<programlisting>
GstElementFactory *factory;
GstElement *element;
// obtain the factory
factory = ...
element = gst_element_factory_create (factory, "name");
</programlisting>
<para>
This way, you do not have to create elements by name which
allows the end-user to select the elements he/she prefers for the
given MIME types.
</para>
</sect1>
<sect1 id="section-factories-basic-types">
<title>GStreamer basic types</title>
<para>
GStreamer only has two builtin types:
</para>
<itemizedlist>
<listitem>
<para>
audio/raw : raw audio samples
</para>
</listitem>
<listitem>
<para>
video/raw and image/raw : raw video data
</para>
</listitem>
</itemizedlist>
<para>
All other MIME types are maintained by the plugin elements.
</para>
</sect1>
</chapter>

View file

@ -0,0 +1,5 @@
<chapter id="chapter-clocks">
<title>Clocks in GStreamer</title>
<para>
</para>
</chapter>

View file

@ -0,0 +1,198 @@
<chapter id="chapter-dparams">
<title>Dynamic Parameters</title>
<sect1 id="section-dparams-getting-started">
<title>Getting Started</title>
<para>
The Dynamic Parameters subsystem is contained within the
<filename>gstcontrol</filename> library.
You need to include the header in your application's source file:
</para>
<programlisting>
...
#include &lt;gst/gst.h&gt;
#include &lt;gst/control/control.h&gt;
...
</programlisting>
<para>
Your application should link to the shared library <filename>gstcontrol</filename>.
</para>
<para>
The <filename>gstcontrol</filename> library needs to be initialized
when your application is run. This can be done after the the GStreamer
library has been initialized.
</para>
<programlisting>
...
gst_init(&amp;argc,&amp;argv);
gst_control_init(&amp;argc,&amp;argv);
...
</programlisting>
</sect1>
<sect1 id="section-dparams-creating">
<title>Creating and Attaching Dynamic Parameters</title>
<para>
Once you have created your elements you can create and attach dparams to them.
First you need to get the element's dparams manager. If you know exactly what kind of element
you have, you may be able to get the dparams manager directly. However if this is not possible,
you can get the dparams manager by calling <filename>gst_dpman_get_manager</filename>.
</para>
<para>
Once you have the dparams manager, you must set the mode that the manager will run in.
There is currently only one mode implemented called <filename>"synchronous"</filename> - this is used for real-time
applications where the dparam value cannot be known ahead of time (such as a slider in a GUI).
The mode is called <filename>"synchronous"</filename> because the dparams are polled by the element for changes before
each buffer is processed. Another yet-to-be-implemented mode is <filename>"asynchronous"</filename>. This is used when
parameter changes are known ahead of time - such as with a timelined editor. The mode is called
<filename>"asynchronous"</filename> because parameter changes may happen in the middle of a buffer being processed.
</para>
<programlisting>
GstElement *sinesrc;
GstDParamManager *dpman;
...
sinesrc = gst_element_factory_make("sinesrc","sine-source");
...
dpman = gst_dpman_get_manager (sinesrc);
gst_dpman_set_mode(dpman, "synchronous");
</programlisting>
<para>
If you don't know the names of the required dparams for your element you can call
<filename>gst_dpman_list_dparam_specs(dpman)</filename> to get a NULL terminated array of param specs.
This array should be freed after use. You can find the name of the required dparam by calling
<filename>g_param_spec_get_name</filename> on each param spec in the array. In our example,
<filename>"volume"</filename> will be the name of our required dparam.
</para>
<para>
Each type of dparam currently has its own <filename>new</filename> function. This may eventually
be replaced by a factory method for creating new instances. A default dparam instance can be created
with the <filename>gst_dparam_new</filename> function. Once it is created it can be attached to a
required dparam in the element.
</para>
<programlisting>
GstDParam *volume;
...
volume = gst_dparam_new(G_TYPE_DOUBLE);
if (gst_dpman_attach_dparam (dpman, "volume", volume)){
/* the dparam was successfully attached */
...
}
</programlisting>
</sect1>
<sect1 id="section-dparams-changing">
<title>Changing Dynamic Parameter Values</title>
<para>
All interaction with dparams to actually set the dparam value is done through simple GObject properties.
There is a property value for each type that dparams supports - these currently being
<filename>"value_double"</filename>, <filename>"value_float"</filename>, <filename>"value_int"</filename> and <filename>"value_int64"</filename>.
To set the value of a dparam, simply set the property which matches the type of your dparam instance.
</para>
<programlisting>
#define ZERO(mem) memset(&amp;mem, 0, sizeof(mem))
...
gdouble set_to_value;
GstDParam *volume;
GValue set_val;
ZERO(set_val);
g_value_init(&amp;set_val, G_TYPE_DOUBLE);
...
g_value_set_double(&amp;set_val, set_to_value);
g_object_set_property(G_OBJECT(volume), "value_double", &amp;set_val);
</programlisting>
<para>Or if you create an actual GValue instance:</para>
<programlisting>
gdouble set_to_value;
GstDParam *volume;
GValue *set_val;
set_val = g_new0(GValue,1);
g_value_init(set_val, G_TYPE_DOUBLE);
...
g_value_set_double(set_val, set_to_value);
g_object_set_property(G_OBJECT(volume), "value_double", set_val);
</programlisting>
</sect1>
<sect1 id="section-dparams-types">
<title>Different Types of Dynamic Parameter</title>
<para>
There are currently only two implementations of dparams so far. They are both for real-time use so
should be run in the <filename>"synchronous"</filename> mode.
</para>
<sect2>
<title>GstDParam - the base dparam type</title>
<para>
All dparam implementations will subclass from this type. It provides a basic implementation which simply
propagates any value changes as soon as it can.
A new instance can be created with the function <filename>GstDParam* gst_dparam_new (GType type)</filename>.
It has the following object properties:
</para>
<itemizedlist>
<listitem><para><filename>"value_double"</filename>
- the property to set and get if it is a double dparam
</para></listitem>
<listitem><para><filename>"value_float"</filename>
- the property to set and get if it is a float dparam
</para></listitem>
<listitem><para><filename>"value_int"</filename>
- the property to set and get if it is an integer dparam
</para></listitem>
<listitem><para><filename>"value_int64"</filename>
- the property to set and get if it is a 64 bit integer dparam
</para></listitem>
<listitem><para><filename>"is_log"</filename>
- readonly boolean which is TRUE if the param should be displayed on a log scale
</para></listitem>
<listitem><para><filename>"is_rate"</filename>
- readonly boolean which is TRUE if the value is a proportion of the sample rate.
For example with a sample rate of 44100, 0.5 would be 22050 Hz and 0.25 would be 11025 Hz.
</para></listitem>
</itemizedlist>
</sect2>
<sect2>
<title>GstDParamSmooth - smoothing real-time dparam</title>
<para>
Some parameter changes can create audible artifacts if they change too rapidly. The GstDParamSmooth
implementation can greatly reduce these artifacts by limiting the rate at which the value can change.
This is currently only supported for double and float dparams - the other types fall back to the default implementation.
A new instance can be created with the function <filename>GstDParam* gst_dpsmooth_new (GType type)</filename>.
It has the following object properties:
</para>
<itemizedlist>
<listitem><para><filename>"update_period"</filename>
- an int64 value specifying the number nanoseconds between updates. This will be ignored in
<filename>"synchronous"</filename> mode since the buffer size dictates the update period.
</para></listitem>
<listitem><para><filename>"slope_time"</filename>
- an int64 value specifying the time period to use in the maximum slope calculation
</para></listitem>
<listitem><para><filename>"slope_delta_double"</filename>
- a double specifying the amount a double value can change in the given slope_time.
</para></listitem>
<listitem><para><filename>"slope_delta_float"</filename>
- a float specifying the amount a float value can change in the given slope_time.
</para></listitem>
</itemizedlist>
<para>
Audible artifacts may not be completely eliminated by using this dparam. The only way to eliminate
artifacts such as "zipper noise" would be for the element to implement its required dparams using the
array method. This would allow dparams to change parameters at the sample rate which should eliminate
any artifacts.
</para>
</sect2>
<sect2>
<title>Timelined dparams</title>
<para>
A yet-to-be-implemented subclass of GstDParam will add an API which allows the creation and manipulation
of points on a timeline. This subclass will also provide a dparam implementation which uses linear
interpolation between these points to find the dparam value at any given time. Further subclasses can
extend this functionality to implement more exotic interpolation algorithms such as splines.
</para>
</sect2>
</sect1>
</chapter>

View file

@ -0,0 +1,168 @@
<chapter id="chapter-threads">
<title>Threads</title>
<para>
GStreamer has support for multithreading through the use of
the <ulink type="http" url="../../gstreamer/html/GstThread.html"><classname>
GstThread</classname></ulink> object. This object is in fact
a special <ulink type="http" url="../../gstreamer/html/GstBin.html"><classname>
GstBin</classname></ulink> that will become a thread when started.
</para>
<para>
To construct a new thread you will perform something like:
</para>
<para>
<programlisting>
GstElement *my_thread;
/* create the thread object */
my_thread = gst_thread_new ("my_thread");
/* you could have used gst_element_factory_make ("thread", "my_thread"); */
g_return_if_fail (my_thread != NULL);
/* add some plugins */
gst_bin_add (GST_BIN (my_thread), GST_ELEMENT (funky_src));
gst_bin_add (GST_BIN (my_thread), GST_ELEMENT (cool_effect));
/* link the elements here... */
...
/* start playing */
gst_element_set_state (GST_ELEMENT (my_thread), GST_STATE_PLAYING);
</programlisting>
</para>
<para>
The above program will create a thread with two elements in it. As soon
as it is set to the PLAYING state, the thread will start to iterate
itself. You never need to explicitly iterate a thread.
</para>
<sect1 id="section-threads-constraints">
<title>Constraints placed on the pipeline by the GstThread</title>
<para>
Within the pipeline, everything is the same as in any other bin. The
difference lies at the thread boundary, at the link between the
thread and the outside world (containing bin). Since GStreamer is
fundamentally buffer-oriented rather than byte-oriented, the natural
solution to this problem is an element that can "buffer" the buffers
between the threads, in a thread-safe fashion. This element is the
queue, described more fully in <xref linkend="chapter-queues"/>. It doesn't
matter if the queue is placed in the containing bin or in the thread
itself, but it needs to be present on one side or the other to enable
inter-thread communication.
</para>
</sect1>
<sect1 id="section-threads-when">
<title>When would you want to use a thread?</title>
<para>
If you are writing a GUI application, making the top-level bin a thread will make your GUI
more responsive. If it were a pipeline instead, it would have to be iterated by your
application's event loop, which increases the latency between events (say, keyboard presses)
and responses from the GUI. In addition, any slight hang in the GUI would delay iteration of
the pipeline, which (for example) could cause pops in the output of the sound card, if it is
an audio pipeline.
</para>
<para>
<xref linkend="section-threads-img"/> shows how a thread can be visualised.
</para>
<figure float="1" id="section-threads-img">
<title>A thread</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/thread.&image;" format="&IMAGE;" />
</imageobject>
</mediaobject>
</figure>
<para>
As an example we show the helloworld program using a thread.
</para>
<para>
<programlisting>
/* example-begin threads.c */
#include &lt;gst/gst.h&gt;
/* we set this to TRUE right before gst_main (), but there could still
be a race condition between setting it and entering the function */
gboolean can_quit = FALSE;
/* eos will be called when the src element has an end of stream */
void
eos (GstElement *src, gpointer data)
{
GstThread *thread = GST_THREAD (data);
g_print ("have eos, quitting\n");
/* stop the bin */
gst_element_set_state (GST_ELEMENT (thread), GST_STATE_NULL);
while (!can_quit) /* waste cycles */ ;
gst_main_quit ();
}
int
main (int argc, char *argv[])
{
GstElement *filesrc, *demuxer, *decoder, *converter, *audiosink;
GstElement *thread;
if (argc &lt; 2) {
g_print ("usage: %s &lt;Ogg/Vorbis filename&gt;\n", argv[0]);
exit (-1);
}
gst_init (&amp;argc, &amp;argv);
/* create a new thread to hold the elements */
thread = gst_thread_new ("thread");
g_assert (thread != NULL);
/* create a disk reader */
filesrc = gst_element_factory_make ("filesrc", "disk_source");
g_assert (filesrc != NULL);
g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL);
g_signal_connect (G_OBJECT (filesrc), "eos",
G_CALLBACK (eos), thread);
/* create an ogg demuxer */
demuxer = gst_element_factory_make ("oggdemux", "demuxer");
g_assert (demuxer != NULL);
/* create a vorbis decoder */
decoder = gst_element_factory_make ("vorbisdec", "decoder");
g_assert (decoder != NULL);
/* create an audio converter */
converter = gst_element_factory_make ("audioconvert", "converter");
g_assert (decoder != NULL);
/* and an audio sink */
audiosink = gst_element_factory_make ("osssink", "play_audio");
g_assert (audiosink != NULL);
/* add objects to the thread */
gst_bin_add_many (GST_BIN (thread), filesrc, demuxer, decoder, converter, audiosink, NULL);
/* link them in the logical order */
gst_element_link_many (filesrc, demuxer, decoder, converter, audiosink, NULL);
/* start playing */
gst_element_set_state (thread, GST_STATE_PLAYING);
/* do whatever you want here, the thread will be playing */
g_print ("thread is playing\n");
can_quit = TRUE;
gst_main ();
gst_object_unref (GST_OBJECT (thread));
exit (0);
}
/* example-end threads.c */
</programlisting>
</para>
</sect1>
</chapter>

View file

@ -0,0 +1,253 @@
<chapter id="chapter-quotes">
<title>Quotes from the Developers</title>
<para>
As well as being a cool piece of software,
<application>GStreamer</application> is a lively project, with
developers from around the globe very actively contributing.
We often hang out on the #gstreamer IRC channel on
irc.freenode.net: the following are a selection of amusing<footnote>
<para>No guarantee of sense of humour compatibility is given.</para>
</footnote> quotes from our conversations.
</para>
<variablelist>
<varlistentry>
<term>2 Nov 2004</term>
<listitem>
<para>
<emphasis>zaheerm</emphasis>:
wtay: unfair u fixed the bug i was using as a feature!
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>14 Oct 2004</term>
<listitem>
<para>
<emphasis>* zaheerm</emphasis>
wonders how he can break gstreamer today :)
</para>
<para>
<emphasis>ensonic</emphasis>:
zaheerm, spider is always a good starting point
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>14 Jun 2004</term>
<listitem>
<para>
<emphasis>teuf</emphasis>: ok, things work much better when I don't write incredibly stupid and buggy code
</para>
<para>
<emphasis>thaytan</emphasis>: I find that too
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>23 Nov 2003</term>
<listitem>
<para>
<emphasis>Uraeus</emphasis>: ah yes, the sleeping part, my mind
is not multitasking so I was still thinking about exercise
</para>
<para>
<emphasis>dolphy</emphasis>: Uraeus: your mind is multitasking
</para>
<para>
<emphasis>dolphy</emphasis>: Uraeus: you just miss low latency patches
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>14 Sep 2002</term>
<listitem>
<para>
--- <emphasis>wingo-party</emphasis> is now known as
<emphasis>wingo</emphasis>
</para>
<para>
* <emphasis>wingo</emphasis> holds head
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>16 Feb 2001</term>
<listitem>
<para>
<emphasis>wtay:</emphasis>
I shipped a few commerical products to &gt;40000 people now but
GStreamer is way more exciting...
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>16 Feb 2001</term>
<listitem>
<para>
*
<emphasis>tool-man</emphasis>
is a gstreamer groupie
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>14 Jan 2001</term>
<listitem>
<para>
<emphasis>Omega:</emphasis>
did you run ldconfig? maybe it talks to init?
</para>
<para>
<emphasis>wtay:</emphasis>
not sure, don't think so...
I did run gstreamer-register though :-)
</para>
<para>
<emphasis>Omega:</emphasis>
ah, that did it then ;-)
</para>
<para>
<emphasis>wtay:</emphasis>
right
</para>
<para>
<emphasis>Omega:</emphasis>
probably not, but in case GStreamer starts turning into an OS, someone please let me know?
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>9 Jan 2001</term>
<listitem>
<para>
<emphasis>wtay:</emphasis>
me tar, you rpm?
</para>
<para>
<emphasis>wtay:</emphasis>
hehe, forgot &quot;zan&quot;
</para>
<para>
<emphasis>Omega:</emphasis>
?
</para>
<para>
<emphasis>wtay:</emphasis>
me tar&quot;zan&quot;, you ...
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>7 Jan 2001</term>
<listitem>
<para>
<emphasis>Omega:</emphasis>
that means probably building an agreggating, cache-massaging
queue to shove N buffers across all at once, forcing cache
transfer.
</para>
<para>
<emphasis>wtay:</emphasis>
never done that before...
</para>
<para>
<emphasis>Omega:</emphasis>
nope, but it's easy to do in gstreamer &lt;g&gt;
</para>
<para>
<emphasis>wtay:</emphasis>
sure, I need to rewrite cp with gstreamer too, someday :-)
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>7 Jan 2001</term>
<listitem>
<para>
<emphasis>wtay:</emphasis>
GStreamer; always at least one developer is awake...
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>5/6 Jan 2001</term>
<listitem>
<para>
<emphasis>wtay:</emphasis>
we need to cut down the time to create an mp3 player down to
seconds...
</para>
<para>
<emphasis>richardb:</emphasis>
:)
</para>
<para>
<emphasis>Omega:</emphasis>
I'm wanting to something more interesting soon, I did the "draw an mp3
player in 15sec" back in October '99.
</para>
<para>
<emphasis>wtay:</emphasis>
by the time Omega gets his hands on the editor, you'll see a
complete audio mixer in the editor :-)
</para>
<para>
<emphasis>richardb:</emphasis>
Well, it clearly has the potential...
</para>
<para>
<emphasis>Omega:</emphasis>
Working on it... ;-)
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>28 Dec 2000</term>
<listitem>
<para>
<emphasis>MPAA:</emphasis>
We will sue you now, you have violated our IP rights!
</para>
<para>
<emphasis>wtay:</emphasis>
hehehe
</para>
<para>
<emphasis>MPAA:</emphasis>
How dare you laugh at us? We have lawyers! We have Congressmen! We have <emphasis>LARS</emphasis>!
</para>
<para>
<emphasis>wtay:</emphasis>
I'm so sorry your honor
</para>
<para>
<emphasis>MPAA:</emphasis>
Hrumph.
</para>
<para>
*
<emphasis>wtay</emphasis>
bows before thy
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>4 Jun 2001</term>
<listitem>
<para><emphasis>taaz:</emphasis> you witchdoctors and your voodoo mpeg2 black magic... </para>
<para><emphasis>omega_:</emphasis> um. I count three, no four different cults there &lt;g&gt; </para>
<para><emphasis>ajmitch:</emphasis> hehe </para>
<para><emphasis>omega_:</emphasis> witchdoctors, voodoo, black magic, </para>
<para><emphasis>omega_:</emphasis> and mpeg </para>
</listitem>
</varlistentry>
</variablelist>
</chapter>

View file

@ -0,0 +1,49 @@
<chapter id="chapter-bins">
<title>Bins</title>
<para>
A bin is a container element. You can add elements to a bin. Since a bin is
an element itself, it can also be added to another bin.
</para>
<para>
Bins allow you to combine a group of linked elements into one logical element. You do
not deal with the individual elements anymore but with just one element, the bin.
We will see that this is extremely powerful when you are going to construct
complex pipelines since it allows you to break up the pipeline in smaller chunks.
</para>
<para>
The bin will also manage the elements contained in it. It will figure out how
the data will flow in the bin and generate an optimal plan for that data flow. Plan
generation is one of the most complicated procedures in GStreamer.
</para>
<figure float="1" id="section-bin-img">
<title>Visualisation of a bin with some elements in it</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/bin-element.&image;" format="&IMAGE;" />
</imageobject>
</mediaobject>
</figure>
<para>
There are two specialized bins available to the GStreamer programmer:
<itemizedlist>
<listitem>
<para>
a pipeline: a generic container that allows scheduling of the
containing elements. The toplevel bin has to be a pipeline.
Every application thus needs at least one of these.
</para>
</listitem>
<listitem>
<para>
a thread: a bin that will be run in a separate execution thread.
You will have to use this bin if you have to carefully
synchronize audio and video, or for buffering. You will learn
more about threads in <xref linkend="chapter-threads"/>.
</para>
</listitem>
</itemizedlist>
</para>
</chapter>

View file

@ -0,0 +1,66 @@
<chapter id="chapter-buffers">
<title>Buffers</title>
<para>
Buffers contain the data that will flow through the pipeline you have
created. A source element will typically create a new buffer and pass
it through a pad to the next element in the chain. When using the
GStreamer infrastructure to create a media pipeline you will not have
to deal with buffers yourself; the elements will do that for you.
</para>
<para>
A buffer consists of:
<itemizedlist>
<listitem>
<para>
a pointer to a piece of memory.
</para>
</listitem>
<listitem>
<para>
the size of the memory.
</para>
</listitem>
<listitem>
<para>
a timestamp for the buffer.
</para>
</listitem>
<listitem>
<para>
A refcount that indicates how many elements are using this
buffer. This refcount will be used to destroy the buffer when no
element has a reference to it.
</para>
</listitem>
</itemizedlist>
</para>
<para>
<!-- FIXME: this is outdated, there is no GstBufferPool in gst-0.8.X -->
GStreamer provides functions to create custom buffer create/destroy algorithms, called
a <classname>GstBufferPool</classname>. This makes it possible to efficiently
allocate and destroy buffer memory. It also makes it possible to exchange memory between
elements by passing the <classname>GstBufferPool</classname>. A video element can,
for example, create a custom buffer allocation algorithm that creates buffers with XSHM
as the buffer memory. An element can use this algorithm to create and fill the buffer
with data.
</para>
<para>
The simple case is that a buffer is created, memory allocated, data put
in it, and passed to the next element. That element reads the data, does
something (like creating a new buffer and decoding into it), and
unreferences the buffer. This causes the data to be freed and the buffer
to be destroyed. A typical MPEG audio decoder works like this.
</para>
<para>
A more complex case is when the filter modifies the data in place. It
does so and simply passes on the buffer to the next element. This is just
as easy to deal with. An element that works in place has to be careful when
the buffer is used in more than one element; a copy on write has to made in this
situation.
</para>
</chapter>

View file

@ -0,0 +1,122 @@
<chapter id="chapter-elements">
<title>Elements</title>
<para>
The most important object in <application>GStreamer</application> for the
application programmer is the <ulink type="http"
url="../../gstreamer/html/GstElement.html"><classname>GstElement</classname>
</ulink>object.
</para>
<sect1 id="section-elements-design">
<title>What is an element ?</title>
<para>
An element is the basic building block for the media pipeline.
All the different high-level components you are going to use are
derived from <ulink type="http" url="../../gstreamer/html/GstElement.html">
<classname>GstElement</classname></ulink>. This means that a
lot of functions you are going to use operate on objects of this class.
</para>
<para>
Elements, from the perspective of GStreamer, are viewed as "black boxes"
with a number of different aspects. One of these aspects is the presence
of "pads" (see <xref linkend="chapter-pads"/>), or link points.
This terminology arises from soldering; pads are where wires can be
attached.
</para>
</sect1>
<sect1 id="section-elements-types">
<title>Types of elements</title>
<sect2 id="section-elements-src">
<title>Source elements</title>
<para>
Source elements generate data for use by a pipeline, for example
reading from disk or from a sound card.
</para>
<para>
<xref linkend="section-element-srcimg"/> shows how we will visualise
a source element.
We always draw a source pad to the right of the element.
</para>
<figure float="1" id="section-element-srcimg">
<title>Visualisation of a source element</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/src-element.&image;" format="&IMAGE;" />
</imageobject>
</mediaobject>
</figure>
<para>
Source elements do not accept data, they only generate data. You can
see this in the figure because it only has a source pad. A source
pad can only generate data.
</para>
</sect2>
<sect2 id="section-elements-filter">
<title>Filters and codecs</title>
<para>
Filter elements have both input and output pads. They operate on
data they receive in their sink pads and produce data on their source
pads. For example, MPEG decoders and volume filters would fall into
this category.
</para>
<para>
Elements are not constrained as to the number of pads they might have;
for example, a video mixer might have two input pads (the images of
the two different video streams) and one output pad.
</para>
<figure float="1" id="section-element-filterimg">
<title>Visualisation of a filter element</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/filter-element.&image;" format="&IMAGE;" />
</imageobject>
</mediaobject>
</figure>
<para>
<xref linkend="section-element-filterimg"/> shows how we will visualise
a filter element.
This element has one sink (input) pad and one source (output) pad.
Sink pads are drawn on the left of the element.
</para>
<figure float="1" id="section-element-multifilterimg">
<title>Visualisation of a filter element with
more than one output pad</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/filter-element-multi.&image;"
format="&IMAGE;" />
</imageobject>
</mediaobject>
</figure>
<para>
<xref linkend="section-element-multifilterimg"/> shows the visualisation of a filter element with
more than one output pad. An example of such a filter is the AVI
demultiplexer. This element will parse the input data and
extract the audio and video data. Most of these filters dynamically
send out a signal when a new pad is created so that the application
programmer can link an arbitrary element to the newly created pad.
</para>
</sect2>
<sect2 id="section-elements-sink">
<title>Sink elements</title>
<para>
Sink elements are end points in a media pipeline. They accept
data but do not produce anything. Disk writing, soundcard playback,
and video output would all be implemented by sink elements.
<xref linkend="section-element-sinkimg"/> shows a sink element.
</para>
<figure float="1" id="section-element-sinkimg">
<title>Visualisation of a sink element</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/sink-element.&image;" format="&IMAGE;" />
</imageobject>
</mediaobject>
</figure>
</sect2>
</sect1>
</chapter>

View file

@ -0,0 +1,280 @@
<chapter id="chapter-hello-world">
<title>Your first application</title>
<para>
This chapter describes the most rudimentary aspects of a
<application>GStreamer</application> application, including initializing
the libraries, creating elements, packing them into a pipeline and playing,
pausing and stopping the pipeline.
</para>
<sect1 id="section-hello-world">
<title>Hello world</title>
<para>
We will create a simple first application, a complete MP3 player, using
standard <application>GStreamer</application> components. The player
will read from a file that is given as the first argument to the program.
</para>
<programlisting>
/* example-begin helloworld.c */
#include &lt;gst/gst.h&gt;
int
main (int argc, char *argv[])
{
GstElement *pipeline, *filesrc, *decoder, *audiosink;
gst_init(&amp;argc, &amp;argv);
if (argc != 2) {
g_print ("usage: %s &lt;mp3 filename&gt;\n", argv[0]);
exit (-1);
}
/* create a new pipeline to hold the elements */
pipeline = gst_pipeline_new ("pipeline");
/* create a disk reader */
filesrc = gst_element_factory_make ("filesrc", "disk_source");
g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL);
/* now it's time to get the decoder */
decoder = gst_element_factory_make ("mad", "decoder");
/* and an audio sink */
audiosink = gst_element_factory_make ("osssink", "play_audio");
/* add objects to the main pipeline */
gst_bin_add_many (GST_BIN (pipeline), filesrc, decoder, audiosink, NULL);
/* link src to sink */
gst_element_link_many (filesrc, decoder, audiosink, NULL);
/* start playing */
gst_element_set_state (pipeline, GST_STATE_PLAYING);
while (gst_bin_iterate (GST_BIN (pipeline)));
/* stop the pipeline */
gst_element_set_state (pipeline, GST_STATE_NULL);
/* we don't need a reference to these objects anymore */
gst_object_unref (GST_OBJECT (pipeline));
/* unreffing the pipeline unrefs the contained elements as well */
exit (0);
}
/* example-end helloworld.c */
</programlisting>
<para>
Let's go through this example step by step.
</para>
<para>
The first thing you have to do is to include the standard
<application>GStreamer</application> headers and
initialize the framework.
</para>
<programlisting>
#include &lt;gst/gst.h&gt;
...
int
main (int argc, char *argv[])
{
...
gst_init(&amp;argc, &amp;argv);
...
</programlisting>
<para>
We are going to create three elements and one pipeline. Since all
elements share the same base type, <ulink type="http"
url="../../gstreamer/html/GstElement.html"><classname>GstElement</classname></ulink>,
we can define them as:
</para>
<programlisting>
...
GstElement *pipeline, *filesrc, *decoder, *audiosink;
...
</programlisting>
<para>
Next, we are going to create an empty pipeline. As you have seen in
the basic introduction, this pipeline will hold and manage all the
elements we are going to pack into it.
</para>
<programlisting>
/* create a new pipeline to hold the elements */
pipeline = gst_pipeline_new ("pipeline");
</programlisting>
<para>
We use the standard constructor for a pipeline: gst_pipeline_new ().
</para>
<para>
We then create a disk source element. The disk source element is able to
read from a file. We use the standard GObject property mechanism to set
a property of the element: the file to read from.
</para>
<programlisting>
/* create a disk reader */
filesrc = gst_element_factory_make ("filesrc", "disk_source");
g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL);
</programlisting>
<note>
<para>
You can check if the filesrc != NULL to verify the creation of the
disk source element.
</para>
</note>
<para>
We now create the MP3 decoder element. This assumes that the 'mad' plugin
is installed on the system where this application is executed.
</para>
<programlisting>
/* now it's time to get the decoder */
decoder = gst_element_factory_make ("mad", "decoder");
</programlisting>
<para>
gst_element_factory_make() takes two arguments: a string that will
identify the element you need and a second argument: how you want
to name the element. The name of the element is something you can
choose yourself and might be used to retrieve the element from a
bin/pipeline.
</para>
<para>
Finally we create our audio sink element. This element will be able
to play back the audio using OSS.
</para>
<programlisting>
/* and an audio sink */
audiosink = gst_element_factory_make ("osssink", "play_audio");
</programlisting>
<para>
We then add the elements to the pipeline.
</para>
<programlisting>
/* add objects to the main pipeline */
gst_bin_add_many (GST_BIN (pipeline), filesrc, decoder, audiosink, NULL);
</programlisting>
<para>
We link the different pads of the elements together like this:
</para>
<programlisting>
/* link src to sink */
gst_element_link_many (filesrc, decoder, audiosink, NULL);
</programlisting>
<para>
We now have created a complete pipeline. We can visualise the
pipeline as follows:
</para>
<figure float="1" id="section-hello-img">
<title>The "hello world" pipeline</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/hello-world.&image;" format="&IMAGE;" />
</imageobject>
</mediaobject>
</figure>
<para>
Everything is now set up to start streaming. We use the following
statements to change the state of the pipeline:
</para>
<programlisting>
/* start playing */
gst_element_set_state (pipeline, GST_STATE_PLAYING);
</programlisting>
<note>
<para>
<application>GStreamer</application> will take care of the READY and PAUSED state for
you when going from NULL to PLAYING.
</para>
</note>
<para>
Since we do not use threads, nothing will happen yet. We have to
call gst_bin_iterate() to execute one iteration of the pipeline.
</para>
<programlisting>
while (gst_bin_iterate (GST_BIN (pipeline)));
</programlisting>
<para>
The gst_bin_iterate() function will return TRUE as long as something
interesting happened inside the pipeline. When the end-of-file has been
reached the _iterate function will return FALSE and we can end the loop.
</para>
<programlisting>
/* stop the pipeline */
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (GST_OBJECT (pipeline));
exit (0);
</programlisting>
<note>
<para>
Don't forget to set the state of the pipeline to NULL. This will free
all of the resources held by the elements.
</para>
</note>
</sect1>
<sect1 id="section-hello-world-compile">
<title>Compiling helloworld.c</title>
<para>
To compile the helloworld example, use:
</para>
<programlisting>
gcc -Wall `pkg-config gstreamer-&GST_MAJORMINOR; --cflags --libs` helloworld.c \
-o helloworld
</programlisting>
<para>
We use pkg-config to get the compiler flags needed to compile
this application. Make sure to have your PKG_CONFIG_PATH environment
variable set to the correct location if you are building this
application against the uninstalled location.
</para>
<para>
You can run the example with
(substitute helloworld.mp3 with you favorite MP3 file):
</para>
<programlisting>
./helloworld helloworld.mp3
</programlisting>
</sect1>
<sect1 id="section-hello-world-conclusion">
<title>Conclusion</title>
<para>
This concludes our first example. As you see, setting up a pipeline
is very low-level but powerful. You will see later in this manual how
you can create a custom MP3 element with a higher-level API.
</para>
<para>
It should be clear from the example that we can very easily replace the
filesrc element with the gnomevfssrc element, giving you instant streaming
from any gnomevfs URL.
</para>
<para>
We can also choose to use another type of sink instead of the audiosink.
We could use a filesink to write the raw samples to a file, for example.
It should also be clear that inserting filters, like a stereo effect,
into the pipeline is not that hard to do. The most important thing is
that you can reuse already existing elements.
</para>
</sect1>
</chapter>

View file

@ -0,0 +1,99 @@
<chapter id="chapter-initialisation">
<title>Initializing <application>GStreamer</application></title>
<para>
When writing a <application>GStreamer</application> application, you can
simply include <filename class='headerfile'>gst/gst.h</filename> to get
access to the library functions.
</para>
<para>
Before the <application>GStreamer</application> libraries can be used,
<function>gst_init</function> has to be called from the main application.
This call will perform the necessary initialization of the library as
well as parse the GStreamer-specific command line options.
</para>
<para>
A typical program
&EXAFOOT;
would have code to initialize GStreamer that
looks like this:
</para>
<programlisting>
<![CDATA[
/* example-begin init.c */
#include <gst/gst.h>
int
main (int argc, char *argv[])
{
guint major, minor, micro;
gst_init (&amp;argc, &amp;argv);
gst_version (&amp;major, &amp;minor, &amp;micro);
printf ("This program is linked against GStreamer %d.%d.%d\n",
major, minor, micro);
return 0;
}
/* example-end init.c */
]]>
</programlisting>
<para>
Use the <symbol>GST_VERSION_MAJOR</symbol>,
<symbol>GST_VERSION_MINOR</symbol> and <symbol>GST_VERSION_MICRO</symbol>
macros to get the <application>GStreamer</application> version you are
building against, or use the function <function>gst_version</function>
to get the version your application is linked against.
<!-- FIXME: include an automatically generated list of these options. -->
</para>
<para>
It is also possible to call the <function>gst_init</function> function
with two <symbol>NULL</symbol> arguments, in which case no command line
options will be parsed by <application>GStreamer</application>.
</para>
<sect1>
<title>The popt interface</title>
<para>
You can also use a popt table to initialize your own parameters as shown in the
next example:
</para>
<programlisting>
/* example-begin popt.c */
#include &lt;gst/gst.h&gt;
int
main(int argc, char *argv[])
{
gboolean silent = FALSE;
gchar *savefile = NULL;
struct poptOption options[] = {
{"silent", 's', POPT_ARG_NONE|POPT_ARGFLAG_STRIP, &amp;silent, 0,
"do not output status information", NULL},
{"output", 'o', POPT_ARG_STRING|POPT_ARGFLAG_STRIP, &amp;savefile, 0,
"save xml representation of pipeline to FILE and exit", "FILE"},
POPT_TABLEEND
};
gst_init_with_popt_table (&amp;argc, &amp;argv, options);
printf ("Run me with --help to see the Application options appended.\n");
return 0;
}
/* example-end popt.c */
</programlisting>
<para>
As shown in this fragment, you can use a <ulink
url="http://developer.gnome.org/doc/guides/popt/"
type="http">popt</ulink> table to define your application-specific
command line options, and pass this table to the
function <function>gst_init_with_popt_table</function>. Your
application options will be parsed in addition to the standard
<application>GStreamer</application> options.
</para>
</sect1>
</chapter>

244
docs/manual/basics-pads.xml Normal file
View file

@ -0,0 +1,244 @@
<chapter id="chapter-pads">
<title>Pads</title>
<para>
As we have seen in <xref linkend="chapter-elements"/>, the pads are the element's
interface to the outside world.
</para>
<para>
The specific type of media that the element can handle will be exposed by the pads.
The description of this media type is done with capabilities(see
<xref linkend="section-caps"/>)
</para>
<para>
Pads are either source or sink pads. The terminology is defined from the
view of the element itself: elements accept data on their sink pads, and
send data out on their source pads. Sink pads are drawn on the left,
while source pads are drawn on the right of an element. In general,
data flows from left to right in the graph.<footnote>
<para>
In reality, there is no objection to data flowing from a
source pad to the sink pad of an element upstream. Data will, however,
always flow from a source pad of one element to the sink pad of
another.
</para></footnote>
</para>
<sect1 id="section-pads-type">
<title>Types of pad</title>
<sect2 id="section-pads-dynamic">
<title>Dynamic pads</title>
<para>
Some elements might not have all of their pads when the element is
created. This
can happen, for example, with an MPEG system demultiplexer. The
demultiplexer will create its pads at runtime when it detects the
different elementary streams in the MPEG system stream.
</para>
<para>
Running <application>gst-inspect mpegdemux</application> will show that
the element has only one pad: a sink pad called 'sink'. The other pads are
"dormant". You can see this in the pad template because there is
an 'Exists: Sometimes'
property. Depending on the type of MPEG file you play, the pads will
be created. We
will see that this is very important when you are going to create dynamic
pipelines later on in this manual.
</para>
</sect2>
<sect2 id="section-pads-request">
<title>Request pads</title>
<para>
An element can also have request pads. These pads are not created
automatically but are only created on demand. This is very useful
for multiplexers, aggregators and tee elements.
</para>
<para>
The tee element, for example, has one input pad and a request padtemplate for the
output pads. Whenever an element wants to get an output pad from the tee element, it
has to request the pad.
</para>
</sect2>
</sect1>
<sect1 id="section-caps">
<title>Capabilities of a pad</title>
<para>
Since the pads play a very important role in how the element is viewed by the
outside world, a mechanism is implemented to describe the data that can
flow through the pad by using capabilities.
</para>
<para>
We will briefly describe what capabilities are, enough for you to get a basic understanding
of the concepts. You will find more information on how to create capabilities in the
Plugin Writer's Guide.
</para>
<sect2 id="section-pads-caps">
<title>Capabilities</title>
<para>
Capabilities are attached to a pad in order to describe
what type of media the pad can handle.
</para>
<para>
Capabilities is shorthand for "capability chain". A capability chain
is a chain of one capability or more.
</para>
<para>
The basic entity is a capability, and is defined by a name, a MIME
type and a set of properties. A capability can be chained to
another capability, which is why we commonly refer to a chain of
capability entities as "capabilities".
<footnote>
<para>
It is important to understand that the term "capabilities" refers
to a chain of one capability or more. This will be clearer when
you see the structure definition of a <ulink type="http"
url="../../gstreamer/html/gstreamer-GstCaps.html"><classname>GstCaps
</classname></ulink>element.
</para>
</footnote>
</para>
<para>
Below is a dump of the capabilities of the element mad, as shown by
<command>gst-inspect</command>.
You can see two pads: sink and src. Both pads have capability information attached to them.
</para>
<para>
The sink pad (input pad) is called 'sink' and takes data of MIME type 'audio/mp3'. It also has
three properties: layer, bitrate and framed.
</para>
<para>
The source pad (output pad) is called 'src' and outputs data of
MIME type 'audio/raw'. It also has four properties: format, depth,
rate and channels.
</para>
<programlisting>
Pads:
SINK template: 'sink'
Availability: Always
Capabilities:
'mad_sink':
MIME type: 'audio/mp3':
SRC template: 'src'
Availability: Always
Capabilities:
'mad_src':
MIME type: 'audio/raw':
format: String: int
endianness: Integer: 1234
width: Integer: 16
depth: Integer: 16
channels: Integer range: 1 - 2
law: Integer: 0
signed: Boolean: TRUE
rate: Integer range: 11025 - 48000
</programlisting>
</sect2>
<sect2 id="section-pads-props">
<title>What are properties ?</title>
<para>
Properties are used to describe extra information for
capabilities. A property consists of a key (a string) and
a value. There are different possible value types that can be used:
</para>
<itemizedlist>
<listitem>
<para>
basic types:
</para>
<itemizedlist>
<listitem>
<para>
an integer value: the property has this exact value.
</para>
</listitem>
<listitem>
<para>
a boolean value: the property is either TRUE or FALSE.
</para>
</listitem>
<listitem>
<para>
a fourcc value: this is a value that is commonly used to
describe an encoding for video,
as used for example by the AVI specification.
<footnote><para>
fourcc values consist of four bytes.
<ulink url="http://www.fourcc.org" type="http">The FOURCC
Definition List</ulink> is the most complete resource
on the allowed fourcc values.
</para></footnote>
</para>
</listitem>
<listitem>
<para>
a float value: the property has this exact floating point value.
</para>
</listitem>
<listitem>
<para>
a string value.
</para>
</listitem>
</itemizedlist>
</listitem>
<listitem>
<para>
range types:
</para>
<itemizedlist>
<listitem>
<para>
an integer range value: the property denotes a range of
possible integers. For example, the wavparse element has
a source pad where the "rate" property can go from 8000 to
48000.
</para>
</listitem>
<listitem>
<para>
a float range value: the property denotes a range of possible
floating point values.
</para>
</listitem>
</itemizedlist>
</listitem>
<listitem>
<para>
a list value: the property can take any value from a list of
basic value types or range types.
</para>
</listitem>
</itemizedlist>
</sect2>
<sect2 id="section-pads-caps-use">
<title>What capabilities are used for</title>
<para>
Capabilities describe in great detail the type of media that is handled by the pads.
They are mostly used for:
</para>
<itemizedlist>
<listitem>
<para>
Autoplugging: automatically finding plugins for a set of capabilities
</para>
</listitem>
<listitem>
<para>
Compatibility detection: when two pads are linked, <application>GStreamer</application>
can verify if the two pads are talking about the same media types.
The process of linking two pads and checking if they are compatible
is called "caps negotiation".
</para>
</listitem>
</itemizedlist>
</sect2>
</sect1>
</chapter>

View file

@ -0,0 +1,31 @@
<chapter id="chapter-plugins">
<title>Plugins</title>
<!-- FIXME: introduce type definitions before this chapter -->
<para>
A plugin is a shared library that contains at least one of the following
items:
</para>
<itemizedlist>
<listitem>
<para>
one or more element factories
</para>
</listitem>
<listitem>
<para>
one or more type definitions
</para>
</listitem>
<listitem>
<para>
one or more auto-pluggers
</para>
</listitem>
<listitem>
<para>
exported symbols for use in other plugins
</para>
</listitem>
</itemizedlist>
</chapter>

View file

@ -0,0 +1,37 @@
<chapter id="chapter-components">
<title>Components</title>
<para>
FIXME: This chapter is way out of date.
</para>
<para>
<application>GStreamer</application> includes components that people can include
in their programs.
</para>
<sect1 id="section-components-gst-play">
<title>GstPlay</title>
<para>
GstPlay is a GtkWidget with a simple API to play, pause and stop a media file.
</para>
</sect1>
<sect1 id="section-components-gst-media-play">
<title>GstMediaPlay</title>
<para>
GstMediaPlay is a complete player widget.
</para>
</sect1>
<sect1 id="section-components-gst-editor">
<title>GstEditor</title>
<para>
GstEditor is a set of widgets to display a graphical representation of a
pipeline.
</para>
</sect1>
</chapter>

View file

@ -0,0 +1,283 @@
<chapter id="chapter-xml">
<title>XML in <application>GStreamer</application></title>
<para>
<application>GStreamer</application> uses XML to store and load
its pipeline definitions. XML is also used internally to manage the
plugin registry. The plugin registry is a file that contains the definition
of all the plugins <application>GStreamer</application> knows about to have
quick access to the specifics of the plugins.
</para>
<para>
We will show you how you can save a pipeline to XML and how you can reload that
XML file again for later use.
</para>
<sect1 id="section-xml-write">
<title>Turning GstElements into XML</title>
<para>
We create a simple pipeline and write it to stdout with
gst_xml_write_file (). The following code constructs an MP3 player
pipeline with two threads and then writes out the XML both to stdout
and to a file. Use this program with one argument: the MP3 file on disk.
</para>
<programlisting>
/* example-begin xml-mp3.c */
#include &lt;stdlib.h&gt;
#include &lt;gst/gst.h&gt;
gboolean playing;
int
main (int argc, char *argv[])
{
GstElement *filesrc, *osssink, *queue, *queue2, *decode;
GstElement *bin;
GstElement *thread, *thread2;
gst_init (&amp;argc,&amp;argv);
if (argc != 2) {
g_print ("usage: %s &lt;mp3 filename&gt;\n", argv[0]);
exit (-1);
}
/* create a new thread to hold the elements */
thread = gst_element_factory_make ("thread", "thread");
g_assert (thread != NULL);
thread2 = gst_element_factory_make ("thread", "thread2");
g_assert (thread2 != NULL);
/* create a new bin to hold the elements */
bin = gst_bin_new ("bin");
g_assert (bin != NULL);
/* create a disk reader */
filesrc = gst_element_factory_make ("filesrc", "disk_source");
g_assert (filesrc != NULL);
g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL);
queue = gst_element_factory_make ("queue", "queue");
queue2 = gst_element_factory_make ("queue", "queue2");
/* and an audio sink */
osssink = gst_element_factory_make ("osssink", "play_audio");
g_assert (osssink != NULL);
decode = gst_element_factory_make ("mad", "decode");
g_assert (decode != NULL);
/* add objects to the main bin */
gst_bin_add_many (GST_BIN (bin), filesrc, queue, NULL);
gst_bin_add_many (GST_BIN (thread), decode, queue2, NULL);
gst_bin_add (GST_BIN (thread2), osssink);
gst_element_link_many (filesrc, queue, decode, queue2, osssink, NULL);
gst_bin_add_many (GST_BIN (bin), thread, thread2, NULL);
/* write the bin to stdout */
gst_xml_write_file (GST_ELEMENT (bin), stdout);
/* write the bin to a file */
gst_xml_write_file (GST_ELEMENT (bin), fopen ("xmlTest.gst", "w"));
exit (0);
}
/* example-end xml-mp3.c */
</programlisting>
<para>
The most important line is:
</para>
<programlisting>
gst_xml_write_file (GST_ELEMENT (bin), stdout);
</programlisting>
<para>
gst_xml_write_file () will turn the given element into an xmlDocPtr that
is then formatted and saved to a file. To save to disk, pass the result
of a fopen(2) as the second argument.
</para>
<para>
The complete element hierarchy will be saved along with the inter element
pad links and the element parameters. Future <application>GStreamer</application>
versions will also allow you to store the signals in the XML file.
</para>
</sect1>
<sect1 id="section-xml-load">
<title>Loading a GstElement from an XML file</title>
<para>
Before an XML file can be loaded, you must create a GstXML object.
A saved XML file can then be loaded with the
gst_xml_parse_file (xml, filename, rootelement) method.
The root element can optionally left NULL. The following code example loads
the previously created XML file and runs it.
</para>
<programlisting>
#include &lt;stdlib.h&gt;
#include &lt;gst/gst.h&gt;
int
main(int argc, char *argv[])
{
GstXML *xml;
GstElement *bin;
gboolean ret;
gst_init (&amp;argc, &amp;argv);
xml = gst_xml_new ();
ret = gst_xml_parse_file(xml, "xmlTest.gst", NULL);
g_assert (ret == TRUE);
bin = gst_xml_get_element (xml, "bin");
g_assert (bin != NULL);
gst_element_set_state (bin, GST_STATE_PLAYING);
while (gst_bin_iterate(GST_BIN(bin)));
gst_element_set_state (bin, GST_STATE_NULL);
exit (0);
}
</programlisting>
<para>
gst_xml_get_element (xml, "name") can be used to get a specific element
from the XML file.
</para>
<para>
gst_xml_get_topelements (xml) can be used to get a list of all toplevel elements
in the XML file.
</para>
<para>
In addition to loading a file, you can also load a from a xmlDocPtr and
an in memory buffer using gst_xml_parse_doc and gst_xml_parse_memory
respectively. Both of these methods return a gboolean indicating
success or failure of the requested action.
</para>
</sect1>
<sect1 id="section-xml-custom">
<title>Adding custom XML tags into the core XML data</title>
<para>
It is possible to add custom XML tags to the core XML created with
gst_xml_write. This feature can be used by an application to add more
information to the save plugins. The editor will for example insert
the position of the elements on the screen using the custom XML tags.
</para>
<para>
It is strongly suggested to save and load the custom XML tags using
a namespace. This will solve the problem of having your XML tags
interfere with the core XML tags.
</para>
<para>
To insert a hook into the element saving procedure you can link
a signal to the GstElement using the following piece of code:
</para>
<programlisting>
xmlNsPtr ns;
...
ns = xmlNewNs (NULL, "http://gstreamer.net/gst-test/1.0/", "test");
...
thread = gst_element_factory_make ("thread", "thread");
g_signal_connect (G_OBJECT (thread), "object_saved",
G_CALLBACK (object_saved), g_strdup ("decoder thread"));
...
</programlisting>
<para>
When the thread is saved, the object_save method will be called. Our example
will insert a comment tag:
</para>
<programlisting>
static void
object_saved (GstObject *object, xmlNodePtr parent, gpointer data)
{
xmlNodePtr child;
child = xmlNewChild (parent, ns, "comment", NULL);
xmlNewChild (child, ns, "text", (gchar *)data);
}
</programlisting>
<para>
Adding the custom tag code to the above example you will get an XML file
with the custom tags in it. Here's an excerpt:
</para>
<programlisting>
...
&lt;gst:element&gt;
&lt;gst:name&gt;thread&lt;/gst:name&gt;
&lt;gst:type&gt;thread&lt;/gst:type&gt;
&lt;gst:version&gt;0.1.0&lt;/gst:version&gt;
...
&lt;/gst:children&gt;
&lt;test:comment&gt;
&lt;test:text&gt;decoder thread&lt;/test:text&gt;
&lt;/test:comment&gt;
&lt;/gst:element&gt;
...
</programlisting>
<para>
To retrieve the custom XML again, you need to attach a signal to
the GstXML object used to load the XML data. You can then parse your
custom XML from the XML tree whenever an object is loaded.
</para>
<para>
We can extend our previous example with the following piece of
code.
</para>
<programlisting>
xml = gst_xml_new ();
g_signal_connect (G_OBJECT (xml), "object_loaded",
G_CALLBACK (xml_loaded), xml);
ret = gst_xml_parse_file (xml, "xmlTest.gst", NULL);
g_assert (ret == TRUE);
</programlisting>
<para>
Whenever a new object has been loaded, the xml_loaded function will
be called. This function looks like:
</para>
<programlisting>
static void
xml_loaded (GstXML *xml, GstObject *object, xmlNodePtr self, gpointer data)
{
xmlNodePtr children = self-&gt;xmlChildrenNode;
while (children) {
if (!strcmp (children-&gt;name, "comment")) {
xmlNodePtr nodes = children-&gt;xmlChildrenNode;
while (nodes) {
if (!strcmp (nodes-&gt;name, "text")) {
gchar *name = g_strdup (xmlNodeGetContent (nodes));
g_print ("object %s loaded with comment '%s'\n",
gst_object_get_name (object), name);
}
nodes = nodes-&gt;next;
}
}
children = children-&gt;next;
}
}
</programlisting>
<para>
As you can see, you'll get a handle to the GstXML object, the
newly loaded GstObject and the xmlNodePtr that was used to create
this object. In the above example we look for our special tag inside
the XML tree that was used to load the object and we print our
comment to the console.
</para>
</sect1>
</chapter>

View file

@ -0,0 +1,111 @@
<chapter id="chapter-motivation">
<title>Motivation</title>
<para>
Linux has historically lagged behind other operating systems in the multimedia
arena. Microsoft's <trademark>Windows</trademark> and Apple's <trademark>MacOS</trademark> both have strong support
for multimedia devices, multimedia content creation,
playback, and realtime processing. Linux, on the other hand, has a poorly integrated
collection of multimedia utilities and applications available, which can hardly compete
with the professional level of software available for MS Windows and MacOS.
</para>
<sect1 id="section-motivation-problems">
<title>Current problems</title>
<para>
We describe the typical problems in today's media handling on Linux.
</para>
<sect2 id="section-motivation-duplicate">
<title>Multitude of duplicate code</title>
<para>
The Linux user who wishes to hear a sound file must hunt through their collection of
sound file players in order to play the tens of sound file formats in wide use today.
Most of these players basically reimplement the same code over and over again.
</para>
<para>
The Linux developer who wishes to embed a video clip in their application must use
crude hacks to run an external video player. There is no library available that a
developer can use to create a custom media player.
</para>
</sect2>
<sect2 id="section-motivation-goal">
<title>'One goal' media players/libraries</title>
<para>
Your typical MPEG player was designed to play MPEG video and audio. Most of
these players have implemented a complete infrastructure focused on
achieving their only goal: playback. No provisions were made to add
filters or special effects to the video or audio data.
</para>
<para>
If you want to convert an MPEG2 video stream into an AVI file, your best
option would be to take all of the MPEG2 decoding algorithms out
of the player and duplicate them into your own AVI encoder. These
algorithms cannot easily be shared across applications.
</para>
<para>
Attempts have been made to create libraries for handling various media types.
Because they focus on a very specific media type (avifile, libmpeg2, ...),
significant work is needed to integrate them due to a lack of a common API.
GStreamer allows you to wrap these libraries with a common API, which
significantly simplifies integration and reuse.
</para>
</sect2>
<sect2 id="section-motivation-plugin">
<title>Non unified plugin mechanisms</title>
<para>
Your typical media player might have a plugin for different media
types. Two media players will typically implement their own plugin
mechanism so that the codecs cannot be easily exchanged. The plugin system
of the typical media player is also very tailored to the specific needs
of the application.
</para>
<para>
The lack of a unified plugin mechanism also seriously hinders the
creation of binary only codecs. No company is willing to port their
code to all the different plugin mechanisms.
</para>
<para>
While GStreamer also uses it own plugin system it offers a very rich
framework for the plugin developper and ensures the plugin can be used
in a wide range of applications, transparently interacting with other
plugins. The framework that GStreamer provides for the plugins is
flexible enough to host even the most demanding plugins.
</para>
</sect2>
<sect2 id="section-motivation-network">
<title>Provision for network transparency</title>
<para>
No infrastructure is present to allow network transparent media
handling. A distributed MPEG encoder will typically duplicate the
same encoder algorithms found in a non-distributed encoder.
</para>
<para>
No provisions have been made for technologies such as
the <ulink url="http://developer.gnome.org/arch/component/bonobo.html"
type="http">GNOME object embedding using Bonobo</ulink>.
</para>
<para>
The GStreamer core does not use network transparent technologies at the
lowest level as it only adds overhead for the local case.
That said, it shouldn't be hard to create a wrapper around the
core components. There are tcp plugins now that implement a GStreamer
Data Protocol that allows pipelines to be slit over TCP. These are
located in the gst-plugins module directory gst/tcp.
</para>
</sect2>
<sect2 id="section-motivation-catchup">
<title>Catch up with the <trademark>Windows</trademark> world</title>
<para>
We need solid media handling if we want to see Linux succeed on
the desktop.
</para>
<para>
We must clear the road for commercially backed codecs and multimedia
applications so that Linux can become an option for doing multimedia.
</para>
</sect2>
</sect1>
</chapter>

View file

@ -0,0 +1,59 @@
<chapter id="chapter-intro">
<title>Introduction</title>
<para>
This chapter gives you an overview of the technologies described in this
book.
</para>
<sect1 id="section-intro-what">
<title>What is GStreamer?</title>
<para>
GStreamer is a framework for creating streaming media applications.
The fundamental design comes from the video pipeline at Oregon Graduate
Institute, as well as some ideas from DirectShow.
</para>
<para>
GStreamer's development framework makes it possible to write any type of
streaming multimedia application. The GStreamer framework is designed
to make it easy to write applications that handle audio or video or both.
It isn't restricted to audio and video, and can process any kind of
data flow.
The pipeline design is made to have little overhead above what the
applied filters induce. This makes GStreamer a good framework for designing
even high-end audio applications which put high demands on latency.
</para>
<para>
One of the the most obvious uses of GStreamer is using it to build
a media player. GStreamer already includes components for building a
media player that can support a very wide variety of formats, including
MP3, Ogg Vorbis, MPEG1, MPEG2, AVI, Quicktime, mod, and more. GStreamer,
however, is much more than just another media player. Its main advantages
are that the pluggable components can be mixed and matched into arbitrary
pipelines so that it's possible to write a full-fledged video or audio
editing application.
</para>
<para>
The framework is based on plugins that will provide the various codec
and other functionality. The plugins can be linked and arranged in
a pipeline. This pipeline defines the flow of the data. Pipelines can
also be edited with a GUI editor and saved as XML so that pipeline
libraries can be made with a minimum of effort.
</para>
<para>
The GStreamer core function is to provide a framework for plugins, data flow
and media type handling/negotiation.
It also provides an API to write applications using the various plugins.
</para>
<para>
This book is about GStreamer from a developer's point of view; it describes
how to write a GStreamer application using the GStreamer libraries and tools.
For an explanation about writing plugins, we suggest the Plugin Writers Guide.
</para>
</sect1>
</chapter>

View file

@ -1,10 +1,6 @@
lib_LTLIBRARIES = libgstreamer-@GST_MAJORMINOR@.la
AS_LIBTOOL_LIB = libgstreamer-@GST_MAJORMINOR@
if GST_DISABLE_OMEGA_COTHREADS
noinst_LTLIBRARIES =
else
noinst_LTLIBRARIES = libcothreads.la
endif
#GST_INSTRUMENT_FLAGS = -finstrument-functions -DGST_ENABLE_FUNC_INSTRUMENTATION
@ -66,8 +62,8 @@ else
GST_URI_SRC = gsturi.c
endif
SUBDIRS = $(GST_PARSE_DIRS) $(GST_REGISTRY_DIRS) . autoplug elements schedulers $(GST_INDEX_DIRS)
DIST_SUBDIRS = autoplug elements parse registries schedulers indexers
SUBDIRS = $(GST_PARSE_DIRS) $(GST_REGISTRY_DIRS) . elements schedulers $(GST_INDEX_DIRS)
DIST_SUBDIRS = elements parse registries schedulers indexers
# make variables for all generated source and header files to make the
# distinction clear
@ -85,6 +81,7 @@ libgstreamer_@GST_MAJORMINOR@_la_SOURCES = \
gstatomic.c \
gstbin.c \
gstbuffer.c \
gstbus.c \
gstcaps.c \
gstclock.c \
gstcpu.c \
@ -98,20 +95,22 @@ libgstreamer_@GST_MAJORMINOR@_la_SOURCES = \
$(GST_INDEX_SRC) \
gstinfo.c \
gstinterface.c \
gstiterator.c \
gstmemchunk.c \
gstmessage.c \
gstpad.c \
gstpipeline.c \
gstplugin.c \
gstpluginfeature.c \
gstprobe.c \
gstqueue.c \
gstquery.c \
gstqueue.c \
gstscheduler.c \
gststructure.c \
gstsystemclock.c \
gsttag.c \
gsttaginterface.c \
gstthread.c \
gsttask.c \
$(GST_TRACE_SRC) \
gsttrashstack.c \
gsttypefind.c \
@ -158,6 +157,7 @@ gst_headers = \
gstobject.h \
gstbin.h \
gstbuffer.h \
gstbus.h \
gstcaps.h \
gstclock.h \
gstcompat.h \
@ -171,21 +171,22 @@ gst_headers = \
gstindex.h \
gstinfo.h \
gstinterface.h \
gstiterator.h \
gstmacros.h \
gstmemchunk.h \
gstmessage.h \
gstpad.h \
gstpipeline.h \
gstplugin.h \
gstpluginfeature.h \
gstprobe.h \
gstqueue.h \
gstquery.h \
gstqueue.h \
gstscheduler.h \
gststructure.h \
gstsystemclock.h \
gsttag.h \
gsttaginterface.h \
gstthread.h \
gsttrace.h \
gsttrashstack.h \
gsttypefind.h \

View file

@ -24,16 +24,17 @@ endif
libgstelements_la_DEPENDENCIES = ../libgstreamer-@GST_MAJORMINOR@.la
libgstelements_la_SOURCES = \
gstaggregator.c \
gstbufferstore.c \
gstelements.c \
gstfakesink.c \
gstfakesrc.c \
gstfilesink.c \
gstfakesink.c \
gstfilesrc.c \
gstidentity.c \
gstelements.c \
#gstaggregator.c \
gstbufferstore.c \
gstfakesink.c \
gstfilesink.c \
gstfdsink.c \
gstfdsrc.c \
gstidentity.c \
gstmd5sink.c \
$(multifilesrc) \
$(pipefilter) \

View file

@ -55,24 +55,24 @@ extern GType gst_filesrc_get_type (void);
extern GstElementDetails gst_filesrc_details;
static struct _elements_entry _elements[] = {
{"aggregator", GST_RANK_NONE, gst_aggregator_get_type},
// {"aggregator", GST_RANK_NONE, gst_aggregator_get_type},
{"fakesrc", GST_RANK_NONE, gst_fakesrc_get_type},
{"fakesink", GST_RANK_NONE, gst_fakesink_get_type},
{"fdsink", GST_RANK_NONE, gst_fdsink_get_type},
{"fdsrc", GST_RANK_NONE, gst_fdsrc_get_type},
{"filesrc", GST_RANK_NONE, gst_filesrc_get_type},
{"filesink", GST_RANK_NONE, gst_filesink_get_type},
{"identity", GST_RANK_NONE, gst_identity_get_type},
{"md5sink", GST_RANK_NONE, gst_md5sink_get_type},
// {"fdsink", GST_RANK_NONE, gst_fdsink_get_type},
// {"fdsrc", GST_RANK_NONE, gst_fdsrc_get_type},
// {"filesink", GST_RANK_NONE, gst_filesink_get_type},
// {"md5sink", GST_RANK_NONE, gst_md5sink_get_type},
#ifndef HAVE_WIN32
{"multifilesrc", GST_RANK_NONE, gst_multifilesrc_get_type},
{"pipefilter", GST_RANK_NONE, gst_pipefilter_get_type},
// {"multifilesrc", GST_RANK_NONE, gst_multifilesrc_get_type},
// {"pipefilter", GST_RANK_NONE, gst_pipefilter_get_type},
#endif
{"shaper", GST_RANK_NONE, gst_shaper_get_type},
{"statistics", GST_RANK_NONE, gst_statistics_get_type},
{"tee", GST_RANK_NONE, gst_tee_get_type},
{"typefind", GST_RANK_NONE, gst_type_find_element_get_type},
{NULL, 0},
// {"shaper", GST_RANK_NONE, gst_shaper_get_type},
// {"statistics", GST_RANK_NONE, gst_statistics_get_type},
// {"tee", GST_RANK_NONE, gst_tee_get_type},
// {"typefind", GST_RANK_NONE, gst_type_find_element_get_type},
// {NULL, 0},
};
static gboolean

View file

@ -113,7 +113,8 @@ static void gst_fakesink_get_property (GObject * object, guint prop_id,
static GstElementStateReturn gst_fakesink_change_state (GstElement * element);
static void gst_fakesink_chain (GstPad * pad, GstData * _data);
static GstFlowReturn gst_fakesink_chain (GstPad * pad, GstBuffer * buffer);
static gboolean gst_fakesink_event (GstPad * pad, GstEvent * event);
static guint gst_fakesink_signals[LAST_SIGNAL] = { 0 };
@ -138,6 +139,8 @@ gst_fakesink_class_init (GstFakeSinkClass * klass)
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_fakesink_set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_fakesink_get_property);
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_NUM_SINKS,
g_param_spec_int ("num_sinks", "Number of sinks",
@ -169,9 +172,6 @@ gst_fakesink_class_init (GstFakeSinkClass * klass)
gst_marshal_VOID__BOXED_OBJECT, G_TYPE_NONE, 2,
GST_TYPE_BUFFER | G_SIGNAL_TYPE_STATIC_SCOPE, GST_TYPE_PAD);
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_fakesink_set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_fakesink_get_property);
gstelement_class->request_new_pad =
GST_DEBUG_FUNCPTR (gst_fakesink_request_new_pad);
gstelement_class->set_clock = GST_DEBUG_FUNCPTR (gst_fakesink_set_clock);
@ -189,6 +189,7 @@ gst_fakesink_init (GstFakeSink * fakesink)
"sink");
gst_element_add_pad (GST_ELEMENT (fakesink), pad);
gst_pad_set_chain_function (pad, GST_DEBUG_FUNCPTR (gst_fakesink_chain));
gst_pad_set_event_function (pad, GST_DEBUG_FUNCPTR (gst_fakesink_event));
fakesink->silent = FALSE;
fakesink->dump = FALSE;
@ -196,8 +197,6 @@ gst_fakesink_init (GstFakeSink * fakesink)
fakesink->last_message = NULL;
fakesink->state_error = FAKESINK_STATE_ERROR_NONE;
fakesink->signal_handoffs = FALSE;
GST_FLAG_SET (fakesink, GST_ELEMENT_EVENT_AWARE);
}
static void
@ -308,45 +307,72 @@ gst_fakesink_get_property (GObject * object, guint prop_id, GValue * value,
}
}
static void
gst_fakesink_chain (GstPad * pad, GstData * _data)
static gboolean
gst_fakesink_event (GstPad * pad, GstEvent * event)
{
GstBuffer *buf = GST_BUFFER (_data);
GstFakeSink *fakesink;
g_return_if_fail (pad != NULL);
g_return_if_fail (GST_IS_PAD (pad));
g_return_if_fail (buf != NULL);
gboolean result = TRUE;
fakesink = GST_FAKESINK (GST_OBJECT_PARENT (pad));
if (GST_IS_EVENT (buf)) {
GstEvent *event = GST_EVENT (buf);
GST_STREAM_LOCK (pad);
if (!fakesink->silent) {
g_free (fakesink->last_message);
if (!fakesink->silent) {
g_free (fakesink->last_message);
fakesink->last_message =
g_strdup_printf ("chain ******* (%s:%s)E (type: %d) %p",
GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE (event), event);
fakesink->last_message =
g_strdup_printf ("chain ******* (%s:%s)E (type: %d) %p",
GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE (event), event);
g_object_notify (G_OBJECT (fakesink), "last_message");
}
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_DISCONTINUOUS:
if (fakesink->sync && fakesink->clock) {
gint64 value = GST_EVENT_DISCONT_OFFSET (event, 0).value;
gst_element_set_time (GST_ELEMENT (fakesink), value);
}
default:
gst_pad_event_default (pad, event);
break;
}
return;
g_object_notify (G_OBJECT (fakesink), "last_message");
}
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_EOS:
{
gst_element_finish_preroll (GST_ELEMENT (fakesink),
GST_STREAM_GET_LOCK (pad));
gst_pipeline_post_message (GST_ELEMENT_MANAGER (fakesink),
gst_message_new_eos (GST_OBJECT (fakesink)));
break;
}
case GST_EVENT_DISCONTINUOUS:
if (fakesink->sync && fakesink->clock) {
//gint64 value = GST_EVENT_DISCONT_OFFSET (event, 0).value;
}
default:
result = gst_pad_event_default (pad, event);
break;
}
GST_STREAM_UNLOCK (pad);
return result;
}
static GstFlowReturn
gst_fakesink_chain (GstPad * pad, GstBuffer * buffer)
{
GstBuffer *buf = GST_BUFFER (buffer);
GstFakeSink *fakesink;
GstFlowReturn result = GST_FLOW_OK;
GstCaps *caps;
fakesink = GST_FAKESINK (GST_OBJECT_PARENT (pad));
caps = gst_buffer_get_caps (buffer);
if (caps && caps != GST_PAD_CAPS (pad)) {
gst_pad_set_caps (pad, caps);
}
/* grab streaming lock to synchronize with event method */
GST_STREAM_LOCK (pad);
result =
gst_element_finish_preroll (GST_ELEMENT (fakesink),
GST_STREAM_GET_LOCK (pad));
if (result != GST_FLOW_OK)
goto exit;
if (fakesink->sync && fakesink->clock) {
gst_element_wait (GST_ELEMENT (fakesink), GST_BUFFER_TIMESTAMP (buf));
}
@ -374,12 +400,17 @@ gst_fakesink_chain (GstPad * pad, GstData * _data)
gst_util_dump_mem (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
}
exit:
GST_STREAM_UNLOCK (pad);
gst_buffer_unref (buf);
return result;
}
static GstElementStateReturn
gst_fakesink_change_state (GstElement * element)
{
GstElementStateReturn ret = GST_STATE_SUCCESS;
GstFakeSink *fakesink = GST_FAKESINK (element);
switch (GST_STATE_TRANSITION (element)) {
@ -390,6 +421,8 @@ gst_fakesink_change_state (GstElement * element)
case GST_STATE_READY_TO_PAUSED:
if (fakesink->state_error == FAKESINK_STATE_ERROR_READY_PAUSED)
goto error;
/* need to complete preroll before this state change completes */
ret = GST_STATE_ASYNC;
break;
case GST_STATE_PAUSED_TO_PLAYING:
if (fakesink->state_error == FAKESINK_STATE_ERROR_PAUSED_PLAYING)
@ -412,9 +445,9 @@ gst_fakesink_change_state (GstElement * element)
}
if (GST_ELEMENT_CLASS (parent_class)->change_state)
return GST_ELEMENT_CLASS (parent_class)->change_state (element);
GST_ELEMENT_CLASS (parent_class)->change_state (element);
return GST_STATE_SUCCESS;
return ret;
error:
GST_ELEMENT_ERROR (element, CORE, STATE_CHANGE, (NULL), (NULL));

View file

@ -180,6 +180,7 @@ GST_BOILERPLATE_FULL (GstFakeSrc, gst_fakesrc, GstElement, GST_TYPE_ELEMENT,
static GstPad *gst_fakesrc_request_new_pad (GstElement * element,
GstPadTemplate * templ, const gchar * unused);
static void gst_fakesrc_update_functions (GstFakeSrc * src);
static gboolean gst_fakesrc_activate (GstPad * pad, gboolean active);
static void gst_fakesrc_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_fakesrc_get_property (GObject * object, guint prop_id,
@ -188,7 +189,7 @@ static void gst_fakesrc_set_clock (GstElement * element, GstClock * clock);
static GstElementStateReturn gst_fakesrc_change_state (GstElement * element);
static GstData *gst_fakesrc_get (GstPad * pad);
static GstFlowReturn gst_fakesrc_get (GstPad * pad, GstBuffer ** buffer);
static void gst_fakesrc_loop (GstElement * element);
static guint gst_fakesrc_signals[LAST_SIGNAL] = { 0 };
@ -214,6 +215,8 @@ gst_fakesrc_class_init (GstFakeSrcClass * klass)
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_fakesrc_set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_fakesrc_get_property);
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_NUM_SOURCES,
g_param_spec_int ("num-sources", "num-sources", "Number of sources",
@ -281,9 +284,6 @@ gst_fakesrc_class_init (GstFakeSrcClass * klass)
gst_marshal_VOID__BOXED_OBJECT, G_TYPE_NONE, 2,
GST_TYPE_BUFFER | G_SIGNAL_TYPE_STATIC_SCOPE, GST_TYPE_PAD);
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_fakesrc_set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_fakesrc_get_property);
gstelement_class->request_new_pad =
GST_DEBUG_FUNCPTR (gst_fakesrc_request_new_pad);
gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_fakesrc_change_state);
@ -434,7 +434,7 @@ gst_fakesrc_event_handler (GstPad * pad, GstEvent * event)
{
GstFakeSrc *src;
src = GST_FAKESRC (gst_pad_get_parent (pad));
src = GST_FAKESRC (gst_object_get_parent (GST_OBJECT (pad)));
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEEK:
@ -467,13 +467,6 @@ gst_fakesrc_update_functions (GstFakeSrc * src)
{
GList *pads;
if (src->loop_based) {
gst_element_set_loop_function (GST_ELEMENT (src),
GST_DEBUG_FUNCPTR (gst_fakesrc_loop));
} else {
gst_element_set_loop_function (GST_ELEMENT (src), NULL);
}
pads = GST_ELEMENT (src)->pads;
while (pads) {
GstPad *pad = GST_PAD (pads->data);
@ -484,6 +477,7 @@ gst_fakesrc_update_functions (GstFakeSrc * src)
gst_pad_set_get_function (pad, GST_DEBUG_FUNCPTR (gst_fakesrc_get));
}
gst_pad_set_activate_function (pad, gst_fakesrc_activate);
gst_pad_set_event_function (pad, gst_fakesrc_event_handler);
gst_pad_set_event_mask_function (pad, gst_fakesrc_get_event_mask);
gst_pad_set_query_function (pad, gst_fakesrc_query);
@ -790,36 +784,40 @@ gst_fakesrc_create_buffer (GstFakeSrc * src)
return buf;
}
static GstData *
gst_fakesrc_get (GstPad * pad)
static GstFlowReturn
gst_fakesrc_get (GstPad * pad, GstBuffer ** buffer)
{
GstFakeSrc *src;
GstBuffer *buf;
GstClockTime time;
GstFlowReturn result = GST_FLOW_OK;
g_return_val_if_fail (pad != NULL, NULL);
g_return_val_if_fail (pad != NULL, GST_FLOW_ERROR);
src = GST_FAKESRC (GST_OBJECT_PARENT (pad));
g_return_val_if_fail (GST_IS_FAKESRC (src), NULL);
g_return_val_if_fail (GST_IS_FAKESRC (src), GST_FLOW_ERROR);
GST_STREAM_LOCK (pad);
if (src->need_flush) {
src->need_flush = FALSE;
return GST_DATA (gst_event_new (GST_EVENT_FLUSH));
gst_pad_push_event (pad, gst_event_new (GST_EVENT_FLUSH));
}
if (src->buffer_count == src->segment_end) {
if (src->segment_loop) {
return GST_DATA (gst_event_new (GST_EVENT_SEGMENT_DONE));
gst_pad_push_event (pad, gst_event_new (GST_EVENT_SEGMENT_DONE));
} else {
gst_element_set_eos (GST_ELEMENT (src));
return GST_DATA (gst_event_new (GST_EVENT_EOS));
gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS));
result = GST_FLOW_UNEXPECTED;
goto done;
}
}
if (src->rt_num_buffers == 0) {
gst_element_set_eos (GST_ELEMENT (src));
return GST_DATA (gst_event_new (GST_EVENT_EOS));
gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS));
result = GST_FLOW_UNEXPECTED;
goto done;
} else {
if (src->rt_num_buffers > 0)
src->rt_num_buffers--;
@ -827,8 +825,9 @@ gst_fakesrc_get (GstPad * pad)
if (src->eos) {
GST_INFO ("fakesrc is setting eos on pad");
gst_element_set_eos (GST_ELEMENT (src));
return GST_DATA (gst_event_new (GST_EVENT_EOS));
gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS));
result = GST_FLOW_UNEXPECTED;
goto done;
}
buf = gst_fakesrc_create_buffer (src);
@ -867,7 +866,12 @@ gst_fakesrc_get (GstPad * pad)
src->bytes_sent += GST_BUFFER_SIZE (buf);
return GST_DATA (buf);
*buffer = buf;
done:
GST_STREAM_UNLOCK (pad);
return result;
}
/**
@ -881,35 +885,82 @@ gst_fakesrc_loop (GstElement * element)
{
GstFakeSrc *src;
const GList *pads;
GstTask *task;
g_return_if_fail (element != NULL);
g_return_if_fail (GST_IS_FAKESRC (element));
src = GST_FAKESRC (element);
task = src->task;
pads = gst_element_get_pad_list (element);
pads = element->pads;
while (pads) {
GstPad *pad = GST_PAD (pads->data);
GstData *data;
GstBuffer *buffer;
GstFlowReturn ret;
data = gst_fakesrc_get (pad);
gst_pad_push (pad, data);
if (src->eos) {
ret = gst_fakesrc_get (pad, &buffer);
if (ret != GST_FLOW_OK) {
gst_task_stop (task);
return;
}
ret = gst_pad_push (pad, buffer);
if (ret != GST_FLOW_OK) {
gst_task_stop (task);
return;
}
if (src->eos) {
gst_task_stop (task);
return;
}
pads = g_list_next (pads);
}
}
static gboolean
gst_fakesrc_activate (GstPad * pad, gboolean active)
{
gboolean result = FALSE;
GstFakeSrc *fakesrc;
fakesrc = GST_FAKESRC (GST_OBJECT_PARENT (pad));
if (active) {
/* if we have a scheduler we can start the task */
if (GST_ELEMENT_MANAGER (fakesrc)) {
GST_STREAM_LOCK (pad);
fakesrc->task =
gst_scheduler_create_task (GST_ELEMENT_MANAGER (fakesrc)->scheduler,
(GstTaskFunction) gst_fakesrc_loop, fakesrc);
gst_task_start (fakesrc->task);
GST_STREAM_UNLOCK (pad);
result = TRUE;
}
} else {
/* step 1, unblock clock sync (if any) */
/* step 2, make sure streaming finishes */
GST_STREAM_LOCK (pad);
/* step 3, stop the task */
gst_task_stop (fakesrc->task);
gst_object_unref (GST_OBJECT (fakesrc->task));
GST_STREAM_UNLOCK (pad);
result = TRUE;
}
return result;
}
static GstElementStateReturn
gst_fakesrc_change_state (GstElement * element)
{
GstFakeSrc *fakesrc;
GstElementStateReturn result = GST_STATE_FAILURE;
g_return_val_if_fail (GST_IS_FAKESRC (element), GST_STATE_FAILURE);
g_return_val_if_fail (GST_IS_FAKESRC (element), result);
fakesrc = GST_FAKESRC (element);
@ -917,6 +968,7 @@ gst_fakesrc_change_state (GstElement * element)
case GST_STATE_NULL_TO_READY:
break;
case GST_STATE_READY_TO_PAUSED:
{
fakesrc->buffer_count = 0;
fakesrc->pattern_byte = 0x00;
fakesrc->need_flush = FALSE;
@ -924,7 +976,14 @@ gst_fakesrc_change_state (GstElement * element)
fakesrc->bytes_sent = 0;
fakesrc->rt_num_buffers = fakesrc->num_buffers;
break;
}
case GST_STATE_PAUSED_TO_PLAYING:
break;
}
result = GST_ELEMENT_CLASS (parent_class)->change_state (element);
switch (GST_STATE_TRANSITION (element)) {
case GST_STATE_PLAYING_TO_PAUSED:
break;
case GST_STATE_PAUSED_TO_READY:
@ -941,8 +1000,5 @@ gst_fakesrc_change_state (GstElement * element)
break;
}
if (GST_ELEMENT_CLASS (parent_class)->change_state)
return GST_ELEMENT_CLASS (parent_class)->change_state (element);
return GST_STATE_SUCCESS;
return result;
}

View file

@ -79,6 +79,8 @@ struct _GstFakeSrc {
gboolean loop_based;
gboolean eos;
GstTask *task;
GstFakeSrcOutputType output;
GstFakeSrcDataType data;
GstFakeSrcSizeType sizetype;

View file

@ -170,11 +170,12 @@ static void gst_filesrc_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static gboolean gst_filesrc_check_filesize (GstFileSrc * src);
static GstData *gst_filesrc_get (GstPad * pad);
static GstFlowReturn gst_filesrc_get (GstPad * pad, GstBuffer ** buffer);
static gboolean gst_filesrc_srcpad_event (GstPad * pad, GstEvent * event);
static gboolean gst_filesrc_srcpad_query (GstPad * pad, GstQueryType type,
GstFormat * format, gint64 * value);
static gboolean gst_filesrc_activate (GstPad * pad, gboolean active);
static GstElementStateReturn gst_filesrc_change_state (GstElement * element);
static void gst_filesrc_uri_handler_init (gpointer g_iface,
@ -214,6 +215,8 @@ gst_filesrc_class_init (GstFileSrcClass * klass)
gobject_class = (GObjectClass *) klass;
gobject_class->set_property = gst_filesrc_set_property;
gobject_class->get_property = gst_filesrc_get_property;
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FD,
g_param_spec_int ("fd", "File-descriptor",
@ -235,8 +238,6 @@ gst_filesrc_class_init (GstFileSrcClass * klass)
"Touch data to force disk read", FALSE, G_PARAM_READWRITE));
gobject_class->dispose = gst_filesrc_dispose;
gobject_class->set_property = gst_filesrc_set_property;
gobject_class->get_property = gst_filesrc_get_property;
gstelement_class->change_state = gst_filesrc_change_state;
}
@ -248,6 +249,7 @@ gst_filesrc_init (GstFileSrc * src)
gst_pad_new_from_template (gst_static_pad_template_get (&srctemplate),
"src");
gst_pad_set_get_function (src->srcpad, gst_filesrc_get);
gst_pad_set_activate_function (src->srcpad, gst_filesrc_activate);
gst_pad_set_event_function (src->srcpad, gst_filesrc_srcpad_event);
gst_pad_set_event_mask_function (src->srcpad, gst_filesrc_get_event_mask);
gst_pad_set_query_function (src->srcpad, gst_filesrc_srcpad_query);
@ -672,7 +674,7 @@ gst_filesrc_get_read (GstFileSrc * src)
if (ret == 0) {
GST_DEBUG ("non-regular file hits EOS");
gst_buffer_unref (buf);
gst_element_set_eos (GST_ELEMENT (src));
//gst_element_set_eos (GST_ELEMENT (src));
return GST_DATA (gst_event_new (GST_EVENT_EOS));
}
readsize = ret;
@ -686,20 +688,22 @@ gst_filesrc_get_read (GstFileSrc * src)
return GST_DATA (buf);
}
static GstData *
gst_filesrc_get (GstPad * pad)
static GstFlowReturn
gst_filesrc_get (GstPad * pad, GstBuffer ** buffer)
{
GstFileSrc *src;
GstData *data;
g_return_val_if_fail (pad != NULL, NULL);
src = GST_FILESRC (gst_pad_get_parent (pad));
g_return_val_if_fail (GST_FLAG_IS_SET (src, GST_FILESRC_OPEN), NULL);
g_return_val_if_fail (GST_FLAG_IS_SET (src, GST_FILESRC_OPEN),
GST_FLOW_WRONG_STATE);
/* check for flush */
if (src->need_flush) {
src->need_flush = FALSE;
GST_DEBUG_OBJECT (src, "sending flush");
return GST_DATA (gst_event_new_flush ());
gst_pad_push_event (pad, gst_event_new_flush ());
}
/* check for seek */
if (src->need_discont) {
@ -710,7 +714,7 @@ gst_filesrc_get (GstPad * pad)
gst_event_new_discontinuous (src->need_discont > 1, GST_FORMAT_BYTES,
(guint64) src->curoffset, GST_FORMAT_UNDEFINED);
src->need_discont = 0;
return GST_DATA (event);
gst_pad_push_event (pad, event);
}
/* check for EOF if it's a regular file */
@ -721,20 +725,31 @@ gst_filesrc_get (GstPad * pad)
GST_DEBUG_OBJECT (src, "eos %" G_GINT64_FORMAT " %" G_GINT64_FORMAT,
src->curoffset, src->filelen);
}
gst_element_set_eos (GST_ELEMENT (src));
return GST_DATA (gst_event_new (GST_EVENT_EOS));
//gst_element_set_eos (GST_ELEMENT (src));
gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS));
return GST_FLOW_WRONG_STATE;
}
}
#ifdef HAVE_MMAP
if (src->using_mmap) {
return gst_filesrc_get_mmap (src);
data = gst_filesrc_get_mmap (src);
} else {
return gst_filesrc_get_read (src);
data = gst_filesrc_get_read (src);
}
#else
return gst_filesrc_get_read (src);
data = gst_filesrc_get_read (src);
#endif
if (data == NULL)
return GST_FLOW_ERROR;
if (GST_IS_EVENT (data)) {
gst_pad_push_event (pad, GST_EVENT (data));
} else {
*buffer = GST_BUFFER (data);
}
return GST_FLOW_OK;
}
/* TRUE if the filesize of the file was updated */
@ -848,10 +863,69 @@ gst_filesrc_close_file (GstFileSrc * src)
GST_FLAG_UNSET (src, GST_FILESRC_OPEN);
}
static void
gst_filesrc_loop (GstElement * element)
{
GstFileSrc *filesrc;
GstFlowReturn result;
GstBuffer *buffer;
filesrc = GST_FILESRC (element);
result = gst_filesrc_get (filesrc->srcpad, &buffer);
if (result != GST_FLOW_OK) {
gst_task_stop (filesrc->task);
return;
}
result = gst_pad_push (filesrc->srcpad, buffer);
if (result != GST_FLOW_OK) {
gst_task_stop (filesrc->task);
}
}
static gboolean
gst_filesrc_activate (GstPad * pad, gboolean active)
{
gboolean result = FALSE;
GstFileSrc *filesrc;
filesrc = GST_FILESRC (GST_OBJECT_PARENT (pad));
if (active) {
/* try to start the task */
GST_STREAM_LOCK (pad);
filesrc->task = gst_element_create_task (GST_ELEMENT (filesrc),
(GstTaskFunction) gst_filesrc_loop, filesrc);
if (filesrc->task) {
gst_task_start (filesrc->task);
result = TRUE;
} else {
result = FALSE;
}
GST_STREAM_UNLOCK (pad);
} else {
/* step 1, unblock clock sync (if any) */
/* step 2, make sure streaming finishes */
GST_STREAM_LOCK (pad);
/* step 3, stop the task */
gst_task_stop (filesrc->task);
gst_object_unref (GST_OBJECT (filesrc->task));
GST_STREAM_UNLOCK (pad);
result = TRUE;
}
return result;
}
static GstElementStateReturn
gst_filesrc_change_state (GstElement * element)
{
GstElementStateReturn result = GST_STATE_SUCCESS;
GstFileSrc *src = GST_FILESRC (element);
switch (GST_STATE_TRANSITION (element)) {
@ -865,6 +939,16 @@ gst_filesrc_change_state (GstElement * element)
return GST_STATE_FAILURE;
}
src->need_discont = 2;
break;
case GST_STATE_PAUSED_TO_PLAYING:
break;
}
result = GST_ELEMENT_CLASS (parent_class)->change_state (element);
switch (GST_STATE_TRANSITION (element)) {
case GST_STATE_PLAYING_TO_PAUSED:
break;
case GST_STATE_PAUSED_TO_READY:
if (GST_FLAG_IS_SET (element, GST_FILESRC_OPEN))
@ -874,10 +958,7 @@ gst_filesrc_change_state (GstElement * element)
break;
}
if (GST_ELEMENT_CLASS (parent_class)->change_state)
return GST_ELEMENT_CLASS (parent_class)->change_state (element);
return GST_STATE_SUCCESS;
return result;
}
static gboolean

View file

@ -69,6 +69,8 @@ struct _GstFileSrc {
gboolean is_regular; /* whether it's (symlink to)
a regular file */
GstTask *task;
GstBuffer *mapbuf;
size_t mapsize;

View file

@ -99,7 +99,8 @@ static void gst_identity_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static GstElementStateReturn gst_identity_change_state (GstElement * element);
static void gst_identity_chain (GstPad * pad, GstData * _data);
static gboolean gst_identity_event (GstPad * pad, GstEvent * event);
static GstFlowReturn gst_identity_chain (GstPad * pad, GstBuffer * buffer);
static void gst_identity_set_clock (GstElement * element, GstClock * clock);
@ -138,6 +139,9 @@ gst_identity_class_init (GstIdentityClass * klass)
gobject_class = G_OBJECT_CLASS (klass);
gstelement_class = GST_ELEMENT_CLASS (klass);
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_identity_set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_identity_get_property);
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LOOP_BASED,
g_param_spec_boolean ("loop-based", "Loop-based",
"Set to TRUE to use loop-based rather than chain-based scheduling",
@ -185,8 +189,6 @@ gst_identity_class_init (GstIdentityClass * klass)
GST_TYPE_BUFFER | G_SIGNAL_TYPE_STATIC_SCOPE);
gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_identity_finalize);
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_identity_set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_identity_get_property);
gstelement_class->set_clock = GST_DEBUG_FUNCPTR (gst_identity_set_clock);
gstelement_class->change_state =
@ -203,15 +205,13 @@ gst_identity_init (GstIdentity * identity)
gst_element_add_pad (GST_ELEMENT (identity), identity->sinkpad);
gst_pad_set_chain_function (identity->sinkpad,
GST_DEBUG_FUNCPTR (gst_identity_chain));
gst_pad_set_link_function (identity->sinkpad, gst_pad_proxy_pad_link);
gst_pad_set_getcaps_function (identity->sinkpad, gst_pad_proxy_getcaps);
gst_pad_set_event_function (identity->sinkpad,
GST_DEBUG_FUNCPTR (gst_identity_event));
identity->srcpad =
gst_pad_new_from_template (gst_static_pad_template_get (&srctemplate),
"src");
gst_element_add_pad (GST_ELEMENT (identity), identity->srcpad);
gst_pad_set_link_function (identity->srcpad, gst_pad_proxy_pad_link);
gst_pad_set_getcaps_function (identity->srcpad, gst_pad_proxy_getcaps);
identity->loop_based = DEFAULT_LOOP_BASED;
identity->sleep_time = DEFAULT_SLEEP_TIME;
@ -225,8 +225,6 @@ gst_identity_init (GstIdentity * identity)
identity->dump = DEFAULT_DUMP;
identity->last_message = NULL;
identity->srccaps = NULL;
GST_FLAG_SET (identity, GST_ELEMENT_EVENT_AWARE);
}
static void
@ -237,35 +235,37 @@ gst_identity_set_clock (GstElement * element, GstClock * clock)
gst_object_replace ((GstObject **) & identity->clock, (GstObject *) clock);
}
static void
gst_identity_chain (GstPad * pad, GstData * _data)
static gboolean
gst_identity_event (GstPad * pad, GstEvent * event)
{
GstBuffer *buf = GST_BUFFER (_data);
GstIdentity *identity;
guint i;
g_return_if_fail (pad != NULL);
g_return_if_fail (GST_IS_PAD (pad));
g_return_if_fail (buf != NULL);
identity = GST_IDENTITY (gst_pad_get_parent (pad));
if (GST_IS_EVENT (buf)) {
GstEvent *event = GST_EVENT (buf);
if (!identity->silent) {
g_free (identity->last_message);
if (!identity->silent) {
g_free (identity->last_message);
identity->last_message =
g_strdup_printf ("chain ******* (%s:%s)E (type: %d) %p",
GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE (event), event);
identity->last_message =
g_strdup_printf ("chain ******* (%s:%s)E (type: %d) %p",
GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE (event), event);
g_object_notify (G_OBJECT (identity), "last_message");
}
gst_pad_event_default (pad, event);
return;
g_object_notify (G_OBJECT (identity), "last_message");
}
return gst_pad_push_event (identity->srcpad, event);
}
static GstFlowReturn
gst_identity_chain (GstPad * pad, GstBuffer * buffer)
{
GstBuffer *buf = GST_BUFFER (buffer);
GstIdentity *identity;
guint i;
g_return_val_if_fail (pad != NULL, GST_FLOW_ERROR);
g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR);
g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
identity = GST_IDENTITY (gst_pad_get_parent (pad));
/* see if we need to do perfect stream checking */
/* invalid timestamp drops us out of check. FIXME: maybe warn ? */
@ -302,7 +302,7 @@ gst_identity_chain (GstPad * pad, GstData * _data)
gst_buffer_unref (buf);
GST_ELEMENT_ERROR (identity, CORE, FAILED,
(_("Failed after iterations as requested.")), (NULL));
return;
return GST_FLOW_ERROR;
}
}
@ -319,7 +319,7 @@ gst_identity_chain (GstPad * pad, GstData * _data)
GST_BUFFER_OFFSET_END (buf), GST_BUFFER_FLAGS (buf), buf);
g_object_notify (G_OBJECT (identity), "last-message");
gst_buffer_unref (buf);
return;
return GST_FLOW_OK;
}
}
if (identity->dump) {
@ -365,37 +365,33 @@ gst_identity_chain (GstPad * pad, GstData * _data)
}
identity->bytes_handled += GST_BUFFER_SIZE (buf);
gst_pad_push (identity->srcpad, GST_DATA (buf));
gst_pad_push (identity->srcpad, buf);
if (identity->sleep_time)
g_usleep (identity->sleep_time);
}
return GST_FLOW_OK;
}
#if 0
static void
gst_identity_loop (GstElement * element)
{
GstIdentity *identity;
GstBuffer *buf;
GstFlowReturn ret;
g_return_if_fail (element != NULL);
g_return_if_fail (GST_IS_IDENTITY (element));
identity = GST_IDENTITY (element);
buf = GST_BUFFER (gst_pad_pull (identity->sinkpad));
if (GST_IS_EVENT (buf)) {
GstEvent *event = GST_EVENT (buf);
if (GST_EVENT_IS_INTERRUPT (event)) {
gst_event_unref (event);
} else {
gst_pad_event_default (identity->sinkpad, event);
}
} else {
gst_identity_chain (identity->sinkpad, GST_DATA (buf));
ret = gst_pad_pull (identity->sinkpad, &buf);
if (ret == GST_FLOW_OK) {
gst_identity_chain (identity->sinkpad, buf);
}
}
#endif
static void
gst_identity_set_property (GObject * object, guint prop_id,
@ -412,12 +408,9 @@ gst_identity_set_property (GObject * object, guint prop_id,
case ARG_LOOP_BASED:
identity->loop_based = g_value_get_boolean (value);
if (identity->loop_based) {
gst_element_set_loop_function (GST_ELEMENT (identity),
gst_identity_loop);
gst_pad_set_chain_function (identity->sinkpad, NULL);
} else {
gst_pad_set_chain_function (identity->sinkpad, gst_identity_chain);
gst_element_set_loop_function (GST_ELEMENT (identity), NULL);
}
break;
case ARG_SLEEP_TIME:

View file

@ -535,9 +535,8 @@ gst_register_core_elements (GstPlugin * plugin)
GST_TYPE_BIN) ||
!gst_element_register (plugin, "pipeline", GST_RANK_PRIMARY,
GST_TYPE_PIPELINE) ||
!gst_element_register (plugin, "thread", GST_RANK_PRIMARY,
GST_TYPE_THREAD) ||
!gst_element_register (plugin, "queue", GST_RANK_NONE, GST_TYPE_QUEUE))
!gst_element_register (plugin, "queue", GST_RANK_NONE, GST_TYPE_QUEUE)
)
g_assert_not_reached ();
return TRUE;
@ -616,6 +615,7 @@ init_post (void)
_gst_plugin_initialize ();
_gst_event_initialize ();
_gst_buffer_initialize ();
_gst_message_initialize ();
_gst_tag_initialize ();
#ifndef GST_DISABLE_REGISTRY
@ -840,50 +840,6 @@ gst_has_threads (void)
return TRUE;
}
static GSList *mainloops = NULL;
/**
* gst_main:
*
* Enters the main GStreamer processing loop.
*
* This function duplicates functionality in glib, and will be removed
* during the 0.9 development series.
*/
void
gst_main (void)
{
GMainLoop *loop;
loop = g_main_loop_new (NULL, FALSE);
mainloops = g_slist_prepend (mainloops, loop);
g_main_loop_run (loop);
}
/**
* gst_main_quit:
*
* Exits the main GStreamer processing loop.
*
* This function duplicates functionality in glib, and will be removed
* during the 0.9 development series.
*/
void
gst_main_quit (void)
{
if (!mainloops)
g_error ("Quit more loops than there are");
else {
GMainLoop *loop = mainloops->data;
mainloops = g_slist_delete_link (mainloops, mainloops);
g_main_loop_quit (loop);
g_main_loop_unref (loop);
}
}
/**
* gst_version:
* @major: pointer to a guint to store the major version number

View file

@ -42,7 +42,9 @@
#include <gst/gstindex.h>
#include <gst/gstinfo.h>
#include <gst/gstinterface.h>
#include <gst/gstiterator.h>
#include <gst/gstmarshal.h>
#include <gst/gstmessage.h>
#include <gst/gstobject.h>
#include <gst/gstpad.h>
#include <gst/gstpipeline.h>
@ -52,7 +54,7 @@
#include <gst/gstsystemclock.h>
#include <gst/gsttag.h>
#include <gst/gsttaginterface.h>
#include <gst/gstthread.h>
#include <gst/gsttask.h>
#include <gst/gsttrace.h>
#include <gst/gsttypefind.h>
#include <gst/gsturi.h>

View file

@ -69,6 +69,7 @@ extern GstDebugCategory *GST_CAT_NEGOTIATION;
extern GstDebugCategory *GST_CAT_REFCOUNTING;
extern GstDebugCategory *GST_CAT_ERROR_SYSTEM;
extern GstDebugCategory *GST_CAT_EVENT;
extern GstDebugCategory *GST_CAT_MESSAGE;
extern GstDebugCategory *GST_CAT_PARAMS;
extern GstDebugCategory *GST_CAT_CALL_TRACE;
@ -99,6 +100,7 @@ extern GstDebugCategory *GST_CAT_CALL_TRACE;
#define GST_CAT_REFCOUNTING NULL
#define GST_CAT_ERROR_SYSTEM NULL
#define GST_CAT_EVENT NULL
#define GST_CAT_MESSAGE NULL
#define GST_CAT_PARAMS NULL
#define GST_CAT_CALL_TRACE NULL

File diff suppressed because it is too large Load diff

View file

@ -25,6 +25,7 @@
#define __GST_BIN_H__
#include <gst/gstelement.h>
#include <gst/gstiterator.h>
G_BEGIN_DECLS
@ -38,15 +39,6 @@ GST_EXPORT GType _gst_bin_type;
#define GST_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_BIN, GstBinClass))
typedef enum {
/* this bin is a manager of child elements, i.e. a pipeline or thread */
GST_BIN_FLAG_MANAGER = GST_ELEMENT_FLAG_LAST,
/* this bin iterates itself */
GST_BIN_SELF_SCHEDULABLE,
/* we prefer to have cothreads when its an option, over chain-based */
GST_BIN_FLAG_PREFER_COTHREADS,
GST_BIN_FLAG_FIXED_CLOCK,
/* padding */
@ -62,8 +54,7 @@ struct _GstBin {
/* our children */
gint numchildren;
GList *children;
GstElementState child_states[GST_NUM_STATES];
guint32 children_cookie;
gpointer _gst_reserved[GST_PADDING];
};
@ -74,11 +65,7 @@ struct _GstBinClass {
/* vtable */
void (*add_element) (GstBin *bin, GstElement *element);
void (*remove_element) (GstBin *bin, GstElement *element);
void (*child_state_change) (GstBin *bin, GstElementState oldstate,
GstElementState newstate, GstElement *element);
/* run a full iteration of operation */
gboolean (*iterate) (GstBin *bin);
GstIterator* (*iterate_elements) (GstBin *bin);
/* signals */
void (*element_added) (GstBin *bin, GstElement *child);
@ -92,30 +79,17 @@ GstElement* gst_bin_new (const gchar *name);
/* add and remove elements from the bin */
void gst_bin_add (GstBin *bin, GstElement *element);
void gst_bin_add_many (GstBin *bin, GstElement *element_1, ...);
void gst_bin_remove (GstBin *bin, GstElement *element);
void gst_bin_remove_many (GstBin *bin, GstElement *element_1, ...);
/* retrieve a single element or the list of children */
GstElement* gst_bin_get_by_name (GstBin *bin, const gchar *name);
GstElement* gst_bin_get_by_name_recurse_up (GstBin *bin, const gchar *name);
G_CONST_RETURN GList*
gst_bin_get_list (GstBin *bin);
GstElement* gst_bin_get_by_interface (GstBin *bin, GType interface);
GList * gst_bin_get_all_by_interface (GstBin *bin, GType interface);
/* retrieve a single child */
GstElement* gst_bin_get_by_name (GstBin *bin, const gchar *name);
GstElement* gst_bin_get_by_name_recurse_up (GstBin *bin, const gchar *name);
GstElement* gst_bin_get_by_interface (GstBin *bin, GType interface);
gboolean gst_bin_iterate (GstBin *bin);
void gst_bin_use_clock (GstBin *bin, GstClock *clock);
GstClock* gst_bin_get_clock (GstBin *bin);
void gst_bin_auto_clock (GstBin *bin);
GstElementStateReturn gst_bin_sync_children_state (GstBin *bin);
/* internal */
/* one of our childs signaled a state change */
void gst_bin_child_state_change (GstBin *bin, GstElementState oldstate,
GstElementState newstate, GstElement *child);
/* retrieve multiple children */
GstIterator* gst_bin_iterate_elements (GstBin *bin);
GstIterator* gst_bin_iterate_sinks (GstBin *bin);
GstIterator* gst_bin_iterate_all_by_interface (GstBin *bin, GType interface);
G_END_DECLS

View file

@ -74,6 +74,8 @@ _gst_buffer_sub_free (GstBuffer * buffer)
GST_BUFFER_DATA (buffer) = NULL;
GST_BUFFER_SIZE (buffer) = 0;
if (GST_BUFFER_CAPS (buffer))
gst_caps_unref (GST_BUFFER_CAPS (buffer));
_GST_DATA_DISPOSE (GST_DATA (buffer));
@ -102,37 +104,21 @@ gst_buffer_default_free (GstBuffer * buffer)
/* set to safe values */
GST_BUFFER_DATA (buffer) = NULL;
GST_BUFFER_SIZE (buffer) = 0;
if (GST_BUFFER_CAPS (buffer))
gst_caps_unref (GST_BUFFER_CAPS (buffer));
_GST_DATA_DISPOSE (GST_DATA (buffer));
gst_buffer_free_chunk (buffer);
}
/**
* gst_buffer_stamp:
* @dest: buffer to stamp
* @src: buffer to stamp from
*
* Copies additional information (timestamps and offsets) from one buffer to
* the other.
*/
void
gst_buffer_stamp (GstBuffer * dest, const GstBuffer * src)
{
g_return_if_fail (dest != NULL);
g_return_if_fail (src != NULL);
GST_BUFFER_TIMESTAMP (dest) = GST_BUFFER_TIMESTAMP (src);
GST_BUFFER_DURATION (dest) = GST_BUFFER_DURATION (src);
GST_BUFFER_OFFSET (dest) = GST_BUFFER_OFFSET (src);
GST_BUFFER_OFFSET_END (dest) = GST_BUFFER_OFFSET_END (src);
}
/**
* gst_buffer_default_copy:
* @buffer: a #GstBuffer to make a copy of.
*
* Make a full newly allocated copy of the given buffer, data and all.
* Note that the caps on the buffer are not copied but their refcount
* is increased.
*
* Returns: the new #GstBuffer.
*/
@ -147,6 +133,8 @@ gst_buffer_default_copy (GstBuffer * buffer)
/* create a fresh new buffer */
copy = gst_buffer_alloc_chunk ();
GST_CAT_LOG (GST_CAT_BUFFER, "copy %p to %p", buffer, copy);
/* copy relevant flags */
flags = GST_DATA_FLAG_SHIFT (GST_BUFFER_KEY_UNIT) |
GST_DATA_FLAG_SHIFT (GST_BUFFER_IN_CAPS) |
@ -165,9 +153,15 @@ gst_buffer_default_copy (GstBuffer * buffer)
GST_BUFFER_SIZE (copy) = GST_BUFFER_SIZE (buffer);
GST_BUFFER_MAXSIZE (copy) = GST_BUFFER_SIZE (buffer);
gst_buffer_stamp (copy, buffer);
GST_BUFFER_TIMESTAMP (copy) = GST_BUFFER_TIMESTAMP (buffer);
GST_BUFFER_DURATION (copy) = GST_BUFFER_DURATION (buffer);
GST_BUFFER_OFFSET (copy) = GST_BUFFER_OFFSET (buffer);
GST_BUFFER_OFFSET_END (copy) = GST_BUFFER_OFFSET_END (buffer);
GST_BUFFER_FREE_DATA_FUNC (copy) = NULL;
GST_BUFFER_PRIVATE (copy) = NULL;
if (GST_BUFFER_CAPS (buffer))
GST_BUFFER_CAPS (copy) = gst_caps_ref (GST_BUFFER_CAPS (buffer));
return copy;
}
@ -225,6 +219,7 @@ gst_buffer_new (void)
GST_BUFFER_OFFSET_END (newbuf) = GST_BUFFER_OFFSET_NONE;
GST_BUFFER_FREE_DATA_FUNC (newbuf) = NULL;
GST_BUFFER_PRIVATE (newbuf) = NULL;
GST_BUFFER_CAPS (newbuf) = NULL;
return newbuf;
}
@ -251,6 +246,52 @@ gst_buffer_new_and_alloc (guint size)
return newbuf;
}
/**
* gst_buffer_get_caps:
* @buffer: a #GstBuffer to get the caps of.
*
* Gets the media type of the buffer. This can be NULL if there
* is not media type attached to this buffer or when the media
* type is the same as the previous received buffer.
*
* Returns: the #GstCaps, or NULL if there was an error or there
* were no caps on this buffer.
*/
GstCaps *
gst_buffer_get_caps (GstBuffer * buffer)
{
g_return_val_if_fail (buffer != NULL, NULL);
return GST_BUFFER_CAPS (buffer);
}
/**
* gst_buffer_set_caps:
* @buffer: a #GstBuffer to set the caps of.
* @caps: a #GstCaps to set.
*
* Sets the media type on the buffer. The caps' refcount will
* be increased and any previous caps on the buffer will be
* unreffed.
*/
void
gst_buffer_set_caps (GstBuffer * buffer, GstCaps * caps)
{
g_return_if_fail (buffer != NULL);
/* unref old caps if any */
if (GST_BUFFER_CAPS (buffer)) {
gst_caps_unref (GST_BUFFER_CAPS (buffer));
}
/* ref new caps if any */
if (caps)
caps = gst_caps_ref (caps);
/* set caps */
GST_BUFFER_CAPS (buffer) = caps;
}
/**
* gst_buffer_create_sub:
* @parent: a parent #GstBuffer to create a subbuffer from.
@ -318,12 +359,10 @@ gst_buffer_create_sub (GstBuffer * parent, guint offset, guint size)
GST_BUFFER_DURATION (buffer) = GST_CLOCK_TIME_NONE;
GST_BUFFER_OFFSET_END (buffer) = GST_BUFFER_OFFSET_NONE;
if (GST_BUFFER_FLAG_IS_SET (parent, GST_BUFFER_DONTKEEP)) {
GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_DONTKEEP);
}
if (GST_BUFFER_FLAG_IS_SET (parent, GST_BUFFER_READONLY)) {
GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_READONLY);
}
GST_BUFFER_CAPS (buffer) = NULL;
return buffer;
}
@ -338,9 +377,6 @@ gst_buffer_create_sub (GstBuffer * parent, guint offset, guint size)
* buffers. The original source buffers will not be modified or
* unref'd.
*
* WARNING: Incorrect use of this function can lead to memory leaks.
* It is recommended to use gst_buffer_join() instead of this function.
*
* If the buffers point to contiguous areas of memory, the buffer
* is created without copying the data.
*
@ -357,33 +393,6 @@ gst_buffer_merge (GstBuffer * buf1, GstBuffer * buf2)
return result;
}
/**
* gst_buffer_join:
* @buf1: a first source #GstBuffer to merge.
* @buf2: the second source #GstBuffer to merge.
*
* Create a new buffer that is the concatenation of the two source
* buffers. The original buffers are unreferenced.
*
* If the buffers point to contiguous areas of memory, the buffer
* is created without copying the data.
*
* Returns: the new #GstBuffer that's the concatenation of the source buffers.
*/
GstBuffer *
gst_buffer_join (GstBuffer * buf1, GstBuffer * buf2)
{
GstBuffer *result;
/* we're just a specific case of the more general gst_buffer_span() */
result = gst_buffer_span (buf1, 0, buf2, buf1->size + buf2->size);
gst_buffer_unref (buf1);
gst_buffer_unref (buf2);
return result;
}
/**
* gst_buffer_is_span_fast:
* @buf1: a first source #GstBuffer.

View file

@ -26,6 +26,7 @@
#include <gst/gstdata.h>
#include <gst/gstclock.h>
#include <gst/gstcaps.h>
G_BEGIN_DECLS
@ -44,10 +45,8 @@ extern GType _gst_buffer_type;
#define GST_BUFFER_REFCOUNT(buf) GST_DATA_REFCOUNT(buf)
#define GST_BUFFER_REFCOUNT_VALUE(buf) GST_DATA_REFCOUNT_VALUE(buf)
#ifndef GST_DISABLE_DEPRECATED
#define GST_BUFFER_COPY_FUNC(buf) GST_DATA_COPY_FUNC(buf)
#define GST_BUFFER_FREE_FUNC(buf) GST_DATA_FREE_FUNC(buf)
#endif
#define GST_BUFFER_FLAGS(buf) GST_DATA_FLAGS(buf)
#define GST_BUFFER_FLAG_IS_SET(buf,flag) GST_DATA_FLAG_IS_SET (buf, flag)
@ -59,6 +58,7 @@ extern GType _gst_buffer_type;
#define GST_BUFFER_MAXSIZE(buf) (GST_BUFFER(buf)->maxsize)
#define GST_BUFFER_TIMESTAMP(buf) (GST_BUFFER(buf)->timestamp)
#define GST_BUFFER_DURATION(buf) (GST_BUFFER(buf)->duration)
#define GST_BUFFER_CAPS(buf) (GST_BUFFER(buf)->caps)
#define GST_BUFFER_OFFSET(buf) (GST_BUFFER(buf)->offset)
#define GST_BUFFER_OFFSET_END(buf) (GST_BUFFER(buf)->offset_end)
#define GST_BUFFER_FREE_DATA_FUNC(buf) (GST_BUFFER(buf)->free_data)
@ -95,13 +95,14 @@ extern GType _gst_buffer_type;
typedef enum {
GST_BUFFER_READONLY = GST_DATA_READONLY,
GST_BUFFER_SUBBUFFER = GST_DATA_FLAG_LAST,
GST_BUFFER_ORIGINAL,
GST_BUFFER_DONTFREE,
GST_BUFFER_KEY_UNIT, /* deprecated, use reverse DELTA_UNIT */
GST_BUFFER_DONTKEEP,
GST_BUFFER_IN_CAPS,
GST_BUFFER_DELTA_UNIT, /* this unit depends on a previous unit */
GST_BUFFER_FLAG_LAST = GST_DATA_FLAG_LAST + 8
GST_BUFFER_ORIGINAL, /* original data, not copied, not currently used */
GST_BUFFER_DONTFREE, /* buffer data is managed by somebody else and cannot be freeed */
GST_BUFFER_KEY_UNIT, /* sync point in the stream, DEPRECATED, use DELTA_UNIT for non-KEY_UNIT buffers */
GST_BUFFER_DISCONT, /* buffer is first after discontinuity in the stream */
GST_BUFFER_IN_CAPS, /* buffer is also part of caps */
GST_BUFFER_GAP, /* buffer has been created to fill a gap in the stream */
GST_BUFFER_DELTA_UNIT, /* can't be used as sync point in stream */
GST_BUFFER_FLAG_LAST = GST_DATA_FLAG_LAST + 8
} GstBufferFlag;
struct _GstBuffer {
@ -116,6 +117,9 @@ struct _GstBuffer {
GstClockTime timestamp;
GstClockTime duration;
/* the media type of this buffer */
GstCaps *caps;
/* media specific offset
* for video frames, this could be the number of frames,
* for audio data, this could be the number of audio samples,
@ -148,17 +152,18 @@ G_STMT_START { \
#define gst_buffer_ref_by_count(buf,c) GST_BUFFER (gst_data_ref_by_count (GST_DATA (buf), c))
#define gst_buffer_unref(buf) gst_data_unref (GST_DATA (buf))
/* copy buffer */
void gst_buffer_stamp (GstBuffer *dest, const GstBuffer *src);
#define gst_buffer_copy(buf) GST_BUFFER (gst_data_copy (GST_DATA (buf)))
#define gst_buffer_is_writable(buf) gst_data_is_writable (GST_DATA (buf))
#define gst_buffer_copy_on_write(buf) GST_BUFFER (gst_data_copy_on_write (GST_DATA (buf)))
GstCaps* gst_buffer_get_caps (GstBuffer *buffer);
void gst_buffer_set_caps (GstBuffer *buffer, GstCaps *caps);
/* creating a subbuffer */
GstBuffer* gst_buffer_create_sub (GstBuffer *parent, guint offset, guint size);
/* merge, span, or append two buffers, intelligently */
GstBuffer* gst_buffer_merge (GstBuffer *buf1, GstBuffer *buf2);
GstBuffer* gst_buffer_join (GstBuffer *buf1, GstBuffer *buf2);
gboolean gst_buffer_is_span_fast (GstBuffer *buf1, GstBuffer *buf2);
GstBuffer* gst_buffer_span (GstBuffer *buf1, guint32 offset, GstBuffer *buf2, guint32 len);

View file

@ -26,10 +26,12 @@
#include "gst_private.h"
#include <gst/gst.h>
#define DEBUG_REFCOUNT
#define CAPS_POISON(caps) G_STMT_START{ \
if (caps) { \
GstCaps *_newcaps = gst_caps_copy (caps); \
gst_caps_free(caps); \
gst_caps_unref(caps); \
caps = _newcaps; \
} \
} G_STMT_END
@ -56,7 +58,7 @@ gst_caps_get_type (void)
if (!gst_caps_type) {
gst_caps_type = g_boxed_type_register_static ("GstCaps",
(GBoxedCopyFunc) gst_caps_copy_conditional,
(GBoxedFreeFunc) gst_caps_free);
(GBoxedFreeFunc) gst_caps_unref);
g_value_register_transform_func (gst_caps_type,
G_TYPE_STRING, gst_caps_transform_to_string);
@ -80,9 +82,14 @@ gst_caps_new_empty (void)
{
GstCaps *caps = g_new0 (GstCaps, 1);
gst_atomic_int_init (&(caps)->refcount, 1);
caps->type = GST_TYPE_CAPS;
caps->structs = g_ptr_array_new ();
#ifdef DEBUG_REFCOUNT
GST_CAT_LOG (GST_CAT_CAPS, "created caps %p", caps);
#endif
return caps;
}
@ -213,15 +220,8 @@ gst_caps_copy (const GstCaps * caps)
return newcaps;
}
/**
* gst_caps_free:
* @caps: the #GstCaps to free
*
* Frees a #GstCaps and all its structures and the structures'
* values.
*/
void
gst_caps_free (GstCaps * caps)
static void
_gst_caps_free (GstCaps * caps)
{
GstStructure *structure;
int i;
@ -236,9 +236,82 @@ gst_caps_free (GstCaps * caps)
#ifdef USE_POISONING
memset (caps, 0xff, sizeof (GstCaps));
#endif
gst_atomic_int_destroy (&(caps)->refcount);
g_free (caps);
}
GstCaps *
gst_caps_copy_on_write (GstCaps * caps)
{
GstCaps *copy;
g_return_val_if_fail (caps != NULL, NULL);
/* we are the only instance reffing this caps */
if (gst_atomic_int_read (&caps->refcount) == 1)
return caps;
/* else copy */
copy = gst_caps_copy (caps);
gst_caps_unref (caps);
return copy;
}
GstCaps *
gst_caps_ref (GstCaps * caps)
{
g_return_val_if_fail (caps != NULL, NULL);
#ifdef DEBUG_REFCOUNT
GST_CAT_LOG (GST_CAT_CAPS, "%p %d->%d", caps,
GST_CAPS_REFCOUNT_VALUE (caps), GST_CAPS_REFCOUNT_VALUE (caps) + 1);
#endif
gst_atomic_int_inc (&caps->refcount);
return caps;
}
GstCaps *
gst_caps_ref_by_count (GstCaps * caps, gint count)
{
g_return_val_if_fail (caps != NULL, NULL);
#ifdef DEBUG_REFCOUNT
GST_CAT_LOG (GST_CAT_CAPS, "%p %d->%d", caps,
GST_CAPS_REFCOUNT_VALUE (caps), GST_CAPS_REFCOUNT_VALUE (caps) + count);
#endif
gst_atomic_int_add (&caps->refcount, count);
return caps;
}
/**
* gst_caps_unref:
* @caps: the #GstCaps to unref
*
* Unref a #GstCaps and and free all its structures and the
* structures' values when the refcount reaches 0.
*/
void
gst_caps_unref (GstCaps * caps)
{
g_return_if_fail (caps != NULL);
g_return_if_fail (GST_CAPS_REFCOUNT_VALUE (caps) > 0);
#ifdef DEBUG_REFCOUNT
GST_CAT_LOG (GST_CAT_CAPS, "%p %d->%d", caps,
GST_CAPS_REFCOUNT_VALUE (caps), GST_CAPS_REFCOUNT_VALUE (caps) - 1);
#endif
/* if we ended up with the refcount at zero, free the caps */
if (gst_atomic_int_dec_and_test (&caps->refcount)) {
_gst_caps_free (caps);
}
}
/**
* gst_static_caps_get:
* @static_caps: the #GstStaticCaps to convert
@ -531,25 +604,6 @@ gst_caps_is_empty (const GstCaps * caps)
return (caps->structs == NULL) || (caps->structs->len == 0);
}
/**
* gst_caps_is_chained:
* @caps: the @GstCaps to test
*
* Determines if @caps contains multiple #GstStructures.
*
* This function is deprecated, and should not be used in new code.
* Use #gst_caps_is_simple() instead.
*
* Returns: TRUE if @caps contains more than one structure
*/
gboolean
gst_caps_is_chained (const GstCaps * caps)
{
g_return_val_if_fail (GST_IS_CAPS (caps), FALSE);
return (caps->structs->len > 1);
}
static gboolean
gst_caps_is_fixed_foreach (GQuark field_id, GValue * value, gpointer unused)
{
@ -675,7 +729,7 @@ gst_caps_is_subset (const GstCaps * subset, const GstCaps * superset)
caps = gst_caps_subtract (subset, superset);
ret = gst_caps_is_empty (caps);
gst_caps_free (caps);
gst_caps_unref (caps);
return ret;
}
@ -835,6 +889,29 @@ gst_caps_intersect (const GstCaps * caps1, const GstCaps * caps2)
return gst_caps_copy (caps1);
dest = gst_caps_new_empty ();
/* run zigzag on top line first
*
* 1 2 4 ..
* 3 5 ..
* 6 ..
* ..
*
*/
#if 0
for (i = 0; i < caps1->structs->len; i++) {
struct1 = gst_caps_get_structure (caps1, i);
for (j = 0; j < caps2->structs->len; j++) {
}
}
#endif
/* run zigzag on right line
*
* .. 1
* .. 2 4
* .. 3 5 6
*/
/* FIXME use loop that preserves the order better */
for (i = 0; i < caps1->structs->len; i++) {
struct1 = gst_caps_get_structure (caps1, i);
for (j = 0; j < caps2->structs->len; j++) {
@ -951,7 +1028,7 @@ gst_caps_subtract (const GstCaps * minuend, const GstCaps * subtrahend)
for (i = 0; i < subtrahend->structs->len; i++) {
sub = gst_caps_get_structure (subtrahend, i);
if (dest) {
gst_caps_free (src);
gst_caps_unref (src);
src = dest;
}
dest = gst_caps_new_empty ();
@ -975,12 +1052,12 @@ gst_caps_subtract (const GstCaps * minuend, const GstCaps * subtrahend)
}
}
if (gst_caps_is_empty (dest)) {
gst_caps_free (src);
gst_caps_unref (src);
return dest;
}
}
gst_caps_free (src);
gst_caps_unref (src);
gst_caps_do_simplify (dest);
return dest;
}
@ -1340,7 +1417,7 @@ gst_caps_replace (GstCaps ** caps, GstCaps * newcaps)
#endif
#endif
if (*caps)
gst_caps_free (*caps);
gst_caps_unref (*caps);
*caps = newcaps;
}
@ -1454,7 +1531,7 @@ gst_caps_from_string (const gchar * string)
if (gst_caps_from_string_inplace (caps, string)) {
return caps;
} else {
gst_caps_free (caps);
gst_caps_unref (caps);
return NULL;
}
}

View file

@ -40,10 +40,8 @@ G_BEGIN_DECLS
#define GST_CAPS_IS_SIMPLE(caps) (gst_caps_get_size(caps) == 1)
#define gst_caps_is_simple(caps) GST_CAPS_IS_SIMPLE(caps)
#ifndef GST_DISABLE_DEPRECATED
#define GST_DEBUG_CAPS(string, caps) \
GST_DEBUG ( string "%s: " GST_PTR_FORMAT, caps)
#endif
#define GST_STATIC_CAPS(string) \
{ \
@ -54,9 +52,16 @@ G_BEGIN_DECLS
typedef struct _GstCaps GstCaps;
typedef struct _GstStaticCaps GstStaticCaps;
/* refcount */
#define GST_CAPS_REFCOUNT(caps) ((GST_CAPS(caps))->refcount)
#define GST_CAPS_REFCOUNT_VALUE(caps) (gst_atomic_int_read (&(GST_CAPS(caps))->refcount))
struct _GstCaps {
GType type;
/* refcounting */
GstAtomicInt refcount;
guint16 flags;
GPtrArray *structs;
@ -80,7 +85,14 @@ GstCaps * gst_caps_new_full (GstStru
GstCaps * gst_caps_new_full_valist (GstStructure *structure,
va_list var_args);
GstCaps * gst_caps_copy (const GstCaps *caps);
void gst_caps_free (GstCaps *caps);
GstCaps * gst_caps_copy_1 (const GstCaps *caps);
/* reference counting */
GstCaps * gst_caps_copy_on_write (GstCaps *caps);
GstCaps * gst_caps_ref (GstCaps* caps);
GstCaps * gst_caps_ref_by_count (GstCaps* caps, gint count);
void gst_caps_unref (GstCaps* caps);
G_CONST_RETURN GstCaps * gst_static_caps_get (GstStaticCaps *static_caps);
/* manipulation */
@ -91,10 +103,6 @@ void gst_caps_append_structure (GstCaps
int gst_caps_get_size (const GstCaps *caps);
GstStructure * gst_caps_get_structure (const GstCaps *caps,
int index);
#ifndef GST_DISABLE_DEPRECATED
GstCaps * gst_caps_split_one (GstCaps *caps);
GstCaps * gst_caps_copy_1 (const GstCaps *caps);
#endif
void gst_caps_set_simple (GstCaps *caps,
char *field, ...);
void gst_caps_set_simple_valist (GstCaps *caps,
@ -104,11 +112,6 @@ void gst_caps_set_simple_valist (GstCaps
/* tests */
gboolean gst_caps_is_any (const GstCaps *caps);
gboolean gst_caps_is_empty (const GstCaps *caps);
#ifndef GST_DISABLE_DEPRECATED
gboolean gst_caps_is_chained (const GstCaps *caps);
gboolean gst_caps_is_equal_fixed (const GstCaps *caps1,
const GstCaps *caps2);
#endif
gboolean gst_caps_is_fixed (const GstCaps *caps);
gboolean gst_caps_is_always_compatible (const GstCaps *caps1,
const GstCaps *caps2);
@ -125,9 +128,6 @@ GstCaps * gst_caps_subtract (const GstCaps *minuend,
GstCaps * gst_caps_union (const GstCaps *caps1,
const GstCaps *caps2);
GstCaps * gst_caps_normalize (const GstCaps *caps);
#ifndef GST_DISABLE_DEPRECATED
GstCaps * gst_caps_simplify (const GstCaps *caps);
#endif
gboolean gst_caps_do_simplify (GstCaps *caps);
#ifndef GST_DISABLE_LOADSAVE

View file

@ -396,8 +396,6 @@ gst_clock_class_init (GstClockClass * klass)
static void
gst_clock_init (GstClock * clock)
{
clock->max_diff = DEFAULT_MAX_DIFF;
clock->start_time = 0;
clock->last_time = 0;
clock->entries = NULL;
@ -570,25 +568,6 @@ gst_clock_reset (GstClock * clock)
GST_UNLOCK (clock);
}
/**
* gst_clock_handle_discont
* @clock: a #GstClock to notify of the discontinuity
* @time: The new time
*
* Notifies the clock of a discontinuity in time.
*
* Returns: TRUE if the clock was updated. It is possible that
* the clock was not updated by this call because only the first
* discontinuitity in the pipeline is honoured.
*/
gboolean
gst_clock_handle_discont (GstClock * clock, guint64 time)
{
GST_ERROR_OBJECT (clock, "called deprecated function.");
return FALSE;
}
/**
* gst_clock_get_time
* @clock: a #GstClock to query
@ -621,61 +600,6 @@ gst_clock_get_time (GstClock * clock)
return ret;
}
/**
* gst_clock_get_event_time:
* @clock: clock to query
*
* Gets the "event time" of a given clock. An event on the clock happens
* whenever this function is called. This ensures that multiple events that
* happen shortly after each other are treated as if they happened at the same
* time. GStreamer uses to keep state changes of multiple elements in sync.
*
* Returns: the time of the event
*/
GstClockTime
gst_clock_get_event_time (GstClock * clock)
{
return gst_clock_get_event_time_delay (clock, 0);
}
/**
* gst_clock_get_event_time_delay:
* @clock: clock to query
* @delay: time before the event actually occurs
*
* Gets the "event time" of a given clock. An event on the clock happens
* whenever this function is called. This ensures that multiple events that
* happen shortly after each other are treated as if they happened at the same
* time. GStreamer uses to keep state changes of multiple elements in sync.
*
* When calling this function, the specified delay will be added to the current
* time to produce the event time. This can be used for events that are
* scheduled to happen at some point in the future.
*
* Returns: the time of the event
*/
GstClockTime
gst_clock_get_event_time_delay (GstClock * clock, GstClockTime delay)
{
GstClockTime time;
g_return_val_if_fail (GST_IS_CLOCK (clock), GST_CLOCK_TIME_NONE);
time = gst_clock_get_time (clock);
if (ABS (GST_CLOCK_DIFF (clock->last_event, time + delay)) <
clock->max_event_diff) {
GST_LOG_OBJECT (clock, "reporting last event time %" G_GUINT64_FORMAT,
clock->last_event);
} else {
clock->last_event = time + delay;
GST_LOG_OBJECT (clock, "reporting new event time %" G_GUINT64_FORMAT,
clock->last_event);
}
return clock->last_event;
}
/**
* gst_clock_get_next_id
* @clock: The clock to query
@ -718,12 +642,8 @@ gst_clock_set_property (GObject * object, guint prop_id,
g_object_notify (object, "stats");
break;
case ARG_MAX_DIFF:
clock->max_diff = g_value_get_int64 (value);
g_object_notify (object, "max-diff");
break;
case ARG_EVENT_DIFF:
clock->max_event_diff = g_value_get_uint64 (value);
g_object_notify (object, "event-diff");
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@ -744,10 +664,8 @@ gst_clock_get_property (GObject * object, guint prop_id,
g_value_set_boolean (value, clock->stats);
break;
case ARG_MAX_DIFF:
g_value_set_int64 (value, clock->max_diff);
break;
case ARG_EVENT_DIFF:
g_value_set_uint64 (value, clock->max_event_diff);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);

View file

@ -55,6 +55,13 @@ G_STMT_START { \
(tv).tv_usec = ((t) - (tv).tv_sec * GST_SECOND) / GST_USECOND; \
} G_STMT_END
#define GST_TIMESPEC_TO_TIME(ts) ((ts).tv_sec * GST_SECOND + (ts).tv_nsec * GST_NSECOND)
#define GST_TIME_TO_TIMESPEC(t,ts) \
G_STMT_START { \
(ts).tv_sec = (t) / GST_SECOND; \
(ts).tv_usec = ((t) - (ts).tv_sec * GST_SECOND) / GST_NSECOND; \
} G_STMT_END
#define GST_CLOCK_ENTRY_TRACE_NAME "GstClockEntry"
typedef struct _GstClockEntry GstClockEntry;
@ -102,7 +109,8 @@ typedef enum
GST_CLOCK_TIMEOUT = 1,
GST_CLOCK_EARLY = 2,
GST_CLOCK_ERROR = 3,
GST_CLOCK_UNSUPPORTED = 4
GST_CLOCK_UNSUPPORTED = 4,
GST_CLOCK_OK = 5
} GstClockReturn;
typedef enum
@ -125,7 +133,6 @@ struct _GstClock {
/* --- protected --- */
GstClockTime start_time;
GstClockTime last_time;
gint64 max_diff;
/* --- private --- */
guint64 resolution;
@ -134,9 +141,6 @@ struct _GstClock {
GCond *active_cond;
gboolean stats;
GstClockTime last_event;
GstClockTime max_event_diff;
gpointer _gst_reserved[GST_PADDING];
};
@ -175,9 +179,6 @@ void gst_clock_reset (GstClock *clock);
gboolean gst_clock_handle_discont (GstClock *clock, guint64 time);
GstClockTime gst_clock_get_time (GstClock *clock);
GstClockTime gst_clock_get_event_time (GstClock *clock);
GstClockTime gst_clock_get_event_time_delay (GstClock *clock, GstClockTime delay);
GstClockID gst_clock_get_next_id (GstClock *clock);

View file

@ -1,6 +1,6 @@
/* GStreamer
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
* 2000 Wim Taymans <wtay@chello.be>
* 2000 Wim Taymans <wim@fluendo.com>
*
* gstdata.c: Data operations
*

File diff suppressed because it is too large Load diff

View file

@ -32,6 +32,8 @@
#include <gst/gstplugin.h>
#include <gst/gstpluginfeature.h>
#include <gst/gstindex.h>
#include <gst/gstiterator.h>
#include <gst/gsttask.h>
#include <gst/gsttag.h>
G_BEGIN_DECLS
@ -80,7 +82,6 @@ GST_EXPORT GType _gst_element_type;
#define GST_ELEMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_ELEMENT, GstElementClass))
/* convenience functions */
#ifndef GST_DISABLE_DEPRECATED
#ifdef G_HAVE_ISO_VARARGS
#define GST_ELEMENT_QUERY_TYPE_FUNCTION(functionname, ...) \
GST_QUERY_TYPE_FUNCTION (GstElement*, functionname, __VA_ARGS__);
@ -96,26 +97,8 @@ GST_EXPORT GType _gst_element_type;
#define GST_ELEMENT_EVENT_MASK_FUNCTION(functionname, a...) \
GST_EVENT_MASK_FUNCTION (GstElement*, functionname, a);
#endif
#endif
typedef enum {
/* element is complex (for some def.) and generally require a cothread */
GST_ELEMENT_COMPLEX = GST_OBJECT_FLAG_LAST,
/* input and output pads aren't directly coupled to each other
examples: queues, multi-output async readers, etc. */
GST_ELEMENT_DECOUPLED,
/* this element should be placed in a thread if at all possible */
GST_ELEMENT_THREAD_SUGGESTED,
/* this element, for some reason, has a loop function that performs
* an infinite loop without calls to gst_element_yield () */
GST_ELEMENT_INFINITE_LOOP,
/* there is a new loopfunction ready for placement */
GST_ELEMENT_NEW_LOOPFUNC,
/* if this element can handle events */
GST_ELEMENT_EVENT_AWARE,
/* use threadsafe property get/set implementation */
GST_ELEMENT_USE_THREADSAFE_PROPERTIES,
/* private flags that can be used by the scheduler */
GST_ELEMENT_SCHEDULER_PRIVATE1,
GST_ELEMENT_SCHEDULER_PRIVATE2,
@ -123,20 +106,13 @@ typedef enum {
/* ignore state changes from parent */
GST_ELEMENT_LOCKED_STATE,
/* element is in error */
GST_ELEMENT_IN_ERROR,
/* use some padding for future expansion */
GST_ELEMENT_FLAG_LAST = GST_OBJECT_FLAG_LAST + 16
} GstElementFlags;
#define GST_ELEMENT_IS_THREAD_SUGGESTED(obj) (GST_FLAG_IS_SET(obj,GST_ELEMENT_THREAD_SUGGESTED))
#define GST_ELEMENT_IS_EVENT_AWARE(obj) (GST_FLAG_IS_SET(obj,GST_ELEMENT_EVENT_AWARE))
#define GST_ELEMENT_IS_DECOUPLED(obj) (GST_FLAG_IS_SET(obj,GST_ELEMENT_DECOUPLED))
#define GST_ELEMENT_NAME(obj) (GST_OBJECT_NAME(obj))
#define GST_ELEMENT_PARENT(obj) (GST_OBJECT_PARENT(obj))
#define GST_ELEMENT_SCHED(obj) (((GstElement*)(obj))->sched)
#define GST_ELEMENT_MANAGER(obj) (((GstElement*)(obj))->manager)
#define GST_ELEMENT_CLOCK(obj) (((GstElement*)(obj))->clock)
#define GST_ELEMENT_PADS(obj) ((obj)->pads)
@ -146,47 +122,52 @@ typedef enum {
if (__msg) \
GST_ERROR_OBJECT (el, "%s", __msg); \
if (__dbg) \
GST_ERROR_OBJECT (el, "%s", __dbg); \
GST_ERROR_OBJECT (el, "%s", __dbg); \
gst_element_error_full (GST_ELEMENT(el), \
GST_ ## domain ## _ERROR, GST_ ## domain ## _ERROR_ ## code, \
GST_ ## domain ## _ERROR, GST_ ## domain ## _ERROR_ ## code, \
__msg, __dbg, __FILE__, GST_FUNCTION, __LINE__); \
} G_STMT_END
/* the state change mutexes and conds */
#define GST_STATE_GET_LOCK(elem) (((GstElement *)(elem))->state_lock)
#define GST_STATE_LOCK(elem) g_mutex_lock(GST_STATE_GET_LOCK(elem))
#define GST_STATE_TRYLOCK(elem) g_mutex_trylock(GST_STATE_GET_LOCK(elem))
#define GST_STATE_UNLOCK(elem) g_mutex_unlock(GST_STATE_GET_LOCK(elem))
#define GST_STATE_GET_COND(elem) (((GstElement *)(elem))->state_cond)
#define GST_STATE_WAIT(elem) g_cond_wait (GST_STATE_GET_COND (elem), GST_STATE_GET_LOCK (elem))
#define GST_STATE_TIMED_WAIT(elem, timeval) g_cond_timed_wait (GST_STATE_GET_COND (elem), GST_STATE_GET_LOCK (elem),\
timeval)
typedef struct _GstElementFactory GstElementFactory;
typedef struct _GstElementFactoryClass GstElementFactoryClass;
typedef void (*GstElementLoopFunction) (GstElement *element);
typedef void (*GstElementPreRunFunction) (GstElement *element);
typedef void (*GstElementPostRunFunction) (GstElement *element);
struct _GstElement {
GstObject object;
/* element state and scheduling */
/* element state */
GMutex *state_lock;
GCond *state_cond;
guint8 current_state;
guint8 pending_state;
GstElementLoopFunction loopfunc;
GstScheduler *sched;
/* element manager */
GstPipeline *manager;
/* private pointer for the scheduler */
gpointer sched_private;
/* allocated clock */
GstClock *clock;
GstClockTimeDiff base_time; /* NULL/READY: 0 - PAUSED: current time - PLAYING: difference to clock */
/* element pads */
/* element pads, these lists can only be iterated while holding
* the element lock or checking the cookie after each lock. */
guint16 numpads;
guint16 numsrcpads;
guint16 numsinkpads;
GList *pads;
GMutex *state_mutex;
GCond *state_cond;
GstElementPreRunFunction pre_run_func;
GstElementPostRunFunction post_run_func;
GAsyncQueue *prop_value_queue;
GMutex *property_mutex;
guint16 numsrcpads;
GList *srcpads;
guint16 numsinkpads;
GList *sinkpads;
guint32 pads_cookie;
gpointer _gst_reserved[GST_PADDING];
};
@ -203,21 +184,35 @@ struct _GstElementClass {
/* templates for our pads */
GList *padtemplates;
gint numpadtemplates;
guint32 pad_templ_cookie;
/* signal callbacks */
void (*state_change) (GstElement *element, GstElementState old, GstElementState state);
void (*new_pad) (GstElement *element, GstPad *pad);
void (*pad_removed) (GstElement *element, GstPad *pad);
void (*error) (GstElement *element, GstElement *source, GError *error, gchar *debug);
void (*eos) (GstElement *element);
void (*found_tag) (GstElement *element, GstElement *source, const GstTagList *tag_list);
void (*no_more_pads) (GstElement *element);
/* local pointers for get/set */
void (*set_property) (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
void (*get_property) (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
/* vtable */
/* vtable*/
gboolean (*release_locks) (GstElement *element);
/* request/release pads */
GstPad* (*request_new_pad) (GstElement *element, GstPadTemplate *templ, const gchar* name);
void (*release_pad) (GstElement *element, GstPad *pad);
/* state changes */
gboolean (*get_state) (GstElement *element, GstElementState *state,
GstElementState *pending, GTimeVal *timeout);
GstElementStateReturn (*change_state) (GstElement *element);
/* manager */
void (*set_manager) (GstElement *element, GstPipeline *pipeline);
/* set/get clocks */
GstClock* (*get_clock) (GstElement *element);
void (*set_clock) (GstElement *element, GstClock *clock);
/* index */
GstIndex* (*get_index) (GstElement *element);
void (*set_index) (GstElement *element, GstIndex *index);
/* query/convert/events functions */
const GstEventMask* (*get_event_masks) (GstElement *element);
@ -230,133 +225,64 @@ struct _GstElementClass {
gboolean (*query) (GstElement *element, GstQueryType type,
GstFormat *format, gint64 *value);
/* change the element state */
GstElementStateReturn (*change_state) (GstElement *element);
/* request/release pads */
GstPad* (*request_new_pad) (GstElement *element, GstPadTemplate *templ, const gchar* name);
void (*release_pad) (GstElement *element, GstPad *pad);
/* set/get clocks */
GstClock* (*get_clock) (GstElement *element);
void (*set_clock) (GstElement *element, GstClock *clock);
/* index */
GstIndex* (*get_index) (GstElement *element);
void (*set_index) (GstElement *element, GstIndex *index);
GstElementStateReturn (*set_state) (GstElement *element, GstElementState state);
/* FIXME 0.9: move up to signals */
void (*no_more_pads) (GstElement *element);
gpointer _gst_reserved[GST_PADDING - 1];
};
/* class stuff */
void gst_element_class_add_pad_template (GstElementClass *klass, GstPadTemplate *templ);
void gst_element_class_install_std_props (GstElementClass *klass,
const gchar *first_name, ...);
void gst_element_class_set_details (GstElementClass *klass,
const GstElementDetails *details);
GstPadTemplate* gst_element_class_get_pad_template (GstElementClass *element_class, const gchar *name);
GList* gst_element_class_get_pad_template_list (GstElementClass *element_class);
#define gst_element_default_deep_notify gst_object_default_deep_notify
void gst_element_default_error (GObject *object, GstObject *orig, GError *error, gchar *debug);
/* element instance */
GType gst_element_get_type (void);
void gst_element_set_loop_function (GstElement *element,
GstElementLoopFunction loop);
#define gst_element_get_name(elem) gst_object_get_name(GST_OBJECT(elem))
#define gst_element_set_name(elem,name) gst_object_set_name(GST_OBJECT(elem),name)
#define gst_element_get_parent(elem) gst_object_get_parent(GST_OBJECT(elem))
#define gst_element_set_parent(elem,parent) gst_object_set_parent(GST_OBJECT(elem),parent)
/* threadsafe versions of their g_object_* counterparts */
void gst_element_set (GstElement *element, const gchar *first_property_name, ...);
void gst_element_get (GstElement *element, const gchar *first_property_name, ...);
void gst_element_set_valist (GstElement *element, const gchar *first_property_name,
va_list var_args);
void gst_element_get_valist (GstElement *element, const gchar *first_property_name,
va_list var_args);
void gst_element_set_property (GstElement *element, const gchar *property_name,
const GValue *value);
void gst_element_get_property (GstElement *element, const gchar *property_name,
GValue *value);
void gst_element_enable_threadsafe_properties (GstElement *element);
void gst_element_disable_threadsafe_properties (GstElement *element);
void gst_element_set_pending_properties (GstElement *element);
/* clocking */
gboolean gst_element_requires_clock (GstElement *element);
gboolean gst_element_provides_clock (GstElement *element);
GstClock* gst_element_get_clock (GstElement *element);
void gst_element_set_clock (GstElement *element, GstClock *clock);
GstClockReturn gst_element_clock_wait (GstElement *element,
GstClockID id, GstClockTimeDiff *jitter);
GstClockTime gst_element_get_time (GstElement *element);
gboolean gst_element_wait (GstElement *element, GstClockTime timestamp);
void gst_element_set_time (GstElement *element, GstClockTime time);
void gst_element_set_time_delay (GstElement *element, GstClockTime time, GstClockTime delay);
void gst_element_adjust_time (GstElement *element, GstClockTimeDiff diff);
/* indexs */
/* indexes */
gboolean gst_element_is_indexable (GstElement *element);
void gst_element_set_index (GstElement *element, GstIndex *index);
GstIndex* gst_element_get_index (GstElement *element);
/* manager and tasks */
void gst_element_set_manager (GstElement *element, GstPipeline *pipeline);
GstPipeline* gst_element_get_manager (GstElement *element);
GstTask* gst_element_create_task (GstElement *element, GstTaskFunction func, gpointer data);
gboolean gst_element_release_locks (GstElement *element);
void gst_element_yield (GstElement *element);
gboolean gst_element_interrupt (GstElement *element);
void gst_element_set_scheduler (GstElement *element, GstScheduler *sched);
GstScheduler* gst_element_get_scheduler (GstElement *element);
void gst_element_add_pad (GstElement *element, GstPad *pad);
/* pad management */
gboolean gst_element_add_pad (GstElement *element, GstPad *pad);
void gst_element_remove_pad (GstElement *element, GstPad *pad);
GstPad * gst_element_add_ghost_pad (GstElement *element, GstPad *pad, const gchar *name);
#ifndef GST_DISABLE_DEPRECATED
void gst_element_remove_ghost_pad (GstElement *element, GstPad *pad);
#endif
void gst_element_no_more_pads (GstElement *element);
GstPad* gst_element_get_pad (GstElement *element, const gchar *name);
GstPad* gst_element_get_static_pad (GstElement *element, const gchar *name);
GstPad* gst_element_get_static_pad (GstElement *element, const gchar *name);
GstPad* gst_element_get_request_pad (GstElement *element, const gchar *name);
void gst_element_release_request_pad (GstElement *element, GstPad *pad);
G_CONST_RETURN GList*
gst_element_get_pad_list (GstElement *element);
GstPad* gst_element_get_compatible_pad (GstElement *element, GstPad *pad);
GstPad* gst_element_get_compatible_pad_filtered (GstElement *element, GstPad *pad,
const GstCaps *filtercaps);
GstPadTemplate* gst_element_class_get_pad_template (GstElementClass *element_class, const gchar *name);
GList* gst_element_class_get_pad_template_list (GstElementClass *element_class);
GstPadTemplate* gst_element_get_pad_template (GstElement *element, const gchar *name);
GList* gst_element_get_pad_template_list (GstElement *element);
GstPadTemplate* gst_element_get_compatible_pad_template (GstElement *element, GstPadTemplate *compattempl);
gboolean gst_element_link (GstElement *src, GstElement *dest);
gboolean gst_element_link_many (GstElement *element_1,
GstElement *element_2, ...);
gboolean gst_element_link_filtered (GstElement *src, GstElement *dest,
const GstCaps *filtercaps);
void gst_element_unlink (GstElement *src, GstElement *dest);
void gst_element_unlink_many (GstElement *element_1,
GstElement *element_2, ...);
gboolean gst_element_link_pads (GstElement *src, const gchar *srcpadname,
GstElement *dest, const gchar *destpadname);
gboolean gst_element_link_pads_filtered (GstElement *src, const gchar *srcpadname,
GstElement *dest, const gchar *destpadname,
const GstCaps *filtercaps);
void gst_element_unlink_pads (GstElement *src, const gchar *srcpadname,
GstElement *dest, const gchar *destpadname);
GstIterator* gst_element_iterate_pads (GstElement *element);
/* event/query/format stuff */
G_CONST_RETURN GstEventMask*
gst_element_get_event_masks (GstElement *element);
gboolean gst_element_send_event (GstElement *element, GstEvent *event);
@ -372,33 +298,31 @@ gboolean gst_element_convert (GstElement *element,
GstFormat src_format, gint64 src_value,
GstFormat *dest_format, gint64 *dest_value);
void gst_element_found_tags (GstElement *element, const GstTagList *tag_list);
void gst_element_found_tags_for_pad (GstElement *element, GstPad *pad, GstClockTime timestamp,
GstTagList *list);
/* messages */
gboolean gst_element_post_message (GstElement *element, GstMessage *message);
void gst_element_set_eos (GstElement *element);
/* error handling */
gchar * _gst_element_error_printf (const gchar *format, ...);
void gst_element_error_full (GstElement *element, GQuark domain, gint code,
gchar *message, gchar *debug,
const gchar *file, const gchar *function, gint line);
/* state management */
gboolean gst_element_is_locked_state (GstElement *element);
void gst_element_set_locked_state (GstElement *element, gboolean locked_state);
gboolean gst_element_sync_state_with_parent (GstElement *element);
GstElementState gst_element_get_state (GstElement *element);
gboolean gst_element_get_state (GstElement *element, GstElementState *state,
GstElementState *pending, GTimeVal *timeout);
GstElementStateReturn gst_element_set_state (GstElement *element, GstElementState state);
void gst_element_wait_state_change (GstElement *element);
G_CONST_RETURN gchar* gst_element_state_get_name (GstElementState state);
void gst_element_abort_state (GstElement *element);
void gst_element_commit_state (GstElement *element);
/* factory management */
GstElementFactory* gst_element_get_factory (GstElement *element);
GstBin* gst_element_get_managing_bin (GstElement *element);
/*
*
* factories stuff
@ -460,19 +384,7 @@ GstElement* gst_element_factory_create (GstElementFactory *factory,
const gchar *name);
GstElement* gst_element_factory_make (const gchar *factoryname, const gchar *name);
gboolean gst_element_factory_can_src_caps (GstElementFactory *factory,
const GstCaps *caps);
gboolean gst_element_factory_can_sink_caps (GstElementFactory *factory,
const GstCaps *caps);
void __gst_element_factory_add_pad_template (GstElementFactory *elementfactory,
GstPadTemplate *templ);
void __gst_element_factory_add_interface (GstElementFactory *elementfactory,
const gchar *interfacename);
G_END_DECLS
#endif /* __GST_ELEMENT_H__ */

View file

@ -252,7 +252,7 @@ gst_element_register (GstPlugin * plugin, const gchar * name, guint rank,
interfaces = g_type_interfaces (type, &n_interfaces);
for (i = 0; i < n_interfaces; i++) {
__gst_element_factory_add_interface (factory, g_type_name (interfaces[i]));
//__gst_element_factory_add_interface (factory, g_type_name (interfaces[i]));
}
g_free (interfaces);

View file

@ -38,22 +38,17 @@ typedef enum {
GST_EVENT_UNKNOWN = 0,
GST_EVENT_EOS = 1,
GST_EVENT_FLUSH = 2,
GST_EVENT_EMPTY = 3,
GST_EVENT_CAPS = 3,
GST_EVENT_DISCONTINUOUS = 4,
/*GST_EVENT_NEW_MEDIA = 5, <- removed */
GST_EVENT_QOS = 6,
GST_EVENT_SEEK = 7,
GST_EVENT_SEEK_SEGMENT = 8,
GST_EVENT_SEGMENT_DONE = 9,
GST_EVENT_SIZE = 10,
GST_EVENT_RATE = 11,
GST_EVENT_FILLER = 12,
GST_EVENT_TS_OFFSET = 13,
GST_EVENT_INTERRUPT = 14,
GST_EVENT_NAVIGATION = 15,
GST_EVENT_TAG = 16
GST_EVENT_QOS = 5,
GST_EVENT_SEEK = 6,
GST_EVENT_SEEK_SEGMENT = 7,
GST_EVENT_SEGMENT_DONE = 8,
GST_EVENT_SIZE = 9,
GST_EVENT_RATE = 10,
GST_EVENT_NAVIGATION = 11,
GST_EVENT_TAG = 12
} GstEventType;
#define GST_EVENT_ANY GST_EVENT_NAVIGATION
#define GST_EVENT_TRACE_NAME "GstEvent"
@ -87,7 +82,6 @@ typedef struct
GstEventFlag flags;
} GstEventMask;
#ifndef GST_DISABLE_DEPRECATED
#ifdef G_HAVE_ISO_VARARGS
#define GST_EVENT_MASK_FUNCTION(type,functionname, ...) \
static const GstEventMask* \
@ -111,7 +105,6 @@ functionname (type pad) \
return masks; \
}
#endif
#endif
/* seek events, extends GstEventFlag */
typedef enum {

View file

@ -515,7 +515,8 @@ gst_index_gtype_resolver (GstIndex * index, GstObject * writer,
gchar ** writer_string, gpointer data)
{
if (GST_IS_PAD (writer)) {
GstElement *element = gst_pad_get_parent (GST_PAD (writer));
GstElement *element =
(GstElement *) gst_object_get_parent (GST_OBJECT (writer));
*writer_string = g_strdup_printf ("%s.%s",
g_type_name (G_OBJECT_TYPE (element)), gst_object_get_name (writer));

View file

@ -153,6 +153,7 @@ GstDebugCategory *GST_CAT_NEGOTIATION = NULL;
GstDebugCategory *GST_CAT_REFCOUNTING = NULL;
GstDebugCategory *GST_CAT_ERROR_SYSTEM = NULL;
GstDebugCategory *GST_CAT_EVENT = NULL;
GstDebugCategory *GST_CAT_MESSAGE = NULL;
GstDebugCategory *GST_CAT_PARAMS = NULL;
GstDebugCategory *GST_CAT_CALL_TRACE = NULL;
GstDebugCategory *GST_CAT_SEEK = NULL;
@ -272,6 +273,8 @@ _gst_debug_init (void)
GST_CAT_EVENT = _gst_debug_category_new ("GST_EVENT",
GST_DEBUG_BOLD | GST_DEBUG_FG_WHITE | GST_DEBUG_BG_RED, NULL);
GST_CAT_MESSAGE = _gst_debug_category_new ("GST_MESSAGE",
GST_DEBUG_BOLD | GST_DEBUG_FG_WHITE | GST_DEBUG_BG_RED, NULL);
GST_CAT_PARAMS = _gst_debug_category_new ("GST_PARAMS",
GST_DEBUG_BOLD | GST_DEBUG_FG_BLACK | GST_DEBUG_BG_YELLOW, NULL);
GST_CAT_CALL_TRACE = _gst_debug_category_new ("GST_CALL_TRACE",

View file

@ -179,7 +179,10 @@ gst_object_class_init (GstObjectClass * klass)
static void
gst_object_init (GstObject * object)
{
//object->lock = g_new0(GStaticRecMutex, 1);
//g_static_rec_mutex_init (object->lock);
object->lock = g_mutex_new ();
object->parent = NULL;
object->name = NULL;
@ -225,6 +228,7 @@ gst_object_ref (GstObject * object)
GST_CAT_LOG_OBJECT (GST_CAT_REFCOUNTING, object, "ref %d->%d",
G_OBJECT (object)->ref_count, G_OBJECT (object)->ref_count + 1);
/* FIXME, not threadsafe */
g_object_ref (G_OBJECT (object));
return object;
}
@ -245,6 +249,7 @@ gst_object_unref (GstObject * object)
GST_CAT_LOG_OBJECT (GST_CAT_REFCOUNTING, object, "unref %d->%d",
G_OBJECT (object)->ref_count, G_OBJECT (object)->ref_count - 1);
/* FIXME, not threadsafe */
g_object_unref (G_OBJECT (object));
}
@ -325,7 +330,9 @@ gst_object_finalize (GObject * object)
g_free (gstobject->name);
//g_static_rec_mutex_free (gstobject->lock);
g_mutex_free (gstobject->lock);
g_free (gstobject->lock);
#ifndef GST_DISABLE_TRACE
{
@ -342,6 +349,8 @@ gst_object_finalize (GObject * object)
parent_class->finalize (object);
}
static GStaticRecMutex dispatch_mutex = G_STATIC_REC_MUTEX_INIT;
/* Changing a GObject property of a GstObject will result in "deep_notify"
* signals being emitted by the object itself, as well as in each parent
* object. This is so that an application can connect a listener to the
@ -354,6 +363,7 @@ gst_object_dispatch_properties_changed (GObject * object,
GstObject *gst_object;
guint i;
g_static_rec_mutex_lock (&dispatch_mutex);
/* do the standard dispatching */
G_OBJECT_CLASS (parent_class)->dispatch_properties_changed (object, n_pspecs,
pspecs);
@ -367,6 +377,7 @@ gst_object_dispatch_properties_changed (GObject * object,
GST_OBJECT_NAME (object) ? GST_OBJECT_NAME (object) : "(null)",
GST_OBJECT_NAME (gst_object) ? GST_OBJECT_NAME (gst_object) :
"(null)", pspecs[i]->name);
/* FIXME, not thread safe */
g_signal_emit (gst_object, gst_object_signals[DEEP_NOTIFY],
g_quark_from_string (pspecs[i]->name), (GstObject *) object,
pspecs[i]);
@ -374,6 +385,7 @@ gst_object_dispatch_properties_changed (GObject * object,
gst_object = GST_OBJECT_PARENT (gst_object);
}
g_static_rec_mutex_unlock (&dispatch_mutex);
}
/**
@ -481,13 +493,19 @@ gst_object_set_name (GstObject * object, const gchar * name)
g_return_if_fail (object != NULL);
g_return_if_fail (GST_IS_OBJECT (object));
GST_LOCK (object);
if (object->name != NULL)
g_free (object->name);
if (name != NULL)
object->name = g_strdup (name);
else
else {
GST_UNLOCK (object);
gst_object_set_name_default (object);
GST_LOCK (object);
}
GST_UNLOCK (object);
}
/**
@ -501,9 +519,15 @@ gst_object_set_name (GstObject * object, const gchar * name)
const gchar *
gst_object_get_name (GstObject * object)
{
g_return_val_if_fail (GST_IS_OBJECT (object), NULL);
const gchar *result = NULL;
return object->name;
g_return_val_if_fail (GST_IS_OBJECT (object), result);
GST_LOCK (object);
result = object->name;
GST_UNLOCK (object);
return result;
}
/**
@ -519,17 +543,18 @@ gst_object_get_name (GstObject * object)
void
gst_object_set_parent (GstObject * object, GstObject * parent)
{
g_return_if_fail (object != NULL);
g_return_if_fail (GST_IS_OBJECT (object));
g_return_if_fail (parent != NULL);
g_return_if_fail (GST_IS_OBJECT (parent));
g_return_if_fail (object != parent);
g_return_if_fail (object->parent == NULL);
GST_CAT_LOG_OBJECT (GST_CAT_REFCOUNTING, object, "set parent (ref and sink)");
GST_LOCK (object);
gst_object_ref (object);
gst_object_sink (object);
object->parent = parent;
GST_UNLOCK (object);
g_signal_emit (G_OBJECT (object), gst_object_signals[PARENT_SET], 0, parent);
}
@ -545,10 +570,15 @@ gst_object_set_parent (GstObject * object, GstObject * parent)
GstObject *
gst_object_get_parent (GstObject * object)
{
g_return_val_if_fail (object != NULL, NULL);
g_return_val_if_fail (GST_IS_OBJECT (object), NULL);
GstObject *result = NULL;
return object->parent;
g_return_val_if_fail (GST_IS_OBJECT (object), result);
GST_LOCK (object);
result = object->parent;
GST_UNLOCK (object);
return result;
}
/**
@ -560,17 +590,24 @@ gst_object_get_parent (GstObject * object)
void
gst_object_unparent (GstObject * object)
{
g_return_if_fail (object != NULL);
g_return_if_fail (GST_IS_OBJECT (object));
if (object->parent == NULL)
return;
GstObject *parent;
g_return_if_fail (GST_IS_OBJECT (object));
GST_LOCK (object);
parent = object->parent;
if (parent == NULL) {
GST_UNLOCK (object);
return;
}
GST_CAT_LOG_OBJECT (GST_CAT_REFCOUNTING, object, "unparent");
object->parent = NULL;
GST_UNLOCK (object);
g_signal_emit (G_OBJECT (object), gst_object_signals[PARENT_UNSET], 0,
object->parent);
parent);
object->parent = NULL;
gst_object_unref (object);
}
@ -593,7 +630,7 @@ gst_object_check_uniqueness (GList * list, const gchar * name)
list = g_list_next (list);
if (strcmp (GST_OBJECT_NAME (child), name) == 0)
if (strcmp (gst_object_get_name (child), name) == 0)
return FALSE;
}

View file

@ -55,13 +55,30 @@ typedef enum
GST_OBJECT_FLAG_LAST = 4
} GstObjectFlags;
#ifdef USE_REC_LOCKS
#define GST_LOCK(obj) (g_static_rec_mutex_lock(GST_OBJECT(obj)->lock))
#define GST_TRYLOCK(obj) (g_static_rec_mutex_trylock(GST_OBJECT(obj)->lock))
#define GST_UNLOCK(obj) (g_static_rec_mutex_unlock(GST_OBJECT(obj)->lock))
#define GST_GET_LOCK(obj) (g_static_mutex_get_mutex(&GST_OBJECT(obj)->lock->mutex))
#define GST_LOCK_TYPE GStaticRecMutex
#define GST_LOCK_CREATE g_static_rec_mutex_new()
#else
#define GST_LOCK(obj) (g_mutex_lock(GST_OBJECT(obj)->lock))
#define GST_TRYLOCK(obj) (g_mutex_trylock(GST_OBJECT(obj)->lock))
#define GST_UNLOCK(obj) (g_mutex_unlock(GST_OBJECT(obj)->lock))
#define GST_GET_LOCK(obj) (GST_OBJECT(obj)->lock)
#define GST_LOCK_TYPE GMutex
#define GST_LOCK_CREATE g_mutex_new()
#endif
struct _GstObject {
GObject object;
gchar *name;
/* locking for all sorts of things */
GMutex *lock;
GST_LOCK_TYPE *lock;
/* this object's parent */
GstObject *parent;
@ -103,11 +120,6 @@ struct _GstObjectClass {
#define GST_OBJECT_DESTROYED(obj) (GST_FLAG_IS_SET (obj, GST_DESTROYED))
#define GST_OBJECT_FLOATING(obj) (GST_FLAG_IS_SET (obj, GST_FLOATING))
/* CR1: object locking - GObject 2.0 doesn't have threadsafe locking */
#define GST_LOCK(obj) (g_mutex_lock(GST_OBJECT(obj)->lock))
#define GST_TRYLOCK(obj) (g_mutex_trylock(GST_OBJECT(obj)->lock))
#define GST_UNLOCK(obj) (g_mutex_unlock(GST_OBJECT(obj)->lock))
#define GST_GET_LOCK(obj) (GST_OBJECT(obj)->lock)
/* normal GObject stuff */

File diff suppressed because it is too large Load diff

View file

@ -50,6 +50,7 @@ GST_EXPORT GType _gst_ghost_pad_type;
#define GST_IS_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PAD))
#define GST_PAD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PAD, GstPad))
#define GST_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_PAD, GstPadClass))
#define GST_PAD_CAST(obj) ((GstPad*)(obj))
/*
* Real Pads
@ -60,6 +61,7 @@ GST_EXPORT GType _gst_ghost_pad_type;
#define GST_IS_REAL_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_REAL_PAD))
#define GST_REAL_PAD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_REAL_PAD, GstRealPad))
#define GST_REAL_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_REAL_PAD, GstRealPadClass))
#define GST_REAL_PAD_CAST(obj) ((GstRealPad*)(obj))
/*
* Ghost Pads
@ -70,6 +72,7 @@ GST_EXPORT GType _gst_ghost_pad_type;
#define GST_IS_GHOST_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_GHOST_PAD))
#define GST_GHOST_PAD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_GHOST_PAD, GstGhostPad))
#define GST_GHOST_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_GHOST_PAD, GstGhostPadClass))
#define GST_GHOST_PAD_CAST(obj) ((GstGhostPad*)(obj))
/*typedef struct _GstPad GstPad; */
@ -84,17 +87,27 @@ typedef struct _GstStaticPadTemplate GstStaticPadTemplate;
typedef struct _GstPadLink GstPadLink;
typedef enum {
GST_PAD_LINK_REFUSED = -1,
GST_PAD_LINK_DELAYED = 0,
GST_PAD_LINK_OK = 1,
GST_PAD_LINK_DONE = 2
GST_PAD_LINK_NOSCHED = -3, /* pads cannot cooperate in scheduling */
GST_PAD_LINK_NOFORMAT = -2, /* pads do not have common format */
GST_PAD_LINK_REFUSED = -1, /* refused for some reason */
GST_PAD_LINK_OK = 0, /* link ok */
} GstPadLinkReturn;
#define GST_PAD_LINK_FAILED(ret) (ret < GST_PAD_LINK_OK)
#define GST_PAD_LINK_SUCCESSFUL(ret) (ret >= GST_PAD_LINK_OK)
typedef enum {
GST_FLOW_OK = 0, /* data passing was ok */
GST_FLOW_RESEND = 1, /* resend buffer, possibly with new caps */
GST_FLOW_ERROR = -1, /* some error occured */
GST_FLOW_NOT_CONNECTED = -2, /* pad is not connected */
GST_FLOW_NOT_NEGOTIATED = -3, /* pad is not negotiated */
GST_FLOW_WRONG_STATE = -4, /* pad is in wrong state */
GST_FLOW_UNEXPECTED = -5, /* did not expect anything */
GST_FLOW_NOT_SUPPORTED = -6 /* function not supported */
} GstFlowReturn;
/* convenience functions */
#ifndef GST_DISABLE_DEPRECATED
#ifdef G_HAVE_ISO_VARARGS
#define GST_PAD_QUERY_TYPE_FUNCTION(functionname, ...) GST_QUERY_TYPE_FUNCTION (GstPad *, functionname, __VA_ARGS__);
#define GST_PAD_FORMATS_FUNCTION(functionname, ...) GST_FORMATS_FUNCTION (GstPad *, functionname, __VA_ARGS__);
@ -104,15 +117,19 @@ typedef enum {
#define GST_PAD_FORMATS_FUNCTION(functionname, a...) GST_FORMATS_FUNCTION (GstPad *, functionname, a);
#define GST_PAD_EVENT_MASK_FUNCTION(functionname, a...) GST_EVENT_MASK_FUNCTION (GstPad *, functionname, a);
#endif
#endif
/* this defines the functions used to chain buffers
* pad is the sink pad (so the same chain function can be used for N pads)
* buf is the buffer being passed */
typedef void (*GstPadChainFunction) (GstPad *pad,GstData *data);
typedef GstData* (*GstPadGetFunction) (GstPad *pad);
/* pad states */
typedef gboolean (*GstPadActivateFunction) (GstPad *pad, gboolean active);
/* data passing */
typedef GstFlowReturn (*GstPadChainFunction) (GstPad *pad, GstBuffer *buffer);
typedef GstFlowReturn (*GstPadGetFunction) (GstPad *pad, GstBuffer **buffer);
typedef GstFlowReturn (*GstPadGetRangeFunction) (GstPad *pad, guint64 offset,
guint64 length, GstBuffer **buffer);
typedef gboolean (*GstPadEventFunction) (GstPad *pad, GstEvent *event);
/* convert/query/format functions */
typedef gboolean (*GstPadConvertFunction) (GstPad *pad,
GstFormat src_format, gint64 src_value,
GstFormat *dest_format, gint64 *dest_value);
@ -123,14 +140,19 @@ typedef const GstFormat* (*GstPadFormatsFunction) (GstPad *pad);
typedef const GstEventMask* (*GstPadEventMaskFunction) (GstPad *pad);
typedef const GstQueryType* (*GstPadQueryTypeFunction) (GstPad *pad);
typedef GstPadLinkReturn (*GstPadLinkFunction) (GstPad *pad, const GstCaps *caps);
/* linking */
typedef GstPadLinkReturn (*GstPadLinkFunction) (GstPad *pad, GstPad *peer);
typedef void (*GstPadUnlinkFunction) (GstPad *pad);
typedef GstCaps* (*GstPadGetCapsFunction) (GstPad *pad);
typedef GstCaps* (*GstPadFixateFunction) (GstPad *pad, const GstCaps *caps);
typedef GstBuffer* (*GstPadBufferAllocFunction) (GstPad *pad, guint64 offset, guint size);
/* caps nego */
typedef GstCaps* (*GstPadGetCapsFunction) (GstPad *pad);
typedef GstBuffer* (*GstPadBufferAllocFunction) (GstPad *pad, guint64 offset, guint size,
GstCaps *caps);
/* misc */
typedef gboolean (*GstPadDispatcherFunction) (GstPad *pad, gpointer data);
typedef void (*GstPadBlockCallback) (GstPad *pad, gboolean blocked, gpointer user_data);
typedef enum {
GST_PAD_UNKNOWN,
GST_PAD_SRC,
@ -139,6 +161,7 @@ typedef enum {
typedef enum {
GST_PAD_DISABLED = GST_OBJECT_FLAG_LAST,
GST_PAD_BLOCKED,
GST_PAD_NEGOTIATING,
GST_PAD_DISPATCHING,
@ -164,15 +187,24 @@ struct _GstPadClass {
struct _GstRealPad {
GstPad pad;
/* streaming lock and cond */
GMutex *stream_lock;
GCond *stream_cond;
/* block cond, mutex is from the object */
GCond *block_cond;
GstPadBlockCallback block_callback;
gpointer block_data;
/* the pad capabilities */
GstCaps *caps;
GstPadFixateFunction appfixatefunc;
GstCaps *appfilter;
GstPadGetCapsFunction getcapsfunc;
GstPadFixateFunction fixatefunc;
GstPadDirection direction;
GstPadActivateFunction activatefunc;
GstPadLinkFunction linkfunc;
GstPadUnlinkFunction unlinkfunc;
GstRealPad *peer;
@ -181,11 +213,10 @@ struct _GstRealPad {
/* data transport functions */
GstPadChainFunction chainfunc;
GstPadChainFunction chainhandler;
GstPadGetFunction getfunc;
GstPadGetFunction gethandler;
GstPadGetRangeFunction getrangefunc;
GstPadEventFunction eventfunc;
GstPadEventFunction eventhandler;
GstPadEventMaskFunction eventmaskfunc;
GList *ghostpads;
@ -197,13 +228,10 @@ struct _GstRealPad {
GstPadQueryTypeFunction querytypefunc;
GstPadIntLinkFunction intlinkfunc;
GstPadBufferAllocFunction bufferallocfunc;
GstPadBufferAllocFunction bufferallocfunc;
GstProbeDispatcher probedisp;
GstPadLink *link;
GstCaps *explicit_caps;
gpointer _gst_reserved[GST_PADDING];
};
@ -211,11 +239,9 @@ struct _GstRealPadClass {
GstPadClass parent_class;
/* signal callbacks */
void (*caps_nego_failed) (GstPad *pad, GstCaps *caps);
void (*linked) (GstPad *pad, GstPad *peer);
void (*unlinked) (GstPad *pad, GstPad *peer);
GstPadFixateFunction appfixatefunc;
void (*request_link) (GstPad *pad);
gpointer _gst_reserved[GST_PADDING];
};
@ -247,12 +273,11 @@ struct _GstGhostPadClass {
#define GST_RPAD_CAPS(pad) (((GstRealPad *)(pad))->caps)
#define GST_RPAD_APPFILTER(pad) (((GstRealPad *)(pad))->appfilter)
#define GST_RPAD_PEER(pad) (((GstRealPad *)(pad))->peer)
#define GST_RPAD_ACTIVATEFUNC(pad) (((GstRealPad *)(pad))->activatefunc)
#define GST_RPAD_CHAINFUNC(pad) (((GstRealPad *)(pad))->chainfunc)
#define GST_RPAD_CHAINHANDLER(pad) (((GstRealPad *)(pad))->chainhandler)
#define GST_RPAD_GETFUNC(pad) (((GstRealPad *)(pad))->getfunc)
#define GST_RPAD_GETHANDLER(pad) (((GstRealPad *)(pad))->gethandler)
#define GST_RPAD_GETRANGEFUNC(pad) (((GstRealPad *)(pad))->getrangefunc)
#define GST_RPAD_EVENTFUNC(pad) (((GstRealPad *)(pad))->eventfunc)
#define GST_RPAD_EVENTHANDLER(pad) (((GstRealPad *)(pad))->eventhandler)
#define GST_RPAD_CONVERTFUNC(pad) (((GstRealPad *)(pad))->convertfunc)
#define GST_RPAD_QUERYFUNC(pad) (((GstRealPad *)(pad))->queryfunc)
#define GST_RPAD_INTLINKFUNC(pad) (((GstRealPad *)(pad))->intlinkfunc)
@ -263,10 +288,28 @@ struct _GstGhostPadClass {
#define GST_RPAD_LINKFUNC(pad) (((GstRealPad *)(pad))->linkfunc)
#define GST_RPAD_UNLINKFUNC(pad) (((GstRealPad *)(pad))->unlinkfunc)
#define GST_RPAD_GETCAPSFUNC(pad) (((GstRealPad *)(pad))->getcapsfunc)
#define GST_RPAD_FIXATEFUNC(pad) (((GstRealPad *)(pad))->fixatefunc)
#define GST_RPAD_BUFFERALLOCFUNC(pad) (((GstRealPad *)(pad))->bufferallocfunc)
#define GST_RPAD_LINK(pad) (((GstRealPad *)(pad))->link)
#define GST_RPAD_EXPLICIT_CAPS(pad) (((GstRealPad *)(pad))->explicit_caps)
#define GST_RPAD_IS_LINKED(pad) (GST_RPAD_PEER(pad) != NULL)
#define GST_RPAD_IS_ACTIVE(pad) (!GST_FLAG_IS_SET(pad, GST_PAD_DISABLED))
#define GST_RPAD_IS_BLOCKED(pad) (GST_FLAG_IS_SET (pad, GST_PAD_BLOCKED))
#define GST_RPAD_IS_NEGOTIATING(pad) (GST_FLAG_IS_SET (pad, GST_PAD_NEGOTIATING))
#define GST_RPAD_IS_DISPATCHING(pad) (GST_FLAG_IS_SET (pad, GST_PAD_DISPATCHING))
#define GST_RPAD_IS_USABLE(pad) (GST_RPAD_IS_LINKED (pad) && \
GST_RPAD_IS_ACTIVE(pad) && GST_RPAD_IS_ACTIVE(GST_RPAD_PEER (pad)))
#define GST_RPAD_CAN_PULL(pad) (GST_IS_REAL_PAD(pad) && GST_REAL_PAD(pad)->gethandler != NULL)
#define GST_RPAD_IS_SRC(pad) (GST_RPAD_DIRECTION(pad) == GST_PAD_SRC)
#define GST_RPAD_IS_SINK(pad) (GST_RPAD_DIRECTION(pad) == GST_PAD_SINK)
#define GST_STREAM_GET_LOCK(pad) (GST_PAD_REALIZE(pad)->stream_lock)
#define GST_STREAM_LOCK(pad) (g_mutex_lock(GST_STREAM_GET_LOCK(pad)))
#define GST_STREAM_TRYLOCK(pad) (g_mutex_trylock(GST_STREAM_GET_LOCK(pad)))
#define GST_STREAM_UNLOCK(pad) (g_mutex_unlock(GST_STREAM_GET_LOCK(pad)))
#define GST_STREAM_GET_COND(pad) (GST_PAD_REALIZE(pad)->stream_cond)
#define GST_PAD_BLOCK_GET_COND(pad) (GST_PAD_REALIZE(pad)->block_cond)
#define GST_PAD_BLOCK_WAIT(pad) (g_cond_wait(GST_PAD_BLOCK_GET_COND (pad), GST_GET_LOCK (pad)))
#define GST_PAD_BLOCK_SIGNAL(pad) (g_cond_signal(GST_PAD_BLOCK_GET_COND (pad)))
/* GstGhostPad */
#define GST_GPAD_REALPAD(pad) (((GstGhostPad *)(pad))->realpad)
@ -274,19 +317,20 @@ struct _GstGhostPadClass {
/* Generic */
#define GST_PAD_REALIZE(pad) (GST_IS_REAL_PAD(pad) ? ((GstRealPad *)(pad)) : GST_GPAD_REALPAD(pad))
#define GST_PAD_DIRECTION(pad) GST_RPAD_DIRECTION(GST_PAD_REALIZE(pad))
#define GST_PAD_CAPS(pad) (gst_pad_get_negotiated_caps(GST_PAD (pad)))
#define GST_PAD_PEER(pad) GST_PAD(GST_RPAD_PEER(GST_PAD_REALIZE(pad)))
#define GST_PAD_CAPS(pad) GST_RPAD_CAPS(GST_PAD_REALIZE (pad))
#define GST_PAD_APPFILTER(pad) GST_RPAD_APPFILTER(GST_PAD_REALIZE (pad))
#define GST_PAD_PEER(pad) GST_PAD_CAST(GST_RPAD_PEER(GST_PAD_REALIZE(pad)))
/* Some check functions (unused?) */
#define GST_PAD_IS_LINKED(pad) (GST_PAD_PEER(pad) != NULL)
#define GST_PAD_IS_ACTIVE(pad) (!GST_FLAG_IS_SET(GST_PAD_REALIZE(pad), GST_PAD_DISABLED))
#define GST_PAD_IS_NEGOTIATING(pad) (GST_FLAG_IS_SET (pad, GST_PAD_NEGOTIATING))
#define GST_PAD_IS_DISPATCHING(pad) (GST_FLAG_IS_SET (pad, GST_PAD_DISPATCHING))
#define GST_PAD_IS_USABLE(pad) (GST_PAD_IS_LINKED (pad) && \
GST_PAD_IS_ACTIVE(pad) && GST_PAD_IS_ACTIVE(GST_PAD_PEER (pad)))
#define GST_PAD_CAN_PULL(pad) (GST_IS_REAL_PAD(pad) && GST_REAL_PAD(pad)->gethandler != NULL)
#define GST_PAD_IS_SRC(pad) (GST_PAD_DIRECTION(pad) == GST_PAD_SRC)
#define GST_PAD_IS_SINK(pad) (GST_PAD_DIRECTION(pad) == GST_PAD_SINK)
#define GST_PAD_IS_LINKED(pad) (GST_RPAD_IS_LINKED(GST_PAD_REALIZE(pad)))
#define GST_PAD_IS_ACTIVE(pad) (GST_RPAD_IS_ACTIVE(GST_PAD_REALIZE(pad)))
#define GST_PAD_IS_BLOCKED(pad) (GST_RPAD_IS_BLOCKED(GST_PAD_REALIZE(pad)))
#define GST_PAD_IS_NEGOTIATING(pad) (GST_RPAD_IS_NEGOTIATING(GST_PAD_REALIZE(pad)))
#define GST_PAD_IS_DISPATCHING(pad) (GST_RPAD_IS_DISPATCHING(GST_PAD_REALIZE(pad)))
#define GST_PAD_IS_USABLE(pad) (GST_RPAD_IS_USABLE(GST_PAD_REALIZE(pad)))
#define GST_PAD_CAN_PULL(pad) (GST_RPAD_CAN_PULL(GST_PAD_REALIZE(pad)))
#define GST_PAD_IS_SRC(pad) (GST_RPAD_IS_SRC(GST_PAD_REALIZE(pad)))
#define GST_PAD_IS_SINK(pad) (GST_RPAD_IS_SINK(GST_PAD_REALIZE(pad)))
/***** PadTemplate *****/
#define GST_TYPE_PAD_TEMPLATE (gst_pad_template_get_type ())
@ -360,35 +404,36 @@ GstPad* gst_pad_new_from_template (GstPadTemplate *templ, const gchar *name);
GstPad* gst_pad_custom_new (GType type, const gchar *name, GstPadDirection direction);
GstPad* gst_pad_custom_new_from_template (GType type, GstPadTemplate *templ, const gchar *name);
void gst_pad_set_name (GstPad *pad, const gchar *name);
G_CONST_RETURN gchar* gst_pad_get_name (GstPad *pad);
#define gst_pad_get_name(pad) gst_object_get_name(GST_OBJECT(pad))
#define gst_pad_set_name(pad,name) gst_object_set_name(GST_OBJECT(pad),name)
#define gst_pad_get_parent(pad) GST_ELEMENT(gst_object_get_parent(GST_OBJECT(pad)))
#define gst_pad_set_parent(pad,parent) gst_object_set_parent(GST_OBJECT(pad),parent)
GstElement* gst_pad_get_real_parent (GstPad *pad);
GstPadDirection gst_pad_get_direction (GstPad *pad);
void gst_pad_set_active (GstPad *pad, gboolean active);
gboolean gst_pad_set_active (GstPad *pad, gboolean active);
gboolean gst_pad_is_active (GstPad *pad);
gboolean gst_pad_set_blocked (GstPad *pad, gboolean blocked);
gboolean gst_pad_set_blocked_async (GstPad *pad, gboolean blocked,
GstPadBlockCallback callback, gpointer user_data);
gboolean gst_pad_is_blocked (GstPad *pad);
void gst_pad_set_element_private (GstPad *pad, gpointer priv);
gpointer gst_pad_get_element_private (GstPad *pad);
void gst_pad_set_parent (GstPad *pad, GstElement *parent);
GstElement* gst_pad_get_parent (GstPad *pad);
GstElement* gst_pad_get_real_parent (GstPad *pad);
GstScheduler* gst_pad_get_scheduler (GstPad *pad);
void gst_pad_add_ghost_pad (GstPad *pad, GstPad *ghostpad);
void gst_pad_remove_ghost_pad (GstPad *pad, GstPad *ghostpad);
GList* gst_pad_get_ghost_pad_list (GstPad *pad);
GstPadTemplate* gst_pad_get_pad_template (GstPad *pad);
void gst_pad_set_bufferalloc_function (GstPad *pad, GstPadBufferAllocFunction bufalloc);
void gst_pad_set_bufferalloc_function (GstPad *pad, GstPadBufferAllocFunction bufalloc);
GstBuffer* gst_pad_alloc_buffer (GstPad *pad, guint64 offset, gint size);
/* data passing setup functions */
void gst_pad_set_activate_function (GstPad *pad, GstPadActivateFunction activate);
void gst_pad_set_chain_function (GstPad *pad, GstPadChainFunction chain);
void gst_pad_set_get_function (GstPad *pad, GstPadGetFunction get);
void gst_pad_set_get_range_function (GstPad *pad, GstPadGetRangeFunction get);
void gst_pad_set_event_function (GstPad *pad, GstPadEventFunction event);
void gst_pad_set_event_mask_function (GstPad *pad, GstPadEventMaskFunction mask_func);
G_CONST_RETURN GstEventMask*
@ -398,62 +443,35 @@ G_CONST_RETURN GstEventMask*
/* pad links */
void gst_pad_set_link_function (GstPad *pad, GstPadLinkFunction link);
gboolean gst_pad_can_link (GstPad *srcpad, GstPad *sinkpad);
gboolean gst_pad_can_link_filtered (GstPad *srcpad, GstPad *sinkpad, const GstCaps *filtercaps);
void gst_pad_set_unlink_function (GstPad *pad, GstPadUnlinkFunction unlink);
gboolean gst_pad_link (GstPad *srcpad, GstPad *sinkpad);
gboolean gst_pad_link_filtered (GstPad *srcpad, GstPad *sinkpad, const GstCaps *filtercaps);
GstPadLinkReturn gst_pad_link (GstPad *srcpad, GstPad *sinkpad);
GstPadLinkReturn gst_pad_link_filtered (GstPad *srcpad, GstPad *sinkpad,
const GstCaps *filtercaps);
GstPadLinkReturn gst_pad_relink_filtered (GstPad *srcpad, GstPad *sinkpad,
const GstCaps *filtercaps);
void gst_pad_unlink (GstPad *srcpad, GstPad *sinkpad);
gboolean gst_pad_is_linked (GstPad *pad);
GstPad* gst_pad_get_peer (GstPad *pad);
/* capsnego functions */
G_CONST_RETURN GstCaps* gst_pad_get_negotiated_caps (GstPad *pad);
gboolean gst_pad_is_negotiated (GstPad *pad);
GstCaps* gst_pad_get_caps (GstPad *pad);
G_CONST_RETURN GstCaps* gst_pad_get_pad_template_caps (GstPad *pad);
GstPadLinkReturn gst_pad_try_set_caps (GstPad *pad, const GstCaps *caps);
GstPadLinkReturn gst_pad_try_set_caps_nonfixed (GstPad *pad, const GstCaps *caps);
gboolean gst_pad_check_compatibility (GstPad *srcpad, GstPad *sinkpad);
void gst_pad_set_getcaps_function (GstPad *pad, GstPadGetCapsFunction getcaps);
void gst_pad_set_fixate_function (GstPad *pad, GstPadFixateFunction fixate);
GstCaps * gst_pad_proxy_getcaps (GstPad *pad);
GstPadLinkReturn gst_pad_proxy_pad_link (GstPad *pad, const GstCaps *caps);
GstCaps * gst_pad_proxy_fixate (GstPad *pad, const GstCaps *caps);
#ifndef GST_DISABLE_DEPRECATED
GstPadLinkReturn gst_pad_proxy_link (GstPad *pad, const GstCaps *caps);
#endif
gboolean gst_pad_set_explicit_caps (GstPad *pad, const GstCaps *caps);
void gst_pad_use_explicit_caps (GstPad *pad);
gboolean gst_pad_relink_filtered (GstPad *srcpad, GstPad *sinkpad, const GstCaps *filtercaps);
#ifndef GST_DISABLE_DEPRECATED
gboolean gst_pad_perform_negotiate (GstPad *srcpad, GstPad *sinkpad);
#endif
GstPadLinkReturn gst_pad_renegotiate (GstPad *pad);
void gst_pad_unnegotiate (GstPad *pad);
gboolean gst_pad_try_relink_filtered (GstPad *srcpad, GstPad *sinkpad, const GstCaps *filtercaps);
GstCaps* gst_pad_get_allowed_caps (GstPad *pad);
void gst_pad_caps_change_notify (GstPad *pad);
gboolean gst_pad_recover_caps_error (GstPad *pad, const GstCaps *allowed);
G_CONST_RETURN GstCaps* gst_pad_get_pad_template_caps (GstPad *pad);
GstCaps * gst_pad_get_allowed_caps (GstPad * pad);
GstCaps * gst_pad_get_caps (GstPad * pad);
gboolean gst_pad_set_caps (GstPad * pad, GstCaps *caps);
/* data passing functions */
void gst_pad_push (GstPad *pad, GstData *data);
GstData* gst_pad_pull (GstPad *pad);
GstFlowReturn gst_pad_push (GstPad *pad, GstBuffer *buffer);
GstFlowReturn gst_pad_pull (GstPad *pad, GstBuffer **buffer);
GstFlowReturn gst_pad_pull_range (GstPad *pad, guint64 offset, guint size,
GstBuffer **buffer);
gboolean gst_pad_push_event (GstPad *pad, GstEvent *event);
gboolean gst_pad_send_event (GstPad *pad, GstEvent *event);
gboolean gst_pad_event_default (GstPad *pad, GstEvent *event);
#ifndef GST_DISABLE_DEPRECATED
GstPad* gst_pad_selectv (GList *padlist);
GstPad* gst_pad_select (GstPad *pad, ...);
GstPad* gst_pad_select_valist (GstPad *pad, va_list varargs);
#endif
/* FIXME 0.9: rename to _select? Otherwise rename SchedulerClass pointer */
GstData * gst_pad_collectv (GstPad **selected, const GList *padlist);
GstData * gst_pad_collect (GstPad **selected, GstPad *pad, ...);
GstData * gst_pad_collect_valist (GstPad **selected, GstPad *pad, va_list varargs);
/* convert/query/format functions */
void gst_pad_set_formats_function (GstPad *pad,
@ -490,6 +508,7 @@ GList* gst_pad_get_internal_links_default (GstPad *pad);
gboolean gst_pad_dispatcher (GstPad *pad, GstPadDispatcherFunction dispatch,
gpointer data);
/* probes */
#define gst_pad_add_probe(pad, probe) \
(gst_probe_dispatcher_add_probe (&(GST_REAL_PAD (pad)->probedisp), probe))
#define gst_pad_remove_probe(pad, probe) \
@ -513,19 +532,12 @@ GstPadTemplate* gst_pad_template_new (const gchar *name_template,
GstPadTemplate * gst_static_pad_template_get (GstStaticPadTemplate *pad_template);
const GstCaps* gst_pad_template_get_caps (GstPadTemplate *templ);
#ifndef GST_DISABLE_DEPRECATED
const GstCaps* gst_pad_template_get_caps_by_name (GstPadTemplate *templ, const gchar *name);
#endif
#ifndef GST_DISABLE_LOADSAVE
xmlNodePtr gst_ghost_pad_save_thyself (GstPad *pad,
xmlNodePtr parent);
#endif
/* for schedulers only */
void gst_pad_call_chain_function (GstPad *pad, GstData *data);
GstData * gst_pad_call_get_function (GstPad *pad);
G_END_DECLS

View file

@ -1,6 +1,6 @@
/* GStreamer
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
* 2000 Wim Taymans <wtay@chello.be>
* 2004 Wim Taymans <wim@fluendo.com>
*
* gstpipeline.c: Overall pipeline management element
*
@ -25,12 +25,13 @@
#include "gstpipeline.h"
#include "gstinfo.h"
#include "gstscheduler.h"
#include "gstsystemclock.h"
static GstElementDetails gst_pipeline_details =
GST_ELEMENT_DETAILS ("Pipeline object",
"Generic/Bin",
"Complete pipeline object",
"Erik Walthinsen <omega@cse.ogi.edu>");
"Erik Walthinsen <omega@cse.ogi.edu>" "Wim Taymans <wim@fluendo.com>");
/* Pipeline signals and args */
enum
@ -52,6 +53,10 @@ static void gst_pipeline_init (GTypeInstance * instance, gpointer g_class);
static void gst_pipeline_dispose (GObject * object);
static GstBusSyncReply pipeline_bus_handler (GstBus * bus, GstMessage * message,
GstPipeline * pipeline);
static GstClock *gst_pipeline_get_clock_func (GstElement * element);
static GstElementStateReturn gst_pipeline_change_state (GstElement * element);
static GstBinClass *parent_class = NULL;
@ -104,43 +109,139 @@ gst_pipeline_class_init (gpointer g_class, gpointer class_data)
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_pipeline_change_state);
gstelement_class->get_clock = GST_DEBUG_FUNCPTR (gst_pipeline_get_clock_func);
}
static void
gst_pipeline_init (GTypeInstance * instance, gpointer g_class)
{
GstScheduler *scheduler;
GstPipeline *pipeline = GST_PIPELINE (instance);
/* pipelines are managing bins */
GST_FLAG_SET (pipeline, GST_BIN_FLAG_MANAGER);
/* get an instance of the default scheduler */
scheduler = gst_scheduler_factory_make (NULL, GST_ELEMENT (pipeline));
pipeline->scheduler =
gst_scheduler_factory_make (NULL, GST_ELEMENT (pipeline));
/* FIXME need better error handling */
if (scheduler == NULL) {
if (pipeline->scheduler == NULL) {
const gchar *name = gst_scheduler_factory_get_default_name ();
g_error ("Critical error: could not get scheduler \"%s\"\n"
"Are you sure you have a registry ?\n"
"Run gst-register as root if you haven't done so yet.", name);
}
pipeline->bus = g_object_new (gst_bus_get_type (), NULL);
gst_bus_set_sync_handler (pipeline->bus,
(GstBusSyncHandler) pipeline_bus_handler, pipeline);
pipeline->eosed = NULL;
GST_ELEMENT_MANAGER (pipeline) = pipeline;
}
static void
gst_pipeline_dispose (GObject * object)
{
GstPipeline *pipeline = GST_PIPELINE (object);
GstScheduler *sched;
g_assert (GST_IS_SCHEDULER (GST_ELEMENT_SCHED (pipeline)));
sched = GST_ELEMENT_SCHED (pipeline);
g_assert (GST_IS_SCHEDULER (pipeline->scheduler));
gst_scheduler_reset (sched);
gst_scheduler_reset (pipeline->scheduler);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static gboolean
is_eos (GstPipeline * pipeline)
{
GstIterator *sinks;
gboolean result = TRUE;
gboolean done = FALSE;
sinks = gst_bin_iterate_sinks (GST_BIN (pipeline));
while (!done) {
gpointer data;
switch (gst_iterator_next (sinks, &data)) {
case GST_ITERATOR_OK:
{
GstElement *element = GST_ELEMENT (data);
GList *eosed;
GstElementState state, pending;
gboolean complete;
complete = gst_element_get_state (element, &state, &pending, NULL);
if (!complete) {
GST_DEBUG ("element %s still performing state change",
gst_element_get_name (element));
result = FALSE;
done = TRUE;
break;
} else if (state != GST_STATE_PLAYING) {
GST_DEBUG ("element %s not playing %d %d",
gst_element_get_name (element), GST_STATE (element),
GST_STATE_PENDING (element));
break;
}
eosed = g_list_find (pipeline->eosed, element);
if (!eosed) {
result = FALSE;
done = TRUE;
}
gst_object_unref (GST_OBJECT (element));
break;
}
case GST_ITERATOR_RESYNC:
result = TRUE;
gst_iterator_resync (sinks);
break;
case GST_ITERATOR_DONE:
done = TRUE;
break;
default:
g_assert_not_reached ();
break;
}
}
return result;
}
static GstBusSyncReply
pipeline_bus_handler (GstBus * bus, GstMessage * message,
GstPipeline * pipeline)
{
GstBusSyncReply result = GST_BUS_PASS;
gboolean posteos = FALSE;
gboolean locked;
/* we don't want messages from the streaming thread while we're doing the
* state change. We do want them from the state change functions. */
locked = GST_STATE_TRYLOCK (pipeline);
switch (GST_MESSAGE_TYPE (message)) {
case GST_MESSAGE_EOS:
if (GST_MESSAGE_SRC (message) != GST_OBJECT (pipeline)) {
pipeline->eosed =
g_list_prepend (pipeline->eosed, GST_MESSAGE_SRC (message));
if (is_eos (pipeline)) {
posteos = TRUE;
}
/* we drop all EOS messages */
result = GST_BUS_DROP;
}
case GST_MESSAGE_ERROR:
break;
default:
break;
}
if (locked)
GST_STATE_UNLOCK (pipeline);
if (posteos) {
gst_bus_post (bus, gst_message_new_eos (GST_OBJECT (pipeline)));
}
return result;
}
/**
* gst_pipeline_new:
* @name: name of new pipeline
@ -158,20 +259,188 @@ gst_pipeline_new (const gchar * name)
static GstElementStateReturn
gst_pipeline_change_state (GstElement * element)
{
GstElementStateReturn result = GST_STATE_SUCCESS;
GstPipeline *pipeline = GST_PIPELINE (element);
switch (GST_STATE_TRANSITION (element)) {
case GST_STATE_NULL_TO_READY:
gst_scheduler_setup (GST_ELEMENT_SCHED (element));
gst_scheduler_setup (pipeline->scheduler);
break;
case GST_STATE_READY_TO_PAUSED:
gst_element_set_clock (element, gst_element_get_clock (element));
pipeline->eosed = NULL;
break;
case GST_STATE_PAUSED_TO_PLAYING:
if (element->clock) {
element->base_time = gst_clock_get_time (element->clock);
}
break;
case GST_STATE_PLAYING_TO_PAUSED:
case GST_STATE_PAUSED_TO_READY:
case GST_STATE_READY_TO_NULL:
break;
}
if (GST_ELEMENT_CLASS (parent_class)->change_state)
return GST_ELEMENT_CLASS (parent_class)->change_state (element);
result = GST_ELEMENT_CLASS (parent_class)->change_state (element);
return GST_STATE_SUCCESS;
/* we wait for async state changes ourselves */
if (result == GST_STATE_ASYNC) {
GST_STATE_UNLOCK (pipeline);
gst_element_get_state (element, NULL, NULL, NULL);
GST_STATE_LOCK (pipeline);
result = GST_STATE_SUCCESS;
}
return result;
}
/**
* gst_pipeline_get_scheduler:
* @pipeline: the pipeline
*
* Gets the #GstScheduler of this pipeline.
*
* Returns: a GstScheduler.
*/
GstScheduler *
gst_pipeline_get_scheduler (GstPipeline * pipeline)
{
return pipeline->scheduler;
}
/**
* gst_pipeline_get_bus:
* @pipeline: the pipeline
*
* Gets the #GstBus of this pipeline.
*
* Returns: a GstBus
*/
GstBus *
gst_pipeline_get_bus (GstPipeline * pipeline)
{
return pipeline->bus;
}
static GstClock *
gst_pipeline_get_clock_func (GstElement * element)
{
GstClock *clock = NULL;
GstPipeline *pipeline = GST_PIPELINE (element);
/* if we have a fixed clock, use that one */
if (GST_FLAG_IS_SET (pipeline, GST_PIPELINE_FLAG_FIXED_CLOCK)) {
clock = pipeline->fixed_clock;
GST_CAT_DEBUG (GST_CAT_CLOCK, "pipeline using fixed clock %p (%s)",
clock, clock ? GST_STR_NULL (GST_OBJECT_NAME (clock)) : "-");
} else {
clock =
GST_ELEMENT_CLASS (parent_class)->get_clock (GST_ELEMENT (pipeline));
/* no clock, use a system clock */
if (!clock) {
clock = gst_system_clock_obtain ();
/* we unref since this function is not supposed to increase refcount
* of clock object returned; this is ok since the systemclock always
* has a refcount of at least one in the current code. */
gst_object_unref (GST_OBJECT (clock));
GST_CAT_DEBUG (GST_CAT_CLOCK, "pipeline obtained system clock: %p (%s)",
clock, clock ? GST_STR_NULL (GST_OBJECT_NAME (clock)) : "-");
} else {
GST_CAT_DEBUG (GST_CAT_CLOCK, "pipeline obtained clock: %p (%s)",
clock, clock ? GST_STR_NULL (GST_OBJECT_NAME (clock)) : "-");
}
}
return clock;
}
/**
* gst_pipeline_get_clock:
* @pipeline: the pipeline
*
* Gets the current clock used by the pipeline.
*
* Returns: a GstClock
*/
GstClock *
gst_pipeline_get_clock (GstPipeline * pipeline)
{
g_return_val_if_fail (GST_IS_PIPELINE (pipeline), NULL);
return gst_pipeline_get_clock_func (GST_ELEMENT (pipeline));
}
/**
* gst_pipeline_use_clock:
* @pipeline: the pipeline
* @clock: the clock to use
*
* Force the pipeline to use the given clock. The pipeline will
* always use the given clock even if new clock providers are added
* to this pipeline.
*/
void
gst_pipeline_use_clock (GstPipeline * pipeline, GstClock * clock)
{
g_return_if_fail (GST_IS_PIPELINE (pipeline));
GST_FLAG_SET (pipeline, GST_PIPELINE_FLAG_FIXED_CLOCK);
gst_object_replace ((GstObject **) & pipeline->fixed_clock,
(GstObject *) clock);
GST_CAT_DEBUG (GST_CAT_CLOCK, "pipeline using fixed clock %p (%s)", clock,
(clock ? GST_OBJECT_NAME (clock) : "nil"));
}
/**
* gst_pipeline_set_clock:
* @pipeline: the pipeline
* @clock: the clock to set
*
* Set the clock for the pipeline. The clock will be distributed
* to all the elements managed by the pipeline.
*/
void
gst_pipeline_set_clock (GstPipeline * pipeline, GstClock * clock)
{
g_return_if_fail (pipeline != NULL);
g_return_if_fail (GST_IS_PIPELINE (pipeline));
GST_ELEMENT_CLASS (parent_class)->set_clock (GST_ELEMENT (pipeline), clock);
}
/**
* gst_pipeline_auto_clock:
* @pipeline: the pipeline
*
* Let the pipeline select a clock automatically.
*/
void
gst_pipeline_auto_clock (GstPipeline * pipeline)
{
g_return_if_fail (pipeline != NULL);
g_return_if_fail (GST_IS_PIPELINE (pipeline));
GST_FLAG_UNSET (pipeline, GST_PIPELINE_FLAG_FIXED_CLOCK);
gst_object_replace ((GstObject **) & pipeline->fixed_clock, NULL);
GST_CAT_DEBUG (GST_CAT_CLOCK, "pipeline using automatic clock");
}
/**
* gst_pipeline_post_message:
* @pipeline: the pipeline
* @message: the message
*
* Post a message on the message bus of this pipeline.
*
* Returns: TRUE if the message could be posted.
*/
gboolean
gst_pipeline_post_message (GstPipeline * pipeline, GstMessage * message)
{
return gst_bus_post (pipeline->bus, message);
}

View file

@ -24,7 +24,9 @@
#ifndef __GST_PIPELINE_H__
#define __GST_PIPELINE_H__
#include <gst/gsttypes.h>
#include <gst/gstbin.h>
#include <gst/gstbus.h>
G_BEGIN_DECLS
@ -35,12 +37,23 @@ G_BEGIN_DECLS
#define GST_IS_PIPELINE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PIPELINE))
#define GST_PIPELINE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_PIPELINE, GstPipelineClass))
typedef struct _GstPipeline GstPipeline;
typedef struct _GstPipelineClass GstPipelineClass;
typedef enum {
/* this pipeline works with a fixed clock */
GST_PIPELINE_FLAG_FIXED_CLOCK = GST_BIN_FLAG_LAST,
/* padding */
GST_PIPELINE_FLAG_LAST = GST_BIN_FLAG_LAST + 4
} GstPipelineFlags;
struct _GstPipeline {
GstBin bin;
GstBus *bus;
GstScheduler *scheduler;
GstClock *fixed_clock; /* fixed clock if any */
GList *eosed; /* list of elements that posted EOS */
gpointer _gst_reserved[GST_PADDING];
};
@ -53,6 +66,14 @@ struct _GstPipelineClass {
GType gst_pipeline_get_type (void);
GstElement* gst_pipeline_new (const gchar *name);
GstScheduler* gst_pipeline_get_scheduler (GstPipeline *pipeline);
GstBus* gst_pipeline_get_bus (GstPipeline *pipeline);
void gst_pipeline_use_clock (GstPipeline *pipeline, GstClock *clock);
void gst_pipeline_set_clock (GstPipeline *pipeline, GstClock *clock);
GstClock* gst_pipeline_get_clock (GstPipeline *pipeline);
void gst_pipeline_auto_clock (GstPipeline *pipeline);
gboolean gst_pipeline_post_message (GstPipeline *pipeline, GstMessage *message);
G_END_DECLS

View file

@ -578,22 +578,6 @@ gst_plugin_get_filename (GstPlugin * plugin)
return plugin->filename;
}
/**
* gst_plugin_get_version:
* @plugin: plugin to get the version of
*
* get the version of the plugin
*
* Returns: the version of the plugin
*/
G_CONST_RETURN gchar *
gst_plugin_get_version (GstPlugin * plugin)
{
g_return_val_if_fail (plugin != NULL, NULL);
return plugin->desc.version;
}
/**
* gst_plugin_get_license:
* @plugin: plugin to get the license of

View file

@ -63,7 +63,7 @@ struct _GstPluginDesc {
gchar *license; /* effective license of plugin */
gchar *package; /* package plugin belongs to */
gchar *origin; /* URL to provider of plugin */
gpointer _gst_reserved[GST_PADDING];
};
@ -71,25 +71,25 @@ struct _GstPlugin {
GstPluginDesc desc;
gchar * filename;
GList * features; /* list of features provided */
gint numfeatures;
GList * features; /* list of features provided */
gint numfeatures;
gpointer manager; /* managing registry */
GModule * module; /* contains the module if plugin is loaded */
gpointer manager; /* managing registry */
GModule * module; /* contains the module if the plugin is loaded */
gpointer _gst_reserved[GST_PADDING];
};
#define GST_PLUGIN_DEFINE(major,minor,name,description,init,version,license,package,origin) \
GST_PLUGIN_EXPORT GstPluginDesc gst_plugin_desc = { \
GST_PLUGIN_EXPORT GstPluginDesc gst_plugin_desc = { \
major, \
minor, \
name, \
description, \
description, \
init, \
NULL, \
version, \
license, \
license, \
package, \
origin, \
GST_PADDING_INIT \
@ -102,18 +102,18 @@ _gst_plugin_static_init__ ##init (void) \
static GstPluginDesc plugin_desc_ = { \
major, \
minor, \
name, \
description, \
init, \
NULL, \
name, \
description, \
init, \
NULL, \
version, \
license, \
license, \
package, \
origin, \
GST_PADDING_INIT \
}; \
_gst_plugin_register_static (&plugin_desc_); \
}
}
#define GST_LICENSE_UNKNOWN "unknown"
@ -125,23 +125,22 @@ typedef gboolean (*GstPluginFilter) (GstPlugin *plugin,
#define GST_TYPE_PLUGIN (gst_plugin_get_type())
GType gst_plugin_get_type (void);
void _gst_plugin_initialize (void);
void _gst_plugin_register_static (GstPluginDesc *desc);
void _gst_plugin_register_static (GstPluginDesc *desc);
G_CONST_RETURN gchar* gst_plugin_get_name (GstPlugin *plugin);
G_CONST_RETURN gchar* gst_plugin_get_description (GstPlugin *plugin);
G_CONST_RETURN gchar* gst_plugin_get_filename (GstPlugin *plugin);
G_CONST_RETURN gchar* gst_plugin_get_version (GstPlugin *plugin);
G_CONST_RETURN gchar* gst_plugin_get_license (GstPlugin *plugin);
G_CONST_RETURN gchar* gst_plugin_get_package (GstPlugin *plugin);
G_CONST_RETURN gchar* gst_plugin_get_origin (GstPlugin *plugin);
G_CONST_RETURN gchar* gst_plugin_get_origin (GstPlugin *plugin);
GModule * gst_plugin_get_module (GstPlugin *plugin);
gboolean gst_plugin_is_loaded (GstPlugin *plugin);
GList* gst_plugin_feature_filter (GstPlugin *plugin,
GList* gst_plugin_feature_filter (GstPlugin *plugin,
GstPluginFeatureFilter filter,
gboolean first,
gpointer user_data);
GList* gst_plugin_list_feature_filter (GList *list,
GList* gst_plugin_list_feature_filter (GList *list,
GstPluginFeatureFilter filter,
gboolean first,
gpointer user_data);
@ -150,15 +149,15 @@ gboolean gst_plugin_name_filter (GstPlugin *plugin, const gchar *name);
GList* gst_plugin_get_feature_list (GstPlugin *plugin);
GstPluginFeature* gst_plugin_find_feature (GstPlugin *plugin, const gchar *name, GType type);
gboolean gst_plugin_check_file (const gchar *filename, GError** error);
GstPlugin * gst_plugin_load_file (const gchar *filename, GError** error);
gboolean gst_plugin_unload_plugin (GstPlugin *plugin);
gboolean gst_plugin_check_file (const gchar *filename, GError** error);
GstPlugin * gst_plugin_load_file (const gchar *filename, GError** error);
gboolean gst_plugin_unload_plugin (GstPlugin *plugin);
void gst_plugin_add_feature (GstPlugin *plugin, GstPluginFeature *feature);
/* shortcuts to load from the registry pool */
gboolean gst_plugin_load (const gchar *name);
gboolean gst_library_load (const gchar *name);
gboolean gst_plugin_load (const gchar *name);
gboolean gst_library_load (const gchar *name);
G_END_DECLS

View file

@ -26,6 +26,7 @@
#include "gstqueue.h"
#include "gstscheduler.h"
#include "gstpipeline.h"
#include "gstevent.h"
#include "gstinfo.h"
#include "gsterror.h"
@ -41,6 +42,24 @@ static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
GST_STATIC_CAPS_ANY);
GST_DEBUG_CATEGORY_STATIC (queue_dataflow);
#define GST_CAT_DEFAULT (queue_dataflow)
#define STATUS(queue, msg) \
GST_CAT_LOG_OBJECT (queue_dataflow, queue, \
"(%s:%s) " msg ": %u of %u-%u buffers, %u of %u-%u " \
"bytes, %" G_GUINT64_FORMAT " of %" G_GUINT64_FORMAT \
"-%" G_GUINT64_FORMAT " ns, %u elements", \
GST_DEBUG_PAD_NAME (pad), \
queue->cur_level.buffers, \
queue->min_threshold.buffers, \
queue->max_size.buffers, \
queue->cur_level.bytes, \
queue->min_threshold.bytes, \
queue->max_size.bytes, \
queue->cur_level.time, \
queue->min_threshold.time, \
queue->max_size.time, \
queue->queue->length)
static GstElementDetails gst_queue_details = GST_ELEMENT_DETAILS ("Queue",
"Generic",
@ -95,13 +114,6 @@ enum
} G_STMT_END
typedef struct _GstQueueEventResponse
{
GstEvent *event;
gboolean ret, handled;
}
GstQueueEventResponse;
static void gst_queue_base_init (GstQueueClass * klass);
static void gst_queue_class_init (GstQueueClass * klass);
static void gst_queue_init (GstQueue * queue);
@ -112,19 +124,24 @@ static void gst_queue_set_property (GObject * object,
static void gst_queue_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec);
static void gst_queue_chain (GstPad * pad, GstData * data);
static GstData *gst_queue_get (GstPad * pad);
static GstFlowReturn gst_queue_chain (GstPad * pad, GstBuffer * buffer);
static GstFlowReturn gst_queue_get (GstPad * pad, GstBuffer ** buffer);
static GstBuffer *gst_queue_bufferalloc (GstPad * pad, guint64 offset,
guint size, GstCaps * caps);
static gboolean gst_queue_handle_sink_event (GstPad * pad, GstEvent * event);
static gboolean gst_queue_handle_src_event (GstPad * pad, GstEvent * event);
static gboolean gst_queue_handle_src_query (GstPad * pad,
GstQueryType type, GstFormat * fmt, gint64 * value);
static GstCaps *gst_queue_getcaps (GstPad * pad);
static GstPadLinkReturn gst_queue_link (GstPad * pad, const GstCaps * caps);
static GstPadLinkReturn gst_queue_link_sink (GstPad * pad, GstPad * peer);
static GstPadLinkReturn gst_queue_link_src (GstPad * pad, GstPad * peer);
static void gst_queue_locked_flush (GstQueue * queue);
static gboolean gst_queue_src_activate (GstPad * pad, gboolean active);
static GstElementStateReturn gst_queue_change_state (GstElement * element);
static gboolean gst_queue_release_locks (GstElement * element);
#define GST_TYPE_QUEUE_LEAKY (queue_leaky_get_type ())
@ -197,6 +214,9 @@ gst_queue_class_init (GstQueueClass * klass)
parent_class = g_type_class_peek_parent (klass);
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_queue_set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_queue_get_property);
/* signals */
gst_queue_signals[SIGNAL_UNDERRUN] =
g_signal_new ("underrun", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST,
@ -267,45 +287,43 @@ gst_queue_class_init (GstQueueClass * klass)
/* set several parent class virtual functions */
gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_queue_finalize);
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_queue_set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_queue_get_property);
gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_queue_change_state);
gstelement_class->release_locks = GST_DEBUG_FUNCPTR (gst_queue_release_locks);
}
static void
gst_queue_init (GstQueue * queue)
{
/* scheduling on this kind of element is, well, interesting */
GST_FLAG_SET (queue, GST_ELEMENT_DECOUPLED);
GST_FLAG_SET (queue, GST_ELEMENT_EVENT_AWARE);
queue->sinkpad =
gst_pad_new_from_template (gst_static_pad_template_get (&sinktemplate),
"sink");
gst_pad_set_chain_function (queue->sinkpad,
GST_DEBUG_FUNCPTR (gst_queue_chain));
gst_element_add_pad (GST_ELEMENT (queue), queue->sinkpad);
gst_pad_set_event_function (queue->sinkpad,
GST_DEBUG_FUNCPTR (gst_queue_handle_sink_event));
gst_pad_set_link_function (queue->sinkpad,
GST_DEBUG_FUNCPTR (gst_queue_link));
GST_DEBUG_FUNCPTR (gst_queue_link_sink));
gst_pad_set_getcaps_function (queue->sinkpad,
GST_DEBUG_FUNCPTR (gst_queue_getcaps));
gst_pad_set_active (queue->sinkpad, TRUE);
gst_pad_set_bufferalloc_function (queue->sinkpad,
GST_DEBUG_FUNCPTR (gst_queue_bufferalloc));
gst_element_add_pad (GST_ELEMENT (queue), queue->sinkpad);
queue->srcpad =
gst_pad_new_from_template (gst_static_pad_template_get (&srctemplate),
"src");
gst_pad_set_get_function (queue->srcpad, GST_DEBUG_FUNCPTR (gst_queue_get));
gst_element_add_pad (GST_ELEMENT (queue), queue->srcpad);
gst_pad_set_link_function (queue->srcpad, GST_DEBUG_FUNCPTR (gst_queue_link));
gst_pad_set_activate_function (queue->srcpad,
GST_DEBUG_FUNCPTR (gst_queue_src_activate));
gst_pad_set_link_function (queue->srcpad,
GST_DEBUG_FUNCPTR (gst_queue_link_src));
gst_pad_set_getcaps_function (queue->srcpad,
GST_DEBUG_FUNCPTR (gst_queue_getcaps));
gst_pad_set_event_function (queue->srcpad,
GST_DEBUG_FUNCPTR (gst_queue_handle_src_event));
gst_pad_set_query_function (queue->srcpad,
GST_DEBUG_FUNCPTR (gst_queue_handle_src_query));
gst_pad_set_active (queue->srcpad, TRUE);
gst_element_add_pad (GST_ELEMENT (queue), queue->srcpad);
queue->cur_level.buffers = 0; /* no content */
queue->cur_level.bytes = 0; /* no content */
@ -326,9 +344,6 @@ gst_queue_init (GstQueue * queue)
queue->qlock = g_mutex_new ();
queue->item_add = g_cond_new ();
queue->item_del = g_cond_new ();
queue->event_done = g_cond_new ();
queue->events = g_queue_new ();
queue->event_lock = g_mutex_new ();
queue->queue = g_queue_new ();
GST_CAT_DEBUG_OBJECT (GST_CAT_THREAD, queue,
@ -349,19 +364,11 @@ gst_queue_finalize (GObject * object)
gst_data_unref (data);
}
g_queue_free (queue->queue);
GST_CAT_DEBUG_OBJECT (GST_CAT_THREAD, queue, "free mutex");
g_mutex_free (queue->qlock);
GST_CAT_DEBUG_OBJECT (GST_CAT_THREAD, queue, "done free mutex");
g_cond_free (queue->item_add);
g_cond_free (queue->item_del);
g_cond_free (queue->event_done);
g_mutex_lock (queue->event_lock);
while (!g_queue_is_empty (queue->events)) {
GstQueueEventResponse *er = g_queue_pop_head (queue->events);
gst_event_unref (er->event);
}
g_mutex_unlock (queue->event_lock);
g_mutex_free (queue->event_lock);
g_queue_free (queue->events);
if (G_OBJECT_CLASS (parent_class)->finalize)
G_OBJECT_CLASS (parent_class)->finalize (object);
@ -371,42 +378,55 @@ static GstCaps *
gst_queue_getcaps (GstPad * pad)
{
GstQueue *queue;
GstPad *otherpad, *otherpeer;
queue = GST_QUEUE (gst_pad_get_parent (pad));
queue = GST_QUEUE (gst_object_get_parent (GST_OBJECT (pad)));
if (queue->cur_level.bytes > 0) {
return gst_caps_copy (queue->negotiated_caps);
otherpad = (pad == queue->srcpad ? queue->sinkpad : queue->srcpad);
otherpeer = gst_pad_get_peer (otherpad);
if (otherpeer == NULL) {
return gst_pad_get_caps (otherpad);
} else {
return gst_pad_get_caps (otherpeer);
}
return gst_pad_proxy_getcaps (pad);
}
static GstPadLinkReturn
gst_queue_link (GstPad * pad, const GstCaps * caps)
gst_queue_link_sink (GstPad * pad, GstPad * peer)
{
return GST_PAD_LINK_OK;
}
static GstPadLinkReturn
gst_queue_link_src (GstPad * pad, GstPad * peer)
{
GstPadLinkReturn result = GST_PAD_LINK_OK;
/* FIXME, see if we need to push or get pulled */
if (GST_RPAD_LINKFUNC (peer))
result = GST_RPAD_LINKFUNC (peer) (peer, pad);
return result;
}
static GstBuffer *
gst_queue_bufferalloc (GstPad * pad, guint64 offset, guint size, GstCaps * caps)
{
GstQueue *queue;
GstPadLinkReturn link_ret;
GstPad *otherpeer;
queue = GST_QUEUE (gst_pad_get_parent (pad));
queue = GST_QUEUE (gst_object_get_parent (GST_OBJECT (pad)));
if (queue->cur_level.bytes > 0) {
if (gst_caps_is_equal (caps, queue->negotiated_caps)) {
return GST_PAD_LINK_OK;
}
return GST_PAD_LINK_REFUSED;
otherpeer = gst_pad_get_peer (queue->srcpad);
if (otherpeer == NULL || GST_RPAD_BUFFERALLOCFUNC (otherpeer) == NULL) {
/* let the default aloc function do the work */
return NULL;
} else {
return GST_RPAD_BUFFERALLOCFUNC (otherpeer) (otherpeer, offset, size, caps);
}
link_ret = gst_pad_proxy_pad_link (pad, caps);
if (GST_PAD_LINK_SUCCESSFUL (link_ret)) {
/* we store an extra copy of the negotiated caps, just in case
* the pads become unnegotiated while we have buffers */
gst_caps_replace (&queue->negotiated_caps, gst_caps_copy (caps));
}
return link_ret;
}
static void
gst_queue_locked_flush (GstQueue * queue)
{
@ -419,7 +439,6 @@ gst_queue_locked_flush (GstQueue * queue)
data when flushing */
gst_data_unref (data);
}
queue->timeval = NULL;
queue->cur_level.buffers = 0;
queue->cur_level.bytes = 0;
queue->cur_level.time = 0;
@ -431,40 +450,6 @@ gst_queue_locked_flush (GstQueue * queue)
g_cond_signal (queue->item_del);
}
static void
gst_queue_handle_pending_events (GstQueue * queue)
{
/* check for events to send upstream */
/* g_queue_get_length is glib 2.4, so don't depend on it yet, use ->length */
GST_CAT_DEBUG_OBJECT (queue_dataflow, queue,
"handling pending events, events queue of size %d",
queue->events->length);
g_mutex_lock (queue->event_lock);
while (!g_queue_is_empty (queue->events)) {
GstQueueEventResponse *er;
er = g_queue_pop_head (queue->events);
GST_CAT_DEBUG_OBJECT (queue_dataflow, queue,
"sending event %p (%d) from event response %p upstream",
er->event, GST_EVENT_TYPE (er->event), er);
if (er->handled) {
/* change this to an assert when this file gets reviewed properly. */
GST_ELEMENT_ERROR (queue, CORE, EVENT, (NULL),
("already handled event %p (%d) from event response %p upstream",
er->event, GST_EVENT_TYPE (er->event), er));
break;
}
g_mutex_unlock (queue->event_lock);
er->ret = gst_pad_event_default (queue->srcpad, er->event);
er->handled = TRUE;
g_cond_signal (queue->event_done);
g_mutex_lock (queue->event_lock);
GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, "event sent");
}
g_mutex_unlock (queue->event_lock);
}
#define STATUS(queue, msg) \
GST_CAT_LOG_OBJECT (queue_dataflow, queue, \
"(%s:%s) " msg ": %u of %u-%u buffers, %u of %u-%u " \
@ -482,58 +467,80 @@ gst_queue_handle_pending_events (GstQueue * queue)
queue->max_size.time, \
queue->queue->length)
static void
gst_queue_chain (GstPad * pad, GstData * data)
static gboolean
gst_queue_handle_sink_event (GstPad * pad, GstEvent * event)
{
GstQueue *queue;
g_return_if_fail (pad != NULL);
g_return_if_fail (GST_IS_PAD (pad));
g_return_if_fail (data != NULL);
queue = GST_QUEUE (GST_OBJECT_PARENT (pad));
restart:
/* we have to lock the queue since we span threads */
GST_QUEUE_MUTEX_LOCK;
gst_queue_handle_pending_events (queue);
/* assume don't need to flush this buffer when the queue is filled */
queue->flush = FALSE;
if (GST_IS_EVENT (data)) {
switch (GST_EVENT_TYPE (data)) {
case GST_EVENT_FLUSH:
STATUS (queue, "received flush event");
gst_queue_locked_flush (queue);
STATUS (queue, "after flush");
break;
case GST_EVENT_EOS:
STATUS (queue, "received EOS");
break;
default:
/* we put the event in the queue, we don't have to act ourselves */
GST_CAT_LOG_OBJECT (queue_dataflow, queue,
"adding event %p of type %d", data, GST_EVENT_TYPE (data));
break;
}
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_FLUSH:
STATUS (queue, "received flush event");
gst_queue_locked_flush (queue);
STATUS (queue, "after flush");
break;
case GST_EVENT_EOS:
STATUS (queue, "received EOS");
break;
default:
/* we put the event in the queue, we don't have to act ourselves */
GST_CAT_LOG_OBJECT (queue_dataflow, queue,
"adding event %p of type %d", event, GST_EVENT_TYPE (event));
break;
}
if (GST_IS_BUFFER (data))
GST_CAT_LOG_OBJECT (queue_dataflow, queue,
"adding buffer %p of size %d", data, GST_BUFFER_SIZE (data));
gst_event_ref (event);
g_queue_push_tail (queue->queue, event);
g_cond_signal (queue->item_add);
GST_QUEUE_MUTEX_UNLOCK;
/* We make space available if we're "full" according to whatever
* the user defined as "full". Note that this only applies to buffers.
* We always handle events and they don't count in our statistics. */
if (GST_IS_BUFFER (data) &&
((queue->max_size.buffers > 0 &&
return TRUE;
}
static gboolean
gst_queue_is_empty (GstQueue * queue)
{
return (queue->queue->length == 0 ||
(queue->min_threshold.buffers > 0 &&
queue->cur_level.buffers < queue->min_threshold.buffers) ||
(queue->min_threshold.bytes > 0 &&
queue->cur_level.bytes < queue->min_threshold.bytes) ||
(queue->min_threshold.time > 0 &&
queue->cur_level.time < queue->min_threshold.time));
}
static gboolean
gst_queue_is_filled (GstQueue * queue)
{
return (((queue->max_size.buffers > 0 &&
queue->cur_level.buffers >= queue->max_size.buffers) ||
(queue->max_size.bytes > 0 &&
queue->cur_level.bytes >= queue->max_size.bytes) ||
(queue->max_size.time > 0 &&
queue->cur_level.time >= queue->max_size.time))) {
queue->cur_level.time >= queue->max_size.time)));
}
static GstFlowReturn
gst_queue_chain (GstPad * pad, GstBuffer * buffer)
{
GstQueue *queue;
queue = GST_QUEUE (GST_OBJECT_PARENT (pad));
/* we have to lock the queue since we span threads */
GST_QUEUE_MUTEX_LOCK;
GST_CAT_LOG_OBJECT (queue_dataflow, queue,
"adding buffer %p of size %d", buffer, GST_BUFFER_SIZE (buffer));
/* We make space available if we're "full" according to whatever
* the user defined as "full". Note that this only applies to buffers.
* We always handle events and they don't count in our statistics. */
if (gst_queue_is_filled (queue)) {
GST_QUEUE_MUTEX_UNLOCK;
g_signal_emit (G_OBJECT (queue), gst_queue_signals[SIGNAL_OVERRUN], 0);
GST_QUEUE_MUTEX_LOCK;
@ -577,15 +584,15 @@ restart:
queue->queue->tail = g_list_last (item);
queue->queue->length--;
/* and unref the data at the end. Twice, because we keep a ref
/* and unref the buffer at the end. Twice, because we keep a ref
* to make things read-only. Also keep our list uptodate. */
queue->cur_level.bytes -= GST_BUFFER_SIZE (data);
queue->cur_level.bytes -= GST_BUFFER_SIZE (buffer);
queue->cur_level.buffers--;
if (GST_BUFFER_DURATION (data) != GST_CLOCK_TIME_NONE)
queue->cur_level.time -= GST_BUFFER_DURATION (data);
if (GST_BUFFER_DURATION (buffer) != GST_CLOCK_TIME_NONE)
queue->cur_level.time -= GST_BUFFER_DURATION (buffer);
gst_data_unref (data);
gst_data_unref (data);
gst_buffer_unref (buffer);
gst_buffer_unref (buffer);
break;
}
@ -597,63 +604,12 @@ restart:
case GST_QUEUE_NO_LEAK:
STATUS (queue, "pre-full wait");
while ((queue->max_size.buffers > 0 &&
queue->cur_level.buffers >= queue->max_size.buffers) ||
(queue->max_size.bytes > 0 &&
queue->cur_level.bytes >= queue->max_size.bytes) ||
(queue->max_size.time > 0 &&
queue->cur_level.time >= queue->max_size.time)) {
while (gst_queue_is_filled (queue)) {
STATUS (queue, "waiting for item_del signal from thread using qlock");
g_cond_wait (queue->item_del, queue->qlock);
/* if there's a pending state change for this queue
* or its manager, switch back to iterator so bottom
* half of state change executes */
if (queue->interrupt) {
GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, "interrupted");
GST_QUEUE_MUTEX_UNLOCK;
if (gst_scheduler_interrupt (gst_pad_get_scheduler (queue->sinkpad),
GST_ELEMENT (queue))) {
goto out_unref;
}
/* if we got here because we were unlocked after a
* flush, we don't need to add the buffer to the
* queue again */
if (queue->flush) {
GST_CAT_DEBUG_OBJECT (queue_dataflow, queue,
"not adding pending buffer after flush");
goto out_unref;
}
GST_CAT_DEBUG_OBJECT (queue_dataflow, queue,
"adding pending buffer after interrupt");
goto restart;
}
if (GST_STATE (queue) != GST_STATE_PLAYING) {
/* this means the other end is shut down. Try to
* signal to resolve the error */
if (!queue->may_deadlock) {
GST_QUEUE_MUTEX_UNLOCK;
gst_data_unref (data);
GST_ELEMENT_ERROR (queue, CORE, THREAD, (NULL),
("deadlock found, shutting down source pad elements"));
/* we don't go to out_unref here, since we want to
* unref the buffer *before* calling GST_ELEMENT_ERROR */
return;
} else {
GST_CAT_WARNING_OBJECT (queue_dataflow, queue,
"%s: waiting for the app to restart "
"source pad elements", GST_ELEMENT_NAME (queue));
}
}
/* OK, we've got a serious issue here. Imagine the situation
* where the puller (next element) is sending an event here,
* so it cannot pull events from the queue, and we cannot
* push data further because the queue is 'full' and therefore,
* we wait here (and do not handle events): deadlock! to solve
* that, we handle pending upstream events here, too. */
gst_queue_handle_pending_events (queue);
STATUS (queue, "waiting for item_del signal from thread using qlock");
g_cond_wait (queue->item_del, queue->qlock);
STATUS (queue, "received item_del signal from thread using qlock");
}
@ -666,19 +622,17 @@ restart:
}
/* put the buffer on the tail of the list. We keep a reference,
* so that the data is read-only while in here. There's a good
* so that the buffer is read-only while in here. There's a good
* reason to do so: we have a size and time counter, and any
* modification to the content could change any of the two. */
gst_data_ref (data);
g_queue_push_tail (queue->queue, data);
gst_buffer_ref (buffer);
g_queue_push_tail (queue->queue, buffer);
/* Note that we only add buffers (not events) to the statistics */
if (GST_IS_BUFFER (data)) {
queue->cur_level.buffers++;
queue->cur_level.bytes += GST_BUFFER_SIZE (data);
if (GST_BUFFER_DURATION (data) != GST_CLOCK_TIME_NONE)
queue->cur_level.time += GST_BUFFER_DURATION (data);
}
/* add buffer to the statistics */
queue->cur_level.buffers++;
queue->cur_level.bytes += GST_BUFFER_SIZE (buffer);
if (GST_BUFFER_DURATION (buffer) != GST_CLOCK_TIME_NONE)
queue->cur_level.time += GST_BUFFER_DURATION (buffer);
STATUS (queue, "+ level");
@ -686,94 +640,41 @@ restart:
g_cond_signal (queue->item_add);
GST_QUEUE_MUTEX_UNLOCK;
return;
return GST_FLOW_OK;
out_unref:
gst_data_unref (data);
return;
gst_buffer_unref (buffer);
return GST_FLOW_OK;
}
static GstData *
gst_queue_get (GstPad * pad)
static GstFlowReturn
gst_queue_get (GstPad * pad, GstBuffer ** buffer)
{
GstQueue *queue;
GstData *data;
GstFlowReturn result = GST_FLOW_OK;
g_return_val_if_fail (pad != NULL, NULL);
g_return_val_if_fail (GST_IS_PAD (pad), NULL);
queue = GST_QUEUE (gst_object_get_parent (GST_OBJECT (pad)));
queue = GST_QUEUE (gst_pad_get_parent (pad));
restart:
/* have to lock for thread-safety */
GST_QUEUE_MUTEX_LOCK;
if (queue->queue->length == 0 ||
(queue->min_threshold.buffers > 0 &&
queue->cur_level.buffers < queue->min_threshold.buffers) ||
(queue->min_threshold.bytes > 0 &&
queue->cur_level.bytes < queue->min_threshold.bytes) ||
(queue->min_threshold.time > 0 &&
queue->cur_level.time < queue->min_threshold.time)) {
restart:
if (gst_queue_is_empty (queue)) {
GST_QUEUE_MUTEX_UNLOCK;
g_signal_emit (G_OBJECT (queue), gst_queue_signals[SIGNAL_UNDERRUN], 0);
GST_QUEUE_MUTEX_LOCK;
STATUS (queue, "pre-empty wait");
while (queue->queue->length == 0 ||
(queue->min_threshold.buffers > 0 &&
queue->cur_level.buffers < queue->min_threshold.buffers) ||
(queue->min_threshold.bytes > 0 &&
queue->cur_level.bytes < queue->min_threshold.bytes) ||
(queue->min_threshold.time > 0 &&
queue->cur_level.time < queue->min_threshold.time)) {
/* if there's a pending state change for this queue or its
* manager, switch back to iterator so bottom half of state
* change executes. */
if (queue->interrupt) {
GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, "interrupted");
GST_QUEUE_MUTEX_UNLOCK;
if (gst_scheduler_interrupt (gst_pad_get_scheduler (queue->srcpad),
GST_ELEMENT (queue)))
return GST_DATA (gst_event_new (GST_EVENT_INTERRUPT));
goto restart;
}
if (GST_STATE (queue) != GST_STATE_PLAYING) {
/* this means the other end is shut down */
if (!queue->may_deadlock) {
GST_QUEUE_MUTEX_UNLOCK;
GST_ELEMENT_ERROR (queue, CORE, THREAD, (NULL),
("deadlock found, shutting down sink pad elements"));
goto restart;
} else {
GST_CAT_WARNING_OBJECT (queue_dataflow, queue,
"%s: waiting for the app to restart "
"source pad elements", GST_ELEMENT_NAME (queue));
}
}
while (gst_queue_is_empty (queue)) {
STATUS (queue, "waiting for item_add");
if (queue->block_timeout != GST_CLOCK_TIME_NONE) {
GTimeVal timeout;
g_get_current_time (&timeout);
g_time_val_add (&timeout, queue->block_timeout / 1000);
GST_LOG_OBJECT (queue, "g_cond_time_wait using qlock from thread %p",
g_thread_self ());
if (!g_cond_timed_wait (queue->item_add, queue->qlock, &timeout)) {
GST_QUEUE_MUTEX_UNLOCK;
GST_CAT_WARNING_OBJECT (queue_dataflow, queue,
"Sending filler event");
return GST_DATA (gst_event_new_filler ());
}
} else {
GST_LOG_OBJECT (queue, "doing g_cond_wait using qlock from thread %p",
g_thread_self ());
g_cond_wait (queue->item_add, queue->qlock);
GST_LOG_OBJECT (queue, "done g_cond_wait using qlock from thread %p",
g_thread_self ());
}
GST_LOG_OBJECT (queue, "doing g_cond_wait using qlock from thread %p",
g_thread_self ());
g_cond_wait (queue->item_add, queue->qlock);
GST_LOG_OBJECT (queue, "done g_cond_wait using qlock from thread %p",
g_thread_self ());
STATUS (queue, "got item_add signal");
}
@ -788,120 +689,68 @@ restart:
GST_CAT_LOG_OBJECT (queue_dataflow, queue,
"retrieved data %p from queue", data);
if (data == NULL)
return NULL;
if (GST_IS_BUFFER (data)) {
/* Update statistics */
queue->cur_level.buffers--;
queue->cur_level.bytes -= GST_BUFFER_SIZE (data);
if (GST_BUFFER_DURATION (data) != GST_CLOCK_TIME_NONE)
queue->cur_level.time -= GST_BUFFER_DURATION (data);
*buffer = GST_BUFFER (data);
} else {
if (GST_EVENT_TYPE (data) == GST_EVENT_EOS) {
result = GST_FLOW_WRONG_STATE;
}
gst_pad_push_event (queue->srcpad, GST_EVENT (data));
if (result == GST_FLOW_OK)
goto restart;
else
goto done;
}
/* Now that we're done, we can lose our own reference to
* the item, since we're no longer in danger. */
gst_data_unref (data);
done:
STATUS (queue, "after _get()");
GST_CAT_LOG_OBJECT (queue_dataflow, queue, "signalling item_del");
g_cond_signal (queue->item_del);
GST_QUEUE_MUTEX_UNLOCK;
/* FIXME: I suppose this needs to be locked, since the EOS
* bit affects the pipeline state. However, that bit is
* locked too so it'd cause a deadlock. */
if (GST_IS_EVENT (data)) {
GstEvent *event = GST_EVENT (data);
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_EOS:
GST_CAT_DEBUG_OBJECT (queue_dataflow, queue,
"queue \"%s\" eos", GST_ELEMENT_NAME (queue));
gst_element_set_eos (GST_ELEMENT (queue));
break;
default:
break;
}
}
return data;
return result;
}
static gboolean
gst_queue_handle_src_event (GstPad * pad, GstEvent * event)
{
GstQueue *queue = GST_QUEUE (gst_pad_get_parent (pad));
gboolean res;
GstQueue *queue = GST_QUEUE (gst_object_get_parent (GST_OBJECT (pad)));
gboolean res = TRUE;
GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, "got event %p (%d)",
event, GST_EVENT_TYPE (event));
gst_event_ref (event);
res = gst_pad_event_default (pad, event);
GST_QUEUE_MUTEX_LOCK;
if (gst_element_get_state (GST_ELEMENT (queue)) == GST_STATE_PLAYING) {
GstQueueEventResponse er;
/* push the event to the queue and wait for upstream consumption */
er.event = event;
er.handled = FALSE;
g_mutex_lock (queue->event_lock);
GST_CAT_DEBUG_OBJECT (queue_dataflow, queue,
"putting event %p (%d) on internal queue", event,
GST_EVENT_TYPE (event));
g_queue_push_tail (queue->events, &er);
g_mutex_unlock (queue->event_lock);
GST_CAT_WARNING_OBJECT (queue_dataflow, queue,
"Preparing for loop for event handler");
/* see the chain function on why this is here - it prevents a deadlock */
g_cond_signal (queue->item_del);
while (!er.handled) {
GTimeVal timeout;
g_get_current_time (&timeout);
g_time_val_add (&timeout, 500 * 1000); /* half a second */
GST_LOG_OBJECT (queue, "doing g_cond_wait using qlock from thread %p",
g_thread_self ());
if (!g_cond_timed_wait (queue->event_done, queue->qlock, &timeout) &&
!er.handled) {
GST_CAT_WARNING_OBJECT (queue_dataflow, queue,
"timeout in upstream event handling, dropping event %p (%d)",
er.event, GST_EVENT_TYPE (er.event));
g_mutex_lock (queue->event_lock);
/* since this queue is for src events (ie upstream), this thread is
* the only one that is pushing stuff on it, so we're sure that
* it's still the tail element. FIXME: But in practice, we should use
* GList instead of GQueue for this so we can remove any element in
* the list. */
g_queue_pop_tail (queue->events);
g_mutex_unlock (queue->event_lock);
gst_event_unref (er.event);
res = FALSE;
goto handled;
}
}
GST_CAT_WARNING_OBJECT (queue_dataflow, queue, "Event handled");
res = er.ret;
} else {
res = gst_pad_event_default (pad, event);
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_FLUSH:
GST_CAT_DEBUG_OBJECT (queue_dataflow, queue,
"FLUSH event, flushing queue\n");
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_FLUSH:
GST_CAT_DEBUG_OBJECT (queue_dataflow, queue,
"FLUSH event, flushing queue\n");
gst_queue_locked_flush (queue);
break;
case GST_EVENT_SEEK:
if (GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH) {
gst_queue_locked_flush (queue);
break;
case GST_EVENT_SEEK:
if (GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH) {
gst_queue_locked_flush (queue);
}
default:
break;
}
}
default:
break;
}
handled:
GST_QUEUE_MUTEX_UNLOCK;
gst_event_unref (event);
return res;
}
@ -911,10 +760,10 @@ gst_queue_handle_src_query (GstPad * pad,
GstQueryType type, GstFormat * fmt, gint64 * value)
{
GstQueue *queue = GST_QUEUE (gst_pad_get_parent (pad));
gboolean res;
res = gst_pad_query (GST_PAD_PEER (queue->sinkpad), type, fmt, value);
if (!res)
if (!GST_PAD_PEER (queue->sinkpad))
return FALSE;
if (!gst_pad_query (GST_PAD_PEER (queue->sinkpad), type, fmt, value))
return FALSE;
if (type == GST_QUERY_POSITION) {
@ -935,22 +784,76 @@ gst_queue_handle_src_query (GstPad * pad,
return TRUE;
}
static gboolean
gst_queue_release_locks (GstElement * element)
static void
gst_queue_loop (GstElement * element)
{
GstQueue *queue;
GstTask *task;
GstBuffer *buffer;
GstFlowReturn ret;
g_return_if_fail (element != NULL);
g_return_if_fail (GST_IS_QUEUE (element));
queue = GST_QUEUE (element);
task = queue->task;
GST_QUEUE_MUTEX_LOCK;
queue->interrupt = TRUE;
g_cond_signal (queue->item_add);
g_cond_signal (queue->item_del);
GST_QUEUE_MUTEX_UNLOCK;
return TRUE;
ret = gst_queue_get (queue->srcpad, &buffer);
if (ret != GST_FLOW_OK) {
GST_CAT_LOG_OBJECT (queue_dataflow, queue, "stopping, get returned %d",
ret);
gst_task_stop (task);
return;
}
ret = gst_pad_push (queue->srcpad, buffer);
if (ret != GST_FLOW_OK) {
GST_CAT_LOG_OBJECT (queue_dataflow, queue, "stopping, push returned %d",
ret);
gst_task_stop (task);
return;
}
}
static gboolean
gst_queue_src_activate (GstPad * pad, gboolean active)
{
gboolean result = FALSE;
GstQueue *queue;
queue = GST_QUEUE (GST_OBJECT_PARENT (pad));
if (active) {
/* if we have a scheduler we can start the task */
if (GST_ELEMENT_MANAGER (queue)) {
GST_STREAM_LOCK (pad);
queue->task =
gst_scheduler_create_task (GST_ELEMENT_MANAGER (queue)->scheduler,
(GstTaskFunction) gst_queue_loop, queue);
gst_task_start (queue->task);
GST_STREAM_UNLOCK (pad);
result = TRUE;
}
} else {
/* step 1, unblock chain and get functions */
queue->interrupt = TRUE;
g_cond_signal (queue->item_add);
g_cond_signal (queue->item_del);
/* step 2, make sure streaming finishes */
GST_STREAM_LOCK (pad);
/* step 3, stop the task */
gst_task_stop (queue->task);
gst_object_unref (GST_OBJECT (queue->task));
GST_STREAM_UNLOCK (pad);
result = TRUE;
}
return result;
}
static GstElementStateReturn
gst_queue_change_state (GstElement * element)
{
@ -970,53 +873,28 @@ gst_queue_change_state (GstElement * element)
case GST_STATE_NULL_TO_READY:
gst_queue_locked_flush (queue);
break;
case GST_STATE_PAUSED_TO_PLAYING:
if (!GST_PAD_IS_LINKED (queue->sinkpad)) {
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, queue,
"queue %s is not linked", GST_ELEMENT_NAME (queue));
/* FIXME can this be? */
g_cond_signal (queue->item_add);
ret = GST_STATE_FAILURE;
goto unlock;
} else {
GstScheduler *src_sched, *sink_sched;
src_sched = gst_pad_get_scheduler (GST_PAD (queue->srcpad));
sink_sched = gst_pad_get_scheduler (GST_PAD (queue->sinkpad));
if (src_sched == sink_sched) {
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, queue,
"queue %s does not connect different schedulers",
GST_ELEMENT_NAME (queue));
g_warning ("queue %s does not connect different schedulers",
GST_ELEMENT_NAME (queue));
ret = GST_STATE_FAILURE;
goto unlock;
}
}
queue->interrupt = FALSE;
case GST_STATE_READY_TO_PAUSED:
break;
case GST_STATE_PAUSED_TO_READY:
gst_queue_locked_flush (queue);
gst_caps_replace (&queue->negotiated_caps, NULL);
case GST_STATE_PAUSED_TO_PLAYING:
queue->interrupt = FALSE;
break;
default:
break;
}
if (GST_ELEMENT_CLASS (parent_class)->change_state)
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
/* this is an ugly hack to make sure our pads are always active.
* Reason for this is that pad activation for the queue element
* depends on 2 schedulers (ugh) */
gst_pad_set_active (queue->sinkpad, TRUE);
gst_pad_set_active (queue->srcpad, TRUE);
unlock:
switch (GST_STATE_TRANSITION (element)) {
case GST_STATE_PLAYING_TO_PAUSED:
break;
case GST_STATE_PAUSED_TO_READY:
gst_queue_locked_flush (queue);
break;
case GST_STATE_READY_TO_NULL:
break;
default:
break;
}
GST_QUEUE_MUTEX_UNLOCK;
GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "done with state change");
@ -1024,7 +902,6 @@ unlock:
return ret;
}
static void
gst_queue_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec)

View file

@ -26,6 +26,7 @@
#include <gst/gstelement.h>
#include <gst/gsttask.h>
G_BEGIN_DECLS
@ -66,6 +67,8 @@ struct _GstQueue {
/* the queue of data we're keeping our grubby hands on */
GQueue *queue;
GstTask *task;
GstQueueSize
cur_level, /* currently in the queue */
max_size, /* max. amount of data allowed in the queue */
@ -80,21 +83,12 @@ struct _GstQueue {
/* it the queue should fail on possible deadlocks */
gboolean may_deadlock;
gboolean interrupt;
gboolean flush;
GMutex *qlock; /* lock for queue (vs object lock) */
GCond *item_add; /* signals buffers now available for reading */
GCond *item_del; /* signals space now available for writing */
GCond *event_done; /* upstream event signaller */
GTimeVal *timeval; /* the timeout for the queue locking */
GQueue *events; /* upstream events get decoupled here */
GstCaps *negotiated_caps;
GMutex *event_lock; /* lock when handling the events queue */
gpointer _gst_reserved[GST_PADDING - 1];
};

View file

@ -76,13 +76,7 @@ gst_scheduler_class_init (GstSchedulerClass * klass)
static void
gst_scheduler_init (GstScheduler * sched)
{
sched->clock_providers = NULL;
sched->clock_receivers = NULL;
sched->schedulers = NULL;
sched->state = GST_SCHEDULER_STATE_NONE;
sched->parent = NULL;
sched->parent_sched = NULL;
sched->clock = NULL;
}
static void
@ -90,15 +84,7 @@ gst_scheduler_dispose (GObject * object)
{
GstScheduler *sched = GST_SCHEDULER (object);
/* thse lists should all be NULL */
GST_DEBUG ("scheduler %p dispose %p %p %p",
object,
sched->clock_providers, sched->clock_receivers, sched->schedulers);
gst_object_replace ((GstObject **) & sched->current_clock, NULL);
gst_object_replace ((GstObject **) & sched->clock, NULL);
G_OBJECT_CLASS (parent_class)->dispose (object);
G_OBJECT_CLASS (parent_class)->dispose (G_OBJECT (sched));
}
/**
@ -139,71 +125,6 @@ gst_scheduler_reset (GstScheduler * sched)
sclass->reset (sched);
}
/**
* gst_scheduler_pad_link:
* @sched: the scheduler
* @srcpad: the srcpad to link
* @sinkpad: the sinkpad to link to
*
* Links the srcpad to the given sinkpad.
*/
void
gst_scheduler_pad_link (GstScheduler * sched, GstPad * srcpad, GstPad * sinkpad)
{
GstSchedulerClass *sclass;
g_return_if_fail (GST_IS_SCHEDULER (sched));
g_return_if_fail (GST_IS_PAD (srcpad));
g_return_if_fail (GST_IS_PAD (sinkpad));
sclass = GST_SCHEDULER_GET_CLASS (sched);
if (sclass->pad_link)
sclass->pad_link (sched, srcpad, sinkpad);
}
/**
* gst_scheduler_pad_unlink:
* @sched: the scheduler
* @srcpad: the srcpad to unlink
* @sinkpad: the sinkpad to unlink from
*
* Unlinks the srcpad from the given sinkpad.
*/
void
gst_scheduler_pad_unlink (GstScheduler * sched, GstPad * srcpad,
GstPad * sinkpad)
{
GstSchedulerClass *sclass;
g_return_if_fail (GST_IS_SCHEDULER (sched));
g_return_if_fail (GST_IS_PAD (srcpad));
g_return_if_fail (GST_IS_PAD (sinkpad));
sclass = GST_SCHEDULER_GET_CLASS (sched);
if (sclass->pad_unlink)
sclass->pad_unlink (sched, srcpad, sinkpad);
}
/**
* gst_scheduler_pad_select:
* @sched: the scheduler
* @padlist: the padlist to select on
*
* register the given padlist for a select operation.
*
* Returns: the pad which received a buffer.
*/
GstPad *
gst_scheduler_pad_select (GstScheduler * sched, GList * padlist)
{
g_return_val_if_fail (GST_IS_SCHEDULER (sched), NULL);
g_return_val_if_fail (padlist != NULL, NULL);
return NULL;
}
/**
* gst_scheduler_add_element:
* @sched: the scheduler
@ -215,43 +136,10 @@ void
gst_scheduler_add_element (GstScheduler * sched, GstElement * element)
{
GstSchedulerClass *sclass;
gboolean redistribute_clock = FALSE;
g_return_if_fail (GST_IS_SCHEDULER (sched));
g_return_if_fail (GST_IS_ELEMENT (element));
/* if it's already in this scheduler, don't bother doing anything */
if (GST_ELEMENT_SCHED (element) == sched) {
GST_CAT_DEBUG (GST_CAT_SCHEDULING, "element %s already in scheduler %p",
GST_ELEMENT_NAME (element), sched);
return;
}
/* if it's not inside this scheduler, it has to be NULL */
g_assert (GST_ELEMENT_SCHED (element) == NULL);
if (gst_element_provides_clock (element)) {
sched->clock_providers = g_list_prepend (sched->clock_providers, element);
GST_CAT_DEBUG (GST_CAT_CLOCK, "added clock provider %s",
GST_ELEMENT_NAME (element));
redistribute_clock = TRUE;
}
if (gst_element_requires_clock (element)) {
sched->clock_receivers = g_list_prepend (sched->clock_receivers, element);
GST_CAT_DEBUG (GST_CAT_CLOCK, "added clock receiver %s",
GST_ELEMENT_NAME (element));
redistribute_clock = TRUE;
}
gst_element_set_scheduler (element, sched);
if (redistribute_clock) {
GstClock *clock;
clock = gst_scheduler_get_clock (sched);
gst_scheduler_set_clock (sched, clock);
}
sclass = GST_SCHEDULER_GET_CLASS (sched);
if (sclass->add_element)
@ -269,420 +157,31 @@ void
gst_scheduler_remove_element (GstScheduler * sched, GstElement * element)
{
GstSchedulerClass *sclass;
GList *link;
gboolean redistribute_clock = FALSE;
g_return_if_fail (GST_IS_SCHEDULER (sched));
g_return_if_fail (GST_IS_ELEMENT (element));
link = g_list_find (sched->clock_providers, element);
if (link) {
sched->clock_providers = g_list_delete_link (sched->clock_providers, link);
GST_CAT_DEBUG (GST_CAT_CLOCK, "removed clock provider %s",
GST_ELEMENT_NAME (element));
redistribute_clock = TRUE;
}
link = g_list_find (sched->clock_receivers, element);
if (link) {
sched->clock_receivers = g_list_delete_link (sched->clock_receivers, link);
GST_CAT_DEBUG (GST_CAT_CLOCK, "removed clock receiver %s",
GST_ELEMENT_NAME (element));
redistribute_clock = TRUE;
}
if (redistribute_clock) {
GstClock *clock;
clock = gst_scheduler_get_clock (sched);
gst_scheduler_set_clock (sched, clock);
}
sclass = GST_SCHEDULER_GET_CLASS (sched);
if (sclass->remove_element)
sclass->remove_element (sched, element);
gst_element_set_scheduler (element, NULL);
}
/**
* gst_scheduler_state_transition:
* @sched: the scheduler
* @element: the element with the state transition
* @transition: the state transition
*
* Tell the scheduler that an element changed its state.
*
* Returns: a GstElementStateReturn indicating success or failure
* of the state transition.
*/
GstElementStateReturn
gst_scheduler_state_transition (GstScheduler * sched, GstElement * element,
gint transition)
GstTask *
gst_scheduler_create_task (GstScheduler * sched, GstTaskFunction func,
gpointer data)
{
GstSchedulerClass *sclass;
GstTask *result = NULL;
g_return_val_if_fail (GST_IS_SCHEDULER (sched), GST_STATE_FAILURE);
g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_FAILURE);
if (element == sched->parent && sched->parent_sched == NULL) {
/* FIXME is distributing the clock in the state change still needed
* when we distribute as soon as we add/remove elements? I think not.*/
switch (transition) {
case GST_STATE_READY_TO_PAUSED:
{
GstClock *clock = gst_scheduler_get_clock (sched);
GST_CAT_DEBUG (GST_CAT_CLOCK,
"scheduler READY to PAUSED clock is %p (%s)", clock,
(clock ? GST_OBJECT_NAME (clock) : "nil"));
gst_scheduler_set_clock (sched, clock);
break;
}
}
}
g_return_val_if_fail (GST_IS_SCHEDULER (sched), result);
sclass = GST_SCHEDULER_GET_CLASS (sched);
if (sclass->state_transition)
return sclass->state_transition (sched, element, transition);
if (sclass->create_task)
result = sclass->create_task (sched, func, data);
return GST_STATE_SUCCESS;
}
/**
* gst_scheduler_scheduling_change:
* @sched: the scheduler
* @element: the element that changed its scheduling strategy
*
* Tell the scheduler that an element changed its scheduling strategy.
* An element could, for example, change its loop function or changes
* from a loop based element to a chain based element.
*/
void
gst_scheduler_scheduling_change (GstScheduler * sched, GstElement * element)
{
GstSchedulerClass *sclass;
g_return_if_fail (GST_IS_SCHEDULER (sched));
g_return_if_fail (GST_IS_ELEMENT (element));
sclass = GST_SCHEDULER_GET_CLASS (sched);
if (sclass->scheduling_change)
sclass->scheduling_change (sched, element);
}
/**
* gst_scheduler_add_scheduler:
* @sched: a #GstScheduler to add to
* @sched2: the #GstScheduler to add
*
* Notifies the scheduler that it has to monitor this scheduler.
*/
void
gst_scheduler_add_scheduler (GstScheduler * sched, GstScheduler * sched2)
{
GstSchedulerClass *sclass;
g_return_if_fail (GST_IS_SCHEDULER (sched));
g_return_if_fail (GST_IS_SCHEDULER (sched2));
g_return_if_fail (sched2->parent_sched == NULL);
GST_DEBUG ("gstscheduler: %p add scheduler %p", sched, sched2);
gst_object_ref (GST_OBJECT (sched2));
gst_object_ref (GST_OBJECT (sched));
sched->schedulers = g_list_prepend (sched->schedulers, sched2);
sched2->parent_sched = sched;
sclass = GST_SCHEDULER_GET_CLASS (sched);
if (sclass->add_scheduler)
sclass->add_scheduler (sched, sched2);
}
/**
* gst_scheduler_remove_scheduler:
* @sched: the scheduler
* @sched2: the scheduler to remove
*
a Notifies the scheduler that it can stop monitoring this scheduler.
*/
void
gst_scheduler_remove_scheduler (GstScheduler * sched, GstScheduler * sched2)
{
GstSchedulerClass *sclass;
g_return_if_fail (GST_IS_SCHEDULER (sched));
g_return_if_fail (GST_IS_SCHEDULER (sched2));
g_return_if_fail (sched2->parent_sched == sched);
GST_DEBUG ("gstscheduler: %p remove scheduler %p", sched, sched2);
sclass = GST_SCHEDULER_GET_CLASS (sched);
if (sclass->remove_scheduler)
sclass->remove_scheduler (sched, sched2);
sched->schedulers = g_list_remove (sched->schedulers, sched2);
sched2->parent_sched = NULL;
gst_object_unref (GST_OBJECT (sched2));
gst_object_unref (GST_OBJECT (sched));
}
/**
* gst_scheduler_lock_element:
* @sched: the scheduler
* @element: the element to lock
*
* Acquire a lock on the given element in the given scheduler.
*/
void
gst_scheduler_lock_element (GstScheduler * sched, GstElement * element)
{
g_return_if_fail (GST_IS_SCHEDULER (sched));
g_return_if_fail (GST_IS_ELEMENT (element));
}
/**
* gst_scheduler_unlock_element:
* @sched: the scheduler
* @element: the element to unlock
*
* Release the lock on the given element in the given scheduler.
*/
void
gst_scheduler_unlock_element (GstScheduler * sched, GstElement * element)
{
GstSchedulerClass *sclass;
g_return_if_fail (GST_IS_SCHEDULER (sched));
g_return_if_fail (GST_IS_ELEMENT (element));
sclass = GST_SCHEDULER_GET_CLASS (sched);
}
/**
* gst_scheduler_error:
* @sched: the scheduler
* @element: the element with the error
*
* Tell the scheduler an element was in error
*/
void
gst_scheduler_error (GstScheduler * sched, GstElement * element)
{
GstSchedulerClass *sclass;
g_return_if_fail (GST_IS_SCHEDULER (sched));
g_return_if_fail (GST_IS_ELEMENT (element));
sclass = GST_SCHEDULER_GET_CLASS (sched);
if (sclass->error)
sclass->error (sched, element);
}
/**
* gst_scheduler_yield:
* @sched: the scheduler
* @element: the element requesting a yield
*
* Tell the scheduler to schedule another element.
*
* Returns: TRUE if the element should save its state, FALSE
* if the scheduler can perform this action itself.
*/
gboolean
gst_scheduler_yield (GstScheduler * sched, GstElement * element)
{
GstSchedulerClass *sclass;
g_return_val_if_fail (GST_IS_SCHEDULER (sched), TRUE);
g_return_val_if_fail (GST_IS_ELEMENT (element), TRUE);
sclass = GST_SCHEDULER_GET_CLASS (sched);
if (sclass->yield)
return sclass->yield (sched, element);
return TRUE;
}
/**
* gst_scheduler_interrupt:
* @sched: the scheduler
* @element: the element requesting an interrupt
*
* Tell the scheduler to interrupt execution of this element.
*
* Returns: TRUE if the element should return NULL from the chain/get
* function.
*/
gboolean
gst_scheduler_interrupt (GstScheduler * sched, GstElement * element)
{
GstSchedulerClass *sclass;
g_return_val_if_fail (GST_IS_SCHEDULER (sched), FALSE);
g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
sclass = GST_SCHEDULER_GET_CLASS (sched);
if (sclass->interrupt)
return sclass->interrupt (sched, element);
return FALSE;
}
/**
* gst_scheduler_get_clock:
* @sched: the scheduler
*
* Gets the current clock used by the scheduler.
*
* Returns: a GstClock
*/
GstClock *
gst_scheduler_get_clock (GstScheduler * sched)
{
GstClock *clock = NULL;
/* if we have a fixed clock, use that one */
if (GST_FLAG_IS_SET (sched, GST_SCHEDULER_FLAG_FIXED_CLOCK)) {
clock = sched->clock;
GST_CAT_DEBUG (GST_CAT_CLOCK, "scheduler using fixed clock %p (%s)",
clock, clock ? GST_STR_NULL (GST_OBJECT_NAME (clock)) : "-");
} else {
GList *schedulers = sched->schedulers;
GList *providers = sched->clock_providers;
/* try to get a clock from one of the schedulers we manage first */
while (schedulers) {
GstScheduler *scheduler = GST_SCHEDULER (schedulers->data);
clock = gst_scheduler_get_clock (scheduler);
if (clock) {
GST_CAT_DEBUG (GST_CAT_CLOCK,
"scheduler found managed sched clock %p (%s)",
clock, clock ? GST_STR_NULL (GST_OBJECT_NAME (clock)) : "-");
break;
}
schedulers = g_list_next (schedulers);
}
/* still no clock, try to find one in the providers */
while (!clock && providers) {
clock = gst_element_get_clock (GST_ELEMENT (providers->data));
if (clock)
GST_CAT_DEBUG (GST_CAT_CLOCK, "scheduler found provider clock: %p (%s)",
clock, clock ? GST_STR_NULL (GST_OBJECT_NAME (clock)) : "-");
providers = g_list_next (providers);
}
/* still no clock, use a system clock */
if (!clock && sched->parent_sched == NULL) {
clock = gst_system_clock_obtain ();
/* we unref since this function is not supposed to increase refcount
* of clock object returned; this is ok since the systemclock always
* has a refcount of at least one in the current code. */
gst_object_unref (GST_OBJECT (clock));
GST_CAT_DEBUG (GST_CAT_CLOCK, "scheduler obtained system clock: %p (%s)",
clock, clock ? GST_STR_NULL (GST_OBJECT_NAME (clock)) : "-");
}
}
return clock;
}
/**
* gst_scheduler_use_clock:
* @sched: the scheduler
* @clock: the clock to use
*
* Force the scheduler to use the given clock. The scheduler will
* always use the given clock even if new clock providers are added
* to this scheduler.
*/
void
gst_scheduler_use_clock (GstScheduler * sched, GstClock * clock)
{
g_return_if_fail (sched != NULL);
g_return_if_fail (GST_IS_SCHEDULER (sched));
GST_FLAG_SET (sched, GST_SCHEDULER_FLAG_FIXED_CLOCK);
gst_object_replace ((GstObject **) & sched->clock, (GstObject *) clock);
GST_CAT_DEBUG (GST_CAT_CLOCK, "scheduler using fixed clock %p (%s)", clock,
(clock ? GST_OBJECT_NAME (clock) : "nil"));
}
/**
* gst_scheduler_set_clock:
* @sched: the scheduler
* @clock: the clock to set
*
* Set the clock for the scheduler. The clock will be distributed
* to all the elements managed by the scheduler.
*/
void
gst_scheduler_set_clock (GstScheduler * sched, GstClock * clock)
{
GList *receivers;
GList *schedulers;
g_return_if_fail (sched != NULL);
g_return_if_fail (GST_IS_SCHEDULER (sched));
receivers = sched->clock_receivers;
schedulers = sched->schedulers;
gst_object_replace ((GstObject **) & sched->current_clock,
(GstObject *) clock);
while (receivers) {
GstElement *element = GST_ELEMENT (receivers->data);
GST_CAT_DEBUG (GST_CAT_CLOCK,
"scheduler setting clock %p (%s) on element %s", clock,
(clock ? GST_OBJECT_NAME (clock) : "nil"), GST_ELEMENT_NAME (element));
gst_element_set_clock (element, clock);
receivers = g_list_next (receivers);
}
while (schedulers) {
GstScheduler *scheduler = GST_SCHEDULER (schedulers->data);
GST_CAT_DEBUG (GST_CAT_CLOCK,
"scheduler setting clock %p (%s) on scheduler %p", clock,
(clock ? GST_OBJECT_NAME (clock) : "nil"), scheduler);
gst_scheduler_set_clock (scheduler, clock);
schedulers = g_list_next (schedulers);
}
}
/**
* gst_scheduler_auto_clock:
* @sched: the scheduler
*
* Let the scheduler select a clock automatically.
*/
void
gst_scheduler_auto_clock (GstScheduler * sched)
{
g_return_if_fail (sched != NULL);
g_return_if_fail (GST_IS_SCHEDULER (sched));
GST_FLAG_UNSET (sched, GST_SCHEDULER_FLAG_FIXED_CLOCK);
gst_object_replace ((GstObject **) & sched->clock, NULL);
GST_CAT_DEBUG (GST_CAT_CLOCK, "scheduler using automatic clock");
return result;
}
GstClockReturn gst_clock_id_wait (GstClockID id, GstClockTimeDiff * jitter);
@ -716,32 +215,6 @@ gst_scheduler_clock_wait (GstScheduler * sched, GstElement * element,
return gst_clock_id_wait (id, jitter);
}
/**
* gst_scheduler_iterate:
* @sched: the scheduler
*
* Perform one iteration on the scheduler.
*
* Returns: a boolean indicating something usefull has happened.
*/
gboolean
gst_scheduler_iterate (GstScheduler * sched)
{
GstSchedulerClass *sclass;
gboolean res = FALSE;
g_return_val_if_fail (GST_IS_SCHEDULER (sched), FALSE);
sclass = GST_SCHEDULER_GET_CLASS (sched);
if (sclass->iterate) {
res = sclass->iterate (sched);
}
return res;
}
/**
* gst_scheduler_show:
* @sched: the scheduler
@ -964,8 +437,6 @@ gst_scheduler_factory_create (GstSchedulerFactory * factory,
sched = GST_SCHEDULER (g_object_new (factory->type, NULL));
sched->parent = parent;
GST_ELEMENT_SCHED (parent) = sched;
/* let's refcount the scheduler */
gst_object_ref (GST_OBJECT (sched));
gst_object_sink (GST_OBJECT (sched));

View file

@ -27,6 +27,7 @@
#include <glib.h>
#include <gst/gstelement.h>
#include <gst/gstbin.h>
#include <gst/gsttask.h>
G_BEGIN_DECLS
@ -38,11 +39,6 @@ G_BEGIN_DECLS
#define GST_SCHEDULER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_SCHEDULER, GstSchedulerClass))
typedef enum {
/* this scheduler works with a fixed clock */
GST_SCHEDULER_FLAG_FIXED_CLOCK = GST_OBJECT_FLAG_LAST,
/* this scheduler supports select and lock calls */
GST_SCHEDULER_FLAG_NEW_API,
/* padding */
GST_SCHEDULER_FLAG_LAST = GST_OBJECT_FLAG_LAST + 4
} GstSchedulerFlags;
@ -52,27 +48,11 @@ typedef enum {
/*typedef struct _GstScheduler GstScheduler; */
/*typedef struct _GstSchedulerClass GstSchedulerClass; */
typedef enum {
GST_SCHEDULER_STATE_NONE,
GST_SCHEDULER_STATE_RUNNING,
GST_SCHEDULER_STATE_STOPPED,
GST_SCHEDULER_STATE_ERROR
} GstSchedulerState;
struct _GstScheduler {
GstObject object;
GstElement *parent;
GstScheduler *parent_sched;
GstSchedulerState state;
GstClock *clock;
GstClock *current_clock;
GList *clock_providers;
GList *clock_receivers;
GList *schedulers;
gpointer _gst_reserved[GST_PADDING];
};
@ -83,26 +63,13 @@ struct _GstSchedulerClass {
/* virtual methods */
void (*setup) (GstScheduler *sched);
void (*reset) (GstScheduler *sched);
void (*add_element) (GstScheduler *sched, GstElement *element);
void (*remove_element) (GstScheduler *sched, GstElement *element);
void (*add_scheduler) (GstScheduler *sched, GstScheduler *sched2);
void (*remove_scheduler) (GstScheduler *sched, GstScheduler *sched2);
GstElementStateReturn (*state_transition) (GstScheduler *sched, GstElement *element, gint transition);
void (*scheduling_change) (GstScheduler *sched, GstElement *element);
/* next two are optional, require NEW_API flag */
/* FIXME 0.9: rename to (un)lock_object */
void (*lock_element) (GstScheduler *sched, GstObject *object);
void (*unlock_element) (GstScheduler *sched, GstObject *object);
gboolean (*yield) (GstScheduler *sched, GstElement *element);
gboolean (*interrupt) (GstScheduler *sched, GstElement *element);
void (*error) (GstScheduler *sched, GstElement *element);
void (*pad_link) (GstScheduler *sched, GstPad *srcpad, GstPad *sinkpad);
void (*pad_unlink) (GstScheduler *sched, GstPad *srcpad, GstPad *sinkpad);
/* optional, requires NEW_API flag */
GstData * (*pad_select) (GstScheduler *sched, GstPad **selected, GstPad **pads);
void (*add_element) (GstScheduler *sched, GstElement * element);
void (*remove_element) (GstScheduler *sched, GstElement * element);
GstTask* (*create_task) (GstScheduler *sched, GstTaskFunction func, gpointer data);
GstClockReturn (*clock_wait) (GstScheduler *sched, GstElement *element,
GstClockID id, GstClockTimeDiff *jitter);
GstSchedulerState (*iterate) (GstScheduler *sched);
/* for debugging */
void (*show) (GstScheduler *sched);
@ -118,32 +85,11 @@ GType gst_scheduler_get_type (void);
void gst_scheduler_setup (GstScheduler *sched);
void gst_scheduler_reset (GstScheduler *sched);
void gst_scheduler_add_element (GstScheduler *sched, GstElement *element);
void gst_scheduler_remove_element (GstScheduler *sched, GstElement *element);
void gst_scheduler_add_scheduler (GstScheduler *sched, GstScheduler *sched2);
void gst_scheduler_remove_scheduler (GstScheduler *sched, GstScheduler *sched2);
GstElementStateReturn gst_scheduler_state_transition (GstScheduler *sched, GstElement *element, gint transition);
void gst_scheduler_scheduling_change (GstScheduler *sched, GstElement *element);
#ifndef GST_DISABLE_DEPRECATED
void gst_scheduler_lock_element (GstScheduler *sched, GstElement *element);
void gst_scheduler_unlock_element (GstScheduler *sched, GstElement *element);
#endif
gboolean gst_scheduler_yield (GstScheduler *sched, GstElement *element);
gboolean gst_scheduler_interrupt (GstScheduler *sched, GstElement *element);
void gst_scheduler_error (GstScheduler *sched, GstElement *element);
void gst_scheduler_pad_link (GstScheduler *sched, GstPad *srcpad, GstPad *sinkpad);
void gst_scheduler_pad_unlink (GstScheduler *sched, GstPad *srcpad, GstPad *sinkpad);
#ifndef GST_DISABLE_DEPRECATED
GstPad* gst_scheduler_pad_select (GstScheduler *sched, GList *padlist);
#endif
GstTask* gst_scheduler_create_task (GstScheduler *sched, GstTaskFunction func, gpointer data);
GstClockReturn gst_scheduler_clock_wait (GstScheduler *sched, GstElement *element,
GstClockID id, GstClockTimeDiff *jitter);
gboolean gst_scheduler_iterate (GstScheduler *sched);
void gst_scheduler_use_clock (GstScheduler *sched, GstClock *clock);
void gst_scheduler_set_clock (GstScheduler *sched, GstClock *clock);
GstClock* gst_scheduler_get_clock (GstScheduler *sched);
void gst_scheduler_auto_clock (GstScheduler *sched);
void gst_scheduler_show (GstScheduler *sched);
@ -159,7 +105,7 @@ void gst_scheduler_show (GstScheduler *sched);
#define GST_SCHEDULER_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_SCHEDULER_FACTORY, GstSchedulerFactoryClass))
/* change this to change the default scheduler */
#define GST_SCHEDULER_DEFAULT_NAME "opt"
#define GST_SCHEDULER_DEFAULT_NAME "thread"
typedef struct _GstSchedulerFactory GstSchedulerFactory;
typedef struct _GstSchedulerFactoryClass GstSchedulerFactoryClass;

View file

@ -191,12 +191,6 @@ gst_system_clock_wait (GstClock * clock, GstClockEntry * entry)
current = gst_clock_get_time (clock);
diff = GST_CLOCK_ENTRY_TIME (entry) - current;
if (diff + clock->max_diff < 0) {
GST_WARNING_OBJECT (clock, "clock is way behind: %" G_GINT64_FORMAT
"s (max allowed is %" G_GINT64_FORMAT "s", -diff, clock->max_diff);
return GST_CLOCK_ENTRY_EARLY;
}
target = gst_system_clock_get_internal_time (clock) + diff;
GST_CAT_DEBUG (GST_CAT_CLOCK, "real_target %" G_GUINT64_FORMAT

View file

@ -94,7 +94,7 @@ gst_type_find_factory_dispose (GObject * object)
GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (object);
if (factory->caps) {
gst_caps_free (factory->caps);
gst_caps_unref (factory->caps);
factory->caps = NULL;
}
if (factory->extensions) {

View file

@ -34,9 +34,12 @@ typedef struct _GstElement GstElement;
typedef struct _GstElementClass GstElementClass;
typedef struct _GstBin GstBin;
typedef struct _GstBinClass GstBinClass;
typedef struct _GstPipeline GstPipeline;
typedef struct _GstPipelineClass GstPipelineClass;
typedef struct _GstScheduler GstScheduler;
typedef struct _GstSchedulerClass GstSchedulerClass;
typedef struct _GstEvent GstEvent;
typedef struct _GstMessage GstMessage;
typedef enum {
GST_STATE_VOID_PENDING = 0,

View file

@ -27,6 +27,8 @@
#include "gstutils.h"
#include "gsturitype.h"
#include "gstinfo.h"
#include "gst-i18n-lib.h"
/**
* gst_util_dump_mem:
@ -367,3 +369,934 @@ gst_print_element_args (GString * buf, gint indent, GstElement * element)
g_free (specs);
}
GstFlowReturn
gst_element_abort_preroll (GstElement * element)
{
GstFlowReturn result = GST_FLOW_OK;
/* grab state change lock */
GST_STATE_LOCK (element);
/* if we are going to PAUSED, we can abort the state change */
if (GST_STATE_TRANSITION (element) == GST_STATE_READY_TO_PAUSED) {
gst_element_abort_state (GST_ELEMENT (element));
}
GST_STATE_UNLOCK (element);
return result;
}
/* call with stream lock held */
GstFlowReturn
gst_element_finish_preroll (GstElement * element, GMutex * streamlock)
{
GstFlowReturn result = GST_FLOW_OK;
/* grab state change lock */
GST_STATE_LOCK (element);
/* if we are going to PAUSED, we can commit the state change */
if (GST_STATE_TRANSITION (element) == GST_STATE_READY_TO_PAUSED) {
gst_element_commit_state (GST_ELEMENT (element));
}
/* if we are paused we need to wait for playing to continue */
if (GST_STATE (element) == GST_STATE_PAUSED) {
GST_CAT_DEBUG (GST_CAT_STATES,
"element %s wants to finish preroll", GST_ELEMENT_NAME (element));
/* FIXME, release streaming lock? */
if (streamlock)
g_mutex_unlock (streamlock);
/* here we wait for the next state change */
do {
GST_CAT_DEBUG (GST_CAT_STATES, "waiting for next state change");
GST_STATE_WAIT (element);
}
while (GST_STATE (element) == GST_STATE_PAUSED);
if (streamlock)
g_mutex_lock (streamlock);
/* check if we got playing */
if (GST_STATE (element) != GST_STATE_PLAYING) {
/* not playing, we can't accept the buffer */
result = GST_FLOW_WRONG_STATE;
}
GST_CAT_DEBUG (GST_CAT_STATES, "done preroll");
}
GST_STATE_UNLOCK (element);
return result;
}
/**
* gst_element_get_compatible_pad_template:
* @element: a #GstElement to get a compatible pad template for.
* @compattempl: the #GstPadTemplate to find a compatible template for.
*
* Retrieves a pad template from @element that is compatible with @compattempl.
* Pads from compatible templates can be linked together.
*
* Returns: a compatible #GstPadTemplate, or NULL if none was found. No
* unreferencing is necessary.
*/
GstPadTemplate *
gst_element_get_compatible_pad_template (GstElement * element,
GstPadTemplate * compattempl)
{
GstPadTemplate *newtempl = NULL;
GList *padlist;
GstElementClass *class;
g_return_val_if_fail (element != NULL, NULL);
g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
g_return_val_if_fail (compattempl != NULL, NULL);
class = GST_ELEMENT_GET_CLASS (element);
padlist = gst_element_class_get_pad_template_list (class);
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
"Looking for a suitable pad template in %s out of %d templates...",
GST_ELEMENT_NAME (element), g_list_length (padlist));
while (padlist) {
GstPadTemplate *padtempl = (GstPadTemplate *) padlist->data;
GstCaps *intersection;
/* Ignore name
* Ignore presence
* Check direction (must be opposite)
* Check caps
*/
GST_CAT_LOG (GST_CAT_CAPS,
"checking pad template %s", padtempl->name_template);
if (padtempl->direction != compattempl->direction) {
GST_CAT_DEBUG (GST_CAT_CAPS,
"compatible direction: found %s pad template \"%s\"",
padtempl->direction == GST_PAD_SRC ? "src" : "sink",
padtempl->name_template);
intersection = gst_caps_intersect (GST_PAD_TEMPLATE_CAPS (compattempl),
GST_PAD_TEMPLATE_CAPS (padtempl));
GST_CAT_DEBUG (GST_CAT_CAPS, "caps are %scompatible",
(intersection ? "" : "not "));
if (!gst_caps_is_empty (intersection))
newtempl = padtempl;
gst_caps_unref (intersection);
if (newtempl)
break;
}
padlist = g_list_next (padlist);
}
if (newtempl)
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
"Returning new pad template %p", newtempl);
else
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "No compatible pad template found");
return newtempl;
}
static GstPad *
gst_element_request_pad (GstElement * element, GstPadTemplate * templ,
const gchar * name)
{
GstPad *newpad = NULL;
GstElementClass *oclass;
oclass = GST_ELEMENT_GET_CLASS (element);
if (oclass->request_new_pad)
newpad = (oclass->request_new_pad) (element, templ, name);
return newpad;
}
/**
* gst_element_get_pad_from_template:
* @element: a #GstElement.
* @templ: a #GstPadTemplate belonging to @element.
*
* Gets a pad from @element described by @templ. If the presence of @templ is
* #GST_PAD_REQUEST, requests a new pad. Can return %NULL for #GST_PAD_SOMETIMES
* templates.
*
* Returns: the #GstPad, or NULL if one could not be found or created.
*/
static GstPad *
gst_element_get_pad_from_template (GstElement * element, GstPadTemplate * templ)
{
GstPad *ret = NULL;
GstPadPresence presence;
/* If this function is ever exported, we need check the validity of `element'
* and `templ', and to make sure the template actually belongs to the
* element. */
presence = GST_PAD_TEMPLATE_PRESENCE (templ);
switch (presence) {
case GST_PAD_ALWAYS:
case GST_PAD_SOMETIMES:
ret = gst_element_get_static_pad (element, templ->name_template);
if (!ret && presence == GST_PAD_ALWAYS)
g_warning
("Element %s has an ALWAYS template %s, but no pad of the same name",
GST_OBJECT_NAME (element), templ->name_template);
break;
case GST_PAD_REQUEST:
ret = gst_element_request_pad (element, templ, NULL);
break;
}
return ret;
}
/**
* gst_element_request_compatible_pad:
* @element: a #GstElement.
* @templ: the #GstPadTemplate to which the new pad should be able to link.
*
* Requests a pad from @element. The returned pad should be unlinked and
* compatible with @templ. Might return an existing pad, or request a new one.
*
* Returns: a #GstPad, or %NULL if one could not be found or created.
*/
GstPad *
gst_element_request_compatible_pad (GstElement * element,
GstPadTemplate * templ)
{
GstPadTemplate *templ_new;
GstPad *pad = NULL;
g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
g_return_val_if_fail (GST_IS_PAD_TEMPLATE (templ), NULL);
/* FIXME: should really loop through the templates, testing each for
* compatibility and pad availability. */
templ_new = gst_element_get_compatible_pad_template (element, templ);
if (templ_new)
pad = gst_element_get_pad_from_template (element, templ_new);
/* This can happen for non-request pads. No need to unref. */
if (pad && GST_PAD_PEER (pad))
pad = NULL;
return pad;
}
/**
* gst_element_get_compatible_pad_filtered:
* @element: a #GstElement in which the pad should be found.
* @pad: the #GstPad to find a compatible one for.
* @filtercaps: the #GstCaps to use as a filter.
*
* Looks for an unlinked pad to which the given pad can link. It is not
* guaranteed that linking the pads will work, though it should work in most
* cases.
*
* Returns: the #GstPad to which a link can be made, or %NULL if one cannot be
* found.
*/
GstPad *
gst_element_get_compatible_pad_filtered (GstElement * element, GstPad * pad,
const GstCaps * filtercaps)
{
const GList *pads;
GstPadTemplate *templ;
GstCaps *templcaps;
GstPad *foundpad = NULL;
g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
g_return_val_if_fail (GST_IS_PAD (pad), NULL);
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
"finding pad in %s compatible with %s:%s and filter %" GST_PTR_FORMAT,
GST_ELEMENT_NAME (element), GST_DEBUG_PAD_NAME (pad), filtercaps);
/* let's use the real pad */
pad = (GstPad *) GST_PAD_REALIZE (pad);
g_return_val_if_fail (pad != NULL, NULL);
g_return_val_if_fail (GST_RPAD_PEER (pad) == NULL, NULL);
/* try to get an existing unlinked pad */
pads = gst_element_get_pad_list (element);
while (pads) {
GstPad *current = GST_PAD (pads->data);
GST_CAT_LOG (GST_CAT_ELEMENT_PADS, "examing pad %s:%s",
GST_DEBUG_PAD_NAME (current));
if (GST_PAD_PEER (current) == NULL &&
gst_pad_can_link_filtered (pad, current, filtercaps)) {
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
"found existing unlinked pad %s:%s", GST_DEBUG_PAD_NAME (current));
return current;
}
pads = g_list_next (pads);
}
/* try to create a new one */
/* requesting is a little crazy, we need a template. Let's create one */
templcaps = GST_PAD_CAPS (pad);
if (filtercaps != NULL) {
GstCaps *temp;
temp = gst_caps_intersect (filtercaps, templcaps);
gst_caps_unref (templcaps);
templcaps = temp;
}
templ = gst_pad_template_new ((gchar *) GST_PAD_NAME (pad),
GST_PAD_DIRECTION (pad), GST_PAD_ALWAYS, templcaps);
foundpad = gst_element_request_compatible_pad (element, templ);
gst_object_unref (GST_OBJECT (templ));
if (foundpad) {
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
"found existing request pad %s:%s", GST_DEBUG_PAD_NAME (foundpad));
return foundpad;
}
GST_CAT_INFO_OBJECT (GST_CAT_ELEMENT_PADS, element,
"Could not find a compatible pad to link to %s:%s",
GST_DEBUG_PAD_NAME (pad));
return NULL;
}
/**
* gst_element_get_compatible_pad:
* @element: a #GstElement in which the pad should be found.
* @pad: the #GstPad to find a compatible one for.
*
* Looks for an unlinked pad to which the given pad can link to.
* It is not guaranteed that linking the pads will work, though
* it should work in most cases.
*
* Returns: the #GstPad to which a link can be made, or %NULL if one
* could not be found.
*/
GstPad *
gst_element_get_compatible_pad (GstElement * element, GstPad * pad)
{
return gst_element_get_compatible_pad_filtered (element, pad, NULL);
}
/**
* gst_element_state_get_name:
* @state: a #GstElementState to get the name of.
*
* Gets a string representing the given state.
*
* Returns: a string with the name of the state.
*/
const gchar *
gst_element_state_get_name (GstElementState state)
{
switch (state) {
#ifdef GST_DEBUG_COLOR
case GST_STATE_VOID_PENDING:
return "NONE_PENDING";
break;
case GST_STATE_NULL:
return "\033[01;34mNULL\033[00m";
break;
case GST_STATE_READY:
return "\033[01;31mREADY\033[00m";
break;
case GST_STATE_PLAYING:
return "\033[01;32mPLAYING\033[00m";
break;
case GST_STATE_PAUSED:
return "\033[01;33mPAUSED\033[00m";
break;
default:
/* This is a memory leak */
return g_strdup_printf ("\033[01;35;41mUNKNOWN!\033[00m(%d)", state);
#else
case GST_STATE_VOID_PENDING:
return "NONE_PENDING";
break;
case GST_STATE_NULL:
return "NULL";
break;
case GST_STATE_READY:
return "READY";
break;
case GST_STATE_PLAYING:
return "PLAYING";
break;
case GST_STATE_PAUSED:
return "PAUSED";
break;
default:
return "UNKNOWN!";
#endif
}
return "";
}
/**
* gst_element_link_pads_filtered:
* @src: a #GstElement containing the source pad.
* @srcpadname: the name of the #GstPad in source element or NULL for any pad.
* @dest: the #GstElement containing the destination pad.
* @destpadname: the name of the #GstPad in destination element or NULL for any pad.
* @filtercaps: the #GstCaps to use as a filter.
*
* Links the two named pads of the source and destination elements.
* Side effect is that if one of the pads has no parent, it becomes a
* child of the parent of the other element. If they have different
* parents, the link fails.
*
* Returns: TRUE if the pads could be linked, FALSE otherwise.
*/
GstPadLinkReturn
gst_element_link_pads_filtered (GstElement * src, const gchar * srcpadname,
GstElement * dest, const gchar * destpadname, const GstCaps * filtercaps)
{
const GList *srcpads, *destpads, *srctempls, *desttempls, *l;
GstPad *srcpad, *destpad;
GstPadTemplate *srctempl, *desttempl;
GstElementClass *srcclass, *destclass;
/* checks */
g_return_val_if_fail (GST_IS_ELEMENT (src), FALSE);
g_return_val_if_fail (GST_IS_ELEMENT (dest), FALSE);
srcclass = GST_ELEMENT_GET_CLASS (src);
destclass = GST_ELEMENT_GET_CLASS (dest);
GST_CAT_INFO (GST_CAT_ELEMENT_PADS,
"trying to link element %s:%s to element %s:%s", GST_ELEMENT_NAME (src),
srcpadname ? srcpadname : "(any)", GST_ELEMENT_NAME (dest),
destpadname ? destpadname : "(any)");
/* now get the pads we're trying to link and a list of all remaining pads */
if (srcpadname) {
srcpad = gst_element_get_pad (src, srcpadname);
if (!srcpad) {
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "no pad %s:%s",
GST_ELEMENT_NAME (src), srcpadname);
return FALSE;
} else {
if (!(GST_PAD_DIRECTION (srcpad) == GST_PAD_SRC)) {
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "pad %s:%s is no src pad",
GST_DEBUG_PAD_NAME (srcpad));
return FALSE;
}
if (GST_PAD_PEER (srcpad) != NULL) {
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "pad %s:%s is already linked",
GST_DEBUG_PAD_NAME (srcpad));
return FALSE;
}
}
srcpads = NULL;
} else {
srcpads = gst_element_get_pad_list (src);
srcpad = srcpads ? (GstPad *) GST_PAD_REALIZE (srcpads->data) : NULL;
}
if (destpadname) {
destpad = gst_element_get_pad (dest, destpadname);
if (!destpad) {
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "no pad %s:%s",
GST_ELEMENT_NAME (dest), destpadname);
return FALSE;
} else {
if (!(GST_PAD_DIRECTION (destpad) == GST_PAD_SINK)) {
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "pad %s:%s is no sink pad",
GST_DEBUG_PAD_NAME (destpad));
return FALSE;
}
if (GST_PAD_PEER (destpad) != NULL) {
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "pad %s:%s is already linked",
GST_DEBUG_PAD_NAME (destpad));
return FALSE;
}
}
destpads = NULL;
} else {
destpads = gst_element_get_pad_list (dest);
destpad = destpads ? (GstPad *) GST_PAD_REALIZE (destpads->data) : NULL;
}
if (srcpadname && destpadname) {
/* two explicitly specified pads */
return gst_pad_link_filtered (srcpad, destpad, filtercaps);
}
if (srcpad) {
/* loop through the allowed pads in the source, trying to find a
* compatible destination pad */
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
"looping through allowed src and dest pads");
do {
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "trying src pad %s:%s",
GST_DEBUG_PAD_NAME (srcpad));
if ((GST_PAD_DIRECTION (srcpad) == GST_PAD_SRC) &&
(GST_PAD_PEER (srcpad) == NULL)) {
GstPad *temp = destpadname ? destpad :
gst_element_get_compatible_pad_filtered (dest, srcpad,
filtercaps);
if (temp
&& gst_pad_link_filtered (srcpad, temp,
filtercaps) == GST_PAD_LINK_OK) {
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "linked pad %s:%s to pad %s:%s",
GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (temp));
return TRUE;
}
}
/* find a better way for this mess */
if (srcpads) {
srcpads = g_list_next (srcpads);
if (srcpads)
srcpad = (GstPad *) GST_PAD_REALIZE (srcpads->data);
}
} while (srcpads);
}
if (srcpadname) {
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "no link possible from %s:%s to %s",
GST_DEBUG_PAD_NAME (srcpad), GST_ELEMENT_NAME (dest));
return FALSE;
}
if (destpad) {
/* loop through the existing pads in the destination */
do {
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "trying dest pad %s:%s",
GST_DEBUG_PAD_NAME (destpad));
if ((GST_PAD_DIRECTION (destpad) == GST_PAD_SINK) &&
(GST_PAD_PEER (destpad) == NULL)) {
GstPad *temp = gst_element_get_compatible_pad_filtered (src, destpad,
filtercaps);
if (temp
&& gst_pad_link_filtered (temp, destpad,
filtercaps) == GST_PAD_LINK_OK) {
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "linked pad %s:%s to pad %s:%s",
GST_DEBUG_PAD_NAME (temp), GST_DEBUG_PAD_NAME (destpad));
return TRUE;
}
}
if (destpads) {
destpads = g_list_next (destpads);
if (destpads)
destpad = (GstPad *) GST_PAD_REALIZE (destpads->data);
}
} while (destpads);
}
if (destpadname) {
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "no link possible from %s to %s:%s",
GST_ELEMENT_NAME (src), GST_DEBUG_PAD_NAME (destpad));
return FALSE;
}
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
"we might have request pads on both sides, checking...");
srctempls = gst_element_class_get_pad_template_list (srcclass);
desttempls = gst_element_class_get_pad_template_list (destclass);
if (srctempls && desttempls) {
while (srctempls) {
srctempl = (GstPadTemplate *) srctempls->data;
if (srctempl->presence == GST_PAD_REQUEST) {
for (l = desttempls; l; l = l->next) {
desttempl = (GstPadTemplate *) l->data;
if (desttempl->presence == GST_PAD_REQUEST &&
desttempl->direction != srctempl->direction) {
if (gst_caps_is_always_compatible (gst_pad_template_get_caps
(srctempl), gst_pad_template_get_caps (desttempl))) {
srcpad =
gst_element_get_request_pad (src, srctempl->name_template);
destpad =
gst_element_get_request_pad (dest, desttempl->name_template);
if (gst_pad_link_filtered (srcpad, destpad,
filtercaps) == GST_PAD_LINK_OK) {
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
"linked pad %s:%s to pad %s:%s",
GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (destpad));
return TRUE;
}
/* it failed, so we release the request pads */
gst_element_release_request_pad (src, srcpad);
gst_element_release_request_pad (dest, destpad);
}
}
}
}
srctempls = srctempls->next;
}
}
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "no link possible from %s to %s",
GST_ELEMENT_NAME (src), GST_ELEMENT_NAME (dest));
return FALSE;
}
/**
* gst_element_link_filtered:
* @src: a #GstElement containing the source pad.
* @dest: the #GstElement containing the destination pad.
* @filtercaps: the #GstCaps to use as a filter.
*
* Links @src to @dest, filtered by @filtercaps. The link must be from source to
* destination; the other direction will not be tried. The function looks for
* existing pads that aren't linked yet. It will request new pads if necessary.
* If multiple links are possible, only one is established.
*
* Returns: TRUE if the elements could be linked, FALSE otherwise.
*/
GstPadLinkReturn
gst_element_link_filtered (GstElement * src, GstElement * dest,
const GstCaps * filtercaps)
{
return gst_element_link_pads_filtered (src, NULL, dest, NULL, filtercaps);
}
/**
* gst_element_link_many:
* @element_1: the first #GstElement in the link chain.
* @element_2: the second #GstElement in the link chain.
* @...: the NULL-terminated list of elements to link in order.
*
* Chain together a series of elements. Uses gst_element_link().
*
* Returns: TRUE on success, FALSE otherwise.
*/
GstPadLinkReturn
gst_element_link_many (GstElement * element_1, GstElement * element_2, ...)
{
va_list args;
g_return_val_if_fail (element_1 != NULL && element_2 != NULL, FALSE);
g_return_val_if_fail (GST_IS_ELEMENT (element_1) &&
GST_IS_ELEMENT (element_2), FALSE);
va_start (args, element_2);
while (element_2) {
if (!gst_element_link (element_1, element_2))
return FALSE;
element_1 = element_2;
element_2 = va_arg (args, GstElement *);
}
va_end (args);
return TRUE;
}
/**
* gst_element_link:
* @src: a #GstElement containing the source pad.
* @dest: the #GstElement containing the destination pad.
*
* Links @src to @dest with no filter caps. See gst_element_link_filtered() for
* more information.
*
* Returns: TRUE if the elements could be linked, FALSE otherwise.
*/
GstPadLinkReturn
gst_element_link (GstElement * src, GstElement * dest)
{
return gst_element_link_pads_filtered (src, NULL, dest, NULL, NULL);
}
/**
* gst_element_link_pads:
* @src: a #GstElement containing the source pad.
* @srcpadname: the name of the #GstPad in the source element.
* @dest: the #GstElement containing the destination pad.
* @destpadname: the name of the #GstPad in destination element.
*
* Links the two named pads of the source and destination elements.
* Side effect is that if one of the pads has no parent, it becomes a
* child of the parent of the other element. If they have different
* parents, the link fails.
*
* Returns: TRUE if the pads could be linked, FALSE otherwise.
*/
GstPadLinkReturn
gst_element_link_pads (GstElement * src, const gchar * srcpadname,
GstElement * dest, const gchar * destpadname)
{
return gst_element_link_pads_filtered (src, srcpadname, dest, destpadname,
NULL);
}
/**
* gst_element_unlink_pads:
* @src: a #GstElement containing the source pad.
* @srcpadname: the name of the #GstPad in source element.
* @dest: a #GstElement containing the destination pad.
* @destpadname: the name of the #GstPad in destination element.
*
* Unlinks the two named pads of the source and destination elements.
*/
void
gst_element_unlink_pads (GstElement * src, const gchar * srcpadname,
GstElement * dest, const gchar * destpadname)
{
GstPad *srcpad, *destpad;
g_return_if_fail (src != NULL);
g_return_if_fail (GST_IS_ELEMENT (src));
g_return_if_fail (srcpadname != NULL);
g_return_if_fail (dest != NULL);
g_return_if_fail (GST_IS_ELEMENT (dest));
g_return_if_fail (destpadname != NULL);
/* obtain the pads requested */
srcpad = gst_element_get_pad (src, srcpadname);
if (srcpad == NULL) {
GST_WARNING_OBJECT (src, "source element has no pad \"%s\"", srcpadname);
return;
}
destpad = gst_element_get_pad (dest, destpadname);
if (srcpad == NULL) {
GST_WARNING_OBJECT (dest, "destination element has no pad \"%s\"",
destpadname);
return;
}
/* we're satisified they can be unlinked, let's do it */
gst_pad_unlink (srcpad, destpad);
}
/**
* gst_element_unlink_many:
* @element_1: the first #GstElement in the link chain.
* @element_2: the second #GstElement in the link chain.
* @...: the NULL-terminated list of elements to unlink in order.
*
* Unlinks a series of elements. Uses gst_element_unlink().
*/
void
gst_element_unlink_many (GstElement * element_1, GstElement * element_2, ...)
{
va_list args;
g_return_if_fail (element_1 != NULL && element_2 != NULL);
g_return_if_fail (GST_IS_ELEMENT (element_1) && GST_IS_ELEMENT (element_2));
va_start (args, element_2);
while (element_2) {
gst_element_unlink (element_1, element_2);
element_1 = element_2;
element_2 = va_arg (args, GstElement *);
}
va_end (args);
}
/**
* gst_element_unlink:
* @src: the source #GstElement to unlink.
* @dest: the sink #GstElement to unlink.
*
* Unlinks all source pads of the source element with all sink pads
* of the sink element to which they are linked.
*/
void
gst_element_unlink (GstElement * src, GstElement * dest)
{
const GList *srcpads;
GstPad *pad;
g_return_if_fail (GST_IS_ELEMENT (src));
g_return_if_fail (GST_IS_ELEMENT (dest));
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "unlinking \"%s\" and \"%s\"",
GST_ELEMENT_NAME (src), GST_ELEMENT_NAME (dest));
srcpads = gst_element_get_pad_list (src);
while (srcpads) {
pad = GST_PAD (srcpads->data);
/* we only care about real src pads */
if (GST_IS_REAL_PAD (pad) && GST_PAD_IS_SRC (pad)) {
GstPad *peerpad = GST_PAD_PEER (pad);
/* see if the pad is connected and is really a pad
* of dest */
if (peerpad && (GST_OBJECT_PARENT (peerpad) == (GstObject *) dest)) {
gst_pad_unlink (pad, peerpad);
}
}
srcpads = g_list_next (srcpads);
}
}
/**
* gst_pad_can_link_filtered:
* @srcpad: the source #GstPad to link.
* @sinkpad: the sink #GstPad to link.
* @filtercaps: the filter #GstCaps.
*
* Checks if the source pad and the sink pad can be linked when constrained
* by the given filter caps. Both @srcpad and @sinkpad must be unlinked.
*
* Returns: TRUE if the pads can be linked, FALSE otherwise.
*/
gboolean
gst_pad_can_link_filtered (GstPad * srcpad, GstPad * sinkpad,
const GstCaps * filtercaps)
{
GstRealPad *realsrc, *realsink;
/* FIXME This function is gross. It's almost a direct copy of
* gst_pad_link_filtered(). Any decent programmer would attempt
* to merge the two functions, which I will do some day. --ds
*/
/* generic checks */
g_return_val_if_fail (srcpad != NULL, FALSE);
g_return_val_if_fail (GST_IS_PAD (srcpad), FALSE);
g_return_val_if_fail (sinkpad != NULL, FALSE);
g_return_val_if_fail (GST_IS_PAD (sinkpad), FALSE);
GST_CAT_INFO (GST_CAT_PADS, "trying to link %s:%s and %s:%s",
GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad));
/* now we need to deal with the real/ghost stuff */
realsrc = GST_PAD_REALIZE (srcpad);
realsink = GST_PAD_REALIZE (sinkpad);
if ((GST_PAD (realsrc) != srcpad) || (GST_PAD (realsink) != sinkpad)) {
GST_CAT_INFO (GST_CAT_PADS, "*actually* linking %s:%s and %s:%s",
GST_DEBUG_PAD_NAME (realsrc), GST_DEBUG_PAD_NAME (realsink));
}
/* FIXME: shouldn't we convert this to g_return_val_if_fail? */
if (GST_RPAD_PEER (realsrc) != NULL) {
GST_CAT_INFO (GST_CAT_PADS, "Real source pad %s:%s has a peer, failed",
GST_DEBUG_PAD_NAME (realsrc));
return FALSE;
}
if (GST_RPAD_PEER (realsink) != NULL) {
GST_CAT_INFO (GST_CAT_PADS, "Real sink pad %s:%s has a peer, failed",
GST_DEBUG_PAD_NAME (realsink));
return FALSE;
}
if (GST_PAD_PARENT (realsrc) == NULL) {
GST_CAT_INFO (GST_CAT_PADS, "Real src pad %s:%s has no parent, failed",
GST_DEBUG_PAD_NAME (realsrc));
return FALSE;
}
if (GST_PAD_PARENT (realsink) == NULL) {
GST_CAT_INFO (GST_CAT_PADS, "Real sink pad %s:%s has no parent, failed",
GST_DEBUG_PAD_NAME (realsrc));
return FALSE;
}
g_return_val_if_fail (realsrc != NULL, GST_PAD_LINK_REFUSED);
g_return_val_if_fail (realsink != NULL, GST_PAD_LINK_REFUSED);
return TRUE;
}
/**
* gst_pad_can_link:
* @srcpad: the source #GstPad to link.
* @sinkpad: the sink #GstPad to link.
*
* Checks if the source pad and the sink pad can be linked.
*
* Returns: TRUE if the pads can be linked, FALSE otherwise.
*/
gboolean
gst_pad_can_link (GstPad * srcpad, GstPad * sinkpad)
{
return gst_pad_can_link_filtered (srcpad, sinkpad, NULL);
}
/**
* gst_object_default_error:
* @object: a #GObject that signalled the error.
* @orig: the #GstObject that initiated the error.
* @error: the GError.
* @debug: an additional debug information string, or NULL.
*
* A default error function.
*
* The default handler will simply print the error string using g_print.
*/
void
gst_object_default_error (GstObject * source, GError * error, gchar * debug)
{
gchar *name = gst_object_get_path_string (source);
g_print (_("ERROR: from element %s: %s\n"), name, error->message);
if (debug)
g_print (_("Additional debug info:\n%s\n"), debug);
g_free (name);
}
/**
* gst_bin_add_many:
* @bin: the bin to add the elements to
* @element_1: the first element to add to the bin
* @...: additional elements to add to the bin
*
* Adds a NULL-terminated list of elements to a bin. This function is
* equivalent to calling #gst_bin_add() for each member of the list.
*/
void
gst_bin_add_many (GstBin * bin, GstElement * element_1, ...)
{
va_list args;
g_return_if_fail (GST_IS_BIN (bin));
g_return_if_fail (GST_IS_ELEMENT (element_1));
va_start (args, element_1);
while (element_1) {
gst_bin_add (bin, element_1);
element_1 = va_arg (args, GstElement *);
}
va_end (args);
}
/**
* gst_bin_remove_many:
* @bin: the bin to remove the elements from
* @element_1: the first element to remove from the bin
* @...: NULL-terminated list of elements to remove from the bin
*
* Remove a list of elements from a bin. This function is equivalent
* to calling #gst_bin_remove with each member of the list.
*/
void
gst_bin_remove_many (GstBin * bin, GstElement * element_1, ...)
{
va_list args;
g_return_if_fail (GST_IS_BIN (bin));
g_return_if_fail (GST_IS_ELEMENT (element_1));
va_start (args, element_1);
while (element_1) {
gst_bin_remove (bin, element_1);
element_1 = va_arg (args, GstElement *);
}
va_end (args);
}

View file

@ -25,7 +25,7 @@
#define __GST_UTILS_H__
#include <glib.h>
#include <gst/gstelement.h>
#include <gst/gstbin.h>
G_BEGIN_DECLS
@ -224,6 +224,49 @@ type_as_function ## _get_type (void) \
#endif /* GST_HAVE_UNALIGNED_ACCESS */
void gst_object_default_error (GstObject * source,
GError * error, gchar * debug);
/* element functions */
GstFlowReturn gst_element_abort_preroll (GstElement *element);
GstFlowReturn gst_element_finish_preroll (GstElement *element, GMutex *streamlock);
GstPad* gst_element_get_compatible_pad (GstElement *element, GstPad *pad);
GstPad* gst_element_get_compatible_pad_filtered (GstElement *element, GstPad *pad,
const GstCaps *filtercaps);
GstPadTemplate* gst_element_get_compatible_pad_template (GstElement *element, GstPadTemplate *compattempl);
G_CONST_RETURN gchar* gst_element_state_get_name (GstElementState state);
GstPadLinkReturn gst_element_link (GstElement *src, GstElement *dest);
GstPadLinkReturn gst_element_link_many (GstElement *element_1,
GstElement *element_2, ...);
GstPadLinkReturn gst_element_link_filtered (GstElement *src, GstElement *dest,
const GstCaps *filtercaps);
void gst_element_unlink (GstElement *src, GstElement *dest);
void gst_element_unlink_many (GstElement *element_1,
GstElement *element_2, ...);
GstPadLinkReturn gst_element_link_pads (GstElement *src, const gchar *srcpadname,
GstElement *dest, const gchar *destpadname);
GstPadLinkReturn gst_element_link_pads_filtered (GstElement *src, const gchar *srcpadname,
GstElement *dest, const gchar *destpadname,
const GstCaps *filtercaps);
void gst_element_unlink_pads (GstElement *src, const gchar *srcpadname,
GstElement *dest, const gchar *destpadname);
/* pad functions */
gboolean gst_pad_can_link (GstPad *srcpad, GstPad *sinkpad);
gboolean gst_pad_can_link_filtered (GstPad *srcpad, GstPad *sinkpad, const GstCaps *filtercaps);
/* bin functions */
void gst_bin_add_many (GstBin *bin, GstElement *element_1, ...);
void gst_bin_remove_many (GstBin *bin, GstElement *element_1, ...);
G_END_DECLS
#endif /* __GST_UTILS_H__ */

View file

@ -13,6 +13,7 @@
#include "../gstinfo.h"
#include "../gsterror.h"
#include "../gsturi.h"
#include "../gstutils.h"
#include "../gstvalue.h"
#include "types.h"
@ -285,7 +286,7 @@ gst_parse_free_link (link_t *link)
g_slist_foreach (link->sink_pads, (GFunc) gst_parse_strfree, NULL);
g_slist_free (link->src_pads);
g_slist_free (link->sink_pads);
if (link->caps) gst_caps_free (link->caps);
if (link->caps) gst_caps_unref (link->caps);
gst_parse_link_free (link);
}
static void
@ -351,7 +352,7 @@ gst_parse_found_pad (GstElement *src, GstPad *pad, gpointer data)
g_signal_handler_disconnect (src, link->signal_id);
g_free (link->src_pad);
g_free (link->sink_pad);
if (link->caps) gst_caps_free (link->caps);
if (link->caps) gst_caps_unref (link->caps);
if (!gst_element_is_locked_state (src))
gst_parse_element_lock (link->sink, FALSE);
g_free (link);
@ -362,7 +363,7 @@ static gboolean
gst_parse_perform_delayed_link (GstElement *src, const gchar *src_pad,
GstElement *sink, const gchar *sink_pad, GstCaps *caps)
{
GList *templs = gst_element_get_pad_template_list (src);
GList *templs = gst_element_class_get_pad_template_list (GST_ELEMENT_GET_CLASS (src));
for (; templs; templs = templs->next) {
GstPadTemplate *templ = (GstPadTemplate *) templs->data;

View file

@ -814,7 +814,7 @@ gst_xml_registry_parse_element_factory (GMarkupParseContext * context,
} else if (!strcmp (tag, "interface")) {
gchar *tmp = g_strndup (text, text_len);
__gst_element_factory_add_interface (factory, tmp);
//__gst_element_factory_add_interface (factory, tmp);
g_free (tmp);
}
@ -1040,8 +1040,8 @@ gst_xml_registry_end_element (GMarkupParseContext * context,
xmlregistry->name_template = NULL;
xmlregistry->caps = NULL;
__gst_element_factory_add_pad_template (GST_ELEMENT_FACTORY
(xmlregistry->current_feature), template);
//__gst_element_factory_add_pad_template (GST_ELEMENT_FACTORY
// (xmlregistry->current_feature), template);
xmlregistry->state = GST_XML_REGISTRY_FEATURE;
xmlregistry->parser = gst_xml_registry_parse_element_factory;
}

View file

@ -1,89 +1,20 @@
if GST_DISABLE_OMEGA_COTHREADS
omegaschedulers =
omegaschedulers_nola =
else
omegaschedulers = \
libgstbasicomegascheduler.la \
libgstentryomegascheduler.la \
libgstoptomegascheduler.la
omegaschedulers_nola = \
libgstbasicomegascheduler \
libgstentryomegascheduler \
libgstoptomegascheduler
endif
plugin_LTLIBRARIES = \
$(omegaschedulers) \
libgstbasicgthreadscheduler.la \
libgstentrygthreadscheduler.la \
libgstoptscheduler.la \
libgstoptgthreadscheduler.la \
libgstfairgthreadscheduler.la
libthreadscheduler.la
AS_LIBTOOL_LIBS = \
$(omegaschedulers_nola) \
libgstbasicgthreadscheduler \
libgstentrygthreadscheduler \
libgstoptscheduler \
libgstoptgthreadscheduler \
libgstfairgthreadscheduler
libthreadscheduler
if GST_DISABLE_OMEGA_COTHREADS
else
libgstbasicomegascheduler_la_SOURCES = gstbasicscheduler.c
libgstbasicomegascheduler_la_CFLAGS = $(GST_OBJ_CFLAGS) -D_COTHREADS_OMEGA
libgstbasicomegascheduler_la_LIBADD = $(GST_OBJ_LIBS) ../libcothreads.la
libgstbasicomegascheduler_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(as_libtool_LDFLAGS)
endif
libthreadscheduler_la_SOURCES = threadscheduler.c
libthreadscheduler_la_CFLAGS = $(GST_OBJ_CFLAGS)
libthreadscheduler_la_LIBADD = $(GST_OBJ_LIBS)
libthreadscheduler_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstbasicgthreadscheduler_la_SOURCES = gstbasicscheduler.c
libgstbasicgthreadscheduler_la_CFLAGS = $(GST_OBJ_CFLAGS) -D_COTHREADS_GTHREAD
libgstbasicgthreadscheduler_la_LIBADD = $(GST_OBJ_LIBS)
libgstbasicgthreadscheduler_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(as_libtool_LDFLAGS)
libgstentrygthreadscheduler_la_SOURCES = entryscheduler.c
libgstentrygthreadscheduler_la_CFLAGS = $(GST_OBJ_CFLAGS) -D_COTHREADS_GTHREAD
libgstentrygthreadscheduler_la_LIBADD = $(GST_OBJ_LIBS)
libgstentrygthreadscheduler_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(as_libtool_LDFLAGS)
if GST_DISABLE_OMEGA_COTHREADS
else
libgstentryomegascheduler_la_SOURCES = entryscheduler.c
libgstentryomegascheduler_la_CFLAGS = $(GST_OBJ_CFLAGS) -D_COTHREADS_OMEGA
libgstentryomegascheduler_la_LIBADD = $(GST_OBJ_LIBS) ../libcothreads.la
libgstentryomegascheduler_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(as_libtool_LDFLAGS)
endif
libgstoptscheduler_la_SOURCES = gstoptimalscheduler.c
libgstoptscheduler_la_CFLAGS = $(GST_OBJ_CFLAGS)
libgstoptscheduler_la_LIBADD = $(GST_OBJ_LIBS)
libgstoptscheduler_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(as_libtool_LDFLAGS)
if GST_DISABLE_OMEGA_COTHREADS
else
libgstoptomegascheduler_la_SOURCES = gstoptimalscheduler.c
libgstoptomegascheduler_la_CFLAGS = $(GST_OBJ_CFLAGS) -D_COTHREADS_OMEGA -DUSE_COTHREADS
libgstoptomegascheduler_la_LIBADD = $(GST_OBJ_LIBS) ../libcothreads.la
libgstoptomegascheduler_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(as_libtool_LDFLAGS)
endif
libgstoptgthreadscheduler_la_SOURCES = gstoptimalscheduler.c
libgstoptgthreadscheduler_la_CFLAGS = $(GST_OBJ_CFLAGS) -D_COTHREADS_GTHREAD -DUSE_COTHREADS
libgstoptgthreadscheduler_la_LIBADD = $(GST_OBJ_LIBS)
libgstoptgthreadscheduler_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(as_libtool_LDFLAGS)
libgstfairgthreadscheduler_la_SOURCES = fairscheduler.c faircothreads.c
libgstfairgthreadscheduler_la_CFLAGS = $(GST_OBJ_CFLAGS) -D_COTHREADS_GTHREAD
libgstfairgthreadscheduler_la_LIBADD = $(GST_OBJ_LIBS)
libgstfairgthreadscheduler_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(as_libtool_LDFLAGS)
noinst_HEADERS = cothreads_compat.h gthread-cothreads.h faircothreads.h
noinst_HEADERS =
if AS_LIBTOOL_WIN32
as_libtool_noinst_DATA_files = $(AS_LIBTOOL_LIB).lib
as_libtool_LDFLAGS = -no-undefined
# depend on this in install-data-local
as-libtool-install-data-local:
for i in $(AS_LIBTOOL_LIBS); do \

View file

@ -153,7 +153,7 @@ gst_bytestream_get_next_buf (GstByteStream * bs)
return FALSE;
GST_DEBUG ("get_next_buf: pulling buffer");
nextbuf = GST_BUFFER (gst_pad_pull (bs->pad));
gst_pad_pull (bs->pad, &nextbuf);
if (!nextbuf)
return FALSE;

View file

@ -39,7 +39,7 @@ GST_BOILERPLATE_FULL (GstFilePad, gst_file_pad, GstRealPad, GST_TYPE_REAL_PAD,
static void gst_file_pad_dispose (GObject * object);
static void gst_file_pad_finalize (GObject * object);
static void gst_file_pad_chain (GstPad * pad, GstData * data);
//static void gst_file_pad_chain (GstPad * pad, GstData * data);
static void gst_file_pad_parent_set (GstObject * object,
GstObject * parent);
@ -68,7 +68,7 @@ gst_file_pad_init (GstFilePad * pad)
/* must do this for set_chain_function to work */
real->direction = GST_PAD_SINK;
gst_pad_set_chain_function (GST_PAD (real), gst_file_pad_chain);
//gst_pad_set_chain_function (GST_PAD (real), gst_file_pad_chain);
pad->adapter = gst_adapter_new ();
pad->in_seek = FALSE;
@ -98,6 +98,7 @@ gst_file_pad_finalize (GObject * object)
GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
}
#if 0
static void
gst_file_pad_chain (GstPad * gst_pad, GstData * data)
{
@ -174,6 +175,7 @@ gst_file_pad_chain (GstPad * gst_pad, GstData * data)
}
}
}
#endif
static void
gst_file_pad_parent_set (GstObject * object, GstObject * parent)
@ -183,10 +185,8 @@ gst_file_pad_parent_set (GstObject * object, GstObject * parent)
/* FIXME: we can only be added to elements, right? */
element = GST_ELEMENT (parent);
if (element->loopfunc)
g_warning ("attempt to add a GstFilePad to a loopbased element.");
if (!GST_FLAG_IS_SET (element, GST_ELEMENT_EVENT_AWARE))
g_warning ("elements using GstFilePad must be event-aware.");
//if (element->loopfunc)
// g_warning ("attempt to add a GstFilePad to a loopbased element.");
GST_CALL_PARENT (GST_OBJECT_CLASS, parent_set, (object, parent));
}

View file

@ -324,7 +324,6 @@ gst_dp_packet_from_event (const GstEvent * event, GstDPHeaderFlag flags,
return FALSE;
case GST_EVENT_EOS:
case GST_EVENT_FLUSH:
case GST_EVENT_EMPTY:
case GST_EVENT_DISCONTINUOUS:
GST_WRITE_UINT64_BE (h + 8, GST_EVENT_TIMESTAMP (event));
pl_length = 0;
@ -354,9 +353,6 @@ gst_dp_packet_from_event (const GstEvent * event, GstDPHeaderFlag flags,
case GST_EVENT_SEGMENT_DONE:
case GST_EVENT_SIZE:
case GST_EVENT_RATE:
case GST_EVENT_FILLER:
case GST_EVENT_TS_OFFSET:
case GST_EVENT_INTERRUPT:
case GST_EVENT_NAVIGATION:
case GST_EVENT_TAG:
g_warning ("Unhandled event type %d, ignoring", GST_EVENT_TYPE (event));
@ -489,7 +485,6 @@ gst_dp_event_from_packet (guint header_length, const guint8 * header,
return FALSE;
case GST_EVENT_EOS:
case GST_EVENT_FLUSH:
case GST_EVENT_EMPTY:
case GST_EVENT_DISCONTINUOUS:
event = gst_event_new (type);
GST_EVENT_TIMESTAMP (event) = GST_DP_HEADER_TIMESTAMP (header);
@ -527,9 +522,6 @@ gst_dp_event_from_packet (guint header_length, const guint8 * header,
case GST_EVENT_SEGMENT_DONE:
case GST_EVENT_SIZE:
case GST_EVENT_RATE:
case GST_EVENT_FILLER:
case GST_EVENT_TS_OFFSET:
case GST_EVENT_INTERRUPT:
case GST_EVENT_NAVIGATION:
case GST_EVENT_TAG:
g_warning ("Unhandled event type %d, ignoring", GST_EVENT_TYPE (event));

View file

@ -24,16 +24,17 @@ endif
libgstelements_la_DEPENDENCIES = ../libgstreamer-@GST_MAJORMINOR@.la
libgstelements_la_SOURCES = \
gstaggregator.c \
gstbufferstore.c \
gstelements.c \
gstfakesink.c \
gstfakesrc.c \
gstfilesink.c \
gstfakesink.c \
gstfilesrc.c \
gstidentity.c \
gstelements.c \
#gstaggregator.c \
gstbufferstore.c \
gstfakesink.c \
gstfilesink.c \
gstfdsink.c \
gstfdsrc.c \
gstidentity.c \
gstmd5sink.c \
$(multifilesrc) \
$(pipefilter) \

View file

@ -55,24 +55,24 @@ extern GType gst_filesrc_get_type (void);
extern GstElementDetails gst_filesrc_details;
static struct _elements_entry _elements[] = {
{"aggregator", GST_RANK_NONE, gst_aggregator_get_type},
// {"aggregator", GST_RANK_NONE, gst_aggregator_get_type},
{"fakesrc", GST_RANK_NONE, gst_fakesrc_get_type},
{"fakesink", GST_RANK_NONE, gst_fakesink_get_type},
{"fdsink", GST_RANK_NONE, gst_fdsink_get_type},
{"fdsrc", GST_RANK_NONE, gst_fdsrc_get_type},
{"filesrc", GST_RANK_NONE, gst_filesrc_get_type},
{"filesink", GST_RANK_NONE, gst_filesink_get_type},
{"identity", GST_RANK_NONE, gst_identity_get_type},
{"md5sink", GST_RANK_NONE, gst_md5sink_get_type},
// {"fdsink", GST_RANK_NONE, gst_fdsink_get_type},
// {"fdsrc", GST_RANK_NONE, gst_fdsrc_get_type},
// {"filesink", GST_RANK_NONE, gst_filesink_get_type},
// {"md5sink", GST_RANK_NONE, gst_md5sink_get_type},
#ifndef HAVE_WIN32
{"multifilesrc", GST_RANK_NONE, gst_multifilesrc_get_type},
{"pipefilter", GST_RANK_NONE, gst_pipefilter_get_type},
// {"multifilesrc", GST_RANK_NONE, gst_multifilesrc_get_type},
// {"pipefilter", GST_RANK_NONE, gst_pipefilter_get_type},
#endif
{"shaper", GST_RANK_NONE, gst_shaper_get_type},
{"statistics", GST_RANK_NONE, gst_statistics_get_type},
{"tee", GST_RANK_NONE, gst_tee_get_type},
{"typefind", GST_RANK_NONE, gst_type_find_element_get_type},
{NULL, 0},
// {"shaper", GST_RANK_NONE, gst_shaper_get_type},
// {"statistics", GST_RANK_NONE, gst_statistics_get_type},
// {"tee", GST_RANK_NONE, gst_tee_get_type},
// {"typefind", GST_RANK_NONE, gst_type_find_element_get_type},
// {NULL, 0},
};
static gboolean

View file

@ -113,7 +113,8 @@ static void gst_fakesink_get_property (GObject * object, guint prop_id,
static GstElementStateReturn gst_fakesink_change_state (GstElement * element);
static void gst_fakesink_chain (GstPad * pad, GstData * _data);
static GstFlowReturn gst_fakesink_chain (GstPad * pad, GstBuffer * buffer);
static gboolean gst_fakesink_event (GstPad * pad, GstEvent * event);
static guint gst_fakesink_signals[LAST_SIGNAL] = { 0 };
@ -138,6 +139,8 @@ gst_fakesink_class_init (GstFakeSinkClass * klass)
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_fakesink_set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_fakesink_get_property);
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_NUM_SINKS,
g_param_spec_int ("num_sinks", "Number of sinks",
@ -169,9 +172,6 @@ gst_fakesink_class_init (GstFakeSinkClass * klass)
gst_marshal_VOID__BOXED_OBJECT, G_TYPE_NONE, 2,
GST_TYPE_BUFFER | G_SIGNAL_TYPE_STATIC_SCOPE, GST_TYPE_PAD);
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_fakesink_set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_fakesink_get_property);
gstelement_class->request_new_pad =
GST_DEBUG_FUNCPTR (gst_fakesink_request_new_pad);
gstelement_class->set_clock = GST_DEBUG_FUNCPTR (gst_fakesink_set_clock);
@ -189,6 +189,7 @@ gst_fakesink_init (GstFakeSink * fakesink)
"sink");
gst_element_add_pad (GST_ELEMENT (fakesink), pad);
gst_pad_set_chain_function (pad, GST_DEBUG_FUNCPTR (gst_fakesink_chain));
gst_pad_set_event_function (pad, GST_DEBUG_FUNCPTR (gst_fakesink_event));
fakesink->silent = FALSE;
fakesink->dump = FALSE;
@ -196,8 +197,6 @@ gst_fakesink_init (GstFakeSink * fakesink)
fakesink->last_message = NULL;
fakesink->state_error = FAKESINK_STATE_ERROR_NONE;
fakesink->signal_handoffs = FALSE;
GST_FLAG_SET (fakesink, GST_ELEMENT_EVENT_AWARE);
}
static void
@ -308,45 +307,72 @@ gst_fakesink_get_property (GObject * object, guint prop_id, GValue * value,
}
}
static void
gst_fakesink_chain (GstPad * pad, GstData * _data)
static gboolean
gst_fakesink_event (GstPad * pad, GstEvent * event)
{
GstBuffer *buf = GST_BUFFER (_data);
GstFakeSink *fakesink;
g_return_if_fail (pad != NULL);
g_return_if_fail (GST_IS_PAD (pad));
g_return_if_fail (buf != NULL);
gboolean result = TRUE;
fakesink = GST_FAKESINK (GST_OBJECT_PARENT (pad));
if (GST_IS_EVENT (buf)) {
GstEvent *event = GST_EVENT (buf);
GST_STREAM_LOCK (pad);
if (!fakesink->silent) {
g_free (fakesink->last_message);
if (!fakesink->silent) {
g_free (fakesink->last_message);
fakesink->last_message =
g_strdup_printf ("chain ******* (%s:%s)E (type: %d) %p",
GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE (event), event);
fakesink->last_message =
g_strdup_printf ("chain ******* (%s:%s)E (type: %d) %p",
GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE (event), event);
g_object_notify (G_OBJECT (fakesink), "last_message");
}
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_DISCONTINUOUS:
if (fakesink->sync && fakesink->clock) {
gint64 value = GST_EVENT_DISCONT_OFFSET (event, 0).value;
gst_element_set_time (GST_ELEMENT (fakesink), value);
}
default:
gst_pad_event_default (pad, event);
break;
}
return;
g_object_notify (G_OBJECT (fakesink), "last_message");
}
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_EOS:
{
gst_element_finish_preroll (GST_ELEMENT (fakesink),
GST_STREAM_GET_LOCK (pad));
gst_pipeline_post_message (GST_ELEMENT_MANAGER (fakesink),
gst_message_new_eos (GST_OBJECT (fakesink)));
break;
}
case GST_EVENT_DISCONTINUOUS:
if (fakesink->sync && fakesink->clock) {
//gint64 value = GST_EVENT_DISCONT_OFFSET (event, 0).value;
}
default:
result = gst_pad_event_default (pad, event);
break;
}
GST_STREAM_UNLOCK (pad);
return result;
}
static GstFlowReturn
gst_fakesink_chain (GstPad * pad, GstBuffer * buffer)
{
GstBuffer *buf = GST_BUFFER (buffer);
GstFakeSink *fakesink;
GstFlowReturn result = GST_FLOW_OK;
GstCaps *caps;
fakesink = GST_FAKESINK (GST_OBJECT_PARENT (pad));
caps = gst_buffer_get_caps (buffer);
if (caps && caps != GST_PAD_CAPS (pad)) {
gst_pad_set_caps (pad, caps);
}
/* grab streaming lock to synchronize with event method */
GST_STREAM_LOCK (pad);
result =
gst_element_finish_preroll (GST_ELEMENT (fakesink),
GST_STREAM_GET_LOCK (pad));
if (result != GST_FLOW_OK)
goto exit;
if (fakesink->sync && fakesink->clock) {
gst_element_wait (GST_ELEMENT (fakesink), GST_BUFFER_TIMESTAMP (buf));
}
@ -374,12 +400,17 @@ gst_fakesink_chain (GstPad * pad, GstData * _data)
gst_util_dump_mem (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
}
exit:
GST_STREAM_UNLOCK (pad);
gst_buffer_unref (buf);
return result;
}
static GstElementStateReturn
gst_fakesink_change_state (GstElement * element)
{
GstElementStateReturn ret = GST_STATE_SUCCESS;
GstFakeSink *fakesink = GST_FAKESINK (element);
switch (GST_STATE_TRANSITION (element)) {
@ -390,6 +421,8 @@ gst_fakesink_change_state (GstElement * element)
case GST_STATE_READY_TO_PAUSED:
if (fakesink->state_error == FAKESINK_STATE_ERROR_READY_PAUSED)
goto error;
/* need to complete preroll before this state change completes */
ret = GST_STATE_ASYNC;
break;
case GST_STATE_PAUSED_TO_PLAYING:
if (fakesink->state_error == FAKESINK_STATE_ERROR_PAUSED_PLAYING)
@ -412,9 +445,9 @@ gst_fakesink_change_state (GstElement * element)
}
if (GST_ELEMENT_CLASS (parent_class)->change_state)
return GST_ELEMENT_CLASS (parent_class)->change_state (element);
GST_ELEMENT_CLASS (parent_class)->change_state (element);
return GST_STATE_SUCCESS;
return ret;
error:
GST_ELEMENT_ERROR (element, CORE, STATE_CHANGE, (NULL), (NULL));

View file

@ -180,6 +180,7 @@ GST_BOILERPLATE_FULL (GstFakeSrc, gst_fakesrc, GstElement, GST_TYPE_ELEMENT,
static GstPad *gst_fakesrc_request_new_pad (GstElement * element,
GstPadTemplate * templ, const gchar * unused);
static void gst_fakesrc_update_functions (GstFakeSrc * src);
static gboolean gst_fakesrc_activate (GstPad * pad, gboolean active);
static void gst_fakesrc_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_fakesrc_get_property (GObject * object, guint prop_id,
@ -188,7 +189,7 @@ static void gst_fakesrc_set_clock (GstElement * element, GstClock * clock);
static GstElementStateReturn gst_fakesrc_change_state (GstElement * element);
static GstData *gst_fakesrc_get (GstPad * pad);
static GstFlowReturn gst_fakesrc_get (GstPad * pad, GstBuffer ** buffer);
static void gst_fakesrc_loop (GstElement * element);
static guint gst_fakesrc_signals[LAST_SIGNAL] = { 0 };
@ -214,6 +215,8 @@ gst_fakesrc_class_init (GstFakeSrcClass * klass)
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_fakesrc_set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_fakesrc_get_property);
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_NUM_SOURCES,
g_param_spec_int ("num-sources", "num-sources", "Number of sources",
@ -281,9 +284,6 @@ gst_fakesrc_class_init (GstFakeSrcClass * klass)
gst_marshal_VOID__BOXED_OBJECT, G_TYPE_NONE, 2,
GST_TYPE_BUFFER | G_SIGNAL_TYPE_STATIC_SCOPE, GST_TYPE_PAD);
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_fakesrc_set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_fakesrc_get_property);
gstelement_class->request_new_pad =
GST_DEBUG_FUNCPTR (gst_fakesrc_request_new_pad);
gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_fakesrc_change_state);
@ -434,7 +434,7 @@ gst_fakesrc_event_handler (GstPad * pad, GstEvent * event)
{
GstFakeSrc *src;
src = GST_FAKESRC (gst_pad_get_parent (pad));
src = GST_FAKESRC (gst_object_get_parent (GST_OBJECT (pad)));
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEEK:
@ -467,13 +467,6 @@ gst_fakesrc_update_functions (GstFakeSrc * src)
{
GList *pads;
if (src->loop_based) {
gst_element_set_loop_function (GST_ELEMENT (src),
GST_DEBUG_FUNCPTR (gst_fakesrc_loop));
} else {
gst_element_set_loop_function (GST_ELEMENT (src), NULL);
}
pads = GST_ELEMENT (src)->pads;
while (pads) {
GstPad *pad = GST_PAD (pads->data);
@ -484,6 +477,7 @@ gst_fakesrc_update_functions (GstFakeSrc * src)
gst_pad_set_get_function (pad, GST_DEBUG_FUNCPTR (gst_fakesrc_get));
}
gst_pad_set_activate_function (pad, gst_fakesrc_activate);
gst_pad_set_event_function (pad, gst_fakesrc_event_handler);
gst_pad_set_event_mask_function (pad, gst_fakesrc_get_event_mask);
gst_pad_set_query_function (pad, gst_fakesrc_query);
@ -790,36 +784,40 @@ gst_fakesrc_create_buffer (GstFakeSrc * src)
return buf;
}
static GstData *
gst_fakesrc_get (GstPad * pad)
static GstFlowReturn
gst_fakesrc_get (GstPad * pad, GstBuffer ** buffer)
{
GstFakeSrc *src;
GstBuffer *buf;
GstClockTime time;
GstFlowReturn result = GST_FLOW_OK;
g_return_val_if_fail (pad != NULL, NULL);
g_return_val_if_fail (pad != NULL, GST_FLOW_ERROR);
src = GST_FAKESRC (GST_OBJECT_PARENT (pad));
g_return_val_if_fail (GST_IS_FAKESRC (src), NULL);
g_return_val_if_fail (GST_IS_FAKESRC (src), GST_FLOW_ERROR);
GST_STREAM_LOCK (pad);
if (src->need_flush) {
src->need_flush = FALSE;
return GST_DATA (gst_event_new (GST_EVENT_FLUSH));
gst_pad_push_event (pad, gst_event_new (GST_EVENT_FLUSH));
}
if (src->buffer_count == src->segment_end) {
if (src->segment_loop) {
return GST_DATA (gst_event_new (GST_EVENT_SEGMENT_DONE));
gst_pad_push_event (pad, gst_event_new (GST_EVENT_SEGMENT_DONE));
} else {
gst_element_set_eos (GST_ELEMENT (src));
return GST_DATA (gst_event_new (GST_EVENT_EOS));
gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS));
result = GST_FLOW_UNEXPECTED;
goto done;
}
}
if (src->rt_num_buffers == 0) {
gst_element_set_eos (GST_ELEMENT (src));
return GST_DATA (gst_event_new (GST_EVENT_EOS));
gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS));
result = GST_FLOW_UNEXPECTED;
goto done;
} else {
if (src->rt_num_buffers > 0)
src->rt_num_buffers--;
@ -827,8 +825,9 @@ gst_fakesrc_get (GstPad * pad)
if (src->eos) {
GST_INFO ("fakesrc is setting eos on pad");
gst_element_set_eos (GST_ELEMENT (src));
return GST_DATA (gst_event_new (GST_EVENT_EOS));
gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS));
result = GST_FLOW_UNEXPECTED;
goto done;
}
buf = gst_fakesrc_create_buffer (src);
@ -867,7 +866,12 @@ gst_fakesrc_get (GstPad * pad)
src->bytes_sent += GST_BUFFER_SIZE (buf);
return GST_DATA (buf);
*buffer = buf;
done:
GST_STREAM_UNLOCK (pad);
return result;
}
/**
@ -881,35 +885,82 @@ gst_fakesrc_loop (GstElement * element)
{
GstFakeSrc *src;
const GList *pads;
GstTask *task;
g_return_if_fail (element != NULL);
g_return_if_fail (GST_IS_FAKESRC (element));
src = GST_FAKESRC (element);
task = src->task;
pads = gst_element_get_pad_list (element);
pads = element->pads;
while (pads) {
GstPad *pad = GST_PAD (pads->data);
GstData *data;
GstBuffer *buffer;
GstFlowReturn ret;
data = gst_fakesrc_get (pad);
gst_pad_push (pad, data);
if (src->eos) {
ret = gst_fakesrc_get (pad, &buffer);
if (ret != GST_FLOW_OK) {
gst_task_stop (task);
return;
}
ret = gst_pad_push (pad, buffer);
if (ret != GST_FLOW_OK) {
gst_task_stop (task);
return;
}
if (src->eos) {
gst_task_stop (task);
return;
}
pads = g_list_next (pads);
}
}
static gboolean
gst_fakesrc_activate (GstPad * pad, gboolean active)
{
gboolean result = FALSE;
GstFakeSrc *fakesrc;
fakesrc = GST_FAKESRC (GST_OBJECT_PARENT (pad));
if (active) {
/* if we have a scheduler we can start the task */
if (GST_ELEMENT_MANAGER (fakesrc)) {
GST_STREAM_LOCK (pad);
fakesrc->task =
gst_scheduler_create_task (GST_ELEMENT_MANAGER (fakesrc)->scheduler,
(GstTaskFunction) gst_fakesrc_loop, fakesrc);
gst_task_start (fakesrc->task);
GST_STREAM_UNLOCK (pad);
result = TRUE;
}
} else {
/* step 1, unblock clock sync (if any) */
/* step 2, make sure streaming finishes */
GST_STREAM_LOCK (pad);
/* step 3, stop the task */
gst_task_stop (fakesrc->task);
gst_object_unref (GST_OBJECT (fakesrc->task));
GST_STREAM_UNLOCK (pad);
result = TRUE;
}
return result;
}
static GstElementStateReturn
gst_fakesrc_change_state (GstElement * element)
{
GstFakeSrc *fakesrc;
GstElementStateReturn result = GST_STATE_FAILURE;
g_return_val_if_fail (GST_IS_FAKESRC (element), GST_STATE_FAILURE);
g_return_val_if_fail (GST_IS_FAKESRC (element), result);
fakesrc = GST_FAKESRC (element);
@ -917,6 +968,7 @@ gst_fakesrc_change_state (GstElement * element)
case GST_STATE_NULL_TO_READY:
break;
case GST_STATE_READY_TO_PAUSED:
{
fakesrc->buffer_count = 0;
fakesrc->pattern_byte = 0x00;
fakesrc->need_flush = FALSE;
@ -924,7 +976,14 @@ gst_fakesrc_change_state (GstElement * element)
fakesrc->bytes_sent = 0;
fakesrc->rt_num_buffers = fakesrc->num_buffers;
break;
}
case GST_STATE_PAUSED_TO_PLAYING:
break;
}
result = GST_ELEMENT_CLASS (parent_class)->change_state (element);
switch (GST_STATE_TRANSITION (element)) {
case GST_STATE_PLAYING_TO_PAUSED:
break;
case GST_STATE_PAUSED_TO_READY:
@ -941,8 +1000,5 @@ gst_fakesrc_change_state (GstElement * element)
break;
}
if (GST_ELEMENT_CLASS (parent_class)->change_state)
return GST_ELEMENT_CLASS (parent_class)->change_state (element);
return GST_STATE_SUCCESS;
return result;
}

View file

@ -79,6 +79,8 @@ struct _GstFakeSrc {
gboolean loop_based;
gboolean eos;
GstTask *task;
GstFakeSrcOutputType output;
GstFakeSrcDataType data;
GstFakeSrcSizeType sizetype;

View file

@ -170,11 +170,12 @@ static void gst_filesrc_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static gboolean gst_filesrc_check_filesize (GstFileSrc * src);
static GstData *gst_filesrc_get (GstPad * pad);
static GstFlowReturn gst_filesrc_get (GstPad * pad, GstBuffer ** buffer);
static gboolean gst_filesrc_srcpad_event (GstPad * pad, GstEvent * event);
static gboolean gst_filesrc_srcpad_query (GstPad * pad, GstQueryType type,
GstFormat * format, gint64 * value);
static gboolean gst_filesrc_activate (GstPad * pad, gboolean active);
static GstElementStateReturn gst_filesrc_change_state (GstElement * element);
static void gst_filesrc_uri_handler_init (gpointer g_iface,
@ -214,6 +215,8 @@ gst_filesrc_class_init (GstFileSrcClass * klass)
gobject_class = (GObjectClass *) klass;
gobject_class->set_property = gst_filesrc_set_property;
gobject_class->get_property = gst_filesrc_get_property;
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FD,
g_param_spec_int ("fd", "File-descriptor",
@ -235,8 +238,6 @@ gst_filesrc_class_init (GstFileSrcClass * klass)
"Touch data to force disk read", FALSE, G_PARAM_READWRITE));
gobject_class->dispose = gst_filesrc_dispose;
gobject_class->set_property = gst_filesrc_set_property;
gobject_class->get_property = gst_filesrc_get_property;
gstelement_class->change_state = gst_filesrc_change_state;
}
@ -248,6 +249,7 @@ gst_filesrc_init (GstFileSrc * src)
gst_pad_new_from_template (gst_static_pad_template_get (&srctemplate),
"src");
gst_pad_set_get_function (src->srcpad, gst_filesrc_get);
gst_pad_set_activate_function (src->srcpad, gst_filesrc_activate);
gst_pad_set_event_function (src->srcpad, gst_filesrc_srcpad_event);
gst_pad_set_event_mask_function (src->srcpad, gst_filesrc_get_event_mask);
gst_pad_set_query_function (src->srcpad, gst_filesrc_srcpad_query);
@ -672,7 +674,7 @@ gst_filesrc_get_read (GstFileSrc * src)
if (ret == 0) {
GST_DEBUG ("non-regular file hits EOS");
gst_buffer_unref (buf);
gst_element_set_eos (GST_ELEMENT (src));
//gst_element_set_eos (GST_ELEMENT (src));
return GST_DATA (gst_event_new (GST_EVENT_EOS));
}
readsize = ret;
@ -686,20 +688,22 @@ gst_filesrc_get_read (GstFileSrc * src)
return GST_DATA (buf);
}
static GstData *
gst_filesrc_get (GstPad * pad)
static GstFlowReturn
gst_filesrc_get (GstPad * pad, GstBuffer ** buffer)
{
GstFileSrc *src;
GstData *data;
g_return_val_if_fail (pad != NULL, NULL);
src = GST_FILESRC (gst_pad_get_parent (pad));
g_return_val_if_fail (GST_FLAG_IS_SET (src, GST_FILESRC_OPEN), NULL);
g_return_val_if_fail (GST_FLAG_IS_SET (src, GST_FILESRC_OPEN),
GST_FLOW_WRONG_STATE);
/* check for flush */
if (src->need_flush) {
src->need_flush = FALSE;
GST_DEBUG_OBJECT (src, "sending flush");
return GST_DATA (gst_event_new_flush ());
gst_pad_push_event (pad, gst_event_new_flush ());
}
/* check for seek */
if (src->need_discont) {
@ -710,7 +714,7 @@ gst_filesrc_get (GstPad * pad)
gst_event_new_discontinuous (src->need_discont > 1, GST_FORMAT_BYTES,
(guint64) src->curoffset, GST_FORMAT_UNDEFINED);
src->need_discont = 0;
return GST_DATA (event);
gst_pad_push_event (pad, event);
}
/* check for EOF if it's a regular file */
@ -721,20 +725,31 @@ gst_filesrc_get (GstPad * pad)
GST_DEBUG_OBJECT (src, "eos %" G_GINT64_FORMAT " %" G_GINT64_FORMAT,
src->curoffset, src->filelen);
}
gst_element_set_eos (GST_ELEMENT (src));
return GST_DATA (gst_event_new (GST_EVENT_EOS));
//gst_element_set_eos (GST_ELEMENT (src));
gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS));
return GST_FLOW_WRONG_STATE;
}
}
#ifdef HAVE_MMAP
if (src->using_mmap) {
return gst_filesrc_get_mmap (src);
data = gst_filesrc_get_mmap (src);
} else {
return gst_filesrc_get_read (src);
data = gst_filesrc_get_read (src);
}
#else
return gst_filesrc_get_read (src);
data = gst_filesrc_get_read (src);
#endif
if (data == NULL)
return GST_FLOW_ERROR;
if (GST_IS_EVENT (data)) {
gst_pad_push_event (pad, GST_EVENT (data));
} else {
*buffer = GST_BUFFER (data);
}
return GST_FLOW_OK;
}
/* TRUE if the filesize of the file was updated */
@ -848,10 +863,69 @@ gst_filesrc_close_file (GstFileSrc * src)
GST_FLAG_UNSET (src, GST_FILESRC_OPEN);
}
static void
gst_filesrc_loop (GstElement * element)
{
GstFileSrc *filesrc;
GstFlowReturn result;
GstBuffer *buffer;
filesrc = GST_FILESRC (element);
result = gst_filesrc_get (filesrc->srcpad, &buffer);
if (result != GST_FLOW_OK) {
gst_task_stop (filesrc->task);
return;
}
result = gst_pad_push (filesrc->srcpad, buffer);
if (result != GST_FLOW_OK) {
gst_task_stop (filesrc->task);
}
}
static gboolean
gst_filesrc_activate (GstPad * pad, gboolean active)
{
gboolean result = FALSE;
GstFileSrc *filesrc;
filesrc = GST_FILESRC (GST_OBJECT_PARENT (pad));
if (active) {
/* try to start the task */
GST_STREAM_LOCK (pad);
filesrc->task = gst_element_create_task (GST_ELEMENT (filesrc),
(GstTaskFunction) gst_filesrc_loop, filesrc);
if (filesrc->task) {
gst_task_start (filesrc->task);
result = TRUE;
} else {
result = FALSE;
}
GST_STREAM_UNLOCK (pad);
} else {
/* step 1, unblock clock sync (if any) */
/* step 2, make sure streaming finishes */
GST_STREAM_LOCK (pad);
/* step 3, stop the task */
gst_task_stop (filesrc->task);
gst_object_unref (GST_OBJECT (filesrc->task));
GST_STREAM_UNLOCK (pad);
result = TRUE;
}
return result;
}
static GstElementStateReturn
gst_filesrc_change_state (GstElement * element)
{
GstElementStateReturn result = GST_STATE_SUCCESS;
GstFileSrc *src = GST_FILESRC (element);
switch (GST_STATE_TRANSITION (element)) {
@ -865,6 +939,16 @@ gst_filesrc_change_state (GstElement * element)
return GST_STATE_FAILURE;
}
src->need_discont = 2;
break;
case GST_STATE_PAUSED_TO_PLAYING:
break;
}
result = GST_ELEMENT_CLASS (parent_class)->change_state (element);
switch (GST_STATE_TRANSITION (element)) {
case GST_STATE_PLAYING_TO_PAUSED:
break;
case GST_STATE_PAUSED_TO_READY:
if (GST_FLAG_IS_SET (element, GST_FILESRC_OPEN))
@ -874,10 +958,7 @@ gst_filesrc_change_state (GstElement * element)
break;
}
if (GST_ELEMENT_CLASS (parent_class)->change_state)
return GST_ELEMENT_CLASS (parent_class)->change_state (element);
return GST_STATE_SUCCESS;
return result;
}
static gboolean

View file

@ -69,6 +69,8 @@ struct _GstFileSrc {
gboolean is_regular; /* whether it's (symlink to)
a regular file */
GstTask *task;
GstBuffer *mapbuf;
size_t mapsize;

View file

@ -99,7 +99,8 @@ static void gst_identity_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static GstElementStateReturn gst_identity_change_state (GstElement * element);
static void gst_identity_chain (GstPad * pad, GstData * _data);
static gboolean gst_identity_event (GstPad * pad, GstEvent * event);
static GstFlowReturn gst_identity_chain (GstPad * pad, GstBuffer * buffer);
static void gst_identity_set_clock (GstElement * element, GstClock * clock);
@ -138,6 +139,9 @@ gst_identity_class_init (GstIdentityClass * klass)
gobject_class = G_OBJECT_CLASS (klass);
gstelement_class = GST_ELEMENT_CLASS (klass);
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_identity_set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_identity_get_property);
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LOOP_BASED,
g_param_spec_boolean ("loop-based", "Loop-based",
"Set to TRUE to use loop-based rather than chain-based scheduling",
@ -185,8 +189,6 @@ gst_identity_class_init (GstIdentityClass * klass)
GST_TYPE_BUFFER | G_SIGNAL_TYPE_STATIC_SCOPE);
gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_identity_finalize);
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_identity_set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_identity_get_property);
gstelement_class->set_clock = GST_DEBUG_FUNCPTR (gst_identity_set_clock);
gstelement_class->change_state =
@ -203,15 +205,13 @@ gst_identity_init (GstIdentity * identity)
gst_element_add_pad (GST_ELEMENT (identity), identity->sinkpad);
gst_pad_set_chain_function (identity->sinkpad,
GST_DEBUG_FUNCPTR (gst_identity_chain));
gst_pad_set_link_function (identity->sinkpad, gst_pad_proxy_pad_link);
gst_pad_set_getcaps_function (identity->sinkpad, gst_pad_proxy_getcaps);
gst_pad_set_event_function (identity->sinkpad,
GST_DEBUG_FUNCPTR (gst_identity_event));
identity->srcpad =
gst_pad_new_from_template (gst_static_pad_template_get (&srctemplate),
"src");
gst_element_add_pad (GST_ELEMENT (identity), identity->srcpad);
gst_pad_set_link_function (identity->srcpad, gst_pad_proxy_pad_link);
gst_pad_set_getcaps_function (identity->srcpad, gst_pad_proxy_getcaps);
identity->loop_based = DEFAULT_LOOP_BASED;
identity->sleep_time = DEFAULT_SLEEP_TIME;
@ -225,8 +225,6 @@ gst_identity_init (GstIdentity * identity)
identity->dump = DEFAULT_DUMP;
identity->last_message = NULL;
identity->srccaps = NULL;
GST_FLAG_SET (identity, GST_ELEMENT_EVENT_AWARE);
}
static void
@ -237,35 +235,37 @@ gst_identity_set_clock (GstElement * element, GstClock * clock)
gst_object_replace ((GstObject **) & identity->clock, (GstObject *) clock);
}
static void
gst_identity_chain (GstPad * pad, GstData * _data)
static gboolean
gst_identity_event (GstPad * pad, GstEvent * event)
{
GstBuffer *buf = GST_BUFFER (_data);
GstIdentity *identity;
guint i;
g_return_if_fail (pad != NULL);
g_return_if_fail (GST_IS_PAD (pad));
g_return_if_fail (buf != NULL);
identity = GST_IDENTITY (gst_pad_get_parent (pad));
if (GST_IS_EVENT (buf)) {
GstEvent *event = GST_EVENT (buf);
if (!identity->silent) {
g_free (identity->last_message);
if (!identity->silent) {
g_free (identity->last_message);
identity->last_message =
g_strdup_printf ("chain ******* (%s:%s)E (type: %d) %p",
GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE (event), event);
identity->last_message =
g_strdup_printf ("chain ******* (%s:%s)E (type: %d) %p",
GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE (event), event);
g_object_notify (G_OBJECT (identity), "last_message");
}
gst_pad_event_default (pad, event);
return;
g_object_notify (G_OBJECT (identity), "last_message");
}
return gst_pad_push_event (identity->srcpad, event);
}
static GstFlowReturn
gst_identity_chain (GstPad * pad, GstBuffer * buffer)
{
GstBuffer *buf = GST_BUFFER (buffer);
GstIdentity *identity;
guint i;
g_return_val_if_fail (pad != NULL, GST_FLOW_ERROR);
g_return_val_if_fail (GST_IS_PAD (pad), GST_FLOW_ERROR);
g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
identity = GST_IDENTITY (gst_pad_get_parent (pad));
/* see if we need to do perfect stream checking */
/* invalid timestamp drops us out of check. FIXME: maybe warn ? */
@ -302,7 +302,7 @@ gst_identity_chain (GstPad * pad, GstData * _data)
gst_buffer_unref (buf);
GST_ELEMENT_ERROR (identity, CORE, FAILED,
(_("Failed after iterations as requested.")), (NULL));
return;
return GST_FLOW_ERROR;
}
}
@ -319,7 +319,7 @@ gst_identity_chain (GstPad * pad, GstData * _data)
GST_BUFFER_OFFSET_END (buf), GST_BUFFER_FLAGS (buf), buf);
g_object_notify (G_OBJECT (identity), "last-message");
gst_buffer_unref (buf);
return;
return GST_FLOW_OK;
}
}
if (identity->dump) {
@ -365,37 +365,33 @@ gst_identity_chain (GstPad * pad, GstData * _data)
}
identity->bytes_handled += GST_BUFFER_SIZE (buf);
gst_pad_push (identity->srcpad, GST_DATA (buf));
gst_pad_push (identity->srcpad, buf);
if (identity->sleep_time)
g_usleep (identity->sleep_time);
}
return GST_FLOW_OK;
}
#if 0
static void
gst_identity_loop (GstElement * element)
{
GstIdentity *identity;
GstBuffer *buf;
GstFlowReturn ret;
g_return_if_fail (element != NULL);
g_return_if_fail (GST_IS_IDENTITY (element));
identity = GST_IDENTITY (element);
buf = GST_BUFFER (gst_pad_pull (identity->sinkpad));
if (GST_IS_EVENT (buf)) {
GstEvent *event = GST_EVENT (buf);
if (GST_EVENT_IS_INTERRUPT (event)) {
gst_event_unref (event);
} else {
gst_pad_event_default (identity->sinkpad, event);
}
} else {
gst_identity_chain (identity->sinkpad, GST_DATA (buf));
ret = gst_pad_pull (identity->sinkpad, &buf);
if (ret == GST_FLOW_OK) {
gst_identity_chain (identity->sinkpad, buf);
}
}
#endif
static void
gst_identity_set_property (GObject * object, guint prop_id,
@ -412,12 +408,9 @@ gst_identity_set_property (GObject * object, guint prop_id,
case ARG_LOOP_BASED:
identity->loop_based = g_value_get_boolean (value);
if (identity->loop_based) {
gst_element_set_loop_function (GST_ELEMENT (identity),
gst_identity_loop);
gst_pad_set_chain_function (identity->sinkpad, NULL);
} else {
gst_pad_set_chain_function (identity->sinkpad, gst_identity_chain);
gst_element_set_loop_function (GST_ELEMENT (identity), NULL);
}
break;
case ARG_SLEEP_TIME:

View file

@ -26,6 +26,7 @@
#include "gstqueue.h"
#include "gstscheduler.h"
#include "gstpipeline.h"
#include "gstevent.h"
#include "gstinfo.h"
#include "gsterror.h"
@ -41,6 +42,24 @@ static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
GST_STATIC_CAPS_ANY);
GST_DEBUG_CATEGORY_STATIC (queue_dataflow);
#define GST_CAT_DEFAULT (queue_dataflow)
#define STATUS(queue, msg) \
GST_CAT_LOG_OBJECT (queue_dataflow, queue, \
"(%s:%s) " msg ": %u of %u-%u buffers, %u of %u-%u " \
"bytes, %" G_GUINT64_FORMAT " of %" G_GUINT64_FORMAT \
"-%" G_GUINT64_FORMAT " ns, %u elements", \
GST_DEBUG_PAD_NAME (pad), \
queue->cur_level.buffers, \
queue->min_threshold.buffers, \
queue->max_size.buffers, \
queue->cur_level.bytes, \
queue->min_threshold.bytes, \
queue->max_size.bytes, \
queue->cur_level.time, \
queue->min_threshold.time, \
queue->max_size.time, \
queue->queue->length)
static GstElementDetails gst_queue_details = GST_ELEMENT_DETAILS ("Queue",
"Generic",
@ -95,13 +114,6 @@ enum
} G_STMT_END
typedef struct _GstQueueEventResponse
{
GstEvent *event;
gboolean ret, handled;
}
GstQueueEventResponse;
static void gst_queue_base_init (GstQueueClass * klass);
static void gst_queue_class_init (GstQueueClass * klass);
static void gst_queue_init (GstQueue * queue);
@ -112,19 +124,24 @@ static void gst_queue_set_property (GObject * object,
static void gst_queue_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec);
static void gst_queue_chain (GstPad * pad, GstData * data);
static GstData *gst_queue_get (GstPad * pad);
static GstFlowReturn gst_queue_chain (GstPad * pad, GstBuffer * buffer);
static GstFlowReturn gst_queue_get (GstPad * pad, GstBuffer ** buffer);
static GstBuffer *gst_queue_bufferalloc (GstPad * pad, guint64 offset,
guint size, GstCaps * caps);
static gboolean gst_queue_handle_sink_event (GstPad * pad, GstEvent * event);
static gboolean gst_queue_handle_src_event (GstPad * pad, GstEvent * event);
static gboolean gst_queue_handle_src_query (GstPad * pad,
GstQueryType type, GstFormat * fmt, gint64 * value);
static GstCaps *gst_queue_getcaps (GstPad * pad);
static GstPadLinkReturn gst_queue_link (GstPad * pad, const GstCaps * caps);
static GstPadLinkReturn gst_queue_link_sink (GstPad * pad, GstPad * peer);
static GstPadLinkReturn gst_queue_link_src (GstPad * pad, GstPad * peer);
static void gst_queue_locked_flush (GstQueue * queue);
static gboolean gst_queue_src_activate (GstPad * pad, gboolean active);
static GstElementStateReturn gst_queue_change_state (GstElement * element);
static gboolean gst_queue_release_locks (GstElement * element);
#define GST_TYPE_QUEUE_LEAKY (queue_leaky_get_type ())
@ -197,6 +214,9 @@ gst_queue_class_init (GstQueueClass * klass)
parent_class = g_type_class_peek_parent (klass);
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_queue_set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_queue_get_property);
/* signals */
gst_queue_signals[SIGNAL_UNDERRUN] =
g_signal_new ("underrun", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST,
@ -267,45 +287,43 @@ gst_queue_class_init (GstQueueClass * klass)
/* set several parent class virtual functions */
gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_queue_finalize);
gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_queue_set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_queue_get_property);
gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_queue_change_state);
gstelement_class->release_locks = GST_DEBUG_FUNCPTR (gst_queue_release_locks);
}
static void
gst_queue_init (GstQueue * queue)
{
/* scheduling on this kind of element is, well, interesting */
GST_FLAG_SET (queue, GST_ELEMENT_DECOUPLED);
GST_FLAG_SET (queue, GST_ELEMENT_EVENT_AWARE);
queue->sinkpad =
gst_pad_new_from_template (gst_static_pad_template_get (&sinktemplate),
"sink");
gst_pad_set_chain_function (queue->sinkpad,
GST_DEBUG_FUNCPTR (gst_queue_chain));
gst_element_add_pad (GST_ELEMENT (queue), queue->sinkpad);
gst_pad_set_event_function (queue->sinkpad,
GST_DEBUG_FUNCPTR (gst_queue_handle_sink_event));
gst_pad_set_link_function (queue->sinkpad,
GST_DEBUG_FUNCPTR (gst_queue_link));
GST_DEBUG_FUNCPTR (gst_queue_link_sink));
gst_pad_set_getcaps_function (queue->sinkpad,
GST_DEBUG_FUNCPTR (gst_queue_getcaps));
gst_pad_set_active (queue->sinkpad, TRUE);
gst_pad_set_bufferalloc_function (queue->sinkpad,
GST_DEBUG_FUNCPTR (gst_queue_bufferalloc));
gst_element_add_pad (GST_ELEMENT (queue), queue->sinkpad);
queue->srcpad =
gst_pad_new_from_template (gst_static_pad_template_get (&srctemplate),
"src");
gst_pad_set_get_function (queue->srcpad, GST_DEBUG_FUNCPTR (gst_queue_get));
gst_element_add_pad (GST_ELEMENT (queue), queue->srcpad);
gst_pad_set_link_function (queue->srcpad, GST_DEBUG_FUNCPTR (gst_queue_link));
gst_pad_set_activate_function (queue->srcpad,
GST_DEBUG_FUNCPTR (gst_queue_src_activate));
gst_pad_set_link_function (queue->srcpad,
GST_DEBUG_FUNCPTR (gst_queue_link_src));
gst_pad_set_getcaps_function (queue->srcpad,
GST_DEBUG_FUNCPTR (gst_queue_getcaps));
gst_pad_set_event_function (queue->srcpad,
GST_DEBUG_FUNCPTR (gst_queue_handle_src_event));
gst_pad_set_query_function (queue->srcpad,
GST_DEBUG_FUNCPTR (gst_queue_handle_src_query));
gst_pad_set_active (queue->srcpad, TRUE);
gst_element_add_pad (GST_ELEMENT (queue), queue->srcpad);
queue->cur_level.buffers = 0; /* no content */
queue->cur_level.bytes = 0; /* no content */
@ -326,9 +344,6 @@ gst_queue_init (GstQueue * queue)
queue->qlock = g_mutex_new ();
queue->item_add = g_cond_new ();
queue->item_del = g_cond_new ();
queue->event_done = g_cond_new ();
queue->events = g_queue_new ();
queue->event_lock = g_mutex_new ();
queue->queue = g_queue_new ();
GST_CAT_DEBUG_OBJECT (GST_CAT_THREAD, queue,
@ -349,19 +364,11 @@ gst_queue_finalize (GObject * object)
gst_data_unref (data);
}
g_queue_free (queue->queue);
GST_CAT_DEBUG_OBJECT (GST_CAT_THREAD, queue, "free mutex");
g_mutex_free (queue->qlock);
GST_CAT_DEBUG_OBJECT (GST_CAT_THREAD, queue, "done free mutex");
g_cond_free (queue->item_add);
g_cond_free (queue->item_del);
g_cond_free (queue->event_done);
g_mutex_lock (queue->event_lock);
while (!g_queue_is_empty (queue->events)) {
GstQueueEventResponse *er = g_queue_pop_head (queue->events);
gst_event_unref (er->event);
}
g_mutex_unlock (queue->event_lock);
g_mutex_free (queue->event_lock);
g_queue_free (queue->events);
if (G_OBJECT_CLASS (parent_class)->finalize)
G_OBJECT_CLASS (parent_class)->finalize (object);
@ -371,42 +378,55 @@ static GstCaps *
gst_queue_getcaps (GstPad * pad)
{
GstQueue *queue;
GstPad *otherpad, *otherpeer;
queue = GST_QUEUE (gst_pad_get_parent (pad));
queue = GST_QUEUE (gst_object_get_parent (GST_OBJECT (pad)));
if (queue->cur_level.bytes > 0) {
return gst_caps_copy (queue->negotiated_caps);
otherpad = (pad == queue->srcpad ? queue->sinkpad : queue->srcpad);
otherpeer = gst_pad_get_peer (otherpad);
if (otherpeer == NULL) {
return gst_pad_get_caps (otherpad);
} else {
return gst_pad_get_caps (otherpeer);
}
return gst_pad_proxy_getcaps (pad);
}
static GstPadLinkReturn
gst_queue_link (GstPad * pad, const GstCaps * caps)
gst_queue_link_sink (GstPad * pad, GstPad * peer)
{
return GST_PAD_LINK_OK;
}
static GstPadLinkReturn
gst_queue_link_src (GstPad * pad, GstPad * peer)
{
GstPadLinkReturn result = GST_PAD_LINK_OK;
/* FIXME, see if we need to push or get pulled */
if (GST_RPAD_LINKFUNC (peer))
result = GST_RPAD_LINKFUNC (peer) (peer, pad);
return result;
}
static GstBuffer *
gst_queue_bufferalloc (GstPad * pad, guint64 offset, guint size, GstCaps * caps)
{
GstQueue *queue;
GstPadLinkReturn link_ret;
GstPad *otherpeer;
queue = GST_QUEUE (gst_pad_get_parent (pad));
queue = GST_QUEUE (gst_object_get_parent (GST_OBJECT (pad)));
if (queue->cur_level.bytes > 0) {
if (gst_caps_is_equal (caps, queue->negotiated_caps)) {
return GST_PAD_LINK_OK;
}
return GST_PAD_LINK_REFUSED;
otherpeer = gst_pad_get_peer (queue->srcpad);
if (otherpeer == NULL || GST_RPAD_BUFFERALLOCFUNC (otherpeer) == NULL) {
/* let the default aloc function do the work */
return NULL;
} else {
return GST_RPAD_BUFFERALLOCFUNC (otherpeer) (otherpeer, offset, size, caps);
}
link_ret = gst_pad_proxy_pad_link (pad, caps);
if (GST_PAD_LINK_SUCCESSFUL (link_ret)) {
/* we store an extra copy of the negotiated caps, just in case
* the pads become unnegotiated while we have buffers */
gst_caps_replace (&queue->negotiated_caps, gst_caps_copy (caps));
}
return link_ret;
}
static void
gst_queue_locked_flush (GstQueue * queue)
{
@ -419,7 +439,6 @@ gst_queue_locked_flush (GstQueue * queue)
data when flushing */
gst_data_unref (data);
}
queue->timeval = NULL;
queue->cur_level.buffers = 0;
queue->cur_level.bytes = 0;
queue->cur_level.time = 0;
@ -431,40 +450,6 @@ gst_queue_locked_flush (GstQueue * queue)
g_cond_signal (queue->item_del);
}
static void
gst_queue_handle_pending_events (GstQueue * queue)
{
/* check for events to send upstream */
/* g_queue_get_length is glib 2.4, so don't depend on it yet, use ->length */
GST_CAT_DEBUG_OBJECT (queue_dataflow, queue,
"handling pending events, events queue of size %d",
queue->events->length);
g_mutex_lock (queue->event_lock);
while (!g_queue_is_empty (queue->events)) {
GstQueueEventResponse *er;
er = g_queue_pop_head (queue->events);
GST_CAT_DEBUG_OBJECT (queue_dataflow, queue,
"sending event %p (%d) from event response %p upstream",
er->event, GST_EVENT_TYPE (er->event), er);
if (er->handled) {
/* change this to an assert when this file gets reviewed properly. */
GST_ELEMENT_ERROR (queue, CORE, EVENT, (NULL),
("already handled event %p (%d) from event response %p upstream",
er->event, GST_EVENT_TYPE (er->event), er));
break;
}
g_mutex_unlock (queue->event_lock);
er->ret = gst_pad_event_default (queue->srcpad, er->event);
er->handled = TRUE;
g_cond_signal (queue->event_done);
g_mutex_lock (queue->event_lock);
GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, "event sent");
}
g_mutex_unlock (queue->event_lock);
}
#define STATUS(queue, msg) \
GST_CAT_LOG_OBJECT (queue_dataflow, queue, \
"(%s:%s) " msg ": %u of %u-%u buffers, %u of %u-%u " \
@ -482,58 +467,80 @@ gst_queue_handle_pending_events (GstQueue * queue)
queue->max_size.time, \
queue->queue->length)
static void
gst_queue_chain (GstPad * pad, GstData * data)
static gboolean
gst_queue_handle_sink_event (GstPad * pad, GstEvent * event)
{
GstQueue *queue;
g_return_if_fail (pad != NULL);
g_return_if_fail (GST_IS_PAD (pad));
g_return_if_fail (data != NULL);
queue = GST_QUEUE (GST_OBJECT_PARENT (pad));
restart:
/* we have to lock the queue since we span threads */
GST_QUEUE_MUTEX_LOCK;
gst_queue_handle_pending_events (queue);
/* assume don't need to flush this buffer when the queue is filled */
queue->flush = FALSE;
if (GST_IS_EVENT (data)) {
switch (GST_EVENT_TYPE (data)) {
case GST_EVENT_FLUSH:
STATUS (queue, "received flush event");
gst_queue_locked_flush (queue);
STATUS (queue, "after flush");
break;
case GST_EVENT_EOS:
STATUS (queue, "received EOS");
break;
default:
/* we put the event in the queue, we don't have to act ourselves */
GST_CAT_LOG_OBJECT (queue_dataflow, queue,
"adding event %p of type %d", data, GST_EVENT_TYPE (data));
break;
}
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_FLUSH:
STATUS (queue, "received flush event");
gst_queue_locked_flush (queue);
STATUS (queue, "after flush");
break;
case GST_EVENT_EOS:
STATUS (queue, "received EOS");
break;
default:
/* we put the event in the queue, we don't have to act ourselves */
GST_CAT_LOG_OBJECT (queue_dataflow, queue,
"adding event %p of type %d", event, GST_EVENT_TYPE (event));
break;
}
if (GST_IS_BUFFER (data))
GST_CAT_LOG_OBJECT (queue_dataflow, queue,
"adding buffer %p of size %d", data, GST_BUFFER_SIZE (data));
gst_event_ref (event);
g_queue_push_tail (queue->queue, event);
g_cond_signal (queue->item_add);
GST_QUEUE_MUTEX_UNLOCK;
/* We make space available if we're "full" according to whatever
* the user defined as "full". Note that this only applies to buffers.
* We always handle events and they don't count in our statistics. */
if (GST_IS_BUFFER (data) &&
((queue->max_size.buffers > 0 &&
return TRUE;
}
static gboolean
gst_queue_is_empty (GstQueue * queue)
{
return (queue->queue->length == 0 ||
(queue->min_threshold.buffers > 0 &&
queue->cur_level.buffers < queue->min_threshold.buffers) ||
(queue->min_threshold.bytes > 0 &&
queue->cur_level.bytes < queue->min_threshold.bytes) ||
(queue->min_threshold.time > 0 &&
queue->cur_level.time < queue->min_threshold.time));
}
static gboolean
gst_queue_is_filled (GstQueue * queue)
{
return (((queue->max_size.buffers > 0 &&
queue->cur_level.buffers >= queue->max_size.buffers) ||
(queue->max_size.bytes > 0 &&
queue->cur_level.bytes >= queue->max_size.bytes) ||
(queue->max_size.time > 0 &&
queue->cur_level.time >= queue->max_size.time))) {
queue->cur_level.time >= queue->max_size.time)));
}
static GstFlowReturn
gst_queue_chain (GstPad * pad, GstBuffer * buffer)
{
GstQueue *queue;
queue = GST_QUEUE (GST_OBJECT_PARENT (pad));
/* we have to lock the queue since we span threads */
GST_QUEUE_MUTEX_LOCK;
GST_CAT_LOG_OBJECT (queue_dataflow, queue,
"adding buffer %p of size %d", buffer, GST_BUFFER_SIZE (buffer));
/* We make space available if we're "full" according to whatever
* the user defined as "full". Note that this only applies to buffers.
* We always handle events and they don't count in our statistics. */
if (gst_queue_is_filled (queue)) {
GST_QUEUE_MUTEX_UNLOCK;
g_signal_emit (G_OBJECT (queue), gst_queue_signals[SIGNAL_OVERRUN], 0);
GST_QUEUE_MUTEX_LOCK;
@ -577,15 +584,15 @@ restart:
queue->queue->tail = g_list_last (item);
queue->queue->length--;
/* and unref the data at the end. Twice, because we keep a ref
/* and unref the buffer at the end. Twice, because we keep a ref
* to make things read-only. Also keep our list uptodate. */
queue->cur_level.bytes -= GST_BUFFER_SIZE (data);
queue->cur_level.bytes -= GST_BUFFER_SIZE (buffer);
queue->cur_level.buffers--;
if (GST_BUFFER_DURATION (data) != GST_CLOCK_TIME_NONE)
queue->cur_level.time -= GST_BUFFER_DURATION (data);
if (GST_BUFFER_DURATION (buffer) != GST_CLOCK_TIME_NONE)
queue->cur_level.time -= GST_BUFFER_DURATION (buffer);
gst_data_unref (data);
gst_data_unref (data);
gst_buffer_unref (buffer);
gst_buffer_unref (buffer);
break;
}
@ -597,63 +604,12 @@ restart:
case GST_QUEUE_NO_LEAK:
STATUS (queue, "pre-full wait");
while ((queue->max_size.buffers > 0 &&
queue->cur_level.buffers >= queue->max_size.buffers) ||
(queue->max_size.bytes > 0 &&
queue->cur_level.bytes >= queue->max_size.bytes) ||
(queue->max_size.time > 0 &&
queue->cur_level.time >= queue->max_size.time)) {
while (gst_queue_is_filled (queue)) {
STATUS (queue, "waiting for item_del signal from thread using qlock");
g_cond_wait (queue->item_del, queue->qlock);
/* if there's a pending state change for this queue
* or its manager, switch back to iterator so bottom
* half of state change executes */
if (queue->interrupt) {
GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, "interrupted");
GST_QUEUE_MUTEX_UNLOCK;
if (gst_scheduler_interrupt (gst_pad_get_scheduler (queue->sinkpad),
GST_ELEMENT (queue))) {
goto out_unref;
}
/* if we got here because we were unlocked after a
* flush, we don't need to add the buffer to the
* queue again */
if (queue->flush) {
GST_CAT_DEBUG_OBJECT (queue_dataflow, queue,
"not adding pending buffer after flush");
goto out_unref;
}
GST_CAT_DEBUG_OBJECT (queue_dataflow, queue,
"adding pending buffer after interrupt");
goto restart;
}
if (GST_STATE (queue) != GST_STATE_PLAYING) {
/* this means the other end is shut down. Try to
* signal to resolve the error */
if (!queue->may_deadlock) {
GST_QUEUE_MUTEX_UNLOCK;
gst_data_unref (data);
GST_ELEMENT_ERROR (queue, CORE, THREAD, (NULL),
("deadlock found, shutting down source pad elements"));
/* we don't go to out_unref here, since we want to
* unref the buffer *before* calling GST_ELEMENT_ERROR */
return;
} else {
GST_CAT_WARNING_OBJECT (queue_dataflow, queue,
"%s: waiting for the app to restart "
"source pad elements", GST_ELEMENT_NAME (queue));
}
}
/* OK, we've got a serious issue here. Imagine the situation
* where the puller (next element) is sending an event here,
* so it cannot pull events from the queue, and we cannot
* push data further because the queue is 'full' and therefore,
* we wait here (and do not handle events): deadlock! to solve
* that, we handle pending upstream events here, too. */
gst_queue_handle_pending_events (queue);
STATUS (queue, "waiting for item_del signal from thread using qlock");
g_cond_wait (queue->item_del, queue->qlock);
STATUS (queue, "received item_del signal from thread using qlock");
}
@ -666,19 +622,17 @@ restart:
}
/* put the buffer on the tail of the list. We keep a reference,
* so that the data is read-only while in here. There's a good
* so that the buffer is read-only while in here. There's a good
* reason to do so: we have a size and time counter, and any
* modification to the content could change any of the two. */
gst_data_ref (data);
g_queue_push_tail (queue->queue, data);
gst_buffer_ref (buffer);
g_queue_push_tail (queue->queue, buffer);
/* Note that we only add buffers (not events) to the statistics */
if (GST_IS_BUFFER (data)) {
queue->cur_level.buffers++;
queue->cur_level.bytes += GST_BUFFER_SIZE (data);
if (GST_BUFFER_DURATION (data) != GST_CLOCK_TIME_NONE)
queue->cur_level.time += GST_BUFFER_DURATION (data);
}
/* add buffer to the statistics */
queue->cur_level.buffers++;
queue->cur_level.bytes += GST_BUFFER_SIZE (buffer);
if (GST_BUFFER_DURATION (buffer) != GST_CLOCK_TIME_NONE)
queue->cur_level.time += GST_BUFFER_DURATION (buffer);
STATUS (queue, "+ level");
@ -686,94 +640,41 @@ restart:
g_cond_signal (queue->item_add);
GST_QUEUE_MUTEX_UNLOCK;
return;
return GST_FLOW_OK;
out_unref:
gst_data_unref (data);
return;
gst_buffer_unref (buffer);
return GST_FLOW_OK;
}
static GstData *
gst_queue_get (GstPad * pad)
static GstFlowReturn
gst_queue_get (GstPad * pad, GstBuffer ** buffer)
{
GstQueue *queue;
GstData *data;
GstFlowReturn result = GST_FLOW_OK;
g_return_val_if_fail (pad != NULL, NULL);
g_return_val_if_fail (GST_IS_PAD (pad), NULL);
queue = GST_QUEUE (gst_object_get_parent (GST_OBJECT (pad)));
queue = GST_QUEUE (gst_pad_get_parent (pad));
restart:
/* have to lock for thread-safety */
GST_QUEUE_MUTEX_LOCK;
if (queue->queue->length == 0 ||
(queue->min_threshold.buffers > 0 &&
queue->cur_level.buffers < queue->min_threshold.buffers) ||
(queue->min_threshold.bytes > 0 &&
queue->cur_level.bytes < queue->min_threshold.bytes) ||
(queue->min_threshold.time > 0 &&
queue->cur_level.time < queue->min_threshold.time)) {
restart:
if (gst_queue_is_empty (queue)) {
GST_QUEUE_MUTEX_UNLOCK;
g_signal_emit (G_OBJECT (queue), gst_queue_signals[SIGNAL_UNDERRUN], 0);
GST_QUEUE_MUTEX_LOCK;
STATUS (queue, "pre-empty wait");
while (queue->queue->length == 0 ||
(queue->min_threshold.buffers > 0 &&
queue->cur_level.buffers < queue->min_threshold.buffers) ||
(queue->min_threshold.bytes > 0 &&
queue->cur_level.bytes < queue->min_threshold.bytes) ||
(queue->min_threshold.time > 0 &&
queue->cur_level.time < queue->min_threshold.time)) {
/* if there's a pending state change for this queue or its
* manager, switch back to iterator so bottom half of state
* change executes. */
if (queue->interrupt) {
GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, "interrupted");
GST_QUEUE_MUTEX_UNLOCK;
if (gst_scheduler_interrupt (gst_pad_get_scheduler (queue->srcpad),
GST_ELEMENT (queue)))
return GST_DATA (gst_event_new (GST_EVENT_INTERRUPT));
goto restart;
}
if (GST_STATE (queue) != GST_STATE_PLAYING) {
/* this means the other end is shut down */
if (!queue->may_deadlock) {
GST_QUEUE_MUTEX_UNLOCK;
GST_ELEMENT_ERROR (queue, CORE, THREAD, (NULL),
("deadlock found, shutting down sink pad elements"));
goto restart;
} else {
GST_CAT_WARNING_OBJECT (queue_dataflow, queue,
"%s: waiting for the app to restart "
"source pad elements", GST_ELEMENT_NAME (queue));
}
}
while (gst_queue_is_empty (queue)) {
STATUS (queue, "waiting for item_add");
if (queue->block_timeout != GST_CLOCK_TIME_NONE) {
GTimeVal timeout;
g_get_current_time (&timeout);
g_time_val_add (&timeout, queue->block_timeout / 1000);
GST_LOG_OBJECT (queue, "g_cond_time_wait using qlock from thread %p",
g_thread_self ());
if (!g_cond_timed_wait (queue->item_add, queue->qlock, &timeout)) {
GST_QUEUE_MUTEX_UNLOCK;
GST_CAT_WARNING_OBJECT (queue_dataflow, queue,
"Sending filler event");
return GST_DATA (gst_event_new_filler ());
}
} else {
GST_LOG_OBJECT (queue, "doing g_cond_wait using qlock from thread %p",
g_thread_self ());
g_cond_wait (queue->item_add, queue->qlock);
GST_LOG_OBJECT (queue, "done g_cond_wait using qlock from thread %p",
g_thread_self ());
}
GST_LOG_OBJECT (queue, "doing g_cond_wait using qlock from thread %p",
g_thread_self ());
g_cond_wait (queue->item_add, queue->qlock);
GST_LOG_OBJECT (queue, "done g_cond_wait using qlock from thread %p",
g_thread_self ());
STATUS (queue, "got item_add signal");
}
@ -788,120 +689,68 @@ restart:
GST_CAT_LOG_OBJECT (queue_dataflow, queue,
"retrieved data %p from queue", data);
if (data == NULL)
return NULL;
if (GST_IS_BUFFER (data)) {
/* Update statistics */
queue->cur_level.buffers--;
queue->cur_level.bytes -= GST_BUFFER_SIZE (data);
if (GST_BUFFER_DURATION (data) != GST_CLOCK_TIME_NONE)
queue->cur_level.time -= GST_BUFFER_DURATION (data);
*buffer = GST_BUFFER (data);
} else {
if (GST_EVENT_TYPE (data) == GST_EVENT_EOS) {
result = GST_FLOW_WRONG_STATE;
}
gst_pad_push_event (queue->srcpad, GST_EVENT (data));
if (result == GST_FLOW_OK)
goto restart;
else
goto done;
}
/* Now that we're done, we can lose our own reference to
* the item, since we're no longer in danger. */
gst_data_unref (data);
done:
STATUS (queue, "after _get()");
GST_CAT_LOG_OBJECT (queue_dataflow, queue, "signalling item_del");
g_cond_signal (queue->item_del);
GST_QUEUE_MUTEX_UNLOCK;
/* FIXME: I suppose this needs to be locked, since the EOS
* bit affects the pipeline state. However, that bit is
* locked too so it'd cause a deadlock. */
if (GST_IS_EVENT (data)) {
GstEvent *event = GST_EVENT (data);
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_EOS:
GST_CAT_DEBUG_OBJECT (queue_dataflow, queue,
"queue \"%s\" eos", GST_ELEMENT_NAME (queue));
gst_element_set_eos (GST_ELEMENT (queue));
break;
default:
break;
}
}
return data;
return result;
}
static gboolean
gst_queue_handle_src_event (GstPad * pad, GstEvent * event)
{
GstQueue *queue = GST_QUEUE (gst_pad_get_parent (pad));
gboolean res;
GstQueue *queue = GST_QUEUE (gst_object_get_parent (GST_OBJECT (pad)));
gboolean res = TRUE;
GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, "got event %p (%d)",
event, GST_EVENT_TYPE (event));
gst_event_ref (event);
res = gst_pad_event_default (pad, event);
GST_QUEUE_MUTEX_LOCK;
if (gst_element_get_state (GST_ELEMENT (queue)) == GST_STATE_PLAYING) {
GstQueueEventResponse er;
/* push the event to the queue and wait for upstream consumption */
er.event = event;
er.handled = FALSE;
g_mutex_lock (queue->event_lock);
GST_CAT_DEBUG_OBJECT (queue_dataflow, queue,
"putting event %p (%d) on internal queue", event,
GST_EVENT_TYPE (event));
g_queue_push_tail (queue->events, &er);
g_mutex_unlock (queue->event_lock);
GST_CAT_WARNING_OBJECT (queue_dataflow, queue,
"Preparing for loop for event handler");
/* see the chain function on why this is here - it prevents a deadlock */
g_cond_signal (queue->item_del);
while (!er.handled) {
GTimeVal timeout;
g_get_current_time (&timeout);
g_time_val_add (&timeout, 500 * 1000); /* half a second */
GST_LOG_OBJECT (queue, "doing g_cond_wait using qlock from thread %p",
g_thread_self ());
if (!g_cond_timed_wait (queue->event_done, queue->qlock, &timeout) &&
!er.handled) {
GST_CAT_WARNING_OBJECT (queue_dataflow, queue,
"timeout in upstream event handling, dropping event %p (%d)",
er.event, GST_EVENT_TYPE (er.event));
g_mutex_lock (queue->event_lock);
/* since this queue is for src events (ie upstream), this thread is
* the only one that is pushing stuff on it, so we're sure that
* it's still the tail element. FIXME: But in practice, we should use
* GList instead of GQueue for this so we can remove any element in
* the list. */
g_queue_pop_tail (queue->events);
g_mutex_unlock (queue->event_lock);
gst_event_unref (er.event);
res = FALSE;
goto handled;
}
}
GST_CAT_WARNING_OBJECT (queue_dataflow, queue, "Event handled");
res = er.ret;
} else {
res = gst_pad_event_default (pad, event);
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_FLUSH:
GST_CAT_DEBUG_OBJECT (queue_dataflow, queue,
"FLUSH event, flushing queue\n");
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_FLUSH:
GST_CAT_DEBUG_OBJECT (queue_dataflow, queue,
"FLUSH event, flushing queue\n");
gst_queue_locked_flush (queue);
break;
case GST_EVENT_SEEK:
if (GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH) {
gst_queue_locked_flush (queue);
break;
case GST_EVENT_SEEK:
if (GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH) {
gst_queue_locked_flush (queue);
}
default:
break;
}
}
default:
break;
}
handled:
GST_QUEUE_MUTEX_UNLOCK;
gst_event_unref (event);
return res;
}
@ -911,10 +760,10 @@ gst_queue_handle_src_query (GstPad * pad,
GstQueryType type, GstFormat * fmt, gint64 * value)
{
GstQueue *queue = GST_QUEUE (gst_pad_get_parent (pad));
gboolean res;
res = gst_pad_query (GST_PAD_PEER (queue->sinkpad), type, fmt, value);
if (!res)
if (!GST_PAD_PEER (queue->sinkpad))
return FALSE;
if (!gst_pad_query (GST_PAD_PEER (queue->sinkpad), type, fmt, value))
return FALSE;
if (type == GST_QUERY_POSITION) {
@ -935,22 +784,76 @@ gst_queue_handle_src_query (GstPad * pad,
return TRUE;
}
static gboolean
gst_queue_release_locks (GstElement * element)
static void
gst_queue_loop (GstElement * element)
{
GstQueue *queue;
GstTask *task;
GstBuffer *buffer;
GstFlowReturn ret;
g_return_if_fail (element != NULL);
g_return_if_fail (GST_IS_QUEUE (element));
queue = GST_QUEUE (element);
task = queue->task;
GST_QUEUE_MUTEX_LOCK;
queue->interrupt = TRUE;
g_cond_signal (queue->item_add);
g_cond_signal (queue->item_del);
GST_QUEUE_MUTEX_UNLOCK;
return TRUE;
ret = gst_queue_get (queue->srcpad, &buffer);
if (ret != GST_FLOW_OK) {
GST_CAT_LOG_OBJECT (queue_dataflow, queue, "stopping, get returned %d",
ret);
gst_task_stop (task);
return;
}
ret = gst_pad_push (queue->srcpad, buffer);
if (ret != GST_FLOW_OK) {
GST_CAT_LOG_OBJECT (queue_dataflow, queue, "stopping, push returned %d",
ret);
gst_task_stop (task);
return;
}
}
static gboolean
gst_queue_src_activate (GstPad * pad, gboolean active)
{
gboolean result = FALSE;
GstQueue *queue;
queue = GST_QUEUE (GST_OBJECT_PARENT (pad));
if (active) {
/* if we have a scheduler we can start the task */
if (GST_ELEMENT_MANAGER (queue)) {
GST_STREAM_LOCK (pad);
queue->task =
gst_scheduler_create_task (GST_ELEMENT_MANAGER (queue)->scheduler,
(GstTaskFunction) gst_queue_loop, queue);
gst_task_start (queue->task);
GST_STREAM_UNLOCK (pad);
result = TRUE;
}
} else {
/* step 1, unblock chain and get functions */
queue->interrupt = TRUE;
g_cond_signal (queue->item_add);
g_cond_signal (queue->item_del);
/* step 2, make sure streaming finishes */
GST_STREAM_LOCK (pad);
/* step 3, stop the task */
gst_task_stop (queue->task);
gst_object_unref (GST_OBJECT (queue->task));
GST_STREAM_UNLOCK (pad);
result = TRUE;
}
return result;
}
static GstElementStateReturn
gst_queue_change_state (GstElement * element)
{
@ -970,53 +873,28 @@ gst_queue_change_state (GstElement * element)
case GST_STATE_NULL_TO_READY:
gst_queue_locked_flush (queue);
break;
case GST_STATE_PAUSED_TO_PLAYING:
if (!GST_PAD_IS_LINKED (queue->sinkpad)) {
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, queue,
"queue %s is not linked", GST_ELEMENT_NAME (queue));
/* FIXME can this be? */
g_cond_signal (queue->item_add);
ret = GST_STATE_FAILURE;
goto unlock;
} else {
GstScheduler *src_sched, *sink_sched;
src_sched = gst_pad_get_scheduler (GST_PAD (queue->srcpad));
sink_sched = gst_pad_get_scheduler (GST_PAD (queue->sinkpad));
if (src_sched == sink_sched) {
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, queue,
"queue %s does not connect different schedulers",
GST_ELEMENT_NAME (queue));
g_warning ("queue %s does not connect different schedulers",
GST_ELEMENT_NAME (queue));
ret = GST_STATE_FAILURE;
goto unlock;
}
}
queue->interrupt = FALSE;
case GST_STATE_READY_TO_PAUSED:
break;
case GST_STATE_PAUSED_TO_READY:
gst_queue_locked_flush (queue);
gst_caps_replace (&queue->negotiated_caps, NULL);
case GST_STATE_PAUSED_TO_PLAYING:
queue->interrupt = FALSE;
break;
default:
break;
}
if (GST_ELEMENT_CLASS (parent_class)->change_state)
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
/* this is an ugly hack to make sure our pads are always active.
* Reason for this is that pad activation for the queue element
* depends on 2 schedulers (ugh) */
gst_pad_set_active (queue->sinkpad, TRUE);
gst_pad_set_active (queue->srcpad, TRUE);
unlock:
switch (GST_STATE_TRANSITION (element)) {
case GST_STATE_PLAYING_TO_PAUSED:
break;
case GST_STATE_PAUSED_TO_READY:
gst_queue_locked_flush (queue);
break;
case GST_STATE_READY_TO_NULL:
break;
default:
break;
}
GST_QUEUE_MUTEX_UNLOCK;
GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "done with state change");
@ -1024,7 +902,6 @@ unlock:
return ret;
}
static void
gst_queue_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec)

View file

@ -26,6 +26,7 @@
#include <gst/gstelement.h>
#include <gst/gsttask.h>
G_BEGIN_DECLS
@ -66,6 +67,8 @@ struct _GstQueue {
/* the queue of data we're keeping our grubby hands on */
GQueue *queue;
GstTask *task;
GstQueueSize
cur_level, /* currently in the queue */
max_size, /* max. amount of data allowed in the queue */
@ -80,21 +83,12 @@ struct _GstQueue {
/* it the queue should fail on possible deadlocks */
gboolean may_deadlock;
gboolean interrupt;
gboolean flush;
GMutex *qlock; /* lock for queue (vs object lock) */
GCond *item_add; /* signals buffers now available for reading */
GCond *item_del; /* signals space now available for writing */
GCond *event_done; /* upstream event signaller */
GTimeVal *timeval; /* the timeout for the queue locking */
GQueue *events; /* upstream events get decoupled here */
GstCaps *negotiated_caps;
GMutex *event_lock; /* lock when handling the events queue */
gpointer _gst_reserved[GST_PADDING - 1];
};

View file

@ -1 +1 @@
af az ca cs en_GB fr it nb nl sq sr sv tr uk
af az ca cs en_GB fr nb nl sq sr sv tr uk

View file

@ -17,50 +17,80 @@
* Boston, MA 02111-1307, USA.
*/
#include "unistd.h"
#include <gst/gst.h>
static gboolean
message_received (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
{
g_print ("message %p\n", message);
if (message->type == GST_MESSAGE_EOS) {
g_print ("EOS!!\n");
gst_main_quit ();
}
gst_message_unref (message);
return TRUE;
}
gint
main (gint argc, gchar * argv[])
{
GstElement *pipeline;
GstElement *fakesrc1, *fakesink1;
GstElement *fakesrc2, *fakesink2;
GstBus *bus;
gst_init (&argc, &argv);
pipeline = gst_pipeline_new ("pipeline");
bus = GST_PIPELINE (pipeline)->bus;
gst_bus_add_watch (bus, (GstBusHandler) message_received, pipeline);
fakesrc1 = gst_element_factory_make ("fakesrc", "fakesrc1");
g_object_set (G_OBJECT (fakesrc1), "num_buffers", 5, NULL);
fakesink1 = gst_element_factory_make ("fakesink", "fakesink1");
gst_bin_add_many (GST_BIN (pipeline), fakesrc1, fakesink1, NULL);
gst_element_link_pads (fakesrc1, "src", fakesink1, "sink");
gst_bin_add (GST_BIN (pipeline), fakesrc1);
gst_bin_add (GST_BIN (pipeline), fakesink1);
gst_pad_link (gst_element_get_pad (fakesrc1, "src"),
gst_element_get_pad (fakesink1, "sink"));
fakesrc2 = gst_element_factory_make ("fakesrc", "fakesrc2");
g_object_set (G_OBJECT (fakesrc2), "num_buffers", 5, NULL);
fakesink2 = gst_element_factory_make ("fakesink", "fakesink2");
gst_bin_add_many (GST_BIN (pipeline), fakesrc2, fakesink2, NULL);
gst_element_link_pads (fakesrc2, "src", fakesink2, "sink");
gst_bin_add (GST_BIN (pipeline), fakesrc2);
gst_bin_add (GST_BIN (pipeline), fakesink2);
gst_pad_link (gst_element_get_pad (fakesrc2, "src"),
gst_element_get_pad (fakesink2, "sink"));
g_signal_connect (G_OBJECT (pipeline), "deep_notify",
G_CALLBACK (gst_element_default_deep_notify), NULL);
G_CALLBACK (gst_object_default_deep_notify), NULL);
GST_FLAG_SET (fakesrc2, GST_ELEMENT_LOCKED_STATE);
GST_FLAG_SET (fakesink2, GST_ELEMENT_LOCKED_STATE);
g_print ("play..\n");
gst_element_set_state (pipeline, GST_STATE_PLAYING);
while (gst_bin_iterate (GST_BIN (pipeline)));
gst_element_set_state (pipeline, GST_STATE_READY);
gst_main ();
g_object_set (G_OBJECT (fakesrc1), "num_buffers", 5, NULL);
gst_element_set_state (pipeline, GST_STATE_READY);
GST_FLAG_UNSET (fakesrc2, GST_ELEMENT_LOCKED_STATE);
GST_FLAG_UNSET (fakesink2, GST_ELEMENT_LOCKED_STATE);
g_print ("play..\n");
gst_element_set_state (pipeline, GST_STATE_PLAYING);
while (gst_bin_iterate (GST_BIN (pipeline)));
gst_main ();
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (GST_OBJECT (pipeline));

View file

@ -17,50 +17,80 @@
* Boston, MA 02111-1307, USA.
*/
#include "unistd.h"
#include <gst/gst.h>
static gboolean
message_received (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
{
g_print ("message %p\n", message);
if (message->type == GST_MESSAGE_EOS) {
g_print ("EOS!!\n");
gst_main_quit ();
}
gst_message_unref (message);
return TRUE;
}
gint
main (gint argc, gchar * argv[])
{
GstElement *pipeline;
GstElement *fakesrc1, *fakesink1;
GstElement *fakesrc2, *fakesink2;
GstBus *bus;
gst_init (&argc, &argv);
pipeline = gst_pipeline_new ("pipeline");
bus = GST_PIPELINE (pipeline)->bus;
gst_bus_add_watch (bus, (GstBusHandler) message_received, pipeline);
fakesrc1 = gst_element_factory_make ("fakesrc", "fakesrc1");
g_object_set (G_OBJECT (fakesrc1), "num_buffers", 5, NULL);
fakesink1 = gst_element_factory_make ("fakesink", "fakesink1");
gst_bin_add_many (GST_BIN (pipeline), fakesrc1, fakesink1, NULL);
gst_element_link_pads (fakesrc1, "src", fakesink1, "sink");
gst_bin_add (GST_BIN (pipeline), fakesrc1);
gst_bin_add (GST_BIN (pipeline), fakesink1);
gst_pad_link (gst_element_get_pad (fakesrc1, "src"),
gst_element_get_pad (fakesink1, "sink"));
fakesrc2 = gst_element_factory_make ("fakesrc", "fakesrc2");
g_object_set (G_OBJECT (fakesrc2), "num_buffers", 5, NULL);
fakesink2 = gst_element_factory_make ("fakesink", "fakesink2");
gst_bin_add_many (GST_BIN (pipeline), fakesrc2, fakesink2, NULL);
gst_element_link_pads (fakesrc2, "src", fakesink2, "sink");
gst_bin_add (GST_BIN (pipeline), fakesrc2);
gst_bin_add (GST_BIN (pipeline), fakesink2);
gst_pad_link (gst_element_get_pad (fakesrc2, "src"),
gst_element_get_pad (fakesink2, "sink"));
g_signal_connect (G_OBJECT (pipeline), "deep_notify",
G_CALLBACK (gst_element_default_deep_notify), NULL);
G_CALLBACK (gst_object_default_deep_notify), NULL);
GST_FLAG_SET (fakesrc2, GST_ELEMENT_LOCKED_STATE);
GST_FLAG_SET (fakesink2, GST_ELEMENT_LOCKED_STATE);
g_print ("play..\n");
gst_element_set_state (pipeline, GST_STATE_PLAYING);
while (gst_bin_iterate (GST_BIN (pipeline)));
gst_element_set_state (pipeline, GST_STATE_READY);
gst_main ();
g_object_set (G_OBJECT (fakesrc1), "num_buffers", 5, NULL);
gst_element_set_state (pipeline, GST_STATE_READY);
GST_FLAG_UNSET (fakesrc2, GST_ELEMENT_LOCKED_STATE);
GST_FLAG_UNSET (fakesink2, GST_ELEMENT_LOCKED_STATE);
g_print ("play..\n");
gst_element_set_state (pipeline, GST_STATE_PLAYING);
while (gst_bin_iterate (GST_BIN (pipeline)));
gst_main ();
gst_element_set_state (pipeline, GST_STATE_NULL);
gst_object_unref (GST_OBJECT (pipeline));

View file

@ -505,40 +505,12 @@ print_element_flag_info (GstElement * element)
n_print ("\n");
n_print ("Element Flags:\n");
if (GST_FLAG_IS_SET (element, GST_ELEMENT_COMPLEX)) {
n_print (" GST_ELEMENT_COMPLEX\n");
have_flags = TRUE;
}
if (GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED)) {
n_print (" GST_ELEMENT_DECOUPLED\n");
have_flags = TRUE;
}
if (GST_FLAG_IS_SET (element, GST_ELEMENT_THREAD_SUGGESTED)) {
n_print (" GST_ELEMENT_THREADSUGGESTED\n");
have_flags = TRUE;
}
if (GST_FLAG_IS_SET (element, GST_ELEMENT_EVENT_AWARE)) {
n_print (" GST_ELEMENT_EVENT_AWARE\n");
have_flags = TRUE;
}
if (!have_flags)
n_print (" no flags set\n");
if (GST_IS_BIN (element)) {
n_print ("\n");
n_print ("Bin Flags:\n");
if (GST_FLAG_IS_SET (element, GST_BIN_FLAG_MANAGER)) {
n_print (" GST_BIN_FLAG_MANAGER\n");
have_flags = TRUE;
}
if (GST_FLAG_IS_SET (element, GST_BIN_SELF_SCHEDULABLE)) {
n_print (" GST_BIN_SELF_SCHEDULABLE\n");
have_flags = TRUE;
}
if (GST_FLAG_IS_SET (element, GST_BIN_FLAG_PREFER_COTHREADS)) {
n_print (" GST_BIN_FLAG_PREFER_COTHREADS\n");
have_flags = TRUE;
}
if (!have_flags)
n_print (" no flags set\n");
}
@ -556,11 +528,7 @@ print_implementation_info (GstElement * element)
n_print ("\n");
n_print ("Element Implementation:\n");
if (element->loopfunc)
n_print (" loopfunc()-based element: %s\n",
GST_DEBUG_FUNCPTR_NAME (element->loopfunc));
else
n_print (" No loopfunc(), must be chain-based or not configured yet\n");
n_print (" No loopfunc(), must be chain-based or not configured yet\n");
n_print (" Has change_state() function: %s\n",
GST_DEBUG_FUNCPTR_NAME (gstelement_class->change_state));
@ -854,7 +822,8 @@ print_children_info (GstElement * element)
if (!GST_IS_BIN (element))
return;
children = (GList *) gst_bin_get_list (GST_BIN (element));
//children = (GList *) gst_bin_get_list (GST_BIN (element));
children = (GList *) GST_BIN (element)->children;
if (children) {
n_print ("\n");
g_print ("Children:\n");

View file

@ -59,63 +59,12 @@ static void sigint_restore (void);
#endif
static gint max_iterations = 0;
static guint64 iterations = 0;
static guint64 sum = 0;
static guint64 min = G_MAXINT64;
static guint64 max = 0;
static GstClock *s_clock;
static GstElement *pipeline;
gboolean caught_intr = FALSE;
gboolean caught_error = FALSE;
gboolean tags = FALSE;
GMainLoop *loop;
gboolean
idle_func (gpointer data)
{
gboolean busy;
GTimeVal tfthen, tfnow;
GstClockTimeDiff diff;
g_get_current_time (&tfthen);
busy = gst_bin_iterate (GST_BIN (data));
iterations++;
g_get_current_time (&tfnow);
diff = GST_TIMEVAL_TO_TIME (tfnow) - GST_TIMEVAL_TO_TIME (tfthen);
sum += diff;
min = MIN (min, diff);
max = MAX (max, diff);
if (!busy || caught_intr || caught_error ||
(max_iterations > 0 && iterations >= max_iterations)) {
char *s_iterations;
char *s_sum;
char *s_ave;
char *s_min;
char *s_max;
gst_main_quit ();
/* We write these all to strings first because
* G_GUINT64_FORMAT and gettext mix very poorly */
s_iterations = g_strdup_printf ("%" G_GUINT64_FORMAT, iterations);
s_sum = g_strdup_printf ("%" G_GUINT64_FORMAT, sum);
s_ave = g_strdup_printf ("%" G_GUINT64_FORMAT, sum / iterations);
s_min = g_strdup_printf ("%" G_GUINT64_FORMAT, min);
s_max = g_strdup_printf ("%" G_GUINT64_FORMAT, max);
g_print (_("Execution ended after %s iterations (sum %s ns, "
"average %s ns, min %s ns, max %s ns).\n"),
s_iterations, s_sum, s_ave, s_min, s_max);
g_free (s_iterations);
g_free (s_sum);
g_free (s_ave);
g_free (s_min);
g_free (s_max);
}
return busy;
}
#ifndef GST_DISABLE_LOADSAVE
static GstElement *
@ -310,21 +259,6 @@ print_tag (const GstTagList * list, const gchar * tag, gpointer unused)
}
}
static void
found_tag (GObject * pipeline, GstElement * source, GstTagList * tags)
{
g_print (_("FOUND TAG : found by element \"%s\".\n"),
GST_STR_NULL (GST_ELEMENT_NAME (source)));
gst_tag_list_foreach (tags, print_tag, NULL);
}
static void
error_cb (GObject * object, GstObject * source, GError * error, gchar * debug)
{
gst_element_default_error (object, source, error, debug);
caught_error = TRUE;
}
#ifndef DISABLE_FAULT_HANDLER
/* we only use sighandler here because the registers are not important */
static void
@ -386,6 +320,38 @@ play_signal_setup (void)
}
#endif
static gboolean
message_received (GstBus * bus, GstMessage * message, GstPipeline * pipeline)
{
switch (GST_MESSAGE_TYPE (message)) {
case GST_MESSAGE_EOS:
if (g_main_loop_is_running (loop))
g_main_loop_quit (loop);
break;
case GST_MESSAGE_TAG:
if (tags) {
g_print (_("FOUND TAG : found by element \"%s\".\n"),
GST_STR_NULL (GST_ELEMENT_NAME (GST_MESSAGE_SRC (message))));
gst_tag_list_foreach (GST_MESSAGE_TAG_LIST (message), print_tag, NULL);
}
break;
case GST_MESSAGE_ERROR:
gst_object_default_error (GST_MESSAGE_SRC (message),
GST_MESSAGE_ERROR_ERROR (message), GST_MESSAGE_ERROR_DEBUG (message));
caught_error = TRUE;
gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL);
if (g_main_loop_is_running (loop))
g_main_loop_quit (loop);
break;
default:
break;
}
gst_message_unref (message);
return TRUE;
}
int
main (int argc, char *argv[])
{
@ -393,7 +359,6 @@ main (int argc, char *argv[])
/* options */
gboolean verbose = FALSE;
gboolean tags = FALSE;
gboolean no_fault = FALSE;
gboolean trace = FALSE;
gchar *savefile = NULL;
@ -501,12 +466,13 @@ main (int argc, char *argv[])
gchar **exclude_list =
exclude_args ? g_strsplit (exclude_args, ",", 0) : NULL;
g_signal_connect (pipeline, "deep_notify",
G_CALLBACK (gst_element_default_deep_notify), exclude_list);
G_CALLBACK (gst_object_default_deep_notify), exclude_list);
}
if (tags) {
g_signal_connect (pipeline, "found-tag", G_CALLBACK (found_tag), NULL);
}
g_signal_connect (pipeline, "error", G_CALLBACK (error_cb), NULL);
loop = g_main_loop_new (NULL, FALSE);
gst_bus_add_watch (GST_PIPELINE (pipeline)->bus,
(GstBusHandler) message_received, pipeline);
#ifndef GST_DISABLE_LOADSAVE
if (savefile) {
@ -515,6 +481,7 @@ main (int argc, char *argv[])
#endif
if (!savefile) {
GstElementState state, pending;
if (!GST_IS_BIN (pipeline)) {
GstElement *real_pipeline = gst_element_factory_make ("pipeline", NULL);
@ -527,28 +494,38 @@ main (int argc, char *argv[])
pipeline = real_pipeline;
}
fprintf (stderr, _("RUNNING pipeline ...\n"));
if (gst_element_set_state (pipeline,
GST_STATE_PLAYING) == GST_STATE_FAILURE) {
fprintf (stderr, _("ERROR: pipeline doesn't want to play.\n"));
fprintf (stderr, _("PREROLL pipeline ...\n"));
if (gst_element_set_state (pipeline, GST_STATE_PAUSED) == GST_STATE_FAILURE) {
fprintf (stderr, _("ERROR: pipeline doesn't want to pause.\n"));
res = -1;
goto end;
}
gst_element_get_state (pipeline, &state, &pending, NULL);
/* see if we got any messages */
while (g_main_context_iteration (NULL, FALSE));
s_clock = gst_bin_get_clock (GST_BIN (pipeline));
if (!GST_FLAG_IS_SET (GST_OBJECT (pipeline), GST_BIN_SELF_SCHEDULABLE)) {
g_idle_add (idle_func, pipeline);
gst_main ();
if (caught_error) {
fprintf (stderr, _("ERROR: pipeline doesn't want to preroll.\n"));
} else {
g_print ("Waiting for the state change... ");
gst_element_wait_state_change (pipeline);
g_print ("got the state change.\n");
GTimeVal tfthen, tfnow;
GstClockTimeDiff diff;
fprintf (stderr, _("RUNNING pipeline ...\n"));
if (gst_element_set_state (pipeline,
GST_STATE_PLAYING) == GST_STATE_FAILURE) {
fprintf (stderr, _("ERROR: pipeline doesn't want to play.\n"));
res = -1;
goto end;
}
g_get_current_time (&tfthen);
g_main_loop_run (loop);
g_get_current_time (&tfnow);
diff = GST_TIMEVAL_TO_TIME (tfnow) - GST_TIMEVAL_TO_TIME (tfthen);
g_print (_("Execution ended after %" G_GUINT64_FORMAT " ns.\n"), diff);
}
if (caught_intr)
res = 2;
if (caught_error)
res = 3;
gst_element_set_state (pipeline, GST_STATE_NULL);
}

View file

@ -25,7 +25,8 @@ idle_func (gpointer data)
}
g_get_current_time (&tfthen);
busy = gst_bin_iterate (GST_BIN (data));
//busy = gst_bin_iterate (GST_BIN (data));
busy = FALSE;
iterations++;
g_get_current_time (&tfnow);
@ -36,7 +37,7 @@ idle_func (gpointer data)
max = MAX (max, diff);
if (!busy) {
gst_main_quit ();
//gst_main_quit ();
/*
g_print ("execution ended after %llu iterations (sum %llu ns, average %llu ns, min %llu ns, max %llu ns)\n",
iterations, sum, sum/iterations, min, max);
@ -76,7 +77,7 @@ main (int argc, char *argv[])
/* Check if we have an element already that is called md5sink0
in the pipeline; if not, add one */
pipeline = (GstElement *) gst_parse_launchv ((const gchar **) argvn, &error);
//pipeline = (GstElement *) gst_parse_launchv ((const gchar **) argvn, &error);
if (!pipeline) {
if (error) {
g_warning ("pipeline could not be constructed: %s\n", error->message);
@ -115,22 +116,17 @@ main (int argc, char *argv[])
: NULL;
g_signal_connect (pipeline, "deep_notify",
G_CALLBACK (gst_element_default_deep_notify), exclude_list);
G_CALLBACK (gst_object_default_deep_notify), exclude_list);
}
g_signal_connect (pipeline, "error",
G_CALLBACK (gst_element_default_error), NULL);
//g_signal_connect (pipeline, "error",
// G_CALLBACK (gst_object_default_error), NULL);
if (gst_element_set_state (pipeline, GST_STATE_PLAYING) != GST_STATE_SUCCESS) {
g_warning ("pipeline doesn't want to play\n");
return 0;
}
if (!GST_FLAG_IS_SET (GST_OBJECT (pipeline), GST_BIN_SELF_SCHEDULABLE)) {
g_idle_add (idle_func, pipeline);
gst_main ();
} else {
gst_element_wait_state_change (pipeline);
}
gst_main ();
gst_element_set_state (pipeline, GST_STATE_NULL);

View file

@ -52,8 +52,10 @@ main (int argc, char *argv[])
g_assert (GST_IS_ELEMENT (source));
typefind = gst_element_factory_make ("typefind", "typefind");
g_assert (GST_IS_ELEMENT (typefind));
gst_bin_add_many (GST_BIN (pipeline), source, typefind, NULL);
gst_element_link (source, typefind);
gst_bin_add (GST_BIN (pipeline), source);
gst_bin_add (GST_BIN (pipeline), typefind);
gst_pad_link (gst_element_get_pad (source, "src"),
gst_element_get_pad (typefind, "sink"));
g_signal_connect (G_OBJECT (typefind), "have-type",
G_CALLBACK (have_type_handler), NULL);
@ -66,8 +68,6 @@ main (int argc, char *argv[])
gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
while (!FOUND) {
if (!gst_bin_iterate (GST_BIN (pipeline)))
break;
}
if (!FOUND) {
g_print ("%s - No type found\n", argv[i]);

View file

@ -472,8 +472,6 @@ print_element_info (GstElementFactory * factory)
GstPad *pad;
GstRealPad *realpad;
GstPadTemplate *padtemplate;
GList *children;
GstElement *child;
gint maxlevel = 0;
element = gst_element_factory_create (factory, "element");
@ -533,40 +531,16 @@ print_element_info (GstElementFactory * factory)
PUT_END_TAG (1, "pad-templates");
PUT_START_TAG (1, "element-flags");
if (GST_FLAG_IS_SET (element, GST_ELEMENT_COMPLEX)) {
PUT_ESCAPED (2, "flag", "GST_ELEMENT_COMPLEX");
}
if (GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED)) {
PUT_ESCAPED (2, "flag", "GST_ELEMENT_DECOUPLED");
}
if (GST_FLAG_IS_SET (element, GST_ELEMENT_THREAD_SUGGESTED)) {
PUT_ESCAPED (2, "flag", "GST_ELEMENT_THREADSUGGESTED");
}
if (GST_FLAG_IS_SET (element, GST_ELEMENT_EVENT_AWARE)) {
PUT_ESCAPED (2, "flag", "GST_ELEMENT_EVENT_AWARE");
}
PUT_END_TAG (1, "element-flags");
if (GST_IS_BIN (element)) {
PUT_START_TAG (1, "bin-flags");
if (GST_FLAG_IS_SET (element, GST_BIN_FLAG_MANAGER)) {
PUT_ESCAPED (2, "flag", "GST_BIN_FLAG_MANAGER");
}
if (GST_FLAG_IS_SET (element, GST_BIN_SELF_SCHEDULABLE)) {
PUT_ESCAPED (2, "flag", "GST_BIN_SELF_SCHEDULABLE");
}
if (GST_FLAG_IS_SET (element, GST_BIN_FLAG_PREFER_COTHREADS)) {
PUT_ESCAPED (2, "flag", "GST_BIN_FLAG_PREFER_COTHREADS");
}
PUT_END_TAG (1, "bin-flags");
}
PUT_START_TAG (1, "element-implementation");
if (element->loopfunc)
PUT_STRING (2, "<loop-based function=\"%s\"/>",
GST_DEBUG_FUNCPTR_NAME (element->loopfunc));
PUT_STRING (2, "<state-change function=\"%s\"/>",
GST_DEBUG_FUNCPTR_NAME (gstelement_class->change_state));
@ -681,17 +655,21 @@ print_element_info (GstElementFactory * factory)
print_element_signals (element, 1);
/* for compound elements */
if (GST_IS_BIN (element)) {
PUT_START_TAG (1, "children");
children = (GList *) gst_bin_get_list (GST_BIN (element));
while (children) {
child = GST_ELEMENT (children->data);
children = g_list_next (children);
/* FIXME: gst_bin_get_list does not exist anymore
if (GST_IS_BIN (element)) {
GList *children;
GstElement *child;
PUT_START_TAG (1, "children");
children = (GList *) gst_bin_get_list (GST_BIN (element));
while (children) {
child = GST_ELEMENT (children->data);
children = g_list_next (children);
PUT_ESCAPED (2, "child", GST_ELEMENT_NAME (child));
}
PUT_END_TAG (1, "children");
}
PUT_ESCAPED (2, "child", GST_ELEMENT_NAME (child));
}
PUT_END_TAG (1, "children");
}
*/
PUT_END_TAG (0, "element");
return 0;