Add sink base class to abstract locking and preroll.

Original commit message from CVS:
* configure.ac:
* docs/design/part-states.txt:
* gst/Makefile.am:
* gst/base/Makefile.am:
* gst/base/gstbasesink.c: (gst_basesink_get_template),
(gst_basesink_base_init), (gst_basesink_class_init),
(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_alloc_buffer),
(gst_basesink_finish_preroll), (gst_basesink_event),
(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_event), (gst_fakesink_preroll),
(gst_fakesink_render), (gst_fakesink_change_state):
* gst/elements/gstfakesink.h:
* gst/gstelement.c: (gst_element_get_random_pad):
* gst/gstevent.h:
* gst/gstiterator.c: (gst_iterator_init), (gst_iterator_new),
(gst_list_iterator_next), (gst_list_iterator_free),
(gst_iterator_new_list), (gst_iterator_pop), (gst_iterator_next),
(gst_iterator_push), (filter_next):
* gst/gstmessage.h:
* gst/gsttrashstack.h:
Add sink base class to abstract locking and preroll.
Make fakesink use the base class.
Some doc fixes.
Fix missing breaks.
This commit is contained in:
Wim Taymans 2005-02-23 11:22:43 +00:00
parent 7ba6b8b6b7
commit ba3534f315
21 changed files with 1550 additions and 835 deletions

View file

@ -1,3 +1,43 @@
2005-02-23 Wim Taymans <wim@fluendo.com>
* configure.ac:
* docs/design/part-states.txt:
* gst/Makefile.am:
* gst/base/Makefile.am:
* gst/base/gstbasesink.c: (gst_basesink_get_template),
(gst_basesink_base_init), (gst_basesink_class_init),
(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_alloc_buffer),
(gst_basesink_finish_preroll), (gst_basesink_event),
(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_event), (gst_fakesink_preroll),
(gst_fakesink_render), (gst_fakesink_change_state):
* gst/elements/gstfakesink.h:
* gst/gstelement.c: (gst_element_get_random_pad):
* gst/gstevent.h:
* gst/gstiterator.c: (gst_iterator_init), (gst_iterator_new),
(gst_list_iterator_next), (gst_list_iterator_free),
(gst_iterator_new_list), (gst_iterator_pop), (gst_iterator_next),
(gst_iterator_push), (filter_next):
* gst/gstmessage.h:
* gst/gsttrashstack.h:
Add sink base class to abstract locking and preroll.
Make fakesink use the base class.
Some doc fixes.
Fix missing breaks.
2005-02-22 Andy Wingo <wingo@pobox.com>
* gst/elements/gstidentity.h

View file

@ -661,6 +661,7 @@ include/Makefile
gst/Makefile
gst/gstconfig.h
gst/gstversion.h
gst/base/Makefile
gst/indexers/Makefile
gst/elements/Makefile
gst/parse/Makefile

View file

@ -15,7 +15,7 @@ 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 sample and then block.
elements however only accept one buffer and then block.
- PLAYING: The same as PAUSED except for sinks, who are now accepting
and rendering data.
@ -57,7 +57,7 @@ The _set_state() function can return 3 possible values:
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 sample
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

View file

@ -62,8 +62,8 @@ else
GST_URI_SRC = gsturi.c
endif
SUBDIRS = $(GST_PARSE_DIRS) $(GST_REGISTRY_DIRS) . elements schedulers $(GST_INDEX_DIRS)
DIST_SUBDIRS = elements parse registries schedulers indexers
SUBDIRS = $(GST_PARSE_DIRS) $(GST_REGISTRY_DIRS) . base elements schedulers $(GST_INDEX_DIRS)
DIST_SUBDIRS = base elements parse registries schedulers indexers
# make variables for all generated source and header files to make the
# distinction clear

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

@ -0,0 +1,23 @@
lib_LTLIBRARIES = libgstbase.la
AS_LIBTOOL_LIB = libgstbase
EXTRA_DIST = $(as_libtool_EXTRA_DIST)
noinst_DATA = $(as_libtool_noinst_DATA_files)
libgstbase_la_DEPENDENCIES = ../libgstreamer-@GST_MAJORMINOR@.la
libgstbase_la_SOURCES = \
gstbasesink.c
libgstbase_la_CFLAGS = $(GST_OBJ_CFLAGS)
libgstbase_la_LIBADD = $(GST_OBJ_LIBS)
libgstbase_la_LDFLAGS = $(as_libtool_LDFLAGS)
noinst_HEADERS =
gstbasesink.h
install-data-local: as-libtool-install-data-local
uninstall-local: as-libtool-uninstall-local
include $(top_srcdir)/common/as-libtool.mak

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

@ -0,0 +1,550 @@
/* 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
/* BaseSink signals and args */
enum
{
/* FILL ME */
SIGNAL_HANDOFF,
LAST_SIGNAL
};
#define DEFAULT_SIZE 1024
#define DEFAULT_HAS_LOOP FALSE
#define DEFAULT_HAS_CHAIN TRUE
enum
{
ARG_0,
ARG_HAS_LOOP,
ARG_HAS_CHAIN
};
#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_alloc_buffer (GstBaseSink * sink,
guint64 offset, guint size, GstCaps * caps);
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 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), ARG_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), ARG_HAS_CHAIN,
g_param_spec_boolean ("has-chain", "has-chain",
"Enable chain-based operation", DEFAULT_HAS_CHAIN,
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->alloc_buffer = GST_DEBUG_FUNCPTR (gst_base_sink_alloc_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_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;
/* it's not null if we got it, but it might not be ours */
sink = GST_BASESINK (object);
switch (prop_id) {
case ARG_HAS_LOOP:
sink->has_loop = g_value_get_boolean (value);
gst_basesink_set_all_pad_functions (sink);
break;
case ARG_HAS_CHAIN:
sink->has_chain = g_value_get_boolean (value);
gst_basesink_set_all_pad_functions (sink);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_basesink_get_property (GObject * object, guint prop_id, GValue * value,
GParamSpec * pspec)
{
GstBaseSink *sink;
/* it's not null if we got it, but it might not be ours */
g_return_if_fail (GST_IS_BASESINK (object));
sink = GST_BASESINK (object);
switch (prop_id) {
case ARG_HAS_LOOP:
g_value_set_boolean (value, sink->has_loop);
break;
case ARG_HAS_CHAIN:
g_value_set_boolean (value, sink->has_chain);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
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_alloc_buffer (GstBaseSink * sink, guint64 offset, guint size,
GstCaps * caps)
{
return NULL;
}
/* STREAM_LOCK should be held */
GstFlowReturn
gst_basesink_finish_preroll (GstBaseSink * basesink, GstPad * pad,
GstBuffer * buffer)
{
gboolean usable;
GstBaseSinkClass *bclass;
/* lock order is important */
GST_STATE_LOCK (basesink);
GST_PREROLL_LOCK (pad);
if (!basesink->need_preroll)
goto no_preroll;
bclass = GST_BASESINK_GET_CLASS (basesink);
if (bclass->preroll)
bclass->preroll (basesink, buffer);
basesink->need_preroll = FALSE;
basesink->have_preroll = TRUE;
gst_element_commit_state (GST_ELEMENT (basesink));
GST_STATE_UNLOCK (basesink);
GST_DEBUG ("element %s waiting to finish preroll",
GST_ELEMENT_NAME (basesink));
GST_PREROLL_WAIT (pad);
GST_DEBUG ("done preroll");
GST_LOCK (pad);
usable = !GST_RPAD_IS_FLUSHING (pad) && GST_RPAD_IS_ACTIVE (pad);
GST_UNLOCK (pad);
if (!usable)
goto unusable;
GST_DEBUG ("done preroll");
basesink->have_preroll = FALSE;
GST_PREROLL_UNLOCK (pad);
return GST_FLOW_OK;
no_preroll:
{
GST_PREROLL_UNLOCK (pad);
GST_STATE_UNLOCK (basesink);
return GST_FLOW_OK;
}
unusable:
{
GST_DEBUG ("pad is flushing");
GST_PREROLL_UNLOCK (pad);
return GST_FLOW_UNEXPECTED;
}
}
static gboolean
gst_basesink_event (GstPad * pad, GstEvent * event)
{
GstBaseSink *basesink;
gboolean result = TRUE;
GstBaseSinkClass *bclass;
basesink = GST_BASESINK (GST_OBJECT_PARENT (pad));
GST_STREAM_LOCK (pad);
bclass = GST_BASESINK_GET_CLASS (basesink);
if (bclass->event)
bclass->event (basesink, event);
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_EOS:
{
GstFlowReturn ret;
ret = gst_basesink_finish_preroll (basesink, pad, NULL);
if (ret == GST_FLOW_OK) {
basesink->eos = TRUE;
/* ok, we can post the message */
gst_element_post_message (GST_ELEMENT (basesink),
gst_message_new_eos (GST_OBJECT (basesink)));
}
break;
}
case GST_EVENT_DISCONTINUOUS:
if (basesink->sync && basesink->clock) {
//gint64 value = GST_EVENT_DISCONT_OFFSET (event, 0).value;
}
default:
result = gst_pad_event_default (pad, event);
break;
}
GST_STREAM_UNLOCK (pad);
return result;
}
static GstFlowReturn
gst_basesink_chain_unlocked (GstPad * pad, GstBuffer * buf)
{
GstBaseSink *basesink;
GstFlowReturn result = GST_FLOW_OK;
GstBaseSinkClass *bclass;
basesink = GST_BASESINK (GST_OBJECT_PARENT (pad));
result = gst_basesink_finish_preroll (basesink, pad, buf);
if (result != GST_FLOW_OK)
goto exit;
/* doClock
if (basesink->sync && basesink->clock) {
gst_element_wait (GST_ELEMENT (basesink), GST_BUFFER_TIMESTAMP (buf));
}
*/
bclass = GST_BASESINK_GET_CLASS (basesink);
if (bclass->render)
bclass->render (basesink, buf);
exit:
gst_buffer_unref (buf);
return result;
}
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;
exit:
GST_STREAM_UNLOCK (pad);
return;
paused:
gst_task_pause (GST_RPAD_TASK (pad));
goto exit;
}
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 */
/* 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);
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->need_preroll = TRUE;
basesink->have_preroll = FALSE;
GST_PREROLL_UNLOCK (basesink->sinkpad);
ret = GST_STATE_ASYNC;
break;
case GST_STATE_PAUSED_TO_PLAYING:
/* the state change completes when we are blocking on a preroll
* sample */
GST_PREROLL_LOCK (basesink->sinkpad);
if (!basesink->have_preroll) {
basesink->need_preroll = TRUE;
ret = GST_STATE_ASYNC;
} else {
/* now let it play */
GST_PREROLL_SIGNAL (basesink->sinkpad);
}
GST_PREROLL_UNLOCK (basesink->sinkpad);
break;
case GST_STATE_PLAYING_TO_PAUSED:
GST_PREROLL_LOCK (basesink->sinkpad);
basesink->need_preroll = TRUE;
/* 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 && !basesink->eos) {
ret = GST_STATE_ASYNC;
}
GST_PREROLL_UNLOCK (basesink->sinkpad);
break;
case GST_STATE_PAUSED_TO_READY:
break;
case GST_STATE_READY_TO_NULL:
break;
default:
break;
}
GST_ELEMENT_CLASS (parent_class)->change_state (element);
return ret;
}

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

@ -0,0 +1,79 @@
/* 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))
typedef struct _GstBaseSink GstBaseSink;
typedef struct _GstBaseSinkClass GstBaseSinkClass;
struct _GstBaseSink {
GstElement element;
GstPad *sinkpad;
GstActivateMode pad_mode;
guint64 offset;
gboolean has_loop;
gboolean has_chain;
gboolean sync;
GstClock *clock;
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* (*alloc_buffer) (GstBaseSink *sink, guint64 offset, guint size,
GstCaps *caps);
void (*event) (GstBaseSink *sink, GstEvent *event);
GstFlowReturn (*preroll) (GstBaseSink *sink, GstBuffer *buffer);
GstFlowReturn (*render) (GstBaseSink *sink, GstBuffer *buffer);
};
GType gst_basesink_get_type(void);
G_END_DECLS
#endif /* __GST_BASESINK_H__ */

View file

@ -45,7 +45,7 @@ libgstelements_la_SOURCES = \
libgstelements_la_CFLAGS = $(GST_OBJ_CFLAGS)
libgstelements_la_LIBADD = $(GST_OBJ_LIBS)
libgstelements_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(as_libtool_LDFLAGS)
libgstelements_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(as_libtool_LDFLAGS) $(top_builddir)/gst/base/libgstbase.la
noinst_HEADERS = \
gstaggregator.h \

View file

@ -1,6 +1,6 @@
/* GStreamer
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
* 2000 Wim Taymans <wtay@chello.be>
* 2005 Wim Taymans <wim@fluendo.com>
*
* gstfakesink.c:
*
@ -36,12 +36,12 @@ static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
GST_DEBUG_CATEGORY_STATIC (gst_fakesink_debug);
#define GST_CAT_DEFAULT gst_fakesink_debug
#define DEFAULT_SIZE 1024
GstElementDetails gst_fakesink_details = GST_ELEMENT_DETAILS ("Fake Sink",
"Sink",
"Black hole for data",
"Erik Walthinsen <omega@cse.ogi.edu>, Tim Waymans <tway@fluendo.com>");
"Erik Walthinsen <omega@cse.ogi.edu>, "
"Wim Taymans <wim@fluendo.com>, "
"Mr. 'frag-me-more' Vanderwingo <wingo@fluendo.com>");
/* FakeSink signals and args */
@ -52,25 +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_HAS_LOOP,
ARG_HAS_CHAIN
};
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)
@ -103,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,
@ -117,12 +112,11 @@ static void gst_fakesink_get_property (GObject * object, guint prop_id,
static GstElementStateReturn gst_fakesink_change_state (GstElement * element);
static GstFlowReturn gst_fakesink_chain_unlocked (GstPad * pad,
static GstFlowReturn gst_fakesink_preroll (GstBaseSink * bsink,
GstBuffer * buffer);
static void gst_fakesink_loop (GstPad * pad);
static GstFlowReturn gst_fakesink_chain (GstPad * pad, GstBuffer * buffer);
static gboolean gst_fakesink_activate (GstPad * pad, GstActivateMode mode);
static gboolean gst_fakesink_event (GstPad * pad, GstEvent * event);
static GstFlowReturn gst_fakesink_render (GstBaseSink * bsink,
GstBuffer * buffer);
static void gst_fakesink_event (GstBaseSink * bsink, GstEvent * event);
static guint gst_fakesink_signals[LAST_SIGNAL] = { 0 };
@ -134,8 +128,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
@ -143,44 +135,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));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_HAS_LOOP,
g_param_spec_boolean ("has-loop", "has-loop",
"Enable loop-based operation", TRUE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_HAS_CHAIN,
g_param_spec_boolean ("has-chain", "has-chain",
"Enable chain-based operation", TRUE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
DEFAULT_DUMP, G_PARAM_READWRITE));
gst_fakesink_signals[SIGNAL_HANDOFF] =
g_signal_new ("handoff", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
@ -188,93 +173,23 @@ 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);
}
static void
gst_fakesink_init (GstFakeSink * fakesink)
{
fakesink->sinkpad =
gst_pad_new_from_template (gst_static_pad_template_get (&sinktemplate),
"sink");
gst_element_add_pad (GST_ELEMENT (fakesink), fakesink->sinkpad);
fakesink->silent = FALSE;
fakesink->dump = FALSE;
fakesink->sync = FALSE;
fakesink->last_message = NULL;
fakesink->state_error = FAKESINK_STATE_ERROR_NONE;
fakesink->signal_handoffs = FALSE;
fakesink->pad_mode = GST_ACTIVATE_NONE;
GST_RPAD_TASK (fakesink->sinkpad) = NULL;
}
static void
gst_fakesink_set_pad_functions (GstFakeSink * this, GstPad * pad)
{
gst_pad_set_activate_function (pad,
GST_DEBUG_FUNCPTR (gst_fakesink_activate));
gst_pad_set_event_function (pad, GST_DEBUG_FUNCPTR (gst_fakesink_event));
if (this->has_chain)
gst_pad_set_chain_function (pad, GST_DEBUG_FUNCPTR (gst_fakesink_chain));
else
gst_pad_set_chain_function (pad, NULL);
if (this->has_loop)
gst_pad_set_loop_function (pad, GST_DEBUG_FUNCPTR (gst_fakesink_loop));
else
gst_pad_set_loop_function (pad, NULL);
}
static void
gst_fakesink_set_all_pad_functions (GstFakeSink * this)
{
GList *l;
for (l = GST_ELEMENT_PADS (this); l; l = l->next)
gst_fakesink_set_pad_functions (this, (GstPad *) l->data);
}
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_fakesink_set_pad_functions (fakesink, sinkpad);
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
@ -283,18 +198,9 @@ 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) {
case ARG_HAS_LOOP:
sink->has_loop = g_value_get_boolean (value);
gst_fakesink_set_all_pad_functions (sink);
break;
case ARG_HAS_CHAIN:
sink->has_chain = g_value_get_boolean (value);
gst_fakesink_set_all_pad_functions (sink);
break;
case ARG_SILENT:
sink->silent = g_value_get_boolean (value);
break;
@ -322,24 +228,12 @@ 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;
case ARG_HAS_LOOP:
g_value_set_boolean (value, sink->has_loop);
break;
case ARG_HAS_CHAIN:
g_value_set_boolean (value, sink->has_chain);
break;
case ARG_SILENT:
g_value_set_boolean (value, sink->silent);
break;
@ -361,256 +255,65 @@ gst_fakesink_get_property (GObject * object, guint prop_id, GValue * value,
}
}
/* STREAM_LOCK should be held */
GstFlowReturn
gst_fakesink_finish_preroll (GstFakeSink * fakesink, GstPad * pad)
static void
gst_fakesink_event (GstBaseSink * bsink, GstEvent * event)
{
/* lock order is important */
GST_STATE_LOCK (fakesink);
GST_PREROLL_LOCK (pad);
if (!fakesink->need_preroll)
goto no_preroll;
GstFakeSink *sink = GST_FAKESINK (bsink);
if (!fakesink->silent) {
g_free (fakesink->last_message);
if (!sink->silent) {
g_free (sink->last_message);
fakesink->last_message =
g_strdup_printf ("preroll ******* (%s:%s)", GST_DEBUG_PAD_NAME (pad));
sink->last_message =
g_strdup_printf ("chain ******* E (type: %d) %p",
GST_EVENT_TYPE (event), event);
g_object_notify (G_OBJECT (fakesink), "last_message");
g_object_notify (G_OBJECT (sink), "last_message");
}
fakesink->need_preroll = FALSE;
fakesink->have_preroll = TRUE;
gst_element_commit_state (GST_ELEMENT (fakesink));
GST_STATE_UNLOCK (fakesink);
{
gboolean usable;
GST_DEBUG ("element %s waiting to finish preroll",
GST_ELEMENT_NAME (fakesink));
GST_PREROLL_WAIT (pad);
GST_DEBUG ("done preroll");
GST_LOCK (pad);
usable = !GST_RPAD_IS_FLUSHING (pad) && GST_RPAD_IS_ACTIVE (pad);
GST_UNLOCK (pad);
if (!usable)
goto unusable;
GST_DEBUG ("done preroll");
}
fakesink->have_preroll = FALSE;
GST_PREROLL_UNLOCK (pad);
return GST_FLOW_OK;
no_preroll:
{
GST_PREROLL_UNLOCK (pad);
GST_STATE_UNLOCK (fakesink);
return GST_FLOW_OK;
}
unusable:
{
GST_DEBUG ("pad is flushing");
GST_PREROLL_UNLOCK (pad);
return GST_FLOW_UNEXPECTED;
}
}
static gboolean
gst_fakesink_event (GstPad * pad, GstEvent * event)
{
GstFakeSink *fakesink;
gboolean result = TRUE;
fakesink = GST_FAKESINK (GST_OBJECT_PARENT (pad));
GST_STREAM_LOCK (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");
}
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_EOS:
{
GstFlowReturn ret;
ret = gst_fakesink_finish_preroll (fakesink, pad);
if (ret == GST_FLOW_OK) {
fakesink->eos = TRUE;
/* ok, we can post the message */
gst_element_post_message (GST_ELEMENT (fakesink),
gst_message_new_eos (GST_OBJECT (fakesink)));
}
break;
}
case GST_EVENT_DISCONTINUOUS:
if (fakesink->sync && fakesink->clock) {
//gint64 value = GST_EVENT_DISCONT_OFFSET (event, 0).value;
}
default:
result = gst_pad_event_default (pad, event);
break;
}
GST_STREAM_UNLOCK (pad);
return result;
}
static GstFlowReturn
gst_fakesink_chain_unlocked (GstPad * pad, GstBuffer * buf)
gst_fakesink_preroll (GstBaseSink * bsink, GstBuffer * buffer)
{
GstFakeSink *fakesink;
GstFlowReturn result = GST_FLOW_OK;
GstFakeSink *sink = GST_FAKESINK (bsink);
fakesink = GST_FAKESINK (GST_OBJECT_PARENT (pad));
if (!sink->silent) {
g_free (sink->last_message);
result = gst_fakesink_finish_preroll (fakesink, pad);
if (result != GST_FLOW_OK)
goto exit;
sink->last_message = g_strdup_printf ("preroll ******* ");
if (fakesink->sync && fakesink->clock) {
gst_element_wait (GST_ELEMENT (fakesink), GST_BUFFER_TIMESTAMP (buf));
g_object_notify (G_OBJECT (sink), "last_message");
}
return GST_FLOW_OK;
}
if (!fakesink->silent) {
g_free (fakesink->last_message);
static GstFlowReturn
gst_fakesink_render (GstBaseSink * bsink, GstBuffer * buf)
{
GstFakeSink *sink = GST_FAKESINK (bsink);
fakesink->last_message =
g_strdup_printf ("chain ******* (%s:%s)< (%d bytes, timestamp: %"
if (!sink->silent) {
g_free (sink->last_message);
sink->last_message =
g_strdup_printf ("chain ******* < (%d bytes, timestamp: %"
GST_TIME_FORMAT ", duration: %" GST_TIME_FORMAT ", offset: %"
G_GINT64_FORMAT ", offset_end: %" G_GINT64_FORMAT ", flags: %d) %p",
GST_DEBUG_PAD_NAME (pad), GST_BUFFER_SIZE (buf),
GST_BUFFER_SIZE (buf),
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_BUFFER_OFFSET (buf),
GST_BUFFER_OFFSET_END (buf), GST_BUFFER_FLAGS (buf), buf);
g_object_notify (G_OBJECT (fakesink), "last_message");
g_object_notify (G_OBJECT (sink), "last_message");
}
if (sink->signal_handoffs)
g_signal_emit (G_OBJECT (sink), gst_fakesink_signals[SIGNAL_HANDOFF], 0,
buf);
if (fakesink->signal_handoffs)
g_signal_emit (G_OBJECT (fakesink), gst_fakesink_signals[SIGNAL_HANDOFF], 0,
buf, pad);
if (fakesink->dump) {
if (sink->dump) {
gst_util_dump_mem (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
}
exit:
gst_buffer_unref (buf);
return result;
}
static GstFlowReturn
gst_fakesink_chain (GstPad * pad, GstBuffer * buf)
{
GstFlowReturn result;
g_assert (GST_FAKESINK (GST_OBJECT_PARENT (pad))->pad_mode ==
GST_ACTIVATE_PUSH);
GST_STREAM_LOCK (pad);
result = gst_fakesink_chain_unlocked (pad, buf);
GST_STREAM_UNLOCK (pad);
return result;
}
static void
gst_fakesink_loop (GstPad * pad)
{
GstFakeSink *fakesink;
GstBuffer *buf = NULL;
GstFlowReturn result;
fakesink = GST_FAKESINK (GST_OBJECT_PARENT (pad));
g_assert (fakesink->pad_mode == GST_ACTIVATE_PULL);
GST_STREAM_LOCK (pad);
result = gst_pad_pull_range (pad, fakesink->offset, DEFAULT_SIZE, &buf);
if (result != GST_FLOW_OK)
goto paused;
result = gst_fakesink_chain_unlocked (pad, buf);
if (result != GST_FLOW_OK)
goto paused;
exit:
GST_STREAM_UNLOCK (pad);
return;
paused:
gst_task_pause (GST_RPAD_TASK (pad));
goto exit;
}
static gboolean
gst_fakesink_activate (GstPad * pad, GstActivateMode mode)
{
gboolean result = FALSE;
GstFakeSink *fakesink;
fakesink = GST_FAKESINK (GST_OBJECT_PARENT (pad));
switch (mode) {
case GST_ACTIVATE_PUSH:
g_return_val_if_fail (fakesink->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 (fakesink->has_loop, FALSE);
if (GST_ELEMENT_SCHEDULER (fakesink)) {
GST_STREAM_LOCK (pad);
GST_RPAD_TASK (pad) =
gst_scheduler_create_task (GST_ELEMENT_SCHEDULER (fakesink),
(GstTaskFunction) gst_fakesink_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 */
/* 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;
}
fakesink->pad_mode = mode;
return result;
return GST_FLOW_OK;
}
static GstElementStateReturn
@ -628,42 +331,14 @@ gst_fakesink_change_state (GstElement * element)
case GST_STATE_READY_TO_PAUSED:
if (fakesink->state_error == FAKESINK_STATE_ERROR_READY_PAUSED)
goto error;
/* need to complete preroll before this state change completes, there
* is no data flow in READY so we cqn safely assume we need to preroll. */
fakesink->offset = 0;
GST_PREROLL_LOCK (fakesink->sinkpad);
fakesink->need_preroll = TRUE;
fakesink->have_preroll = FALSE;
GST_PREROLL_UNLOCK (fakesink->sinkpad);
ret = GST_STATE_ASYNC;
break;
case GST_STATE_PAUSED_TO_PLAYING:
if (fakesink->state_error == FAKESINK_STATE_ERROR_PAUSED_PLAYING)
goto error;
/* the state change completes when we are blocking on a preroll
* sample */
GST_PREROLL_LOCK (fakesink->sinkpad);
if (!fakesink->have_preroll) {
fakesink->need_preroll = TRUE;
ret = GST_STATE_ASYNC;
} else {
/* now let it play */
GST_PREROLL_SIGNAL (fakesink->sinkpad);
}
GST_PREROLL_UNLOCK (fakesink->sinkpad);
break;
case GST_STATE_PLAYING_TO_PAUSED:
if (fakesink->state_error == FAKESINK_STATE_ERROR_PLAYING_PAUSED)
goto error;
GST_PREROLL_LOCK (fakesink->sinkpad);
fakesink->need_preroll = TRUE;
/* if we don't have a preroll buffer and we have not received EOS,
* we need to wait for a preroll */
if (!fakesink->have_preroll && !fakesink->eos) {
ret = GST_STATE_ASYNC;
}
GST_PREROLL_UNLOCK (fakesink->sinkpad);
break;
case GST_STATE_PAUSED_TO_READY:
if (fakesink->state_error == FAKESINK_STATE_ERROR_PAUSED_READY)
@ -679,7 +354,7 @@ gst_fakesink_change_state (GstElement * element)
break;
}
GST_ELEMENT_CLASS (parent_class)->change_state (element);
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
return ret;

View file

@ -25,6 +25,7 @@
#define __GST_FAKESINK_H__
#include <gst/gst.h>
#include <gst/base/gstbasesink.h>
G_BEGIN_DECLS
@ -54,29 +55,18 @@ typedef struct _GstFakeSink GstFakeSink;
typedef struct _GstFakeSinkClass GstFakeSinkClass;
struct _GstFakeSink {
GstElement element;
GstPad *sinkpad;
GstBaseSink element;
gboolean silent;
gboolean dump;
gboolean has_loop;
gboolean has_chain;
gboolean sync;
gboolean signal_handoffs;
GstClock *clock;
GstFakeSinkStateError state_error;
GstActivateMode pad_mode;
guint64 offset;
gboolean eos;
gboolean need_preroll;
gboolean have_preroll;
gchar *last_message;
};
struct _GstFakeSinkClass {
GstElementClass parent_class;
GstBaseSinkClass parent_class;
/* signals */
void (*handoff) (GstElement *element, GstBuffer *buf, GstPad *pad);

View file

@ -978,8 +978,10 @@ gst_element_get_random_pad (GstElement * element, GstPadDirection dir)
switch (dir) {
case GST_PAD_SRC:
pads = element->srcpads;
break;
case GST_PAD_SINK:
pads = element->sinkpads;
break;
default:
g_warning ("unknown pad direction");
return NULL;

View file

@ -34,6 +34,19 @@ G_BEGIN_DECLS
GST_EXPORT GType _gst_event_type;
/**
* GstBufferFlag:
* @GST_EVENT_UNKNOWN:
* @GST_EVENT_EOS:
* @GST_EVENT_FLUSH:
* @GST_EVENT_DISCONTINUOUS:
* @GST_EVENT_QOS:
* @GST_EVENT_SEEK:
* @GST_EVENT_SIZE:
* @GST_EVENT_RATE:
* @GST_EVENT_NAVIGATION:
* @GST_EVENT_TAG:
*/
typedef enum {
GST_EVENT_UNKNOWN = 0,
GST_EVENT_EOS = 1,

View file

@ -318,7 +318,7 @@ typedef struct _GstIteratorFilter
static GstIteratorResult
filter_next (GstIteratorFilter * it, gpointer * elem)
{
GstIteratorResult result;
GstIteratorResult result = GST_ITERATOR_ERROR;
gboolean done = FALSE;
*elem = NULL;

View file

@ -28,20 +28,42 @@
#include <gst/gsttag.h>
#include <gst/gststructure.h>
G_BEGIN_DECLS GST_EXPORT GType _gst_message_type;
G_BEGIN_DECLS
GST_EXPORT GType _gst_message_type;
/**
* GstMessageType:
* @GST_MESSAGE_UNKNOWN: an undefined message
* @GST_MESSAGE_EOS: end-of-stream reached in a pipeline
* @GST_MESSAGE_ERROR: an error occured
* @GST_MESSAGE_WARNING: a warning occured.
* @GST_MESSAGE_INFO: an info message occured
* @GST_MESSAGE_TAG: a tag was found.
* @GST_MESSAGE_BUFFERING: the pipeline is buffering
* @GST_MESSAGE_STATE_CHANGED: a state change happened
* @GST_MESSAGE_STEP_DONE: a framestep finished.
* @GST_MESSAGE_NEW_CLOCK: a new clock was selected in the pipeline
* @GST_MESSAGE_STRUCTURE_CHANGE: the structure of the pipeline changed.
* @GST_MESSAGE_STREAM_STATUS: status about a stream, emited when it starts,
* stops, errors, etc..
* @GST_MESSAGE_ANY: mask for all of the above messages.
*/
typedef enum
{
GST_MESSAGE_UNKNOWN = 0,
GST_MESSAGE_EOS = (1 << 0),
GST_MESSAGE_ERROR = (1 << 1),
GST_MESSAGE_WARNING = (1 << 2),
GST_MESSAGE_INFO = (1 << 3),
GST_MESSAGE_TAG = (1 << 4),
GST_MESSAGE_BUFFERING = (1 << 5),
GST_MESSAGE_STATE_CHANGED = (1 << 6),
GST_MESSAGE_STEP_DONE = (1 << 7),
GST_MESSAGE_ANY = 0xffffffff
GST_MESSAGE_UNKNOWN = 0,
GST_MESSAGE_EOS = (1 << 0),
GST_MESSAGE_ERROR = (1 << 1),
GST_MESSAGE_WARNING = (1 << 2),
GST_MESSAGE_INFO = (1 << 3),
GST_MESSAGE_TAG = (1 << 4),
GST_MESSAGE_BUFFERING = (1 << 5),
GST_MESSAGE_STATE_CHANGED = (1 << 6),
GST_MESSAGE_STEP_DONE = (1 << 7),
GST_MESSAGE_NEW_CLOCK = (1 << 8),
GST_MESSAGE_STRUCTURE_CHANGE = (1 << 9),
GST_MESSAGE_STREAM_STATUS = (1 << 10),
GST_MESSAGE_ANY = 0xffffffff
} GstMessageType;
#define GST_MESSAGE_TRACE_NAME "GstMessage"
@ -50,6 +72,9 @@ typedef enum
#define GST_MESSAGE(message) ((GstMessage*)(message))
#define GST_IS_MESSAGE(message) (GST_DATA_TYPE(message) == GST_TYPE_MESSAGE)
/* the lock is used to handle the synchronous handling of messages,
* the emiting thread is block until the handling thread processed
* the message using this mutex/cond pair */
#define GST_MESSAGE_GET_LOCK(message) (GST_MESSAGE(message)->lock)
#define GST_MESSAGE_LOCK(message) g_mutex_lock(GST_MESSAGE_GET_LOCK(message))
#define GST_MESSAGE_UNLOCK(message) g_mutex_unlock(GST_MESSAGE_GET_LOCK(message))

View file

@ -98,7 +98,7 @@ gst_trash_stack_pop (GstTrashStack *stack)
* problem that arises when a pop and push of the same element happens
* right between when we read head->next and try to swing the new pointer
* into place. This is usually solved using a counter which makes it highly
* inlikely that we manage to grab the wrong head->next value.
* unlikely that we manage to grab the wrong head->next value.
*/
__asm__ __volatile__ (
" pushl %%ebx; \n\t"

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

@ -0,0 +1,23 @@
lib_LTLIBRARIES = libgstbase.la
AS_LIBTOOL_LIB = libgstbase
EXTRA_DIST = $(as_libtool_EXTRA_DIST)
noinst_DATA = $(as_libtool_noinst_DATA_files)
libgstbase_la_DEPENDENCIES = ../libgstreamer-@GST_MAJORMINOR@.la
libgstbase_la_SOURCES = \
gstbasesink.c
libgstbase_la_CFLAGS = $(GST_OBJ_CFLAGS)
libgstbase_la_LIBADD = $(GST_OBJ_LIBS)
libgstbase_la_LDFLAGS = $(as_libtool_LDFLAGS)
noinst_HEADERS =
gstbasesink.h
install-data-local: as-libtool-install-data-local
uninstall-local: as-libtool-uninstall-local
include $(top_srcdir)/common/as-libtool.mak

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

@ -0,0 +1,550 @@
/* 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
/* BaseSink signals and args */
enum
{
/* FILL ME */
SIGNAL_HANDOFF,
LAST_SIGNAL
};
#define DEFAULT_SIZE 1024
#define DEFAULT_HAS_LOOP FALSE
#define DEFAULT_HAS_CHAIN TRUE
enum
{
ARG_0,
ARG_HAS_LOOP,
ARG_HAS_CHAIN
};
#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_alloc_buffer (GstBaseSink * sink,
guint64 offset, guint size, GstCaps * caps);
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 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), ARG_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), ARG_HAS_CHAIN,
g_param_spec_boolean ("has-chain", "has-chain",
"Enable chain-based operation", DEFAULT_HAS_CHAIN,
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->alloc_buffer = GST_DEBUG_FUNCPTR (gst_base_sink_alloc_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_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;
/* it's not null if we got it, but it might not be ours */
sink = GST_BASESINK (object);
switch (prop_id) {
case ARG_HAS_LOOP:
sink->has_loop = g_value_get_boolean (value);
gst_basesink_set_all_pad_functions (sink);
break;
case ARG_HAS_CHAIN:
sink->has_chain = g_value_get_boolean (value);
gst_basesink_set_all_pad_functions (sink);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_basesink_get_property (GObject * object, guint prop_id, GValue * value,
GParamSpec * pspec)
{
GstBaseSink *sink;
/* it's not null if we got it, but it might not be ours */
g_return_if_fail (GST_IS_BASESINK (object));
sink = GST_BASESINK (object);
switch (prop_id) {
case ARG_HAS_LOOP:
g_value_set_boolean (value, sink->has_loop);
break;
case ARG_HAS_CHAIN:
g_value_set_boolean (value, sink->has_chain);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
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_alloc_buffer (GstBaseSink * sink, guint64 offset, guint size,
GstCaps * caps)
{
return NULL;
}
/* STREAM_LOCK should be held */
GstFlowReturn
gst_basesink_finish_preroll (GstBaseSink * basesink, GstPad * pad,
GstBuffer * buffer)
{
gboolean usable;
GstBaseSinkClass *bclass;
/* lock order is important */
GST_STATE_LOCK (basesink);
GST_PREROLL_LOCK (pad);
if (!basesink->need_preroll)
goto no_preroll;
bclass = GST_BASESINK_GET_CLASS (basesink);
if (bclass->preroll)
bclass->preroll (basesink, buffer);
basesink->need_preroll = FALSE;
basesink->have_preroll = TRUE;
gst_element_commit_state (GST_ELEMENT (basesink));
GST_STATE_UNLOCK (basesink);
GST_DEBUG ("element %s waiting to finish preroll",
GST_ELEMENT_NAME (basesink));
GST_PREROLL_WAIT (pad);
GST_DEBUG ("done preroll");
GST_LOCK (pad);
usable = !GST_RPAD_IS_FLUSHING (pad) && GST_RPAD_IS_ACTIVE (pad);
GST_UNLOCK (pad);
if (!usable)
goto unusable;
GST_DEBUG ("done preroll");
basesink->have_preroll = FALSE;
GST_PREROLL_UNLOCK (pad);
return GST_FLOW_OK;
no_preroll:
{
GST_PREROLL_UNLOCK (pad);
GST_STATE_UNLOCK (basesink);
return GST_FLOW_OK;
}
unusable:
{
GST_DEBUG ("pad is flushing");
GST_PREROLL_UNLOCK (pad);
return GST_FLOW_UNEXPECTED;
}
}
static gboolean
gst_basesink_event (GstPad * pad, GstEvent * event)
{
GstBaseSink *basesink;
gboolean result = TRUE;
GstBaseSinkClass *bclass;
basesink = GST_BASESINK (GST_OBJECT_PARENT (pad));
GST_STREAM_LOCK (pad);
bclass = GST_BASESINK_GET_CLASS (basesink);
if (bclass->event)
bclass->event (basesink, event);
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_EOS:
{
GstFlowReturn ret;
ret = gst_basesink_finish_preroll (basesink, pad, NULL);
if (ret == GST_FLOW_OK) {
basesink->eos = TRUE;
/* ok, we can post the message */
gst_element_post_message (GST_ELEMENT (basesink),
gst_message_new_eos (GST_OBJECT (basesink)));
}
break;
}
case GST_EVENT_DISCONTINUOUS:
if (basesink->sync && basesink->clock) {
//gint64 value = GST_EVENT_DISCONT_OFFSET (event, 0).value;
}
default:
result = gst_pad_event_default (pad, event);
break;
}
GST_STREAM_UNLOCK (pad);
return result;
}
static GstFlowReturn
gst_basesink_chain_unlocked (GstPad * pad, GstBuffer * buf)
{
GstBaseSink *basesink;
GstFlowReturn result = GST_FLOW_OK;
GstBaseSinkClass *bclass;
basesink = GST_BASESINK (GST_OBJECT_PARENT (pad));
result = gst_basesink_finish_preroll (basesink, pad, buf);
if (result != GST_FLOW_OK)
goto exit;
/* doClock
if (basesink->sync && basesink->clock) {
gst_element_wait (GST_ELEMENT (basesink), GST_BUFFER_TIMESTAMP (buf));
}
*/
bclass = GST_BASESINK_GET_CLASS (basesink);
if (bclass->render)
bclass->render (basesink, buf);
exit:
gst_buffer_unref (buf);
return result;
}
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;
exit:
GST_STREAM_UNLOCK (pad);
return;
paused:
gst_task_pause (GST_RPAD_TASK (pad));
goto exit;
}
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 */
/* 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);
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->need_preroll = TRUE;
basesink->have_preroll = FALSE;
GST_PREROLL_UNLOCK (basesink->sinkpad);
ret = GST_STATE_ASYNC;
break;
case GST_STATE_PAUSED_TO_PLAYING:
/* the state change completes when we are blocking on a preroll
* sample */
GST_PREROLL_LOCK (basesink->sinkpad);
if (!basesink->have_preroll) {
basesink->need_preroll = TRUE;
ret = GST_STATE_ASYNC;
} else {
/* now let it play */
GST_PREROLL_SIGNAL (basesink->sinkpad);
}
GST_PREROLL_UNLOCK (basesink->sinkpad);
break;
case GST_STATE_PLAYING_TO_PAUSED:
GST_PREROLL_LOCK (basesink->sinkpad);
basesink->need_preroll = TRUE;
/* 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 && !basesink->eos) {
ret = GST_STATE_ASYNC;
}
GST_PREROLL_UNLOCK (basesink->sinkpad);
break;
case GST_STATE_PAUSED_TO_READY:
break;
case GST_STATE_READY_TO_NULL:
break;
default:
break;
}
GST_ELEMENT_CLASS (parent_class)->change_state (element);
return ret;
}

View file

@ -0,0 +1,79 @@
/* 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))
typedef struct _GstBaseSink GstBaseSink;
typedef struct _GstBaseSinkClass GstBaseSinkClass;
struct _GstBaseSink {
GstElement element;
GstPad *sinkpad;
GstActivateMode pad_mode;
guint64 offset;
gboolean has_loop;
gboolean has_chain;
gboolean sync;
GstClock *clock;
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* (*alloc_buffer) (GstBaseSink *sink, guint64 offset, guint size,
GstCaps *caps);
void (*event) (GstBaseSink *sink, GstEvent *event);
GstFlowReturn (*preroll) (GstBaseSink *sink, GstBuffer *buffer);
GstFlowReturn (*render) (GstBaseSink *sink, GstBuffer *buffer);
};
GType gst_basesink_get_type(void);
G_END_DECLS
#endif /* __GST_BASESINK_H__ */

View file

@ -45,7 +45,7 @@ libgstelements_la_SOURCES = \
libgstelements_la_CFLAGS = $(GST_OBJ_CFLAGS)
libgstelements_la_LIBADD = $(GST_OBJ_LIBS)
libgstelements_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(as_libtool_LDFLAGS)
libgstelements_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(as_libtool_LDFLAGS) $(top_builddir)/gst/base/libgstbase.la
noinst_HEADERS = \
gstaggregator.h \

View file

@ -1,6 +1,6 @@
/* GStreamer
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
* 2000 Wim Taymans <wtay@chello.be>
* 2005 Wim Taymans <wim@fluendo.com>
*
* gstfakesink.c:
*
@ -36,12 +36,12 @@ static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
GST_DEBUG_CATEGORY_STATIC (gst_fakesink_debug);
#define GST_CAT_DEFAULT gst_fakesink_debug
#define DEFAULT_SIZE 1024
GstElementDetails gst_fakesink_details = GST_ELEMENT_DETAILS ("Fake Sink",
"Sink",
"Black hole for data",
"Erik Walthinsen <omega@cse.ogi.edu>, Tim Waymans <tway@fluendo.com>");
"Erik Walthinsen <omega@cse.ogi.edu>, "
"Wim Taymans <wim@fluendo.com>, "
"Mr. 'frag-me-more' Vanderwingo <wingo@fluendo.com>");
/* FakeSink signals and args */
@ -52,25 +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_HAS_LOOP,
ARG_HAS_CHAIN
};
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)
@ -103,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,
@ -117,12 +112,11 @@ static void gst_fakesink_get_property (GObject * object, guint prop_id,
static GstElementStateReturn gst_fakesink_change_state (GstElement * element);
static GstFlowReturn gst_fakesink_chain_unlocked (GstPad * pad,
static GstFlowReturn gst_fakesink_preroll (GstBaseSink * bsink,
GstBuffer * buffer);
static void gst_fakesink_loop (GstPad * pad);
static GstFlowReturn gst_fakesink_chain (GstPad * pad, GstBuffer * buffer);
static gboolean gst_fakesink_activate (GstPad * pad, GstActivateMode mode);
static gboolean gst_fakesink_event (GstPad * pad, GstEvent * event);
static GstFlowReturn gst_fakesink_render (GstBaseSink * bsink,
GstBuffer * buffer);
static void gst_fakesink_event (GstBaseSink * bsink, GstEvent * event);
static guint gst_fakesink_signals[LAST_SIGNAL] = { 0 };
@ -134,8 +128,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
@ -143,44 +135,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));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_HAS_LOOP,
g_param_spec_boolean ("has-loop", "has-loop",
"Enable loop-based operation", TRUE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_HAS_CHAIN,
g_param_spec_boolean ("has-chain", "has-chain",
"Enable chain-based operation", TRUE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
DEFAULT_DUMP, G_PARAM_READWRITE));
gst_fakesink_signals[SIGNAL_HANDOFF] =
g_signal_new ("handoff", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
@ -188,93 +173,23 @@ 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);
}
static void
gst_fakesink_init (GstFakeSink * fakesink)
{
fakesink->sinkpad =
gst_pad_new_from_template (gst_static_pad_template_get (&sinktemplate),
"sink");
gst_element_add_pad (GST_ELEMENT (fakesink), fakesink->sinkpad);
fakesink->silent = FALSE;
fakesink->dump = FALSE;
fakesink->sync = FALSE;
fakesink->last_message = NULL;
fakesink->state_error = FAKESINK_STATE_ERROR_NONE;
fakesink->signal_handoffs = FALSE;
fakesink->pad_mode = GST_ACTIVATE_NONE;
GST_RPAD_TASK (fakesink->sinkpad) = NULL;
}
static void
gst_fakesink_set_pad_functions (GstFakeSink * this, GstPad * pad)
{
gst_pad_set_activate_function (pad,
GST_DEBUG_FUNCPTR (gst_fakesink_activate));
gst_pad_set_event_function (pad, GST_DEBUG_FUNCPTR (gst_fakesink_event));
if (this->has_chain)
gst_pad_set_chain_function (pad, GST_DEBUG_FUNCPTR (gst_fakesink_chain));
else
gst_pad_set_chain_function (pad, NULL);
if (this->has_loop)
gst_pad_set_loop_function (pad, GST_DEBUG_FUNCPTR (gst_fakesink_loop));
else
gst_pad_set_loop_function (pad, NULL);
}
static void
gst_fakesink_set_all_pad_functions (GstFakeSink * this)
{
GList *l;
for (l = GST_ELEMENT_PADS (this); l; l = l->next)
gst_fakesink_set_pad_functions (this, (GstPad *) l->data);
}
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_fakesink_set_pad_functions (fakesink, sinkpad);
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
@ -283,18 +198,9 @@ 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) {
case ARG_HAS_LOOP:
sink->has_loop = g_value_get_boolean (value);
gst_fakesink_set_all_pad_functions (sink);
break;
case ARG_HAS_CHAIN:
sink->has_chain = g_value_get_boolean (value);
gst_fakesink_set_all_pad_functions (sink);
break;
case ARG_SILENT:
sink->silent = g_value_get_boolean (value);
break;
@ -322,24 +228,12 @@ 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;
case ARG_HAS_LOOP:
g_value_set_boolean (value, sink->has_loop);
break;
case ARG_HAS_CHAIN:
g_value_set_boolean (value, sink->has_chain);
break;
case ARG_SILENT:
g_value_set_boolean (value, sink->silent);
break;
@ -361,256 +255,65 @@ gst_fakesink_get_property (GObject * object, guint prop_id, GValue * value,
}
}
/* STREAM_LOCK should be held */
GstFlowReturn
gst_fakesink_finish_preroll (GstFakeSink * fakesink, GstPad * pad)
static void
gst_fakesink_event (GstBaseSink * bsink, GstEvent * event)
{
/* lock order is important */
GST_STATE_LOCK (fakesink);
GST_PREROLL_LOCK (pad);
if (!fakesink->need_preroll)
goto no_preroll;
GstFakeSink *sink = GST_FAKESINK (bsink);
if (!fakesink->silent) {
g_free (fakesink->last_message);
if (!sink->silent) {
g_free (sink->last_message);
fakesink->last_message =
g_strdup_printf ("preroll ******* (%s:%s)", GST_DEBUG_PAD_NAME (pad));
sink->last_message =
g_strdup_printf ("chain ******* E (type: %d) %p",
GST_EVENT_TYPE (event), event);
g_object_notify (G_OBJECT (fakesink), "last_message");
g_object_notify (G_OBJECT (sink), "last_message");
}
fakesink->need_preroll = FALSE;
fakesink->have_preroll = TRUE;
gst_element_commit_state (GST_ELEMENT (fakesink));
GST_STATE_UNLOCK (fakesink);
{
gboolean usable;
GST_DEBUG ("element %s waiting to finish preroll",
GST_ELEMENT_NAME (fakesink));
GST_PREROLL_WAIT (pad);
GST_DEBUG ("done preroll");
GST_LOCK (pad);
usable = !GST_RPAD_IS_FLUSHING (pad) && GST_RPAD_IS_ACTIVE (pad);
GST_UNLOCK (pad);
if (!usable)
goto unusable;
GST_DEBUG ("done preroll");
}
fakesink->have_preroll = FALSE;
GST_PREROLL_UNLOCK (pad);
return GST_FLOW_OK;
no_preroll:
{
GST_PREROLL_UNLOCK (pad);
GST_STATE_UNLOCK (fakesink);
return GST_FLOW_OK;
}
unusable:
{
GST_DEBUG ("pad is flushing");
GST_PREROLL_UNLOCK (pad);
return GST_FLOW_UNEXPECTED;
}
}
static gboolean
gst_fakesink_event (GstPad * pad, GstEvent * event)
{
GstFakeSink *fakesink;
gboolean result = TRUE;
fakesink = GST_FAKESINK (GST_OBJECT_PARENT (pad));
GST_STREAM_LOCK (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");
}
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_EOS:
{
GstFlowReturn ret;
ret = gst_fakesink_finish_preroll (fakesink, pad);
if (ret == GST_FLOW_OK) {
fakesink->eos = TRUE;
/* ok, we can post the message */
gst_element_post_message (GST_ELEMENT (fakesink),
gst_message_new_eos (GST_OBJECT (fakesink)));
}
break;
}
case GST_EVENT_DISCONTINUOUS:
if (fakesink->sync && fakesink->clock) {
//gint64 value = GST_EVENT_DISCONT_OFFSET (event, 0).value;
}
default:
result = gst_pad_event_default (pad, event);
break;
}
GST_STREAM_UNLOCK (pad);
return result;
}
static GstFlowReturn
gst_fakesink_chain_unlocked (GstPad * pad, GstBuffer * buf)
gst_fakesink_preroll (GstBaseSink * bsink, GstBuffer * buffer)
{
GstFakeSink *fakesink;
GstFlowReturn result = GST_FLOW_OK;
GstFakeSink *sink = GST_FAKESINK (bsink);
fakesink = GST_FAKESINK (GST_OBJECT_PARENT (pad));
if (!sink->silent) {
g_free (sink->last_message);
result = gst_fakesink_finish_preroll (fakesink, pad);
if (result != GST_FLOW_OK)
goto exit;
sink->last_message = g_strdup_printf ("preroll ******* ");
if (fakesink->sync && fakesink->clock) {
gst_element_wait (GST_ELEMENT (fakesink), GST_BUFFER_TIMESTAMP (buf));
g_object_notify (G_OBJECT (sink), "last_message");
}
return GST_FLOW_OK;
}
if (!fakesink->silent) {
g_free (fakesink->last_message);
static GstFlowReturn
gst_fakesink_render (GstBaseSink * bsink, GstBuffer * buf)
{
GstFakeSink *sink = GST_FAKESINK (bsink);
fakesink->last_message =
g_strdup_printf ("chain ******* (%s:%s)< (%d bytes, timestamp: %"
if (!sink->silent) {
g_free (sink->last_message);
sink->last_message =
g_strdup_printf ("chain ******* < (%d bytes, timestamp: %"
GST_TIME_FORMAT ", duration: %" GST_TIME_FORMAT ", offset: %"
G_GINT64_FORMAT ", offset_end: %" G_GINT64_FORMAT ", flags: %d) %p",
GST_DEBUG_PAD_NAME (pad), GST_BUFFER_SIZE (buf),
GST_BUFFER_SIZE (buf),
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), GST_BUFFER_OFFSET (buf),
GST_BUFFER_OFFSET_END (buf), GST_BUFFER_FLAGS (buf), buf);
g_object_notify (G_OBJECT (fakesink), "last_message");
g_object_notify (G_OBJECT (sink), "last_message");
}
if (sink->signal_handoffs)
g_signal_emit (G_OBJECT (sink), gst_fakesink_signals[SIGNAL_HANDOFF], 0,
buf);
if (fakesink->signal_handoffs)
g_signal_emit (G_OBJECT (fakesink), gst_fakesink_signals[SIGNAL_HANDOFF], 0,
buf, pad);
if (fakesink->dump) {
if (sink->dump) {
gst_util_dump_mem (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
}
exit:
gst_buffer_unref (buf);
return result;
}
static GstFlowReturn
gst_fakesink_chain (GstPad * pad, GstBuffer * buf)
{
GstFlowReturn result;
g_assert (GST_FAKESINK (GST_OBJECT_PARENT (pad))->pad_mode ==
GST_ACTIVATE_PUSH);
GST_STREAM_LOCK (pad);
result = gst_fakesink_chain_unlocked (pad, buf);
GST_STREAM_UNLOCK (pad);
return result;
}
static void
gst_fakesink_loop (GstPad * pad)
{
GstFakeSink *fakesink;
GstBuffer *buf = NULL;
GstFlowReturn result;
fakesink = GST_FAKESINK (GST_OBJECT_PARENT (pad));
g_assert (fakesink->pad_mode == GST_ACTIVATE_PULL);
GST_STREAM_LOCK (pad);
result = gst_pad_pull_range (pad, fakesink->offset, DEFAULT_SIZE, &buf);
if (result != GST_FLOW_OK)
goto paused;
result = gst_fakesink_chain_unlocked (pad, buf);
if (result != GST_FLOW_OK)
goto paused;
exit:
GST_STREAM_UNLOCK (pad);
return;
paused:
gst_task_pause (GST_RPAD_TASK (pad));
goto exit;
}
static gboolean
gst_fakesink_activate (GstPad * pad, GstActivateMode mode)
{
gboolean result = FALSE;
GstFakeSink *fakesink;
fakesink = GST_FAKESINK (GST_OBJECT_PARENT (pad));
switch (mode) {
case GST_ACTIVATE_PUSH:
g_return_val_if_fail (fakesink->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 (fakesink->has_loop, FALSE);
if (GST_ELEMENT_SCHEDULER (fakesink)) {
GST_STREAM_LOCK (pad);
GST_RPAD_TASK (pad) =
gst_scheduler_create_task (GST_ELEMENT_SCHEDULER (fakesink),
(GstTaskFunction) gst_fakesink_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 */
/* 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;
}
fakesink->pad_mode = mode;
return result;
return GST_FLOW_OK;
}
static GstElementStateReturn
@ -628,42 +331,14 @@ gst_fakesink_change_state (GstElement * element)
case GST_STATE_READY_TO_PAUSED:
if (fakesink->state_error == FAKESINK_STATE_ERROR_READY_PAUSED)
goto error;
/* need to complete preroll before this state change completes, there
* is no data flow in READY so we cqn safely assume we need to preroll. */
fakesink->offset = 0;
GST_PREROLL_LOCK (fakesink->sinkpad);
fakesink->need_preroll = TRUE;
fakesink->have_preroll = FALSE;
GST_PREROLL_UNLOCK (fakesink->sinkpad);
ret = GST_STATE_ASYNC;
break;
case GST_STATE_PAUSED_TO_PLAYING:
if (fakesink->state_error == FAKESINK_STATE_ERROR_PAUSED_PLAYING)
goto error;
/* the state change completes when we are blocking on a preroll
* sample */
GST_PREROLL_LOCK (fakesink->sinkpad);
if (!fakesink->have_preroll) {
fakesink->need_preroll = TRUE;
ret = GST_STATE_ASYNC;
} else {
/* now let it play */
GST_PREROLL_SIGNAL (fakesink->sinkpad);
}
GST_PREROLL_UNLOCK (fakesink->sinkpad);
break;
case GST_STATE_PLAYING_TO_PAUSED:
if (fakesink->state_error == FAKESINK_STATE_ERROR_PLAYING_PAUSED)
goto error;
GST_PREROLL_LOCK (fakesink->sinkpad);
fakesink->need_preroll = TRUE;
/* if we don't have a preroll buffer and we have not received EOS,
* we need to wait for a preroll */
if (!fakesink->have_preroll && !fakesink->eos) {
ret = GST_STATE_ASYNC;
}
GST_PREROLL_UNLOCK (fakesink->sinkpad);
break;
case GST_STATE_PAUSED_TO_READY:
if (fakesink->state_error == FAKESINK_STATE_ERROR_PAUSED_READY)
@ -679,7 +354,7 @@ gst_fakesink_change_state (GstElement * element)
break;
}
GST_ELEMENT_CLASS (parent_class)->change_state (element);
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
return ret;

View file

@ -25,6 +25,7 @@
#define __GST_FAKESINK_H__
#include <gst/gst.h>
#include <gst/base/gstbasesink.h>
G_BEGIN_DECLS
@ -54,29 +55,18 @@ typedef struct _GstFakeSink GstFakeSink;
typedef struct _GstFakeSinkClass GstFakeSinkClass;
struct _GstFakeSink {
GstElement element;
GstPad *sinkpad;
GstBaseSink element;
gboolean silent;
gboolean dump;
gboolean has_loop;
gboolean has_chain;
gboolean sync;
gboolean signal_handoffs;
GstClock *clock;
GstFakeSinkStateError state_error;
GstActivateMode pad_mode;
guint64 offset;
gboolean eos;
gboolean need_preroll;
gboolean have_preroll;
gchar *last_message;
};
struct _GstFakeSinkClass {
GstElementClass parent_class;
GstBaseSinkClass parent_class;
/* signals */
void (*handoff) (GstElement *element, GstBuffer *buf, GstPad *pad);