bufferlist: fix a comment
[platform/upstream/gstreamer.git] / gst / gstclock.c
1 /* GStreamer
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>
5  *
6  * gstclock.c: Clock subsystem for maintaining time sync
7  *
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.
12  *
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.
17  *
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.
22  */
23
24 /**
25  * SECTION:gstclock
26  * @short_description: Abstract class for global clocks
27  * @see_also: #GstSystemClock, #GstPipeline
28  *
29  * GStreamer uses a global clock to synchronize the plugins in a pipeline.
30  * Different clock implementations are possible by implementing this abstract
31  * base class.
32  *
33  * The #GstClock returns a monotonically increasing time with the method
34  * gst_clock_get_time(). Its accuracy and base time depend on the specific
35  * clock implementation but time is always expressed in nanoseconds. Since the
36  * baseline of the clock is undefined, the clock time returned is not
37  * meaningful in itself, what matters are the deltas between two clock times.
38  * The time returned by a clock is called the absolute time.
39  *
40  * The pipeline uses the clock to calculate the stream time. Usually all
41  * renderers synchronize to the global clock using the buffer timestamps, the
42  * newsegment events and the element's base time, see #GstPipeline.
43  *
44  * A clock implementation can support periodic and single shot clock
45  * notifications both synchronous and asynchronous.
46  *
47  * One first needs to create a #GstClockID for the periodic or single shot
48  * notification using gst_clock_new_single_shot_id() or
49  * gst_clock_new_periodic_id().
50  *
51  * To perform a blocking wait for the specific time of the #GstClockID use the
52  * gst_clock_id_wait(). To receive a callback when the specific time is reached
53  * in the clock use gst_clock_id_wait_async(). Both these calls can be
54  * interrupted with the gst_clock_id_unschedule() call. If the blocking wait is
55  * unscheduled a return value of #GST_CLOCK_UNSCHEDULED is returned.
56  *
57  * Periodic callbacks scheduled async will be repeatedly called automatically
58  * until it is unscheduled. To schedule a sync periodic callback,
59  * gst_clock_id_wait() should be called repeatedly.
60  *
61  * The async callbacks can happen from any thread, either provided by the core
62  * or from a streaming thread. The application should be prepared for this.
63  *
64  * A #GstClockID that has been unscheduled cannot be used again for any wait
65  * operation, a new #GstClockID should be created and the old unscheduled one
66  * should be destroyed with gst_clock_id_unref().
67  *
68  * It is possible to perform a blocking wait on the same #GstClockID from
69  * multiple threads. However, registering the same #GstClockID for multiple
70  * async notifications is not possible, the callback will only be called for
71  * the thread registering the entry last.
72  *
73  * None of the wait operations unref the #GstClockID, the owner is responsible
74  * for unreffing the ids itself. This holds for both periodic and single shot
75  * notifications. The reason being that the owner of the #GstClockID has to
76  * keep a handle to the #GstClockID to unblock the wait on FLUSHING events or
77  * state changes and if the entry would be unreffed automatically, the handle 
78  * might become invalid without any notification.
79  *
80  * These clock operations do not operate on the stream time, so the callbacks
81  * will also occur when not in PLAYING state as if the clock just keeps on
82  * running. Some clocks however do not progress when the element that provided
83  * the clock is not PLAYING.
84  *
85  * When a clock has the #GST_CLOCK_FLAG_CAN_SET_MASTER flag set, it can be
86  * slaved to another #GstClock with the gst_clock_set_master(). The clock will
87  * then automatically be synchronized to this master clock by repeatedly
88  * sampling the master clock and the slave clock and recalibrating the slave
89  * clock with gst_clock_set_calibration(). This feature is mostly useful for
90  * plugins that have an internal clock but must operate with another clock
91  * selected by the #GstPipeline.  They can track the offset and rate difference
92  * of their internal clock relative to the master clock by using the
93  * gst_clock_get_calibration() function. 
94  *
95  * The master/slave synchronisation can be tuned with the #GstClock:timeout,
96  * #GstClock:window-size and #GstClock:window-threshold properties.
97  * The #GstClock:timeout property defines the interval to sample the master
98  * clock and run the calibration functions. #GstClock:window-size defines the
99  * number of samples to use when calibrating and #GstClock:window-threshold
100  * defines the minimum number of samples before the calibration is performed.
101  *
102  * Last reviewed on 2006-08-11 (0.10.10)
103  */
104
105
106 #include "gst_private.h"
107 #include <time.h>
108
109 #include "gstclock.h"
110 #include "gstinfo.h"
111 #include "gstutils.h"
112
113 #ifndef GST_DISABLE_TRACE
114 /* #define GST_WITH_ALLOC_TRACE */
115 #include "gsttrace.h"
116 static GstAllocTrace *_gst_clock_entry_trace;
117 #endif
118
119 /* #define DEBUGGING_ENABLED */
120
121 #define DEFAULT_STATS                   FALSE
122 #define DEFAULT_WINDOW_SIZE             32
123 #define DEFAULT_WINDOW_THRESHOLD        4
124 #define DEFAULT_TIMEOUT                 GST_SECOND / 10
125
126 enum
127 {
128   PROP_0,
129   PROP_STATS,
130   PROP_WINDOW_SIZE,
131   PROP_WINDOW_THRESHOLD,
132   PROP_TIMEOUT
133 };
134
135 static void gst_clock_class_init (GstClockClass * klass);
136 static void gst_clock_init (GstClock * clock);
137 static void gst_clock_dispose (GObject * object);
138 static void gst_clock_finalize (GObject * object);
139
140 static void gst_clock_set_property (GObject * object, guint prop_id,
141     const GValue * value, GParamSpec * pspec);
142 static void gst_clock_get_property (GObject * object, guint prop_id,
143     GValue * value, GParamSpec * pspec);
144 static void gst_clock_update_stats (GstClock * clock);
145
146
147 static GstObjectClass *parent_class = NULL;
148
149 /* static guint gst_clock_signals[LAST_SIGNAL] = { 0 }; */
150
151 static GstClockID
152 gst_clock_entry_new (GstClock * clock, GstClockTime time,
153     GstClockTime interval, GstClockEntryType type)
154 {
155   GstClockEntry *entry;
156
157   entry = g_slice_new (GstClockEntry);
158 #ifndef GST_DISABLE_TRACE
159   gst_alloc_trace_new (_gst_clock_entry_trace, entry);
160 #endif
161   GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
162       "created entry %p, time %" GST_TIME_FORMAT, entry, GST_TIME_ARGS (time));
163
164   entry->refcount = 1;
165   entry->clock = clock;
166   entry->type = type;
167   entry->time = time;
168   entry->interval = interval;
169   entry->status = GST_CLOCK_OK;
170   entry->func = NULL;
171   entry->user_data = NULL;
172
173   return (GstClockID) entry;
174 }
175
176 /**
177  * gst_clock_id_ref:
178  * @id: The #GstClockID to ref
179  *
180  * Increase the refcount of given @id.
181  *
182  * Returns: The same #GstClockID with increased refcount.
183  *
184  * MT safe.
185  */
186 GstClockID
187 gst_clock_id_ref (GstClockID id)
188 {
189   g_return_val_if_fail (id != NULL, NULL);
190
191   g_atomic_int_inc (&((GstClockEntry *) id)->refcount);
192
193   return id;
194 }
195
196 static void
197 _gst_clock_id_free (GstClockID id)
198 {
199   g_return_if_fail (id != NULL);
200
201   GST_CAT_DEBUG (GST_CAT_CLOCK, "freed entry %p", id);
202
203 #ifndef GST_DISABLE_TRACE
204   gst_alloc_trace_free (_gst_clock_entry_trace, id);
205 #endif
206   g_slice_free (GstClockEntry, id);
207 }
208
209 /**
210  * gst_clock_id_unref:
211  * @id: The #GstClockID to unref
212  *
213  * Unref given @id. When the refcount reaches 0 the
214  * #GstClockID will be freed.
215  *
216  * MT safe.
217  */
218 void
219 gst_clock_id_unref (GstClockID id)
220 {
221   gint zero;
222
223   g_return_if_fail (id != NULL);
224
225   zero = g_atomic_int_dec_and_test (&((GstClockEntry *) id)->refcount);
226   /* if we ended up with the refcount at zero, free the id */
227   if (zero) {
228     _gst_clock_id_free (id);
229   }
230 }
231
232 /**
233  * gst_clock_new_single_shot_id
234  * @clock: The #GstClockID to get a single shot notification from
235  * @time: the requested time
236  *
237  * Get a #GstClockID from @clock to trigger a single shot
238  * notification at the requested time. The single shot id should be
239  * unreffed after usage.
240  *
241  * Returns: A #GstClockID that can be used to request the time notification.
242  *
243  * MT safe.
244  */
245 GstClockID
246 gst_clock_new_single_shot_id (GstClock * clock, GstClockTime time)
247 {
248   g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);
249
250   return gst_clock_entry_new (clock,
251       time, GST_CLOCK_TIME_NONE, GST_CLOCK_ENTRY_SINGLE);
252 }
253
254 /**
255  * gst_clock_new_periodic_id
256  * @clock: The #GstClockID to get a periodic notification id from
257  * @start_time: the requested start time
258  * @interval: the requested interval
259  *
260  * Get an ID from @clock to trigger a periodic notification.
261  * The periodic notifications will start at time @start_time and
262  * will then be fired with the given @interval. @id should be unreffed
263  * after usage.
264  *
265  * Returns: A #GstClockID that can be used to request the time notification.
266  *
267  * MT safe.
268  */
269 GstClockID
270 gst_clock_new_periodic_id (GstClock * clock, GstClockTime start_time,
271     GstClockTime interval)
272 {
273   g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);
274   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (start_time), NULL);
275   g_return_val_if_fail (interval != 0, NULL);
276   g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (interval), NULL);
277
278   return gst_clock_entry_new (clock,
279       start_time, interval, GST_CLOCK_ENTRY_PERIODIC);
280 }
281
282 /**
283  * gst_clock_id_compare_func
284  * @id1: A #GstClockID
285  * @id2: A #GstClockID to compare with
286  *
287  * Compares the two #GstClockID instances. This function can be used
288  * as a GCompareFunc when sorting ids.
289  *
290  * Returns: negative value if a < b; zero if a = b; positive value if a > b
291  *
292  * MT safe.
293  */
294 gint
295 gst_clock_id_compare_func (gconstpointer id1, gconstpointer id2)
296 {
297   GstClockEntry *entry1, *entry2;
298
299   entry1 = (GstClockEntry *) id1;
300   entry2 = (GstClockEntry *) id2;
301
302   if (GST_CLOCK_ENTRY_TIME (entry1) > GST_CLOCK_ENTRY_TIME (entry2)) {
303     return 1;
304   }
305   if (GST_CLOCK_ENTRY_TIME (entry1) < GST_CLOCK_ENTRY_TIME (entry2)) {
306     return -1;
307   }
308   return 0;
309 }
310
311 /**
312  * gst_clock_id_get_time
313  * @id: The #GstClockID to query
314  *
315  * Get the time of the clock ID
316  *
317  * Returns: the time of the given clock id.
318  *
319  * MT safe.
320  */
321 GstClockTime
322 gst_clock_id_get_time (GstClockID id)
323 {
324   g_return_val_if_fail (id != NULL, GST_CLOCK_TIME_NONE);
325
326   return GST_CLOCK_ENTRY_TIME ((GstClockEntry *) id);
327 }
328
329 /**
330  * gst_clock_id_wait
331  * @id: The #GstClockID to wait on
332  * @jitter: A pointer that will contain the jitter, can be %NULL.
333  *
334  * Perform a blocking wait on @id. 
335  * @id should have been created with gst_clock_new_single_shot_id()
336  * or gst_clock_new_periodic_id() and should not have been unscheduled
337  * with a call to gst_clock_id_unschedule(). 
338  *
339  * If the @jitter argument is not %NULL and this function returns #GST_CLOCK_OK
340  * or #GST_CLOCK_EARLY, it will contain the difference
341  * against the clock and the time of @id when this method was
342  * called. 
343  * Positive values indicate how late @id was relative to the clock
344  * (in which case this function will return #GST_CLOCK_EARLY). 
345  * Negative values indicate how much time was spent waiting on the clock 
346  * before this function returned.
347  *
348  * Returns: the result of the blocking wait. #GST_CLOCK_EARLY will be returned
349  * if the current clock time is past the time of @id, #GST_CLOCK_OK if 
350  * @id was scheduled in time. #GST_CLOCK_UNSCHEDULED if @id was 
351  * unscheduled with gst_clock_id_unschedule().
352  *
353  * MT safe.
354  */
355 GstClockReturn
356 gst_clock_id_wait (GstClockID id, GstClockTimeDiff * jitter)
357 {
358   GstClockEntry *entry;
359   GstClock *clock;
360   GstClockReturn res;
361   GstClockTime requested;
362   GstClockClass *cclass;
363
364   g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR);
365
366   entry = (GstClockEntry *) id;
367   requested = GST_CLOCK_ENTRY_TIME (entry);
368
369   clock = GST_CLOCK_ENTRY_CLOCK (entry);
370
371   /* can't sync on invalid times */
372   if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested)))
373     goto invalid_time;
374
375   cclass = GST_CLOCK_GET_CLASS (clock);
376
377   GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "waiting on clock entry %p", id);
378
379   /* if we have a wait_jitter function, use that */
380   if (G_LIKELY (cclass->wait_jitter)) {
381     res = cclass->wait_jitter (clock, entry, jitter);
382   } else {
383     /* check if we have a simple _wait function otherwise. The function without
384      * the jitter arg is less optimal as we need to do an additional _get_time()
385      * which is not atomic with the _wait() and a typical _wait() function does
386      * yet another _get_time() anyway. */
387     if (G_UNLIKELY (cclass->wait == NULL))
388       goto not_supported;
389
390     if (jitter) {
391       GstClockTime now = gst_clock_get_time (clock);
392
393       /* jitter is the diff against the clock when this entry is scheduled. Negative
394        * values mean that the entry was in time, a positive value means that the
395        * entry was too late. */
396       *jitter = GST_CLOCK_DIFF (requested, now);
397     }
398     res = cclass->wait (clock, entry);
399   }
400
401   GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
402       "done waiting entry %p, res: %d", id, res);
403
404   if (entry->type == GST_CLOCK_ENTRY_PERIODIC)
405     entry->time = requested + entry->interval;
406
407   if (clock->stats)
408     gst_clock_update_stats (clock);
409
410   return res;
411
412   /* ERRORS */
413 invalid_time:
414   {
415     GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
416         "invalid time requested, returning _BADTIME");
417     return GST_CLOCK_BADTIME;
418   }
419 not_supported:
420   {
421     GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "clock wait is not supported");
422     return GST_CLOCK_UNSUPPORTED;
423   }
424 }
425
426 /**
427  * gst_clock_id_wait_async:
428  * @id: a #GstClockID to wait on
429  * @func: The callback function
430  * @user_data: User data passed in the callback
431  *
432  * Register a callback on the given #GstClockID @id with the given
433  * function and user_data. When passing a #GstClockID with an invalid
434  * time to this function, the callback will be called immediately
435  * with  a time set to GST_CLOCK_TIME_NONE. The callback will
436  * be called when the time of @id has been reached.
437  *
438  * The callback @func can be invoked from any thread, either provided by the
439  * core or from a streaming thread. The application should be prepared for this.
440  *
441  * Returns: the result of the non blocking wait.
442  *
443  * MT safe.
444  */
445 GstClockReturn
446 gst_clock_id_wait_async (GstClockID id,
447     GstClockCallback func, gpointer user_data)
448 {
449   GstClockEntry *entry;
450   GstClock *clock;
451   GstClockReturn res;
452   GstClockClass *cclass;
453   GstClockTime requested;
454
455   g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR);
456   g_return_val_if_fail (func != NULL, GST_CLOCK_ERROR);
457
458   entry = (GstClockEntry *) id;
459   requested = GST_CLOCK_ENTRY_TIME (entry);
460   clock = GST_CLOCK_ENTRY_CLOCK (entry);
461
462   /* can't sync on invalid times */
463   if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested)))
464     goto invalid_time;
465
466   cclass = GST_CLOCK_GET_CLASS (clock);
467
468   if (G_UNLIKELY (cclass->wait_async == NULL))
469     goto not_supported;
470
471   entry->func = func;
472   entry->user_data = user_data;
473
474   res = cclass->wait_async (clock, entry);
475
476   return res;
477
478   /* ERRORS */
479 invalid_time:
480   {
481     (func) (clock, GST_CLOCK_TIME_NONE, id, user_data);
482     GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
483         "invalid time requested, returning _BADTIME");
484     return GST_CLOCK_BADTIME;
485   }
486 not_supported:
487   {
488     GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "clock wait is not supported");
489     return GST_CLOCK_UNSUPPORTED;
490   }
491 }
492
493 /**
494  * gst_clock_id_unschedule:
495  * @id: The id to unschedule
496  *
497  * Cancel an outstanding request with @id. This can either
498  * be an outstanding async notification or a pending sync notification.
499  * After this call, @id cannot be used anymore to receive sync or
500  * async notifications, you need to create a new #GstClockID.
501  *
502  * MT safe.
503  */
504 void
505 gst_clock_id_unschedule (GstClockID id)
506 {
507   GstClockEntry *entry;
508   GstClock *clock;
509   GstClockClass *cclass;
510
511   g_return_if_fail (id != NULL);
512
513   entry = (GstClockEntry *) id;
514   clock = entry->clock;
515
516   cclass = GST_CLOCK_GET_CLASS (clock);
517
518   if (G_LIKELY (cclass->unschedule))
519     cclass->unschedule (clock, entry);
520 }
521
522
523 /**
524  * GstClock abstract base class implementation
525  */
526 G_DEFINE_TYPE (GstClock, gst_clock, GST_TYPE_OBJECT);
527
528 static void
529 gst_clock_class_init (GstClockClass * klass)
530 {
531   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
532
533   parent_class = g_type_class_peek_parent (klass);
534
535 #ifndef GST_DISABLE_TRACE
536   _gst_clock_entry_trace =
537       gst_alloc_trace_register (GST_CLOCK_ENTRY_TRACE_NAME);
538 #endif
539
540   gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_clock_dispose);
541   gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_clock_finalize);
542   gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_clock_set_property);
543   gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_clock_get_property);
544
545   g_object_class_install_property (gobject_class, PROP_STATS,
546       g_param_spec_boolean ("stats", "Stats",
547           "Enable clock stats (unimplemented)", DEFAULT_STATS,
548           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
549   g_object_class_install_property (gobject_class, PROP_WINDOW_SIZE,
550       g_param_spec_int ("window-size", "Window size",
551           "The size of the window used to calculate rate and offset", 2, 1024,
552           DEFAULT_WINDOW_SIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
553   g_object_class_install_property (gobject_class, PROP_WINDOW_THRESHOLD,
554       g_param_spec_int ("window-threshold", "Window threshold",
555           "The threshold to start calculating rate and offset", 2, 1024,
556           DEFAULT_WINDOW_THRESHOLD,
557           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
558   g_object_class_install_property (gobject_class, PROP_TIMEOUT,
559       g_param_spec_uint64 ("timeout", "Timeout",
560           "The amount of time, in nanoseconds, to sample master and slave clocks",
561           0, G_MAXUINT64, DEFAULT_TIMEOUT,
562           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
563 }
564
565 static void
566 gst_clock_init (GstClock * clock)
567 {
568   clock->last_time = 0;
569   clock->entries = NULL;
570   clock->entries_changed = g_cond_new ();
571   clock->stats = FALSE;
572
573   clock->internal_calibration = 0;
574   clock->external_calibration = 0;
575   clock->rate_numerator = 1;
576   clock->rate_denominator = 1;
577
578   clock->slave_lock = g_mutex_new ();
579   clock->window_size = DEFAULT_WINDOW_SIZE;
580   clock->window_threshold = DEFAULT_WINDOW_THRESHOLD;
581   clock->filling = TRUE;
582   clock->time_index = 0;
583   clock->timeout = DEFAULT_TIMEOUT;
584   clock->times = g_new0 (GstClockTime, 4 * clock->window_size);
585 }
586
587 static void
588 gst_clock_dispose (GObject * object)
589 {
590   GstClock *clock = GST_CLOCK (object);
591   GstClock **master_p;
592
593   GST_OBJECT_LOCK (clock);
594   master_p = &clock->master;
595   gst_object_replace ((GstObject **) master_p, NULL);
596   GST_OBJECT_UNLOCK (clock);
597
598   G_OBJECT_CLASS (parent_class)->dispose (object);
599 }
600
601 static void
602 gst_clock_finalize (GObject * object)
603 {
604   GstClock *clock = GST_CLOCK (object);
605
606   GST_CLOCK_SLAVE_LOCK (clock);
607   if (clock->clockid) {
608     gst_clock_id_unschedule (clock->clockid);
609     gst_clock_id_unref (clock->clockid);
610     clock->clockid = NULL;
611   }
612   g_free (clock->times);
613   clock->times = NULL;
614   GST_CLOCK_SLAVE_UNLOCK (clock);
615
616   g_cond_free (clock->entries_changed);
617   g_mutex_free (clock->slave_lock);
618
619   G_OBJECT_CLASS (parent_class)->finalize (object);
620 }
621
622 /**
623  * gst_clock_set_resolution
624  * @clock: a #GstClock
625  * @resolution: The resolution to set
626  *
627  * Set the accuracy of the clock. Some clocks have the possibility to operate
628  * with different accuracy at the expense of more resource usage. There is
629  * normally no need to change the default resolution of a clock. The resolution
630  * of a clock can only be changed if the clock has the
631  * #GST_CLOCK_FLAG_CAN_SET_RESOLUTION flag set.
632  *
633  * Returns: the new resolution of the clock.
634  */
635 GstClockTime
636 gst_clock_set_resolution (GstClock * clock, GstClockTime resolution)
637 {
638   GstClockClass *cclass;
639
640   g_return_val_if_fail (GST_IS_CLOCK (clock), 0);
641   g_return_val_if_fail (resolution != 0, 0);
642
643   cclass = GST_CLOCK_GET_CLASS (clock);
644
645   if (cclass->change_resolution)
646     clock->resolution =
647         cclass->change_resolution (clock, clock->resolution, resolution);
648
649   return clock->resolution;
650 }
651
652 /**
653  * gst_clock_get_resolution
654  * @clock: a #GstClock
655  *
656  * Get the accuracy of the clock. The accuracy of the clock is the granularity
657  * of the values returned by gst_clock_get_time().
658  *
659  * Returns: the resolution of the clock in units of #GstClockTime.
660  *
661  * MT safe.
662  */
663 GstClockTime
664 gst_clock_get_resolution (GstClock * clock)
665 {
666   GstClockClass *cclass;
667
668   g_return_val_if_fail (GST_IS_CLOCK (clock), 0);
669
670   cclass = GST_CLOCK_GET_CLASS (clock);
671
672   if (cclass->get_resolution)
673     return cclass->get_resolution (clock);
674
675   return 1;
676 }
677
678 /**
679  * gst_clock_adjust_unlocked
680  * @clock: a #GstClock to use
681  * @internal: a clock time
682  *
683  * Converts the given @internal clock time to the external time, adjusting for the
684  * rate and reference time set with gst_clock_set_calibration() and making sure
685  * that the returned time is increasing. This function should be called with the
686  * clock's OBJECT_LOCK held and is mainly used by clock subclasses.
687  *
688  * This function is the reverse of gst_clock_unadjust_unlocked().
689  *
690  * Returns: the converted time of the clock.
691  */
692 GstClockTime
693 gst_clock_adjust_unlocked (GstClock * clock, GstClockTime internal)
694 {
695   GstClockTime ret, cinternal, cexternal, cnum, cdenom;
696
697   /* get calibration values for readability */
698   cinternal = clock->internal_calibration;
699   cexternal = clock->external_calibration;
700   cnum = clock->rate_numerator;
701   cdenom = clock->rate_denominator;
702
703   /* avoid divide by 0 */
704   if (cdenom == 0)
705     cnum = cdenom = 1;
706
707   /* The formula is (internal - cinternal) * cnum / cdenom + cexternal
708    *
709    * Since we do math on unsigned 64-bit ints we have to special case for
710    * internal < cinternal to get the sign right. this case is not very common,
711    * though.
712    */
713   if (G_LIKELY (internal >= cinternal)) {
714     ret = gst_util_uint64_scale (internal - cinternal, cnum, cdenom);
715     ret += cexternal;
716   } else {
717     ret = gst_util_uint64_scale (cinternal - internal, cnum, cdenom);
718     /* clamp to 0 */
719     if (cexternal > ret)
720       ret = cexternal - ret;
721     else
722       ret = 0;
723   }
724
725   /* make sure the time is increasing */
726   clock->last_time = MAX (ret, clock->last_time);
727
728   return clock->last_time;
729 }
730
731 /**
732  * gst_clock_unadjust_unlocked
733  * @clock: a #GstClock to use
734  * @external: an external clock time
735  *
736  * Converts the given @external clock time to the internal time of @clock,
737  * using the rate and reference time set with gst_clock_set_calibration().
738  * This function should be called with the clock's OBJECT_LOCK held and
739  * is mainly used by clock subclasses.
740  *
741  * This function is the reverse of gst_clock_adjust_unlocked().
742  *
743  * Returns: the internal time of the clock corresponding to @external.
744  *
745  * Since: 0.10.13
746  */
747 GstClockTime
748 gst_clock_unadjust_unlocked (GstClock * clock, GstClockTime external)
749 {
750   GstClockTime ret, cinternal, cexternal, cnum, cdenom;
751
752   /* get calibration values for readability */
753   cinternal = clock->internal_calibration;
754   cexternal = clock->external_calibration;
755   cnum = clock->rate_numerator;
756   cdenom = clock->rate_denominator;
757
758   /* avoid divide by 0 */
759   if (cnum == 0)
760     cnum = cdenom = 1;
761
762   /* The formula is (external - cexternal) * cdenom / cnum + cinternal */
763   if (external >= cexternal) {
764     ret = gst_util_uint64_scale (external - cexternal, cdenom, cnum);
765     ret += cinternal;
766   } else {
767     ret = gst_util_uint64_scale (cexternal - external, cdenom, cnum);
768     if (cinternal > ret)
769       ret = cinternal - ret;
770     else
771       ret = 0;
772   }
773   return ret;
774 }
775
776 /**
777  * gst_clock_get_internal_time
778  * @clock: a #GstClock to query
779  *
780  * Gets the current internal time of the given clock. The time is returned
781  * unadjusted for the offset and the rate.
782  *
783  * Returns: the internal time of the clock. Or GST_CLOCK_TIME_NONE when
784  * given invalid input.
785  *
786  * MT safe.
787  */
788 GstClockTime
789 gst_clock_get_internal_time (GstClock * clock)
790 {
791   GstClockTime ret;
792   GstClockClass *cclass;
793
794   g_return_val_if_fail (GST_IS_CLOCK (clock), GST_CLOCK_TIME_NONE);
795
796   cclass = GST_CLOCK_GET_CLASS (clock);
797
798   if (G_UNLIKELY (cclass->get_internal_time == NULL))
799     goto not_supported;
800
801   ret = cclass->get_internal_time (clock);
802
803   GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "internal time %" GST_TIME_FORMAT,
804       GST_TIME_ARGS (ret));
805
806   return ret;
807
808   /* ERRORS */
809 not_supported:
810   {
811     GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
812         "internal time not supported, return 0");
813     return G_GINT64_CONSTANT (0);
814   }
815 }
816
817 /**
818  * gst_clock_get_time
819  * @clock: a #GstClock to query
820  *
821  * Gets the current time of the given clock. The time is always
822  * monotonically increasing and adjusted according to the current
823  * offset and rate.
824  *
825  * Returns: the time of the clock. Or GST_CLOCK_TIME_NONE when
826  * given invalid input.
827  *
828  * MT safe.
829  */
830 GstClockTime
831 gst_clock_get_time (GstClock * clock)
832 {
833   GstClockTime ret;
834
835   g_return_val_if_fail (GST_IS_CLOCK (clock), GST_CLOCK_TIME_NONE);
836
837   ret = gst_clock_get_internal_time (clock);
838
839   GST_OBJECT_LOCK (clock);
840   /* this will scale for rate and offset */
841   ret = gst_clock_adjust_unlocked (clock, ret);
842   GST_OBJECT_UNLOCK (clock);
843
844   GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "adjusted time %" GST_TIME_FORMAT,
845       GST_TIME_ARGS (ret));
846
847   return ret;
848 }
849
850 /**
851  * gst_clock_set_calibration
852  * @clock: a #GstClock to calibrate
853  * @internal: a reference internal time
854  * @external: a reference external time
855  * @rate_num: the numerator of the rate of the clock relative to its
856  *            internal time 
857  * @rate_denom: the denominator of the rate of the clock
858  *
859  * Adjusts the rate and time of @clock. A rate of 1/1 is the normal speed of
860  * the clock. Values bigger than 1/1 make the clock go faster.
861  *
862  * @internal and @external are calibration parameters that arrange that
863  * gst_clock_get_time() should have been @external at internal time @internal.
864  * This internal time should not be in the future; that is, it should be less
865  * than the value of gst_clock_get_internal_time() when this function is called.
866  *
867  * Subsequent calls to gst_clock_get_time() will return clock times computed as
868  * follows:
869  *
870  * <programlisting>
871  *   time = (internal_time - internal) * rate_num / rate_denom + external
872  * </programlisting>
873  *
874  * This formula is implemented in gst_clock_adjust_unlocked(). Of course, it
875  * tries to do the integer arithmetic as precisely as possible.
876  *
877  * Note that gst_clock_get_time() always returns increasing values so when you
878  * move the clock backwards, gst_clock_get_time() will report the previous value
879  * until the clock catches up.
880  *
881  * MT safe.
882  */
883 void
884 gst_clock_set_calibration (GstClock * clock, GstClockTime internal, GstClockTime
885     external, GstClockTime rate_num, GstClockTime rate_denom)
886 {
887   g_return_if_fail (GST_IS_CLOCK (clock));
888   g_return_if_fail (rate_num != GST_CLOCK_TIME_NONE);
889   g_return_if_fail (rate_denom > 0 && rate_denom != GST_CLOCK_TIME_NONE);
890   g_return_if_fail (internal <= gst_clock_get_internal_time (clock));
891
892   GST_OBJECT_LOCK (clock);
893   GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
894       "internal %" GST_TIME_FORMAT " external %" GST_TIME_FORMAT " %"
895       G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT " = %f", GST_TIME_ARGS (internal),
896       GST_TIME_ARGS (external), rate_num, rate_denom,
897       gst_guint64_to_gdouble (rate_num) / gst_guint64_to_gdouble (rate_denom));
898
899   clock->internal_calibration = internal;
900   clock->external_calibration = external;
901   clock->rate_numerator = rate_num;
902   clock->rate_denominator = rate_denom;
903   GST_OBJECT_UNLOCK (clock);
904 }
905
906 /**
907  * gst_clock_get_calibration
908  * @clock: a #GstClock 
909  * @internal: a location to store the internal time
910  * @external: a location to store the external time
911  * @rate_num: a location to store the rate numerator
912  * @rate_denom: a location to store the rate denominator
913  *
914  * Gets the internal rate and reference time of @clock. See
915  * gst_clock_set_calibration() for more information.
916  *
917  * @internal, @external, @rate_num, and @rate_denom can be left %NULL if the
918  * caller is not interested in the values.
919  *
920  * MT safe.
921  */
922 void
923 gst_clock_get_calibration (GstClock * clock, GstClockTime * internal,
924     GstClockTime * external, GstClockTime * rate_num, GstClockTime * rate_denom)
925 {
926   g_return_if_fail (GST_IS_CLOCK (clock));
927
928   GST_OBJECT_LOCK (clock);
929   if (rate_num)
930     *rate_num = clock->rate_numerator;
931   if (rate_denom)
932     *rate_denom = clock->rate_denominator;
933   if (external)
934     *external = clock->external_calibration;
935   if (internal)
936     *internal = clock->internal_calibration;
937   GST_OBJECT_UNLOCK (clock);
938 }
939
940 /* will be called repeatedly to sample the master and slave clock
941  * to recalibrate the clock  */
942 static gboolean
943 gst_clock_slave_callback (GstClock * master, GstClockTime time,
944     GstClockID id, GstClock * clock)
945 {
946   GstClockTime stime, mtime;
947   gdouble r_squared;
948
949   stime = gst_clock_get_internal_time (clock);
950   mtime = gst_clock_get_time (master);
951
952   GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
953       "master %" GST_TIME_FORMAT ", slave %" GST_TIME_FORMAT,
954       GST_TIME_ARGS (mtime), GST_TIME_ARGS (stime));
955
956   gst_clock_add_observation (clock, stime, mtime, &r_squared);
957
958   /* FIXME, we can use the r_squared value to adjust the timeout
959    * value of the clockid */
960
961   return TRUE;
962 }
963
964 /**
965  * gst_clock_set_master
966  * @clock: a #GstClock 
967  * @master: a master #GstClock 
968  *
969  * Set @master as the master clock for @clock. @clock will be automatically
970  * calibrated so that gst_clock_get_time() reports the same time as the
971  * master clock.  
972  * 
973  * A clock provider that slaves its clock to a master can get the current
974  * calibration values with gst_clock_get_calibration().
975  *
976  * @master can be %NULL in which case @clock will not be slaved anymore. It will
977  * however keep reporting its time adjusted with the last configured rate 
978  * and time offsets.
979  *
980  * Returns: %TRUE if the clock is capable of being slaved to a master clock. 
981  * Trying to set a master on a clock without the 
982  * #GST_CLOCK_FLAG_CAN_SET_MASTER flag will make this function return %FALSE.
983  *
984  * MT safe.
985  */
986 gboolean
987 gst_clock_set_master (GstClock * clock, GstClock * master)
988 {
989   GstClock **master_p;
990
991   g_return_val_if_fail (GST_IS_CLOCK (clock), FALSE);
992   g_return_val_if_fail (master != clock, FALSE);
993
994   GST_OBJECT_LOCK (clock);
995   /* we always allow setting the master to NULL */
996   if (master && !GST_OBJECT_FLAG_IS_SET (clock, GST_CLOCK_FLAG_CAN_SET_MASTER))
997     goto not_supported;
998
999   GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
1000       "slaving %p to master clock %p", clock, master);
1001   master_p = &clock->master;
1002   gst_object_replace ((GstObject **) master_p, (GstObject *) master);
1003   GST_OBJECT_UNLOCK (clock);
1004
1005   GST_CLOCK_SLAVE_LOCK (clock);
1006   if (clock->clockid) {
1007     gst_clock_id_unschedule (clock->clockid);
1008     gst_clock_id_unref (clock->clockid);
1009     clock->clockid = NULL;
1010   }
1011   if (master) {
1012     clock->filling = TRUE;
1013     clock->time_index = 0;
1014     /* use the master periodic id to schedule sampling and
1015      * clock calibration. */
1016     clock->clockid = gst_clock_new_periodic_id (master,
1017         gst_clock_get_time (master), clock->timeout);
1018     gst_clock_id_wait_async (clock->clockid,
1019         (GstClockCallback) gst_clock_slave_callback, clock);
1020   }
1021   GST_CLOCK_SLAVE_UNLOCK (clock);
1022
1023   return TRUE;
1024
1025   /* ERRORS */
1026 not_supported:
1027   {
1028     GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
1029         "cannot be slaved to a master clock");
1030     GST_OBJECT_UNLOCK (clock);
1031     return FALSE;
1032   }
1033 }
1034
1035 /**
1036  * gst_clock_get_master
1037  * @clock: a #GstClock 
1038  *
1039  * Get the master clock that @clock is slaved to or %NULL when the clock is
1040  * not slaved to any master clock.
1041  *
1042  * Returns: a master #GstClock or %NULL when this clock is not slaved to a
1043  * master clock. Unref after usage.
1044  *
1045  * MT safe.
1046  */
1047 GstClock *
1048 gst_clock_get_master (GstClock * clock)
1049 {
1050   GstClock *result = NULL;
1051
1052   g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);
1053
1054   GST_OBJECT_LOCK (clock);
1055   if (clock->master)
1056     result = gst_object_ref (clock->master);
1057   GST_OBJECT_UNLOCK (clock);
1058
1059   return result;
1060 }
1061
1062 /* http://mathworld.wolfram.com/LeastSquaresFitting.html
1063  * with SLAVE_LOCK
1064  */
1065 static gboolean
1066 do_linear_regression (GstClock * clock, GstClockTime * m_num,
1067     GstClockTime * m_denom, GstClockTime * b, GstClockTime * xbase,
1068     gdouble * r_squared)
1069 {
1070   GstClockTime *newx, *newy;
1071   GstClockTime xmin, ymin, xbar, ybar, xbar4, ybar4;
1072   GstClockTimeDiff sxx, sxy, syy;
1073   GstClockTime *x, *y;
1074   gint i, j;
1075   guint n;
1076
1077   xbar = ybar = sxx = syy = sxy = 0;
1078
1079   x = clock->times;
1080   y = clock->times + 2;
1081   n = clock->filling ? clock->time_index : clock->window_size;
1082
1083 #ifdef DEBUGGING_ENABLED
1084   GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "doing regression on:");
1085   for (i = j = 0; i < n; i++, j += 4)
1086     GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
1087         "  %" G_GUINT64_FORMAT "  %" G_GUINT64_FORMAT, x[j], y[j]);
1088 #endif
1089
1090   xmin = ymin = G_MAXUINT64;
1091   for (i = j = 0; i < n; i++, j += 4) {
1092     xmin = MIN (xmin, x[j]);
1093     ymin = MIN (ymin, y[j]);
1094   }
1095
1096 #ifdef DEBUGGING_ENABLED
1097   GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "min x: %" G_GUINT64_FORMAT,
1098       xmin);
1099   GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "min y: %" G_GUINT64_FORMAT,
1100       ymin);
1101 #endif
1102
1103   newx = clock->times + 1;
1104   newy = clock->times + 3;
1105
1106   /* strip off unnecessary bits of precision */
1107   for (i = j = 0; i < n; i++, j += 4) {
1108     newx[j] = x[j] - xmin;
1109     newy[j] = y[j] - ymin;
1110   }
1111
1112 #ifdef DEBUGGING_ENABLED
1113   GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "reduced numbers:");
1114   for (i = j = 0; i < n; i++, j += 4)
1115     GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
1116         "  %" G_GUINT64_FORMAT "  %" G_GUINT64_FORMAT, newx[j], newy[j]);
1117 #endif
1118
1119   /* have to do this precisely otherwise the results are pretty much useless.
1120    * should guarantee that none of these accumulators can overflow */
1121
1122   /* quantities on the order of 1e10 -> 30 bits; window size a max of 2^10, so
1123      this addition could end up around 2^40 or so -- ample headroom */
1124   for (i = j = 0; i < n; i++, j += 4) {
1125     xbar += newx[j];
1126     ybar += newy[j];
1127   }
1128   xbar /= n;
1129   ybar /= n;
1130
1131 #ifdef DEBUGGING_ENABLED
1132   GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "  xbar  = %" G_GUINT64_FORMAT,
1133       xbar);
1134   GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "  ybar  = %" G_GUINT64_FORMAT,
1135       ybar);
1136 #endif
1137
1138   /* multiplying directly would give quantities on the order of 1e20 -> 60 bits;
1139      times the window size that's 70 which is too much. Instead we (1) subtract
1140      off the xbar*ybar in the loop instead of after, to avoid accumulation; (2)
1141      shift off 4 bits from each multiplicand, giving an expected ceiling of 52
1142      bits, which should be enough. Need to check the incoming range and domain
1143      to ensure this is an appropriate loss of precision though. */
1144   xbar4 = xbar >> 4;
1145   ybar4 = ybar >> 4;
1146   for (i = j = 0; i < n; i++, j += 4) {
1147     GstClockTime newx4, newy4;
1148
1149     newx4 = newx[j] >> 4;
1150     newy4 = newy[j] >> 4;
1151
1152     sxx += newx4 * newx4 - xbar4 * xbar4;
1153     syy += newy4 * newy4 - ybar4 * ybar4;
1154     sxy += newx4 * newy4 - xbar4 * ybar4;
1155   }
1156
1157   if (G_UNLIKELY (sxx == 0))
1158     goto invalid;
1159
1160   *m_num = sxy;
1161   *m_denom = sxx;
1162   *xbase = xmin;
1163   *b = (ybar + ymin) - gst_util_uint64_scale (xbar, *m_num, *m_denom);
1164   *r_squared = ((double) sxy * (double) sxy) / ((double) sxx * (double) syy);
1165
1166 #ifdef DEBUGGING_ENABLED
1167   GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "  m      = %g",
1168       ((double) *m_num) / *m_denom);
1169   GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "  b      = %" G_GUINT64_FORMAT,
1170       *b);
1171   GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "  xbase  = %" G_GUINT64_FORMAT,
1172       *xbase);
1173   GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "  r2     = %g", *r_squared);
1174 #endif
1175
1176   return TRUE;
1177
1178 invalid:
1179   {
1180     GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "sxx == 0, regression failed");
1181     return FALSE;
1182   }
1183 }
1184
1185 /**
1186  * gst_clock_add_observation
1187  * @clock: a #GstClock 
1188  * @slave: a time on the slave
1189  * @master: a time on the master
1190  * @r_squared: a pointer to hold the result
1191  *
1192  * The time @master of the master clock and the time @slave of the slave
1193  * clock are added to the list of observations. If enough observations
1194  * are available, a linear regression algorithm is run on the
1195  * observations and @clock is recalibrated.
1196  *
1197  * If this functions returns %TRUE, @r_squared will contain the 
1198  * correlation coefficient of the interpolation. A value of 1.0
1199  * means a perfect regression was performed. This value can
1200  * be used to control the sampling frequency of the master and slave
1201  * clocks.
1202  *
1203  * Returns: %TRUE if enough observations were added to run the 
1204  * regression algorithm.
1205  *
1206  * MT safe.
1207  */
1208 gboolean
1209 gst_clock_add_observation (GstClock * clock, GstClockTime slave,
1210     GstClockTime master, gdouble * r_squared)
1211 {
1212   GstClockTime m_num, m_denom, b, xbase;
1213
1214   g_return_val_if_fail (GST_IS_CLOCK (clock), FALSE);
1215   g_return_val_if_fail (r_squared != NULL, FALSE);
1216
1217   GST_CLOCK_SLAVE_LOCK (clock);
1218
1219   GST_CAT_LOG_OBJECT (GST_CAT_CLOCK, clock,
1220       "adding observation slave %" GST_TIME_FORMAT ", master %" GST_TIME_FORMAT,
1221       GST_TIME_ARGS (slave), GST_TIME_ARGS (master));
1222
1223   clock->times[(4 * clock->time_index)] = slave;
1224   clock->times[(4 * clock->time_index) + 2] = master;
1225
1226   clock->time_index++;
1227   if (G_UNLIKELY (clock->time_index == clock->window_size)) {
1228     clock->filling = FALSE;
1229     clock->time_index = 0;
1230   }
1231
1232   if (G_UNLIKELY (clock->filling
1233           && clock->time_index < clock->window_threshold))
1234     goto filling;
1235
1236   if (!do_linear_regression (clock, &m_num, &m_denom, &b, &xbase, r_squared))
1237     goto invalid;
1238
1239   GST_CLOCK_SLAVE_UNLOCK (clock);
1240
1241   GST_CAT_LOG_OBJECT (GST_CAT_CLOCK, clock,
1242       "adjusting clock to m=%" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT ", b=%"
1243       G_GUINT64_FORMAT " (rsquared=%g)", m_num, m_denom, b, *r_squared);
1244
1245   /* if we have a valid regression, adjust the clock */
1246   gst_clock_set_calibration (clock, xbase, b, m_num, m_denom);
1247
1248   return TRUE;
1249
1250 filling:
1251   {
1252     GST_CLOCK_SLAVE_UNLOCK (clock);
1253     return FALSE;
1254   }
1255 invalid:
1256   {
1257     /* no valid regression has been done, ignore the result then */
1258     GST_CLOCK_SLAVE_UNLOCK (clock);
1259     return TRUE;
1260   }
1261 }
1262
1263 static void
1264 gst_clock_update_stats (GstClock * clock)
1265 {
1266 }
1267
1268 static void
1269 gst_clock_set_property (GObject * object, guint prop_id,
1270     const GValue * value, GParamSpec * pspec)
1271 {
1272   GstClock *clock;
1273
1274   clock = GST_CLOCK (object);
1275
1276   switch (prop_id) {
1277     case PROP_STATS:
1278       GST_OBJECT_LOCK (clock);
1279       clock->stats = g_value_get_boolean (value);
1280       GST_OBJECT_UNLOCK (clock);
1281       g_object_notify (object, "stats");
1282       break;
1283     case PROP_WINDOW_SIZE:
1284       GST_CLOCK_SLAVE_LOCK (clock);
1285       clock->window_size = g_value_get_int (value);
1286       clock->window_threshold =
1287           MIN (clock->window_threshold, clock->window_size);
1288       clock->times =
1289           g_renew (GstClockTime, clock->times, 4 * clock->window_size);
1290       /* restart calibration */
1291       clock->filling = TRUE;
1292       clock->time_index = 0;
1293       GST_CLOCK_SLAVE_UNLOCK (clock);
1294       break;
1295     case PROP_WINDOW_THRESHOLD:
1296       GST_CLOCK_SLAVE_LOCK (clock);
1297       clock->window_threshold =
1298           MIN (g_value_get_int (value), clock->window_size);
1299       GST_CLOCK_SLAVE_UNLOCK (clock);
1300       break;
1301     case PROP_TIMEOUT:
1302       GST_CLOCK_SLAVE_LOCK (clock);
1303       clock->timeout = g_value_get_uint64 (value);
1304       GST_CLOCK_SLAVE_UNLOCK (clock);
1305       break;
1306     default:
1307       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1308       break;
1309   }
1310 }
1311
1312 static void
1313 gst_clock_get_property (GObject * object, guint prop_id,
1314     GValue * value, GParamSpec * pspec)
1315 {
1316   GstClock *clock;
1317
1318   clock = GST_CLOCK (object);
1319
1320   switch (prop_id) {
1321     case PROP_STATS:
1322       GST_OBJECT_LOCK (clock);
1323       g_value_set_boolean (value, clock->stats);
1324       GST_OBJECT_UNLOCK (clock);
1325       break;
1326     case PROP_WINDOW_SIZE:
1327       GST_CLOCK_SLAVE_LOCK (clock);
1328       g_value_set_int (value, clock->window_size);
1329       GST_CLOCK_SLAVE_UNLOCK (clock);
1330       break;
1331     case PROP_WINDOW_THRESHOLD:
1332       GST_CLOCK_SLAVE_LOCK (clock);
1333       g_value_set_int (value, clock->window_threshold);
1334       GST_CLOCK_SLAVE_UNLOCK (clock);
1335       break;
1336     case PROP_TIMEOUT:
1337       GST_CLOCK_SLAVE_LOCK (clock);
1338       g_value_set_uint64 (value, clock->timeout);
1339       GST_CLOCK_SLAVE_UNLOCK (clock);
1340       break;
1341     default:
1342       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1343       break;
1344   }
1345 }