- Added first attempt at general caching mechanism (GstTimeCache renamed to GstCache)
[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  *
5  * gstclock.c: Clock subsystem for maintaining time sync
6  *
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.
11  *
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.
16  *
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.
21  */
22
23 #include <sys/time.h>
24
25 /* #define GST_DEBUG_ENABLED */
26 #include "gst_private.h"
27
28 #include "gstclock.h"
29 #include "gstlog.h"
30 #include "gstmemchunk.h"
31
32 enum {
33   ARG_0,
34   ARG_STATS,
35 };
36
37 #define CLASS(clock)  GST_CLOCK_CLASS (G_OBJECT_GET_CLASS (clock))
38
39 static GstMemChunk *_gst_clock_entries_chunk;
40
41 static void             gst_clock_class_init            (GstClockClass *klass);
42 static void             gst_clock_init                  (GstClock *clock);
43 static void             gst_clock_set_property          (GObject *object, guint prop_id, 
44                                                          const GValue *value, GParamSpec *pspec);
45 static void             gst_clock_get_property          (GObject *object, guint prop_id, 
46                                                          GValue *value, GParamSpec * pspec);
47 static void             gst_clock_update_stats          (GstClock *clock);
48
49
50 static GstObjectClass *parent_class = NULL;
51 /* static guint gst_clock_signals[LAST_SIGNAL] = { 0 }; */
52
53 static GMutex *_gst_clock_mutex;
54 static GCond  *_gst_clock_cond;
55
56 static inline GstClockID
57 gst_clock_entry_new (GstClock *clock, GstClockTime time, 
58                      GstClockTime interval, GstClockEntryType type)
59 {
60   GstClockEntry *entry;
61
62   entry = gst_mem_chunk_alloc (_gst_clock_entries_chunk);
63
64   entry->clock = clock;
65   entry->time = time;
66   entry->interval = time;
67   entry->type = type;
68   entry->status = GST_CLOCK_ENTRY_OK;
69
70   return (GstClockID) entry;
71 }
72
73 /**
74  * gst_clock_new_single_shot_id
75  * @clock: The clockid to get a single shot notification from
76  * @time: the requested time
77  *
78  * Get an ID from the given clock to trigger a single shot 
79  * notification at the requested time.
80  *
81  * Returns: An id that can be used to request the time notification.
82  */
83 GstClockID
84 gst_clock_new_single_shot_id (GstClock *clock, GstClockTime time)
85 {
86   g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);
87
88   return gst_clock_entry_new (clock, 
89                               time, 
90                               GST_CLOCK_TIME_NONE, 
91                               GST_CLOCK_ENTRY_SINGLE);
92 }
93
94 /**
95  * gst_clock_new_periodic__id
96  * @clock: The clockid to get a periodic notification id from
97  * @start_time: the requested start time
98  * @interval: the requested interval
99  *
100  * Get an ID from the given clock to trigger a periodic notification.
101  * The periodeic notifications will be start at time start_time and
102  * will then be fired with the given interval.
103  *
104  * Returns: An id that can be used to request the time notification.
105  */
106 GstClockID
107 gst_clock_new_periodic_id (GstClock *clock, GstClockTime start_time,
108                            GstClockTime interval)
109 {
110   g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);
111   g_return_val_if_fail (start_time != GST_CLOCK_TIME_NONE, NULL);
112   g_return_val_if_fail (interval != 0, NULL);
113
114   return gst_clock_entry_new (clock, 
115                               start_time, 
116                               interval, 
117                               GST_CLOCK_ENTRY_PERIODIC);
118 }
119
120 /**
121  * gst_clock_id_get_time
122  * @id: The clockid to query
123  *
124  * Get the time of the clock ID
125  *
126  * Returns: the time of the given clock id
127  */
128 GstClockTime
129 gst_clock_id_get_time (GstClockID id)
130 {
131   g_return_val_if_fail (id != NULL, GST_CLOCK_TIME_NONE);
132
133   return GST_CLOCK_ENTRY_TIME ((GstClockEntry *)id);
134 }
135
136
137 /**
138  * gst_clock_id_wait
139  * @id: The clockid to wait on
140  * @jitter: A pointer that will contain the jitter
141  *
142  * Perform a blocking wait on the given ID. The jitter arg can be
143  * NULL
144  *
145  * Returns: the result of the blocking wait.
146  */
147 GstClockReturn
148 gst_clock_id_wait (GstClockID id, GstClockTimeDiff *jitter)
149 {
150   GstClockEntry *entry;
151   GstClock *clock;
152   GstClockReturn res = GST_CLOCK_UNSUPPORTED;
153   GstClockTime requested;
154   
155   g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR);
156
157   entry = (GstClockEntry *) id;
158   requested = GST_CLOCK_ENTRY_TIME (entry);
159
160   if (requested == GST_CLOCK_TIME_NONE) {
161     res = GST_CLOCK_TIMEOUT;
162     goto done;
163   }
164   
165   clock = GST_CLOCK_ENTRY_CLOCK (entry);
166   
167   if (CLASS (clock)->wait) {
168     GstClockTime now;
169
170     do {
171       res = CLASS (clock)->wait (clock, entry);
172     }
173     while (res == GST_CLOCK_ENTRY_RESTART);
174
175     if (jitter) {
176       now = gst_clock_get_time (clock);
177       *jitter = now - requested;
178     }
179
180     if (clock->stats) {
181       gst_clock_update_stats (clock);
182     }
183   }
184
185 done:
186   if (entry->type == GST_CLOCK_ENTRY_SINGLE) {
187     gst_clock_id_free (id);
188   }
189
190   return res;
191 }
192
193 /**
194  * gst_clock_wait_async
195  * @clock: a #GstClock to wait on
196  * @time: The #GstClockTime to wait for
197  * @func: The callback function 
198  * @user_data: User data passed in the calback
199  *
200  * Register a callback on the given clock that will be triggered 
201  * when the clock has reached the given time. A ClockID is returned
202  * that can be used to cancel the request.
203  *
204  * Returns: the result of the non blocking wait.
205  */
206 GstClockReturn
207 gst_clock_id_wait_async (GstClockID id,
208                          GstClockCallback func, gpointer user_data)
209 {
210   GstClockEntry *entry;
211   GstClock *clock;
212   GstClockReturn res = GST_CLOCK_UNSUPPORTED;
213   
214   g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR);
215   g_return_val_if_fail (func != NULL, GST_CLOCK_ERROR);
216
217   entry = (GstClockEntry *) id;
218   clock = entry->clock;
219
220   if (GST_CLOCK_ENTRY_TIME (entry) == GST_CLOCK_TIME_NONE) {
221     (func) (clock, GST_CLOCK_TIME_NONE, id, user_data);
222     return GST_CLOCK_TIMEOUT;
223   }
224
225   if (CLASS (clock)->wait_async) {
226     res = CLASS (clock)->wait_async (clock, entry, func, user_data);
227   }
228
229   return res;
230 }
231
232 /**
233  * gst_clock_remove_id
234  * @clock: The clock to cancel the request on
235  * @id: The id to cancel
236  *
237  * Cancel an outstanding async notification request with the given ID.
238  * This can be an ID generated with gst_clock_wait_async() or 
239  * gst_clock_notify_async().
240  */
241 void
242 gst_clock_id_unschedule (GstClockID id)
243 {
244   GstClockEntry *entry;
245   GstClock *clock;
246   
247   g_return_if_fail (id != NULL);
248
249   entry = (GstClockEntry *) id;
250   clock = entry->clock;
251
252   if (CLASS (clock)->unschedule)
253     CLASS (clock)->unschedule (clock, entry);
254 }
255
256 /**
257  * gst_clock_id_free
258  * @id: The clockid to free
259  *
260  * Free the resources held by the given id
261  */
262 void
263 gst_clock_id_free (GstClockID id)
264 {
265   g_return_if_fail (id != NULL);
266
267   gst_mem_chunk_free (_gst_clock_entries_chunk, id);
268 }
269
270 /**
271  * gst_clock_unlock_id
272  * @id: The clockid to unlock
273  *
274  * Unlock the givan ClockID.
275  */
276 void
277 gst_clock_id_unlock (GstClockID id)
278 {
279   GstClockEntry *entry;
280   GstClock *clock;
281   
282   g_return_if_fail (id != NULL);
283
284   entry = (GstClockEntry *) id;
285   clock = entry->clock;
286
287   if (CLASS (clock)->unlock)
288     CLASS (clock)->unlock (clock, entry);
289 }
290
291
292 /**
293  * GstClock abstract base class implementation
294  */
295 GType
296 gst_clock_get_type (void)
297 {
298   static GType clock_type = 0;
299
300   if (!clock_type) {
301     static const GTypeInfo clock_info = {
302       sizeof (GstClockClass),
303       NULL,
304       NULL,
305       (GClassInitFunc) gst_clock_class_init,
306       NULL,
307       NULL,
308       sizeof (GstClock),
309       4,
310       (GInstanceInitFunc) gst_clock_init,
311       NULL
312     };
313     clock_type = g_type_register_static (GST_TYPE_OBJECT, "GstClock", 
314                                          &clock_info,  G_TYPE_FLAG_ABSTRACT);
315   }
316   return clock_type;
317 }
318
319 static void
320 gst_clock_class_init (GstClockClass *klass)
321 {
322   GObjectClass *gobject_class;
323   GstObjectClass *gstobject_class;
324
325   gobject_class = (GObjectClass*) klass;
326   gstobject_class = (GstObjectClass*) klass;
327
328   parent_class = g_type_class_ref (GST_TYPE_OBJECT);
329
330   if (!g_thread_supported ())
331     g_thread_init (NULL);
332
333   _gst_clock_entries_chunk = gst_mem_chunk_new ("GstClockEntries",
334                      sizeof (GstClockEntry), sizeof (GstClockEntry) * 32,
335                      G_ALLOC_AND_FREE);
336
337   _gst_clock_mutex = g_mutex_new ();
338   _gst_clock_cond  = g_cond_new ();
339
340   gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_clock_set_property);
341   gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_clock_get_property);
342
343   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_STATS,
344     g_param_spec_boolean ("stats", "Stats", "Enable clock stats",
345                           FALSE, G_PARAM_READWRITE));
346 }
347
348 static void
349 gst_clock_init (GstClock *clock)
350 {
351   clock->speed = 1.0;
352   clock->active = FALSE;
353   clock->start_time = 0;
354   clock->last_time = 0;
355   clock->entries = NULL;
356   clock->flags = 0;
357   clock->stats = FALSE;
358
359   clock->active_mutex = g_mutex_new ();
360   clock->active_cond = g_cond_new ();
361 }
362
363 /**
364  * gst_clock_set_speed
365  * @clock: a #GstClock to modify
366  * @speed: the speed to set on the clock
367  *
368  * Sets the speed on the given clock. 1.0 is the default 
369  * speed.
370  *
371  * Returns: the new speed of the clock.
372  */
373 gdouble
374 gst_clock_set_speed (GstClock *clock, gdouble speed)
375 {
376   g_return_val_if_fail (GST_IS_CLOCK (clock), 0.0);
377
378   if (CLASS (clock)->change_speed)
379     clock->speed = CLASS (clock)->change_speed (clock, clock->speed, speed);
380
381   return clock->speed;
382 }
383
384 /**
385  * gst_clock_get_speed
386  * @clock: a #GstClock to query
387  *
388  * Gets the speed of the given clock.
389  *
390  * Returns: the speed of the clock.
391  */
392 gdouble
393 gst_clock_get_speed (GstClock *clock)
394 {
395   g_return_val_if_fail (GST_IS_CLOCK (clock), 0.0);
396
397   return clock->speed;
398 }
399
400 /**
401  * gst_clock_set_resolution
402  * @clock: The clock set the resolution on
403  * @resolution: The resolution to set
404  *
405  * Set the accuracy of the clock.
406  *
407  * Returns: the new resolution of the clock.
408  */
409 guint64
410 gst_clock_set_resolution (GstClock *clock, guint64 resolution)
411 {
412   g_return_val_if_fail (GST_IS_CLOCK (clock), 0LL);
413   g_return_val_if_fail (resolution != 0, 0LL);
414
415   if (CLASS (clock)->change_resolution)
416     clock->resolution = CLASS (clock)->change_resolution (clock, clock->resolution, resolution);
417
418   return clock->resolution;
419 }
420
421 /**
422  * gst_clock_get_resolution
423  * @clock: The clock get the resolution of
424  *
425  * Get the accuracy of the clock.
426  *
427  * Returns: the resolution of the clock in microseconds.
428  */
429 guint64
430 gst_clock_get_resolution (GstClock *clock)
431 {
432   g_return_val_if_fail (GST_IS_CLOCK (clock), 0LL);
433
434   if (CLASS (clock)->get_resolution)
435     return CLASS (clock)->get_resolution (clock);
436
437   return 1LL;
438 }
439
440 /**
441  * gst_clock_set_active
442  * @clock: a #GstClock to set state of
443  * @active: flag indicating if the clock should be activated (TRUE) or deactivated
444  *
445  * Activates or deactivates the clock based on the active parameter.
446  * As soon as the clock is activated, the time will start ticking.
447  */
448 void
449 gst_clock_set_active (GstClock *clock, gboolean active)
450 {
451   GstClockTime time = 0LL;
452
453   g_return_if_fail (GST_IS_CLOCK (clock));
454
455   clock->active = active;
456                 
457   if (CLASS (clock)->get_internal_time) {
458     time = CLASS (clock)->get_internal_time (clock);
459   }
460
461   GST_LOCK (clock);
462   if (active) {
463     clock->start_time = time - clock->last_time;
464     clock->accept_discont = TRUE;
465   }
466   else {
467     clock->last_time = time - clock->start_time;
468     clock->accept_discont = FALSE;
469   }
470   GST_UNLOCK (clock);
471
472   g_mutex_lock (clock->active_mutex);   
473   g_cond_broadcast (clock->active_cond);        
474   g_mutex_unlock (clock->active_mutex); 
475 }
476
477 /**
478  * gst_clock_is_active
479  * @clock: a #GstClock to query
480  *
481  * Checks if the given clock is active.
482  * 
483  * Returns: TRUE if the clock is active.
484  */
485 gboolean
486 gst_clock_is_active (GstClock *clock)
487 {
488   g_return_val_if_fail (GST_IS_CLOCK (clock), FALSE);
489
490   return clock->active;
491 }
492
493 /**
494  * gst_clock_reset
495  * @clock: a #GstClock to reset
496  *
497  * Reset the clock to time 0.
498  */
499 void
500 gst_clock_reset (GstClock *clock)
501 {
502   GstClockTime time = 0LL;
503
504   g_return_if_fail (GST_IS_CLOCK (clock));
505
506   if (CLASS (clock)->get_internal_time) {
507     time = CLASS (clock)->get_internal_time (clock);
508   }
509
510   GST_LOCK (clock);
511   clock->active = FALSE;
512   clock->start_time = time;
513   clock->last_time = 0LL;
514   GST_UNLOCK (clock);
515 }
516
517 /**
518  * gst_clock_handle_discont
519  * @clock: a #GstClock to notify of the discontinuity
520  * @time: The new time
521  *
522  * Notifies the clock of a discontinuity in time.
523  * 
524  * Returns: TRUE if the clock was updated. It is possible that
525  * the clock was not updated by this call because only the first
526  * discontinuitity in the pipeline is honoured.
527  */
528 gboolean
529 gst_clock_handle_discont (GstClock *clock, guint64 time)
530 {
531   GstClockTime itime = 0LL;
532   
533   GST_DEBUG (GST_CAT_CLOCK, "clock discont %llu %llu %d", time, clock->start_time, clock->accept_discont);
534
535   if (time == GST_CLOCK_TIME_NONE)
536     return TRUE;
537
538   GST_LOCK (clock);
539   if (clock->accept_discont) {
540     if (CLASS (clock)->get_internal_time) {
541       itime = CLASS (clock)->get_internal_time (clock);
542     }
543   }
544   else {
545     GST_UNLOCK (clock);
546     GST_DEBUG (GST_CAT_CLOCK, "clock discont refused %llu %llu", time, clock->start_time);
547     return FALSE;
548   }
549
550   clock->start_time = itime - time;
551   clock->last_time = time;
552   clock->accept_discont = FALSE;
553   GST_UNLOCK (clock);
554
555   GST_DEBUG (GST_CAT_CLOCK, "new time %llu", gst_clock_get_time (clock));
556
557   g_mutex_lock (clock->active_mutex);   
558   g_cond_broadcast (clock->active_cond);        
559   g_mutex_unlock (clock->active_mutex); 
560
561   return TRUE;
562 }
563
564 /**
565  * gst_clock_get_time
566  * @clock: a #GstClock to query
567  *
568  * Gets the current time of the given clock. The time is always
569  * monotonically increasing.
570  * 
571  * Returns: the time of the clock.
572  */
573 GstClockTime
574 gst_clock_get_time (GstClock *clock)
575 {
576   GstClockTime ret = 0LL;
577
578   g_return_val_if_fail (GST_IS_CLOCK (clock), 0LL);
579
580   if (!clock->active) {
581     /* clock is not activen return previous time */
582     ret = clock->last_time;
583   }
584   else {
585     if (CLASS (clock)->get_internal_time) {
586       ret = CLASS (clock)->get_internal_time (clock) - clock->start_time;
587     }
588     /* make sure the time is increasing, else return last_time */
589     if ((gint64) ret < (gint64) clock->last_time) {
590       ret = clock->last_time;
591     }
592     else {
593       clock->last_time = ret;
594     }
595   }
596
597   return ret;
598 }
599
600 /**
601  * gst_clock_get_next_id
602  * @clock: The clock to query
603  *
604  * Get the clockid of the next event.
605  *
606  * Returns: a clockid or NULL is no event is pending.
607  */
608 GstClockID
609 gst_clock_get_next_id (GstClock *clock)
610 {
611   GstClockEntry *entry = NULL;
612
613   g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);
614
615   GST_LOCK (clock);
616   if (clock->entries)
617     entry = GST_CLOCK_ENTRY (clock->entries->data);
618   GST_UNLOCK (clock);
619
620   return (GstClockID *) entry;
621 }
622
623 static void
624 gst_clock_update_stats (GstClock *clock)
625 {
626 }
627
628 static void
629 gst_clock_set_property (GObject *object, guint prop_id,
630                         const GValue *value, GParamSpec *pspec)
631 {
632   GstClock *clock;
633              
634   clock = GST_CLOCK (object);
635
636   switch (prop_id) {
637     case ARG_STATS:
638       clock->stats = g_value_get_boolean (value);
639       break;
640     default:
641       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
642       break;
643    }
644 }
645
646 static void
647 gst_clock_get_property (GObject *object, guint prop_id, 
648                         GValue *value, GParamSpec * pspec)
649 {
650   GstClock *clock;
651              
652   clock = GST_CLOCK (object);
653
654   switch (prop_id) {
655     case ARG_STATS:
656       g_value_set_boolean (value, clock->stats);
657       break;
658     default:
659       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
660       break;
661    }
662 }