gstclock.c 43.4 KB
Newer Older
1 2 3
/* GStreamer
 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
 *                    2000 Wim Taymans <wtay@chello.be>
4
 *                    2004 Wim Taymans <wim@fluendo.com>
5 6
 *
 * gstclock.c: Clock subsystem for maintaining time sync
Wim Taymans's avatar
Wim Taymans committed
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
23

24 25 26
/**
 * SECTION:gstclock
 * @short_description: Abstract class for global clocks
Wim Taymans's avatar
Wim Taymans committed
27
 * @see_also: #GstSystemClock, #GstPipeline
28
 *
Wim Taymans's avatar
Wim Taymans committed
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 or, more conveniently, by subclassing #GstSystemClock.
32
 *
Wim Taymans's avatar
Wim Taymans committed
33
 * The #GstClock returns a monotonically increasing time with the method
Wim Taymans's avatar
Wim Taymans committed
34 35
 * gst_clock_get_time(). Its accuracy and base time depend on the specific
 * clock implementation but time is always expressed in nanoseconds. Since the
Wim Taymans's avatar
Wim Taymans committed
36
 * baseline of the clock is undefined, the clock time returned is not
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
37
 * meaningful in itself, what matters are the deltas between two clock times.
Wim Taymans's avatar
Wim Taymans committed
38
 * The time returned by a clock is called the absolute time.
Wim Taymans's avatar
Wim Taymans committed
39
 *
40
 * The pipeline uses the clock to calculate the running time. Usually all
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
41
 * renderers synchronize to the global clock using the buffer timestamps, the
Wim Taymans's avatar
Wim Taymans committed
42
 * newsegment events and the element's base time, see #GstPipeline.
Wim Taymans's avatar
Wim Taymans committed
43
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
44 45
 * A clock implementation can support periodic and single shot clock
 * notifications both synchronous and asynchronous.
Wim Taymans's avatar
Wim Taymans committed
46 47
 *
 * One first needs to create a #GstClockID for the periodic or single shot
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
48 49
 * notification using gst_clock_new_single_shot_id() or
 * gst_clock_new_periodic_id().
Wim Taymans's avatar
Wim Taymans committed
50 51 52
 *
 * To perform a blocking wait for the specific time of the #GstClockID use the
 * gst_clock_id_wait(). To receive a callback when the specific time is reached
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
53 54
 * in the clock use gst_clock_id_wait_async(). Both these calls can be
 * interrupted with the gst_clock_id_unschedule() call. If the blocking wait is
Stefan Kost's avatar
Stefan Kost committed
55
 * unscheduled a return value of #GST_CLOCK_UNSCHEDULED is returned.
Wim Taymans's avatar
Wim Taymans committed
56
 *
57
 * Periodic callbacks scheduled async will be repeatedly called automatically
Wim Taymans's avatar
Wim Taymans committed
58
 * until it is unscheduled. To schedule a sync periodic callback,
59
 * gst_clock_id_wait() should be called repeatedly.
60
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
61 62
 * The async callbacks can happen from any thread, either provided by the core
 * or from a streaming thread. The application should be prepared for this.
Wim Taymans's avatar
Wim Taymans committed
63 64
 *
 * A #GstClockID that has been unscheduled cannot be used again for any wait
Wim Taymans's avatar
Wim Taymans committed
65
 * operation, a new #GstClockID should be created and the old unscheduled one
66
 * should be destroyed with gst_clock_id_unref().
Wim Taymans's avatar
Wim Taymans committed
67
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
68 69
 * It is possible to perform a blocking wait on the same #GstClockID from
 * multiple threads. However, registering the same #GstClockID for multiple
Wim Taymans's avatar
Wim Taymans committed
70 71
 * async notifications is not possible, the callback will only be called for
 * the thread registering the entry last.
Wim Taymans's avatar
Wim Taymans committed
72
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
73 74 75 76
 * None of the wait operations unref the #GstClockID, the owner is responsible
 * for unreffing the ids itself. This holds for both periodic and single shot
 * notifications. The reason being that the owner of the #GstClockID has to
 * keep a handle to the #GstClockID to unblock the wait on FLUSHING events or
Wim Taymans's avatar
Wim Taymans committed
77 78
 * state changes and if the entry would be unreffed automatically, the handle 
 * might become invalid without any notification.
Wim Taymans's avatar
Wim Taymans committed
79
 *
80
 * These clock operations do not operate on the running time, so the callbacks
Wim Taymans's avatar
Wim Taymans committed
81 82 83 84
 * will also occur when not in PLAYING state as if the clock just keeps on
 * running. Some clocks however do not progress when the element that provided
 * the clock is not PLAYING.
 *
85
 * When a clock has the #GST_CLOCK_FLAG_CAN_SET_MASTER flag set, it can be
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
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
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
88
 * sampling the master clock and the slave clock and recalibrating the slave
Kjartan Maraas's avatar
Kjartan Maraas committed
89
 * clock with gst_clock_set_calibration(). This feature is mostly useful for
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
90 91 92
 * plugins that have an internal clock but must operate with another clock
 * selected by the #GstPipeline.  They can track the offset and rate difference
 * of their internal clock relative to the master clock by using the
Wim Taymans's avatar
Wim Taymans committed
93 94
 * gst_clock_get_calibration() function. 
 *
Stefan Kost's avatar
Stefan Kost committed
95 96 97 98 99 100
 * The master/slave synchronisation can be tuned with the #GstClock:timeout,
 * #GstClock:window-size and #GstClock:window-threshold properties.
 * The #GstClock:timeout property defines the interval to sample the master
 * clock and run the calibration functions. #GstClock:window-size defines the
 * number of samples to use when calibrating and #GstClock:window-threshold
 * defines the minimum number of samples before the calibration is performed.
Wim Taymans's avatar
Wim Taymans committed
101
 *
102
 * Last reviewed on 2009-05-21 (0.10.24)
103
 */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
104

105 106

#include "gst_private.h"
107
#include <time.h>
108

109
#include "gstclock.h"
110
#include "gstinfo.h"
111
#include "gstutils.h"
Wim Taymans's avatar
Wim Taymans committed
112

Wim Taymans's avatar
Wim Taymans committed
113
#ifndef GST_DISABLE_TRACE
Wim Taymans's avatar
Wim Taymans committed
114
/* #define GST_WITH_ALLOC_TRACE */
Wim Taymans's avatar
Wim Taymans committed
115 116 117
#include "gsttrace.h"
static GstAllocTrace *_gst_clock_entry_trace;
#endif
Wim Taymans's avatar
Wim Taymans committed
118

119 120
/* #define DEBUGGING_ENABLED */

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
121 122 123 124
#define DEFAULT_STATS                   FALSE
#define DEFAULT_WINDOW_SIZE             32
#define DEFAULT_WINDOW_THRESHOLD        4
#define DEFAULT_TIMEOUT                 GST_SECOND / 10
125

126 127
enum
{
128 129 130 131 132
  PROP_0,
  PROP_STATS,
  PROP_WINDOW_SIZE,
  PROP_WINDOW_THRESHOLD,
  PROP_TIMEOUT
133 134
};

135 136 137 138 139 140 141 142
struct _GstClockPrivate
{
  gint pre_count;
  gint post_count;
};

/* seqlocks */
#define read_seqbegin(clock)                                   \
143
  g_atomic_int_get (&clock->ABI.priv->post_count);
144 145 146 147 148

static inline gboolean
read_seqretry (GstClock * clock, gint seq)
{
  /* no retry if the seqnum did not change */
149
  if (G_LIKELY (seq == g_atomic_int_get (&clock->ABI.priv->pre_count)))
150 151 152 153 154 155 156 157 158 159 160
    return FALSE;

  /* wait for the writer to finish and retry */
  GST_OBJECT_LOCK (clock);
  GST_OBJECT_UNLOCK (clock);
  return TRUE;
}

#define write_seqlock(clock)                      \
G_STMT_START {                                    \
  GST_OBJECT_LOCK (clock);                        \
161
  g_atomic_int_inc (&clock->ABI.priv->pre_count);     \
162 163 164 165
} G_STMT_END;

#define write_sequnlock(clock)                    \
G_STMT_START {                                    \
166
  g_atomic_int_inc (&clock->ABI.priv->post_count);    \
167 168 169
  GST_OBJECT_UNLOCK (clock);                      \
} G_STMT_END;

170
static void gst_clock_dispose (GObject * object);
Wim Taymans's avatar
Wim Taymans committed
171
static void gst_clock_finalize (GObject * object);
Wim Taymans's avatar
Wim Taymans committed
172

173 174 175 176 177
static void gst_clock_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec);
static void gst_clock_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec);
static void gst_clock_update_stats (GstClock * clock);
178

179

180
static GstObjectClass *parent_class = NULL;
181

182 183
/* static guint gst_clock_signals[LAST_SIGNAL] = { 0 }; */

Wim Taymans's avatar
Wim Taymans committed
184
static GstClockID
185 186
gst_clock_entry_new (GstClock * clock, GstClockTime time,
    GstClockTime interval, GstClockEntryType type)
187 188
{
  GstClockEntry *entry;
189

190
  entry = g_slice_new (GstClockEntry);
Wim Taymans's avatar
Wim Taymans committed
191
#ifndef GST_DISABLE_TRACE
Wim Taymans's avatar
Wim Taymans committed
192
  gst_alloc_trace_new (_gst_clock_entry_trace, entry);
Wim Taymans's avatar
Wim Taymans committed
193
#endif
194 195
  GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
      "created entry %p, time %" GST_TIME_FORMAT, entry, GST_TIME_ARGS (time));
196

197
  entry->refcount = 1;
198
  entry->clock = clock;
199
  entry->type = type;
200
  entry->time = time;
201
  entry->interval = interval;
202
  entry->status = GST_CLOCK_OK;
203 204
  entry->func = NULL;
  entry->user_data = NULL;
205
  entry->destroy_data = NULL;
206 207
  entry->unscheduled = FALSE;
  entry->woken_up = FALSE;
208

209 210
  return (GstClockID) entry;
}
211

212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
/* WARNING : Does not modify the refoucnt
 * WARNING : Do not use if a pending clock operation is happening on that entry */
static gboolean
gst_clock_entry_reinit (GstClock * clock, GstClockEntry * entry,
    GstClockTime time, GstClockTime interval, GstClockEntryType type)
{
  g_return_val_if_fail (entry->status != GST_CLOCK_BUSY, FALSE);
  g_return_val_if_fail (entry->clock == clock, FALSE);

  entry->type = type;
  entry->time = time;
  entry->interval = interval;
  entry->status = GST_CLOCK_OK;

  return TRUE;
}

/**
 * gst_clock_single_shot_id_reinit:
 * @clock: a #GstClock
 * @id: a #GstClockID
 * @time: The requested time.
 *
 * Reinitializes the provided single shot @id to the provided time. Does not
 * modify the reference count.
 *
 * Returns: %TRUE if the GstClockID could be reinitialized to the provided
 * @time, else %FALSE.
 */
gboolean
gst_clock_single_shot_id_reinit (GstClock * clock, GstClockID id,
    GstClockTime time)
{
  return gst_clock_entry_reinit (clock, (GstClockEntry *) id, time,
      GST_CLOCK_TIME_NONE, GST_CLOCK_ENTRY_SINGLE);
}

249 250
/**
 * gst_clock_id_ref:
Wim Taymans's avatar
Wim Taymans committed
251
 * @id: The #GstClockID to ref
252
 *
Wim Taymans's avatar
Wim Taymans committed
253
 * Increase the refcount of given @id.
254 255 256 257 258 259 260 261 262 263
 *
 * Returns: The same #GstClockID with increased refcount.
 *
 * MT safe.
 */
GstClockID
gst_clock_id_ref (GstClockID id)
{
  g_return_val_if_fail (id != NULL, NULL);

264
  g_atomic_int_inc (&((GstClockEntry *) id)->refcount);
265 266 267 268 269 270 271

  return id;
}

static void
_gst_clock_id_free (GstClockID id)
{
272
  GstClockEntry *entry;
273 274 275
  g_return_if_fail (id != NULL);

  GST_CAT_DEBUG (GST_CAT_CLOCK, "freed entry %p", id);
276 277 278
  entry = (GstClockEntry *) id;
  if (entry->destroy_data)
    entry->destroy_data (entry->user_data);
279 280 281 282

#ifndef GST_DISABLE_TRACE
  gst_alloc_trace_free (_gst_clock_entry_trace, id);
#endif
283
  g_slice_free (GstClockEntry, id);
284 285 286 287
}

/**
 * gst_clock_id_unref:
Wim Taymans's avatar
Wim Taymans committed
288
 * @id: The #GstClockID to unref
289
 *
Wim Taymans's avatar
Wim Taymans committed
290
 * Unref given @id. When the refcount reaches 0 the
291 292 293 294 295 296 297 298 299 300 301
 * #GstClockID will be freed.
 *
 * MT safe.
 */
void
gst_clock_id_unref (GstClockID id)
{
  gint zero;

  g_return_if_fail (id != NULL);

302
  zero = g_atomic_int_dec_and_test (&((GstClockEntry *) id)->refcount);
303 304 305 306 307 308
  /* if we ended up with the refcount at zero, free the id */
  if (zero) {
    _gst_clock_id_free (id);
  }
}

309 310
/**
 * gst_clock_new_single_shot_id
Wim Taymans's avatar
Wim Taymans committed
311
 * @clock: The #GstClockID to get a single shot notification from
312 313
 * @time: the requested time
 *
Wim Taymans's avatar
Wim Taymans committed
314
 * Get a #GstClockID from @clock to trigger a single shot
315 316
 * notification at the requested time. The single shot id should be
 * unreffed after usage.
317
 *
Wim Taymans's avatar
Wim Taymans committed
318
 * Returns: A #GstClockID that can be used to request the time notification.
319 320
 *
 * MT safe.
321 322
 */
GstClockID
323
gst_clock_new_single_shot_id (GstClock * clock, GstClockTime time)
324
{
325 326
  g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);

327 328
  return gst_clock_entry_new (clock,
      time, GST_CLOCK_TIME_NONE, GST_CLOCK_ENTRY_SINGLE);
329 330 331
}

/**
Wim Taymans's avatar
Wim Taymans committed
332
 * gst_clock_new_periodic_id
Wim Taymans's avatar
Wim Taymans committed
333
 * @clock: The #GstClockID to get a periodic notification id from
334 335 336
 * @start_time: the requested start time
 * @interval: the requested interval
 *
Wim Taymans's avatar
Wim Taymans committed
337
 * Get an ID from @clock to trigger a periodic notification.
338 339
 * The periodic notifications will start at time @start_time and
 * will then be fired with the given @interval. @id should be unreffed
340
 * after usage.
341
 *
Wim Taymans's avatar
Wim Taymans committed
342
 * Returns: A #GstClockID that can be used to request the time notification.
343 344
 *
 * MT safe.
345 346
 */
GstClockID
347 348
gst_clock_new_periodic_id (GstClock * clock, GstClockTime start_time,
    GstClockTime interval)
349
{
350
  g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);
351
  g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (start_time), NULL);
352
  g_return_val_if_fail (interval != 0, NULL);
353
  g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (interval), NULL);
354

355 356
  return gst_clock_entry_new (clock,
      start_time, interval, GST_CLOCK_ENTRY_PERIODIC);
357 358
}

359 360
/**
 * gst_clock_id_compare_func
Wim Taymans's avatar
Wim Taymans committed
361 362
 * @id1: A #GstClockID
 * @id2: A #GstClockID to compare with
363
 *
Wim Taymans's avatar
Wim Taymans committed
364
 * Compares the two #GstClockID instances. This function can be used
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
 * as a GCompareFunc when sorting ids.
 *
 * Returns: negative value if a < b; zero if a = b; positive value if a > b
 *
 * MT safe.
 */
gint
gst_clock_id_compare_func (gconstpointer id1, gconstpointer id2)
{
  GstClockEntry *entry1, *entry2;

  entry1 = (GstClockEntry *) id1;
  entry2 = (GstClockEntry *) id2;

  if (GST_CLOCK_ENTRY_TIME (entry1) > GST_CLOCK_ENTRY_TIME (entry2)) {
    return 1;
  }
  if (GST_CLOCK_ENTRY_TIME (entry1) < GST_CLOCK_ENTRY_TIME (entry2)) {
    return -1;
  }
Wim Taymans's avatar
Wim Taymans committed
385
  return 0;
386 387
}

388 389
/**
 * gst_clock_id_get_time
Wim Taymans's avatar
Wim Taymans committed
390
 * @id: The #GstClockID to query
391 392 393
 *
 * Get the time of the clock ID
 *
394 395 396
 * Returns: the time of the given clock id.
 *
 * MT safe.
397 398 399 400 401 402
 */
GstClockTime
gst_clock_id_get_time (GstClockID id)
{
  g_return_val_if_fail (id != NULL, GST_CLOCK_TIME_NONE);

403
  return GST_CLOCK_ENTRY_TIME ((GstClockEntry *) id);
404 405 406 407
}

/**
 * gst_clock_id_wait
Wim Taymans's avatar
Wim Taymans committed
408
 * @id: The #GstClockID to wait on
409
 * @jitter: A pointer that will contain the jitter, can be %NULL.
Wim Taymans's avatar
Wim Taymans committed
410 411 412 413 414 415
 *
 * Perform a blocking wait on @id. 
 * @id should have been created with gst_clock_new_single_shot_id()
 * or gst_clock_new_periodic_id() and should not have been unscheduled
 * with a call to gst_clock_id_unschedule(). 
 *
416
 * If the @jitter argument is not %NULL and this function returns #GST_CLOCK_OK
Wim Taymans's avatar
Wim Taymans committed
417 418
 * or #GST_CLOCK_EARLY, it will contain the difference
 * against the clock and the time of @id when this method was
419 420
 * called. 
 * Positive values indicate how late @id was relative to the clock
421
 * (in which case this function will return #GST_CLOCK_EARLY). 
422
 * Negative values indicate how much time was spent waiting on the clock 
423
 * before this function returned.
Wim Taymans's avatar
Wim Taymans committed
424 425 426 427 428
 *
 * Returns: the result of the blocking wait. #GST_CLOCK_EARLY will be returned
 * if the current clock time is past the time of @id, #GST_CLOCK_OK if 
 * @id was scheduled in time. #GST_CLOCK_UNSCHEDULED if @id was 
 * unscheduled with gst_clock_id_unschedule().
429 430
 *
 * MT safe.
431 432
 */
GstClockReturn
433
gst_clock_id_wait (GstClockID id, GstClockTimeDiff * jitter)
434 435
{
  GstClockEntry *entry;
436
  GstClock *clock;
437
  GstClockReturn res;
438
  GstClockTime requested;
Wim Taymans's avatar
Wim Taymans committed
439
  GstClockClass *cclass;
440

441
  g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR);
442

443 444
  entry = (GstClockEntry *) id;
  requested = GST_CLOCK_ENTRY_TIME (entry);
445

446 447
  clock = GST_CLOCK_ENTRY_CLOCK (entry);

Wim Taymans's avatar
Wim Taymans committed
448
  /* can't sync on invalid times */
449 450 451
  if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested)))
    goto invalid_time;

Wim Taymans's avatar
Wim Taymans committed
452
  cclass = GST_CLOCK_GET_CLASS (clock);
453

Wim Taymans's avatar
Wim Taymans committed
454 455
  GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "waiting on clock entry %p", id);

456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475
  /* if we have a wait_jitter function, use that */
  if (G_LIKELY (cclass->wait_jitter)) {
    res = cclass->wait_jitter (clock, entry, jitter);
  } else {
    /* check if we have a simple _wait function otherwise. The function without
     * the jitter arg is less optimal as we need to do an additional _get_time()
     * which is not atomic with the _wait() and a typical _wait() function does
     * yet another _get_time() anyway. */
    if (G_UNLIKELY (cclass->wait == NULL))
      goto not_supported;

    if (jitter) {
      GstClockTime now = gst_clock_get_time (clock);

      /* jitter is the diff against the clock when this entry is scheduled. Negative
       * values mean that the entry was in time, a positive value means that the
       * entry was too late. */
      *jitter = GST_CLOCK_DIFF (requested, now);
    }
    res = cclass->wait (clock, entry);
Wim Taymans's avatar
Wim Taymans committed
476 477 478 479 480
  }

  GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
      "done waiting entry %p, res: %d", id, res);

481 482
  if (entry->type == GST_CLOCK_ENTRY_PERIODIC)
    entry->time = requested + entry->interval;
Wim Taymans's avatar
Wim Taymans committed
483

484
  if (G_UNLIKELY (clock->stats))
Wim Taymans's avatar
Wim Taymans committed
485 486
    gst_clock_update_stats (clock);

487
  return res;
488 489 490 491

  /* ERRORS */
invalid_time:
  {
492 493
    GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
        "invalid time requested, returning _BADTIME");
494 495
    return GST_CLOCK_BADTIME;
  }
Wim Taymans's avatar
Wim Taymans committed
496 497 498 499 500
not_supported:
  {
    GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "clock wait is not supported");
    return GST_CLOCK_UNSUPPORTED;
  }
501 502 503
}

/**
504
 * gst_clock_id_wait_async_full:
Wim Taymans's avatar
Wim Taymans committed
505
 * @id: a #GstClockID to wait on
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
506
 * @func: The callback function
507
 * @user_data: User data passed in the callback
508
 * @destroy_data: #GDestroyNotify for user_data
509
 *
Wim Taymans's avatar
Wim Taymans committed
510 511
 * Register a callback on the given #GstClockID @id with the given
 * function and user_data. When passing a #GstClockID with an invalid
512
 * time to this function, the callback will be called immediately
513
 * with  a time set to GST_CLOCK_TIME_NONE. The callback will
Wim Taymans's avatar
Wim Taymans committed
514
 * be called when the time of @id has been reached.
515
 *
516 517 518
 * The callback @func can be invoked from any thread, either provided by the
 * core or from a streaming thread. The application should be prepared for this.
 *
519
 * Returns: the result of the non blocking wait.
520 521
 *
 * MT safe.
522 523
 *
 * Since: 0.10.30
524 525
 */
GstClockReturn
526 527
gst_clock_id_wait_async_full (GstClockID id,
    GstClockCallback func, gpointer user_data, GDestroyNotify destroy_data)
528 529 530
{
  GstClockEntry *entry;
  GstClock *clock;
531
  GstClockReturn res;
Wim Taymans's avatar
Wim Taymans committed
532
  GstClockClass *cclass;
533
  GstClockTime requested;
534

535
  g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR);
536
  g_return_val_if_fail (func != NULL, GST_CLOCK_ERROR);
537 538

  entry = (GstClockEntry *) id;
539 540
  requested = GST_CLOCK_ENTRY_TIME (entry);
  clock = GST_CLOCK_ENTRY_CLOCK (entry);
541

Wim Taymans's avatar
Wim Taymans committed
542
  /* can't sync on invalid times */
543 544 545
  if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested)))
    goto invalid_time;

Wim Taymans's avatar
Wim Taymans committed
546 547
  cclass = GST_CLOCK_GET_CLASS (clock);

Wim Taymans's avatar
Wim Taymans committed
548 549 550 551 552
  if (G_UNLIKELY (cclass->wait_async == NULL))
    goto not_supported;

  entry->func = func;
  entry->user_data = user_data;
553
  entry->destroy_data = destroy_data;
Wim Taymans's avatar
Wim Taymans committed
554 555

  res = cclass->wait_async (clock, entry);
Wim Taymans's avatar
Wim Taymans committed
556

557 558
  return res;

559 560 561 562
  /* ERRORS */
invalid_time:
  {
    (func) (clock, GST_CLOCK_TIME_NONE, id, user_data);
563 564
    GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
        "invalid time requested, returning _BADTIME");
565 566
    return GST_CLOCK_BADTIME;
  }
Wim Taymans's avatar
Wim Taymans committed
567 568 569 570 571
not_supported:
  {
    GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "clock wait is not supported");
    return GST_CLOCK_UNSUPPORTED;
  }
572 573
}

574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599
/**
 * gst_clock_id_wait_async:
 * @id: a #GstClockID to wait on
 * @func: The callback function
 * @user_data: User data passed in the callback
 *
 * Register a callback on the given #GstClockID @id with the given
 * function and user_data. When passing a #GstClockID with an invalid
 * time to this function, the callback will be called immediately
 * with  a time set to GST_CLOCK_TIME_NONE. The callback will
 * be called when the time of @id has been reached.
 *
 * The callback @func can be invoked from any thread, either provided by the
 * core or from a streaming thread. The application should be prepared for this.
 *
 * Returns: the result of the non blocking wait.
 *
 * MT safe.
 */
GstClockReturn
gst_clock_id_wait_async (GstClockID id,
    GstClockCallback func, gpointer user_data)
{
  return gst_clock_id_wait_async_full (id, func, user_data, NULL);
}

600
/**
Wim Taymans's avatar
Wim Taymans committed
601 602
 * gst_clock_id_unschedule:
 * @id: The id to unschedule
603
 *
Wim Taymans's avatar
Wim Taymans committed
604
 * Cancel an outstanding request with @id. This can either
605
 * be an outstanding async notification or a pending sync notification.
Wim Taymans's avatar
Wim Taymans committed
606 607
 * After this call, @id cannot be used anymore to receive sync or
 * async notifications, you need to create a new #GstClockID.
608 609
 *
 * MT safe.
610 611 612 613 614 615
 */
void
gst_clock_id_unschedule (GstClockID id)
{
  GstClockEntry *entry;
  GstClock *clock;
Wim Taymans's avatar
Wim Taymans committed
616
  GstClockClass *cclass;
617

618 619 620 621
  g_return_if_fail (id != NULL);

  entry = (GstClockEntry *) id;
  clock = entry->clock;
622

Wim Taymans's avatar
Wim Taymans committed
623 624
  cclass = GST_CLOCK_GET_CLASS (clock);

Wim Taymans's avatar
Wim Taymans committed
625
  if (G_LIKELY (cclass->unschedule))
Wim Taymans's avatar
Wim Taymans committed
626
    cclass->unschedule (clock, entry);
627 628
}

629

Stefan Kost's avatar
Stefan Kost committed
630
/*
631 632
 * GstClock abstract base class implementation
 */
633
G_DEFINE_TYPE (GstClock, gst_clock, GST_TYPE_OBJECT);
634

635
static void
636
gst_clock_class_init (GstClockClass * klass)
637
{
638
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
639

640
  parent_class = g_type_class_peek_parent (klass);
641

Wim Taymans's avatar
Wim Taymans committed
642
#ifndef GST_DISABLE_TRACE
643 644
  _gst_clock_entry_trace =
      gst_alloc_trace_register (GST_CLOCK_ENTRY_TRACE_NAME);
Wim Taymans's avatar
Wim Taymans committed
645
#endif
646

647 648 649 650
  gobject_class->dispose = gst_clock_dispose;
  gobject_class->finalize = gst_clock_finalize;
  gobject_class->set_property = gst_clock_set_property;
  gobject_class->get_property = gst_clock_get_property;
651

652
  g_object_class_install_property (gobject_class, PROP_STATS,
653 654
      g_param_spec_boolean ("stats", "Stats",
          "Enable clock stats (unimplemented)", DEFAULT_STATS,
655
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
656
  g_object_class_install_property (gobject_class, PROP_WINDOW_SIZE,
657 658
      g_param_spec_int ("window-size", "Window size",
          "The size of the window used to calculate rate and offset", 2, 1024,
659
          DEFAULT_WINDOW_SIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
660 661
  g_object_class_install_property (gobject_class, PROP_WINDOW_THRESHOLD,
      g_param_spec_int ("window-threshold", "Window threshold",
662
          "The threshold to start calculating rate and offset", 2, 1024,
663 664
          DEFAULT_WINDOW_THRESHOLD,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
665
  g_object_class_install_property (gobject_class, PROP_TIMEOUT,
666 667
      g_param_spec_uint64 ("timeout", "Timeout",
          "The amount of time, in nanoseconds, to sample master and slave clocks",
668 669
          0, G_MAXUINT64, DEFAULT_TIMEOUT,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
670 671

  g_type_class_add_private (klass, sizeof (GstClockPrivate));
Wim Taymans's avatar
Wim Taymans committed
672 673
}

674
static void
675
gst_clock_init (GstClock * clock)
Wim Taymans's avatar
Wim Taymans committed
676
{
677 678
  clock->last_time = 0;
  clock->entries = NULL;
679
  clock->entries_changed = g_cond_new ();
680
  clock->stats = FALSE;
681

682
  clock->ABI.priv =
683 684
      G_TYPE_INSTANCE_GET_PRIVATE (clock, GST_TYPE_CLOCK, GstClockPrivate);

685 686
  clock->internal_calibration = 0;
  clock->external_calibration = 0;
687 688 689 690
  clock->rate_numerator = 1;
  clock->rate_denominator = 1;

  clock->slave_lock = g_mutex_new ();
691 692
  clock->window_size = DEFAULT_WINDOW_SIZE;
  clock->window_threshold = DEFAULT_WINDOW_THRESHOLD;
Wim Taymans's avatar
Wim Taymans committed
693
  clock->filling = TRUE;
694 695 696
  clock->time_index = 0;
  clock->timeout = DEFAULT_TIMEOUT;
  clock->times = g_new0 (GstClockTime, 4 * clock->window_size);
Wim Taymans's avatar
Wim Taymans committed
697 698
}

699 700 701 702
static void
gst_clock_dispose (GObject * object)
{
  GstClock *clock = GST_CLOCK (object);
703
  GstClock **master_p;
704 705

  GST_OBJECT_LOCK (clock);
706 707
  master_p = &clock->master;
  gst_object_replace ((GstObject **) master_p, NULL);
708 709 710 711 712
  GST_OBJECT_UNLOCK (clock);

  G_OBJECT_CLASS (parent_class)->dispose (object);
}

Wim Taymans's avatar
Wim Taymans committed
713
static void
Wim Taymans's avatar
Wim Taymans committed
714
gst_clock_finalize (GObject * object)
Wim Taymans's avatar
Wim Taymans committed
715 716 717
{
  GstClock *clock = GST_CLOCK (object);

718
  GST_CLOCK_SLAVE_LOCK (clock);
719 720 721 722 723 724 725
  if (clock->clockid) {
    gst_clock_id_unschedule (clock->clockid);
    gst_clock_id_unref (clock->clockid);
    clock->clockid = NULL;
  }
  g_free (clock->times);
  clock->times = NULL;
726
  GST_CLOCK_SLAVE_UNLOCK (clock);
727

728
  g_cond_free (clock->entries_changed);
729 730
  g_mutex_free (clock->slave_lock);

Wim Taymans's avatar
Wim Taymans committed
731
  G_OBJECT_CLASS (parent_class)->finalize (object);
Wim Taymans's avatar
Wim Taymans committed
732 733
}

Wim Taymans's avatar
Wim Taymans committed
734
/**
735
 * gst_clock_set_resolution
736
 * @clock: a #GstClock
737
 * @resolution: The resolution to set
Wim Taymans's avatar
Wim Taymans committed
738
 *
739 740 741 742 743
 * Set the accuracy of the clock. Some clocks have the possibility to operate
 * with different accuracy at the expense of more resource usage. There is
 * normally no need to change the default resolution of a clock. The resolution
 * of a clock can only be changed if the clock has the
 * #GST_CLOCK_FLAG_CAN_SET_RESOLUTION flag set.
744 745
 *
 * Returns: the new resolution of the clock.
Wim Taymans's avatar
Wim Taymans committed
746
 */
747 748
GstClockTime
gst_clock_set_resolution (GstClock * clock, GstClockTime resolution)
Wim Taymans's avatar
Wim Taymans committed
749
{
Wim Taymans's avatar
Wim Taymans committed
750 751
  GstClockClass *cclass;

752 753
  g_return_val_if_fail (GST_IS_CLOCK (clock), 0);
  g_return_val_if_fail (resolution != 0, 0);
754

Wim Taymans's avatar
Wim Taymans committed
755 756 757
  cclass = GST_CLOCK_GET_CLASS (clock);

  if (cclass->change_resolution)
758
    clock->resolution =
759
        cclass->change_resolution (clock, clock->resolution, resolution);
760

761 762
  return clock->resolution;
}
763

764 765
/**
 * gst_clock_get_resolution
766
 * @clock: a #GstClock
767
 *
768 769
 * Get the accuracy of the clock. The accuracy of the clock is the granularity
 * of the values returned by gst_clock_get_time().
770
 *
771
 * Returns: the resolution of the clock in units of #GstClockTime.
772 773
 *
 * MT safe.
774
 */
775
GstClockTime
776
gst_clock_get_resolution (GstClock * clock)
777
{
Wim Taymans's avatar
Wim Taymans committed
778 779
  GstClockClass *cclass;

780
  g_return_val_if_fail (GST_IS_CLOCK (clock), 0);
781

Wim Taymans's avatar
Wim Taymans committed
782 783 784 785
  cclass = GST_CLOCK_GET_CLASS (clock);

  if (cclass->get_resolution)
    return cclass->get_resolution (clock);
786

787
  return 1;
Wim Taymans's avatar
Wim Taymans committed
788 789
}

Wim Taymans's avatar
Wim Taymans committed
790
/**
791 792 793
 * gst_clock_adjust_unlocked
 * @clock: a #GstClock to use
 * @internal: a clock time
Wim Taymans's avatar
Wim Taymans committed
794
 *
795
 * Converts the given @internal clock time to the external time, adjusting for the
796 797
 * rate and reference time set with gst_clock_set_calibration() and making sure
 * that the returned time is increasing. This function should be called with the
798
 * clock's OBJECT_LOCK held and is mainly used by clock subclasses.
Wim Taymans's avatar
Wim Taymans committed
799
 *
800
 * This function is the reverse of gst_clock_unadjust_unlocked().
801
 *
802
 * Returns: the converted time of the clock.
803
 */
804 805
GstClockTime
gst_clock_adjust_unlocked (GstClock * clock, GstClockTime internal)
806
{
807 808 809 810 811 812 813 814 815
  GstClockTime ret, cinternal, cexternal, cnum, cdenom;

  /* get calibration values for readability */
  cinternal = clock->internal_calibration;
  cexternal = clock->external_calibration;
  cnum = clock->rate_numerator;
  cdenom = clock->rate_denominator;

  /* avoid divide by 0 */
816
  if (G_UNLIKELY (cdenom == 0))
817 818 819 820 821
    cnum = cdenom = 1;

  /* The formula is (internal - cinternal) * cnum / cdenom + cexternal
   *
   * Since we do math on unsigned 64-bit ints we have to special case for
822
   * internal < cinternal to get the sign right. this case is not very common,
823 824 825
   * though.
   */
  if (G_LIKELY (internal >= cinternal)) {
826 827
    ret = internal - cinternal;
    ret = gst_util_uint64_scale (ret, cnum, cdenom);
828 829
    ret += cexternal;
  } else {
830 831
    ret = cinternal - internal;
    ret = gst_util_uint64_scale (ret, cnum, cdenom);
832
    /* clamp to 0 */
833
    if (G_LIKELY (cexternal > ret))
834 835 836 837
      ret = cexternal - ret;
    else
      ret = 0;
  }
838 839 840 841 842

  /* make sure the time is increasing */
  clock->last_time = MAX (ret, clock->last_time);

  return clock->last_time;
843 844
}

845 846 847 848 849 850 851 852 853 854
/**
 * gst_clock_unadjust_unlocked
 * @clock: a #GstClock to use
 * @external: an external clock time
 *
 * Converts the given @external clock time to the internal time of @clock,
 * using the rate and reference time set with gst_clock_set_calibration().
 * This function should be called with the clock's OBJECT_LOCK held and
 * is mainly used by clock subclasses.
 *
855
 * This function is the reverse of gst_clock_adjust_unlocked().
856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872
 *
 * Returns: the internal time of the clock corresponding to @external.
 *
 * Since: 0.10.13
 */
GstClockTime
gst_clock_unadjust_unlocked (GstClock * clock, GstClockTime external)
{
  GstClockTime ret, cinternal, cexternal, cnum, cdenom;

  /* get calibration values for readability */
  cinternal = clock->internal_calibration;
  cexternal = clock->external_calibration;
  cnum = clock->rate_numerator;
  cdenom = clock->rate_denominator;

  /* avoid divide by 0 */
873
  if (G_UNLIKELY (cnum == 0))
874 875 876
    cnum = cdenom = 1;

  /* The formula is (external - cexternal) * cdenom / cnum + cinternal */
877 878 879
  if (G_LIKELY (external >= cexternal)) {
    ret = external - cexternal;
    ret = gst_util_uint64_scale (ret, cdenom, cnum);
880 881
    ret += cinternal;
  } else {
882 883 884
    ret = cexternal - external;
    ret = gst_util_uint64_scale (ret, cdenom, cnum);
    if (G_LIKELY (cinternal > ret))
885 886 887 888 889 890 891
      ret = cinternal - ret;
    else
      ret = 0;
  }
  return ret;
}

Wim Taymans's avatar
Wim Taymans committed
892
/**
893
 * gst_clock_get_internal_time
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
894
 * @clock: a #GstClock to query
Wim Taymans's avatar
Wim Taymans committed
895
 *
896 897
 * Gets the current internal time of the given clock. The time is returned
 * unadjusted for the offset and the rate.
898
 *
899
 * Returns: the internal time of the clock. Or GST_CLOCK_TIME_NONE when
900
 * given invalid input.
901 902
 *
 * MT safe.
Wim Taymans's avatar
Wim Taymans committed
903
 */
904
GstClockTime