mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 11:41:09 +00:00
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:
parent
047c0a3e9b
commit
3ffce00efc
91 changed files with 7047 additions and 7866 deletions
39
ChangeLog
39
ChangeLog
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 <gst/gst.h>
|
||||
&hash;include <gst/control/control.h>
|
||||
...
|
||||
gst_init(&argc,&argv);
|
||||
gst_control_init(&argc,&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:
|
||||
|
||||
|
||||
|
|
|
@ -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 ##### -->
|
||||
|
|
257
docs/manual/advanced-autoplugging.xml
Normal file
257
docs/manual/advanced-autoplugging.xml
Normal 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.ℑ" 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>
|
5
docs/manual/advanced-clocks.xml
Normal file
5
docs/manual/advanced-clocks.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<chapter id="chapter-clocks">
|
||||
<title>Clocks in GStreamer</title>
|
||||
<para>
|
||||
</para>
|
||||
</chapter>
|
198
docs/manual/advanced-dparams.xml
Normal file
198
docs/manual/advanced-dparams.xml
Normal 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 <gst/gst.h>
|
||||
#include <gst/control/control.h>
|
||||
...
|
||||
</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(&argc,&argv);
|
||||
gst_control_init(&argc,&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(&mem, 0, sizeof(mem))
|
||||
...
|
||||
|
||||
gdouble set_to_value;
|
||||
GstDParam *volume;
|
||||
GValue set_val;
|
||||
ZERO(set_val);
|
||||
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>
|
||||
<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>
|
168
docs/manual/advanced-threads.xml
Normal file
168
docs/manual/advanced-threads.xml
Normal 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.ℑ" 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 <gst/gst.h>
|
||||
|
||||
/* 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 < 2) {
|
||||
g_print ("usage: %s <Ogg/Vorbis filename>\n", argv[0]);
|
||||
exit (-1);
|
||||
}
|
||||
|
||||
gst_init (&argc, &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>
|
253
docs/manual/appendix-quotes.xml
Normal file
253
docs/manual/appendix-quotes.xml
Normal 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 >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 "zan"
|
||||
</para>
|
||||
<para>
|
||||
<emphasis>Omega:</emphasis>
|
||||
?
|
||||
</para>
|
||||
<para>
|
||||
<emphasis>wtay:</emphasis>
|
||||
me tar"zan", 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 <g>
|
||||
</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 <g> </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>
|
49
docs/manual/basics-bins.xml
Normal file
49
docs/manual/basics-bins.xml
Normal 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.ℑ" 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>
|
66
docs/manual/basics-data.xml
Normal file
66
docs/manual/basics-data.xml
Normal 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>
|
122
docs/manual/basics-elements.xml
Normal file
122
docs/manual/basics-elements.xml
Normal 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.ℑ" 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.ℑ" 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.ℑ"
|
||||
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.ℑ" format="&IMAGE;" />
|
||||
</imageobject>
|
||||
</mediaobject>
|
||||
</figure>
|
||||
</sect2>
|
||||
</sect1>
|
||||
</chapter>
|
280
docs/manual/basics-helloworld.xml
Normal file
280
docs/manual/basics-helloworld.xml
Normal 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 <gst/gst.h>
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GstElement *pipeline, *filesrc, *decoder, *audiosink;
|
||||
|
||||
gst_init(&argc, &argv);
|
||||
|
||||
if (argc != 2) {
|
||||
g_print ("usage: %s <mp3 filename>\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 <gst/gst.h>
|
||||
|
||||
...
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
...
|
||||
gst_init(&argc, &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.ℑ" 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>
|
99
docs/manual/basics-init.xml
Normal file
99
docs/manual/basics-init.xml
Normal 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 (&argc, &argv);
|
||||
|
||||
gst_version (&major, &minor, &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 <gst/gst.h>
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
gboolean silent = FALSE;
|
||||
gchar *savefile = NULL;
|
||||
struct poptOption options[] = {
|
||||
{"silent", 's', POPT_ARG_NONE|POPT_ARGFLAG_STRIP, &silent, 0,
|
||||
"do not output status information", NULL},
|
||||
{"output", 'o', POPT_ARG_STRING|POPT_ARGFLAG_STRIP, &savefile, 0,
|
||||
"save xml representation of pipeline to FILE and exit", "FILE"},
|
||||
POPT_TABLEEND
|
||||
};
|
||||
|
||||
gst_init_with_popt_table (&argc, &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
244
docs/manual/basics-pads.xml
Normal 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>
|
31
docs/manual/basics-plugins.xml
Normal file
31
docs/manual/basics-plugins.xml
Normal 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>
|
37
docs/manual/highlevel-components.xml
Normal file
37
docs/manual/highlevel-components.xml
Normal 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>
|
283
docs/manual/highlevel-xml.xml
Normal file
283
docs/manual/highlevel-xml.xml
Normal 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 <stdlib.h>
|
||||
#include <gst/gst.h>
|
||||
|
||||
gboolean playing;
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GstElement *filesrc, *osssink, *queue, *queue2, *decode;
|
||||
GstElement *bin;
|
||||
GstElement *thread, *thread2;
|
||||
|
||||
gst_init (&argc,&argv);
|
||||
|
||||
if (argc != 2) {
|
||||
g_print ("usage: %s <mp3 filename>\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 <stdlib.h>
|
||||
#include <gst/gst.h>
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
GstXML *xml;
|
||||
GstElement *bin;
|
||||
gboolean ret;
|
||||
|
||||
gst_init (&argc, &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>
|
||||
...
|
||||
<gst:element>
|
||||
<gst:name>thread</gst:name>
|
||||
<gst:type>thread</gst:type>
|
||||
<gst:version>0.1.0</gst:version>
|
||||
...
|
||||
</gst:children>
|
||||
<test:comment>
|
||||
<test:text>decoder thread</test:text>
|
||||
</test:comment>
|
||||
</gst:element>
|
||||
...
|
||||
</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->xmlChildrenNode;
|
||||
|
||||
while (children) {
|
||||
if (!strcmp (children->name, "comment")) {
|
||||
xmlNodePtr nodes = children->xmlChildrenNode;
|
||||
|
||||
while (nodes) {
|
||||
if (!strcmp (nodes->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->next;
|
||||
}
|
||||
}
|
||||
children = children->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>
|
111
docs/manual/intro-motivation.xml
Normal file
111
docs/manual/intro-motivation.xml
Normal 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>
|
59
docs/manual/intro-preface.xml
Normal file
59
docs/manual/intro-preface.xml
Normal 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>
|
|
@ -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 \
|
||||
|
|
|
@ -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) \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -79,6 +79,8 @@ struct _GstFakeSrc {
|
|||
gboolean loop_based;
|
||||
gboolean eos;
|
||||
|
||||
GstTask *task;
|
||||
|
||||
GstFakeSrcOutputType output;
|
||||
GstFakeSrcDataType data;
|
||||
GstFakeSrcSizeType sizetype;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -69,6 +69,8 @@ struct _GstFileSrc {
|
|||
gboolean is_regular; /* whether it's (symlink to)
|
||||
a regular file */
|
||||
|
||||
GstTask *task;
|
||||
|
||||
GstBuffer *mapbuf;
|
||||
size_t mapsize;
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
50
gst/gst.c
50
gst/gst.c
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
1085
gst/gstbin.c
1085
gst/gstbin.c
File diff suppressed because it is too large
Load diff
48
gst/gstbin.h
48
gst/gstbin.h
|
@ -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
|
||||
|
||||
|
|
117
gst/gstbuffer.c
117
gst/gstbuffer.c
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
149
gst/gstcaps.c
149
gst/gstcaps.c
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
2528
gst/gstelement.c
2528
gst/gstelement.c
File diff suppressed because it is too large
Load diff
236
gst/gstelement.h
236
gst/gstelement.h
|
@ -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__ */
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
2902
gst/gstpad.c
2902
gst/gstpad.c
File diff suppressed because it is too large
Load diff
226
gst/gstpad.h
226
gst/gstpad.h
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,12 +102,12 @@ _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 \
|
||||
|
@ -125,15 +125,14 @@ 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);
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
|
691
gst/gstqueue.c
691
gst/gstqueue.c
|
@ -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)
|
||||
|
|
|
@ -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];
|
||||
};
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
933
gst/gstutils.c
933
gst/gstutils.c
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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) \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -79,6 +79,8 @@ struct _GstFakeSrc {
|
|||
gboolean loop_based;
|
||||
gboolean eos;
|
||||
|
||||
GstTask *task;
|
||||
|
||||
GstFakeSrcOutputType output;
|
||||
GstFakeSrcDataType data;
|
||||
GstFakeSrcSizeType sizetype;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -69,6 +69,8 @@ struct _GstFileSrc {
|
|||
gboolean is_regular; /* whether it's (symlink to)
|
||||
a regular file */
|
||||
|
||||
GstTask *task;
|
||||
|
||||
GstBuffer *mapbuf;
|
||||
size_t mapsize;
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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];
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue