* Boston, MA 02111-1307, USA.
*/
-/* #define GST_DEBUG_ENABLED */
#include "gst_private.h"
#include "gstevent.h"
#include "gstbin.h"
#include "gstxml.h"
-#include "gstlog.h"
+#include "gstinfo.h"
#include "gstscheduler.h"
#include "gstindex.h"
-GstElementDetails gst_bin_details = {
+static GstElementDetails gst_bin_details = GST_ELEMENT_DETAILS (
"Generic bin",
"Generic/Bin",
- "LGPL",
"Simple container object",
- VERSION,
- "Erik Walthinsen <omega@cse.ogi.edu>",
- "(C) 1999",
-};
+ "Erik Walthinsen <omega@cse.ogi.edu>"
+);
GType _gst_bin_type = 0;
+static gboolean _gst_boolean_did_something_accumulator (GSignalInvocationHint *ihint,
+ GValue *return_accu, const GValue *handler_return, gpointer dummy);
+
static void gst_bin_dispose (GObject * object);
static GstElementStateReturn gst_bin_change_state (GstElement *element);
{
ELEMENT_ADDED,
ELEMENT_REMOVED,
+ ITERATE,
LAST_SIGNAL
};
enum
{
- ARG_0,
+ ARG_0
/* FILL ME */
};
+static void gst_bin_base_init (gpointer g_class);
static void gst_bin_class_init (GstBinClass * klass);
static void gst_bin_init (GstBin * bin);
if (!_gst_bin_type) {
static const GTypeInfo bin_info = {
sizeof (GstBinClass),
- NULL,
+ gst_bin_base_init,
NULL,
(GClassInitFunc) gst_bin_class_init,
NULL,
}
static void
+gst_bin_base_init (gpointer g_class)
+{
+ GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
+
+ gst_element_class_set_details (gstelement_class, &gst_bin_details);
+}
+
+static void
gst_bin_class_init (GstBinClass * klass)
{
GObjectClass *gobject_class;
gst_bin_signals[ELEMENT_ADDED] =
g_signal_new ("element_added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GstBinClass, element_added), NULL, NULL,
- gst_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
+ gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
gst_bin_signals[ELEMENT_REMOVED] =
g_signal_new ("element_removed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GstBinClass, element_removed), NULL, NULL,
- gst_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
+ gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
+ gst_bin_signals[ITERATE] =
+ g_signal_new ("iterate", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GstBinClass, iterate),
+ _gst_boolean_did_something_accumulator, NULL,
+ gst_marshal_BOOLEAN__VOID, G_TYPE_BOOLEAN, 0);
gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_bin_dispose);
klass->iterate = GST_DEBUG_FUNCPTR (gst_bin_iterate_func);
}
+static gboolean
+_gst_boolean_did_something_accumulator (GSignalInvocationHint *ihint,
+ GValue *return_accu, const GValue *handler_return, gpointer dummy)
+{
+ gboolean did_something;
+
+ did_something = g_value_get_boolean (handler_return);
+ if (did_something) {
+ g_value_set_boolean (return_accu, TRUE);
+ }
+
+ /* always continue emission */
+ return TRUE;
+}
+
static void
gst_bin_init (GstBin * bin)
{
bin->numchildren = 0;
bin->children = NULL;
-
- bin->pre_iterate_func = NULL;
- bin->post_iterate_func = NULL;
- bin->pre_iterate_data = NULL;
- bin->post_iterate_data = NULL;
}
/**
static void
gst_bin_set_element_sched (GstElement *element, GstScheduler *sched)
{
- GST_INFO (GST_CAT_SCHEDULING, "setting element \"%s\" sched to %p", GST_ELEMENT_NAME (element),
+ GST_CAT_INFO (GST_CAT_SCHEDULING, "setting element \"%s\" sched to %p", GST_ELEMENT_NAME (element),
sched);
/* if it's actually a Bin */
if (GST_IS_BIN (element)) {
if (GST_FLAG_IS_SET (element, GST_BIN_FLAG_MANAGER)) {
- GST_INFO_ELEMENT (GST_CAT_PARENTAGE, element, "child is already a manager, not resetting");
+ GST_CAT_INFO (GST_CAT_PARENTAGE, "[%s]: child is already a manager, not resetting", GST_ELEMENT_NAME (element));
if (GST_ELEMENT_SCHED (element))
gst_scheduler_add_scheduler (sched, GST_ELEMENT_SCHED (element));
return;
}
- GST_INFO_ELEMENT (GST_CAT_PARENTAGE, element, "setting children's schedule to parent's");
+ GST_CAT_INFO (GST_CAT_PARENTAGE, "[%s]: setting children's schedule to parent's", GST_ELEMENT_NAME (element));
gst_scheduler_add_element (sched, element);
/* set the children's schedule */
/* if the peer element exists and is a candidate */
if (GST_PAD_PEER (pad)) {
if (gst_pad_get_scheduler (GST_PAD_PEER (pad)) == sched) {
- GST_INFO (GST_CAT_SCHEDULING,
+ GST_CAT_INFO (GST_CAT_SCHEDULING,
"peer is in same scheduler, telling scheduler");
if (GST_PAD_IS_SRC (pad))
gst_bin_unset_element_sched (GstElement *element, GstScheduler *sched)
{
if (GST_ELEMENT_SCHED (element) == NULL) {
- GST_INFO (GST_CAT_SCHEDULING, "element \"%s\" has no scheduler",
+ GST_CAT_INFO (GST_CAT_SCHEDULING, "element \"%s\" has no scheduler",
GST_ELEMENT_NAME (element));
return;
}
- GST_INFO (GST_CAT_SCHEDULING, "removing element \"%s\" from its sched %p",
+ GST_CAT_INFO (GST_CAT_SCHEDULING, "removing element \"%s\" from its sched %p",
GST_ELEMENT_NAME (element), GST_ELEMENT_SCHED (element));
/* if it's actually a Bin */
if (GST_IS_BIN (element)) {
if (GST_FLAG_IS_SET (element, GST_BIN_FLAG_MANAGER)) {
- GST_INFO_ELEMENT (GST_CAT_PARENTAGE, element,
- "child is already a manager, not unsetting sched");
+ GST_CAT_INFO (GST_CAT_PARENTAGE, "[%s]: child is already a manager, not unsetting sched",
+ GST_ELEMENT_NAME (element));
if (sched) {
gst_scheduler_remove_scheduler (sched, GST_ELEMENT_SCHED (element));
}
/* if the peer element exists and is a candidate */
if (GST_PAD_PEER (pad)) {
if (gst_pad_get_scheduler (GST_PAD_PEER (pad)) == sched) {
- GST_INFO (GST_CAT_SCHEDULING, "peer is in same scheduler, telling scheduler");
+ GST_CAT_INFO (GST_CAT_SCHEDULING, "peer is in same scheduler, telling scheduler");
if (GST_PAD_IS_SRC (pad))
gst_scheduler_pad_unlink (sched, pad, GST_PAD_PEER (pad));
/* the element must not already have a parent */
g_return_if_fail (GST_ELEMENT_PARENT (element) == NULL);
- /* must be not be in PLAYING state in order to modify bin */
- g_return_if_fail (GST_STATE (bin) != GST_STATE_PLAYING);
-
/* then check to see if the element's name is already taken in the bin */
if (gst_object_check_uniqueness (bin->children,
GST_ELEMENT_NAME (element)) == FALSE)
gst_bin_set_element_sched (element, sched);
}
- GST_INFO_ELEMENT (GST_CAT_PARENTAGE, bin, "added child \"%s\"", GST_ELEMENT_NAME (element));
+ GST_CAT_INFO (GST_CAT_PARENTAGE, "[%s]: added child \"%s\"",
+ GST_ELEMENT_NAME (bin), GST_ELEMENT_NAME (element));
g_signal_emit (G_OBJECT (bin), gst_bin_signals[ELEMENT_ADDED], 0, element);
}
g_return_if_fail (GST_IS_BIN (bin));
g_return_if_fail (GST_IS_ELEMENT (element));
- GST_DEBUG (GST_CAT_PARENTAGE, "adding element \"%s\" to bin \"%s\"",
+ GST_CAT_DEBUG (GST_CAT_PARENTAGE, "adding element \"%s\" to bin \"%s\"",
GST_ELEMENT_NAME (element), GST_ELEMENT_NAME (bin));
bclass = GST_BIN_GET_CLASS (bin);
while (state >>= 1) state_idx++;
bin->child_states[state_idx]--;
- GST_INFO_ELEMENT (GST_CAT_PARENTAGE, bin, "removed child %s", GST_ELEMENT_NAME (element));
+ GST_CAT_INFO (GST_CAT_PARENTAGE, "[%s]: removed child %s",
+ GST_ELEMENT_NAME (bin), GST_ELEMENT_NAME (element));
/* ref as we're going to emit a signal */
gst_object_ref (GST_OBJECT (element));
{
GstBinClass *bclass;
- GST_DEBUG_ELEMENT (GST_CAT_PARENTAGE, bin, "trying to remove child %s", GST_ELEMENT_NAME (element));
+ GST_CAT_DEBUG (GST_CAT_PARENTAGE, "[%s]: trying to remove child %s", GST_ELEMENT_NAME (bin), GST_ELEMENT_NAME (element));
g_return_if_fail (GST_IS_BIN (bin));
g_return_if_fail (GST_IS_ELEMENT (element));
g_return_if_fail (bin->children != NULL);
- /* must not be in PLAYING state in order to modify bin */
- g_return_if_fail (GST_STATE (bin) != GST_STATE_PLAYING);
-
bclass = GST_BIN_GET_CLASS (bin);
if (bclass->remove_element) {
g_return_if_fail (GST_IS_BIN (bin));
g_return_if_fail (GST_IS_ELEMENT (child));
- GST_INFO (GST_CAT_STATES, "child %s changed state in bin %s from %s to %s",
+ GST_CAT_INFO (GST_CAT_STATES, "child %s changed state in bin %s from %s to %s",
GST_ELEMENT_NAME (child), GST_ELEMENT_NAME (bin),
gst_element_state_get_name (oldstate), gst_element_state_get_name (newstate));
if (bin->child_states[i] != 0) {
gint state = (1 << i);
if (GST_STATE (bin) != state) {
- GST_INFO (GST_CAT_STATES, "bin %s need state change to %s",
+ GST_CAT_INFO (GST_CAT_STATES, "bin %s need state change to %s",
GST_ELEMENT_NAME (bin), gst_element_state_get_name (state));
GST_STATE_PENDING (bin) = state;
GST_UNLOCK (bin);
pending = GST_STATE_PENDING (element);
transition = GST_STATE_TRANSITION (element);
- GST_INFO_ELEMENT (GST_CAT_STATES, element, "changing childrens' state from %s to %s",
- gst_element_state_get_name (old_state), gst_element_state_get_name (pending));
+ GST_CAT_INFO (GST_CAT_STATES, "[%s]: changing childrens' state from %s to %s",
+ GST_ELEMENT_NAME (element),
+ gst_element_state_get_name (old_state), gst_element_state_get_name (pending));
if (pending == GST_STATE_VOID_PENDING)
return GST_STATE_SUCCESS;
+ if (old_state == pending)
+ {
+ GST_CAT_INFO (GST_CAT_STATES, "[%s]: old and pending state are both %s, returning",
+ GST_ELEMENT_NAME (element), gst_element_state_get_name (pending));
+ return GST_STATE_SUCCESS;
+ }
+
children = bin->children;
while (children) {
switch (gst_element_set_state (child, pending)) {
case GST_STATE_FAILURE:
- GST_STATE_PENDING (element) = GST_STATE_VOID_PENDING;
- GST_DEBUG (GST_CAT_STATES, "child '%s' failed to go to state %d(%s)",
+ GST_CAT_DEBUG (GST_CAT_STATES, "child '%s' failed to go to state %d(%s)",
GST_ELEMENT_NAME (child), pending, gst_element_state_get_name (pending));
gst_element_set_state (child, old_child_state);
+ /* FIXME, this is legacy code, a failed state change of a child should
+ * return a failure in all cases */
if (GST_ELEMENT_SCHED (child) == GST_ELEMENT_SCHED (element)) {
/* try to reset it to what is was */
GST_STATE_PENDING (element) = old_state;
- gst_bin_change_state (element);
return GST_STATE_FAILURE;
}
break;
case GST_STATE_ASYNC:
- GST_DEBUG (GST_CAT_STATES, "child '%s' is changing state asynchronously",
+ GST_CAT_DEBUG (GST_CAT_STATES, "child '%s' is changing state asynchronously",
GST_ELEMENT_NAME (child));
have_async = TRUE;
break;
}
}
- GST_INFO_ELEMENT (GST_CAT_STATES, element, "done changing bin's state from %s to %s, now in %s",
+ GST_CAT_INFO (GST_CAT_STATES, "[%s]: done changing bin's state from %s to %s, now in %s",
+ GST_ELEMENT_NAME (element),
gst_element_state_get_name (old_state),
gst_element_state_get_name (pending),
gst_element_state_get_name (GST_STATE (element)));
GstElementStateReturn ret;
if (parent_class->change_state) {
- GST_DEBUG_ELEMENT (GST_CAT_STATES, bin, "setting bin's own state");
+ GST_CAT_DEBUG (GST_CAT_STATES, "[%s]: setting bin's own state", GST_ELEMENT_NAME (bin));
ret = parent_class->change_state (GST_ELEMENT (bin));
return ret;
GList *children, *orig;
GstElement *child;
- GST_DEBUG (GST_CAT_REFCOUNTING, "dispose");
+ GST_CAT_DEBUG (GST_CAT_REFCOUNTING, "dispose");
if (gst_element_get_state (GST_ELEMENT (object)) == GST_STATE_PLAYING)
gst_element_set_state (GST_ELEMENT (object), GST_STATE_PAUSED);
g_return_val_if_fail (GST_IS_BIN (bin), NULL);
g_return_val_if_fail (name != NULL, NULL);
- GST_INFO_ELEMENT (GST_CAT_PARENTAGE, bin, "looking up child element %s", name);
+ GST_CAT_INFO (GST_CAT_PARENTAGE, "[%s]: looking up child element %s",
+ GST_ELEMENT_NAME (bin), name);
children = bin->children;
while (children) {
}
/**
+ * gst_bin_get_by_interface:
+ * @bin: bin to find element in
+ * @interface: interface to be implemented by interface
+ *
+ * Looks for the first element inside the bin that implements the given
+ * interface. If such an element is found, it returns the element. You can
+ * cast this element to the given interface afterwards.
+ * If you want all elements that implement the interface, use
+ * gst_bin_get_all_by_interface(). The function recurses bins inside bins.
+ *
+ * Returns: An element inside the bin implementing the interface.
+ */
+GstElement *
+gst_bin_get_by_interface (GstBin *bin, const GType interface)
+{
+ GList *walk;
+
+ g_return_val_if_fail (GST_IS_BIN (bin), NULL);
+ g_return_val_if_fail (G_TYPE_IS_INTERFACE (interface), NULL);
+
+ walk = bin->children;
+ while (walk) {
+ if (G_TYPE_CHECK_INSTANCE_TYPE (walk->data, interface))
+ return GST_ELEMENT (walk->data);
+ if (GST_IS_BIN (walk->data)) {
+ GstElement *ret;
+ ret = gst_bin_get_by_interface (GST_BIN (walk->data), interface);
+ if (ret)
+ return ret;
+ }
+ walk = g_list_next (walk);
+ }
+
+ return NULL;
+}
+
+/**
+ * gst_bin_get_all_by_interface:
+ * @bin: bin to find elements in
+ * @interface: interface to be implemented by interface
+ *
+ * Looks for all element inside the bin that implements the given
+ * interface. You can safely cast all returned elements to the given interface.
+ * The function recurses bins inside bins. You need to free the list using
+ * g_list_free() after use.
+ *
+ * Returns: An element inside the bin implementing the interface.
+ */
+GList *
+gst_bin_get_all_by_interface (GstBin *bin, const GType interface)
+{
+ GList *walk, *ret = NULL;
+
+ g_return_val_if_fail (GST_IS_BIN (bin), NULL);
+ g_return_val_if_fail (G_TYPE_IS_INTERFACE (interface), NULL);
+
+ walk = bin->children;
+ while (walk) {
+ if (G_TYPE_CHECK_INSTANCE_TYPE (walk->data, interface))
+ ret = g_list_prepend (ret, walk->data);
+ if (GST_IS_BIN (walk->data)) {
+ ret = g_list_concat (ret,
+ gst_bin_get_all_by_interface (GST_BIN (walk->data), interface));
+ }
+ walk = g_list_next (walk);
+ }
+
+ return ret;
+}
+
+/**
* gst_bin_sync_children_state:
* @bin: #Gstbin to sync state
*
state = GST_STATE (bin);
children = bin->children;
- GST_INFO (GST_CAT_STATES, "syncing state of children with bin \"%s\"'s state %s",
+ GST_CAT_INFO (GST_CAT_STATES, "syncing state of children with bin \"%s\"'s state %s",
GST_ELEMENT_NAME (bin), gst_element_state_get_name (state));
while (children) {
childlist = xmlNewChild (parent, NULL, "children", NULL);
- GST_INFO_ELEMENT (GST_CAT_XML, bin, "saving %d children", bin->numchildren);
+ GST_CAT_INFO (GST_CAT_XML, "[%s]: saving %d children",
+ GST_ELEMENT_NAME (bin), bin->numchildren);
children = bin->children;
while (children) {
while (field) {
if (!strcmp (field->name, "children")) {
- GST_INFO_ELEMENT (GST_CAT_XML, GST_ELEMENT (object), "loading children");
+ GST_CAT_INFO (GST_CAT_XML, "[%s]: loading children", GST_ELEMENT_NAME (object));
childlist = field->xmlChildrenNode;
while (childlist) {
if (!strcmp (childlist->name, "element")) {
gboolean
gst_bin_iterate (GstBin *bin)
{
- GstBinClass *oclass;
- gboolean running = TRUE;
-
- GST_DEBUG_ENTER ("(\"%s\")", GST_ELEMENT_NAME (bin));
+ gboolean running;
g_return_val_if_fail (bin != NULL, FALSE);
g_return_val_if_fail (GST_IS_BIN (bin), FALSE);
- oclass = GST_BIN_GET_CLASS (bin);
+ GST_CAT_LOG_OBJECT (GST_CAT_DATAFLOW, bin, "starting iteration");
gst_object_ref (GST_OBJECT (bin));
- if (bin->pre_iterate_func)
- (bin->pre_iterate_func) (bin, bin->pre_iterate_data);
-
- if (oclass->iterate)
- running = (oclass->iterate) (bin);
+ running = FALSE;
+ g_signal_emit (G_OBJECT (bin), gst_bin_signals[ITERATE], 0, &running);
- if (bin->post_iterate_func)
- (bin->post_iterate_func) (bin, bin->post_iterate_data);
-
- GST_DEBUG_LEAVE ("(\"%s\") %d", GST_ELEMENT_NAME (bin), running);
+ GST_CAT_LOG_OBJECT (GST_CAT_DATAFLOW, bin, "finished iteration");
if (!running) {
if (GST_STATE (bin) == GST_STATE_PLAYING &&
GST_STATE_PENDING (bin) == GST_STATE_VOID_PENDING) {
- GST_DEBUG_ELEMENT (GST_CAT_DATAFLOW, bin,
- "polling for child shutdown after useless iteration");
- usleep (1);
- //gst_element_wait_state_change (GST_ELEMENT (bin));
+ GST_CAT_DEBUG (GST_CAT_DATAFLOW, "[%s]: polling for child shutdown after useless iteration",
+ GST_ELEMENT_NAME (bin));
+ g_usleep (1);
running = TRUE;
}
}
return running;
}
-/**
- * gst_bin_set_pre_iterate_function:
- * @bin: #Gstbin to attach to
- * @func: callback function to call
- * @user_data: user data to put in the function call
- *
- * Attaches a callback which will be run before every iteration of the bin
- *
- */
-void
-gst_bin_set_pre_iterate_function (GstBin *bin, GstBinPrePostIterateFunction func, gpointer user_data)
-{
- g_return_if_fail (GST_IS_BIN (bin));
-
- if (!GST_FLAG_IS_SET (bin, GST_BIN_FLAG_MANAGER))
- g_warning ("setting pre_iterate on a non MANAGER bin has no effect");
-
- bin->pre_iterate_func = func;
- bin->pre_iterate_data = user_data;
-}
-
-/**
- * gst_bin_set_post_iterate_function:
- * @bin: #Gstbin to attach to
- * @func: callback function to call
- * @user_data: user data to put in the function call
- *
- * Attaches a callback which will be run after every iteration of the bin
- *
- */
-void
-gst_bin_set_post_iterate_function (GstBin *bin, GstBinPrePostIterateFunction func, gpointer user_data)
-{
- g_return_if_fail (GST_IS_BIN (bin));
-
- if (!GST_FLAG_IS_SET (bin, GST_BIN_FLAG_MANAGER))
- g_warning ("setting post_iterate on a non MANAGER bin has no effect");
-
- bin->post_iterate_func = func;
- bin->post_iterate_data = user_data;
-}