From c2f41a8906da4d2694eb111154283c6c9334b9de Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 21 Mar 2005 17:34:02 +0000 Subject: [PATCH] Next big merge. Original commit message from CVS: 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. --- ChangeLog | 190 ++ check/Makefile.am | 1 + common | 2 +- gst/Makefile.am | 27 +- gst/elements/Makefile.am | 29 +- gst/elements/gstelements.c | 22 +- gst/elements/gstfakesink.c | 66 +- gst/elements/gstfakesrc.c | 325 ++- gst/elements/gstfakesrc.h | 4 +- gst/elements/gstfilesrc.c | 247 +- gst/elements/gstidentity.c | 482 +++- gst/elements/gstidentity.h | 16 +- gst/elements/gsttee.c | 378 +-- gst/elements/gsttee.h | 6 + gst/gst.c | 6 +- gst/gst.h | 3 +- gst/gstbin.c | 618 +---- gst/gstbin.h | 21 - gst/gstbus.c | 621 +++++ gst/gstbus.h | 103 + gst/gstcaps.h | 2 +- gst/gstdata.h | 2 +- gst/gstelement.c | 1238 +++------- gst/gstelement.h | 140 +- gst/gstevent.c | 71 +- gst/gstevent.h | 46 +- gst/gstmessage.c | 404 ++++ gst/gstmessage.h | 141 ++ gst/gstpad.c | 3316 +++++++++++--------------- gst/gstpad.h | 230 +- gst/gstpipeline.c | 161 +- gst/gstpipeline.h | 3 + gst/gstprobe.h | 2 +- gst/gstqueue.c | 777 +++--- gst/gstqueue.h | 12 +- gst/gstscheduler.c | 645 +---- gst/gstscheduler.h | 78 +- gst/gststructure.c | 2 +- gst/gststructure.h | 2 +- gst/gsttaginterface.h | 2 +- gst/gsttagsetter.h | 2 +- gst/gsttask.c | 210 ++ gst/gsttask.h | 96 + gst/gstthread.c | 746 ------ gst/gstthread.h | 88 - gst/gsttypes.h | 1 + gst/schedulers/Makefile.am | 81 +- gst/schedulers/cothreads_compat.h | 79 - gst/schedulers/entryscheduler.c | 1175 --------- gst/schedulers/faircothreads.c | 615 ----- gst/schedulers/faircothreads.h | 163 -- gst/schedulers/fairscheduler.c | 1415 ----------- gst/schedulers/gstbasicscheduler.c | 1494 ------------ gst/schedulers/gstoptimalscheduler.c | 2933 ----------------------- gst/schedulers/gthread-cothreads.h | 221 -- gst/schedulers/threadscheduler.c | 347 +++ libs/gst/Makefile.am | 2 +- libs/gst/bytestream/bytestream.c | 2 +- libs/gst/bytestream/filepad.c | 12 +- libs/gst/dataprotocol/dataprotocol.c | 33 - plugins/elements/Makefile.am | 29 +- plugins/elements/gstelements.c | 22 +- plugins/elements/gstfakesink.c | 66 +- plugins/elements/gstfakesrc.c | 325 ++- plugins/elements/gstfakesrc.h | 4 +- plugins/elements/gstfilesrc.c | 247 +- plugins/elements/gstidentity.c | 482 +++- plugins/elements/gstidentity.h | 16 +- plugins/elements/gstqueue.c | 777 +++--- plugins/elements/gstqueue.h | 12 +- plugins/elements/gsttee.c | 378 +-- plugins/elements/gsttee.h | 6 + tests/benchmarks/complexity.c | 3 +- tests/benchmarks/mass-elements.c | 3 +- tests/check/Makefile.am | 1 + tests/complexity.c | 3 +- tests/mass_elements.c | 3 +- tests/old/testsuite/states/locked.c | 51 +- tests/old/testsuite/states/parent.c | 22 +- testsuite/states/locked.c | 51 +- testsuite/states/parent.c | 22 +- tools/gst-inspect.c | 28 +- tools/gst-launch.c | 244 +- tools/gst-md5sum.c | 122 +- tools/gst-typefind.c | 2 - tools/gst-xmlinspect.c | 47 +- 86 files changed, 7431 insertions(+), 15691 deletions(-) create mode 100644 gst/gstbus.c create mode 100644 gst/gstbus.h create mode 100644 gst/gstmessage.c create mode 100644 gst/gstmessage.h create mode 100644 gst/gsttask.c create mode 100644 gst/gsttask.h delete mode 100644 gst/gstthread.c delete mode 100644 gst/gstthread.h delete mode 100644 gst/schedulers/cothreads_compat.h delete mode 100644 gst/schedulers/entryscheduler.c delete mode 100644 gst/schedulers/faircothreads.c delete mode 100644 gst/schedulers/faircothreads.h delete mode 100644 gst/schedulers/fairscheduler.c delete mode 100644 gst/schedulers/gstbasicscheduler.c delete mode 100644 gst/schedulers/gstoptimalscheduler.c delete mode 100644 gst/schedulers/gthread-cothreads.h create mode 100644 gst/schedulers/threadscheduler.c 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;