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 #include "gst_private.h"
29 #include "gstmemchunk.h"
31 #ifndef GST_DISABLE_TRACE
32 /* #define GST_WITH_ALLOC_TRACE */
34 static GstAllocTrace *_gst_clock_entry_trace;
37 #define DEFAULT_EVENT_DIFF (GST_SECOND)
38 #define DEFAULT_MAX_DIFF (2 * GST_SECOND)
47 static GstMemChunk *_gst_clock_entries_chunk;
49 void gst_clock_id_unlock (GstClockID id);
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;
64 /* static guint gst_clock_signals[LAST_SIGNAL] = { 0 }; */
67 gst_clock_entry_new (GstClock *clock, GstClockTime time,
68 GstClockTime interval, GstClockEntryType type)
72 entry = gst_mem_chunk_alloc (_gst_clock_entries_chunk);
73 #ifndef GST_DISABLE_TRACE
74 gst_alloc_trace_new (_gst_clock_entry_trace, entry);
79 entry->interval = time;
81 entry->status = GST_CLOCK_ENTRY_OK;
83 return (GstClockID) entry;
87 * gst_clock_new_single_shot_id
88 * @clock: The clockid to get a single shot notification from
89 * @time: the requested time
91 * Get an ID from the given clock to trigger a single shot
92 * notification at the requested time.
94 * Returns: An id that can be used to request the time notification.
97 gst_clock_new_single_shot_id (GstClock *clock, GstClockTime time)
99 g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);
101 return gst_clock_entry_new (clock,
104 GST_CLOCK_ENTRY_SINGLE);
108 * gst_clock_new_periodic_id
109 * @clock: The clockid to get a periodic notification id from
110 * @start_time: the requested start time
111 * @interval: the requested interval
113 * Get an ID from the given clock to trigger a periodic notification.
114 * The periodeic notifications will be start at time start_time and
115 * will then be fired with the given interval.
117 * Returns: An id that can be used to request the time notification.
120 gst_clock_new_periodic_id (GstClock *clock, GstClockTime start_time,
121 GstClockTime interval)
123 g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);
124 g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (start_time), NULL);
125 g_return_val_if_fail (interval != 0, NULL);
127 return gst_clock_entry_new (clock,
130 GST_CLOCK_ENTRY_PERIODIC);
134 * gst_clock_id_get_time
135 * @id: The clockid to query
137 * Get the time of the clock ID
139 * Returns: the time of the given clock id
142 gst_clock_id_get_time (GstClockID id)
144 g_return_val_if_fail (id != NULL, GST_CLOCK_TIME_NONE);
146 return GST_CLOCK_ENTRY_TIME ((GstClockEntry *)id);
152 * @id: The clockid to wait on
153 * @jitter: A pointer that will contain the jitter
155 * Perform a blocking wait on the given ID. The jitter arg can be
158 * Returns: the result of the blocking wait.
161 gst_clock_id_wait (GstClockID id, GstClockTimeDiff *jitter)
163 GstClockEntry *entry;
165 GstClockReturn res = GST_CLOCK_UNSUPPORTED;
166 GstClockTime requested;
167 GstClockClass *cclass;
169 g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR);
171 entry = (GstClockEntry *) id;
172 requested = GST_CLOCK_ENTRY_TIME (entry);
174 if (! GST_CLOCK_TIME_IS_VALID (requested)) {
175 GST_CAT_DEBUG (GST_CAT_CLOCK, "invalid time requested, returning _TIMEOUT");
176 return GST_CLOCK_TIMEOUT;
179 clock = GST_CLOCK_ENTRY_CLOCK (entry);
180 cclass = GST_CLOCK_GET_CLASS (clock);
186 clock->entries = g_list_prepend (clock->entries, entry);
189 GST_CAT_DEBUG (GST_CAT_CLOCK, "waiting on clock");
191 res = cclass->wait (clock, entry);
193 while (res == GST_CLOCK_ENTRY_RESTART);
194 GST_CAT_DEBUG (GST_CAT_CLOCK, "done waiting");
197 clock->entries = g_list_remove (clock->entries, entry);
201 now = gst_clock_get_time (clock);
202 *jitter = now - requested;
206 gst_clock_update_stats (clock);
214 * gst_clock_id_wait_async:
215 * @id: a #GstClockID to wait on
216 * @func: The callback function
217 * @user_data: User data passed in the calback
219 * Register a callback on the given clockid with the given
220 * function and user_data.
222 * Returns: the result of the non blocking wait.
225 gst_clock_id_wait_async (GstClockID id,
226 GstClockCallback func, gpointer user_data)
228 GstClockEntry *entry;
230 GstClockReturn res = GST_CLOCK_UNSUPPORTED;
231 GstClockClass *cclass;
233 g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR);
234 g_return_val_if_fail (func != NULL, GST_CLOCK_ERROR);
236 entry = (GstClockEntry *) id;
237 clock = entry->clock;
239 if (! GST_CLOCK_TIME_IS_VALID (GST_CLOCK_ENTRY_TIME (entry))) {
240 (func) (clock, GST_CLOCK_TIME_NONE, id, user_data);
241 return GST_CLOCK_TIMEOUT;
244 cclass = GST_CLOCK_GET_CLASS (clock);
246 if (cclass->wait_async) {
248 entry->user_data = user_data;
250 res = cclass->wait_async (clock, entry);
257 gst_clock_reschedule_func (GstClockEntry *entry)
259 entry->status = GST_CLOCK_ENTRY_OK;
261 gst_clock_id_unlock ((GstClockID)entry);
265 * gst_clock_id_unschedule:
266 * @id: The id to unschedule
268 * Cancel an outstanding async notification request with the given ID.
271 gst_clock_id_unschedule (GstClockID id)
273 GstClockEntry *entry;
275 GstClockClass *cclass;
277 g_return_if_fail (id != NULL);
279 entry = (GstClockEntry *) id;
280 clock = entry->clock;
282 cclass = GST_CLOCK_GET_CLASS (clock);
284 if (cclass->unschedule)
285 cclass->unschedule (clock, entry);
290 * @id: The clockid to free
292 * Free the resources held by the given id
295 gst_clock_id_free (GstClockID id)
297 g_return_if_fail (id != NULL);
299 #ifndef GST_DISABLE_TRACE
300 gst_alloc_trace_free (_gst_clock_entry_trace, id);
302 gst_mem_chunk_free (_gst_clock_entries_chunk, id);
306 * gst_clock_id_unlock:
307 * @id: The clockid to unlock
309 * Unlock the givan ClockID.
312 gst_clock_id_unlock (GstClockID id)
314 GstClockEntry *entry;
316 GstClockClass *cclass;
318 g_return_if_fail (id != NULL);
320 entry = (GstClockEntry *) id;
321 clock = entry->clock;
323 cclass = GST_CLOCK_GET_CLASS (clock);
326 cclass->unlock (clock, entry);
331 * GstClock abstract base class implementation
334 gst_clock_get_type (void)
336 static GType clock_type = 0;
339 static const GTypeInfo clock_info = {
340 sizeof (GstClockClass),
343 (GClassInitFunc) gst_clock_class_init,
348 (GInstanceInitFunc) gst_clock_init,
351 clock_type = g_type_register_static (GST_TYPE_OBJECT, "GstClock",
352 &clock_info, G_TYPE_FLAG_ABSTRACT);
358 gst_clock_class_init (GstClockClass *klass)
360 GObjectClass *gobject_class;
361 GstObjectClass *gstobject_class;
363 gobject_class = (GObjectClass*) klass;
364 gstobject_class = (GstObjectClass*) klass;
366 parent_class = g_type_class_ref (GST_TYPE_OBJECT);
368 if (!g_thread_supported ())
369 g_thread_init (NULL);
371 _gst_clock_entries_chunk = gst_mem_chunk_new ("GstClockEntries",
372 sizeof (GstClockEntry), sizeof (GstClockEntry) * 32,
375 #ifndef GST_DISABLE_TRACE
376 _gst_clock_entry_trace = gst_alloc_trace_register (GST_CLOCK_ENTRY_TRACE_NAME);
379 gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_clock_dispose);
380 gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_clock_set_property);
381 gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_clock_get_property);
383 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_STATS,
384 g_param_spec_boolean ("stats", "Stats", "Enable clock stats",
385 FALSE, G_PARAM_READWRITE));
386 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MAX_DIFF,
387 g_param_spec_int64 ("max-diff", "Max diff", "The maximum amount of time to wait in nanoseconds",
388 0, G_MAXINT64, DEFAULT_MAX_DIFF, G_PARAM_READWRITE));
389 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_EVENT_DIFF,
390 g_param_spec_uint64 ("event-diff", "event diff",
391 "The amount of time that may elapse until 2 events are treated as happening at different times",
392 0, G_MAXUINT64, DEFAULT_EVENT_DIFF, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
396 gst_clock_init (GstClock *clock)
398 clock->max_diff = DEFAULT_MAX_DIFF;
401 clock->active = TRUE;
402 clock->start_time = 0;
403 clock->last_time = 0;
404 clock->entries = NULL;
406 clock->stats = FALSE;
408 clock->active_mutex = g_mutex_new ();
409 clock->active_cond = g_cond_new ();
413 gst_clock_dispose (GObject *object)
415 GstClock *clock = GST_CLOCK (object);
417 g_mutex_free (clock->active_mutex);
418 g_cond_free (clock->active_cond);
420 G_OBJECT_CLASS (parent_class)->dispose (object);
424 * gst_clock_set_speed
425 * @clock: a #GstClock to modify
426 * @speed: the speed to set on the clock
428 * Sets the speed on the given clock. 1.0 is the default
431 * Returns: the new speed of the clock.
434 gst_clock_set_speed (GstClock *clock, gdouble speed)
436 g_return_val_if_fail (GST_IS_CLOCK (clock), 0.0);
438 GST_WARNING_OBJECT (clock, "called deprecated function");
443 * gst_clock_get_speed
444 * @clock: a #GstClock to query
446 * Gets the speed of the given clock.
448 * Returns: the speed of the clock.
451 gst_clock_get_speed (GstClock *clock)
453 g_return_val_if_fail (GST_IS_CLOCK (clock), 0.0);
455 GST_WARNING_OBJECT (clock, "called deprecated function");
460 * gst_clock_set_resolution
461 * @clock: The clock set the resolution on
462 * @resolution: The resolution to set
464 * Set the accuracy of the clock.
466 * Returns: the new resolution of the clock.
469 gst_clock_set_resolution (GstClock *clock, guint64 resolution)
471 GstClockClass *cclass;
473 g_return_val_if_fail (GST_IS_CLOCK (clock), G_GINT64_CONSTANT (0));
474 g_return_val_if_fail (resolution != 0, G_GINT64_CONSTANT (0));
476 cclass = GST_CLOCK_GET_CLASS (clock);
478 if (cclass->change_resolution)
479 clock->resolution = cclass->change_resolution (clock, clock->resolution, resolution);
481 return clock->resolution;
485 * gst_clock_get_resolution
486 * @clock: The clock get the resolution of
488 * Get the accuracy of the clock.
490 * Returns: the resolution of the clock in microseconds.
493 gst_clock_get_resolution (GstClock *clock)
495 GstClockClass *cclass;
497 g_return_val_if_fail (GST_IS_CLOCK (clock), G_GINT64_CONSTANT (0));
499 cclass = GST_CLOCK_GET_CLASS (clock);
501 if (cclass->get_resolution)
502 return cclass->get_resolution (clock);
504 return G_GINT64_CONSTANT (1);
508 * gst_clock_set_active
509 * @clock: a #GstClock to set state of
510 * @active: flag indicating if the clock should be activated (TRUE) or deactivated
512 * Activates or deactivates the clock based on the active parameter.
513 * As soon as the clock is activated, the time will start ticking.
516 gst_clock_set_active (GstClock *clock, gboolean active)
518 g_return_if_fail (GST_IS_CLOCK (clock));
520 GST_ERROR_OBJECT (clock, "called deprecated function that does nothing now.");
526 * gst_clock_is_active
527 * @clock: a #GstClock to query
529 * Checks if the given clock is active.
531 * Returns: TRUE if the clock is active.
534 gst_clock_is_active (GstClock *clock)
536 g_return_val_if_fail (GST_IS_CLOCK (clock), FALSE);
538 GST_WARNING_OBJECT (clock, "called deprecated function.");
545 * @clock: a #GstClock to reset
547 * Reset the clock to time 0.
550 gst_clock_reset (GstClock *clock)
552 GstClockTime time = G_GINT64_CONSTANT (0);
553 GstClockClass *cclass;
555 g_return_if_fail (GST_IS_CLOCK (clock));
557 GST_ERROR_OBJECT (clock, "called deprecated function.");
559 cclass = GST_CLOCK_GET_CLASS (clock);
561 if (cclass->get_internal_time) {
562 time = cclass->get_internal_time (clock);
566 //clock->active = FALSE;
567 clock->start_time = time;
568 clock->last_time = G_GINT64_CONSTANT (0);
569 g_list_foreach (clock->entries, (GFunc) gst_clock_reschedule_func, NULL);
574 * gst_clock_handle_discont
575 * @clock: a #GstClock to notify of the discontinuity
576 * @time: The new time
578 * Notifies the clock of a discontinuity in time.
580 * Returns: TRUE if the clock was updated. It is possible that
581 * the clock was not updated by this call because only the first
582 * discontinuitity in the pipeline is honoured.
585 gst_clock_handle_discont (GstClock *clock, guint64 time)
587 GST_ERROR_OBJECT (clock, "called deprecated function.");
594 * @clock: a #GstClock to query
596 * Gets the current time of the given clock. The time is always
597 * monotonically increasing.
599 * Returns: the time of the clock.
602 gst_clock_get_time (GstClock *clock)
604 GstClockTime ret = G_GINT64_CONSTANT (0);
605 GstClockClass *cclass;
607 g_return_val_if_fail (GST_IS_CLOCK (clock), G_GINT64_CONSTANT (0));
609 cclass = GST_CLOCK_GET_CLASS (clock);
611 if (cclass->get_internal_time) {
612 ret = cclass->get_internal_time (clock) - clock->start_time;
614 /* make sure the time is increasing, else return last_time */
615 if ((gint64) ret < (gint64) clock->last_time) {
616 ret = clock->last_time;
619 clock->last_time = ret;
626 * gst_clock_get_event_time:
627 * @clock: clock to query
629 * Gets the "event time" of a given clock. An event on the clock happens
630 * whenever this function is called. This ensures that multiple events that
631 * happen shortly after each other are treated as if they happened at the same
632 * time. GStreamer uses to keep state changes of multiple elements in sync.
634 * Returns: the time of the event
637 gst_clock_get_event_time (GstClock *clock)
641 g_return_val_if_fail (GST_IS_CLOCK (clock), GST_CLOCK_TIME_NONE);
643 time = gst_clock_get_time (clock);
645 if (clock->last_event + clock->max_event_diff >= time) {
646 GST_LOG_OBJECT (clock, "reporting last event time %"G_GUINT64_FORMAT,
649 GST_LOG_OBJECT (clock, "reporting new event time %"G_GUINT64_FORMAT,
651 clock->last_event = time;
654 return clock->last_event;
658 * gst_clock_get_next_id
659 * @clock: The clock to query
661 * Get the clockid of the next event.
663 * Returns: a clockid or NULL is no event is pending.
666 gst_clock_get_next_id (GstClock *clock)
668 GstClockEntry *entry = NULL;
670 g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);
674 entry = GST_CLOCK_ENTRY (clock->entries->data);
677 return (GstClockID *) entry;
681 gst_clock_update_stats (GstClock *clock)
686 gst_clock_set_property (GObject *object, guint prop_id,
687 const GValue *value, GParamSpec *pspec)
691 clock = GST_CLOCK (object);
695 clock->stats = g_value_get_boolean (value);
696 g_object_notify (object, "stats");
699 clock->max_diff = g_value_get_int64 (value);
700 g_object_notify (object, "max-diff");
703 clock->max_event_diff = g_value_get_uint64 (value);
704 g_object_notify (object, "event-diff");
707 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
713 gst_clock_get_property (GObject *object, guint prop_id,
714 GValue *value, GParamSpec * pspec)
718 clock = GST_CLOCK (object);
722 g_value_set_boolean (value, clock->stats);
725 g_value_set_int64 (value, clock->max_diff);
728 g_value_set_uint64 (value, clock->max_event_diff);
731 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);