+2005-01-31 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
+
+ * docs/pwg/advanced-types.xml:
+ Fix description for buffer-frames=0.
+ * docs/gst/tmpl/gstbin.sgml:
+ * gst/gstbin.c: (gst_bin_child_state_change_func),
+ (gst_bin_change_state), (gst_bin_change_state_norecurse):
+ * gst/gstbin.h:
+ * testsuite/threads/Makefile.am:
+ * testsuite/threads/threadi.c: (cb_timeout), (cb_quit), (cb_eos),
+ (cb_state), (cb_play), (main):
+ Fix non-recursive state changes to *really* change the state
+ of the object, and not just call parent_class->state_change.
+ Fix a lot of lockups caused by this. Fixes #132775. Add test
+ for the problem. Also enable test to show #142588 (fixed).
+ * gst/gstthread.c: (gst_thread_change_state),
+ (gst_thread_child_state_change):
+ Don't exit the thread if we go to NULL and are inside thread
+ context. Instead, return control to the main thread context
+ and exit from there.
+ * gst/gstelement.c: (gst_element_disable_threadsafe_properties):
+ Don't unset virtual functions, since those may still be used.
+ That's not necessarily correct, but suffices for now.
+ * configure.ac:
+ * testsuite/Makefile.am:
+ * testsuite/pad/Makefile.am:
+ * testsuite/pad/chainnopull.c: (gst_test_sink_class_init),
+ (gst_test_sink_base_init), (gst_test_sink_chain),
+ (gst_test_sink_init), (main):
+ * testsuite/pad/getnopush.c: (gst_test_src_class_init),
+ (gst_test_src_base_init), (gst_test_src_get), (gst_test_src_init),
+ (main):
+ * testsuite/pad/link.c: (gst_test_element_class_init),
+ (gst_test_element_base_init), (gst_test_src_get),
+ (gst_test_src_loop), (gst_test_src_init), (gst_test_filter_chain),
+ (gst_test_filter_loop), (gst_test_filter_init),
+ (gst_test_sink_chain), (gst_test_sink_loop), (gst_test_sink_init),
+ (cb_error), (main):
+ Add tests to show #150546. Pass, but should fail (currently
+ disabled from the testsuite).
+ * gst/gstscheduler.c: (gst_scheduler_dispose):
+ Dereference child schedulers on dispose (#94464).
+ * testsuite/bytestream/filepadsink.c: (gst_fp_sink_init):
+ Fix typo.
+ * testsuite/threads/thread.c: (main):
+ Add more debug.
+
2005-01-29 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
* gst/gstpad.c: (gst_pad_push):
testsuite/ghostpads/Makefile
testsuite/indexers/Makefile
testsuite/negotiation/Makefile
+testsuite/pad/Makefile
testsuite/parse/Makefile
testsuite/plugin/Makefile
testsuite/refcounting/Makefile
@GST_BIN_SELF_SCHEDULABLE:
@GST_BIN_FLAG_PREFER_COTHREADS:
@GST_BIN_FLAG_FIXED_CLOCK:
+@GST_BIN_STATE_LOCKED:
@GST_BIN_FLAG_LAST:
<!-- ##### FUNCTION gst_bin_new ##### -->
<row>
<entry>buffer-frames</entry>
<entry>integer</entry>
- <entry>greater than 0</entry>
+ <entry>Any</entry>
<entry>
The number of frames per buffer. The reason for this property
is that the element does not need to reuse buffers or use data
spanned over multiple buffers, so this property - when used
rightly - will decrease latency. Note that some people think that
this property is very ugly, whereas others think it is vital for
- the use of &GStreamer; in professional audio applications.
+ the use of &GStreamer; in professional audio applications. The
+ special value zero is reserved and implies that size is variable
+ between buffers.
</entry>
</row>
bin->child_states[new_idx]++;
for (i = GST_NUM_STATES - 1; i >= 0; i--) {
- if (bin->child_states[i] != 0) {
+ if (bin->child_states[i] != 0 || i == 0) {
gint state = (1 << i);
- if (GST_STATE (bin) != state) {
+ /* 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));
GstBin *bin;
GstElementStateReturn ret;
GstElementState old_state, pending;
- SetKidStateData data;
g_return_val_if_fail (GST_IS_BIN (element), GST_STATE_FAILURE);
if (pending == GST_STATE_VOID_PENDING)
return GST_STATE_SUCCESS;
- data.pending = pending;
- data.result = GST_STATE_SUCCESS;
- if (!gst_bin_foreach (bin, set_kid_state_func, &data)) {
- GST_STATE_PENDING (element) = old_state;
- return GST_STATE_FAILURE;
+ /* 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");
}
- 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 (data.result == GST_STATE_ASYNC)
- ret = GST_STATE_ASYNC;
- else {
- /* 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;
+ /* 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 ret;
- if (parent_class->change_state) {
+ if (GST_ELEMENT_GET_CLASS (bin)->change_state) {
GST_CAT_LOG_OBJECT (GST_CAT_STATES, bin, "setting bin's own state");
- ret = parent_class->change_state (GST_ELEMENT (bin));
+
+ /* 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
/**
* GstBinFlags:
- * @GST_BIN_FLAG_MANAGER: This bin has a scheduler and can be used as a toplevel bin.
- * @GST_BIN_SELF_SCHEDULABLE: This bin iterates itself, so no calls to gst_bin_iterate() should be made.
- * @GST_BIN_FLAG_PREFER_COTHREADS: This bin preferes to have its elements scheduled with cothreads
- * @GST_BIN_FLAG_FIXED_CLOCK: This bin uses a fixed clock, possibly the one set with gst_bin_use_clock().
- * @GST_BIN_FLAG_LAST: id of last for chaining flsg definitions
- *
- * Flags for a #GstBin .
+ * @GST_BIN_FLAG_MANAGER: this bin is a manager of child elements, i.e.
+ * a pipeline or thread.
+ * @GST_BIN_SELF_SCHEDULABLE: the bin iterates itself.
+ * @GST_BIN_FLAG_PREFER_COTHREADS: we prefer to have cothreads when its
+ * an option, over chain-based.
+ * @GST_BIN_FLAG_FIXED_CLOCK: bin has one clock that cannot be changed.
+ * @GST_BIN_STATE_LOCKED: indicator that we are in a non-recursive
+ * state-change on the bin, or that kids should not change parent state.
+ * Both are internally used to prevent infinitely recursive loops of
+ * state changes. Since they are mutually exclusive and serve the same
+ * purpose, we use the same flag for them.
+ * @GST_BIN_FLAG_LAST: the last enum in the series of flags in a bin,
+ * derived classes can use this as first value in a list of flags.
+ *
+ * GstBinFlags are a set of flags specific to bins. Most are set/used
+ * internally. They can be checked using the GST_FLAG_IS_SET () macro,
+ * 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_FLAG_PREFER_COTHREADS,
GST_BIN_FLAG_FIXED_CLOCK,
- /* padding */
+ GST_BIN_STATE_LOCKED,
GST_BIN_FLAG_LAST = GST_ELEMENT_FLAG_LAST + 5
} GstBinFlags;
g_return_if_fail (GST_IS_ELEMENT (element));
GST_FLAG_UNSET (element, GST_ELEMENT_USE_THREADSAFE_PROPERTIES);
- element->pre_run_func = NULL;
- element->post_run_func = NULL;
+
+ //element->pre_run_func = NULL;
+ //element->post_run_func = NULL;
/* let's keep around that async queue */
}
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);
}
if (thread->thread_id != NULL) {
thread->thread_id = NULL;
if (is_self) {
- /* or should we continue? */
GST_LOG_OBJECT (thread,
- "Thread %s is destroying itself. Function call will not return!",
+ "Thread %s is destroying itself. Returning to mainloop ASAP!",
GST_ELEMENT_NAME (thread));
- gst_scheduler_reset (GST_ELEMENT_SCHED (thread));
-
- /* unlock and signal - we are out */
- GST_DEBUG_OBJECT (thread, "signal");
- g_cond_signal (thread->cond);
- GST_DEBUG_OBJECT (thread, "unlock");
- g_mutex_unlock (thread->lock);
-
- GST_INFO_OBJECT (thread, "GThread %p is exiting", g_thread_self ());
-
- g_signal_emit (G_OBJECT (thread), gst_thread_signals[SHUTDOWN], 0);
-
- g_thread_exit (NULL);
- return GST_STATE_SUCCESS;
+ break;
} else {
/* now wait for the thread to destroy itself */
GST_DEBUG_OBJECT (thread, "signal");
} 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);
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");
bins bytestream caps cleanup clock \
$(GST_DEBUG_DIRS) \
dlopen dynparams \
- elements ghostpads indexers negotiation \
+ elements ghostpads indexers negotiation pad \
$(GST_PARSE_DIRS) \
plugin refcounting schedulers states tags threads
bins bytestream caps cleanup clock \
debug \
dlopen dynparams \
- elements ghostpads indexers negotiation \
+ elements ghostpads indexers negotiation pad \
parse \
plugin refcounting schedulers states tags threads
fp->sinkpad =
GST_FILE_PAD (gst_file_pad_new (gst_static_pad_template_get (&template),
- "src"));
+ "sink"));
gst_file_pad_set_iterate_function (fp->sinkpad, do_tests);
gst_element_add_pad (GST_ELEMENT (fp), GST_PAD (fp->sinkpad));
}
--- /dev/null
+include ../Rules
+
+tests_pass = link
+tests_fail =
+tests_ignore = chainnopull getnopush
--- /dev/null
+/*
+ * this tests that chain-based pads don't pull.
+ */
+
+#include <gst/gst.h>
+
+typedef struct _GstTestSink
+{
+ GstElement parent;
+ GstPad *sinkpad;
+} GstTestSink;
+
+typedef GstElementClass GstTestSinkClass;
+
+static void
+gst_test_sink_class_init (GstTestSinkClass * klass)
+{
+}
+
+static void
+gst_test_sink_base_init (gpointer klass)
+{
+}
+
+static void
+gst_test_sink_chain (GstPad * pad, GstData * data)
+{
+ data = gst_pad_pull (pad);
+}
+
+static void
+gst_test_sink_init (GstTestSink * sink)
+{
+ sink->sinkpad = gst_pad_new ("sink", GST_PAD_SINK);
+ gst_pad_set_chain_function (sink->sinkpad, gst_test_sink_chain);
+ gst_element_add_pad (GST_ELEMENT (sink), sink->sinkpad);
+}
+
+GST_BOILERPLATE (GstTestSink, gst_test_sink, GstElement, GST_TYPE_ELEMENT);
+
+int
+main (int argc, char *argv[])
+{
+ GstElement *pipeline, *fakesrc, *testsink;
+ gint n;
+
+ gst_init (&argc, &argv);
+
+ pipeline = gst_pipeline_new ("p");
+ fakesrc = gst_element_factory_make ("fakesrc", "src");
+ testsink = g_object_new (gst_test_sink_get_type (), NULL);
+ gst_object_set_name (GST_OBJECT (testsink), "sink");
+ gst_bin_add_many (GST_BIN (pipeline), fakesrc, testsink, NULL);
+ gst_element_link (fakesrc, testsink);
+ gst_element_set_state (pipeline, GST_STATE_PLAYING);
+
+ for (n = 0; n < 100; n++) {
+ if (!gst_bin_iterate (GST_BIN (pipeline)))
+ break;
+ }
+
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+ gst_object_unref (GST_OBJECT (pipeline));
+
+ return 0;
+}
--- /dev/null
+/*
+ * this tests that get-based pads don't push.
+ */
+
+#include <gst/gst.h>
+
+typedef struct _GstTestSrc
+{
+ GstElement parent;
+ GstPad *srcpad;
+} GstTestSrc;
+
+typedef GstElementClass GstTestSrcClass;
+
+static void
+gst_test_src_class_init (GstTestSrcClass * klass)
+{
+}
+static void
+gst_test_src_base_init (gpointer klass)
+{
+}
+
+static GstData *
+gst_test_src_get (GstPad * pad)
+{
+ GstEvent *event;
+
+ event = gst_event_new (GST_EVENT_INTERRUPT);
+ gst_event_ref (event);
+ gst_pad_push (pad, GST_DATA (event));
+
+ return GST_DATA (event);
+}
+
+static void
+gst_test_src_init (GstTestSrc * src)
+{
+ src->srcpad = gst_pad_new ("src", GST_PAD_SRC);
+ gst_pad_set_get_function (src->srcpad, gst_test_src_get);
+ gst_element_add_pad (GST_ELEMENT (src), src->srcpad);
+}
+
+GST_BOILERPLATE (GstTestSrc, gst_test_src, GstElement, GST_TYPE_ELEMENT);
+
+int
+main (int argc, char *argv[])
+{
+ GstElement *pipeline, *testsrc, *fakesink;
+ gint n;
+
+ gst_init (&argc, &argv);
+
+ pipeline = gst_pipeline_new ("p");
+ testsrc = g_object_new (gst_test_src_get_type (), NULL);
+ gst_object_set_name (GST_OBJECT (testsrc), "src");
+ fakesink = gst_element_factory_make ("fakesink", "sink");
+ gst_bin_add_many (GST_BIN (pipeline), testsrc, fakesink, NULL);
+ gst_element_link (testsrc, fakesink);
+ gst_element_set_state (pipeline, GST_STATE_PLAYING);
+
+ for (n = 0; n < 100; n++) {
+ if (!gst_bin_iterate (GST_BIN (pipeline)))
+ break;
+ }
+
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+ gst_object_unref (GST_OBJECT (pipeline));
+
+ return 0;
+}
--- /dev/null
+/*
+ * Test that:
+ * - get-based sources can return data, loop-based sources can push.
+ * - chain-based filters receive/push, loop-based filters can pull/push.
+ * - chain-based sinks receive, loop-based sinks pull.
+ */
+
+#include <gst/gst.h>
+
+/*
+ * Scary type code.
+ */
+
+typedef struct _GstTestElement
+{
+ GstElement parent;
+ GstPad *srcpad, *sinkpad;
+} GstTestSrc, GstTestFilter, GstTestSink, GstTestElement;
+
+typedef GstElementClass GstTestSrcClass, GstTestFilterClass, GstTestSinkClass,
+ GstTestElementClass;
+
+#define gst_test_src_class_init gst_test_element_class_init
+#define gst_test_filter_class_init gst_test_element_class_init
+#define gst_test_sink_class_init gst_test_element_class_init
+
+#define gst_test_src_base_init gst_test_element_base_init
+#define gst_test_filter_base_init gst_test_element_base_init
+#define gst_test_sink_base_init gst_test_element_base_init
+
+static void
+gst_test_element_class_init (GstTestElementClass * klass)
+{
+}
+static void
+gst_test_element_base_init (gpointer klass)
+{
+}
+
+/*
+ * Actual element code.
+ */
+
+gboolean loop = FALSE;
+
+static GstData *
+gst_test_src_get (GstPad * pad)
+{
+ return GST_DATA (gst_event_new (GST_EVENT_INTERRUPT));
+}
+
+static void
+gst_test_src_loop (GstElement * element)
+{
+ GstTestSrc *src = (GstTestElement *) element;
+
+ gst_pad_push (src->srcpad, gst_test_src_get (src->srcpad));
+}
+
+static void
+gst_test_src_init (GstTestElement * src)
+{
+ src->srcpad = gst_pad_new ("src", GST_PAD_SRC);
+ if (loop) {
+ gst_element_set_loop_function (GST_ELEMENT (src), gst_test_src_loop);
+ } else {
+ gst_pad_set_get_function (src->srcpad, gst_test_src_get);
+ }
+ gst_element_add_pad (GST_ELEMENT (src), src->srcpad);
+
+ GST_FLAG_SET (src, GST_ELEMENT_EVENT_AWARE);
+}
+
+static void
+gst_test_filter_chain (GstPad * pad, GstData * data)
+{
+ GstTestFilter *filter = (GstTestElement *) gst_pad_get_parent (pad);
+
+ gst_pad_push (filter->srcpad, data);
+}
+
+static void
+gst_test_filter_loop (GstElement * element)
+{
+ GstTestFilter *filter = (GstTestElement *) element;
+
+ gst_test_filter_chain (filter->sinkpad, gst_pad_pull (filter->sinkpad));
+}
+
+static void
+gst_test_filter_init (GstTestElement * filter)
+{
+ filter->sinkpad = gst_pad_new ("sink", GST_PAD_SINK);
+ if (loop) {
+ gst_element_set_loop_function (GST_ELEMENT (filter), gst_test_filter_loop);
+ } else {
+ gst_pad_set_chain_function (filter->sinkpad, gst_test_filter_chain);
+ }
+ gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
+
+ filter->srcpad = gst_pad_new ("src", GST_PAD_SRC);
+ gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
+
+ GST_FLAG_SET (filter, GST_ELEMENT_EVENT_AWARE);
+}
+
+static void
+gst_test_sink_chain (GstPad * pad, GstData * data)
+{
+ gst_data_unref (data);
+}
+
+static void
+gst_test_sink_loop (GstElement * element)
+{
+ GstTestSink *sink = (GstTestElement *) element;
+
+ gst_test_sink_chain (sink->sinkpad, gst_pad_pull (sink->sinkpad));
+}
+
+static void
+gst_test_sink_init (GstTestElement * sink)
+{
+ sink->sinkpad = gst_pad_new ("sink", GST_PAD_SINK);
+ if (loop) {
+ gst_element_set_loop_function (GST_ELEMENT (sink), gst_test_sink_loop);
+ } else {
+ gst_pad_set_chain_function (sink->sinkpad, gst_test_sink_chain);
+ }
+ gst_element_add_pad (GST_ELEMENT (sink), sink->sinkpad);
+
+ GST_FLAG_SET (sink, GST_ELEMENT_EVENT_AWARE);
+}
+
+#define parent_class src_parent_class
+GST_BOILERPLATE (GstTestSrc, gst_test_src, GstElement, GST_TYPE_ELEMENT);
+#undef parent_class
+#define parent_class filter_parent_class
+GST_BOILERPLATE (GstTestFilter, gst_test_filter, GstElement, GST_TYPE_ELEMENT);
+#undef parent_class
+#define parent_class sink_parent_class
+GST_BOILERPLATE (GstTestSink, gst_test_sink, GstElement, GST_TYPE_ELEMENT);
+#undef parent_class
+
+/*
+ * Actual test.
+ */
+
+static void
+cb_error (GstElement * element)
+{
+ g_assert_not_reached ();
+}
+
+int
+main (int argc, char *argv[])
+{
+ GstElement *pipeline, *src, *filter, *sink;
+ gint n, r;
+ gboolean res;
+
+ gst_init (&argc, &argv);
+
+ for (r = 0; r < 2; r++) {
+ pipeline = gst_pipeline_new ("p");
+ g_signal_connect (pipeline, "error", G_CALLBACK (cb_error), NULL);
+ src = g_object_new (gst_test_src_get_type (), NULL);
+ gst_object_set_name (GST_OBJECT (src), "src");
+ filter = g_object_new (gst_test_filter_get_type (), NULL);
+ gst_object_set_name (GST_OBJECT (filter), "filter");
+ sink = g_object_new (gst_test_sink_get_type (), NULL);
+ gst_object_set_name (GST_OBJECT (sink), "sink");
+ gst_bin_add_many (GST_BIN (pipeline), src, filter, sink, NULL);
+ res = gst_element_link (src, filter);
+ g_assert (res);
+ res = gst_element_link (filter, sink);
+ g_assert (res);
+ gst_element_set_state (pipeline, GST_STATE_PLAYING);
+
+ for (n = 0; n < 100; n++) {
+ if (!gst_bin_iterate (GST_BIN (pipeline)))
+ g_assert_not_reached ();
+ }
+
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+ gst_object_unref (GST_OBJECT (pipeline));
+
+ /* switch element types */
+ g_print ("Loop=%s done\n", loop ? "true" : "false");
+ loop = !loop;
+ }
+
+ return 0;
+}
include ../Rules
-tests_pass = thread1 thread2 thread3 thread4 thread5 threade threadf signal1 159852
+tests_pass = thread1 thread2 thread3 thread4 thread5 threade threadf signal1 159852 threadg threadi
tests_fail = 159566 signal2 signal3
# threadh
-tests_ignore = queue threadb threadc threadd staticrec threadg
+tests_ignore = queue threadb threadc threadd staticrec
thread1_SOURCES = thread.c
thread1_CFLAGS = -DTESTNUM=1 $(AM_CFLAGS)
gst_element_set_state (pipeline, GST_STATE_PLAYING);
g_print ("running ...\n");
while (gst_bin_iterate (GST_BIN (pipeline)));
+ g_print ("done ...\n");
gst_element_set_state (pipeline, GST_STATE_NULL);
}
if (TESTNUM == 4) {
--- /dev/null
+/*
+ * Test two ways of going non-lineairly to PLAYING. Both tests have a thread
+ * containing a fakesrc/sink and a containing thread.
+ *
+ * Test 1 tests by adding fakesrc, putting thread to PLAYING, adding
+ * fakesink, syncing state and see if it iterates.
+ *
+ * Test2 tests by adding fakesrc/fakesink, setting fakesrc to PLAYING
+ * (which should increment the container state) and then synchronizing
+ * state. This reflects bug #123775.
+ */
+
+#include <gst/gst.h>
+
+static GstElement *pipeline, *fakesrc, *fakesink;
+gboolean bug = FALSE;
+
+static gboolean
+cb_timeout (gpointer data)
+{
+ g_assert_not_reached ();
+
+ return FALSE;
+}
+
+static gboolean
+cb_quit (gpointer data)
+{
+ gst_main_quit ();
+
+ g_print ("Quit mainloop\n");
+
+ /* once */
+ return FALSE;
+}
+
+static void
+cb_eos (gpointer data)
+{
+ g_print ("Received EOS\n");
+
+ g_idle_add ((GSourceFunc) cb_quit, NULL);
+}
+
+static void
+cb_state (GstElement * element, GstElementState old_state,
+ GstElementState new_state, gpointer data)
+{
+ g_print ("Changed state from %d to %d\n", old_state, new_state);
+}
+
+static gboolean
+cb_play (gpointer data)
+{
+ GstElementStateReturn res;
+
+ if (bug) {
+ g_print ("Setting state\n");
+ gst_element_set_state (fakesrc, GST_STATE_PLAYING);
+ g_print ("Done\n");
+ } else {
+ gst_element_set_state (pipeline, GST_STATE_PLAYING);
+ gst_bin_add (GST_BIN (pipeline), fakesink);
+ }
+ g_print ("Syncing state\n");
+ res = gst_bin_sync_children_state (GST_BIN (data));
+ g_assert (res == GST_STATE_SUCCESS);
+
+ g_print ("Set to playing correctly: %d\n", GST_STATE (pipeline));
+
+ /* once */
+ return FALSE;
+}
+
+gint
+main (gint argc, gchar * argv[])
+{
+ gint n, id;
+
+ gst_init (&argc, &argv);
+
+ for (n = 0; n < 2; n++) {
+ pipeline = gst_thread_new ("p");
+ g_signal_connect (pipeline, "state-change", G_CALLBACK (cb_state), NULL);
+ fakesrc = gst_element_factory_make ("fakesrc", "src");
+ g_object_set (G_OBJECT (fakesrc), "num-buffers", 1, NULL);
+ fakesink = gst_element_factory_make ("fakesink", "sink");
+ if (bug) {
+ gst_bin_add_many (GST_BIN (pipeline), fakesrc, fakesink, NULL);
+ } else {
+ gst_bin_add (GST_BIN (pipeline), fakesrc);
+ }
+ gst_element_link (fakesrc, fakesink);
+ g_signal_connect (pipeline, "eos", G_CALLBACK (cb_eos), NULL);
+ g_idle_add ((GSourceFunc) cb_play, pipeline);
+
+ /* give 5 seconds */
+ id = g_timeout_add (5000, (GSourceFunc) cb_timeout, NULL);
+ g_print ("Enter mainloop\n");
+ gst_main ();
+ g_source_remove (id);
+
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+ gst_object_unref (GST_OBJECT (pipeline));
+
+ g_print ("Done with reproduce-bug-123775=%s\n", bug ? "true" : "false");
+ bug = !bug;
+ }
+
+ return 0;
+}
bins bytestream caps cleanup clock \
$(GST_DEBUG_DIRS) \
dlopen dynparams \
- elements ghostpads indexers negotiation \
+ elements ghostpads indexers negotiation pad \
$(GST_PARSE_DIRS) \
plugin refcounting schedulers states tags threads
bins bytestream caps cleanup clock \
debug \
dlopen dynparams \
- elements ghostpads indexers negotiation \
+ elements ghostpads indexers negotiation pad \
parse \
plugin refcounting schedulers states tags threads
fp->sinkpad =
GST_FILE_PAD (gst_file_pad_new (gst_static_pad_template_get (&template),
- "src"));
+ "sink"));
gst_file_pad_set_iterate_function (fp->sinkpad, do_tests);
gst_element_add_pad (GST_ELEMENT (fp), GST_PAD (fp->sinkpad));
}
--- /dev/null
+include ../Rules
+
+tests_pass = link
+tests_fail =
+tests_ignore = chainnopull getnopush
--- /dev/null
+/*
+ * this tests that chain-based pads don't pull.
+ */
+
+#include <gst/gst.h>
+
+typedef struct _GstTestSink
+{
+ GstElement parent;
+ GstPad *sinkpad;
+} GstTestSink;
+
+typedef GstElementClass GstTestSinkClass;
+
+static void
+gst_test_sink_class_init (GstTestSinkClass * klass)
+{
+}
+
+static void
+gst_test_sink_base_init (gpointer klass)
+{
+}
+
+static void
+gst_test_sink_chain (GstPad * pad, GstData * data)
+{
+ data = gst_pad_pull (pad);
+}
+
+static void
+gst_test_sink_init (GstTestSink * sink)
+{
+ sink->sinkpad = gst_pad_new ("sink", GST_PAD_SINK);
+ gst_pad_set_chain_function (sink->sinkpad, gst_test_sink_chain);
+ gst_element_add_pad (GST_ELEMENT (sink), sink->sinkpad);
+}
+
+GST_BOILERPLATE (GstTestSink, gst_test_sink, GstElement, GST_TYPE_ELEMENT);
+
+int
+main (int argc, char *argv[])
+{
+ GstElement *pipeline, *fakesrc, *testsink;
+ gint n;
+
+ gst_init (&argc, &argv);
+
+ pipeline = gst_pipeline_new ("p");
+ fakesrc = gst_element_factory_make ("fakesrc", "src");
+ testsink = g_object_new (gst_test_sink_get_type (), NULL);
+ gst_object_set_name (GST_OBJECT (testsink), "sink");
+ gst_bin_add_many (GST_BIN (pipeline), fakesrc, testsink, NULL);
+ gst_element_link (fakesrc, testsink);
+ gst_element_set_state (pipeline, GST_STATE_PLAYING);
+
+ for (n = 0; n < 100; n++) {
+ if (!gst_bin_iterate (GST_BIN (pipeline)))
+ break;
+ }
+
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+ gst_object_unref (GST_OBJECT (pipeline));
+
+ return 0;
+}
--- /dev/null
+/*
+ * this tests that get-based pads don't push.
+ */
+
+#include <gst/gst.h>
+
+typedef struct _GstTestSrc
+{
+ GstElement parent;
+ GstPad *srcpad;
+} GstTestSrc;
+
+typedef GstElementClass GstTestSrcClass;
+
+static void
+gst_test_src_class_init (GstTestSrcClass * klass)
+{
+}
+static void
+gst_test_src_base_init (gpointer klass)
+{
+}
+
+static GstData *
+gst_test_src_get (GstPad * pad)
+{
+ GstEvent *event;
+
+ event = gst_event_new (GST_EVENT_INTERRUPT);
+ gst_event_ref (event);
+ gst_pad_push (pad, GST_DATA (event));
+
+ return GST_DATA (event);
+}
+
+static void
+gst_test_src_init (GstTestSrc * src)
+{
+ src->srcpad = gst_pad_new ("src", GST_PAD_SRC);
+ gst_pad_set_get_function (src->srcpad, gst_test_src_get);
+ gst_element_add_pad (GST_ELEMENT (src), src->srcpad);
+}
+
+GST_BOILERPLATE (GstTestSrc, gst_test_src, GstElement, GST_TYPE_ELEMENT);
+
+int
+main (int argc, char *argv[])
+{
+ GstElement *pipeline, *testsrc, *fakesink;
+ gint n;
+
+ gst_init (&argc, &argv);
+
+ pipeline = gst_pipeline_new ("p");
+ testsrc = g_object_new (gst_test_src_get_type (), NULL);
+ gst_object_set_name (GST_OBJECT (testsrc), "src");
+ fakesink = gst_element_factory_make ("fakesink", "sink");
+ gst_bin_add_many (GST_BIN (pipeline), testsrc, fakesink, NULL);
+ gst_element_link (testsrc, fakesink);
+ gst_element_set_state (pipeline, GST_STATE_PLAYING);
+
+ for (n = 0; n < 100; n++) {
+ if (!gst_bin_iterate (GST_BIN (pipeline)))
+ break;
+ }
+
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+ gst_object_unref (GST_OBJECT (pipeline));
+
+ return 0;
+}
--- /dev/null
+/*
+ * Test that:
+ * - get-based sources can return data, loop-based sources can push.
+ * - chain-based filters receive/push, loop-based filters can pull/push.
+ * - chain-based sinks receive, loop-based sinks pull.
+ */
+
+#include <gst/gst.h>
+
+/*
+ * Scary type code.
+ */
+
+typedef struct _GstTestElement
+{
+ GstElement parent;
+ GstPad *srcpad, *sinkpad;
+} GstTestSrc, GstTestFilter, GstTestSink, GstTestElement;
+
+typedef GstElementClass GstTestSrcClass, GstTestFilterClass, GstTestSinkClass,
+ GstTestElementClass;
+
+#define gst_test_src_class_init gst_test_element_class_init
+#define gst_test_filter_class_init gst_test_element_class_init
+#define gst_test_sink_class_init gst_test_element_class_init
+
+#define gst_test_src_base_init gst_test_element_base_init
+#define gst_test_filter_base_init gst_test_element_base_init
+#define gst_test_sink_base_init gst_test_element_base_init
+
+static void
+gst_test_element_class_init (GstTestElementClass * klass)
+{
+}
+static void
+gst_test_element_base_init (gpointer klass)
+{
+}
+
+/*
+ * Actual element code.
+ */
+
+gboolean loop = FALSE;
+
+static GstData *
+gst_test_src_get (GstPad * pad)
+{
+ return GST_DATA (gst_event_new (GST_EVENT_INTERRUPT));
+}
+
+static void
+gst_test_src_loop (GstElement * element)
+{
+ GstTestSrc *src = (GstTestElement *) element;
+
+ gst_pad_push (src->srcpad, gst_test_src_get (src->srcpad));
+}
+
+static void
+gst_test_src_init (GstTestElement * src)
+{
+ src->srcpad = gst_pad_new ("src", GST_PAD_SRC);
+ if (loop) {
+ gst_element_set_loop_function (GST_ELEMENT (src), gst_test_src_loop);
+ } else {
+ gst_pad_set_get_function (src->srcpad, gst_test_src_get);
+ }
+ gst_element_add_pad (GST_ELEMENT (src), src->srcpad);
+
+ GST_FLAG_SET (src, GST_ELEMENT_EVENT_AWARE);
+}
+
+static void
+gst_test_filter_chain (GstPad * pad, GstData * data)
+{
+ GstTestFilter *filter = (GstTestElement *) gst_pad_get_parent (pad);
+
+ gst_pad_push (filter->srcpad, data);
+}
+
+static void
+gst_test_filter_loop (GstElement * element)
+{
+ GstTestFilter *filter = (GstTestElement *) element;
+
+ gst_test_filter_chain (filter->sinkpad, gst_pad_pull (filter->sinkpad));
+}
+
+static void
+gst_test_filter_init (GstTestElement * filter)
+{
+ filter->sinkpad = gst_pad_new ("sink", GST_PAD_SINK);
+ if (loop) {
+ gst_element_set_loop_function (GST_ELEMENT (filter), gst_test_filter_loop);
+ } else {
+ gst_pad_set_chain_function (filter->sinkpad, gst_test_filter_chain);
+ }
+ gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
+
+ filter->srcpad = gst_pad_new ("src", GST_PAD_SRC);
+ gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
+
+ GST_FLAG_SET (filter, GST_ELEMENT_EVENT_AWARE);
+}
+
+static void
+gst_test_sink_chain (GstPad * pad, GstData * data)
+{
+ gst_data_unref (data);
+}
+
+static void
+gst_test_sink_loop (GstElement * element)
+{
+ GstTestSink *sink = (GstTestElement *) element;
+
+ gst_test_sink_chain (sink->sinkpad, gst_pad_pull (sink->sinkpad));
+}
+
+static void
+gst_test_sink_init (GstTestElement * sink)
+{
+ sink->sinkpad = gst_pad_new ("sink", GST_PAD_SINK);
+ if (loop) {
+ gst_element_set_loop_function (GST_ELEMENT (sink), gst_test_sink_loop);
+ } else {
+ gst_pad_set_chain_function (sink->sinkpad, gst_test_sink_chain);
+ }
+ gst_element_add_pad (GST_ELEMENT (sink), sink->sinkpad);
+
+ GST_FLAG_SET (sink, GST_ELEMENT_EVENT_AWARE);
+}
+
+#define parent_class src_parent_class
+GST_BOILERPLATE (GstTestSrc, gst_test_src, GstElement, GST_TYPE_ELEMENT);
+#undef parent_class
+#define parent_class filter_parent_class
+GST_BOILERPLATE (GstTestFilter, gst_test_filter, GstElement, GST_TYPE_ELEMENT);
+#undef parent_class
+#define parent_class sink_parent_class
+GST_BOILERPLATE (GstTestSink, gst_test_sink, GstElement, GST_TYPE_ELEMENT);
+#undef parent_class
+
+/*
+ * Actual test.
+ */
+
+static void
+cb_error (GstElement * element)
+{
+ g_assert_not_reached ();
+}
+
+int
+main (int argc, char *argv[])
+{
+ GstElement *pipeline, *src, *filter, *sink;
+ gint n, r;
+ gboolean res;
+
+ gst_init (&argc, &argv);
+
+ for (r = 0; r < 2; r++) {
+ pipeline = gst_pipeline_new ("p");
+ g_signal_connect (pipeline, "error", G_CALLBACK (cb_error), NULL);
+ src = g_object_new (gst_test_src_get_type (), NULL);
+ gst_object_set_name (GST_OBJECT (src), "src");
+ filter = g_object_new (gst_test_filter_get_type (), NULL);
+ gst_object_set_name (GST_OBJECT (filter), "filter");
+ sink = g_object_new (gst_test_sink_get_type (), NULL);
+ gst_object_set_name (GST_OBJECT (sink), "sink");
+ gst_bin_add_many (GST_BIN (pipeline), src, filter, sink, NULL);
+ res = gst_element_link (src, filter);
+ g_assert (res);
+ res = gst_element_link (filter, sink);
+ g_assert (res);
+ gst_element_set_state (pipeline, GST_STATE_PLAYING);
+
+ for (n = 0; n < 100; n++) {
+ if (!gst_bin_iterate (GST_BIN (pipeline)))
+ g_assert_not_reached ();
+ }
+
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+ gst_object_unref (GST_OBJECT (pipeline));
+
+ /* switch element types */
+ g_print ("Loop=%s done\n", loop ? "true" : "false");
+ loop = !loop;
+ }
+
+ return 0;
+}
include ../Rules
-tests_pass = thread1 thread2 thread3 thread4 thread5 threade threadf signal1 159852
+tests_pass = thread1 thread2 thread3 thread4 thread5 threade threadf signal1 159852 threadg threadi
tests_fail = 159566 signal2 signal3
# threadh
-tests_ignore = queue threadb threadc threadd staticrec threadg
+tests_ignore = queue threadb threadc threadd staticrec
thread1_SOURCES = thread.c
thread1_CFLAGS = -DTESTNUM=1 $(AM_CFLAGS)
gst_element_set_state (pipeline, GST_STATE_PLAYING);
g_print ("running ...\n");
while (gst_bin_iterate (GST_BIN (pipeline)));
+ g_print ("done ...\n");
gst_element_set_state (pipeline, GST_STATE_NULL);
}
if (TESTNUM == 4) {
--- /dev/null
+/*
+ * Test two ways of going non-lineairly to PLAYING. Both tests have a thread
+ * containing a fakesrc/sink and a containing thread.
+ *
+ * Test 1 tests by adding fakesrc, putting thread to PLAYING, adding
+ * fakesink, syncing state and see if it iterates.
+ *
+ * Test2 tests by adding fakesrc/fakesink, setting fakesrc to PLAYING
+ * (which should increment the container state) and then synchronizing
+ * state. This reflects bug #123775.
+ */
+
+#include <gst/gst.h>
+
+static GstElement *pipeline, *fakesrc, *fakesink;
+gboolean bug = FALSE;
+
+static gboolean
+cb_timeout (gpointer data)
+{
+ g_assert_not_reached ();
+
+ return FALSE;
+}
+
+static gboolean
+cb_quit (gpointer data)
+{
+ gst_main_quit ();
+
+ g_print ("Quit mainloop\n");
+
+ /* once */
+ return FALSE;
+}
+
+static void
+cb_eos (gpointer data)
+{
+ g_print ("Received EOS\n");
+
+ g_idle_add ((GSourceFunc) cb_quit, NULL);
+}
+
+static void
+cb_state (GstElement * element, GstElementState old_state,
+ GstElementState new_state, gpointer data)
+{
+ g_print ("Changed state from %d to %d\n", old_state, new_state);
+}
+
+static gboolean
+cb_play (gpointer data)
+{
+ GstElementStateReturn res;
+
+ if (bug) {
+ g_print ("Setting state\n");
+ gst_element_set_state (fakesrc, GST_STATE_PLAYING);
+ g_print ("Done\n");
+ } else {
+ gst_element_set_state (pipeline, GST_STATE_PLAYING);
+ gst_bin_add (GST_BIN (pipeline), fakesink);
+ }
+ g_print ("Syncing state\n");
+ res = gst_bin_sync_children_state (GST_BIN (data));
+ g_assert (res == GST_STATE_SUCCESS);
+
+ g_print ("Set to playing correctly: %d\n", GST_STATE (pipeline));
+
+ /* once */
+ return FALSE;
+}
+
+gint
+main (gint argc, gchar * argv[])
+{
+ gint n, id;
+
+ gst_init (&argc, &argv);
+
+ for (n = 0; n < 2; n++) {
+ pipeline = gst_thread_new ("p");
+ g_signal_connect (pipeline, "state-change", G_CALLBACK (cb_state), NULL);
+ fakesrc = gst_element_factory_make ("fakesrc", "src");
+ g_object_set (G_OBJECT (fakesrc), "num-buffers", 1, NULL);
+ fakesink = gst_element_factory_make ("fakesink", "sink");
+ if (bug) {
+ gst_bin_add_many (GST_BIN (pipeline), fakesrc, fakesink, NULL);
+ } else {
+ gst_bin_add (GST_BIN (pipeline), fakesrc);
+ }
+ gst_element_link (fakesrc, fakesink);
+ g_signal_connect (pipeline, "eos", G_CALLBACK (cb_eos), NULL);
+ g_idle_add ((GSourceFunc) cb_play, pipeline);
+
+ /* give 5 seconds */
+ id = g_timeout_add (5000, (GSourceFunc) cb_timeout, NULL);
+ g_print ("Enter mainloop\n");
+ gst_main ();
+ g_source_remove (id);
+
+ gst_element_set_state (pipeline, GST_STATE_NULL);
+ gst_object_unref (GST_OBJECT (pipeline));
+
+ g_print ("Done with reproduce-bug-123775=%s\n", bug ? "true" : "false");
+ bug = !bug;
+ }
+
+ return 0;
+}