gstclock.c 21.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
#define DEFAULT_EVENT_DIFF	(GST_SECOND)
102 103
#define DEFAULT_MAX_DIFF	(2 * GST_SECOND)

104 105
enum
{
106 107
  ARG_0,
  ARG_STATS,
108 109
  ARG_MAX_DIFF,
  ARG_EVENT_DIFF
110 111
};

112 113
static void gst_clock_class_init (GstClockClass * klass);
static void gst_clock_init (GstClock * clock);
Wim Taymans's avatar
Wim Taymans committed
114
static void gst_clock_finalize (GObject * object);
Wim Taymans's avatar
Wim Taymans committed
115

116 117 118 119 120
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);
121

122

123
static GstObjectClass *parent_class = NULL;
124

125 126
/* static guint gst_clock_signals[LAST_SIGNAL] = { 0 }; */

Wim Taymans's avatar
Wim Taymans committed
127
static GstClockID
128 129
gst_clock_entry_new (GstClock * clock, GstClockTime time,
    GstClockTime interval, GstClockEntryType type)
130 131
{
  GstClockEntry *entry;
132

133
  entry = g_malloc0 (sizeof (GstClockEntry));
Wim Taymans's avatar
Wim Taymans committed
134
#ifndef GST_DISABLE_TRACE
Wim Taymans's avatar
Wim Taymans committed
135
  gst_alloc_trace_new (_gst_clock_entry_trace, entry);
Wim Taymans's avatar
Wim Taymans committed
136
#endif
137 138
  GST_CAT_DEBUG (GST_CAT_CLOCK, "created entry %p, time %" GST_TIME_FORMAT,
      entry, GST_TIME_ARGS (time));
139

140
  gst_atomic_int_set (&entry->refcount, 1);
141 142
  entry->clock = clock;
  entry->time = time;
143
  entry->interval = interval;
144
  entry->type = type;
145
  entry->status = GST_CLOCK_BUSY;
146

147 148
  return (GstClockID) entry;
}
149

150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
/**
 * 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);

165
  g_atomic_int_inc (&((GstClockEntry *) id)->refcount);
166 167 168 169 170 171 172 173 174 175 176 177 178 179

  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
180
  g_free (id);
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
}

/**
 * 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);

199
  zero = g_atomic_int_dec_and_test (&((GstClockEntry *) id)->refcount);
200 201 202 203 204 205
  /* if we ended up with the refcount at zero, free the id */
  if (zero) {
    _gst_clock_id_free (id);
  }
}

206 207 208 209 210
/**
 * 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
211
 * Get an ID from the given clock to trigger a single shot
212 213
 * notification at the requested time. The single shot id should be
 * unreffed after usage.
214 215
 *
 * Returns: An id that can be used to request the time notification.
216 217
 *
 * MT safe.
218 219
 */
GstClockID
220
gst_clock_new_single_shot_id (GstClock * clock, GstClockTime time)
221
{
222 223
  g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);

224 225
  return gst_clock_entry_new (clock,
      time, GST_CLOCK_TIME_NONE, GST_CLOCK_ENTRY_SINGLE);
226 227 228
}

/**
Wim Taymans's avatar
Wim Taymans committed
229
 * gst_clock_new_periodic_id
230 231 232 233 234 235
 * @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
236 237
 * will then be fired with the given interval. The id should be unreffed
 * after usage.
238 239
 *
 * Returns: An id that can be used to request the time notification.
240 241
 *
 * MT safe.
242 243
 */
GstClockID
244 245
gst_clock_new_periodic_id (GstClock * clock, GstClockTime start_time,
    GstClockTime interval)
246
{
247
  g_return_val_if_fail (GST_IS_CLOCK (clock), NULL);
248
  g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (start_time), NULL);
249 250
  g_return_val_if_fail (interval != 0, NULL);

251 252
  return gst_clock_entry_new (clock,
      start_time, interval, GST_CLOCK_ENTRY_PERIODIC);
253 254
}

255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
/**
 * 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;
}

285 286 287 288 289 290
/**
 * gst_clock_id_get_time
 * @id: The clockid to query
 *
 * Get the time of the clock ID
 *
291 292 293
 * Returns: the time of the given clock id.
 *
 * MT safe.
294 295 296 297 298 299
 */
GstClockTime
gst_clock_id_get_time (GstClockID id)
{
  g_return_val_if_fail (id != NULL, GST_CLOCK_TIME_NONE);

300
  return GST_CLOCK_ENTRY_TIME ((GstClockEntry *) id);
301 302 303 304 305 306 307 308 309
}


/**
 * 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
310
 * NULL.
311 312
 *
 * Returns: the result of the blocking wait.
313 314
 *
 * MT safe.
315 316
 */
GstClockReturn
317
gst_clock_id_wait (GstClockID id, GstClockTimeDiff * jitter)
318 319
{
  GstClockEntry *entry;
320
  GstClock *clock;
321
  GstClockReturn res;
322
  GstClockTime requested;
Wim Taymans's avatar
Wim Taymans committed
323
  GstClockClass *cclass;
324

325
  g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR);
326

327 328
  entry = (GstClockEntry *) id;
  requested = GST_CLOCK_ENTRY_TIME (entry);
329

330 331 332 333 334
  if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested)))
    goto invalid_time;

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

336
  clock = GST_CLOCK_ENTRY_CLOCK (entry);
Wim Taymans's avatar
Wim Taymans committed
337
  cclass = GST_CLOCK_GET_CLASS (clock);
338

339
  if (G_LIKELY (cclass->wait)) {
340

341 342 343
    GST_CAT_DEBUG (GST_CAT_CLOCK, "waiting on clock entry %p", id);
    res = cclass->wait (clock, entry);
    GST_CAT_DEBUG (GST_CAT_CLOCK, "done waiting entry %p", id);
344

345
    if (jitter) {
346 347
      GstClockTime now = gst_clock_get_time (clock);

348 349
      *jitter = now - requested;
    }
350 351 352
    if (entry->type == GST_CLOCK_ENTRY_PERIODIC) {
      entry->time += entry->interval;
    }
353 354 355 356

    if (clock->stats) {
      gst_clock_update_stats (clock);
    }
357 358
  } else {
    res = GST_CLOCK_UNSUPPORTED;
359 360
  }
  return res;
361 362 363 364 365 366 367 368 369 370 371 372

  /* ERRORS */
invalid_time:
  {
    GST_CAT_DEBUG (GST_CAT_CLOCK, "invalid time requested, returning _BADTIME");
    return GST_CLOCK_BADTIME;
  }
unscheduled:
  {
    GST_CAT_DEBUG (GST_CAT_CLOCK, "entry was unscheduled return _UNSCHEDULED");
    return GST_CLOCK_UNSCHEDULED;
  }
373 374 375
}

/**
Wim Taymans's avatar
Wim Taymans committed
376 377
 * gst_clock_id_wait_async:
 * @id: a #GstClockID to wait on
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
378
 * @func: The callback function
379 380
 * @user_data: User data passed in the calback
 *
Wim Taymans's avatar
Wim Taymans committed
381
 * Register a callback on the given clockid with the given
382 383 384 385
 * 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.
386
 *
387
 * Returns: the result of the non blocking wait.
388 389
 *
 * MT safe.
390 391 392
 */
GstClockReturn
gst_clock_id_wait_async (GstClockID id,
393
    GstClockCallback func, gpointer user_data)
394 395 396
{
  GstClockEntry *entry;
  GstClock *clock;
397
  GstClockReturn res;
Wim Taymans's avatar
Wim Taymans committed
398
  GstClockClass *cclass;
399
  GstClockTime requested;
400

401
  g_return_val_if_fail (id != NULL, GST_CLOCK_ERROR);
402
  g_return_val_if_fail (func != NULL, GST_CLOCK_ERROR);
403 404

  entry = (GstClockEntry *) id;
405 406
  requested = GST_CLOCK_ENTRY_TIME (entry);
  clock = GST_CLOCK_ENTRY_CLOCK (entry);
407

408 409 410 411 412
  if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (requested)))
    goto invalid_time;

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

Wim Taymans's avatar
Wim Taymans committed
414 415 416 417 418 419 420
  cclass = GST_CLOCK_GET_CLASS (clock);

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

    res = cclass->wait_async (clock, entry);
421 422
  } else {
    res = GST_CLOCK_UNSUPPORTED;
423 424 425
  }
  return res;

426 427 428 429 430 431 432 433 434 435 436 437
  /* ERRORS */
invalid_time:
  {
    (func) (clock, GST_CLOCK_TIME_NONE, id, user_data);
    GST_CAT_DEBUG (GST_CAT_CLOCK, "invalid time requested, returning _BADTIME");
    return GST_CLOCK_BADTIME;
  }
unscheduled:
  {
    GST_CAT_DEBUG (GST_CAT_CLOCK, "entry was unscheduled return _UNSCHEDULED");
    return GST_CLOCK_UNSCHEDULED;
  }
438 439
}

440
/**
Wim Taymans's avatar
Wim Taymans committed
441 442
 * gst_clock_id_unschedule:
 * @id: The id to unschedule
443
 *
444 445 446 447 448 449
 * 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.
450 451 452 453 454 455
 */
void
gst_clock_id_unschedule (GstClockID id)
{
  GstClockEntry *entry;
  GstClock *clock;
Wim Taymans's avatar
Wim Taymans committed
456
  GstClockClass *cclass;
457

458 459 460 461
  g_return_if_fail (id != NULL);

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

Wim Taymans's avatar
Wim Taymans committed
463 464 465 466
  cclass = GST_CLOCK_GET_CLASS (clock);

  if (cclass->unschedule)
    cclass->unschedule (clock, entry);
467 468
}

469 470 471 472

/**
 * GstClock abstract base class implementation
 */
473 474
GType
gst_clock_get_type (void)
Wim Taymans's avatar
Wim Taymans committed
475
{
476 477 478 479
  static GType clock_type = 0;

  if (!clock_type) {
    static const GTypeInfo clock_info = {
480
      sizeof (GstClockClass),
481 482 483 484 485
      NULL,
      NULL,
      (GClassInitFunc) gst_clock_class_init,
      NULL,
      NULL,
486
      sizeof (GstClock),
487
      0,
488 489 490
      (GInstanceInitFunc) gst_clock_init,
      NULL
    };
491

492
    clock_type = g_type_register_static (GST_TYPE_OBJECT, "GstClock",
493
        &clock_info, G_TYPE_FLAG_ABSTRACT);
494 495 496
  }
  return clock_type;
}
497

498
static void
499
gst_clock_class_init (GstClockClass * klass)
500 501 502
{
  GObjectClass *gobject_class;
  GstObjectClass *gstobject_class;
503

504 505
  gobject_class = (GObjectClass *) klass;
  gstobject_class = (GstObjectClass *) klass;
506

507
  parent_class = g_type_class_ref (GST_TYPE_OBJECT);
508

Wim Taymans's avatar
Wim Taymans committed
509 510 511
  if (!g_thread_supported ())
    g_thread_init (NULL);

Wim Taymans's avatar
Wim Taymans committed
512
#ifndef GST_DISABLE_TRACE
513 514
  _gst_clock_entry_trace =
      gst_alloc_trace_register (GST_CLOCK_ENTRY_TRACE_NAME);
Wim Taymans's avatar
Wim Taymans committed
515
#endif
516

Wim Taymans's avatar
Wim Taymans committed
517
  gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_clock_finalize);
518 519 520 521
  gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_clock_set_property);
  gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_clock_get_property);

  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_STATS,
522
      g_param_spec_boolean ("stats", "Stats", "Enable clock stats",
523
          FALSE, G_PARAM_READWRITE));
Wim Taymans's avatar
Wim Taymans committed
524 525
}

526
static void
527
gst_clock_init (GstClock * clock)
Wim Taymans's avatar
Wim Taymans committed
528
{
529 530
  clock->last_time = 0;
  clock->entries = NULL;
531
  clock->entries_changed = g_cond_new ();
532 533
  clock->flags = 0;
  clock->stats = FALSE;
534

535 536 537
  clock->internal_calibration = 0;
  clock->external_calibration = 0;
  clock->rate = 1.0;
Wim Taymans's avatar
Wim Taymans committed
538 539
}

Wim Taymans's avatar
Wim Taymans committed
540
static void
Wim Taymans's avatar
Wim Taymans committed
541
gst_clock_finalize (GObject * object)
Wim Taymans's avatar
Wim Taymans committed
542 543 544
{
  GstClock *clock = GST_CLOCK (object);

545
  g_cond_free (clock->entries_changed);
Wim Taymans's avatar
Wim Taymans committed
546

Wim Taymans's avatar
Wim Taymans committed
547
  G_OBJECT_CLASS (parent_class)->finalize (object);
Wim Taymans's avatar
Wim Taymans committed
548 549
}

Wim Taymans's avatar
Wim Taymans committed
550
/**
551 552 553
 * gst_clock_set_resolution
 * @clock: The clock set the resolution on
 * @resolution: The resolution to set
Wim Taymans's avatar
Wim Taymans committed
554
 *
555 556 557
 * Set the accuracy of the clock.
 *
 * Returns: the new resolution of the clock.
Wim Taymans's avatar
Wim Taymans committed
558
 */
559
guint64
560
gst_clock_set_resolution (GstClock * clock, guint64 resolution)
Wim Taymans's avatar
Wim Taymans committed
561
{
Wim Taymans's avatar
Wim Taymans committed
562 563
  GstClockClass *cclass;

Wim Taymans's avatar
Wim Taymans committed
564 565
  g_return_val_if_fail (GST_IS_CLOCK (clock), G_GINT64_CONSTANT (0));
  g_return_val_if_fail (resolution != 0, G_GINT64_CONSTANT (0));
566

Wim Taymans's avatar
Wim Taymans committed
567 568 569
  cclass = GST_CLOCK_GET_CLASS (clock);

  if (cclass->change_resolution)
570
    clock->resolution =
571
        cclass->change_resolution (clock, clock->resolution, resolution);
572

573 574
  return clock->resolution;
}
575

576 577 578 579 580 581 582
/**
 * gst_clock_get_resolution
 * @clock: The clock get the resolution of
 *
 * Get the accuracy of the clock.
 *
 * Returns: the resolution of the clock in microseconds.
583 584
 *
 * MT safe.
585 586
 */
guint64
587
gst_clock_get_resolution (GstClock * clock)
588
{
Wim Taymans's avatar
Wim Taymans committed
589 590
  GstClockClass *cclass;

Wim Taymans's avatar
Wim Taymans committed
591
  g_return_val_if_fail (GST_IS_CLOCK (clock), G_GINT64_CONSTANT (0));
592

Wim Taymans's avatar
Wim Taymans committed
593 594 595 596
  cclass = GST_CLOCK_GET_CLASS (clock);

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

Wim Taymans's avatar
Wim Taymans committed
598
  return G_GINT64_CONSTANT (1);
Wim Taymans's avatar
Wim Taymans committed
599 600
}

Wim Taymans's avatar
Wim Taymans committed
601
/**
602 603 604
 * gst_clock_adjust_unlocked
 * @clock: a #GstClock to use
 * @internal: a clock time
Wim Taymans's avatar
Wim Taymans committed
605
 *
606
 * Converts the given @internal clock time to the real time, adjusting for the
607 608 609
 * 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
610
 *
611
 * Returns: the converted time of the clock.
612
 *
613
 * MT safe.
614
 */
615 616
GstClockTime
gst_clock_adjust_unlocked (GstClock * clock, GstClockTime internal)
617
{
618
  GstClockTime ret;
619

620 621
  ret = (internal - clock->internal_calibration) * clock->rate;
  ret += clock->external_calibration;
622 623 624 625 626

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

  return clock->last_time;
627 628
}

Wim Taymans's avatar
Wim Taymans committed
629
/**
630
 * gst_clock_get_internal_time
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
631
 * @clock: a #GstClock to query
Wim Taymans's avatar
Wim Taymans committed
632
 *
633 634
 * Gets the current internal time of the given clock. The time is returned
 * unadjusted for the offset and the rate.
635
 *
636
 * Returns: the internal time of the clock. Or GST_CLOCK_TIME_NONE when
637 638 639
 * giving wrong input.
 *
 * MT safe.
Wim Taymans's avatar
Wim Taymans committed
640
 */
641
GstClockTime
642
gst_clock_get_internal_time (GstClock * clock)
643
{
644
  GstClockTime ret;
645
  GstClockClass *cclass;
646

647
  g_return_val_if_fail (GST_IS_CLOCK (clock), GST_CLOCK_TIME_NONE);
648

649
  cclass = GST_CLOCK_GET_CLASS (clock);
Wim Taymans's avatar
Wim Taymans committed
650

651
  if (cclass->get_internal_time) {
652
    ret = cclass->get_internal_time (clock);
653
  } else {
654
    ret = G_GINT64_CONSTANT (0);
655
  }
656 657
  GST_CAT_DEBUG (GST_CAT_CLOCK, "internal time %" GST_TIME_FORMAT,
      GST_TIME_ARGS (ret));
658

659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683
  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);

684
  GST_LOCK (clock);
685
  /* this will scale for rate and offset */
686 687
  ret = gst_clock_adjust_unlocked (clock, ret);
  GST_UNLOCK (clock);
688

689 690
  GST_CAT_DEBUG (GST_CAT_CLOCK, "adjusted time %" GST_TIME_FORMAT,
      GST_TIME_ARGS (ret));
691

692
  return ret;
693 694
}

695 696 697 698 699 700 701 702 703 704 705 706 707 708 709
/**
 * 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.
710 711 712 713 714
 *
 * Subsequent calls to gst_clock_get_time() will return clock times computed as
 * follows:
 *
 * <programlisting>
715
 *   time = (internal_time - @internal) * @rate + @external
716 717 718 719 720 721 722 723 724
 * </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
725 726
gst_clock_set_calibration (GstClock * clock, GstClockTime internal, GstClockTime
    external, gdouble rate)
727 728 729
{
  g_return_if_fail (GST_IS_CLOCK (clock));
  g_return_if_fail (rate > 0.0);
730
  g_return_if_fail (internal <= gst_clock_get_internal_time (clock));
731 732

  GST_LOCK (clock);
733 734 735
  clock->internal_calibration = internal;
  clock->external_calibration = external;
  clock->rate = rate;
736 737 738 739
  GST_UNLOCK (clock);
}

/**
740 741 742 743
 * gst_clock_get_calibration
 * @clock: a #GstClock 
 * @internal: a location to store the internal time
 * @external: a location to store the external time
744 745
 * @rate: a location to store the rate
 *
746 747
 * Gets the internal rate and reference time of @clock. See
 * gst_clock_set_calibration() for more information.
748
 *
749 750 751
 * @internal, @external and @rate can be left NULL if the caller
 * is not interested in the values.
 *
752 753 754
 * MT safe.
 */
void
755 756
gst_clock_get_calibration (GstClock * clock, GstClockTime * internal,
    GstClockTime * external, gdouble * rate)
757 758 759 760
{
  g_return_if_fail (GST_IS_CLOCK (clock));

  GST_LOCK (clock);
761
  if (rate)
762
    *rate = clock->rate;
763
  if (external)
764
    *external = clock->external_calibration;
765
  if (internal)
766
    *internal = clock->internal_calibration;
767 768 769
  GST_UNLOCK (clock);
}

770
static void
771
gst_clock_update_stats (GstClock * clock)
772
{
773 774
}

775
static void
776 777
gst_clock_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * pspec)
778
{
779
  GstClock *clock;
780

781 782 783 784 785
  clock = GST_CLOCK (object);

  switch (prop_id) {
    case ARG_STATS:
      clock->stats = g_value_get_boolean (value);
786
      g_object_notify (object, "stats");
787 788 789 790
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
791
  }
Wim Taymans's avatar
Wim Taymans committed
792
}
793

794
static void
795 796
gst_clock_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * pspec)
797
{
798
  GstClock *clock;
799

800 801 802 803 804 805 806 807 808
  clock = GST_CLOCK (object);

  switch (prop_id) {
    case ARG_STATS:
      g_value_set_boolean (value, clock->stats);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
809
  }
810
}