+2005-03-28 Wim Taymans <wim@fluendo.com>
+
+ * configure.ac:
+ * docs/design/part-gstelement.txt:
+ * docs/design/part-negotiation.txt:
+ * docs/design/part-preroll.txt:
+ * docs/design/part-scheduling.txt:
+ * docs/design/part-states.txt:
+ * gst/Makefile.am:
+ * gst/base/Makefile.am:
+ * gst/base/README:
+ * gst/base/gstbasesink.c: (gst_basesink_get_template),
+ (gst_basesink_base_init), (gst_basesink_class_init),
+ (gst_basesink_pad_getcaps), (gst_basesink_pad_setcaps),
+ (gst_basesink_pad_buffer_alloc), (gst_basesink_init),
+ (gst_basesink_set_pad_functions),
+ (gst_basesink_set_all_pad_functions), (gst_basesink_set_clock),
+ (gst_basesink_set_property), (gst_basesink_get_property),
+ (gst_base_sink_get_template), (gst_base_sink_get_caps),
+ (gst_base_sink_set_caps), (gst_base_sink_buffer_alloc),
+ (gst_basesink_preroll_queue_push),
+ (gst_basesink_preroll_queue_empty),
+ (gst_basesink_preroll_queue_flush), (gst_basesink_finish_preroll),
+ (gst_basesink_event), (gst_basesink_get_times),
+ (gst_basesink_do_sync), (gst_basesink_handle_buffer),
+ (gst_basesink_chain_unlocked), (gst_basesink_chain),
+ (gst_basesink_loop), (gst_basesink_activate),
+ (gst_basesink_change_state):
+ * gst/base/gstbasesink.h:
+ * gst/elements/Makefile.am:
+ * gst/elements/gstfakesink.c: (gst_fakesink_base_init),
+ (gst_fakesink_class_init), (gst_fakesink_init),
+ (gst_fakesink_set_property), (gst_fakesink_get_property),
+ (gst_fakesink_get_times), (gst_fakesink_event),
+ (gst_fakesink_preroll), (gst_fakesink_render),
+ (gst_fakesink_change_state):
+ * gst/elements/gstfakesink.h:
+ * gst/gstbin.c: (gst_bin_class_init), (gst_bin_set_manager),
+ (gst_bin_get_state), (gst_bin_change_state), (gst_bin_send_event):
+ * gst/gstelement.c: (gst_element_add_pad),
+ (gst_element_get_state_func), (gst_element_abort_state),
+ (gst_element_commit_state), (gst_element_lost_state),
+ (gst_element_set_state), (gst_element_pads_activate):
+ * gst/gstpad.c: (gst_pad_set_active), (gst_pad_event_default):
+ * gst/gstpipeline.c: (gst_pipeline_send_event),
+ (gst_pipeline_change_state):
+ Added state change code.
+ Added/updated docs.
+ Added sink base class, make fakesink extend the base class.
+ Small cleanups in GstPipeline.
+
2005-03-26 David Schleef <ds@schleef.org>
* gst/Makefile.am: remove gstcpu.[ch]. The gst_cpu functionality
gst/Makefile
gst/gstconfig.h
gst/gstversion.h
-gst/autoplug/Makefile
+gst/base/Makefile
gst/indexers/Makefile
gst/elements/Makefile
gst/parse/Makefile
gst/registries/Makefile
libs/Makefile
libs/gst/Makefile
-libs/gst/bytestream/Makefile
libs/gst/control/Makefile
libs/gst/dataprotocol/Makefile
libs/gst/getbits/Makefile
tests/threadstate/Makefile
testsuite/Makefile
testsuite/bins/Makefile
-testsuite/bytestream/Makefile
testsuite/caps/Makefile
testsuite/cleanup/Makefile
testsuite/clock/Makefile
testsuite/ghostpads/Makefile
testsuite/indexers/Makefile
testsuite/negotiation/Makefile
-testsuite/pad/Makefile
testsuite/parse/Makefile
testsuite/plugin/Makefile
testsuite/refcounting/Makefile
Name
----
-All elements are named, and while they should ideally be unique in any given pipeline, the do not have to be. The only
-guaranteed unique name for an element is its complete path in the object hierarchy. Functions are provided to set and
-get the name of the element. _set_name() creates a local copy of the string passed. _get_name() returns the actual
-element's pointer. Therefore, the argument to _set_name() is the responsibility of the caller to free if necessary,
-but the return from _get_name() is definitely not to be messed with by the caller. Accordingly, _get_name() returns an
-const gchar *.
-
-Providing a new name to an element causes it to free its internal copy of the name and make a copy of the new name.
-This means that you must consider the pointer returned by _get_name() to be short-lived. If you must make use of the
-name beyond the immediate scope, it is suggested that you make yourself a copy of it. If you know for a fact neither
-the pointer nor its contents will change, you may retain the original pointer. If you get odd results when using the
-returned string, that's the first thing to check.
+All elements are named, and while they should ideally be unique in any given
+pipeline, they do not have to be. The only guaranteed unique name for an
+element is its complete path in the object hierarchy. In other words, an
+element's name is unique inside its parent. (This follows from GstObject's
+name explanation)
+
+This uniqueness is guaranteed through all functions where either parentage
+or name of an element is changed.
Pads
--- /dev/null
+Negotiation
+-----------
+
+Negotiation happens when elements want to push buffers and need to decide
+on the format. This is called downstream negotiation because the upstream
+element decides the format for the downstream element. This is the most
+common case.
+
+Negotiation can also happen when a downstream element wants to receive
+another data format from an upstream element. This is called upstream
+negotiation.
+
+The basics of negotiation are as follows:
+
+ - GstCaps (see part-caps.txt) are attached and refcounted before they
+ are attached to a buffer to describe the contents of the buffer.
+ It is possible to add a NULL caps to a buffer, this means that the
+ buffer type did not change relative to the previous buffer. If no
+ previous buffer was received by a downstream element, it is free to
+ discard the buffer.
+
+ - Before receiving a buffer, an element must check if the datatype of
+ the buffer has changed. The element should reconfigure itself to the
+ new format before processing the buffer data. If the data type on
+ the buffer is not acceptable, the element should refuse the buffer.
+
+ - When requesting a buffer from a bufferpool, the prefered type should
+ be passed to the buffer allocation function. After receiving a buffer
+ from a bufferpool, the datatype should be checked again.
+
+ - A bufferpool allocation function should try to allocate a buffer of the
+ prefered type. If there is a good reason to choose another type, the
+ alloc function should see if that other type is accepted by the other
+ element, then allocate a buffer of that type and attach the type to the
+ buffer before returning it.
+
+
+The general flow for a source pad starting the negotiation.
+
+ src sink
+ | |
+ | accepts? |
+ type A |---------------->|
+ | yes |
+ |<----------------|
+ | |
+ get buffer | alloc_buf |
+ from pool |---------------->|
+ with type A | | Create buffer of type A.
+ | |
+ check type |<----------------|
+ and use A | |
+ | push |
+ push buffer |---------------->| Receive type A, reconfigure to
+ with new type| | process type A.
+ | |
+
+ One possible implementation in pseudo code:
+
+ [element wants to create a buffer]
+ if not format
+ # see what the peer can do
+ peercaps = gst_pad_peer_get_caps (srcpad)
+ # see what we can do
+ ourcaps = gst_pad_get_caps (srcpad)
+
+ # get common formats
+ candidates = gst_caps_intersect (peercaps, ourcaps)
+
+ foreach candidate in candidates
+ # make sure the caps is fixed
+ fixedcaps = gst_pad_fixate_caps (srcpad, candidate)
+
+ # see if the peer accepts it
+ if gst_pad_peer_accept_caps (srcpad, fixedcaps)
+ # store the caps as the negotiated caps, this will
+ # call the setcaps function on the pad
+ gst_pad_set_caps (srcpad, fixedcaps)
+ break
+ endif
+ done
+ endif
+
+ # if the type is different, this will call the setcaps function of
+ # the pad.
+ buffer = gst_pad_alloc_buffer (srcpad, 0, size, GST_PAD_CAPS (fixedcaps));
+ if buffer
+ [fill buffer and push]
+ elseif
+ [no buffer, either no peer or no acceptable format found]
+ endif
+
+
+The general flow for a sink pad starting a renegotiation.
+
+ src sink
+ | |
+ | accepts? |
+ |<----------------| type B
+ | yes |
+ |---------------->|
+ | |
+ get buffer | alloc_buf |
+ from pool |---------------->|
+ with type | | Create buffer of new type B.
+ | |
+ check type |<----------------|
+ and | |
+ reconfigure | |
+ | push |
+ push buffer |---------------->| Receive type B, reconfigure to
+ with new type| | process type B.
+ | |
+
+
+
+Use case:
+
+
+videotestsrc ! xvimagesink
+
+ 1) Who decides what format to use?
+ - src pad always decides, by convention. sinkpad can suggest a format
+ by putting it high in the getcaps function GstCaps.
+ - since the src decides, it can always choose something that it can do,
+ so this step can only fail if the sinkpad stated it could accept
+ something while later on it couldn't.
+
+ 2) When does negotiation happen?
+ - before srcpad does a push, it figures out a type as stated in 1), then
+ it calls the pad alloc function with the type. The sinkpad has to
+ create a buffer of that type, src fills the buffer and sends it to sink.
+ - since the sink stated in 1) it could accept the type, it will be able to
+ create a buffer of the type and handle it.
+ - sink checks media type of buffer and configures itself for this type.
+
+ 3) How can sink request another format?
+ - sink asks if new format is possible for the source.
+ - sink returns buffer with new type in allocfunction.
+ - src receives buffer with new type, reconfigures and pushes.
+ - sink can always select something it can create and handle since it takes
+ the initiative. src should be able to handle the new type since it said
+ it could accept it.
+
+videotestsrc ! queue ! xvimagesink
+
+ - queue implements an allocfunction, proxying all calls to its srcpad peer.
+ - queue proxies all accept and getcaps to the other peer pad.
+ - queue contains buffers with different types.
+
+
+
--- /dev/null
+Preroll
+-------
+
+A sink element can only complete the state change to PAUSED after a buffer
+has been queued on the input pad or pads. This process is called prerolling
+and is needed to fill the pipeline with buffers so that the transition to
+PLAYING goes as fast as possible with no visual delay for the user.
+
+After receiving a buffer (or EOS) on a pad the chain/event function should
+wait to render the buffers or in the EOS case, wait to post the EOS
+message.
+
+Several things can happen that require the preroll lock to be unlocked. This
+include state changes or flush events.
+
+
+Committing the state
+--------------------
+
+When going to PAUSED and PLAYING a buffer should be queued in the pad. We also
+make this requirement for going to PLAYING since a flush event in the PAUSED
+state could unqueue the buffer again.
+
+The state is commited in the following conditions:
+
+ - a buffer is received on a sinkpad
+ - an EOS is received on a sinkpad.
+
+We require the state change to be commited in EOS as well since an EOS means
+by definition that no buffer is going to arrive anymore.
+
+
+Unlocking the preroll
+---------------------
+
+The following conditions unlock the preroll:
+
+ - a state change
+ - a flush event
+
+
+Result of the preroll
+---------------------
+
+After the preroll is unlocked, the element can be in the following states:
+
+ - in a state that disallows it to process the data (flushing, pad inactive)
+ - in a state that allows it to process the data.
+
--- /dev/null
+Scheduling
+----------
+
+The scheduling in GStreamer is based on pads actively pushing (producing) data or
+pad pulling in data (consuming) from other pads.
+
+Pushing
+-------
+
+A pad can produce data and push it to the next pad. A pad that behaves this way
+exposes a loop function that will be called repeadedly until it returns false.
+The loop function is allowed to block whenever it wants. When the pad is deactivated
+the loop function should unblock though.
+
+A pad operating in the push mode can only produce data to a pad that exposes a
+chain function. This chain function will be called with the buffer produced by
+the pushing pad.
+
+This method of producing data is called the streaming mode since the producer
+produces a constant stream of data.
+
+Pulling
+-------
+
+Pads that operate in pulling mode can only pull data from a pad that exposes the
+pullregion function. In this case, the sink pad exposes a loop function that will be
+called repeadedly until the task is stopped.
+
+After pulling data from the peer pad, the loop function will typically call the
+push function to push the result to the peer sinkpad.
+
+
+Deciding the scheduling mode
+----------------------------
+
+When the core performs the pad activate function, it will select a scheduling mode
+for the pads. Sinkpads that expose a loop function are prefered over source pads
+with a loop function so that the pull mode is selected when possible. Selecting the
+pull mode is more efficient because it allows for arbitrary seeking and random access
+to the data.
+
+The chain function
+------------------
+
+The chain function will be called when a upstream element perform a _push() on the pad.
+The upstream element can be another chain based element or a pushing source.
+
+The getrange function
+---------------------
+
+The getrange function is called when a peer pad perform a _pullregion() on the pad. This
+downstream pad can be a pulling element or another pullregion() based element.
+
+Plug-in techniques
+------------------
+
+Multi-sink elements
+-------------------
+
+ Elements with multiple sinks can either expose a loop function on each of the pads to
+ actively pullregion data or they can expose a chain function on each pad.
+
+ Implementing a chain function is usually easy and allows for all possible scheduling
+ methods.
+
+ Pad select
+ ----------
+
+ If the chain based sink wants to wait for one of the pads to receive a buffer, just
+ implement the action to perform in the chain function. Be aware that the action could
+ be performed in different threads and possibly simultaneously so grab the STREAM_LOCK.
+
+ Collect pads
+ ------------
+
+ If the chain based sink pads all require one buffer before the element can operate on
+ the data, collect all the buffers in the chain function and perform the action when
+ all chainpads received the buffer.
+
+ In this case you probably also don't want to accept more data on a pad that has a buffer
+ queued. This can easily be done with the following code snippet:
+
+ static GstFlowReturn _chain (GstPad *pad, GstBuffer *buffer)
+ {
+ LOCK (mylock);
+ while (pad->store != NULL) {
+ WAIT (mycond, mylock);
+ }
+ pad->store = buffer;
+ SIGNAL (mycond);
+ UNLOCK (mylock);
+
+ return GST_FLOW_OK;
+ }
+
+ static void _pull (GstPad *pad, GstBuffer **buffer)
+ {
+ LOCK (mylock);
+ while (pad->store == NULL) {
+ WAIT (mycond, mylock);
+ }
+ **buffer = pad->store;
+ pad->store = NULL;
+ SIGNAL (mycond);
+ UNLOCK (mylock);
+ }
+
+
+Cases
+-----
+ Inside the braces below the pads is stated what function the
+ pad support:
+
+ l: exposes a loop function, so it can act as a pushing source.
+ g: exposes a getrange function
+ c: exposes a chain function
+
+ following scheduling decisions are made based on the scheduling
+ methods exposed by the pads:
+
+ (g) - (l): sinkpad will pull data from src
+ (l) - (c): srcpad actively pushes data to sinkpad
+ () - (c): srcpad will push data to sinkpad.
+
+ () - () : not schedulable.
+ () - (l): not schedulable.
+ (g) - () : not schedulable.
+ (g) - (c): not schedulable.
+ (l) - () : not schedulable.
+ (l) - (l): not schedulable
+
+ () - (g): impossible
+ (g) - (g): impossible.
+ (l) - (g): impossible
+ (c) - () : impossible
+ (c) - (g): impossible
+ (c) - (l): impossible
+ (c) - (c): impossible
+
+ +---------+ +------------+ +-----------+
+ | filesrc | | mp3decoder | | audiosink |
+ | src--sink src--sink |
+ +---------+ +------------+ +-----------+
+ (l-g) (c) () (c)
+
+ When activating the pads:
+
+ * audiosink has a chain function and the peer pad has no
+ loop function, no scheduling is done.
+ * mp3decoder and filesrc expose an (l) - (c) connection,
+ a thread is created to call the srcpad loop function.
+
+ +---------+ +------------+ +----------+
+ | filesrc | | avidemuxer | | fakesink |
+ | src--sink src--sink |
+ +---------+ +------------+ +----------+
+ (l-g) (l) () (c)
+
+ * fakesink has a chain function and the peer pad has no
+ loop function, no scheduling is done.
+ * avidemuxer and filesrc expose an (g) - (l) connection,
+ a thread is created to call the sinkpad loop function.
+
+ +---------+ +----------+ +------------+ +----------+
+ | filesrc | | identity | | avidemuxer | | fakesink |
+ | src--sink src--sink src--sink |
+ +---------+ +----------+ +------------+ +----------+
+ (l-g) (c) () (l) () (c)
+
+ * fakesink has a chain function and the peer pad has no
+ loop function, no scheduling is done.
+ * avidemuxer and identity expose no schedulable connection so
+ this pipeline is not schedulable.
+
+ +---------+ +----------+ +------------+ +----------+
+ | filesrc | | identity | | avidemuxer | | fakesink |
+ | src--sink src--sink src--sink |
+ +---------+ +----------+ +------------+ +----------+
+ (l-g) (c-l) (g) (l) () (c)
+
+ * fakesink has a chain function and the peer pad has no
+ loop function, no scheduling is done.
+ * avidemuxer and identity expose an (g) - (l) connection,
+ a thread is created to call the sinkpad loop function.
+ * identity knows the srcpad is getrange based and uses the
+ thread from avidemux to getrange data from filesrc.
+
+ +---------+ +----------+ +------------+ +----------+
+ | filesrc | | identity | | oggdemuxer | | fakesink |
+ | src--sink src--sink src--sink |
+ +---------+ +----------+ +------------+ +----------+
+ (l-g) (c) () (l-c) () (c)
+
+ * fakesink has a chain function and the peer pad has no
+ loop function, no scheduling is done.
+ * oggdemuxer and identity expose an () - (l-c) connection,
+ oggdemux has to operate in chain mode.
+ * identity chan only work chain based and so filesrc creates
+ a thread to push data to identity.
+
+
--- /dev/null
+States
+======
+
+Both elements and pads can be in different states. The states of the pads are
+linked to the state of the element so the design of the states is mainly
+focused around the element states.
+
+An element can be in 4 states. NULL, READY, PAUSED and PLAYING. When an element
+is initially instantiated, it is in the NULL state.
+
+
+State definitions
+-----------------
+
+ - NULL: This is the initial state of an element.
+ - READY: The element should be prepared to go to PAUSED.
+ - PAUSED: The element should be ready to accept and process data. Sink
+ elements however only accept one buffer and then block.
+ - PLAYING: The same as PAUSED except for sinks, who are now accepting
+ and rendering data.
+
+We call the sequence NULL->PLAYING an upwards state change and PLAYING->NULL
+a downwards state change.
+
+
+State variables
+---------------
+
+An element has a special lock to manage the state changes. This lock is called
+the STATE_LOCK.
+
+The STATE_LOCK protects 3 element variables:
+
+ - STATE
+ - PENDING_STATE
+ - STATE_ERROR flag
+
+The STATE always reflects the current state of the element. The PENDING_STATE
+always reflects the required state of the element. The PENDING_STATE can be
+VOID_PENDING if the element is in the right state. The STATE_ERROR flag
+indicates that an error occured while doing the last state change.
+
+
+Setting state on elements
+-------------------------
+
+The state of an element can be changed with _element_set_state(). When chaning
+the state of an element all intermediate states will also be set on the element
+until the final desired state is set.
+
+The _set_state() function can return 3 possible values:
+
+ GST_STATE_FAILURE: The state change failed for some reason. The plugin should
+ have posted an error message on the bus with information.
+
+ GST_STATE_SUCCESS: The state change is completed successfully.
+
+ GST_STATE_ASYNC: The state change will complete later on. This can happen
+ When the element needs a long time to perform the state
+ change or for sinks that need to receive the first buffer
+ before they can complete the state change (preroll).
+
+In the case of an async state change, it is not possible to proceed to the next
+state until the current state change completed. After receiving an ASYNC return
+value, you can use _element_get_state() to poll the status of the element.
+
+When setting the state of an element, the PENDING_STATE is set to the required
+state and the STATE_ERROR flag is cleared. Then the state change function of the
+element is called and the result of that function is used to update the STATE,
+PENDING_STATE and STATE_ERROR flags. If the function returned ASYNC, this result
+is immediatly returned to the caller.
+
+
+Getting state of elements
+-------------------------
+
+The _get_state() function takes 3 arguments, two pointers that will hold the
+current and pending state and one GTimeVal that holds a timeout value. The
+function returns a GstElementStateReturn.
+
+ - If the element returned SUCCESS to the previous _set_state() function, this
+ function will return the last state set on the element and VOID_PENDING in
+ the pending state value.
+
+ - If the element returned FAILURE to the previous _set_state() call, this
+ funciton will return FAILURE with the state set to the current state of
+ the element and the pending state set to the value used in the last call
+ of _set_state().
+
+ - If the element returned ASYNC to the previous _set_state() call, this function
+ will wait for the element to complete its state change up to the amount of time
+ specified in the GTimeVal.
+
+ * If the element does not complete the state change in the specified amount of
+ time, this function will return ASYNC with the state set to the current state
+ and the pending state set to the pending state.
+
+ * If the element completes the state change within the specified timeout, this
+ function returns the updated state and VOID_PENDING as the pending state.
+
+ * If the element aborts the ASYNC state change due to an error within the
+ specified timeout, this function returns FAILURE with the state set to last
+ successfull state and pending set to the last attempt. The element should
+ also post an error message on the bus with more information about the problem.
+
+
+States in GstBin
+----------------
+
+A GstBin manages the state of its children. It does this by propagating the state
+changes performed on it to all of its children. The _set_state() function on a
+bin will call the _set_state() function on all of its children.
+
+The children are iterated from the sink elements to the source elements. This makes
+sure that when changing the state of an element, the downstream elements are in
+the correct state to process the eventual buffers. In the case of a downwards
+state change, the sink elements will shut down first which makes the upstream
+elements shut down as well since the _push() function returns a GST_FLOW_WRONG_STATE
+error.
+
+If all the children return SUCCESS, the function returns SUCCESS as well.
+
+If one of the children returns FAILURE, the function returns FAILURE as well. In
+this state it is possible that some elements successfuly changed state. The
+application can check which elements have a changed state, which were in error
+and which were not affected by iterating the elements and calling _get_state()
+on the elements.
+
+If after calling the state function on all children, one of the children returned
+ASYNC, the function returns ASYNC as well.
+
+The current state of the bin can be retrieved with _get_state(). This function will
+call the _get_state() function on all the elements. If one of the children returns
+FAILURE or ASYNC, the bin reports FAILURE or ASYNC respectively. The bin also
+updates its state variables after polling its children, this means that the state
+variables of the bin are only updated after calling _get_state() on the bin.
+
+The _get_state() function will be called on the children with the same timout value
+so the function can potentially block timeout*num_children.
+
+
+Implementing states in elements
+-------------------------------
+
+READY
+-----
+
+
+
+
+
GST_URI_SRC = gsturi.c
endif
-SUBDIRS = $(GST_PARSE_DIRS) $(GST_REGISTRY_DIRS) . elements schedulers $(GST_INDEX_DIRS)
-DIST_SUBDIRS = elements parse registries schedulers indexers
+SUBDIRS = $(GST_PARSE_DIRS) $(GST_REGISTRY_DIRS) . base elements schedulers $(GST_INDEX_DIRS)
+DIST_SUBDIRS = base elements parse registries schedulers indexers
# make variables for all generated source and header files to make the
# distinction clear
--- /dev/null
+lib_LTLIBRARIES = libgstbase.la
+AS_LIBTOOL_LIB = libgstbase
+
+EXTRA_DIST = $(as_libtool_EXTRA_DIST)
+noinst_DATA = $(as_libtool_noinst_DATA_files)
+
+libgstbase_la_DEPENDENCIES = ../libgstreamer-@GST_MAJORMINOR@.la
+libgstbase_la_SOURCES = \
+ gstbasesink.c
+
+libgstbase_la_CFLAGS = $(GST_OBJ_CFLAGS)
+libgstbase_la_LIBADD = $(GST_OBJ_LIBS)
+libgstbase_la_LDFLAGS = $(as_libtool_LDFLAGS)
+
+noinst_HEADERS =
+ gstbasesink.h
+
+install-data-local: as-libtool-install-data-local
+
+uninstall-local: as-libtool-uninstall-local
+
+include $(top_srcdir)/common/as-libtool.mak
+
--- /dev/null
+Base classes
+------------
+
+GstBaseSink
+
+ Base class for sink elements.
+
+ - one sinkpad
+ - handles state changes
+ - does flushing
+ - preroll with optional preview
+ - pull/push mode
+ - EOS handling
+
+ FIXME: not much point making it operate in pull mode as a generic
+ base class I guess...
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstbasesink.c:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "gstbasesink.h"
+#include <gst/gstmarshal.h>
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+GST_DEBUG_CATEGORY_STATIC (gst_basesink_debug);
+#define GST_CAT_DEFAULT gst_basesink_debug
+
+/* #define DEBUGGING */
+#ifdef DEBUGGING
+#define DEBUG(str,args...) g_print (str,##args)
+#else
+#define DEBUG(str,args...)
+#endif
+
+/* BaseSink signals and properties */
+enum
+{
+ /* FILL ME */
+ SIGNAL_HANDOFF,
+ LAST_SIGNAL
+};
+
+#define DEFAULT_SIZE 1024
+#define DEFAULT_HAS_LOOP FALSE
+#define DEFAULT_HAS_CHAIN TRUE
+
+enum
+{
+ PROP_0,
+ PROP_HAS_LOOP,
+ PROP_HAS_CHAIN,
+ PROP_PREROLL_QUEUE_LEN
+};
+
+#define _do_init(bla) \
+ GST_DEBUG_CATEGORY_INIT (gst_basesink_debug, "basesink", 0, "basesink element");
+
+GST_BOILERPLATE_FULL (GstBaseSink, gst_basesink, GstElement, GST_TYPE_ELEMENT,
+ _do_init);
+
+static void gst_basesink_set_clock (GstElement * element, GstClock * clock);
+
+static void gst_basesink_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_basesink_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
+static GstStaticPadTemplate *gst_base_sink_get_template (GstBaseSink * sink);
+static GstCaps *gst_base_sink_get_caps (GstBaseSink * sink);
+static gboolean gst_base_sink_set_caps (GstBaseSink * sink, GstCaps * caps);
+static GstBuffer *gst_base_sink_buffer_alloc (GstBaseSink * sink,
+ guint64 offset, guint size, GstCaps * caps);
+static void gst_basesink_get_times (GstBaseSink * basesink, GstBuffer * buffer,
+ GstClockTime * start, GstClockTime * end);
+
+static GstElementStateReturn gst_basesink_change_state (GstElement * element);
+
+static GstFlowReturn gst_basesink_chain_unlocked (GstPad * pad,
+ GstBuffer * buffer);
+static void gst_basesink_loop (GstPad * pad);
+static GstFlowReturn gst_basesink_chain (GstPad * pad, GstBuffer * buffer);
+static gboolean gst_basesink_activate (GstPad * pad, GstActivateMode mode);
+static gboolean gst_basesink_event (GstPad * pad, GstEvent * event);
+static inline void gst_basesink_handle_buffer (GstBaseSink * basesink,
+ GstBuffer * buf);
+
+static GstStaticPadTemplate *
+gst_basesink_get_template (GstBaseSink * bsink)
+{
+ GstStaticPadTemplate *template = NULL;
+ GstBaseSinkClass *bclass;
+
+ bclass = GST_BASESINK_GET_CLASS (bsink);
+
+ if (bclass->get_template)
+ template = bclass->get_template (bsink);
+
+ if (template == NULL) {
+ template = &sinktemplate;
+ }
+ return template;
+}
+
+static void
+gst_basesink_base_init (gpointer g_class)
+{
+ //GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
+
+ /*
+ gst_element_class_add_pad_template (gstelement_class,
+ gst_static_pad_template_get (&sinktemplate));
+ */
+}
+
+static void
+gst_basesink_class_init (GstBaseSinkClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+
+ gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_basesink_set_property);
+ gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_basesink_get_property);
+
+ g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HAS_LOOP,
+ g_param_spec_boolean ("has-loop", "has-loop",
+ "Enable loop-based operation", DEFAULT_HAS_LOOP,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HAS_CHAIN,
+ g_param_spec_boolean ("has-chain", "has-chain",
+ "Enable chain-based operation", DEFAULT_HAS_CHAIN,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (G_OBJECT_CLASS (klass),
+ PROP_PREROLL_QUEUE_LEN,
+ g_param_spec_uint ("preroll-queue-len", "preroll-queue-len",
+ "Number of buffers to queue during preroll", 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ gstelement_class->set_clock = GST_DEBUG_FUNCPTR (gst_basesink_set_clock);
+ gstelement_class->change_state =
+ GST_DEBUG_FUNCPTR (gst_basesink_change_state);
+
+ klass->get_caps = GST_DEBUG_FUNCPTR (gst_base_sink_get_caps);
+ klass->set_caps = GST_DEBUG_FUNCPTR (gst_base_sink_set_caps);
+ klass->get_template = GST_DEBUG_FUNCPTR (gst_base_sink_get_template);
+ klass->buffer_alloc = GST_DEBUG_FUNCPTR (gst_base_sink_buffer_alloc);
+ klass->get_times = GST_DEBUG_FUNCPTR (gst_basesink_get_times);
+}
+
+static GstCaps *
+gst_basesink_pad_getcaps (GstPad * pad)
+{
+ GstBaseSinkClass *bclass;
+ GstBaseSink *bsink;
+ GstCaps *caps = NULL;
+
+ bsink = GST_BASESINK (GST_PAD_PARENT (pad));
+ bclass = GST_BASESINK_GET_CLASS (bsink);
+ if (bclass->get_caps)
+ caps = bclass->get_caps (bsink);
+
+ if (caps == NULL) {
+ GstStaticPadTemplate *stemplate;
+ GstPadTemplate *template;
+
+ stemplate = gst_basesink_get_template (bsink);
+ template = gst_static_pad_template_get (stemplate);
+ caps = gst_caps_copy (gst_pad_template_get_caps (template));
+ }
+ return caps;
+}
+
+static gboolean
+gst_basesink_pad_setcaps (GstPad * pad, GstCaps * caps)
+{
+ GstBaseSinkClass *bclass;
+ GstBaseSink *bsink;
+ gboolean res = FALSE;
+
+ bsink = GST_BASESINK (GST_PAD_PARENT (pad));
+ bclass = GST_BASESINK_GET_CLASS (bsink);
+ if (bclass->set_caps)
+ res = bclass->set_caps (bsink, caps);
+
+ return res;
+}
+
+static GstBuffer *
+gst_basesink_pad_buffer_alloc (GstPad * pad, guint64 offset, guint size,
+ GstCaps * caps)
+{
+ GstBaseSinkClass *bclass;
+ GstBaseSink *bsink;
+ GstBuffer *buffer = NULL;
+
+ bsink = GST_BASESINK (GST_PAD_PARENT (pad));
+ bclass = GST_BASESINK_GET_CLASS (bsink);
+ if (bclass->buffer_alloc)
+ buffer = bclass->buffer_alloc (bsink, offset, size, caps);
+
+ return buffer;
+}
+
+static void
+gst_basesink_init (GstBaseSink * basesink)
+{
+ GstStaticPadTemplate *template;
+
+ template = gst_basesink_get_template (basesink);
+
+ basesink->sinkpad =
+ gst_pad_new_from_template (gst_static_pad_template_get (template),
+ "sink");
+ gst_pad_set_getcaps_function (basesink->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_basesink_pad_getcaps));
+ gst_pad_set_setcaps_function (basesink->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_basesink_pad_setcaps));
+ gst_pad_set_bufferalloc_function (basesink->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_basesink_pad_buffer_alloc));
+ gst_element_add_pad (GST_ELEMENT (basesink), basesink->sinkpad);
+
+ basesink->pad_mode = GST_ACTIVATE_NONE;
+ GST_RPAD_TASK (basesink->sinkpad) = NULL;
+}
+
+static void
+gst_basesink_set_pad_functions (GstBaseSink * this, GstPad * pad)
+{
+ gst_pad_set_activate_function (pad,
+ GST_DEBUG_FUNCPTR (gst_basesink_activate));
+ gst_pad_set_event_function (pad, GST_DEBUG_FUNCPTR (gst_basesink_event));
+
+ if (this->has_chain)
+ gst_pad_set_chain_function (pad, GST_DEBUG_FUNCPTR (gst_basesink_chain));
+ else
+ gst_pad_set_chain_function (pad, NULL);
+
+ if (this->has_loop)
+ gst_pad_set_loop_function (pad, GST_DEBUG_FUNCPTR (gst_basesink_loop));
+ else
+ gst_pad_set_loop_function (pad, NULL);
+}
+
+static void
+gst_basesink_set_all_pad_functions (GstBaseSink * this)
+{
+ GList *l;
+
+ for (l = GST_ELEMENT_PADS (this); l; l = l->next)
+ gst_basesink_set_pad_functions (this, (GstPad *) l->data);
+}
+
+static void
+gst_basesink_set_clock (GstElement * element, GstClock * clock)
+{
+ GstBaseSink *sink;
+
+ sink = GST_BASESINK (element);
+
+ sink->clock = clock;
+}
+
+static void
+gst_basesink_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstBaseSink *sink;
+
+ sink = GST_BASESINK (object);
+
+ GST_LOCK (sink);
+ switch (prop_id) {
+ case PROP_HAS_LOOP:
+ sink->has_loop = g_value_get_boolean (value);
+ gst_basesink_set_all_pad_functions (sink);
+ break;
+ case PROP_HAS_CHAIN:
+ sink->has_chain = g_value_get_boolean (value);
+ gst_basesink_set_all_pad_functions (sink);
+ break;
+ case PROP_PREROLL_QUEUE_LEN:
+ /* preroll lock necessary to serialize with finish_preroll */
+ GST_PREROLL_LOCK (sink->sinkpad);
+ sink->preroll_queue_max_len = g_value_get_uint (value);
+ GST_PREROLL_UNLOCK (sink->sinkpad);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+ GST_UNLOCK (sink);
+}
+
+static void
+gst_basesink_get_property (GObject * object, guint prop_id, GValue * value,
+ GParamSpec * pspec)
+{
+ GstBaseSink *sink;
+
+ sink = GST_BASESINK (object);
+
+ GST_LOCK (sink);
+ switch (prop_id) {
+ case PROP_HAS_LOOP:
+ g_value_set_boolean (value, sink->has_loop);
+ break;
+ case PROP_HAS_CHAIN:
+ g_value_set_boolean (value, sink->has_chain);
+ break;
+ case PROP_PREROLL_QUEUE_LEN:
+ g_value_set_uint (value, sink->preroll_queue_max_len);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+ GST_UNLOCK (sink);
+}
+
+static GstStaticPadTemplate *
+gst_base_sink_get_template (GstBaseSink * sink)
+{
+ return &sinktemplate;
+}
+
+static GstCaps *
+gst_base_sink_get_caps (GstBaseSink * sink)
+{
+ return NULL;
+}
+
+static gboolean
+gst_base_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
+{
+ return TRUE;
+}
+
+static GstBuffer *
+gst_base_sink_buffer_alloc (GstBaseSink * sink, guint64 offset, guint size,
+ GstCaps * caps)
+{
+ return NULL;
+}
+
+/* with PREROLL_LOCK */
+static void
+gst_basesink_preroll_queue_push (GstBaseSink * basesink, GstPad * pad,
+ GstBuffer * buffer)
+{
+ if (basesink->preroll_queue->length == 0) {
+ GstBaseSinkClass *bclass = GST_BASESINK_GET_CLASS (basesink);
+
+ if (bclass->preroll)
+ bclass->preroll (basesink, buffer);
+ }
+
+ if (basesink->preroll_queue->length < basesink->preroll_queue_max_len) {
+ DEBUG ("push %p %p\n", basesink, buffer);
+ g_queue_push_tail (basesink->preroll_queue, buffer);
+ } else {
+ /* block until the state changes, or we get a flush, or something */
+ DEBUG ("block %p %p\n", basesink, buffer);
+ GST_DEBUG ("element %s waiting to finish preroll",
+ GST_ELEMENT_NAME (basesink));
+ basesink->need_preroll = FALSE;
+ basesink->have_preroll = TRUE;
+ GST_PREROLL_WAIT (pad);
+ GST_DEBUG ("done preroll");
+ basesink->have_preroll = FALSE;
+ }
+}
+
+/* with PREROLL_LOCK */
+static void
+gst_basesink_preroll_queue_empty (GstBaseSink * basesink, GstPad * pad)
+{
+ GstBuffer *buf;
+ GQueue *q = basesink->preroll_queue;
+
+ if (q) {
+ DEBUG ("empty queue\n");
+ while ((buf = g_queue_pop_head (q))) {
+ DEBUG ("pop %p\n", buf);
+ gst_basesink_handle_buffer (basesink, buf);
+ }
+ DEBUG ("queue len %p %d\n", basesink, q->length);
+ }
+}
+
+/* with PREROLL_LOCK */
+static void
+gst_basesink_preroll_queue_flush (GstBaseSink * basesink)
+{
+ GstBuffer *buf;
+ GQueue *q = basesink->preroll_queue;
+
+ DEBUG ("flush %p\n", basesink);
+ if (q) {
+ while ((buf = g_queue_pop_head (q))) {
+ DEBUG ("pop %p\n", buf);
+ gst_buffer_unref (buf);
+ }
+ }
+}
+
+typedef enum
+{
+ PREROLL_QUEUEING,
+ PREROLL_PLAYING,
+ PREROLL_FLUSHING,
+ PREROLL_ERROR
+} PrerollReturn;
+
+/* with STREAM_LOCK */
+PrerollReturn
+gst_basesink_finish_preroll (GstBaseSink * basesink, GstPad * pad,
+ GstBuffer * buffer)
+{
+ gboolean usable;
+
+ DEBUG ("finish preroll %p <\n", basesink);
+ /* lock order is important */
+ GST_STATE_LOCK (basesink);
+ GST_PREROLL_LOCK (pad);
+ DEBUG ("finish preroll %p >\n", basesink);
+ if (!basesink->need_preroll)
+ goto no_preroll;
+
+ gst_element_commit_state (GST_ELEMENT (basesink));
+ GST_STATE_UNLOCK (basesink);
+
+ gst_basesink_preroll_queue_push (basesink, pad, buffer);
+
+ GST_LOCK (pad);
+ usable = !GST_RPAD_IS_FLUSHING (pad) && GST_RPAD_IS_ACTIVE (pad);
+ GST_UNLOCK (pad);
+ if (!usable)
+ goto unusable;
+
+ if (basesink->need_preroll)
+ goto still_queueing;
+
+ GST_DEBUG ("done preroll");
+
+ gst_basesink_preroll_queue_empty (basesink, pad);
+
+ GST_PREROLL_UNLOCK (pad);
+
+ return PREROLL_PLAYING;
+
+no_preroll:
+ {
+ /* maybe it was another sink that blocked in preroll, need to check for
+ buffers to drain */
+ if (basesink->preroll_queue->length)
+ gst_basesink_preroll_queue_empty (basesink, pad);
+ GST_PREROLL_UNLOCK (pad);
+ GST_STATE_UNLOCK (basesink);
+ return PREROLL_PLAYING;
+ }
+unusable:
+ {
+ GST_DEBUG ("pad is flushing");
+ GST_PREROLL_UNLOCK (pad);
+ return PREROLL_FLUSHING;
+ }
+still_queueing:
+ {
+ GST_PREROLL_UNLOCK (pad);
+ return PREROLL_QUEUEING;
+ }
+}
+
+static gboolean
+gst_basesink_event (GstPad * pad, GstEvent * event)
+{
+ GstBaseSink *basesink;
+ gboolean result = TRUE;
+ GstBaseSinkClass *bclass;
+
+ basesink = GST_BASESINK (GST_OBJECT_PARENT (pad));
+
+ bclass = GST_BASESINK_GET_CLASS (basesink);
+
+ DEBUG ("event %p\n", basesink);
+
+ if (bclass->event)
+ bclass->event (basesink, event);
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_EOS:
+ {
+ gboolean need_eos;
+
+ GST_STREAM_LOCK (pad);
+
+ GST_PREROLL_LOCK (pad);
+ gst_basesink_preroll_queue_empty (basesink, pad);
+ GST_PREROLL_UNLOCK (pad);
+
+ GST_LOCK (basesink);
+ need_eos = basesink->eos = TRUE;
+ if (basesink->clock) {
+ /* wait for last buffer to finish if we have a valid end time */
+ if (GST_CLOCK_TIME_IS_VALID (basesink->end_time)) {
+ basesink->clock_id = gst_clock_new_single_shot_id (basesink->clock,
+ basesink->end_time + GST_ELEMENT (basesink)->base_time);
+ GST_UNLOCK (basesink);
+
+ gst_clock_id_wait (basesink->clock_id, NULL);
+
+ GST_LOCK (basesink);
+ if (basesink->clock_id) {
+ gst_clock_id_unref (basesink->clock_id);
+ basesink->clock_id = NULL;
+ }
+ basesink->end_time = GST_CLOCK_TIME_NONE;
+ need_eos = basesink->eos;
+ }
+ GST_UNLOCK (basesink);
+
+ /* if we are still EOS, we can post the EOS message */
+ if (need_eos) {
+ /* ok, now we can post the message */
+ gst_element_post_message (GST_ELEMENT (basesink),
+ gst_message_new_eos (GST_OBJECT (basesink)));
+ }
+ }
+ GST_STREAM_UNLOCK (pad);
+ break;
+ }
+ case GST_EVENT_DISCONTINUOUS:
+ GST_STREAM_LOCK (pad);
+ if (basesink->clock) {
+ //gint64 value = GST_EVENT_DISCONT_OFFSET (event, 0).value;
+ }
+ GST_STREAM_UNLOCK (pad);
+ break;
+ case GST_EVENT_FLUSH:
+ /* make sure we are not blocked on the clock also clear any pending
+ * eos state. */
+ if (!GST_EVENT_FLUSH_DONE (event)) {
+ GST_LOCK (basesink);
+ basesink->eos = FALSE;
+ if (basesink->clock_id) {
+ gst_clock_id_unschedule (basesink->clock_id);
+ }
+ GST_UNLOCK (basesink);
+
+ /* unlock from a possible state change/preroll */
+ GST_PREROLL_LOCK (pad);
+ basesink->need_preroll = TRUE;
+ gst_basesink_preroll_queue_flush (basesink);
+ GST_PREROLL_SIGNAL (pad);
+ GST_PREROLL_UNLOCK (pad);
+ }
+ /* now we are completely unblocked and the _chain method
+ * will return */
+ break;
+ default:
+ result = gst_pad_event_default (pad, event);
+ break;
+ }
+
+ return result;
+}
+
+static void
+gst_basesink_get_times (GstBaseSink * basesink, GstBuffer * buffer,
+ GstClockTime * start, GstClockTime * end)
+{
+ GstClockTime timestamp, duration;
+
+ timestamp = GST_BUFFER_TIMESTAMP (buffer);
+ if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
+ duration = GST_BUFFER_DURATION (buffer);
+ if (GST_CLOCK_TIME_IS_VALID (duration)) {
+ *end = timestamp + duration;
+ }
+ *start = timestamp;
+ }
+}
+
+static void
+gst_basesink_do_sync (GstBaseSink * basesink, GstBuffer * buffer)
+{
+ if (basesink->clock) {
+ GstClockReturn ret;
+ GstClockTime start, end;
+ GstBaseSinkClass *bclass;
+
+ bclass = GST_BASESINK_GET_CLASS (basesink);
+ start = end = -1;
+ if (bclass->get_times)
+ bclass->get_times (basesink, buffer, &start, &end);
+
+ if (GST_CLOCK_TIME_IS_VALID (start)) {
+ /* save clock id so that we can unlock it if needed */
+ GST_LOCK (basesink);
+ basesink->clock_id = gst_clock_new_single_shot_id (basesink->clock,
+ start + GST_ELEMENT (basesink)->base_time);
+ basesink->end_time = end;
+ GST_UNLOCK (basesink);
+
+ ret = gst_clock_id_wait (basesink->clock_id, NULL);
+
+ GST_LOCK (basesink);
+ if (basesink->clock_id) {
+ gst_clock_id_unref (basesink->clock_id);
+ basesink->clock_id = NULL;
+ }
+ basesink->end_time = GST_CLOCK_TIME_NONE;
+ GST_UNLOCK (basesink);
+
+ GST_LOG_OBJECT (basesink, "clock entry done: %d", ret);
+ }
+ }
+}
+
+static inline void
+gst_basesink_handle_buffer (GstBaseSink * basesink, GstBuffer * buf)
+{
+ GstBaseSinkClass *bclass;
+
+ gst_basesink_do_sync (basesink, buf);
+
+ bclass = GST_BASESINK_GET_CLASS (basesink);
+ if (bclass->render)
+ bclass->render (basesink, buf);
+
+ DEBUG ("unref %p %p\n", basesink, buf);
+ gst_buffer_unref (buf);
+}
+
+static GstFlowReturn
+gst_basesink_chain_unlocked (GstPad * pad, GstBuffer * buf)
+{
+ GstBaseSink *basesink;
+ PrerollReturn result;
+
+ basesink = GST_BASESINK (GST_OBJECT_PARENT (pad));
+
+ DEBUG ("chain_unlocked %p\n", basesink);
+
+ result = gst_basesink_finish_preroll (basesink, pad, buf);
+
+ DEBUG ("chain_unlocked %p after\n", basesink);
+
+ switch (result) {
+ case PREROLL_QUEUEING:
+ return GST_FLOW_OK;
+ case PREROLL_PLAYING:
+ gst_basesink_handle_buffer (basesink, buf);
+ return GST_FLOW_OK;
+ case PREROLL_FLUSHING:
+ return GST_FLOW_UNEXPECTED;
+ default:
+ g_assert_not_reached ();
+ return GST_FLOW_UNEXPECTED;
+ }
+}
+
+static GstFlowReturn
+gst_basesink_chain (GstPad * pad, GstBuffer * buf)
+{
+ GstFlowReturn result;
+
+ g_assert (GST_BASESINK (GST_OBJECT_PARENT (pad))->pad_mode ==
+ GST_ACTIVATE_PUSH);
+
+ GST_STREAM_LOCK (pad);
+
+ result = gst_basesink_chain_unlocked (pad, buf);
+
+ GST_STREAM_UNLOCK (pad);
+
+ return result;
+}
+
+static void
+gst_basesink_loop (GstPad * pad)
+{
+ GstBaseSink *basesink;
+ GstBuffer *buf = NULL;
+ GstFlowReturn result;
+
+ basesink = GST_BASESINK (GST_OBJECT_PARENT (pad));
+
+ g_assert (basesink->pad_mode == GST_ACTIVATE_PULL);
+
+ GST_STREAM_LOCK (pad);
+
+ result = gst_pad_pull_range (pad, basesink->offset, DEFAULT_SIZE, &buf);
+ if (result != GST_FLOW_OK)
+ goto paused;
+
+ result = gst_basesink_chain_unlocked (pad, buf);
+ if (result != GST_FLOW_OK)
+ goto paused;
+
+ /* default */
+ GST_STREAM_UNLOCK (pad);
+ return;
+
+paused:
+ gst_task_pause (GST_RPAD_TASK (pad));
+ GST_STREAM_UNLOCK (pad);
+ return;
+}
+
+static gboolean
+gst_basesink_activate (GstPad * pad, GstActivateMode mode)
+{
+ gboolean result = FALSE;
+ GstBaseSink *basesink;
+
+ basesink = GST_BASESINK (GST_OBJECT_PARENT (pad));
+
+ switch (mode) {
+ case GST_ACTIVATE_PUSH:
+ g_return_val_if_fail (basesink->has_chain, FALSE);
+ result = TRUE;
+ break;
+ case GST_ACTIVATE_PULL:
+ /* if we have a scheduler we can start the task */
+ g_return_val_if_fail (basesink->has_loop, FALSE);
+ if (GST_ELEMENT_SCHEDULER (basesink)) {
+ GST_STREAM_LOCK (pad);
+ GST_RPAD_TASK (pad) =
+ gst_scheduler_create_task (GST_ELEMENT_SCHEDULER (basesink),
+ (GstTaskFunction) gst_basesink_loop, pad);
+
+ gst_task_start (GST_RPAD_TASK (pad));
+ GST_STREAM_UNLOCK (pad);
+ result = TRUE;
+ }
+ break;
+ case GST_ACTIVATE_NONE:
+ /* step 1, unblock clock sync (if any) or any other blocking thing */
+ GST_LOCK (basesink);
+ if (basesink->clock_id) {
+ gst_clock_id_unschedule (basesink->clock_id);
+ }
+ GST_UNLOCK (basesink);
+
+ /* unlock preroll */
+ GST_PREROLL_LOCK (pad);
+ GST_PREROLL_SIGNAL (pad);
+ GST_PREROLL_UNLOCK (pad);
+
+ /* step 2, make sure streaming finishes */
+ GST_STREAM_LOCK (pad);
+ /* step 3, stop the task */
+ if (GST_RPAD_TASK (pad)) {
+ gst_task_stop (GST_RPAD_TASK (pad));
+ gst_object_unref (GST_OBJECT (GST_RPAD_TASK (pad)));
+ GST_RPAD_TASK (pad) = NULL;
+ }
+ GST_STREAM_UNLOCK (pad);
+
+ result = TRUE;
+ break;
+ }
+ basesink->pad_mode = mode;
+
+ return result;
+}
+
+static GstElementStateReturn
+gst_basesink_change_state (GstElement * element)
+{
+ GstElementStateReturn ret = GST_STATE_SUCCESS;
+ GstBaseSink *basesink = GST_BASESINK (element);
+ GstElementState transition = GST_STATE_TRANSITION (element);
+
+ DEBUG ("state change > %p %x\n", basesink, transition);
+
+ switch (transition) {
+ case GST_STATE_NULL_TO_READY:
+ break;
+ case GST_STATE_READY_TO_PAUSED:
+ /* need to complete preroll before this state change completes, there
+ * is no data flow in READY so we cqn safely assume we need to preroll. */
+ basesink->offset = 0;
+ GST_PREROLL_LOCK (basesink->sinkpad);
+ basesink->preroll_queue = g_queue_new ();
+ basesink->need_preroll = TRUE;
+ basesink->have_preroll = FALSE;
+ GST_PREROLL_UNLOCK (basesink->sinkpad);
+ ret = GST_STATE_ASYNC;
+ break;
+ case GST_STATE_PAUSED_TO_PLAYING:
+ GST_PREROLL_LOCK (basesink->sinkpad);
+ if (basesink->have_preroll) {
+ /* now let it play */
+ GST_PREROLL_SIGNAL (basesink->sinkpad);
+ } else {
+ /* FIXME. We do not have a preroll and we don't need it anymore
+ * now, this is a case we want to avoid. One way would be to make
+ * a 'lost state' function that makes get_state return PAUSED with
+ * ASYNC to indicate that we are prerolling again. */
+ basesink->need_preroll = FALSE;
+ }
+ GST_PREROLL_UNLOCK (basesink->sinkpad);
+ break;
+ default:
+ break;
+ }
+
+ GST_ELEMENT_CLASS (parent_class)->change_state (element);
+
+ switch (transition) {
+ case GST_STATE_PLAYING_TO_PAUSED:
+ {
+ gboolean eos;
+
+ /* unlock clock wait if any */
+ GST_LOCK (basesink);
+ if (basesink->clock_id) {
+ gst_clock_id_unschedule (basesink->clock_id);
+ }
+ eos = basesink->eos;
+ GST_UNLOCK (basesink);
+
+ GST_PREROLL_LOCK (basesink->sinkpad);
+ /* if we don't have a preroll buffer and we have not received EOS,
+ * we need to wait for a preroll */
+ if (!basesink->have_preroll && !eos) {
+ basesink->need_preroll = TRUE;
+ ret = GST_STATE_ASYNC;
+ }
+ GST_PREROLL_UNLOCK (basesink->sinkpad);
+ break;
+ }
+ case GST_STATE_PAUSED_TO_READY:
+ /* flush out the data thread if it's locked in finish_preroll */
+ GST_PREROLL_LOCK (basesink->sinkpad);
+
+ gst_basesink_preroll_queue_flush (basesink);
+ g_queue_free (basesink->preroll_queue);
+ basesink->preroll_queue = NULL;
+
+ if (basesink->have_preroll)
+ GST_PREROLL_SIGNAL (basesink->sinkpad);
+
+ basesink->need_preroll = FALSE;
+ basesink->have_preroll = FALSE;
+ GST_PREROLL_UNLOCK (basesink->sinkpad);
+
+ /* make sure the element is finished processing */
+ GST_STREAM_LOCK (basesink->sinkpad);
+ GST_STREAM_UNLOCK (basesink->sinkpad);
+ break;
+ case GST_STATE_READY_TO_NULL:
+ break;
+ default:
+ break;
+ }
+
+ DEBUG ("state change < %p %x\n", basesink, transition);
+ return ret;
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ *
+ * gstbasesink.h:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_BASESINK_H__
+#define __GST_BASESINK_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+
+#define GST_TYPE_BASESINK (gst_basesink_get_type())
+#define GST_BASESINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_BASESINK,GstBaseSink))
+#define GST_BASESINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_BASESINK,GstBaseSinkClass))
+#define GST_BASESINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_BASESINK, GstBaseSinkClass))
+#define GST_IS_BASESINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_BASESINK))
+#define GST_IS_BASESINK_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_BASESINK))
+
+#define GST_BASESINK_CLOCK(obj) (GST_BASESINK (obj)->clock)
+#define GST_BASESINK_PAD(obj) (GST_BASESINK (obj)->sinkpad)
+
+typedef struct _GstBaseSink GstBaseSink;
+typedef struct _GstBaseSinkClass GstBaseSinkClass;
+
+struct _GstBaseSink {
+ GstElement element;
+
+ GstPad *sinkpad;
+ GstActivateMode pad_mode;
+
+ GQueue *preroll_queue; /* with PREROLL_LOCK */
+ gint preroll_queue_max_len; /* with PREROLL_LOCK */
+
+ guint64 offset;
+ gboolean has_loop;
+ gboolean has_chain;
+
+ GstClock *clock;
+ GstClockID clock_id;
+ GstClockTime end_time;
+
+ gboolean eos;
+ gboolean need_preroll;
+ gboolean have_preroll;
+};
+
+struct _GstBaseSinkClass {
+ GstElementClass parent_class;
+
+ GstStaticPadTemplate* (*get_template) (GstBaseSink *sink);
+
+ GstCaps* (*get_caps) (GstBaseSink *sink);
+ gboolean (*set_caps) (GstBaseSink *sink, GstCaps *caps);
+
+ GstBuffer* (*buffer_alloc) (GstBaseSink *sink, guint64 offset, guint size,
+ GstCaps *caps);
+
+ void (*get_times) (GstBaseSink *sink, GstBuffer *buffer,
+ GstClockTime *start, GstClockTime *end);
+
+ void (*event) (GstBaseSink *sink, GstEvent *event);
+ GstFlowReturn (*preroll) (GstBaseSink *sink, GstBuffer *buffer);
+ GstFlowReturn (*render) (GstBaseSink *sink, GstBuffer *buffer);
+};
+
+GType gst_basesink_get_type(void);
+
+G_END_DECLS
+
+#endif /* __GST_BASESINK_H__ */
libgstelements_la_CFLAGS = $(GST_OBJ_CFLAGS)
libgstelements_la_LIBADD = $(GST_OBJ_LIBS)
-libgstelements_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(as_libtool_LDFLAGS)
+libgstelements_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(as_libtool_LDFLAGS) $(top_builddir)/gst/base/libgstbase.la
noinst_HEADERS = \
gstaggregator.h \
/* GStreamer
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
- * 2000 Wim Taymans <wtay@chello.be>
+ * 2005 Wim Taymans <wim@fluendo.com>
*
* gstfakesink.c:
*
GstElementDetails gst_fakesink_details = GST_ELEMENT_DETAILS ("Fake Sink",
"Sink",
"Black hole for data",
- "Erik Walthinsen <omega@cse.ogi.edu>");
+ "Erik Walthinsen <omega@cse.ogi.edu>, "
+ "Wim Taymans <wim@fluendo.com>, "
+ "Mr. 'frag-me-more' Vanderwingo <wingo@fluendo.com>");
/* FakeSink signals and args */
LAST_SIGNAL
};
+#define DEFAULT_STATE_ERROR FAKESINK_STATE_ERROR_NONE
+#define DEFAULT_SILENT FALSE
+#define DEFAULT_DUMP FALSE
+#define DEFAULT_SYNC FALSE
+#define DEFAULT_SIGNAL_HANDOFFS FALSE
+#define DEFAULT_LAST_MESSAGE NULL
+
enum
{
ARG_0,
ARG_STATE_ERROR,
- ARG_NUM_SINKS,
ARG_SILENT,
ARG_DUMP,
ARG_SYNC,
ARG_SIGNAL_HANDOFFS,
- ARG_LAST_MESSAGE
+ ARG_LAST_MESSAGE,
};
-GstStaticPadTemplate fakesink_sink_template = GST_STATIC_PAD_TEMPLATE ("sink%d",
- GST_PAD_SINK,
- GST_PAD_REQUEST,
- GST_STATIC_CAPS_ANY);
-
#define GST_TYPE_FAKESINK_STATE_ERROR (gst_fakesink_state_error_get_type())
static GType
gst_fakesink_state_error_get_type (void)
#define _do_init(bla) \
GST_DEBUG_CATEGORY_INIT (gst_fakesink_debug, "fakesink", 0, "fakesink element");
-GST_BOILERPLATE_FULL (GstFakeSink, gst_fakesink, GstElement, GST_TYPE_ELEMENT,
+GST_BOILERPLATE_FULL (GstFakeSink, gst_fakesink, GstBaseSink, GST_TYPE_BASESINK,
_do_init);
-static void gst_fakesink_set_clock (GstElement * element, GstClock * clock);
-static GstPad *gst_fakesink_request_new_pad (GstElement * element,
- GstPadTemplate * templ, const gchar * unused);
-
static void gst_fakesink_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_fakesink_get_property (GObject * object, guint prop_id,
static GstElementStateReturn gst_fakesink_change_state (GstElement * element);
-static GstFlowReturn gst_fakesink_chain (GstPad * pad, GstBuffer * buffer);
-static gboolean gst_fakesink_event (GstPad * pad, GstEvent * event);
+static GstFlowReturn gst_fakesink_preroll (GstBaseSink * bsink,
+ GstBuffer * buffer);
+static GstFlowReturn gst_fakesink_render (GstBaseSink * bsink,
+ GstBuffer * buffer);
+static void gst_fakesink_event (GstBaseSink * bsink, GstEvent * event);
+static void gst_fakesink_get_times (GstBaseSink * bsink, GstBuffer * buffer,
+ GstClockTime * start, GstClockTime * end);
static guint gst_fakesink_signals[LAST_SIGNAL] = { 0 };
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&sinktemplate));
gst_element_class_set_details (gstelement_class, &gst_fakesink_details);
- gst_element_class_add_pad_template (gstelement_class,
- gst_static_pad_template_get (&fakesink_sink_template));
}
static void
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
+ GstBaseSinkClass *gstbasesink_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
+ gstbasesink_class = (GstBaseSinkClass *) 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",
- "The number of sinkpads", 1, G_MAXINT, 1, G_PARAM_READABLE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_STATE_ERROR,
g_param_spec_enum ("state_error", "State Error",
"Generate a state change error", GST_TYPE_FAKESINK_STATE_ERROR,
- FAKESINK_STATE_ERROR_NONE, G_PARAM_READWRITE));
+ DEFAULT_STATE_ERROR, G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LAST_MESSAGE,
g_param_spec_string ("last_message", "Last Message",
- "The message describing current status", NULL, G_PARAM_READABLE));
+ "The message describing current status", DEFAULT_LAST_MESSAGE,
+ G_PARAM_READABLE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SYNC,
- g_param_spec_boolean ("sync", "Sync", "Sync on the clock", FALSE,
+ g_param_spec_boolean ("sync", "Sync", "Sync on the clock", DEFAULT_SYNC,
G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SIGNAL_HANDOFFS,
g_param_spec_boolean ("signal-handoffs", "Signal handoffs",
- "Send a signal before unreffing the buffer", FALSE,
+ "Send a signal before unreffing the buffer", DEFAULT_SIGNAL_HANDOFFS,
G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SILENT,
g_param_spec_boolean ("silent", "Silent",
- "Don't produce last_message events", FALSE, G_PARAM_READWRITE));
+ "Don't produce last_message events", DEFAULT_SILENT,
+ G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DUMP,
g_param_spec_boolean ("dump", "Dump", "Dump received bytes to stdout",
- FALSE, G_PARAM_READWRITE));
+ DEFAULT_DUMP, G_PARAM_READWRITE));
gst_fakesink_signals[SIGNAL_HANDOFF] =
g_signal_new ("handoff", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
gst_marshal_VOID__BOXED_OBJECT, G_TYPE_NONE, 2,
GST_TYPE_BUFFER | G_SIGNAL_TYPE_STATIC_SCOPE, GST_TYPE_PAD);
- gstelement_class->request_new_pad =
- GST_DEBUG_FUNCPTR (gst_fakesink_request_new_pad);
- gstelement_class->set_clock = GST_DEBUG_FUNCPTR (gst_fakesink_set_clock);
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_fakesink_change_state);
+
+ gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_fakesink_event);
+ gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_fakesink_preroll);
+ gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_fakesink_render);
+ gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_fakesink_get_times);
}
static void
gst_fakesink_init (GstFakeSink * fakesink)
{
- GstPad *pad;
-
- pad =
- gst_pad_new_from_template (gst_static_pad_template_get (&sinktemplate),
- "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;
- fakesink->sync = FALSE;
- fakesink->last_message = NULL;
- fakesink->state_error = FAKESINK_STATE_ERROR_NONE;
- fakesink->signal_handoffs = FALSE;
-}
-
-static void
-gst_fakesink_set_clock (GstElement * element, GstClock * clock)
-{
- GstFakeSink *sink;
-
- sink = GST_FAKESINK (element);
-
- sink->clock = clock;
-}
-
-static GstPad *
-gst_fakesink_request_new_pad (GstElement * element, GstPadTemplate * templ,
- const gchar * unused)
-{
- gchar *name;
- GstPad *sinkpad;
- GstFakeSink *fakesink;
-
- g_return_val_if_fail (GST_IS_FAKESINK (element), NULL);
-
- if (templ->direction != GST_PAD_SINK) {
- g_warning ("gstfakesink: request new pad that is not a SINK pad\n");
- return NULL;
- }
-
- fakesink = GST_FAKESINK (element);
-
- name = g_strdup_printf ("sink%d", GST_ELEMENT (fakesink)->numsinkpads);
-
- sinkpad = gst_pad_new_from_template (templ, name);
- g_free (name);
- gst_pad_set_chain_function (sinkpad, GST_DEBUG_FUNCPTR (gst_fakesink_chain));
-
- gst_element_add_pad (GST_ELEMENT (fakesink), sinkpad);
-
- return sinkpad;
+ fakesink->silent = DEFAULT_SILENT;
+ fakesink->dump = DEFAULT_DUMP;
+ fakesink->sync = DEFAULT_SYNC;
+ fakesink->last_message = DEFAULT_LAST_MESSAGE;
+ fakesink->state_error = DEFAULT_STATE_ERROR;
+ fakesink->signal_handoffs = DEFAULT_SIGNAL_HANDOFFS;
}
static void
{
GstFakeSink *sink;
- /* it's not null if we got it, but it might not be ours */
sink = GST_FAKESINK (object);
switch (prop_id) {
{
GstFakeSink *sink;
- /* it's not null if we got it, but it might not be ours */
- g_return_if_fail (GST_IS_FAKESINK (object));
-
sink = GST_FAKESINK (object);
switch (prop_id) {
- case ARG_NUM_SINKS:
- g_value_set_int (value, GST_ELEMENT (sink)->numsinkpads);
- break;
case ARG_STATE_ERROR:
g_value_set_enum (value, sink->state_error);
break;
}
}
-static gboolean
-gst_fakesink_event (GstPad * pad, GstEvent * event)
+static void
+gst_fakesink_get_times (GstBaseSink * bsink, GstBuffer * buffer,
+ GstClockTime * start, GstClockTime * end)
{
- GstFakeSink *fakesink;
+ GstFakeSink *sink = GST_FAKESINK (bsink);
- fakesink = GST_FAKESINK (GST_OBJECT_PARENT (pad));
+ if (sink->sync) {
+ GST_BASESINK_CLASS (parent_class)->get_times (bsink, buffer, start, end);
+ }
+}
- if (!fakesink->silent) {
- g_free (fakesink->last_message);
+static void
+gst_fakesink_event (GstBaseSink * bsink, GstEvent * event)
+{
+ GstFakeSink *sink = GST_FAKESINK (bsink);
- fakesink->last_message =
- g_strdup_printf ("chain ******* (%s:%s)E (type: %d) %p",
- GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE (event), event);
+ if (!sink->silent) {
+ g_free (sink->last_message);
- g_object_notify (G_OBJECT (fakesink), "last_message");
- }
+ sink->last_message =
+ g_strdup_printf ("chain ******* E (type: %d) %p",
+ GST_EVENT_TYPE (event), event);
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_DISCONTINUOUS:
- default:
- gst_pad_event_default (pad, event);
- break;
+ g_object_notify (G_OBJECT (sink), "last_message");
}
-
- return TRUE;
}
static GstFlowReturn
-gst_fakesink_chain (GstPad * pad, GstBuffer * buffer)
+gst_fakesink_preroll (GstBaseSink * bsink, GstBuffer * buffer)
{
- GstBuffer *buf = GST_BUFFER (buffer);
- GstFakeSink *fakesink;
+ GstFakeSink *sink = GST_FAKESINK (bsink);
+
+ if (!sink->silent) {
+ g_free (sink->last_message);
- fakesink = GST_FAKESINK (GST_OBJECT_PARENT (pad));
+ sink->last_message = g_strdup_printf ("preroll ******* ");
- if (fakesink->sync && fakesink->clock) {
- //gst_element_wait (GST_ELEMENT (fakesink), GST_BUFFER_TIMESTAMP (buf));
+ g_object_notify (G_OBJECT (sink), "last_message");
}
+ return GST_FLOW_OK;
+}
- if (!fakesink->silent) {
- g_free (fakesink->last_message);
+static GstFlowReturn
+gst_fakesink_render (GstBaseSink * bsink, GstBuffer * buf)
+{
+ GstFakeSink *sink = GST_FAKESINK (bsink);
- fakesink->last_message =
- g_strdup_printf ("chain ******* (%s:%s)< (%d bytes, timestamp: %"
+ if (!sink->silent) {
+ g_free (sink->last_message);
+
+ sink->last_message =
+ g_strdup_printf ("chain ******* < (%d bytes, timestamp: %"
GST_TIME_FORMAT ", duration: %" GST_TIME_FORMAT ", offset: %"
G_GINT64_FORMAT ", offset_end: %" G_GINT64_FORMAT ", flags: %d) %p",
- GST_DEBUG_PAD_NAME (pad), GST_BUFFER_SIZE (buf),
+ GST_BUFFER_SIZE (buf),
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_BUFFER_OFFSET (buf),
GST_BUFFER_OFFSET_END (buf), GST_BUFFER_FLAGS (buf), buf);
- g_object_notify (G_OBJECT (fakesink), "last_message");
+ g_object_notify (G_OBJECT (sink), "last_message");
}
+ if (sink->signal_handoffs)
+ g_signal_emit (G_OBJECT (sink), gst_fakesink_signals[SIGNAL_HANDOFF], 0,
+ buf);
- if (fakesink->signal_handoffs)
- g_signal_emit (G_OBJECT (fakesink), gst_fakesink_signals[SIGNAL_HANDOFF], 0,
- buf, pad);
-
- if (fakesink->dump) {
+ if (sink->dump) {
gst_util_dump_mem (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
}
- gst_buffer_unref (buf);
-
return GST_FLOW_OK;
}
static GstElementStateReturn
gst_fakesink_change_state (GstElement * element)
{
+ GstElementStateReturn ret = GST_STATE_SUCCESS;
GstFakeSink *fakesink = GST_FAKESINK (element);
+ GstElementState transition = GST_STATE_TRANSITION (element);
- switch (GST_STATE_TRANSITION (element)) {
+ switch (transition) {
case GST_STATE_NULL_TO_READY:
if (fakesink->state_error == FAKESINK_STATE_ERROR_NULL_READY)
goto error;
g_free (fakesink->last_message);
fakesink->last_message = NULL;
break;
+ default:
+ break;
}
- if (GST_ELEMENT_CLASS (parent_class)->change_state)
- return GST_ELEMENT_CLASS (parent_class)->change_state (element);
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
- return GST_STATE_SUCCESS;
+ return ret;
error:
GST_ELEMENT_ERROR (element, CORE, STATE_CHANGE, (NULL), (NULL));
#define __GST_FAKESINK_H__
#include <gst/gst.h>
+#include <gst/base/gstbasesink.h>
G_BEGIN_DECLS
typedef struct _GstFakeSinkClass GstFakeSinkClass;
struct _GstFakeSink {
- GstElement element;
+ GstBaseSink element;
gboolean silent;
gboolean dump;
gboolean sync;
gboolean signal_handoffs;
- GstClock *clock;
GstFakeSinkStateError state_error;
-
gchar *last_message;
};
struct _GstFakeSinkClass {
- GstElementClass parent_class;
+ GstBaseSinkClass parent_class;
/* signals */
void (*handoff) (GstElement *element, GstBuffer *buf, GstPad *pad);
static gboolean gst_bin_add_func (GstBin * bin, GstElement * element);
static gboolean gst_bin_remove_func (GstBin * bin, GstElement * element);
-static void gst_bin_set_manager (GstElement * element, GstPipeline * manager);
-static gboolean gst_bin_send_event (GstElement * element, GstEvent * event);
-
#ifndef GST_DISABLE_INDEX
static void gst_bin_set_index_func (GstElement * element, GstIndex * index);
#endif
static GstClock *gst_bin_get_clock_func (GstElement * element);
static void gst_bin_set_clock_func (GstElement * element, GstClock * clock);
+static void gst_bin_set_manager (GstElement * element, GstPipeline * manager);
static void gst_bin_set_bus (GstElement * element, GstBus * bus);
static void gst_bin_set_scheduler (GstElement * element, GstScheduler * sched);
+static gboolean gst_bin_send_event (GstElement * element, GstEvent * event);
#ifndef GST_DISABLE_LOADSAVE
static xmlNodePtr gst_bin_save_thyself (GstObject * object, xmlNodePtr parent);
GST_DEBUG_FUNCPTR (gst_bin_restore_thyself);
#endif
- gstelement_class->set_manager = GST_DEBUG_FUNCPTR (gst_bin_set_manager);
- gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_bin_send_event);
gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_bin_change_state);
gstelement_class->get_state = GST_DEBUG_FUNCPTR (gst_bin_get_state);
#ifndef GST_DISABLE_INDEX
#endif
gstelement_class->get_clock = GST_DEBUG_FUNCPTR (gst_bin_get_clock_func);
gstelement_class->set_clock = GST_DEBUG_FUNCPTR (gst_bin_set_clock_func);
+ gstelement_class->set_manager = GST_DEBUG_FUNCPTR (gst_bin_set_manager);
gstelement_class->set_bus = GST_DEBUG_FUNCPTR (gst_bin_set_bus);
gstelement_class->set_scheduler = GST_DEBUG_FUNCPTR (gst_bin_set_scheduler);
+ gstelement_class->send_event = GST_DEBUG_FUNCPTR (gst_bin_send_event);
+
klass->add_element = GST_DEBUG_FUNCPTR (gst_bin_add_func);
klass->remove_element = GST_DEBUG_FUNCPTR (gst_bin_remove_func);
}
GST_UNLOCK (bin);
}
+/* set the manager on all of the children in this bin
+ *
+ * MT safe
+ */
+static void
+gst_bin_set_manager (GstElement * element, GstPipeline * manager)
+{
+ GstBin *bin = GST_BIN (element);
+ GList *kids;
+ GstElement *kid;
+
+ GST_ELEMENT_CLASS (parent_class)->set_manager (element, manager);
+
+ GST_LOCK (element);
+ for (kids = bin->children; kids != NULL; kids = kids->next) {
+ kid = GST_ELEMENT (kids->data);
+ gst_element_set_manager (kid, manager);
+ }
+ GST_UNLOCK (element);
+}
+
/* add an element to this bin
*
* MT safe
gst_bin_get_state (GstElement * element, GstElementState * state,
GstElementState * pending, GTimeVal * timeout)
{
- /* implement me */
- return GST_STATE_FAILURE;
+ GstBin *bin = GST_BIN (element);
+ GstElementStateReturn ret;
+ GList *children;
+ guint32 children_cookie;
+
+ /* we cannot take the state lock yet as we might block when querying
+ * the children, holding the lock too long for no reason. */
+
+ /* next we poll all children for their state to see if one of them
+ * is still busy with its state change. */
+ GST_LOCK (bin);
+restart:
+ ret = GST_STATE_SUCCESS;
+ children = bin->children;
+ children_cookie = bin->children_cookie;
+ while (children) {
+ GstElement *child = GST_ELEMENT_CAST (children->data);
+
+ gst_object_ref (GST_OBJECT_CAST (child));
+ GST_UNLOCK (bin);
+
+ /* ret is ASYNC if some child is still performing the state change */
+ ret = gst_element_get_state (child, NULL, NULL, timeout);
+
+ gst_object_unref (GST_OBJECT_CAST (child));
+
+ if (ret != GST_STATE_SUCCESS) {
+ /* some child is still busy or in error, we can report that
+ * right away. */
+ goto done;
+ }
+ /* now grab the lock to iterate to the next child */
+ GST_LOCK (bin);
+ if (G_UNLIKELY (children_cookie != bin->children_cookie))
+ /* child added/removed during state change, restart */
+ goto restart;
+
+ children = g_list_next (children);
+ }
+ GST_UNLOCK (bin);
+
+done:
+ /* now we can take the state lock */
+ GST_STATE_LOCK (bin);
+ switch (ret) {
+ case GST_STATE_SUCCESS:
+ /* we can commit the state */
+ gst_element_commit_state (element);
+ break;
+ case GST_STATE_FAILURE:
+ /* some element failed, abort the state change */
+ gst_element_abort_state (element);
+ break;
+ default:
+ /* other cases are just passed along */
+ break;
+ }
+
+ /* and report the state if needed */
+ 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. It works
static GstElementStateReturn
gst_bin_change_state (GstElement * element)
{
- /* implement me */
- return GST_STATE_FAILURE;
+ GstBin *bin;
+ GstElementStateReturn ret;
+ GstElementState old_state, pending;
+ gboolean have_async = FALSE;
+ GList *children;
+ guint32 children_cookie;
+ GQueue *elem_queue; /* list of elements waiting for a state change */
+
+ bin = GST_BIN (element);
+
+ /* we don't need to take the STATE_LOCK, it is already taken */
+ old_state = GST_STATE (element);
+ pending = GST_STATE_PENDING (element);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "changing state of children from %s to %s",
+ gst_element_state_get_name (old_state),
+ gst_element_state_get_name (pending));
+
+ if (pending == GST_STATE_VOID_PENDING)
+ return GST_STATE_SUCCESS;
+
+ /* all elements added to this queue should have their refcount
+ * incremented */
+ elem_queue = g_queue_new ();
+
+ /* first step, find all sink elements, these are the elements
+ * without (linked) source pads. */
+ GST_LOCK (bin);
+restart:
+ children = bin->children;
+ children_cookie = bin->children_cookie;
+ while (children) {
+ GstElement *child = GST_ELEMENT_CAST (children->data);
+
+ gst_object_ref (GST_OBJECT_CAST (child));
+ GST_UNLOCK (bin);
+
+ if (bin_element_is_sink (child, bin) == 0) {
+ /* this also keeps the refcount on the element, note that
+ * the _is_sink function unrefs the element when it is not
+ * a sink. */
+ g_queue_push_tail (elem_queue, child);
+ }
+
+ GST_LOCK (bin);
+ if (G_UNLIKELY (children_cookie != bin->children_cookie)) {
+ /* undo what we had */
+ g_queue_foreach (elem_queue, (GFunc) gst_object_unref, NULL);
+ while (g_queue_pop_head (elem_queue));
+ goto restart;
+ }
+
+ children = g_list_next (children);
+ }
+ GST_UNLOCK (bin);
+
+ /* 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;
+ gboolean locked;
+
+ /* queue all elements connected to the sinkpads of this element */
+ GST_LOCK (qelement);
+ pads = qelement->sinkpads;
+ while (pads) {
+ GstPad *pad = GST_PAD_CAST (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) {
+ GstObject *peer_elem;
+
+ peer_elem = gst_object_get_parent (GST_OBJECT_CAST (peer));
+
+ if (peer_elem) {
+ GstObject *parent;
+
+ /* see if this element is in the bin we are currently handling */
+ parent = gst_object_get_parent (GST_OBJECT_CAST (peer_elem));
+ if (parent && parent == GST_OBJECT_CAST (bin)) {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "adding element %s to queue", GST_ELEMENT_NAME (peer_elem));
+
+ /* was reffed before pushing on the queue by the
+ * gst_object_get_parent() call we used to get the element. */
+ g_queue_push_tail (elem_queue, peer_elem);
+ } else {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "not adding element %s to queue, it is in another bin",
+ GST_ELEMENT_NAME (peer_elem));
+ }
+ if (parent) {
+ gst_object_unref (GST_OBJECT_CAST (parent));
+ }
+ }
+ gst_object_unref (GST_OBJECT_CAST (peer));
+ } else {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "pad %s:%s does not have a peer", GST_DEBUG_PAD_NAME (pad));
+ }
+ pads = g_list_next (pads);
+ }
+ /* peel off the locked flag and release the element lock */
+ locked = GST_FLAG_IS_SET (qelement, GST_ELEMENT_LOCKED_STATE);
+ GST_UNLOCK (qelement);
+
+ if (G_UNLIKELY (locked))
+ goto next_element;
+
+ qelement->base_time = element->base_time;
+ 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 refcount of element we popped off the queue */
+ gst_object_unref (GST_OBJECT (qelement));
+ goto exit;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+ next_element:
+ 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,
+ "done changing bin's state from %s to %s, now in %s",
+ gst_element_state_get_name (old_state),
+ gst_element_state_get_name (pending),
+ gst_element_state_get_name (GST_STATE (element)));
+
+exit:
+ /* release refcounts in queue, should normally be empty */
+ g_queue_foreach (elem_queue, (GFunc) gst_object_unref, NULL);
+ g_queue_free (elem_queue);
+
+ return ret;
}
static void
G_OBJECT_CLASS (parent_class)->dispose (object);
}
-static void
-gst_bin_set_manager (GstElement * element, GstPipeline * manager)
-{
- GstBin *bin = GST_BIN (element);
- GList *kids;
- GstElement *kid;
-
- GST_ELEMENT_CLASS (parent_class)->set_manager (element, manager);
-
- GST_LOCK (element);
- for (kids = bin->children; kids != NULL; kids = kids->next) {
- kid = GST_ELEMENT (kids->data);
- gst_element_set_manager (kid, manager);
- }
- GST_UNLOCK (element);
-}
-
/*
* This function is a utility event handler for seek events.
- * It will change pipeline state to PAUSED, iterate event
- * over all available sinks, distribute new basetime to the
- * pipeline after preroll is done and then re-set to PLAYING.
+ * It will send the event to all sinks.
* Applications are free to override this behaviour and
* implement their own seek handler, but this will work for
* pretty much all cases in practice.
*/
-
static gboolean
gst_bin_send_event (GstElement * element, GstEvent * event)
{
GstBin *bin = GST_BIN (element);
GstIterator *iter;
- GstElement *sink;
- gpointer data;
gboolean res = TRUE;
+ gboolean done = FALSE;
iter = gst_bin_iterate_sinks (bin);
GST_DEBUG_OBJECT (bin, "Sending event to sink children");
- /* iterate over all sinks; preroll will take care of sync,
- * discont event handling will take care of proper clock
- * adjustment. Sweet. */
- while (gst_iterator_next (iter, &data) == GST_ITERATOR_OK) {
- gst_event_ref (event);
- sink = GST_ELEMENT (data);
- res &= gst_element_send_event (sink, event);
- gst_object_unref (GST_OBJECT (sink));
+ while (!done) {
+ gpointer data;
+
+ switch (gst_iterator_next (iter, &data)) {
+ case GST_ITERATOR_OK:
+ {
+ GstElement *sink;
+
+ gst_event_ref (event);
+ sink = GST_ELEMENT_CAST (data);
+ res &= gst_element_send_event (sink, event);
+ gst_object_unref (GST_OBJECT (sink));
+ break;
+ }
+ case GST_ITERATOR_RESYNC:
+ gst_iterator_resync (iter);
+ res = TRUE;
+ break;
+ default:
+ case GST_ITERATOR_DONE:
+ done = TRUE;
+ break;
+ }
}
gst_iterator_free (iter);
gst_event_unref (event);
element->pads_cookie++;
GST_UNLOCK (element);
+ GST_STATE_LOCK (element);
+ /* activate pad when we are playing */
+ if (GST_STATE (element) == GST_STATE_PLAYING)
+ /* FIXME, figure out mode */
+ gst_pad_set_active (pad, GST_ACTIVATE_PUSH);
+ GST_STATE_UNLOCK (element);
+
/* emit the NEW_PAD signal */
g_signal_emit (G_OBJECT (element), gst_element_signals[NEW_PAD], 0, pad);
GstElementState * state, GstElementState * pending, GTimeVal * timeout)
{
GstElementStateReturn ret = GST_STATE_FAILURE;
+ GstElementState old_pending;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
+
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "getting state");
+
+ GST_STATE_LOCK (element);
+ /* we got an error, report immediatly */
+ if (GST_STATE_ERROR (element))
+ goto done;
+
+ old_pending = GST_STATE_PENDING (element);
+ if (old_pending != GST_STATE_VOID_PENDING) {
+ GTimeVal *timeval, abstimeout;
+
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "wait for pending");
+ if (timeout) {
+ /* make timeout absolute */
+ g_get_current_time (&abstimeout);
+ g_time_val_add (&abstimeout,
+ timeout->tv_sec * G_USEC_PER_SEC + timeout->tv_usec);
+ timeval = &abstimeout;
+ } else {
+ timeval = NULL;
+ }
+ /* we have a pending state change, wait for it to complete */
+ if (!GST_STATE_TIMED_WAIT (element, timeval)) {
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "timeout");
+ /* timeout triggered */
+ ret = GST_STATE_ASYNC;
+ } else {
+ /* could be success or failure */
+ if (old_pending == GST_STATE (element)) {
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "got success");
+ ret = GST_STATE_SUCCESS;
+ } else {
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "got failure");
+ ret = GST_STATE_FAILURE;
+ }
+ }
+ }
+ /* if nothing is pending anymore we can return SUCCESS */
+ if (GST_STATE_PENDING (element) == GST_STATE_VOID_PENDING) {
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "nothing pending");
+ ret = GST_STATE_SUCCESS;
+ }
+
+done:
+ if (state)
+ *state = GST_STATE (element);
+ if (pending)
+ *pending = GST_STATE_PENDING (element);
+
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+ "state current: %s, pending: %s",
+ gst_element_state_get_name (GST_STATE (element)),
+ gst_element_state_get_name (GST_STATE_PENDING (element)));
+
+ GST_STATE_UNLOCK (element);
- /* implment me */
return ret;
}
void
gst_element_abort_state (GstElement * element)
{
- /* implment me */
+ GstElementState pending;
+
+ g_return_if_fail (GST_IS_ELEMENT (element));
+
+ pending = GST_STATE_PENDING (element);
+
+ if (pending != GST_STATE_VOID_PENDING && !GST_STATE_ERROR (element)) {
+ 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));
+
+ /* flag error */
+ GST_STATE_ERROR (element) = TRUE;
+
+ GST_STATE_BROADCAST (element);
+ }
}
/**
void
gst_element_commit_state (GstElement * element)
{
- /* implement me */
+ GstElementState pending;
+ GstMessage *message;
+
+ 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;
+ GST_STATE_ERROR (element) = FALSE;
+
+ g_signal_emit (G_OBJECT (element), gst_element_signals[STATE_CHANGE],
+ 0, old_state, pending);
+ message = gst_message_new_state_changed (GST_OBJECT (element),
+ old_state, pending);
+ gst_element_post_message (element, message);
+ GST_STATE_BROADCAST (element);
+ }
}
/**
void
gst_element_lost_state (GstElement * element)
{
- /* implement me */
+ g_return_if_fail (GST_IS_ELEMENT (element));
+
+ if (GST_STATE_PENDING (element) == GST_STATE_VOID_PENDING &&
+ !GST_STATE_ERROR (element)) {
+ GstElementState current_state = GST_STATE (element);
+
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+ "lost state of %s", gst_element_state_get_name (current_state));
+
+ GST_STATE_PENDING (element) = current_state;
+ GST_STATE_ERROR (element) = FALSE;
+ }
}
/**
GstElementStateReturn
gst_element_set_state (GstElement * element, GstElementState state)
{
- /* implement me */
- return GST_STATE_SUCCESS;
+ GstElementClass *oclass;
+ GstElementState current;
+ GstElementStateReturn return_val = GST_STATE_SUCCESS;
+
+ /* get the element state lock */
+ GST_STATE_LOCK (element);
+
+#if 0
+ /* a state change is pending and we are not in error, the element is busy
+ * with a state change and we cannot proceed.
+ * FIXME, does not work for a bin.*/
+ if (G_UNLIKELY (GST_STATE_PENDING (element) != GST_STATE_VOID_PENDING &&
+ !GST_STATE_ERROR (element)))
+ goto was_busy;
+#endif
+
+ /* clear the error flag */
+ GST_STATE_ERROR (element) = FALSE;
+
+ /* 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 (current), gst_element_state_get_name (state));
+
+ oclass = GST_ELEMENT_GET_CLASS (element);
+
+ /* 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;
+
+ /* calculate the pending state */
+ if (current < state)
+ pending = current << 1;
+ else if (current > state)
+ pending = current >> 1;
+ else
+ pending = current;
+
+ /* set the pending state variable */
+ GST_STATE_PENDING (element) = pending;
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "%s: setting state from %s to %s",
+ (pending != state ? "intermediate" : "final"),
+ 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:
+ 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:
+ goto invalid_return;
+ }
+ /* 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");
+
+ return return_val;
+
+ /* ERROR */
+#if 0
+was_busy:
+ {
+ GST_STATE_UNLOCK (element);
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+ "was busy with a state change");
+
+ return GST_STATE_BUSY;
+ }
+#endif
+invalid_return:
+ {
+ GST_STATE_UNLOCK (element);
+ /* somebody added a GST_STATE_ and forgot to do stuff here ! */
+ g_critical ("unkown return value from a state change function");
+ return GST_STATE_FAILURE;
+ }
}
/* is called with STATE_LOCK
static gboolean
gst_element_pads_activate (GstElement * element, gboolean active)
{
- return FALSE;
+ GList *pads;
+ gboolean result;
+ guint32 cookie;
+
+ GST_LOCK (element);
+restart:
+ result = TRUE;
+ pads = element->pads;
+ cookie = element->pads_cookie;
+ for (; pads && result; pads = g_list_next (pads)) {
+ GstPad *pad = GST_PAD (pads->data);
+
+ gst_object_ref (GST_OBJECT (pad));
+ GST_UNLOCK (element);
+
+ /* we only care about real pads */
+ if (GST_IS_REAL_PAD (pad)) {
+ GstRealPad *peer;
+ gboolean pad_loop, pad_get;
+ gboolean delay = FALSE;
+
+ /* see if the pad has a loop function and grab
+ * the peer */
+ GST_LOCK (pad);
+ pad_get = GST_RPAD_GETRANGEFUNC (pad) != NULL;
+ pad_loop = GST_RPAD_LOOPFUNC (pad) != NULL;
+ peer = GST_RPAD_PEER (pad);
+ if (peer)
+ gst_object_ref (GST_OBJECT (peer));
+ GST_UNLOCK (pad);
+
+ if (peer) {
+ gboolean peer_loop, peer_get;
+
+ /* see if the peer has a getrange function */
+ peer_get = GST_RPAD_GETRANGEFUNC (peer) != NULL;
+ /* see if the peer has a loop function */
+ peer_loop = GST_RPAD_LOOPFUNC (peer) != NULL;
+
+ /* sinkpads with a loop function are delayed since they
+ * need the srcpad to be active first */
+ if (GST_PAD_IS_SINK (pad) && pad_loop && peer_get) {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "delaying pad %s", GST_OBJECT_NAME (pad));
+ delay = TRUE;
+ } else if (GST_PAD_IS_SRC (pad) && peer_loop && pad_get) {
+ /* If the pad is a source and the peer has a loop function,
+ * we can activate the srcpad and then the loopbased sinkpad */
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "%sactivating pad %s", (active ? "" : "(de)"),
+ GST_OBJECT_NAME (pad));
+ result &= gst_pad_set_active (pad,
+ (active ? GST_ACTIVATE_PULL : GST_ACTIVATE_NONE));
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "%sactivating delayed pad %s", (active ? "" : "(de)"),
+ GST_OBJECT_NAME (peer));
+ result &= gst_pad_set_active (GST_PAD (peer),
+ (active ? GST_ACTIVATE_PULL : GST_ACTIVATE_NONE));
+
+ /* set flag here since we don't want the code below to activate
+ * the pad again */
+ delay = TRUE;
+ }
+ gst_object_unref (GST_OBJECT (peer));
+ }
+
+ /* all other conditions are just push based pads */
+ if (!delay) {
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "%sactivating pad %s", (active ? "" : "(de)"),
+ GST_OBJECT_NAME (pad));
+
+ result &= gst_pad_set_active (pad,
+ (active ? GST_ACTIVATE_PUSH : GST_ACTIVATE_NONE));
+ }
+ }
+ gst_object_unref (GST_OBJECT (pad));
+
+ GST_LOCK (element);
+ if (cookie != element->pads_cookie)
+ goto restart;
+ }
+ GST_UNLOCK (element);
+
+ return result;
}
/* is called with STATE_LOCK */
#include "gstmarshal.h"
#include "gstutils.h"
#include "gstelement.h"
-#include "gstpipeline.h"
#include "gstbin.h"
#include "gstscheduler.h"
#include "gstevent.h"
gboolean
gst_pad_set_active (GstPad * pad, GstActivateMode mode)
{
- /* implement me */
- return FALSE;
-}
+ GstRealPad *realpad;
+ gboolean old;
+ GstPadActivateFunction activatefunc;
+ gboolean active;
+
+ g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+
+ GST_PAD_REALIZE_AND_LOCK (pad, realpad, lost_ghostpad);
+
+ active = (mode != GST_ACTIVATE_NONE);
+
+ old = GST_PAD_IS_ACTIVE (realpad);
+
+ /* if nothing changed, we can just exit */
+ if (G_UNLIKELY (old == active))
+ goto exit;
+
+ /* 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_UNSET (realpad, GST_PAD_ACTIVE);
+ /* unlock blocked pads so element can resume and stop */
+ GST_PAD_BLOCK_SIGNAL (realpad);
+ }
+
+ if (active) {
+ if (GST_PAD_DIRECTION (pad) == GST_PAD_SRC) {
+ if (mode == GST_ACTIVATE_PULL) {
+ if (!realpad->getrangefunc)
+ goto wrong_mode;
+ } else {
+ /* we can push if driven by a chain or loop on the sink pad */
+ }
+ } else { /* sink pads */
+ if (mode == GST_ACTIVATE_PULL) {
+ /* the src can drive us with getrange */
+ } else {
+ if (!realpad->chainfunc)
+ goto wrong_mode;
+ }
+ }
+ }
+
+ activatefunc = realpad->activatefunc;
+ if (activatefunc) {
+ gboolean result;
+
+ GST_CAT_DEBUG (GST_CAT_PADS,
+ "calling activate function on pad %s:%s with mode %d",
+ GST_DEBUG_PAD_NAME (realpad), mode);
+
+ /* unlock so element can sync */
+ GST_UNLOCK (realpad);
+ result = activatefunc (GST_PAD_CAST (realpad), mode);
+ /* and lock again */
+ GST_LOCK (realpad);
+ if (result == FALSE)
+ goto activate_error;
+ }
+
+ /* when going to active alow data passing now */
+ if (active) {
+ GST_CAT_DEBUG (GST_CAT_PADS, "activating pad %s:%s",
+ GST_DEBUG_PAD_NAME (realpad));
+ GST_FLAG_SET (realpad, GST_PAD_ACTIVE);
+ }
+
+exit:
+ GST_UNLOCK (realpad);
+ return TRUE;
+
+ /* errors */
+lost_ghostpad:
+ {
+ return FALSE;
+ }
+wrong_mode:
+ {
+ GST_CAT_DEBUG (GST_CAT_PADS,
+ "pad %s:%s lacks functions to be active in mode %d",
+ GST_DEBUG_PAD_NAME (realpad), mode);
+ GST_UNLOCK (realpad);
+ return FALSE;
+ }
+activate_error:
+ {
+ GST_CAT_DEBUG (GST_CAT_PADS,
+ "activate function returned FALSE for pad %s:%s",
+ GST_DEBUG_PAD_NAME (realpad));
+ GST_UNLOCK (realpad);
+ return FALSE;
+ }
+}
/**
* gst_pad_is_active:
*
* Returns: TRUE if the event was sent succesfully.
*/
-
gboolean
gst_pad_event_default (GstPad * pad, GstEvent * event)
{
g_return_val_if_fail (event != NULL, FALSE);
switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_DISCONTINUOUS:{
- GstElement *element = gst_pad_get_parent (pad);
- guint64 time;
-
- if (element && element->clock && GST_ELEMENT_MANAGER (element) &&
- gst_event_discont_get_value (event, GST_FORMAT_TIME, &time)) {
- GST_PIPELINE (GST_ELEMENT_MANAGER (element))->stream_time = time;
- }
- break;
- }
- case GST_EVENT_EOS:{
+ case GST_EVENT_EOS:
+ {
GstRealPad *rpad = GST_PAD_REALIZE (pad);
if (GST_RPAD_TASK (rpad)) {
return result;
}
+/* sending an event on the pipeline pauses the pipeline if it
+ * was playing.
+ */
static gboolean
gst_pipeline_send_event (GstElement * element, GstEvent * event)
{
gboolean was_playing;
gboolean res;
+ GstElementState state;
- GST_STATE_LOCK (element);
- /* hmm... questionable */
- was_playing = (GST_STATE (element) == GST_STATE_PLAYING);
- GST_STATE_UNLOCK (element);
+ /* need to call _get_state() since a bin state is only updated
+ * with this call. FIXME, we should probably not block but just
+ * take a snapshot. */
+ gst_element_get_state (element, &state, NULL, NULL);
+ was_playing = state == GST_STATE_PLAYING;
if (was_playing && GST_EVENT_TYPE (event) == GST_EVENT_SEEK)
gst_element_set_state (element, GST_STATE_PAUSED);
static GstElementStateReturn
gst_pipeline_change_state (GstElement * element)
{
- /* implement me */
- return GST_STATE_FAILURE;
+ GstElementStateReturn result = GST_STATE_SUCCESS;
+ GstPipeline *pipeline = GST_PIPELINE (element);
+ gint transition = GST_STATE_TRANSITION (element);
+
+ switch (transition) {
+ case GST_STATE_NULL_TO_READY:
+ gst_scheduler_setup (GST_ELEMENT_SCHEDULER (pipeline));
+ break;
+ case GST_STATE_READY_TO_PAUSED:
+ {
+ GstClock *clock;
+
+ clock = gst_element_get_clock (element);
+ gst_element_set_clock (element, clock);
+ pipeline->eosed = NULL;
+ break;
+ }
+ case GST_STATE_PAUSED_TO_PLAYING:
+ if (element->clock) {
+ GstClockTime start_time = gst_clock_get_time (element->clock);
+
+ element->base_time = start_time -
+ pipeline->stream_time + pipeline->delay;
+ GST_DEBUG ("stream_time=%" GST_TIME_FORMAT ", start_time=%"
+ GST_TIME_FORMAT, GST_TIME_ARGS (pipeline->stream_time),
+ GST_TIME_ARGS (start_time));
+ } else {
+ element->base_time = 0;
+ GST_DEBUG ("no clock, using base time of 0");
+ }
+ break;
+ case GST_STATE_PLAYING_TO_PAUSED:
+ case GST_STATE_PAUSED_TO_READY:
+ case GST_STATE_READY_TO_NULL:
+ break;
+ }
+
+ result = GST_ELEMENT_CLASS (parent_class)->change_state (element);
+
+ switch (transition) {
+ case GST_STATE_READY_TO_PAUSED:
+ pipeline->stream_time = 0;
+ break;
+ case GST_STATE_PAUSED_TO_PLAYING:
+ break;
+ case GST_STATE_PLAYING_TO_PAUSED:
+ if (element->clock) {
+ pipeline->stream_time = gst_clock_get_time (element->clock) -
+ element->base_time;
+ }
+ GST_DEBUG ("stream_time=%" GST_TIME_FORMAT,
+ GST_TIME_ARGS (pipeline->stream_time));
+ break;
+ case GST_STATE_PAUSED_TO_READY:
+ break;
+ case GST_STATE_READY_TO_NULL:
+ break;
+ }
+
+ /* we wait for async state changes ourselves.
+ * FIXME this can block forever, better do this in a worker
+ * thread or use a timeout? */
+ if (result == GST_STATE_ASYNC) {
+ GTimeVal *timeval, timeout;
+
+ GST_STATE_UNLOCK (pipeline);
+
+ GST_LOCK (pipeline);
+ if (pipeline->play_timeout > 0) {
+ GST_TIME_TO_TIMEVAL (pipeline->play_timeout, timeout);
+ timeval = &timeout;
+ } else {
+ timeval = NULL;
+ }
+ GST_UNLOCK (pipeline);
+
+ result = gst_element_get_state (element, NULL, NULL, timeval);
+ GST_STATE_LOCK (pipeline);
+ }
+
+ return result;
}
/**
--- /dev/null
+lib_LTLIBRARIES = libgstbase.la
+AS_LIBTOOL_LIB = libgstbase
+
+EXTRA_DIST = $(as_libtool_EXTRA_DIST)
+noinst_DATA = $(as_libtool_noinst_DATA_files)
+
+libgstbase_la_DEPENDENCIES = ../libgstreamer-@GST_MAJORMINOR@.la
+libgstbase_la_SOURCES = \
+ gstbasesink.c
+
+libgstbase_la_CFLAGS = $(GST_OBJ_CFLAGS)
+libgstbase_la_LIBADD = $(GST_OBJ_LIBS)
+libgstbase_la_LDFLAGS = $(as_libtool_LDFLAGS)
+
+noinst_HEADERS =
+ gstbasesink.h
+
+install-data-local: as-libtool-install-data-local
+
+uninstall-local: as-libtool-uninstall-local
+
+include $(top_srcdir)/common/as-libtool.mak
+
--- /dev/null
+Base classes
+------------
+
+GstBaseSink
+
+ Base class for sink elements.
+
+ - one sinkpad
+ - handles state changes
+ - does flushing
+ - preroll with optional preview
+ - pull/push mode
+ - EOS handling
+
+ FIXME: not much point making it operate in pull mode as a generic
+ base class I guess...
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2005 Wim Taymans <wim@fluendo.com>
+ *
+ * gstbasesink.c:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "gstbasesink.h"
+#include <gst/gstmarshal.h>
+
+static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS_ANY);
+
+GST_DEBUG_CATEGORY_STATIC (gst_basesink_debug);
+#define GST_CAT_DEFAULT gst_basesink_debug
+
+/* #define DEBUGGING */
+#ifdef DEBUGGING
+#define DEBUG(str,args...) g_print (str,##args)
+#else
+#define DEBUG(str,args...)
+#endif
+
+/* BaseSink signals and properties */
+enum
+{
+ /* FILL ME */
+ SIGNAL_HANDOFF,
+ LAST_SIGNAL
+};
+
+#define DEFAULT_SIZE 1024
+#define DEFAULT_HAS_LOOP FALSE
+#define DEFAULT_HAS_CHAIN TRUE
+
+enum
+{
+ PROP_0,
+ PROP_HAS_LOOP,
+ PROP_HAS_CHAIN,
+ PROP_PREROLL_QUEUE_LEN
+};
+
+#define _do_init(bla) \
+ GST_DEBUG_CATEGORY_INIT (gst_basesink_debug, "basesink", 0, "basesink element");
+
+GST_BOILERPLATE_FULL (GstBaseSink, gst_basesink, GstElement, GST_TYPE_ELEMENT,
+ _do_init);
+
+static void gst_basesink_set_clock (GstElement * element, GstClock * clock);
+
+static void gst_basesink_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_basesink_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
+static GstStaticPadTemplate *gst_base_sink_get_template (GstBaseSink * sink);
+static GstCaps *gst_base_sink_get_caps (GstBaseSink * sink);
+static gboolean gst_base_sink_set_caps (GstBaseSink * sink, GstCaps * caps);
+static GstBuffer *gst_base_sink_buffer_alloc (GstBaseSink * sink,
+ guint64 offset, guint size, GstCaps * caps);
+static void gst_basesink_get_times (GstBaseSink * basesink, GstBuffer * buffer,
+ GstClockTime * start, GstClockTime * end);
+
+static GstElementStateReturn gst_basesink_change_state (GstElement * element);
+
+static GstFlowReturn gst_basesink_chain_unlocked (GstPad * pad,
+ GstBuffer * buffer);
+static void gst_basesink_loop (GstPad * pad);
+static GstFlowReturn gst_basesink_chain (GstPad * pad, GstBuffer * buffer);
+static gboolean gst_basesink_activate (GstPad * pad, GstActivateMode mode);
+static gboolean gst_basesink_event (GstPad * pad, GstEvent * event);
+static inline void gst_basesink_handle_buffer (GstBaseSink * basesink,
+ GstBuffer * buf);
+
+static GstStaticPadTemplate *
+gst_basesink_get_template (GstBaseSink * bsink)
+{
+ GstStaticPadTemplate *template = NULL;
+ GstBaseSinkClass *bclass;
+
+ bclass = GST_BASESINK_GET_CLASS (bsink);
+
+ if (bclass->get_template)
+ template = bclass->get_template (bsink);
+
+ if (template == NULL) {
+ template = &sinktemplate;
+ }
+ return template;
+}
+
+static void
+gst_basesink_base_init (gpointer g_class)
+{
+ //GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
+
+ /*
+ gst_element_class_add_pad_template (gstelement_class,
+ gst_static_pad_template_get (&sinktemplate));
+ */
+}
+
+static void
+gst_basesink_class_init (GstBaseSinkClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+
+ gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+
+ gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_basesink_set_property);
+ gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_basesink_get_property);
+
+ g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HAS_LOOP,
+ g_param_spec_boolean ("has-loop", "has-loop",
+ "Enable loop-based operation", DEFAULT_HAS_LOOP,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HAS_CHAIN,
+ g_param_spec_boolean ("has-chain", "has-chain",
+ "Enable chain-based operation", DEFAULT_HAS_CHAIN,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ g_object_class_install_property (G_OBJECT_CLASS (klass),
+ PROP_PREROLL_QUEUE_LEN,
+ g_param_spec_uint ("preroll-queue-len", "preroll-queue-len",
+ "Number of buffers to queue during preroll", 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ gstelement_class->set_clock = GST_DEBUG_FUNCPTR (gst_basesink_set_clock);
+ gstelement_class->change_state =
+ GST_DEBUG_FUNCPTR (gst_basesink_change_state);
+
+ klass->get_caps = GST_DEBUG_FUNCPTR (gst_base_sink_get_caps);
+ klass->set_caps = GST_DEBUG_FUNCPTR (gst_base_sink_set_caps);
+ klass->get_template = GST_DEBUG_FUNCPTR (gst_base_sink_get_template);
+ klass->buffer_alloc = GST_DEBUG_FUNCPTR (gst_base_sink_buffer_alloc);
+ klass->get_times = GST_DEBUG_FUNCPTR (gst_basesink_get_times);
+}
+
+static GstCaps *
+gst_basesink_pad_getcaps (GstPad * pad)
+{
+ GstBaseSinkClass *bclass;
+ GstBaseSink *bsink;
+ GstCaps *caps = NULL;
+
+ bsink = GST_BASESINK (GST_PAD_PARENT (pad));
+ bclass = GST_BASESINK_GET_CLASS (bsink);
+ if (bclass->get_caps)
+ caps = bclass->get_caps (bsink);
+
+ if (caps == NULL) {
+ GstStaticPadTemplate *stemplate;
+ GstPadTemplate *template;
+
+ stemplate = gst_basesink_get_template (bsink);
+ template = gst_static_pad_template_get (stemplate);
+ caps = gst_caps_copy (gst_pad_template_get_caps (template));
+ }
+ return caps;
+}
+
+static gboolean
+gst_basesink_pad_setcaps (GstPad * pad, GstCaps * caps)
+{
+ GstBaseSinkClass *bclass;
+ GstBaseSink *bsink;
+ gboolean res = FALSE;
+
+ bsink = GST_BASESINK (GST_PAD_PARENT (pad));
+ bclass = GST_BASESINK_GET_CLASS (bsink);
+ if (bclass->set_caps)
+ res = bclass->set_caps (bsink, caps);
+
+ return res;
+}
+
+static GstBuffer *
+gst_basesink_pad_buffer_alloc (GstPad * pad, guint64 offset, guint size,
+ GstCaps * caps)
+{
+ GstBaseSinkClass *bclass;
+ GstBaseSink *bsink;
+ GstBuffer *buffer = NULL;
+
+ bsink = GST_BASESINK (GST_PAD_PARENT (pad));
+ bclass = GST_BASESINK_GET_CLASS (bsink);
+ if (bclass->buffer_alloc)
+ buffer = bclass->buffer_alloc (bsink, offset, size, caps);
+
+ return buffer;
+}
+
+static void
+gst_basesink_init (GstBaseSink * basesink)
+{
+ GstStaticPadTemplate *template;
+
+ template = gst_basesink_get_template (basesink);
+
+ basesink->sinkpad =
+ gst_pad_new_from_template (gst_static_pad_template_get (template),
+ "sink");
+ gst_pad_set_getcaps_function (basesink->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_basesink_pad_getcaps));
+ gst_pad_set_setcaps_function (basesink->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_basesink_pad_setcaps));
+ gst_pad_set_bufferalloc_function (basesink->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_basesink_pad_buffer_alloc));
+ gst_element_add_pad (GST_ELEMENT (basesink), basesink->sinkpad);
+
+ basesink->pad_mode = GST_ACTIVATE_NONE;
+ GST_RPAD_TASK (basesink->sinkpad) = NULL;
+}
+
+static void
+gst_basesink_set_pad_functions (GstBaseSink * this, GstPad * pad)
+{
+ gst_pad_set_activate_function (pad,
+ GST_DEBUG_FUNCPTR (gst_basesink_activate));
+ gst_pad_set_event_function (pad, GST_DEBUG_FUNCPTR (gst_basesink_event));
+
+ if (this->has_chain)
+ gst_pad_set_chain_function (pad, GST_DEBUG_FUNCPTR (gst_basesink_chain));
+ else
+ gst_pad_set_chain_function (pad, NULL);
+
+ if (this->has_loop)
+ gst_pad_set_loop_function (pad, GST_DEBUG_FUNCPTR (gst_basesink_loop));
+ else
+ gst_pad_set_loop_function (pad, NULL);
+}
+
+static void
+gst_basesink_set_all_pad_functions (GstBaseSink * this)
+{
+ GList *l;
+
+ for (l = GST_ELEMENT_PADS (this); l; l = l->next)
+ gst_basesink_set_pad_functions (this, (GstPad *) l->data);
+}
+
+static void
+gst_basesink_set_clock (GstElement * element, GstClock * clock)
+{
+ GstBaseSink *sink;
+
+ sink = GST_BASESINK (element);
+
+ sink->clock = clock;
+}
+
+static void
+gst_basesink_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstBaseSink *sink;
+
+ sink = GST_BASESINK (object);
+
+ GST_LOCK (sink);
+ switch (prop_id) {
+ case PROP_HAS_LOOP:
+ sink->has_loop = g_value_get_boolean (value);
+ gst_basesink_set_all_pad_functions (sink);
+ break;
+ case PROP_HAS_CHAIN:
+ sink->has_chain = g_value_get_boolean (value);
+ gst_basesink_set_all_pad_functions (sink);
+ break;
+ case PROP_PREROLL_QUEUE_LEN:
+ /* preroll lock necessary to serialize with finish_preroll */
+ GST_PREROLL_LOCK (sink->sinkpad);
+ sink->preroll_queue_max_len = g_value_get_uint (value);
+ GST_PREROLL_UNLOCK (sink->sinkpad);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+ GST_UNLOCK (sink);
+}
+
+static void
+gst_basesink_get_property (GObject * object, guint prop_id, GValue * value,
+ GParamSpec * pspec)
+{
+ GstBaseSink *sink;
+
+ sink = GST_BASESINK (object);
+
+ GST_LOCK (sink);
+ switch (prop_id) {
+ case PROP_HAS_LOOP:
+ g_value_set_boolean (value, sink->has_loop);
+ break;
+ case PROP_HAS_CHAIN:
+ g_value_set_boolean (value, sink->has_chain);
+ break;
+ case PROP_PREROLL_QUEUE_LEN:
+ g_value_set_uint (value, sink->preroll_queue_max_len);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+ GST_UNLOCK (sink);
+}
+
+static GstStaticPadTemplate *
+gst_base_sink_get_template (GstBaseSink * sink)
+{
+ return &sinktemplate;
+}
+
+static GstCaps *
+gst_base_sink_get_caps (GstBaseSink * sink)
+{
+ return NULL;
+}
+
+static gboolean
+gst_base_sink_set_caps (GstBaseSink * sink, GstCaps * caps)
+{
+ return TRUE;
+}
+
+static GstBuffer *
+gst_base_sink_buffer_alloc (GstBaseSink * sink, guint64 offset, guint size,
+ GstCaps * caps)
+{
+ return NULL;
+}
+
+/* with PREROLL_LOCK */
+static void
+gst_basesink_preroll_queue_push (GstBaseSink * basesink, GstPad * pad,
+ GstBuffer * buffer)
+{
+ if (basesink->preroll_queue->length == 0) {
+ GstBaseSinkClass *bclass = GST_BASESINK_GET_CLASS (basesink);
+
+ if (bclass->preroll)
+ bclass->preroll (basesink, buffer);
+ }
+
+ if (basesink->preroll_queue->length < basesink->preroll_queue_max_len) {
+ DEBUG ("push %p %p\n", basesink, buffer);
+ g_queue_push_tail (basesink->preroll_queue, buffer);
+ } else {
+ /* block until the state changes, or we get a flush, or something */
+ DEBUG ("block %p %p\n", basesink, buffer);
+ GST_DEBUG ("element %s waiting to finish preroll",
+ GST_ELEMENT_NAME (basesink));
+ basesink->need_preroll = FALSE;
+ basesink->have_preroll = TRUE;
+ GST_PREROLL_WAIT (pad);
+ GST_DEBUG ("done preroll");
+ basesink->have_preroll = FALSE;
+ }
+}
+
+/* with PREROLL_LOCK */
+static void
+gst_basesink_preroll_queue_empty (GstBaseSink * basesink, GstPad * pad)
+{
+ GstBuffer *buf;
+ GQueue *q = basesink->preroll_queue;
+
+ if (q) {
+ DEBUG ("empty queue\n");
+ while ((buf = g_queue_pop_head (q))) {
+ DEBUG ("pop %p\n", buf);
+ gst_basesink_handle_buffer (basesink, buf);
+ }
+ DEBUG ("queue len %p %d\n", basesink, q->length);
+ }
+}
+
+/* with PREROLL_LOCK */
+static void
+gst_basesink_preroll_queue_flush (GstBaseSink * basesink)
+{
+ GstBuffer *buf;
+ GQueue *q = basesink->preroll_queue;
+
+ DEBUG ("flush %p\n", basesink);
+ if (q) {
+ while ((buf = g_queue_pop_head (q))) {
+ DEBUG ("pop %p\n", buf);
+ gst_buffer_unref (buf);
+ }
+ }
+}
+
+typedef enum
+{
+ PREROLL_QUEUEING,
+ PREROLL_PLAYING,
+ PREROLL_FLUSHING,
+ PREROLL_ERROR
+} PrerollReturn;
+
+/* with STREAM_LOCK */
+PrerollReturn
+gst_basesink_finish_preroll (GstBaseSink * basesink, GstPad * pad,
+ GstBuffer * buffer)
+{
+ gboolean usable;
+
+ DEBUG ("finish preroll %p <\n", basesink);
+ /* lock order is important */
+ GST_STATE_LOCK (basesink);
+ GST_PREROLL_LOCK (pad);
+ DEBUG ("finish preroll %p >\n", basesink);
+ if (!basesink->need_preroll)
+ goto no_preroll;
+
+ gst_element_commit_state (GST_ELEMENT (basesink));
+ GST_STATE_UNLOCK (basesink);
+
+ gst_basesink_preroll_queue_push (basesink, pad, buffer);
+
+ GST_LOCK (pad);
+ usable = !GST_RPAD_IS_FLUSHING (pad) && GST_RPAD_IS_ACTIVE (pad);
+ GST_UNLOCK (pad);
+ if (!usable)
+ goto unusable;
+
+ if (basesink->need_preroll)
+ goto still_queueing;
+
+ GST_DEBUG ("done preroll");
+
+ gst_basesink_preroll_queue_empty (basesink, pad);
+
+ GST_PREROLL_UNLOCK (pad);
+
+ return PREROLL_PLAYING;
+
+no_preroll:
+ {
+ /* maybe it was another sink that blocked in preroll, need to check for
+ buffers to drain */
+ if (basesink->preroll_queue->length)
+ gst_basesink_preroll_queue_empty (basesink, pad);
+ GST_PREROLL_UNLOCK (pad);
+ GST_STATE_UNLOCK (basesink);
+ return PREROLL_PLAYING;
+ }
+unusable:
+ {
+ GST_DEBUG ("pad is flushing");
+ GST_PREROLL_UNLOCK (pad);
+ return PREROLL_FLUSHING;
+ }
+still_queueing:
+ {
+ GST_PREROLL_UNLOCK (pad);
+ return PREROLL_QUEUEING;
+ }
+}
+
+static gboolean
+gst_basesink_event (GstPad * pad, GstEvent * event)
+{
+ GstBaseSink *basesink;
+ gboolean result = TRUE;
+ GstBaseSinkClass *bclass;
+
+ basesink = GST_BASESINK (GST_OBJECT_PARENT (pad));
+
+ bclass = GST_BASESINK_GET_CLASS (basesink);
+
+ DEBUG ("event %p\n", basesink);
+
+ if (bclass->event)
+ bclass->event (basesink, event);
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_EOS:
+ {
+ gboolean need_eos;
+
+ GST_STREAM_LOCK (pad);
+
+ GST_PREROLL_LOCK (pad);
+ gst_basesink_preroll_queue_empty (basesink, pad);
+ GST_PREROLL_UNLOCK (pad);
+
+ GST_LOCK (basesink);
+ need_eos = basesink->eos = TRUE;
+ if (basesink->clock) {
+ /* wait for last buffer to finish if we have a valid end time */
+ if (GST_CLOCK_TIME_IS_VALID (basesink->end_time)) {
+ basesink->clock_id = gst_clock_new_single_shot_id (basesink->clock,
+ basesink->end_time + GST_ELEMENT (basesink)->base_time);
+ GST_UNLOCK (basesink);
+
+ gst_clock_id_wait (basesink->clock_id, NULL);
+
+ GST_LOCK (basesink);
+ if (basesink->clock_id) {
+ gst_clock_id_unref (basesink->clock_id);
+ basesink->clock_id = NULL;
+ }
+ basesink->end_time = GST_CLOCK_TIME_NONE;
+ need_eos = basesink->eos;
+ }
+ GST_UNLOCK (basesink);
+
+ /* if we are still EOS, we can post the EOS message */
+ if (need_eos) {
+ /* ok, now we can post the message */
+ gst_element_post_message (GST_ELEMENT (basesink),
+ gst_message_new_eos (GST_OBJECT (basesink)));
+ }
+ }
+ GST_STREAM_UNLOCK (pad);
+ break;
+ }
+ case GST_EVENT_DISCONTINUOUS:
+ GST_STREAM_LOCK (pad);
+ if (basesink->clock) {
+ //gint64 value = GST_EVENT_DISCONT_OFFSET (event, 0).value;
+ }
+ GST_STREAM_UNLOCK (pad);
+ break;
+ case GST_EVENT_FLUSH:
+ /* make sure we are not blocked on the clock also clear any pending
+ * eos state. */
+ if (!GST_EVENT_FLUSH_DONE (event)) {
+ GST_LOCK (basesink);
+ basesink->eos = FALSE;
+ if (basesink->clock_id) {
+ gst_clock_id_unschedule (basesink->clock_id);
+ }
+ GST_UNLOCK (basesink);
+
+ /* unlock from a possible state change/preroll */
+ GST_PREROLL_LOCK (pad);
+ basesink->need_preroll = TRUE;
+ gst_basesink_preroll_queue_flush (basesink);
+ GST_PREROLL_SIGNAL (pad);
+ GST_PREROLL_UNLOCK (pad);
+ }
+ /* now we are completely unblocked and the _chain method
+ * will return */
+ break;
+ default:
+ result = gst_pad_event_default (pad, event);
+ break;
+ }
+
+ return result;
+}
+
+static void
+gst_basesink_get_times (GstBaseSink * basesink, GstBuffer * buffer,
+ GstClockTime * start, GstClockTime * end)
+{
+ GstClockTime timestamp, duration;
+
+ timestamp = GST_BUFFER_TIMESTAMP (buffer);
+ if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
+ duration = GST_BUFFER_DURATION (buffer);
+ if (GST_CLOCK_TIME_IS_VALID (duration)) {
+ *end = timestamp + duration;
+ }
+ *start = timestamp;
+ }
+}
+
+static void
+gst_basesink_do_sync (GstBaseSink * basesink, GstBuffer * buffer)
+{
+ if (basesink->clock) {
+ GstClockReturn ret;
+ GstClockTime start, end;
+ GstBaseSinkClass *bclass;
+
+ bclass = GST_BASESINK_GET_CLASS (basesink);
+ start = end = -1;
+ if (bclass->get_times)
+ bclass->get_times (basesink, buffer, &start, &end);
+
+ if (GST_CLOCK_TIME_IS_VALID (start)) {
+ /* save clock id so that we can unlock it if needed */
+ GST_LOCK (basesink);
+ basesink->clock_id = gst_clock_new_single_shot_id (basesink->clock,
+ start + GST_ELEMENT (basesink)->base_time);
+ basesink->end_time = end;
+ GST_UNLOCK (basesink);
+
+ ret = gst_clock_id_wait (basesink->clock_id, NULL);
+
+ GST_LOCK (basesink);
+ if (basesink->clock_id) {
+ gst_clock_id_unref (basesink->clock_id);
+ basesink->clock_id = NULL;
+ }
+ basesink->end_time = GST_CLOCK_TIME_NONE;
+ GST_UNLOCK (basesink);
+
+ GST_LOG_OBJECT (basesink, "clock entry done: %d", ret);
+ }
+ }
+}
+
+static inline void
+gst_basesink_handle_buffer (GstBaseSink * basesink, GstBuffer * buf)
+{
+ GstBaseSinkClass *bclass;
+
+ gst_basesink_do_sync (basesink, buf);
+
+ bclass = GST_BASESINK_GET_CLASS (basesink);
+ if (bclass->render)
+ bclass->render (basesink, buf);
+
+ DEBUG ("unref %p %p\n", basesink, buf);
+ gst_buffer_unref (buf);
+}
+
+static GstFlowReturn
+gst_basesink_chain_unlocked (GstPad * pad, GstBuffer * buf)
+{
+ GstBaseSink *basesink;
+ PrerollReturn result;
+
+ basesink = GST_BASESINK (GST_OBJECT_PARENT (pad));
+
+ DEBUG ("chain_unlocked %p\n", basesink);
+
+ result = gst_basesink_finish_preroll (basesink, pad, buf);
+
+ DEBUG ("chain_unlocked %p after\n", basesink);
+
+ switch (result) {
+ case PREROLL_QUEUEING:
+ return GST_FLOW_OK;
+ case PREROLL_PLAYING:
+ gst_basesink_handle_buffer (basesink, buf);
+ return GST_FLOW_OK;
+ case PREROLL_FLUSHING:
+ return GST_FLOW_UNEXPECTED;
+ default:
+ g_assert_not_reached ();
+ return GST_FLOW_UNEXPECTED;
+ }
+}
+
+static GstFlowReturn
+gst_basesink_chain (GstPad * pad, GstBuffer * buf)
+{
+ GstFlowReturn result;
+
+ g_assert (GST_BASESINK (GST_OBJECT_PARENT (pad))->pad_mode ==
+ GST_ACTIVATE_PUSH);
+
+ GST_STREAM_LOCK (pad);
+
+ result = gst_basesink_chain_unlocked (pad, buf);
+
+ GST_STREAM_UNLOCK (pad);
+
+ return result;
+}
+
+static void
+gst_basesink_loop (GstPad * pad)
+{
+ GstBaseSink *basesink;
+ GstBuffer *buf = NULL;
+ GstFlowReturn result;
+
+ basesink = GST_BASESINK (GST_OBJECT_PARENT (pad));
+
+ g_assert (basesink->pad_mode == GST_ACTIVATE_PULL);
+
+ GST_STREAM_LOCK (pad);
+
+ result = gst_pad_pull_range (pad, basesink->offset, DEFAULT_SIZE, &buf);
+ if (result != GST_FLOW_OK)
+ goto paused;
+
+ result = gst_basesink_chain_unlocked (pad, buf);
+ if (result != GST_FLOW_OK)
+ goto paused;
+
+ /* default */
+ GST_STREAM_UNLOCK (pad);
+ return;
+
+paused:
+ gst_task_pause (GST_RPAD_TASK (pad));
+ GST_STREAM_UNLOCK (pad);
+ return;
+}
+
+static gboolean
+gst_basesink_activate (GstPad * pad, GstActivateMode mode)
+{
+ gboolean result = FALSE;
+ GstBaseSink *basesink;
+
+ basesink = GST_BASESINK (GST_OBJECT_PARENT (pad));
+
+ switch (mode) {
+ case GST_ACTIVATE_PUSH:
+ g_return_val_if_fail (basesink->has_chain, FALSE);
+ result = TRUE;
+ break;
+ case GST_ACTIVATE_PULL:
+ /* if we have a scheduler we can start the task */
+ g_return_val_if_fail (basesink->has_loop, FALSE);
+ if (GST_ELEMENT_SCHEDULER (basesink)) {
+ GST_STREAM_LOCK (pad);
+ GST_RPAD_TASK (pad) =
+ gst_scheduler_create_task (GST_ELEMENT_SCHEDULER (basesink),
+ (GstTaskFunction) gst_basesink_loop, pad);
+
+ gst_task_start (GST_RPAD_TASK (pad));
+ GST_STREAM_UNLOCK (pad);
+ result = TRUE;
+ }
+ break;
+ case GST_ACTIVATE_NONE:
+ /* step 1, unblock clock sync (if any) or any other blocking thing */
+ GST_LOCK (basesink);
+ if (basesink->clock_id) {
+ gst_clock_id_unschedule (basesink->clock_id);
+ }
+ GST_UNLOCK (basesink);
+
+ /* unlock preroll */
+ GST_PREROLL_LOCK (pad);
+ GST_PREROLL_SIGNAL (pad);
+ GST_PREROLL_UNLOCK (pad);
+
+ /* step 2, make sure streaming finishes */
+ GST_STREAM_LOCK (pad);
+ /* step 3, stop the task */
+ if (GST_RPAD_TASK (pad)) {
+ gst_task_stop (GST_RPAD_TASK (pad));
+ gst_object_unref (GST_OBJECT (GST_RPAD_TASK (pad)));
+ GST_RPAD_TASK (pad) = NULL;
+ }
+ GST_STREAM_UNLOCK (pad);
+
+ result = TRUE;
+ break;
+ }
+ basesink->pad_mode = mode;
+
+ return result;
+}
+
+static GstElementStateReturn
+gst_basesink_change_state (GstElement * element)
+{
+ GstElementStateReturn ret = GST_STATE_SUCCESS;
+ GstBaseSink *basesink = GST_BASESINK (element);
+ GstElementState transition = GST_STATE_TRANSITION (element);
+
+ DEBUG ("state change > %p %x\n", basesink, transition);
+
+ switch (transition) {
+ case GST_STATE_NULL_TO_READY:
+ break;
+ case GST_STATE_READY_TO_PAUSED:
+ /* need to complete preroll before this state change completes, there
+ * is no data flow in READY so we cqn safely assume we need to preroll. */
+ basesink->offset = 0;
+ GST_PREROLL_LOCK (basesink->sinkpad);
+ basesink->preroll_queue = g_queue_new ();
+ basesink->need_preroll = TRUE;
+ basesink->have_preroll = FALSE;
+ GST_PREROLL_UNLOCK (basesink->sinkpad);
+ ret = GST_STATE_ASYNC;
+ break;
+ case GST_STATE_PAUSED_TO_PLAYING:
+ GST_PREROLL_LOCK (basesink->sinkpad);
+ if (basesink->have_preroll) {
+ /* now let it play */
+ GST_PREROLL_SIGNAL (basesink->sinkpad);
+ } else {
+ /* FIXME. We do not have a preroll and we don't need it anymore
+ * now, this is a case we want to avoid. One way would be to make
+ * a 'lost state' function that makes get_state return PAUSED with
+ * ASYNC to indicate that we are prerolling again. */
+ basesink->need_preroll = FALSE;
+ }
+ GST_PREROLL_UNLOCK (basesink->sinkpad);
+ break;
+ default:
+ break;
+ }
+
+ GST_ELEMENT_CLASS (parent_class)->change_state (element);
+
+ switch (transition) {
+ case GST_STATE_PLAYING_TO_PAUSED:
+ {
+ gboolean eos;
+
+ /* unlock clock wait if any */
+ GST_LOCK (basesink);
+ if (basesink->clock_id) {
+ gst_clock_id_unschedule (basesink->clock_id);
+ }
+ eos = basesink->eos;
+ GST_UNLOCK (basesink);
+
+ GST_PREROLL_LOCK (basesink->sinkpad);
+ /* if we don't have a preroll buffer and we have not received EOS,
+ * we need to wait for a preroll */
+ if (!basesink->have_preroll && !eos) {
+ basesink->need_preroll = TRUE;
+ ret = GST_STATE_ASYNC;
+ }
+ GST_PREROLL_UNLOCK (basesink->sinkpad);
+ break;
+ }
+ case GST_STATE_PAUSED_TO_READY:
+ /* flush out the data thread if it's locked in finish_preroll */
+ GST_PREROLL_LOCK (basesink->sinkpad);
+
+ gst_basesink_preroll_queue_flush (basesink);
+ g_queue_free (basesink->preroll_queue);
+ basesink->preroll_queue = NULL;
+
+ if (basesink->have_preroll)
+ GST_PREROLL_SIGNAL (basesink->sinkpad);
+
+ basesink->need_preroll = FALSE;
+ basesink->have_preroll = FALSE;
+ GST_PREROLL_UNLOCK (basesink->sinkpad);
+
+ /* make sure the element is finished processing */
+ GST_STREAM_LOCK (basesink->sinkpad);
+ GST_STREAM_UNLOCK (basesink->sinkpad);
+ break;
+ case GST_STATE_READY_TO_NULL:
+ break;
+ default:
+ break;
+ }
+
+ DEBUG ("state change < %p %x\n", basesink, transition);
+ return ret;
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2000 Wim Taymans <wtay@chello.be>
+ *
+ * gstbasesink.h:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_BASESINK_H__
+#define __GST_BASESINK_H__
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+
+#define GST_TYPE_BASESINK (gst_basesink_get_type())
+#define GST_BASESINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_BASESINK,GstBaseSink))
+#define GST_BASESINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_BASESINK,GstBaseSinkClass))
+#define GST_BASESINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_BASESINK, GstBaseSinkClass))
+#define GST_IS_BASESINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_BASESINK))
+#define GST_IS_BASESINK_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_BASESINK))
+
+#define GST_BASESINK_CLOCK(obj) (GST_BASESINK (obj)->clock)
+#define GST_BASESINK_PAD(obj) (GST_BASESINK (obj)->sinkpad)
+
+typedef struct _GstBaseSink GstBaseSink;
+typedef struct _GstBaseSinkClass GstBaseSinkClass;
+
+struct _GstBaseSink {
+ GstElement element;
+
+ GstPad *sinkpad;
+ GstActivateMode pad_mode;
+
+ GQueue *preroll_queue; /* with PREROLL_LOCK */
+ gint preroll_queue_max_len; /* with PREROLL_LOCK */
+
+ guint64 offset;
+ gboolean has_loop;
+ gboolean has_chain;
+
+ GstClock *clock;
+ GstClockID clock_id;
+ GstClockTime end_time;
+
+ gboolean eos;
+ gboolean need_preroll;
+ gboolean have_preroll;
+};
+
+struct _GstBaseSinkClass {
+ GstElementClass parent_class;
+
+ GstStaticPadTemplate* (*get_template) (GstBaseSink *sink);
+
+ GstCaps* (*get_caps) (GstBaseSink *sink);
+ gboolean (*set_caps) (GstBaseSink *sink, GstCaps *caps);
+
+ GstBuffer* (*buffer_alloc) (GstBaseSink *sink, guint64 offset, guint size,
+ GstCaps *caps);
+
+ void (*get_times) (GstBaseSink *sink, GstBuffer *buffer,
+ GstClockTime *start, GstClockTime *end);
+
+ void (*event) (GstBaseSink *sink, GstEvent *event);
+ GstFlowReturn (*preroll) (GstBaseSink *sink, GstBuffer *buffer);
+ GstFlowReturn (*render) (GstBaseSink *sink, GstBuffer *buffer);
+};
+
+GType gst_basesink_get_type(void);
+
+G_END_DECLS
+
+#endif /* __GST_BASESINK_H__ */
libgstelements_la_CFLAGS = $(GST_OBJ_CFLAGS)
libgstelements_la_LIBADD = $(GST_OBJ_LIBS)
-libgstelements_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(as_libtool_LDFLAGS)
+libgstelements_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(as_libtool_LDFLAGS) $(top_builddir)/gst/base/libgstbase.la
noinst_HEADERS = \
gstaggregator.h \
/* GStreamer
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
- * 2000 Wim Taymans <wtay@chello.be>
+ * 2005 Wim Taymans <wim@fluendo.com>
*
* gstfakesink.c:
*
GstElementDetails gst_fakesink_details = GST_ELEMENT_DETAILS ("Fake Sink",
"Sink",
"Black hole for data",
- "Erik Walthinsen <omega@cse.ogi.edu>");
+ "Erik Walthinsen <omega@cse.ogi.edu>, "
+ "Wim Taymans <wim@fluendo.com>, "
+ "Mr. 'frag-me-more' Vanderwingo <wingo@fluendo.com>");
/* FakeSink signals and args */
LAST_SIGNAL
};
+#define DEFAULT_STATE_ERROR FAKESINK_STATE_ERROR_NONE
+#define DEFAULT_SILENT FALSE
+#define DEFAULT_DUMP FALSE
+#define DEFAULT_SYNC FALSE
+#define DEFAULT_SIGNAL_HANDOFFS FALSE
+#define DEFAULT_LAST_MESSAGE NULL
+
enum
{
ARG_0,
ARG_STATE_ERROR,
- ARG_NUM_SINKS,
ARG_SILENT,
ARG_DUMP,
ARG_SYNC,
ARG_SIGNAL_HANDOFFS,
- ARG_LAST_MESSAGE
+ ARG_LAST_MESSAGE,
};
-GstStaticPadTemplate fakesink_sink_template = GST_STATIC_PAD_TEMPLATE ("sink%d",
- GST_PAD_SINK,
- GST_PAD_REQUEST,
- GST_STATIC_CAPS_ANY);
-
#define GST_TYPE_FAKESINK_STATE_ERROR (gst_fakesink_state_error_get_type())
static GType
gst_fakesink_state_error_get_type (void)
#define _do_init(bla) \
GST_DEBUG_CATEGORY_INIT (gst_fakesink_debug, "fakesink", 0, "fakesink element");
-GST_BOILERPLATE_FULL (GstFakeSink, gst_fakesink, GstElement, GST_TYPE_ELEMENT,
+GST_BOILERPLATE_FULL (GstFakeSink, gst_fakesink, GstBaseSink, GST_TYPE_BASESINK,
_do_init);
-static void gst_fakesink_set_clock (GstElement * element, GstClock * clock);
-static GstPad *gst_fakesink_request_new_pad (GstElement * element,
- GstPadTemplate * templ, const gchar * unused);
-
static void gst_fakesink_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_fakesink_get_property (GObject * object, guint prop_id,
static GstElementStateReturn gst_fakesink_change_state (GstElement * element);
-static GstFlowReturn gst_fakesink_chain (GstPad * pad, GstBuffer * buffer);
-static gboolean gst_fakesink_event (GstPad * pad, GstEvent * event);
+static GstFlowReturn gst_fakesink_preroll (GstBaseSink * bsink,
+ GstBuffer * buffer);
+static GstFlowReturn gst_fakesink_render (GstBaseSink * bsink,
+ GstBuffer * buffer);
+static void gst_fakesink_event (GstBaseSink * bsink, GstEvent * event);
+static void gst_fakesink_get_times (GstBaseSink * bsink, GstBuffer * buffer,
+ GstClockTime * start, GstClockTime * end);
static guint gst_fakesink_signals[LAST_SIGNAL] = { 0 };
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&sinktemplate));
gst_element_class_set_details (gstelement_class, &gst_fakesink_details);
- gst_element_class_add_pad_template (gstelement_class,
- gst_static_pad_template_get (&fakesink_sink_template));
}
static void
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
+ GstBaseSinkClass *gstbasesink_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
+ gstbasesink_class = (GstBaseSinkClass *) 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",
- "The number of sinkpads", 1, G_MAXINT, 1, G_PARAM_READABLE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_STATE_ERROR,
g_param_spec_enum ("state_error", "State Error",
"Generate a state change error", GST_TYPE_FAKESINK_STATE_ERROR,
- FAKESINK_STATE_ERROR_NONE, G_PARAM_READWRITE));
+ DEFAULT_STATE_ERROR, G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LAST_MESSAGE,
g_param_spec_string ("last_message", "Last Message",
- "The message describing current status", NULL, G_PARAM_READABLE));
+ "The message describing current status", DEFAULT_LAST_MESSAGE,
+ G_PARAM_READABLE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SYNC,
- g_param_spec_boolean ("sync", "Sync", "Sync on the clock", FALSE,
+ g_param_spec_boolean ("sync", "Sync", "Sync on the clock", DEFAULT_SYNC,
G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SIGNAL_HANDOFFS,
g_param_spec_boolean ("signal-handoffs", "Signal handoffs",
- "Send a signal before unreffing the buffer", FALSE,
+ "Send a signal before unreffing the buffer", DEFAULT_SIGNAL_HANDOFFS,
G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SILENT,
g_param_spec_boolean ("silent", "Silent",
- "Don't produce last_message events", FALSE, G_PARAM_READWRITE));
+ "Don't produce last_message events", DEFAULT_SILENT,
+ G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DUMP,
g_param_spec_boolean ("dump", "Dump", "Dump received bytes to stdout",
- FALSE, G_PARAM_READWRITE));
+ DEFAULT_DUMP, G_PARAM_READWRITE));
gst_fakesink_signals[SIGNAL_HANDOFF] =
g_signal_new ("handoff", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
gst_marshal_VOID__BOXED_OBJECT, G_TYPE_NONE, 2,
GST_TYPE_BUFFER | G_SIGNAL_TYPE_STATIC_SCOPE, GST_TYPE_PAD);
- gstelement_class->request_new_pad =
- GST_DEBUG_FUNCPTR (gst_fakesink_request_new_pad);
- gstelement_class->set_clock = GST_DEBUG_FUNCPTR (gst_fakesink_set_clock);
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_fakesink_change_state);
+
+ gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_fakesink_event);
+ gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_fakesink_preroll);
+ gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_fakesink_render);
+ gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_fakesink_get_times);
}
static void
gst_fakesink_init (GstFakeSink * fakesink)
{
- GstPad *pad;
-
- pad =
- gst_pad_new_from_template (gst_static_pad_template_get (&sinktemplate),
- "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;
- fakesink->sync = FALSE;
- fakesink->last_message = NULL;
- fakesink->state_error = FAKESINK_STATE_ERROR_NONE;
- fakesink->signal_handoffs = FALSE;
-}
-
-static void
-gst_fakesink_set_clock (GstElement * element, GstClock * clock)
-{
- GstFakeSink *sink;
-
- sink = GST_FAKESINK (element);
-
- sink->clock = clock;
-}
-
-static GstPad *
-gst_fakesink_request_new_pad (GstElement * element, GstPadTemplate * templ,
- const gchar * unused)
-{
- gchar *name;
- GstPad *sinkpad;
- GstFakeSink *fakesink;
-
- g_return_val_if_fail (GST_IS_FAKESINK (element), NULL);
-
- if (templ->direction != GST_PAD_SINK) {
- g_warning ("gstfakesink: request new pad that is not a SINK pad\n");
- return NULL;
- }
-
- fakesink = GST_FAKESINK (element);
-
- name = g_strdup_printf ("sink%d", GST_ELEMENT (fakesink)->numsinkpads);
-
- sinkpad = gst_pad_new_from_template (templ, name);
- g_free (name);
- gst_pad_set_chain_function (sinkpad, GST_DEBUG_FUNCPTR (gst_fakesink_chain));
-
- gst_element_add_pad (GST_ELEMENT (fakesink), sinkpad);
-
- return sinkpad;
+ fakesink->silent = DEFAULT_SILENT;
+ fakesink->dump = DEFAULT_DUMP;
+ fakesink->sync = DEFAULT_SYNC;
+ fakesink->last_message = DEFAULT_LAST_MESSAGE;
+ fakesink->state_error = DEFAULT_STATE_ERROR;
+ fakesink->signal_handoffs = DEFAULT_SIGNAL_HANDOFFS;
}
static void
{
GstFakeSink *sink;
- /* it's not null if we got it, but it might not be ours */
sink = GST_FAKESINK (object);
switch (prop_id) {
{
GstFakeSink *sink;
- /* it's not null if we got it, but it might not be ours */
- g_return_if_fail (GST_IS_FAKESINK (object));
-
sink = GST_FAKESINK (object);
switch (prop_id) {
- case ARG_NUM_SINKS:
- g_value_set_int (value, GST_ELEMENT (sink)->numsinkpads);
- break;
case ARG_STATE_ERROR:
g_value_set_enum (value, sink->state_error);
break;
}
}
-static gboolean
-gst_fakesink_event (GstPad * pad, GstEvent * event)
+static void
+gst_fakesink_get_times (GstBaseSink * bsink, GstBuffer * buffer,
+ GstClockTime * start, GstClockTime * end)
{
- GstFakeSink *fakesink;
+ GstFakeSink *sink = GST_FAKESINK (bsink);
- fakesink = GST_FAKESINK (GST_OBJECT_PARENT (pad));
+ if (sink->sync) {
+ GST_BASESINK_CLASS (parent_class)->get_times (bsink, buffer, start, end);
+ }
+}
- if (!fakesink->silent) {
- g_free (fakesink->last_message);
+static void
+gst_fakesink_event (GstBaseSink * bsink, GstEvent * event)
+{
+ GstFakeSink *sink = GST_FAKESINK (bsink);
- fakesink->last_message =
- g_strdup_printf ("chain ******* (%s:%s)E (type: %d) %p",
- GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE (event), event);
+ if (!sink->silent) {
+ g_free (sink->last_message);
- g_object_notify (G_OBJECT (fakesink), "last_message");
- }
+ sink->last_message =
+ g_strdup_printf ("chain ******* E (type: %d) %p",
+ GST_EVENT_TYPE (event), event);
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_DISCONTINUOUS:
- default:
- gst_pad_event_default (pad, event);
- break;
+ g_object_notify (G_OBJECT (sink), "last_message");
}
-
- return TRUE;
}
static GstFlowReturn
-gst_fakesink_chain (GstPad * pad, GstBuffer * buffer)
+gst_fakesink_preroll (GstBaseSink * bsink, GstBuffer * buffer)
{
- GstBuffer *buf = GST_BUFFER (buffer);
- GstFakeSink *fakesink;
+ GstFakeSink *sink = GST_FAKESINK (bsink);
+
+ if (!sink->silent) {
+ g_free (sink->last_message);
- fakesink = GST_FAKESINK (GST_OBJECT_PARENT (pad));
+ sink->last_message = g_strdup_printf ("preroll ******* ");
- if (fakesink->sync && fakesink->clock) {
- //gst_element_wait (GST_ELEMENT (fakesink), GST_BUFFER_TIMESTAMP (buf));
+ g_object_notify (G_OBJECT (sink), "last_message");
}
+ return GST_FLOW_OK;
+}
- if (!fakesink->silent) {
- g_free (fakesink->last_message);
+static GstFlowReturn
+gst_fakesink_render (GstBaseSink * bsink, GstBuffer * buf)
+{
+ GstFakeSink *sink = GST_FAKESINK (bsink);
- fakesink->last_message =
- g_strdup_printf ("chain ******* (%s:%s)< (%d bytes, timestamp: %"
+ if (!sink->silent) {
+ g_free (sink->last_message);
+
+ sink->last_message =
+ g_strdup_printf ("chain ******* < (%d bytes, timestamp: %"
GST_TIME_FORMAT ", duration: %" GST_TIME_FORMAT ", offset: %"
G_GINT64_FORMAT ", offset_end: %" G_GINT64_FORMAT ", flags: %d) %p",
- GST_DEBUG_PAD_NAME (pad), GST_BUFFER_SIZE (buf),
+ GST_BUFFER_SIZE (buf),
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_BUFFER_OFFSET (buf),
GST_BUFFER_OFFSET_END (buf), GST_BUFFER_FLAGS (buf), buf);
- g_object_notify (G_OBJECT (fakesink), "last_message");
+ g_object_notify (G_OBJECT (sink), "last_message");
}
+ if (sink->signal_handoffs)
+ g_signal_emit (G_OBJECT (sink), gst_fakesink_signals[SIGNAL_HANDOFF], 0,
+ buf);
- if (fakesink->signal_handoffs)
- g_signal_emit (G_OBJECT (fakesink), gst_fakesink_signals[SIGNAL_HANDOFF], 0,
- buf, pad);
-
- if (fakesink->dump) {
+ if (sink->dump) {
gst_util_dump_mem (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
}
- gst_buffer_unref (buf);
-
return GST_FLOW_OK;
}
static GstElementStateReturn
gst_fakesink_change_state (GstElement * element)
{
+ GstElementStateReturn ret = GST_STATE_SUCCESS;
GstFakeSink *fakesink = GST_FAKESINK (element);
+ GstElementState transition = GST_STATE_TRANSITION (element);
- switch (GST_STATE_TRANSITION (element)) {
+ switch (transition) {
case GST_STATE_NULL_TO_READY:
if (fakesink->state_error == FAKESINK_STATE_ERROR_NULL_READY)
goto error;
g_free (fakesink->last_message);
fakesink->last_message = NULL;
break;
+ default:
+ break;
}
- if (GST_ELEMENT_CLASS (parent_class)->change_state)
- return GST_ELEMENT_CLASS (parent_class)->change_state (element);
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
- return GST_STATE_SUCCESS;
+ return ret;
error:
GST_ELEMENT_ERROR (element, CORE, STATE_CHANGE, (NULL), (NULL));
#define __GST_FAKESINK_H__
#include <gst/gst.h>
+#include <gst/base/gstbasesink.h>
G_BEGIN_DECLS
typedef struct _GstFakeSinkClass GstFakeSinkClass;
struct _GstFakeSink {
- GstElement element;
+ GstBaseSink element;
gboolean silent;
gboolean dump;
gboolean sync;
gboolean signal_handoffs;
- GstClock *clock;
GstFakeSinkStateError state_error;
-
gchar *last_message;
};
struct _GstFakeSinkClass {
- GstElementClass parent_class;
+ GstBaseSinkClass parent_class;
/* signals */
void (*handoff) (GstElement *element, GstBuffer *buf, GstPad *pad);