From 8377afc0bac3ec1164e83024f4f1c3d7c8696b64 Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Thu, 18 Aug 2005 15:31:28 +0000 Subject: [PATCH] check/: I wrote a test! 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. --- ChangeLog | 34 ++++ check/Makefile.am | 1 + check/gst/gstevents.c | 370 +++++++++++++++++++++++++++++++++++++++++++ docs/design/part-seeking.txt | 2 +- docs/gst/tmpl/gstevent.sgml | 6 +- gst/base/gstbasesink.c | 8 +- gst/gstbus.c | 12 +- gst/gstevent.h | 14 +- gst/gstpad.c | 4 +- gst/gstqueue.c | 13 +- libs/gst/base/gstbasesink.c | 8 +- plugins/elements/gstqueue.c | 13 +- tests/check/Makefile.am | 1 + tests/check/gst/gstevents.c | 370 +++++++++++++++++++++++++++++++++++++++++++ 14 files changed, 840 insertions(+), 16 deletions(-) create mode 100644 check/gst/gstevents.c create mode 100644 tests/check/gst/gstevents.c diff --git a/ChangeLog b/ChangeLog index 2469834..6c1ac9b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,37 @@ +2005-08-18 Jan Schmidt + + * 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 * gst/gstpipeline.c (gst_pipeline_change_state): Interpret diff --git a/check/Makefile.am b/check/Makefile.am index f5f7fa1..d4fbc05 100644 --- a/check/Makefile.am +++ b/check/Makefile.am @@ -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 index 0000000..e4c507b --- /dev/null +++ b/check/gst/gstevents.c @@ -0,0 +1,370 @@ +/* GStreamer + * Copyright (C) 2005 Jan Schmidt + * + * 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; +} diff --git a/docs/design/part-seeking.txt b/docs/design/part-seeking.txt index c8c8367..d9affcd 100644 --- a/docs/design/part-seeking.txt +++ b/docs/design/part-seeking.txt @@ -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 diff --git a/docs/gst/tmpl/gstevent.sgml b/docs/gst/tmpl/gstevent.sgml index 4872e40..ff29d63 100644 --- a/docs/gst/tmpl/gstevent.sgml +++ b/docs/gst/tmpl/gstevent.sgml @@ -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: diff --git a/gst/base/gstbasesink.c b/gst/base/gstbasesink.c index 2fabac9..50f59e2 100644 --- a/gst/base/gstbasesink.c +++ b/gst/base/gstbasesink.c @@ -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++; diff --git a/gst/gstbus.c b/gst/gstbus.c index b5da3ca..1362f2b 100644 --- a/gst/gstbus.c +++ b/gst/gstbus.c @@ -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); diff --git a/gst/gstevent.h b/gst/gstevent.h index ef7bf3d..1d5baff 100644 --- a/gst/gstevent.h +++ b/gst/gstevent.h @@ -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, diff --git a/gst/gstpad.c b/gst/gstpad.c index c591cb9..d293f80 100644 --- a/gst/gstpad.c +++ b/gst/gstpad.c @@ -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 */ diff --git a/gst/gstqueue.c b/gst/gstqueue.c index 3a8600b..1a31209 100644 --- a/gst/gstqueue.c +++ b/gst/gstqueue.c @@ -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; } diff --git a/libs/gst/base/gstbasesink.c b/libs/gst/base/gstbasesink.c index 2fabac9..50f59e2 100644 --- a/libs/gst/base/gstbasesink.c +++ b/libs/gst/base/gstbasesink.c @@ -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++; diff --git a/plugins/elements/gstqueue.c b/plugins/elements/gstqueue.c index 3a8600b..1a31209 100644 --- a/plugins/elements/gstqueue.c +++ b/plugins/elements/gstqueue.c @@ -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; } diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index f5f7fa1..d4fbc05 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -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 index 0000000..e4c507b --- /dev/null +++ b/tests/check/gst/gstevents.c @@ -0,0 +1,370 @@ +/* GStreamer + * Copyright (C) 2005 Jan Schmidt + * + * 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; +} -- 2.7.4