mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-28 11:10:37 +00:00
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:
parent
a7abc22271
commit
326d36b8d8
26 changed files with 3601 additions and 362 deletions
51
ChangeLog
51
ChangeLog
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
152
docs/design/part-negotiation.txt
Normal file
152
docs/design/part-negotiation.txt
Normal 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.
|
||||
|
||||
|
||||
|
49
docs/design/part-preroll.txt
Normal file
49
docs/design/part-preroll.txt
Normal 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.
|
||||
|
201
docs/design/part-scheduling.txt
Normal file
201
docs/design/part-scheduling.txt
Normal 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
151
docs/design/part-states.txt
Normal 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
|
||||
-----
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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
23
gst/base/Makefile.am
Normal 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
16
gst/base/README
Normal 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
873
gst/base/gstbasesink.c
Normal 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
89
gst/base/gstbasesink.h
Normal 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__ */
|
|
@ -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 \
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
|
|
330
gst/gstbin.c
330
gst/gstbin.c
|
@ -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);
|
||||
|
|
324
gst/gstelement.c
324
gst/gstelement.c
|
@ -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 */
|
||||
|
|
112
gst/gstpad.c
112
gst/gstpad.c
|
@ -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)) {
|
||||
|
|
|
@ -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
23
libs/gst/base/Makefile.am
Normal 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
16
libs/gst/base/README
Normal 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
873
libs/gst/base/gstbasesink.c
Normal 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
libs/gst/base/gstbasesink.h
Normal file
89
libs/gst/base/gstbasesink.h
Normal 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__ */
|
|
@ -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 \
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue