gst_test_clock_has_id \
gst_test_clock_peek_next_pending_id \
gst_test_clock_wait_for_next_pending_id \
- gst_test_clock_wait_for_pending_id_count \
+ gst_test_clock_wait_for_multiple_pending_ids \
gst_test_clock_process_next_clock_id \
- gst_test_clock_get_next_entry_time
+ gst_test_clock_get_next_entry_time \
+ gst_test_clock_wait_for_multiple_pending_ids \
+ gst_test_clock_process_id_list \
+ gst_test_clock_id_list_get_latest_time \
+ gst_test_clock_id_list_free
+
LIBGSTCHECK_EXPORTED_SYMBOLS = \
$(LIBGSTCHECK_EXPORTED_VARS) \
*
* Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
* Copyright (C) 2012 Sebastian Rasmussen <sebastian.rasmussen@axis.com>
+ * Copyright (C) 2012 Havard Graff <havard@pexip.com>
+ * Copyright (C) 2013 Haakon Sporsheim <haakon@pexip.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
*
* #GstTestClock keeps track of these clock notifications. By calling
* gst_test_clock_wait_for_next_pending_id() or
- * gst_test_clock_wait_for_pending_id_count() a unit tests may wait for the
+ * gst_test_clock_wait_for_multiple_pending_ids() a unit tests may wait for the
* next one or several clock notifications to be requested. Additionally unit
* tests may release blocked waits in a controlled fashion by calling
* gst_test_clock_process_next_clock_id(). This way a unit test can control the
*
* N.B.: When a unit test waits for a certain amount of clock notifications to
* be requested in gst_test_clock_wait_for_next_pending_id() or
- * gst_test_clock_wait_for_pending_id_count() then these functions may block
+ * gst_test_clock_wait_for_multiple_pending_ids() then these functions may block
* for a long time. If they block forever then the expected clock notifications
* were never requested from #GstTestClock, and so the assumptions in the code
* of the unit test are wrong. The unit test case runner in gstcheck is
static GstClockTime
gst_test_clock_get_resolution (GstClock * clock)
{
+ (void) clock;
return 1;
}
return gst_clock_id_compare_func (ctx_a->clock_entry, ctx_b->clock_entry);
}
+static void
+process_entry_context_unlocked (GstTestClock * test_clock,
+ GstClockEntryContext * ctx)
+{
+ GstTestClockPrivate *priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock);
+ GstClockEntry *entry = ctx->clock_entry;
+
+ if (ctx->time_diff >= 0)
+ GST_CLOCK_ENTRY_STATUS (entry) = GST_CLOCK_OK;
+ else
+ GST_CLOCK_ENTRY_STATUS (entry) = GST_CLOCK_EARLY;
+
+ if (entry->func != NULL) {
+ GST_OBJECT_UNLOCK (test_clock);
+ entry->func (GST_CLOCK (test_clock), priv->internal_time, entry,
+ entry->user_data);
+ GST_OBJECT_LOCK (test_clock);
+ }
+
+ gst_test_clock_remove_entry (test_clock, entry);
+
+ if (GST_CLOCK_ENTRY_TYPE (entry) == GST_CLOCK_ENTRY_PERIODIC) {
+ GST_CLOCK_ENTRY_TIME (entry) += GST_CLOCK_ENTRY_INTERVAL (entry);
+
+ if (entry->func != NULL)
+ gst_test_clock_add_entry (test_clock, entry, NULL);
+ }
+}
+
+static GstTestClockIDList *
+gst_test_clock_get_pending_id_list_unlocked (GstTestClock * test_clock)
+{
+ GstTestClockPrivate *priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock);
+ GstTestClockIDList *result = g_new0 (GstTestClockIDList, 1);
+
+ if (priv->entry_contexts != NULL) {
+ GList *cur;
+ for (cur = priv->entry_contexts; cur != NULL; cur = cur->next) {
+ GstClockEntryContext *ctx = cur->data;
+
+ result->cur =
+ g_list_append (result->cur, gst_clock_id_ref (ctx->clock_entry));
+ }
+ }
+ result->length = g_list_length (result->cur);
+
+ return result;
+}
+
/**
* gst_test_clock_new:
*
}
/**
- * gst_test_clock_wait_for_pending_id_count:
- * @test_clock: #GstTestClock for which to await having enough pending clock
- * @count: the number of pending clock notifications to wait for
- *
- * Blocks until at least @count clock notifications have been requested from
- * @test_clock. There is no timeout for this wait, see the main description of
- * #GstTestClock.
- *
- * MT safe.
- *
- * Since: 1.2
- */
-void
-gst_test_clock_wait_for_pending_id_count (GstTestClock * test_clock,
- guint count)
-{
- GstTestClockPrivate *priv;
-
- g_return_if_fail (GST_IS_TEST_CLOCK (test_clock));
-
- priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock);
-
- GST_OBJECT_LOCK (test_clock);
-
- while (gst_test_clock_peek_id_count_unlocked (test_clock) < count)
- g_cond_wait (&priv->entry_added_cond, GST_OBJECT_GET_LOCK (test_clock));
-
- GST_OBJECT_UNLOCK (test_clock);
-}
-
-/**
* gst_test_clock_process_next_clock_id:
* @test_clock: a #GstTestClock for which to retrieve the next pending clock
* notification
result = gst_clock_id_ref (ctx->clock_entry);
}
- if (result != NULL) {
- GstClockEntry *entry = ctx->clock_entry;
-
- if (ctx->time_diff >= 0)
- GST_CLOCK_ENTRY_STATUS (entry) = GST_CLOCK_OK;
- else
- GST_CLOCK_ENTRY_STATUS (entry) = GST_CLOCK_EARLY;
-
- if (entry->func != NULL) {
- GST_OBJECT_UNLOCK (test_clock);
- entry->func (GST_CLOCK (test_clock), priv->internal_time, entry,
- entry->user_data);
- GST_OBJECT_LOCK (test_clock);
- }
-
- gst_test_clock_remove_entry (test_clock, entry);
-
- if (GST_CLOCK_ENTRY_TYPE (entry) == GST_CLOCK_ENTRY_PERIODIC) {
- GST_CLOCK_ENTRY_TIME (entry) += GST_CLOCK_ENTRY_INTERVAL (entry);
-
- if (entry->func != NULL)
- gst_test_clock_add_entry (test_clock, entry, NULL);
- }
- }
+ if (result != NULL)
+ process_entry_context_unlocked (test_clock, ctx);
GST_OBJECT_UNLOCK (test_clock);
return result;
}
+
+/**
+ * gst_test_clock_wait_for_multiple_pending_ids:
+ * @test_clock: #GstTestClock for which to await having enough pending clock
+ * @count: the number of pending clock notifications to wait for
+ * @pending_list: A #GstTestClockIDList with pending #GstClockIDs
+ *
+ * Blocks until at least @count clock notifications have been requested from
+ * @test_clock. There is no timeout for this wait, see the main description of
+ * #GstTestClock.
+ *
+ * MT safe.
+ *
+ * Since: 1.2
+ */
+void
+gst_test_clock_wait_for_multiple_pending_ids (GstTestClock * test_clock,
+ guint count, GstTestClockIDList ** pending_list)
+{
+ GstTestClockPrivate *priv;
+
+ g_return_if_fail (GST_IS_TEST_CLOCK (test_clock));
+ priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock);
+
+ GST_OBJECT_LOCK (test_clock);
+
+ while (g_list_length (priv->entry_contexts) < count)
+ g_cond_wait (&priv->entry_added_cond, GST_OBJECT_GET_LOCK (test_clock));
+
+ if (pending_list)
+ *pending_list = gst_test_clock_get_pending_id_list_unlocked (test_clock);
+
+ GST_OBJECT_UNLOCK (test_clock);
+}
+
+/**
+ * gst_test_clock_process_id_list:
+ * @test_clock: #GstTestClock for which to process the pending IDs
+ * @pending_list: A #GstTestClockIDList with pending #GstClockIDs
+ *
+ * Processes and releases the pending IDs in #GstTestClockIDList
+ *
+ * MT safe.
+ *
+ * Since: 1.2
+ */
+guint
+gst_test_clock_process_id_list (GstTestClock * test_clock,
+ GstTestClockIDList * pending_list)
+{
+ GstTestClockPrivate *priv;
+ GList *cur;
+ guint result = 0;
+
+ g_return_val_if_fail (GST_IS_TEST_CLOCK (test_clock), 0);
+ priv = GST_TEST_CLOCK_GET_PRIVATE (test_clock);
+
+ GST_OBJECT_LOCK (test_clock);
+
+ for (cur = pending_list->cur; cur != NULL; cur = cur->next) {
+ GstClockID pending_id = cur->data;
+ GstClockEntryContext *ctx =
+ gst_test_clock_lookup_entry_context (test_clock, pending_id);
+ if (ctx) {
+ process_entry_context_unlocked (test_clock, ctx);
+ result++;
+ }
+ }
+ GST_OBJECT_UNLOCK (test_clock);
+
+ return result;
+}
+
+/**
+ * gst_test_clock_id_list_get_latest_time:
+ * @pending_list: A #GstTestClockIDList with pending #GstClockIDs
+ *
+ * Finds the latest time inside the #GstTestClockIDList
+ *
+ * MT safe.
+ *
+ * Since: 1.2
+ */
+GstClockTime
+gst_test_clock_id_list_get_latest_time (GstTestClockIDList * pending_list)
+{
+ GList *cur;
+ GstClockTime result = 0;
+
+ for (cur = pending_list->cur; cur != NULL; cur = cur->next) {
+ GstClockID *pending_id = cur->data;
+ GstClockTime time = gst_clock_id_get_time (pending_id);
+ if (time > result)
+ result = time;
+ }
+
+ return result;
+}
+
+/**
+ * gst_test_clock_id_list_free:
+ * @pending_list: A #GstTestClockIDList with pending #GstClockIDs
+ *
+ * Free the supplied #GstTestClockIDList
+ *
+ * MT safe.
+ *
+ * Since: 1.2
+ */
+void
+gst_test_clock_id_list_free (GstTestClockIDList * pending_list)
+{
+ GList *cur;
+
+ for (cur = pending_list->cur; cur != NULL; cur = cur->next) {
+ GstClockID *pending_id = cur->data;
+ gst_clock_id_unref (pending_id);
+ }
+
+ g_list_free (pending_list->cur);
+ g_free (pending_list);
+}
*
* Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
* Copyright (C) 2012 Sebastian Rasmussen <sebastian.rasmussen@axis.com>
+ * Copyright (C) 2012 Havard Graff <havard@pexip.com>
+ * Copyright (C) 2013 Haakon Sporsheim <haakon@pexip.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
typedef struct _GstTestClockClass GstTestClockClass;
typedef struct _GstTestClockPrivate GstTestClockPrivate;
+typedef struct _GstTestClockIDList GstTestClockIDList;
+
/**
* GstTestClock:
*
GstClockClass parent_class;
};
+/**
+ * GstTestClockIDList:
+ * @cur: A #GList with all pending #GstClockID
+ * @length: A #guint with the length of the list
+ *
+ * A #GstTestClockIDList structure, which is returned when waiting for multiple IDs
+ *
+ * Since: 1.2
+ */
+ struct _GstTestClockIDList
+{
+ GList * cur;
+ guint length;
+};
+
GType gst_test_clock_get_type (void);
GstClock * gst_test_clock_new (void);
GstClock * gst_test_clock_new_with_start_time (GstClockTime start_time);
-void gst_test_clock_set_time (GstTestClock * test_clock,
- GstClockTime new_time);
+void gst_test_clock_set_time (GstTestClock * test_clock,
+ GstClockTime new_time);
-void gst_test_clock_advance_time (GstTestClock * test_clock,
- GstClockTimeDiff delta);
+void gst_test_clock_advance_time (GstTestClock * test_clock,
+ GstClockTimeDiff delta);
guint gst_test_clock_peek_id_count (GstTestClock * test_clock);
gboolean gst_test_clock_peek_next_pending_id (GstTestClock * test_clock,
GstClockID * pending_id);
-void gst_test_clock_wait_for_next_pending_id (GstTestClock * test_clock,
- GstClockID * pending_id);
-
-void gst_test_clock_wait_for_pending_id_count (GstTestClock * test_clock,
- guint count);
+void gst_test_clock_wait_for_next_pending_id (GstTestClock * test_clock,
+ GstClockID * pending_id);
GstClockID gst_test_clock_process_next_clock_id (GstTestClock * test_clock);
GstClockTime gst_test_clock_get_next_entry_time (GstTestClock * test_clock);
+void gst_test_clock_wait_for_multiple_pending_ids (GstTestClock * test_clock,
+ guint count,
+ GstTestClockIDList ** pending_list);
+
+guint gst_test_clock_process_id_list (GstTestClock * test_clock,
+ GstTestClockIDList * pending_list);
+
+GstClockTime gst_test_clock_id_list_get_latest_time (GstTestClockIDList * list);
+
+void gst_test_clock_id_list_free (GstTestClockIDList * list);
+
G_END_DECLS
#endif /* __GST_TEST_CLOCK_H__ */
context_b.clock_id = gst_clock_id_ref (clock_id_b);
context_b.jitter = 0;
- gst_test_clock_wait_for_pending_id_count (test_clock, 0);
+ gst_test_clock_wait_for_multiple_pending_ids (test_clock, 0, NULL);
worker_thread_b =
g_thread_new ("worker_thread_b",
test_wait_pending_single_shot_id_sync_worker, &context_b);
- gst_test_clock_wait_for_pending_id_count (test_clock, 1);
+ gst_test_clock_wait_for_multiple_pending_ids (test_clock, 1, NULL);
gst_test_clock_wait_for_next_pending_id (test_clock, &pending_id);
assert_pending_id (pending_id, clock_id_b, GST_CLOCK_ENTRY_SINGLE,
6 * GST_SECOND);
g_thread_new ("worker_thread_a",
test_wait_pending_single_shot_id_sync_worker, &context_a);
- gst_test_clock_wait_for_pending_id_count (test_clock, 2);
+ gst_test_clock_wait_for_multiple_pending_ids (test_clock, 2, NULL);
gst_test_clock_wait_for_next_pending_id (test_clock, &pending_id);
assert_pending_id (pending_id, clock_id_a, GST_CLOCK_ENTRY_SINGLE,
5 * GST_SECOND);
GST_CLOCK_OK);
gst_clock_id_unref (processed_id);
- gst_test_clock_wait_for_pending_id_count (test_clock, 1);
+ gst_test_clock_wait_for_multiple_pending_ids (test_clock, 1, NULL);
gst_test_clock_wait_for_next_pending_id (test_clock, &pending_id);
assert_pending_id (pending_id, clock_id_b, GST_CLOCK_ENTRY_SINGLE,
6 * GST_SECOND);
GST_CLOCK_OK);
gst_clock_id_unref (processed_id);
- gst_test_clock_wait_for_pending_id_count (test_clock, 0);
+ gst_test_clock_wait_for_multiple_pending_ids (test_clock, 0, NULL);
g_thread_join (worker_thread_a);
g_thread_join (worker_thread_b);
gst_object_unref (clock);
}
+GST_END_TEST;
+
+GST_START_TEST (test_processing_multiple_ids)
+{
+ GstClock *clock;
+ GstTestClock *test_clock;
+ GstClockID clock_id_a;
+ GstClockID clock_id_b;
+ SyncClockWaitContext context_a;
+ SyncClockWaitContext context_b;
+ GThread *worker_thread_a;
+ GThread *worker_thread_b;
+ GstTestClockIDList * pending_list;
+
+ clock = gst_test_clock_new_with_start_time (GST_SECOND);
+ test_clock = GST_TEST_CLOCK (clock);
+
+ /* register a wait for 5 seconds */
+ clock_id_a = gst_clock_new_single_shot_id (clock, 5 * GST_SECOND);
+ context_a.clock_id = gst_clock_id_ref (clock_id_a);
+ context_a.jitter = 0;
+ worker_thread_a =
+ g_thread_new ("worker_thread_a",
+ test_wait_pending_single_shot_id_sync_worker, &context_a);
+
+ /* register another wait for 6 seconds */
+ clock_id_b = gst_clock_new_single_shot_id (clock, 6 * GST_SECOND);
+ context_b.clock_id = gst_clock_id_ref (clock_id_b);
+ context_b.jitter = 0;
+ worker_thread_b =
+ g_thread_new ("worker_thread_b",
+ test_wait_pending_single_shot_id_sync_worker, &context_b);
+
+ /* wait for two waits */
+ gst_test_clock_wait_for_multiple_pending_ids (test_clock, 2, &pending_list);
+ /* assert they are correct */
+ assert_pending_id (pending_list->cur->data, clock_id_a, GST_CLOCK_ENTRY_SINGLE,
+ 5 * GST_SECOND);
+ assert_pending_id (pending_list->cur->next->data, clock_id_b, GST_CLOCK_ENTRY_SINGLE,
+ 6 * GST_SECOND);
+
+ /* verify we are waiting for 6 seconds as the latest time */
+ fail_unless_equals_int64 (6 * GST_SECOND,
+ gst_test_clock_id_list_get_latest_time (pending_list));
+
+ /* process both ID's at the same time */
+ gst_test_clock_process_id_list (test_clock, pending_list);
+ gst_test_clock_id_list_free (pending_list);
+
+ g_thread_join (worker_thread_a);
+ g_thread_join (worker_thread_b);
+
+ fail_unless_equals_int64 (-4 * GST_SECOND, context_a.jitter);
+ fail_unless_equals_int64 (-5 * GST_SECOND, context_b.jitter);
+
+ gst_clock_id_unref (context_a.clock_id);
+ gst_clock_id_unref (context_b.clock_id);
+
+ gst_clock_id_unref (clock_id_a);
+ gst_clock_id_unref (clock_id_b);
+
+ gst_object_unref (clock);
+}
GST_END_TEST;
GST_START_TEST (test_single_shot_async_past)
tcase_add_test (tc_chain, test_wait_pending_single_shot_id);
tcase_add_test (tc_chain, test_wait_pending_periodic_id);
tcase_add_test (tc_chain, test_single_shot_sync_simultaneous_no_timeout);
+ tcase_add_test (tc_chain, test_processing_multiple_ids);
tcase_add_test (tc_chain, test_single_shot_sync_past);
tcase_add_test (tc_chain, test_single_shot_sync_present);
tcase_add_test (tc_chain, test_single_shot_sync_future);