source: trunk/third/gstreamer/gst/gstclock.c @ 21005

Revision 21005, 18.2 KB checked in by ghudson, 20 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r21004, which included commits to RCS files with non-trunk default branches.
Line 
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 <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"
34static GstAllocTrace *_gst_clock_entry_trace;
35#endif
36
37#define DEFAULT_EVENT_DIFF      (GST_SECOND)
38#define DEFAULT_MAX_DIFF        (2 * GST_SECOND)
39
40enum
41{
42  ARG_0,
43  ARG_STATS,
44  ARG_MAX_DIFF,
45  ARG_EVENT_DIFF
46};
47
48static GstMemChunk *_gst_clock_entries_chunk;
49
50void gst_clock_id_unlock (GstClockID id);
51
52
53static void gst_clock_class_init (GstClockClass * klass);
54static void gst_clock_init (GstClock * clock);
55static void gst_clock_dispose (GObject * object);
56
57static void gst_clock_set_property (GObject * object, guint prop_id,
58    const GValue * value, GParamSpec * pspec);
59static void gst_clock_get_property (GObject * object, guint prop_id,
60    GValue * value, GParamSpec * pspec);
61static void gst_clock_update_stats (GstClock * clock);
62
63
64static GstObjectClass *parent_class = NULL;
65
66/* static guint gst_clock_signals[LAST_SIGNAL] = { 0 }; */
67
68static GstClockID
69gst_clock_entry_new (GstClock * clock, GstClockTime time,
70    GstClockTime interval, GstClockEntryType type)
71{
72  GstClockEntry *entry;
73
74  entry = gst_mem_chunk_alloc (_gst_clock_entries_chunk);
75#ifndef GST_DISABLE_TRACE
76  gst_alloc_trace_new (_gst_clock_entry_trace, entry);
77#endif
78
79  entry->clock = clock;
80  entry->time = time;
81  entry->interval = interval;
82  entry->type = type;
83  entry->status = GST_CLOCK_ENTRY_OK;
84
85  return (GstClockID) entry;
86}
87
88/**
89 * gst_clock_new_single_shot_id
90 * @clock: The clockid to get a single shot notification from
91 * @time: the requested time
92 *
93 * Get an ID from the given clock to trigger a single shot
94 * notification at the requested time.
95 *
96 * Returns: An id that can be used to request the time notification.
97 */
98GstClockID
99gst_clock_new_single_shot_id (GstClock * clock, GstClockTime time)
100{
101  g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);
102
103  return gst_clock_entry_new (clock,
104      time, GST_CLOCK_TIME_NONE, 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 */
119GstClockID
120gst_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, interval, GST_CLOCK_ENTRY_PERIODIC);
129}
130
131/**
132 * gst_clock_id_get_time
133 * @id: The clockid to query
134 *
135 * Get the time of the clock ID
136 *
137 * Returns: the time of the given clock id
138 */
139GstClockTime
140gst_clock_id_get_time (GstClockID id)
141{
142  g_return_val_if_fail (id != NULL, GST_CLOCK_TIME_NONE);
143
144  return GST_CLOCK_ENTRY_TIME ((GstClockEntry *) id);
145}
146
147
148/**
149 * gst_clock_id_wait
150 * @id: The clockid to wait on
151 * @jitter: A pointer that will contain the jitter
152 *
153 * Perform a blocking wait on the given ID. The jitter arg can be
154 * NULL
155 *
156 * Returns: the result of the blocking wait.
157 */
158GstClockReturn
159gst_clock_id_wait (GstClockID id, GstClockTimeDiff * jitter)
160{
161  GstClockEntry *entry;
162  GstClock *clock;
163  GstClockReturn res = GST_CLOCK_UNSUPPORTED;
164  GstClockTime requested;
165  GstClockClass *cclass;
166
167  g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR);
168
169  entry = (GstClockEntry *) id;
170  requested = GST_CLOCK_ENTRY_TIME (entry);
171
172  if (!GST_CLOCK_TIME_IS_VALID (requested)) {
173    GST_CAT_DEBUG (GST_CAT_CLOCK, "invalid time requested, returning _TIMEOUT");
174    return GST_CLOCK_TIMEOUT;
175  }
176
177  clock = GST_CLOCK_ENTRY_CLOCK (entry);
178  cclass = GST_CLOCK_GET_CLASS (clock);
179
180  if (cclass->wait) {
181    GstClockTime now;
182
183    GST_LOCK (clock);
184    clock->entries = g_list_prepend (clock->entries, entry);
185    GST_UNLOCK (clock);
186
187    GST_CAT_DEBUG (GST_CAT_CLOCK, "waiting on clock");
188    do {
189      res = cclass->wait (clock, entry);
190    }
191    while (res == GST_CLOCK_ENTRY_RESTART);
192    GST_CAT_DEBUG (GST_CAT_CLOCK, "done waiting");
193
194    GST_LOCK (clock);
195    clock->entries = g_list_remove (clock->entries, entry);
196    GST_UNLOCK (clock);
197
198    if (jitter) {
199      now = gst_clock_get_time (clock);
200      *jitter = now - requested;
201    }
202
203    if (clock->stats) {
204      gst_clock_update_stats (clock);
205    }
206  }
207
208  return res;
209}
210
211/**
212 * gst_clock_id_wait_async:
213 * @id: a #GstClockID to wait on
214 * @func: The callback function
215 * @user_data: User data passed in the calback
216 *
217 * Register a callback on the given clockid with the given
218 * function and user_data.
219 *
220 * Returns: the result of the non blocking wait.
221 */
222GstClockReturn
223gst_clock_id_wait_async (GstClockID id,
224    GstClockCallback func, gpointer user_data)
225{
226  GstClockEntry *entry;
227  GstClock *clock;
228  GstClockReturn res = GST_CLOCK_UNSUPPORTED;
229  GstClockClass *cclass;
230
231  g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR);
232  g_return_val_if_fail (func != NULL, GST_CLOCK_ERROR);
233
234  entry = (GstClockEntry *) id;
235  clock = entry->clock;
236
237  if (!GST_CLOCK_TIME_IS_VALID (GST_CLOCK_ENTRY_TIME (entry))) {
238    (func) (clock, GST_CLOCK_TIME_NONE, id, user_data);
239    return GST_CLOCK_TIMEOUT;
240  }
241
242  cclass = GST_CLOCK_GET_CLASS (clock);
243
244  if (cclass->wait_async) {
245    entry->func = func;
246    entry->user_data = user_data;
247
248    res = cclass->wait_async (clock, entry);
249  }
250
251  return res;
252}
253
254static void
255gst_clock_reschedule_func (GstClockEntry * entry)
256{
257  entry->status = GST_CLOCK_ENTRY_OK;
258
259  gst_clock_id_unlock ((GstClockID) entry);
260}
261
262/**
263 * gst_clock_id_unschedule:
264 * @id: The id to unschedule
265 *
266 * Cancel an outstanding async notification request with the given ID.
267 */
268void
269gst_clock_id_unschedule (GstClockID id)
270{
271  GstClockEntry *entry;
272  GstClock *clock;
273  GstClockClass *cclass;
274
275  g_return_if_fail (id != NULL);
276
277  entry = (GstClockEntry *) id;
278  clock = entry->clock;
279
280  cclass = GST_CLOCK_GET_CLASS (clock);
281
282  if (cclass->unschedule)
283    cclass->unschedule (clock, entry);
284}
285
286/**
287 * gst_clock_id_free:
288 * @id: The clockid to free
289 *
290 * Free the resources held by the given id
291 */
292void
293gst_clock_id_free (GstClockID id)
294{
295  g_return_if_fail (id != NULL);
296
297#ifndef GST_DISABLE_TRACE
298  gst_alloc_trace_free (_gst_clock_entry_trace, id);
299#endif
300  gst_mem_chunk_free (_gst_clock_entries_chunk, id);
301}
302
303/**
304 * gst_clock_id_unlock:
305 * @id: The clockid to unlock
306 *
307 * Unlock the givan ClockID.
308 */
309void
310gst_clock_id_unlock (GstClockID id)
311{
312  GstClockEntry *entry;
313  GstClock *clock;
314  GstClockClass *cclass;
315
316  g_return_if_fail (id != NULL);
317
318  entry = (GstClockEntry *) id;
319  clock = entry->clock;
320
321  cclass = GST_CLOCK_GET_CLASS (clock);
322
323  if (cclass->unlock)
324    cclass->unlock (clock, entry);
325}
326
327
328/**
329 * GstClock abstract base class implementation
330 */
331GType
332gst_clock_get_type (void)
333{
334  static GType clock_type = 0;
335
336  if (!clock_type) {
337    static const GTypeInfo clock_info = {
338      sizeof (GstClockClass),
339      NULL,
340      NULL,
341      (GClassInitFunc) gst_clock_class_init,
342      NULL,
343      NULL,
344      sizeof (GstClock),
345      0,
346      (GInstanceInitFunc) gst_clock_init,
347      NULL
348    };
349
350    clock_type = g_type_register_static (GST_TYPE_OBJECT, "GstClock",
351        &clock_info, G_TYPE_FLAG_ABSTRACT);
352  }
353  return clock_type;
354}
355
356static void
357gst_clock_class_init (GstClockClass * klass)
358{
359  GObjectClass *gobject_class;
360  GstObjectClass *gstobject_class;
361
362  gobject_class = (GObjectClass *) klass;
363  gstobject_class = (GstObjectClass *) klass;
364
365  parent_class = g_type_class_ref (GST_TYPE_OBJECT);
366
367  if (!g_thread_supported ())
368    g_thread_init (NULL);
369
370  _gst_clock_entries_chunk = gst_mem_chunk_new ("GstClockEntries",
371      sizeof (GstClockEntry), sizeof (GstClockEntry) * 32, G_ALLOC_AND_FREE);
372
373#ifndef GST_DISABLE_TRACE
374  _gst_clock_entry_trace =
375      gst_alloc_trace_register (GST_CLOCK_ENTRY_TRACE_NAME);
376#endif
377
378  gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_clock_dispose);
379  gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_clock_set_property);
380  gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_clock_get_property);
381
382  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_STATS,
383      g_param_spec_boolean ("stats", "Stats", "Enable clock stats",
384          FALSE, G_PARAM_READWRITE));
385  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MAX_DIFF,
386      g_param_spec_int64 ("max-diff", "Max diff",
387          "The maximum amount of time to wait in nanoseconds", 0, G_MAXINT64,
388          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,
393          G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
394}
395
396static void
397gst_clock_init (GstClock * clock)
398{
399  clock->max_diff = DEFAULT_MAX_DIFF;
400
401  clock->start_time = 0;
402  clock->last_time = 0;
403  clock->entries = NULL;
404  clock->flags = 0;
405  clock->stats = FALSE;
406
407  clock->active_mutex = g_mutex_new ();
408  clock->active_cond = g_cond_new ();
409}
410
411static void
412gst_clock_dispose (GObject * object)
413{
414  GstClock *clock = GST_CLOCK (object);
415
416  g_mutex_free (clock->active_mutex);
417  g_cond_free (clock->active_cond);
418
419  G_OBJECT_CLASS (parent_class)->dispose (object);
420}
421
422/**
423 * gst_clock_set_speed
424 * @clock: a #GstClock to modify
425 * @speed: the speed to set on the clock
426 *
427 * Sets the speed on the given clock. 1.0 is the default
428 * speed.
429 *
430 * Returns: the new speed of the clock.
431 */
432gdouble
433gst_clock_set_speed (GstClock * clock, gdouble speed)
434{
435  g_return_val_if_fail (GST_IS_CLOCK (clock), 0.0);
436
437  GST_WARNING_OBJECT (clock, "called deprecated function");
438  return 1.0;
439}
440
441/**
442 * gst_clock_get_speed
443 * @clock: a #GstClock to query
444 *
445 * Gets the speed of the given clock.
446 *
447 * Returns: the speed of the clock.
448 */
449gdouble
450gst_clock_get_speed (GstClock * clock)
451{
452  g_return_val_if_fail (GST_IS_CLOCK (clock), 0.0);
453
454  GST_WARNING_OBJECT (clock, "called deprecated function");
455  return 1.0;
456}
457
458/**
459 * gst_clock_set_resolution
460 * @clock: The clock set the resolution on
461 * @resolution: The resolution to set
462 *
463 * Set the accuracy of the clock.
464 *
465 * Returns: the new resolution of the clock.
466 */
467guint64
468gst_clock_set_resolution (GstClock * clock, guint64 resolution)
469{
470  GstClockClass *cclass;
471
472  g_return_val_if_fail (GST_IS_CLOCK (clock), G_GINT64_CONSTANT (0));
473  g_return_val_if_fail (resolution != 0, G_GINT64_CONSTANT (0));
474
475  cclass = GST_CLOCK_GET_CLASS (clock);
476
477  if (cclass->change_resolution)
478    clock->resolution =
479        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 */
492guint64
493gst_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 */
515void
516gst_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 */
533gboolean
534gst_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 */
549void
550gst_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 */
584gboolean
585gst_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 */
601GstClockTime
602gst_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  } else {
618    clock->last_time = ret;
619  }
620
621  return ret;
622}
623
624/**
625 * gst_clock_get_event_time:
626 * @clock: clock to query
627 *
628 * Gets the "event time" of a given clock. An event on the clock happens
629 * whenever this function is called. This ensures that multiple events that
630 * happen shortly after each other are treated as if they happened at the same
631 * time. GStreamer uses to keep state changes of multiple elements in sync.
632 *
633 * Returns: the time of the event
634 */
635GstClockTime
636gst_clock_get_event_time (GstClock * clock)
637{
638  return gst_clock_get_event_time_delay (clock, 0);
639}
640
641/**
642 * gst_clock_get_event_time_delay:
643 * @clock: clock to query
644 * @delay: time before the event actually occurs
645 *
646 * Gets the "event time" of a given clock. An event on the clock happens
647 * whenever this function is called. This ensures that multiple events that
648 * happen shortly after each other are treated as if they happened at the same
649 * time. GStreamer uses to keep state changes of multiple elements in sync.
650 *
651 * When calling this function, the specified delay will be added to the current
652 * time to produce the event time. This can be used for events that are
653 * scheduled to happen at some point in the future.
654 *
655 * Returns: the time of the event
656 */
657GstClockTime
658gst_clock_get_event_time_delay (GstClock * clock, GstClockTime delay)
659{
660  GstClockTime time;
661
662  g_return_val_if_fail (GST_IS_CLOCK (clock), GST_CLOCK_TIME_NONE);
663
664  time = gst_clock_get_time (clock);
665
666  if (ABS (GST_CLOCK_DIFF (clock->last_event, time + delay)) <
667      clock->max_event_diff) {
668    GST_LOG_OBJECT (clock, "reporting last event time %" G_GUINT64_FORMAT,
669        clock->last_event);
670  } else {
671    clock->last_event = time + delay;
672    GST_LOG_OBJECT (clock, "reporting new event time %" G_GUINT64_FORMAT,
673        clock->last_event);
674  }
675
676  return clock->last_event;
677}
678
679/**
680 * gst_clock_get_next_id
681 * @clock: The clock to query
682 *
683 * Get the clockid of the next event.
684 *
685 * Returns: a clockid or NULL is no event is pending.
686 */
687GstClockID
688gst_clock_get_next_id (GstClock * clock)
689{
690  GstClockEntry *entry = NULL;
691
692  g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);
693
694  GST_LOCK (clock);
695  if (clock->entries)
696    entry = GST_CLOCK_ENTRY (clock->entries->data);
697  GST_UNLOCK (clock);
698
699  return (GstClockID *) entry;
700}
701
702static void
703gst_clock_update_stats (GstClock * clock)
704{
705}
706
707static void
708gst_clock_set_property (GObject * object, guint prop_id,
709    const GValue * value, GParamSpec * pspec)
710{
711  GstClock *clock;
712
713  clock = GST_CLOCK (object);
714
715  switch (prop_id) {
716    case ARG_STATS:
717      clock->stats = g_value_get_boolean (value);
718      g_object_notify (object, "stats");
719      break;
720    case ARG_MAX_DIFF:
721      clock->max_diff = g_value_get_int64 (value);
722      g_object_notify (object, "max-diff");
723      break;
724    case ARG_EVENT_DIFF:
725      clock->max_event_diff = g_value_get_uint64 (value);
726      g_object_notify (object, "event-diff");
727      break;
728    default:
729      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
730      break;
731  }
732}
733
734static void
735gst_clock_get_property (GObject * object, guint prop_id,
736    GValue * value, GParamSpec * pspec)
737{
738  GstClock *clock;
739
740  clock = GST_CLOCK (object);
741
742  switch (prop_id) {
743    case ARG_STATS:
744      g_value_set_boolean (value, clock->stats);
745      break;
746    case ARG_MAX_DIFF:
747      g_value_set_int64 (value, clock->max_diff);
748      break;
749    case ARG_EVENT_DIFF:
750      g_value_set_uint64 (value, clock->max_event_diff);
751      break;
752    default:
753      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
754      break;
755  }
756}
Note: See TracBrowser for help on using the repository browser.