mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-09-08 13:18:52 +00:00
Original commit message from CVS: Patch by: René Stadler <mail at renestadler dot de> * gst/gstclock.c: (gst_clock_id_wait): Make period ids add the interval to the origial requested time instead of the possibly updated time which can be wrong when there are multiple waiters for the same id. Fixes #382592. * gst/gstsystemclock.c: (gst_system_clock_async_thread), (gst_system_clock_id_wait_jitter_unlocked), (gst_system_clock_id_wait_jitter): Fix restart in the async notify thread when an async entry is added to the front of the list. Fixes #381492. * tests/check/gst/gstsystemclock.c: (store_callback), (notify_callback), (GST_START_TEST), (gst_systemclock_suite): Added test for multiple async waits. Added test for async wait order.
318 lines
10 KiB
C
318 lines
10 KiB
C
/* GStreamer
|
|
* Copyright (C) 2005 Wim Taymans <wim@fluendo.com>
|
|
*
|
|
* gstsystemclock.c: Unit test for GstSystemClock
|
|
*
|
|
* 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 <gst/check/gstcheck.h>
|
|
|
|
GST_START_TEST (test_signedness)
|
|
{
|
|
GstClockTime time[] = { 0, 1, G_MAXUINT64 / GST_SECOND };
|
|
GstClockTimeDiff diff[] =
|
|
{ 0, 1, -1, G_MAXINT64 / GST_SECOND, G_MININT64 / GST_SECOND };
|
|
guint i;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (time); i++) {
|
|
fail_if (time[i] != (time[i] * GST_SECOND / GST_SECOND));
|
|
}
|
|
for (i = 0; i < G_N_ELEMENTS (diff); i++) {
|
|
fail_if (diff[i] != (diff[i] * GST_SECOND / GST_SECOND));
|
|
}
|
|
}
|
|
|
|
GST_END_TEST
|
|
#define TIME_UNIT (GST_SECOND / 5)
|
|
static void
|
|
gst_clock_debug (GstClock * clock)
|
|
{
|
|
GstClockTime time;
|
|
|
|
time = gst_clock_get_time (clock);
|
|
g_message ("Clock info: time %" G_GUINT64_FORMAT "\n", time);
|
|
}
|
|
|
|
static gboolean
|
|
ok_callback (GstClock * clock, GstClockTime time,
|
|
GstClockID id, gpointer user_data)
|
|
{
|
|
g_message ("unlocked async id %p\n", id);
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
error_callback (GstClock * clock, GstClockTime time,
|
|
GstClockID id, gpointer user_data)
|
|
{
|
|
g_message ("unlocked unscheduled async id %p, this is wrong\n", id);
|
|
fail_if (TRUE);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
store_callback (GstClock * clock, GstClockTime time,
|
|
GstClockID id, gpointer user_data)
|
|
{
|
|
GstClockID *store_id = user_data;
|
|
|
|
g_message ("unlocked async id %p\n", id);
|
|
*store_id = id;
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
notify_callback (GstClock * clock, GstClockTime time,
|
|
GstClockID id, gpointer user_data)
|
|
{
|
|
gboolean *ret = (gboolean *) user_data;
|
|
|
|
if (ret != NULL)
|
|
*ret = TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
GST_START_TEST (test_single_shot)
|
|
{
|
|
GstClock *clock;
|
|
GstClockID id, id2;
|
|
GstClockTime base;
|
|
GstClockReturn result;
|
|
|
|
clock = gst_system_clock_obtain ();
|
|
fail_unless (clock != NULL, "Could not create instance of GstSystemClock");
|
|
|
|
gst_clock_debug (clock);
|
|
base = gst_clock_get_time (clock);
|
|
|
|
id = gst_clock_new_single_shot_id (clock, base + TIME_UNIT);
|
|
fail_unless (id != NULL, "Could not create single shot id");
|
|
|
|
g_message ("waiting one second\n");
|
|
result = gst_clock_id_wait (id, NULL);
|
|
gst_clock_debug (clock);
|
|
fail_unless (result == GST_CLOCK_OK, "Waiting did not return OK");
|
|
|
|
g_message ("waiting in the past\n");
|
|
result = gst_clock_id_wait (id, NULL);
|
|
gst_clock_debug (clock);
|
|
fail_unless (result == GST_CLOCK_EARLY, "Waiting did not return EARLY");
|
|
gst_clock_id_unref (id);
|
|
|
|
id = gst_clock_new_single_shot_id (clock, base + 2 * TIME_UNIT);
|
|
g_message ("waiting one second async id %p\n", id);
|
|
result = gst_clock_id_wait_async (id, ok_callback, NULL);
|
|
gst_clock_id_unref (id);
|
|
fail_unless (result == GST_CLOCK_OK, "Waiting did not return OK");
|
|
g_usleep (TIME_UNIT / (2 * 1000));
|
|
|
|
id = gst_clock_new_single_shot_id (clock, base + 5 * TIME_UNIT);
|
|
g_message ("waiting one second async, with cancel on id %p\n", id);
|
|
result = gst_clock_id_wait_async (id, error_callback, NULL);
|
|
fail_unless (result == GST_CLOCK_OK, "Waiting did not return OK");
|
|
g_usleep (TIME_UNIT / (2 * 1000));
|
|
g_message ("cancel id %p after half a time unit\n", id);
|
|
gst_clock_id_unschedule (id);
|
|
gst_clock_id_unref (id);
|
|
g_message ("canceled id %p\n", id);
|
|
|
|
g_message ("waiting multiple one second async, with cancel\n");
|
|
id = gst_clock_new_single_shot_id (clock, base + 5 * TIME_UNIT);
|
|
id2 = gst_clock_new_single_shot_id (clock, base + 6 * TIME_UNIT);
|
|
g_message ("waiting id %p\n", id);
|
|
result = gst_clock_id_wait_async (id, ok_callback, NULL);
|
|
fail_unless (result == GST_CLOCK_OK, "Waiting did not return OK");
|
|
gst_clock_id_unref (id);
|
|
g_message ("waiting id %p\n", id2);
|
|
result = gst_clock_id_wait_async (id2, error_callback, NULL);
|
|
fail_unless (result == GST_CLOCK_OK, "Waiting did not return OK");
|
|
g_usleep (TIME_UNIT / (2 * 1000));
|
|
g_message ("cancel id %p after half a time unit\n", id2);
|
|
gst_clock_id_unschedule (id2);
|
|
g_message ("canceled id %p\n", id2);
|
|
gst_clock_id_unref (id2);
|
|
g_usleep (TIME_UNIT / (2 * 1000));
|
|
}
|
|
|
|
GST_END_TEST
|
|
GST_START_TEST (test_periodic_shot)
|
|
{
|
|
GstClock *clock;
|
|
GstClockID id, id2;
|
|
GstClockTime base;
|
|
GstClockReturn result;
|
|
|
|
clock = gst_system_clock_obtain ();
|
|
fail_unless (clock != NULL, "Could not create instance of GstSystemClock");
|
|
|
|
gst_clock_debug (clock);
|
|
base = gst_clock_get_time (clock);
|
|
|
|
/* signal every half a time unit */
|
|
id = gst_clock_new_periodic_id (clock, base + TIME_UNIT, TIME_UNIT / 2);
|
|
fail_unless (id != NULL, "Could not create periodic id");
|
|
|
|
g_message ("waiting one time unit\n");
|
|
result = gst_clock_id_wait (id, NULL);
|
|
gst_clock_debug (clock);
|
|
fail_unless (result == GST_CLOCK_OK, "Waiting did not return OK");
|
|
|
|
g_message ("waiting for the next\n");
|
|
result = gst_clock_id_wait (id, NULL);
|
|
gst_clock_debug (clock);
|
|
fail_unless (result == GST_CLOCK_OK, "Waiting did not return OK");
|
|
|
|
g_message ("waiting for the next async %p\n", id);
|
|
result = gst_clock_id_wait_async (id, ok_callback, NULL);
|
|
fail_unless (result == GST_CLOCK_OK, "Waiting did not return OK");
|
|
g_usleep (TIME_UNIT / (2 * 1000));
|
|
|
|
g_message ("waiting some more for the next async %p\n", id);
|
|
result = gst_clock_id_wait_async (id, ok_callback, NULL);
|
|
fail_unless (result == GST_CLOCK_OK, "Waiting did not return OK");
|
|
g_usleep (TIME_UNIT / (2 * 1000));
|
|
|
|
id2 = gst_clock_new_periodic_id (clock, base + TIME_UNIT, TIME_UNIT / 2);
|
|
fail_unless (id2 != NULL, "Could not create second periodic id");
|
|
|
|
g_message ("waiting some more for another async %p\n", id2);
|
|
result = gst_clock_id_wait_async (id2, ok_callback, NULL);
|
|
fail_unless (result == GST_CLOCK_OK, "Waiting did not return OK");
|
|
g_usleep (TIME_UNIT / (2 * 1000));
|
|
|
|
g_message ("unschedule %p\n", id);
|
|
gst_clock_id_unschedule (id);
|
|
|
|
/* entry cannot be used again */
|
|
result = gst_clock_id_wait_async (id, error_callback, NULL);
|
|
fail_unless (result == GST_CLOCK_UNSCHEDULED,
|
|
"Waiting did not return UNSCHEDULED");
|
|
result = gst_clock_id_wait (id, NULL);
|
|
fail_unless (result == GST_CLOCK_UNSCHEDULED,
|
|
"Waiting did not return UNSCHEDULED");
|
|
g_usleep (TIME_UNIT / (2 * 1000));
|
|
|
|
/* clean up */
|
|
gst_clock_id_unref (id);
|
|
}
|
|
|
|
GST_END_TEST
|
|
GST_START_TEST (test_async_order)
|
|
{
|
|
GstClock *clock;
|
|
GstClockID id1, id2, last_id = NULL;
|
|
GstClockTime base;
|
|
GstClockReturn result;
|
|
|
|
clock = gst_system_clock_obtain ();
|
|
fail_unless (clock != NULL, "Could not create instance of GstSystemClock");
|
|
|
|
gst_clock_debug (clock);
|
|
base = gst_clock_get_time (clock);
|
|
|
|
id1 = gst_clock_new_single_shot_id (clock, base + 2 * TIME_UNIT);
|
|
id2 = gst_clock_new_single_shot_id (clock, base + 1 * TIME_UNIT);
|
|
result = gst_clock_id_wait_async (id1, store_callback, &last_id);
|
|
fail_unless (result == GST_CLOCK_OK, "Waiting did not return OK");
|
|
g_usleep (TIME_UNIT / (2 * 1000));
|
|
result = gst_clock_id_wait_async (id2, store_callback, &last_id);
|
|
fail_unless (result == GST_CLOCK_OK, "Waiting did not return OK");
|
|
g_usleep (TIME_UNIT / 1000);
|
|
fail_unless (last_id == id2, "Expected notification for id2 to come first");
|
|
g_usleep (TIME_UNIT / 1000);
|
|
fail_unless (last_id == id1, "Missing notification for id1");
|
|
gst_clock_id_unref (id1);
|
|
gst_clock_id_unref (id2);
|
|
}
|
|
|
|
GST_END_TEST
|
|
GST_START_TEST (test_periodic_multi)
|
|
{
|
|
GstClock *clock;
|
|
GstClockID clock_id;
|
|
GstClockTime base;
|
|
GstClockReturn result;
|
|
gboolean got_callback = FALSE;
|
|
|
|
clock = gst_system_clock_obtain ();
|
|
fail_unless (clock != NULL, "Could not create instance of GstSystemClock");
|
|
|
|
gst_clock_debug (clock);
|
|
base = gst_clock_get_time (clock);
|
|
|
|
clock_id = gst_clock_new_periodic_id (clock, base + TIME_UNIT, TIME_UNIT);
|
|
gst_clock_id_wait (clock_id, NULL);
|
|
fail_unless (gst_clock_get_time (clock) >= base + TIME_UNIT);
|
|
fail_unless (gst_clock_get_time (clock) < base + 2 * TIME_UNIT);
|
|
|
|
/* now perform a concurrent wait and wait_async */
|
|
|
|
result = gst_clock_id_wait_async (clock_id, notify_callback, &got_callback);
|
|
fail_unless (result == GST_CLOCK_OK, "Async waiting did not return OK");
|
|
fail_unless (got_callback == FALSE);
|
|
result = gst_clock_id_wait (clock_id, NULL);
|
|
fail_unless (result == GST_CLOCK_OK, "Waiting did not return OK");
|
|
fail_unless (gst_clock_get_time (clock) >= base + 2 * TIME_UNIT);
|
|
/* give the async thread some time to call our callback: */
|
|
g_usleep (TIME_UNIT / (10 * 1000));
|
|
fail_unless (got_callback == TRUE, "got no async callback (1)");
|
|
fail_unless (gst_clock_get_time (clock) < base + 3 * TIME_UNIT);
|
|
got_callback = FALSE;
|
|
|
|
result = gst_clock_id_wait (clock_id, NULL);
|
|
fail_unless (result == GST_CLOCK_OK, "Waiting did not return OK");
|
|
fail_unless (gst_clock_get_time (clock) >= base + 3 * TIME_UNIT);
|
|
/* give the async thread some time to call our callback: */
|
|
g_usleep (TIME_UNIT / (10 * 1000));
|
|
fail_unless (got_callback == TRUE, "got no async callback (2)");
|
|
fail_unless (gst_clock_get_time (clock) < base + 4 * TIME_UNIT);
|
|
}
|
|
|
|
GST_END_TEST
|
|
GST_START_TEST (test_diff)
|
|
{
|
|
GstClockTime time1[] =
|
|
{ 0, -1, 0, 1, 2 * GST_SECOND, -GST_SECOND, -GST_SECOND };
|
|
GstClockTime time2[] =
|
|
{ 0, 1, 1, 0, 1 * GST_SECOND, -GST_SECOND, GST_SECOND };
|
|
GstClockTimeDiff d[] = { 0, 2, 1, -1, -GST_SECOND, 0, 2 * GST_SECOND };
|
|
guint i;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (d); i++) {
|
|
fail_if (d[i] != GST_CLOCK_DIFF (time1[i], time2[i]));
|
|
}
|
|
}
|
|
GST_END_TEST Suite *
|
|
gst_systemclock_suite (void)
|
|
{
|
|
Suite *s = suite_create ("GstSystemClock");
|
|
TCase *tc_chain = tcase_create ("waiting");
|
|
|
|
suite_add_tcase (s, tc_chain);
|
|
tcase_add_test (tc_chain, test_signedness);
|
|
tcase_add_test (tc_chain, test_single_shot);
|
|
tcase_add_test (tc_chain, test_periodic_shot);
|
|
tcase_add_test (tc_chain, test_periodic_multi);
|
|
tcase_add_test (tc_chain, test_async_order);
|
|
tcase_add_test (tc_chain, test_diff);
|
|
|
|
return s;
|
|
}
|
|
|
|
GST_CHECK_MAIN (gst_systemclock);
|