X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gst%2Fgstelement.c;h=64aa39a04fd1db11d99c795e5e1e8d83e2e29869;hb=ce43de86902c4e9c8ed4e9682602664cb9bce2ee;hp=d6deca8139f3893d90d8c680711842950d1a80b4;hpb=66d19b65fb6eee8283a6c2ac742be251f66d631c;p=platform%2Fupstream%2Fgstreamer.git diff --git a/gst/gstelement.c b/gst/gstelement.c index d6deca8..64aa39a 100644 --- a/gst/gstelement.c +++ b/gst/gstelement.c @@ -16,12 +16,13 @@ * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ /** * SECTION:gstelement + * @title: GstElement * @short_description: Abstract base class for all pipeline elements * @see_also: #GstElementFactory, #GstPad * @@ -34,7 +35,7 @@ * 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 + * Elements can have pads (of the type #GstPad). These pads link to pads on * other elements. #GstBuffer flow between these linked pads. * A #GstElement has a #GList of #GstPad structures for all their input (or sink) * and output (or source) pads. @@ -43,8 +44,7 @@ * * An existing pad of an element can be retrieved by name with * gst_element_get_static_pad(). A new dynamic pad can be created using - * gst_element_request_pad() with a #GstPadTemplate or - * gst_element_get_request_pad() with the template name such as "src_\%d". + * gst_element_request_pad() with a #GstPadTemplate. * An iterator of all pads can be retrieved with gst_element_iterate_pads(). * * Elements can be linked through their pads. @@ -65,23 +65,18 @@ * 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 + * the #GST_ELEMENT_FLAG_PROVIDE_CLOCK flag is set. 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 + * Not all elements require a clock to operate correctly. If the + * #GST_ELEMENT_FLAG_REQUIRE_CLOCK() flag is set, a clock should be set on the * element with gst_element_set_clock(). * - * Note that clock slection and distribution is normally handled by the + * Note that clock selection 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 2009-05-29 (0.10.24) */ -/* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex - * with newer GLib versions (>= 2.31.0) */ -#define GLIB_DISABLE_DEPRECATION_WARNINGS #include "gst_private.h" #include #include @@ -91,15 +86,20 @@ #include "gstelementmetadata.h" #include "gstenumtypes.h" #include "gstbus.h" -#include "gstmarshal.h" #include "gsterror.h" #include "gstevent.h" #include "gstutils.h" #include "gstinfo.h" +#include "gstquark.h" +#include "gsttracerutils.h" #include "gstvalue.h" #include "gst-i18n-lib.h" #include "glib-compat-private.h" +#ifndef GST_DISABLE_GST_DEBUG +#include "printf/printf.h" +#endif + /* Element signals and args */ enum { @@ -119,8 +119,8 @@ enum static void gst_element_class_init (GstElementClass * klass); static void gst_element_init (GstElement * element); static void gst_element_base_class_init (gpointer g_class); -static void gst_element_base_class_finalize (gpointer g_class); +static void gst_element_constructed (GObject * object); static void gst_element_dispose (GObject * object); static void gst_element_finalize (GObject * object); @@ -133,6 +133,10 @@ static GstStateChangeReturn gst_element_set_state_func (GstElement * element, static gboolean gst_element_set_clock_func (GstElement * element, GstClock * clock); static void gst_element_set_bus_func (GstElement * element, GstBus * bus); +static gboolean gst_element_post_message_default (GstElement * element, + GstMessage * message); +static void gst_element_set_context_default (GstElement * element, + GstContext * context); static gboolean gst_element_default_send_event (GstElement * element, GstEvent * event); @@ -143,9 +147,13 @@ static GstPadTemplate * gst_element_class_get_request_pad_template (GstElementClass * element_class, const gchar * name); +static void gst_element_call_async_func (gpointer data, gpointer user_data); + static GstObjectClass *parent_class = NULL; static guint gst_element_signals[LAST_SIGNAL] = { 0 }; +static GThreadPool *gst_element_pool = NULL; + /* this is used in gstelementfactory.c:gst_element_register() */ GQuark __gst_elementclass_factory = 0; @@ -159,7 +167,7 @@ gst_element_get_type (void) static const GTypeInfo element_info = { sizeof (GstElementClass), gst_element_base_class_init, - gst_element_base_class_finalize, + NULL, /* base_class_finalize */ (GClassInitFunc) gst_element_class_init, NULL, NULL, @@ -180,6 +188,21 @@ gst_element_get_type (void) } static void +gst_element_setup_thread_pool (void) +{ + GError *err = NULL; + + GST_DEBUG ("creating element thread pool"); + gst_element_pool = + g_thread_pool_new ((GFunc) gst_element_call_async_func, NULL, -1, FALSE, + &err); + if (err != NULL) { + g_critical ("could not alloc threadpool %s", err->message); + g_clear_error (&err); + } +} + +static void gst_element_class_init (GstElementClass * klass) { GObjectClass *gobject_class; @@ -202,7 +225,7 @@ gst_element_class_init (GstElementClass * klass) gst_element_signals[PAD_ADDED] = g_signal_new ("pad-added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstElementClass, pad_added), NULL, NULL, - gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_PAD); + g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_PAD); /** * GstElement::pad-removed: * @gstelement: the object which received the signal @@ -213,7 +236,7 @@ gst_element_class_init (GstElementClass * klass) gst_element_signals[PAD_REMOVED] = g_signal_new ("pad-removed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstElementClass, pad_removed), NULL, NULL, - gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_PAD); + g_cclosure_marshal_generic, G_TYPE_NONE, 1, GST_TYPE_PAD); /** * GstElement::no-more-pads: * @gstelement: the object which received the signal @@ -225,10 +248,11 @@ gst_element_class_init (GstElementClass * klass) gst_element_signals[NO_MORE_PADS] = g_signal_new ("no-more-pads", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstElementClass, no_more_pads), NULL, - NULL, gst_marshal_VOID__VOID, G_TYPE_NONE, 0); + NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 0); gobject_class->dispose = gst_element_dispose; gobject_class->finalize = gst_element_finalize; + gobject_class->constructed = gst_element_constructed; klass->change_state = GST_DEBUG_FUNCPTR (gst_element_change_state_func); klass->set_state = GST_DEBUG_FUNCPTR (gst_element_set_state_func); @@ -238,8 +262,12 @@ gst_element_class_init (GstElementClass * klass) klass->query = GST_DEBUG_FUNCPTR (gst_element_default_query); klass->send_event = GST_DEBUG_FUNCPTR (gst_element_default_send_event); klass->numpadtemplates = 0; + klass->post_message = GST_DEBUG_FUNCPTR (gst_element_post_message_default); + klass->set_context = GST_DEBUG_FUNCPTR (gst_element_set_context_default); klass->elementfactory = NULL; + + gst_element_setup_thread_pool (); } static void @@ -270,19 +298,8 @@ gst_element_base_class_init (gpointer g_class) element_class->elementfactory = g_type_get_qdata (G_TYPE_FROM_CLASS (element_class), __gst_elementclass_factory); - GST_DEBUG ("type %s : factory %p", G_OBJECT_CLASS_NAME (element_class), - element_class->elementfactory); -} - -static void -gst_element_base_class_finalize (gpointer g_class) -{ - GstElementClass *klass = GST_ELEMENT_CLASS (g_class); - - g_list_foreach (klass->padtemplates, (GFunc) gst_object_unref, NULL); - g_list_free (klass->padtemplates); - - gst_structure_free (klass->metadata); + GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "type %s : factory %p", + G_OBJECT_CLASS_NAME (element_class), element_class->elementfactory); } static void @@ -294,8 +311,15 @@ gst_element_init (GstElement * element) GST_STATE_PENDING (element) = GST_STATE_VOID_PENDING; GST_STATE_RETURN (element) = GST_STATE_CHANGE_SUCCESS; - g_static_rec_mutex_init (&element->state_lock); - element->state_cond = g_cond_new (); + g_rec_mutex_init (&element->state_lock); + g_cond_init (&element->state_cond); +} + +static void +gst_element_constructed (GObject * object) +{ + GST_TRACER_ELEMENT_NEW (GST_ELEMENT_CAST (object)); + G_OBJECT_CLASS (parent_class)->constructed (object); } /** @@ -304,10 +328,10 @@ gst_element_init (GstElement * element) * @pad: the #GstPad to release. * * Makes the element free the previously requested pad as obtained - * with gst_element_get_request_pad(). + * with gst_element_request_pad(). * * This does not unref the pad. If the pad was created by using - * gst_element_get_request_pad(), gst_element_release_request_pad() needs to be + * gst_element_request_pad(), gst_element_release_request_pad() needs to be * followed by gst_object_unref() to free the @pad. * * MT safe. @@ -319,13 +343,17 @@ gst_element_release_request_pad (GstElement * element, GstPad * pad) g_return_if_fail (GST_IS_ELEMENT (element)); g_return_if_fail (GST_IS_PAD (pad)); + g_return_if_fail (GST_PAD_PAD_TEMPLATE (pad) == NULL || + GST_PAD_TEMPLATE_PRESENCE (GST_PAD_PAD_TEMPLATE (pad)) == + GST_PAD_REQUEST); + g_return_if_fail (GST_PAD_PARENT (pad) == element); oclass = GST_ELEMENT_GET_CLASS (element); /* if the element implements a custom release function we call that, else we * simply remove the pad from the element */ if (oclass->release_pad) - (oclass->release_pad) (element, pad); + oclass->release_pad (element, pad); else gst_element_remove_pad (element, pad); } @@ -335,11 +363,11 @@ gst_element_release_request_pad (GstElement * element, GstPad * pad) * @element: a #GstElement to query * * Get the clock provided by the given element. - * An element is only required to provide a clock in the PAUSED - * state. Some elements can provide a clock in other states. + * > An element is only required to provide a clock in the PAUSED + * > state. Some elements can provide a clock in other states. * - * Returns: (transfer full): the GstClock provided by the element or %NULL - * if no clock could be provided. Unref after usage. + * Returns: (transfer full) (nullable): the GstClock provided by the + * element or %NULL if no clock could be provided. Unref after usage. * * MT safe. */ @@ -375,7 +403,7 @@ gst_element_set_clock_func (GstElement * element, GstClock * clock) /** * gst_element_set_clock: * @element: a #GstElement to set the clock for. - * @clock: the #GstClock to set for the element. + * @clock: (transfer none) (allow-none): the #GstClock to set for the element. * * Sets the clock for the element. This function increases the * refcount on the clock. Any previously set clock on the object @@ -413,7 +441,10 @@ gst_element_set_clock (GstElement * element, GstClock * clock) * Gets the currently configured clock of the element. This is the clock as was * last set with gst_element_set_clock(). * - * Returns: (transfer full): the #GstClock of the element. unref after usage. + * Elements in a pipeline will only have their clock set when the + * pipeline is in the PLAYING state. + * + * Returns: (transfer full) (nullable): the #GstClock of the element. unref after usage. * * MT safe. */ @@ -502,8 +533,6 @@ gst_element_get_base_time (GstElement * element) * pipelines, and you can also ensure that the pipelines have the same clock. * * MT safe. - * - * Since: 0.10.24 */ void gst_element_set_start_time (GstElement * element, GstClockTime time) @@ -535,8 +564,6 @@ gst_element_set_start_time (GstElement * element, GstClockTime time) * MT safe. * * Returns: the start time of the element. - * - * Since: 0.10.24 */ GstClockTime gst_element_get_start_time (GstElement * element) @@ -583,8 +610,8 @@ gst_element_set_index (GstElement * element, GstIndex * index) * * Gets the index from the element. * - * Returns: (transfer full): a #GstIndex or %NULL when no index was set on the - * element. unref after usage. + * Returns: (transfer full) (nullable): a #GstIndex or %NULL when no + * index was set on the element. unref after usage. * * MT safe. */ @@ -608,14 +635,13 @@ gst_element_get_index (GstElement * element) /** * gst_element_add_pad: * @element: a #GstElement to add the pad to. - * @pad: (transfer full): the #GstPad to add to the element. + * @pad: (transfer floating): the #GstPad to add to the element. * * Adds a pad (link point) to @element. @pad's parent will be set to @element; * see gst_object_set_parent() for refcounting information. * - * 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. See gst_pad_set_active() for more information about activating pads. + * Pads are automatically activated when added in the PAUSED or PLAYING + * state. * * The pad and the element should be unlocked when calling this function. * @@ -631,7 +657,7 @@ gboolean gst_element_add_pad (GstElement * element, GstPad * pad) { gchar *pad_name; - gboolean flushing; + gboolean active; g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); g_return_val_if_fail (GST_IS_PAD (pad), FALSE); @@ -641,7 +667,7 @@ gst_element_add_pad (GstElement * element, GstPad * 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)); - flushing = GST_PAD_IS_FLUSHING (pad); + active = GST_PAD_IS_ACTIVE (pad); GST_OBJECT_FLAG_SET (pad, GST_PAD_FLAG_NEED_PARENT); GST_OBJECT_UNLOCK (pad); @@ -655,16 +681,10 @@ gst_element_add_pad (GstElement * element, GstPad * pad) GST_OBJECT_CAST (element)))) goto had_parent; - /* check for flushing pads */ - if (flushing && (GST_STATE (element) > GST_STATE_READY || + /* check for active pads */ + if (!active && (GST_STATE (element) > GST_STATE_READY || GST_STATE_NEXT (element) == GST_STATE_PAUSED)) { - g_warning ("adding flushing pad '%s' to running element '%s', you need to " - "use gst_pad_set_active(pad,TRUE) before adding it.", - GST_STR_NULL (pad_name), GST_ELEMENT_NAME (element)); - /* unset flushing */ - GST_OBJECT_LOCK (pad); - GST_PAD_UNSET_FLUSHING (pad); - GST_OBJECT_UNLOCK (pad); + gst_pad_set_active (pad, TRUE); } g_free (pad_name); @@ -672,24 +692,24 @@ gst_element_add_pad (GstElement * element, GstPad * pad) /* add it to the list */ switch (gst_pad_get_direction (pad)) { case GST_PAD_SRC: - element->srcpads = g_list_prepend (element->srcpads, pad); + element->srcpads = g_list_append (element->srcpads, pad); element->numsrcpads++; break; case GST_PAD_SINK: - element->sinkpads = g_list_prepend (element->sinkpads, pad); + element->sinkpads = g_list_append (element->sinkpads, pad); element->numsinkpads++; break; default: goto no_direction; } - element->pads = g_list_prepend (element->pads, pad); + element->pads = g_list_append (element->pads, pad); element->numpads++; element->pads_cookie++; GST_OBJECT_UNLOCK (element); /* emit the PAD_ADDED signal */ g_signal_emit (element, gst_element_signals[PAD_ADDED], 0, pad); - + GST_TRACER_ELEMENT_ADD_PAD (element, pad); return TRUE; /* ERROR cases */ @@ -699,6 +719,8 @@ name_exists: pad_name, GST_ELEMENT_NAME (element)); GST_OBJECT_UNLOCK (element); g_free (pad_name); + gst_object_ref_sink (pad); + gst_object_unref (pad); return FALSE; } had_parent: @@ -732,7 +754,7 @@ no_direction: * * This function is used by plugin developers and should not be used * by applications. Pads that were dynamically requested from elements - * with gst_element_get_request_pad() should be released with the + * with gst_element_request_pad() should be released with the * gst_element_release_request_pad() function instead. * * Pads are not automatically deactivated so elements should perform the needed @@ -801,7 +823,7 @@ gst_element_remove_pad (GstElement * element, GstPad * pad) /* emit the PAD_REMOVED signal before unparenting and losing the last ref. */ g_signal_emit (element, gst_element_signals[PAD_REMOVED], 0, pad); - + GST_TRACER_ELEMENT_REMOVE_PAD (element, pad); gst_object_unparent (GST_OBJECT_CAST (pad)); return TRUE; @@ -864,8 +886,8 @@ pad_compare_name (GstPad * pad1, const gchar * name) * Retrieves a pad from @element by name. This version only retrieves * already-existing (i.e. 'static') pads. * - * Returns: (transfer full): the requested #GstPad if found, otherwise %NULL. - * unref after usage. + * Returns: (transfer full) (nullable): the requested #GstPad if + * found, otherwise %NULL. unref after usage. * * MT safe. */ @@ -898,57 +920,143 @@ gst_element_get_static_pad (GstElement * element, const gchar * name) return result; } -static GstPad * -_gst_element_request_pad (GstElement * element, GstPadTemplate * templ, - const gchar * name, const GstCaps * caps) +static gboolean +gst_element_is_valid_request_template_name (const gchar * templ_name, + const gchar * name) { - GstPad *newpad = NULL; - GstElementClass *oclass; + gchar *endptr; + const gchar *templ_name_ptr, *name_ptr; + gboolean next_specifier; + guint templ_postfix_len = 0, name_postfix_len = 0; - oclass = GST_ELEMENT_GET_CLASS (element); + g_return_val_if_fail (templ_name != NULL, FALSE); + g_return_val_if_fail (name != NULL, FALSE); -#ifndef G_DISABLE_CHECKS - /* Some sanity checking here */ - if (name) { - GstPad *pad; + /* Is this the template name? */ + if (strcmp (templ_name, name) == 0) + return TRUE; - /* Is this the template name? */ - if (strstr (name, "%") || !strchr (templ->name_template, '%')) { - g_return_val_if_fail (strcmp (name, templ->name_template) == 0, NULL); - } else { - const gchar *str, *data; - gchar *endptr; + /* otherwise check all the specifiers */ + do { + /* Because of sanity checks in gst_pad_template_new(), we know that %s + * and %d and %u, occurring at the template_name */ + templ_name_ptr = strchr (templ_name, '%'); + + /* check characters ahead of the specifier */ + if (!templ_name_ptr || strlen (name) <= templ_name_ptr - templ_name + || strncmp (templ_name, name, templ_name_ptr - templ_name) != 0) { + return FALSE; + } + + /* %s is not allowed for multiple specifiers, just a single specifier can be + * accepted in gst_pad_template_new() and can not be mixed with other + * specifier '%u' and '%d' */ + if (*(templ_name_ptr + 1) == 's' && g_strcmp0 (templ_name, name) == 0) { + return TRUE; + } - /* Otherwise check if it's a valid name for the name template */ - str = strchr (templ->name_template, '%'); - g_return_val_if_fail (str != NULL, NULL); - g_return_val_if_fail (strncmp (templ->name_template, name, - str - templ->name_template) == 0, NULL); - g_return_val_if_fail (strlen (name) > str - templ->name_template, NULL); + name_ptr = name + (templ_name_ptr - templ_name); - data = name + (str - templ->name_template); + /* search next specifier, each of specifier should be separated by '_' */ + templ_name = strchr (templ_name_ptr, '_'); + name = strchr (name_ptr, '_'); + + /* don't match the number of specifiers */ + if ((templ_name && !name) || (!templ_name && name)) + return FALSE; + + if (templ_name && name) + next_specifier = TRUE; + else + next_specifier = FALSE; + + /* check characters followed by the specifier */ + if (*(templ_name_ptr + 2) != '\0' && *(templ_name_ptr + 2) != '_') { + if (next_specifier) { + templ_postfix_len = templ_name - (templ_name_ptr + 2); + name_postfix_len = name - name_ptr; + } else { + templ_postfix_len = strlen (templ_name_ptr + 2); + name_postfix_len = strlen (name_ptr); + } + + if (strncmp (templ_name_ptr + 2, + name_ptr + name_postfix_len - templ_postfix_len, + templ_postfix_len) != 0) { + return FALSE; + } + } + + /* verify the specifier */ + if (*(name_ptr) == '%') { + guint len; + + len = (next_specifier) ? name - name_ptr : strlen (name_ptr); + + if (strncmp (name_ptr, templ_name_ptr, len) != 0) + return FALSE; + + } else { + const gchar *specifier; + gchar *target = NULL; + + /* extract specifier when it has postfix characters */ + if (name_postfix_len > templ_postfix_len) { + target = g_strndup (name_ptr, name_postfix_len - templ_postfix_len); + } + specifier = target ? target : name_ptr; - /* Can either be %s or %d or %u, do sanity checking for %d */ - if (*(str + 1) == 'd') { + if (*(templ_name_ptr + 1) == 'd') { gint64 tmp; /* it's an int */ - tmp = g_ascii_strtoll (data, &endptr, 10); - g_return_val_if_fail (tmp >= G_MININT && tmp <= G_MAXINT - && *endptr == '\0', NULL); - } else if (*(str + 1) == 'u') { + tmp = g_ascii_strtoll (specifier, &endptr, 10); + if (tmp < G_MININT || tmp > G_MAXINT || (*endptr != '\0' + && *endptr != '_')) + return FALSE; + } else if (*(templ_name_ptr + 1) == 'u') { guint64 tmp; /* it's an int */ - tmp = g_ascii_strtoull (data, &endptr, 10); - g_return_val_if_fail (tmp <= G_MAXUINT && *endptr == '\0', NULL); + tmp = g_ascii_strtoull (specifier, &endptr, 10); + if (tmp > G_MAXUINT || (*endptr != '\0' && *endptr != '_')) + return FALSE; } + + g_free (target); + } + + /* otherwise we increment these from NULL to 1 */ + if (next_specifier) { + templ_name++; + name++; } + } while (next_specifier); + + return TRUE; +} + +static GstPad * +_gst_element_request_pad (GstElement * element, GstPadTemplate * templ, + const gchar * name, const GstCaps * caps) +{ + GstPad *newpad = NULL; + GstElementClass *oclass; + + oclass = GST_ELEMENT_GET_CLASS (element); + +#ifndef G_DISABLE_CHECKS + /* Some sanity checking here */ + if (name) { + GstPad *pad; + + g_return_val_if_fail (gst_element_is_valid_request_template_name + (templ->name_template, name), NULL); pad = gst_element_get_static_pad (element, name); if (pad) { gst_object_unref (pad); - /* FIXME 0.11: Change this to g_return_val_if_fail() */ + /* FIXME 2.0: Change this to g_return_val_if_fail() */ g_critical ("Element %s already has a pad named %s, the behaviour of " " gst_element_get_request_pad() for existing pads is undefined!", GST_ELEMENT_NAME (element), name); @@ -974,11 +1082,12 @@ _gst_element_request_pad (GstElement * element, GstPadTemplate * templ, * retrieves request pads. The pad should be released with * gst_element_release_request_pad(). * - * This method is slow and will be deprecated in the future. New code should - * use gst_element_request_pad() with the requested template. + * This method is slower than manually getting the pad template and calling + * gst_element_request_pad() if the pads should have a specific name (e.g. + * @name is "src_1" instead of "src_\%u"). * - * Returns: (transfer full): requested #GstPad if found, otherwise %NULL. - * Release after usage. + * Returns: (transfer full) (nullable): requested #GstPad if found, + * otherwise %NULL. Release after usage. */ GstPad * gst_element_get_request_pad (GstElement * element, const gchar * name) @@ -988,8 +1097,6 @@ gst_element_get_request_pad (GstElement * element, const gchar * name) const gchar *req_name = NULL; gboolean templ_found = FALSE; GList *list; - const gchar *data; - gchar *str, *endptr = NULL; GstElementClass *class; g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); @@ -997,13 +1104,10 @@ gst_element_get_request_pad (GstElement * element, const gchar * name) class = GST_ELEMENT_GET_CLASS (element); - /* if the name contains a %, we assume it's the complete template name. Get - * the template and try to get a pad */ - if (strstr (name, "%")) { - templ = gst_element_class_get_request_pad_template (class, name); - req_name = NULL; - if (templ) - templ_found = TRUE; + templ = gst_element_class_get_request_pad_template (class, name); + if (templ) { + req_name = strstr (name, "%") ? NULL : name; + templ_found = TRUE; } else { /* there is no % in the name, try to find a matching template */ list = class->padtemplates; @@ -1012,48 +1116,12 @@ gst_element_get_request_pad (GstElement * element, const gchar * name) if (templ->presence == GST_PAD_REQUEST) { GST_CAT_DEBUG (GST_CAT_PADS, "comparing %s to %s", name, templ->name_template); - /* see if we find an exact match */ - if (strcmp (name, templ->name_template) == 0) { + if (gst_element_is_valid_request_template_name (templ->name_template, + name)) { templ_found = TRUE; req_name = name; break; } - /* Because of sanity checks in gst_pad_template_new(), we know that %s - and %d and %u, occurring at the end of the name_template, are the only - possibilities. */ - else if ((str = strchr (templ->name_template, '%')) - && strncmp (templ->name_template, name, - str - templ->name_template) == 0 - && strlen (name) > str - templ->name_template) { - data = name + (str - templ->name_template); - if (*(str + 1) == 'd') { - glong tmp; - - /* it's an int */ - tmp = strtol (data, &endptr, 10); - if (tmp != G_MINLONG && tmp != G_MAXLONG && endptr && - *endptr == '\0') { - templ_found = TRUE; - req_name = name; - break; - } - } else if (*(str + 1) == 'u') { - gulong tmp; - - /* it's an int */ - tmp = strtoul (data, &endptr, 10); - if (tmp != G_MAXULONG && endptr && *endptr == '\0') { - templ_found = TRUE; - req_name = name; - break; - } - } else { - /* it's a string */ - templ_found = TRUE; - req_name = name; - break; - } - } } list = list->next; } @@ -1068,7 +1136,7 @@ gst_element_get_request_pad (GstElement * element, const gchar * name) } /** - * gst_element_request_pad: + * gst_element_request_pad: (virtual request_new_pad) * @element: a #GstElement to find a request pad of. * @templ: a #GstPadTemplate of which we want a pad of. * @name: (transfer none) (allow-none): the name of the request #GstPad @@ -1082,10 +1150,8 @@ gst_element_get_request_pad (GstElement * element, const gchar * name) * * The pad should be released with gst_element_release_request_pad(). * - * Returns: (transfer full): requested #GstPad if found, otherwise %NULL. - * Release after usage. - * - * Since: 0.10.32 + * Returns: (transfer full) (nullable): requested #GstPad if found, + * otherwise %NULL. Release after usage. */ GstPad * gst_element_request_pad (GstElement * element, @@ -1093,6 +1159,7 @@ gst_element_request_pad (GstElement * element, { g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); g_return_val_if_fail (templ != NULL, NULL); + g_return_val_if_fail (templ->presence == GST_PAD_REQUEST, NULL); return _gst_element_request_pad (element, templ, name, caps); } @@ -1119,8 +1186,10 @@ gst_element_iterate_pad_list (GstElement * element, GList ** padlist) * be freed after usage. Also more specialized iterators exists such as * gst_element_iterate_src_pads() or gst_element_iterate_sink_pads(). * - * Returns: (transfer full): the #GstIterator of #GstPad. Unref each pad - * after use. + * The order of pads returned by the iterator will be the order in which + * the pads were added to the element. + * + * Returns: (transfer full): the #GstIterator of #GstPad. * * MT safe. */ @@ -1138,8 +1207,10 @@ gst_element_iterate_pads (GstElement * element) * * Retrieves an iterator of @element's source pads. * - * Returns: (transfer full): the #GstIterator of #GstPad. Unref each pad - * after use. + * The order of pads returned by the iterator will be the order in which + * the pads were added to the element. + * + * Returns: (transfer full): the #GstIterator of #GstPad. * * MT safe. */ @@ -1157,8 +1228,10 @@ gst_element_iterate_src_pads (GstElement * element) * * Retrieves an iterator of @element's sink pads. * - * Returns: (transfer full): the #GstIterator of #GstPad. Unref each pad - * after use. + * The order of pads returned by the iterator will be the order in which + * the pads were added to the element. + * + * Returns: (transfer full): the #GstIterator of #GstPad. * * MT safe. */ @@ -1170,15 +1243,133 @@ gst_element_iterate_sink_pads (GstElement * element) return gst_element_iterate_pad_list (element, &element->sinkpads); } +static gboolean +gst_element_do_foreach_pad (GstElement * element, + GstElementForeachPadFunc func, gpointer user_data, + GList ** p_pads, guint16 * p_npads) +{ + gboolean ret = TRUE; + GstPad **pads; + guint n_pads, i; + GList *l; + + g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); + g_return_val_if_fail (func != NULL, FALSE); + + GST_OBJECT_LOCK (element); + n_pads = *p_npads; + pads = g_newa (GstPad *, n_pads + 1); + for (l = *p_pads, i = 0; l != NULL; l = l->next) { + g_assert (i < n_pads); + pads[i++] = gst_object_ref (l->data); + } + GST_OBJECT_UNLOCK (element); + + if (n_pads == 0) + return FALSE; + + for (i = 0; i < n_pads; ++i) { + ret = func (element, pads[i], user_data); + if (!ret) + break; + } + + for (i = 0; i < n_pads; ++i) + gst_object_unref (pads[i]); + + return ret; +} + +/** + * gst_element_foreach_sink_pad: + * @element: a #GstElement to iterate sink pads of + * @func: (scope call): function to call for each sink pad + * @user_data: (closure): user data passed to @func + * + * Call @func with @user_data for each of @element's sink pads. @func will be + * called exactly once for each sink pad that exists at the time of this call, + * unless one of the calls to @func returns %FALSE in which case we will stop + * iterating pads and return early. If new sink pads are added or sink pads + * are removed while the sink pads are being iterated, this will not be taken + * into account until next time this function is used. + * + * Returns: %FALSE if @element had no sink pads or if one of the calls to @func + * returned %FALSE. + * + * Since: 1.14 + */ +gboolean +gst_element_foreach_sink_pad (GstElement * element, + GstElementForeachPadFunc func, gpointer user_data) +{ + return gst_element_do_foreach_pad (element, func, user_data, + &element->sinkpads, &element->numsinkpads); +} + +/** + * gst_element_foreach_src_pad: + * @element: a #GstElement to iterate source pads of + * @func: (scope call): function to call for each source pad + * @user_data: (closure): user data passed to @func + * + * Call @func with @user_data for each of @element's source pads. @func will be + * called exactly once for each source pad that exists at the time of this call, + * unless one of the calls to @func returns %FALSE in which case we will stop + * iterating pads and return early. If new source pads are added or source pads + * are removed while the source pads are being iterated, this will not be taken + * into account until next time this function is used. + * + * Returns: %FALSE if @element had no source pads or if one of the calls + * to @func returned %FALSE. + * + * Since: 1.14 + */ +gboolean +gst_element_foreach_src_pad (GstElement * element, + GstElementForeachPadFunc func, gpointer user_data) +{ + return gst_element_do_foreach_pad (element, func, user_data, + &element->srcpads, &element->numsrcpads); +} + +/** + * gst_element_foreach_pad: + * @element: a #GstElement to iterate pads of + * @func: (scope call): function to call for each pad + * @user_data: (closure): user data passed to @func + * + * Call @func with @user_data for each of @element's pads. @func will be called + * exactly once for each pad that exists at the time of this call, unless + * one of the calls to @func returns %FALSE in which case we will stop + * iterating pads and return early. If new pads are added or pads are removed + * while pads are being iterated, this will not be taken into account until + * next time this function is used. + * + * Returns: %FALSE if @element had no pads or if one of the calls to @func + * returned %FALSE. + * + * Since: 1.14 + */ +gboolean +gst_element_foreach_pad (GstElement * element, GstElementForeachPadFunc func, + gpointer user_data) +{ + return gst_element_do_foreach_pad (element, func, user_data, + &element->pads, &element->numpads); +} + /** * gst_element_class_add_pad_template: * @klass: the #GstElementClass to add the pad template to. - * @templ: (transfer full): a #GstPadTemplate to add to the element class. + * @templ: (transfer floating): a #GstPadTemplate to add to the element class. * * Adds a padtemplate to an element class. This is mainly used in the _class_init * functions of classes. If a pad template with the same name as an already * existing one is added the old one is replaced by the new one. * + * @templ's reference count will be incremented, and any floating + * reference will be removed (see gst_object_ref_sink()) + * */ void gst_element_class_add_pad_template (GstElementClass * klass, @@ -1196,6 +1387,7 @@ gst_element_class_add_pad_template (GstElementClass * klass, /* Found pad with the same name, replace and return */ if (strcmp (templ->name_template, padtempl->name_template) == 0) { + gst_object_ref_sink (padtempl); gst_object_unref (padtempl); template_list->data = templ; return; @@ -1211,6 +1403,48 @@ gst_element_class_add_pad_template (GstElementClass * klass, } /** + * gst_element_class_add_static_pad_template: + * @klass: the #GstElementClass to add the pad template to. + * @static_templ: #GstStaticPadTemplate to add as pad template to the element class. + * + * Adds a pad template to an element class based on the static pad template + * @templ. This is mainly used in the _class_init functions of element + * implementations. If a pad template with the same name already exists, + * the old one is replaced by the new one. + * + * Since: 1.8 + */ +void +gst_element_class_add_static_pad_template (GstElementClass * klass, + GstStaticPadTemplate * static_templ) +{ + gst_element_class_add_pad_template (klass, + gst_static_pad_template_get (static_templ)); +} + +/** + * gst_element_class_add_static_pad_template_with_gtype: + * @klass: the #GstElementClass to add the pad template to. + * @static_templ: #GstStaticPadTemplate to add as pad template to the element class. + * @pad_type: The #GType of the pad to create + * + * Adds a pad template to an element class based on the static pad template + * @templ. This is mainly used in the _class_init functions of element + * implementations. If a pad template with the same name already exists, + * the old one is replaced by the new one. + * + * Since: 1.14 + */ +void +gst_element_class_add_static_pad_template_with_gtype (GstElementClass * klass, + GstStaticPadTemplate * static_templ, GType pad_type) +{ + gst_element_class_add_pad_template (klass, + gst_pad_template_new_from_static_pad_template_with_gtype (static_templ, + pad_type)); +} + +/** * gst_element_class_add_metadata: * @klass: class to set metadata for * @key: the key to set @@ -1231,6 +1465,34 @@ gst_element_class_add_metadata (GstElementClass * klass, } /** + * gst_element_class_add_static_metadata: + * @klass: class to set metadata for + * @key: the key to set + * @value: the value to set + * + * Set @key with @value as metadata in @klass. + * + * Same as gst_element_class_add_metadata(), but @value must be a static string + * or an inlined string, as it will not be copied. (GStreamer plugins will + * be made resident once loaded, so this function can be used even from + * dynamically loaded plugins.) + */ +void +gst_element_class_add_static_metadata (GstElementClass * klass, + const gchar * key, const gchar * value) +{ + GValue val = G_VALUE_INIT; + + g_return_if_fail (GST_IS_ELEMENT_CLASS (klass)); + g_return_if_fail (key != NULL); + g_return_if_fail (value != NULL); + + g_value_init (&val, G_TYPE_STRING); + g_value_set_static_string (&val, value); + gst_structure_take_value ((GstStructure *) klass->metadata, key, &val); +} + +/** * gst_element_class_set_metadata: * @klass: class to set metadata for * @longname: The long English name of the element. E.g. "File Sink" @@ -1243,7 +1505,7 @@ gst_element_class_add_metadata (GstElementClass * klass, * multiple author metadata. E.g: "Joe Bloggs <joe.blogs at foo.com>" * * Sets the detailed information for a #GstElementClass. - * This function is for use in _class_init functions only. + * > This function is for use in _class_init functions only. */ void gst_element_class_set_metadata (GstElementClass * klass, @@ -1251,12 +1513,67 @@ gst_element_class_set_metadata (GstElementClass * klass, const gchar * description, const gchar * author) { g_return_if_fail (GST_IS_ELEMENT_CLASS (klass)); + g_return_if_fail (longname != NULL && *longname != '\0'); + g_return_if_fail (classification != NULL && *classification != '\0'); + g_return_if_fail (description != NULL && *description != '\0'); + g_return_if_fail (author != NULL && *author != '\0'); - gst_structure_set ((GstStructure *) klass->metadata, - GST_ELEMENT_METADATA_LONGNAME, G_TYPE_STRING, longname, - GST_ELEMENT_METADATA_KLASS, G_TYPE_STRING, classification, - GST_ELEMENT_METADATA_DESCRIPTION, G_TYPE_STRING, description, - GST_ELEMENT_METADATA_AUTHOR, G_TYPE_STRING, author, NULL); + gst_structure_id_set ((GstStructure *) klass->metadata, + GST_QUARK (ELEMENT_METADATA_LONGNAME), G_TYPE_STRING, longname, + GST_QUARK (ELEMENT_METADATA_KLASS), G_TYPE_STRING, classification, + GST_QUARK (ELEMENT_METADATA_DESCRIPTION), G_TYPE_STRING, description, + GST_QUARK (ELEMENT_METADATA_AUTHOR), G_TYPE_STRING, author, NULL); +} + +/** + * gst_element_class_set_static_metadata: + * @klass: class to set metadata for + * @longname: The long English name of the element. E.g. "File Sink" + * @classification: String describing the type of element, as an unordered list + * separated with slashes ('/'). See draft-klass.txt of the design docs + * for more details and common types. E.g: "Sink/File" + * @description: Sentence describing the purpose of the element. + * E.g: "Write stream to a file" + * @author: Name and contact details of the author(s). Use \n to separate + * multiple author metadata. E.g: "Joe Bloggs <joe.blogs at foo.com>" + * + * Sets the detailed information for a #GstElementClass. + * + * > This function is for use in _class_init functions only. + * + * Same as gst_element_class_set_metadata(), but @longname, @classification, + * @description, and @author must be static strings or inlined strings, as + * they will not be copied. (GStreamer plugins will be made resident once + * loaded, so this function can be used even from dynamically loaded plugins.) + */ +void +gst_element_class_set_static_metadata (GstElementClass * klass, + const gchar * longname, const gchar * classification, + const gchar * description, const gchar * author) +{ + GstStructure *s = (GstStructure *) klass->metadata; + GValue val = G_VALUE_INIT; + + g_return_if_fail (GST_IS_ELEMENT_CLASS (klass)); + g_return_if_fail (longname != NULL && *longname != '\0'); + g_return_if_fail (classification != NULL && *classification != '\0'); + g_return_if_fail (description != NULL && *description != '\0'); + g_return_if_fail (author != NULL && *author != '\0'); + + g_value_init (&val, G_TYPE_STRING); + + g_value_set_static_string (&val, longname); + gst_structure_id_set_value (s, GST_QUARK (ELEMENT_METADATA_LONGNAME), &val); + + g_value_set_static_string (&val, classification); + gst_structure_id_set_value (s, GST_QUARK (ELEMENT_METADATA_KLASS), &val); + + g_value_set_static_string (&val, description); + gst_structure_id_set_value (s, GST_QUARK (ELEMENT_METADATA_DESCRIPTION), + &val); + + g_value_set_static_string (&val, author); + gst_structure_id_take_value (s, GST_QUARK (ELEMENT_METADATA_AUTHOR), &val); } /** @@ -1278,14 +1595,34 @@ gst_element_class_get_metadata (GstElementClass * klass, const gchar * key) } /** + * gst_element_get_metadata: + * @element: class to get metadata for + * @key: the key to get + * + * Get metadata with @key in @klass. + * + * Returns: the metadata for @key. + * + * Since: 1.14 + */ +const gchar * +gst_element_get_metadata (GstElement * element, const gchar * key) +{ + g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); + g_return_val_if_fail (key != NULL, NULL); + + return gst_element_class_get_metadata (GST_ELEMENT_GET_CLASS (element), key); +} + +/** * gst_element_class_get_pad_template_list: * @element_class: a #GstElementClass to get pad templates of. * * Retrieves a list of the pad templates associated with @element_class. The * list must not be modified by the calling code. - * If you use this function in the #GInstanceInitFunc of an object class - * that has subclasses, make sure to pass the g_class parameter of the - * #GInstanceInitFunc here. + * > If you use this function in the #GInstanceInitFunc of an object class + * > that has subclasses, make sure to pass the g_class parameter of the + * > #GInstanceInitFunc here. * * Returns: (transfer none) (element-type Gst.PadTemplate): the #GList of * pad templates. @@ -1299,17 +1636,39 @@ gst_element_class_get_pad_template_list (GstElementClass * element_class) } /** + * gst_element_get_pad_template_list: + * @element: a #GstElement to get pad templates of. + * + * Retrieves a list of the pad templates associated with @element. The + * list must not be modified by the calling code. + * + * Returns: (transfer none) (element-type Gst.PadTemplate): the #GList of + * pad templates. + * + * Since: 1.14 + */ +GList * +gst_element_get_pad_template_list (GstElement * element) +{ + g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); + + return + gst_element_class_get_pad_template_list (GST_ELEMENT_GET_CLASS (element)); +} + +/** * gst_element_class_get_pad_template: * @element_class: a #GstElementClass to get the pad template of. * @name: the name of the #GstPadTemplate to get. * * Retrieves a padtemplate from @element_class with the given name. - * If you use this function in the #GInstanceInitFunc of an object class - * that has subclasses, make sure to pass the g_class parameter of the - * #GInstanceInitFunc here. + * > If you use this function in the #GInstanceInitFunc of an object class + * > that has subclasses, make sure to pass the g_class parameter of the + * > #GInstanceInitFunc here. * - * Returns: (transfer none): the #GstPadTemplate with the given name, or %NULL - * if none was found. No unreferencing is necessary. + * Returns: (transfer none) (nullable): the #GstPadTemplate with the + * given name, or %NULL if none was found. No unreferencing is + * necessary. */ GstPadTemplate * gst_element_class_get_pad_template (GstElementClass * @@ -1334,6 +1693,29 @@ gst_element_class_get_pad_template (GstElementClass * return NULL; } +/** + * gst_element_get_pad_template: + * @element: a #GstElement to get the pad template of. + * @name: the name of the #GstPadTemplate to get. + * + * Retrieves a padtemplate from @element with the given name. + * + * Returns: (transfer none) (nullable): the #GstPadTemplate with the + * given name, or %NULL if none was found. No unreferencing is + * necessary. + * + * Since: 1.14 + */ +GstPadTemplate * +gst_element_get_pad_template (GstElement * element, const gchar * name) +{ + g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); + g_return_val_if_fail (name != NULL, NULL); + + return gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (element), + name); +} + static GstPadTemplate * gst_element_class_get_request_pad_template (GstElementClass * element_class, const gchar * name) @@ -1348,7 +1730,7 @@ gst_element_class_get_request_pad_template (GstElementClass * } /* get a random pad on element of the given direction. - * The pad is random in a sense that it is the first pad that is (optionaly) linked. + * The pad is random in a sense that it is the first pad that is (optionally) linked. */ static GstPad * gst_element_get_random_pad (GstElement * element, @@ -1416,8 +1798,8 @@ gst_element_default_send_event (GstElement * element, GstEvent * event) GstPad *pad; pad = GST_EVENT_IS_DOWNSTREAM (event) ? - gst_element_get_random_pad (element, TRUE, GST_PAD_SRC) : - gst_element_get_random_pad (element, TRUE, GST_PAD_SINK); + gst_element_get_random_pad (element, TRUE, GST_PAD_SINK) : + gst_element_get_random_pad (element, TRUE, GST_PAD_SRC); if (pad) { GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, @@ -1426,7 +1808,7 @@ gst_element_default_send_event (GstElement * element, GstEvent * event) (GST_PAD_DIRECTION (pad) == GST_PAD_SRC ? "src" : "sink"), GST_DEBUG_PAD_NAME (pad)); - result = gst_pad_push_event (pad, event); + result = gst_pad_send_event (pad, event); gst_object_unref (pad); } else { GST_CAT_INFO (GST_CAT_ELEMENT_PADS, "can't send %s event on element %s", @@ -1443,14 +1825,15 @@ gst_element_default_send_event (GstElement * element, GstEvent * event) * * Sends an event to an element. If the element doesn't implement an * event handler, the event will be pushed on a random linked sink pad for - * upstream events or a random linked source pad for downstream events. + * downstream events or a random linked source pad for upstream events. * - * This function takes owership of the provided event so you should + * This function takes ownership of the 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. - * * MT safe. + * + * Returns: %TRUE if the event was handled. Events that trigger a preroll (such + * as flushing seeks and steps) will emit %GST_MESSAGE_ASYNC_DONE. */ gboolean gst_element_send_event (GstElement * element, GstEvent * event) @@ -1468,6 +1851,8 @@ gst_element_send_event (GstElement * element, GstEvent * 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 { + gst_event_unref (event); } GST_STATE_UNLOCK (element); @@ -1480,8 +1865,8 @@ gst_element_send_event (GstElement * element, GstEvent * event) * @rate: The new playback rate * @format: The format of the seek values * @flags: The optional seek flags. - * @cur_type: The type and flags for the new current position - * @cur: The value of the new current position + * @start_type: The type and flags for the new start position + * @start: The value of the new start position * @stop_type: The type and flags for the new stop position * @stop: The value of the new stop position * @@ -1489,13 +1874,14 @@ gst_element_send_event (GstElement * element, GstEvent * event) * the parameters. The seek event is sent to the element using * gst_element_send_event(). * - * Returns: %TRUE if the event was handled. - * * MT safe. + * + * Returns: %TRUE if the event was handled. Flushing seeks will trigger a + * preroll, which will emit %GST_MESSAGE_ASYNC_DONE. */ gboolean gst_element_seek (GstElement * element, gdouble rate, GstFormat format, - GstSeekFlags flags, GstSeekType cur_type, gint64 cur, + GstSeekFlags flags, GstSeekType start_type, gint64 start, GstSeekType stop_type, gint64 stop) { GstEvent *event; @@ -1504,7 +1890,8 @@ gst_element_seek (GstElement * element, gdouble rate, GstFormat format, g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); event = - gst_event_new_seek (rate, format, flags, cur_type, cur, stop_type, stop); + gst_event_new_seek (rate, format, flags, start_type, start, stop_type, + stop); result = gst_element_send_event (element, event); return result; @@ -1550,45 +1937,34 @@ gst_element_default_query (GstElement * element, GstQuery * query) * * Please note that some queries might need a running pipeline to work. * - * Returns: TRUE if the query could be performed. + * Returns: %TRUE if the query could be performed. * * MT safe. */ gboolean gst_element_query (GstElement * element, GstQuery * query) { - GstElementClass *oclass; - gboolean result = FALSE; + GstElementClass *klass; + gboolean res = FALSE; g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); g_return_val_if_fail (query != NULL, FALSE); - oclass = GST_ELEMENT_GET_CLASS (element); + GST_TRACER_ELEMENT_QUERY_PRE (element, query); - if (oclass->query) { + klass = GST_ELEMENT_GET_CLASS (element); + if (klass->query) { GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "send query on element %s", GST_ELEMENT_NAME (element)); - result = oclass->query (element, query); + res = klass->query (element, query); } - return result; + + GST_TRACER_ELEMENT_QUERY_POST (element, query, res); + return res; } -/** - * gst_element_post_message: - * @element: a #GstElement posting the message - * @message: (transfer full): a #GstMessage to post - * - * 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. The function returns - * %FALSE if the element did not have a bus. - * - * MT safe. - */ -gboolean -gst_element_post_message (GstElement * element, GstMessage * message) +static gboolean +gst_element_post_message_default (GstElement * element, GstMessage * message) { GstBus *bus; gboolean result = FALSE; @@ -1624,13 +2000,48 @@ no_bus: } /** + * gst_element_post_message: + * @element: a #GstElement posting the message + * @message: (transfer full): a #GstMessage to post + * + * 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. The function returns + * %FALSE if the element did not have a bus. + * + * MT safe. + */ +gboolean +gst_element_post_message (GstElement * element, GstMessage * message) +{ + GstElementClass *klass; + gboolean res = FALSE; + + g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); + g_return_val_if_fail (message != NULL, FALSE); + + GST_TRACER_ELEMENT_POST_MESSAGE_PRE (element, message); + + klass = GST_ELEMENT_GET_CLASS (element); + if (klass->post_message) + res = klass->post_message (element, message); + else + gst_message_unref (message); + + GST_TRACER_ELEMENT_POST_MESSAGE_POST (element, res); + return res; +} + +/** * _gst_element_error_printf: - * @format: the printf-like format to use, or %NULL + * @format: (allow-none): the printf-like format to use, or %NULL * * This function is only used internally by the gst_element_error() macro. * - * Returns: (transfer full): a newly allocated string, or %NULL if the format - * was %NULL or "" + * Returns: (transfer full) (nullable): a newly allocated string, or + * %NULL if the format was %NULL or "" * * MT safe. */ @@ -1639,6 +2050,7 @@ _gst_element_error_printf (const gchar * format, ...) { va_list args; gchar *buffer; + int len; if (format == NULL) return NULL; @@ -1646,13 +2058,19 @@ _gst_element_error_printf (const gchar * format, ...) return NULL; va_start (args, format); - buffer = g_strdup_vprintf (format, args); + + len = __gst_vasprintf (&buffer, format, args); + va_end (args); + + if (len < 0) + buffer = NULL; + return buffer; } /** - * gst_element_message_full: + * gst_element_message_full_with_details: * @element: a #GstElement to send message from * @type: the #GstMessageType * @domain: the GStreamer GError domain this message belongs to @@ -1666,18 +2084,20 @@ _gst_element_error_printf (const gchar * format, ...) * @file: the source code file where the error was generated * @function: the source code function where the error was generated * @line: the source code line where the error was generated + * @structure:(transfer full): optional details structure * * Post an error, warning or info message on the bus from inside an element. * * @type must be of #GST_MESSAGE_ERROR, #GST_MESSAGE_WARNING or * #GST_MESSAGE_INFO. * - * MT safe. + * Since: 1.10 */ -void gst_element_message_full +void gst_element_message_full_with_details (GstElement * element, GstMessageType type, GQuark domain, gint code, gchar * text, - gchar * debug, const gchar * file, const gchar * function, gint line) + gchar * debug, const gchar * file, const gchar * function, gint line, + GstStructure * structure) { GError *gerror = NULL; gchar *name; @@ -1724,20 +2144,24 @@ void gst_element_message_full switch (type) { case GST_MESSAGE_ERROR: message = - gst_message_new_error (GST_OBJECT_CAST (element), gerror, sent_debug); + gst_message_new_error_with_details (GST_OBJECT_CAST (element), gerror, + sent_debug, structure); break; case GST_MESSAGE_WARNING: - message = gst_message_new_warning (GST_OBJECT_CAST (element), gerror, - sent_debug); + message = + gst_message_new_warning_with_details (GST_OBJECT_CAST (element), + gerror, sent_debug, structure); break; case GST_MESSAGE_INFO: - message = gst_message_new_info (GST_OBJECT_CAST (element), gerror, - sent_debug); + message = + gst_message_new_info_with_details (GST_OBJECT_CAST (element), gerror, + sent_debug, structure); break; default: g_assert_not_reached (); break; } + gst_element_post_message (element, message); GST_CAT_INFO_OBJECT (GST_CAT_ERROR_SYSTEM, element, "posted %s message: %s", @@ -1750,6 +2174,38 @@ void gst_element_message_full } /** + * gst_element_message_full: + * @element: a #GstElement to send message from + * @type: the #GstMessageType + * @domain: the GStreamer GError domain this message belongs to + * @code: the GError code belonging to the domain + * @text: (allow-none) (transfer full): an allocated text string to be used + * as a replacement for the default message connected to code, + * or %NULL + * @debug: (allow-none) (transfer full): an allocated debug message to be + * used as a replacement for the default debugging information, + * or %NULL + * @file: the source code file where the error was generated + * @function: the source code function where the error was generated + * @line: the source code line where the error was generated + * + * Post an error, warning or info message on the bus from inside an element. + * + * @type must be of #GST_MESSAGE_ERROR, #GST_MESSAGE_WARNING or + * #GST_MESSAGE_INFO. + * + * MT safe. + */ +void gst_element_message_full + (GstElement * element, GstMessageType type, + GQuark domain, gint code, gchar * text, + gchar * debug, const gchar * file, const gchar * function, gint line) +{ + gst_element_message_full_with_details (element, type, domain, code, text, + debug, file, function, line, NULL); +} + +/** * gst_element_is_locked_state: * @element: a #GstElement. * @@ -1761,7 +2217,7 @@ void gst_element_message_full * * MT safe. * - * Returns: TRUE, if the element's state is locked. + * Returns: %TRUE, if the element's state is locked. */ gboolean gst_element_is_locked_state (GstElement * element) @@ -1780,14 +2236,18 @@ gst_element_is_locked_state (GstElement * element) /** * gst_element_set_locked_state: * @element: a #GstElement - * @locked_state: TRUE to lock the element's state + * @locked_state: %TRUE to lock the element's state * * Locks the state of an element, so state changes of the parent don't affect * this element anymore. * + * Note that this is racy if the state lock of the parent bin is not taken. + * The parent bin might've just checked the flag in another thread and as the + * next step proceed to change the child element's state. + * * MT safe. * - * Returns: TRUE if the state was changed, FALSE if bad parameters were given + * Returns: %TRUE if the state was changed, %FALSE if bad parameters were given * or the elements state-locking needed no change. */ gboolean @@ -1832,9 +2292,9 @@ was_ok: * @element: a #GstElement. * * Tries to change the state of the element to the same as its parent. - * If this function returns FALSE, the state of element is undefined. + * 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. + * Returns: %TRUE, if the element's state could be synced to the parent's state. * * MT safe. */ @@ -1922,22 +2382,9 @@ gst_element_get_state_func (GstElement * element, old_pending = GST_STATE_PENDING (element); if (old_pending != GST_STATE_VOID_PENDING) { - GTimeVal *timeval, abstimeout; + gboolean signaled; guint32 cookie; - if (timeout != GST_CLOCK_TIME_NONE) { - glong add = timeout / 1000; - - if (add == 0) - goto done; - - /* make timeout absolute */ - g_get_current_time (&abstimeout); - g_time_val_add (&abstimeout, add); - timeval = &abstimeout; - } else { - timeval = NULL; - } /* get cookie to detect state changes during waiting */ cookie = element->state_cookie; @@ -1945,7 +2392,17 @@ gst_element_get_state_func (GstElement * 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)) { + if (timeout != GST_CLOCK_TIME_NONE) { + gint64 end_time; + /* make timeout absolute */ + end_time = g_get_monotonic_time () + (timeout / 1000); + signaled = GST_STATE_WAIT_UNTIL (element, end_time); + } else { + GST_STATE_WAIT (element); + signaled = TRUE; + } + + if (!signaled) { GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "timed out"); /* timeout triggered */ ret = GST_STATE_CHANGE_ASYNC; @@ -1991,7 +2448,7 @@ interrupted: if (pending) *pending = GST_STATE_VOID_PENDING; - GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "interruped"); + GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "interrupted"); GST_OBJECT_UNLOCK (element); @@ -2146,6 +2603,8 @@ _priv_gst_element_state_changed (GstElement * element, GstState oldstate, * This method is used internally and should normally not be called by plugins * or applications. * + * This function must be called with STATE_LOCK held. + * * Returns: The result of the commit state change. * * MT safe. @@ -2324,7 +2783,8 @@ only_async_start: * 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. + * of the state change or it can wait for a %GST_MESSAGE_ASYNC_DONE or + * %GST_MESSAGE_STATE_CHANGED on the bus. * * State changes to %GST_STATE_READY or %GST_STATE_NULL never return * #GST_STATE_CHANGE_ASYNC. @@ -2485,12 +2945,16 @@ gst_element_change_state (GstElement * element, GstStateChange transition) oclass = GST_ELEMENT_GET_CLASS (element); + GST_TRACER_ELEMENT_CHANGE_STATE_PRE (element, 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; + GST_TRACER_ELEMENT_CHANGE_STATE_POST (element, transition, ret); + switch (ret) { case GST_STATE_CHANGE_FAILURE: GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, @@ -2522,14 +2986,14 @@ gst_element_change_state (GstElement * element, GstStateChange transition) 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 + /* we can commit the state now which will proceed 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 + /* we can commit the state now which will proceed to * the next state */ ret = gst_element_continue_state (element, ret); break; @@ -2565,15 +3029,20 @@ invalid_return: } /* gst_iterator_fold functions for pads_activate - * Stop the iterator if activating one pad failed. */ + * Stop the iterator if activating one pad failed, but only if that pad + * has not been removed from the element. */ static gboolean activate_pads (const GValue * vpad, GValue * ret, gboolean * active) { GstPad *pad = g_value_get_object (vpad); gboolean cont = TRUE; - if (!(cont = gst_pad_set_active (pad, *active))) - g_value_set_boolean (ret, FALSE); + if (!gst_pad_set_active (pad, *active)) { + if (GST_PAD_PARENT (pad) != NULL) { + cont = FALSE; + g_value_set_boolean (ret, FALSE); + } + } return cont; } @@ -2625,7 +3094,7 @@ gst_element_pads_activate (GstElement * element, gboolean active) gboolean res; GST_CAT_DEBUG_OBJECT (GST_CAT_ELEMENT_PADS, element, - "pads_activate with active %d", active); + "%s pads", active ? "activate" : "deactivate"); iter = gst_element_iterate_src_pads (element); res = @@ -2644,7 +3113,7 @@ gst_element_pads_activate (GstElement * element, gboolean active) goto sink_failed; GST_CAT_DEBUG_OBJECT (GST_CAT_ELEMENT_PADS, element, - "pads_activate successful"); + "pad %sactivation successful", active ? "" : "de"); return TRUE; @@ -2652,7 +3121,7 @@ gst_element_pads_activate (GstElement * element, gboolean active) src_failed: { GST_CAT_DEBUG_OBJECT (GST_CAT_ELEMENT_PADS, element, - "source pads_activate failed"); + "pad %sactivation failed", active ? "" : "de"); return FALSE; } sink_failed: @@ -2697,13 +3166,34 @@ gst_element_change_state_func (GstElement * element, GstStateChange transition) case GST_STATE_CHANGE_PLAYING_TO_PAUSED: break; case GST_STATE_CHANGE_PAUSED_TO_READY: - case GST_STATE_CHANGE_READY_TO_NULL: + case GST_STATE_CHANGE_READY_TO_NULL:{ + GList *l; + /* deactivate pads in both cases, since they are activated on ready->paused but the element might not have made it to paused */ if (!gst_element_pads_activate (element, FALSE)) { result = GST_STATE_CHANGE_FAILURE; } + + /* Remove all non-persistent contexts */ + GST_OBJECT_LOCK (element); + for (l = element->contexts; l;) { + GstContext *context = l->data; + + if (!gst_context_is_persistent (context)) { + GList *next; + + gst_context_unref (context); + next = l->next; + element->contexts = g_list_delete_link (element->contexts, l); + l = next; + } else { + l = l->next; + } + } + GST_OBJECT_UNLOCK (element); break; + } default: /* this will catch real but unhandled state changes; * can only be caused by: @@ -2736,8 +3226,8 @@ was_ok: * * Retrieves the factory that was used to create this element. * - * Returns: (transfer none): the #GstElementFactory used for creating this - * element. no refcounting is needed. + * Returns: (transfer none) (nullable): the #GstElementFactory used for creating this + * element or %NULL if element has not been registered (static element). no refcounting is needed. */ GstElementFactory * gst_element_get_factory (GstElement * element) @@ -2753,22 +3243,46 @@ gst_element_dispose (GObject * object) GstElement *element = GST_ELEMENT_CAST (object); GstClock **clock_p; GstBus **bus_p; + GstElementClass *oclass; + GList *walk; - GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "dispose"); + oclass = GST_ELEMENT_GET_CLASS (element); + + GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "%p dispose", element); if (GST_STATE (element) != GST_STATE_NULL) goto not_null; - GST_CAT_DEBUG_OBJECT (GST_CAT_ELEMENT_PADS, element, - "removing %d pads", g_list_length (element->pads)); - /* first we break all our links with the outside */ - while (element->pads && element->pads->data) { - /* don't call _remove_pad with NULL */ - gst_element_remove_pad (element, GST_PAD_CAST (element->pads->data)); + /* start by releasing all request pads, this might also remove some dynamic + * pads */ + walk = element->pads; + while (walk) { + GstPad *pad = GST_PAD_CAST (walk->data); + + walk = walk->next; + + if (oclass->release_pad && GST_PAD_PAD_TEMPLATE (pad) && + GST_PAD_TEMPLATE_PRESENCE (GST_PAD_PAD_TEMPLATE (pad)) + == GST_PAD_REQUEST) { + GST_CAT_DEBUG_OBJECT (GST_CAT_ELEMENT_PADS, element, + "removing request pad %s:%s", GST_DEBUG_PAD_NAME (pad)); + oclass->release_pad (element, pad); + + /* in case the release_pad function removed the next pad too */ + if (walk && g_list_position (element->pads, walk) == -1) + walk = element->pads; + } } - if (G_UNLIKELY (element->pads != NULL)) { - g_critical ("could not remove pads from element %s", - GST_STR_NULL (GST_OBJECT_NAME (object))); + /* remove the remaining pads */ + while (element->pads) { + GstPad *pad = GST_PAD_CAST (element->pads->data); + GST_CAT_DEBUG_OBJECT (GST_CAT_ELEMENT_PADS, element, + "removing pad %s:%s", GST_DEBUG_PAD_NAME (pad)); + if (!gst_element_remove_pad (element, pad)) { + /* only happens when someone unparented our pad.. */ + g_critical ("failed to remove pad %s:%s", GST_DEBUG_PAD_NAME (pad)); + break; + } } GST_OBJECT_LOCK (element); @@ -2776,9 +3290,11 @@ gst_element_dispose (GObject * object) bus_p = &element->bus; gst_object_replace ((GstObject **) clock_p, NULL); gst_object_replace ((GstObject **) bus_p, NULL); + g_list_free_full (element->contexts, (GDestroyNotify) gst_context_unref); GST_OBJECT_UNLOCK (element); - GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "parent class dispose"); + GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "%p parent class dispose", + element); G_OBJECT_CLASS (parent_class)->dispose (object); @@ -2809,12 +3325,13 @@ gst_element_finalize (GObject * object) { GstElement *element = GST_ELEMENT_CAST (object); - GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "finalize"); + GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "%p finalize", element); - g_cond_free (element->state_cond); - g_static_rec_mutex_free (&element->state_lock); + g_cond_clear (&element->state_cond); + g_rec_mutex_clear (&element->state_lock); - GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "finalize parent"); + GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "%p finalize parent", + element); G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -2837,7 +3354,7 @@ gst_element_set_bus_func (GstElement * element, GstBus * bus) /** * gst_element_set_bus: * @element: a #GstElement to set the bus of. - * @bus: (transfer none): the #GstBus to set. + * @bus: (transfer none) (allow-none): the #GstBus to set. * * Sets the bus of the element. Increases the refcount on the bus. * For internal use only, unless you're testing elements. @@ -2864,7 +3381,8 @@ gst_element_set_bus (GstElement * element, GstBus * bus) * Returns the bus of the element. Note that only a #GstPipeline will provide a * bus for the application. * - * Returns: (transfer full): the element's #GstBus. unref after usage. + * Returns: (transfer full) (nullable): the element's #GstBus. unref after + * usage. * * MT safe. */ @@ -2885,3 +3403,355 @@ gst_element_get_bus (GstElement * element) return result; } + +static void +gst_element_set_context_default (GstElement * element, GstContext * context) +{ + const gchar *context_type; + GList *l; + + g_return_if_fail (GST_IS_CONTEXT (context)); + context_type = gst_context_get_context_type (context); + g_return_if_fail (context_type != NULL); + + GST_OBJECT_LOCK (element); + for (l = element->contexts; l; l = l->next) { + GstContext *tmp = l->data; + const gchar *tmp_type = gst_context_get_context_type (tmp); + + /* Always store newest context but never replace + * a persistent one by a non-persistent one */ + if (g_strcmp0 (context_type, tmp_type) == 0 && + (gst_context_is_persistent (context) || + !gst_context_is_persistent (tmp))) { + gst_context_replace ((GstContext **) & l->data, context); + break; + } + } + /* Not found? Add */ + if (l == NULL) { + element->contexts = + g_list_prepend (element->contexts, gst_context_ref (context)); + } + GST_OBJECT_UNLOCK (element); +} + +/** + * gst_element_set_context: + * @element: a #GstElement to set the context of. + * @context: (transfer none): the #GstContext to set. + * + * Sets the context of the element. Increases the refcount of the context. + * + * MT safe. + */ +void +gst_element_set_context (GstElement * element, GstContext * context) +{ + GstElementClass *oclass; + + g_return_if_fail (GST_IS_ELEMENT (element)); + g_return_if_fail (GST_IS_CONTEXT (context)); + + oclass = GST_ELEMENT_GET_CLASS (element); + + GST_CAT_DEBUG_OBJECT (GST_CAT_CONTEXT, element, + "set context %p %" GST_PTR_FORMAT, context, + gst_context_get_structure (context)); + + if (oclass->set_context) + oclass->set_context (element, context); +} + +/** + * gst_element_get_contexts: + * @element: a #GstElement to set the context of. + * + * Gets the contexts set on the element. + * + * MT safe. + * + * Returns: (element-type Gst.Context) (transfer full): List of #GstContext + * + * Since: 1.8 + */ +GList * +gst_element_get_contexts (GstElement * element) +{ + GList *ret; + + g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); + + GST_OBJECT_LOCK (element); + ret = g_list_copy_deep (element->contexts, (GCopyFunc) gst_context_ref, NULL); + GST_OBJECT_UNLOCK (element); + + return ret; +} + +static gint +_match_context_type (GstContext * c1, const gchar * context_type) +{ + const gchar *c1_type; + + c1_type = gst_context_get_context_type (c1); + + return g_strcmp0 (c1_type, context_type); +} + +/** + * gst_element_get_context_unlocked: + * @element: a #GstElement to get the context of. + * @context_type: a name of a context to retrieve + * + * Gets the context with @context_type set on the element or NULL. + * + * Returns: (transfer full) (nullable): A #GstContext or NULL + * + * Since: 1.8 + */ +GstContext * +gst_element_get_context_unlocked (GstElement * element, + const gchar * context_type) +{ + GstContext *ret = NULL; + GList *node; + + g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); + + node = + g_list_find_custom (element->contexts, context_type, + (GCompareFunc) _match_context_type); + if (node && node->data) + ret = gst_context_ref (node->data); + + return ret; +} + +/** + * gst_element_get_context: + * @element: a #GstElement to get the context of. + * @context_type: a name of a context to retrieve + * + * Gets the context with @context_type set on the element or NULL. + * + * MT safe. + * + * Returns: (transfer full): A #GstContext or NULL + * + * Since: 1.8 + */ +GstContext * +gst_element_get_context (GstElement * element, const gchar * context_type) +{ + GstContext *ret = NULL; + + g_return_val_if_fail (GST_IS_ELEMENT (element), NULL); + + GST_OBJECT_LOCK (element); + ret = gst_element_get_context_unlocked (element, context_type); + GST_OBJECT_UNLOCK (element); + + return ret; +} + +static void +gst_element_property_post_notify_msg (GstElement * element, GObject * obj, + GParamSpec * pspec, gboolean include_value) +{ + GValue val = G_VALUE_INIT; + GValue *v; + + GST_LOG_OBJECT (element, "property '%s' of object %" GST_PTR_FORMAT " has " + "changed, posting message with%s value", pspec->name, obj, + include_value ? "" : "out"); + + if (include_value && (pspec->flags & G_PARAM_READABLE) != 0) { + g_value_init (&val, pspec->value_type); + g_object_get_property (obj, pspec->name, &val); + v = &val; + } else { + v = NULL; + } + gst_element_post_message (element, + gst_message_new_property_notify (GST_OBJECT_CAST (obj), pspec->name, v)); +} + +static void +gst_element_property_deep_notify_cb (GstElement * element, GObject * prop_obj, + GParamSpec * pspec, gpointer user_data) +{ + gboolean include_value = GPOINTER_TO_INT (user_data); + + gst_element_property_post_notify_msg (element, prop_obj, pspec, + include_value); +} + +static void +gst_element_property_notify_cb (GObject * obj, GParamSpec * pspec, + gpointer user_data) +{ + gboolean include_value = GPOINTER_TO_INT (user_data); + + gst_element_property_post_notify_msg (GST_ELEMENT_CAST (obj), obj, pspec, + include_value); +} + +/** + * gst_element_add_property_notify_watch: + * @element: a #GstElement to watch for property changes + * @property_name: (allow-none): name of property to watch for changes, or + * NULL to watch all properties + * @include_value: whether to include the new property value in the message + * + * Returns: a watch id, which can be used in connection with + * gst_element_remove_property_notify_watch() to remove the watch again. + * + * Since: 1.10 + */ +gulong +gst_element_add_property_notify_watch (GstElement * element, + const gchar * property_name, gboolean include_value) +{ + const gchar *sep; + gchar *signal_name; + gulong id; + + g_return_val_if_fail (GST_IS_ELEMENT (element), 0); + + sep = (property_name != NULL) ? "::" : NULL; + signal_name = g_strconcat ("notify", sep, property_name, NULL); + id = g_signal_connect (element, signal_name, + G_CALLBACK (gst_element_property_notify_cb), + GINT_TO_POINTER (include_value)); + g_free (signal_name); + + return id; +} + +/** + * gst_element_add_property_deep_notify_watch: + * @element: a #GstElement to watch (recursively) for property changes + * @property_name: (allow-none): name of property to watch for changes, or + * NULL to watch all properties + * @include_value: whether to include the new property value in the message + * + * Returns: a watch id, which can be used in connection with + * gst_element_remove_property_notify_watch() to remove the watch again. + * + * Since: 1.10 + */ +gulong +gst_element_add_property_deep_notify_watch (GstElement * element, + const gchar * property_name, gboolean include_value) +{ + const gchar *sep; + gchar *signal_name; + gulong id; + + g_return_val_if_fail (GST_IS_ELEMENT (element), 0); + + sep = (property_name != NULL) ? "::" : NULL; + signal_name = g_strconcat ("deep-notify", sep, property_name, NULL); + id = g_signal_connect (element, signal_name, + G_CALLBACK (gst_element_property_deep_notify_cb), + GINT_TO_POINTER (include_value)); + g_free (signal_name); + + return id; +} + +/** + * gst_element_remove_property_notify_watch: + * @element: a #GstElement being watched for property changes + * @watch_id: watch id to remove + * + * Since: 1.10 + */ +void +gst_element_remove_property_notify_watch (GstElement * element, gulong watch_id) +{ + g_signal_handler_disconnect (element, watch_id); +} + +typedef struct +{ + GstElement *element; + GstElementCallAsyncFunc func; + gpointer user_data; + GDestroyNotify destroy_notify; +} GstElementCallAsyncData; + +static void +gst_element_call_async_func (gpointer data, gpointer user_data) +{ + GstElementCallAsyncData *async_data = data; + + async_data->func (async_data->element, async_data->user_data); + if (async_data->destroy_notify) + async_data->destroy_notify (async_data->user_data); + gst_object_unref (async_data->element); + g_free (async_data); +} + +/** + * gst_element_call_async: + * @element: a #GstElement + * @func: Function to call asynchronously from another thread + * @user_data: Data to pass to @func + * @destroy_notify: GDestroyNotify for @user_data + * + * Calls @func from another thread and passes @user_data to it. This is to be + * used for cases when a state change has to be performed from a streaming + * thread, directly via gst_element_set_state() or indirectly e.g. via SEEK + * events. + * + * Calling those functions directly from the streaming thread will cause + * deadlocks in many situations, as they might involve waiting for the + * streaming thread to shut down from this very streaming thread. + * + * MT safe. + * + * Since: 1.10 + */ +void +gst_element_call_async (GstElement * element, GstElementCallAsyncFunc func, + gpointer user_data, GDestroyNotify destroy_notify) +{ + GstElementCallAsyncData *async_data; + + g_return_if_fail (GST_IS_ELEMENT (element)); + + async_data = g_new0 (GstElementCallAsyncData, 1); + async_data->element = gst_object_ref (element); + async_data->func = func; + async_data->user_data = user_data; + async_data->destroy_notify = destroy_notify; + + g_thread_pool_push (gst_element_pool, async_data, NULL); +} + +void +_priv_gst_element_cleanup (void) +{ + if (gst_element_pool) { + g_thread_pool_free (gst_element_pool, FALSE, TRUE); + gst_element_setup_thread_pool (); + } +} + +GstStructure * +gst_make_element_message_details (const char *name, ...) +{ + GstStructure *structure; + va_list varargs; + + if (name == NULL) + return NULL; + + va_start (varargs, name); + structure = gst_structure_new_valist ("details", name, varargs); + va_end (varargs); + + return structure; +}