diff --git a/ChangeLog b/ChangeLog index bcb49c601e..4299469338 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,43 @@ +2005-02-23 Wim Taymans + + * 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 * gst/elements/gstidentity.h diff --git a/configure.ac b/configure.ac index 37013cde70..057b5051a2 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/docs/design/part-states.txt b/docs/design/part-states.txt index efd9f8abfe..55a6bfa343 100644 --- a/docs/design/part-states.txt +++ b/docs/design/part-states.txt @@ -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 diff --git a/gst/Makefile.am b/gst/Makefile.am index 8e2c174397..fc7a37e317 100644 --- a/gst/Makefile.am +++ b/gst/Makefile.am @@ -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 diff --git a/gst/base/Makefile.am b/gst/base/Makefile.am new file mode 100644 index 0000000000..613124910f --- /dev/null +++ b/gst/base/Makefile.am @@ -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 + diff --git a/gst/base/gstbasesink.c b/gst/base/gstbasesink.c new file mode 100644 index 0000000000..d7b228b7a2 --- /dev/null +++ b/gst/base/gstbasesink.c @@ -0,0 +1,550 @@ +/* GStreamer + * Copyright (C) 2005 Wim Taymans + * + * 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 + +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; +} diff --git a/gst/base/gstbasesink.h b/gst/base/gstbasesink.h new file mode 100644 index 0000000000..6717a4fde5 --- /dev/null +++ b/gst/base/gstbasesink.h @@ -0,0 +1,79 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen + * 2000 Wim Taymans + * + * 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 + +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__ */ diff --git a/gst/elements/Makefile.am b/gst/elements/Makefile.am index 057cfedbe1..705433a258 100644 --- a/gst/elements/Makefile.am +++ b/gst/elements/Makefile.am @@ -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 \ diff --git a/gst/elements/gstfakesink.c b/gst/elements/gstfakesink.c index 0b7a0bba05..4b0fcefb28 100644 --- a/gst/elements/gstfakesink.c +++ b/gst/elements/gstfakesink.c @@ -1,6 +1,6 @@ /* GStreamer * Copyright (C) 1999,2000 Erik Walthinsen - * 2000 Wim Taymans + * 2005 Wim Taymans * * 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 , Tim Waymans "); + "Erik Walthinsen , " + "Wim Taymans , " + "Mr. 'frag-me-more' Vanderwingo "); /* 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; diff --git a/gst/elements/gstfakesink.h b/gst/elements/gstfakesink.h index 73b1f70d9c..ecb7e9e76a 100644 --- a/gst/elements/gstfakesink.h +++ b/gst/elements/gstfakesink.h @@ -25,6 +25,7 @@ #define __GST_FAKESINK_H__ #include +#include 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); diff --git a/gst/gstelement.c b/gst/gstelement.c index 7c91ffe2f5..65bbf3923d 100644 --- a/gst/gstelement.c +++ b/gst/gstelement.c @@ -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; diff --git a/gst/gstevent.h b/gst/gstevent.h index a0dfa81c0f..c8a6dbdd9e 100644 --- a/gst/gstevent.h +++ b/gst/gstevent.h @@ -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, diff --git a/gst/gstiterator.c b/gst/gstiterator.c index a6a39c6a6f..6a7af84943 100644 --- a/gst/gstiterator.c +++ b/gst/gstiterator.c @@ -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; diff --git a/gst/gstmessage.h b/gst/gstmessage.h index 16af4c6592..79385bd7ca 100644 --- a/gst/gstmessage.h +++ b/gst/gstmessage.h @@ -28,20 +28,42 @@ #include #include -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)) diff --git a/gst/gsttrashstack.h b/gst/gsttrashstack.h index f4682144d8..7f1c2262b5 100644 --- a/gst/gsttrashstack.h +++ b/gst/gsttrashstack.h @@ -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" diff --git a/libs/gst/base/Makefile.am b/libs/gst/base/Makefile.am new file mode 100644 index 0000000000..613124910f --- /dev/null +++ b/libs/gst/base/Makefile.am @@ -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 + diff --git a/libs/gst/base/gstbasesink.c b/libs/gst/base/gstbasesink.c new file mode 100644 index 0000000000..d7b228b7a2 --- /dev/null +++ b/libs/gst/base/gstbasesink.c @@ -0,0 +1,550 @@ +/* GStreamer + * Copyright (C) 2005 Wim Taymans + * + * 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 + +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; +} diff --git a/libs/gst/base/gstbasesink.h b/libs/gst/base/gstbasesink.h new file mode 100644 index 0000000000..6717a4fde5 --- /dev/null +++ b/libs/gst/base/gstbasesink.h @@ -0,0 +1,79 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen + * 2000 Wim Taymans + * + * 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 + +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__ */ diff --git a/plugins/elements/Makefile.am b/plugins/elements/Makefile.am index 057cfedbe1..705433a258 100644 --- a/plugins/elements/Makefile.am +++ b/plugins/elements/Makefile.am @@ -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 \ diff --git a/plugins/elements/gstfakesink.c b/plugins/elements/gstfakesink.c index 0b7a0bba05..4b0fcefb28 100644 --- a/plugins/elements/gstfakesink.c +++ b/plugins/elements/gstfakesink.c @@ -1,6 +1,6 @@ /* GStreamer * Copyright (C) 1999,2000 Erik Walthinsen - * 2000 Wim Taymans + * 2005 Wim Taymans * * 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 , Tim Waymans "); + "Erik Walthinsen , " + "Wim Taymans , " + "Mr. 'frag-me-more' Vanderwingo "); /* 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; diff --git a/plugins/elements/gstfakesink.h b/plugins/elements/gstfakesink.h index 73b1f70d9c..ecb7e9e76a 100644 --- a/plugins/elements/gstfakesink.h +++ b/plugins/elements/gstfakesink.h @@ -25,6 +25,7 @@ #define __GST_FAKESINK_H__ #include +#include 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);