diff --git a/ChangeLog b/ChangeLog index 842322802d..0ab61d5960 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,193 @@ +2005-03-21 Wim Taymans + + * check/Makefile.am: + * gst/Makefile.am: + * gst/elements/Makefile.am: + * gst/elements/gstelements.c: + * gst/elements/gstfakesink.c: (gst_fakesink_init), + (gst_fakesink_event), (gst_fakesink_chain): + * gst/elements/gstfakesrc.c: (gst_fakesrc_class_init), + (gst_fakesrc_init), (gst_fakesrc_get_event_mask), + (gst_fakesrc_event_handler), (gst_fakesrc_set_pad_functions), + (gst_fakesrc_set_all_pad_functions), (gst_fakesrc_request_new_pad), + (gst_fakesrc_set_property), (gst_fakesrc_get_property), + (gst_fakesrc_get_range_unlocked), (gst_fakesrc_get_range), + (gst_fakesrc_loop), (gst_fakesrc_activate), + (gst_fakesrc_change_state): + * gst/elements/gstfakesrc.h: + * gst/elements/gstfilesrc.c: (gst_filesrc_init), + (gst_filesrc_get_read), (gst_filesrc_getrange), (gst_filesrc_get), + (gst_filesrc_open_file), (gst_filesrc_loop), + (gst_filesrc_activate), (gst_filesrc_change_state), + (filesrc_find_peek), (filesrc_find_suggest), + (gst_filesrc_type_find): + * gst/elements/gstidentity.c: (gst_identity_finalize), + (gst_identity_class_init), (gst_identity_init), + (gst_identity_proxy_getcaps), (identity_queue_push), + (identity_queue_pop), (identity_queue_flush), (gst_identity_event), + (gst_identity_getrange), (gst_identity_chain), + (gst_identity_sink_loop), (gst_identity_src_loop), + (gst_identity_handle_buffer), (gst_identity_set_dataflow_funcs), + (gst_identity_set_property), (gst_identity_get_property), + (gst_identity_change_state): + * gst/elements/gstidentity.h: + * gst/elements/gsttee.c: (gst_tee_class_init), (gst_tee_init), + (gst_tee_update_pad_functions), (gst_tee_request_new_pad), + (gst_tee_set_property), (gst_tee_get_property), (gst_tee_do_push), + (gst_tee_handle_buffer), (gst_tee_chain), (gst_tee_loop), + (gst_tee_sink_activate): + * gst/elements/gsttee.h: + * gst/gst.c: (gst_register_core_elements), (init_post): + * gst/gst.h: + * gst/gstbin.c: (gst_bin_class_init), (gst_bin_set_bus), + (gst_bin_set_scheduler), (gst_bin_add_func), (gst_bin_add), + (gst_bin_remove_func), (gst_bin_remove), (gst_bin_get_state), + (gst_bin_change_state): + * gst/gstbin.h: + * gst/gstbus.c: (gst_bus_get_type), (gst_bus_class_init), + (gst_bus_init), (gst_bus_dispose), (gst_bus_set_property), + (gst_bus_get_property), (gst_bus_new), (gst_bus_post), + (gst_bus_have_pending), (gst_bus_pop), (gst_bus_peek), + (gst_bus_set_sync_handler), (gst_bus_create_watch), + (bus_watch_callback), (bus_watch_destroy), + (gst_bus_add_watch_full), (gst_bus_add_watch), (poll_handler), + (poll_timeout), (gst_bus_poll): + * gst/gstbus.h: + * gst/gstcaps.h: + * gst/gstdata.h: + * gst/gstelement.c: (gst_element_class_init), (gst_element_init), + (gst_element_post_message), (gst_element_message_full), + (gst_element_get_state_func), (gst_element_get_state), + (gst_element_abort_state), (gst_element_commit_state), + (gst_element_lost_state), (gst_element_set_state), + (gst_element_pads_activate), (gst_element_change_state), + (gst_element_dispose), (gst_element_set_manager_func), + (gst_element_set_bus_func), (gst_element_set_scheduler_func), + (gst_element_set_manager), (gst_element_get_manager), + (gst_element_set_bus), (gst_element_get_bus), + (gst_element_set_scheduler), (gst_element_get_scheduler): + * gst/gstelement.h: + * gst/gstevent.c: (gst_event_new_segment_seek), + (gst_event_new_flush): + * gst/gstevent.h: + * gst/gstmessage.c: (_gst_message_initialize), (_gst_message_copy), + (_gst_message_free), (gst_message_get_type), (gst_message_new), + (gst_message_new_eos), (gst_message_new_error), + (gst_message_new_warning), (gst_message_new_tag), + (gst_message_new_state_changed), (gst_message_new_application), + (gst_message_get_structure), (gst_message_parse_tag), + (gst_message_parse_state_changed), (gst_message_parse_error), + (gst_message_parse_warning): + * gst/gstmessage.h: + * gst/gstpad.c: (gst_real_pad_class_init), (gst_real_pad_init), + (gst_real_pad_set_property), (gst_pad_set_active), + (gst_pad_is_active), (gst_pad_set_blocked_async), + (gst_pad_set_blocked), (gst_pad_is_blocked), + (gst_pad_set_activate_function), (gst_pad_set_loop_function), + (gst_pad_set_getrange_function), (gst_pad_set_acceptcaps_function), + (gst_pad_set_fixatecaps_function), (gst_pad_set_setcaps_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_get_caps), + (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_get_negotiated_caps), + (gst_pad_get_filter_caps), (gst_pad_alloc_buffer), + (gst_real_pad_dispose), (gst_real_pad_finalize), + (handle_pad_block), (gst_pad_push), (gst_pad_pull_range), + (gst_pad_event_default_dispatch), (gst_pad_event_default), + (gst_pad_push_event), (gst_pad_send_event), (gst_pad_get_formats): + * gst/gstpad.h: + * gst/gstpipeline.c: (gst_pipeline_init), (is_eos), + (pipeline_bus_handler), (gst_pipeline_change_state), + (gst_pipeline_get_scheduler), (gst_pipeline_get_bus): + * gst/gstpipeline.h: + * gst/gstprobe.h: + * gst/gstqueue.c: (gst_queue_class_init), (gst_queue_init), + (gst_queue_finalize), (gst_queue_getcaps), (gst_queue_link_sink), + (gst_queue_link_src), (gst_queue_bufferalloc), + (gst_queue_locked_flush), (gst_queue_handle_sink_event), + (gst_queue_is_empty), (gst_queue_is_filled), (gst_queue_chain), + (gst_queue_loop), (gst_queue_handle_src_event), + (gst_queue_handle_src_query), (gst_queue_src_activate), + (gst_queue_change_state): + * gst/gstqueue.h: + * gst/gstscheduler.c: (gst_scheduler_init), + (gst_scheduler_dispose), (gst_scheduler_create_task), + (gst_scheduler_factory_create): + * gst/gstscheduler.h: + * gst/gststructure.c: (gst_structure_get_type), + (gst_structure_copy_conditional): + * gst/gststructure.h: + * gst/gsttaginterface.h: + * gst/gsttask.c: (gst_task_get_type), (gst_task_class_init), + (gst_task_init), (gst_task_dispose), (gst_task_create), + (gst_task_get_state), (gst_task_start), (gst_task_stop), + (gst_task_pause): + * gst/gsttask.h: + * gst/gstthread.c: + * gst/gstthread.h: + * gst/gsttypes.h: + * gst/schedulers/Makefile.am: + * 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_task_get_type), + (gst_thread_scheduler_task_class_init), + (gst_thread_scheduler_task_init), + (gst_thread_scheduler_task_start), + (gst_thread_scheduler_task_stop), + (gst_thread_scheduler_task_pause), (gst_thread_scheduler_get_type), + (gst_thread_scheduler_class_init), (gst_thread_scheduler_func), + (gst_thread_scheduler_init), (gst_thread_scheduler_create_task), + (gst_thread_scheduler_setup), (gst_thread_scheduler_reset), + (plugin_init): + * libs/gst/Makefile.am: + * libs/gst/bytestream/bytestream.c: (gst_bytestream_get_next_buf): + * libs/gst/bytestream/filepad.c: (gst_file_pad_init), + (gst_file_pad_parent_set): + * libs/gst/dataprotocol/dataprotocol.c: (gst_dp_packet_from_event), + (gst_dp_event_from_packet): + * tests/complexity.c: (main): + * tests/mass_elements.c: (main): + * testsuite/states/locked.c: (message_received), (main): + * testsuite/states/parent.c: (main): + * tools/gst-inspect.c: (print_element_flag_info), + (print_implementation_info), (print_pad_info): + * tools/gst-launch.c: (check_intr), (play_handler), (event_loop), + (main): + * tools/gst-md5sum.c: (event_loop), (main): + * tools/gst-typefind.c: (main): + * tools/gst-xmlinspect.c: (print_element_info): + Next big merge. + Added GstBus for mainloop integration. + Added GstMessage for sending notifications on the bus. + Added GstTask as an abstraction for pipeline entry points. + Removed GstThread. + Removed Schedulers. + Simplified GstQueue for multithreaded core. + Made _link threadsafe, removed old capsnego. + Added STREAM_LOCK and PREROLL_LOCK in GstPad. + Added pad blocking functions. + Reworked scheduling functions in GstPad to prepare for + scheduling updates soon. + Moved events out of data stream. + Simplified GstEvent types. + Added return values to push/pull. + Removed clocking from GstElement. + Added prototypes for state change function for next merge. + Removed iterate from bins and state change management. + Fixed some elements, disabled others for now. + Fixed -inspect and -launch. + Added check for GstBus. + 2005-03-10 Wim Taymans * docs/design/part-MT-refcounting.txt: diff --git a/check/Makefile.am b/check/Makefile.am index 66a2464798..37b452533a 100644 --- a/check/Makefile.am +++ b/check/Makefile.am @@ -20,6 +20,7 @@ CLEANFILES = core.* TESTS = $(top_builddir)/tools/gst-register-@GST_MAJORMINOR@ \ gst/gstbin \ + gst/gstbus \ gst/gstcaps \ gst/gstdata \ gst/gstiterator \ diff --git a/common b/common index b2638c1007..131c263212 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit b2638c100721f67b280c3b43b21f1ce1c9b5e316 +Subproject commit 131c2632127e6f061b5270d8f80651782a4fdd13 diff --git a/gst/Makefile.am b/gst/Makefile.am index 2a5dfdb3e8..ff02b8aa33 100644 --- a/gst/Makefile.am +++ b/gst/Makefile.am @@ -1,10 +1,6 @@ lib_LTLIBRARIES = libgstreamer-@GST_MAJORMINOR@.la AS_LIBTOOL_LIB = libgstreamer-@GST_MAJORMINOR@ -if GST_DISABLE_OMEGA_COTHREADS noinst_LTLIBRARIES = -else -noinst_LTLIBRARIES = libcothreads.la -endif #GST_INSTRUMENT_FLAGS = -finstrument-functions -DGST_ENABLE_FUNC_INSTRUMENTATION @@ -67,7 +63,7 @@ GST_URI_SRC = gsturi.c endif SUBDIRS = $(GST_PARSE_DIRS) $(GST_REGISTRY_DIRS) . elements schedulers $(GST_INDEX_DIRS) -DIST_SUBDIRS = autoplug elements parse registries schedulers indexers +DIST_SUBDIRS = elements parse registries schedulers indexers # make variables for all generated source and header files to make the # distinction clear @@ -85,6 +81,7 @@ libgstreamer_@GST_MAJORMINOR@_la_SOURCES = \ gstatomic.c \ gstbin.c \ gstbuffer.c \ + gstbus.c \ gstcaps.c \ gstclock.c \ gstcpu.c \ @@ -100,19 +97,20 @@ libgstreamer_@GST_MAJORMINOR@_la_SOURCES = \ gstinterface.c \ gstiterator.c \ gstmemchunk.c \ + gstmessage.c \ gstpad.c \ gstpipeline.c \ gstplugin.c \ gstpluginfeature.c \ gstprobe.c \ - gstqueue.c \ gstquery.c \ + gstqueue.c \ gstscheduler.c \ gststructure.c \ gstsystemclock.c \ gsttag.c \ gsttaginterface.c \ - gstthread.c \ + gsttask.c \ $(GST_TRACE_SRC) \ gsttrashstack.c \ gsttypefind.c \ @@ -159,6 +157,7 @@ gst_headers = \ gstobject.h \ gstbin.h \ gstbuffer.h \ + gstbus.h \ gstcaps.h \ gstclock.h \ gstcompat.h \ @@ -175,19 +174,20 @@ gst_headers = \ gstiterator.h \ gstmacros.h \ gstmemchunk.h \ + gstmessage.h \ gstpad.h \ gstpipeline.h \ gstplugin.h \ gstpluginfeature.h \ gstprobe.h \ - gstqueue.h \ gstquery.h \ + gstqueue.h \ gstscheduler.h \ gststructure.h \ gstsystemclock.h \ gsttag.h \ gsttaginterface.h \ - gstthread.h \ + gsttask.h \ gsttrace.h \ gsttrashstack.h \ gsttypefind.h \ @@ -215,15 +215,6 @@ noinst_HEADERS = \ gstarch.h \ cothreads.h -if GST_DISABLE_OMEGA_COTHREADS -#libcothreads_la_SOURCES = -#libcothreads_la_CFLAGS = -else -libcothreads_la_SOURCES = cothreads.c -libcothreads_la_CFLAGS = $(libgstreamer_@GST_MAJORMINOR@_la_CFLAGS) -endif - - gstmarshal.h: gstmarshal.list glib-genmarshal --header --prefix=gst_marshal $(srcdir)/gstmarshal.list > gstmarshal.h.tmp mv gstmarshal.h.tmp gstmarshal.h diff --git a/gst/elements/Makefile.am b/gst/elements/Makefile.am index dc18de4fc0..db5cb1fcea 100644 --- a/gst/elements/Makefile.am +++ b/gst/elements/Makefile.am @@ -24,23 +24,24 @@ endif libgstelements_la_DEPENDENCIES = ../libgstreamer-@GST_MAJORMINOR@.la libgstelements_la_SOURCES = \ - gstaggregator.c \ - gstbufferstore.c \ - gstelements.c \ - gstfakesink.c \ gstfakesrc.c \ - gstfilesink.c \ + gstfakesink.c \ gstfilesrc.c \ - gstfdsink.c \ - gstfdsrc.c \ gstidentity.c \ - gstmd5sink.c \ - $(multifilesrc) \ - $(pipefilter) \ - gstshaper.c \ - gststatistics.c \ - gsttee.c \ - gsttypefindelement.c + gstelements.c \ + gsttee.c + +# gstaggregator.c \ +# gstbufferstore.c \ +# gstfilesink.c \ +# gstfdsink.c \ +# gstfdsrc.c \ +# gstmd5sink.c \ +# $(multifilesrc) \ +# $(pipefilter) \ +# gstshaper.c \ +# gststatistics.c \ +# gsttypefindelement.c libgstelements_la_CFLAGS = $(GST_OBJ_CFLAGS) libgstelements_la_LIBADD = $(GST_OBJ_LIBS) diff --git a/gst/elements/gstelements.c b/gst/elements/gstelements.c index af4bcb1f0b..2099c74e07 100644 --- a/gst/elements/gstelements.c +++ b/gst/elements/gstelements.c @@ -55,24 +55,24 @@ extern GType gst_filesrc_get_type (void); extern GstElementDetails gst_filesrc_details; static struct _elements_entry _elements[] = { - {"aggregator", GST_RANK_NONE, gst_aggregator_get_type}, +// {"aggregator", GST_RANK_NONE, gst_aggregator_get_type}, {"fakesrc", GST_RANK_NONE, gst_fakesrc_get_type}, {"fakesink", GST_RANK_NONE, gst_fakesink_get_type}, - {"fdsink", GST_RANK_NONE, gst_fdsink_get_type}, - {"fdsrc", GST_RANK_NONE, gst_fdsrc_get_type}, {"filesrc", GST_RANK_NONE, gst_filesrc_get_type}, - {"filesink", GST_RANK_NONE, gst_filesink_get_type}, {"identity", GST_RANK_NONE, gst_identity_get_type}, - {"md5sink", GST_RANK_NONE, gst_md5sink_get_type}, +// {"fdsink", GST_RANK_NONE, gst_fdsink_get_type}, +// {"fdsrc", GST_RANK_NONE, gst_fdsrc_get_type}, +// {"filesink", GST_RANK_NONE, gst_filesink_get_type}, +// {"md5sink", GST_RANK_NONE, gst_md5sink_get_type}, #ifndef HAVE_WIN32 - {"multifilesrc", GST_RANK_NONE, gst_multifilesrc_get_type}, - {"pipefilter", GST_RANK_NONE, gst_pipefilter_get_type}, +// {"multifilesrc", GST_RANK_NONE, gst_multifilesrc_get_type}, +// {"pipefilter", GST_RANK_NONE, gst_pipefilter_get_type}, #endif - {"shaper", GST_RANK_NONE, gst_shaper_get_type}, - {"statistics", GST_RANK_NONE, gst_statistics_get_type}, +// {"shaper", GST_RANK_NONE, gst_shaper_get_type}, +// {"statistics", GST_RANK_NONE, gst_statistics_get_type}, {"tee", GST_RANK_NONE, gst_tee_get_type}, - {"typefind", GST_RANK_NONE, gst_type_find_element_get_type}, - {NULL, 0}, +// {"typefind", GST_RANK_NONE, gst_type_find_element_get_type}, +// {NULL, 0}, }; static gboolean diff --git a/gst/elements/gstfakesink.c b/gst/elements/gstfakesink.c index fcf64b5d30..5491678618 100644 --- a/gst/elements/gstfakesink.c +++ b/gst/elements/gstfakesink.c @@ -113,7 +113,8 @@ static void gst_fakesink_get_property (GObject * object, guint prop_id, static GstElementStateReturn gst_fakesink_change_state (GstElement * element); -static void gst_fakesink_chain (GstPad * pad, GstData * _data); +static GstFlowReturn gst_fakesink_chain (GstPad * pad, GstBuffer * buffer); +static gboolean gst_fakesink_event (GstPad * pad, GstEvent * event); static guint gst_fakesink_signals[LAST_SIGNAL] = { 0 }; @@ -188,6 +189,7 @@ gst_fakesink_init (GstFakeSink * fakesink) "sink"); gst_element_add_pad (GST_ELEMENT (fakesink), pad); gst_pad_set_chain_function (pad, GST_DEBUG_FUNCPTR (gst_fakesink_chain)); + gst_pad_set_event_function (pad, GST_DEBUG_FUNCPTR (gst_fakesink_event)); fakesink->silent = FALSE; fakesink->dump = FALSE; @@ -195,8 +197,6 @@ gst_fakesink_init (GstFakeSink * fakesink) fakesink->last_message = NULL; fakesink->state_error = FAKESINK_STATE_ERROR_NONE; fakesink->signal_handoffs = FALSE; - - GST_FLAG_SET (fakesink, GST_ELEMENT_EVENT_AWARE); } static void @@ -307,47 +307,43 @@ gst_fakesink_get_property (GObject * object, guint prop_id, GValue * value, } } -static void -gst_fakesink_chain (GstPad * pad, GstData * _data) +static gboolean +gst_fakesink_event (GstPad * pad, GstEvent * event) { - GstBuffer *buf = GST_BUFFER (_data); GstFakeSink *fakesink; - g_return_if_fail (pad != NULL); - g_return_if_fail (GST_IS_PAD (pad)); - g_return_if_fail (buf != NULL); - fakesink = GST_FAKESINK (GST_OBJECT_PARENT (pad)); - if (GST_IS_EVENT (buf)) { - GstEvent *event = GST_EVENT (buf); + if (!fakesink->silent) { + g_free (fakesink->last_message); - if (!fakesink->silent) { - g_free (fakesink->last_message); + fakesink->last_message = + g_strdup_printf ("chain ******* (%s:%s)E (type: %d) %p", + GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE (event), event); - fakesink->last_message = - g_strdup_printf ("chain ******* (%s:%s)E (type: %d) %p", - GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE (event), event); - - g_object_notify (G_OBJECT (fakesink), "last_message"); - } - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_DISCONTINUOUS: - if (fakesink->sync && fakesink->clock) { - gint64 value = GST_EVENT_DISCONT_OFFSET (event, 0).value; - - gst_element_set_time (GST_ELEMENT (fakesink), value); - } - default: - gst_pad_event_default (pad, event); - break; - } - return; + g_object_notify (G_OBJECT (fakesink), "last_message"); } + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_DISCONTINUOUS: + default: + gst_pad_event_default (pad, event); + break; + } + + return TRUE; +} + +static GstFlowReturn +gst_fakesink_chain (GstPad * pad, GstBuffer * buffer) +{ + GstBuffer *buf = GST_BUFFER (buffer); + GstFakeSink *fakesink; + + fakesink = GST_FAKESINK (GST_OBJECT_PARENT (pad)); + if (fakesink->sync && fakesink->clock) { - gst_element_wait (GST_ELEMENT (fakesink), GST_BUFFER_TIMESTAMP (buf)); + //gst_element_wait (GST_ELEMENT (fakesink), GST_BUFFER_TIMESTAMP (buf)); } if (!fakesink->silent) { @@ -374,6 +370,8 @@ gst_fakesink_chain (GstPad * pad, GstData * _data) } gst_buffer_unref (buf); + + return GST_FLOW_OK; } static GstElementStateReturn diff --git a/gst/elements/gstfakesrc.c b/gst/elements/gstfakesrc.c index 82bb94f678..cb5f7dc210 100644 --- a/gst/elements/gstfakesrc.c +++ b/gst/elements/gstfakesrc.c @@ -64,7 +64,8 @@ enum { ARG_0, ARG_NUM_SOURCES, - ARG_LOOP_BASED, + ARG_HAS_LOOP, + ARG_HAS_GETRANGE, ARG_OUTPUT, ARG_DATA, ARG_SIZETYPE, @@ -179,7 +180,7 @@ GST_BOILERPLATE_FULL (GstFakeSrc, gst_fakesrc, GstElement, GST_TYPE_ELEMENT, static GstPad *gst_fakesrc_request_new_pad (GstElement * element, GstPadTemplate * templ, const gchar * unused); -static void gst_fakesrc_update_functions (GstFakeSrc * src); +static gboolean gst_fakesrc_activate (GstPad * pad, GstActivateMode mode); static void gst_fakesrc_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_fakesrc_get_property (GObject * object, guint prop_id, @@ -188,8 +189,9 @@ static void gst_fakesrc_set_clock (GstElement * element, GstClock * clock); static GstElementStateReturn gst_fakesrc_change_state (GstElement * element); -static GstData *gst_fakesrc_get (GstPad * pad); -static void gst_fakesrc_loop (GstElement * element); +static void gst_fakesrc_loop (GstPad * pad); +static GstFlowReturn gst_fakesrc_get_range (GstPad * pad, guint64 offset, + guint length, GstBuffer ** buf); static guint gst_fakesrc_signals[LAST_SIGNAL] = { 0 }; @@ -220,9 +222,14 @@ gst_fakesrc_class_init (GstFakeSrcClass * klass) g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_NUM_SOURCES, g_param_spec_int ("num-sources", "num-sources", "Number of sources", 1, G_MAXINT, 1, G_PARAM_READABLE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LOOP_BASED, - g_param_spec_boolean ("loop-based", "loop-based", - "Enable loop-based operation", FALSE, G_PARAM_READWRITE)); + g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_HAS_LOOP, + g_param_spec_boolean ("has-loop", "has-loop", + "Enable loop-based operation", TRUE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_HAS_GETRANGE, + g_param_spec_boolean ("has-getrange", "has-getrange", + "Enable getrange-based operation", TRUE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_OUTPUT, g_param_spec_enum ("output", "output", "Output method (currently unused)", GST_TYPE_FAKESRC_OUTPUT, FAKESRC_FIRST_LAST_LOOP, G_PARAM_READWRITE)); @@ -300,9 +307,6 @@ gst_fakesrc_init (GstFakeSrc * fakesrc) "src"); gst_element_add_pad (GST_ELEMENT (fakesrc), pad); - fakesrc->loop_based = FALSE; - gst_fakesrc_update_functions (fakesrc); - fakesrc->output = FAKESRC_FIRST_LAST_LOOP; fakesrc->segment_start = -1; fakesrc->segment_end = -1; @@ -324,6 +328,7 @@ gst_fakesrc_init (GstFakeSrc * fakesrc) fakesrc->last_message = NULL; fakesrc->datarate = DEFAULT_DATARATE; fakesrc->sync = DEFAULT_SYNC; + fakesrc->pad_mode = GST_ACTIVATE_NONE; } static void @@ -336,35 +341,6 @@ gst_fakesrc_set_clock (GstElement * element, GstClock * clock) src->clock = clock; } - -static GstPad * -gst_fakesrc_request_new_pad (GstElement * element, GstPadTemplate * templ, - const gchar * unused) -{ - gchar *name; - GstPad *srcpad; - GstFakeSrc *fakesrc; - - g_return_val_if_fail (GST_IS_FAKESRC (element), NULL); - - if (templ->direction != GST_PAD_SRC) { - g_warning ("gstfakesrc: request new pad that is not a SRC pad\n"); - return NULL; - } - - fakesrc = GST_FAKESRC (element); - - name = g_strdup_printf ("src%d", GST_ELEMENT (fakesrc)->numsrcpads); - - srcpad = gst_pad_new_from_template (templ, name); - gst_element_add_pad (GST_ELEMENT (fakesrc), srcpad); - gst_fakesrc_update_functions (fakesrc); - - g_free (name); - - return srcpad; -} - static const GstFormat * gst_fakesrc_get_formats (GstPad * pad) { @@ -419,8 +395,7 @@ static const GstEventMask * gst_fakesrc_get_event_mask (GstPad * pad) { static const GstEventMask masks[] = { - {GST_EVENT_SEEK, GST_SEEK_FLAG_FLUSH}, - {GST_EVENT_SEEK_SEGMENT, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_SEGMENT_LOOP}, + {GST_EVENT_SEEK, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_SEGMENT_LOOP}, {GST_EVENT_FLUSH, 0}, {0, 0}, }; @@ -433,22 +408,16 @@ gst_fakesrc_event_handler (GstPad * pad, GstEvent * event) { GstFakeSrc *src; - src = GST_FAKESRC (gst_pad_get_parent (pad)); + src = GST_FAKESRC (GST_PAD_PARENT (pad)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: - src->buffer_count = GST_EVENT_SEEK_OFFSET (event); - - if (!GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH) { - break; - } - /* else we do a flush too */ - case GST_EVENT_SEEK_SEGMENT: src->segment_start = GST_EVENT_SEEK_OFFSET (event); src->segment_end = GST_EVENT_SEEK_ENDOFFSET (event); src->buffer_count = src->segment_start; src->segment_loop = GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_SEGMENT_LOOP; + src->need_flush = GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH; break; case GST_EVENT_FLUSH: src->need_flush = TRUE; @@ -462,34 +431,61 @@ gst_fakesrc_event_handler (GstPad * pad, GstEvent * event) } static void -gst_fakesrc_update_functions (GstFakeSrc * src) +gst_fakesrc_set_pad_functions (GstFakeSrc * src, GstPad * pad) { - GList *pads; + gst_pad_set_activate_function (pad, gst_fakesrc_activate); + gst_pad_set_event_function (pad, gst_fakesrc_event_handler); + gst_pad_set_event_mask_function (pad, gst_fakesrc_get_event_mask); + gst_pad_set_query_function (pad, gst_fakesrc_query); + gst_pad_set_query_type_function (pad, gst_fakesrc_get_query_types); + gst_pad_set_formats_function (pad, gst_fakesrc_get_formats); - if (src->loop_based) { - gst_element_set_loop_function (GST_ELEMENT (src), - GST_DEBUG_FUNCPTR (gst_fakesrc_loop)); - } else { - gst_element_set_loop_function (GST_ELEMENT (src), NULL); + if (src->has_loop) + gst_pad_set_loop_function (pad, gst_fakesrc_loop); + else + gst_pad_set_loop_function (pad, NULL); + + if (src->has_getrange) + gst_pad_set_getrange_function (pad, gst_fakesrc_get_range); + else + gst_pad_set_getrange_function (pad, NULL); +} + +static void +gst_fakesrc_set_all_pad_functions (GstFakeSrc * src) +{ + GList *l; + + for (l = GST_ELEMENT_PADS (src); l; l = l->next) + gst_fakesrc_set_pad_functions (src, (GstPad *) l->data); +} + +static GstPad * +gst_fakesrc_request_new_pad (GstElement * element, GstPadTemplate * templ, + const gchar * unused) +{ + gchar *name; + GstPad *srcpad; + GstFakeSrc *fakesrc; + + g_return_val_if_fail (GST_IS_FAKESRC (element), NULL); + + if (templ->direction != GST_PAD_SRC) { + g_warning ("gstfakesrc: request new pad that is not a SRC pad\n"); + return NULL; } - pads = GST_ELEMENT (src)->pads; - while (pads) { - GstPad *pad = GST_PAD (pads->data); + fakesrc = GST_FAKESRC (element); - if (src->loop_based) { - gst_pad_set_get_function (pad, NULL); - } else { - gst_pad_set_get_function (pad, GST_DEBUG_FUNCPTR (gst_fakesrc_get)); - } + name = g_strdup_printf ("src%d", GST_ELEMENT (fakesrc)->numsrcpads); - gst_pad_set_event_function (pad, gst_fakesrc_event_handler); - gst_pad_set_event_mask_function (pad, gst_fakesrc_get_event_mask); - gst_pad_set_query_function (pad, gst_fakesrc_query); - gst_pad_set_query_type_function (pad, gst_fakesrc_get_query_types); - gst_pad_set_formats_function (pad, gst_fakesrc_get_formats); - pads = g_list_next (pads); - } + srcpad = gst_pad_new_from_template (templ, name); + gst_element_add_pad (GST_ELEMENT (fakesrc), srcpad); + gst_fakesrc_set_pad_functions (fakesrc, srcpad); + + g_free (name); + + return srcpad; } static void @@ -511,13 +507,16 @@ gst_fakesrc_set_property (GObject * object, guint prop_id, const GValue * value, { GstFakeSrc *src; - /* it's not null if we got it, but it might not be ours */ src = GST_FAKESRC (object); switch (prop_id) { - case ARG_LOOP_BASED: - src->loop_based = g_value_get_boolean (value); - gst_fakesrc_update_functions (src); + case ARG_HAS_LOOP: + src->has_loop = g_value_get_boolean (value); + gst_fakesrc_set_all_pad_functions (src); + break; + case ARG_HAS_GETRANGE: + src->has_getrange = g_value_get_boolean (value); + gst_fakesrc_set_all_pad_functions (src); break; case ARG_OUTPUT: g_warning ("not yet implemented"); @@ -595,8 +594,11 @@ gst_fakesrc_get_property (GObject * object, guint prop_id, GValue * value, case ARG_NUM_SOURCES: g_value_set_int (value, GST_ELEMENT (src)->numsrcpads); break; - case ARG_LOOP_BASED: - g_value_set_boolean (value, src->loop_based); + case ARG_HAS_LOOP: + g_value_set_boolean (value, src->has_loop); + break; + case ARG_HAS_GETRANGE: + g_value_set_boolean (value, src->has_getrange); break; case ARG_OUTPUT: g_value_set_enum (value, src->output); @@ -789,36 +791,28 @@ gst_fakesrc_create_buffer (GstFakeSrc * src) return buf; } -static GstData * -gst_fakesrc_get (GstPad * pad) +static GstFlowReturn +gst_fakesrc_get_range_unlocked (GstPad * pad, guint64 offset, guint length, + GstBuffer ** ret) { GstFakeSrc *src; GstBuffer *buf; GstClockTime time; - g_return_val_if_fail (pad != NULL, NULL); - src = GST_FAKESRC (GST_OBJECT_PARENT (pad)); - g_return_val_if_fail (GST_IS_FAKESRC (src), NULL); - - if (src->need_flush) { - src->need_flush = FALSE; - return GST_DATA (gst_event_new (GST_EVENT_FLUSH)); - } - if (src->buffer_count == src->segment_end) { if (src->segment_loop) { - return GST_DATA (gst_event_new (GST_EVENT_SEGMENT_DONE)); + //gst_pad_push_event (pad, gst_event_new (GST_EVENT_SEGMENT_DONE)); } else { - gst_element_set_eos (GST_ELEMENT (src)); - return GST_DATA (gst_event_new (GST_EVENT_EOS)); + gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS)); + return GST_FLOW_UNEXPECTED; } } if (src->rt_num_buffers == 0) { - gst_element_set_eos (GST_ELEMENT (src)); - return GST_DATA (gst_event_new (GST_EVENT_EOS)); + gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS)); + return GST_FLOW_UNEXPECTED; } else { if (src->rt_num_buffers > 0) src->rt_num_buffers--; @@ -826,8 +820,8 @@ gst_fakesrc_get (GstPad * pad) if (src->eos) { GST_INFO ("fakesrc is setting eos on pad"); - gst_element_set_eos (GST_ELEMENT (src)); - return GST_DATA (gst_event_new (GST_EVENT_EOS)); + gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS)); + return GST_FLOW_UNEXPECTED; } buf = gst_fakesrc_create_buffer (src); @@ -838,7 +832,7 @@ gst_fakesrc_get (GstPad * pad) if (src->datarate > 0) { time = (src->bytes_sent * GST_SECOND) / src->datarate; if (src->sync) { - gst_element_wait (GST_ELEMENT (src), time); + /* gst_element_wait (GST_ELEMENT (src), time); */ } GST_BUFFER_DURATION (buf) = @@ -866,49 +860,121 @@ gst_fakesrc_get (GstPad * pad) src->bytes_sent += GST_BUFFER_SIZE (buf); - return GST_DATA (buf); + *ret = buf; + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_fakesrc_get_range (GstPad * pad, guint64 offset, guint length, + GstBuffer ** ret) +{ + GstFlowReturn fret; + + g_assert (GST_FAKESRC (GST_OBJECT_PARENT (pad))->pad_mode == + GST_ACTIVATE_PULL); + + GST_STREAM_LOCK (pad); + + fret = gst_fakesrc_get_range_unlocked (pad, offset, length, ret); + + GST_STREAM_UNLOCK (pad); + + return fret; } -/** - * gst_fakesrc_loop: - * @element: the faksesrc to loop - * - * generate an empty buffer and push it to the next element. - */ static void -gst_fakesrc_loop (GstElement * element) +gst_fakesrc_loop (GstPad * pad) { GstFakeSrc *src; - const GList *pads; + GstBuffer *buf = NULL; + GstFlowReturn ret; - g_return_if_fail (element != NULL); - g_return_if_fail (GST_IS_FAKESRC (element)); + src = GST_FAKESRC (GST_OBJECT_PARENT (pad)); - src = GST_FAKESRC (element); + g_assert (src->pad_mode == GST_ACTIVATE_PUSH); - pads = element->pads; - - while (pads) { - GstPad *pad = GST_PAD (pads->data); - GstData *data; - - data = gst_fakesrc_get (pad); - gst_pad_push (pad, data); - - if (src->eos) { - return; - } - - pads = g_list_next (pads); + GST_STREAM_LOCK (pad); + if (src->need_flush) { + src->need_flush = FALSE; + gst_pad_push_event (pad, gst_event_new (GST_EVENT_FLUSH)); } + + ret = gst_fakesrc_get_range_unlocked (pad, src->buffer_count, + DEFAULT_SIZEMAX, &buf); + if (ret != GST_FLOW_OK) { + goto pause; + } + + ret = gst_pad_push (pad, buf); + if (ret != GST_FLOW_OK) { + goto pause; + } + + GST_STREAM_UNLOCK (pad); + return; + +pause: + gst_task_pause (GST_RPAD_TASK (pad)); + GST_STREAM_UNLOCK (pad); + return; +} + +static gboolean +gst_fakesrc_activate (GstPad * pad, GstActivateMode mode) +{ + gboolean result = FALSE; + GstFakeSrc *fakesrc; + + fakesrc = GST_FAKESRC (GST_OBJECT_PARENT (pad)); + + switch (mode) { + case GST_ACTIVATE_PUSH: + /* if we have a scheduler we can start the task */ + g_return_val_if_fail (fakesrc->has_loop, FALSE); + if (GST_ELEMENT_SCHEDULER (fakesrc)) { + GST_STREAM_LOCK (pad); + GST_RPAD_TASK (pad) = + gst_scheduler_create_task (GST_ELEMENT_SCHEDULER (fakesrc), + (GstTaskFunction) gst_fakesrc_loop, pad); + + gst_task_start (GST_RPAD_TASK (pad)); + GST_STREAM_UNLOCK (pad); + result = TRUE; + } + break; + case GST_ACTIVATE_PULL: + g_return_val_if_fail (fakesrc->has_getrange, FALSE); + result = TRUE; + break; + case GST_ACTIVATE_NONE: + /* step 1, unblock clock sync (if any) */ + + /* step 2, make sure streaming finishes */ + GST_STREAM_LOCK (pad); + /* step 3, stop the task */ + if (GST_RPAD_TASK (pad)) { + gst_task_stop (GST_RPAD_TASK (pad)); + gst_object_unref (GST_OBJECT (GST_RPAD_TASK (pad))); + GST_RPAD_TASK (pad) = NULL; + } + GST_STREAM_UNLOCK (pad); + + result = TRUE; + break; + } + + fakesrc->pad_mode = mode; + + return result; } static GstElementStateReturn gst_fakesrc_change_state (GstElement * element) { GstFakeSrc *fakesrc; + GstElementStateReturn result = GST_STATE_FAILURE; - g_return_val_if_fail (GST_IS_FAKESRC (element), GST_STATE_FAILURE); + g_return_val_if_fail (GST_IS_FAKESRC (element), result); fakesrc = GST_FAKESRC (element); @@ -916,6 +982,7 @@ gst_fakesrc_change_state (GstElement * element) case GST_STATE_NULL_TO_READY: break; case GST_STATE_READY_TO_PAUSED: + { fakesrc->buffer_count = 0; fakesrc->pattern_byte = 0x00; fakesrc->need_flush = FALSE; @@ -923,7 +990,14 @@ gst_fakesrc_change_state (GstElement * element) fakesrc->bytes_sent = 0; fakesrc->rt_num_buffers = fakesrc->num_buffers; break; + } case GST_STATE_PAUSED_TO_PLAYING: + break; + } + + result = GST_ELEMENT_CLASS (parent_class)->change_state (element); + + switch (GST_STATE_TRANSITION (element)) { case GST_STATE_PLAYING_TO_PAUSED: break; case GST_STATE_PAUSED_TO_READY: @@ -940,8 +1014,5 @@ gst_fakesrc_change_state (GstElement * element) break; } - if (GST_ELEMENT_CLASS (parent_class)->change_state) - return GST_ELEMENT_CLASS (parent_class)->change_state (element); - - return GST_STATE_SUCCESS; + return result; } diff --git a/gst/elements/gstfakesrc.h b/gst/elements/gstfakesrc.h index 5214f0bb7c..a5251b85f3 100644 --- a/gst/elements/gstfakesrc.h +++ b/gst/elements/gstfakesrc.h @@ -76,13 +76,15 @@ typedef struct _GstFakeSrcClass GstFakeSrcClass; struct _GstFakeSrc { GstElement element; - gboolean loop_based; + gboolean has_loop; + gboolean has_getrange; gboolean eos; GstFakeSrcOutputType output; GstFakeSrcDataType data; GstFakeSrcSizeType sizetype; GstFakeSrcFillType filltype; + GstActivateMode pad_mode; guint sizemin; guint sizemax; diff --git a/gst/elements/gstfilesrc.c b/gst/elements/gstfilesrc.c index cddf90b400..ce9354f5c0 100644 --- a/gst/elements/gstfilesrc.c +++ b/gst/elements/gstfilesrc.c @@ -170,13 +170,17 @@ static void gst_filesrc_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static gboolean gst_filesrc_check_filesize (GstFileSrc * src); -static GstData *gst_filesrc_get (GstPad * pad); +static GstFlowReturn gst_filesrc_get (GstPad * pad, GstBuffer ** buffer); +static GstFlowReturn gst_filesrc_getrange (GstPad * pad, guint64 offset, + guint length, GstBuffer ** buffer); static gboolean gst_filesrc_srcpad_event (GstPad * pad, GstEvent * event); static gboolean gst_filesrc_srcpad_query (GstPad * pad, GstQueryType type, GstFormat * format, gint64 * value); +static gboolean gst_filesrc_activate (GstPad * pad, GstActivateMode mode); static GstElementStateReturn gst_filesrc_change_state (GstElement * element); +static GstCaps *gst_filesrc_type_find (GstFileSrc * src); static void gst_filesrc_uri_handler_init (gpointer g_iface, gpointer iface_data); @@ -247,7 +251,8 @@ gst_filesrc_init (GstFileSrc * src) src->srcpad = gst_pad_new_from_template (gst_static_pad_template_get (&srctemplate), "src"); - gst_pad_set_get_function (src->srcpad, gst_filesrc_get); + gst_pad_set_getrange_function (src->srcpad, gst_filesrc_getrange); + gst_pad_set_activate_function (src->srcpad, gst_filesrc_activate); gst_pad_set_event_function (src->srcpad, gst_filesrc_srcpad_event); gst_pad_set_event_mask_function (src->srcpad, gst_filesrc_get_event_mask); gst_pad_set_query_function (src->srcpad, gst_filesrc_srcpad_query); @@ -672,7 +677,7 @@ gst_filesrc_get_read (GstFileSrc * src) if (ret == 0) { GST_DEBUG ("non-regular file hits EOS"); gst_buffer_unref (buf); - gst_element_set_eos (GST_ELEMENT (src)); + //gst_element_set_eos (GST_ELEMENT (src)); return GST_DATA (gst_event_new (GST_EVENT_EOS)); } readsize = ret; @@ -686,20 +691,36 @@ gst_filesrc_get_read (GstFileSrc * src) return GST_DATA (buf); } -static GstData * -gst_filesrc_get (GstPad * pad) +static GstFlowReturn +gst_filesrc_getrange (GstPad * pad, guint64 offset, guint length, + GstBuffer ** buffer) { GstFileSrc *src; - g_return_val_if_fail (pad != NULL, NULL); - src = GST_FILESRC (gst_pad_get_parent (pad)); - g_return_val_if_fail (GST_FLAG_IS_SET (src, GST_FILESRC_OPEN), NULL); + src = GST_FILESRC (GST_PAD_PARENT (pad)); + + src->curoffset = offset; + src->block_size = length; + + return gst_filesrc_get (pad, buffer); +} + +static GstFlowReturn +gst_filesrc_get (GstPad * pad, GstBuffer ** buffer) +{ + GstFileSrc *src; + GstData *data; + + src = GST_FILESRC (GST_PAD_PARENT (pad)); + + g_return_val_if_fail (GST_FLAG_IS_SET (src, GST_FILESRC_OPEN), + GST_FLOW_WRONG_STATE); /* check for flush */ if (src->need_flush) { src->need_flush = FALSE; GST_DEBUG_OBJECT (src, "sending flush"); - return GST_DATA (gst_event_new_flush ()); + gst_pad_push_event (pad, gst_event_new_flush (TRUE)); } /* check for seek */ if (src->need_discont) { @@ -710,7 +731,7 @@ gst_filesrc_get (GstPad * pad) gst_event_new_discontinuous (src->need_discont > 1, GST_FORMAT_BYTES, (guint64) src->curoffset, GST_FORMAT_UNDEFINED); src->need_discont = 0; - return GST_DATA (event); + gst_pad_push_event (pad, event); } /* check for EOF if it's a regular file */ @@ -721,20 +742,33 @@ gst_filesrc_get (GstPad * pad) GST_DEBUG_OBJECT (src, "eos %" G_GINT64_FORMAT " %" G_GINT64_FORMAT, src->curoffset, src->filelen); } - gst_element_set_eos (GST_ELEMENT (src)); - return GST_DATA (gst_event_new (GST_EVENT_EOS)); + //gst_element_set_eos (GST_ELEMENT (src)); + gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS)); + return GST_FLOW_WRONG_STATE; } } #ifdef HAVE_MMAP if (src->using_mmap) { - return gst_filesrc_get_mmap (src); + data = gst_filesrc_get_mmap (src); } else { - return gst_filesrc_get_read (src); + data = gst_filesrc_get_read (src); } #else - return gst_filesrc_get_read (src); + data = gst_filesrc_get_read (src); #endif + if (data == NULL) { + GST_DEBUG_OBJECT (src, "could not get data"); + return GST_FLOW_ERROR; + } + + if (GST_IS_EVENT (data)) { + gst_pad_push_event (pad, GST_EVENT (data)); + } else { + *buffer = GST_BUFFER (data); + } + + return GST_FLOW_OK; } /* TRUE if the filesize of the file was updated */ @@ -821,6 +855,22 @@ gst_filesrc_open_file (GstFileSrc * src) src->curoffset = 0; GST_FLAG_SET (src, GST_FILESRC_OPEN); + + { + GstCaps *caps; + guint64 offset; + guint length; + + offset = src->curoffset; + length = src->block_size; + + caps = gst_filesrc_type_find (src); + gst_pad_set_caps (src->srcpad, caps); + + src->curoffset = offset; + src->block_size = length; + } + } return TRUE; } @@ -848,10 +898,77 @@ gst_filesrc_close_file (GstFileSrc * src) GST_FLAG_UNSET (src, GST_FILESRC_OPEN); } +static void +gst_filesrc_loop (GstPad * pad) +{ + GstFileSrc *filesrc; + gboolean result; + GstBuffer *buffer; + + filesrc = GST_FILESRC (GST_PAD_PARENT (pad)); + + GST_STREAM_LOCK (pad); + + result = gst_filesrc_get (pad, &buffer); + if (result != GST_FLOW_OK) { + gst_task_pause (GST_RPAD_TASK (pad)); + goto done; + } + result = gst_pad_push (pad, buffer); + if (result != GST_FLOW_OK) { + gst_task_pause (GST_RPAD_TASK (pad)); + } +done: + GST_STREAM_UNLOCK (pad); +} + + +static gboolean +gst_filesrc_activate (GstPad * pad, GstActivateMode mode) +{ + gboolean result = FALSE; + GstFileSrc *filesrc; + + filesrc = GST_FILESRC (GST_OBJECT_PARENT (pad)); + + switch (mode) { + case GST_ACTIVATE_PUSH: + /* if we have a scheduler we can start the task */ + if (GST_ELEMENT_SCHEDULER (filesrc)) { + GST_STREAM_LOCK (pad); + GST_RPAD_TASK (pad) = + gst_scheduler_create_task (GST_ELEMENT_SCHEDULER (filesrc), + (GstTaskFunction) gst_filesrc_loop, pad); + + gst_task_start (GST_RPAD_TASK (pad)); + result = TRUE; + GST_STREAM_UNLOCK (pad); + } + break; + case GST_ACTIVATE_PULL: + result = TRUE; + break; + case GST_ACTIVATE_NONE: + /* step 1, unblock clock sync (if any) */ + + /* step 2, make sure streaming finishes */ + GST_STREAM_LOCK (pad); + /* step 3, stop the task */ + gst_task_stop (GST_RPAD_TASK (pad)); + GST_STREAM_UNLOCK (pad); + + result = TRUE; + break; + } + return result; +} + static GstElementStateReturn gst_filesrc_change_state (GstElement * element) { + GstElementStateReturn result = GST_STATE_SUCCESS; + GstFileSrc *src = GST_FILESRC (element); switch (GST_STATE_TRANSITION (element)) { @@ -866,6 +983,17 @@ gst_filesrc_change_state (GstElement * element) } src->need_discont = 2; break; + case GST_STATE_PAUSED_TO_PLAYING: + gst_task_start (GST_RPAD_TASK (src->srcpad)); + break; + } + + result = GST_ELEMENT_CLASS (parent_class)->change_state (element); + + switch (GST_STATE_TRANSITION (element)) { + case GST_STATE_PLAYING_TO_PAUSED: + gst_task_start (GST_RPAD_TASK (src->srcpad)); + break; case GST_STATE_PAUSED_TO_READY: if (GST_FLAG_IS_SET (element, GST_FILESRC_OPEN)) gst_filesrc_close_file (GST_FILESRC (element)); @@ -874,10 +1002,7 @@ gst_filesrc_change_state (GstElement * element) break; } - if (GST_ELEMENT_CLASS (parent_class)->change_state) - return GST_ELEMENT_CLASS (parent_class)->change_state (element); - - return GST_STATE_SUCCESS; + return result; } static gboolean @@ -1013,6 +1138,90 @@ error: return FALSE; } +typedef struct +{ + GstFileSrc *src; + guint best_probability; + GstCaps *caps; + + GstBuffer *buffer; +} +FileSrcTypeFind; + +static guint8 * +filesrc_find_peek (gpointer data, gint64 offset, guint size) +{ + FileSrcTypeFind *find; + GstBuffer *buffer; + GstFileSrc *src; + + if (size == 0) + return NULL; + + find = (FileSrcTypeFind *) data; + src = find->src; + + if (offset < 0) { + offset += src->filelen; + } + + buffer = NULL; + gst_filesrc_getrange (src->srcpad, offset, size, &buffer); + + if (find->buffer) { + gst_buffer_unref (find->buffer); + } + find->buffer = buffer; + + return GST_BUFFER_DATA (buffer); +} + +static void +filesrc_find_suggest (gpointer data, guint probability, const GstCaps * caps) +{ + FileSrcTypeFind *find = (FileSrcTypeFind *) data; + + if (probability > find->best_probability) { + gst_caps_replace (&find->caps, gst_caps_copy (caps)); + find->best_probability = probability; + } +} + + +static GstCaps * +gst_filesrc_type_find (GstFileSrc * src) +{ + GstTypeFind gst_find; + FileSrcTypeFind find; + GList *walk, *type_list = NULL; + GstCaps *result = NULL; + + walk = type_list = gst_type_find_factory_get_list (); + + find.src = src; + find.best_probability = 0; + find.caps = NULL; + find.buffer = NULL; + gst_find.data = &find; + gst_find.peek = filesrc_find_peek; + gst_find.suggest = filesrc_find_suggest; + gst_find.get_length = NULL; + + while (walk) { + GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (walk->data); + + gst_type_find_factory_call_function (factory, &gst_find); + if (find.best_probability >= GST_TYPE_FIND_MAXIMUM) + break; + walk = g_list_next (walk); + } + + if (find.best_probability > 0) + result = find.caps; + + return result; +} + /*** GSTURIHANDLER INTERFACE *************************************************/ static guint diff --git a/gst/elements/gstidentity.c b/gst/elements/gstidentity.c index 909f4b9a95..0b8a6d2f7e 100644 --- a/gst/elements/gstidentity.c +++ b/gst/elements/gstidentity.c @@ -1,6 +1,7 @@ /* GStreamer * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans + * 2005 Wim Taymans * * gstidentity.c: * @@ -71,21 +72,28 @@ enum enum { - ARG_0, - ARG_LOOP_BASED, - ARG_SLEEP_TIME, - ARG_DUPLICATE, - ARG_ERROR_AFTER, - ARG_DROP_PROBABILITY, - ARG_DATARATE, - ARG_SILENT, - ARG_LAST_MESSAGE, - ARG_DUMP, - ARG_SYNC, - ARG_CHECK_PERFECT + PROP_0, + PROP_HAS_GETRANGE, + PROP_HAS_CHAIN, + PROP_HAS_SINK_LOOP, + PROP_HAS_SRC_LOOP, + PROP_LOOP_BASED, + PROP_SLEEP_TIME, + PROP_DUPLICATE, + PROP_ERROR_AFTER, + PROP_DROP_PROBABILITY, + PROP_DATARATE, + PROP_SILENT, + PROP_LAST_MESSAGE, + PROP_DUMP, + PROP_SYNC, + PROP_CHECK_PERFECT }; +typedef GstFlowReturn (*IdentityPushFunc) (GstIdentity *, GstBuffer *); + + #define _do_init(bla) \ GST_DEBUG_CATEGORY_INIT (gst_identity_debug, "identity", 0, "identity element"); @@ -99,8 +107,16 @@ static void gst_identity_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static GstElementStateReturn gst_identity_change_state (GstElement * element); -static void gst_identity_chain (GstPad * pad, GstData * _data); +static gboolean gst_identity_event (GstPad * pad, GstEvent * event); +static GstFlowReturn gst_identity_getrange (GstPad * pad, guint64 offset, + guint length, GstBuffer ** buffer); +static GstFlowReturn gst_identity_chain (GstPad * pad, GstBuffer * buffer); +static void gst_identity_src_loop (GstPad * pad); +static void gst_identity_sink_loop (GstPad * pad); +static GstFlowReturn gst_identity_handle_buffer (GstIdentity * identity, + GstBuffer * buf); static void gst_identity_set_clock (GstElement * element, GstClock * clock); +static GstCaps *gst_identity_proxy_getcaps (GstPad * pad); static guint gst_identity_signals[LAST_SIGNAL] = { 0 }; @@ -124,6 +140,9 @@ gst_identity_finalize (GObject * object) identity = GST_IDENTITY (object); + g_mutex_free (identity->pen_lock); + g_cond_free (identity->pen_cond); + g_free (identity->last_message); G_OBJECT_CLASS (parent_class)->finalize (object); @@ -141,42 +160,54 @@ gst_identity_class_init (GstIdentityClass * klass) gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_identity_set_property); gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_identity_get_property); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LOOP_BASED, - g_param_spec_boolean ("loop-based", "Loop-based", - "Set to TRUE to use loop-based rather than chain-based scheduling", - DEFAULT_LOOP_BASED, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SLEEP_TIME, + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HAS_GETRANGE, + g_param_spec_boolean ("has-getrange", "Has getrange", + "If the src pad will implement a getrange function", + TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HAS_CHAIN, + g_param_spec_boolean ("has-chain", "Has chain", + "If the sink pad will implement a chain function", + TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HAS_SRC_LOOP, + g_param_spec_boolean ("has-src-loop", "Has src loop", + "If the src pad will implement a loop function", + FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HAS_SINK_LOOP, + g_param_spec_boolean ("has-sink-loop", "Has sink loop", + "If the sink pad will implement a loop function", + FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SLEEP_TIME, g_param_spec_uint ("sleep-time", "Sleep time", "Microseconds to sleep between processing", 0, G_MAXUINT, DEFAULT_SLEEP_TIME, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DUPLICATE, + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DUPLICATE, g_param_spec_uint ("duplicate", "Duplicate Buffers", "Push the buffers N times", 0, G_MAXUINT, DEFAULT_DUPLICATE, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ERROR_AFTER, + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_ERROR_AFTER, g_param_spec_int ("error_after", "Error After", "Error after N buffers", G_MININT, G_MAXINT, DEFAULT_ERROR_AFTER, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DROP_PROBABILITY, - g_param_spec_float ("drop_probability", "Drop Probability", - "The Probability a buffer is dropped", 0.0, 1.0, + g_object_class_install_property (G_OBJECT_CLASS (klass), + PROP_DROP_PROBABILITY, g_param_spec_float ("drop_probability", + "Drop Probability", "The Probability a buffer is dropped", 0.0, 1.0, DEFAULT_DROP_PROBABILITY, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DATARATE, + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DATARATE, g_param_spec_int ("datarate", "Datarate", "(Re)timestamps buffers with number of bytes per second (0 = inactive)", 0, G_MAXINT, DEFAULT_DATARATE, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SILENT, + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SILENT, g_param_spec_boolean ("silent", "silent", "silent", DEFAULT_SILENT, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LAST_MESSAGE, + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LAST_MESSAGE, g_param_spec_string ("last-message", "last-message", "last-message", NULL, G_PARAM_READABLE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DUMP, + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DUMP, g_param_spec_boolean ("dump", "Dump", "Dump buffer contents", DEFAULT_DUMP, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SYNC, + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SYNC, g_param_spec_boolean ("sync", "Synchronize", "Synchronize to pipeline clock", DEFAULT_SYNC, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_CHECK_PERFECT, + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_CHECK_PERFECT, g_param_spec_boolean ("check-perfect", "Check For Perfect Stream", "Verify that the stream is time- and data-contiguous", DEFAULT_CHECK_PERFECT, G_PARAM_READWRITE)); @@ -202,19 +233,20 @@ gst_identity_init (GstIdentity * identity) gst_pad_new_from_template (gst_static_pad_template_get (&sinktemplate), "sink"); gst_element_add_pad (GST_ELEMENT (identity), identity->sinkpad); - gst_pad_set_chain_function (identity->sinkpad, - GST_DEBUG_FUNCPTR (gst_identity_chain)); - //gst_pad_set_link_function (identity->sinkpad, gst_pad_proxy_pad_link); - gst_pad_set_getcaps_function (identity->sinkpad, gst_pad_proxy_getcaps); + gst_pad_set_getcaps_function (identity->sinkpad, + GST_DEBUG_FUNCPTR (gst_identity_proxy_getcaps)); + gst_pad_set_event_function (identity->sinkpad, + GST_DEBUG_FUNCPTR (gst_identity_event)); identity->srcpad = gst_pad_new_from_template (gst_static_pad_template_get (&srctemplate), "src"); + gst_pad_set_getcaps_function (identity->srcpad, + GST_DEBUG_FUNCPTR (gst_identity_proxy_getcaps)); + gst_pad_set_getrange_function (identity->srcpad, + GST_DEBUG_FUNCPTR (gst_identity_getrange)); gst_element_add_pad (GST_ELEMENT (identity), identity->srcpad); - //gst_pad_set_link_function (identity->srcpad, gst_pad_proxy_pad_link); - gst_pad_set_getcaps_function (identity->srcpad, gst_pad_proxy_getcaps); - identity->loop_based = DEFAULT_LOOP_BASED; identity->sleep_time = DEFAULT_SLEEP_TIME; identity->duplicate = DEFAULT_DUPLICATE; identity->error_after = DEFAULT_ERROR_AFTER; @@ -227,7 +259,10 @@ gst_identity_init (GstIdentity * identity) identity->last_message = NULL; identity->srccaps = NULL; - GST_FLAG_SET (identity, GST_ELEMENT_EVENT_AWARE); + identity->pen_data = NULL; + identity->pen_lock = g_mutex_new (); + identity->pen_cond = g_cond_new (); + identity->pen_flushing = FALSE; } static void @@ -238,36 +273,230 @@ gst_identity_set_clock (GstElement * element, GstClock * clock) gst_object_replace ((GstObject **) & identity->clock, (GstObject *) clock); } +static GstCaps * +gst_identity_proxy_getcaps (GstPad * pad) +{ + GstPad *otherpad; + GstIdentity *identity = GST_IDENTITY (GST_OBJECT_PARENT (pad)); + + otherpad = pad == identity->srcpad ? identity->sinkpad : identity->srcpad; + + return gst_pad_peer_get_caps (otherpad); +} + +static gboolean +identity_queue_push (GstIdentity * identity, GstData * data) +{ + gboolean ret; + + g_mutex_lock (identity->pen_lock); + while (identity->pen_data && !identity->pen_flushing) + g_cond_wait (identity->pen_cond, identity->pen_lock); + if (identity->pen_flushing) { + gst_data_unref (identity->pen_data); + identity->pen_data = NULL; + gst_data_unref (data); + ret = FALSE; + } else { + identity->pen_data = data; + ret = TRUE; + } + g_cond_signal (identity->pen_cond); + g_mutex_unlock (identity->pen_lock); + + return ret; +} + +static GstData * +identity_queue_pop (GstIdentity * identity) +{ + GstData *ret; + + g_mutex_lock (identity->pen_lock); + while (!(ret = identity->pen_data) && !identity->pen_flushing) + g_cond_wait (identity->pen_cond, identity->pen_lock); + g_cond_signal (identity->pen_cond); + g_mutex_unlock (identity->pen_lock); + + return ret; +} static void -gst_identity_chain (GstPad * pad, GstData * _data) +identity_queue_flush (GstIdentity * identity) +{ + g_mutex_lock (identity->pen_lock); + identity->pen_flushing = TRUE; + g_cond_signal (identity->pen_cond); + g_mutex_unlock (identity->pen_lock); +} + +static gboolean +gst_identity_event (GstPad * pad, GstEvent * event) { - GstBuffer *buf = GST_BUFFER (_data); GstIdentity *identity; - guint i; + gboolean ret; - g_return_if_fail (pad != NULL); - g_return_if_fail (GST_IS_PAD (pad)); - g_return_if_fail (buf != NULL); + identity = GST_IDENTITY (GST_PAD_PARENT (pad)); - identity = GST_IDENTITY (gst_pad_get_parent (pad)); + GST_STREAM_LOCK (pad); - if (GST_IS_EVENT (buf)) { - GstEvent *event = GST_EVENT (buf); + if (!identity->silent) { + g_free (identity->last_message); - if (!identity->silent) { - g_free (identity->last_message); + identity->last_message = + g_strdup_printf ("chain ******* (%s:%s)E (type: %d) %p", + GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE (event), event); - identity->last_message = - g_strdup_printf ("chain ******* (%s:%s)E (type: %d) %p", - GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE (event), event); - - g_object_notify (G_OBJECT (identity), "last_message"); - } - gst_pad_event_default (pad, event); - return; + g_object_notify (G_OBJECT (identity), "last_message"); } + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH: + /* forward event */ + gst_pad_event_default (pad, event); + if (GST_EVENT_FLUSH_DONE (event)) { + if (identity->sink_mode == GST_ACTIVATE_PULL) { + /* already have the sink stream lock */ + gst_task_start (GST_RPAD_TASK (identity->sinkpad)); + } + if (identity->src_mode == GST_ACTIVATE_PUSH) { + GST_STREAM_LOCK (identity->srcpad); + gst_task_start (GST_RPAD_TASK (identity->srcpad)); + GST_STREAM_UNLOCK (identity->srcpad); + } + } else { + /* unblock both functions */ + identity_queue_flush (identity); + + } + ret = TRUE; + goto done; + case GST_EVENT_EOS: + if (identity->sink_mode == GST_ACTIVATE_PULL) { + /* already have the sink stream lock */ + gst_task_pause (GST_RPAD_TASK (identity->sinkpad)); + } + break; + default: + break; + } + + if (identity->decoupled) { + ret = identity_queue_push (identity, (GstData *) event); + } else { + ret = gst_pad_push_event (identity->srcpad, event); + } + +done: + GST_STREAM_UNLOCK (pad); + return ret; +} + +static GstFlowReturn +gst_identity_getrange (GstPad * pad, guint64 offset, + guint length, GstBuffer ** buffer) +{ + GstIdentity *identity; + GstFlowReturn ret; + + identity = GST_IDENTITY (GST_PAD_PARENT (pad)); + + GST_STREAM_LOCK (pad); + + ret = gst_pad_pull_range (identity->sinkpad, offset, length, buffer); + + GST_STREAM_UNLOCK (pad); + + return ret; +} + +static GstFlowReturn +gst_identity_chain (GstPad * pad, GstBuffer * buffer) +{ + GstIdentity *identity; + GstFlowReturn ret = GST_FLOW_OK; + + identity = GST_IDENTITY (GST_PAD_PARENT (pad)); + + GST_STREAM_LOCK (pad); + + ret = gst_identity_handle_buffer (identity, buffer); + + GST_STREAM_UNLOCK (pad); + + return ret; +} + +#define DEFAULT_PULL_SIZE 1024 + +static void +gst_identity_sink_loop (GstPad * pad) +{ + GstIdentity *identity; + GstBuffer *buffer; + GstFlowReturn ret; + + identity = GST_IDENTITY (GST_PAD_PARENT (pad)); + + GST_STREAM_LOCK (pad); + + ret = gst_pad_pull_range (pad, identity->offset, DEFAULT_PULL_SIZE, &buffer); + if (ret != GST_FLOW_OK) + goto sink_loop_pause; + + ret = gst_identity_handle_buffer (identity, buffer); + if (ret != GST_FLOW_OK) + goto sink_loop_pause; + + GST_STREAM_UNLOCK (pad); + return; + +sink_loop_pause: + gst_task_pause (GST_RPAD_TASK (identity->sinkpad)); + GST_STREAM_UNLOCK (pad); + return; +} + +static void +gst_identity_src_loop (GstPad * pad) +{ + GstIdentity *identity; + GstData *data; + GstFlowReturn ret; + + identity = GST_IDENTITY (GST_PAD_PARENT (pad)); + + GST_STREAM_LOCK (pad); + + data = identity_queue_pop (identity); + if (!data) /* we're getting flushed */ + goto src_loop_pause; + + if (GST_IS_EVENT (data)) { + if (GST_EVENT_TYPE (data) == GST_EVENT_EOS) + gst_task_pause (GST_RPAD_TASK (identity->srcpad)); + gst_pad_push_event (identity->srcpad, GST_EVENT (data)); + } else { + ret = gst_pad_push (identity->srcpad, (GstBuffer *) data); + if (ret != GST_FLOW_OK) + goto src_loop_pause; + } + + GST_STREAM_UNLOCK (pad); + return; + +src_loop_pause: + gst_task_pause (GST_RPAD_TASK (identity->srcpad)); + GST_STREAM_UNLOCK (pad); + return; +} + +static GstFlowReturn +gst_identity_handle_buffer (GstIdentity * identity, GstBuffer * buf) +{ + GstFlowReturn ret = GST_FLOW_OK; + guint i; + /* see if we need to do perfect stream checking */ /* invalid timestamp drops us out of check. FIXME: maybe warn ? */ if (identity->check_perfect && @@ -303,7 +532,7 @@ gst_identity_chain (GstPad * pad, GstData * _data) gst_buffer_unref (buf); GST_ELEMENT_ERROR (identity, CORE, FAILED, (_("Failed after iterations as requested.")), (NULL)); - return; + return GST_FLOW_ERROR; } } @@ -320,9 +549,10 @@ gst_identity_chain (GstPad * pad, GstData * _data) GST_BUFFER_OFFSET_END (buf), GST_BUFFER_FLAGS (buf), buf); g_object_notify (G_OBJECT (identity), "last-message"); gst_buffer_unref (buf); - return; + return GST_FLOW_OK; } } + if (identity->dump) { gst_util_dump_mem (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf)); } @@ -346,7 +576,7 @@ gst_identity_chain (GstPad * pad, GstData * _data) time = GST_BUFFER_TIMESTAMP (buf); if (identity->datarate > 0) { - time = identity->bytes_handled * GST_SECOND / identity->datarate; + time = identity->offset * GST_SECOND / identity->datarate; GST_BUFFER_TIMESTAMP (buf) = time; GST_BUFFER_DURATION (buf) = @@ -361,41 +591,49 @@ gst_identity_chain (GstPad * pad, GstData * _data) if (identity->sync) { if (identity->clock) { - gst_element_wait (GST_ELEMENT (identity), time); + /* gst_element_wait (GST_ELEMENT (identity), time); */ } } - identity->bytes_handled += GST_BUFFER_SIZE (buf); - gst_pad_push (identity->srcpad, GST_DATA (buf)); + identity->offset += GST_BUFFER_SIZE (buf); + if (identity->decoupled) { + if (!identity_queue_push (identity, (GstData *) buf)) + return GST_FLOW_UNEXPECTED; + } else { + ret = gst_pad_push (identity->srcpad, buf); + if (ret != GST_FLOW_OK) + return ret; + } if (identity->sleep_time) g_usleep (identity->sleep_time); } + + return ret; } static void -gst_identity_loop (GstElement * element) +gst_identity_set_dataflow_funcs (GstIdentity * identity) { - GstIdentity *identity; - GstBuffer *buf; + if (identity->has_getrange) + gst_pad_set_getrange_function (identity->srcpad, gst_identity_getrange); + else + gst_pad_set_getrange_function (identity->srcpad, NULL); - g_return_if_fail (element != NULL); - g_return_if_fail (GST_IS_IDENTITY (element)); + if (identity->has_chain) + gst_pad_set_chain_function (identity->sinkpad, gst_identity_chain); + else + gst_pad_set_chain_function (identity->sinkpad, NULL); - identity = GST_IDENTITY (element); + if (identity->has_src_loop) + gst_pad_set_loop_function (identity->srcpad, gst_identity_src_loop); + else + gst_pad_set_loop_function (identity->srcpad, NULL); - buf = GST_BUFFER (gst_pad_pull (identity->sinkpad)); - if (GST_IS_EVENT (buf)) { - GstEvent *event = GST_EVENT (buf); - - if (GST_EVENT_IS_INTERRUPT (event)) { - gst_event_unref (event); - } else { - gst_pad_event_default (identity->sinkpad, event); - } - } else { - gst_identity_chain (identity->sinkpad, GST_DATA (buf)); - } + if (identity->has_sink_loop) + gst_pad_set_loop_function (identity->sinkpad, gst_identity_sink_loop); + else + gst_pad_set_loop_function (identity->sinkpad, NULL); } static void @@ -404,48 +642,50 @@ gst_identity_set_property (GObject * object, guint prop_id, { GstIdentity *identity; - /* it's not null if we got it, but it might not be ours */ - g_return_if_fail (GST_IS_IDENTITY (object)); - identity = GST_IDENTITY (object); switch (prop_id) { - case ARG_LOOP_BASED: - identity->loop_based = g_value_get_boolean (value); - if (identity->loop_based) { - gst_element_set_loop_function (GST_ELEMENT (identity), - gst_identity_loop); - gst_pad_set_chain_function (identity->sinkpad, NULL); - } else { - gst_pad_set_chain_function (identity->sinkpad, gst_identity_chain); - gst_element_set_loop_function (GST_ELEMENT (identity), NULL); - } + case PROP_HAS_GETRANGE: + identity->has_getrange = g_value_get_boolean (value); + gst_identity_set_dataflow_funcs (identity); break; - case ARG_SLEEP_TIME: + case PROP_HAS_CHAIN: + identity->has_chain = g_value_get_boolean (value); + gst_identity_set_dataflow_funcs (identity); + break; + case PROP_HAS_SRC_LOOP: + identity->has_src_loop = g_value_get_boolean (value); + gst_identity_set_dataflow_funcs (identity); + break; + case PROP_HAS_SINK_LOOP: + identity->has_sink_loop = g_value_get_boolean (value); + gst_identity_set_dataflow_funcs (identity); + break; + case PROP_SLEEP_TIME: identity->sleep_time = g_value_get_uint (value); break; - case ARG_SILENT: + case PROP_SILENT: identity->silent = g_value_get_boolean (value); break; - case ARG_DUPLICATE: + case PROP_DUPLICATE: identity->duplicate = g_value_get_uint (value); break; - case ARG_DUMP: + case PROP_DUMP: identity->dump = g_value_get_boolean (value); break; - case ARG_ERROR_AFTER: + case PROP_ERROR_AFTER: identity->error_after = g_value_get_int (value); break; - case ARG_DROP_PROBABILITY: + case PROP_DROP_PROBABILITY: identity->drop_probability = g_value_get_float (value); break; - case ARG_DATARATE: + case PROP_DATARATE: identity->datarate = g_value_get_int (value); break; - case ARG_SYNC: + case PROP_SYNC: identity->sync = g_value_get_boolean (value); break; - case ARG_CHECK_PERFECT: + case PROP_CHECK_PERFECT: identity->check_perfect = g_value_get_boolean (value); break; default: @@ -460,43 +700,49 @@ gst_identity_get_property (GObject * object, guint prop_id, GValue * value, { GstIdentity *identity; - /* it's not null if we got it, but it might not be ours */ - g_return_if_fail (GST_IS_IDENTITY (object)); - identity = GST_IDENTITY (object); switch (prop_id) { - case ARG_LOOP_BASED: - g_value_set_boolean (value, identity->loop_based); + case PROP_HAS_GETRANGE: + g_value_set_boolean (value, identity->has_getrange); break; - case ARG_SLEEP_TIME: + case PROP_HAS_CHAIN: + g_value_set_boolean (value, identity->has_chain); + break; + case PROP_HAS_SRC_LOOP: + g_value_set_boolean (value, identity->has_src_loop); + break; + case PROP_HAS_SINK_LOOP: + g_value_set_boolean (value, identity->has_sink_loop); + break; + case PROP_SLEEP_TIME: g_value_set_uint (value, identity->sleep_time); break; - case ARG_DUPLICATE: + case PROP_DUPLICATE: g_value_set_uint (value, identity->duplicate); break; - case ARG_ERROR_AFTER: + case PROP_ERROR_AFTER: g_value_set_int (value, identity->error_after); break; - case ARG_DROP_PROBABILITY: + case PROP_DROP_PROBABILITY: g_value_set_float (value, identity->drop_probability); break; - case ARG_DATARATE: + case PROP_DATARATE: g_value_set_int (value, identity->datarate); break; - case ARG_SILENT: + case PROP_SILENT: g_value_set_boolean (value, identity->silent); break; - case ARG_DUMP: + case PROP_DUMP: g_value_set_boolean (value, identity->dump); break; - case ARG_LAST_MESSAGE: + case PROP_LAST_MESSAGE: g_value_set_string (value, identity->last_message); break; - case ARG_SYNC: + case PROP_SYNC: g_value_set_boolean (value, identity->sync); break; - case ARG_CHECK_PERFECT: + case PROP_CHECK_PERFECT: g_value_set_boolean (value, identity->check_perfect); break; default: @@ -518,7 +764,7 @@ gst_identity_change_state (GstElement * element) case GST_STATE_NULL_TO_READY: break; case GST_STATE_READY_TO_PAUSED: - identity->bytes_handled = 0; + identity->offset = 0; identity->prev_timestamp = GST_CLOCK_TIME_NONE; identity->prev_duration = GST_CLOCK_TIME_NONE; identity->prev_offset_end = -1; diff --git a/gst/elements/gstidentity.h b/gst/elements/gstidentity.h index 00203beed5..e187f2e2c2 100644 --- a/gst/elements/gstidentity.h +++ b/gst/elements/gstidentity.h @@ -50,7 +50,19 @@ struct _GstIdentity { GstPad *sinkpad; GstPad *srcpad; - gboolean loop_based; + GstData *pen_data; + GMutex *pen_lock; + GCond *pen_cond; + gboolean pen_flushing; + + gboolean has_chain; + gboolean has_getrange; + gboolean has_src_loop; + gboolean has_sink_loop; + GstActivateMode sink_mode; + GstActivateMode src_mode; + gboolean decoupled; + guint duplicate; gint error_after; gfloat drop_probability; @@ -67,7 +79,7 @@ struct _GstIdentity { gchar *last_message; GstCaps *srccaps; - guint64 bytes_handled; + guint64 offset; }; struct _GstIdentityClass { diff --git a/gst/elements/gsttee.c b/gst/elements/gsttee.c index 94887582b5..cd7213c693 100644 --- a/gst/elements/gsttee.c +++ b/gst/elements/gsttee.c @@ -1,6 +1,7 @@ /* GStreamer * Copyright (C) 1999,2000 Erik Walthinsen - * 2000 Wim Taymans + * 2000,2001,2002,2003,2004,2005 Wim Taymans + * * * gsttee.c: Tee element, one in N out * @@ -40,21 +41,16 @@ GstElementDetails gst_tee_details = GST_ELEMENT_DETAILS ("Tee pipe fitting", "Generic", "1-to-N pipe fitting", "Erik Walthinsen , " - "Wim Taymans "); - -/* Tee signals and args */ -enum -{ - /* FILL ME */ - LAST_SIGNAL -}; + "Wim \"Tim\" Taymans "); enum { - ARG_0, - ARG_SILENT, - ARG_NUM_PADS, - ARG_LAST_MESSAGE + PROP_0, + PROP_NUM_SRC_PADS, + PROP_HAS_SINK_LOOP, + PROP_HAS_CHAIN, + PROP_SILENT, + PROP_LAST_MESSAGE /* FILL ME */ }; @@ -77,7 +73,9 @@ static void gst_tee_set_property (GObject * object, guint prop_id, static void gst_tee_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); -static void gst_tee_chain (GstPad * pad, GstData * _data); +static GstFlowReturn gst_tee_chain (GstPad * pad, GstBuffer * buffer); +static void gst_tee_loop (GstPad * pad); +static gboolean gst_tee_sink_activate (GstPad * pad, GstActivateMode mode); static void @@ -113,22 +111,26 @@ gst_tee_class_init (GstTeeClass * klass) gobject_class = (GObjectClass *) klass; gstelement_class = (GstElementClass *) klass; + gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_tee_finalize); gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_tee_set_property); gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_tee_get_property); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_NUM_PADS, - g_param_spec_int ("num_pads", "num_pads", "num_pads", + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_NUM_SRC_PADS, + g_param_spec_int ("num-src-pads", "num-src-pads", "num-src-pads", 0, G_MAXINT, 0, G_PARAM_READABLE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SILENT, + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HAS_SINK_LOOP, + g_param_spec_boolean ("has-sink-loop", "has-sink-loop", "has-sink-loop", + FALSE, G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HAS_CHAIN, + g_param_spec_boolean ("has-chain", "has-chain", "has-chain", + TRUE, G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SILENT, g_param_spec_boolean ("silent", "silent", "silent", TRUE, G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LAST_MESSAGE, + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LAST_MESSAGE, g_param_spec_string ("last_message", "last_message", "last_message", NULL, G_PARAM_READABLE)); - - gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_tee_finalize); - gstelement_class->request_new_pad = GST_DEBUG_FUNCPTR (gst_tee_request_new_pad); } @@ -140,72 +142,30 @@ gst_tee_init (GstTee * tee) gst_pad_new_from_template (gst_static_pad_template_get (&sinktemplate), "sink"); gst_element_add_pad (GST_ELEMENT (tee), tee->sinkpad); - gst_pad_set_chain_function (tee->sinkpad, GST_DEBUG_FUNCPTR (gst_tee_chain)); - //gst_pad_set_link_function (tee->sinkpad, GST_DEBUG_FUNCPTR (gst_pad_proxy_pad_link)); + gst_pad_set_setcaps_function (tee->sinkpad, + GST_DEBUG_FUNCPTR (gst_pad_proxy_setcaps)); gst_pad_set_getcaps_function (tee->sinkpad, GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps)); tee->last_message = NULL; } -/* helper compare function */ -gint -name_pad_compare (gconstpointer a, gconstpointer b) +static void +gst_tee_update_pad_functions (GstTee * tee) { - GstPad *pad = (GstPad *) a; - gchar *name = (gchar *) b; + gst_pad_set_activate_function (tee->sinkpad, + GST_DEBUG_FUNCPTR (gst_tee_sink_activate)); - g_assert (GST_IS_PAD (pad)); + if (tee->has_chain) + gst_pad_set_chain_function (tee->sinkpad, + GST_DEBUG_FUNCPTR (gst_tee_chain)); + else + gst_pad_set_chain_function (tee->sinkpad, NULL); - return strcmp (name, gst_pad_get_name (pad)); /* returns 0 if match */ -} - -static GstCaps * -gst_tee_getcaps (GstPad * _pad) -{ - GstTee *tee = GST_TEE (gst_pad_get_parent (_pad)); - GstCaps *caps = gst_caps_new_any (), *tmp, *res; - GstPad *pad; - const GList *pads; - - for (pads = GST_ELEMENT (tee)->pads; pads != NULL; pads = pads->next) { - pad = GST_PAD (pads->data); - if (pad == _pad) - continue; - - tmp = gst_pad_get_allowed_caps (pad); - res = gst_caps_intersect (caps, tmp); - gst_caps_unref (tmp); - gst_caps_unref (caps); - caps = res; - } - - return caps; -} - -static GstPadLinkReturn -gst_tee_link (GstPad * _pad, const GstCaps * caps) -{ - GstTee *tee = GST_TEE (gst_pad_get_parent (_pad)); - GstPadLinkReturn res; - GstPad *pad; - const GList *pads; - - GST_DEBUG_OBJECT (tee, "Forwarding link to all other pads"); - - for (pads = GST_ELEMENT (tee)->pads; pads != NULL; pads = pads->next) { - pad = GST_PAD (pads->data); - if (pad == _pad) - continue; - - res = gst_pad_try_set_caps (pad, caps); - GST_DEBUG_OBJECT (tee, "Pad %s:%s gave response %d", - GST_DEBUG_PAD_NAME (pad), res); - if (GST_PAD_LINK_FAILED (res)) - return res; - } - - return GST_PAD_LINK_OK; + if (tee->has_sink_loop) + gst_pad_set_loop_function (tee->sinkpad, GST_DEBUG_FUNCPTR (gst_tee_loop)); + else + gst_pad_set_loop_function (tee->sinkpad, NULL); } static GstPad * @@ -215,48 +175,21 @@ gst_tee_request_new_pad (GstElement * element, GstPadTemplate * templ, gchar *name; GstPad *srcpad; GstTee *tee; - gint i = 0; - const GList *pads; - - g_return_val_if_fail (GST_IS_TEE (element), NULL); - - if (templ->direction != GST_PAD_SRC) { - g_warning ("gsttee: request new pad that is not a SRC pad\n"); - return NULL; - } tee = GST_TEE (element); - /* try names in order and find one that's not in use atm */ - pads = element->pads; - - name = NULL; - while (!name) { - name = g_strdup_printf ("src%d", i); - if (g_list_find_custom ((GList *) pads, (gconstpointer) name, - name_pad_compare) != NULL) { - /* this name is taken, use the next one */ - ++i; - g_free (name); - name = NULL; - } - } - if (!tee->silent) { - g_free (tee->last_message); - tee->last_message = g_strdup_printf ("new pad %s", name); - g_object_notify (G_OBJECT (tee), "last_message"); - } + GST_LOCK (tee); + name = g_strdup_printf ("src%d", tee->pad_counter++); + GST_UNLOCK (tee); srcpad = gst_pad_new_from_template (templ, name); g_free (name); - gst_pad_set_link_function (srcpad, GST_DEBUG_FUNCPTR (gst_tee_link)); - gst_pad_set_getcaps_function (srcpad, GST_DEBUG_FUNCPTR (gst_tee_getcaps)); - gst_element_add_pad (GST_ELEMENT (tee), srcpad); - GST_PAD_ELEMENT_PRIVATE (srcpad) = NULL; - if (GST_PAD_CAPS (tee->sinkpad)) { - gst_pad_try_set_caps (srcpad, GST_PAD_CAPS (tee->sinkpad)); - } + gst_pad_set_setcaps_function (srcpad, + GST_DEBUG_FUNCPTR (gst_pad_proxy_setcaps)); + gst_pad_set_getcaps_function (srcpad, + GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps)); + gst_element_add_pad (GST_ELEMENT (tee), srcpad); return srcpad; } @@ -265,95 +198,202 @@ static void gst_tee_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { - GstTee *tee; - - /* it's not null if we got it, but it might not be ours */ - g_return_if_fail (GST_IS_TEE (object)); - - tee = GST_TEE (object); + GstTee *tee = GST_TEE (object); + GST_LOCK (tee); switch (prop_id) { - case ARG_SILENT: + case PROP_HAS_SINK_LOOP: + tee->has_sink_loop = g_value_get_boolean (value); + gst_tee_update_pad_functions (tee); + break; + case PROP_HAS_CHAIN: + tee->has_chain = g_value_get_boolean (value); + gst_tee_update_pad_functions (tee); + break; + case PROP_SILENT: tee->silent = g_value_get_boolean (value); - g_object_notify (G_OBJECT (tee), "silent"); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } + GST_UNLOCK (tee); } static void gst_tee_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { - GstTee *tee; - - /* it's not null if we got it, but it might not be ours */ - g_return_if_fail (GST_IS_TEE (object)); - - tee = GST_TEE (object); + GstTee *tee = GST_TEE (object); + GST_LOCK (tee); switch (prop_id) { - case ARG_NUM_PADS: + case PROP_NUM_SRC_PADS: g_value_set_int (value, GST_ELEMENT (tee)->numsrcpads); break; - case ARG_SILENT: + case PROP_HAS_SINK_LOOP: + g_value_set_boolean (value, tee->has_sink_loop); + break; + case PROP_HAS_CHAIN: + g_value_set_boolean (value, tee->has_chain); + break; + case PROP_SILENT: g_value_set_boolean (value, tee->silent); break; - case ARG_LAST_MESSAGE: + case PROP_LAST_MESSAGE: g_value_set_string (value, tee->last_message); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } + GST_UNLOCK (tee); } -/** - * gst_tee_chain: - * @pad: the pad to follow - * @buf: the buffer to pass - * - * Chain a buffer on a pad. - */ -static void -gst_tee_chain (GstPad * pad, GstData * _data) +typedef struct { - GstBuffer *buf = GST_BUFFER (_data); GstTee *tee; - const GList *pads; + GstBuffer *buffer; +} PushData; - g_return_if_fail (pad != NULL); - g_return_if_fail (GST_IS_PAD (pad)); - g_return_if_fail (buf != NULL); +static gboolean +gst_tee_do_push (GstPad * pad, GValue * ret, PushData * data) +{ + GstFlowReturn res; + + if (GST_PAD_DIRECTION (pad) != GST_PAD_SRC || !GST_PAD_IS_USABLE (pad)) + return TRUE; + + if (G_UNLIKELY (!data->tee->silent)) { + GstTee *tee = data->tee; + GstBuffer *buf = data->buffer; + + g_free (tee->last_message); + tee->last_message = + g_strdup_printf ("chain ******* (%s:%s)t (%d bytes, %" + G_GUINT64_FORMAT ") %p", GST_DEBUG_PAD_NAME (pad), + GST_BUFFER_SIZE (buf), GST_BUFFER_TIMESTAMP (buf), buf); + g_object_notify (G_OBJECT (tee), "last_message"); + } + + res = gst_pad_push (pad, gst_buffer_ref (data->buffer)); + g_value_set_enum (ret, res); + return (res == GST_FLOW_OK); +} + +static GstFlowReturn +gst_tee_handle_buffer (GstTee * tee, GstBuffer * buffer) +{ + GstIterator *iter; + PushData data; + GValue ret = { 0, }; + GstIteratorResult res; + + tee->offset += GST_BUFFER_SIZE (buffer); + + g_value_init (&ret, GST_TYPE_FLOW_RETURN); + iter = gst_element_iterate_pads (GST_ELEMENT (tee)); + data.tee = tee; + data.buffer = buffer; + + res = gst_iterator_fold (iter, (GstIteratorFoldFunction) gst_tee_do_push, + &ret, &data); + gst_iterator_free (iter); + + gst_buffer_unref (buffer); + + /* no need to unset gvalue */ + return g_value_get_enum (&ret); +} + +#define WITH_STREAM_LOCK(pad, expr) G_STMT_START {\ + GST_STREAM_LOCK (pad); \ + expr; \ + GST_STREAM_UNLOCK (pad); \ +}G_STMT_END + +static GstFlowReturn +gst_tee_chain (GstPad * pad, GstBuffer * buffer) +{ + GstFlowReturn res; + GstTee *tee; tee = GST_TEE (gst_pad_get_parent (pad)); - gst_buffer_ref_by_count (buf, GST_ELEMENT (tee)->numsrcpads - 1); + WITH_STREAM_LOCK (pad, res = gst_tee_handle_buffer (tee, buffer)); - pads = GST_ELEMENT (tee)->pads; - - while (pads) { - GstPad *outpad = GST_PAD (pads->data); - - pads = g_list_next (pads); - - if (GST_PAD_DIRECTION (outpad) != GST_PAD_SRC) - continue; - - if (!tee->silent) { - g_free (tee->last_message); - tee->last_message = - g_strdup_printf ("chain ******* (%s:%s)t (%d bytes, %" - G_GUINT64_FORMAT ") %p", GST_DEBUG_PAD_NAME (outpad), - GST_BUFFER_SIZE (buf), GST_BUFFER_TIMESTAMP (buf), buf); - g_object_notify (G_OBJECT (tee), "last_message"); - } - - if (GST_PAD_IS_USABLE (outpad)) - gst_pad_push (outpad, GST_DATA (buf)); - else - gst_buffer_unref (buf); - } + return res; +} + +#define DEFAULT_SIZE 1024 + +static void +gst_tee_loop (GstPad * pad) +{ + GstBuffer *buffer; + GstFlowReturn res; + GstTee *tee; + + GST_STREAM_LOCK (pad); + + tee = GST_TEE (gst_pad_get_parent (pad)); + + res = gst_pad_pull_range (pad, tee->offset, DEFAULT_SIZE, &buffer); + if (res != GST_FLOW_OK) + goto pause_task; + + res = gst_tee_handle_buffer (tee, buffer); + if (res != GST_FLOW_OK) + goto pause_task; + + GST_STREAM_UNLOCK (pad); + return; + +pause_task: + gst_pad_pause_task (pad); + GST_STREAM_UNLOCK (pad); + return; +} + +static gboolean +gst_tee_sink_activate (GstPad * pad, GstActivateMode mode) +{ + gboolean result = FALSE; + GstTee *tee; + + tee = GST_TEE (GST_OBJECT_PARENT (pad)); + + switch (mode) { + case GST_ACTIVATE_PUSH: + g_return_val_if_fail (tee->has_chain, FALSE); + result = TRUE; + break; + case GST_ACTIVATE_PULL: + g_return_val_if_fail (tee->has_sink_loop, FALSE); + if (GST_ELEMENT_SCHEDULER (tee)) { + GST_STREAM_LOCK (pad); + GST_RPAD_TASK (pad) = + gst_scheduler_create_task (GST_ELEMENT_SCHEDULER (tee), + (GstTaskFunction) gst_tee_loop, pad); + + gst_pad_start_task (pad); + GST_STREAM_UNLOCK (pad); + result = TRUE; + } + break; + case GST_ACTIVATE_NONE: + GST_STREAM_LOCK (pad); + if (GST_RPAD_TASK (pad)) { + gst_pad_stop_task (pad); + gst_object_unref (GST_OBJECT (GST_RPAD_TASK (pad))); + GST_RPAD_TASK (pad) = NULL; + } + GST_STREAM_UNLOCK (pad); + + result = TRUE; + break; + } + tee->sink_mode = mode; + + return result; } diff --git a/gst/elements/gsttee.h b/gst/elements/gsttee.h index f28b09d9d5..a575403205 100644 --- a/gst/elements/gsttee.h +++ b/gst/elements/gsttee.h @@ -49,6 +49,12 @@ struct _GstTee { GstPad *sinkpad; gboolean silent; + gboolean has_chain; + gboolean has_sink_loop; + gint pad_counter; + guint64 offset; + GstActivateMode sink_mode; + gchar *last_message; }; diff --git a/gst/gst.c b/gst/gst.c index 5000685568..53b1c27aa5 100644 --- a/gst/gst.c +++ b/gst/gst.c @@ -535,9 +535,8 @@ gst_register_core_elements (GstPlugin * plugin) GST_TYPE_BIN) || !gst_element_register (plugin, "pipeline", GST_RANK_PRIMARY, GST_TYPE_PIPELINE) || - !gst_element_register (plugin, "thread", GST_RANK_PRIMARY, - GST_TYPE_THREAD) || - !gst_element_register (plugin, "queue", GST_RANK_NONE, GST_TYPE_QUEUE)) + !gst_element_register (plugin, "queue", GST_RANK_NONE, GST_TYPE_QUEUE) + ) g_assert_not_reached (); return TRUE; @@ -617,6 +616,7 @@ init_post (void) _gst_plugin_initialize (); _gst_event_initialize (); _gst_buffer_initialize (); + _gst_message_initialize (); _gst_tag_initialize (); #ifndef GST_DISABLE_REGISTRY diff --git a/gst/gst.h b/gst/gst.h index 9bb8602013..ae2ca5c924 100644 --- a/gst/gst.h +++ b/gst/gst.h @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -53,7 +54,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/gst/gstbin.c b/gst/gstbin.c index c433befb7d..830beac3bc 100644 --- a/gst/gstbin.c +++ b/gst/gstbin.c @@ -52,13 +52,11 @@ static GstElementDetails gst_bin_details = GST_ELEMENT_DETAILS ("Generic bin", GType _gst_bin_type = 0; -static gboolean _gst_boolean_did_something_accumulator (GSignalInvocationHint * - ihint, GValue * return_accu, const GValue * handler_return, gpointer dummy); - static void gst_bin_dispose (GObject * object); static GstElementStateReturn gst_bin_change_state (GstElement * element); -static GstElementStateReturn gst_bin_change_state_norecurse (GstBin * bin); +static GstElementStateReturn gst_bin_get_state (GstElement * element, + GstElementState * state, GstElementState * pending, GTimeVal * timeout); static gboolean gst_bin_add_func (GstBin * bin, GstElement * element); static gboolean gst_bin_remove_func (GstBin * bin, GstElement * element); @@ -69,12 +67,9 @@ static void gst_bin_set_index_func (GstElement * element, GstIndex * index); static GstClock *gst_bin_get_clock_func (GstElement * element); static void gst_bin_set_clock_func (GstElement * element, GstClock * clock); -static void gst_bin_child_state_change_func (GstBin * bin, - GstElementState oldstate, GstElementState newstate, GstElement * child); -GstElementStateReturn gst_bin_set_state (GstElement * element, - GstElementState state); +static void gst_bin_set_bus (GstElement * element, GstBus * bus); +static void gst_bin_set_scheduler (GstElement * element, GstScheduler * sched); -static gboolean gst_bin_iterate_func (GstBin * bin); #ifndef GST_DISABLE_LOADSAVE static xmlNodePtr gst_bin_save_thyself (GstObject * object, xmlNodePtr parent); @@ -86,7 +81,6 @@ enum { ELEMENT_ADDED, ELEMENT_REMOVED, - ITERATE, LAST_SIGNAL }; @@ -163,11 +157,6 @@ gst_bin_class_init (GstBinClass * klass) g_signal_new ("element-removed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GstBinClass, element_removed), NULL, NULL, gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_ELEMENT); - gst_bin_signals[ITERATE] = - g_signal_new ("iterate", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GstBinClass, iterate), - _gst_boolean_did_something_accumulator, NULL, gst_marshal_BOOLEAN__VOID, - G_TYPE_BOOLEAN, 0); gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_bin_dispose); @@ -178,34 +167,17 @@ gst_bin_class_init (GstBinClass * klass) #endif gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_bin_change_state); - gstelement_class->set_state = GST_DEBUG_FUNCPTR (gst_bin_set_state); + gstelement_class->get_state = GST_DEBUG_FUNCPTR (gst_bin_get_state); #ifndef GST_DISABLE_INDEX gstelement_class->set_index = GST_DEBUG_FUNCPTR (gst_bin_set_index_func); #endif gstelement_class->get_clock = GST_DEBUG_FUNCPTR (gst_bin_get_clock_func); gstelement_class->set_clock = GST_DEBUG_FUNCPTR (gst_bin_set_clock_func); + gstelement_class->set_bus = GST_DEBUG_FUNCPTR (gst_bin_set_bus); + gstelement_class->set_scheduler = GST_DEBUG_FUNCPTR (gst_bin_set_scheduler); klass->add_element = GST_DEBUG_FUNCPTR (gst_bin_add_func); klass->remove_element = GST_DEBUG_FUNCPTR (gst_bin_remove_func); - - klass->child_state_change = - GST_DEBUG_FUNCPTR (gst_bin_child_state_change_func); - klass->iterate = GST_DEBUG_FUNCPTR (gst_bin_iterate_func); -} - -static gboolean -_gst_boolean_did_something_accumulator (GSignalInvocationHint * ihint, - GValue * return_accu, const GValue * handler_return, gpointer dummy) -{ - gboolean did_something; - - did_something = g_value_get_boolean (handler_return); - if (did_something) { - g_value_set_boolean (return_accu, TRUE); - } - - /* always continue emission */ - return TRUE; } static void @@ -300,130 +272,50 @@ gst_bin_get_clock_func (GstElement * element) return result; } -/* will be removed */ +/* set the bus on all of the children in this bin + * + * MT safe + */ static void -gst_bin_set_element_sched (GstElement * element, GstScheduler * sched) +gst_bin_set_bus (GstElement * element, GstBus * bus) { - GST_CAT_LOG (GST_CAT_SCHEDULING, "setting element \"%s\" sched to %p", - GST_ELEMENT_NAME (element), sched); + GList *children; + GstBin *bin; - /* if it's actually a Bin */ - if (GST_IS_BIN (element)) { - if (GST_FLAG_IS_SET (element, GST_BIN_FLAG_MANAGER)) { - GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, element, - "child is already a manager, not resetting sched"); - if (GST_ELEMENT_SCHEDULER (element)) - gst_scheduler_add_scheduler (sched, GST_ELEMENT_SCHEDULER (element)); - return; - } + bin = GST_BIN (element); - GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, element, - "setting child bin's scheduler to be the same as the parent's"); - gst_scheduler_add_element (sched, element); + parent_class->set_bus (element, bus); - /* set the children's schedule */ - g_list_foreach (GST_BIN (element)->children, - (GFunc) gst_bin_set_element_sched, sched); - } else { - /* otherwise, if it's just a regular old element */ - GList *pads; + GST_LOCK (bin); + for (children = bin->children; children; children = g_list_next (children)) { + GstElement *child = GST_ELEMENT (children->data); - gst_scheduler_add_element (sched, element); - - if (!GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED)) { - /* set the sched pointer in all the pads */ - pads = element->pads; - while (pads) { - GstPad *pad; - - pad = GST_PAD (pads->data); - pads = g_list_next (pads); - - /* we only operate on real pads */ - if (!GST_IS_REAL_PAD (pad)) - continue; - - /* if the peer element exists and is a candidate */ - if (GST_PAD_PEER (pad)) { - if (gst_pad_get_scheduler (GST_PAD_PEER (pad)) == sched) { - GST_CAT_LOG (GST_CAT_SCHEDULING, - "peer is in same scheduler, telling scheduler"); - - if (GST_PAD_IS_SRC (pad)) - gst_scheduler_pad_link (sched, pad, GST_PAD_PEER (pad)); - else - gst_scheduler_pad_link (sched, GST_PAD_PEER (pad), pad); - } - } - } - } + gst_element_set_bus (child, bus); } + GST_UNLOCK (bin); } -/* will be removed */ +/* set the scheduler on all of the children in this bin + * + * MT safe + */ static void -gst_bin_unset_element_sched (GstElement * element, GstScheduler * sched) +gst_bin_set_scheduler (GstElement * element, GstScheduler * sched) { - if (GST_ELEMENT_SCHEDULER (element) == NULL) { - GST_CAT_DEBUG (GST_CAT_SCHEDULING, "element \"%s\" has no scheduler", - GST_ELEMENT_NAME (element)); - return; - } - - GST_CAT_DEBUG (GST_CAT_SCHEDULING, - "removing element \"%s\" from its sched %p", GST_ELEMENT_NAME (element), - GST_ELEMENT_SCHEDULER (element)); - - /* if it's actually a Bin */ - if (GST_IS_BIN (element)) { - - if (GST_FLAG_IS_SET (element, GST_BIN_FLAG_MANAGER)) { - GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, element, - "child is already a manager, not unsetting sched"); - if (sched) { - gst_scheduler_remove_scheduler (sched, GST_ELEMENT_SCHEDULER (element)); - } - return; - } - /* for each child, remove them from their schedule */ - g_list_foreach (GST_BIN (element)->children, - (GFunc) gst_bin_unset_element_sched, sched); - - gst_scheduler_remove_element (GST_ELEMENT_SCHEDULER (element), element); - } else { - /* otherwise, if it's just a regular old element */ - GList *pads; - - if (!GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED)) { - /* unset the sched pointer in all the pads */ - pads = element->pads; - while (pads) { - GstPad *pad; - - pad = GST_PAD (pads->data); - pads = g_list_next (pads); - - /* we only operate on real pads */ - if (!GST_IS_REAL_PAD (pad)) - continue; - - /* if the peer element exists and is a candidate */ - if (GST_PAD_PEER (pad)) { - if (gst_pad_get_scheduler (GST_PAD_PEER (pad)) == sched) { - GST_CAT_LOG (GST_CAT_SCHEDULING, - "peer is in same scheduler, telling scheduler"); - - if (GST_PAD_IS_SRC (pad)) - gst_scheduler_pad_unlink (sched, pad, GST_PAD_PEER (pad)); - else - gst_scheduler_pad_unlink (sched, GST_PAD_PEER (pad), pad); - } - } - } - } - - gst_scheduler_remove_element (GST_ELEMENT_SCHEDULER (element), element); + GList *children; + GstBin *bin; + + bin = GST_BIN (element); + + parent_class->set_scheduler (element, sched); + + GST_LOCK (bin); + for (children = bin->children; children; children = g_list_next (children)) { + GstElement *child = GST_ELEMENT (children->data); + + gst_element_set_scheduler (child, sched); } + GST_UNLOCK (bin); } /* add an element to this bin @@ -462,6 +354,8 @@ gst_bin_add_func (GstBin * bin, GstElement * element) bin->numchildren++; bin->children_cookie++; + gst_element_set_manager (element, GST_ELEMENT (bin)->manager); + gst_element_set_bus (element, GST_ELEMENT (bin)->bus); gst_element_set_scheduler (element, GST_ELEMENT_SCHEDULER (bin)); GST_UNLOCK (bin); @@ -571,6 +465,8 @@ gst_bin_remove_func (GstBin * bin, GstElement * element) elem_name); g_free (elem_name); + gst_element_set_manager (element, NULL); + gst_element_set_bus (element, NULL); gst_element_set_scheduler (element, NULL); /* we ref here because after the _unparent() the element can be disposed @@ -837,290 +733,38 @@ gst_bin_iterate_sinks (GstBin * bin) return result; } -/** - * gst_bin_child_state_change: - * @bin: #GstBin with the child - * @oldstate: The old child state - * @newstate: The new child state - * @child: #GstElement that signaled an changed state +/* this functions loops over all children, as soon as one does + * not return SUCCESS, we return that value. * - * An internal function to inform the parent bin about a state change - * of a child. - * - * Marked for removal. + * MT safe */ -void -gst_bin_child_state_change (GstBin * bin, GstElementState oldstate, - GstElementState newstate, GstElement * child) +static GstElementStateReturn +gst_bin_get_state (GstElement * element, GstElementState * state, + GstElementState * pending, GTimeVal * timeout) { - GstBinClass *bclass; - - g_return_if_fail (GST_IS_BIN (bin)); - g_return_if_fail (GST_IS_ELEMENT (child)); - - GST_CAT_LOG (GST_CAT_STATES, "child %s changed state in bin %s from %s to %s", - GST_ELEMENT_NAME (child), GST_ELEMENT_NAME (bin), - gst_element_state_get_name (oldstate), - gst_element_state_get_name (newstate)); - - bclass = GST_BIN_GET_CLASS (bin); - - if (bclass->child_state_change) { - bclass->child_state_change (bin, oldstate, newstate, child); - } else { - g_warning ("cannot signal state change of child %s to bin %s\n", - GST_ELEMENT_NAME (child), GST_ELEMENT_NAME (bin)); - } + /* implement me */ + return GST_STATE_FAILURE; } -/* will be removed */ -static void -gst_bin_child_state_change_func (GstBin * bin, GstElementState oldstate, - GstElementState newstate, GstElement * child) -{ - GstElementState old = 0, new = 0; - gint old_idx = 0, new_idx = 0, i; - - old = oldstate; - new = newstate; - while ((old >>= 1) != 0) - old_idx++; - while ((new >>= 1) != 0) - new_idx++; - - GST_LOCK (bin); - GST_LOG_BIN_CONTENTS (bin, "before child state change"); - bin->child_states[old_idx]--; - bin->child_states[new_idx]++; - - for (i = GST_NUM_STATES - 1; i >= 0; i--) { - if (bin->child_states[i] != 0) { - gint state = (1 << i); - - /* We only change state on the parent if the state is not locked. - * State locking can occur if the bin itself set state on children, - * which should not recurse since it leads to infinite loops. */ - if (GST_STATE (bin) != state && - !GST_FLAG_IS_SET (bin, GST_BIN_STATE_LOCKED)) { - GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin, - "highest child state is %s, changing bin state accordingly", - gst_element_state_get_name (state)); - GST_STATE_PENDING (bin) = state; - GST_UNLOCK (bin); - gst_bin_change_state_norecurse (bin); - if (state != GST_STATE (bin)) { - g_warning ("%s: state change in callback %d %d", - GST_ELEMENT_NAME (bin), state, GST_STATE (bin)); - } - GST_LOG_BIN_CONTENTS (bin, "after child state change"); - return; - } - break; - } - } - GST_LOG_BIN_CONTENTS (bin, "after child state change"); - GST_UNLOCK (bin); -} - -typedef gboolean (*GstBinForeachFunc) (GstBin * bin, GstElement * element, - gpointer data); - -/* - * gst_bin_foreach: - * @bin: bin to traverse - * @func: function to call on each child - * @data: user data handed to each function call +/* this function is called with the STATE_LOCK held. It works + * as follows: * - * Calls @func on each child of the bin. If @func returns FALSE, - * gst_bin_foreach() immediately returns. - * It is assumed that calling @func may alter the set of @bin's children. @func - * will only be called on children that were in @bin when gst_bin_foreach() was - * called, and that are still in @bin when the child is reached. + * 1) put all sink elements on the queue. + * 2) change state of elements in queue, put linked elements to queue. + * 3) while queue not empty goto 2) * - * Returns: TRUE if @func always returned TRUE, FALSE otherwise + * This will effectively change the state of all elements in the bin + * from the sinks to the sources. We have to change the states this + * way so that when a source element pushes data, the downstream element + * is in the right state to receive the data. * - * Marked for removal. - **/ -static gboolean -gst_bin_foreach (GstBin * bin, GstBinForeachFunc func, gpointer data) -{ - GList *kids, *walk; - - g_return_val_if_fail (GST_IS_BIN (bin), FALSE); - g_return_val_if_fail (func != NULL, FALSE); - - kids = g_list_copy (bin->children); - - for (walk = kids; walk; walk = g_list_next (walk)) { - GstElement *element = (GstElement *) walk->data; - - if (g_list_find (bin->children, element)) { - gboolean res = func (bin, element, data); - - if (!res) { - g_list_free (kids); - return FALSE; - } - } - } - - g_list_free (kids); - return TRUE; -} - -typedef struct -{ - GstElementState pending; - GstElementStateReturn result; -} -SetKidStateData; -static int -set_kid_state_func (GstBin * bin, GstElement * child, gpointer user_data) -{ - GstElementState old_child_state; - SetKidStateData *data = user_data; - - if (GST_FLAG_IS_SET (child, GST_ELEMENT_LOCKED_STATE)) { - return TRUE; - } - - old_child_state = GST_STATE (child); - - GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, bin, - "changing state of child %s from current %s to pending %s", - GST_ELEMENT_NAME (child), gst_element_state_get_name (old_child_state), - gst_element_state_get_name (data->pending)); - - switch (gst_element_set_state (child, data->pending)) { - case GST_STATE_FAILURE: - GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin, - "child '%s' failed to go to state %d(%s)", - GST_ELEMENT_NAME (child), - data->pending, gst_element_state_get_name (data->pending)); - - gst_element_set_state (child, old_child_state); - return FALSE; /* error out to the caller */ - - case GST_STATE_ASYNC: - GST_CAT_INFO_OBJECT (GST_CAT_STATES, bin, - "child '%s' is changing state asynchronously", - GST_ELEMENT_NAME (child)); - data->result = GST_STATE_ASYNC; - return TRUE; - - case GST_STATE_SUCCESS: - GST_CAT_DEBUG (GST_CAT_STATES, - "child '%s' changed state to %d(%s) successfully", - GST_ELEMENT_NAME (child), data->pending, - gst_element_state_get_name (data->pending)); - return TRUE; - - default: - g_assert_not_reached (); - return FALSE; /* satisfy gcc */ - } -} - + * MT safe. + */ static GstElementStateReturn gst_bin_change_state (GstElement * element) { - GstBin *bin; - GstElementStateReturn ret; - GstElementState old_state, pending; - - g_return_val_if_fail (GST_IS_BIN (element), GST_STATE_FAILURE); - - bin = GST_BIN (element); - - old_state = GST_STATE (element); - pending = GST_STATE_PENDING (element); - - GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, - "changing state of children from %s to %s", - gst_element_state_get_name (old_state), - gst_element_state_get_name (pending)); - - if (pending == GST_STATE_VOID_PENDING) - return GST_STATE_SUCCESS; - - /* If we're changing state non-recursively (see _norecurse()), - * this flag is already set and we should not set children states. */ - if (!GST_FLAG_IS_SET (bin, GST_BIN_STATE_LOCKED)) { - SetKidStateData data; - - /* So now we use this flag to make sure that kids don't re-set our - * state, which would lead to infinite loops. */ - GST_FLAG_SET (bin, GST_BIN_STATE_LOCKED); - data.pending = pending; - data.result = GST_STATE_SUCCESS; - if (!gst_bin_foreach (bin, set_kid_state_func, &data)) { - GST_FLAG_UNSET (bin, GST_BIN_STATE_LOCKED); - GST_STATE_PENDING (element) = old_state; - return GST_STATE_FAILURE; - } - GST_FLAG_UNSET (bin, GST_BIN_STATE_LOCKED); - - GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, - "done changing bin's state from %s to %s, now in %s", - gst_element_state_get_name (old_state), - gst_element_state_get_name (pending), - gst_element_state_get_name (GST_STATE (element))); - - /* if we're async, the kids will change state later (when the - * lock-state flag is no longer held) and all will be fine. */ - if (data.result == GST_STATE_ASYNC) - return GST_STATE_ASYNC; - } else { - GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, - "Not recursing state change onto children"); - } - - /* FIXME: this should have been done by the children already, no? */ - if (parent_class->change_state) { - ret = parent_class->change_state (element); - } else { - ret = GST_STATE_SUCCESS; - } - return ret; -} - -GstElementStateReturn -gst_bin_set_state (GstElement * element, GstElementState state) -{ - GstBin *bin = GST_BIN (element); - - if (GST_STATE (bin) == state) { - SetKidStateData data; - - data.pending = state; - data.result = GST_STATE_SUCCESS; - if (!gst_bin_foreach (bin, set_kid_state_func, &data)) { - return GST_STATE_FAILURE; - } else { - return data.result; - } - } else { - return GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS, set_state, (element, - state), GST_STATE_FAILURE); - } -} - -static GstElementStateReturn -gst_bin_change_state_norecurse (GstBin * bin) -{ - GstElementStateReturn ret; - - if (GST_ELEMENT_GET_CLASS (bin)->change_state) { - GST_CAT_LOG_OBJECT (GST_CAT_STATES, bin, "setting bin's own state"); - - /* Non-recursive state change flag */ - GST_FLAG_SET (bin, GST_BIN_STATE_LOCKED); - ret = GST_ELEMENT_GET_CLASS (bin)->change_state (GST_ELEMENT (bin)); - GST_FLAG_UNSET (bin, GST_BIN_STATE_LOCKED); - - return ret; - } else - return GST_STATE_FAILURE; + /* implement me */ + return GST_STATE_FAILURE; } static void @@ -1303,58 +947,6 @@ gst_bin_iterate_all_by_interface (GstBin * bin, GType interface) return result; } -/** - * gst_bin_sync_children_state: - * @bin: #Gstbin to sync state - * - * Tries to set the state of the children of this bin to the same state of the - * bin by calling gst_element_set_state for each child not already having a - * synchronized state. - * - * Returns: The worst return value of any gst_element_set_state. So if one child - * returns #GST_STATE_FAILURE while all others return #GST_STATE_SUCCESS - * this function returns #GST_STATE_FAILURE. - */ -GstElementStateReturn -gst_bin_sync_children_state (GstBin * bin) -{ - GList *children; - GstElement *element; - GstElementState state; - GstElementStateReturn ret = GST_STATE_SUCCESS; - - g_return_val_if_fail (GST_IS_BIN (bin), GST_STATE_FAILURE); - - state = GST_STATE (bin); - children = bin->children; - GST_CAT_INFO (GST_CAT_STATES, - "syncing state of children with bin \"%s\"'s state %s", - GST_ELEMENT_NAME (bin), gst_element_state_get_name (state)); - - while (children) { - element = GST_ELEMENT (children->data); - children = children->next; - if (GST_STATE (element) != state) { - switch (gst_element_set_state (element, state)) { - case GST_STATE_SUCCESS: - break; - case GST_STATE_ASYNC: - if (ret == GST_STATE_SUCCESS) - ret = GST_STATE_ASYNC; - break; - case GST_STATE_FAILURE: - ret = GST_STATE_FAILURE; - break; - default: - /* make sure gst_element_set_state never returns this */ - g_assert_not_reached (); - } - } - } - - return ret; -} - #ifndef GST_DISABLE_LOADSAVE static xmlNodePtr gst_bin_save_thyself (GstObject * object, xmlNodePtr parent) @@ -1416,85 +1008,3 @@ gst_bin_restore_thyself (GstObject * object, xmlNodePtr self) (GST_OBJECT_CLASS (parent_class)->restore_thyself) (object, self); } #endif /* GST_DISABLE_LOADSAVE */ - -static GStaticRecMutex iterate_lock = G_STATIC_REC_MUTEX_INIT; - -static gboolean -gst_bin_iterate_func (GstBin * bin) -{ - GstScheduler *sched = GST_ELEMENT_SCHEDULER (bin); - - g_static_rec_mutex_unlock (&iterate_lock); - - /* only iterate if this is the manager bin */ - if (sched && sched->parent == GST_ELEMENT (bin)) { - GstSchedulerState state; - - state = gst_scheduler_iterate (sched); - - if (state == GST_SCHEDULER_STATE_RUNNING) { - goto done; - } else if (state == GST_SCHEDULER_STATE_ERROR) { - gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PAUSED); - } else if (state == GST_SCHEDULER_STATE_STOPPED) { - /* check if we have children scheds that are still running */ - /* FIXME: remove in 0.9? autouseless because iterations gone? */ - GList *walk; - - for (walk = sched->schedulers; walk; walk = g_list_next (walk)) { - GstScheduler *test = walk->data; - - g_return_val_if_fail (test->parent, FALSE); - if (GST_STATE (test->parent) == GST_STATE_PLAYING) { - GST_CAT_DEBUG_OBJECT (GST_CAT_SCHEDULING, bin, - "current bin is not iterating, but children are, " - "so returning TRUE anyway..."); - g_usleep (1); - goto done; - } - } - } - } else { - g_warning ("bin \"%s\" is not the managing bin, can't be iterated on!\n", - GST_ELEMENT_NAME (bin)); - } - - g_static_rec_mutex_lock (&iterate_lock); - - return FALSE; - -done: - g_static_rec_mutex_lock (&iterate_lock); - return TRUE; -} - -/** - * gst_bin_iterate: - * @bin: a#GstBin to iterate. - * - * Iterates over the elements in this bin. - * - * Returns: TRUE if the bin did something useful. This value - * can be used to determine it the bin is in EOS. - */ -gboolean -gst_bin_iterate (GstBin * bin) -{ - gboolean running; - - g_return_val_if_fail (bin != NULL, FALSE); - g_return_val_if_fail (GST_IS_BIN (bin), FALSE); - - GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, bin, "starting iteration"); - gst_object_ref (GST_OBJECT (bin)); - - g_static_rec_mutex_lock (&iterate_lock); - running = FALSE; - g_signal_emit (G_OBJECT (bin), gst_bin_signals[ITERATE], 0, &running); - g_static_rec_mutex_unlock (&iterate_lock); - - gst_object_unref (GST_OBJECT (bin)); - GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, bin, "finished iteration"); - - return running; -} diff --git a/gst/gstbin.h b/gst/gstbin.h index c6ab722ee7..6f0d94818d 100644 --- a/gst/gstbin.h +++ b/gst/gstbin.h @@ -60,9 +60,6 @@ GST_EXPORT GType _gst_bin_type; * and (un)set using GST_FLAG_SET () and GST_FLAG_UNSET (). */ typedef enum { - GST_BIN_FLAG_MANAGER = GST_ELEMENT_FLAG_LAST, - GST_BIN_SELF_SCHEDULABLE, - GST_BIN_STATE_LOCKED, /* padding */ GST_BIN_FLAG_LAST = GST_ELEMENT_FLAG_LAST + 5 } GstBinFlags; @@ -84,8 +81,6 @@ struct _GstBin { GList *children; guint32 children_cookie; - GstElementState child_states[GST_NUM_STATES]; - /*< private >*/ gpointer _gst_reserved[GST_PADDING]; }; @@ -94,13 +89,6 @@ struct _GstBinClass { GstElementClass parent_class; /*< public >*/ - - /* run a full iteration of operation */ - gboolean (*iterate) (GstBin *bin); - void (*child_state_change) (GstBin *bin, GstElementState oldstate, - GstElementState newstate, GstElement *element); - - /* signals */ void (*element_added) (GstBin *bin, GstElement *child); void (*element_removed) (GstBin *bin, GstElement *child); @@ -133,15 +121,6 @@ GstIterator* gst_bin_iterate_recurse (GstBin *bin); GstIterator* gst_bin_iterate_sinks (GstBin *bin); GstIterator* gst_bin_iterate_all_by_interface (GstBin *bin, GType interface); -gboolean gst_bin_iterate (GstBin *bin); - -GstElementStateReturn gst_bin_sync_children_state (GstBin *bin); - -/* internal */ -/* one of our childs signaled a state change */ -void gst_bin_child_state_change (GstBin *bin, GstElementState oldstate, - GstElementState newstate, GstElement *child); - G_END_DECLS diff --git a/gst/gstbus.c b/gst/gstbus.c new file mode 100644 index 0000000000..7720dd5889 --- /dev/null +++ b/gst/gstbus.c @@ -0,0 +1,621 @@ +/* GStreamer + * Copyright (C) 2004 Wim Taymans + * + * gstbus.c: GstBus subsystem + * + * 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 +#include +#include +#include + +#include "gst_private.h" +#include "gstinfo.h" + +#include "gstbus.h" + +enum +{ + ARG_0, +}; + +static void gst_bus_class_init (GstBusClass * klass); +static void gst_bus_init (GstBus * bus); +static void gst_bus_dispose (GObject * object); + +static void gst_bus_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_bus_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static GstObjectClass *parent_class = NULL; + +/* static guint gst_bus_signals[LAST_SIGNAL] = { 0 }; */ + +GType +gst_bus_get_type (void) +{ + static GType bus_type = 0; + + if (!bus_type) { + static const GTypeInfo bus_info = { + sizeof (GstBusClass), + NULL, + NULL, + (GClassInitFunc) gst_bus_class_init, + NULL, + NULL, + sizeof (GstBus), + 0, + (GInstanceInitFunc) gst_bus_init, + NULL + }; + + bus_type = g_type_register_static (GST_TYPE_OBJECT, "GstBus", &bus_info, 0); + } + return bus_type; +} + +static void +gst_bus_class_init (GstBusClass * klass) +{ + GObjectClass *gobject_class; + GstObjectClass *gstobject_class; + + gobject_class = (GObjectClass *) klass; + gstobject_class = (GstObjectClass *) klass; + + parent_class = g_type_class_ref (GST_TYPE_OBJECT); + + if (!g_thread_supported ()) + g_thread_init (NULL); + + gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_bus_dispose); + gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_bus_set_property); + gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_bus_get_property); +} + +static void +gst_bus_init (GstBus * bus) +{ + bus->queue = g_queue_new (); + bus->queue_lock = g_mutex_new (); + + if (socketpair (PF_UNIX, SOCK_STREAM, 0, bus->control_socket) < 0) + goto no_socketpair; + + bus->io_channel = g_io_channel_unix_new (bus->control_socket[0]); + + return; + + /* errors */ +no_socketpair: + { + g_warning ("cannot create io channel"); + bus->io_channel = NULL; + } +} + +static void +gst_bus_dispose (GObject * object) +{ + GstBus *bus; + + bus = GST_BUS (object); + + if (bus->io_channel) { + g_io_channel_shutdown (bus->io_channel, TRUE, NULL); + g_io_channel_unref (bus->io_channel); + bus->io_channel = NULL; + } + close (bus->control_socket[0]); + close (bus->control_socket[1]); + + if (bus->queue) { + g_mutex_lock (bus->queue_lock); + g_queue_free (bus->queue); + bus->queue = NULL; + g_mutex_unlock (bus->queue_lock); + g_mutex_free (bus->queue_lock); + bus->queue_lock = NULL; + } + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gst_bus_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstBus *bus; + + bus = GST_BUS (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_bus_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstBus *bus; + + bus = GST_BUS (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +GstBus * +gst_bus_new (void) +{ + GstBus *result; + + result = g_object_new (gst_bus_get_type (), NULL); + + return result; +} + +/** + * gst_bus_post: + * @bus: a #GstBus to post on + * @message: The #GstMessage to post + * + * Post a message on the given bus. + * + * Returns: TRUE if the message could be posted. + * + * MT safe. + */ +gboolean +gst_bus_post (GstBus * bus, GstMessage * message) +{ + gchar c; + GstBusSyncReply reply = GST_BUS_PASS; + GstBusSyncHandler handler; + gpointer handler_data; + gboolean need_write = FALSE; + ssize_t write_ret = -1; + + g_return_val_if_fail (GST_IS_BUS (bus), FALSE); + g_return_val_if_fail (GST_IS_MESSAGE (message), FALSE); + + //g_print ("posting message on bus, type %d\n", GST_MESSAGE_TYPE (message)); + GST_DEBUG_OBJECT (bus, "posting message on bus"); + + GST_LOCK (bus); + handler = bus->sync_handler; + handler_data = bus->sync_handler_data; + GST_UNLOCK (bus); + + /* first call the sync handler if it is installed */ + if (handler) { + reply = handler (bus, message, handler_data); + } + + /* now see what we should do with the message */ + switch (reply) { + case GST_BUS_DROP: + /* drop the message */ + break; + case GST_BUS_PASS: + /* pass the message to the async queue */ + g_mutex_lock (bus->queue_lock); + if (g_queue_get_length (bus->queue) == 0) + need_write = TRUE; + g_queue_push_tail (bus->queue, message); + g_mutex_unlock (bus->queue_lock); + + if (g_queue_get_length (bus->queue) == 0) + need_write = TRUE; + if (need_write) { + c = 'p'; + errno = EAGAIN; + while (write_ret == -1) { + switch (errno) { + case EAGAIN: + case EINTR: + break; + default: + perror ("gst_bus_post: could not write to fd"); + return FALSE; + } + write_ret = write (bus->control_socket[1], &c, 1); + } + } + break; + case GST_BUS_ASYNC: + { + /* async delivery, we need a mutex and a cond to block + * on */ + GMutex *lock = g_mutex_new (); + GCond *cond = g_cond_new (); + + GST_MESSAGE_COND (message) = cond; + GST_MESSAGE_GET_LOCK (message) = lock; + + GST_DEBUG ("waiting for async delivery of message %p", message); + + /* now we lock the message mutex, send the message to the async + * queue. When the message is handled by the app and destroyed, + * the cond will be signalled and we can continue */ + g_mutex_lock (lock); + g_mutex_lock (bus->queue_lock); + if (g_queue_get_length (bus->queue) == 0) + need_write = TRUE; + g_queue_push_tail (bus->queue, message); + g_mutex_unlock (bus->queue_lock); + + if (need_write) { + c = 'p'; + errno = EAGAIN; + while (write_ret == -1) { + switch (errno) { + case EAGAIN: + case EINTR: + break; + default: + perror ("gst_bus_post: could not write to fd"); + return FALSE; + } + write_ret = write (bus->control_socket[1], &c, 1); + } + } + + /* now block till the message is freed */ + g_cond_wait (cond, lock); + g_mutex_unlock (lock); + + GST_DEBUG ("message %p delivered asynchronously", message); + + g_mutex_free (lock); + g_cond_free (cond); + break; + } + } + + return TRUE; +} + +/** + * gst_bus_have_pending: + * @bus: a #GstBus to check + * + * Check if there are pending messages on the bus that should be + * handled. + * + * Returns: TRUE if there are messages on the bus to be handled. + * + * MT safe. + */ +gboolean +gst_bus_have_pending (GstBus * bus) +{ + gint length; + + g_return_val_if_fail (GST_IS_BUS (bus), FALSE); + + g_mutex_lock (bus->queue_lock); + length = g_queue_get_length (bus->queue); + g_mutex_unlock (bus->queue_lock); + + return (length > 0); +} + +/** + * gst_bus_pop: + * @bus: a #GstBus to pop + * + * Get a message from the bus. + * + * Returns: The #GstMessage that is on the bus, or NULL if the bus is empty. + * + * MT safe. + */ +GstMessage * +gst_bus_pop (GstBus * bus) +{ + GstMessage *message; + gboolean needs_read = FALSE; + + g_return_val_if_fail (GST_IS_BUS (bus), NULL); + + g_mutex_lock (bus->queue_lock); + message = g_queue_pop_head (bus->queue); + if (message && g_queue_get_length (bus->queue) == 0) + needs_read = TRUE; + g_mutex_unlock (bus->queue_lock); + + if (needs_read) { + gchar c; + ssize_t read_ret = -1; + + /* the char in the fd is essentially just a way to wake us up. read it off so + we're not woken up again. */ + errno = EAGAIN; + while (read_ret == -1) { + switch (errno) { + case EAGAIN: + case EINTR: + break; + default: + perror ("gst_bus_pop: could not read from fd"); + return NULL; + } + read_ret = read (bus->control_socket[0], &c, 1); + } + } + + return message; +} + +/** + * gst_bus_peek: + * @bus: a #GstBus + * + * Peek the message on the top of the bus' queue. The bus maintains ownership of + * the message, and the message will remain on the bus' message queue. + * + * Returns: The #GstMessage that is on the bus, or NULL if the bus is empty. + * + * MT safe. + */ +GstMessage * +gst_bus_peek (GstBus * bus) +{ + GstMessage *message; + + g_return_val_if_fail (GST_IS_BUS (bus), NULL); + + g_mutex_lock (bus->queue_lock); + message = g_queue_peek_head (bus->queue); + g_mutex_unlock (bus->queue_lock); + + return message; +} + +/** + * gst_bus_set_sync_handler: + * @bus: a #GstBus to install the handler on + * @func: The handler function to install + * @data: User data that will be sent to the handler function. + * + * Install a synchronous handler on the bus. The function will be called + * every time a new message is posted on the bus. Note that the function + * will be called in the same thread context as the posting object. + */ +void +gst_bus_set_sync_handler (GstBus * bus, GstBusSyncHandler func, gpointer data) +{ + g_return_if_fail (GST_IS_BUS (bus)); + + GST_LOCK (bus); + bus->sync_handler = func; + bus->sync_handler_data = data; + GST_UNLOCK (bus); +} + +/** + * gst_bus_create_watch: + * @bus: a #GstBus to create the watch for + * + * Create watch for this bus. + * + * Returns: A #GSource that can be added to a mainloop. + */ +GSource * +gst_bus_create_watch (GstBus * bus) +{ + GSource *source; + + g_return_val_if_fail (GST_IS_BUS (bus), NULL); + + /* FIXME, we need to ref the bus and unref it when the source + * is destroyed */ + source = g_io_create_watch (bus->io_channel, G_IO_IN); + + return source; +} + +typedef struct +{ + GSource *source; + GstBus *bus; + gint priority; + GstBusHandler handler; + gpointer user_data; + GDestroyNotify notify; +} GstBusWatch; + +static gboolean +bus_watch_callback (GIOChannel * channel, GIOCondition cond, + GstBusWatch * watch) +{ + GstMessage *message; + gboolean needs_pop = TRUE; + + g_return_val_if_fail (GST_IS_BUS (watch->bus), FALSE); + + message = gst_bus_peek (watch->bus); + + g_return_val_if_fail (message != NULL, TRUE); + + if (watch->handler) + needs_pop = watch->handler (watch->bus, message, watch->user_data); + + if (needs_pop) + gst_message_unref (gst_bus_pop (watch->bus)); + + return TRUE; +} + +static void +bus_watch_destroy (GstBusWatch * watch) +{ + if (watch->notify) { + watch->notify (watch->user_data); + } + gst_object_unref (GST_OBJECT_CAST (watch->bus)); + g_free (watch); +} + +/** + * gst_bus_add_watch_full: + * @bus: a #GstBus to create the watch for + * @handler: A function to call when a message is received. + * + * Adds the bus to the mainloop with the given priority. If the handler returns + * TRUE, the message will then be popped off the queue. When the handler is + * called, the message belongs to the caller; if you want to keep a copy of it, + * call gst_message_ref before leaving the handler. + * + * Returns: The event source id. + * + * MT safe. + */ +guint +gst_bus_add_watch_full (GstBus * bus, gint priority, + GstBusHandler handler, gpointer user_data, GDestroyNotify notify) +{ + guint id; + GstBusWatch *watch; + + g_return_val_if_fail (GST_IS_BUS (bus), 0); + + watch = g_new (GstBusWatch, 1); + + gst_object_ref (GST_OBJECT_CAST (bus)); + watch->source = gst_bus_create_watch (bus); + watch->bus = bus; + watch->priority = priority; + watch->handler = handler; + watch->user_data = user_data; + watch->notify = notify; + + if (priority != G_PRIORITY_DEFAULT) + g_source_set_priority (watch->source, priority); + + g_source_set_callback (watch->source, (GSourceFunc) bus_watch_callback, + watch, (GDestroyNotify) bus_watch_destroy); + + id = g_source_attach (watch->source, NULL); + g_source_unref (watch->source); + + return id; +} + +/** + * gst_bus_add_watch: + * @bus: a #GstBus to create the watch for + * + * Adds the bus to the mainloop with the default priority. + * + * Returns: The event source id. + * + * MT safe. + */ +guint +gst_bus_add_watch (GstBus * bus, GstBusHandler handler, gpointer user_data) +{ + return gst_bus_add_watch_full (bus, G_PRIORITY_DEFAULT, handler, user_data, + NULL); +} + +typedef struct +{ + GMainLoop *loop; + guint timeout_id; + GstMessageType events; + GstMessageType revent; +} GstBusPollData; + +static gboolean +poll_handler (GstBus * bus, GstMessage * message, GstBusPollData * poll_data) +{ + if (GST_MESSAGE_TYPE (message) & poll_data->events) { + poll_data->revent = GST_MESSAGE_TYPE (message); + if (g_main_loop_is_running (poll_data->loop)) + g_main_loop_quit (poll_data->loop); + /* keep the message on the queue */ + return FALSE; + } else { + /* pop and unref the message */ + return TRUE; + } +} + +static gboolean +poll_timeout (GstBusPollData * poll_data) +{ + poll_data->timeout_id = 0; + g_main_loop_quit (poll_data->loop); + /* returning FALSE will remove the source id */ + return FALSE; +} + +/** + * gst_bus_poll: + * @bus: a #GstBus + * @events: a mask of #GstMessageType, representing the set of message types to + * poll for. + * @timeout: the poll timeout, as a #GstClockTimeDiff, or -1 to poll indefinitely. + * + * Poll the bus for events. Will block while waiting for events to come. You can + * specify a maximum time to poll with the @timeout parameter. If @timeout is + * negative, this function will block indefinitely. + * + * Returns: The type of the message that was received, or GST_MESSAGE_UNKNOWN if + * the poll timed out. The message will remain in the bus queue; you will need + * to gst_bus_pop() it off before entering gst_bus_poll() again. + */ +GstMessageType +gst_bus_poll (GstBus * bus, GstMessageType events, GstClockTimeDiff timeout) +{ + GstBusPollData *poll_data; + GstMessageType ret; + guint id; + + poll_data = g_new0 (GstBusPollData, 1); + if (timeout >= 0) + poll_data->timeout_id = g_timeout_add (timeout / GST_MSECOND, + (GSourceFunc) poll_timeout, poll_data); + poll_data->loop = g_main_loop_new (NULL, FALSE); + poll_data->events = events; + poll_data->revent = GST_MESSAGE_UNKNOWN; + + id = gst_bus_add_watch (bus, (GstBusHandler) poll_handler, poll_data); + g_main_loop_run (poll_data->loop); + g_source_remove (id); + + ret = poll_data->revent; + + if (poll_data->timeout_id) + g_source_remove (poll_data->timeout_id); + g_main_loop_unref (poll_data->loop); + g_free (poll_data); + + return ret; +} diff --git a/gst/gstbus.h b/gst/gstbus.h new file mode 100644 index 0000000000..cc49f63b29 --- /dev/null +++ b/gst/gstbus.h @@ -0,0 +1,103 @@ +/* GStreamer + * Copyright (C) 2004 Wim Taymans + * + * gstbus.h: Header for GstBus subsystem + * + * 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 __GST_BUS_H__ +#define __GST_BUS_H__ + +#include +#include +#include + +G_BEGIN_DECLS + +/* --- standard type macros --- */ +#define GST_TYPE_BUS (gst_bus_get_type ()) +#define GST_BUS(bus) (G_TYPE_CHECK_INSTANCE_CAST ((bus), GST_TYPE_BUS, GstBus)) +#define GST_IS_BUS(bus) (G_TYPE_CHECK_INSTANCE_TYPE ((bus), GST_TYPE_BUS)) +#define GST_BUS_CLASS(bclass) (G_TYPE_CHECK_CLASS_CAST ((bclass), GST_TYPE_BUS, GstBusClass)) +#define GST_IS_BUS_CLASS(bclass) (G_TYPE_CHECK_CLASS_TYPE ((bclass), GST_TYPE_BUS)) +#define GST_BUS_GET_CLASS(bus) (G_TYPE_INSTANCE_GET_CLASS ((bus), GST_TYPE_BUS, GstBusClass)) +#define GST_BUS_CAST(bus) ((GstBus*)(bus)) + +typedef enum +{ + GST_BUS_DROP = 0, /* drop message */ + GST_BUS_PASS = 1, /* pass message to async queue */ + GST_BUS_ASYNC = 2, /* pass message to async queue, continue if message is handled */ +} GstBusSyncReply; + +typedef GstBusSyncReply (*GstBusSyncHandler) (GstBus * bus, GstMessage * message, gpointer data); +typedef gboolean (*GstBusHandler) (GstBus * bus, GstMessage * message, gpointer data); + +struct _GstBus +{ + GstObject object; + + /*< private > */ + GQueue *queue; + GMutex *queue_lock; + + GstBusSyncHandler sync_handler; + gpointer sync_handler_data; + + gint control_socket[2]; + GIOChannel *io_channel; + + /*< private > */ + gpointer _gst_reserved[GST_PADDING]; +}; + +struct _GstBusClass +{ + GstObjectClass parent_class; + + /*< private > */ + gpointer _gst_reserved[GST_PADDING]; +}; + +GType gst_bus_get_type (void); + +GstBus* gst_bus_new (void); + +gboolean gst_bus_post (GstBus * bus, GstMessage * message); + +gboolean gst_bus_have_pending (GstBus * bus); +GstMessage * gst_bus_peek (GstBus * bus); +GstMessage * gst_bus_pop (GstBus * bus); + +void gst_bus_set_sync_handler (GstBus * bus, GstBusSyncHandler func, + gpointer data); + +GSource * gst_bus_create_watch (GstBus * bus); +guint gst_bus_add_watch_full (GstBus * bus, + gint priority, + GstBusHandler handler, + gpointer user_data, + GDestroyNotify notify); +guint gst_bus_add_watch (GstBus * bus, + GstBusHandler handler, + gpointer user_data); +GstMessageType gst_bus_poll (GstBus *bus, GstMessageType events, + GstClockTimeDiff timeout); + + +G_END_DECLS +#endif /* __GST_BUS_H__ */ diff --git a/gst/gstcaps.h b/gst/gstcaps.h index e0c21f7e5d..5943223522 100644 --- a/gst/gstcaps.h +++ b/gst/gstcaps.h @@ -81,7 +81,7 @@ struct _GstStaticCaps { gpointer _gst_reserved[GST_PADDING]; }; -GType gst_caps_get_type (void) G_GNUC_CONST; +GType gst_caps_get_type (void); GstCaps * gst_caps_new_empty (void); GstCaps * gst_caps_new_any (void); GstCaps * gst_caps_new_simple (const char *media_type, diff --git a/gst/gstdata.h b/gst/gstdata.h index 9cd447b2b4..de13f85581 100644 --- a/gst/gstdata.h +++ b/gst/gstdata.h @@ -102,7 +102,7 @@ GstData* gst_data_ref_by_count (GstData* data, gint count); void gst_data_unref (GstData* data); /* GType for GstData */ -GType gst_data_get_type (void) G_GNUC_CONST; +GType gst_data_get_type (void); G_END_DECLS diff --git a/gst/gstelement.c b/gst/gstelement.c index 7e8b217535..1fbe1aa5bc 100644 --- a/gst/gstelement.c +++ b/gst/gstelement.c @@ -26,7 +26,7 @@ #include #include "gstelement.h" -#include "gstbin.h" +#include "gstbus.h" #include "gstmarshal.h" #include "gsterror.h" #include "gstscheduler.h" @@ -41,9 +41,6 @@ enum STATE_CHANGE, NEW_PAD, PAD_REMOVED, - ERROR, - EOS, - FOUND_TAG, NO_MORE_PADS, /* add more above */ LAST_SIGNAL @@ -68,12 +65,13 @@ static void gst_element_dispose (GObject * object); static void gst_element_finalize (GObject * object); static GstElementStateReturn gst_element_change_state (GstElement * element); -static void gst_element_error_func (GstElement * element, GstElement * source, - GError * error, gchar * debug); -static void gst_element_found_tag_func (GstElement * element, - GstElement * source, const GstTagList * tag_list); -static GstElementStateReturn gst_element_set_state_func (GstElement * element, - GstElementState state); +static GstElementStateReturn gst_element_get_state_func (GstElement * element, + GstElementState * state, GstElementState * pending, GTimeVal * timeout); +static void gst_element_set_manager_func (GstElement * element, + GstPipeline * manager); +static void gst_element_set_bus_func (GstElement * element, GstBus * bus); +static void gst_element_set_scheduler_func (GstElement * element, + GstScheduler * scheduler); #ifndef GST_DISABLE_LOADSAVE static xmlNodePtr gst_element_save_thyself (GstObject * object, @@ -154,43 +152,6 @@ gst_element_class_init (GstElementClass * klass) g_signal_new ("pad-removed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstElementClass, pad_removed), NULL, NULL, gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); - /** - * GstElement::error: - * @gstelement: the object which received the signal - * @element: - * @error: - * @message: - * - * a #GstError has occured during data processing - */ - gst_element_signals[ERROR] = - g_signal_new ("error", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GstElementClass, error), NULL, NULL, - gst_marshal_VOID__OBJECT_BOXED_STRING, G_TYPE_NONE, 3, GST_TYPE_ELEMENT, - GST_TYPE_G_ERROR, G_TYPE_STRING); - /** - * GstElement::eos: - * @gstelement: the object which received the signal - * - * the end of the stream has been reached - */ - gst_element_signals[EOS] = - g_signal_new ("eos", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GstElementClass, eos), NULL, NULL, - gst_marshal_VOID__VOID, G_TYPE_NONE, 0); - /** - * GstElement::found-tag: - * @gstelement: the object which received the signal - * @element: - * @tags: - * - * tags for the incomming stream have been received - */ - gst_element_signals[FOUND_TAG] = - g_signal_new ("found-tag", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GstElementClass, found_tag), NULL, NULL, - gst_marshal_VOID__OBJECT_BOXED, G_TYPE_NONE, 2, GST_TYPE_ELEMENT, - GST_TYPE_TAG_LIST); /** * GstElement::no-more-pads: * @gstelement: the object which received the signal @@ -212,10 +173,11 @@ gst_element_class_init (GstElementClass * klass) #endif klass->change_state = GST_DEBUG_FUNCPTR (gst_element_change_state); - klass->error = GST_DEBUG_FUNCPTR (gst_element_error_func); - klass->found_tag = GST_DEBUG_FUNCPTR (gst_element_found_tag_func); + klass->get_state = GST_DEBUG_FUNCPTR (gst_element_get_state_func); + klass->set_manager = GST_DEBUG_FUNCPTR (gst_element_set_manager_func); + klass->set_bus = GST_DEBUG_FUNCPTR (gst_element_set_bus_func); + klass->set_scheduler = GST_DEBUG_FUNCPTR (gst_element_set_scheduler_func); klass->numpadtemplates = 0; - klass->set_state = GST_DEBUG_FUNCPTR (gst_element_set_state_func); klass->elementfactory = NULL; } @@ -251,8 +213,7 @@ gst_element_init (GstElement * element) element->pads = NULL; element->srcpads = NULL; element->sinkpads = NULL; - element->loopfunc = NULL; - element->scheduler = NULL; + element->manager = NULL; element->clock = NULL; element->sched_private = NULL; element->state_lock = g_mutex_new (); @@ -411,233 +372,6 @@ gst_element_get_clock (GstElement * element) return NULL; } -/** - * gst_element_clock_wait: - * @element: a #GstElement. - * @id: the #GstClock to use. - * @jitter: the difference between requested time and actual time. - * - * Waits for a specific time on the clock. - * - * Returns: the #GstClockReturn result of the wait operation. - */ -GstClockReturn -gst_element_clock_wait (GstElement * element, GstClockID id, - GstClockTimeDiff * jitter) -{ - GstClockReturn res; - - g_return_val_if_fail (GST_IS_ELEMENT (element), GST_CLOCK_ERROR); - - if (GST_ELEMENT_SCHEDULER (element)) { - GST_CAT_DEBUG (GST_CAT_CLOCK, "waiting on scheduler clock with id %d"); - res = - gst_scheduler_clock_wait (GST_ELEMENT_SCHEDULER (element), element, id, - jitter); - } else { - GST_CAT_DEBUG (GST_CAT_CLOCK, "no scheduler, returning GST_CLOCK_OK"); - res = GST_CLOCK_OK; - } - - return res; -} - -#undef GST_CAT_DEFAULT -#define GST_CAT_DEFAULT GST_CAT_CLOCK -/** - * gst_element_get_time: - * @element: element to query - * - * Query the element's time. FIXME: The element must use - * - * Returns: the current stream time in #GST_STATE_PLAYING, - * the element base time in #GST_STATE_PAUSED, - * or #GST_CLOCK_TIME_NONE otherwise. - */ -/* FIXME: this should always return time on the same scale. Now it returns - * the (absolute) base_time in PAUSED and the (current running) time in - * PLAYING. - * Solution: have a get_base_time and make the element subtract if it needs - * to. In PAUSED return the same as PLAYING, ie. the current timestamp where - * the element is at according to the provided clock. - */ -GstClockTime -gst_element_get_time (GstElement * element) -{ - g_return_val_if_fail (GST_IS_ELEMENT (element), GST_CLOCK_TIME_NONE); - - if (element->clock == NULL) { - GST_WARNING_OBJECT (element, "element queries time but has no clock"); - return GST_CLOCK_TIME_NONE; - } - switch (element->current_state) { - case GST_STATE_NULL: - case GST_STATE_READY: - return GST_CLOCK_TIME_NONE; - case GST_STATE_PAUSED: - return element->base_time; - case GST_STATE_PLAYING: - return gst_clock_get_time (element->clock) - element->base_time; - default: - g_assert_not_reached (); - return GST_CLOCK_TIME_NONE; - } -} - -/** - * gst_element_wait: - * @element: element that should wait - * @timestamp: what timestamp to wait on - * - * Waits until the given relative time stamp for the element has arrived. - * When this function returns successfully, the relative time point specified - * in the timestamp has passed for this element. - * This function can only be called on elements in - * #GST_STATE_PLAYING - * - * Returns: TRUE on success. - */ -gboolean -gst_element_wait (GstElement * element, GstClockTime timestamp) -{ - GstClockID id; - GstClockReturn ret; - GstClockTime time; - - g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); - g_return_val_if_fail (GST_IS_CLOCK (element->clock), FALSE); - g_return_val_if_fail (element->current_state == GST_STATE_PLAYING, FALSE); - g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timestamp), FALSE); - - /* shortcut when we're already late... */ - time = gst_element_get_time (element); - GST_CAT_LOG_OBJECT (GST_CAT_CLOCK, element, "element time %" GST_TIME_FORMAT, - GST_TIME_ARGS (time)); - if (time >= timestamp) { - GST_CAT_INFO_OBJECT (GST_CAT_CLOCK, element, - "called gst_element_wait (% " GST_TIME_FORMAT ") and was late (%" - GST_TIME_FORMAT, GST_TIME_ARGS (timestamp), - GST_TIME_ARGS (gst_element_get_time (element))); - return TRUE; - } - - id = gst_clock_new_single_shot_id (element->clock, - element->base_time + timestamp); - ret = gst_element_clock_wait (element, id, NULL); - gst_clock_id_unref (id); - - return ret == GST_CLOCK_OK; -} - -/** - * gst_element_set_time: - * @element: element to set time on - * @time: time to set - * - * Sets the current time of the element. This function can be used when handling - * discont events. You can only call this function on an element with a clock in - * #GST_STATE_PAUSED or #GST_STATE_PLAYING. You might want to have a look at - * gst_element_adjust_time(), if you want to adjust by a difference as that is - * more accurate. - */ -void -gst_element_set_time (GstElement * element, GstClockTime time) -{ - gst_element_set_time_delay (element, time, 0); -} - -/** - * gst_element_set_time_delay: - * @element: element to set time on - * @time: time to set - * @delay: a delay to discount from the given time - * - * Sets the current time of the element to time - delay. This function can be - * used when handling discont events in elements writing to an external buffer, - * i. e., an audio sink that writes to a sound card that buffers the sound - * before playing it. The delay should be the current buffering delay. - * - * You can only call this function on an element with a clock in - * #GST_STATE_PAUSED or #GST_STATE_PLAYING. You might want to have a look at - * gst_element_adjust_time(), if you want to adjust by a difference as that is - * more accurate. - */ -void -gst_element_set_time_delay (GstElement * element, GstClockTime time, - GstClockTime delay) -{ - GstClockTime event_time; - - g_return_if_fail (GST_IS_ELEMENT (element)); - g_return_if_fail (GST_IS_CLOCK (element->clock)); - g_return_if_fail (element->current_state >= GST_STATE_PAUSED); - g_return_if_fail (time >= delay); - - switch (element->current_state) { - case GST_STATE_PAUSED: - element->base_time = time - delay; - break; - case GST_STATE_PLAYING: - event_time = GST_CLOCK_TIME_NONE; - //gst_clock_get_event_time_delay (element->clock, delay); - GST_CAT_LOG_OBJECT (GST_CAT_CLOCK, element, - "clock time %" GST_TIME_FORMAT ": setting element time to %" - GST_TIME_FORMAT, GST_TIME_ARGS (event_time), GST_TIME_ARGS (time)); - element->base_time = event_time - time; - break; - default: - g_assert_not_reached (); - break; - } -} - -/** - * gst_element_adjust_time: - * @element: element to adjust time on - * @diff: difference to adjust - * - * Adjusts the current time of the element by the specified difference. This - * function can be used when handling discont events. You can only call this - * function on an element with a clock in #GST_STATE_PAUSED or - * #GST_STATE_PLAYING. It is more accurate than gst_element_set_time(). - */ -void -gst_element_adjust_time (GstElement * element, GstClockTimeDiff diff) -{ - GstClockTime time; - - g_return_if_fail (GST_IS_ELEMENT (element)); - g_return_if_fail (GST_IS_CLOCK (element->clock)); - g_return_if_fail (element->current_state >= GST_STATE_PAUSED); - - switch (element->current_state) { - case GST_STATE_PAUSED: - if (diff < 0 && element->base_time < abs (diff)) { - g_warning ("attempted to set the current time of element %s below 0", - GST_OBJECT_NAME (element)); - element->base_time = 0; - } else { - element->base_time += diff; - } - break; - case GST_STATE_PLAYING: - time = gst_clock_get_time (element->clock); - if (time < element->base_time - diff) { - g_warning ("attempted to set the current time of element %s below 0", - GST_OBJECT_NAME (element)); - element->base_time = time; - } else { - element->base_time -= diff; - } - break; - default: - g_assert_not_reached (); - break; - } -} - -#undef GST_CAT_DEFAULT - #ifndef GST_DISABLE_INDEX /** * gst_element_is_indexable: @@ -711,30 +445,6 @@ gst_element_get_index (GstElement * element) } #endif -/** - * gst_element_release_locks: - * @element: a #GstElement to release all locks on. - * - * Instruct the element to release all the locks it is holding, such as - * blocking reads, waiting for the clock, ... - * - * Returns: TRUE if the locks could be released. - */ -gboolean -gst_element_release_locks (GstElement * element) -{ - GstElementClass *oclass; - - g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); - - oclass = GST_ELEMENT_GET_CLASS (element); - - if (oclass->release_locks) - return oclass->release_locks (element); - - return TRUE; -} - /** * gst_element_add_pad: * @element: a #GstElement to add the pad to. @@ -1277,29 +987,6 @@ gst_element_class_get_pad_template (GstElementClass * element_class, return NULL; } -static void -gst_element_error_func (GstElement * element, GstElement * source, - GError * error, gchar * debug) -{ - GstObject *parent = GST_OBJECT_PARENT (element); - - /* tell the parent */ - if (parent) { - gst_object_ref (GST_OBJECT (element)); - gst_object_ref (parent); - GST_CAT_DEBUG (GST_CAT_ERROR_SYSTEM, - "forwarding error \"%s\" from %s to %s", error->message, - GST_ELEMENT_NAME (element), GST_OBJECT_NAME (parent)); - - g_signal_emit (G_OBJECT (parent), - gst_element_signals[ERROR], 0, source, error, debug); - GST_CAT_DEBUG (GST_CAT_ERROR_SYSTEM, "forwarded error \"%s\" from %s to %s", - error->message, GST_ELEMENT_NAME (element), GST_OBJECT_NAME (parent)); - gst_object_unref (GST_OBJECT (element)); - gst_object_unref (GST_OBJECT (parent)); - } -} - static GstPad * gst_element_get_random_pad (GstElement * element, GstPadDirection dir) { @@ -1663,6 +1350,47 @@ gst_element_convert (GstElement * element, return result; } +/** + * gst_element_post_message: + * @element: a #GstElement posting the message + * @message: a #GstMessage to post + * + * Post a message on the elements #GstBus. + * + * Returns: TRUE if the message was successfuly posted. + * + * MT safe. + */ +gboolean +gst_element_post_message (GstElement * element, GstMessage * message) +{ + GstBus *bus; + gboolean result = FALSE; + + GST_DEBUG ("posting message %p ...", message); + + g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); + g_return_val_if_fail (message != NULL, FALSE); + + GST_LOCK (element); + bus = element->bus; + + if (G_UNLIKELY (bus == NULL)) { + GST_DEBUG ("... but I won't because I have no bus"); + GST_UNLOCK (element); + gst_data_unref (GST_DATA (message)); + return FALSE; + } + gst_object_ref (GST_OBJECT (bus)); + GST_DEBUG ("... on bus %p", bus); + GST_UNLOCK (element); + + result = gst_bus_post (bus, message); + gst_object_unref (GST_OBJECT (bus)); + + return result; +} + /** * _gst_element_error_printf: * @format: the printf-like format to use, or NULL @@ -1691,102 +1419,79 @@ _gst_element_error_printf (const gchar * format, ...) } /** - * gst_element_error_full: - * @element: a #GstElement with the error. - * @domain: the GStreamer error domain this error belongs to. - * @code: the error code belonging to the domain - * @message: an allocated message to be used as a replacement for the default - * message connected to code, or NULL - * @debug: an allocated debug message to be used as a replacement for the - * default debugging information, or NULL - * @file: the source code file where the error was generated + * gst_element_message_full: + * @element: a #GstElement to send message from + * @type: the #GstMessageType + * @domain: the GStreamer GError domain this message belongs to + * @code: the GError code belonging to the domain + * @text: an allocated text string to be used as a replacement for the + * default message connected to code, or NULL + * @debug: an allocated debug message to be used as a replacement for the + * default debugging information, or NULL + * @file: the source code file where the error was generated * @function: the source code function where the error was generated - * @line: the source code line where the error was generated + * @line: the source code line where the error was generated * - * Signals an error condition on an element. - * This function is used internally by elements. - * It results in the "error" signal. + * Post an error or warning message on the bus from inside an element. + * + * MT safe. */ -void gst_element_error_full - (GstElement * element, GQuark domain, gint code, gchar * message, +void gst_element_message_full + (GstElement * element, GstMessageType type, + GQuark domain, gint code, gchar * text, gchar * debug, const gchar * file, const gchar * function, gint line) { - GError *error = NULL; + GError *gerror = NULL; gchar *name; - gchar *sent_message; + gchar *sent_text; gchar *sent_debug; + GstMessage *message = NULL; /* checks */ + GST_DEBUG ("start"); g_return_if_fail (GST_IS_ELEMENT (element)); + g_return_if_fail ((type == GST_MESSAGE_ERROR) || + (type == GST_MESSAGE_WARNING)); - /* check if we send the given message or the default error message */ - if ((message == NULL) || (message[0] == 0)) { - /* we got this message from g_strdup_printf (""); */ - g_free (message); - sent_message = gst_error_get_message (domain, code); + /* check if we send the given text or the default error text */ + if ((text == NULL) || (text[0] == 0)) { + /* text could have come from g_strdup_printf (""); */ + g_free (text); + sent_text = gst_error_get_message (domain, code); } else - sent_message = message; + sent_text = text; + /* construct a sent_debug with extra information from source */ if ((debug == NULL) || (debug[0] == 0)) { - /* we got this debug from g_strdup_printf (""); */ - g_free (debug); - debug = NULL; - } - - /* create error message */ - GST_CAT_INFO (GST_CAT_ERROR_SYSTEM, "signaling error in %s: %s", - GST_ELEMENT_NAME (element), sent_message); - error = g_error_new_literal (domain, code, sent_message); - - /* if the element was already in error, stop now */ - if (GST_FLAG_IS_SET (element, GST_ELEMENT_IN_ERROR)) { - GST_CAT_INFO (GST_CAT_ERROR_SYSTEM, "recursive ERROR detected in %s", - GST_ELEMENT_NAME (element)); - g_free (sent_message); - if (debug) - g_free (debug); - return; - } - - GST_FLAG_SET (element, GST_ELEMENT_IN_ERROR); - - /* emit the signal, make sure the element stays available */ - gst_object_ref (GST_OBJECT (element)); - name = gst_object_get_path_string (GST_OBJECT (element)); - if (debug) + /* debug could have come from g_strdup_printf (""); */ + sent_debug = NULL; + } else { + name = gst_object_get_path_string (GST_OBJECT (element)); sent_debug = g_strdup_printf ("%s(%d): %s: %s:\n%s", file, line, function, name, debug ? debug : ""); - else - sent_debug = NULL; + g_free (name); + } g_free (debug); - g_free (name); - g_signal_emit (G_OBJECT (element), gst_element_signals[ERROR], 0, element, - error, sent_debug); - GST_CAT_INFO (GST_CAT_ERROR_SYSTEM, "signalled error in %s: %s", - GST_ELEMENT_NAME (element), sent_message); - /* tell the scheduler */ - if (GST_ELEMENT_SCHEDULER (element)) { - gst_scheduler_error (GST_ELEMENT_SCHEDULER (element), element); + /* create gerror and post message */ + GST_CAT_INFO_OBJECT (GST_CAT_ERROR_SYSTEM, element, "posting message: %s", + sent_text); + gerror = g_error_new_literal (domain, code, sent_text); + + if (type == GST_MESSAGE_ERROR) { + message = gst_message_new_error (GST_OBJECT (element), gerror, sent_debug); + } else { + message = gst_message_new_warning (GST_OBJECT (element), gerror, + sent_debug); } + gst_element_post_message (element, message); - if (GST_STATE (element) == GST_STATE_PLAYING) { - GstElementStateReturn ret; - - ret = gst_element_set_state (element, GST_STATE_PAUSED); - if (ret != GST_STATE_SUCCESS) { - g_warning ("could not PAUSE element \"%s\" after error, help!", - GST_ELEMENT_NAME (element)); - } - } - - GST_FLAG_UNSET (element, GST_ELEMENT_IN_ERROR); + GST_CAT_INFO_OBJECT (GST_CAT_ERROR_SYSTEM, element, "posted message: %s", + sent_text); /* cleanup */ - gst_object_unref (GST_OBJECT (element)); - g_free (sent_message); - g_free (sent_debug); - g_error_free (error); + g_free (sent_text); + /* sent_debug is not part of the gerror, so don't free it here */ } /** @@ -1892,34 +1597,115 @@ gst_element_sync_state_with_parent (GstElement * element) return TRUE; } -/** - * gst_element_get_state: - * @element: a #GstElement to get the state of. - * - * Gets the state of the element. - * - * Returns: the #GstElementState of the element. - */ -GstElementState -gst_element_get_state (GstElement * element) +/* MT safe */ +static GstElementStateReturn +gst_element_get_state_func (GstElement * element, + GstElementState * state, GstElementState * pending, GTimeVal * timeout) { - g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_VOID_PENDING); + GstElementStateReturn ret = GST_STATE_FAILURE; - return GST_STATE (element); + /* implment me */ + return ret; } /** - * gst_element_wait_state_change: - * @element: a #GstElement to wait for a state change on. + * gst_element_get_state: + * @element: a #GstElement to get the state of. + * @state: a pointer to #GstElementState to hold the state. Can be NULL. + * @pending: a pointer to #GstElementState to hold the pending state. + * Can be NULL. + * @timeout: a #GTimeVal to specify the timeout for an async + * state change or NULL for infinite timeout. * - * Waits and blocks until the element changed its state. + * Gets the state of the element. + * + * For elements that performed an ASYNC state change, as reported by + * #gst_element_set_state(), this function will block up to the + * specified timeout value for the state change to complete. + * If the element completes the state change or goes into + * an error, this function returns immediatly with a return value of + * GST_STATE_SUCCESS or GST_STATE_FAILURE respectively. + * + * Returns: GST_STATE_SUCCESS if the element has no more pending state and + * the last state change succeeded, GST_STATE_ASYNC + * if the element is still performing a state change or + * GST_STATE_FAILURE if the last state change failed. + * + * MT safe. + */ +GstElementStateReturn +gst_element_get_state (GstElement * element, + GstElementState * state, GstElementState * pending, GTimeVal * timeout) +{ + GstElementClass *oclass; + GstElementStateReturn result = GST_STATE_FAILURE; + + g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_FAILURE); + + oclass = GST_ELEMENT_GET_CLASS (element); + + if (oclass->get_state) + result = (oclass->get_state) (element, state, pending, timeout); + + return result; +} + +/** + * gst_element_abort_state: + * @element: a #GstElement to abort the state of. + * + * Abort the state change of the element. This function is used + * by elements that do asynchronous state changes and find out + * something is wrong. + * + * This function should be called with the STATE_LOCK held. + * + * MT safe. */ void -gst_element_wait_state_change (GstElement * element) +gst_element_abort_state (GstElement * element) { - GST_STATE_LOCK (element); - GST_STATE_WAIT (element); - GST_STATE_UNLOCK (element); + /* implment me */ +} + +/** + * gst_element_commit_state: + * @element: a #GstElement to commit the state of. + * + * Commit the state change of the element. This function is used + * by elements that do asynchronous state changes. + * + * This function can only be called with the STATE_LOCK held. + * + * MT safe. + */ +void +gst_element_commit_state (GstElement * element) +{ + /* implement me */ +} + +/** + * gst_element_lost_state: + * @element: a #GstElement the state is lost of + * + * Brings the element to the lost state. The current state of the + * element is copied to the pending state so that any call to + * #gst_element_get_state() will return ASYNC. + * This is mostly used for elements that lost their preroll buffer + * in the PAUSED state after a flush, they become PAUSED again + * if a new preroll buffer is queued. + * This function can only be called when the element is currently + * not in error or an async state change. + * + * This function can only be called with the STATE_LOCK held. + * + * MT safe. + */ +void +gst_element_lost_state (GstElement * element) +{ + /* implement me */ } /** @@ -1931,187 +1717,38 @@ gst_element_wait_state_change (GstElement * element) * requested state by going through all the intermediary states and calling * the class's state change function for each. * - * Returns: TRUE if the state was successfully set. - * (using #GstElementStateReturn). + * Returns: Result of the state change using #GstElementStateReturn. + * + * MT safe. */ GstElementStateReturn gst_element_set_state (GstElement * element, GstElementState state) { - GstElementClass *klass = GST_ELEMENT_GET_CLASS (element); - GstElementStateReturn ret; - - g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_FAILURE); - GST_DEBUG_OBJECT (element, "setting state to %s", - gst_element_state_get_name (state)); - klass = GST_ELEMENT_GET_CLASS (element); - g_return_val_if_fail (klass->set_state, GST_STATE_FAILURE); - - /* a set_state function is mandatory */ - gst_object_ref (GST_OBJECT (element)); - ret = klass->set_state (element, state); - gst_object_unref (GST_OBJECT (element)); - - return ret; -} - -static GstElementStateReturn -gst_element_set_state_func (GstElement * element, GstElementState state) -{ - GstElementClass *oclass; - GstElementState curpending; - GstElementStateReturn return_val = GST_STATE_SUCCESS; - - oclass = GST_ELEMENT_GET_CLASS (element); - - /* start with the current state */ - curpending = GST_STATE (element); - - if (state == curpending) { - GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, - "element is already in requested state %s, returning", - gst_element_state_get_name (state)); - return GST_STATE_SUCCESS; - } - - /* reentrancy issues with signals in change_state) */ - gst_object_ref (GST_OBJECT (element)); - GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, "setting state from %s to %s", - gst_element_state_get_name (curpending), - gst_element_state_get_name (state)); - - /* loop until the final requested state is set */ - - while (GST_STATE (element) != state - && GST_STATE (element) != GST_STATE_VOID_PENDING) { - /* move the curpending state in the correct direction */ - if (curpending < state) - curpending <<= 1; - else - curpending >>= 1; - - /* set the pending state variable */ - GST_STATE_PENDING (element) = curpending; - - if (curpending != state) { - GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, - "intermediate: setting state from %s to %s", - gst_element_state_get_name (GST_STATE (element)), - gst_element_state_get_name (curpending)); - } else { - GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, element, - "start: setting current state %s again", - gst_element_state_get_name (GST_STATE (element))); - } - - /* call the state change function so it can set the state */ - if (oclass->change_state) - return_val = (oclass->change_state) (element); - - switch (return_val) { - case GST_STATE_FAILURE: - GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, - "have failed change_state return"); - goto exit; - case GST_STATE_ASYNC: - GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, - "element will change state async"); - goto exit; - case GST_STATE_SUCCESS: - /* Last thing we do is verify that a successful state change really - * did change the state... */ - /* if it did not, this is an error - fix the element that does this */ - if (GST_STATE (element) != curpending) { - g_warning ("element %s claimed state-change success," - "but state didn't change to %s. State is %s (%s pending), " - "fix the element", - GST_ELEMENT_NAME (element), - gst_element_state_get_name (curpending), - gst_element_state_get_name (GST_STATE (element)), - gst_element_state_get_name (GST_STATE_PENDING (element))); - return_val = GST_STATE_FAILURE; - goto exit; - } - break; - default: - /* somebody added a GST_STATE_ and forgot to do stuff here ! */ - g_assert_not_reached (); - } - } - -exit: - gst_object_unref (GST_OBJECT (element)); - - return return_val; + /* implement me */ + return GST_STATE_SUCCESS; } +/* is called with STATE_LOCK + * + * This function activates the pads of a given element. + * + * TODO: activate pads from src to sinks? + * move pad activate logic to GstPad because we also need this + * when pads are added to elements? + */ static gboolean -gst_element_negotiate_pads (GstElement * element) -{ - GList *pads; - - GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, element, "negotiating pads"); - - for (pads = GST_ELEMENT_PADS (element); pads; pads = g_list_next (pads)) { - GstPad *pad = GST_PAD (pads->data); - - if (!GST_IS_REAL_PAD (pad)) - continue; - - /* if we have a link on this pad and it doesn't have caps - * allready, try to negotiate */ - if (!gst_pad_is_negotiated (pad)) { - GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, element, - "perform negotiate for %s:%s", GST_DEBUG_PAD_NAME (pad)); - if (gst_pad_renegotiate (pad) == GST_PAD_LINK_REFUSED) - return FALSE; - } - } - - return TRUE; -} - -static void -gst_element_clear_pad_caps (GstElement * element) -{ - GList *pads = GST_ELEMENT_PADS (element); - - GST_CAT_DEBUG_OBJECT (GST_CAT_CAPS, element, "clearing pad caps"); - - while (pads) { - GstPad *pad = GST_PAD (pads->data); - - gst_pad_unnegotiate (pad); - if (GST_IS_REAL_PAD (pad)) { - gst_caps_replace (&GST_RPAD_EXPLICIT_CAPS (pad), NULL); - } - - pads = g_list_next (pads); - } -} - -static void gst_element_pads_activate (GstElement * element, gboolean active) { - GList *pads = element->pads; - - while (pads) { - GstPad *pad = GST_PAD (pads->data); - - pads = g_list_next (pads); - - if (!GST_IS_REAL_PAD (pad)) - continue; - - gst_pad_set_active (pad, active); - } + return FALSE; } +/* is called with STATE_LOCK */ static GstElementStateReturn gst_element_change_state (GstElement * element) { - GstElementState old_state, old_pending; - GstObject *parent; - gint old_transition; + GstElementState old_state; + gint old_pending, old_transition; + GstElementStateReturn result = GST_STATE_SUCCESS; g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_FAILURE); @@ -2128,56 +1765,36 @@ gst_element_change_state (GstElement * element) return GST_STATE_SUCCESS; } - /* we need to ref the object because of reentrancy issues with the signal - * handlers (including those in pads and gst_bin_child_state_change */ - gst_object_ref (GST_OBJECT (element)); GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "default handler tries setting state from %s to %s (%04x)", gst_element_state_get_name (old_state), gst_element_state_get_name (old_pending), old_transition); - /* we set the state change early for the negotiation functions */ - GST_STATE (element) = old_pending; - GST_STATE_PENDING (element) = GST_STATE_VOID_PENDING; - switch (old_transition) { - case GST_STATE_PLAYING_TO_PAUSED: - if (element->clock) { - GstClockTimeDiff time = GST_CLOCK_TIME_NONE; //gst_clock_get_event_time (element->clock); - - g_assert (time >= element->base_time); - element->base_time = time - element->base_time; - GST_CAT_LOG_OBJECT (GST_CAT_CLOCK, element, "setting base time to %" - G_GINT64_FORMAT, element->base_time); + case GST_STATE_NULL_TO_READY: + break; + case GST_STATE_READY_TO_PAUSED: + if (!gst_element_pads_activate (element, TRUE)) { + result = GST_STATE_FAILURE; } - gst_element_pads_activate (element, FALSE); break; case GST_STATE_PAUSED_TO_PLAYING: - gst_element_pads_activate (element, TRUE); - if (element->clock) { - GstClockTime time = GST_CLOCK_TIME_NONE; //gst_clock_get_event_time (element->clock); - - element->base_time = time - element->base_time; - GST_CAT_LOG_OBJECT (GST_CAT_CLOCK, element, "setting base time to %" - GST_TIME_FORMAT, GST_TIME_ARGS (element->base_time)); + GST_LOCK (element); + if (GST_ELEMENT_MANAGER (element)) { + element->base_time = + GST_ELEMENT_CAST (GST_ELEMENT_MANAGER (element))->base_time; } + GST_UNLOCK (element); break; - /* if we are going to paused, we try to negotiate the pads */ - case GST_STATE_READY_TO_PAUSED: - g_assert (element->base_time == 0); - if (!gst_element_negotiate_pads (element)) { - GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, - "failed state change, could not negotiate pads"); - goto failure; - } + case GST_STATE_PLAYING_TO_PAUSED: break; - /* going to the READY state clears all pad caps */ - /* FIXME: Why doesn't this happen on READY => NULL? -- Company */ case GST_STATE_PAUSED_TO_READY: - element->base_time = 0; - gst_element_clear_pad_caps (element); + if (!gst_element_pads_activate (element, FALSE)) { + result = GST_STATE_FAILURE; + } else { + element->base_time = 0; + } break; - case GST_STATE_NULL_TO_READY: case GST_STATE_READY_TO_NULL: break; default: @@ -2192,46 +1809,7 @@ gst_element_change_state (GstElement * element) break; } - parent = GST_ELEMENT_PARENT (element); - - GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, - "signaling state change from %s to %s", - gst_element_state_get_name (old_state), - gst_element_state_get_name (GST_STATE (element))); - - /* tell the scheduler if we have one */ - if (GST_ELEMENT_SCHEDULER (element)) { - if (gst_scheduler_state_transition (GST_ELEMENT_SCHEDULER (element), - element, old_transition) != GST_STATE_SUCCESS) { - GST_CAT_INFO_OBJECT (GST_CAT_STATES, element, - "scheduler could not change state"); - goto failure; - } - } - - /* tell our parent about the state change */ - if (parent && GST_IS_BIN (parent)) { - gst_bin_child_state_change (GST_BIN (parent), old_state, - GST_STATE (element), element); - } - /* at this point the state of the element could have changed again */ - - g_signal_emit (G_OBJECT (element), gst_element_signals[STATE_CHANGE], - 0, old_state, GST_STATE (element)); - - /* signal the state change in case somebody is waiting for us */ - GST_STATE_BROADCAST (element); - - gst_object_unref (GST_OBJECT (element)); - return GST_STATE_SUCCESS; - -failure: - /* undo the state change */ - GST_STATE (element) = old_state; - GST_STATE_PENDING (element) = old_pending; - gst_object_unref (GST_OBJECT (element)); - - return GST_STATE_FAILURE; + return result; } /** @@ -2257,7 +1835,8 @@ gst_element_dispose (GObject * object) GST_CAT_INFO_OBJECT (GST_CAT_REFCOUNTING, element, "dispose"); - gst_element_set_state (element, GST_STATE_NULL); + /* ref so we don't hit 0 again */ + gst_object_ref (GST_OBJECT (object)); /* first we break all our links with the ouside */ while (element->pads) { @@ -2269,7 +1848,7 @@ gst_element_dispose (GObject * object) } GST_LOCK (element); - gst_object_replace ((GstObject **) & element->scheduler, NULL); + gst_object_replace ((GstObject **) & element->manager, NULL); gst_object_replace ((GstObject **) & element->clock, NULL); GST_UNLOCK (element); @@ -2432,38 +2011,141 @@ gst_element_restore_thyself (GstObject * object, xmlNodePtr self) } #endif /* GST_DISABLE_LOADSAVE */ -/** - * gst_element_yield: - * @element: a #GstElement to yield. - * - * Requests a yield operation for the element. The scheduler will typically - * give control to another element. - */ -void -gst_element_yield (GstElement * element) +static void +gst_element_set_manager_func (GstElement * element, GstPipeline * manager) { - if (GST_ELEMENT_SCHEDULER (element)) { - gst_scheduler_yield (GST_ELEMENT_SCHEDULER (element), element); - } + g_return_if_fail (GST_IS_ELEMENT (element)); + + GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, element, "setting manager to %p", + manager); + + /* setting the manager cannot increase the refcount */ + GST_LOCK (element); + GST_ELEMENT_MANAGER (element) = manager; + GST_UNLOCK (element); +} + +static void +gst_element_set_bus_func (GstElement * element, GstBus * bus) +{ + g_return_if_fail (GST_IS_ELEMENT (element)); + + GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, element, "setting bus to %p", bus); + + GST_LOCK (element); + gst_object_replace ((GstObject **) & GST_ELEMENT_BUS (element), + GST_OBJECT (bus)); + GST_UNLOCK (element); +} + +static void +gst_element_set_scheduler_func (GstElement * element, GstScheduler * scheduler) +{ + g_return_if_fail (GST_IS_ELEMENT (element)); + + GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, element, "setting scheduler to %p", + scheduler); + + GST_LOCK (element); + gst_object_replace ((GstObject **) & GST_ELEMENT_SCHEDULER (element), + GST_OBJECT (scheduler)); + GST_UNLOCK (element); } /** - * gst_element_interrupt: - * @element: a #GstElement to interrupt. + * gst_element_set_manager: + * @element: a #GstElement to set the manager of. + * @manager: the #GstManager to set. * - * Requests the scheduler of this element to interrupt the execution of - * this element and scheduler another one. + * Sets the manager of the element. For internal use only, unless you're + * writing a new bin subclass. * - * Returns: TRUE if the element should exit its chain/loop/get - * function ASAP, depending on the scheduler implementation. + * MT safe. */ -gboolean -gst_element_interrupt (GstElement * element) +void +gst_element_set_manager (GstElement * element, GstPipeline * manager) { - if (GST_ELEMENT_SCHEDULER (element)) { - return gst_scheduler_interrupt (GST_ELEMENT_SCHEDULER (element), element); - } else - return TRUE; + GstElementClass *oclass; + + g_return_if_fail (GST_IS_ELEMENT (element)); + + oclass = GST_ELEMENT_GET_CLASS (element); + + if (oclass->set_manager) + oclass->set_manager (element, manager); +} + + +/** + * gst_element_get_manager: + * @element: a #GstElement to get the manager of. + * + * Returns the manager of the element. + * + * Returns: the element's #GstPipeline. unref after usage. + * + * MT safe. + */ +GstPipeline * +gst_element_get_manager (GstElement * element) +{ + GstPipeline *result = NULL; + + g_return_val_if_fail (GST_IS_ELEMENT (element), result); + + GST_LOCK (element); + result = GST_ELEMENT_MANAGER (element); + gst_object_ref (GST_OBJECT (result)); + GST_UNLOCK (element); + + return result; +} + +/** + * gst_element_set_bus: + * @element: a #GstElement to set the bus of. + * @bus: the #GstBus to set. + * + * Sets the bus of the element. For internal use only, unless you're + * testing elements. + * + * MT safe. + */ +void +gst_element_set_bus (GstElement * element, GstBus * bus) +{ + GstElementClass *oclass; + + g_return_if_fail (GST_IS_ELEMENT (element)); + + oclass = GST_ELEMENT_GET_CLASS (element); + + if (oclass->set_bus) + oclass->set_bus (element, bus); +} + +/** + * gst_element_get_bus: + * @element: a #GstElement to get the bus of. + * + * Returns the bus of the element. + * + * Returns: the element's #GstBus. + * + * MT safe. + */ +GstBus * +gst_element_get_bus (GstElement * element) +{ + GstBus *result = NULL; + + g_return_val_if_fail (GST_IS_ELEMENT (element), result); + + GST_LOCK (element); + result = GST_ELEMENT_BUS (element); + GST_UNLOCK (element); + + return result; } /** @@ -2512,183 +2194,3 @@ gst_element_get_scheduler (GstElement * element) return result; } - -/** - * gst_element_set_loop_function: - * @element: a #GstElement to set the loop function of. - * @loop: Pointer to #GstElementLoopFunction. - * - * This sets the loop function for the element. The function pointed to - * can deviate from the GstElementLoopFunction definition in type of - * pointer only. - * - * NOTE: in order for this to take effect, the current loop function *must* - * exit. Assuming the loop function itself is the only one who will cause - * a new loopfunc to be assigned, this should be no problem. - */ -void -gst_element_set_loop_function (GstElement * element, - GstElementLoopFunction loop) -{ - gboolean need_notify = FALSE; - - g_return_if_fail (GST_IS_ELEMENT (element)); - - /* if the element changed from loop based to chain/get based - * or vice versa, we need to inform the scheduler about that */ - if ((element->loopfunc == NULL && loop != NULL) || - (element->loopfunc != NULL && loop == NULL)) { - need_notify = TRUE; - } - - /* set the loop function */ - element->loopfunc = loop; - - if (need_notify) { - /* set the NEW_LOOPFUNC flag so everyone knows to go try again */ - GST_FLAG_SET (element, GST_ELEMENT_NEW_LOOPFUNC); - - if (GST_ELEMENT_SCHEDULER (element)) { - gst_scheduler_scheduling_change (GST_ELEMENT_SCHEDULER (element), - element); - } - } -} -static inline void -gst_element_emit_found_tag (GstElement * element, GstElement * source, - const GstTagList * tag_list) -{ - gst_object_ref (GST_OBJECT (element)); - g_signal_emit (element, gst_element_signals[FOUND_TAG], 0, source, tag_list); - gst_object_unref (GST_OBJECT (element)); -} -static void -gst_element_found_tag_func (GstElement * element, GstElement * source, - const GstTagList * tag_list) -{ - /* tell the parent */ - if (GST_OBJECT_PARENT (element)) { - GST_CAT_LOG_OBJECT (GST_CAT_EVENT, element, "forwarding tag event to %s", - GST_OBJECT_NAME (GST_OBJECT_PARENT (element))); - gst_element_emit_found_tag (GST_ELEMENT (GST_OBJECT_PARENT (element)), - source, tag_list); - } -} - -/** - * gst_element_found_tags: - * @element: the element that found the tags - * @tag_list: the found tags - * - * This function emits the found_tags signal. This is a recursive signal, so - * every parent will emit that signal, too, before this function returns. - * Only emit this signal, when you extracted these tags out of the data stream, - * not when you handle an event. - */ -void -gst_element_found_tags (GstElement * element, const GstTagList * tag_list) -{ - gst_element_emit_found_tag (element, element, tag_list); -} - -/** - * gst_element_found_tags_for_pad: - * @element: element that found the tag - * @pad: src pad the tags correspond to - * @timestamp: time the tags were found - * @list: the taglist - * - * This is a convenience routine for tag finding. Most of the time you only - * want to push the found tags down one pad, in that case this function is for - * you. It takes ownership of the taglist, emits the found-tag signal and - * pushes a tag event down the pad. - * This function may not be used in a #GstPadGetFunction, because it calls - * gst_pad_push(). In those functions, call gst_element_found_tags(), create a - * tag event with gst_event_new_tag() and return that from your - * #GstPadGetFunction. - */ -void -gst_element_found_tags_for_pad (GstElement * element, GstPad * pad, - GstClockTime timestamp, GstTagList * list) -{ - GstEvent *tag_event; - - g_return_if_fail (GST_IS_ELEMENT (element)); - g_return_if_fail (GST_IS_REAL_PAD (pad)); - g_return_if_fail (GST_PAD_DIRECTION (pad) == GST_PAD_SRC); - g_return_if_fail (element == GST_PAD_PARENT (pad)); - g_return_if_fail (list != NULL); - - tag_event = gst_event_new_tag (list); - GST_EVENT_TIMESTAMP (tag_event) = timestamp; - gst_element_found_tags (element, gst_event_tag_get_list (tag_event)); - if (GST_PAD_IS_USABLE (pad)) { - gst_pad_push (pad, GST_DATA (tag_event)); - } else { - gst_data_unref (GST_DATA (tag_event)); - } -} - -static inline void -gst_element_set_eos_recursive (GstElement * element) -{ - /* this function is only called, when we were in PLAYING before. So every - parent that's PAUSED was PLAYING before. That means it has reached EOS. */ - GstElement *parent; - - GST_CAT_DEBUG (GST_CAT_EVENT, "setting recursive EOS on %s", - GST_OBJECT_NAME (element)); - g_signal_emit (G_OBJECT (element), gst_element_signals[EOS], 0); - - if (!GST_OBJECT_PARENT (element)) - return; - - parent = GST_ELEMENT (GST_OBJECT_PARENT (element)); - if (GST_STATE (parent) == GST_STATE_PAUSED) - gst_element_set_eos_recursive (parent); -} - -/** - * gst_element_set_eos: - * @element: a #GstElement to set to the EOS state. - * - * Perform the actions needed to bring the element in the EOS state. - */ -void -gst_element_set_eos (GstElement * element) -{ - g_return_if_fail (GST_IS_ELEMENT (element)); - - GST_CAT_DEBUG (GST_CAT_EVENT, "setting EOS on element %s", - GST_OBJECT_NAME (element)); - - if (GST_STATE (element) == GST_STATE_PLAYING) { - gst_element_set_state (element, GST_STATE_PAUSED); - gst_element_set_eos_recursive (element); - } else { - g_signal_emit (G_OBJECT (element), gst_element_signals[EOS], 0); - } -} - -/** - * gst_element_get_managing_bin: - * @element: a #GstElement to get the managing bin of. - * - * Gets the managing bin (a pipeline or a thread, for example) of an element. - * - * Returns: the #GstBin, or NULL on failure. - **/ -GstBin * -gst_element_get_managing_bin (GstElement * element) -{ - GstBin *bin; - - g_return_val_if_fail (element != NULL, NULL); - - bin = GST_BIN (gst_object_get_parent (GST_OBJECT (element))); - - while (bin && !GST_FLAG_IS_SET (GST_OBJECT (bin), GST_BIN_FLAG_MANAGER)) - bin = GST_BIN (gst_object_get_parent (GST_OBJECT (bin))); - - return bin; -} diff --git a/gst/gstelement.h b/gst/gstelement.h index b611317b40..2d71f2f481 100644 --- a/gst/gstelement.h +++ b/gst/gstelement.h @@ -33,6 +33,7 @@ #include #include #include +#include #include G_BEGIN_DECLS @@ -105,17 +106,6 @@ GST_EXPORT GType _gst_element_type; typedef enum { - /* input and output pads aren't directly coupled to each other - examples: queues, multi-output async readers, etc. */ - GST_ELEMENT_DECOUPLED, - /* this element, for some reason, has a loop function that performs - * an infinite loop without calls to gst_element_yield () */ - GST_ELEMENT_INFINITE_LOOP, - /* there is a new loopfunction ready for placement */ - GST_ELEMENT_NEW_LOOPFUNC, - /* if this element can handle events */ - GST_ELEMENT_EVENT_AWARE, - /* private flags that can be used by the scheduler */ GST_ELEMENT_SCHEDULER_PRIVATE1, GST_ELEMENT_SCHEDULER_PRIVATE2, @@ -123,20 +113,16 @@ typedef enum /* ignore state changes from parent */ GST_ELEMENT_LOCKED_STATE, - /* element is in error */ - GST_ELEMENT_IN_ERROR, - /* use some padding for future expansion */ GST_ELEMENT_FLAG_LAST = GST_OBJECT_FLAG_LAST + 16 } GstElementFlags; -#define GST_ELEMENT_IS_THREAD_SUGGESTED(obj) (GST_FLAG_IS_SET(obj,GST_ELEMENT_THREAD_SUGGESTED)) -#define GST_ELEMENT_IS_EVENT_AWARE(obj) (GST_FLAG_IS_SET(obj,GST_ELEMENT_EVENT_AWARE)) -#define GST_ELEMENT_IS_DECOUPLED(obj) (GST_FLAG_IS_SET(obj,GST_ELEMENT_DECOUPLED)) #define GST_ELEMENT_IS_LOCKED_STATE(obj) (GST_FLAG_IS_SET(obj,GST_ELEMENT_LOCKED_STATE)) #define GST_ELEMENT_NAME(obj) (GST_OBJECT_NAME(obj)) -#define GST_ELEMENT_PARENT(obj) (GST_OBJECT_PARENT(obj)) +#define GST_ELEMENT_PARENT(obj) (GST_ELEMENT_CAST(GST_OBJECT_PARENT(obj))) +#define GST_ELEMENT_MANAGER(obj) (GST_ELEMENT_CAST(obj)->manager) +#define GST_ELEMENT_BUS(obj) (GST_ELEMENT_CAST(obj)->bus) #define GST_ELEMENT_SCHEDULER(obj) (GST_ELEMENT_CAST(obj)->scheduler) #define GST_ELEMENT_CLOCK(obj) (GST_ELEMENT_CAST(obj)->clock) #define GST_ELEMENT_PADS(obj) (GST_ELEMENT_CAST(obj)->pads) @@ -153,23 +139,32 @@ typedef enum * data processing error. The pipeline will throw an error signal and the * application will be requested to stop further media processing. */ -#define GST_ELEMENT_ERROR(el, domain, code, message, debug) \ -G_STMT_START { \ - gchar *__msg = _gst_element_error_printf message; \ - gchar *__dbg = _gst_element_error_printf debug; \ - if (__msg) \ - GST_ERROR_OBJECT (el, "%s", __msg); \ - if (__dbg) \ - GST_ERROR_OBJECT (el, "%s", __dbg); \ - gst_element_error_full (GST_ELEMENT(el), \ - GST_ ## domain ## _ERROR, GST_ ## domain ## _ERROR_ ## code, \ - __msg, __dbg, __FILE__, GST_FUNCTION, __LINE__); \ +#define GST_ELEMENT_ERROR(el, domain, code, text, debug) \ +G_STMT_START { \ + gchar *__txt = _gst_element_error_printf text; \ + gchar *__dbg = _gst_element_error_printf debug; \ + if (__txt) \ + GST_ERROR_OBJECT (el, "%s", __txt); \ + if (__dbg) \ + GST_ERROR_OBJECT (el, "%s", __dbg); \ + gst_element_message_full (GST_ELEMENT(el), GST_MESSAGE_ERROR, \ + GST_ ## domain ## _ERROR, GST_ ## domain ## _ERROR_ ## code, \ + __txt, __dbg, __FILE__, GST_FUNCTION, __LINE__); \ } G_STMT_END -typedef struct _GstElementFactory GstElementFactory; -typedef struct _GstElementFactoryClass GstElementFactoryClass; - -typedef void (*GstElementLoopFunction) (GstElement *element); +/* log a (non-fatal) warning message and post it on the bus */ +#define GST_ELEMENT_WARNING(el, domain, code, text, debug) \ +G_STMT_START { \ + gchar *__txt = _gst_element_error_printf text; \ + gchar *__dbg = _gst_element_error_printf debug; \ + if (__txt) \ + GST_WARNING_OBJECT (el, "%s", __txt); \ + if (__dbg) \ + GST_WARNING_OBJECT (el, "%s", __dbg); \ + gst_element_message_full (GST_ELEMENT(el), GST_MESSAGE_WARNING, \ + GST_ ## domain ## _ERROR, GST_ ## domain ## _ERROR_ ## code, \ + __txt, __dbg, __FILE__, GST_FUNCTION, __LINE__); \ +} G_STMT_END /* the state change mutexes and conds */ #define GST_STATE_GET_LOCK(elem) (GST_ELEMENT_CAST(elem)->state_lock) @@ -183,6 +178,9 @@ typedef void (*GstElementLoopFunction) (GstElement *element); #define GST_STATE_SIGNAL(elem) g_cond_signal (GST_STATE_GET_COND (elem)); #define GST_STATE_BROADCAST(elem) g_cond_broadcast (GST_STATE_GET_COND (elem)); +typedef struct _GstElementFactory GstElementFactory; +typedef struct _GstElementFactoryClass GstElementFactoryClass; + struct _GstElement { GstObject object; @@ -196,8 +194,9 @@ struct _GstElement gboolean state_error; /* flag is set when the element has an error in the last state change. it is cleared when doing another state change. */ /*< public >*/ /* with LOCK */ - /* scheduling */ - GstElementLoopFunction loopfunc; + /* element manager */ + GstPipeline *manager; + GstBus *bus; GstScheduler *scheduler; /* private pointer for the scheduler */ gpointer sched_private; @@ -241,9 +240,6 @@ struct _GstElementClass void (*new_pad) (GstElement *element, GstPad *pad); void (*pad_removed) (GstElement *element, GstPad *pad); void (*no_more_pads) (GstElement *element); - void (*error) (GstElement *element, GstElement *source, GError *error, gchar *debug); - void (*eos) (GstElement *element); - void (*found_tag) (GstElement *element, GstElement *source, const GstTagList *tag_list); /*< protected >*/ /* vtable */ @@ -253,11 +249,13 @@ struct _GstElementClass void (*release_pad) (GstElement *element, GstPad *pad); /* state changes */ + GstElementStateReturn (*get_state) (GstElement * element, GstElementState * state, + GstElementState * pending, GTimeVal * timeout); GstElementStateReturn (*change_state) (GstElement *element); - GstElementStateReturn (*set_state) (GstElement *element, GstElementState state); - /* scheduling */ - gboolean (*release_locks) (GstElement *element); + /* manager */ + void (*set_manager) (GstElement * element, GstPipeline * pipeline); + void (*set_bus) (GstElement * element, GstBus * bus); void (*set_scheduler) (GstElement *element, GstScheduler *scheduler); /* set/get clocks */ @@ -304,29 +302,19 @@ gboolean gst_element_requires_clock (GstElement *element); gboolean gst_element_provides_clock (GstElement *element); GstClock* gst_element_get_clock (GstElement *element); void gst_element_set_clock (GstElement *element, GstClock *clock); -GstClockReturn gst_element_clock_wait (GstElement *element, - GstClockID id, GstClockTimeDiff *jitter); -GstClockTime gst_element_get_time (GstElement *element); -gboolean gst_element_wait (GstElement *element, GstClockTime timestamp); -void gst_element_set_time (GstElement *element, GstClockTime time); -void gst_element_set_time_delay (GstElement *element, GstClockTime time, GstClockTime delay); - -void gst_element_adjust_time (GstElement *element, GstClockTimeDiff diff); /* indexes */ gboolean gst_element_is_indexable (GstElement *element); void gst_element_set_index (GstElement *element, GstIndex *index); GstIndex* gst_element_get_index (GstElement *element); -/* scheduling */ -void gst_element_set_loop_function (GstElement *element, - GstElementLoopFunction loop); -gboolean gst_element_release_locks (GstElement *element); -void gst_element_yield (GstElement *element); -gboolean gst_element_interrupt (GstElement *element); +/* manager and tasks */ +void gst_element_set_manager (GstElement * element, GstPipeline * pipeline); +GstPipeline *gst_element_get_manager (GstElement * element); +void gst_element_set_bus (GstElement * element, GstBus * bus); +GstBus *gst_element_get_bus (GstElement * element); void gst_element_set_scheduler (GstElement *element, GstScheduler *sched); GstScheduler* gst_element_get_scheduler (GstElement *element); -GstBin* gst_element_get_managing_bin (GstElement *element); /* pad management */ gboolean gst_element_add_pad (GstElement *element, GstPad *pad); @@ -357,34 +345,33 @@ gboolean gst_element_convert (GstElement *element, GstFormat src_format, gint64 src_value, GstFormat *dest_format, gint64 *dest_value); +/* messages */ +gboolean gst_element_post_message (GstElement * element, GstMessage * message); + /* error handling */ gchar * _gst_element_error_printf (const gchar *format, ...); -void gst_element_error_full (GstElement *element, GQuark domain, gint code, - gchar *message, gchar *debug, - const gchar *file, const gchar *function, gint line); -void gst_element_default_error (GObject *object, GstObject *orig, GError *error, gchar *debug); -#define gst_element_default_deep_notify gst_object_default_deep_notify +void gst_element_message_full (GstElement * element, GstMessageType type, + GQuark domain, gint code, gchar * text, gchar * debug, const gchar * file, + const gchar * function, gint line); /* state management */ gboolean gst_element_is_locked_state (GstElement *element); gboolean gst_element_set_locked_state (GstElement *element, gboolean locked_state); gboolean gst_element_sync_state_with_parent (GstElement *element); -GstElementState gst_element_get_state (GstElement *element); +GstElementStateReturn gst_element_get_state (GstElement * element, + GstElementState * state, + GstElementState * pending, + GTimeVal * timeout); GstElementStateReturn gst_element_set_state (GstElement *element, GstElementState state); -void gst_element_wait_state_change (GstElement *element); - -void gst_element_set_eos (GstElement *element); +void gst_element_abort_state (GstElement * element); +void gst_element_commit_state (GstElement * element); +void gst_element_lost_state (GstElement * element); /* factory management */ GstElementFactory* gst_element_get_factory (GstElement *element); - -/* misc */ -void gst_element_found_tags (GstElement *element, const GstTagList *tag_list); -void gst_element_found_tags_for_pad (GstElement *element, GstPad *pad, GstClockTime timestamp, - GstTagList *list); /* * * factories stuff @@ -446,15 +433,10 @@ GstElement* gst_element_factory_create (GstElementFactory *factory, const gchar *name); GstElement* gst_element_factory_make (const gchar *factoryname, const gchar *name); -gboolean gst_element_factory_can_src_caps (GstElementFactory *factory, - const GstCaps *caps); -gboolean gst_element_factory_can_sink_caps (GstElementFactory *factory, - const GstCaps *caps); - -void __gst_element_factory_add_pad_template (GstElementFactory *elementfactory, - GstPadTemplate *templ); -void __gst_element_factory_add_interface (GstElementFactory *elementfactory, - const gchar *interfacename); +void __gst_element_factory_add_pad_template (GstElementFactory *elementfactory, + GstPadTemplate *templ); +void __gst_element_factory_add_interface (GstElementFactory *elementfactory, + const gchar *interfacename); G_END_DECLS diff --git a/gst/gstevent.c b/gst/gstevent.c index 5a64088b20..b91be01157 100644 --- a/gst/gstevent.c +++ b/gst/gstevent.c @@ -25,7 +25,6 @@ #include "gst_private.h" #include "gstdata_private.h" -#include "gstclock.h" #include "gstinfo.h" #include "gstmemchunk.h" #include "gstevent.h" @@ -347,9 +346,9 @@ gst_event_new_segment_seek (GstSeekType type, gint64 start, gint64 stop) { GstEvent *event; - g_return_val_if_fail (start < stop, NULL); + g_return_val_if_fail (start < stop || stop == -1, NULL); - event = gst_event_new (GST_EVENT_SEEK_SEGMENT); + event = gst_event_new (GST_EVENT_SEEK); GST_EVENT_SEEK_TYPE (event) = type; GST_EVENT_SEEK_OFFSET (event) = start; @@ -359,70 +358,20 @@ gst_event_new_segment_seek (GstSeekType type, gint64 start, gint64 stop) } /** - * gst_event_new_filler_stamped: - * @time: timestamp of the filler, in nanoseconds. - * @duration: duration of the filler, in nanoseconds. + * gst_event_new_flush: + * @done: Indicates the end of the flush * - * Creates "filler" data, which is basically empty data that is used to - * synchronize streams if one stream has no data for a while. This is - * used to prevent deadlocks. + * Allocate a new flush event. * - * Returns: the newly created event. + * Returns: A new flush event. */ - GstEvent * -gst_event_new_filler_stamped (guint64 time, guint64 duration) +gst_event_new_flush (gboolean done) { - GstEvent *event = gst_event_new_filler (); + GstEvent *event; - GST_EVENT_TIMESTAMP (event) = time; - if (GST_CLOCK_TIME_IS_VALID (duration)) { - GValue value = { 0 }; - - event->event_data.structure.structure = - gst_structure_new ("application/x-gst-filler", NULL); - g_value_init (&value, G_TYPE_UINT64); - g_value_set_uint64 (&value, duration); - gst_structure_set_value (event->event_data.structure.structure, - "duration", &value); - g_value_unset (&value); - } + event = gst_event_new (GST_EVENT_FLUSH); + GST_EVENT_FLUSH_DONE (event) = done; return event; } - -/** - * gst_event_filler_get_duration: - * @event: the event to get the duration from. - * - * Filler events are used to synchronize streams (and thereby prevent - * application deadlocks) if one stream receives no data for a while. - * This function gets the duration of a filler event, which is the - * amount of time from the start of this event (see GST_EVENT_TIMESTAMP()) - * that no data is available. - * - * Returns: duration of the lack of data, or GST_CLOCK_TIME_NONE. - */ - -guint64 -gst_event_filler_get_duration (GstEvent * event) -{ - const GValue *value; - - g_return_val_if_fail (event != NULL, GST_CLOCK_TIME_NONE); - g_return_val_if_fail (GST_EVENT_TYPE (event) == GST_EVENT_FILLER, - GST_CLOCK_TIME_NONE); - - /* check the event */ - if (!event->event_data.structure.structure) - return GST_CLOCK_TIME_NONE; - value = gst_structure_get_value (event->event_data.structure.structure, - "duration"); - if (!value) - return GST_CLOCK_TIME_NONE; - g_return_val_if_fail (G_VALUE_TYPE (value) == G_TYPE_UINT64, - GST_CLOCK_TIME_NONE); - - /* return */ - return g_value_get_uint64 (value); -} diff --git a/gst/gstevent.h b/gst/gstevent.h index b476ba972f..921da90d86 100644 --- a/gst/gstevent.h +++ b/gst/gstevent.h @@ -34,24 +34,30 @@ G_BEGIN_DECLS GST_EXPORT GType _gst_event_type; +/** + * GstEventType: + * @GST_EVENT_UNKNOWN: + * @GST_EVENT_EOS: + * @GST_EVENT_FLUSH: + * @GST_EVENT_DISCONTINUOUS: + * @GST_EVENT_QOS: + * @GST_EVENT_SEEK: + * @GST_EVENT_SIZE: + * @GST_EVENT_RATE: + * @GST_EVENT_NAVIGATION: + * @GST_EVENT_TAG: + */ typedef enum { GST_EVENT_UNKNOWN = 0, GST_EVENT_EOS = 1, GST_EVENT_FLUSH = 2, - GST_EVENT_EMPTY = 3, - GST_EVENT_DISCONTINUOUS = 4, - /*GST_EVENT_NEW_MEDIA = 5, <- removed */ - GST_EVENT_QOS = 6, - GST_EVENT_SEEK = 7, - GST_EVENT_SEEK_SEGMENT = 8, - GST_EVENT_SEGMENT_DONE = 9, - GST_EVENT_SIZE = 10, - GST_EVENT_RATE = 11, - GST_EVENT_FILLER = 12, - GST_EVENT_TS_OFFSET = 13, - GST_EVENT_INTERRUPT = 14, - GST_EVENT_NAVIGATION = 15, - GST_EVENT_TAG = 16 + GST_EVENT_DISCONTINUOUS = 3, + GST_EVENT_QOS = 4, + GST_EVENT_SEEK = 5, + GST_EVENT_SIZE = 8, + GST_EVENT_RATE = 9, + GST_EVENT_NAVIGATION = 10, + GST_EVENT_TAG = 11 } GstEventType; #define GST_EVENT_ANY GST_EVENT_NAVIGATION @@ -87,7 +93,6 @@ typedef struct GstEventFlag flags; } GstEventMask; -#ifndef GST_DISABLE_DEPRECATED #ifdef G_HAVE_ISO_VARARGS #define GST_EVENT_MASK_FUNCTION(type,functionname, ...) \ static const GstEventMask* \ @@ -111,7 +116,6 @@ functionname (type pad) \ return masks; \ } #endif -#endif /* seek events, extends GstEventFlag */ typedef enum { @@ -153,6 +157,8 @@ typedef struct #define GST_EVENT_DISCONT_OFFSET(event,i) (GST_EVENT(event)->event_data.discont.offsets[i]) #define GST_EVENT_DISCONT_OFFSET_LEN(event) (GST_EVENT(event)->event_data.discont.noffsets) +#define GST_EVENT_FLUSH_DONE(event) (GST_EVENT(event)->event_data.flush.done) + #define GST_EVENT_SIZE_FORMAT(event) (GST_EVENT(event)->event_data.size.format) #define GST_EVENT_SIZE_VALUE(event) (GST_EVENT(event)->event_data.size.value) @@ -178,6 +184,9 @@ struct _GstEvent { gint noffsets; gboolean new_media; } discont; + struct { + gboolean done; + } flush; struct { GstFormat format; gint64 value; @@ -226,12 +235,9 @@ GstEvent* gst_event_new_discontinuous_valist (gboolean new_media, gboolean gst_event_discont_get_value (GstEvent *event, GstFormat format, gint64 *value); #define gst_event_new_filler() gst_event_new(GST_EVENT_FILLER) -GstEvent* gst_event_new_filler_stamped (guint64 time, - guint64 duration); -guint64 gst_event_filler_get_duration (GstEvent *event); /* flush events */ -#define gst_event_new_flush() gst_event_new(GST_EVENT_FLUSH) +GstEvent* gst_event_new_flush (gboolean done); G_END_DECLS diff --git a/gst/gstmessage.c b/gst/gstmessage.c new file mode 100644 index 0000000000..5aa38f8d74 --- /dev/null +++ b/gst/gstmessage.c @@ -0,0 +1,404 @@ +/* GStreamer + * Copyright (C) 2004 Wim Taymans + * + * gstmessage.c: GstMessage subsystem + * + * 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 /* memcpy */ + +#include "gst_private.h" +#include "gstdata_private.h" +#include "gstinfo.h" +#include "gstmemchunk.h" +#include "gstmessage.h" +#include "gsttag.h" + +#ifndef GST_DISABLE_TRACE +/* #define GST_WITH_ALLOC_TRACE */ +#include "gsttrace.h" +static GstAllocTrace *_message_trace; +#endif + +static GstMemChunk *chunk; + +/* #define MEMPROF */ + +GType _gst_message_type; + +void +_gst_message_initialize (void) +{ + GST_CAT_INFO (GST_CAT_GST_INIT, "init messages"); + + /* register the type */ + _gst_message_type = g_boxed_type_register_static ("GstMessage", + (GBoxedCopyFunc) gst_data_copy, (GBoxedFreeFunc) gst_data_unref); + +#ifndef GST_DISABLE_TRACE + _message_trace = gst_alloc_trace_register (GST_MESSAGE_TRACE_NAME); +#endif + + chunk = gst_mem_chunk_new ("GstMessageChunk", sizeof (GstMessage), + sizeof (GstMessage) * 50, 0); +} + +static GstMessage * +_gst_message_copy (GstMessage * message) +{ + GstMessage *copy; + + GST_CAT_INFO (GST_CAT_MESSAGE, "copy message %p", message); + + copy = gst_mem_chunk_alloc (chunk); +#ifndef GST_DISABLE_TRACE + gst_alloc_trace_new (_message_trace, copy); +#endif + + memcpy (copy, message, sizeof (GstMessage)); + if (GST_MESSAGE_SRC (copy)) { + gst_object_ref (GST_MESSAGE_SRC (copy)); + } + + if (copy->structure) + copy->structure = gst_structure_copy (copy->structure); + + return copy; +} + +static void +_gst_message_free (GstMessage * message) +{ + GST_CAT_INFO (GST_CAT_MESSAGE, "freeing message %p", message); + + if (GST_MESSAGE_SRC (message)) { + gst_object_unref (GST_MESSAGE_SRC (message)); + } + + if (message->lock) { + GST_MESSAGE_LOCK (message); + GST_MESSAGE_SIGNAL (message); + GST_MESSAGE_UNLOCK (message); + } + + if (message->structure) + gst_structure_free (message->structure); + + _GST_DATA_DISPOSE (GST_DATA (message)); +#ifndef GST_DISABLE_TRACE + gst_alloc_trace_free (_message_trace, message); +#endif + gst_mem_chunk_free (chunk, message); +} + +GType +gst_message_get_type (void) +{ + return _gst_message_type; +} + +/** + * gst_message_new: + * @type: The type of the new message + * + * Allocate a new message of the given type. + * + * Returns: A new message. + * + * MT safe. + */ +GstMessage * +gst_message_new (GstMessageType type, GstObject * src) +{ + GstMessage *message; + + message = gst_mem_chunk_alloc0 (chunk); +#ifndef GST_DISABLE_TRACE + gst_alloc_trace_new (_message_trace, message); +#endif + + GST_CAT_INFO (GST_CAT_MESSAGE, "creating new message %p %d", message, type); + + _GST_DATA_INIT (GST_DATA (message), + _gst_message_type, + 0, + (GstDataFreeFunction) _gst_message_free, + (GstDataCopyFunction) _gst_message_copy); + + GST_MESSAGE_TYPE (message) = type; + GST_MESSAGE_TIMESTAMP (message) = G_GINT64_CONSTANT (0); + if (src) { + gst_object_ref (src); + GST_MESSAGE_SRC (message) = src; + } + + return message; +} + + +/** + * gst_message_new_eos: + * + * Create a new eos message. + * + * Returns: The new eos message. + * + * MT safe. + */ +GstMessage * +gst_message_new_eos (GstObject * src) +{ + GstMessage *message; + + message = gst_message_new (GST_MESSAGE_EOS, src); + + return message; +} + +/** + * gst_message_new_error: + * @src: The object originating the message. + * @error: The GError for this message. + * @debug: A debugging string for something or other. + * + * Create a new error message. The message will take ownership of @error and + * @debug. + * + * Returns: The new error message. + * + * MT safe. + */ +GstMessage * +gst_message_new_error (GstObject * src, GError * error, gchar * debug) +{ + GstMessage *message; + GstStructure *s; + + message = gst_message_new (GST_MESSAGE_ERROR, src); + s = gst_structure_new ("GstMessageError", "gerror", G_TYPE_POINTER, error, + "debug", G_TYPE_STRING, debug, NULL); + message->structure = s; + + return message; +} + +/** + * gst_message_new_warning: + * @src: The object originating the message. + * @error: The GError for this message. + * @debug: A debugging string for something or other. + * + * Create a new warning message. The message will take ownership of @error and + * @debug. + * + * Returns: The new warning message. + * + * MT safe. + */ +GstMessage * +gst_message_new_warning (GstObject * src, GError * error, gchar * debug) +{ + GstMessage *message; + GstStructure *s; + + message = gst_message_new (GST_MESSAGE_WARNING, src); + s = gst_structure_new ("GstMessageWarning", "gerror", G_TYPE_POINTER, error, + "debug", G_TYPE_STRING, debug, NULL); + message->structure = s; + + return message; +} + +/** + * gst_message_new_tag: + * @src: The object originating the message. + * @tag_list: The tag list for the message. + * + * Create a new tag message. The message will take ownership of the tag list. + * + * Returns: The new tag message. + * + * MT safe. + */ +GstMessage * +gst_message_new_tag (GstObject * src, GstTagList * tag_list) +{ + GstMessage *message; + + message = gst_message_new (GST_MESSAGE_TAG, src); + message->structure = tag_list; + + return message; +} + +/** + * gst_message_new_state_change: + * @src: The object originating the message. + * @old: The previous state. + * @new: The new (current) state. + * + * Create a state change message. + * + * Returns: The new state change message. + * + * MT safe. + */ +GstMessage * +gst_message_new_state_changed (GstObject * src, GstElementState old, + GstElementState new) +{ + GstMessage *message; + GstStructure *s; + + message = gst_message_new (GST_MESSAGE_STATE_CHANGED, src); + s = gst_structure_new ("GstMessageError", "old-state", G_TYPE_INT, old, + "new-state", G_TYPE_INT, new, NULL); + message->structure = s; + + return message; +} + +/** + * gst_message_new_application: + * @structure: The structure for the message. The message will take ownership of + * the structure. + * + * Create a new application-specific message. These messages can be used by + * application-specific plugins to pass data to the app. + * + * Returns: The new message. + * + * MT safe. + */ +GstMessage * +gst_message_new_application (GstStructure * structure) +{ + GstMessage *message; + + message = gst_message_new (GST_MESSAGE_APPLICATION, NULL); + message->structure = structure; + + return message; +} + +/** + * gst_message_get_structure: + * @message: The #GstMessage. + * + * Access the structure of the message. + * + * Returns: The structure of the message, owned by the message. + * + * MT safe. + */ +const GstStructure * +gst_message_get_structure (GstMessage * message) +{ + g_return_val_if_fail (GST_IS_MESSAGE (message), NULL); + return message->structure; +} + +/** + * gst_message_parse_tag: + * @message: A valid #GstMessage of type GST_MESSAGE_TAG. + * + * Extracts the tag list from the GstMessage. The tag list returned in the + * output argument is a copy; the caller must free it when done. + * + * MT safe. + */ +void +gst_message_parse_tag (GstMessage * message, GstTagList ** tag_list) +{ + g_return_if_fail (GST_IS_MESSAGE (message)); + g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_TAG); + + *tag_list = (GstTagList *) gst_structure_copy (message->structure); +} + +/** + * gst_message_parse_tag: + * @message: A valid #GstMessage of type GST_MESSAGE_STATE_CHANGED. + * + * Extracts the old and new states from the GstMessage. + * + * MT safe. + */ +void +gst_message_parse_state_changed (GstMessage * message, GstElementState * old, + GstElementState * new) +{ + g_return_if_fail (GST_IS_MESSAGE (message)); + g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_STATE_CHANGED); + + if (!gst_structure_get_int (message->structure, "old-state", (gint *) old)) + g_assert_not_reached (); + if (!gst_structure_get_int (message->structure, "new-state", (gint *) new)) + g_assert_not_reached (); +} + +/** + * gst_message_parse_error: + * @message: A valid #GstMessage of type GST_MESSAGE_ERROR. + * + * Extracts the GError and debug strung from the GstMessage. The values returned + * in the output arguments are copies; the caller must free them when done. + * + * MT safe. + */ +void +gst_message_parse_error (GstMessage * message, GError ** gerror, gchar ** debug) +{ + const GValue *error_gvalue; + + g_return_if_fail (GST_IS_MESSAGE (message)); + g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR); + + error_gvalue = gst_structure_get_value (message->structure, "gerror"); + g_return_if_fail (error_gvalue != NULL); + g_return_if_fail (G_VALUE_TYPE (error_gvalue) == G_TYPE_POINTER); + + *gerror = g_error_copy (g_value_get_pointer (error_gvalue)); + *debug = g_strdup (gst_structure_get_string (message->structure, "debug")); +} + +/** + * gst_message_parse_warning: + * @message: A valid #GstMessage of type GST_MESSAGE_WARNING. + * + * Extracts the GError and debug strung from the GstMessage. The values returned + * in the output arguments are copies; the caller must free them when done. + * + * MT safe. + */ +void +gst_message_parse_warning (GstMessage * message, GError ** gerror, + gchar ** debug) +{ + const GValue *error_gvalue; + + g_return_if_fail (GST_IS_MESSAGE (message)); + g_return_if_fail (GST_MESSAGE_TYPE (message) == GST_MESSAGE_WARNING); + + error_gvalue = gst_structure_get_value (message->structure, "gerror"); + g_return_if_fail (error_gvalue != NULL); + g_return_if_fail (G_VALUE_TYPE (error_gvalue) == G_TYPE_POINTER); + + *gerror = g_error_copy (g_value_get_pointer (error_gvalue)); + *debug = g_strdup (gst_structure_get_string (message->structure, "debug")); +} diff --git a/gst/gstmessage.h b/gst/gstmessage.h new file mode 100644 index 0000000000..5d4658b81f --- /dev/null +++ b/gst/gstmessage.h @@ -0,0 +1,141 @@ +/* GStreamer + * Copyright (C) 2004 Wim Taymans + * + * gstmessage.h: Header for GstMessage subsystem + * + * 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 __GST_MESSAGE_H__ +#define __GST_MESSAGE_H__ + +#include +#include +#include +#include +#include + +G_BEGIN_DECLS + +GST_EXPORT GType _gst_message_type; + +/** + * GstMessageType: + * @GST_MESSAGE_UNKNOWN: an undefined message + * @GST_MESSAGE_EOS: end-of-stream reached in a pipeline + * @GST_MESSAGE_ERROR: an error occured + * @GST_MESSAGE_WARNING: a warning occured. + * @GST_MESSAGE_INFO: an info message occured + * @GST_MESSAGE_TAG: a tag was found. + * @GST_MESSAGE_BUFFERING: the pipeline is buffering + * @GST_MESSAGE_STATE_CHANGED: a state change happened + * @GST_MESSAGE_STEP_DONE: a framestep finished. + * @GST_MESSAGE_NEW_CLOCK: a new clock was selected in the pipeline + * @GST_MESSAGE_STRUCTURE_CHANGE: the structure of the pipeline changed. + * @GST_MESSAGE_STREAM_STATUS: status about a stream, emited when it starts, + * stops, errors, etc.. + * @GST_MESSAGE_APPLICATION: message posted by the application, possibly + * via an application-specific element. + * @GST_MESSAGE_ANY: mask for all of the above messages. + */ +typedef enum +{ + GST_MESSAGE_UNKNOWN = 0, + GST_MESSAGE_EOS = (1 << 0), + GST_MESSAGE_ERROR = (1 << 1), + GST_MESSAGE_WARNING = (1 << 2), + GST_MESSAGE_INFO = (1 << 3), + GST_MESSAGE_TAG = (1 << 4), + GST_MESSAGE_BUFFERING = (1 << 5), + GST_MESSAGE_STATE_CHANGED = (1 << 6), + GST_MESSAGE_STEP_DONE = (1 << 7), + GST_MESSAGE_NEW_CLOCK = (1 << 8), + GST_MESSAGE_STRUCTURE_CHANGE = (1 << 9), + GST_MESSAGE_STREAM_STATUS = (1 << 10), + GST_MESSAGE_APPLICATION = (1 << 11), + GST_MESSAGE_ANY = 0xffffffff +} GstMessageType; + +#define GST_MESSAGE_TRACE_NAME "GstMessage" + +#define GST_TYPE_MESSAGE (_gst_message_type) +#define GST_MESSAGE(message) ((GstMessage*)(message)) +#define GST_IS_MESSAGE(message) (GST_DATA_TYPE(message) == GST_TYPE_MESSAGE) + +/* the lock is used to handle the synchronous handling of messages, + * the emiting thread is block until the handling thread processed + * the message using this mutex/cond pair */ +#define GST_MESSAGE_GET_LOCK(message) (GST_MESSAGE(message)->lock) +#define GST_MESSAGE_LOCK(message) g_mutex_lock(GST_MESSAGE_GET_LOCK(message)) +#define GST_MESSAGE_UNLOCK(message) g_mutex_unlock(GST_MESSAGE_GET_LOCK(message)) +#define GST_MESSAGE_COND(message) (GST_MESSAGE(message)->cond) +#define GST_MESSAGE_WAIT(message) g_cond_wait(GST_MESSAGE_COND(message),GST_MESSAGE_GET_LOCK(message)) +#define GST_MESSAGE_SIGNAL(message) g_cond_signal(GST_MESSAGE_COND(message)) + +#define GST_MESSAGE_TYPE(message) (GST_MESSAGE(message)->type) +#define GST_MESSAGE_TIMESTAMP(message) (GST_MESSAGE(message)->timestamp) +#define GST_MESSAGE_SRC(message) (GST_MESSAGE(message)->src) + +struct _GstMessage +{ + GstData data; + + /*< public > *//* with MESSAGE_LOCK */ + GMutex *lock; /* lock and cond for async delivery */ + GCond *cond; + + /*< public > *//* with COW */ + GstMessageType type; + guint64 timestamp; + GstObject *src; + + GstStructure *structure; + + /*< private > */ + gpointer _gst_reserved[GST_PADDING]; +}; + +void _gst_message_initialize (void); + +GType gst_message_get_type (void); +GstMessage * gst_message_new (GstMessageType type, GstObject * src); + +/* refcounting */ +#define gst_message_ref(ev) GST_MESSAGE (gst_data_ref (GST_DATA (ev))) +#define gst_message_ref_by_count(ev,c) GST_MESSAGE (gst_data_ref_by_count (GST_DATA (ev), c)) +#define gst_message_unref(ev) gst_data_unref (GST_DATA (ev)) +/* copy message */ +#define gst_message_copy(ev) GST_MESSAGE (gst_data_copy (GST_DATA (ev))) + +GstMessage * gst_message_new_eos (GstObject * src); +GstMessage * gst_message_new_error (GstObject * src, GError * error, gchar * debug); +GstMessage * gst_message_new_warning (GstObject * src, GError * error, gchar * debug); +GstMessage * gst_message_new_tag (GstObject * src, GstTagList * tag_list); +GstMessage * gst_message_new_state_changed (GstObject * src, GstElementState old_state, + GstElementState new_state); +GstMessage * gst_message_new_application (GstStructure *structure); + +const GstStructure * gst_message_get_structure (GstMessage *message); + +void gst_message_parse_tag (GstMessage *message, GstTagList **tag_list); +void gst_message_parse_state_changed (GstMessage *message, GstElementState *old_state, + GstElementState *new_state); +void gst_message_parse_error (GstMessage *message, GError **gerror, gchar **debug); +void gst_message_parse_warning (GstMessage *message, GError **gerror, gchar **debug); + + +G_END_DECLS +#endif /* __GST_MESSAGE_H__ */ diff --git a/gst/gstpad.c b/gst/gstpad.c index 87234916bb..5c727e3fa6 100644 --- a/gst/gstpad.c +++ b/gst/gstpad.c @@ -60,28 +60,6 @@ GST_DEBUG_CATEGORY_STATIC (debug_dataflow); GST_UNLOCK (pad); \ } -struct _GstPadLink -{ - GType type; - - gboolean bla; - gboolean srcnotify; - gboolean sinknotify; - - GstPad *srcpad; - GstPad *sinkpad; - - GstCaps *srccaps; - GstCaps *sinkcaps; - GstCaps *filtercaps; - GstCaps *caps; - - GstPadFixateFunction app_fixate; - - gboolean engaged; - GstData *temp_store; /* used only when we invented a DISCONT */ -}; - enum { TEMPL_PAD_CREATED, @@ -100,11 +78,6 @@ static void gst_pad_init (GstPad * pad); static void gst_pad_dispose (GObject * object); static void gst_pad_set_pad_template (GstPad * pad, GstPadTemplate * templ); -static GstCaps *_gst_pad_default_fixate_func (GstPad * pad, - const GstCaps * caps); - -static gboolean gst_pad_link_try (GstPadLink * link); -static void gst_pad_link_free (GstPadLink * link); #ifndef GST_DISABLE_LOADSAVE static xmlNodePtr gst_pad_save_thyself (GstObject * object, xmlNodePtr parent); @@ -170,7 +143,7 @@ enum { REAL_LINKED, REAL_UNLINKED, - REAL_FIXATE, + REAL_REQUEST_LINK, /* FILL ME */ REAL_LAST_SIGNAL }; @@ -188,13 +161,11 @@ static void gst_real_pad_init (GstRealPad * pad); static void gst_real_pad_dispose (GObject * object); static void gst_real_pad_finalize (GObject * object); - -static gboolean _gst_real_pad_fixate_accumulator (GSignalInvocationHint * ihint, - GValue * return_accu, const GValue * handler_return, gpointer dummy); static void gst_real_pad_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_real_pad_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); +static GstCaps *gst_real_pad_get_caps_unlocked (GstRealPad * realpad); GType _gst_real_pad_type = 0; @@ -243,12 +214,10 @@ gst_real_pad_class_init (GstRealPadClass * klass) g_signal_new ("unlinked", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRealPadClass, unlinked), NULL, NULL, gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_PAD); - gst_real_pad_signals[REAL_FIXATE] = - g_signal_new ("fixate", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GstRealPadClass, appfixatefunc), - _gst_real_pad_fixate_accumulator, NULL, - gst_marshal_BOXED__BOXED, GST_TYPE_CAPS, 1, - GST_TYPE_CAPS | G_SIGNAL_TYPE_STATIC_SCOPE); + gst_real_pad_signals[REAL_REQUEST_LINK] = + g_signal_new ("request_link", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRealPadClass, request_link), NULL, + NULL, gst_marshal_VOID__OBJECT, G_TYPE_NONE, 0); g_object_class_install_property (G_OBJECT_CLASS (klass), REAL_ARG_ACTIVE, g_param_spec_boolean ("active", "Active", "Whether the pad is active.", @@ -263,18 +232,6 @@ gst_real_pad_class_init (GstRealPadClass * klass) gstobject_class->path_string_separator = "."; } -static gboolean -_gst_real_pad_fixate_accumulator (GSignalInvocationHint * ihint, - GValue * return_accu, const GValue * handler_return, gpointer dummy) -{ - if (gst_value_get_caps (handler_return)) { - g_value_copy (handler_return, return_accu); - /* stop emission if something was returned */ - return FALSE; - } - return TRUE; -} - static void gst_real_pad_init (GstRealPad * pad) { @@ -282,10 +239,6 @@ gst_real_pad_init (GstRealPad * pad) pad->peer = NULL; pad->chainfunc = NULL; - pad->getfunc = NULL; - - pad->chainhandler = NULL; - pad->gethandler = NULL; pad->ghostpads = NULL; pad->caps = NULL; @@ -303,7 +256,14 @@ gst_real_pad_init (GstRealPad * pad) pad->querytypefunc = gst_pad_get_query_types_default; GST_FLAG_UNSET (pad, GST_PAD_ACTIVE); - GST_FLAG_UNSET (pad, GST_PAD_NEGOTIATING); + + pad->preroll_lock = g_mutex_new (); + pad->preroll_cond = g_cond_new (); + + pad->stream_rec_lock = g_new (GStaticRecMutex, 1); + g_static_rec_mutex_init (pad->stream_rec_lock); + + pad->block_cond = g_cond_new (); gst_probe_dispatcher_init (&pad->probedisp); } @@ -316,6 +276,7 @@ gst_real_pad_set_property (GObject * object, guint prop_id, switch (prop_id) { case REAL_ARG_ACTIVE: + g_warning ("FIXME: not useful any more!!!"); gst_pad_set_active (GST_PAD (object), g_value_get_boolean (value)); break; default: @@ -481,93 +442,16 @@ lost_ghostpad: * Activates or deactivates the given pad. * * Returns: TRUE if the operation was successfull. + * + * MT safe. */ gboolean -gst_pad_set_active (GstPad * pad, gboolean active) +gst_pad_set_active (GstPad * pad, GstActivateMode mode) { - GstRealPad *realpad; - gboolean old; - GstPadLink *link; - - g_return_val_if_fail (GST_IS_PAD (pad), FALSE); - - old = GST_PAD_IS_ACTIVE (pad); - - if (old == active) - return TRUE; - - realpad = GST_PAD_REALIZE (pad); - - if (active) { - GST_CAT_DEBUG (GST_CAT_PADS, "activating pad %s:%s", - GST_DEBUG_PAD_NAME (realpad)); - GST_FLAG_SET (realpad, GST_PAD_ACTIVE); - } else { - GST_CAT_DEBUG (GST_CAT_PADS, "de-activating pad %s:%s", - GST_DEBUG_PAD_NAME (realpad)); - GST_FLAG_UNSET (realpad, GST_PAD_ACTIVE); - } - link = GST_RPAD_LINK (realpad); - if (link) { - if (link->temp_store) { - GST_CAT_INFO (GST_CAT_PADS, - "deleting cached data %p from bufpen of pad %s:%s", link->temp_store, - GST_DEBUG_PAD_NAME (realpad)); - gst_data_unref (link->temp_store); - link->temp_store = NULL; - } - } - - g_object_notify (G_OBJECT (realpad), "active"); - - return TRUE; + /* implement me */ + return FALSE; } -/** - * gst_pad_set_active_recursive: - * @pad: the #GstPad to activate or deactivate. - * @active: TRUE to activate the pad. - * - * Activates or deactivates the given pad and all internally linked - * pads upstream until it finds an element with multiple source pads. - */ -void -gst_pad_set_active_recursive (GstPad * pad, gboolean active) -{ - GstElement *parent; - const GList *int_links; - - g_return_if_fail (GST_IS_PAD (pad)); - g_return_if_fail (GST_PAD_IS_SRC (pad)); - - GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad, - "Recursively %s pad %s:%s", active ? "activating" : "deactivating", - GST_DEBUG_PAD_NAME (pad)); - - gst_pad_set_active (pad, active); - - /* If we have more than one sourcepad, then the other pads should - * possibly be kept active. FIXME: maybe we should recurse - * activation if any one pad is active and recurse deactivation - * if no single pad is active? */ - parent = gst_pad_get_parent (pad); - if (!parent || parent->numsrcpads > 1) - return; - - for (int_links = gst_pad_get_internal_links (pad); - int_links; int_links = g_list_next (int_links)) { - GstPad *sinkpad = GST_PAD (int_links->data); - GstPad *peer = GST_PAD_PEER (sinkpad); - - GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, sinkpad, - "Recursing %s on pad %s:%s", - active ? "activation" : "deactivation", GST_DEBUG_PAD_NAME (sinkpad)); - - gst_pad_set_active (sinkpad, active); - if (peer) - gst_pad_set_active_recursive (peer, active); - } -} /** * gst_pad_is_active: @@ -599,6 +483,181 @@ lost_ghostpad: } } +/** + * gst_pad_set_blocked_async: + * @pad: the #GstPad to block or unblock + * @blocked: boolean indicating we should block or unblock + * @callback: #GstPadBlockCallback that will be called when the + * operation succeeds. + * @user_data: user data passed to the callback + * + * Blocks or unblocks the dataflow on a pad. The provided callback + * is called when the operation succeeds. This can take a while as + * the pad can only become blocked when real dataflow is happening. + * When the pipeline is stalled, for example in PAUSED, this can + * take an indeterminate amount of time. + * You can pass NULL as the callback to make this call block. Be + * carefull with this blocking call as it might not return for + * reasons stated above. + * + * Returns: TRUE if the pad could be blocked. This function can fail + * if wrong parameters were passed or the pad was already in the + * requested state. + * + * MT safe. + */ +gboolean +gst_pad_set_blocked_async (GstPad * pad, gboolean blocked, + GstPadBlockCallback callback, gpointer user_data) +{ + gboolean was_blocked; + GstRealPad *realpad; + + g_return_val_if_fail (GST_IS_PAD (pad), FALSE); + + GST_PAD_REALIZE_AND_LOCK (pad, realpad, lost_ghostpad); + + was_blocked = GST_RPAD_IS_BLOCKED (realpad); + + if (G_UNLIKELY (was_blocked == blocked)) + goto had_right_state; + + if (blocked) { + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, realpad, "blocking pad %s:%s", + GST_DEBUG_PAD_NAME (realpad)); + + GST_FLAG_SET (realpad, GST_PAD_BLOCKED); + realpad->block_callback = callback; + realpad->block_data = user_data; + if (!callback) { + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, realpad, "waiting for block"); + GST_PAD_BLOCK_WAIT (realpad); + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, realpad, "blocked"); + } + } else { + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, realpad, "unblocking pad %s:%s", + GST_DEBUG_PAD_NAME (realpad)); + + GST_FLAG_UNSET (realpad, GST_PAD_BLOCKED); + + realpad->block_callback = callback; + realpad->block_data = user_data; + + if (callback) { + GST_PAD_BLOCK_SIGNAL (realpad); + } else { + GST_PAD_BLOCK_SIGNAL (realpad); + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, realpad, "waiting for unblock"); + GST_PAD_BLOCK_WAIT (realpad); + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, realpad, "unblocked"); + } + } + GST_UNLOCK (realpad); + + return TRUE; + +lost_ghostpad: + { + return FALSE; + } +had_right_state: + { + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, realpad, + "pad %s:%s was in right state", GST_DEBUG_PAD_NAME (realpad)); + GST_UNLOCK (realpad); + return FALSE; + } +} + +/** + * gst_pad_set_blocked: + * @pad: the #GstPad to block or unblock + * @blocked: boolean indicating we should block or unblock + * + * Blocks or unblocks the dataflow on a pad. This function is + * a shortcut for @gst_pad_set_blocked_async() with a NULL + * callback. + * + * Returns: TRUE if the pad could be blocked. This function can fail + * wrong parameters were passed or the pad was already in the + * requested state. + * + * MT safe. + */ +gboolean +gst_pad_set_blocked (GstPad * pad, gboolean blocked) +{ + return gst_pad_set_blocked_async (pad, blocked, NULL, NULL); +} + +/** + * gst_pad_is_blocked: + * @pad: the #GstPad to query + * + * Checks if the pad is blocked or not. This function returns the + * last requested state of the pad. It is not certain that the pad + * is actually blocked at this point. + * + * Returns: TRUE if the pad is blocked. + * + * MT safe. + */ +gboolean +gst_pad_is_blocked (GstPad * pad) +{ + gboolean result = FALSE; + GstRealPad *realpad; + + g_return_val_if_fail (GST_IS_PAD (pad), result); + + GST_PAD_REALIZE_AND_LOCK (pad, realpad, lost_ghostpad); + result = GST_FLAG_IS_SET (realpad, GST_PAD_BLOCKED); + GST_UNLOCK (realpad); + + return result; + +lost_ghostpad: + { + return FALSE; + } +} + +/** + * gst_pad_set_activate_function: + * @pad: a real sink #GstPad. + * @chain: the #GstPadActivateFunction to set. + * + * Sets the given activate function for the pad. The activate function is called to + * start or stop dataflow on a pad. + */ +void +gst_pad_set_activate_function (GstPad * pad, GstPadActivateFunction activate) +{ + g_return_if_fail (GST_IS_REAL_PAD (pad)); + + GST_RPAD_ACTIVATEFUNC (pad) = activate; + GST_CAT_DEBUG (GST_CAT_PADS, "activatefunc for %s:%s set to %s", + GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (activate)); +} + +/** + * gst_pad_set_loop_function: + * @pad: a real sink #GstPad. + * @chain: the #GstPadLoopFunction to set. + * + * Sets the given loop function for the pad. The loop function is called + * repeadedly to pull/push buffers from/to the peer pad. + */ +void +gst_pad_set_loop_function (GstPad * pad, GstPadLoopFunction loop) +{ + g_return_if_fail (GST_IS_REAL_PAD (pad)); + + GST_RPAD_LOOPFUNC (pad) = loop; + GST_CAT_DEBUG (GST_CAT_PADS, "loopfunc for %s:%s set to %s", + GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (loop)); +} + /** * gst_pad_set_chain_function: * @pad: a real sink #GstPad. @@ -619,23 +678,23 @@ gst_pad_set_chain_function (GstPad * pad, GstPadChainFunction chain) } /** - * gst_pad_set_get_function: + * gst_pad_set_getrange_function: * @pad: a real source #GstPad. - * @get: the #GstPadGetFunction to set. + * @get: the #GstPadGetRangeFunction to set. * - * Sets the given get function for the pad. The get function is called to - * produce a new #GstData to start the processing pipeline. Get functions cannot + * Sets the given getrange function for the pad. The getrange function is called to + * produce a new #GstBuffer to start the processing pipeline. Getrange functions cannot * return %NULL. */ void -gst_pad_set_get_function (GstPad * pad, GstPadGetFunction get) +gst_pad_set_getrange_function (GstPad * pad, GstPadGetRangeFunction get) { g_return_if_fail (GST_IS_REAL_PAD (pad)); g_return_if_fail (GST_RPAD_DIRECTION (pad) == GST_PAD_SRC); - GST_RPAD_GETFUNC (pad) = get; + GST_RPAD_GETRANGEFUNC (pad) = get; - GST_CAT_DEBUG (GST_CAT_PADS, "getfunc for %s:%s set to %s", + GST_CAT_DEBUG (GST_CAT_PADS, "getrangefunc for %s:%s set to %s", GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (get)); } @@ -884,26 +943,14 @@ gst_pad_set_formats_function (GstPad * pad, GstPadFormatsFunction formats) * @link: the #GstPadLinkFunction to set. * * Sets the given link function for the pad. It will be called when the pad is - * linked or relinked with caps. The caps passed to the link function are - * guaranteed to be fixed. This means that you can assume that the caps is not - * ANY or EMPTY, and that there is exactly one structure in the caps, and that - * all the fields in the structure are fixed. + * linked or relinked with caps. The caps passed to the link function is + * the filtered caps for the connnection. It can contain a non fixed caps. * - * The return value GST_PAD_LINK_OK should be used when the caps are acceptable, - * and you've extracted all the necessary information from the caps and set the - * element's internal state appropriately. + * The return value GST_PAD_LINK_OK should be used when the connection can be + * made. * - * The return value GST_PAD_LINK_REFUSED should be used when the caps are - * unacceptable for whatever reason. - * - * The return value GST_PAD_LINK_DELAYED should be used when the element is in a - * state where it can't determine whether the caps are acceptable or not. This - * is often used if the element needs to open a device or process data before - * determining acceptable caps. - * - * @link must not call gst_caps_try_set_caps() on the pad that was specified as - * a parameter, although it may (and often should) call gst_caps_try_set_caps() - * on other pads. + * The return value GST_PAD_LINK_REFUSED should be used when the connection + * cannot be made for some reason. */ void gst_pad_set_link_function (GstPad * pad, GstPadLinkFunction link) @@ -933,31 +980,6 @@ gst_pad_set_unlink_function (GstPad * pad, GstPadUnlinkFunction unlink) GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (unlink)); } -/** - * gst_pad_set_fixate_function: - * @pad: a real #GstPad. - * @fixate: the #GstPadFixateFunction to set. - * - * Sets the given fixate function for the pad. Its job is to narrow down the - * possible caps for a connection. Fixate functions are called with a const - * caps, and should return a caps that is a strict subset of the given caps. - * That is, @fixate should create a caps that is "more fixed" than previously, - * but it does not have to return fixed caps. If @fixate can't provide more - * fixed caps, it should return %NULL. - * - * Note that @fixate will only be called after the "fixate" signal is emitted, - * and only if the caps are still non-fixed. - */ -void -gst_pad_set_fixate_function (GstPad * pad, GstPadFixateFunction fixate) -{ - g_return_if_fail (GST_IS_REAL_PAD (pad)); - - GST_RPAD_FIXATEFUNC (pad) = fixate; - GST_CAT_DEBUG (GST_CAT_PADS, "fixatefunc for %s:%s set to %s", - GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (fixate)); -} - /** * gst_pad_set_getcaps_function: * @pad: a real #GstPad. @@ -981,7 +1003,8 @@ gst_pad_set_fixate_function (GstPad * pad, GstPadFixateFunction fixate) * @getcaps should return the most specific caps it reasonably can, since this * helps with autoplugging. * - * Note that the return value from @getcaps is owned by the caller. + * Note that the return value from @getcaps is owned by the caller, so the caller + * should unref the caps after usage. */ void gst_pad_set_getcaps_function (GstPad * pad, GstPadGetCapsFunction getcaps) @@ -993,6 +1016,66 @@ gst_pad_set_getcaps_function (GstPad * pad, GstPadGetCapsFunction getcaps) GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (getcaps)); } +/** + * gst_pad_set_acceptcaps_function: + * @pad: a real #GstPad. + * @acceptcaps: the #GstPadAcceptCapsFunction to set. + * + * Sets the given acceptcaps function for the pad. The acceptcaps function + * will be called to check if the pad can accept the given caps. + */ +void +gst_pad_set_acceptcaps_function (GstPad * pad, + GstPadAcceptCapsFunction acceptcaps) +{ + g_return_if_fail (GST_IS_REAL_PAD (pad)); + + GST_RPAD_ACCEPTCAPSFUNC (pad) = acceptcaps; + GST_CAT_DEBUG (GST_CAT_PADS, "acceptcapsfunc for %s:%s set to %s", + GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (acceptcaps)); +} + +/** + * gst_pad_set_fixatecaps_function: + * @pad: a real #GstPad. + * @fixatecaps: the #GstPadFixateCapsFunction to set. + * + * Sets the given fixatecaps function for the pad. The fixatecaps function + * will be called whenever the default values for a GstCaps needs to be + * filled in. + */ +void +gst_pad_set_fixatecaps_function (GstPad * pad, + GstPadFixateCapsFunction fixatecaps) +{ + g_return_if_fail (GST_IS_REAL_PAD (pad)); + + GST_RPAD_FIXATECAPSFUNC (pad) = fixatecaps; + GST_CAT_DEBUG (GST_CAT_PADS, "fixatecapsfunc for %s:%s set to %s", + GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (fixatecaps)); +} + +/** + * gst_pad_set_setcaps_function: + * @pad: a real #GstPad. + * @setcaps: the #GstPadSetCapsFunction to set. + * + * Sets the given setcaps function for the pad. The setcaps function + * will be called whenever a buffer with a new media type is pushed or + * pulled from the pad. The pad/element needs to update it's internal + * structures to process the new media type. If this new type is not + * acceptable, the setcaps function should return FALSE. + */ +void +gst_pad_set_setcaps_function (GstPad * pad, GstPadSetCapsFunction setcaps) +{ + g_return_if_fail (GST_IS_REAL_PAD (pad)); + + GST_RPAD_SETCAPSFUNC (pad) = setcaps; + GST_CAT_DEBUG (GST_CAT_PADS, "setcapsfunc for %s:%s set to %s", + GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (setcaps)); +} + /** * gst_pad_set_bufferalloc_function: * @pad: a real sink #GstPad. @@ -1013,8 +1096,6 @@ gst_pad_set_bufferalloc_function (GstPad * pad, GST_DEBUG_PAD_NAME (pad), GST_DEBUG_FUNCPTR_NAME (bufalloc)); } -/* FIXME 0.9: Do we actually want to allow the case where src and sink are - switched? */ /** * gst_pad_unlink: * @srcpad: the source #GstPad to unlink. @@ -1022,36 +1103,36 @@ gst_pad_set_bufferalloc_function (GstPad * pad, * * Unlinks the source pad from the sink pad. Will emit the "unlinked" signal on * both pads. + * + * Returns: TRUE if the pads were unlinked. This function returns FALSE if + * the pads were not linked together. + * + * MT safe. */ -void +gboolean gst_pad_unlink (GstPad * srcpad, GstPad * sinkpad) { GstRealPad *realsrc, *realsink; - GstScheduler *src_sched, *sink_sched; - g_return_if_fail (GST_IS_PAD (srcpad)); - g_return_if_fail (GST_IS_PAD (sinkpad)); + g_return_val_if_fail (GST_IS_PAD (srcpad), FALSE); + g_return_val_if_fail (GST_IS_PAD (sinkpad), FALSE); GST_CAT_INFO (GST_CAT_ELEMENT_PADS, "unlinking %s:%s(%p) and %s:%s(%p)", GST_DEBUG_PAD_NAME (srcpad), srcpad, GST_DEBUG_PAD_NAME (sinkpad), sinkpad); - realsrc = GST_PAD_REALIZE (srcpad); - realsink = GST_PAD_REALIZE (sinkpad); + GST_PAD_REALIZE_AND_LOCK (srcpad, realsrc, lost_src_ghostpad); - g_return_if_fail (GST_RPAD_PEER (realsrc) != NULL); - g_return_if_fail (GST_RPAD_PEER (realsink) == realsrc); + if (G_UNLIKELY (GST_RPAD_DIRECTION (realsrc) != GST_PAD_SRC)) + goto not_srcpad; - if ((GST_RPAD_DIRECTION (realsrc) == GST_PAD_SINK) && - (GST_RPAD_DIRECTION (realsink) == GST_PAD_SRC)) { - GstRealPad *temppad; + GST_PAD_REALIZE_AND_LOCK (sinkpad, realsink, lost_sink_ghostpad); - temppad = realsrc; - realsrc = realsink; - realsink = temppad; - } - g_return_if_fail ((GST_RPAD_DIRECTION (realsrc) == GST_PAD_SRC) && - (GST_RPAD_DIRECTION (realsink) == GST_PAD_SINK)); + if (G_UNLIKELY (GST_RPAD_DIRECTION (realsink) != GST_PAD_SINK)) + goto not_sinkpad; + + if (G_UNLIKELY (GST_RPAD_PEER (realsrc) != realsink)) + goto not_linked_together; if (GST_RPAD_UNLINKFUNC (realsrc)) { GST_RPAD_UNLINKFUNC (realsrc) (GST_PAD (realsrc)); @@ -1060,27 +1141,16 @@ gst_pad_unlink (GstPad * srcpad, GstPad * sinkpad) GST_RPAD_UNLINKFUNC (realsink) (GST_PAD (realsink)); } - /* get the schedulers before we unlink */ - src_sched = gst_pad_get_scheduler (GST_PAD (realsrc)); - sink_sched = gst_pad_get_scheduler (GST_PAD (realsink)); - - if (GST_RPAD_LINK (realsrc)) - gst_pad_link_free (GST_RPAD_LINK (realsrc)); - /* first clear peers */ GST_RPAD_PEER (realsrc) = NULL; GST_RPAD_PEER (realsink) = NULL; - GST_RPAD_LINK (realsrc) = NULL; - GST_RPAD_LINK (realsink) = NULL; - /* now tell the scheduler */ - if (src_sched && src_sched == sink_sched) { - gst_scheduler_pad_unlink (src_sched, GST_PAD (realsrc), GST_PAD (realsink)); - } + /* clear filter, note that we leave the pad caps as they are */ + gst_caps_replace (&GST_RPAD_APPFILTER (realsrc), NULL); + gst_caps_replace (&GST_RPAD_APPFILTER (realsink), NULL); - /* hold a reference, as they can go away in the signal handlers */ - gst_object_ref (GST_OBJECT (realsrc)); - gst_object_ref (GST_OBJECT (realsink)); + GST_UNLOCK (realsink); + GST_UNLOCK (realsrc); /* fire off a signal to each of the pads telling them * that they've been unlinked */ @@ -1090,10 +1160,40 @@ gst_pad_unlink (GstPad * srcpad, GstPad * sinkpad) 0, realsrc); GST_CAT_INFO (GST_CAT_ELEMENT_PADS, "unlinked %s:%s and %s:%s", - GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad)); + GST_DEBUG_PAD_NAME (realsrc), GST_DEBUG_PAD_NAME (realsink)); - gst_object_unref (GST_OBJECT (realsrc)); - gst_object_unref (GST_OBJECT (realsink)); + return TRUE; + +lost_src_ghostpad: + { + return FALSE; + } +not_srcpad: + { + g_critical ("pad %s is not a source pad", GST_PAD_NAME (realsrc)); + GST_UNLOCK (realsrc); + return FALSE; + } +lost_sink_ghostpad: + { + GST_UNLOCK (realsrc); + return FALSE; + } +not_sinkpad: + { + g_critical ("pad %s is not a sink pad", GST_PAD_NAME (realsink)); + GST_UNLOCK (realsink); + GST_UNLOCK (realsrc); + return FALSE; + } +not_linked_together: + { + /* we do not emit a warning in this case because unlinking cannot + * be made MT safe.*/ + GST_UNLOCK (realsink); + GST_UNLOCK (realsrc); + return FALSE; + } } /** @@ -1125,581 +1225,139 @@ lost_ghostpad: } } -static gboolean -gst_pad_check_schedulers (GstRealPad * realsrc, GstRealPad * realsink) -{ - GstScheduler *src_sched, *sink_sched; - gint num_decoupled = 0; - - src_sched = gst_pad_get_scheduler (GST_PAD (realsrc)); - sink_sched = gst_pad_get_scheduler (GST_PAD (realsink)); - - if (src_sched && sink_sched) { - if (GST_FLAG_IS_SET (GST_PAD_PARENT (realsrc), GST_ELEMENT_DECOUPLED)) - num_decoupled++; - if (GST_FLAG_IS_SET (GST_PAD_PARENT (realsink), GST_ELEMENT_DECOUPLED)) - num_decoupled++; - - if (src_sched != sink_sched && num_decoupled != 1) { - return FALSE; - } - } - return TRUE; -} - -#define GST_PAD_LINK_SRC(pad) ((GST_PAD_IS_SRC (pad)) ? (pad) : GST_PAD_PEER (pad)) -#define GST_PAD_LINK_SINK(pad) ((GST_PAD_IS_SINK (pad)) ? (pad) : GST_PAD_PEER (pad)) - -static GstPadLink * -gst_pad_link_new (void) -{ - GstPadLink *link; - - link = g_new0 (GstPadLink, 1); - link->sinknotify = TRUE; - link->srcnotify = TRUE; - - link->engaged = FALSE; - return link; -} - -static void -gst_pad_link_free (GstPadLink * link) -{ - if (link->srccaps) - gst_caps_unref (link->srccaps); - if (link->sinkcaps) - gst_caps_unref (link->sinkcaps); - if (link->filtercaps) - gst_caps_unref (link->filtercaps); - if (link->caps) - gst_caps_unref (link->caps); - if (link->temp_store) - gst_data_unref (link->temp_store); -#ifdef USE_POISONING - memset (link, 0xff, sizeof (*link)); -#endif - g_free (link); -} - -static void -gst_pad_link_intersect (GstPadLink * link) -{ - GstCaps *pad_intersection; - - if (link->caps) - gst_caps_unref (link->caps); - - GST_DEBUG ("intersecting link from %s:%s to %s:%s", - GST_DEBUG_PAD_NAME (link->srcpad), GST_DEBUG_PAD_NAME (link->sinkpad)); - GST_DEBUG ("... srccaps %" GST_PTR_FORMAT, link->srccaps); - GST_DEBUG ("... sinkcaps %" GST_PTR_FORMAT, link->sinkcaps); - GST_DEBUG ("... filtercaps %" GST_PTR_FORMAT, link->filtercaps); - - pad_intersection = gst_caps_intersect (link->srccaps, link->sinkcaps); - - if (link->filtercaps) { - GST_DEBUG ("unfiltered intersection %" GST_PTR_FORMAT, pad_intersection); - link->caps = gst_caps_intersect (pad_intersection, link->filtercaps); - gst_caps_unref (pad_intersection); - } else { - link->caps = pad_intersection; - } - - GST_DEBUG ("filtered intersection %" GST_PTR_FORMAT, link->caps); -} - -static gboolean -gst_pad_link_ready_for_negotiation (GstPadLink * link) -{ - GstElement *parent; - - parent = GST_PAD_PARENT (link->srcpad); - if (!parent || GST_STATE (parent) < GST_STATE_READY) { - GST_DEBUG ("parent %s of pad %s:%s is not READY", - GST_ELEMENT_NAME (parent), GST_DEBUG_PAD_NAME (link->srcpad)); - return FALSE; - } - parent = GST_PAD_PARENT (link->sinkpad); - if (!parent || GST_STATE (parent) < GST_STATE_READY) { - GST_DEBUG ("parent %s of pad %s:%s is not READY", - GST_ELEMENT_NAME (parent), GST_DEBUG_PAD_NAME (link->sinkpad)); - return FALSE; - } - - return TRUE; -} - -static void -gst_pad_link_fixate (GstPadLink * link) -{ - GstCaps *caps; - GstCaps *newcaps; - - caps = link->caps; - - g_return_if_fail (caps != NULL); - g_return_if_fail (!gst_caps_is_empty (caps)); - - GST_DEBUG ("trying to fixate caps %" GST_PTR_FORMAT, caps); - - gst_caps_do_simplify (caps); - while (!gst_caps_is_fixed (caps)) { - int i; - - for (i = 0; i < 5; i++) { - newcaps = NULL; - switch (i) { - case 0: - g_signal_emit (G_OBJECT (link->srcpad), - gst_real_pad_signals[REAL_FIXATE], 0, caps, &newcaps); - GST_DEBUG ("app srcpad signal fixated to %" GST_PTR_FORMAT, newcaps); - break; - case 1: - g_signal_emit (G_OBJECT (link->sinkpad), - gst_real_pad_signals[REAL_FIXATE], 0, caps, &newcaps); - GST_DEBUG ("app sinkpad signal fixated to %" GST_PTR_FORMAT, newcaps); - break; - case 2: - if (GST_RPAD_FIXATEFUNC (link->srcpad)) { - newcaps = - GST_RPAD_FIXATEFUNC (link->srcpad) (GST_PAD (link->srcpad), - caps); - GST_DEBUG ("srcpad %s:%s fixated to %" GST_PTR_FORMAT, - GST_DEBUG_PAD_NAME (link->srcpad), newcaps); - } else - GST_DEBUG ("srcpad %s:%s doesn't have a fixate function", - GST_DEBUG_PAD_NAME (link->srcpad)); - - break; - case 3: - if (GST_RPAD_FIXATEFUNC (link->sinkpad)) { - newcaps = - GST_RPAD_FIXATEFUNC (link->sinkpad) (GST_PAD (link->sinkpad), - caps); - GST_DEBUG ("sinkpad %s:%s fixated to %" GST_PTR_FORMAT, - GST_DEBUG_PAD_NAME (link->sinkpad), newcaps); - } else - GST_DEBUG ("sinkpad %s:%s doesn't have a fixate function", - GST_DEBUG_PAD_NAME (link->sinkpad)); - break; - case 4: - newcaps = _gst_pad_default_fixate_func (GST_PAD (link->srcpad), caps); - GST_DEBUG ("core fixated to %" GST_PTR_FORMAT, newcaps); - break; - } - if (newcaps) { - G_GNUC_UNUSED gboolean bad; - - gst_caps_do_simplify (newcaps); -#ifndef G_DISABLE_CHECKS - /* some mad checking for correctly working fixation functions */ - - if (i == 4) { - /* we trust the default fixation function unconditionally */ - bad = FALSE; - } else if (gst_caps_is_empty (newcaps)) { - g_warning - ("a fixation function did not fixate correctly, it returned empty caps"); - gst_caps_unref (newcaps); - continue; - } else if (gst_caps_is_any (caps)) { - bad = gst_caps_is_any (newcaps); - } else { - GstCaps *test = gst_caps_subtract (caps, newcaps); - - bad = gst_caps_is_empty (test); - gst_caps_unref (test); - /* simplifying is ok, too */ - if (bad) - bad = (gst_caps_get_size (newcaps) >= gst_caps_get_size (caps)); - } - if (bad) { - gchar *newcaps_str = gst_caps_to_string (newcaps); - gchar *caps_str = gst_caps_to_string (caps); - - g_warning - ("a fixation function did not fixate correctly, the returned caps %s are no true subset of %s.", - newcaps_str, caps_str); - g_free (newcaps_str); - g_free (caps_str); - gst_caps_unref (newcaps); - } else -#endif - { - gst_caps_unref (caps); - caps = newcaps; - break; - } - } - } - } - - link->caps = caps; -} - +/* FIXME leftover from an attempt at refactoring... */ static GstPadLinkReturn -gst_pad_link_call_link_functions (GstPadLink * link) +gst_pad_link_prepare_filtered (GstPad * srcpad, GstPad * sinkpad, + GstRealPad ** outrealsrc, GstRealPad ** outrealsink, + const GstCaps * filtercaps) { - GstPadLinkReturn res = GST_PAD_LINK_OK; + GstRealPad *realsrc, *realsink; - /* Detect recursion. */ - if (GST_PAD_IS_NEGOTIATING (link->srcpad) || - GST_PAD_IS_NEGOTIATING (link->sinkpad)) { - GST_ERROR ("The link functions have recursed, please file a bug!"); - return GST_PAD_LINK_REFUSED; + /* generic checks */ + g_return_val_if_fail (GST_IS_PAD (srcpad), GST_PAD_LINK_REFUSED); + g_return_val_if_fail (GST_IS_PAD (sinkpad), GST_PAD_LINK_REFUSED); + + GST_CAT_INFO (GST_CAT_PADS, "trying to link %s:%s and %s:%s", + GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad)); + + /* now we need to deal with the real/ghost stuff */ + GST_PAD_REALIZE_AND_LOCK (srcpad, realsrc, lost_src_ghostpad); + + if (G_UNLIKELY (GST_RPAD_DIRECTION (realsrc) != GST_PAD_SRC)) + goto not_srcpad; + + if (G_UNLIKELY (GST_RPAD_PEER (realsrc) != NULL)) + goto src_was_linked; + + GST_PAD_REALIZE_AND_LOCK (sinkpad, realsink, lost_sink_ghostpad); + + if (G_UNLIKELY (GST_RPAD_DIRECTION (realsink) != GST_PAD_SINK)) + goto not_sinkpad; + + if (G_UNLIKELY (GST_RPAD_PEER (realsink) != NULL)) + goto sink_was_linked; + + if ((GST_PAD (realsrc) != srcpad) || (GST_PAD (realsink) != sinkpad)) { + GST_CAT_INFO (GST_CAT_PADS, "*actually* linking %s:%s and %s:%s", + GST_DEBUG_PAD_NAME (realsrc), GST_DEBUG_PAD_NAME (realsink)); } + *outrealsrc = realsrc; + *outrealsink = realsink; - /* Both of the pads are in negotiation, so we set the NEGOTIATING flag on both - * of them now to avoid recursion from either pad. */ - GST_FLAG_SET (link->srcpad, GST_PAD_NEGOTIATING); - GST_FLAG_SET (link->sinkpad, GST_PAD_NEGOTIATING); + /* check pad caps for non-empty intersection */ + { + GstCaps *srccaps; + GstCaps *sinkcaps; - /* If this doesn't run, the status is left to the default OK value. */ - if (link->srcnotify && GST_RPAD_LINKFUNC (link->srcpad)) { - /* call the link function */ - GST_DEBUG_OBJECT (link->srcpad, - "calling link function with caps %" GST_PTR_FORMAT, link->caps); - res = GST_RPAD_LINKFUNC (link->srcpad) (GST_PAD (link->srcpad), link->caps); + srccaps = gst_real_pad_get_caps_unlocked (realsrc); + sinkcaps = gst_real_pad_get_caps_unlocked (realsink); + GST_CAT_DEBUG (GST_CAT_CAPS, "got caps %p and %p", srccaps, sinkcaps); - GST_DEBUG_OBJECT (link->srcpad, "got reply %d from link function", res); + if (srccaps && sinkcaps) { + GstCaps *caps; - if (GST_PAD_LINK_FAILED (res)) { - GST_CAT_INFO_OBJECT (GST_CAT_CAPS, link->srcpad, - "pad doesn't accept caps %" GST_PTR_FORMAT, link->caps); + caps = gst_caps_intersect (srccaps, sinkcaps); + GST_CAT_DEBUG (GST_CAT_CAPS, + "intersection caps %p %" GST_PTR_FORMAT, caps, caps); + + if (filtercaps) { + GstCaps *tmp; + + tmp = gst_caps_intersect (caps, filtercaps); + gst_caps_unref (caps); + caps = tmp; + } + if (!caps || gst_caps_is_empty (caps)) + goto no_format; } } - if (GST_PAD_LINK_SUCCESSFUL (res) && - link->sinknotify && GST_RPAD_LINKFUNC (link->sinkpad)) { - /* call the link function */ - GST_DEBUG_OBJECT (link->sinkpad, - "calling link function with caps %" GST_PTR_FORMAT, link->caps); - res = GST_RPAD_LINKFUNC (link->sinkpad) (GST_PAD (link->sinkpad), - link->caps); + /* FIXME check pad scheduling for non-empty intersection */ - GST_DEBUG_OBJECT (link->sinkpad, "got reply %d from link function", res); + /* update filter */ + if (filtercaps) { + GstCaps *filtercopy; - if (GST_PAD_LINK_FAILED (res)) { - GST_CAT_INFO_OBJECT (GST_CAT_CAPS, link->sinkpad, - "pad doesn't accept caps %" GST_PTR_FORMAT, link->caps); - } - } + filtercopy = gst_caps_copy (filtercaps); + filtercopy = gst_caps_ref (filtercopy); - GST_FLAG_UNSET (link->srcpad, GST_PAD_NEGOTIATING); - GST_FLAG_UNSET (link->sinkpad, GST_PAD_NEGOTIATING); - return res; -} - -static GstPadLinkReturn -gst_pad_link_negotiate (GstPadLink * link) -{ - GST_DEBUG ("negotiating link from pad %s:%s to pad %s:%s", - GST_DEBUG_PAD_NAME (link->srcpad), GST_DEBUG_PAD_NAME (link->sinkpad)); - - gst_pad_link_intersect (link); - if (gst_caps_is_empty (link->caps)) - return GST_PAD_LINK_REFUSED; - - gst_pad_link_fixate (link); - if (gst_caps_is_empty (link->caps)) - return GST_PAD_LINK_REFUSED; - - if (!gst_pad_link_ready_for_negotiation (link)) { - return GST_PAD_LINK_DELAYED; - } - GST_DEBUG ("calling link_functions between %s:%s and %s:%s with caps %" - GST_PTR_FORMAT, GST_DEBUG_PAD_NAME (link->srcpad), - GST_DEBUG_PAD_NAME (link->sinkpad), link->caps); - - return gst_pad_link_call_link_functions (link); -} - -/** - * gst_pad_link_try: - * @link: link to try - * - * Tries to (re)link the pads with the given link. The function takes ownership - * of the supplied link. If the function returns FALSE and an old link existed, - * that link can be assumed to work unchanged. - * - * Returns: TRUE if the link succeeded, FALSE if not. - */ -static gboolean -gst_pad_link_try (GstPadLink * link) -{ - GstPad *srcpad, *sinkpad; - GstPadLink *oldlink; - GstPadLinkReturn ret; - - /* we use assertions here, because this function is static */ - g_assert (link); - srcpad = link->srcpad; - g_assert (srcpad); - sinkpad = link->sinkpad; - g_assert (sinkpad); - oldlink = GST_RPAD_LINK (srcpad); - g_assert (oldlink == GST_RPAD_LINK (sinkpad)); - - GST_DEBUG ("negotiating given link"); - ret = gst_pad_link_negotiate (link); - if (GST_PAD_LINK_FAILED (ret) && oldlink && oldlink->caps) { - GST_DEBUG ("negotiating failed, but there was a valid old link"); - oldlink->srcnotify = link->srcnotify; - oldlink->sinknotify = link->sinknotify; - if (GST_PAD_LINK_FAILED (gst_pad_link_call_link_functions (oldlink))) { - g_warning ("pads don't accept old caps. We assume they did though"); - } - } - if (ret == GST_PAD_LINK_REFUSED) { - GST_DEBUG ("link refused, returning"); - gst_pad_link_free (link); - return ret; - } - if (ret == GST_PAD_LINK_DELAYED) { - GST_DEBUG ("link delayed, replacing link caps and returning"); - gst_caps_replace (&link->caps, NULL); - } - - GST_RPAD_PEER (srcpad) = GST_REAL_PAD (link->sinkpad); - GST_RPAD_PEER (sinkpad) = GST_REAL_PAD (link->srcpad); - if (oldlink) { - GST_DEBUG ("copying stuff from oldlink"); - link->temp_store = oldlink->temp_store; - GST_DEBUG ("moving old data temp store %p", link->temp_store); - link->engaged = oldlink->engaged; - oldlink->temp_store = NULL; - gst_pad_link_free (oldlink); - } - GST_RPAD_LINK (srcpad) = link; - GST_RPAD_LINK (sinkpad) = link; - if (ret == GST_PAD_LINK_OK) { - GST_DEBUG ("notifying caps after successful link"); - g_object_notify (G_OBJECT (srcpad), "caps"); - g_object_notify (G_OBJECT (sinkpad), "caps"); - } - - return ret; -} - -/** - * gst_pad_renegotiate: - * @pad: a #GstPad - * - * Initiate caps negotiation on @pad. @pad must be linked. - * - * If @pad's parent is not at least in #GST_STATE_READY, returns - * #GST_PAD_LINK_DELAYED. - * - * Otherwise caps are retrieved from both @pad and its peer by calling their - * getcaps functions. They are then intersected, returning #GST_PAD_LINK_FAIL if - * there is no intersection. - * - * The intersection is fixated if necessary, and then the link functions of @pad - * and its peer are called. - * - * Returns: The return value of @pad's link function (see - * gst_pad_set_link_function()), or #GST_PAD_LINK_OK if there is no link - * function. - * - * The macros GST_PAD_LINK_SUCCESSFUL() and GST_PAD_LINK_FAILED() should be used - * when you just need success/failure information. - */ -GstPadLinkReturn -gst_pad_renegotiate (GstPad * pad) -{ - GstPadLink *link; - - g_return_val_if_fail (GST_IS_PAD (pad), GST_PAD_LINK_REFUSED); - if (!GST_PAD_PEER (pad)) - return GST_PAD_LINK_OK; - - link = gst_pad_link_new (); - - link->srcpad = GST_PAD_LINK_SRC (pad); - link->sinkpad = GST_PAD_LINK_SINK (pad); - - if (!gst_pad_link_ready_for_negotiation (link)) { - gst_pad_link_free (link); - return GST_PAD_LINK_DELAYED; - } - - if (GST_REAL_PAD (pad)->link->filtercaps) { - link->filtercaps = gst_caps_copy (GST_REAL_PAD (pad)->link->filtercaps); - } - link->srccaps = gst_pad_get_caps (link->srcpad); - link->sinkcaps = gst_pad_get_caps (link->sinkpad); - - return gst_pad_link_try (link); -} - -/** - * gst_pad_try_set_caps: - * @pad: a #GstPad - * @caps: #GstCaps to set on @pad - * - * Try to set the caps on @pad. @caps must be fixed. If @pad is unlinked, - * returns #GST_PAD_LINK_OK without doing anything. Otherwise, start caps - * negotiation on @pad. - * - * Returns: The return value of @pad's link function (see - * gst_pad_set_link_function()), or #GST_PAD_LINK_OK if there is no link - * function. - * - * The macros GST_PAD_LINK_SUCCESSFUL() and GST_PAD_LINK_FAILED() should be used - * when you just need success/failure information. - */ -GstPadLinkReturn -gst_pad_try_set_caps (GstPad * pad, const GstCaps * caps) -{ - GstPadLink *link; - GstPadLink *oldlink; - GstPadLinkReturn ret; - - g_return_val_if_fail (GST_IS_REAL_PAD (pad), GST_PAD_LINK_REFUSED); - - GST_LOG_OBJECT (pad, "Trying to set %" GST_PTR_FORMAT, caps); - - if (GST_PAD_IS_NEGOTIATING (pad)) { - GST_DEBUG_OBJECT (pad, "Detected a recursion, just returning OK"); - return GST_PAD_LINK_OK; - } - - GST_CAT_INFO_OBJECT (GST_CAT_CAPS, pad, "caps %" GST_PTR_FORMAT, caps); - /* setting non-fixed caps on a pad is not allowed */ - if (!gst_caps_is_fixed (caps)) { - GST_CAT_INFO (GST_CAT_CAPS, - "trying to set unfixed caps on pad %s:%s, not allowed", - GST_DEBUG_PAD_NAME (pad)); - g_warning ("trying to set non fixed caps on pad %s:%s, not allowed", - GST_DEBUG_PAD_NAME (pad)); - - GST_DEBUG ("unfixed caps %" GST_PTR_FORMAT, caps); - return GST_PAD_LINK_REFUSED; - } - - /* we allow setting caps on non-linked pads. It's ignored */ - if (!GST_PAD_PEER (pad)) { - GST_DEBUG ("unlinked pad %s:%s, returning OK", GST_DEBUG_PAD_NAME (pad)); - return GST_PAD_LINK_OK; - } - - /* we just checked that a peer exists */ - g_assert (GST_PAD_LINK_SRC (pad)); - g_assert (GST_PAD_LINK_SINK (pad)); - - /* if the desired caps are already there, it's trivially ok */ - if (GST_PAD_CAPS (pad) && gst_caps_is_equal (caps, GST_PAD_CAPS (pad))) { - GST_DEBUG ("pad %s:%s already has these caps", GST_DEBUG_PAD_NAME (pad)); - return GST_PAD_LINK_OK; - } - - link = gst_pad_link_new (); - - link->srcpad = GST_PAD_LINK_SRC (pad); - link->sinkpad = GST_PAD_LINK_SINK (pad); - - if (!gst_pad_link_ready_for_negotiation (link)) { - GST_DEBUG ("link not ready for negotiating, delaying"); - gst_pad_link_free (link); - return GST_PAD_LINK_DELAYED; - } - - oldlink = GST_REAL_PAD (pad)->link; - if (oldlink && oldlink->filtercaps) { - link->filtercaps = gst_caps_copy (oldlink->filtercaps); - } - if (link->srcpad == pad) { - link->srccaps = gst_caps_copy (caps); - link->sinkcaps = gst_pad_get_caps (link->sinkpad); - link->srcnotify = FALSE; + gst_caps_replace (&GST_PAD_APPFILTER (realsrc), filtercopy); + gst_caps_replace (&GST_PAD_APPFILTER (realsink), filtercopy); } else { - link->srccaps = gst_pad_get_caps (link->srcpad); - link->sinkcaps = gst_caps_copy (caps); - link->sinknotify = FALSE; + gst_caps_replace (&GST_PAD_APPFILTER (realsrc), NULL); + gst_caps_replace (&GST_PAD_APPFILTER (realsink), NULL); } + return GST_PAD_LINK_OK; - GST_DEBUG ("trying to link"); - ret = gst_pad_link_try (link); - - return ret; -} - -/* FIXME, temporarily put here until real code gets backported */ -gboolean -gst_pad_set_caps (GstPad * pad, GstCaps * caps) -{ - return TRUE; -} - -/** - * gst_pad_try_set_caps_nonfixed: - * @pad: a real #GstPad - * @caps: #GstCaps to set on @pad - * - * Like gst_pad_try_set_caps(), but allows non-fixed caps. - * - * Returns: a #GstPadLinkReturn, like gst_pad_try_set_caps(). - */ -GstPadLinkReturn -gst_pad_try_set_caps_nonfixed (GstPad * pad, const GstCaps * caps) -{ - GstPadLink *link; - GstPadLink *oldlink; - GstPadLinkReturn ret; - - g_return_val_if_fail (GST_IS_REAL_PAD (pad), GST_PAD_LINK_REFUSED); - g_return_val_if_fail (!GST_PAD_IS_NEGOTIATING (pad), GST_PAD_LINK_REFUSED); - - /* we allow setting caps on non-linked pads. It's ignored */ - if (!GST_PAD_PEER (pad)) { - return GST_PAD_LINK_OK; +lost_src_ghostpad: + { + return GST_PAD_LINK_REFUSED; } - - /* we just checked that a peer exists */ - g_assert (GST_PAD_LINK_SRC (pad)); - g_assert (GST_PAD_LINK_SINK (pad)); - - /* if the link is already negotiated and the caps are compatible - * with what we're setting, it's trivially OK. */ - if (GST_PAD_CAPS (pad)) { - GstCaps *intersection; - - intersection = gst_caps_intersect (caps, GST_PAD_CAPS (pad)); - if (!gst_caps_is_empty (intersection)) { - gst_caps_unref (intersection); - return GST_PAD_LINK_OK; - } - gst_caps_unref (intersection); +not_srcpad: + { + g_critical ("pad %s is not a source pad", GST_PAD_NAME (realsrc)); + GST_UNLOCK (realsrc); + return GST_PAD_LINK_WRONG_DIRECTION; } - - link = gst_pad_link_new (); - - link->srcpad = GST_PAD_LINK_SRC (pad); - link->sinkpad = GST_PAD_LINK_SINK (pad); - - if (!gst_pad_link_ready_for_negotiation (link)) { - gst_pad_link_free (link); - return GST_PAD_LINK_DELAYED; +src_was_linked: + { + GST_CAT_INFO (GST_CAT_PADS, "src %s:%s was linked", + GST_DEBUG_PAD_NAME (realsrc)); + /* we do not emit a warning in this case because unlinking cannot + * be made MT safe.*/ + GST_UNLOCK (realsrc); + return GST_PAD_LINK_WAS_LINKED; } - - oldlink = GST_REAL_PAD (pad)->link; - if (oldlink && oldlink->filtercaps) { - link->filtercaps = gst_caps_copy (oldlink->filtercaps); +lost_sink_ghostpad: + { + GST_DEBUG ("lost sink ghostpad"); + GST_UNLOCK (realsrc); + return GST_PAD_LINK_REFUSED; } - if (link->srcpad == pad) { - link->srccaps = gst_caps_copy (caps); - link->sinkcaps = gst_pad_get_caps (link->sinkpad); - link->srcnotify = FALSE; - } else { - link->srccaps = gst_pad_get_caps (link->srcpad); - link->sinkcaps = gst_caps_copy (caps); - link->sinknotify = FALSE; +not_sinkpad: + { + g_critical ("pad %s is not a sink pad", GST_PAD_NAME (realsink)); + GST_UNLOCK (realsink); + GST_UNLOCK (realsrc); + return GST_PAD_LINK_WRONG_DIRECTION; + } +sink_was_linked: + { + GST_CAT_INFO (GST_CAT_PADS, "sink %s:%s was linked", + GST_DEBUG_PAD_NAME (realsink)); + /* we do not emit a warning in this case because unlinking cannot + * be made MT safe.*/ + GST_UNLOCK (realsink); + GST_UNLOCK (realsrc); + return GST_PAD_LINK_WAS_LINKED; + } +no_format: + { + GST_CAT_INFO (GST_CAT_PADS, "caps are incompatible"); + GST_UNLOCK (realsink); + GST_UNLOCK (realsrc); + return GST_PAD_LINK_NOFORMAT; } - - ret = gst_pad_link_try (link); - - return ret; } /** @@ -1711,120 +1369,82 @@ gst_pad_try_set_caps_nonfixed (GstPad * pad, const GstCaps * caps) * Links the source pad and the sink pad, constrained * by the given filter caps. * - * Returns: TRUE if the pads have been linked, FALSE otherwise. + * The filtercaps will be copied and refcounted, so you should unref + * it yourself after using this function. + * + * Returns: A result code indicating if the connection worked or + * what went wrong. + * + * MT Safe. */ -gboolean +GstPadLinkReturn gst_pad_link_filtered (GstPad * srcpad, GstPad * sinkpad, const GstCaps * filtercaps) { GstRealPad *realsrc, *realsink; - GstScheduler *src_sched, *sink_sched; - GstPadLink *link; + GstPadLinkReturn result; - /* generic checks */ - g_return_val_if_fail (srcpad != NULL, FALSE); - g_return_val_if_fail (GST_IS_PAD (srcpad), FALSE); - g_return_val_if_fail (sinkpad != NULL, FALSE); - g_return_val_if_fail (GST_IS_PAD (sinkpad), FALSE); + result = gst_pad_link_prepare_filtered (srcpad, sinkpad, &realsrc, &realsink, + filtercaps); - GST_CAT_INFO (GST_CAT_PADS, "trying to link %s:%s and %s:%s", - GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad)); + if (result != GST_PAD_LINK_OK) + goto prepare_failed; - /* now we need to deal with the real/ghost stuff */ - realsrc = GST_PAD_REALIZE (srcpad); - realsink = GST_PAD_REALIZE (sinkpad); + GST_UNLOCK (realsink); + GST_UNLOCK (realsrc); - if ((GST_PAD (realsrc) != srcpad) || (GST_PAD (realsink) != sinkpad)) { - GST_CAT_INFO (GST_CAT_PADS, "*actually* linking %s:%s and %s:%s", + /* FIXME released the locks here, concurrent thread might link + * something else. */ + if (GST_RPAD_LINKFUNC (realsrc)) { + /* this one will call the peer link function */ + result = + GST_RPAD_LINKFUNC (realsrc) (GST_PAD (realsrc), GST_PAD (realsink)); + } else if (GST_RPAD_LINKFUNC (realsink)) { + /* if no source link function, we need to call the sink link + * function ourselves. */ + result = + GST_RPAD_LINKFUNC (realsink) (GST_PAD (realsink), GST_PAD (realsrc)); + } else { + result = GST_PAD_LINK_OK; + } + + GST_LOCK (realsrc); + GST_LOCK (realsink); + if (result == GST_PAD_LINK_OK) { + GST_RPAD_PEER (realsrc) = GST_REAL_PAD (realsink); + GST_RPAD_PEER (realsink) = GST_REAL_PAD (realsrc); + + GST_UNLOCK (realsink); + GST_UNLOCK (realsrc); + + /* fire off a signal to each of the pads telling them + * that they've been linked */ + g_signal_emit (G_OBJECT (realsrc), gst_real_pad_signals[REAL_LINKED], + 0, realsink); + g_signal_emit (G_OBJECT (realsink), gst_real_pad_signals[REAL_LINKED], + 0, realsrc); + + GST_CAT_INFO (GST_CAT_PADS, "linked %s:%s and %s:%s, successful", GST_DEBUG_PAD_NAME (realsrc), GST_DEBUG_PAD_NAME (realsink)); - } - /* FIXME: shouldn't we convert this to g_return_val_if_fail? */ - if (GST_RPAD_PEER (realsrc) != NULL) { - GST_CAT_INFO (GST_CAT_PADS, "Real source pad %s:%s has a peer, failed", - GST_DEBUG_PAD_NAME (realsrc)); - return FALSE; - } - if (GST_RPAD_PEER (realsink) != NULL) { - GST_CAT_INFO (GST_CAT_PADS, "Real sink pad %s:%s has a peer, failed", - GST_DEBUG_PAD_NAME (realsink)); - return FALSE; - } - if (GST_PAD_PARENT (realsrc) == NULL) { - GST_CAT_INFO (GST_CAT_PADS, "Real src pad %s:%s has no parent, failed", - GST_DEBUG_PAD_NAME (realsrc)); - return FALSE; - } - if (GST_PAD_PARENT (realsink) == NULL) { - GST_CAT_INFO (GST_CAT_PADS, "Real sink pad %s:%s has no parent, failed", - GST_DEBUG_PAD_NAME (realsrc)); - return FALSE; - } - - if (!gst_pad_check_schedulers (realsrc, realsink)) { - g_warning ("linking pads with different scheds requires " - "exactly one decoupled element (such as queue)"); - return FALSE; - } - - g_return_val_if_fail (realsrc != NULL, GST_PAD_LINK_REFUSED); - g_return_val_if_fail (realsink != NULL, GST_PAD_LINK_REFUSED); - - link = gst_pad_link_new (); - - if (GST_RPAD_DIRECTION (realsrc) == GST_PAD_SRC) { - link->srcpad = GST_PAD (realsrc); - link->sinkpad = GST_PAD (realsink); } else { - link->srcpad = GST_PAD (realsink); - link->sinkpad = GST_PAD (realsrc); + GST_CAT_INFO (GST_CAT_PADS, "link between %s:%s and %s:%s failed", + GST_DEBUG_PAD_NAME (realsrc), GST_DEBUG_PAD_NAME (realsink)); + + /* remove the filter again */ + if (filtercaps) { + gst_caps_replace (&GST_RPAD_APPFILTER (realsrc), NULL); + gst_caps_replace (&GST_RPAD_APPFILTER (realsink), NULL); + } + + GST_UNLOCK (realsink); + GST_UNLOCK (realsrc); } + return result; - if (GST_RPAD_DIRECTION (link->srcpad) != GST_PAD_SRC) { - GST_CAT_INFO (GST_CAT_PADS, - "Real src pad %s:%s is not a source pad, failed", - GST_DEBUG_PAD_NAME (link->srcpad)); - gst_pad_link_free (link); - return FALSE; +prepare_failed: + { + return result; } - if (GST_RPAD_DIRECTION (link->sinkpad) != GST_PAD_SINK) { - GST_CAT_INFO (GST_CAT_PADS, "Real sink pad %s:%s is not a sink pad, failed", - GST_DEBUG_PAD_NAME (link->sinkpad)); - gst_pad_link_free (link); - return FALSE; - } - - link->srccaps = gst_pad_get_caps (link->srcpad); - link->sinkcaps = gst_pad_get_caps (link->sinkpad); - if (filtercaps) - link->filtercaps = gst_caps_copy (filtercaps); - if (gst_pad_link_try (link) == GST_PAD_LINK_REFUSED) - return FALSE; - - /* fire off a signal to each of the pads telling them - * that they've been linked */ - g_signal_emit (G_OBJECT (link->srcpad), gst_real_pad_signals[REAL_LINKED], - 0, link->sinkpad); - g_signal_emit (G_OBJECT (link->sinkpad), gst_real_pad_signals[REAL_LINKED], - 0, link->srcpad); - - src_sched = gst_pad_get_scheduler (GST_PAD (link->srcpad)); - sink_sched = gst_pad_get_scheduler (GST_PAD (link->sinkpad)); - - /* now tell the scheduler */ - if (src_sched && src_sched == sink_sched) { - gst_scheduler_pad_link (src_sched, - GST_PAD (link->srcpad), GST_PAD (link->sinkpad)); - } else { - GST_CAT_INFO (GST_CAT_PADS, - "not telling link to scheduler %s:%s and %s:%s, %p %p", - GST_DEBUG_PAD_NAME (link->srcpad), GST_DEBUG_PAD_NAME (link->sinkpad), - src_sched, sink_sched); - } - - GST_CAT_INFO (GST_CAT_PADS, "linked %s:%s and %s:%s, successful", - GST_DEBUG_PAD_NAME (link->srcpad), GST_DEBUG_PAD_NAME (link->sinkpad)); - - return TRUE; } /** @@ -1834,9 +1454,10 @@ gst_pad_link_filtered (GstPad * srcpad, GstPad * sinkpad, * * Links the source pad to the sink pad. * - * Returns: TRUE if the pad could be linked, FALSE otherwise. + * Returns: A result code indicating if the connection worked or + * what went wrong. */ -gboolean +GstPadLinkReturn gst_pad_link (GstPad * srcpad, GstPad * sinkpad) { return gst_pad_link_filtered (srcpad, sinkpad, NULL); @@ -1876,43 +1497,6 @@ gst_pad_get_pad_template (GstPad * pad) } -/** - * gst_pad_get_scheduler: - * @pad: a #GstPad to get the scheduler of. - * - * Gets the scheduler of the pad. Since the pad does not - * have a scheduler of its own, the scheduler of the parent - * is taken. For decoupled pads, the scheduler of the peer - * parent is taken. - * - * Returns: the #GstScheduler of the pad, or %NULL if there is no parent or the - * parent is not yet in a managing bin. - */ -GstScheduler * -gst_pad_get_scheduler (GstPad * pad) -{ - GstScheduler *scheduler = NULL; - GstElement *parent; - - g_return_val_if_fail (GST_IS_PAD (pad), NULL); - - parent = gst_pad_get_parent (pad); - if (parent) { - if (GST_FLAG_IS_SET (parent, GST_ELEMENT_DECOUPLED)) { - GstRealPad *peer = GST_RPAD_PEER (pad); - - if (peer) { - scheduler = - gst_element_get_scheduler (gst_pad_get_parent (GST_PAD (peer))); - } - } else { - scheduler = gst_element_get_scheduler (parent); - } - } - - return scheduler; -} - /** * gst_pad_get_real_parent: * @pad: a #GstPad to get the real parent of. @@ -1979,237 +1563,6 @@ gst_pad_remove_ghost_pad (GstPad * pad, GstPad * ghostpad) GST_GPAD_REALPAD (ghostpad) = NULL; } -static gboolean -_gst_pad_default_fixate_value (const GValue * value, GValue * dest) -{ - GType type = G_VALUE_TYPE (value); - - if (gst_value_is_fixed (value)) - return TRUE; - - if (type == GST_TYPE_INT_RANGE) { - g_value_init (dest, G_TYPE_INT); - g_value_set_int (dest, gst_value_get_int_range_min (value)); - } else if (type == GST_TYPE_DOUBLE_RANGE) { - g_value_init (dest, G_TYPE_DOUBLE); - g_value_set_double (dest, gst_value_get_double_range_min (value)); - } else if (type == GST_TYPE_LIST) { - gst_value_init_and_copy (dest, gst_value_list_get_value (value, 0)); - } else if (type == GST_TYPE_FIXED_LIST) { - gint size, n; - GValue dest_kid = { 0 }; - const GValue *kid; - - /* check recursively */ - g_value_init (dest, GST_TYPE_FIXED_LIST); - size = gst_value_list_get_size (value); - for (n = 0; n < size; n++) { - kid = gst_value_list_get_value (value, n); - if (_gst_pad_default_fixate_value (kid, &dest_kid)) { - gst_value_list_append_value (dest, kid); - } else { - gst_value_list_append_value (dest, &dest_kid); - g_value_unset (&dest_kid); - } - } - } else { - g_critical ("Don't know how to fixate value type %s", g_type_name (type)); - } - - return FALSE; -} - -static gboolean -_gst_pad_default_fixate_foreach (GQuark field_id, const GValue * value, - gpointer s) -{ - GstStructure *structure = (GstStructure *) s; - GValue dest = { 0 }; - - if (_gst_pad_default_fixate_value (value, &dest)) - return TRUE; - gst_structure_id_set_value (structure, field_id, &dest); - g_value_unset (&dest); - - return FALSE; -} - -static GstCaps * -_gst_pad_default_fixate_func (GstPad * pad, const GstCaps * caps) -{ - static GstStaticCaps octetcaps = GST_STATIC_CAPS ("application/octet-stream"); - GstStructure *structure; - GstCaps *newcaps; - - g_return_val_if_fail (pad != NULL, NULL); - g_return_val_if_fail (caps != NULL, NULL); - g_return_val_if_fail (!gst_caps_is_empty (caps), NULL); - - if (gst_caps_is_any (caps)) { - return gst_caps_copy (gst_static_caps_get (&octetcaps)); - } - - if (caps->structs->len > 1) { - return gst_caps_new_full (gst_structure_copy (gst_caps_get_structure (caps, - 0)), NULL); - } - - newcaps = gst_caps_copy (caps); - structure = gst_caps_get_structure (newcaps, 0); - gst_structure_foreach (structure, _gst_pad_default_fixate_foreach, structure); - - return newcaps; -} - -/** - * gst_pad_perform_negotiate: - * @srcpad: the source #GstPad. - * @sinkpad: the sink #GstPad. - * - * Tries to negotiate the pads. See gst_pad_renegotiate() for a brief - * description of caps negotiation. - * - * Returns: TRUE if the pads were succesfully negotiated, FALSE otherwise. - */ -gboolean -gst_pad_perform_negotiate (GstPad * srcpad, GstPad * sinkpad) -{ - return GST_PAD_LINK_SUCCESSFUL (gst_pad_renegotiate (srcpad)); -} - -static void -gst_pad_link_unnegotiate (GstPadLink * link) -{ - g_return_if_fail (link != NULL); - - if (link->caps) { - gst_caps_unref (link->caps); - link->caps = NULL; - link->engaged = FALSE; - if (GST_RPAD_LINK (link->srcpad) != link) { - g_warning ("unnegotiating unset link"); - } else { - g_object_notify (G_OBJECT (link->srcpad), "caps"); - } - if (GST_RPAD_LINK (link->sinkpad) != link) { - g_warning ("unnegotiating unset link"); - } else { - g_object_notify (G_OBJECT (link->sinkpad), "caps"); - } - } -} - -/** - * gst_pad_unnegotiate: - * @pad: pad to unnegotiate - * - * "Unnegotiates" a pad. The currently negotiated caps are cleared and the pad - * needs renegotiation. - */ -void -gst_pad_unnegotiate (GstPad * pad) -{ - GstPadLink *link; - - g_return_if_fail (GST_IS_PAD (pad)); - - link = GST_RPAD_LINK (GST_PAD_REALIZE (pad)); - if (link) - gst_pad_link_unnegotiate (link); -} - -/* returning NULL indicates that the arguments are invalid */ -static GstPadLink * -gst_pad_link_prepare (GstPad * srcpad, GstPad * sinkpad, - const GstCaps * filtercaps) -{ - GstRealPad *realsrc, *realsink; - GstPadLink *link; - - g_return_val_if_fail (GST_IS_PAD (srcpad), NULL); - g_return_val_if_fail (GST_IS_PAD (sinkpad), NULL); - - realsrc = GST_PAD_REALIZE (srcpad); - realsink = GST_PAD_REALIZE (sinkpad); - - if ((GST_PAD (realsrc) != srcpad) || (GST_PAD (realsink) != sinkpad)) { - GST_CAT_DEBUG (GST_CAT_PADS, "*actually* linking %s:%s and %s:%s", - GST_DEBUG_PAD_NAME (realsrc), GST_DEBUG_PAD_NAME (realsink)); - } - - g_return_val_if_fail (GST_RPAD_PEER (realsrc) == NULL, NULL); - g_return_val_if_fail (GST_RPAD_PEER (realsink) == NULL, NULL); - g_return_val_if_fail (GST_PAD_PARENT (realsrc) != NULL, NULL); - g_return_val_if_fail (GST_PAD_PARENT (realsink) != NULL, NULL); - - if (!gst_pad_check_schedulers (realsrc, realsink)) { - g_warning ("linking pads with different scheds requires " - "exactly one decoupled element (such as queue)"); - return NULL; - } - - if (GST_RPAD_DIRECTION (realsrc) == GST_RPAD_DIRECTION (realsink)) { - g_warning ("%s:%s and %s:%s are both %s pads, failed", - GST_DEBUG_PAD_NAME (realsrc), GST_DEBUG_PAD_NAME (realsink), - GST_RPAD_DIRECTION (realsrc) == GST_PAD_SRC ? "src" : "sink"); - return NULL; - } - - link = gst_pad_link_new (); - - if (GST_RPAD_DIRECTION (realsrc) == GST_PAD_SRC) { - link->srcpad = GST_PAD (realsrc); - link->sinkpad = GST_PAD (realsink); - } else { - link->srcpad = GST_PAD (realsink); - link->sinkpad = GST_PAD (realsrc); - } - - link->srccaps = gst_pad_get_caps (link->srcpad); - link->sinkcaps = gst_pad_get_caps (link->sinkpad); - if (filtercaps) - link->filtercaps = gst_caps_copy (filtercaps); - - return link; -} - -/** - * gst_pad_try_relink_filtered: - * @srcpad: the source #GstPad to relink. - * @sinkpad: the sink #GstPad to relink. - * @filtercaps: the #GstPad to use as a filter in the relink. - * - * Tries to relink the given source and sink pad, constrained by the given - * capabilities. - * - * Returns: TRUE if the pads were succesfully renegotiated, FALSE otherwise. - */ -gboolean -gst_pad_try_relink_filtered (GstPad * srcpad, GstPad * sinkpad, - const GstCaps * filtercaps) -{ - GstPadLink *link; - - GST_INFO ("trying to relink %" GST_PTR_FORMAT " and %" GST_PTR_FORMAT - " with filtercaps %" GST_PTR_FORMAT, srcpad, sinkpad); - - link = gst_pad_link_prepare (srcpad, sinkpad, filtercaps); - if (!link) - return FALSE; - - if (GST_RPAD_PEER (link->srcpad) != (GstRealPad *) link->sinkpad) { - g_warning ("Pads %s:%s and %s:%s were never linked", - GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad)); - gst_pad_link_free (link); - return FALSE; - } - - if (GST_PAD_LINK_FAILED (gst_pad_link_try (link))) - return FALSE; - - return TRUE; -} - /** * gst_pad_relink_filtered: * @srcpad: the source #GstPad to relink. @@ -2218,230 +1571,190 @@ gst_pad_try_relink_filtered (GstPad * srcpad, GstPad * sinkpad, * * Relinks the given source and sink pad, constrained by the given * capabilities. If the relink fails, the pads are unlinked - * and FALSE is returned. + * and an error code is returned. * - * Returns: TRUE if the pads were succesfully relinked, FALSE otherwise. + * Returns: The result code of the operation. + * + * MT safe */ -gboolean +GstPadLinkReturn gst_pad_relink_filtered (GstPad * srcpad, GstPad * sinkpad, const GstCaps * filtercaps) { - if (gst_pad_try_relink_filtered (srcpad, sinkpad, filtercaps)) - return TRUE; + GstRealPad *realsrc, *realsink; - gst_pad_unlink (srcpad, sinkpad); - return FALSE; -} + /* FIXME refactor and share code with link/unlink */ -/** - * gst_pad_proxy_fixate: - * @pad: a #GstPad to proxy. - * @caps: the #GstCaps to fixate - * - * Implements a default fixate function based on the caps set on the other - * pads in the element. This function should only be used if every pad - * has the same pad template caps. - * - * Returns: a fixated caps, or NULL if caps cannot be fixed - */ -GstCaps * -gst_pad_proxy_fixate (GstPad * pad, const GstCaps * caps) -{ - GstElement *element; - const GList *pads; - const GstCaps *othercaps; + /* generic checks */ + g_return_val_if_fail (GST_IS_PAD (srcpad), GST_PAD_LINK_REFUSED); + g_return_val_if_fail (GST_IS_PAD (sinkpad), GST_PAD_LINK_REFUSED); - g_return_val_if_fail (GST_IS_PAD (pad), NULL); - g_return_val_if_fail (caps != NULL, NULL); + GST_CAT_INFO (GST_CAT_PADS, "trying to relink %s:%s and %s:%s", + GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad)); - GST_DEBUG ("proxying fixate for %s:%s\n", GST_DEBUG_PAD_NAME (pad)); + /* now we need to deal with the real/ghost stuff */ + GST_PAD_REALIZE_AND_LOCK (srcpad, realsrc, lost_src_ghostpad); - element = gst_pad_get_parent (pad); + if (G_UNLIKELY (GST_RPAD_DIRECTION (realsrc) != GST_PAD_SRC)) + goto not_srcpad; - /* FIXME, not MT safe but this function will be removed */ - pads = element->pads; + GST_PAD_REALIZE_AND_LOCK (sinkpad, realsink, lost_sink_ghostpad); - while (pads) { - GstPad *otherpad = GST_PAD (pads->data); + if (G_UNLIKELY (GST_RPAD_DIRECTION (realsink) != GST_PAD_SINK)) + goto not_sinkpad; - /* FIXME check that each pad has the same pad template caps */ + if (G_UNLIKELY (GST_RPAD_PEER (realsink) != realsrc)) + goto not_linked_together; - if (otherpad != pad) { - othercaps = gst_pad_get_negotiated_caps (otherpad); - - if (othercaps && !gst_caps_is_subset (caps, othercaps)) { - GstCaps *icaps; - - icaps = gst_caps_intersect (othercaps, caps); - if (!gst_caps_is_empty (icaps)) { - return icaps; - } else { - gst_caps_unref (icaps); - } - } - } - pads = g_list_next (pads); + if ((GST_PAD (realsrc) != srcpad) || (GST_PAD (realsink) != sinkpad)) { + GST_CAT_INFO (GST_CAT_PADS, "*actually* relinking %s:%s and %s:%s", + GST_DEBUG_PAD_NAME (realsrc), GST_DEBUG_PAD_NAME (realsink)); } - return NULL; -} + /* update filter */ + if (filtercaps) { + GstCaps *filtercopy; -/** - * gst_pad_set_explicit_caps: - * @pad: a #GstPad to set the explicit caps of - * @caps: the #GstCaps to set - * - * If a pad has been told to use explicit caps, this function is used - * to set the explicit caps. If @caps is NULL, the explicit caps are - * unset. - * - * This function calls gst_pad_try_set_caps() on the pad. If that - * call fails, GST_ELEMENT_ERROR() is called to indicate a negotiation - * failure. - * - * Returns: TRUE if the caps were set correctly, otherwise FALSE - */ -gboolean -gst_pad_set_explicit_caps (GstPad * pad, const GstCaps * caps) -{ - GstPadLinkReturn link_ret; + filtercopy = gst_caps_copy (filtercaps); + filtercopy = gst_caps_ref (filtercopy); - g_return_val_if_fail (GST_IS_PAD (pad), FALSE); - g_return_val_if_fail (caps == NULL || gst_caps_is_fixed (caps), FALSE); - - GST_CAT_DEBUG (GST_CAT_PADS, - "setting explicit caps on %s:%s to %" GST_PTR_FORMAT, - GST_DEBUG_PAD_NAME (pad), caps); - - if (caps == NULL) { - GST_CAT_DEBUG (GST_CAT_PADS, "caps is NULL"); - gst_caps_replace (&GST_RPAD_EXPLICIT_CAPS (pad), NULL); - return TRUE; + gst_caps_replace (&GST_PAD_APPFILTER (realsrc), filtercopy); + gst_caps_replace (&GST_PAD_APPFILTER (realsink), filtercopy); + } else { + gst_caps_replace (&GST_PAD_APPFILTER (realsrc), NULL); + gst_caps_replace (&GST_PAD_APPFILTER (realsink), NULL); } + /* clear caps to force renegotiation */ + gst_caps_replace (&GST_PAD_CAPS (realsrc), NULL); + gst_caps_replace (&GST_PAD_CAPS (realsink), NULL); + GST_UNLOCK (realsink); + GST_UNLOCK (realsrc); - gst_caps_replace (&GST_RPAD_EXPLICIT_CAPS (pad), gst_caps_copy (caps)); - - if (!GST_PAD_IS_LINKED (pad)) { - GST_CAT_DEBUG (GST_CAT_PADS, "pad is not linked"); - return TRUE; - } - link_ret = gst_pad_try_set_caps (pad, caps); - if (link_ret == GST_PAD_LINK_REFUSED) { - gchar *caps_str = gst_caps_to_string (caps); - - GST_ELEMENT_ERROR (gst_pad_get_parent (pad), CORE, PAD, (NULL), - ("failed to negotiate (try_set_caps with \"%s\" returned REFUSED)", - caps_str)); - g_free (caps_str); - return FALSE; - } - - return TRUE; -} - -static GstCaps * -gst_pad_explicit_getcaps (GstPad * pad) -{ - g_return_val_if_fail (GST_IS_PAD (pad), NULL); - - if (GST_RPAD_EXPLICIT_CAPS (pad) == NULL) { - const GstCaps *caps = gst_pad_get_pad_template_caps (pad); - - return gst_caps_copy (caps); - } - return gst_caps_copy (GST_RPAD_EXPLICIT_CAPS (pad)); -} - -static GstPadLinkReturn -gst_pad_explicit_link (GstPad * pad, const GstCaps * caps) -{ - g_return_val_if_fail (GST_IS_PAD (pad), GST_PAD_LINK_REFUSED); - g_return_val_if_fail (caps != NULL, GST_PAD_LINK_REFUSED); - - if (GST_RPAD_EXPLICIT_CAPS (pad) == NULL) { - return GST_PAD_LINK_DELAYED; - } + GST_CAT_INFO (GST_CAT_PADS, "relinked %s:%s and %s:%s, successful", + GST_DEBUG_PAD_NAME (realsrc), GST_DEBUG_PAD_NAME (realsink)); return GST_PAD_LINK_OK; + +lost_src_ghostpad: + { + return GST_PAD_LINK_REFUSED; + } +not_srcpad: + { + g_critical ("pad %s is not a source pad", GST_PAD_NAME (realsrc)); + GST_UNLOCK (realsrc); + return GST_PAD_LINK_WRONG_DIRECTION; + } +lost_sink_ghostpad: + { + GST_DEBUG ("lost sink ghostpad"); + GST_UNLOCK (realsrc); + return GST_PAD_LINK_REFUSED; + } +not_sinkpad: + { + g_critical ("pad %s is not a sink pad", GST_PAD_NAME (realsink)); + GST_UNLOCK (realsink); + GST_UNLOCK (realsrc); + return GST_PAD_LINK_WRONG_DIRECTION; + } +not_linked_together: + { + GST_CAT_INFO (GST_CAT_PADS, "src %s:%s was not linked with sink %s:%s", + GST_DEBUG_PAD_NAME (realsrc), GST_DEBUG_PAD_NAME (realsink)); + /* we do not emit a warning in this case because unlinking cannot + * be made MT safe.*/ + GST_UNLOCK (realsink); + GST_UNLOCK (realsrc); + return GST_PAD_LINK_REFUSED; + } } -/** - * gst_pad_use_explicit_caps: - * @pad: a #GstPad to set to use explicit caps - * - * This function handles negotiation for pads that need to be set - * to particular caps under complete control of the element, based - * on some state in the element. This is often the case with - * decoders and other elements whose caps is determined by the data - * stream. - * - * WARNING: This function is a hack and will be replaced with something - * better in gstreamer-0.9. - */ -void -gst_pad_use_explicit_caps (GstPad * pad) +/* should be called with the pad LOCK held */ +static GstCaps * +gst_real_pad_get_caps_unlocked (GstRealPad * realpad) { - g_return_if_fail (GST_IS_PAD (pad)); + GstCaps *result = NULL, *filter; - gst_pad_set_getcaps_function (pad, gst_pad_explicit_getcaps); - gst_pad_set_link_function (pad, gst_pad_explicit_link); - gst_caps_replace (&GST_RPAD_EXPLICIT_CAPS (pad), NULL); -} + GST_CAT_DEBUG (GST_CAT_CAPS, "get pad caps of %s:%s (%p)", + GST_DEBUG_PAD_NAME (realpad), realpad); -/** - * gst_pad_proxy_link: - * @pad: a #GstPad to proxy to. - * @caps: the #GstCaps to use in proxying. - * - * Proxies the link function to the specified pad. - * - * Returns: TRUE if the peer pad accepted the caps, FALSE otherwise. - */ -GstPadLinkReturn -gst_pad_proxy_link (GstPad * pad, const GstCaps * caps) -{ - return gst_pad_try_set_caps (pad, caps); -} + if (GST_RPAD_GETCAPSFUNC (realpad)) { + GST_CAT_DEBUG (GST_CAT_CAPS, "dispatching to pad getcaps function"); -/** - * gst_pad_is_negotiated: - * @pad: a #GstPad to get the negotiation status of - * - * Returns: TRUE if the pad has successfully negotiated caps. - */ -gboolean -gst_pad_is_negotiated (GstPad * pad) -{ - g_return_val_if_fail (GST_IS_PAD (pad), FALSE); + GST_FLAG_SET (realpad, GST_PAD_IN_GETCAPS); + GST_UNLOCK (realpad); + result = GST_RPAD_GETCAPSFUNC (realpad) (GST_PAD (realpad)); + GST_LOCK (realpad); + GST_FLAG_UNSET (realpad, GST_PAD_IN_GETCAPS); - if (!(pad = (GstPad *) GST_PAD_REALIZE (pad))) - return FALSE; - if (!GST_RPAD_LINK (pad)) - return FALSE; + if (result == NULL) { + g_critical ("pad %s:%s returned NULL caps from getcaps function\n", + GST_DEBUG_PAD_NAME (realpad)); + } else { +#ifndef G_DISABLE_ASSERT + /* check that the returned caps are a real subset of the template caps */ + if (GST_PAD_PAD_TEMPLATE (realpad)) { + const GstCaps *templ_caps = + GST_PAD_TEMPLATE_CAPS (GST_PAD_PAD_TEMPLATE (realpad)); + if (!gst_caps_is_subset (result, templ_caps)) { + GstCaps *temp; - return (GST_RPAD_LINK (pad)->caps != NULL); -} + GST_CAT_ERROR_OBJECT (GST_CAT_CAPS, realpad, + "pad returned caps %" GST_PTR_FORMAT + " which are not a real subset of its template caps %" + GST_PTR_FORMAT, result, templ_caps); + g_warning + ("pad %s:%s returned caps that are not a real subset of its template caps", + GST_DEBUG_PAD_NAME (realpad)); + temp = gst_caps_intersect (templ_caps, result); + gst_caps_unref (result); + result = temp; + } + } +#endif + goto done; + } + } + if (GST_PAD_PAD_TEMPLATE (realpad)) { + GstPadTemplate *templ = GST_PAD_PAD_TEMPLATE (realpad); -/** - * gst_pad_get_negotiated_caps: - * @pad: a #GstPad to get the negotiated capabilites of - * - * Gets the currently negotiated caps of a pad. - * - * Returns: the currently negotiated caps of a pad, or NULL if the pad isn't - * negotiated. - */ -G_CONST_RETURN GstCaps * -gst_pad_get_negotiated_caps (GstPad * pad) -{ - g_return_val_if_fail (GST_IS_PAD (pad), NULL); + result = GST_PAD_TEMPLATE_CAPS (templ); + GST_CAT_DEBUG (GST_CAT_CAPS, + "using pad template %p with caps %p %" GST_PTR_FORMAT, templ, result, + result); - if (!(pad = (GstPad *) GST_PAD_REALIZE (pad))) - return NULL; - if (!GST_RPAD_LINK (pad)) - return NULL; + result = gst_caps_ref (result); + goto done; + } + if (GST_RPAD_CAPS (realpad)) { + result = GST_RPAD_CAPS (realpad); - return GST_RPAD_LINK (pad)->caps; + GST_CAT_DEBUG (GST_CAT_CAPS, + "using pad caps %p %" GST_PTR_FORMAT, result, result); + + result = gst_caps_ref (result); + goto done; + } + + GST_CAT_DEBUG (GST_CAT_CAPS, "pad has no caps"); + result = gst_caps_new_empty (); + +done: + filter = GST_RPAD_APPFILTER (realpad); + + if (filter) { + GstCaps *temp = result; + + GST_CAT_DEBUG (GST_CAT_CAPS, + "app filter %p %" GST_PTR_FORMAT, filter, filter); + result = gst_caps_intersect (temp, filter); + gst_caps_unref (temp); + GST_CAT_DEBUG (GST_CAT_CAPS, + "caps after intersection with app filter %p %" GST_PTR_FORMAT, result, + result); + } + return result; } /** @@ -2452,90 +1765,44 @@ gst_pad_get_negotiated_caps (GstPad * pad) * * Returns: the #GstCaps of this pad. This function returns a new caps, so use * gst_caps_unref to get rid of it. + * + * MT safe. */ GstCaps * gst_pad_get_caps (GstPad * pad) { GstRealPad *realpad; + GstCaps *result = NULL; g_return_val_if_fail (GST_IS_PAD (pad), NULL); - realpad = GST_PAD_REALIZE (pad); + /* now we need to deal with the real/ghost stuff */ + GST_PAD_REALIZE_AND_LOCK (pad, realpad, lost_ghostpad); GST_CAT_DEBUG (GST_CAT_CAPS, "get pad caps of %s:%s (%p)", GST_DEBUG_PAD_NAME (realpad), realpad); - if (GST_PAD_IS_DISPATCHING (realpad)) + if (G_UNLIKELY (GST_RPAD_IS_IN_GETCAPS (realpad))) + goto was_dispatching; + + result = gst_real_pad_get_caps_unlocked (realpad); + GST_UNLOCK (realpad); + + return result; + +lost_ghostpad: + { + return NULL; + } +was_dispatching: + { GST_CAT_DEBUG (GST_CAT_CAPS, - "pad %s:%s is already dispatching -- looking for a template", + "pad %s:%s is already dispatching!", GST_DEBUG_PAD_NAME (realpad)); + g_warning ("pad %s:%s recursively called getcaps!", GST_DEBUG_PAD_NAME (realpad)); - - if (GST_RPAD_GETCAPSFUNC (realpad) && !GST_PAD_IS_DISPATCHING (realpad)) { - GstCaps *caps; - - GST_CAT_DEBUG (GST_CAT_CAPS, "dispatching to pad getcaps function"); - - GST_FLAG_SET (realpad, GST_PAD_DISPATCHING); - caps = GST_RPAD_GETCAPSFUNC (realpad) (GST_PAD (realpad)); - GST_FLAG_UNSET (realpad, GST_PAD_DISPATCHING); - - if (caps == NULL) { - g_critical ("pad %s:%s returned NULL caps from getcaps function\n", - GST_DEBUG_PAD_NAME (realpad)); - } else { -#ifndef G_DISABLE_ASSERT - /* check that the returned caps are a real subset of the template caps */ - if (GST_PAD_PAD_TEMPLATE (realpad)) { - const GstCaps *templ_caps = - GST_PAD_TEMPLATE_CAPS (GST_PAD_PAD_TEMPLATE (realpad)); - if (!gst_caps_is_subset (caps, templ_caps)) { - GstCaps *temp; - - GST_CAT_ERROR_OBJECT (GST_CAT_CAPS, pad, - "pad returned caps %" GST_PTR_FORMAT - " which are not a real subset of its template caps %" - GST_PTR_FORMAT, caps, templ_caps); - g_warning - ("pad %s:%s returned caps that are not a real subset of its template caps", - GST_DEBUG_PAD_NAME (realpad)); - temp = gst_caps_intersect (templ_caps, caps); - gst_caps_unref (caps); - caps = temp; - } - } -#endif - return caps; - } + GST_UNLOCK (realpad); + return NULL; } - if (GST_PAD_PAD_TEMPLATE (realpad)) { - GstPadTemplate *templ = GST_PAD_PAD_TEMPLATE (realpad); - const GstCaps *caps; - - caps = GST_PAD_TEMPLATE_CAPS (templ); - GST_CAT_DEBUG (GST_CAT_CAPS, - "using pad template %p with caps %" GST_PTR_FORMAT, templ, caps); - -#if 0 - /* FIXME we should enable something like this someday, but this is - * a bit buggy */ - if (!gst_caps_is_fixed (caps)) { - g_warning - ("pad %s:%s (%p) has no getcaps function and the pad template returns non-fixed caps. Element is probably broken.\n", - GST_DEBUG_PAD_NAME (realpad), realpad); - } -#endif - - return gst_caps_copy (GST_PAD_TEMPLATE_CAPS (templ)); - } - GST_CAT_DEBUG (GST_CAT_CAPS, "pad has no caps"); - -#if 0 - /* FIXME enable */ - g_warning ("pad %s:%s (%p) has no pad template\n", - GST_DEBUG_PAD_NAME (realpad), realpad); -#endif - - return gst_caps_new_any (); } /** @@ -2565,6 +1832,9 @@ gst_pad_peer_get_caps (GstPad * pad) if (G_UNLIKELY (peerpad == NULL)) goto no_peer; + if (G_UNLIKELY (GST_RPAD_IS_IN_GETCAPS (peerpad))) + goto was_dispatching; + gst_object_ref (GST_OBJECT (peerpad)); GST_UNLOCK (realpad); @@ -2583,6 +1853,230 @@ no_peer: GST_UNLOCK (realpad); return gst_caps_new_any (); } +was_dispatching: + { + GST_CAT_DEBUG (GST_CAT_CAPS, + "pad %s:%s is already dispatching!", GST_DEBUG_PAD_NAME (realpad)); + g_warning ("pad %s:%s recursively called getcaps!", + GST_DEBUG_PAD_NAME (realpad)); + GST_UNLOCK (realpad); + return NULL; + } +} + +/** + * gst_pad_fixate_caps: + * @pad: a #GstPad to fixate + * + * Fixate a caps on the given pad. + * + * Returns: a fixated #GstCaps. + */ +GstCaps * +gst_pad_fixate_caps (GstPad * pad, GstCaps * caps) +{ + /* FIXME, implement me, call the fixate function for the pad */ + return caps; +} + +/** + * gst_pad_accept_caps: + * @pad: a #GstPad to check + * + * Check if the given pad accepts the caps. + * + * Returns: TRUE if the pad can accept the caps. + */ +gboolean +gst_pad_accept_caps (GstPad * pad, GstCaps * caps) +{ + GstRealPad *realpad; + gboolean result; + + g_return_val_if_fail (GST_IS_PAD (pad), FALSE); + + /* now we need to deal with the real/ghost stuff */ + GST_PAD_REALIZE_AND_LOCK (pad, realpad, lost_ghostpad); + + GST_CAT_DEBUG (GST_CAT_CAPS, "pad accept caps of %s:%s (%p)", + GST_DEBUG_PAD_NAME (realpad), realpad); + + /* FIXME, call accept function */ + result = FALSE; + GST_UNLOCK (realpad); + + return result; + +lost_ghostpad: + { + return FALSE; + } +} + +/** + * gst_pad_peer_accept_caps: + * @pad: a #GstPad to check + * + * Check if the given pad accepts the caps. + * + * Returns: TRUE if the pad can accept the caps. + */ +gboolean +gst_pad_peer_accept_caps (GstPad * pad, GstCaps * caps) +{ + GstRealPad *realpad, *peerpad; + gboolean result; + + g_return_val_if_fail (GST_IS_PAD (pad), FALSE); + + /* now we need to deal with the real/ghost stuff */ + GST_PAD_REALIZE_AND_LOCK (pad, realpad, lost_ghostpad); + + GST_CAT_DEBUG (GST_CAT_CAPS, "peer accept caps of %s:%s (%p)", + GST_DEBUG_PAD_NAME (realpad), realpad); + + peerpad = GST_RPAD_PEER (realpad); + if (G_UNLIKELY (peerpad == NULL)) + goto no_peer; + + result = gst_pad_accept_caps (GST_PAD_CAST (peerpad), caps); + GST_UNLOCK (realpad); + + return result; + +lost_ghostpad: + { + return FALSE; + } +no_peer: + { + GST_UNLOCK (realpad); + return TRUE; + } +} + +/** + * gst_pad_set_caps: + * @pad: a #GstPad to set the capabilities of. + * @caps: a #GstCaps to set. + * + * Sets the capabilities of this pad. The caps must be fixed. Any previous + * caps on the pad will be unreffed. This function refs the caps so you should + * unref if as soon as you don't need it anymore. + * It is possible to set NULL caps, which will make the pad unnegotiated + * again. + * + * Returns: TRUE if the caps could be set. FALSE if the caps were not fixed + * or bad parameters were provided to this function. + * + * MT safe. + */ +gboolean +gst_pad_set_caps (GstPad * pad, GstCaps * caps) +{ + GstPadSetCapsFunction setcaps; + + g_return_val_if_fail (GST_IS_REAL_PAD (pad), FALSE); + + GST_LOCK (pad); + setcaps = GST_RPAD_SETCAPSFUNC (pad); + + /* call setcaps function to configure the pad */ + if (setcaps != NULL) { + if (!GST_RPAD_IS_IN_SETCAPS (pad)) { + GST_FLAG_SET (pad, GST_PAD_IN_SETCAPS); + GST_UNLOCK (pad); + if (!setcaps (pad, caps)) + goto could_not_set; + GST_LOCK (pad); + } else { + GST_CAT_DEBUG (GST_CAT_CAPS, "pad %s:%s was dispatching", + GST_DEBUG_PAD_NAME (pad)); + } + } + + if (GST_PAD_CAPS (pad)) + gst_caps_unref (GST_PAD_CAPS (pad)); + + if (caps) + caps = gst_caps_ref (caps); + + GST_PAD_CAPS (pad) = caps; + GST_CAT_DEBUG (GST_CAT_CAPS, "%s:%s caps %" GST_PTR_FORMAT, + GST_DEBUG_PAD_NAME (pad), caps); + GST_UNLOCK (pad); + + g_object_notify (G_OBJECT (pad), "caps"); + + return TRUE; + +could_not_set: + { + GST_LOCK (pad); + GST_FLAG_UNSET (pad, GST_PAD_IN_SETCAPS); + GST_UNLOCK (pad); + GST_CAT_DEBUG (GST_CAT_CAPS, "caps %" GST_PTR_FORMAT " could not be set", + caps); + return FALSE; + } +} + +static gboolean +gst_pad_configure_sink (GstPad * pad, GstCaps * caps) +{ + GstPadAcceptCapsFunction acceptcaps; + GstPadSetCapsFunction setcaps; + + acceptcaps = GST_RPAD_ACCEPTCAPSFUNC (pad); + setcaps = GST_RPAD_SETCAPSFUNC (pad); + + /* See if pad accepts the caps, by calling acceptcaps, only + * needed if no setcaps function */ + if (setcaps == NULL && acceptcaps != NULL) { + if (!acceptcaps (pad, caps)) + goto not_accepted; + } + /* set caps on pad if call succeeds */ + gst_pad_set_caps (pad, caps); + /* no need to unref the caps here, set_caps takes a ref and + * our ref goes away when we leave this function. */ + + return TRUE; + +not_accepted: + { + GST_CAT_DEBUG (GST_CAT_CAPS, "caps %" GST_PTR_FORMAT " not accepted", caps); + return FALSE; + } +} + +static gboolean +gst_pad_configure_src (GstPad * pad, GstCaps * caps) +{ + GstPadAcceptCapsFunction acceptcaps; + GstPadSetCapsFunction setcaps; + + acceptcaps = GST_RPAD_ACCEPTCAPSFUNC (pad); + setcaps = GST_RPAD_SETCAPSFUNC (pad); + + /* See if pad accepts the caps, by calling acceptcaps, only + * needed if no setcaps function */ + if (setcaps == NULL && acceptcaps != NULL) { + if (!acceptcaps (pad, caps)) + goto not_accepted; + } + /* set caps on pad if call succeeds */ + gst_pad_set_caps (pad, caps); + /* no need to unref the caps here, set_caps takes a ref and + * our ref goes away when we leave this function. */ + + return TRUE; + +not_accepted: + { + GST_CAT_DEBUG (GST_CAT_CAPS, "caps %" GST_PTR_FORMAT " not accepted", caps); + return FALSE; + } } /** @@ -2748,36 +2242,112 @@ no_peer: } /** - * gst_pad_caps_change_notify: - * @pad: a #GstPad + * gst_pad_get_negotiated_caps: + * @pad: a #GstPad. * - * Called to indicate that the return value of @pad's getcaps function may have - * changed, and that a renegotiation is suggested. + * Gets the capabilities of the media type that currently flows through @pad + * and its peer. + * + * This function can be used on both src and sinkpads. Note that srcpads are + * always negotiated before sinkpads so it is possible that the negotiated caps + * on the srcpad do not match the negotiated caps of the peer. + * + * Returns: the negotiated #GstCaps of the pad link. Free the caps when + * you no longer need it. This function returns NULL when the @pad has no + * peer or is not negotiated yet. + * + * MT safe. */ -void -gst_pad_caps_change_notify (GstPad * pad) +GstCaps * +gst_pad_get_negotiated_caps (GstPad * pad) { + GstCaps *caps; + GstRealPad *realpad, *peer; + + g_return_val_if_fail (GST_IS_PAD (pad), NULL); + + GST_PAD_REALIZE_AND_LOCK (pad, realpad, lost_ghostpad); + + if (G_UNLIKELY ((peer = GST_RPAD_PEER (realpad)) == NULL)) + goto no_peer; + + GST_CAT_DEBUG (GST_CAT_PROPERTIES, "%s:%s: getting negotiated caps", + GST_DEBUG_PAD_NAME (realpad)); + + caps = GST_RPAD_CAPS (realpad); + if (caps) + gst_caps_ref (caps); + GST_UNLOCK (pad); + + GST_CAT_DEBUG (GST_CAT_CAPS, "negotiated caps %" GST_PTR_FORMAT, caps); + + return caps; + +lost_ghostpad: + { + GST_UNLOCK (pad); + return NULL; + } +no_peer: + { + GST_CAT_DEBUG (GST_CAT_PROPERTIES, "%s:%s: no peer", + GST_DEBUG_PAD_NAME (realpad)); + GST_UNLOCK (realpad); + + return NULL; + } } /** - * gst_pad_recover_caps_error: - * @pad: a #GstPad that had a failed capsnego - * @allowed: possible caps for the link + * gst_pad_get_filter_caps: + * @pad: a real #GstPad. * - * Attempt to recover from a failed caps negotiation. This function - * is typically called by a plugin that exhausted its list of caps - * and wants the application to resolve the issue. The application - * should connect to the pad's caps_nego_failed signal and should - * resolve the issue by connecting another element for example. + * Gets the capabilities of filter that currently configured on @pad + * and its peer. * - * Returns: TRUE when the issue was resolved, dumps detailed information - * on the console and returns FALSE otherwise. + * Returns: the filter #GstCaps of the pad link. Free the caps when + * you no longer need it. This function returns NULL when the @pad has no + * peer or there is no filter configured. + * + * MT safe. */ -gboolean -gst_pad_recover_caps_error (GstPad * pad, const GstCaps * allowed) +GstCaps * +gst_pad_get_filter_caps (GstPad * pad) { - /* FIXME */ - return FALSE; + GstCaps *caps; + GstRealPad *realpad, *peer; + + g_return_val_if_fail (GST_IS_PAD (pad), NULL); + + GST_PAD_REALIZE_AND_LOCK (pad, realpad, lost_ghostpad); + + if (G_UNLIKELY ((peer = GST_RPAD_PEER (realpad)) == NULL)) + goto no_peer; + + GST_CAT_DEBUG (GST_CAT_PROPERTIES, "%s:%s: getting filter caps", + GST_DEBUG_PAD_NAME (realpad)); + + if ((caps = GST_RPAD_APPFILTER (realpad)) != NULL) + gst_caps_ref (caps); + GST_UNLOCK (pad); + + GST_CAT_DEBUG (GST_CAT_CAPS, "filter caps %" GST_PTR_FORMAT, caps); + + return caps; + +lost_ghostpad: + { + GST_UNLOCK (pad); + return NULL; + } +no_peer: + { + GST_CAT_DEBUG (GST_CAT_PROPERTIES, "%s:%s: no peer", + GST_DEBUG_PAD_NAME (realpad)); + GST_UNLOCK (realpad); + + return NULL; + } } /** @@ -2785,37 +2355,90 @@ gst_pad_recover_caps_error (GstPad * pad, const GstCaps * allowed) * @pad: a source #GstPad * @offset: the offset of the new buffer in the stream * @size: the size of the new buffer + * @caps: the caps of the new buffer * * Allocates a new, empty buffer optimized to push to pad @pad. This - * function only works if @pad is a source pad. + * function only works if @pad is a source pad and a GST_REAL_PAD and + * has a peer. + * You need to check the caps of the buffer after performing this + * function and renegotiate to the format if needed. * - * Returns: a new, empty #GstBuffer, or NULL if there is an error + * Returns: a new, empty #GstBuffer, or NULL if wrong parameters + * were provided or the peer pad is not able to provide a buffer + * that can be handled by the caller. + * + * MT safe. */ GstBuffer * -gst_pad_alloc_buffer (GstPad * pad, guint64 offset, gint size) +gst_pad_alloc_buffer (GstPad * pad, guint64 offset, gint size, GstCaps * caps) { GstRealPad *peer; + GstBuffer *result = NULL; + GstPadBufferAllocFunction bufferallocfunc; + gboolean caps_changed; - g_return_val_if_fail (GST_IS_PAD (pad), NULL); + g_return_val_if_fail (GST_IS_REAL_PAD (pad), NULL); g_return_val_if_fail (GST_PAD_IS_SRC (pad), NULL); - peer = GST_RPAD_PEER (pad); + GST_LOCK (pad); + if (G_UNLIKELY ((peer = GST_RPAD_PEER (pad)) == NULL)) + goto no_peer; - if (peer && peer->bufferallocfunc) { - GstBuffer *ret; - - GST_CAT_DEBUG (GST_CAT_BUFFER, "(%s:%s): getting buffer", - GST_DEBUG_PAD_NAME (pad)); - GST_CAT_DEBUG (GST_CAT_PADS, - "calling bufferallocfunc &%s (@%p) of peer pad %s:%s", - GST_DEBUG_FUNCPTR_NAME (peer->bufferallocfunc), - &peer->bufferallocfunc, GST_DEBUG_PAD_NAME (((GstPad *) peer))); - - ret = (peer->bufferallocfunc) (GST_PAD (peer), offset, size); - if (ret) - return ret; + if (G_LIKELY ((bufferallocfunc = peer->bufferallocfunc) == NULL)) { + GST_UNLOCK (pad); + goto fallback; + } + + gst_object_ref (GST_OBJECT_CAST (peer)); + GST_UNLOCK (pad); + + GST_CAT_DEBUG (GST_CAT_PADS, + "calling bufferallocfunc &%s (@%p) of peer pad %s:%s", + GST_DEBUG_FUNCPTR_NAME (bufferallocfunc), + &bufferallocfunc, GST_DEBUG_PAD_NAME (peer)); + + result = bufferallocfunc (GST_PAD_CAST (peer), offset, size, caps); + + gst_object_unref (GST_OBJECT_CAST (peer)); + + if (G_UNLIKELY (result == NULL)) { + goto fallback; + } + + /* FIXME, move capnego this into a base class? */ + caps = GST_BUFFER_CAPS (result); + caps_changed = caps && caps != GST_RPAD_CAPS (pad); + /* we got a new datatype on the pad, see if it can handle it */ + if (G_UNLIKELY (caps_changed)) { + if (G_UNLIKELY (!gst_pad_configure_src (GST_PAD_CAST (pad), caps))) + goto not_negotiated; + } + + return result; + +no_peer: + { + /* pad has no peer */ + GST_CAT_DEBUG (GST_CAT_PADS, + "%s:%s called bufferallocfunc but had no peer, returning NULL", + GST_DEBUG_PAD_NAME (pad)); + GST_UNLOCK (pad); + return NULL; + } + /* fallback case, allocate a buffer of our own, add pad caps. */ +fallback: + { + result = gst_buffer_new_and_alloc (size); + gst_buffer_set_caps (result, caps); + + return result; + } +not_negotiated: + { + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, + "alloc function retured unacceptable buffer"); + return NULL; } - return gst_buffer_new_and_alloc (size); } static void @@ -2866,6 +2489,10 @@ gst_real_pad_dispose (GObject * object) g_assert (rpad->ghostpads == NULL); } + /* clear the caps */ + gst_caps_replace (&GST_RPAD_CAPS (pad), NULL); + gst_caps_replace (&GST_RPAD_APPFILTER (pad), NULL); + if (GST_IS_ELEMENT (GST_OBJECT_PARENT (pad))) { GST_CAT_DEBUG (GST_CAT_REFCOUNTING, "removing pad from element '%s'", GST_OBJECT_NAME (GST_OBJECT (GST_ELEMENT (GST_OBJECT_PARENT (pad))))); @@ -2873,21 +2500,30 @@ gst_real_pad_dispose (GObject * object) gst_element_remove_pad (GST_ELEMENT (GST_OBJECT_PARENT (pad)), pad); } - if (GST_RPAD_EXPLICIT_CAPS (pad)) { - GST_ERROR_OBJECT (pad, "still explicit caps %" GST_PTR_FORMAT " set", - GST_RPAD_EXPLICIT_CAPS (pad)); - g_warning ("pad %p has still explicit caps set", pad); - gst_caps_replace (&GST_RPAD_EXPLICIT_CAPS (pad), NULL); - } G_OBJECT_CLASS (real_pad_parent_class)->dispose (object); } static void gst_real_pad_finalize (GObject * object) { - //GstRealPad *rpad; + GstRealPad *rpad; - //rpad = GST_REAL_PAD (object); + rpad = GST_REAL_PAD (object); + + if (rpad->stream_rec_lock) { + g_static_rec_mutex_free (rpad->stream_rec_lock); + rpad->stream_rec_lock = NULL; + } + if (rpad->preroll_lock) { + g_mutex_free (rpad->preroll_lock); + g_cond_free (rpad->preroll_cond); + rpad->preroll_lock = NULL; + rpad->preroll_cond = NULL; + } + if (rpad->block_cond) { + g_cond_free (rpad->block_cond); + rpad->block_cond = NULL; + } G_OBJECT_CLASS (real_pad_parent_class)->finalize (object); } @@ -3027,404 +2663,228 @@ gst_ghost_pad_save_thyself (GstPad * pad, xmlNodePtr parent) } #endif /* GST_DISABLE_LOADSAVE */ -static GstData * -_invent_event (GstPad * pad, GstBuffer * buffer) +/* + * should be called with pad lock held + * + * MT safe. + */ +static void +handle_pad_block (GstRealPad * pad) { - GstEvent *event; - GstEventType event_type; - guint64 offset; + GstPadBlockCallback callback; + gpointer user_data; - if (GST_BUFFER_OFFSET_IS_VALID (buffer)) - event_type = GST_FORMAT_DEFAULT; - else - event_type = GST_FORMAT_UNDEFINED; + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, + "signal block taken on pad %s:%s", GST_DEBUG_PAD_NAME (pad)); - offset = GST_BUFFER_OFFSET (buffer); + /* need to grab extra ref for the callbacks */ + gst_object_ref (GST_OBJECT (pad)); - if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) { - GstClockTime timestamp = GST_BUFFER_TIMESTAMP (buffer); - - event = gst_event_new_discontinuous (TRUE, - GST_FORMAT_TIME, timestamp, event_type, offset, GST_FORMAT_UNDEFINED); - GST_CAT_WARNING (GST_CAT_SCHEDULING, - "needed to invent a DISCONT %p (time %" G_GUINT64_FORMAT - ") for %s:%s => %s:%s", event, timestamp, - GST_DEBUG_PAD_NAME (GST_PAD_PEER (pad)), GST_DEBUG_PAD_NAME (pad)); + callback = pad->block_callback; + if (callback) { + user_data = pad->block_data; + GST_UNLOCK (pad); + callback (GST_PAD_CAST (pad), TRUE, user_data); + GST_LOCK (pad); } else { - event = gst_event_new_discontinuous (TRUE, - event_type, offset, GST_FORMAT_UNDEFINED); - GST_CAT_WARNING (GST_CAT_SCHEDULING, - "needed to invent a DISCONT %p (no time) for %s:%s => %s:%s", event, - GST_DEBUG_PAD_NAME (GST_PAD_PEER (pad)), GST_DEBUG_PAD_NAME (pad)); + GST_PAD_BLOCK_SIGNAL (pad); } - return GST_DATA (event); + while (GST_RPAD_IS_BLOCKED (pad)) + GST_PAD_BLOCK_WAIT (pad); + + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "got unblocked"); + + callback = pad->block_callback; + if (callback) { + user_data = pad->block_data; + GST_UNLOCK (pad); + callback (GST_PAD_CAST (pad), FALSE, user_data); + GST_LOCK (pad); + } else { + GST_PAD_BLOCK_SIGNAL (pad); + } + + gst_object_unref (GST_OBJECT (pad)); } /** * gst_pad_push: * @pad: a source #GstPad. - * @data: the #GstData to push. + * @buffer: the #GstBuffer to push. * - * Pushes a buffer or an event to the peer of @pad. @pad must be linked. May - * only be called by @pad's parent. + * Pushes a buffer to the peer of @pad. @pad must be linked. + * + * Returns: a #GstFlowReturn from the peer pad. + * + * MT safe. */ -void -gst_pad_push (GstPad * pad, GstData * data) +GstFlowReturn +gst_pad_push (GstPad * pad, GstBuffer * buffer) { GstRealPad *peer; + GstFlowReturn ret; + GstPadChainFunction chainfunc; + GstCaps *caps; + gboolean caps_changed; - g_return_if_fail (GST_IS_PAD (pad)); - g_return_if_fail (GST_PAD_DIRECTION (pad) == GST_PAD_SRC); - g_return_if_fail (!GST_FLAG_IS_SET (GST_PAD_REALIZE (pad), - GST_RPAD_IN_GETFUNC)); - g_return_if_fail (data != NULL); + g_return_val_if_fail (GST_IS_REAL_PAD (pad), GST_FLOW_ERROR); + g_return_val_if_fail (GST_RPAD_DIRECTION (pad) == GST_PAD_SRC, + GST_FLOW_ERROR); + g_return_val_if_fail (buffer != NULL, GST_FLOW_ERROR); + g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR); - DEBUG_DATA (pad, data, "gst_pad_push"); - if (!gst_probe_dispatcher_dispatch (&(GST_REAL_PAD (pad)->probedisp), &data)) { + GST_LOCK (pad); + while (G_UNLIKELY (GST_RPAD_IS_BLOCKED (pad))) + handle_pad_block (GST_REAL_PAD_CAST (pad)); + + if (G_UNLIKELY ((peer = GST_RPAD_PEER (pad)) == NULL)) + goto not_linked; + + if (G_UNLIKELY (!GST_RPAD_IS_ACTIVE (peer))) + goto not_active; + + if (G_UNLIKELY (GST_RPAD_IS_FLUSHING (peer))) + goto flushing; + + gst_object_ref (GST_OBJECT_CAST (peer)); + GST_UNLOCK (pad); + + /* FIXME, move capnego this into a base class? */ + caps = GST_BUFFER_CAPS (buffer); + caps_changed = caps && caps != GST_RPAD_CAPS (peer); + /* we got a new datatype on the peer pad, see if it can handle it */ + if (G_UNLIKELY (caps_changed)) { + if (G_UNLIKELY (!gst_pad_configure_sink (GST_PAD_CAST (peer), caps))) + goto not_negotiated; + } + + /* NOTE: we read the peer chainfunc unlocked. + * we cannot hold the lock for the peer so we might send + * the data to the wrong function. This is not really a + * problem since functions are assigned at creation time + * and don't change that often... */ + if (G_UNLIKELY ((chainfunc = peer->chainfunc) == NULL)) + goto no_function; + + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, + "calling chainfunction &%s of peer pad %s:%s", + GST_DEBUG_FUNCPTR_NAME (chainfunc), GST_DEBUG_PAD_NAME (peer)); + + ret = chainfunc (GST_PAD_CAST (peer), buffer); + + gst_object_unref (GST_OBJECT_CAST (peer)); + + return ret; + + /* ERROR recovery here */ +not_linked: + { GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, - "not pushing data %p, blocked by probe", data); - gst_data_unref (data); - return; + "pushing, but it was not linked"); + GST_UNLOCK (pad); + return GST_FLOW_NOT_CONNECTED; } - - if (!GST_PAD_IS_LINKED (pad)) { +not_active: + { GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, - "not pushing data %p as pad is unconnected", data); - gst_data_unref (data); - return; + "pushing, but it was inactive"); + GST_UNLOCK (pad); + return GST_FLOW_WRONG_STATE; } - - if (GST_IS_BUFFER (data) && !gst_pad_is_negotiated (pad)) { - g_warning ("pushing data on non-negotiated pad %s:%s, not allowed.", - GST_DEBUG_PAD_NAME (pad)); - gst_data_unref (data); - return; +flushing: + { + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, + "pushing, but pad was flushing"); + GST_UNLOCK (pad); + return GST_FLOW_UNEXPECTED; } - - GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "pushing"); - peer = GST_RPAD_PEER (pad); - - if (!peer) { - g_warning ("push on pad %s:%s but it is unlinked", - GST_DEBUG_PAD_NAME (pad)); - } else { - if (!GST_IS_EVENT (data) && !GST_PAD_IS_ACTIVE (peer)) { - g_warning ("push on peer of pad %s:%s but peer is not active", - GST_DEBUG_PAD_NAME (pad)); - return; - } - - if (peer->chainhandler) { - if (data) { - if (!gst_probe_dispatcher_dispatch (&peer->probedisp, &data)) { - GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, - "not pushing data %p, blocked by probe", data); - gst_data_unref (data); - return; - } - - GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, - "calling chainhandler &%s of peer pad %s:%s", - GST_DEBUG_FUNCPTR_NAME (peer->chainhandler), - GST_DEBUG_PAD_NAME (GST_PAD (peer))); - (peer->chainhandler) (GST_PAD (peer), data); - return; - } else { - g_warning ("trying to push a NULL buffer on pad %s:%s", - GST_DEBUG_PAD_NAME (peer)); - return; - } - } else { - g_warning ("internal error: push on pad %s:%s but it has no chainhandler", - GST_DEBUG_PAD_NAME (peer)); - } +not_negotiated: + { + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, + "pushing buffer but peer did not accept"); + return GST_FLOW_NOT_NEGOTIATED; } - /* clean up the mess here */ - if (data != NULL) - gst_data_unref (data); -} - -/** - * gst_pad_pull: - * @pad: a sink #GstPad. - * - * Pulls an event or a buffer from the peer pad. May only be called by @pad's - * parent. - * - * Returns: a new #GstData from the peer pad. - */ -GstData * -gst_pad_pull (GstPad * pad) -{ - GstRealPad *peer; - GstData *data; - - g_return_val_if_fail (GST_PAD_DIRECTION (pad) == GST_PAD_SINK, - GST_DATA (gst_event_new (GST_EVENT_INTERRUPT))); - g_return_val_if_fail (!GST_FLAG_IS_SET (GST_PAD_REALIZE (pad), - GST_RPAD_IN_CHAINFUNC), - GST_DATA (gst_event_new (GST_EVENT_INTERRUPT))); - - peer = GST_RPAD_PEER (pad); - - if (!peer) { +no_function: + { + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, + "pushing, but not chainhandler"); GST_ELEMENT_ERROR (GST_PAD_PARENT (pad), CORE, PAD, (NULL), - ("pull on pad %s:%s but it was unlinked", GST_DEBUG_PAD_NAME (pad))); - } else { - restart: - if (peer->gethandler) { - GstPadLink *link = GST_RPAD_LINK (pad); - - GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, - "calling gethandler %s of peer pad %s:%s", - GST_DEBUG_FUNCPTR_NAME (peer->gethandler), GST_DEBUG_PAD_NAME (peer)); - - if (link->temp_store) { - g_assert (link->engaged); - GST_DEBUG ("moving temp_store %p to data", link->temp_store); - data = link->temp_store; - link->temp_store = NULL; - } else { - data = (peer->gethandler) (GST_PAD (peer)); - /* refetch - we might have been relinked */ - link = GST_RPAD_LINK (pad); - peer = GST_RPAD_PEER (pad); - } - - if (data) { - if (!link->engaged) { - g_assert (link->temp_store == NULL); - if (GST_IS_BUFFER (data)) { - GST_DEBUG ("moving data buffer %p back to temp_store", data); - link->temp_store = data; - link->engaged = TRUE; - data = _invent_event (pad, GST_BUFFER (data)); - } else if (GST_IS_EVENT (data) && - GST_EVENT_TYPE (data) == GST_EVENT_DISCONTINUOUS && - GST_EVENT_DISCONT_NEW_MEDIA (data)) { - link->engaged = TRUE; - GST_CAT_LOG (GST_CAT_SCHEDULING, - "link engaged by discont event %p for pad %s:%s", data, - GST_DEBUG_PAD_NAME (pad)); - } - } - GST_DEBUG ("calling gst_probe_dispatcher_dispatch on data %p", data); - if (!gst_probe_dispatcher_dispatch (&peer->probedisp, &data)) { - GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, - "not returning pulled data %p, blocked by probe", data); - gst_data_unref (data); - goto restart; - } - DEBUG_DATA (pad, data, "gst_pad_pull returned"); - return data; - } - - /* no null buffers allowed */ - GST_ELEMENT_ERROR (GST_PAD_PARENT (pad), CORE, PAD, (NULL), - ("NULL buffer during pull on %s:%s", GST_DEBUG_PAD_NAME (pad))); - } else { - GST_ELEMENT_ERROR (GST_PAD_PARENT (pad), CORE, PAD, (NULL), - ("pull on pad %s:%s but the peer pad %s:%s has no gethandler", - GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (peer))); - } - } - data = GST_DATA (gst_event_new (GST_EVENT_INTERRUPT)); - DEBUG_DATA (pad, data, "gst_pad_pull returned created"); - return data; -} - -GstData * -gst_pad_collect_array (GstScheduler * scheduler, GstPad ** selected, - GstPad ** padlist) -{ - GstSchedulerClass *klass = GST_SCHEDULER_GET_CLASS (scheduler); - - if (!GST_FLAG_IS_SET (scheduler, GST_SCHEDULER_FLAG_NEW_API) || - !klass->pad_select) { - /* better randomness? */ - if (selected) - *selected = padlist[0]; - return gst_pad_pull (padlist[0]); - } else { - GstPad *select; - - return klass->pad_select (scheduler, selected ? selected : &select, - padlist); + ("push on pad %s:%s but the peer pad %s:%s has no chainfunction", + GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (peer))); + gst_object_unref (GST_OBJECT (peer)); + return GST_FLOW_ERROR; } } /** - * gst_pad_collectv: - * @selected: set to the pad the buffer comes from if not NULL - * @padlist: a #GList of sink pads. + * gst_pad_pull_range: + * @pad: a sink #GstPad. + * @buffer: a pointer to hold the #GstBuffer. + * @offset: The start offset of the buffer + * @length: The length of the buffer * - * Waits for a buffer on any of the list of pads. Each #GstPad in @padlist must - * belong to the same element and be owned by the caller. + * Pulls a buffer from the peer pad. @pad must be linked. * - * Returns: the #GstData that was available + * Returns: a #GstFlowReturn from the peer pad. + * + * MT safe. */ -GstData * -gst_pad_collectv (GstPad ** selected, const GList * padlist) +GstFlowReturn +gst_pad_pull_range (GstPad * pad, guint64 offset, guint size, + GstBuffer ** buffer) { - /* need to use alloca here because we must not leak data */ - GstPad **pads; - GstPad *test; - GstElement *element = NULL; - int i = 0; + GstRealPad *peer; + GstFlowReturn ret; + GstPadGetRangeFunction getrangefunc; - g_return_val_if_fail (padlist != NULL, NULL); - pads = g_alloca (sizeof (gpointer) * (g_list_length ((GList *) padlist) + 1)); - for (; padlist; padlist = g_list_next (padlist)) { - test = GST_PAD (padlist->data); - g_return_val_if_fail (GST_IS_PAD (test), NULL); - g_return_val_if_fail (GST_PAD_IS_SINK (test), NULL); - if (element) { - g_return_val_if_fail (element == gst_pad_get_parent (test), NULL); - } else { - element = gst_pad_get_parent (test); - } - pads[i++] = test; + g_return_val_if_fail (GST_IS_REAL_PAD (pad), GST_FLOW_ERROR); + g_return_val_if_fail (GST_RPAD_DIRECTION (pad) == GST_PAD_SINK, + GST_FLOW_ERROR); + g_return_val_if_fail (buffer != NULL, GST_FLOW_ERROR); + + GST_LOCK (pad); + + while (G_UNLIKELY (GST_RPAD_IS_BLOCKED (pad))) + handle_pad_block (GST_REAL_PAD_CAST (pad)); + + if (G_UNLIKELY ((peer = GST_RPAD_PEER (pad)) == NULL)) + goto not_connected; + + gst_object_ref (GST_OBJECT_CAST (peer)); + GST_UNLOCK (pad); + + /* see note in above function */ + if (G_UNLIKELY ((getrangefunc = peer->getrangefunc) == NULL)) + goto no_function; + + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, + "calling getrangefunc %s of peer pad %s:%s", + GST_DEBUG_FUNCPTR_NAME (getrangefunc), GST_DEBUG_PAD_NAME (peer)); + + ret = getrangefunc (GST_PAD_CAST (peer), offset, size, buffer); + + gst_object_unref (GST_OBJECT_CAST (peer)); + + return ret; + + /* ERROR recovery here */ +not_connected: + { + GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, + "pulling range, but it was not linked"); + GST_UNLOCK (pad); + return GST_FLOW_NOT_CONNECTED; } - pads[i] = NULL; - - return gst_pad_collect_array (GST_ELEMENT_SCHEDULER (element), selected, - pads); -} - -/** - * gst_pad_collect: - * @selected: set to the pad the buffer comes from if not NULL - * @pad: first pad - * @...: more sink pads. - * - * Waits for a buffer on the given set of pads. - * - * Returns: the #GstData that was available. - */ -GstData * -gst_pad_collect (GstPad ** selected, GstPad * pad, ...) -{ - GstData *result; - va_list var_args; - - g_return_val_if_fail (GST_IS_PAD (pad), NULL); - - va_start (var_args, pad); - - result = gst_pad_collect_valist (selected, pad, var_args); - - va_end (var_args); - - return result; -} - -/** - * gst_pad_collect_valist: - * @selected: set to the pad the buffer comes from if not NULL - * @pad: first pad - * @...: more sink pads. - * - * Waits for a buffer on the given set of pads. - * - * Returns: the #GstData that was available. - */ -GstData * -gst_pad_collect_valist (GstPad ** selected, GstPad * pad, va_list var_args) -{ - GstPad **padlist; - GstElement *element = NULL; - gint i = 0, maxlength; - - g_return_val_if_fail (GST_IS_PAD (pad), NULL); - - element = gst_pad_get_parent (pad); - maxlength = element->numsinkpads; - /* can we make this list a bit smaller than this upper limit? */ - padlist = g_alloca (sizeof (gpointer) * (maxlength + 1)); - while (pad) { - g_return_val_if_fail (i < maxlength, NULL); - g_return_val_if_fail (element == gst_pad_get_parent (pad), NULL); - padlist[i++] = pad; - pad = va_arg (var_args, GstPad *); +no_function: + { + GST_ELEMENT_ERROR (GST_PAD_PARENT (pad), CORE, PAD, (NULL), + ("pullrange on pad %s:%s but the peer pad %s:%s has no getrangefunction", + GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (peer))); + gst_object_unref (GST_OBJECT (peer)); + return GST_FLOW_ERROR; } - padlist[i] = NULL; - return gst_pad_collect_array (GST_ELEMENT_SCHEDULER (element), selected, - padlist); -} - -/** - * gst_pad_selectv: - * @padlist: a #GList of sink pads. - * - * Waits for a buffer on any of the list of pads. Each #GstPad in @padlist must - * be owned by the calling code. - * - * Returns: the #GstPad that has a buffer available. - * Use #gst_pad_pull() to get the buffer. - */ -GstPad * -gst_pad_selectv (GList * padlist) -{ - return NULL; -} - -/** - * gst_pad_select_valist: - * @pad: a first #GstPad to perform the select on. - * @varargs: A va_list of more pads to select on. - * - * Waits for a buffer on the given set of pads. - * - * Returns: the #GstPad that has a buffer available. - * Use #gst_pad_pull() to get the buffer. - */ -GstPad * -gst_pad_select_valist (GstPad * pad, va_list var_args) -{ - GstPad *result; - GList *padlist = NULL; - - if (pad == NULL) - return NULL; - - while (pad) { - padlist = g_list_prepend (padlist, pad); - pad = va_arg (var_args, GstPad *); - } - result = gst_pad_selectv (padlist); - g_list_free (padlist); - - return result; -} - -/** - * gst_pad_select: - * @pad: a first sink #GstPad to perform the select on. - * @...: A NULL-terminated list of more pads to select on. - * - * Waits for a buffer on the given set of pads. - * - * Returns: the #GstPad that has a buffer available. - * Use #gst_pad_pull() to get the buffer. - */ -GstPad * -gst_pad_select (GstPad * pad, ...) -{ - GstPad *result; - va_list var_args; - - if (pad == NULL) - return NULL; - - va_start (var_args, pad); - - result = gst_pad_select_valist (pad, var_args); - - va_end (var_args); - - return result; } /************************************************************************ @@ -3862,14 +3322,16 @@ gst_pad_get_internal_links (GstPad * pad) static gboolean -gst_pad_event_default_dispatch (GstPad * pad, GstElement * element, - GstEvent * event) +gst_pad_event_default_dispatch (GstPad * pad, GstEvent * event) { GList *orig, *pads; + gboolean result; GST_INFO_OBJECT (pad, "Sending event %p to all internally linked pads", event); + result = (GST_PAD_DIRECTION (pad) == GST_PAD_SINK); + orig = pads = gst_pad_get_internal_links (pad); while (pads) { @@ -3885,24 +3347,25 @@ gst_pad_event_default_dispatch (GstPad * pad, GstElement * element, GST_LOG_OBJECT (pad, "Reffing and sending event %p to %s:%s", event, GST_DEBUG_PAD_NAME (eventpad)); gst_event_ref (event); - gst_pad_push (eventpad, GST_DATA (event)); + gst_pad_push_event (eventpad, event); } else { - GstPad *peerpad = GST_PAD (GST_RPAD_PEER (eventpad)); - /* we only send the event on one pad, multi-sinkpad elements * should implement a handler */ - g_list_free (orig); GST_LOG_OBJECT (pad, "sending event %p to one sink pad %s:%s", event, GST_DEBUG_PAD_NAME (eventpad)); - return gst_pad_send_event (peerpad, event); + result = gst_pad_push_event (eventpad, event); + goto done; } } } /* we handled the incoming event so we unref once */ GST_LOG_OBJECT (pad, "handled event %p, unreffing", event); gst_event_unref (event); + +done: g_list_free (orig); - return (GST_PAD_DIRECTION (pad) == GST_PAD_SINK); + + return result; } /** @@ -3921,47 +3384,23 @@ gst_pad_event_default_dispatch (GstPad * pad, GstElement * element, gboolean gst_pad_event_default (GstPad * pad, GstEvent * event) { - GstElement *element; - g_return_val_if_fail (GST_IS_PAD (pad), FALSE); g_return_val_if_fail (event != NULL, FALSE); - element = GST_PAD_PARENT (pad); - switch (GST_EVENT_TYPE (event)) { case GST_EVENT_EOS: - gst_pad_event_default_dispatch (pad, element, event); - gst_element_set_eos (element); - break; - case GST_EVENT_DISCONTINUOUS: { - guint64 time; + GstRealPad *rpad = GST_PAD_REALIZE (pad); - if (gst_element_requires_clock (element) && element->clock) { - if (gst_event_discont_get_value (event, GST_FORMAT_TIME, &time)) { - gst_element_set_time (element, time); - } else { - GstFormat format = GST_FORMAT_TIME; - guint i; - - for (i = 0; i < event->event_data.discont.noffsets; i++) { - if (gst_pad_convert (pad, - event->event_data.discont.offsets[i].format, - event->event_data.discont.offsets[i].value, &format, - &time)) { - gst_element_set_time (element, time); - } else if (i == event->event_data.discont.noffsets) { - g_warning - ("can't adjust clock to new time when time not provided"); - } - } - } + if (GST_RPAD_TASK (rpad)) { + GST_DEBUG_OBJECT (rpad, "pausing task because of eos"); + gst_task_pause (GST_RPAD_TASK (rpad)); } } + return gst_pad_event_default_dispatch (pad, event); default: - return gst_pad_event_default_dispatch (pad, element, event); + return gst_pad_event_default_dispatch (pad, event); } - return TRUE; } /** @@ -4006,44 +3445,106 @@ gst_pad_dispatcher (GstPad * pad, GstPadDispatcherFunction dispatch, return res; } +/** + * gst_pad_push_event: + * @pad: a #GstPad to push the event to. + * @event: the #GstEvent to send to the pad. + * + * Sends the event to the peer of the given pad. + * + * Returns: TRUE if the event was handled. + * + * MT safe. + */ +gboolean +gst_pad_push_event (GstPad * pad, GstEvent * event) +{ + GstRealPad *peerpad; + gboolean result; + + g_return_val_if_fail (GST_IS_REAL_PAD (pad), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + GST_LOCK (pad); + peerpad = GST_RPAD_PEER (pad); + if (peerpad == NULL) + goto not_linked; + + gst_object_ref (GST_OBJECT_CAST (peerpad)); + GST_UNLOCK (pad); + + result = gst_pad_send_event (GST_PAD_CAST (peerpad), event); + + gst_object_unref (GST_OBJECT_CAST (peerpad)); + + return result; + + /* ERROR handling */ +not_linked: + { + GST_UNLOCK (pad); + return FALSE; + } +} + /** * gst_pad_send_event: * @pad: a #GstPad to send the event to. * @event: the #GstEvent to send to the pad. * - * Sends the event to the pad. + * Sends the event to the pad. This function can be used + * by applications to send events in the pipeline. * * Returns: TRUE if the event was handled. */ gboolean gst_pad_send_event (GstPad * pad, GstEvent * event) { - gboolean success = FALSE; + gboolean result = FALSE; GstRealPad *rpad; - GstElement *parent; + GstPadEventFunction eventfunc; g_return_val_if_fail (GST_IS_PAD (pad), FALSE); g_return_val_if_fail (event != NULL, FALSE); - parent = gst_pad_get_parent (pad); - g_return_val_if_fail (GST_STATE (parent) >= GST_STATE_PAUSED, FALSE); rpad = GST_PAD_REALIZE (pad); - if (GST_EVENT_SRC (event) == NULL) GST_EVENT_SRC (event) = gst_object_ref (GST_OBJECT (rpad)); GST_CAT_DEBUG (GST_CAT_EVENT, "have event type %d on pad %s:%s", GST_EVENT_TYPE (event), GST_DEBUG_PAD_NAME (rpad)); - if (GST_RPAD_EVENTHANDLER (rpad)) - success = GST_RPAD_EVENTHANDLER (rpad) (GST_PAD (rpad), event); - else { - g_warning ("pad %s:%s has no event handler", GST_DEBUG_PAD_NAME (rpad)); - gst_event_unref (event); + if (GST_PAD_IS_SINK (pad)) { + if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH) { + GST_CAT_DEBUG (GST_CAT_EVENT, "have flush event"); + GST_LOCK (pad); + if (GST_EVENT_FLUSH_DONE (event)) { + GST_CAT_DEBUG (GST_CAT_EVENT, "clear flush flag"); + GST_FLAG_UNSET (pad, GST_PAD_FLUSHING); + } else { + GST_CAT_DEBUG (GST_CAT_EVENT, "set flush flag"); + GST_FLAG_SET (pad, GST_PAD_FLUSHING); + } + GST_UNLOCK (pad); + } } - return success; + if ((eventfunc = GST_RPAD_EVENTFUNC (rpad)) == NULL) + goto no_function; + + result = eventfunc (GST_PAD_CAST (rpad), event); + + return result; + + /* ERROR handling */ +no_function: + { + g_warning ("pad %s:%s has no event handler, file a bug.", + GST_DEBUG_PAD_NAME (rpad)); + gst_event_unref (event); + return FALSE; + } } typedef struct @@ -4264,86 +3765,3 @@ gst_pad_get_formats (GstPad * pad) return NULL; } - -#define CALL_CHAINFUNC(pad, data) G_STMT_START {\ - GstData *__temp = (data); \ - DEBUG_DATA (pad, __temp, "calling chain function with "); \ - if (GST_IS_EVENT (__temp) && \ - !GST_FLAG_IS_SET (gst_pad_get_parent (pad), GST_ELEMENT_EVENT_AWARE)) { \ - gst_pad_send_event (pad, GST_EVENT (__temp)); \ - } else { \ - GST_FLAG_SET (pad, GST_RPAD_IN_CHAINFUNC); \ - GST_RPAD_CHAINFUNC (pad) (pad, __temp); \ - GST_FLAG_UNSET (pad, GST_RPAD_IN_CHAINFUNC); \ - } \ -}G_STMT_END -/** - * gst_pad_call_chain_function: - * @pad: sink pad to call chain function on - * @data: data to call the chain function with - * - * Calls the chain function of the given pad while making sure the internal - * consistency is kept. Use this function inside schedulers instead of calling - * the chain function yourself. - */ -void -gst_pad_call_chain_function (GstPad * pad, GstData * data) -{ - GstPadLink *link; - - g_return_if_fail (GST_IS_REAL_PAD (pad)); - g_return_if_fail (GST_PAD_IS_SINK (pad)); - g_return_if_fail (data != NULL); - g_return_if_fail (GST_RPAD_CHAINFUNC (pad) != NULL); - g_return_if_fail (GST_RPAD_LINK (pad) != NULL); - - link = GST_RPAD_LINK (pad); - if (!link->engaged) { - g_assert (link->temp_store == NULL); - if (GST_IS_BUFFER (data)) { - GST_DEBUG ("moving data buffer %p back to temp_store", data); - link->temp_store = data; - link->engaged = TRUE; - CALL_CHAINFUNC (pad, _invent_event (pad, GST_BUFFER (data))); - link = GST_RPAD_LINK (pad); - if (link->temp_store == NULL) /* happens after relinking in chainfunc */ - return; - g_assert (link->temp_store == data); - link->temp_store = NULL; - } else if (GST_IS_EVENT (data) && - GST_EVENT_TYPE (data) == GST_EVENT_DISCONTINUOUS && - GST_EVENT_DISCONT_NEW_MEDIA (data)) { - link->engaged = TRUE; - GST_CAT_LOG (GST_CAT_SCHEDULING, - "link engaged by discont event %p for pad %s:%s", data, - GST_DEBUG_PAD_NAME (pad)); - } - } - CALL_CHAINFUNC (pad, data); -} - -/** - * gst_pad_call_get_function: - * @pad: sink pad to call chain function on - * - * Calls the get function of the given pad while making sure the internal - * consistency is kept. Use this function inside schedulers instead of calling - * the get function yourself. - * - * Returns: the data provided by the pad or NULL if no data was available. - */ -GstData * -gst_pad_call_get_function (GstPad * pad) -{ - GstData *data; - - g_return_val_if_fail (GST_IS_REAL_PAD (pad), NULL); - g_return_val_if_fail (GST_PAD_IS_SRC (pad), NULL); - g_return_val_if_fail (GST_RPAD_GETFUNC (pad) != NULL, NULL); - - GST_FLAG_SET (pad, GST_RPAD_IN_GETFUNC); - data = GST_RPAD_GETFUNC (pad) (pad); - GST_FLAG_UNSET (pad, GST_RPAD_IN_GETFUNC); - DEBUG_DATA (pad, data, "getfunction returned"); - return data; -} diff --git a/gst/gstpad.h b/gst/gstpad.h index 7ccf5f8d21..fc28d5d786 100644 --- a/gst/gstpad.h +++ b/gst/gstpad.h @@ -32,6 +32,7 @@ #include #include #include +#include G_BEGIN_DECLS @@ -87,10 +88,12 @@ typedef struct _GstStaticPadTemplate GstStaticPadTemplate; typedef struct _GstPadLink GstPadLink; typedef enum { - GST_PAD_LINK_REFUSED = -1, - GST_PAD_LINK_DELAYED = 0, - GST_PAD_LINK_OK = 1, - GST_PAD_LINK_DONE = 2 + GST_PAD_LINK_NOSCHED = -5, /* pads cannot cooperate in scheduling */ + GST_PAD_LINK_NOFORMAT = -4, /* pads do not have common format */ + GST_PAD_LINK_REFUSED = -3, /* refused for some reason */ + GST_PAD_LINK_WRONG_DIRECTION = -2, /* pads have wrong direction */ + GST_PAD_LINK_WAS_LINKED = -1, /* pad was already linked */ + GST_PAD_LINK_OK = 0, /* link ok */ } GstPadLinkReturn; #define GST_PAD_LINK_FAILED(ret) (ret < GST_PAD_LINK_OK) @@ -128,11 +131,11 @@ typedef enum { /* pad states */ typedef gboolean (*GstPadActivateFunction) (GstPad *pad, GstActivateMode mode); -/* this defines the functions used to chain buffers - * pad is the sink pad (so the same chain function can be used for N pads) - * buf is the buffer being passed */ -typedef void (*GstPadChainFunction) (GstPad *pad,GstData *data); -typedef GstData* (*GstPadGetFunction) (GstPad *pad); +/* data passing */ +typedef void (*GstPadLoopFunction) (GstPad *pad); +typedef GstFlowReturn (*GstPadChainFunction) (GstPad *pad, GstBuffer *buffer); +typedef GstFlowReturn (*GstPadGetRangeFunction) (GstPad *pad, guint64 offset, + guint length, GstBuffer **buffer); typedef gboolean (*GstPadEventFunction) (GstPad *pad, GstEvent *event); /* convert/query/format functions */ @@ -147,16 +150,20 @@ typedef const GstEventMask* (*GstPadEventMaskFunction) (GstPad *pad); typedef const GstQueryType* (*GstPadQueryTypeFunction) (GstPad *pad); /* linking */ -typedef GstPadLinkReturn (*GstPadLinkFunction) (GstPad *pad, const GstCaps *caps); -typedef void (*GstPadUnlinkFunction) (GstPad *pad); +typedef GstPadLinkReturn (*GstPadLinkFunction) (GstPad *pad, GstPad *peer); +typedef void (*GstPadUnlinkFunction) (GstPad *pad); /* caps nego */ -typedef GstCaps* (*GstPadGetCapsFunction) (GstPad *pad); -typedef GstCaps* (*GstPadFixateFunction) (GstPad *pad, const GstCaps *caps); -typedef GstBuffer* (*GstPadBufferAllocFunction) (GstPad *pad, guint64 offset, guint size); - +typedef GstCaps* (*GstPadGetCapsFunction) (GstPad *pad); +typedef gboolean (*GstPadSetCapsFunction) (GstPad *pad, GstCaps *caps); +typedef gboolean (*GstPadAcceptCapsFunction) (GstPad *pad, GstCaps *caps); +typedef GstCaps* (*GstPadFixateCapsFunction) (GstPad *pad, GstCaps *caps); +typedef GstBuffer* (*GstPadBufferAllocFunction) (GstPad *pad, guint64 offset, guint size, + GstCaps *caps); /* misc */ -typedef gboolean (*GstPadDispatcherFunction) (GstPad *pad, gpointer data); +typedef gboolean (*GstPadDispatcherFunction) (GstPad *pad, gpointer data); + +typedef void (*GstPadBlockCallback) (GstPad *pad, gboolean blocked, gpointer user_data); typedef enum { GST_PAD_UNKNOWN, @@ -166,8 +173,10 @@ typedef enum { typedef enum { GST_PAD_ACTIVE = GST_OBJECT_FLAG_LAST, - GST_PAD_NEGOTIATING, - GST_PAD_DISPATCHING, + GST_PAD_BLOCKED, + GST_PAD_FLUSHING, + GST_PAD_IN_GETCAPS, + GST_PAD_IN_SETCAPS, GST_PAD_FLAG_LAST = GST_OBJECT_FLAG_LAST + 8 } GstPadFlags; @@ -188,39 +197,48 @@ struct _GstPadClass { gpointer _gst_reserved[GST_PADDING]; }; -typedef enum { - GST_RPAD_IN_GETFUNC = GST_PAD_FLAG_LAST, - GST_RPAD_IN_CHAINFUNC, - GST_RPAD_FLAG_LAST = GST_PAD_FLAG_LAST + 4 -} GstRealPadFlags; - struct _GstRealPad { GstPad pad; /* direction cannot change after creating the pad */ GstPadDirection direction; + /*< public >*/ /* with STREAM_LOCK */ + /* streaming rec_lock */ + GStaticRecMutex *stream_rec_lock; + GstTask *task; + /*< public >*/ /* with PREROLL_LOCK */ + GMutex *preroll_lock; + GCond *preroll_cond; + + /*< public >*/ /* with LOCK */ + /* block cond, mutex is from the object */ + GCond *block_cond; + GstPadBlockCallback block_callback; + gpointer block_data; + /* the pad capabilities */ - GstCaps *caps; - GstCaps *appfilter; - GstPadGetCapsFunction getcapsfunc; - GstPadFixateFunction appfixatefunc; - GstPadFixateFunction fixatefunc; + GstCaps *caps; + GstCaps *appfilter; + GstPadGetCapsFunction getcapsfunc; + GstPadSetCapsFunction setcapsfunc; + GstPadAcceptCapsFunction acceptcapsfunc; + GstPadFixateCapsFunction fixatecapsfunc; + + GstPadActivateFunction activatefunc; /* pad link */ - GstPadLinkFunction linkfunc; - GstPadUnlinkFunction unlinkfunc; - GstRealPad *peer; + GstPadLinkFunction linkfunc; + GstPadUnlinkFunction unlinkfunc; + GstRealPad *peer; - gpointer sched_private; + gpointer sched_private; /* data transport functions */ - GstPadChainFunction chainfunc; - GstPadChainFunction chainhandler; - GstPadGetFunction getfunc; - GstPadGetFunction gethandler; + GstPadLoopFunction loopfunc; + GstPadChainFunction chainfunc; + GstPadGetRangeFunction getrangefunc; GstPadEventFunction eventfunc; - GstPadEventFunction eventhandler; GstPadEventMaskFunction eventmaskfunc; @@ -239,9 +257,6 @@ struct _GstRealPad { GstProbeDispatcher probedisp; - GstPadLink *link; - GstCaps *explicit_caps; - /*< private >*/ gpointer _gst_reserved[GST_PADDING]; }; @@ -252,9 +267,7 @@ struct _GstRealPadClass { /* signal callbacks */ void (*linked) (GstPad *pad, GstPad *peer); void (*unlinked) (GstPad *pad, GstPad *peer); - GstPadFixateFunction appfixatefunc; - - void (*caps_nego_failed) (GstPad *pad, GstCaps *caps); + void (*request_link) (GstPad *pad); /*< private >*/ gpointer _gst_reserved[GST_PADDING]; @@ -284,12 +297,12 @@ struct _GstGhostPadClass { /* GstRealPad */ #define GST_RPAD_DIRECTION(pad) (GST_REAL_PAD_CAST(pad)->direction) +#define GST_RPAD_TASK(pad) (GST_REAL_PAD_CAST(pad)->task) +#define GST_RPAD_ACTIVATEFUNC(pad) (GST_REAL_PAD_CAST(pad)->activatefunc) +#define GST_RPAD_LOOPFUNC(pad) (GST_REAL_PAD_CAST(pad)->loopfunc) #define GST_RPAD_CHAINFUNC(pad) (GST_REAL_PAD_CAST(pad)->chainfunc) -#define GST_RPAD_CHAINHANDLER(pad) (GST_REAL_PAD_CAST(pad)->chainhandler) -#define GST_RPAD_GETFUNC(pad) (GST_REAL_PAD_CAST(pad)->getfunc) -#define GST_RPAD_GETHANDLER(pad) (GST_REAL_PAD_CAST(pad)->gethandler) +#define GST_RPAD_GETRANGEFUNC(pad) (GST_REAL_PAD_CAST(pad)->getrangefunc) #define GST_RPAD_EVENTFUNC(pad) (GST_REAL_PAD_CAST(pad)->eventfunc) -#define GST_RPAD_EVENTHANDLER(pad) (GST_REAL_PAD_CAST(pad)->eventhandler) #define GST_RPAD_CONVERTFUNC(pad) (GST_REAL_PAD_CAST(pad)->convertfunc) #define GST_RPAD_QUERYFUNC(pad) (GST_REAL_PAD_CAST(pad)->queryfunc) #define GST_RPAD_INTLINKFUNC(pad) (GST_REAL_PAD_CAST(pad)->intlinkfunc) @@ -304,35 +317,62 @@ struct _GstGhostPadClass { #define GST_RPAD_CAPS(pad) (GST_REAL_PAD_CAST(pad)->caps) #define GST_RPAD_APPFILTER(pad) (GST_REAL_PAD_CAST(pad)->appfilter) #define GST_RPAD_GETCAPSFUNC(pad) (GST_REAL_PAD_CAST(pad)->getcapsfunc) -#define GST_RPAD_FIXATEFUNC(pad) (GST_REAL_PAD_CAST(pad)->fixatefunc) -#define GST_RPAD_LINK(pad) (GST_REAL_PAD_CAST(pad)->link) -#define GST_RPAD_EXPLICIT_CAPS(pad) (GST_REAL_PAD_CAST(pad)->explicit_caps) +#define GST_RPAD_SETCAPSFUNC(pad) (GST_REAL_PAD_CAST(pad)->setcapsfunc) +#define GST_RPAD_ACCEPTCAPSFUNC(pad) (GST_REAL_PAD_CAST(pad)->acceptcapsfunc) +#define GST_RPAD_FIXATECAPSFUNC(pad) (GST_REAL_PAD_CAST(pad)->fixatecapsfunc) #define GST_RPAD_BUFFERALLOCFUNC(pad) (GST_REAL_PAD_CAST(pad)->bufferallocfunc) #define GST_RPAD_IS_LINKED(pad) (GST_RPAD_PEER(pad) != NULL) #define GST_RPAD_IS_ACTIVE(pad) (GST_FLAG_IS_SET (pad, GST_PAD_ACTIVE)) +#define GST_RPAD_IS_BLOCKED(pad) (GST_FLAG_IS_SET (pad, GST_PAD_BLOCKED)) +#define GST_RPAD_IS_FLUSHING(pad) (GST_FLAG_IS_SET (pad, GST_PAD_FLUSHING)) +#define GST_RPAD_IS_IN_GETCAPS(pad) (GST_FLAG_IS_SET (pad, GST_PAD_IN_GETCAPS)) +#define GST_RPAD_IS_IN_SETCAPS(pad) (GST_FLAG_IS_SET (pad, GST_PAD_IN_SETCAPS)) #define GST_RPAD_IS_USABLE(pad) (GST_RPAD_IS_LINKED (pad) && \ GST_RPAD_IS_ACTIVE(pad) && GST_RPAD_IS_ACTIVE(GST_RPAD_PEER (pad))) #define GST_RPAD_IS_SRC(pad) (GST_RPAD_DIRECTION(pad) == GST_PAD_SRC) #define GST_RPAD_IS_SINK(pad) (GST_RPAD_DIRECTION(pad) == GST_PAD_SINK) +#define GST_STREAM_GET_LOCK(pad) (GST_PAD_REALIZE(pad)->stream_rec_lock) +#define GST_STREAM_LOCK(pad) (g_static_rec_mutex_lock(GST_STREAM_GET_LOCK(pad))) +#define GST_STREAM_TRYLOCK(pad) (g_static_rec_mutex_trylock(GST_STREAM_GET_LOCK(pad))) +#define GST_STREAM_UNLOCK(pad) (g_static_rec_mutex_unlock(GST_STREAM_GET_LOCK(pad))) + +#define GST_PREROLL_GET_LOCK(pad) (GST_PAD_REALIZE(pad)->preroll_lock) +#define GST_PREROLL_LOCK(pad) (g_mutex_lock(GST_PREROLL_GET_LOCK(pad))) +#define GST_PREROLL_TRYLOCK(pad) (g_mutex_trylock(GST_PREROLL_GET_LOCK(pad))) +#define GST_PREROLL_UNLOCK(pad) (g_mutex_unlock(GST_PREROLL_GET_LOCK(pad))) +#define GST_PREROLL_GET_COND(pad) (GST_PAD_REALIZE(pad)->preroll_cond) +#define GST_PREROLL_WAIT(pad) g_cond_wait (GST_PREROLL_GET_COND (pad), GST_PREROLL_GET_LOCK (pad)) +#define GST_PREROLL_TIMED_WAIT(pad, timeval) g_cond_timed_wait (GST_PREROLL_GET_COND (pad), GST_PREROLL_GET_LOCK (pad),\ + timeval) +#define GST_PREROLL_SIGNAL(pad) g_cond_signal (GST_PREROLL_GET_COND (pad)); +#define GST_PREROLL_BROADCAST(pad) g_cond_broadcast (GST_PREROLL_GET_COND (pad)); + +#define GST_PAD_BLOCK_GET_COND(pad) (GST_PAD_REALIZE(pad)->block_cond) +#define GST_PAD_BLOCK_WAIT(pad) (g_cond_wait(GST_PAD_BLOCK_GET_COND (pad), GST_GET_LOCK (pad))) +#define GST_PAD_BLOCK_SIGNAL(pad) (g_cond_signal(GST_PAD_BLOCK_GET_COND (pad))) + /* GstGhostPad */ #define GST_GPAD_REALPAD(pad) (((GstGhostPad *)(pad))->realpad) -/* Generic */ +/* Generic, be VERY carefull with these macros as the ghostpad could be lost */ #define GST_PAD_REALIZE(pad) (GST_IS_REAL_PAD(pad) ? ((GstRealPad *)(pad)) : GST_GPAD_REALPAD(pad)) #define GST_PAD_DIRECTION(pad) GST_RPAD_DIRECTION(GST_PAD_REALIZE(pad)) -#define GST_PAD_CAPS(pad) (gst_pad_get_negotiated_caps(GST_PAD (pad))) +#define GST_PAD_CAPS(pad) GST_RPAD_CAPS(GST_PAD_REALIZE (pad)) +#define GST_PAD_APPFILTER(pad) GST_RPAD_APPFILTER(GST_PAD_REALIZE (pad)) #define GST_PAD_PEER(pad) GST_PAD_CAST(GST_RPAD_PEER(GST_PAD_REALIZE(pad))) /* Some check functions (unused?) */ #define GST_PAD_IS_LINKED(pad) (GST_RPAD_IS_LINKED(GST_PAD_REALIZE(pad))) #define GST_PAD_IS_ACTIVE(pad) (GST_RPAD_IS_ACTIVE(GST_PAD_REALIZE(pad))) -#define GST_PAD_IS_NEGOTIATING(pad) (GST_FLAG_IS_SET (pad, GST_PAD_NEGOTIATING)) -#define GST_PAD_IS_DISPATCHING(pad) (GST_FLAG_IS_SET (pad, GST_PAD_DISPATCHING)) +#define GST_PAD_IS_BLOCKED(pad) (GST_RPAD_IS_BLOCKED(GST_PAD_REALIZE(pad))) +#define GST_PAD_IS_NEGOTIATING(pad) (GST_RPAD_IS_NEGOTIATING(GST_PAD_REALIZE(pad))) +#define GST_PAD_IS_IN_GETCAPS(pad) (GST_RPAD_IS_IN_GETCAPS(GST_PAD_REALIZE(pad))) +#define GST_PAD_IS_IN_SETCAPS(pad) (GST_RPAD_IS_IN_SETCAPS(GST_PAD_REALIZE(pad))) #define GST_PAD_IS_USABLE(pad) (GST_RPAD_IS_USABLE(GST_PAD_REALIZE(pad))) -#define GST_PAD_CAN_PULL(pad) (GST_IS_REAL_PAD(pad) && GST_REAL_PAD(pad)->gethandler != NULL) +#define GST_PAD_CAN_PULL(pad) (GST_RPAD_CAN_PULL(GST_PAD_REALIZE(pad))) #define GST_PAD_IS_SRC(pad) (GST_RPAD_IS_SRC(GST_PAD_REALIZE(pad))) #define GST_PAD_IS_SINK(pad) (GST_RPAD_IS_SINK(GST_PAD_REALIZE(pad))) @@ -417,22 +457,27 @@ GstElement* gst_pad_get_real_parent (GstPad *pad); GstPadDirection gst_pad_get_direction (GstPad *pad); -gboolean gst_pad_set_active (GstPad *pad, gboolean active); +gboolean gst_pad_set_active (GstPad *pad, GstActivateMode mode); gboolean gst_pad_is_active (GstPad *pad); +gboolean gst_pad_set_blocked (GstPad *pad, gboolean blocked); +gboolean gst_pad_set_blocked_async (GstPad *pad, gboolean blocked, + GstPadBlockCallback callback, gpointer user_data); +gboolean gst_pad_is_blocked (GstPad *pad); void gst_pad_set_element_private (GstPad *pad, gpointer priv); gpointer gst_pad_get_element_private (GstPad *pad); -GstScheduler* gst_pad_get_scheduler (GstPad *pad); - GstPadTemplate* gst_pad_get_pad_template (GstPad *pad); -void gst_pad_set_bufferalloc_function (GstPad *pad, GstPadBufferAllocFunction bufalloc); -GstBuffer* gst_pad_alloc_buffer (GstPad *pad, guint64 offset, gint size); +void gst_pad_set_bufferalloc_function (GstPad *pad, GstPadBufferAllocFunction bufalloc); +GstBuffer* gst_pad_alloc_buffer (GstPad *pad, guint64 offset, gint size, + GstCaps *caps); /* data passing setup functions */ +void gst_pad_set_activate_function (GstPad *pad, GstPadActivateFunction activate); +void gst_pad_set_loop_function (GstPad *pad, GstPadLoopFunction loop); void gst_pad_set_chain_function (GstPad *pad, GstPadChainFunction chain); -void gst_pad_set_get_function (GstPad *pad, GstPadGetFunction get); +void gst_pad_set_getrange_function (GstPad *pad, GstPadGetRangeFunction get); void gst_pad_set_event_function (GstPad *pad, GstPadEventFunction event); void gst_pad_set_event_mask_function (GstPad *pad, GstPadEventMaskFunction mask_func); G_CONST_RETURN GstEventMask* @@ -442,13 +487,14 @@ G_CONST_RETURN GstEventMask* /* pad links */ void gst_pad_set_link_function (GstPad *pad, GstPadLinkFunction link); -gboolean gst_pad_can_link (GstPad *srcpad, GstPad *sinkpad); -gboolean gst_pad_can_link_filtered (GstPad *srcpad, GstPad *sinkpad, const GstCaps *filtercaps); void gst_pad_set_unlink_function (GstPad *pad, GstPadUnlinkFunction unlink); -gboolean gst_pad_link (GstPad *srcpad, GstPad *sinkpad); -gboolean gst_pad_link_filtered (GstPad *srcpad, GstPad *sinkpad, const GstCaps *filtercaps); -void gst_pad_unlink (GstPad *srcpad, GstPad *sinkpad); +GstPadLinkReturn gst_pad_link (GstPad *srcpad, GstPad *sinkpad); +GstPadLinkReturn gst_pad_link_filtered (GstPad *srcpad, GstPad *sinkpad, + const GstCaps *filtercaps); +GstPadLinkReturn gst_pad_relink_filtered (GstPad *srcpad, GstPad *sinkpad, + const GstCaps *filtercaps); +gboolean gst_pad_unlink (GstPad *srcpad, GstPad *sinkpad); gboolean gst_pad_is_linked (GstPad *pad); GstPad* gst_pad_get_peer (GstPad *pad); @@ -456,45 +502,38 @@ GstPad* gst_pad_realize (GstPad *pad); /* capsnego functions */ void gst_pad_set_getcaps_function (GstPad *pad, GstPadGetCapsFunction getcaps); -void gst_pad_set_fixate_function (GstPad *pad, GstPadFixateFunction fixate); -GstCaps * gst_pad_proxy_getcaps (GstPad *pad); -GstCaps * gst_pad_proxy_fixate (GstPad *pad, const GstCaps *caps); -gboolean gst_pad_set_explicit_caps (GstPad *pad, const GstCaps *caps); -void gst_pad_use_explicit_caps (GstPad *pad); -gboolean gst_pad_relink_filtered (GstPad *srcpad, GstPad *sinkpad, const GstCaps *filtercaps); -GstPadLinkReturn gst_pad_renegotiate (GstPad *pad); -void gst_pad_unnegotiate (GstPad *pad); -gboolean gst_pad_try_relink_filtered (GstPad *srcpad, GstPad *sinkpad, const GstCaps *filtercaps); -void gst_pad_caps_change_notify (GstPad *pad); - -gboolean gst_pad_recover_caps_error (GstPad *pad, const GstCaps *allowed); +void gst_pad_set_acceptcaps_function (GstPad *pad, GstPadAcceptCapsFunction acceptcaps); +void gst_pad_set_fixatecaps_function (GstPad *pad, GstPadFixateCapsFunction fixatecaps); +void gst_pad_set_setcaps_function (GstPad *pad, GstPadSetCapsFunction setcaps); G_CONST_RETURN GstCaps* gst_pad_get_pad_template_caps (GstPad *pad); /* capsnego function for connected/unconnected pads */ -GstCaps* gst_pad_get_caps (GstPad *pad); -gboolean gst_pad_set_caps (GstPad *pad, GstCaps *caps); -GstPadLinkReturn gst_pad_try_set_caps (GstPad *pad, const GstCaps *caps); -GstPadLinkReturn gst_pad_try_set_caps_nonfixed (GstPad *pad, const GstCaps *caps); -gboolean gst_pad_check_compatibility (GstPad *srcpad, GstPad *sinkpad); +GstCaps * gst_pad_get_caps (GstPad * pad); +GstCaps* gst_pad_fixate_caps (GstPad * pad, GstCaps *caps); +gboolean gst_pad_accept_caps (GstPad * pad, GstCaps *caps); +gboolean gst_pad_set_caps (GstPad * pad, GstCaps *caps); GstCaps * gst_pad_peer_get_caps (GstPad * pad); +gboolean gst_pad_peer_accept_caps (GstPad * pad, GstCaps *caps); /* capsnego for connected pads */ -GstCaps* gst_pad_get_allowed_caps (GstPad *pad); -G_CONST_RETURN GstCaps* gst_pad_get_negotiated_caps (GstPad *pad); -gboolean gst_pad_is_negotiated (GstPad *pad); - +GstCaps * gst_pad_get_allowed_caps (GstPad * srcpad); +GstCaps * gst_pad_get_negotiated_caps (GstPad * pad); +GstCaps * gst_pad_get_filter_caps (GstPad * pad); /* data passing functions */ -void gst_pad_push (GstPad *pad, GstData *data); -GstData* gst_pad_pull (GstPad *pad); +GstFlowReturn gst_pad_push (GstPad *pad, GstBuffer *buffer); +GstFlowReturn gst_pad_pull_range (GstPad *pad, guint64 offset, guint size, + GstBuffer **buffer); +gboolean gst_pad_push_event (GstPad *pad, GstEvent *event); gboolean gst_pad_send_event (GstPad *pad, GstEvent *event); gboolean gst_pad_event_default (GstPad *pad, GstEvent *event); -/* FIXME 0.9: rename to _select? Otherwise rename SchedulerClass pointer */ -GstData * gst_pad_collectv (GstPad **selected, const GList *padlist); -GstData * gst_pad_collect (GstPad **selected, GstPad *pad, ...); -GstData * gst_pad_collect_valist (GstPad **selected, GstPad *pad, va_list varargs); + +/* pad tasks */ +gboolean gst_pad_start_task (GstPad *pad); +gboolean gst_pad_pause_task (GstPad *pad); +gboolean gst_pad_stop_task (GstPad *pad); /* convert/query/format functions */ void gst_pad_set_formats_function (GstPad *pad, @@ -561,9 +600,6 @@ xmlNodePtr gst_ghost_pad_save_thyself (GstPad *pad, xmlNodePtr parent); #endif -/* for schedulers only */ -void gst_pad_call_chain_function (GstPad *pad, GstData *data); -GstData * gst_pad_call_get_function (GstPad *pad); G_END_DECLS diff --git a/gst/gstpipeline.c b/gst/gstpipeline.c index f7eedeed39..6aec13f577 100644 --- a/gst/gstpipeline.c +++ b/gst/gstpipeline.c @@ -61,6 +61,9 @@ static void gst_pipeline_set_property (GObject * object, guint prop_id, static void gst_pipeline_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); +static GstBusSyncReply pipeline_bus_handler (GstBus * bus, GstMessage * message, + GstPipeline * pipeline); + static GstClock *gst_pipeline_get_clock_func (GstElement * element); static GstElementStateReturn gst_pipeline_change_state (GstElement * element); @@ -135,9 +138,7 @@ gst_pipeline_init (GTypeInstance * instance, gpointer g_class) { GstScheduler *scheduler; GstPipeline *pipeline = GST_PIPELINE (instance); - - /* pipelines are managing bins */ - GST_FLAG_SET (pipeline, GST_BIN_FLAG_MANAGER); + GstBus *bus; /* get an instance of the default scheduler */ scheduler = gst_scheduler_factory_make (NULL, GST_ELEMENT (pipeline)); @@ -150,6 +151,16 @@ gst_pipeline_init (GTypeInstance * instance, gpointer g_class) "Are you sure you have a registry ?\n" "Run gst-register as root if you haven't done so yet.", name); } + bus = g_object_new (gst_bus_get_type (), NULL); + gst_bus_set_sync_handler (bus, + (GstBusSyncHandler) pipeline_bus_handler, pipeline); + pipeline->eosed = NULL; + pipeline->delay = DEFAULT_DELAY; + pipeline->play_timeout = DEFAULT_PLAY_TIMEOUT; + /* we are our own manager */ + GST_ELEMENT_MANAGER (pipeline) = pipeline; + gst_element_set_bus (GST_ELEMENT (pipeline), bus); + gst_element_set_scheduler (GST_ELEMENT (pipeline), scheduler); } static void @@ -205,6 +216,103 @@ gst_pipeline_get_property (GObject * object, guint prop_id, GST_UNLOCK (pipeline); } +static gboolean +is_eos (GstPipeline * pipeline) +{ + GstIterator *sinks; + gboolean result = TRUE; + gboolean done = FALSE; + + sinks = gst_bin_iterate_sinks (GST_BIN (pipeline)); + while (!done) { + gpointer data; + + switch (gst_iterator_next (sinks, &data)) { + case GST_ITERATOR_OK: + { + GstElement *element = GST_ELEMENT (data); + GList *eosed; + GstElementState state, pending; + GstElementStateReturn complete; + gchar *name; + + complete = gst_element_get_state (element, &state, &pending, NULL); + name = gst_element_get_name (element); + + if (complete == GST_STATE_ASYNC) { + GST_DEBUG ("element %s still performing state change", name); + result = FALSE; + done = TRUE; + goto done; + } else if (state != GST_STATE_PLAYING) { + GST_DEBUG ("element %s not playing %d %d", name, state, pending); + goto done; + } + eosed = g_list_find (pipeline->eosed, element); + if (!eosed) { + result = FALSE; + done = TRUE; + } + done: + g_free (name); + gst_object_unref (GST_OBJECT (element)); + break; + } + case GST_ITERATOR_RESYNC: + result = TRUE; + gst_iterator_resync (sinks); + break; + case GST_ITERATOR_DONE: + done = TRUE; + break; + default: + g_assert_not_reached (); + break; + } + } + gst_iterator_free (sinks); + return result; +} + +/* FIXME, make me threadsafe */ +static GstBusSyncReply +pipeline_bus_handler (GstBus * bus, GstMessage * message, + GstPipeline * pipeline) +{ + GstBusSyncReply result = GST_BUS_PASS; + gboolean posteos = FALSE; + + /* 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. */ + + switch (GST_MESSAGE_TYPE (message)) { + case GST_MESSAGE_EOS: + if (GST_MESSAGE_SRC (message) != GST_OBJECT (pipeline)) { + GST_LOCK (bus); + pipeline->eosed = + g_list_prepend (pipeline->eosed, GST_MESSAGE_SRC (message)); + GST_UNLOCK (bus); + if (is_eos (pipeline)) { + posteos = TRUE; + } + /* we drop all EOS messages */ + result = GST_BUS_DROP; + gst_message_unref (message); + } + break; + case GST_MESSAGE_ERROR: + break; + default: + break; + } + + if (posteos) { + gst_bus_post (bus, gst_message_new_eos (GST_OBJECT (pipeline))); + } + + return result; +} + /** * gst_pipeline_new: * @name: name of new pipeline @@ -221,25 +329,44 @@ gst_pipeline_new (const gchar * name) return gst_element_factory_make ("pipeline", name); } +/* MT safe */ static GstElementStateReturn gst_pipeline_change_state (GstElement * element) { - switch (GST_STATE_TRANSITION (element)) { - case GST_STATE_NULL_TO_READY: - gst_scheduler_setup (GST_ELEMENT_SCHEDULER (element)); - break; - case GST_STATE_READY_TO_PAUSED: - case GST_STATE_PAUSED_TO_PLAYING: - case GST_STATE_PLAYING_TO_PAUSED: - case GST_STATE_PAUSED_TO_READY: - case GST_STATE_READY_TO_NULL: - break; - } + /* implement me */ + return GST_STATE_FAILURE; +} - if (GST_ELEMENT_CLASS (parent_class)->change_state) - return GST_ELEMENT_CLASS (parent_class)->change_state (element); +/** + * gst_pipeline_get_scheduler: + * @pipeline: the pipeline + * + * Gets the #GstScheduler of this pipeline. + * + * Returns: a GstScheduler. + * + * MT safe. + */ +GstScheduler * +gst_pipeline_get_scheduler (GstPipeline * pipeline) +{ + return gst_element_get_scheduler (GST_ELEMENT (pipeline)); +} - return GST_STATE_SUCCESS; +/** + * gst_pipeline_get_bus: + * @pipeline: the pipeline + * + * Gets the #GstBus of this pipeline. + * + * Returns: a GstBus + * + * MT safe. + */ +GstBus * +gst_pipeline_get_bus (GstPipeline * pipeline) +{ + return gst_element_get_bus (GST_ELEMENT (pipeline)); } static GstClock * diff --git a/gst/gstpipeline.h b/gst/gstpipeline.h index 9163f89014..c726a8f500 100644 --- a/gst/gstpipeline.h +++ b/gst/gstpipeline.h @@ -26,6 +26,7 @@ #include #include +#include G_BEGIN_DECLS @@ -69,6 +70,8 @@ struct _GstPipelineClass { GType gst_pipeline_get_type (void); GstElement* gst_pipeline_new (const gchar *name); +GstScheduler* gst_pipeline_get_scheduler (GstPipeline *pipeline); +GstBus* gst_pipeline_get_bus (GstPipeline *pipeline); void gst_pipeline_use_clock (GstPipeline *pipeline, GstClock *clock); void gst_pipeline_set_clock (GstPipeline *pipeline, GstClock *clock); diff --git a/gst/gstprobe.h b/gst/gstprobe.h index 326fe96154..99235eab68 100644 --- a/gst/gstprobe.h +++ b/gst/gstprobe.h @@ -34,7 +34,7 @@ G_BEGIN_DECLS typedef struct _GstProbe GstProbe; -GType gst_probe_get_type (void) G_GNUC_CONST; +GType gst_probe_get_type (void); /* the callback should return FALSE if the data should be discarded */ typedef gboolean (*GstProbeCallback) (GstProbe *probe, diff --git a/gst/gstqueue.c b/gst/gstqueue.c index ac889f4ef2..3999c6d57d 100644 --- a/gst/gstqueue.c +++ b/gst/gstqueue.c @@ -2,6 +2,7 @@ * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans * 2003 Colin Walters + * 2005 Wim Taymans * * gstqueue.c: * @@ -26,6 +27,7 @@ #include "gstqueue.h" #include "gstscheduler.h" +#include "gstpipeline.h" #include "gstevent.h" #include "gstinfo.h" #include "gsterror.h" @@ -113,13 +115,6 @@ enum } G_STMT_END -typedef struct _GstQueueEventResponse -{ - GstEvent *event; - gboolean ret, handled; -} -GstQueueEventResponse; - static void gst_queue_base_init (GstQueueClass * klass); static void gst_queue_class_init (GstQueueClass * klass); static void gst_queue_init (GstQueue * queue); @@ -130,21 +125,24 @@ static void gst_queue_set_property (GObject * object, static void gst_queue_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); -static void gst_queue_chain (GstPad * pad, GstData * data); -static GstData *gst_queue_get (GstPad * pad); +static GstFlowReturn gst_queue_chain (GstPad * pad, GstBuffer * buffer); +static GstBuffer *gst_queue_bufferalloc (GstPad * pad, guint64 offset, + guint size, GstCaps * caps); +static void gst_queue_loop (GstPad * pad); + +static gboolean gst_queue_handle_sink_event (GstPad * pad, GstEvent * event); static gboolean gst_queue_handle_src_event (GstPad * pad, GstEvent * event); static gboolean gst_queue_handle_src_query (GstPad * pad, GstQueryType type, GstFormat * fmt, gint64 * value); static GstCaps *gst_queue_getcaps (GstPad * pad); -static GstPadLinkReturn -gst_queue_link_sink (GstPad * pad, const GstCaps * caps); -static GstPadLinkReturn gst_queue_link_src (GstPad * pad, const GstCaps * caps); +static GstPadLinkReturn gst_queue_link_sink (GstPad * pad, GstPad * peer); +static GstPadLinkReturn gst_queue_link_src (GstPad * pad, GstPad * peer); static void gst_queue_locked_flush (GstQueue * queue); +static gboolean gst_queue_src_activate (GstPad * pad, GstActivateMode mode); static GstElementStateReturn gst_queue_change_state (GstElement * element); -static gboolean gst_queue_release_locks (GstElement * element); #define GST_TYPE_QUEUE_LEAKY (queue_leaky_get_type ()) @@ -292,33 +290,32 @@ gst_queue_class_init (GstQueueClass * klass) gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_queue_finalize); gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_queue_change_state); - gstelement_class->release_locks = GST_DEBUG_FUNCPTR (gst_queue_release_locks); } static void gst_queue_init (GstQueue * queue) { - /* scheduling on this kind of element is, well, interesting */ - GST_FLAG_SET (queue, GST_ELEMENT_DECOUPLED); - GST_FLAG_SET (queue, GST_ELEMENT_EVENT_AWARE); - queue->sinkpad = gst_pad_new_from_template (gst_static_pad_template_get (&sinktemplate), "sink"); gst_pad_set_chain_function (queue->sinkpad, GST_DEBUG_FUNCPTR (gst_queue_chain)); - gst_element_add_pad (GST_ELEMENT (queue), queue->sinkpad); + gst_pad_set_event_function (queue->sinkpad, + GST_DEBUG_FUNCPTR (gst_queue_handle_sink_event)); gst_pad_set_link_function (queue->sinkpad, GST_DEBUG_FUNCPTR (gst_queue_link_sink)); gst_pad_set_getcaps_function (queue->sinkpad, GST_DEBUG_FUNCPTR (gst_queue_getcaps)); - gst_pad_set_active (queue->sinkpad, TRUE); + gst_pad_set_bufferalloc_function (queue->sinkpad, + GST_DEBUG_FUNCPTR (gst_queue_bufferalloc)); + gst_element_add_pad (GST_ELEMENT (queue), queue->sinkpad); queue->srcpad = gst_pad_new_from_template (gst_static_pad_template_get (&srctemplate), "src"); - gst_pad_set_get_function (queue->srcpad, GST_DEBUG_FUNCPTR (gst_queue_get)); - gst_element_add_pad (GST_ELEMENT (queue), queue->srcpad); + gst_pad_set_loop_function (queue->srcpad, GST_DEBUG_FUNCPTR (gst_queue_loop)); + gst_pad_set_activate_function (queue->srcpad, + GST_DEBUG_FUNCPTR (gst_queue_src_activate)); gst_pad_set_link_function (queue->srcpad, GST_DEBUG_FUNCPTR (gst_queue_link_src)); gst_pad_set_getcaps_function (queue->srcpad, @@ -327,12 +324,12 @@ gst_queue_init (GstQueue * queue) GST_DEBUG_FUNCPTR (gst_queue_handle_src_event)); gst_pad_set_query_function (queue->srcpad, GST_DEBUG_FUNCPTR (gst_queue_handle_src_query)); - gst_pad_set_active (queue->srcpad, TRUE); + gst_element_add_pad (GST_ELEMENT (queue), queue->srcpad); queue->cur_level.buffers = 0; /* no content */ queue->cur_level.bytes = 0; /* no content */ queue->cur_level.time = 0; /* no content */ - queue->max_size.buffers = 100; /* 100 buffers */ + queue->max_size.buffers = 200; /* 200 buffers */ queue->max_size.bytes = 10 * 1024 * 1024; /* 10 MB */ queue->max_size.time = GST_SECOND; /* 1 s. */ queue->min_threshold.buffers = 0; /* no threshold */ @@ -348,9 +345,6 @@ gst_queue_init (GstQueue * queue) queue->qlock = g_mutex_new (); queue->item_add = g_cond_new (); queue->item_del = g_cond_new (); - queue->event_done = g_cond_new (); - queue->events = g_queue_new (); - queue->event_lock = g_mutex_new (); queue->queue = g_queue_new (); GST_CAT_DEBUG_OBJECT (GST_CAT_THREAD, queue, @@ -371,19 +365,11 @@ gst_queue_finalize (GObject * object) gst_data_unref (data); } g_queue_free (queue->queue); + GST_CAT_DEBUG_OBJECT (GST_CAT_THREAD, queue, "free mutex"); g_mutex_free (queue->qlock); + GST_CAT_DEBUG_OBJECT (GST_CAT_THREAD, queue, "done free mutex"); g_cond_free (queue->item_add); g_cond_free (queue->item_del); - g_cond_free (queue->event_done); - g_mutex_lock (queue->event_lock); - while (!g_queue_is_empty (queue->events)) { - GstQueueEventResponse *er = g_queue_pop_head (queue->events); - - gst_event_unref (er->event); - } - g_mutex_unlock (queue->event_lock); - g_mutex_free (queue->event_lock); - g_queue_free (queue->events); if (G_OBJECT_CLASS (parent_class)->finalize) G_OBJECT_CLASS (parent_class)->finalize (object); @@ -393,103 +379,69 @@ static GstCaps * gst_queue_getcaps (GstPad * pad) { GstQueue *queue; + GstPad *otherpad; + GstCaps *result; - queue = GST_QUEUE (gst_pad_get_parent (pad)); + queue = GST_QUEUE (GST_PAD_PARENT (pad)); - if (pad == queue->srcpad && queue->cur_level.bytes > 0) { - return gst_caps_copy (queue->negotiated_caps); - } + otherpad = (pad == queue->srcpad ? queue->sinkpad : queue->srcpad); + result = gst_pad_peer_get_caps (otherpad); - return gst_pad_proxy_getcaps (pad); + return result; } static GstPadLinkReturn -gst_queue_link_sink (GstPad * pad, const GstCaps * caps) +gst_queue_link_sink (GstPad * pad, GstPad * peer) { - GstQueue *queue; - GstPadLinkReturn link_ret; - - queue = GST_QUEUE (gst_pad_get_parent (pad)); - - if (queue->cur_level.bytes > 0) { - if (gst_caps_is_equal (caps, queue->negotiated_caps)) { - return GST_PAD_LINK_OK; - } else if (GST_STATE (queue) != GST_STATE_PLAYING) { - return GST_PAD_LINK_DELAYED; - } - - /* Wait until the queue is empty before attempting the pad - negotiation. */ - GST_QUEUE_MUTEX_LOCK; - - STATUS (queue, "waiting for queue to get empty"); - while (queue->cur_level.bytes > 0) { - g_cond_wait (queue->item_del, queue->qlock); - if (queue->interrupt) { - GST_QUEUE_MUTEX_UNLOCK; - return GST_PAD_LINK_DELAYED; - } - } - STATUS (queue, "queue is now empty"); - - GST_QUEUE_MUTEX_UNLOCK; - } - - link_ret = GST_PAD_LINK_OK; -#if 0 - link_ret = gst_pad_proxy_pad_link (pad, caps); - - if (GST_PAD_LINK_SUCCESSFUL (link_ret)) { - /* we store an extra copy of the negotiated caps, just in case - * the pads become unnegotiated while we have buffers */ - gst_caps_replace (&queue->negotiated_caps, gst_caps_copy (caps)); - } -#endif - - return link_ret; + return GST_PAD_LINK_OK; } static GstPadLinkReturn -gst_queue_link_src (GstPad * pad, const GstCaps * caps) +gst_queue_link_src (GstPad * pad, GstPad * peer) +{ + GstPadLinkReturn result = GST_PAD_LINK_OK; + + /* FIXME, see if we need to push or get pulled */ + if (GST_RPAD_LINKFUNC (peer)) + result = GST_RPAD_LINKFUNC (peer) (peer, pad); + + return result; +} + +static GstBuffer * +gst_queue_bufferalloc (GstPad * pad, guint64 offset, guint size, GstCaps * caps) { GstQueue *queue; - GstPadLinkReturn link_ret; + GstPad *otherpeer; + GstBuffer *result = NULL; - queue = GST_QUEUE (gst_pad_get_parent (pad)); + queue = GST_QUEUE (GST_PAD_PARENT (pad)); - if (queue->cur_level.bytes > 0) { - if (gst_caps_is_equal (caps, queue->negotiated_caps)) { - return GST_PAD_LINK_OK; - } - return GST_PAD_LINK_REFUSED; + otherpeer = gst_pad_get_peer (queue->srcpad); + if (otherpeer == NULL || GST_RPAD_BUFFERALLOCFUNC (otherpeer) == NULL) { + /* let the default aloc function do the work */ + result = NULL; + } else { + result = + GST_RPAD_BUFFERALLOCFUNC (otherpeer) (otherpeer, offset, size, caps); } -#if 0 - link_ret = gst_pad_proxy_pad_link (pad, caps); -#endif - link_ret = GST_PAD_LINK_OK; + if (otherpeer) + gst_object_unref (GST_OBJECT (otherpeer)); - if (GST_PAD_LINK_SUCCESSFUL (link_ret)) { - /* we store an extra copy of the negotiated caps, just in case - * the pads become unnegotiated while we have buffers */ - gst_caps_replace (&queue->negotiated_caps, gst_caps_copy (caps)); - } - - return link_ret; + return result; } + static void gst_queue_locked_flush (GstQueue * queue) { while (!g_queue_is_empty (queue->queue)) { GstData *data = g_queue_pop_head (queue->queue); - /* First loose the reference we added when putting that data in the queue */ - gst_data_unref (data); /* Then loose another reference because we are supposed to destroy that data when flushing */ gst_data_unref (data); } - queue->timeval = NULL; queue->cur_level.buffers = 0; queue->cur_level.bytes = 0; queue->cur_level.time = 0; @@ -501,92 +453,120 @@ gst_queue_locked_flush (GstQueue * queue) g_cond_signal (queue->item_del); } -static void -gst_queue_handle_pending_events (GstQueue * queue) -{ - /* check for events to send upstream */ - /* g_queue_get_length is glib 2.4, so don't depend on it yet, use ->length */ - GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, - "handling pending events, events queue of size %d", - queue->events->length); - g_mutex_lock (queue->event_lock); - while (!g_queue_is_empty (queue->events)) { - GstQueueEventResponse *er; +#define STATUS(queue, msg) \ + GST_CAT_LOG_OBJECT (queue_dataflow, queue, \ + "(%s:%s) " msg ": %u of %u-%u buffers, %u of %u-%u " \ + "bytes, %" G_GUINT64_FORMAT " of %" G_GUINT64_FORMAT \ + "-%" G_GUINT64_FORMAT " ns, %u elements", \ + GST_DEBUG_PAD_NAME (pad), \ + queue->cur_level.buffers, \ + queue->min_threshold.buffers, \ + queue->max_size.buffers, \ + queue->cur_level.bytes, \ + queue->min_threshold.bytes, \ + queue->max_size.bytes, \ + queue->cur_level.time, \ + queue->min_threshold.time, \ + queue->max_size.time, \ + queue->queue->length) - er = g_queue_pop_head (queue->events); - - GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, - "sending event %p (%d) from event response %p upstream", - er->event, GST_EVENT_TYPE (er->event), er); - if (er->handled) { - /* change this to an assert when this file gets reviewed properly. */ - GST_ELEMENT_ERROR (queue, CORE, EVENT, (NULL), - ("already handled event %p (%d) from event response %p upstream", - er->event, GST_EVENT_TYPE (er->event), er)); - break; - } - g_mutex_unlock (queue->event_lock); - er->ret = gst_pad_event_default (queue->srcpad, er->event); - er->handled = TRUE; - g_cond_signal (queue->event_done); - g_mutex_lock (queue->event_lock); - GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, "event sent"); - } - g_mutex_unlock (queue->event_lock); -} - -static void -gst_queue_chain (GstPad * pad, GstData * data) +static gboolean +gst_queue_handle_sink_event (GstPad * pad, GstEvent * event) { GstQueue *queue; - g_return_if_fail (pad != NULL); - g_return_if_fail (GST_IS_PAD (pad)); - g_return_if_fail (data != NULL); - queue = GST_QUEUE (GST_OBJECT_PARENT (pad)); -restart: - /* we have to lock the queue since we span threads */ - GST_QUEUE_MUTEX_LOCK; - - gst_queue_handle_pending_events (queue); - - /* assume don't need to flush this buffer when the queue is filled */ - queue->flush = FALSE; - - if (GST_IS_EVENT (data)) { - switch (GST_EVENT_TYPE (data)) { - case GST_EVENT_FLUSH: - STATUS (queue, "received flush event"); + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH: + STATUS (queue, "received flush event"); + /* forward event */ + gst_pad_event_default (pad, event); + if (GST_EVENT_FLUSH_DONE (event)) { + GST_STREAM_LOCK (queue->srcpad); + gst_task_start (GST_RPAD_TASK (queue->srcpad)); + GST_STREAM_UNLOCK (queue->srcpad); + } else { + /* now unblock the chain function */ + GST_QUEUE_MUTEX_LOCK; gst_queue_locked_flush (queue); + GST_QUEUE_MUTEX_UNLOCK; + STATUS (queue, "after flush"); - break; - case GST_EVENT_EOS: - STATUS (queue, "received EOS"); - break; - default: - /* we put the event in the queue, we don't have to act ourselves */ - GST_CAT_LOG_OBJECT (queue_dataflow, queue, - "adding event %p of type %d", data, GST_EVENT_TYPE (data)); - break; - } + + /* unblock the loop function */ + g_cond_signal (queue->item_add); + + /* make sure it stops */ + GST_STREAM_LOCK (queue->srcpad); + gst_task_pause (GST_RPAD_TASK (queue->srcpad)); + GST_CAT_LOG_OBJECT (queue_dataflow, queue, "loop stopped"); + GST_STREAM_UNLOCK (queue->srcpad); + } + goto done; + case GST_EVENT_EOS: + STATUS (queue, "received EOS"); + break; + default: + /* we put the event in the queue, we don't have to act ourselves */ + GST_CAT_LOG_OBJECT (queue_dataflow, queue, + "adding event %p of type %d", event, GST_EVENT_TYPE (event)); + break; } - if (GST_IS_BUFFER (data)) - GST_CAT_LOG_OBJECT (queue_dataflow, queue, - "adding buffer %p of size %d", data, GST_BUFFER_SIZE (data)); + GST_QUEUE_MUTEX_LOCK; + g_queue_push_tail (queue->queue, event); + g_cond_signal (queue->item_add); - /* We make space available if we're "full" according to whatever - * the user defined as "full". Note that this only applies to buffers. - * We always handle events and they don't count in our statistics. */ - if (GST_IS_BUFFER (data) && - ((queue->max_size.buffers > 0 && + GST_QUEUE_MUTEX_UNLOCK; +done: + + return TRUE; +} + +static gboolean +gst_queue_is_empty (GstQueue * queue) +{ + return (queue->queue->length == 0 || + (queue->min_threshold.buffers > 0 && + queue->cur_level.buffers < queue->min_threshold.buffers) || + (queue->min_threshold.bytes > 0 && + queue->cur_level.bytes < queue->min_threshold.bytes) || + (queue->min_threshold.time > 0 && + queue->cur_level.time < queue->min_threshold.time)); +} + +static gboolean +gst_queue_is_filled (GstQueue * queue) +{ + return (((queue->max_size.buffers > 0 && queue->cur_level.buffers >= queue->max_size.buffers) || (queue->max_size.bytes > 0 && queue->cur_level.bytes >= queue->max_size.bytes) || (queue->max_size.time > 0 && - queue->cur_level.time >= queue->max_size.time))) { + queue->cur_level.time >= queue->max_size.time))); +} + + +static GstFlowReturn +gst_queue_chain (GstPad * pad, GstBuffer * buffer) +{ + GstQueue *queue; + + queue = GST_QUEUE (GST_OBJECT_PARENT (pad)); + + GST_STREAM_LOCK (pad); + + /* we have to lock the queue since we span threads */ + GST_QUEUE_MUTEX_LOCK; + + GST_CAT_LOG_OBJECT (queue_dataflow, queue, + "adding buffer %p of size %d", buffer, GST_BUFFER_SIZE (buffer)); + + /* We make space available if we're "full" according to whatever + * the user defined as "full". Note that this only applies to buffers. + * We always handle events and they don't count in our statistics. */ + while (gst_queue_is_filled (queue)) { GST_QUEUE_MUTEX_UNLOCK; g_signal_emit (G_OBJECT (queue), gst_queue_signals[SIGNAL_OVERRUN], 0); GST_QUEUE_MUTEX_LOCK; @@ -598,7 +578,6 @@ restart: GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, "queue is full, leaking buffer on upstream end"); /* now we can clean up and exit right away */ - GST_QUEUE_MUTEX_UNLOCK; goto out_unref; /* leak first buffer in the queue */ @@ -630,15 +609,15 @@ restart: queue->queue->tail = g_list_last (item); queue->queue->length--; - /* and unref the data at the end. Twice, because we keep a ref + /* and unref the buffer at the end. Twice, because we keep a ref * to make things read-only. Also keep our list uptodate. */ - queue->cur_level.bytes -= GST_BUFFER_SIZE (data); + queue->cur_level.bytes -= GST_BUFFER_SIZE (buffer); queue->cur_level.buffers--; - if (GST_BUFFER_DURATION (data) != GST_CLOCK_TIME_NONE) - queue->cur_level.time -= GST_BUFFER_DURATION (data); + if (GST_BUFFER_DURATION (buffer) != GST_CLOCK_TIME_NONE) + queue->cur_level.time -= GST_BUFFER_DURATION (buffer); - gst_data_unref (data); - gst_data_unref (data); + gst_buffer_unref (buffer); + gst_buffer_unref (buffer); break; } @@ -650,66 +629,13 @@ restart: case GST_QUEUE_NO_LEAK: STATUS (queue, "pre-full wait"); - while ((queue->max_size.buffers > 0 && - queue->cur_level.buffers >= queue->max_size.buffers) || - (queue->max_size.bytes > 0 && - queue->cur_level.bytes >= queue->max_size.bytes) || - (queue->max_size.time > 0 && - queue->cur_level.time >= queue->max_size.time)) { + while (gst_queue_is_filled (queue)) { + STATUS (queue, "waiting for item_del signal from thread using qlock"); + g_cond_wait (queue->item_del, queue->qlock); + /* if there's a pending state change for this queue * or its manager, switch back to iterator so bottom * half of state change executes */ - if (queue->interrupt) { - GstScheduler *sched; - - GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, "interrupted"); - queue->interrupt = FALSE; - GST_QUEUE_MUTEX_UNLOCK; - sched = gst_pad_get_scheduler (queue->sinkpad); - if (!sched || gst_scheduler_interrupt (sched, GST_ELEMENT (queue))) { - goto out_unref; - } - /* if we got here because we were unlocked after a - * flush, we don't need to add the buffer to the - * queue again */ - if (queue->flush) { - GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, - "not adding pending buffer after flush"); - goto out_unref; - } - GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, - "adding pending buffer after interrupt"); - goto restart; - } - - if (GST_STATE (queue) != GST_STATE_PLAYING) { - /* this means the other end is shut down. Try to - * signal to resolve the error */ - if (!queue->may_deadlock) { - GST_QUEUE_MUTEX_UNLOCK; - gst_data_unref (data); - GST_ELEMENT_ERROR (queue, CORE, THREAD, (NULL), - ("deadlock found, shutting down source pad elements")); - /* we don't go to out_unref here, since we want to - * unref the buffer *before* calling GST_ELEMENT_ERROR */ - return; - } else { - GST_CAT_WARNING_OBJECT (queue_dataflow, queue, - "%s: waiting for the app to restart " - "source pad elements", GST_ELEMENT_NAME (queue)); - } - } - - /* OK, we've got a serious issue here. Imagine the situation - * where the puller (next element) is sending an event here, - * so it cannot pull events from the queue, and we cannot - * push data further because the queue is 'full' and therefore, - * we wait here (and do not handle events): deadlock! to solve - * that, we handle pending upstream events here, too. */ - gst_queue_handle_pending_events (queue); - - STATUS (queue, "waiting for item_del signal from thread using qlock"); - g_cond_wait (queue->item_del, queue->qlock); STATUS (queue, "received item_del signal from thread using qlock"); } @@ -720,119 +646,84 @@ restart: break; } } + /* we are flushing */ + if (GST_RPAD_IS_FLUSHING (pad)) + goto out_flushing; - /* put the buffer on the tail of the list. We keep a reference, - * so that the data is read-only while in here. There's a good - * reason to do so: we have a size and time counter, and any - * modification to the content could change any of the two. */ - gst_data_ref (data); - g_queue_push_tail (queue->queue, data); + g_queue_push_tail (queue->queue, buffer); - /* Note that we only add buffers (not events) to the statistics */ - if (GST_IS_BUFFER (data)) { - queue->cur_level.buffers++; - queue->cur_level.bytes += GST_BUFFER_SIZE (data); - if (GST_BUFFER_DURATION (data) != GST_CLOCK_TIME_NONE) - queue->cur_level.time += GST_BUFFER_DURATION (data); - } + /* add buffer to the statistics */ + queue->cur_level.buffers++; + queue->cur_level.bytes += GST_BUFFER_SIZE (buffer); + if (GST_BUFFER_DURATION (buffer) != GST_CLOCK_TIME_NONE) + queue->cur_level.time += GST_BUFFER_DURATION (buffer); STATUS (queue, "+ level"); GST_CAT_LOG_OBJECT (queue_dataflow, queue, "signalling item_add"); g_cond_signal (queue->item_add); GST_QUEUE_MUTEX_UNLOCK; + GST_STREAM_UNLOCK (pad); - return; + return GST_FLOW_OK; out_unref: - gst_data_unref (data); - return; + GST_QUEUE_MUTEX_UNLOCK; + GST_STREAM_UNLOCK (pad); + + gst_buffer_unref (buffer); + + return GST_FLOW_OK; + +out_flushing: + GST_CAT_LOG_OBJECT (queue_dataflow, queue, "exit because of flush"); + GST_QUEUE_MUTEX_UNLOCK; + gst_task_pause (GST_RPAD_TASK (queue->srcpad)); + GST_STREAM_UNLOCK (pad); + + gst_buffer_unref (buffer); + + return GST_FLOW_UNEXPECTED; } -static GstData * -gst_queue_get (GstPad * pad) +static void +gst_queue_loop (GstPad * pad) { GstQueue *queue; GstData *data; + gboolean restart = TRUE; - g_return_val_if_fail (pad != NULL, NULL); - g_return_val_if_fail (GST_IS_PAD (pad), NULL); + queue = GST_QUEUE (GST_PAD_PARENT (pad)); - queue = GST_QUEUE (gst_pad_get_parent (pad)); + GST_STREAM_LOCK (pad); -restart: /* have to lock for thread-safety */ GST_QUEUE_MUTEX_LOCK; - if (queue->queue->length == 0 || - (queue->min_threshold.buffers > 0 && - queue->cur_level.buffers < queue->min_threshold.buffers) || - (queue->min_threshold.bytes > 0 && - queue->cur_level.bytes < queue->min_threshold.bytes) || - (queue->min_threshold.time > 0 && - queue->cur_level.time < queue->min_threshold.time)) { +restart: + while (gst_queue_is_empty (queue)) { GST_QUEUE_MUTEX_UNLOCK; g_signal_emit (G_OBJECT (queue), gst_queue_signals[SIGNAL_UNDERRUN], 0); GST_QUEUE_MUTEX_LOCK; STATUS (queue, "pre-empty wait"); - while (queue->queue->length == 0 || - (queue->min_threshold.buffers > 0 && - queue->cur_level.buffers < queue->min_threshold.buffers) || - (queue->min_threshold.bytes > 0 && - queue->cur_level.bytes < queue->min_threshold.bytes) || - (queue->min_threshold.time > 0 && - queue->cur_level.time < queue->min_threshold.time)) { - /* if there's a pending state change for this queue or its - * manager, switch back to iterator so bottom half of state - * change executes. */ - if (queue->interrupt) { - GstScheduler *sched; - - GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, "interrupted"); - queue->interrupt = FALSE; - GST_QUEUE_MUTEX_UNLOCK; - sched = gst_pad_get_scheduler (queue->srcpad); - if (!sched || gst_scheduler_interrupt (sched, GST_ELEMENT (queue))) - return GST_DATA (gst_event_new (GST_EVENT_INTERRUPT)); - goto restart; - } - if (GST_STATE (queue) != GST_STATE_PLAYING) { - /* this means the other end is shut down */ - if (!queue->may_deadlock) { - GST_QUEUE_MUTEX_UNLOCK; - GST_ELEMENT_ERROR (queue, CORE, THREAD, (NULL), - ("deadlock found, shutting down sink pad elements")); - goto restart; - } else { - GST_CAT_WARNING_OBJECT (queue_dataflow, queue, - "%s: waiting for the app to restart " - "source pad elements", GST_ELEMENT_NAME (queue)); - } - } - + while (gst_queue_is_empty (queue)) { STATUS (queue, "waiting for item_add"); - if (queue->block_timeout != GST_CLOCK_TIME_NONE) { - GTimeVal timeout; + /* we are flushing */ + if (GST_RPAD_IS_FLUSHING (queue->sinkpad)) + goto out_flushing; - g_get_current_time (&timeout); - g_time_val_add (&timeout, queue->block_timeout / 1000); - GST_LOG_OBJECT (queue, "g_cond_time_wait using qlock from thread %p", - g_thread_self ()); - if (!g_cond_timed_wait (queue->item_add, queue->qlock, &timeout)) { - GST_QUEUE_MUTEX_UNLOCK; - GST_CAT_WARNING_OBJECT (queue_dataflow, queue, - "Sending filler event"); - return GST_DATA (gst_event_new_filler ()); - } - } else { - GST_LOG_OBJECT (queue, "doing g_cond_wait using qlock from thread %p", - g_thread_self ()); - g_cond_wait (queue->item_add, queue->qlock); - GST_LOG_OBJECT (queue, "done g_cond_wait using qlock from thread %p", - g_thread_self ()); - } + GST_LOG_OBJECT (queue, "doing g_cond_wait using qlock from thread %p", + g_thread_self ()); + g_cond_wait (queue->item_add, queue->qlock); + + /* we got unlocked because we are flushing */ + if (GST_RPAD_IS_FLUSHING (queue->sinkpad)) + goto out_flushing; + + GST_LOG_OBJECT (queue, "done g_cond_wait using qlock from thread %p", + g_thread_self ()); STATUS (queue, "got item_add signal"); } @@ -847,120 +738,73 @@ restart: GST_CAT_LOG_OBJECT (queue_dataflow, queue, "retrieved data %p from queue", data); - if (data == NULL) - return NULL; - if (GST_IS_BUFFER (data)) { + GstFlowReturn result; + /* Update statistics */ queue->cur_level.buffers--; queue->cur_level.bytes -= GST_BUFFER_SIZE (data); if (GST_BUFFER_DURATION (data) != GST_CLOCK_TIME_NONE) queue->cur_level.time -= GST_BUFFER_DURATION (data); - } - /* Now that we're done, we can lose our own reference to - * the item, since we're no longer in danger. */ - gst_data_unref (data); + GST_QUEUE_MUTEX_UNLOCK; + result = gst_pad_push (pad, GST_BUFFER (data)); + GST_QUEUE_MUTEX_LOCK; + if (result != GST_FLOW_OK) { + gst_task_pause (GST_RPAD_TASK (queue->srcpad)); + } + } else { + if (GST_EVENT_TYPE (data) == GST_EVENT_EOS) { + gst_task_pause (GST_RPAD_TASK (queue->srcpad)); + restart = FALSE; + } + GST_QUEUE_MUTEX_UNLOCK; + gst_pad_push_event (queue->srcpad, GST_EVENT (data)); + GST_QUEUE_MUTEX_LOCK; + if (restart == TRUE) + goto restart; + } STATUS (queue, "after _get()"); GST_CAT_LOG_OBJECT (queue_dataflow, queue, "signalling item_del"); g_cond_signal (queue->item_del); GST_QUEUE_MUTEX_UNLOCK; + GST_STREAM_UNLOCK (pad); + return; - /* FIXME: I suppose this needs to be locked, since the EOS - * bit affects the pipeline state. However, that bit is - * locked too so it'd cause a deadlock. */ - if (GST_IS_EVENT (data)) { - GstEvent *event = GST_EVENT (data); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_EOS: - GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, - "queue \"%s\" eos", GST_ELEMENT_NAME (queue)); - gst_element_set_eos (GST_ELEMENT (queue)); - break; - default: - break; - } - } - - return data; +out_flushing: + GST_CAT_LOG_OBJECT (queue_dataflow, queue, "exit because of flush"); + gst_task_pause (GST_RPAD_TASK (pad)); + GST_QUEUE_MUTEX_UNLOCK; + GST_STREAM_UNLOCK (pad); + return; } static gboolean gst_queue_handle_src_event (GstPad * pad, GstEvent * event) { - GstQueue *queue = GST_QUEUE (gst_pad_get_parent (pad)); - gboolean res; + GstQueue *queue = GST_QUEUE (GST_PAD_PARENT (pad)); + gboolean res = TRUE; GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, "got event %p (%d)", event, GST_EVENT_TYPE (event)); + + gst_event_ref (event); + res = gst_pad_event_default (pad, event); GST_QUEUE_MUTEX_LOCK; - if (gst_element_get_state (GST_ELEMENT (queue)) == GST_STATE_PLAYING) { - GstQueueEventResponse er; - - /* push the event to the queue and wait for upstream consumption */ - er.event = event; - er.handled = FALSE; - g_mutex_lock (queue->event_lock); - GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, - "putting event %p (%d) on internal queue", event, - GST_EVENT_TYPE (event)); - g_queue_push_tail (queue->events, &er); - g_mutex_unlock (queue->event_lock); - GST_CAT_WARNING_OBJECT (queue_dataflow, queue, - "Preparing for loop for event handler"); - /* see the chain function on why this is here - it prevents a deadlock */ - g_cond_signal (queue->item_del); - while (!er.handled) { - GTimeVal timeout; - - g_get_current_time (&timeout); - g_time_val_add (&timeout, 500 * 1000); /* half a second */ - GST_LOG_OBJECT (queue, "doing g_cond_wait using qlock from thread %p", - g_thread_self ()); - if (!g_cond_timed_wait (queue->event_done, queue->qlock, &timeout) && - !er.handled) { - GST_CAT_WARNING_OBJECT (queue_dataflow, queue, - "timeout in upstream event handling, dropping event %p (%d)", - er.event, GST_EVENT_TYPE (er.event)); - g_mutex_lock (queue->event_lock); - /* since this queue is for src events (ie upstream), this thread is - * the only one that is pushing stuff on it, so we're sure that - * it's still the tail element. FIXME: But in practice, we should use - * GList instead of GQueue for this so we can remove any element in - * the list. */ - g_queue_pop_tail (queue->events); - g_mutex_unlock (queue->event_lock); - gst_event_unref (er.event); - res = FALSE; - goto handled; - } - } - GST_CAT_WARNING_OBJECT (queue_dataflow, queue, "Event handled"); - res = er.ret; - } else { - res = gst_pad_event_default (pad, event); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_FLUSH: - GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, - "FLUSH event, flushing queue\n"); + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK: + if (GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH) { gst_queue_locked_flush (queue); - break; - case GST_EVENT_SEEK: - if (GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH) { - gst_queue_locked_flush (queue); - } - default: - break; - } + } + default: + break; } -handled: GST_QUEUE_MUTEX_UNLOCK; + gst_event_unref (event); return res; } @@ -969,13 +813,11 @@ static gboolean gst_queue_handle_src_query (GstPad * pad, GstQueryType type, GstFormat * fmt, gint64 * value) { - GstQueue *queue = GST_QUEUE (gst_pad_get_parent (pad)); - gboolean res; + GstQueue *queue = GST_QUEUE (GST_PAD_PARENT (pad)); if (!GST_PAD_PEER (queue->sinkpad)) return FALSE; - res = gst_pad_query (GST_PAD_PEER (queue->sinkpad), type, fmt, value); - if (!res) + if (!gst_pad_query (GST_PAD_PEER (queue->sinkpad), type, fmt, value)) return FALSE; if (type == GST_QUERY_POSITION) { @@ -997,21 +839,44 @@ gst_queue_handle_src_query (GstPad * pad, } static gboolean -gst_queue_release_locks (GstElement * element) +gst_queue_src_activate (GstPad * pad, GstActivateMode mode) { + gboolean result = FALSE; GstQueue *queue; - queue = GST_QUEUE (element); + queue = GST_QUEUE (GST_OBJECT_PARENT (pad)); - GST_QUEUE_MUTEX_LOCK; - queue->interrupt = TRUE; - g_cond_signal (queue->item_add); - g_cond_signal (queue->item_del); - GST_QUEUE_MUTEX_UNLOCK; + if (mode == GST_ACTIVATE_PUSH) { + /* if we have a scheduler we can start the task */ + if (GST_ELEMENT_SCHEDULER (queue)) { + GST_STREAM_LOCK (pad); + GST_RPAD_TASK (pad) = + gst_scheduler_create_task (GST_ELEMENT_SCHEDULER (queue), + (GstTaskFunction) gst_queue_loop, pad); - return TRUE; + gst_task_start (GST_RPAD_TASK (pad)); + GST_STREAM_UNLOCK (pad); + result = TRUE; + } + } else { + /* step 1, unblock chain and loop functions */ + queue->interrupt = TRUE; + g_cond_signal (queue->item_add); + g_cond_signal (queue->item_del); + + /* step 2, make sure streaming finishes */ + GST_STREAM_LOCK (pad); + /* step 3, stop the task */ + gst_task_stop (GST_RPAD_TASK (pad)); + gst_object_unref (GST_OBJECT (GST_RPAD_TASK (pad))); + GST_STREAM_UNLOCK (pad); + + result = TRUE; + } + return result; } + static GstElementStateReturn gst_queue_change_state (GstElement * element) { @@ -1020,71 +885,38 @@ gst_queue_change_state (GstElement * element) queue = GST_QUEUE (element); - GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, - "starting state change 0x%x", GST_STATE_TRANSITION (element)); + GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "starting state change"); /* lock the queue so another thread (not in sync with this thread's state) - * can't call this queue's _get (or whatever) - */ + * can't call this queue's _loop (or whatever) */ GST_QUEUE_MUTEX_LOCK; switch (GST_STATE_TRANSITION (element)) { case GST_STATE_NULL_TO_READY: gst_queue_locked_flush (queue); break; - case GST_STATE_PAUSED_TO_PLAYING: - if (!GST_PAD_IS_LINKED (queue->sinkpad)) { - GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, queue, - "queue %s is not linked", GST_ELEMENT_NAME (queue)); - /* FIXME can this be? */ - g_cond_signal (queue->item_add); - - ret = GST_STATE_FAILURE; - goto unlock; - } else { - GstScheduler *src_sched, *sink_sched; - - src_sched = gst_pad_get_scheduler (GST_PAD (queue->srcpad)); - sink_sched = gst_pad_get_scheduler (GST_PAD (queue->sinkpad)); - - if (src_sched == sink_sched) { - GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, queue, - "queue %s does not connect different schedulers", - GST_ELEMENT_NAME (queue)); - - g_warning ("queue %s does not connect different schedulers", - GST_ELEMENT_NAME (queue)); - - ret = GST_STATE_FAILURE; - goto unlock; - } - } - queue->interrupt = FALSE; + case GST_STATE_READY_TO_PAUSED: break; - case GST_STATE_PAUSED_TO_READY: - gst_queue_locked_flush (queue); - gst_caps_replace (&queue->negotiated_caps, NULL); + case GST_STATE_PAUSED_TO_PLAYING: + queue->interrupt = FALSE; break; default: break; } - GST_QUEUE_MUTEX_UNLOCK; + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element); - if (GST_ELEMENT_CLASS (parent_class)->change_state) - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element); - - /* this is an ugly hack to make sure our pads are always active. - * Reason for this is that pad activation for the queue element - * depends on 2 schedulers (ugh) */ - gst_pad_set_active (queue->sinkpad, TRUE); - gst_pad_set_active (queue->srcpad, TRUE); - - GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "done with state change"); - - return ret; - -unlock: + switch (GST_STATE_TRANSITION (element)) { + case GST_STATE_PLAYING_TO_PAUSED: + break; + case GST_STATE_PAUSED_TO_READY: + gst_queue_locked_flush (queue); + break; + case GST_STATE_READY_TO_NULL: + break; + default: + break; + } GST_QUEUE_MUTEX_UNLOCK; GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "done with state change"); @@ -1092,7 +924,6 @@ unlock: return ret; } - static void gst_queue_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) diff --git a/gst/gstqueue.h b/gst/gstqueue.h index e473f404ef..9ff1c86805 100644 --- a/gst/gstqueue.h +++ b/gst/gstqueue.h @@ -27,7 +27,6 @@ #include - G_BEGIN_DECLS #define GST_TYPE_QUEUE \ @@ -80,23 +79,14 @@ struct _GstQueue { /* it the queue should fail on possible deadlocks */ gboolean may_deadlock; - gboolean interrupt; gboolean flush; GMutex *qlock; /* lock for queue (vs object lock) */ GCond *item_add; /* signals buffers now available for reading */ GCond *item_del; /* signals space now available for writing */ - GCond *event_done; /* upstream event signaller */ - GTimeVal *timeval; /* the timeout for the queue locking */ - GQueue *events; /* upstream events get decoupled here */ - - GstCaps *negotiated_caps; - - GMutex *event_lock; /* lock when handling the events queue */ - - gpointer _gst_reserved[GST_PADDING - 1]; + gpointer _gst_reserved[GST_PADDING]; }; struct _GstQueueClass { diff --git a/gst/gstscheduler.c b/gst/gstscheduler.c index fe0470e2f3..4b00e04684 100644 --- a/gst/gstscheduler.c +++ b/gst/gstscheduler.c @@ -76,13 +76,7 @@ gst_scheduler_class_init (GstSchedulerClass * klass) static void gst_scheduler_init (GstScheduler * sched) { - sched->clock_providers = NULL; - sched->clock_receivers = NULL; - sched->schedulers = NULL; - sched->state = GST_SCHEDULER_STATE_NONE; sched->parent = NULL; - sched->parent_sched = NULL; - sched->clock = NULL; } static void @@ -90,21 +84,7 @@ gst_scheduler_dispose (GObject * object) { GstScheduler *sched = GST_SCHEDULER (object); - /* thse lists should all be NULL */ - GST_DEBUG ("scheduler %p dispose %p %p %p", - object, - sched->clock_providers, sched->clock_receivers, sched->schedulers); - - gst_object_replace ((GstObject **) & sched->current_clock, NULL); - gst_object_replace ((GstObject **) & sched->clock, NULL); - - /* kids are held reference to, so dereference here. */ - while (sched->schedulers != NULL) { - gst_scheduler_remove_scheduler (sched, - GST_SCHEDULER (sched->schedulers->data)); - } - - G_OBJECT_CLASS (parent_class)->dispose (object); + G_OBJECT_CLASS (parent_class)->dispose (G_OBJECT (sched)); } /** @@ -145,626 +125,21 @@ gst_scheduler_reset (GstScheduler * sched) sclass->reset (sched); } -/** - * gst_scheduler_pad_link: - * @sched: the scheduler - * @srcpad: the srcpad to link - * @sinkpad: the sinkpad to link to - * - * Links the srcpad to the given sinkpad. - */ -void -gst_scheduler_pad_link (GstScheduler * sched, GstPad * srcpad, GstPad * sinkpad) +GstTask * +gst_scheduler_create_task (GstScheduler * sched, GstTaskFunction func, + gpointer data) { GstSchedulerClass *sclass; + GstTask *result = NULL; - g_return_if_fail (GST_IS_SCHEDULER (sched)); - g_return_if_fail (GST_IS_PAD (srcpad)); - g_return_if_fail (GST_IS_PAD (sinkpad)); + g_return_val_if_fail (GST_IS_SCHEDULER (sched), result); sclass = GST_SCHEDULER_GET_CLASS (sched); - if (sclass->pad_link) - sclass->pad_link (sched, srcpad, sinkpad); -} + if (sclass->create_task) + result = sclass->create_task (sched, func, data); -/** - * gst_scheduler_pad_unlink: - * @sched: the scheduler - * @srcpad: the srcpad to unlink - * @sinkpad: the sinkpad to unlink from - * - * Unlinks the srcpad from the given sinkpad. - */ -void -gst_scheduler_pad_unlink (GstScheduler * sched, GstPad * srcpad, - GstPad * sinkpad) -{ - GstSchedulerClass *sclass; - - g_return_if_fail (GST_IS_SCHEDULER (sched)); - g_return_if_fail (GST_IS_PAD (srcpad)); - g_return_if_fail (GST_IS_PAD (sinkpad)); - - sclass = GST_SCHEDULER_GET_CLASS (sched); - - if (sclass->pad_unlink) - sclass->pad_unlink (sched, srcpad, sinkpad); -} - -/** - * gst_scheduler_pad_select: - * @sched: the scheduler - * @padlist: the padlist to select on - * - * register the given padlist for a select operation. - * - * Returns: the pad which received a buffer. - */ -GstPad * -gst_scheduler_pad_select (GstScheduler * sched, GList * padlist) -{ - g_return_val_if_fail (GST_IS_SCHEDULER (sched), NULL); - g_return_val_if_fail (padlist != NULL, NULL); - - return NULL; -} - -/** - * 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; - gboolean redistribute_clock = FALSE; - - g_return_if_fail (GST_IS_SCHEDULER (sched)); - g_return_if_fail (GST_IS_ELEMENT (element)); - - /* if it's already in this scheduler, don't bother doing anything */ - if (GST_ELEMENT_SCHEDULER (element) == sched) { - GST_CAT_DEBUG (GST_CAT_SCHEDULING, "element %s already in scheduler %p", - GST_ELEMENT_NAME (element), sched); - return; - } - - /* if it's not inside this scheduler, it has to be NULL */ - g_assert (GST_ELEMENT_SCHEDULER (element) == NULL); - - if (gst_element_provides_clock (element)) { - sched->clock_providers = g_list_prepend (sched->clock_providers, element); - GST_CAT_DEBUG (GST_CAT_CLOCK, "added clock provider %s", - GST_ELEMENT_NAME (element)); - redistribute_clock = TRUE; - } - if (gst_element_requires_clock (element)) { - sched->clock_receivers = g_list_prepend (sched->clock_receivers, element); - GST_CAT_DEBUG (GST_CAT_CLOCK, "added clock receiver %s", - GST_ELEMENT_NAME (element)); - redistribute_clock = TRUE; - } - - gst_element_set_scheduler (element, sched); - - if (redistribute_clock) { - GstClock *clock; - - clock = gst_scheduler_get_clock (sched); - gst_scheduler_set_clock (sched, clock); - } - - 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; - GList *link; - gboolean redistribute_clock = FALSE; - - g_return_if_fail (GST_IS_SCHEDULER (sched)); - g_return_if_fail (GST_IS_ELEMENT (element)); - - link = g_list_find (sched->clock_providers, element); - if (link) { - sched->clock_providers = g_list_delete_link (sched->clock_providers, link); - GST_CAT_DEBUG (GST_CAT_CLOCK, "removed clock provider %s", - GST_ELEMENT_NAME (element)); - redistribute_clock = TRUE; - } - link = g_list_find (sched->clock_receivers, element); - if (link) { - sched->clock_receivers = g_list_delete_link (sched->clock_receivers, link); - GST_CAT_DEBUG (GST_CAT_CLOCK, "removed clock receiver %s", - GST_ELEMENT_NAME (element)); - redistribute_clock = TRUE; - } - - if (redistribute_clock) { - GstClock *clock; - - clock = gst_scheduler_get_clock (sched); - gst_scheduler_set_clock (sched, clock); - } - - sclass = GST_SCHEDULER_GET_CLASS (sched); - - if (sclass->remove_element) - sclass->remove_element (sched, element); - - gst_element_set_scheduler (element, NULL); -} - -/** - * gst_scheduler_state_transition: - * @sched: the scheduler - * @element: the element with the state transition - * @transition: the state transition - * - * Tell the scheduler that an element changed its state. - * - * Returns: a GstElementStateReturn indicating success or failure - * of the state transition. - */ -GstElementStateReturn -gst_scheduler_state_transition (GstScheduler * sched, GstElement * element, - gint transition) -{ - GstSchedulerClass *sclass; - - g_return_val_if_fail (GST_IS_SCHEDULER (sched), GST_STATE_FAILURE); - g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_FAILURE); - - if (element == sched->parent && sched->parent_sched == NULL) { - /* FIXME is distributing the clock in the state change still needed - * when we distribute as soon as we add/remove elements? I think not.*/ - switch (transition) { - case GST_STATE_READY_TO_PAUSED: - { - GstClock *clock = gst_scheduler_get_clock (sched); - - GST_CAT_DEBUG (GST_CAT_CLOCK, - "scheduler READY to PAUSED clock is %p (%s)", clock, - (clock ? GST_OBJECT_NAME (clock) : "nil")); - - gst_scheduler_set_clock (sched, clock); - break; - } - } - } - - sclass = GST_SCHEDULER_GET_CLASS (sched); - - if (sclass->state_transition) - return sclass->state_transition (sched, element, transition); - - return GST_STATE_SUCCESS; -} - -/** - * gst_scheduler_scheduling_change: - * @sched: the scheduler - * @element: the element that changed its scheduling strategy - * - * Tell the scheduler that an element changed its scheduling strategy. - * An element could, for example, change its loop function or changes - * from a loop based element to a chain based element. - */ -void -gst_scheduler_scheduling_change (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->scheduling_change) - sclass->scheduling_change (sched, element); -} - -/** - * gst_scheduler_add_scheduler: - * @sched: a #GstScheduler to add to - * @sched2: the #GstScheduler to add - * - * Notifies the scheduler that it has to monitor this scheduler. - */ -void -gst_scheduler_add_scheduler (GstScheduler * sched, GstScheduler * sched2) -{ - GstSchedulerClass *sclass; - - g_return_if_fail (GST_IS_SCHEDULER (sched)); - g_return_if_fail (GST_IS_SCHEDULER (sched2)); - g_return_if_fail (sched2->parent_sched == NULL); - - GST_DEBUG ("gstscheduler: %p add scheduler %p", sched, sched2); - - gst_object_ref (GST_OBJECT (sched2)); - gst_object_ref (GST_OBJECT (sched)); - - sched->schedulers = g_list_prepend (sched->schedulers, sched2); - sched2->parent_sched = sched; - - sclass = GST_SCHEDULER_GET_CLASS (sched); - - if (sclass->add_scheduler) - sclass->add_scheduler (sched, sched2); -} - -/** - * gst_scheduler_remove_scheduler: - * @sched: the scheduler - * @sched2: the scheduler to remove - * - a Notifies the scheduler that it can stop monitoring this scheduler. - */ -void -gst_scheduler_remove_scheduler (GstScheduler * sched, GstScheduler * sched2) -{ - GstSchedulerClass *sclass; - - g_return_if_fail (GST_IS_SCHEDULER (sched)); - g_return_if_fail (GST_IS_SCHEDULER (sched2)); - g_return_if_fail (sched2->parent_sched == sched); - - GST_DEBUG ("gstscheduler: %p remove scheduler %p", sched, sched2); - - sclass = GST_SCHEDULER_GET_CLASS (sched); - - if (sclass->remove_scheduler) - sclass->remove_scheduler (sched, sched2); - - sched->schedulers = g_list_remove (sched->schedulers, sched2); - sched2->parent_sched = NULL; - - gst_object_unref (GST_OBJECT (sched2)); - gst_object_unref (GST_OBJECT (sched)); -} - -/** - * gst_scheduler_lock_element: - * @sched: the scheduler - * @element: the element to lock - * - * Acquire a lock on the given element in the given scheduler. - */ -void -gst_scheduler_lock_element (GstScheduler * sched, GstElement * element) -{ - g_return_if_fail (GST_IS_SCHEDULER (sched)); - g_return_if_fail (GST_IS_ELEMENT (element)); -} - -/** - * gst_scheduler_unlock_element: - * @sched: the scheduler - * @element: the element to unlock - * - * Release the lock on the given element in the given scheduler. - */ -void -gst_scheduler_unlock_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); -} - -/** - * gst_scheduler_error: - * @sched: the scheduler - * @element: the element with the error - * - * Tell the scheduler an element was in error - */ -void -gst_scheduler_error (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->error) - sclass->error (sched, element); -} - -/** - * gst_scheduler_yield: - * @sched: the scheduler - * @element: the element requesting a yield - * - * Tell the scheduler to schedule another element. - * - * Returns: TRUE if the element should save its state, FALSE - * if the scheduler can perform this action itself. - */ -gboolean -gst_scheduler_yield (GstScheduler * sched, GstElement * element) -{ - GstSchedulerClass *sclass; - - g_return_val_if_fail (GST_IS_SCHEDULER (sched), TRUE); - g_return_val_if_fail (GST_IS_ELEMENT (element), TRUE); - - sclass = GST_SCHEDULER_GET_CLASS (sched); - - if (sclass->yield) - return sclass->yield (sched, element); - - return TRUE; -} - -/** - * gst_scheduler_interrupt: - * @sched: the scheduler - * @element: the element requesting an interrupt - * - * Tell the scheduler to interrupt execution of this element. - * - * Returns: TRUE if the element should return NULL from the chain/get - * function. - */ -gboolean -gst_scheduler_interrupt (GstScheduler * sched, GstElement * element) -{ - GstSchedulerClass *sclass; - - g_return_val_if_fail (GST_IS_SCHEDULER (sched), FALSE); - g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE); - - sclass = GST_SCHEDULER_GET_CLASS (sched); - - if (sclass->interrupt) - return sclass->interrupt (sched, element); - - return FALSE; -} - -/** - * gst_scheduler_get_clock: - * @sched: the scheduler - * - * Gets the current clock used by the scheduler. - * - * Returns: a GstClock - */ -GstClock * -gst_scheduler_get_clock (GstScheduler * sched) -{ - GstClock *clock = NULL; - - /* if we have a fixed clock, use that one */ - if (GST_FLAG_IS_SET (sched, GST_SCHEDULER_FLAG_FIXED_CLOCK)) { - clock = sched->clock; - - GST_CAT_DEBUG (GST_CAT_CLOCK, "scheduler using fixed clock %p (%s)", - clock, clock ? GST_STR_NULL (GST_OBJECT_NAME (clock)) : "-"); - } else { - GList *schedulers = sched->schedulers; - GList *providers = sched->clock_providers; - - /* try to get a clock from one of the schedulers we manage first */ - while (schedulers) { - GstScheduler *scheduler = GST_SCHEDULER (schedulers->data); - - clock = gst_scheduler_get_clock (scheduler); - if (clock) { - GST_CAT_DEBUG (GST_CAT_CLOCK, - "scheduler found managed sched clock %p (%s)", - clock, clock ? GST_STR_NULL (GST_OBJECT_NAME (clock)) : "-"); - break; - } - - schedulers = g_list_next (schedulers); - } - /* still no clock, try to find one in the providers */ - while (!clock && providers) { - clock = gst_element_get_clock (GST_ELEMENT (providers->data)); - if (clock) - GST_CAT_DEBUG (GST_CAT_CLOCK, "scheduler found provider clock: %p (%s)", - clock, clock ? GST_STR_NULL (GST_OBJECT_NAME (clock)) : "-"); - providers = g_list_next (providers); - } - /* still no clock, use a system clock */ - if (!clock && sched->parent_sched == NULL) { - clock = gst_system_clock_obtain (); - /* we unref since this function is not supposed to increase refcount - * of clock object returned; this is ok since the systemclock always - * has a refcount of at least one in the current code. */ - gst_object_unref (GST_OBJECT (clock)); - GST_CAT_DEBUG (GST_CAT_CLOCK, "scheduler obtained system clock: %p (%s)", - clock, clock ? GST_STR_NULL (GST_OBJECT_NAME (clock)) : "-"); - } - } - - return clock; -} - -/** - * gst_scheduler_use_clock: - * @sched: the scheduler - * @clock: the clock to use - * - * Force the scheduler to use the given clock. The scheduler will - * always use the given clock even if new clock providers are added - * to this scheduler. - */ -void -gst_scheduler_use_clock (GstScheduler * sched, GstClock * clock) -{ - g_return_if_fail (sched != NULL); - g_return_if_fail (GST_IS_SCHEDULER (sched)); - - GST_FLAG_SET (sched, GST_SCHEDULER_FLAG_FIXED_CLOCK); - - gst_object_replace ((GstObject **) & sched->clock, (GstObject *) clock); - - GST_CAT_DEBUG (GST_CAT_CLOCK, "scheduler using fixed clock %p (%s)", clock, - (clock ? GST_OBJECT_NAME (clock) : "nil")); -} - -/** - * gst_scheduler_set_clock: - * @sched: the scheduler - * @clock: the clock to set - * - * Set the clock for the scheduler. The clock will be distributed - * to all the elements managed by the scheduler. - */ -void -gst_scheduler_set_clock (GstScheduler * sched, GstClock * clock) -{ - GList *receivers; - GList *schedulers; - - g_return_if_fail (sched != NULL); - g_return_if_fail (GST_IS_SCHEDULER (sched)); - - receivers = sched->clock_receivers; - schedulers = sched->schedulers; - - gst_object_replace ((GstObject **) & sched->current_clock, - (GstObject *) clock); - - while (receivers) { - GstElement *element = GST_ELEMENT (receivers->data); - - GST_CAT_DEBUG (GST_CAT_CLOCK, - "scheduler setting clock %p (%s) on element %s", clock, - (clock ? GST_OBJECT_NAME (clock) : "nil"), GST_ELEMENT_NAME (element)); - - gst_element_set_clock (element, clock); - receivers = g_list_next (receivers); - } - while (schedulers) { - GstScheduler *scheduler = GST_SCHEDULER (schedulers->data); - - GST_CAT_DEBUG (GST_CAT_CLOCK, - "scheduler setting clock %p (%s) on scheduler %p", clock, - (clock ? GST_OBJECT_NAME (clock) : "nil"), scheduler); - gst_scheduler_set_clock (scheduler, clock); - schedulers = g_list_next (schedulers); - } -} - -/** - * gst_scheduler_auto_clock: - * @sched: the scheduler - * - * Let the scheduler select a clock automatically. - */ -void -gst_scheduler_auto_clock (GstScheduler * sched) -{ - g_return_if_fail (sched != NULL); - g_return_if_fail (GST_IS_SCHEDULER (sched)); - - GST_FLAG_UNSET (sched, GST_SCHEDULER_FLAG_FIXED_CLOCK); - - gst_object_replace ((GstObject **) & sched->clock, NULL); - - GST_CAT_DEBUG (GST_CAT_CLOCK, "scheduler using automatic clock"); -} - -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_iterate: - * @sched: the scheduler - * - * Perform one iteration on the scheduler. - * - * Returns: a boolean indicating something usefull has happened. - */ -gboolean -gst_scheduler_iterate (GstScheduler * sched) -{ - GstSchedulerClass *sclass; - gboolean res = FALSE; - - g_return_val_if_fail (GST_IS_SCHEDULER (sched), FALSE); - - sclass = GST_SCHEDULER_GET_CLASS (sched); - - if (sclass->iterate) { - res = sclass->iterate (sched); - } - - return res; -} - - -/** - * 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); + return result; } /* @@ -970,8 +345,6 @@ gst_scheduler_factory_create (GstSchedulerFactory * factory, sched = GST_SCHEDULER (g_object_new (factory->type, NULL)); sched->parent = parent; - GST_ELEMENT_SCHEDULER (parent) = sched; - /* let's refcount the scheduler */ gst_object_ref (GST_OBJECT (sched)); gst_object_sink (GST_OBJECT (sched)); diff --git a/gst/gstscheduler.h b/gst/gstscheduler.h index 9cca5ad1d6..d6e6102338 100644 --- a/gst/gstscheduler.h +++ b/gst/gstscheduler.h @@ -27,6 +27,7 @@ #include #include #include +#include G_BEGIN_DECLS @@ -38,11 +39,6 @@ G_BEGIN_DECLS #define GST_SCHEDULER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_SCHEDULER, GstSchedulerClass)) typedef enum { - /* this scheduler works with a fixed clock */ - GST_SCHEDULER_FLAG_FIXED_CLOCK = GST_OBJECT_FLAG_LAST, - /* this scheduler supports select and lock calls */ - GST_SCHEDULER_FLAG_NEW_API, - /* padding */ GST_SCHEDULER_FLAG_LAST = GST_OBJECT_FLAG_LAST + 4 } GstSchedulerFlags; @@ -52,27 +48,11 @@ typedef enum { /*typedef struct _GstScheduler GstScheduler; */ /*typedef struct _GstSchedulerClass GstSchedulerClass; */ -typedef enum { - GST_SCHEDULER_STATE_NONE, - GST_SCHEDULER_STATE_RUNNING, - GST_SCHEDULER_STATE_STOPPED, - GST_SCHEDULER_STATE_ERROR -} GstSchedulerState; struct _GstScheduler { GstObject object; GstElement *parent; - GstScheduler *parent_sched; - - GstSchedulerState state; - GstClock *clock; - GstClock *current_clock; - - GList *clock_providers; - GList *clock_receivers; - - GList *schedulers; gpointer _gst_reserved[GST_PADDING]; }; @@ -83,32 +63,8 @@ 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); - void (*add_scheduler) (GstScheduler *sched, GstScheduler *sched2); - void (*remove_scheduler) (GstScheduler *sched, GstScheduler *sched2); - GstElementStateReturn (*state_transition) (GstScheduler *sched, GstElement *element, gint transition); - void (*scheduling_change) (GstScheduler *sched, GstElement *element); - /* next two are optional, require NEW_API flag */ - /* FIXME 0.9: rename to (un)lock_object */ - void (*lock_element) (GstScheduler *sched, GstObject *object); - void (*unlock_element) (GstScheduler *sched, GstObject *object); - gboolean (*yield) (GstScheduler *sched, GstElement *element); - gboolean (*interrupt) (GstScheduler *sched, GstElement *element); - void (*error) (GstScheduler *sched, GstElement *element); - void (*pad_link) (GstScheduler *sched, GstPad *srcpad, GstPad *sinkpad); - void (*pad_unlink) (GstScheduler *sched, GstPad *srcpad, GstPad *sinkpad); - /* optional, requires NEW_API flag */ - GstData * (*pad_select) (GstScheduler *sched, GstPad **selected, GstPad **pads); - GstClockReturn (*clock_wait) (GstScheduler *sched, GstElement *element, - GstClockID id, GstClockTimeDiff *jitter); - GstSchedulerState (*iterate) (GstScheduler *sched); - /* for debugging */ - void (*show) (GstScheduler *sched); - /* signals */ - void (*object_sync) (GstScheduler *sched, GstClock *clock, GstObject *object, - GstClockID id); + GstTask* (*create_task) (GstScheduler *sched, GstTaskFunction func, gpointer data); gpointer _gst_reserved[GST_PADDING]; }; @@ -118,34 +74,8 @@ GType gst_scheduler_get_type (void); void gst_scheduler_setup (GstScheduler *sched); void gst_scheduler_reset (GstScheduler *sched); -void gst_scheduler_add_element (GstScheduler *sched, GstElement *element); -void gst_scheduler_remove_element (GstScheduler *sched, GstElement *element); -void gst_scheduler_add_scheduler (GstScheduler *sched, GstScheduler *sched2); -void gst_scheduler_remove_scheduler (GstScheduler *sched, GstScheduler *sched2); -GstElementStateReturn gst_scheduler_state_transition (GstScheduler *sched, GstElement *element, gint transition); -void gst_scheduler_scheduling_change (GstScheduler *sched, GstElement *element); -#ifndef GST_DISABLE_DEPRECATED -void gst_scheduler_lock_element (GstScheduler *sched, GstElement *element); -void gst_scheduler_unlock_element (GstScheduler *sched, GstElement *element); -#endif -gboolean gst_scheduler_yield (GstScheduler *sched, GstElement *element); -gboolean gst_scheduler_interrupt (GstScheduler *sched, GstElement *element); -void gst_scheduler_error (GstScheduler *sched, GstElement *element); -void gst_scheduler_pad_link (GstScheduler *sched, GstPad *srcpad, GstPad *sinkpad); -void gst_scheduler_pad_unlink (GstScheduler *sched, GstPad *srcpad, GstPad *sinkpad); -#ifndef GST_DISABLE_DEPRECATED -GstPad* gst_scheduler_pad_select (GstScheduler *sched, GList *padlist); -#endif -GstClockReturn gst_scheduler_clock_wait (GstScheduler *sched, GstElement *element, - GstClockID id, GstClockTimeDiff *jitter); -gboolean gst_scheduler_iterate (GstScheduler *sched); +GstTask* gst_scheduler_create_task (GstScheduler *sched, GstTaskFunction func, gpointer data); -void gst_scheduler_use_clock (GstScheduler *sched, GstClock *clock); -void gst_scheduler_set_clock (GstScheduler *sched, GstClock *clock); -GstClock* gst_scheduler_get_clock (GstScheduler *sched); -void gst_scheduler_auto_clock (GstScheduler *sched); - -void gst_scheduler_show (GstScheduler *sched); /* * creating schedulers @@ -159,7 +89,7 @@ void gst_scheduler_show (GstScheduler *sched); #define GST_SCHEDULER_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_SCHEDULER_FACTORY, GstSchedulerFactoryClass)) /* change this to change the default scheduler */ -#define GST_SCHEDULER_DEFAULT_NAME "opt" +#define GST_SCHEDULER_DEFAULT_NAME "thread" typedef struct _GstSchedulerFactory GstSchedulerFactory; typedef struct _GstSchedulerFactoryClass GstSchedulerFactoryClass; diff --git a/gst/gststructure.c b/gst/gststructure.c index abe1f4382f..cc3a590a53 100644 --- a/gst/gststructure.c +++ b/gst/gststructure.c @@ -61,7 +61,7 @@ static gboolean gst_structure_parse_simple_string (gchar * s, gchar ** end); GType gst_structure_get_type (void) { - static GType gst_structure_type; + static GType gst_structure_type = 0; if (!gst_structure_type) { gst_structure_type = g_boxed_type_register_static ("GstStructure", diff --git a/gst/gststructure.h b/gst/gststructure.h index 1fa05531e0..8cdb33f63a 100644 --- a/gst/gststructure.h +++ b/gst/gststructure.h @@ -54,7 +54,7 @@ struct _GstStructure { gpointer _gst_reserved[GST_PADDING]; }; -GType gst_structure_get_type (void) G_GNUC_CONST; +GType gst_structure_get_type (void); GstStructure * gst_structure_empty_new (const gchar * name); GstStructure * gst_structure_id_empty_new (GQuark quark); diff --git a/gst/gsttaginterface.h b/gst/gsttaginterface.h index f28f431c17..eaf494076d 100644 --- a/gst/gsttaginterface.h +++ b/gst/gsttaginterface.h @@ -46,7 +46,7 @@ struct _GstTagSetterIFace /* virtual table */ }; -GType gst_tag_setter_get_type (void) G_GNUC_CONST; +GType gst_tag_setter_get_type (void); void gst_tag_setter_merge (GstTagSetter * setter, const GstTagList * list, diff --git a/gst/gsttagsetter.h b/gst/gsttagsetter.h index f28f431c17..eaf494076d 100644 --- a/gst/gsttagsetter.h +++ b/gst/gsttagsetter.h @@ -46,7 +46,7 @@ struct _GstTagSetterIFace /* virtual table */ }; -GType gst_tag_setter_get_type (void) G_GNUC_CONST; +GType gst_tag_setter_get_type (void); void gst_tag_setter_merge (GstTagSetter * setter, const GstTagList * list, diff --git a/gst/gsttask.c b/gst/gsttask.c new file mode 100644 index 0000000000..31ea0f9e69 --- /dev/null +++ b/gst/gsttask.c @@ -0,0 +1,210 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen + * 2005 Wim Taymans + * + * gsttask.c: Streaming tasks + * + * 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 "gst_private.h" + +#include "gstinfo.h" +#include "gsttask.h" + +static void gst_task_class_init (GstTaskClass * klass); +static void gst_task_init (GstTask * task); +static void gst_task_dispose (GObject * object); + +static GstObjectClass *parent_class = NULL; + +GType +gst_task_get_type (void) +{ + static GType _gst_task_type = 0; + + if (!_gst_task_type) { + static const GTypeInfo task_info = { + sizeof (GstTaskClass), + NULL, + NULL, + (GClassInitFunc) gst_task_class_init, + NULL, + NULL, + sizeof (GstTask), + 0, + (GInstanceInitFunc) gst_task_init, + NULL + }; + + _gst_task_type = + g_type_register_static (GST_TYPE_OBJECT, "GstTask", + &task_info, G_TYPE_FLAG_ABSTRACT); + } + return _gst_task_type; +} + +static void +gst_task_class_init (GstTaskClass * klass) +{ + GObjectClass *gobject_class; + + gobject_class = (GObjectClass *) klass; + + parent_class = g_type_class_ref (GST_TYPE_OBJECT); + + gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_task_dispose); +} + +static void +gst_task_init (GstTask * task) +{ + task->cond = g_cond_new (); + task->state = GST_TASK_STOPPED; +} + +static void +gst_task_dispose (GObject * object) +{ + GstTask *task = GST_TASK (object); + + GST_DEBUG ("task %p dispose", task); + + g_cond_free (task->cond); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +/** + * gst_task_create: + * @func: The #GstTaskFunction to use + * @data: User data to pass to @func + * + * Create a new Task that will repeadedly call the provided @func + * with @data as a parameter. Typically the task will run in + * a new thread. + * + * Returns: A new #GstTask. + * + * MT safe. + */ +GstTask * +gst_task_create (GstTaskFunction func, gpointer data) +{ + return NULL; +} + +/** + * gst_task_get_state: + * @task: The #GstTask to query + * + * Get the current state of the task. + * + * Returns: The #GstTaskState of the task + * + * MT safe. + */ +GstTaskState +gst_task_get_state (GstTask * task) +{ + GstTaskState result; + + g_return_val_if_fail (GST_IS_TASK (task), GST_TASK_STOPPED); + + GST_LOCK (task); + result = task->state; + GST_UNLOCK (task); + + return result; +} + +/** + * gst_task_start: + * @task: The #GstTask to start + * + * Starts @task. + * + * Returns: TRUE if the task could be started. + * + * MT safe. + */ +gboolean +gst_task_start (GstTask * task) +{ + GstTaskClass *tclass; + gboolean result = FALSE; + + g_return_val_if_fail (GST_IS_TASK (task), FALSE); + + tclass = GST_TASK_GET_CLASS (task); + + if (tclass->start) + result = tclass->start (task); + + return result; +} + +/** + * gst_task_stop: + * @task: The #GstTask to stop + * + * Stops @task. + * + * Returns: TRUE if the task could be stopped. + * + * MT safe. + */ +gboolean +gst_task_stop (GstTask * task) +{ + GstTaskClass *tclass; + gboolean result = FALSE; + + g_return_val_if_fail (GST_IS_TASK (task), FALSE); + + tclass = GST_TASK_GET_CLASS (task); + + if (tclass->stop) + result = tclass->stop (task); + + return result; +} + +/** + * gst_task_pause: + * @task: The #GstTask to pause + * + * Pauses @task. + * + * Returns: TRUE if the task could be paused. + * + * MT safe. + */ +gboolean +gst_task_pause (GstTask * task) +{ + GstTaskClass *tclass; + gboolean result = FALSE; + + g_return_val_if_fail (GST_IS_TASK (task), FALSE); + + tclass = GST_TASK_GET_CLASS (task); + + if (tclass->pause) + result = tclass->pause (task); + + return result; +} diff --git a/gst/gsttask.h b/gst/gsttask.h new file mode 100644 index 0000000000..935a4c7acf --- /dev/null +++ b/gst/gsttask.h @@ -0,0 +1,96 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * <2005> Wim Taymans + * + * gsttask.h: Streaming tasks + * + * 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 __GST_TASK_H__ +#define __GST_TASK_H__ + +#include + +G_BEGIN_DECLS + +typedef void (*GstTaskFunction) (void *data); + +/* --- standard type macros --- */ +#define GST_TYPE_TASK (gst_task_get_type ()) +#define GST_TASK(task) (G_TYPE_CHECK_INSTANCE_CAST ((task), GST_TYPE_TASK, GstTask)) +#define GST_IS_TASK(task) (G_TYPE_CHECK_INSTANCE_TYPE ((task), GST_TYPE_TASK)) +#define GST_TASK_CLASS(tclass) (G_TYPE_CHECK_CLASS_CAST ((tclass), GST_TYPE_TASK, GstTaskClass)) +#define GST_IS_TASK_CLASS(tclass) (G_TYPE_CHECK_CLASS_TYPE ((tclass), GST_TYPE_TASK)) +#define GST_TASK_GET_CLASS(task) (G_TYPE_INSTANCE_GET_CLASS ((task), GST_TYPE_TASK, GstTaskClass)) +#define GST_TASK_CAST(task) ((GstTask*)(task)) + +typedef struct _GstTask GstTask; +typedef struct _GstTaskClass GstTaskClass; + +typedef enum { + GST_TASK_STARTED, + GST_TASK_STOPPED, + GST_TASK_PAUSED, +} GstTaskState; + +#define GST_TASK_STATE(task) (GST_TASK_CAST(task)->state) + +#define GST_TASK_GET_COND(task) (GST_TASK_CAST(task)->cond) +#define GST_TASK_WAIT(task) g_cond_wait(GST_TASK_GET_COND (task), GST_GET_LOCK (task)) +#define GST_TASK_SIGNAL(task) g_cond_signal(GST_TASK_GET_COND (task)) +#define GST_TASK_BROADCAST(task) g_cond_breadcast(GST_TASK_GET_COND (task)) + +struct _GstTask { + GstObject object; + + /*< public >*/ /* with TASK_LOCK */ + GstTaskState state; + GCond *cond; + + GstTaskFunction func; + gpointer data; + + /*< private >*/ + gpointer _gst_reserved[GST_PADDING]; +}; + +struct _GstTaskClass { + GstObjectClass parent_class; + + /*< protected >*/ + gboolean (*start) (GstTask *task); + gboolean (*stop) (GstTask *task); + gboolean (*pause) (GstTask *task); + + /*< private >*/ + gpointer _gst_reserved[GST_PADDING]; +}; + +GType gst_task_get_type (void); + +GstTask* gst_task_create (GstTaskFunction func, gpointer data); + +GstTaskState gst_task_get_state (GstTask *task); + +gboolean gst_task_start (GstTask *task); +gboolean gst_task_stop (GstTask *task); +gboolean gst_task_pause (GstTask *task); + +G_END_DECLS + +#endif /* __GST_TASK_H__ */ + diff --git a/gst/gstthread.c b/gst/gstthread.c deleted file mode 100644 index 2a6c5865c4..0000000000 --- a/gst/gstthread.c +++ /dev/null @@ -1,746 +0,0 @@ -/* GStreamer - * Copyright (C) 1999,2000 Erik Walthinsen - * 2000 Wim Taymans - * 2003 Benjamin Otte - * - * gstthread.c: Threaded container object - * - * 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 "gst_private.h" - -#include "gstthread.h" -#include "gstmarshal.h" -#include "gstscheduler.h" -#include "gstutils.h" -#include "gstinfo.h" - -#define GST_CAT_DEFAULT GST_CAT_THREAD -#define STACK_SIZE 0x200000 - -static GstElementDetails gst_thread_details = -GST_ELEMENT_DETAILS ("Threaded container", - "Generic/Bin", - "Container that creates/manages a thread", - "Erik Walthinsen , " - "Benjamin Otte set_property = GST_DEBUG_FUNCPTR (gst_thread_set_property); - gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_thread_get_property); - - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PRIORITY, - g_param_spec_enum ("priority", "Scheduling Policy", - "The scheduling priority of the thread", GST_TYPE_THREAD_PRIORITY, - G_THREAD_PRIORITY_NORMAL, G_PARAM_READWRITE)); - - gst_thread_signals[SHUTDOWN] = - g_signal_new ("shutdown", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GstThreadClass, shutdown), NULL, NULL, - gst_marshal_VOID__VOID, G_TYPE_NONE, 0); - - gobject_class->dispose = gst_thread_dispose; - -#ifndef GST_DISABLE_LOADSAVE - gstobject_class->save_thyself = GST_DEBUG_FUNCPTR (gst_thread_save_thyself); - gstobject_class->restore_thyself = - GST_DEBUG_FUNCPTR (gst_thread_restore_thyself); -#endif - - gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_thread_change_state); - - gstbin_class->child_state_change = - GST_DEBUG_FUNCPTR (gst_thread_child_state_change); -} - -static void -gst_thread_init (GTypeInstance * instance, gpointer g_class) -{ - GstScheduler *scheduler; - GstThread *thread = GST_THREAD (instance); - - GST_DEBUG ("initializing thread"); - - /* threads are managing bins and iterate themselves */ - /* CR1: the GstBin code checks these flags */ - GST_FLAG_SET (thread, GST_BIN_FLAG_MANAGER); - GST_FLAG_SET (thread, GST_BIN_SELF_SCHEDULABLE); - - scheduler = gst_scheduler_factory_make (NULL, GST_ELEMENT (thread)); - g_assert (scheduler); - - thread->lock = g_mutex_new (); - thread->cond = g_cond_new (); - thread->iterate_lock = g_mutex_new (); - - thread->thread_id = (GThread *) NULL; /* set in NULL -> READY */ - thread->priority = G_THREAD_PRIORITY_NORMAL; -} - -static void -gst_thread_dispose (GObject * object) -{ - GstThread *thread = GST_THREAD (object); - - GST_CAT_DEBUG (GST_CAT_REFCOUNTING, "GstThread: dispose"); - - /* if we get here, the thread is really stopped as it has released - * the last refcount to the thread object, so we can safely free the - * mutex and cond vars */ - G_OBJECT_CLASS (parent_class)->dispose (object); - - g_assert (GST_STATE (thread) == GST_STATE_NULL); - - GST_CAT_DEBUG (GST_CAT_REFCOUNTING, "GstThread: dispose, freeing locks"); - - g_mutex_free (thread->lock); - g_cond_free (thread->cond); - g_mutex_free (thread->iterate_lock); - - gst_object_replace ((GstObject **) & GST_ELEMENT_SCHEDULER (thread), NULL); -} - -/** - * gst_thread_set_priority: - * @thread: the thread to change - * @priority: the new priority for the thread - * - * change the thread's priority - */ -void -gst_thread_set_priority (GstThread * thread, GThreadPriority priority) -{ - g_return_if_fail (GST_IS_THREAD (thread)); - - thread->priority = priority; -} - -static void -gst_thread_set_property (GObject * object, guint prop_id, const GValue * value, - GParamSpec * pspec) -{ - GstThread *thread; - - thread = GST_THREAD (object); - - switch (prop_id) { - case ARG_PRIORITY: - thread->priority = g_value_get_enum (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_thread_get_property (GObject * object, guint prop_id, GValue * value, - GParamSpec * pspec) -{ - GstThread *thread; - - thread = GST_THREAD (object); - - switch (prop_id) { - case ARG_PRIORITY: - g_value_set_enum (value, thread->priority); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - - -/** - * gst_thread_new: - * @name: the name of the thread - * - * Create a new thread with the given name. - * - * Returns: The new thread - */ -GstElement * -gst_thread_new (const gchar * name) -{ - return gst_element_factory_make ("thread", name); -} - -/** - * gst_thread_get_current: - * - * Gets the current GstThread. - * - * Returns: The current GstThread or NULL if you are not running inside a - * #GstThread. - */ -GstThread * -gst_thread_get_current (void) -{ - return (GstThread *) g_private_get (gst_thread_current); -} - -static inline void -gst_thread_release_children_locks (GstThread * thread) -{ - GstRealPad *peer = NULL; - GstElement *peerelement; - - /* not MT safe but gstthread is going away soon */ - GList *elements = (GList *) GST_BIN (thread)->children; - - while (elements) { - GstElement *element = GST_ELEMENT (elements->data); - GList *pads; - - g_assert (element); - GST_DEBUG_OBJECT (thread, "waking element \"%s\"", - GST_ELEMENT_NAME (element)); - elements = g_list_next (elements); - - if (!gst_element_release_locks (element)) - g_warning ("element %s could not release locks", - GST_ELEMENT_NAME (element)); - - pads = GST_ELEMENT_PADS (element); - - while (pads) { - if (GST_PAD_PEER (pads->data)) { - peer = GST_REAL_PAD (GST_PAD_PEER (pads->data)); - pads = g_list_next (pads); - } else { - pads = g_list_next (pads); - continue; - } - - if (!peer) - continue; - - peerelement = GST_PAD_PARENT (peer); - if (!peerelement) - continue; /* FIXME: deal with case where there's no peer */ - - if (GST_ELEMENT_SCHEDULER (peerelement) != GST_ELEMENT_SCHEDULER (thread)) { - GST_LOG_OBJECT (thread, "element \"%s\" has pad cross sched boundary", - GST_ELEMENT_NAME (element)); - GST_LOG_OBJECT (thread, "waking element \"%s\"", - GST_ELEMENT_NAME (peerelement)); - if (!gst_element_release_locks (peerelement)) - g_warning ("element %s could not release locks", - GST_ELEMENT_NAME (peerelement)); - } - } - } -} - -/* sync the main thread, if there is one and makes sure that the thread - * is not spinning and in the waiting state. We can only do this if - * this function is not called from the thread itself. - * - * This function should be called with the thread lock held. - */ -static void -gst_thread_sync (GstThread * thread, gboolean is_self) -{ - if (thread->thread_id == NULL) - return; - - /* need to stop spinning in any case */ - GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING); - - if (is_self) { - /* we're trying to sync ourself. Not much we can do here but hope - * that the thread will stop spinning and end up in the waiting - * state */ - GST_DEBUG_OBJECT (thread, "syncing itself"); - } else { - GST_DEBUG_OBJECT (thread, "syncing thread, grabbing lock"); - /* another thread is trying to sync us. The strategy is to - * repeadetly unlock the element locks until the thread is - * blocking in the waiting state. We use a timeout because - * unlocking all of the children at the same time is not - * atomic and might fail. */ - while (!GST_FLAG_IS_SET (thread, GST_THREAD_STATE_WAITING)) { - GTimeVal tv; - - GST_LOG_OBJECT (thread, "syncing thread..."); - - /* release child locks */ - gst_thread_release_children_locks (thread); - g_get_current_time (&tv); - g_time_val_add (&tv, 1000); /* wait a millisecond to sync the thread */ - GST_DEBUG_OBJECT (thread, "wait"); - g_cond_timed_wait (thread->cond, thread->lock, &tv); - } - GST_LOG_OBJECT (thread, "caught thread"); - /* at this point we should be waiting */ - g_assert (GST_FLAG_IS_SET (thread, GST_THREAD_STATE_WAITING)); - } - g_assert (!GST_FLAG_IS_SET (thread, GST_THREAD_STATE_SPINNING)); -} - -static GstElementStateReturn -gst_thread_change_state (GstElement * element) -{ - GstThread *thread; - GstElementStateReturn ret; - gint transition; - gboolean is_self, reverting = FALSE; - - g_return_val_if_fail (GST_IS_THREAD (element), GST_STATE_FAILURE); - - GST_DEBUG_OBJECT (element, "changing state from %s to %s", - gst_element_state_get_name (GST_STATE (element)), - gst_element_state_get_name (GST_STATE_PENDING (element))); - - thread = GST_THREAD (element); - - /* boolean to check if we called the state change in the same thread as - * the iterate thread */ - is_self = (thread == gst_thread_get_current ()); - transition = GST_STATE_TRANSITION (element); - -revert: - GST_LOG_OBJECT (thread, "grabbing lock"); - g_mutex_lock (thread->lock); - - gst_thread_sync (thread, is_self); - - /* no iteration is allowed during this state change because an iteration - * can cause another state change conflicting with this one */ - /* do not try to grab the lock if this method is called from the - * same thread as the iterate thread, the lock might be held and we - * might deadlock */ - if (!is_self && !reverting) - g_mutex_lock (thread->iterate_lock); - - switch (transition) { - case GST_STATE_NULL_TO_READY:{ - GError *err = NULL; - - /* create the thread */ - GST_FLAG_UNSET (thread, GST_THREAD_STATE_REAPING); - GST_LOG_OBJECT (element, "grabbing lock"); - thread->thread_id = g_thread_create_full (gst_thread_main_loop, - thread, STACK_SIZE, FALSE, TRUE, thread->priority, &err); - if (!thread->thread_id) { - GST_ERROR_OBJECT (element, "g_thread_create_full failed: %s", - err->message); - g_error_free (err); - goto error_out; - } - GST_LOG_OBJECT (element, "GThread created"); - - /* wait for it to 'spin up' */ - g_cond_wait (thread->cond, thread->lock); - break; - } - case GST_STATE_READY_TO_PAUSED: - break; - case GST_STATE_PAUSED_TO_PLAYING: - { - /* reset self to spinning */ - GST_FLAG_SET (thread, GST_THREAD_STATE_SPINNING); - break; - } - case GST_STATE_PLAYING_TO_PAUSED: - { - GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING); - break; - } - case GST_STATE_PAUSED_TO_READY: - GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING); - break; - case GST_STATE_READY_TO_NULL: - /* we can't join the threads here, because this could have been triggered - by ourself (ouch) */ - GST_LOG_OBJECT (thread, "destroying GThread %p", thread->thread_id); - GST_FLAG_SET (thread, GST_THREAD_STATE_REAPING); - GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING); - /* thread was already gone */ - if (thread->thread_id != NULL) { - thread->thread_id = NULL; - if (is_self) { - GST_LOG_OBJECT (thread, - "Thread %s is destroying itself. Returning to mainloop ASAP!", - GST_ELEMENT_NAME (thread)); - break; - } else { - /* now wait for the thread to destroy itself */ - GST_DEBUG_OBJECT (thread, "signal"); - g_cond_signal (thread->cond); - GST_DEBUG_OBJECT (thread, "wait"); - g_cond_wait (thread->cond, thread->lock); - GST_DEBUG_OBJECT (thread, "done"); - /* it should be dead now */ - } - } - break; - default: - break; - } - GST_LOG_OBJECT (thread, "unlocking lock"); - g_mutex_unlock (thread->lock); - - if (reverting) { - goto error_out_unlocked; - } else if (GST_ELEMENT_CLASS (parent_class)->change_state) { - ret = GST_ELEMENT_CLASS (parent_class)->change_state (GST_ELEMENT (thread)); - if (ret == GST_STATE_FAILURE) { - reverting = TRUE; - transition = ((transition & 0xff) << 8) | (transition >> 8); - goto revert; - } - } else { - ret = GST_STATE_SUCCESS; - } - - g_mutex_lock (thread->lock); - if (GST_STATE (thread) == GST_STATE_PLAYING && - GST_FLAG_IS_SET (thread, GST_THREAD_STATE_WAITING)) { - GST_FLAG_SET (thread, GST_THREAD_STATE_SPINNING); - if (!is_self) { - g_cond_signal (thread->cond); - } - } - g_mutex_unlock (thread->lock); - - if (!is_self) - g_mutex_unlock (thread->iterate_lock); - - return ret; - -error_out: - GST_CAT_DEBUG (GST_CAT_STATES, "changing state from %s to %s failed for %s", - gst_element_state_get_name (GST_STATE (element)), - gst_element_state_get_name (GST_STATE_PENDING (element)), - GST_ELEMENT_NAME (element)); - - g_mutex_unlock (thread->lock); - -error_out_unlocked: - if (!is_self) - g_mutex_unlock (thread->iterate_lock); - - return GST_STATE_FAILURE; -} - -/* When a child changes its state the thread might have to start. - * - * When we are in the thread context we set the spinning flag. - * When we are not in the thread context, we grab the lock. The - * thread can then only be in the waiting or spinning state. If it's - * waiting we signal it to start. If it was spinning, we let it spin. - */ -static void -gst_thread_child_state_change (GstBin * bin, GstElementState oldstate, - GstElementState newstate, GstElement * element) -{ - GstThread *thread = GST_THREAD (bin); - gboolean is_self; - GstThread *current; - - current = gst_thread_get_current (); - - is_self = (current == thread); - - GST_LOG_OBJECT (bin, "(from thread %s) child %s changed state from %s to %s", - current ? GST_ELEMENT_NAME (current) : - "(none)", GST_ELEMENT_NAME (element), - gst_element_state_get_name (oldstate), - gst_element_state_get_name (newstate)); - - if (parent_class->child_state_change) - parent_class->child_state_change (bin, oldstate, newstate, element); - - /* if we're changing from playing to paused, kids will one-by-one go - * to paused while we're playing, which is bad if we execute the code - * below. We should only do that when a child explicitely changed - * state outside our own context. */ - if (GST_FLAG_IS_SET (bin, GST_BIN_STATE_LOCKED)) - return; - - /* see if we have to wake up the thread now. */ - if (is_self) { - GST_LOG_OBJECT (element, "we are in the thread context"); - /* we are the thread, set the spinning flag so that we start spinning - * next time */ - if (newstate == GST_STATE_PLAYING) { - GST_FLAG_SET (thread, GST_THREAD_STATE_SPINNING); - } - } else { - g_mutex_lock (thread->lock); - /* thread is now spinning or waiting after grabbing the lock */ - if (newstate == GST_STATE_PLAYING) { - if (!GST_FLAG_IS_SET (thread, GST_THREAD_STATE_WAITING)) { - /* its spinning, that's not really a problem, it will - * continue to do so */ - GST_LOG_OBJECT (element, "thread is playing"); - } else { - /* waiting, set spinning flag and trigger restart. */ - GST_LOG_OBJECT (element, "signal playing"); - GST_FLAG_SET (thread, GST_THREAD_STATE_SPINNING); - g_cond_signal (GST_THREAD (bin)->cond); - } - } - g_mutex_unlock (thread->lock); - } - GST_LOG_OBJECT (element, "done child state change"); -} - -/** - * gst_thread_main_loop: - * @arg: the thread to start - * - * The main loop of the thread. The thread will iterate - * while the state is GST_THREAD_STATE_SPINNING. - */ -static void * -gst_thread_main_loop (void *arg) -{ - GstThread *thread = NULL; - GstElement *element = NULL; - GstScheduler *sched; - - thread = GST_THREAD (arg); - GST_LOG_OBJECT (element, "grabbing lock"); - g_mutex_lock (thread->lock); - element = GST_ELEMENT (arg); - GST_LOG_OBJECT (thread, "Started main loop"); - - /* initialize gst_thread_current */ - g_private_set (gst_thread_current, thread); - - /* set up the element's scheduler */ - gst_scheduler_setup (GST_ELEMENT_SCHEDULER (thread)); - GST_FLAG_UNSET (thread, GST_THREAD_STATE_REAPING); - GST_FLAG_UNSET (thread, GST_THREAD_STATE_WAITING); - - /* signals the startup of the thread */ - GST_LOG_OBJECT (element, "signal"); - g_cond_signal (thread->cond); - GST_LOG_OBJECT (element, "unlocking lock"); - - gst_object_ref (GST_OBJECT (thread)); - /* as long as we're not dying we can continue */ - while (!(GST_FLAG_IS_SET (thread, GST_THREAD_STATE_REAPING))) { - /* in the playing state we need to do something special */ - if (GST_STATE (thread) == GST_STATE_PLAYING) { - GST_LOG_OBJECT (thread, "starting to iterate"); - /* continue iteration while spinning */ - while (GST_FLAG_IS_SET (thread, GST_THREAD_STATE_SPINNING)) { - gboolean status; - - g_mutex_unlock (thread->lock); - g_mutex_lock (thread->iterate_lock); - status = gst_bin_iterate (GST_BIN (thread)); - g_mutex_unlock (thread->iterate_lock); - g_mutex_lock (thread->lock); - - if (!status) { - GST_DEBUG_OBJECT (thread, "iterate returned false"); - if (GST_STATE (thread) != GST_STATE_PLAYING) { - GST_DEBUG_OBJECT (thread, - "stopping spinning as state is not playing"); - GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING); - } - } - /* if we hold the last refcount, the app unreffed the - * thread and we should stop ASAP */ - if (G_OBJECT (thread)->ref_count == 1) { - GST_DEBUG_OBJECT (thread, "reaping as refcount is only 1"); - GST_FLAG_SET (thread, GST_THREAD_STATE_REAPING); - GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING); - } - } - } - /* do not try to sync when we are REAPING */ - if (!(GST_FLAG_IS_SET (thread, GST_THREAD_STATE_REAPING))) { - /* sync section */ - GST_LOG_OBJECT (thread, "entering sync"); - GST_DEBUG_OBJECT (thread, "signal"); - g_cond_signal (thread->cond); - GST_DEBUG_OBJECT (thread, "wait"); - GST_FLAG_UNSET (thread, GST_THREAD_STATE_SPINNING); - GST_FLAG_SET (thread, GST_THREAD_STATE_WAITING); - g_cond_wait (thread->cond, thread->lock); - GST_FLAG_UNSET (thread, GST_THREAD_STATE_WAITING); - GST_LOG_OBJECT (thread, "wait done"); - } - } - GST_LOG_OBJECT (thread, "unlocking lock"); - thread->thread_id = NULL; - g_mutex_unlock (thread->lock); - - /* we need to destroy the scheduler here because it might have - * mapped it's stack into the threads stack space */ - sched = GST_ELEMENT_SCHEDULER (thread); - if (sched) - gst_scheduler_reset (sched); - - g_signal_emit (G_OBJECT (thread), gst_thread_signals[SHUTDOWN], 0); - - /* signal - we are out */ - GST_LOG_OBJECT (thread, "Thread %p exits main loop", g_thread_self ()); - g_cond_signal (thread->cond); - gst_object_unref (GST_OBJECT (thread)); - /* don't assume the GstThread object exists anymore now */ - - return NULL; -} - -#ifndef GST_DISABLE_LOADSAVE -static xmlNodePtr -gst_thread_save_thyself (GstObject * object, xmlNodePtr self) -{ - if (GST_OBJECT_CLASS (parent_class)->save_thyself) - GST_OBJECT_CLASS (parent_class)->save_thyself (object, self); - return NULL; -} - -static void -gst_thread_restore_thyself (GstObject * object, xmlNodePtr self) -{ - GST_LOG_OBJECT (object, "restoring"); - - if (GST_OBJECT_CLASS (parent_class)->restore_thyself) - GST_OBJECT_CLASS (parent_class)->restore_thyself (object, self); -} -#endif /* GST_DISABLE_LOADSAVE */ diff --git a/gst/gstthread.h b/gst/gstthread.h deleted file mode 100644 index 806923d684..0000000000 --- a/gst/gstthread.h +++ /dev/null @@ -1,88 +0,0 @@ -/* GStreamer - * Copyright (C) 1999,2000 Erik Walthinsen - * 2000 Wim Taymans - * - * gstthread.h: Header for GstThread object - * - * 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 __GST_THREAD_H__ -#define __GST_THREAD_H__ - -#include - -#include - - -G_BEGIN_DECLS - -extern GPrivate *gst_thread_current; - -typedef enum { - GST_THREAD_STATE_SPINNING = GST_BIN_FLAG_LAST, - GST_THREAD_STATE_REAPING, - GST_THREAD_STATE_WAITING, - - /* padding */ - GST_THREAD_FLAG_LAST = GST_BIN_FLAG_LAST + 4 -} GstThreadState; - -#define GST_TYPE_THREAD (gst_thread_get_type()) -#define GST_THREAD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_THREAD,GstThread)) -#define GST_IS_THREAD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_THREAD)) -#define GST_THREAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_THREAD,GstThreadClass)) -#define GST_IS_THREAD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_THREAD)) -#define GST_THREAD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_THREAD, GstThreadClass)) - -typedef struct _GstThread GstThread; -typedef struct _GstThreadClass GstThreadClass; - -struct _GstThread { - GstBin bin; - - GThread *thread_id; /* id of the thread, if any */ - GThreadPriority priority; - - GMutex *lock; /* thread lock/condititon pairs */ - GCond *cond; /* used to control the thread */ - - GMutex *iterate_lock; /* lock iteration in state change */ - - gpointer _gst_reserved[GST_PADDING-1]; -}; - -struct _GstThreadClass { - GstBinClass parent_class; - - /* signals */ - void (*shutdown) (GstThread *thread); - - gpointer _gst_reserved[GST_PADDING]; -}; - -GType gst_thread_get_type (void); - -GstElement* gst_thread_new (const gchar *name); - -void gst_thread_set_priority (GstThread *thread, GThreadPriority priority); -GstThread * gst_thread_get_current (void); - -G_END_DECLS - - -#endif /* __GST_THREAD_H__ */ diff --git a/gst/gsttypes.h b/gst/gsttypes.h index 62bafc713b..a6e3a51047 100644 --- a/gst/gsttypes.h +++ b/gst/gsttypes.h @@ -41,6 +41,7 @@ typedef struct _GstBusClass GstBusClass; typedef struct _GstScheduler GstScheduler; typedef struct _GstSchedulerClass GstSchedulerClass; typedef struct _GstEvent GstEvent; +typedef struct _GstMessage GstMessage; typedef enum { GST_STATE_VOID_PENDING = 0, diff --git a/gst/schedulers/Makefile.am b/gst/schedulers/Makefile.am index b67ccf691a..d581bbca85 100644 --- a/gst/schedulers/Makefile.am +++ b/gst/schedulers/Makefile.am @@ -1,82 +1,15 @@ -if GST_DISABLE_OMEGA_COTHREADS -omegaschedulers = -omegaschedulers_nola = -else -omegaschedulers = \ - libgstbasicomegascheduler.la \ - libgstentryomegascheduler.la \ - libgstoptomegascheduler.la -omegaschedulers_nola = \ - libgstbasicomegascheduler \ - libgstentryomegascheduler \ - libgstoptomegascheduler -endif plugin_LTLIBRARIES = \ - $(omegaschedulers) \ - libgstbasicgthreadscheduler.la \ - libgstentrygthreadscheduler.la \ - libgstoptscheduler.la \ - libgstoptgthreadscheduler.la \ - libgstfairgthreadscheduler.la + libthreadscheduler.la AS_LIBTOOL_LIBS = \ - $(omegaschedulers_nola) \ - libgstbasicgthreadscheduler \ - libgstentrygthreadscheduler \ - libgstoptscheduler \ - libgstoptgthreadscheduler \ - libgstfairgthreadscheduler + libthreadscheduler -if GST_DISABLE_OMEGA_COTHREADS -else -libgstbasicomegascheduler_la_SOURCES = gstbasicscheduler.c -libgstbasicomegascheduler_la_CFLAGS = $(GST_OBJ_CFLAGS) -D_COTHREADS_OMEGA -libgstbasicomegascheduler_la_LIBADD = $(GST_OBJ_LIBS) ../libcothreads.la -libgstbasicomegascheduler_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(as_libtool_LDFLAGS) -endif +libthreadscheduler_la_SOURCES = threadscheduler.c +libthreadscheduler_la_CFLAGS = $(GST_OBJ_CFLAGS) +libthreadscheduler_la_LIBADD = $(GST_OBJ_LIBS) +libthreadscheduler_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -libgstbasicgthreadscheduler_la_SOURCES = gstbasicscheduler.c -libgstbasicgthreadscheduler_la_CFLAGS = $(GST_OBJ_CFLAGS) -D_COTHREADS_GTHREAD -libgstbasicgthreadscheduler_la_LIBADD = $(GST_OBJ_LIBS) -libgstbasicgthreadscheduler_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(as_libtool_LDFLAGS) - -libgstentrygthreadscheduler_la_SOURCES = entryscheduler.c -libgstentrygthreadscheduler_la_CFLAGS = $(GST_OBJ_CFLAGS) -D_COTHREADS_GTHREAD -libgstentrygthreadscheduler_la_LIBADD = $(GST_OBJ_LIBS) -libgstentrygthreadscheduler_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(as_libtool_LDFLAGS) - -if GST_DISABLE_OMEGA_COTHREADS -else -libgstentryomegascheduler_la_SOURCES = entryscheduler.c -libgstentryomegascheduler_la_CFLAGS = $(GST_OBJ_CFLAGS) -D_COTHREADS_OMEGA -libgstentryomegascheduler_la_LIBADD = $(GST_OBJ_LIBS) ../libcothreads.la -libgstentryomegascheduler_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(as_libtool_LDFLAGS) -endif - -libgstoptscheduler_la_SOURCES = gstoptimalscheduler.c -libgstoptscheduler_la_CFLAGS = $(GST_OBJ_CFLAGS) -libgstoptscheduler_la_LIBADD = $(GST_OBJ_LIBS) -libgstoptscheduler_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(as_libtool_LDFLAGS) - -if GST_DISABLE_OMEGA_COTHREADS -else -libgstoptomegascheduler_la_SOURCES = gstoptimalscheduler.c -libgstoptomegascheduler_la_CFLAGS = $(GST_OBJ_CFLAGS) -D_COTHREADS_OMEGA -DUSE_COTHREADS -libgstoptomegascheduler_la_LIBADD = $(GST_OBJ_LIBS) ../libcothreads.la -libgstoptomegascheduler_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(as_libtool_LDFLAGS) -endif - -libgstoptgthreadscheduler_la_SOURCES = gstoptimalscheduler.c -libgstoptgthreadscheduler_la_CFLAGS = $(GST_OBJ_CFLAGS) -D_COTHREADS_GTHREAD -DUSE_COTHREADS -libgstoptgthreadscheduler_la_LIBADD = $(GST_OBJ_LIBS) -libgstoptgthreadscheduler_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(as_libtool_LDFLAGS) - -libgstfairgthreadscheduler_la_SOURCES = fairscheduler.c faircothreads.c -libgstfairgthreadscheduler_la_CFLAGS = $(GST_OBJ_CFLAGS) -D_COTHREADS_GTHREAD -libgstfairgthreadscheduler_la_LIBADD = $(GST_OBJ_LIBS) -libgstfairgthreadscheduler_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(as_libtool_LDFLAGS) - -noinst_HEADERS = cothreads_compat.h gthread-cothreads.h faircothreads.h +noinst_HEADERS = if AS_LIBTOOL_WIN32 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 2a672860d8..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)->scheduler)) - -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 c5c0f667ef..0000000000 --- a/gst/schedulers/faircothreads.c +++ /dev/null @@ -1,615 +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. */ - }; - - -#ifndef GST_DISABLE_GST_DEBUG - static gchar *gst_fairscheduler_ct_state_names[] = { - "stopped", - "suspended", - "running" - }; -#endif - - -/* - * 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; -#ifndef GST_DISABLE_GST_DEBUG - g_print (" %p: %s (%d)\n", ct, ct->readable_name->str, ct->pid); -#endif - } -} - - -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 51765863c3..0000000000 --- a/gst/schedulers/fairscheduler.c +++ /dev/null @@ -1,1415 +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); - { -#ifndef GST_DISABLE_GST_DEBUG - gulong msecs; - double elapsed = g_timer_elapsed (fsched->iter_timer, &msecs); -#endif - - 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) -{ -#ifndef GST_DISABLE_GST_DEBUG - GstFairScheduler *fsched = GST_FAIR_SCHEDULER (sched); -#endif - 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) -{ -#ifndef GST_DISABLE_GST_DEBUG - GstFairScheduler *fsched = GST_FAIR_SCHEDULER (sched); -#endif - 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) -{ -#ifndef GST_DISABLE_GST_DEBUG - GstFairScheduler *fsched = GST_FAIR_SCHEDULER (sched); -#endif - 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) -{ -#ifndef GST_DISABLE_GST_DEBUG - GstFairScheduler *fsched = GST_FAIR_SCHEDULER (sched); -#endif - - 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 c7cd67594e..0000000000 --- a/gst/schedulers/gstbasicscheduler.c +++ /dev/null @@ -1,1494 +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_SCHEDULER (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{ \ - SCHED (element)->current = element; \ - do_cothread_switch (GST_ELEMENT_THREADSTATE (element)); \ -}G_STMT_END - -#define do_switch_to_main(sched) G_STMT_START{ \ - ((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{ \ - 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)) { - 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)) { - 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. */ - 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 = element->pads; - 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->scheduler != 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->scheduler = 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_SCHEDULER (GST_PAD_PARENT (pad)) == - GST_ELEMENT_SCHEDULER (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); - 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_SCHEDULER (srcelement), GST_ELEMENT_SCHEDULER (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 5c594fd661..0000000000 --- a/gst/schedulers/gstoptimalscheduler.c +++ /dev/null @@ -1,2933 +0,0 @@ -/* GStreamer - * Copyright (C) 1999,2000 Erik Walthinsen - * 2000 Wim Taymans - * - * gstoptimalscheduler.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; - -#define GST_OPT_LOCK(sched) (g_static_rec_mutex_lock (&((GstOptScheduler *)sched)->lock)) -#define GST_OPT_UNLOCK(sched) (g_static_rec_mutex_unlock (&((GstOptScheduler *)sched)->lock)) - -struct _GstOptScheduler -{ - GstScheduler parent; - - GStaticRecMutex lock; - - 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 gboolean 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, - GstOptSchedulerGroup * only_group); -#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_finalize (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); - gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_opt_scheduler_finalize); - - 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) -{ - g_static_rec_mutex_init (&scheduler->lock); - - 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 void -gst_opt_scheduler_finalize (GObject * object) -{ - GstOptScheduler *osched = GST_OPT_SCHEDULER (object); - - g_static_rec_mutex_free (&osched->lock); - - G_OBJECT_CLASS (parent_class)->finalize (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; -} - -/* we need to remove a decoupled element from the scheduler, this - * involves finding all the groups that have this element as an - * entry point and disabling them. */ -static void -remove_decoupled (GstScheduler * sched, GstElement * element) -{ - GSList *chains; - GList *schedulers; - GstOptScheduler *osched = GST_OPT_SCHEDULER (sched); - - GST_DEBUG_OBJECT (sched, "removing decoupled element \"%s\"", - GST_OBJECT_NAME (element)); - - for (chains = osched->chains; chains; chains = g_slist_next (chains)) { - GstOptSchedulerChain *chain = (GstOptSchedulerChain *) chains->data; - GSList *groups; - - for (groups = chain->groups; groups; groups = g_slist_next (groups)) { - GstOptSchedulerGroup *group = (GstOptSchedulerGroup *) groups->data; - - if (group->entry) { - GST_DEBUG_OBJECT (sched, "group %p, entry %s", group, - GST_STR_NULL (GST_OBJECT_NAME (group->entry))); - } - - if (group->entry == element) { - GST_DEBUG ("clearing element %p \"%s\" as entry from group %p", - element, GST_ELEMENT_NAME (element), group); - group->entry = NULL; - group->type = GST_OPT_SCHEDULER_GROUP_UNKNOWN; - } - } - } - /* and remove from any child scheduler */ - for (schedulers = sched->schedulers; schedulers; - schedulers = g_list_next (schedulers)) { - remove_decoupled (GST_SCHEDULER (schedulers->data), element); - } -} - -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) { - GST_DEBUG ("clearing element %p \"%s\" as entry from group %p", - element, GST_ELEMENT_NAME (element), group); - 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; - GstElement *entry = NULL; - - lcopy = g_slist_copy (group->elements); - /* also add entry point, this is made so that decoupled elements - * are also reffed since they are not added to the list of group - * elements. */ - if (group->entry && GST_ELEMENT_IS_DECOUPLED (group->entry)) { - entry = group->entry; - gst_object_ref (GST_OBJECT (entry)); - } - - for (l = lcopy; l; l = l->next) { - GstElement *e = (GstElement *) l->data; - - gst_object_ref (GST_OBJECT (e)); - } - - group->schedulefunc (group->argc, group->argv); - - for (l = lcopy; l; l = l->next) { - GstElement *e = (GstElement *) l->data; - - gst_object_unref (GST_OBJECT (e)); - } - if (entry) - gst_object_unref (GST_OBJECT (entry)); - g_slist_free (lcopy); - } - return TRUE; -#endif -} - -#ifndef USE_COTHREADS -static void -gst_opt_scheduler_schedule_run_queue (GstOptScheduler * osched, - GstOptSchedulerGroup * only_group) -{ - 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++; - - do { - GstOptSchedulerGroup *group; - gboolean res; - - if (only_group) - group = only_group; - else - 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); - } while (osched->runqueue && !only_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 gboolean -schedule_chain (GstOptSchedulerChain * chain) -{ - GSList *groups; - GstOptScheduler *osched; - gboolean scheduled = FALSE; - - 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); - - /* since the lock on the group list is only released when we schedule - * a group and since we only schedule one group, we don't need to - * worry about the list getting corrupted. */ - 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, NULL); -#endif - - scheduled = TRUE; - - GST_LOG ("done scheduling group %p in chain %p", group, chain); - unref_group (group); - /* stop scheduling more groups */ - break; - } - - groups = g_slist_next (groups); - } - return scheduled; -} - -/* this function is inserted in the gethandler when you are not - * supposed to call _pull on the pad. */ -static GstData * -get_invalid_call (GstPad * pad) -{ - GST_ELEMENT_ERROR (GST_PAD_PARENT (pad), CORE, PAD, (NULL), - ("get on pad %s:%s but the peer is operating chain based and so is not " - "allowed to pull, fix the element.", GST_DEBUG_PAD_NAME (pad))); - - return GST_DATA (gst_event_new (GST_EVENT_INTERRUPT)); -} - -/* this function is inserted in the chainhandler when you are not - * supposed to call _push on the pad. */ -static void -chain_invalid_call (GstPad * pad, GstData * data) -{ - GST_ELEMENT_ERROR (GST_PAD_PARENT (pad), CORE, PAD, (NULL), - ("chain on pad %s:%s but the pad is get based", - GST_DEBUG_PAD_NAME (pad))); - - gst_data_unref (data); -} - -/* 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; - GstOptScheduler *osched; - - /* what if the entry point disappeared? */ - if (entry == NULL || group->chain == NULL) - return 0; - - osched = group->chain->sched; - - pads = entry->pads; - - GST_LOG ("executing get-based group %p", group); - - group->flags |= GST_OPT_SCHEDULER_GROUP_RUNNING; - - GST_OPT_UNLOCK (osched); - 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); - } - } - GST_OPT_LOCK (osched); - - 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. - * This function should be called with the scheduler lock held. */ -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 (group->chain == NULL) - return 0; - - if (entry->loopfunc) { - GstOptScheduler *osched = group->chain->sched; - - GST_OPT_UNLOCK (osched); - entry->loopfunc (entry); - GST_OPT_LOCK (osched); - } 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); - - GST_OPT_LOCK (osched); -#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_OPT_UNLOCK (osched); - - 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; - - GST_OPT_LOCK (osched); - 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, group); - GST_LOG ("return from recurse group %p", group); - - /* if the other group was disabled we might have to break out of the loop */ - disabled = GST_OPT_SCHEDULER_GROUP_IS_DISABLED (group); - 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); - data = GST_DATA (gst_event_new (GST_EVENT_INTERRUPT)); - goto done; - } -#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); - -#ifndef USE_COTHREADS -done: -#endif - GST_OPT_UNLOCK (osched); - - 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); - - GST_OPT_LOCK (sched); - /* 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"); - } - } - goto done; - } - - /* we don't care about decoupled elements after this */ - if (GST_ELEMENT_IS_DECOUPLED (element)) { - res = GST_STATE_SUCCESS; - goto done; - } - - /* 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 *) element->pads; - - g_list_foreach (pads, (GFunc) pad_clear_queued, NULL); - break; - } - default: - break; - } - - //gst_scheduler_show (sched); - -done: - GST_OPT_UNLOCK (sched); - - 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 = element->pads; - 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; - - GST_OPT_LOCK (sched); - chain = create_chain (osched); - - group = create_group (chain, element, GST_OPT_SCHEDULER_GROUP_LOOP); - group->entry = element; - GST_OPT_UNLOCK (sched); - - 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)); - - GST_OPT_LOCK (sched); - /* decoupled elements are not added to the scheduler lists and should therefore - * not be removed */ - if (GST_ELEMENT_IS_DECOUPLED (element)) { - remove_decoupled (sched, element); - goto done; - } - - /* 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; - -done: - GST_OPT_UNLOCK (sched); -} - -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_OPT_LOCK (sched); - GST_INFO ("scheduler set interrupted state"); - osched->state = GST_OPT_SCHEDULER_STATE_INTERRUPTED; - GST_OPT_UNLOCK (sched); - } - return TRUE; -#endif -} - -static void -gst_opt_scheduler_error (GstScheduler * sched, GstElement * element) -{ - GstOptScheduler *osched = GST_OPT_SCHEDULER (sched); - GstOptSchedulerGroup *group; - - GST_OPT_LOCK (sched); - get_group (element, &group); - if (group) - group_error_handler (group); - - osched->state = GST_OPT_SCHEDULER_STATE_ERROR; - GST_OPT_UNLOCK (sched); -} - -/* 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); - - GST_OPT_LOCK (sched); - /* 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"); - goto done; - } - } - } 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"); - goto done; - } - } - } 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) = get_invalid_call; - 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_GETHANDLER (srcpad) = get_invalid_call; - 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; - GST_RPAD_CHAINHANDLER (sinkpad) = chain_invalid_call; - - /* 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_GETHANDLER (srcpad) = gst_opt_scheduler_get_wrapper; - GST_RPAD_CHAINHANDLER (sinkpad) = gst_opt_scheduler_loop_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; - } -done: - GST_OPT_UNLOCK (sched); -} - -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); - } - } - /* don't forget to set any decoupled entry points that are not accounted for in the - * element list (since they belong to two groups). */ - if (group->entry) { - if (visited) { - GST_ELEMENT_SET_VISITED (group->entry); - } else { - GST_ELEMENT_UNSET_VISITED (group->entry); - } - } -} - -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 = element->pads; - 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); - - GST_OPT_LOCK (sched); - 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"); - goto done; - } - - /* 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 */ - } -done: - GST_OPT_UNLOCK (sched); -} - -/* 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; - - GST_OPT_LOCK (sched); - 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); - scheduled = schedule_chain (chain); - 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; - } - - /* grab the next chain before we unref, the list we are iterating - * can only be updated in the unref method */ - 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--; - } - GST_OPT_UNLOCK (sched); - - return state; -} - - -static void -gst_opt_scheduler_show (GstScheduler * sched) -{ - GstOptScheduler *osched = GST_OPT_SCHEDULER (sched); - GSList *chains; - - GST_OPT_LOCK (sched); - - 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); - } - } - } - GST_OPT_UNLOCK (sched); -} - -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 new file mode 100644 index 0000000000..75f68dac52 --- /dev/null +++ b/gst/schedulers/threadscheduler.c @@ -0,0 +1,347 @@ +/* GStreamer2 + * Copyright (C) 2004 Wim Taymans + * + * threadscheduler.c: scheduler using threads + * + * 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 "../gst-i18n-lib.h" + +GST_DEBUG_CATEGORY_STATIC (debug_scheduler); +#define GST_CAT_DEFAULT debug_scheduler + +#define GST_TYPE_THREAD_SCHEDULER \ + (gst_thread_scheduler_get_type ()) +#define GST_THREAD_SCHEDULER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_THREAD_SCHEDULER,GstThreadScheduler)) +#define GST_THREAD_SCHEDULER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_THREAD_SCHEDULER,GstThreadSchedulerClass)) +#define GST_IS_THREAD_SCHEDULER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_THREAD_SCHEDULER)) +#define GST_IS_THREAD_SCHEDULER_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_THREAD_SCHEDULER)) + +#define SCHED(element) (GST_THREAD_SCHEDULER ((element)->sched)) + +GType gst_thread_scheduler_get_type (void); + +typedef struct _GstThreadScheduler GstThreadScheduler; +typedef struct _GstThreadSchedulerClass GstThreadSchedulerClass; + +struct _GstThreadScheduler +{ + GstScheduler scheduler; + + GThreadPool *pool; +}; + +struct _GstThreadSchedulerClass +{ + GstSchedulerClass scheduler_class; +}; + +#define ELEMENT_PRIVATE(element) GST_ELEMENT (element)->sched_private +#define PAD_PRIVATE(pad) (GST_REAL_PAD (pad))->sched_private + +#define GST_TYPE_THREAD_SCHEDULER_TASK \ + (gst_thread_scheduler_task_get_type ()) +#define GST_THREAD_SCHEDULER_TASK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_THREAD_SCHEDULER_TASK,GstThreadSchedulerTask)) +#define GST_THREAD_SCHEDULER_TASK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_THREAD_SCHEDULER_TASK,GstThreadSchedulerTaskClass)) +#define GST_IS_THREAD_SCHEDULER_TASK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_THREAD_SCHEDULER_TASK)) +#define GST_IS_THREAD_SCHEDULER_TASK_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_THREAD_SCHEDULER_TASK)) + +typedef struct _GstThreadSchedulerTask GstThreadSchedulerTask; +typedef struct _GstThreadSchedulerTaskClass GstThreadSchedulerTaskClass; + +struct _GstThreadSchedulerTask +{ + GstTask task; +}; + +struct _GstThreadSchedulerTaskClass +{ + GstTaskClass parent_class; +}; + +static void gst_thread_scheduler_task_class_init (gpointer g_class, + gpointer data); +static void gst_thread_scheduler_task_init (GstThreadSchedulerTask * object); + +static gboolean gst_thread_scheduler_task_start (GstTask * task); +static gboolean gst_thread_scheduler_task_stop (GstTask * task); +static gboolean gst_thread_scheduler_task_pause (GstTask * task); + +GType +gst_thread_scheduler_task_get_type (void) +{ + static GType object_type = 0; + + if (object_type == 0) { + static const GTypeInfo object_info = { + sizeof (GstThreadSchedulerTaskClass), + NULL, + NULL, + gst_thread_scheduler_task_class_init, + NULL, + NULL, + sizeof (GstThreadSchedulerTask), + 0, + (GInstanceInitFunc) gst_thread_scheduler_task_init + }; + + object_type = + g_type_register_static (GST_TYPE_TASK, + "GstThreadSchedulerTask", &object_info, 0); + } + return object_type; +} + +static void +gst_thread_scheduler_task_class_init (gpointer klass, gpointer class_data) +{ + GstTaskClass *task = GST_TASK_CLASS (klass); + + task->start = gst_thread_scheduler_task_start; + task->stop = gst_thread_scheduler_task_stop; + task->pause = gst_thread_scheduler_task_pause; +} + +static void +gst_thread_scheduler_task_init (GstThreadSchedulerTask * task) +{ + GST_TASK (task)->state = GST_TASK_STOPPED; +} + +static gboolean +gst_thread_scheduler_task_start (GstTask * task) +{ + GstThreadSchedulerTask *ttask = GST_THREAD_SCHEDULER_TASK (task); + GstThreadScheduler *tsched = + GST_THREAD_SCHEDULER (GST_OBJECT_PARENT (GST_OBJECT (task))); + GstTaskState old; + + GST_DEBUG_OBJECT (task, "Starting task %p", task); + + GST_LOCK (ttask); + old = GST_TASK_CAST (ttask)->state; + GST_TASK_CAST (ttask)->state = GST_TASK_STARTED; + switch (old) { + case GST_TASK_STOPPED: + g_thread_pool_push (tsched->pool, task, NULL); + break; + case GST_TASK_PAUSED: + GST_TASK_SIGNAL (ttask); + break; + case GST_TASK_STARTED: + break; + } + GST_UNLOCK (ttask); + + return TRUE; +} + +static gboolean +gst_thread_scheduler_task_stop (GstTask * task) +{ + GstThreadSchedulerTask *ttask = GST_THREAD_SCHEDULER_TASK (task); + GstTaskState old; + + GST_DEBUG_OBJECT (task, "Stopping task %p", task); + + GST_LOCK (ttask); + old = GST_TASK_CAST (ttask)->state; + GST_TASK_CAST (ttask)->state = GST_TASK_STOPPED; + switch (old) { + case GST_TASK_STOPPED: + break; + case GST_TASK_PAUSED: + GST_TASK_SIGNAL (ttask); + break; + case GST_TASK_STARTED: + break; + } + GST_UNLOCK (ttask); + + return TRUE; +} + +static gboolean +gst_thread_scheduler_task_pause (GstTask * task) +{ + GstThreadSchedulerTask *ttask = GST_THREAD_SCHEDULER_TASK (task); + GstThreadScheduler *tsched = + GST_THREAD_SCHEDULER (GST_OBJECT_PARENT (GST_OBJECT (task))); + GstTaskState old; + + GST_DEBUG_OBJECT (task, "Pausing task %p", task); + + GST_LOCK (ttask); + old = GST_TASK_CAST (ttask)->state; + GST_TASK_CAST (ttask)->state = GST_TASK_PAUSED; + switch (old) { + case GST_TASK_STOPPED: + g_thread_pool_push (tsched->pool, task, NULL); + break; + case GST_TASK_PAUSED: + break; + case GST_TASK_STARTED: + break; + } + GST_UNLOCK (ttask); + + return TRUE; +} + +static void gst_thread_scheduler_class_init (gpointer g_class, gpointer data); +static void gst_thread_scheduler_init (GstThreadScheduler * object); + +GType +gst_thread_scheduler_get_type (void) +{ + static GType object_type = 0; + + if (object_type == 0) { + static const GTypeInfo object_info = { + sizeof (GstThreadSchedulerClass), + NULL, + NULL, + gst_thread_scheduler_class_init, + NULL, + NULL, + sizeof (GstThreadScheduler), + 0, + (GInstanceInitFunc) gst_thread_scheduler_init + }; + + object_type = + g_type_register_static (GST_TYPE_SCHEDULER, + "GstThreadScheduler", &object_info, 0); + } + return object_type; +} + +static void gst_thread_scheduler_setup (GstScheduler * sched); +static void gst_thread_scheduler_reset (GstScheduler * sched); +static GstTask *gst_thread_scheduler_create_task (GstScheduler * sched, + GstTaskFunction func, gpointer data); + +static void +gst_thread_scheduler_class_init (gpointer klass, gpointer class_data) +{ + GstSchedulerClass *scheduler = GST_SCHEDULER_CLASS (klass); + + scheduler->setup = gst_thread_scheduler_setup; + scheduler->reset = gst_thread_scheduler_reset; + scheduler->create_task = gst_thread_scheduler_create_task; +} + +static void +gst_thread_scheduler_func (GstThreadSchedulerTask * ttask, + GstThreadScheduler * sched) +{ + GstTask *task = GST_TASK (ttask); + + gst_object_ref (GST_OBJECT (task)); + + GST_DEBUG_OBJECT (sched, "Entering task %p, thread %p", task, + g_thread_self ()); + + GST_LOCK (task); + while (G_LIKELY (task->state != GST_TASK_STOPPED)) { + while (G_UNLIKELY (task->state == GST_TASK_PAUSED)) { + GST_TASK_SIGNAL (task); + GST_TASK_WAIT (task); + if (task->state == GST_TASK_STOPPED) + goto done; + } + GST_UNLOCK (task); + + task->func (task->data); + + GST_LOCK (task); + } +done: + GST_UNLOCK (task); + + GST_DEBUG_OBJECT (sched, "Exit task %p, thread %p", task, g_thread_self ()); + + gst_object_unref (GST_OBJECT (task)); +} + +static void +gst_thread_scheduler_init (GstThreadScheduler * scheduler) +{ + scheduler->pool = g_thread_pool_new ( + (GFunc) gst_thread_scheduler_func, scheduler, -1, FALSE, NULL); +} + +static GstTask * +gst_thread_scheduler_create_task (GstScheduler * sched, GstTaskFunction func, + gpointer data) +{ + GstThreadSchedulerTask *task; + + task = + GST_THREAD_SCHEDULER_TASK (g_object_new (GST_TYPE_THREAD_SCHEDULER_TASK, + NULL)); + gst_object_set_parent (GST_OBJECT (task), GST_OBJECT (sched)); + GST_TASK_CAST (task)->func = func; + GST_TASK_CAST (task)->data = data; + + GST_DEBUG_OBJECT (sched, "Created task %p", task); + + return GST_TASK_CAST (task); +} + +static void +gst_thread_scheduler_setup (GstScheduler * sched) +{ +} + +static void +gst_thread_scheduler_reset (GstScheduler * sched) +{ +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + GstSchedulerFactory *factory; + + GST_DEBUG_CATEGORY_INIT (debug_scheduler, "thread", 0, "thread scheduler"); + + factory = gst_scheduler_factory_new ("thread", + "A scheduler using threads", GST_TYPE_THREAD_SCHEDULER); + if (factory == NULL) + return FALSE; + + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, "gstthreadscheduler", + "a thread scheduler", plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, + GST_ORIGIN) diff --git a/libs/gst/Makefile.am b/libs/gst/Makefile.am index 86d6564def..e778366c40 100644 --- a/libs/gst/Makefile.am +++ b/libs/gst/Makefile.am @@ -1 +1 @@ -SUBDIRS = bytestream control dataprotocol getbits +SUBDIRS = control dataprotocol getbits diff --git a/libs/gst/bytestream/bytestream.c b/libs/gst/bytestream/bytestream.c index 1954de5c18..760d04a928 100644 --- a/libs/gst/bytestream/bytestream.c +++ b/libs/gst/bytestream/bytestream.c @@ -154,7 +154,7 @@ gst_bytestream_get_next_buf (GstByteStream * bs) return FALSE; GST_DEBUG ("get_next_buf: pulling buffer"); - nextbuf = GST_BUFFER (gst_pad_pull (bs->pad)); + gst_pad_pull (bs->pad, &nextbuf); if (!nextbuf) return FALSE; diff --git a/libs/gst/bytestream/filepad.c b/libs/gst/bytestream/filepad.c index ee93f36574..588e4f396a 100644 --- a/libs/gst/bytestream/filepad.c +++ b/libs/gst/bytestream/filepad.c @@ -39,7 +39,7 @@ GST_BOILERPLATE_FULL (GstFilePad, gst_file_pad, GstRealPad, GST_TYPE_REAL_PAD, static void gst_file_pad_dispose (GObject * object); static void gst_file_pad_finalize (GObject * object); - static void gst_file_pad_chain (GstPad * pad, GstData * data); + //static void gst_file_pad_chain (GstPad * pad, GstData * data); static void gst_file_pad_parent_set (GstObject * object, GstObject * parent); @@ -68,7 +68,7 @@ gst_file_pad_init (GstFilePad * pad) /* must do this for set_chain_function to work */ real->direction = GST_PAD_SINK; - gst_pad_set_chain_function (GST_PAD (real), gst_file_pad_chain); + //gst_pad_set_chain_function (GST_PAD (real), gst_file_pad_chain); pad->adapter = gst_adapter_new (); pad->in_seek = FALSE; @@ -98,6 +98,7 @@ gst_file_pad_finalize (GObject * object) GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); } +#if 0 static void gst_file_pad_chain (GstPad * gst_pad, GstData * data) { @@ -174,6 +175,7 @@ gst_file_pad_chain (GstPad * gst_pad, GstData * data) } } } +#endif static void gst_file_pad_parent_set (GstObject * object, GstObject * parent) @@ -183,10 +185,8 @@ gst_file_pad_parent_set (GstObject * object, GstObject * parent) /* FIXME: we can only be added to elements, right? */ element = GST_ELEMENT (parent); - if (element->loopfunc) - g_warning ("attempt to add a GstFilePad to a loopbased element."); - if (!GST_FLAG_IS_SET (element, GST_ELEMENT_EVENT_AWARE)) - g_warning ("elements using GstFilePad must be event-aware."); + //if (element->loopfunc) + // g_warning ("attempt to add a GstFilePad to a loopbased element."); GST_CALL_PARENT (GST_OBJECT_CLASS, parent_set, (object, parent)); } diff --git a/libs/gst/dataprotocol/dataprotocol.c b/libs/gst/dataprotocol/dataprotocol.c index b271883c37..0ae75af0f0 100644 --- a/libs/gst/dataprotocol/dataprotocol.c +++ b/libs/gst/dataprotocol/dataprotocol.c @@ -324,22 +324,12 @@ gst_dp_packet_from_event (const GstEvent * event, GstDPHeaderFlag flags, return FALSE; case GST_EVENT_EOS: case GST_EVENT_FLUSH: - case GST_EVENT_EMPTY: case GST_EVENT_DISCONTINUOUS: GST_WRITE_UINT64_BE (h + 8, GST_EVENT_TIMESTAMP (event)); pl_length = 0; *payload = NULL; break; case GST_EVENT_SEEK: - pl_length = 4 + 8 + 4; - *payload = g_malloc0 (pl_length); - GST_WRITE_UINT32_BE (*payload, (guint32) GST_EVENT_SEEK_TYPE (event)); - GST_WRITE_UINT64_BE (*payload + 4, - (guint64) GST_EVENT_SEEK_OFFSET (event)); - GST_WRITE_UINT32_BE (*payload + 12, - (guint32) GST_EVENT_SEEK_ACCURACY (event)); - break; - case GST_EVENT_SEEK_SEGMENT: pl_length = 4 + 8 + 8 + 4; *payload = g_malloc0 (pl_length); GST_WRITE_UINT32_BE (*payload, (guint32) GST_EVENT_SEEK_TYPE (event)); @@ -351,12 +341,8 @@ gst_dp_packet_from_event (const GstEvent * event, GstDPHeaderFlag flags, (guint32) GST_EVENT_SEEK_ACCURACY (event)); break; case GST_EVENT_QOS: - case GST_EVENT_SEGMENT_DONE: case GST_EVENT_SIZE: case GST_EVENT_RATE: - case GST_EVENT_FILLER: - case GST_EVENT_TS_OFFSET: - case GST_EVENT_INTERRUPT: case GST_EVENT_NAVIGATION: case GST_EVENT_TAG: g_warning ("Unhandled event type %d, ignoring", GST_EVENT_TYPE (event)); @@ -489,26 +475,11 @@ gst_dp_event_from_packet (guint header_length, const guint8 * header, return FALSE; case GST_EVENT_EOS: case GST_EVENT_FLUSH: - case GST_EVENT_EMPTY: case GST_EVENT_DISCONTINUOUS: event = gst_event_new (type); GST_EVENT_TIMESTAMP (event) = GST_DP_HEADER_TIMESTAMP (header); break; case GST_EVENT_SEEK: - { - GstSeekType type; - gint64 offset; - GstSeekAccuracy accuracy; - - type = (GstSeekType) GST_READ_UINT32_BE (payload); - offset = (gint64) GST_READ_UINT64_BE (payload + 4); - accuracy = (GstSeekAccuracy) GST_READ_UINT32_BE (payload + 12); - event = gst_event_new_seek (type, offset); - GST_EVENT_TIMESTAMP (event) = GST_DP_HEADER_TIMESTAMP (header); - GST_EVENT_SEEK_ACCURACY (event) = accuracy; - break; - } - case GST_EVENT_SEEK_SEGMENT: { GstSeekType type; gint64 offset, endoffset; @@ -524,12 +495,8 @@ gst_dp_event_from_packet (guint header_length, const guint8 * header, break; } case GST_EVENT_QOS: - case GST_EVENT_SEGMENT_DONE: case GST_EVENT_SIZE: case GST_EVENT_RATE: - case GST_EVENT_FILLER: - case GST_EVENT_TS_OFFSET: - case GST_EVENT_INTERRUPT: case GST_EVENT_NAVIGATION: case GST_EVENT_TAG: g_warning ("Unhandled event type %d, ignoring", GST_EVENT_TYPE (event)); diff --git a/plugins/elements/Makefile.am b/plugins/elements/Makefile.am index dc18de4fc0..db5cb1fcea 100644 --- a/plugins/elements/Makefile.am +++ b/plugins/elements/Makefile.am @@ -24,23 +24,24 @@ endif libgstelements_la_DEPENDENCIES = ../libgstreamer-@GST_MAJORMINOR@.la libgstelements_la_SOURCES = \ - gstaggregator.c \ - gstbufferstore.c \ - gstelements.c \ - gstfakesink.c \ gstfakesrc.c \ - gstfilesink.c \ + gstfakesink.c \ gstfilesrc.c \ - gstfdsink.c \ - gstfdsrc.c \ gstidentity.c \ - gstmd5sink.c \ - $(multifilesrc) \ - $(pipefilter) \ - gstshaper.c \ - gststatistics.c \ - gsttee.c \ - gsttypefindelement.c + gstelements.c \ + gsttee.c + +# gstaggregator.c \ +# gstbufferstore.c \ +# gstfilesink.c \ +# gstfdsink.c \ +# gstfdsrc.c \ +# gstmd5sink.c \ +# $(multifilesrc) \ +# $(pipefilter) \ +# gstshaper.c \ +# gststatistics.c \ +# gsttypefindelement.c libgstelements_la_CFLAGS = $(GST_OBJ_CFLAGS) libgstelements_la_LIBADD = $(GST_OBJ_LIBS) diff --git a/plugins/elements/gstelements.c b/plugins/elements/gstelements.c index af4bcb1f0b..2099c74e07 100644 --- a/plugins/elements/gstelements.c +++ b/plugins/elements/gstelements.c @@ -55,24 +55,24 @@ extern GType gst_filesrc_get_type (void); extern GstElementDetails gst_filesrc_details; static struct _elements_entry _elements[] = { - {"aggregator", GST_RANK_NONE, gst_aggregator_get_type}, +// {"aggregator", GST_RANK_NONE, gst_aggregator_get_type}, {"fakesrc", GST_RANK_NONE, gst_fakesrc_get_type}, {"fakesink", GST_RANK_NONE, gst_fakesink_get_type}, - {"fdsink", GST_RANK_NONE, gst_fdsink_get_type}, - {"fdsrc", GST_RANK_NONE, gst_fdsrc_get_type}, {"filesrc", GST_RANK_NONE, gst_filesrc_get_type}, - {"filesink", GST_RANK_NONE, gst_filesink_get_type}, {"identity", GST_RANK_NONE, gst_identity_get_type}, - {"md5sink", GST_RANK_NONE, gst_md5sink_get_type}, +// {"fdsink", GST_RANK_NONE, gst_fdsink_get_type}, +// {"fdsrc", GST_RANK_NONE, gst_fdsrc_get_type}, +// {"filesink", GST_RANK_NONE, gst_filesink_get_type}, +// {"md5sink", GST_RANK_NONE, gst_md5sink_get_type}, #ifndef HAVE_WIN32 - {"multifilesrc", GST_RANK_NONE, gst_multifilesrc_get_type}, - {"pipefilter", GST_RANK_NONE, gst_pipefilter_get_type}, +// {"multifilesrc", GST_RANK_NONE, gst_multifilesrc_get_type}, +// {"pipefilter", GST_RANK_NONE, gst_pipefilter_get_type}, #endif - {"shaper", GST_RANK_NONE, gst_shaper_get_type}, - {"statistics", GST_RANK_NONE, gst_statistics_get_type}, +// {"shaper", GST_RANK_NONE, gst_shaper_get_type}, +// {"statistics", GST_RANK_NONE, gst_statistics_get_type}, {"tee", GST_RANK_NONE, gst_tee_get_type}, - {"typefind", GST_RANK_NONE, gst_type_find_element_get_type}, - {NULL, 0}, +// {"typefind", GST_RANK_NONE, gst_type_find_element_get_type}, +// {NULL, 0}, }; static gboolean diff --git a/plugins/elements/gstfakesink.c b/plugins/elements/gstfakesink.c index fcf64b5d30..5491678618 100644 --- a/plugins/elements/gstfakesink.c +++ b/plugins/elements/gstfakesink.c @@ -113,7 +113,8 @@ static void gst_fakesink_get_property (GObject * object, guint prop_id, static GstElementStateReturn gst_fakesink_change_state (GstElement * element); -static void gst_fakesink_chain (GstPad * pad, GstData * _data); +static GstFlowReturn gst_fakesink_chain (GstPad * pad, GstBuffer * buffer); +static gboolean gst_fakesink_event (GstPad * pad, GstEvent * event); static guint gst_fakesink_signals[LAST_SIGNAL] = { 0 }; @@ -188,6 +189,7 @@ gst_fakesink_init (GstFakeSink * fakesink) "sink"); gst_element_add_pad (GST_ELEMENT (fakesink), pad); gst_pad_set_chain_function (pad, GST_DEBUG_FUNCPTR (gst_fakesink_chain)); + gst_pad_set_event_function (pad, GST_DEBUG_FUNCPTR (gst_fakesink_event)); fakesink->silent = FALSE; fakesink->dump = FALSE; @@ -195,8 +197,6 @@ gst_fakesink_init (GstFakeSink * fakesink) fakesink->last_message = NULL; fakesink->state_error = FAKESINK_STATE_ERROR_NONE; fakesink->signal_handoffs = FALSE; - - GST_FLAG_SET (fakesink, GST_ELEMENT_EVENT_AWARE); } static void @@ -307,47 +307,43 @@ gst_fakesink_get_property (GObject * object, guint prop_id, GValue * value, } } -static void -gst_fakesink_chain (GstPad * pad, GstData * _data) +static gboolean +gst_fakesink_event (GstPad * pad, GstEvent * event) { - GstBuffer *buf = GST_BUFFER (_data); GstFakeSink *fakesink; - g_return_if_fail (pad != NULL); - g_return_if_fail (GST_IS_PAD (pad)); - g_return_if_fail (buf != NULL); - fakesink = GST_FAKESINK (GST_OBJECT_PARENT (pad)); - if (GST_IS_EVENT (buf)) { - GstEvent *event = GST_EVENT (buf); + if (!fakesink->silent) { + g_free (fakesink->last_message); - if (!fakesink->silent) { - g_free (fakesink->last_message); + fakesink->last_message = + g_strdup_printf ("chain ******* (%s:%s)E (type: %d) %p", + GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE (event), event); - fakesink->last_message = - g_strdup_printf ("chain ******* (%s:%s)E (type: %d) %p", - GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE (event), event); - - g_object_notify (G_OBJECT (fakesink), "last_message"); - } - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_DISCONTINUOUS: - if (fakesink->sync && fakesink->clock) { - gint64 value = GST_EVENT_DISCONT_OFFSET (event, 0).value; - - gst_element_set_time (GST_ELEMENT (fakesink), value); - } - default: - gst_pad_event_default (pad, event); - break; - } - return; + g_object_notify (G_OBJECT (fakesink), "last_message"); } + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_DISCONTINUOUS: + default: + gst_pad_event_default (pad, event); + break; + } + + return TRUE; +} + +static GstFlowReturn +gst_fakesink_chain (GstPad * pad, GstBuffer * buffer) +{ + GstBuffer *buf = GST_BUFFER (buffer); + GstFakeSink *fakesink; + + fakesink = GST_FAKESINK (GST_OBJECT_PARENT (pad)); + if (fakesink->sync && fakesink->clock) { - gst_element_wait (GST_ELEMENT (fakesink), GST_BUFFER_TIMESTAMP (buf)); + //gst_element_wait (GST_ELEMENT (fakesink), GST_BUFFER_TIMESTAMP (buf)); } if (!fakesink->silent) { @@ -374,6 +370,8 @@ gst_fakesink_chain (GstPad * pad, GstData * _data) } gst_buffer_unref (buf); + + return GST_FLOW_OK; } static GstElementStateReturn diff --git a/plugins/elements/gstfakesrc.c b/plugins/elements/gstfakesrc.c index 82bb94f678..cb5f7dc210 100644 --- a/plugins/elements/gstfakesrc.c +++ b/plugins/elements/gstfakesrc.c @@ -64,7 +64,8 @@ enum { ARG_0, ARG_NUM_SOURCES, - ARG_LOOP_BASED, + ARG_HAS_LOOP, + ARG_HAS_GETRANGE, ARG_OUTPUT, ARG_DATA, ARG_SIZETYPE, @@ -179,7 +180,7 @@ GST_BOILERPLATE_FULL (GstFakeSrc, gst_fakesrc, GstElement, GST_TYPE_ELEMENT, static GstPad *gst_fakesrc_request_new_pad (GstElement * element, GstPadTemplate * templ, const gchar * unused); -static void gst_fakesrc_update_functions (GstFakeSrc * src); +static gboolean gst_fakesrc_activate (GstPad * pad, GstActivateMode mode); static void gst_fakesrc_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); static void gst_fakesrc_get_property (GObject * object, guint prop_id, @@ -188,8 +189,9 @@ static void gst_fakesrc_set_clock (GstElement * element, GstClock * clock); static GstElementStateReturn gst_fakesrc_change_state (GstElement * element); -static GstData *gst_fakesrc_get (GstPad * pad); -static void gst_fakesrc_loop (GstElement * element); +static void gst_fakesrc_loop (GstPad * pad); +static GstFlowReturn gst_fakesrc_get_range (GstPad * pad, guint64 offset, + guint length, GstBuffer ** buf); static guint gst_fakesrc_signals[LAST_SIGNAL] = { 0 }; @@ -220,9 +222,14 @@ gst_fakesrc_class_init (GstFakeSrcClass * klass) g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_NUM_SOURCES, g_param_spec_int ("num-sources", "num-sources", "Number of sources", 1, G_MAXINT, 1, G_PARAM_READABLE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LOOP_BASED, - g_param_spec_boolean ("loop-based", "loop-based", - "Enable loop-based operation", FALSE, G_PARAM_READWRITE)); + g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_HAS_LOOP, + g_param_spec_boolean ("has-loop", "has-loop", + "Enable loop-based operation", TRUE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_HAS_GETRANGE, + g_param_spec_boolean ("has-getrange", "has-getrange", + "Enable getrange-based operation", TRUE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_OUTPUT, g_param_spec_enum ("output", "output", "Output method (currently unused)", GST_TYPE_FAKESRC_OUTPUT, FAKESRC_FIRST_LAST_LOOP, G_PARAM_READWRITE)); @@ -300,9 +307,6 @@ gst_fakesrc_init (GstFakeSrc * fakesrc) "src"); gst_element_add_pad (GST_ELEMENT (fakesrc), pad); - fakesrc->loop_based = FALSE; - gst_fakesrc_update_functions (fakesrc); - fakesrc->output = FAKESRC_FIRST_LAST_LOOP; fakesrc->segment_start = -1; fakesrc->segment_end = -1; @@ -324,6 +328,7 @@ gst_fakesrc_init (GstFakeSrc * fakesrc) fakesrc->last_message = NULL; fakesrc->datarate = DEFAULT_DATARATE; fakesrc->sync = DEFAULT_SYNC; + fakesrc->pad_mode = GST_ACTIVATE_NONE; } static void @@ -336,35 +341,6 @@ gst_fakesrc_set_clock (GstElement * element, GstClock * clock) src->clock = clock; } - -static GstPad * -gst_fakesrc_request_new_pad (GstElement * element, GstPadTemplate * templ, - const gchar * unused) -{ - gchar *name; - GstPad *srcpad; - GstFakeSrc *fakesrc; - - g_return_val_if_fail (GST_IS_FAKESRC (element), NULL); - - if (templ->direction != GST_PAD_SRC) { - g_warning ("gstfakesrc: request new pad that is not a SRC pad\n"); - return NULL; - } - - fakesrc = GST_FAKESRC (element); - - name = g_strdup_printf ("src%d", GST_ELEMENT (fakesrc)->numsrcpads); - - srcpad = gst_pad_new_from_template (templ, name); - gst_element_add_pad (GST_ELEMENT (fakesrc), srcpad); - gst_fakesrc_update_functions (fakesrc); - - g_free (name); - - return srcpad; -} - static const GstFormat * gst_fakesrc_get_formats (GstPad * pad) { @@ -419,8 +395,7 @@ static const GstEventMask * gst_fakesrc_get_event_mask (GstPad * pad) { static const GstEventMask masks[] = { - {GST_EVENT_SEEK, GST_SEEK_FLAG_FLUSH}, - {GST_EVENT_SEEK_SEGMENT, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_SEGMENT_LOOP}, + {GST_EVENT_SEEK, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_SEGMENT_LOOP}, {GST_EVENT_FLUSH, 0}, {0, 0}, }; @@ -433,22 +408,16 @@ gst_fakesrc_event_handler (GstPad * pad, GstEvent * event) { GstFakeSrc *src; - src = GST_FAKESRC (gst_pad_get_parent (pad)); + src = GST_FAKESRC (GST_PAD_PARENT (pad)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: - src->buffer_count = GST_EVENT_SEEK_OFFSET (event); - - if (!GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH) { - break; - } - /* else we do a flush too */ - case GST_EVENT_SEEK_SEGMENT: src->segment_start = GST_EVENT_SEEK_OFFSET (event); src->segment_end = GST_EVENT_SEEK_ENDOFFSET (event); src->buffer_count = src->segment_start; src->segment_loop = GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_SEGMENT_LOOP; + src->need_flush = GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH; break; case GST_EVENT_FLUSH: src->need_flush = TRUE; @@ -462,34 +431,61 @@ gst_fakesrc_event_handler (GstPad * pad, GstEvent * event) } static void -gst_fakesrc_update_functions (GstFakeSrc * src) +gst_fakesrc_set_pad_functions (GstFakeSrc * src, GstPad * pad) { - GList *pads; + gst_pad_set_activate_function (pad, gst_fakesrc_activate); + gst_pad_set_event_function (pad, gst_fakesrc_event_handler); + gst_pad_set_event_mask_function (pad, gst_fakesrc_get_event_mask); + gst_pad_set_query_function (pad, gst_fakesrc_query); + gst_pad_set_query_type_function (pad, gst_fakesrc_get_query_types); + gst_pad_set_formats_function (pad, gst_fakesrc_get_formats); - if (src->loop_based) { - gst_element_set_loop_function (GST_ELEMENT (src), - GST_DEBUG_FUNCPTR (gst_fakesrc_loop)); - } else { - gst_element_set_loop_function (GST_ELEMENT (src), NULL); + if (src->has_loop) + gst_pad_set_loop_function (pad, gst_fakesrc_loop); + else + gst_pad_set_loop_function (pad, NULL); + + if (src->has_getrange) + gst_pad_set_getrange_function (pad, gst_fakesrc_get_range); + else + gst_pad_set_getrange_function (pad, NULL); +} + +static void +gst_fakesrc_set_all_pad_functions (GstFakeSrc * src) +{ + GList *l; + + for (l = GST_ELEMENT_PADS (src); l; l = l->next) + gst_fakesrc_set_pad_functions (src, (GstPad *) l->data); +} + +static GstPad * +gst_fakesrc_request_new_pad (GstElement * element, GstPadTemplate * templ, + const gchar * unused) +{ + gchar *name; + GstPad *srcpad; + GstFakeSrc *fakesrc; + + g_return_val_if_fail (GST_IS_FAKESRC (element), NULL); + + if (templ->direction != GST_PAD_SRC) { + g_warning ("gstfakesrc: request new pad that is not a SRC pad\n"); + return NULL; } - pads = GST_ELEMENT (src)->pads; - while (pads) { - GstPad *pad = GST_PAD (pads->data); + fakesrc = GST_FAKESRC (element); - if (src->loop_based) { - gst_pad_set_get_function (pad, NULL); - } else { - gst_pad_set_get_function (pad, GST_DEBUG_FUNCPTR (gst_fakesrc_get)); - } + name = g_strdup_printf ("src%d", GST_ELEMENT (fakesrc)->numsrcpads); - gst_pad_set_event_function (pad, gst_fakesrc_event_handler); - gst_pad_set_event_mask_function (pad, gst_fakesrc_get_event_mask); - gst_pad_set_query_function (pad, gst_fakesrc_query); - gst_pad_set_query_type_function (pad, gst_fakesrc_get_query_types); - gst_pad_set_formats_function (pad, gst_fakesrc_get_formats); - pads = g_list_next (pads); - } + srcpad = gst_pad_new_from_template (templ, name); + gst_element_add_pad (GST_ELEMENT (fakesrc), srcpad); + gst_fakesrc_set_pad_functions (fakesrc, srcpad); + + g_free (name); + + return srcpad; } static void @@ -511,13 +507,16 @@ gst_fakesrc_set_property (GObject * object, guint prop_id, const GValue * value, { GstFakeSrc *src; - /* it's not null if we got it, but it might not be ours */ src = GST_FAKESRC (object); switch (prop_id) { - case ARG_LOOP_BASED: - src->loop_based = g_value_get_boolean (value); - gst_fakesrc_update_functions (src); + case ARG_HAS_LOOP: + src->has_loop = g_value_get_boolean (value); + gst_fakesrc_set_all_pad_functions (src); + break; + case ARG_HAS_GETRANGE: + src->has_getrange = g_value_get_boolean (value); + gst_fakesrc_set_all_pad_functions (src); break; case ARG_OUTPUT: g_warning ("not yet implemented"); @@ -595,8 +594,11 @@ gst_fakesrc_get_property (GObject * object, guint prop_id, GValue * value, case ARG_NUM_SOURCES: g_value_set_int (value, GST_ELEMENT (src)->numsrcpads); break; - case ARG_LOOP_BASED: - g_value_set_boolean (value, src->loop_based); + case ARG_HAS_LOOP: + g_value_set_boolean (value, src->has_loop); + break; + case ARG_HAS_GETRANGE: + g_value_set_boolean (value, src->has_getrange); break; case ARG_OUTPUT: g_value_set_enum (value, src->output); @@ -789,36 +791,28 @@ gst_fakesrc_create_buffer (GstFakeSrc * src) return buf; } -static GstData * -gst_fakesrc_get (GstPad * pad) +static GstFlowReturn +gst_fakesrc_get_range_unlocked (GstPad * pad, guint64 offset, guint length, + GstBuffer ** ret) { GstFakeSrc *src; GstBuffer *buf; GstClockTime time; - g_return_val_if_fail (pad != NULL, NULL); - src = GST_FAKESRC (GST_OBJECT_PARENT (pad)); - g_return_val_if_fail (GST_IS_FAKESRC (src), NULL); - - if (src->need_flush) { - src->need_flush = FALSE; - return GST_DATA (gst_event_new (GST_EVENT_FLUSH)); - } - if (src->buffer_count == src->segment_end) { if (src->segment_loop) { - return GST_DATA (gst_event_new (GST_EVENT_SEGMENT_DONE)); + //gst_pad_push_event (pad, gst_event_new (GST_EVENT_SEGMENT_DONE)); } else { - gst_element_set_eos (GST_ELEMENT (src)); - return GST_DATA (gst_event_new (GST_EVENT_EOS)); + gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS)); + return GST_FLOW_UNEXPECTED; } } if (src->rt_num_buffers == 0) { - gst_element_set_eos (GST_ELEMENT (src)); - return GST_DATA (gst_event_new (GST_EVENT_EOS)); + gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS)); + return GST_FLOW_UNEXPECTED; } else { if (src->rt_num_buffers > 0) src->rt_num_buffers--; @@ -826,8 +820,8 @@ gst_fakesrc_get (GstPad * pad) if (src->eos) { GST_INFO ("fakesrc is setting eos on pad"); - gst_element_set_eos (GST_ELEMENT (src)); - return GST_DATA (gst_event_new (GST_EVENT_EOS)); + gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS)); + return GST_FLOW_UNEXPECTED; } buf = gst_fakesrc_create_buffer (src); @@ -838,7 +832,7 @@ gst_fakesrc_get (GstPad * pad) if (src->datarate > 0) { time = (src->bytes_sent * GST_SECOND) / src->datarate; if (src->sync) { - gst_element_wait (GST_ELEMENT (src), time); + /* gst_element_wait (GST_ELEMENT (src), time); */ } GST_BUFFER_DURATION (buf) = @@ -866,49 +860,121 @@ gst_fakesrc_get (GstPad * pad) src->bytes_sent += GST_BUFFER_SIZE (buf); - return GST_DATA (buf); + *ret = buf; + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_fakesrc_get_range (GstPad * pad, guint64 offset, guint length, + GstBuffer ** ret) +{ + GstFlowReturn fret; + + g_assert (GST_FAKESRC (GST_OBJECT_PARENT (pad))->pad_mode == + GST_ACTIVATE_PULL); + + GST_STREAM_LOCK (pad); + + fret = gst_fakesrc_get_range_unlocked (pad, offset, length, ret); + + GST_STREAM_UNLOCK (pad); + + return fret; } -/** - * gst_fakesrc_loop: - * @element: the faksesrc to loop - * - * generate an empty buffer and push it to the next element. - */ static void -gst_fakesrc_loop (GstElement * element) +gst_fakesrc_loop (GstPad * pad) { GstFakeSrc *src; - const GList *pads; + GstBuffer *buf = NULL; + GstFlowReturn ret; - g_return_if_fail (element != NULL); - g_return_if_fail (GST_IS_FAKESRC (element)); + src = GST_FAKESRC (GST_OBJECT_PARENT (pad)); - src = GST_FAKESRC (element); + g_assert (src->pad_mode == GST_ACTIVATE_PUSH); - pads = element->pads; - - while (pads) { - GstPad *pad = GST_PAD (pads->data); - GstData *data; - - data = gst_fakesrc_get (pad); - gst_pad_push (pad, data); - - if (src->eos) { - return; - } - - pads = g_list_next (pads); + GST_STREAM_LOCK (pad); + if (src->need_flush) { + src->need_flush = FALSE; + gst_pad_push_event (pad, gst_event_new (GST_EVENT_FLUSH)); } + + ret = gst_fakesrc_get_range_unlocked (pad, src->buffer_count, + DEFAULT_SIZEMAX, &buf); + if (ret != GST_FLOW_OK) { + goto pause; + } + + ret = gst_pad_push (pad, buf); + if (ret != GST_FLOW_OK) { + goto pause; + } + + GST_STREAM_UNLOCK (pad); + return; + +pause: + gst_task_pause (GST_RPAD_TASK (pad)); + GST_STREAM_UNLOCK (pad); + return; +} + +static gboolean +gst_fakesrc_activate (GstPad * pad, GstActivateMode mode) +{ + gboolean result = FALSE; + GstFakeSrc *fakesrc; + + fakesrc = GST_FAKESRC (GST_OBJECT_PARENT (pad)); + + switch (mode) { + case GST_ACTIVATE_PUSH: + /* if we have a scheduler we can start the task */ + g_return_val_if_fail (fakesrc->has_loop, FALSE); + if (GST_ELEMENT_SCHEDULER (fakesrc)) { + GST_STREAM_LOCK (pad); + GST_RPAD_TASK (pad) = + gst_scheduler_create_task (GST_ELEMENT_SCHEDULER (fakesrc), + (GstTaskFunction) gst_fakesrc_loop, pad); + + gst_task_start (GST_RPAD_TASK (pad)); + GST_STREAM_UNLOCK (pad); + result = TRUE; + } + break; + case GST_ACTIVATE_PULL: + g_return_val_if_fail (fakesrc->has_getrange, FALSE); + result = TRUE; + break; + case GST_ACTIVATE_NONE: + /* step 1, unblock clock sync (if any) */ + + /* step 2, make sure streaming finishes */ + GST_STREAM_LOCK (pad); + /* step 3, stop the task */ + if (GST_RPAD_TASK (pad)) { + gst_task_stop (GST_RPAD_TASK (pad)); + gst_object_unref (GST_OBJECT (GST_RPAD_TASK (pad))); + GST_RPAD_TASK (pad) = NULL; + } + GST_STREAM_UNLOCK (pad); + + result = TRUE; + break; + } + + fakesrc->pad_mode = mode; + + return result; } static GstElementStateReturn gst_fakesrc_change_state (GstElement * element) { GstFakeSrc *fakesrc; + GstElementStateReturn result = GST_STATE_FAILURE; - g_return_val_if_fail (GST_IS_FAKESRC (element), GST_STATE_FAILURE); + g_return_val_if_fail (GST_IS_FAKESRC (element), result); fakesrc = GST_FAKESRC (element); @@ -916,6 +982,7 @@ gst_fakesrc_change_state (GstElement * element) case GST_STATE_NULL_TO_READY: break; case GST_STATE_READY_TO_PAUSED: + { fakesrc->buffer_count = 0; fakesrc->pattern_byte = 0x00; fakesrc->need_flush = FALSE; @@ -923,7 +990,14 @@ gst_fakesrc_change_state (GstElement * element) fakesrc->bytes_sent = 0; fakesrc->rt_num_buffers = fakesrc->num_buffers; break; + } case GST_STATE_PAUSED_TO_PLAYING: + break; + } + + result = GST_ELEMENT_CLASS (parent_class)->change_state (element); + + switch (GST_STATE_TRANSITION (element)) { case GST_STATE_PLAYING_TO_PAUSED: break; case GST_STATE_PAUSED_TO_READY: @@ -940,8 +1014,5 @@ gst_fakesrc_change_state (GstElement * element) break; } - if (GST_ELEMENT_CLASS (parent_class)->change_state) - return GST_ELEMENT_CLASS (parent_class)->change_state (element); - - return GST_STATE_SUCCESS; + return result; } diff --git a/plugins/elements/gstfakesrc.h b/plugins/elements/gstfakesrc.h index 5214f0bb7c..a5251b85f3 100644 --- a/plugins/elements/gstfakesrc.h +++ b/plugins/elements/gstfakesrc.h @@ -76,13 +76,15 @@ typedef struct _GstFakeSrcClass GstFakeSrcClass; struct _GstFakeSrc { GstElement element; - gboolean loop_based; + gboolean has_loop; + gboolean has_getrange; gboolean eos; GstFakeSrcOutputType output; GstFakeSrcDataType data; GstFakeSrcSizeType sizetype; GstFakeSrcFillType filltype; + GstActivateMode pad_mode; guint sizemin; guint sizemax; diff --git a/plugins/elements/gstfilesrc.c b/plugins/elements/gstfilesrc.c index cddf90b400..ce9354f5c0 100644 --- a/plugins/elements/gstfilesrc.c +++ b/plugins/elements/gstfilesrc.c @@ -170,13 +170,17 @@ static void gst_filesrc_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static gboolean gst_filesrc_check_filesize (GstFileSrc * src); -static GstData *gst_filesrc_get (GstPad * pad); +static GstFlowReturn gst_filesrc_get (GstPad * pad, GstBuffer ** buffer); +static GstFlowReturn gst_filesrc_getrange (GstPad * pad, guint64 offset, + guint length, GstBuffer ** buffer); static gboolean gst_filesrc_srcpad_event (GstPad * pad, GstEvent * event); static gboolean gst_filesrc_srcpad_query (GstPad * pad, GstQueryType type, GstFormat * format, gint64 * value); +static gboolean gst_filesrc_activate (GstPad * pad, GstActivateMode mode); static GstElementStateReturn gst_filesrc_change_state (GstElement * element); +static GstCaps *gst_filesrc_type_find (GstFileSrc * src); static void gst_filesrc_uri_handler_init (gpointer g_iface, gpointer iface_data); @@ -247,7 +251,8 @@ gst_filesrc_init (GstFileSrc * src) src->srcpad = gst_pad_new_from_template (gst_static_pad_template_get (&srctemplate), "src"); - gst_pad_set_get_function (src->srcpad, gst_filesrc_get); + gst_pad_set_getrange_function (src->srcpad, gst_filesrc_getrange); + gst_pad_set_activate_function (src->srcpad, gst_filesrc_activate); gst_pad_set_event_function (src->srcpad, gst_filesrc_srcpad_event); gst_pad_set_event_mask_function (src->srcpad, gst_filesrc_get_event_mask); gst_pad_set_query_function (src->srcpad, gst_filesrc_srcpad_query); @@ -672,7 +677,7 @@ gst_filesrc_get_read (GstFileSrc * src) if (ret == 0) { GST_DEBUG ("non-regular file hits EOS"); gst_buffer_unref (buf); - gst_element_set_eos (GST_ELEMENT (src)); + //gst_element_set_eos (GST_ELEMENT (src)); return GST_DATA (gst_event_new (GST_EVENT_EOS)); } readsize = ret; @@ -686,20 +691,36 @@ gst_filesrc_get_read (GstFileSrc * src) return GST_DATA (buf); } -static GstData * -gst_filesrc_get (GstPad * pad) +static GstFlowReturn +gst_filesrc_getrange (GstPad * pad, guint64 offset, guint length, + GstBuffer ** buffer) { GstFileSrc *src; - g_return_val_if_fail (pad != NULL, NULL); - src = GST_FILESRC (gst_pad_get_parent (pad)); - g_return_val_if_fail (GST_FLAG_IS_SET (src, GST_FILESRC_OPEN), NULL); + src = GST_FILESRC (GST_PAD_PARENT (pad)); + + src->curoffset = offset; + src->block_size = length; + + return gst_filesrc_get (pad, buffer); +} + +static GstFlowReturn +gst_filesrc_get (GstPad * pad, GstBuffer ** buffer) +{ + GstFileSrc *src; + GstData *data; + + src = GST_FILESRC (GST_PAD_PARENT (pad)); + + g_return_val_if_fail (GST_FLAG_IS_SET (src, GST_FILESRC_OPEN), + GST_FLOW_WRONG_STATE); /* check for flush */ if (src->need_flush) { src->need_flush = FALSE; GST_DEBUG_OBJECT (src, "sending flush"); - return GST_DATA (gst_event_new_flush ()); + gst_pad_push_event (pad, gst_event_new_flush (TRUE)); } /* check for seek */ if (src->need_discont) { @@ -710,7 +731,7 @@ gst_filesrc_get (GstPad * pad) gst_event_new_discontinuous (src->need_discont > 1, GST_FORMAT_BYTES, (guint64) src->curoffset, GST_FORMAT_UNDEFINED); src->need_discont = 0; - return GST_DATA (event); + gst_pad_push_event (pad, event); } /* check for EOF if it's a regular file */ @@ -721,20 +742,33 @@ gst_filesrc_get (GstPad * pad) GST_DEBUG_OBJECT (src, "eos %" G_GINT64_FORMAT " %" G_GINT64_FORMAT, src->curoffset, src->filelen); } - gst_element_set_eos (GST_ELEMENT (src)); - return GST_DATA (gst_event_new (GST_EVENT_EOS)); + //gst_element_set_eos (GST_ELEMENT (src)); + gst_pad_push_event (pad, gst_event_new (GST_EVENT_EOS)); + return GST_FLOW_WRONG_STATE; } } #ifdef HAVE_MMAP if (src->using_mmap) { - return gst_filesrc_get_mmap (src); + data = gst_filesrc_get_mmap (src); } else { - return gst_filesrc_get_read (src); + data = gst_filesrc_get_read (src); } #else - return gst_filesrc_get_read (src); + data = gst_filesrc_get_read (src); #endif + if (data == NULL) { + GST_DEBUG_OBJECT (src, "could not get data"); + return GST_FLOW_ERROR; + } + + if (GST_IS_EVENT (data)) { + gst_pad_push_event (pad, GST_EVENT (data)); + } else { + *buffer = GST_BUFFER (data); + } + + return GST_FLOW_OK; } /* TRUE if the filesize of the file was updated */ @@ -821,6 +855,22 @@ gst_filesrc_open_file (GstFileSrc * src) src->curoffset = 0; GST_FLAG_SET (src, GST_FILESRC_OPEN); + + { + GstCaps *caps; + guint64 offset; + guint length; + + offset = src->curoffset; + length = src->block_size; + + caps = gst_filesrc_type_find (src); + gst_pad_set_caps (src->srcpad, caps); + + src->curoffset = offset; + src->block_size = length; + } + } return TRUE; } @@ -848,10 +898,77 @@ gst_filesrc_close_file (GstFileSrc * src) GST_FLAG_UNSET (src, GST_FILESRC_OPEN); } +static void +gst_filesrc_loop (GstPad * pad) +{ + GstFileSrc *filesrc; + gboolean result; + GstBuffer *buffer; + + filesrc = GST_FILESRC (GST_PAD_PARENT (pad)); + + GST_STREAM_LOCK (pad); + + result = gst_filesrc_get (pad, &buffer); + if (result != GST_FLOW_OK) { + gst_task_pause (GST_RPAD_TASK (pad)); + goto done; + } + result = gst_pad_push (pad, buffer); + if (result != GST_FLOW_OK) { + gst_task_pause (GST_RPAD_TASK (pad)); + } +done: + GST_STREAM_UNLOCK (pad); +} + + +static gboolean +gst_filesrc_activate (GstPad * pad, GstActivateMode mode) +{ + gboolean result = FALSE; + GstFileSrc *filesrc; + + filesrc = GST_FILESRC (GST_OBJECT_PARENT (pad)); + + switch (mode) { + case GST_ACTIVATE_PUSH: + /* if we have a scheduler we can start the task */ + if (GST_ELEMENT_SCHEDULER (filesrc)) { + GST_STREAM_LOCK (pad); + GST_RPAD_TASK (pad) = + gst_scheduler_create_task (GST_ELEMENT_SCHEDULER (filesrc), + (GstTaskFunction) gst_filesrc_loop, pad); + + gst_task_start (GST_RPAD_TASK (pad)); + result = TRUE; + GST_STREAM_UNLOCK (pad); + } + break; + case GST_ACTIVATE_PULL: + result = TRUE; + break; + case GST_ACTIVATE_NONE: + /* step 1, unblock clock sync (if any) */ + + /* step 2, make sure streaming finishes */ + GST_STREAM_LOCK (pad); + /* step 3, stop the task */ + gst_task_stop (GST_RPAD_TASK (pad)); + GST_STREAM_UNLOCK (pad); + + result = TRUE; + break; + } + return result; +} + static GstElementStateReturn gst_filesrc_change_state (GstElement * element) { + GstElementStateReturn result = GST_STATE_SUCCESS; + GstFileSrc *src = GST_FILESRC (element); switch (GST_STATE_TRANSITION (element)) { @@ -866,6 +983,17 @@ gst_filesrc_change_state (GstElement * element) } src->need_discont = 2; break; + case GST_STATE_PAUSED_TO_PLAYING: + gst_task_start (GST_RPAD_TASK (src->srcpad)); + break; + } + + result = GST_ELEMENT_CLASS (parent_class)->change_state (element); + + switch (GST_STATE_TRANSITION (element)) { + case GST_STATE_PLAYING_TO_PAUSED: + gst_task_start (GST_RPAD_TASK (src->srcpad)); + break; case GST_STATE_PAUSED_TO_READY: if (GST_FLAG_IS_SET (element, GST_FILESRC_OPEN)) gst_filesrc_close_file (GST_FILESRC (element)); @@ -874,10 +1002,7 @@ gst_filesrc_change_state (GstElement * element) break; } - if (GST_ELEMENT_CLASS (parent_class)->change_state) - return GST_ELEMENT_CLASS (parent_class)->change_state (element); - - return GST_STATE_SUCCESS; + return result; } static gboolean @@ -1013,6 +1138,90 @@ error: return FALSE; } +typedef struct +{ + GstFileSrc *src; + guint best_probability; + GstCaps *caps; + + GstBuffer *buffer; +} +FileSrcTypeFind; + +static guint8 * +filesrc_find_peek (gpointer data, gint64 offset, guint size) +{ + FileSrcTypeFind *find; + GstBuffer *buffer; + GstFileSrc *src; + + if (size == 0) + return NULL; + + find = (FileSrcTypeFind *) data; + src = find->src; + + if (offset < 0) { + offset += src->filelen; + } + + buffer = NULL; + gst_filesrc_getrange (src->srcpad, offset, size, &buffer); + + if (find->buffer) { + gst_buffer_unref (find->buffer); + } + find->buffer = buffer; + + return GST_BUFFER_DATA (buffer); +} + +static void +filesrc_find_suggest (gpointer data, guint probability, const GstCaps * caps) +{ + FileSrcTypeFind *find = (FileSrcTypeFind *) data; + + if (probability > find->best_probability) { + gst_caps_replace (&find->caps, gst_caps_copy (caps)); + find->best_probability = probability; + } +} + + +static GstCaps * +gst_filesrc_type_find (GstFileSrc * src) +{ + GstTypeFind gst_find; + FileSrcTypeFind find; + GList *walk, *type_list = NULL; + GstCaps *result = NULL; + + walk = type_list = gst_type_find_factory_get_list (); + + find.src = src; + find.best_probability = 0; + find.caps = NULL; + find.buffer = NULL; + gst_find.data = &find; + gst_find.peek = filesrc_find_peek; + gst_find.suggest = filesrc_find_suggest; + gst_find.get_length = NULL; + + while (walk) { + GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (walk->data); + + gst_type_find_factory_call_function (factory, &gst_find); + if (find.best_probability >= GST_TYPE_FIND_MAXIMUM) + break; + walk = g_list_next (walk); + } + + if (find.best_probability > 0) + result = find.caps; + + return result; +} + /*** GSTURIHANDLER INTERFACE *************************************************/ static guint diff --git a/plugins/elements/gstidentity.c b/plugins/elements/gstidentity.c index 909f4b9a95..0b8a6d2f7e 100644 --- a/plugins/elements/gstidentity.c +++ b/plugins/elements/gstidentity.c @@ -1,6 +1,7 @@ /* GStreamer * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans + * 2005 Wim Taymans * * gstidentity.c: * @@ -71,21 +72,28 @@ enum enum { - ARG_0, - ARG_LOOP_BASED, - ARG_SLEEP_TIME, - ARG_DUPLICATE, - ARG_ERROR_AFTER, - ARG_DROP_PROBABILITY, - ARG_DATARATE, - ARG_SILENT, - ARG_LAST_MESSAGE, - ARG_DUMP, - ARG_SYNC, - ARG_CHECK_PERFECT + PROP_0, + PROP_HAS_GETRANGE, + PROP_HAS_CHAIN, + PROP_HAS_SINK_LOOP, + PROP_HAS_SRC_LOOP, + PROP_LOOP_BASED, + PROP_SLEEP_TIME, + PROP_DUPLICATE, + PROP_ERROR_AFTER, + PROP_DROP_PROBABILITY, + PROP_DATARATE, + PROP_SILENT, + PROP_LAST_MESSAGE, + PROP_DUMP, + PROP_SYNC, + PROP_CHECK_PERFECT }; +typedef GstFlowReturn (*IdentityPushFunc) (GstIdentity *, GstBuffer *); + + #define _do_init(bla) \ GST_DEBUG_CATEGORY_INIT (gst_identity_debug, "identity", 0, "identity element"); @@ -99,8 +107,16 @@ static void gst_identity_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static GstElementStateReturn gst_identity_change_state (GstElement * element); -static void gst_identity_chain (GstPad * pad, GstData * _data); +static gboolean gst_identity_event (GstPad * pad, GstEvent * event); +static GstFlowReturn gst_identity_getrange (GstPad * pad, guint64 offset, + guint length, GstBuffer ** buffer); +static GstFlowReturn gst_identity_chain (GstPad * pad, GstBuffer * buffer); +static void gst_identity_src_loop (GstPad * pad); +static void gst_identity_sink_loop (GstPad * pad); +static GstFlowReturn gst_identity_handle_buffer (GstIdentity * identity, + GstBuffer * buf); static void gst_identity_set_clock (GstElement * element, GstClock * clock); +static GstCaps *gst_identity_proxy_getcaps (GstPad * pad); static guint gst_identity_signals[LAST_SIGNAL] = { 0 }; @@ -124,6 +140,9 @@ gst_identity_finalize (GObject * object) identity = GST_IDENTITY (object); + g_mutex_free (identity->pen_lock); + g_cond_free (identity->pen_cond); + g_free (identity->last_message); G_OBJECT_CLASS (parent_class)->finalize (object); @@ -141,42 +160,54 @@ gst_identity_class_init (GstIdentityClass * klass) gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_identity_set_property); gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_identity_get_property); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LOOP_BASED, - g_param_spec_boolean ("loop-based", "Loop-based", - "Set to TRUE to use loop-based rather than chain-based scheduling", - DEFAULT_LOOP_BASED, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SLEEP_TIME, + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HAS_GETRANGE, + g_param_spec_boolean ("has-getrange", "Has getrange", + "If the src pad will implement a getrange function", + TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HAS_CHAIN, + g_param_spec_boolean ("has-chain", "Has chain", + "If the sink pad will implement a chain function", + TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HAS_SRC_LOOP, + g_param_spec_boolean ("has-src-loop", "Has src loop", + "If the src pad will implement a loop function", + FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HAS_SINK_LOOP, + g_param_spec_boolean ("has-sink-loop", "Has sink loop", + "If the sink pad will implement a loop function", + FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SLEEP_TIME, g_param_spec_uint ("sleep-time", "Sleep time", "Microseconds to sleep between processing", 0, G_MAXUINT, DEFAULT_SLEEP_TIME, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DUPLICATE, + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DUPLICATE, g_param_spec_uint ("duplicate", "Duplicate Buffers", "Push the buffers N times", 0, G_MAXUINT, DEFAULT_DUPLICATE, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ERROR_AFTER, + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_ERROR_AFTER, g_param_spec_int ("error_after", "Error After", "Error after N buffers", G_MININT, G_MAXINT, DEFAULT_ERROR_AFTER, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DROP_PROBABILITY, - g_param_spec_float ("drop_probability", "Drop Probability", - "The Probability a buffer is dropped", 0.0, 1.0, + g_object_class_install_property (G_OBJECT_CLASS (klass), + PROP_DROP_PROBABILITY, g_param_spec_float ("drop_probability", + "Drop Probability", "The Probability a buffer is dropped", 0.0, 1.0, DEFAULT_DROP_PROBABILITY, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DATARATE, + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DATARATE, g_param_spec_int ("datarate", "Datarate", "(Re)timestamps buffers with number of bytes per second (0 = inactive)", 0, G_MAXINT, DEFAULT_DATARATE, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SILENT, + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SILENT, g_param_spec_boolean ("silent", "silent", "silent", DEFAULT_SILENT, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LAST_MESSAGE, + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LAST_MESSAGE, g_param_spec_string ("last-message", "last-message", "last-message", NULL, G_PARAM_READABLE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DUMP, + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DUMP, g_param_spec_boolean ("dump", "Dump", "Dump buffer contents", DEFAULT_DUMP, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SYNC, + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SYNC, g_param_spec_boolean ("sync", "Synchronize", "Synchronize to pipeline clock", DEFAULT_SYNC, G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_CHECK_PERFECT, + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_CHECK_PERFECT, g_param_spec_boolean ("check-perfect", "Check For Perfect Stream", "Verify that the stream is time- and data-contiguous", DEFAULT_CHECK_PERFECT, G_PARAM_READWRITE)); @@ -202,19 +233,20 @@ gst_identity_init (GstIdentity * identity) gst_pad_new_from_template (gst_static_pad_template_get (&sinktemplate), "sink"); gst_element_add_pad (GST_ELEMENT (identity), identity->sinkpad); - gst_pad_set_chain_function (identity->sinkpad, - GST_DEBUG_FUNCPTR (gst_identity_chain)); - //gst_pad_set_link_function (identity->sinkpad, gst_pad_proxy_pad_link); - gst_pad_set_getcaps_function (identity->sinkpad, gst_pad_proxy_getcaps); + gst_pad_set_getcaps_function (identity->sinkpad, + GST_DEBUG_FUNCPTR (gst_identity_proxy_getcaps)); + gst_pad_set_event_function (identity->sinkpad, + GST_DEBUG_FUNCPTR (gst_identity_event)); identity->srcpad = gst_pad_new_from_template (gst_static_pad_template_get (&srctemplate), "src"); + gst_pad_set_getcaps_function (identity->srcpad, + GST_DEBUG_FUNCPTR (gst_identity_proxy_getcaps)); + gst_pad_set_getrange_function (identity->srcpad, + GST_DEBUG_FUNCPTR (gst_identity_getrange)); gst_element_add_pad (GST_ELEMENT (identity), identity->srcpad); - //gst_pad_set_link_function (identity->srcpad, gst_pad_proxy_pad_link); - gst_pad_set_getcaps_function (identity->srcpad, gst_pad_proxy_getcaps); - identity->loop_based = DEFAULT_LOOP_BASED; identity->sleep_time = DEFAULT_SLEEP_TIME; identity->duplicate = DEFAULT_DUPLICATE; identity->error_after = DEFAULT_ERROR_AFTER; @@ -227,7 +259,10 @@ gst_identity_init (GstIdentity * identity) identity->last_message = NULL; identity->srccaps = NULL; - GST_FLAG_SET (identity, GST_ELEMENT_EVENT_AWARE); + identity->pen_data = NULL; + identity->pen_lock = g_mutex_new (); + identity->pen_cond = g_cond_new (); + identity->pen_flushing = FALSE; } static void @@ -238,36 +273,230 @@ gst_identity_set_clock (GstElement * element, GstClock * clock) gst_object_replace ((GstObject **) & identity->clock, (GstObject *) clock); } +static GstCaps * +gst_identity_proxy_getcaps (GstPad * pad) +{ + GstPad *otherpad; + GstIdentity *identity = GST_IDENTITY (GST_OBJECT_PARENT (pad)); + + otherpad = pad == identity->srcpad ? identity->sinkpad : identity->srcpad; + + return gst_pad_peer_get_caps (otherpad); +} + +static gboolean +identity_queue_push (GstIdentity * identity, GstData * data) +{ + gboolean ret; + + g_mutex_lock (identity->pen_lock); + while (identity->pen_data && !identity->pen_flushing) + g_cond_wait (identity->pen_cond, identity->pen_lock); + if (identity->pen_flushing) { + gst_data_unref (identity->pen_data); + identity->pen_data = NULL; + gst_data_unref (data); + ret = FALSE; + } else { + identity->pen_data = data; + ret = TRUE; + } + g_cond_signal (identity->pen_cond); + g_mutex_unlock (identity->pen_lock); + + return ret; +} + +static GstData * +identity_queue_pop (GstIdentity * identity) +{ + GstData *ret; + + g_mutex_lock (identity->pen_lock); + while (!(ret = identity->pen_data) && !identity->pen_flushing) + g_cond_wait (identity->pen_cond, identity->pen_lock); + g_cond_signal (identity->pen_cond); + g_mutex_unlock (identity->pen_lock); + + return ret; +} static void -gst_identity_chain (GstPad * pad, GstData * _data) +identity_queue_flush (GstIdentity * identity) +{ + g_mutex_lock (identity->pen_lock); + identity->pen_flushing = TRUE; + g_cond_signal (identity->pen_cond); + g_mutex_unlock (identity->pen_lock); +} + +static gboolean +gst_identity_event (GstPad * pad, GstEvent * event) { - GstBuffer *buf = GST_BUFFER (_data); GstIdentity *identity; - guint i; + gboolean ret; - g_return_if_fail (pad != NULL); - g_return_if_fail (GST_IS_PAD (pad)); - g_return_if_fail (buf != NULL); + identity = GST_IDENTITY (GST_PAD_PARENT (pad)); - identity = GST_IDENTITY (gst_pad_get_parent (pad)); + GST_STREAM_LOCK (pad); - if (GST_IS_EVENT (buf)) { - GstEvent *event = GST_EVENT (buf); + if (!identity->silent) { + g_free (identity->last_message); - if (!identity->silent) { - g_free (identity->last_message); + identity->last_message = + g_strdup_printf ("chain ******* (%s:%s)E (type: %d) %p", + GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE (event), event); - identity->last_message = - g_strdup_printf ("chain ******* (%s:%s)E (type: %d) %p", - GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE (event), event); - - g_object_notify (G_OBJECT (identity), "last_message"); - } - gst_pad_event_default (pad, event); - return; + g_object_notify (G_OBJECT (identity), "last_message"); } + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH: + /* forward event */ + gst_pad_event_default (pad, event); + if (GST_EVENT_FLUSH_DONE (event)) { + if (identity->sink_mode == GST_ACTIVATE_PULL) { + /* already have the sink stream lock */ + gst_task_start (GST_RPAD_TASK (identity->sinkpad)); + } + if (identity->src_mode == GST_ACTIVATE_PUSH) { + GST_STREAM_LOCK (identity->srcpad); + gst_task_start (GST_RPAD_TASK (identity->srcpad)); + GST_STREAM_UNLOCK (identity->srcpad); + } + } else { + /* unblock both functions */ + identity_queue_flush (identity); + + } + ret = TRUE; + goto done; + case GST_EVENT_EOS: + if (identity->sink_mode == GST_ACTIVATE_PULL) { + /* already have the sink stream lock */ + gst_task_pause (GST_RPAD_TASK (identity->sinkpad)); + } + break; + default: + break; + } + + if (identity->decoupled) { + ret = identity_queue_push (identity, (GstData *) event); + } else { + ret = gst_pad_push_event (identity->srcpad, event); + } + +done: + GST_STREAM_UNLOCK (pad); + return ret; +} + +static GstFlowReturn +gst_identity_getrange (GstPad * pad, guint64 offset, + guint length, GstBuffer ** buffer) +{ + GstIdentity *identity; + GstFlowReturn ret; + + identity = GST_IDENTITY (GST_PAD_PARENT (pad)); + + GST_STREAM_LOCK (pad); + + ret = gst_pad_pull_range (identity->sinkpad, offset, length, buffer); + + GST_STREAM_UNLOCK (pad); + + return ret; +} + +static GstFlowReturn +gst_identity_chain (GstPad * pad, GstBuffer * buffer) +{ + GstIdentity *identity; + GstFlowReturn ret = GST_FLOW_OK; + + identity = GST_IDENTITY (GST_PAD_PARENT (pad)); + + GST_STREAM_LOCK (pad); + + ret = gst_identity_handle_buffer (identity, buffer); + + GST_STREAM_UNLOCK (pad); + + return ret; +} + +#define DEFAULT_PULL_SIZE 1024 + +static void +gst_identity_sink_loop (GstPad * pad) +{ + GstIdentity *identity; + GstBuffer *buffer; + GstFlowReturn ret; + + identity = GST_IDENTITY (GST_PAD_PARENT (pad)); + + GST_STREAM_LOCK (pad); + + ret = gst_pad_pull_range (pad, identity->offset, DEFAULT_PULL_SIZE, &buffer); + if (ret != GST_FLOW_OK) + goto sink_loop_pause; + + ret = gst_identity_handle_buffer (identity, buffer); + if (ret != GST_FLOW_OK) + goto sink_loop_pause; + + GST_STREAM_UNLOCK (pad); + return; + +sink_loop_pause: + gst_task_pause (GST_RPAD_TASK (identity->sinkpad)); + GST_STREAM_UNLOCK (pad); + return; +} + +static void +gst_identity_src_loop (GstPad * pad) +{ + GstIdentity *identity; + GstData *data; + GstFlowReturn ret; + + identity = GST_IDENTITY (GST_PAD_PARENT (pad)); + + GST_STREAM_LOCK (pad); + + data = identity_queue_pop (identity); + if (!data) /* we're getting flushed */ + goto src_loop_pause; + + if (GST_IS_EVENT (data)) { + if (GST_EVENT_TYPE (data) == GST_EVENT_EOS) + gst_task_pause (GST_RPAD_TASK (identity->srcpad)); + gst_pad_push_event (identity->srcpad, GST_EVENT (data)); + } else { + ret = gst_pad_push (identity->srcpad, (GstBuffer *) data); + if (ret != GST_FLOW_OK) + goto src_loop_pause; + } + + GST_STREAM_UNLOCK (pad); + return; + +src_loop_pause: + gst_task_pause (GST_RPAD_TASK (identity->srcpad)); + GST_STREAM_UNLOCK (pad); + return; +} + +static GstFlowReturn +gst_identity_handle_buffer (GstIdentity * identity, GstBuffer * buf) +{ + GstFlowReturn ret = GST_FLOW_OK; + guint i; + /* see if we need to do perfect stream checking */ /* invalid timestamp drops us out of check. FIXME: maybe warn ? */ if (identity->check_perfect && @@ -303,7 +532,7 @@ gst_identity_chain (GstPad * pad, GstData * _data) gst_buffer_unref (buf); GST_ELEMENT_ERROR (identity, CORE, FAILED, (_("Failed after iterations as requested.")), (NULL)); - return; + return GST_FLOW_ERROR; } } @@ -320,9 +549,10 @@ gst_identity_chain (GstPad * pad, GstData * _data) GST_BUFFER_OFFSET_END (buf), GST_BUFFER_FLAGS (buf), buf); g_object_notify (G_OBJECT (identity), "last-message"); gst_buffer_unref (buf); - return; + return GST_FLOW_OK; } } + if (identity->dump) { gst_util_dump_mem (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf)); } @@ -346,7 +576,7 @@ gst_identity_chain (GstPad * pad, GstData * _data) time = GST_BUFFER_TIMESTAMP (buf); if (identity->datarate > 0) { - time = identity->bytes_handled * GST_SECOND / identity->datarate; + time = identity->offset * GST_SECOND / identity->datarate; GST_BUFFER_TIMESTAMP (buf) = time; GST_BUFFER_DURATION (buf) = @@ -361,41 +591,49 @@ gst_identity_chain (GstPad * pad, GstData * _data) if (identity->sync) { if (identity->clock) { - gst_element_wait (GST_ELEMENT (identity), time); + /* gst_element_wait (GST_ELEMENT (identity), time); */ } } - identity->bytes_handled += GST_BUFFER_SIZE (buf); - gst_pad_push (identity->srcpad, GST_DATA (buf)); + identity->offset += GST_BUFFER_SIZE (buf); + if (identity->decoupled) { + if (!identity_queue_push (identity, (GstData *) buf)) + return GST_FLOW_UNEXPECTED; + } else { + ret = gst_pad_push (identity->srcpad, buf); + if (ret != GST_FLOW_OK) + return ret; + } if (identity->sleep_time) g_usleep (identity->sleep_time); } + + return ret; } static void -gst_identity_loop (GstElement * element) +gst_identity_set_dataflow_funcs (GstIdentity * identity) { - GstIdentity *identity; - GstBuffer *buf; + if (identity->has_getrange) + gst_pad_set_getrange_function (identity->srcpad, gst_identity_getrange); + else + gst_pad_set_getrange_function (identity->srcpad, NULL); - g_return_if_fail (element != NULL); - g_return_if_fail (GST_IS_IDENTITY (element)); + if (identity->has_chain) + gst_pad_set_chain_function (identity->sinkpad, gst_identity_chain); + else + gst_pad_set_chain_function (identity->sinkpad, NULL); - identity = GST_IDENTITY (element); + if (identity->has_src_loop) + gst_pad_set_loop_function (identity->srcpad, gst_identity_src_loop); + else + gst_pad_set_loop_function (identity->srcpad, NULL); - buf = GST_BUFFER (gst_pad_pull (identity->sinkpad)); - if (GST_IS_EVENT (buf)) { - GstEvent *event = GST_EVENT (buf); - - if (GST_EVENT_IS_INTERRUPT (event)) { - gst_event_unref (event); - } else { - gst_pad_event_default (identity->sinkpad, event); - } - } else { - gst_identity_chain (identity->sinkpad, GST_DATA (buf)); - } + if (identity->has_sink_loop) + gst_pad_set_loop_function (identity->sinkpad, gst_identity_sink_loop); + else + gst_pad_set_loop_function (identity->sinkpad, NULL); } static void @@ -404,48 +642,50 @@ gst_identity_set_property (GObject * object, guint prop_id, { GstIdentity *identity; - /* it's not null if we got it, but it might not be ours */ - g_return_if_fail (GST_IS_IDENTITY (object)); - identity = GST_IDENTITY (object); switch (prop_id) { - case ARG_LOOP_BASED: - identity->loop_based = g_value_get_boolean (value); - if (identity->loop_based) { - gst_element_set_loop_function (GST_ELEMENT (identity), - gst_identity_loop); - gst_pad_set_chain_function (identity->sinkpad, NULL); - } else { - gst_pad_set_chain_function (identity->sinkpad, gst_identity_chain); - gst_element_set_loop_function (GST_ELEMENT (identity), NULL); - } + case PROP_HAS_GETRANGE: + identity->has_getrange = g_value_get_boolean (value); + gst_identity_set_dataflow_funcs (identity); break; - case ARG_SLEEP_TIME: + case PROP_HAS_CHAIN: + identity->has_chain = g_value_get_boolean (value); + gst_identity_set_dataflow_funcs (identity); + break; + case PROP_HAS_SRC_LOOP: + identity->has_src_loop = g_value_get_boolean (value); + gst_identity_set_dataflow_funcs (identity); + break; + case PROP_HAS_SINK_LOOP: + identity->has_sink_loop = g_value_get_boolean (value); + gst_identity_set_dataflow_funcs (identity); + break; + case PROP_SLEEP_TIME: identity->sleep_time = g_value_get_uint (value); break; - case ARG_SILENT: + case PROP_SILENT: identity->silent = g_value_get_boolean (value); break; - case ARG_DUPLICATE: + case PROP_DUPLICATE: identity->duplicate = g_value_get_uint (value); break; - case ARG_DUMP: + case PROP_DUMP: identity->dump = g_value_get_boolean (value); break; - case ARG_ERROR_AFTER: + case PROP_ERROR_AFTER: identity->error_after = g_value_get_int (value); break; - case ARG_DROP_PROBABILITY: + case PROP_DROP_PROBABILITY: identity->drop_probability = g_value_get_float (value); break; - case ARG_DATARATE: + case PROP_DATARATE: identity->datarate = g_value_get_int (value); break; - case ARG_SYNC: + case PROP_SYNC: identity->sync = g_value_get_boolean (value); break; - case ARG_CHECK_PERFECT: + case PROP_CHECK_PERFECT: identity->check_perfect = g_value_get_boolean (value); break; default: @@ -460,43 +700,49 @@ gst_identity_get_property (GObject * object, guint prop_id, GValue * value, { GstIdentity *identity; - /* it's not null if we got it, but it might not be ours */ - g_return_if_fail (GST_IS_IDENTITY (object)); - identity = GST_IDENTITY (object); switch (prop_id) { - case ARG_LOOP_BASED: - g_value_set_boolean (value, identity->loop_based); + case PROP_HAS_GETRANGE: + g_value_set_boolean (value, identity->has_getrange); break; - case ARG_SLEEP_TIME: + case PROP_HAS_CHAIN: + g_value_set_boolean (value, identity->has_chain); + break; + case PROP_HAS_SRC_LOOP: + g_value_set_boolean (value, identity->has_src_loop); + break; + case PROP_HAS_SINK_LOOP: + g_value_set_boolean (value, identity->has_sink_loop); + break; + case PROP_SLEEP_TIME: g_value_set_uint (value, identity->sleep_time); break; - case ARG_DUPLICATE: + case PROP_DUPLICATE: g_value_set_uint (value, identity->duplicate); break; - case ARG_ERROR_AFTER: + case PROP_ERROR_AFTER: g_value_set_int (value, identity->error_after); break; - case ARG_DROP_PROBABILITY: + case PROP_DROP_PROBABILITY: g_value_set_float (value, identity->drop_probability); break; - case ARG_DATARATE: + case PROP_DATARATE: g_value_set_int (value, identity->datarate); break; - case ARG_SILENT: + case PROP_SILENT: g_value_set_boolean (value, identity->silent); break; - case ARG_DUMP: + case PROP_DUMP: g_value_set_boolean (value, identity->dump); break; - case ARG_LAST_MESSAGE: + case PROP_LAST_MESSAGE: g_value_set_string (value, identity->last_message); break; - case ARG_SYNC: + case PROP_SYNC: g_value_set_boolean (value, identity->sync); break; - case ARG_CHECK_PERFECT: + case PROP_CHECK_PERFECT: g_value_set_boolean (value, identity->check_perfect); break; default: @@ -518,7 +764,7 @@ gst_identity_change_state (GstElement * element) case GST_STATE_NULL_TO_READY: break; case GST_STATE_READY_TO_PAUSED: - identity->bytes_handled = 0; + identity->offset = 0; identity->prev_timestamp = GST_CLOCK_TIME_NONE; identity->prev_duration = GST_CLOCK_TIME_NONE; identity->prev_offset_end = -1; diff --git a/plugins/elements/gstidentity.h b/plugins/elements/gstidentity.h index 00203beed5..e187f2e2c2 100644 --- a/plugins/elements/gstidentity.h +++ b/plugins/elements/gstidentity.h @@ -50,7 +50,19 @@ struct _GstIdentity { GstPad *sinkpad; GstPad *srcpad; - gboolean loop_based; + GstData *pen_data; + GMutex *pen_lock; + GCond *pen_cond; + gboolean pen_flushing; + + gboolean has_chain; + gboolean has_getrange; + gboolean has_src_loop; + gboolean has_sink_loop; + GstActivateMode sink_mode; + GstActivateMode src_mode; + gboolean decoupled; + guint duplicate; gint error_after; gfloat drop_probability; @@ -67,7 +79,7 @@ struct _GstIdentity { gchar *last_message; GstCaps *srccaps; - guint64 bytes_handled; + guint64 offset; }; struct _GstIdentityClass { diff --git a/plugins/elements/gstqueue.c b/plugins/elements/gstqueue.c index ac889f4ef2..3999c6d57d 100644 --- a/plugins/elements/gstqueue.c +++ b/plugins/elements/gstqueue.c @@ -2,6 +2,7 @@ * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans * 2003 Colin Walters + * 2005 Wim Taymans * * gstqueue.c: * @@ -26,6 +27,7 @@ #include "gstqueue.h" #include "gstscheduler.h" +#include "gstpipeline.h" #include "gstevent.h" #include "gstinfo.h" #include "gsterror.h" @@ -113,13 +115,6 @@ enum } G_STMT_END -typedef struct _GstQueueEventResponse -{ - GstEvent *event; - gboolean ret, handled; -} -GstQueueEventResponse; - static void gst_queue_base_init (GstQueueClass * klass); static void gst_queue_class_init (GstQueueClass * klass); static void gst_queue_init (GstQueue * queue); @@ -130,21 +125,24 @@ static void gst_queue_set_property (GObject * object, static void gst_queue_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); -static void gst_queue_chain (GstPad * pad, GstData * data); -static GstData *gst_queue_get (GstPad * pad); +static GstFlowReturn gst_queue_chain (GstPad * pad, GstBuffer * buffer); +static GstBuffer *gst_queue_bufferalloc (GstPad * pad, guint64 offset, + guint size, GstCaps * caps); +static void gst_queue_loop (GstPad * pad); + +static gboolean gst_queue_handle_sink_event (GstPad * pad, GstEvent * event); static gboolean gst_queue_handle_src_event (GstPad * pad, GstEvent * event); static gboolean gst_queue_handle_src_query (GstPad * pad, GstQueryType type, GstFormat * fmt, gint64 * value); static GstCaps *gst_queue_getcaps (GstPad * pad); -static GstPadLinkReturn -gst_queue_link_sink (GstPad * pad, const GstCaps * caps); -static GstPadLinkReturn gst_queue_link_src (GstPad * pad, const GstCaps * caps); +static GstPadLinkReturn gst_queue_link_sink (GstPad * pad, GstPad * peer); +static GstPadLinkReturn gst_queue_link_src (GstPad * pad, GstPad * peer); static void gst_queue_locked_flush (GstQueue * queue); +static gboolean gst_queue_src_activate (GstPad * pad, GstActivateMode mode); static GstElementStateReturn gst_queue_change_state (GstElement * element); -static gboolean gst_queue_release_locks (GstElement * element); #define GST_TYPE_QUEUE_LEAKY (queue_leaky_get_type ()) @@ -292,33 +290,32 @@ gst_queue_class_init (GstQueueClass * klass) gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_queue_finalize); gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_queue_change_state); - gstelement_class->release_locks = GST_DEBUG_FUNCPTR (gst_queue_release_locks); } static void gst_queue_init (GstQueue * queue) { - /* scheduling on this kind of element is, well, interesting */ - GST_FLAG_SET (queue, GST_ELEMENT_DECOUPLED); - GST_FLAG_SET (queue, GST_ELEMENT_EVENT_AWARE); - queue->sinkpad = gst_pad_new_from_template (gst_static_pad_template_get (&sinktemplate), "sink"); gst_pad_set_chain_function (queue->sinkpad, GST_DEBUG_FUNCPTR (gst_queue_chain)); - gst_element_add_pad (GST_ELEMENT (queue), queue->sinkpad); + gst_pad_set_event_function (queue->sinkpad, + GST_DEBUG_FUNCPTR (gst_queue_handle_sink_event)); gst_pad_set_link_function (queue->sinkpad, GST_DEBUG_FUNCPTR (gst_queue_link_sink)); gst_pad_set_getcaps_function (queue->sinkpad, GST_DEBUG_FUNCPTR (gst_queue_getcaps)); - gst_pad_set_active (queue->sinkpad, TRUE); + gst_pad_set_bufferalloc_function (queue->sinkpad, + GST_DEBUG_FUNCPTR (gst_queue_bufferalloc)); + gst_element_add_pad (GST_ELEMENT (queue), queue->sinkpad); queue->srcpad = gst_pad_new_from_template (gst_static_pad_template_get (&srctemplate), "src"); - gst_pad_set_get_function (queue->srcpad, GST_DEBUG_FUNCPTR (gst_queue_get)); - gst_element_add_pad (GST_ELEMENT (queue), queue->srcpad); + gst_pad_set_loop_function (queue->srcpad, GST_DEBUG_FUNCPTR (gst_queue_loop)); + gst_pad_set_activate_function (queue->srcpad, + GST_DEBUG_FUNCPTR (gst_queue_src_activate)); gst_pad_set_link_function (queue->srcpad, GST_DEBUG_FUNCPTR (gst_queue_link_src)); gst_pad_set_getcaps_function (queue->srcpad, @@ -327,12 +324,12 @@ gst_queue_init (GstQueue * queue) GST_DEBUG_FUNCPTR (gst_queue_handle_src_event)); gst_pad_set_query_function (queue->srcpad, GST_DEBUG_FUNCPTR (gst_queue_handle_src_query)); - gst_pad_set_active (queue->srcpad, TRUE); + gst_element_add_pad (GST_ELEMENT (queue), queue->srcpad); queue->cur_level.buffers = 0; /* no content */ queue->cur_level.bytes = 0; /* no content */ queue->cur_level.time = 0; /* no content */ - queue->max_size.buffers = 100; /* 100 buffers */ + queue->max_size.buffers = 200; /* 200 buffers */ queue->max_size.bytes = 10 * 1024 * 1024; /* 10 MB */ queue->max_size.time = GST_SECOND; /* 1 s. */ queue->min_threshold.buffers = 0; /* no threshold */ @@ -348,9 +345,6 @@ gst_queue_init (GstQueue * queue) queue->qlock = g_mutex_new (); queue->item_add = g_cond_new (); queue->item_del = g_cond_new (); - queue->event_done = g_cond_new (); - queue->events = g_queue_new (); - queue->event_lock = g_mutex_new (); queue->queue = g_queue_new (); GST_CAT_DEBUG_OBJECT (GST_CAT_THREAD, queue, @@ -371,19 +365,11 @@ gst_queue_finalize (GObject * object) gst_data_unref (data); } g_queue_free (queue->queue); + GST_CAT_DEBUG_OBJECT (GST_CAT_THREAD, queue, "free mutex"); g_mutex_free (queue->qlock); + GST_CAT_DEBUG_OBJECT (GST_CAT_THREAD, queue, "done free mutex"); g_cond_free (queue->item_add); g_cond_free (queue->item_del); - g_cond_free (queue->event_done); - g_mutex_lock (queue->event_lock); - while (!g_queue_is_empty (queue->events)) { - GstQueueEventResponse *er = g_queue_pop_head (queue->events); - - gst_event_unref (er->event); - } - g_mutex_unlock (queue->event_lock); - g_mutex_free (queue->event_lock); - g_queue_free (queue->events); if (G_OBJECT_CLASS (parent_class)->finalize) G_OBJECT_CLASS (parent_class)->finalize (object); @@ -393,103 +379,69 @@ static GstCaps * gst_queue_getcaps (GstPad * pad) { GstQueue *queue; + GstPad *otherpad; + GstCaps *result; - queue = GST_QUEUE (gst_pad_get_parent (pad)); + queue = GST_QUEUE (GST_PAD_PARENT (pad)); - if (pad == queue->srcpad && queue->cur_level.bytes > 0) { - return gst_caps_copy (queue->negotiated_caps); - } + otherpad = (pad == queue->srcpad ? queue->sinkpad : queue->srcpad); + result = gst_pad_peer_get_caps (otherpad); - return gst_pad_proxy_getcaps (pad); + return result; } static GstPadLinkReturn -gst_queue_link_sink (GstPad * pad, const GstCaps * caps) +gst_queue_link_sink (GstPad * pad, GstPad * peer) { - GstQueue *queue; - GstPadLinkReturn link_ret; - - queue = GST_QUEUE (gst_pad_get_parent (pad)); - - if (queue->cur_level.bytes > 0) { - if (gst_caps_is_equal (caps, queue->negotiated_caps)) { - return GST_PAD_LINK_OK; - } else if (GST_STATE (queue) != GST_STATE_PLAYING) { - return GST_PAD_LINK_DELAYED; - } - - /* Wait until the queue is empty before attempting the pad - negotiation. */ - GST_QUEUE_MUTEX_LOCK; - - STATUS (queue, "waiting for queue to get empty"); - while (queue->cur_level.bytes > 0) { - g_cond_wait (queue->item_del, queue->qlock); - if (queue->interrupt) { - GST_QUEUE_MUTEX_UNLOCK; - return GST_PAD_LINK_DELAYED; - } - } - STATUS (queue, "queue is now empty"); - - GST_QUEUE_MUTEX_UNLOCK; - } - - link_ret = GST_PAD_LINK_OK; -#if 0 - link_ret = gst_pad_proxy_pad_link (pad, caps); - - if (GST_PAD_LINK_SUCCESSFUL (link_ret)) { - /* we store an extra copy of the negotiated caps, just in case - * the pads become unnegotiated while we have buffers */ - gst_caps_replace (&queue->negotiated_caps, gst_caps_copy (caps)); - } -#endif - - return link_ret; + return GST_PAD_LINK_OK; } static GstPadLinkReturn -gst_queue_link_src (GstPad * pad, const GstCaps * caps) +gst_queue_link_src (GstPad * pad, GstPad * peer) +{ + GstPadLinkReturn result = GST_PAD_LINK_OK; + + /* FIXME, see if we need to push or get pulled */ + if (GST_RPAD_LINKFUNC (peer)) + result = GST_RPAD_LINKFUNC (peer) (peer, pad); + + return result; +} + +static GstBuffer * +gst_queue_bufferalloc (GstPad * pad, guint64 offset, guint size, GstCaps * caps) { GstQueue *queue; - GstPadLinkReturn link_ret; + GstPad *otherpeer; + GstBuffer *result = NULL; - queue = GST_QUEUE (gst_pad_get_parent (pad)); + queue = GST_QUEUE (GST_PAD_PARENT (pad)); - if (queue->cur_level.bytes > 0) { - if (gst_caps_is_equal (caps, queue->negotiated_caps)) { - return GST_PAD_LINK_OK; - } - return GST_PAD_LINK_REFUSED; + otherpeer = gst_pad_get_peer (queue->srcpad); + if (otherpeer == NULL || GST_RPAD_BUFFERALLOCFUNC (otherpeer) == NULL) { + /* let the default aloc function do the work */ + result = NULL; + } else { + result = + GST_RPAD_BUFFERALLOCFUNC (otherpeer) (otherpeer, offset, size, caps); } -#if 0 - link_ret = gst_pad_proxy_pad_link (pad, caps); -#endif - link_ret = GST_PAD_LINK_OK; + if (otherpeer) + gst_object_unref (GST_OBJECT (otherpeer)); - if (GST_PAD_LINK_SUCCESSFUL (link_ret)) { - /* we store an extra copy of the negotiated caps, just in case - * the pads become unnegotiated while we have buffers */ - gst_caps_replace (&queue->negotiated_caps, gst_caps_copy (caps)); - } - - return link_ret; + return result; } + static void gst_queue_locked_flush (GstQueue * queue) { while (!g_queue_is_empty (queue->queue)) { GstData *data = g_queue_pop_head (queue->queue); - /* First loose the reference we added when putting that data in the queue */ - gst_data_unref (data); /* Then loose another reference because we are supposed to destroy that data when flushing */ gst_data_unref (data); } - queue->timeval = NULL; queue->cur_level.buffers = 0; queue->cur_level.bytes = 0; queue->cur_level.time = 0; @@ -501,92 +453,120 @@ gst_queue_locked_flush (GstQueue * queue) g_cond_signal (queue->item_del); } -static void -gst_queue_handle_pending_events (GstQueue * queue) -{ - /* check for events to send upstream */ - /* g_queue_get_length is glib 2.4, so don't depend on it yet, use ->length */ - GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, - "handling pending events, events queue of size %d", - queue->events->length); - g_mutex_lock (queue->event_lock); - while (!g_queue_is_empty (queue->events)) { - GstQueueEventResponse *er; +#define STATUS(queue, msg) \ + GST_CAT_LOG_OBJECT (queue_dataflow, queue, \ + "(%s:%s) " msg ": %u of %u-%u buffers, %u of %u-%u " \ + "bytes, %" G_GUINT64_FORMAT " of %" G_GUINT64_FORMAT \ + "-%" G_GUINT64_FORMAT " ns, %u elements", \ + GST_DEBUG_PAD_NAME (pad), \ + queue->cur_level.buffers, \ + queue->min_threshold.buffers, \ + queue->max_size.buffers, \ + queue->cur_level.bytes, \ + queue->min_threshold.bytes, \ + queue->max_size.bytes, \ + queue->cur_level.time, \ + queue->min_threshold.time, \ + queue->max_size.time, \ + queue->queue->length) - er = g_queue_pop_head (queue->events); - - GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, - "sending event %p (%d) from event response %p upstream", - er->event, GST_EVENT_TYPE (er->event), er); - if (er->handled) { - /* change this to an assert when this file gets reviewed properly. */ - GST_ELEMENT_ERROR (queue, CORE, EVENT, (NULL), - ("already handled event %p (%d) from event response %p upstream", - er->event, GST_EVENT_TYPE (er->event), er)); - break; - } - g_mutex_unlock (queue->event_lock); - er->ret = gst_pad_event_default (queue->srcpad, er->event); - er->handled = TRUE; - g_cond_signal (queue->event_done); - g_mutex_lock (queue->event_lock); - GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, "event sent"); - } - g_mutex_unlock (queue->event_lock); -} - -static void -gst_queue_chain (GstPad * pad, GstData * data) +static gboolean +gst_queue_handle_sink_event (GstPad * pad, GstEvent * event) { GstQueue *queue; - g_return_if_fail (pad != NULL); - g_return_if_fail (GST_IS_PAD (pad)); - g_return_if_fail (data != NULL); - queue = GST_QUEUE (GST_OBJECT_PARENT (pad)); -restart: - /* we have to lock the queue since we span threads */ - GST_QUEUE_MUTEX_LOCK; - - gst_queue_handle_pending_events (queue); - - /* assume don't need to flush this buffer when the queue is filled */ - queue->flush = FALSE; - - if (GST_IS_EVENT (data)) { - switch (GST_EVENT_TYPE (data)) { - case GST_EVENT_FLUSH: - STATUS (queue, "received flush event"); + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH: + STATUS (queue, "received flush event"); + /* forward event */ + gst_pad_event_default (pad, event); + if (GST_EVENT_FLUSH_DONE (event)) { + GST_STREAM_LOCK (queue->srcpad); + gst_task_start (GST_RPAD_TASK (queue->srcpad)); + GST_STREAM_UNLOCK (queue->srcpad); + } else { + /* now unblock the chain function */ + GST_QUEUE_MUTEX_LOCK; gst_queue_locked_flush (queue); + GST_QUEUE_MUTEX_UNLOCK; + STATUS (queue, "after flush"); - break; - case GST_EVENT_EOS: - STATUS (queue, "received EOS"); - break; - default: - /* we put the event in the queue, we don't have to act ourselves */ - GST_CAT_LOG_OBJECT (queue_dataflow, queue, - "adding event %p of type %d", data, GST_EVENT_TYPE (data)); - break; - } + + /* unblock the loop function */ + g_cond_signal (queue->item_add); + + /* make sure it stops */ + GST_STREAM_LOCK (queue->srcpad); + gst_task_pause (GST_RPAD_TASK (queue->srcpad)); + GST_CAT_LOG_OBJECT (queue_dataflow, queue, "loop stopped"); + GST_STREAM_UNLOCK (queue->srcpad); + } + goto done; + case GST_EVENT_EOS: + STATUS (queue, "received EOS"); + break; + default: + /* we put the event in the queue, we don't have to act ourselves */ + GST_CAT_LOG_OBJECT (queue_dataflow, queue, + "adding event %p of type %d", event, GST_EVENT_TYPE (event)); + break; } - if (GST_IS_BUFFER (data)) - GST_CAT_LOG_OBJECT (queue_dataflow, queue, - "adding buffer %p of size %d", data, GST_BUFFER_SIZE (data)); + GST_QUEUE_MUTEX_LOCK; + g_queue_push_tail (queue->queue, event); + g_cond_signal (queue->item_add); - /* We make space available if we're "full" according to whatever - * the user defined as "full". Note that this only applies to buffers. - * We always handle events and they don't count in our statistics. */ - if (GST_IS_BUFFER (data) && - ((queue->max_size.buffers > 0 && + GST_QUEUE_MUTEX_UNLOCK; +done: + + return TRUE; +} + +static gboolean +gst_queue_is_empty (GstQueue * queue) +{ + return (queue->queue->length == 0 || + (queue->min_threshold.buffers > 0 && + queue->cur_level.buffers < queue->min_threshold.buffers) || + (queue->min_threshold.bytes > 0 && + queue->cur_level.bytes < queue->min_threshold.bytes) || + (queue->min_threshold.time > 0 && + queue->cur_level.time < queue->min_threshold.time)); +} + +static gboolean +gst_queue_is_filled (GstQueue * queue) +{ + return (((queue->max_size.buffers > 0 && queue->cur_level.buffers >= queue->max_size.buffers) || (queue->max_size.bytes > 0 && queue->cur_level.bytes >= queue->max_size.bytes) || (queue->max_size.time > 0 && - queue->cur_level.time >= queue->max_size.time))) { + queue->cur_level.time >= queue->max_size.time))); +} + + +static GstFlowReturn +gst_queue_chain (GstPad * pad, GstBuffer * buffer) +{ + GstQueue *queue; + + queue = GST_QUEUE (GST_OBJECT_PARENT (pad)); + + GST_STREAM_LOCK (pad); + + /* we have to lock the queue since we span threads */ + GST_QUEUE_MUTEX_LOCK; + + GST_CAT_LOG_OBJECT (queue_dataflow, queue, + "adding buffer %p of size %d", buffer, GST_BUFFER_SIZE (buffer)); + + /* We make space available if we're "full" according to whatever + * the user defined as "full". Note that this only applies to buffers. + * We always handle events and they don't count in our statistics. */ + while (gst_queue_is_filled (queue)) { GST_QUEUE_MUTEX_UNLOCK; g_signal_emit (G_OBJECT (queue), gst_queue_signals[SIGNAL_OVERRUN], 0); GST_QUEUE_MUTEX_LOCK; @@ -598,7 +578,6 @@ restart: GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, "queue is full, leaking buffer on upstream end"); /* now we can clean up and exit right away */ - GST_QUEUE_MUTEX_UNLOCK; goto out_unref; /* leak first buffer in the queue */ @@ -630,15 +609,15 @@ restart: queue->queue->tail = g_list_last (item); queue->queue->length--; - /* and unref the data at the end. Twice, because we keep a ref + /* and unref the buffer at the end. Twice, because we keep a ref * to make things read-only. Also keep our list uptodate. */ - queue->cur_level.bytes -= GST_BUFFER_SIZE (data); + queue->cur_level.bytes -= GST_BUFFER_SIZE (buffer); queue->cur_level.buffers--; - if (GST_BUFFER_DURATION (data) != GST_CLOCK_TIME_NONE) - queue->cur_level.time -= GST_BUFFER_DURATION (data); + if (GST_BUFFER_DURATION (buffer) != GST_CLOCK_TIME_NONE) + queue->cur_level.time -= GST_BUFFER_DURATION (buffer); - gst_data_unref (data); - gst_data_unref (data); + gst_buffer_unref (buffer); + gst_buffer_unref (buffer); break; } @@ -650,66 +629,13 @@ restart: case GST_QUEUE_NO_LEAK: STATUS (queue, "pre-full wait"); - while ((queue->max_size.buffers > 0 && - queue->cur_level.buffers >= queue->max_size.buffers) || - (queue->max_size.bytes > 0 && - queue->cur_level.bytes >= queue->max_size.bytes) || - (queue->max_size.time > 0 && - queue->cur_level.time >= queue->max_size.time)) { + while (gst_queue_is_filled (queue)) { + STATUS (queue, "waiting for item_del signal from thread using qlock"); + g_cond_wait (queue->item_del, queue->qlock); + /* if there's a pending state change for this queue * or its manager, switch back to iterator so bottom * half of state change executes */ - if (queue->interrupt) { - GstScheduler *sched; - - GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, "interrupted"); - queue->interrupt = FALSE; - GST_QUEUE_MUTEX_UNLOCK; - sched = gst_pad_get_scheduler (queue->sinkpad); - if (!sched || gst_scheduler_interrupt (sched, GST_ELEMENT (queue))) { - goto out_unref; - } - /* if we got here because we were unlocked after a - * flush, we don't need to add the buffer to the - * queue again */ - if (queue->flush) { - GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, - "not adding pending buffer after flush"); - goto out_unref; - } - GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, - "adding pending buffer after interrupt"); - goto restart; - } - - if (GST_STATE (queue) != GST_STATE_PLAYING) { - /* this means the other end is shut down. Try to - * signal to resolve the error */ - if (!queue->may_deadlock) { - GST_QUEUE_MUTEX_UNLOCK; - gst_data_unref (data); - GST_ELEMENT_ERROR (queue, CORE, THREAD, (NULL), - ("deadlock found, shutting down source pad elements")); - /* we don't go to out_unref here, since we want to - * unref the buffer *before* calling GST_ELEMENT_ERROR */ - return; - } else { - GST_CAT_WARNING_OBJECT (queue_dataflow, queue, - "%s: waiting for the app to restart " - "source pad elements", GST_ELEMENT_NAME (queue)); - } - } - - /* OK, we've got a serious issue here. Imagine the situation - * where the puller (next element) is sending an event here, - * so it cannot pull events from the queue, and we cannot - * push data further because the queue is 'full' and therefore, - * we wait here (and do not handle events): deadlock! to solve - * that, we handle pending upstream events here, too. */ - gst_queue_handle_pending_events (queue); - - STATUS (queue, "waiting for item_del signal from thread using qlock"); - g_cond_wait (queue->item_del, queue->qlock); STATUS (queue, "received item_del signal from thread using qlock"); } @@ -720,119 +646,84 @@ restart: break; } } + /* we are flushing */ + if (GST_RPAD_IS_FLUSHING (pad)) + goto out_flushing; - /* put the buffer on the tail of the list. We keep a reference, - * so that the data is read-only while in here. There's a good - * reason to do so: we have a size and time counter, and any - * modification to the content could change any of the two. */ - gst_data_ref (data); - g_queue_push_tail (queue->queue, data); + g_queue_push_tail (queue->queue, buffer); - /* Note that we only add buffers (not events) to the statistics */ - if (GST_IS_BUFFER (data)) { - queue->cur_level.buffers++; - queue->cur_level.bytes += GST_BUFFER_SIZE (data); - if (GST_BUFFER_DURATION (data) != GST_CLOCK_TIME_NONE) - queue->cur_level.time += GST_BUFFER_DURATION (data); - } + /* add buffer to the statistics */ + queue->cur_level.buffers++; + queue->cur_level.bytes += GST_BUFFER_SIZE (buffer); + if (GST_BUFFER_DURATION (buffer) != GST_CLOCK_TIME_NONE) + queue->cur_level.time += GST_BUFFER_DURATION (buffer); STATUS (queue, "+ level"); GST_CAT_LOG_OBJECT (queue_dataflow, queue, "signalling item_add"); g_cond_signal (queue->item_add); GST_QUEUE_MUTEX_UNLOCK; + GST_STREAM_UNLOCK (pad); - return; + return GST_FLOW_OK; out_unref: - gst_data_unref (data); - return; + GST_QUEUE_MUTEX_UNLOCK; + GST_STREAM_UNLOCK (pad); + + gst_buffer_unref (buffer); + + return GST_FLOW_OK; + +out_flushing: + GST_CAT_LOG_OBJECT (queue_dataflow, queue, "exit because of flush"); + GST_QUEUE_MUTEX_UNLOCK; + gst_task_pause (GST_RPAD_TASK (queue->srcpad)); + GST_STREAM_UNLOCK (pad); + + gst_buffer_unref (buffer); + + return GST_FLOW_UNEXPECTED; } -static GstData * -gst_queue_get (GstPad * pad) +static void +gst_queue_loop (GstPad * pad) { GstQueue *queue; GstData *data; + gboolean restart = TRUE; - g_return_val_if_fail (pad != NULL, NULL); - g_return_val_if_fail (GST_IS_PAD (pad), NULL); + queue = GST_QUEUE (GST_PAD_PARENT (pad)); - queue = GST_QUEUE (gst_pad_get_parent (pad)); + GST_STREAM_LOCK (pad); -restart: /* have to lock for thread-safety */ GST_QUEUE_MUTEX_LOCK; - if (queue->queue->length == 0 || - (queue->min_threshold.buffers > 0 && - queue->cur_level.buffers < queue->min_threshold.buffers) || - (queue->min_threshold.bytes > 0 && - queue->cur_level.bytes < queue->min_threshold.bytes) || - (queue->min_threshold.time > 0 && - queue->cur_level.time < queue->min_threshold.time)) { +restart: + while (gst_queue_is_empty (queue)) { GST_QUEUE_MUTEX_UNLOCK; g_signal_emit (G_OBJECT (queue), gst_queue_signals[SIGNAL_UNDERRUN], 0); GST_QUEUE_MUTEX_LOCK; STATUS (queue, "pre-empty wait"); - while (queue->queue->length == 0 || - (queue->min_threshold.buffers > 0 && - queue->cur_level.buffers < queue->min_threshold.buffers) || - (queue->min_threshold.bytes > 0 && - queue->cur_level.bytes < queue->min_threshold.bytes) || - (queue->min_threshold.time > 0 && - queue->cur_level.time < queue->min_threshold.time)) { - /* if there's a pending state change for this queue or its - * manager, switch back to iterator so bottom half of state - * change executes. */ - if (queue->interrupt) { - GstScheduler *sched; - - GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, "interrupted"); - queue->interrupt = FALSE; - GST_QUEUE_MUTEX_UNLOCK; - sched = gst_pad_get_scheduler (queue->srcpad); - if (!sched || gst_scheduler_interrupt (sched, GST_ELEMENT (queue))) - return GST_DATA (gst_event_new (GST_EVENT_INTERRUPT)); - goto restart; - } - if (GST_STATE (queue) != GST_STATE_PLAYING) { - /* this means the other end is shut down */ - if (!queue->may_deadlock) { - GST_QUEUE_MUTEX_UNLOCK; - GST_ELEMENT_ERROR (queue, CORE, THREAD, (NULL), - ("deadlock found, shutting down sink pad elements")); - goto restart; - } else { - GST_CAT_WARNING_OBJECT (queue_dataflow, queue, - "%s: waiting for the app to restart " - "source pad elements", GST_ELEMENT_NAME (queue)); - } - } - + while (gst_queue_is_empty (queue)) { STATUS (queue, "waiting for item_add"); - if (queue->block_timeout != GST_CLOCK_TIME_NONE) { - GTimeVal timeout; + /* we are flushing */ + if (GST_RPAD_IS_FLUSHING (queue->sinkpad)) + goto out_flushing; - g_get_current_time (&timeout); - g_time_val_add (&timeout, queue->block_timeout / 1000); - GST_LOG_OBJECT (queue, "g_cond_time_wait using qlock from thread %p", - g_thread_self ()); - if (!g_cond_timed_wait (queue->item_add, queue->qlock, &timeout)) { - GST_QUEUE_MUTEX_UNLOCK; - GST_CAT_WARNING_OBJECT (queue_dataflow, queue, - "Sending filler event"); - return GST_DATA (gst_event_new_filler ()); - } - } else { - GST_LOG_OBJECT (queue, "doing g_cond_wait using qlock from thread %p", - g_thread_self ()); - g_cond_wait (queue->item_add, queue->qlock); - GST_LOG_OBJECT (queue, "done g_cond_wait using qlock from thread %p", - g_thread_self ()); - } + GST_LOG_OBJECT (queue, "doing g_cond_wait using qlock from thread %p", + g_thread_self ()); + g_cond_wait (queue->item_add, queue->qlock); + + /* we got unlocked because we are flushing */ + if (GST_RPAD_IS_FLUSHING (queue->sinkpad)) + goto out_flushing; + + GST_LOG_OBJECT (queue, "done g_cond_wait using qlock from thread %p", + g_thread_self ()); STATUS (queue, "got item_add signal"); } @@ -847,120 +738,73 @@ restart: GST_CAT_LOG_OBJECT (queue_dataflow, queue, "retrieved data %p from queue", data); - if (data == NULL) - return NULL; - if (GST_IS_BUFFER (data)) { + GstFlowReturn result; + /* Update statistics */ queue->cur_level.buffers--; queue->cur_level.bytes -= GST_BUFFER_SIZE (data); if (GST_BUFFER_DURATION (data) != GST_CLOCK_TIME_NONE) queue->cur_level.time -= GST_BUFFER_DURATION (data); - } - /* Now that we're done, we can lose our own reference to - * the item, since we're no longer in danger. */ - gst_data_unref (data); + GST_QUEUE_MUTEX_UNLOCK; + result = gst_pad_push (pad, GST_BUFFER (data)); + GST_QUEUE_MUTEX_LOCK; + if (result != GST_FLOW_OK) { + gst_task_pause (GST_RPAD_TASK (queue->srcpad)); + } + } else { + if (GST_EVENT_TYPE (data) == GST_EVENT_EOS) { + gst_task_pause (GST_RPAD_TASK (queue->srcpad)); + restart = FALSE; + } + GST_QUEUE_MUTEX_UNLOCK; + gst_pad_push_event (queue->srcpad, GST_EVENT (data)); + GST_QUEUE_MUTEX_LOCK; + if (restart == TRUE) + goto restart; + } STATUS (queue, "after _get()"); GST_CAT_LOG_OBJECT (queue_dataflow, queue, "signalling item_del"); g_cond_signal (queue->item_del); GST_QUEUE_MUTEX_UNLOCK; + GST_STREAM_UNLOCK (pad); + return; - /* FIXME: I suppose this needs to be locked, since the EOS - * bit affects the pipeline state. However, that bit is - * locked too so it'd cause a deadlock. */ - if (GST_IS_EVENT (data)) { - GstEvent *event = GST_EVENT (data); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_EOS: - GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, - "queue \"%s\" eos", GST_ELEMENT_NAME (queue)); - gst_element_set_eos (GST_ELEMENT (queue)); - break; - default: - break; - } - } - - return data; +out_flushing: + GST_CAT_LOG_OBJECT (queue_dataflow, queue, "exit because of flush"); + gst_task_pause (GST_RPAD_TASK (pad)); + GST_QUEUE_MUTEX_UNLOCK; + GST_STREAM_UNLOCK (pad); + return; } static gboolean gst_queue_handle_src_event (GstPad * pad, GstEvent * event) { - GstQueue *queue = GST_QUEUE (gst_pad_get_parent (pad)); - gboolean res; + GstQueue *queue = GST_QUEUE (GST_PAD_PARENT (pad)); + gboolean res = TRUE; GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, "got event %p (%d)", event, GST_EVENT_TYPE (event)); + + gst_event_ref (event); + res = gst_pad_event_default (pad, event); GST_QUEUE_MUTEX_LOCK; - if (gst_element_get_state (GST_ELEMENT (queue)) == GST_STATE_PLAYING) { - GstQueueEventResponse er; - - /* push the event to the queue and wait for upstream consumption */ - er.event = event; - er.handled = FALSE; - g_mutex_lock (queue->event_lock); - GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, - "putting event %p (%d) on internal queue", event, - GST_EVENT_TYPE (event)); - g_queue_push_tail (queue->events, &er); - g_mutex_unlock (queue->event_lock); - GST_CAT_WARNING_OBJECT (queue_dataflow, queue, - "Preparing for loop for event handler"); - /* see the chain function on why this is here - it prevents a deadlock */ - g_cond_signal (queue->item_del); - while (!er.handled) { - GTimeVal timeout; - - g_get_current_time (&timeout); - g_time_val_add (&timeout, 500 * 1000); /* half a second */ - GST_LOG_OBJECT (queue, "doing g_cond_wait using qlock from thread %p", - g_thread_self ()); - if (!g_cond_timed_wait (queue->event_done, queue->qlock, &timeout) && - !er.handled) { - GST_CAT_WARNING_OBJECT (queue_dataflow, queue, - "timeout in upstream event handling, dropping event %p (%d)", - er.event, GST_EVENT_TYPE (er.event)); - g_mutex_lock (queue->event_lock); - /* since this queue is for src events (ie upstream), this thread is - * the only one that is pushing stuff on it, so we're sure that - * it's still the tail element. FIXME: But in practice, we should use - * GList instead of GQueue for this so we can remove any element in - * the list. */ - g_queue_pop_tail (queue->events); - g_mutex_unlock (queue->event_lock); - gst_event_unref (er.event); - res = FALSE; - goto handled; - } - } - GST_CAT_WARNING_OBJECT (queue_dataflow, queue, "Event handled"); - res = er.ret; - } else { - res = gst_pad_event_default (pad, event); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_FLUSH: - GST_CAT_DEBUG_OBJECT (queue_dataflow, queue, - "FLUSH event, flushing queue\n"); + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK: + if (GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH) { gst_queue_locked_flush (queue); - break; - case GST_EVENT_SEEK: - if (GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH) { - gst_queue_locked_flush (queue); - } - default: - break; - } + } + default: + break; } -handled: GST_QUEUE_MUTEX_UNLOCK; + gst_event_unref (event); return res; } @@ -969,13 +813,11 @@ static gboolean gst_queue_handle_src_query (GstPad * pad, GstQueryType type, GstFormat * fmt, gint64 * value) { - GstQueue *queue = GST_QUEUE (gst_pad_get_parent (pad)); - gboolean res; + GstQueue *queue = GST_QUEUE (GST_PAD_PARENT (pad)); if (!GST_PAD_PEER (queue->sinkpad)) return FALSE; - res = gst_pad_query (GST_PAD_PEER (queue->sinkpad), type, fmt, value); - if (!res) + if (!gst_pad_query (GST_PAD_PEER (queue->sinkpad), type, fmt, value)) return FALSE; if (type == GST_QUERY_POSITION) { @@ -997,21 +839,44 @@ gst_queue_handle_src_query (GstPad * pad, } static gboolean -gst_queue_release_locks (GstElement * element) +gst_queue_src_activate (GstPad * pad, GstActivateMode mode) { + gboolean result = FALSE; GstQueue *queue; - queue = GST_QUEUE (element); + queue = GST_QUEUE (GST_OBJECT_PARENT (pad)); - GST_QUEUE_MUTEX_LOCK; - queue->interrupt = TRUE; - g_cond_signal (queue->item_add); - g_cond_signal (queue->item_del); - GST_QUEUE_MUTEX_UNLOCK; + if (mode == GST_ACTIVATE_PUSH) { + /* if we have a scheduler we can start the task */ + if (GST_ELEMENT_SCHEDULER (queue)) { + GST_STREAM_LOCK (pad); + GST_RPAD_TASK (pad) = + gst_scheduler_create_task (GST_ELEMENT_SCHEDULER (queue), + (GstTaskFunction) gst_queue_loop, pad); - return TRUE; + gst_task_start (GST_RPAD_TASK (pad)); + GST_STREAM_UNLOCK (pad); + result = TRUE; + } + } else { + /* step 1, unblock chain and loop functions */ + queue->interrupt = TRUE; + g_cond_signal (queue->item_add); + g_cond_signal (queue->item_del); + + /* step 2, make sure streaming finishes */ + GST_STREAM_LOCK (pad); + /* step 3, stop the task */ + gst_task_stop (GST_RPAD_TASK (pad)); + gst_object_unref (GST_OBJECT (GST_RPAD_TASK (pad))); + GST_STREAM_UNLOCK (pad); + + result = TRUE; + } + return result; } + static GstElementStateReturn gst_queue_change_state (GstElement * element) { @@ -1020,71 +885,38 @@ gst_queue_change_state (GstElement * element) queue = GST_QUEUE (element); - GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, - "starting state change 0x%x", GST_STATE_TRANSITION (element)); + GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "starting state change"); /* lock the queue so another thread (not in sync with this thread's state) - * can't call this queue's _get (or whatever) - */ + * can't call this queue's _loop (or whatever) */ GST_QUEUE_MUTEX_LOCK; switch (GST_STATE_TRANSITION (element)) { case GST_STATE_NULL_TO_READY: gst_queue_locked_flush (queue); break; - case GST_STATE_PAUSED_TO_PLAYING: - if (!GST_PAD_IS_LINKED (queue->sinkpad)) { - GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, queue, - "queue %s is not linked", GST_ELEMENT_NAME (queue)); - /* FIXME can this be? */ - g_cond_signal (queue->item_add); - - ret = GST_STATE_FAILURE; - goto unlock; - } else { - GstScheduler *src_sched, *sink_sched; - - src_sched = gst_pad_get_scheduler (GST_PAD (queue->srcpad)); - sink_sched = gst_pad_get_scheduler (GST_PAD (queue->sinkpad)); - - if (src_sched == sink_sched) { - GST_CAT_DEBUG_OBJECT (GST_CAT_STATES, queue, - "queue %s does not connect different schedulers", - GST_ELEMENT_NAME (queue)); - - g_warning ("queue %s does not connect different schedulers", - GST_ELEMENT_NAME (queue)); - - ret = GST_STATE_FAILURE; - goto unlock; - } - } - queue->interrupt = FALSE; + case GST_STATE_READY_TO_PAUSED: break; - case GST_STATE_PAUSED_TO_READY: - gst_queue_locked_flush (queue); - gst_caps_replace (&queue->negotiated_caps, NULL); + case GST_STATE_PAUSED_TO_PLAYING: + queue->interrupt = FALSE; break; default: break; } - GST_QUEUE_MUTEX_UNLOCK; + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element); - if (GST_ELEMENT_CLASS (parent_class)->change_state) - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element); - - /* this is an ugly hack to make sure our pads are always active. - * Reason for this is that pad activation for the queue element - * depends on 2 schedulers (ugh) */ - gst_pad_set_active (queue->sinkpad, TRUE); - gst_pad_set_active (queue->srcpad, TRUE); - - GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "done with state change"); - - return ret; - -unlock: + switch (GST_STATE_TRANSITION (element)) { + case GST_STATE_PLAYING_TO_PAUSED: + break; + case GST_STATE_PAUSED_TO_READY: + gst_queue_locked_flush (queue); + break; + case GST_STATE_READY_TO_NULL: + break; + default: + break; + } GST_QUEUE_MUTEX_UNLOCK; GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "done with state change"); @@ -1092,7 +924,6 @@ unlock: return ret; } - static void gst_queue_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) diff --git a/plugins/elements/gstqueue.h b/plugins/elements/gstqueue.h index e473f404ef..9ff1c86805 100644 --- a/plugins/elements/gstqueue.h +++ b/plugins/elements/gstqueue.h @@ -27,7 +27,6 @@ #include - G_BEGIN_DECLS #define GST_TYPE_QUEUE \ @@ -80,23 +79,14 @@ struct _GstQueue { /* it the queue should fail on possible deadlocks */ gboolean may_deadlock; - gboolean interrupt; gboolean flush; GMutex *qlock; /* lock for queue (vs object lock) */ GCond *item_add; /* signals buffers now available for reading */ GCond *item_del; /* signals space now available for writing */ - GCond *event_done; /* upstream event signaller */ - GTimeVal *timeval; /* the timeout for the queue locking */ - GQueue *events; /* upstream events get decoupled here */ - - GstCaps *negotiated_caps; - - GMutex *event_lock; /* lock when handling the events queue */ - - gpointer _gst_reserved[GST_PADDING - 1]; + gpointer _gst_reserved[GST_PADDING]; }; struct _GstQueueClass { diff --git a/plugins/elements/gsttee.c b/plugins/elements/gsttee.c index 94887582b5..cd7213c693 100644 --- a/plugins/elements/gsttee.c +++ b/plugins/elements/gsttee.c @@ -1,6 +1,7 @@ /* GStreamer * Copyright (C) 1999,2000 Erik Walthinsen - * 2000 Wim Taymans + * 2000,2001,2002,2003,2004,2005 Wim Taymans + * * * gsttee.c: Tee element, one in N out * @@ -40,21 +41,16 @@ GstElementDetails gst_tee_details = GST_ELEMENT_DETAILS ("Tee pipe fitting", "Generic", "1-to-N pipe fitting", "Erik Walthinsen , " - "Wim Taymans "); - -/* Tee signals and args */ -enum -{ - /* FILL ME */ - LAST_SIGNAL -}; + "Wim \"Tim\" Taymans "); enum { - ARG_0, - ARG_SILENT, - ARG_NUM_PADS, - ARG_LAST_MESSAGE + PROP_0, + PROP_NUM_SRC_PADS, + PROP_HAS_SINK_LOOP, + PROP_HAS_CHAIN, + PROP_SILENT, + PROP_LAST_MESSAGE /* FILL ME */ }; @@ -77,7 +73,9 @@ static void gst_tee_set_property (GObject * object, guint prop_id, static void gst_tee_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); -static void gst_tee_chain (GstPad * pad, GstData * _data); +static GstFlowReturn gst_tee_chain (GstPad * pad, GstBuffer * buffer); +static void gst_tee_loop (GstPad * pad); +static gboolean gst_tee_sink_activate (GstPad * pad, GstActivateMode mode); static void @@ -113,22 +111,26 @@ gst_tee_class_init (GstTeeClass * klass) gobject_class = (GObjectClass *) klass; gstelement_class = (GstElementClass *) klass; + gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_tee_finalize); gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_tee_set_property); gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_tee_get_property); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_NUM_PADS, - g_param_spec_int ("num_pads", "num_pads", "num_pads", + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_NUM_SRC_PADS, + g_param_spec_int ("num-src-pads", "num-src-pads", "num-src-pads", 0, G_MAXINT, 0, G_PARAM_READABLE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SILENT, + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HAS_SINK_LOOP, + g_param_spec_boolean ("has-sink-loop", "has-sink-loop", "has-sink-loop", + FALSE, G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_HAS_CHAIN, + g_param_spec_boolean ("has-chain", "has-chain", "has-chain", + TRUE, G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SILENT, g_param_spec_boolean ("silent", "silent", "silent", TRUE, G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); - g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LAST_MESSAGE, + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LAST_MESSAGE, g_param_spec_string ("last_message", "last_message", "last_message", NULL, G_PARAM_READABLE)); - - gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_tee_finalize); - gstelement_class->request_new_pad = GST_DEBUG_FUNCPTR (gst_tee_request_new_pad); } @@ -140,72 +142,30 @@ gst_tee_init (GstTee * tee) gst_pad_new_from_template (gst_static_pad_template_get (&sinktemplate), "sink"); gst_element_add_pad (GST_ELEMENT (tee), tee->sinkpad); - gst_pad_set_chain_function (tee->sinkpad, GST_DEBUG_FUNCPTR (gst_tee_chain)); - //gst_pad_set_link_function (tee->sinkpad, GST_DEBUG_FUNCPTR (gst_pad_proxy_pad_link)); + gst_pad_set_setcaps_function (tee->sinkpad, + GST_DEBUG_FUNCPTR (gst_pad_proxy_setcaps)); gst_pad_set_getcaps_function (tee->sinkpad, GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps)); tee->last_message = NULL; } -/* helper compare function */ -gint -name_pad_compare (gconstpointer a, gconstpointer b) +static void +gst_tee_update_pad_functions (GstTee * tee) { - GstPad *pad = (GstPad *) a; - gchar *name = (gchar *) b; + gst_pad_set_activate_function (tee->sinkpad, + GST_DEBUG_FUNCPTR (gst_tee_sink_activate)); - g_assert (GST_IS_PAD (pad)); + if (tee->has_chain) + gst_pad_set_chain_function (tee->sinkpad, + GST_DEBUG_FUNCPTR (gst_tee_chain)); + else + gst_pad_set_chain_function (tee->sinkpad, NULL); - return strcmp (name, gst_pad_get_name (pad)); /* returns 0 if match */ -} - -static GstCaps * -gst_tee_getcaps (GstPad * _pad) -{ - GstTee *tee = GST_TEE (gst_pad_get_parent (_pad)); - GstCaps *caps = gst_caps_new_any (), *tmp, *res; - GstPad *pad; - const GList *pads; - - for (pads = GST_ELEMENT (tee)->pads; pads != NULL; pads = pads->next) { - pad = GST_PAD (pads->data); - if (pad == _pad) - continue; - - tmp = gst_pad_get_allowed_caps (pad); - res = gst_caps_intersect (caps, tmp); - gst_caps_unref (tmp); - gst_caps_unref (caps); - caps = res; - } - - return caps; -} - -static GstPadLinkReturn -gst_tee_link (GstPad * _pad, const GstCaps * caps) -{ - GstTee *tee = GST_TEE (gst_pad_get_parent (_pad)); - GstPadLinkReturn res; - GstPad *pad; - const GList *pads; - - GST_DEBUG_OBJECT (tee, "Forwarding link to all other pads"); - - for (pads = GST_ELEMENT (tee)->pads; pads != NULL; pads = pads->next) { - pad = GST_PAD (pads->data); - if (pad == _pad) - continue; - - res = gst_pad_try_set_caps (pad, caps); - GST_DEBUG_OBJECT (tee, "Pad %s:%s gave response %d", - GST_DEBUG_PAD_NAME (pad), res); - if (GST_PAD_LINK_FAILED (res)) - return res; - } - - return GST_PAD_LINK_OK; + if (tee->has_sink_loop) + gst_pad_set_loop_function (tee->sinkpad, GST_DEBUG_FUNCPTR (gst_tee_loop)); + else + gst_pad_set_loop_function (tee->sinkpad, NULL); } static GstPad * @@ -215,48 +175,21 @@ gst_tee_request_new_pad (GstElement * element, GstPadTemplate * templ, gchar *name; GstPad *srcpad; GstTee *tee; - gint i = 0; - const GList *pads; - - g_return_val_if_fail (GST_IS_TEE (element), NULL); - - if (templ->direction != GST_PAD_SRC) { - g_warning ("gsttee: request new pad that is not a SRC pad\n"); - return NULL; - } tee = GST_TEE (element); - /* try names in order and find one that's not in use atm */ - pads = element->pads; - - name = NULL; - while (!name) { - name = g_strdup_printf ("src%d", i); - if (g_list_find_custom ((GList *) pads, (gconstpointer) name, - name_pad_compare) != NULL) { - /* this name is taken, use the next one */ - ++i; - g_free (name); - name = NULL; - } - } - if (!tee->silent) { - g_free (tee->last_message); - tee->last_message = g_strdup_printf ("new pad %s", name); - g_object_notify (G_OBJECT (tee), "last_message"); - } + GST_LOCK (tee); + name = g_strdup_printf ("src%d", tee->pad_counter++); + GST_UNLOCK (tee); srcpad = gst_pad_new_from_template (templ, name); g_free (name); - gst_pad_set_link_function (srcpad, GST_DEBUG_FUNCPTR (gst_tee_link)); - gst_pad_set_getcaps_function (srcpad, GST_DEBUG_FUNCPTR (gst_tee_getcaps)); - gst_element_add_pad (GST_ELEMENT (tee), srcpad); - GST_PAD_ELEMENT_PRIVATE (srcpad) = NULL; - if (GST_PAD_CAPS (tee->sinkpad)) { - gst_pad_try_set_caps (srcpad, GST_PAD_CAPS (tee->sinkpad)); - } + gst_pad_set_setcaps_function (srcpad, + GST_DEBUG_FUNCPTR (gst_pad_proxy_setcaps)); + gst_pad_set_getcaps_function (srcpad, + GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps)); + gst_element_add_pad (GST_ELEMENT (tee), srcpad); return srcpad; } @@ -265,95 +198,202 @@ static void gst_tee_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { - GstTee *tee; - - /* it's not null if we got it, but it might not be ours */ - g_return_if_fail (GST_IS_TEE (object)); - - tee = GST_TEE (object); + GstTee *tee = GST_TEE (object); + GST_LOCK (tee); switch (prop_id) { - case ARG_SILENT: + case PROP_HAS_SINK_LOOP: + tee->has_sink_loop = g_value_get_boolean (value); + gst_tee_update_pad_functions (tee); + break; + case PROP_HAS_CHAIN: + tee->has_chain = g_value_get_boolean (value); + gst_tee_update_pad_functions (tee); + break; + case PROP_SILENT: tee->silent = g_value_get_boolean (value); - g_object_notify (G_OBJECT (tee), "silent"); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } + GST_UNLOCK (tee); } static void gst_tee_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { - GstTee *tee; - - /* it's not null if we got it, but it might not be ours */ - g_return_if_fail (GST_IS_TEE (object)); - - tee = GST_TEE (object); + GstTee *tee = GST_TEE (object); + GST_LOCK (tee); switch (prop_id) { - case ARG_NUM_PADS: + case PROP_NUM_SRC_PADS: g_value_set_int (value, GST_ELEMENT (tee)->numsrcpads); break; - case ARG_SILENT: + case PROP_HAS_SINK_LOOP: + g_value_set_boolean (value, tee->has_sink_loop); + break; + case PROP_HAS_CHAIN: + g_value_set_boolean (value, tee->has_chain); + break; + case PROP_SILENT: g_value_set_boolean (value, tee->silent); break; - case ARG_LAST_MESSAGE: + case PROP_LAST_MESSAGE: g_value_set_string (value, tee->last_message); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } + GST_UNLOCK (tee); } -/** - * gst_tee_chain: - * @pad: the pad to follow - * @buf: the buffer to pass - * - * Chain a buffer on a pad. - */ -static void -gst_tee_chain (GstPad * pad, GstData * _data) +typedef struct { - GstBuffer *buf = GST_BUFFER (_data); GstTee *tee; - const GList *pads; + GstBuffer *buffer; +} PushData; - g_return_if_fail (pad != NULL); - g_return_if_fail (GST_IS_PAD (pad)); - g_return_if_fail (buf != NULL); +static gboolean +gst_tee_do_push (GstPad * pad, GValue * ret, PushData * data) +{ + GstFlowReturn res; + + if (GST_PAD_DIRECTION (pad) != GST_PAD_SRC || !GST_PAD_IS_USABLE (pad)) + return TRUE; + + if (G_UNLIKELY (!data->tee->silent)) { + GstTee *tee = data->tee; + GstBuffer *buf = data->buffer; + + g_free (tee->last_message); + tee->last_message = + g_strdup_printf ("chain ******* (%s:%s)t (%d bytes, %" + G_GUINT64_FORMAT ") %p", GST_DEBUG_PAD_NAME (pad), + GST_BUFFER_SIZE (buf), GST_BUFFER_TIMESTAMP (buf), buf); + g_object_notify (G_OBJECT (tee), "last_message"); + } + + res = gst_pad_push (pad, gst_buffer_ref (data->buffer)); + g_value_set_enum (ret, res); + return (res == GST_FLOW_OK); +} + +static GstFlowReturn +gst_tee_handle_buffer (GstTee * tee, GstBuffer * buffer) +{ + GstIterator *iter; + PushData data; + GValue ret = { 0, }; + GstIteratorResult res; + + tee->offset += GST_BUFFER_SIZE (buffer); + + g_value_init (&ret, GST_TYPE_FLOW_RETURN); + iter = gst_element_iterate_pads (GST_ELEMENT (tee)); + data.tee = tee; + data.buffer = buffer; + + res = gst_iterator_fold (iter, (GstIteratorFoldFunction) gst_tee_do_push, + &ret, &data); + gst_iterator_free (iter); + + gst_buffer_unref (buffer); + + /* no need to unset gvalue */ + return g_value_get_enum (&ret); +} + +#define WITH_STREAM_LOCK(pad, expr) G_STMT_START {\ + GST_STREAM_LOCK (pad); \ + expr; \ + GST_STREAM_UNLOCK (pad); \ +}G_STMT_END + +static GstFlowReturn +gst_tee_chain (GstPad * pad, GstBuffer * buffer) +{ + GstFlowReturn res; + GstTee *tee; tee = GST_TEE (gst_pad_get_parent (pad)); - gst_buffer_ref_by_count (buf, GST_ELEMENT (tee)->numsrcpads - 1); + WITH_STREAM_LOCK (pad, res = gst_tee_handle_buffer (tee, buffer)); - pads = GST_ELEMENT (tee)->pads; - - while (pads) { - GstPad *outpad = GST_PAD (pads->data); - - pads = g_list_next (pads); - - if (GST_PAD_DIRECTION (outpad) != GST_PAD_SRC) - continue; - - if (!tee->silent) { - g_free (tee->last_message); - tee->last_message = - g_strdup_printf ("chain ******* (%s:%s)t (%d bytes, %" - G_GUINT64_FORMAT ") %p", GST_DEBUG_PAD_NAME (outpad), - GST_BUFFER_SIZE (buf), GST_BUFFER_TIMESTAMP (buf), buf); - g_object_notify (G_OBJECT (tee), "last_message"); - } - - if (GST_PAD_IS_USABLE (outpad)) - gst_pad_push (outpad, GST_DATA (buf)); - else - gst_buffer_unref (buf); - } + return res; +} + +#define DEFAULT_SIZE 1024 + +static void +gst_tee_loop (GstPad * pad) +{ + GstBuffer *buffer; + GstFlowReturn res; + GstTee *tee; + + GST_STREAM_LOCK (pad); + + tee = GST_TEE (gst_pad_get_parent (pad)); + + res = gst_pad_pull_range (pad, tee->offset, DEFAULT_SIZE, &buffer); + if (res != GST_FLOW_OK) + goto pause_task; + + res = gst_tee_handle_buffer (tee, buffer); + if (res != GST_FLOW_OK) + goto pause_task; + + GST_STREAM_UNLOCK (pad); + return; + +pause_task: + gst_pad_pause_task (pad); + GST_STREAM_UNLOCK (pad); + return; +} + +static gboolean +gst_tee_sink_activate (GstPad * pad, GstActivateMode mode) +{ + gboolean result = FALSE; + GstTee *tee; + + tee = GST_TEE (GST_OBJECT_PARENT (pad)); + + switch (mode) { + case GST_ACTIVATE_PUSH: + g_return_val_if_fail (tee->has_chain, FALSE); + result = TRUE; + break; + case GST_ACTIVATE_PULL: + g_return_val_if_fail (tee->has_sink_loop, FALSE); + if (GST_ELEMENT_SCHEDULER (tee)) { + GST_STREAM_LOCK (pad); + GST_RPAD_TASK (pad) = + gst_scheduler_create_task (GST_ELEMENT_SCHEDULER (tee), + (GstTaskFunction) gst_tee_loop, pad); + + gst_pad_start_task (pad); + GST_STREAM_UNLOCK (pad); + result = TRUE; + } + break; + case GST_ACTIVATE_NONE: + GST_STREAM_LOCK (pad); + if (GST_RPAD_TASK (pad)) { + gst_pad_stop_task (pad); + gst_object_unref (GST_OBJECT (GST_RPAD_TASK (pad))); + GST_RPAD_TASK (pad) = NULL; + } + GST_STREAM_UNLOCK (pad); + + result = TRUE; + break; + } + tee->sink_mode = mode; + + return result; } diff --git a/plugins/elements/gsttee.h b/plugins/elements/gsttee.h index f28b09d9d5..a575403205 100644 --- a/plugins/elements/gsttee.h +++ b/plugins/elements/gsttee.h @@ -49,6 +49,12 @@ struct _GstTee { GstPad *sinkpad; gboolean silent; + gboolean has_chain; + gboolean has_sink_loop; + gint pad_counter; + guint64 offset; + GstActivateMode sink_mode; + gchar *last_message; }; diff --git a/tests/benchmarks/complexity.c b/tests/benchmarks/complexity.c index 703aa3230a..e6c428291d 100644 --- a/tests/benchmarks/complexity.c +++ b/tests/benchmarks/complexity.c @@ -114,7 +114,8 @@ main (gint argc, gchar * argv[]) GST_TIME_ARGS (end - start)); start = gst_get_current_time (); - while (gst_bin_iterate (GST_BIN (pipeline))); + gst_bus_poll (gst_element_get_bus (pipeline), + GST_MESSAGE_EOS | GST_MESSAGE_ERROR, -1); end = gst_get_current_time (); g_print ("%" GST_TIME_FORMAT " - putting %u buffers through\n", GST_TIME_ARGS (end - start), BUFFER_COUNT); diff --git a/tests/benchmarks/mass-elements.c b/tests/benchmarks/mass-elements.c index d9d707096e..3a6ad4d9ee 100644 --- a/tests/benchmarks/mass-elements.c +++ b/tests/benchmarks/mass-elements.c @@ -95,7 +95,8 @@ main (gint argc, gchar * argv[]) GST_TIME_ARGS (end - start)); start = gst_get_current_time (); - while (gst_bin_iterate (GST_BIN (pipeline))); + gst_bus_poll (gst_element_get_bus (pipeline), + GST_MESSAGE_EOS | GST_MESSAGE_ERROR, -1); end = gst_get_current_time (); g_print ("%" GST_TIME_FORMAT " - putting %u buffers through\n", GST_TIME_ARGS (end - start), buffers); diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index 66a2464798..37b452533a 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -20,6 +20,7 @@ CLEANFILES = core.* TESTS = $(top_builddir)/tools/gst-register-@GST_MAJORMINOR@ \ gst/gstbin \ + gst/gstbus \ gst/gstcaps \ gst/gstdata \ gst/gstiterator \ diff --git a/tests/complexity.c b/tests/complexity.c index 703aa3230a..e6c428291d 100644 --- a/tests/complexity.c +++ b/tests/complexity.c @@ -114,7 +114,8 @@ main (gint argc, gchar * argv[]) GST_TIME_ARGS (end - start)); start = gst_get_current_time (); - while (gst_bin_iterate (GST_BIN (pipeline))); + gst_bus_poll (gst_element_get_bus (pipeline), + GST_MESSAGE_EOS | GST_MESSAGE_ERROR, -1); end = gst_get_current_time (); g_print ("%" GST_TIME_FORMAT " - putting %u buffers through\n", GST_TIME_ARGS (end - start), BUFFER_COUNT); diff --git a/tests/mass_elements.c b/tests/mass_elements.c index d9d707096e..3a6ad4d9ee 100644 --- a/tests/mass_elements.c +++ b/tests/mass_elements.c @@ -95,7 +95,8 @@ main (gint argc, gchar * argv[]) GST_TIME_ARGS (end - start)); start = gst_get_current_time (); - while (gst_bin_iterate (GST_BIN (pipeline))); + gst_bus_poll (gst_element_get_bus (pipeline), + GST_MESSAGE_EOS | GST_MESSAGE_ERROR, -1); end = gst_get_current_time (); g_print ("%" GST_TIME_FORMAT " - putting %u buffers through\n", GST_TIME_ARGS (end - start), buffers); diff --git a/tests/old/testsuite/states/locked.c b/tests/old/testsuite/states/locked.c index ecf969ec3e..d66565ddeb 100644 --- a/tests/old/testsuite/states/locked.c +++ b/tests/old/testsuite/states/locked.c @@ -17,50 +17,85 @@ * Boston, MA 02111-1307, USA. */ +#include "unistd.h" + #include +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; +} + gint main (gint argc, gchar * argv[]) { GstElement *pipeline; GstElement *fakesrc1, *fakesink1; GstElement *fakesrc2, *fakesink2; + GstBus *bus; gst_init (&argc, &argv); 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", 5, NULL); fakesink1 = gst_element_factory_make ("fakesink", "fakesink1"); - gst_bin_add_many (GST_BIN (pipeline), fakesrc1, fakesink1, NULL); - gst_element_link_pads (fakesrc1, "src", fakesink1, "sink"); + 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")); fakesrc2 = gst_element_factory_make ("fakesrc", "fakesrc2"); g_object_set (G_OBJECT (fakesrc2), "num_buffers", 5, NULL); fakesink2 = gst_element_factory_make ("fakesink", "fakesink2"); - gst_bin_add_many (GST_BIN (pipeline), fakesrc2, fakesink2, NULL); - gst_element_link_pads (fakesrc2, "src", fakesink2, "sink"); + gst_bin_add (GST_BIN (pipeline), fakesrc2); + gst_bin_add (GST_BIN (pipeline), fakesink2); + gst_pad_link (gst_element_get_pad (fakesrc2, "src"), + gst_element_get_pad (fakesink2, "sink")); g_signal_connect (G_OBJECT (pipeline), "deep_notify", - G_CALLBACK (gst_element_default_deep_notify), NULL); + G_CALLBACK (gst_object_default_deep_notify), NULL); GST_FLAG_SET (fakesrc2, GST_ELEMENT_LOCKED_STATE); GST_FLAG_SET (fakesink2, GST_ELEMENT_LOCKED_STATE); + g_print ("play..\n"); gst_element_set_state (pipeline, GST_STATE_PLAYING); - while (gst_bin_iterate (GST_BIN (pipeline))); - gst_element_set_state (pipeline, GST_STATE_READY); + + g_main_loop_run (loop); g_object_set (G_OBJECT (fakesrc1), "num_buffers", 5, NULL); + gst_element_set_state (pipeline, GST_STATE_READY); + GST_FLAG_UNSET (fakesrc2, GST_ELEMENT_LOCKED_STATE); GST_FLAG_UNSET (fakesink2, GST_ELEMENT_LOCKED_STATE); + g_print ("play..\n"); gst_element_set_state (pipeline, GST_STATE_PLAYING); - while (gst_bin_iterate (GST_BIN (pipeline))); + + g_main_loop_run (loop); + gst_element_set_state (pipeline, GST_STATE_NULL); gst_object_unref (GST_OBJECT (pipeline)); diff --git a/tests/old/testsuite/states/parent.c b/tests/old/testsuite/states/parent.c index aa583c5aa3..fe8b2441c6 100644 --- a/tests/old/testsuite/states/parent.c +++ b/tests/old/testsuite/states/parent.c @@ -67,7 +67,7 @@ main (gint argc, gchar * argv[]) gst_bin_add (GST_BIN (pipeline), bin2); g_signal_connect (G_OBJECT (pipeline), "deep_notify", - G_CALLBACK (gst_element_default_deep_notify), NULL); + G_CALLBACK (gst_object_default_deep_notify), NULL); /* setting pipeline to READY should bring in all children to READY */ gst_element_set_state (pipeline, GST_STATE_READY); @@ -77,31 +77,35 @@ main (gint argc, gchar * argv[]) g_assert (GST_STATE (identity) == GST_STATE_READY); g_assert (GST_STATE (fakesink) == GST_STATE_READY); - /* setting fakesink to PAUSED should set pipeline and bin2 to PAUSED */ + /* setting fakesink to PAUSED should not affect pipeline and bin2 */ gst_element_set_state (fakesink, GST_STATE_PAUSED); g_assert (GST_STATE (bin1) == GST_STATE_READY); - g_assert (GST_STATE (bin2) == GST_STATE_PAUSED); + g_assert (GST_STATE (bin2) == GST_STATE_READY); g_assert (GST_STATE (fakesrc) == GST_STATE_READY); g_assert (GST_STATE (identity) == GST_STATE_READY); - g_assert (GST_STATE (fakesink) == GST_STATE_PAUSED); + g_assert (GST_STATE (fakesink) == GST_STATE_READY); - /* setting fakesrc to PAUSED should set bin1 and fakesrc to PAUSED */ + /* setting fakesrc to PAUSED should not affect bin1 */ gst_element_set_state (fakesrc, GST_STATE_PAUSED); - g_assert (GST_STATE (bin1) == GST_STATE_PAUSED); - g_assert (GST_STATE (bin2) == GST_STATE_PAUSED); + g_assert (GST_STATE (bin1) == GST_STATE_READY); + g_assert (GST_STATE (bin2) == GST_STATE_READY); g_assert (GST_STATE (fakesrc) == GST_STATE_PAUSED); g_assert (GST_STATE (identity) == GST_STATE_READY); - g_assert (GST_STATE (fakesink) == GST_STATE_PAUSED); + g_assert (GST_STATE (fakesink) == GST_STATE_READY); /* setting bin1 to PAUSED, even though it is already, should set * identity to PAUSED as well */ gst_element_set_state (bin1, GST_STATE_PAUSED); + gst_element_get_state (bin2, NULL, NULL, NULL); g_assert (GST_STATE (bin1) == GST_STATE_PAUSED); - g_assert (GST_STATE (bin2) == GST_STATE_PAUSED); + g_assert (GST_STATE (bin2) == GST_STATE_READY); g_assert (GST_STATE (fakesrc) == GST_STATE_PAUSED); g_assert (GST_STATE (identity) == GST_STATE_PAUSED); g_assert (GST_STATE (fakesink) == GST_STATE_PAUSED); + gst_element_set_state (pipeline, GST_STATE_PLAYING); + g_usleep (1000000); + g_print ("passed.\n"); return 0; } diff --git a/testsuite/states/locked.c b/testsuite/states/locked.c index ecf969ec3e..d66565ddeb 100644 --- a/testsuite/states/locked.c +++ b/testsuite/states/locked.c @@ -17,50 +17,85 @@ * Boston, MA 02111-1307, USA. */ +#include "unistd.h" + #include +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; +} + gint main (gint argc, gchar * argv[]) { GstElement *pipeline; GstElement *fakesrc1, *fakesink1; GstElement *fakesrc2, *fakesink2; + GstBus *bus; gst_init (&argc, &argv); 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", 5, NULL); fakesink1 = gst_element_factory_make ("fakesink", "fakesink1"); - gst_bin_add_many (GST_BIN (pipeline), fakesrc1, fakesink1, NULL); - gst_element_link_pads (fakesrc1, "src", fakesink1, "sink"); + 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")); fakesrc2 = gst_element_factory_make ("fakesrc", "fakesrc2"); g_object_set (G_OBJECT (fakesrc2), "num_buffers", 5, NULL); fakesink2 = gst_element_factory_make ("fakesink", "fakesink2"); - gst_bin_add_many (GST_BIN (pipeline), fakesrc2, fakesink2, NULL); - gst_element_link_pads (fakesrc2, "src", fakesink2, "sink"); + gst_bin_add (GST_BIN (pipeline), fakesrc2); + gst_bin_add (GST_BIN (pipeline), fakesink2); + gst_pad_link (gst_element_get_pad (fakesrc2, "src"), + gst_element_get_pad (fakesink2, "sink")); g_signal_connect (G_OBJECT (pipeline), "deep_notify", - G_CALLBACK (gst_element_default_deep_notify), NULL); + G_CALLBACK (gst_object_default_deep_notify), NULL); GST_FLAG_SET (fakesrc2, GST_ELEMENT_LOCKED_STATE); GST_FLAG_SET (fakesink2, GST_ELEMENT_LOCKED_STATE); + g_print ("play..\n"); gst_element_set_state (pipeline, GST_STATE_PLAYING); - while (gst_bin_iterate (GST_BIN (pipeline))); - gst_element_set_state (pipeline, GST_STATE_READY); + + g_main_loop_run (loop); g_object_set (G_OBJECT (fakesrc1), "num_buffers", 5, NULL); + gst_element_set_state (pipeline, GST_STATE_READY); + GST_FLAG_UNSET (fakesrc2, GST_ELEMENT_LOCKED_STATE); GST_FLAG_UNSET (fakesink2, GST_ELEMENT_LOCKED_STATE); + g_print ("play..\n"); gst_element_set_state (pipeline, GST_STATE_PLAYING); - while (gst_bin_iterate (GST_BIN (pipeline))); + + g_main_loop_run (loop); + gst_element_set_state (pipeline, GST_STATE_NULL); gst_object_unref (GST_OBJECT (pipeline)); diff --git a/testsuite/states/parent.c b/testsuite/states/parent.c index aa583c5aa3..fe8b2441c6 100644 --- a/testsuite/states/parent.c +++ b/testsuite/states/parent.c @@ -67,7 +67,7 @@ main (gint argc, gchar * argv[]) gst_bin_add (GST_BIN (pipeline), bin2); g_signal_connect (G_OBJECT (pipeline), "deep_notify", - G_CALLBACK (gst_element_default_deep_notify), NULL); + G_CALLBACK (gst_object_default_deep_notify), NULL); /* setting pipeline to READY should bring in all children to READY */ gst_element_set_state (pipeline, GST_STATE_READY); @@ -77,31 +77,35 @@ main (gint argc, gchar * argv[]) g_assert (GST_STATE (identity) == GST_STATE_READY); g_assert (GST_STATE (fakesink) == GST_STATE_READY); - /* setting fakesink to PAUSED should set pipeline and bin2 to PAUSED */ + /* setting fakesink to PAUSED should not affect pipeline and bin2 */ gst_element_set_state (fakesink, GST_STATE_PAUSED); g_assert (GST_STATE (bin1) == GST_STATE_READY); - g_assert (GST_STATE (bin2) == GST_STATE_PAUSED); + g_assert (GST_STATE (bin2) == GST_STATE_READY); g_assert (GST_STATE (fakesrc) == GST_STATE_READY); g_assert (GST_STATE (identity) == GST_STATE_READY); - g_assert (GST_STATE (fakesink) == GST_STATE_PAUSED); + g_assert (GST_STATE (fakesink) == GST_STATE_READY); - /* setting fakesrc to PAUSED should set bin1 and fakesrc to PAUSED */ + /* setting fakesrc to PAUSED should not affect bin1 */ gst_element_set_state (fakesrc, GST_STATE_PAUSED); - g_assert (GST_STATE (bin1) == GST_STATE_PAUSED); - g_assert (GST_STATE (bin2) == GST_STATE_PAUSED); + g_assert (GST_STATE (bin1) == GST_STATE_READY); + g_assert (GST_STATE (bin2) == GST_STATE_READY); g_assert (GST_STATE (fakesrc) == GST_STATE_PAUSED); g_assert (GST_STATE (identity) == GST_STATE_READY); - g_assert (GST_STATE (fakesink) == GST_STATE_PAUSED); + g_assert (GST_STATE (fakesink) == GST_STATE_READY); /* setting bin1 to PAUSED, even though it is already, should set * identity to PAUSED as well */ gst_element_set_state (bin1, GST_STATE_PAUSED); + gst_element_get_state (bin2, NULL, NULL, NULL); g_assert (GST_STATE (bin1) == GST_STATE_PAUSED); - g_assert (GST_STATE (bin2) == GST_STATE_PAUSED); + g_assert (GST_STATE (bin2) == GST_STATE_READY); g_assert (GST_STATE (fakesrc) == GST_STATE_PAUSED); g_assert (GST_STATE (identity) == GST_STATE_PAUSED); g_assert (GST_STATE (fakesink) == GST_STATE_PAUSED); + gst_element_set_state (pipeline, GST_STATE_PLAYING); + g_usleep (1000000); + g_print ("passed.\n"); return 0; } diff --git a/tools/gst-inspect.c b/tools/gst-inspect.c index ab1403f174..eff4feb1fa 100644 --- a/tools/gst-inspect.c +++ b/tools/gst-inspect.c @@ -505,28 +505,12 @@ print_element_flag_info (GstElement * element) n_print ("\n"); n_print ("Element Flags:\n"); - if (GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED)) { - n_print (" GST_ELEMENT_DECOUPLED\n"); - have_flags = TRUE; - } - if (GST_FLAG_IS_SET (element, GST_ELEMENT_EVENT_AWARE)) { - n_print (" GST_ELEMENT_EVENT_AWARE\n"); - have_flags = TRUE; - } if (!have_flags) n_print (" no flags set\n"); if (GST_IS_BIN (element)) { n_print ("\n"); n_print ("Bin Flags:\n"); - if (GST_FLAG_IS_SET (element, GST_BIN_FLAG_MANAGER)) { - n_print (" GST_BIN_FLAG_MANAGER\n"); - have_flags = TRUE; - } - if (GST_FLAG_IS_SET (element, GST_BIN_SELF_SCHEDULABLE)) { - n_print (" GST_BIN_SELF_SCHEDULABLE\n"); - have_flags = TRUE; - } if (!have_flags) n_print (" no flags set\n"); } @@ -544,11 +528,7 @@ print_implementation_info (GstElement * element) n_print ("\n"); n_print ("Element Implementation:\n"); - if (element->loopfunc) - n_print (" loopfunc()-based element: %s\n", - GST_DEBUG_FUNCPTR_NAME (element->loopfunc)); - else - n_print (" No loopfunc(), must be chain-based or not configured yet\n"); + n_print (" No loopfunc(), must be chain-based or not configured yet\n"); n_print (" Has change_state() function: %s\n", GST_DEBUG_FUNCPTR_NAME (gstelement_class->change_state)); @@ -647,9 +627,9 @@ print_pad_info (GstElement * element) if (realpad->chainfunc) n_print (" Has chainfunc(): %s\n", GST_DEBUG_FUNCPTR_NAME (realpad->chainfunc)); - if (realpad->getfunc) - n_print (" Has getfunc(): %s\n", - GST_DEBUG_FUNCPTR_NAME (realpad->getfunc)); + if (realpad->getrangefunc) + n_print (" Has getrangefunc(): %s\n", + GST_DEBUG_FUNCPTR_NAME (realpad->getrangefunc)); if (realpad->formatsfunc != gst_pad_get_formats_default) { n_print (" Supports seeking/conversion/query formats:\n"); print_formats (gst_pad_get_formats (GST_PAD (realpad))); diff --git a/tools/gst-launch.c b/tools/gst-launch.c index bcda0bfb61..20f7db07db 100644 --- a/tools/gst-launch.c +++ b/tools/gst-launch.c @@ -59,72 +59,11 @@ static void sigint_restore (void); #endif static gint max_iterations = 0; -static guint64 iterations = 0; -static guint64 sum = 0; -static guint64 min = G_MAXINT64; -static guint64 max = 0; -static GstClock *s_clock; static GstElement *pipeline; -static gboolean caught_intr = FALSE; -static gboolean caught_error = FALSE; -static gboolean need_new_state = FALSE; -static GstElementState new_state; -static GMainLoop *loop; +gboolean caught_intr = FALSE; +gboolean caught_error = FALSE; +gboolean tags = FALSE; -gboolean -idle_func (gpointer data) -{ - gboolean busy; - GTimeVal tfthen, tfnow; - GstClockTimeDiff diff; - - g_get_current_time (&tfthen); - busy = gst_bin_iterate (GST_BIN (data)); - iterations++; - g_get_current_time (&tfnow); - - diff = GST_TIMEVAL_TO_TIME (tfnow) - GST_TIMEVAL_TO_TIME (tfthen); - - sum += diff; - min = MIN (min, diff); - max = MAX (max, diff); - - if (need_new_state) { - gst_element_set_state (pipeline, new_state); - need_new_state = FALSE; - } - - if (!busy || caught_intr || caught_error || - (max_iterations > 0 && iterations >= max_iterations)) { - char *s_iterations; - char *s_sum; - char *s_ave; - char *s_min; - char *s_max; - - g_main_loop_quit (loop); - g_main_loop_unref (loop); - - /* We write these all to strings first because - * G_GUINT64_FORMAT and gettext mix very poorly */ - s_iterations = g_strdup_printf ("%" G_GUINT64_FORMAT, iterations); - s_sum = g_strdup_printf ("%" G_GUINT64_FORMAT, sum); - s_ave = g_strdup_printf ("%" G_GUINT64_FORMAT, sum / iterations); - s_min = g_strdup_printf ("%" G_GUINT64_FORMAT, min); - s_max = g_strdup_printf ("%" G_GUINT64_FORMAT, max); - - g_print (_("Execution ended after %s iterations (sum %s ns, " - "average %s ns, min %s ns, max %s ns).\n"), - s_iterations, s_sum, s_ave, s_min, s_max); - g_free (s_iterations); - g_free (s_sum); - g_free (s_ave); - g_free (s_min); - g_free (s_max); - } - - return busy; -} #ifndef GST_DISABLE_LOADSAVE static GstElement * @@ -323,21 +262,6 @@ print_tag (const GstTagList * list, const gchar * tag, gpointer unused) } } -static void -found_tag (GObject * pipeline, GstElement * source, GstTagList * tags) -{ - g_print (_("FOUND TAG : found by element \"%s\".\n"), - GST_STR_NULL (GST_ELEMENT_NAME (source))); - gst_tag_list_foreach (tags, print_tag, NULL); -} - -static void -error_cb (GObject * object, GstObject * source, GError * error, gchar * debug) -{ - gst_element_default_error (object, source, error, debug); - caught_error = TRUE; -} - #ifndef DISABLE_FAULT_HANDLER /* we only use sighandler here because the registers are not important */ static void @@ -350,6 +274,20 @@ sigint_handler_sighandler (int signum) caught_intr = TRUE; } +static gboolean +check_intr (GstElement * pipeline) +{ + if (!caught_intr) { + return TRUE; + } else { + caught_intr = FALSE; + g_print ("Pausing pipeline.\n"); + gst_element_set_state (pipeline, GST_STATE_PAUSED); + + return FALSE; + } +} + static void sigint_setup (void) { @@ -377,12 +315,12 @@ play_handler (int signum) { switch (signum) { case SIGUSR1: - new_state = GST_STATE_PLAYING; - need_new_state = TRUE; + g_print ("Caught SIGUSR1 - Play request.\n"); + gst_element_set_state (pipeline, GST_STATE_PLAYING); break; case SIGUSR2: - new_state = GST_STATE_NULL; - need_new_state = TRUE; + g_print ("Caught SIGUSR2 - Stop request.\n"); + gst_element_set_state (pipeline, GST_STATE_NULL); break; } } @@ -397,7 +335,80 @@ play_signal_setup (void) sigaction (SIGUSR1, &action, NULL); sigaction (SIGUSR2, &action, NULL); } -#endif +#endif /* DISABLE_FAULT_HANDLER */ + +static gboolean +event_loop (GstElement * pipeline, gboolean blocking) +{ + GstBus *bus; + GstMessageType revent; + GstMessage *message = NULL; + + bus = gst_element_get_bus (GST_ELEMENT (pipeline)); + + g_timeout_add (50, (GSourceFunc) check_intr, pipeline); + + while (TRUE) { + revent = gst_bus_poll (bus, GST_MESSAGE_ANY, blocking ? -1 : 0); + + /* if the poll timed out, only when !blocking */ + if (revent == GST_MESSAGE_UNKNOWN) + return FALSE; + + message = gst_bus_pop (bus); + g_return_val_if_fail (message != NULL, TRUE); + + switch (revent) { + case GST_MESSAGE_EOS: + gst_message_unref (message); + return FALSE; + case GST_MESSAGE_TAG: + if (tags) { + GstTagList *tags; + + gst_message_parse_tag (message, &tags); + g_print (_("FOUND TAG : found by element \"%s\".\n"), + GST_STR_NULL (GST_ELEMENT_NAME (GST_MESSAGE_SRC (message)))); + gst_tag_list_foreach (tags, print_tag, NULL); + gst_tag_list_free (tags); + } + gst_message_unref (message); + break; + case GST_MESSAGE_WARNING: + case GST_MESSAGE_ERROR:{ + GError *gerror; + gchar *debug; + + gst_message_parse_error (message, &gerror, &debug); + gst_message_unref (message); + gst_object_default_error (GST_MESSAGE_SRC (message), gerror, debug); + g_error_free (gerror); + g_free (debug); + return TRUE; + } + case GST_MESSAGE_STATE_CHANGED:{ + GstElementState old, new; + + gst_message_parse_state_changed (message, &old, &new); + gst_message_unref (message); + if (!(old == GST_STATE_PLAYING && new == GST_STATE_PAUSED)) + break; + g_print (_ + ("Element \"%s\" has gone from PLAYING to PAUSED, quitting.\n"), + GST_STR_NULL (GST_ELEMENT_NAME (GST_MESSAGE_SRC (message)))); + /* cut out of the event loop if check_intr set us to PAUSED */ + return FALSE; + } + default: + /* just be quiet by default */ + gst_message_unref (message); + break; + } + } + + g_assert_not_reached (); + return TRUE; +} int main (int argc, char *argv[]) @@ -406,7 +417,6 @@ main (int argc, char *argv[]) /* options */ gboolean verbose = FALSE; - gboolean tags = FALSE; gboolean no_fault = FALSE; gboolean trace = FALSE; gchar *savefile = NULL; @@ -514,13 +524,8 @@ main (int argc, char *argv[]) gchar **exclude_list = exclude_args ? g_strsplit (exclude_args, ",", 0) : NULL; g_signal_connect (pipeline, "deep_notify", - G_CALLBACK (gst_element_default_deep_notify), exclude_list); + G_CALLBACK (gst_object_default_deep_notify), exclude_list); } - if (tags) { - g_signal_connect (pipeline, "found-tag", G_CALLBACK (found_tag), NULL); - } - g_signal_connect (pipeline, "error", G_CALLBACK (error_cb), NULL); - #ifndef GST_DISABLE_LOADSAVE if (savefile) { gst_xml_write_file (GST_ELEMENT (pipeline), fopen (savefile, "w")); @@ -528,6 +533,7 @@ main (int argc, char *argv[]) #endif if (!savefile) { + GstElementState state, pending; if (!GST_IS_BIN (pipeline)) { GstElement *real_pipeline = gst_element_factory_make ("pipeline", NULL); @@ -540,37 +546,55 @@ main (int argc, char *argv[]) pipeline = real_pipeline; } - fprintf (stderr, _("RUNNING pipeline ...\n")); - if (gst_element_set_state (pipeline, - GST_STATE_PLAYING) == GST_STATE_FAILURE) { - fprintf (stderr, _("ERROR: pipeline doesn't want to play.\n")); + fprintf (stderr, _("PREROLL pipeline ...\n")); + if (gst_element_set_state (pipeline, GST_STATE_PAUSED) == GST_STATE_FAILURE) { + fprintf (stderr, _("ERROR: pipeline doesn't want to pause.\n")); res = -1; goto end; } - s_clock = gst_element_get_clock (GST_ELEMENT (pipeline)); + gst_element_get_state (pipeline, &state, &pending, NULL); + caught_error = event_loop (pipeline, FALSE); - if (!GST_FLAG_IS_SET (GST_OBJECT (pipeline), GST_BIN_SELF_SCHEDULABLE)) { - g_idle_add (idle_func, pipeline); - loop = g_main_loop_new (NULL, FALSE); - g_main_loop_run (loop); + /* see if we got any messages */ + while (g_main_context_iteration (NULL, FALSE)); + + if (caught_error) { + fprintf (stderr, _("ERROR: pipeline doesn't want to preroll.\n")); } else { - g_print ("Waiting for the state change... "); - gst_element_wait_state_change (pipeline); - g_print ("got the state change.\n"); - } - if (caught_intr) { - g_print ("Caught interrupt.\n"); - res = 2; - } - if (caught_error) - res = 3; + GTimeVal tfthen, tfnow; + GstClockTimeDiff diff; + fprintf (stderr, _("RUNNING pipeline ...\n")); + if (gst_element_set_state (pipeline, + GST_STATE_PLAYING) == GST_STATE_FAILURE) { + fprintf (stderr, _("ERROR: pipeline doesn't want to play.\n")); + res = -1; + goto end; + } + + g_get_current_time (&tfthen); + caught_error = event_loop (pipeline, TRUE); + g_get_current_time (&tfnow); + + diff = GST_TIMEVAL_TO_TIME (tfnow) - GST_TIMEVAL_TO_TIME (tfthen); + + g_print (_("Execution ended after %" G_GUINT64_FORMAT " ns.\n"), diff); + } + fprintf (stderr, _("PAUSE pipeline ...\n")); + gst_element_set_state (pipeline, GST_STATE_PAUSED); + gst_element_get_state (pipeline, &state, &pending, NULL); + fprintf (stderr, _("READY pipeline ...\n")); + gst_element_set_state (pipeline, GST_STATE_READY); + gst_element_get_state (pipeline, &state, &pending, NULL); + fprintf (stderr, _("NULL pipeline ...\n")); gst_element_set_state (pipeline, GST_STATE_NULL); + gst_element_get_state (pipeline, &state, &pending, NULL); } end: + fprintf (stderr, _("FREEING pipeline ...\n")); gst_object_unref (GST_OBJECT (pipeline)); if (trace) diff --git a/tools/gst-md5sum.c b/tools/gst-md5sum.c index c5052f6a6b..33f7d8eb6f 100644 --- a/tools/gst-md5sum.c +++ b/tools/gst-md5sum.c @@ -7,77 +7,65 @@ #include #include -static guint64 iterations = 0; -static guint64 sum = 0; -static guint64 min = G_MAXINT64; -static guint64 max = 0; -static GstClock *s_clock; -static GMainLoop *loop; - -gboolean -idle_func (gpointer data) +/* blocking */ +static gboolean +event_loop (GstElement * pipeline) { - gboolean busy; - GTimeVal tfthen, tfnow; - GstClockTimeDiff diff; + GstBus *bus; + GstMessageType revent; + GstMessage *message = NULL; - if (s_clock) { - //g_print ("%lld\n", gst_clock_get_time (s_clock)); + bus = gst_element_get_bus (GST_ELEMENT (pipeline)); + + while (TRUE) { + revent = gst_bus_poll (bus, GST_MESSAGE_ANY, -1); + + message = gst_bus_pop (bus); + g_return_val_if_fail (message != NULL, TRUE); + + switch (revent) { + case GST_MESSAGE_EOS: + gst_message_unref (message); + return FALSE; + case GST_MESSAGE_WARNING: + case GST_MESSAGE_ERROR:{ + GError *gerror; + gchar *debug; + + gst_message_parse_error (message, &gerror, &debug); + gst_message_unref (message); + gst_object_default_error (GST_MESSAGE_SRC (message), gerror, debug); + g_error_free (gerror); + g_free (debug); + return TRUE; + } + default: + gst_message_unref (message); + break; + } } - g_get_current_time (&tfthen); - busy = gst_bin_iterate (GST_BIN (data)); - iterations++; - g_get_current_time (&tfnow); - - diff = GST_TIMEVAL_TO_TIME (tfnow) - GST_TIMEVAL_TO_TIME (tfthen); - - sum += diff; - min = MIN (min, diff); - max = MAX (max, diff); - - if (!busy) { - g_main_loop_quit (loop); - g_main_loop_unref (loop); - /* - g_print ("execution ended after %llu iterations (sum %llu ns, average %llu ns, min %llu ns, max %llu ns)\n", - iterations, sum, sum/iterations, min, max); - */ - } - - return busy; + g_assert_not_reached (); + return TRUE; } int main (int argc, char *argv[]) { - /* options */ - gboolean verbose = FALSE; - gchar *exclude_args = NULL; - struct poptOption options[] = { - {"verbose", 'v', POPT_ARG_NONE | POPT_ARGFLAG_STRIP, &verbose, 0, - "do not output status information", NULL}, - POPT_TABLEEND - }; - GstElement *pipeline = NULL; - gchar **argvn; GError *error = NULL; GstElement *md5sink; + gchar **argvn; gchar *md5string = g_malloc0 (33); free (malloc (8)); /* -lefence */ setlocale (LC_ALL, ""); - gst_init_with_popt_table (&argc, &argv, options); + gst_init (&argc, &argv); - /* make a parseable argvn array */ argvn = g_new0 (char *, argc); memcpy (argvn, argv + 1, sizeof (char *) * (argc - 1)); - - /* Check if we have an element already that is called md5sink0 - in the pipeline; if not, add one */ pipeline = (GstElement *) gst_parse_launchv ((const gchar **) argvn, &error); if (!pipeline) { if (error) { @@ -90,17 +78,9 @@ main (int argc, char *argv[]) md5sink = gst_bin_get_by_name (GST_BIN (pipeline), "md5sink0"); if (md5sink == NULL) { - g_print ("adding an md5sink element to the pipeline\n"); - /* make a null-terminated version of argv with ! md5sink appended - * ! is stored in argvn[argc - 1], md5sink in argvn[argc], - * NULL pointer in argvn[argc + 1] */ - g_free (argvn); - argvn = g_new0 (char *, argc + 2); - memcpy (argvn, argv + 1, sizeof (char *) * (argc - 1)); - argvn[argc - 1] = g_strdup_printf ("!"); - argvn[argc] = g_strdup_printf ("md5sink"); - pipeline = - (GstElement *) gst_parse_launchv ((const gchar **) argvn, &error); + g_print ("ERROR: pipeline has no element named md5sink0.\n"); + g_print ("Did you forget to put an md5sink in the pipeline?\n"); + return 1; } if (!pipeline) { @@ -112,28 +92,12 @@ main (int argc, char *argv[]) return 1; } - if (verbose) { - gchar **exclude_list = exclude_args ? g_strsplit (exclude_args, ",", 0) - : NULL; - - g_signal_connect (pipeline, "deep_notify", - G_CALLBACK (gst_element_default_deep_notify), exclude_list); - } - g_signal_connect (pipeline, "error", - G_CALLBACK (gst_element_default_error), NULL); - if (gst_element_set_state (pipeline, GST_STATE_PLAYING) != GST_STATE_SUCCESS) { g_warning ("pipeline doesn't want to play\n"); - return 0; + return 1; } - if (!GST_FLAG_IS_SET (GST_OBJECT (pipeline), GST_BIN_SELF_SCHEDULABLE)) { - g_idle_add (idle_func, pipeline); - loop = g_main_loop_new (NULL, FALSE); - g_main_loop_run (loop); - } else { - gst_element_wait_state_change (pipeline); - } + event_loop (pipeline); gst_element_set_state (pipeline, GST_STATE_NULL); diff --git a/tools/gst-typefind.c b/tools/gst-typefind.c index bf463431b0..b3fe66c6dc 100644 --- a/tools/gst-typefind.c +++ b/tools/gst-typefind.c @@ -68,8 +68,6 @@ main (int argc, char *argv[]) gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING); while (!FOUND) { - if (!gst_bin_iterate (GST_BIN (pipeline))) - break; } if (!FOUND) { g_print ("%s - No type found\n", argv[i]); diff --git a/tools/gst-xmlinspect.c b/tools/gst-xmlinspect.c index 847257fb0e..9ce4233104 100644 --- a/tools/gst-xmlinspect.c +++ b/tools/gst-xmlinspect.c @@ -472,8 +472,6 @@ print_element_info (GstElementFactory * factory) GstPad *pad; GstRealPad *realpad; GstPadTemplate *padtemplate; - GList *children; - GstElement *child; gint maxlevel = 0; element = gst_element_factory_create (factory, "element"); @@ -533,31 +531,16 @@ print_element_info (GstElementFactory * factory) PUT_END_TAG (1, "pad-templates"); PUT_START_TAG (1, "element-flags"); - if (GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED)) { - PUT_ESCAPED (2, "flag", "GST_ELEMENT_DECOUPLED"); - } - if (GST_FLAG_IS_SET (element, GST_ELEMENT_EVENT_AWARE)) { - PUT_ESCAPED (2, "flag", "GST_ELEMENT_EVENT_AWARE"); - } PUT_END_TAG (1, "element-flags"); if (GST_IS_BIN (element)) { PUT_START_TAG (1, "bin-flags"); - if (GST_FLAG_IS_SET (element, GST_BIN_FLAG_MANAGER)) { - PUT_ESCAPED (2, "flag", "GST_BIN_FLAG_MANAGER"); - } - if (GST_FLAG_IS_SET (element, GST_BIN_SELF_SCHEDULABLE)) { - PUT_ESCAPED (2, "flag", "GST_BIN_SELF_SCHEDULABLE"); - } PUT_END_TAG (1, "bin-flags"); } PUT_START_TAG (1, "element-implementation"); - if (element->loopfunc) - PUT_STRING (2, "", - GST_DEBUG_FUNCPTR_NAME (element->loopfunc)); PUT_STRING (2, "", GST_DEBUG_FUNCPTR_NAME (gstelement_class->change_state)); @@ -619,9 +602,9 @@ print_element_info (GstElementFactory * factory) if (realpad->chainfunc) PUT_STRING (4, "", GST_DEBUG_FUNCPTR_NAME (realpad->chainfunc)); - if (realpad->getfunc) - PUT_STRING (4, "", - GST_DEBUG_FUNCPTR_NAME (realpad->getfunc)); + if (realpad->getrangefunc) + PUT_STRING (4, "", + GST_DEBUG_FUNCPTR_NAME (realpad->getrangefunc)); if (realpad->formatsfunc != gst_pad_get_formats_default) { PUT_STRING (4, "", GST_DEBUG_FUNCPTR_NAME (realpad->formatsfunc)); @@ -672,17 +655,21 @@ print_element_info (GstElementFactory * factory) print_element_signals (element, 1); /* for compound elements */ - if (GST_IS_BIN (element)) { - PUT_START_TAG (1, "children"); - children = (GList *) GST_BIN (element)->children; - while (children) { - child = GST_ELEMENT (children->data); - children = g_list_next (children); + /* FIXME: gst_bin_get_list does not exist anymore + if (GST_IS_BIN (element)) { + GList *children; + GstElement *child; + PUT_START_TAG (1, "children"); + children = (GList *) gst_bin_get_list (GST_BIN (element)); + while (children) { + child = GST_ELEMENT (children->data); + children = g_list_next (children); - PUT_ESCAPED (2, "child", GST_ELEMENT_NAME (child)); - } - PUT_END_TAG (1, "children"); - } + PUT_ESCAPED (2, "child", GST_ELEMENT_NAME (child)); + } + PUT_END_TAG (1, "children"); + } + */ PUT_END_TAG (0, "element"); return 0;