From 10e648c7d982360441a61d4c1dac5e049e88020f Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 26 Jan 2005 10:56:09 +0000 Subject: [PATCH] Removed schedulers and compat stuff. Original commit message from CVS: Removed schedulers and compat stuff. Use iterators in bin for the various lookup functions. Optimized the clock a bit. Added some more docs. Fixed scheduling in the ()-(l) case. Fixed and added some testcases. Removed old code. --- ChangeLog | 88 + docs/design/part-scheduling.txt | 25 +- gst/elements/gstfakesrc.c | 42 - gst/elements/gstidentity.c | 5 +- gst/gst.c | 27 - gst/gst.h | 6 - gst/gstbin.c | 202 +- gst/gstbin.h | 3 + gst/gstbus.c | 2 + gst/gstclock.c | 41 +- gst/gstclock.h | 2 + gst/gstcompat.h | 53 +- gst/gstelement.c | 34 +- gst/gstformat.c | 2 +- gst/gstiterator.c | 86 +- gst/gstiterator.h | 18 +- gst/gstobject.c | 1 - gst/gstpad.c | 6 +- gst/gstpipeline.c | 4 - gst/gstquery.c | 2 +- gst/gstscheduler.c | 92 - gst/gstscheduler.h | 16 - gst/gstsystemclock.c | 42 +- gst/gsttask.h | 2 +- gst/gsttypes.h | 1 - gst/schedulers/cothreads_compat.h | 79 - gst/schedulers/entryscheduler.c | 1175 ---------- gst/schedulers/faircothreads.c | 612 ------ gst/schedulers/faircothreads.h | 163 -- gst/schedulers/fairscheduler.c | 1405 ------------ gst/schedulers/gstbasicscheduler.c | 1514 ------------- gst/schedulers/gstoptimalscheduler.c | 2757 ------------------------ gst/schedulers/gthread-cothreads.h | 221 -- gst/schedulers/threadscheduler.c | 34 - plugins/elements/gstfakesrc.c | 42 - plugins/elements/gstidentity.c | 5 +- tests/old/testsuite/bins/interface.c | 41 +- tests/old/testsuite/dlopen/loadgst.c | 6 +- tests/old/testsuite/states/.gitignore | 1 + tests/old/testsuite/states/Makefile.am | 2 +- tests/old/testsuite/states/stress.c | 97 + testsuite/bins/interface.c | 41 +- testsuite/dlopen/loadgst.c | 6 +- testsuite/states/.gitignore | 1 + testsuite/states/Makefile.am | 2 +- testsuite/states/stress.c | 97 + 46 files changed, 689 insertions(+), 8414 deletions(-) delete mode 100644 gst/schedulers/cothreads_compat.h delete mode 100644 gst/schedulers/entryscheduler.c delete mode 100644 gst/schedulers/faircothreads.c delete mode 100644 gst/schedulers/faircothreads.h delete mode 100644 gst/schedulers/fairscheduler.c delete mode 100644 gst/schedulers/gstbasicscheduler.c delete mode 100644 gst/schedulers/gstoptimalscheduler.c delete mode 100644 gst/schedulers/gthread-cothreads.h create mode 100644 tests/old/testsuite/states/stress.c create mode 100644 testsuite/states/stress.c diff --git a/ChangeLog b/ChangeLog index a4646e4b76..bb4e725f8b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,91 @@ +2005-01-26 Wim Taymans + + * docs/design/part-scheduling.txt: + * gst/elements/gstfakesrc.c: (gst_fakesrc_activate): + * gst/elements/gstidentity.c: (gst_identity_chain): + * gst/gst.c: + * gst/gst.h: + * gst/gstbin.c: (gst_bin_set_index), (gst_bin_set_clock), + (gst_bin_set_bus), (gst_bin_set_scheduler), (gst_bin_add_func), + (iterate_child), (gst_bin_iterate_elements), + (iterate_child_recurse), (gst_bin_iterate_recurse), + (gst_bin_iterate_recurse_up), (bin_element_is_sink), + (gst_bin_get_state), (gst_bin_change_state), (gst_bin_dispose), + (compare_name), (gst_bin_get_by_name), + (gst_bin_get_by_name_recurse_up), (compare_interface), + (gst_bin_get_by_interface), (gst_bin_iterate_all_by_interface): + * gst/gstbin.h: + * gst/gstbus.c: (gst_bus_post): + * gst/gstclock.c: (gst_clock_id_ref), (gst_clock_id_unref), + (gst_clock_id_compare_func), (gst_clock_id_wait), + (gst_clock_id_wait_async), (gst_clock_init), + (gst_clock_adjust_unlocked), (gst_clock_get_time): + * gst/gstclock.h: + * gst/gstcompat.h: + * gst/gstelement.c: (gst_element_add_pad), + (gst_element_remove_pad), (iterate_pad), + (gst_element_iterate_pads), (gst_element_get_state_func), + (gst_element_set_state), (gst_element_pads_activate), + (gst_element_change_state), (gst_element_dispose): + * gst/gstformat.c: (gst_format_iterate_definitions): + * gst/gstiterator.c: (gst_iterator_init), (gst_iterator_new), + (gst_list_iterator_next), (gst_list_iterator_free), + (gst_iterator_new_list), (gst_iterator_pop), (gst_iterator_next), + (gst_iterator_resync), (gst_iterator_free), (gst_iterator_push), + (gst_iterator_filter), (gst_iterator_foreach), + (gst_iterator_find_custom): + * gst/gstiterator.h: + * gst/gstobject.c: (gst_object_class_init), (gst_object_ref), + (gst_object_unref), (gst_object_sink), (gst_object_dispose), + (gst_object_dispatch_properties_changed), (gst_object_set_name), + (gst_object_set_parent), (gst_object_unparent), + (gst_object_check_uniqueness), (gst_object_get_path_string): + * gst/gstpad.c: (gst_pad_set_active), (gst_pad_is_active), + (gst_pad_set_blocked_async), (gst_pad_set_acceptcaps_function), + (gst_pad_set_fixatecaps_function), (gst_pad_unlink), + (gst_pad_link_prepare_filtered), (gst_pad_link_filtered), + (gst_pad_relink_filtered), (gst_real_pad_get_caps_unlocked), + (gst_pad_peer_get_caps), (gst_pad_fixate_caps), + (gst_pad_accept_caps), (gst_pad_peer_accept_caps), + (gst_pad_set_caps), (gst_pad_configure_sink), + (gst_pad_configure_src), (gst_pad_realize), (gst_pad_alloc_buffer), + (gst_pad_push), (gst_pad_pull_range), (gst_pad_push_event), + (gst_pad_send_event): + * gst/gstpipeline.c: (is_eos), (pipeline_bus_handler), + (gst_pipeline_change_state): + * gst/gstquery.c: (gst_query_type_iterate_definitions): + * gst/gstscheduler.c: + * gst/gstscheduler.h: + * gst/gstsystemclock.c: (gst_system_clock_init), + (gst_system_clock_async_thread), + (gst_system_clock_id_wait_unlocked), (gst_system_clock_id_wait), + (gst_system_clock_id_wait_async), (gst_system_clock_id_unschedule): + * gst/gsttask.h: + * gst/gsttypes.h: + * gst/schedulers/cothreads_compat.h: + * gst/schedulers/entryscheduler.c: + * gst/schedulers/faircothreads.c: + * gst/schedulers/faircothreads.h: + * gst/schedulers/fairscheduler.c: + * gst/schedulers/gstbasicscheduler.c: + * gst/schedulers/gstoptimalscheduler.c: + * gst/schedulers/gthread-cothreads.h: + * gst/schedulers/threadscheduler.c: + (gst_thread_scheduler_class_init), (gst_thread_scheduler_func): + * testsuite/bins/interface.c: (main): + * testsuite/dlopen/loadgst.c: (do_test): + * testsuite/states/.cvsignore: + * testsuite/states/Makefile.am: + * testsuite/states/stress.c: (message_received), (state_change), + (main): + Removed schedulers and compat stuff. + Use iterators in bin for the various lookup functions. + Optimized the clock a bit. + Added some more docs. + Fixed scheduling in the ()-(l) case. + Fixed and added some testcases. + Removed old code. + 2005-01-19 Wim Taymans * gst/elements/gstfilesrc.c: (gst_filesrc_getrange), diff --git a/docs/design/part-scheduling.txt b/docs/design/part-scheduling.txt index 6608d092bb..386cf54a03 100644 --- a/docs/design/part-scheduling.txt +++ b/docs/design/part-scheduling.txt @@ -67,7 +67,8 @@ Multi-sink elements ---------- If the chain based sink wants to wait for one of the pads to receive a buffer, just - implement the action to perform in the chain function. + implement the action to perform in the chain function. Be aware that the action could + be performed in different threads and possibly simultaneously so grab the STREAM_LOCK. Collect pads ------------ @@ -176,3 +177,25 @@ Cases | src--sink src--sink src--sink | +---------+ +----------+ +------------+ +----------+ (l-g) (c-l) (g) (l) () (c) + + * fakesink has a chain function and the peer pad has no + loop function, no scheduling is done. + * avidemuxer and identity expose an (g) - (l) connection, + a thread is created to call the sinkpad loop function. + * identity knows the srcpad is getrange based and uses the + thread from avidemux to getrange data from filesrc. + + +---------+ +----------+ +------------+ +----------+ + | filesrc | | identity | | oggdemuxer | | fakesink | + | src--sink src--sink src--sink | + +---------+ +----------+ +------------+ +----------+ + (l-g) (c) () (l-c) () (c) + + * fakesink has a chain function and the peer pad has no + loop function, no scheduling is done. + * oggdemuxer and identity expose an () - (l-c) connection, + oggdemux has to operate in chain mode. + * identity chan only work chain based and so filesrc creates + a thread to push data to identity. + + diff --git a/gst/elements/gstfakesrc.c b/gst/elements/gstfakesrc.c index f5c1dce12c..15fbcf855d 100644 --- a/gst/elements/gstfakesrc.c +++ b/gst/elements/gstfakesrc.c @@ -861,48 +861,6 @@ done: GST_STREAM_UNLOCK (pad); } -#if 0 -/** - * gst_fakesrc_loop: - * @element: the faksesrc to loop - * - * generate an empty buffer and push it to the next element. - */ -static gboolean -gst_fakesrc_loop (GstPad * pad) -{ - GstFakeSrc *src; - const GList *pads; - GstTask *task; - - src = GST_FAKESRC (GST_PAD_PARENT (pad)); - task = src->task; - - pads = GST_ELEMENT (src)->pads; - - while (pads) { - GstPad *pad = GST_PAD (pads->data); - GstBuffer *buffer; - GstFlowReturn ret; - - ret = gst_fakesrc_get (pad, &buffer); - if (ret != GST_FLOW_OK) { - return FALSE; - } - ret = gst_pad_push (pad, buffer); - if (ret != GST_FLOW_OK) { - return FALSE; - } - - if (src->eos) { - return FALSE; - } - pads = g_list_next (pads); - } - return TRUE; -} -#endif - static gboolean gst_fakesrc_activate (GstPad * pad, GstActivateMode mode) { diff --git a/gst/elements/gstidentity.c b/gst/elements/gstidentity.c index 12948950a8..ae135b84ae 100644 --- a/gst/elements/gstidentity.c +++ b/gst/elements/gstidentity.c @@ -259,6 +259,7 @@ gst_identity_chain (GstPad * pad, GstBuffer * buffer) { GstBuffer *buf = GST_BUFFER (buffer); GstIdentity *identity; + GstFlowReturn result = GST_FLOW_OK; guint i; g_return_val_if_fail (pad != NULL, GST_FLOW_ERROR); @@ -365,12 +366,12 @@ gst_identity_chain (GstPad * pad, GstBuffer * buffer) } identity->bytes_handled += GST_BUFFER_SIZE (buf); - gst_pad_push (identity->srcpad, buf); + result = gst_pad_push (identity->srcpad, buf); if (identity->sleep_time) g_usleep (identity->sleep_time); } - return GST_FLOW_OK; + return result; } #if 0 diff --git a/gst/gst.c b/gst/gst.c index d8a4c0cd2c..3aa7085cb5 100644 --- a/gst/gst.c +++ b/gst/gst.c @@ -813,33 +813,6 @@ init_popt_callback (poptContext context, enum poptCallbackReason reason, } } -/** - * gst_use_threads: - * @use_threads: a #gboolean indicating whether threads should be used - * - * This function is deprecated and should not be used in new code. - * GStreamer requires threads to be enabled at all times. - */ -void -gst_use_threads (gboolean use_threads) -{ -} - -/** - * gst_has_threads: - * - * This function is deprecated and should not be used in new code. - * - * Queries if GStreamer has threads enabled. - * - * Returns: %TRUE if threads are enabled. - */ -gboolean -gst_has_threads (void) -{ - return TRUE; -} - /** * gst_version: * @major: pointer to a guint to store the major version number diff --git a/gst/gst.h b/gst/gst.h index 11b2ba5972..ae2ca5c924 100644 --- a/gst/gst.h +++ b/gst/gst.h @@ -97,12 +97,6 @@ gboolean gst_init_check_with_popt_table (int *argc, char **argv[], const GstPoptOption * gst_init_get_popt_table (void); -void gst_use_threads (gboolean use_threads); -gboolean gst_has_threads (void); - -void gst_main (void); -void gst_main_quit (void); - G_END_DECLS #endif /* __GST_H__ */ diff --git a/gst/gstbin.c b/gst/gstbin.c index cace33a425..7aa25dcfc9 100644 --- a/gst/gstbin.c +++ b/gst/gstbin.c @@ -521,6 +521,13 @@ no_function: return FALSE; } +static GstIteratorItem +iterate_child (GstIterator * it, GstElement * child) +{ + gst_object_ref (GST_OBJECT (child)); + return GST_ITERATOR_ITEM_PASS; +} + /** * gst_bin_iterate_elements: * @bin: #Gstbin to iterate the elements of @@ -542,20 +549,76 @@ gst_bin_iterate_elements (GstBin * bin) g_return_val_if_fail (GST_IS_BIN (bin), NULL); GST_LOCK (bin); - /* add ref because the iterator refs the bin */ + /* add ref because the iterator refs the bin. When the iterator + * is freed it will unref the bin again using the provided dispose + * function. */ gst_object_ref (GST_OBJECT (bin)); result = gst_iterator_new_list (GST_GET_LOCK (bin), &bin->children_cookie, &bin->children, bin, - (GstIteratorRefFunction) gst_object_ref, - (GstIteratorUnrefFunction) gst_object_unref, + (GstIteratorItemFunction) iterate_child, (GstIteratorDisposeFunction) gst_object_unref); GST_UNLOCK (bin); return result; } +static GstIteratorItem +iterate_child_recurse (GstIterator * it, GstElement * child) +{ + gst_object_ref (GST_OBJECT (child)); + if (GST_IS_BIN (child)) { + GstIterator *other = gst_bin_iterate_recurse (GST_BIN (child)); + + gst_iterator_push (it, other); + } + return GST_ITERATOR_ITEM_PASS; +} + +/** + * gst_bin_iterate_recurse: + * @bin: #Gstbin to iterate the elements of + * + * Get an iterator for the elements in this bin. + * Each element will have its refcount increased, so unref + * after usage. This iterator recurses into GstBin children. + * + * Returns: a #GstIterator of #GstElements. gst_iterator_free after + * use. returns NULL when passing bad parameters. + * + * MT safe. + */ +GstIterator * +gst_bin_iterate_recurse (GstBin * bin) +{ + GstIterator *result; + + g_return_val_if_fail (GST_IS_BIN (bin), NULL); + + GST_LOCK (bin); + /* add ref because the iterator refs the bin. When the iterator + * is freed it will unref the bin again using the provided dispose + * function. */ + gst_object_ref (GST_OBJECT (bin)); + result = gst_iterator_new_list (GST_GET_LOCK (bin), + &bin->children_cookie, + &bin->children, + bin, + (GstIteratorItemFunction) iterate_child_recurse, + (GstIteratorDisposeFunction) gst_object_unref); + GST_UNLOCK (bin); + + return result; + return NULL; +} + +GstIterator * +gst_bin_iterate_recurse_up (GstBin * bin) +{ + return NULL; +} + /* returns 0 if the element is a sink, this is made so that * we can use this function as a filter * @@ -937,7 +1000,6 @@ gst_bin_dispose (GObject * object) /* ref to not hit 0 again */ gst_object_ref (GST_OBJECT (object)); - gst_element_set_state (GST_ELEMENT (object), GST_STATE_NULL); while (bin->children) { gst_bin_remove (bin, GST_ELEMENT (bin->children->data)); @@ -949,6 +1011,21 @@ gst_bin_dispose (GObject * object) G_OBJECT_CLASS (parent_class)->dispose (object); } +static gint +compare_name (GstElement * element, const gchar * name) +{ + gint eq; + + GST_LOCK (element); + eq = strcmp (GST_ELEMENT_NAME (element), name) == 0; + GST_UNLOCK (element); + + if (eq != 0) { + gst_object_unref (GST_OBJECT (element)); + } + return eq; +} + /** * gst_bin_get_by_name: * @bin: #Gstbin to search @@ -958,46 +1035,24 @@ gst_bin_dispose (GObject * object) * function recurses into subbins. * * Returns: the element with the given name. Returns NULL if the - * element is not found or when bad parameters were given. + * element is not found or when bad parameters were given. Unref after + * usage. * * MT safe. */ GstElement * gst_bin_get_by_name (GstBin * bin, const gchar * name) { - GList *children; - GstElement *result = NULL; + GstIterator *children; + GstIterator *result; g_return_val_if_fail (GST_IS_BIN (bin), NULL); - g_return_val_if_fail (name != NULL, NULL); - GST_CAT_INFO (GST_CAT_PARENTAGE, "[%s]: looking up child element %s", - GST_ELEMENT_NAME (bin), name); + children = gst_bin_iterate_recurse (bin); + result = gst_iterator_find_custom (children, (gpointer) name, + (GCompareFunc) compare_name); - /* we recursively lock all elements, this might be a bit too much.. */ - GST_LOCK (bin); - for (children = bin->children; children; children = g_list_next (children)) { - GstElement *child = GST_ELEMENT_CAST (children->data); - gboolean eq; - - GST_LOCK (child); - eq = strcmp (GST_ELEMENT_NAME (child), name) == 0; - GST_UNLOCK (child); - - if (eq) { - result = child; - break; - } - if (GST_IS_BIN (child)) { - result = gst_bin_get_by_name (GST_BIN (child), name); - if (result) { - break; - } - } - } - GST_UNLOCK (bin); - - return result; + return GST_ELEMENT_CAST (result); } /** @@ -1009,7 +1064,7 @@ gst_bin_get_by_name (GstBin * bin, const gchar * name) * element is not found, a recursion is performed on the parent bin. * * Returns: the element with the given name or NULL when the element - * was not found or bad parameters were given. + * was not found or bad parameters were given. Unref after usage. * * MT safe. */ @@ -1037,6 +1092,23 @@ gst_bin_get_by_name_recurse_up (GstBin * bin, const gchar * name) return result; } + +static gint +compare_interface (GstElement * element, gpointer interface) +{ + gint ret; + + if (G_TYPE_CHECK_INSTANCE_TYPE (element, GPOINTER_TO_INT (interface))) { + ret = 0; + } else { + /* we did not find the element, need to release the ref + * added by the iterator */ + gst_object_unref (GST_OBJECT (element)); + ret = 1; + } + return ret; +} + /** * gst_bin_get_by_interface: * @bin: bin to find element in @@ -1046,36 +1118,26 @@ gst_bin_get_by_name_recurse_up (GstBin * bin, const gchar * name) * interface. If such an element is found, it returns the element. You can * cast this element to the given interface afterwards. * If you want all elements that implement the interface, use - * gst_bin_get_all_by_interface(). The function recurses bins inside bins. + * gst_bin_iterate_all_by_interface(). The function recurses bins inside bins. * - * Returns: An element inside the bin implementing the interface. + * Returns: An element inside the bin implementing the interface. Unref after + * usage. * * MT safe. */ GstElement * gst_bin_get_by_interface (GstBin * bin, GType interface) { - GList *walk; - GstElement *result = NULL; + GstIterator *children; + GstIterator *result; g_return_val_if_fail (GST_IS_BIN (bin), NULL); - g_return_val_if_fail (G_TYPE_IS_INTERFACE (interface), NULL); - GST_LOCK (bin); - for (walk = bin->children; walk; walk = g_list_next (walk)) { - if (G_TYPE_CHECK_INSTANCE_TYPE (walk->data, interface)) { - result = GST_ELEMENT_CAST (walk->data); - break; - } - if (GST_IS_BIN (walk->data)) { - result = gst_bin_get_by_interface (GST_BIN (walk->data), interface); - if (result) - break; - } - } - GST_UNLOCK (bin); + children = gst_bin_iterate_recurse (bin); + result = gst_iterator_find_custom (children, GINT_TO_POINTER (interface), + (GCompareFunc) compare_interface); - return result; + return GST_ELEMENT_CAST (result); } /** @@ -1085,35 +1147,25 @@ gst_bin_get_by_interface (GstBin * bin, GType interface) * * Looks for all elements inside the bin that implements the given * interface. You can safely cast all returned elements to the given interface. - * The function recurses bins inside bins. You need to free the list using - * g_list_free() after use. + * The function recurses bins inside bins. The iterator will return a series + * of #GstElement that should be unreffed after usage. * - * Returns: The elements inside the bin implementing the interface. + * Returns: An iterator for the elements inside the bin implementing the interface. * - * MT safe. */ -GList * -gst_bin_get_all_by_interface (GstBin * bin, GType interface) +GstIterator * +gst_bin_iterate_all_by_interface (GstBin * bin, GType interface) { - GList *walk; - GList *ret = NULL; + GstIterator *children; + GstIterator *result; g_return_val_if_fail (GST_IS_BIN (bin), NULL); - g_return_val_if_fail (G_TYPE_IS_INTERFACE (interface), NULL); - GST_LOCK (bin); - for (walk = bin->children; walk; walk = g_list_next (walk)) { - if (G_TYPE_CHECK_INSTANCE_TYPE (walk->data, interface)) { - ret = g_list_prepend (ret, walk->data); - } - if (GST_IS_BIN (walk->data)) { - ret = g_list_concat (ret, - gst_bin_get_all_by_interface (GST_BIN (walk->data), interface)); - } - } - GST_UNLOCK (bin); + children = gst_bin_iterate_recurse (bin); + result = gst_iterator_filter (children, GINT_TO_POINTER (interface), + (GCompareFunc) compare_interface); - return ret; + return result; } #ifndef GST_DISABLE_LOADSAVE diff --git a/gst/gstbin.h b/gst/gstbin.h index 3cf23faf2f..ded86d3e22 100644 --- a/gst/gstbin.h +++ b/gst/gstbin.h @@ -98,6 +98,9 @@ GstElement* gst_bin_get_by_interface (GstBin *bin, GType interface); /* retrieve multiple children */ GstIterator* gst_bin_iterate_elements (GstBin *bin); +GstIterator* gst_bin_iterate_recurse (GstBin *bin); +GstIterator* gst_bin_iterate_recurse_up (GstBin *bin); + GstIterator* gst_bin_iterate_sinks (GstBin *bin); GstIterator* gst_bin_iterate_all_by_interface (GstBin *bin, GType interface); diff --git a/gst/gstbus.c b/gst/gstbus.c index 873ead06dc..2c0d4cc782 100644 --- a/gst/gstbus.c +++ b/gst/gstbus.c @@ -195,6 +195,8 @@ gst_bus_post (GstBus * bus, GstMessage * message) g_return_val_if_fail (GST_IS_BUS (bus), FALSE); g_return_val_if_fail (GST_IS_MESSAGE (message), FALSE); + GST_DEBUG_OBJECT (bus, "posting message on bus"); + GST_LOCK (bus); handler = bus->sync_handler; handler_data = bus->sync_handler_data; diff --git a/gst/gstclock.c b/gst/gstclock.c index 80a06f0fc8..76113601d2 100644 --- a/gst/gstclock.c +++ b/gst/gstclock.c @@ -508,6 +508,34 @@ gst_clock_get_resolution (GstClock * clock) return G_GINT64_CONSTANT (1); } +/** + * gst_clock_adjust_unlocked + * @clock: a #GstClock to use + * @internal: a clock time + * + * Converts the given @internal clock time to the real time, adjusting + * and making sure that the returned time is increasing. + * This function should be called with the clock lock held. + * + * Returns: the converted time of the clock. + * + * MT safe. + */ +GstClockTime +gst_clock_adjust_unlocked (GstClock * clock, GstClockTime internal) +{ + GstClockTime ret; + + ret = internal + clock->adjust; + /* make sure the time is increasing, else return last_time */ + if ((gint64) ret < (gint64) clock->last_time) { + ret = clock->last_time; + } else { + clock->last_time = ret; + } + return ret; +} + /** * gst_clock_get_time * @clock: a #GstClock to query @@ -535,17 +563,16 @@ gst_clock_get_time (GstClock * clock) } else { ret = G_GINT64_CONSTANT (0); } + GST_CAT_DEBUG (GST_CAT_CLOCK, "internal time %" GST_TIME_FORMAT, + GST_TIME_ARGS (ret)); - /* make sure the time is increasing, else return last_time */ GST_LOCK (clock); - ret += clock->adjust; - if ((gint64) ret < (gint64) clock->last_time) { - ret = clock->last_time; - } else { - clock->last_time = ret; - } + ret = gst_clock_adjust_unlocked (clock, ret); GST_UNLOCK (clock); + GST_CAT_DEBUG (GST_CAT_CLOCK, "adjusted time %" GST_TIME_FORMAT, + GST_TIME_ARGS (ret)); + return ret; } diff --git a/gst/gstclock.h b/gst/gstclock.h index 29c125b823..d8e46d86bd 100644 --- a/gst/gstclock.h +++ b/gst/gstclock.h @@ -179,6 +179,8 @@ guint64 gst_clock_get_resolution (GstClock *clock); GstClockTime gst_clock_get_time (GstClock *clock); void gst_clock_set_time_adjust (GstClock *clock, GstClockTime adjust); +GstClockTime gst_clock_adjust_unlocked (GstClock *clock, GstClockTime internal); + /* creating IDs that can be used to get notifications */ GstClockID gst_clock_new_single_shot_id (GstClock *clock, diff --git a/gst/gstcompat.h b/gst/gstcompat.h index dd47c17a51..513f4398f8 100644 --- a/gst/gstcompat.h +++ b/gst/gstcompat.h @@ -1,6 +1,6 @@ /* GStreamer * Copyright (C) 1999,2000 Erik Walthinsen - * 2000 Wim Taymans + * 2004 Wim Taymans * * gstcompat.h: backwards compatibility stuff * @@ -28,57 +28,6 @@ G_BEGIN_DECLS #ifndef GST_DISABLE_DEPRECATED -/* 0.5.2 changes; remove these ASAP */ - -/* element functions */ -#define gst_element_connect(a,b) gst_element_link(a,b) -#define gst_element_connect_pads(a,b,c,d) \ - gst_element_link_pads(a,b,c,d) -#ifdef G_HAVE_ISO_VARARGS -#define gst_element_connect_many(a,...) gst_element_link_many(a,__VA_ARGS__) -#elif defined(G_HAVE_GNUC_VARARGS) -#define gst_element_connect_many(a,args...) \ - gst_element_link_many(a, ## args) -#else -/* FIXME: need an inline function */ -#endif -#define gst_element_connect_filtered(a,b,c) \ - gst_element_link_filtered(a,b,c) -#define gst_element_disconnect(a,b) gst_element_unlink(a,b) - -/* pad functions */ -#define gst_pad_connect(a,b) gst_pad_link(a,b) -#define gst_pad_connect_filtered(a,b,c) gst_pad_link_filtered(a,b,c) -#define gst_pad_disconnect(a,b) gst_pad_unlink(a,b) -#define gst_pad_proxy_connect(a,b) gst_pad_proxy_link(a,b) -#define gst_pad_set_connect_function(a,b) \ - gst_pad_set_link_function(a,b) - -/* pad macros */ -#define GST_PAD_IS_CONNECTED(a) GST_PAD_IS_LINKED(a) - -/* pad enums */ -#define GST_PAD_CONNECT_REFUSED GST_PAD_LINK_REFUSED -#define GST_PAD_CONNECT_DELAYED GST_PAD_LINK_DELAYED -#define GST_PAD_CONNECT_OK GST_PAD_LINK_OK -#define GST_PAD_CONNECT_DONE GST_PAD_LINK_DONE -typedef GstPadLinkReturn GstPadConnectReturn; - -/* pad function types */ -typedef GstPadLinkFunction GstPadConnectFunction; - -/* probably not used */ -/* - * GST_RPAD_LINKFUNC - */ - -/* 0.8.1.1 removal; remove completely in 0.9 */ -/* information messages */ -# ifdef G_HAVE_ISO_VARARGS -#define gst_info(...) GST_INFO(__VA_ARGS__) -# elif defined(G_HAVE_GNUC_VARARGS) -#define gst_info(format,args...) GST_INFO(format,##args) -# endif #endif /* not GST_DISABLE_DEPRECATED */ diff --git a/gst/gstelement.c b/gst/gstelement.c index 05ef242d01..3b381b03ef 100644 --- a/gst/gstelement.c +++ b/gst/gstelement.c @@ -830,6 +830,13 @@ gst_element_get_pad (GstElement * element, const gchar * name) return pad; } +GstIteratorItem +iterate_pad (GstIterator * it, GstPad * pad) +{ + gst_object_ref (GST_OBJECT_CAST (pad)); + return GST_ITERATOR_ITEM_PASS; +} + /** * gst_element_iterate_pads: * @element: a #GstElement to iterate pads of. @@ -853,8 +860,7 @@ gst_element_iterate_pads (GstElement * element) &element->pads_cookie, &element->pads, element, - (GstIteratorRefFunction) gst_object_ref, - (GstIteratorUnrefFunction) gst_object_unref, + (GstIteratorItemFunction) iterate_pad, (GstIteratorDisposeFunction) gst_object_unref); GST_UNLOCK (element); @@ -1571,6 +1577,8 @@ gst_element_get_state_func (GstElement * element, g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); + GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "getting state"); + GST_STATE_LOCK (element); /* we got an error, report immediatly */ if (GST_STATE_ERROR (element)) @@ -1578,21 +1586,26 @@ gst_element_get_state_func (GstElement * element, old_pending = GST_STATE_PENDING (element); if (old_pending != GST_STATE_VOID_PENDING) { + GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "wait for pending"); /* we have a pending state change, wait for it to complete */ if (!GST_STATE_TIMED_WAIT (element, timeout)) { + GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "timeout"); /* timeout triggered */ ret = GST_STATE_ASYNC; } else { /* could be success or failure */ if (old_pending == GST_STATE (element)) { + GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "got success"); ret = GST_STATE_SUCCESS; } else { + GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "got failure"); ret = GST_STATE_FAILURE; } } } /* if nothing is pending anymore we can return SUCCESS */ if (GST_STATE_PENDING (element) == GST_STATE_VOID_PENDING) { + GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "nothing pending"); ret = GST_STATE_SUCCESS; } @@ -1602,6 +1615,11 @@ done: if (pending) *pending = GST_STATE_PENDING (element); + GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, + "state current: %s, pending: %s", + gst_element_state_get_name (GST_STATE (element)), + gst_element_state_get_name (GST_STATE_PENDING (element))); + GST_STATE_UNLOCK (element); return ret; @@ -1877,12 +1895,13 @@ restart: /* we only care about real pads */ if (GST_IS_REAL_PAD (pad)) { GstRealPad *peer; - gboolean pad_loop; + gboolean pad_loop, pad_get; gboolean delay = FALSE; /* see if the pad has a loop function and grab * the peer */ GST_LOCK (pad); + pad_get = GST_RPAD_GETRANGEFUNC (pad) != NULL; pad_loop = GST_RPAD_LOOPFUNC (pad) != NULL; peer = GST_RPAD_PEER (pad); if (peer) @@ -1890,18 +1909,20 @@ restart: GST_UNLOCK (pad); if (peer) { - gboolean peer_loop; + gboolean peer_loop, peer_get; + /* see if the peer has a getrange function */ + peer_get = GST_RPAD_GETRANGEFUNC (peer) != NULL; /* see if the peer has a loop function */ peer_loop = GST_RPAD_LOOPFUNC (peer) != NULL; /* sinkpads with a loop function are delayed since they * need the srcpad to be active first */ - if (GST_PAD_IS_SINK (pad) && pad_loop) { + if (GST_PAD_IS_SINK (pad) && pad_loop && peer_get) { GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, "delaying pad %s", GST_OBJECT_NAME (pad)); delay = TRUE; - } else if (GST_PAD_IS_SRC (pad) && peer_loop) { + } else if (GST_PAD_IS_SRC (pad) && peer_loop && pad_get) { /* If the pad is a source and the peer has a loop function, * we can activate the srcpad and then the loopbased sinkpad */ GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, @@ -2039,7 +2060,6 @@ gst_element_dispose (GObject * object) /* ref so we don't hit 0 again */ gst_object_ref (GST_OBJECT (object)); - gst_element_set_state (element, GST_STATE_NULL); /* first we break all our links with the ouside */ while (element->pads) { diff --git a/gst/gstformat.c b/gst/gstformat.c index 15ce519f67..975be25238 100644 --- a/gst/gstformat.c +++ b/gst/gstformat.c @@ -194,7 +194,7 @@ gst_format_iterate_definitions (void) g_static_mutex_lock (&mutex); result = gst_iterator_new_list (g_static_mutex_get_mutex (&mutex), - &_n_values, &_gst_formats, NULL, NULL, NULL, NULL); + &_n_values, &_gst_formats, NULL, NULL, NULL); g_static_mutex_unlock (&mutex); return result; diff --git a/gst/gstiterator.c b/gst/gstiterator.c index 12f3eb6d37..a6a39c6a6f 100644 --- a/gst/gstiterator.c +++ b/gst/gstiterator.c @@ -27,14 +27,17 @@ gst_iterator_init (GstIterator * it, GMutex * lock, guint32 * master_cookie, GstIteratorNextFunction next, + GstIteratorItemFunction item, GstIteratorResyncFunction resync, GstIteratorFreeFunction free) { it->lock = lock; it->master_cookie = master_cookie; it->cookie = *master_cookie; it->next = next; + it->item = item; it->resync = resync; it->free = free; + it->pushed = NULL; } /** @@ -43,6 +46,7 @@ gst_iterator_init (GstIterator * it, * @lock: pointer to a #GMutex. * @master_cookie: pointer to a guint32 to protect the iterated object. * @next: function to get next item + * @item: function to call on each item retrieved * @resync: function to resync the iterator * @free: function to free the iterator * @@ -58,6 +62,7 @@ gst_iterator_new (guint size, GMutex * lock, guint32 * master_cookie, GstIteratorNextFunction next, + GstIteratorItemFunction item, GstIteratorResyncFunction resync, GstIteratorFreeFunction free) { GstIterator *result; @@ -69,7 +74,7 @@ gst_iterator_new (guint size, g_return_val_if_fail (free != NULL, NULL); result = g_malloc (size); - gst_iterator_init (result, lock, master_cookie, next, resync, free); + gst_iterator_init (result, lock, master_cookie, next, item, resync, free); return result; } @@ -83,8 +88,6 @@ typedef struct _GstListIterator gpointer owner; GList **orig; GList *list; /* pointer in list */ - GstIteratorRefFunction reffunc; - GstIteratorUnrefFunction unreffunc; GstIteratorDisposeFunction freefunc; } GstListIterator; @@ -95,9 +98,6 @@ gst_list_iterator_next (GstListIterator * it, gpointer * elem) return GST_ITERATOR_DONE; *elem = it->list->data; - if (it->reffunc) { - it->reffunc (*elem); - } it->list = g_list_next (it->list); return GST_ITERATOR_OK; @@ -139,8 +139,7 @@ gst_iterator_new_list (GMutex * lock, guint32 * master_cookie, GList ** list, gpointer owner, - GstIteratorRefFunction ref, - GstIteratorUnrefFunction unref, GstIteratorDisposeFunction free) + GstIteratorItemFunction item, GstIteratorDisposeFunction free) { GstListIterator *result; @@ -149,19 +148,27 @@ gst_iterator_new_list (GMutex * lock, lock, master_cookie, (GstIteratorNextFunction) gst_list_iterator_next, + (GstIteratorItemFunction) item, (GstIteratorResyncFunction) gst_list_iterator_resync, (GstIteratorFreeFunction) gst_list_iterator_free); result->owner = owner; result->orig = list; result->list = *list; - result->reffunc = ref; - result->unreffunc = unref; result->freefunc = free; return GST_ITERATOR (result); } +static void +gst_iterator_pop (GstIterator * it) +{ + if (it->pushed) { + gst_iterator_free (it->pushed); + it->pushed = NULL; + } +} + /** * gst_iterator_next: * @it: The #GstIterator to iterate @@ -181,14 +188,43 @@ gst_iterator_next (GstIterator * it, gpointer * elem) g_return_val_if_fail (it != NULL, GST_ITERATOR_ERROR); g_return_val_if_fail (elem != NULL, GST_ITERATOR_ERROR); +restart: + if (it->pushed) { + result = gst_iterator_next (it->pushed, elem); + if (result == GST_ITERATOR_DONE) { + /* we are done with this iterator, pop it and + * fallthrough iterating the main iterator again. */ + gst_iterator_pop (it); + } else { + return result; + } + } + if (G_LIKELY (it->lock)) g_mutex_lock (it->lock); + if (G_UNLIKELY (*it->master_cookie != it->cookie)) { result = GST_ITERATOR_RESYNC; goto done; } result = it->next (it, elem); + if (it->item) { + GstIteratorItem itemres; + + itemres = it->item (it, *elem); + switch (itemres) { + case GST_ITERATOR_ITEM_SKIP: + if (G_LIKELY (it->lock)) + g_mutex_unlock (it->lock); + goto restart; + case GST_ITERATOR_ITEM_END: + result = GST_ITERATOR_DONE; + break; + case GST_ITERATOR_ITEM_PASS: + break; + } + } done: if (G_LIKELY (it->lock)) @@ -211,6 +247,8 @@ gst_iterator_resync (GstIterator * it) { g_return_if_fail (it != NULL); + gst_iterator_pop (it); + if (G_LIKELY (it->lock)) g_mutex_lock (it->lock); it->resync (it); @@ -232,9 +270,34 @@ gst_iterator_free (GstIterator * it) { g_return_if_fail (it != NULL); + gst_iterator_pop (it); + it->free (it); } +/** + * gst_iterator_push: + * @it: The #GstIterator to use + * @other: The #GstIterator to push + * + * Pushes @other iterator onto @it. All calls performed on @it are + * forwarded tot @other. If @other returns #GST_ITERATOR_DONE, it is + * popped again and calls are handled by @it again. + * + * This function is mainly used by objects implementing the iterator + * next function to recurse into substructures. + * + * MT safe. + */ +void +gst_iterator_push (GstIterator * it, GstIterator * other) +{ + g_return_if_fail (it != NULL); + g_return_if_fail (other != NULL); + + it->pushed = other; +} + typedef struct _GstIteratorFilter { GstIterator iterator; @@ -345,6 +408,7 @@ gst_iterator_filter (GstIterator * it, gpointer user_data, GCompareFunc func) result = (GstIteratorFilter *) gst_iterator_new (sizeof (GstIteratorFilter), it->lock, it->master_cookie, (GstIteratorNextFunction) filter_next, + (GstIteratorItemFunction) NULL, (GstIteratorResyncFunction) filter_resync, (GstIteratorFreeFunction) filter_free); it->lock = NULL; @@ -381,6 +445,7 @@ gst_iterator_foreach (GstIterator * it, GFunc function, gpointer user_data) gst_iterator_init (GST_ITERATOR (&filter), it->lock, it->master_cookie, (GstIteratorNextFunction) filter_next, + (GstIteratorItemFunction) NULL, (GstIteratorResyncFunction) filter_resync, (GstIteratorFreeFunction) filter_uninit); it->lock = NULL; @@ -421,6 +486,7 @@ gst_iterator_find_custom (GstIterator * it, gpointer user_data, gst_iterator_init (GST_ITERATOR (&filter), it->lock, it->master_cookie, (GstIteratorNextFunction) filter_next, + (GstIteratorItemFunction) NULL, (GstIteratorResyncFunction) filter_resync, (GstIteratorFreeFunction) filter_uninit); it->lock = NULL; diff --git a/gst/gstiterator.h b/gst/gstiterator.h index da920dfa23..434eb41c96 100644 --- a/gst/gstiterator.h +++ b/gst/gstiterator.h @@ -35,11 +35,16 @@ typedef enum { typedef struct _GstIterator GstIterator; -typedef void (*GstIteratorRefFunction) (gpointer item); -typedef void (*GstIteratorUnrefFunction) (gpointer item); +typedef enum { + GST_ITERATOR_ITEM_SKIP = 0, /* skip item */ + GST_ITERATOR_ITEM_PASS = 1, /* return item */ + GST_ITERATOR_ITEM_END = 2, /* stop after this item */ +} GstIteratorItem; + typedef void (*GstIteratorDisposeFunction) (gpointer owner); typedef GstIteratorResult (*GstIteratorNextFunction) (GstIterator *it, gpointer *result); +typedef GstIteratorItem (*GstIteratorItemFunction) (GstIterator *it, gpointer item); typedef void (*GstIteratorResyncFunction) (GstIterator *it); typedef void (*GstIteratorFreeFunction) (GstIterator *it); @@ -50,9 +55,12 @@ typedef void (*GstIteratorFreeFunction) (GstIterator *it); struct _GstIterator { GstIteratorNextFunction next; + GstIteratorItemFunction item; GstIteratorResyncFunction resync; GstIteratorFreeFunction free; + GstIterator *pushed; /* pushed iterator */ + GMutex *lock; guint32 cookie; /* cookie of the iterator */ guint32 *master_cookie; /* pointer to guint32 holding the cookie when this @@ -64,6 +72,7 @@ GstIterator* gst_iterator_new (guint size, GMutex *lock, guint32 *master_cookie, GstIteratorNextFunction next, + GstIteratorItemFunction item, GstIteratorResyncFunction resync, GstIteratorFreeFunction free); @@ -71,8 +80,7 @@ GstIterator* gst_iterator_new_list (GMutex *lock, guint32 *master_cookie, GList **list, gpointer owner, - GstIteratorRefFunction ref, - GstIteratorUnrefFunction unref, + GstIteratorItemFunction item, GstIteratorDisposeFunction free); /* using iterators */ @@ -80,6 +88,8 @@ GstIteratorResult gst_iterator_next (GstIterator *it, gpointer *result); void gst_iterator_resync (GstIterator *it); void gst_iterator_free (GstIterator *it); +void gst_iterator_push (GstIterator *it, GstIterator *other); + /* special functions that operate on iterators */ void gst_iterator_foreach (GstIterator *it, GFunc function, gpointer user_data); diff --git a/gst/gstobject.c b/gst/gstobject.c index 6aa9b91061..1a1a0c11ab 100644 --- a/gst/gstobject.c +++ b/gst/gstobject.c @@ -420,7 +420,6 @@ gst_object_dispose (GObject * object) PATCH_REFCOUNT1 (object) parent_class->dispose (object); - } /* finalize is called when the object has to free its resources */ diff --git a/gst/gstpad.c b/gst/gstpad.c index 4d1230c5b1..e76a97ac27 100644 --- a/gst/gstpad.c +++ b/gst/gstpad.c @@ -464,6 +464,10 @@ gst_pad_set_active (GstPad * pad, GstActivateMode mode) if (activatefunc) { gboolean result; + GST_CAT_DEBUG (GST_CAT_PADS, + "calling activate function on pad %s:%s with mode %d", + GST_DEBUG_PAD_NAME (realpad), mode); + /* unlock so element can sync */ GST_UNLOCK (realpad); result = activatefunc (GST_PAD_CAST (realpad), mode); @@ -519,7 +523,7 @@ gst_pad_is_active (GstPad * pad) g_return_val_if_fail (GST_IS_PAD (pad), FALSE); GST_PAD_REALIZE_AND_LOCK (pad, realpad, lost_ghostpad); - result = !!GST_FLAG_IS_SET (realpad, GST_PAD_ACTIVE); + result = GST_FLAG_IS_SET (realpad, GST_PAD_ACTIVE); GST_UNLOCK (realpad); return result; diff --git a/gst/gstpipeline.c b/gst/gstpipeline.c index 6b82090287..c5fc17e5e2 100644 --- a/gst/gstpipeline.c +++ b/gst/gstpipeline.c @@ -216,11 +216,9 @@ pipeline_bus_handler (GstBus * bus, GstMessage * message, { GstBusSyncReply result = GST_BUS_PASS; gboolean posteos = FALSE; - gboolean locked; /* we don't want messages from the streaming thread while we're doing the * state change. We do want them from the state change functions. */ - locked = GST_STATE_TRYLOCK (pipeline); switch (GST_MESSAGE_TYPE (message)) { case GST_MESSAGE_EOS: @@ -242,8 +240,6 @@ pipeline_bus_handler (GstBus * bus, GstMessage * message, default: break; } - if (locked) - GST_STATE_UNLOCK (pipeline); if (posteos) { gst_bus_post (bus, gst_message_new_eos (GST_OBJECT (pipeline))); diff --git a/gst/gstquery.c b/gst/gstquery.c index e9e3ecc067..ffb64498bf 100644 --- a/gst/gstquery.c +++ b/gst/gstquery.c @@ -192,7 +192,7 @@ gst_query_type_iterate_definitions (void) g_static_mutex_lock (&mutex); result = gst_iterator_new_list (g_static_mutex_get_mutex (&mutex), - &_n_values, &_gst_queries, NULL, NULL, NULL, NULL); + &_n_values, &_gst_queries, NULL, NULL, NULL); g_static_mutex_unlock (&mutex); return result; diff --git a/gst/gstscheduler.c b/gst/gstscheduler.c index 053907d0d4..4b00e04684 100644 --- a/gst/gstscheduler.c +++ b/gst/gstscheduler.c @@ -125,48 +125,6 @@ gst_scheduler_reset (GstScheduler * sched) sclass->reset (sched); } -/** - * gst_scheduler_add_element: - * @sched: the scheduler - * @element: the element to add to the scheduler - * - * Add an element to the scheduler. - */ -void -gst_scheduler_add_element (GstScheduler * sched, GstElement * element) -{ - GstSchedulerClass *sclass; - - g_return_if_fail (GST_IS_SCHEDULER (sched)); - g_return_if_fail (GST_IS_ELEMENT (element)); - - sclass = GST_SCHEDULER_GET_CLASS (sched); - - if (sclass->add_element) - sclass->add_element (sched, element); -} - -/** - * gst_scheduler_remove_element: - * @sched: the scheduler - * @element: the element to remove - * - * Remove an element from the scheduler. - */ -void -gst_scheduler_remove_element (GstScheduler * sched, GstElement * element) -{ - GstSchedulerClass *sclass; - - g_return_if_fail (GST_IS_SCHEDULER (sched)); - g_return_if_fail (GST_IS_ELEMENT (element)); - - sclass = GST_SCHEDULER_GET_CLASS (sched); - - if (sclass->remove_element) - sclass->remove_element (sched, element); -} - GstTask * gst_scheduler_create_task (GstScheduler * sched, GstTaskFunction func, gpointer data) @@ -184,56 +142,6 @@ gst_scheduler_create_task (GstScheduler * sched, GstTaskFunction func, return result; } -GstClockReturn gst_clock_id_wait (GstClockID id, GstClockTimeDiff * jitter); - -/** - * gst_scheduler_clock_wait: - * @sched: the scheduler - * @element: the element that wants to wait - * @id: the clockid to use - * @jitter: the time difference between requested time and actual time - * - * Wait till the clock reaches a specific time. The ClockID can - * be obtained from #gst_clock_new_single_shot_id. - * - * Returns: the status of the operation - */ -GstClockReturn -gst_scheduler_clock_wait (GstScheduler * sched, GstElement * element, - GstClockID id, GstClockTimeDiff * jitter) -{ - GstSchedulerClass *sclass; - - g_return_val_if_fail (GST_IS_SCHEDULER (sched), GST_CLOCK_ERROR); - g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR); - - sclass = GST_SCHEDULER_GET_CLASS (sched); - - if (sclass->clock_wait) - return sclass->clock_wait (sched, element, id, jitter); - else - return gst_clock_id_wait (id, jitter); -} - -/** - * gst_scheduler_show: - * @sched: the scheduler - * - * Dump the state of the scheduler - */ -void -gst_scheduler_show (GstScheduler * sched) -{ - GstSchedulerClass *sclass; - - g_return_if_fail (GST_IS_SCHEDULER (sched)); - - sclass = GST_SCHEDULER_GET_CLASS (sched); - - if (sclass->show) - sclass->show (sched); -} - /* * Factory stuff starts here * diff --git a/gst/gstscheduler.h b/gst/gstscheduler.h index c24f07ab06..d6e6102338 100644 --- a/gst/gstscheduler.h +++ b/gst/gstscheduler.h @@ -63,20 +63,9 @@ struct _GstSchedulerClass { /* virtual methods */ void (*setup) (GstScheduler *sched); void (*reset) (GstScheduler *sched); - void (*add_element) (GstScheduler *sched, GstElement * element); - void (*remove_element) (GstScheduler *sched, GstElement * element); GstTask* (*create_task) (GstScheduler *sched, GstTaskFunction func, gpointer data); - GstClockReturn (*clock_wait) (GstScheduler *sched, GstElement *element, - GstClockID id, GstClockTimeDiff *jitter); - /* for debugging */ - void (*show) (GstScheduler *sched); - - /* signals */ - void (*object_sync) (GstScheduler *sched, GstClock *clock, GstObject *object, - GstClockID id); - gpointer _gst_reserved[GST_PADDING]; }; @@ -88,11 +77,6 @@ void gst_scheduler_reset (GstScheduler *sched); GstTask* gst_scheduler_create_task (GstScheduler *sched, GstTaskFunction func, gpointer data); -GstClockReturn gst_scheduler_clock_wait (GstScheduler *sched, GstElement *element, - GstClockID id, GstClockTimeDiff *jitter); - -void gst_scheduler_show (GstScheduler *sched); - /* * creating schedulers * diff --git a/gst/gstsystemclock.c b/gst/gstsystemclock.c index ed9906bf83..0365246c48 100644 --- a/gst/gstsystemclock.c +++ b/gst/gstsystemclock.c @@ -36,6 +36,8 @@ static GstClockTime gst_system_clock_get_internal_time (GstClock * clock); static guint64 gst_system_clock_get_resolution (GstClock * clock); static GstClockReturn gst_system_clock_id_wait (GstClock * clock, GstClockEntry * entry); +static GstClockReturn gst_system_clock_id_wait_unlocked + (GstClock * clock, GstClockEntry * entry); static GstClockReturn gst_system_clock_id_wait_async (GstClock * clock, GstClockEntry * entry); static void gst_system_clock_id_unschedule (GstClock * clock, @@ -232,11 +234,9 @@ gst_system_clock_async_thread (GstClock * clock) goto next_entry; } - /* now wait for the entry, it's a bit stupid to release the lock - * here but we have to since the next function grabs the lock... */ - GST_UNLOCK (clock); - res = gst_system_clock_id_wait (clock, (GstClockID) entry); - GST_LOCK (clock); + /* now wait for the entry, we already hold the lock */ + res = gst_system_clock_id_wait_unlocked (clock, (GstClockID) entry); + switch (res) { case GST_CLOCK_UNSCHEDULED: /* entry was unscheduled, move to the next */ @@ -308,31 +308,32 @@ gst_system_clock_get_resolution (GstClock * clock) * MT safe. */ static GstClockReturn -gst_system_clock_id_wait (GstClock * clock, GstClockEntry * entry) +gst_system_clock_id_wait_unlocked (GstClock * clock, GstClockEntry * entry) { - GstClockTime current, target; - gint64 diff; + GstClockTime real, current, target; + GstClockTimeDiff diff; - current = gst_clock_get_time (clock); - diff = GST_CLOCK_ENTRY_TIME (entry) - current; + real = GST_CLOCK_GET_CLASS (clock)->get_internal_time (clock); + target = GST_CLOCK_ENTRY_TIME (entry); + current = gst_clock_adjust_unlocked (clock, real); + diff = target - current; target = gst_system_clock_get_internal_time (clock) + diff; - GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p real_target %" GST_TIME_FORMAT + GST_CAT_DEBUG (GST_CAT_CLOCK, "entry %p" " target %" GST_TIME_FORMAT " now %" GST_TIME_FORMAT + " real %" GST_TIME_FORMAT " diff %" G_GINT64_FORMAT, entry, GST_TIME_ARGS (target), - GST_TIME_ARGS (GST_CLOCK_ENTRY_TIME (entry)), - GST_TIME_ARGS (current), diff); + GST_TIME_ARGS (current), GST_TIME_ARGS (real), diff); if (diff > 0) { GTimeVal tv; GST_TIME_TO_TIMEVAL (target, tv); - GST_LOCK (clock); while (TRUE) { /* now wait on the entry, it either times out or the cond is signaled. */ if (!GST_CLOCK_TIMED_WAIT (clock, &tv)) { @@ -349,13 +350,24 @@ gst_system_clock_id_wait (GstClock * clock, GstClockEntry * entry) break; } } - GST_UNLOCK (clock); } else { entry->status = GST_CLOCK_EARLY; } return entry->status; } +static GstClockReturn +gst_system_clock_id_wait (GstClock * clock, GstClockEntry * entry) +{ + GstClockReturn ret; + + GST_LOCK (clock); + ret = gst_system_clock_id_wait_unlocked (clock, entry); + GST_UNLOCK (clock); + + return ret; +} + /* Add an entry to the list of pending async waits. The entry is inserted * in sorted order. If we inserted the entry at the head of the list, we * need to signal the thread as it might either be waiting on it or waiting diff --git a/gst/gsttask.h b/gst/gsttask.h index bf38dabb87..935a4c7acf 100644 --- a/gst/gsttask.h +++ b/gst/gsttask.h @@ -57,7 +57,7 @@ typedef enum { struct _GstTask { GstObject object; - /*< public >*/ /* with LOCK */ + /*< public >*/ /* with TASK_LOCK */ GstTaskState state; GCond *cond; diff --git a/gst/gsttypes.h b/gst/gsttypes.h index fcc206c4b7..78345d0c1c 100644 --- a/gst/gsttypes.h +++ b/gst/gsttypes.h @@ -57,7 +57,6 @@ typedef enum GST_STATE_FAILURE = 0, GST_STATE_SUCCESS = 1, GST_STATE_ASYNC = 2, - GST_STATE_BUSY = 3 } GstElementStateReturn; typedef enum diff --git a/gst/schedulers/cothreads_compat.h b/gst/schedulers/cothreads_compat.h deleted file mode 100644 index 41d0cdf4a8..0000000000 --- a/gst/schedulers/cothreads_compat.h +++ /dev/null @@ -1,79 +0,0 @@ -/* GStreamer - * Copyright (C) 1999,2000 Erik Walthinsen - * 2000 Wim Taymans - * - * cothreads_compat.h: Compatibility macros between cothreads packages - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -/* use the old cothreads implementation in gst/cothreads.[ch] */ -#if defined(_COTHREADS_OMEGA) - -#include "../cothreads.h" - -/* the name of this cothreads type */ -#define COTHREADS_TYPE omega -#define COTHREADS_NAME "omega" -#define COTHREADS_NAME_CAPITAL "Omega" - -/* unify the structs - * - * "cothread" and "cothread_context" need to be defined - */ -typedef struct _cothread_state cothread; - -/* define functions - * the macros are prepended with "do_" - */ -#define do_cothreads_init(x) /* NOP */ - -#define do_cothreads_stackquery(stack,size) cothread_stackquery(stack,size) - -#define do_cothread_switch(to) cothread_switch(to) - -#define do_cothread_create(new_cothread, context, func, argc, argv) \ - G_STMT_START{ \ - new_cothread = cothread_create (context); \ - if (new_cothread) { \ - cothread_setfunc (new_cothread, (func), (argc), (argv)); \ - } \ - }G_STMT_END - -#define do_cothread_setfunc(cothread, context, func, argc, argv) \ - cothread_setfunc ((cothread), (func), (argc), (argv)) - -#define do_cothread_destroy(cothread) cothread_free(cothread) - -#define do_cothread_context_init() (cothread_context_init ()) -#define do_cothread_context_destroy(context) cothread_context_free (context) - -#define do_cothread_get_current(context) (cothread_current()) -#define do_cothread_get_main(context) (cothread_current_main()) - - - - -/* use the gthread-based cothreads implementation */ -#elif defined(_COTHREADS_GTHREAD) - -#include "gthread-cothreads.h" - - -/* bail out with an error if no cothreads package is defined */ -#else -#error "No cothreads package defined" -#endif diff --git a/gst/schedulers/entryscheduler.c b/gst/schedulers/entryscheduler.c deleted file mode 100644 index e9dac3fb50..0000000000 --- a/gst/schedulers/entryscheduler.c +++ /dev/null @@ -1,1175 +0,0 @@ -/* GStreamer - * Copyright (C) 2004 Benjamin Otte - * - * gstentryscheduler.c: A scheduler based on entries - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include -#include "cothreads_compat.h" -#include "../gst-i18n-lib.h" - -GST_DEBUG_CATEGORY_STATIC (debug_scheduler); -#define GST_CAT_DEFAULT debug_scheduler - - -#define GET_TYPE(x) gst_entry_ ## x ## _scheduler_get_type -#define GST_TYPE_ENTRY_SCHEDULER \ - (GET_TYPE (COTHREADS_TYPE) ()) -#define GST_ENTRY_SCHEDULER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ENTRY_SCHEDULER,GstEntryScheduler)) -#define GST_ENTRY_SCHEDULER_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ENTRY_SCHEDULER,GstEntrySchedulerClass)) -#define GST_IS_ENTRY_SCHEDULER(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ENTRY_SCHEDULER)) -#define GST_IS_ENTRY_SCHEDULER_CLASS(obj) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ENTRY_SCHEDULER)) - -#define SCHED_ASSERT(sched, assertion) G_STMT_START{ \ - if (!(assertion)) \ - gst_scheduler_show (GST_SCHEDULER (sched)); \ - g_assert (assertion); \ -}G_STMT_END - -typedef enum -{ - WAIT_FOR_NOTHING, - WAIT_FOR_MUM, - WAIT_FOR_PADS, /* pad must be scheduled */ - /* add more */ - WAIT_FOR_ANYTHING -} -WaitInfo; - -typedef enum -{ - ENTRY_UNDEFINED, - ENTRY_COTHREAD, - ENTRY_LINK -} -EntryType; - -typedef struct -{ - EntryType type; -} -Entry; - -#define ENTRY_IS_COTHREAD(x) (((Entry *)(x))->type == ENTRY_COTHREAD) -#define ENTRY_IS_LINK(x) (((Entry *)(x))->type == ENTRY_LINK) - -typedef struct _GstEntryScheduler GstEntryScheduler; -typedef struct _GstEntrySchedulerClass GstEntrySchedulerClass; - -typedef struct -{ - Entry entry; - /* pointer to scheduler */ - GstEntryScheduler *sched; - /* pointer to element */ - GstElement *element; - /* the main function of the cothread */ - int (*main) (int argc, gchar ** argv); - /* wether the given pad is schedulable */ - gboolean (*can_schedule) (GstRealPad * pad); - /* what the element is currently waiting for */ - WaitInfo wait; - /* cothread of element */ - cothread *thread; - /* pad to schedule next */ - GstRealPad *schedule_pad; -} -CothreadPrivate; - -#define ELEMENT_PRIVATE(element) ((CothreadPrivate *) GST_ELEMENT (element)->sched_private) -#define SCHED(element) (GST_ENTRY_SCHEDULER ((element)->sched)) - -typedef struct -{ - Entry entry; - /* pads */ - GstRealPad *srcpad; - GstRealPad *sinkpad; - /* private struct of srcpad's element, needed for decoupled elements */ - CothreadPrivate *src; - /* private struct of sinkpad's element */ - CothreadPrivate *sink; - /* current data */ - GstData *bufpen; -} -LinkPrivate; - -#define PAD_PRIVATE(pad) ((LinkPrivate *) (GST_REAL_PAD (pad))->sched_private) - -struct _GstEntryScheduler -{ - GstScheduler scheduler; - - cothread_context *context; - - GList *schedule_now; /* entry points that must be scheduled this - iteration */ - GList *schedule_possible; /* possible entry points */ - GList *waiting; /* elements waiting for the clock */ - gboolean error; /* if an element threw an error */ - - GSList *reaping; /* cothreads we need to destroy but can't */ -}; - -struct _GstEntrySchedulerClass -{ - GstSchedulerClass scheduler_class; -}; - -static void gst_entry_scheduler_class_init (gpointer g_class, gpointer data); -static void gst_entry_scheduler_init (GstEntryScheduler * object); - - -GType GET_TYPE (COTHREADS_TYPE) (void) -{ - static GType object_type = 0; - - if (object_type == 0) { - static const GTypeInfo object_info = { - sizeof (GstEntrySchedulerClass), - NULL, - NULL, - gst_entry_scheduler_class_init, - NULL, - NULL, - sizeof (GstEntryScheduler), - 0, - (GInstanceInitFunc) gst_entry_scheduler_init - }; - - object_type = - g_type_register_static (GST_TYPE_SCHEDULER, - "GstEntry" COTHREADS_NAME_CAPITAL "Scheduler", &object_info, 0); - } - return object_type; -} - -static int gst_entry_scheduler_loop_wrapper (int argc, char **argv); -static int gst_entry_scheduler_get_wrapper (int argc, char **argv); -static int gst_entry_scheduler_chain_wrapper (int argc, char **argv); - -static void gst_entry_scheduler_setup (GstScheduler * sched); -static void gst_entry_scheduler_reset (GstScheduler * sched); -static void gst_entry_scheduler_add_element (GstScheduler * sched, - GstElement * element); -static void gst_entry_scheduler_remove_element (GstScheduler * sched, - GstElement * element); -static GstElementStateReturn gst_entry_scheduler_state_transition (GstScheduler - * sched, GstElement * element, gint transition); -static gboolean gst_entry_scheduler_yield (GstScheduler * sched, - GstElement * element); -static gboolean gst_entry_scheduler_interrupt (GstScheduler * sched, - GstElement * element); -static void gst_entry_scheduler_error (GstScheduler * sched, - GstElement * element); -static void gst_entry_scheduler_pad_link (GstScheduler * sched, GstPad * srcpad, - GstPad * sinkpad); -static void gst_entry_scheduler_pad_unlink (GstScheduler * sched, - GstPad * srcpad, GstPad * sinkpad); -static GstData *gst_entry_scheduler_pad_select (GstScheduler * sched, - GstPad ** pulled_from, GstPad ** pads); -static GstSchedulerState gst_entry_scheduler_iterate (GstScheduler * sched); -static void gst_entry_scheduler_show (GstScheduler * scheduler); - -static gboolean can_schedule_pad (GstRealPad * pad); -static void schedule_next_element (GstEntryScheduler * sched); - -static void -gst_entry_scheduler_class_init (gpointer klass, gpointer class_data) -{ - GstSchedulerClass *scheduler = GST_SCHEDULER_CLASS (klass); - - scheduler->setup = gst_entry_scheduler_setup; - scheduler->reset = gst_entry_scheduler_reset; - scheduler->add_element = gst_entry_scheduler_add_element; - scheduler->remove_element = gst_entry_scheduler_remove_element; - scheduler->state_transition = gst_entry_scheduler_state_transition; - scheduler->yield = gst_entry_scheduler_yield; - scheduler->interrupt = gst_entry_scheduler_interrupt; - scheduler->error = gst_entry_scheduler_error; - scheduler->pad_link = gst_entry_scheduler_pad_link; - scheduler->pad_unlink = gst_entry_scheduler_pad_unlink; - scheduler->pad_select = gst_entry_scheduler_pad_select; - scheduler->clock_wait = NULL; - scheduler->iterate = gst_entry_scheduler_iterate; - scheduler->show = gst_entry_scheduler_show; - - do_cothreads_init (NULL); -} - -static void -gst_entry_scheduler_init (GstEntryScheduler * scheduler) -{ - GST_FLAG_SET (scheduler, GST_SCHEDULER_FLAG_NEW_API); -} - -/* - * We've got to setup 5 different element types here: - * - loopbased - * - chainbased - * - chainbased PADS of decoupled elements - * - getbased - * - getbased PADS of decoupled elements - */ - -/* - * LOOPBASED - */ - -typedef struct -{ - CothreadPrivate element; - GstPad **sinkpads; -} -LoopPrivate; - -#define LOOP_PRIVATE(x) ((LoopPrivate *) ELEMENT_PRIVATE (x)) - -static gboolean -_can_schedule_loop (GstRealPad * pad) -{ - LoopPrivate *priv; - gint i = 0; - - g_assert (PAD_PRIVATE (pad)); - - if (GST_PAD_IS_SRC (pad)) - return FALSE; - - priv = LOOP_PRIVATE (gst_pad_get_parent (GST_PAD (pad))); - g_assert (priv); - if (!priv->sinkpads) - return FALSE; - - while (priv->sinkpads[i]) { - if (pad == GST_REAL_PAD (priv->sinkpads[i++])) - return TRUE; - } - return FALSE; -} - -static int -gst_entry_scheduler_loop_wrapper (int argc, char **argv) -{ - CothreadPrivate *priv = (CothreadPrivate *) argv; - GstElement *element = priv->element; - - priv->wait = WAIT_FOR_NOTHING; - do { - g_assert (priv->wait == WAIT_FOR_NOTHING); - GST_LOG_OBJECT (SCHED (element), "calling loopfunc for element %s", - GST_ELEMENT_NAME (element)); - if (element->loopfunc) { - element->loopfunc (element); - } else { - GST_ELEMENT_ERROR (element, CORE, SCHEDULER, (_("badly behaving plugin")), - ("loop-based element %s removed loopfunc during processing", - GST_OBJECT_NAME (element))); - } - GST_LOG_OBJECT (SCHED (element), "done calling loopfunc for element %s", - GST_OBJECT_NAME (element)); - priv->wait = WAIT_FOR_NOTHING; - schedule_next_element (SCHED (element)); - } while (TRUE); - - return 0; -} - -static CothreadPrivate * -setup_loop (GstEntryScheduler * sched, GstElement * element) -{ - /* the types not matching is intentional, that's why it's g_new0 */ - CothreadPrivate *priv = (CothreadPrivate *) g_new0 (LoopPrivate, 1); - - priv->element = element; - priv->main = gst_entry_scheduler_loop_wrapper; - priv->wait = WAIT_FOR_NOTHING; - priv->can_schedule = _can_schedule_loop; - - return priv; -} - -/* - * CHAINBASED - */ - -static GstData * -get_buffer (GstEntryScheduler * sched, GstRealPad * pad) -{ - LinkPrivate *priv = PAD_PRIVATE (pad); - GstData *data = priv->bufpen; - - priv->bufpen = NULL; - g_assert (data); - return data; -} - -static int -gst_entry_scheduler_chain_wrapper (int argc, char **argv) -{ - CothreadPrivate *priv = (CothreadPrivate *) argv; - GstElement *element = priv->element; - - priv->wait = WAIT_FOR_PADS; - do { - GstRealPad *pad = priv->schedule_pad; - - g_assert (priv->wait == WAIT_FOR_PADS); - g_assert (pad); - g_assert (GST_PAD_IS_SINK (pad)); - g_assert (PAD_PRIVATE (pad)->bufpen != NULL); - GST_LOG_OBJECT (priv->sched, "calling chainfunc for pad %s:%s", - GST_DEBUG_PAD_NAME (pad)); - if (pad->chainfunc) { - GstData *data = get_buffer (priv->sched, pad); - - gst_pad_call_chain_function (GST_PAD (pad), data); - /* don't do anything after here with the pad, it might already be dead! - the element is still alive though */ - } else { - GST_ELEMENT_ERROR (element, CORE, SCHEDULER, (_("badly behaving plugin")), - ("chain-based element %s removed chainfunc of pad during processing", - GST_OBJECT_NAME (element))); - gst_data_unref (PAD_PRIVATE (pad)->bufpen); - PAD_PRIVATE (pad)->bufpen = NULL; - } - GST_LOG_OBJECT (priv->sched, "done calling chainfunc for element %s", - GST_OBJECT_NAME (element)); - priv->wait = WAIT_FOR_PADS; - schedule_next_element (priv->sched); - } while (TRUE); - - return 0; -} - -static gboolean -_can_schedule_chain (GstRealPad * pad) -{ - g_assert (PAD_PRIVATE (pad)); - - if (GST_PAD_IS_SRC (pad)) - return FALSE; - - g_assert (PAD_PRIVATE (pad)); - return PAD_PRIVATE (pad)->sink->wait == WAIT_FOR_PADS; -} - -static CothreadPrivate * -setup_chain (GstEntryScheduler * sched, GstElement * element) -{ - CothreadPrivate *priv = g_new0 (CothreadPrivate, 1); - - priv->main = gst_entry_scheduler_chain_wrapper; - priv->wait = WAIT_FOR_PADS; - priv->can_schedule = _can_schedule_chain; - - return priv; -} - -/* - * GETBASED - */ - -static int -gst_entry_scheduler_get_wrapper (int argc, char **argv) -{ - CothreadPrivate *priv = (CothreadPrivate *) argv; - GstElement *element = priv->element; - - priv->wait = WAIT_FOR_PADS; - do { - GstRealPad *pad = priv->schedule_pad; - - g_assert (pad); - g_assert (GST_PAD_IS_SRC (pad)); - g_assert (PAD_PRIVATE (pad)->bufpen == NULL); - GST_LOG_OBJECT (priv->sched, "calling getfunc for pad %s:%s", - GST_DEBUG_PAD_NAME (pad)); - if (pad->getfunc) { - GstData *data = gst_pad_call_get_function (GST_PAD (pad)); - - /* make sure the pad still exists and is linked */ - if (!g_list_find (element->pads, pad)) { - GST_ELEMENT_ERROR (element, CORE, SCHEDULER, - (_("badly behaving plugin")), - ("get-based element %s removed pad during getfunc", - GST_OBJECT_NAME (element))); - gst_data_unref (data); - } else if (!GST_PAD_PEER (pad)) { - GST_ELEMENT_ERROR (element, CORE, SCHEDULER, - (_("badly behaving plugin")), - ("get-based element %s unlinked pad during getfunc", - GST_OBJECT_NAME (element))); - gst_data_unref (data); - } else { - PAD_PRIVATE (pad)->bufpen = data; - priv->sched->schedule_now = - g_list_prepend (priv->sched->schedule_now, PAD_PRIVATE (pad)); - } - } else { - GST_ELEMENT_ERROR (element, CORE, SCHEDULER, (_("badly behaving plugin")), - ("get-based element %s removed getfunc during processing", - GST_OBJECT_NAME (element))); - } - GST_LOG_OBJECT (priv->sched, "done calling getfunc for element %s", - GST_ELEMENT_NAME (element)); - - priv->wait = WAIT_FOR_PADS; - schedule_next_element (priv->sched); - } while (TRUE); - - return 0; -} - -static gboolean -_can_schedule_get (GstRealPad * pad) -{ - g_assert (PAD_PRIVATE (pad)); - g_assert (GST_PAD_IS_SRC (pad)); - - g_assert (PAD_PRIVATE (pad)); - return PAD_PRIVATE (pad)->bufpen == NULL && - PAD_PRIVATE (pad)->src->wait == WAIT_FOR_PADS && - can_schedule_pad (PAD_PRIVATE (pad)->sinkpad); -} - -static CothreadPrivate * -setup_get (GstEntryScheduler * sched, GstElement * element) -{ - CothreadPrivate *priv = g_new0 (CothreadPrivate, 1); - - priv->main = gst_entry_scheduler_get_wrapper; - priv->wait = WAIT_FOR_PADS; - priv->can_schedule = _can_schedule_get; - - return priv; -} - -/* - * scheduling functions - */ - -static gboolean -can_schedule_pad (GstRealPad * pad) -{ - LinkPrivate *link = PAD_PRIVATE (pad); - - g_assert (link); - if (GST_STATE (gst_pad_get_parent (GST_PAD (pad))) != GST_STATE_PLAYING) - return FALSE; - if (GST_PAD_IS_SINK (pad)) { - return link->sink->can_schedule (pad); - } else { - return link->src->can_schedule (pad); - } -} - -static gboolean -can_schedule (Entry * entry) -{ - if (ENTRY_IS_LINK (entry)) { - LinkPrivate *link = (LinkPrivate *) entry; - CothreadPrivate *priv; - GstRealPad *pad; - - if (link->bufpen) { - priv = link->sink; - pad = link->sinkpad; - } else { - priv = link->src; - pad = link->srcpad; - } - if (priv->wait != WAIT_FOR_PADS) - return FALSE; - return can_schedule_pad (pad); - } else if (ENTRY_IS_COTHREAD (entry)) { - CothreadPrivate *priv = (CothreadPrivate *) entry; - GList *list; - - if (priv->wait != WAIT_FOR_NOTHING) - return FALSE; - if (GST_STATE (priv->element) != GST_STATE_PLAYING) - return FALSE; - if (GST_FLAG_IS_SET (priv->element, GST_ELEMENT_DECOUPLED)) { - g_assert (PAD_PRIVATE (priv->schedule_pad)); - return TRUE; - } - for (list = priv->element->pads; list; list = g_list_next (list)) { - GstPad *pad = GST_PAD (list->data); - - if (GST_PAD_IS_SRC (pad) && PAD_PRIVATE (pad) && - PAD_PRIVATE (pad)->bufpen != NULL) - return FALSE; - } - return TRUE; - } else { - g_assert_not_reached (); - return FALSE; - } -} - -static void -safe_cothread_switch (GstEntryScheduler * scheduler, cothread * thread) -{ - GList *list; - cothread *cur = do_cothread_get_current (scheduler->context); - - if (cur == thread) { - GST_LOG_OBJECT (scheduler, "switch to same cothread, ignoring"); - } - - for (list = scheduler->schedule_possible; list; list = g_list_next (list)) { - if (ENTRY_IS_COTHREAD (list->data)) { - CothreadPrivate *priv = (CothreadPrivate *) list->data; - - if (priv->thread == thread) - gst_object_ref (GST_OBJECT (priv->element)); - if (priv->thread == cur) - gst_object_unref (GST_OBJECT (priv->element)); - } - } - do_cothread_switch (thread); - if (cur == do_cothread_get_main (scheduler->context)) { - GSList *walk; - - for (walk = scheduler->reaping; walk; walk = g_slist_next (walk)) { - do_cothread_destroy (walk->data); - } - g_slist_free (scheduler->reaping); - scheduler->reaping = NULL; - } -} - -/* the meat - no guarantee as to which cothread this function is called */ -static void -schedule (GstEntryScheduler * sched, Entry * entry) -{ - CothreadPrivate *schedule_me; - - g_assert (can_schedule (entry)); - sched->schedule_now = g_list_remove (sched->schedule_now, entry); - sched->schedule_possible = g_list_remove (sched->schedule_possible, entry); - sched->schedule_possible = g_list_append (sched->schedule_possible, entry); - if (ENTRY_IS_LINK (entry)) { - LinkPrivate *link = (LinkPrivate *) entry; - - if (link->bufpen) { - schedule_me = link->sink; - schedule_me->schedule_pad = link->sinkpad; - } else { - schedule_me = link->src; - schedule_me->schedule_pad = link->srcpad; - } - GST_DEBUG_OBJECT (sched, "scheduling pad %s:%s", - GST_DEBUG_PAD_NAME (schedule_me->schedule_pad)); - } else if (ENTRY_IS_COTHREAD (entry)) { - schedule_me = (CothreadPrivate *) entry; - GST_DEBUG_OBJECT (sched, "scheduling element %s", - GST_OBJECT_NAME (schedule_me->element)); - } else { - g_assert_not_reached (); - GST_DEBUG_OBJECT (sched, "scheduling main after error"); - sched->error = TRUE; - safe_cothread_switch (sched, do_cothread_get_main (sched->context)); - return; - } - - if (!schedule_me->thread) { - GST_LOG_OBJECT (sched, "creating cothread for %p (element %s)", schedule_me, - GST_OBJECT_NAME (schedule_me->element)); - do_cothread_create (schedule_me->thread, sched->context, schedule_me->main, - 0, (gchar **) schedule_me); - } - - safe_cothread_switch (sched, schedule_me->thread); -} - -/* this function will die a horrible death if you have cyclic pipelines */ -static Entry * -schedule_forward (Entry * entry) -{ - if (can_schedule (entry)) - return entry; - if (ENTRY_IS_LINK (entry)) { - return schedule_forward ((Entry *) ((LinkPrivate *) entry)->sink); - } else if (ENTRY_IS_COTHREAD (entry)) { - GList *list; - GstElement *element = ((CothreadPrivate *) entry)->element; - - if (GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED)) - return NULL; - for (list = element->pads; list; list = g_list_next (list)) { - if (GST_PAD_IS_SINK (list->data) || !PAD_PRIVATE (list->data)) - continue; - entry = schedule_forward ((Entry *) PAD_PRIVATE (list->data)); - if (entry) - return entry; - } - } else { - g_assert_not_reached (); - } - return NULL; -} - -static void -schedule_next_element (GstEntryScheduler * scheduler) -{ - if (scheduler->error) { - GST_DEBUG_OBJECT (scheduler, "scheduling main after error"); - safe_cothread_switch (scheduler, do_cothread_get_main (scheduler->context)); - } else if (scheduler->waiting) { - /* FIXME: write me */ - g_assert_not_reached (); - } else if (scheduler->schedule_now) { - GList *test; - - for (test = scheduler->schedule_now; test; test = g_list_next (test)) { - Entry *entry = schedule_forward ((Entry *) test->data); - - if (entry) { - schedule (scheduler, entry); - return; - } - } - if (!scheduler->waiting) { - GST_ERROR_OBJECT (scheduler, - "have stuff that must be scheduled, but nothing that can be scheduled"); - scheduler->error = TRUE; - } - } - GST_DEBUG_OBJECT (scheduler, "scheduling main"); - safe_cothread_switch (scheduler, do_cothread_get_main (scheduler->context)); -} - -/* - * handlers to attach to pads - */ - -static void -gst_entry_scheduler_chain_handler (GstPad * pad, GstData * data) -{ - LinkPrivate *priv = PAD_PRIVATE (pad); - CothreadPrivate *thread = priv->src; - GstEntryScheduler *sched = thread->sched; - - GST_LOG_OBJECT (sched, "putting data %p in pen of pad %s:%s", - data, GST_DEBUG_PAD_NAME (pad)); - - if (priv->bufpen != NULL) { - GST_ERROR_OBJECT (sched, "scheduling error: trying to push data in bufpen" - "of pad %s:%s, but bufpen was full", GST_DEBUG_PAD_NAME (pad)); - sched->error = TRUE; - gst_data_unref (data); - } else { - priv->bufpen = data; - sched->schedule_now = g_list_append (sched->schedule_now, priv); - } - - thread->wait = WAIT_FOR_NOTHING; - schedule_next_element (sched); - - GST_LOG_OBJECT (sched, "done"); -} - -static GstData * -gst_entry_scheduler_get_handler (GstPad * pad) -{ - GstData *data; - GstEntryScheduler *sched = GST_ENTRY_SCHEDULER (gst_pad_get_scheduler (pad)); - GstPad *pads[2] = { NULL, NULL }; - GstPad *ret; - - pad = GST_PAD_PEER (pad); - pads[0] = pad; - GST_LOG_OBJECT (sched, "pad %s:%s pulls", GST_DEBUG_PAD_NAME (pad)); - - data = gst_entry_scheduler_pad_select (GST_SCHEDULER (sched), &ret, pads); - g_assert (pad == ret); - - GST_LOG_OBJECT (sched, "done with %s:%s", GST_DEBUG_PAD_NAME (pad)); - return data; -} - -static gboolean -gst_entry_scheduler_event_handler (GstPad * srcpad, GstEvent * event) -{ - /* FIXME: need to do more here? */ - return GST_RPAD_EVENTFUNC (srcpad) (srcpad, event); -} - -/* - * Entry points for this scheduler. - */ - -static GstData * -gst_entry_scheduler_pad_select (GstScheduler * scheduler, GstPad ** pulled_from, - GstPad ** pads) -{ - GstData *data; - GstRealPad *pad = NULL; - GstElement *element = NULL; - GstEntryScheduler *sched = GST_ENTRY_SCHEDULER (scheduler); - gint i = 0; - - /* sanity check */ - while (pads[i]) { - pad = GST_REAL_PAD (pads[i++]); - if (PAD_PRIVATE (pad)->bufpen) { - sched->schedule_now = - g_list_remove (sched->schedule_now, PAD_PRIVATE (pad)); - goto found; - } - } - element = gst_pad_get_parent (GST_PAD (pad)); - g_assert (element); - g_assert (ELEMENT_PRIVATE (element)->main == - gst_entry_scheduler_loop_wrapper); - LOOP_PRIVATE (element)->sinkpads = pads; - ELEMENT_PRIVATE (element)->wait = WAIT_FOR_PADS; - schedule_next_element (SCHED (element)); - LOOP_PRIVATE (element)->sinkpads = NULL; - pad = ELEMENT_PRIVATE (element)->schedule_pad; - g_assert (PAD_PRIVATE (pad)->bufpen); -found: - data = get_buffer (sched, pad); - g_return_val_if_fail (pulled_from, data); - *pulled_from = GST_PAD (pad); - return data; -} - -static void -gst_entry_scheduler_setup (GstScheduler * sched) -{ - /* first create thread context */ - if (GST_ENTRY_SCHEDULER (sched)->context == NULL) { - GST_DEBUG_OBJECT (sched, "initializing cothread context"); - GST_ENTRY_SCHEDULER (sched)->context = do_cothread_context_init (); - } -} - -static void -safe_cothread_destroy (CothreadPrivate * thread) -{ - GstEntryScheduler *scheduler = thread->sched; - - if (do_cothread_get_current (scheduler->context) == - do_cothread_get_main (scheduler->context)) { - do_cothread_destroy (thread->thread); - } else { - GST_WARNING_OBJECT (scheduler, "delaying destruction of cothread %p", - thread->thread); - scheduler->reaping = g_slist_prepend (scheduler->reaping, thread->thread); - } - thread->thread = NULL; -} - -static void -gst_entry_scheduler_remove_all_cothreads (GstEntryScheduler * scheduler) -{ - GList *list; - - for (list = scheduler->schedule_possible; list; list = g_list_next (list)) { - if (ENTRY_IS_COTHREAD (list->data)) { - CothreadPrivate *priv = (CothreadPrivate *) list->data; - - if (priv->thread) - safe_cothread_destroy (priv); - } - } -} - -static void -gst_entry_scheduler_reset (GstScheduler * sched) -{ - GstEntryScheduler *scheduler = GST_ENTRY_SCHEDULER (sched); - - if (scheduler->context) { - g_return_if_fail (scheduler->reaping == NULL); - gst_entry_scheduler_remove_all_cothreads (scheduler); - do_cothread_context_destroy (scheduler->context); - scheduler->context = NULL; - } -} - -static CothreadPrivate * -_setup_cothread (GstEntryScheduler * sched, GstElement * element, - CothreadPrivate * (*setup_func) (GstEntryScheduler *, GstElement *)) -{ - CothreadPrivate *priv = setup_func (sched, element); - - priv->entry.type = ENTRY_COTHREAD; - priv->sched = sched; - priv->element = element; - sched->schedule_possible = g_list_prepend (sched->schedule_possible, priv); - - if (GST_STATE (element) >= GST_STATE_READY) - gst_entry_scheduler_state_transition (GST_SCHEDULER (sched), element, - GST_STATE_NULL_TO_READY); - if (GST_STATE (element) >= GST_STATE_PAUSED) - gst_entry_scheduler_state_transition (GST_SCHEDULER (sched), element, - GST_STATE_READY_TO_PAUSED); - if (GST_STATE (element) >= GST_STATE_PLAYING) - gst_entry_scheduler_state_transition (GST_SCHEDULER (sched), element, - GST_STATE_PAUSED_TO_PLAYING); - - return priv; -} - -static void -gst_entry_scheduler_add_element (GstScheduler * scheduler, GstElement * element) -{ - GstEntryScheduler *sched = GST_ENTRY_SCHEDULER (scheduler); - - if (GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED)) { - GST_INFO_OBJECT (sched, "decoupled element %s added, ignoring", - GST_OBJECT_NAME (element)); - return; - } - - g_assert (element->sched_private == NULL); - if (element->loopfunc) { - element->sched_private = _setup_cothread (sched, element, setup_loop); - } -} - -static void -_remove_cothread (CothreadPrivate * priv) -{ - GstEntryScheduler *sched = priv->sched; - - sched->waiting = g_list_remove (sched->waiting, priv); - sched->schedule_now = g_list_remove (sched->schedule_now, priv); - sched->schedule_possible = g_list_remove (sched->schedule_possible, priv); - - if (priv->thread) - safe_cothread_destroy (priv); - g_free (priv); -} - -static void -gst_entry_scheduler_remove_element (GstScheduler * scheduler, - GstElement * element) -{ - if (GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED)) { - GST_INFO_OBJECT (scheduler, "decoupled element %s added, ignoring", - GST_OBJECT_NAME (element)); - return; - } - - if (element->sched_private) { - _remove_cothread (element->sched_private); - element->sched_private = NULL; - } -} - -static GstElementStateReturn -gst_entry_scheduler_state_transition (GstScheduler * scheduler, - GstElement * element, gint transition) -{ - GstEntryScheduler *sched = GST_ENTRY_SCHEDULER (scheduler); - - if (GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED)) - return GST_STATE_SUCCESS; - - /* check if our parent changed state */ - switch (transition) { - case GST_STATE_NULL_TO_READY: - break; - case GST_STATE_READY_TO_PAUSED: - break; - case GST_STATE_PAUSED_TO_PLAYING: - break; - case GST_STATE_PLAYING_TO_PAUSED: - break; - case GST_STATE_PAUSED_TO_READY: - if (element == scheduler->parent) { - gst_entry_scheduler_remove_all_cothreads (sched); - } - if (element->sched_private != NULL - && ELEMENT_PRIVATE (element)->thread != NULL) { - safe_cothread_destroy (ELEMENT_PRIVATE (element)); - } - break; - case GST_STATE_READY_TO_NULL: - break; - default: - g_warning ("invalid state change %d for element %s", transition, - GST_OBJECT_NAME (element)); - return GST_STATE_FAILURE; - } - - return GST_STATE_SUCCESS; -} - -static gboolean -gst_entry_scheduler_yield (GstScheduler * sched, GstElement * element) -{ - /* g_assert (ELEMENT_PRIVATE (element)); */ - /* FIXME: queue thinks it may just interrupt, is that ok? */ - if (!ELEMENT_PRIVATE (element)) - return TRUE; - ELEMENT_PRIVATE (element)->wait = WAIT_FOR_NOTHING; - schedule_next_element (GST_ENTRY_SCHEDULER (sched)); - return FALSE; -} - -static gboolean -gst_entry_scheduler_interrupt (GstScheduler * sched, GstElement * element) -{ - /* FIXME? */ - return gst_entry_scheduler_yield (sched, element); -} - -static void -gst_entry_scheduler_error (GstScheduler * scheduler, GstElement * element) -{ - GST_ENTRY_SCHEDULER (scheduler)->error = TRUE; -} - -static void -gst_entry_scheduler_pad_link (GstScheduler * scheduler, GstPad * srcpad, - GstPad * sinkpad) -{ - GstEntryScheduler *sched = GST_ENTRY_SCHEDULER (scheduler); - LinkPrivate *priv; - GstElement *element; - - priv = g_new0 (LinkPrivate, 1); - priv->entry.type = ENTRY_LINK; - /* wrap srcpad */ - element = gst_pad_get_parent (srcpad); - priv->srcpad = GST_REAL_PAD (srcpad); - if (GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED)) { - priv->src = _setup_cothread (sched, element, setup_get); - } else { - priv->src = ELEMENT_PRIVATE (element); - if (!priv->src) { - GList *list; - - for (list = element->pads; list; list = g_list_next (list)) { - if (GST_PAD_IS_SINK (list->data)) { - priv->src = _setup_cothread (sched, element, setup_chain); - break; - } - } - if (!priv->src) - priv->src = _setup_cothread (sched, element, setup_get); - element->sched_private = priv->src; - } - } - GST_RPAD_GETHANDLER (srcpad) = gst_entry_scheduler_get_handler; - GST_RPAD_EVENTHANDLER (srcpad) = gst_entry_scheduler_event_handler; - GST_REAL_PAD (srcpad)->sched_private = priv; - /* wrap sinkpad */ - element = gst_pad_get_parent (sinkpad); - priv->sinkpad = GST_REAL_PAD (sinkpad); - if (GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED)) { - priv->sink = _setup_cothread (sched, element, setup_chain); - } else { - priv->sink = ELEMENT_PRIVATE (element); - if (priv->sink) { - /* LOOP or CHAIN */ - g_assert (priv->sink->main != gst_entry_scheduler_get_wrapper); - } else { - priv->sink = _setup_cothread (sched, element, setup_chain); - element->sched_private = priv->sink; - } - } - GST_RPAD_CHAINHANDLER (sinkpad) = gst_entry_scheduler_chain_handler; - GST_RPAD_EVENTHANDLER (sinkpad) = gst_entry_scheduler_event_handler; - GST_REAL_PAD (sinkpad)->sched_private = priv; - - sched->schedule_possible = g_list_prepend (sched->schedule_possible, priv); -} - -static void -gst_entry_scheduler_pad_unlink (GstScheduler * scheduler, GstPad * srcpad, - GstPad * sinkpad) -{ - GstEntryScheduler *sched = GST_ENTRY_SCHEDULER (scheduler); - LinkPrivate *priv; - GstElement *element; - - priv = PAD_PRIVATE (srcpad); - /* wrap srcpad */ - element = gst_pad_get_parent (srcpad); - if (GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED)) - _remove_cothread (priv->src); - GST_RPAD_GETHANDLER (srcpad) = NULL; - GST_RPAD_EVENTHANDLER (srcpad) = NULL; - GST_REAL_PAD (srcpad)->sched_private = NULL; - /* wrap sinkpad */ - element = gst_pad_get_parent (sinkpad); - if (GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED)) - _remove_cothread (priv->sink); - GST_RPAD_CHAINHANDLER (sinkpad) = NULL; - GST_RPAD_EVENTHANDLER (sinkpad) = NULL; - GST_REAL_PAD (sinkpad)->sched_private = NULL; - - if (priv->bufpen) { - GST_WARNING_OBJECT (sched, - "found data in bufpen while unlinking %s:%s and %s:%s, discarding", - GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad)); - gst_data_unref (priv->bufpen); - } - sched->schedule_now = g_list_remove (sched->schedule_now, priv); - sched->schedule_possible = g_list_remove (sched->schedule_possible, priv); - g_free (priv); -} - -static GstSchedulerState -gst_entry_scheduler_iterate (GstScheduler * scheduler) -{ - GstEntryScheduler *sched = GST_ENTRY_SCHEDULER (scheduler); - GList *entries = sched->schedule_possible; - GstSchedulerState ret = GST_SCHEDULER_STATE_STOPPED; - - GST_LOG_OBJECT (sched, "starting iteration in bin %s", - GST_ELEMENT_NAME (scheduler->parent)); - sched->error = FALSE; - - if (sched->schedule_now) { - ret = GST_SCHEDULER_STATE_RUNNING; - } else { - while (entries) { - if (can_schedule ((Entry *) entries->data)) { - Entry *entry = entries->data; - - ret = GST_SCHEDULER_STATE_RUNNING; - sched->schedule_now = g_list_prepend (sched->schedule_now, entry); - sched->schedule_possible = - g_list_remove (sched->schedule_possible, entry); - sched->schedule_possible = - g_list_append (sched->schedule_possible, entry); - break; - } - entries = g_list_next (entries); - } - } - if (ret == GST_SCHEDULER_STATE_RUNNING) - schedule_next_element (sched); - if (sched->error || sched->schedule_now) { - GST_ERROR_OBJECT (sched, "returning error because of %s", - sched->error ? "element error" : "unschedulable elements"); -#if 0 - gst_entry_scheduler_show (scheduler); -#endif - return GST_SCHEDULER_STATE_ERROR; -#if 0 - } else if (GST_STATE (GST_SCHEDULER (sched)->parent) == GST_STATE_PLAYING && - ret == GST_SCHEDULER_STATE_STOPPED && scheduler->schedulers == NULL) { - GST_ERROR_OBJECT (sched, - "returning error because we contain running elements and we didn't do a thing"); - gst_entry_scheduler_show (scheduler); - return GST_SCHEDULER_STATE_ERROR; -#endif - } else if (ret == GST_SCHEDULER_STATE_STOPPED) { - GST_INFO_OBJECT (sched, "done iterating returning STOPPED"); - return GST_SCHEDULER_STATE_STOPPED; - } else { - return ret; - } -} - -static const gchar * -print_state (CothreadPrivate * priv) -{ - switch (priv->wait) { - case WAIT_FOR_NOTHING: - return "runnable"; - case WAIT_FOR_PADS: - return "waiting for pads"; - case WAIT_FOR_ANYTHING: - case WAIT_FOR_MUM: - default: - g_assert_not_reached (); - } - return ""; -} - -static void -print_entry (GstEntryScheduler * sched, Entry * entry) -{ - if (ENTRY_IS_LINK (entry)) { - LinkPrivate *link = (LinkPrivate *) entry; - - g_print (" %s", can_schedule (entry) ? "OK" : " "); - g_print (" %s:%s%s =>", GST_DEBUG_PAD_NAME (link->srcpad), - can_schedule_pad (link->srcpad) ? " (active)" : ""); - g_print (" %s:%s%s", GST_DEBUG_PAD_NAME (link->sinkpad), - can_schedule_pad (link->sinkpad) ? " (active)" : ""); - g_print ("%s\n", link->bufpen ? " FILLED" : ""); -/* g_print (" %s %s:%s%s => %s:%s%s%s\n", can_schedule (entry) ? "OK" : " ", GST_DEBUG_PAD_NAME (link->srcpad), - link->src->can_schedule (link->srcpad) ? " (active)" : "", - GST_DEBUG_PAD_NAME (link->sink), - link->sink->can_schedule (link->sinkpad) ? "(active) " : "", - link->bufpen ? " FILLED" : ""); -*/ } else if (ENTRY_IS_COTHREAD (entry)) { - CothreadPrivate *priv = (CothreadPrivate *) entry; - - g_print (" %s %s (%s)\n", can_schedule (entry) ? "OK" : " ", - GST_ELEMENT_NAME (priv->element), print_state (priv)); - } else { - g_assert_not_reached (); - } -} - -static void -gst_entry_scheduler_show (GstScheduler * scheduler) -{ - GstEntryScheduler *sched = GST_ENTRY_SCHEDULER (scheduler); - GList *list; - - g_print ("entry points waiting:\n"); - for (list = sched->waiting; list; list = g_list_next (list)) { - print_entry (sched, (Entry *) list->data); - } - g_print ("entry points to schedule now:\n"); - for (list = sched->schedule_now; list; list = g_list_next (list)) { - print_entry (sched, (Entry *) list->data); - } - g_print ("entry points that might be scheduled:\n"); - for (list = sched->schedule_possible; list; list = g_list_next (list)) { - print_entry (sched, (Entry *) list->data); - } -} - -static gboolean -plugin_init (GstPlugin * plugin) -{ - if (!gst_scheduler_register (plugin, "entry" COTHREADS_NAME, - "A entry scheduler using " COTHREADS_NAME " cothreads", - GST_TYPE_ENTRY_SCHEDULER)) - return FALSE; - - GST_DEBUG_CATEGORY_INIT (debug_scheduler, "entry" COTHREADS_NAME, 0, - "entry " COTHREADS_NAME "scheduler"); - - return TRUE; -} - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, "gstentry" COTHREADS_NAME "scheduler", "an entry scheduler using " COTHREADS_NAME " cothreads", /* FIXME */ - plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN) diff --git a/gst/schedulers/faircothreads.c b/gst/schedulers/faircothreads.c deleted file mode 100644 index 721b199aa8..0000000000 --- a/gst/schedulers/faircothreads.c +++ /dev/null @@ -1,612 +0,0 @@ -/* GStreamer - * Copyright (C) 2004 Martin Soto - * - * faircothread.c: High level cothread implementation for the fair scheduler. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include - -#include - -#ifdef _COTHREADS_PTH -#include "pth-cothreads.h" -#else -#include "cothreads_compat.h" -#endif - -#include "faircothreads.h" - -#if !defined(GST_DISABLE_GST_DEBUG) && defined(FAIRSCHEDULER_USE_GETTID) -#include -#include - -_syscall0 (pid_t, gettid) -#endif - GST_DEBUG_CATEGORY_EXTERN (debug_fair_ct); -#define GST_CAT_DEFAULT debug_fair_ct - - -/* - * Support for Asynchronous Operations - */ - - enum - { - ASYNC_OP_CHANGE_STATE = 1, - ASYNC_OP_AWAKE - }; - - typedef struct _AsyncOp AsyncOp; - typedef struct _AsyncOpChangeState AsyncOpChangeState; - typedef struct _AsyncOpAwake AsyncOpAwake; - - struct _AsyncOp - { - int type; - }; - - struct _AsyncOpChangeState - { - AsyncOp parent; - GstFairSchedulerCothread *ct; /* Cothread whose state will be - changed. */ - gint new_state; /* New state for the cothread. */ - }; - - struct _AsyncOpAwake - { - AsyncOp parent; - GstFairSchedulerCothread *ct; /* Cothread to awake. */ - gint priority; /* Priority for the cothread. */ - }; - - - static gchar *gst_fairscheduler_ct_state_names[] = { - "stopped", - "suspended", - "running" - }; - - -/* - * Helpers - */ - -static int -cothread_base_func (int argc, char **argv) -{ - GstFairSchedulerCothread *ct; - - g_return_val_if_fail (argc >= 1, -1); - - ct = (GstFairSchedulerCothread *) argv[0]; - - GST_INFO ("queue %p: Cothread %p starting", ct->queue, ct); -#ifndef GST_DISABLE_GST_DEBUG -#ifdef FAIRSCHEDULER_USE_GETTID - ct->pid = gettid (); -#else - ct->pid = 0; -#endif -#endif - - /* Call the thread function. This looks sort of funny, but there's - no other way I know of doing it. */ - switch (argc - 1) { - case 0: - ct->func (ct, NULL); - break; - case 1: - ct->func (ct, argv[1], NULL); - break; - case 2: - ct->func (ct, argv[1], argv[2], NULL); - break; - case 3: - ct->func (ct, argv[1], argv[2], argv[3], NULL); - break; - case 4: - ct->func (ct, argv[1], argv[2], argv[3], argv[4], NULL); - break; - case 5: - ct->func (ct, argv[1], argv[2], argv[3], argv[4], argv[5], NULL); - break; - case 6: - ct->func (ct, argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], NULL); - break; - case 7: - ct->func (ct, argv[1], argv[2], argv[3], argv[4], argv[5], - argv[6], argv[7], NULL); - break; - default: - g_return_val_if_reached (-1); - break; - } - - /* After the cothread function is finished, we go to the stopped - state. */ - gst_fair_scheduler_cothread_change_state (ct, - GST_FAIRSCHEDULER_CTSTATE_STOPPED); - - return 0; -} - - -static void -cothread_activate (GstFairSchedulerCothread * ct, gint priority) -{ - GST_DEBUG ("queue %p: activating cothread %p", ct->queue, ct); - - if (priority > 0) { - g_queue_push_head (ct->queue->ct_queue, ct); - } else { - g_queue_push_tail (ct->queue->ct_queue, ct); - } -} - - -static void -cothread_deactivate (GstFairSchedulerCothread * ct) -{ - GList *node; - - GST_DEBUG ("queue %p: deactivating cothread %p", ct->queue, ct); - - /* Find the node. */ - node = g_list_find (ct->queue->ct_queue->head, ct); - if (node == NULL) { - return; - } - - if (node->next == NULL) { - g_queue_pop_tail (ct->queue->ct_queue); - } else { - ct->queue->ct_queue->head = - g_list_remove_link (ct->queue->ct_queue->head, node); - } -} - - -static void -queue_async_op (GstFairSchedulerCothreadQueue * queue, AsyncOp * op) -{ - g_mutex_lock (queue->async_mutex); - g_queue_push_tail (queue->async_queue, op); - g_cond_signal (queue->new_async_op); - g_mutex_unlock (queue->async_mutex); -} - - -/* - * Cothreads API - */ - -extern GstFairSchedulerCothreadQueue * -gst_fair_scheduler_cothread_queue_new (void) -{ - GstFairSchedulerCothreadQueue *new; - - new = g_malloc (sizeof (GstFairSchedulerCothreadQueue)); - - new->context = NULL; - new->ct_queue = g_queue_new (); - - new->async_queue = g_queue_new (); - new->async_mutex = g_mutex_new (); - new->new_async_op = g_cond_new (); - - return new; -} - - -extern void -gst_fair_scheduler_cothread_queue_destroy (GstFairSchedulerCothreadQueue * - queue) -{ - GList *iter; - - /* Destroy all remaining cothreads. */ - for (iter = queue->ct_queue->head; iter != NULL; iter = iter->next) { - gst_fair_scheduler_cothread_destroy ( - (GstFairSchedulerCothread *) iter->data); - } - g_queue_free (queue->ct_queue); - - for (iter = queue->async_queue->head; iter != NULL; iter = iter->next) { - g_free (iter->data); - } - g_queue_free (queue->async_queue); - - g_mutex_free (queue->async_mutex); - g_cond_free (queue->new_async_op); - - g_free (queue); -} - - -extern void -gst_fair_scheduler_cothread_queue_start (GstFairSchedulerCothreadQueue * queue) -{ - if (queue->context == NULL) { - do_cothreads_init (NULL); - queue->context = do_cothread_context_init (); - } -} - - -extern void -gst_fair_scheduler_cothread_queue_stop (GstFairSchedulerCothreadQueue * queue) -{ - if (queue->context != NULL) { - do_cothread_context_destroy (queue->context); - } -} - - -gboolean -gst_fair_scheduler_cothread_queue_iterate (GstFairSchedulerCothreadQueue * - queue) -{ - GstFairSchedulerCothread *ct; - - g_return_val_if_fail (queue->context != NULL, FALSE); - - GST_LOG ("queue %p: iterating", queue); - - /* Perform any pending asynchronous operations. Checking the queue - is safe and more efficient without locking the mutex. */ - if (!g_queue_is_empty (queue->async_queue)) { - AsyncOp *basic_op; - - GST_LOG ("queue %p: processing asynchronous operations", queue); - - g_mutex_lock (queue->async_mutex); - - while (!g_queue_is_empty (queue->async_queue)) { - basic_op = (AsyncOp *) g_queue_pop_head (queue->async_queue); - - switch (basic_op->type) { - case ASYNC_OP_CHANGE_STATE: - { - AsyncOpChangeState *op = (AsyncOpChangeState *) basic_op; - - gst_fair_scheduler_cothread_change_state (op->ct, op->new_state); - } - break; - case ASYNC_OP_AWAKE: - { - AsyncOpAwake *op = (AsyncOpAwake *) basic_op; - - gst_fair_scheduler_cothread_awake (op->ct, op->priority); - } - break; - default: - g_return_val_if_reached (FALSE); - break; - } - - g_free (basic_op); - } - - g_mutex_unlock (queue->async_mutex); - } - - /* First cothread in the queue (if any) should get control. */ - ct = g_queue_peek_head (queue->ct_queue); - - if (ct == NULL) { - GTimeVal timeout; - - g_get_current_time (&timeout); - g_time_val_add (&timeout, 5000); - - /* No cothread available, wait until some other thread queues an - operation. */ - g_mutex_lock (queue->async_mutex); - g_cond_timed_wait (queue->new_async_op, queue->async_mutex, &timeout); - g_mutex_unlock (queue->async_mutex); - - return FALSE; - } - - g_return_val_if_fail (ct->state == GST_FAIRSCHEDULER_CTSTATE_RUNNING, FALSE); - - /* Check for a cothread mutex. */ - if (ct->mutex != NULL) { - g_mutex_lock (ct->mutex); - ct->mutex = NULL; - } - - GST_LOG ("queue %p: giving control to %p", queue, ct); - - /* Handle control to the cothread. */ - do_cothread_switch (ct->execst); - - return TRUE; -} - - -void -gst_fair_scheduler_cothread_queue_show (GstFairSchedulerCothreadQueue * queue) -{ - GList *iter; - GstFairSchedulerCothread *ct; - - g_print ("\n Running cothreads (last is active):\n"); - - for (iter = queue->ct_queue->tail; iter != NULL; iter = iter->prev) { - ct = (GstFairSchedulerCothread *) iter->data; - - g_print (" %p: %s (%d)\n", ct, ct->readable_name->str, ct->pid); - } -} - - -GstFairSchedulerCothread * -gst_fair_scheduler_cothread_new (GstFairSchedulerCothreadQueue * queue, - GstFairSchedulerCtFunc function, gpointer first_arg, ...) -{ - GstFairSchedulerCothread *new; - va_list ap; - gpointer arg; - - new = g_malloc (sizeof (GstFairSchedulerCothread)); - - new->queue = queue; - new->func = function; - - /* The first parameter is always the cothread structure itself. */ - new->argv[0] = (char *) new; - new->argc = 1; - - /* Store the parameters. */ - va_start (ap, first_arg); - arg = first_arg; - while (new->argc < GST_FAIRSCHEDULER_MAX_CTARGS && arg != NULL) { - new->argv[new->argc] = (char *) arg; - new->argc++; - arg = va_arg (ap, gpointer); - } - - /* Make sure we don't have more parameters than we can handle. */ - g_return_val_if_fail (arg == NULL, NULL); - - /* Creation of the actual execution state is defered to transition - to running/suspended. */ - new->execst = NULL; - - /* All cothreads are created in the stopped state. */ - new->state = GST_FAIRSCHEDULER_CTSTATE_STOPPED; - - new->mutex = NULL; - -#ifndef GST_DISABLE_GST_DEBUG - new->readable_name = g_string_new (""); - new->pid = 0; -#endif - - GST_DEBUG ("queue %p: cothread %p created", queue, new); - - return new; -} - - -void -gst_fair_scheduler_cothread_destroy (GstFairSchedulerCothread * ct) -{ - GST_DEBUG ("queue %p: destroying cothread %p", ct->queue, ct); - - if (ct->state != GST_FAIRSCHEDULER_CTSTATE_STOPPED) { - cothread_deactivate (ct); - } - - if (ct->execst != NULL) { - do_cothread_destroy (ct->execst); - } -#ifndef GST_DISABLE_GST_DEBUG - g_string_free (ct->readable_name, TRUE); -#endif - - g_free (ct); -} - - -void -gst_fair_scheduler_cothread_change_state (GstFairSchedulerCothread * ct, - gint new_state) -{ - if (new_state == ct->state) { - return; - } - - GST_DEBUG ("queue %p: changing state of %p from %s to %s", ct->queue, ct, - gst_fairscheduler_ct_state_names[ct->state], - gst_fairscheduler_ct_state_names[new_state]); - - switch (ct->state) { - case GST_FAIRSCHEDULER_CTSTATE_STOPPED: - /* (Re)Initialize the cothread. */ - if (ct->execst == NULL) { - /* Initialize cothread's execution state. */ - do_cothread_create (ct->execst, ct->queue->context, - cothread_base_func, ct->argc, ct->argv); - GST_LOG_OBJECT (ct->queue, - "cothread %p has exec state %p", ct, ct->execst); - } else { - /* Reset cothread's execution state. */ - do_cothread_setfunc (ct->execst, ct->queue->context, - cothread_base_func, ct->argc, ct->argv); - } - - ct->sleeping = FALSE; - - if (new_state == GST_FAIRSCHEDULER_CTSTATE_RUNNING) { - cothread_activate (ct, 0); - } - - break; - - case GST_FAIRSCHEDULER_CTSTATE_RUNNING: - if (!ct->sleeping) { - cothread_deactivate (ct); - } - break; - - case GST_FAIRSCHEDULER_CTSTATE_SUSPENDED: - if (new_state == GST_FAIRSCHEDULER_CTSTATE_RUNNING && !ct->sleeping) { - cothread_activate (ct, 0); - } - break; - } - - ct->state = new_state; -} - - -void -gst_fair_scheduler_cothread_change_state_async (GstFairSchedulerCothread * ct, - gint new_state) -{ - AsyncOpChangeState *op; - - /* Queue an asynchronous operation. */ - op = g_new (AsyncOpChangeState, 1); - op->parent.type = ASYNC_OP_CHANGE_STATE; - op->ct = ct; - op->new_state = new_state; - - queue_async_op (ct->queue, (AsyncOp *) op); -} - - -void -gst_fair_scheduler_cothread_sleep (GstFairSchedulerCothreadQueue * queue) -{ - gst_fair_scheduler_cothread_sleep_mutex (queue, NULL); -} - - -/* - * Go to sleep but unblock the mutex while sleeping. - */ -void -gst_fair_scheduler_cothread_sleep_mutex (GstFairSchedulerCothreadQueue * queue, - GMutex * mutex) -{ - GstFairSchedulerCothread *ct; - - g_return_if_fail (queue->context != NULL); - - /* The sleep operation can be invoked when the cothread is already - deactivated. */ - ct = gst_fair_scheduler_cothread_current (queue); - if (ct != NULL && ct->execst == do_cothread_get_current (queue->context)) { - ct = g_queue_pop_head (queue->ct_queue); - ct->sleeping = TRUE; - } - - ct->mutex = mutex; - if (mutex != NULL) { - g_mutex_unlock (mutex); - } - - GST_LOG ("queue %p: cothread going to sleep", queue); - - /* Switch back to the main cothread. */ - do_cothread_switch (do_cothread_get_main (queue->context)); -} - - -void -gst_fair_scheduler_cothread_yield (GstFairSchedulerCothreadQueue * queue) -{ - gst_fair_scheduler_cothread_yield_mutex (queue, NULL); -} - - -void -gst_fair_scheduler_cothread_yield_mutex (GstFairSchedulerCothreadQueue * queue, - GMutex * mutex) -{ - GstFairSchedulerCothread *ct; - - g_return_if_fail (queue->context != NULL); - - /* The yield operation can be invoked when the cothread is already - deactivated. */ - ct = gst_fair_scheduler_cothread_current (queue); - if (ct != NULL && ct->execst == do_cothread_get_current (queue->context)) { - ct = g_queue_pop_head (queue->ct_queue); - g_queue_push_tail (queue->ct_queue, ct); - } - - ct->mutex = mutex; - if (mutex != NULL) { - g_mutex_unlock (mutex); - } - - GST_LOG ("queue %p: cothread yielding control", queue); - - /* Switch back to the main cothread. */ - do_cothread_switch (do_cothread_get_main (queue->context)); -} - - -void -gst_fair_scheduler_cothread_awake (GstFairSchedulerCothread * ct, gint priority) -{ - g_return_if_fail (ct->state != GST_FAIRSCHEDULER_CTSTATE_STOPPED); - - if (!ct->sleeping) { - /* Cothread is already awake. */ - return; - } - - ct->sleeping = FALSE; - - if (ct->state == GST_FAIRSCHEDULER_CTSTATE_RUNNING) { - cothread_activate (ct, priority); - } -} - - -void -gst_fair_scheduler_cothread_awake_async (GstFairSchedulerCothread * ct, - gint priority) -{ - AsyncOpAwake *op; - - /* Queue an asynchronous operation. */ - op = g_new (AsyncOpAwake, 1); - op->parent.type = ASYNC_OP_AWAKE; - op->ct = ct; - op->priority = priority; - - queue_async_op (ct->queue, (AsyncOp *) op); -} - - -GstFairSchedulerCothread * -gst_fair_scheduler_cothread_current (GstFairSchedulerCothreadQueue * queue) -{ - return g_queue_peek_head (queue->ct_queue); -} diff --git a/gst/schedulers/faircothreads.h b/gst/schedulers/faircothreads.h deleted file mode 100644 index 98faeca730..0000000000 --- a/gst/schedulers/faircothreads.h +++ /dev/null @@ -1,163 +0,0 @@ -/* GStreamer - * Copyright (C) 2004 Martin Soto - * - * faircothread.h: High level cothread implementation for the fair scheduler. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __FAIRCOTHREADS_H__ -#define __FAIRCOTHREADS_H__ - - -#ifdef _COTHREADS_PTH -#include "pth-cothreads.h" -#else -#define GTHREAD_COTHREADS_NO_DEFINITIONS -#include "cothreads_compat.h" -#endif - - -typedef struct _GstFairSchedulerCothread GstFairSchedulerCothread; -typedef struct _GstFairSchedulerCothreadQueue GstFairSchedulerCothreadQueue; - -/* Possible states of a cothread. */ -enum -{ - GST_FAIRSCHEDULER_CTSTATE_STOPPED, - GST_FAIRSCHEDULER_CTSTATE_SUSPENDED, - GST_FAIRSCHEDULER_CTSTATE_RUNNING, -}; - -/* Maximum number of cothread parameters. */ -#define GST_FAIRSCHEDULER_MAX_CTARGS 7 - -/* Cothread function type. */ -typedef void (*GstFairSchedulerCtFunc) (GstFairSchedulerCothread * ct, - gpointer first_arg, ...); - -struct _GstFairSchedulerCothread { - GstFairSchedulerCothreadQueue *queue; - /* Cothread queue this cothread - belongs to. */ - GstFairSchedulerCtFunc func; /* Cothread function. */ - char *argv[1 + GST_FAIRSCHEDULER_MAX_CTARGS]; /* - Arguments for the cothread function. - argv[0] is always the cothread - object itself. */ - int argc; /* Number of stored parameters. */ - - cothread *execst; /* Execution state for this cothread. */ - gint state; /* Current cothread state. */ - gboolean sleeping; /* Is this cothread sleeping? */ - - GMutex *mutex; /* If not null, a mutex to lock before - giving control to this cothread. */ - -#ifndef GST_DISABLE_GST_DEBUG - GString *readable_name; /* Readable name for this cothread. */ - gint pid; /* Process or thread id associated to - this cothread. */ -#endif -}; - -struct _GstFairSchedulerCothreadQueue { - cothread_context *context; /* Cothread context. */ - GQueue *ct_queue; /* Queue of currently running - cothreads. New cothreads are pushed - on the tail. If a cothread is - executing, it is the one in the - head. */ - - /* Asynchronous support. */ - GQueue *async_queue; /* Queue storing asynchronous - operations (operations on the queue - requested potentially from other - threads. */ - GMutex *async_mutex; /* Mutex to protect acceses to - async_queue. */ - GCond *new_async_op; /* Condition variable to signal the - presence of a new asynchronous - operation in the queue. */ -}; - - -extern GstFairSchedulerCothreadQueue * -gst_fair_scheduler_cothread_queue_new (void); - -extern void -gst_fair_scheduler_cothread_queue_destroy ( - GstFairSchedulerCothreadQueue * queue); - -extern void -gst_fair_scheduler_cothread_queue_start ( - GstFairSchedulerCothreadQueue * queue); - -extern void -gst_fair_scheduler_cothread_queue_stop ( - GstFairSchedulerCothreadQueue * queue); - -extern gboolean -gst_fair_scheduler_cothread_queue_iterate ( - GstFairSchedulerCothreadQueue * queue); - -extern void -gst_fair_scheduler_cothread_queue_show ( - GstFairSchedulerCothreadQueue * queue); - - -extern GstFairSchedulerCothread * -gst_fair_scheduler_cothread_new (GstFairSchedulerCothreadQueue * queue, - GstFairSchedulerCtFunc function, gpointer first_arg, ...); - -extern void -gst_fair_scheduler_cothread_destroy (GstFairSchedulerCothread * ct); - -extern void -gst_fair_scheduler_cothread_change_state (GstFairSchedulerCothread * ct, - gint new_state); - -extern void -gst_fair_scheduler_cothread_change_state_async ( - GstFairSchedulerCothread * ct, gint new_state); - -extern void -gst_fair_scheduler_cothread_sleep (GstFairSchedulerCothreadQueue * queue); - -extern void -gst_fair_scheduler_cothread_sleep_mutex ( - GstFairSchedulerCothreadQueue * queue, GMutex * mutex); - -extern void -gst_fair_scheduler_cothread_yield (GstFairSchedulerCothreadQueue * queue); - -extern void -gst_fair_scheduler_cothread_yield_mutex ( - GstFairSchedulerCothreadQueue * queue, GMutex * mutex); - -extern void -gst_fair_scheduler_cothread_awake (GstFairSchedulerCothread * ct, - gint priority); - -extern void -gst_fair_scheduler_cothread_awake_async (GstFairSchedulerCothread * ct, - gint priority); - -extern GstFairSchedulerCothread * -gst_fair_scheduler_cothread_current (GstFairSchedulerCothreadQueue * queue); - - -#endif /* __FAIRCOTHREADS_H__ */ diff --git a/gst/schedulers/fairscheduler.c b/gst/schedulers/fairscheduler.c deleted file mode 100644 index a395646faa..0000000000 --- a/gst/schedulers/fairscheduler.c +++ /dev/null @@ -1,1405 +0,0 @@ -/* GStreamer - * Copyright (C) 2004 Martin Soto - * - * gstfairscheduler.c: Fair cothread based scheduler - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include - -#include -#include - -#include "faircothreads.h" - - -GST_DEBUG_CATEGORY_STATIC (debug_fair); -#define GST_CAT_DEFAULT debug_fair -GST_DEBUG_CATEGORY (debug_fair_ct); -GST_DEBUG_CATEGORY_STATIC (debug_fair_queues); - - -typedef struct _GstFairScheduler GstFairScheduler; -typedef struct _GstFairSchedulerClass GstFairSchedulerClass; -typedef struct _GstFairSchedulerPrivElem GstFairSchedulerPrivElem; -typedef struct _GstFairSchedulerPrivLink GstFairSchedulerPrivLink; -typedef struct _GstFairSchedulerWaitEntry GstFairSchedulerWaitEntry; - - -#define GST_TYPE_FAIR_SCHEDULER \ - (gst_fair_scheduler_get_type()) -#define GST_FAIR_SCHEDULER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FAIR_SCHEDULER,GstFairScheduler)) -#define GST_FAIR_SCHEDULER_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FAIR_SCHEDULER,GstFairSchedulerClass)) -#define GST_IS_FAIR_SCHEDULER(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FAIR_SCHEDULER)) -#define GST_IS_FAIR_SCHEDULER_CLASS(obj) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FAIR_SCHEDULER)) - - -/* Private scheduler data associated to an element. */ -struct _GstFairSchedulerPrivElem -{ - GstFairSchedulerCothread *elem_ct; - /* Element's cothread. */ - GArray *chain_get_pads; /* Pads in this element with either a - get or a chain function. */ -}; - -#define ELEM_PRIVATE(element) \ - ((GstFairSchedulerPrivElem *) GST_ELEMENT(element)->sched_private) - - -/* Private scheduler data associated to a pad link. This structure is - always stored in the source pad of the link. */ -struct _GstFairSchedulerPrivLink -{ - GstFairScheduler *owner; /* The "owner" of this link. */ - - GstData *bufpen; /* A placeholder for one buffer. */ - GstFairSchedulerCothread *waiting_writer; - /* Cothread waiting to write. */ - GstFairSchedulerCothread *waiting_reader; - /* Cothread waiting to read. */ - - GstFairSchedulerCothread *decoupled_ct; - /* Cothread to handle the decoupled - pad in this link (if any). */ - gulong decoupled_signal_id; /* Id for the signal handler - responsible for managing the - cothread. */ - - /* Queue optimizations. */ - gulong queue_blocked_signal_id; - /* Id for the signal handler connected - to the under/overrun signal of a - queue. */ - GstFairSchedulerCothread *waiting_for_queue; - /* Cothread waiting for a queue to - unblock. */ -}; - -#define LINK_PRIVATE(pad) \ - ((GstFairSchedulerPrivLink *) \ - (GST_PAD_IS_SRC (pad) ? \ - GST_REAL_PAD(pad)->sched_private : \ - GST_RPAD_PEER (GST_REAL_PAD(pad))->sched_private)) - - -/* An entry in the clock wait list. */ -struct _GstFairSchedulerWaitEntry -{ - GstFairSchedulerCothread *ct; /* The waiting cothread. */ - GstClockTime time; /* The clock time it should wake up - on. */ -}; - - -struct _GstFairScheduler -{ - GstScheduler parent; - - GstFairSchedulerCothreadQueue *cothreads; - /* The queue handling the cothreads - for the scheduler. */ - - /* Scheduling control. */ - gboolean in_element; /* True if we are running element - code. */ - - /* Clock wait support. */ - GSList *waiting; /* List of waiting cothreads. Elements - are GstFairSchedulerWaitEntry - structures. */ - - /* Timing statistics. */ - GTimer *iter_timer; /* Iteration timer. */ - guint iter_count; /* Iteration count. */ - -#ifndef GST_DISABLE_GST_DEBUG - GList *elements; /* List of all registered elements - (needed only for debugging. */ - GList *sources; /* List of all source pads involved in - registered links (needed only for - debugging. */ -#endif -}; - - -struct _GstFairSchedulerClass -{ - GstSchedulerClass parent_class; -}; - - -static GType _gst_fair_scheduler_type = 0; - - -enum -{ - ARG_0, -}; - - -/* Standard GObject Operations */ - -static GType gst_fair_scheduler_get_type (void); - -static void gst_fair_scheduler_class_init (GstFairSchedulerClass * klass); - -static void gst_fair_scheduler_init (GObject * object); - -static void gst_fair_scheduler_dispose (GObject * object); - -static void -gst_fair_scheduler_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); - -static void -gst_fair_scheduler_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); - - -/* Cothread Function Wrappers */ - -static void -gst_fair_scheduler_loop_wrapper (GstFairSchedulerCothread * ct, - GstElement * element); - - -/* Chain, Get and Event Handlers */ - -static void gst_fair_scheduler_chain_handler (GstPad * pad, GstData * data); - -static GstData *gst_fair_scheduler_get_handler (GstPad * pad); - - -/* GstScheduler Operations */ - -static void gst_fair_scheduler_setup (GstScheduler * sched); - -static void gst_fair_scheduler_reset (GstScheduler * sched); - -static void -gst_fair_scheduler_add_element (GstScheduler * sched, GstElement * element); - -static void -gst_fair_scheduler_remove_element (GstScheduler * sched, GstElement * element); - -static GstElementStateReturn -gst_fair_scheduler_state_transition (GstScheduler * sched, - GstElement * element, gint transition); - -static void -decoupled_state_transition (GstElement * element, gint old_state, - gint new_state, gpointer user_data); - -static void -gst_fair_scheduler_scheduling_change (GstScheduler * sched, - GstElement * element); - -static gboolean -gst_fair_scheduler_yield (GstScheduler * sched, GstElement * element); - -static gboolean -gst_fair_scheduler_interrupt (GstScheduler * sched, GstElement * element); - -static void -gst_fair_scheduler_error (GstScheduler * sched, GstElement * element); - -static void -gst_fair_scheduler_pad_link (GstScheduler * sched, GstPad * srcpad, - GstPad * sinkpad); - -static void -gst_fair_scheduler_pad_unlink (GstScheduler * sched, GstPad * srcpad, - GstPad * sinkpad); - -static GstData *gst_fair_scheduler_pad_select (GstScheduler * sched, - GstPad ** pulled_from, GstPad ** pads); - -static GstClockReturn -gst_fair_scheduler_clock_wait (GstScheduler * sched, GstElement * element, - GstClockID id, GstClockTimeDiff * jitter); - -static GstSchedulerState gst_fair_scheduler_iterate (GstScheduler * sched); - -static void gst_fair_scheduler_show (GstScheduler * sched); - - -static GstSchedulerClass *parent_class = NULL; - - -/* - * Standard GObject Operations - */ - -static GType -gst_fair_scheduler_get_type (void) -{ - if (!_gst_fair_scheduler_type) { - static const GTypeInfo scheduler_info = { - sizeof (GstFairSchedulerClass), - NULL, - NULL, - (GClassInitFunc) gst_fair_scheduler_class_init, - NULL, - NULL, - sizeof (GstFairScheduler), - 0, - (GInstanceInitFunc) gst_fair_scheduler_init, - NULL - }; - - _gst_fair_scheduler_type = g_type_register_static (GST_TYPE_SCHEDULER, - "GstFair" COTHREADS_NAME_CAPITAL "Scheduler", &scheduler_info, 0); - } - return _gst_fair_scheduler_type; -} - - -static void -gst_fair_scheduler_class_init (GstFairSchedulerClass * klass) -{ - GObjectClass *gobject_class; - GstObjectClass *gstobject_class; - GstSchedulerClass *gstscheduler_class; - - gobject_class = (GObjectClass *) klass; - gstobject_class = (GstObjectClass *) klass; - gstscheduler_class = (GstSchedulerClass *) klass; - - parent_class = g_type_class_ref (GST_TYPE_SCHEDULER); - - gobject_class->set_property = gst_fair_scheduler_set_property; - gobject_class->get_property = gst_fair_scheduler_get_property; - gobject_class->dispose = gst_fair_scheduler_dispose; - - gstscheduler_class->setup = gst_fair_scheduler_setup; - gstscheduler_class->reset = gst_fair_scheduler_reset; - gstscheduler_class->add_element = gst_fair_scheduler_add_element; - gstscheduler_class->remove_element = gst_fair_scheduler_remove_element; - gstscheduler_class->state_transition = gst_fair_scheduler_state_transition; - gstscheduler_class->scheduling_change = gst_fair_scheduler_scheduling_change; - gstscheduler_class->yield = gst_fair_scheduler_yield; - gstscheduler_class->interrupt = gst_fair_scheduler_interrupt; - gstscheduler_class->error = gst_fair_scheduler_error; - gstscheduler_class->pad_link = gst_fair_scheduler_pad_link; - gstscheduler_class->pad_unlink = gst_fair_scheduler_pad_unlink; - gstscheduler_class->pad_select = gst_fair_scheduler_pad_select; - gstscheduler_class->clock_wait = gst_fair_scheduler_clock_wait; - gstscheduler_class->iterate = gst_fair_scheduler_iterate; - gstscheduler_class->show = gst_fair_scheduler_show; -} - - -static void -gst_fair_scheduler_init (GObject * object) -{ - GstFairScheduler *fsched = GST_FAIR_SCHEDULER (object); - - fsched->cothreads = gst_fair_scheduler_cothread_queue_new (); - - /* Proudly suporting the select operation since 2004! */ - GST_FLAG_SET (fsched, GST_SCHEDULER_FLAG_NEW_API); - - fsched->in_element = FALSE; - - fsched->waiting = NULL; - - fsched->iter_timer = g_timer_new (); - -#ifndef GST_DISABLE_GST_DEBUG - fsched->elements = NULL; - fsched->sources = NULL; -#endif -} - - -static void -gst_fair_scheduler_dispose (GObject * object) -{ - GstFairScheduler *fsched = GST_FAIR_SCHEDULER (object); - - GST_WARNING_OBJECT (fsched, "disposing"); - - g_slist_free (fsched->waiting); - - g_timer_destroy (fsched->iter_timer); - - gst_fair_scheduler_cothread_queue_destroy (fsched->cothreads); - -#ifndef GST_DISABLE_GST_DEBUG - g_list_free (fsched->elements); - g_list_free (fsched->sources); -#endif - - G_OBJECT_CLASS (parent_class)->dispose (object); -} - - -static void -gst_fair_scheduler_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - /*GstFairScheduler *fsched = GST_FAIR_SCHEDULER (object); */ - - switch (prop_id) { - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - - -static void -gst_fair_scheduler_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - /*GstFairScheduler *fsched = GST_FAIR_SCHEDULER (object); */ - - switch (prop_id) { - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - - -/* - * Helpers - */ - -static GstFairSchedulerPrivLink * -get_link_priv (GstPad * pad) -{ - GstFairSchedulerPrivLink *priv; - - GstRealPad *real = GST_PAD_REALIZE (pad); - - if (GST_RPAD_DIRECTION (real) == GST_PAD_SINK) { - real = GST_RPAD_PEER (real); - } - - priv = LINK_PRIVATE (real); - g_return_val_if_fail (priv != NULL, NULL); - - return priv; -} - - -static void -set_cothread_state (GstFairSchedulerCothread * ct, GstElementState state) -{ - guint ct_state; - - switch (state) { - case GST_STATE_PLAYING: - ct_state = GST_FAIRSCHEDULER_CTSTATE_RUNNING; - break; - case GST_STATE_PAUSED: - ct_state = GST_FAIRSCHEDULER_CTSTATE_SUSPENDED; - break; - default: - ct_state = GST_FAIRSCHEDULER_CTSTATE_STOPPED; - break; - } - - gst_fair_scheduler_cothread_change_state_async (ct, ct_state); -} - - -static GstPad * -find_ready_pad (GstPad ** pads) -{ - GstPad *pad; - GstFairSchedulerPrivLink *priv; - int i; - - for (i = 0; pads[i] != NULL; i++) { - pad = pads[i]; - priv = LINK_PRIVATE (pad); - - if (GST_PAD_IS_SRC (pad) && priv->bufpen == NULL) { - return pad; - } else if (GST_PAD_IS_SINK (pad) && priv->bufpen != NULL) { - return pad; - } - } - - return NULL; -} - - -static GstPad * -gst_fair_scheduler_internal_select (GstFairScheduler * fsched, GstPad ** pads) -{ - GstPad *pad; - GstFairSchedulerPrivLink *priv; - int i; - - pad = find_ready_pad (pads); - if (pad == NULL) { - /* Register the current cothread as waiting writer/reader for - every pad on the list. */ - for (i = 0; pads[i] != NULL; i++) { - pad = pads[i]; - priv = LINK_PRIVATE (pad); - - if (GST_PAD_IS_SRC (pad)) { - g_return_val_if_fail (priv->waiting_writer == NULL, NULL); - priv->waiting_writer = - gst_fair_scheduler_cothread_current (fsched->cothreads); - } else { - g_return_val_if_fail (priv->waiting_reader == NULL, NULL); - priv->waiting_reader = - gst_fair_scheduler_cothread_current (fsched->cothreads); - } - } - - /* Sleep until at least one of the pads becomes ready. */ - gst_fair_scheduler_cothread_sleep (fsched->cothreads); - - /* Deregister from all pads. */ - for (i = 0; pads[i] != NULL; i++) { - pad = pads[i]; - priv = LINK_PRIVATE (pad); - - if (GST_PAD_IS_SRC (pad)) { - priv->waiting_writer = NULL; - } else { - priv->waiting_reader = NULL; - } - } - - /* This time it should work. */ - pad = find_ready_pad (pads); - } - - /* At this point, we must have a pad to return. */ - g_return_val_if_fail (pad != NULL, NULL); - - return pad; -} - - -/* - * Cothread Function Wrappers - */ - -static void -gst_fair_scheduler_loop_wrapper (GstFairSchedulerCothread * ct, - GstElement * element) -{ - GST_DEBUG ("Queue %p: entering loop wrapper for '%s'", ct->queue, - GST_OBJECT_NAME (element)); - - g_return_if_fail (element->loopfunc != NULL); - - gst_object_ref (GST_OBJECT (element)); - - while (gst_element_get_state (element) == GST_STATE_PLAYING) { - element->loopfunc (element); - } - - gst_object_unref (GST_OBJECT (element)); - - GST_DEBUG ("Queue %p: leaving loop wrapper for '%s'", ct->queue, - GST_OBJECT_NAME (element)); -} - - -static void -gst_fair_scheduler_chain_get_wrapper (GstFairSchedulerCothread * ct, - GstElement * element) -{ - GstData *data; - GstPad *pad; - GstFairScheduler *fsched = - GST_FAIR_SCHEDULER (gst_element_get_scheduler (element)); - - GST_DEBUG ("Queue %p: entering chain/get wrapper for '%s'", ct->queue, - GST_OBJECT_NAME (element)); - - gst_object_ref (GST_OBJECT (element)); - - while (gst_element_get_state (element) == GST_STATE_PLAYING) { - /* Run a select on the pad list. */ - pad = gst_fair_scheduler_internal_select (fsched, - (GstPad **) ELEM_PRIVATE (element)->chain_get_pads->data); - - if (GST_PAD_IS_SRC (pad)) { - g_return_if_fail (GST_RPAD_GETFUNC (pad) != NULL); - data = gst_pad_call_get_function (pad); - gst_pad_push (pad, data); - } else { - g_return_if_fail (GST_RPAD_CHAINFUNC (pad) != NULL); - data = gst_pad_pull (pad); - gst_pad_call_chain_function (pad, data); - } - } - - gst_object_unref (GST_OBJECT (element)); - - GST_DEBUG ("Queue %p: leaving chain/get wrapper for '%s'", ct->queue, - GST_OBJECT_NAME (element)); -} - - -static void -gst_fair_scheduler_queue_read_blocked_handler (GstQueue * queue, GstPad * pad) -{ - GstFairSchedulerPrivLink *priv; - - priv = LINK_PRIVATE (pad); - - GST_CAT_LOG_OBJECT (debug_fair_queues, priv->owner, - "entering \"blocked\" handler for pad '%s:%s'", GST_DEBUG_PAD_NAME (pad)); - - gst_fair_scheduler_cothread_sleep (priv->owner->cothreads); - - GST_CAT_LOG_OBJECT (debug_fair_queues, priv->owner, - "leaving \"blocked\" handler for queue '%s:%s'", - GST_DEBUG_PAD_NAME (pad)); -} - - -static void -gst_fair_scheduler_decoupled_chain_wrapper (GstFairSchedulerCothread * ct, - GstPad * pad) -{ - GstElement *parent = GST_PAD_PARENT (pad); - GstFairSchedulerPrivLink *priv; - GstData *data; - - g_return_if_fail (GST_RPAD_CHAINFUNC (pad) != NULL); - - priv = LINK_PRIVATE (pad); - - GST_DEBUG ("Queue %p: entering chain wrapper loop for '%s:%s'", ct->queue, - GST_DEBUG_PAD_NAME (pad)); - - gst_object_ref (GST_OBJECT (parent)); - - while (gst_element_get_state (parent) == GST_STATE_PLAYING) { - data = gst_pad_pull (pad); - - gst_pad_call_chain_function (pad, data); - - if (priv->waiting_for_queue != NULL) { - gst_fair_scheduler_cothread_awake_async (priv->waiting_for_queue, 0); - } - } - - gst_object_unref (GST_OBJECT (parent)); - - GST_DEBUG ("Queue %p: leaving chain wrapper loop for '%s:%s'", - ct->queue, GST_DEBUG_PAD_NAME (pad)); -} - - -static void -gst_fair_scheduler_decoupled_get_wrapper (GstFairSchedulerCothread * ct, - GstPad * pad) -{ - GstElement *parent = GST_PAD_PARENT (pad); - GstFairSchedulerPrivLink *priv, *sink_priv = NULL; - GstData *data; - - g_return_if_fail (GST_RPAD_GETFUNC (pad) != NULL); - - priv = LINK_PRIVATE (pad); - - if (GST_IS_QUEUE (parent)) { - /* Decoupled elements are almost always queues. We optimize for - this case. The signal handler stops the cothread when the queue - has no material available. */ - - priv->queue_blocked_signal_id = g_signal_connect (parent, - "underrun", - (GCallback) gst_fair_scheduler_queue_read_blocked_handler, pad); - - /* Register this cothread at the opposite side of the queue. */ - sink_priv = LINK_PRIVATE (gst_element_get_pad (parent, "sink")); - sink_priv->waiting_for_queue = ct; - } - - GST_DEBUG ("Queue %p: entering get wrapper loop for '%s:%s'", ct->queue, - GST_DEBUG_PAD_NAME (pad)); - - gst_object_ref (GST_OBJECT (parent)); - - while (gst_element_get_state (parent) == GST_STATE_PLAYING) { - data = gst_pad_call_get_function (pad); - gst_pad_push (pad, data); - } - - gst_object_unref (GST_OBJECT (parent)); - - GST_DEBUG ("Queue %p: leaving get wrapper loop for '%s:%s'", ct->queue, - GST_DEBUG_PAD_NAME (pad)); - - if (GST_IS_QUEUE (parent)) { - sink_priv->waiting_for_queue = NULL; - - /* Disconnect from the signal. */ - g_signal_handler_disconnect (parent, priv->queue_blocked_signal_id); - priv->queue_blocked_signal_id = 0; - } -} - - -/* - * Chain and Get Handlers - */ - -static void -gst_fair_scheduler_chain_handler (GstPad * pad, GstData * data) -{ - GstFairSchedulerPrivLink *priv = get_link_priv (pad); - GstFairScheduler *fsched = priv->owner; - - while (priv->bufpen != NULL) { - /* The buffer is full. Sleep until it's available again. */ - if (priv->waiting_writer != NULL) { - GST_ERROR_OBJECT (fsched, - "concurrent writers not supported, pad '%s:%s', waiting %p, " - "current %p, ", GST_DEBUG_PAD_NAME (pad), - priv->waiting_writer, - gst_fair_scheduler_cothread_current (fsched->cothreads)); - return; - } - priv->waiting_writer = - gst_fair_scheduler_cothread_current (fsched->cothreads); - gst_fair_scheduler_cothread_sleep (fsched->cothreads); - - /* After sleeping we must be at the head. */ - g_return_if_fail (priv->waiting_writer == - gst_fair_scheduler_cothread_current (fsched->cothreads)); - priv->waiting_writer = NULL; - } - - g_return_if_fail (priv->bufpen == NULL); - - /* Fill the bufpen. */ - priv->bufpen = data; - - /* If there's a waiting reader, wake it up. */ - if (priv->waiting_reader != NULL) { - gst_fair_scheduler_cothread_awake (priv->waiting_reader, 0); - } - - GST_LOG_OBJECT (fsched, "pushed data <%p> on pad '%s:%s'", - data, GST_DEBUG_PAD_NAME (GST_RPAD_PEER (pad))); -} - - -static GstData * -gst_fair_scheduler_get_handler (GstPad * pad) -{ - GstFairSchedulerPrivLink *priv = get_link_priv (pad); - GstFairScheduler *fsched = priv->owner; - GstData *ret; - - while (priv->bufpen == NULL) { - /* The buffer is empty. Sleep until there's something to read. */ - if (priv->waiting_reader != NULL) { - GST_ERROR_OBJECT (fsched, "concurrent readers not supported"); - return NULL; - } - priv->waiting_reader = - gst_fair_scheduler_cothread_current (fsched->cothreads); - gst_fair_scheduler_cothread_sleep (fsched->cothreads); - - /* We should still be there after sleeping. */ - g_return_val_if_fail (priv->waiting_reader == - gst_fair_scheduler_cothread_current (fsched->cothreads), NULL); - priv->waiting_reader = NULL; - } - - g_return_val_if_fail (priv->bufpen != NULL, NULL); - - /* Empty the bufpen. */ - ret = priv->bufpen; - priv->bufpen = NULL; - - /* If there's a waiting writer, wake it up. */ - if (priv->waiting_writer != NULL) { - gst_fair_scheduler_cothread_awake (priv->waiting_writer, 0); - } - - GST_LOG_OBJECT (fsched, "pulled data <%p> from pad '%s:%s'", - ret, GST_DEBUG_PAD_NAME (GST_RPAD_PEER (pad))); - - return ret; -} - - -/* - * GstScheduler Entry Points - */ - -static void -gst_fair_scheduler_setup (GstScheduler * sched) -{ - GstFairScheduler *fsched = GST_FAIR_SCHEDULER (sched); - - GST_DEBUG_OBJECT (fsched, "setting up scheduler"); - - /* Initialize the cothread system. */ - gst_fair_scheduler_cothread_queue_start (fsched->cothreads); - - fsched->iter_count = 0; - g_timer_start (fsched->iter_timer); -} - - -static void -gst_fair_scheduler_reset (GstScheduler * sched) -{ - GstFairScheduler *fsched = GST_FAIR_SCHEDULER (sched); - - GST_DEBUG_OBJECT (fsched, "resetting scheduler"); - - g_timer_stop (fsched->iter_timer); - { - gulong msecs; - double elapsed = g_timer_elapsed (fsched->iter_timer, &msecs); - - GST_INFO_OBJECT (fsched, - "%u iterations in %0.3fs, %.0f iterations/sec.", - fsched->iter_count, elapsed, fsched->iter_count / elapsed); - } - - /* Shut down the cothreads system. */ - gst_fair_scheduler_cothread_queue_stop (fsched->cothreads); -} - - -static void -gst_fair_scheduler_add_element (GstScheduler * sched, GstElement * element) -{ - GstFairScheduler *fsched = GST_FAIR_SCHEDULER (sched); - GstFairSchedulerPrivElem *priv; - - if (GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED)) { - /* Decoupled elements don't have their own cothread. Their pads do - have one, though, but it is assigned in the link operation. */ - return; - } - - GST_DEBUG_OBJECT (fsched, "adding element '%s'", GST_OBJECT_NAME (element)); - - g_return_if_fail (ELEM_PRIVATE (element) == NULL); - - priv = g_malloc (sizeof (GstFairSchedulerPrivElem)); - - /* Create the element's cothread. */ - if (element->loopfunc != NULL) { - priv->elem_ct = - gst_fair_scheduler_cothread_new (fsched->cothreads, - (GstFairSchedulerCtFunc) gst_fair_scheduler_loop_wrapper, - element, NULL); -#ifndef GST_DISABLE_GST_DEBUG - g_string_printf (priv->elem_ct->readable_name, "%s:loop", - GST_OBJECT_NAME (element)); -#endif - GST_CAT_INFO_OBJECT (debug_fair_ct, fsched, - "cothread %p is loop for element '%s'", - priv->elem_ct, GST_OBJECT_NAME (element)); - } else { - priv->elem_ct = - gst_fair_scheduler_cothread_new (fsched->cothreads, - (GstFairSchedulerCtFunc) gst_fair_scheduler_chain_get_wrapper, - element, NULL); -#ifndef GST_DISABLE_GST_DEBUG - g_string_printf (priv->elem_ct->readable_name, "%s:chain/get", - GST_OBJECT_NAME (element)); -#endif - GST_CAT_INFO_OBJECT (debug_fair_ct, fsched, - "cothread %p is chain/get for element '%s'", - priv->elem_ct, GST_OBJECT_NAME (element)); - } - - set_cothread_state (priv->elem_ct, gst_element_get_state (element)); - - priv->chain_get_pads = g_array_new (TRUE, FALSE, sizeof (GstPad *)); - - element->sched_private = priv; - -#ifndef GST_DISABLE_GST_DEBUG - fsched->elements = g_list_prepend (fsched->elements, element); -#endif -} - - -static void -gst_fair_scheduler_remove_element (GstScheduler * sched, GstElement * element) -{ - GstFairScheduler *fsched = GST_FAIR_SCHEDULER (sched); - GstFairSchedulerPrivElem *priv = ELEM_PRIVATE (element); - - if (GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED)) { - return; - } - - GST_DEBUG_OBJECT (fsched, "removing element '%s'", GST_OBJECT_NAME (element)); - - g_return_if_fail (priv != NULL); - - /* Clean up the cothread. */ - g_return_if_fail (priv->elem_ct != NULL); - gst_fair_scheduler_cothread_destroy (priv->elem_ct); - -#ifndef GST_DISABLE_GST_DEBUG - fsched->elements = g_list_remove (fsched->elements, element); -#endif - - g_free (priv); - element->sched_private = NULL; -} - - -static void -gst_fair_scheduler_pad_link (GstScheduler * sched, GstPad * srcpad, - GstPad * sinkpad) -{ - GstFairScheduler *fsched = GST_FAIR_SCHEDULER (sched); - GstFairSchedulerPrivLink *priv; - GstElement *src_parent, *sink_parent; - - g_return_if_fail (LINK_PRIVATE (srcpad) == NULL); - - GST_DEBUG_OBJECT (fsched, "linking pads '%s:%s' and '%s:%s'", - GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad)); - - /* Initialize the private information block. */ - priv = g_malloc (sizeof (GstFairSchedulerPrivLink)); - - priv->owner = fsched; - priv->bufpen = NULL; - priv->waiting_writer = NULL; - priv->waiting_reader = NULL; - priv->decoupled_ct = NULL; - priv->decoupled_signal_id = 0; - priv->queue_blocked_signal_id = 0; - priv->waiting_for_queue = NULL; - - GST_REAL_PAD (srcpad)->sched_private = priv; - - src_parent = GST_PAD_PARENT (srcpad); - sink_parent = GST_PAD_PARENT (sinkpad); - - if (GST_RPAD_GETFUNC (srcpad) != NULL) { - if (GST_FLAG_IS_SET (src_parent, GST_ELEMENT_DECOUPLED)) { - /* Pad is decoupled. Create a separate cothread to run its get - function. */ - priv->decoupled_ct = - gst_fair_scheduler_cothread_new (fsched->cothreads, - (GstFairSchedulerCtFunc) gst_fair_scheduler_decoupled_get_wrapper, - srcpad, NULL); -#ifndef GST_DISABLE_GST_DEBUG - g_string_printf (priv->decoupled_ct->readable_name, "%s:%s:get", - GST_DEBUG_PAD_NAME (srcpad)); -#endif - GST_CAT_INFO_OBJECT (debug_fair_ct, fsched, - "cothread %p is get for pad '%s:%s'", - priv->decoupled_ct, GST_DEBUG_PAD_NAME (srcpad)); - - /* Connect to the state change signal of the decoupled element - in order to manage the state of this cothread. */ - priv->decoupled_signal_id = g_signal_connect (src_parent, - "state-change", (GCallback) decoupled_state_transition, - priv->decoupled_ct); - - set_cothread_state (priv->decoupled_ct, - gst_element_get_state (src_parent)); - } else { - g_array_append_val (ELEM_PRIVATE (src_parent)->chain_get_pads, srcpad); - } - } - - if (GST_RPAD_CHAINFUNC (sinkpad) != NULL) { - if (GST_FLAG_IS_SET (sink_parent, GST_ELEMENT_DECOUPLED)) { - /* Pad is decoupled. Create a separate cothread to run its chain - function. */ - priv->decoupled_ct = - gst_fair_scheduler_cothread_new (fsched->cothreads, - (GstFairSchedulerCtFunc) gst_fair_scheduler_decoupled_chain_wrapper, - sinkpad, NULL); -#ifndef GST_DISABLE_GST_DEBUG - g_string_printf (priv->decoupled_ct->readable_name, "%s:%s:chain", - GST_DEBUG_PAD_NAME (srcpad)); -#endif - GST_CAT_INFO_OBJECT (debug_fair_ct, fsched, - "cothread %p is chain for pad '%s:%s'", - priv->decoupled_ct, GST_DEBUG_PAD_NAME (sinkpad)); - - /* Connect to the state change signal of the decoupled element - in order to manage the state of this cothread. */ - priv->decoupled_signal_id = g_signal_connect (sink_parent, - "state-change", (GCallback) decoupled_state_transition, - priv->decoupled_ct); - - set_cothread_state (priv->decoupled_ct, - gst_element_get_state (sink_parent)); - } else { - g_array_append_val (ELEM_PRIVATE (sink_parent)->chain_get_pads, sinkpad); - } - } - - /* Set the data handlers. */ - GST_RPAD_GETHANDLER (srcpad) = gst_fair_scheduler_get_handler; - GST_RPAD_EVENTHANDLER (srcpad) = GST_RPAD_EVENTFUNC (srcpad); - - GST_RPAD_CHAINHANDLER (sinkpad) = gst_fair_scheduler_chain_handler; - GST_RPAD_EVENTHANDLER (sinkpad) = GST_RPAD_EVENTFUNC (sinkpad); - -#ifndef GST_DISABLE_GST_DEBUG - fsched->sources = g_list_prepend (fsched->sources, srcpad); -#endif -} - - -static void -array_remove_pad (GArray * array, GstPad * pad) -{ - int i; - - for (i = 0; i < array->len; i++) { - if (g_array_index (array, GstPad *, i) == pad) { - g_array_remove_index_fast (array, i); - break; - } - } -} - - -static void -gst_fair_scheduler_pad_unlink (GstScheduler * sched, GstPad * srcpad, - GstPad * sinkpad) -{ - GstFairScheduler *fsched = GST_FAIR_SCHEDULER (sched); - GstFairSchedulerPrivLink *priv; - GstElement *src_parent, *sink_parent; - - priv = LINK_PRIVATE (srcpad); - g_return_if_fail (priv != NULL); - - GST_DEBUG_OBJECT (fsched, "unlinking pads '%s:%s' and '%s:%s'", - GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad)); - - src_parent = GST_PAD_PARENT (srcpad); - sink_parent = GST_PAD_PARENT (sinkpad); - - if (GST_RPAD_GETFUNC (srcpad) != NULL) { - if (GST_FLAG_IS_SET (src_parent, GST_ELEMENT_DECOUPLED)) { - gst_fair_scheduler_cothread_destroy (priv->decoupled_ct); - } else { - array_remove_pad (ELEM_PRIVATE (src_parent)->chain_get_pads, srcpad); - } - } - - if (GST_RPAD_CHAINFUNC (sinkpad) != NULL) { - if (GST_FLAG_IS_SET (sink_parent, GST_ELEMENT_DECOUPLED)) { - gst_fair_scheduler_cothread_destroy (priv->decoupled_ct); - } else { - array_remove_pad (ELEM_PRIVATE (sink_parent)->chain_get_pads, sinkpad); - } - } - - if (priv->decoupled_signal_id != 0) { - g_signal_handler_disconnect (sink_parent, priv->decoupled_signal_id); - } - if (priv->queue_blocked_signal_id != 0) { - g_signal_handler_disconnect (sink_parent, priv->queue_blocked_signal_id); - } - - if (priv->bufpen != NULL) { - gst_data_unref (priv->bufpen); - } - g_free (priv); - - GST_REAL_PAD (srcpad)->sched_private = NULL; - -#ifndef GST_DISABLE_GST_DEBUG - fsched->sources = g_list_remove (fsched->sources, srcpad); -#endif -} - - -static GstElementStateReturn -gst_fair_scheduler_state_transition (GstScheduler * sched, - GstElement * element, gint transition) -{ - GstFairScheduler *fsched = GST_FAIR_SCHEDULER (sched); - gint old_state, new_state; - - GST_DEBUG_OBJECT (sched, "Element %s changing from %s to %s", - GST_ELEMENT_NAME (element), - gst_element_state_get_name (transition >> 8), - gst_element_state_get_name (transition & 0xff)); - - if (GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED)) { - return GST_STATE_SUCCESS; - } - - /* The parent element must be handled specially. */ - if (GST_IS_BIN (element)) { - if (GST_SCHEDULER_PARENT (sched) == element) { - switch (transition) { - case GST_STATE_PLAYING_TO_PAUSED: - GST_INFO_OBJECT (fsched, "setting scheduler state to stopped"); - GST_SCHEDULER_STATE (sched) = GST_SCHEDULER_STATE_STOPPED; - break; - case GST_STATE_PAUSED_TO_PLAYING: - GST_INFO_OBJECT (fsched, "setting scheduler state to running"); - GST_SCHEDULER_STATE (sched) = GST_SCHEDULER_STATE_RUNNING; - break; - } - } - return GST_STATE_SUCCESS; - } - - /* FIXME: Are there eny GStreamer macros for doing this? */ - old_state = transition >> 8; - new_state = transition & 0xff; - if (old_state < new_state) { - set_cothread_state (ELEM_PRIVATE (element)->elem_ct, transition & 0xff); - } - - return GST_STATE_SUCCESS; -} - - -static void -decoupled_state_transition (GstElement * element, gint old_state, - gint new_state, gpointer user_data) -{ - GstFairSchedulerCothread *ct = (GstFairSchedulerCothread *) user_data; - - /* This function is only responsible for activating the - cothread. The wrapper function itself does the deactivation. This - is necessary to avoid weird interactions between multiple - threads. */ - if (old_state < new_state) { - set_cothread_state (ct, new_state); - } -} - - -static void -gst_fair_scheduler_scheduling_change (GstScheduler * sched, - GstElement * element) -{ - GstFairScheduler *fsched = GST_FAIR_SCHEDULER (sched); - - GST_WARNING_OBJECT (fsched, "operation not implemented"); -} - - -static gboolean -gst_fair_scheduler_yield (GstScheduler * sched, GstElement * element) -{ - GstFairScheduler *fsched = GST_FAIR_SCHEDULER (sched); - - g_return_val_if_fail (fsched->in_element, FALSE); - - /* FIXME: What's the difference between yield and interrupt? */ - gst_fair_scheduler_cothread_yield (fsched->cothreads); - - return FALSE; -} - - -static gboolean -gst_fair_scheduler_interrupt (GstScheduler * sched, GstElement * element) -{ - GstFairScheduler *fsched = GST_FAIR_SCHEDULER (sched); - - g_return_val_if_fail (fsched->in_element, FALSE); - - gst_fair_scheduler_cothread_yield (fsched->cothreads); - - return FALSE; -} - - -static void -gst_fair_scheduler_error (GstScheduler * sched, GstElement * element) -{ - GstFairScheduler *fsched = GST_FAIR_SCHEDULER (sched); - - GST_SCHEDULER_STATE (sched) = GST_SCHEDULER_STATE_STOPPED; - if (fsched->in_element) { - gst_fair_scheduler_cothread_yield (fsched->cothreads); - } -} - - -static gint -wait_entry_compare (const GstFairSchedulerWaitEntry * first, - const GstFairSchedulerWaitEntry * second) -{ - if (first->time < second->time) { - return -1; - } else if (first->time == second->time) { - return 0; - } else { - return 1; - } -} - - -static GstData * -gst_fair_scheduler_pad_select (GstScheduler * sched, - GstPad ** pulled_from, GstPad ** pads) -{ - GstFairScheduler *fsched = GST_FAIR_SCHEDULER (sched); - - *pulled_from = gst_fair_scheduler_internal_select (fsched, pads); - g_return_val_if_fail (GST_PAD_IS_SINK (*pulled_from), NULL); - - return gst_pad_pull (*pulled_from); -} - - -static GstClockReturn -gst_fair_scheduler_clock_wait (GstScheduler * sched, GstElement * element, - GstClockID id, GstClockTimeDiff * jitter) -{ - GstFairScheduler *fsched = GST_FAIR_SCHEDULER (sched); - GstClockEntry *clock_entry = (GstClockEntry *) id; - GstClockTime requested, now; - GstFairSchedulerWaitEntry *entry; - - g_return_val_if_fail (sched->current_clock != NULL, GST_CLOCK_ERROR); - g_return_val_if_fail (sched->current_clock == - GST_CLOCK_ENTRY_CLOCK (clock_entry), GST_CLOCK_ERROR); - - now = gst_clock_get_time (sched->current_clock); - requested = GST_CLOCK_ENTRY_TIME (clock_entry); - - if (requested <= now) { - /* It is already too late. */ - if (jitter) { - *jitter = now - requested; - } - return GST_CLOCK_EARLY; - } - - /* Insert a wait entry. */ - entry = g_malloc (sizeof (GstFairSchedulerWaitEntry)); - entry->ct = gst_fair_scheduler_cothread_current (fsched->cothreads); - entry->time = requested; - fsched->waiting = g_slist_insert_sorted (fsched->waiting, entry, - (GCompareFunc) wait_entry_compare); - - /* Go to sleep until it is time... */ - gst_fair_scheduler_cothread_sleep (fsched->cothreads); - - if (jitter) { - now = gst_clock_get_time (sched->current_clock); - *jitter = now - requested; - } - - /* FIXME: Is this the right value to return? */ - return GST_CLOCK_EARLY; -} - - -static GstSchedulerState -gst_fair_scheduler_iterate (GstScheduler * sched) -{ - GstFairScheduler *fsched = GST_FAIR_SCHEDULER (sched); - GstFairSchedulerWaitEntry *entry; - GSList *activate = NULL, *node; - GstClockTime now; - gboolean res; - - /* Count a new iteration for the stats. */ - ++fsched->iter_count; - - /* Check for waiting cothreads. */ - if (fsched->waiting != NULL && sched->current_clock != NULL) { - now = gst_clock_get_time (sched->current_clock); - - /* We need to activate all cothreads whose waiting time was - already reached by the clock. The following code makes sure - that the cothread with the earlier waiting time will be - scheduled first. */ - - /* Move all ready cothreads to the activate list. */ - while (fsched->waiting != NULL) { - entry = (GstFairSchedulerWaitEntry *) fsched->waiting->data; - - if (entry->time > now) { - break; - } - - /* Extract a node from the begining of the waiting - list. */ - node = fsched->waiting; - fsched->waiting = fsched->waiting->next; - - /* Add it to the beginning of the activate list. */ - node->next = activate; - activate = node; - } - - /* Activate the threads in the activate list. */ - while (activate != NULL) { - entry = (GstFairSchedulerWaitEntry *) activate->data; - gst_fair_scheduler_cothread_awake (entry->ct, 1); - activate = g_slist_delete_link (activate, activate); - g_free (entry); - } - } - - /* Handle control to the next cothread. */ - fsched->in_element = TRUE; - res = gst_fair_scheduler_cothread_queue_iterate (fsched->cothreads); - fsched->in_element = FALSE; - - return res ? GST_SCHEDULER_STATE_RUNNING : GST_SCHEDULER_STATE_STOPPED; -} - - -static void -gst_fair_scheduler_show (GstScheduler * sched) -{ -#ifndef GST_DISABLE_GST_DEBUG - GstFairScheduler *fsched = GST_FAIR_SCHEDULER (sched); - GstElement *element; - GstPad *pad; - GstFairSchedulerPrivLink *link_priv; - GstFairSchedulerWaitEntry *entry; - GList *iter1; - GSList *iter2; - GList *iterpads; - - g_print ("Fair scheduler at %p:\n", fsched); - - g_print ("\n Registered elements:\n"); - - for (iter1 = fsched->elements; iter1 != NULL; iter1 = iter1->next) { - element = GST_ELEMENT (iter1->data); - - g_print ("\n %p: %s (%s)\n", element, GST_ELEMENT_NAME (element), - g_type_name (G_OBJECT_TYPE (element))); - - if (GST_IS_BIN (element)) { - continue; - } - - for (iterpads = GST_ELEMENT_PADS (element); iterpads != NULL; - iterpads = iterpads->next) { - pad = GST_PAD (iterpads->data); - - if (GST_IS_GHOST_PAD (pad)) { - continue; - } - - if (GST_PAD_IS_SINK (pad)) { - g_print (" Sink "); - } else { - g_print (" Source "); - } - - g_print ("'%s'", GST_PAD_NAME (pad)); - - link_priv = LINK_PRIVATE (pad); - - if (link_priv == NULL) { - g_print (", unlinked"); - } else { - if (link_priv->bufpen != NULL) { - g_print (", buffer in bufpen"); - } - if (link_priv->waiting_writer != NULL) { - g_print (", waiting writer '%s'", - link_priv->waiting_writer->readable_name->str); - } - if (link_priv->waiting_reader != NULL) { - g_print (", waiting reader '%s'", - link_priv->waiting_reader->readable_name->str); - } - if (link_priv->waiting_for_queue != NULL) { - g_print (", waiting for queue '%s'", - link_priv->waiting_for_queue->readable_name->str); - } - } - - g_print ("\n"); - } - } - - gst_fair_scheduler_cothread_queue_show (fsched->cothreads); - - g_print ("\n Waiting cothreads (current time %" GST_TIME_FORMAT "):\n", - GST_TIME_ARGS (gst_clock_get_time (sched->current_clock))); - - for (iter2 = fsched->waiting; iter2 != NULL; iter2 = iter2->next) { - entry = (GstFairSchedulerWaitEntry *) iter2->data; - g_print (" %p: %s (%d), time = %" GST_TIME_FORMAT "\n", entry->ct, - entry->ct->readable_name->str, entry->ct->pid, - GST_TIME_ARGS (entry->time)); - } -#else - g_print ("Sorry, the 'show' method only works when " - "debugging is activated."); -#endif -} - - -/* - * Plugin Initialization - */ -static gboolean -plugin_init (GstPlugin * plugin) -{ - GstSchedulerFactory *factory; - - GST_DEBUG_CATEGORY_INIT (debug_fair, "fair", 0, "fair scheduler"); - GST_DEBUG_CATEGORY_INIT (debug_fair_ct, "fairct", 0, - "fair scheduler cothreads"); - GST_DEBUG_CATEGORY_INIT (debug_fair_queues, "fairqueues", 0, - "fair scheduler queue related optimizations"); - - factory = gst_scheduler_factory_new ("fair" COTHREADS_NAME, - "A fair scheduler based on " COTHREADS_NAME " cothreads", - gst_fair_scheduler_get_type ()); - - if (factory != NULL) { - gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); - } else { - g_warning ("could not register scheduler: fair"); - } - return TRUE; -} - - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, - GST_VERSION_MINOR, - "gstfair" COTHREADS_NAME "scheduler", - "A 'fair' type scheduler based on " COTHREADS_NAME " cothreads", - plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN); diff --git a/gst/schedulers/gstbasicscheduler.c b/gst/schedulers/gstbasicscheduler.c deleted file mode 100644 index d82be10ced..0000000000 --- a/gst/schedulers/gstbasicscheduler.c +++ /dev/null @@ -1,1514 +0,0 @@ -/* GStreamer - * Copyright (C) 1999,2000 Erik Walthinsen - * 2000 Wim Taymans - * - * gstscheduler.c: Default scheduling code for most cases - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include - -#include "cothreads_compat.h" - -GST_DEBUG_CATEGORY_STATIC (debug_dataflow); -GST_DEBUG_CATEGORY_STATIC (debug_scheduler); -#define GST_CAT_DEFAULT debug_scheduler - -typedef struct _GstSchedulerChain GstSchedulerChain; - -#define GST_ELEMENT_THREADSTATE(elem) (GST_ELEMENT (elem)->sched_private) -#define GST_RPAD_BUFPEN(pad) (GST_REAL_PAD(pad)->sched_private) - -#define GST_ELEMENT_COTHREAD_STOPPING GST_ELEMENT_SCHEDULER_PRIVATE1 -#define GST_ELEMENT_IS_COTHREAD_STOPPING(element) GST_FLAG_IS_SET((element), GST_ELEMENT_COTHREAD_STOPPING) - -typedef struct _GstBasicScheduler GstBasicScheduler; -typedef struct _GstBasicSchedulerClass GstBasicSchedulerClass; - -#ifdef _COTHREADS_STANDARD -# define _SCHEDULER_NAME "standard" -#else -# define _SCHEDULER_NAME "basic" -#endif - -struct _GstSchedulerChain -{ - GstBasicScheduler *sched; - - GList *disabled; - - GList *elements; - gint num_elements; - - GstElement *entry; - - gint cothreaded_elements; - gboolean schedule; -}; - -#define GST_TYPE_BASIC_SCHEDULER \ - (gst_basic_scheduler_get_type()) -#define GST_BASIC_SCHEDULER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_BASIC_SCHEDULER,GstBasicScheduler)) -#define GST_BASIC_SCHEDULER_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_BASIC_SCHEDULER,GstBasicSchedulerClass)) -#define GST_IS_BASIC_SCHEDULER(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_BASIC_SCHEDULER)) -#define GST_IS_BASIC_SCHEDULER_CLASS(obj) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_BASIC_SCHEDULER)) - -#define SCHED(element) GST_BASIC_SCHEDULER (GST_ELEMENT_SCHED (element)) - -typedef enum -{ - GST_BASIC_SCHEDULER_STATE_NONE, - GST_BASIC_SCHEDULER_STATE_STOPPED, - GST_BASIC_SCHEDULER_STATE_ERROR, - GST_BASIC_SCHEDULER_STATE_RUNNING -} -GstBasicSchedulerState; - -typedef enum -{ - /* something important has changed inside the scheduler */ - GST_BASIC_SCHEDULER_CHANGE = GST_SCHEDULER_FLAG_LAST -} -GstBasicSchedulerFlags; - -struct _GstBasicScheduler -{ - GstScheduler parent; - - GList *elements; - gint num_elements; - - GList *chains; - gint num_chains; - - GstBasicSchedulerState state; - - cothread_context *context; - GstElement *current; -}; - -struct _GstBasicSchedulerClass -{ - GstSchedulerClass parent_class; -}; - -static GType _gst_basic_scheduler_type = 0; - -static void gst_basic_scheduler_class_init (GstBasicSchedulerClass * klass); -static void gst_basic_scheduler_init (GstBasicScheduler * scheduler); - -static void gst_basic_scheduler_dispose (GObject * object); - -static void gst_basic_scheduler_setup (GstScheduler * sched); -static void gst_basic_scheduler_reset (GstScheduler * sched); -static void gst_basic_scheduler_add_element (GstScheduler * sched, - GstElement * element); -static void gst_basic_scheduler_remove_element (GstScheduler * sched, - GstElement * element); -static GstElementStateReturn gst_basic_scheduler_state_transition (GstScheduler - * sched, GstElement * element, gint transition); -static gboolean gst_basic_scheduler_yield (GstScheduler * sched, - GstElement * element); -static gboolean gst_basic_scheduler_interrupt (GstScheduler * sched, - GstElement * element); -static void gst_basic_scheduler_error (GstScheduler * sched, - GstElement * element); -static void gst_basic_scheduler_pad_link (GstScheduler * sched, GstPad * srcpad, - GstPad * sinkpad); -static void gst_basic_scheduler_pad_unlink (GstScheduler * sched, - GstPad * srcpad, GstPad * sinkpad); -static GstData *gst_basic_scheduler_pad_select (GstScheduler * sched, - GstPad ** selected, GstPad ** padlist); -static GstSchedulerState gst_basic_scheduler_iterate (GstScheduler * sched); - -static void gst_basic_scheduler_show (GstScheduler * sched); - -static GstSchedulerClass *parent_class = NULL; - -/* for threaded bins, these pre- and post-run functions lock and unlock the - * elements. we have to avoid deadlocks, so we make these convenience macros - * that will avoid using do_cothread_switch from within the scheduler. */ - -#define do_element_switch(element) G_STMT_START{ \ - GstElement *from = SCHED (element)->current; \ - if (from && from->post_run_func) \ - from->post_run_func (from); \ - SCHED (element)->current = element; \ - if (element->pre_run_func) \ - element->pre_run_func (element); \ - do_cothread_switch (GST_ELEMENT_THREADSTATE (element)); \ -}G_STMT_END - -#define do_switch_to_main(sched) G_STMT_START{ \ - GstElement *current = ((GstBasicScheduler*)sched)->current; \ - if (current && current->post_run_func) \ - current->post_run_func (current); \ - ((GstBasicScheduler*) sched)->current = NULL; \ - do_cothread_switch \ - (do_cothread_get_main \ - (((GstBasicScheduler*)sched)->context)); \ -}G_STMT_END - -#define do_switch_from_main(entry) G_STMT_START{ \ - if (entry->pre_run_func) \ - entry->pre_run_func (entry); \ - SCHED (entry)->current = entry; \ - do_cothread_switch (GST_ELEMENT_THREADSTATE (entry)); \ -}G_STMT_END - -static GType -gst_basic_scheduler_get_type (void) -{ - if (!_gst_basic_scheduler_type) { - static const GTypeInfo scheduler_info = { - sizeof (GstBasicSchedulerClass), - NULL, - NULL, - (GClassInitFunc) gst_basic_scheduler_class_init, - NULL, - NULL, - sizeof (GstBasicScheduler), - 0, - (GInstanceInitFunc) gst_basic_scheduler_init, - NULL - }; - - _gst_basic_scheduler_type = - g_type_register_static (GST_TYPE_SCHEDULER, - "Gst" COTHREADS_NAME_CAPITAL "Scheduler", &scheduler_info, 0); - } - return _gst_basic_scheduler_type; -} - -static void -gst_basic_scheduler_class_init (GstBasicSchedulerClass * klass) -{ - GObjectClass *gobject_class; - GstObjectClass *gstobject_class; - GstSchedulerClass *gstscheduler_class; - - gobject_class = (GObjectClass *) klass; - gstobject_class = (GstObjectClass *) klass; - gstscheduler_class = (GstSchedulerClass *) klass; - - parent_class = g_type_class_ref (GST_TYPE_SCHEDULER); - - gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_basic_scheduler_dispose); - - gstscheduler_class->setup = GST_DEBUG_FUNCPTR (gst_basic_scheduler_setup); - gstscheduler_class->reset = GST_DEBUG_FUNCPTR (gst_basic_scheduler_reset); - gstscheduler_class->add_element = - GST_DEBUG_FUNCPTR (gst_basic_scheduler_add_element); - gstscheduler_class->remove_element = - GST_DEBUG_FUNCPTR (gst_basic_scheduler_remove_element); - gstscheduler_class->state_transition = - GST_DEBUG_FUNCPTR (gst_basic_scheduler_state_transition); - gstscheduler_class->yield = GST_DEBUG_FUNCPTR (gst_basic_scheduler_yield); - gstscheduler_class->interrupt = - GST_DEBUG_FUNCPTR (gst_basic_scheduler_interrupt); - gstscheduler_class->error = GST_DEBUG_FUNCPTR (gst_basic_scheduler_error); - gstscheduler_class->pad_link = - GST_DEBUG_FUNCPTR (gst_basic_scheduler_pad_link); - gstscheduler_class->pad_unlink = - GST_DEBUG_FUNCPTR (gst_basic_scheduler_pad_unlink); - gstscheduler_class->pad_select = - GST_DEBUG_FUNCPTR (gst_basic_scheduler_pad_select); - gstscheduler_class->clock_wait = NULL; - gstscheduler_class->iterate = GST_DEBUG_FUNCPTR (gst_basic_scheduler_iterate); - - gstscheduler_class->show = GST_DEBUG_FUNCPTR (gst_basic_scheduler_show); - - do_cothreads_init (NULL); -} - -static void -gst_basic_scheduler_init (GstBasicScheduler * scheduler) -{ - scheduler->elements = NULL; - scheduler->num_elements = 0; - scheduler->chains = NULL; - scheduler->num_chains = 0; - - GST_FLAG_SET (scheduler, GST_SCHEDULER_FLAG_NEW_API); -} - -static void -gst_basic_scheduler_dispose (GObject * object) -{ - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -static gboolean -plugin_init (GstPlugin * plugin) -{ - if (!gst_scheduler_register (plugin, "basic" COTHREADS_NAME, - "A basic scheduler using " COTHREADS_NAME " cothreads", - gst_basic_scheduler_get_type ())) - return FALSE; - - GST_DEBUG_CATEGORY_INIT (debug_dataflow, "basic_dataflow", 0, - "basic scheduler dataflow"); - GST_DEBUG_CATEGORY_INIT (debug_scheduler, "basic_scheduler", 0, - "basic scheduler general information"); - - return TRUE; -} - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, - GST_VERSION_MINOR, - "gstbasic" COTHREADS_NAME "scheduler", - "a basic scheduler using " COTHREADS_NAME " cothreads", - plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN) - - static int gst_basic_scheduler_loopfunc_wrapper (int argc, char **argv) -{ - GstElement *element = GST_ELEMENT (argv); - G_GNUC_UNUSED const gchar *name = GST_ELEMENT_NAME (element); - - GST_DEBUG ("entering loopfunc wrapper of %s", name); - - gst_object_ref (GST_OBJECT (element)); - do { - GST_CAT_DEBUG (debug_dataflow, "calling loopfunc %s for element %s", - GST_DEBUG_FUNCPTR_NAME (element->loopfunc), name); - (element->loopfunc) (element); - GST_CAT_DEBUG (debug_dataflow, "element %s ended loop function", name); - - } while (!GST_ELEMENT_IS_COTHREAD_STOPPING (element)); - GST_FLAG_UNSET (element, GST_ELEMENT_COTHREAD_STOPPING); - - /* due to oddities in the cothreads code, when this function returns it will - * switch to the main cothread. thus, we need to unlock the current element. */ - if (SCHED (element)) { - if (SCHED (element)->current && SCHED (element)->current->post_run_func) { - SCHED (element)->current->post_run_func (SCHED (element)->current); - } - SCHED (element)->current = NULL; - } - - GST_DEBUG ("leaving loopfunc wrapper of %s", name); - gst_object_unref (GST_OBJECT (element)); - - return 0; -} - -static int -gst_basic_scheduler_chain_wrapper (int argc, char **argv) -{ - GSList *already_iterated = NULL; - GstElement *element = GST_ELEMENT (argv); - G_GNUC_UNUSED const gchar *name = GST_ELEMENT_NAME (element); - - GST_DEBUG ("entered chain wrapper of element %s", name); - - GST_CAT_DEBUG (debug_dataflow, "stepping through pads"); - - gst_object_ref (GST_OBJECT (element)); - do { - GList *pads; - - do { - pads = element->pads; - - while (pads) { - GstPad *pad = GST_PAD (pads->data); - GstRealPad *realpad; - - if (!GST_IS_REAL_PAD (pad)) - continue; - - realpad = GST_REAL_PAD (pad); - - if (GST_RPAD_DIRECTION (realpad) == GST_PAD_SINK && - GST_PAD_IS_LINKED (realpad) && - g_slist_find (already_iterated, pad) == NULL) { - GstData *data; - - GST_CAT_DEBUG (debug_dataflow, "pulling data from %s:%s", name, - GST_PAD_NAME (pad)); - data = gst_pad_pull (pad); - if (data) { - if (GST_IS_EVENT (data) && !GST_ELEMENT_IS_EVENT_AWARE (element)) { - gst_pad_send_event (pad, GST_EVENT (data)); - } else { - GST_CAT_DEBUG (debug_dataflow, - "calling chain function of %s:%s %p", name, - GST_PAD_NAME (pad), data); - gst_pad_call_chain_function (GST_PAD (realpad), data); - GST_CAT_DEBUG (debug_dataflow, - "calling chain function of element %s done", name); - } - } - already_iterated = g_slist_prepend (already_iterated, pad); - break; - } - pads = g_list_next (pads); - } - } while (pads != NULL); - if (already_iterated == NULL) { - GST_DEBUG_OBJECT (SCHED (element), "nothing to iterate for element %s", - GST_ELEMENT_NAME (element)); - break; - } - g_slist_free (already_iterated); - already_iterated = NULL; - } while (!GST_ELEMENT_IS_COTHREAD_STOPPING (element)); - - GST_FLAG_UNSET (element, GST_ELEMENT_COTHREAD_STOPPING); - - /* due to oddities in the cothreads code, when this function returns it will - * switch to the main cothread. thus, we need to unlock the current element. */ - if (SCHED (element)) { - if (SCHED (element)->current && SCHED (element)->current->post_run_func) { - SCHED (element)->current->post_run_func (SCHED (element)->current); - } - SCHED (element)->current = NULL; - } - - GST_CAT_DEBUG (debug_dataflow, "leaving chain wrapper of element %s", name); - gst_object_unref (GST_OBJECT (element)); - - return 0; -} - -static int -gst_basic_scheduler_src_wrapper (int argc, char **argv) -{ - GstElement *element = GST_ELEMENT (argv); - GList *pads; - GstRealPad *realpad; - GstData *data = NULL; - gboolean inf_loop; - G_GNUC_UNUSED const gchar *name = GST_ELEMENT_NAME (element); - - GST_DEBUG ("entering src wrapper of element %s", name); - - do { - inf_loop = TRUE; - pads = element->pads; - while (pads) { - - if (!GST_IS_REAL_PAD (pads->data)) - continue; - - realpad = GST_REAL_PAD (pads->data); - - pads = g_list_next (pads); - if (GST_RPAD_DIRECTION (realpad) == GST_PAD_SRC) { - inf_loop = FALSE; - GST_CAT_DEBUG (debug_dataflow, "calling _getfunc for %s:%s", - GST_DEBUG_PAD_NAME (realpad)); - data = gst_pad_call_get_function (GST_PAD (realpad)); - if (data) { - GST_CAT_DEBUG (debug_dataflow, "calling gst_pad_push on pad %s:%s %p", - GST_DEBUG_PAD_NAME (realpad), data); - gst_pad_push (GST_PAD (realpad), data); - } - } - } - } while (!GST_ELEMENT_IS_COTHREAD_STOPPING (element) && !inf_loop); - - GST_FLAG_UNSET (element, GST_ELEMENT_COTHREAD_STOPPING); - - /* due to oddities in the cothreads code, when this function returns it will - * switch to the main cothread. thus, we need to unlock the current element. */ - if (SCHED (element)->current->post_run_func) - SCHED (element)->current->post_run_func (SCHED (element)->current); - SCHED (element)->current = NULL; - - GST_DEBUG ("leaving src wrapper of element %s", name); - - return 0; -} - -static void -gst_basic_scheduler_chainhandler_proxy (GstPad * pad, GstData * data) -{ - gint loop_count = 100; - GstElement *parent; - GstRealPad *peer; - - parent = GST_PAD_PARENT (pad); - peer = GST_RPAD_PEER (pad); - - GST_CAT_DEBUG (debug_dataflow, "putting buffer %p in peer \"%s:%s\"'s pen", - data, GST_DEBUG_PAD_NAME (peer)); - - /* - * loop until the bufferpen is empty so we can fill it up again - */ - while (GST_RPAD_BUFPEN (GST_RPAD_PEER (pad)) != NULL && --loop_count) { - GST_CAT_DEBUG (debug_dataflow, "switching to %p to empty bufpen %d", - GST_ELEMENT_THREADSTATE (parent), loop_count); - - do_element_switch (parent); - - /* we may no longer be the same pad, check. */ - if (GST_RPAD_PEER (peer) != (GstRealPad *) pad) { - GST_CAT_DEBUG (debug_dataflow, "new pad in mid-switch!"); - pad = (GstPad *) GST_RPAD_PEER (peer); - } - parent = GST_PAD_PARENT (pad); - peer = GST_RPAD_PEER (pad); - } - - if (loop_count == 0) { - GST_ELEMENT_ERROR (parent, CORE, SCHEDULER, (NULL), - ("(internal error) basic: maximum number of switches exceeded")); - return; - } - - g_assert (GST_RPAD_BUFPEN (GST_RPAD_PEER (pad)) == NULL); - - /* now fill the bufferpen and switch so it can be consumed */ - GST_RPAD_BUFPEN (GST_RPAD_PEER (pad)) = data; - GST_CAT_DEBUG (debug_dataflow, "switching to %p to consume buffer %p", - GST_ELEMENT_THREADSTATE (GST_PAD_PARENT (pad)), data); - - do_element_switch (parent); - - GST_CAT_DEBUG (debug_dataflow, "leaving chainhandler proxy of %s:%s", - GST_DEBUG_PAD_NAME (pad)); -} - -static void -gst_basic_scheduler_select_proxy (GstPad * pad, GstData * data) -{ - GstElement *parent; - - parent = GST_PAD_PARENT (pad); - - GST_CAT_DEBUG (debug_dataflow, "putting buffer %p in peer's pen of pad %s:%s", - data, GST_DEBUG_PAD_NAME (pad)); - - g_assert (GST_RPAD_BUFPEN (GST_RPAD_PEER (pad)) == NULL); - /* now fill the bufferpen and switch so it can be consumed */ - GST_RPAD_BUFPEN (GST_RPAD_PEER (pad)) = data; - GST_CAT_DEBUG (debug_dataflow, "switching to %p", - GST_ELEMENT_THREADSTATE (parent)); - /* FIXME temporarily diabled */ - /* parent->select_pad = pad; */ - - do_element_switch (parent); - - GST_CAT_DEBUG (debug_dataflow, "done switching"); -} - - -static GstData * -gst_basic_scheduler_gethandler_proxy (GstPad * pad) -{ - GstData *data; - GstElement *parent; - GstRealPad *peer; - - GST_CAT_DEBUG (debug_dataflow, "entering gethandler proxy of %s:%s", - GST_DEBUG_PAD_NAME (pad)); - - parent = GST_PAD_PARENT (pad); - peer = GST_RPAD_PEER (pad); - - /* FIXME this should be bounded */ - /* we will loop switching to the peer until it's filled up the bufferpen */ - while (GST_RPAD_BUFPEN (pad) == NULL) { - - GST_CAT_DEBUG (debug_dataflow, "switching to \"%s\": %p to fill bufpen", - GST_ELEMENT_NAME (parent), GST_ELEMENT_THREADSTATE (parent)); - - do_element_switch (parent); - - /* we may no longer be the same pad, check. */ - if (GST_RPAD_PEER (peer) != (GstRealPad *) pad) { - GST_CAT_DEBUG (debug_dataflow, "new pad in mid-switch!"); - pad = (GstPad *) GST_RPAD_PEER (peer); - if (!pad) { - GST_ELEMENT_ERROR (parent, CORE, PAD, (NULL), ("pad unlinked")); - } - parent = GST_PAD_PARENT (pad); - peer = GST_RPAD_PEER (pad); - } - } - GST_CAT_DEBUG (debug_dataflow, "done switching"); - - /* now grab the buffer from the pen, clear the pen, and return the buffer */ - data = GST_RPAD_BUFPEN (pad); - GST_RPAD_BUFPEN (pad) = NULL; - - GST_CAT_DEBUG (debug_dataflow, "leaving gethandler proxy of %s:%s", - GST_DEBUG_PAD_NAME (pad)); - - return data; -} - -static gboolean -gst_basic_scheduler_eventhandler_proxy (GstPad * srcpad, GstEvent * event) -{ - gboolean flush; - - GST_CAT_INFO (debug_dataflow, "intercepting event %d on pad %s:%s", - GST_EVENT_TYPE (event), GST_DEBUG_PAD_NAME (srcpad)); - - /* figure out if we need to flush */ - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_FLUSH: - flush = TRUE; - break; - case GST_EVENT_SEEK: - case GST_EVENT_SEEK_SEGMENT: - flush = GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH; - break; - default: - flush = FALSE; - break; - } - - if (flush) { - GstData *data = GST_RPAD_BUFPEN (srcpad); - - GST_CAT_INFO (debug_dataflow, "event is flush"); - - if (data) { - GST_CAT_INFO (debug_dataflow, "need to clear some buffers"); - - gst_data_unref (data); - GST_RPAD_BUFPEN (srcpad) = NULL; - } - } - return GST_RPAD_EVENTFUNC (srcpad) (srcpad, event); -} - -static gboolean -gst_basic_scheduler_cothreaded_chain (GstBin * bin, GstSchedulerChain * chain) -{ - GList *elements; - GstElement *element; - cothread_func wrapper_function; - const GList *pads; - GstPad *pad; - - GST_DEBUG ("chain is using COTHREADS"); - - g_assert (chain->sched->context != NULL); - - /* walk through all the chain's elements */ - elements = chain->elements; - while (elements) { - gboolean decoupled; - - element = GST_ELEMENT (elements->data); - elements = g_list_next (elements); - - decoupled = GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED); - - /* start out without a wrapper function, we select it later */ - wrapper_function = NULL; - - /* if the element has a loopfunc... */ - if (element->loopfunc != NULL) { - wrapper_function = - GST_DEBUG_FUNCPTR (gst_basic_scheduler_loopfunc_wrapper); - GST_DEBUG ("element '%s' is a loop-based", GST_ELEMENT_NAME (element)); - } else { - /* otherwise we need to decide what kind of cothread - * if it's not DECOUPLED, we decide based on - * whether it's a source or not */ - if (!decoupled) { - /* if it doesn't have any sinks, it must be a source (duh) */ - if (element->numsinkpads == 0) { - wrapper_function = - GST_DEBUG_FUNCPTR (gst_basic_scheduler_src_wrapper); - GST_DEBUG ("element '%s' is a source, using _src_wrapper", - GST_ELEMENT_NAME (element)); - } else { - wrapper_function = - GST_DEBUG_FUNCPTR (gst_basic_scheduler_chain_wrapper); - GST_DEBUG ("element '%s' is a filter, using _chain_wrapper", - GST_ELEMENT_NAME (element)); - } - } - } - - /* now we have to walk through the pads to set up their state */ - pads = gst_element_get_pad_list (element); - while (pads) { - GstPad *peerpad; - - pad = GST_PAD (pads->data); - pads = g_list_next (pads); - - if (!GST_IS_REAL_PAD (pad)) - continue; - - peerpad = GST_PAD_PEER (pad); - if (peerpad) { - GstElement *peerelement = GST_ELEMENT (GST_PAD_PARENT (peerpad)); - gboolean different_sched = - (peerelement->sched != GST_SCHEDULER (chain->sched)); - gboolean peer_decoupled = - GST_FLAG_IS_SET (peerelement, GST_ELEMENT_DECOUPLED); - - GST_DEBUG ("inspecting pad %s:%s", GST_DEBUG_PAD_NAME (peerpad)); - - /* we don't need to check this for decoupled elements */ - if (!decoupled) { - /* if the peer element is in another schedule, - * it's not decoupled and we are not decoupled - * either, we have an error */ - if (different_sched && !peer_decoupled) { - GST_ELEMENT_ERROR (element, CORE, SCHEDULER, (NULL), - ("element \"%s\" is not decoupled but has pads in different schedulers", - GST_ELEMENT_NAME (element))); - return FALSE; - } - /* ok, the peer is in a different scheduler and is decoupled, - * we need to set the - * handlers so we can talk with it */ - else if (different_sched) { - if (GST_RPAD_DIRECTION (peerpad) == GST_PAD_SINK) { - GST_DEBUG ("copying chain func into push proxy for peer %s:%s", - GST_DEBUG_PAD_NAME (peerpad)); - GST_RPAD_CHAINHANDLER (peerpad) = gst_pad_call_chain_function; - } else { - GST_DEBUG ("copying get func into pull proxy for peer %s:%s", - GST_DEBUG_PAD_NAME (peerpad)); - GST_RPAD_GETHANDLER (peerpad) = gst_pad_call_get_function; - } - } - } - /* in any case we need to copy the eventfunc into the handler */ - GST_RPAD_EVENTHANDLER (peerpad) = GST_RPAD_EVENTFUNC (peerpad); - } - - /* if the element is DECOUPLED or outside the manager, we have to chain */ - if (decoupled) { - /* set the chain proxies */ - if (GST_RPAD_DIRECTION (pad) == GST_PAD_SINK) { - GST_DEBUG ("copying chain function into push proxy for %s:%s", - GST_DEBUG_PAD_NAME (pad)); - GST_RPAD_CHAINHANDLER (pad) = gst_pad_call_chain_function; - } else { - GST_DEBUG ("copying get function into pull proxy for %s:%s", - GST_DEBUG_PAD_NAME (pad)); - GST_RPAD_GETHANDLER (pad) = gst_pad_call_get_function; - } - } - /* otherwise we really are a cothread */ - else { - if (GST_RPAD_DIRECTION (pad) == GST_PAD_SINK) { - GST_DEBUG ("setting cothreaded push proxy for sinkpad %s:%s", - GST_DEBUG_PAD_NAME (pad)); - GST_RPAD_CHAINHANDLER (pad) = - GST_DEBUG_FUNCPTR (gst_basic_scheduler_chainhandler_proxy); - GST_RPAD_EVENTHANDLER (pad) = GST_RPAD_EVENTFUNC (pad); - } else { - GST_DEBUG ("setting cothreaded pull proxy for srcpad %s:%s", - GST_DEBUG_PAD_NAME (pad)); - GST_RPAD_GETHANDLER (pad) = - GST_DEBUG_FUNCPTR (gst_basic_scheduler_gethandler_proxy); - /* the gethandler proxy function can queue a buffer in the bufpen, we need - * to remove this buffer when a flush event is sent on the pad */ - GST_RPAD_EVENTHANDLER (pad) = - GST_DEBUG_FUNCPTR (gst_basic_scheduler_eventhandler_proxy); - } - } - } - - /* need to set up the cothread now */ - if (wrapper_function != NULL) { - if (GST_ELEMENT_THREADSTATE (element) == NULL) { - GST_DEBUG ("about to create a cothread, wrapper for '%s' is &%s", - GST_ELEMENT_NAME (element), - GST_DEBUG_FUNCPTR_NAME (wrapper_function)); - do_cothread_create (GST_ELEMENT_THREADSTATE (element), - chain->sched->context, wrapper_function, 0, (char **) element); - if (GST_ELEMENT_THREADSTATE (element) == NULL) { - GST_ELEMENT_ERROR (element, RESOURCE, TOO_LAZY, (NULL), - ("could not create cothread for \"%s\"", - GST_ELEMENT_NAME (element))); - return FALSE; - } - GST_DEBUG ("created cothread %p for '%s'", - GST_ELEMENT_THREADSTATE (element), GST_ELEMENT_NAME (element)); - } else { - /* set the cothread wrapper function */ - GST_DEBUG ("about to set the wrapper function for '%s' to &%s", - GST_ELEMENT_NAME (element), - GST_DEBUG_FUNCPTR_NAME (wrapper_function)); - do_cothread_setfunc (GST_ELEMENT_THREADSTATE (element), - chain->sched->context, wrapper_function, 0, (char **) element); - GST_DEBUG ("set wrapper function for '%s' to &%s", - GST_ELEMENT_NAME (element), - GST_DEBUG_FUNCPTR_NAME (wrapper_function)); - } - } - } - - return TRUE; -} - -static GstSchedulerChain * -gst_basic_scheduler_chain_new (GstBasicScheduler * sched) -{ - GstSchedulerChain *chain = g_new (GstSchedulerChain, 1); - - /* initialize the chain with sane values */ - chain->sched = sched; - chain->disabled = NULL; - chain->elements = NULL; - chain->num_elements = 0; - chain->entry = NULL; - chain->cothreaded_elements = 0; - chain->schedule = FALSE; - - /* add the chain to the schedulers' list of chains */ - sched->chains = g_list_prepend (sched->chains, chain); - sched->num_chains++; - - /* notify the scheduler that something changed */ - GST_FLAG_SET (sched, GST_BASIC_SCHEDULER_CHANGE); - - GST_INFO ("created new chain %p, now are %d chains in sched %p", - chain, sched->num_chains, sched); - - return chain; -} - -static void -gst_basic_scheduler_chain_destroy (GstSchedulerChain * chain) -{ - GstBasicScheduler *sched = chain->sched; - - /* remove the chain from the schedulers' list of chains */ - sched->chains = g_list_remove (sched->chains, chain); - sched->num_chains--; - - /* destroy the chain */ - g_list_free (chain->disabled); /* should be empty... */ - g_list_free (chain->elements); /* ditto */ - - GST_INFO ("destroyed chain %p, now are %d chains in sched %p", chain, - sched->num_chains, sched); - - g_free (chain); - - /* notify the scheduler that something changed */ - GST_FLAG_SET (sched, GST_BASIC_SCHEDULER_CHANGE); -} - -static void -gst_basic_scheduler_chain_add_element (GstSchedulerChain * chain, - GstElement * element) -{ - /* set the sched pointer for the element */ - element->sched = GST_SCHEDULER (chain->sched); - - /* add the element to either the main list or the disabled list */ - if (GST_STATE (element) == GST_STATE_PLAYING) { - GST_INFO ("adding element \"%s\" to chain %p enabled", - GST_ELEMENT_NAME (element), chain); - chain->elements = g_list_prepend (chain->elements, element); - } else { - GST_INFO ("adding element \"%s\" to chain %p disabled", - GST_ELEMENT_NAME (element), chain); - chain->disabled = g_list_prepend (chain->disabled, element); - } - chain->num_elements++; - - /* notify the scheduler that something changed */ - GST_FLAG_SET (chain->sched, GST_BASIC_SCHEDULER_CHANGE); -} - -static gboolean -gst_basic_scheduler_chain_enable_element (GstSchedulerChain * chain, - GstElement * element) -{ - GST_INFO ("enabling element \"%s\" in chain %p", - GST_ELEMENT_NAME (element), chain); - - /* remove from disabled list */ - chain->disabled = g_list_remove (chain->disabled, element); - - /* add to elements list */ - chain->elements = g_list_prepend (chain->elements, element); - - /* notify the scheduler that something changed */ - GST_FLAG_SET (chain->sched, GST_BASIC_SCHEDULER_CHANGE); - /* GST_FLAG_UNSET(element, GST_ELEMENT_COTHREAD_STOPPING); */ - - /* reschedule the chain */ - return gst_basic_scheduler_cothreaded_chain (GST_BIN (GST_SCHEDULER (chain-> - sched)->parent), chain); -} - -static void -gst_basic_scheduler_chain_disable_element (GstSchedulerChain * chain, - GstElement * element) -{ - GST_INFO ("disabling element \"%s\" in chain %p", - GST_ELEMENT_NAME (element), chain); - - /* remove from elements list */ - chain->elements = g_list_remove (chain->elements, element); - - /* add to disabled list */ - chain->disabled = g_list_prepend (chain->disabled, element); - - /* notify the scheduler that something changed */ - GST_FLAG_SET (chain->sched, GST_BASIC_SCHEDULER_CHANGE); - GST_FLAG_SET (element, GST_ELEMENT_COTHREAD_STOPPING); - - /* reschedule the chain */ -/* FIXME this should be done only if manager state != NULL */ -/* gst_basic_scheduler_cothreaded_chain(GST_BIN(chain->sched->parent),chain); */ -} - -static void -gst_basic_scheduler_chain_remove_element (GstSchedulerChain * chain, - GstElement * element) -{ - GST_INFO ("removing element \"%s\" from chain %p", GST_ELEMENT_NAME (element), - chain); - - /* if it's active, deactivate it */ - if (g_list_find (chain->elements, element)) { - gst_basic_scheduler_chain_disable_element (chain, element); - } - /* we have to check for a threadstate here because a queue doesn't have one */ - if (GST_ELEMENT_THREADSTATE (element)) { - do_cothread_destroy (GST_ELEMENT_THREADSTATE (element)); - GST_ELEMENT_THREADSTATE (element) = NULL; - } - - /* remove the element from the list of elements */ - chain->disabled = g_list_remove (chain->disabled, element); - chain->num_elements--; - - /* notify the scheduler that something changed */ - GST_FLAG_SET (chain->sched, GST_BASIC_SCHEDULER_CHANGE); - - /* if there are no more elements in the chain, destroy the chain */ - if (chain->num_elements == 0) - gst_basic_scheduler_chain_destroy (chain); - -} - -static void -gst_basic_scheduler_chain_elements (GstBasicScheduler * sched, - GstElement * element1, GstElement * element2) -{ - GList *chains; - GstSchedulerChain *chain; - GstSchedulerChain *chain1 = NULL, *chain2 = NULL; - GstElement *element; - - /* first find the chains that hold the two */ - chains = sched->chains; - while (chains) { - chain = (GstSchedulerChain *) (chains->data); - chains = g_list_next (chains); - - if (g_list_find (chain->disabled, element1)) - chain1 = chain; - else if (g_list_find (chain->elements, element1)) - chain1 = chain; - - if (g_list_find (chain->disabled, element2)) - chain2 = chain; - else if (g_list_find (chain->elements, element2)) - chain2 = chain; - } - - /* first check to see if they're in the same chain, we're done if that's the case */ - if ((chain1 != NULL) && (chain1 == chain2)) { - GST_INFO ("elements are already in the same chain"); - return; - } - - /* now, if neither element has a chain, create one */ - if ((chain1 == NULL) && (chain2 == NULL)) { - GST_INFO ("creating new chain to hold two new elements"); - chain = gst_basic_scheduler_chain_new (sched); - gst_basic_scheduler_chain_add_element (chain, element1); - gst_basic_scheduler_chain_add_element (chain, element2); - /* FIXME chain changed here */ -/* gst_basic_scheduler_cothreaded_chain(chain->sched->parent,chain); */ - - /* otherwise if both have chains already, join them */ - } else if ((chain1 != NULL) && (chain2 != NULL)) { - GST_INFO ("merging chain %p into chain %p", chain2, chain1); - /* take the contents of chain2 and merge them into chain1 */ - chain1->disabled = - g_list_concat (chain1->disabled, g_list_copy (chain2->disabled)); - chain1->elements = - g_list_concat (chain1->elements, g_list_copy (chain2->elements)); - chain1->num_elements += chain2->num_elements; - gst_basic_scheduler_chain_destroy (chain2); - if (sched->context) - - gst_basic_scheduler_cothreaded_chain (GST_BIN (GST_SCHEDULER (chain1-> - sched)->parent), chain1); - - /* otherwise one has a chain already, the other doesn't */ - } else { - /* pick out which one has the chain, and which doesn't */ - if (chain1 != NULL) - chain = chain1, element = element2; - else - chain = chain2, element = element1; - - GST_INFO ("adding element to existing chain"); - gst_basic_scheduler_chain_add_element (chain, element); - /* FIXME chain changed here */ -/* gst_basic_scheduler_cothreaded_chain(chain->sched->parent,chain); */ - } - -} - - -/* find the chain within the scheduler that holds the element, if any */ -static GstSchedulerChain * -gst_basic_scheduler_find_chain (GstBasicScheduler * sched, GstElement * element) -{ - GList *chains; - GstSchedulerChain *chain; - - GST_INFO ("searching for element \"%s\" in chains", - GST_ELEMENT_NAME (element)); - - chains = sched->chains; - while (chains) { - chain = (GstSchedulerChain *) (chains->data); - chains = g_list_next (chains); - - if (g_list_find (chain->elements, element)) - return chain; - if (g_list_find (chain->disabled, element)) - return chain; - } - - return NULL; -} - -static void -gst_basic_scheduler_chain_recursive_add (GstSchedulerChain * chain, - GstElement * element, gboolean remove) -{ - GList *pads; - GstPad *pad; - GstElement *peerelement; - GstSchedulerChain *prevchain; - - /* check to see if it's in a chain already */ - prevchain = gst_basic_scheduler_find_chain (chain->sched, element); - /* if it's already in another chain, either remove or punt */ - if (prevchain != NULL) { - if (remove == TRUE) - gst_basic_scheduler_chain_remove_element (prevchain, element); - else - return; - } - - /* add it to this one */ - gst_basic_scheduler_chain_add_element (chain, element); - - GST_DEBUG ("recursing on element \"%s\"", GST_ELEMENT_NAME (element)); - /* now go through all the pads and see which peers can be added */ - pads = element->pads; - while (pads) { - pad = GST_PAD (pads->data); - pads = g_list_next (pads); - - GST_DEBUG ("have pad %s:%s, checking for valid peer", - GST_DEBUG_PAD_NAME (pad)); - /* if the peer exists and could be in the same chain */ - if (GST_PAD_PEER (pad)) { - GST_DEBUG ("has peer %s:%s", GST_DEBUG_PAD_NAME (GST_PAD_PEER (pad))); - peerelement = GST_PAD_PARENT (GST_PAD_PEER (pad)); - if (GST_ELEMENT_SCHED (GST_PAD_PARENT (pad)) == - GST_ELEMENT_SCHED (peerelement)) { - GST_DEBUG ("peer \"%s\" is valid for same chain", - GST_ELEMENT_NAME (peerelement)); - gst_basic_scheduler_chain_recursive_add (chain, peerelement, remove); - } - } - } -} - -/* - * Entry points for this scheduler. - */ -static void -gst_basic_scheduler_setup (GstScheduler * sched) -{ - /* first create thread context */ - if (GST_BASIC_SCHEDULER (sched)->context == NULL) { - GST_DEBUG ("initializing cothread context"); - GST_BASIC_SCHEDULER (sched)->context = do_cothread_context_init (); - } -} - -static void -gst_basic_scheduler_reset (GstScheduler * sched) -{ - cothread_context *ctx; - GList *elements = GST_BASIC_SCHEDULER (sched)->elements; - - while (elements) { - GstElement *element = GST_ELEMENT (elements->data); - - if (GST_ELEMENT_THREADSTATE (element)) { - do_cothread_destroy (GST_ELEMENT_THREADSTATE (element)); - GST_ELEMENT_THREADSTATE (element) = NULL; - } - elements = g_list_next (elements); - } - - ctx = GST_BASIC_SCHEDULER (sched)->context; - - do_cothread_context_destroy (ctx); - - GST_BASIC_SCHEDULER (sched)->context = NULL; -} - -static void -gst_basic_scheduler_add_element (GstScheduler * sched, GstElement * element) -{ - GstSchedulerChain *chain; - GstBasicScheduler *bsched = GST_BASIC_SCHEDULER (sched); - - GST_INFO ("adding element \"%s\" to scheduler", GST_ELEMENT_NAME (element)); - - /* only deal with elements after this point, not bins */ - /* exception is made for Bin's that are schedulable, like the autoplugger */ - if (GST_IS_BIN (element) - && !GST_FLAG_IS_SET (element, GST_BIN_SELF_SCHEDULABLE)) - return; - - /* first add it to the list of elements that are to be scheduled */ - bsched->elements = g_list_prepend (bsched->elements, element); - bsched->num_elements++; - - /* create a chain to hold it, and add */ - chain = gst_basic_scheduler_chain_new (bsched); - gst_basic_scheduler_chain_add_element (chain, element); -} - -static void -gst_basic_scheduler_remove_element (GstScheduler * sched, GstElement * element) -{ - GstSchedulerChain *chain; - GstBasicScheduler *bsched = GST_BASIC_SCHEDULER (sched); - - if (g_list_find (bsched->elements, element)) { - GST_INFO ("removing element \"%s\" from scheduler", - GST_ELEMENT_NAME (element)); - - /* if we are removing the currently scheduled element */ - if (bsched->current == element) { - GST_FLAG_SET (element, GST_ELEMENT_COTHREAD_STOPPING); - if (element->post_run_func) - element->post_run_func (element); - bsched->current = NULL; - } - /* find what chain the element is in */ - chain = gst_basic_scheduler_find_chain (bsched, element); - - /* remove it from its chain */ - if (chain != NULL) { - gst_basic_scheduler_chain_remove_element (chain, element); - } - - /* remove it from the list of elements */ - bsched->elements = g_list_remove (bsched->elements, element); - bsched->num_elements--; - - /* unset the scheduler pointer in the element */ - } -} - -static GstElementStateReturn -gst_basic_scheduler_state_transition (GstScheduler * sched, - GstElement * element, gint transition) -{ - GstSchedulerChain *chain; - GstBasicScheduler *bsched = GST_BASIC_SCHEDULER (sched); - - /* check if our parent changed state */ - if (GST_SCHEDULER_PARENT (sched) == element) { - GST_INFO ("parent \"%s\" changed state", GST_ELEMENT_NAME (element)); - if (transition == GST_STATE_PLAYING_TO_PAUSED) { - GST_INFO ("setting scheduler state to stopped"); - GST_SCHEDULER_STATE (sched) = GST_SCHEDULER_STATE_STOPPED; - } else if (transition == GST_STATE_PAUSED_TO_PLAYING) { - GST_INFO ("setting scheduler state to running"); - GST_SCHEDULER_STATE (sched) = GST_SCHEDULER_STATE_RUNNING; - } else { - GST_INFO ("no interesting state change, doing nothing"); - } - } else if (transition == GST_STATE_PLAYING_TO_PAUSED || - transition == GST_STATE_PAUSED_TO_PLAYING) { - /* find the chain the element is in */ - chain = gst_basic_scheduler_find_chain (bsched, element); - - /* remove it from the chain */ - if (chain) { - if (transition == GST_STATE_PLAYING_TO_PAUSED) { - gst_basic_scheduler_chain_disable_element (chain, element); - } else if (transition == GST_STATE_PAUSED_TO_PLAYING) { - if (!gst_basic_scheduler_chain_enable_element (chain, element)) { - GST_INFO ("could not enable element \"%s\"", - GST_ELEMENT_NAME (element)); - return GST_STATE_FAILURE; - } - } - } else { - GST_INFO ("element \"%s\" not found in any chain, no state change", - GST_ELEMENT_NAME (element)); - } - } - - return GST_STATE_SUCCESS; -} - -static gboolean -gst_basic_scheduler_yield (GstScheduler * sched, GstElement * element) -{ - if (GST_ELEMENT_IS_COTHREAD_STOPPING (element)) { - - do_switch_to_main (sched); - - /* no need to do a pre_run, the cothread is stopping */ - } - return FALSE; -} - -static gboolean -gst_basic_scheduler_interrupt (GstScheduler * sched, GstElement * element) -{ - - GST_FLAG_SET (element, GST_ELEMENT_COTHREAD_STOPPING); - do_switch_to_main (sched); - - return FALSE; -} - -static void -gst_basic_scheduler_error (GstScheduler * sched, GstElement * element) -{ - GstBasicScheduler *bsched = GST_BASIC_SCHEDULER (sched); - - if (GST_ELEMENT_THREADSTATE (element)) { - GstSchedulerChain *chain; - - chain = gst_basic_scheduler_find_chain (bsched, element); - if (chain) - gst_basic_scheduler_chain_disable_element (chain, element); - - GST_SCHEDULER_STATE (sched) = GST_SCHEDULER_STATE_ERROR; - - do_switch_to_main (sched); - } -} - -static void -gst_basic_scheduler_pad_link (GstScheduler * sched, GstPad * srcpad, - GstPad * sinkpad) -{ - GstElement *srcelement, *sinkelement; - GstBasicScheduler *bsched = GST_BASIC_SCHEDULER (sched); - - srcelement = GST_PAD_PARENT (srcpad); - g_return_if_fail (srcelement != NULL); - sinkelement = GST_PAD_PARENT (sinkpad); - g_return_if_fail (sinkelement != NULL); - - GST_INFO ("have pad linked callback on %s:%s to %s:%s", - GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad)); - GST_DEBUG ("srcpad sched is %p, sinkpad sched is %p", - GST_ELEMENT_SCHED (srcelement), GST_ELEMENT_SCHED (sinkelement)); - - gst_basic_scheduler_chain_elements (bsched, srcelement, sinkelement); -} - -static void -gst_basic_scheduler_pad_unlink (GstScheduler * sched, GstPad * srcpad, - GstPad * sinkpad) -{ - GstElement *element1, *element2; - GstSchedulerChain *chain1, *chain2; - GstBasicScheduler *bsched = GST_BASIC_SCHEDULER (sched); - - GST_INFO ("unlinking pads %s:%s and %s:%s", - GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad)); - - /* we need to have the parent elements of each pad */ - element1 = GST_ELEMENT (GST_PAD_PARENT (srcpad)); - element2 = GST_ELEMENT (GST_PAD_PARENT (sinkpad)); - - /* first task is to remove the old chain they belonged to. - * this can be accomplished by taking either of the elements, - * since they are guaranteed to be in the same chain - * FIXME is it potentially better to make an attempt at splitting cleaner?? - */ - chain1 = gst_basic_scheduler_find_chain (bsched, element1); - chain2 = gst_basic_scheduler_find_chain (bsched, element2); - -/* FIXME: The old code still works in most cases, but does not deal with - * the problem of screwed up sched chains in some autoplugging cases. - * The new code has an infinite recursion bug during pipeline shutdown, - * which must be fixed before it can be enabled again. - */ -#if 1 - if (chain1 != chain2) { - /* elements not in the same chain don't need to be separated */ - GST_INFO ("elements not in the same chain"); - return; - } - - if (chain1) { - GST_INFO ("destroying chain"); - gst_basic_scheduler_chain_destroy (chain1); - - /* now create a new chain to hold element1 and build it from scratch */ - chain1 = gst_basic_scheduler_chain_new (bsched); - gst_basic_scheduler_chain_recursive_add (chain1, element1, FALSE); - } - - /* check the other element to see if it landed in the newly created chain */ - if (gst_basic_scheduler_find_chain (bsched, element2) == NULL) { - /* if not in chain, create chain and build from scratch */ - chain2 = gst_basic_scheduler_chain_new (bsched); - gst_basic_scheduler_chain_recursive_add (chain2, element2, FALSE); - } -#else - - /* if they're both in the same chain, move second set of elements to a new chain */ - if (chain1 && (chain1 == chain2)) { - GST_INFO ("creating new chain for second element and peers"); - chain2 = gst_basic_scheduler_chain_new (bsched); - gst_basic_scheduler_chain_recursive_add (chain2, element2, TRUE); - } -#endif -} - -static GstData * -gst_basic_scheduler_pad_select (GstScheduler * sched, GstPad ** selected, - GstPad ** padlist) -{ - GstData *data = NULL; - gint i = 0; - - GST_INFO ("performing select"); - - while (padlist[i]) { - GstPad *pad = padlist[i]; - - GST_RPAD_CHAINHANDLER (pad) = - GST_DEBUG_FUNCPTR (gst_basic_scheduler_select_proxy); - } - - do_element_switch (GST_PAD_PARENT (GST_PAD_PEER (padlist[0]))); - - i = 0; - while (padlist[i]) { - GstPad *pad = padlist[i]; - - if (GST_RPAD_BUFPEN (pad)) { - *selected = pad; - data = GST_RPAD_BUFPEN (pad); - GST_RPAD_BUFPEN (pad) = NULL; - } - - GST_RPAD_CHAINHANDLER (pad) = - GST_DEBUG_FUNCPTR (gst_basic_scheduler_chainhandler_proxy); - } - - g_assert (data != NULL); - return data; -} - -static GstSchedulerState -gst_basic_scheduler_iterate (GstScheduler * sched) -{ - GList *chains; - GstSchedulerChain *chain; - GstElement *entry; - GList *elements; - gint scheduled = 0; - GstBasicScheduler *bsched = GST_BASIC_SCHEDULER (sched); - - GST_CAT_LOG_OBJECT (debug_dataflow, sched, - "starting iteration in bin %s", GST_ELEMENT_NAME (sched->parent)); - - /* clear the changes flag */ - GST_FLAG_UNSET (bsched, GST_BASIC_SCHEDULER_CHANGE); - - /* step through all the chains */ - chains = bsched->chains; - - if (chains == NULL) - return GST_SCHEDULER_STATE_STOPPED; - - while (chains) { - chain = (GstSchedulerChain *) (chains->data); - chains = g_list_next (chains); - - /* all we really have to do is switch to the first child */ - /* FIXME this should be lots more intelligent about where to start */ - GST_CAT_DEBUG (debug_dataflow, - "starting iteration via cothreads using %s scheduler", _SCHEDULER_NAME); - - if (chain->elements) { - entry = NULL; /*MattH ADDED? */ - GST_DEBUG ("there are %d elements in this chain", chain->num_elements); - elements = chain->elements; - while (elements) { - entry = GST_ELEMENT (elements->data); - elements = g_list_next (elements); - if (GST_FLAG_IS_SET (entry, GST_ELEMENT_DECOUPLED)) { - GST_DEBUG ("entry \"%s\" is DECOUPLED, skipping", - GST_ELEMENT_NAME (entry)); - entry = NULL; - } else if (GST_FLAG_IS_SET (entry, GST_ELEMENT_INFINITE_LOOP)) { - GST_DEBUG ("entry \"%s\" is not valid, skipping", - GST_ELEMENT_NAME (entry)); - entry = NULL; - } else - break; - } - if (entry) { - GstSchedulerState state; - - GST_FLAG_SET (entry, GST_ELEMENT_COTHREAD_STOPPING); - - GST_CAT_DEBUG (debug_dataflow, - "set COTHREAD_STOPPING flag on \"%s\"(@%p)", - GST_ELEMENT_NAME (entry), entry); - if (GST_ELEMENT_THREADSTATE (entry)) { - - do_switch_from_main (entry); - - state = GST_SCHEDULER_STATE (sched); - /* if something changed, return - go on else */ - if (GST_FLAG_IS_SET (bsched, GST_BASIC_SCHEDULER_CHANGE) && - state != GST_SCHEDULER_STATE_ERROR) - return GST_SCHEDULER_STATE_RUNNING; - } else { - GST_CAT_DEBUG (debug_dataflow, - "cothread switch not possible, element has no threadstate"); - return GST_SCHEDULER_STATE_ERROR; - } - - /* following is a check to see if the chain was interrupted due to a - * top-half state_change(). (i.e., if there's a pending state.) - * - * if it was, return to gstthread.c::gst_thread_main_loop() to - * execute the state change. - */ - GST_CAT_DEBUG (debug_dataflow, "cothread switch ended or interrupted"); - - if (state != GST_SCHEDULER_STATE_RUNNING) { - GST_CAT_INFO (debug_dataflow, "scheduler is not running, in state %d", - state); - return state; - } - - scheduled++; - } else { - GST_CAT_INFO (debug_dataflow, - "no entry in this chain, trying the next one"); - } - } else { - GST_CAT_INFO (debug_dataflow, - "no enabled elements in this chain, trying the next one"); - } - } - - GST_CAT_LOG_OBJECT (debug_dataflow, sched, "leaving (%s)", - GST_ELEMENT_NAME (sched->parent)); - if (scheduled == 0) { - GST_CAT_INFO (debug_dataflow, "nothing was scheduled, return STOPPED"); - return GST_SCHEDULER_STATE_STOPPED; - } else { - GST_CAT_INFO (debug_dataflow, "scheduler still running, return RUNNING"); - return GST_SCHEDULER_STATE_RUNNING; - } -} - - -static void -gst_basic_scheduler_show (GstScheduler * sched) -{ - GList *chains, *elements; - GstElement *element; - GstSchedulerChain *chain; - GstBasicScheduler *bsched = GST_BASIC_SCHEDULER (sched); - - if (sched == NULL) { - g_print ("scheduler doesn't exist for this element\n"); - return; - } - - g_return_if_fail (GST_IS_SCHEDULER (sched)); - - g_print ("SCHEDULER DUMP FOR MANAGING BIN \"%s\"\n", - GST_ELEMENT_NAME (sched->parent)); - - g_print ("scheduler has %d elements in it: ", bsched->num_elements); - elements = bsched->elements; - while (elements) { - element = GST_ELEMENT (elements->data); - elements = g_list_next (elements); - - g_print ("%s, ", GST_ELEMENT_NAME (element)); - } - g_print ("\n"); - - g_print ("scheduler has %d chains in it\n", bsched->num_chains); - chains = bsched->chains; - while (chains) { - chain = (GstSchedulerChain *) (chains->data); - chains = g_list_next (chains); - - g_print ("%p: ", chain); - - elements = chain->disabled; - while (elements) { - element = GST_ELEMENT (elements->data); - elements = g_list_next (elements); - - g_print ("!%s, ", GST_ELEMENT_NAME (element)); - } - - elements = chain->elements; - while (elements) { - element = GST_ELEMENT (elements->data); - elements = g_list_next (elements); - - g_print ("%s, ", GST_ELEMENT_NAME (element)); - } - g_print ("\n"); - } -} diff --git a/gst/schedulers/gstoptimalscheduler.c b/gst/schedulers/gstoptimalscheduler.c deleted file mode 100644 index 66544c3b1b..0000000000 --- a/gst/schedulers/gstoptimalscheduler.c +++ /dev/null @@ -1,2757 +0,0 @@ -/* GStreamer - * Copyright (C) 1999,2000 Erik Walthinsen - * 2000 Wim Taymans - * - * gstscheduler.c: Default scheduling code for most cases - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include - -GST_DEBUG_CATEGORY_STATIC (debug_scheduler); -#define GST_CAT_DEFAULT debug_scheduler - -#ifdef USE_COTHREADS -# include "cothreads_compat.h" -#else -# define COTHREADS_NAME_CAPITAL "" -# define COTHREADS_NAME "" -#endif - -#define GST_ELEMENT_SCHED_CONTEXT(elem) ((GstOptSchedulerCtx*) (GST_ELEMENT (elem)->sched_private)) -#define GST_ELEMENT_SCHED_GROUP(elem) (GST_ELEMENT_SCHED_CONTEXT (elem)->group) -/* need this first macro to not run into lvalue casts */ -#define GST_PAD_DATAPEN(pad) (GST_REAL_PAD(pad)->sched_private) -#define GST_PAD_DATALIST(pad) ((GList*) GST_PAD_DATAPEN(pad)) - -#define GST_ELEMENT_COTHREAD_STOPPING GST_ELEMENT_SCHEDULER_PRIVATE1 -#define GST_ELEMENT_IS_COTHREAD_STOPPING(element) GST_FLAG_IS_SET((element), GST_ELEMENT_COTHREAD_STOPPING) -#define GST_ELEMENT_VISITED GST_ELEMENT_SCHEDULER_PRIVATE2 -#define GST_ELEMENT_IS_VISITED(element) GST_FLAG_IS_SET((element), GST_ELEMENT_VISITED) -#define GST_ELEMENT_SET_VISITED(element) GST_FLAG_SET((element), GST_ELEMENT_VISITED) -#define GST_ELEMENT_UNSET_VISITED(element) GST_FLAG_UNSET((element), GST_ELEMENT_VISITED) - -typedef struct _GstOptScheduler GstOptScheduler; -typedef struct _GstOptSchedulerClass GstOptSchedulerClass; - -#define GST_TYPE_OPT_SCHEDULER \ - (gst_opt_scheduler_get_type()) -#define GST_OPT_SCHEDULER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OPT_SCHEDULER,GstOptScheduler)) -#define GST_OPT_SCHEDULER_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OPT_SCHEDULER,GstOptSchedulerClass)) -#define GST_IS_OPT_SCHEDULER(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OPT_SCHEDULER)) -#define GST_IS_OPT_SCHEDULER_CLASS(obj) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OPT_SCHEDULER)) - -typedef enum -{ - GST_OPT_SCHEDULER_STATE_NONE, - GST_OPT_SCHEDULER_STATE_STOPPED, - GST_OPT_SCHEDULER_STATE_ERROR, - GST_OPT_SCHEDULER_STATE_RUNNING, - GST_OPT_SCHEDULER_STATE_INTERRUPTED -} -GstOptSchedulerState; - -struct _GstOptScheduler -{ - GstScheduler parent; - - GstOptSchedulerState state; - -#ifdef USE_COTHREADS - cothread_context *context; -#endif - gint iterations; - - GSList *elements; - GSList *chains; - - GList *runqueue; - gint recursion; - - gint max_recursion; - gint live_groups; - gint live_chains; - gint live_links; -}; - -struct _GstOptSchedulerClass -{ - GstSchedulerClass parent_class; -}; - -static GType _gst_opt_scheduler_type = 0; - -typedef enum -{ - GST_OPT_SCHEDULER_CHAIN_DIRTY = (1 << 1), - GST_OPT_SCHEDULER_CHAIN_DISABLED = (1 << 2), - GST_OPT_SCHEDULER_CHAIN_RUNNING = (1 << 3) -} -GstOptSchedulerChainFlags; - -#define GST_OPT_SCHEDULER_CHAIN_SET_DIRTY(chain) ((chain)->flags |= GST_OPT_SCHEDULER_CHAIN_DIRTY) -#define GST_OPT_SCHEDULER_CHAIN_SET_CLEAN(chain) ((chain)->flags &= ~GST_OPT_SCHEDULER_CHAIN_DIRTY) -#define GST_OPT_SCHEDULER_CHAIN_IS_DIRTY(chain) ((chain)->flags & GST_OPT_SCHEDULER_CHAIN_DIRTY) - -#define GST_OPT_SCHEDULER_CHAIN_DISABLE(chain) ((chain)->flags |= GST_OPT_SCHEDULER_CHAIN_DISABLED) -#define GST_OPT_SCHEDULER_CHAIN_ENABLE(chain) ((chain)->flags &= ~GST_OPT_SCHEDULER_CHAIN_DISABLED) -#define GST_OPT_SCHEDULER_CHAIN_IS_DISABLED(chain) ((chain)->flags & GST_OPT_SCHEDULER_CHAIN_DISABLED) - -typedef struct _GstOptSchedulerChain GstOptSchedulerChain; - -struct _GstOptSchedulerChain -{ - gint refcount; - - GstOptScheduler *sched; - - GstOptSchedulerChainFlags flags; - - GSList *groups; /* the groups in this chain */ - gint num_groups; - gint num_enabled; -}; - -/* - * elements that are scheduled in one cothread - */ -typedef enum -{ - GST_OPT_SCHEDULER_GROUP_DIRTY = (1 << 1), /* this group has been modified */ - GST_OPT_SCHEDULER_GROUP_COTHREAD_STOPPING = (1 << 2), /* the group's cothread stops after one iteration */ - GST_OPT_SCHEDULER_GROUP_DISABLED = (1 << 3), /* this group is disabled */ - GST_OPT_SCHEDULER_GROUP_RUNNING = (1 << 4), /* this group is running */ - GST_OPT_SCHEDULER_GROUP_SCHEDULABLE = (1 << 5), /* this group is schedulable */ - GST_OPT_SCHEDULER_GROUP_VISITED = (1 << 6) /* this group is visited when finding links */ -} -GstOptSchedulerGroupFlags; - -typedef enum -{ - GST_OPT_SCHEDULER_GROUP_UNKNOWN = 3, - GST_OPT_SCHEDULER_GROUP_GET = 1, - GST_OPT_SCHEDULER_GROUP_LOOP = 2 -} -GstOptSchedulerGroupType; - -#define GST_OPT_SCHEDULER_GROUP_SET_FLAG(group,flag) ((group)->flags |= (flag)) -#define GST_OPT_SCHEDULER_GROUP_UNSET_FLAG(group,flag) ((group)->flags &= ~(flag)) -#define GST_OPT_SCHEDULER_GROUP_IS_FLAG_SET(group,flag) ((group)->flags & (flag)) - -#define GST_OPT_SCHEDULER_GROUP_DISABLE(group) ((group)->flags |= GST_OPT_SCHEDULER_GROUP_DISABLED) -#define GST_OPT_SCHEDULER_GROUP_ENABLE(group) ((group)->flags &= ~GST_OPT_SCHEDULER_GROUP_DISABLED) -#define GST_OPT_SCHEDULER_GROUP_IS_ENABLED(group) (!((group)->flags & GST_OPT_SCHEDULER_GROUP_DISABLED)) -#define GST_OPT_SCHEDULER_GROUP_IS_DISABLED(group) ((group)->flags & GST_OPT_SCHEDULER_GROUP_DISABLED) - - -typedef struct _GstOptSchedulerGroup GstOptSchedulerGroup; -typedef struct _GstOptSchedulerGroupLink GstOptSchedulerGroupLink; - -/* used to keep track of links with other groups */ -struct _GstOptSchedulerGroupLink -{ - GstOptSchedulerGroup *src; /* the group we are linked with */ - GstOptSchedulerGroup *sink; /* the group we are linked with */ - gint count; /* the number of links with the group */ -}; - -#define IS_GROUP_LINK(link, srcg, sinkg) ((link->src == srcg && link->sink == sinkg) || \ - (link->sink == srcg && link->src == sinkg)) -#define OTHER_GROUP_LINK(link, group) (link->src == group ? link->sink : link->src) - -typedef int (*GroupScheduleFunction) (int argc, char *argv[]); - -struct _GstOptSchedulerGroup -{ - GstOptSchedulerChain *chain; /* the chain this group belongs to */ - GstOptSchedulerGroupFlags flags; /* flags for this group */ - GstOptSchedulerGroupType type; /* flags for this group */ - GstOptScheduler *sched; /* the scheduler */ - - gint refcount; - - GSList *elements; /* elements of this group */ - gint num_elements; - gint num_enabled; - GstElement *entry; /* the group's entry point */ - - GSList *group_links; /* other groups that are linked with this group */ - -#ifdef USE_COTHREADS - cothread *cothread; /* the cothread of this group */ -#else - GroupScheduleFunction schedulefunc; -#endif - int argc; - char **argv; -}; - -/* - * 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); -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, gboolean with_links); -static GstOptSchedulerGroup *remove_from_group (GstOptSchedulerGroup * group, - GstElement * element); -static void group_dec_links_for_element (GstOptSchedulerGroup * group, - GstElement * element); -static void group_inc_links_for_element (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); -static gboolean schedule_group (GstOptSchedulerGroup * group); -static void get_group (GstElement * element, 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); -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); - - -/* - * 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 - */ -typedef struct _GstOptSchedulerCtx GstOptSchedulerCtx; - -typedef enum -{ - GST_OPT_SCHEDULER_CTX_DISABLED = (1 << 1) /* the element is disabled */ -} -GstOptSchedulerCtxFlags; - -struct _GstOptSchedulerCtx -{ - GstOptSchedulerGroup *group; /* the group this element belongs to */ - - 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 gst_opt_scheduler_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec); -static void gst_opt_scheduler_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec); - -static void gst_opt_scheduler_dispose (GObject * object); - -static void gst_opt_scheduler_setup (GstScheduler * sched); -static void gst_opt_scheduler_reset (GstScheduler * sched); -static void gst_opt_scheduler_add_element (GstScheduler * sched, - GstElement * element); -static void gst_opt_scheduler_remove_element (GstScheduler * sched, - GstElement * element); -static GstElementStateReturn gst_opt_scheduler_state_transition (GstScheduler * - sched, GstElement * element, gint transition); -static void gst_opt_scheduler_scheduling_change (GstScheduler * sched, - GstElement * element); -static gboolean gst_opt_scheduler_yield (GstScheduler * sched, - GstElement * element); -static gboolean gst_opt_scheduler_interrupt (GstScheduler * sched, - GstElement * element); -static void gst_opt_scheduler_error (GstScheduler * sched, - GstElement * element); -static void gst_opt_scheduler_pad_link (GstScheduler * sched, GstPad * srcpad, - GstPad * sinkpad); -static void gst_opt_scheduler_pad_unlink (GstScheduler * sched, GstPad * srcpad, - GstPad * sinkpad); -static GstSchedulerState gst_opt_scheduler_iterate (GstScheduler * sched); - -static void gst_opt_scheduler_show (GstScheduler * sched); - -static GstSchedulerClass *parent_class = NULL; - -static GType -gst_opt_scheduler_get_type (void) -{ - if (!_gst_opt_scheduler_type) { - static const GTypeInfo scheduler_info = { - sizeof (GstOptSchedulerClass), - NULL, - NULL, - (GClassInitFunc) gst_opt_scheduler_class_init, - NULL, - NULL, - sizeof (GstOptScheduler), - 0, - (GInstanceInitFunc) gst_opt_scheduler_init, - NULL - }; - - _gst_opt_scheduler_type = g_type_register_static (GST_TYPE_SCHEDULER, - "GstOpt" COTHREADS_NAME_CAPITAL "Scheduler", &scheduler_info, 0); - } - return _gst_opt_scheduler_type; -} - -static void -gst_opt_scheduler_class_init (GstOptSchedulerClass * klass) -{ - GObjectClass *gobject_class; - GstObjectClass *gstobject_class; - GstSchedulerClass *gstscheduler_class; - - gobject_class = (GObjectClass *) klass; - gstobject_class = (GstObjectClass *) klass; - gstscheduler_class = (GstSchedulerClass *) klass; - - parent_class = g_type_class_ref (GST_TYPE_SCHEDULER); - - gobject_class->set_property = - GST_DEBUG_FUNCPTR (gst_opt_scheduler_set_property); - gobject_class->get_property = - GST_DEBUG_FUNCPTR (gst_opt_scheduler_get_property); - gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_opt_scheduler_dispose); - - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ITERATIONS, - g_param_spec_int ("iterations", "Iterations", - "Number of groups to schedule in one iteration (-1 == until EOS/error)", - -1, G_MAXINT, 1, G_PARAM_READWRITE)); -#ifndef USE_COTHREADS - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MAX_RECURSION, - g_param_spec_int ("max_recursion", "Max recursion", - "Maximum number of recursions", 1, G_MAXINT, 100, G_PARAM_READWRITE)); -#endif - - gstscheduler_class->setup = GST_DEBUG_FUNCPTR (gst_opt_scheduler_setup); - gstscheduler_class->reset = GST_DEBUG_FUNCPTR (gst_opt_scheduler_reset); - gstscheduler_class->add_element = - GST_DEBUG_FUNCPTR (gst_opt_scheduler_add_element); - gstscheduler_class->remove_element = - GST_DEBUG_FUNCPTR (gst_opt_scheduler_remove_element); - gstscheduler_class->state_transition = - GST_DEBUG_FUNCPTR (gst_opt_scheduler_state_transition); - gstscheduler_class->scheduling_change = - GST_DEBUG_FUNCPTR (gst_opt_scheduler_scheduling_change); - gstscheduler_class->yield = GST_DEBUG_FUNCPTR (gst_opt_scheduler_yield); - gstscheduler_class->interrupt = - GST_DEBUG_FUNCPTR (gst_opt_scheduler_interrupt); - gstscheduler_class->error = GST_DEBUG_FUNCPTR (gst_opt_scheduler_error); - gstscheduler_class->pad_link = GST_DEBUG_FUNCPTR (gst_opt_scheduler_pad_link); - gstscheduler_class->pad_unlink = - GST_DEBUG_FUNCPTR (gst_opt_scheduler_pad_unlink); - gstscheduler_class->clock_wait = NULL; - gstscheduler_class->iterate = GST_DEBUG_FUNCPTR (gst_opt_scheduler_iterate); - gstscheduler_class->show = GST_DEBUG_FUNCPTR (gst_opt_scheduler_show); - -#ifdef USE_COTHREADS - do_cothreads_init (NULL); -#endif -} - -static void -gst_opt_scheduler_init (GstOptScheduler * scheduler) -{ - scheduler->elements = NULL; - scheduler->iterations = 1; - scheduler->max_recursion = 100; - scheduler->live_groups = 0; - scheduler->live_chains = 0; - scheduler->live_links = 0; -} - -static void -gst_opt_scheduler_dispose (GObject * object) -{ - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -static gboolean -plugin_init (GstPlugin * plugin) -{ -#ifdef USE_COTHREADS - if (!gst_scheduler_register (plugin, "opt" COTHREADS_NAME, - "An optimal scheduler using " COTHREADS_NAME " cothreads", - gst_opt_scheduler_get_type ())) -#else - if (!gst_scheduler_register (plugin, "opt", - "An optimal scheduler using no cothreads", - gst_opt_scheduler_get_type ())) -#endif - return FALSE; - - GST_DEBUG_CATEGORY_INIT (debug_scheduler, "scheduler", 0, - "optimal scheduler"); - - return TRUE; -} - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, - GST_VERSION_MINOR, - "gstopt" COTHREADS_NAME "scheduler", - "An optimal scheduler using " COTHREADS_NAME " cothreads", - plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN); - - -static GstOptSchedulerChain * -ref_chain (GstOptSchedulerChain * chain) -{ - GST_LOG ("ref chain %p %d->%d", chain, chain->refcount, chain->refcount + 1); - chain->refcount++; - - return chain; -} - -static GstOptSchedulerChain * -unref_chain (GstOptSchedulerChain * chain) -{ - GST_LOG ("unref chain %p %d->%d", chain, - chain->refcount, chain->refcount - 1); - - if (--chain->refcount == 0) { - destroy_chain (chain); - chain = NULL; - } - - return chain; -} - -static GstOptSchedulerChain * -create_chain (GstOptScheduler * osched) -{ - GstOptSchedulerChain *chain; - - chain = g_new0 (GstOptSchedulerChain, 1); - chain->sched = osched; - chain->refcount = 1; - chain->flags = GST_OPT_SCHEDULER_CHAIN_DISABLED; - osched->live_chains++; - - gst_object_ref (GST_OBJECT (osched)); - osched->chains = g_slist_prepend (osched->chains, chain); - - GST_LOG ("new chain %p, %d live chains now", chain, osched->live_chains); - - return chain; -} - -static void -destroy_chain (GstOptSchedulerChain * chain) -{ - GstOptScheduler *osched; - - GST_LOG ("destroy chain %p", chain); - - g_assert (chain->num_groups == 0); - g_assert (chain->groups == NULL); - - osched = chain->sched; - osched->chains = g_slist_remove (osched->chains, chain); - osched->live_chains--; - - GST_LOG ("%d live chains now", osched->live_chains); - - gst_object_unref (GST_OBJECT (osched)); - - g_free (chain); -} - -static GstOptSchedulerChain * -add_to_chain (GstOptSchedulerChain * chain, GstOptSchedulerGroup * group) -{ - gboolean enabled; - - 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); - chain->num_groups++; - - enabled = GST_OPT_SCHEDULER_GROUP_IS_ENABLED (group); - - if (enabled) { - /* we can now setup the scheduling of the group */ - setup_group_scheduler (chain->sched, group); - - chain->num_enabled++; - if (chain->num_enabled == chain->num_groups) { - GST_LOG ("enabling chain %p after adding of enabled group", chain); - GST_OPT_SCHEDULER_CHAIN_ENABLE (chain); - } - } - - /* queue a resort of the group list, which determines which group will be run - * first. */ - GST_OPT_SCHEDULER_CHAIN_SET_DIRTY (chain); - - return chain; -} - -static GstOptSchedulerChain * -remove_from_chain (GstOptSchedulerChain * chain, GstOptSchedulerGroup * group) -{ - gboolean enabled; - - GST_LOG ("removing group %p from chain %p", group, chain); - - if (!chain) - return NULL; - - g_assert (group); - g_assert (group->chain == chain); - - enabled = GST_OPT_SCHEDULER_GROUP_IS_ENABLED (group); - - group->chain = NULL; - chain->groups = g_slist_remove (chain->groups, group); - chain->num_groups--; - unref_group (group); - - if (chain->num_groups == 0) - chain = unref_chain (chain); - else { - /* removing an enabled group from the chain decrements the - * enabled counter */ - if (enabled) { - chain->num_enabled--; - if (chain->num_enabled == 0) { - GST_LOG ("disabling chain %p after removal of the only enabled group", - chain); - GST_OPT_SCHEDULER_CHAIN_DISABLE (chain); - } - } else { - if (chain->num_enabled == chain->num_groups) { - GST_LOG ("enabling chain %p after removal of the only disabled group", - chain); - GST_OPT_SCHEDULER_CHAIN_ENABLE (chain); - } - } - } - - GST_OPT_SCHEDULER_CHAIN_SET_DIRTY (chain); - - chain = unref_chain (chain); - return chain; -} - -static GstOptSchedulerChain * -merge_chains (GstOptSchedulerChain * chain1, GstOptSchedulerChain * chain2) -{ - GSList *walk; - - g_assert (chain1 != NULL); - - GST_LOG ("merging chain %p and %p", chain1, chain2); - - /* FIXME: document how chain2 can be NULL */ - if (chain1 == chain2 || chain2 == NULL) - return chain1; - - /* 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; - - walk = g_slist_next (walk); - - GST_LOG ("reparenting group %p from chain %p to %p", group, chain2, chain1); - - ref_group (group); - - remove_from_chain (chain2, group); - add_to_chain (chain1, group); - - unref_group (group); - } - - /* chain2 is now freed, if nothing else was referencing it before */ - - return chain1; -} - -/* sorts the group list so that terminal sinks come first -- prevents pileup of - * datas in datapens */ -static void -sort_chain (GstOptSchedulerChain * chain) -{ - GSList *original = chain->groups; - GSList *new = NULL; - GSList *walk, *links, *this; - - /* if there's only one group, just return */ - if (!original->next) - return; - /* otherwise, we know that all groups are somehow linked together */ - - GST_LOG ("sorting chain %p (%d groups)", chain, g_slist_length (original)); - - /* first find the terminal sinks */ - for (walk = original; walk;) { - GstOptSchedulerGroup *group = (GstOptSchedulerGroup *) walk->data; - - this = walk; - walk = walk->next; - if (group->group_links) { - gboolean is_sink = TRUE; - - for (links = group->group_links; links; links = links->next) - if (((GstOptSchedulerGroupLink *) links->data)->src == group) - is_sink = FALSE; - if (is_sink) { - /* found one */ - original = g_slist_remove_link (original, this); - new = g_slist_concat (new, this); - } - } - } - g_assert (new != NULL); - - /* now look for the elements that are linked to the terminal sinks */ - for (walk = new; walk; walk = walk->next) { - GstOptSchedulerGroup *group = (GstOptSchedulerGroup *) walk->data; - - for (links = group->group_links; links; links = links->next) { - this = - g_slist_find (original, - ((GstOptSchedulerGroupLink *) links->data)->src); - if (this) { - original = g_slist_remove_link (original, this); - new = g_slist_concat (new, this); - } - } - } - g_assert (original == NULL); - - chain->groups = new; -} - -static void -chain_group_set_enabled (GstOptSchedulerChain * chain, - GstOptSchedulerGroup * group, gboolean enabled) -{ - gboolean oldstate; - - 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); - - oldstate = (GST_OPT_SCHEDULER_GROUP_IS_ENABLED (group) ? TRUE : FALSE); - if (oldstate == enabled) { - GST_LOG ("group %p in chain %p was in correct state", group, chain); - return; - } - - if (enabled) - GST_OPT_SCHEDULER_GROUP_ENABLE (group); - else - GST_OPT_SCHEDULER_GROUP_DISABLE (group); - - if (enabled) { - g_assert (chain->num_enabled < chain->num_groups); - - chain->num_enabled++; - - 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); - } - } else { - g_assert (chain->num_enabled > 0); - - chain->num_enabled--; - GST_DEBUG ("disable group %p in chain %p, now %d groups enabled out of %d", - group, chain, chain->num_enabled, chain->num_groups); - - if (chain->num_enabled == 0) { - GST_DEBUG ("disable chain %p", chain); - GST_OPT_SCHEDULER_CHAIN_DISABLE (chain); - } - } -} - -/* recursively migrate the group and all connected groups into the new chain */ -static void -chain_recursively_migrate_group (GstOptSchedulerChain * chain, - GstOptSchedulerGroup * group) -{ - GSList *links; - - /* group already in chain */ - if (group->chain == chain) - return; - - /* first remove the group from its old chain */ - remove_from_chain (group->chain, group); - /* add to new chain */ - add_to_chain (chain, group); - - /* then follow all links */ - for (links = group->group_links; links; links = g_slist_next (links)) { - GstOptSchedulerGroupLink *link = (GstOptSchedulerGroupLink *) links->data; - - chain_recursively_migrate_group (chain, OTHER_GROUP_LINK (link, group)); - } -} - -static GstOptSchedulerGroup * -ref_group (GstOptSchedulerGroup * group) -{ - GST_LOG ("ref group %p %d->%d", group, group->refcount, group->refcount + 1); - - group->refcount++; - - return group; -} - -static GstOptSchedulerGroup * -unref_group (GstOptSchedulerGroup * group) -{ - GST_LOG ("unref group %p %d->%d", group, - group->refcount, group->refcount - 1); - - if (--group->refcount == 0) { - destroy_group (group); - group = NULL; - } - - return group; -} - -static GstOptSchedulerGroup * -create_group (GstOptSchedulerChain * chain, GstElement * element, - GstOptSchedulerGroupType type) -{ - GstOptSchedulerGroup *group; - - group = g_new0 (GstOptSchedulerGroup, 1); - GST_LOG ("new group %p, type %d", group, type); - group->refcount = 1; /* float... */ - group->flags = GST_OPT_SCHEDULER_GROUP_DISABLED; - group->type = type; - group->sched = chain->sched; - group->sched->live_groups++; - - add_to_group (group, element, FALSE); - add_to_chain (chain, group); - group = unref_group (group); /* ...and sink. */ - - GST_LOG ("%d live groups now", group->sched->live_groups); - /* 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); - g_assert (group->group_links == NULL); - - if (group->flags & GST_OPT_SCHEDULER_GROUP_SCHEDULABLE) - destroy_group_scheduler (group); - - group->sched->live_groups--; - GST_LOG ("%d live groups now", group->sched->live_groups); - - g_free (group); -} - -static GstOptSchedulerGroup * -add_to_group (GstOptSchedulerGroup * group, GstElement * element, - gboolean with_links) -{ - g_assert (group != NULL); - g_assert (element != NULL); - - GST_DEBUG ("adding element %p \"%s\" to group %p", element, - GST_ELEMENT_NAME (element), group); - - if (GST_ELEMENT_IS_DECOUPLED (element)) { - GST_DEBUG ("element \"%s\" is decoupled, not adding to group %p", - GST_ELEMENT_NAME (element), group); - return group; - } - - g_assert (GST_ELEMENT_SCHED_GROUP (element) == NULL); - - /* first increment the links that this group has with other groups through - * this element */ - if (with_links) - group_inc_links_for_element (group, element); - - /* Ref the group... */ - GST_ELEMENT_SCHED_GROUP (element) = ref_group (group); - - gst_object_ref (GST_OBJECT (element)); - group->elements = g_slist_prepend (group->elements, element); - group->num_elements++; - - if (gst_element_get_state (element) == GST_STATE_PLAYING) { - group_element_set_enabled (group, element, TRUE); - } - - return group; -} - -static GstOptSchedulerGroup * -remove_from_group (GstOptSchedulerGroup * group, GstElement * element) -{ - GST_DEBUG ("removing element %p \"%s\" from group %p", - element, GST_ELEMENT_NAME (element), group); - - g_assert (group != NULL); - g_assert (element != NULL); - /* this assert also catches the decoupled elements */ - g_assert (GST_ELEMENT_SCHED_GROUP (element) == group); - - /* first decrement the links that this group has with other groups through - * this element */ - group_dec_links_for_element (group, element); - - if (gst_element_get_state (element) == GST_STATE_PLAYING) { - group_element_set_enabled (group, element, FALSE); - } - - group->elements = g_slist_remove (group->elements, element); - group->num_elements--; - - /* if the element was an entry point in the group, clear the group's - * entry point, and mark it as unknown */ - if (group->entry == element) { - group->entry = NULL; - group->type = GST_OPT_SCHEDULER_GROUP_UNKNOWN; - } - - GST_ELEMENT_SCHED_GROUP (element) = NULL; - gst_object_unref (GST_OBJECT (element)); - - if (group->num_elements == 0) { - 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; -} - -/* check if an element is part of the given group. We have to be carefull - * as decoupled elements are added as entry but are not added to the elements - * list */ -static gboolean -group_has_element (GstOptSchedulerGroup * group, GstElement * element) -{ - if (group->entry == element) - return TRUE; - - return (g_slist_find (group->elements, element) != NULL); -} - -/* 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) -{ - g_assert (group1 != NULL); - - GST_DEBUG ("merging groups %p and %p", group1, group2); - - if (group1 == group2 || group2 == NULL) - return group1; - - /* make sure they end up in the same chain */ - merge_chains (group1->chain, group2->chain); - - while (group2 && group2->elements) { - GstElement *element = (GstElement *) group2->elements->data; - - group2 = remove_from_group (group2, element); - add_to_group (group1, element, TRUE); - } - - 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; - - GST_DEBUG ("setup group %p scheduler, type %d", group, group->type); - - 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 - /* we can only initialize the cothread stuff when we have - * a cothread context */ - if (osched->context) { - 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_DEBUG ("group %p has errored", group); - - chain_group_set_enabled (group->chain, group, FALSE); - group->chain->sched->state = GST_OPT_SCHEDULER_STATE_ERROR; -} - -/* this function enables/disables an element, it will set/clear a flag on the element - * and tells the chain that the group is enabled if all elements inside the group are - * enabled */ -static void -group_element_set_enabled (GstOptSchedulerGroup * group, GstElement * element, - gboolean enabled) -{ - g_assert (group != NULL); - g_assert (element != NULL); - - 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) { - g_assert (group->num_enabled < group->num_elements); - - group->num_enabled++; - - GST_DEBUG - ("enable element %s in group %p, now %d elements enabled out of %d", - GST_ELEMENT_NAME (element), group, group->num_enabled, - group->num_elements); - - if (group->num_enabled == group->num_elements) { - 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 { - g_assert (group->num_enabled > 0); - - group->num_enabled--; - - GST_DEBUG - ("disable element %s in group %p, now %d elements enabled out of %d", - GST_ELEMENT_NAME (element), group, group->num_enabled, - group->num_elements); - - if (group->num_enabled == 0) { - 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); - } - } - } -} - -/* a group is scheduled by doing a cothread switch to it or - * by calling the schedule function. In the non-cothread case - * we cannot run already running groups so we return FALSE here - * to indicate this to the caller */ -static gboolean -schedule_group (GstOptSchedulerGroup * group) -{ - if (!group->entry) { - GST_INFO ("not scheduling group %p without entry", group); - /* FIXME, we return true here, while the group is actually - * not schedulable. We might want to disable the element that caused - * this group to be scheduled instead */ - return TRUE; - } -#ifdef USE_COTHREADS - if (group->cothread) - do_cothread_switch (group->cothread); - else - g_warning ("(internal error): trying to schedule group without cothread"); - return TRUE; -#else - /* cothreads automatically call the pre- and post-run functions for us; - * without cothreads we need to call them manually */ - if (group->schedulefunc == NULL) { - GST_INFO ("not scheduling group %p without schedulefunc", group); - return FALSE; - } else { - GSList *l, *lcopy; - - lcopy = g_slist_copy (group->elements); - for (l = lcopy; l; l = l->next) { - GstElement *e = (GstElement *) l->data; - - gst_object_ref (GST_OBJECT (e)); - if (e->pre_run_func) - e->pre_run_func (e); - } - - group->schedulefunc (group->argc, group->argv); - - for (l = lcopy; l; l = l->next) { - GstElement *e = (GstElement *) l->data; - - if (e->post_run_func) - e->post_run_func (e); - - gst_object_unref (GST_OBJECT (e)); - } - g_slist_free (lcopy); - - } - return TRUE; -#endif -} - -#ifndef USE_COTHREADS -static void -gst_opt_scheduler_schedule_run_queue (GstOptScheduler * osched) -{ - 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) { - osched->state = GST_OPT_SCHEDULER_STATE_ERROR; - return; - } - - osched->recursion++; - - while (osched->runqueue) { - GstOptSchedulerGroup *group; - gboolean res; - - group = (GstOptSchedulerGroup *) osched->runqueue->data; - - /* runqueue holds refcount to group */ - osched->runqueue = g_list_remove (osched->runqueue, group); - - GST_LOG_OBJECT (osched, "scheduling group %p", group); - - if (GST_OPT_SCHEDULER_GROUP_IS_ENABLED (group)) { - res = schedule_group (group); - } else { - GST_INFO_OBJECT (osched, - "group was disabled while it was on the queue, not scheduling"); - res = TRUE; - } - if (!res) { - g_warning ("error scheduling group %p", group); - group_error_handler (group); - } else { - GST_LOG_OBJECT (osched, "done scheduling group %p", group); - } - unref_group (group); - } - - GST_LOG_OBJECT (osched, "run queue length after scheduling %d", - g_list_length (osched->runqueue)); - - osched->recursion--; -} -#endif - -/* a chain is scheduled by picking the first active group and scheduling it */ -static void -schedule_chain (GstOptSchedulerChain * chain) -{ - GSList *groups; - GstOptScheduler *osched; - - osched = chain->sched; - - /* if the chain has changed, we need to resort the groups so we enter in the - proper place */ - if (GST_OPT_SCHEDULER_CHAIN_IS_DIRTY (chain)) - sort_chain (chain); - GST_OPT_SCHEDULER_CHAIN_SET_CLEAN (chain); - - groups = chain->groups; - while (groups) { - GstOptSchedulerGroup *group = (GstOptSchedulerGroup *) groups->data; - - if (!GST_OPT_SCHEDULER_GROUP_IS_DISABLED (group)) { - ref_group (group); - GST_LOG ("scheduling group %p in chain %p", group, chain); - -#ifdef USE_COTHREADS - schedule_group (group); -#else - osched->recursion = 0; - if (!g_list_find (osched->runqueue, group)) { - ref_group (group); - osched->runqueue = g_list_append (osched->runqueue, group); - } - gst_opt_scheduler_schedule_run_queue (osched); -#endif - - GST_LOG ("done scheduling group %p in chain %p", group, chain); - unref_group (group); - break; - } - - groups = g_slist_next (groups); - } -} - -/* a get-based group is scheduled by getting a buffer from the get based - * entry point and by pushing the buffer to the peer. - * We also set the running flag on this group for as long as this - * function is running. */ -static int -get_group_schedule_function (int argc, char *argv[]) -{ - GstOptSchedulerGroup *group = (GstOptSchedulerGroup *) argv; - GstElement *entry = group->entry; - const GList *pads = gst_element_get_pad_list (entry); - - GST_LOG ("executing get-based group %p", group); - - group->flags |= GST_OPT_SCHEDULER_GROUP_RUNNING; - - while (pads) { - GstData *data; - GstPad *pad = GST_PAD (pads->data); - - pads = g_list_next (pads); - - /* skip sinks and ghostpads */ - if (!GST_PAD_IS_SRC (pad) || !GST_IS_REAL_PAD (pad)) - continue; - - GST_DEBUG ("doing get and push on pad \"%s:%s\" in group %p", - GST_DEBUG_PAD_NAME (pad), group); - - data = gst_pad_call_get_function (pad); - if (data) { - if (GST_EVENT_IS_INTERRUPT (data)) { - GST_DEBUG ("unreffing interrupt event %p", data); - gst_event_unref (GST_EVENT (data)); - break; - } - gst_pad_push (pad, data); - } - } - - group->flags &= ~GST_OPT_SCHEDULER_GROUP_RUNNING; - - return 0; -} - -/* a loop-based group is scheduled by calling the loop function - * on the entry point. - * We also set the running flag on this group for as long as this - * function is running. */ -static int -loop_group_schedule_function (int argc, char *argv[]) -{ - GstOptSchedulerGroup *group = (GstOptSchedulerGroup *) argv; - GstElement *entry = group->entry; - - GST_LOG ("executing loop-based group %p", group); - - group->flags |= GST_OPT_SCHEDULER_GROUP_RUNNING; - - GST_DEBUG ("calling loopfunc of element %s in group %p", - GST_ELEMENT_NAME (entry), group); - - if (entry->loopfunc) - entry->loopfunc (entry); - else - group_error_handler (group); - - GST_LOG ("returned from loopfunc of element %s in group %p", - GST_ELEMENT_NAME (entry), group); - - group->flags &= ~GST_OPT_SCHEDULER_GROUP_RUNNING; - - return 0; - -} - -/* the function to schedule an unknown group, which just gives an error */ -static int -unknown_group_schedule_function (int argc, char *argv[]) -{ - GstOptSchedulerGroup *group = (GstOptSchedulerGroup *) argv; - - g_warning ("(internal error) unknown group type %d, disabling\n", - group->type); - group_error_handler (group); - - return 0; -} - -/* this function is called when the first element of a chain-loop or a loop-loop - * link performs a push to the loop element. We then schedule the - * group with the loop-based element until the datapen is empty */ -static void -gst_opt_scheduler_loop_wrapper (GstPad * sinkpad, GstData * data) -{ - GstOptSchedulerGroup *group; - GstOptScheduler *osched; - 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_DATALIST (peer)) { - g_warning ("deadlock detected, disabling group %p", group); - group_error_handler (group); - } else { - GST_LOG ("queueing data %p on %s:%s's datapen", data, - GST_DEBUG_PAD_NAME (peer)); - GST_PAD_DATAPEN (peer) = g_list_append (GST_PAD_DATALIST (peer), data); - schedule_group (group); - } -#else - GST_LOG ("queueing data %p on %s:%s's datapen", data, - GST_DEBUG_PAD_NAME (peer)); - GST_PAD_DATAPEN (peer) = g_list_append (GST_PAD_DATALIST (peer), data); - if (!(group->flags & GST_OPT_SCHEDULER_GROUP_RUNNING)) { - GST_LOG ("adding group %p to runqueue", group); - if (!g_list_find (osched->runqueue, group)) { - ref_group (group); - osched->runqueue = g_list_append (osched->runqueue, group); - } - } -#endif - - GST_LOG ("%d datas left on %s:%s's datapen after chain handler", - g_list_length (GST_PAD_DATALIST (peer)), GST_DEBUG_PAD_NAME (peer)); -} - -/* this function is called by a loop based element that performs a - * pull on a sinkpad. We schedule the peer group until the datapen - * is filled with the data so that this function can return */ -static GstData * -gst_opt_scheduler_get_wrapper (GstPad * srcpad) -{ - GstData *data; - GstOptSchedulerGroup *group; - GstOptScheduler *osched; - gboolean disabled; - - GST_LOG ("get handler for %" GST_PTR_FORMAT, srcpad); - - /* first try to grab a queued data */ - if (GST_PAD_DATALIST (srcpad)) { - data = GST_PAD_DATALIST (srcpad)->data; - GST_PAD_DATAPEN (srcpad) = g_list_remove (GST_PAD_DATALIST (srcpad), data); - - GST_LOG ("returning popped queued data %p", data); - - return data; - } - GST_LOG ("need to schedule the peer element"); - - /* else we need to schedule the peer element */ - get_group (GST_PAD_PARENT (srcpad), &group); - if (group == NULL) { - /* wow, peer has no group */ - GST_LOG ("peer without group detected"); - //group_error_handler (group); - return GST_DATA (gst_event_new (GST_EVENT_INTERRUPT)); - } - osched = group->chain->sched; - data = NULL; - disabled = FALSE; - - do { - GST_LOG ("scheduling upstream group %p to fill datapen", group); -#ifdef USE_COTHREADS - schedule_group (group); -#else - if (!(group->flags & GST_OPT_SCHEDULER_GROUP_RUNNING)) { - ref_group (group); - - if (!g_list_find (osched->runqueue, group)) { - ref_group (group); - osched->runqueue = g_list_append (osched->runqueue, group); - } - - GST_LOG ("recursing into scheduler group %p", group); - gst_opt_scheduler_schedule_run_queue (osched); - 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); - group = unref_group (group); - /* group is gone */ - if (group == NULL) { - /* if the group was gone we also might have to break out of the loop */ - disabled = TRUE; - } - } else { - /* in this case, the group was running and we wanted to swtich to it, - * this is not allowed in the optimal scheduler (yet) */ - g_warning ("deadlock detected, disabling group %p", group); - group_error_handler (group); - return GST_DATA (gst_event_new (GST_EVENT_INTERRUPT)); - } -#endif - /* if the scheduler interrupted, make sure we send an INTERRUPTED event - * to the loop based element */ - if (osched->state == GST_OPT_SCHEDULER_STATE_INTERRUPTED) { - GST_INFO ("scheduler interrupted, return interrupt event"); - data = GST_DATA (gst_event_new (GST_EVENT_INTERRUPT)); - } else { - if (GST_PAD_DATALIST (srcpad)) { - data = GST_PAD_DATALIST (srcpad)->data; - GST_PAD_DATAPEN (srcpad) = - g_list_remove (GST_PAD_DATALIST (srcpad), data); - } else if (disabled) { - /* no data in queue and peer group was disabled */ - data = GST_DATA (gst_event_new (GST_EVENT_INTERRUPT)); - } - } - } - while (data == NULL); - - GST_LOG ("get handler, returning data %p, queue length %d", - data, g_list_length (GST_PAD_DATALIST (srcpad))); - - return data; -} - -static void -pad_clear_queued (GstPad * srcpad, gpointer user_data) -{ - GList *datalist = GST_PAD_DATALIST (srcpad); - - if (datalist) { - GST_LOG ("need to clear some datas"); - g_list_foreach (datalist, (GFunc) gst_data_unref, NULL); - g_list_free (datalist); - GST_PAD_DATAPEN (srcpad) = NULL; - } -} - -static gboolean -gst_opt_scheduler_event_wrapper (GstPad * srcpad, GstEvent * event) -{ - gboolean flush; - - GST_DEBUG ("intercepting event type %d on pad %s:%s", - GST_EVENT_TYPE (event), GST_DEBUG_PAD_NAME (srcpad)); - - /* figure out if this is a flush event */ - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_FLUSH: - flush = TRUE; - break; - case GST_EVENT_SEEK: - case GST_EVENT_SEEK_SEGMENT: - flush = GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH; - break; - default: - flush = FALSE; - break; - } - - if (flush) { - GST_LOG ("event triggers a flush"); - - pad_clear_queued (srcpad, NULL); - } - return GST_RPAD_EVENTFUNC (srcpad) (srcpad, event); -} - -static GstElementStateReturn -gst_opt_scheduler_state_transition (GstScheduler * sched, GstElement * element, - gint transition) -{ - GstOptSchedulerGroup *group; - GstElementStateReturn res = GST_STATE_SUCCESS; - - GST_DEBUG ("element \"%s\" state change (%04x)", - GST_ELEMENT_NAME (element) ? GST_ELEMENT_NAME (element) : "(null)", - transition); - - /* we check the state of the managing pipeline here */ - if (GST_IS_BIN (element)) { - if (GST_SCHEDULER_PARENT (sched) == element) { - GST_LOG ("parent \"%s\" changed state", - GST_ELEMENT_NAME (element) ? GST_ELEMENT_NAME (element) : "(null)"); - - switch (transition) { - case GST_STATE_PLAYING_TO_PAUSED: - GST_INFO ("setting scheduler state to stopped"); - GST_SCHEDULER_STATE (sched) = GST_SCHEDULER_STATE_STOPPED; - break; - case GST_STATE_PAUSED_TO_PLAYING: - GST_INFO ("setting scheduler state to running"); - GST_SCHEDULER_STATE (sched) = GST_SCHEDULER_STATE_RUNNING; - break; - default: - GST_LOG ("no interesting state change, doing nothing"); - } - } - return res; - } - - /* we don't care about decoupled elements after this */ - if (GST_ELEMENT_IS_DECOUPLED (element)) - return GST_STATE_SUCCESS; - - /* get the group of the element */ - group = GST_ELEMENT_SCHED_GROUP (element); - - switch (transition) { - case GST_STATE_PAUSED_TO_PLAYING: - /* an element without a group has to be an unlinked src, sink - * filter element */ - if (!group) { - GST_INFO ("element \"%s\" has no group", GST_ELEMENT_NAME (element)); - } - /* else construct the scheduling context of this group and enable it */ - else { - group_element_set_enabled (group, element, TRUE); - } - break; - case GST_STATE_PLAYING_TO_PAUSED: - /* if the element still has a group, we disable it */ - if (group) - group_element_set_enabled (group, element, FALSE); - break; - case GST_STATE_PAUSED_TO_READY: - { - GList *pads = (GList *) gst_element_get_pad_list (element); - - g_list_foreach (pads, (GFunc) pad_clear_queued, NULL); - break; - } - default: - break; - } - - return res; -} - -static void -gst_opt_scheduler_scheduling_change (GstScheduler * sched, GstElement * element) -{ - g_warning ("scheduling change, implement me"); -} - -static void -get_group (GstElement * element, GstOptSchedulerGroup ** group) -{ - GstOptSchedulerCtx *ctx; - - ctx = GST_ELEMENT_SCHED_CONTEXT (element); - if (ctx) - *group = ctx->group; - else - *group = NULL; -} - -/* - * the idea is to put the two elements into the same group. - * - When no element is inside a group, we create a new group and add - * the elements to it. - * - When one of the elements has a group, add the other element to - * that group - * - if both of the elements have a group, we merge the groups, which - * will also merge the chains. - * Group links must be managed by the caller. - */ -static GstOptSchedulerGroup * -group_elements (GstOptScheduler * osched, GstElement * element1, - GstElement * element2, GstOptSchedulerGroupType type) -{ - GstOptSchedulerGroup *group1, *group2, *group = NULL; - - get_group (element1, &group1); - get_group (element2, &group2); - - /* none of the elements is added to a group, create a new group - * and chain to add the elements to */ - if (!group1 && !group2) { - GstOptSchedulerChain *chain; - - GST_DEBUG ("creating new group to hold \"%s\" and \"%s\"", - GST_ELEMENT_NAME (element1), GST_ELEMENT_NAME (element2)); - - chain = create_chain (osched); - group = create_group (chain, element1, type); - add_to_group (group, element2, TRUE); - } - /* the first element has a group */ - else if (group1) { - GST_DEBUG ("adding \"%s\" to \"%s\"'s group", - GST_ELEMENT_NAME (element2), GST_ELEMENT_NAME (element1)); - - /* the second element also has a group, merge */ - if (group2) - merge_groups (group1, group2); - /* the second element has no group, add it to the group - * of the first element */ - else - add_to_group (group1, element2, TRUE); - - group = group1; - } - /* element1 has no group, element2 does. Add element1 to the - * group of element2 */ - else { - GST_DEBUG ("adding \"%s\" to \"%s\"'s group", - GST_ELEMENT_NAME (element1), GST_ELEMENT_NAME (element2)); - add_to_group (group2, element1, TRUE); - group = group2; - } - return group; -} - -/* - * increment link counts between groups -- it's important that src is actually - * the src group, so we can introspect the topology later - */ -static void -group_inc_link (GstOptSchedulerGroup * src, GstOptSchedulerGroup * sink) -{ - GSList *links = src->group_links; - gboolean done = FALSE; - GstOptSchedulerGroupLink *link; - - /* first try to find a previous link */ - while (links && !done) { - link = (GstOptSchedulerGroupLink *) links->data; - links = g_slist_next (links); - - if (IS_GROUP_LINK (link, src, sink)) { - /* we found a link to this group, increment the link count */ - link->count++; - GST_LOG ("incremented group link count between %p and %p to %d", - src, sink, link->count); - done = TRUE; - } - } - if (!done) { - /* no link was found, create a new one */ - link = g_new0 (GstOptSchedulerGroupLink, 1); - - link->src = src; - link->sink = sink; - link->count = 1; - - src->group_links = g_slist_prepend (src->group_links, link); - sink->group_links = g_slist_prepend (sink->group_links, link); - - src->sched->live_links++; - - GST_DEBUG ("added group link between %p and %p, %d live links now", - src, sink, src->sched->live_links); - } -} - -/* - * decrement link counts between groups, returns TRUE if the link count reaches - * 0 -- note that the groups are not necessarily ordered as (src, sink) like - * inc_link requires - */ -static gboolean -group_dec_link (GstOptSchedulerGroup * group1, GstOptSchedulerGroup * group2) -{ - GSList *links = group1->group_links; - gboolean res = FALSE; - GstOptSchedulerGroupLink *link; - - while (links) { - link = (GstOptSchedulerGroupLink *) links->data; - links = g_slist_next (links); - - if (IS_GROUP_LINK (link, group1, group2)) { - g_assert (link->count > 0); - link->count--; - GST_LOG ("link count between %p and %p is now %d", - group1, group2, link->count); - if (link->count == 0) { - GstOptSchedulerGroup *iso_group = NULL; - - group1->group_links = g_slist_remove (group1->group_links, link); - group2->group_links = g_slist_remove (group2->group_links, link); - group1->sched->live_links--; - - GST_LOG ("%d live links now", group1->sched->live_links); - - g_free (link); - GST_DEBUG ("removed group link between %p and %p", group1, group2); - if (group1->group_links == NULL) { - /* group1 has no more links with other groups */ - iso_group = group1; - } else if (group2->group_links == NULL) { - /* group2 has no more links with other groups */ - iso_group = group2; - } - if (iso_group) { - GstOptSchedulerChain *chain; - - GST_DEBUG ("group %p has become isolated, moving to new chain", - iso_group); - - chain = create_chain (iso_group->chain->sched); - remove_from_chain (iso_group->chain, iso_group); - add_to_chain (chain, iso_group); - } - res = TRUE; - } - break; - } - } - return res; -} - - -typedef enum -{ - GST_OPT_INVALID, - GST_OPT_GET_TO_CHAIN, - GST_OPT_LOOP_TO_CHAIN, - GST_OPT_GET_TO_LOOP, - GST_OPT_CHAIN_TO_CHAIN, - GST_OPT_CHAIN_TO_LOOP, - GST_OPT_LOOP_TO_LOOP -} -LinkType; - -/* - * Entry points for this scheduler. - */ -static void -gst_opt_scheduler_setup (GstScheduler * sched) -{ -#ifdef USE_COTHREADS - GstOptScheduler *osched = GST_OPT_SCHEDULER (sched); - - /* first create thread context */ - if (osched->context == NULL) { - GST_DEBUG ("initializing cothread context"); - osched->context = do_cothread_context_init (); - } -#endif -} - -static void -gst_opt_scheduler_reset (GstScheduler * sched) -{ -#ifdef USE_COTHREADS - GstOptScheduler *osched = GST_OPT_SCHEDULER (sched); - GSList *chains = osched->chains; - - while (chains) { - GstOptSchedulerChain *chain = (GstOptSchedulerChain *) chains->data; - GSList *groups = chain->groups; - - while (groups) { - GstOptSchedulerGroup *group = (GstOptSchedulerGroup *) groups->data; - - destroy_group_scheduler (group); - groups = groups->next; - } - chains = chains->next; - } - - if (osched->context) { - do_cothread_context_destroy (osched->context); - osched->context = NULL; - } -#endif -} - -static void -gst_opt_scheduler_add_element (GstScheduler * sched, GstElement * element) -{ - GstOptScheduler *osched = GST_OPT_SCHEDULER (sched); - GstOptSchedulerCtx *ctx; - const GList *pads; - - GST_DEBUG_OBJECT (sched, "adding element \"%s\"", GST_OBJECT_NAME (element)); - - /* decoupled elements are not added to the scheduler lists */ - if (GST_ELEMENT_IS_DECOUPLED (element)) - return; - - ctx = g_new0 (GstOptSchedulerCtx, 1); - GST_ELEMENT (element)->sched_private = ctx; - ctx->flags = GST_OPT_SCHEDULER_CTX_DISABLED; - - /* set event handler on all pads here so events work unconnected too; - * in _link, it can be overruled if need be */ - /* FIXME: we should also do this when new pads on the element are created; - but there are no hooks, so we do it again in _link */ - pads = gst_element_get_pad_list (element); - while (pads) { - GstPad *pad = GST_PAD (pads->data); - - pads = g_list_next (pads); - - if (!GST_IS_REAL_PAD (pad)) - continue; - GST_RPAD_EVENTHANDLER (pad) = GST_RPAD_EVENTFUNC (pad); - } - - /* loop based elements *always* end up in their own group. It can eventually - * be merged with another group when a link is made */ - if (element->loopfunc) { - GstOptSchedulerGroup *group; - GstOptSchedulerChain *chain; - - chain = create_chain (osched); - - group = create_group (chain, element, GST_OPT_SCHEDULER_GROUP_LOOP); - group->entry = element; - - GST_LOG ("added element \"%s\" as loop based entry", - GST_ELEMENT_NAME (element)); - } -} - -static void -gst_opt_scheduler_remove_element (GstScheduler * sched, GstElement * element) -{ - GstOptSchedulerGroup *group; - - GST_DEBUG_OBJECT (sched, "removing element \"%s\"", - GST_OBJECT_NAME (element)); - - /* decoupled elements are not added to the scheduler lists and should therefore - * not be removed */ - if (GST_ELEMENT_IS_DECOUPLED (element)) - return; - - /* the element is guaranteed to live in it's own group/chain now */ - get_group (element, &group); - if (group) { - remove_from_group (group, element); - } - - g_free (GST_ELEMENT (element)->sched_private); - GST_ELEMENT (element)->sched_private = NULL; -} - -static gboolean -gst_opt_scheduler_yield (GstScheduler * sched, GstElement * element) -{ -#ifdef USE_COTHREADS - /* yield hands control to the main cothread context if the requesting - * element is the entry point of the group */ - GstOptSchedulerGroup *group; - - get_group (element, &group); - if (group && group->entry == element) - do_cothread_switch (do_cothread_get_main (((GstOptScheduler *) sched)-> - context)); - - return FALSE; -#else - g_warning ("element %s performs a yield, please fix the element", - GST_ELEMENT_NAME (element)); - return TRUE; -#endif -} - -static gboolean -gst_opt_scheduler_interrupt (GstScheduler * sched, GstElement * element) -{ - GST_INFO ("interrupt from \"%s\"", GST_OBJECT_NAME (element)); - -#ifdef USE_COTHREADS - do_cothread_switch (do_cothread_get_main (((GstOptScheduler *) sched)-> - context)); - return FALSE; -#else - { - GstOptScheduler *osched = GST_OPT_SCHEDULER (sched); - - GST_INFO ("scheduler set interrupted state"); - osched->state = GST_OPT_SCHEDULER_STATE_INTERRUPTED; - } - return TRUE; -#endif -} - -static void -gst_opt_scheduler_error (GstScheduler * sched, GstElement * element) -{ - GstOptScheduler *osched = GST_OPT_SCHEDULER (sched); - GstOptSchedulerGroup *group; - - get_group (element, &group); - if (group) - group_error_handler (group); - - osched->state = GST_OPT_SCHEDULER_STATE_ERROR; -} - -/* link pads, merge groups and chains */ -static void -gst_opt_scheduler_pad_link (GstScheduler * sched, GstPad * srcpad, - GstPad * sinkpad) -{ - GstOptScheduler *osched = GST_OPT_SCHEDULER (sched); - LinkType type = GST_OPT_INVALID; - GstElement *src_element, *sink_element; - - GST_INFO ("scheduling link between %s:%s and %s:%s", - GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad)); - - src_element = GST_PAD_PARENT (srcpad); - sink_element = GST_PAD_PARENT (sinkpad); - - /* first we need to figure out what type of link we're dealing - * with */ - if (src_element->loopfunc && sink_element->loopfunc) - type = GST_OPT_LOOP_TO_LOOP; - else { - if (src_element->loopfunc) { - if (GST_RPAD_CHAINFUNC (sinkpad)) - type = GST_OPT_LOOP_TO_CHAIN; - } else if (sink_element->loopfunc) { - if (GST_RPAD_GETFUNC (srcpad)) { - type = GST_OPT_GET_TO_LOOP; - /* this could be tricky, the get based source could - * already be part of a loop based group in another pad, - * we assert on that for now */ - if (GST_ELEMENT_SCHED_CONTEXT (src_element) != NULL && - GST_ELEMENT_SCHED_GROUP (src_element) != NULL) { - GstOptSchedulerGroup *group = GST_ELEMENT_SCHED_GROUP (src_element); - - /* if the loop based element is the entry point we're ok, if it - * isn't then we have multiple loop based elements in this group */ - if (group->entry != sink_element) { - g_error - ("internal error: cannot schedule get to loop in multi-loop based group"); - return; - } - } - } else - type = GST_OPT_CHAIN_TO_LOOP; - } else { - if (GST_RPAD_GETFUNC (srcpad) && GST_RPAD_CHAINFUNC (sinkpad)) { - type = GST_OPT_GET_TO_CHAIN; - /* the get based source could already be part of a loop - * based group in another pad, we assert on that for now */ - if (GST_ELEMENT_SCHED_CONTEXT (src_element) != NULL && - GST_ELEMENT_SCHED_GROUP (src_element) != NULL) { - GstOptSchedulerGroup *group = GST_ELEMENT_SCHED_GROUP (src_element); - - /* if the get based element is the entry point we're ok, if it - * isn't then we have a mixed loop/chain based group */ - if (group->entry != src_element) { - g_error ("internal error: cannot schedule get to chain " - "with mixed loop/chain based group"); - return; - } - } - } else - type = GST_OPT_CHAIN_TO_CHAIN; - } - } - - /* since we can't set event handlers on pad creation after addition, it is - * best we set all of them again to the default before linking */ - GST_RPAD_EVENTHANDLER (srcpad) = GST_RPAD_EVENTFUNC (srcpad); - GST_RPAD_EVENTHANDLER (sinkpad) = GST_RPAD_EVENTFUNC (sinkpad); - - /* for each link type, perform specific actions */ - switch (type) { - case GST_OPT_GET_TO_CHAIN: - { - GstOptSchedulerGroup *group = NULL; - - GST_LOG ("get to chain based link"); - - /* setup get/chain handlers */ - GST_RPAD_GETHANDLER (srcpad) = gst_pad_call_get_function; - GST_RPAD_CHAINHANDLER (sinkpad) = gst_pad_call_chain_function; - - /* 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, src_element, sink_element, - GST_OPT_SCHEDULER_GROUP_GET); - - /* if there is not yet an entry in the group, select the source - * element as the entry point and mark the group as a get based - * group */ - if (!group->entry) { - group->entry = src_element; - group->type = GST_OPT_SCHEDULER_GROUP_GET; - - GST_DEBUG ("setting \"%s\" as entry point of _get-based group %p", - GST_ELEMENT_NAME (src_element), group); - - setup_group_scheduler (osched, group); - } - break; - } - case GST_OPT_LOOP_TO_CHAIN: - case GST_OPT_CHAIN_TO_CHAIN: - GST_LOG ("loop/chain to chain based link"); - - GST_RPAD_CHAINHANDLER (sinkpad) = gst_pad_call_chain_function; - - /* the two elements should be put into the same group, this also means - * that they are in the same chain automatically, in case of a loop-based - * src_element, there will be a group for src_element and sink_element - * will be added to it. In the case a new group is created, we can't know - * the type so we pass UNKNOWN as an arg */ - group_elements (osched, src_element, sink_element, - GST_OPT_SCHEDULER_GROUP_UNKNOWN); - break; - case GST_OPT_GET_TO_LOOP: - GST_LOG ("get to loop based link"); - - GST_RPAD_GETHANDLER (srcpad) = gst_pad_call_get_function; - - /* the two elements should be put into the same group, this also means - * that they are in the same chain automatically, sink_element is - * loop-based so it already has a group where src_element will be added - * to */ - group_elements (osched, src_element, sink_element, - GST_OPT_SCHEDULER_GROUP_LOOP); - break; - case GST_OPT_CHAIN_TO_LOOP: - case GST_OPT_LOOP_TO_LOOP: - { - GstOptSchedulerGroup *group1, *group2; - - GST_LOG ("chain/loop to loop based link"); - - GST_RPAD_CHAINHANDLER (sinkpad) = gst_opt_scheduler_loop_wrapper; - GST_RPAD_GETHANDLER (srcpad) = gst_opt_scheduler_get_wrapper; - /* events on the srcpad have to be intercepted as we might need to - * flush the buffer lists, so override the given eventfunc */ - GST_RPAD_EVENTHANDLER (srcpad) = gst_opt_scheduler_event_wrapper; - - group1 = GST_ELEMENT_SCHED_GROUP (src_element); - group2 = GST_ELEMENT_SCHED_GROUP (sink_element); - - g_assert (group2 != NULL); - - /* group2 is guaranteed to exist as it contains a loop-based element. - * group1 only exists if src_element is linked to some other element */ - if (!group1) { - /* create a new group for src_element as it cannot be merged into another group - * here. we create the group in the same chain as the loop-based element. note - * that creating a new group will not increment the links with other groups */ - GST_DEBUG ("creating new group for element %s", - GST_ELEMENT_NAME (src_element)); - group1 = - create_group (group2->chain, src_element, - GST_OPT_SCHEDULER_GROUP_LOOP); - } else { - /* both elements are already in a group, make sure they are added to - * the same chain */ - merge_chains (group1->chain, group2->chain); - } - /* increment the group link counters */ - group_inc_link (group1, group2); - break; - } - case GST_OPT_INVALID: - g_error ("(internal error) invalid element link, what are you doing?"); - break; - } -} - -static void -group_elements_set_visited (GstOptSchedulerGroup * group, gboolean visited) -{ - GSList *elements; - - for (elements = group->elements; elements; elements = g_slist_next (elements)) { - GstElement *element = GST_ELEMENT (elements->data); - - if (visited) { - GST_ELEMENT_SET_VISITED (element); - } else { - GST_ELEMENT_UNSET_VISITED (element); - } - } -} - -static GList * -element_get_reachables_func (GstElement * element, GstOptSchedulerGroup * group, - GstPad * brokenpad) -{ - GList *result = NULL; - const GList *pads; - - /* if no element or element not in group or been there, return NULL */ - if (element == NULL || !group_has_element (group, element) || - GST_ELEMENT_IS_VISITED (element)) - return NULL; - - GST_ELEMENT_SET_VISITED (element); - - result = g_list_prepend (result, element); - - pads = gst_element_get_pad_list (element); - while (pads) { - GstPad *pad = GST_PAD (pads->data); - GstPad *peer; - - pads = g_list_next (pads); - - /* we only operate on real pads and on the pad that is not broken */ - if (!GST_IS_REAL_PAD (pad) || pad == brokenpad) - continue; - - peer = GST_PAD_PEER (pad); - if (!GST_IS_REAL_PAD (peer) || peer == brokenpad) - continue; - - if (peer) { - GstElement *parent; - GList *res; - - parent = GST_PAD_PARENT (peer); - - res = element_get_reachables_func (parent, group, brokenpad); - - result = g_list_concat (result, res); - } - } - - return result; -} - -static GList * -element_get_reachables (GstElement * element, GstOptSchedulerGroup * group, - GstPad * brokenpad) -{ - GList *result; - - /* reset visited flags */ - group_elements_set_visited (group, FALSE); - - result = element_get_reachables_func (element, group, brokenpad); - - /* and reset visited flags again */ - group_elements_set_visited (group, FALSE); - - return result; -} - -/* - * checks if a target group is still reachable from the group without taking the broken - * group link into account. - */ -static gboolean -group_can_reach_group (GstOptSchedulerGroup * group, - GstOptSchedulerGroup * target) -{ - gboolean reachable = FALSE; - const GSList *links = group->group_links; - - GST_LOG ("checking if group %p can reach %p", group, target); - - /* seems like we found the target element */ - if (group == target) { - GST_LOG ("found way to reach %p", target); - return TRUE; - } - - /* if the group is marked as visited, we don't need to check here */ - if (GST_OPT_SCHEDULER_GROUP_IS_FLAG_SET (group, - GST_OPT_SCHEDULER_GROUP_VISITED)) { - GST_LOG ("already visited %p", group); - return FALSE; - } - - /* mark group as visited */ - GST_OPT_SCHEDULER_GROUP_SET_FLAG (group, GST_OPT_SCHEDULER_GROUP_VISITED); - - while (links && !reachable) { - GstOptSchedulerGroupLink *link = (GstOptSchedulerGroupLink *) links->data; - GstOptSchedulerGroup *other; - - links = g_slist_next (links); - - /* find other group in this link */ - other = OTHER_GROUP_LINK (link, group); - - GST_LOG ("found link from %p to %p, count %d", group, other, link->count); - - /* check if we can reach the target recursively */ - reachable = group_can_reach_group (other, target); - } - /* unset the visited flag, note that this is not optimal as we might be checking - * groups several times when they are reachable with a loop. An alternative would be - * to not clear the group flag at this stage but clear all flags in the chain when - * all groups are checked. */ - GST_OPT_SCHEDULER_GROUP_UNSET_FLAG (group, GST_OPT_SCHEDULER_GROUP_VISITED); - - GST_LOG ("leaving group %p with %s", group, (reachable ? "TRUE" : "FALSE")); - - return reachable; -} - -/* - * Go through all the pads of the given element and decrement the links that - * this group has with the group of the peer element. This function is mainly used - * to update the group connections before we remove the element from the group. - */ -static void -group_dec_links_for_element (GstOptSchedulerGroup * group, GstElement * element) -{ - GList *l; - GstPad *pad; - GstOptSchedulerGroup *peer_group; - - for (l = GST_ELEMENT_PADS (element); l; l = l->next) { - pad = (GstPad *) l->data; - if (GST_IS_REAL_PAD (pad) && GST_PAD_PEER (pad)) { - get_group (GST_PAD_PARENT (GST_PAD_PEER (pad)), &peer_group); - if (peer_group && peer_group != group) - group_dec_link (group, peer_group); - } - } -} - -/* - * Go through all the pads of the given element and increment the links that - * this group has with the group of the peer element. This function is mainly used - * to update the group connections before we add the element to the group. - */ -static void -group_inc_links_for_element (GstOptSchedulerGroup * group, GstElement * element) -{ - GList *l; - GstPad *pad; - GstOptSchedulerGroup *peer_group; - - GST_DEBUG ("group %p, element %s ", group, gst_element_get_name (element)); - - for (l = GST_ELEMENT_PADS (element); l; l = l->next) { - pad = (GstPad *) l->data; - if (GST_IS_REAL_PAD (pad) && GST_PAD_PEER (pad)) { - get_group (GST_PAD_PARENT (GST_PAD_PEER (pad)), &peer_group); - if (peer_group && peer_group != group) - if (GST_PAD_DIRECTION (pad) == GST_PAD_SRC) - group_inc_link (group, peer_group); - else - group_inc_link (peer_group, group); - } - } -} - -static void -debug_element (GstElement * element, GstOptScheduler * osched) -{ - GST_LOG ("element %s", gst_element_get_name (element)); -} - -/* move this group in the chain of the groups it has links with */ -static void -rechain_group (GstOptSchedulerGroup * group) -{ - GstOptSchedulerChain *chain = NULL; - GSList *links; - - GST_LOG ("checking if this group needs rechaining"); - - /* follow all links */ - for (links = group->group_links; links; links = g_slist_next (links)) { - GstOptSchedulerGroupLink *link = (GstOptSchedulerGroupLink *) links->data; - GstOptSchedulerGroup *other; - - other = OTHER_GROUP_LINK (link, group); - GST_LOG ("found link with other group %p with chain %p", other, - other->chain); - - /* first time, take chain */ - if (chain == NULL) { - chain = other->chain; - } - /* second time, chain should be the same */ - else if (other->chain != chain) { - g_warning ("(internal error): chain inconsistency"); - } - } - if (!chain) { - GST_LOG ("no new chain found, not rechaining"); - } else if (chain != group->chain) { - GST_LOG ("need to move group %p to chain %p", group, chain); - /* remove from old chain */ - remove_from_chain (group->chain, group); - /* and move to new chain */ - add_to_chain (chain, group); - } else { - GST_LOG ("group %p is in correct chain %p", group, chain); - } -} - -/* make sure that the group does not contain only single element. - * Only loop-based groups can contain a single element. */ -static GstOptSchedulerGroup * -normalize_group (GstOptSchedulerGroup * group) -{ - gint num; - gboolean have_decoupled = FALSE; - - if (group == NULL) - return NULL; - - num = group->num_elements; - /* decoupled elements are not added to the group but are - * added as an entry */ - if (group->entry && GST_ELEMENT_IS_DECOUPLED (group->entry)) { - num++; - have_decoupled = TRUE; - } - - if (num == 1 && group->type != GST_OPT_SCHEDULER_GROUP_LOOP) { - GST_LOG ("removing last element from group %p", group); - if (have_decoupled) { - group->entry = NULL; - 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); - } else { - group = remove_from_group (group, GST_ELEMENT (group->elements->data)); - } - } - return group; -} - -/* migrate the element and all connected elements to a new group without looking at - * the brokenpad */ -static GstOptSchedulerGroup * -group_migrate_connected (GstOptScheduler * osched, GstElement * element, - GstOptSchedulerGroup * group, GstPad * brokenpad) -{ - GList *connected, *c; - GstOptSchedulerGroup *new_group = NULL, *tst; - GstOptSchedulerChain *chain; - gint len; - - if (GST_ELEMENT_IS_DECOUPLED (element)) { - GST_LOG ("element is decoupled and thus not in the group"); - /* the element is decoupled and is therefore not in the group */ - return NULL; - } - - get_group (element, &tst); - if (tst == NULL) { - GST_LOG ("element has no group, not interesting"); - return NULL; - } - - GST_LOG ("migrate connected elements to new group"); - connected = element_get_reachables (element, group, brokenpad); - GST_LOG ("elements to move to new group:"); - g_list_foreach (connected, (GFunc) debug_element, NULL); - - len = g_list_length (connected); - - if (len == 0) { - g_warning ("(internal error) found lost element %s", - gst_element_get_name (element)); - return NULL; - } else if (len == 1) { - group = remove_from_group (group, GST_ELEMENT (connected->data)); - GST_LOG - ("not migrating to new group as the group would only contain 1 element"); - g_list_free (connected); - GST_LOG ("new group is old group now"); - new_group = group; - } else { - /* we create a new chain to hold the new group */ - chain = create_chain (osched); - - for (c = connected; c; c = g_list_next (c)) { - GstElement *element = GST_ELEMENT (c->data); - - group = remove_from_group (group, element); - if (new_group == NULL) { - new_group = - create_group (chain, element, GST_OPT_SCHEDULER_GROUP_UNKNOWN); - } else { - add_to_group (new_group, element, TRUE); - } - } - g_list_free (connected); - - /* remove last element from the group if any. Make sure not to remove - * the loop based entry point of a group as this always needs one group */ - if (group != NULL) { - group = normalize_group (group); - } - } - - if (new_group != NULL) { - new_group = normalize_group (new_group); - if (new_group == NULL) - return NULL; - /* at this point the new group lives in its own chain but might - * have to be merged with another chain, this happens when the new - * group has a link with another group in another chain */ - rechain_group (new_group); - } - - - return new_group; -} - -static void -gst_opt_scheduler_pad_unlink (GstScheduler * sched, - GstPad * srcpad, GstPad * sinkpad) -{ - GstOptScheduler *osched = GST_OPT_SCHEDULER (sched); - GstElement *src_element, *sink_element; - GstOptSchedulerGroup *group1, *group2; - - GST_INFO ("unscheduling link between %s:%s and %s:%s", - GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad)); - - src_element = GST_PAD_PARENT (srcpad); - sink_element = GST_PAD_PARENT (sinkpad); - - get_group (src_element, &group1); - get_group (sink_element, &group2); - - /* for decoupled elements (that are never put into a group) we use the - * group of the peer element for the remainder of the algorithm */ - if (GST_ELEMENT_IS_DECOUPLED (src_element)) { - group1 = group2; - } - if (GST_ELEMENT_IS_DECOUPLED (sink_element)) { - group2 = group1; - } - - /* if one the elements has no group (anymore) we don't really care - * about the link */ - if (!group1 || !group2) { - GST_LOG - ("one (or both) of the elements is not in a group, not interesting"); - return; - } - - /* easy part, groups are different */ - if (group1 != group2) { - gboolean zero; - - GST_LOG ("elements are in different groups"); - - /* we can remove the links between the groups now */ - zero = group_dec_link (group1, group2); - - /* if the groups are not directly connected anymore, we have to perform a - * recursive check to see if they are really unlinked */ - if (zero) { - gboolean still_link; - GstOptSchedulerChain *chain; - - /* see if group1 and group2 are still connected in any indirect way */ - still_link = group_can_reach_group (group1, group2); - - GST_DEBUG ("group %p %s reach group %p", group1, - (still_link ? "can" : "can't"), group2); - if (!still_link) { - /* groups are really disconnected, migrate one group to a new chain */ - chain = create_chain (osched); - chain_recursively_migrate_group (chain, group1); - - GST_DEBUG ("migrated group %p to new chain %p", group1, chain); - } - } else { - GST_DEBUG ("group %p still has direct link with group %p", group1, - group2); - } - } - /* hard part, groups are equal */ - else { - GstOptSchedulerGroup *group; - - /* since group1 == group2, it doesn't matter which group we take */ - group = group1; - - GST_LOG ("elements are in the same group %p", group); - - if (group->entry == NULL) { - /* it doesn't really matter, we just have to make sure that both - * elements end up in another group if they are not connected */ - GST_LOG ("group %p has no entry, moving source element to new group", - group); - group_migrate_connected (osched, src_element, group, srcpad); - } else { - GList *reachables; - - GST_LOG ("group %p has entry %p", group, group->entry); - - /* get of a list of all elements that are still managed by the old - * entry element */ - reachables = element_get_reachables (group->entry, group, srcpad); - GST_LOG ("elements still reachable from the entry:"); - g_list_foreach (reachables, (GFunc) debug_element, sched); - - /* if the source is reachable from the entry, we can leave it in the group */ - if (g_list_find (reachables, src_element)) { - GST_LOG - ("source element still reachable from the entry, leaving in group"); - } else { - GST_LOG - ("source element not reachable from the entry, moving to new group"); - group_migrate_connected (osched, src_element, group, srcpad); - } - - /* if the sink is reachable from the entry, we can leave it in the group */ - if (g_list_find (reachables, sink_element)) { - GST_LOG - ("sink element still reachable from the entry, leaving in group"); - } else { - GST_LOG - ("sink element not reachable from the entry, moving to new group"); - group_migrate_connected (osched, sink_element, group, srcpad); - } - g_list_free (reachables); - } - /* at this point the group can be freed and gone, so don't touch */ - } -} - -/* a scheduler iteration is done by looping and scheduling the active chains */ -static GstSchedulerState -gst_opt_scheduler_iterate (GstScheduler * sched) -{ - GstSchedulerState state = GST_SCHEDULER_STATE_STOPPED; - GstOptScheduler *osched = GST_OPT_SCHEDULER (sched); - gint iterations = osched->iterations; - - osched->state = GST_OPT_SCHEDULER_STATE_RUNNING; - - GST_DEBUG_OBJECT (sched, "iterating"); - - while (iterations) { - gboolean scheduled = FALSE; - GSList *chains; - - /* we have to schedule each of the scheduler chains now */ - chains = osched->chains; - while (chains) { - GstOptSchedulerChain *chain = (GstOptSchedulerChain *) chains->data; - - 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; - GST_LOG ("scheduled chain %p", chain); - } else { - GST_LOG ("not scheduling disabled chain %p", chain); - } - - /* don't schedule any more chains when in error */ - if (osched->state == GST_OPT_SCHEDULER_STATE_ERROR) { - GST_ERROR_OBJECT (sched, "in error state"); - /* unref the chain here as we move out of the while loop */ - unref_chain (chain); - break; - } else if (osched->state == GST_OPT_SCHEDULER_STATE_INTERRUPTED) { - GST_DEBUG_OBJECT (osched, "got interrupted, continue with next chain"); - osched->state = GST_OPT_SCHEDULER_STATE_RUNNING; - } - - chains = g_slist_next (chains); - unref_chain (chain); - } - - /* at this point it's possible that the scheduler state is - * in error, we then return an error */ - if (osched->state == GST_OPT_SCHEDULER_STATE_ERROR) { - state = GST_SCHEDULER_STATE_ERROR; - break; - } else { - /* if chains were scheduled, return our current state */ - if (scheduled) - state = GST_SCHEDULER_STATE (sched); - /* if no chains were scheduled, we say we are stopped */ - else { - state = GST_SCHEDULER_STATE_STOPPED; - break; - } - } - if (iterations > 0) - iterations--; - } - - return state; -} - - -static void -gst_opt_scheduler_show (GstScheduler * sched) -{ - GstOptScheduler *osched = GST_OPT_SCHEDULER (sched); - GSList *chains; - - g_print ("iterations: %d\n", osched->iterations); - g_print ("max recursion: %d\n", osched->max_recursion); - - chains = osched->chains; - while (chains) { - GstOptSchedulerChain *chain = (GstOptSchedulerChain *) chains->data; - GSList *groups = chain->groups; - - chains = g_slist_next (chains); - - g_print ("+- chain %p: refcount %d, %d groups, %d enabled, flags %d\n", - chain, chain->refcount, chain->num_groups, chain->num_enabled, - chain->flags); - - while (groups) { - GstOptSchedulerGroup *group = (GstOptSchedulerGroup *) groups->data; - GSList *elements = group->elements; - GSList *group_links = group->group_links; - - groups = g_slist_next (groups); - - g_print - (" +- group %p: refcount %d, %d elements, %d enabled, flags %d, entry %s, %s\n", - group, group->refcount, group->num_elements, group->num_enabled, - group->flags, - (group->entry ? GST_ELEMENT_NAME (group->entry) : "(none)"), - (group->type == - GST_OPT_SCHEDULER_GROUP_GET ? "get-based" : "loop-based")); - - while (elements) { - GstElement *element = (GstElement *) elements->data; - - elements = g_slist_next (elements); - - g_print (" +- element %s\n", GST_ELEMENT_NAME (element)); - } - while (group_links) { - GstOptSchedulerGroupLink *link = - (GstOptSchedulerGroupLink *) group_links->data; - - group_links = g_slist_next (group_links); - - g_print ("group link %p between %p and %p, count %d\n", - link, link->src, link->sink, link->count); - } - } - } -} - -static void -gst_opt_scheduler_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - GstOptScheduler *osched; - - g_return_if_fail (GST_IS_OPT_SCHEDULER (object)); - - osched = GST_OPT_SCHEDULER (object); - - switch (prop_id) { - case ARG_ITERATIONS: - g_value_set_int (value, osched->iterations); - break; - case ARG_MAX_RECURSION: - g_value_set_int (value, osched->max_recursion); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_opt_scheduler_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - GstOptScheduler *osched; - - g_return_if_fail (GST_IS_OPT_SCHEDULER (object)); - - osched = GST_OPT_SCHEDULER (object); - - switch (prop_id) { - case ARG_ITERATIONS: - osched->iterations = g_value_get_int (value); - break; - case ARG_MAX_RECURSION: - osched->max_recursion = g_value_get_int (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} diff --git a/gst/schedulers/gthread-cothreads.h b/gst/schedulers/gthread-cothreads.h deleted file mode 100644 index 8051cfa509..0000000000 --- a/gst/schedulers/gthread-cothreads.h +++ /dev/null @@ -1,221 +0,0 @@ -/* GStreamer - * Copyright (C) 2003 Benjamin Otte - * - * gthread-cothreads.c: cothreads implemented via GThread for compatibility - * They're probably slooooooow - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __GTHREAD_COTHREADS_H__ -#define __GTHREAD_COTHREADS_H__ - -#include -#include - -/* the name of this cothreads */ -#define COTHREADS_TYPE gthread -#define COTHREADS_NAME "gthread" -#define COTHREADS_NAME_CAPITAL "GThread" - -/* - * Theory of operation: - * Instead of using cothreads, GThreads and 1 mutex are used. - * Every thread may only run if it holds the mutex. Otherwise it holds its own - * cond which has to be signaled to wakeit up. - */ - -/* define "cothread", "cothread_context" and "cothread_func" */ -typedef int (*cothread_func) (int, char **); - -typedef struct _cothread cothread; -typedef struct _cothread_context cothread_context; - -struct _cothread_context { - GSList * cothreads; /* contains all threads but main */ - cothread * main; - cothread * current; - GMutex * mutex; - GstThread * gst_thread; /* the GstThread we're running from */ -}; - -struct _cothread { - GThread * thread; - GCond * cond; - cothread_func run; - int argc; - char ** argv; - cothread * creator; - gboolean die; - cothread_context * context; -}; - -#ifndef GTHREAD_COTHREADS_NO_DEFINITIONS - -/* define functions - * Functions starting with "do_" are used by the scheduler. - */ -static void do_cothreads_init (void *unused); -static cothread_context *do_cothread_context_init (void); -static void do_cothread_context_destroy (cothread_context *context); -static cothread * cothread_create (cothread_context *context, - cothread_func func, - int argc, - char **argv); -#define do_cothread_create(new_cothread, context, func, argc, argv) \ - G_STMT_START{ \ - new_cothread = cothread_create ((context), (func), argc, (char**) (argv)); \ - }G_STMT_END -static void do_cothread_switch (cothread *to); -static void do_cothread_destroy (cothread *thread); -#define do_cothread_get_current(context) ((context)->current) -#define do_cothread_get_main(context) ((context)->main) - -static void -do_cothreads_init (void *unused) -{ - if (!g_thread_supported ()) g_thread_init (NULL); -} -static cothread_context * -do_cothread_context_init (void) -{ - cothread_context *ret = g_new0 (cothread_context, 1); - - ret->main = g_new0 (cothread, 1); - ret->main->thread = g_thread_self (); - ret->main->cond = g_cond_new (); - ret->main->die = FALSE; - ret->main->context = ret; - ret->mutex = g_mutex_new (); - ret->cothreads = NULL; - ret->current = ret->main; - ret->gst_thread = gst_thread_get_current(); - g_mutex_lock (ret->mutex); - - return ret; -} -static void -do_cothread_context_destroy (cothread_context *context) -{ - g_assert (g_thread_self() == context->main->thread); - - while (context->cothreads) { - do_cothread_destroy ((cothread *) context->cothreads->data); - } - g_mutex_unlock (context->mutex); - g_mutex_free (context->mutex); - g_cond_free (context->main->cond); - g_free (context->main); - - g_free (context); -} -static void -die (cothread *to_die) { - g_cond_free (to_die->cond); - to_die->context->cothreads = g_slist_remove (to_die->context->cothreads, to_die); - g_free (to_die); - g_thread_exit (to_die); - /* don't unlock the mutex here, the thread waiting for us to die is gonna take it */ -} -static gpointer -run_new_thread (gpointer data) -{ - cothread *self = (cothread *) data; - - g_mutex_lock (self->context->mutex); - g_private_set (gst_thread_current, self->context->gst_thread); - g_cond_signal (self->creator->cond); - g_cond_wait (self->cond, self->context->mutex); - if (self->die) - die (self); - while (TRUE) { - self->run (self->argc, self->argv); - /* compatibility */ - do_cothread_switch (do_cothread_get_main (self->context)); - } - g_assert_not_reached (); - return NULL; -} -static cothread * -cothread_create (cothread_context *context, cothread_func func, int argc, char **argv) -{ - cothread *ret; - - if ((ret = g_new (cothread, 1)) == NULL) { - goto out1; - } - ret->cond = g_cond_new (); - ret->run = func; - ret->argc = argc; - ret->argv = argv; - ret->creator = do_cothread_get_current (context); - ret->die = FALSE; - ret->context = context; - context->cothreads = g_slist_prepend (context->cothreads, ret); - ret->thread = g_thread_create (run_new_thread, ret, TRUE, NULL); - if (ret->thread == NULL) goto out2; - g_cond_wait (do_cothread_get_current (context)->cond, context->mutex); - return ret; - -out2: - context->cothreads = g_slist_remove (context->cothreads, ret); - g_free (ret); -out1: - return NULL; -} - -static void do_cothread_switch (cothread *to) -{ - cothread *self = do_cothread_get_current(to->context); - - if (self != to) { - self->context->current = to; - g_cond_signal (to->cond); - g_cond_wait (self->cond, self->context->mutex); - if (self->die) - die (self); - } -} - -#define do_cothread_setfunc(thread,context,_func,_argc,_argv) G_STMT_START {\ - ((cothread *)(thread))->run = (_func); \ - ((cothread *)(thread))->argc = _argc; \ - ((cothread *)(thread))->argv = _argv; \ -}G_STMT_END - -static void -do_cothread_destroy (cothread *thread) -{ - GThread *join; - cothread_context *context; - g_return_if_fail (thread != thread->context->main); - g_return_if_fail (thread != thread->context->current); - - thread->die = TRUE; - join = thread->thread; - context = thread->context; - g_cond_signal (thread->cond); - g_mutex_unlock (thread->context->mutex); - g_thread_join (join); - /* the mutex was locked by the thread that we joined, no need to lock again */ -} - -#define do_cothread_get_current(context) ((context)->current) -#define do_cothread_get_main(context) ((context)->main) - -#endif /* GTHREAD_COTHREADS_NO_DEFINITIONS */ - -#endif /* __GTHREAD_COTHREADS_H__ */ diff --git a/gst/schedulers/threadscheduler.c b/gst/schedulers/threadscheduler.c index ef5b113071..75f68dac52 100644 --- a/gst/schedulers/threadscheduler.c +++ b/gst/schedulers/threadscheduler.c @@ -245,13 +245,8 @@ gst_thread_scheduler_get_type (void) static void gst_thread_scheduler_setup (GstScheduler * sched); static void gst_thread_scheduler_reset (GstScheduler * sched); -static void gst_thread_scheduler_add_element (GstScheduler * sched, - GstElement * element); -static void gst_thread_scheduler_remove_element (GstScheduler * sched, - GstElement * element); static GstTask *gst_thread_scheduler_create_task (GstScheduler * sched, GstTaskFunction func, gpointer data); -static void gst_thread_scheduler_show (GstScheduler * scheduler); static void gst_thread_scheduler_class_init (gpointer klass, gpointer class_data) @@ -260,11 +255,7 @@ gst_thread_scheduler_class_init (gpointer klass, gpointer class_data) scheduler->setup = gst_thread_scheduler_setup; scheduler->reset = gst_thread_scheduler_reset; - scheduler->add_element = gst_thread_scheduler_add_element; - scheduler->remove_element = gst_thread_scheduler_remove_element; scheduler->create_task = gst_thread_scheduler_create_task; - scheduler->clock_wait = NULL; - scheduler->show = gst_thread_scheduler_show; } static void @@ -335,31 +326,6 @@ gst_thread_scheduler_reset (GstScheduler * sched) { } -static void -gst_thread_scheduler_add_element (GstScheduler * scheduler, - GstElement * element) -{ - g_print ("add element\n"); -} - -static void -gst_thread_scheduler_remove_element (GstScheduler * scheduler, - GstElement * element) -{ - GstThreadSchedulerTask *task;; - - task = ELEMENT_PRIVATE (element); - if (task) { - g_object_unref (G_OBJECT (task)); - ELEMENT_PRIVATE (element) = NULL;; - } -} - -static void -gst_thread_scheduler_show (GstScheduler * scheduler) -{ -} - static gboolean plugin_init (GstPlugin * plugin) { diff --git a/plugins/elements/gstfakesrc.c b/plugins/elements/gstfakesrc.c index f5c1dce12c..15fbcf855d 100644 --- a/plugins/elements/gstfakesrc.c +++ b/plugins/elements/gstfakesrc.c @@ -861,48 +861,6 @@ done: GST_STREAM_UNLOCK (pad); } -#if 0 -/** - * gst_fakesrc_loop: - * @element: the faksesrc to loop - * - * generate an empty buffer and push it to the next element. - */ -static gboolean -gst_fakesrc_loop (GstPad * pad) -{ - GstFakeSrc *src; - const GList *pads; - GstTask *task; - - src = GST_FAKESRC (GST_PAD_PARENT (pad)); - task = src->task; - - pads = GST_ELEMENT (src)->pads; - - while (pads) { - GstPad *pad = GST_PAD (pads->data); - GstBuffer *buffer; - GstFlowReturn ret; - - ret = gst_fakesrc_get (pad, &buffer); - if (ret != GST_FLOW_OK) { - return FALSE; - } - ret = gst_pad_push (pad, buffer); - if (ret != GST_FLOW_OK) { - return FALSE; - } - - if (src->eos) { - return FALSE; - } - pads = g_list_next (pads); - } - return TRUE; -} -#endif - static gboolean gst_fakesrc_activate (GstPad * pad, GstActivateMode mode) { diff --git a/plugins/elements/gstidentity.c b/plugins/elements/gstidentity.c index 12948950a8..ae135b84ae 100644 --- a/plugins/elements/gstidentity.c +++ b/plugins/elements/gstidentity.c @@ -259,6 +259,7 @@ gst_identity_chain (GstPad * pad, GstBuffer * buffer) { GstBuffer *buf = GST_BUFFER (buffer); GstIdentity *identity; + GstFlowReturn result = GST_FLOW_OK; guint i; g_return_val_if_fail (pad != NULL, GST_FLOW_ERROR); @@ -365,12 +366,12 @@ gst_identity_chain (GstPad * pad, GstBuffer * buffer) } identity->bytes_handled += GST_BUFFER_SIZE (buf); - gst_pad_push (identity->srcpad, buf); + result = gst_pad_push (identity->srcpad, buf); if (identity->sleep_time) g_usleep (identity->sleep_time); } - return GST_FLOW_OK; + return result; } #if 0 diff --git a/tests/old/testsuite/bins/interface.c b/tests/old/testsuite/bins/interface.c index 028b8f0b81..ca15a95240 100644 --- a/tests/old/testsuite/bins/interface.c +++ b/tests/old/testsuite/bins/interface.c @@ -19,14 +19,15 @@ #include -/* tests if gst_bin_get_(all_)by_interface works */ +/* tests if gst_bin_iterate_(all_)by_interface works */ gint main (gint argc, gchar * argv[]) { GstBin *bin, *bin2; - GList *list; + GstIterator *it; GstElement *filesrc; + gpointer item; gst_init (&argc, &argv); @@ -39,20 +40,22 @@ main (gint argc, gchar * argv[]) gst_bin_add (bin, filesrc); g_assert (gst_bin_get_by_interface (bin, GST_TYPE_URI_HANDLER) == filesrc); - list = gst_bin_get_all_by_interface (bin, GST_TYPE_URI_HANDLER); - g_assert (g_list_length (list) == 1); - g_assert (list->data == (gpointer) filesrc); - g_list_free (list); + it = gst_bin_iterate_all_by_interface (bin, GST_TYPE_URI_HANDLER); + g_assert (gst_iterator_next (it, &item) == GST_ITERATOR_OK); + g_assert (item == (gpointer) filesrc); + g_assert (gst_iterator_next (it, &item) == GST_ITERATOR_DONE); + gst_iterator_free (it); gst_bin_add_many (bin, gst_element_factory_make ("identity", NULL), gst_element_factory_make ("identity", NULL), gst_element_factory_make ("identity", NULL), NULL); g_assert (gst_bin_get_by_interface (bin, GST_TYPE_URI_HANDLER) == filesrc); - list = gst_bin_get_all_by_interface (bin, GST_TYPE_URI_HANDLER); - g_assert (g_list_length (list) == 1); - g_assert (list->data == (gpointer) filesrc); - g_list_free (list); + it = gst_bin_iterate_all_by_interface (bin, GST_TYPE_URI_HANDLER); + g_assert (gst_iterator_next (it, &item) == GST_ITERATOR_OK); + g_assert (item == (gpointer) filesrc); + g_assert (gst_iterator_next (it, &item) == GST_ITERATOR_DONE); + gst_iterator_free (it); bin2 = bin; bin = GST_BIN (gst_bin_new (NULL)); @@ -62,16 +65,20 @@ main (gint argc, gchar * argv[]) gst_element_factory_make ("identity", NULL), GST_ELEMENT (bin2), gst_element_factory_make ("identity", NULL), NULL); g_assert (gst_bin_get_by_interface (bin, GST_TYPE_URI_HANDLER) == filesrc); - list = gst_bin_get_all_by_interface (bin, GST_TYPE_URI_HANDLER); - g_assert (g_list_length (list) == 1); - g_assert (list->data == (gpointer) filesrc); - g_list_free (list); + it = gst_bin_iterate_all_by_interface (bin, GST_TYPE_URI_HANDLER); + g_assert (gst_iterator_next (it, &item) == GST_ITERATOR_OK); + g_assert (item == (gpointer) filesrc); + g_assert (gst_iterator_next (it, &item) == GST_ITERATOR_DONE); + gst_iterator_free (it); gst_bin_add (bin, gst_element_factory_make ("filesrc", NULL)); gst_bin_add (bin2, gst_element_factory_make ("filesrc", NULL)); - list = gst_bin_get_all_by_interface (bin, GST_TYPE_URI_HANDLER); - g_assert (g_list_length (list) == 3); - g_list_free (list); + it = gst_bin_iterate_all_by_interface (bin, GST_TYPE_URI_HANDLER); + g_assert (gst_iterator_next (it, &item) == GST_ITERATOR_OK); + g_assert (gst_iterator_next (it, &item) == GST_ITERATOR_OK); + g_assert (gst_iterator_next (it, &item) == GST_ITERATOR_OK); + g_assert (gst_iterator_next (it, &item) == GST_ITERATOR_DONE); + gst_iterator_free (it); g_object_unref (bin); return 0; diff --git a/tests/old/testsuite/dlopen/loadgst.c b/tests/old/testsuite/dlopen/loadgst.c index 9bfbd0a12c..59d6e72c61 100644 --- a/tests/old/testsuite/dlopen/loadgst.c +++ b/tests/old/testsuite/dlopen/loadgst.c @@ -7,22 +7,20 @@ do_test (void) { GstElement *pipeline; int i; - gboolean ret; gst_init (NULL, NULL); pipeline = gst_parse_launch ("fakesrc ! fakesink", NULL); g_assert (pipeline != NULL); - gst_element_set_state (pipeline, GST_STATE_PLAYING); for (i = 0; i < 100; i++) { - ret = gst_bin_iterate (GST_BIN (pipeline)); - g_assert (ret); + g_usleep (1000); g_print ("%s", (i & 1) ? "+" : "-"); } g_print ("\n"); + gst_element_set_state (pipeline, GST_STATE_NULL); gst_object_unref (GST_OBJECT (pipeline)); } diff --git a/tests/old/testsuite/states/.gitignore b/tests/old/testsuite/states/.gitignore index ae643e03fd..debc336b83 100644 --- a/tests/old/testsuite/states/.gitignore +++ b/tests/old/testsuite/states/.gitignore @@ -13,3 +13,4 @@ test4 bin locked parent +stress diff --git a/tests/old/testsuite/states/Makefile.am b/tests/old/testsuite/states/Makefile.am index 5aa2c0d078..ccd5f2b5f9 100644 --- a/tests/old/testsuite/states/Makefile.am +++ b/tests/old/testsuite/states/Makefile.am @@ -1,5 +1,5 @@ include ../Rules -tests_pass = test1 test2 test3 test4 locked parent +tests_pass = test1 test2 test3 test4 locked parent stress tests_fail = tests_ignore = diff --git a/tests/old/testsuite/states/stress.c b/tests/old/testsuite/states/stress.c new file mode 100644 index 0000000000..b959e912c5 --- /dev/null +++ b/tests/old/testsuite/states/stress.c @@ -0,0 +1,97 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "unistd.h" + +#include + +static GRand *myrand; +static GMainLoop *loop; + +static gboolean +message_received (GstBus * bus, GstMessage * message, GstPipeline * pipeline) +{ + g_print ("message %p\n", message); + + if (message->type == GST_MESSAGE_EOS) { + g_print ("EOS!!\n"); + if (g_main_loop_is_running (loop)) + g_main_loop_quit (loop); + } + gst_message_unref (message); + + return TRUE; +} + +static gboolean +state_change (GstElement * element) +{ + g_usleep (g_rand_int_range (myrand, 100, 600)); + g_print ("pause..\n"); + gst_element_set_state (element, GST_STATE_PAUSED); + gst_element_get_state (element, NULL, NULL, NULL); + g_print ("done\n"); + g_usleep (g_rand_int_range (myrand, 50, 100)); + g_print ("play..\n"); + gst_element_set_state (element, GST_STATE_PLAYING); + gst_element_get_state (element, NULL, NULL, NULL); + g_print ("done\n"); + + return TRUE; +} + +gint +main (gint argc, gchar * argv[]) +{ + GstElement *pipeline; + GstElement *fakesrc1, *fakesink1; + GstBus *bus; + + gst_init (&argc, &argv); + + myrand = g_rand_new (); + + pipeline = gst_pipeline_new ("pipeline"); + + loop = g_main_loop_new (NULL, FALSE); + bus = gst_element_get_bus (pipeline); + gst_bus_add_watch (bus, (GstBusHandler) message_received, pipeline); + gst_object_unref (GST_OBJECT (bus)); + + fakesrc1 = gst_element_factory_make ("fakesrc", "fakesrc1"); + g_object_set (G_OBJECT (fakesrc1), "num_buffers", 1000, NULL); + fakesink1 = gst_element_factory_make ("fakesink", "fakesink1"); + + gst_bin_add (GST_BIN (pipeline), fakesrc1); + gst_bin_add (GST_BIN (pipeline), fakesink1); + gst_pad_link (gst_element_get_pad (fakesrc1, "src"), + gst_element_get_pad (fakesink1, "sink")); + + g_signal_connect (G_OBJECT (pipeline), "deep_notify", + G_CALLBACK (gst_object_default_deep_notify), NULL); + + g_idle_add ((GSourceFunc) state_change, pipeline); + g_main_loop_run (loop); + + gst_element_set_state (pipeline, GST_STATE_NULL); + + gst_object_unref (GST_OBJECT (pipeline)); + + return 0; +} diff --git a/testsuite/bins/interface.c b/testsuite/bins/interface.c index 028b8f0b81..ca15a95240 100644 --- a/testsuite/bins/interface.c +++ b/testsuite/bins/interface.c @@ -19,14 +19,15 @@ #include -/* tests if gst_bin_get_(all_)by_interface works */ +/* tests if gst_bin_iterate_(all_)by_interface works */ gint main (gint argc, gchar * argv[]) { GstBin *bin, *bin2; - GList *list; + GstIterator *it; GstElement *filesrc; + gpointer item; gst_init (&argc, &argv); @@ -39,20 +40,22 @@ main (gint argc, gchar * argv[]) gst_bin_add (bin, filesrc); g_assert (gst_bin_get_by_interface (bin, GST_TYPE_URI_HANDLER) == filesrc); - list = gst_bin_get_all_by_interface (bin, GST_TYPE_URI_HANDLER); - g_assert (g_list_length (list) == 1); - g_assert (list->data == (gpointer) filesrc); - g_list_free (list); + it = gst_bin_iterate_all_by_interface (bin, GST_TYPE_URI_HANDLER); + g_assert (gst_iterator_next (it, &item) == GST_ITERATOR_OK); + g_assert (item == (gpointer) filesrc); + g_assert (gst_iterator_next (it, &item) == GST_ITERATOR_DONE); + gst_iterator_free (it); gst_bin_add_many (bin, gst_element_factory_make ("identity", NULL), gst_element_factory_make ("identity", NULL), gst_element_factory_make ("identity", NULL), NULL); g_assert (gst_bin_get_by_interface (bin, GST_TYPE_URI_HANDLER) == filesrc); - list = gst_bin_get_all_by_interface (bin, GST_TYPE_URI_HANDLER); - g_assert (g_list_length (list) == 1); - g_assert (list->data == (gpointer) filesrc); - g_list_free (list); + it = gst_bin_iterate_all_by_interface (bin, GST_TYPE_URI_HANDLER); + g_assert (gst_iterator_next (it, &item) == GST_ITERATOR_OK); + g_assert (item == (gpointer) filesrc); + g_assert (gst_iterator_next (it, &item) == GST_ITERATOR_DONE); + gst_iterator_free (it); bin2 = bin; bin = GST_BIN (gst_bin_new (NULL)); @@ -62,16 +65,20 @@ main (gint argc, gchar * argv[]) gst_element_factory_make ("identity", NULL), GST_ELEMENT (bin2), gst_element_factory_make ("identity", NULL), NULL); g_assert (gst_bin_get_by_interface (bin, GST_TYPE_URI_HANDLER) == filesrc); - list = gst_bin_get_all_by_interface (bin, GST_TYPE_URI_HANDLER); - g_assert (g_list_length (list) == 1); - g_assert (list->data == (gpointer) filesrc); - g_list_free (list); + it = gst_bin_iterate_all_by_interface (bin, GST_TYPE_URI_HANDLER); + g_assert (gst_iterator_next (it, &item) == GST_ITERATOR_OK); + g_assert (item == (gpointer) filesrc); + g_assert (gst_iterator_next (it, &item) == GST_ITERATOR_DONE); + gst_iterator_free (it); gst_bin_add (bin, gst_element_factory_make ("filesrc", NULL)); gst_bin_add (bin2, gst_element_factory_make ("filesrc", NULL)); - list = gst_bin_get_all_by_interface (bin, GST_TYPE_URI_HANDLER); - g_assert (g_list_length (list) == 3); - g_list_free (list); + it = gst_bin_iterate_all_by_interface (bin, GST_TYPE_URI_HANDLER); + g_assert (gst_iterator_next (it, &item) == GST_ITERATOR_OK); + g_assert (gst_iterator_next (it, &item) == GST_ITERATOR_OK); + g_assert (gst_iterator_next (it, &item) == GST_ITERATOR_OK); + g_assert (gst_iterator_next (it, &item) == GST_ITERATOR_DONE); + gst_iterator_free (it); g_object_unref (bin); return 0; diff --git a/testsuite/dlopen/loadgst.c b/testsuite/dlopen/loadgst.c index 9bfbd0a12c..59d6e72c61 100644 --- a/testsuite/dlopen/loadgst.c +++ b/testsuite/dlopen/loadgst.c @@ -7,22 +7,20 @@ do_test (void) { GstElement *pipeline; int i; - gboolean ret; gst_init (NULL, NULL); pipeline = gst_parse_launch ("fakesrc ! fakesink", NULL); g_assert (pipeline != NULL); - gst_element_set_state (pipeline, GST_STATE_PLAYING); for (i = 0; i < 100; i++) { - ret = gst_bin_iterate (GST_BIN (pipeline)); - g_assert (ret); + g_usleep (1000); g_print ("%s", (i & 1) ? "+" : "-"); } g_print ("\n"); + gst_element_set_state (pipeline, GST_STATE_NULL); gst_object_unref (GST_OBJECT (pipeline)); } diff --git a/testsuite/states/.gitignore b/testsuite/states/.gitignore index ae643e03fd..debc336b83 100644 --- a/testsuite/states/.gitignore +++ b/testsuite/states/.gitignore @@ -13,3 +13,4 @@ test4 bin locked parent +stress diff --git a/testsuite/states/Makefile.am b/testsuite/states/Makefile.am index 5aa2c0d078..ccd5f2b5f9 100644 --- a/testsuite/states/Makefile.am +++ b/testsuite/states/Makefile.am @@ -1,5 +1,5 @@ include ../Rules -tests_pass = test1 test2 test3 test4 locked parent +tests_pass = test1 test2 test3 test4 locked parent stress tests_fail = tests_ignore = diff --git a/testsuite/states/stress.c b/testsuite/states/stress.c new file mode 100644 index 0000000000..b959e912c5 --- /dev/null +++ b/testsuite/states/stress.c @@ -0,0 +1,97 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "unistd.h" + +#include + +static GRand *myrand; +static GMainLoop *loop; + +static gboolean +message_received (GstBus * bus, GstMessage * message, GstPipeline * pipeline) +{ + g_print ("message %p\n", message); + + if (message->type == GST_MESSAGE_EOS) { + g_print ("EOS!!\n"); + if (g_main_loop_is_running (loop)) + g_main_loop_quit (loop); + } + gst_message_unref (message); + + return TRUE; +} + +static gboolean +state_change (GstElement * element) +{ + g_usleep (g_rand_int_range (myrand, 100, 600)); + g_print ("pause..\n"); + gst_element_set_state (element, GST_STATE_PAUSED); + gst_element_get_state (element, NULL, NULL, NULL); + g_print ("done\n"); + g_usleep (g_rand_int_range (myrand, 50, 100)); + g_print ("play..\n"); + gst_element_set_state (element, GST_STATE_PLAYING); + gst_element_get_state (element, NULL, NULL, NULL); + g_print ("done\n"); + + return TRUE; +} + +gint +main (gint argc, gchar * argv[]) +{ + GstElement *pipeline; + GstElement *fakesrc1, *fakesink1; + GstBus *bus; + + gst_init (&argc, &argv); + + myrand = g_rand_new (); + + pipeline = gst_pipeline_new ("pipeline"); + + loop = g_main_loop_new (NULL, FALSE); + bus = gst_element_get_bus (pipeline); + gst_bus_add_watch (bus, (GstBusHandler) message_received, pipeline); + gst_object_unref (GST_OBJECT (bus)); + + fakesrc1 = gst_element_factory_make ("fakesrc", "fakesrc1"); + g_object_set (G_OBJECT (fakesrc1), "num_buffers", 1000, NULL); + fakesink1 = gst_element_factory_make ("fakesink", "fakesink1"); + + gst_bin_add (GST_BIN (pipeline), fakesrc1); + gst_bin_add (GST_BIN (pipeline), fakesink1); + gst_pad_link (gst_element_get_pad (fakesrc1, "src"), + gst_element_get_pad (fakesink1, "sink")); + + g_signal_connect (G_OBJECT (pipeline), "deep_notify", + G_CALLBACK (gst_object_default_deep_notify), NULL); + + g_idle_add ((GSourceFunc) state_change, pipeline); + g_main_loop_run (loop); + + gst_element_set_state (pipeline, GST_STATE_NULL); + + gst_object_unref (GST_OBJECT (pipeline)); + + return 0; +}