+2004-02-24 Andy Wingo <wingo@pobox.com>
+
+ * gst/gstelement.c (gst_element_dispose): Protect against multiple
+ invocations.
+
+ * gst/schedulers/gstoptimalscheduler.c
+ I added a mess of prototypes at the top of the file by way of
+ documentation. Some of the operations on chains and groups were
+ re-organized.
+
+ (create_group): Added a type argument so if the group is enabled,
+ the setup_group_scheduler knows what to do.
+ (group_elements): Added a type argument here, too, to be passed on
+ to create_group.
+ (group_element_set_enabled): If an unlinked PLAYING element is
+ added to a bin, we have to create a new group to hold the element,
+ and this function will be called before the group is added to the
+ chain. Thus we have a valid case for group->chain==NULL. Instead
+ of calling chain_group_set_enabled, just set the flag on the group
+ (the chain's status will be set when the group is added to it).
+ (gst_opt_scheduler_state_transition, chain_group_set_enabled):
+ Setup the group scheduler when the group is enabled, not
+ specifically when an element goes PAUSED->PLAYING. This means
+ PLAYING elements can be added, linked, and scheduled into a
+ PLAYING pipeline, as was intended.
+ (add_to_group): Don't ref the group twice. I don't know when this
+ double-ref got in here. Removing it has the potential to cause
+ segfaults if other parts of the scheduler are buggy. If you find
+ that the scheduler is segfaulting for you, put in an extra ref
+ here and see if that hacks over the underlying issue. Of course,
+ then find out what code is unreffing a group it doesn't own...
+ (create_group): Make the extra refcount floating, and remove it
+ after adding the element. This means that...
+ (unref_group): Destroy when the refcount reaches 0, not 1, like
+ every other refcounted object in the known universe.
+ (remove_from_group): When a group becomes empty, set it to be not
+ active, and remove it from its chain. Don't unref it again,
+ there's no floating reference any more.
+ (destroy_group): We have to remove the group from the chain in
+ remove_from_group (rather than here) to break refcounting cycles
+ (the chain always has a ref on the group). So assert that
+ group->chain==NULL.
+ (ref_group_by_count): Removed, it was commented out anyway.
+ (merge_chains): Use the remove_from_chain and add_to_chain
+ primitives to do the reparenting, instead of rolling our own
+ implementation.
+ (add_to_chain): The first non-disabled group in the chain's group
+ list will be the entry point for the chain. Because buffers can
+ accumulate in loop elements' peer bufpens, we preferentially
+ schedule loop groups before get groups to avoid unnecessary
+ execution of get-based groups when the bufpens are already full.
+ (gst_opt_scheduler_schedule_run_queue): Debug fixes.
+ (get_group_schedule_function): Ditto.
+ (loop_group_schedule_function): Ditto.
+ (gst_opt_scheduler_loop_wrapper): Ditto.
+ (gst_opt_scheduler_iterate): Ditto.
+
+ I understand the opt scheduler now, yippee!
+
+ * gst/gstpad.c: All throughout, added FIXMEs to look at for 0.9.
+ (gst_pad_get_name, gst_pad_set_chain_function)
+ (gst_pad_set_get_function, gst_pad_set_event_function)
+ (gst_pad_set_event_mask_function, gst_pad_get_event_masks)
+ (gst_pad_get_event_masks_default, gst_pad_set_convert_function)
+ (gst_pad_set_query_function, gst_pad_get_query_types)
+ (gst_pad_get_query_types_default)
+ (gst_pad_set_internal_link_function)
+ (gst_pad_set_formats_function, gst_pad_set_link_function)
+ (gst_pad_set_fixate_function, gst_pad_set_getcaps_function)
+ (gst_pad_set_bufferalloc_function, gst_pad_unlink)
+ (gst_pad_renegotiate, gst_pad_set_parent, gst_pad_get_parent)
+ (gst_pad_add_ghost_pad, gst_pad_proxy_getcaps)
+ (gst_pad_proxy_pad_link, gst_pad_proxy_fixate)
+ (gst_pad_get_pad_template_caps, gst_pad_check_compatibility)
+ (gst_pad_get_peer, gst_pad_get_allowed_caps)
+ (gst_pad_alloc_buffer, gst_pad_push, gst_pad_pull)
+ (gst_pad_selectv, gst_pad_select, gst_pad_template_get_caps)
+ (gst_pad_event_default_dispatch, gst_pad_event_default)
+ (gst_pad_dispatcher, gst_pad_send_event, gst_pad_convert_default)
+ (gst_pad_convert, gst_pad_query_default, gst_pad_query)
+ (gst_pad_get_formats_default, gst_pad_get_formats): Better
+ argument checks, and some doc fixes.
+
+ (gst_pad_custom_new_from_template): Um, does anyone
+ use these functions? Actually make a custom pad instead of a
+ normal one.
+ (gst_pad_try_set_caps): Transpose some checks.
+ (gst_pad_try_set_caps_nonfixed): Same, and use a macro to check if
+ the pad is in negotiation.
+ (gst_pad_try_relink_filtered): Use pad_link_prepare.
+
+ * gst/gstelement.c: Remove prototypes also defined in gstclock.h.
+
+ * gst/gstelement.h:
+ * gst/gstclock.h: Un-deprecate the old clocking API, as discussed
+ on the list.
+
2004-02-24 Thomas Vander Stichele <thomas at apestaart dot org>
* gst/gstbin.c: (gst_bin_add):
<para>
First we will examine the code you would be likely to place in a header
file (although since the interface to the code is entirely defined by the
- pluging system, and doesn't depend on reading a header file, this is not
+ plugin system, and doesn't depend on reading a header file, this is not
crucial.)
The code here can be found in
--- /dev/null
+-*- outline -*-
+
+* Creating Elements Without Factories
+
+** The purpose of factories
+
+On a typical GStreamer system, there are approximately 6.022*10^23
+plugins. GStreamer knows about all of them because of the registry. The
+goal is to avoid initializing each one of them, when maybe for your
+application you only need one or two.
+
+The problem becomes, how do you create an instance of the plugin? The
+normal way to instantiate a class is via g_object_new (TYPE, ARGS...).
+In the case that the plugin isn't loaded, you don't know its type, and
+can't even get it from the type name.
+
+Element factories exist to solve this problem by associating names (like
+"sinesrc" or "identity") with certain types that are provided by the
+plugin. Then when the user asks for "sinesrc", the appropriate plugin is
+loaded, its types are initialized, and then gst_element_factory_create
+creates the object for you.
+
+** Why not factories?
+
+To review, factories (1) allow plugins to remain unloaded if not
+necessary, and (2) make it easy to create elements.
+
+If you are writing an application that has custom elements (as is the
+case with most serious applications), you will probably have the plugin
+loaded up already, and you will have access to the type of the element.
+To muck about creating a plugin for the app, registering the element
+with the plugin, and then creating it with the element factory API
+actually takes more work than the normal way.
+
+** g_object_new
+
+So you want to avoid factories. To create objects with a simple
+g_object_new call is our strategy. However, to preserve the same
+semantics as gst_element_factory_create, we need to know what else is
+needed to initialize a GStreamer element.
+
+The other things that gst_element_factory_create does are as follows:
+
+*** Sets the ->elementfactory member on the element class
+
+Note that anything trying to get the factory won't work (e.g.
+gst_element_get_factory). Thankfully this is less of a problem after the
+0.7 plugin system changes.
+
+*** Initializes the name of the element
+
+To do this ourselves, we either call gst_object_set_name, or when we
+set the "name" property when creating the object.
+
+** Summary
+
+To create a GStreamer element when you know the type, you can just use
+
+g_object_new (get_type_of_my_element (),
+ "name", the_name_you_want_possibly_null,
+ ... any other properties ...
+ NULL);
GType gst_clock_get_type (void);
-#ifndef GST_DISABLE_DEPRECATED
gdouble gst_clock_set_speed (GstClock *clock, gdouble speed);
gdouble gst_clock_get_speed (GstClock *clock);
-#endif
guint64 gst_clock_set_resolution (GstClock *clock, guint64 resolution);
guint64 gst_clock_get_resolution (GstClock *clock);
-#ifndef GST_DISABLE_DEPRECATED
void gst_clock_set_active (GstClock *clock, gboolean active);
gboolean gst_clock_is_active (GstClock *clock);
void gst_clock_reset (GstClock *clock);
gboolean gst_clock_handle_discont (GstClock *clock, guint64 time);
-#endif
GstClockTime gst_clock_get_time (GstClock *clock);
GstClockTime gst_clock_get_event_time (GstClock *clock);
-/* FIXME: deprecate? */
-#ifndef GST_DISABLE_DEPRECATED
GstClockID gst_clock_get_next_id (GstClock *clock);
/* creating IDs that can be used to get notifications */
void gst_clock_id_unschedule (GstClockID id);
void gst_clock_id_unlock (GstClockID id);
void gst_clock_id_free (GstClockID id);
-#endif
G_END_DECLS
}
}
-GstClockID gst_clock_new_single_shot_id (GstClock *clock,
- GstClockTime time);
-void gst_clock_id_free (GstClockID id);
/**
* gst_element_wait:
* @element: element that should wait
element->numsrcpads = 0;
element->numsinkpads = 0;
element->numpads = 0;
- g_mutex_free (element->state_mutex);
- g_cond_free (element->state_cond);
+ if (element->state_mutex)
+ g_mutex_free (element->state_mutex);
+ if (element->state_cond)
+ g_cond_free (element->state_cond);
if (element->prop_value_queue)
g_async_queue_unref (element->prop_value_queue);
gboolean gst_element_provides_clock (GstElement *element);
GstClock* gst_element_get_clock (GstElement *element);
void gst_element_set_clock (GstElement *element, GstClock *clock);
-#ifndef GST_DISABLE_DEPRECATED
GstClockReturn gst_element_clock_wait (GstElement *element,
GstClockID id, GstClockTimeDiff *jitter);
-#endif
GstClockTime gst_element_get_time (GstElement *element);
gboolean gst_element_wait (GstElement *element, GstClockTime timestamp);
void gst_element_set_time (GstElement *element, GstClockTime time);
{
GstPad *pad;
- g_return_val_if_fail (templ != NULL, NULL);
+ g_return_val_if_fail (GST_IS_PAD_TEMPLATE (templ), NULL);
- pad = gst_pad_new (name, templ->direction);
+ pad = gst_pad_custom_new (type, name, templ->direction);
gst_pad_set_pad_template (pad, templ);
return pad;
templ, name);
}
+/* FIXME 0.9: GST_PAD_UNKNOWN needs to die! */
/**
* gst_pad_get_direction:
* @pad: a #GstPad to get the direction of.
gst_object_set_name (GST_OBJECT (pad), name);
}
+/* FIXME 0.9: This function must die */
/**
* gst_pad_get_name:
* @pad: a #GstPad to get the name of.
const gchar*
gst_pad_get_name (GstPad *pad)
{
- g_return_val_if_fail (pad != NULL, NULL);
g_return_val_if_fail (GST_IS_PAD (pad), NULL);
return GST_OBJECT_NAME (pad);
/**
* gst_pad_set_chain_function:
- * @pad: a #GstPad to set the chain function for.
+ * @pad: a real sink #GstPad.
* @chain: the #GstPadChainFunction to set.
*
- * Sets the given chain function for the pad.
+ * Sets the given chain function for the pad. The chain function is called to
+ * process a #GstData input buffer.
*/
void
gst_pad_set_chain_function (GstPad *pad, GstPadChainFunction chain)
{
- g_return_if_fail (pad != NULL);
g_return_if_fail (GST_IS_REAL_PAD (pad));
g_return_if_fail (GST_RPAD_DIRECTION (pad) == GST_PAD_SINK);
/**
* gst_pad_set_get_function:
- * @pad: a #GstPad to set the get function for.
+ * @pad: a real source #GstPad.
* @get: the #GstPadGetFunction to set.
*
- * Sets the given get function for the pad.
+ * Sets the given get function for the pad. The get function is called to
+ * produce a new #GstData to start the processing pipeline. Get functions cannot
+ * return %NULL.
*/
void
gst_pad_set_get_function (GstPad *pad,
GstPadGetFunction get)
{
- g_return_if_fail (pad != NULL);
g_return_if_fail (GST_IS_REAL_PAD (pad));
g_return_if_fail (GST_RPAD_DIRECTION (pad) == GST_PAD_SRC);
/**
* gst_pad_set_event_function:
- * @pad: a #GstPad to set the event handler for.
+ * @pad: a real source #GstPad.
* @event: the #GstPadEventFunction to set.
*
* Sets the given event handler for the pad.
/**
* gst_pad_set_event_mask_function:
- * @pad: a #GstPad to set the event mask function for.
+ * @pad: a real #GstPad of either direction.
* @mask_func: the #GstPadEventMaskFunction to set.
*
* Sets the given event mask function for the pad.
/**
* gst_pad_get_event_masks:
- * @pad: a #GstPad to get the event mask for.
+ * @pad: a #GstPad.
*
* Gets the array of eventmasks from the given pad.
*
{
GstRealPad *rpad;
- if (pad == NULL)
- return FALSE;
+ g_return_val_if_fail (GST_IS_PAD (pad), NULL);
rpad = GST_PAD_REALIZE (pad);
/**
* gst_pad_get_event_masks_default:
- * @pad: a #GstPad to get the event mask for.
+ * @pad: a #GstPad.
*
* Invokes the default event masks dispatcher on the pad.
*
{
GstEventMask *result = NULL;
+ g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+
gst_pad_dispatcher (pad, (GstPadDispatcherFunction)
gst_pad_get_event_masks_dispatcher, &result);
/**
* gst_pad_set_convert_function:
- * @pad: a #GstPad to set the convert function for.
+ * @pad: a real #GstPad of either direction.
* @convert: the #GstPadConvertFunction to set.
*
* Sets the given convert function for the pad.
gst_pad_set_convert_function (GstPad *pad,
GstPadConvertFunction convert)
{
- g_return_if_fail (pad != NULL);
g_return_if_fail (GST_IS_REAL_PAD (pad));
GST_RPAD_CONVERTFUNC (pad) = convert;
/**
* gst_pad_set_query_function:
- * @pad: the #GstPad to set the query function for.
+ * @pad: a real #GstPad of either direction.
* @query: the #GstPadQueryFunction to set.
*
* Set the given query function for the pad.
void
gst_pad_set_query_function (GstPad *pad, GstPadQueryFunction query)
{
- g_return_if_fail (pad != NULL);
g_return_if_fail (GST_IS_REAL_PAD (pad));
GST_RPAD_QUERYFUNC (pad) = query;
/**
* gst_pad_set_query_type_function:
- * @pad: the #GstPad to set the query type function for.
+ * @pad: a real #GstPad of either direction.
* @type_func: the #GstPadQueryTypeFunction to set.
*
* Set the given query type function for the pad.
void
gst_pad_set_query_type_function (GstPad *pad, GstPadQueryTypeFunction type_func)
{
- g_return_if_fail (pad != NULL);
g_return_if_fail (GST_IS_REAL_PAD (pad));
GST_RPAD_QUERYTYPEFUNC (pad) = type_func;
/**
* gst_pad_get_query_types:
- * @pad: the #GstPad to query
+ * @pad: a #GstPad.
*
* Get an array of supported queries that can be performed
* on this pad.
{
GstRealPad *rpad;
- if (pad == NULL)
- return FALSE;
+ g_return_val_if_fail (GST_IS_PAD (pad), NULL);
rpad = GST_PAD_REALIZE (pad);
- g_return_val_if_fail (rpad, FALSE);
+ g_return_val_if_fail (rpad, NULL);
if (GST_RPAD_QUERYTYPEFUNC (rpad))
return GST_RPAD_QUERYTYPEFUNC (rpad) (GST_PAD (pad));
/**
* gst_pad_get_query_types_default:
- * @pad: the #GstPad to query
+ * @pad: a #GstPad.
*
* Invoke the default dispatcher for the query types on
* the pad.
{
GstQueryType *result = NULL;
+ g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+
gst_pad_dispatcher (pad, (GstPadDispatcherFunction)
gst_pad_get_query_types_dispatcher, &result);
/**
* gst_pad_set_internal_link_function:
- * @pad: a #GstPad to set the internal link function for.
+ * @pad: a real #GstPad of either direction.
* @intlink: the #GstPadIntLinkFunction to set.
*
* Sets the given internal link function for the pad.
gst_pad_set_internal_link_function (GstPad *pad,
GstPadIntLinkFunction intlink)
{
- g_return_if_fail (pad != NULL);
g_return_if_fail (GST_IS_REAL_PAD (pad));
GST_RPAD_INTLINKFUNC (pad) = intlink;
/**
* gst_pad_set_formats_function:
- * @pad: the #GstPad to set the formats function for.
+ * @pad: a real #GstPad of either direction.
* @formats: the #GstPadFormatsFunction to set.
*
* Sets the given formats function for the pad.
void
gst_pad_set_formats_function (GstPad *pad, GstPadFormatsFunction formats)
{
- g_return_if_fail (pad != NULL);
g_return_if_fail (GST_IS_REAL_PAD (pad));
GST_RPAD_FORMATSFUNC (pad) = formats;
/**
* gst_pad_set_link_function:
- * @pad: a #GstPad to set the link function for.
+ * @pad: a real #GstPad.
* @link: the #GstPadLinkFunction to set.
*
* Sets the given link function for the pad. It will be called when the pad is
*/
void
gst_pad_set_link_function (GstPad *pad,
- GstPadLinkFunction link)
+ GstPadLinkFunction link)
{
- g_return_if_fail (pad != NULL);
g_return_if_fail (GST_IS_REAL_PAD (pad));
GST_RPAD_LINKFUNC (pad) = link;
/**
* gst_pad_set_unlink_function:
- * @pad: a #GstPad to set the unlink function for.
+ * @pad: a real #GstPad.
* @unlink: the #GstPadUnlinkFunction to set.
*
* Sets the given unlink function for the pad. It will be called
gst_pad_set_unlink_function (GstPad *pad,
GstPadUnlinkFunction unlink)
{
- g_return_if_fail (pad != NULL);
g_return_if_fail (GST_IS_REAL_PAD (pad));
GST_RPAD_UNLINKFUNC (pad) = unlink;
/**
* gst_pad_set_fixate_function:
- * @pad: a #GstPad to set the fixate function for.
+ * @pad: a real #GstPad.
* @fixate: the #GstPadFixateFunction to set.
*
* Sets the given fixate function for the pad. Its job is to narrow down the
void
gst_pad_set_fixate_function (GstPad *pad, GstPadFixateFunction fixate)
{
- g_return_if_fail (pad != NULL);
g_return_if_fail (GST_IS_REAL_PAD (pad));
GST_RPAD_FIXATEFUNC (pad) = fixate;
/**
* gst_pad_set_getcaps_function:
- * @pad: a #GstPad to set the getcaps function for.
+ * @pad: a real #GstPad.
* @getcaps: the #GstPadGetCapsFunction to set.
*
* Sets the given getcaps function for the pad. @getcaps should return the
gst_pad_set_getcaps_function (GstPad *pad,
GstPadGetCapsFunction getcaps)
{
- g_return_if_fail (pad != NULL);
g_return_if_fail (GST_IS_REAL_PAD (pad));
GST_RPAD_GETCAPSFUNC (pad) = getcaps;
/**
* gst_pad_set_bufferalloc_function:
- * @pad: a #GstPad to set the bufferalloc function for.
+ * @pad: a real sink #GstPad.
* @bufalloc: the #GstPadBufferAllocFunction to set.
*
* Sets the given bufferalloc function for the pad. Note that the
gst_pad_set_bufferalloc_function (GstPad *pad,
GstPadBufferAllocFunction bufalloc)
{
- g_return_if_fail (pad != NULL);
g_return_if_fail (GST_IS_REAL_PAD (pad));
g_return_if_fail (GST_PAD_IS_SINK (pad));
GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (bufalloc));
}
+/* FIXME 0.9: Do we actually want to allow the case where src and sink are
+ switched? */
/**
* gst_pad_unlink:
* @srcpad: the source #GstPad to unlink.
*/
void
gst_pad_unlink (GstPad *srcpad,
- GstPad *sinkpad)
+ GstPad *sinkpad)
{
GstRealPad *realsrc, *realsink;
GstScheduler *src_sched, *sink_sched;
- /* generic checks */
- g_return_if_fail (srcpad != NULL);
g_return_if_fail (GST_IS_PAD (srcpad));
- g_return_if_fail (sinkpad != NULL);
g_return_if_fail (GST_IS_PAD (sinkpad));
GST_CAT_INFO (GST_CAT_ELEMENT_PADS, "unlinking %s:%s(%p) and %s:%s(%p)",
GST_DEBUG_PAD_NAME (srcpad), srcpad,
GST_DEBUG_PAD_NAME (sinkpad), sinkpad);
- /* now we need to deal with the real/ghost stuff */
realsrc = GST_PAD_REALIZE (srcpad);
realsink = GST_PAD_REALIZE (sinkpad);
{
GstPadLink *link;
- g_return_val_if_fail (pad != NULL, GST_PAD_LINK_REFUSED);
g_return_val_if_fail (GST_IS_PAD (pad), GST_PAD_LINK_REFUSED);
-
g_return_val_if_fail (GST_PAD_LINK_SRC (pad), GST_PAD_LINK_REFUSED);
g_return_val_if_fail (GST_PAD_LINK_SINK (pad), GST_PAD_LINK_REFUSED);
GstPadLink *oldlink;
GstPadLinkReturn ret;
- g_return_val_if_fail (pad != NULL, GST_PAD_LINK_REFUSED);
g_return_val_if_fail (GST_IS_REAL_PAD (pad), GST_PAD_LINK_REFUSED);
g_return_val_if_fail (!GST_PAD_IS_NEGOTIATING (pad), GST_PAD_LINK_REFUSED);
return GST_PAD_LINK_OK;
}
+ g_return_val_if_fail (GST_PAD_LINK_SRC (pad), GST_PAD_LINK_REFUSED);
+ g_return_val_if_fail (GST_PAD_LINK_SINK (pad), GST_PAD_LINK_REFUSED);
+
/* if the desired caps are already there, it's trivially ok */
if (GST_PAD_CAPS (pad) && gst_caps_is_equal_fixed (caps,
GST_PAD_CAPS (pad))) {
return GST_PAD_LINK_OK;
}
- g_return_val_if_fail (GST_PAD_LINK_SRC (pad), GST_PAD_LINK_REFUSED);
- g_return_val_if_fail (GST_PAD_LINK_SINK (pad), GST_PAD_LINK_REFUSED);
-
link = gst_pad_link_new ();
link->srcpad = GST_PAD_LINK_SRC (pad);
/**
* gst_pad_try_set_caps_nonfixed:
- * @pad: a #GstPad
+ * @pad: a real #GstPad
* @caps: #GstCaps to set on @pad
*
* Like gst_pad_try_set_caps(), but allows non-fixed caps.
GstPadLink *oldlink;
GstPadLinkReturn ret;
- g_return_val_if_fail (pad != NULL, GST_PAD_LINK_REFUSED);
g_return_val_if_fail (GST_IS_REAL_PAD (pad), GST_PAD_LINK_REFUSED);
- g_return_val_if_fail (!GST_FLAG_IS_SET (pad, GST_PAD_NEGOTIATING),
- GST_PAD_LINK_REFUSED);
+ g_return_val_if_fail (!GST_PAD_IS_NEGOTIATING (pad), GST_PAD_LINK_REFUSED);
/* we allow setting caps on non-linked pads. It's ignored */
if (!GST_PAD_PEER (pad)) {
return GST_PAD_LINK_OK;
}
+ g_return_val_if_fail (GST_PAD_LINK_SRC (pad), GST_PAD_LINK_REFUSED);
+ g_return_val_if_fail (GST_PAD_LINK_SINK (pad), GST_PAD_LINK_REFUSED);
+
/* if the link is already negotiated and the caps are compatible
* with what we're setting, it's trivially OK. */
if (GST_PAD_CAPS (pad)) {
gst_caps_free (intersection);
}
- g_return_val_if_fail (GST_PAD_LINK_SRC (pad), GST_PAD_LINK_REFUSED);
- g_return_val_if_fail (GST_PAD_LINK_SINK (pad), GST_PAD_LINK_REFUSED);
-
link = gst_pad_link_new ();
link->srcpad = GST_PAD_LINK_SRC (pad);
realsrc = GST_PAD_REALIZE (srcpad);
realsink = GST_PAD_REALIZE (sinkpad);
+ g_return_val_if_fail (GST_RPAD_PEER (realsrc) == NULL, NULL);
+ g_return_val_if_fail (GST_RPAD_PEER (realsink) == NULL, NULL);
+ g_return_val_if_fail (GST_PAD_PARENT (realsrc) != NULL, NULL);
+ g_return_val_if_fail (GST_PAD_PARENT (realsink) != NULL, NULL);
+
if ((GST_PAD (realsrc) != srcpad) || (GST_PAD (realsink) != sinkpad)) {
GST_CAT_INFO (GST_CAT_PADS, "*actually* linking %s:%s and %s:%s",
GST_DEBUG_PAD_NAME (realsrc), GST_DEBUG_PAD_NAME (realsink));
*/
gboolean
gst_pad_link_filtered (GstPad *srcpad, GstPad *sinkpad,
- const GstCaps *filtercaps)
+ const GstCaps *filtercaps)
{
GstRealPad *realsrc, *realsink;
GstScheduler *src_sched, *sink_sched;
return gst_pad_link_filtered (srcpad, sinkpad, NULL);
}
+/* FIXME 0.9: Remove this */
/**
* gst_pad_set_parent:
* @pad: a #GstPad to set the parent of.
void
gst_pad_set_parent (GstPad *pad, GstElement *parent)
{
- g_return_if_fail (pad != NULL);
g_return_if_fail (GST_IS_PAD (pad));
g_return_if_fail (GST_PAD_PARENT (pad) == NULL);
- g_return_if_fail (parent != NULL);
- g_return_if_fail (GST_IS_OBJECT (parent));
- g_return_if_fail ((gpointer) pad != (gpointer) parent);
+ g_return_if_fail (GST_IS_ELEMENT (parent));
gst_object_set_parent (GST_OBJECT (pad), GST_OBJECT (parent));
}
+/* FIXME 0.9: Remove this */
/**
* gst_pad_get_parent:
* @pad: the #GstPad to get the parent of.
if (templ)
g_signal_emit (G_OBJECT (templ), gst_pad_template_signals[TEMPL_PAD_CREATED], 0, pad);
}
+
/**
* gst_pad_get_pad_template:
- * @pad: a #GstPad to get the pad template of.
+ * @pad: a #GstPad.
*
- * Gets the pad template object of this pad.
+ * Gets the template for @pad.
*
- * Returns: the #GstPadTemplate from which this pad was instantiated.
+ * Returns: the #GstPadTemplate from which this pad was instantiated, or %NULL
+ * if this pad has no template.
*/
GstPadTemplate*
gst_pad_get_pad_template (GstPad *pad)
* is taken. For decoupled pads, the scheduler of the peer
* parent is taken.
*
- * Returns: the #GstScheduler of the pad.
+ * Returns: the #GstScheduler of the pad, or %NULL if there is no parent or the
+ * parent is not yet in a managing bin.
*/
GstScheduler*
gst_pad_get_scheduler (GstPad *pad)
GstElement*
gst_pad_get_real_parent (GstPad *pad)
{
- g_return_val_if_fail (pad != NULL, NULL);
g_return_val_if_fail (GST_IS_PAD (pad), NULL);
return GST_PAD_PARENT (GST_PAD (GST_PAD_REALIZE (pad)));
}
+/* FIXME 0.9: Make static. */
/**
* gst_pad_add_ghost_pad:
* @pad: a #GstPad to attach the ghost pad to.
{
GstRealPad *realpad;
- g_return_if_fail (pad != NULL);
g_return_if_fail (GST_IS_PAD (pad));
- g_return_if_fail (ghostpad != NULL);
g_return_if_fail (GST_IS_GHOST_PAD (ghostpad));
/* if we're ghosting a ghost pad, drill down to find the real pad */
gst_pad_set_pad_template (GST_PAD (ghostpad), GST_PAD_PAD_TEMPLATE (pad));
}
+/* FIXME 0.9: Make static. */
/**
* gst_pad_remove_ghost_pad:
* @pad: a #GstPad to remove the ghost pad from.
GList*
gst_pad_get_ghost_pad_list (GstPad *pad)
{
- g_return_val_if_fail (pad != NULL, NULL);
g_return_val_if_fail (GST_IS_PAD (pad), NULL);
return GST_PAD_REALIZE(pad)->ghostpads;
*/
gboolean
gst_pad_try_relink_filtered (GstPad *srcpad, GstPad *sinkpad,
- const GstCaps *filtercaps)
+ const GstCaps *filtercaps)
{
- GstRealPad *realsrc, *realsink;
GstPadLink *link;
- /* generic checks */
- g_return_val_if_fail (GST_IS_PAD (srcpad), FALSE);
- g_return_val_if_fail (GST_IS_PAD (sinkpad), FALSE);
+ GST_INFO ("trying to relink %" GST_PTR_FORMAT " and %" GST_PTR_FORMAT
+ " with filtercaps %" GST_PTR_FORMAT, srcpad, sinkpad);
- GST_CAT_INFO (GST_CAT_PADS, "trying to relink %s:%s and %s:%s with filtercaps %" GST_PTR_FORMAT,
- GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad), filtercaps);
-
- /* now we need to deal with the real/ghost stuff */
- realsrc = GST_PAD_REALIZE (srcpad);
- realsink = GST_PAD_REALIZE (sinkpad);
-
- g_return_val_if_fail (realsrc != NULL, FALSE);
- g_return_val_if_fail (realsink != NULL, FALSE);
- g_return_val_if_fail (GST_RPAD_PEER (realsrc) == realsink, FALSE);
- g_assert (realsrc == GST_RPAD_PEER (realsink));
- if ((GST_PAD (realsrc) != srcpad) || (GST_PAD (realsink) != sinkpad)) {
- GST_CAT_INFO (GST_CAT_PADS, "*actually* linking %s:%s and %s:%s",
- GST_DEBUG_PAD_NAME (realsrc), GST_DEBUG_PAD_NAME (realsink));
- }
+ link = gst_pad_link_prepare (srcpad, sinkpad, filtercaps);
+ if (!link) return FALSE;
- link = gst_pad_link_new ();
-
- if (GST_RPAD_DIRECTION (realsrc) == GST_PAD_SRC) {
- link->srcpad = GST_PAD (realsrc);
- link->sinkpad = GST_PAD (realsink);
- } else {
- link->srcpad = GST_PAD (realsink);
- link->sinkpad = GST_PAD (realsrc);
- }
-
- if (GST_RPAD_DIRECTION (link->srcpad) != GST_PAD_SRC) {
- GST_CAT_INFO (GST_CAT_PADS, "Real src pad %s:%s is not a source pad, failed",
- GST_DEBUG_PAD_NAME (link->srcpad));
- gst_pad_link_free (link);
- return FALSE;
- }
- if (GST_RPAD_DIRECTION (link->sinkpad) != GST_PAD_SINK) {
- GST_CAT_INFO (GST_CAT_PADS, "Real sink pad %s:%s is not a sink pad, failed",
- GST_DEBUG_PAD_NAME (link->sinkpad));
+ if (GST_RPAD_PEER (link->srcpad) != (GstRealPad*)link->sinkpad) {
+ g_warning ("Pads %s:%s and %s:%s were never linked",
+ GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad));
gst_pad_link_free (link);
return FALSE;
}
- link->srccaps = gst_pad_get_caps (link->srcpad);
- link->sinkcaps = gst_pad_get_caps (link->sinkpad);
- if (filtercaps) link->filtercaps = gst_caps_copy (filtercaps);
if (GST_PAD_LINK_FAILED (gst_pad_link_try (link)))
return FALSE;
*/
gboolean
gst_pad_relink_filtered (GstPad *srcpad, GstPad *sinkpad,
- const GstCaps *filtercaps)
+ const GstCaps *filtercaps)
{
if (gst_pad_try_relink_filtered (srcpad, sinkpad, filtercaps))
return TRUE;
const GList *pads;
GstCaps *caps;
+ g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+
GST_DEBUG ("proxying getcaps for %s:%s\n", GST_DEBUG_PAD_NAME (pad));
element = gst_pad_get_parent (pad);
*
* Calls gst_pad_try_set_caps() for every other pad belonging to the
* same element as @pad. If gst_pad_try_set_caps() fails on any pad,
- * the proxy link fails.
+ * the proxy link fails. May be used only during negotiation.
*
* Returns: GST_PAD_LINK_OK if sucessful
*/
const GList *pads;
GstPadLinkReturn ret;
+ g_return_val_if_fail (GST_IS_PAD (pad), GST_PAD_LINK_REFUSED);
+ g_return_val_if_fail (caps != NULL, GST_PAD_LINK_REFUSED);
+
GST_DEBUG ("proxying pad link for %s:%s\n", GST_DEBUG_PAD_NAME (pad));
element = gst_pad_get_parent (pad);
const GList *pads;
const GstCaps *othercaps;
+ g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+ g_return_val_if_fail (caps != NULL, NULL);
+
GST_DEBUG ("proxying fixate for %s:%s\n", GST_DEBUG_PAD_NAME (pad));
element = gst_pad_get_parent (pad);
gst_pad_get_pad_template_caps (GstPad *pad)
{
static GstStaticCaps anycaps = GST_STATIC_CAPS ("ANY");
- g_return_val_if_fail (pad != NULL, NULL);
g_return_val_if_fail (GST_IS_PAD (pad), NULL);
if (GST_PAD_PAD_TEMPLATE (pad))
#if 0
/* FIXME this should be enabled some day */
+ /* wingo: why? mail the list during 0.9 when you find this :) */
g_warning("pad %s:%s (%p) has no pad template\n",
GST_DEBUG_PAD_NAME (realpad), realpad);
#endif
return gst_static_caps_get(&anycaps);
}
+/* FIXME 0.9: This function should probably die, or at least be renamed to
+ * get_caps_by_format. */
/**
* gst_pad_template_get_caps_by_name:
* @templ: a #GstPadTemplate to get the capabilities of.
return NULL;
}
+/* FIXME 0.9: What good is this if it only works for already-negotiated pads? */
/**
* gst_pad_check_compatibility:
* @srcpad: the source #GstPad to check.
* @sinkpad: the sink #GstPad to check against.
*
- * Checks if two pads have compatible capabilities.
+ * Checks if two pads have compatible capabilities. If neither one has yet been
+ * negotiated, returns TRUE for no good reason.
*
* Returns: TRUE if they are compatible or if the capabilities could not be
* checked, FALSE if the capabilities are not compatible.
gboolean
gst_pad_check_compatibility (GstPad *srcpad, GstPad *sinkpad)
{
- g_return_val_if_fail (srcpad != NULL, FALSE);
g_return_val_if_fail (GST_IS_PAD (srcpad), FALSE);
- g_return_val_if_fail (sinkpad != NULL, FALSE);
g_return_val_if_fail (GST_IS_PAD (sinkpad), FALSE);
if (GST_PAD_CAPS (srcpad) && GST_PAD_CAPS (sinkpad)) {
* gst_pad_get_peer:
* @pad: a #GstPad to get the peer of.
*
- * Gets the peer pad of @pad.
+ * Gets the peer of @pad.
*
* Returns: the peer #GstPad.
*/
GstPad*
gst_pad_get_peer (GstPad *pad)
{
- g_return_val_if_fail (pad != NULL, NULL);
g_return_val_if_fail (GST_IS_PAD (pad), NULL);
return GST_PAD (GST_PAD_PEER (pad));
/**
* gst_pad_get_allowed_caps:
- * @pad: a #GstPad to get the allowed caps of.
+ * @pad: a real #GstPad.
*
- * Gets the capabilities of the allowed media types that can
- * flow through this pad. The caller must free the resulting caps.
+ * Gets the capabilities of the allowed media types that can flow through @pad.
+ * The caller must free the resulting caps.
*
* Returns: the allowed #GstCaps of the pad link. Free the caps when
* you no longer need it.
GstCaps *icaps;
GstPadLink *link;
- g_return_val_if_fail (pad != NULL, NULL);
g_return_val_if_fail (GST_IS_REAL_PAD (pad), NULL);
GST_CAT_DEBUG (GST_CAT_PROPERTIES, "get allowed caps of %s:%s",
gboolean
gst_pad_recover_caps_error (GstPad *pad, const GstCaps *allowed)
{
+ /* FIXME */
return FALSE;
}
+/* FIXME 0.9: Is it so difficult to write SOURCE? */
/**
* gst_pad_alloc_buffer:
- * @pad: a #GstPad to get the buffer from.
+ * @pad: a source #GstPad.
*
* Allocates a new, empty buffer optimized to push to pad @pad. This
* function only works if @pad is a src pad.
{
GstRealPad *peer;
- g_return_val_if_fail (pad != NULL, NULL);
g_return_val_if_fail (GST_IS_PAD (pad), NULL);
g_return_val_if_fail (GST_PAD_IS_SRC (pad), NULL);
/**
* gst_pad_push:
- * @pad: a #GstPad to push the buffer out of.
+ * @pad: a source #GstPad.
* @data: the #GstData to push.
*
- * Pushes a buffer or an event to the peer of @pad. @pad must be linked.
+ * Pushes a buffer or an event to the peer of @pad. @pad must be linked. May
+ * only be called by @pad's parent.
*/
void
gst_pad_push (GstPad *pad, GstData *data)
/**
* gst_pad_pull:
- * @pad: a #GstPad to pull a buffer from.
+ * @pad: a sink #GstPad.
*
- * Pulls an event or a buffer from the peer pad.
+ * Pulls an event or a buffer from the peer pad. May only be called by @pad's
+ * parent.
*
* Returns: a new #GstData from the peer pad.
*/
/**
* gst_pad_selectv:
- * @padlist: a #GList of pads.
+ * @padlist: a #GList of sink pads.
*
- * Waits for a buffer on any of the list of pads.
+ * Waits for a buffer on any of the list of pads. Each #GstPad in @padlist must
+ * be owned by the calling code.
*
* Returns: the #GstPad that has a buffer available.
* Use #gst_pad_pull() to get the buffer.
return pad;
}
+/* FIXME 0.9: Don't allow the first pad to be NULL */
/**
* gst_pad_select:
- * @pad: a first #GstPad to perform the select on.
+ * @pad: a first sink #GstPad to perform the select on.
* @...: A NULL-terminated list of more pads to select on.
*
* Waits for a buffer on the given set of pads.
const GstCaps*
gst_pad_template_get_caps (GstPadTemplate *templ)
{
- g_return_val_if_fail (templ != NULL, NULL);
+ g_return_val_if_fail (GST_IS_PAD_TEMPLATE (templ), NULL);
return GST_PAD_TEMPLATE_CAPS (templ);
}
{
GList *orig, *pads;
+ GST_INFO_OBJECT (pad, "Sending event %p to all internally linked pads", event);
+
orig = pads = gst_pad_get_internal_links (pad);
while (pads) {
GstPad *eventpad = GST_PAD (pads->data);
pads = g_list_next (pads);
- /* for all pads in the opposite direction that are linked */
+ /* for all of the internally-linked pads that are actually linked */
if (GST_PAD_IS_LINKED (eventpad)) {
if (GST_PAD_DIRECTION (eventpad) == GST_PAD_SRC) {
/* increase the refcount */
* @pad: a #GstPad to call the default event handler on.
* @event: the #GstEvent to handle.
*
- * Invokes the default event handler for the given pad.
+ * Invokes the default event handler for the given pad. End-of-stream and
+ * discontinuity events are handled specially, and then the event is sent to all
+ * pads internally linked to @pad. Note that if there are many possible sink
+ * pads that are internally linked to @pad, only one will be sent an event.
+ * Multi-sinkpad elements should implement custom event handlers.
*
* Returns: TRUE if the event was sent succesfully.
*/
GstElement *element;
g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
- g_return_val_if_fail (event, FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
element = GST_PAD_PARENT (pad);
GList *int_pads, *orig;
g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
- g_return_val_if_fail (data, FALSE);
+ g_return_val_if_fail (dispatch != NULL, FALSE);
orig = int_pads = gst_pad_get_internal_links (pad);
GstRealPad *rpad;
g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
- g_return_val_if_fail (event, FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
rpad = GST_PAD_REALIZE (pad);
GstPadConvertData data;
g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
- g_return_val_if_fail (dest_format, FALSE);
- g_return_val_if_fail (dest_value, FALSE);
+ g_return_val_if_fail (dest_format != NULL, FALSE);
+ g_return_val_if_fail (dest_value != NULL, FALSE);
data.src_format = src_format;
data.src_value = src_value;
GstRealPad *rpad;
g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
- g_return_val_if_fail (dest_format, FALSE);
- g_return_val_if_fail (dest_value, FALSE);
+ g_return_val_if_fail (dest_format != NULL, FALSE);
+ g_return_val_if_fail (dest_value != NULL, FALSE);
if (src_format == *dest_format) {
*dest_value = src_value;
GstPadQueryData data;
g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
- g_return_val_if_fail (format, FALSE);
- g_return_val_if_fail (value, FALSE);
+ g_return_val_if_fail (format != NULL, FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
data.type = type;
data.format = format;
GstRealPad *rpad;
g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
- g_return_val_if_fail (format, FALSE);
- g_return_val_if_fail (value, FALSE);
+ g_return_val_if_fail (format != NULL, FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
rpad = GST_PAD_REALIZE (pad);
{
GstFormat *result = NULL;
+ g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+
gst_pad_dispatcher (pad, (GstPadDispatcherFunction)
gst_pad_get_formats_dispatcher, &result);
{
GstRealPad *rpad;
- g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
+ g_return_val_if_fail (GST_IS_PAD (pad), NULL);
rpad = GST_PAD_REALIZE (pad);
};
-/* some group operations */
+/*
+ * A group is a set of elements through which data can flow without switching
+ * cothreads or without invoking the scheduler's run queue.
+ */
static GstOptSchedulerGroup* ref_group (GstOptSchedulerGroup *group);
-#ifndef USE_COTHREADS
-/*
-static GstOptSchedulerGroup* ref_group_by_count (GstOptSchedulerGroup *group, gint count);
-*/
-#endif
static GstOptSchedulerGroup* unref_group (GstOptSchedulerGroup *group);
+static GstOptSchedulerGroup* create_group (GstOptSchedulerChain *chain,
+ GstElement *element,
+ GstOptSchedulerGroupType type);
static void destroy_group (GstOptSchedulerGroup *group);
+static GstOptSchedulerGroup* add_to_group (GstOptSchedulerGroup *group,
+ GstElement *element);
+static GstOptSchedulerGroup* remove_from_group (GstOptSchedulerGroup *group,
+ GstElement *element);
+static GstOptSchedulerGroup* merge_groups (GstOptSchedulerGroup *group1,
+ GstOptSchedulerGroup *group2);
+static void setup_group_scheduler (GstOptScheduler *osched,
+ GstOptSchedulerGroup *group);
+static void destroy_group_scheduler (GstOptSchedulerGroup *group);
+static void group_error_handler (GstOptSchedulerGroup *group);
static void group_element_set_enabled (GstOptSchedulerGroup *group,
- GstElement *element, gboolean enabled);
+ GstElement *element,
+ gboolean enabled);
+static gboolean schedule_group (GstOptSchedulerGroup *group);
+
+/*
+ * A chain is a set of groups that are linked to each other.
+ */
+static void destroy_chain (GstOptSchedulerChain *chain);
+static GstOptSchedulerChain* create_chain (GstOptScheduler *osched);
+static GstOptSchedulerChain* ref_chain (GstOptSchedulerChain *chain);
+static GstOptSchedulerChain* unref_chain (GstOptSchedulerChain *chain);
+static GstOptSchedulerChain* add_to_chain (GstOptSchedulerChain *chain,
+ GstOptSchedulerGroup *group);
+static GstOptSchedulerChain* remove_from_chain (GstOptSchedulerChain *chain,
+ GstOptSchedulerGroup *group);
+static GstOptSchedulerChain* merge_chains (GstOptSchedulerChain *chain1,
+ GstOptSchedulerChain *chain2);
+static void chain_recursively_migrate_group (GstOptSchedulerChain *chain,
+ GstOptSchedulerGroup *group);
static void chain_group_set_enabled (GstOptSchedulerChain *chain,
- GstOptSchedulerGroup *group, gboolean enabled);
+ GstOptSchedulerGroup *group,
+ gboolean enabled);
+static void schedule_chain (GstOptSchedulerChain *chain);
+
+
+/*
+ * The schedule functions are the entry points for cothreads, or called directly
+ * by gst_opt_scheduler_schedule_run_queue
+ */
+static int get_group_schedule_function (int argc, char *argv[]);
+static int loop_group_schedule_function (int argc, char *argv[]);
+static int unknown_group_schedule_function (int argc, char *argv[]);
+
+
+/*
+ * These wrappers are set on the pads as the chain handler (what happens when
+ * gst_pad_push is called) or get handler (for gst_pad_pull).
+ */
+static void gst_opt_scheduler_loop_wrapper (GstPad *sinkpad, GstData *data);
+static GstData* gst_opt_scheduler_get_wrapper (GstPad *srcpad);
+static void gst_opt_scheduler_chain_wrapper (GstPad *sinkpad, GstData *data);
+
+
+/*
+ * Without cothreads, gst_pad_push or gst_pad_pull on a loop-based group will
+ * just queue the peer element on a list. We need to actually run the queue
+ * instead of relying on cothreads to do the switch for us.
+ */
+#ifndef USE_COTHREADS
+static void gst_opt_scheduler_schedule_run_queue (GstOptScheduler *osched);
+#endif
+
+
/*
* Scheduler private data for an element
*/
GstOptSchedulerCtxFlags flags; /* flags for this element */
};
+
+/*
+ * Implementation of GstScheduler
+ */
enum
{
ARG_0,
ARG_ITERATIONS,
ARG_MAX_RECURSION,
};
-
static void gst_opt_scheduler_class_init (GstOptSchedulerClass *klass);
static void gst_opt_scheduler_init (GstOptScheduler *scheduler);
);
-static void
-destroy_chain (GstOptSchedulerChain *chain)
+static GstOptSchedulerChain*
+ref_chain (GstOptSchedulerChain *chain)
{
- GstOptScheduler *osched;
-
- GST_LOG ( "destroy chain %p", chain);
+ GST_LOG ("ref chain %p %d->%d", chain,
+ chain->refcount, chain->refcount+1);
+ chain->refcount++;
- g_assert (chain->num_groups == 0);
- g_assert (chain->groups == NULL);
+ return chain;
+}
- osched = chain->sched;
- osched->chains = g_slist_remove (osched->chains, chain);
+static GstOptSchedulerChain*
+unref_chain (GstOptSchedulerChain *chain)
+{
+ GST_LOG ("unref chain %p %d->%d", chain,
+ chain->refcount, chain->refcount-1);
- gst_object_unref (GST_OBJECT (osched));
+ if (--chain->refcount == 0) {
+ destroy_chain (chain);
+ chain = NULL;
+ }
- g_free (chain);
+ return chain;
}
static GstOptSchedulerChain*
return chain;
}
-static GstOptSchedulerChain*
-ref_chain (GstOptSchedulerChain *chain)
+static void
+destroy_chain (GstOptSchedulerChain *chain)
{
- GST_LOG ("ref chain %p %d->%d", chain,
- chain->refcount, chain->refcount+1);
- chain->refcount++;
+ GstOptScheduler *osched;
+
+ GST_LOG ( "destroy chain %p", chain);
- return chain;
-}
+ g_assert (chain->num_groups == 0);
+ g_assert (chain->groups == NULL);
-static GstOptSchedulerChain*
-unref_chain (GstOptSchedulerChain *chain)
-{
- GST_LOG ("unref chain %p %d->%d", chain,
- chain->refcount, chain->refcount-1);
+ osched = chain->sched;
+ osched->chains = g_slist_remove (osched->chains, chain);
- if (--chain->refcount == 0) {
- destroy_chain (chain);
- chain = NULL;
- }
+ gst_object_unref (GST_OBJECT (osched));
- return chain;
+ g_free (chain);
}
static GstOptSchedulerChain*
add_to_chain (GstOptSchedulerChain *chain, GstOptSchedulerGroup *group)
{
- GST_LOG ( "adding group %p to chain %p", group, chain);
+ GST_LOG ("adding group %p to chain %p", group, chain);
g_assert (group->chain == NULL);
group = ref_group (group);
group->chain = ref_chain (chain);
- chain->groups = g_slist_prepend (chain->groups, group);
+
+ /* The first non-disabled group in the chain's group list will be the entry
+ point for the chain. Because buffers can accumulate in loop elements' peer
+ bufpens, we preferentially schedule loop groups before get groups to avoid
+ unnecessary execution of get-based groups when the bufpens are already
+ full. */
+ if (group->type == GST_OPT_SCHEDULER_GROUP_LOOP)
+ chain->groups = g_slist_prepend (chain->groups, group);
+ else
+ chain->groups = g_slist_append (chain->groups, group);
+
chain->num_groups++;
if (GST_OPT_SCHEDULER_GROUP_IS_ENABLED (group)) {
static GstOptSchedulerChain*
remove_from_chain (GstOptSchedulerChain *chain, GstOptSchedulerGroup *group)
{
- GST_LOG ( "removing group %p from chain %p", group, chain);
+ GST_LOG ("removing group %p from chain %p", group, chain);
if (!chain)
return NULL;
GST_LOG ("merging chain %p and %p", chain1, chain2);
+ /* FIXME: document how chain2 can be NULL */
if (chain1 == chain2 || chain2 == NULL)
return chain1;
- ref_chain (chain2);
+ /* switch if it's more efficient */
+ if (chain1->num_groups < chain2->num_groups) {
+ GstOptSchedulerChain *tmp = chain2;
+ chain2 = chain1;
+ chain1 = tmp;
+ }
+
walk = chain2->groups;
while (walk) {
GstOptSchedulerGroup *group = (GstOptSchedulerGroup *) walk->data;
GST_LOG ("reparenting group %p from chain %p to %p",
group, chain2, chain1);
- group->chain = NULL;
- chain2->num_groups--;
- chain2 = unref_chain (chain2);
+ ref_group (group);
+
+ remove_from_chain (chain2, group);
+ add_to_chain (chain1, group);
- group->chain = ref_chain (chain1);
- chain1->groups = g_slist_prepend (chain1->groups, group);
- chain1->num_groups++;
+ unref_group (group);
}
- g_slist_free (chain2->groups);
- chain2->groups = NULL;
- unref_chain (chain2);
+
+ /* chain2 is now freed, if nothing else was referencing it before */
return chain1;
}
static void
chain_group_set_enabled (GstOptSchedulerChain *chain, GstOptSchedulerGroup *group, gboolean enabled)
{
- g_assert (chain != NULL);
g_assert (group != NULL);
+ g_assert (chain != NULL);
GST_LOG ("request to %d group %p in chain %p, have %d groups enabled out of %d",
enabled, group, chain, chain->num_enabled, chain->num_groups);
GST_DEBUG ("enable group %p in chain %p, now %d groups enabled out of %d", group, chain,
chain->num_enabled, chain->num_groups);
+ /* OK to call even if the scheduler (cothread context / schedulerfunc) was
+ setup already -- will get destroyed when the group is destroyed */
+ setup_group_scheduler (chain->sched, group);
+
if (chain->num_enabled == chain->num_groups) {
GST_DEBUG ("enable chain %p", chain);
GST_OPT_SCHEDULER_CHAIN_ENABLE (chain);
return group;
}
-#ifndef USE_COTHREADS
-/* remove me
static GstOptSchedulerGroup*
-ref_group_by_count (GstOptSchedulerGroup *group, gint count)
+unref_group (GstOptSchedulerGroup *group)
{
- GST_LOG ("ref group %p %d->%d", group,
- group->refcount, group->refcount+count);
+ GST_LOG ("unref group %p %d->%d", group,
+ group->refcount, group->refcount-1);
- group->refcount += count;
+ if (--group->refcount == 0) {
+ destroy_group (group);
+ group = NULL;
+ }
return group;
}
-*/
-#endif
static GstOptSchedulerGroup*
-unref_group (GstOptSchedulerGroup *group)
+create_group (GstOptSchedulerChain *chain, GstElement *element,
+ GstOptSchedulerGroupType type)
{
- GST_LOG ("unref group %p %d->%d", group,
- group->refcount, group->refcount-1);
+ GstOptSchedulerGroup *group;
- if (--group->refcount == 1) {
- destroy_group (group);
- group = NULL;
- }
+ group = g_new0 (GstOptSchedulerGroup, 1);
+ GST_LOG ("new group %p", group);
+ group->refcount = 1; /* float... */
+ group->flags = GST_OPT_SCHEDULER_GROUP_DISABLED;
+ group->type = type;
+
+ add_to_group (group, element);
+ add_to_chain (chain, group);
+ group = unref_group (group); /* ...and sink. */
+ /* group's refcount is now 2 (one for the element, one for the chain) */
+
return group;
}
+static void
+destroy_group (GstOptSchedulerGroup *group)
+{
+ GST_LOG ("destroy group %p", group);
+
+ g_assert (group != NULL);
+ g_assert (group->elements == NULL);
+ g_assert (group->chain == NULL);
+
+ if (group->flags & GST_OPT_SCHEDULER_GROUP_SCHEDULABLE)
+ destroy_group_scheduler (group);
+
+ g_free (group);
+}
+
static GstOptSchedulerGroup*
add_to_group (GstOptSchedulerGroup *group, GstElement *element)
{
g_assert (GST_ELEMENT_SCHED_GROUP (element) == NULL);
+ /* Ref the group... */
GST_ELEMENT_SCHED_GROUP (element) = ref_group (group);
gst_object_ref (GST_OBJECT (element));
group_element_set_enabled (group, element, TRUE);
}
- /* Ref the group... */
- ref_group (group);
-
return group;
}
static GstOptSchedulerGroup*
-create_group (GstOptSchedulerChain *chain, GstElement *element)
-{
- GstOptSchedulerGroup *group;
-
- group = g_new0 (GstOptSchedulerGroup, 1);
- GST_LOG ("new group %p", group);
- group->refcount = 1;
- group->flags = GST_OPT_SCHEDULER_GROUP_DISABLED;
-
- add_to_group (group, element);
- add_to_chain (chain, group);
-
- return group;
-}
-
-static void
-destroy_group_scheduler (GstOptSchedulerGroup *group)
-{
- g_assert (group);
-
- if (group->flags & GST_OPT_SCHEDULER_GROUP_RUNNING)
- g_warning ("destroying running group scheduler");
-
-#ifdef USE_COTHREADS
- if (group->cothread) {
- do_cothread_destroy (group->cothread);
- group->cothread = NULL;
- }
-#else
- group->schedulefunc = NULL;
- group->argc = 0;
- group->argv = NULL;
-#endif
-
- group->flags &= ~GST_OPT_SCHEDULER_GROUP_SCHEDULABLE;
-}
-
-static void
-destroy_group (GstOptSchedulerGroup *group)
-{
- GST_LOG ("destroy group %p", group);
-
- g_assert (group != NULL);
- g_assert (group->elements == NULL);
-
- remove_from_chain (group->chain, group);
-
- if (group->flags & GST_OPT_SCHEDULER_GROUP_SCHEDULABLE)
- destroy_group_scheduler (group);
-
- g_free (group);
-}
-
-static GstOptSchedulerGroup*
remove_from_group (GstOptSchedulerGroup *group, GstElement *element)
{
GST_DEBUG ("removing element \"%s\" from group %p", GST_ELEMENT_NAME (element), group);
gst_object_unref (GST_OBJECT (element));
if (group->num_elements == 0) {
- group = unref_group (group);
+ GST_LOG ("group %p is now empty", group);
+ /* don't know in what case group->chain would be NULL, but putting this here
+ in deference to 0.8 -- remove me in 0.9 */
+ if (group->chain) {
+ GST_LOG ("removing group %p from its chain", group);
+ chain_group_set_enabled (group->chain, group, FALSE);
+ remove_from_chain (group->chain, group);
+ }
}
group = unref_group (group);
return group;
}
+/* FIXME need to check if the groups are of the same type -- otherwise need to
+ setup the scheduler again, if it is setup */
static GstOptSchedulerGroup*
merge_groups (GstOptSchedulerGroup *group1, GstOptSchedulerGroup *group2)
{
return group1;
}
+/* setup the scheduler context for a group. The right schedule function
+ * is selected based on the group type and cothreads are created if
+ * needed */
+static void
+setup_group_scheduler (GstOptScheduler *osched, GstOptSchedulerGroup *group)
+{
+ GroupScheduleFunction wrapper;
+
+ wrapper = unknown_group_schedule_function;
+
+ /* figure out the wrapper function for this group */
+ if (group->type == GST_OPT_SCHEDULER_GROUP_GET)
+ wrapper = get_group_schedule_function;
+ else if (group->type == GST_OPT_SCHEDULER_GROUP_LOOP)
+ wrapper = loop_group_schedule_function;
+
+#ifdef USE_COTHREADS
+ if (!(group->flags & GST_OPT_SCHEDULER_GROUP_SCHEDULABLE)) {
+ do_cothread_create (group->cothread, osched->context,
+ (cothread_func) wrapper, 0, (char **) group);
+ }
+ else {
+ do_cothread_setfunc (group->cothread, osched->context,
+ (cothread_func) wrapper, 0, (char **) group);
+ }
+#else
+ group->schedulefunc = wrapper;
+ group->argc = 0;
+ group->argv = (char **) group;
+#endif
+ group->flags |= GST_OPT_SCHEDULER_GROUP_SCHEDULABLE;
+}
+
+static void
+destroy_group_scheduler (GstOptSchedulerGroup *group)
+{
+ g_assert (group);
+
+ if (group->flags & GST_OPT_SCHEDULER_GROUP_RUNNING)
+ g_warning ("destroying running group scheduler");
+
+#ifdef USE_COTHREADS
+ if (group->cothread) {
+ do_cothread_destroy (group->cothread);
+ group->cothread = NULL;
+ }
+#else
+ group->schedulefunc = NULL;
+ group->argc = 0;
+ group->argv = NULL;
+#endif
+
+ group->flags &= ~GST_OPT_SCHEDULER_GROUP_SCHEDULABLE;
+}
+
static void
group_error_handler (GstOptSchedulerGroup *group)
{
GST_LOG ("request to %d element %s in group %p, have %d elements enabled out of %d",
enabled, GST_ELEMENT_NAME (element), group, group->num_enabled, group->num_elements);
+ /* Note that if an unlinked PLAYING element is added to a bin, we have to
+ create a new group to hold the element, and this function will be called
+ before the group is added to the chain. Thus we have a valid case for
+ group->chain==NULL. */
+
if (enabled) {
if (group->num_enabled < group->num_elements)
group->num_enabled++;
GST_ELEMENT_NAME (element), group, group->num_enabled, group->num_elements);
if (group->num_enabled == group->num_elements) {
- GST_LOG ("enable group %p", group);
- chain_group_set_enabled (group->chain, group, TRUE);
+ if (!group->chain) {
+ GST_DEBUG ("enable chainless group %p", group);
+ GST_OPT_SCHEDULER_GROUP_ENABLE (group);
+ } else {
+ GST_LOG ("enable group %p", group);
+ chain_group_set_enabled (group->chain, group, TRUE);
+ }
}
}
else {
GST_ELEMENT_NAME (element), group, group->num_enabled, group->num_elements);
if (group->num_enabled == 0) {
- GST_LOG ("disable group %p", group);
- chain_group_set_enabled (group->chain, group, FALSE);
+ if (!group->chain) {
+ GST_DEBUG ("disable chainless group %p", group);
+ GST_OPT_SCHEDULER_GROUP_DISABLE (group);
+ } else {
+ GST_LOG ("disable group %p", group);
+ chain_group_set_enabled (group->chain, group, FALSE);
+ }
}
}
}
static void
gst_opt_scheduler_schedule_run_queue (GstOptScheduler *osched)
{
- GST_LOG_OBJECT (osched, "entering scheduler run queue recursion %d %d",
- osched->recursion, g_list_length (osched->runqueue));
+ GST_LOG_OBJECT (osched, "running queue: %d groups, recursed %d times",
+ g_list_length (osched->runqueue),
+ osched->recursion, g_list_length (osched->runqueue));
+
+ /* note that we have a ref on each group on the queue (unref after running) */
/* make sure we don't exceed max_recursion */
if (osched->recursion > osched->max_recursion) {
group = (GstOptSchedulerGroup *) osched->runqueue->data;
- /* runqueue hols refcount to group */
+ /* runqueue holds refcount to group */
osched->runqueue = g_list_remove (osched->runqueue, group);
GST_LOG_OBJECT (osched, "scheduling group %p", group);
GstElement *entry = group->entry;
const GList *pads = gst_element_get_pad_list (entry);
- GST_LOG ("get wrapper of group %p", group);
+ GST_LOG ("executing get-based group %p", group);
group->flags |= GST_OPT_SCHEDULER_GROUP_RUNNING;
GstOptSchedulerGroup *group = (GstOptSchedulerGroup *) argv;
GstElement *entry = group->entry;
- GST_LOG ("loop wrapper of group %p", group);
+ GST_LOG ("executing loop-based group %p", group);
group->flags |= GST_OPT_SCHEDULER_GROUP_RUNNING;
{
GstOptSchedulerGroup *group;
GstOptScheduler *osched;
-
- GST_LOG ("loop wrapper, putting buffer in bufpen");
+ GstRealPad *peer;
group = GST_ELEMENT_SCHED_GROUP (GST_PAD_PARENT (sinkpad));
osched = group->chain->sched;
+ peer = GST_RPAD_PEER (sinkpad);
+ GST_LOG ("chain handler for loop-based pad %" GST_PTR_FORMAT, sinkpad);
#ifdef USE_COTHREADS
- if (GST_PAD_BUFLIST (GST_RPAD_PEER (sinkpad))) {
+ if (GST_PAD_BUFLIST (peer)) {
g_warning ("deadlock detected, disabling group %p", group);
group_error_handler (group);
}
else {
- GST_PAD_BUFLIST (GST_RPAD_PEER (sinkpad)) = g_list_append (GST_PAD_BUFLIST (GST_RPAD_PEER (sinkpad)), data);
+ GST_LOG ("queueing data %p on %s:%s's bufpen", data,
+ GST_DEBUG_PAD_NAME (peer));
+ GST_PAD_BUFLIST (peer) = g_list_append (GST_PAD_BUFLIST (peer), data);
schedule_group (group);
}
#else
- GST_PAD_BUFLIST (GST_RPAD_PEER (sinkpad)) = g_list_append (GST_PAD_BUFLIST (GST_RPAD_PEER (sinkpad)), data);
+ GST_LOG ("queueing data %p on %s:%s's bufpen", data,
+ GST_DEBUG_PAD_NAME (peer));
+ GST_PAD_BUFLIST (peer) = g_list_append (GST_PAD_BUFLIST (peer), data);
if (!(group->flags & GST_OPT_SCHEDULER_GROUP_RUNNING)) {
- GST_LOG ("adding %p to runqueue", group);
+ GST_LOG ("adding group %p to runqueue", group);
if (!g_list_find (osched->runqueue, group))
{
ref_group (group);
}
#endif
- GST_LOG ("after loop wrapper buflist %d",
- g_list_length (GST_PAD_BUFLIST (GST_RPAD_PEER (sinkpad))));
+ GST_LOG ("%d buffers left on %s:%s's bufpen after chain handler",
+ g_list_length (GST_PAD_BUFLIST (peer)));
}
/* this function is called by a loop based element that performs a
GstOptScheduler *osched;
gboolean disabled;
- GST_LOG ("get wrapper, removing buffer from bufpen");
+ GST_LOG ("get handler for %" GST_PTR_FORMAT, srcpad);
/* first try to grab a queued buffer */
if (GST_PAD_BUFLIST (srcpad)) {
data = GST_PAD_BUFLIST (srcpad)->data;
GST_PAD_BUFLIST (srcpad) = g_list_remove (GST_PAD_BUFLIST (srcpad), data);
- GST_LOG ("get wrapper, returning queued data %d",
- g_list_length (GST_PAD_BUFLIST (srcpad)));
+ GST_LOG ("returning popped queued data %p", data);
return data;
}
disabled = FALSE;
do {
+ GST_LOG ("scheduling upstream group %p to fill bufpen", group);
#ifdef USE_COTHREADS
schedule_group (group);
#else
osched->runqueue = g_list_append (osched->runqueue, group);
}
- GST_LOG_OBJECT (osched, "recursing into scheduler group %p", group);
+ GST_LOG ("recursing into scheduler group %p", group);
gst_opt_scheduler_schedule_run_queue (osched);
- GST_LOG_OBJECT (osched, "return from recurse group %p", group);
+ GST_LOG ("return from recurse group %p", group);
/* if the other group was disabled we might have to break out of the loop */
disabled = GST_OPT_SCHEDULER_GROUP_IS_DISABLED (group);
}
while (data == NULL);
- GST_LOG ("get wrapper, returning data %p, queue length %d",
+ GST_LOG ("get handler, returning data %p, queue length %d",
data, g_list_length (GST_PAD_BUFLIST (srcpad)));
return data;
return GST_RPAD_EVENTFUNC (srcpad) (srcpad, event);
}
-
-/* setup the scheduler context for a group. The right schedule function
- * is selected based on the group type and cothreads are created if
- * needed */
-static void
-setup_group_scheduler (GstOptScheduler *osched, GstOptSchedulerGroup *group)
-{
- GroupScheduleFunction wrapper;
-
- wrapper = unknown_group_schedule_function;
-
- /* figure out the wrapper function for this group */
- if (group->type == GST_OPT_SCHEDULER_GROUP_GET)
- wrapper = get_group_schedule_function;
- else if (group->type == GST_OPT_SCHEDULER_GROUP_LOOP)
- wrapper = loop_group_schedule_function;
-
-#ifdef USE_COTHREADS
- if (!(group->flags & GST_OPT_SCHEDULER_GROUP_SCHEDULABLE)) {
- do_cothread_create (group->cothread, osched->context,
- (cothread_func) wrapper, 0, (char **) group);
- }
- else {
- do_cothread_setfunc (group->cothread, osched->context,
- (cothread_func) wrapper, 0, (char **) group);
- }
-#else
- group->schedulefunc = wrapper;
- group->argc = 0;
- group->argv = (char **) group;
-#endif
- group->flags |= GST_OPT_SCHEDULER_GROUP_SCHEDULABLE;
-}
-
static GstElementStateReturn
gst_opt_scheduler_state_transition (GstScheduler *sched, GstElement *element, gint transition)
{
- GstOptScheduler *osched = GST_OPT_SCHEDULER (sched);
GstOptSchedulerGroup *group;
GstElementStateReturn res = GST_STATE_SUCCESS;
}
/* else construct the scheduling context of this group and enable it */
else {
- setup_group_scheduler (osched, group);
group_element_set_enabled (group, element, TRUE);
}
break;
* will also merge the chains.
*/
static GstOptSchedulerGroup*
-group_elements (GstOptScheduler *osched, GstElement *element1, GstElement *element2)
+group_elements (GstOptScheduler *osched, GstElement *element1, GstElement *element2,
+ GstOptSchedulerGroupType type)
{
GstOptSchedulerGroup *group1, *group2, *group = NULL;
GST_ELEMENT_NAME (element1), GST_ELEMENT_NAME (element2));
chain = create_chain (osched);
- group = create_group (chain, element1);
+ group = create_group (chain, element1, type);
add_to_group (group, element2);
}
/* the first element has a group */
chain = create_chain (osched);
- group = create_group (chain, element);
+ group = create_group (chain, element, GST_OPT_SCHEDULER_GROUP_LOOP);
group->entry = element;
- group->type = GST_OPT_SCHEDULER_GROUP_LOOP;
GST_LOG ("added element \"%s\" as loop based entry", GST_ELEMENT_NAME (element));
}
/* the two elements should be put into the same group,
* this also means that they are in the same chain automatically */
- group = group_elements (osched, element1, element2);
+ group = group_elements (osched, element1, element2,
+ GST_OPT_SCHEDULER_GROUP_GET);
/* if there is not yet an entry in the group, select the source
* element as the entry point */
if (!group->entry) {
group->entry = element1;
- group->type = GST_OPT_SCHEDULER_GROUP_GET;
GST_DEBUG ("setting \"%s\" as entry point of _get-based group %p",
GST_ELEMENT_NAME (element1), group);
* this also means that they are in the same chain automatically,
* in case of a loop-based element1, there will be a group for element1 and
* element2 will be added to it. */
- group_elements (osched, element1, element2);
+ group_elements (osched, element1, element2, GST_OPT_SCHEDULER_GROUP_LOOP);
break;
case GST_OPT_GET_TO_LOOP:
GST_LOG ("get to loop based link");
* this also means that they are in the same chain automatically,
* element2 is loop-based so it already has a group where element1
* will be added to */
- group_elements (osched, element1, element2);
+ group_elements (osched, element1, element2, GST_OPT_SCHEDULER_GROUP_LOOP);
break;
case GST_OPT_CHAIN_TO_LOOP:
case GST_OPT_LOOP_TO_LOOP:
/* create a new group for element1 as it cannot be merged into another group
* here. we create the group in the same chain as the loop-based element. */
GST_DEBUG ("creating new group for element %s", GST_ELEMENT_NAME (element1));
- group1 = create_group (group2->chain, element1);
+ group1 = create_group (group2->chain, element1,
+ GST_OPT_SCHEDULER_GROUP_LOOP);
}
else {
/* both elements are already in a group, make sure they are added to
osched->state = GST_OPT_SCHEDULER_STATE_RUNNING;
- GST_DEBUG ("iterating scheduler %p", sched);
+ GST_DEBUG_OBJECT (sched, "iterating");
while (iterations) {
gboolean scheduled = FALSE;
ref_chain (chain);
/* if the chain is not disabled, schedule it */
if (!GST_OPT_SCHEDULER_CHAIN_IS_DISABLED (chain)) {
+ GST_LOG ("scheduling chain %p", chain);
schedule_chain (chain);
scheduled = TRUE;
}
osched->state = GST_OPT_SCHEDULER_STATE_RUNNING;
}
- GST_LOG_OBJECT (sched, "iterate scheduled %p", chain);
-
chains = g_slist_next (chains);
unref_chain (chain);
}