gstclock.c 43.3 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
 *
 * 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
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
20 21
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
Wim Taymans's avatar
Wim Taymans committed
22
 */
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.
101
 */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
102

103
#include "gst_private.h"
104
#include <time.h>
105

106
#include "gstclock.h"
107
#include "gstinfo.h"
108
#include "gstutils.h"
109
#include "glib-compat-private.h"
Wim Taymans's avatar
Wim Taymans committed
110

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

117 118
/* #define DEBUGGING_ENABLED */

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
119 120 121
#define DEFAULT_WINDOW_SIZE             32
#define DEFAULT_WINDOW_THRESHOLD        4
#define DEFAULT_TIMEOUT                 GST_SECOND / 10
122

123 124
enum
{
125 126 127 128
  PROP_0,
  PROP_WINDOW_SIZE,
  PROP_WINDOW_THRESHOLD,
  PROP_TIMEOUT
129 130
};

Wim Taymans's avatar
Wim Taymans committed
131 132 133
#define GST_CLOCK_SLAVE_LOCK(clock)     g_mutex_lock (&GST_CLOCK_CAST (clock)->priv->slave_lock)
#define GST_CLOCK_SLAVE_UNLOCK(clock)   g_mutex_unlock (&GST_CLOCK_CAST (clock)->priv->slave_lock)

134 135
struct _GstClockPrivate
{
Wim Taymans's avatar
Wim Taymans committed
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
  GMutex slave_lock;            /* order: SLAVE_LOCK, OBJECT_LOCK */

  /* with LOCK */
  GstClockTime internal_calibration;
  GstClockTime external_calibration;
  GstClockTime rate_numerator;
  GstClockTime rate_denominator;
  GstClockTime last_time;

  /* with LOCK */
  GstClockTime resolution;

  /* for master/slave clocks */
  GstClock *master;

  /* with SLAVE_LOCK */
  gboolean filling;
  gint window_size;
  gint window_threshold;
  gint time_index;
  GstClockTime timeout;
  GstClockTime *times;
  GstClockID clockid;

160 161 162 163 164 165
  gint pre_count;
  gint post_count;
};

/* seqlocks */
#define read_seqbegin(clock)                                   \
Wim Taymans's avatar
Wim Taymans committed
166
  g_atomic_int_get (&clock->priv->post_count);
167 168 169 170 171

static inline gboolean
read_seqretry (GstClock * clock, gint seq)
{
  /* no retry if the seqnum did not change */
Wim Taymans's avatar
Wim Taymans committed
172
  if (G_LIKELY (seq == g_atomic_int_get (&clock->priv->pre_count)))
173 174 175 176 177 178 179 180 181 182 183
    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);                        \
Wim Taymans's avatar
Wim Taymans committed
184
  g_atomic_int_inc (&clock->priv->pre_count);     \
185 186 187 188
} G_STMT_END;

#define write_sequnlock(clock)                    \
G_STMT_START {                                    \
Wim Taymans's avatar
Wim Taymans committed
189
  g_atomic_int_inc (&clock->priv->post_count);    \
190 191 192
  GST_OBJECT_UNLOCK (clock);                      \
} G_STMT_END;

193
#ifndef GST_DISABLE_GST_DEBUG
194 195 196
static const gchar *
gst_clock_return_get_name (GstClockReturn ret)
{
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
  switch (ret) {
    case GST_CLOCK_OK:
      return "ok";
    case GST_CLOCK_EARLY:
      return "early";
    case GST_CLOCK_UNSCHEDULED:
      return "unscheduled";
    case GST_CLOCK_BUSY:
      return "busy";
    case GST_CLOCK_BADTIME:
      return "bad-time";
    case GST_CLOCK_ERROR:
      return "error";
    case GST_CLOCK_UNSUPPORTED:
      return "unsupported";
    case GST_CLOCK_DONE:
      return "done";
    default:
      break;
216
  }
217

218 219
  return "unknown";
}
220
#endif /* GST_DISABLE_GST_DEBUG */
221

222
static void gst_clock_dispose (GObject * object);
Wim Taymans's avatar
Wim Taymans committed
223
static void gst_clock_finalize (GObject * object);
Wim Taymans's avatar
Wim Taymans committed
224

225 226 227 228
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);
229

230 231
/* static guint gst_clock_signals[LAST_SIGNAL] = { 0 }; */

Wim Taymans's avatar
Wim Taymans committed
232
static GstClockID
233 234
gst_clock_entry_new (GstClock * clock, GstClockTime time,
    GstClockTime interval, GstClockEntryType type)
235 236
{
  GstClockEntry *entry;
237

238
  entry = g_slice_new (GstClockEntry);
Wim Taymans's avatar
Wim Taymans committed
239
#ifndef GST_DISABLE_TRACE
Wim Taymans's avatar
Wim Taymans committed
240
  _gst_alloc_trace_new (_gst_clock_entry_trace, entry);
Wim Taymans's avatar
Wim Taymans committed
241
#endif
242 243
  GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
      "created entry %p, time %" GST_TIME_FORMAT, entry, GST_TIME_ARGS (time));
244

245
  entry->refcount = 1;
246
  entry->clock = clock;
247
  entry->type = type;
248
  entry->time = time;
249
  entry->interval = interval;
250
  entry->status = GST_CLOCK_OK;
251 252
  entry->func = NULL;
  entry->user_data = NULL;
253
  entry->destroy_data = NULL;
254 255
  entry->unscheduled = FALSE;
  entry->woken_up = FALSE;
256

257 258
  return (GstClockID) entry;
}
259

260
/* WARNING : Does not modify the refcount
261 262 263 264 265 266 267 268 269 270 271 272
 * 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;
273 274
  entry->unscheduled = FALSE;
  entry->woken_up = FALSE;
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298

  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);
}

299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
/**
 * gst_clock_periodic_id_reinit:
 * @clock: a #GstClock
 * @id: a #GstClockID
 * @start_time: the requested start time
 * @interval: the requested interval
 *
 * Reinitializes the provided periodic @id to the provided start time and
 * interval. Does not modify the reference count.
 *
 * Returns: %TRUE if the GstClockID could be reinitialized to the provided
 * @time, else %FALSE.
 */
gboolean
gst_clock_periodic_id_reinit (GstClock * clock, GstClockID id,
    GstClockTime start_time, GstClockTime interval)
{
  return gst_clock_entry_reinit (clock, (GstClockEntry *) id, start_time,
      interval, GST_CLOCK_ENTRY_PERIODIC);
}

320 321
/**
 * gst_clock_id_ref:
Wim Taymans's avatar
Wim Taymans committed
322
 * @id: The #GstClockID to ref
323
 *
Wim Taymans's avatar
Wim Taymans committed
324
 * Increase the refcount of given @id.
325
 *
326
 * Returns: (transfer full): The same #GstClockID with increased refcount.
327 328 329 330 331 332 333 334
 *
 * MT safe.
 */
GstClockID
gst_clock_id_ref (GstClockID id)
{
  g_return_val_if_fail (id != NULL, NULL);

335
  g_atomic_int_inc (&((GstClockEntry *) id)->refcount);
336 337 338 339 340 341 342

  return id;
}

static void
_gst_clock_id_free (GstClockID id)
{
343
  GstClockEntry *entry;
344 345 346
  g_return_if_fail (id != NULL);

  GST_CAT_DEBUG (GST_CAT_CLOCK, "freed entry %p", id);
347 348 349
  entry = (GstClockEntry *) id;
  if (entry->destroy_data)
    entry->destroy_data (entry->user_data);
350 351

#ifndef GST_DISABLE_TRACE
Wim Taymans's avatar
Wim Taymans committed
352
  _gst_alloc_trace_free (_gst_clock_entry_trace, id);
353
#endif
354
  g_slice_free (GstClockEntry, id);
355 356 357 358
}

/**
 * gst_clock_id_unref:
359
 * @id: (transfer full): The #GstClockID to unref
360
 *
Wim Taymans's avatar
Wim Taymans committed
361
 * Unref given @id. When the refcount reaches 0 the
362 363 364 365 366 367 368 369 370 371 372
 * #GstClockID will be freed.
 *
 * MT safe.
 */
void
gst_clock_id_unref (GstClockID id)
{
  gint zero;

  g_return_if_fail (id != NULL);

373
  zero = g_atomic_int_dec_and_test (&((GstClockEntry *) id)->refcount);
374 375 376 377 378 379
  /* if we ended up with the refcount at zero, free the id */
  if (zero) {
    _gst_clock_id_free (id);
  }
}

380
/**
381
 * gst_clock_new_single_shot_id:
Wim Taymans's avatar
Wim Taymans committed
382
 * @clock: The #GstClockID to get a single shot notification from
383 384
 * @time: the requested time
 *
Wim Taymans's avatar
Wim Taymans committed
385
 * Get a #GstClockID from @clock to trigger a single shot
386 387
 * notification at the requested time. The single shot id should be
 * unreffed after usage.
388
 *
389 390 391 392
 * Free-function: gst_clock_id_unref
 *
 * Returns: (transfer full): a #GstClockID that can be used to request the
 *     time notification.
393 394
 *
 * MT safe.
395 396
 */
GstClockID
397
gst_clock_new_single_shot_id (GstClock * clock, GstClockTime time)
398
{
399 400
  g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);

401 402
  return gst_clock_entry_new (clock,
      time, GST_CLOCK_TIME_NONE, GST_CLOCK_ENTRY_SINGLE);
403 404 405
}

/**
406
 * gst_clock_new_periodic_id:
Wim Taymans's avatar
Wim Taymans committed
407
 * @clock: The #GstClockID to get a periodic notification id from
408 409 410
 * @start_time: the requested start time
 * @interval: the requested interval
 *
Wim Taymans's avatar
Wim Taymans committed
411
 * Get an ID from @clock to trigger a periodic notification.
412 413
 * The periodic notifications will start at time @start_time and
 * will then be fired with the given @interval. @id should be unreffed
414
 * after usage.
415
 *
416 417 418 419
 * Free-function: gst_clock_id_unref
 *
 * Returns: (transfer full): a #GstClockID that can be used to request the
 *     time notification.
420 421
 *
 * MT safe.
422 423
 */
GstClockID
424 425
gst_clock_new_periodic_id (GstClock * clock, GstClockTime start_time,
    GstClockTime interval)
426
{
427
  g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);
428
  g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (start_time), NULL);
429
  g_return_val_if_fail (interval != 0, NULL);
430
  g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (interval), NULL);
431

432 433
  return gst_clock_entry_new (clock,
      start_time, interval, GST_CLOCK_ENTRY_PERIODIC);
434 435
}

436
/**
437
 * gst_clock_id_compare_func:
Wim Taymans's avatar
Wim Taymans committed
438 439
 * @id1: A #GstClockID
 * @id2: A #GstClockID to compare with
440
 *
Wim Taymans's avatar
Wim Taymans committed
441
 * Compares the two #GstClockID instances. This function can be used
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
 * 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
462
  return 0;
463 464
}

465
/**
466
 * gst_clock_id_get_time:
Wim Taymans's avatar
Wim Taymans committed
467
 * @id: The #GstClockID to query
468 469 470
 *
 * Get the time of the clock ID
 *
471 472 473
 * Returns: the time of the given clock id.
 *
 * MT safe.
474 475 476 477 478 479
 */
GstClockTime
gst_clock_id_get_time (GstClockID id)
{
  g_return_val_if_fail (id != NULL, GST_CLOCK_TIME_NONE);

480
  return GST_CLOCK_ENTRY_TIME ((GstClockEntry *) id);
481 482 483
}

/**
484
 * gst_clock_id_wait:
Wim Taymans's avatar
Wim Taymans committed
485
 * @id: The #GstClockID to wait on
Wim Taymans's avatar
Wim Taymans committed
486
 * @jitter: (out) (allow-none): a pointer that will contain the jitter,
487
 *     can be %NULL.
Wim Taymans's avatar
Wim Taymans committed
488 489 490 491 492 493
 *
 * 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(). 
 *
494
 * If the @jitter argument is not %NULL and this function returns #GST_CLOCK_OK
Wim Taymans's avatar
Wim Taymans committed
495 496
 * or #GST_CLOCK_EARLY, it will contain the difference
 * against the clock and the time of @id when this method was
497 498
 * called. 
 * Positive values indicate how late @id was relative to the clock
499
 * (in which case this function will return #GST_CLOCK_EARLY). 
500
 * Negative values indicate how much time was spent waiting on the clock 
501
 * before this function returned.
Wim Taymans's avatar
Wim Taymans committed
502 503 504 505 506
 *
 * 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().
507 508
 *
 * MT safe.
509 510
 */
GstClockReturn
511
gst_clock_id_wait (GstClockID id, GstClockTimeDiff * jitter)
512 513
{
  GstClockEntry *entry;
514
  GstClock *clock;
515
  GstClockReturn res;
516
  GstClockTime requested;
Wim Taymans's avatar
Wim Taymans committed
517
  GstClockClass *cclass;
518

519
  g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR);
520

521 522
  entry = (GstClockEntry *) id;
  requested = GST_CLOCK_ENTRY_TIME (entry);
523

524 525
  clock = GST_CLOCK_ENTRY_CLOCK (entry);

Wim Taymans's avatar
Wim Taymans committed
526
  /* can't sync on invalid times */
527 528 529
  if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested)))
    goto invalid_time;

Wim Taymans's avatar
Wim Taymans committed
530
  cclass = GST_CLOCK_GET_CLASS (clock);
531

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

534
  /* if we have a wait_jitter function, use that */
Wim Taymans's avatar
Wim Taymans committed
535 536 537 538
  if (G_UNLIKELY (cclass->wait == NULL))
    goto not_supported;

  res = cclass->wait (clock, entry, jitter);
Wim Taymans's avatar
Wim Taymans committed
539 540

  GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
541 542
      "done waiting entry %p, res: %d (%s)", id, res,
      gst_clock_return_get_name (res));
Wim Taymans's avatar
Wim Taymans committed
543

544 545
  if (entry->type == GST_CLOCK_ENTRY_PERIODIC)
    entry->time = requested + entry->interval;
Wim Taymans's avatar
Wim Taymans committed
546

547
  return res;
548 549 550 551

  /* ERRORS */
invalid_time:
  {
552 553
    GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
        "invalid time requested, returning _BADTIME");
554 555
    return GST_CLOCK_BADTIME;
  }
Wim Taymans's avatar
Wim Taymans committed
556 557 558 559 560
not_supported:
  {
    GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "clock wait is not supported");
    return GST_CLOCK_UNSUPPORTED;
  }
561 562 563
}

/**
Wim Taymans's avatar
Wim Taymans committed
564
 * gst_clock_id_wait_async:
Wim Taymans's avatar
Wim Taymans committed
565
 * @id: a #GstClockID to wait on
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
566
 * @func: The callback function
567
 * @user_data: User data passed in the callback
568
 * @destroy_data: #GDestroyNotify for user_data
569
 *
Wim Taymans's avatar
Wim Taymans committed
570 571
 * Register a callback on the given #GstClockID @id with the given
 * function and user_data. When passing a #GstClockID with an invalid
572
 * time to this function, the callback will be called immediately
573
 * with  a time set to GST_CLOCK_TIME_NONE. The callback will
Wim Taymans's avatar
Wim Taymans committed
574
 * be called when the time of @id has been reached.
575
 *
576 577 578
 * 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.
 *
579
 * Returns: the result of the non blocking wait.
580 581
 *
 * MT safe.
582 583
 */
GstClockReturn
Wim Taymans's avatar
Wim Taymans committed
584
gst_clock_id_wait_async (GstClockID id,
585
    GstClockCallback func, gpointer user_data, GDestroyNotify destroy_data)
586 587 588
{
  GstClockEntry *entry;
  GstClock *clock;
589
  GstClockReturn res;
Wim Taymans's avatar
Wim Taymans committed
590
  GstClockClass *cclass;
591
  GstClockTime requested;
592

593
  g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR);
594
  g_return_val_if_fail (func != NULL, GST_CLOCK_ERROR);
595 596

  entry = (GstClockEntry *) id;
597 598
  requested = GST_CLOCK_ENTRY_TIME (entry);
  clock = GST_CLOCK_ENTRY_CLOCK (entry);
599

Wim Taymans's avatar
Wim Taymans committed
600
  /* can't sync on invalid times */
601 602 603
  if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested)))
    goto invalid_time;

Wim Taymans's avatar
Wim Taymans committed
604 605
  cclass = GST_CLOCK_GET_CLASS (clock);

Wim Taymans's avatar
Wim Taymans committed
606 607 608 609 610
  if (G_UNLIKELY (cclass->wait_async == NULL))
    goto not_supported;

  entry->func = func;
  entry->user_data = user_data;
611
  entry->destroy_data = destroy_data;
Wim Taymans's avatar
Wim Taymans committed
612 613

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

615 616
  return res;

617 618 619 620
  /* ERRORS */
invalid_time:
  {
    (func) (clock, GST_CLOCK_TIME_NONE, id, user_data);
621 622
    GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
        "invalid time requested, returning _BADTIME");
623 624
    return GST_CLOCK_BADTIME;
  }
Wim Taymans's avatar
Wim Taymans committed
625 626 627 628 629
not_supported:
  {
    GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "clock wait is not supported");
    return GST_CLOCK_UNSUPPORTED;
  }
630 631
}

632
/**
Wim Taymans's avatar
Wim Taymans committed
633 634
 * gst_clock_id_unschedule:
 * @id: The id to unschedule
635
 *
Wim Taymans's avatar
Wim Taymans committed
636
 * Cancel an outstanding request with @id. This can either
637
 * be an outstanding async notification or a pending sync notification.
Wim Taymans's avatar
Wim Taymans committed
638 639
 * After this call, @id cannot be used anymore to receive sync or
 * async notifications, you need to create a new #GstClockID.
640 641
 *
 * MT safe.
642 643 644 645 646 647
 */
void
gst_clock_id_unschedule (GstClockID id)
{
  GstClockEntry *entry;
  GstClock *clock;
Wim Taymans's avatar
Wim Taymans committed
648
  GstClockClass *cclass;
649

650 651 652 653
  g_return_if_fail (id != NULL);

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

Wim Taymans's avatar
Wim Taymans committed
655 656
  cclass = GST_CLOCK_GET_CLASS (clock);

Wim Taymans's avatar
Wim Taymans committed
657
  if (G_LIKELY (cclass->unschedule))
Wim Taymans's avatar
Wim Taymans committed
658
    cclass->unschedule (clock, entry);
659 660
}

661

Stefan Kost's avatar
Stefan Kost committed
662
/*
663 664
 * GstClock abstract base class implementation
 */
Wim Taymans's avatar
Wim Taymans committed
665
#define gst_clock_parent_class parent_class
Wim Taymans's avatar
Wim Taymans committed
666
G_DEFINE_ABSTRACT_TYPE (GstClock, gst_clock, GST_TYPE_OBJECT);
667

668
static void
669
gst_clock_class_init (GstClockClass * klass)
670
{
671
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
672

Wim Taymans's avatar
Wim Taymans committed
673
#ifndef GST_DISABLE_TRACE
Wim Taymans's avatar
Wim Taymans committed
674
  _gst_clock_entry_trace = _gst_alloc_trace_register ("GstClockEntry", -1);
Wim Taymans's avatar
Wim Taymans committed
675
#endif
676

677 678 679 680
  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;
681

682
  g_object_class_install_property (gobject_class, PROP_WINDOW_SIZE,
683 684
      g_param_spec_int ("window-size", "Window size",
          "The size of the window used to calculate rate and offset", 2, 1024,
685
          DEFAULT_WINDOW_SIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
686 687
  g_object_class_install_property (gobject_class, PROP_WINDOW_THRESHOLD,
      g_param_spec_int ("window-threshold", "Window threshold",
688
          "The threshold to start calculating rate and offset", 2, 1024,
689 690
          DEFAULT_WINDOW_THRESHOLD,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
691
  g_object_class_install_property (gobject_class, PROP_TIMEOUT,
692 693
      g_param_spec_uint64 ("timeout", "Timeout",
          "The amount of time, in nanoseconds, to sample master and slave clocks",
694 695
          0, G_MAXUINT64, DEFAULT_TIMEOUT,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
696 697

  g_type_class_add_private (klass, sizeof (GstClockPrivate));
Wim Taymans's avatar
Wim Taymans committed
698 699
}

700
static void
701
gst_clock_init (GstClock * clock)
Wim Taymans's avatar
Wim Taymans committed
702
{
Wim Taymans's avatar
Wim Taymans committed
703
  GstClockPrivate *priv;
704

Wim Taymans's avatar
Wim Taymans committed
705
  clock->priv = priv =
706 707
      G_TYPE_INSTANCE_GET_PRIVATE (clock, GST_TYPE_CLOCK, GstClockPrivate);

Wim Taymans's avatar
Wim Taymans committed
708 709 710 711 712 713 714 715 716 717 718 719 720 721
  priv->last_time = 0;

  priv->internal_calibration = 0;
  priv->external_calibration = 0;
  priv->rate_numerator = 1;
  priv->rate_denominator = 1;

  g_mutex_init (&priv->slave_lock);
  priv->window_size = DEFAULT_WINDOW_SIZE;
  priv->window_threshold = DEFAULT_WINDOW_THRESHOLD;
  priv->filling = TRUE;
  priv->time_index = 0;
  priv->timeout = DEFAULT_TIMEOUT;
  priv->times = g_new0 (GstClockTime, 4 * priv->window_size);
722 723 724

  /* clear floating flag */
  gst_object_ref_sink (clock);
Wim Taymans's avatar
Wim Taymans committed
725 726
}

727 728 729 730
static void
gst_clock_dispose (GObject * object)
{
  GstClock *clock = GST_CLOCK (object);
731
  GstClock **master_p;
732 733

  GST_OBJECT_LOCK (clock);
Wim Taymans's avatar
Wim Taymans committed
734
  master_p = &clock->priv->master;
735
  gst_object_replace ((GstObject **) master_p, NULL);
736 737 738 739 740
  GST_OBJECT_UNLOCK (clock);

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

Wim Taymans's avatar
Wim Taymans committed
741
static void
Wim Taymans's avatar
Wim Taymans committed
742
gst_clock_finalize (GObject * object)
Wim Taymans's avatar
Wim Taymans committed
743 744 745
{
  GstClock *clock = GST_CLOCK (object);

746
  GST_CLOCK_SLAVE_LOCK (clock);
Wim Taymans's avatar
Wim Taymans committed
747 748 749 750
  if (clock->priv->clockid) {
    gst_clock_id_unschedule (clock->priv->clockid);
    gst_clock_id_unref (clock->priv->clockid);
    clock->priv->clockid = NULL;
751
  }
Wim Taymans's avatar
Wim Taymans committed
752 753
  g_free (clock->priv->times);
  clock->priv->times = NULL;
754
  GST_CLOCK_SLAVE_UNLOCK (clock);
755

Wim Taymans's avatar
Wim Taymans committed
756
  g_mutex_clear (&clock->priv->slave_lock);
757

Wim Taymans's avatar
Wim Taymans committed
758
  G_OBJECT_CLASS (parent_class)->finalize (object);
Wim Taymans's avatar
Wim Taymans committed
759 760
}

Wim Taymans's avatar
Wim Taymans committed
761
/**
762
 * gst_clock_set_resolution:
763
 * @clock: a #GstClock
764
 * @resolution: The resolution to set
Wim Taymans's avatar
Wim Taymans committed
765
 *
766 767 768 769 770
 * 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.
771 772
 *
 * Returns: the new resolution of the clock.
Wim Taymans's avatar
Wim Taymans committed
773
 */
774 775
GstClockTime
gst_clock_set_resolution (GstClock * clock, GstClockTime resolution)
Wim Taymans's avatar
Wim Taymans committed
776
{
Wim Taymans's avatar
Wim Taymans committed
777
  GstClockPrivate *priv;
Wim Taymans's avatar
Wim Taymans committed
778 779
  GstClockClass *cclass;

780 781
  g_return_val_if_fail (GST_IS_CLOCK (clock), 0);
  g_return_val_if_fail (resolution != 0, 0);
782

Wim Taymans's avatar
Wim Taymans committed
783
  cclass = GST_CLOCK_GET_CLASS (clock);
Wim Taymans's avatar
Wim Taymans committed
784
  priv = clock->priv;
Wim Taymans's avatar
Wim Taymans committed
785 786

  if (cclass->change_resolution)
Wim Taymans's avatar
Wim Taymans committed
787 788
    priv->resolution =
        cclass->change_resolution (clock, priv->resolution, resolution);
789

Wim Taymans's avatar
Wim Taymans committed
790
  return priv->resolution;
791
}
792

793
/**
794
 * gst_clock_get_resolution:
795
 * @clock: a #GstClock
796
 *
797 798
 * Get the accuracy of the clock. The accuracy of the clock is the granularity
 * of the values returned by gst_clock_get_time().
799
 *
800
 * Returns: the resolution of the clock in units of #GstClockTime.
801 802
 *
 * MT safe.
803
 */
804
GstClockTime
805
gst_clock_get_resolution (GstClock * clock)
806
{
Wim Taymans's avatar
Wim Taymans committed
807 808
  GstClockClass *cclass;

809
  g_return_val_if_fail (GST_IS_CLOCK (clock), 0);
810

Wim Taymans's avatar
Wim Taymans committed
811 812 813 814
  cclass = GST_CLOCK_GET_CLASS (clock);

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

816
  return 1;
Wim Taymans's avatar
Wim Taymans committed
817 818
}

Wim Taymans's avatar
Wim Taymans committed
819
/**
820
 * gst_clock_adjust_with_calibration:
821
 * @clock: a #GstClock to use
822 823 824 825 826 827 828 829 830 831 832 833
 * @internal_target: a clock time
 * @cinternal: a reference internal time
 * @cexternal: a reference external time
 * @cnum: the numerator of the rate of the clock relative to its
 *        internal time
 * @cdenom: the denominator of the rate of the clock
 *
 * Converts the given @internal_target clock time to the external time,
 * using the passed calibration parameters. This function performs the
 * same calculation as gst_clock_adjust_unlocked() when called using the
 * current calibration parameters, but doesn't ensure a monotonically
 * increasing result as gst_clock_adjust_unlocked() does.
834
 *
835
 * Returns: the converted time of the clock.
836 837
 *
 * Since: 1.6
838
 */
839
GstClockTime
840 841 842
gst_clock_adjust_with_calibration (GstClock * clock,
    GstClockTime internal_target, GstClockTime cinternal,
    GstClockTime cexternal, GstClockTime cnum, GstClockTime cdenom)
843
{
844
  GstClockTime ret;
845 846

  /* avoid divide by 0 */
847
  if (G_UNLIKELY (cdenom == 0))
848 849 850 851 852
    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
853
   * internal < cinternal to get the sign right. this case is not very common,
854 855
   * though.
   */
856 857
  if (G_LIKELY (internal_target >= cinternal)) {
    ret = internal_target - cinternal;
858
    ret = gst_util_uint64_scale (ret, cnum, cdenom);
859 860
    ret += cexternal;
  } else {
861
    ret = cinternal - internal_target;
862
    ret = gst_util_uint64_scale (ret, cnum, cdenom);
863
    /* clamp to 0 */
864
    if (G_LIKELY (cexternal > ret))
865 866 867 868
      ret = cexternal - ret;
    else
      ret = 0;
  }
869

870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902
  return ret;
}

/**
 * gst_clock_adjust_unlocked:
 * @clock: a #GstClock to use
 * @internal: a clock time
 *
 * Converts the given @internal clock time to the external time, adjusting for the
 * 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
 * clock's OBJECT_LOCK held and is mainly used by clock subclasses.
 *
 * This function is the reverse of gst_clock_unadjust_unlocked().
 *
 * Returns: the converted time of the clock.
 */
GstClockTime
gst_clock_adjust_unlocked (GstClock * clock, GstClockTime internal)
{
  GstClockTime ret, cinternal, cexternal, cnum, cdenom;
  GstClockPrivate *priv = clock->priv;

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

  ret =
      gst_clock_adjust_with_calibration (clock, internal, cinternal, cexternal,
      cnum, cdenom);

903
  /* make sure the time is increasing */
Wim Taymans's avatar
Wim Taymans committed
904
  priv->last_time = MAX (ret, priv->last_time);
905

Wim Taymans's avatar
Wim Taymans committed
906
  return priv->last_time;
907 908
}

909
/**
910
 * gst_clock_unadjust_unlocked:
911 912 913 914 915 916 917 918
 * @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.
 *
919
 * This function is the reverse of gst_clock_adjust_unlocked().
920 921 922 923 924 925 926
 *
 * Returns: the internal time of the clock corresponding to @external.
 */
GstClockTime
gst_clock_unadjust_unlocked (GstClock * clock, GstClockTime external)
{
  GstClockTime ret, cinternal, cexternal, cnum, cdenom;
Wim Taymans's avatar
Wim Taymans committed
927
  GstClockPrivate *priv = clock->priv;
928 929

  /* get calibration values for readability */
Wim Taymans's avatar
Wim Taymans committed
930 931 932 933
  cinternal = priv->internal_calibration;
  cexternal = priv->external_calibration;
  cnum = priv->rate_numerator;
  cdenom = priv->rate_denominator;
934 935

  /* avoid divide by 0 */
936
  if (G_UNLIKELY (cnum == 0