Added state change code.

Original commit message from CVS:
Added state change code.
Added/updated docs.
Added sink base class, make fakesink extend the base class.
Small cleanups in GstPipeline.
This commit is contained in:
Wim Taymans 2005-03-28 14:54:33 +00:00
parent a7abc22271
commit 326d36b8d8
26 changed files with 3601 additions and 362 deletions

View file

@ -1,3 +1,54 @@
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

View file

@ -655,7 +655,7 @@ include/Makefile
gst/Makefile
gst/gstconfig.h
gst/gstversion.h
gst/autoplug/Makefile
gst/base/Makefile
gst/indexers/Makefile
gst/elements/Makefile
gst/parse/Makefile
@ -663,7 +663,6 @@ gst/schedulers/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
@ -679,7 +678,6 @@ tests/sched/Makefile
tests/threadstate/Makefile
testsuite/Makefile
testsuite/bins/Makefile
testsuite/bytestream/Makefile
testsuite/caps/Makefile
testsuite/cleanup/Makefile
testsuite/clock/Makefile
@ -690,7 +688,6 @@ testsuite/elements/Makefile
testsuite/ghostpads/Makefile
testsuite/indexers/Makefile
testsuite/negotiation/Makefile
testsuite/pad/Makefile
testsuite/parse/Makefile
testsuite/plugin/Makefile
testsuite/refcounting/Makefile

View file

@ -28,18 +28,14 @@ Element. This allows deeply nested pipelines, and the possibility of
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 *.
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)
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.
This uniqueness is guaranteed through all functions where either parentage
or name of an element is changed.
Pads

View file

@ -0,0 +1,152 @@
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.

View file

@ -0,0 +1,49 @@
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.

View file

@ -0,0 +1,201 @@
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.

151
docs/design/part-states.txt Normal file
View file

@ -0,0 +1,151 @@
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
-----

View file

@ -62,8 +62,8 @@ else
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

23
gst/base/Makefile.am Normal file
View file

@ -0,0 +1,23 @@
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

16
gst/base/README Normal file
View file

@ -0,0 +1,16 @@
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...

873
gst/base/gstbasesink.c Normal file
View file

@ -0,0 +1,873 @@
/* 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;
}

89
gst/base/gstbasesink.h Normal file
View file

@ -0,0 +1,89 @@
/* 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__ */

View file

@ -45,7 +45,7 @@ libgstelements_la_SOURCES = \
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 \

View file

@ -1,6 +1,6 @@
/* 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:
*
@ -39,7 +39,9 @@ GST_DEBUG_CATEGORY_STATIC (gst_fakesink_debug);
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 */
@ -50,23 +52,24 @@ enum
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)
@ -99,13 +102,9 @@ 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,
@ -113,8 +112,13 @@ 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 };
@ -126,8 +130,6 @@ gst_fakesink_base_init (gpointer g_class)
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
@ -135,36 +137,37 @@ gst_fakesink_class_init (GstFakeSinkClass * klass)
{
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,
@ -172,69 +175,24 @@ gst_fakesink_class_init (GstFakeSinkClass * klass)
gst_marshal_VOID__BOXED_OBJECT, G_TYPE_NONE, 2,
GST_TYPE_BUFFER | G_SIGNAL_TYPE_STATIC_SCOPE, GST_TYPE_PAD);
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
@ -243,7 +201,6 @@ gst_fakesink_set_property (GObject * object, guint prop_id,
{
GstFakeSink *sink;
/* it's not null if we got it, but it might not be ours */
sink = GST_FAKESINK (object);
switch (prop_id) {
@ -274,15 +231,9 @@ gst_fakesink_get_property (GObject * object, guint prop_id, GValue * value,
{
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;
@ -307,79 +258,86 @@ gst_fakesink_get_property (GObject * object, guint prop_id, GValue * value,
}
}
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 (!fakesink->silent) {
g_free (fakesink->last_message);
fakesink->last_message =
g_strdup_printf ("chain ******* (%s:%s)E (type: %d) %p",
GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE (event), event);
g_object_notify (G_OBJECT (fakesink), "last_message");
if (sink->sync) {
GST_BASESINK_CLASS (parent_class)->get_times (bsink, buffer, start, end);
}
}
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_DISCONTINUOUS:
default:
gst_pad_event_default (pad, event);
break;
static void
gst_fakesink_event (GstBaseSink * bsink, GstEvent * event)
{
GstFakeSink *sink = GST_FAKESINK (bsink);
if (!sink->silent) {
g_free (sink->last_message);
sink->last_message =
g_strdup_printf ("chain ******* E (type: %d) %p",
GST_EVENT_TYPE (event), event);
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);
fakesink = GST_FAKESINK (GST_OBJECT_PARENT (pad));
if (!sink->silent) {
g_free (sink->last_message);
if (fakesink->sync && fakesink->clock) {
//gst_element_wait (GST_ELEMENT (fakesink), GST_BUFFER_TIMESTAMP (buf));
sink->last_message = g_strdup_printf ("preroll ******* ");
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;
@ -406,12 +364,13 @@ gst_fakesink_change_state (GstElement * element)
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));

View file

@ -25,6 +25,7 @@
#define __GST_FAKESINK_H__
#include <gst/gst.h>
#include <gst/base/gstbasesink.h>
G_BEGIN_DECLS
@ -54,20 +55,18 @@ typedef struct _GstFakeSink GstFakeSink;
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);

View file

@ -61,18 +61,17 @@ static GstElementStateReturn gst_bin_get_state (GstElement * element,
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);
@ -169,8 +168,6 @@ gst_bin_class_init (GstBinClass * klass)
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
@ -178,9 +175,12 @@ gst_bin_class_init (GstBinClass * klass)
#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);
}
@ -323,6 +323,27 @@ gst_bin_set_scheduler (GstElement * element, GstScheduler * sched)
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
@ -747,8 +768,73 @@ static GstElementStateReturn
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
@ -768,8 +854,178 @@ gst_bin_get_state (GstElement * element, GstElementState * state,
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
@ -792,53 +1048,47 @@ gst_bin_dispose (GObject * object)
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);

View file

@ -508,6 +508,13 @@ gst_element_add_pad (GstElement * element, GstPad * pad)
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);
@ -1603,8 +1610,66 @@ gst_element_get_state_func (GstElement * element,
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;
}
@ -1665,7 +1730,24 @@ gst_element_get_state (GstElement * element,
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);
}
}
/**
@ -1682,7 +1764,31 @@ gst_element_abort_state (GstElement * 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);
}
}
/**
@ -1705,7 +1811,18 @@ gst_element_commit_state (GstElement * 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;
}
}
/**
@ -1724,8 +1841,116 @@ gst_element_lost_state (GstElement * element)
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
@ -1739,7 +1964,92 @@ gst_element_set_state (GstElement * element, GstElementState state)
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 */

View file

@ -26,7 +26,6 @@
#include "gstmarshal.h"
#include "gstutils.h"
#include "gstelement.h"
#include "gstpipeline.h"
#include "gstbin.h"
#include "gstscheduler.h"
#include "gstevent.h"
@ -449,10 +448,101 @@ lost_ghostpad:
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:
@ -3382,7 +3472,6 @@ done:
*
* Returns: TRUE if the event was sent succesfully.
*/
gboolean
gst_pad_event_default (GstPad * pad, GstEvent * event)
{
@ -3390,17 +3479,8 @@ 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)) {

View file

@ -277,16 +277,21 @@ is_eos (GstPipeline * pipeline)
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);
@ -358,8 +363,87 @@ gst_pipeline_new (const gchar * name)
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;
}
/**

23
libs/gst/base/Makefile.am Normal file
View file

@ -0,0 +1,23 @@
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

16
libs/gst/base/README Normal file
View file

@ -0,0 +1,16 @@
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...

873
libs/gst/base/gstbasesink.c Normal file
View file

@ -0,0 +1,873 @@
/* 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;
}

View file

@ -0,0 +1,89 @@
/* 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__ */

View file

@ -45,7 +45,7 @@ libgstelements_la_SOURCES = \
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 \

View file

@ -1,6 +1,6 @@
/* 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:
*
@ -39,7 +39,9 @@ GST_DEBUG_CATEGORY_STATIC (gst_fakesink_debug);
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 */
@ -50,23 +52,24 @@ enum
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)
@ -99,13 +102,9 @@ 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,
@ -113,8 +112,13 @@ 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 };
@ -126,8 +130,6 @@ gst_fakesink_base_init (gpointer g_class)
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
@ -135,36 +137,37 @@ gst_fakesink_class_init (GstFakeSinkClass * klass)
{
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,
@ -172,69 +175,24 @@ gst_fakesink_class_init (GstFakeSinkClass * klass)
gst_marshal_VOID__BOXED_OBJECT, G_TYPE_NONE, 2,
GST_TYPE_BUFFER | G_SIGNAL_TYPE_STATIC_SCOPE, GST_TYPE_PAD);
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
@ -243,7 +201,6 @@ gst_fakesink_set_property (GObject * object, guint prop_id,
{
GstFakeSink *sink;
/* it's not null if we got it, but it might not be ours */
sink = GST_FAKESINK (object);
switch (prop_id) {
@ -274,15 +231,9 @@ gst_fakesink_get_property (GObject * object, guint prop_id, GValue * value,
{
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;
@ -307,79 +258,86 @@ gst_fakesink_get_property (GObject * object, guint prop_id, GValue * value,
}
}
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 (!fakesink->silent) {
g_free (fakesink->last_message);
fakesink->last_message =
g_strdup_printf ("chain ******* (%s:%s)E (type: %d) %p",
GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE (event), event);
g_object_notify (G_OBJECT (fakesink), "last_message");
if (sink->sync) {
GST_BASESINK_CLASS (parent_class)->get_times (bsink, buffer, start, end);
}
}
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_DISCONTINUOUS:
default:
gst_pad_event_default (pad, event);
break;
static void
gst_fakesink_event (GstBaseSink * bsink, GstEvent * event)
{
GstFakeSink *sink = GST_FAKESINK (bsink);
if (!sink->silent) {
g_free (sink->last_message);
sink->last_message =
g_strdup_printf ("chain ******* E (type: %d) %p",
GST_EVENT_TYPE (event), event);
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);
fakesink = GST_FAKESINK (GST_OBJECT_PARENT (pad));
if (!sink->silent) {
g_free (sink->last_message);
if (fakesink->sync && fakesink->clock) {
//gst_element_wait (GST_ELEMENT (fakesink), GST_BUFFER_TIMESTAMP (buf));
sink->last_message = g_strdup_printf ("preroll ******* ");
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;
@ -406,12 +364,13 @@ gst_fakesink_change_state (GstElement * element)
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));

View file

@ -25,6 +25,7 @@
#define __GST_FAKESINK_H__
#include <gst/gst.h>
#include <gst/base/gstbasesink.h>
G_BEGIN_DECLS
@ -54,20 +55,18 @@ typedef struct _GstFakeSink GstFakeSink;
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);