A little example program to show how trigger-based elements can work.
authorDavid Schleef <ds@schleef.org>
Wed, 30 Mar 2005 03:57:39 +0000 (03:57 +0000)
committerDavid Schleef <ds@schleef.org>
Wed, 30 Mar 2005 03:57:39 +0000 (03:57 +0000)
Original commit message from CVS:
* configure.ac:
* testsuite/trigger/Makefile.am:
* testsuite/trigger/trigger.c: A little example program to show
how trigger-based elements can work.

ChangeLog
configure.ac
tests/old/testsuite/trigger/Makefile.am [new file with mode: 0644]
tests/old/testsuite/trigger/trigger.c [new file with mode: 0644]
testsuite/trigger/Makefile.am [new file with mode: 0644]
testsuite/trigger/trigger.c [new file with mode: 0644]

index 040875e..389d09c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2005-03-29  David Schleef  <ds@schleef.org>
+
+       * configure.ac:
+       * testsuite/trigger/Makefile.am:
+       * testsuite/trigger/trigger.c: A little example program to show
+       how trigger-based elements can work.
+
 2005-03-29  Wim Taymans  <wim@fluendo.com>
 
        * gst/base/Makefile.am:
index e5ca119..7855bbd 100644 (file)
@@ -697,6 +697,7 @@ testsuite/schedulers/Makefile
 testsuite/states/Makefile
 testsuite/tags/Makefile
 testsuite/threads/Makefile
+testsuite/trigger/Makefile
 examples/Makefile
 examples/cutter/Makefile
 examples/helloworld/Makefile
diff --git a/tests/old/testsuite/trigger/Makefile.am b/tests/old/testsuite/trigger/Makefile.am
new file mode 100644 (file)
index 0000000..ef85850
--- /dev/null
@@ -0,0 +1,9 @@
+
+
+noinst_PROGRAMS = trigger
+
+trigger_SOURCES = trigger.c
+trigger_CFLAGS = $(GST_OBJ_CFLAGS)
+trigger_LDADD = $(GST_OBJS_LIBS) $(GLIB_LIBS)
+
+
diff --git a/tests/old/testsuite/trigger/trigger.c b/tests/old/testsuite/trigger/trigger.c
new file mode 100644 (file)
index 0000000..dba91bf
--- /dev/null
@@ -0,0 +1,856 @@
+
+#include <glib.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <gst/gst.h>
+
+#define INFINITY 1e10
+
+typedef struct _Element Element;
+typedef struct _Feature Feature;
+typedef struct _ElementInfo ElementInfo;
+
+enum
+{
+  PAD_SRC,
+  PAD_SINK,
+  FD,
+  TIME
+};
+
+struct _Element
+{
+  char *name;
+  GList *features;
+  int type;
+  gboolean init;
+  gboolean idle;
+
+  void (*iterate) (Element * element);
+  void (*caps_iterate) (Element * element);
+
+  int state;
+};
+
+struct _Feature
+{
+  char *name;
+  int type;
+
+  Element *parent;
+
+  gboolean waiting;
+  gboolean ready;
+
+  /* for pads */
+  gboolean bufpen;
+  Feature *peer;
+  GstCaps *caps;
+
+  /* for fds */
+  double next_time;
+  double interval;
+};
+
+struct _ElementInfo
+{
+  char *type_name;
+  void (*iterate) (Element * element);
+  void (*caps_iterate) (Element * element);
+};
+
+GList *elements;
+
+void run (void);
+void dump (void);
+void dump_element (Element * e);
+
+Element *element_factory_make (const char *type);
+Element *element_factory_make_full (const char *type, const char *name);
+void element_link (const char *name1, const char *name2);
+void element_link_full (const char *name1, const char *padname1,
+    const char *name2, const char *padname2);
+Element *element_get (const char *name);
+gboolean element_ready (Element * e);
+gboolean feature_is_ready (Feature * f);
+double element_next_time (Element * e);
+
+Feature *feature_create (Element * e, int type, const char *name);
+Feature *feature_get (Element * e, const char *name);
+gboolean feature_ready (Element * e, const char *name);
+
+void fakesrc_iterate (Element * element);
+void identity_iterate (Element * element);
+void fakesink_iterate (Element * element);
+void audiosink_iterate (Element * element);
+void mad_iterate (Element * element);
+void queue_iterate (Element * element);
+void videosink_iterate (Element * element);
+void tee_iterate (Element * element);
+
+void fakesrc_caps (Element * element);
+void identity_caps (Element * element);
+void fakesink_caps (Element * element);
+void audiosink_caps (Element * element);
+void mad_caps (Element * element);
+void queue_caps (Element * element);
+void videosink_caps (Element * element);
+void tee_caps (Element * element);
+
+double time;
+
+int
+main (int argc, char *argv[])
+{
+  int x = 8;
+
+  switch (x) {
+    case 0:
+      /* fakesrc ! fakesink */
+      element_factory_make ("fakesrc");
+      element_factory_make ("fakesink");
+      element_link ("fakesrc", "fakesink");
+      break;
+    case 1:
+      /* fakesrc ! identity ! fakesink */
+      element_factory_make ("fakesrc");
+      element_factory_make ("identity");
+      element_factory_make ("fakesink");
+
+      element_link ("fakesrc", "identity");
+      element_link ("identity", "fakesink");
+      break;
+    case 2:
+      /* fakesrc ! identity ! identity ! fakesink */
+      element_factory_make ("fakesrc");
+      element_factory_make_full ("identity", "identity0");
+      element_factory_make_full ("identity", "identity1");
+      element_factory_make ("fakesink");
+
+      element_link ("fakesrc", "identity0");
+      element_link ("identity0", "identity1");
+      element_link ("identity1", "fakesink");
+      break;
+    case 3:
+      /* fakesrc ! audiosink */
+      element_factory_make ("fakesrc");
+      element_factory_make ("audiosink");
+
+      element_link ("fakesrc", "audiosink");
+      break;
+    case 4:
+      /* fakesrc ! mad ! fakesink */
+      element_factory_make ("fakesrc");
+      element_factory_make ("mad");
+      element_factory_make ("fakesink");
+
+      element_link ("fakesrc", "mad");
+      element_link ("mad", "fakesink");
+      break;
+    case 5:
+      /* fakesrc ! queue ! fakesink */
+      element_factory_make ("fakesrc");
+      element_factory_make ("queue");
+      element_factory_make ("fakesink");
+
+      element_link ("fakesrc", "queue");
+      element_link ("queue", "fakesink");
+      break;
+    case 6:
+      /* fakesrc ! queue ! audiosink */
+      element_factory_make ("fakesrc");
+      element_factory_make ("queue");
+      element_factory_make ("audiosink");
+
+      element_link ("fakesrc", "queue");
+      element_link ("queue", "audiosink");
+      break;
+    case 7:
+      /* fakesrc ! videosink */
+      element_factory_make ("fakesrc");
+      element_factory_make ("videosink");
+
+      element_link ("fakesrc", "videosink");
+      break;
+    case 8:
+      /* fakesrc ! tee ! videosink tee0.src2 ! videosink */
+      element_factory_make ("fakesrc");
+      element_factory_make ("tee");
+      element_factory_make_full ("videosink", "vs0");
+      element_factory_make_full ("videosink", "vs1");
+
+      element_link ("fakesrc", "tee");
+      element_link_full ("tee", "src1", "vs0", "sink");
+      element_link_full ("tee", "src2", "vs1", "sink");
+      break;
+  }
+
+  run ();
+
+  return 0;
+}
+
+void
+run (void)
+{
+  int iter = 0;
+
+  while (iter < 20) {
+    Element *e;
+    GList *l;
+    gboolean did_something = FALSE;
+    double ent = INFINITY;
+
+    g_print ("iteration %d time %g\n", iter, time);
+    for (l = g_list_first (elements); l; l = g_list_next (l)) {
+      double nt;
+
+      e = l->data;
+      if (element_ready (e)) {
+        g_print ("%s: is ready, iterating\n", e->name);
+        e->iterate (e);
+        did_something = TRUE;
+      } else {
+        g_print ("%s: is not ready\n", e->name);
+      }
+      nt = element_next_time (e);
+      if (nt < ent) {
+        ent = nt;
+      }
+    }
+    if (did_something == FALSE) {
+      if (ent < INFINITY) {
+        g_print ("nothing to do, waiting for %g\n", ent);
+        time = ent;
+      } else {
+        g_print ("ERROR: deadlock\n");
+        exit (1);
+      }
+    }
+    iter++;
+  }
+
+}
+
+void
+dump (void)
+{
+  Element *e;
+  GList *l;
+
+  for (l = g_list_first (elements); l; l = g_list_next (l)) {
+    e = l->data;
+    dump_element (e);
+  }
+}
+
+void
+dump_element (Element * e)
+{
+  Feature *f;
+  GList *m;
+
+  g_print ("%s:\n", e->name);
+  for (m = g_list_first (e->features); m; m = g_list_next (m)) {
+    f = m->data;
+    g_print ("  %s:\n", f->name);
+    g_print ("    type %d\n", f->type);
+    g_print ("    ready %d\n", f->ready);
+    g_print ("    waiting %d\n", f->waiting);
+
+  }
+}
+
+/* Element */
+
+const ElementInfo element_types[] = {
+  {"fakesrc", fakesrc_iterate},
+  {"identity", identity_iterate},
+  {"fakesink", fakesink_iterate},
+  {"audiosink", audiosink_iterate},
+  {"mad", mad_iterate},
+  {"queue", queue_iterate},
+  {"videosink", videosink_iterate},
+  {"tee", tee_iterate},
+  {NULL, NULL}
+};
+
+Element *
+element_factory_make (const char *type)
+{
+  return element_factory_make_full (type, type);
+}
+
+Element *
+element_factory_make_full (const char *type, const char *name)
+{
+  int i;
+  Element *e = g_new0 (Element, 1);
+
+  for (i = 0; element_types[i].type_name; i++) {
+    if (strcmp (type, element_types[i].type_name) == 0) {
+      e->type = i;
+      e->iterate = element_types[i].iterate;
+      e->caps_iterate = element_types[i].iterate;
+      e->iterate (e);
+      e->name = g_strdup (name);
+
+      elements = g_list_append (elements, e);
+
+      return e;
+    }
+  }
+
+  g_print ("ERROR: element type %s not found\n", type);
+  return NULL;
+}
+
+void
+element_link (const char *name1, const char *name2)
+{
+  element_link_full (name1, "src", name2, "sink");
+}
+
+void
+element_link_full (const char *name1, const char *padname1, const char *name2,
+    const char *padname2)
+{
+  Element *e1, *e2;
+  Feature *pad1, *pad2;
+
+  e1 = element_get (name1);
+  e2 = element_get (name2);
+
+  pad1 = feature_get (e1, padname1);
+  pad2 = feature_get (e2, padname2);
+
+  pad1->peer = pad2;
+  pad2->peer = pad1;
+
+}
+
+Element *
+element_get (const char *name)
+{
+  GList *l;
+
+  for (l = g_list_first (elements); l; l = g_list_next (l)) {
+    Element *e = l->data;
+
+    if (strcmp (name, e->name) == 0)
+      return e;
+  }
+
+  g_print ("ERROR: element_get(%s) element not found\n", name);
+  return NULL;
+}
+
+gboolean
+element_ready (Element * e)
+{
+  GList *l;
+
+  //dump_element(e);
+  if (e->idle)
+    return TRUE;
+  for (l = g_list_first (e->features); l; l = g_list_next (l)) {
+    Feature *f = l->data;
+
+    if (f->waiting && feature_is_ready (f)) {
+      g_print ("element %s is ready because feature %s is ready\n",
+          e->name, f->name);
+      return TRUE;
+    }
+  }
+  return FALSE;
+}
+
+double
+element_next_time (Element * e)
+{
+  GList *l;
+  double ent = INFINITY;
+
+  for (l = g_list_first (e->features); l; l = g_list_next (l)) {
+    Feature *f = l->data;
+
+    if (f->type == FD || f->type == TIME) {
+      if (f->next_time < ent) {
+        ent = f->next_time;
+      }
+    }
+  }
+  return ent;
+}
+
+/* Feature */
+
+Feature *
+feature_get (Element * e, const char *name)
+{
+  GList *l;
+
+  for (l = g_list_first (e->features); l; l = g_list_next (l)) {
+    Feature *f = l->data;
+
+    if (strcmp (name, f->name) == 0)
+      return f;
+  }
+
+  g_print ("ERROR: feature_get(%s): feature not found\n", name);
+  return NULL;
+}
+
+Feature *
+feature_create (Element * e, int type, const char *name)
+{
+  Feature *f = g_new0 (Feature, 1);
+
+  f->name = g_strdup (name);
+  f->type = type;
+
+  e->features = g_list_append (e->features, f);
+
+  return f;
+}
+
+void
+feature_wait (Element * e, const char *name, gboolean wait)
+{
+  Feature *f = feature_get (e, name);
+
+  f->waiting = wait;
+  switch (f->type) {
+    case PAD_SRC:
+      if (f->peer) {
+        f->peer->ready = wait && f->peer->bufpen;
+      }
+      break;
+    case PAD_SINK:
+      if (f->peer) {
+        f->peer->ready = wait && f->bufpen;
+      }
+      break;
+  }
+}
+
+gboolean
+feature_ready (Element * e, const char *name)
+{
+  Feature *f = feature_get (e, name);
+
+  return feature_is_ready (f);
+}
+
+gboolean
+feature_is_ready (Feature * f)
+{
+  switch (f->type) {
+    case PAD_SRC:
+      if (f->peer) {
+        return f->peer->waiting && !f->peer->bufpen;
+      }
+      break;
+    case PAD_SINK:
+      if (f->peer) {
+        return f->peer->waiting && f->bufpen;
+      }
+      break;
+    case FD:
+      g_print ("testing %g <= %g\n", f->next_time, time);
+      if (f->next_time <= time) {
+        return TRUE;
+      } else {
+        return FALSE;
+      }
+      break;
+    case TIME:
+      g_print ("testing %g <= %g\n", f->next_time, time);
+      if (f->next_time <= time) {
+        return TRUE;
+      } else {
+        return FALSE;
+      }
+      break;
+  }
+
+  return FALSE;
+}
+
+void
+pad_push (Element * e, const char *name)
+{
+  Feature *f = feature_get (e, name);
+
+  g_assert (f->type == PAD_SRC);
+
+  g_print ("pushing pad on %s:%s\n", e->name, name);
+  if (f->peer->bufpen) {
+    g_print ("ERROR: push when bufpen full link\n");
+    exit (0);
+  }
+  f->peer->bufpen = TRUE;
+  f->peer->ready = f->waiting;
+  f->ready = FALSE;
+}
+
+void
+pad_pull (Element * e, const char *name)
+{
+  Feature *f = feature_get (e, name);
+
+  g_assert (f->type == PAD_SINK);
+
+  g_print ("pulling pad on %s:%s\n", e->name, name);
+  if (!f->bufpen) {
+    g_print ("ERROR: pull when bufpen empty\n");
+    exit (0);
+  }
+  f->bufpen = FALSE;
+  f->ready = FALSE;
+  f->peer->ready = f->waiting;
+}
+
+void
+fd_push (Element * e, const char *name)
+{
+  Feature *f = feature_get (e, name);
+
+  g_assert (f->type == FD);
+
+  g_print ("pushing to fd %s:%s\n", e->name, name);
+  if (time < f->next_time) {
+    g_print ("ERROR: push too early\n");
+    exit (0);
+  }
+  f->next_time += f->interval;
+}
+
+void
+fd_start (Element * e, const char *name, double interval)
+{
+  Feature *f = feature_get (e, name);
+
+  g_assert (f->type == FD);
+
+  f->interval = interval;
+  f->next_time = f->interval;
+}
+
+void
+time_start (Element * e, const char *name, double interval)
+{
+  Feature *f = feature_get (e, name);
+
+  g_assert (f->type == TIME);
+
+  f->interval = interval;
+  f->next_time = f->interval;
+}
+
+void
+time_increment (Element * e, const char *name, double interval)
+{
+  Feature *f = feature_get (e, name);
+
+  g_assert (f->type == TIME);
+
+  f->interval = interval;
+  f->next_time += f->interval;
+}
+
+/* elements */
+
+void
+fakesrc_iterate (Element * element)
+{
+  //Event *event;
+
+  if (!element->init) {
+    feature_create (element, PAD_SRC, "src");
+
+    feature_wait (element, "src", TRUE);
+
+    element->init = TRUE;
+    return;
+  }
+
+  pad_push (element, "src");
+}
+
+void
+identity_iterate (Element * element)
+{
+  if (!element->init) {
+    feature_create (element, PAD_SINK, "sink");
+    feature_create (element, PAD_SRC, "src");
+
+    feature_wait (element, "sink", FALSE);
+    feature_wait (element, "src", TRUE);
+
+    element->init = TRUE;
+    return;
+  }
+
+  if (feature_ready (element, "sink") && feature_ready (element, "src")) {
+    pad_pull (element, "sink");
+    pad_push (element, "src");
+    feature_wait (element, "sink", FALSE);
+    feature_wait (element, "src", TRUE);
+  } else {
+    if (feature_ready (element, "sink")) {
+      g_print ("ERROR: assert not reached\n");
+      feature_wait (element, "src", TRUE);
+      feature_wait (element, "sink", FALSE);
+    }
+    if (feature_ready (element, "src")) {
+      feature_wait (element, "src", FALSE);
+      feature_wait (element, "sink", TRUE);
+    }
+  }
+}
+
+void
+fakesink_iterate (Element * element)
+{
+  if (!element->init) {
+    feature_create (element, PAD_SINK, "sink");
+
+    element->idle = TRUE;
+    element->init = TRUE;
+    return;
+  }
+
+  if (feature_ready (element, "sink")) {
+    pad_pull (element, "sink");
+    g_print ("FAKESINK\n");
+  } else {
+    feature_wait (element, "sink", TRUE);
+    element->idle = FALSE;
+  }
+
+}
+
+void
+audiosink_iterate (Element * element)
+{
+  if (!element->init) {
+    Feature *f;
+
+    feature_create (element, PAD_SINK, "sink");
+    f = feature_create (element, FD, "fd");
+    fd_start (element, "fd", 1024 / 44100.0);
+
+    feature_wait (element, "fd", TRUE);
+
+    element->init = TRUE;
+    return;
+  }
+
+  if (feature_ready (element, "fd")) {
+    if (feature_ready (element, "sink")) {
+      pad_pull (element, "sink");
+      fd_push (element, "fd");
+      g_print ("AUDIOSINK\n");
+      feature_wait (element, "fd", TRUE);
+      feature_wait (element, "sink", FALSE);
+    } else {
+      feature_wait (element, "fd", FALSE);
+      feature_wait (element, "sink", TRUE);
+    }
+  } else {
+    g_print ("ERROR: assert not reached\n");
+
+    feature_wait (element, "sink", FALSE);
+    feature_wait (element, "fd", TRUE);
+  }
+
+}
+
+void
+mad_iterate (Element * element)
+{
+  if (!element->init) {
+    feature_create (element, PAD_SINK, "sink");
+    feature_create (element, PAD_SRC, "src");
+
+    element->state = 0;
+    feature_wait (element, "sink", FALSE);
+    feature_wait (element, "src", TRUE);
+
+    element->init = TRUE;
+    return;
+  }
+
+  if (element->state > 0) {
+    if (feature_ready (element, "src")) {
+      pad_push (element, "src");
+      element->state--;
+      if (element->state > 0) {
+        feature_wait (element, "sink", FALSE);
+        feature_wait (element, "src", TRUE);
+      } else {
+        feature_wait (element, "sink", FALSE);
+        feature_wait (element, "src", TRUE);
+      }
+    } else {
+      g_print ("ERROR: assert not reached\n");
+    }
+  } else {
+    if (feature_ready (element, "sink")) {
+      pad_pull (element, "sink");
+      element->state += 5;
+      pad_push (element, "src");
+      element->state--;
+      feature_wait (element, "sink", FALSE);
+      feature_wait (element, "src", TRUE);
+    } else {
+      feature_wait (element, "sink", TRUE);
+      feature_wait (element, "src", FALSE);
+    }
+  }
+}
+
+void
+queue_iterate (Element * element)
+{
+  if (!element->init) {
+    feature_create (element, PAD_SINK, "sink");
+    feature_create (element, PAD_SRC, "src");
+
+    element->state = 0;
+    feature_wait (element, "sink", FALSE);
+    feature_wait (element, "src", TRUE);
+
+    element->init = TRUE;
+    return;
+  }
+
+  if (feature_ready (element, "sink") && element->state < 5) {
+    pad_pull (element, "sink");
+    element->state++;
+  }
+  if (feature_ready (element, "src") && element->state > 0) {
+    pad_push (element, "src");
+    element->state--;
+  }
+
+  if (element->state < 5) {
+    feature_wait (element, "sink", TRUE);
+  } else {
+    feature_wait (element, "sink", FALSE);
+  }
+  if (element->state > 0) {
+    feature_wait (element, "src", TRUE);
+  } else {
+    feature_wait (element, "src", FALSE);
+  }
+}
+
+void
+demux_iterate (Element * element)
+{
+  if (!element->init) {
+    feature_create (element, PAD_SINK, "sink");
+    feature_create (element, PAD_SRC, "video_src");
+    feature_create (element, PAD_SRC, "audio_src");
+
+    feature_wait (element, "sink", TRUE);
+    feature_wait (element, "video_src", FALSE);
+    feature_wait (element, "audio_src", FALSE);
+
+    element->init = TRUE;
+    return;
+  }
+#if 0
+  /* demux waits for a buffer on the sinkpad, then queues buffers and
+   * eventually pushes them to sinkpads */
+  if (feautre_ready (element, "sink") &&) {
+
+  }
+#endif
+
+}
+
+void
+videosink_iterate (Element * element)
+{
+  if (!element->init) {
+    Feature *f;
+
+    feature_create (element, PAD_SINK, "sink");
+    f = feature_create (element, TIME, "time");
+    time_start (element, "time", 1 / 25.0);
+
+    feature_wait (element, "sink", TRUE);
+    feature_wait (element, "time", FALSE);
+
+    element->init = TRUE;
+    return;
+  }
+
+  /* this version hold the buffer in the bufpen */
+  if (feature_ready (element, "sink")) {
+    if (feature_ready (element, "time")) {
+      pad_pull (element, "sink");
+      g_print ("VIDEOSINK\n");
+      time_increment (element, "time", 1 / 25.0);
+      feature_wait (element, "time", FALSE);
+      feature_wait (element, "sink", TRUE);
+    } else {
+      feature_wait (element, "time", TRUE);
+      feature_wait (element, "sink", FALSE);
+    }
+  } else {
+    g_print ("ERROR: assert not reached\n");
+  }
+}
+
+void
+tee_iterate (Element * element)
+{
+  if (!element->init) {
+    feature_create (element, PAD_SINK, "sink");
+    feature_create (element, PAD_SRC, "src1");
+    feature_create (element, PAD_SRC, "src2");
+
+    feature_wait (element, "sink", FALSE);
+    feature_wait (element, "src1", TRUE);
+    feature_wait (element, "src2", TRUE);
+
+    element->init = TRUE;
+    return;
+  }
+
+  /* this version hold the buffer in the bufpen */
+  if (feature_ready (element, "sink")) {
+    pad_pull (element, "sink");
+    pad_push (element, "src1");
+    pad_push (element, "src2");
+
+    feature_wait (element, "sink", FALSE);
+    feature_wait (element, "src1", TRUE);
+    feature_wait (element, "src2", TRUE);
+  } else {
+    if (feature_ready (element, "src1")) {
+      if (feature_ready (element, "src2")) {
+        feature_wait (element, "sink", TRUE);
+        feature_wait (element, "src1", FALSE);
+        feature_wait (element, "src2", FALSE);
+      } else {
+        feature_wait (element, "sink", FALSE);
+        feature_wait (element, "src1", FALSE);
+        feature_wait (element, "src2", TRUE);
+      }
+    } else {
+      if (feature_ready (element, "src2")) {
+        feature_wait (element, "sink", FALSE);
+        feature_wait (element, "src1", TRUE);
+        feature_wait (element, "src2", FALSE);
+      } else {
+        g_print ("ERROR: assert not reached\n");
+      }
+    }
+  }
+}
diff --git a/testsuite/trigger/Makefile.am b/testsuite/trigger/Makefile.am
new file mode 100644 (file)
index 0000000..ef85850
--- /dev/null
@@ -0,0 +1,9 @@
+
+
+noinst_PROGRAMS = trigger
+
+trigger_SOURCES = trigger.c
+trigger_CFLAGS = $(GST_OBJ_CFLAGS)
+trigger_LDADD = $(GST_OBJS_LIBS) $(GLIB_LIBS)
+
+
diff --git a/testsuite/trigger/trigger.c b/testsuite/trigger/trigger.c
new file mode 100644 (file)
index 0000000..dba91bf
--- /dev/null
@@ -0,0 +1,856 @@
+
+#include <glib.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <gst/gst.h>
+
+#define INFINITY 1e10
+
+typedef struct _Element Element;
+typedef struct _Feature Feature;
+typedef struct _ElementInfo ElementInfo;
+
+enum
+{
+  PAD_SRC,
+  PAD_SINK,
+  FD,
+  TIME
+};
+
+struct _Element
+{
+  char *name;
+  GList *features;
+  int type;
+  gboolean init;
+  gboolean idle;
+
+  void (*iterate) (Element * element);
+  void (*caps_iterate) (Element * element);
+
+  int state;
+};
+
+struct _Feature
+{
+  char *name;
+  int type;
+
+  Element *parent;
+
+  gboolean waiting;
+  gboolean ready;
+
+  /* for pads */
+  gboolean bufpen;
+  Feature *peer;
+  GstCaps *caps;
+
+  /* for fds */
+  double next_time;
+  double interval;
+};
+
+struct _ElementInfo
+{
+  char *type_name;
+  void (*iterate) (Element * element);
+  void (*caps_iterate) (Element * element);
+};
+
+GList *elements;
+
+void run (void);
+void dump (void);
+void dump_element (Element * e);
+
+Element *element_factory_make (const char *type);
+Element *element_factory_make_full (const char *type, const char *name);
+void element_link (const char *name1, const char *name2);
+void element_link_full (const char *name1, const char *padname1,
+    const char *name2, const char *padname2);
+Element *element_get (const char *name);
+gboolean element_ready (Element * e);
+gboolean feature_is_ready (Feature * f);
+double element_next_time (Element * e);
+
+Feature *feature_create (Element * e, int type, const char *name);
+Feature *feature_get (Element * e, const char *name);
+gboolean feature_ready (Element * e, const char *name);
+
+void fakesrc_iterate (Element * element);
+void identity_iterate (Element * element);
+void fakesink_iterate (Element * element);
+void audiosink_iterate (Element * element);
+void mad_iterate (Element * element);
+void queue_iterate (Element * element);
+void videosink_iterate (Element * element);
+void tee_iterate (Element * element);
+
+void fakesrc_caps (Element * element);
+void identity_caps (Element * element);
+void fakesink_caps (Element * element);
+void audiosink_caps (Element * element);
+void mad_caps (Element * element);
+void queue_caps (Element * element);
+void videosink_caps (Element * element);
+void tee_caps (Element * element);
+
+double time;
+
+int
+main (int argc, char *argv[])
+{
+  int x = 8;
+
+  switch (x) {
+    case 0:
+      /* fakesrc ! fakesink */
+      element_factory_make ("fakesrc");
+      element_factory_make ("fakesink");
+      element_link ("fakesrc", "fakesink");
+      break;
+    case 1:
+      /* fakesrc ! identity ! fakesink */
+      element_factory_make ("fakesrc");
+      element_factory_make ("identity");
+      element_factory_make ("fakesink");
+
+      element_link ("fakesrc", "identity");
+      element_link ("identity", "fakesink");
+      break;
+    case 2:
+      /* fakesrc ! identity ! identity ! fakesink */
+      element_factory_make ("fakesrc");
+      element_factory_make_full ("identity", "identity0");
+      element_factory_make_full ("identity", "identity1");
+      element_factory_make ("fakesink");
+
+      element_link ("fakesrc", "identity0");
+      element_link ("identity0", "identity1");
+      element_link ("identity1", "fakesink");
+      break;
+    case 3:
+      /* fakesrc ! audiosink */
+      element_factory_make ("fakesrc");
+      element_factory_make ("audiosink");
+
+      element_link ("fakesrc", "audiosink");
+      break;
+    case 4:
+      /* fakesrc ! mad ! fakesink */
+      element_factory_make ("fakesrc");
+      element_factory_make ("mad");
+      element_factory_make ("fakesink");
+
+      element_link ("fakesrc", "mad");
+      element_link ("mad", "fakesink");
+      break;
+    case 5:
+      /* fakesrc ! queue ! fakesink */
+      element_factory_make ("fakesrc");
+      element_factory_make ("queue");
+      element_factory_make ("fakesink");
+
+      element_link ("fakesrc", "queue");
+      element_link ("queue", "fakesink");
+      break;
+    case 6:
+      /* fakesrc ! queue ! audiosink */
+      element_factory_make ("fakesrc");
+      element_factory_make ("queue");
+      element_factory_make ("audiosink");
+
+      element_link ("fakesrc", "queue");
+      element_link ("queue", "audiosink");
+      break;
+    case 7:
+      /* fakesrc ! videosink */
+      element_factory_make ("fakesrc");
+      element_factory_make ("videosink");
+
+      element_link ("fakesrc", "videosink");
+      break;
+    case 8:
+      /* fakesrc ! tee ! videosink tee0.src2 ! videosink */
+      element_factory_make ("fakesrc");
+      element_factory_make ("tee");
+      element_factory_make_full ("videosink", "vs0");
+      element_factory_make_full ("videosink", "vs1");
+
+      element_link ("fakesrc", "tee");
+      element_link_full ("tee", "src1", "vs0", "sink");
+      element_link_full ("tee", "src2", "vs1", "sink");
+      break;
+  }
+
+  run ();
+
+  return 0;
+}
+
+void
+run (void)
+{
+  int iter = 0;
+
+  while (iter < 20) {
+    Element *e;
+    GList *l;
+    gboolean did_something = FALSE;
+    double ent = INFINITY;
+
+    g_print ("iteration %d time %g\n", iter, time);
+    for (l = g_list_first (elements); l; l = g_list_next (l)) {
+      double nt;
+
+      e = l->data;
+      if (element_ready (e)) {
+        g_print ("%s: is ready, iterating\n", e->name);
+        e->iterate (e);
+        did_something = TRUE;
+      } else {
+        g_print ("%s: is not ready\n", e->name);
+      }
+      nt = element_next_time (e);
+      if (nt < ent) {
+        ent = nt;
+      }
+    }
+    if (did_something == FALSE) {
+      if (ent < INFINITY) {
+        g_print ("nothing to do, waiting for %g\n", ent);
+        time = ent;
+      } else {
+        g_print ("ERROR: deadlock\n");
+        exit (1);
+      }
+    }
+    iter++;
+  }
+
+}
+
+void
+dump (void)
+{
+  Element *e;
+  GList *l;
+
+  for (l = g_list_first (elements); l; l = g_list_next (l)) {
+    e = l->data;
+    dump_element (e);
+  }
+}
+
+void
+dump_element (Element * e)
+{
+  Feature *f;
+  GList *m;
+
+  g_print ("%s:\n", e->name);
+  for (m = g_list_first (e->features); m; m = g_list_next (m)) {
+    f = m->data;
+    g_print ("  %s:\n", f->name);
+    g_print ("    type %d\n", f->type);
+    g_print ("    ready %d\n", f->ready);
+    g_print ("    waiting %d\n", f->waiting);
+
+  }
+}
+
+/* Element */
+
+const ElementInfo element_types[] = {
+  {"fakesrc", fakesrc_iterate},
+  {"identity", identity_iterate},
+  {"fakesink", fakesink_iterate},
+  {"audiosink", audiosink_iterate},
+  {"mad", mad_iterate},
+  {"queue", queue_iterate},
+  {"videosink", videosink_iterate},
+  {"tee", tee_iterate},
+  {NULL, NULL}
+};
+
+Element *
+element_factory_make (const char *type)
+{
+  return element_factory_make_full (type, type);
+}
+
+Element *
+element_factory_make_full (const char *type, const char *name)
+{
+  int i;
+  Element *e = g_new0 (Element, 1);
+
+  for (i = 0; element_types[i].type_name; i++) {
+    if (strcmp (type, element_types[i].type_name) == 0) {
+      e->type = i;
+      e->iterate = element_types[i].iterate;
+      e->caps_iterate = element_types[i].iterate;
+      e->iterate (e);
+      e->name = g_strdup (name);
+
+      elements = g_list_append (elements, e);
+
+      return e;
+    }
+  }
+
+  g_print ("ERROR: element type %s not found\n", type);
+  return NULL;
+}
+
+void
+element_link (const char *name1, const char *name2)
+{
+  element_link_full (name1, "src", name2, "sink");
+}
+
+void
+element_link_full (const char *name1, const char *padname1, const char *name2,
+    const char *padname2)
+{
+  Element *e1, *e2;
+  Feature *pad1, *pad2;
+
+  e1 = element_get (name1);
+  e2 = element_get (name2);
+
+  pad1 = feature_get (e1, padname1);
+  pad2 = feature_get (e2, padname2);
+
+  pad1->peer = pad2;
+  pad2->peer = pad1;
+
+}
+
+Element *
+element_get (const char *name)
+{
+  GList *l;
+
+  for (l = g_list_first (elements); l; l = g_list_next (l)) {
+    Element *e = l->data;
+
+    if (strcmp (name, e->name) == 0)
+      return e;
+  }
+
+  g_print ("ERROR: element_get(%s) element not found\n", name);
+  return NULL;
+}
+
+gboolean
+element_ready (Element * e)
+{
+  GList *l;
+
+  //dump_element(e);
+  if (e->idle)
+    return TRUE;
+  for (l = g_list_first (e->features); l; l = g_list_next (l)) {
+    Feature *f = l->data;
+
+    if (f->waiting && feature_is_ready (f)) {
+      g_print ("element %s is ready because feature %s is ready\n",
+          e->name, f->name);
+      return TRUE;
+    }
+  }
+  return FALSE;
+}
+
+double
+element_next_time (Element * e)
+{
+  GList *l;
+  double ent = INFINITY;
+
+  for (l = g_list_first (e->features); l; l = g_list_next (l)) {
+    Feature *f = l->data;
+
+    if (f->type == FD || f->type == TIME) {
+      if (f->next_time < ent) {
+        ent = f->next_time;
+      }
+    }
+  }
+  return ent;
+}
+
+/* Feature */
+
+Feature *
+feature_get (Element * e, const char *name)
+{
+  GList *l;
+
+  for (l = g_list_first (e->features); l; l = g_list_next (l)) {
+    Feature *f = l->data;
+
+    if (strcmp (name, f->name) == 0)
+      return f;
+  }
+
+  g_print ("ERROR: feature_get(%s): feature not found\n", name);
+  return NULL;
+}
+
+Feature *
+feature_create (Element * e, int type, const char *name)
+{
+  Feature *f = g_new0 (Feature, 1);
+
+  f->name = g_strdup (name);
+  f->type = type;
+
+  e->features = g_list_append (e->features, f);
+
+  return f;
+}
+
+void
+feature_wait (Element * e, const char *name, gboolean wait)
+{
+  Feature *f = feature_get (e, name);
+
+  f->waiting = wait;
+  switch (f->type) {
+    case PAD_SRC:
+      if (f->peer) {
+        f->peer->ready = wait && f->peer->bufpen;
+      }
+      break;
+    case PAD_SINK:
+      if (f->peer) {
+        f->peer->ready = wait && f->bufpen;
+      }
+      break;
+  }
+}
+
+gboolean
+feature_ready (Element * e, const char *name)
+{
+  Feature *f = feature_get (e, name);
+
+  return feature_is_ready (f);
+}
+
+gboolean
+feature_is_ready (Feature * f)
+{
+  switch (f->type) {
+    case PAD_SRC:
+      if (f->peer) {
+        return f->peer->waiting && !f->peer->bufpen;
+      }
+      break;
+    case PAD_SINK:
+      if (f->peer) {
+        return f->peer->waiting && f->bufpen;
+      }
+      break;
+    case FD:
+      g_print ("testing %g <= %g\n", f->next_time, time);
+      if (f->next_time <= time) {
+        return TRUE;
+      } else {
+        return FALSE;
+      }
+      break;
+    case TIME:
+      g_print ("testing %g <= %g\n", f->next_time, time);
+      if (f->next_time <= time) {
+        return TRUE;
+      } else {
+        return FALSE;
+      }
+      break;
+  }
+
+  return FALSE;
+}
+
+void
+pad_push (Element * e, const char *name)
+{
+  Feature *f = feature_get (e, name);
+
+  g_assert (f->type == PAD_SRC);
+
+  g_print ("pushing pad on %s:%s\n", e->name, name);
+  if (f->peer->bufpen) {
+    g_print ("ERROR: push when bufpen full link\n");
+    exit (0);
+  }
+  f->peer->bufpen = TRUE;
+  f->peer->ready = f->waiting;
+  f->ready = FALSE;
+}
+
+void
+pad_pull (Element * e, const char *name)
+{
+  Feature *f = feature_get (e, name);
+
+  g_assert (f->type == PAD_SINK);
+
+  g_print ("pulling pad on %s:%s\n", e->name, name);
+  if (!f->bufpen) {
+    g_print ("ERROR: pull when bufpen empty\n");
+    exit (0);
+  }
+  f->bufpen = FALSE;
+  f->ready = FALSE;
+  f->peer->ready = f->waiting;
+}
+
+void
+fd_push (Element * e, const char *name)
+{
+  Feature *f = feature_get (e, name);
+
+  g_assert (f->type == FD);
+
+  g_print ("pushing to fd %s:%s\n", e->name, name);
+  if (time < f->next_time) {
+    g_print ("ERROR: push too early\n");
+    exit (0);
+  }
+  f->next_time += f->interval;
+}
+
+void
+fd_start (Element * e, const char *name, double interval)
+{
+  Feature *f = feature_get (e, name);
+
+  g_assert (f->type == FD);
+
+  f->interval = interval;
+  f->next_time = f->interval;
+}
+
+void
+time_start (Element * e, const char *name, double interval)
+{
+  Feature *f = feature_get (e, name);
+
+  g_assert (f->type == TIME);
+
+  f->interval = interval;
+  f->next_time = f->interval;
+}
+
+void
+time_increment (Element * e, const char *name, double interval)
+{
+  Feature *f = feature_get (e, name);
+
+  g_assert (f->type == TIME);
+
+  f->interval = interval;
+  f->next_time += f->interval;
+}
+
+/* elements */
+
+void
+fakesrc_iterate (Element * element)
+{
+  //Event *event;
+
+  if (!element->init) {
+    feature_create (element, PAD_SRC, "src");
+
+    feature_wait (element, "src", TRUE);
+
+    element->init = TRUE;
+    return;
+  }
+
+  pad_push (element, "src");
+}
+
+void
+identity_iterate (Element * element)
+{
+  if (!element->init) {
+    feature_create (element, PAD_SINK, "sink");
+    feature_create (element, PAD_SRC, "src");
+
+    feature_wait (element, "sink", FALSE);
+    feature_wait (element, "src", TRUE);
+
+    element->init = TRUE;
+    return;
+  }
+
+  if (feature_ready (element, "sink") && feature_ready (element, "src")) {
+    pad_pull (element, "sink");
+    pad_push (element, "src");
+    feature_wait (element, "sink", FALSE);
+    feature_wait (element, "src", TRUE);
+  } else {
+    if (feature_ready (element, "sink")) {
+      g_print ("ERROR: assert not reached\n");
+      feature_wait (element, "src", TRUE);
+      feature_wait (element, "sink", FALSE);
+    }
+    if (feature_ready (element, "src")) {
+      feature_wait (element, "src", FALSE);
+      feature_wait (element, "sink", TRUE);
+    }
+  }
+}
+
+void
+fakesink_iterate (Element * element)
+{
+  if (!element->init) {
+    feature_create (element, PAD_SINK, "sink");
+
+    element->idle = TRUE;
+    element->init = TRUE;
+    return;
+  }
+
+  if (feature_ready (element, "sink")) {
+    pad_pull (element, "sink");
+    g_print ("FAKESINK\n");
+  } else {
+    feature_wait (element, "sink", TRUE);
+    element->idle = FALSE;
+  }
+
+}
+
+void
+audiosink_iterate (Element * element)
+{
+  if (!element->init) {
+    Feature *f;
+
+    feature_create (element, PAD_SINK, "sink");
+    f = feature_create (element, FD, "fd");
+    fd_start (element, "fd", 1024 / 44100.0);
+
+    feature_wait (element, "fd", TRUE);
+
+    element->init = TRUE;
+    return;
+  }
+
+  if (feature_ready (element, "fd")) {
+    if (feature_ready (element, "sink")) {
+      pad_pull (element, "sink");
+      fd_push (element, "fd");
+      g_print ("AUDIOSINK\n");
+      feature_wait (element, "fd", TRUE);
+      feature_wait (element, "sink", FALSE);
+    } else {
+      feature_wait (element, "fd", FALSE);
+      feature_wait (element, "sink", TRUE);
+    }
+  } else {
+    g_print ("ERROR: assert not reached\n");
+
+    feature_wait (element, "sink", FALSE);
+    feature_wait (element, "fd", TRUE);
+  }
+
+}
+
+void
+mad_iterate (Element * element)
+{
+  if (!element->init) {
+    feature_create (element, PAD_SINK, "sink");
+    feature_create (element, PAD_SRC, "src");
+
+    element->state = 0;
+    feature_wait (element, "sink", FALSE);
+    feature_wait (element, "src", TRUE);
+
+    element->init = TRUE;
+    return;
+  }
+
+  if (element->state > 0) {
+    if (feature_ready (element, "src")) {
+      pad_push (element, "src");
+      element->state--;
+      if (element->state > 0) {
+        feature_wait (element, "sink", FALSE);
+        feature_wait (element, "src", TRUE);
+      } else {
+        feature_wait (element, "sink", FALSE);
+        feature_wait (element, "src", TRUE);
+      }
+    } else {
+      g_print ("ERROR: assert not reached\n");
+    }
+  } else {
+    if (feature_ready (element, "sink")) {
+      pad_pull (element, "sink");
+      element->state += 5;
+      pad_push (element, "src");
+      element->state--;
+      feature_wait (element, "sink", FALSE);
+      feature_wait (element, "src", TRUE);
+    } else {
+      feature_wait (element, "sink", TRUE);
+      feature_wait (element, "src", FALSE);
+    }
+  }
+}
+
+void
+queue_iterate (Element * element)
+{
+  if (!element->init) {
+    feature_create (element, PAD_SINK, "sink");
+    feature_create (element, PAD_SRC, "src");
+
+    element->state = 0;
+    feature_wait (element, "sink", FALSE);
+    feature_wait (element, "src", TRUE);
+
+    element->init = TRUE;
+    return;
+  }
+
+  if (feature_ready (element, "sink") && element->state < 5) {
+    pad_pull (element, "sink");
+    element->state++;
+  }
+  if (feature_ready (element, "src") && element->state > 0) {
+    pad_push (element, "src");
+    element->state--;
+  }
+
+  if (element->state < 5) {
+    feature_wait (element, "sink", TRUE);
+  } else {
+    feature_wait (element, "sink", FALSE);
+  }
+  if (element->state > 0) {
+    feature_wait (element, "src", TRUE);
+  } else {
+    feature_wait (element, "src", FALSE);
+  }
+}
+
+void
+demux_iterate (Element * element)
+{
+  if (!element->init) {
+    feature_create (element, PAD_SINK, "sink");
+    feature_create (element, PAD_SRC, "video_src");
+    feature_create (element, PAD_SRC, "audio_src");
+
+    feature_wait (element, "sink", TRUE);
+    feature_wait (element, "video_src", FALSE);
+    feature_wait (element, "audio_src", FALSE);
+
+    element->init = TRUE;
+    return;
+  }
+#if 0
+  /* demux waits for a buffer on the sinkpad, then queues buffers and
+   * eventually pushes them to sinkpads */
+  if (feautre_ready (element, "sink") &&) {
+
+  }
+#endif
+
+}
+
+void
+videosink_iterate (Element * element)
+{
+  if (!element->init) {
+    Feature *f;
+
+    feature_create (element, PAD_SINK, "sink");
+    f = feature_create (element, TIME, "time");
+    time_start (element, "time", 1 / 25.0);
+
+    feature_wait (element, "sink", TRUE);
+    feature_wait (element, "time", FALSE);
+
+    element->init = TRUE;
+    return;
+  }
+
+  /* this version hold the buffer in the bufpen */
+  if (feature_ready (element, "sink")) {
+    if (feature_ready (element, "time")) {
+      pad_pull (element, "sink");
+      g_print ("VIDEOSINK\n");
+      time_increment (element, "time", 1 / 25.0);
+      feature_wait (element, "time", FALSE);
+      feature_wait (element, "sink", TRUE);
+    } else {
+      feature_wait (element, "time", TRUE);
+      feature_wait (element, "sink", FALSE);
+    }
+  } else {
+    g_print ("ERROR: assert not reached\n");
+  }
+}
+
+void
+tee_iterate (Element * element)
+{
+  if (!element->init) {
+    feature_create (element, PAD_SINK, "sink");
+    feature_create (element, PAD_SRC, "src1");
+    feature_create (element, PAD_SRC, "src2");
+
+    feature_wait (element, "sink", FALSE);
+    feature_wait (element, "src1", TRUE);
+    feature_wait (element, "src2", TRUE);
+
+    element->init = TRUE;
+    return;
+  }
+
+  /* this version hold the buffer in the bufpen */
+  if (feature_ready (element, "sink")) {
+    pad_pull (element, "sink");
+    pad_push (element, "src1");
+    pad_push (element, "src2");
+
+    feature_wait (element, "sink", FALSE);
+    feature_wait (element, "src1", TRUE);
+    feature_wait (element, "src2", TRUE);
+  } else {
+    if (feature_ready (element, "src1")) {
+      if (feature_ready (element, "src2")) {
+        feature_wait (element, "sink", TRUE);
+        feature_wait (element, "src1", FALSE);
+        feature_wait (element, "src2", FALSE);
+      } else {
+        feature_wait (element, "sink", FALSE);
+        feature_wait (element, "src1", FALSE);
+        feature_wait (element, "src2", TRUE);
+      }
+    } else {
+      if (feature_ready (element, "src2")) {
+        feature_wait (element, "sink", FALSE);
+        feature_wait (element, "src1", TRUE);
+        feature_wait (element, "src2", FALSE);
+      } else {
+        g_print ("ERROR: assert not reached\n");
+      }
+    }
+  }
+}