2 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3 * 2000 Wim Taymans <wtay@chello.be>
5 * gstclock.c: Clock subsystem for maintaining time sync
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
25 /* #define GST_DEBUG_ENABLED */
26 #include "gst_private.h"
30 #include "gstmemchunk.h"
37 #define CLASS(clock) GST_CLOCK_CLASS (G_OBJECT_GET_CLASS (clock))
39 static GstMemChunk *_gst_clock_entries_chunk;
41 static void gst_clock_class_init (GstClockClass *klass);
42 static void gst_clock_init (GstClock *clock);
43 static void gst_clock_set_property (GObject *object, guint prop_id,
44 const GValue *value, GParamSpec *pspec);
45 static void gst_clock_get_property (GObject *object, guint prop_id,
46 GValue *value, GParamSpec * pspec);
47 static void gst_clock_update_stats (GstClock *clock);
50 static GstObjectClass *parent_class = NULL;
51 /* static guint gst_clock_signals[LAST_SIGNAL] = { 0 }; */
53 static GMutex *_gst_clock_mutex;
54 static GCond *_gst_clock_cond;
56 static inline GstClockID
57 gst_clock_entry_new (GstClock *clock, GstClockTime time,
58 GstClockTime interval, GstClockEntryType type)
62 entry = gst_mem_chunk_alloc (_gst_clock_entries_chunk);
66 entry->interval = time;
68 entry->status = GST_CLOCK_ENTRY_OK;
70 return (GstClockID) entry;
74 * gst_clock_new_single_shot_id
75 * @clock: The clockid to get a single shot notification from
76 * @time: the requested time
78 * Get an ID from the given clock to trigger a single shot
79 * notification at the requested time.
81 * Returns: An id that can be used to request the time notification.
84 gst_clock_new_single_shot_id (GstClock *clock, GstClockTime time)
86 g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);
88 return gst_clock_entry_new (clock,
91 GST_CLOCK_ENTRY_SINGLE);
95 * gst_clock_new_periodic__id
96 * @clock: The clockid to get a periodic notification id from
97 * @start_time: the requested start time
98 * @interval: the requested interval
100 * Get an ID from the given clock to trigger a periodic notification.
101 * The periodeic notifications will be start at time start_time and
102 * will then be fired with the given interval.
104 * Returns: An id that can be used to request the time notification.
107 gst_clock_new_periodic_id (GstClock *clock, GstClockTime start_time,
108 GstClockTime interval)
110 g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);
111 g_return_val_if_fail (start_time != GST_CLOCK_TIME_NONE, NULL);
112 g_return_val_if_fail (interval != 0, NULL);
114 return gst_clock_entry_new (clock,
117 GST_CLOCK_ENTRY_PERIODIC);
121 * gst_clock_id_get_time
122 * @id: The clockid to query
124 * Get the time of the clock ID
126 * Returns: the time of the given clock id
129 gst_clock_id_get_time (GstClockID id)
131 g_return_val_if_fail (id != NULL, GST_CLOCK_TIME_NONE);
133 return GST_CLOCK_ENTRY_TIME ((GstClockEntry *)id);
139 * @id: The clockid to wait on
140 * @jitter: A pointer that will contain the jitter
142 * Perform a blocking wait on the given ID. The jitter arg can be
145 * Returns: the result of the blocking wait.
148 gst_clock_id_wait (GstClockID id, GstClockTimeDiff *jitter)
150 GstClockEntry *entry;
152 GstClockReturn res = GST_CLOCK_UNSUPPORTED;
153 GstClockTime requested;
155 g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR);
157 entry = (GstClockEntry *) id;
158 requested = GST_CLOCK_ENTRY_TIME (entry);
160 if (requested == GST_CLOCK_TIME_NONE) {
161 res = GST_CLOCK_TIMEOUT;
165 clock = GST_CLOCK_ENTRY_CLOCK (entry);
167 if (CLASS (clock)->wait) {
171 res = CLASS (clock)->wait (clock, entry);
173 while (res == GST_CLOCK_ENTRY_RESTART);
176 now = gst_clock_get_time (clock);
177 *jitter = now - requested;
181 gst_clock_update_stats (clock);
186 if (entry->type == GST_CLOCK_ENTRY_SINGLE) {
187 gst_clock_id_free (id);
194 * gst_clock_wait_async
195 * @clock: a #GstClock to wait on
196 * @time: The #GstClockTime to wait for
197 * @func: The callback function
198 * @user_data: User data passed in the calback
200 * Register a callback on the given clock that will be triggered
201 * when the clock has reached the given time. A ClockID is returned
202 * that can be used to cancel the request.
204 * Returns: the result of the non blocking wait.
207 gst_clock_id_wait_async (GstClockID id,
208 GstClockCallback func, gpointer user_data)
210 GstClockEntry *entry;
212 GstClockReturn res = GST_CLOCK_UNSUPPORTED;
214 g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR);
215 g_return_val_if_fail (func != NULL, GST_CLOCK_ERROR);
217 entry = (GstClockEntry *) id;
218 clock = entry->clock;
220 if (GST_CLOCK_ENTRY_TIME (entry) == GST_CLOCK_TIME_NONE) {
221 (func) (clock, GST_CLOCK_TIME_NONE, id, user_data);
222 return GST_CLOCK_TIMEOUT;
225 if (CLASS (clock)->wait_async) {
226 res = CLASS (clock)->wait_async (clock, entry, func, user_data);
233 * gst_clock_remove_id
234 * @clock: The clock to cancel the request on
235 * @id: The id to cancel
237 * Cancel an outstanding async notification request with the given ID.
238 * This can be an ID generated with gst_clock_wait_async() or
239 * gst_clock_notify_async().
242 gst_clock_id_unschedule (GstClockID id)
244 GstClockEntry *entry;
247 g_return_if_fail (id != NULL);
249 entry = (GstClockEntry *) id;
250 clock = entry->clock;
252 if (CLASS (clock)->unschedule)
253 CLASS (clock)->unschedule (clock, entry);
258 * @id: The clockid to free
260 * Free the resources held by the given id
263 gst_clock_id_free (GstClockID id)
265 g_return_if_fail (id != NULL);
267 gst_mem_chunk_free (_gst_clock_entries_chunk, id);
271 * gst_clock_unlock_id
272 * @id: The clockid to unlock
274 * Unlock the givan ClockID.
277 gst_clock_id_unlock (GstClockID id)
279 GstClockEntry *entry;
282 g_return_if_fail (id != NULL);
284 entry = (GstClockEntry *) id;
285 clock = entry->clock;
287 if (CLASS (clock)->unlock)
288 CLASS (clock)->unlock (clock, entry);
293 * GstClock abstract base class implementation
296 gst_clock_get_type (void)
298 static GType clock_type = 0;
301 static const GTypeInfo clock_info = {
302 sizeof (GstClockClass),
305 (GClassInitFunc) gst_clock_class_init,
310 (GInstanceInitFunc) gst_clock_init,
313 clock_type = g_type_register_static (GST_TYPE_OBJECT, "GstClock",
314 &clock_info, G_TYPE_FLAG_ABSTRACT);
320 gst_clock_class_init (GstClockClass *klass)
322 GObjectClass *gobject_class;
323 GstObjectClass *gstobject_class;
325 gobject_class = (GObjectClass*) klass;
326 gstobject_class = (GstObjectClass*) klass;
328 parent_class = g_type_class_ref (GST_TYPE_OBJECT);
330 if (!g_thread_supported ())
331 g_thread_init (NULL);
333 _gst_clock_entries_chunk = gst_mem_chunk_new ("GstClockEntries",
334 sizeof (GstClockEntry), sizeof (GstClockEntry) * 32,
337 _gst_clock_mutex = g_mutex_new ();
338 _gst_clock_cond = g_cond_new ();
340 gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_clock_set_property);
341 gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_clock_get_property);
343 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_STATS,
344 g_param_spec_boolean ("stats", "Stats", "Enable clock stats",
345 FALSE, G_PARAM_READWRITE));
349 gst_clock_init (GstClock *clock)
352 clock->active = FALSE;
353 clock->start_time = 0;
354 clock->last_time = 0;
355 clock->entries = NULL;
357 clock->stats = FALSE;
359 clock->active_mutex = g_mutex_new ();
360 clock->active_cond = g_cond_new ();
364 * gst_clock_set_speed
365 * @clock: a #GstClock to modify
366 * @speed: the speed to set on the clock
368 * Sets the speed on the given clock. 1.0 is the default
371 * Returns: the new speed of the clock.
374 gst_clock_set_speed (GstClock *clock, gdouble speed)
376 g_return_val_if_fail (GST_IS_CLOCK (clock), 0.0);
378 if (CLASS (clock)->change_speed)
379 clock->speed = CLASS (clock)->change_speed (clock, clock->speed, speed);
385 * gst_clock_get_speed
386 * @clock: a #GstClock to query
388 * Gets the speed of the given clock.
390 * Returns: the speed of the clock.
393 gst_clock_get_speed (GstClock *clock)
395 g_return_val_if_fail (GST_IS_CLOCK (clock), 0.0);
401 * gst_clock_set_resolution
402 * @clock: The clock set the resolution on
403 * @resolution: The resolution to set
405 * Set the accuracy of the clock.
407 * Returns: the new resolution of the clock.
410 gst_clock_set_resolution (GstClock *clock, guint64 resolution)
412 g_return_val_if_fail (GST_IS_CLOCK (clock), 0LL);
413 g_return_val_if_fail (resolution != 0, 0LL);
415 if (CLASS (clock)->change_resolution)
416 clock->resolution = CLASS (clock)->change_resolution (clock, clock->resolution, resolution);
418 return clock->resolution;
422 * gst_clock_get_resolution
423 * @clock: The clock get the resolution of
425 * Get the accuracy of the clock.
427 * Returns: the resolution of the clock in microseconds.
430 gst_clock_get_resolution (GstClock *clock)
432 g_return_val_if_fail (GST_IS_CLOCK (clock), 0LL);
434 if (CLASS (clock)->get_resolution)
435 return CLASS (clock)->get_resolution (clock);
441 * gst_clock_set_active
442 * @clock: a #GstClock to set state of
443 * @active: flag indicating if the clock should be activated (TRUE) or deactivated
445 * Activates or deactivates the clock based on the active parameter.
446 * As soon as the clock is activated, the time will start ticking.
449 gst_clock_set_active (GstClock *clock, gboolean active)
451 GstClockTime time = 0LL;
453 g_return_if_fail (GST_IS_CLOCK (clock));
455 clock->active = active;
457 if (CLASS (clock)->get_internal_time) {
458 time = CLASS (clock)->get_internal_time (clock);
463 clock->start_time = time - clock->last_time;
464 clock->accept_discont = TRUE;
467 clock->last_time = time - clock->start_time;
468 clock->accept_discont = FALSE;
472 g_mutex_lock (clock->active_mutex);
473 g_cond_broadcast (clock->active_cond);
474 g_mutex_unlock (clock->active_mutex);
478 * gst_clock_is_active
479 * @clock: a #GstClock to query
481 * Checks if the given clock is active.
483 * Returns: TRUE if the clock is active.
486 gst_clock_is_active (GstClock *clock)
488 g_return_val_if_fail (GST_IS_CLOCK (clock), FALSE);
490 return clock->active;
495 * @clock: a #GstClock to reset
497 * Reset the clock to time 0.
500 gst_clock_reset (GstClock *clock)
502 GstClockTime time = 0LL;
504 g_return_if_fail (GST_IS_CLOCK (clock));
506 if (CLASS (clock)->get_internal_time) {
507 time = CLASS (clock)->get_internal_time (clock);
511 clock->active = FALSE;
512 clock->start_time = time;
513 clock->last_time = 0LL;
518 * gst_clock_handle_discont
519 * @clock: a #GstClock to notify of the discontinuity
520 * @time: The new time
522 * Notifies the clock of a discontinuity in time.
524 * Returns: TRUE if the clock was updated. It is possible that
525 * the clock was not updated by this call because only the first
526 * discontinuitity in the pipeline is honoured.
529 gst_clock_handle_discont (GstClock *clock, guint64 time)
531 GstClockTime itime = 0LL;
533 GST_DEBUG (GST_CAT_CLOCK, "clock discont %" G_GUINT64_FORMAT
534 " %" G_GUINT64_FORMAT " %d",
535 time, clock->start_time, clock->accept_discont);
537 if (time == GST_CLOCK_TIME_NONE)
541 if (clock->accept_discont) {
542 if (CLASS (clock)->get_internal_time) {
543 itime = CLASS (clock)->get_internal_time (clock);
548 GST_DEBUG (GST_CAT_CLOCK, "clock discont refused %" G_GUINT64_FORMAT
549 " %" G_GUINT64_FORMAT,
550 time, clock->start_time);
554 clock->start_time = itime - time;
555 clock->last_time = time;
556 clock->accept_discont = FALSE;
559 GST_DEBUG (GST_CAT_CLOCK, "new time %" G_GUINT64_FORMAT,
560 gst_clock_get_time (clock));
562 g_mutex_lock (clock->active_mutex);
563 g_cond_broadcast (clock->active_cond);
564 g_mutex_unlock (clock->active_mutex);
571 * @clock: a #GstClock to query
573 * Gets the current time of the given clock. The time is always
574 * monotonically increasing.
576 * Returns: the time of the clock.
579 gst_clock_get_time (GstClock *clock)
581 GstClockTime ret = 0LL;
583 g_return_val_if_fail (GST_IS_CLOCK (clock), 0LL);
585 if (!clock->active) {
586 /* clock is not activen return previous time */
587 ret = clock->last_time;
590 if (CLASS (clock)->get_internal_time) {
591 ret = CLASS (clock)->get_internal_time (clock) - clock->start_time;
593 /* make sure the time is increasing, else return last_time */
594 if ((gint64) ret < (gint64) clock->last_time) {
595 ret = clock->last_time;
598 clock->last_time = ret;
606 * gst_clock_get_next_id
607 * @clock: The clock to query
609 * Get the clockid of the next event.
611 * Returns: a clockid or NULL is no event is pending.
614 gst_clock_get_next_id (GstClock *clock)
616 GstClockEntry *entry = NULL;
618 g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);
622 entry = GST_CLOCK_ENTRY (clock->entries->data);
625 return (GstClockID *) entry;
629 gst_clock_update_stats (GstClock *clock)
634 gst_clock_set_property (GObject *object, guint prop_id,
635 const GValue *value, GParamSpec *pspec)
639 clock = GST_CLOCK (object);
643 clock->stats = g_value_get_boolean (value);
646 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
652 gst_clock_get_property (GObject *object, guint prop_id,
653 GValue *value, GParamSpec * pspec)
657 clock = GST_CLOCK (object);
661 g_value_set_boolean (value, clock->stats);
664 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);