From 3ffce00efcfd902f0a190c7b7938c6ab34a2f448 Mon Sep 17 00:00:00 2001 From: Thomas Vander Stichele Date: Wed, 8 Dec 2004 17:40:37 +0000 Subject: [PATCH] commiting wim's preliminary threaded work to a branch Original commit message from CVS: commiting wim's preliminary threaded work to a branch --- ChangeLog | 39 +- Makefile.am | 8 +- autogen.sh | 2 +- docs/gst/gstreamer-sections.txt | 1 - docs/gst/tmpl/gstbuffer.sgml | 18 + docs/libs/tmpl/gstcontrol.sgml | 66 +- docs/libs/tmpl/gstgetbits.sgml | 12 +- docs/manual/advanced-autoplugging.xml | 257 +++ docs/manual/advanced-clocks.xml | 5 + docs/manual/advanced-dparams.xml | 198 ++ docs/manual/advanced-threads.xml | 168 ++ docs/manual/appendix-quotes.xml | 253 +++ docs/manual/basics-bins.xml | 49 + docs/manual/basics-data.xml | 66 + docs/manual/basics-elements.xml | 122 ++ docs/manual/basics-helloworld.xml | 280 +++ docs/manual/basics-init.xml | 99 + docs/manual/basics-pads.xml | 244 +++ docs/manual/basics-plugins.xml | 31 + docs/manual/highlevel-components.xml | 37 + docs/manual/highlevel-xml.xml | 283 +++ docs/manual/intro-motivation.xml | 111 + docs/manual/intro-preface.xml | 59 + gst/Makefile.am | 21 +- gst/elements/Makefile.am | 13 +- gst/elements/gstelements.c | 24 +- gst/elements/gstfakesink.c | 107 +- gst/elements/gstfakesrc.c | 128 +- gst/elements/gstfakesrc.h | 2 + gst/elements/gstfilesrc.c | 119 +- gst/elements/gstfilesrc.h | 2 + gst/elements/gstidentity.c | 87 +- gst/gst.c | 50 +- gst/gst.h | 4 +- gst/gst_private.h | 2 + gst/gstbin.c | 1085 ++++----- gst/gstbin.h | 48 +- gst/gstbuffer.c | 117 +- gst/gstbuffer.h | 27 +- gst/gstcaps.c | 149 +- gst/gstcaps.h | 30 +- gst/gstclock.c | 82 - gst/gstclock.h | 17 +- gst/gstdata.c | 2 +- gst/gstelement.c | 2530 ++++----------------- gst/gstelement.h | 236 +- gst/gstelementfactory.c | 2 +- gst/gstevent.h | 25 +- gst/gstindex.c | 3 +- gst/gstinfo.c | 3 + gst/gstobject.c | 67 +- gst/gstobject.h | 24 +- gst/gstpad.c | 2902 ++++++------------------- gst/gstpad.h | 226 +- gst/gstpipeline.c | 301 ++- gst/gstpipeline.h | 25 +- gst/gstplugin.c | 16 - gst/gstplugin.h | 47 +- gst/gstqueue.c | 691 +++--- gst/gstqueue.h | 12 +- gst/gstscheduler.c | 547 +---- gst/gstscheduler.h | 74 +- gst/gstsystemclock.c | 6 - gst/gsttypefind.c | 2 +- gst/gsttypes.h | 3 + gst/gstutils.c | 933 ++++++++ gst/gstutils.h | 45 +- gst/parse/grammar.y | 7 +- gst/registries/gstxmlregistry.c | 6 +- gst/schedulers/Makefile.am | 83 +- libs/gst/bytestream/bytestream.c | 2 +- libs/gst/bytestream/filepad.c | 12 +- libs/gst/dataprotocol/dataprotocol.c | 8 - plugins/elements/Makefile.am | 13 +- plugins/elements/gstelements.c | 24 +- plugins/elements/gstfakesink.c | 107 +- plugins/elements/gstfakesrc.c | 128 +- plugins/elements/gstfakesrc.h | 2 + plugins/elements/gstfilesrc.c | 119 +- plugins/elements/gstfilesrc.h | 2 + plugins/elements/gstidentity.c | 87 +- plugins/elements/gstqueue.c | 691 +++--- plugins/elements/gstqueue.h | 12 +- po/LINGUAS | 2 +- tests/old/testsuite/states/locked.c | 46 +- testsuite/states/locked.c | 46 +- tools/gst-inspect.c | 37 +- tools/gst-launch.c | 157 +- tools/gst-md5sum.c | 20 +- tools/gst-typefind.c | 8 +- tools/gst-xmlinspect.c | 50 +- 91 files changed, 7047 insertions(+), 7866 deletions(-) create mode 100644 docs/manual/advanced-autoplugging.xml create mode 100644 docs/manual/advanced-clocks.xml create mode 100644 docs/manual/advanced-dparams.xml create mode 100644 docs/manual/advanced-threads.xml create mode 100644 docs/manual/appendix-quotes.xml create mode 100644 docs/manual/basics-bins.xml create mode 100644 docs/manual/basics-data.xml create mode 100644 docs/manual/basics-elements.xml create mode 100644 docs/manual/basics-helloworld.xml create mode 100644 docs/manual/basics-init.xml create mode 100644 docs/manual/basics-pads.xml create mode 100644 docs/manual/basics-plugins.xml create mode 100644 docs/manual/highlevel-components.xml create mode 100644 docs/manual/highlevel-xml.xml create mode 100644 docs/manual/intro-motivation.xml create mode 100644 docs/manual/intro-preface.xml diff --git a/ChangeLog b/ChangeLog index 8619823654..50e2cb0e2c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,41 +1,7 @@ 2004-12-08 Thomas Vander Stichele - * autogen.sh: - remove patch if autopoint fails - -2004-12-08 Ronald S. Bultje - - * docs/gst/gstreamer-sections.txt: - Document Thomas' addition, fix build, make Luis the sheriff happy. - -2004-12-07 Thomas Vander Stichele - - * gst/gstplugin.c: - * gst/gstplugin.h: - add accessor for version field - -2004-12-06 Thomas Vander Stichele - - submitted by: Luca Ferretti - - * po/LINGUAS: - * po/it.po: - New tranlation added: Italian - -2004-12-03 Ronald S. Bultje - - * 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 - - * 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 * gst/autoplug/gstspider.c: (plugin_init): diff --git a/Makefile.am b/Makefile.am index aa4111ecb7..6b07b00001 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 diff --git a/autogen.sh b/autogen.sh index adbaa7e250..bb84be30f3 100755 --- a/autogen.sh +++ b/autogen.sh @@ -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 diff --git a/docs/gst/gstreamer-sections.txt b/docs/gst/gstreamer-sections.txt index b2ad2e88a2..9b9b351b8c 100644 --- a/docs/gst/gstreamer-sections.txt +++ b/docs/gst/gstreamer-sections.txt @@ -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 diff --git a/docs/gst/tmpl/gstbuffer.sgml b/docs/gst/tmpl/gstbuffer.sgml index 56000aaafd..ff23335740 100644 --- a/docs/gst/tmpl/gstbuffer.sgml +++ b/docs/gst/tmpl/gstbuffer.sgml @@ -348,10 +348,28 @@ If the refcount is 1, this function just returns the original buffer. +<<<<<<< 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 diff --git a/docs/libs/tmpl/gstcontrol.sgml b/docs/libs/tmpl/gstcontrol.sgml index 3ebdcba645..c293daec4d 100644 --- a/docs/libs/tmpl/gstcontrol.sgml +++ b/docs/libs/tmpl/gstcontrol.sgml @@ -2,37 +2,20 @@ gstcontrol -dynamic parameter functionality. + -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. + - -To use this library one needs to add some code to initialize it. - -Adding the control library to a project - -... -&hash;include <gst/gst.h> -&hash;include <gst/control/control.h> -... -gst_init(&argc,&argv); -gst_control_init(&argc,&argv); -... - - -The next step is to get hold of the GstDParamManager instance of a GstElement. - + @@ -124,10 +107,8 @@ The next step is to get hold of the GstDParamManager instance of a GstElement. @dpman: -@dparam_name: -@Returns: - @name: +@Returns: @@ -136,40 +117,10 @@ The next step is to get hold of the GstDParamManager instance of a GstElement. @dpman: -@dparam_name: -@Returns: - @name: - - - - - - - -@dpman: @Returns: - - - - - -@dpman: -@dparam_name: -@Returns: - - - - - - - -@dpman: -@rate: - - @@ -259,12 +210,3 @@ The next step is to get hold of the GstDParamManager instance of a GstElement. @update_info: - - - - - -@type: -@Returns: - - diff --git a/docs/libs/tmpl/gstgetbits.sgml b/docs/libs/tmpl/gstgetbits.sgml index 1d107d65fa..f3c678a106 100644 --- a/docs/libs/tmpl/gstgetbits.sgml +++ b/docs/libs/tmpl/gstgetbits.sgml @@ -2,18 +2,20 @@ gstgetbits -accelerated routines for getting bits from a data stream. + + + @@ -130,8 +132,6 @@ accelerated routines for getting bits from a data stream. @gb: @n: - -@num: @@ -325,8 +325,6 @@ accelerated routines for getting bits from a data stream. @gb: @n: - -@num: @@ -536,8 +534,6 @@ accelerated routines for getting bits from a data stream. @gb: @n: - -@num: @@ -555,8 +551,6 @@ accelerated routines for getting bits from a data stream. @gb: @n: - -@num: diff --git a/docs/manual/advanced-autoplugging.xml b/docs/manual/advanced-autoplugging.xml new file mode 100644 index 0000000000..230828a10e --- /dev/null +++ b/docs/manual/advanced-autoplugging.xml @@ -0,0 +1,257 @@ + + More on factories + + 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. + + + + We will first explain the concepts involved before we move on + to the reworked helloworld example using autoplugging. + + + The problems with the helloworld example + + If we take a look at how the elements were created in the previous + example we used a rather crude mechanism: + + + + ... + /* now it's time to get the parser */ + decoder = gst_element_factory_make ("mad", "decoder"); + ... + + + + 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. + + + We have to introduce the concept of MIME types and capabilities + added to the source and sink pads. + + + + + More on MIME Types + + 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. + + + A MIME (Multipurpose Internet Mail Extension) type is a pair of + strings that denote a certain type of data. Examples include: + + + + audio/x-raw-int : raw audio samples + + + + + audio/mpeg : MPEG audio + + + + + video/mpeg : MPEG video + + + + + + 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. + + + As we have seen in the previous chapter, MIME types are added + to the Capability structure of a pad. + + + + shows the MIME types associated with + each pad from the "hello world" example. + +
+ The Hello world pipeline with MIME types + + + + + + +
+ + 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. + + + 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". + + + + 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. + + + +
+ + + GStreamer types + + 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. + + + 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. + + + The type information is maintained in a list of + GstType. The definition of a + GstType is like: + + + +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 */ +}; + + + + All operations on GstType occur + via their guint16 id numbers, with + the GstType structure private to the GStreamer + library. + + + + MIME type to id conversion + + + We can obtain the id for a given MIME type + with the following piece of code: + + + guint16 id; + + id = gst_type_find_by_mime ("audio/mpeg"); + + + This function will return 0 if the type was not known. + + + + + id to <classname>GstType</classname> conversion + + We can obtain the GstType for a given id + with the following piece of code: + + + GstType *type; + + type = gst_type_find_by_id (id); + + + This function will return NULL if the id was not associated with + any known GstType + + + + + extension to id conversion + + We can obtain the id for a given file extension + with the following piece of code: + + + guint16 id; + + id = gst_type_find_by_ext (".mp3"); + + + This function will return 0 if the extension was not known. + + + For more information, see . + + + + + + Creating elements with the factory + + 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: + + + GstElementFactory *factory; + GstElement *element; + + // obtain the factory + factory = ... + + element = gst_element_factory_create (factory, "name"); + + + 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. + + + + + GStreamer basic types + + GStreamer only has two builtin types: + + + + + audio/raw : raw audio samples + + + + + video/raw and image/raw : raw video data + + + + + All other MIME types are maintained by the plugin elements. + + + +
diff --git a/docs/manual/advanced-clocks.xml b/docs/manual/advanced-clocks.xml new file mode 100644 index 0000000000..68b3720203 --- /dev/null +++ b/docs/manual/advanced-clocks.xml @@ -0,0 +1,5 @@ + + Clocks in GStreamer + + + diff --git a/docs/manual/advanced-dparams.xml b/docs/manual/advanced-dparams.xml new file mode 100644 index 0000000000..b0f9d71f68 --- /dev/null +++ b/docs/manual/advanced-dparams.xml @@ -0,0 +1,198 @@ + + Dynamic Parameters + + + Getting Started + + The Dynamic Parameters subsystem is contained within the + gstcontrol library. + + You need to include the header in your application's source file: + + +... +#include <gst/gst.h> +#include <gst/control/control.h> +... + + + Your application should link to the shared library gstcontrol. + + + The gstcontrol library needs to be initialized + when your application is run. This can be done after the the GStreamer + library has been initialized. + + + ... + gst_init(&argc,&argv); + gst_control_init(&argc,&argv); + ... + + + + + Creating and Attaching Dynamic Parameters + + 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 gst_dpman_get_manager. + + + 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 "synchronous" - 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 "synchronous" because the dparams are polled by the element for changes before + each buffer is processed. Another yet-to-be-implemented mode is "asynchronous". This is used when + parameter changes are known ahead of time - such as with a timelined editor. The mode is called + "asynchronous" because parameter changes may happen in the middle of a buffer being processed. + + + GstElement *sinesrc; + GstDParamManager *dpman; + ... + sinesrc = gst_element_factory_make("sinesrc","sine-source"); + ... + dpman = gst_dpman_get_manager (sinesrc); + gst_dpman_set_mode(dpman, "synchronous"); + + + If you don't know the names of the required dparams for your element you can call + gst_dpman_list_dparam_specs(dpman) 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 + g_param_spec_get_name on each param spec in the array. In our example, + "volume" will be the name of our required dparam. + + + Each type of dparam currently has its own new function. This may eventually + be replaced by a factory method for creating new instances. A default dparam instance can be created + with the gst_dparam_new function. Once it is created it can be attached to a + required dparam in the element. + + + GstDParam *volume; + ... + volume = gst_dparam_new(G_TYPE_DOUBLE); + if (gst_dpman_attach_dparam (dpman, "volume", volume)){ + /* the dparam was successfully attached */ + ... + } + + + + + Changing Dynamic Parameter Values + + 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 + "value_double", "value_float", "value_int" and "value_int64". + To set the value of a dparam, simply set the property which matches the type of your dparam instance. + + +#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); + + Or if you create an actual GValue instance: + + 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); + + + + + + Different Types of Dynamic Parameter + + There are currently only two implementations of dparams so far. They are both for real-time use so + should be run in the "synchronous" mode. + + + GstDParam - the base dparam type + + 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 GstDParam* gst_dparam_new (GType type). + It has the following object properties: + + + "value_double" + - the property to set and get if it is a double dparam + + "value_float" + - the property to set and get if it is a float dparam + + "value_int" + - the property to set and get if it is an integer dparam + + "value_int64" + - the property to set and get if it is a 64 bit integer dparam + + "is_log" + - readonly boolean which is TRUE if the param should be displayed on a log scale + + "is_rate" + - 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. + + + + + GstDParamSmooth - smoothing real-time dparam + + 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 GstDParam* gst_dpsmooth_new (GType type). + It has the following object properties: + + + "update_period" + - an int64 value specifying the number nanoseconds between updates. This will be ignored in + "synchronous" mode since the buffer size dictates the update period. + + "slope_time" + - an int64 value specifying the time period to use in the maximum slope calculation + + "slope_delta_double" + - a double specifying the amount a double value can change in the given slope_time. + + "slope_delta_float" + - a float specifying the amount a float value can change in the given slope_time. + + + + 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. + + + + + Timelined dparams + + 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. + + + + + diff --git a/docs/manual/advanced-threads.xml b/docs/manual/advanced-threads.xml new file mode 100644 index 0000000000..05ea3cd8d1 --- /dev/null +++ b/docs/manual/advanced-threads.xml @@ -0,0 +1,168 @@ + + Threads + + GStreamer has support for multithreading through the use of + the + GstThread object. This object is in fact + a special + GstBin that will become a thread when started. + + + + To construct a new thread you will perform something like: + + + + + 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); + + + + + 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. + + + + Constraints placed on the pipeline by the GstThread + + 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 . 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. + + + + When would you want to use a thread? + + 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. + + + shows how a thread can be visualised. + +
+ A thread + + + + + +
+ + + As an example we show the helloworld program using a thread. + + + + +/* 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 */ + + +
+
diff --git a/docs/manual/appendix-quotes.xml b/docs/manual/appendix-quotes.xml new file mode 100644 index 0000000000..e2704282e6 --- /dev/null +++ b/docs/manual/appendix-quotes.xml @@ -0,0 +1,253 @@ + + Quotes from the Developers + + As well as being a cool piece of software, + GStreamer 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 + No guarantee of sense of humour compatibility is given. + quotes from our conversations. + + + + + + + 2 Nov 2004 + + + zaheerm: +wtay: unfair u fixed the bug i was using as a feature! + + + + + + + 14 Oct 2004 + + + * zaheerm +wonders how he can break gstreamer today :) + + + ensonic: +zaheerm, spider is always a good starting point + + + + + + 14 Jun 2004 + + + teuf: ok, things work much better when I don't write incredibly stupid and buggy code + + + thaytan: I find that too + + + + + 23 Nov 2003 + + + Uraeus: ah yes, the sleeping part, my mind + is not multitasking so I was still thinking about exercise + + + dolphy: Uraeus: your mind is multitasking + + + dolphy: Uraeus: you just miss low latency patches + + + + + + 14 Sep 2002 + + + --- wingo-party is now known as + wingo + + + * wingo holds head + + + + + + 16 Feb 2001 + + + wtay: + I shipped a few commerical products to >40000 people now but + GStreamer is way more exciting... + + + + + 16 Feb 2001 + + + * + tool-man + is a gstreamer groupie + + + + + 14 Jan 2001 + + + Omega: + did you run ldconfig? maybe it talks to init? + + + wtay: + not sure, don't think so... + I did run gstreamer-register though :-) + + + Omega: + ah, that did it then ;-) + + + wtay: + right + + + Omega: + probably not, but in case GStreamer starts turning into an OS, someone please let me know? + + + + + 9 Jan 2001 + + + wtay: + me tar, you rpm? + + + wtay: + hehe, forgot "zan" + + + Omega: + ? + + + wtay: + me tar"zan", you ... + + + + + 7 Jan 2001 + + + Omega: + that means probably building an agreggating, cache-massaging + queue to shove N buffers across all at once, forcing cache + transfer. + + + wtay: + never done that before... + + + Omega: + nope, but it's easy to do in gstreamer <g> + + + wtay: + sure, I need to rewrite cp with gstreamer too, someday :-) + + + + + 7 Jan 2001 + + + wtay: + GStreamer; always at least one developer is awake... + + + + + 5/6 Jan 2001 + + + wtay: + we need to cut down the time to create an mp3 player down to + seconds... + + + richardb: + :) + + + Omega: + I'm wanting to something more interesting soon, I did the "draw an mp3 + player in 15sec" back in October '99. + + + wtay: + by the time Omega gets his hands on the editor, you'll see a + complete audio mixer in the editor :-) + + + richardb: + Well, it clearly has the potential... + + + Omega: + Working on it... ;-) + + + + + 28 Dec 2000 + + + MPAA: + We will sue you now, you have violated our IP rights! + + + wtay: + hehehe + + + MPAA: + How dare you laugh at us? We have lawyers! We have Congressmen! We have LARS! + + + wtay: + I'm so sorry your honor + + + MPAA: + Hrumph. + + + * + wtay + bows before thy + + + + + 4 Jun 2001 + + taaz: you witchdoctors and your voodoo mpeg2 black magic... + omega_: um. I count three, no four different cults there <g> + ajmitch: hehe + omega_: witchdoctors, voodoo, black magic, + omega_: and mpeg + + + + diff --git a/docs/manual/basics-bins.xml b/docs/manual/basics-bins.xml new file mode 100644 index 0000000000..c1d6b3bbd2 --- /dev/null +++ b/docs/manual/basics-bins.xml @@ -0,0 +1,49 @@ + + Bins + + 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. + + + 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. + + + 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. + + +
+ Visualisation of a bin with some elements in it + + + + + +
+ + + There are two specialized bins available to the GStreamer programmer: + + + + + 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. + + + + + 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 . + + + + +
diff --git a/docs/manual/basics-data.xml b/docs/manual/basics-data.xml new file mode 100644 index 0000000000..2fef01a8c0 --- /dev/null +++ b/docs/manual/basics-data.xml @@ -0,0 +1,66 @@ + + Buffers + + 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. + + + A buffer consists of: + + + + + a pointer to a piece of memory. + + + + + the size of the memory. + + + + + a timestamp for the buffer. + + + + + 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. + + + + + + + + GStreamer provides functions to create custom buffer create/destroy algorithms, called + a GstBufferPool. This makes it possible to efficiently + allocate and destroy buffer memory. It also makes it possible to exchange memory between + elements by passing the GstBufferPool. 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. + + + + 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. + + + + 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. + + + diff --git a/docs/manual/basics-elements.xml b/docs/manual/basics-elements.xml new file mode 100644 index 0000000000..0ce686c79a --- /dev/null +++ b/docs/manual/basics-elements.xml @@ -0,0 +1,122 @@ + + Elements + + The most important object in GStreamer for the + application programmer is the GstElement + object. + + + + What is an element ? + + 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 + GstElement. This means that a + lot of functions you are going to use operate on objects of this class. + + + 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 ), or link points. + This terminology arises from soldering; pads are where wires can be + attached. + + + + + Types of elements + + + Source elements + + Source elements generate data for use by a pipeline, for example + reading from disk or from a sound card. + + + shows how we will visualise + a source element. + We always draw a source pad to the right of the element. + +
+ Visualisation of a source element + + + + + +
+ + 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. + +
+ + + Filters and codecs + + 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. + + + 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. + +
+ Visualisation of a filter element + + + + + +
+ + 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. + +
+ Visualisation of a filter element with + more than one output pad + + + + + +
+ + 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. + +
+ + + Sink elements + + 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. + shows a sink element. + +
+ Visualisation of a sink element + + + + + +
+
+
+
diff --git a/docs/manual/basics-helloworld.xml b/docs/manual/basics-helloworld.xml new file mode 100644 index 0000000000..68611b4fcf --- /dev/null +++ b/docs/manual/basics-helloworld.xml @@ -0,0 +1,280 @@ + + Your first application + + This chapter describes the most rudimentary aspects of a + GStreamer application, including initializing + the libraries, creating elements, packing them into a pipeline and playing, + pausing and stopping the pipeline. + + + + Hello world + + We will create a simple first application, a complete MP3 player, using + standard GStreamer components. The player + will read from a file that is given as the first argument to the program. + + + +/* 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 */ + + + + Let's go through this example step by step. + + + + The first thing you have to do is to include the standard + GStreamer headers and + initialize the framework. + + + +#include <gst/gst.h> + + ... + +int +main (int argc, char *argv[]) +{ + ... + gst_init(&argc, &argv); + ... + + + + + We are going to create three elements and one pipeline. Since all + elements share the same base type, GstElement, + we can define them as: + + + ... + GstElement *pipeline, *filesrc, *decoder, *audiosink; + ... + + + + 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. + + + /* create a new pipeline to hold the elements */ + pipeline = gst_pipeline_new ("pipeline"); + + + We use the standard constructor for a pipeline: gst_pipeline_new (). + + + + 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. + + + /* create a disk reader */ + filesrc = gst_element_factory_make ("filesrc", "disk_source"); + g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL); + + + + You can check if the filesrc != NULL to verify the creation of the + disk source element. + + + + + We now create the MP3 decoder element. This assumes that the 'mad' plugin + is installed on the system where this application is executed. + + + /* now it's time to get the decoder */ + decoder = gst_element_factory_make ("mad", "decoder"); + + + 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. + + + + Finally we create our audio sink element. This element will be able + to play back the audio using OSS. + + + /* and an audio sink */ + audiosink = gst_element_factory_make ("osssink", "play_audio"); + + + + We then add the elements to the pipeline. + + + /* add objects to the main pipeline */ + gst_bin_add_many (GST_BIN (pipeline), filesrc, decoder, audiosink, NULL); + + + + We link the different pads of the elements together like this: + + + /* link src to sink */ + gst_element_link_many (filesrc, decoder, audiosink, NULL); + + + + We now have created a complete pipeline. We can visualise the + pipeline as follows: + +
+ The "hello world" pipeline + + + + + + +
+ + + Everything is now set up to start streaming. We use the following + statements to change the state of the pipeline: + + + /* start playing */ + gst_element_set_state (pipeline, GST_STATE_PLAYING); + + + + + GStreamer will take care of the READY and PAUSED state for + you when going from NULL to PLAYING. + + + + + Since we do not use threads, nothing will happen yet. We have to + call gst_bin_iterate() to execute one iteration of the pipeline. + + + while (gst_bin_iterate (GST_BIN (pipeline))); + + + 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. + + + /* stop the pipeline */ + gst_element_set_state (pipeline, GST_STATE_NULL); + + gst_object_unref (GST_OBJECT (pipeline)); + + exit (0); + + + + Don't forget to set the state of the pipeline to NULL. This will free + all of the resources held by the elements. + + + +
+ + + Compiling helloworld.c + + To compile the helloworld example, use: + + + gcc -Wall `pkg-config gstreamer-&GST_MAJORMINOR; --cflags --libs` helloworld.c \ + -o helloworld + + + 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. + + + You can run the example with + (substitute helloworld.mp3 with you favorite MP3 file): + + + ./helloworld helloworld.mp3 + + + + + Conclusion + + 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. + + + 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. + + + 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. + + +
diff --git a/docs/manual/basics-init.xml b/docs/manual/basics-init.xml new file mode 100644 index 0000000000..054b7b5198 --- /dev/null +++ b/docs/manual/basics-init.xml @@ -0,0 +1,99 @@ + + Initializing <application>GStreamer</application> + + When writing a GStreamer application, you can + simply include gst/gst.h to get + access to the library functions. + + + Before the GStreamer libraries can be used, + gst_init 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. + + + A typical program + &EXAFOOT; + would have code to initialize GStreamer that + looks like this: + + + + + +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 */ +]]> + + + Use the GST_VERSION_MAJOR, + GST_VERSION_MINOR and GST_VERSION_MICRO + macros to get the GStreamer version you are + building against, or use the function gst_version + to get the version your application is linked against. + + + + It is also possible to call the gst_init function + with two NULL arguments, in which case no command line + options will be parsed by GStreamer. + + + The popt interface + +You can also use a popt table to initialize your own parameters as shown in the +next example: + + +/* 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 */ + + + As shown in this fragment, you can use a popt table to define your application-specific + command line options, and pass this table to the + function gst_init_with_popt_table. Your + application options will be parsed in addition to the standard + GStreamer options. + + + + diff --git a/docs/manual/basics-pads.xml b/docs/manual/basics-pads.xml new file mode 100644 index 0000000000..e7a44f6a2c --- /dev/null +++ b/docs/manual/basics-pads.xml @@ -0,0 +1,244 @@ + + Pads + + As we have seen in , the pads are the element's + interface to the outside world. + + + 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 + ) + + + + 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. + + 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. + + + + + Types of pad + + + Dynamic pads + + 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. + + + Running gst-inspect mpegdemux 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. + + + + Request pads + + 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. + + + 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. + + + + + + + Capabilities of a pad + + 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. + + + 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. + + + + Capabilities + + Capabilities are attached to a pad in order to describe + what type of media the pad can handle. + + + Capabilities is shorthand for "capability chain". A capability chain + is a chain of one capability or more. + + + 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". + + + 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 GstCaps + element. + + + + + Below is a dump of the capabilities of the element mad, as shown by + gst-inspect. + You can see two pads: sink and src. Both pads have capability information attached to them. + + + 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. + + + 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. + + +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 + + + + What are properties ? + + 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: + + + + + + basic types: + + + + + an integer value: the property has this exact value. + + + + + a boolean value: the property is either TRUE or FALSE. + + + + + 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. + + fourcc values consist of four bytes. + The FOURCC + Definition List is the most complete resource + on the allowed fourcc values. + + + + + + a float value: the property has this exact floating point value. + + + + + a string value. + + + + + + + + range types: + + + + + 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. + + + + + a float range value: the property denotes a range of possible + floating point values. + + + + + + + a list value: the property can take any value from a list of + basic value types or range types. + + + + + + + What capabilities are used for + + Capabilities describe in great detail the type of media that is handled by the pads. + They are mostly used for: + + + + + Autoplugging: automatically finding plugins for a set of capabilities + + + + + Compatibility detection: when two pads are linked, GStreamer + 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". + + + + + + diff --git a/docs/manual/basics-plugins.xml b/docs/manual/basics-plugins.xml new file mode 100644 index 0000000000..3189384766 --- /dev/null +++ b/docs/manual/basics-plugins.xml @@ -0,0 +1,31 @@ + + Plugins + + + A plugin is a shared library that contains at least one of the following + items: + + + + + + one or more element factories + + + + + one or more type definitions + + + + + one or more auto-pluggers + + + + + exported symbols for use in other plugins + + + + diff --git a/docs/manual/highlevel-components.xml b/docs/manual/highlevel-components.xml new file mode 100644 index 0000000000..2875d46d75 --- /dev/null +++ b/docs/manual/highlevel-components.xml @@ -0,0 +1,37 @@ + + Components + + + FIXME: This chapter is way out of date. + + + + GStreamer includes components that people can include + in their programs. + + + + GstPlay + + GstPlay is a GtkWidget with a simple API to play, pause and stop a media file. + + + + + + GstMediaPlay + + GstMediaPlay is a complete player widget. + + + + + + GstEditor + + GstEditor is a set of widgets to display a graphical representation of a + pipeline. + + + + diff --git a/docs/manual/highlevel-xml.xml b/docs/manual/highlevel-xml.xml new file mode 100644 index 0000000000..cd0104163f --- /dev/null +++ b/docs/manual/highlevel-xml.xml @@ -0,0 +1,283 @@ + + XML in <application>GStreamer</application> + + GStreamer 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 GStreamer knows about to have + quick access to the specifics of the plugins. + + + + We will show you how you can save a pipeline to XML and how you can reload that + XML file again for later use. + + + + Turning GstElements into XML + + + 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. + + + +/* 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 */ + + + The most important line is: + + + gst_xml_write_file (GST_ELEMENT (bin), stdout); + + + 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. + + + The complete element hierarchy will be saved along with the inter element + pad links and the element parameters. Future GStreamer + versions will also allow you to store the signals in the XML file. + + + + + Loading a GstElement from an XML file + + 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. + + +#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); +} + + + gst_xml_get_element (xml, "name") can be used to get a specific element + from the XML file. + + + gst_xml_get_topelements (xml) can be used to get a list of all toplevel elements + in the XML file. + + + 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. + + + + Adding custom XML tags into the core XML data + + + 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. + + + 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. + + + To insert a hook into the element saving procedure you can link + a signal to the GstElement using the following piece of code: + + +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")); + ... + + + When the thread is saved, the object_save method will be called. Our example + will insert a comment tag: + + +static void +object_saved (GstObject *object, xmlNodePtr parent, gpointer data) +{ + xmlNodePtr child; + + child = xmlNewChild (parent, ns, "comment", NULL); + xmlNewChild (child, ns, "text", (gchar *)data); +} + + + 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: + + + ... + <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> + ... + + + 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. + + + + We can extend our previous example with the following piece of + code. + + + + 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); + + + + Whenever a new object has been loaded, the xml_loaded function will + be called. This function looks like: + + +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; + } +} + + + 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. + + + + diff --git a/docs/manual/intro-motivation.xml b/docs/manual/intro-motivation.xml new file mode 100644 index 0000000000..f36b110a08 --- /dev/null +++ b/docs/manual/intro-motivation.xml @@ -0,0 +1,111 @@ + + Motivation + + Linux has historically lagged behind other operating systems in the multimedia + arena. Microsoft's Windows and Apple's MacOS 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. + + + + Current problems + + We describe the typical problems in today's media handling on Linux. + + + Multitude of duplicate code + + 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. + + + 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. + + + + + 'One goal' media players/libraries + + 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. + + + 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. + + + 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. + + + + + Non unified plugin mechanisms + + 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. + + + 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. + + + 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. + + + + + Provision for network transparency + + 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. + + + No provisions have been made for technologies such as + the GNOME object embedding using Bonobo. + + + 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. + + + + + Catch up with the <trademark>Windows</trademark> world + + We need solid media handling if we want to see Linux succeed on + the desktop. + + + We must clear the road for commercially backed codecs and multimedia + applications so that Linux can become an option for doing multimedia. + + + + diff --git a/docs/manual/intro-preface.xml b/docs/manual/intro-preface.xml new file mode 100644 index 0000000000..fd38af17f1 --- /dev/null +++ b/docs/manual/intro-preface.xml @@ -0,0 +1,59 @@ + + Introduction + + This chapter gives you an overview of the technologies described in this + book. + + + + What is GStreamer? + + 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. + + + + 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. + + + + 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. + + + + 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. + + + + 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. + + + + 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. + + + + diff --git a/gst/Makefile.am b/gst/Makefile.am index 644b780f3d..75425862d7 100644 --- a/gst/Makefile.am +++ b/gst/Makefile.am @@ -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 \ diff --git a/gst/elements/Makefile.am b/gst/elements/Makefile.am index dc18de4fc0..057cfedbe1 100644 --- a/gst/elements/Makefile.am +++ b/gst/elements/Makefile.am @@ -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) \ diff --git a/gst/elements/gstelements.c b/gst/elements/gstelements.c index af4bcb1f0b..6442e200d1 100644 --- a/gst/elements/gstelements.c +++ b/gst/elements/gstelements.c @@ -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 diff --git a/gst/elements/gstfakesink.c b/gst/elements/gstfakesink.c index 851b6f9375..76135d2593 100644 --- a/gst/elements/gstfakesink.c +++ b/gst/elements/gstfakesink.c @@ -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)); diff --git a/gst/elements/gstfakesrc.c b/gst/elements/gstfakesrc.c index 0b64860e90..570f0fe661 100644 --- a/gst/elements/gstfakesrc.c +++ b/gst/elements/gstfakesrc.c @@ -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; } diff --git a/gst/elements/gstfakesrc.h b/gst/elements/gstfakesrc.h index 5214f0bb7c..717898ccc8 100644 --- a/gst/elements/gstfakesrc.h +++ b/gst/elements/gstfakesrc.h @@ -79,6 +79,8 @@ struct _GstFakeSrc { gboolean loop_based; gboolean eos; + GstTask *task; + GstFakeSrcOutputType output; GstFakeSrcDataType data; GstFakeSrcSizeType sizetype; diff --git a/gst/elements/gstfilesrc.c b/gst/elements/gstfilesrc.c index 2a21eb6da0..5dca556b21 100644 --- a/gst/elements/gstfilesrc.c +++ b/gst/elements/gstfilesrc.c @@ -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 diff --git a/gst/elements/gstfilesrc.h b/gst/elements/gstfilesrc.h index 17230088c4..d69f0e8f4e 100644 --- a/gst/elements/gstfilesrc.h +++ b/gst/elements/gstfilesrc.h @@ -69,6 +69,8 @@ struct _GstFileSrc { gboolean is_regular; /* whether it's (symlink to) a regular file */ + GstTask *task; + GstBuffer *mapbuf; size_t mapsize; diff --git a/gst/elements/gstidentity.c b/gst/elements/gstidentity.c index 835697c209..12948950a8 100644 --- a/gst/elements/gstidentity.c +++ b/gst/elements/gstidentity.c @@ -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: diff --git a/gst/gst.c b/gst/gst.c index 65c8717dd6..d8a4c0cd2c 100644 --- a/gst/gst.c +++ b/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 diff --git a/gst/gst.h b/gst/gst.h index bbe8344836..11b2ba5972 100644 --- a/gst/gst.h +++ b/gst/gst.h @@ -42,7 +42,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -52,7 +54,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/gst/gst_private.h b/gst/gst_private.h index a892bd9c53..fa9f13fdcc 100644 --- a/gst/gst_private.h +++ b/gst/gst_private.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 diff --git a/gst/gstbin.c b/gst/gstbin.c index 75f49e85b4..5c5391043e 100644 --- a/gst/gstbin.c +++ b/gst/gstbin.c @@ -1,7 +1,7 @@ /* GStreamer * * Copyright (C) 1999,2000 Erik Walthinsen - * 2000 Wim Taymans + * 2004 Wim Taymans * * gstbin.c: GstBin container object and support code * @@ -46,33 +46,25 @@ GST_DEBUG_CATEGORY_STATIC (bin_debug); static GstElementDetails gst_bin_details = GST_ELEMENT_DETAILS ("Generic bin", "Generic/Bin", "Simple container object", - "Erik Walthinsen "); + "Erik Walthinsen ," "Wim Taymans "); GType _gst_bin_type = 0; -static gboolean _gst_boolean_did_something_accumulator (GSignalInvocationHint * - ihint, GValue * return_accu, const GValue * handler_return, gpointer dummy); - static void gst_bin_dispose (GObject * object); static GstElementStateReturn gst_bin_change_state (GstElement * element); -static GstElementStateReturn gst_bin_change_state_norecurse (GstBin * bin); +static gboolean gst_bin_get_state (GstElement * element, + GstElementState * state, GstElementState * pending, GTimeVal * timeout); #ifndef GST_DISABLE_INDEX static void gst_bin_set_index (GstElement * element, GstIndex * index); #endif +static void gst_bin_set_clock (GstElement * element, GstClock * clock); +static GstClock *gst_bin_get_clock (GstElement * element); static void gst_bin_add_func (GstBin * bin, GstElement * element); static void gst_bin_remove_func (GstBin * bin, GstElement * element); -static void gst_bin_child_state_change_func (GstBin * bin, - GstElementState oldstate, GstElementState newstate, GstElement * child); -GstElementStateReturn gst_bin_set_state (GstElement * element, - GstElementState state); - -static GstClock *gst_bin_get_clock_func (GstElement * element); -static void gst_bin_set_clock_func (GstElement * element, GstClock * clock); - -static gboolean gst_bin_iterate_func (GstBin * bin); +static GstIterator *gst_bin_iterate_elements_func (GstBin * bin); #ifndef GST_DISABLE_LOADSAVE static xmlNodePtr gst_bin_save_thyself (GstObject * object, xmlNodePtr parent); @@ -84,7 +76,6 @@ enum { ELEMENT_ADDED, ELEMENT_REMOVED, - ITERATE, LAST_SIGNAL }; @@ -161,11 +152,6 @@ gst_bin_class_init (GstBinClass * klass) g_signal_new ("element-removed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstBinClass, element_removed), NULL, NULL, gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_ELEMENT); - gst_bin_signals[ITERATE] = - g_signal_new ("iterate", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GstBinClass, iterate), - _gst_boolean_did_something_accumulator, NULL, gst_marshal_BOOLEAN__VOID, - G_TYPE_BOOLEAN, 0); gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_bin_dispose); @@ -176,41 +162,24 @@ gst_bin_class_init (GstBinClass * klass) #endif gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_bin_change_state); - gstelement_class->set_state = GST_DEBUG_FUNCPTR (gst_bin_set_state); + gstelement_class->get_state = GST_DEBUG_FUNCPTR (gst_bin_get_state); #ifndef GST_DISABLE_INDEX gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_bin_set_index); #endif + gstelement_class->get_clock = GST_DEBUG_FUNCPTR (gst_bin_get_clock); + gstelement_class->set_clock = GST_DEBUG_FUNCPTR (gst_bin_set_clock); klass->add_element = GST_DEBUG_FUNCPTR (gst_bin_add_func); klass->remove_element = GST_DEBUG_FUNCPTR (gst_bin_remove_func); - klass->child_state_change = - GST_DEBUG_FUNCPTR (gst_bin_child_state_change_func); - klass->iterate = GST_DEBUG_FUNCPTR (gst_bin_iterate_func); -} - -static gboolean -_gst_boolean_did_something_accumulator (GSignalInvocationHint * ihint, - GValue * return_accu, const GValue * handler_return, gpointer dummy) -{ - gboolean did_something; - - did_something = g_value_get_boolean (handler_return); - if (did_something) { - g_value_set_boolean (return_accu, TRUE); - } - - /* always continue emission */ - return TRUE; + klass->iterate_elements = GST_DEBUG_FUNCPTR (gst_bin_iterate_elements_func); } static void gst_bin_init (GstBin * bin) { - /* in general, we prefer to use cothreads for most things */ - GST_FLAG_SET (bin, GST_BIN_FLAG_PREFER_COTHREADS); - bin->numchildren = 0; bin->children = NULL; + bin->children_cookie = 0; } /** @@ -227,252 +196,92 @@ gst_bin_new (const gchar * name) return gst_element_factory_make ("bin", name); } -static GstClock * -gst_bin_get_clock_func (GstElement * element) -{ - if (GST_ELEMENT_SCHED (element)) - return gst_scheduler_get_clock (GST_ELEMENT_SCHED (element)); - - return NULL; -} - -static void -gst_bin_set_clock_func (GstElement * element, GstClock * clock) -{ - if (GST_ELEMENT_SCHED (element)) - gst_scheduler_use_clock (GST_ELEMENT_SCHED (element), clock); -} - -/** - * gst_bin_get_clock: - * @bin: a #GstBin to get the clock of - * - * Gets the current clock of the (scheduler of the) bin. - * - * Returns: the #GstClock of the bin - */ -GstClock * -gst_bin_get_clock (GstBin * bin) -{ - g_return_val_if_fail (bin != NULL, NULL); - g_return_val_if_fail (GST_IS_BIN (bin), NULL); - - return gst_bin_get_clock_func (GST_ELEMENT (bin)); -} - -/** - * gst_bin_use_clock: - * @bin: the bin to set the clock for - * @clock: the clock to use. - * - * Force the bin to use the given clock. Use NULL to - * force it to use no clock at all. - */ -void -gst_bin_use_clock (GstBin * bin, GstClock * clock) -{ - g_return_if_fail (GST_IS_BIN (bin)); - - gst_bin_set_clock_func (GST_ELEMENT (bin), clock); -} - -/** - * gst_bin_auto_clock: - * @bin: the bin to autoclock - * - * Let the bin select a clock automatically. - */ -void -gst_bin_auto_clock (GstBin * bin) -{ - g_return_if_fail (GST_IS_BIN (bin)); - - if (GST_ELEMENT_SCHED (bin)) - gst_scheduler_auto_clock (GST_ELEMENT_SCHED (bin)); -} - #ifndef GST_DISABLE_INDEX +static void +bin_set_index_func (GstElement * element, GstIndex * index) +{ + gst_element_set_index (element, index); + gst_object_unref (GST_OBJECT (element)); +} + static void gst_bin_set_index (GstElement * element, GstIndex * index) { - GstBin *bin = GST_BIN (element); + GstIterator *it = gst_bin_iterate_elements (GST_BIN (element)); - g_return_if_fail (GST_IS_BIN (bin)); - - g_list_foreach (bin->children, (GFunc) gst_element_set_index, index); + gst_iterator_foreach (it, (GFunc) bin_set_index_func, index); + gst_iterator_free (it); } #endif static void -gst_bin_set_element_sched (GstElement * element, GstScheduler * sched) +bin_set_clock_func (GstElement * element, GstClock * clock) { - GST_CAT_LOG (GST_CAT_SCHEDULING, "setting element \"%s\" sched to %p", - GST_ELEMENT_NAME (element), sched); - - /* if it's actually a Bin */ - if (GST_IS_BIN (element)) { - if (GST_FLAG_IS_SET (element, GST_BIN_FLAG_MANAGER)) { - GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, element, - "child is already a manager, not resetting sched"); - if (GST_ELEMENT_SCHED (element)) - gst_scheduler_add_scheduler (sched, GST_ELEMENT_SCHED (element)); - return; - } - - GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, element, - "setting child bin's scheduler to be the same as the parent's"); - gst_scheduler_add_element (sched, element); - - /* set the children's schedule */ - g_list_foreach (GST_BIN (element)->children, - (GFunc) gst_bin_set_element_sched, sched); - } else { - /* otherwise, if it's just a regular old element */ - GList *pads; - - gst_scheduler_add_element (sched, element); - - if (!GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED)) { - /* set the sched pointer in all the pads */ - pads = element->pads; - while (pads) { - GstPad *pad; - - pad = GST_PAD (pads->data); - pads = g_list_next (pads); - - /* we only operate on real pads */ - if (!GST_IS_REAL_PAD (pad)) - continue; - - /* if the peer element exists and is a candidate */ - if (GST_PAD_PEER (pad)) { - if (gst_pad_get_scheduler (GST_PAD_PEER (pad)) == sched) { - GST_CAT_LOG (GST_CAT_SCHEDULING, - "peer is in same scheduler, telling scheduler"); - - if (GST_PAD_IS_SRC (pad)) - gst_scheduler_pad_link (sched, pad, GST_PAD_PEER (pad)); - else - gst_scheduler_pad_link (sched, GST_PAD_PEER (pad), pad); - } - } - } - } - } + gst_element_set_clock (element, clock); + gst_object_unref (GST_OBJECT (element)); } static void -gst_bin_unset_element_sched (GstElement * element, GstScheduler * sched) +gst_bin_set_clock (GstElement * element, GstClock * clock) { - if (GST_ELEMENT_SCHED (element) == NULL) { - GST_CAT_DEBUG (GST_CAT_SCHEDULING, "element \"%s\" has no scheduler", - GST_ELEMENT_NAME (element)); - return; - } + GstIterator *it = gst_bin_iterate_elements (GST_BIN (element)); - GST_CAT_DEBUG (GST_CAT_SCHEDULING, - "removing element \"%s\" from its sched %p", GST_ELEMENT_NAME (element), - GST_ELEMENT_SCHED (element)); - - /* if it's actually a Bin */ - if (GST_IS_BIN (element)) { - - if (GST_FLAG_IS_SET (element, GST_BIN_FLAG_MANAGER)) { - GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, element, - "child is already a manager, not unsetting sched"); - if (sched) { - gst_scheduler_remove_scheduler (sched, GST_ELEMENT_SCHED (element)); - } - return; - } - /* for each child, remove them from their schedule */ - g_list_foreach (GST_BIN (element)->children, - (GFunc) gst_bin_unset_element_sched, sched); - - gst_scheduler_remove_element (GST_ELEMENT_SCHED (element), element); - } else { - /* otherwise, if it's just a regular old element */ - GList *pads; - - if (!GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED)) { - /* unset the sched pointer in all the pads */ - pads = element->pads; - while (pads) { - GstPad *pad; - - pad = GST_PAD (pads->data); - pads = g_list_next (pads); - - /* we only operate on real pads */ - if (!GST_IS_REAL_PAD (pad)) - continue; - - /* if the peer element exists and is a candidate */ - if (GST_PAD_PEER (pad)) { - if (gst_pad_get_scheduler (GST_PAD_PEER (pad)) == sched) { - GST_CAT_LOG (GST_CAT_SCHEDULING, - "peer is in same scheduler, telling scheduler"); - - if (GST_PAD_IS_SRC (pad)) - gst_scheduler_pad_unlink (sched, pad, GST_PAD_PEER (pad)); - else - gst_scheduler_pad_unlink (sched, GST_PAD_PEER (pad), pad); - } - } - } - } - - gst_scheduler_remove_element (GST_ELEMENT_SCHED (element), element); - } + gst_iterator_foreach (it, (GFunc) bin_set_clock_func, clock); + gst_iterator_free (it); } - -/** - * 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, ...) +static gint +bin_get_clock_func (GstElement * element, GstClock ** clock) { - va_list args; + GstClock *result; - 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 *); + result = gst_element_get_clock (element); + if (result) { + *clock = result; + return 0; /* found */ } - va_end (args); + gst_object_unref (GST_OBJECT (element)); + return 1; +} + +static GstClock * +gst_bin_get_clock (GstElement * element) +{ + GstIterator *children; + GstElement *found; + GstClock *result = NULL; + + children = gst_bin_iterate_elements (GST_BIN (element)); + found = (GstElement *) gst_iterator_find_custom (children, &result, + (GCompareFunc) bin_get_clock_func); + gst_iterator_free (children); + if (found) { + gst_object_unref (GST_OBJECT (found)); + } + return result; } static void gst_bin_add_func (GstBin * bin, GstElement * element) { - gint state_idx = 0; - GstElementState state; - GstScheduler *sched; + GstPipeline *manager; /* the element must not already have a parent */ g_return_if_fail (GST_ELEMENT_PARENT (element) == NULL); - /* then check to see if the element's name is already taken in the bin */ + GST_LOCK (bin); + /* then check to see if the element's name is already taken in the bin, + * we can sefely take the lock here. */ if (gst_object_check_uniqueness (bin->children, GST_ELEMENT_NAME (element)) == FALSE) { g_warning ("Name %s is not unique in bin %s, not adding\n", GST_ELEMENT_NAME (element), GST_ELEMENT_NAME (bin)); + GST_UNLOCK (bin); return; } + manager = GST_ELEMENT (bin)->manager; + gst_element_set_manager (element, manager); if (GST_STATE (element) > GST_STATE (bin)) { GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin, @@ -483,26 +292,15 @@ gst_bin_add_func (GstBin * bin, GstElement * element) /* set the element's parent and add the element to the bin's list of children */ gst_object_set_parent (GST_OBJECT (element), GST_OBJECT (bin)); - bin->children = g_list_append (bin->children, element); + bin->children = g_list_prepend (bin->children, element); bin->numchildren++; - - /* bump our internal state counter */ - state = GST_STATE (element); - while ((state >>= 1) != 0) - state_idx++; - bin->child_states[state_idx]++; - - /* now we have to deal with manager stuff - * we can only do this if there's a scheduler: - * if we're not a manager, and aren't attached to anything, we have no sched (yet) */ - sched = GST_ELEMENT_SCHED (bin); - if (sched) { - gst_bin_set_element_sched (element, sched); - } + bin->children_cookie++; GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, bin, "added element \"%s\"", GST_OBJECT_NAME (element)); + GST_UNLOCK (bin); + g_signal_emit (G_OBJECT (bin), gst_bin_signals[ELEMENT_ADDED], 0, element); } @@ -530,61 +328,43 @@ gst_bin_add (GstBin * bin, GstElement * element) if (bclass->add_element) { bclass->add_element (bin, element); } else { - GST_ELEMENT_ERROR (bin, CORE, FAILED, (NULL), - ("cannot add element %s to bin %s", - GST_ELEMENT_NAME (element), GST_ELEMENT_NAME (bin))); + g_warning ("cannot add element %s to bin %s", + GST_ELEMENT_NAME (element), GST_ELEMENT_NAME (bin)); } } static void gst_bin_remove_func (GstBin * bin, GstElement * element) { - gint state_idx = 0; - GstElementState state; - /* the element must have its parent set to the current bin */ g_return_if_fail (GST_ELEMENT_PARENT (element) == (GstObject *) bin); + GST_LOCK (bin); + /* the element must be in the bin's list of children */ if (g_list_find (bin->children, element) == NULL) { g_warning ("no element \"%s\" in bin \"%s\"\n", GST_ELEMENT_NAME (element), GST_ELEMENT_NAME (bin)); + GST_UNLOCK (bin); return; } - /* remove this element from the list of managed elements */ - gst_bin_unset_element_sched (element, GST_ELEMENT_SCHED (bin)); - /* now remove the element from the list of elements */ bin->children = g_list_remove (bin->children, element); bin->numchildren--; - - /* bump our internal state counter */ - state = GST_STATE (element); - while ((state >>= 1) != 0) - state_idx++; - bin->child_states[state_idx]--; + bin->children_cookie++; GST_CAT_INFO_OBJECT (GST_CAT_PARENTAGE, bin, "removed child \"%s\"", GST_OBJECT_NAME (element)); + GST_UNLOCK (bin); + + gst_element_set_manager (element, NULL); + /* ref as we're going to emit a signal */ gst_object_ref (GST_OBJECT (element)); gst_object_unparent (GST_OBJECT (element)); - /* if we're down to zero children, force state to NULL */ - while (bin->numchildren == 0 && GST_ELEMENT_SCHED (bin) != NULL && - GST_STATE (bin) > GST_STATE_NULL) { - GstElementState next = GST_STATE (bin) >> 1; - - GST_STATE_PENDING (bin) = next; - gst_bin_change_state_norecurse (bin); - if (!GST_STATE (bin) == next) { - g_warning ("bin %s failed state change to %d", GST_ELEMENT_NAME (bin), - next); - break; - } - } g_signal_emit (G_OBJECT (bin), gst_bin_signals[ELEMENT_REMOVED], 0, element); /* element is really out of our control now */ @@ -623,216 +403,228 @@ gst_bin_remove (GstBin * bin, GstElement * element) } } -/** - * 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, ...) +typedef struct _GstBinIterator { - va_list args; + GstIterator iterator; + GstBin *bin; + GList *list; /* pointer in list */ +} GstBinIterator; - 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); -} - -/** - * gst_bin_child_state_change: - * @bin: #GstBin with the child - * @oldstate: The old child state - * @newstate: The new child state - * @child: #GstElement that signaled an changed state - * - * An internal function to inform the parent bin about a state change - * of a child. - */ -void -gst_bin_child_state_change (GstBin * bin, GstElementState oldstate, - GstElementState newstate, GstElement * child) +static GstIteratorResult +gst_bin_iterator_next (GstBinIterator * it, GstElement ** elem) { - GstBinClass *bclass; + if (it->list == NULL) + return GST_ITERATOR_DONE; - g_return_if_fail (GST_IS_BIN (bin)); - g_return_if_fail (GST_IS_ELEMENT (child)); + *elem = GST_ELEMENT (it->list->data); + gst_object_ref (GST_OBJECT (*elem)); - GST_CAT_LOG (GST_CAT_STATES, "child %s changed state in bin %s from %s to %s", - GST_ELEMENT_NAME (child), GST_ELEMENT_NAME (bin), - gst_element_state_get_name (oldstate), - gst_element_state_get_name (newstate)); + it->list = g_list_next (it->list); - bclass = GST_BIN_GET_CLASS (bin); - - if (bclass->child_state_change) { - bclass->child_state_change (bin, oldstate, newstate, child); - } else { - g_warning ("cannot signal state change of child %s to bin %s\n", - GST_ELEMENT_NAME (child), GST_ELEMENT_NAME (bin)); - } + return GST_ITERATOR_OK; } static void -gst_bin_child_state_change_func (GstBin * bin, GstElementState oldstate, - GstElementState newstate, GstElement * child) +gst_bin_iterator_resync (GstBinIterator * it) { - GstElementState old = 0, new = 0; - gint old_idx = 0, new_idx = 0, i; + it->list = it->bin->children; +} - old = oldstate; - new = newstate; - while ((old >>= 1) != 0) - old_idx++; - while ((new >>= 1) != 0) - new_idx++; +static void +gst_bin_iterator_free (GstBinIterator * it) +{ + gst_object_unref (GST_OBJECT (it->bin)); + g_free (it); +} + +static GstIterator * +gst_bin_iterate_elements_func (GstBin * bin) +{ + GstBinIterator *result; GST_LOCK (bin); - GST_LOG_BIN_CONTENTS (bin, "before child state change"); - bin->child_states[old_idx]--; - bin->child_states[new_idx]++; + result = (GstBinIterator *) gst_iterator_new (sizeof (GstBinIterator), + GST_GET_LOCK (bin), + &bin->children_cookie, + (GstIteratorNextFunction) gst_bin_iterator_next, + (GstIteratorResyncFunction) gst_bin_iterator_resync, + (GstIteratorFreeFunction) gst_bin_iterator_free); - for (i = GST_NUM_STATES - 1; i >= 0; i--) { - if (bin->child_states[i] != 0) { - gint state = (1 << i); - - if (GST_STATE (bin) != state) { - GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin, - "highest child state is %s, changing bin state accordingly", - gst_element_state_get_name (state)); - GST_STATE_PENDING (bin) = state; - GST_UNLOCK (bin); - gst_bin_change_state_norecurse (bin); - if (state != GST_STATE (bin)) { - g_warning ("%s: state change in callback %d %d", - GST_ELEMENT_NAME (bin), state, GST_STATE (bin)); - } - GST_LOG_BIN_CONTENTS (bin, "after child state change"); - return; - } - break; - } - } - GST_LOG_BIN_CONTENTS (bin, "after child state change"); + result->bin = GST_BIN (gst_object_ref (GST_OBJECT (bin))); + result->list = bin->children; GST_UNLOCK (bin); + + return GST_ITERATOR (result); } -typedef gboolean (*GstBinForeachFunc) (GstBin * bin, GstElement * element, - gpointer data); - -/* - * gst_bin_foreach: - * @bin: bin to traverse - * @func: function to call on each child - * @data: user data handed to each function call +/** + * gst_bin_iterate_elements: + * @bin: #Gstbin to iterate the elements of * - * Calls @func on each child of the bin. If @func returns FALSE, - * gst_bin_foreach() immediately returns. - * It is assumed that calling @func may alter the set of @bin's children. @func - * will only be called on children that were in @bin when gst_bin_foreach() was - * called, and that are still in @bin when the child is reached. + * Get an iterator for the elements in this bin. + * Each element will have its refcount increased, so unref + * after usage. * - * Returns: TRUE if @func always returned TRUE, FALSE otherwise - **/ -static gboolean -gst_bin_foreach (GstBin * bin, GstBinForeachFunc func, gpointer data) + * Returns: a #GstIterator of #GstElements. gst_iterator_free after + * use. + */ +GstIterator * +gst_bin_iterate_elements (GstBin * bin) { - GList *kids, *walk; + GstBinClass *bclass; + GstIterator *result; - g_return_val_if_fail (GST_IS_BIN (bin), FALSE); - g_return_val_if_fail (func != NULL, FALSE); + g_return_val_if_fail (GST_IS_BIN (bin), NULL); - kids = g_list_copy (bin->children); + bclass = GST_BIN_GET_CLASS (bin); - for (walk = kids; walk; walk = g_list_next (walk)) { - GstElement *element = (GstElement *) walk->data; + if (bclass->iterate_elements) { + result = bclass->iterate_elements (bin); + } else { + g_warning ("cannot iterate elements of bin %s", GST_ELEMENT_NAME (bin)); + result = NULL; + } + return result; +} - if (g_list_find (bin->children, element)) { - gboolean res = func (bin, element, data); +/* returns 0 if the element is a sink, this is made so that + * we can use this function as a filter */ +static gint +bin_element_is_sink (GstElement * child, GstBin * bin) +{ + gint ret = 1; - if (!res) { - g_list_free (kids); - return FALSE; + /* check if this is a sink element, these are the elements + * without (linked) source pads. */ + if (child->numsrcpads == 0) { + /* shortcut */ + GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin, + "adding child %s as sink", gst_element_get_name (child)); + ret = 0; + } else { + /* loop over all pads, try to figure out if this element + * is a sink because it has no linked source pads */ + GList *pads; + gboolean connected_src = FALSE; + + for (pads = child->srcpads; pads; pads = g_list_next (pads)) { + GstPad *pad = GST_PAD (pads->data); + + if (GST_PAD_IS_LINKED (pad)) { + connected_src = TRUE; + break; } } + if (connected_src) { + GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin, + "not adding child %s as sink: linked source pads", + gst_element_get_name (child)); + } else { + GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin, + "adding child %s as sink since it has unlinked source pads", + gst_element_get_name (child)); + ret = 0; + } } + /* we did not find the element, need to release the ref + * added by the iterator */ + if (ret == 1) + gst_object_unref (GST_OBJECT (child)); - g_list_free (kids); - return TRUE; + return ret; } -typedef struct +/** + * gst_bin_iterate_sinks: + * @bin: #Gstbin to iterate on + * + * Get an iterator for the sink elements in this bin. + * Each element will have its refcount increased, so unref + * after usage. + * + * Returns: a #GstIterator of #GstElements. gst_iterator_free after use. + */ +GstIterator * +gst_bin_iterate_sinks (GstBin * bin) { - GstElementState pending; - GstElementStateReturn result; + GstIterator *children; + GstIterator *result; + + g_return_val_if_fail (GST_IS_BIN (bin), NULL); + + children = gst_bin_iterate_elements (bin); + result = gst_iterator_filter (children, bin, + (GCompareFunc) bin_element_is_sink); + + return result; } -SetKidStateData; -static int -set_kid_state_func (GstBin * bin, GstElement * child, gpointer user_data) + +static gint +bin_find_pending_child (GstElement * child, GTimeVal * timeout) { - GstElementState old_child_state; - SetKidStateData *data = user_data; + gboolean ret; - if (GST_FLAG_IS_SET (child, GST_ELEMENT_LOCKED_STATE)) { - return TRUE; - } + ret = gst_element_get_state (GST_ELEMENT (child), NULL, NULL, timeout); + /* ret is false if some child is still performing the state change */ + gst_object_unref (GST_OBJECT (child)); - old_child_state = GST_STATE (child); - - GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin, - "changing state of child %s from current %s to pending %s", - GST_ELEMENT_NAME (child), gst_element_state_get_name (old_child_state), - gst_element_state_get_name (data->pending)); - - switch (gst_element_set_state (child, data->pending)) { - case GST_STATE_FAILURE: - GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin, - "child '%s' failed to go to state %d(%s)", - GST_ELEMENT_NAME (child), - data->pending, gst_element_state_get_name (data->pending)); - - gst_element_set_state (child, old_child_state); - return FALSE; /* error out to the caller */ - - case GST_STATE_ASYNC: - GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin, - "child '%s' is changing state asynchronously", - GST_ELEMENT_NAME (child)); - data->result = GST_STATE_ASYNC; - return TRUE; - - case GST_STATE_SUCCESS: - GST_CAT_DEBUG (GST_CAT_STATES, - "child '%s' changed state to %d(%s) successfully", - GST_ELEMENT_NAME (child), data->pending, - gst_element_state_get_name (data->pending)); - return TRUE; - - default: - g_assert_not_reached (); - return FALSE; /* satisfy gcc */ - } + return (ret == FALSE ? 0 : 1); } +/* this functions loops over all children, as soon as one is + * still performing the state change, FALSE is returned. */ +static gboolean +gst_bin_get_state (GstElement * element, GstElementState * state, + GstElementState * pending, GTimeVal * timeout) +{ + gboolean ret = TRUE; + GstBin *bin = GST_BIN (element); + GstIterator *children; + gboolean have_async = FALSE; + gpointer child; + + /* we cannot take the state lock yet as we might block when querying + * the children, holding the lock too long for no reason */ + children = gst_bin_iterate_sinks (bin); + child = gst_iterator_find_custom (children, timeout, + (GCompareFunc) bin_find_pending_child); + gst_iterator_free (children); + /* we unreffed the child in the comparefunc */ + if (child) { + have_async = TRUE; + ret = FALSE; + } + + /* now we can take the state lock */ + GST_STATE_LOCK (bin); + if (!have_async) { + /* no async children, we can commit the state */ + gst_element_commit_state (element); + } + + /* and report the state */ + if (state) + *state = GST_STATE (element); + if (pending) + *pending = GST_STATE_PENDING (element); + + GST_STATE_UNLOCK (bin); + + return ret; +} + +/* this function is called with the STATE_LOCK held */ static GstElementStateReturn gst_bin_change_state (GstElement * element) { GstBin *bin; GstElementStateReturn ret; GstElementState old_state, pending; - SetKidStateData data; + gboolean have_async = FALSE; + GstIterator *sinks; + gboolean done = FALSE; + + GQueue *elem_queue; /* list of elements waiting for a state change */ g_return_val_if_fail (GST_IS_BIN (element), GST_STATE_FAILURE); @@ -849,11 +641,119 @@ gst_bin_change_state (GstElement * element) if (pending == GST_STATE_VOID_PENDING) return GST_STATE_SUCCESS; - data.pending = pending; - data.result = GST_STATE_SUCCESS; - if (!gst_bin_foreach (bin, set_kid_state_func, &data)) { - GST_STATE_PENDING (element) = old_state; - return GST_STATE_FAILURE; + elem_queue = g_queue_new (); + + /* first step, find all sink elements, these are the elements + * without (linked) source pads. */ + sinks = gst_bin_iterate_sinks (bin); + while (!done) { + gpointer child; + + switch (gst_iterator_next (sinks, &child)) { + case GST_ITERATOR_OK: + /* this also keeps the refcount on the element */ + g_queue_push_tail (elem_queue, child); + break; + case GST_ITERATOR_RESYNC: + /* undo what we had */ + g_queue_foreach (elem_queue, (GFunc) gst_object_unref, NULL); + while (g_queue_pop_head (elem_queue)); + gst_iterator_resync (sinks); + break; + case GST_ITERATOR_DONE: + done = TRUE; + break; + default: + g_assert_not_reached (); + break; + } + } + gst_iterator_free (sinks); + + /* second step, change state of elements in the queue */ + while (!g_queue_is_empty (elem_queue)) { + GstElement *qelement = g_queue_pop_head (elem_queue); + GList *pads; + + /* queue all elements connected to the sinkpads of this element */ + /* FIXME, not MT safe !! */ + for (pads = qelement->sinkpads; pads; pads = g_list_next (pads)) { + GstPad *pad = GST_PAD (pads->data); + GstPad *peer; + + GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, + "found sinkpad %s:%s", GST_DEBUG_PAD_NAME (pad)); + + peer = gst_pad_get_peer (pad); + if (peer) { + GstElement *peer_elem; + + /* FIXME does not work for bins etc */ + peer_elem = GST_ELEMENT (gst_object_get_parent (GST_OBJECT (peer))); + + GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, + "adding element %s to queue", gst_element_get_name (peer_elem)); + + /* ref before pushing on the queue */ + gst_object_ref (GST_OBJECT (peer_elem)); + g_queue_push_tail (elem_queue, peer_elem); + } else { + GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, + "pad %s:%s does not have a peer", GST_DEBUG_PAD_NAME (pad)); + } + } + + if (GST_FLAG_IS_SET (qelement, GST_ELEMENT_LOCKED_STATE)) { + gst_object_unref (GST_OBJECT (qelement)); + continue; + } + + /* FIXME handle delayed elements like src and loop based + * elements */ + ret = gst_element_set_state (qelement, pending); + switch (ret) { + case GST_STATE_SUCCESS: + GST_CAT_DEBUG (GST_CAT_STATES, + "child '%s' changed state to %d(%s) successfully", + GST_ELEMENT_NAME (qelement), pending, + gst_element_state_get_name (pending)); + break; + case GST_STATE_ASYNC: + GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, + "child '%s' is changing state asynchronously", + GST_ELEMENT_NAME (qelement)); + have_async = TRUE; + break; + case GST_STATE_FAILURE: + GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, + "child '%s' failed to go to state %d(%s)", + GST_ELEMENT_NAME (qelement), + pending, gst_element_state_get_name (pending)); + ret = GST_STATE_FAILURE; + /* release refcounts in queue */ + g_queue_foreach (elem_queue, (GFunc) gst_object_unref, NULL); + /* release refcount of element we popped off the queue */ + gst_object_unref (GST_OBJECT (qelement)); + goto exit; + default: + g_assert_not_reached (); + break; + } + gst_object_unref (GST_OBJECT (qelement)); + } + + if (have_async) { + ret = GST_STATE_ASYNC; + } else { + if (parent_class->change_state) { + ret = parent_class->change_state (element); + } else { + ret = GST_STATE_SUCCESS; + } + if (ret == GST_STATE_SUCCESS) { + /* we can commit the state change now */ + gst_element_commit_state (element); + } } GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, @@ -862,52 +762,12 @@ gst_bin_change_state (GstElement * element) gst_element_state_get_name (pending), gst_element_state_get_name (GST_STATE (element))); - if (data.result == GST_STATE_ASYNC) - ret = GST_STATE_ASYNC; - else { - /* FIXME: this should have been done by the children already, no? */ - if (parent_class->change_state) { - ret = parent_class->change_state (element); - } else - ret = GST_STATE_SUCCESS; - } +exit: + g_queue_free (elem_queue); + return ret; } -GstElementStateReturn -gst_bin_set_state (GstElement * element, GstElementState state) -{ - GstBin *bin = GST_BIN (element); - - if (GST_STATE (bin) == state) { - SetKidStateData data; - - data.pending = state; - data.result = GST_STATE_SUCCESS; - if (!gst_bin_foreach (bin, set_kid_state_func, &data)) { - return GST_STATE_FAILURE; - } else { - return data.result; - } - } else { - return GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS, set_state, (element, - state), GST_STATE_FAILURE); - } -} - -static GstElementStateReturn -gst_bin_change_state_norecurse (GstBin * bin) -{ - GstElementStateReturn ret; - - if (parent_class->change_state) { - GST_CAT_LOG_OBJECT (GST_CAT_STATES, bin, "setting bin's own state"); - ret = parent_class->change_state (GST_ELEMENT (bin)); - - return ret; - } else - return GST_STATE_FAILURE; -} static void gst_bin_dispose (GObject * object) @@ -921,6 +781,7 @@ gst_bin_dispose (GObject * object) while (bin->children) { gst_bin_remove (bin, GST_ELEMENT (bin->children->data)); } + GST_CAT_DEBUG_OBJECT (GST_CAT_REFCOUNTING, object, "dispose no children"); g_assert (bin->children == NULL); g_assert (bin->numchildren == 0); @@ -932,38 +793,46 @@ gst_bin_dispose (GObject * object) * @bin: #Gstbin to search * @name: the element name to search for * - * Get the element with the given name from this bin. + * Get the element with the given name from this bin. This + * function recurses into subbins. * * Returns: the element with the given name */ GstElement * gst_bin_get_by_name (GstBin * bin, const gchar * name) { - GList *children; - GstElement *child; + const GList *children; + GstElement *result = NULL; - g_return_val_if_fail (bin != NULL, NULL); - g_return_val_if_fail (GST_IS_BIN (bin), NULL); - g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (bin != NULL, result); + g_return_val_if_fail (GST_IS_BIN (bin), result); + g_return_val_if_fail (name != NULL, result); GST_CAT_INFO (GST_CAT_PARENTAGE, "[%s]: looking up child element %s", GST_ELEMENT_NAME (bin), name); + /* FIXME, does not use the virtual method to get + * the children */ + GST_LOCK (bin); children = bin->children; while (children) { - child = GST_ELEMENT (children->data); - if (!strcmp (GST_OBJECT_NAME (child), name)) - return child; - if (GST_IS_BIN (child)) { - GstElement *res = gst_bin_get_by_name (GST_BIN (child), name); + GstElement *child = GST_ELEMENT (children->data); - if (res) - return res; + if (!strcmp (GST_OBJECT_NAME (child), name)) { + result = child; + break; + } + if (GST_IS_BIN (child)) { + result = gst_bin_get_by_name (GST_BIN (child), name); + if (result) { + break; + } } children = g_list_next (children); } + GST_UNLOCK (bin); - return NULL; + return result; } /** @@ -999,22 +868,6 @@ gst_bin_get_by_name_recurse_up (GstBin * bin, const gchar * name) return result; } -/** - * gst_bin_get_list: - * @bin: #Gstbin to get the list from - * - * Get the list of elements in this bin. - * - * Returns: a GList of elements - */ -const GList * -gst_bin_get_list (GstBin * bin) -{ - g_return_val_if_fail (GST_IS_BIN (bin), NULL); - - return bin->children; -} - /** * gst_bin_get_by_interface: * @bin: bin to find element in @@ -1031,26 +884,31 @@ gst_bin_get_list (GstBin * bin) GstElement * gst_bin_get_by_interface (GstBin * bin, GType interface) { - GList *walk; + const GList *walk; + GstElement *result = NULL; g_return_val_if_fail (GST_IS_BIN (bin), NULL); g_return_val_if_fail (G_TYPE_IS_INTERFACE (interface), NULL); + /* FIXME, does not use the virtual method to get + * the children */ + GST_LOCK (bin); walk = bin->children; while (walk) { - if (G_TYPE_CHECK_INSTANCE_TYPE (walk->data, interface)) - return GST_ELEMENT (walk->data); + if (G_TYPE_CHECK_INSTANCE_TYPE (walk->data, interface)) { + result = GST_ELEMENT (walk->data); + break; + } if (GST_IS_BIN (walk->data)) { - GstElement *ret; - - ret = gst_bin_get_by_interface (GST_BIN (walk->data), interface); - if (ret) - return ret; + result = gst_bin_get_by_interface (GST_BIN (walk->data), interface); + if (result) + break; } walk = g_list_next (walk); } + GST_UNLOCK (bin); - return NULL; + return result; } /** @@ -1063,16 +921,19 @@ gst_bin_get_by_interface (GstBin * bin, GType interface) * The function recurses bins inside bins. You need to free the list using * g_list_free() after use. * - * Returns: An element inside the bin implementing the interface. + * Returns: The elements inside the bin implementing the interface. */ GList * gst_bin_get_all_by_interface (GstBin * bin, GType interface) { - GList *walk, *ret = NULL; + const GList *walk; + GList *ret = NULL; g_return_val_if_fail (GST_IS_BIN (bin), NULL); g_return_val_if_fail (G_TYPE_IS_INTERFACE (interface), NULL); + /* FIXME, not MT safe and does not use the virtual method to get + * the children */ walk = bin->children; while (walk) { if (G_TYPE_CHECK_INSTANCE_TYPE (walk->data, interface)) { @@ -1090,58 +951,6 @@ gst_bin_get_all_by_interface (GstBin * bin, GType interface) return ret; } -/** - * gst_bin_sync_children_state: - * @bin: #Gstbin to sync state - * - * Tries to set the state of the children of this bin to the same state of the - * bin by calling gst_element_set_state for each child not already having a - * synchronized state. - * - * Returns: The worst return value of any gst_element_set_state. So if one child - * returns #GST_STATE_FAILURE while all others return #GST_STATE_SUCCESS - * this function returns #GST_STATE_FAILURE. - */ -GstElementStateReturn -gst_bin_sync_children_state (GstBin * bin) -{ - GList *children; - GstElement *element; - GstElementState state; - GstElementStateReturn ret = GST_STATE_SUCCESS; - - g_return_val_if_fail (GST_IS_BIN (bin), GST_STATE_FAILURE); - - state = GST_STATE (bin); - children = bin->children; - GST_CAT_INFO (GST_CAT_STATES, - "syncing state of children with bin \"%s\"'s state %s", - GST_ELEMENT_NAME (bin), gst_element_state_get_name (state)); - - while (children) { - element = GST_ELEMENT (children->data); - children = children->next; - if (GST_STATE (element) != state) { - switch (gst_element_set_state (element, state)) { - case GST_STATE_SUCCESS: - break; - case GST_STATE_ASYNC: - if (ret == GST_STATE_SUCCESS) - ret = GST_STATE_ASYNC; - break; - case GST_STATE_FAILURE: - ret = GST_STATE_FAILURE; - break; - default: - /* make sure gst_element_set_state never returns this */ - g_assert_not_reached (); - } - } - } - - return ret; -} - #ifndef GST_DISABLE_LOADSAVE static xmlNodePtr gst_bin_save_thyself (GstObject * object, xmlNodePtr parent) @@ -1203,73 +1012,3 @@ gst_bin_restore_thyself (GstObject * object, xmlNodePtr self) (GST_OBJECT_CLASS (parent_class)->restore_thyself) (object, self); } #endif /* GST_DISABLE_LOADSAVE */ - -static gboolean -gst_bin_iterate_func (GstBin * bin) -{ - GstScheduler *sched = GST_ELEMENT_SCHED (bin); - - /* only iterate if this is the manager bin */ - if (sched && sched->parent == GST_ELEMENT (bin)) { - GstSchedulerState state; - - state = gst_scheduler_iterate (sched); - - if (state == GST_SCHEDULER_STATE_RUNNING) { - return TRUE; - } else if (state == GST_SCHEDULER_STATE_ERROR) { - gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PAUSED); - } else if (state == GST_SCHEDULER_STATE_STOPPED) { - /* check if we have children scheds that are still running */ - /* FIXME: remove in 0.9? autouseless because iterations gone? */ - GList *walk; - - for (walk = sched->schedulers; walk; walk = g_list_next (walk)) { - GstScheduler *test = walk->data; - - g_return_val_if_fail (test->parent, FALSE); - if (GST_STATE (test->parent) == GST_STATE_PLAYING) { - GST_CAT_DEBUG_OBJECT (GST_CAT_SCHEDULING, bin, - "current bin is not iterating, but children are, " - "so returning TRUE anyway..."); - g_usleep (1); - return TRUE; - } - } - } - } else { - g_warning ("bin \"%s\" is not the managing bin, can't be iterated on!\n", - GST_ELEMENT_NAME (bin)); - } - - return FALSE; -} - -/** - * gst_bin_iterate: - * @bin: a#GstBin to iterate. - * - * Iterates over the elements in this bin. - * - * Returns: TRUE if the bin did something useful. This value - * can be used to determine it the bin is in EOS. - */ -gboolean -gst_bin_iterate (GstBin * bin) -{ - gboolean running; - - g_return_val_if_fail (bin != NULL, FALSE); - g_return_val_if_fail (GST_IS_BIN (bin), FALSE); - - GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, bin, "starting iteration"); - gst_object_ref (GST_OBJECT (bin)); - - running = FALSE; - g_signal_emit (G_OBJECT (bin), gst_bin_signals[ITERATE], 0, &running); - - gst_object_unref (GST_OBJECT (bin)); - GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, bin, "finished iteration"); - - return running; -} diff --git a/gst/gstbin.h b/gst/gstbin.h index b0c3f538f5..938d84f77b 100644 --- a/gst/gstbin.h +++ b/gst/gstbin.h @@ -25,6 +25,7 @@ #define __GST_BIN_H__ #include +#include 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 diff --git a/gst/gstbuffer.c b/gst/gstbuffer.c index 79015398f4..20c647e961 100644 --- a/gst/gstbuffer.c +++ b/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. diff --git a/gst/gstbuffer.h b/gst/gstbuffer.h index b23e5b745e..fcd1243270 100644 --- a/gst/gstbuffer.h +++ b/gst/gstbuffer.h @@ -26,6 +26,7 @@ #include #include +#include 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); diff --git a/gst/gstcaps.c b/gst/gstcaps.c index e84263e736..7cd760363a 100644 --- a/gst/gstcaps.c +++ b/gst/gstcaps.c @@ -26,10 +26,12 @@ #include "gst_private.h" #include +#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; } } diff --git a/gst/gstcaps.h b/gst/gstcaps.h index 359175cce9..3995e577a6 100644 --- a/gst/gstcaps.h +++ b/gst/gstcaps.h @@ -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 diff --git a/gst/gstclock.c b/gst/gstclock.c index 1552d6617d..bd29182093 100644 --- a/gst/gstclock.c +++ b/gst/gstclock.c @@ -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); diff --git a/gst/gstclock.h b/gst/gstclock.h index dabb624bd7..fb5879a74a 100644 --- a/gst/gstclock.h +++ b/gst/gstclock.h @@ -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); diff --git a/gst/gstdata.c b/gst/gstdata.c index 52b8ce766f..90a60b5778 100644 --- a/gst/gstdata.c +++ b/gst/gstdata.c @@ -1,6 +1,6 @@ /* GStreamer * Copyright (C) 1999,2000 Erik Walthinsen - * 2000 Wim Taymans + * 2000 Wim Taymans * * gstdata.c: Data operations * diff --git a/gst/gstelement.c b/gst/gstelement.c index 65dbd6f98f..5a89125ec4 100644 --- a/gst/gstelement.c +++ b/gst/gstelement.c @@ -29,11 +29,12 @@ #include "gstbin.h" #include "gstmarshal.h" #include "gsterror.h" -#include "gstscheduler.h" #include "gstevent.h" #include "gstutils.h" #include "gstinfo.h" #include "gst-i18n-lib.h" +#include "gstscheduler.h" +#include "gstpipeline.h" /* Element signals and args */ enum @@ -41,9 +42,6 @@ enum STATE_CHANGE, NEW_PAD, PAD_REMOVED, - ERROR, - EOS, - FOUND_TAG, NO_MORE_PADS, /* add more above */ LAST_SIGNAL @@ -64,20 +62,13 @@ static void gst_element_init (GstElement * element); static void gst_element_base_class_init (gpointer g_class); static void gst_element_base_class_finalize (gpointer g_class); -static void gst_element_real_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_element_real_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); - static void gst_element_dispose (GObject * object); static GstElementStateReturn gst_element_change_state (GstElement * element); -static void gst_element_error_func (GstElement * element, GstElement * source, - GError * error, gchar * debug); -static void gst_element_found_tag_func (GstElement * element, - GstElement * source, const GstTagList * tag_list); -static GstElementStateReturn gst_element_set_state_func (GstElement * element, - GstElementState state); +static gboolean gst_element_get_state_func (GstElement * element, + GstElementState * state, GstElementState * pending, GTimeVal * timeout); +static void gst_element_set_manager_func (GstElement * element, + GstPipeline * manager); #ifndef GST_DISABLE_LOADSAVE static xmlNodePtr gst_element_save_thyself (GstObject * object, @@ -136,30 +127,11 @@ gst_element_class_init (GstElementClass * klass) g_signal_new ("pad-removed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstElementClass, pad_removed), NULL, NULL, gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); - gst_element_signals[ERROR] = - g_signal_new ("error", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GstElementClass, error), NULL, NULL, - gst_marshal_VOID__OBJECT_BOXED_STRING, G_TYPE_NONE, 3, GST_TYPE_ELEMENT, - GST_TYPE_G_ERROR, G_TYPE_STRING); - gst_element_signals[EOS] = - g_signal_new ("eos", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GstElementClass, eos), NULL, NULL, - gst_marshal_VOID__VOID, G_TYPE_NONE, 0); - gst_element_signals[FOUND_TAG] = - g_signal_new ("found-tag", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GstElementClass, found_tag), NULL, NULL, - gst_marshal_VOID__OBJECT_BOXED, G_TYPE_NONE, 2, GST_TYPE_ELEMENT, - GST_TYPE_TAG_LIST); gst_element_signals[NO_MORE_PADS] = g_signal_new ("no-more-pads", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstElementClass, no_more_pads), NULL, NULL, gst_marshal_VOID__VOID, G_TYPE_NONE, 0); - gobject_class->set_property = - GST_DEBUG_FUNCPTR (gst_element_real_set_property); - gobject_class->get_property = - GST_DEBUG_FUNCPTR (gst_element_real_get_property); - gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_element_dispose); #ifndef GST_DISABLE_LOADSAVE @@ -169,10 +141,9 @@ gst_element_class_init (GstElementClass * klass) #endif klass->change_state = GST_DEBUG_FUNCPTR (gst_element_change_state); - klass->error = GST_DEBUG_FUNCPTR (gst_element_error_func); - klass->found_tag = GST_DEBUG_FUNCPTR (gst_element_found_tag_func); + klass->get_state = GST_DEBUG_FUNCPTR (gst_element_get_state_func); + klass->set_manager = GST_DEBUG_FUNCPTR (gst_element_set_manager_func); klass->numpadtemplates = 0; - klass->set_state = GST_DEBUG_FUNCPTR (gst_element_set_state_func); klass->elementfactory = NULL; } @@ -180,15 +151,8 @@ gst_element_class_init (GstElementClass * klass) static void gst_element_base_class_init (gpointer g_class) { - GObjectClass *gobject_class = G_OBJECT_CLASS (g_class); GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - - gobject_class->set_property = - GST_DEBUG_FUNCPTR (gst_element_real_set_property); - gobject_class->get_property = - GST_DEBUG_FUNCPTR (gst_element_real_get_property); - memset (&element_class->details, 0, sizeof (GstElementDetails)); element_class->padtemplates = NULL; } @@ -211,36 +175,18 @@ gst_element_init (GstElement * element) element->numpads = 0; element->numsrcpads = 0; element->numsinkpads = 0; + element->pads_cookie = 0; element->pads = NULL; - element->loopfunc = NULL; - element->sched = NULL; + element->srcpads = NULL; + element->sinkpads = NULL; + element->manager = NULL; element->clock = NULL; element->sched_private = NULL; - element->state_mutex = g_mutex_new (); + element->state_lock = g_mutex_new (); element->state_cond = g_cond_new (); } -static void -gst_element_real_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstElementClass *oclass = GST_ELEMENT_GET_CLASS (object); - - if (oclass->set_property) - (oclass->set_property) (object, prop_id, value, pspec); -} - -static void -gst_element_real_get_property (GObject * object, guint prop_id, GValue * value, - GParamSpec * pspec) -{ - GstElementClass *oclass = GST_ELEMENT_GET_CLASS (object); - - if (oclass->get_property) - (oclass->get_property) (object, prop_id, value, pspec); -} - -/** +/** * gst_element_default_error: * @object: a #GObject that signalled the error. * @orig: the #GstObject that initiated the error. @@ -265,407 +211,6 @@ gst_element_default_error (GObject * object, GstObject * source, GError * error, g_free (name); } -typedef struct -{ - const GParamSpec *pspec; - GValue value; -} -prop_value_t; - -static void -element_set_property (GstElement * element, const GParamSpec * pspec, - const GValue * value) -{ - prop_value_t *prop_value = g_new0 (prop_value_t, 1); - - prop_value->pspec = pspec; - prop_value->value = *value; - - g_async_queue_push (element->prop_value_queue, prop_value); -} - -static void -element_get_property (GstElement * element, const GParamSpec * pspec, - GValue * value) -{ - g_mutex_lock (element->property_mutex); - g_object_get_property ((GObject *) element, pspec->name, value); - g_mutex_unlock (element->property_mutex); -} - -static void -gst_element_threadsafe_properties_pre_run (GstElement * element) -{ - /* need to ref the object because we don't want to lose the object - * before the post run function is called */ - gst_object_ref (GST_OBJECT (element)); - GST_DEBUG ("locking element %s", GST_OBJECT_NAME (element)); - g_mutex_lock (element->property_mutex); - gst_element_set_pending_properties (element); -} - -static void -gst_element_threadsafe_properties_post_run (GstElement * element) -{ - GST_DEBUG ("unlocking element %s", GST_OBJECT_NAME (element)); - g_mutex_unlock (element->property_mutex); - gst_object_unref (GST_OBJECT (element)); -} - -/** - * gst_element_enable_threadsafe_properties: - * @element: a #GstElement to enable threadsafe properties on. - * - * Installs an asynchronous queue, a mutex and pre- and post-run functions on - * this element so that properties on the element can be set in a - * threadsafe way. - */ -void -gst_element_enable_threadsafe_properties (GstElement * element) -{ - g_return_if_fail (GST_IS_ELEMENT (element)); - - GST_FLAG_SET (element, GST_ELEMENT_USE_THREADSAFE_PROPERTIES); - element->pre_run_func = gst_element_threadsafe_properties_pre_run; - element->post_run_func = gst_element_threadsafe_properties_post_run; - if (!element->prop_value_queue) - element->prop_value_queue = g_async_queue_new (); - if (!element->property_mutex) - element->property_mutex = g_mutex_new (); -} - -/** - * gst_element_disable_threadsafe_properties: - * @element: a #GstElement to disable threadsafe properties on. - * - * Removes the threadsafe properties, post- and pre-run locks from - * this element. - */ -void -gst_element_disable_threadsafe_properties (GstElement * element) -{ - g_return_if_fail (GST_IS_ELEMENT (element)); - - GST_FLAG_UNSET (element, GST_ELEMENT_USE_THREADSAFE_PROPERTIES); - element->pre_run_func = NULL; - element->post_run_func = NULL; - /* let's keep around that async queue */ -} - -/** - * gst_element_set_pending_properties: - * @element: a #GstElement to set the pending properties on. - * - * Sets all pending properties on the threadsafe properties enabled - * element. - */ -void -gst_element_set_pending_properties (GstElement * element) -{ - prop_value_t *prop_value; - - while ((prop_value = g_async_queue_try_pop (element->prop_value_queue))) { - g_object_set_property ((GObject *) element, prop_value->pspec->name, - &prop_value->value); - g_value_unset (&prop_value->value); - g_free (prop_value); - } -} - -/* following 6 functions taken mostly from gobject.c */ - -/** - * gst_element_set: - * @element: a #GstElement to set properties on. - * @first_property_name: the first property to set. - * @...: value of the first property, and more properties to set, ending - * with NULL. - * - * Sets properties on an element. If the element uses threadsafe properties, - * they will be queued and set on the object when it is scheduled again. - */ -void -gst_element_set (GstElement * element, const gchar * first_property_name, ...) -{ - va_list var_args; - - g_return_if_fail (GST_IS_ELEMENT (element)); - - va_start (var_args, first_property_name); - gst_element_set_valist (element, first_property_name, var_args); - va_end (var_args); -} - -/** - * gst_element_get: - * @element: a #GstElement to get properties of. - * @first_property_name: the first property to get. - * @...: pointer to a variable to store the first property in, as well as - * more properties to get, ending with NULL. - * - * Gets properties from an element. If the element uses threadsafe properties, - * the element will be locked before getting the given properties. - */ -void -gst_element_get (GstElement * element, const gchar * first_property_name, ...) -{ - va_list var_args; - - g_return_if_fail (GST_IS_ELEMENT (element)); - - va_start (var_args, first_property_name); - gst_element_get_valist (element, first_property_name, var_args); - va_end (var_args); -} - -/** - * gst_element_set_valist: - * @element: a #GstElement to set properties on. - * @first_property_name: the first property to set. - * @var_args: the var_args list of other properties to get. - * - * Sets properties on an element. If the element uses threadsafe properties, - * the property change will be put on the async queue. - */ -void -gst_element_set_valist (GstElement * element, const gchar * first_property_name, - va_list var_args) -{ - const gchar *name; - GObject *object; - - g_return_if_fail (GST_IS_ELEMENT (element)); - - object = (GObject *) element; - - GST_CAT_DEBUG (GST_CAT_PROPERTIES, - "setting valist of properties starting with %s on element %s", - first_property_name, gst_element_get_name (element)); - - if (!GST_FLAG_IS_SET (element, GST_ELEMENT_USE_THREADSAFE_PROPERTIES)) { - g_object_set_valist (object, first_property_name, var_args); - return; - } - - g_object_ref (object); - - name = first_property_name; - - while (name) { - GValue value = { 0, }; - GParamSpec *pspec; - gchar *error = NULL; - - pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), name); - - if (!pspec) { - g_warning ("%s: object class `%s' has no property named `%s'", - G_STRLOC, G_OBJECT_TYPE_NAME (object), name); - break; - } - if (!(pspec->flags & G_PARAM_WRITABLE)) { - g_warning ("%s: property `%s' of object class `%s' is not writable", - G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (object)); - break; - } - - g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); - - G_VALUE_COLLECT (&value, var_args, 0, &error); - if (error) { - g_warning ("%s: %s", G_STRLOC, error); - g_free (error); - - /* we purposely leak the value here, it might not be - * in a sane state if an error condition occoured - */ - break; - } - - element_set_property (element, pspec, &value); - g_value_unset (&value); - - name = va_arg (var_args, gchar *); - } - - g_object_unref (object); -} - -/** - * gst_element_get_valist: - * @element: a #GstElement to get properties of. - * @first_property_name: the first property to get. - * @var_args: the var_args list of other properties to get. - * - * Gets properties from an element. If the element uses threadsafe properties, - * the element will be locked before getting the given properties. - */ -void -gst_element_get_valist (GstElement * element, const gchar * first_property_name, - va_list var_args) -{ - const gchar *name; - GObject *object; - - g_return_if_fail (GST_IS_ELEMENT (element)); - - object = (GObject *) element; - - if (!GST_FLAG_IS_SET (element, GST_ELEMENT_USE_THREADSAFE_PROPERTIES)) { - g_object_get_valist (object, first_property_name, var_args); - return; - } - - g_object_ref (object); - - name = first_property_name; - - while (name) { - GValue value = { 0, }; - GParamSpec *pspec; - gchar *error; - - pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), name); - - if (!pspec) { - g_warning ("%s: object class `%s' has no property named `%s'", - G_STRLOC, G_OBJECT_TYPE_NAME (object), name); - break; - } - if (!(pspec->flags & G_PARAM_READABLE)) { - g_warning ("%s: property `%s' of object class `%s' is not readable", - G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (object)); - break; - } - - g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); - - element_get_property (element, pspec, &value); - - G_VALUE_LCOPY (&value, var_args, 0, &error); - if (error) { - g_warning ("%s: %s", G_STRLOC, error); - g_free (error); - g_value_unset (&value); - break; - } - - g_value_unset (&value); - - name = va_arg (var_args, gchar *); - } - - g_object_unref (object); -} - -/** - * gst_element_set_property: - * @element: a #GstElement to set properties on. - * @property_name: the first property to get. - * @value: the #GValue that holds the value to set. - * - * Sets a property on an element. If the element uses threadsafe properties, - * the property will be put on the async queue. - */ -void -gst_element_set_property (GstElement * element, const gchar * property_name, - const GValue * value) -{ - GParamSpec *pspec; - GObject *object; - - g_return_if_fail (GST_IS_ELEMENT (element)); - g_return_if_fail (property_name != NULL); - g_return_if_fail (G_IS_VALUE (value)); - - object = (GObject *) element; - - GST_CAT_DEBUG (GST_CAT_PROPERTIES, "setting property %s on element %s", - property_name, gst_element_get_name (element)); - if (!GST_FLAG_IS_SET (element, GST_ELEMENT_USE_THREADSAFE_PROPERTIES)) { - g_object_set_property (object, property_name, value); - return; - } - - g_object_ref (object); - - pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), - property_name); - - if (!pspec) - g_warning ("%s: object class `%s' has no property named `%s'", - G_STRLOC, G_OBJECT_TYPE_NAME (object), property_name); - else - element_set_property (element, pspec, value); - - g_object_unref (object); -} - -/** - * gst_element_get_property: - * @element: a #GstElement to get properties of. - * @property_name: the first property to get. - * @value: the #GValue to store the property value in. - * - * Gets a property from an element. If the element uses threadsafe properties, - * the element will be locked before getting the given property. - */ -void -gst_element_get_property (GstElement * element, const gchar * property_name, - GValue * value) -{ - GParamSpec *pspec; - GObject *object; - - g_return_if_fail (GST_IS_ELEMENT (element)); - g_return_if_fail (property_name != NULL); - g_return_if_fail (G_IS_VALUE (value)); - - object = (GObject *) element; - - if (!GST_FLAG_IS_SET (element, GST_ELEMENT_USE_THREADSAFE_PROPERTIES)) { - g_object_get_property (object, property_name, value); - return; - } - - g_object_ref (object); - - pspec = - g_object_class_find_property (G_OBJECT_GET_CLASS (object), property_name); - - if (!pspec) - g_warning ("%s: object class `%s' has no property named `%s'", - G_STRLOC, G_OBJECT_TYPE_NAME (object), property_name); - else { - GValue *prop_value, tmp_value = { 0, }; - - /* auto-conversion of the callers value type - */ - if (G_VALUE_TYPE (value) == G_PARAM_SPEC_VALUE_TYPE (pspec)) { - g_value_reset (value); - prop_value = value; - } else if (!g_value_type_transformable (G_PARAM_SPEC_VALUE_TYPE (pspec), - G_VALUE_TYPE (value))) { - g_warning - ("can't retrieve property `%s' of type `%s' as value of type `%s'", - pspec->name, g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)), - G_VALUE_TYPE_NAME (value)); - g_object_unref (object); - return; - } else { - g_value_init (&tmp_value, G_PARAM_SPEC_VALUE_TYPE (pspec)); - prop_value = &tmp_value; - } - element_get_property (element, pspec, prop_value); - if (prop_value != value) { - g_value_transform (prop_value, value); - g_value_unset (&tmp_value); - } - } - - g_object_unref (object); -} - static GstPad * gst_element_request_pad (GstElement * element, GstPadTemplate * templ, const gchar * name) @@ -714,9 +259,13 @@ gst_element_release_request_pad (GstElement * element, GstPad * pad) gboolean gst_element_requires_clock (GstElement * element) { - g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); + gboolean result = FALSE; - return (GST_ELEMENT_GET_CLASS (element)->set_clock != NULL); + g_return_val_if_fail (GST_IS_ELEMENT (element), result); + + result = (GST_ELEMENT_GET_CLASS (element)->set_clock != NULL); + + return result; } /** @@ -730,9 +279,13 @@ gst_element_requires_clock (GstElement * element) gboolean gst_element_provides_clock (GstElement * element) { + gboolean result = FALSE; + g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); - return (GST_ELEMENT_GET_CLASS (element)->get_clock != NULL); + result = (GST_ELEMENT_GET_CLASS (element)->get_clock != NULL); + + return result; } /** @@ -754,7 +307,9 @@ gst_element_set_clock (GstElement * element, GstClock * clock) if (oclass->set_clock) oclass->set_clock (element, clock); + GST_LOCK (element); gst_object_replace ((GstObject **) & element->clock, (GstObject *) clock); + GST_UNLOCK (element); } /** @@ -780,232 +335,6 @@ gst_element_get_clock (GstElement * element) return NULL; } -/** - * gst_element_clock_wait: - * @element: a #GstElement. - * @id: the #GstClock to use. - * @jitter: the difference between requested time and actual time. - * - * Waits for a specific time on the clock. - * - * Returns: the #GstClockReturn result of the wait operation. - */ -GstClockReturn -gst_element_clock_wait (GstElement * element, GstClockID id, - GstClockTimeDiff * jitter) -{ - GstClockReturn res; - - g_return_val_if_fail (GST_IS_ELEMENT (element), GST_CLOCK_ERROR); - - if (GST_ELEMENT_SCHED (element)) { - GST_CAT_DEBUG (GST_CAT_CLOCK, "waiting on scheduler clock with id %d"); - res = - gst_scheduler_clock_wait (GST_ELEMENT_SCHED (element), element, id, - jitter); - } else { - GST_CAT_DEBUG (GST_CAT_CLOCK, "no scheduler, returning GST_CLOCK_TIMEOUT"); - res = GST_CLOCK_TIMEOUT; - } - - return res; -} - -#undef GST_CAT_DEFAULT -#define GST_CAT_DEFAULT GST_CAT_CLOCK -/** - * gst_element_get_time: - * @element: element to query - * - * Query the element's time. FIXME: The element must use - * - * Returns: the current stream time in #GST_STATE_PLAYING, - * the element base time in #GST_STATE_PAUSED, - * or #GST_CLOCK_TIME_NONE otherwise. - */ -/* FIXME: this should always return time on the same scale. Now it returns - * the (absolute) base_time in PAUSED and the (current running) time in - * PLAYING. - * Solution: have a get_base_time and make the element subtract if it needs - * to. In PAUSED return the same as PLAYING, ie. the current timestamp where - * the element is at according to the provided clock. - */ -GstClockTime -gst_element_get_time (GstElement * element) -{ - g_return_val_if_fail (GST_IS_ELEMENT (element), GST_CLOCK_TIME_NONE); - - if (element->clock == NULL) { - GST_WARNING_OBJECT (element, "element queries time but has no clock"); - return GST_CLOCK_TIME_NONE; - } - switch (element->current_state) { - case GST_STATE_NULL: - case GST_STATE_READY: - return GST_CLOCK_TIME_NONE; - case GST_STATE_PAUSED: - return element->base_time; - case GST_STATE_PLAYING: - return gst_clock_get_time (element->clock) - element->base_time; - default: - g_assert_not_reached (); - return GST_CLOCK_TIME_NONE; - } -} - -/** - * gst_element_wait: - * @element: element that should wait - * @timestamp: what timestamp to wait on - * - * Waits until the given relative time stamp for the element has arrived. - * When this function returns successfully, the relative time point specified - * in the timestamp has passed for this element. - * This function can only be called on elements in - * #GST_STATE_PLAYING - * - * Returns: TRUE on success. - */ -gboolean -gst_element_wait (GstElement * element, GstClockTime timestamp) -{ - GstClockID id; - GstClockReturn ret; - GstClockTime time; - - g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); - g_return_val_if_fail (GST_IS_CLOCK (element->clock), FALSE); - g_return_val_if_fail (element->current_state == GST_STATE_PLAYING, FALSE); - g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE); - - /* shortcut when we're already late... */ - time = gst_element_get_time (element); - GST_CAT_LOG_OBJECT (GST_CAT_CLOCK, element, "element time %" GST_TIME_FORMAT, - GST_TIME_ARGS (time)); - if (time >= timestamp) { - GST_CAT_INFO_OBJECT (GST_CAT_CLOCK, element, - "called gst_element_wait (% " GST_TIME_FORMAT ") and was late (%" - GST_TIME_FORMAT, GST_TIME_ARGS (timestamp), - GST_TIME_ARGS (gst_element_get_time (element))); - return TRUE; - } - - id = gst_clock_new_single_shot_id (element->clock, - element->base_time + timestamp); - ret = gst_element_clock_wait (element, id, NULL); - gst_clock_id_free (id); - - return ret == GST_CLOCK_STOPPED; -} - -/** - * gst_element_set_time: - * @element: element to set time on - * @time: time to set - * - * Sets the current time of the element. This function can be used when handling - * discont events. You can only call this function on an element with a clock in - * #GST_STATE_PAUSED or #GST_STATE_PLAYING. You might want to have a look at - * gst_element_adjust_time(), if you want to adjust by a difference as that is - * more accurate. - */ -void -gst_element_set_time (GstElement * element, GstClockTime time) -{ - gst_element_set_time_delay (element, time, 0); -} - -/** - * gst_element_set_time_delay: - * @element: element to set time on - * @time: time to set - * @delay: a delay to discount from the given time - * - * Sets the current time of the element to time - delay. This function can be - * used when handling discont events in elements writing to an external buffer, - * i. e., an audio sink that writes to a sound card that buffers the sound - * before playing it. The delay should be the current buffering delay. - * - * You can only call this function on an element with a clock in - * #GST_STATE_PAUSED or #GST_STATE_PLAYING. You might want to have a look at - * gst_element_adjust_time(), if you want to adjust by a difference as that is - * more accurate. - */ -void -gst_element_set_time_delay (GstElement * element, GstClockTime time, - GstClockTime delay) -{ - GstClockTime event_time; - - g_return_if_fail (GST_IS_ELEMENT (element)); - g_return_if_fail (GST_IS_CLOCK (element->clock)); - g_return_if_fail (element->current_state >= GST_STATE_PAUSED); - g_return_if_fail (time >= delay); - - switch (element->current_state) { - case GST_STATE_PAUSED: - element->base_time = time - delay; - break; - case GST_STATE_PLAYING: - event_time = gst_clock_get_event_time_delay (element->clock, delay); - GST_CAT_LOG_OBJECT (GST_CAT_CLOCK, element, - "clock time %" GST_TIME_FORMAT ": setting element time to %" - GST_TIME_FORMAT, GST_TIME_ARGS (event_time), GST_TIME_ARGS (time)); - element->base_time = event_time - time; - break; - default: - g_assert_not_reached (); - break; - } -} - -/** - * gst_element_adjust_time: - * @element: element to adjust time on - * @diff: difference to adjust - * - * Adjusts the current time of the element by the specified difference. This - * function can be used when handling discont events. You can only call this - * function on an element with a clock in #GST_STATE_PAUSED or - * #GST_STATE_PLAYING. It is more accurate than gst_element_set_time(). - */ -void -gst_element_adjust_time (GstElement * element, GstClockTimeDiff diff) -{ - GstClockTime time; - - g_return_if_fail (GST_IS_ELEMENT (element)); - g_return_if_fail (GST_IS_CLOCK (element->clock)); - g_return_if_fail (element->current_state >= GST_STATE_PAUSED); - - switch (element->current_state) { - case GST_STATE_PAUSED: - if (diff < 0 && element->base_time < abs (diff)) { - g_warning ("attempted to set the current time of element %s below 0", - GST_OBJECT_NAME (element)); - element->base_time = 0; - } else { - element->base_time += diff; - } - break; - case GST_STATE_PLAYING: - time = gst_clock_get_time (element->clock); - if (time < element->base_time - diff) { - g_warning ("attempted to set the current time of element %s below 0", - GST_OBJECT_NAME (element)); - element->base_time = time; - } else { - element->base_time -= diff; - } - break; - default: - g_assert_not_reached (); - break; - } -} - -#undef GST_CAT_DEFAULT - #ifndef GST_DISABLE_INDEX /** * gst_element_is_indexable: @@ -1018,9 +347,13 @@ gst_element_adjust_time (GstElement * element, GstClockTimeDiff diff) gboolean gst_element_is_indexable (GstElement * element) { - g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); + gboolean result = FALSE; - return (GST_ELEMENT_GET_CLASS (element)->set_index != NULL); + g_return_val_if_fail (GST_IS_ELEMENT (element), result); + + result = (GST_ELEMENT_GET_CLASS (element)->set_index != NULL); + + return result; } /** @@ -1069,30 +402,6 @@ gst_element_get_index (GstElement * element) } #endif -/** - * gst_element_release_locks: - * @element: a #GstElement to release all locks on. - * - * Instruct the element to release all the locks it is holding, such as - * blocking reads, waiting for the clock, ... - * - * Returns: TRUE if the locks could be released. - */ -gboolean -gst_element_release_locks (GstElement * element) -{ - GstElementClass *oclass; - - g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); - - oclass = GST_ELEMENT_GET_CLASS (element); - - if (oclass->release_locks) - return oclass->release_locks (element); - - return TRUE; -} - /** * gst_element_add_pad: * @element: a #GstElement to add the pad to. @@ -1102,20 +411,28 @@ gst_element_release_locks (GstElement * element) * see gst_object_set_parent() for refcounting information. * * Pads are automatically activated when the element is in state PLAYING. + * + * Returns: TRUE if the pad could be added. This function can fail when + * passing bad arguments or when a pad with the same name already existed. */ -void +gboolean gst_element_add_pad (GstElement * element, GstPad * pad) { - g_return_if_fail (GST_IS_ELEMENT (element)); - g_return_if_fail (GST_IS_PAD (pad)); + g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); + g_return_val_if_fail (GST_IS_PAD (pad), FALSE); /* first check to make sure the pad hasn't already been added to another * element */ - g_return_if_fail (GST_PAD_PARENT (pad) == NULL); + g_return_val_if_fail (GST_PAD_PARENT (pad) == NULL, FALSE); + GST_LOCK (element); /* then check to see if there's already a pad by that name here */ - g_return_if_fail (gst_object_check_uniqueness (element->pads, - GST_PAD_NAME (pad)) == TRUE); + if (!gst_object_check_uniqueness (element->pads, GST_PAD_NAME (pad))) { + g_warning ("Padname %s:%s is not unique in element %s, not adding\n", + GST_DEBUG_PAD_NAME (pad), GST_ELEMENT_NAME (element)); + GST_UNLOCK (element); + return FALSE; + } GST_CAT_INFO_OBJECT (GST_CAT_ELEMENT_PADS, element, "adding pad '%s'", GST_STR_NULL (GST_OBJECT_NAME (pad))); @@ -1124,27 +441,34 @@ gst_element_add_pad (GstElement * element, GstPad * pad) gst_object_set_parent (GST_OBJECT (pad), GST_OBJECT (element)); /* add it to the list */ - element->pads = g_list_append (element->pads, pad); - element->numpads++; - switch (gst_pad_get_direction (pad)) { case GST_PAD_SRC: + element->srcpads = g_list_prepend (element->srcpads, pad); element->numsrcpads++; break; case GST_PAD_SINK: + element->sinkpads = g_list_prepend (element->sinkpads, pad); element->numsinkpads++; break; default: - /* can happen for ghost pads */ + g_assert_not_reached (); break; } + element->pads = g_list_prepend (element->pads, pad); + element->numpads++; + element->pads_cookie++; + GST_UNLOCK (element); - /* activate element when we are playing */ + GST_STATE_LOCK (element); + /* activate pad when we are playing */ if (GST_STATE (element) == GST_STATE_PLAYING) gst_pad_set_active (pad, TRUE); + GST_STATE_UNLOCK (element); /* emit the NEW_PAD signal */ g_signal_emit (G_OBJECT (element), gst_element_signals[NEW_PAD], 0, pad); + + return TRUE; } /** @@ -1168,13 +492,12 @@ gst_element_add_ghost_pad (GstElement * element, GstPad * pad, g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); g_return_val_if_fail (GST_IS_PAD (pad), NULL); - /* then check to see if there's already a pad by that name here */ - g_return_val_if_fail (gst_object_check_uniqueness (element->pads, - name) == TRUE, NULL); - ghostpad = gst_ghost_pad_new (name, pad); - gst_element_add_pad (element, ghostpad); + if (!gst_element_add_pad (element, ghostpad)) { + gst_object_unref (GST_OBJECT (ghostpad)); + ghostpad = NULL; + } return ghostpad; } @@ -1198,55 +521,39 @@ gst_element_remove_pad (GstElement * element, GstPad * pad) g_return_if_fail (GST_PAD_PARENT (pad) == element); if (GST_IS_REAL_PAD (pad)) { - /* unlink if necessary */ + /* unlink */ if (GST_RPAD_PEER (pad) != NULL) { gst_pad_unlink (pad, GST_PAD (GST_RPAD_PEER (pad))); } - gst_caps_replace (&GST_RPAD_EXPLICIT_CAPS (pad), NULL); } else if (GST_IS_GHOST_PAD (pad)) { g_object_set (pad, "real-pad", NULL, NULL); } + GST_LOCK (element); /* remove it from the list */ - element->pads = g_list_remove (element->pads, pad); - element->numpads--; switch (gst_pad_get_direction (pad)) { case GST_PAD_SRC: + element->srcpads = g_list_remove (element->srcpads, pad); element->numsrcpads--; break; case GST_PAD_SINK: + element->sinkpads = g_list_remove (element->sinkpads, pad); element->numsinkpads--; break; default: /* can happen for ghost pads */ break; } + element->pads = g_list_remove (element->pads, pad); + element->numpads--; + element->pads_cookie++; + GST_UNLOCK (element); g_signal_emit (G_OBJECT (element), gst_element_signals[PAD_REMOVED], 0, pad); gst_object_unparent (GST_OBJECT (pad)); } -/** - * gst_element_remove_ghost_pad: - * @element: a #GstElement to remove the ghost pad from. - * @pad: ghost #GstPad to remove. - * - * Removes a ghost pad from an element. Deprecated, use gst_element_remove_pad() - * instead. - */ -void -gst_element_remove_ghost_pad (GstElement * element, GstPad * pad) -{ - g_return_if_fail (GST_IS_ELEMENT (element)); - g_return_if_fail (GST_IS_GHOST_PAD (pad)); - - g_warning ("gst_element_remove_ghost_pad is deprecated.\n" - "Use gst_element_remove_pad instead."); - - gst_element_remove_pad (element, pad); -} - /** * gst_element_no_more_pads: * @element: a #GstElement @@ -1291,6 +598,12 @@ gst_element_get_pad (GstElement * element, const gchar * name) return pad; } +static gint +pad_compare_name (GstPad * pad1, const gchar * name) +{ + return strcmp (GST_PAD_NAME (pad1), name); +} + /** * gst_element_get_static_pad: * @element: a #GstElement to find a static pad of. @@ -1304,28 +617,28 @@ gst_element_get_pad (GstElement * element, const gchar * name) GstPad * gst_element_get_static_pad (GstElement * element, const gchar * name) { - GList *walk; + GList *find; + GstPad *result = NULL; - g_return_val_if_fail (element != NULL, NULL); - g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); - g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (GST_IS_ELEMENT (element), result); + g_return_val_if_fail (name != NULL, result); - walk = element->pads; - while (walk) { - GstPad *pad; - - pad = GST_PAD (walk->data); - if (strcmp (GST_PAD_NAME (pad), name) == 0) { - GST_CAT_INFO (GST_CAT_ELEMENT_PADS, "found pad %s:%s", - GST_DEBUG_PAD_NAME (pad)); - return pad; - } - walk = g_list_next (walk); + GST_LOCK (element); + find = + g_list_find_custom (element->pads, name, (GCompareFunc) pad_compare_name); + if (find) { + result = GST_PAD (find->data); } + GST_UNLOCK (element); - GST_CAT_INFO (GST_CAT_ELEMENT_PADS, "no such pad '%s' in element \"%s\"", - name, GST_OBJECT_NAME (element)); - return NULL; + if (result == NULL) { + GST_CAT_INFO (GST_CAT_ELEMENT_PADS, "no such pad '%s' in element \"%s\"", + name, GST_OBJECT_NAME (element)); + } else { + GST_CAT_INFO (GST_CAT_ELEMENT_PADS, "found pad %s:%s", + GST_DEBUG_PAD_NAME (result)); + } + return result; } /** @@ -1349,18 +662,21 @@ gst_element_get_request_pad (GstElement * element, const gchar * name) gint n; const gchar *data; gchar *str, *endptr = NULL; + 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 (name != NULL, NULL); + class = GST_ELEMENT_GET_CLASS (element); + if (strstr (name, "%")) { - templ = gst_element_get_pad_template (element, name); + templ = gst_element_class_get_pad_template (class, name); req_name = NULL; if (templ) templ_found = TRUE; } else { - list = gst_element_get_pad_template_list (element); + list = gst_element_class_get_pad_template_list (class); while (!templ_found && list) { templ = (GstPadTemplate *) list->data; if (templ->presence == GST_PAD_REQUEST) { @@ -1414,11 +730,17 @@ gst_element_get_request_pad (GstElement * element, const gchar * name) const GList * gst_element_get_pad_list (GstElement * element) { - g_return_val_if_fail (element != NULL, NULL); - g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); + const GList *result = NULL; + + g_return_val_if_fail (element != NULL, result); + g_return_val_if_fail (GST_IS_ELEMENT (element), result); + + g_warning ("calling gst_element_get_pad_list is MT unsafe!!"); /* return the list of pads */ - return element->pads; + result = element->pads; + + return result; } /** @@ -1521,723 +843,41 @@ gst_element_class_get_pad_template (GstElementClass * element_class, return NULL; } -/** - * gst_element_get_pad_template_list: - * @element: a #GstElement to get pad templates of. - * - * Retrieves a list of the pad templates associated with the element. - * (FIXME: Should be deprecated in favor of - * gst_element_class_get_pad_template_list). - * - * Returns: the #GList of padtemplates. - */ -GList * -gst_element_get_pad_template_list (GstElement * element) -{ - g_return_val_if_fail (element != NULL, NULL); - g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); - - return GST_ELEMENT_GET_CLASS (element)->padtemplates; -} - -/** - * gst_element_get_pad_template: - * @element: a #GstElement to get the pad template of. - * @name: the name of the #GstPadTemplate to get. - * - * Retrieves a padtemplate from this element with the - * given name. - * (FIXME: Should be deprecated in favor of gst_element_class_get_pad_template). - * - * Returns: the #GstPadTemplate with the given name, or NULL if none was found. - * No unreferencing is necessary. - */ -GstPadTemplate * -gst_element_get_pad_template (GstElement * element, const gchar * name) -{ - g_return_val_if_fail (element != NULL, NULL); - g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); - g_return_val_if_fail (name != NULL, NULL); - - return gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (element), - name); -} - -/** - * 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; - - 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); - - padlist = gst_element_get_pad_template_list (element); - - 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_free (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; -} - -/** - * 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_get_caps (pad); - if (filtercaps != NULL) { - GstCaps *temp; - - temp = gst_caps_intersect (filtercaps, templcaps); - gst_caps_free (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_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. - */ -gboolean -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; - - /* checks */ - g_return_val_if_fail (GST_IS_ELEMENT (src), FALSE); - g_return_val_if_fail (GST_IS_ELEMENT (dest), FALSE); - - 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_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_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_get_pad_template_list (src); - desttempls = gst_element_get_pad_template_list (dest); - - 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_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. - */ -gboolean -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. - */ -gboolean -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. - */ -gboolean -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. - */ -gboolean -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); - } -} - -static void -gst_element_error_func (GstElement * element, GstElement * source, - GError * error, gchar * debug) -{ - GstObject *parent = GST_OBJECT_PARENT (element); - - /* tell the parent */ - if (parent) { - gst_object_ref (GST_OBJECT (element)); - gst_object_ref (parent); - GST_CAT_DEBUG (GST_CAT_ERROR_SYSTEM, - "forwarding error \"%s\" from %s to %s", error->message, - GST_ELEMENT_NAME (element), GST_OBJECT_NAME (parent)); - - g_signal_emit (G_OBJECT (parent), - gst_element_signals[ERROR], 0, source, error, debug); - GST_CAT_DEBUG (GST_CAT_ERROR_SYSTEM, "forwarded error \"%s\" from %s to %s", - error->message, GST_ELEMENT_NAME (element), GST_OBJECT_NAME (parent)); - gst_object_unref (GST_OBJECT (element)); - gst_object_unref (GST_OBJECT (parent)); - } -} - static GstPad * gst_element_get_random_pad (GstElement * element, GstPadDirection dir) { - GList *pads = element->pads; + GstPad *result = NULL; + GList *pads; GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "getting a random pad"); - while (pads) { + + GST_LOCK (element); + switch (dir) { + case GST_PAD_SRC: + pads = element->srcpads; + case GST_PAD_SINK: + pads = element->sinkpads; + default: + g_warning ("unknown pad direction"); + return NULL; + } + for (; pads; pads = g_list_next (pads)) { GstPad *pad = GST_PAD (pads->data); GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "checking pad %s:%s", GST_DEBUG_PAD_NAME (pad)); - if (GST_PAD_DIRECTION (pad) == dir) { - if (GST_PAD_IS_LINKED (pad)) { - return pad; - } else { - GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "pad %s:%s is not linked", - GST_DEBUG_PAD_NAME (pad)); - } + if (GST_PAD_IS_LINKED (pad)) { + result = pad; + break; } else { - GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "pad %s:%s is in wrong direction", + GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "pad %s:%s is not linked", GST_DEBUG_PAD_NAME (pad)); } - - pads = g_list_next (pads); } - return NULL; + GST_UNLOCK (element); + + return result; } /** @@ -2476,6 +1116,27 @@ gst_element_convert (GstElement * element, return FALSE; } +gboolean +gst_element_post_message (GstElement * element, GstMessage * message) +{ + GstPipeline *manager; + gboolean result = FALSE; + + g_return_val_if_fail (GST_IS_ELEMENT (element), result); + g_return_val_if_fail (message != NULL, result); + + manager = element->manager; + + if (manager == NULL) { + gst_data_unref (GST_DATA (message)); + return result; + } + + result = gst_pipeline_post_message (manager, message); + + return result; +} + /** * _gst_element_error_printf: * @format: the printf-like format to use, or NULL @@ -2549,18 +1210,6 @@ void gst_element_error_full GST_ELEMENT_NAME (element), sent_message); error = g_error_new_literal (domain, code, sent_message); - /* if the element was already in error, stop now */ - if (GST_FLAG_IS_SET (element, GST_ELEMENT_IN_ERROR)) { - GST_CAT_INFO (GST_CAT_ERROR_SYSTEM, "recursive ERROR detected in %s", - GST_ELEMENT_NAME (element)); - g_free (sent_message); - if (debug) - g_free (debug); - return; - } - - GST_FLAG_SET (element, GST_ELEMENT_IN_ERROR); - /* emit the signal, make sure the element stays available */ gst_object_ref (GST_OBJECT (element)); name = gst_object_get_path_string (GST_OBJECT (element)); @@ -2571,33 +1220,16 @@ void gst_element_error_full sent_debug = NULL; g_free (debug); g_free (name); - g_signal_emit (G_OBJECT (element), gst_element_signals[ERROR], 0, element, - error, sent_debug); + + gst_element_post_message (element, + gst_message_new_error (GST_OBJECT (element), error, sent_debug)); + GST_CAT_INFO (GST_CAT_ERROR_SYSTEM, "signalled error in %s: %s", GST_ELEMENT_NAME (element), sent_message); - /* tell the scheduler */ - if (element->sched) { - gst_scheduler_error (element->sched, element); - } - - if (GST_STATE (element) == GST_STATE_PLAYING) { - GstElementStateReturn ret; - - ret = gst_element_set_state (element, GST_STATE_PAUSED); - if (ret != GST_STATE_SUCCESS) { - g_warning ("could not PAUSE element \"%s\" after error, help!", - GST_ELEMENT_NAME (element)); - } - } - - GST_FLAG_UNSET (element, GST_ELEMENT_IN_ERROR); - /* cleanup */ gst_object_unref (GST_OBJECT (element)); g_free (sent_message); - g_free (sent_debug); - g_error_free (error); } /** @@ -2615,9 +1247,15 @@ void gst_element_error_full gboolean gst_element_is_locked_state (GstElement * element) { - g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); + gboolean result = FALSE; - return GST_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE) ? TRUE : FALSE; + g_return_val_if_fail (GST_IS_ELEMENT (element), result); + + GST_LOCK (element); + result = GST_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE) ? TRUE : FALSE; + GST_UNLOCK (element); + + return result; } /** @@ -2635,10 +1273,11 @@ gst_element_set_locked_state (GstElement * element, gboolean locked_state) g_return_if_fail (GST_IS_ELEMENT (element)); + GST_LOCK (element); old = GST_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE); if (old == locked_state) - return; + goto exit; if (locked_state) { GST_CAT_DEBUG (GST_CAT_STATES, "locking state of element %s", @@ -2649,6 +1288,9 @@ gst_element_set_locked_state (GstElement * element, gboolean locked_state) GST_ELEMENT_NAME (element)); GST_FLAG_UNSET (element, GST_ELEMENT_LOCKED_STATE); } + +exit: + GST_UNLOCK (element); } /** @@ -2680,34 +1322,135 @@ gst_element_sync_state_with_parent (GstElement * element) return TRUE; } +static gboolean +gst_element_get_state_func (GstElement * element, + GstElementState * state, GstElementState * pending, GTimeVal * timeout) +{ + gboolean ret = FALSE; + GstElementState old_pending; + + g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); + + GST_STATE_LOCK (element); + old_pending = GST_STATE_PENDING (element); + if (old_pending != GST_STATE_VOID_PENDING) { + if (!GST_STATE_TIMED_WAIT (element, timeout)) { + /* timeout triggered */ + if (state) + *state = GST_STATE (element); + if (pending) + *pending = GST_STATE_PENDING (element); + ret = FALSE; + } else { + /* could be success or failure, we could check here if the + * state is equal to the old pending state */ + } + } + /* if nothing is pending anymore we can return TRUE and + * set the values of the current and panding state */ + if (GST_STATE_PENDING (element) == GST_STATE_VOID_PENDING) { + if (state) + *state = GST_STATE (element); + if (pending) + *pending = GST_STATE_VOID_PENDING; + ret = TRUE; + } + GST_STATE_UNLOCK (element); + + return ret; +} + + /** * gst_element_get_state: * @element: a #GstElement to get the state of. + * @state: a pointer to #GstElementState to hold the state. Can be NULL. + * @pending: a pointer to #GstElementState to hold the pending state. + * Can be NULL. + * @timeout: a #GTimeVal to specify the timeout for an async + * state change. * * Gets the state of the element. * - * Returns: the #GstElementState of the element. + * Returns: TRUE if the element has no more pending state, FALSE + * if the element is still performing a state change. */ -GstElementState -gst_element_get_state (GstElement * element) +gboolean +gst_element_get_state (GstElement * element, + GstElementState * state, GstElementState * pending, GTimeVal * timeout) { - g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_VOID_PENDING); + GstElementClass *oclass; - return GST_STATE (element); + g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); + + oclass = GST_ELEMENT_GET_CLASS (element); + + if (oclass->get_state) + return (oclass->get_state) (element, state, pending, timeout); + + return FALSE; } /** - * gst_element_wait_state_change: - * @element: a #GstElement to wait for a state change on. + * gst_element_abort_state: + * @element: a #GstElement to abort the state of. * - * Waits and blocks until the element changed its state. + * Abort the state change of the element. This function is used + * by elements that do asynchronous state changes and find out + * something is wrong. */ void -gst_element_wait_state_change (GstElement * element) +gst_element_abort_state (GstElement * element) { - g_mutex_lock (element->state_mutex); - g_cond_wait (element->state_cond, element->state_mutex); - g_mutex_unlock (element->state_mutex); + GstElementState pending; + + g_return_if_fail (GST_IS_ELEMENT (element)); + + pending = GST_STATE_PENDING (element); + + if (pending != GST_STATE_VOID_PENDING) { + GstElementState old_state = GST_STATE (element); + + GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, + "aborting state from %s to %s", gst_element_state_get_name (old_state), + gst_element_state_get_name (pending)); + + GST_STATE_PENDING (element) = GST_STATE_VOID_PENDING; + + g_cond_broadcast (GST_STATE_GET_COND (element)); + } +} + +/** + * gst_element_commit_state: + * @element: a #GstElement to commit the state of. + * + * Commit the state change of the element. This function is used + * by elements that do asynchronous state changes. + */ +void +gst_element_commit_state (GstElement * element) +{ + GstElementState pending; + + g_return_if_fail (GST_IS_ELEMENT (element)); + + pending = GST_STATE_PENDING (element); + + if (pending != GST_STATE_VOID_PENDING) { + GstElementState old_state = GST_STATE (element); + + GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, + "commiting state from %s to %s", gst_element_state_get_name (old_state), + gst_element_state_get_name (pending)); + + GST_STATE (element) = pending; + GST_STATE_PENDING (element) = GST_STATE_VOID_PENDING; + + g_signal_emit (G_OBJECT (element), gst_element_signals[STATE_CHANGE], + 0, old_state, pending); + g_cond_broadcast (GST_STATE_GET_COND (element)); + } } /** @@ -2724,165 +1467,107 @@ gst_element_wait_state_change (GstElement * element) */ GstElementStateReturn gst_element_set_state (GstElement * element, GstElementState state) -{ - GstElementClass *klass = GST_ELEMENT_GET_CLASS (element); - GstElementStateReturn ret; - - g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_FAILURE); - GST_DEBUG_OBJECT (element, "setting state to %s", - gst_element_state_get_name (state)); - klass = GST_ELEMENT_GET_CLASS (element); - g_return_val_if_fail (klass->set_state, GST_STATE_FAILURE); - - /* a set_state function is mandatory */ - gst_object_ref (GST_OBJECT (element)); - ret = klass->set_state (element, state); - gst_object_unref (GST_OBJECT (element)); - - return ret; -} - -static GstElementStateReturn -gst_element_set_state_func (GstElement * element, GstElementState state) { GstElementClass *oclass; - GstElementState curpending; + GstElementState current; GstElementStateReturn return_val = GST_STATE_SUCCESS; oclass = GST_ELEMENT_GET_CLASS (element); - /* start with the current state */ - curpending = GST_STATE (element); - - if (state == curpending) { - GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, - "element is already in requested state %s, returning", - gst_element_state_get_name (state)); - return GST_STATE_SUCCESS; - } - /* reentrancy issues with signals in change_state) */ gst_object_ref (GST_OBJECT (element)); + + /* get the element state lock */ + GST_STATE_LOCK (element); + + /* start with the current state */ + current = GST_STATE (element); + GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "setting state from %s to %s", - gst_element_state_get_name (curpending), - gst_element_state_get_name (state)); + gst_element_state_get_name (current), gst_element_state_get_name (state)); - /* loop until the final requested state is set */ + /* We always perform at least one state change, even if the + * current state is equal to the required state. This is needed + * for bins that sync their children. */ + do { + GstElementState pending; - while (GST_STATE (element) != state - && GST_STATE (element) != GST_STATE_VOID_PENDING) { - /* move the curpending state in the correct direction */ - if (curpending < state) - curpending <<= 1; + /* calculate the pending state */ + if (current < state) + pending = current << 1; + else if (current > state) + pending = current >> 1; else - curpending >>= 1; + pending = current; /* set the pending state variable */ - GST_STATE_PENDING (element) = curpending; + GST_STATE_PENDING (element) = pending; - if (curpending != state) { + if (pending != state) { GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "intermediate: setting state from %s to %s", - gst_element_state_get_name (GST_STATE (element)), - gst_element_state_get_name (curpending)); + gst_element_state_get_name (current), + gst_element_state_get_name (pending)); } else { GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, - "start: setting current state %s again", - gst_element_state_get_name (GST_STATE (element))); + "final: setting state from %s to %s", + gst_element_state_get_name (current), + gst_element_state_get_name (pending)); } /* call the state change function so it can set the state */ if (oclass->change_state) return_val = (oclass->change_state) (element); + else + return_val = GST_STATE_FAILURE; switch (return_val) { case GST_STATE_FAILURE: GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "have failed change_state return"); + /* state change failure exits the loop */ + gst_element_abort_state (element); goto exit; case GST_STATE_ASYNC: GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "element will change state async"); + /* an async state change exits the loop, we can only + * go to the next state change when this one completes. */ goto exit; case GST_STATE_SUCCESS: - /* Last thing we do is verify that a successful state change really - * did change the state... */ - /* if it did not, this is an error - fix the element that does this */ - if (GST_STATE (element) != curpending) { - g_warning ("element %s claimed state-change success," - "but state didn't change to %s. State is %s (%s pending), " - "fix the element", - GST_ELEMENT_NAME (element), - gst_element_state_get_name (curpending), - gst_element_state_get_name (GST_STATE (element)), - gst_element_state_get_name (GST_STATE_PENDING (element))); - return_val = GST_STATE_FAILURE; - goto exit; - } + GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, + "element changed state successfuly"); + /* we can commit the state now and proceed to the next state */ + gst_element_commit_state (element); + GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "commited state"); break; default: /* somebody added a GST_STATE_ and forgot to do stuff here ! */ g_assert_not_reached (); } + /* get the current state of the element and see if we need to do more + * state changes */ + current = GST_STATE (element); } + while (current != state); exit: + GST_STATE_UNLOCK (element); + + GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "exit state change"); + gst_object_unref (GST_OBJECT (element)); return return_val; } static gboolean -gst_element_negotiate_pads (GstElement * element) -{ - GList *pads; - - GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, element, "negotiating pads"); - - for (pads = GST_ELEMENT_PADS (element); pads; pads = g_list_next (pads)) { - GstPad *pad = GST_PAD (pads->data); - - if (!GST_IS_REAL_PAD (pad)) - continue; - - /* if we have a link on this pad and it doesn't have caps - * allready, try to negotiate */ - if (!gst_pad_is_negotiated (pad)) { - GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, element, - "perform negotiate for %s:%s", GST_DEBUG_PAD_NAME (pad)); - if (gst_pad_renegotiate (pad) == GST_PAD_LINK_REFUSED) - return FALSE; - } - } - - return TRUE; -} - -static void -gst_element_clear_pad_caps (GstElement * element) -{ - GList *pads = GST_ELEMENT_PADS (element); - - GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, element, "clearing pad caps"); - - while (pads) { - GstPad *pad = GST_PAD (pads->data); - - gst_pad_unnegotiate (pad); - if (GST_IS_REAL_PAD (pad)) { - gst_caps_replace (&GST_RPAD_EXPLICIT_CAPS (pad), NULL); - } - - pads = g_list_next (pads); - } -} - -static void gst_element_pads_activate (GstElement * element, gboolean active) { GList *pads = element->pads; + gboolean result = TRUE; - while (pads) { + while (pads && result) { GstPad *pad = GST_PAD (pads->data); pads = g_list_next (pads); @@ -2890,16 +1575,18 @@ gst_element_pads_activate (GstElement * element, gboolean active) if (!GST_IS_REAL_PAD (pad)) continue; - gst_pad_set_active (pad, active); + result &= gst_pad_set_active (pad, active); } + + return result; } static GstElementStateReturn gst_element_change_state (GstElement * element) { - GstElementState old_state, old_pending; - GstObject *parent; - gint old_transition; + GstElementState old_state; + gint old_pending, old_transition; + GstElementStateReturn result = GST_STATE_SUCCESS; g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_FAILURE); @@ -2916,56 +1603,33 @@ gst_element_change_state (GstElement * element) return GST_STATE_SUCCESS; } - /* we need to ref the object because of reentrancy issues with the signal - * handlers (including those in pads and gst_bin_child_state_change */ - gst_object_ref (GST_OBJECT (element)); GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "default handler tries setting state from %s to %s (%04x)", gst_element_state_get_name (old_state), gst_element_state_get_name (old_pending), old_transition); - /* we set the state change early for the negotiation functions */ - GST_STATE (element) = old_pending; - GST_STATE_PENDING (element) = GST_STATE_VOID_PENDING; - switch (old_transition) { - case GST_STATE_PLAYING_TO_PAUSED: - if (element->clock) { - GstClockTimeDiff time = gst_clock_get_event_time (element->clock); - - g_assert (time >= element->base_time); - element->base_time = time - element->base_time; - GST_CAT_LOG_OBJECT (GST_CAT_CLOCK, element, "setting base time to %" - G_GINT64_FORMAT, element->base_time); + case GST_STATE_NULL_TO_READY: + break; + case GST_STATE_READY_TO_PAUSED: + if (!gst_element_pads_activate (element, TRUE)) { + result = GST_STATE_FAILURE; } - gst_element_pads_activate (element, FALSE); break; case GST_STATE_PAUSED_TO_PLAYING: - gst_element_pads_activate (element, TRUE); - if (element->clock) { - GstClockTime time = gst_clock_get_event_time (element->clock); - - element->base_time = time - element->base_time; - GST_CAT_LOG_OBJECT (GST_CAT_CLOCK, element, "setting base time to %" - GST_TIME_FORMAT, GST_TIME_ARGS (element->base_time)); + if (GST_ELEMENT_MANAGER (element)) { + element->base_time = + GST_ELEMENT (GST_ELEMENT_MANAGER (element))->base_time; } break; - /* if we are going to paused, we try to negotiate the pads */ - case GST_STATE_READY_TO_PAUSED: - g_assert (element->base_time == 0); - if (!gst_element_negotiate_pads (element)) { - GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, - "failed state change, could not negotiate pads"); - goto failure; - } + case GST_STATE_PLAYING_TO_PAUSED: break; - /* going to the READY state clears all pad caps */ - /* FIXME: Why doesn't this happen on READY => NULL? -- Company */ case GST_STATE_PAUSED_TO_READY: element->base_time = 0; - gst_element_clear_pad_caps (element); + if (!gst_element_pads_activate (element, FALSE)) { + result = GST_STATE_FAILURE; + } break; - case GST_STATE_NULL_TO_READY: case GST_STATE_READY_TO_NULL: break; default: @@ -2980,46 +1644,7 @@ gst_element_change_state (GstElement * element) break; } - parent = GST_ELEMENT_PARENT (element); - - GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, - "signaling state change from %s to %s", - gst_element_state_get_name (old_state), - gst_element_state_get_name (GST_STATE (element))); - - /* tell the scheduler if we have one */ - if (element->sched) { - if (gst_scheduler_state_transition (element->sched, element, - old_transition) != GST_STATE_SUCCESS) { - GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, - "scheduler could not change state"); - goto failure; - } - } - - /* tell our parent about the state change */ - if (parent && GST_IS_BIN (parent)) { - gst_bin_child_state_change (GST_BIN (parent), old_state, - GST_STATE (element), element); - } - /* at this point the state of the element could have changed again */ - - g_signal_emit (G_OBJECT (element), gst_element_signals[STATE_CHANGE], - 0, old_state, GST_STATE (element)); - - /* signal the state change in case somebody is waiting for us */ - g_cond_signal (element->state_cond); - - gst_object_unref (GST_OBJECT (element)); - return GST_STATE_SUCCESS; - -failure: - /* undo the state change */ - GST_STATE (element) = old_state; - GST_STATE_PENDING (element) = old_pending; - gst_object_unref (GST_OBJECT (element)); - - return GST_STATE_FAILURE; + return result; } /** @@ -3055,23 +1680,15 @@ gst_element_dispose (GObject * object) element->numsrcpads = 0; element->numsinkpads = 0; element->numpads = 0; - if (element->state_mutex) - g_mutex_free (element->state_mutex); - element->state_mutex = NULL; if (element->state_cond) g_cond_free (element->state_cond); element->state_cond = NULL; - if (element->prop_value_queue) - g_async_queue_unref (element->prop_value_queue); - element->prop_value_queue = NULL; - if (element->property_mutex) - g_mutex_free (element->property_mutex); - element->property_mutex = NULL; - - gst_object_replace ((GstObject **) & element->sched, NULL); + gst_object_replace ((GstObject **) & element->manager, NULL); gst_object_replace ((GstObject **) & element->clock, NULL); + GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "dispose parent"); + G_OBJECT_CLASS (parent_class)->dispose (object); } @@ -3210,425 +1827,76 @@ gst_element_restore_thyself (GstObject * object, xmlNodePtr self) } #endif /* GST_DISABLE_LOADSAVE */ -/** - * gst_element_yield: - * @element: a #GstElement to yield. - * - * Requests a yield operation for the element. The scheduler will typically - * give control to another element. - */ -void -gst_element_yield (GstElement * element) +static void +gst_element_set_manager_func (GstElement * element, GstPipeline * manager) { - if (GST_ELEMENT_SCHED (element)) { - gst_scheduler_yield (GST_ELEMENT_SCHED (element), element); - } + g_return_if_fail (GST_IS_ELEMENT (element)); + + GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, element, "setting manager to %p", + manager); + + GST_LOCK (element); + gst_object_replace ((GstObject **) & GST_ELEMENT_MANAGER (element), + GST_OBJECT (manager)); + GST_UNLOCK (element); } /** - * gst_element_interrupt: - * @element: a #GstElement to interrupt. + * gst_element_set_manager: + * @element: a #GstElement to set the manager of. + * @manager: the #GstManager to set. * - * Requests the scheduler of this element to interrupt the execution of - * this element and scheduler another one. - * - * Returns: TRUE if the element should exit its chain/loop/get - * function ASAP, depending on the scheduler implementation. - */ -gboolean -gst_element_interrupt (GstElement * element) -{ - if (GST_ELEMENT_SCHED (element)) { - return gst_scheduler_interrupt (GST_ELEMENT_SCHED (element), element); - } else - return TRUE; -} - -/** - * gst_element_set_scheduler: - * @element: a #GstElement to set the scheduler of. - * @sched: the #GstScheduler to set. - * - * Sets the scheduler of the element. For internal use only, unless you're + * Sets the manager of the element. For internal use only, unless you're * writing a new bin subclass. */ void -gst_element_set_scheduler (GstElement * element, GstScheduler * sched) +gst_element_set_manager (GstElement * element, GstPipeline * manager) { - g_return_if_fail (GST_IS_ELEMENT (element)); - - GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, element, "setting scheduler to %p", - sched); - - gst_object_replace ((GstObject **) & GST_ELEMENT_SCHED (element), - GST_OBJECT (sched)); -} - -/** - * gst_element_get_scheduler: - * @element: a #GstElement to get the scheduler of. - * - * Returns the scheduler of the element. - * - * Returns: the element's #GstScheduler. - */ -GstScheduler * -gst_element_get_scheduler (GstElement * element) -{ - g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); - - return GST_ELEMENT_SCHED (element); -} - -/** - * gst_element_set_loop_function: - * @element: a #GstElement to set the loop function of. - * @loop: Pointer to #GstElementLoopFunction. - * - * This sets the loop function for the element. The function pointed to - * can deviate from the GstElementLoopFunction definition in type of - * pointer only. - * - * NOTE: in order for this to take effect, the current loop function *must* - * exit. Assuming the loop function itself is the only one who will cause - * a new loopfunc to be assigned, this should be no problem. - */ -void -gst_element_set_loop_function (GstElement * element, - GstElementLoopFunction loop) -{ - gboolean need_notify = FALSE; + GstElementClass *oclass; g_return_if_fail (GST_IS_ELEMENT (element)); - /* if the element changed from loop based to chain/get based - * or vice versa, we need to inform the scheduler about that */ - if ((element->loopfunc == NULL && loop != NULL) || - (element->loopfunc != NULL && loop == NULL)) { - need_notify = TRUE; - } + oclass = GST_ELEMENT_GET_CLASS (element); - /* set the loop function */ - element->loopfunc = loop; - - if (need_notify) { - /* set the NEW_LOOPFUNC flag so everyone knows to go try again */ - GST_FLAG_SET (element, GST_ELEMENT_NEW_LOOPFUNC); - - if (GST_ELEMENT_SCHED (element)) { - gst_scheduler_scheduling_change (GST_ELEMENT_SCHED (element), element); - } - } -} -static inline void -gst_element_emit_found_tag (GstElement * element, GstElement * source, - const GstTagList * tag_list) -{ - gst_object_ref (GST_OBJECT (element)); - g_signal_emit (element, gst_element_signals[FOUND_TAG], 0, source, tag_list); - gst_object_unref (GST_OBJECT (element)); -} -static void -gst_element_found_tag_func (GstElement * element, GstElement * source, - const GstTagList * tag_list) -{ - /* tell the parent */ - if (GST_OBJECT_PARENT (element)) { - GST_CAT_LOG_OBJECT (GST_CAT_EVENT, element, "forwarding tag event to %s", - GST_OBJECT_NAME (GST_OBJECT_PARENT (element))); - gst_element_emit_found_tag (GST_ELEMENT (GST_OBJECT_PARENT (element)), - source, tag_list); - } + if (oclass->set_manager) + oclass->set_manager (element, manager); } /** - * gst_element_found_tags: - * @element: the element that found the tags - * @tag_list: the found tags + * gst_element_get_manager: + * @element: a #GstElement to get the manager of. * - * This function emits the found_tags signal. This is a recursive signal, so - * every parent will emit that signal, too, before this function returns. - * Only emit this signal, when you extracted these tags out of the data stream, - * not when you handle an event. + * Returns the manager of the element. + * + * Returns: the element's #GstPipeline. */ -void -gst_element_found_tags (GstElement * element, const GstTagList * tag_list) +GstPipeline * +gst_element_get_manager (GstElement * element) { - gst_element_emit_found_tag (element, element, tag_list); + GstPipeline *result = NULL; + + g_return_val_if_fail (GST_IS_ELEMENT (element), result); + + GST_LOCK (element); + result = GST_ELEMENT_MANAGER (element); + GST_UNLOCK (element); + + return result; } -/** - * gst_element_found_tags_for_pad: - * @element: element that found the tag - * @pad: src pad the tags correspond to - * @timestamp: time the tags were found - * @list: the taglist - * - * This is a convenience routine for tag finding. Most of the time you only - * want to push the found tags down one pad, in that case this function is for - * you. It takes ownership of the taglist, emits the found-tag signal and - * pushes a tag event down the pad. - * This function may not be used in a #GstPadGetFunction, because it calls - * gst_pad_push(). In those functions, call gst_element_found_tags(), create a - * tag event with gst_event_new_tag() and return that from your - * #GstPadGetFunction. - */ -void -gst_element_found_tags_for_pad (GstElement * element, GstPad * pad, - GstClockTime timestamp, GstTagList * list) +GstTask * +gst_element_create_task (GstElement * element, GstTaskFunction func, + gpointer data) { - GstEvent *tag_event; + GstPipeline *pipeline; + GstTask *result = NULL; - g_return_if_fail (GST_IS_ELEMENT (element)); - g_return_if_fail (GST_IS_REAL_PAD (pad)); - g_return_if_fail (GST_PAD_DIRECTION (pad) == GST_PAD_SRC); - g_return_if_fail (element == GST_PAD_PARENT (pad)); - g_return_if_fail (list != NULL); - - tag_event = gst_event_new_tag (list); - GST_EVENT_TIMESTAMP (tag_event) = timestamp; - gst_element_found_tags (element, gst_event_tag_get_list (tag_event)); - if (GST_PAD_IS_USABLE (pad)) { - gst_pad_push (pad, GST_DATA (tag_event)); - } else { - gst_data_unref (GST_DATA (tag_event)); + GST_LOCK (element); + pipeline = GST_ELEMENT_MANAGER (element); + if (pipeline) { + result = gst_scheduler_create_task (pipeline->scheduler, func, data); } -} - -static inline void -gst_element_set_eos_recursive (GstElement * element) -{ - /* this function is only called, when we were in PLAYING before. So every - parent that's PAUSED was PLAYING before. That means it has reached EOS. */ - GstElement *parent; - - GST_CAT_DEBUG (GST_CAT_EVENT, "setting recursive EOS on %s", - GST_OBJECT_NAME (element)); - g_signal_emit (G_OBJECT (element), gst_element_signals[EOS], 0); - - if (!GST_OBJECT_PARENT (element)) - return; - - parent = GST_ELEMENT (GST_OBJECT_PARENT (element)); - if (GST_STATE (parent) == GST_STATE_PAUSED) - gst_element_set_eos_recursive (parent); -} - -/** - * gst_element_set_eos: - * @element: a #GstElement to set to the EOS state. - * - * Perform the actions needed to bring the element in the EOS state. - */ -void -gst_element_set_eos (GstElement * element) -{ - g_return_if_fail (GST_IS_ELEMENT (element)); - - GST_CAT_DEBUG (GST_CAT_EVENT, "setting EOS on element %s", - GST_OBJECT_NAME (element)); - - if (GST_STATE (element) == GST_STATE_PLAYING) { - gst_element_set_state (element, GST_STATE_PAUSED); - gst_element_set_eos_recursive (element); - } else { - g_signal_emit (G_OBJECT (element), gst_element_signals[EOS], 0); - } -} - - -/** - * 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 ""; -} - -static void -gst_element_populate_std_props (GObjectClass * klass, const gchar * prop_name, - guint arg_id, GParamFlags flags) -{ - GQuark prop_id = g_quark_from_string (prop_name); - GParamSpec *pspec; - - static GQuark fd_id = 0; - static GQuark blocksize_id; - static GQuark bytesperread_id; - static GQuark dump_id; - static GQuark filesize_id; - static GQuark mmapsize_id; - static GQuark location_id; - static GQuark offset_id; - static GQuark silent_id; - static GQuark touch_id; - - if (!fd_id) { - fd_id = g_quark_from_static_string ("fd"); - blocksize_id = g_quark_from_static_string ("blocksize"); - bytesperread_id = g_quark_from_static_string ("bytesperread"); - dump_id = g_quark_from_static_string ("dump"); - filesize_id = g_quark_from_static_string ("filesize"); - mmapsize_id = g_quark_from_static_string ("mmapsize"); - location_id = g_quark_from_static_string ("location"); - offset_id = g_quark_from_static_string ("offset"); - silent_id = g_quark_from_static_string ("silent"); - touch_id = g_quark_from_static_string ("touch"); - } - - if (prop_id == fd_id) { - pspec = g_param_spec_int ("fd", "File-descriptor", - "File-descriptor for the file being read", 0, G_MAXINT, 0, flags); - } else if (prop_id == blocksize_id) { - pspec = g_param_spec_ulong ("blocksize", "Block Size", - "Block size to read per buffer", 0, G_MAXULONG, 4096, flags); - - } else if (prop_id == bytesperread_id) { - pspec = g_param_spec_int ("bytesperread", "Bytes per read", - "Number of bytes to read per buffer", G_MININT, G_MAXINT, 0, flags); - - } else if (prop_id == dump_id) { - pspec = g_param_spec_boolean ("dump", "Dump", - "Dump bytes to stdout", FALSE, flags); - - } else if (prop_id == filesize_id) { - pspec = g_param_spec_int64 ("filesize", "File Size", - "Size of the file being read", 0, G_MAXINT64, 0, flags); - - } else if (prop_id == mmapsize_id) { - pspec = g_param_spec_ulong ("mmapsize", "mmap() Block Size", - "Size in bytes of mmap()d regions", 0, G_MAXULONG, 4 * 1048576, flags); - - } else if (prop_id == location_id) { - pspec = g_param_spec_string ("location", "File Location", - "Location of the file to read", NULL, flags); - - } else if (prop_id == offset_id) { - pspec = g_param_spec_int64 ("offset", "File Offset", - "Byte offset of current read pointer", 0, G_MAXINT64, 0, flags); - - } else if (prop_id == silent_id) { - pspec = g_param_spec_boolean ("silent", "Silent", "Don't produce events", - FALSE, flags); - - } else if (prop_id == touch_id) { - pspec = g_param_spec_boolean ("touch", "Touch read data", - "Touch data to force disk read before " "push ()", TRUE, flags); - } else { - g_warning ("Unknown - 'standard' property '%s' id %d from klass %s", - prop_name, arg_id, g_type_name (G_OBJECT_CLASS_TYPE (klass))); - pspec = NULL; - } - - if (pspec) { - g_object_class_install_property (klass, arg_id, pspec); - } -} - -/** - * gst_element_class_install_std_props: - * @klass: the #GstElementClass to add the properties to. - * @first_name: the name of the first property. - * in a NULL terminated - * @...: the id and flags of the first property, followed by - * further 'name', 'id', 'flags' triplets and terminated by NULL. - * - * Adds a list of standardized properties with types to the @klass. - * the id is for the property switch in your get_prop method, and - * the flags determine readability / writeability. - **/ -void -gst_element_class_install_std_props (GstElementClass * klass, - const gchar * first_name, ...) -{ - const char *name; - - va_list args; - - g_return_if_fail (GST_IS_ELEMENT_CLASS (klass)); - - va_start (args, first_name); - - name = first_name; - - while (name) { - int arg_id = va_arg (args, int); - int flags = va_arg (args, int); - - gst_element_populate_std_props ((GObjectClass *) klass, name, arg_id, - flags); - - name = va_arg (args, char *); - } - - va_end (args); -} - -/** - * gst_element_get_managing_bin: - * @element: a #GstElement to get the managing bin of. - * - * Gets the managing bin (a pipeline or a thread, for example) of an element. - * - * Returns: the #GstBin, or NULL on failure. - **/ -GstBin * -gst_element_get_managing_bin (GstElement * element) -{ - GstBin *bin; - - g_return_val_if_fail (element != NULL, NULL); - - bin = GST_BIN (gst_object_get_parent (GST_OBJECT (element))); - - while (bin && !GST_FLAG_IS_SET (GST_OBJECT (bin), GST_BIN_FLAG_MANAGER)) - bin = GST_BIN (gst_object_get_parent (GST_OBJECT (bin))); - - return bin; + GST_UNLOCK (element); + + return result; } diff --git a/gst/gstelement.h b/gst/gstelement.h index 4fee1b9616..09ced64e46 100644 --- a/gst/gstelement.h +++ b/gst/gstelement.h @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include 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__ */ diff --git a/gst/gstelementfactory.c b/gst/gstelementfactory.c index 32fefb51e6..a719a8124f 100644 --- a/gst/gstelementfactory.c +++ b/gst/gstelementfactory.c @@ -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); diff --git a/gst/gstevent.h b/gst/gstevent.h index c8e4b5a7ba..3e91c24cac 100644 --- a/gst/gstevent.h +++ b/gst/gstevent.h @@ -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 { diff --git a/gst/gstindex.c b/gst/gstindex.c index ec28790b23..b5164d28ab 100644 --- a/gst/gstindex.c +++ b/gst/gstindex.c @@ -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)); diff --git a/gst/gstinfo.c b/gst/gstinfo.c index e017ba7031..5bd205e2d5 100644 --- a/gst/gstinfo.c +++ b/gst/gstinfo.c @@ -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", diff --git a/gst/gstobject.c b/gst/gstobject.c index 536c6f4c4b..00b73c2b07 100644 --- a/gst/gstobject.c +++ b/gst/gstobject.c @@ -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; } diff --git a/gst/gstobject.h b/gst/gstobject.h index bbb9dc8181..47a63931a2 100644 --- a/gst/gstobject.h +++ b/gst/gstobject.h @@ -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 */ diff --git a/gst/gstpad.c b/gst/gstpad.c index e4ab30e4fa..37607b862b 100644 --- a/gst/gstpad.c +++ b/gst/gstpad.c @@ -47,28 +47,6 @@ GST_DEBUG_CATEGORY_STATIC (debug_dataflow); }G_STMT_END #define GST_CAT_DEFAULT GST_CAT_PADS -struct _GstPadLink -{ - GType type; - - gboolean bla; - gboolean srcnotify; - gboolean sinknotify; - - GstPad *srcpad; - GstPad *sinkpad; - - GstCaps *srccaps; - GstCaps *sinkcaps; - GstCaps *filtercaps; - GstCaps *caps; - - GstPadFixateFunction app_fixate; - - gboolean engaged; - GstData *temp_store; /* used only when we invented a DISCONT */ -}; - enum { TEMPL_PAD_CREATED, @@ -87,11 +65,6 @@ static void gst_pad_init (GstPad * pad); static void gst_pad_dispose (GObject * object); static void gst_pad_set_pad_template (GstPad * pad, GstPadTemplate * templ); -static GstCaps *_gst_pad_default_fixate_func (GstPad * pad, - const GstCaps * caps); - -static gboolean gst_pad_link_try (GstPadLink * link); -static void gst_pad_link_free (GstPadLink * link); #ifndef GST_DISABLE_LOADSAVE static xmlNodePtr gst_pad_save_thyself (GstObject * object, xmlNodePtr parent); @@ -155,7 +128,7 @@ enum { REAL_LINKED, REAL_UNLINKED, - REAL_FIXATE, + REAL_REQUEST_LINK, /* FILL ME */ REAL_LAST_SIGNAL }; @@ -172,8 +145,6 @@ static void gst_real_pad_class_init (GstRealPadClass * klass); static void gst_real_pad_init (GstRealPad * pad); static void gst_real_pad_dispose (GObject * object); -static gboolean _gst_real_pad_fixate_accumulator (GSignalInvocationHint * ihint, - GValue * return_accu, const GValue * handler_return, gpointer dummy); static void gst_real_pad_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_real_pad_get_property (GObject * object, guint prop_id, @@ -225,12 +196,10 @@ gst_real_pad_class_init (GstRealPadClass * klass) g_signal_new ("unlinked", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRealPadClass, unlinked), NULL, NULL, gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_PAD); - gst_real_pad_signals[REAL_FIXATE] = - g_signal_new ("fixate", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GstRealPadClass, appfixatefunc), - _gst_real_pad_fixate_accumulator, NULL, - gst_marshal_BOXED__BOXED, GST_TYPE_CAPS, 1, - GST_TYPE_CAPS | G_SIGNAL_TYPE_STATIC_SCOPE); + gst_real_pad_signals[REAL_REQUEST_LINK] = + g_signal_new ("request_link", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRealPadClass, request_link), NULL, + NULL, gst_marshal_VOID__OBJECT, G_TYPE_NONE, 0); g_object_class_install_property (G_OBJECT_CLASS (klass), REAL_ARG_ACTIVE, g_param_spec_boolean ("active", "Active", "Whether the pad is active.", @@ -245,30 +214,14 @@ gst_real_pad_class_init (GstRealPadClass * klass) gstobject_class->path_string_separator = "."; } -static gboolean -_gst_real_pad_fixate_accumulator (GSignalInvocationHint * ihint, - GValue * return_accu, const GValue * handler_return, gpointer dummy) -{ - if (gst_value_get_caps (handler_return)) { - g_value_copy (handler_return, return_accu); - /* stop emission if something was returned */ - return FALSE; - } - return TRUE; -} - static void gst_real_pad_init (GstRealPad * pad) { - pad->direction = GST_PAD_UNKNOWN; pad->peer = NULL; pad->chainfunc = NULL; pad->getfunc = NULL; - pad->chainhandler = NULL; - pad->gethandler = NULL; - pad->ghostpads = NULL; pad->caps = NULL; @@ -287,6 +240,11 @@ gst_real_pad_init (GstRealPad * pad) GST_FLAG_SET (pad, GST_PAD_DISABLED); GST_FLAG_UNSET (pad, GST_PAD_NEGOTIATING); + pad->stream_lock = g_mutex_new (); + pad->stream_cond = g_cond_new (); + + pad->block_cond = g_cond_new (); + gst_probe_dispatcher_init (&pad->probedisp); } @@ -344,8 +302,6 @@ gst_pad_custom_new (GType type, const gchar * name, GstPadDirection direction) { GstRealPad *pad; - g_return_val_if_fail (direction != GST_PAD_UNKNOWN, NULL); - pad = g_object_new (type, NULL); gst_object_set_name (GST_OBJECT (pad), name); GST_RPAD_DIRECTION (pad) = direction; @@ -414,7 +370,6 @@ gst_pad_new_from_template (GstPadTemplate * templ, const gchar * name) templ, name); } -/* FIXME 0.9: GST_PAD_UNKNOWN needs to die! */ /** * gst_pad_get_direction: * @pad: a #GstPad to get the direction of. @@ -426,12 +381,15 @@ gst_pad_new_from_template (GstPadTemplate * templ, const gchar * name) GstPadDirection gst_pad_get_direction (GstPad * pad) { + GstPadDirection result; + g_return_val_if_fail (GST_IS_PAD (pad), GST_PAD_UNKNOWN); - if (GST_IS_REAL_PAD (pad)) - return GST_PAD_DIRECTION (pad); - else - return GST_PAD_UNKNOWN; + GST_LOCK (pad); + result = GST_PAD_DIRECTION (pad); + GST_UNLOCK (pad); + + return result; } /** @@ -440,44 +398,57 @@ gst_pad_get_direction (GstPad * pad) * @active: TRUE to activate the pad. * * Activates or deactivates the given pad. + * + * Returns: TRUE if the operation was successfull. */ -void +gboolean gst_pad_set_active (GstPad * pad, gboolean active) { GstRealPad *realpad; gboolean old; - GstPadLink *link; + gboolean result = FALSE; + GstPadActivateFunction activatefunc; - g_return_if_fail (GST_IS_PAD (pad)); + g_return_val_if_fail (GST_IS_PAD (pad), result); + GST_LOCK (pad); old = GST_PAD_IS_ACTIVE (pad); if (old == active) - return; + goto done; realpad = GST_PAD_REALIZE (pad); - if (active) { - GST_CAT_DEBUG (GST_CAT_PADS, "activating pad %s:%s", - GST_DEBUG_PAD_NAME (realpad)); - GST_FLAG_UNSET (realpad, GST_PAD_DISABLED); - } else { + /* make sure data is disallowed when going inactive */ + if (!active) { GST_CAT_DEBUG (GST_CAT_PADS, "de-activating pad %s:%s", GST_DEBUG_PAD_NAME (realpad)); GST_FLAG_SET (realpad, GST_PAD_DISABLED); - } - link = GST_RPAD_LINK (realpad); - if (link) { - if (link->temp_store) { - GST_CAT_INFO (GST_CAT_PADS, - "deleting cached data %p from bufpen of pad %s:%s", link->temp_store, - GST_DEBUG_PAD_NAME (realpad)); - gst_data_unref (link->temp_store); - link->temp_store = NULL; - } + /* unlock blocked pads so element can resume and stop */ + GST_PAD_BLOCK_SIGNAL (pad); } - g_object_notify (G_OBJECT (realpad), "active"); + activatefunc = realpad->activatefunc; + if (activatefunc) { + /* unlock so element can sync */ + GST_UNLOCK (pad); + result = activatefunc (pad, active); + /* and lock again */ + GST_LOCK (pad); + } else { + result = TRUE; + } + + /* when going to active alow data passing now */ + if (active && result == TRUE) { + GST_CAT_DEBUG (GST_CAT_PADS, "activating pad %s:%s", + GST_DEBUG_PAD_NAME (realpad)); + GST_FLAG_UNSET (realpad, GST_PAD_DISABLED); + } +done: + GST_UNLOCK (pad); + + return result; } /** @@ -491,43 +462,157 @@ gst_pad_set_active (GstPad * pad, gboolean active) gboolean gst_pad_is_active (GstPad * pad) { - g_return_val_if_fail (GST_IS_PAD (pad), FALSE); + gboolean result = FALSE; - return !GST_FLAG_IS_SET (pad, GST_PAD_DISABLED); + g_return_val_if_fail (GST_IS_PAD (pad), result); + + GST_LOCK (pad); + result = !GST_FLAG_IS_SET (pad, GST_PAD_DISABLED); + GST_UNLOCK (pad); + + return result; } /** - * gst_pad_set_name: - * @pad: a #GstPad to set the name of. - * @name: the name of the pad. + * gst_pad_set_blocked_async: + * @pad: the #GstPad to block or unblock + * @blocked: boolean indicating we should block or unblock + * @callback: #GstPadBlockCallback that will be called when the + * operation succeeds. + * @user_data: user data passed to the callback * - * Sets the name of a pad. If name is NULL, then a guaranteed unique - * name will be assigned. + * Blocks or unblocks the dataflow on a pad. The provided callback + * is called when the operation succeeds. This can take a while as + * the pad can only become blocked when real dataflow is happening. + * When the pipeline is stalled, for example in PAUSED, this can + * take an indeterminate amount of time. + * You can pass NULL as the callback to make this call block. Be + * carefull with this blocking call as it might not return for + * reasons stated above. + * + * Returns: TRUE if the pad could be blocked. This function can fail + * wrong parameters were passed or the pad was already in the + * requested state. + */ +gboolean +gst_pad_set_blocked_async (GstPad * pad, gboolean blocked, + GstPadBlockCallback callback, gpointer user_data) +{ + gboolean result = TRUE; + gboolean was_blocked; + GstRealPad *realpad; + + /* only for real pads for now, need to transfer the lock + * to the realized pad */ + g_return_val_if_fail (GST_IS_REAL_PAD (pad), FALSE); + + GST_LOCK (pad); + realpad = GST_PAD_REALIZE (pad); + + /* beware for turning flags into booleans */ + was_blocked = !!GST_RPAD_IS_BLOCKED (realpad); + + if (was_blocked == blocked) { + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "pad %s:%s was in right state", + GST_DEBUG_PAD_NAME (pad)); + result = FALSE; + goto done; + } + if (blocked) { + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "blocking pad %s:%s", + GST_DEBUG_PAD_NAME (pad)); + + GST_FLAG_SET (realpad, GST_PAD_BLOCKED); + realpad->block_callback = callback; + realpad->block_data = user_data; + if (!callback) { + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "waiting for block"); + GST_PAD_BLOCK_WAIT (pad); + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "blocked"); + } + } else { + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "unblocking pad %s:%s", + GST_DEBUG_PAD_NAME (pad)); + + GST_FLAG_UNSET (realpad, GST_PAD_BLOCKED); + + realpad->block_callback = callback; + realpad->block_data = user_data; + + if (callback) { + GST_PAD_BLOCK_SIGNAL (pad); + } else { + GST_PAD_BLOCK_SIGNAL (pad); + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "waiting for unblock"); + GST_PAD_BLOCK_WAIT (pad); + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "unblocked"); + } + } +done: + GST_UNLOCK (pad); + + return result; +} + +/** + * gst_pad_set_blocked: + * @pad: the #GstPad to block or unblock + * @blocked: boolean indicating we should block or unblock + * + * Blocks or unblocks the dataflow on a pad. This function is + * a shortcut for @gst_pad_set_blocked_async() with a NULL + * callback. + * + * Returns: TRUE if the pad could be blocked. This function can fail + * wrong parameters were passed or the pad was already in the + * requested state. + */ +gboolean +gst_pad_set_blocked (GstPad * pad, gboolean blocked) +{ + return gst_pad_set_blocked_async (pad, blocked, NULL, NULL); +} + +/** + * gst_pad_is_blocked: + * @pad: the #GstPad to query + * + * Checks if the pad is blocked or not. This function returns the + * last requested state of the pad. It is not certain that the pad + * is actually blocked at this point. + * + * Returns: TRUE if the pad is blocked. + */ +gboolean +gst_pad_is_blocked (GstPad * pad) +{ + gboolean result = FALSE; + + g_return_val_if_fail (GST_IS_PAD (pad), result); + + GST_LOCK (pad); + result = GST_FLAG_IS_SET (pad, GST_PAD_BLOCKED); + GST_UNLOCK (pad); + + return result; +} + +/** + * gst_pad_set_activate_function: + * @pad: a real sink #GstPad. + * @chain: the #GstPadActivateFunction to set. + * + * Sets the given activate function for the pad. The activate function is called to + * start or stop dataflow on a pad. */ void -gst_pad_set_name (GstPad * pad, const gchar * name) +gst_pad_set_activate_function (GstPad * pad, GstPadActivateFunction activate) { - g_return_if_fail (GST_IS_PAD (pad)); + g_return_if_fail (GST_IS_REAL_PAD (pad)); - gst_object_set_name (GST_OBJECT (pad), name); -} - -/* FIXME 0.9: This function must die */ -/** - * gst_pad_get_name: - * @pad: a #GstPad to get the name of. - * - * Gets the name of a pad. - * - * Returns: the name of the pad. This is not a newly allocated pointer - * so you must not free it. - */ -const gchar * -gst_pad_get_name (GstPad * pad) -{ - g_return_val_if_fail (GST_IS_PAD (pad), NULL); - - return GST_OBJECT_NAME (pad); + GST_RPAD_ACTIVATEFUNC (pad) = activate; + GST_CAT_DEBUG (GST_CAT_PADS, "activatefunc for %s:%s set to %s", + GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (activate)); } /** @@ -581,7 +666,6 @@ void gst_pad_set_event_function (GstPad * pad, GstPadEventFunction event) { g_return_if_fail (GST_IS_REAL_PAD (pad)); - g_return_if_fail (GST_RPAD_DIRECTION (pad) == GST_PAD_SRC); GST_RPAD_EVENTFUNC (pad) = event; @@ -816,26 +900,14 @@ gst_pad_set_formats_function (GstPad * pad, GstPadFormatsFunction formats) * @link: the #GstPadLinkFunction to set. * * Sets the given link function for the pad. It will be called when the pad is - * linked or relinked with caps. The caps passed to the link function are - * guaranteed to be fixed. This means that you can assume that the caps is not - * ANY or EMPTY, and that there is exactly one structure in the caps, and that - * all the fields in the structure are fixed. + * linked or relinked with caps. The caps passed to the link function is + * the filtered caps for the connnection. It can contain a non fixed caps. * - * The return value GST_PAD_LINK_OK should be used when the caps are acceptable, - * and you've extracted all the necessary information from the caps and set the - * element's internal state appropriately. + * The return value GST_PAD_LINK_OK should be used when the connection can be + * made. * - * The return value GST_PAD_LINK_REFUSED should be used when the caps are - * unacceptable for whatever reason. - * - * The return value GST_PAD_LINK_DELAYED should be used when the element is in a - * state where it can't determine whether the caps are acceptable or not. This - * is often used if the element needs to open a device or process data before - * determining acceptable caps. - * - * @link must not call gst_caps_try_set_caps() on the pad that was specified as - * a parameter, although it may (and often should) call gst_caps_try_set_caps() - * on other pads. + * The return value GST_PAD_LINK_REFUSED should be used when the connection + * cannot be made for some reason. */ void gst_pad_set_link_function (GstPad * pad, GstPadLinkFunction link) @@ -865,31 +937,6 @@ gst_pad_set_unlink_function (GstPad * pad, GstPadUnlinkFunction unlink) GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (unlink)); } -/** - * gst_pad_set_fixate_function: - * @pad: a real #GstPad. - * @fixate: the #GstPadFixateFunction to set. - * - * Sets the given fixate function for the pad. Its job is to narrow down the - * possible caps for a connection. Fixate functions are called with a const - * caps, and should return a caps that is a strict subset of the given caps. - * That is, @fixate should create a caps that is "more fixed" than previously, - * but it does not have to return fixed caps. If @fixate can't provide more - * fixed caps, it should return %NULL. - * - * Note that @fixate will only be called after the "fixate" signal is emitted, - * and only if the caps are still non-fixed. - */ -void -gst_pad_set_fixate_function (GstPad * pad, GstPadFixateFunction fixate) -{ - g_return_if_fail (GST_IS_REAL_PAD (pad)); - - GST_RPAD_FIXATEFUNC (pad) = fixate; - GST_CAT_DEBUG (GST_CAT_PADS, "fixatefunc for %s:%s set to %s", - GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (fixate)); -} - /** * gst_pad_set_getcaps_function: * @pad: a real #GstPad. @@ -910,8 +957,7 @@ gst_pad_set_fixate_function (GstPad * pad, GstPadFixateFunction fixate) * allowed caps on other pads. For demuxers and decoders, the caps returned by * the srcpad's getcaps function is directly related to the stream data. Again, * @getcaps should return the most specific caps it reasonably can, since this - * helps with autoplugging. However, the returned caps should not depend on the - * stream type currently negotiated for @pad. + * helps with autoplugging. * * Note that the return value from @getcaps is owned by the caller. */ @@ -945,8 +991,6 @@ gst_pad_set_bufferalloc_function (GstPad * pad, GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (bufalloc)); } -/* FIXME 0.9: Do we actually want to allow the case where src and sink are - switched? */ /** * gst_pad_unlink: * @srcpad: the source #GstPad to unlink. @@ -959,7 +1003,6 @@ void gst_pad_unlink (GstPad * srcpad, GstPad * sinkpad) { GstRealPad *realsrc, *realsink; - GstScheduler *src_sched, *sink_sched; g_return_if_fail (GST_IS_PAD (srcpad)); g_return_if_fail (GST_IS_PAD (sinkpad)); @@ -974,14 +1017,6 @@ gst_pad_unlink (GstPad * srcpad, GstPad * sinkpad) g_return_if_fail (GST_RPAD_PEER (realsrc) != NULL); g_return_if_fail (GST_RPAD_PEER (realsink) == realsrc); - if ((GST_RPAD_DIRECTION (realsrc) == GST_PAD_SINK) && - (GST_RPAD_DIRECTION (realsink) == GST_PAD_SRC)) { - GstRealPad *temppad; - - temppad = realsrc; - realsrc = realsink; - realsink = temppad; - } g_return_if_fail ((GST_RPAD_DIRECTION (realsrc) == GST_PAD_SRC) && (GST_RPAD_DIRECTION (realsink) == GST_PAD_SINK)); @@ -992,23 +1027,13 @@ gst_pad_unlink (GstPad * srcpad, GstPad * sinkpad) GST_RPAD_UNLINKFUNC (realsink) (GST_PAD (realsink)); } - /* get the schedulers before we unlink */ - src_sched = gst_pad_get_scheduler (GST_PAD (realsrc)); - sink_sched = gst_pad_get_scheduler (GST_PAD (realsink)); - - if (GST_RPAD_LINK (realsrc)) - gst_pad_link_free (GST_RPAD_LINK (realsrc)); - /* first clear peers */ GST_RPAD_PEER (realsrc) = NULL; GST_RPAD_PEER (realsink) = NULL; - GST_RPAD_LINK (realsrc) = NULL; - GST_RPAD_LINK (realsink) = NULL; - /* now tell the scheduler */ - if (src_sched && src_sched == sink_sched) { - gst_scheduler_pad_unlink (src_sched, GST_PAD (realsrc), GST_PAD (realsink)); - } + /* clear filter, note that we leave the pad caps as they are */ + gst_caps_replace (&GST_RPAD_APPFILTER (realsrc), NULL); + gst_caps_replace (&GST_RPAD_APPFILTER (realsink), NULL); /* hold a reference, as they can go away in the signal handlers */ gst_object_ref (GST_OBJECT (realsrc)); @@ -1044,701 +1069,6 @@ gst_pad_is_linked (GstPad * pad) return GST_PAD_PEER (pad) != NULL; } -static gboolean -gst_pad_check_schedulers (GstRealPad * realsrc, GstRealPad * realsink) -{ - GstScheduler *src_sched, *sink_sched; - gint num_decoupled = 0; - - src_sched = gst_pad_get_scheduler (GST_PAD (realsrc)); - sink_sched = gst_pad_get_scheduler (GST_PAD (realsink)); - - if (src_sched && sink_sched) { - if (GST_FLAG_IS_SET (GST_PAD_PARENT (realsrc), GST_ELEMENT_DECOUPLED)) - num_decoupled++; - if (GST_FLAG_IS_SET (GST_PAD_PARENT (realsink), GST_ELEMENT_DECOUPLED)) - num_decoupled++; - - if (src_sched != sink_sched && num_decoupled != 1) { - return FALSE; - } - } - return TRUE; -} - -#define GST_PAD_LINK_SRC(pad) ((GST_PAD_IS_SRC (pad)) ? (pad) : GST_PAD_PEER (pad)) -#define GST_PAD_LINK_SINK(pad) ((GST_PAD_IS_SINK (pad)) ? (pad) : GST_PAD_PEER (pad)) - -static GstPadLink * -gst_pad_link_new (void) -{ - GstPadLink *link; - - link = g_new0 (GstPadLink, 1); - link->sinknotify = TRUE; - link->srcnotify = TRUE; - - link->engaged = FALSE; - return link; -} - -static void -gst_pad_link_free (GstPadLink * link) -{ - if (link->srccaps) - gst_caps_free (link->srccaps); - if (link->sinkcaps) - gst_caps_free (link->sinkcaps); - if (link->filtercaps) - gst_caps_free (link->filtercaps); - if (link->caps) - gst_caps_free (link->caps); - if (link->temp_store) - gst_data_unref (link->temp_store); -#ifdef USE_POISONING - memset (link, 0xff, sizeof (*link)); -#endif - g_free (link); -} - -static void -gst_pad_link_intersect (GstPadLink * link) -{ - GstCaps *pad_intersection; - - if (link->caps) - gst_caps_free (link->caps); - - GST_DEBUG ("intersecting link from %s:%s to %s:%s", - GST_DEBUG_PAD_NAME (link->srcpad), GST_DEBUG_PAD_NAME (link->sinkpad)); - GST_DEBUG ("... srccaps %" GST_PTR_FORMAT, link->srccaps); - GST_DEBUG ("... sinkcaps %" GST_PTR_FORMAT, link->sinkcaps); - GST_DEBUG ("... filtercaps %" GST_PTR_FORMAT, link->filtercaps); - - pad_intersection = gst_caps_intersect (link->srccaps, link->sinkcaps); - - if (link->filtercaps) { - GST_DEBUG ("unfiltered intersection %" GST_PTR_FORMAT, pad_intersection); - link->caps = gst_caps_intersect (pad_intersection, link->filtercaps); - gst_caps_free (pad_intersection); - } else { - link->caps = pad_intersection; - } - - GST_DEBUG ("filtered intersection %" GST_PTR_FORMAT, link->caps); -} - -static gboolean -gst_pad_link_ready_for_negotiation (GstPadLink * link) -{ - GstElement *parent; - - parent = GST_PAD_PARENT (link->srcpad); - if (!parent || GST_STATE (parent) < GST_STATE_READY) { - GST_DEBUG ("parent %s of pad %s:%s is not READY", - GST_ELEMENT_NAME (parent), GST_DEBUG_PAD_NAME (link->srcpad)); - return FALSE; - } - parent = GST_PAD_PARENT (link->sinkpad); - if (!parent || GST_STATE (parent) < GST_STATE_READY) { - GST_DEBUG ("parent %s of pad %s:%s is not READY", - GST_ELEMENT_NAME (parent), GST_DEBUG_PAD_NAME (link->sinkpad)); - return FALSE; - } - - return TRUE; -} - -static void -gst_pad_link_fixate (GstPadLink * link) -{ - GstCaps *caps; - GstCaps *newcaps; - - caps = link->caps; - - g_return_if_fail (caps != NULL); - g_return_if_fail (!gst_caps_is_empty (caps)); - - GST_DEBUG ("trying to fixate caps %" GST_PTR_FORMAT, caps); - - gst_caps_do_simplify (caps); - while (!gst_caps_is_fixed (caps)) { - int i; - - for (i = 0; i < 5; i++) { - newcaps = NULL; - switch (i) { - case 0: - g_signal_emit (G_OBJECT (link->srcpad), - gst_real_pad_signals[REAL_FIXATE], 0, caps, &newcaps); - GST_DEBUG ("app srcpad signal fixated to %" GST_PTR_FORMAT, newcaps); - break; - case 1: - g_signal_emit (G_OBJECT (link->sinkpad), - gst_real_pad_signals[REAL_FIXATE], 0, caps, &newcaps); - GST_DEBUG ("app sinkpad signal fixated to %" GST_PTR_FORMAT, newcaps); - break; - case 2: - if (GST_RPAD_FIXATEFUNC (link->srcpad)) { - newcaps = - GST_RPAD_FIXATEFUNC (link->srcpad) (GST_PAD (link->srcpad), - caps); - GST_DEBUG ("srcpad %s:%s fixated to %" GST_PTR_FORMAT, - GST_DEBUG_PAD_NAME (link->srcpad), newcaps); - } else - GST_DEBUG ("srcpad %s:%s doesn't have a fixate function", - GST_DEBUG_PAD_NAME (link->srcpad)); - - break; - case 3: - if (GST_RPAD_FIXATEFUNC (link->sinkpad)) { - newcaps = - GST_RPAD_FIXATEFUNC (link->sinkpad) (GST_PAD (link->sinkpad), - caps); - GST_DEBUG ("sinkpad %s:%s fixated to %" GST_PTR_FORMAT, - GST_DEBUG_PAD_NAME (link->sinkpad), newcaps); - } else - GST_DEBUG ("sinkpad %s:%s doesn't have a fixate function", - GST_DEBUG_PAD_NAME (link->sinkpad)); - break; - case 4: - newcaps = _gst_pad_default_fixate_func (GST_PAD (link->srcpad), caps); - GST_DEBUG ("core fixated to %" GST_PTR_FORMAT, newcaps); - break; - } - if (newcaps) { - G_GNUC_UNUSED gboolean bad; - - gst_caps_do_simplify (newcaps); -#ifndef G_DISABLE_CHECKS - /* some mad checking for correctly working fixation functions */ - - if (i == 4) { - /* we trust the default fixation function unconditionally */ - bad = FALSE; - } else if (gst_caps_is_empty (newcaps)) { - g_warning - ("a fixation function did not fixate correctly, it returned empty caps"); - gst_caps_free (newcaps); - continue; - } else if (gst_caps_is_any (caps)) { - bad = gst_caps_is_any (newcaps); - } else { - GstCaps *test = gst_caps_subtract (caps, newcaps); - - bad = gst_caps_is_empty (test); - gst_caps_free (test); - /* simplifying is ok, too */ - if (bad) - bad = (gst_caps_get_size (newcaps) >= gst_caps_get_size (caps)); - } - if (bad) { - gchar *newcaps_str = gst_caps_to_string (newcaps); - gchar *caps_str = gst_caps_to_string (caps); - - g_warning - ("a fixation function did not fixate correctly, the returned caps %s are no true subset of %s.", - newcaps_str, caps_str); - g_free (newcaps_str); - g_free (caps_str); - gst_caps_free (newcaps); - } else -#endif - { - gst_caps_free (caps); - caps = newcaps; - break; - } - } - } - } - - link->caps = caps; -} - -static GstPadLinkReturn -gst_pad_link_call_link_functions (GstPadLink * link) -{ - GstPadLinkReturn res = GST_PAD_LINK_OK; - - /* Detect recursion. */ - if (GST_PAD_IS_NEGOTIATING (link->srcpad) || - GST_PAD_IS_NEGOTIATING (link->sinkpad)) { - GST_ERROR ("The link functions have recursed, please file a bug!"); - return GST_PAD_LINK_REFUSED; - } - - /* Both of the pads are in negotiation, so we set the NEGOTIATING flag on both - * of them now to avoid recursion from either pad. */ - GST_FLAG_SET (link->srcpad, GST_PAD_NEGOTIATING); - GST_FLAG_SET (link->sinkpad, GST_PAD_NEGOTIATING); - - /* If this doesn't run, the status is left to the default OK value. */ - if (link->srcnotify && GST_RPAD_LINKFUNC (link->srcpad)) { - /* call the link function */ - GST_DEBUG_OBJECT (link->srcpad, - "calling link function with caps %" GST_PTR_FORMAT, link->caps); - res = GST_RPAD_LINKFUNC (link->srcpad) (GST_PAD (link->srcpad), link->caps); - - GST_DEBUG_OBJECT (link->srcpad, "got reply %d from link function", res); - - if (GST_PAD_LINK_FAILED (res)) { - GST_CAT_INFO_OBJECT (GST_CAT_CAPS, link->srcpad, - "pad doesn't accept caps %" GST_PTR_FORMAT, link->caps); - } - } - - if (GST_PAD_LINK_SUCCESSFUL (res) && - link->sinknotify && GST_RPAD_LINKFUNC (link->sinkpad)) { - /* call the link function */ - GST_DEBUG_OBJECT (link->sinkpad, - "calling link function with caps %" GST_PTR_FORMAT, link->caps); - res = GST_RPAD_LINKFUNC (link->sinkpad) (GST_PAD (link->sinkpad), - link->caps); - - GST_DEBUG_OBJECT (link->sinkpad, "got reply %d from link function", res); - - if (GST_PAD_LINK_FAILED (res)) { - GST_CAT_INFO_OBJECT (GST_CAT_CAPS, link->sinkpad, - "pad doesn't accept caps %" GST_PTR_FORMAT, link->caps); - } - } - - GST_FLAG_UNSET (link->srcpad, GST_PAD_NEGOTIATING); - GST_FLAG_UNSET (link->sinkpad, GST_PAD_NEGOTIATING); - return res; -} - -static GstPadLinkReturn -gst_pad_link_negotiate (GstPadLink * link) -{ - GST_DEBUG ("negotiating link from pad %s:%s to pad %s:%s", - GST_DEBUG_PAD_NAME (link->srcpad), GST_DEBUG_PAD_NAME (link->sinkpad)); - - gst_pad_link_intersect (link); - if (gst_caps_is_empty (link->caps)) - return GST_PAD_LINK_REFUSED; - - gst_pad_link_fixate (link); - if (gst_caps_is_empty (link->caps)) - return GST_PAD_LINK_REFUSED; - - if (!gst_pad_link_ready_for_negotiation (link)) { - return GST_PAD_LINK_DELAYED; - } - GST_DEBUG ("calling link_functions between %s:%s and %s:%s with caps %" - GST_PTR_FORMAT, GST_DEBUG_PAD_NAME (link->srcpad), - GST_DEBUG_PAD_NAME (link->sinkpad), link->caps); - - return gst_pad_link_call_link_functions (link); -} - -/** - * gst_pad_link_try: - * @link: link to try - * - * Tries to (re)link the pads with the given link. The function takes ownership - * of the supplied link. If the function returns FALSE and an old link existed, - * that link can be assumed to work unchanged. - * - * Returns: TRUE if the link succeeded, FALSE if not. - */ -static gboolean -gst_pad_link_try (GstPadLink * link) -{ - GstPad *srcpad, *sinkpad; - GstPadLink *oldlink; - GstPadLinkReturn ret; - - /* we use assertions here, because this function is static */ - g_assert (link); - srcpad = link->srcpad; - g_assert (srcpad); - sinkpad = link->sinkpad; - g_assert (sinkpad); - oldlink = GST_RPAD_LINK (srcpad); - g_assert (oldlink == GST_RPAD_LINK (sinkpad)); - - GST_DEBUG ("negotiating given link"); - ret = gst_pad_link_negotiate (link); - if (GST_PAD_LINK_FAILED (ret) && oldlink && oldlink->caps) { - GST_DEBUG ("negotiating failed, but there was a valid old link"); - oldlink->srcnotify = link->srcnotify; - oldlink->sinknotify = link->sinknotify; - if (GST_PAD_LINK_FAILED (gst_pad_link_call_link_functions (oldlink))) { - g_warning ("pads don't accept old caps. We assume they did though"); - } - } - if (ret == GST_PAD_LINK_REFUSED) { - GST_DEBUG ("link refused, returning"); - gst_pad_link_free (link); - return ret; - } - if (ret == GST_PAD_LINK_DELAYED) { - GST_DEBUG ("link delayed, replacing link caps and returning"); - gst_caps_replace (&link->caps, NULL); - } - - GST_RPAD_PEER (srcpad) = GST_REAL_PAD (link->sinkpad); - GST_RPAD_PEER (sinkpad) = GST_REAL_PAD (link->srcpad); - if (oldlink) { - GST_DEBUG ("copying stuff from oldlink"); - link->temp_store = oldlink->temp_store; - GST_DEBUG ("moving old data temp store %p", link->temp_store); - link->engaged = oldlink->engaged; - oldlink->temp_store = NULL; - gst_pad_link_free (oldlink); - } - GST_RPAD_LINK (srcpad) = link; - GST_RPAD_LINK (sinkpad) = link; - if (ret == GST_PAD_LINK_OK) { - GST_DEBUG ("notifying caps after successful link"); - g_object_notify (G_OBJECT (srcpad), "caps"); - g_object_notify (G_OBJECT (sinkpad), "caps"); - } - - return ret; -} - -/** - * gst_pad_renegotiate: - * @pad: a #GstPad - * - * Initiate caps negotiation on @pad. @pad must be linked. - * - * If @pad's parent is not at least in #GST_STATE_READY, returns - * #GST_PAD_LINK_DELAYED. - * - * Otherwise caps are retrieved from both @pad and its peer by calling their - * getcaps functions. They are then intersected, returning #GST_PAD_LINK_FAIL if - * there is no intersection. - * - * The intersection is fixated if necessary, and then the link functions of @pad - * and its peer are called. - * - * Returns: The return value of @pad's link function (see - * gst_pad_set_link_function()), or #GST_PAD_LINK_OK if there is no link - * function. - * - * The macros GST_PAD_LINK_SUCCESSFUL() and GST_PAD_LINK_FAILED() should be used - * when you just need success/failure information. - */ -GstPadLinkReturn -gst_pad_renegotiate (GstPad * pad) -{ - GstPadLink *link; - - g_return_val_if_fail (GST_IS_PAD (pad), GST_PAD_LINK_REFUSED); - if (!GST_PAD_PEER (pad)) - return GST_PAD_LINK_OK; - - link = gst_pad_link_new (); - - link->srcpad = GST_PAD_LINK_SRC (pad); - link->sinkpad = GST_PAD_LINK_SINK (pad); - - if (!gst_pad_link_ready_for_negotiation (link)) { - gst_pad_link_free (link); - return GST_PAD_LINK_DELAYED; - } - - if (GST_REAL_PAD (pad)->link->filtercaps) { - link->filtercaps = gst_caps_copy (GST_REAL_PAD (pad)->link->filtercaps); - } - link->srccaps = gst_pad_get_caps (link->srcpad); - link->sinkcaps = gst_pad_get_caps (link->sinkpad); - - return gst_pad_link_try (link); -} - -/** - * gst_pad_try_set_caps: - * @pad: a #GstPad - * @caps: #GstCaps to set on @pad - * - * Try to set the caps on @pad. @caps must be fixed. If @pad is unlinked, - * returns #GST_PAD_LINK_OK without doing anything. Otherwise, start caps - * negotiation on @pad. - * - * Returns: The return value of @pad's link function (see - * gst_pad_set_link_function()), or #GST_PAD_LINK_OK if there is no link - * function. - * - * The macros GST_PAD_LINK_SUCCESSFUL() and GST_PAD_LINK_FAILED() should be used - * when you just need success/failure information. - */ -GstPadLinkReturn -gst_pad_try_set_caps (GstPad * pad, const GstCaps * caps) -{ - GstPadLink *link; - GstPadLink *oldlink; - GstPadLinkReturn ret; - - g_return_val_if_fail (GST_IS_REAL_PAD (pad), GST_PAD_LINK_REFUSED); - - GST_LOG_OBJECT (pad, "Trying to set %" GST_PTR_FORMAT, caps); - - if (GST_PAD_IS_NEGOTIATING (pad)) { - GST_DEBUG_OBJECT (pad, "Detected a recursion, just returning OK"); - return GST_PAD_LINK_OK; - } - - GST_CAT_INFO_OBJECT (GST_CAT_CAPS, pad, "caps %" GST_PTR_FORMAT, caps); - /* setting non-fixed caps on a pad is not allowed */ - if (!gst_caps_is_fixed (caps)) { - GST_CAT_INFO (GST_CAT_CAPS, - "trying to set unfixed caps on pad %s:%s, not allowed", - GST_DEBUG_PAD_NAME (pad)); - g_warning ("trying to set non fixed caps on pad %s:%s, not allowed", - GST_DEBUG_PAD_NAME (pad)); - - GST_DEBUG ("unfixed caps %" GST_PTR_FORMAT, caps); - return GST_PAD_LINK_REFUSED; - } - - /* we allow setting caps on non-linked pads. It's ignored */ - if (!GST_PAD_PEER (pad)) { - GST_DEBUG ("unlinked pad %s:%s, returning OK", GST_DEBUG_PAD_NAME (pad)); - return GST_PAD_LINK_OK; - } - - /* we just checked that a peer exists */ - g_assert (GST_PAD_LINK_SRC (pad)); - g_assert (GST_PAD_LINK_SINK (pad)); - - /* if the desired caps are already there, it's trivially ok */ - if (GST_PAD_CAPS (pad) && gst_caps_is_equal (caps, GST_PAD_CAPS (pad))) { - GST_DEBUG ("pad %s:%s already has these caps", GST_DEBUG_PAD_NAME (pad)); - return GST_PAD_LINK_OK; - } - - link = gst_pad_link_new (); - - link->srcpad = GST_PAD_LINK_SRC (pad); - link->sinkpad = GST_PAD_LINK_SINK (pad); - - if (!gst_pad_link_ready_for_negotiation (link)) { - GST_DEBUG ("link not ready for negotiating, delaying"); - gst_pad_link_free (link); - return GST_PAD_LINK_DELAYED; - } - - oldlink = GST_REAL_PAD (pad)->link; - if (oldlink && oldlink->filtercaps) { - link->filtercaps = gst_caps_copy (oldlink->filtercaps); - } - if (link->srcpad == pad) { - link->srccaps = gst_caps_copy (caps); - link->sinkcaps = gst_pad_get_caps (link->sinkpad); - link->srcnotify = FALSE; - } else { - link->srccaps = gst_pad_get_caps (link->srcpad); - link->sinkcaps = gst_caps_copy (caps); - link->sinknotify = FALSE; - } - - GST_DEBUG ("trying to link"); - ret = gst_pad_link_try (link); - - return ret; -} - -/** - * gst_pad_try_set_caps_nonfixed: - * @pad: a real #GstPad - * @caps: #GstCaps to set on @pad - * - * Like gst_pad_try_set_caps(), but allows non-fixed caps. - * - * Returns: a #GstPadLinkReturn, like gst_pad_try_set_caps(). - */ -GstPadLinkReturn -gst_pad_try_set_caps_nonfixed (GstPad * pad, const GstCaps * caps) -{ - GstPadLink *link; - GstPadLink *oldlink; - GstPadLinkReturn ret; - - g_return_val_if_fail (GST_IS_REAL_PAD (pad), GST_PAD_LINK_REFUSED); - g_return_val_if_fail (!GST_PAD_IS_NEGOTIATING (pad), GST_PAD_LINK_REFUSED); - - /* we allow setting caps on non-linked pads. It's ignored */ - if (!GST_PAD_PEER (pad)) { - return GST_PAD_LINK_OK; - } - - /* we just checked that a peer exists */ - g_assert (GST_PAD_LINK_SRC (pad)); - g_assert (GST_PAD_LINK_SINK (pad)); - - /* if the link is already negotiated and the caps are compatible - * with what we're setting, it's trivially OK. */ - if (GST_PAD_CAPS (pad)) { - GstCaps *intersection; - - intersection = gst_caps_intersect (caps, GST_PAD_CAPS (pad)); - if (!gst_caps_is_empty (intersection)) { - gst_caps_free (intersection); - return GST_PAD_LINK_OK; - } - gst_caps_free (intersection); - } - - link = gst_pad_link_new (); - - link->srcpad = GST_PAD_LINK_SRC (pad); - link->sinkpad = GST_PAD_LINK_SINK (pad); - - if (!gst_pad_link_ready_for_negotiation (link)) { - gst_pad_link_free (link); - return GST_PAD_LINK_DELAYED; - } - - oldlink = GST_REAL_PAD (pad)->link; - if (oldlink && oldlink->filtercaps) { - link->filtercaps = gst_caps_copy (oldlink->filtercaps); - } - if (link->srcpad == pad) { - link->srccaps = gst_caps_copy (caps); - link->sinkcaps = gst_pad_get_caps (link->sinkpad); - link->srcnotify = FALSE; - } else { - link->srccaps = gst_pad_get_caps (link->srcpad); - link->sinkcaps = gst_caps_copy (caps); - link->sinknotify = FALSE; - } - - ret = gst_pad_link_try (link); - - return ret; -} - -/** - * 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; - GstPadLink *link; - - /* 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; - } - - if (!gst_pad_check_schedulers (realsrc, realsink)) { - g_warning ("linking pads with different scheds requires " - "exactly one decoupled element (such as queue)"); - return FALSE; - } - - g_return_val_if_fail (realsrc != NULL, GST_PAD_LINK_REFUSED); - g_return_val_if_fail (realsink != NULL, GST_PAD_LINK_REFUSED); - - link = gst_pad_link_new (); - - if (GST_RPAD_DIRECTION (realsrc) == GST_PAD_SRC) { - link->srcpad = GST_PAD (realsrc); - link->sinkpad = GST_PAD (realsink); - } else { - link->srcpad = GST_PAD (realsink); - link->sinkpad = GST_PAD (realsrc); - } - - if (GST_RPAD_DIRECTION (link->srcpad) != GST_PAD_SRC) { - GST_CAT_INFO (GST_CAT_PADS, - "Real src pad %s:%s is not a source pad, failed", - GST_DEBUG_PAD_NAME (link->srcpad)); - gst_pad_link_free (link); - return FALSE; - } - if (GST_RPAD_DIRECTION (link->sinkpad) != GST_PAD_SINK) { - GST_CAT_INFO (GST_CAT_PADS, "Real sink pad %s:%s is not a sink pad, failed", - GST_DEBUG_PAD_NAME (link->sinkpad)); - gst_pad_link_free (link); - return FALSE; - } - - link->srccaps = gst_pad_get_caps (link->srcpad); - link->sinkcaps = gst_pad_get_caps (link->sinkpad); - if (filtercaps) - link->filtercaps = gst_caps_copy (filtercaps); - - gst_pad_link_intersect (link); - if (gst_caps_is_empty (link->caps)) { - gst_pad_link_free (link); - return FALSE; - } - - gst_pad_link_free (link); - 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_pad_link_filtered: * @srcpad: the source #GstPad to link. @@ -1748,21 +1078,24 @@ gst_pad_can_link (GstPad * srcpad, GstPad * sinkpad) * Links the source pad and the sink pad, constrained * by the given filter caps. * - * Returns: TRUE if the pads have been linked, FALSE otherwise. + * The filtercaps will be copied and refcounted, so you should unref + * it yourself after using this function. + * + * Returns: A result code indicating if the connection worked or + * what went wrong. */ -gboolean +GstPadLinkReturn gst_pad_link_filtered (GstPad * srcpad, GstPad * sinkpad, const GstCaps * filtercaps) { GstRealPad *realsrc, *realsink; - GstScheduler *src_sched, *sink_sched; - GstPadLink *link; + GstPadLinkReturn result; /* 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); + g_return_val_if_fail (srcpad != NULL, GST_PAD_LINK_REFUSED); + g_return_val_if_fail (GST_IS_PAD (srcpad), GST_PAD_LINK_REFUSED); + g_return_val_if_fail (sinkpad != NULL, GST_PAD_LINK_REFUSED); + g_return_val_if_fail (GST_IS_PAD (sinkpad), GST_PAD_LINK_REFUSED); GST_CAT_INFO (GST_CAT_PADS, "trying to link %s:%s and %s:%s", GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad)); @@ -1775,93 +1108,109 @@ gst_pad_link_filtered (GstPad * srcpad, GstPad * 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; - } - - if (!gst_pad_check_schedulers (realsrc, realsink)) { - g_warning ("linking pads with different scheds requires " - "exactly one decoupled element (such as queue)"); - return FALSE; - } g_return_val_if_fail (realsrc != NULL, GST_PAD_LINK_REFUSED); g_return_val_if_fail (realsink != NULL, GST_PAD_LINK_REFUSED); - link = gst_pad_link_new (); + GST_LOCK (realsrc); + GST_LOCK (realsink); - if (GST_RPAD_DIRECTION (realsrc) == GST_PAD_SRC) { - link->srcpad = GST_PAD (realsrc); - link->sinkpad = GST_PAD (realsink); - } else { - link->srcpad = GST_PAD (realsink); - link->sinkpad = GST_PAD (realsrc); + /* 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)); + result = GST_PAD_LINK_REFUSED; + goto error; + } + 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)); + result = GST_PAD_LINK_REFUSED; + goto error; } - if (GST_RPAD_DIRECTION (link->srcpad) != GST_PAD_SRC) { + if (GST_RPAD_DIRECTION (realsrc) != GST_PAD_SRC) { GST_CAT_INFO (GST_CAT_PADS, "Real src pad %s:%s is not a source pad, failed", - GST_DEBUG_PAD_NAME (link->srcpad)); - gst_pad_link_free (link); - return FALSE; + GST_DEBUG_PAD_NAME (realsrc)); + result = GST_PAD_LINK_REFUSED; + goto error; } - if (GST_RPAD_DIRECTION (link->sinkpad) != GST_PAD_SINK) { + if (GST_RPAD_DIRECTION (realsink) != GST_PAD_SINK) { GST_CAT_INFO (GST_CAT_PADS, "Real sink pad %s:%s is not a sink pad, failed", - GST_DEBUG_PAD_NAME (link->sinkpad)); - gst_pad_link_free (link); - return FALSE; + GST_DEBUG_PAD_NAME (realsink)); + result = GST_PAD_LINK_REFUSED; + goto error; } + /* FIXME check pad caps for non-empty intersection */ - link->srccaps = gst_pad_get_caps (link->srcpad); - link->sinkcaps = gst_pad_get_caps (link->sinkpad); - if (filtercaps) - link->filtercaps = gst_caps_copy (filtercaps); - if (gst_pad_link_try (link) == GST_PAD_LINK_REFUSED) - return FALSE; + /* update filter before calling the link functions */ + if (filtercaps) { + GstCaps *filtercopy = gst_caps_copy (filtercaps); - /* fire off a signal to each of the pads telling them - * that they've been linked */ - g_signal_emit (G_OBJECT (link->srcpad), gst_real_pad_signals[REAL_LINKED], - 0, link->sinkpad); - g_signal_emit (G_OBJECT (link->sinkpad), gst_real_pad_signals[REAL_LINKED], - 0, link->srcpad); + filtercopy = gst_caps_ref (filtercopy); - src_sched = gst_pad_get_scheduler (GST_PAD (link->srcpad)); - sink_sched = gst_pad_get_scheduler (GST_PAD (link->sinkpad)); - - /* now tell the scheduler */ - if (src_sched && src_sched == sink_sched) { - gst_scheduler_pad_link (src_sched, - GST_PAD (link->srcpad), GST_PAD (link->sinkpad)); + GST_RPAD_APPFILTER (realsrc) = filtercopy; + GST_RPAD_APPFILTER (realsink) = filtercopy; } else { - GST_CAT_INFO (GST_CAT_PADS, - "not telling link to scheduler %s:%s and %s:%s, %p %p", - GST_DEBUG_PAD_NAME (link->srcpad), GST_DEBUG_PAD_NAME (link->sinkpad), - src_sched, sink_sched); + GST_RPAD_APPFILTER (realsrc) = NULL; + GST_RPAD_APPFILTER (realsink) = NULL; + } + GST_UNLOCK (realsrc); + GST_UNLOCK (realsink); + + if (GST_RPAD_LINKFUNC (realsrc)) { + /* this one will call the peer link function */ + result = + GST_RPAD_LINKFUNC (realsrc) (GST_PAD (realsrc), GST_PAD (realsink)); + } else if (GST_RPAD_LINKFUNC (realsink)) { + /* if no source link function, we need to call the sink link + * function ourselves. */ + result = + GST_RPAD_LINKFUNC (realsink) (GST_PAD (realsink), GST_PAD (realsrc)); + } else { + result = GST_PAD_LINK_OK; } - GST_CAT_INFO (GST_CAT_PADS, "linked %s:%s and %s:%s, successful", - GST_DEBUG_PAD_NAME (link->srcpad), GST_DEBUG_PAD_NAME (link->sinkpad)); + GST_LOCK (realsrc); + GST_LOCK (realsink); + if (result == GST_PAD_LINK_OK) { + GST_RPAD_PEER (realsrc) = GST_REAL_PAD (realsink); + GST_RPAD_PEER (realsink) = GST_REAL_PAD (realsrc); - return TRUE; + GST_UNLOCK (realsrc); + GST_UNLOCK (realsink); + + /* fire off a signal to each of the pads telling them + * that they've been linked */ + g_signal_emit (G_OBJECT (realsrc), gst_real_pad_signals[REAL_LINKED], + 0, realsink); + g_signal_emit (G_OBJECT (realsink), gst_real_pad_signals[REAL_LINKED], + 0, realsrc); + + GST_CAT_INFO (GST_CAT_PADS, "linked %s:%s and %s:%s, successful", + GST_DEBUG_PAD_NAME (realsrc), GST_DEBUG_PAD_NAME (realsink)); + } else { + GST_CAT_INFO (GST_CAT_PADS, "link between %s:%s and %s:%s failed", + GST_DEBUG_PAD_NAME (realsrc), GST_DEBUG_PAD_NAME (realsink)); + + /* remove the filter again */ + if (filtercaps) { + gst_caps_replace (&GST_RPAD_APPFILTER (realsrc), NULL); + gst_caps_replace (&GST_RPAD_APPFILTER (realsink), NULL); + } + + GST_UNLOCK (realsrc); + GST_UNLOCK (realsink); + } + + return result; + +error: + GST_UNLOCK (realsrc); + GST_UNLOCK (realsink); + + return result; } /** @@ -1871,52 +1220,15 @@ gst_pad_link_filtered (GstPad * srcpad, GstPad * sinkpad, * * Links the source pad to the sink pad. * - * Returns: TRUE if the pad could be linked, FALSE otherwise. + * Returns: A result code indicating if the connection worked or + * what went wrong. */ -gboolean +GstPadLinkReturn gst_pad_link (GstPad * srcpad, GstPad * sinkpad) { return gst_pad_link_filtered (srcpad, sinkpad, NULL); } -/* FIXME 0.9: Remove this */ -/** - * gst_pad_set_parent: - * @pad: a #GstPad to set the parent of. - * @parent: the new parent #GstElement. - * - * Sets the parent object of a pad. Deprecated, use gst_object_set_parent() - * instead. - */ -void -gst_pad_set_parent (GstPad * pad, GstElement * parent) -{ - g_return_if_fail (GST_IS_PAD (pad)); - g_return_if_fail (GST_PAD_PARENT (pad) == NULL); - g_return_if_fail (GST_IS_ELEMENT (parent)); - - gst_object_set_parent (GST_OBJECT (pad), GST_OBJECT (parent)); -} - -/* FIXME 0.9: Remove this */ -/** - * gst_pad_get_parent: - * @pad: the #GstPad to get the parent of. - * - * Gets the parent object of this pad. Deprecated, use gst_object_get_parent() - * instead. - * - * Returns: the parent #GstElement. - */ -GstElement * -gst_pad_get_parent (GstPad * pad) -{ - g_return_val_if_fail (pad != NULL, NULL); - g_return_val_if_fail (GST_IS_PAD (pad), NULL); - - return GST_PAD_PARENT (pad); -} - static void gst_pad_set_pad_template (GstPad * pad, GstPadTemplate * templ) { @@ -1949,43 +1261,6 @@ gst_pad_get_pad_template (GstPad * pad) } -/** - * gst_pad_get_scheduler: - * @pad: a #GstPad to get the scheduler of. - * - * Gets the scheduler of the pad. Since the pad does not - * have a scheduler of its own, the scheduler of the parent - * is taken. For decoupled pads, the scheduler of the peer - * parent is taken. - * - * Returns: the #GstScheduler of the pad, or %NULL if there is no parent or the - * parent is not yet in a managing bin. - */ -GstScheduler * -gst_pad_get_scheduler (GstPad * pad) -{ - GstScheduler *scheduler = NULL; - GstElement *parent; - - g_return_val_if_fail (GST_IS_PAD (pad), NULL); - - parent = gst_pad_get_parent (pad); - if (parent) { - if (GST_FLAG_IS_SET (parent, GST_ELEMENT_DECOUPLED)) { - GstRealPad *peer = GST_RPAD_PEER (pad); - - if (peer) { - scheduler = - gst_element_get_scheduler (gst_pad_get_parent (GST_PAD (peer))); - } - } else { - scheduler = gst_element_get_scheduler (parent); - } - } - - return scheduler; -} - /** * gst_pad_get_real_parent: * @pad: a #GstPad to get the real parent of. @@ -2004,23 +1279,12 @@ gst_pad_get_real_parent (GstPad * pad) return GST_PAD_PARENT (GST_PAD (GST_PAD_REALIZE (pad))); } -/* FIXME 0.9: Make static. */ -/** - * gst_pad_add_ghost_pad: - * @pad: a #GstPad to attach the ghost pad to. - * @ghostpad: the ghost #GstPad to to the pad. - * - * Adds a ghost pad to a pad. Private function, will be removed from the API in - * 0.9. - */ -void +/* FIXME not MT safe */ +static void gst_pad_add_ghost_pad (GstPad * pad, GstPad * ghostpad) { GstRealPad *realpad; - g_return_if_fail (GST_IS_PAD (pad)); - g_return_if_fail (GST_IS_GHOST_PAD (ghostpad)); - /* if we're ghosting a ghost pad, drill down to find the real pad */ realpad = (GstRealPad *) pad; while (GST_IS_GHOST_PAD (realpad)) @@ -2033,21 +1297,11 @@ gst_pad_add_ghost_pad (GstPad * pad, GstPad * ghostpad) gst_pad_set_pad_template (GST_PAD (ghostpad), GST_PAD_PAD_TEMPLATE (pad)); } -/* FIXME 0.9: Make static. */ -/** - * gst_pad_remove_ghost_pad: - * @pad: a #GstPad to remove the ghost pad from. - * @ghostpad: the ghost #GstPad to remove from the pad. - * - * Removes a ghost pad from a pad. Private, will be removed from the API in 0.9. - */ -void +static void gst_pad_remove_ghost_pad (GstPad * pad, GstPad * ghostpad) { GstRealPad *realpad; - g_return_if_fail (GST_IS_PAD (pad)); - g_return_if_fail (GST_IS_GHOST_PAD (ghostpad)); realpad = GST_PAD_REALIZE (pad); g_return_if_fail (GST_GPAD_REALPAD (ghostpad) == realpad); @@ -2056,252 +1310,6 @@ gst_pad_remove_ghost_pad (GstPad * pad, GstPad * ghostpad) GST_GPAD_REALPAD (ghostpad) = NULL; } -/** - * gst_pad_get_ghost_pad_list: - * @pad: a #GstPad to get the ghost pads of. - * - * Gets the ghost pads of this pad. - * - * Returns: a #GList of ghost pads. - */ -GList * -gst_pad_get_ghost_pad_list (GstPad * pad) -{ - g_return_val_if_fail (GST_IS_PAD (pad), NULL); - - return GST_PAD_REALIZE (pad)->ghostpads; -} - -static gboolean -_gst_pad_default_fixate_value (const GValue * value, GValue * dest) -{ - GType type = G_VALUE_TYPE (value); - - if (gst_value_is_fixed (value)) - return TRUE; - - if (type == GST_TYPE_INT_RANGE) { - g_value_init (dest, G_TYPE_INT); - g_value_set_int (dest, gst_value_get_int_range_min (value)); - } else if (type == GST_TYPE_DOUBLE_RANGE) { - g_value_init (dest, G_TYPE_DOUBLE); - g_value_set_double (dest, gst_value_get_double_range_min (value)); - } else if (type == GST_TYPE_LIST) { - gst_value_init_and_copy (dest, gst_value_list_get_value (value, 0)); - } else if (type == GST_TYPE_FIXED_LIST) { - gint size, n; - GValue dest_kid = { 0 }; - const GValue *kid; - - /* check recursively */ - g_value_init (dest, GST_TYPE_FIXED_LIST); - size = gst_value_list_get_size (value); - for (n = 0; n < size; n++) { - kid = gst_value_list_get_value (value, n); - if (_gst_pad_default_fixate_value (kid, &dest_kid)) { - gst_value_list_append_value (dest, kid); - } else { - gst_value_list_append_value (dest, &dest_kid); - g_value_unset (&dest_kid); - } - } - } else { - g_critical ("Don't know how to fixate value type %s", g_type_name (type)); - } - - return FALSE; -} - -static gboolean -_gst_pad_default_fixate_foreach (GQuark field_id, GValue * value, gpointer s) -{ - GstStructure *structure = (GstStructure *) s; - GValue dest = { 0 }; - - if (_gst_pad_default_fixate_value (value, &dest)) - return TRUE; - gst_structure_id_set_value (structure, field_id, &dest); - g_value_unset (&dest); - - return FALSE; -} - -static GstCaps * -_gst_pad_default_fixate_func (GstPad * pad, const GstCaps * caps) -{ - static GstStaticCaps octetcaps = GST_STATIC_CAPS ("application/octet-stream"); - GstStructure *structure; - GstCaps *newcaps; - - g_return_val_if_fail (pad != NULL, NULL); - g_return_val_if_fail (caps != NULL, NULL); - g_return_val_if_fail (!gst_caps_is_empty (caps), NULL); - - if (gst_caps_is_any (caps)) { - return gst_caps_copy (gst_static_caps_get (&octetcaps)); - } - - if (caps->structs->len > 1) { - return gst_caps_new_full (gst_structure_copy (gst_caps_get_structure (caps, - 0)), NULL); - } - - newcaps = gst_caps_copy (caps); - structure = gst_caps_get_structure (newcaps, 0); - gst_structure_foreach (structure, _gst_pad_default_fixate_foreach, structure); - - return newcaps; -} - -/** - * gst_pad_perform_negotiate: - * @srcpad: the source #GstPad. - * @sinkpad: the sink #GstPad. - * - * Tries to negotiate the pads. See gst_pad_renegotiate() for a brief - * description of caps negotiation. - * - * Returns: TRUE if the pads were succesfully negotiated, FALSE otherwise. - */ -gboolean -gst_pad_perform_negotiate (GstPad * srcpad, GstPad * sinkpad) -{ - return GST_PAD_LINK_SUCCESSFUL (gst_pad_renegotiate (srcpad)); -} - -static void -gst_pad_link_unnegotiate (GstPadLink * link) -{ - g_return_if_fail (link != NULL); - - if (link->caps) { - gst_caps_free (link->caps); - link->caps = NULL; - link->engaged = FALSE; - if (GST_RPAD_LINK (link->srcpad) != link) { - g_warning ("unnegotiating unset link"); - } else { - g_object_notify (G_OBJECT (link->srcpad), "caps"); - } - if (GST_RPAD_LINK (link->sinkpad) != link) { - g_warning ("unnegotiating unset link"); - } else { - g_object_notify (G_OBJECT (link->sinkpad), "caps"); - } - } -} - -/** - * gst_pad_unnegotiate: - * @pad: pad to unnegotiate - * - * "Unnegotiates" a pad. The currently negotiated caps are cleared and the pad - * needs renegotiation. - */ -void -gst_pad_unnegotiate (GstPad * pad) -{ - GstPadLink *link; - - g_return_if_fail (GST_IS_PAD (pad)); - - link = GST_RPAD_LINK (GST_PAD_REALIZE (pad)); - if (link) - gst_pad_link_unnegotiate (link); -} - -/* returning NULL indicates that the arguments are invalid */ -static GstPadLink * -gst_pad_link_prepare (GstPad * srcpad, GstPad * sinkpad, - const GstCaps * filtercaps) -{ - GstRealPad *realsrc, *realsink; - GstPadLink *link; - - g_return_val_if_fail (GST_IS_PAD (srcpad), NULL); - g_return_val_if_fail (GST_IS_PAD (sinkpad), NULL); - - realsrc = GST_PAD_REALIZE (srcpad); - realsink = GST_PAD_REALIZE (sinkpad); - - if ((GST_PAD (realsrc) != srcpad) || (GST_PAD (realsink) != sinkpad)) { - GST_CAT_DEBUG (GST_CAT_PADS, "*actually* linking %s:%s and %s:%s", - GST_DEBUG_PAD_NAME (realsrc), GST_DEBUG_PAD_NAME (realsink)); - } - - g_return_val_if_fail (GST_RPAD_PEER (realsrc) == NULL, NULL); - g_return_val_if_fail (GST_RPAD_PEER (realsink) == NULL, NULL); - g_return_val_if_fail (GST_PAD_PARENT (realsrc) != NULL, NULL); - g_return_val_if_fail (GST_PAD_PARENT (realsink) != NULL, NULL); - - if (!gst_pad_check_schedulers (realsrc, realsink)) { - g_warning ("linking pads with different scheds requires " - "exactly one decoupled element (such as queue)"); - return NULL; - } - - if (GST_RPAD_DIRECTION (realsrc) == GST_RPAD_DIRECTION (realsink)) { - g_warning ("%s:%s and %s:%s are both %s pads, failed", - GST_DEBUG_PAD_NAME (realsrc), GST_DEBUG_PAD_NAME (realsink), - GST_RPAD_DIRECTION (realsrc) == GST_PAD_SRC ? "src" : "sink"); - return NULL; - } - - link = gst_pad_link_new (); - - if (GST_RPAD_DIRECTION (realsrc) == GST_PAD_SRC) { - link->srcpad = GST_PAD (realsrc); - link->sinkpad = GST_PAD (realsink); - } else { - link->srcpad = GST_PAD (realsink); - link->sinkpad = GST_PAD (realsrc); - } - - link->srccaps = gst_pad_get_caps (link->srcpad); - link->sinkcaps = gst_pad_get_caps (link->sinkpad); - if (filtercaps) - link->filtercaps = gst_caps_copy (filtercaps); - - return link; -} - -/** - * gst_pad_try_relink_filtered: - * @srcpad: the source #GstPad to relink. - * @sinkpad: the sink #GstPad to relink. - * @filtercaps: the #GstPad to use as a filter in the relink. - * - * Tries to relink the given source and sink pad, constrained by the given - * capabilities. - * - * Returns: TRUE if the pads were succesfully renegotiated, FALSE otherwise. - */ -gboolean -gst_pad_try_relink_filtered (GstPad * srcpad, GstPad * sinkpad, - const GstCaps * filtercaps) -{ - GstPadLink *link; - - GST_INFO ("trying to relink %" GST_PTR_FORMAT " and %" GST_PTR_FORMAT - " with filtercaps %" GST_PTR_FORMAT, srcpad, sinkpad); - - link = gst_pad_link_prepare (srcpad, sinkpad, filtercaps); - if (!link) - return FALSE; - - if (GST_RPAD_PEER (link->srcpad) != (GstRealPad *) link->sinkpad) { - g_warning ("Pads %s:%s and %s:%s were never linked", - GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad)); - gst_pad_link_free (link); - return FALSE; - } - - if (GST_PAD_LINK_FAILED (gst_pad_link_try (link))) - return FALSE; - - return TRUE; -} - /** * gst_pad_relink_filtered: * @srcpad: the source #GstPad to relink. @@ -2310,323 +1318,32 @@ gst_pad_try_relink_filtered (GstPad * srcpad, GstPad * sinkpad, * * Relinks the given source and sink pad, constrained by the given * capabilities. If the relink fails, the pads are unlinked - * and FALSE is returned. + * and an error code is returned. * - * Returns: TRUE if the pads were succesfully relinked, FALSE otherwise. + * Returns: The result code of the operation. */ -gboolean +GstPadLinkReturn gst_pad_relink_filtered (GstPad * srcpad, GstPad * sinkpad, const GstCaps * filtercaps) { - if (gst_pad_try_relink_filtered (srcpad, sinkpad, filtercaps)) - return TRUE; + GstCaps *filtercopy = gst_caps_copy (filtercaps); - gst_pad_unlink (srcpad, sinkpad); - return FALSE; -} + filtercopy = gst_caps_ref (filtercopy); -/** - * gst_pad_proxy_getcaps: - * @pad: a #GstPad to proxy. - * - * Calls gst_pad_get_allowed_caps() for every other pad belonging to the - * same element as @pad, and returns the intersection of the results. - * - * This function is useful as a default getcaps function for an element - * that can handle any stream format, but requires all its pads to have - * the same caps. Two such elements are tee and aggregator. - * - * Returns: the intersection of the other pads' allowed caps. - */ -GstCaps * -gst_pad_proxy_getcaps (GstPad * pad) -{ - GstElement *element; - const GList *pads; - GstCaps *caps, *intersected; + /* probably check if intersection with new filter is ok */ + GST_LOCK (srcpad); + GST_LOCK (sinkpad); + gst_caps_replace (&GST_PAD_CAPS (srcpad), NULL); + gst_caps_replace (&GST_PAD_APPFILTER (srcpad), filtercopy); - g_return_val_if_fail (GST_IS_PAD (pad), NULL); - - GST_DEBUG ("proxying getcaps for %s:%s", GST_DEBUG_PAD_NAME (pad)); - - element = gst_pad_get_parent (pad); - - pads = gst_element_get_pad_list (element); - - caps = gst_caps_new_any (); - while (pads) { - GstPad *otherpad = GST_PAD (pads->data); - GstCaps *temp; - - if (otherpad != pad) { - GstCaps *allowed = gst_pad_get_allowed_caps (otherpad); - - temp = gst_caps_intersect (caps, allowed); - gst_caps_free (caps); - gst_caps_free (allowed); - caps = temp; - } - - pads = g_list_next (pads); - } - - intersected = gst_caps_intersect (caps, gst_pad_get_pad_template_caps (pad)); - gst_caps_free (caps); - return intersected; -} - -/** - * gst_pad_proxy_pad_link: - * @pad: a #GstPad to proxy from - * @caps: the #GstCaps to link with - * - * Calls gst_pad_try_set_caps() for every other pad belonging to the - * same element as @pad. If gst_pad_try_set_caps() fails on any pad, - * the proxy link fails. May be used only during negotiation. - * - * Returns: GST_PAD_LINK_OK if sucessful - */ -GstPadLinkReturn -gst_pad_proxy_pad_link (GstPad * pad, const GstCaps * caps) -{ - GstElement *element; - const GList *pads; - GstPadLinkReturn ret; - - g_return_val_if_fail (GST_IS_PAD (pad), GST_PAD_LINK_REFUSED); - g_return_val_if_fail (caps != NULL, GST_PAD_LINK_REFUSED); - - GST_DEBUG ("proxying pad link for %s:%s", GST_DEBUG_PAD_NAME (pad)); - - element = gst_pad_get_parent (pad); - - pads = gst_element_get_pad_list (element); - - while (pads) { - GstPad *otherpad = GST_PAD (pads->data); - - if (otherpad != pad) { - ret = gst_pad_try_set_caps (otherpad, caps); - if (GST_PAD_LINK_FAILED (ret)) { - return ret; - } - } - pads = g_list_next (pads); - } + gst_caps_replace (&GST_PAD_CAPS (sinkpad), NULL); + gst_caps_replace (&GST_PAD_APPFILTER (sinkpad), filtercopy); + GST_UNLOCK (sinkpad); + GST_UNLOCK (srcpad); return GST_PAD_LINK_OK; } -/** - * gst_pad_proxy_fixate: - * @pad: a #GstPad to proxy. - * @caps: the #GstCaps to fixate - * - * Implements a default fixate function based on the caps set on the other - * pads in the element. This function should only be used if every pad - * has the same pad template caps. - * - * Returns: a fixated caps, or NULL if caps cannot be fixed - */ -GstCaps * -gst_pad_proxy_fixate (GstPad * pad, const GstCaps * caps) -{ - GstElement *element; - const GList *pads; - const GstCaps *othercaps; - - g_return_val_if_fail (GST_IS_PAD (pad), NULL); - g_return_val_if_fail (caps != NULL, NULL); - - GST_DEBUG ("proxying fixate for %s:%s\n", GST_DEBUG_PAD_NAME (pad)); - - element = gst_pad_get_parent (pad); - - pads = gst_element_get_pad_list (element); - - while (pads) { - GstPad *otherpad = GST_PAD (pads->data); - - /* FIXME check that each pad has the same pad template caps */ - - if (otherpad != pad) { - othercaps = gst_pad_get_negotiated_caps (otherpad); - - if (othercaps && !gst_caps_is_subset (caps, othercaps)) { - GstCaps *icaps; - - icaps = gst_caps_intersect (othercaps, caps); - if (!gst_caps_is_empty (icaps)) { - return icaps; - } else { - gst_caps_free (icaps); - } - } - } - pads = g_list_next (pads); - } - - return NULL; -} - -/** - * gst_pad_set_explicit_caps: - * @pad: a #GstPad to set the explicit caps of - * @caps: the #GstCaps to set - * - * If a pad has been told to use explicit caps, this function is used - * to set the explicit caps. If @caps is NULL, the explicit caps are - * unset. - * - * This function calls gst_pad_try_set_caps() on the pad. If that - * call fails, GST_ELEMENT_ERROR() is called to indicate a negotiation - * failure. - * - * Returns: TRUE if the caps were set correctly, otherwise FALSE - */ -gboolean -gst_pad_set_explicit_caps (GstPad * pad, const GstCaps * caps) -{ - GstPadLinkReturn link_ret; - - g_return_val_if_fail (GST_IS_PAD (pad), FALSE); - g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE); - - GST_CAT_DEBUG (GST_CAT_PADS, - "setting explicit caps on %s:%s to %" GST_PTR_FORMAT, - GST_DEBUG_PAD_NAME (pad), caps); - - if (caps == NULL) { - GST_CAT_DEBUG (GST_CAT_PADS, "caps is NULL"); - gst_caps_replace (&GST_RPAD_EXPLICIT_CAPS (pad), NULL); - return TRUE; - } - - gst_caps_replace (&GST_RPAD_EXPLICIT_CAPS (pad), gst_caps_copy (caps)); - - if (!GST_PAD_IS_LINKED (pad)) { - GST_CAT_DEBUG (GST_CAT_PADS, "pad is not linked"); - return TRUE; - } - link_ret = gst_pad_try_set_caps (pad, caps); - if (link_ret == GST_PAD_LINK_REFUSED) { - gchar *caps_str = gst_caps_to_string (caps); - - GST_ELEMENT_ERROR (gst_pad_get_parent (pad), CORE, PAD, (NULL), - ("failed to negotiate (try_set_caps with \"%s\" returned REFUSED)", - caps_str)); - g_free (caps_str); - return FALSE; - } - - return TRUE; -} - -static GstCaps * -gst_pad_explicit_getcaps (GstPad * pad) -{ - g_return_val_if_fail (GST_IS_PAD (pad), NULL); - - if (GST_RPAD_EXPLICIT_CAPS (pad) == NULL) { - const GstCaps *caps = gst_pad_get_pad_template_caps (pad); - - return gst_caps_copy (caps); - } - return gst_caps_copy (GST_RPAD_EXPLICIT_CAPS (pad)); -} - -static GstPadLinkReturn -gst_pad_explicit_link (GstPad * pad, const GstCaps * caps) -{ - g_return_val_if_fail (GST_IS_PAD (pad), GST_PAD_LINK_REFUSED); - g_return_val_if_fail (caps != NULL, GST_PAD_LINK_REFUSED); - - if (GST_RPAD_EXPLICIT_CAPS (pad) == NULL) { - return GST_PAD_LINK_DELAYED; - } - - return GST_PAD_LINK_OK; -} - -/** - * gst_pad_use_explicit_caps: - * @pad: a #GstPad to set to use explicit caps - * - * This function handles negotiation for pads that need to be set - * to particular caps under complete control of the element, based - * on some state in the element. This is often the case with - * decoders and other elements whose caps is determined by the data - * stream. - * - * WARNING: This function is a hack and will be replaced with something - * better in gstreamer-0.9. - */ -void -gst_pad_use_explicit_caps (GstPad * pad) -{ - g_return_if_fail (GST_IS_PAD (pad)); - - gst_pad_set_getcaps_function (pad, gst_pad_explicit_getcaps); - gst_pad_set_link_function (pad, gst_pad_explicit_link); - gst_caps_replace (&GST_RPAD_EXPLICIT_CAPS (pad), NULL); -} - -/** - * gst_pad_proxy_link: - * @pad: a #GstPad to proxy to. - * @caps: the #GstCaps to use in proxying. - * - * Proxies the link function to the specified pad. - * - * Returns: TRUE if the peer pad accepted the caps, FALSE otherwise. - */ -GstPadLinkReturn -gst_pad_proxy_link (GstPad * pad, const GstCaps * caps) -{ - return gst_pad_try_set_caps (pad, caps); -} - -/** - * gst_pad_is_negotiated: - * @pad: a #GstPad to get the negotiation status of - * - * Returns: TRUE if the pad has successfully negotiated caps. - */ -gboolean -gst_pad_is_negotiated (GstPad * pad) -{ - g_return_val_if_fail (GST_IS_PAD (pad), FALSE); - - if (!(pad = (GstPad *) GST_PAD_REALIZE (pad))) - return FALSE; - if (!GST_RPAD_LINK (pad)) - return FALSE; - - return (GST_RPAD_LINK (pad)->caps != NULL); -} - -/** - * gst_pad_get_negotiated_caps: - * @pad: a #GstPad to get the negotiated capabilites of - * - * Gets the currently negotiated caps of a pad. - * - * Returns: the currently negotiated caps of a pad, or NULL if the pad isn't - * negotiated. - */ -G_CONST_RETURN GstCaps * -gst_pad_get_negotiated_caps (GstPad * pad) -{ - g_return_val_if_fail (GST_IS_PAD (pad), NULL); - - if (!(pad = (GstPad *) GST_PAD_REALIZE (pad))) - return NULL; - if (!GST_RPAD_LINK (pad)) - return NULL; - - return GST_RPAD_LINK (pad)->caps; -} - /** * gst_pad_get_caps: * @pad: a #GstPad to get the capabilities of. @@ -2634,12 +1351,13 @@ gst_pad_get_negotiated_caps (GstPad * pad) * Gets the capabilities of this pad. * * Returns: the #GstCaps of this pad. This function returns a new caps, so use - * gst_caps_free to get rid of it. + * gst_caps_unref to get rid of it. */ GstCaps * gst_pad_get_caps (GstPad * pad) { GstRealPad *realpad; + GstCaps *result = NULL, *filter; g_return_val_if_fail (GST_IS_PAD (pad), NULL); @@ -2648,21 +1366,24 @@ gst_pad_get_caps (GstPad * pad) GST_CAT_DEBUG (GST_CAT_CAPS, "get pad caps of %s:%s (%p)", GST_DEBUG_PAD_NAME (realpad), realpad); - if (GST_PAD_IS_DISPATCHING (realpad)) + if (GST_PAD_IS_DISPATCHING (realpad)) { GST_CAT_DEBUG (GST_CAT_CAPS, - "pad %s:%s is already dispatching -- looking for a template", + "pad %s:%s is already dispatching!", GST_DEBUG_PAD_NAME (realpad)); + g_warning ("pad %s:%s recursively called getcaps!", GST_DEBUG_PAD_NAME (realpad)); + return NULL; + } - if (GST_RPAD_GETCAPSFUNC (realpad) && !GST_PAD_IS_DISPATCHING (realpad)) { - GstCaps *caps; + + if (GST_RPAD_GETCAPSFUNC (realpad)) { GST_CAT_DEBUG (GST_CAT_CAPS, "dispatching to pad getcaps function"); GST_FLAG_SET (realpad, GST_PAD_DISPATCHING); - caps = GST_RPAD_GETCAPSFUNC (realpad) (GST_PAD (realpad)); + result = GST_RPAD_GETCAPSFUNC (realpad) (GST_PAD (realpad)); GST_FLAG_UNSET (realpad, GST_PAD_DISPATCHING); - if (caps == NULL) { + if (result == NULL) { g_critical ("pad %s:%s returned NULL caps from getcaps function\n", GST_DEBUG_PAD_NAME (realpad)); } else { @@ -2671,54 +1392,87 @@ gst_pad_get_caps (GstPad * pad) if (GST_PAD_PAD_TEMPLATE (realpad)) { const GstCaps *templ_caps = GST_PAD_TEMPLATE_CAPS (GST_PAD_PAD_TEMPLATE (realpad)); - if (!gst_caps_is_subset (caps, templ_caps)) { + if (!gst_caps_is_subset (result, templ_caps)) { GstCaps *temp; GST_CAT_ERROR_OBJECT (GST_CAT_CAPS, pad, "pad returned caps %" GST_PTR_FORMAT " which are not a real subset of its template caps %" - GST_PTR_FORMAT, caps, templ_caps); + GST_PTR_FORMAT, result, templ_caps); g_warning ("pad %s:%s returned caps that are not a real subset of its template caps", GST_DEBUG_PAD_NAME (realpad)); - temp = gst_caps_intersect (templ_caps, caps); - gst_caps_free (caps); - caps = temp; + temp = gst_caps_intersect (templ_caps, result); + gst_caps_unref (result); + result = temp; } } #endif - return caps; + goto done; } } if (GST_PAD_PAD_TEMPLATE (realpad)) { GstPadTemplate *templ = GST_PAD_PAD_TEMPLATE (realpad); - const GstCaps *caps; - caps = GST_PAD_TEMPLATE_CAPS (templ); + result = GST_PAD_TEMPLATE_CAPS (templ); GST_CAT_DEBUG (GST_CAT_CAPS, - "using pad template %p with caps %" GST_PTR_FORMAT, templ, caps); + "using pad template %p with caps %p %" GST_PTR_FORMAT, templ, result, + result); -#if 0 - /* FIXME we should enable something like this someday, but this is - * a bit buggy */ - if (!gst_caps_is_fixed (caps)) { - g_warning - ("pad %s:%s (%p) has no getcaps function and the pad template returns non-fixed caps. Element is probably broken.\n", - GST_DEBUG_PAD_NAME (realpad), realpad); - } -#endif - - return gst_caps_copy (GST_PAD_TEMPLATE_CAPS (templ)); + result = gst_caps_copy (result); + goto done; } GST_CAT_DEBUG (GST_CAT_CAPS, "pad has no caps"); -#if 0 - /* FIXME enable */ - g_warning ("pad %s:%s (%p) has no pad template\n", - GST_DEBUG_PAD_NAME (realpad), realpad); -#endif + result = gst_caps_new_any (); - return gst_caps_new_any (); +done: + filter = GST_RPAD_APPFILTER (realpad); + + if (filter) { + GST_CAT_DEBUG (GST_CAT_CAPS, + "app filter %p %" GST_PTR_FORMAT, filter, filter); + result = gst_caps_intersect (result, filter); + GST_CAT_DEBUG (GST_CAT_CAPS, + "caps after intersection with app filter %p %" GST_PTR_FORMAT, result, + result); + } + + return result; +} + +/** + * gst_pad_set_caps: + * @pad: a #GstPad to set the capabilities of. + * @caps: a #GstCaps to set. + * + * Sets the capabilities of this pad. The caps must be fixed. Any previous + * caps on the pad will be unreffed. This function refs the caps so you should + * unref if as soon as you don't need it anymore. + * It is possible to set NULL caps, which will make the pad unnegotiated + * again. + * + * Returns: TRUE if the caps could be set. FALSE if the caps were not fixed + * or bad parameters were provided to this function. + */ +gboolean +gst_pad_set_caps (GstPad * pad, GstCaps * caps) +{ + g_return_val_if_fail (GST_IS_REAL_PAD (pad), FALSE); + + GST_LOCK (pad); + if (GST_PAD_CAPS (pad)) + gst_caps_unref (GST_PAD_CAPS (pad)); + + if (caps) + caps = gst_caps_ref (caps); + + GST_PAD_CAPS (pad) = caps; + GST_UNLOCK (pad); + + g_object_notify (G_OBJECT (pad), "caps"); + + return TRUE; } /** @@ -2740,77 +1494,9 @@ gst_pad_get_pad_template_caps (GstPad * pad) if (GST_PAD_PAD_TEMPLATE (pad)) return GST_PAD_TEMPLATE_CAPS (GST_PAD_PAD_TEMPLATE (pad)); -#if 0 - /* FIXME this should be enabled some day */ - /* wingo: why? mail the list during 0.9 when you find this :) */ - g_warning ("pad %s:%s (%p) has no pad template\n", - GST_DEBUG_PAD_NAME (realpad), realpad); -#endif - return gst_static_caps_get (&anycaps); } -/* FIXME 0.9: This function should probably die, or at least be renamed to - * get_caps_by_format. */ -/** - * gst_pad_template_get_caps_by_name: - * @templ: a #GstPadTemplate to get the capabilities of. - * @name: the name of the capability to get. - * - * Gets the capability with the given name from @templ. - * - * Returns: the #GstCaps of this pad template, or NULL if not found. If you - * intend to keep a reference on the caps, make a copy (see gst_caps_copy ()). - */ -const GstCaps * -gst_pad_template_get_caps_by_name (GstPadTemplate * templ, const gchar * name) -{ - GstCaps *caps; - - g_return_val_if_fail (templ != NULL, NULL); - - caps = GST_PAD_TEMPLATE_CAPS (templ); - if (!caps) - return NULL; - - /* FIXME */ - return NULL; -} - -/* FIXME 0.9: What good is this if it only works for already-negotiated pads? */ -/** - * gst_pad_check_compatibility: - * @srcpad: the source #GstPad to check. - * @sinkpad: the sink #GstPad to check against. - * - * Checks if two pads have compatible capabilities. If neither one has yet been - * negotiated, returns TRUE for no good reason. - * - * Returns: TRUE if they are compatible or if the capabilities could not be - * checked, FALSE if the capabilities are not compatible. - */ -gboolean -gst_pad_check_compatibility (GstPad * srcpad, GstPad * sinkpad) -{ - g_return_val_if_fail (GST_IS_PAD (srcpad), FALSE); - g_return_val_if_fail (GST_IS_PAD (sinkpad), FALSE); - - if (GST_PAD_CAPS (srcpad) && GST_PAD_CAPS (sinkpad)) { - if (!gst_caps_is_always_compatible (GST_PAD_CAPS (srcpad), - GST_PAD_CAPS (sinkpad))) { - return FALSE; - } else { - return TRUE; - } - } else { - GST_CAT_DEBUG (GST_CAT_PADS, - "could not check capabilities of pads (%s:%s) and (%s:%s) %p %p", - GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad), - GST_PAD_CAPS (srcpad), GST_PAD_CAPS (sinkpad)); - return TRUE; - } -} - /** * gst_pad_get_peer: * @pad: a #GstPad to get the peer of. @@ -2843,72 +1529,26 @@ gst_pad_get_allowed_caps (GstPad * pad) const GstCaps *mycaps; GstCaps *caps; GstCaps *peercaps; - GstCaps *icaps; - GstPadLink *link; g_return_val_if_fail (GST_IS_REAL_PAD (pad), NULL); GST_CAT_DEBUG (GST_CAT_PROPERTIES, "%s:%s: getting allowed caps", GST_DEBUG_PAD_NAME (pad)); - mycaps = gst_pad_get_pad_template_caps (pad); + mycaps = gst_pad_get_caps (pad); if (GST_RPAD_PEER (pad) == NULL) { - GST_CAT_DEBUG (GST_CAT_PROPERTIES, "%s:%s: no peer, returning template", + GST_CAT_DEBUG (GST_CAT_PROPERTIES, "%s:%s: no peer, returning own caps", GST_DEBUG_PAD_NAME (pad)); return gst_caps_copy (mycaps); } peercaps = gst_pad_get_caps (GST_PAD_PEER (pad)); caps = gst_caps_intersect (mycaps, peercaps); - gst_caps_free (peercaps); + gst_caps_unref (peercaps); - link = GST_RPAD_LINK (pad); - if (link->filtercaps) { - icaps = gst_caps_intersect (caps, link->filtercaps); - gst_caps_free (caps); - GST_CAT_DEBUG (GST_CAT_PROPERTIES, - "%s:%s: returning filtered intersection with peer", - GST_DEBUG_PAD_NAME (pad)); - return icaps; - } else { - GST_CAT_DEBUG (GST_CAT_PROPERTIES, - "%s:%s: returning unfiltered intersection with peer", - GST_DEBUG_PAD_NAME (pad)); - return caps; - } -} + GST_CAT_DEBUG (GST_CAT_CAPS, "allowed caps %" GST_PTR_FORMAT, caps); -/** - * gst_pad_caps_change_notify: - * @pad: a #GstPad - * - * Called to indicate that the return value of @pad's getcaps function may have - * changed, and that a renegotiation is suggested. - */ -void -gst_pad_caps_change_notify (GstPad * pad) -{ -} - -/** - * gst_pad_recover_caps_error: - * @pad: a #GstPad that had a failed capsnego - * @allowed: possible caps for the link - * - * Attempt to recover from a failed caps negotiation. This function - * is typically called by a plugin that exhausted its list of caps - * and wants the application to resolve the issue. The application - * should connect to the pad's caps_nego_failed signal and should - * resolve the issue by connecting another element for example. - * - * Returns: TRUE when the issue was resolved, dumps detailed information - * on the console and returns FALSE otherwise. - */ -gboolean -gst_pad_recover_caps_error (GstPad * pad, const GstCaps * allowed) -{ - /* FIXME */ - return FALSE; + return caps; } /** @@ -2916,9 +1556,12 @@ gst_pad_recover_caps_error (GstPad * pad, const GstCaps * allowed) * @pad: a source #GstPad * @offset: the offset of the new buffer in the stream * @size: the size of the new buffer + * @caps: the caps of the new buffer * * Allocates a new, empty buffer optimized to push to pad @pad. This - * function only works if @pad is a source pad. + * function only works if @pad is a source pad. You need to check the + * caps of the buffer after performing this function and renegotiate + * to the format if needed. * * Returns: a new, empty #GstBuffer, or NULL if there is an error */ @@ -2926,27 +1569,36 @@ GstBuffer * gst_pad_alloc_buffer (GstPad * pad, guint64 offset, gint size) { GstRealPad *peer; + GstBuffer *result = NULL; + GstPadBufferAllocFunction bufferallocfunc; - g_return_val_if_fail (GST_IS_PAD (pad), NULL); + g_return_val_if_fail (GST_IS_REAL_PAD (pad), NULL); g_return_val_if_fail (GST_PAD_IS_SRC (pad), NULL); - peer = GST_RPAD_PEER (pad); + if ((peer = GST_RPAD_PEER (pad)) == NULL) + goto fallback; - if (peer && peer->bufferallocfunc) { - GstBuffer *ret; + if ((bufferallocfunc = peer->bufferallocfunc) == NULL) + goto fallback; - GST_CAT_DEBUG (GST_CAT_BUFFER, "(%s:%s): getting buffer", - GST_DEBUG_PAD_NAME (pad)); - GST_CAT_DEBUG (GST_CAT_PADS, - "calling bufferallocfunc &%s (@%p) of peer pad %s:%s", - GST_DEBUG_FUNCPTR_NAME (peer->bufferallocfunc), - &peer->bufferallocfunc, GST_DEBUG_PAD_NAME (((GstPad *) peer))); + GST_CAT_DEBUG (GST_CAT_PADS, + "calling bufferallocfunc &%s (@%p) of peer pad %s:%s", + GST_DEBUG_FUNCPTR_NAME (bufferallocfunc), + &bufferallocfunc, GST_DEBUG_PAD_NAME (((GstPad *) peer))); - ret = (peer->bufferallocfunc) (GST_PAD (peer), offset, size); - if (ret) - return ret; - } - return gst_buffer_new_and_alloc (size); + result = bufferallocfunc (GST_PAD (peer), offset, size, GST_PAD_CAPS (pad)); + + if (result == NULL) + goto fallback; + + return result; + + /* fallback case */ +fallback: + result = gst_buffer_new_and_alloc (size); + gst_buffer_set_caps (result, GST_PAD_CAPS (pad)); + + return result; } static void @@ -2993,6 +1645,10 @@ gst_real_pad_dispose (GObject * object) g_assert (GST_REAL_PAD (pad)->ghostpads == NULL); } + /* clear the caps */ + gst_caps_replace (&GST_RPAD_CAPS (pad), NULL); + gst_caps_replace (&GST_RPAD_APPFILTER (pad), NULL); + if (GST_IS_ELEMENT (GST_OBJECT_PARENT (pad))) { GST_CAT_DEBUG (GST_CAT_REFCOUNTING, "removing pad from element '%s'", GST_OBJECT_NAME (GST_OBJECT (GST_ELEMENT (GST_OBJECT_PARENT (pad))))); @@ -3000,12 +1656,6 @@ gst_real_pad_dispose (GObject * object) gst_element_remove_pad (GST_ELEMENT (GST_OBJECT_PARENT (pad)), pad); } - if (GST_RPAD_EXPLICIT_CAPS (pad)) { - GST_ERROR_OBJECT (pad, "still explicit caps %" GST_PTR_FORMAT " set", - GST_RPAD_EXPLICIT_CAPS (pad)); - g_warning ("pad %p has still explicit caps set", pad); - gst_caps_replace (&GST_RPAD_EXPLICIT_CAPS (pad), NULL); - } G_OBJECT_CLASS (real_pad_parent_class)->dispose (object); } @@ -3118,9 +1768,6 @@ gst_pad_save_thyself (GstObject * object, xmlNodePtr parent) return parent; } -/* FIXME: shouldn't it be gst_pad_ghost_* ? - * dunno -- wingo 7 feb 2004 - */ /** * gst_ghost_pad_save_thyself: * @pad: a ghost #GstPad to save. @@ -3147,384 +1794,245 @@ gst_ghost_pad_save_thyself (GstPad * pad, xmlNodePtr parent) } #endif /* GST_DISABLE_LOADSAVE */ -static GstData * -_invent_event (GstPad * pad, GstBuffer * buffer) +/* should be called with pad lock held */ +static void +handle_pad_block (GstPad * pad) { - GstEvent *event; - GstEventType event_type; - guint64 offset; + GstPadBlockCallback callback; + gpointer user_data; + GstRealPad *realpad; - if (GST_BUFFER_OFFSET_IS_VALID (buffer)) - event_type = GST_FORMAT_DEFAULT; - else - event_type = GST_FORMAT_UNDEFINED; + realpad = GST_REAL_PAD (pad); - offset = GST_BUFFER_OFFSET (buffer); + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, + "signal block taken on pad %s:%s", GST_DEBUG_PAD_NAME (pad)); - if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) { - GstClockTime timestamp = GST_BUFFER_TIMESTAMP (buffer); - - event = gst_event_new_discontinuous (TRUE, - GST_FORMAT_TIME, timestamp, event_type, offset, GST_FORMAT_UNDEFINED); - GST_CAT_WARNING (GST_CAT_SCHEDULING, - "needed to invent a DISCONT %p (time %" G_GUINT64_FORMAT - ") for %s:%s => %s:%s", event, timestamp, - GST_DEBUG_PAD_NAME (GST_PAD_PEER (pad)), GST_DEBUG_PAD_NAME (pad)); + callback = realpad->block_callback; + if (callback) { + user_data = realpad->block_data; + GST_UNLOCK (pad); + callback (pad, TRUE, user_data); + GST_LOCK (pad); } else { - event = gst_event_new_discontinuous (TRUE, - event_type, offset, GST_FORMAT_UNDEFINED); - GST_CAT_WARNING (GST_CAT_SCHEDULING, - "needed to invent a DISCONT %p (no time) for %s:%s => %s:%s", event, - GST_DEBUG_PAD_NAME (GST_PAD_PEER (pad)), GST_DEBUG_PAD_NAME (pad)); + GST_PAD_BLOCK_SIGNAL (pad); } - return GST_DATA (event); + while (GST_RPAD_IS_BLOCKED (pad)) + GST_PAD_BLOCK_WAIT (pad); + + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "got unblocked"); + + callback = realpad->block_callback; + if (callback) { + user_data = realpad->block_data; + GST_UNLOCK (pad); + callback (pad, FALSE, user_data); + GST_LOCK (pad); + } else { + GST_PAD_BLOCK_SIGNAL (pad); + } } /** * gst_pad_push: * @pad: a source #GstPad. - * @data: the #GstData to push. + * @buffer: the #GstBuffer to push. * - * Pushes a buffer or an event to the peer of @pad. @pad must be linked. May + * Pushes a buffer to the peer of @pad. @pad must be linked. May * only be called by @pad's parent. + * + * Returns: a #GstFlowReturn from the peer pad. */ -void -gst_pad_push (GstPad * pad, GstData * data) +GstFlowReturn +gst_pad_push (GstPad * pad, GstBuffer * buffer) { GstRealPad *peer; + GstFlowReturn ret; + GstPadChainFunction chainfunc; - g_return_if_fail (GST_IS_PAD (pad)); - g_return_if_fail (GST_PAD_DIRECTION (pad) == GST_PAD_SRC); - g_return_if_fail (data != NULL); + g_return_val_if_fail (GST_IS_REAL_PAD (pad), GST_FLOW_ERROR); + g_return_val_if_fail (GST_PAD_DIRECTION (pad) == GST_PAD_SRC, GST_FLOW_ERROR); + g_return_val_if_fail (buffer != NULL, GST_FLOW_ERROR); + g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR); - DEBUG_DATA (pad, data, "gst_pad_push"); - if (!gst_probe_dispatcher_dispatch (&(GST_REAL_PAD (pad)->probedisp), &data)) - return; + GST_LOCK (pad); - if (!GST_PAD_IS_LINKED (pad)) { - GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, - "not pushing data %p as pad is unconnected", data); - gst_data_unref (data); - return; - } + while (GST_RPAD_IS_BLOCKED (pad)) + handle_pad_block (pad); - if (GST_IS_BUFFER (data) && !gst_pad_is_negotiated (pad)) { - g_warning ("pushing data on non-negotiated pad %s:%s, not allowed.", - GST_DEBUG_PAD_NAME (pad)); - gst_data_unref (data); - return; - } + if ((peer = GST_RPAD_PEER (pad)) == NULL) + goto not_linked; - GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "pushing"); - peer = GST_RPAD_PEER (pad); + if (!GST_RPAD_IS_ACTIVE (peer)) + goto not_active; - if (!peer) { - g_warning ("push on pad %s:%s but it is unlinked", - GST_DEBUG_PAD_NAME (pad)); - } else { - if (!GST_IS_EVENT (data) && !GST_PAD_IS_ACTIVE (peer)) { - g_warning ("push on peer of pad %s:%s but peer is not active", - GST_DEBUG_PAD_NAME (pad)); - return; - } + if ((chainfunc = peer->chainfunc) == NULL) + goto no_function; - if (peer->chainhandler) { - if (data) { - GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, - "calling chainhandler &%s of peer pad %s:%s", - GST_DEBUG_FUNCPTR_NAME (peer->chainhandler), - GST_DEBUG_PAD_NAME (GST_PAD (peer))); - if (!gst_probe_dispatcher_dispatch (&peer->probedisp, &data)) - return; + GST_UNLOCK (pad); - (peer->chainhandler) (GST_PAD (peer), data); - return; - } else { - g_warning ("trying to push a NULL buffer on pad %s:%s", - GST_DEBUG_PAD_NAME (peer)); - return; - } - } else { - g_warning ("internal error: push on pad %s:%s but it has no chainhandler", - GST_DEBUG_PAD_NAME (peer)); - } - } - /* clean up the mess here */ - if (data != NULL) - gst_data_unref (data); + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, + "calling chainfunction &%s of peer pad %s:%s", + GST_DEBUG_FUNCPTR_NAME (chainfunc), GST_DEBUG_PAD_NAME (peer)); + + ret = chainfunc (GST_PAD_CAST (peer), buffer); + + return ret; + + /* ERROR recovery here */ +not_linked: + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, + "pushing, but it was not linked"); + ret = GST_FLOW_NOT_CONNECTED; + goto error; + +not_active: + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "pushing, but it was inactive"); + ret = GST_FLOW_WRONG_STATE; + goto error; + +no_function: + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "pushing, but not chainhandler"); + g_warning ("push on pad %s:%s but it has no chainhandler, file a bug.", + GST_DEBUG_PAD_NAME (peer)); + ret = GST_FLOW_ERROR; + goto error; + +error: + GST_UNLOCK (pad); + + return ret; } /** * gst_pad_pull: * @pad: a sink #GstPad. + * @buffer: a pointer to hold the #GstBuffer. * - * Pulls an event or a buffer from the peer pad. May only be called by @pad's + * Pulls a buffer from the peer pad. May only be called by @pad's * parent. * - * Returns: a new #GstData from the peer pad. + * Returns: a #GstFlowReturn from the peer pad. */ -GstData * -gst_pad_pull (GstPad * pad) +GstFlowReturn +gst_pad_pull (GstPad * pad, GstBuffer ** buffer) { GstRealPad *peer; - GstData *data; + GstFlowReturn ret; + GstPadGetFunction getfunc; + g_return_val_if_fail (GST_IS_REAL_PAD (pad), GST_FLOW_ERROR); g_return_val_if_fail (GST_PAD_DIRECTION (pad) == GST_PAD_SINK, - GST_DATA (gst_event_new (GST_EVENT_INTERRUPT))); + GST_FLOW_ERROR); + g_return_val_if_fail (buffer != NULL, GST_FLOW_ERROR); - peer = GST_RPAD_PEER (pad); + GST_LOCK (pad); - if (!peer) { - GST_ELEMENT_ERROR (GST_PAD_PARENT (pad), CORE, PAD, (NULL), - ("pull on pad %s:%s but it was unlinked", GST_DEBUG_PAD_NAME (pad))); - } else { - restart: - if (peer->gethandler) { - GstPadLink *link = GST_RPAD_LINK (pad); + while (GST_RPAD_IS_BLOCKED (pad)) + handle_pad_block (pad); - GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, - "calling gethandler %s of peer pad %s:%s", - GST_DEBUG_FUNCPTR_NAME (peer->gethandler), GST_DEBUG_PAD_NAME (peer)); + if ((peer = GST_RPAD_PEER (pad)) == NULL) + goto not_connected; - if (link->temp_store) { - g_assert (link->engaged); - GST_DEBUG ("moving temp_store %p to data", link->temp_store); - data = link->temp_store; - link->temp_store = NULL; - } else { - data = (peer->gethandler) (GST_PAD (peer)); - /* refetch - we might have been relinked */ - link = GST_RPAD_LINK (pad); - peer = GST_RPAD_PEER (pad); - } + if ((getfunc = peer->getfunc) == NULL) + goto no_function; - if (data) { - if (!link->engaged) { - g_assert (link->temp_store == NULL); - if (GST_IS_BUFFER (data)) { - GST_DEBUG ("moving data buffer %p back to temp_store", data); - link->temp_store = data; - link->engaged = TRUE; - data = _invent_event (pad, GST_BUFFER (data)); - } else if (GST_IS_EVENT (data) && - GST_EVENT_TYPE (data) == GST_EVENT_DISCONTINUOUS && - GST_EVENT_DISCONT_NEW_MEDIA (data)) { - link->engaged = TRUE; - GST_CAT_LOG (GST_CAT_SCHEDULING, - "link engaged by discont event %p for pad %s:%s", data, - GST_DEBUG_PAD_NAME (pad)); - } - } - GST_DEBUG ("calling gst_probe_dispatcher_dispatch on data %p", data); - if (!gst_probe_dispatcher_dispatch (&peer->probedisp, &data)) - goto restart; - DEBUG_DATA (pad, data, "gst_pad_pull returned"); - return data; - } + GST_UNLOCK (pad); - /* no null buffers allowed */ - GST_ELEMENT_ERROR (GST_PAD_PARENT (pad), CORE, PAD, (NULL), - ("NULL buffer during pull on %s:%s", GST_DEBUG_PAD_NAME (pad))); - } else { - GST_ELEMENT_ERROR (GST_PAD_PARENT (pad), CORE, PAD, (NULL), - ("pull on pad %s:%s but the peer pad %s:%s has no gethandler", - GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (peer))); - } - } - data = GST_DATA (gst_event_new (GST_EVENT_INTERRUPT)); - DEBUG_DATA (pad, data, "gst_pad_pull returned created"); - return data; -} + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, + "calling getfunc %s of peer pad %s:%s", + GST_DEBUG_FUNCPTR_NAME (getfunc), GST_DEBUG_PAD_NAME (peer)); -GstData * -gst_pad_collect_array (GstScheduler * scheduler, GstPad ** selected, - GstPad ** padlist) -{ - GstSchedulerClass *klass = GST_SCHEDULER_GET_CLASS (scheduler); + ret = getfunc (GST_PAD_CAST (peer), buffer); - if (!GST_FLAG_IS_SET (scheduler, GST_SCHEDULER_FLAG_NEW_API) || - !klass->pad_select) { - /* better randomness? */ - if (selected) - *selected = padlist[0]; - return gst_pad_pull (padlist[0]); - } else { - GstPad *select; + return ret; - return klass->pad_select (scheduler, selected ? selected : &select, - padlist); - } + /* ERROR recovery here */ +not_connected: + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, + "pulling, but it was not linked"); + ret = GST_FLOW_NOT_CONNECTED; + goto error; + +no_function: + GST_ELEMENT_ERROR (GST_PAD_PARENT (pad), CORE, PAD, (NULL), + ("pull on pad %s:%s but the peer pad %s:%s has no getfunc", + GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (peer))); + ret = GST_FLOW_ERROR; + goto error; + +error: + GST_UNLOCK (pad); + + return ret; } /** - * gst_pad_collectv: - * @selected: set to the pad the buffer comes from if not NULL - * @padlist: a #GList of sink pads. + * gst_pad_pull_range: + * @pad: a sink #GstPad. + * @buffer: a pointer to hold the #GstBuffer. + * @offset: The start offset of the buffer + * @length: The length of the buffer * - * Waits for a buffer on any of the list of pads. Each #GstPad in @padlist must - * belong to the same element and be owned by the caller. + * Pulls a buffer from the peer pad. May only be called by @pad's + * parent. * - * Returns: the #GstData that was available + * Returns: a #GstFlowReturn from the peer pad. */ -GstData * -gst_pad_collectv (GstPad ** selected, const GList * padlist) +GstFlowReturn +gst_pad_pull_range (GstPad * pad, guint64 offset, guint size, + GstBuffer ** buffer) { - /* need to use alloca here because we must not leak data */ - GstPad **pads; - GstPad *test; - GstElement *element = NULL; - int i = 0; + GstRealPad *peer; + GstFlowReturn ret; + GstPadGetRangeFunction getrangefunc; - g_return_val_if_fail (padlist != NULL, NULL); - pads = g_alloca (sizeof (gpointer) * (g_list_length ((GList *) padlist) + 1)); - for (; padlist; padlist = g_list_next (padlist)) { - test = GST_PAD (padlist->data); - g_return_val_if_fail (GST_IS_PAD (test), NULL); - g_return_val_if_fail (GST_PAD_IS_SINK (test), NULL); - if (element) { - g_return_val_if_fail (element == gst_pad_get_parent (test), NULL); - } else { - element = gst_pad_get_parent (test); - } - pads[i++] = test; - } - pads[i] = NULL; + g_return_val_if_fail (GST_IS_REAL_PAD (pad), GST_FLOW_ERROR); + g_return_val_if_fail (GST_PAD_DIRECTION (pad) == GST_PAD_SINK, + GST_FLOW_ERROR); + g_return_val_if_fail (buffer != NULL, GST_FLOW_ERROR); - return gst_pad_collect_array (GST_ELEMENT_SCHED (element), selected, pads); -} + GST_LOCK (pad); -/** - * gst_pad_collect: - * @selected: set to the pad the buffer comes from if not NULL - * @pad: first pad - * @...: more sink pads. - * - * Waits for a buffer on the given set of pads. - * - * Returns: the #GstData that was available. - */ -GstData * -gst_pad_collect (GstPad ** selected, GstPad * pad, ...) -{ - GstData *result; - va_list var_args; + while (GST_RPAD_IS_BLOCKED (pad)) + handle_pad_block (pad); - g_return_val_if_fail (GST_IS_PAD (pad), NULL); + if ((peer = GST_RPAD_PEER (pad)) == NULL) + goto not_connected; - va_start (var_args, pad); + if ((getrangefunc = peer->getrangefunc) == NULL) + goto no_function; - result = gst_pad_collect_valist (selected, pad, var_args); + GST_UNLOCK (pad); - va_end (var_args); + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, + "calling getrangefunc %s of peer pad %s:%s", + GST_DEBUG_FUNCPTR_NAME (getrangefunc), GST_DEBUG_PAD_NAME (peer)); - return result; -} + ret = getrangefunc (GST_PAD_CAST (peer), offset, size, buffer); -/** - * gst_pad_collect_valist: - * @selected: set to the pad the buffer comes from if not NULL - * @pad: first pad - * @...: more sink pads. - * - * Waits for a buffer on the given set of pads. - * - * Returns: the #GstData that was available. - */ -GstData * -gst_pad_collect_valist (GstPad ** selected, GstPad * pad, va_list var_args) -{ - GstPad **padlist; - GstElement *element = NULL; - gint i = 0, maxlength; + return ret; - g_return_val_if_fail (GST_IS_PAD (pad), NULL); + /* ERROR recovery here */ +not_connected: + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, + "pulling range, but it was not linked"); + ret = GST_FLOW_NOT_CONNECTED; + goto error; - element = gst_pad_get_parent (pad); - maxlength = element->numsinkpads; - /* can we make this list a bit smaller than this upper limit? */ - padlist = g_alloca (sizeof (gpointer) * (maxlength + 1)); - while (pad) { - g_return_val_if_fail (i < maxlength, NULL); - g_return_val_if_fail (element == gst_pad_get_parent (pad), NULL); - padlist[i++] = pad; - pad = va_arg (var_args, GstPad *); - } - padlist[i] = NULL; - return gst_pad_collect_array (GST_ELEMENT_SCHED (element), selected, padlist); -} +no_function: + GST_ELEMENT_ERROR (GST_PAD_PARENT (pad), CORE, PAD, (NULL), + ("pullrange on pad %s:%s but the peer pad %s:%s has no getrangefunction", + GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (peer))); + ret = GST_FLOW_ERROR; + goto error; -/** - * gst_pad_selectv: - * @padlist: a #GList of sink pads. - * - * Waits for a buffer on any of the list of pads. Each #GstPad in @padlist must - * be owned by the calling code. - * - * Returns: the #GstPad that has a buffer available. - * Use #gst_pad_pull() to get the buffer. - */ -GstPad * -gst_pad_selectv (GList * padlist) -{ - return NULL; -} +error: + GST_UNLOCK (pad); -/** - * gst_pad_select_valist: - * @pad: a first #GstPad to perform the select on. - * @varargs: A va_list of more pads to select on. - * - * Waits for a buffer on the given set of pads. - * - * Returns: the #GstPad that has a buffer available. - * Use #gst_pad_pull() to get the buffer. - */ -GstPad * -gst_pad_select_valist (GstPad * pad, va_list var_args) -{ - GstPad *result; - GList *padlist = NULL; + return ret; - if (pad == NULL) - return NULL; - while (pad) { - padlist = g_list_prepend (padlist, pad); - pad = va_arg (var_args, GstPad *); - } - result = gst_pad_selectv (padlist); - g_list_free (padlist); - - return result; -} - -/** - * gst_pad_select: - * @pad: a first sink #GstPad to perform the select on. - * @...: A NULL-terminated list of more pads to select on. - * - * Waits for a buffer on the given set of pads. - * - * Returns: the #GstPad that has a buffer available. - * Use #gst_pad_pull() to get the buffer. - */ -GstPad * -gst_pad_select (GstPad * pad, ...) -{ - GstPad *result; - va_list var_args; - - if (pad == NULL) - return NULL; - - va_start (var_args, pad); - - result = gst_pad_select_valist (pad, var_args); - - va_end (var_args); - - return result; } /************************************************************************ @@ -3590,7 +2098,7 @@ gst_pad_template_dispose (GObject * object) g_free (GST_PAD_TEMPLATE_NAME_TEMPLATE (templ)); if (GST_PAD_TEMPLATE_CAPS (templ)) { - gst_caps_free (GST_PAD_TEMPLATE_CAPS (templ)); + gst_caps_unref (GST_PAD_TEMPLATE_CAPS (templ)); } G_OBJECT_CLASS (padtemplate_parent_class)->dispose (object); @@ -3694,16 +2202,6 @@ gst_pad_template_new (const gchar * name_template, if (!name_is_valid (name_template, presence)) return NULL; -#if 0 -#ifdef USE_POISONING - if (caps) { - GstCaps *newcaps = gst_caps_copy (caps); - - gst_caps_free (caps); - caps = newcaps; - } -#endif -#endif new = g_object_new (gst_pad_template_get_type (), "name", name_template, NULL); @@ -3965,21 +2463,23 @@ gst_pad_get_internal_links (GstPad * pad) rpad = GST_PAD_REALIZE (pad); if (GST_RPAD_INTLINKFUNC (rpad)) - res = GST_RPAD_INTLINKFUNC (rpad) (GST_PAD (rpad)); + res = GST_RPAD_INTLINKFUNC (rpad) (GST_PAD_CAST (rpad)); return res; } static gboolean -gst_pad_event_default_dispatch (GstPad * pad, GstElement * element, - GstEvent * event) +gst_pad_event_default_dispatch (GstPad * pad, GstEvent * event) { GList *orig, *pads; + gboolean result; GST_INFO_OBJECT (pad, "Sending event %p to all internally linked pads", event); + result = (GST_PAD_DIRECTION (pad) == GST_PAD_SINK); + orig = pads = gst_pad_get_internal_links (pad); while (pads) { @@ -3995,24 +2495,25 @@ gst_pad_event_default_dispatch (GstPad * pad, GstElement * element, GST_LOG_OBJECT (pad, "Reffing and sending event %p to %s:%s", event, GST_DEBUG_PAD_NAME (eventpad)); gst_event_ref (event); - gst_pad_push (eventpad, GST_DATA (event)); + gst_pad_push_event (eventpad, event); } else { - GstPad *peerpad = GST_PAD (GST_RPAD_PEER (eventpad)); - /* we only send the event on one pad, multi-sinkpad elements * should implement a handler */ - g_list_free (orig); GST_LOG_OBJECT (pad, "sending event %p to one sink pad %s:%s", event, GST_DEBUG_PAD_NAME (eventpad)); - return gst_pad_send_event (peerpad, event); + result = gst_pad_push_event (eventpad, event); + goto done; } } } /* we handled the incoming event so we unref once */ GST_LOG_OBJECT (pad, "handled event %p, unreffing", event); gst_event_unref (event); + +done: g_list_free (orig); - return (GST_PAD_DIRECTION (pad) == GST_PAD_SINK); + + return result; } /** @@ -4031,47 +2532,10 @@ gst_pad_event_default_dispatch (GstPad * pad, GstElement * element, gboolean gst_pad_event_default (GstPad * pad, GstEvent * event) { - GstElement *element; - g_return_val_if_fail (GST_IS_PAD (pad), FALSE); g_return_val_if_fail (event != NULL, FALSE); - element = GST_PAD_PARENT (pad); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_EOS: - gst_pad_event_default_dispatch (pad, element, event); - gst_element_set_eos (element); - break; - case GST_EVENT_DISCONTINUOUS: - { - guint64 time; - - if (gst_element_requires_clock (element) && element->clock) { - if (gst_event_discont_get_value (event, GST_FORMAT_TIME, &time)) { - gst_element_set_time (element, time); - } else { - GstFormat format = GST_FORMAT_TIME; - guint i; - - for (i = 0; i < event->event_data.discont.noffsets; i++) { - if (gst_pad_convert (pad, - event->event_data.discont.offsets[i].format, - event->event_data.discont.offsets[i].value, &format, - &time)) { - gst_element_set_time (element, time); - } else if (i == event->event_data.discont.noffsets) { - g_warning - ("can't adjust clock to new time when time not provided"); - } - } - } - } - } - default: - return gst_pad_event_default_dispatch (pad, element, event); - } - return TRUE; + return gst_pad_event_default_dispatch (pad, event); } /** @@ -4116,44 +2580,89 @@ gst_pad_dispatcher (GstPad * pad, GstPadDispatcherFunction dispatch, return res; } +/** + * gst_pad_push_event: + * @pad: a #GstPad to push the event to. + * @event: the #GstEvent to send to the pad. + * + * Sends the event to the peer of the given pad. + * + * Returns: TRUE if the event was handled. + */ +gboolean +gst_pad_push_event (GstPad * pad, GstEvent * event) +{ + GstRealPad *peerpad; + gboolean result; + + g_return_val_if_fail (GST_IS_REAL_PAD (pad), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + GST_LOCK (pad); + peerpad = GST_RPAD_PEER (pad); + if (peerpad == NULL) + goto not_linked; + + GST_UNLOCK (pad); + + result = gst_pad_send_event (GST_PAD_CAST (peerpad), event); + + return result; + + /* ERROR handling */ +not_linked: + result = FALSE; + goto error; + +error: + GST_UNLOCK (pad); + + return result; +} + /** * gst_pad_send_event: * @pad: a #GstPad to send the event to. * @event: the #GstEvent to send to the pad. * - * Sends the event to the pad. + * Sends the event to the pad. This function can be used + * by applications to send events in the pipeline. * * Returns: TRUE if the event was handled. */ gboolean gst_pad_send_event (GstPad * pad, GstEvent * event) { - gboolean success = FALSE; + gboolean result = FALSE; GstRealPad *rpad; - GstElement *parent; + GstPadEventFunction eventfunc; g_return_val_if_fail (GST_IS_PAD (pad), FALSE); g_return_val_if_fail (event != NULL, FALSE); - parent = gst_pad_get_parent (pad); - g_return_val_if_fail (GST_STATE (parent) >= GST_STATE_PAUSED, FALSE); rpad = GST_PAD_REALIZE (pad); - if (GST_EVENT_SRC (event) == NULL) GST_EVENT_SRC (event) = gst_object_ref (GST_OBJECT (rpad)); GST_CAT_DEBUG (GST_CAT_EVENT, "have event type %d on pad %s:%s", GST_EVENT_TYPE (event), GST_DEBUG_PAD_NAME (rpad)); - if (GST_RPAD_EVENTHANDLER (rpad)) - success = GST_RPAD_EVENTHANDLER (rpad) (GST_PAD (rpad), event); - else { - g_warning ("pad %s:%s has no event handler", GST_DEBUG_PAD_NAME (rpad)); - gst_event_unref (event); - } + if ((eventfunc = GST_RPAD_EVENTFUNC (rpad)) == NULL) + goto no_function; - return success; + result = eventfunc (GST_PAD_CAST (rpad), event); + + return result; + + /* ERROR handling */ +no_function: + g_warning ("pad %s:%s has no event handler, file a bug.", + GST_DEBUG_PAD_NAME (rpad)); + result = FALSE; + gst_event_unref (event); + + return result; } typedef struct @@ -4374,82 +2883,3 @@ gst_pad_get_formats (GstPad * pad) return NULL; } - -#define CALL_CHAINFUNC(pad, data) G_STMT_START {\ - GstData *__temp = (data); \ - DEBUG_DATA (pad, __temp, "calling chain function with "); \ - if (GST_IS_EVENT (__temp) && \ - !GST_FLAG_IS_SET (gst_pad_get_parent (pad), GST_ELEMENT_EVENT_AWARE)) { \ - gst_pad_send_event (pad, GST_EVENT (__temp)); \ - } else { \ - GST_RPAD_CHAINFUNC (pad) (pad, __temp); \ - } \ -}G_STMT_END -/** - * gst_pad_call_chain_function: - * @pad: sink pad to call chain function on - * @data: data to call the chain function with - * - * Calls the chain function of the given pad while making sure the internal - * consistency is kept. Use this function inside schedulers instead of calling - * the chain function yourself. - */ -void -gst_pad_call_chain_function (GstPad * pad, GstData * data) -{ - GstPadLink *link; - - g_return_if_fail (GST_IS_REAL_PAD (pad)); - g_return_if_fail (GST_PAD_IS_SINK (pad)); - g_return_if_fail (data != NULL); - g_return_if_fail (GST_RPAD_CHAINFUNC (pad) != NULL); - g_return_if_fail (GST_RPAD_LINK (pad) != NULL); - - link = GST_RPAD_LINK (pad); - if (!link->engaged) { - g_assert (link->temp_store == NULL); - if (GST_IS_BUFFER (data)) { - GST_DEBUG ("moving data buffer %p back to temp_store", data); - link->temp_store = data; - link->engaged = TRUE; - CALL_CHAINFUNC (pad, _invent_event (pad, GST_BUFFER (data))); - link = GST_RPAD_LINK (pad); - if (link->temp_store == NULL) /* happens after relinking in chainfunc */ - return; - g_assert (link->temp_store == data); - link->temp_store = NULL; - } else if (GST_IS_EVENT (data) && - GST_EVENT_TYPE (data) == GST_EVENT_DISCONTINUOUS && - GST_EVENT_DISCONT_NEW_MEDIA (data)) { - link->engaged = TRUE; - GST_CAT_LOG (GST_CAT_SCHEDULING, - "link engaged by discont event %p for pad %s:%s", data, - GST_DEBUG_PAD_NAME (pad)); - } - } - CALL_CHAINFUNC (pad, data); -} - -/** - * gst_pad_call_get_function: - * @pad: sink pad to call chain function on - * - * Calls the get function of the given pad while making sure the internal - * consistency is kept. Use this function inside schedulers instead of calling - * the get function yourself. - * - * Returns: the data provided by the pad or NULL if no data was available. - */ -GstData * -gst_pad_call_get_function (GstPad * pad) -{ - GstData *data; - - g_return_val_if_fail (GST_IS_REAL_PAD (pad), NULL); - g_return_val_if_fail (GST_PAD_IS_SRC (pad), NULL); - g_return_val_if_fail (GST_RPAD_GETFUNC (pad) != NULL, NULL); - - data = GST_RPAD_GETFUNC (pad) (pad); - DEBUG_DATA (pad, data, "getfunction returned"); - return data; -} diff --git a/gst/gstpad.h b/gst/gstpad.h index b3b661fe00..d0974933a9 100644 --- a/gst/gstpad.h +++ b/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 diff --git a/gst/gstpipeline.c b/gst/gstpipeline.c index acfb8f3991..73abd8ffa3 100644 --- a/gst/gstpipeline.c +++ b/gst/gstpipeline.c @@ -1,6 +1,6 @@ /* GStreamer * Copyright (C) 1999,2000 Erik Walthinsen - * 2000 Wim Taymans + * 2004 Wim Taymans * * 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 "); + "Erik Walthinsen " "Wim Taymans "); /* 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); } diff --git a/gst/gstpipeline.h b/gst/gstpipeline.h index 5dd5b11c06..d8e31805c1 100644 --- a/gst/gstpipeline.h +++ b/gst/gstpipeline.h @@ -24,7 +24,9 @@ #ifndef __GST_PIPELINE_H__ #define __GST_PIPELINE_H__ +#include #include +#include 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 diff --git a/gst/gstplugin.c b/gst/gstplugin.c index 0fbfc8eb16..429853c724 100644 --- a/gst/gstplugin.c +++ b/gst/gstplugin.c @@ -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 diff --git a/gst/gstplugin.h b/gst/gstplugin.h index bf5d8c670b..058cb1f352 100644 --- a/gst/gstplugin.h +++ b/gst/gstplugin.h @@ -63,7 +63,7 @@ struct _GstPluginDesc { gchar *license; /* effective license of plugin */ gchar *package; /* package plugin belongs to */ gchar *origin; /* URL to provider of plugin */ - + gpointer _gst_reserved[GST_PADDING]; }; @@ -71,25 +71,25 @@ struct _GstPlugin { GstPluginDesc desc; gchar * filename; - GList * features; /* list of features provided */ - gint numfeatures; + GList * features; /* list of features provided */ + gint numfeatures; - gpointer manager; /* managing registry */ - GModule * module; /* contains the module if plugin is loaded */ + gpointer manager; /* managing registry */ + GModule * module; /* contains the module if the plugin is loaded */ gpointer _gst_reserved[GST_PADDING]; }; #define GST_PLUGIN_DEFINE(major,minor,name,description,init,version,license,package,origin) \ -GST_PLUGIN_EXPORT GstPluginDesc gst_plugin_desc = { \ +GST_PLUGIN_EXPORT GstPluginDesc gst_plugin_desc = { \ major, \ minor, \ name, \ - description, \ + description, \ init, \ NULL, \ version, \ - license, \ + license, \ package, \ origin, \ GST_PADDING_INIT \ @@ -102,18 +102,18 @@ _gst_plugin_static_init__ ##init (void) \ static GstPluginDesc plugin_desc_ = { \ major, \ minor, \ - name, \ - description, \ - init, \ - NULL, \ + name, \ + description, \ + init, \ + NULL, \ version, \ - license, \ + license, \ package, \ origin, \ GST_PADDING_INIT \ }; \ _gst_plugin_register_static (&plugin_desc_); \ -} +} #define GST_LICENSE_UNKNOWN "unknown" @@ -125,23 +125,22 @@ typedef gboolean (*GstPluginFilter) (GstPlugin *plugin, #define GST_TYPE_PLUGIN (gst_plugin_get_type()) GType gst_plugin_get_type (void); void _gst_plugin_initialize (void); -void _gst_plugin_register_static (GstPluginDesc *desc); +void _gst_plugin_register_static (GstPluginDesc *desc); G_CONST_RETURN gchar* gst_plugin_get_name (GstPlugin *plugin); G_CONST_RETURN gchar* gst_plugin_get_description (GstPlugin *plugin); G_CONST_RETURN gchar* gst_plugin_get_filename (GstPlugin *plugin); -G_CONST_RETURN gchar* gst_plugin_get_version (GstPlugin *plugin); G_CONST_RETURN gchar* gst_plugin_get_license (GstPlugin *plugin); G_CONST_RETURN gchar* gst_plugin_get_package (GstPlugin *plugin); -G_CONST_RETURN gchar* gst_plugin_get_origin (GstPlugin *plugin); +G_CONST_RETURN gchar* gst_plugin_get_origin (GstPlugin *plugin); GModule * gst_plugin_get_module (GstPlugin *plugin); gboolean gst_plugin_is_loaded (GstPlugin *plugin); -GList* gst_plugin_feature_filter (GstPlugin *plugin, +GList* gst_plugin_feature_filter (GstPlugin *plugin, GstPluginFeatureFilter filter, gboolean first, gpointer user_data); -GList* gst_plugin_list_feature_filter (GList *list, +GList* gst_plugin_list_feature_filter (GList *list, GstPluginFeatureFilter filter, gboolean first, gpointer user_data); @@ -150,15 +149,15 @@ gboolean gst_plugin_name_filter (GstPlugin *plugin, const gchar *name); GList* gst_plugin_get_feature_list (GstPlugin *plugin); GstPluginFeature* gst_plugin_find_feature (GstPlugin *plugin, const gchar *name, GType type); -gboolean gst_plugin_check_file (const gchar *filename, GError** error); -GstPlugin * gst_plugin_load_file (const gchar *filename, GError** error); -gboolean gst_plugin_unload_plugin (GstPlugin *plugin); +gboolean gst_plugin_check_file (const gchar *filename, GError** error); +GstPlugin * gst_plugin_load_file (const gchar *filename, GError** error); +gboolean gst_plugin_unload_plugin (GstPlugin *plugin); void gst_plugin_add_feature (GstPlugin *plugin, GstPluginFeature *feature); /* shortcuts to load from the registry pool */ -gboolean gst_plugin_load (const gchar *name); -gboolean gst_library_load (const gchar *name); +gboolean gst_plugin_load (const gchar *name); +gboolean gst_library_load (const gchar *name); G_END_DECLS diff --git a/gst/gstqueue.c b/gst/gstqueue.c index 6e54087496..8727967025 100644 --- a/gst/gstqueue.c +++ b/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) diff --git a/gst/gstqueue.h b/gst/gstqueue.h index e473f404ef..35ae2e2949 100644 --- a/gst/gstqueue.h +++ b/gst/gstqueue.h @@ -26,6 +26,7 @@ #include +#include 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]; }; diff --git a/gst/gstscheduler.c b/gst/gstscheduler.c index b74bc9e2a3..053907d0d4 100644 --- a/gst/gstscheduler.c +++ b/gst/gstscheduler.c @@ -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)); diff --git a/gst/gstscheduler.h b/gst/gstscheduler.h index 9cca5ad1d6..c24f07ab06 100644 --- a/gst/gstscheduler.h +++ b/gst/gstscheduler.h @@ -27,6 +27,7 @@ #include #include #include +#include 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; diff --git a/gst/gstsystemclock.c b/gst/gstsystemclock.c index 83a0eeb706..c633232187 100644 --- a/gst/gstsystemclock.c +++ b/gst/gstsystemclock.c @@ -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 diff --git a/gst/gsttypefind.c b/gst/gsttypefind.c index e6ae25f7af..fcb9058b1f 100644 --- a/gst/gsttypefind.c +++ b/gst/gsttypefind.c @@ -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) { diff --git a/gst/gsttypes.h b/gst/gsttypes.h index 0c091cba1b..8182ff8aa8 100644 --- a/gst/gsttypes.h +++ b/gst/gsttypes.h @@ -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, diff --git a/gst/gstutils.c b/gst/gstutils.c index e00e23e612..dd585bb50d 100644 --- a/gst/gstutils.c +++ b/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); +} diff --git a/gst/gstutils.h b/gst/gstutils.h index 86b7d9b883..4849b46897 100644 --- a/gst/gstutils.h +++ b/gst/gstutils.h @@ -25,7 +25,7 @@ #define __GST_UTILS_H__ #include -#include +#include 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__ */ diff --git a/gst/parse/grammar.y b/gst/parse/grammar.y index 9c8a6b5661..8f4a7dd101 100644 --- a/gst/parse/grammar.y +++ b/gst/parse/grammar.y @@ -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; diff --git a/gst/registries/gstxmlregistry.c b/gst/registries/gstxmlregistry.c index 396ea1359e..b7f624acbd 100644 --- a/gst/registries/gstxmlregistry.c +++ b/gst/registries/gstxmlregistry.c @@ -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; } diff --git a/gst/schedulers/Makefile.am b/gst/schedulers/Makefile.am index b67ccf691a..4d7ee1a3ae 100644 --- a/gst/schedulers/Makefile.am +++ b/gst/schedulers/Makefile.am @@ -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 \ diff --git a/libs/gst/bytestream/bytestream.c b/libs/gst/bytestream/bytestream.c index 2a1ef69cbc..8e5b4cc66d 100644 --- a/libs/gst/bytestream/bytestream.c +++ b/libs/gst/bytestream/bytestream.c @@ -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; diff --git a/libs/gst/bytestream/filepad.c b/libs/gst/bytestream/filepad.c index ee93f36574..588e4f396a 100644 --- a/libs/gst/bytestream/filepad.c +++ b/libs/gst/bytestream/filepad.c @@ -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)); } diff --git a/libs/gst/dataprotocol/dataprotocol.c b/libs/gst/dataprotocol/dataprotocol.c index 27512987ea..398ae117ac 100644 --- a/libs/gst/dataprotocol/dataprotocol.c +++ b/libs/gst/dataprotocol/dataprotocol.c @@ -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)); diff --git a/plugins/elements/Makefile.am b/plugins/elements/Makefile.am index dc18de4fc0..057cfedbe1 100644 --- a/plugins/elements/Makefile.am +++ b/plugins/elements/Makefile.am @@ -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) \ diff --git a/plugins/elements/gstelements.c b/plugins/elements/gstelements.c index af4bcb1f0b..6442e200d1 100644 --- a/plugins/elements/gstelements.c +++ b/plugins/elements/gstelements.c @@ -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 diff --git a/plugins/elements/gstfakesink.c b/plugins/elements/gstfakesink.c index 851b6f9375..76135d2593 100644 --- a/plugins/elements/gstfakesink.c +++ b/plugins/elements/gstfakesink.c @@ -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)); diff --git a/plugins/elements/gstfakesrc.c b/plugins/elements/gstfakesrc.c index 0b64860e90..570f0fe661 100644 --- a/plugins/elements/gstfakesrc.c +++ b/plugins/elements/gstfakesrc.c @@ -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; } diff --git a/plugins/elements/gstfakesrc.h b/plugins/elements/gstfakesrc.h index 5214f0bb7c..717898ccc8 100644 --- a/plugins/elements/gstfakesrc.h +++ b/plugins/elements/gstfakesrc.h @@ -79,6 +79,8 @@ struct _GstFakeSrc { gboolean loop_based; gboolean eos; + GstTask *task; + GstFakeSrcOutputType output; GstFakeSrcDataType data; GstFakeSrcSizeType sizetype; diff --git a/plugins/elements/gstfilesrc.c b/plugins/elements/gstfilesrc.c index 2a21eb6da0..5dca556b21 100644 --- a/plugins/elements/gstfilesrc.c +++ b/plugins/elements/gstfilesrc.c @@ -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 diff --git a/plugins/elements/gstfilesrc.h b/plugins/elements/gstfilesrc.h index 17230088c4..d69f0e8f4e 100644 --- a/plugins/elements/gstfilesrc.h +++ b/plugins/elements/gstfilesrc.h @@ -69,6 +69,8 @@ struct _GstFileSrc { gboolean is_regular; /* whether it's (symlink to) a regular file */ + GstTask *task; + GstBuffer *mapbuf; size_t mapsize; diff --git a/plugins/elements/gstidentity.c b/plugins/elements/gstidentity.c index 835697c209..12948950a8 100644 --- a/plugins/elements/gstidentity.c +++ b/plugins/elements/gstidentity.c @@ -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: diff --git a/plugins/elements/gstqueue.c b/plugins/elements/gstqueue.c index 6e54087496..8727967025 100644 --- a/plugins/elements/gstqueue.c +++ b/plugins/elements/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) diff --git a/plugins/elements/gstqueue.h b/plugins/elements/gstqueue.h index e473f404ef..35ae2e2949 100644 --- a/plugins/elements/gstqueue.h +++ b/plugins/elements/gstqueue.h @@ -26,6 +26,7 @@ #include +#include 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]; }; diff --git a/po/LINGUAS b/po/LINGUAS index ba285eea4e..ad01c63c19 100644 --- a/po/LINGUAS +++ b/po/LINGUAS @@ -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 diff --git a/tests/old/testsuite/states/locked.c b/tests/old/testsuite/states/locked.c index ecf969ec3e..e73413c273 100644 --- a/tests/old/testsuite/states/locked.c +++ b/tests/old/testsuite/states/locked.c @@ -17,50 +17,80 @@ * Boston, MA 02111-1307, USA. */ +#include "unistd.h" + #include +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)); diff --git a/testsuite/states/locked.c b/testsuite/states/locked.c index ecf969ec3e..e73413c273 100644 --- a/testsuite/states/locked.c +++ b/testsuite/states/locked.c @@ -17,50 +17,80 @@ * Boston, MA 02111-1307, USA. */ +#include "unistd.h" + #include +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)); diff --git a/tools/gst-inspect.c b/tools/gst-inspect.c index 266c9a5f94..fa985b45e7 100644 --- a/tools/gst-inspect.c +++ b/tools/gst-inspect.c @@ -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"); diff --git a/tools/gst-launch.c b/tools/gst-launch.c index dea2b871fd..4e21852563 100644 --- a/tools/gst-launch.c +++ b/tools/gst-launch.c @@ -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); } diff --git a/tools/gst-md5sum.c b/tools/gst-md5sum.c index 3101c85679..05465a07f2 100644 --- a/tools/gst-md5sum.c +++ b/tools/gst-md5sum.c @@ -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); diff --git a/tools/gst-typefind.c b/tools/gst-typefind.c index 7ea6034e93..b3fe66c6dc 100644 --- a/tools/gst-typefind.c +++ b/tools/gst-typefind.c @@ -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]); diff --git a/tools/gst-xmlinspect.c b/tools/gst-xmlinspect.c index 16aacafa00..1887edb829 100644 --- a/tools/gst-xmlinspect.c +++ b/tools/gst-xmlinspect.c @@ -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, "", - GST_DEBUG_FUNCPTR_NAME (element->loopfunc)); PUT_STRING (2, "", 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;