* Boston, MA 02111-1307, USA.
*/
+/**
+ * SECTION:gstelement
+ * @short_description: Abstract base class for all pipeline elements
+ * @see_also: #GstElementFactory, #GstPad
+ *
+ * GstElement is the abstract base class needed to construct an element that
+ * can be used in a GStreamer pipeline. Please refer to the plugin writers
+ * guide for more information on creating #GstElement subclasses.
+ *
+ * The name of a #GstElement can be get with gst_element_get_name() and set with
+ * gst_element_set_name(). For speed, GST_ELEMENT_NAME() can be used in the
+ * core when using the appropriate locking. Do not use this in plug-ins or
+ * applications in order to retain ABI compatibility.
+ *
+ * All elements have pads (of the type #GstPad). These pads link to pads on
+ * other elements. #GstBuffer flow between these linked pads.
+ * A #GstElement has a #GList of #GstPad structures for all their input (or sink)
+ * and output (or source) pads.
+ * Core and plug-in writers can add and remove pads with gst_element_add_pad()
+ * and gst_element_remove_pad().
+ *
+ * A pad of an element can be retrieved by name with gst_element_get_pad().
+ * An iterator of all pads can be retrieved with gst_element_iterate_pads().
+ *
+ * Elements can be linked through their pads.
+ * If the link is straightforward, use the gst_element_link()
+ * convenience function to link two elements, or gst_element_link_many()
+ * for more elements in a row.
+ * Use gst_element_link_filtered() to link two elements constrained by
+ * a specified set of #GstCaps.
+ * For finer control, use gst_element_link_pads() and
+ * gst_element_link_pads_filtered() to specify the pads to link on
+ * each element by name.
+ *
+ * Each element has a state (see #GstState). You can get and set the state
+ * of an element with gst_element_get_state() and gst_element_set_state().
+ * To get a string representation of a #GstState, use
+ * gst_element_state_get_name().
+ *
+ * You can get and set a #GstClock on an element using gst_element_get_clock()
+ * and gst_element_set_clock().
+ * Some elements can provide a clock for the pipeline if
+ * gst_element_provides_clock() returns %TRUE. With the
+ * gst_element_provide_clock() method one can retrieve the clock provided by
+ * such an element.
+ * Not all elements require a clock to operate correctly. If
+ * gst_element_requires_clock() returns %TRUE, a clock should be set on the
+ * element with gst_element_set_clock().
+ *
+ * Note that clock slection and distribution is normally handled by the
+ * toplevel #GstPipeline so the clock functions are only to be used in very
+ * specific situations.
+ *
+ * Last reviewed on 2006-03-12 (0.10.5)
+ */
+
#include "gst_private.h"
#include <glib.h>
#include <stdarg.h>
#include "gstbus.h"
#include "gstmarshal.h"
#include "gsterror.h"
-#include "gstscheduler.h"
#include "gstevent.h"
#include "gstutils.h"
#include "gstinfo.h"
/* Element signals and args */
enum
{
- STATE_CHANGE,
NEW_PAD,
PAD_REMOVED,
NO_MORE_PADS,
static void gst_element_dispose (GObject * object);
static void gst_element_finalize (GObject * object);
-static GstElementStateReturn gst_element_change_state (GstElement * element);
-static GstElementStateReturn gst_element_get_state_func (GstElement * element,
- GstElementState * state, GstElementState * pending, GTimeVal * timeout);
-static void gst_element_set_manager_func (GstElement * element,
- GstPipeline * manager);
+static GstStateChangeReturn gst_element_change_state (GstElement * element,
+ GstStateChange transition);
+static GstStateChangeReturn gst_element_change_state_func (GstElement * element,
+ GstStateChange transition);
+static GstStateChangeReturn gst_element_get_state_func (GstElement * element,
+ GstState * state, GstState * pending, GstClockTime timeout);
+static GstStateChangeReturn gst_element_set_state_func (GstElement * element,
+ GstState state);
static void gst_element_set_bus_func (GstElement * element, GstBus * bus);
-static void gst_element_set_scheduler_func (GstElement * element,
- GstScheduler * scheduler);
#ifndef GST_DISABLE_LOADSAVE
static xmlNodePtr gst_element_save_thyself (GstObject * object,
static void gst_element_restore_thyself (GstObject * parent, xmlNodePtr self);
#endif
-GType _gst_element_type = 0;
-
static GstObjectClass *parent_class = NULL;
static guint gst_element_signals[LAST_SIGNAL] = { 0 };
GType
gst_element_get_type (void)
{
- if (!_gst_element_type) {
+ static GType gst_element_type = 0;
+
+ if (G_UNLIKELY (gst_element_type == 0)) {
static const GTypeInfo element_info = {
sizeof (GstElementClass),
gst_element_base_class_init,
NULL
};
- _gst_element_type = g_type_register_static (GST_TYPE_OBJECT, "GstElement",
+ gst_element_type = g_type_register_static (GST_TYPE_OBJECT, "GstElement",
&element_info, G_TYPE_FLAG_ABSTRACT);
}
- return _gst_element_type;
+ return gst_element_type;
}
static void
parent_class = g_type_class_ref (GST_TYPE_OBJECT);
/**
- * GstElement::state-change:
+ * GstElement::pad-added:
* @gstelement: the object which received the signal
- * @int:
- * @int:
+ * @new_pad: the pad that has been added
*
- * the #GstElementState of the element has been changed
- */
- gst_element_signals[STATE_CHANGE] =
- g_signal_new ("state-change", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstElementClass, state_change), NULL,
- NULL, gst_marshal_VOID__INT_INT, G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
- /**
- * GstElement::new-pad:
- * @gstelement: the object which received the signal
- * @object:
- *
- * a new #GstPad has been added to the element
+ * a new #GstPad has been added to the element.
*/
gst_element_signals[NEW_PAD] =
- g_signal_new ("new-pad", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GstElementClass, new_pad), NULL, NULL,
+ g_signal_new ("pad-added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GstElementClass, pad_added), NULL, NULL,
gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT);
/**
* GstElement::pad-removed:
* @gstelement: the object which received the signal
- * @object:
+ * @old_pad: the pad that has been removed
*
* a #GstPad has been removed from the element
*/
* GstElement::no-more-pads:
* @gstelement: the object which received the signal
*
- * ?
+ * This signals that the element will not generate more dynamic pads.
*/
gst_element_signals[NO_MORE_PADS] =
g_signal_new ("no-more-pads", G_TYPE_FROM_CLASS (klass),
GST_DEBUG_FUNCPTR (gst_element_restore_thyself);
#endif
- klass->change_state = GST_DEBUG_FUNCPTR (gst_element_change_state);
+ klass->change_state = GST_DEBUG_FUNCPTR (gst_element_change_state_func);
+ klass->set_state = GST_DEBUG_FUNCPTR (gst_element_set_state_func);
klass->get_state = GST_DEBUG_FUNCPTR (gst_element_get_state_func);
- klass->set_manager = GST_DEBUG_FUNCPTR (gst_element_set_manager_func);
klass->set_bus = GST_DEBUG_FUNCPTR (gst_element_set_bus_func);
- klass->set_scheduler = GST_DEBUG_FUNCPTR (gst_element_set_scheduler_func);
klass->numpadtemplates = 0;
klass->elementfactory = NULL;
static void
gst_element_init (GstElement * element)
{
- element->current_state = GST_STATE_NULL;
- element->pending_state = GST_STATE_VOID_PENDING;
- element->numpads = 0;
- element->numsrcpads = 0;
- element->numsinkpads = 0;
- element->pads_cookie = 0;
- element->pads = NULL;
- element->srcpads = NULL;
- element->sinkpads = NULL;
- element->manager = NULL;
- element->clock = NULL;
- element->sched_private = NULL;
- element->state_lock = g_mutex_new ();
+ GST_STATE (element) = GST_STATE_NULL;
+ GST_STATE_NEXT (element) = GST_STATE_VOID_PENDING;
+ GST_STATE_PENDING (element) = GST_STATE_VOID_PENDING;
+ GST_STATE_RETURN (element) = GST_STATE_CHANGE_SUCCESS;
+
+ element->state_lock = g_new0 (GStaticRecMutex, 1);
+ g_static_rec_mutex_init (element->state_lock);
element->state_cond = g_cond_new ();
}
gboolean
gst_element_requires_clock (GstElement * element)
{
- gboolean result = FALSE;
+ gboolean result;
- g_return_val_if_fail (GST_IS_ELEMENT (element), result);
+ g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
result = (GST_ELEMENT_GET_CLASS (element)->set_clock != NULL);
* gst_element_provides_clock:
* @element: a #GstElement to query
*
- * Query if the element provides a clock.
+ * Query if the element provides a clock. A #GstClock provided by an
+ * element can be used as the global #GstClock for the pipeline.
*
* Returns: TRUE if the element provides a clock
*
gboolean
gst_element_provides_clock (GstElement * element)
{
- gboolean result = FALSE;
+ gboolean result;
g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
- result = (GST_ELEMENT_GET_CLASS (element)->get_clock != NULL);
+ result = (GST_ELEMENT_GET_CLASS (element)->provide_clock != NULL);
+
+ return result;
+}
+
+/**
+ * gst_element_provide_clock:
+ * @element: a #GstElement to query
+ *
+ * Get the clock provided by the given element.
+ *
+ * Returns: the GstClock provided by the element or NULL
+ * if no clock could be provided. Unref after usage.
+ *
+ * MT safe.
+ */
+GstClock *
+gst_element_provide_clock (GstElement * element)
+{
+ GstClock *result = NULL;
+ GstElementClass *oclass;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
+
+ oclass = GST_ELEMENT_GET_CLASS (element);
+
+ if (oclass->provide_clock)
+ result = oclass->provide_clock (element);
return result;
}
* refcount on the clock. Any previously set clock on the object
* is unreffed.
*
+ * Returns: TRUE if the element accepted the clock.
+ *
* MT safe.
*/
-void
+gboolean
gst_element_set_clock (GstElement * element, GstClock * clock)
{
GstElementClass *oclass;
+ gboolean res = TRUE;
- g_return_if_fail (GST_IS_ELEMENT (element));
+ g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
oclass = GST_ELEMENT_GET_CLASS (element);
+ GST_DEBUG_OBJECT (element, "setting clock %p", clock);
+
if (oclass->set_clock)
- oclass->set_clock (element, clock);
+ res = oclass->set_clock (element, clock);
- GST_LOCK (element);
- gst_object_replace ((GstObject **) & element->clock, (GstObject *) clock);
- GST_UNLOCK (element);
+ if (res) {
+ GST_OBJECT_LOCK (element);
+ gst_object_replace ((GstObject **) & element->clock, (GstObject *) clock);
+ GST_OBJECT_UNLOCK (element);
+ }
+ return res;
}
/**
* gst_element_get_clock:
* @element: a #GstElement to get the clock of.
*
- * Gets the clock of the element. If the element provides a clock,
- * this function will return this clock. For elements that do not
- * provide a clock, this function returns NULL.
+ * Gets the currently configured clock of the element.
*
* Returns: the #GstClock of the element. unref after usage.
*
GstClock *
gst_element_get_clock (GstElement * element)
{
- GstElementClass *oclass;
+ GstClock *result;
g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
- oclass = GST_ELEMENT_GET_CLASS (element);
+ GST_OBJECT_LOCK (element);
+ if ((result = element->clock))
+ gst_object_ref (result);
+ GST_OBJECT_UNLOCK (element);
- if (oclass->get_clock)
- return oclass->get_clock (element);
+ return result;
+}
- return NULL;
+/**
+ * gst_element_set_base_time:
+ * @element: a #GstElement.
+ * @time: the base time to set.
+ *
+ * Set the base time of an element. See gst_element_get_base_time().
+ *
+ * MT safe.
+ */
+void
+gst_element_set_base_time (GstElement * element, GstClockTime time)
+{
+ g_return_if_fail (GST_IS_ELEMENT (element));
+
+ GST_OBJECT_LOCK (element);
+ element->base_time = time;
+ GST_OBJECT_UNLOCK (element);
+
+ GST_DEBUG_OBJECT (element, "set base_time=%" GST_TIME_FORMAT,
+ GST_TIME_ARGS (time));
+}
+
+/**
+ * gst_element_get_base_time:
+ * @element: a #GstElement.
+ *
+ * Returns the base time of the element. The base time is the
+ * absolute time of the clock when this element was last put to
+ * PLAYING. Substracting the base time from the clock time gives
+ * the stream time of the element.
+ *
+ * Returns: the base time of the element.
+ *
+ * MT safe.
+ */
+GstClockTime
+gst_element_get_base_time (GstElement * element)
+{
+ GstClockTime result;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), GST_CLOCK_TIME_NONE);
+
+ GST_OBJECT_LOCK (element);
+ result = element->base_time;
+ GST_OBJECT_UNLOCK (element);
+
+ return result;
}
#ifndef GST_DISABLE_INDEX
gboolean
gst_element_is_indexable (GstElement * element)
{
- gboolean result = FALSE;
+ gboolean result;
- g_return_val_if_fail (GST_IS_ELEMENT (element), result);
+ g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
result = (GST_ELEMENT_GET_CLASS (element)->set_index != NULL);
* @element: a #GstElement.
* @index: a #GstIndex.
*
- * Set the specified GstIndex on the element.
+ * Set @index on the element. The refcount of the index
+ * will be increased, any previously set index is unreffed.
*
* MT safe.
*/
* see gst_object_set_parent() for refcounting information.
*
* Pads are not automatically activated so elements should perform the needed
- * steps to activate the pad.
+ * steps to activate the pad in case this pad is added in the PAUSED or PLAYING
+ * state.
*
* The pad and the element should be unlocked when calling this function.
*
* Returns: TRUE if the pad could be added. This function can fail when
- * passing bad arguments or when a pad with the same name already existed.
+ * a pad with the same name already existed or the pad already had another
+ * parent.
*
* MT safe.
*/
g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
/* locking pad to look at the name */
- GST_LOCK (pad);
+ GST_OBJECT_LOCK (pad);
pad_name = g_strdup (GST_PAD_NAME (pad));
GST_CAT_INFO_OBJECT (GST_CAT_ELEMENT_PADS, element, "adding pad '%s'",
GST_STR_NULL (pad_name));
- GST_UNLOCK (pad);
+ GST_OBJECT_UNLOCK (pad);
/* then check to see if there's already a pad by that name here */
- GST_LOCK (element);
+ GST_OBJECT_LOCK (element);
if (G_UNLIKELY (!gst_object_check_uniqueness (element->pads, pad_name)))
goto name_exists;
element->numsinkpads++;
break;
default:
- /* can happen for ghost pads */
- g_warning ("adding pad %s:%s wothout direction",
- GST_DEBUG_PAD_NAME (pad));
- break;
+ goto no_direction;
}
element->pads = g_list_prepend (element->pads, pad);
element->numpads++;
element->pads_cookie++;
- GST_UNLOCK (element);
+ GST_OBJECT_UNLOCK (element);
/* emit the NEW_PAD signal */
g_signal_emit (G_OBJECT (element), gst_element_signals[NEW_PAD], 0, pad);
{
g_critical ("Padname %s is not unique in element %s, not adding",
pad_name, GST_ELEMENT_NAME (element));
- GST_UNLOCK (element);
+ GST_OBJECT_UNLOCK (element);
g_free (pad_name);
return FALSE;
}
g_critical
("Pad %s already has parent when trying to add to element %s",
pad_name, GST_ELEMENT_NAME (element));
- GST_UNLOCK (element);
+ GST_OBJECT_UNLOCK (element);
g_free (pad_name);
return FALSE;
}
-}
-
-/**
- * gst_element_add_ghost_pad:
- * @element: a #GstElement to add the ghost pad to.
- * @pad: the #GstPad from which the new ghost pad will be created.
- * @name: the name of the new ghost pad, or NULL to assign a unique name
- * automatically.
- *
- * Creates a ghost pad from @pad, and adds it to @element via
- * gst_element_add_pad().
- *
- * Returns: the added ghost #GstPad, or NULL on error.
- *
- * MT safe.
- */
-GstPad *
-gst_element_add_ghost_pad (GstElement * element, GstPad * pad,
- const gchar * name)
-{
- GstPad *ghostpad;
-
- g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
- g_return_val_if_fail (GST_IS_PAD (pad), NULL);
-
- ghostpad = gst_ghost_pad_new (name, pad);
-
- if (!gst_element_add_pad (element, ghostpad)) {
- gst_object_unref (GST_OBJECT (ghostpad));
- ghostpad = NULL;
+no_direction:
+ {
+ GST_OBJECT_LOCK (pad);
+ g_critical
+ ("Trying to add pad %s to element %s, but it has no direction",
+ GST_OBJECT_NAME (pad), GST_ELEMENT_NAME (element));
+ GST_OBJECT_UNLOCK (pad);
+ GST_OBJECT_UNLOCK (element);
+ return FALSE;
}
-
- return ghostpad;
}
/**
* referenced elsewhere.
*
* Returns: TRUE if the pad could be removed. Can return FALSE if the
- * pad is not belonging to the provided element or when wrong parameters
- * are passed to this function.
+ * pad is not belonging to the provided element.
*
* MT safe.
*/
gboolean
gst_element_remove_pad (GstElement * element, GstPad * pad)
{
- gchar *pad_name;
+ GstPad *peer;
g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
/* locking pad to look at the name and parent */
- GST_LOCK (pad);
- pad_name = g_strdup (GST_PAD_NAME (pad));
-
+ GST_OBJECT_LOCK (pad);
GST_CAT_INFO_OBJECT (GST_CAT_ELEMENT_PADS, element, "removing pad '%s'",
- GST_STR_NULL (pad_name));
+ GST_STR_NULL (GST_PAD_NAME (pad)));
if (G_UNLIKELY (GST_PAD_PARENT (pad) != element))
goto not_our_pad;
- GST_UNLOCK (pad);
-
- g_free (pad_name);
-
- /* FIXME, is this redundant with pad disposal? */
- if (GST_IS_REAL_PAD (pad)) {
- GstPad *peer = gst_pad_get_peer (pad);
-
- /* unlink */
- if (peer != NULL) {
- /* window for MT unsafeness, someone else could unlink here
- * and then we call unlink with wrong pads. The unlink
- * function would catch this and safely return failed. */
- if (GST_PAD_IS_SRC (pad))
- gst_pad_unlink (pad, GST_PAD_CAST (peer));
- else
- gst_pad_unlink (GST_PAD_CAST (peer), pad);
+ GST_OBJECT_UNLOCK (pad);
+
+ /* unlink */
+ if ((peer = gst_pad_get_peer (pad))) {
+ /* window for MT unsafeness, someone else could unlink here
+ * and then we call unlink with wrong pads. The unlink
+ * function would catch this and safely return failed. */
+ if (GST_PAD_IS_SRC (pad))
+ gst_pad_unlink (pad, peer);
+ else
+ gst_pad_unlink (peer, pad);
- gst_object_unref (GST_OBJECT (peer));
- }
- } else if (GST_IS_GHOST_PAD (pad)) {
- g_object_set (pad, "real-pad", NULL, NULL);
+ gst_object_unref (peer);
}
- GST_LOCK (element);
+ GST_OBJECT_LOCK (element);
/* remove it from the list */
switch (gst_pad_get_direction (pad)) {
case GST_PAD_SRC:
element->numsinkpads--;
break;
default:
- /* can happen for ghost pads */
+ g_critical ("Removing pad without direction???");
break;
}
element->pads = g_list_remove (element->pads, pad);
element->numpads--;
element->pads_cookie++;
- GST_UNLOCK (element);
+ GST_OBJECT_UNLOCK (element);
g_signal_emit (G_OBJECT (element), gst_element_signals[PAD_REMOVED], 0, pad);
not_our_pad:
{
/* FIXME, locking order? */
- GST_LOCK (element);
+ GST_OBJECT_LOCK (element);
g_critical ("Padname %s:%s does not belong to element %s when removing",
GST_ELEMENT_NAME (GST_PAD_PARENT (pad)), GST_PAD_NAME (pad),
GST_ELEMENT_NAME (element));
- GST_UNLOCK (element);
- GST_UNLOCK (pad);
- g_free (pad_name);
+ GST_OBJECT_UNLOCK (element);
+ GST_OBJECT_UNLOCK (pad);
return FALSE;
}
}
*
* Use this function to signal that the element does not expect any more pads
* to show up in the current pipeline. This function should be called whenever
- * pads have been added by the element itself. Elements with GST_PAD_SOMETIMES
+ * pads have been added by the element itself. Elements with #GST_PAD_SOMETIMES
* pad templates use this in combination with autopluggers to figure out that
* the element is done initializing its pads.
*
{
gint result;
- GST_LOCK (pad1);
+ GST_OBJECT_LOCK (pad1);
result = strcmp (GST_PAD_NAME (pad1), name);
- GST_UNLOCK (pad1);
+ GST_OBJECT_UNLOCK (pad1);
return result;
}
g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
g_return_val_if_fail (name != NULL, NULL);
- GST_LOCK (element);
+ GST_OBJECT_LOCK (element);
find =
g_list_find_custom (element->pads, name, (GCompareFunc) pad_compare_name);
if (find) {
result = GST_PAD_CAST (find->data);
- gst_object_ref (GST_OBJECT_CAST (result));
+ gst_object_ref (result);
}
if (result == NULL) {
GST_CAT_INFO (GST_CAT_ELEMENT_PADS, "found pad %s:%s",
GST_ELEMENT_NAME (element), name);
}
- GST_UNLOCK (element);
+ GST_OBJECT_UNLOCK (element);
return result;
}
newpad = (oclass->request_new_pad) (element, templ, name);
if (newpad)
- gst_object_ref (GST_OBJECT (newpad));
+ gst_object_ref (newpad);
return newpad;
}
return pad;
}
-GstIteratorItem
+static GstIteratorItem
iterate_pad (GstIterator * it, GstPad * pad)
{
- gst_object_ref (GST_OBJECT_CAST (pad));
+ gst_object_ref (pad);
return GST_ITERATOR_ITEM_PASS;
}
+static GstIterator *
+gst_element_iterate_pad_list (GstElement * element, GList ** padlist)
+{
+ GstIterator *result;
+
+ GST_OBJECT_LOCK (element);
+ gst_object_ref (element);
+ result = gst_iterator_new_list (GST_TYPE_PAD,
+ GST_OBJECT_GET_LOCK (element),
+ &element->pads_cookie,
+ padlist,
+ element,
+ (GstIteratorItemFunction) iterate_pad,
+ (GstIteratorDisposeFunction) gst_object_unref);
+ GST_OBJECT_UNLOCK (element);
+
+ return result;
+}
+
/**
* gst_element_iterate_pads:
* @element: a #GstElement to iterate pads of.
*
- * Retrieves an iterattor of @element's pads.
+ * Retrieves an iterattor of @element's pads. The iterator should
+ * be freed after usage.
*
- * Returns: the #GstIterator of #GstPad. unref each pad after usage.
+ * Returns: the #GstIterator of #GstPad. Unref each pad after use.
*
* MT safe.
*/
GstIterator *
gst_element_iterate_pads (GstElement * element)
{
- GstIterator *result;
-
g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
- GST_LOCK (element);
- gst_object_ref (GST_OBJECT (element));
- result = gst_iterator_new_list (GST_GET_LOCK (element),
- &element->pads_cookie,
- &element->pads,
- element,
- (GstIteratorItemFunction) iterate_pad,
- (GstIteratorDisposeFunction) gst_object_unref);
- GST_UNLOCK (element);
+ return gst_element_iterate_pad_list (element, &element->pads);
+}
- return result;
+/**
+ * gst_element_iterate_src_pads:
+ * @element: a #GstElement.
+ *
+ * Retrieves an iterator of @element's source pads.
+ *
+ * Returns: the #GstIterator of #GstPad. Unref each pad after use.
+ *
+ * MT safe.
+ */
+GstIterator *
+gst_element_iterate_src_pads (GstElement * element)
+{
+ g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
+
+ return gst_element_iterate_pad_list (element, &element->srcpads);
}
+/**
+ * gst_element_iterate_sink_pads:
+ * @element: a #GstElement.
+ *
+ * Retrieves an iterator of @element's sink pads.
+ *
+ * Returns: the #GstIterator of #GstPad. Unref each pad after use.
+ *
+ * MT safe.
+ */
+GstIterator *
+gst_element_iterate_sink_pads (GstElement * element)
+{
+ g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
+
+ return gst_element_iterate_pad_list (element, &element->sinkpads);
+}
/**
* gst_element_class_add_pad_template:
templ->name_template) == NULL);
klass->padtemplates = g_list_append (klass->padtemplates,
- gst_object_ref (GST_OBJECT (templ)));
+ gst_object_ref (templ));
klass->numpadtemplates++;
}
switch (dir) {
case GST_PAD_SRC:
- GST_LOCK (element);
+ GST_OBJECT_LOCK (element);
pads = element->srcpads;
break;
case GST_PAD_SINK:
- GST_LOCK (element);
+ GST_OBJECT_LOCK (element);
pads = element->sinkpads;
break;
default:
for (; pads; pads = g_list_next (pads)) {
GstPad *pad = GST_PAD (pads->data);
- GST_LOCK (pad);
+ GST_OBJECT_LOCK (pad);
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "checking pad %s:%s",
GST_DEBUG_PAD_NAME (pad));
if (GST_PAD_IS_LINKED (pad)) {
- GST_UNLOCK (pad);
+ GST_OBJECT_UNLOCK (pad);
result = pad;
break;
} else {
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "pad %s:%s is not linked",
GST_DEBUG_PAD_NAME (pad));
}
- GST_UNLOCK (pad);
+ GST_OBJECT_UNLOCK (pad);
}
if (result)
- gst_object_ref (GST_OBJECT (result));
+ gst_object_ref (result);
- GST_UNLOCK (element);
+ GST_OBJECT_UNLOCK (element);
return result;
/* ERROR handling */
wrong_direction:
{
- g_warning ("unknown pad direction");
+ g_warning ("unknown pad direction %d", dir);
return NULL;
}
}
* Sends an event to an element. If the element doesn't
* implement an event handler, the event will be forwarded
* to a random sink pad. This function takes owership of the
- * provided event so you should _ref it if you want to reuse
+ * provided event so you should gst_event_ref() it if you want to reuse
* the event after this call.
*
* Returns: TRUE if the event was handled.
oclass = GST_ELEMENT_GET_CLASS (element);
if (oclass->send_event) {
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "send %s event on element %s",
+ GST_EVENT_TYPE_NAME (event), GST_ELEMENT_NAME (element));
result = oclass->send_event (element, event);
} else {
- GstPad *pad = gst_element_get_random_pad (element, GST_PAD_SINK);
+ GstPad *pad = GST_EVENT_IS_DOWNSTREAM (event) ?
+ gst_element_get_random_pad (element, GST_PAD_SRC) :
+ gst_element_get_random_pad (element, GST_PAD_SINK);
if (pad) {
- GstPad *peer = gst_pad_get_peer (pad);
-
- if (peer) {
- GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
- "sending event to random pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
+ "pushing %s event to random %s pad %s:%s",
+ GST_EVENT_TYPE_NAME (event),
+ (GST_PAD_DIRECTION (pad) == GST_PAD_SRC ? "src" : "sink"),
+ GST_DEBUG_PAD_NAME (pad));
- result = gst_pad_send_event (peer, event);
- gst_object_unref (GST_OBJECT (peer));
- }
- gst_object_unref (GST_OBJECT (pad));
+ result = gst_pad_push_event (pad, event);
+ gst_object_unref (pad);
+ } else {
+ GST_CAT_INFO (GST_CAT_ELEMENT_PADS, "can't send %s event on element %s",
+ GST_EVENT_TYPE_NAME (event), GST_ELEMENT_NAME (element));
}
}
- GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "can't send event on element %s",
- GST_ELEMENT_NAME (element));
-
return result;
}
/**
* gst_element_seek:
* @element: a #GstElement to send the event to.
- * @seek_type: the method to use for seeking.
- * @offset: the offset to seek to.
+ * @rate: The new playback rate
+ * @format: The format of the seek values
+ * @flags: The optional seek flags.
+ * @cur_type: The type and flags for the new current position
+ * @cur: The value of the new current position
+ * @stop_type: The type and flags for the new stop position
+ * @stop: The value of the new stop position
*
* Sends a seek event to an element.
*
- * Returns: TRUE if the event was handled.
+ * Returns: %TRUE if the event was handled.
*
* MT safe.
*/
gboolean
-gst_element_seek (GstElement * element, GstSeekType seek_type, guint64 offset)
+gst_element_seek (GstElement * element, gdouble rate, GstFormat format,
+ GstSeekFlags flags, GstSeekType cur_type, gint64 cur,
+ GstSeekType stop_type, gint64 stop)
{
GstEvent *event;
gboolean result;
g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
- event = gst_event_new_seek (seek_type, offset);
+ event =
+ gst_event_new_seek (rate, format, flags, cur_type, cur, stop_type, stop);
result = gst_element_send_event (element, event);
return result;
if (peer) {
result = gst_pad_get_query_types (peer);
- gst_object_unref (GST_OBJECT (peer));
+ gst_object_unref (peer);
}
- gst_object_unref (GST_OBJECT (pad));
+ gst_object_unref (pad);
}
}
return result;
/**
* gst_element_query:
* @element: a #GstElement to perform the query on.
- * @type: the #GstQueryType.
- * @format: the #GstFormat pointer to hold the format of the result.
- * @value: the pointer to the value of the result.
+ * @query: the #GstQuery.
*
* Performs a query on the given element. If the format is set
- * to GST_FORMAT_DEFAULT and this function returns TRUE, the
+ * to #GST_FORMAT_DEFAULT and this function returns TRUE, the
* format pointer will hold the default format.
* For element that don't implement a query handler, this function
* forwards the query to a random usable sinkpad of this element.
oclass = GST_ELEMENT_GET_CLASS (element);
if (oclass->query) {
+ GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "send query on element %s",
+ GST_ELEMENT_NAME (element));
result = oclass->query (element, query);
} else {
GstPad *pad = gst_element_get_random_pad (element, GST_PAD_SRC);
if (pad) {
result = gst_pad_query (pad, query);
- gst_object_unref (GST_OBJECT (pad));
+ gst_object_unref (pad);
} else {
pad = gst_element_get_random_pad (element, GST_PAD_SINK);
if (pad) {
if (peer) {
result = gst_pad_query (peer, query);
- gst_object_unref (GST_OBJECT (peer));
+ gst_object_unref (peer);
}
- gst_object_unref (GST_OBJECT (pad));
+ gst_object_unref (pad);
}
}
}
* @element: a #GstElement posting the message
* @message: a #GstMessage to post
*
- * Post a message on the elements #GstBus.
+ * Post a message on the element's #GstBus. This function takes ownership of the
+ * message; if you want to access the message after this call, you should add an
+ * additional reference before calling.
*
- * Returns: TRUE if the message was successfuly posted.
+ * Returns: TRUE if the message was successfully posted.
*
* MT safe.
*/
GstBus *bus;
gboolean result = FALSE;
- GST_DEBUG ("posting message %p ...", message);
-
g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
g_return_val_if_fail (message != NULL, FALSE);
- GST_LOCK (element);
+ GST_OBJECT_LOCK (element);
bus = element->bus;
- if (G_UNLIKELY (bus == NULL)) {
- GST_DEBUG ("... but I won't because I have no bus");
- GST_UNLOCK (element);
- gst_data_unref (GST_DATA (message));
- return FALSE;
- }
- gst_object_ref (GST_OBJECT (bus));
- GST_DEBUG ("... on bus %p", bus);
- GST_UNLOCK (element);
+ if (G_UNLIKELY (bus == NULL))
+ goto no_bus;
+
+ gst_object_ref (bus);
+ GST_OBJECT_UNLOCK (element);
result = gst_bus_post (bus, message);
- gst_object_unref (GST_OBJECT (bus));
+ gst_object_unref (bus);
return result;
+
+no_bus:
+ {
+ GST_DEBUG ("not posting message %p: no bus", message);
+ GST_OBJECT_UNLOCK (element);
+ gst_message_unref (message);
+ return FALSE;
+ }
}
/**
* _gst_element_error_printf:
* @format: the printf-like format to use, or NULL
*
- * This function is only used internally by the #gst_element_error macro.
+ * This function is only used internally by the gst_element_error() macro.
*
* Returns: a newly allocated string, or NULL if the format was NULL or ""
*
gchar *name;
gchar *sent_text;
gchar *sent_debug;
+ gboolean has_debug = TRUE;
GstMessage *message = NULL;
/* checks */
- GST_DEBUG ("start");
+ GST_DEBUG_OBJECT (element, "start");
g_return_if_fail (GST_IS_ELEMENT (element));
g_return_if_fail ((type == GST_MESSAGE_ERROR) ||
(type == GST_MESSAGE_WARNING));
/* construct a sent_debug with extra information from source */
if ((debug == NULL) || (debug[0] == 0)) {
/* debug could have come from g_strdup_printf (""); */
- sent_debug = NULL;
- } else {
- name = gst_object_get_path_string (GST_OBJECT (element));
- sent_debug = g_strdup_printf ("%s(%d): %s: %s:\n%s",
- file, line, function, name, debug ? debug : "");
- g_free (name);
+ has_debug = FALSE;
}
+
+ name = gst_object_get_path_string (GST_OBJECT (element));
+ if (has_debug)
+ sent_debug = g_strdup_printf ("%s(%d): %s (): %s:\n%s",
+ file, line, function, name, debug);
+ else
+ sent_debug = g_strdup_printf ("%s(%d): %s (): %s",
+ file, line, function, name);
+ g_free (name);
g_free (debug);
/* create gerror and post message */
if (type == GST_MESSAGE_ERROR) {
message = gst_message_new_error (GST_OBJECT (element), gerror, sent_debug);
- } else {
+ } else if (type == GST_MESSAGE_WARNING) {
message = gst_message_new_warning (GST_OBJECT (element), gerror,
sent_debug);
+ } else {
+ g_assert_not_reached ();
}
gst_element_post_message (element, message);
sent_text);
/* cleanup */
+ g_error_free (gerror);
+ g_free (sent_debug);
g_free (sent_text);
- /* sent_debug is not part of the gerror, so don't free it here */
}
/**
* This way you can leave currently unused elements inside bins. Just lock their
* state before changing the state from #GST_STATE_NULL.
*
- * Returns: TRUE, if the element's state is locked.
- *
* MT safe.
+ *
+ * Returns: TRUE, if the element's state is locked.
*/
gboolean
gst_element_is_locked_state (GstElement * element)
{
- gboolean result = FALSE;
+ gboolean result;
g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
- GST_LOCK (element);
- result = GST_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE);
- GST_UNLOCK (element);
+ GST_OBJECT_LOCK (element);
+ result = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE);
+ GST_OBJECT_UNLOCK (element);
return result;
}
* Locks the state of an element, so state changes of the parent don't affect
* this element anymore.
*
- * Returns: TRUE if the state was changed, FALSE if bad params were given or
- * the element was already in the correct state.
- *
* MT safe.
+ *
+ * Returns: TRUE if the state was changed, FALSE if bad parameterss were given
+ * or the elements state-locking needed no change.
*/
gboolean
gst_element_set_locked_state (GstElement * element, gboolean locked_state)
g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
- GST_LOCK (element);
- old = GST_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE);
+ GST_OBJECT_LOCK (element);
+ old = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE);
if (G_UNLIKELY (old == locked_state))
goto was_ok;
if (locked_state) {
GST_CAT_DEBUG (GST_CAT_STATES, "locking state of element %s",
GST_ELEMENT_NAME (element));
- GST_FLAG_SET (element, GST_ELEMENT_LOCKED_STATE);
+ GST_OBJECT_FLAG_SET (element, GST_ELEMENT_LOCKED_STATE);
} else {
GST_CAT_DEBUG (GST_CAT_STATES, "unlocking state of element %s",
GST_ELEMENT_NAME (element));
- GST_FLAG_UNSET (element, GST_ELEMENT_LOCKED_STATE);
+ GST_OBJECT_FLAG_UNSET (element, GST_ELEMENT_LOCKED_STATE);
}
- GST_UNLOCK (element);
+ GST_OBJECT_UNLOCK (element);
return TRUE;
was_ok:
- GST_UNLOCK (element);
+ GST_CAT_DEBUG (GST_CAT_STATES, "elements %s was in locked state %d",
+ GST_ELEMENT_NAME (element), old);
+ GST_OBJECT_UNLOCK (element);
return FALSE;
}
* If this function returns FALSE, the state of element is undefined.
*
* Returns: TRUE, if the element's state could be synced to the parent's state.
+ *
+ * MT safe.
*/
gboolean
gst_element_sync_state_with_parent (GstElement * element)
GstElement *parent;
g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
- parent = GST_ELEMENT (GST_ELEMENT_PARENT (element));
- g_return_val_if_fail (GST_IS_BIN (parent), FALSE);
- GST_CAT_DEBUG (GST_CAT_STATES, "syncing state of element %s (%s) to %s (%s)",
- GST_ELEMENT_NAME (element),
- gst_element_state_get_name (GST_STATE (element)),
- GST_ELEMENT_NAME (parent),
- gst_element_state_get_name (GST_STATE (parent)));
+ if ((parent = GST_ELEMENT_CAST (gst_element_get_parent (element)))) {
+ GstState parent_current, parent_pending;
+ GstStateChangeReturn ret;
+
+ GST_OBJECT_LOCK (parent);
+ parent_current = GST_STATE (parent);
+ parent_pending = GST_STATE_PENDING (parent);
+ GST_OBJECT_UNLOCK (parent);
+
+ GST_CAT_DEBUG (GST_CAT_STATES,
+ "syncing state of element %s (%s) to %s (%s, %s)",
+ GST_ELEMENT_NAME (element),
+ gst_element_state_get_name (GST_STATE (element)),
+ GST_ELEMENT_NAME (parent), gst_element_state_get_name (parent_current),
+ gst_element_state_get_name (parent_pending));
+
+ ret = gst_element_set_state (element, parent_current);
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ goto failed;
+
+ gst_object_unref (parent);
- if (gst_element_set_state (element, GST_STATE (parent)) == GST_STATE_FAILURE) {
+ return TRUE;
+ }
+ return FALSE;
+
+ /* ERROR */
+failed:
+ {
return FALSE;
}
- return TRUE;
}
/* MT safe */
-static GstElementStateReturn
+static GstStateChangeReturn
gst_element_get_state_func (GstElement * element,
- GstElementState * state, GstElementState * pending, GTimeVal * timeout)
+ GstState * state, GstState * pending, GstClockTime timeout)
{
- GstElementStateReturn ret = GST_STATE_FAILURE;
- GstElementState old_pending;
+ GstStateChangeReturn ret = GST_STATE_CHANGE_FAILURE;
+ GstState old_pending;
- g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "getting state");
- GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "getting state");
+ GST_OBJECT_LOCK (element);
+ ret = GST_STATE_RETURN (element);
- GST_STATE_LOCK (element);
/* we got an error, report immediatly */
- if (GST_STATE_ERROR (element))
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ goto done;
+
+ /* we got no_preroll, report immediatly */
+ if (ret == GST_STATE_CHANGE_NO_PREROLL)
+ goto done;
+
+ /* no need to wait async if we are not async */
+ if (ret != GST_STATE_CHANGE_ASYNC)
goto done;
old_pending = GST_STATE_PENDING (element);
if (old_pending != GST_STATE_VOID_PENDING) {
GTimeVal *timeval, abstimeout;
+ guint32 cookie;
+
+ if (timeout != GST_CLOCK_TIME_NONE) {
+ glong add = timeout / 1000;
+
+ if (add == 0)
+ goto done;
- GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "wait for pending");
- if (timeout) {
/* make timeout absolute */
g_get_current_time (&abstimeout);
- g_time_val_add (&abstimeout,
- timeout->tv_sec * G_USEC_PER_SEC + timeout->tv_usec);
+ g_time_val_add (&abstimeout, add);
timeval = &abstimeout;
} else {
timeval = NULL;
}
+ /* get cookie to dected state change during waiting */
+ cookie = element->state_cookie;
+
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+ "waiting for element to commit state");
+
/* we have a pending state change, wait for it to complete */
if (!GST_STATE_TIMED_WAIT (element, timeval)) {
- GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "timeout");
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "timed out");
/* timeout triggered */
- ret = GST_STATE_ASYNC;
+ ret = GST_STATE_CHANGE_ASYNC;
} else {
+ if (cookie != element->state_cookie)
+ goto interrupted;
+
/* could be success or failure */
if (old_pending == GST_STATE (element)) {
- GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "got success");
- ret = GST_STATE_SUCCESS;
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "got success");
+ ret = GST_STATE_CHANGE_SUCCESS;
} else {
- GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "got failure");
- ret = GST_STATE_FAILURE;
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "got failure");
+ ret = GST_STATE_CHANGE_FAILURE;
}
}
}
/* if nothing is pending anymore we can return SUCCESS */
if (GST_STATE_PENDING (element) == GST_STATE_VOID_PENDING) {
- GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "nothing pending");
- ret = GST_STATE_SUCCESS;
+ GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "nothing pending");
+ ret = GST_STATE_CHANGE_SUCCESS;
}
done:
if (pending)
*pending = GST_STATE_PENDING (element);
- GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
- "state current: %s, pending: %s",
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "state current: %s, pending: %s, result: %d",
gst_element_state_get_name (GST_STATE (element)),
- gst_element_state_get_name (GST_STATE_PENDING (element)));
-
- GST_STATE_UNLOCK (element);
+ gst_element_state_get_name (GST_STATE_PENDING (element)), ret);
+ GST_OBJECT_UNLOCK (element);
return ret;
+
+interrupted:
+ {
+ if (state)
+ *state = GST_STATE_VOID_PENDING;
+ if (pending)
+ *pending = GST_STATE_VOID_PENDING;
+
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "get_state() interruped");
+
+ GST_OBJECT_UNLOCK (element);
+
+ return GST_STATE_CHANGE_FAILURE;
+ }
}
/**
* gst_element_get_state:
* @element: a #GstElement to get the state of.
- * @state: a pointer to #GstElementState to hold the state. Can be NULL.
- * @pending: a pointer to #GstElementState to hold the pending state.
- * Can be NULL.
- * @timeout: a #GTimeVal to specify the timeout for an async
- * state change or NULL for infinite timeout.
+ * @state: a pointer to #GstState to hold the state. Can be NULL.
+ * @pending: a pointer to #GstState to hold the pending state.
+ * Can be %NULL.
+ * @timeout: a #GstClockTime to specify the timeout for an async
+ * state change or %GST_CLOCK_TIME_NONE for infinite timeout.
*
- * Gets the state of the element.
+ * Gets the state of the element.
*
- * For elements that performed an ASYNC state change, as reported by
- * #gst_element_set_state(), this function will block up to the
- * specified timeout value for the state change to complete.
+ * For elements that performed an ASYNC state change, as reported by
+ * gst_element_set_state(), this function will block up to the
+ * specified timeout value for the state change to complete.
* If the element completes the state change or goes into
- * an error, this function returns immediatly with a return value of
- * GST_STATE_SUCCESS or GST_STATE_FAILURE respectively.
- *
- * Returns: GST_STATE_SUCCESS if the element has no more pending state and
- * the last state change succeeded, GST_STATE_ASYNC
- * if the element is still performing a state change or
- * GST_STATE_FAILURE if the last state change failed.
+ * an error, this function returns immediately with a return value of
+ * %GST_STATE_CHANGE_SUCCESS or %GST_STATE_CHANGE_FAILURE respectively.
+ *
+ * For elements that did not return %GST_STATE_CHANGE_ASYNC, this function
+ * returns the current and pending state immediately.
+ *
+ * This function returns %GST_STATE_CHANGE_NO_PREROLL if the element
+ * successfully changed its state but is not able to provide data yet.
+ * This mostly
+ * happens for live sources that only produce data in the PLAYING state.
+ * While the state change return is equivalent to %GST_STATE_CHANGE_SUCCESS, it
+ * is returned to the application to signal that some sink elements might not
+ * be able to complete their state change because an element is not producing
+ * data to complete the preroll. When setting the element to playing,
+ * the preroll will complete and playback will start.
+ *
+ * Returns: %GST_STATE_CHANGE_SUCCESS if the element has no more pending state
+ * and the last state change succeeded, %GST_STATE_CHANGE_ASYNC if the
+ * element is still performing a state change or
+ * %GST_STATE_CHANGE_FAILURE if the last state change failed.
*
* MT safe.
*/
-GstElementStateReturn
+GstStateChangeReturn
gst_element_get_state (GstElement * element,
- GstElementState * state, GstElementState * pending, GTimeVal * timeout)
+ GstState * state, GstState * pending, GstClockTime timeout)
{
GstElementClass *oclass;
- GstElementStateReturn result = GST_STATE_FAILURE;
+ GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
- g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_FAILURE);
+ g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_CHANGE_FAILURE);
oclass = GST_ELEMENT_GET_CLASS (element);
* @element: a #GstElement to abort the state of.
*
* Abort the state change of the element. This function is used
- * by elements that do asynchronous state changes and find out
+ * by elements that do asynchronous state changes and find out
* something is wrong.
*
* This function should be called with the STATE_LOCK held.
void
gst_element_abort_state (GstElement * element)
{
- GstElementState pending;
+ GstState pending;
+
+#ifndef GST_DISABLE_GST_DEBUG
+ GstState old_state;
+#endif
g_return_if_fail (GST_IS_ELEMENT (element));
+ GST_OBJECT_LOCK (element);
pending = GST_STATE_PENDING (element);
- if (pending != GST_STATE_VOID_PENDING && !GST_STATE_ERROR (element)) {
+ if (pending == GST_STATE_VOID_PENDING ||
+ GST_STATE_RETURN (element) == GST_STATE_CHANGE_FAILURE)
+ goto nothing_aborted;
+
#ifndef GST_DISABLE_GST_DEBUG
- GstElementState old_state = GST_STATE (element);
+ old_state = GST_STATE (element);
+
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+ "aborting state from %s to %s", gst_element_state_get_name (old_state),
+ gst_element_state_get_name (pending));
#endif
- GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
- "aborting state from %s to %s", gst_element_state_get_name (old_state),
- gst_element_state_get_name (pending));
+ /* flag error */
+ GST_STATE_RETURN (element) = GST_STATE_CHANGE_FAILURE;
- /* flag error */
- GST_STATE_ERROR (element) = TRUE;
+ GST_STATE_BROADCAST (element);
+ GST_OBJECT_UNLOCK (element);
- GST_STATE_BROADCAST (element);
+ return;
+
+nothing_aborted:
+ {
+ GST_OBJECT_UNLOCK (element);
+ return;
}
}
/**
- * gst_element_commit_state:
- * @element: a #GstElement to commit the state of.
- *
- * Commit the state change of the element. This function is used
- * by elements that do asynchronous state changes.
- *
- * This function can only be called with the STATE_LOCK held.
- *
- * MT safe.
+ * gst_element_continue_state:
+ * @element: a #GstElement to continue the state change of.
+ * @ret: The previous state return value
+ *
+ * Commit the state change of the element and proceed to the next
+ * pending state if any. This function is used
+ * by elements that do asynchronous state changes.
+ * The core will normally call this method automatically when an
+ * element returned SUCCESS from the state change function.
+ * Elements that return ASYNC from the change_state function should
+ * eventually call this method from the streaming thread to signal
+ * successfull state change completion.
+ *
+ * If after calling this method the element still has not reached
+ * the pending state, the next state change is performed.
+ *
+ * Returns: The result of the commit state change.
+ *
+ * MT safe.
*/
-void
-gst_element_commit_state (GstElement * element)
+GstStateChangeReturn
+gst_element_continue_state (GstElement * element, GstStateChangeReturn ret)
{
- GstElementState pending;
+ GstState pending;
+ GstState old_ret, old_state, old_next;
+ GstState current, next;
GstMessage *message;
+ GstStateChange transition;
- g_return_if_fail (GST_IS_ELEMENT (element));
-
+ GST_OBJECT_LOCK (element);
+ old_ret = GST_STATE_RETURN (element);
+ GST_STATE_RETURN (element) = ret;
pending = GST_STATE_PENDING (element);
- if (pending != GST_STATE_VOID_PENDING) {
- GstElementState old_state = GST_STATE (element);
+ /* check if there is something to commit */
+ if (pending == GST_STATE_VOID_PENDING)
+ goto nothing_pending;
- GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
- "commiting state from %s to %s", gst_element_state_get_name (old_state),
- gst_element_state_get_name (pending));
+ old_state = GST_STATE (element);
+ /* this is the state we should go to next */
+ old_next = GST_STATE_NEXT (element);
+ /* update current state */
+ current = GST_STATE (element) = old_next;
+
+ /* see if we reached the final state */
+ if (pending == current)
+ goto complete;
+
+ next = GST_STATE_GET_NEXT (current, pending);
+ transition = GST_STATE_TRANSITION (current, next);
+
+ GST_STATE_NEXT (element) = next;
+ /* mark busy */
+ GST_STATE_RETURN (element) = GST_STATE_CHANGE_ASYNC;
+ GST_OBJECT_UNLOCK (element);
+
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+ "committing state from %s to %s, pending %s",
+ gst_element_state_get_name (old_state),
+ gst_element_state_get_name (old_next),
+ gst_element_state_get_name (pending));
+
+ message = gst_message_new_state_changed (GST_OBJECT (element),
+ old_state, old_next, pending);
+ gst_element_post_message (element, message);
- GST_STATE (element) = pending;
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+ "continue state change %s to %s, final %s",
+ gst_element_state_get_name (current),
+ gst_element_state_get_name (next), gst_element_state_get_name (pending));
+
+ ret = gst_element_change_state (element, transition);
+
+ return ret;
+
+nothing_pending:
+ {
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "nothing pending");
+ GST_OBJECT_UNLOCK (element);
+ return ret;
+ }
+complete:
+ {
GST_STATE_PENDING (element) = GST_STATE_VOID_PENDING;
- GST_STATE_ERROR (element) = FALSE;
+ GST_STATE_NEXT (element) = GST_STATE_VOID_PENDING;
+
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "completed state change");
+ GST_OBJECT_UNLOCK (element);
+
+ /* don't post silly messages with the same state. This can happen
+ * when an element state is changed to what it already was. For bins
+ * this can be the result of a lost state, which we check with the
+ * previous return value.
+ * We do signal the cond though as a _get_state() might be blocking
+ * on it. */
+ if (old_state != old_next || old_ret == GST_STATE_CHANGE_ASYNC) {
+ message = gst_message_new_state_changed (GST_OBJECT (element),
+ old_state, old_next, GST_STATE_VOID_PENDING);
+ gst_element_post_message (element, message);
+ }
- g_signal_emit (G_OBJECT (element), gst_element_signals[STATE_CHANGE],
- 0, old_state, pending);
- message = gst_message_new_state_changed (GST_OBJECT (element),
- old_state, pending);
- gst_element_post_message (element, message);
GST_STATE_BROADCAST (element);
+
+ return ret;
}
}
*
* Brings the element to the lost state. The current state of the
* element is copied to the pending state so that any call to
- * #gst_element_get_state() will return ASYNC.
+ * gst_element_get_state() will return ASYNC.
+ *
* This is mostly used for elements that lost their preroll buffer
* in the PAUSED state after a flush, they become PAUSED again
* if a new preroll buffer is queued.
void
gst_element_lost_state (GstElement * element)
{
+ GstState current_state;
+ GstMessage *message;
+
g_return_if_fail (GST_IS_ELEMENT (element));
- if (GST_STATE_PENDING (element) == GST_STATE_VOID_PENDING &&
- !GST_STATE_ERROR (element)) {
- GstElementState current_state = GST_STATE (element);
+ GST_OBJECT_LOCK (element);
+ if (GST_STATE_PENDING (element) != GST_STATE_VOID_PENDING ||
+ GST_STATE_RETURN (element) == GST_STATE_CHANGE_FAILURE)
+ goto nothing_lost;
- GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
- "lost state of %s", gst_element_state_get_name (current_state));
+ current_state = GST_STATE (element);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "lost state of %s", gst_element_state_get_name (current_state));
+
+ GST_STATE_NEXT (element) = current_state;
+ GST_STATE_PENDING (element) = current_state;
+ GST_STATE_RETURN (element) = GST_STATE_CHANGE_ASYNC;
+ GST_OBJECT_UNLOCK (element);
+
+ message = gst_message_new_state_changed (GST_OBJECT (element),
+ current_state, current_state, current_state);
+ gst_element_post_message (element, message);
+
+ /* and mark us dirty */
+ message = gst_message_new_state_dirty (GST_OBJECT (element));
+ gst_element_post_message (element, message);
+
+ return;
- GST_STATE_PENDING (element) = current_state;
- GST_STATE_ERROR (element) = FALSE;
+nothing_lost:
+ {
+ GST_OBJECT_UNLOCK (element);
+ return;
}
}
/**
* gst_element_set_state:
* @element: a #GstElement to change state of.
- * @state: the element's new #GstElementState.
+ * @state: the element's new #GstState.
*
* Sets the state of the element. This function will try to set the
* requested state by going through all the intermediary states and calling
* the class's state change function for each.
*
- * Returns: Result of the state change using #GstElementStateReturn.
+ * This function can return #GST_STATE_CHANGE_ASYNC, in which case the
+ * element will perform the remainder of the state change asynchronously in
+ * another thread.
+ * An application can use gst_element_get_state() to wait for the completion
+ * of the state change or it can wait for a state change message on the bus.
+ *
+ * Returns: Result of the state change using #GstStateChangeReturn.
*
* MT safe.
*/
-GstElementStateReturn
-gst_element_set_state (GstElement * element, GstElementState state)
+GstStateChangeReturn
+gst_element_set_state (GstElement * element, GstState state)
{
GstElementClass *oclass;
- GstElementState current;
- GstElementStateReturn return_val = GST_STATE_SUCCESS;
+ GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
- /* get the element state lock */
- GST_STATE_LOCK (element);
+ g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_CHANGE_FAILURE);
-#if 0
- /* a state change is pending and we are not in error, the element is busy
- * with a state change and we cannot proceed.
- * FIXME, does not work for a bin.*/
- if (G_UNLIKELY (GST_STATE_PENDING (element) != GST_STATE_VOID_PENDING &&
- !GST_STATE_ERROR (element)))
- goto was_busy;
-#endif
+ oclass = GST_ELEMENT_GET_CLASS (element);
- /* clear the error flag */
- GST_STATE_ERROR (element) = FALSE;
+ if (oclass->set_state)
+ result = (oclass->set_state) (element, state);
- /* start with the current state */
- current = GST_STATE (element);
+ return result;
+}
- GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "setting state from %s to %s",
- gst_element_state_get_name (current), gst_element_state_get_name (state));
+/*
+ * default set state function, calculates the next state based
+ * on current state and calls the change_state function
+ */
+static GstStateChangeReturn
+gst_element_set_state_func (GstElement * element, GstState state)
+{
+ GstState current, next, old_pending;
+ GstStateChangeReturn ret;
+ GstStateChange transition;
+ GstStateChangeReturn old_ret;
- oclass = GST_ELEMENT_GET_CLASS (element);
+ g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_CHANGE_FAILURE);
- /* We always perform at least one state change, even if the
- * current state is equal to the required state. This is needed
- * for bins that sync their children. */
- do {
- GstElementState pending;
-
- /* calculate the pending state */
- if (current < state)
- pending = current << 1;
- else if (current > state)
- pending = current >> 1;
- else
- pending = current;
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "set_state to %s",
+ gst_element_state_get_name (state));
- /* set the pending state variable */
- GST_STATE_PENDING (element) = pending;
+ /* state lock is taken to protect the set_state() and get_state()
+ * procedures, it does not lock any variables. */
+ GST_STATE_LOCK (element);
- GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
- "%s: setting state from %s to %s",
- (pending != state ? "intermediate" : "final"),
- gst_element_state_get_name (current),
- gst_element_state_get_name (pending));
-
- /* call the state change function so it can set the state */
- if (oclass->change_state)
- return_val = (oclass->change_state) (element);
- else
- return_val = GST_STATE_FAILURE;
-
- switch (return_val) {
- case GST_STATE_FAILURE:
- GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
- "have failed change_state return");
- /* state change failure exits the loop */
- gst_element_abort_state (element);
- goto exit;
- case GST_STATE_ASYNC:
- GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
- "element will change state async");
- /* an async state change exits the loop, we can only
- * go to the next state change when this one completes. */
- goto exit;
- case GST_STATE_SUCCESS:
- GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
- "element changed state successfuly");
- /* we can commit the state now and proceed to the next state */
- gst_element_commit_state (element);
- GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "commited state");
- break;
- default:
- goto invalid_return;
+ /* now calculate how to get to the new state */
+ GST_OBJECT_LOCK (element);
+ old_ret = GST_STATE_RETURN (element);
+ /* previous state change returned an error, remove all pending
+ * and next states */
+ if (old_ret == GST_STATE_CHANGE_FAILURE) {
+ GST_STATE_NEXT (element) = GST_STATE_VOID_PENDING;
+ GST_STATE_PENDING (element) = GST_STATE_VOID_PENDING;
+ GST_STATE_RETURN (element) = GST_STATE_CHANGE_SUCCESS;
+ }
+
+ current = GST_STATE (element);
+ next = GST_STATE_NEXT (element);
+ old_pending = GST_STATE_PENDING (element);
+ element->state_cookie++;
+
+ /* this is the (new) state we should go to */
+ GST_STATE_PENDING (element) = state;
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "current %s, old_pending %s, next %s, old return %d",
+ gst_element_state_get_name (current),
+ gst_element_state_get_name (old_pending),
+ gst_element_state_get_name (next), old_ret);
+
+ /* if the element was busy doing a state change, we just update the
+ * target state, it'll get to it async then. */
+ if (old_pending != GST_STATE_VOID_PENDING) {
+ /* upwards state change will happen ASYNC */
+ if (old_pending <= state)
+ goto was_busy;
+ /* element is going to this state already */
+ else if (next == state)
+ goto was_busy;
+ /* element was performing an ASYNC upward state change and
+ * we request to go downward again. Start from the next pending
+ * state then. */
+ else if (next > state
+ && GST_STATE_RETURN (element) == GST_STATE_CHANGE_ASYNC) {
+ current = next;
}
- /* get the current state of the element and see if we need to do more
- * state changes */
- current = GST_STATE (element);
}
- while (current != state);
+ next = GST_STATE_GET_NEXT (current, state);
+ /* now we store the next state */
+ GST_STATE_NEXT (element) = next;
+ /* mark busy, we need to check that there is actually a state change
+ * to be done else we could accidentally override SUCCESS/NO_PREROLL and
+ * the default element change_state function has no way to know what the
+ * old value was... could consider this a FIXME...*/
+ if (current != next)
+ GST_STATE_RETURN (element) = GST_STATE_CHANGE_ASYNC;
+
+ transition = GST_STATE_TRANSITION (current, next);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "%s: setting state from %s to %s",
+ (next != state ? "intermediate" : "final"),
+ gst_element_state_get_name (current), gst_element_state_get_name (next));
+
+ /* now signal any waiters, they will error since the cookie was increased */
+ GST_STATE_BROADCAST (element);
+
+ GST_OBJECT_UNLOCK (element);
+
+ ret = gst_element_change_state (element, transition);
-exit:
GST_STATE_UNLOCK (element);
- GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "exit state change");
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "returned %d", ret);
- return return_val;
+ return ret;
- /* ERROR */
-#if 0
was_busy:
{
+ GST_STATE_RETURN (element) = GST_STATE_CHANGE_ASYNC;
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "element was busy with async state change");
+ GST_OBJECT_UNLOCK (element);
+
GST_STATE_UNLOCK (element);
- GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
- "was busy with a state change");
- return GST_STATE_BUSY;
+ return GST_STATE_CHANGE_ASYNC;
}
-#endif
+}
+
+/* with STATE_LOCK */
+static GstStateChangeReturn
+gst_element_change_state (GstElement * element, GstStateChange transition)
+{
+ GstElementClass *oclass;
+ GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
+ GstState current;
+ GstState next;
+
+ oclass = GST_ELEMENT_GET_CLASS (element);
+
+ /* start with the current state. */
+ current = GST_STATE_TRANSITION_CURRENT (transition);
+ next = GST_STATE_TRANSITION_NEXT (transition);
+
+ /* call the state change function so it can set the state */
+ if (oclass->change_state)
+ ret = (oclass->change_state) (element, transition);
+ else
+ ret = GST_STATE_CHANGE_FAILURE;
+
+ switch (ret) {
+ case GST_STATE_CHANGE_FAILURE:
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+ "have FAILURE change_state return");
+ /* state change failure */
+ gst_element_abort_state (element);
+ break;
+ case GST_STATE_CHANGE_ASYNC:
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "element will change state ASYNC");
+
+ /* if we go upwards, we give the app a change to wait for
+ * completion */
+ if (current < next)
+ goto async;
+
+ /* else we just continue the state change downwards */
+ GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
+ "forcing commit state %s < %s",
+ gst_element_state_get_name (current),
+ gst_element_state_get_name (next));
+
+ ret = gst_element_continue_state (element, GST_STATE_CHANGE_SUCCESS);
+ break;
+ case GST_STATE_CHANGE_SUCCESS:
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "element changed state SUCCESS");
+ /* we can commit the state now which will proceeed to
+ * the next state */
+ ret = gst_element_continue_state (element, ret);
+ break;
+ case GST_STATE_CHANGE_NO_PREROLL:
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "element changed state NO_PREROLL");
+ /* we can commit the state now which will proceeed to
+ * the next state */
+ ret = gst_element_continue_state (element, ret);
+ break;
+ default:
+ goto invalid_return;
+ }
+
+ GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "exit state change %d", ret);
+
+ return ret;
+
+async:
+ GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "exit async state change %d",
+ ret);
+
+ return ret;
+
+ /* ERROR */
invalid_return:
{
- GST_STATE_UNLOCK (element);
+ GST_OBJECT_LOCK (element);
/* somebody added a GST_STATE_ and forgot to do stuff here ! */
- g_critical ("unkown return value from a state change function");
- return GST_STATE_FAILURE;
+ g_critical ("%s: unknown return value %d from a state change function",
+ GST_ELEMENT_NAME (element), ret);
+
+ ret = GST_STATE_CHANGE_FAILURE;
+ GST_STATE_RETURN (element) = ret;
+ GST_OBJECT_UNLOCK (element);
+
+ return ret;
}
}
-/* is called with STATE_LOCK
- *
- * This function activates the pads of a given element.
- *
- * TODO: activate pads from src to sinks?
- * move pad activate logic to GstPad because we also need this
- * when pads are added to elements?
- */
+/* gst_iterator_fold functions for pads_activate */
+
static gboolean
-gst_element_pads_activate (GstElement * element, gboolean active)
+activate_pads (GstPad * pad, GValue * ret, gboolean * active)
{
- GList *pads;
- gboolean result;
- guint32 cookie;
-
- GST_LOCK (element);
-restart:
- result = TRUE;
- pads = element->pads;
- cookie = element->pads_cookie;
- for (; pads && result; pads = g_list_next (pads)) {
- GstPad *pad = GST_PAD (pads->data);
+ if (!gst_pad_set_active (pad, *active))
+ g_value_set_boolean (ret, FALSE);
+ else if (!*active)
+ gst_pad_set_caps (pad, NULL);
- gst_object_ref (GST_OBJECT (pad));
- GST_UNLOCK (element);
+ gst_object_unref (pad);
+ return TRUE;
+}
- /* we only care about real pads */
- if (GST_IS_REAL_PAD (pad)) {
- GstRealPad *peer;
- gboolean pad_loop, pad_get;
- gboolean done = FALSE;
+/* returns false on error or early cutout of the fold, true otherwise */
+static gboolean
+iterator_fold_with_resync (GstIterator * iter, GstIteratorFoldFunction func,
+ GValue * ret, gpointer user_data)
+{
+ GstIteratorResult ires;
+ gboolean res = TRUE;
- /* see if the pad has a loop function and grab
- * the peer */
- pad_get = gst_pad_check_pull_range (pad);
- GST_LOCK (pad);
- pad_loop = GST_RPAD_LOOPFUNC (pad) != NULL;
- peer = GST_RPAD_PEER (pad);
- if (peer)
- gst_object_ref (GST_OBJECT (peer));
- GST_UNLOCK (pad);
+ while (1) {
+ ires = gst_iterator_fold (iter, func, ret, user_data);
- GST_DEBUG ("pad %s:%s: get: %d, loop: %d",
- GST_DEBUG_PAD_NAME (pad), pad_get, pad_loop);
+ switch (ires) {
+ case GST_ITERATOR_RESYNC:
+ gst_iterator_resync (iter);
+ break;
+ case GST_ITERATOR_DONE:
+ res = TRUE;
+ goto done;
+ default:
+ res = FALSE;
+ goto done;
+ }
+ }
- if (peer) {
- gboolean peer_loop, peer_get;
- GstActivateMode mode;
-
- /* see if the peer has a getrange function */
- peer_get = gst_pad_check_pull_range (GST_PAD_CAST (peer));
- /* see if the peer has a loop function */
- peer_loop = GST_RPAD_LOOPFUNC (peer) != NULL;
-
- GST_DEBUG ("peer %s:%s: get: %d, loop: %d",
- GST_DEBUG_PAD_NAME (peer), peer_get, peer_loop);
-
- /* If the pad is a sink with loop and the peer has a get function,
- * we can activate the sinkpad, FIXME, logic is reversed as
- * check_pull_range() checks the peer of the given pad. */
- if ((GST_PAD_IS_SINK (pad) && pad_get && pad_loop) ||
- (GST_PAD_IS_SRC (pad) && peer_get && peer_loop)) {
- GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
- "%sactivating pad %s pull mode", (active ? "" : "(de)"),
- GST_OBJECT_NAME (pad));
- /* only one of pad_random and peer_random can be true */
- mode = GST_ACTIVATE_PULL;
- result &= gst_pad_set_active (pad, active ? mode : GST_ACTIVATE_NONE);
- done = TRUE;
- }
- gst_object_unref (GST_OBJECT (peer));
- }
+done:
+ return res;
+}
- if (!done) {
- /* all other conditions are just push based pads */
- GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
- "%sactivating pad %s push mode", (active ? "" : "(de)"),
- GST_OBJECT_NAME (pad));
+/* is called with STATE_LOCK
+ */
+static gboolean
+gst_element_pads_activate (GstElement * element, gboolean active)
+{
+ GValue ret = { 0, };
+ GstIterator *iter;
+ gboolean fold_ok;
- result &= gst_pad_set_active (pad,
- (active ? GST_ACTIVATE_PUSH : GST_ACTIVATE_NONE));
- }
- }
- gst_object_unref (GST_OBJECT (pad));
+ GST_DEBUG_OBJECT (element, "pads_activate with active %d", active);
+ /* no need to unset this later, it's just a boolean */
+ g_value_init (&ret, G_TYPE_BOOLEAN);
+ g_value_set_boolean (&ret, TRUE);
- GST_LOCK (element);
- if (cookie != element->pads_cookie)
- goto restart;
+ if (active)
+ iter = gst_element_iterate_src_pads (element);
+ else
+ iter = gst_element_iterate_sink_pads (element);
+ fold_ok = iterator_fold_with_resync
+ (iter, (GstIteratorFoldFunction) activate_pads, &ret, &active);
+ gst_iterator_free (iter);
+ if (!fold_ok || !g_value_get_boolean (&ret)) {
+ GST_DEBUG_OBJECT (element, "pads_activate failed");
+ return FALSE;
}
- GST_UNLOCK (element);
- return result;
+ if (active)
+ iter = gst_element_iterate_sink_pads (element);
+ else
+ iter = gst_element_iterate_src_pads (element);
+ fold_ok = iterator_fold_with_resync
+ (iter, (GstIteratorFoldFunction) activate_pads, &ret, &active);
+ gst_iterator_free (iter);
+ if (!fold_ok || !g_value_get_boolean (&ret)) {
+ GST_DEBUG_OBJECT (element, "pads_activate failed");
+ return FALSE;
+ }
+
+ GST_DEBUG_OBJECT (element, "pads_activate successful");
+ return TRUE;
}
/* is called with STATE_LOCK */
-static GstElementStateReturn
-gst_element_change_state (GstElement * element)
+static GstStateChangeReturn
+gst_element_change_state_func (GstElement * element, GstStateChange transition)
{
- GstElementState old_state;
- gint old_pending, old_transition;
- GstElementStateReturn result = GST_STATE_SUCCESS;
+ GstState state, next;
+ GstStateChangeReturn result = GST_STATE_CHANGE_SUCCESS;
- g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_FAILURE);
+ g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_CHANGE_FAILURE);
- old_state = GST_STATE (element);
- old_pending = GST_STATE_PENDING (element);
- old_transition = GST_STATE_TRANSITION (element);
+ state = GST_STATE_TRANSITION_CURRENT (transition);
+ next = GST_STATE_TRANSITION_NEXT (transition);
/* if the element already is in the given state, we just return success */
- if (old_pending == GST_STATE_VOID_PENDING ||
- old_state == GST_STATE_PENDING (element)) {
- GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
- "element is already in the %s state",
- gst_element_state_get_name (old_state));
- return GST_STATE_SUCCESS;
- }
+ if (next == GST_STATE_VOID_PENDING || state == next)
+ goto was_ok;
GST_CAT_LOG_OBJECT (GST_CAT_STATES, element,
"default handler tries setting state from %s to %s (%04x)",
- gst_element_state_get_name (old_state),
- gst_element_state_get_name (old_pending), old_transition);
+ gst_element_state_get_name (state),
+ gst_element_state_get_name (next), transition);
- switch (old_transition) {
- case GST_STATE_NULL_TO_READY:
+ switch (transition) {
+ case GST_STATE_CHANGE_NULL_TO_READY:
break;
- case GST_STATE_READY_TO_PAUSED:
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
if (!gst_element_pads_activate (element, TRUE)) {
- result = GST_STATE_FAILURE;
+ result = GST_STATE_CHANGE_FAILURE;
}
break;
- case GST_STATE_PAUSED_TO_PLAYING:
- GST_LOCK (element);
- if (GST_ELEMENT_MANAGER (element)) {
- element->base_time =
- GST_ELEMENT_CAST (GST_ELEMENT_MANAGER (element))->base_time;
- }
- GST_UNLOCK (element);
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
break;
- case GST_STATE_PLAYING_TO_PAUSED:
+ case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
break;
- case GST_STATE_PAUSED_TO_READY:
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ case GST_STATE_CHANGE_READY_TO_NULL:
+ /* deactivate pads in both cases, since they are activated on
+ ready->paused but the element might not have made it to paused */
if (!gst_element_pads_activate (element, FALSE)) {
- result = GST_STATE_FAILURE;
+ result = GST_STATE_CHANGE_FAILURE;
} else {
- element->base_time = 0;
+ gst_element_set_base_time (element, 0);
}
break;
- case GST_STATE_READY_TO_NULL:
- break;
default:
/* this will catch real but unhandled state changes;
* can only be caused by:
* - somehow the element was asked to jump across an intermediate state
*/
g_warning ("Unhandled state change from %s to %s",
- gst_element_state_get_name (old_state),
- gst_element_state_get_name (old_pending));
+ gst_element_state_get_name (state),
+ gst_element_state_get_name (next));
break;
}
-
return result;
+
+was_ok:
+ {
+ GST_OBJECT_LOCK (element);
+ result = GST_STATE_RETURN (element);
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "element is already in the %s state",
+ gst_element_state_get_name (state));
+ GST_OBJECT_UNLOCK (element);
+
+ return result;
+ }
}
/**
* Retrieves the factory that was used to create this element.
*
* Returns: the #GstElementFactory used for creating this element.
+ * no refcounting is needed.
*/
GstElementFactory *
gst_element_get_factory (GstElement * element)
GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "dispose");
- /* ref so we don't hit 0 again */
- gst_object_ref (GST_OBJECT (object));
+ if (GST_STATE (element) != GST_STATE_NULL) {
+ g_critical
+ ("\nTrying to dispose element %s, but it is not in the NULL state.\n"
+ "You need to explicitly set elements to the NULL state before\n"
+ "dropping the final reference, to allow them to clean up.\n",
+ GST_OBJECT_NAME (element));
+ return;
+ }
+ g_return_if_fail (GST_STATE_PENDING (element) == GST_STATE_VOID_PENDING);
- /* first we break all our links with the ouside */
- while (element->pads) {
- gst_element_remove_pad (element, GST_PAD (element->pads->data));
+ GST_DEBUG ("removing %d pads", g_list_length (element->pads));
+ /* first we break all our links with the outside */
+ while (element->pads && element->pads->data) {
+ /* don't call _remove_pad with NULL */
+ gst_element_remove_pad (element, GST_PAD_CAST (element->pads->data));
}
if (G_UNLIKELY (element->pads != 0)) {
g_critical ("could not remove pads from element %s",
GST_STR_NULL (GST_OBJECT_NAME (object)));
}
- GST_LOCK (element);
- gst_object_replace ((GstObject **) & element->manager, NULL);
+ GST_OBJECT_LOCK (element);
gst_object_replace ((GstObject **) & element->clock, NULL);
- GST_UNLOCK (element);
+ gst_object_replace ((GstObject **) & element->bus, NULL);
+ GST_OBJECT_UNLOCK (element);
- GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "dispose parent");
+ GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "parent class dispose");
G_OBJECT_CLASS (parent_class)->dispose (object);
}
g_cond_free (element->state_cond);
element->state_cond = NULL;
GST_STATE_UNLOCK (element);
- g_mutex_free (element->state_lock);
+ g_static_rec_mutex_free (element->state_lock);
+ g_free (element->state_lock);
+ element->state_lock = NULL;
GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "finalize parent");
GstElementClass *oclass;
GParamSpec **specs, *spec;
guint nspecs;
- gint i;
+ guint i;
GValue value = { 0, };
GstElement *element;
(xmlChar *) GST_PLUGIN_FEATURE (factory)->name);
}
-/* FIXME: what is this? */
-/* if (element->manager) */
-/* xmlNewChild(parent, NULL, "manager", GST_ELEMENT_NAME(element->manager)); */
-
/* params */
specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (object), &nspecs);
}
}
+ g_free (specs);
+
pads = GST_ELEMENT_PADS (element);
while (pads) {
#endif /* GST_DISABLE_LOADSAVE */
static void
-gst_element_set_manager_func (GstElement * element, GstPipeline * manager)
-{
- g_return_if_fail (GST_IS_ELEMENT (element));
-
- GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, element, "setting manager to %p",
- manager);
-
- /* setting the manager cannot increase the refcount */
- GST_LOCK (element);
- GST_ELEMENT_MANAGER (element) = manager;
- GST_UNLOCK (element);
-}
-
-static void
gst_element_set_bus_func (GstElement * element, GstBus * bus)
{
g_return_if_fail (GST_IS_ELEMENT (element));
GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, element, "setting bus to %p", bus);
- GST_LOCK (element);
+ GST_OBJECT_LOCK (element);
gst_object_replace ((GstObject **) & GST_ELEMENT_BUS (element),
- GST_OBJECT (bus));
- GST_UNLOCK (element);
-}
-
-static void
-gst_element_set_scheduler_func (GstElement * element, GstScheduler * scheduler)
-{
- g_return_if_fail (GST_IS_ELEMENT (element));
-
- GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, element, "setting scheduler to %p",
- scheduler);
-
- GST_LOCK (element);
- gst_object_replace ((GstObject **) & GST_ELEMENT_SCHEDULER (element),
- GST_OBJECT (scheduler));
- GST_UNLOCK (element);
-}
-
-/**
- * gst_element_set_manager:
- * @element: a #GstElement to set the manager of.
- * @manager: the #GstManager to set.
- *
- * Sets the manager of the element. For internal use only, unless you're
- * writing a new bin subclass.
- *
- * MT safe.
- */
-void
-gst_element_set_manager (GstElement * element, GstPipeline * manager)
-{
- GstElementClass *oclass;
-
- g_return_if_fail (GST_IS_ELEMENT (element));
-
- oclass = GST_ELEMENT_GET_CLASS (element);
-
- if (oclass->set_manager)
- oclass->set_manager (element, manager);
-}
-
-
-/**
- * gst_element_get_manager:
- * @element: a #GstElement to get the manager of.
- *
- * Returns the manager of the element.
- *
- * Returns: the element's #GstPipeline. unref after usage.
- *
- * MT safe.
- */
-GstPipeline *
-gst_element_get_manager (GstElement * element)
-{
- GstPipeline *result = NULL;
-
- g_return_val_if_fail (GST_IS_ELEMENT (element), result);
-
- GST_LOCK (element);
- result = GST_ELEMENT_MANAGER (element);
- gst_object_ref (GST_OBJECT (result));
- GST_UNLOCK (element);
-
- return result;
+ GST_OBJECT_CAST (bus));
+ GST_OBJECT_UNLOCK (element);
}
/**
*
* Returns the bus of the element.
*
- * Returns: the element's #GstBus.
+ * Returns: the element's #GstBus. unref after usage.
*
* MT safe.
*/
g_return_val_if_fail (GST_IS_ELEMENT (element), result);
- GST_LOCK (element);
+ GST_OBJECT_LOCK (element);
result = GST_ELEMENT_BUS (element);
- GST_UNLOCK (element);
-
- return result;
-}
-
-/**
- * gst_element_set_scheduler:
- * @element: a #GstElement to set the scheduler of.
- * @scheduler: the #GstScheduler to set.
- *
- * Sets the scheduler of the element. For internal use only, unless you're
- * testing elements.
- *
- * MT safe.
- */
-void
-gst_element_set_scheduler (GstElement * element, GstScheduler * scheduler)
-{
- GstElementClass *oclass;
-
- g_return_if_fail (GST_IS_ELEMENT (element));
-
- oclass = GST_ELEMENT_GET_CLASS (element);
-
- if (oclass->set_scheduler)
- oclass->set_scheduler (element, scheduler);
-}
-
-/**
- * gst_element_get_scheduler:
- * @element: a #GstElement to get the scheduler of.
- *
- * Returns the scheduler of the element.
- *
- * Returns: the element's #GstScheduler.
- *
- * MT safe.
- */
-GstScheduler *
-gst_element_get_scheduler (GstElement * element)
-{
- GstScheduler *result = NULL;
-
- g_return_val_if_fail (GST_IS_ELEMENT (element), result);
+ gst_object_ref (result);
+ GST_OBJECT_UNLOCK (element);
- GST_LOCK (element);
- result = GST_ELEMENT_SCHEDULER (element);
- GST_UNLOCK (element);
+ GST_DEBUG_OBJECT (element, "got bus %" GST_PTR_FORMAT, result);
return result;
}