* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
+
/**
* SECTION:gstelement
* @short_description: Abstract base class for all pipeline elements
* GstElement is the base class needed to construct an element that can be
* used in a GStreamer pipeline. As such, it is not a functional entity, and
* cannot do anything when placed in a pipeline.
- *
- * 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
+ *
+ * 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. Buffers flow between these linked pads.
- * A GstElement has a GList of #GstPad structures for all their input (or sink)
- * and output (or source) pads.
+ * 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()
+ * 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
+ * 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().
- * You can wait for an element to change it's state with gst_element_wait_state_change().
- * To get a string representation of a #GstState, use
+ * 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
+ * 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.
+ * 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 2005-11-23 (0.9.5)
*/
+
#include "gst_private.h"
#include <glib.h>
#include <stdarg.h>
/* Element signals and args */
enum
{
- STATE_CHANGE,
NEW_PAD,
PAD_REMOVED,
NO_MORE_PADS,
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, GTimeVal * timeout);
+ 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);
#ifndef GST_DISABLE_LOADSAVE
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 (!gst_element_type) {
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
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_bus = GST_DEBUG_FUNCPTR (gst_element_set_bus_func);
klass->numpadtemplates = 0;
static void
gst_element_init (GstElement * element)
{
- element->current_state = GST_STATE_NULL;
- element->pending_state = GST_STATE_VOID_PENDING;
- 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 ();
}
* 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
*
* 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;
}
/**
g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
- GST_LOCK (element);
+ GST_OBJECT_LOCK (element);
if ((result = element->clock))
gst_object_ref (result);
- GST_UNLOCK (element);
+ GST_OBJECT_UNLOCK (element);
return result;
}
{
g_return_if_fail (GST_IS_ELEMENT (element));
- GST_LOCK (element);
+ GST_OBJECT_LOCK (element);
element->base_time = time;
- GST_UNLOCK (element);
+ GST_OBJECT_UNLOCK (element);
+
+ GST_DEBUG_OBJECT (element, "set base_time=%" GST_TIME_FORMAT,
+ GST_TIME_ARGS (time));
}
/**
g_return_val_if_fail (GST_IS_ELEMENT (element), GST_CLOCK_TIME_NONE);
- GST_LOCK (element);
+ GST_OBJECT_LOCK (element);
result = element->base_time;
- GST_UNLOCK (element);
+ GST_OBJECT_UNLOCK (element);
return result;
}
* @element: a #GstElement.
* @index: a #GstIndex.
*
- * Set the specified GstIndex on the element. The refcount of the index
+ * Set @index on the element. The refcount of the index
* will be increased, any previously set index is unreffed.
*
* MT safe.
*
* Pads are not automatically activated so elements should perform the needed
* steps to activate the pad in case this pad is added in the PAUSED or PLAYING
- * state.
+ * 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->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;
}
no_direction:
{
- GST_LOCK (pad);
+ 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_UNLOCK (pad);
- GST_UNLOCK (element);
+ GST_OBJECT_UNLOCK (pad);
+ GST_OBJECT_UNLOCK (element);
return FALSE;
}
}
* 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.
*/
g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
/* locking pad to look at the name and parent */
- GST_LOCK (pad);
+ GST_OBJECT_LOCK (pad);
GST_CAT_INFO_OBJECT (GST_CAT_ELEMENT_PADS, element, "removing pad '%s'",
GST_STR_NULL (GST_PAD_NAME (pad)));
if (G_UNLIKELY (GST_PAD_PARENT (pad) != element))
goto not_our_pad;
- GST_UNLOCK (pad);
+ GST_OBJECT_UNLOCK (pad);
/* unlink */
if ((peer = gst_pad_get_peer (pad))) {
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->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);
+ 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) {
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;
}
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.
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 (element);
- result = gst_iterator_new_list (GST_TYPE_PAD,
- GST_GET_LOCK (element),
- &element->pads_cookie,
- &element->pads,
- element,
- (GstIteratorItemFunction) iterate_pad,
- (GstIteratorDisposeFunction) gst_object_unref);
- GST_UNLOCK (element);
-
- return result;
-}
-
-static gint
-direction_filter (gconstpointer pad, gconstpointer direction)
-{
- if (GST_PAD_DIRECTION (pad) == GPOINTER_TO_INT (direction)) {
- /* pass the ref through */
- return 0;
- } else {
- /* unref */
- /* FIXME: this is very stupid */
- gst_object_unref (GST_OBJECT_CAST (pad));
- return 1;
- }
+ return gst_element_iterate_pad_list (element, &element->pads);
}
/**
* gst_element_iterate_src_pads:
* @element: a #GstElement.
*
- * Retrieves an iterator of @element's source pads.
+ * Retrieves an iterator of @element's source pads.
*
* Returns: the #GstIterator of #GstPad. Unref each pad after use.
*
{
g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
- return gst_iterator_filter (gst_element_iterate_pads (element),
- direction_filter, GINT_TO_POINTER (GST_PAD_SRC));
+ 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.
+ * Retrieves an iterator of @element's sink pads.
*
* Returns: the #GstIterator of #GstPad. Unref each pad after use.
*
{
g_return_val_if_fail (GST_IS_ELEMENT (element), NULL);
- return gst_iterator_filter (gst_element_iterate_pads (element),
- direction_filter, GINT_TO_POINTER (GST_PAD_SINK));
+ return gst_element_iterate_pad_list (element, &element->sinkpads);
}
/**
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 (result);
- GST_UNLOCK (element);
+ GST_OBJECT_UNLOCK (element);
return result;
* 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 event on element %s",
+ GST_ELEMENT_NAME (element));
result = oclass->send_event (element, event);
} else {
GstPad *pad = gst_element_get_random_pad (element, GST_PAD_SINK);
result = gst_pad_push_event (pad, event);
gst_object_unref (pad);
} else {
- GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "can't send event on element %s",
+ GST_CAT_INFO (GST_CAT_ELEMENT_PADS, "can't send event on element %s",
GST_ELEMENT_NAME (element));
}
}
* @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);
* @element: a #GstElement posting the message
* @message: a #GstMessage to post
*
- * Post a message on the element's #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 successfully posted.
*
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_message_unref (message);
- return FALSE;
- }
+ if (G_UNLIKELY (bus == NULL))
+ goto no_bus;
+
gst_object_ref (bus);
- GST_DEBUG ("... on bus %" GST_PTR_FORMAT, bus);
- GST_UNLOCK (element);
+ GST_OBJECT_UNLOCK (element);
result = gst_bus_post (bus, message);
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;
+ }
}
/**
* 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)
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_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);
- if (gst_element_set_state (element,
- GST_STATE (parent)) == GST_STATE_CHANGE_FAILURE) {
+ 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);
+
+ return TRUE;
+ }
+ return FALSE;
+
+ /* ERROR */
+failed:
+ {
return FALSE;
}
- return TRUE;
}
/* MT safe */
static GstStateChangeReturn
gst_element_get_state_func (GstElement * element,
- GstState * state, GstState * pending, GTimeVal * timeout)
+ GstState * state, GstState * pending, GstClockTime timeout)
{
GstStateChangeReturn ret = GST_STATE_CHANGE_FAILURE;
GstState old_pending;
- GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "getting state");
+ GST_CAT_DEBUG_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_NO_PREROLL (element)) {
- ret = GST_STATE_CHANGE_NO_PREROLL;
+ if (ret == GST_STATE_CHANGE_FAILURE)
goto done;
- }
- /* we got an error, report immediatly */
- if (GST_STATE_ERROR (element)) {
- ret = GST_STATE_CHANGE_FAILURE;
+ /* 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_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");
+ 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");
+ 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");
+ GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "nothing pending");
ret = GST_STATE_CHANGE_SUCCESS;
}
if (pending)
*pending = GST_STATE_PENDING (element);
- GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
- "state current: %s, pending: %s, error: %d, no_preroll: %d, result: %d",
+ 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_ERROR (element), GST_STATE_NO_PREROLL (element), ret);
-
- 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;
+ }
}
/**
* @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 #GTimeVal to specify the timeout for an async
- * state change or NULL for infinite timeout.
+ * @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_CHANGE_SUCCESS or GST_STATE_CHANGE_FAILURE respectively.
- *
- * 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.
+ * 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.
*/
GstStateChangeReturn
gst_element_get_state (GstElement * element,
- GstState * state, GstState * pending, GTimeVal * timeout)
+ GstState * state, GstState * pending, GstClockTime timeout)
{
GstElementClass *oclass;
GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
{
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
- GstState 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)
{
GstState pending;
+ GstState old_ret, old_state, old_next;
+ GstState current, next;
+ GstMessage *message;
+ GstStateChange transition;
+
+ GST_OBJECT_LOCK (element);
+ old_ret = GST_STATE_RETURN (element);
+ GST_STATE_RETURN (element) = ret;
+ pending = GST_STATE_PENDING (element);
- g_return_if_fail (GST_IS_ELEMENT (element));
+ /* check if there is something to commit */
+ if (pending == GST_STATE_VOID_PENDING)
+ goto nothing_pending;
- pending = GST_STATE_PENDING (element);
+ 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;
- if (pending != GST_STATE_VOID_PENDING) {
- GstState old_state = GST_STATE (element);
- GstMessage *message;
+ /* see if we reached the final state */
+ if (pending == current)
+ goto complete;
- GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
- "committing state from %s to %s",
- gst_element_state_get_name (old_state),
- gst_element_state_get_name (pending));
+ next = GST_STATE_GET_NEXT (current, pending);
+ transition = GST_STATE_TRANSITION (current, next);
+
+ GST_STATE_NEXT (element) = next;
+ 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_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;
- GST_STATE (element) = pending;
+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);
+ }
- 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.
+ *
* 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)) {
- GstState current_state = GST_STATE (element);
- GstMessage *message;
+ 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);
- GST_STATE_PENDING (element) = current_state;
- GST_STATE_ERROR (element) = FALSE;
+ return;
- message = gst_message_new_state_changed (GST_OBJECT (element),
- current_state, current_state);
- gst_element_post_message (element, message);
+nothing_lost:
+ {
+ GST_OBJECT_UNLOCK (element);
+ return;
}
}
* requested state by going through all the intermediary states and calling
* the class's state change function for each.
*
+ * 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.
gst_element_set_state (GstElement * element, GstState state)
{
GstElementClass *oclass;
- GstState current;
- GstStateChangeReturn return_val = GST_STATE_CHANGE_SUCCESS;
+ GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE;
+
+ g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_CHANGE_FAILURE);
+
+ oclass = GST_ELEMENT_GET_CLASS (element);
+
+ if (oclass->set_state)
+ result = (oclass->set_state) (element, state);
+
+ return result;
+}
+
+/*
+ * 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;
- GstState pending;
- GTimeVal tv;
+ GstStateChange transition;
+ GstStateChangeReturn old_ret;
g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_CHANGE_FAILURE);
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "set_state to %s",
gst_element_state_get_name (state));
- /* get current element state, need to call the method so that
- * we call the virtual method and subclasses can implement their
- * own algorithms */
- GST_TIME_TO_TIMEVAL (0, tv);
- ret = gst_element_get_state (element, ¤t, &pending, &tv);
+ /* state lock is taken to protect the set_state() and get_state()
+ * procedures, it does not lock any variables. */
GST_STATE_LOCK (element);
- /* this is the state we should go to */
- GST_STATE_FINAL (element) = state;
- if (ret == GST_STATE_CHANGE_ASYNC) {
- /* force next state keeping ASYNC, this is atomic as we hold
- * the STATE_LOCK */
- gst_element_commit_state (element);
- gst_element_lost_state (element);
- if (state == GST_STATE_PENDING (element)) {
- GST_STATE_UNLOCK (element);
- return GST_STATE_CHANGE_ASYNC;
- }
+
+ /* 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;
}
- /* start with the current state */
current = GST_STATE (element);
+ next = GST_STATE_NEXT (element);
+ old_pending = GST_STATE_PENDING (element);
+ element->state_cookie++;
- 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));
+ /* this is the (new) state we should go to */
+ GST_STATE_PENDING (element) = state;
- oclass = GST_ELEMENT_GET_CLASS (element);
+ 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);
- /* 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 {
- GstState pending;
-
- /* calculate the pending state */
- if (current < state)
- pending = current + 1;
- else if (current > state)
- pending = current - 1;
- else
- pending = current;
+ /* 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;
+ }
+ }
+ next = GST_STATE_GET_NEXT (current, state);
+ /* now we store the next state */
+ GST_STATE_NEXT (element) = next;
+ 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);
- /* set the pending state variable */
- GST_STATE_PENDING (element) = pending;
+ ret = gst_element_change_state (element, transition);
+ GST_STATE_UNLOCK (element);
+
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "returned %d", ret);
+
+ return ret;
+
+was_busy:
+ {
+ GST_STATE_RETURN (element) = GST_STATE_CHANGE_ASYNC;
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, GST_STATE_CHANGE (element));
- else
- return_val = GST_STATE_CHANGE_FAILURE;
-
- /* clear the error and preroll flag, we need to do that after
- * calling the virtual change_state function so that it can use the
- * old previous value. */
- GST_STATE_ERROR (element) = FALSE;
- GST_STATE_NO_PREROLL (element) = FALSE;
-
- switch (return_val) {
- case GST_STATE_CHANGE_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_CHANGE_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_CHANGE_SUCCESS:
- GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
- "element changed state successfully");
- /* 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, "committed state");
- break;
- case GST_STATE_CHANGE_NO_PREROLL:
- GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
- "element changed state successfully and can't preroll");
- /* we can commit the state now and proceed to the next state */
- gst_element_commit_state (element);
- GST_STATE_NO_PREROLL (element) = TRUE;
- GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "committed state");
- break;
- default:
- goto invalid_return;
- }
- /* get the current state of the element and see if we need to do more
- * state changes */
- current = GST_STATE (element);
+ "element was busy with async state change");
+ GST_OBJECT_UNLOCK (element);
+
+ GST_STATE_UNLOCK (element);
+
+ return GST_STATE_CHANGE_ASYNC;
}
- while (current != state);
+}
-exit:
- GST_STATE_FINAL (element) = GST_STATE_VOID_PENDING;
- GST_STATE_UNLOCK (element);
+/* 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;
- GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "exit state change");
+ oclass = GST_ELEMENT_GET_CLASS (element);
- return return_val;
+ /* 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_OBJECT_LOCK (element);
+ GST_STATE_RETURN (element) = ret;
+ GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "exit async state change %d",
+ ret);
+ GST_OBJECT_UNLOCK (element);
+
+ return ret;
/* ERROR */
invalid_return:
{
- GST_STATE_FINAL (element) = GST_STATE_VOID_PENDING;
- GST_STATE_UNLOCK (element);
+ GST_OBJECT_LOCK (element);
/* somebody added a GST_STATE_ and forgot to do stuff here ! */
- g_critical ("unknown return value %d from a state change function",
- return_val);
- return GST_STATE_CHANGE_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;
}
}
GstIterator *iter;
gboolean fold_ok;
+ 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);
fold_ok = iterator_fold_with_resync
(iter, (GstIteratorFoldFunction) activate_pads, &ret, &active);
gst_iterator_free (iter);
- if (!fold_ok || !g_value_get_boolean (&ret))
+ if (!fold_ok || !g_value_get_boolean (&ret)) {
+ GST_DEBUG_OBJECT (element, "pads_activate failed");
return FALSE;
+ }
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))
+ 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 GstStateChangeReturn
-gst_element_change_state (GstElement * element, GstStateChange transition)
+gst_element_change_state_func (GstElement * element, GstStateChange transition)
{
- GstState state, pending;
+ GstState state, next;
GstStateChangeReturn result = GST_STATE_CHANGE_SUCCESS;
g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_CHANGE_FAILURE);
- state = GST_STATE (element);
- pending = GST_STATE_PENDING (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 (pending == GST_STATE_VOID_PENDING || state == GST_STATE_PENDING (element))
+ 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 (state),
- gst_element_state_get_name (pending), transition);
+ gst_element_state_get_name (next), transition);
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
*/
g_warning ("Unhandled state change from %s to %s",
gst_element_state_get_name (state),
- gst_element_state_get_name (pending));
+ 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));
- if (GST_STATE_NO_PREROLL (element))
- return GST_STATE_CHANGE_NO_PREROLL;
- else
- return GST_STATE_CHANGE_SUCCESS;
+ GST_OBJECT_UNLOCK (element);
+
+ return result;
}
}
*
* Retrieves the factory that was used to create this element.
*
- * Returns: the #GstElementFactory used for creating this element.
+ * Returns: the #GstElementFactory used for creating this element.
* no refcounting is needed.
*/
GstElementFactory *
GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "dispose");
- g_return_if_fail (GST_STATE (element) == GST_STATE_NULL);
+ 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);
+ GST_DEBUG ("removing %d pads", g_list_length (element->pads));
/* first we break all our links with the outside */
- while (element->pads) {
+ 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)) {
GST_STR_NULL (GST_OBJECT_NAME (object)));
}
- GST_LOCK (element);
+ GST_OBJECT_LOCK (element);
gst_object_replace ((GstObject **) & element->clock, NULL);
gst_object_replace ((GstObject **) & element->bus, NULL);
- GST_UNLOCK (element);
+ GST_OBJECT_UNLOCK (element);
GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "parent class dispose");
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;
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_CAST (bus));
- GST_UNLOCK (element);
+ GST_OBJECT_UNLOCK (element);
}
/**
g_return_val_if_fail (GST_IS_ELEMENT (element), result);
- GST_LOCK (element);
+ GST_OBJECT_LOCK (element);
result = GST_ELEMENT_BUS (element);
gst_object_ref (result);
- GST_UNLOCK (element);
+ GST_OBJECT_UNLOCK (element);
GST_DEBUG_OBJECT (element, "got bus %" GST_PTR_FORMAT, result);