X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gst%2Fgstutils.c;h=e41399ec8cf414f20813583dc94fe07c3eec249b;hb=5470f6df00595f4ab44871e0e633bf15006abc5c;hp=daf1ba87b8d6ef6c9f67d9d252f90fa6ca2f4724;hpb=6c7573226e307a87ec06fb43ce157a2ad767073c;p=platform%2Fupstream%2Fgstreamer.git diff --git a/gst/gstutils.c b/gst/gstutils.c index daf1ba8..e41399e 100644 --- a/gst/gstutils.c +++ b/gst/gstutils.c @@ -2,6 +2,8 @@ * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans * 2002 Thomas Vander Stichele + * 2004 Wim Taymans + * 2015 Jan Schmidt * * gstutils.c: Utility functions * @@ -23,10 +25,15 @@ /** * SECTION:gstutils + * @title: GstUtils * @short_description: Various utility functions * */ +/* FIXME 2.0: suppress warnings for deprecated API such as GValueArray + * with newer GLib versions (>= 2.31.0) */ +#define GLIB_DISABLE_DEPRECATION_WARNINGS + #include "gst_private.h" #include #include @@ -43,7 +50,7 @@ /** * gst_util_dump_mem: - * @mem: a pointer to the memory to dump + * @mem: (array length=size): a pointer to the memory to dump * @size: the size of the memory block to dump * * Dumps the memory block into a hex representation. Useful for debugging. @@ -79,6 +86,24 @@ gst_util_dump_mem (const guchar * mem, guint size) g_string_free (chars, TRUE); } +/** + * gst_util_dump_buffer: + * @buf: a #GstBuffer whose memory to dump + * + * Dumps the buffer memory into a hex representation. Useful for debugging. + * + * Since: 1.14 + */ +void +gst_util_dump_buffer (GstBuffer * buf) +{ + GstMapInfo map; + + if (gst_buffer_map (buf, &map, GST_MAP_READ)) { + gst_util_dump_mem (map.data, map.size); + gst_buffer_unmap (buf, &map); + } +} /** * gst_util_set_value_from_string: @@ -117,7 +142,7 @@ gst_util_set_value_from_string (GValue * value, const gchar * value_str) * @name: the name of the argument to set * @value: the string value to set * - * Convertes the string value to the type of the objects argument and + * Converts the string value to the type of the objects argument and * sets the argument with it. * * Note that this function silently returns if @object has no property named @@ -150,7 +175,7 @@ gst_util_set_object_arg (GObject * object, const gchar * name, g_value_init (&v, value_type); /* special case for element <-> xml (de)serialisation */ - if (GST_VALUE_HOLDS_STRUCTURE (&v) && strcmp (value, "NULL") == 0) { + if (value_type == GST_TYPE_STRUCTURE && strcmp (value, "NULL") == 0) { g_value_set_boxed (&v, NULL); goto done; } @@ -164,6 +189,75 @@ done: g_value_unset (&v); } +/** + * gst_util_set_object_array: + * @object: the object to set the array to + * @name: the name of the property to set + * @array: a #GValueArray containing the values + * + * Transfer a #GValueArray to %GST_TYPE_ARRAY and set this value on the + * specified property name. This allow language bindings to set GST_TYPE_ARRAY + * properties which are otherwise not an accessible type. + * + * Since: 1.12 + */ +gboolean +gst_util_set_object_array (GObject * object, const gchar * name, + const GValueArray * array) +{ + GValue v1 = G_VALUE_INIT, v2 = G_VALUE_INIT; + gboolean ret = FALSE; + + g_value_init (&v1, G_TYPE_VALUE_ARRAY); + g_value_init (&v2, GST_TYPE_ARRAY); + + g_value_set_static_boxed (&v1, array); + + if (g_value_transform (&v1, &v2)) { + g_object_set_property (object, name, &v2); + ret = TRUE; + } + + g_value_unset (&v1); + g_value_unset (&v2); + + return ret; +} + +/** + * gst_util_get_object_array: + * @object: the object to set the array to + * @name: the name of the property to set + * @array: (out): a return #GValueArray + * + * Get a property of type %GST_TYPE_ARRAY and transform it into a + * #GValueArray. This allow language bindings to get GST_TYPE_ARRAY + * properties which are otherwise not an accessible type. + * + * Since: 1.12 + */ +gboolean +gst_util_get_object_array (GObject * object, const gchar * name, + GValueArray ** array) +{ + GValue v1 = G_VALUE_INIT, v2 = G_VALUE_INIT; + gboolean ret = FALSE; + + g_value_init (&v1, G_TYPE_VALUE_ARRAY); + g_value_init (&v2, GST_TYPE_ARRAY); + + g_object_get_property (object, name, &v2); + + if (g_value_transform (&v2, &v1)) { + *array = g_value_get_boxed (&v1); + ret = TRUE; + } + + g_value_unset (&v2); + + return ret; +} + /* work around error C2520: conversion from unsigned __int64 to double * not implemented, use signed __int64 * @@ -694,15 +788,23 @@ gst_util_uint64_scale_int_ceil (guint64 val, gint num, gint denom) * on a segment-done message to be the same as that of the last seek event, to * indicate that event and the message correspond to the same segment. * + * This function never returns %GST_SEQNUM_INVALID (which is 0). + * * Returns: A constantly incrementing 32-bit unsigned integer, which might - * overflow back to 0 at some point. Use gst_util_seqnum_compare() to make sure + * overflow at some point. Use gst_util_seqnum_compare() to make sure * you handle wraparound correctly. */ guint32 gst_util_seqnum_next (void) { - static gint counter = 0; - return g_atomic_int_add (&counter, 1); + static gint counter = 1; + gint ret = g_atomic_int_add (&counter, 1); + + /* Make sure we don't return 0 */ + if (G_UNLIKELY (ret == GST_SEQNUM_INVALID)) + ret = g_atomic_int_add (&counter, 1); + + return ret; } /** @@ -773,8 +875,8 @@ gst_element_create_all_pads (GstElement * element) * Retrieves a pad template from @element that is compatible with @compattempl. * Pads from compatible templates can be linked together. * - * Returns: (transfer none): a compatible #GstPadTemplate, or NULL if none - * was found. No unreferencing is necessary. + * Returns: (transfer none) (nullable): a compatible #GstPadTemplate, + * or %NULL if none was found. No unreferencing is necessary. */ GstPadTemplate * gst_element_get_compatible_pad_template (GstElement * element, @@ -850,8 +952,8 @@ gst_element_get_compatible_pad_template (GstElement * element, * #GST_PAD_REQUEST, requests a new pad. Can return %NULL for #GST_PAD_SOMETIMES * templates. * - * Returns: (transfer full): the #GstPad, or NULL if one could not be found - * or created. + * Returns: (transfer full) (nullable): the #GstPad, or %NULL if one + * could not be found or created. */ static GstPad * gst_element_get_pad_from_template (GstElement * element, GstPadTemplate * templ) @@ -891,7 +993,8 @@ gst_element_get_pad_from_template (GstElement * element, GstPadTemplate * templ) * Requests a pad from @element. The returned pad should be unlinked and * compatible with @templ. Might return an existing pad, or request a new one. * - * Returns: a #GstPad, or %NULL if one could not be found or created. + * Returns: (nullable): a #GstPad, or %NULL if one could not be found + * or created. */ static GstPad * gst_element_request_compatible_pad (GstElement * element, @@ -908,10 +1011,11 @@ gst_element_request_compatible_pad (GstElement * element, templ_new = gst_element_get_compatible_pad_template (element, templ); if (templ_new) pad = gst_element_get_pad_from_template (element, templ_new); - - /* This can happen for non-request pads. No need to unref. */ - if (pad && GST_PAD_PEER (pad)) + /* This can happen for non-request pads. */ + if (pad && GST_PAD_PEER (pad)) { + gst_object_unref (pad); pad = NULL; + } return pad; } @@ -923,11 +1027,6 @@ gst_element_request_compatible_pad (GstElement * element, static gboolean gst_pad_check_link (GstPad * srcpad, GstPad * sinkpad) { - /* FIXME This function is gross. It's almost a direct copy of - * gst_pad_link_filtered(). Any decent programmer would attempt - * to merge the two functions, which I will do some day. --ds - */ - /* generic checks */ g_return_val_if_fail (GST_IS_PAD (srcpad), FALSE); g_return_val_if_fail (GST_IS_PAD (sinkpad), FALSE); @@ -935,7 +1034,6 @@ gst_pad_check_link (GstPad * srcpad, GstPad * sinkpad) GST_CAT_INFO (GST_CAT_PADS, "trying to link %s:%s and %s:%s", GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad)); - /* FIXME: shouldn't we convert this to g_return_val_if_fail? */ if (GST_PAD_PEER (srcpad) != NULL) { GST_CAT_INFO (GST_CAT_PADS, "Source pad %s:%s has a peer, failed", GST_DEBUG_PAD_NAME (srcpad)); @@ -984,8 +1082,9 @@ gst_pad_check_link (GstPad * srcpad, GstPad * sinkpad) * and if none can be found, it will request a compatible REQUEST pad by looking * at the templates of @element. * - * Returns: (transfer full): the #GstPad to which a link can be made, or %NULL - * if one cannot be found. gst_object_unref() after usage. + * Returns: (transfer full) (nullable): the #GstPad to which a link + * can be made, or %NULL if one cannot be found. gst_object_unref() + * after usage. */ GstPad * gst_element_get_compatible_pad (GstElement * element, GstPad * pad, @@ -1103,8 +1202,13 @@ gst_element_get_compatible_pad (GstElement * element, GstPad * pad, /* try to create a new one */ /* requesting is a little crazy, we need a template. Let's create one */ - /* FIXME: why not gst_pad_get_pad_template (pad); */ templcaps = gst_pad_query_caps (pad, NULL); + if (caps) { + GstCaps *inter = gst_caps_intersect (templcaps, caps); + + gst_caps_unref (templcaps); + templcaps = inter; + } templ = gst_pad_template_new ((gchar *) GST_PAD_NAME (pad), GST_PAD_DIRECTION (pad), GST_PAD_ALWAYS, templcaps); gst_caps_unref (templcaps); @@ -1179,6 +1283,46 @@ gst_element_state_change_return_get_name (GstStateChangeReturn state_ret) } } +/** + * gst_state_change_get_name: + * @transition: a #GstStateChange to get the name of. + * + * Gets a string representing the given state transition. + * + * Returns: (transfer none): a string with the name of the state + * result. + * + * Since: 1.14 + */ +const gchar * +gst_state_change_get_name (GstStateChange transition) +{ + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + return "NULL->READY"; + case GST_STATE_CHANGE_READY_TO_PAUSED: + return "READY->PAUSED"; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + return "PAUSED->PLAYING"; + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + return "PLAYING->PAUSED"; + case GST_STATE_CHANGE_PAUSED_TO_READY: + return "PAUSED->READY"; + case GST_STATE_CHANGE_READY_TO_NULL: + return "READY->NULL"; + case GST_STATE_CHANGE_NULL_TO_NULL: + return "NULL->NULL"; + case GST_STATE_CHANGE_READY_TO_READY: + return "READY->READY"; + case GST_STATE_CHANGE_PAUSED_TO_PAUSED: + return "PAUSED->PAUSED"; + case GST_STATE_CHANGE_PLAYING_TO_PLAYING: + return "PLAYING->PLAYING"; + } + + return "Unknown state return"; +} + static gboolean gst_element_factory_can_accept_all_caps_in_direction (GstElementFactory * @@ -1362,11 +1506,13 @@ find_common_root (GstObject * o1, GstObject * o2) gst_object_unref (kid2); return root; } + gst_object_unref (root); root = kid2; if (!object_has_ancestor (o2, kid1, &kid2)) { gst_object_unref (kid1); return root; } + gst_object_unref (root); root = kid1; } } @@ -1386,20 +1532,19 @@ ghost_up (GstElement * e, GstPad * pad) gpad = gst_ghost_pad_new (name, pad); g_free (name); - GST_STATE_LOCK (e); - gst_element_get_state (e, ¤t, &next, 0); + GST_STATE_LOCK (parent); + gst_element_get_state (GST_ELEMENT (parent), ¤t, &next, 0); - if (current > GST_STATE_READY || next == GST_STATE_PAUSED) + if (current > GST_STATE_READY || next >= GST_STATE_PAUSED) gst_pad_set_active (gpad, TRUE); if (!gst_element_add_pad ((GstElement *) parent, gpad)) { g_warning ("Pad named %s already exists in element %s\n", GST_OBJECT_NAME (gpad), GST_OBJECT_NAME (parent)); - gst_object_unref ((GstObject *) gpad); - GST_STATE_UNLOCK (e); + GST_STATE_UNLOCK (parent); return NULL; } - GST_STATE_UNLOCK (e); + GST_STATE_UNLOCK (parent); return gpad; } @@ -1450,8 +1595,24 @@ prepare_link_maybe_ghosting (GstPad ** src, GstPad ** sink, /* we need to setup some ghost pads */ root = find_common_root (e1, e2); if (!root) { - g_warning ("Trying to connect elements that don't share a common " - "ancestor: %s and %s", GST_ELEMENT_NAME (e1), GST_ELEMENT_NAME (e2)); + if (GST_OBJECT_PARENT (e1) == NULL) + g_warning ("Trying to link elements %s and %s that don't share a common " + "ancestor: %s hasn't been added to a bin or pipeline, but %s is in %s", + GST_ELEMENT_NAME (e1), GST_ELEMENT_NAME (e2), + GST_ELEMENT_NAME (e1), GST_ELEMENT_NAME (e2), + GST_ELEMENT_NAME (GST_OBJECT_PARENT (e2))); + else if (GST_OBJECT_PARENT (e2) == NULL) + g_warning ("Trying to link elements %s and %s that don't share a common " + "ancestor: %s hasn't been added to a bin or pipeline, and %s is in %s", + GST_ELEMENT_NAME (e1), GST_ELEMENT_NAME (e2), + GST_ELEMENT_NAME (e2), GST_ELEMENT_NAME (e1), + GST_ELEMENT_NAME (GST_OBJECT_PARENT (e1))); + else + g_warning ("Trying to link elements %s and %s that don't share a common " + "ancestor: %s is in %s, and %s is in %s", + GST_ELEMENT_NAME (e1), GST_ELEMENT_NAME (e2), + GST_ELEMENT_NAME (e1), GST_ELEMENT_NAME (GST_OBJECT_PARENT (e1)), + GST_ELEMENT_NAME (e2), GST_ELEMENT_NAME (GST_OBJECT_PARENT (e2))); return FALSE; } @@ -1502,13 +1663,82 @@ pad_link_maybe_ghosting (GstPad * src, GstPad * sink, GstPadLinkCheck flags) } /** + * gst_pad_link_maybe_ghosting_full: + * @src: a #GstPad + * @sink: a #GstPad + * @flags: some #GstPadLinkCheck flags + * + * Links @src to @sink, creating any #GstGhostPad's in between as necessary. + * + * This is a convenience function to save having to create and add intermediate + * #GstGhostPad's as required for linking across #GstBin boundaries. + * + * If @src or @sink pads don't have parent elements or do not share a common + * ancestor, the link will fail. + * + * Calling gst_pad_link_maybe_ghosting_full() with + * @flags == %GST_PAD_LINK_CHECK_DEFAULT is the recommended way of linking + * pads with safety checks applied. + * + * Returns: whether the link succeeded. + * + * Since: 1.10 + */ +gboolean +gst_pad_link_maybe_ghosting_full (GstPad * src, GstPad * sink, + GstPadLinkCheck flags) +{ + g_return_val_if_fail (GST_IS_PAD (src), FALSE); + g_return_val_if_fail (GST_IS_PAD (sink), FALSE); + + return pad_link_maybe_ghosting (src, sink, flags); +} + +/** + * gst_pad_link_maybe_ghosting: + * @src: a #GstPad + * @sink: a #GstPad + * + * Links @src to @sink, creating any #GstGhostPad's in between as necessary. + * + * This is a convenience function to save having to create and add intermediate + * #GstGhostPad's as required for linking across #GstBin boundaries. + * + * If @src or @sink pads don't have parent elements or do not share a common + * ancestor, the link will fail. + * + * Returns: whether the link succeeded. + * + * Since: 1.10 + */ +gboolean +gst_pad_link_maybe_ghosting (GstPad * src, GstPad * sink) +{ + g_return_val_if_fail (GST_IS_PAD (src), FALSE); + g_return_val_if_fail (GST_IS_PAD (sink), FALSE); + + return gst_pad_link_maybe_ghosting_full (src, sink, + GST_PAD_LINK_CHECK_DEFAULT); +} + +static void +release_and_unref_pad (GstElement * element, GstPad * pad, gboolean requestpad) +{ + if (pad) { + if (requestpad) + gst_element_release_request_pad (element, pad); + gst_object_unref (pad); + } +} + +/** * gst_element_link_pads_full: * @src: a #GstElement containing the source pad. * @srcpadname: (allow-none): the name of the #GstPad in source element - * or NULL for any pad. + * or %NULL for any pad. * @dest: (transfer none): the #GstElement containing the destination pad. * @destpadname: (allow-none): the name of the #GstPad in destination element, - * or NULL for any pad. + * or %NULL for any pad. * @flags: the #GstPadLinkCheck to be performed when linking pads. * * Links the two named pads of the source and destination elements. @@ -1522,7 +1752,7 @@ pad_link_maybe_ghosting (GstPad * src, GstPad * sink, GstPadLinkCheck flags) * * This is a convenience function for gst_pad_link_full(). * - * Returns: TRUE if the pads could be linked, FALSE otherwise. + * Returns: %TRUE if the pads could be linked, %FALSE otherwise. */ gboolean gst_element_link_pads_full (GstElement * src, const gchar * srcpadname, @@ -1532,6 +1762,7 @@ gst_element_link_pads_full (GstElement * src, const gchar * srcpadname, GstPad *srcpad, *destpad; GstPadTemplate *srctempl, *desttempl; GstElementClass *srcclass, *destclass; + gboolean srcrequest, destrequest; /* checks */ g_return_val_if_fail (GST_IS_ELEMENT (src), FALSE); @@ -1542,11 +1773,16 @@ gst_element_link_pads_full (GstElement * src, const gchar * srcpadname, srcpadname ? srcpadname : "(any)", GST_ELEMENT_NAME (dest), destpadname ? destpadname : "(any)"); + srcrequest = FALSE; + destrequest = FALSE; + /* get a src pad */ if (srcpadname) { /* name specified, look it up */ - if (!(srcpad = gst_element_get_static_pad (src, srcpadname))) - srcpad = gst_element_get_request_pad (src, srcpadname); + if (!(srcpad = gst_element_get_static_pad (src, srcpadname))) { + if ((srcpad = gst_element_get_request_pad (src, srcpadname))) + srcrequest = TRUE; + } if (!srcpad) { GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "no pad %s:%s", GST_ELEMENT_NAME (src), srcpadname); @@ -1555,13 +1791,15 @@ gst_element_link_pads_full (GstElement * src, const gchar * srcpadname, if (!(GST_PAD_DIRECTION (srcpad) == GST_PAD_SRC)) { GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "pad %s:%s is no src pad", GST_DEBUG_PAD_NAME (srcpad)); - gst_object_unref (srcpad); + release_and_unref_pad (src, srcpad, srcrequest); return FALSE; } if (GST_PAD_PEER (srcpad) != NULL) { GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "pad %s:%s is already linked to %s:%s", GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (GST_PAD_PEER (srcpad))); + /* already linked request pads look like static pads, so the request pad + * was never requested a second time above, so no need to release it */ gst_object_unref (srcpad); return FALSE; } @@ -1580,17 +1818,21 @@ gst_element_link_pads_full (GstElement * src, const gchar * srcpadname, /* get a destination pad */ if (destpadname) { /* name specified, look it up */ - if (!(destpad = gst_element_get_static_pad (dest, destpadname))) - destpad = gst_element_get_request_pad (dest, destpadname); + if (!(destpad = gst_element_get_static_pad (dest, destpadname))) { + if ((destpad = gst_element_get_request_pad (dest, destpadname))) + destrequest = TRUE; + } if (!destpad) { GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "no pad %s:%s", GST_ELEMENT_NAME (dest), destpadname); + release_and_unref_pad (src, srcpad, srcrequest); return FALSE; } else { if (!(GST_PAD_DIRECTION (destpad) == GST_PAD_SINK)) { GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "pad %s:%s is no sink pad", GST_DEBUG_PAD_NAME (destpad)); - gst_object_unref (destpad); + release_and_unref_pad (src, srcpad, srcrequest); + release_and_unref_pad (dest, destpad, destrequest); return FALSE; } if (GST_PAD_PEER (destpad) != NULL) { @@ -1598,6 +1840,9 @@ gst_element_link_pads_full (GstElement * src, const gchar * srcpadname, "pad %s:%s is already linked to %s:%s", GST_DEBUG_PAD_NAME (destpad), GST_DEBUG_PAD_NAME (GST_PAD_PEER (destpad))); + release_and_unref_pad (src, srcpad, srcrequest); + /* already linked request pads look like static pads, so the request pad + * was never requested a second time above, so no need to release it */ gst_object_unref (destpad); return FALSE; } @@ -1619,9 +1864,13 @@ gst_element_link_pads_full (GstElement * src, const gchar * srcpadname, /* two explicitly specified pads */ result = pad_link_maybe_ghosting (srcpad, destpad, flags); - gst_object_unref (srcpad); - gst_object_unref (destpad); - + if (result) { + gst_object_unref (srcpad); + gst_object_unref (destpad); + } else { + release_and_unref_pad (src, srcpad, srcrequest); + release_and_unref_pad (dest, destpad, destrequest); + } return result; } @@ -1635,6 +1884,7 @@ gst_element_link_pads_full (GstElement * src, const gchar * srcpadname, GST_DEBUG_PAD_NAME (srcpad)); if ((GST_PAD_DIRECTION (srcpad) == GST_PAD_SRC) && (GST_PAD_PEER (srcpad) == NULL)) { + gboolean temprequest = FALSE; GstPad *temp; if (destpadname) { @@ -1642,6 +1892,11 @@ gst_element_link_pads_full (GstElement * src, const gchar * srcpadname, gst_object_ref (temp); } else { temp = gst_element_get_compatible_pad (dest, srcpad, NULL); + if (temp && GST_PAD_PAD_TEMPLATE (temp) + && GST_PAD_TEMPLATE_PRESENCE (GST_PAD_PAD_TEMPLATE (temp)) == + GST_PAD_REQUEST) { + temprequest = TRUE; + } } if (temp && pad_link_maybe_ghosting (srcpad, temp, flags)) { @@ -1655,6 +1910,8 @@ gst_element_link_pads_full (GstElement * src, const gchar * srcpadname, } if (temp) { + if (temprequest) + gst_element_release_request_pad (dest, temp); gst_object_unref (temp); } } @@ -1672,13 +1929,16 @@ gst_element_link_pads_full (GstElement * src, const gchar * srcpadname, if (srcpadname) { GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "no link possible from %s:%s to %s", GST_DEBUG_PAD_NAME (srcpad), GST_ELEMENT_NAME (dest)); + /* no need to release any request pad as both src- and destpadname must be + * set to end up here, but this case has already been taken care of above */ if (destpad) gst_object_unref (destpad); destpad = NULL; } - if (srcpad) - gst_object_unref (srcpad); - srcpad = NULL; + if (srcpad) { + release_and_unref_pad (src, srcpad, srcrequest); + srcpad = NULL; + } if (destpad) { /* loop through the existing pads in the destination */ @@ -1688,6 +1948,13 @@ gst_element_link_pads_full (GstElement * src, const gchar * srcpadname, if ((GST_PAD_DIRECTION (destpad) == GST_PAD_SINK) && (GST_PAD_PEER (destpad) == NULL)) { GstPad *temp = gst_element_get_compatible_pad (src, destpad, NULL); + gboolean temprequest = FALSE; + + if (temp && GST_PAD_PAD_TEMPLATE (temp) + && GST_PAD_TEMPLATE_PRESENCE (GST_PAD_PAD_TEMPLATE (temp)) == + GST_PAD_REQUEST) { + temprequest = TRUE; + } if (temp && pad_link_maybe_ghosting (temp, destpad, flags)) { GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "linked pad %s:%s to pad %s:%s", @@ -1696,9 +1963,8 @@ gst_element_link_pads_full (GstElement * src, const gchar * srcpadname, gst_object_unref (destpad); return TRUE; } - if (temp) { - gst_object_unref (temp); - } + + release_and_unref_pad (src, temp, temprequest); } if (destpads) { destpads = g_list_next (destpads); @@ -1714,11 +1980,15 @@ gst_element_link_pads_full (GstElement * src, const gchar * srcpadname, if (destpadname) { GST_CAT_DEBUG (GST_CAT_ELEMENT_PADS, "no link possible from %s to %s:%s", GST_ELEMENT_NAME (src), GST_DEBUG_PAD_NAME (destpad)); - gst_object_unref (destpad); + release_and_unref_pad (dest, destpad, destrequest); return FALSE; } else { - if (destpad) + /* no need to release any request pad as the case of unset destpatname and + * destpad being a request pad has already been taken care of when looking + * though the destination pads above */ + if (destpad) { gst_object_unref (destpad); + } destpad = NULL; } @@ -1761,10 +2031,14 @@ gst_element_link_pads_full (GstElement * src, const gchar * srcpadname, return TRUE; } /* it failed, so we release the request pads */ - if (srcpad) + if (srcpad) { gst_element_release_request_pad (src, srcpad); - if (destpad) + gst_object_unref (srcpad); + } + if (destpad) { gst_element_release_request_pad (dest, destpad); + gst_object_unref (destpad); + } } gst_caps_unref (srccaps); gst_caps_unref (destcaps); @@ -1784,17 +2058,17 @@ gst_element_link_pads_full (GstElement * src, const gchar * srcpadname, * gst_element_link_pads: * @src: a #GstElement containing the source pad. * @srcpadname: (allow-none): the name of the #GstPad in source element - * or NULL for any pad. + * or %NULL for any pad. * @dest: (transfer none): the #GstElement containing the destination pad. * @destpadname: (allow-none): the name of the #GstPad in destination element, - * or NULL for any pad. + * or %NULL for any pad. * * Links the two named pads of the source and destination elements. * Side effect is that if one of the pads has no parent, it becomes a * child of the parent of the other element. If they have different * parents, the link fails. * - * Returns: TRUE if the pads could be linked, FALSE otherwise. + * Returns: %TRUE if the pads could be linked, %FALSE otherwise. */ gboolean gst_element_link_pads (GstElement * src, const gchar * srcpadname, @@ -1808,19 +2082,19 @@ gst_element_link_pads (GstElement * src, const gchar * srcpadname, * gst_element_link_pads_filtered: * @src: a #GstElement containing the source pad. * @srcpadname: (allow-none): the name of the #GstPad in source element - * or NULL for any pad. + * or %NULL for any pad. * @dest: (transfer none): the #GstElement containing the destination pad. * @destpadname: (allow-none): the name of the #GstPad in destination element - * or NULL for any pad. + * or %NULL for any pad. * @filter: (transfer none) (allow-none): the #GstCaps to filter the link, - * or #NULL for no filter. + * or %NULL for no filter. * * Links the two named pads of the source and destination elements. Side effect * is that if one of the pads has no parent, it becomes a child of the parent of * the other element. If they have different parents, the link fails. If @caps - * is not #NULL, makes sure that the caps of the link is a subset of @caps. + * is not %NULL, makes sure that the caps of the link is a subset of @caps. * - * Returns: TRUE if the pads could be linked, FALSE otherwise. + * Returns: %TRUE if the pads could be linked, %FALSE otherwise. */ gboolean gst_element_link_pads_filtered (GstElement * src, const gchar * srcpadname, @@ -1850,7 +2124,6 @@ gst_element_link_pads_filtered (GstElement * src, const gchar * srcpadname, if (!gst_bin_add (GST_BIN (parent), capsfilter)) { GST_ERROR ("Could not add capsfilter"); - gst_object_unref (capsfilter); gst_object_unref (parent); return FALSE; } @@ -1906,7 +2179,7 @@ gst_element_link_pads_filtered (GstElement * src, const gchar * srcpadname, * Make sure you have added your elements to a bin or pipeline with * gst_bin_add() before trying to link them. * - * Returns: TRUE if the elements could be linked, FALSE otherwise. + * Returns: %TRUE if the elements could be linked, %FALSE otherwise. */ gboolean gst_element_link (GstElement * src, GstElement * dest) @@ -1918,13 +2191,13 @@ gst_element_link (GstElement * src, GstElement * dest) * gst_element_link_many: * @element_1: (transfer none): the first #GstElement in the link chain. * @element_2: (transfer none): the second #GstElement in the link chain. - * @...: the NULL-terminated list of elements to link in order. + * @...: the %NULL-terminated list of elements to link in order. * * Chain together a series of elements. Uses gst_element_link(). * Make sure you have added your elements to a bin or pipeline with * gst_bin_add() before trying to link them. * - * Returns: TRUE on success, FALSE otherwise. + * Returns: %TRUE on success, %FALSE otherwise. */ gboolean gst_element_link_many (GstElement * element_1, GstElement * element_2, ...) @@ -1957,7 +2230,7 @@ gst_element_link_many (GstElement * element_1, GstElement * element_2, ...) * @src: a #GstElement containing the source pad. * @dest: (transfer none): the #GstElement containing the destination pad. * @filter: (transfer none) (allow-none): the #GstCaps to filter the link, - * or #NULL for no filter. + * or %NULL for no filter. * * Links @src to @dest using the given caps as filtercaps. * The link must be from source to @@ -1968,7 +2241,7 @@ gst_element_link_many (GstElement * element_1, GstElement * element_2, ...) * Make sure you have added your elements to a bin or pipeline with * gst_bin_add() before trying to link them. * - * Returns: TRUE if the pads could be linked, FALSE otherwise. + * Returns: %TRUE if the pads could be linked, %FALSE otherwise. */ gboolean gst_element_link_filtered (GstElement * src, GstElement * dest, @@ -2038,7 +2311,7 @@ free_src: * gst_element_unlink_many: * @element_1: (transfer none): the first #GstElement in the link chain. * @element_2: (transfer none): the second #GstElement in the link chain. - * @...: the NULL-terminated list of elements to unlink in order. + * @...: the %NULL-terminated list of elements to unlink in order. * * Unlinks a series of elements. Uses gst_element_unlink(). */ @@ -2134,12 +2407,19 @@ gst_element_unlink (GstElement * src, GstElement * dest) * @element: a #GstElement to invoke the position query on. * @format: the #GstFormat requested * @cur: (out) (allow-none): a location in which to store the current - * position, or NULL. + * position, or %NULL. + * + * Queries an element (usually top-level pipeline or playbin element) for the + * stream position in nanoseconds. This will be a value between 0 and the + * stream duration (if the stream duration is known). This query will usually + * only work once the pipeline is prerolled (i.e. reached PAUSED or PLAYING + * state). The application will receive an ASYNC_DONE message on the pipeline + * bus when that is the case. * - * Queries an element for the stream position. If one repeatedly calls this - * function one can also create and reuse it in gst_element_query(). + * If one repeatedly calls this function one can also create a query and reuse + * it in gst_element_query(). * - * Returns: TRUE if the query could be performed. + * Returns: %TRUE if the query could be performed. */ gboolean gst_element_query_position (GstElement * element, GstFormat format, @@ -2148,6 +2428,9 @@ gst_element_query_position (GstElement * element, GstFormat format, GstQuery *query; gboolean ret; + if (cur != NULL) + *cur = GST_CLOCK_TIME_NONE; + g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); g_return_val_if_fail (format != GST_FORMAT_UNDEFINED, FALSE); @@ -2166,11 +2449,18 @@ gst_element_query_position (GstElement * element, GstFormat format, * gst_element_query_duration: * @element: a #GstElement to invoke the duration query on. * @format: the #GstFormat requested - * @duration: (out): A location in which to store the total duration, or NULL. + * @duration: (out) (allow-none): A location in which to store the total duration, or %NULL. * - * Queries an element for the total stream duration. + * Queries an element (usually top-level pipeline or playbin element) for the + * total stream duration in nanoseconds. This query will only work once the + * pipeline is prerolled (i.e. reached PAUSED or PLAYING state). The application + * will receive an ASYNC_DONE message on the pipeline bus when that is the case. * - * Returns: TRUE if the query could be performed. + * If the duration changes for some reason, you will get a DURATION_CHANGED + * message on the pipeline bus, in which case you should re-query the duration + * using this function. + * + * Returns: %TRUE if the query could be performed. */ gboolean gst_element_query_duration (GstElement * element, GstFormat format, @@ -2179,6 +2469,9 @@ gst_element_query_duration (GstElement * element, GstFormat format, GstQuery *query; gboolean ret; + if (duration != NULL) + *duration = GST_CLOCK_TIME_NONE; + g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); g_return_val_if_fail (format != GST_FORMAT_UNDEFINED, FALSE); @@ -2196,14 +2489,14 @@ gst_element_query_duration (GstElement * element, GstFormat format, /** * gst_element_query_convert: * @element: a #GstElement to invoke the convert query on. - * @src_format: (inout): a #GstFormat to convert from. + * @src_format: a #GstFormat to convert from. * @src_val: a value to convert. * @dest_format: the #GstFormat to convert to. * @dest_val: (out): a pointer to the result. * * Queries an element to convert @src_val in @src_format to @dest_format. * - * Returns: TRUE if the query could be performed. + * Returns: %TRUE if the query could be performed. */ gboolean gst_element_query_convert (GstElement * element, GstFormat src_format, @@ -2269,7 +2562,7 @@ gst_element_seek_simple (GstElement * element, GstFormat format, g_return_val_if_fail (seek_pos >= 0, FALSE); return gst_element_seek (element, 1.0, format, seek_flags, - GST_SEEK_TYPE_SET, seek_pos, GST_SEEK_TYPE_NONE, 0); + GST_SEEK_TYPE_SET, seek_pos, GST_SEEK_TYPE_SET, GST_CLOCK_TIME_NONE); } /** @@ -2295,10 +2588,11 @@ gst_pad_use_fixed_caps (GstPad * pad) * @pad: a pad * * Gets the parent of @pad, cast to a #GstElement. If a @pad has no parent or - * its parent is not an element, return NULL. + * its parent is not an element, return %NULL. * - * Returns: (transfer full): the parent of the pad. The caller has a - * reference on the parent, so unref when you're finished with it. + * Returns: (transfer full) (nullable): the parent of the pad. The + * caller has a reference on the parent, so unref when you're finished + * with it. * * MT safe. */ @@ -2322,10 +2616,10 @@ gst_pad_get_parent_element (GstPad * pad) * gst_object_default_error: * @source: the #GstObject that initiated the error. * @error: (in): the GError. - * @debug: (in) (allow-none): an additional debug information string, or NULL + * @debug: (in) (allow-none): an additional debug information string, or %NULL * * A default error function that uses g_printerr() to display the error message - * and the optional debug sting.. + * and the optional debug string.. * * The default handler will simply print the error string using g_print. */ @@ -2343,12 +2637,12 @@ gst_object_default_error (GstObject * source, const GError * error, } /** - * gst_bin_add_many: + * gst_bin_add_many: (skip) * @bin: a #GstBin - * @element_1: (transfer full): the #GstElement element to add to the bin - * @...: (transfer full): additional elements to add to the bin + * @element_1: (transfer floating): the #GstElement element to add to the bin + * @...: additional elements to add to the bin * - * Adds a NULL-terminated list of elements to a bin. This function is + * Adds a %NULL-terminated list of elements to a bin. This function is * equivalent to calling gst_bin_add() for each member of the list. The return * value of each gst_bin_add() is ignored. */ @@ -2372,10 +2666,10 @@ gst_bin_add_many (GstBin * bin, GstElement * element_1, ...) } /** - * gst_bin_remove_many: + * gst_bin_remove_many: (skip) * @bin: a #GstBin * @element_1: (transfer none): the first #GstElement to remove from the bin - * @...: (transfer none): NULL-terminated list of elements to remove from the bin + * @...: (transfer none): %NULL-terminated list of elements to remove from the bin * * Remove a list of elements from a bin. This function is equivalent * to calling gst_bin_remove() with each member of the list. @@ -2422,14 +2716,14 @@ query_accept_caps_func (GstPad * pad, QueryAcceptCapsData * data) * @pad: a #GstPad to proxy. * @query: an ACCEPT_CAPS #GstQuery. * - * Calls gst_pad_accept_caps() for all internally linked pads of @pad and + * Checks if all internally linked pads of @pad accepts the caps in @query and * returns the intersection of the results. * * This function is useful as a default accept caps query function for an element * that can handle any stream format, but requires caps that are acceptable for - * all oposite pads. + * all opposite pads. * - * Returns: TRUE if @query could be executed + * Returns: %TRUE if @query could be executed */ gboolean gst_pad_proxy_query_accept_caps (GstPad * pad, GstQuery * query) @@ -2445,12 +2739,16 @@ gst_pad_proxy_query_accept_caps (GstPad * pad, GstQuery * query) data.query = query; /* value to hold the return, by default it holds TRUE */ + /* FIXME: TRUE is wrong when there are no pads */ data.ret = TRUE; gst_pad_forward (pad, (GstPadForwardFunction) query_accept_caps_func, &data); gst_query_set_accept_caps_result (query, data.ret); - return TRUE; + GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, "proxying accept caps query: %d", + data.ret); + + return data.ret; } typedef struct @@ -2486,14 +2784,14 @@ query_caps_func (GstPad * pad, QueryCapsData * data) * @pad: a #GstPad to proxy. * @query: a CAPS #GstQuery. * - * Calls gst_pad_query_caps() for all internally linked pads fof @pad and returns + * Calls gst_pad_query_caps() for all internally linked pads of @pad and returns * the intersection of the results. * * This function is useful as a default caps query function for an element * that can handle any stream format, but requires all its pads to have * the same caps. Two such elements are tee and adder. * - * Returns: TRUE if @query could be executed + * Returns: %TRUE if @query could be executed */ gboolean gst_pad_proxy_query_caps (GstPad * pad, GstQuery * query) @@ -2524,6 +2822,7 @@ gst_pad_proxy_query_caps (GstPad * pad, GstQuery * query) gst_query_set_caps_result (query, result); gst_caps_unref (result); + /* FIXME: return something depending on the processing */ return TRUE; } @@ -2531,11 +2830,11 @@ gst_pad_proxy_query_caps (GstPad * pad, GstQuery * query) * gst_pad_query_position: * @pad: a #GstPad to invoke the position query on. * @format: the #GstFormat requested - * @cur: (out): A location in which to store the current position, or NULL. + * @cur: (out) (allow-none): A location in which to store the current position, or %NULL. * * Queries a pad for the stream position. * - * Returns: TRUE if the query could be performed. + * Returns: %TRUE if the query could be performed. */ gboolean gst_pad_query_position (GstPad * pad, GstFormat format, gint64 * cur) @@ -2543,6 +2842,9 @@ gst_pad_query_position (GstPad * pad, GstFormat format, gint64 * cur) GstQuery *query; gboolean ret; + if (cur != NULL) + *cur = GST_CLOCK_TIME_NONE; + g_return_val_if_fail (GST_IS_PAD (pad), FALSE); g_return_val_if_fail (format != GST_FORMAT_UNDEFINED, FALSE); @@ -2560,11 +2862,11 @@ gst_pad_query_position (GstPad * pad, GstFormat format, gint64 * cur) * Must be a sink pad. * @format: the #GstFormat requested * @cur: (out) (allow-none): a location in which to store the current - * position, or NULL. + * position, or %NULL. * * Queries the peer of a given sink pad for the stream position. * - * Returns: TRUE if the query could be performed. + * Returns: %TRUE if the query could be performed. */ gboolean gst_pad_peer_query_position (GstPad * pad, GstFormat format, gint64 * cur) @@ -2572,8 +2874,10 @@ gst_pad_peer_query_position (GstPad * pad, GstFormat format, gint64 * cur) GstQuery *query; gboolean ret = FALSE; + if (cur != NULL) + *cur = GST_CLOCK_TIME_NONE; + g_return_val_if_fail (GST_IS_PAD (pad), FALSE); - g_return_val_if_fail (GST_PAD_IS_SINK (pad), FALSE); g_return_val_if_fail (format != GST_FORMAT_UNDEFINED, FALSE); query = gst_query_new_position (format); @@ -2589,11 +2893,11 @@ gst_pad_peer_query_position (GstPad * pad, GstFormat format, gint64 * cur) * @pad: a #GstPad to invoke the duration query on. * @format: the #GstFormat requested * @duration: (out) (allow-none): a location in which to store the total - * duration, or NULL. + * duration, or %NULL. * * Queries a pad for the total stream duration. * - * Returns: TRUE if the query could be performed. + * Returns: %TRUE if the query could be performed. */ gboolean gst_pad_query_duration (GstPad * pad, GstFormat format, gint64 * duration) @@ -2601,6 +2905,9 @@ gst_pad_query_duration (GstPad * pad, GstFormat format, gint64 * duration) GstQuery *query; gboolean ret; + if (duration != NULL) + *duration = GST_CLOCK_TIME_NONE; + g_return_val_if_fail (GST_IS_PAD (pad), FALSE); g_return_val_if_fail (format != GST_FORMAT_UNDEFINED, FALSE); @@ -2618,11 +2925,11 @@ gst_pad_query_duration (GstPad * pad, GstFormat format, gint64 * duration) * Must be a sink pad. * @format: the #GstFormat requested * @duration: (out) (allow-none): a location in which to store the total - * duration, or NULL. + * duration, or %NULL. * * Queries the peer pad of a given sink pad for the total stream duration. * - * Returns: TRUE if the query could be performed. + * Returns: %TRUE if the query could be performed. */ gboolean gst_pad_peer_query_duration (GstPad * pad, GstFormat format, gint64 * duration) @@ -2630,6 +2937,9 @@ gst_pad_peer_query_duration (GstPad * pad, GstFormat format, gint64 * duration) GstQuery *query; gboolean ret = FALSE; + if (duration != NULL) + *duration = GST_CLOCK_TIME_NONE; + g_return_val_if_fail (GST_IS_PAD (pad), FALSE); g_return_val_if_fail (GST_PAD_IS_SINK (pad), FALSE); g_return_val_if_fail (format != GST_FORMAT_UNDEFINED, FALSE); @@ -2652,7 +2962,7 @@ gst_pad_peer_query_duration (GstPad * pad, GstFormat format, gint64 * duration) * * Queries a pad to convert @src_val in @src_format to @dest_format. * - * Returns: TRUE if the query could be performed. + * Returns: %TRUE if the query could be performed. */ gboolean gst_pad_query_convert (GstPad * pad, GstFormat src_format, gint64 src_val, @@ -2690,7 +3000,7 @@ gst_pad_query_convert (GstPad * pad, GstFormat src_format, gint64 src_val, * Queries the peer pad of a given sink pad to convert @src_val in @src_format * to @dest_format. * - * Returns: TRUE if the query could be performed. + * Returns: %TRUE if the query could be performed. */ gboolean gst_pad_peer_query_convert (GstPad * pad, GstFormat src_format, gint64 src_val, @@ -2715,19 +3025,19 @@ gst_pad_peer_query_convert (GstPad * pad, GstFormat src_format, gint64 src_val, /** * gst_pad_query_caps: * @pad: a #GstPad to get the capabilities of. - * @filter: (allow-none): suggested #GstCaps, or NULL + * @filter: (allow-none): suggested #GstCaps, or %NULL * * Gets the capabilities this pad can produce or consume. * Note that this method doesn't necessarily return the caps set by sending a * gst_event_new_caps() - use gst_pad_get_current_caps() for that instead. * gst_pad_query_caps returns all possible caps a pad can operate with, using * the pad's CAPS query function, If the query fails, this function will return - * @filter, if not #NULL, otherwise ANY. + * @filter, if not %NULL, otherwise ANY. * * When called on sinkpads @filter contains the caps that * upstream could produce in the order preferred by upstream. When * called on srcpads @filter contains the caps accepted by - * downstream in the preffered order. @filter might be %NULL but + * downstream in the preferred order. @filter might be %NULL but * if it is not %NULL the returned caps will be a subset of @filter. * * Note that this function does not return writable #GstCaps, use @@ -2766,7 +3076,7 @@ gst_pad_query_caps (GstPad * pad, GstCaps * filter) /** * gst_pad_peer_query_caps: * @pad: a #GstPad to get the capabilities of. - * @filter: (allow-none): a #GstCaps filter, or NULL. + * @filter: (allow-none): a #GstCaps filter, or %NULL. * * Gets the capabilities of the peer connected to this pad. Similar to * gst_pad_query_caps(). @@ -2774,12 +3084,12 @@ gst_pad_query_caps (GstPad * pad, GstCaps * filter) * When called on srcpads @filter contains the caps that * upstream could produce in the order preferred by upstream. When * called on sinkpads @filter contains the caps accepted by - * downstream in the preffered order. @filter might be %NULL but + * downstream in the preferred order. @filter might be %NULL but * if it is not %NULL the returned caps will be a subset of @filter. * - * Returns: the caps of the peer pad with incremented ref-count. When there is - * no peer pad, this function returns @filter or, when @filter is %NULL, ANY - * caps. + * Returns: (transfer full): the caps of the peer pad with incremented + * ref-count. When there is no peer pad, this function returns @filter or, + * when @filter is %NULL, ANY caps. */ GstCaps * gst_pad_peer_query_caps (GstPad * pad, GstCaps * filter) @@ -2816,7 +3126,7 @@ gst_pad_peer_query_caps (GstPad * pad, GstCaps * filter) * * Check if the given pad accepts the caps. * - * Returns: TRUE if the pad can accept the caps. + * Returns: %TRUE if the pad can accept the caps. */ gboolean gst_pad_query_accept_caps (GstPad * pad, GstCaps * caps) @@ -2846,9 +3156,9 @@ gst_pad_query_accept_caps (GstPad * pad, GstCaps * caps) * @caps: a #GstCaps to check on the pad * * Check if the peer of @pad accepts @caps. If @pad has no peer, this function - * returns TRUE. + * returns %TRUE. * - * Returns: TRUE if the peer of @pad can accept the caps or @pad has no peer. + * Returns: %TRUE if the peer of @pad can accept the caps or @pad has no peer. */ gboolean gst_pad_peer_query_accept_caps (GstPad * pad, GstCaps * caps) @@ -2935,11 +3245,12 @@ element_find_unlinked_pad (GstElement * element, GstPadDirection direction) * * Recursively looks for elements with an unlinked pad of the given * direction within the specified bin and returns an unlinked pad - * if one is found, or NULL otherwise. If a pad is found, the caller + * if one is found, or %NULL otherwise. If a pad is found, the caller * owns a reference to it and should use gst_object_unref() on the * pad when it is not needed any longer. * - * Returns: (transfer full): unlinked pad of the given direction, or NULL. + * Returns: (transfer full) (nullable): unlinked pad of the given + * direction, %NULL. */ GstPad * gst_bin_find_unlinked_pad (GstBin * bin, GstPadDirection direction) @@ -2982,12 +3293,64 @@ gst_bin_find_unlinked_pad (GstBin * bin, GstPadDirection direction) return pad; } +static void +gst_bin_sync_children_states_foreach (const GValue * value, gpointer user_data) +{ + gboolean *success = user_data; + GstElement *element = g_value_get_object (value); + + if (gst_element_is_locked_state (element)) { + *success = TRUE; + } else { + *success = *success && gst_element_sync_state_with_parent (element); + + if (GST_IS_BIN (element)) + *success = *success + && gst_bin_sync_children_states (GST_BIN_CAST (element)); + } +} + +/** + * gst_bin_sync_children_states: + * @bin: a #GstBin + * + * Synchronizes the state of every child of @bin with the state + * of @bin. See also gst_element_sync_state_with_parent(). + * + * Returns: %TRUE if syncing the state was successful for all children, + * otherwise %FALSE. + * + * Since: 1.6 + */ +gboolean +gst_bin_sync_children_states (GstBin * bin) +{ + GstIterator *it; + GstIteratorResult res = GST_ITERATOR_OK; + gboolean success = TRUE; + + it = gst_bin_iterate_sorted (bin); + + do { + if (res == GST_ITERATOR_RESYNC) { + success = TRUE; + gst_iterator_resync (it); + } + res = + gst_iterator_foreach (it, gst_bin_sync_children_states_foreach, + &success); + } while (res == GST_ITERATOR_RESYNC); + gst_iterator_free (it); + + return success; +} + /** * gst_parse_bin_from_description: * @bin_description: command line describing the bin * @ghost_unlinked_pads: whether to automatically create ghost pads * for unlinked source or sink pads within the bin - * @err: where to store the error message in case of an error, or NULL + * @err: where to store the error message in case of an error, or %NULL * * This is a convenience wrapper around gst_parse_launch() to create a * #GstBin from a gst-launch-style pipeline description. See @@ -2999,8 +3362,8 @@ gst_bin_find_unlinked_pad (GstBin * bin, GstPadDirection direction) * and want them all ghosted, you will have to create the ghost pads * yourself). * - * Returns: (transfer floating) (type Gst.Bin): a newly-created bin, - * or %NULL if an error occurred. + * Returns: (transfer floating) (type Gst.Bin) (nullable): a + * newly-created bin, or %NULL if an error occurred. */ GstElement * gst_parse_bin_from_description (const gchar * bin_description, @@ -3018,7 +3381,7 @@ gst_parse_bin_from_description (const gchar * bin_description, * @context: (transfer none) (allow-none): a parse context allocated with * gst_parse_context_new(), or %NULL * @flags: parsing options, or #GST_PARSE_FLAG_NONE - * @err: where to store the error message in case of an error, or NULL + * @err: where to store the error message in case of an error, or %NULL * * This is a convenience wrapper around gst_parse_launch() to create a * #GstBin from a gst-launch-style pipeline description. See @@ -3030,8 +3393,10 @@ gst_parse_bin_from_description (const gchar * bin_description, * and want them all ghosted, you will have to create the ghost pads * yourself). * - * Returns: (transfer full) (type Gst.Bin): a newly-created bin, or - * %NULL if an error occurred. + * Returns: (transfer floating) (type Gst.Element) (nullable): a newly-created + * element, which is guaranteed to be a bin unless + * GST_FLAG_NO_SINGLE_ELEMENT_BINS was passed, or %NULL if an error + * occurred. */ GstElement * gst_parse_bin_from_description_full (const gchar * bin_description, @@ -3040,6 +3405,7 @@ gst_parse_bin_from_description_full (const gchar * bin_description, { #ifndef GST_DISABLE_PARSE GstPad *pad = NULL; + GstElement *element; GstBin *bin; gchar *desc; @@ -3049,16 +3415,26 @@ gst_parse_bin_from_description_full (const gchar * bin_description, GST_DEBUG ("Making bin from description '%s'", bin_description); /* parse the pipeline to a bin */ - desc = g_strdup_printf ("bin.( %s )", bin_description); - bin = (GstBin *) gst_parse_launch_full (desc, context, flags, err); - g_free (desc); + if (flags & GST_PARSE_FLAG_NO_SINGLE_ELEMENT_BINS) { + element = gst_parse_launch_full (bin_description, context, flags, err); + } else { + desc = g_strdup_printf ("bin.( %s )", bin_description); + element = gst_parse_launch_full (desc, context, flags, err); + g_free (desc); + } - if (bin == NULL || (err && *err != NULL)) { - if (bin) - gst_object_unref (bin); + if (element == NULL || (err && *err != NULL)) { + if (element) + gst_object_unref (element); return NULL; } + if (GST_IS_BIN (element)) { + bin = GST_BIN (element); + } else { + return element; + } + /* find pads and ghost them if necessary */ if (ghost_unlinked_pads) { if ((pad = gst_bin_find_unlinked_pad (bin, GST_PAD_SRC))) { @@ -3096,16 +3472,14 @@ gst_parse_bin_from_description_full (const gchar * bin_description, GstClockTime gst_util_get_timestamp (void) { -#if defined (HAVE_POSIX_TIMERS) && defined(HAVE_MONOTONIC_CLOCK) +#if defined (HAVE_POSIX_TIMERS) && defined(HAVE_MONOTONIC_CLOCK) &&\ + defined (HAVE_CLOCK_GETTIME) struct timespec now; clock_gettime (CLOCK_MONOTONIC, &now); return GST_TIMESPEC_TO_TIME (now); #else - GTimeVal now; - - g_get_current_time (&now); - return GST_TIMEVAL_TO_TIME (now); + return g_get_monotonic_time () * 1000; #endif } @@ -3127,7 +3501,8 @@ gst_util_get_timestamp (void) * * The complexity of this search function is O(log (num_elements)). * - * Returns: (transfer none): The address of the found element or %NULL if nothing was found + * Returns: (transfer none) (nullable): The address of the found + * element or %NULL if nothing was found */ gpointer gst_util_array_binary_search (gpointer array, guint num_elements, @@ -3391,6 +3766,13 @@ gst_util_fraction_multiply (gint a_n, gint a_d, gint b_n, gint b_d, g_return_val_if_fail (a_d != 0, FALSE); g_return_val_if_fail (b_d != 0, FALSE); + /* early out if either is 0, as its gcd would be 0 */ + if (a_n == 0 || b_n == 0) { + *res_n = 0; + *res_d = 1; + return TRUE; + } + gcd = gst_util_greatest_common_divisor (a_n, a_d); a_n /= gcd; a_d /= gcd; @@ -3472,8 +3854,6 @@ gst_util_fraction_add (gint a_n, gint a_d, gint b_n, gint b_d, gint * res_n, return FALSE; if (G_MAXINT / ABS (a_d) < ABS (b_d)) return FALSE; - if (G_MAXINT / ABS (a_d) < ABS (b_d)) - return FALSE; *res_n = (a_n * b_d) + (a_d * b_n); *res_d = a_d * b_d; @@ -3536,32 +3916,9 @@ gst_util_fraction_compare (gint a_n, gint a_d, gint b_n, gint b_d) g_return_val_if_reached (0); } -/** - * gst_pad_create_stream_id_printf_valist: - * @pad: A source #GstPad - * @parent: Parent #GstElement of @pad - * @stream_id: (allow-none): The stream-id - * @var_args: parameters for the @stream_id format string - * - * Creates a stream-id for the source #GstPad @pad by combining the - * upstream information with the optional @stream_id of the stream - * of @pad. @pad must have a parent #GstElement and which must have zero - * or one sinkpad. @stream_id can only be %NULL if the parent element - * of @pad has only a single source pad. - * - * This function generates an unique stream-id by getting the upstream - * stream-start event stream ID and appending @stream_id to it. If the - * element has no sinkpad it will generate an upstream stream-id by - * doing an URI query on the element and in the worst case just uses - * a random number. Source elements that don't implement the URI - * handler interface should ideally generate a unique, deterministic - * stream-id manually instead. - * - * Returns: A stream-id for @pad. g_free() after usage. - */ -gchar * -gst_pad_create_stream_id_printf_valist (GstPad * pad, GstElement * parent, - const gchar * stream_id, va_list var_args) +static gchar * +gst_pad_create_stream_id_internal (GstPad * pad, GstElement * parent, + const gchar * stream_id) { GstEvent *upstream_event; gchar *upstream_stream_id = NULL, *new_stream_id; @@ -3600,15 +3957,17 @@ gst_pad_create_stream_id_printf_valist (GstPad * pad, GstElement * parent, * here is for source elements */ if (!upstream_stream_id) { GstQuery *query; + gchar *uri = NULL; /* Try to generate one from the URI query and * if it fails take a random number instead */ query = gst_query_new_uri (); if (gst_element_query (parent, query)) { - GChecksum *cs; - gchar *uri; - gst_query_parse_uri (query, &uri); + } + + if (uri) { + GChecksum *cs; /* And then generate an SHA256 sum of the URI */ cs = g_checksum_new (G_CHECKSUM_SHA256); @@ -3629,9 +3988,7 @@ gst_pad_create_stream_id_printf_valist (GstPad * pad, GstElement * parent, } if (stream_id) { - gchar *expanded = g_strdup_vprintf (stream_id, var_args); - new_stream_id = g_strconcat (upstream_stream_id, "/", expanded, NULL); - g_free (expanded); + new_stream_id = g_strconcat (upstream_stream_id, "/", stream_id, NULL); } else { new_stream_id = g_strdup (upstream_stream_id); } @@ -3642,6 +3999,45 @@ gst_pad_create_stream_id_printf_valist (GstPad * pad, GstElement * parent, } /** + * gst_pad_create_stream_id_printf_valist: + * @pad: A source #GstPad + * @parent: Parent #GstElement of @pad + * @stream_id: (allow-none): The stream-id + * @var_args: parameters for the @stream_id format string + * + * Creates a stream-id for the source #GstPad @pad by combining the + * upstream information with the optional @stream_id of the stream + * of @pad. @pad must have a parent #GstElement and which must have zero + * or one sinkpad. @stream_id can only be %NULL if the parent element + * of @pad has only a single source pad. + * + * This function generates an unique stream-id by getting the upstream + * stream-start event stream ID and appending @stream_id to it. If the + * element has no sinkpad it will generate an upstream stream-id by + * doing an URI query on the element and in the worst case just uses + * a random number. Source elements that don't implement the URI + * handler interface should ideally generate a unique, deterministic + * stream-id manually instead. + * + * Returns: A stream-id for @pad. g_free() after usage. + */ +gchar * +gst_pad_create_stream_id_printf_valist (GstPad * pad, GstElement * parent, + const gchar * stream_id, va_list var_args) +{ + gchar *expanded = NULL, *new_stream_id; + + if (stream_id) + expanded = g_strdup_vprintf (stream_id, var_args); + + new_stream_id = gst_pad_create_stream_id_internal (pad, parent, expanded); + + g_free (expanded); + + return new_stream_id; +} + +/** * gst_pad_create_stream_id_printf: * @pad: A source #GstPad * @parent: Parent #GstElement of @pad @@ -3699,13 +4095,17 @@ gst_pad_create_stream_id_printf (GstPad * pad, GstElement * parent, * handler interface should ideally generate a unique, deterministic * stream-id manually instead. * + * Since stream IDs are sorted alphabetically, any numbers in the + * stream ID should be printed with a fixed number of characters, + * preceded by 0's, such as by using the format \%03u instead of \%u. + * * Returns: A stream-id for @pad. g_free() after usage. */ gchar * gst_pad_create_stream_id (GstPad * pad, GstElement * parent, const gchar * stream_id) { - return gst_pad_create_stream_id_printf (pad, parent, stream_id, NULL); + return gst_pad_create_stream_id_internal (pad, parent, stream_id); } /** @@ -3721,8 +4121,9 @@ gst_pad_create_stream_id (GstPad * pad, GstElement * parent, * The returned stream-id string should be treated as an opaque string, its * contents should not be interpreted. * - * Returns: a newly-allocated copy of the stream-idfor @pad, or %NULL. - * g_free() the returned string when no longer needed. + * Returns: (nullable): a newly-allocated copy of the stream-id for + * @pad, or %NULL. g_free() the returned string when no longer + * needed. * * Since: 1.2 */ @@ -3747,3 +4148,327 @@ gst_pad_get_stream_id (GstPad * pad) return ret; } + +/** + * gst_pad_get_stream: + * @pad: A source #GstPad + * + * Returns the current #GstStream for the @pad, or %NULL if none has been + * set yet, i.e. the pad has not received a stream-start event yet. + * + * This is a convenience wrapper around gst_pad_get_sticky_event() and + * gst_event_parse_stream(). + * + * Returns: (nullable) (transfer full): the current #GstStream for @pad, or %NULL. + * unref the returned stream when no longer needed. + * + * Since: 1.10 + */ +GstStream * +gst_pad_get_stream (GstPad * pad) +{ + GstStream *stream = NULL; + GstEvent *event; + + g_return_val_if_fail (GST_IS_PAD (pad), NULL); + + event = gst_pad_get_sticky_event (pad, GST_EVENT_STREAM_START, 0); + if (event != NULL) { + gst_event_parse_stream (event, &stream); + gst_event_unref (event); + GST_LOG_OBJECT (pad, "pad has stream object %p", stream); + } else { + GST_DEBUG_OBJECT (pad, "pad has not received a stream-start event yet"); + } + + return stream; +} + +/** + * gst_util_group_id_next: + * + * Return a constantly incrementing group id. + * + * This function is used to generate a new group-id for the + * stream-start event. + * + * This function never returns %GST_GROUP_ID_INVALID (which is 0) + * + * Returns: A constantly incrementing unsigned integer, which might + * overflow back to 0 at some point. + */ +guint +gst_util_group_id_next (void) +{ + static gint counter = 1; + gint ret = g_atomic_int_add (&counter, 1); + + /* Make sure we don't return GST_GROUP_ID_INVALID */ + if (G_UNLIKELY (ret == GST_GROUP_ID_INVALID)) + ret = g_atomic_int_add (&counter, 1); + + return ret; +} + +/* Compute log2 of the passed 64-bit number by finding the highest set bit */ +static guint +gst_log2 (GstClockTime in) +{ + const guint64 b[] = + { 0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000, 0xFFFFFFFF00000000LL }; + const guint64 S[] = { 1, 2, 4, 8, 16, 32 }; + int i; + + guint count = 0; + for (i = 5; i >= 0; i--) { + if (in & b[i]) { + in >>= S[i]; + count |= S[i]; + } + } + + return count; +} + +/** + * gst_calculate_linear_regression: (skip) + * @xy: Pairs of (x,y) values + * @temp: Temporary scratch space used by the function + * @n: number of (x,y) pairs + * @m_num: (out): numerator of calculated slope + * @m_denom: (out): denominator of calculated slope + * @b: (out): Offset at Y-axis + * @xbase: (out): Offset at X-axis + * @r_squared: (out): R-squared + * + * Calculates the linear regression of the values @xy and places the + * result in @m_num, @m_denom, @b and @xbase, representing the function + * y(x) = m_num/m_denom * (x - xbase) + b + * that has the least-square distance from all points @x and @y. + * + * @r_squared will contain the remaining error. + * + * If @temp is not %NULL, it will be used as temporary space for the function, + * in which case the function works without any allocation at all. If @temp is + * %NULL, an allocation will take place. @temp should have at least the same + * amount of memory allocated as @xy, i.e. 2*n*sizeof(GstClockTime). + * + * > This function assumes (x,y) values with reasonable large differences + * > between them. It will not calculate the exact results if the differences + * > between neighbouring values are too small due to not being able to + * > represent sub-integer values during the calculations. + * + * Returns: %TRUE if the linear regression was successfully calculated + * + * Since: 1.12 + */ +/* http://mathworld.wolfram.com/LeastSquaresFitting.html + * with SLAVE_LOCK + */ +gboolean +gst_calculate_linear_regression (const GstClockTime * xy, + GstClockTime * temp, guint n, + GstClockTime * m_num, GstClockTime * m_denom, + GstClockTime * b, GstClockTime * xbase, gdouble * r_squared) +{ + const GstClockTime *x, *y; + GstClockTime *newx, *newy; + GstClockTime xmin, ymin, xbar, ybar, xbar4, ybar4; + GstClockTime xmax, ymax; + GstClockTimeDiff sxx, sxy, syy; + gint i, j; + gint pshift = 0; + gint max_bits; + + g_return_val_if_fail (xy != NULL, FALSE); + g_return_val_if_fail (m_num != NULL, FALSE); + g_return_val_if_fail (m_denom != NULL, FALSE); + g_return_val_if_fail (b != NULL, FALSE); + g_return_val_if_fail (xbase != NULL, FALSE); + g_return_val_if_fail (r_squared != NULL, FALSE); + + x = xy; + y = xy + 1; + + xbar = ybar = sxx = syy = sxy = 0; + + xmin = ymin = G_MAXUINT64; + xmax = ymax = 0; + for (i = j = 0; i < n; i++, j += 2) { + xmin = MIN (xmin, x[j]); + ymin = MIN (ymin, y[j]); + + xmax = MAX (xmax, x[j]); + ymax = MAX (ymax, y[j]); + } + + if (temp == NULL) { + /* Allocate up to 1kb on the stack, otherwise heap */ + newx = n > 64 ? g_new (GstClockTime, 2 * n) : g_newa (GstClockTime, 2 * n); + newy = newx + 1; + } else { + newx = temp; + newy = temp + 1; + } + + /* strip off unnecessary bits of precision */ + for (i = j = 0; i < n; i++, j += 2) { + newx[j] = x[j] - xmin; + newy[j] = y[j] - ymin; + } + +#ifdef DEBUGGING_ENABLED + GST_CAT_DEBUG (GST_CAT_CLOCK, "reduced numbers:"); + for (i = j = 0; i < n; i++, j += 2) + GST_CAT_DEBUG (GST_CAT_CLOCK, + " %" G_GUINT64_FORMAT " %" G_GUINT64_FORMAT, newx[j], newy[j]); +#endif + + /* have to do this precisely otherwise the results are pretty much useless. + * should guarantee that none of these accumulators can overflow */ + + /* quantities on the order of 1e10 to 1e13 -> 30-35 bits; + * window size a max of 2^10, so + this addition could end up around 2^45 or so -- ample headroom */ + for (i = j = 0; i < n; i++, j += 2) { + /* Just in case assumptions about headroom prove false, let's check */ + if ((newx[j] > 0 && G_MAXUINT64 - xbar <= newx[j]) || + (newy[j] > 0 && G_MAXUINT64 - ybar <= newy[j])) { + GST_CAT_WARNING (GST_CAT_CLOCK, + "Regression overflowed in clock slaving! xbar %" + G_GUINT64_FORMAT " newx[j] %" G_GUINT64_FORMAT " ybar %" + G_GUINT64_FORMAT " newy[j] %" G_GUINT64_FORMAT, xbar, newx[j], ybar, + newy[j]); + if (temp == NULL && n > 64) + g_free (newx); + return FALSE; + } + + xbar += newx[j]; + ybar += newy[j]; + } + xbar /= n; + ybar /= n; + + /* multiplying directly would give quantities on the order of 1e20-1e26 -> + * 60 bits to 70 bits times the window size that's 80 which is too much. + * Instead we (1) subtract off the xbar*ybar in the loop instead of after, + * to avoid accumulation; (2) shift off some estimated number of bits from + * each multiplicand to limit the expected ceiling. For strange + * distributions of input values, things can still overflow, in which + * case we drop precision and retry - at most a few times, in practice rarely + */ + + /* Guess how many bits we might need for the usual distribution of input, + * with a fallback loop that drops precision if things go pear-shaped */ + max_bits = gst_log2 (MAX (xmax - xmin, ymax - ymin)) * 7 / 8 + gst_log2 (n); + if (max_bits > 64) + pshift = max_bits - 64; + + i = 0; + do { +#ifdef DEBUGGING_ENABLED + GST_CAT_DEBUG (GST_CAT_CLOCK, + "Restarting regression with precision shift %u", pshift); +#endif + + xbar4 = xbar >> pshift; + ybar4 = ybar >> pshift; + sxx = syy = sxy = 0; + for (i = j = 0; i < n; i++, j += 2) { + GstClockTime newx4, newy4; + GstClockTimeDiff tmp; + + newx4 = newx[j] >> pshift; + newy4 = newy[j] >> pshift; + + tmp = (newx4 + xbar4) * (newx4 - xbar4); + if (G_UNLIKELY (tmp > 0 && sxx > 0 && (G_MAXINT64 - sxx <= tmp))) { + do { + /* Drop some precision and restart */ + pshift++; + sxx /= 4; + tmp /= 4; + } while (G_MAXINT64 - sxx <= tmp); + break; + } else if (G_UNLIKELY (tmp < 0 && sxx < 0 && (G_MININT64 - sxx >= tmp))) { + do { + /* Drop some precision and restart */ + pshift++; + sxx /= 4; + tmp /= 4; + } while (G_MININT64 - sxx >= tmp); + break; + } + sxx += tmp; + + tmp = newy4 * newy4 - ybar4 * ybar4; + if (G_UNLIKELY (tmp > 0 && syy > 0 && (G_MAXINT64 - syy <= tmp))) { + do { + pshift++; + syy /= 4; + tmp /= 4; + } while (G_MAXINT64 - syy <= tmp); + break; + } else if (G_UNLIKELY (tmp < 0 && syy < 0 && (G_MININT64 - syy >= tmp))) { + do { + pshift++; + syy /= 4; + tmp /= 4; + } while (G_MININT64 - syy >= tmp); + break; + } + syy += tmp; + + tmp = newx4 * newy4 - xbar4 * ybar4; + if (G_UNLIKELY (tmp > 0 && sxy > 0 && (G_MAXINT64 - sxy <= tmp))) { + do { + pshift++; + sxy /= 4; + tmp /= 4; + } while (G_MAXINT64 - sxy <= tmp); + break; + } else if (G_UNLIKELY (tmp < 0 && sxy < 0 && (G_MININT64 - sxy >= tmp))) { + do { + pshift++; + sxy /= 4; + tmp /= 4; + } while (G_MININT64 - sxy >= tmp); + break; + } + sxy += tmp; + } + } while (i < n); + + if (G_UNLIKELY (sxx == 0)) + goto invalid; + + *m_num = sxy; + *m_denom = sxx; + *b = (ymin + ybar) - gst_util_uint64_scale_round (xbar, *m_num, *m_denom); + /* Report base starting from the most recent observation */ + *xbase = xmax; + *b += gst_util_uint64_scale_round (xmax - xmin, *m_num, *m_denom); + + *r_squared = ((double) sxy * (double) sxy) / ((double) sxx * (double) syy); + +#ifdef DEBUGGING_ENABLED + GST_CAT_DEBUG (GST_CAT_CLOCK, " m = %g", ((double) *m_num) / *m_denom); + GST_CAT_DEBUG (GST_CAT_CLOCK, " b = %" G_GUINT64_FORMAT, *b); + GST_CAT_DEBUG (GST_CAT_CLOCK, " xbase = %" G_GUINT64_FORMAT, *xbase); + GST_CAT_DEBUG (GST_CAT_CLOCK, " r2 = %g", *r_squared); +#endif + + if (temp == NULL && n > 64) + g_free (newx); + + return TRUE; + +invalid: + { + GST_CAT_DEBUG (GST_CAT_CLOCK, "sxx == 0, regression failed"); + if (temp == NULL && n > 64) + g_free (newx); + return FALSE; + } +}