+2005-03-21 Wim Taymans <wim@fluendo.com>
+
+ * 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 <wim@fluendo.com>
* docs/design/part-MT-refcounting.txt:
TESTS = $(top_builddir)/tools/gst-register-@GST_MAJORMINOR@ \
gst/gstbin \
+ gst/gstbus \
gst/gstcaps \
gst/gstdata \
gst/gstiterator \
-Subproject commit b2638c100721f67b280c3b43b21f1ce1c9b5e316
+Subproject commit 131c2632127e6f061b5270d8f80651782a4fdd13
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
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
gstatomic.c \
gstbin.c \
gstbuffer.c \
+ gstbus.c \
gstcaps.c \
gstclock.c \
gstcpu.c \
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 \
gstobject.h \
gstbin.h \
gstbuffer.h \
+ gstbus.h \
gstcaps.h \
gstclock.h \
gstcompat.h \
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 \
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
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)
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
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 };
"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;
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
}
}
-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);
+
+ fakesink->last_message =
+ g_strdup_printf ("chain ******* (%s:%s)E (type: %d) %p",
+ GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE (event), event);
- if (!fakesink->silent) {
- g_free (fakesink->last_message);
+ g_object_notify (G_OBJECT (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);
-
- 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;
+ 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) {
}
gst_buffer_unref (buf);
+
+ return GST_FLOW_OK;
}
static GstElementStateReturn
{
ARG_0,
ARG_NUM_SOURCES,
- ARG_LOOP_BASED,
+ ARG_HAS_LOOP,
+ ARG_HAS_GETRANGE,
ARG_OUTPUT,
ARG_DATA,
ARG_SIZETYPE,
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,
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 };
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));
"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;
fakesrc->last_message = NULL;
fakesrc->datarate = DEFAULT_DATARATE;
fakesrc->sync = DEFAULT_SYNC;
+ fakesrc->pad_mode = GST_ACTIVATE_NONE;
}
static void
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)
{
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},
};
{
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;
}
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->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);
+}
- 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);
- }
+static void
+gst_fakesrc_set_all_pad_functions (GstFakeSrc * src)
+{
+ GList *l;
- pads = GST_ELEMENT (src)->pads;
- while (pads) {
- GstPad *pad = GST_PAD (pads->data);
+ for (l = GST_ELEMENT_PADS (src); l; l = l->next)
+ gst_fakesrc_set_pad_functions (src, (GstPad *) l->data);
+}
- if (src->loop_based) {
- gst_pad_set_get_function (pad, NULL);
- } else {
- gst_pad_set_get_function (pad, GST_DEBUG_FUNCPTR (gst_fakesrc_get));
- }
+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);
- 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);
+ 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_set_pad_functions (fakesrc, srcpad);
+
+ g_free (name);
+
+ return srcpad;
}
static void
{
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");
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);
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--;
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);
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) =
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);
+
+ GST_STREAM_LOCK (pad);
+ if (src->need_flush) {
+ src->need_flush = FALSE;
+ gst_pad_push_event (pad, gst_event_new (GST_EVENT_FLUSH));
+ }
- pads = element->pads;
+ ret = gst_fakesrc_get_range_unlocked (pad, src->buffer_count,
+ DEFAULT_SIZEMAX, &buf);
+ if (ret != GST_FLOW_OK) {
+ goto pause;
+ }
- while (pads) {
- GstPad *pad = GST_PAD (pads->data);
- GstData *data;
+ ret = gst_pad_push (pad, buf);
+ if (ret != GST_FLOW_OK) {
+ goto pause;
+ }
- data = gst_fakesrc_get (pad);
- gst_pad_push (pad, data);
+ GST_STREAM_UNLOCK (pad);
+ return;
- if (src->eos) {
- 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);
- pads = g_list_next (pads);
+ 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);
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;
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:
break;
}
- if (GST_ELEMENT_CLASS (parent_class)->change_state)
- return GST_ELEMENT_CLASS (parent_class)->change_state (element);
-
- return GST_STATE_SUCCESS;
+ return result;
}
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;
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);
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);
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;
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) {
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 */
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 */
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;
}
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)) {
}
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));
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
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
/* GStreamer
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
* 2000 Wim Taymans <wtay@chello.be>
+ * 2005 Wim Taymans <wim@fluendo.com>
*
* gstidentity.c:
*
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");
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 };
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);
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));
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;
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
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");
+ }
- g_object_notify (G_OBJECT (identity), "last_message");
- }
- gst_pad_event_default (pad, event);
- return;
+ 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 &&
gst_buffer_unref (buf);
GST_ELEMENT_ERROR (identity, CORE, FAILED,
(_("Failed after iterations as requested.")), (NULL));
- return;
+ return GST_FLOW_ERROR;
}
}
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));
}
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) =
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;
-
- g_return_if_fail (element != NULL);
- g_return_if_fail (GST_IS_IDENTITY (element));
-
- identity = GST_IDENTITY (element);
-
- 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_getrange)
+ gst_pad_set_getrange_function (identity->srcpad, gst_identity_getrange);
+ else
+ gst_pad_set_getrange_function (identity->srcpad, NULL);
+
+ if (identity->has_chain)
+ gst_pad_set_chain_function (identity->sinkpad, gst_identity_chain);
+ else
+ gst_pad_set_chain_function (identity->sinkpad, NULL);
+
+ 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);
+
+ 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
{
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 PROP_HAS_CHAIN:
+ identity->has_chain = g_value_get_boolean (value);
+ gst_identity_set_dataflow_funcs (identity);
break;
- case ARG_SLEEP_TIME:
+ 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:
{
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 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 ARG_SLEEP_TIME:
+ 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:
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;
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;
gchar *last_message;
GstCaps *srccaps;
- guint64 bytes_handled;
+ guint64 offset;
};
struct _GstIdentityClass {
/* GStreamer
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
- * 2000 Wim Taymans <wim.taymans@chello.be>
+ * 2000,2001,2002,2003,2004,2005 Wim Taymans <wim@fluendo.com>
+ *
*
* gsttee.c: Tee element, one in N out
*
"Generic",
"1-to-N pipe fitting",
"Erik Walthinsen <omega@cse.ogi.edu>, "
- "Wim Taymans <wim.taymans@chello.be>");
+ "Wim \"Tim\" Taymans <wim@fluendo.com>");
-/* Tee signals and args */
enum
{
- /* FILL ME */
- LAST_SIGNAL
-};
-
-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 */
};
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
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);
}
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)
-{
- GstPad *pad = (GstPad *) a;
- gchar *name = (gchar *) b;
-
- g_assert (GST_IS_PAD (pad));
-
- 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)
+static void
+gst_tee_update_pad_functions (GstTee * tee)
{
- 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;
+ gst_pad_set_activate_function (tee->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_tee_sink_activate));
+
+ 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);
+
+ 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 *
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;
}
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.
- */
+typedef struct
+{
+ GstTee *tee;
+ GstBuffer *buffer;
+} PushData;
+
+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));
+
+ WITH_STREAM_LOCK (pad, res = gst_tee_handle_buffer (tee, buffer));
+
+ return res;
+}
+
+#define DEFAULT_SIZE 1024
+
static void
-gst_tee_chain (GstPad * pad, GstData * _data)
+gst_tee_loop (GstPad * pad)
{
- GstBuffer *buf = GST_BUFFER (_data);
+ GstBuffer *buffer;
+ GstFlowReturn res;
GstTee *tee;
- const GList *pads;
- g_return_if_fail (pad != NULL);
- g_return_if_fail (GST_IS_PAD (pad));
- g_return_if_fail (buf != NULL);
+ GST_STREAM_LOCK (pad);
tee = GST_TEE (gst_pad_get_parent (pad));
- gst_buffer_ref_by_count (buf, GST_ELEMENT (tee)->numsrcpads - 1);
+ res = gst_pad_pull_range (pad, tee->offset, DEFAULT_SIZE, &buffer);
+ if (res != GST_FLOW_OK)
+ goto pause_task;
- pads = GST_ELEMENT (tee)->pads;
+ res = gst_tee_handle_buffer (tee, buffer);
+ if (res != GST_FLOW_OK)
+ goto pause_task;
- while (pads) {
- GstPad *outpad = GST_PAD (pads->data);
+ GST_STREAM_UNLOCK (pad);
+ return;
- pads = g_list_next (pads);
+pause_task:
+ gst_pad_pause_task (pad);
+ GST_STREAM_UNLOCK (pad);
+ return;
+}
- if (GST_PAD_DIRECTION (outpad) != GST_PAD_SRC)
- continue;
+static gboolean
+gst_tee_sink_activate (GstPad * pad, GstActivateMode mode)
+{
+ gboolean result = FALSE;
+ GstTee *tee;
- 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");
- }
+ tee = GST_TEE (GST_OBJECT_PARENT (pad));
- if (GST_PAD_IS_USABLE (outpad))
- gst_pad_push (outpad, GST_DATA (buf));
- else
- gst_buffer_unref (buf);
+ 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;
}
GstPad *sinkpad;
gboolean silent;
+ gboolean has_chain;
+ gboolean has_sink_loop;
+ gint pad_counter;
+ guint64 offset;
+ GstActivateMode sink_mode;
+
gchar *last_message;
};
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;
_gst_plugin_initialize ();
_gst_event_initialize ();
_gst_buffer_initialize ();
+ _gst_message_initialize ();
_gst_tag_initialize ();
#ifndef GST_DISABLE_REGISTRY
#include <gst/gstinterface.h>
#include <gst/gstiterator.h>
#include <gst/gstmarshal.h>
+#include <gst/gstmessage.h>
#include <gst/gstobject.h>
#include <gst/gstpad.h>
#include <gst/gstpipeline.h>
#include <gst/gstsystemclock.h>
#include <gst/gsttag.h>
#include <gst/gsttaginterface.h>
-#include <gst/gstthread.h>
+#include <gst/gsttask.h>
#include <gst/gsttrace.h>
#include <gst/gsttypefind.h>
#include <gst/gsturi.h>
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);
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);
{
ELEMENT_ADDED,
ELEMENT_REMOVED,
- ITERATE,
LAST_SIGNAL
};
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);
#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
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);
-
- /* 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;
- }
-
- 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);
-
- /* 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_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;
+ GList *children;
+ GstBin *bin;
- pad = GST_PAD (pads->data);
- pads = g_list_next (pads);
+ bin = GST_BIN (element);
- /* we only operate on real pads */
- if (!GST_IS_REAL_PAD (pad))
- continue;
+ parent_class->set_bus (element, bus);
- /* 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");
+ GST_LOCK (bin);
+ for (children = bin->children; children; children = g_list_next (children)) {
+ GstElement *child = GST_ELEMENT (children->data);
- 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)) {
+ GList *children;
+ GstBin *bin;
- 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);
+ bin = GST_BIN (element);
- gst_scheduler_remove_element (GST_ELEMENT_SCHEDULER (element), element);
- } else {
- /* otherwise, if it's just a regular old element */
- GList *pads;
+ parent_class->set_scheduler (element, sched);
- 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_LOCK (bin);
+ for (children = bin->children; children; children = g_list_next (children)) {
+ GstElement *child = GST_ELEMENT (children->data);
- gst_scheduler_remove_element (GST_ELEMENT_SCHEDULER (element), element);
+ gst_element_set_scheduler (child, sched);
}
+ GST_UNLOCK (bin);
}
/* add an element to this bin
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);
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
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
- *
- * An internal function to inform the parent bin about a state change
- * of a child.
+/* this functions loops over all children, as soon as one does
+ * not return SUCCESS, we return that value.
*
- * Marked for removal.
+ * MT safe
*/
-void
-gst_bin_child_state_change (GstBin * bin, GstElementState oldstate,
- GstElementState newstate, GstElement * child)
-{
- 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));
- }
-}
-
-/* will be removed */
-static void
-gst_bin_child_state_change_func (GstBin * bin, GstElementState oldstate,
- GstElementState newstate, GstElement * child)
+static GstElementStateReturn
+gst_bin_get_state (GstElement * element, GstElementState * state,
+ GstElementState * pending, GTimeVal * timeout)
{
- 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);
+ /* implement me */
+ return GST_STATE_FAILURE;
}
-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
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)
(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;
-}
* 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;
GList *children;
guint32 children_cookie;
- GstElementState child_states[GST_NUM_STATES];
-
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};
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);
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
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
+ *
+ * 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 <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#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;
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
+ *
+ * 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 <gst/gsttypes.h>
+#include <gst/gstmessage.h>
+#include <gst/gstclock.h>
+
+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__ */
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,
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
#include <gobject/gvaluecollector.h>
#include "gstelement.h"
-#include "gstbin.h"
+#include "gstbus.h"
#include "gstmarshal.h"
#include "gsterror.h"
#include "gstscheduler.h"
STATE_CHANGE,
NEW_PAD,
PAD_REMOVED,
- ERROR,
- EOS,
- FOUND_TAG,
NO_MORE_PADS,
/* add more above */
LAST_SIGNAL
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,
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
*
#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;
}
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 ();
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.
- * <note>This function can only be called on elements in
- * #GST_STATE_PLAYING</note>
- *
- * 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:
#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.
* @pad: the #GstPad to add to the element.
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)
{
}
/**
+ * 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
*
}
/**
- * 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));
-
- /* 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);
+ g_return_if_fail ((type == GST_MESSAGE_ERROR) ||
+ (type == GST_MESSAGE_WARNING));
+
+ /* 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 (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);
+ g_free (name);
}
+ g_free (debug);
- if (GST_STATE (element) == GST_STATE_PLAYING) {
- GstElementStateReturn ret;
+ /* 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);
- 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));
- }
+ 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);
- 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 */
}
/**
return TRUE;
}
+/* MT safe */
+static GstElementStateReturn
+gst_element_get_state_func (GstElement * element,
+ GstElementState * state, GstElementState * pending, GTimeVal * timeout)
+{
+ GstElementStateReturn ret = GST_STATE_FAILURE;
+
+ /* implment me */
+ return ret;
+}
+
/**
* 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.
+ *
+ * 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.
*
- * Gets the state of the element.
+ * This function should be called with the STATE_LOCK held.
*
- * Returns: the #GstElementState of the element.
+ * MT safe.
*/
-GstElementState
-gst_element_get_state (GstElement * element)
+void
+gst_element_abort_state (GstElement * element)
{
- g_return_val_if_fail (GST_IS_ELEMENT (element), GST_STATE_VOID_PENDING);
+ /* implment me */
+}
- return GST_STATE (element);
+/**
+ * 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_wait_state_change:
- * @element: a #GstElement to wait for a state change on.
+ * gst_element_lost_state:
+ * @element: a #GstElement the state is lost of
*
- * Waits and blocks until the element changed its state.
+ * 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_wait_state_change (GstElement * element)
+gst_element_lost_state (GstElement * element)
{
- GST_STATE_LOCK (element);
- GST_STATE_WAIT (element);
- GST_STATE_UNLOCK (element);
+ /* implement me */
}
/**
* 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);
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:
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;
}
/**
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) {
}
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);
}
#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);
}
-/**
- * gst_element_interrupt:
- * @element: a #GstElement to interrupt.
- *
- * Requests the scheduler of this element to interrupt the execution of
- * this element and scheduler another one.
- *
- * Returns: TRUE if the element should exit its chain/loop/get
- * function ASAP, depending on the scheduler implementation.
- */
-gboolean
-gst_element_interrupt (GstElement * element)
+static void
+gst_element_set_bus_func (GstElement * element, GstBus * bus)
{
- if (GST_ELEMENT_SCHEDULER (element)) {
- return gst_scheduler_interrupt (GST_ELEMENT_SCHEDULER (element), element);
- } else
- return TRUE;
+ 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_set_scheduler:
- * @element: a #GstElement to set the scheduler of.
- * @scheduler: the #GstScheduler to set.
+ * gst_element_set_manager:
+ * @element: a #GstElement to set the manager of.
+ * @manager: the #GstManager to set.
*
- * Sets the scheduler of the element. For internal use only, unless you're
- * testing elements.
+ * Sets the manager of the element. For internal use only, unless you're
+ * writing a new bin subclass.
*
* MT safe.
*/
void
-gst_element_set_scheduler (GstElement * element, GstScheduler * scheduler)
+gst_element_set_manager (GstElement * element, GstPipeline * manager)
{
GstElementClass *oclass;
oclass = GST_ELEMENT_GET_CLASS (element);
- if (oclass->set_scheduler)
- oclass->set_scheduler (element, scheduler);
+ if (oclass->set_manager)
+ oclass->set_manager (element, manager);
}
+
/**
- * gst_element_get_scheduler:
- * @element: a #GstElement to get the scheduler of.
+ * gst_element_get_manager:
+ * @element: a #GstElement to get the manager of.
*
- * Returns the scheduler of the element.
+ * Returns the manager of the element.
*
- * Returns: the element's #GstScheduler.
+ * Returns: the element's #GstPipeline. unref after usage.
*
* MT safe.
*/
-GstScheduler *
-gst_element_get_scheduler (GstElement * element)
+GstPipeline *
+gst_element_get_manager (GstElement * element)
{
- GstScheduler *result = NULL;
+ GstPipeline *result = NULL;
g_return_val_if_fail (GST_IS_ELEMENT (element), result);
GST_LOCK (element);
- result = GST_ELEMENT_SCHEDULER (element);
+ result = GST_ELEMENT_MANAGER (element);
+ gst_object_ref (GST_OBJECT (result));
GST_UNLOCK (element);
return result;
}
/**
- * gst_element_set_loop_function:
- * @element: a #GstElement to set the loop function of.
- * @loop: Pointer to #GstElementLoopFunction.
+ * gst_element_set_bus:
+ * @element: a #GstElement to set the bus of.
+ * @bus: the #GstBus to set.
*
- * This sets the loop function for the element. The function pointed to
- * can deviate from the GstElementLoopFunction definition in type of
- * pointer only.
+ * Sets the bus of the element. For internal use only, unless you're
+ * testing elements.
*
- * 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.
+ * MT safe.
*/
void
-gst_element_set_loop_function (GstElement * element,
- GstElementLoopFunction loop)
+gst_element_set_bus (GstElement * element, GstBus * bus)
{
- gboolean need_notify = FALSE;
+ GstElementClass *oclass;
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);
+ oclass = GST_ELEMENT_GET_CLASS (element);
- 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);
- }
+ if (oclass->set_bus)
+ oclass->set_bus (element, bus);
}
/**
- * 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.
- * <note>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.</note>
+ * 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.
*/
-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)
+GstBus *
+gst_element_get_bus (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;
+ GstBus *result = NULL;
- 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);
+ g_return_val_if_fail (GST_IS_ELEMENT (element), result);
- if (!GST_OBJECT_PARENT (element))
- return;
+ GST_LOCK (element);
+ result = GST_ELEMENT_BUS (element);
+ GST_UNLOCK (element);
- parent = GST_ELEMENT (GST_OBJECT_PARENT (element));
- if (GST_STATE (parent) == GST_STATE_PAUSED)
- gst_element_set_eos_recursive (parent);
+ return result;
}
/**
- * gst_element_set_eos:
- * @element: a #GstElement to set to the EOS state.
+ * gst_element_set_scheduler:
+ * @element: a #GstElement to set the scheduler of.
+ * @scheduler: the #GstScheduler to set.
+ *
+ * Sets the scheduler of the element. For internal use only, unless you're
+ * testing elements.
*
- * Perform the actions needed to bring the element in the EOS state.
+ * MT safe.
*/
void
-gst_element_set_eos (GstElement * element)
+gst_element_set_scheduler (GstElement * element, GstScheduler * scheduler)
{
+ GstElementClass *oclass;
+
g_return_if_fail (GST_IS_ELEMENT (element));
- GST_CAT_DEBUG (GST_CAT_EVENT, "setting EOS on element %s",
- GST_OBJECT_NAME (element));
+ oclass = GST_ELEMENT_GET_CLASS (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);
- }
+ if (oclass->set_scheduler)
+ oclass->set_scheduler (element, scheduler);
}
/**
- * gst_element_get_managing_bin:
- * @element: a #GstElement to get the managing bin of.
+ * gst_element_get_scheduler:
+ * @element: a #GstElement to get the scheduler of.
*
- * Gets the managing bin (a pipeline or a thread, for example) of an element.
+ * Returns the scheduler of the element.
*
- * Returns: the #GstBin, or NULL on failure.
- **/
-GstBin *
-gst_element_get_managing_bin (GstElement * element)
+ * Returns: the element's #GstScheduler.
+ *
+ * MT safe.
+ */
+GstScheduler *
+gst_element_get_scheduler (GstElement * element)
{
- GstBin *bin;
-
- g_return_val_if_fail (element != NULL, NULL);
+ GstScheduler *result = NULL;
- bin = GST_BIN (gst_object_get_parent (GST_OBJECT (element)));
+ g_return_val_if_fail (GST_IS_ELEMENT (element), result);
- while (bin && !GST_FLAG_IS_SET (GST_OBJECT (bin), GST_BIN_FLAG_MANAGER))
- bin = GST_BIN (gst_object_get_parent (GST_OBJECT (bin)));
+ GST_LOCK (element);
+ result = GST_ELEMENT_SCHEDULER (element);
+ GST_UNLOCK (element);
- return bin;
+ return result;
}
#include <gst/gstpluginfeature.h>
#include <gst/gstindex.h>
#include <gst/gstiterator.h>
+#include <gst/gstmessage.h>
#include <gst/gsttag.h>
G_BEGIN_DECLS
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,
/* 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)
* 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)
#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;
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;
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 */
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 */
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);
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
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
#include "gst_private.h"
#include "gstdata_private.h"
-#include "gstclock.h"
#include "gstinfo.h"
#include "gstmemchunk.h"
#include "gstevent.h"
{
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;
}
/**
- * 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 ();
-
- 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);
- }
+ GstEvent *event;
- return event;
-}
+ event = gst_event_new (GST_EVENT_FLUSH);
+ GST_EVENT_FLUSH_DONE (event) = done;
-/**
- * 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);
+ return event;
}
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
GstEventFlag flags;
} GstEventMask;
-#ifndef GST_DISABLE_DEPRECATED
#ifdef G_HAVE_ISO_VARARGS
#define GST_EVENT_MASK_FUNCTION(type,functionname, ...) \
static const GstEventMask* \
return masks; \
}
#endif
-#endif
/* seek events, extends GstEventFlag */
typedef enum {
#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)
gboolean new_media;
} discont;
struct {
+ gboolean done;
+ } flush;
+ struct {
GstFormat format;
gint64 value;
} size;
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
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
+ *
+ * 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 <string.h> /* 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"));
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
+ *
+ * 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 <gst/gsttypes.h>
+#include <gst/gstdata.h>
+#include <gst/gstobject.h>
+#include <gst/gsttag.h>
+#include <gst/gststructure.h>
+
+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__ */
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,
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);
{
REAL_LINKED,
REAL_UNLINKED,
- REAL_FIXATE,
+ REAL_REQUEST_LINK,
/* FILL ME */
REAL_LAST_SIGNAL
};
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;
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.",
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)
{
pad->peer = NULL;
pad->chainfunc = NULL;
- pad->getfunc = NULL;
-
- pad->chainhandler = NULL;
- pad->gethandler = NULL;
pad->ghostpads = NULL;
pad->caps = NULL;
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);
}
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:
* Activates or deactivates the given pad.
*
* Returns: TRUE if the operation was successfull.
+ *
+ * MT safe.
+ */
+gboolean
+gst_pad_set_active (GstPad * pad, GstActivateMode mode)
+{
+ /* implement me */
+ return FALSE;
+}
+
+
+/**
+ * gst_pad_is_active:
+ * @pad: the #GstPad to query
+ *
+ * Query if a pad is active
+ *
+ * Returns: TRUE if the pad is active.
+ *
+ * MT safe.
*/
gboolean
-gst_pad_set_active (GstPad * pad, gboolean active)
+gst_pad_is_active (GstPad * pad)
{
+ gboolean result = FALSE;
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;
+ GST_PAD_REALIZE_AND_LOCK (pad, realpad, lost_ghostpad);
+ result = GST_FLAG_IS_SET (realpad, GST_PAD_ACTIVE);
+ GST_UNLOCK (realpad);
- realpad = GST_PAD_REALIZE (pad);
+ return result;
- 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;
- }
+lost_ghostpad:
+ {
+ return FALSE;
}
-
- g_object_notify (G_OBJECT (realpad), "active");
-
- return TRUE;
}
/**
- * gst_pad_set_active_recursive:
- * @pad: the #GstPad to activate or deactivate.
- * @active: TRUE to activate the pad.
+ * 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.
*
- * Activates or deactivates the given pad and all internally linked
- * pads upstream until it finds an element with multiple source pads.
+ * 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.
*/
-void
-gst_pad_set_active_recursive (GstPad * pad, gboolean active)
+gboolean
+gst_pad_set_blocked_async (GstPad * pad, gboolean blocked,
+ GstPadBlockCallback callback, gpointer user_data)
{
- GstElement *parent;
- const GList *int_links;
+ gboolean was_blocked;
+ GstRealPad *realpad;
- g_return_if_fail (GST_IS_PAD (pad));
- g_return_if_fail (GST_PAD_IS_SRC (pad));
+ g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
- GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, pad,
- "Recursively %s pad %s:%s", active ? "activating" : "deactivating",
- GST_DEBUG_PAD_NAME (pad));
+ GST_PAD_REALIZE_AND_LOCK (pad, realpad, lost_ghostpad);
- gst_pad_set_active (pad, active);
+ was_blocked = GST_RPAD_IS_BLOCKED (realpad);
- /* 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;
+ 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));
- 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_FLAG_UNSET (realpad, GST_PAD_BLOCKED);
- GST_CAT_DEBUG_OBJECT (GST_CAT_PADS, sinkpad,
- "Recursing %s on pad %s:%s",
- active ? "activation" : "deactivation", GST_DEBUG_PAD_NAME (sinkpad));
+ realpad->block_callback = callback;
+ realpad->block_data = user_data;
- gst_pad_set_active (sinkpad, active);
- if (peer)
- gst_pad_set_active_recursive (peer, active);
+ 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_is_active:
- * @pad: the #GstPad to query
+ * gst_pad_set_blocked:
+ * @pad: the #GstPad to block or unblock
+ * @blocked: boolean indicating we should block or unblock
*
- * Query if a pad is active
+ * 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 is active.
+ * 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_is_active (GstPad * pad)
+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), FALSE);
+ 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_ACTIVE);
+ result = GST_FLAG_IS_SET (realpad, GST_PAD_BLOCKED);
GST_UNLOCK (realpad);
return result;
}
/**
+ * 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.
* @chain: the #GstPadChainFunction to set.
}
/**
- * 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));
}
* @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.
- *
- * 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.
+ * 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_REFUSED should be used when the caps are
- * unacceptable for whatever reason.
+ * The return value GST_PAD_LINK_OK should be used when the connection can be
+ * made.
*
- * 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)
}
/**
- * 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.
* @getcaps: the #GstPadGetCapsFunction to set.
* @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)
}
/**
+ * 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.
* @bufalloc: the #GstPadBufferAllocFunction to set.
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.
*
* 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));
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 */
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));
+
+ return TRUE;
- gst_object_unref (GST_OBJECT (realsrc));
- gst_object_unref (GST_OBJECT (realsink));
+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;
+ }
}
/**
}
}
-static gboolean
-gst_pad_check_schedulers (GstRealPad * realsrc, GstRealPad * realsink)
+/* FIXME leftover from an attempt at refactoring... */
+static GstPadLinkReturn
+gst_pad_link_prepare_filtered (GstPad * srcpad, GstPad * sinkpad,
+ GstRealPad ** outrealsrc, GstRealPad ** outrealsink,
+ const GstCaps * filtercaps)
{
- 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))
+ GstRealPad *realsrc, *realsink;
-static GstPadLink *
-gst_pad_link_new (void)
-{
- GstPadLink *link;
+ /* 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);
- link = g_new0 (GstPadLink, 1);
- link->sinknotify = TRUE;
- link->srcnotify = TRUE;
+ GST_CAT_INFO (GST_CAT_PADS, "trying to link %s:%s and %s:%s",
+ GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad));
- link->engaged = FALSE;
- return link;
-}
+ /* now we need to deal with the real/ghost stuff */
+ GST_PAD_REALIZE_AND_LOCK (srcpad, realsrc, lost_src_ghostpad);
-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);
-}
+ if (G_UNLIKELY (GST_RPAD_DIRECTION (realsrc) != GST_PAD_SRC))
+ goto not_srcpad;
-static void
-gst_pad_link_intersect (GstPadLink * link)
-{
- GstCaps *pad_intersection;
+ if (G_UNLIKELY (GST_RPAD_PEER (realsrc) != NULL))
+ goto src_was_linked;
- if (link->caps)
- gst_caps_unref (link->caps);
+ GST_PAD_REALIZE_AND_LOCK (sinkpad, realsink, lost_sink_ghostpad);
- 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);
+ if (G_UNLIKELY (GST_RPAD_DIRECTION (realsink) != GST_PAD_SINK))
+ goto not_sinkpad;
- pad_intersection = gst_caps_intersect (link->srccaps, link->sinkcaps);
+ if (G_UNLIKELY (GST_RPAD_PEER (realsink) != NULL))
+ goto sink_was_linked;
- 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;
+ 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;
- 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;
- }
+ /* check pad caps for non-empty intersection */
+ {
+ GstCaps *srccaps;
+ GstCaps *sinkcaps;
- return TRUE;
-}
+ 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);
-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;
+ if (srccaps && sinkcaps) {
+ GstCaps *caps;
- gst_caps_do_simplify (newcaps);
-#ifndef G_DISABLE_CHECKS
- /* some mad checking for correctly working fixation functions */
+ caps = gst_caps_intersect (srccaps, sinkcaps);
+ GST_CAT_DEBUG (GST_CAT_CAPS,
+ "intersection caps %p %" GST_PTR_FORMAT, caps, caps);
- 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);
+ if (filtercaps) {
+ GstCaps *tmp;
- 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;
- }
+ tmp = gst_caps_intersect (caps, filtercaps);
+ gst_caps_unref (caps);
+ caps = tmp;
}
+ if (!caps || gst_caps_is_empty (caps))
+ goto no_format;
}
}
- link->caps = caps;
-}
+ /* FIXME check pad scheduling for non-empty intersection */
-static GstPadLinkReturn
-gst_pad_link_call_link_functions (GstPadLink * link)
-{
- GstPadLinkReturn res = GST_PAD_LINK_OK;
-
- /* 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;
- }
+ /* update filter */
+ if (filtercaps) {
+ GstCaps *filtercopy;
- /* 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);
+ filtercopy = gst_caps_copy (filtercaps);
+ filtercopy = gst_caps_ref (filtercopy);
- /* 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);
-
- GST_DEBUG_OBJECT (link->srcpad, "got reply %d from link function", res);
-
- 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);
- }
- }
-
- 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);
-
- GST_DEBUG_OBJECT (link->sinkpad, "got reply %d from link function", res);
-
- 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);
- }
+ 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);
}
+ return GST_PAD_LINK_OK;
- 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))
+lost_src_ghostpad:
+ {
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);
+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->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;
+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;
}
-
- 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);
+lost_sink_ghostpad:
+ {
+ GST_DEBUG ("lost sink ghostpad");
+ GST_UNLOCK (realsrc);
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;
- } else {
- link->srccaps = gst_pad_get_caps (link->srcpad);
- link->sinkcaps = gst_caps_copy (caps);
- link->sinknotify = FALSE;
- }
-
- 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;
- }
-
- /* 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);
- }
-
- 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;
+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;
}
-
- oldlink = GST_REAL_PAD (pad)->link;
- if (oldlink && oldlink->filtercaps) {
- link->filtercaps = gst_caps_copy (oldlink->filtercaps);
+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;
}
- 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;
+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;
}
/**
* 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;
-
- /* 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);
-
- 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 */
- realsrc = GST_PAD_REALIZE (srcpad);
- realsink = GST_PAD_REALIZE (sinkpad);
-
- 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));
- }
- /* 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;
+ GstPadLinkReturn result;
+
+ result = gst_pad_link_prepare_filtered (srcpad, sinkpad, &realsrc, &realsink,
+ filtercaps);
+
+ if (result != GST_PAD_LINK_OK)
+ goto prepare_failed;
+
+ GST_UNLOCK (realsink);
+ GST_UNLOCK (realsrc);
+
+ /* 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;
}
- if (!gst_pad_check_schedulers (realsrc, realsink)) {
- g_warning ("linking pads with different scheds requires "
- "exactly one decoupled element (such as queue)");
- return FALSE;
- }
+ 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);
- g_return_val_if_fail (realsrc != NULL, GST_PAD_LINK_REFUSED);
- g_return_val_if_fail (realsink != NULL, GST_PAD_LINK_REFUSED);
+ GST_UNLOCK (realsink);
+ GST_UNLOCK (realsrc);
- link = gst_pad_link_new ();
+ /* 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);
- if (GST_RPAD_DIRECTION (realsrc) == GST_PAD_SRC) {
- link->srcpad = GST_PAD (realsrc);
- link->sinkpad = GST_PAD (realsink);
+ GST_CAT_INFO (GST_CAT_PADS, "linked %s:%s and %s:%s, successful",
+ GST_DEBUG_PAD_NAME (realsrc), GST_DEBUG_PAD_NAME (realsink));
} else {
- link->srcpad = GST_PAD (realsink);
- link->sinkpad = GST_PAD (realsrc);
- }
-
- if (GST_RPAD_DIRECTION (link->srcpad) != GST_PAD_SRC) {
- GST_CAT_INFO (GST_CAT_PADS,
- "Real src pad %s:%s is not a source pad, failed",
- GST_DEBUG_PAD_NAME (link->srcpad));
- gst_pad_link_free (link);
- return FALSE;
- }
- if (GST_RPAD_DIRECTION (link->sinkpad) != GST_PAD_SINK) {
- GST_CAT_INFO (GST_CAT_PADS, "Real sink pad %s:%s is not a sink pad, failed",
- GST_DEBUG_PAD_NAME (link->sinkpad));
- gst_pad_link_free (link);
- return FALSE;
- }
+ GST_CAT_INFO (GST_CAT_PADS, "link between %s:%s and %s:%s failed",
+ GST_DEBUG_PAD_NAME (realsrc), GST_DEBUG_PAD_NAME (realsink));
- 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;
+ /* remove the filter again */
+ if (filtercaps) {
+ gst_caps_replace (&GST_RPAD_APPFILTER (realsrc), NULL);
+ gst_caps_replace (&GST_RPAD_APPFILTER (realsink), NULL);
+ }
- /* 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_UNLOCK (realsink);
+ GST_UNLOCK (realsrc);
}
+ return result;
- 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;
+prepare_failed:
+ {
+ return result;
+ }
}
/**
*
* 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);
/**
- * 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.
*
GST_GPAD_REALPAD (ghostpad) = NULL;
}
-static gboolean
-_gst_pad_default_fixate_value (const GValue * value, GValue * dest)
+/**
+ * gst_pad_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.
+ *
+ * Relinks the given source and sink pad, constrained by the given
+ * capabilities. If the relink fails, the pads are unlinked
+ * and an error code is returned.
+ *
+ * Returns: The result code of the operation.
+ *
+ * MT safe
+ */
+GstPadLinkReturn
+gst_pad_relink_filtered (GstPad * srcpad, GstPad * sinkpad,
+ const GstCaps * filtercaps)
{
- GType type = G_VALUE_TYPE (value);
-
- if (gst_value_is_fixed (value))
- return TRUE;
+ GstRealPad *realsrc, *realsink;
- 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));
- }
+ /* FIXME refactor and share code with link/unlink */
- return FALSE;
-}
+ /* 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);
-static gboolean
-_gst_pad_default_fixate_foreach (GQuark field_id, const GValue * value,
- gpointer s)
-{
- GstStructure *structure = (GstStructure *) s;
- GValue dest = { 0 };
+ GST_CAT_INFO (GST_CAT_PADS, "trying to relink %s:%s and %s:%s",
+ GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad));
- if (_gst_pad_default_fixate_value (value, &dest))
- return TRUE;
- gst_structure_id_set_value (structure, field_id, &dest);
- g_value_unset (&dest);
+ /* now we need to deal with the real/ghost stuff */
+ GST_PAD_REALIZE_AND_LOCK (srcpad, realsrc, lost_src_ghostpad);
- return FALSE;
-}
+ if (G_UNLIKELY (GST_RPAD_DIRECTION (realsrc) != GST_PAD_SRC))
+ goto not_srcpad;
-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;
+ GST_PAD_REALIZE_AND_LOCK (sinkpad, realsink, lost_sink_ghostpad);
- 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 (G_UNLIKELY (GST_RPAD_DIRECTION (realsink) != GST_PAD_SINK))
+ goto not_sinkpad;
- if (gst_caps_is_any (caps)) {
- return gst_caps_copy (gst_static_caps_get (&octetcaps));
- }
+ if (G_UNLIKELY (GST_RPAD_PEER (realsink) != realsrc))
+ goto not_linked_together;
- if (caps->structs->len > 1) {
- return gst_caps_new_full (gst_structure_copy (gst_caps_get_structure (caps,
- 0)), NULL);
+ 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));
}
- newcaps = gst_caps_copy (caps);
- structure = gst_caps_get_structure (newcaps, 0);
- gst_structure_foreach (structure, _gst_pad_default_fixate_foreach, structure);
+ /* update filter */
+ if (filtercaps) {
+ GstCaps *filtercopy;
- return newcaps;
-}
+ filtercopy = gst_caps_copy (filtercaps);
+ filtercopy = gst_caps_ref (filtercopy);
-/**
- * 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_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_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;
+ GST_CAT_INFO (GST_CAT_PADS, "relinked %s:%s and %s:%s, successful",
+ GST_DEBUG_PAD_NAME (realsrc), GST_DEBUG_PAD_NAME (realsink));
- g_return_if_fail (GST_IS_PAD (pad));
+ return GST_PAD_LINK_OK;
- link = GST_RPAD_LINK (GST_PAD_REALIZE (pad));
- if (link)
- gst_pad_link_unnegotiate (link);
+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;
+ }
}
-/* returning NULL indicates that the arguments are invalid */
-static GstPadLink *
-gst_pad_link_prepare (GstPad * srcpad, GstPad * sinkpad,
- const GstCaps * filtercaps)
+/* should be called with the pad LOCK held */
+static GstCaps *
+gst_real_pad_get_caps_unlocked (GstRealPad * realpad)
{
- GstRealPad *realsrc, *realsink;
- GstPadLink *link;
+ GstCaps *result = NULL, *filter;
- 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);
+ GST_CAT_DEBUG (GST_CAT_CAPS, "get pad caps of %s:%s (%p)",
+ GST_DEBUG_PAD_NAME (realpad), realpad);
- 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));
- }
+ if (GST_RPAD_GETCAPSFUNC (realpad)) {
+ GST_CAT_DEBUG (GST_CAT_CAPS, "dispatching to pad getcaps function");
- 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);
+ 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 (!gst_pad_check_schedulers (realsrc, realsink)) {
- g_warning ("linking pads with different scheds requires "
- "exactly one decoupled element (such as queue)");
- return NULL;
- }
+ 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;
- 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;
+ 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);
- link = gst_pad_link_new ();
+ 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 (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);
+ result = gst_caps_ref (result);
+ goto done;
}
+ if (GST_RPAD_CAPS (realpad)) {
+ result = GST_RPAD_CAPS (realpad);
- link->srccaps = gst_pad_get_caps (link->srcpad);
- link->sinkcaps = gst_pad_get_caps (link->sinkpad);
- if (filtercaps)
- link->filtercaps = gst_caps_copy (filtercaps);
+ GST_CAT_DEBUG (GST_CAT_CAPS,
+ "using pad caps %p %" GST_PTR_FORMAT, result, result);
- return link;
-}
+ result = gst_caps_ref (result);
+ goto done;
+ }
-/**
- * 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_CAT_DEBUG (GST_CAT_CAPS, "pad has no caps");
+ result = gst_caps_new_empty ();
- GST_INFO ("trying to relink %" GST_PTR_FORMAT " and %" GST_PTR_FORMAT
- " with filtercaps %" GST_PTR_FORMAT, srcpad, sinkpad);
+done:
+ filter = GST_RPAD_APPFILTER (realpad);
- link = gst_pad_link_prepare (srcpad, sinkpad, filtercaps);
- if (!link)
- return FALSE;
+ if (filter) {
+ GstCaps *temp = result;
- 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;
+ 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);
}
-
- if (GST_PAD_LINK_FAILED (gst_pad_link_try (link)))
- return FALSE;
-
- return TRUE;
+ return result;
}
/**
- * gst_pad_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.
- *
- * Relinks the given source and sink pad, constrained by the given
- * capabilities. If the relink fails, the pads are unlinked
- * and FALSE is returned.
+ * gst_pad_get_caps:
+ * @pad: a #GstPad to get the capabilities of.
*
- * Returns: TRUE if the pads were succesfully relinked, FALSE otherwise.
- */
-gboolean
-gst_pad_relink_filtered (GstPad * srcpad, GstPad * sinkpad,
- const GstCaps * filtercaps)
-{
- if (gst_pad_try_relink_filtered (srcpad, sinkpad, filtercaps))
- return TRUE;
-
- gst_pad_unlink (srcpad, sinkpad);
- return FALSE;
-}
-
-/**
- * gst_pad_proxy_fixate:
- * @pad: a #GstPad to proxy.
- * @caps: the #GstCaps to fixate
+ * Gets the capabilities of this pad.
*
- * 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: the #GstCaps of this pad. This function returns a new caps, so use
+ * gst_caps_unref to get rid of it.
*
- * Returns: a fixated caps, or NULL if caps cannot be fixed
+ * MT safe.
*/
GstCaps *
-gst_pad_proxy_fixate (GstPad * pad, const GstCaps * caps)
+gst_pad_get_caps (GstPad * pad)
{
- GstElement *element;
- const GList *pads;
- const GstCaps *othercaps;
+ GstRealPad *realpad;
+ GstCaps *result = NULL;
g_return_val_if_fail (GST_IS_PAD (pad), NULL);
- g_return_val_if_fail (caps != NULL, NULL);
-
- GST_DEBUG ("proxying fixate for %s:%s\n", GST_DEBUG_PAD_NAME (pad));
-
- element = gst_pad_get_parent (pad);
- /* FIXME, not MT safe but this function will be removed */
- pads = element->pads;
+ /* now we need to deal with the real/ghost stuff */
+ GST_PAD_REALIZE_AND_LOCK (pad, realpad, lost_ghostpad);
- while (pads) {
- GstPad *otherpad = GST_PAD (pads->data);
+ GST_CAT_DEBUG (GST_CAT_CAPS, "get pad caps of %s:%s (%p)",
+ GST_DEBUG_PAD_NAME (realpad), realpad);
- /* FIXME check that each pad has the same pad template caps */
+ if (G_UNLIKELY (GST_RPAD_IS_IN_GETCAPS (realpad)))
+ goto was_dispatching;
- if (otherpad != pad) {
- othercaps = gst_pad_get_negotiated_caps (otherpad);
+ result = gst_real_pad_get_caps_unlocked (realpad);
+ GST_UNLOCK (realpad);
- if (othercaps && !gst_caps_is_subset (caps, othercaps)) {
- GstCaps *icaps;
+ return result;
- icaps = gst_caps_intersect (othercaps, caps);
- if (!gst_caps_is_empty (icaps)) {
- return icaps;
- } else {
- gst_caps_unref (icaps);
- }
- }
- }
- pads = g_list_next (pads);
+lost_ghostpad:
+ {
+ return NULL;
+ }
+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;
}
-
- return NULL;
}
/**
- * gst_pad_set_explicit_caps:
- * @pad: a #GstPad to set the explicit caps of
- * @caps: the #GstCaps to set
+ * gst_pad_peer_get_caps:
+ * @pad: a #GstPad to get the peer capabilities of.
*
- * 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.
+ * Gets the capabilities of the peer connected to this pad.
*
- * 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
+ * Returns: the #GstCaps of the peer pad. This function returns a new caps, so use
+ * gst_caps_unref to get rid of it.
*/
-gboolean
-gst_pad_set_explicit_caps (GstPad * pad, const GstCaps * caps)
+GstCaps *
+gst_pad_peer_get_caps (GstPad * pad)
{
- GstPadLinkReturn link_ret;
-
- g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
- g_return_val_if_fail (caps == NULL || gst_caps_is_fixed (caps), FALSE);
+ GstRealPad *realpad, *peerpad;
+ GstCaps *result = NULL;
- GST_CAT_DEBUG (GST_CAT_PADS,
- "setting explicit caps on %s:%s to %" GST_PTR_FORMAT,
- GST_DEBUG_PAD_NAME (pad), caps);
+ g_return_val_if_fail (GST_IS_PAD (pad), NULL);
- if (caps == NULL) {
- GST_CAT_DEBUG (GST_CAT_PADS, "caps is NULL");
- gst_caps_replace (&GST_RPAD_EXPLICIT_CAPS (pad), NULL);
- return TRUE;
- }
+ /* now we need to deal with the real/ghost stuff */
+ GST_PAD_REALIZE_AND_LOCK (pad, realpad, lost_ghostpad);
- gst_caps_replace (&GST_RPAD_EXPLICIT_CAPS (pad), gst_caps_copy (caps));
+ GST_CAT_DEBUG (GST_CAT_CAPS, "get peer caps of %s:%s (%p)",
+ GST_DEBUG_PAD_NAME (realpad), realpad);
- 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;
- }
+ peerpad = GST_RPAD_PEER (realpad);
+ if (G_UNLIKELY (peerpad == NULL))
+ goto no_peer;
- return TRUE;
-}
+ if (G_UNLIKELY (GST_RPAD_IS_IN_GETCAPS (peerpad)))
+ goto was_dispatching;
-static GstCaps *
-gst_pad_explicit_getcaps (GstPad * pad)
-{
- g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+ gst_object_ref (GST_OBJECT (peerpad));
+ GST_UNLOCK (realpad);
- if (GST_RPAD_EXPLICIT_CAPS (pad) == NULL) {
- const GstCaps *caps = gst_pad_get_pad_template_caps (pad);
+ result = gst_pad_get_caps (GST_PAD_CAST (peerpad));
- return gst_caps_copy (caps);
- }
- return gst_caps_copy (GST_RPAD_EXPLICIT_CAPS (pad));
-}
+ gst_object_unref (GST_OBJECT (peerpad));
-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);
+ return result;
- if (GST_RPAD_EXPLICIT_CAPS (pad) == NULL) {
- return GST_PAD_LINK_DELAYED;
+lost_ghostpad:
+ {
+ return NULL;
+ }
+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;
}
-
- return GST_PAD_LINK_OK;
-}
-
-/**
- * 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)
-{
- g_return_if_fail (GST_IS_PAD (pad));
-
- 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_pad_proxy_link:
- * @pad: a #GstPad to proxy to.
- * @caps: the #GstCaps to use in proxying.
+ * gst_pad_fixate_caps:
+ * @pad: a #GstPad to fixate
*
- * Proxies the link function to the specified pad.
+ * Fixate a caps on the given pad.
*
- * Returns: TRUE if the peer pad accepted the caps, FALSE otherwise.
+ * Returns: a fixated #GstCaps.
*/
-GstPadLinkReturn
-gst_pad_proxy_link (GstPad * pad, const GstCaps * caps)
+GstCaps *
+gst_pad_fixate_caps (GstPad * pad, GstCaps * caps)
{
- return gst_pad_try_set_caps (pad, caps);
+ /* FIXME, implement me, call the fixate function for the pad */
+ return caps;
}
/**
- * gst_pad_is_negotiated:
- * @pad: a #GstPad to get the negotiation status of
+ * gst_pad_accept_caps:
+ * @pad: a #GstPad to check
+ *
+ * Check if the given pad accepts the caps.
*
- * Returns: TRUE if the pad has successfully negotiated caps.
+ * Returns: TRUE if the pad can accept the caps.
*/
gboolean
-gst_pad_is_negotiated (GstPad * pad)
+gst_pad_accept_caps (GstPad * pad, GstCaps * caps)
{
+ GstRealPad *realpad;
+ gboolean result;
+
g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
- if (!(pad = (GstPad *) GST_PAD_REALIZE (pad)))
- return FALSE;
- if (!GST_RPAD_LINK (pad))
- return FALSE;
+ /* now we need to deal with the real/ghost stuff */
+ GST_PAD_REALIZE_AND_LOCK (pad, realpad, lost_ghostpad);
- return (GST_RPAD_LINK (pad)->caps != NULL);
-}
+ GST_CAT_DEBUG (GST_CAT_CAPS, "pad accept caps of %s:%s (%p)",
+ GST_DEBUG_PAD_NAME (realpad), 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);
+ /* FIXME, call accept function */
+ result = FALSE;
+ GST_UNLOCK (realpad);
- if (!(pad = (GstPad *) GST_PAD_REALIZE (pad)))
- return NULL;
- if (!GST_RPAD_LINK (pad))
- return NULL;
+ return result;
- return GST_RPAD_LINK (pad)->caps;
+lost_ghostpad:
+ {
+ return FALSE;
+ }
}
/**
- * gst_pad_get_caps:
- * @pad: a #GstPad to get the capabilities of.
+ * gst_pad_peer_accept_caps:
+ * @pad: a #GstPad to check
*
- * Gets the capabilities of this pad.
+ * Check if the given pad accepts the caps.
*
- * Returns: the #GstCaps of this pad. This function returns a new caps, so use
- * gst_caps_unref to get rid of it.
+ * Returns: TRUE if the pad can accept the caps.
*/
-GstCaps *
-gst_pad_get_caps (GstPad * pad)
+gboolean
+gst_pad_peer_accept_caps (GstPad * pad, GstCaps * caps)
{
- GstRealPad *realpad;
+ GstRealPad *realpad, *peerpad;
+ gboolean result;
- g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+ g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
- 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_CAT_DEBUG (GST_CAT_CAPS, "peer accept caps of %s:%s (%p)",
GST_DEBUG_PAD_NAME (realpad), realpad);
- if (GST_PAD_IS_DISPATCHING (realpad))
- GST_CAT_DEBUG (GST_CAT_CAPS,
- "pad %s:%s is already dispatching -- looking for a template",
- 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");
+ peerpad = GST_RPAD_PEER (realpad);
+ if (G_UNLIKELY (peerpad == NULL))
+ goto no_peer;
- GST_FLAG_SET (realpad, GST_PAD_DISPATCHING);
- caps = GST_RPAD_GETCAPSFUNC (realpad) (GST_PAD (realpad));
- GST_FLAG_UNSET (realpad, GST_PAD_DISPATCHING);
+ result = gst_pad_accept_caps (GST_PAD_CAST (peerpad), caps);
+ GST_UNLOCK (realpad);
- 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;
+ return result;
- 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;
- }
+lost_ghostpad:
+ {
+ return FALSE;
}
- 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));
+no_peer:
+ {
+ GST_UNLOCK (realpad);
+ return TRUE;
}
- 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 ();
}
/**
- * gst_pad_peer_get_caps:
- * @pad: a #GstPad to get the peer capabilities of.
+ * gst_pad_set_caps:
+ * @pad: a #GstPad to set the capabilities of.
+ * @caps: a #GstCaps to set.
*
- * Gets the capabilities of the peer connected to this pad.
+ * 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: the #GstCaps of the peer pad. This function returns a new caps, so use
- * gst_caps_unref to get rid of it.
+ * 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.
*/
-GstCaps *
-gst_pad_peer_get_caps (GstPad * pad)
+gboolean
+gst_pad_set_caps (GstPad * pad, GstCaps * caps)
{
- GstRealPad *realpad, *peerpad;
- GstCaps *result = NULL;
+ GstPadSetCapsFunction setcaps;
- g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+ g_return_val_if_fail (GST_IS_REAL_PAD (pad), FALSE);
- /* now we need to deal with the real/ghost stuff */
- GST_PAD_REALIZE_AND_LOCK (pad, realpad, lost_ghostpad);
+ 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));
+ }
+ }
- GST_CAT_DEBUG (GST_CAT_CAPS, "get peer caps of %s:%s (%p)",
- GST_DEBUG_PAD_NAME (realpad), realpad);
+ if (GST_PAD_CAPS (pad))
+ gst_caps_unref (GST_PAD_CAPS (pad));
- peerpad = GST_RPAD_PEER (realpad);
- if (G_UNLIKELY (peerpad == NULL))
- goto no_peer;
+ if (caps)
+ caps = gst_caps_ref (caps);
- gst_object_ref (GST_OBJECT (peerpad));
- GST_UNLOCK (realpad);
+ 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);
- result = gst_pad_get_caps (GST_PAD_CAST (peerpad));
+ g_object_notify (G_OBJECT (pad), "caps");
- gst_object_unref (GST_OBJECT (peerpad));
+ return TRUE;
- return result;
+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;
+ }
+}
-lost_ghostpad:
+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:
{
- return NULL;
+ GST_CAT_DEBUG (GST_CAT_CAPS, "caps %" GST_PTR_FORMAT " not accepted", caps);
+ return FALSE;
}
-no_peer:
+}
+
+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_UNLOCK (realpad);
- return gst_caps_new_any ();
+ GST_CAT_DEBUG (GST_CAT_CAPS, "caps %" GST_PTR_FORMAT " not accepted", caps);
+ return FALSE;
}
}
}
/**
- * gst_pad_caps_change_notify:
- * @pad: a #GstPad
+ * gst_pad_get_negotiated_caps:
+ * @pad: a #GstPad.
+ *
+ * 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.
*
- * Called to indicate that the return value of @pad's getcaps function may have
- * changed, and that a renegotiation is suggested.
+ * 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
- *
- * 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.
- *
- * Returns: TRUE when the issue was resolved, dumps detailed information
- * on the console and returns FALSE otherwise.
+ * gst_pad_get_filter_caps:
+ * @pad: a real #GstPad.
+ *
+ * Gets the capabilities of filter that currently configured on @pad
+ * and its peer.
+ *
+ * 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;
+ }
}
/**
* @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_REAL_PAD (pad), NULL);
+ g_return_val_if_fail (GST_PAD_IS_SRC (pad), NULL);
+
+ GST_LOCK (pad);
+ if (G_UNLIKELY ((peer = GST_RPAD_PEER (pad)) == NULL))
+ goto no_peer;
+
+ 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));
- g_return_val_if_fail (GST_IS_PAD (pad), NULL);
- g_return_val_if_fail (GST_PAD_IS_SRC (pad), NULL);
+ result = bufferallocfunc (GST_PAD_CAST (peer), offset, size, caps);
+
+ gst_object_unref (GST_OBJECT_CAST (peer));
- peer = GST_RPAD_PEER (pad);
+ if (G_UNLIKELY (result == NULL)) {
+ goto fallback;
+ }
- if (peer && peer->bufferallocfunc) {
- GstBuffer *ret;
+ /* 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;
+ }
- GST_CAT_DEBUG (GST_CAT_BUFFER, "(%s:%s): getting buffer",
- GST_DEBUG_PAD_NAME (pad));
+ return result;
+
+no_peer:
+ {
+ /* pad has no peer */
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)));
+ "%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);
- ret = (peer->bufferallocfunc) (GST_PAD (peer), offset, size);
- if (ret)
- return ret;
+ 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
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)))));
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);
}
}
#endif /* GST_DISABLE_LOADSAVE */
-static GstData *
-_invent_event (GstPad * pad, GstBuffer * buffer)
-{
- GstEvent *event;
- GstEventType event_type;
- guint64 offset;
-
- if (GST_BUFFER_OFFSET_IS_VALID (buffer))
- event_type = GST_FORMAT_DEFAULT;
- else
- event_type = GST_FORMAT_UNDEFINED;
-
- offset = GST_BUFFER_OFFSET (buffer);
-
- 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));
- } 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));
- }
-
- return GST_DATA (event);
-}
-
-/**
- * gst_pad_push:
- * @pad: a source #GstPad.
- * @data: the #GstData to push.
+/*
+ * should be called with pad lock held
*
- * Pushes a buffer or an event to the peer of @pad. @pad must be linked. May
- * only be called by @pad's parent.
+ * MT safe.
*/
-void
-gst_pad_push (GstPad * pad, GstData * data)
+static void
+handle_pad_block (GstRealPad * pad)
{
- GstRealPad *peer;
-
- 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);
+ GstPadBlockCallback callback;
+ gpointer user_data;
- DEBUG_DATA (pad, data, "gst_pad_push");
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+ "signal block taken on pad %s:%s", GST_DEBUG_PAD_NAME (pad));
- if (!gst_probe_dispatcher_dispatch (&(GST_REAL_PAD (pad)->probedisp), &data)) {
- GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
- "not pushing data %p, blocked by probe", data);
- gst_data_unref (data);
- return;
- }
+ /* need to grab extra ref for the callbacks */
+ gst_object_ref (GST_OBJECT (pad));
- if (!GST_PAD_IS_LINKED (pad)) {
- GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
- "not pushing data %p as pad is unconnected", data);
- gst_data_unref (data);
- return;
+ 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 {
+ GST_PAD_BLOCK_SIGNAL (pad);
}
- 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;
- }
+ while (GST_RPAD_IS_BLOCKED (pad))
+ GST_PAD_BLOCK_WAIT (pad);
- GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "pushing");
- peer = GST_RPAD_PEER (pad);
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad, "got unblocked");
- if (!peer) {
- g_warning ("push on pad %s:%s but it is unlinked",
- GST_DEBUG_PAD_NAME (pad));
+ 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 {
- 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));
- }
+ GST_PAD_BLOCK_SIGNAL (pad);
}
- /* clean up the mess here */
- if (data != NULL)
- gst_data_unref (data);
+
+ gst_object_unref (GST_OBJECT (pad));
}
/**
- * gst_pad_pull:
- * @pad: a sink #GstPad.
+ * gst_pad_push:
+ * @pad: a source #GstPad.
+ * @buffer: the #GstBuffer to push.
*
- * Pulls an event or a buffer from the peer pad. May only be called by @pad's
- * parent.
+ * Pushes a buffer to the peer of @pad. @pad must be linked.
*
- * Returns: a new #GstData from the peer pad.
+ * Returns: a #GstFlowReturn from the peer pad.
+ *
+ * MT safe.
*/
-GstData *
-gst_pad_pull (GstPad * pad)
+GstFlowReturn
+gst_pad_push (GstPad * pad, GstBuffer * buffer)
{
GstRealPad *peer;
- GstData *data;
+ GstFlowReturn ret;
+ GstPadChainFunction chainfunc;
+ GstCaps *caps;
+ gboolean caps_changed;
- 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)));
+ 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);
- peer = GST_RPAD_PEER (pad);
- if (!peer) {
- 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);
- }
+ GST_LOCK (pad);
+ while (G_UNLIKELY (GST_RPAD_IS_BLOCKED (pad)))
+ handle_pad_block (GST_REAL_PAD_CAST (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;
- }
+ if (G_UNLIKELY ((peer = GST_RPAD_PEER (pad)) == NULL))
+ goto not_linked;
- /* 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;
-}
+ if (G_UNLIKELY (!GST_RPAD_IS_ACTIVE (peer)))
+ goto not_active;
-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;
+ if (G_UNLIKELY (GST_RPAD_IS_FLUSHING (peer)))
+ goto flushing;
- return klass->pad_select (scheduler, selected ? selected : &select,
- padlist);
- }
-}
+ gst_object_ref (GST_OBJECT_CAST (peer));
+ GST_UNLOCK (pad);
-/**
- * gst_pad_collectv:
- * @selected: set to the pad the buffer comes from if not NULL
- * @padlist: a #GList of sink pads.
- *
- * 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.
- *
- * Returns: the #GstData that was available
- */
-GstData *
-gst_pad_collectv (GstPad ** selected, const GList * padlist)
-{
- /* need to use alloca here because we must not leak data */
- GstPad **pads;
- GstPad *test;
- GstElement *element = NULL;
- int i = 0;
-
- 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;
+ /* 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;
}
- 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);
+ /* 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;
- result = gst_pad_collect_valist (selected, pad, var_args);
+ 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));
- va_end (var_args);
-
- return result;
-}
+ ret = chainfunc (GST_PAD_CAST (peer), buffer);
-/**
- * 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;
+ gst_object_unref (GST_OBJECT_CAST (peer));
- g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+ return ret;
- 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 *);
+ /* ERROR recovery here */
+not_linked:
+ {
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+ "pushing, but it was not linked");
+ GST_UNLOCK (pad);
+ return GST_FLOW_NOT_CONNECTED;
+ }
+not_active:
+ {
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+ "pushing, but it was inactive");
+ GST_UNLOCK (pad);
+ return GST_FLOW_WRONG_STATE;
+ }
+flushing:
+ {
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+ "pushing, but pad was flushing");
+ GST_UNLOCK (pad);
+ return GST_FLOW_UNEXPECTED;
+ }
+not_negotiated:
+ {
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+ "pushing buffer but peer did not accept");
+ return GST_FLOW_NOT_NEGOTIATED;
+ }
+no_function:
+ {
+ GST_CAT_LOG_OBJECT (GST_CAT_SCHEDULING, pad,
+ "pushing, but not chainhandler");
+ GST_ELEMENT_ERROR (GST_PAD_PARENT (pad), CORE, PAD, (NULL),
+ ("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;
}
- 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.
+ * 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
*
- * 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.
+ * Pulls a buffer from the peer pad. @pad must be linked.
*
- * Waits for a buffer on the given set of pads.
+ * Returns: a #GstFlowReturn from the peer pad.
*
- * Returns: the #GstPad that has a buffer available.
- * Use #gst_pad_pull() to get the buffer.
+ * MT safe.
*/
-GstPad *
-gst_pad_select_valist (GstPad * pad, va_list var_args)
+GstFlowReturn
+gst_pad_pull_range (GstPad * pad, guint64 offset, guint size,
+ GstBuffer ** buffer)
{
- GstPad *result;
- GList *padlist = NULL;
+ GstRealPad *peer;
+ GstFlowReturn ret;
+ GstPadGetRangeFunction getrangefunc;
- if (pad == NULL)
- return 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_SINK,
+ GST_FLOW_ERROR);
+ g_return_val_if_fail (buffer != NULL, GST_FLOW_ERROR);
- while (pad) {
- padlist = g_list_prepend (padlist, pad);
- pad = va_arg (var_args, GstPad *);
- }
- result = gst_pad_selectv (padlist);
- g_list_free (padlist);
+ GST_LOCK (pad);
- return result;
-}
+ while (G_UNLIKELY (GST_RPAD_IS_BLOCKED (pad)))
+ handle_pad_block (GST_REAL_PAD_CAST (pad));
-/**
- * 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 (G_UNLIKELY ((peer = GST_RPAD_PEER (pad)) == NULL))
+ goto not_connected;
- if (pad == NULL)
- return NULL;
+ gst_object_ref (GST_OBJECT_CAST (peer));
+ GST_UNLOCK (pad);
- va_start (var_args, pad);
+ /* see note in above function */
+ if (G_UNLIKELY ((getrangefunc = peer->getrangefunc) == NULL))
+ goto no_function;
- result = gst_pad_select_valist (pad, var_args);
+ 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));
- va_end (var_args);
+ ret = getrangefunc (GST_PAD_CAST (peer), offset, size, buffer);
- return result;
+ 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;
+ }
+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;
+ }
}
/************************************************************************
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) {
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;
}
/**
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;
-
- 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");
- }
- }
- }
+ GstRealPad *rpad = GST_PAD_REALIZE (pad);
+
+ 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;
}
/**
}
/**
+ * 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
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;
-}
#include <gst/gstevent.h>
#include <gst/gstprobe.h>
#include <gst/gstquery.h>
+#include <gst/gsttask.h>
G_BEGIN_DECLS
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)
/* 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 */
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,
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;
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;
GstProbeDispatcher probedisp;
- GstPadLink *link;
- GstCaps *explicit_caps;
-
/*< private >*/
gpointer _gst_reserved[GST_PADDING];
};
/* 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];
/* 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)
#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)))
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*
/* 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);
/* 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,
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
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);
{
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));
"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
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
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 *
#include <gst/gsttypes.h>
#include <gst/gstbin.h>
+#include <gst/gstbus.h>
G_BEGIN_DECLS
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);
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,
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
* 2000 Wim Taymans <wtay@chello.be>
* 2003 Colin Walters <cwalters@gnome.org>
+ * 2005 Wim Taymans <wim@fluendo.com>
*
* gstqueue.c:
*
#include "gstqueue.h"
#include "gstscheduler.h"
+#include "gstpipeline.h"
#include "gstevent.h"
#include "gstinfo.h"
#include "gsterror.h"
} 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);
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 ())
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,
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 */
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,
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);
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;
- }
+ return GST_PAD_LINK_OK;
+}
- link_ret = GST_PAD_LINK_OK;
-#if 0
- link_ret = gst_pad_proxy_pad_link (pad, caps);
+static GstPadLinkReturn
+gst_queue_link_src (GstPad * pad, GstPad * peer)
+{
+ GstPadLinkReturn result = GST_PAD_LINK_OK;
- 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
+ /* FIXME, see if we need to push or get pulled */
+ if (GST_RPAD_LINKFUNC (peer))
+ result = GST_RPAD_LINKFUNC (peer) (peer, pad);
- return link_ret;
+ return result;
}
-static GstPadLinkReturn
-gst_queue_link_src (GstPad * pad, const GstCaps * caps)
+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;
- }
-#if 0
- link_ret = gst_pad_proxy_pad_link (pad, caps);
-#endif
- link_ret = GST_PAD_LINK_OK;
-
- 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));
+ 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 (otherpeer)
+ gst_object_unref (GST_OBJECT (otherpeer));
- 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;
g_cond_signal (queue->item_del);
}
-static void
-gst_queue_handle_pending_events (GstQueue * queue)
+#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)
+
+static gboolean
+gst_queue_handle_sink_event (GstPad * pad, GstEvent * event)
{
- /* 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;
-
- 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));
+ GstQueue *queue;
+
+ queue = GST_QUEUE (GST_OBJECT_PARENT (pad));
+
+ 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");
+
+ /* 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;
- }
- 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);
+
+ GST_QUEUE_MUTEX_LOCK;
+ g_queue_push_tail (queue->queue, event);
+ g_cond_signal (queue->item_add);
+
+ GST_QUEUE_MUTEX_UNLOCK;
+done:
+
+ return TRUE;
}
-static void
-gst_queue_chain (GstPad * pad, GstData * data)
+static gboolean
+gst_queue_is_empty (GstQueue * queue)
{
- 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)));
+}
+
- g_return_if_fail (pad != NULL);
- g_return_if_fail (GST_IS_PAD (pad));
- g_return_if_fail (data != NULL);
+static GstFlowReturn
+gst_queue_chain (GstPad * pad, GstBuffer * buffer)
+{
+ GstQueue *queue;
queue = GST_QUEUE (GST_OBJECT_PARENT (pad));
-restart:
+ GST_STREAM_LOCK (pad);
+
/* 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");
- gst_queue_locked_flush (queue);
- 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;
- }
- }
-
- if (GST_IS_BUFFER (data))
- GST_CAT_LOG_OBJECT (queue_dataflow, queue,
- "adding buffer %p of size %d", data, GST_BUFFER_SIZE (data));
+ 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. */
- if (GST_IS_BUFFER (data) &&
- ((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)) {
GST_QUEUE_MUTEX_UNLOCK;
g_signal_emit (G_OBJECT (queue), gst_queue_signals[SIGNAL_OVERRUN], 0);
GST_QUEUE_MUTEX_LOCK;
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 */
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;
}
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");
}
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;
-
- 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 ());
- }
+ /* we are flushing */
+ if (GST_RPAD_IS_FLUSHING (queue->sinkpad))
+ goto out_flushing;
+
+ 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");
}
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_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);
+ gst_event_ref (event);
+ res = gst_pad_event_default (pad, event);
+ GST_QUEUE_MUTEX_LOCK;
- 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;
}
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) {
}
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);
+
+ 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);
- return TRUE;
+ /* 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)
{
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_READY_TO_PAUSED:
+ 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));
+ queue->interrupt = FALSE;
+ break;
+ default:
+ break;
+ }
- g_warning ("queue %s does not connect different schedulers",
- GST_ELEMENT_NAME (queue));
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
- ret = GST_STATE_FAILURE;
- goto unlock;
- }
- }
- queue->interrupt = FALSE;
+ switch (GST_STATE_TRANSITION (element)) {
+ case GST_STATE_PLAYING_TO_PAUSED:
break;
case GST_STATE_PAUSED_TO_READY:
gst_queue_locked_flush (queue);
- gst_caps_replace (&queue->negotiated_caps, NULL);
+ break;
+ case GST_STATE_READY_TO_NULL:
break;
default:
break;
}
-
- GST_QUEUE_MUTEX_UNLOCK;
-
- 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:
GST_QUEUE_MUTEX_UNLOCK;
GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "done with state change");
return ret;
}
-
static void
gst_queue_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec)
#include <gst/gstelement.h>
-
G_BEGIN_DECLS
#define GST_TYPE_QUEUE \
/* 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 {
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
{
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));
}
/**
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)
-{
- 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_link)
- sclass->pad_link (sched, srcpad, sinkpad);
-}
-
-/**
- * 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)
+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_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);
+ if (sclass->create_task)
+ result = sclass->create_task (sched, func, data);
- 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;
}
/*
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));
#include <glib.h>
#include <gst/gstelement.h>
#include <gst/gstbin.h>
+#include <gst/gsttask.h>
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;
/*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];
};
/* 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];
};
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);
-
-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);
+GstTask* gst_scheduler_create_task (GstScheduler *sched, GstTaskFunction func, gpointer data);
+
/*
* creating schedulers
#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;
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",
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);
/* 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,
/* 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,
--- /dev/null
+/* GStreamer
+ * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
+ * 2005 Wim Taymans <wim@fluendo.com>
+ *
+ * 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;
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ * <2005> Wim Taymans <wim@fluendo.com>
+ *
+ * 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 <gst/gstobject.h>
+
+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__ */
+
+++ /dev/null
-/* GStreamer
- * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
- * 2000 Wim Taymans <wtay@chello.be>
- * 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
- *
- * 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 <omega@cse.ogi.edu>, "
- "Benjamin Otte <in7y118@informatik.uni-hamburg.de"
- "Wim Taymans <wim@fluendo.com");
-
-/* Thread signals and args */
-enum
-{
- SHUTDOWN,
- /* FILL ME */
- LAST_SIGNAL
-};
-
-enum
-{
- SPINUP = 0,
- STATECHANGE,
- STARTUP
-};
-
-enum
-{
- ARG_0,
- ARG_PRIORITY
-};
-
-
-static void gst_thread_base_init (gpointer g_class);
-static void gst_thread_class_init (gpointer g_class, gpointer class_data);
-static void gst_thread_init (GTypeInstance * instance, gpointer g_class);
-
-static void gst_thread_dispose (GObject * object);
-
-static void gst_thread_set_property (GObject * object, guint prop_id,
- const GValue * value, GParamSpec * pspec);
-static void gst_thread_get_property (GObject * object, guint prop_id,
- GValue * value, GParamSpec * pspec);
-static GstElementStateReturn gst_thread_change_state (GstElement * element);
-static void gst_thread_child_state_change (GstBin * bin,
- GstElementState oldstate, GstElementState newstate, GstElement * element);
-
-static void gst_thread_sync (GstThread * thread, gboolean is_self);
-
-#ifndef GST_DISABLE_LOADSAVE
-static xmlNodePtr gst_thread_save_thyself (GstObject * object,
- xmlNodePtr parent);
-static void gst_thread_restore_thyself (GstObject * object, xmlNodePtr self);
-#endif
-
-static void *gst_thread_main_loop (void *arg);
-
-#define GST_TYPE_THREAD_PRIORITY (gst_thread_priority_get_type())
-static GType
-gst_thread_priority_get_type (void)
-{
- static GType thread_priority_type = 0;
- static GEnumValue thread_priority[] = {
- {G_THREAD_PRIORITY_LOW, "LOW", "Low Priority Scheduling"},
- {G_THREAD_PRIORITY_NORMAL, "NORMAL", "Normal Scheduling"},
- {G_THREAD_PRIORITY_HIGH, "HIGH", "High Priority Scheduling"},
- {G_THREAD_PRIORITY_URGENT, "URGENT", "Urgent Scheduling"},
- {0, NULL, NULL},
- };
-
- if (!thread_priority_type) {
- thread_priority_type =
- g_enum_register_static ("GstThreadPriority", thread_priority);
- }
- return thread_priority_type;
-}
-
-static GstBinClass *parent_class = NULL;
-static guint gst_thread_signals[LAST_SIGNAL] = { 0 };
-GPrivate *gst_thread_current;
-
-GType
-gst_thread_get_type (void)
-{
- static GType thread_type = 0;
-
- if (!thread_type) {
- static const GTypeInfo thread_info = {
- sizeof (GstThreadClass),
- gst_thread_base_init,
- NULL,
- gst_thread_class_init,
- NULL,
- NULL,
- sizeof (GstThread),
- 0,
- gst_thread_init,
- NULL
- };
-
- thread_type = g_type_register_static (GST_TYPE_BIN, "GstThread",
- &thread_info, 0);
- }
- return thread_type;
-}
-
-static void
-gst_thread_base_init (gpointer g_class)
-{
- GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
-
- gst_element_class_set_details (gstelement_class, &gst_thread_details);
-}
-static void
-do_nothing (gpointer hi)
-{
-}
-static void
-gst_thread_class_init (gpointer g_class, gpointer class_data)
-{
- GObjectClass *gobject_class = G_OBJECT_CLASS (g_class);
-
-#ifndef GST_DISABLE_LOADSAVE
- GstObjectClass *gstobject_class = GST_OBJECT_CLASS (g_class);
-#endif
- GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
- GstBinClass *gstbin_class = GST_BIN_CLASS (g_class);
- GstThreadClass *klass = GST_THREAD_CLASS (g_class);
-
- /* setup gst_thread_current */
- gst_thread_current = g_private_new (do_nothing);
-
- parent_class = g_type_class_peek_parent (g_class);
-
- gobject_class->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 */
+++ /dev/null
-/* GStreamer
- * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
- * 2000 Wim Taymans <wtay@chello.be>
- *
- * 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 <glib.h>
-
-#include <gst/gstbin.h>
-
-
-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__ */
typedef struct _GstScheduler GstScheduler;
typedef struct _GstSchedulerClass GstSchedulerClass;
typedef struct _GstEvent GstEvent;
+typedef struct _GstMessage GstMessage;
typedef enum {
GST_STATE_VOID_PENDING = 0,
-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
-
-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
-
-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)
+ libthreadscheduler
-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)
+libthreadscheduler_la_SOURCES = threadscheduler.c
+libthreadscheduler_la_CFLAGS = $(GST_OBJ_CFLAGS)
+libthreadscheduler_la_LIBADD = $(GST_OBJ_LIBS)
+libthreadscheduler_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
-noinst_HEADERS = cothreads_compat.h gthread-cothreads.h faircothreads.h
+noinst_HEADERS =
if AS_LIBTOOL_WIN32
+++ /dev/null
-/* GStreamer
- * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
- * 2000 Wim Taymans <wtay@chello.be>
- *
- * 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
+++ /dev/null
-/* GStreamer
- * Copyright (C) 2004 Benjamin Otte <otte@gnome.org>
- *
- * 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 <gst/gst.h>
-#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)
+++ /dev/null
-/* GStreamer
- * Copyright (C) 2004 Martin Soto <martinsoto@users.sourceforge.net>
- *
- * 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 <glib.h>
-
-#include <gst/gst.h>
-
-#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 <sys/types.h>
-#include <linux/unistd.h>
-
-_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);
-}
+++ /dev/null
-/* GStreamer
- * Copyright (C) 2004 Martin Soto <martinsoto@users.sourceforge.net>
- *
- * 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__ */
+++ /dev/null
-/* GStreamer
- * Copyright (C) 2004 Martin Soto <martinsoto@users.sourceforge.net>
- *
- * 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 <glib.h>
-
-#include <gst/gst.h>
-#include <gst/gstqueue.h>
-
-#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);
+++ /dev/null
-/* GStreamer
- * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
- * 2000 Wim Taymans <wtay@chello.be>
- *
- * gstscheduler.c: Default scheduling code for most cases
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include <gst/gst.h>
-
-#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");
- }
-}
+++ /dev/null
-/* GStreamer
- * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
- * 2000 Wim Taymans <wtay@chello.be>
- *
- * 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/gst.h>
-
-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;
- }
-}
+++ /dev/null
-/* GStreamer
- * Copyright (C) 2003 Benjamin Otte <in7y118@public.uni-hamburg.de>
- *
- * 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 <glib.h>
-#include <gst/gstthread.h>
-
-/* 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__ */
--- /dev/null
+/* GStreamer2
+ * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
+ *
+ * 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 <gst/gst.h>
+#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)
-SUBDIRS = bytestream control dataprotocol getbits
+SUBDIRS = control dataprotocol getbits
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;
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);
/* 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;
GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
}
+#if 0
static void
gst_file_pad_chain (GstPad * gst_pad, GstData * data)
{
}
}
}
+#endif
static void
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));
}
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));
(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));
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);
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;
GstSeekAccuracy accuracy;
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));
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)
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
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 };
"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;
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
}
}
-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);
+
+ fakesink->last_message =
+ g_strdup_printf ("chain ******* (%s:%s)E (type: %d) %p",
+ GST_DEBUG_PAD_NAME (pad), GST_EVENT_TYPE (event), event);
- if (!fakesink->silent) {
- g_free (fakesink->last_message);
+ g_object_notify (G_OBJECT (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);
-
- 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;
+ 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) {
}
gst_buffer_unref (buf);
+
+ return GST_FLOW_OK;
}
static GstElementStateReturn
{
ARG_0,
ARG_NUM_SOURCES,
- ARG_LOOP_BASED,
+ ARG_HAS_LOOP,
+ ARG_HAS_GETRANGE,
ARG_OUTPUT,
ARG_DATA,
ARG_SIZETYPE,
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,
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 };
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));
"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;
fakesrc->last_message = NULL;
fakesrc->datarate = DEFAULT_DATARATE;
fakesrc->sync = DEFAULT_SYNC;
+ fakesrc->pad_mode = GST_ACTIVATE_NONE;
}
static void
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)
{
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},
};
{
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;
}
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->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);
+}
- 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);
- }
+static void
+gst_fakesrc_set_all_pad_functions (GstFakeSrc * src)
+{
+ GList *l;
- pads = GST_ELEMENT (src)->pads;
- while (pads) {
- GstPad *pad = GST_PAD (pads->data);
+ for (l = GST_ELEMENT_PADS (src); l; l = l->next)
+ gst_fakesrc_set_pad_functions (src, (GstPad *) l->data);
+}
- if (src->loop_based) {
- gst_pad_set_get_function (pad, NULL);
- } else {
- gst_pad_set_get_function (pad, GST_DEBUG_FUNCPTR (gst_fakesrc_get));
- }
+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);
- 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);
+ 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_set_pad_functions (fakesrc, srcpad);
+
+ g_free (name);
+
+ return srcpad;
}
static void
{
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");
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);
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--;
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);
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) =
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);
+
+ GST_STREAM_LOCK (pad);
+ if (src->need_flush) {
+ src->need_flush = FALSE;
+ gst_pad_push_event (pad, gst_event_new (GST_EVENT_FLUSH));
+ }
- pads = element->pads;
+ ret = gst_fakesrc_get_range_unlocked (pad, src->buffer_count,
+ DEFAULT_SIZEMAX, &buf);
+ if (ret != GST_FLOW_OK) {
+ goto pause;
+ }
- while (pads) {
- GstPad *pad = GST_PAD (pads->data);
- GstData *data;
+ ret = gst_pad_push (pad, buf);
+ if (ret != GST_FLOW_OK) {
+ goto pause;
+ }
- data = gst_fakesrc_get (pad);
- gst_pad_push (pad, data);
+ GST_STREAM_UNLOCK (pad);
+ return;
- if (src->eos) {
- 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);
- pads = g_list_next (pads);
+ 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);
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;
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:
break;
}
- if (GST_ELEMENT_CLASS (parent_class)->change_state)
- return GST_ELEMENT_CLASS (parent_class)->change_state (element);
-
- return GST_STATE_SUCCESS;
+ return result;
}
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;
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);
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);
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;
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) {
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 */
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 */
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;
}
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)) {
}
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));
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
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
/* GStreamer
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
* 2000 Wim Taymans <wtay@chello.be>
+ * 2005 Wim Taymans <wim@fluendo.com>
*
* gstidentity.c:
*
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");
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 };
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);
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));
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;
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
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");
+ }
- g_object_notify (G_OBJECT (identity), "last_message");
- }
- gst_pad_event_default (pad, event);
- return;
+ 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 &&
gst_buffer_unref (buf);
GST_ELEMENT_ERROR (identity, CORE, FAILED,
(_("Failed after iterations as requested.")), (NULL));
- return;
+ return GST_FLOW_ERROR;
}
}
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));
}
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) =
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;
-
- g_return_if_fail (element != NULL);
- g_return_if_fail (GST_IS_IDENTITY (element));
-
- identity = GST_IDENTITY (element);
-
- 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_getrange)
+ gst_pad_set_getrange_function (identity->srcpad, gst_identity_getrange);
+ else
+ gst_pad_set_getrange_function (identity->srcpad, NULL);
+
+ if (identity->has_chain)
+ gst_pad_set_chain_function (identity->sinkpad, gst_identity_chain);
+ else
+ gst_pad_set_chain_function (identity->sinkpad, NULL);
+
+ 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);
+
+ 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
{
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 PROP_HAS_CHAIN:
+ identity->has_chain = g_value_get_boolean (value);
+ gst_identity_set_dataflow_funcs (identity);
break;
- case ARG_SLEEP_TIME:
+ 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:
{
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 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 ARG_SLEEP_TIME:
+ 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:
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;
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;
gchar *last_message;
GstCaps *srccaps;
- guint64 bytes_handled;
+ guint64 offset;
};
struct _GstIdentityClass {
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
* 2000 Wim Taymans <wtay@chello.be>
* 2003 Colin Walters <cwalters@gnome.org>
+ * 2005 Wim Taymans <wim@fluendo.com>
*
* gstqueue.c:
*
#include "gstqueue.h"
#include "gstscheduler.h"
+#include "gstpipeline.h"
#include "gstevent.h"
#include "gstinfo.h"
#include "gsterror.h"
} 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);
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 ())
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,
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 */
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,
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);
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;
- }
+ return GST_PAD_LINK_OK;
+}
- link_ret = GST_PAD_LINK_OK;
-#if 0
- link_ret = gst_pad_proxy_pad_link (pad, caps);
+static GstPadLinkReturn
+gst_queue_link_src (GstPad * pad, GstPad * peer)
+{
+ GstPadLinkReturn result = GST_PAD_LINK_OK;
- 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
+ /* FIXME, see if we need to push or get pulled */
+ if (GST_RPAD_LINKFUNC (peer))
+ result = GST_RPAD_LINKFUNC (peer) (peer, pad);
- return link_ret;
+ return result;
}
-static GstPadLinkReturn
-gst_queue_link_src (GstPad * pad, const GstCaps * caps)
+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;
- }
-#if 0
- link_ret = gst_pad_proxy_pad_link (pad, caps);
-#endif
- link_ret = GST_PAD_LINK_OK;
-
- 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));
+ 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 (otherpeer)
+ gst_object_unref (GST_OBJECT (otherpeer));
- 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;
g_cond_signal (queue->item_del);
}
-static void
-gst_queue_handle_pending_events (GstQueue * queue)
+#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)
+
+static gboolean
+gst_queue_handle_sink_event (GstPad * pad, GstEvent * event)
{
- /* 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;
-
- 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));
+ GstQueue *queue;
+
+ queue = GST_QUEUE (GST_OBJECT_PARENT (pad));
+
+ 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");
+
+ /* 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;
- }
- 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);
+
+ GST_QUEUE_MUTEX_LOCK;
+ g_queue_push_tail (queue->queue, event);
+ g_cond_signal (queue->item_add);
+
+ GST_QUEUE_MUTEX_UNLOCK;
+done:
+
+ return TRUE;
}
-static void
-gst_queue_chain (GstPad * pad, GstData * data)
+static gboolean
+gst_queue_is_empty (GstQueue * queue)
{
- 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)));
+}
+
- g_return_if_fail (pad != NULL);
- g_return_if_fail (GST_IS_PAD (pad));
- g_return_if_fail (data != NULL);
+static GstFlowReturn
+gst_queue_chain (GstPad * pad, GstBuffer * buffer)
+{
+ GstQueue *queue;
queue = GST_QUEUE (GST_OBJECT_PARENT (pad));
-restart:
+ GST_STREAM_LOCK (pad);
+
/* 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");
- gst_queue_locked_flush (queue);
- 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;
- }
- }
-
- if (GST_IS_BUFFER (data))
- GST_CAT_LOG_OBJECT (queue_dataflow, queue,
- "adding buffer %p of size %d", data, GST_BUFFER_SIZE (data));
+ 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. */
- if (GST_IS_BUFFER (data) &&
- ((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)) {
GST_QUEUE_MUTEX_UNLOCK;
g_signal_emit (G_OBJECT (queue), gst_queue_signals[SIGNAL_OVERRUN], 0);
GST_QUEUE_MUTEX_LOCK;
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 */
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;
}
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");
}
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;
-
- 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 ());
- }
+ /* we are flushing */
+ if (GST_RPAD_IS_FLUSHING (queue->sinkpad))
+ goto out_flushing;
+
+ 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");
}
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_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);
+ gst_event_ref (event);
+ res = gst_pad_event_default (pad, event);
+ GST_QUEUE_MUTEX_LOCK;
- 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;
}
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) {
}
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);
+
+ 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);
- return TRUE;
+ /* 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)
{
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_READY_TO_PAUSED:
+ 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));
+ queue->interrupt = FALSE;
+ break;
+ default:
+ break;
+ }
- g_warning ("queue %s does not connect different schedulers",
- GST_ELEMENT_NAME (queue));
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element);
- ret = GST_STATE_FAILURE;
- goto unlock;
- }
- }
- queue->interrupt = FALSE;
+ switch (GST_STATE_TRANSITION (element)) {
+ case GST_STATE_PLAYING_TO_PAUSED:
break;
case GST_STATE_PAUSED_TO_READY:
gst_queue_locked_flush (queue);
- gst_caps_replace (&queue->negotiated_caps, NULL);
+ break;
+ case GST_STATE_READY_TO_NULL:
break;
default:
break;
}
-
- GST_QUEUE_MUTEX_UNLOCK;
-
- 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:
GST_QUEUE_MUTEX_UNLOCK;
GST_CAT_LOG_OBJECT (GST_CAT_STATES, element, "done with state change");
return ret;
}
-
static void
gst_queue_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec)
#include <gst/gstelement.h>
-
G_BEGIN_DECLS
#define GST_TYPE_QUEUE \
/* 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 {
/* GStreamer
* Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
- * 2000 Wim Taymans <wim.taymans@chello.be>
+ * 2000,2001,2002,2003,2004,2005 Wim Taymans <wim@fluendo.com>
+ *
*
* gsttee.c: Tee element, one in N out
*
"Generic",
"1-to-N pipe fitting",
"Erik Walthinsen <omega@cse.ogi.edu>, "
- "Wim Taymans <wim.taymans@chello.be>");
+ "Wim \"Tim\" Taymans <wim@fluendo.com>");
-/* Tee signals and args */
enum
{
- /* FILL ME */
- LAST_SIGNAL
-};
-
-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 */
};
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
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);
}
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)
-{
- GstPad *pad = (GstPad *) a;
- gchar *name = (gchar *) b;
-
- g_assert (GST_IS_PAD (pad));
-
- 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)
+static void
+gst_tee_update_pad_functions (GstTee * tee)
{
- 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;
+ gst_pad_set_activate_function (tee->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_tee_sink_activate));
+
+ 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);
+
+ 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 *
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;
}
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.
- */
+typedef struct
+{
+ GstTee *tee;
+ GstBuffer *buffer;
+} PushData;
+
+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));
+
+ WITH_STREAM_LOCK (pad, res = gst_tee_handle_buffer (tee, buffer));
+
+ return res;
+}
+
+#define DEFAULT_SIZE 1024
+
static void
-gst_tee_chain (GstPad * pad, GstData * _data)
+gst_tee_loop (GstPad * pad)
{
- GstBuffer *buf = GST_BUFFER (_data);
+ GstBuffer *buffer;
+ GstFlowReturn res;
GstTee *tee;
- const GList *pads;
- g_return_if_fail (pad != NULL);
- g_return_if_fail (GST_IS_PAD (pad));
- g_return_if_fail (buf != NULL);
+ GST_STREAM_LOCK (pad);
tee = GST_TEE (gst_pad_get_parent (pad));
- gst_buffer_ref_by_count (buf, GST_ELEMENT (tee)->numsrcpads - 1);
+ res = gst_pad_pull_range (pad, tee->offset, DEFAULT_SIZE, &buffer);
+ if (res != GST_FLOW_OK)
+ goto pause_task;
- pads = GST_ELEMENT (tee)->pads;
+ res = gst_tee_handle_buffer (tee, buffer);
+ if (res != GST_FLOW_OK)
+ goto pause_task;
- while (pads) {
- GstPad *outpad = GST_PAD (pads->data);
+ GST_STREAM_UNLOCK (pad);
+ return;
- pads = g_list_next (pads);
+pause_task:
+ gst_pad_pause_task (pad);
+ GST_STREAM_UNLOCK (pad);
+ return;
+}
- if (GST_PAD_DIRECTION (outpad) != GST_PAD_SRC)
- continue;
+static gboolean
+gst_tee_sink_activate (GstPad * pad, GstActivateMode mode)
+{
+ gboolean result = FALSE;
+ GstTee *tee;
- 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");
- }
+ tee = GST_TEE (GST_OBJECT_PARENT (pad));
- if (GST_PAD_IS_USABLE (outpad))
- gst_pad_push (outpad, GST_DATA (buf));
- else
- gst_buffer_unref (buf);
+ 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;
}
GstPad *sinkpad;
gboolean silent;
+ gboolean has_chain;
+ gboolean has_sink_loop;
+ gint pad_counter;
+ guint64 offset;
+ GstActivateMode sink_mode;
+
gchar *last_message;
};
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);
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);
TESTS = $(top_builddir)/tools/gst-register-@GST_MAJORMINOR@ \
gst/gstbin \
+ gst/gstbus \
gst/gstcaps \
gst/gstdata \
gst/gstiterator \
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);
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);
* Boston, MA 02111-1307, USA.
*/
+#include "unistd.h"
+
#include <gst/gst.h>
+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));
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);
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;
}
* Boston, MA 02111-1307, USA.
*/
+#include "unistd.h"
+
#include <gst/gst.h>
+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));
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);
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;
}
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");
}
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));
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)));
#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
-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;
- }
+gboolean caught_intr = FALSE;
+gboolean caught_error = FALSE;
+gboolean tags = 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 *
}
}
-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
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)
{
{
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;
}
}
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[])
/* options */
gboolean verbose = FALSE;
- gboolean tags = FALSE;
gboolean no_fault = FALSE;
gboolean trace = FALSE;
gchar *savefile = NULL;
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"));
#endif
if (!savefile) {
+ GstElementState state, pending;
if (!GST_IS_BIN (pipeline)) {
GstElement *real_pipeline = gst_element_factory_make ("pipeline", NULL);
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);
+
+ /* see if we got any messages */
+ while (g_main_context_iteration (NULL, 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);
+ 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)
#include <gst/gst.h>
#include <locale.h>
-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;
-
- if (s_clock) {
- //g_print ("%lld\n", gst_clock_get_time (s_clock));
- }
-
- 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);
- */
+ GstBus *bus;
+ GstMessageType revent;
+ GstMessage *message = NULL;
+
+ 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;
+ }
}
- 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) {
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) {
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);
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]);
GstPad *pad;
GstRealPad *realpad;
GstPadTemplate *padtemplate;
- GList *children;
- GstElement *child;
gint maxlevel = 0;
element = gst_element_factory_create (factory, "element");
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, "<loop-based function=\"%s\"/>",
- GST_DEBUG_FUNCPTR_NAME (element->loopfunc));
PUT_STRING (2, "<state-change function=\"%s\"/>",
GST_DEBUG_FUNCPTR_NAME (gstelement_class->change_state));
if (realpad->chainfunc)
PUT_STRING (4, "<chain-based function=\"%s\"/>",
GST_DEBUG_FUNCPTR_NAME (realpad->chainfunc));
- if (realpad->getfunc)
- PUT_STRING (4, "<get-based function=\"%s\"/>",
- GST_DEBUG_FUNCPTR_NAME (realpad->getfunc));
+ if (realpad->getrangefunc)
+ PUT_STRING (4, "<get-range-based function=\"%s\"/>",
+ GST_DEBUG_FUNCPTR_NAME (realpad->getrangefunc));
if (realpad->formatsfunc != gst_pad_get_formats_default) {
PUT_STRING (4, "<formats-function function=\"%s\">",
GST_DEBUG_FUNCPTR_NAME (realpad->formatsfunc));
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);
-
- PUT_ESCAPED (2, "child", GST_ELEMENT_NAME (child));
- }
- PUT_END_TAG (1, "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_END_TAG (0, "element");
return 0;