check/: I wrote a test!
authorJan Schmidt <thaytan@mad.scientist.com>
Thu, 18 Aug 2005 15:31:28 +0000 (15:31 +0000)
committerJan Schmidt <thaytan@mad.scientist.com>
Thu, 18 Aug 2005 15:31:28 +0000 (15:31 +0000)
Original commit message from CVS:
* check/Makefile.am:
* check/gst/gstevents.c: (GST_START_TEST), (event_probe),
(test_event), (timediff), (gstevents_suite), (main):
I wrote a test!

* docs/design/part-seeking.txt:
Spelling correction

* docs/gst/tmpl/gstevent.sgml:
Docs updates.

* gst/base/gstbasesink.c: (gst_base_sink_handle_object):
Treat a buffer-without-newsegment the same as a receiving
a newsegment not in time format, and disable syncing to the clock
with a warning.

* gst/gstbus.c: (gst_bus_set_sync_handler):
Assert if anyone tries to replace the existing sync_handler for bus,
as only the owner should be setting it.

* gst/gstevent.h:
Have a fixed set of custom event enums with events identified by
their structure name (as in 0.8), rather than a free-for-all
allowing collisions between enum values from different plugins.

* gst/gstpad.c: (gst_pad_class_init):
Docs change.

* gst/gstqueue.c: (gst_queue_handle_sink_event):
Handle out-of-band downstream events from the sending thread.

14 files changed:
ChangeLog
check/Makefile.am
check/gst/gstevents.c [new file with mode: 0644]
docs/design/part-seeking.txt
docs/gst/tmpl/gstevent.sgml
gst/base/gstbasesink.c
gst/gstbus.c
gst/gstevent.h
gst/gstpad.c
gst/gstqueue.c
libs/gst/base/gstbasesink.c
plugins/elements/gstqueue.c
tests/check/Makefile.am
tests/check/gst/gstevents.c [new file with mode: 0644]

index 2469834..6c1ac9b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,37 @@
+2005-08-18  Jan Schmidt  <thaytan@mad.scientist.com>
+
+       * check/Makefile.am:
+       * check/gst/gstevents.c: (GST_START_TEST), (event_probe),
+       (test_event), (timediff), (gstevents_suite), (main):
+         I wrote a test!
+
+       * docs/design/part-seeking.txt:
+         Spelling correction
+
+       * docs/gst/tmpl/gstevent.sgml:
+       * docs/gst/tmpl/gstfakesrc.sgml:
+         Docs updates.
+
+       * gst/base/gstbasesink.c: (gst_base_sink_handle_object):
+         Treat a buffer-without-newsegment the same as a receiving 
+         a newsegment not in time format, and disable syncing to the clock
+         with a warning.
+
+       * gst/gstbus.c: (gst_bus_set_sync_handler):
+         Assert if anyone tries to replace the existing sync_handler for bus, 
+         as only the owner should be setting it.
+
+       * gst/gstevent.h:
+         Have a fixed set of custom event enums with events identified by
+         their structure name (as in 0.8), rather than a free-for-all
+         allowing collisions between enum values from different plugins.
+
+       * gst/gstpad.c: (gst_pad_class_init):
+         Docs change.
+         
+       * gst/gstqueue.c: (gst_queue_handle_sink_event):
+         Handle out-of-band downstream events from the sending thread.
+
 2005-08-17  Andy Wingo  <wingo@pobox.com>
 
        * gst/gstpipeline.c (gst_pipeline_change_state): Interpret
index f5f7fa1..d4fbc05 100644 (file)
@@ -37,6 +37,7 @@ TESTS = $(top_builddir)/tools/gst-register    \
         gst/gstbus                             \
        gst/gstcaps                             \
        gst/gstelement                          \
+       gst/gstevents                           \
        gst/gstghostpad                         \
        gst/gstiterator                         \
        gst/gstmessage                          \
diff --git a/check/gst/gstevents.c b/check/gst/gstevents.c
new file mode 100644 (file)
index 0000000..e4c507b
--- /dev/null
@@ -0,0 +1,370 @@
+/* GStreamer
+ * Copyright (C) 2005 Jan Schmidt <thaytan@mad.scientist.com>
+ *
+ * gstevents.c: Unit test for event handling
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#include "../gstcheck.h"
+
+GST_START_TEST (create_custom_events)
+{
+  GstEvent *event, *event2;
+  GstStructure *structure;
+
+  /* FLUSH_START */
+  {
+    event = gst_event_new_flush_start ();
+    fail_if (event == NULL);
+    fail_unless (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_START);
+    fail_unless (GST_EVENT_IS_UPSTREAM (event));
+    fail_unless (GST_EVENT_IS_DOWNSTREAM (event));
+    fail_if (GST_EVENT_IS_SERIALIZED (event));
+    gst_event_unref (event);
+  }
+  /* FLUSH_STOP */
+  {
+    event = gst_event_new_flush_stop ();
+    fail_if (event == NULL);
+    fail_unless (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP);
+    fail_unless (GST_EVENT_IS_UPSTREAM (event));
+    fail_unless (GST_EVENT_IS_DOWNSTREAM (event));
+    fail_if (GST_EVENT_IS_SERIALIZED (event));
+    gst_event_unref (event);
+  }
+  /* EOS */
+  {
+    event = gst_event_new_eos ();
+    fail_if (event == NULL);
+    fail_unless (GST_EVENT_TYPE (event) == GST_EVENT_EOS);
+    fail_if (GST_EVENT_IS_UPSTREAM (event));
+    fail_unless (GST_EVENT_IS_DOWNSTREAM (event));
+    fail_unless (GST_EVENT_IS_SERIALIZED (event));
+    gst_event_unref (event);
+  }
+  /* NEWSEGMENT */
+  {
+    gdouble rate;
+    GstFormat format;
+    gint64 start, end, base;
+
+    event = gst_event_new_newsegment (0.5, GST_FORMAT_TIME, 1, G_MAXINT64,
+        0xdeadbeef);
+    fail_if (event == NULL);
+    fail_unless (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT);
+    fail_if (GST_EVENT_IS_UPSTREAM (event));
+    fail_unless (GST_EVENT_IS_DOWNSTREAM (event));
+    fail_unless (GST_EVENT_IS_SERIALIZED (event));
+
+    gst_event_parse_newsegment (event, &rate, &format, &start, &end, &base);
+    fail_unless (rate == 0.5);
+    fail_unless (format == GST_FORMAT_TIME);
+    fail_unless (start == 1);
+    fail_unless (end == G_MAXINT64);
+    fail_unless (base == 0xdeadbeef);
+
+    gst_event_unref (event);
+  }
+  /* TAGS */
+  {
+    GstTagList *taglist = gst_tag_list_new ();
+    GstTagList *tl2 = NULL;
+
+    event = gst_event_new_tag (taglist);
+    fail_if (taglist == NULL);
+    fail_if (event == NULL);
+    fail_unless (GST_EVENT_TYPE (event) == GST_EVENT_TAG);
+    fail_if (GST_EVENT_IS_UPSTREAM (event));
+    fail_unless (GST_EVENT_IS_DOWNSTREAM (event));
+    fail_unless (GST_EVENT_IS_SERIALIZED (event));
+
+    gst_event_parse_tag (event, &tl2);
+    fail_unless (taglist == tl2);
+    gst_event_unref (event);
+  }
+
+  /* FIXME: Add tests for FILLERS and QOS when they are implemented. */
+
+  /* SEEK */
+  {
+    gdouble rate;
+    GstFormat format;
+    GstSeekFlags flags;
+    GstSeekType cur_type, stop_type;
+    gint64 cur, stop;
+
+    event = gst_event_new_seek (0.5, GST_FORMAT_BYTES,
+        GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
+        GST_SEEK_TYPE_SET, 1, GST_SEEK_TYPE_NONE, 0xdeadbeef);
+
+    fail_if (event == NULL);
+    fail_unless (GST_EVENT_TYPE (event) == GST_EVENT_SEEK);
+    fail_unless (GST_EVENT_IS_UPSTREAM (event));
+    fail_if (GST_EVENT_IS_DOWNSTREAM (event));
+    fail_if (GST_EVENT_IS_SERIALIZED (event));
+
+    gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur,
+        &stop_type, &stop);
+    fail_unless (rate == 0.5);
+    fail_unless (format == GST_FORMAT_BYTES);
+    fail_unless (flags == (GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE));
+    fail_unless (cur_type == GST_SEEK_TYPE_SET);
+    fail_unless (cur == 1);
+    fail_unless (stop_type == GST_SEEK_TYPE_NONE);
+    fail_unless (stop == 0xdeadbeef);
+
+    gst_event_unref (event);
+  }
+
+  /* NAVIGATION */
+  {
+    structure = gst_structure_new ("application/x-gst-navigation", "event",
+        G_TYPE_STRING, "key-press", "key", G_TYPE_STRING, "mon", NULL);
+    fail_if (structure == NULL);
+    event = gst_event_new_navigation (structure);
+    fail_if (event == NULL);
+    fail_unless (GST_EVENT_TYPE (event) == GST_EVENT_NAVIGATION);
+    fail_unless (GST_EVENT_IS_UPSTREAM (event));
+    fail_if (GST_EVENT_IS_DOWNSTREAM (event));
+    fail_if (GST_EVENT_IS_SERIALIZED (event));
+
+    fail_unless (gst_event_get_structure (event) == structure);
+    gst_event_unref (event);
+  }
+
+  /* Custom event types */
+  {
+    structure = gst_structure_empty_new ("application/x-custom");
+    fail_if (structure == NULL);
+    event = gst_event_new_custom (GST_EVENT_CUSTOM_UP, structure);
+    fail_if (event == NULL);
+    fail_unless (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_UP);
+    fail_unless (GST_EVENT_IS_UPSTREAM (event));
+    fail_if (GST_EVENT_IS_DOWNSTREAM (event));
+    fail_if (GST_EVENT_IS_SERIALIZED (event));
+    fail_unless (gst_event_get_structure (event) == structure);
+    gst_event_unref (event);
+
+    /* Decided not to test the other custom enum types, as they
+     * only differ by the value of the enum passed to gst_event_new_custom
+     */
+  }
+
+  /* Event copying */
+  {
+    structure = gst_structure_empty_new ("application/x-custom");
+    fail_if (structure == NULL);
+    event = gst_event_new_custom (GST_EVENT_CUSTOM_BOTH, structure);
+
+    fail_if (event == NULL);
+    event2 = gst_event_copy (event);
+    fail_if (event2 == NULL);
+    fail_unless (GST_EVENT_TYPE (event) == GST_EVENT_TYPE (event2));
+
+    /* The structure should have been duplicated */
+    fail_if (gst_event_get_structure (event) ==
+        gst_event_get_structure (event2));
+    gst_event_unref (event);
+    gst_event_unref (event2);
+  }
+}
+
+GST_END_TEST GTimeVal sent_event_time;
+GstEvent *got_event_before_q, *got_event_after_q;
+GTimeVal got_event_time;
+
+static gboolean
+event_probe (GstPad * pad, GstMiniObject ** data, gpointer user_data)
+{
+  gboolean before_q = (gboolean) user_data;
+
+  fail_unless (GST_IS_EVENT (data));
+
+  if (before_q) {
+    switch (GST_EVENT_TYPE (GST_EVENT (data))) {
+      case GST_EVENT_CUSTOM_UP:
+      case GST_EVENT_CUSTOM_BOTH:
+      case GST_EVENT_CUSTOM_BOTH_OOB:
+        gst_event_ref (data);
+        g_get_current_time (&got_event_time);
+        got_event_before_q = GST_EVENT (data);
+        break;
+      default:
+        break;
+    }
+  } else {
+    switch (GST_EVENT_TYPE (GST_EVENT (data))) {
+      case GST_EVENT_CUSTOM_DS:
+      case GST_EVENT_CUSTOM_DS_OOB:
+      case GST_EVENT_CUSTOM_BOTH:
+      case GST_EVENT_CUSTOM_BOTH_OOB:
+        gst_event_ref (data);
+        g_get_current_time (&got_event_time);
+        got_event_after_q = GST_EVENT (data);
+        break;
+      default:
+        break;
+    }
+  }
+
+  return TRUE;
+}
+
+static void test_event
+    (GstEventType type, GstPad * pad, gboolean expect_before_q)
+{
+  GstEvent *event;
+  gint i;
+
+  got_event_before_q = got_event_after_q = NULL;
+
+  event = gst_event_new_custom (type,
+      gst_structure_empty_new ("application/x-custom"));
+  g_get_current_time (&sent_event_time);
+  got_event_time.tv_sec = 0;
+  got_event_time.tv_usec = 0;
+
+  gst_pad_push_event (pad, event);
+
+  /* Wait up to 5 seconds for the event to appear */
+  if (expect_before_q) {
+    for (i = 0; i < 50; i++) {
+      g_usleep (G_USEC_PER_SEC / 10);
+      if (got_event_before_q != NULL)
+        break;
+    }
+    fail_if (got_event_before_q == NULL);
+    fail_unless (GST_EVENT_TYPE (got_event_before_q) == type);
+  } else {
+    for (i = 0; i < 50; i++) {
+      g_usleep (G_USEC_PER_SEC / 10);
+      if (got_event_after_q != NULL)
+        break;
+    }
+    fail_if (got_event_after_q == NULL);
+    fail_unless (GST_EVENT_TYPE (got_event_after_q) == type);
+  }
+};
+
+static gint64
+timediff (GTimeVal * end, GTimeVal * start)
+{
+  return (end->tv_sec - start->tv_sec) * G_USEC_PER_SEC +
+      (end->tv_usec - start->tv_usec);
+}
+
+GST_START_TEST (send_custom_events)
+{
+  /* Run some tests on custom events. Checking for serialisation and whatnot.
+   * pipeline is fakesrc ! queue ! fakesink */
+  GstBin *pipeline;
+  GstElement *fakesrc, *fakesink, *queue;
+  GstPad *srcpad, *sinkpad;
+
+  fail_if ((pipeline = (GstBin *) gst_pipeline_new ("testpipe")) == NULL);
+  fail_if ((fakesrc = gst_element_factory_make ("fakesrc", NULL)) == NULL);
+  fail_if ((fakesink = gst_element_factory_make ("fakesink", NULL)) == NULL);
+  fail_if ((queue = gst_element_factory_make ("queue", NULL)) == NULL);
+
+  gst_bin_add_many (pipeline, fakesrc, queue, fakesink, NULL);
+  fail_unless (gst_element_link_many (fakesrc, queue, fakesink, NULL));
+
+  /* Send 25 buffers per sec */
+  g_object_set (G_OBJECT (fakesrc), "silent", TRUE, "datarate", 25,
+      "sizemax", 1, "sizetype", 2, NULL);
+  g_object_set (G_OBJECT (queue), "max-size-buffers", 0, "max-size-time",
+      (guint64) 1.1 * GST_SECOND, "max-size-bytes", 0, NULL);
+  g_object_set (G_OBJECT (fakesink), "silent", TRUE, "sync", TRUE, NULL);
+
+  fail_if ((srcpad = gst_element_get_pad (fakesrc, "src")) == NULL);
+  gst_pad_add_event_probe (srcpad, (GCallback) event_probe,
+      GINT_TO_POINTER (TRUE));
+
+  fail_if ((sinkpad = gst_element_get_pad (fakesink, "sink")) == NULL);
+  gst_pad_add_event_probe (sinkpad, (GCallback) event_probe,
+      GINT_TO_POINTER (FALSE));
+
+  gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
+
+  /* Upstream events */
+  test_event (GST_EVENT_CUSTOM_UP, sinkpad, TRUE);
+  fail_unless (timediff (&got_event_time, &sent_event_time) < G_USEC_PER_SEC,
+      "GST_EVENT_CUSTOM_UP took to long to reach source");
+
+  test_event (GST_EVENT_CUSTOM_BOTH, sinkpad, TRUE);
+  fail_unless (timediff (&got_event_time, &sent_event_time) < G_USEC_PER_SEC,
+      "GST_EVENT_CUSTOM_BOTH took to long to reach source");
+
+  test_event (GST_EVENT_CUSTOM_BOTH_OOB, sinkpad, TRUE);
+  fail_unless (timediff (&got_event_time, &sent_event_time) < G_USEC_PER_SEC,
+      "GST_EVENT_CUSTOM_BOTH_OOB took to long to reach source");
+
+  /* Out of band downstream events */
+  test_event (GST_EVENT_CUSTOM_DS_OOB, srcpad, FALSE);
+  fail_unless (timediff (&got_event_time, &sent_event_time) < G_USEC_PER_SEC,
+      "GST_EVENT_CUSTOM_DS_OOB took to long to reach source");
+
+  test_event (GST_EVENT_CUSTOM_BOTH_OOB, srcpad, FALSE);
+  fail_unless (timediff (&got_event_time, &sent_event_time) < G_USEC_PER_SEC,
+      "GST_EVENT_CUSTOM_BOTH_OOB took to long to reach source");
+
+  /* In-band downstream events are expected to take at least 1 second 
+   * to traverse the the queue */
+  test_event (GST_EVENT_CUSTOM_DS, srcpad, FALSE);
+  fail_unless (timediff (&got_event_time, &sent_event_time) >= G_USEC_PER_SEC,
+      "GST_EVENT_CUSTOM_BOTH_OOB arrived at sink too quickly for an in-band event");
+
+  test_event (GST_EVENT_CUSTOM_BOTH, srcpad, FALSE);
+  fail_unless (timediff (&got_event_time, &sent_event_time) >= G_USEC_PER_SEC,
+      "GST_EVENT_CUSTOM_BOTH_OOB arrived at sink too quickly for an in-band event");
+
+  gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL);
+  gst_bin_watch_for_state_change (GST_BIN (pipeline));
+
+  gst_object_unref (pipeline);
+}
+
+GST_END_TEST Suite * gstevents_suite (void)
+{
+  Suite *s = suite_create ("GstEvent");
+  TCase *tc_chain = tcase_create ("customevents");
+
+  tcase_set_timeout (tc_chain, 20);
+
+  suite_add_tcase (s, tc_chain);
+  tcase_add_test (tc_chain, create_custom_events);
+  tcase_add_test (tc_chain, send_custom_events);
+  return s;
+}
+
+int
+main (int argc, char **argv)
+{
+  int nf;
+
+  Suite *s = gstevents_suite ();
+  SRunner *sr = srunner_create (s);
+
+  gst_check_init (&argc, &argv);
+
+  srunner_run_all (sr, CK_NORMAL);
+  nf = srunner_ntests_failed (sr);
+  srunner_free (sr);
+
+  return nf;
+}
index c8c8367..d9affcd 100644 (file)
@@ -6,7 +6,7 @@ media between a certain start and stop time, called a segment.
 
 Different kinds of seeking exist:
 
- - immeditate seeking with low latency (FLUSH seek)
+ - immediate seeking with low latency (FLUSH seek)
  - seeking without flush, playback will start from the new
    position after all the queues are emptied with old data.
  - segment seeking with and without FLUSH, this can be used to
index 4872e40..ff29d63 100644 (file)
@@ -56,7 +56,11 @@ The different major types of events.
 @GST_EVENT_QOS: a quality of service event
 @GST_EVENT_SEEK: a seek event.
 @GST_EVENT_NAVIGATION: 
-@GST_EVENT_CUSTOM_START: 
+@GST_EVENT_CUSTOM_UP: 
+@GST_EVENT_CUSTOM_DS: 
+@GST_EVENT_CUSTOM_DS_OOB: 
+@GST_EVENT_CUSTOM_BOTH: 
+@GST_EVENT_CUSTOM_BOTH_OOB: 
 
 <!-- ##### MACRO GST_EVENT_TRACE_NAME ##### -->
 <para>
index 2fabac9..50f59e2 100644 (file)
@@ -514,8 +514,12 @@ gst_base_sink_handle_object (GstBaseSink * basesink, GstPad * pad,
   } else {
     if (!basesink->have_discont) {
       GST_ELEMENT_ERROR (basesink, STREAM, STOPPED,
-          ("received buffer without a discont"),
-          ("received buffer without a discont"));
+          ("Received buffer without a new-segment. Cannot sync to clock."),
+          ("Received buffer without a new-segment. Cannot sync to clock."));
+      basesink->have_discont = TRUE;
+      /* this means this sink will not be able to sync to the clock */
+      basesink->discont_start = 0;
+      basesink->discont_stop = 0;
     }
     basesink->preroll_queued++;
     basesink->buffers_queued++;
index b5da3ca..1362f2b 100644 (file)
@@ -380,9 +380,12 @@ gst_bus_peek (GstBus * bus)
  * @func: The handler function to install
  * @data: User data that will be sent to the handler function.
  *
- * Install a synchronous handler on the bus. The function will be called
+ * Sets the synchronous handler on the bus. The function will be called
  * every time a new message is posted on the bus. Note that the function
- * will be called in the same thread context as the posting object.
+ * will be called in the same thread context as the posting object. This
+ * function is usually only called by the creator of the bus. Applications
+ * should handle messages asynchronously using the gst_bus watch and poll
+ * functions.
  */
 void
 gst_bus_set_sync_handler (GstBus * bus, GstBusSyncHandler func, gpointer data)
@@ -390,6 +393,11 @@ gst_bus_set_sync_handler (GstBus * bus, GstBusSyncHandler func, gpointer data)
   g_return_if_fail (GST_IS_BUS (bus));
 
   GST_LOCK (bus);
+
+  /* Assert if the user attempts to replace an existing sync_handler,
+   * other than to clear it */
+  g_assert (func == NULL || bus->sync_handler == NULL);
+
   bus->sync_handler = func;
   bus->sync_handler_data = data;
   GST_UNLOCK (bus);
index ef7bf3d..1d5baff 100644 (file)
@@ -58,6 +58,12 @@ G_BEGIN_DECLS
  * @GST_EVENT_QOS: a quality message
  * @GST_EVENT_SEEK: a request for a new playback position and rate.
  * @GST_EVENT_NAVIGATION: a navigation event
+ * @GST_EVENT_CUSTOM_UP: Custom upstream event, carrying a custom GstStructure
+ * @GST_EVENT_CUSTOM_DS: Custom in-stream downstream, carrying a custom GstStructure
+ * @GST_EVENT_CUSTOM_DS_OOB: Custom out-of-band downstream event, carrying a custom GstStructure
+ * @GST_EVENT_CUSTOM_BOTH: Custom up or downstream event. In-band when travelling downstream.
+ * @GST_EVENT_CUSTOM_BOTH_OOB: Custom up or downstream out-of-band event.
+ *
  */
 typedef enum {
   GST_EVENT_UNKNOWN            = GST_EVENT_MAKE_TYPE (0, 0),
@@ -75,7 +81,11 @@ typedef enum {
   GST_EVENT_NAVIGATION         = GST_EVENT_MAKE_TYPE (9, GST_EVDIR_US),
 
   /* custom events start here */
-  GST_EVENT_CUSTOM_START       = 32
+  GST_EVENT_CUSTOM_UP          = GST_EVENT_MAKE_TYPE (32, GST_EVDIR_US),
+  GST_EVENT_CUSTOM_DS          = GST_EVENT_MAKE_TYPE (32, GST_EVDIR_DS | GST_EVSER),
+  GST_EVENT_CUSTOM_DS_OOB      = GST_EVENT_MAKE_TYPE (32, GST_EVDIR_DS),
+  GST_EVENT_CUSTOM_BOTH                = GST_EVENT_MAKE_TYPE (32, GST_EVDIR_BOTH | GST_EVSER),
+  GST_EVENT_CUSTOM_BOTH_OOB    = GST_EVENT_MAKE_TYPE (32, GST_EVDIR_BOTH)
 } GstEventType;
 
 #define GST_EVENT_TRACE_NAME   "GstEvent"
@@ -187,10 +197,12 @@ GstEvent* gst_event_new_tag               (GstTagList *taglist);
 void           gst_event_parse_tag             (GstEvent *event, GstTagList **taglist);
 
 /* filler event */
+/* FIXME: FILLER events need to be fully specified and implemented */
 GstEvent *     gst_event_new_filler            (void);
 
 
 /* QOS events */
+/* FIXME: QOS events need to be fully specified and implemented */
 GstEvent*      gst_event_new_qos               (gdouble proportion, GstClockTimeDiff diff,
                                                 GstClockTime timestamp);
 void           gst_event_parse_qos             (GstEvent *event, gdouble *proportion, GstClockTimeDiff *diff,
index c591cb9..d293f80 100644 (file)
@@ -192,7 +192,9 @@ gst_pad_class_init (GstPadClass * klass)
    * @pad: the pad that emitted the signal
    * @mini_obj: new data
    *
-   * Signals that new data is available on the pad. This signal is used internaly.
+   * Signals that new data is available on the pad. This signal is used 
+   * internally for implementing pad probes. 
+   * See gst_pad_add_*_probe functions.
    *
    * Returns: %TRUE to keep the data, %FALSE to drop it
    */
index 3a8600b..1a31209 100644 (file)
@@ -555,9 +555,16 @@ gst_queue_handle_sink_event (GstPad * pad, GstEvent * event)
       STATUS (queue, "received EOS");
       break;
     default:
-      /* we put the event in the queue, we don't have to act ourselves */
-      GST_CAT_LOG_OBJECT (queue_dataflow, queue,
-          "adding event %p of type %d", event, GST_EVENT_TYPE (event));
+      if (GST_EVENT_IS_SERIALIZED (event)) {
+        /* we put the event in the queue, we don't have to act ourselves */
+        GST_CAT_LOG_OBJECT (queue_dataflow, queue,
+            "adding event %p of type %d", event, GST_EVENT_TYPE (event));
+      } else {
+        gst_event_ref (event);
+        gst_pad_push_event (queue->srcpad, event);
+        gst_event_unref (event);
+        goto done;
+      }
       break;
   }
 
index 2fabac9..50f59e2 100644 (file)
@@ -514,8 +514,12 @@ gst_base_sink_handle_object (GstBaseSink * basesink, GstPad * pad,
   } else {
     if (!basesink->have_discont) {
       GST_ELEMENT_ERROR (basesink, STREAM, STOPPED,
-          ("received buffer without a discont"),
-          ("received buffer without a discont"));
+          ("Received buffer without a new-segment. Cannot sync to clock."),
+          ("Received buffer without a new-segment. Cannot sync to clock."));
+      basesink->have_discont = TRUE;
+      /* this means this sink will not be able to sync to the clock */
+      basesink->discont_start = 0;
+      basesink->discont_stop = 0;
     }
     basesink->preroll_queued++;
     basesink->buffers_queued++;
index 3a8600b..1a31209 100644 (file)
@@ -555,9 +555,16 @@ gst_queue_handle_sink_event (GstPad * pad, GstEvent * event)
       STATUS (queue, "received EOS");
       break;
     default:
-      /* we put the event in the queue, we don't have to act ourselves */
-      GST_CAT_LOG_OBJECT (queue_dataflow, queue,
-          "adding event %p of type %d", event, GST_EVENT_TYPE (event));
+      if (GST_EVENT_IS_SERIALIZED (event)) {
+        /* we put the event in the queue, we don't have to act ourselves */
+        GST_CAT_LOG_OBJECT (queue_dataflow, queue,
+            "adding event %p of type %d", event, GST_EVENT_TYPE (event));
+      } else {
+        gst_event_ref (event);
+        gst_pad_push_event (queue->srcpad, event);
+        gst_event_unref (event);
+        goto done;
+      }
       break;
   }
 
index f5f7fa1..d4fbc05 100644 (file)
@@ -37,6 +37,7 @@ TESTS = $(top_builddir)/tools/gst-register    \
         gst/gstbus                             \
        gst/gstcaps                             \
        gst/gstelement                          \
+       gst/gstevents                           \
        gst/gstghostpad                         \
        gst/gstiterator                         \
        gst/gstmessage                          \
diff --git a/tests/check/gst/gstevents.c b/tests/check/gst/gstevents.c
new file mode 100644 (file)
index 0000000..e4c507b
--- /dev/null
@@ -0,0 +1,370 @@
+/* GStreamer
+ * Copyright (C) 2005 Jan Schmidt <thaytan@mad.scientist.com>
+ *
+ * gstevents.c: Unit test for event handling
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#include "../gstcheck.h"
+
+GST_START_TEST (create_custom_events)
+{
+  GstEvent *event, *event2;
+  GstStructure *structure;
+
+  /* FLUSH_START */
+  {
+    event = gst_event_new_flush_start ();
+    fail_if (event == NULL);
+    fail_unless (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_START);
+    fail_unless (GST_EVENT_IS_UPSTREAM (event));
+    fail_unless (GST_EVENT_IS_DOWNSTREAM (event));
+    fail_if (GST_EVENT_IS_SERIALIZED (event));
+    gst_event_unref (event);
+  }
+  /* FLUSH_STOP */
+  {
+    event = gst_event_new_flush_stop ();
+    fail_if (event == NULL);
+    fail_unless (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP);
+    fail_unless (GST_EVENT_IS_UPSTREAM (event));
+    fail_unless (GST_EVENT_IS_DOWNSTREAM (event));
+    fail_if (GST_EVENT_IS_SERIALIZED (event));
+    gst_event_unref (event);
+  }
+  /* EOS */
+  {
+    event = gst_event_new_eos ();
+    fail_if (event == NULL);
+    fail_unless (GST_EVENT_TYPE (event) == GST_EVENT_EOS);
+    fail_if (GST_EVENT_IS_UPSTREAM (event));
+    fail_unless (GST_EVENT_IS_DOWNSTREAM (event));
+    fail_unless (GST_EVENT_IS_SERIALIZED (event));
+    gst_event_unref (event);
+  }
+  /* NEWSEGMENT */
+  {
+    gdouble rate;
+    GstFormat format;
+    gint64 start, end, base;
+
+    event = gst_event_new_newsegment (0.5, GST_FORMAT_TIME, 1, G_MAXINT64,
+        0xdeadbeef);
+    fail_if (event == NULL);
+    fail_unless (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT);
+    fail_if (GST_EVENT_IS_UPSTREAM (event));
+    fail_unless (GST_EVENT_IS_DOWNSTREAM (event));
+    fail_unless (GST_EVENT_IS_SERIALIZED (event));
+
+    gst_event_parse_newsegment (event, &rate, &format, &start, &end, &base);
+    fail_unless (rate == 0.5);
+    fail_unless (format == GST_FORMAT_TIME);
+    fail_unless (start == 1);
+    fail_unless (end == G_MAXINT64);
+    fail_unless (base == 0xdeadbeef);
+
+    gst_event_unref (event);
+  }
+  /* TAGS */
+  {
+    GstTagList *taglist = gst_tag_list_new ();
+    GstTagList *tl2 = NULL;
+
+    event = gst_event_new_tag (taglist);
+    fail_if (taglist == NULL);
+    fail_if (event == NULL);
+    fail_unless (GST_EVENT_TYPE (event) == GST_EVENT_TAG);
+    fail_if (GST_EVENT_IS_UPSTREAM (event));
+    fail_unless (GST_EVENT_IS_DOWNSTREAM (event));
+    fail_unless (GST_EVENT_IS_SERIALIZED (event));
+
+    gst_event_parse_tag (event, &tl2);
+    fail_unless (taglist == tl2);
+    gst_event_unref (event);
+  }
+
+  /* FIXME: Add tests for FILLERS and QOS when they are implemented. */
+
+  /* SEEK */
+  {
+    gdouble rate;
+    GstFormat format;
+    GstSeekFlags flags;
+    GstSeekType cur_type, stop_type;
+    gint64 cur, stop;
+
+    event = gst_event_new_seek (0.5, GST_FORMAT_BYTES,
+        GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
+        GST_SEEK_TYPE_SET, 1, GST_SEEK_TYPE_NONE, 0xdeadbeef);
+
+    fail_if (event == NULL);
+    fail_unless (GST_EVENT_TYPE (event) == GST_EVENT_SEEK);
+    fail_unless (GST_EVENT_IS_UPSTREAM (event));
+    fail_if (GST_EVENT_IS_DOWNSTREAM (event));
+    fail_if (GST_EVENT_IS_SERIALIZED (event));
+
+    gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur,
+        &stop_type, &stop);
+    fail_unless (rate == 0.5);
+    fail_unless (format == GST_FORMAT_BYTES);
+    fail_unless (flags == (GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE));
+    fail_unless (cur_type == GST_SEEK_TYPE_SET);
+    fail_unless (cur == 1);
+    fail_unless (stop_type == GST_SEEK_TYPE_NONE);
+    fail_unless (stop == 0xdeadbeef);
+
+    gst_event_unref (event);
+  }
+
+  /* NAVIGATION */
+  {
+    structure = gst_structure_new ("application/x-gst-navigation", "event",
+        G_TYPE_STRING, "key-press", "key", G_TYPE_STRING, "mon", NULL);
+    fail_if (structure == NULL);
+    event = gst_event_new_navigation (structure);
+    fail_if (event == NULL);
+    fail_unless (GST_EVENT_TYPE (event) == GST_EVENT_NAVIGATION);
+    fail_unless (GST_EVENT_IS_UPSTREAM (event));
+    fail_if (GST_EVENT_IS_DOWNSTREAM (event));
+    fail_if (GST_EVENT_IS_SERIALIZED (event));
+
+    fail_unless (gst_event_get_structure (event) == structure);
+    gst_event_unref (event);
+  }
+
+  /* Custom event types */
+  {
+    structure = gst_structure_empty_new ("application/x-custom");
+    fail_if (structure == NULL);
+    event = gst_event_new_custom (GST_EVENT_CUSTOM_UP, structure);
+    fail_if (event == NULL);
+    fail_unless (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_UP);
+    fail_unless (GST_EVENT_IS_UPSTREAM (event));
+    fail_if (GST_EVENT_IS_DOWNSTREAM (event));
+    fail_if (GST_EVENT_IS_SERIALIZED (event));
+    fail_unless (gst_event_get_structure (event) == structure);
+    gst_event_unref (event);
+
+    /* Decided not to test the other custom enum types, as they
+     * only differ by the value of the enum passed to gst_event_new_custom
+     */
+  }
+
+  /* Event copying */
+  {
+    structure = gst_structure_empty_new ("application/x-custom");
+    fail_if (structure == NULL);
+    event = gst_event_new_custom (GST_EVENT_CUSTOM_BOTH, structure);
+
+    fail_if (event == NULL);
+    event2 = gst_event_copy (event);
+    fail_if (event2 == NULL);
+    fail_unless (GST_EVENT_TYPE (event) == GST_EVENT_TYPE (event2));
+
+    /* The structure should have been duplicated */
+    fail_if (gst_event_get_structure (event) ==
+        gst_event_get_structure (event2));
+    gst_event_unref (event);
+    gst_event_unref (event2);
+  }
+}
+
+GST_END_TEST GTimeVal sent_event_time;
+GstEvent *got_event_before_q, *got_event_after_q;
+GTimeVal got_event_time;
+
+static gboolean
+event_probe (GstPad * pad, GstMiniObject ** data, gpointer user_data)
+{
+  gboolean before_q = (gboolean) user_data;
+
+  fail_unless (GST_IS_EVENT (data));
+
+  if (before_q) {
+    switch (GST_EVENT_TYPE (GST_EVENT (data))) {
+      case GST_EVENT_CUSTOM_UP:
+      case GST_EVENT_CUSTOM_BOTH:
+      case GST_EVENT_CUSTOM_BOTH_OOB:
+        gst_event_ref (data);
+        g_get_current_time (&got_event_time);
+        got_event_before_q = GST_EVENT (data);
+        break;
+      default:
+        break;
+    }
+  } else {
+    switch (GST_EVENT_TYPE (GST_EVENT (data))) {
+      case GST_EVENT_CUSTOM_DS:
+      case GST_EVENT_CUSTOM_DS_OOB:
+      case GST_EVENT_CUSTOM_BOTH:
+      case GST_EVENT_CUSTOM_BOTH_OOB:
+        gst_event_ref (data);
+        g_get_current_time (&got_event_time);
+        got_event_after_q = GST_EVENT (data);
+        break;
+      default:
+        break;
+    }
+  }
+
+  return TRUE;
+}
+
+static void test_event
+    (GstEventType type, GstPad * pad, gboolean expect_before_q)
+{
+  GstEvent *event;
+  gint i;
+
+  got_event_before_q = got_event_after_q = NULL;
+
+  event = gst_event_new_custom (type,
+      gst_structure_empty_new ("application/x-custom"));
+  g_get_current_time (&sent_event_time);
+  got_event_time.tv_sec = 0;
+  got_event_time.tv_usec = 0;
+
+  gst_pad_push_event (pad, event);
+
+  /* Wait up to 5 seconds for the event to appear */
+  if (expect_before_q) {
+    for (i = 0; i < 50; i++) {
+      g_usleep (G_USEC_PER_SEC / 10);
+      if (got_event_before_q != NULL)
+        break;
+    }
+    fail_if (got_event_before_q == NULL);
+    fail_unless (GST_EVENT_TYPE (got_event_before_q) == type);
+  } else {
+    for (i = 0; i < 50; i++) {
+      g_usleep (G_USEC_PER_SEC / 10);
+      if (got_event_after_q != NULL)
+        break;
+    }
+    fail_if (got_event_after_q == NULL);
+    fail_unless (GST_EVENT_TYPE (got_event_after_q) == type);
+  }
+};
+
+static gint64
+timediff (GTimeVal * end, GTimeVal * start)
+{
+  return (end->tv_sec - start->tv_sec) * G_USEC_PER_SEC +
+      (end->tv_usec - start->tv_usec);
+}
+
+GST_START_TEST (send_custom_events)
+{
+  /* Run some tests on custom events. Checking for serialisation and whatnot.
+   * pipeline is fakesrc ! queue ! fakesink */
+  GstBin *pipeline;
+  GstElement *fakesrc, *fakesink, *queue;
+  GstPad *srcpad, *sinkpad;
+
+  fail_if ((pipeline = (GstBin *) gst_pipeline_new ("testpipe")) == NULL);
+  fail_if ((fakesrc = gst_element_factory_make ("fakesrc", NULL)) == NULL);
+  fail_if ((fakesink = gst_element_factory_make ("fakesink", NULL)) == NULL);
+  fail_if ((queue = gst_element_factory_make ("queue", NULL)) == NULL);
+
+  gst_bin_add_many (pipeline, fakesrc, queue, fakesink, NULL);
+  fail_unless (gst_element_link_many (fakesrc, queue, fakesink, NULL));
+
+  /* Send 25 buffers per sec */
+  g_object_set (G_OBJECT (fakesrc), "silent", TRUE, "datarate", 25,
+      "sizemax", 1, "sizetype", 2, NULL);
+  g_object_set (G_OBJECT (queue), "max-size-buffers", 0, "max-size-time",
+      (guint64) 1.1 * GST_SECOND, "max-size-bytes", 0, NULL);
+  g_object_set (G_OBJECT (fakesink), "silent", TRUE, "sync", TRUE, NULL);
+
+  fail_if ((srcpad = gst_element_get_pad (fakesrc, "src")) == NULL);
+  gst_pad_add_event_probe (srcpad, (GCallback) event_probe,
+      GINT_TO_POINTER (TRUE));
+
+  fail_if ((sinkpad = gst_element_get_pad (fakesink, "sink")) == NULL);
+  gst_pad_add_event_probe (sinkpad, (GCallback) event_probe,
+      GINT_TO_POINTER (FALSE));
+
+  gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_PLAYING);
+
+  /* Upstream events */
+  test_event (GST_EVENT_CUSTOM_UP, sinkpad, TRUE);
+  fail_unless (timediff (&got_event_time, &sent_event_time) < G_USEC_PER_SEC,
+      "GST_EVENT_CUSTOM_UP took to long to reach source");
+
+  test_event (GST_EVENT_CUSTOM_BOTH, sinkpad, TRUE);
+  fail_unless (timediff (&got_event_time, &sent_event_time) < G_USEC_PER_SEC,
+      "GST_EVENT_CUSTOM_BOTH took to long to reach source");
+
+  test_event (GST_EVENT_CUSTOM_BOTH_OOB, sinkpad, TRUE);
+  fail_unless (timediff (&got_event_time, &sent_event_time) < G_USEC_PER_SEC,
+      "GST_EVENT_CUSTOM_BOTH_OOB took to long to reach source");
+
+  /* Out of band downstream events */
+  test_event (GST_EVENT_CUSTOM_DS_OOB, srcpad, FALSE);
+  fail_unless (timediff (&got_event_time, &sent_event_time) < G_USEC_PER_SEC,
+      "GST_EVENT_CUSTOM_DS_OOB took to long to reach source");
+
+  test_event (GST_EVENT_CUSTOM_BOTH_OOB, srcpad, FALSE);
+  fail_unless (timediff (&got_event_time, &sent_event_time) < G_USEC_PER_SEC,
+      "GST_EVENT_CUSTOM_BOTH_OOB took to long to reach source");
+
+  /* In-band downstream events are expected to take at least 1 second 
+   * to traverse the the queue */
+  test_event (GST_EVENT_CUSTOM_DS, srcpad, FALSE);
+  fail_unless (timediff (&got_event_time, &sent_event_time) >= G_USEC_PER_SEC,
+      "GST_EVENT_CUSTOM_BOTH_OOB arrived at sink too quickly for an in-band event");
+
+  test_event (GST_EVENT_CUSTOM_BOTH, srcpad, FALSE);
+  fail_unless (timediff (&got_event_time, &sent_event_time) >= G_USEC_PER_SEC,
+      "GST_EVENT_CUSTOM_BOTH_OOB arrived at sink too quickly for an in-band event");
+
+  gst_element_set_state (GST_ELEMENT (pipeline), GST_STATE_NULL);
+  gst_bin_watch_for_state_change (GST_BIN (pipeline));
+
+  gst_object_unref (pipeline);
+}
+
+GST_END_TEST Suite * gstevents_suite (void)
+{
+  Suite *s = suite_create ("GstEvent");
+  TCase *tc_chain = tcase_create ("customevents");
+
+  tcase_set_timeout (tc_chain, 20);
+
+  suite_add_tcase (s, tc_chain);
+  tcase_add_test (tc_chain, create_custom_events);
+  tcase_add_test (tc_chain, send_custom_events);
+  return s;
+}
+
+int
+main (int argc, char **argv)
+{
+  int nf;
+
+  Suite *s = gstevents_suite ();
+  SRunner *sr = srunner_create (s);
+
+  gst_check_init (&argc, &argv);
+
+  srunner_run_all (sr, CK_NORMAL);
+  nf = srunner_ntests_failed (sr);
+  srunner_free (sr);
+
+  return nf;
+}