2 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3 * 2000 Wim Taymans <wtay@chello.be>
4 * 2004 Wim Taymans <wim@fluendo.com>
6 * gstclock.c: Clock subsystem for maintaining time sync
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
26 #include "gst_private.h"
30 #include "gstmemchunk.h"
31 #include "gstatomic_impl.h"
33 #ifndef GST_DISABLE_TRACE
34 /* #define GST_WITH_ALLOC_TRACE */
36 static GstAllocTrace *_gst_clock_entry_trace;
39 #define DEFAULT_EVENT_DIFF (GST_SECOND)
40 #define DEFAULT_MAX_DIFF (2 * GST_SECOND)
50 static GstMemChunk *_gst_clock_entries_chunk;
52 static void gst_clock_class_init (GstClockClass * klass);
53 static void gst_clock_init (GstClock * clock);
54 static void gst_clock_dispose (GObject * object);
56 static void gst_clock_set_property (GObject * object, guint prop_id,
57 const GValue * value, GParamSpec * pspec);
58 static void gst_clock_get_property (GObject * object, guint prop_id,
59 GValue * value, GParamSpec * pspec);
60 static void gst_clock_update_stats (GstClock * clock);
63 static GstObjectClass *parent_class = NULL;
65 /* static guint gst_clock_signals[LAST_SIGNAL] = { 0 }; */
68 gst_clock_entry_new (GstClock * clock, GstClockTime time,
69 GstClockTime interval, GstClockEntryType type)
73 entry = gst_mem_chunk_alloc (_gst_clock_entries_chunk);
74 #ifndef GST_DISABLE_TRACE
75 gst_alloc_trace_new (_gst_clock_entry_trace, entry);
77 GST_CAT_DEBUG (GST_CAT_CLOCK, "created entry %p", entry);
79 gst_atomic_int_init (&entry->refcount, 1);
82 entry->interval = interval;
84 entry->status = GST_CLOCK_BUSY;
86 return (GstClockID) entry;
91 * @id: The clockid to ref
93 * Increase the refcount of the given clockid.
95 * Returns: The same #GstClockID with increased refcount.
100 gst_clock_id_ref (GstClockID id)
102 g_return_val_if_fail (id != NULL, NULL);
104 gst_atomic_int_inc (&((GstClockEntry *) id)->refcount);
110 _gst_clock_id_free (GstClockID id)
112 g_return_if_fail (id != NULL);
114 GST_CAT_DEBUG (GST_CAT_CLOCK, "freed entry %p", id);
116 #ifndef GST_DISABLE_TRACE
117 gst_alloc_trace_free (_gst_clock_entry_trace, id);
119 gst_mem_chunk_free (_gst_clock_entries_chunk, id);
123 * gst_clock_id_unref:
124 * @id: The clockid to unref
126 * Unref the given clockid. When the refcount reaches 0 the
127 * #GstClockID will be freed.
132 gst_clock_id_unref (GstClockID id)
136 g_return_if_fail (id != NULL);
138 zero = gst_atomic_int_dec_and_test (&((GstClockEntry *) id)->refcount);
139 /* if we ended up with the refcount at zero, free the id */
141 _gst_clock_id_free (id);
146 * gst_clock_new_single_shot_id
147 * @clock: The clockid to get a single shot notification from
148 * @time: the requested time
150 * Get an ID from the given clock to trigger a single shot
151 * notification at the requested time. The single shot id should be
152 * unreffed after usage.
154 * Returns: An id that can be used to request the time notification.
159 gst_clock_new_single_shot_id (GstClock * clock, GstClockTime time)
161 g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);
163 return gst_clock_entry_new (clock,
164 time, GST_CLOCK_TIME_NONE, GST_CLOCK_ENTRY_SINGLE);
168 * gst_clock_new_periodic_id
169 * @clock: The clockid to get a periodic notification id from
170 * @start_time: the requested start time
171 * @interval: the requested interval
173 * Get an ID from the given clock to trigger a periodic notification.
174 * The periodeic notifications will be start at time start_time and
175 * will then be fired with the given interval. The id should be unreffed
178 * Returns: An id that can be used to request the time notification.
183 gst_clock_new_periodic_id (GstClock * clock, GstClockTime start_time,
184 GstClockTime interval)
186 g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);
187 g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (start_time), NULL);
188 g_return_val_if_fail (interval != 0, NULL);
190 return gst_clock_entry_new (clock,
191 start_time, interval, GST_CLOCK_ENTRY_PERIODIC);
195 * gst_clock_id_compare_func
197 * @id2: A clockid to compare with
199 * Compares the two GstClockID instances. This function can be used
200 * as a GCompareFunc when sorting ids.
202 * Returns: negative value if a < b; zero if a = b; positive value if a > b
207 gst_clock_id_compare_func (gconstpointer id1, gconstpointer id2)
209 GstClockEntry *entry1, *entry2;
211 entry1 = (GstClockEntry *) id1;
212 entry2 = (GstClockEntry *) id2;
214 if (GST_CLOCK_ENTRY_TIME (entry1) > GST_CLOCK_ENTRY_TIME (entry2)) {
217 if (GST_CLOCK_ENTRY_TIME (entry1) < GST_CLOCK_ENTRY_TIME (entry2)) {
221 return entry1 - entry2;
225 * gst_clock_id_get_time
226 * @id: The clockid to query
228 * Get the time of the clock ID
230 * Returns: the time of the given clock id.
235 gst_clock_id_get_time (GstClockID id)
237 g_return_val_if_fail (id != NULL, GST_CLOCK_TIME_NONE);
239 return GST_CLOCK_ENTRY_TIME ((GstClockEntry *) id);
245 * @id: The clockid to wait on
246 * @jitter: A pointer that will contain the jitter
248 * Perform a blocking wait on the given ID. The jitter arg can be
251 * Returns: the result of the blocking wait.
256 gst_clock_id_wait (GstClockID id, GstClockTimeDiff * jitter)
258 GstClockEntry *entry;
261 GstClockTime requested;
262 GstClockClass *cclass;
264 g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR);
266 entry = (GstClockEntry *) id;
267 requested = GST_CLOCK_ENTRY_TIME (entry);
269 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested)))
272 if (G_UNLIKELY (entry->status == GST_CLOCK_UNSCHEDULED))
275 clock = GST_CLOCK_ENTRY_CLOCK (entry);
276 cclass = GST_CLOCK_GET_CLASS (clock);
278 if (G_LIKELY (cclass->wait)) {
280 GST_CAT_DEBUG (GST_CAT_CLOCK, "waiting on clock entry %p", id);
281 res = cclass->wait (clock, entry);
282 GST_CAT_DEBUG (GST_CAT_CLOCK, "done waiting entry %p", id);
285 GstClockTime now = gst_clock_get_time (clock);
287 *jitter = now - requested;
289 if (entry->type == GST_CLOCK_ENTRY_PERIODIC) {
290 entry->time += entry->interval;
294 gst_clock_update_stats (clock);
297 res = GST_CLOCK_UNSUPPORTED;
304 GST_CAT_DEBUG (GST_CAT_CLOCK, "invalid time requested, returning _BADTIME");
305 return GST_CLOCK_BADTIME;
309 GST_CAT_DEBUG (GST_CAT_CLOCK, "entry was unscheduled return _UNSCHEDULED");
310 return GST_CLOCK_UNSCHEDULED;
315 * gst_clock_id_wait_async:
316 * @id: a #GstClockID to wait on
317 * @func: The callback function
318 * @user_data: User data passed in the calback
320 * Register a callback on the given clockid with the given
321 * function and user_data. When passing an id with an invalid
322 * time to this function, the callback will be called immediatly
323 * with a time set to GST_CLOCK_TIME_NONE. The callback will
324 * be called when the time of the id has been reached.
326 * Returns: the result of the non blocking wait.
331 gst_clock_id_wait_async (GstClockID id,
332 GstClockCallback func, gpointer user_data)
334 GstClockEntry *entry;
337 GstClockClass *cclass;
338 GstClockTime requested;
340 g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR);
341 g_return_val_if_fail (func != NULL, GST_CLOCK_ERROR);
343 entry = (GstClockEntry *) id;
344 requested = GST_CLOCK_ENTRY_TIME (entry);
345 clock = GST_CLOCK_ENTRY_CLOCK (entry);
347 if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested)))
350 if (G_UNLIKELY (entry->status == GST_CLOCK_UNSCHEDULED))
353 cclass = GST_CLOCK_GET_CLASS (clock);
355 if (cclass->wait_async) {
357 entry->user_data = user_data;
359 res = cclass->wait_async (clock, entry);
361 res = GST_CLOCK_UNSUPPORTED;
368 (func) (clock, GST_CLOCK_TIME_NONE, id, user_data);
369 GST_CAT_DEBUG (GST_CAT_CLOCK, "invalid time requested, returning _BADTIME");
370 return GST_CLOCK_BADTIME;
374 GST_CAT_DEBUG (GST_CAT_CLOCK, "entry was unscheduled return _UNSCHEDULED");
375 return GST_CLOCK_UNSCHEDULED;
380 * gst_clock_id_unschedule:
381 * @id: The id to unschedule
383 * Cancel an outstanding request with the given ID. This can either
384 * be an outstanding async notification or a pending sync notification.
385 * After this call, the @id cannot be used anymore to receive sync or
386 * async notifications, you need to create a new GstClockID.
391 gst_clock_id_unschedule (GstClockID id)
393 GstClockEntry *entry;
395 GstClockClass *cclass;
397 g_return_if_fail (id != NULL);
399 entry = (GstClockEntry *) id;
400 clock = entry->clock;
402 cclass = GST_CLOCK_GET_CLASS (clock);
404 if (cclass->unschedule)
405 cclass->unschedule (clock, entry);
410 * GstClock abstract base class implementation
413 gst_clock_get_type (void)
415 static GType clock_type = 0;
418 static const GTypeInfo clock_info = {
419 sizeof (GstClockClass),
422 (GClassInitFunc) gst_clock_class_init,
427 (GInstanceInitFunc) gst_clock_init,
431 clock_type = g_type_register_static (GST_TYPE_OBJECT, "GstClock",
432 &clock_info, G_TYPE_FLAG_ABSTRACT);
438 gst_clock_class_init (GstClockClass * klass)
440 GObjectClass *gobject_class;
441 GstObjectClass *gstobject_class;
443 gobject_class = (GObjectClass *) klass;
444 gstobject_class = (GstObjectClass *) klass;
446 parent_class = g_type_class_ref (GST_TYPE_OBJECT);
448 if (!g_thread_supported ())
449 g_thread_init (NULL);
451 _gst_clock_entries_chunk = gst_mem_chunk_new ("GstClockEntries",
452 sizeof (GstClockEntry), sizeof (GstClockEntry) * 32, G_ALLOC_AND_FREE);
454 #ifndef GST_DISABLE_TRACE
455 _gst_clock_entry_trace =
456 gst_alloc_trace_register (GST_CLOCK_ENTRY_TRACE_NAME);
459 gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_clock_dispose);
460 gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_clock_set_property);
461 gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_clock_get_property);
463 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_STATS,
464 g_param_spec_boolean ("stats", "Stats", "Enable clock stats",
465 FALSE, G_PARAM_READWRITE));
469 gst_clock_init (GstClock * clock)
472 clock->last_time = 0;
473 clock->entries = NULL;
474 clock->entries_changed = g_cond_new ();
476 clock->stats = FALSE;
480 gst_clock_dispose (GObject * object)
482 GstClock *clock = GST_CLOCK (object);
484 g_cond_free (clock->entries_changed);
486 G_OBJECT_CLASS (parent_class)->dispose (object);
490 * gst_clock_set_resolution
491 * @clock: The clock set the resolution on
492 * @resolution: The resolution to set
494 * Set the accuracy of the clock.
496 * Returns: the new resolution of the clock.
499 gst_clock_set_resolution (GstClock * clock, guint64 resolution)
501 GstClockClass *cclass;
503 g_return_val_if_fail (GST_IS_CLOCK (clock), G_GINT64_CONSTANT (0));
504 g_return_val_if_fail (resolution != 0, G_GINT64_CONSTANT (0));
506 cclass = GST_CLOCK_GET_CLASS (clock);
508 if (cclass->change_resolution)
510 cclass->change_resolution (clock, clock->resolution, resolution);
512 return clock->resolution;
516 * gst_clock_get_resolution
517 * @clock: The clock get the resolution of
519 * Get the accuracy of the clock.
521 * Returns: the resolution of the clock in microseconds.
526 gst_clock_get_resolution (GstClock * clock)
528 GstClockClass *cclass;
530 g_return_val_if_fail (GST_IS_CLOCK (clock), G_GINT64_CONSTANT (0));
532 cclass = GST_CLOCK_GET_CLASS (clock);
534 if (cclass->get_resolution)
535 return cclass->get_resolution (clock);
537 return G_GINT64_CONSTANT (1);
541 * gst_clock_adjust_unlocked
542 * @clock: a #GstClock to use
543 * @internal: a clock time
545 * Converts the given @internal clock time to the real time, adjusting
546 * and making sure that the returned time is increasing.
547 * This function should be called with the clock lock held.
549 * Returns: the converted time of the clock.
554 gst_clock_adjust_unlocked (GstClock * clock, GstClockTime internal)
558 ret = internal + clock->adjust;
559 /* make sure the time is increasing, else return last_time */
560 if ((gint64) ret < (gint64) clock->last_time) {
561 ret = clock->last_time;
563 clock->last_time = ret;
570 * @clock: a #GstClock to query
572 * Gets the current time of the given clock. The time is always
573 * monotonically increasing.
575 * Returns: the time of the clock. Or GST_CLOCK_TIME_NONE when
576 * giving wrong input.
581 gst_clock_get_time (GstClock * clock)
584 GstClockClass *cclass;
586 g_return_val_if_fail (GST_IS_CLOCK (clock), GST_CLOCK_TIME_NONE);
588 cclass = GST_CLOCK_GET_CLASS (clock);
590 if (cclass->get_internal_time) {
591 ret = cclass->get_internal_time (clock);
593 ret = G_GINT64_CONSTANT (0);
595 GST_CAT_DEBUG (GST_CAT_CLOCK, "internal time %" GST_TIME_FORMAT,
596 GST_TIME_ARGS (ret));
599 ret = gst_clock_adjust_unlocked (clock, ret);
602 GST_CAT_DEBUG (GST_CAT_CLOCK, "adjusted time %" GST_TIME_FORMAT,
603 GST_TIME_ARGS (ret));
609 * gst_clock_set_time_adjust
610 * @clock: a #GstClock to adjust
611 * @adjust: the adjust value
613 * Adjusts the current time of the clock with the adjust value.
614 * A positive value moves the clock forwards and a backwards value
615 * moves it backwards. Note that _get_time() always returns
616 * increasing values so when you move the clock backwards, _get_time()
617 * will report the previous value until the clock catches up.
622 gst_clock_set_time_adjust (GstClock * clock, GstClockTime adjust)
624 g_return_if_fail (GST_IS_CLOCK (clock));
627 clock->adjust = adjust;
632 gst_clock_update_stats (GstClock * clock)
637 gst_clock_set_property (GObject * object, guint prop_id,
638 const GValue * value, GParamSpec * pspec)
642 clock = GST_CLOCK (object);
646 clock->stats = g_value_get_boolean (value);
647 g_object_notify (object, "stats");
650 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
656 gst_clock_get_property (GObject * object, guint prop_id,
657 GValue * value, GParamSpec * pspec)
661 clock = GST_CLOCK (object);
665 g_value_set_boolean (value, clock->stats);
668 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);