First THREADED backport attempt, focusing on adding locks and making sure the API...
[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 #include <time.h>
25
26 #include "gst_private.h"
27
28 #include "gstclock.h"
29 #include "gstinfo.h"
30 #include "gstmemchunk.h"
31 #include "gstatomic_impl.h"
32
33 #ifndef GST_DISABLE_TRACE
34 /* #define GST_WITH_ALLOC_TRACE */
35 #include "gsttrace.h"
36 static GstAllocTrace *_gst_clock_entry_trace;
37 #endif
38
39 #define DEFAULT_EVENT_DIFF      (GST_SECOND)
40 #define DEFAULT_MAX_DIFF        (2 * GST_SECOND)
41
42 enum
43 {
44   ARG_0,
45   ARG_STATS,
46   ARG_MAX_DIFF,
47   ARG_EVENT_DIFF
48 };
49
50 static GstMemChunk *_gst_clock_entries_chunk;
51
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);
55
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);
61
62
63 static GstObjectClass *parent_class = NULL;
64
65 /* static guint gst_clock_signals[LAST_SIGNAL] = { 0 }; */
66
67 static GstClockID
68 gst_clock_entry_new (GstClock * clock, GstClockTime time,
69     GstClockTime interval, GstClockEntryType type)
70 {
71   GstClockEntry *entry;
72
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);
76 #endif
77   GST_CAT_DEBUG (GST_CAT_CLOCK, "created entry %p", entry);
78
79   gst_atomic_int_init (&entry->refcount, 1);
80   entry->clock = clock;
81   entry->time = time;
82   entry->interval = interval;
83   entry->type = type;
84   entry->status = GST_CLOCK_BUSY;
85
86   return (GstClockID) entry;
87 }
88
89 /**
90  * gst_clock_id_ref:
91  * @id: The clockid to ref
92  *
93  * Increase the refcount of the given clockid.
94  *
95  * Returns: The same #GstClockID with increased refcount.
96  *
97  * MT safe.
98  */
99 GstClockID
100 gst_clock_id_ref (GstClockID id)
101 {
102   g_return_val_if_fail (id != NULL, NULL);
103
104   gst_atomic_int_inc (&((GstClockEntry *) id)->refcount);
105
106   return id;
107 }
108
109 static void
110 _gst_clock_id_free (GstClockID id)
111 {
112   g_return_if_fail (id != NULL);
113
114   GST_CAT_DEBUG (GST_CAT_CLOCK, "freed entry %p", id);
115
116 #ifndef GST_DISABLE_TRACE
117   gst_alloc_trace_free (_gst_clock_entry_trace, id);
118 #endif
119   gst_mem_chunk_free (_gst_clock_entries_chunk, id);
120 }
121
122 /**
123  * gst_clock_id_unref:
124  * @id: The clockid to unref
125  *
126  * Unref the given clockid. When the refcount reaches 0 the
127  * #GstClockID will be freed.
128  *
129  * MT safe.
130  */
131 void
132 gst_clock_id_unref (GstClockID id)
133 {
134   gint zero;
135
136   g_return_if_fail (id != NULL);
137
138   zero = gst_atomic_int_dec_and_test (&((GstClockEntry *) id)->refcount);
139   /* if we ended up with the refcount at zero, free the id */
140   if (zero) {
141     _gst_clock_id_free (id);
142   }
143 }
144
145 /**
146  * gst_clock_new_single_shot_id
147  * @clock: The clockid to get a single shot notification from
148  * @time: the requested time
149  *
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.
153  *
154  * Returns: An id that can be used to request the time notification.
155  *
156  * MT safe.
157  */
158 GstClockID
159 gst_clock_new_single_shot_id (GstClock * clock, GstClockTime time)
160 {
161   g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);
162
163   return gst_clock_entry_new (clock,
164       time, GST_CLOCK_TIME_NONE, GST_CLOCK_ENTRY_SINGLE);
165 }
166
167 /**
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
172  *
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
176  * after usage.
177  *
178  * Returns: An id that can be used to request the time notification.
179  *
180  * MT safe.
181  */
182 GstClockID
183 gst_clock_new_periodic_id (GstClock * clock, GstClockTime start_time,
184     GstClockTime interval)
185 {
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);
189
190   return gst_clock_entry_new (clock,
191       start_time, interval, GST_CLOCK_ENTRY_PERIODIC);
192 }
193
194 /**
195  * gst_clock_id_compare_func
196  * @id1: A clockid
197  * @id2: A clockid to compare with
198  *
199  * Compares the two GstClockID instances. This function can be used
200  * as a GCompareFunc when sorting ids.
201  *
202  * Returns: negative value if a < b; zero if a = b; positive value if a > b
203  *
204  * MT safe.
205  */
206 gint
207 gst_clock_id_compare_func (gconstpointer id1, gconstpointer id2)
208 {
209   GstClockEntry *entry1, *entry2;
210
211   entry1 = (GstClockEntry *) id1;
212   entry2 = (GstClockEntry *) id2;
213
214   if (GST_CLOCK_ENTRY_TIME (entry1) > GST_CLOCK_ENTRY_TIME (entry2)) {
215     return 1;
216   }
217   if (GST_CLOCK_ENTRY_TIME (entry1) < GST_CLOCK_ENTRY_TIME (entry2)) {
218     return -1;
219   }
220
221   return entry1 - entry2;
222 }
223
224 /**
225  * gst_clock_id_get_time
226  * @id: The clockid to query
227  *
228  * Get the time of the clock ID
229  *
230  * Returns: the time of the given clock id.
231  *
232  * MT safe.
233  */
234 GstClockTime
235 gst_clock_id_get_time (GstClockID id)
236 {
237   g_return_val_if_fail (id != NULL, GST_CLOCK_TIME_NONE);
238
239   return GST_CLOCK_ENTRY_TIME ((GstClockEntry *) id);
240 }
241
242
243 /**
244  * gst_clock_id_wait
245  * @id: The clockid to wait on
246  * @jitter: A pointer that will contain the jitter
247  *
248  * Perform a blocking wait on the given ID. The jitter arg can be
249  * NULL.
250  *
251  * Returns: the result of the blocking wait.
252  *
253  * MT safe.
254  */
255 GstClockReturn
256 gst_clock_id_wait (GstClockID id, GstClockTimeDiff * jitter)
257 {
258   GstClockEntry *entry;
259   GstClock *clock;
260   GstClockReturn res;
261   GstClockTime requested;
262   GstClockClass *cclass;
263
264   g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR);
265
266   entry = (GstClockEntry *) id;
267   requested = GST_CLOCK_ENTRY_TIME (entry);
268
269   if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested)))
270     goto invalid_time;
271
272   if (G_UNLIKELY (entry->status == GST_CLOCK_UNSCHEDULED))
273     goto unscheduled;
274
275   clock = GST_CLOCK_ENTRY_CLOCK (entry);
276   cclass = GST_CLOCK_GET_CLASS (clock);
277
278   if (G_LIKELY (cclass->wait)) {
279
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);
283
284     if (jitter) {
285       GstClockTime now = gst_clock_get_time (clock);
286
287       *jitter = now - requested;
288     }
289     if (entry->type == GST_CLOCK_ENTRY_PERIODIC) {
290       entry->time += entry->interval;
291     }
292
293     if (clock->stats) {
294       gst_clock_update_stats (clock);
295     }
296   } else {
297     res = GST_CLOCK_UNSUPPORTED;
298   }
299   return res;
300
301   /* ERRORS */
302 invalid_time:
303   {
304     GST_CAT_DEBUG (GST_CAT_CLOCK, "invalid time requested, returning _BADTIME");
305     return GST_CLOCK_BADTIME;
306   }
307 unscheduled:
308   {
309     GST_CAT_DEBUG (GST_CAT_CLOCK, "entry was unscheduled return _UNSCHEDULED");
310     return GST_CLOCK_UNSCHEDULED;
311   }
312 }
313
314 /**
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
319  *
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.
325  *
326  * Returns: the result of the non blocking wait.
327  *
328  * MT safe.
329  */
330 GstClockReturn
331 gst_clock_id_wait_async (GstClockID id,
332     GstClockCallback func, gpointer user_data)
333 {
334   GstClockEntry *entry;
335   GstClock *clock;
336   GstClockReturn res;
337   GstClockClass *cclass;
338   GstClockTime requested;
339
340   g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR);
341   g_return_val_if_fail (func != NULL, GST_CLOCK_ERROR);
342
343   entry = (GstClockEntry *) id;
344   requested = GST_CLOCK_ENTRY_TIME (entry);
345   clock = GST_CLOCK_ENTRY_CLOCK (entry);
346
347   if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested)))
348     goto invalid_time;
349
350   if (G_UNLIKELY (entry->status == GST_CLOCK_UNSCHEDULED))
351     goto unscheduled;
352
353   cclass = GST_CLOCK_GET_CLASS (clock);
354
355   if (cclass->wait_async) {
356     entry->func = func;
357     entry->user_data = user_data;
358
359     res = cclass->wait_async (clock, entry);
360   } else {
361     res = GST_CLOCK_UNSUPPORTED;
362   }
363   return res;
364
365   /* ERRORS */
366 invalid_time:
367   {
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;
371   }
372 unscheduled:
373   {
374     GST_CAT_DEBUG (GST_CAT_CLOCK, "entry was unscheduled return _UNSCHEDULED");
375     return GST_CLOCK_UNSCHEDULED;
376   }
377 }
378
379 /**
380  * gst_clock_id_unschedule:
381  * @id: The id to unschedule
382  *
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.
387  *
388  * MT safe.
389  */
390 void
391 gst_clock_id_unschedule (GstClockID id)
392 {
393   GstClockEntry *entry;
394   GstClock *clock;
395   GstClockClass *cclass;
396
397   g_return_if_fail (id != NULL);
398
399   entry = (GstClockEntry *) id;
400   clock = entry->clock;
401
402   cclass = GST_CLOCK_GET_CLASS (clock);
403
404   if (cclass->unschedule)
405     cclass->unschedule (clock, entry);
406 }
407
408
409 /**
410  * GstClock abstract base class implementation
411  */
412 GType
413 gst_clock_get_type (void)
414 {
415   static GType clock_type = 0;
416
417   if (!clock_type) {
418     static const GTypeInfo clock_info = {
419       sizeof (GstClockClass),
420       NULL,
421       NULL,
422       (GClassInitFunc) gst_clock_class_init,
423       NULL,
424       NULL,
425       sizeof (GstClock),
426       0,
427       (GInstanceInitFunc) gst_clock_init,
428       NULL
429     };
430
431     clock_type = g_type_register_static (GST_TYPE_OBJECT, "GstClock",
432         &clock_info, G_TYPE_FLAG_ABSTRACT);
433   }
434   return clock_type;
435 }
436
437 static void
438 gst_clock_class_init (GstClockClass * klass)
439 {
440   GObjectClass *gobject_class;
441   GstObjectClass *gstobject_class;
442
443   gobject_class = (GObjectClass *) klass;
444   gstobject_class = (GstObjectClass *) klass;
445
446   parent_class = g_type_class_ref (GST_TYPE_OBJECT);
447
448   if (!g_thread_supported ())
449     g_thread_init (NULL);
450
451   _gst_clock_entries_chunk = gst_mem_chunk_new ("GstClockEntries",
452       sizeof (GstClockEntry), sizeof (GstClockEntry) * 32, G_ALLOC_AND_FREE);
453
454 #ifndef GST_DISABLE_TRACE
455   _gst_clock_entry_trace =
456       gst_alloc_trace_register (GST_CLOCK_ENTRY_TRACE_NAME);
457 #endif
458
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);
462
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));
466 }
467
468 static void
469 gst_clock_init (GstClock * clock)
470 {
471   clock->adjust = 0;
472   clock->last_time = 0;
473   clock->entries = NULL;
474   clock->entries_changed = g_cond_new ();
475   clock->flags = 0;
476   clock->stats = FALSE;
477 }
478
479 static void
480 gst_clock_dispose (GObject * object)
481 {
482   GstClock *clock = GST_CLOCK (object);
483
484   g_cond_free (clock->entries_changed);
485
486   G_OBJECT_CLASS (parent_class)->dispose (object);
487 }
488
489 /**
490  * gst_clock_set_resolution
491  * @clock: The clock set the resolution on
492  * @resolution: The resolution to set
493  *
494  * Set the accuracy of the clock.
495  *
496  * Returns: the new resolution of the clock.
497  */
498 guint64
499 gst_clock_set_resolution (GstClock * clock, guint64 resolution)
500 {
501   GstClockClass *cclass;
502
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));
505
506   cclass = GST_CLOCK_GET_CLASS (clock);
507
508   if (cclass->change_resolution)
509     clock->resolution =
510         cclass->change_resolution (clock, clock->resolution, resolution);
511
512   return clock->resolution;
513 }
514
515 /**
516  * gst_clock_get_resolution
517  * @clock: The clock get the resolution of
518  *
519  * Get the accuracy of the clock.
520  *
521  * Returns: the resolution of the clock in microseconds.
522  *
523  * MT safe.
524  */
525 guint64
526 gst_clock_get_resolution (GstClock * clock)
527 {
528   GstClockClass *cclass;
529
530   g_return_val_if_fail (GST_IS_CLOCK (clock), G_GINT64_CONSTANT (0));
531
532   cclass = GST_CLOCK_GET_CLASS (clock);
533
534   if (cclass->get_resolution)
535     return cclass->get_resolution (clock);
536
537   return G_GINT64_CONSTANT (1);
538 }
539
540 /**
541  * gst_clock_adjust_unlocked
542  * @clock: a #GstClock to use
543  * @internal: a clock time
544  *
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.
548  *
549  * Returns: the converted time of the clock.
550  *
551  * MT safe.
552  */
553 GstClockTime
554 gst_clock_adjust_unlocked (GstClock * clock, GstClockTime internal)
555 {
556   GstClockTime ret;
557
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;
562   } else {
563     clock->last_time = ret;
564   }
565   return ret;
566 }
567
568 /**
569  * gst_clock_get_time
570  * @clock: a #GstClock to query
571  *
572  * Gets the current time of the given clock. The time is always
573  * monotonically increasing.
574  *
575  * Returns: the time of the clock. Or GST_CLOCK_TIME_NONE when
576  * giving wrong input.
577  *
578  * MT safe.
579  */
580 GstClockTime
581 gst_clock_get_time (GstClock * clock)
582 {
583   GstClockTime ret;
584   GstClockClass *cclass;
585
586   g_return_val_if_fail (GST_IS_CLOCK (clock), GST_CLOCK_TIME_NONE);
587
588   cclass = GST_CLOCK_GET_CLASS (clock);
589
590   if (cclass->get_internal_time) {
591     ret = cclass->get_internal_time (clock);
592   } else {
593     ret = G_GINT64_CONSTANT (0);
594   }
595   GST_CAT_DEBUG (GST_CAT_CLOCK, "internal time %" GST_TIME_FORMAT,
596       GST_TIME_ARGS (ret));
597
598   GST_LOCK (clock);
599   ret = gst_clock_adjust_unlocked (clock, ret);
600   GST_UNLOCK (clock);
601
602   GST_CAT_DEBUG (GST_CAT_CLOCK, "adjusted time %" GST_TIME_FORMAT,
603       GST_TIME_ARGS (ret));
604
605   return ret;
606 }
607
608 /**
609  * gst_clock_set_time_adjust
610  * @clock: a #GstClock to adjust
611  * @adjust: the adjust value
612  *
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.
618  *
619  * MT safe.
620  */
621 void
622 gst_clock_set_time_adjust (GstClock * clock, GstClockTime adjust)
623 {
624   g_return_if_fail (GST_IS_CLOCK (clock));
625
626   GST_LOCK (clock);
627   clock->adjust = adjust;
628   GST_UNLOCK (clock);
629 }
630
631 static void
632 gst_clock_update_stats (GstClock * clock)
633 {
634 }
635
636 static void
637 gst_clock_set_property (GObject * object, guint prop_id,
638     const GValue * value, GParamSpec * pspec)
639 {
640   GstClock *clock;
641
642   clock = GST_CLOCK (object);
643
644   switch (prop_id) {
645     case ARG_STATS:
646       clock->stats = g_value_get_boolean (value);
647       g_object_notify (object, "stats");
648       break;
649     default:
650       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
651       break;
652   }
653 }
654
655 static void
656 gst_clock_get_property (GObject * object, guint prop_id,
657     GValue * value, GParamSpec * pspec)
658 {
659   GstClock *clock;
660
661   clock = GST_CLOCK (object);
662
663   switch (prop_id) {
664     case ARG_STATS:
665       g_value_set_boolean (value, clock->stats);
666       break;
667     default:
668       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
669       break;
670   }
671 }