* @short_description: Abstract base class for all pipeline elements
* @see_also: #GstElementFactory, #GstPad
*
- * 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.
+ * 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
+ * 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)
+ * 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().
* 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.
+ * 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
+ * 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"
GstStateChange transition);
static GstStateChangeReturn gst_element_change_state_func (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);
#ifndef GST_DISABLE_LOADSAVE
{
static GType gst_element_type = 0;
- if (!gst_element_type) {
+ if (G_UNLIKELY (gst_element_type == 0)) {
static const GTypeInfo element_info = {
sizeof (GstElementClass),
gst_element_base_class_init,
#endif
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;
* 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;
}
* @element: a #GstElement.
* @time: the base time to set.
*
- * Set the base time of an element. See @gst_element_get_base_time().
+ * Set the base time of an element. See gst_element_get_base_time().
*
* MT safe.
*/
{
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.
* 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;
}
{
GstIterator *result;
- GST_LOCK (element);
+ GST_OBJECT_LOCK (element);
gst_object_ref (element);
result = gst_iterator_new_list (GST_TYPE_PAD,
- GST_GET_LOCK (element),
+ GST_OBJECT_GET_LOCK (element),
&element->pads_cookie,
padlist,
element,
(GstIteratorItemFunction) iterate_pad,
(GstIteratorDisposeFunction) gst_object_unref);
- GST_UNLOCK (element);
+ GST_OBJECT_UNLOCK (element);
return result;
}
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 %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) {
GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS,
- "pushing event to random pad %s:%s", GST_DEBUG_PAD_NAME (pad));
+ "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_push_event (pad, event);
gst_object_unref (pad);
} else {
- GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "can't send event on element %s",
- GST_ELEMENT_NAME (element));
+ GST_CAT_INFO (GST_CAT_ELEMENT_PADS, "can't send %s event on element %s",
+ GST_EVENT_TYPE_NAME (event), GST_ELEMENT_NAME (element));
}
}
return 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);
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))
goto no_bus;
gst_object_ref (bus);
- GST_UNLOCK (element);
+ GST_OBJECT_UNLOCK (element);
result = gst_bus_post (bus, message);
gst_object_unref (bus);
no_bus:
{
GST_DEBUG ("not posting message %p: no bus", message);
- GST_UNLOCK (element);
+ 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 */
* 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);
+ GST_OBJECT_LOCK (element);
result = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE);
- GST_UNLOCK (element);
+ 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);
+ GST_OBJECT_LOCK (element);
old = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_LOCKED_STATE);
if (G_UNLIKELY (old == locked_state))
GST_ELEMENT_NAME (element));
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;
}
GstState parent_current, parent_pending;
GstStateChangeReturn ret;
- GST_STATE_LOCK (parent);
+ GST_OBJECT_LOCK (parent);
parent_current = GST_STATE (parent);
parent_pending = GST_STATE_PENDING (parent);
- GST_STATE_UNLOCK (parent);
+ GST_OBJECT_UNLOCK (parent);
GST_CAT_DEBUG (GST_CAT_STATES,
"syncing state of element %s (%s) to %s (%s, %s)",
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "getting state");
- GST_STATE_LOCK (element);
+ GST_OBJECT_LOCK (element);
ret = GST_STATE_RETURN (element);
/* we got an error, report immediatly */
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;
} 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, "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_DEBUG_OBJECT (GST_CAT_STATES, element, "got success");
"state current: %s, pending: %s, result: %d",
gst_element_state_get_name (GST_STATE (element)),
gst_element_state_get_name (GST_STATE_PENDING (element)), ret);
-
- GST_STATE_UNLOCK (element);
+ 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;
+ }
}
/**
* @element: a #GstElement to get the state of.
* @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.
+ * Can be %NULL.
* @timeout: a #GstClockTime to specify the timeout for an async
- * state change or GST_CLOCK_TIME_NONE for infinite timeout.
+ * state change or %GST_CLOCK_TIME_NONE for infinite timeout.
*
* 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
+ * 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.
- *
- * For elements that did not return ASYNC, this function returns the
- * current and pending state immediatly.
- *
- * 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.
*/
{
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_RETURN (element) != GST_STATE_CHANGE_FAILURE) {
+ 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_RETURN (element) = GST_STATE_CHANGE_FAILURE;
- GST_STATE_BROADCAST (element);
+ GST_STATE_BROADCAST (element);
+ GST_OBJECT_UNLOCK (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.
- * 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.
- *
- * This function can only be called with the STATE_LOCK held.
- *
- * Returns: The result of the commit state change.
- *
- * 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.
*/
GstStateChangeReturn
-gst_element_commit_state (GstElement * element)
+gst_element_continue_state (GstElement * element, GstStateChangeReturn ret)
{
GstState pending;
- GstStateChangeReturn ret;
-
- g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_CHANGE_FAILURE);
-
- ret = GST_STATE_CHANGE_SUCCESS;
+ 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);
/* check if there is something to commit */
- if (pending != GST_STATE_VOID_PENDING) {
- GstState old_state;
- GstState current, next;
- GstMessage *message;
-
- GST_LOCK (element);
- GST_OBJECT_FLAG_SET (element, GST_ELEMENT_CHANGING_STATE);
- GST_UNLOCK (element);
-
- old_state = GST_STATE (element);
- /* this is the state we should go to next */
- next = GST_STATE_NEXT (element);
- /* update current state */
- current = GST_STATE (element) = next;
-
- /* see if we reached the final state */
- if (pending == next) {
- pending = GST_STATE_VOID_PENDING;
- GST_STATE_PENDING (element) = pending;
- GST_STATE_NEXT (element) = pending;
- ret = GST_STATE_CHANGE_SUCCESS;
- } else {
- /* not there yet, will get there ASYNC */
- ret = GST_STATE_CHANGE_ASYNC;
- }
+ if (pending == GST_STATE_VOID_PENDING)
+ goto nothing_pending;
- GST_STATE_RETURN (element) = ret;
+ 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;
- GST_CAT_DEBUG_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 (next),
- gst_element_state_get_name (pending));
+ /* see if we reached the final state */
+ if (pending == current)
+ goto complete;
- message = gst_message_new_state_changed (GST_OBJECT (element),
- FALSE, old_state, next, pending);
- gst_element_post_message (element, message);
+ next = GST_STATE_GET_NEXT (current, pending);
+ transition = GST_STATE_TRANSITION (current, next);
- if (pending != GST_STATE_VOID_PENDING) {
- GstStateChange transition;
+ 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);
- /* calc new next state */
- next = GST_STATE_GET_NEXT (current, 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));
- GST_CAT_DEBUG_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);
- /* create transition */
- transition = GST_STATE_TRANSITION (current, next);
+ return ret;
- /* perform next transition */
- ret = gst_element_change_state (element, transition);
- } else {
- GST_STATE_BROADCAST (element);
+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_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);
}
- GST_LOCK (element);
- GST_OBJECT_FLAG_UNSET (element, GST_ELEMENT_CHANGING_STATE);
- GST_UNLOCK (element);
+
+ GST_STATE_BROADCAST (element);
+
+ return ret;
}
- 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
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_RETURN (element) != GST_STATE_CHANGE_FAILURE) {
- GstState current_state;
- 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;
- current_state = GST_STATE (element);
+ 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_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_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;
- message = gst_message_new_state_changed (GST_OBJECT (element),
- FALSE, current_state, 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.
+ * 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.
*
GstStateChangeReturn
gst_element_set_state (GstElement * element, GstState state)
{
+ GstElementClass *oclass;
+ 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;
GstStateChange transition;
GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "set_state to %s",
gst_element_state_get_name (state));
+ /* state lock is taken to protect the set_state() and get_state()
+ * procedures, it does not lock any variables. */
GST_STATE_LOCK (element);
- GST_LOCK (element);
- GST_OBJECT_FLAG_SET (element, GST_ELEMENT_CHANGING_STATE);
- GST_UNLOCK (element);
-
+ /* 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 */
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;
}
}
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));
- transition = GST_STATE_TRANSITION (current, 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);
- GST_LOCK (element);
- GST_OBJECT_FLAG_UNSET (element, GST_ELEMENT_CHANGING_STATE);
- GST_UNLOCK (element);
GST_STATE_UNLOCK (element);
- GST_DEBUG_OBJECT (element, "returned %d", ret);
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "returned %d", ret);
return ret;
was_busy:
{
GST_STATE_RETURN (element) = GST_STATE_CHANGE_ASYNC;
- GST_LOCK (element);
- GST_OBJECT_FLAG_UNSET (element, GST_ELEMENT_CHANGING_STATE);
- GST_UNLOCK (element);
- GST_STATE_UNLOCK (element);
+ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element,
+ "element was busy with async state change");
+ GST_OBJECT_UNLOCK (element);
- GST_DEBUG_OBJECT (element, "element was busy with async state change");
+ GST_STATE_UNLOCK (element);
return GST_STATE_CHANGE_ASYNC;
}
-
}
/* with STATE_LOCK */
current = GST_STATE_TRANSITION_CURRENT (transition);
next = GST_STATE_TRANSITION_NEXT (transition);
- /* now we store the next state */
- GST_STATE_NEXT (element) = next;
-
/* call the state change function so it can set the state */
if (oclass->change_state)
ret = (oclass->change_state) (element, transition);
/* if we go upwards, we give the app a change to wait for
* completion */
if (current < next)
- goto exit;
+ goto async;
/* else we just continue the state change downwards */
GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
gst_element_state_get_name (current),
gst_element_state_get_name (next));
- GST_STATE_RETURN (element) = GST_STATE_CHANGE_SUCCESS;
-
- ret = gst_element_commit_state (element);
+ 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_commit_state (element);
+ 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 */
- gst_element_commit_state (element);
- ret = GST_STATE_CHANGE_NO_PREROLL;
+ ret = gst_element_continue_state (element, ret);
break;
default:
- ret = GST_STATE_CHANGE_FAILURE;
goto invalid_return;
}
-exit:
- GST_STATE_RETURN (element) = ret;
-
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_RETURN (element) = ret;
+ 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", ret);
+ 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;
}
}
g_value_init (&ret, G_TYPE_BOOLEAN);
g_value_set_boolean (&ret, TRUE);
- iter = gst_element_iterate_src_pads (element);
+ 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);
return FALSE;
}
- iter = gst_element_iterate_sink_pads (element);
+ 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);
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));
- return GST_STATE_RETURN (element);
+ GST_OBJECT_UNLOCK (element);
+
+ return result;
}
}
}
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");
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);