gstclock.c 31.1 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 27 28
/**
 * SECTION:gstclock
 * @short_description: Abstract class for global clocks
 * @see_also: #GstSystemClock
 *
Wim Taymans's avatar
Wim Taymans committed
29
 * GStreamer uses a global clock to synchronize the plugins in a pipeline.
30 31 32
 * Different clock implementations are possible by implementing this abstract
 * base class.
 *
Wim Taymans's avatar
Wim Taymans committed
33 34 35 36 37 38 39 40 41
 * The #GstClock returns a monotonically increasing time with the method
 * gst_clock_get_time(). Its accuracy and base time depends on the specific clock
 * implementation but time is always expessed in nanoseconds. Since the
 * baseline of the clock is undefined, the clock time returned is not
 * meaningfull in itself, what matters are the deltas between two clock
 * times.
 *
 * The pipeline uses the clock to calculate the stream time.
 * Usually all renderers synchronize to the global clock using the buffer timestamps,
42
 * the newsegment events and the element's base time.
Wim Taymans's avatar
Wim Taymans committed
43
 *
Stefan Kost's avatar
Stefan Kost committed
44
 * The time of the clock in itself is not very useful for an application.
Wim Taymans's avatar
Wim Taymans committed
45 46 47 48 49 50 51 52 53 54 55 56 57
 *
 * A clock implementation can support periodic and single shot clock notifications 
 * both synchronous and asynchronous.
 *
 * One first needs to create a #GstClockID for the periodic or single shot
 * notification using gst_clock_new_single_shot_id() or gst_clock_new_periodic_id().
 *
 * 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
 * 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 unscheduled
 * a return value of GST_CLOCK_UNSCHEDULED is returned.
 *
58 59 60 61
 * Periodic callbacks scheduled async will be repeadedly called automatically until
 * it is unscheduled. To schedule an async periodic callback, gst_clock_id_wait() 
 * should be called repeadedly.
 *
Wim Taymans's avatar
Wim Taymans committed
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
 * 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.
 *
 * A #GstClockID that has been unscheduled cannot be used again for any wait
 * operation.
 *
 * It is possible to perform a blocking wait on the same #GstClockID from multiple
 * threads. However, registering the same #GstClockID for multiple async notifications is
 * not possible, the callback will only be called once.
 *
 * 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 state changes and if we unref it automatically, the handle might be
 * invalid.
 *
 * These clock operations do not operate on the stream time, so the callbacks
 * 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.
 *
 * Last reviewed on 2005-10-28 (0.9.4)
85
 */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
86

87
#include <time.h>
88 89

#include "gst_private.h"
90

91
#include "gstclock.h"
92
#include "gstinfo.h"
93
#include "gstutils.h"
Wim Taymans's avatar
Wim Taymans committed
94

Wim Taymans's avatar
Wim Taymans committed
95
#ifndef GST_DISABLE_TRACE
Wim Taymans's avatar
Wim Taymans committed
96
/* #define GST_WITH_ALLOC_TRACE */
Wim Taymans's avatar
Wim Taymans committed
97 98 99
#include "gsttrace.h"
static GstAllocTrace *_gst_clock_entry_trace;
#endif
Wim Taymans's avatar
Wim Taymans committed
100

101 102 103 104 105 106 107 108 109 110 111 112
/* #define DEBUGGING_ENABLED */

#ifdef DEBUGGING_ENABLED
#define DEBUG(x, args...) g_print (x "\n", ##args)
#else
#define DEBUG(x, args...)       /* nop */
#endif

#define DEFAULT_STATS			FALSE
#define DEFAULT_WINDOW_SIZE		32
#define DEFAULT_WINDOW_THRESHOLD	4
#define DEFAULT_TIMEOUT         	GST_SECOND / 10
113

114 115
enum
{
116 117 118 119 120
  PROP_0,
  PROP_STATS,
  PROP_WINDOW_SIZE,
  PROP_WINDOW_THRESHOLD,
  PROP_TIMEOUT
121 122
};

123 124
static void gst_clock_class_init (GstClockClass * klass);
static void gst_clock_init (GstClock * clock);
Wim Taymans's avatar
Wim Taymans committed
125
static void gst_clock_finalize (GObject * object);
Wim Taymans's avatar
Wim Taymans committed
126

127 128 129 130 131
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);
132

133

134
static GstObjectClass *parent_class = NULL;
135

136 137
/* static guint gst_clock_signals[LAST_SIGNAL] = { 0 }; */

Wim Taymans's avatar
Wim Taymans committed
138
static GstClockID
139 140
gst_clock_entry_new (GstClock * clock, GstClockTime time,
    GstClockTime interval, GstClockEntryType type)
141 142
{
  GstClockEntry *entry;
143

144
  entry = g_malloc0 (sizeof (GstClockEntry));
Wim Taymans's avatar
Wim Taymans committed
145
#ifndef GST_DISABLE_TRACE
Wim Taymans's avatar
Wim Taymans committed
146
  gst_alloc_trace_new (_gst_clock_entry_trace, entry);
Wim Taymans's avatar
Wim Taymans committed
147
#endif
148 149
  GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
      "created entry %p, time %" GST_TIME_FORMAT, entry, GST_TIME_ARGS (time));
150

151
  gst_atomic_int_set (&entry->refcount, 1);
152 153
  entry->clock = clock;
  entry->time = time;
154
  entry->interval = interval;
155
  entry->type = type;
156
  entry->status = GST_CLOCK_BUSY;
157

158 159
  return (GstClockID) entry;
}
160

161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
/**
 * gst_clock_id_ref:
 * @id: The clockid to ref
 *
 * Increase the refcount of the given clockid.
 *
 * Returns: The same #GstClockID with increased refcount.
 *
 * MT safe.
 */
GstClockID
gst_clock_id_ref (GstClockID id)
{
  g_return_val_if_fail (id != NULL, NULL);

176
  g_atomic_int_inc (&((GstClockEntry *) id)->refcount);
177 178 179 180 181 182 183 184 185 186 187 188 189 190

  return id;
}

static void
_gst_clock_id_free (GstClockID id)
{
  g_return_if_fail (id != NULL);

  GST_CAT_DEBUG (GST_CAT_CLOCK, "freed entry %p", id);

#ifndef GST_DISABLE_TRACE
  gst_alloc_trace_free (_gst_clock_entry_trace, id);
#endif
191
  g_free (id);
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
}

/**
 * gst_clock_id_unref:
 * @id: The clockid to unref
 *
 * Unref the given clockid. When the refcount reaches 0 the
 * #GstClockID will be freed.
 *
 * MT safe.
 */
void
gst_clock_id_unref (GstClockID id)
{
  gint zero;

  g_return_if_fail (id != NULL);

210
  zero = g_atomic_int_dec_and_test (&((GstClockEntry *) id)->refcount);
211 212 213 214 215 216
  /* if we ended up with the refcount at zero, free the id */
  if (zero) {
    _gst_clock_id_free (id);
  }
}

217 218 219 220 221
/**
 * gst_clock_new_single_shot_id
 * @clock: The clockid to get a single shot notification from
 * @time: the requested time
 *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
222
 * Get an ID from the given clock to trigger a single shot
223 224
 * notification at the requested time. The single shot id should be
 * unreffed after usage.
225 226
 *
 * Returns: An id that can be used to request the time notification.
227 228
 *
 * MT safe.
229 230
 */
GstClockID
231
gst_clock_new_single_shot_id (GstClock * clock, GstClockTime time)
232
{
233 234
  g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);

235 236
  return gst_clock_entry_new (clock,
      time, GST_CLOCK_TIME_NONE, GST_CLOCK_ENTRY_SINGLE);
237 238 239
}

/**
Wim Taymans's avatar
Wim Taymans committed
240
 * gst_clock_new_periodic_id
241 242 243 244 245 246
 * @clock: The clockid to get a periodic notification id from
 * @start_time: the requested start time
 * @interval: the requested interval
 *
 * Get an ID from the given clock to trigger a periodic notification.
 * The periodeic notifications will be start at time start_time and
247 248
 * will then be fired with the given interval. The id should be unreffed
 * after usage.
249 250
 *
 * Returns: An id that can be used to request the time notification.
251 252
 *
 * MT safe.
253 254
 */
GstClockID
255 256
gst_clock_new_periodic_id (GstClock * clock, GstClockTime start_time,
    GstClockTime interval)
257
{
258
  g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);
259
  g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (start_time), NULL);
260 261
  g_return_val_if_fail (interval != 0, NULL);

262 263
  return gst_clock_entry_new (clock,
      start_time, interval, GST_CLOCK_ENTRY_PERIODIC);
264 265
}

266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
/**
 * gst_clock_id_compare_func
 * @id1: A clockid
 * @id2: A clockid to compare with
 *
 * Compares the two GstClockID instances. This function can be used
 * 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;
  }

  return entry1 - entry2;
}

296 297 298 299 300 301
/**
 * gst_clock_id_get_time
 * @id: The clockid to query
 *
 * Get the time of the clock ID
 *
302 303 304
 * Returns: the time of the given clock id.
 *
 * MT safe.
305 306 307 308 309 310
 */
GstClockTime
gst_clock_id_get_time (GstClockID id)
{
  g_return_val_if_fail (id != NULL, GST_CLOCK_TIME_NONE);

311
  return GST_CLOCK_ENTRY_TIME ((GstClockEntry *) id);
312 313 314 315 316 317 318 319 320
}


/**
 * gst_clock_id_wait
 * @id: The clockid to wait on
 * @jitter: A pointer that will contain the jitter
 *
 * Perform a blocking wait on the given ID. The jitter arg can be
321
 * NULL.
322 323
 *
 * Returns: the result of the blocking wait.
324 325
 *
 * MT safe.
326 327
 */
GstClockReturn
328
gst_clock_id_wait (GstClockID id, GstClockTimeDiff * jitter)
329 330
{
  GstClockEntry *entry;
331
  GstClock *clock;
332
  GstClockReturn res;
333
  GstClockTime requested;
Wim Taymans's avatar
Wim Taymans committed
334
  GstClockClass *cclass;
335

336
  g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR);
337

338 339
  entry = (GstClockEntry *) id;
  requested = GST_CLOCK_ENTRY_TIME (entry);
340

341 342
  clock = GST_CLOCK_ENTRY_CLOCK (entry);

343 344 345 346 347
  if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested)))
    goto invalid_time;

  if (G_UNLIKELY (entry->status == GST_CLOCK_UNSCHEDULED))
    goto unscheduled;
348

Wim Taymans's avatar
Wim Taymans committed
349
  cclass = GST_CLOCK_GET_CLASS (clock);
350

351
  if (G_LIKELY (cclass->wait)) {
352

353 354
    GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "waiting on clock entry %p",
        id);
355
    res = cclass->wait (clock, entry);
356
    GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "done waiting entry %p", id);
357

358
    if (jitter) {
359 360
      GstClockTime now = gst_clock_get_time (clock);

361 362
      *jitter = now - requested;
    }
363 364 365
    if (entry->type == GST_CLOCK_ENTRY_PERIODIC) {
      entry->time += entry->interval;
    }
366 367 368 369

    if (clock->stats) {
      gst_clock_update_stats (clock);
    }
370 371
  } else {
    res = GST_CLOCK_UNSUPPORTED;
372 373
  }
  return res;
374 375 376 377

  /* ERRORS */
invalid_time:
  {
378 379
    GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
        "invalid time requested, returning _BADTIME");
380 381 382 383
    return GST_CLOCK_BADTIME;
  }
unscheduled:
  {
384 385
    GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
        "entry was unscheduled return _UNSCHEDULED");
386 387
    return GST_CLOCK_UNSCHEDULED;
  }
388 389 390
}

/**
Wim Taymans's avatar
Wim Taymans committed
391 392
 * gst_clock_id_wait_async:
 * @id: a #GstClockID to wait on
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
393
 * @func: The callback function
394 395
 * @user_data: User data passed in the calback
 *
Wim Taymans's avatar
Wim Taymans committed
396
 * Register a callback on the given clockid with the given
397 398 399 400
 * function and user_data. When passing an id with an invalid
 * time to this function, the callback will be called immediatly
 * with  a time set to GST_CLOCK_TIME_NONE. The callback will
 * be called when the time of the id has been reached.
401
 *
402
 * Returns: the result of the non blocking wait.
403 404
 *
 * MT safe.
405 406 407
 */
GstClockReturn
gst_clock_id_wait_async (GstClockID id,
408
    GstClockCallback func, gpointer user_data)
409 410 411
{
  GstClockEntry *entry;
  GstClock *clock;
412
  GstClockReturn res;
Wim Taymans's avatar
Wim Taymans committed
413
  GstClockClass *cclass;
414
  GstClockTime requested;
415

416
  g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR);
417
  g_return_val_if_fail (func != NULL, GST_CLOCK_ERROR);
418 419

  entry = (GstClockEntry *) id;
420 421
  requested = GST_CLOCK_ENTRY_TIME (entry);
  clock = GST_CLOCK_ENTRY_CLOCK (entry);
422

423 424 425 426 427
  if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested)))
    goto invalid_time;

  if (G_UNLIKELY (entry->status == GST_CLOCK_UNSCHEDULED))
    goto unscheduled;
428

Wim Taymans's avatar
Wim Taymans committed
429 430 431 432 433 434 435
  cclass = GST_CLOCK_GET_CLASS (clock);

  if (cclass->wait_async) {
    entry->func = func;
    entry->user_data = user_data;

    res = cclass->wait_async (clock, entry);
436 437
  } else {
    res = GST_CLOCK_UNSUPPORTED;
438 439 440
  }
  return res;

441 442 443 444
  /* ERRORS */
invalid_time:
  {
    (func) (clock, GST_CLOCK_TIME_NONE, id, user_data);
445 446
    GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
        "invalid time requested, returning _BADTIME");
447 448 449 450
    return GST_CLOCK_BADTIME;
  }
unscheduled:
  {
451 452
    GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
        "entry was unscheduled return _UNSCHEDULED");
453 454
    return GST_CLOCK_UNSCHEDULED;
  }
455 456
}

457
/**
Wim Taymans's avatar
Wim Taymans committed
458 459
 * gst_clock_id_unschedule:
 * @id: The id to unschedule
460
 *
461 462 463 464 465 466
 * Cancel an outstanding request with the given ID. This can either
 * be an outstanding async notification or a pending sync notification.
 * After this call, the @id cannot be used anymore to receive sync or
 * async notifications, you need to create a new GstClockID.
 *
 * MT safe.
467 468 469 470 471 472
 */
void
gst_clock_id_unschedule (GstClockID id)
{
  GstClockEntry *entry;
  GstClock *clock;
Wim Taymans's avatar
Wim Taymans committed
473
  GstClockClass *cclass;
474

475 476 477 478
  g_return_if_fail (id != NULL);

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

Wim Taymans's avatar
Wim Taymans committed
480 481 482 483
  cclass = GST_CLOCK_GET_CLASS (clock);

  if (cclass->unschedule)
    cclass->unschedule (clock, entry);
484 485
}

486 487 488 489

/**
 * GstClock abstract base class implementation
 */
490 491
GType
gst_clock_get_type (void)
Wim Taymans's avatar
Wim Taymans committed
492
{
493 494 495 496
  static GType clock_type = 0;

  if (!clock_type) {
    static const GTypeInfo clock_info = {
497
      sizeof (GstClockClass),
498 499 500 501 502
      NULL,
      NULL,
      (GClassInitFunc) gst_clock_class_init,
      NULL,
      NULL,
503
      sizeof (GstClock),
504
      0,
505 506 507
      (GInstanceInitFunc) gst_clock_init,
      NULL
    };
508

509
    clock_type = g_type_register_static (GST_TYPE_OBJECT, "GstClock",
510
        &clock_info, G_TYPE_FLAG_ABSTRACT);
511 512 513
  }
  return clock_type;
}
514

515
static void
516
gst_clock_class_init (GstClockClass * klass)
517 518 519
{
  GObjectClass *gobject_class;
  GstObjectClass *gstobject_class;
520

521 522
  gobject_class = (GObjectClass *) klass;
  gstobject_class = (GstObjectClass *) klass;
523

524
  parent_class = g_type_class_ref (GST_TYPE_OBJECT);
525

Wim Taymans's avatar
Wim Taymans committed
526 527 528
  if (!g_thread_supported ())
    g_thread_init (NULL);

Wim Taymans's avatar
Wim Taymans committed
529
#ifndef GST_DISABLE_TRACE
530 531
  _gst_clock_entry_trace =
      gst_alloc_trace_register (GST_CLOCK_ENTRY_TRACE_NAME);
Wim Taymans's avatar
Wim Taymans committed
532
#endif
533

Wim Taymans's avatar
Wim Taymans committed
534
  gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_clock_finalize);
535 536 537
  gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_clock_set_property);
  gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_clock_get_property);

538
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_STATS,
539
      g_param_spec_boolean ("stats", "Stats", "Enable clock stats",
540 541 542 543 544 545 546 547 548 549 550 551 552 553
          DEFAULT_STATS, G_PARAM_READWRITE));
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_WINDOW_SIZE,
      g_param_spec_int ("window-size", "Window size",
          "The size of the window used to calculate rate and offset", 2, 1024,
          DEFAULT_WINDOW_SIZE, G_PARAM_READWRITE));
  g_object_class_install_property (G_OBJECT_CLASS (klass),
      PROP_WINDOW_THRESHOLD, g_param_spec_int ("window-threshold",
          "Window threshold",
          "The threshold to start calculating rate and offset", 2, 1024,
          DEFAULT_WINDOW_THRESHOLD, G_PARAM_READWRITE));
  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TIMEOUT,
      g_param_spec_uint64 ("timeout", "Timeout",
          "The amount of time, in nanoseconds, to sample master and slave clocks",
          0, G_MAXUINT64, DEFAULT_TIMEOUT, G_PARAM_READWRITE));
Wim Taymans's avatar
Wim Taymans committed
554 555
}

556
static void
557
gst_clock_init (GstClock * clock)
Wim Taymans's avatar
Wim Taymans committed
558
{
559 560
  clock->last_time = 0;
  clock->entries = NULL;
561
  clock->entries_changed = g_cond_new ();
562
  clock->stats = FALSE;
563

564 565 566
  clock->internal_calibration = 0;
  clock->external_calibration = 0;
  clock->rate = 1.0;
567 568 569 570 571 572
  clock->filling = TRUE;
  clock->window_size = DEFAULT_WINDOW_SIZE;
  clock->window_threshold = DEFAULT_WINDOW_THRESHOLD;
  clock->time_index = 0;
  clock->timeout = DEFAULT_TIMEOUT;
  clock->times = g_new0 (GstClockTime, 4 * clock->window_size);
Wim Taymans's avatar
Wim Taymans committed
573 574
}

Wim Taymans's avatar
Wim Taymans committed
575
static void
Wim Taymans's avatar
Wim Taymans committed
576
gst_clock_finalize (GObject * object)
Wim Taymans's avatar
Wim Taymans committed
577 578 579
{
  GstClock *clock = GST_CLOCK (object);

580 581 582 583 584 585 586
  GST_OBJECT_LOCK (clock);
  if (clock->clockid) {
    gst_clock_id_unschedule (clock->clockid);
    gst_clock_id_unref (clock->clockid);
    clock->clockid = NULL;
  }

587
  g_cond_free (clock->entries_changed);
Wim Taymans's avatar
Wim Taymans committed
588

589 590 591 592
  g_free (clock->times);
  clock->times = NULL;
  GST_OBJECT_UNLOCK (clock);

Wim Taymans's avatar
Wim Taymans committed
593
  G_OBJECT_CLASS (parent_class)->finalize (object);
Wim Taymans's avatar
Wim Taymans committed
594 595
}

Wim Taymans's avatar
Wim Taymans committed
596
/**
597
 * gst_clock_set_resolution
598
 * @clock: a #GstClock
599
 * @resolution: The resolution to set
Wim Taymans's avatar
Wim Taymans committed
600
 *
601 602 603
 * Set the accuracy of the clock.
 *
 * Returns: the new resolution of the clock.
Wim Taymans's avatar
Wim Taymans committed
604
 */
605 606
GstClockTime
gst_clock_set_resolution (GstClock * clock, GstClockTime resolution)
Wim Taymans's avatar
Wim Taymans committed
607
{
Wim Taymans's avatar
Wim Taymans committed
608 609
  GstClockClass *cclass;

610 611
  g_return_val_if_fail (GST_IS_CLOCK (clock), 0);
  g_return_val_if_fail (resolution != 0, 0);
612

Wim Taymans's avatar
Wim Taymans committed
613 614 615
  cclass = GST_CLOCK_GET_CLASS (clock);

  if (cclass->change_resolution)
616
    clock->resolution =
617
        cclass->change_resolution (clock, clock->resolution, resolution);
618

619 620
  return clock->resolution;
}
621

622 623
/**
 * gst_clock_get_resolution
624
 * @clock: a #GstClock
625 626 627
 *
 * Get the accuracy of the clock.
 *
628
 * Returns: the resolution of the clock in units of #GstClockTime.
629 630
 *
 * MT safe.
631
 */
632
GstClockTime
633
gst_clock_get_resolution (GstClock * clock)
634
{
Wim Taymans's avatar
Wim Taymans committed
635 636
  GstClockClass *cclass;

637
  g_return_val_if_fail (GST_IS_CLOCK (clock), 0);
638

Wim Taymans's avatar
Wim Taymans committed
639 640 641 642
  cclass = GST_CLOCK_GET_CLASS (clock);

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

644
  return 1;
Wim Taymans's avatar
Wim Taymans committed
645 646
}

Wim Taymans's avatar
Wim Taymans committed
647
/**
648 649 650
 * gst_clock_adjust_unlocked
 * @clock: a #GstClock to use
 * @internal: a clock time
Wim Taymans's avatar
Wim Taymans committed
651
 *
652
 * Converts the given @internal clock time to the real time, adjusting for the
653 654 655
 * 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 LOCK held and is mainly used by clock subclasses.
Wim Taymans's avatar
Wim Taymans committed
656
 *
657
 * Returns: the converted time of the clock.
658
 *
659
 * MT safe.
660
 */
661 662
GstClockTime
gst_clock_adjust_unlocked (GstClock * clock, GstClockTime internal)
663
{
664
  GstClockTime ret;
665

666 667
  ret = (internal - clock->internal_calibration) * clock->rate;
  ret += clock->external_calibration;
668 669 670 671 672

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

  return clock->last_time;
673 674
}

Wim Taymans's avatar
Wim Taymans committed
675
/**
676
 * gst_clock_get_internal_time
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
677
 * @clock: a #GstClock to query
Wim Taymans's avatar
Wim Taymans committed
678
 *
679 680
 * Gets the current internal time of the given clock. The time is returned
 * unadjusted for the offset and the rate.
681
 *
682
 * Returns: the internal time of the clock. Or GST_CLOCK_TIME_NONE when
683 684 685
 * giving wrong input.
 *
 * MT safe.
Wim Taymans's avatar
Wim Taymans committed
686
 */
687
GstClockTime
688
gst_clock_get_internal_time (GstClock * clock)
689
{
690
  GstClockTime ret;
691
  GstClockClass *cclass;
692

693
  g_return_val_if_fail (GST_IS_CLOCK (clock), GST_CLOCK_TIME_NONE);
694

695
  cclass = GST_CLOCK_GET_CLASS (clock);
Wim Taymans's avatar
Wim Taymans committed
696

697
  if (cclass->get_internal_time) {
698
    ret = cclass->get_internal_time (clock);
699
  } else {
700
    ret = G_GINT64_CONSTANT (0);
701
  }
702
  GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "internal time %" GST_TIME_FORMAT,
703
      GST_TIME_ARGS (ret));
704

705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729
  return ret;
}

/**
 * gst_clock_get_time
 * @clock: a #GstClock to query
 *
 * Gets the current time of the given clock. The time is always
 * monotonically increasing and adjusted according to the current
 * offset and rate.
 *
 * Returns: the time of the clock. Or GST_CLOCK_TIME_NONE when
 * giving wrong input.
 *
 * MT safe.
 */
GstClockTime
gst_clock_get_time (GstClock * clock)
{
  GstClockTime ret;

  g_return_val_if_fail (GST_IS_CLOCK (clock), GST_CLOCK_TIME_NONE);

  ret = gst_clock_get_internal_time (clock);

730
  GST_OBJECT_LOCK (clock);
731
  /* this will scale for rate and offset */
732
  ret = gst_clock_adjust_unlocked (clock, ret);
733
  GST_OBJECT_UNLOCK (clock);
734

735
  GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock, "adjusted time %" GST_TIME_FORMAT,
736
      GST_TIME_ARGS (ret));
737

738
  return ret;
739 740
}

741 742 743 744 745 746 747 748 749 750 751 752 753 754 755
/**
 * gst_clock_set_calibration
 * @clock: a #GstClock to calibrate
 * @internal: a reference internal time
 * @external: a reference external time
 * @rate: the rate of the clock relative to its internal time
 *
 * Adjusts the rate and time of @clock. A @rate of 1.0 is the normal speed of
 * the clock. Values bigger than 1.0 make the clock go faster. @rate must be
 * positive.
 *
 * @internal and @external are calibration parameters that arrange that
 * gst_clock_get_time() should have been @external at internal time @internal.
 * This internal time should not be in the future; that is, it should be less
 * than the value of gst_clock_get_internal_time() when this function is called.
756 757 758 759 760
 *
 * Subsequent calls to gst_clock_get_time() will return clock times computed as
 * follows:
 *
 * <programlisting>
761
 *   time = (internal_time - @internal) * @rate + @external
762 763 764 765 766 767 768 769 770
 * </programlisting>
 *
 * Note that gst_clock_get_time() always returns increasing values so when you
 * move the clock backwards, gst_clock_get_time() will report the previous value
 * until the clock catches up.
 *
 * MT safe.
 */
void
771 772
gst_clock_set_calibration (GstClock * clock, GstClockTime internal, GstClockTime
    external, gdouble rate)
773 774 775
{
  g_return_if_fail (GST_IS_CLOCK (clock));
  g_return_if_fail (rate > 0.0);
776
  g_return_if_fail (internal <= gst_clock_get_internal_time (clock));
777

778
  GST_OBJECT_LOCK (clock);
779 780 781
  clock->internal_calibration = internal;
  clock->external_calibration = external;
  clock->rate = rate;
782
  GST_OBJECT_UNLOCK (clock);
783 784 785
}

/**
786 787 788 789
 * gst_clock_get_calibration
 * @clock: a #GstClock 
 * @internal: a location to store the internal time
 * @external: a location to store the external time
790 791
 * @rate: a location to store the rate
 *
792 793
 * Gets the internal rate and reference time of @clock. See
 * gst_clock_set_calibration() for more information.
794
 *
795 796 797
 * @internal, @external and @rate can be left NULL if the caller
 * is not interested in the values.
 *
798 799 800
 * MT safe.
 */
void
801 802
gst_clock_get_calibration (GstClock * clock, GstClockTime * internal,
    GstClockTime * external, gdouble * rate)
803 804 805
{
  g_return_if_fail (GST_IS_CLOCK (clock));

806
  GST_OBJECT_LOCK (clock);
807
  if (rate)
808
    *rate = clock->rate;
809
  if (external)
810
    *external = clock->external_calibration;
811
  if (internal)
812
    *internal = clock->internal_calibration;
813
  GST_OBJECT_UNLOCK (clock);
814 815
}

816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839
/* will be called repeadedly to sample the master and slave clock
 * to recalibrate the clock */
static gboolean
gst_clock_slave_callback (GstClock * master, GstClockTime time,
    GstClockID id, GstClock * clock)
{
  GstClockTime stime, mtime;
  gdouble r_squared;

  stime = gst_clock_get_internal_time (clock);
  mtime = gst_clock_get_time (master);

  GST_CAT_DEBUG_OBJECT (GST_CAT_CLOCK, clock,
      "master %" GST_TIME_FORMAT ", slave %" GST_TIME_FORMAT,
      GST_TIME_ARGS (mtime), GST_TIME_ARGS (stime));

  gst_clock_add_observation (clock, stime, mtime, &r_squared);

  /* FIXME, we can use the r_squared value to adjust the timeout
   * value of the clockid */

  return TRUE;
}

840 841 842 843 844 845 846 847 848 849 850 851
/**
 * gst_clock_set_master
 * @clock: a #GstClock 
 * @master: a master #GstClock 
 *
 * Set @master as the master clock for @clock. @clock will be automatically
 * calibrated so that gst_clock_get_time() reports the same time as the
 * master clock.  
 * 
 * A clock provider that slaves its clock to a master can get the current
 * calibration values with gst_clock_get_calibration().
 *
852 853
 * Returns: TRUE if the clock is capable of being slaved to a master clock.
 *
854 855
 * MT safe.
 */
856
gboolean
857 858
gst_clock_set_master (GstClock * clock, GstClock * master)
{
859 860
  g_return_val_if_fail (GST_IS_CLOCK (clock), FALSE);

861
  GST_OBJECT_LOCK (clock);
862 863 864 865 866
  /* we always allow setting the master to NULL */
  if (master && !GST_OBJECT_FLAG_IS_SET (clock, GST_CLOCK_FLAG_CAN_SET_MASTER))
    goto not_supported;

  GST_DEBUG_OBJECT (clock, "slaving to master clock %p", master);
867
  gst_object_replace ((GstObject **) & clock->master, (GstObject *) master);
868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883

  if (clock->clockid) {
    gst_clock_id_unschedule (clock->clockid);
    gst_clock_id_unref (clock->clockid);
    clock->clockid = NULL;
  }
  if (master) {
    clock->filling = TRUE;
    clock->time_index = 0;
    /* use the master periodic id to schedule sampling and
     * clock calibration. */
    clock->clockid = gst_clock_new_periodic_id (master,
        gst_clock_get_time (master), clock->timeout);
    gst_clock_id_wait_async (clock->clockid,
        (GstClockCallback) gst_clock_slave_callback, clock);
  }
884
  GST_OBJECT_UNLOCK (clock);
885 886 887 888 889 890 891 892

  return TRUE;

not_supported:
  {
    GST_DEBUG_OBJECT (clock, "cannot be slaved to a master clock");
    return FALSE;
  }
893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911
}

/**
 * gst_clock_get_master
 * @clock: a #GstClock 
 *
 * Get the master clock that @clock is slaved to or NULL when the clock is
 * not slaved to any master clock.
 *
 * Returns: a master #GstClock or NULL when this clock is not slaved to a master
 * clock. Unref after usage.
 *
 * MT safe.
 */
GstClock *
gst_clock_get_master (GstClock * clock)
{
  GstClock *result = NULL;

912 913
  g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);

914 915 916 917 918 919 920 921
  GST_OBJECT_LOCK (clock);
  if (clock->master)
    result = gst_object_ref (clock->master);
  GST_OBJECT_UNLOCK (clock);

  return result;
}

922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075
/* http://mathworld.wolfram.com/LeastSquaresFitting.html */
static gboolean
do_linear_regression (GstClock * clock, gdouble * m,
    GstClockTime * b, GstClockTime * xbase, gdouble * r_squared)
{
  GstClockTime *newx, *newy;
  GstClockTime xmin, ymin, xbar, ybar, xbar4, ybar4;
  GstClockTimeDiff sxx, sxy, syy;
  GstClockTime *x, *y;
  gint i, j;
  guint n;

  xbar = ybar = sxx = syy = sxy = 0;

  x = clock->times;
  y = clock->times + 2,
      n = clock->filling ? clock->time_index : clock->window_size;

#ifdef DEBUGGING_ENABLED
  DEBUG ("doing regression on:");
  for (i = 0; i < n; i++)
    DEBUG ("  %" G_GUINT64_FORMAT "  %" G_GUINT64_FORMAT, x[i], y[i]);
#endif

  xmin = ymin = G_MAXUINT64;
  for (i = j = 0; i < n; i++, j += 4) {
    xmin = MIN (xmin, x[j]);
    ymin = MIN (ymin, y[j]);
  }

  DEBUG ("min x: %" G_GUINT64_FORMAT, xmin);
  DEBUG ("min y: %" G_GUINT64_FORMAT, ymin);

  newx = clock->times + 1;
  newy = clock->times + 3;

  /* strip off unnecessary bits of precision */
  for (i = j = 0; i < n; i++, j += 4) {
    newx[j] = x[j] - xmin;
    newy[j] = y[j] - ymin;
  }

#ifdef DEBUGGING_ENABLED
  DEBUG ("reduced numbers:");
  for (i = j = 0; i < n; i++, j += 4)
    DEBUG ("  %" G_GUINT64_FORMAT "  %" G_GUINT64_FORMAT, newx[j], newy[j]);
#endif

  /* have to do this precisely otherwise the results are pretty much useless.
   * should guarantee that none of these accumulators can overflow */

  /* quantities on the order of 1e10 -> 30 bits; window size a max of 2^10, so
     this addition could end up around 2^40 or so -- ample headroom */
  for (i = j = 0; i < n; i++, j += 4) {
    xbar += newx[j];
    ybar += newy[j];
  }
  xbar /= n;
  ybar /= n;

  DEBUG ("  xbar  = %" G_GUINT64_FORMAT, xbar);
  DEBUG ("  ybar  = %" G_GUINT64_FORMAT, ybar);

  /* multiplying directly would give quantities on the order of 1e20 -> 60 bits;
     times the window size that's 70 which is too much. Instead we (1) subtract
     off the xbar*ybar in the loop instead of after, to avoid accumulation; (2)
     shift off 4 bits from each multiplicand, giving an expected ceiling of 52
     bits, which should be enough. Need to check the incoming range and domain
     to ensure this is an appropriate loss of precision though. */
  xbar4 = xbar >> 4;
  ybar4 = ybar >> 4;
  for (i = j = 0; i < n; i++, j += 4) {
    GstClockTime newx4, newy4;

    newx4 = newx[j] >> 4;
    newy4 = newy[j] >> 4;

    sxx += newx4 * newx4 - xbar4 * xbar4;
    syy += newy4 * newy4 - ybar4 * ybar4;
    sxy += newx4 * newy4 - xbar4 * ybar4;
  }

  *m = ((double) sxy) / sxx;
  *xbase = xmin;
  *b = (ybar + ymin) - (GstClockTime) (xbar * *m);
  *r_squared = ((double) sxy * (double) sxy) / ((double) sxx * (double) syy);

  DEBUG ("  m      = %g", *m);
  DEBUG ("  b      = %" G_GUINT64_FORMAT, *b);
  DEBUG ("  xbase  = %" G_GUINT64_FORMAT, *xbase);
  DEBUG ("  r2     = %g", *r_squared);

  return TRUE;
}

/**
 * gst_clock_add_observation
 * @clock: a #GstClock 
 * @slave: an time on the slave
 * @master: an time on the master
 * @r_squared: a pointer to hold the result
 *
 * The time @master of the master clock and the time @slave of the slave
 * clock are added to the list of observations. If enough observations
 * are available, a linear regression algorithm is run on the
 * observations and @clock is recalibrated.
 *
 * This function should be called with @clock OBJECT_LOCK.
 *
 * Returns: TRUE if enough observations were added to run the 
 * regression algorithm.
 *
 * MT safe.
 */
gboolean
gst_clock_add_observation (GstClock * clock, GstClockTime slave,
    GstClockTime master, gdouble * r_squared)
{
  GstClockTime b, xbase;
  gdouble m;

  g_return_val_if_fail (GST_IS_CLOCK (clock), FALSE);
  g_return_val_if_fail (r_squared != NULL, FALSE);

  clock->times[(4 * clock->time_index)] = slave;
  clock->times[(4 * clock->time_index) + 2] = master;

  clock->time_index++;
  if (clock->time_index == clock->window_size) {
    clock->filling = FALSE;
    clock->time_index = 0;
  }

  if (clock->filling && clock->time_index < clock->window_threshold)
    goto filling;

  do_linear_regression (clock, &m, &b, &xbase, r_squared);

  GST_CAT_LOG_OBJECT (GST_CAT_CLOCK, clock,
      "adjusting clock to m=%g, b=%" G_GINT64_FORMAT " (rsquared=%g)", m, b,
      *r_squared);

  clock->internal_calibration = xbase;
  clock->external_calibration = b;
  clock->rate = m;

  return TRUE;

filling:
  {
    return FALSE;
  }
}

1076
static void
1077
gst_clock_update_stats (GstClock * clock)
1078
{
1079 1080
}

1081
static void
1082 1083
gst_clock_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
1084
{
1085
  GstClock *clock;
1086

1087 1088 1089
  clock = GST_CLOCK (object);

  switch (prop_id) {
1090 1091
    case PROP_STATS:
      GST_OBJECT_LOCK (clock);
1092
      clock->stats = g_value_get_boolean (value);
1093
      GST_OBJECT_UNLOCK (clock);
1094
      g_object_notify (object, "stats");
1095
      break;
1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115
    case PROP_WINDOW_SIZE:
      GST_OBJECT_LOCK (clock);
      clock->window_size = g_value_get_int (value);
      clock->window_threshold =
          MIN (clock->window_threshold, clock->window_size);
      clock->times =
          g_renew (GstClockTime, clock->times, 4 * clock->window_size);
      GST_OBJECT_UNLOCK (clock);
      break;
    case PROP_WINDOW_THRESHOLD:
      GST_OBJECT_LOCK (clock);
      clock->window_threshold =
          MIN (g_value_get_int (value), clock->window_size);
      GST_OBJECT_UNLOCK (clock);
      break;
    case PROP_TIMEOUT:
      GST_OBJECT_LOCK (clock);
      clock->timeout = g_value_get_uint64 (value);
      GST_OBJECT_UNLOCK (clock);
      break;
1116 1117 1118
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
1119
  }
Wim Taymans's avatar
Wim Taymans committed
1120
}
1121

1122
static void