fix doc build fix autogen
[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 #include "gst_private.h"
26
27 #include "gstclock.h"
28 #include "gstinfo.h"
29 #include "gstmemchunk.h"
30
31 #ifndef GST_DISABLE_TRACE
32 /* #define GST_WITH_ALLOC_TRACE */
33 #include "gsttrace.h"
34 static GstAllocTrace *_gst_clock_entry_trace;
35 #endif
36
37 #define DEFAULT_EVENT_DIFF      (GST_SECOND)
38 #define DEFAULT_MAX_DIFF        (2 * GST_SECOND)
39
40 enum {
41   ARG_0,
42   ARG_STATS,
43   ARG_MAX_DIFF,
44   ARG_EVENT_DIFF
45 };
46
47 static GstMemChunk   *_gst_clock_entries_chunk;
48
49 void                    gst_clock_id_unlock             (GstClockID id);
50
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 /* static guint gst_clock_signals[LAST_SIGNAL] = { 0 }; */
65
66 static GstClockID
67 gst_clock_entry_new (GstClock *clock, GstClockTime time, 
68                      GstClockTime interval, GstClockEntryType type)
69 {
70   GstClockEntry *entry;
71
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);
75 #endif
76
77   entry->clock = clock;
78   entry->time = time;
79   entry->interval = time;
80   entry->type = type;
81   entry->status = GST_CLOCK_ENTRY_OK;
82
83   return (GstClockID) entry;
84 }
85
86 /**
87  * gst_clock_new_single_shot_id
88  * @clock: The clockid to get a single shot notification from
89  * @time: the requested time
90  *
91  * Get an ID from the given clock to trigger a single shot 
92  * notification at the requested time.
93  *
94  * Returns: An id that can be used to request the time notification.
95  */
96 GstClockID
97 gst_clock_new_single_shot_id (GstClock *clock, GstClockTime time)
98 {
99   g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);
100
101   return gst_clock_entry_new (clock, 
102                               time, 
103                               GST_CLOCK_TIME_NONE, 
104                               GST_CLOCK_ENTRY_SINGLE);
105 }
106
107 /**
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
112  *
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.
116  *
117  * Returns: An id that can be used to request the time notification.
118  */
119 GstClockID
120 gst_clock_new_periodic_id (GstClock *clock, GstClockTime start_time,
121                            GstClockTime interval)
122 {
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);
126
127   return gst_clock_entry_new (clock, 
128                               start_time, 
129                               interval, 
130                               GST_CLOCK_ENTRY_PERIODIC);
131 }
132
133 /**
134  * gst_clock_id_get_time
135  * @id: The clockid to query
136  *
137  * Get the time of the clock ID
138  *
139  * Returns: the time of the given clock id
140  */
141 GstClockTime
142 gst_clock_id_get_time (GstClockID id)
143 {
144   g_return_val_if_fail (id != NULL, GST_CLOCK_TIME_NONE);
145
146   return GST_CLOCK_ENTRY_TIME ((GstClockEntry *)id);
147 }
148
149
150 /**
151  * gst_clock_id_wait
152  * @id: The clockid to wait on
153  * @jitter: A pointer that will contain the jitter
154  *
155  * Perform a blocking wait on the given ID. The jitter arg can be
156  * NULL
157  *
158  * Returns: the result of the blocking wait.
159  */
160 GstClockReturn
161 gst_clock_id_wait (GstClockID id, GstClockTimeDiff *jitter)
162 {
163   GstClockEntry *entry;
164   GstClock *clock;
165   GstClockReturn res = GST_CLOCK_UNSUPPORTED;
166   GstClockTime requested;
167   GstClockClass *cclass;
168   
169   g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR);
170
171   entry = (GstClockEntry *) id;
172   requested = GST_CLOCK_ENTRY_TIME (entry);
173
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;
177   }
178
179   clock = GST_CLOCK_ENTRY_CLOCK (entry);
180   cclass = GST_CLOCK_GET_CLASS (clock);
181   
182   if (cclass->wait) {
183     GstClockTime now;
184     
185     GST_LOCK (clock);
186     clock->entries = g_list_prepend (clock->entries, entry);
187     GST_UNLOCK (clock);
188
189     GST_CAT_DEBUG (GST_CAT_CLOCK, "waiting on clock");
190     do {
191       res = cclass->wait (clock, entry);
192     }
193     while (res == GST_CLOCK_ENTRY_RESTART);
194     GST_CAT_DEBUG (GST_CAT_CLOCK, "done waiting");
195
196     GST_LOCK (clock);
197     clock->entries = g_list_remove (clock->entries, entry);
198     GST_UNLOCK (clock);
199
200     if (jitter) {
201       now = gst_clock_get_time (clock);
202       *jitter = now - requested;
203     }
204
205     if (clock->stats) {
206       gst_clock_update_stats (clock);
207     }
208   }
209
210   return res;
211 }
212
213 /**
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
218  *
219  * Register a callback on the given clockid with the given
220  * function and user_data.
221  *
222  * Returns: the result of the non blocking wait.
223  */
224 GstClockReturn
225 gst_clock_id_wait_async (GstClockID id,
226                          GstClockCallback func, gpointer user_data)
227 {
228   GstClockEntry *entry;
229   GstClock *clock;
230   GstClockReturn res = GST_CLOCK_UNSUPPORTED;
231   GstClockClass *cclass;
232   
233   g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR);
234   g_return_val_if_fail (func != NULL, GST_CLOCK_ERROR);
235
236   entry = (GstClockEntry *) id;
237   clock = entry->clock;
238
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;
242   }
243
244   cclass = GST_CLOCK_GET_CLASS (clock);
245
246   if (cclass->wait_async) {
247     entry->func = func;
248     entry->user_data = user_data;
249
250     res = cclass->wait_async (clock, entry);
251   }
252
253   return res;
254 }
255
256 static void
257 gst_clock_reschedule_func (GstClockEntry *entry)
258 {
259   entry->status = GST_CLOCK_ENTRY_OK;
260   
261   gst_clock_id_unlock ((GstClockID)entry);
262 }
263
264 /**
265  * gst_clock_id_unschedule:
266  * @id: The id to unschedule
267  *
268  * Cancel an outstanding async notification request with the given ID.
269  */
270 void
271 gst_clock_id_unschedule (GstClockID id)
272 {
273   GstClockEntry *entry;
274   GstClock *clock;
275   GstClockClass *cclass;
276   
277   g_return_if_fail (id != NULL);
278
279   entry = (GstClockEntry *) id;
280   clock = entry->clock;
281
282   cclass = GST_CLOCK_GET_CLASS (clock);
283
284   if (cclass->unschedule)
285     cclass->unschedule (clock, entry);
286 }
287
288 /**
289  * gst_clock_id_free:
290  * @id: The clockid to free
291  *
292  * Free the resources held by the given id
293  */
294 void
295 gst_clock_id_free (GstClockID id)
296 {
297   g_return_if_fail (id != NULL);
298
299 #ifndef GST_DISABLE_TRACE
300   gst_alloc_trace_free (_gst_clock_entry_trace, id);
301 #endif
302   gst_mem_chunk_free (_gst_clock_entries_chunk, id);
303 }
304
305 /**
306  * gst_clock_id_unlock:
307  * @id: The clockid to unlock
308  *
309  * Unlock the givan ClockID.
310  */
311 void
312 gst_clock_id_unlock (GstClockID id)
313 {
314   GstClockEntry *entry;
315   GstClock *clock;
316   GstClockClass *cclass;
317   
318   g_return_if_fail (id != NULL);
319
320   entry = (GstClockEntry *) id;
321   clock = entry->clock;
322
323   cclass = GST_CLOCK_GET_CLASS (clock);
324
325   if (cclass->unlock)
326     cclass->unlock (clock, entry);
327 }
328
329
330 /**
331  * GstClock abstract base class implementation
332  */
333 GType
334 gst_clock_get_type (void)
335 {
336   static GType clock_type = 0;
337
338   if (!clock_type) {
339     static const GTypeInfo clock_info = {
340       sizeof (GstClockClass),
341       NULL,
342       NULL,
343       (GClassInitFunc) gst_clock_class_init,
344       NULL,
345       NULL,
346       sizeof (GstClock),
347       4,
348       (GInstanceInitFunc) gst_clock_init,
349       NULL
350     };
351     clock_type = g_type_register_static (GST_TYPE_OBJECT, "GstClock", 
352                                          &clock_info,  G_TYPE_FLAG_ABSTRACT);
353   }
354   return clock_type;
355 }
356
357 static void
358 gst_clock_class_init (GstClockClass *klass)
359 {
360   GObjectClass *gobject_class;
361   GstObjectClass *gstobject_class;
362
363   gobject_class = (GObjectClass*) klass;
364   gstobject_class = (GstObjectClass*) klass;
365
366   parent_class = g_type_class_ref (GST_TYPE_OBJECT);
367
368   if (!g_thread_supported ())
369     g_thread_init (NULL);
370
371   _gst_clock_entries_chunk = gst_mem_chunk_new ("GstClockEntries",
372                      sizeof (GstClockEntry), sizeof (GstClockEntry) * 32,
373                      G_ALLOC_AND_FREE);
374
375 #ifndef GST_DISABLE_TRACE
376   _gst_clock_entry_trace = gst_alloc_trace_register (GST_CLOCK_ENTRY_TRACE_NAME);
377 #endif
378
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);
382
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));
393 }
394
395 static void
396 gst_clock_init (GstClock *clock)
397 {
398   clock->max_diff = DEFAULT_MAX_DIFF;
399
400   clock->speed = 1.0;
401   clock->active = TRUE;
402   clock->start_time = 0;
403   clock->last_time = 0;
404   clock->entries = NULL;
405   clock->flags = 0;
406   clock->stats = FALSE;
407
408   clock->active_mutex = g_mutex_new ();
409   clock->active_cond = g_cond_new ();
410 }
411
412 static void
413 gst_clock_dispose (GObject *object)
414 {
415   GstClock *clock = GST_CLOCK (object);
416
417   g_mutex_free (clock->active_mutex);
418   g_cond_free (clock->active_cond);
419
420   G_OBJECT_CLASS (parent_class)->dispose (object);
421 }
422
423 /**
424  * gst_clock_set_speed
425  * @clock: a #GstClock to modify
426  * @speed: the speed to set on the clock
427  *
428  * Sets the speed on the given clock. 1.0 is the default 
429  * speed.
430  *
431  * Returns: the new speed of the clock.
432  */
433 gdouble
434 gst_clock_set_speed (GstClock *clock, gdouble speed)
435 {
436   g_return_val_if_fail (GST_IS_CLOCK (clock), 0.0);
437
438   GST_WARNING_OBJECT (clock, "called deprecated function");
439   return clock->speed;
440 }
441
442 /**
443  * gst_clock_get_speed
444  * @clock: a #GstClock to query
445  *
446  * Gets the speed of the given clock.
447  *
448  * Returns: the speed of the clock.
449  */
450 gdouble
451 gst_clock_get_speed (GstClock *clock)
452 {
453   g_return_val_if_fail (GST_IS_CLOCK (clock), 0.0);
454
455   GST_WARNING_OBJECT (clock, "called deprecated function");
456   return clock->speed;
457 }
458
459 /**
460  * gst_clock_set_resolution
461  * @clock: The clock set the resolution on
462  * @resolution: The resolution to set
463  *
464  * Set the accuracy of the clock.
465  *
466  * Returns: the new resolution of the clock.
467  */
468 guint64
469 gst_clock_set_resolution (GstClock *clock, guint64 resolution)
470 {
471   GstClockClass *cclass;
472
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));
475
476   cclass = GST_CLOCK_GET_CLASS (clock);
477
478   if (cclass->change_resolution)
479     clock->resolution = cclass->change_resolution (clock, clock->resolution, resolution);
480
481   return clock->resolution;
482 }
483
484 /**
485  * gst_clock_get_resolution
486  * @clock: The clock get the resolution of
487  *
488  * Get the accuracy of the clock.
489  *
490  * Returns: the resolution of the clock in microseconds.
491  */
492 guint64
493 gst_clock_get_resolution (GstClock *clock)
494 {
495   GstClockClass *cclass;
496
497   g_return_val_if_fail (GST_IS_CLOCK (clock), G_GINT64_CONSTANT (0));
498
499   cclass = GST_CLOCK_GET_CLASS (clock);
500
501   if (cclass->get_resolution)
502     return cclass->get_resolution (clock);
503
504   return G_GINT64_CONSTANT (1);
505 }
506
507 /**
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
511  *
512  * Activates or deactivates the clock based on the active parameter.
513  * As soon as the clock is activated, the time will start ticking.
514  */
515 void
516 gst_clock_set_active (GstClock *clock, gboolean active)
517 {
518   g_return_if_fail (GST_IS_CLOCK (clock));
519   
520   GST_ERROR_OBJECT (clock, "called deprecated function that does nothing now.");
521
522   return;
523 }
524
525 /**
526  * gst_clock_is_active
527  * @clock: a #GstClock to query
528  *
529  * Checks if the given clock is active.
530  * 
531  * Returns: TRUE if the clock is active.
532  */
533 gboolean
534 gst_clock_is_active (GstClock *clock)
535 {
536   g_return_val_if_fail (GST_IS_CLOCK (clock), FALSE);
537
538   GST_WARNING_OBJECT (clock, "called deprecated function.");
539
540   return TRUE;
541 }
542
543 /**
544  * gst_clock_reset
545  * @clock: a #GstClock to reset
546  *
547  * Reset the clock to time 0.
548  */
549 void
550 gst_clock_reset (GstClock *clock)
551 {
552   GstClockTime time = G_GINT64_CONSTANT (0);
553   GstClockClass *cclass;
554
555   g_return_if_fail (GST_IS_CLOCK (clock));
556
557   GST_ERROR_OBJECT (clock, "called deprecated function.");
558
559   cclass = GST_CLOCK_GET_CLASS (clock);
560                 
561   if (cclass->get_internal_time) {
562     time = cclass->get_internal_time (clock);
563   }
564
565   GST_LOCK (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);
570   GST_UNLOCK (clock);
571 }
572
573 /**
574  * gst_clock_handle_discont
575  * @clock: a #GstClock to notify of the discontinuity
576  * @time: The new time
577  *
578  * Notifies the clock of a discontinuity in time.
579  *
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.
583  */
584 gboolean
585 gst_clock_handle_discont (GstClock *clock, guint64 time)
586 {
587   GST_ERROR_OBJECT (clock, "called deprecated function.");
588
589   return FALSE;
590 }
591
592 /**
593  * gst_clock_get_time
594  * @clock: a #GstClock to query
595  *
596  * Gets the current time of the given clock. The time is always
597  * monotonically increasing.
598  *
599  * Returns: the time of the clock.
600  */
601 GstClockTime
602 gst_clock_get_time (GstClock *clock)
603 {
604   GstClockTime ret = G_GINT64_CONSTANT (0);
605   GstClockClass *cclass;
606
607   g_return_val_if_fail (GST_IS_CLOCK (clock), G_GINT64_CONSTANT (0));
608
609   cclass = GST_CLOCK_GET_CLASS (clock);
610
611   if (cclass->get_internal_time) {
612     ret = cclass->get_internal_time (clock) - clock->start_time;
613   }
614   /* make sure the time is increasing, else return last_time */
615   if ((gint64) ret < (gint64) clock->last_time) {
616     ret = clock->last_time;
617   }
618   else {
619     clock->last_time = ret;
620   }
621
622   return ret;
623 }
624
625 /**
626  * gst_clock_get_event_time:
627  * @clock: clock to query
628  * 
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.
633  *
634  * Returns: the time of the event
635  */
636 GstClockTime
637 gst_clock_get_event_time (GstClock *clock)
638 {
639   GstClockTime time;
640   
641   g_return_val_if_fail (GST_IS_CLOCK (clock), GST_CLOCK_TIME_NONE);
642   
643   time = gst_clock_get_time (clock);
644
645   if (clock->last_event + clock->max_event_diff >= time) {
646     GST_LOG_OBJECT (clock, "reporting last event time %"G_GUINT64_FORMAT, 
647         clock->last_event);
648   } else {
649     GST_LOG_OBJECT (clock, "reporting new event time %"G_GUINT64_FORMAT, 
650         clock->last_event);
651     clock->last_event = time;
652   }
653   
654   return clock->last_event;
655 }
656
657 /**
658  * gst_clock_get_next_id
659  * @clock: The clock to query
660  *
661  * Get the clockid of the next event.
662  *
663  * Returns: a clockid or NULL is no event is pending.
664  */
665 GstClockID
666 gst_clock_get_next_id (GstClock *clock)
667 {
668   GstClockEntry *entry = NULL;
669
670   g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);
671
672   GST_LOCK (clock);
673   if (clock->entries)
674     entry = GST_CLOCK_ENTRY (clock->entries->data);
675   GST_UNLOCK (clock);
676
677   return (GstClockID *) entry;
678 }
679
680 static void
681 gst_clock_update_stats (GstClock *clock)
682 {
683 }
684
685 static void
686 gst_clock_set_property (GObject *object, guint prop_id,
687                         const GValue *value, GParamSpec *pspec)
688 {
689   GstClock *clock;
690              
691   clock = GST_CLOCK (object);
692
693   switch (prop_id) {
694     case ARG_STATS:
695       clock->stats = g_value_get_boolean (value);
696       g_object_notify (object, "stats");
697       break;
698     case ARG_MAX_DIFF:
699       clock->max_diff = g_value_get_int64 (value);
700       g_object_notify (object, "max-diff");
701       break;
702     case ARG_EVENT_DIFF:
703       clock->max_event_diff = g_value_get_uint64 (value);
704       g_object_notify (object, "event-diff");
705       break;
706     default:
707       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
708       break;
709    }
710 }
711
712 static void
713 gst_clock_get_property (GObject *object, guint prop_id, 
714                         GValue *value, GParamSpec * pspec)
715 {
716   GstClock *clock;
717              
718   clock = GST_CLOCK (object);
719
720   switch (prop_id) {
721     case ARG_STATS:
722       g_value_set_boolean (value, clock->stats);
723       break;
724     case ARG_MAX_DIFF:
725       g_value_set_int64 (value, clock->max_diff);
726       break;
727     case ARG_EVENT_DIFF:
728       g_value_set_uint64 (value, clock->max_event_diff);
729       break;
730     default:
731       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
732       break;
733    }
734 }