docs/pwg/advanced-types.xml: Fix description for buffer-frames=0.
authorRonald S. Bultje <rbultje@ronald.bitfreak.net>
Mon, 31 Jan 2005 15:51:19 +0000 (15:51 +0000)
committerRonald S. Bultje <rbultje@ronald.bitfreak.net>
Mon, 31 Jan 2005 15:51:19 +0000 (15:51 +0000)
Original commit message from CVS:
* 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.

27 files changed:
ChangeLog
configure.ac
docs/gst/tmpl/gstbin.sgml
docs/pwg/advanced-types.xml
gst/gstbin.c
gst/gstbin.h
gst/gstelement.c
gst/gstscheduler.c
gst/gstthread.c
tests/old/testsuite/Makefile.am
tests/old/testsuite/bytestream/filepadsink.c
tests/old/testsuite/pad/Makefile.am [new file with mode: 0644]
tests/old/testsuite/pad/chainnopull.c [new file with mode: 0644]
tests/old/testsuite/pad/getnopush.c [new file with mode: 0644]
tests/old/testsuite/pad/link.c [new file with mode: 0644]
tests/old/testsuite/threads/Makefile.am
tests/old/testsuite/threads/thread.c
tests/old/testsuite/threads/threadi.c [new file with mode: 0644]
testsuite/Makefile.am
testsuite/bytestream/filepadsink.c
testsuite/pad/Makefile.am [new file with mode: 0644]
testsuite/pad/chainnopull.c [new file with mode: 0644]
testsuite/pad/getnopush.c [new file with mode: 0644]
testsuite/pad/link.c [new file with mode: 0644]
testsuite/threads/Makefile.am
testsuite/threads/thread.c
testsuite/threads/threadi.c [new file with mode: 0644]

index 3d2d788..f7ae32a 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,50 @@
+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):
index cd17138..4ada7b3 100644 (file)
@@ -684,6 +684,7 @@ testsuite/elements/Makefile
 testsuite/ghostpads/Makefile
 testsuite/indexers/Makefile
 testsuite/negotiation/Makefile
+testsuite/pad/Makefile
 testsuite/parse/Makefile
 testsuite/plugin/Makefile
 testsuite/refcounting/Makefile
index 69fe1c0..933d011 100644 (file)
@@ -98,6 +98,7 @@ FALSE.
 @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 ##### -->
index 40e9f88..98c6090 100644 (file)
@@ -334,14 +334,16 @@ plugin_init (GstPlugin *plugin)
           <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>
 
index 6975432..eaa07e1 100644 (file)
@@ -705,10 +705,14 @@ gst_bin_child_state_change_func (GstBin * bin, GstElementState oldstate,
   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));
@@ -832,7 +836,6 @@ gst_bin_change_state (GstElement * element)
   GstBin *bin;
   GstElementStateReturn ret;
   GstElementState old_state, pending;
-  SetKidStateData data;
 
   g_return_val_if_fail (GST_IS_BIN (element), GST_STATE_FAILURE);
 
@@ -849,27 +852,43 @@ gst_bin_change_state (GstElement * element)
   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;
 }
@@ -900,9 +919,13 @@ gst_bin_change_state_norecurse (GstBin * bin)
 {
   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
index 85ecd71..a7333e6 100644 (file)
@@ -39,20 +39,30 @@ GST_EXPORT GType _gst_bin_type;
 
 /**
  * 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;
 
index 604154a..cec465a 100644 (file)
@@ -398,8 +398,9 @@ gst_element_disable_threadsafe_properties (GstElement * element)
   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 */
 }
 
index b74bc9e..1bc7c15 100644 (file)
@@ -98,6 +98,12 @@ gst_scheduler_dispose (GObject * object)
   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);
 }
 
index a3c5d91..fe7849a 100644 (file)
@@ -508,24 +508,10 @@ gst_thread_change_state (GstElement * element)
       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");
@@ -548,6 +534,17 @@ gst_thread_change_state (GstElement * element)
   } 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);
 
@@ -595,6 +592,13 @@ gst_thread_child_state_change (GstBin * bin, GstElementState oldstate,
   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");
index 0c14538..54507be 100644 (file)
@@ -17,7 +17,7 @@ SUBDIRS = \
        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
 
@@ -25,7 +25,7 @@ DIST_SUBDIRS = \
        bins bytestream caps cleanup clock \
        debug \
        dlopen dynparams \
-       elements ghostpads indexers negotiation \
+       elements ghostpads indexers negotiation pad \
        parse \
        plugin refcounting schedulers states tags threads
 
index f25d47d..e5ac8b9 100644 (file)
@@ -84,7 +84,7 @@ gst_fp_sink_init (GstFpSink * fp)
 
   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));
 }
diff --git a/tests/old/testsuite/pad/Makefile.am b/tests/old/testsuite/pad/Makefile.am
new file mode 100644 (file)
index 0000000..b2f7931
--- /dev/null
@@ -0,0 +1,5 @@
+include ../Rules
+
+tests_pass = link
+tests_fail =
+tests_ignore = chainnopull getnopush
diff --git a/tests/old/testsuite/pad/chainnopull.c b/tests/old/testsuite/pad/chainnopull.c
new file mode 100644 (file)
index 0000000..ed83dfc
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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;
+}
diff --git a/tests/old/testsuite/pad/getnopush.c b/tests/old/testsuite/pad/getnopush.c
new file mode 100644 (file)
index 0000000..7041c90
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * 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;
+}
diff --git a/tests/old/testsuite/pad/link.c b/tests/old/testsuite/pad/link.c
new file mode 100644 (file)
index 0000000..5d22c38
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * 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;
+}
index c4cfa9c..3d0f5c9 100644 (file)
@@ -1,11 +1,11 @@
 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)
index 40eac8d..4614c41 100644 (file)
@@ -95,6 +95,7 @@ main (gint argc, gchar * argv[])
     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) {
diff --git a/tests/old/testsuite/threads/threadi.c b/tests/old/testsuite/threads/threadi.c
new file mode 100644 (file)
index 0000000..e935104
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * 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;
+}
index 0c14538..54507be 100644 (file)
@@ -17,7 +17,7 @@ SUBDIRS = \
        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
 
@@ -25,7 +25,7 @@ DIST_SUBDIRS = \
        bins bytestream caps cleanup clock \
        debug \
        dlopen dynparams \
-       elements ghostpads indexers negotiation \
+       elements ghostpads indexers negotiation pad \
        parse \
        plugin refcounting schedulers states tags threads
 
index f25d47d..e5ac8b9 100644 (file)
@@ -84,7 +84,7 @@ gst_fp_sink_init (GstFpSink * fp)
 
   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));
 }
diff --git a/testsuite/pad/Makefile.am b/testsuite/pad/Makefile.am
new file mode 100644 (file)
index 0000000..b2f7931
--- /dev/null
@@ -0,0 +1,5 @@
+include ../Rules
+
+tests_pass = link
+tests_fail =
+tests_ignore = chainnopull getnopush
diff --git a/testsuite/pad/chainnopull.c b/testsuite/pad/chainnopull.c
new file mode 100644 (file)
index 0000000..ed83dfc
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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;
+}
diff --git a/testsuite/pad/getnopush.c b/testsuite/pad/getnopush.c
new file mode 100644 (file)
index 0000000..7041c90
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * 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;
+}
diff --git a/testsuite/pad/link.c b/testsuite/pad/link.c
new file mode 100644 (file)
index 0000000..5d22c38
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * 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;
+}
index c4cfa9c..3d0f5c9 100644 (file)
@@ -1,11 +1,11 @@
 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)
index 40eac8d..4614c41 100644 (file)
@@ -95,6 +95,7 @@ main (gint argc, gchar * argv[])
     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) {
diff --git a/testsuite/threads/threadi.c b/testsuite/threads/threadi.c
new file mode 100644 (file)
index 0000000..e935104
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * 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;
+}