rtpjitterbuffer.c 37.5 KB
Newer Older
1
/* GStreamer
2
 * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
3 4 5 6 7 8 9 10 11 12 13 14 15
 *
 * 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
16 17
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
18 19
 */
#include <string.h>
20
#include <stdlib.h>
21 22 23 24 25 26 27 28 29

#include <gst/rtp/gstrtpbuffer.h>
#include <gst/rtp/gstrtcpbuffer.h>

#include "rtpjitterbuffer.h"

GST_DEBUG_CATEGORY_STATIC (rtp_jitter_buffer_debug);
#define GST_CAT_DEFAULT rtp_jitter_buffer_debug

30 31 32
#define MAX_WINDOW	RTP_JITTER_BUFFER_MAX_WINDOW
#define MAX_TIME	(2 * GST_SECOND)

33 34 35 36 37 38 39 40 41 42 43 44 45 46
/* signals and args */
enum
{
  LAST_SIGNAL
};

enum
{
  PROP_0
};

/* GObject vmethods */
static void rtp_jitter_buffer_finalize (GObject * object);

47 48 49 50 51 52 53 54 55
GType
rtp_jitter_buffer_mode_get_type (void)
{
  static GType jitter_buffer_mode_type = 0;
  static const GEnumValue jitter_buffer_modes[] = {
    {RTP_JITTER_BUFFER_MODE_NONE, "Only use RTP timestamps", "none"},
    {RTP_JITTER_BUFFER_MODE_SLAVE, "Slave receiver to sender clock", "slave"},
    {RTP_JITTER_BUFFER_MODE_BUFFER, "Do low/high watermark buffering",
        "buffer"},
56 57
    {RTP_JITTER_BUFFER_MODE_SYNCED, "Synchronized sender and receiver clocks",
        "synced"},
58 59 60 61 62 63 64 65 66 67
    {0, NULL, NULL},
  };

  if (!jitter_buffer_mode_type) {
    jitter_buffer_mode_type =
        g_enum_register_static ("RTPJitterBufferMode", jitter_buffer_modes);
  }
  return jitter_buffer_mode_type;
}

68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
/* static guint rtp_jitter_buffer_signals[LAST_SIGNAL] = { 0 }; */

G_DEFINE_TYPE (RTPJitterBuffer, rtp_jitter_buffer, G_TYPE_OBJECT);

static void
rtp_jitter_buffer_class_init (RTPJitterBufferClass * klass)
{
  GObjectClass *gobject_class;

  gobject_class = (GObjectClass *) klass;

  gobject_class->finalize = rtp_jitter_buffer_finalize;

  GST_DEBUG_CATEGORY_INIT (rtp_jitter_buffer_debug, "rtpjitterbuffer", 0,
      "RTP Jitter Buffer");
}

static void
rtp_jitter_buffer_init (RTPJitterBuffer * jbuf)
{
88 89
  g_mutex_init (&jbuf->clock_lock);

90
  jbuf->packets = g_queue_new ();
91
  jbuf->mode = RTP_JITTER_BUFFER_MODE_SLAVE;
92 93

  rtp_jitter_buffer_reset_skew (jbuf);
94 95 96 97 98 99 100 101 102
}

static void
rtp_jitter_buffer_finalize (GObject * object)
{
  RTPJitterBuffer *jbuf;

  jbuf = RTP_JITTER_BUFFER_CAST (object);

103 104 105 106 107 108 109 110 111
  if (jbuf->media_clock_synced_id)
    g_signal_handler_disconnect (jbuf->media_clock,
        jbuf->media_clock_synced_id);
  if (jbuf->media_clock)
    gst_object_unref (jbuf->media_clock);

  if (jbuf->pipeline_clock)
    gst_object_unref (jbuf->pipeline_clock);

112 113
  g_queue_free (jbuf->packets);

114 115
  g_mutex_clear (&jbuf->clock_lock);

116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
  G_OBJECT_CLASS (rtp_jitter_buffer_parent_class)->finalize (object);
}

/**
 * rtp_jitter_buffer_new:
 *
 * Create an #RTPJitterBuffer.
 *
 * Returns: a new #RTPJitterBuffer. Use g_object_unref() after usage.
 */
RTPJitterBuffer *
rtp_jitter_buffer_new (void)
{
  RTPJitterBuffer *jbuf;

  jbuf = g_object_new (RTP_TYPE_JITTER_BUFFER, NULL);

  return jbuf;
}

136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
/**
 * rtp_jitter_buffer_get_mode:
 * @jbuf: an #RTPJitterBuffer
 *
 * Get the current jitterbuffer mode.
 *
 * Returns: the current jitterbuffer mode.
 */
RTPJitterBufferMode
rtp_jitter_buffer_get_mode (RTPJitterBuffer * jbuf)
{
  return jbuf->mode;
}

/**
 * rtp_jitter_buffer_set_mode:
 * @jbuf: an #RTPJitterBuffer
 * @mode: a #RTPJitterBufferMode
 *
 * Set the buffering and clock slaving algorithm used in the @jbuf.
 */
void
rtp_jitter_buffer_set_mode (RTPJitterBuffer * jbuf, RTPJitterBufferMode mode)
{
  jbuf->mode = mode;
161
}
162

163 164 165 166 167 168 169 170 171 172 173
GstClockTime
rtp_jitter_buffer_get_delay (RTPJitterBuffer * jbuf)
{
  return jbuf->delay;
}

void
rtp_jitter_buffer_set_delay (RTPJitterBuffer * jbuf, GstClockTime delay)
{
  jbuf->delay = delay;
  jbuf->low_level = (delay * 15) / 100;
174 175
  /* the high level is at 90% in order to release packets before we fill up the
   * buffer up to the latency */
176
  jbuf->high_level = (delay * 90) / 100;
Wim Taymans's avatar
Wim Taymans committed
177

178
  GST_DEBUG ("delay %" GST_TIME_FORMAT ", min %" GST_TIME_FORMAT ", max %"
Wim Taymans's avatar
Wim Taymans committed
179
      GST_TIME_FORMAT, GST_TIME_ARGS (jbuf->delay),
180
      GST_TIME_ARGS (jbuf->low_level), GST_TIME_ARGS (jbuf->high_level));
181 182
}

183 184 185
/**
 * rtp_jitter_buffer_set_clock_rate:
 * @jbuf: an #RTPJitterBuffer
186
 * @clock_rate: the new clock rate
187 188 189 190 191 192 193
 *
 * Set the clock rate in the jitterbuffer.
 */
void
rtp_jitter_buffer_set_clock_rate (RTPJitterBuffer * jbuf, guint32 clock_rate)
{
  if (jbuf->clock_rate != clock_rate) {
194 195
    GST_DEBUG ("Clock rate changed from %" G_GUINT32_FORMAT " to %"
        G_GUINT32_FORMAT, jbuf->clock_rate, clock_rate);
196
    jbuf->clock_rate = clock_rate;
197
    rtp_jitter_buffer_reset_skew (jbuf);
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
  }
}

/**
 * rtp_jitter_buffer_get_clock_rate:
 * @jbuf: an #RTPJitterBuffer
 *
 * Get the currently configure clock rate in @jbuf.
 *
 * Returns: the current clock-rate
 */
guint32
rtp_jitter_buffer_get_clock_rate (RTPJitterBuffer * jbuf)
{
  return jbuf->clock_rate;
}
214

215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 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 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
static void
media_clock_synced_cb (GstClock * clock, gboolean synced,
    RTPJitterBuffer * jbuf)
{
  GstClockTime internal, external;

  g_mutex_lock (&jbuf->clock_lock);
  if (jbuf->pipeline_clock) {
    internal = gst_clock_get_internal_time (jbuf->media_clock);
    external = gst_clock_get_time (jbuf->pipeline_clock);

    gst_clock_set_calibration (jbuf->media_clock, internal, external, 1, 1);
  }
  g_mutex_unlock (&jbuf->clock_lock);
}

/**
 * rtp_jitter_buffer_set_media_clock:
 * @jbuf: an #RTPJitterBuffer
 * @clock: (transfer full): media #GstClock
 * @clock_offset: RTP time at clock epoch or -1
 *
 * Sets the media clock for the media and the clock offset
 *
 */
void
rtp_jitter_buffer_set_media_clock (RTPJitterBuffer * jbuf, GstClock * clock,
    guint64 clock_offset)
{
  g_mutex_lock (&jbuf->clock_lock);
  if (jbuf->media_clock) {
    if (jbuf->media_clock_synced_id)
      g_signal_handler_disconnect (jbuf->media_clock,
          jbuf->media_clock_synced_id);
    jbuf->media_clock_synced_id = 0;
    gst_object_unref (jbuf->media_clock);
  }
  jbuf->media_clock = clock;
  jbuf->media_clock_offset = clock_offset;

  if (jbuf->pipeline_clock && jbuf->media_clock &&
      jbuf->pipeline_clock != jbuf->media_clock) {
    jbuf->media_clock_synced_id =
        g_signal_connect (jbuf->media_clock, "synced",
        G_CALLBACK (media_clock_synced_cb), jbuf);
    if (gst_clock_is_synced (jbuf->media_clock)) {
      GstClockTime internal, external;

      internal = gst_clock_get_internal_time (jbuf->media_clock);
      external = gst_clock_get_time (jbuf->pipeline_clock);

      gst_clock_set_calibration (jbuf->media_clock, internal, external, 1, 1);
    }

    gst_clock_set_master (jbuf->media_clock, jbuf->pipeline_clock);
  }
  g_mutex_unlock (&jbuf->clock_lock);
}

/**
 * rtp_jitter_buffer_set_pipeline_clock:
 * @jbuf: an #RTPJitterBuffer
 * @clock: pipeline #GstClock
 *
 * Sets the pipeline clock
 *
 */
void
rtp_jitter_buffer_set_pipeline_clock (RTPJitterBuffer * jbuf, GstClock * clock)
{
  g_mutex_lock (&jbuf->clock_lock);
  if (jbuf->pipeline_clock)
    gst_object_unref (jbuf->pipeline_clock);
  jbuf->pipeline_clock = clock ? gst_object_ref (clock) : NULL;

  if (jbuf->pipeline_clock && jbuf->media_clock &&
      jbuf->pipeline_clock != jbuf->media_clock) {
    if (gst_clock_is_synced (jbuf->media_clock)) {
      GstClockTime internal, external;

      internal = gst_clock_get_internal_time (jbuf->media_clock);
      external = gst_clock_get_time (jbuf->pipeline_clock);

      gst_clock_set_calibration (jbuf->media_clock, internal, external, 1, 1);
    }

    gst_clock_set_master (jbuf->media_clock, jbuf->pipeline_clock);
  }
  g_mutex_unlock (&jbuf->clock_lock);
}

gboolean
rtp_jitter_buffer_get_rfc7273_sync (RTPJitterBuffer * jbuf)
{
  return jbuf->rfc7273_sync;
}

void
rtp_jitter_buffer_set_rfc7273_sync (RTPJitterBuffer * jbuf,
    gboolean rfc7273_sync)
{
  jbuf->rfc7273_sync = rfc7273_sync;
}

319 320 321 322 323 324
/**
 * rtp_jitter_buffer_reset_skew:
 * @jbuf: an #RTPJitterBuffer
 *
 * Reset the skew calculations in @jbuf.
 */
325 326 327 328 329
void
rtp_jitter_buffer_reset_skew (RTPJitterBuffer * jbuf)
{
  jbuf->base_time = -1;
  jbuf->base_rtptime = -1;
330
  jbuf->base_extrtp = -1;
331
  jbuf->media_clock_base_time = -1;
332
  jbuf->ext_rtptime = -1;
333
  jbuf->last_rtptime = -1;
334 335 336 337 338
  jbuf->window_pos = 0;
  jbuf->window_filling = TRUE;
  jbuf->window_min = 0;
  jbuf->skew = 0;
  jbuf->prev_send_diff = -1;
339
  jbuf->prev_out_time = -1;
340
  jbuf->need_resync = TRUE;
341

342
  GST_DEBUG ("reset skew correction");
343
}
344

345 346 347 348 349 350 351 352 353 354 355 356 357
/**
 * rtp_jitter_buffer_disable_buffering:
 * @jbuf: an #RTPJitterBuffer
 * @disabled: the new state
 *
 * Enable or disable buffering on @jbuf.
 */
void
rtp_jitter_buffer_disable_buffering (RTPJitterBuffer * jbuf, gboolean disabled)
{
  jbuf->buffering_disabled = disabled;
}

358 359 360 361 362
static void
rtp_jitter_buffer_resync (RTPJitterBuffer * jbuf, GstClockTime time,
    GstClockTime gstrtptime, guint64 ext_rtptime, gboolean reset_skew)
{
  jbuf->base_time = time;
363
  jbuf->media_clock_base_time = -1;
364 365 366 367 368 369 370 371 372 373 374
  jbuf->base_rtptime = gstrtptime;
  jbuf->base_extrtp = ext_rtptime;
  jbuf->prev_out_time = -1;
  jbuf->prev_send_diff = -1;
  if (reset_skew) {
    jbuf->window_filling = TRUE;
    jbuf->window_pos = 0;
    jbuf->window_min = 0;
    jbuf->window_size = 0;
    jbuf->skew = 0;
  }
375
  jbuf->need_resync = FALSE;
376 377
}

Wim Taymans's avatar
Wim Taymans committed
378 379
static guint64
get_buffer_level (RTPJitterBuffer * jbuf)
380
{
381
  RTPJitterBufferItem *high_buf = NULL, *low_buf = NULL;
Wim Taymans's avatar
Wim Taymans committed
382
  guint64 level;
383

Sangkyu Park's avatar
Sangkyu Park committed
384
  /* first buffer with timestamp */
385
  high_buf = (RTPJitterBufferItem *) g_queue_peek_tail_link (jbuf->packets);
386
  while (high_buf) {
387
    if (high_buf->dts != -1 || high_buf->pts != -1)
388 389
      break;

390
    high_buf = (RTPJitterBufferItem *) g_list_previous (high_buf);
391 392
  }

393
  low_buf = (RTPJitterBufferItem *) g_queue_peek_head_link (jbuf->packets);
394
  while (low_buf) {
395
    if (low_buf->dts != -1 || low_buf->pts != -1)
396 397
      break;

398
    low_buf = (RTPJitterBufferItem *) g_list_next (low_buf);
399
  }
400 401

  if (!high_buf || !low_buf || high_buf == low_buf) {
Wim Taymans's avatar
Wim Taymans committed
402
    level = 0;
403 404 405
  } else {
    guint64 high_ts, low_ts;

406 407
    high_ts = high_buf->dts != -1 ? high_buf->dts : high_buf->pts;
    low_ts = low_buf->dts != -1 ? low_buf->dts : low_buf->pts;
408 409

    if (high_ts > low_ts)
Wim Taymans's avatar
Wim Taymans committed
410
      level = high_ts - low_ts;
411
    else
Wim Taymans's avatar
Wim Taymans committed
412
      level = 0;
413 414 415 416 417

    GST_LOG_OBJECT (jbuf,
        "low %" GST_TIME_FORMAT " high %" GST_TIME_FORMAT " level %"
        G_GUINT64_FORMAT, GST_TIME_ARGS (low_ts), GST_TIME_ARGS (high_ts),
        level);
418
  }
Wim Taymans's avatar
Wim Taymans committed
419 420 421 422 423 424 425 426 427 428 429
  return level;
}

static void
update_buffer_level (RTPJitterBuffer * jbuf, gint * percent)
{
  gboolean post = FALSE;
  guint64 level;

  level = get_buffer_level (jbuf);
  GST_DEBUG ("buffer level %" GST_TIME_FORMAT, GST_TIME_ARGS (level));
430

431 432 433 434 435
  if (jbuf->buffering_disabled) {
    GST_DEBUG ("buffering is disabled");
    level = jbuf->high_level;
  }

436 437
  if (jbuf->buffering) {
    post = TRUE;
438
    if (level >= jbuf->high_level) {
439 440 441 442
      GST_DEBUG ("buffering finished");
      jbuf->buffering = FALSE;
    }
  } else {
Wim Taymans's avatar
Wim Taymans committed
443
    if (level < jbuf->low_level) {
444 445 446 447 448 449
      GST_DEBUG ("buffering started");
      jbuf->buffering = TRUE;
      post = TRUE;
    }
  }
  if (post) {
Wim Taymans's avatar
Wim Taymans committed
450
    gint perc;
451

452
    if (jbuf->buffering && (jbuf->high_level != 0)) {
Wim Taymans's avatar
Wim Taymans committed
453
      perc = (level * 100 / jbuf->high_level);
Wim Taymans's avatar
Wim Taymans committed
454
      perc = MIN (perc, 100);
455
    } else {
Wim Taymans's avatar
Wim Taymans committed
456
      perc = 100;
457
    }
458

Wim Taymans's avatar
Wim Taymans committed
459 460
    if (percent)
      *percent = perc;
461

Wim Taymans's avatar
Wim Taymans committed
462
    GST_DEBUG ("buffering %d", perc);
463 464 465
  }
}

466
/* For the clock skew we use a windowed low point averaging algorithm as can be
467 468 469 470 471 472
 * found in Fober, Orlarey and Letz, 2005, "Real Time Clock Skew Estimation
 * over Network Delays":
 * http://www.grame.fr/Ressources/pub/TR-050601.pdf
 * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.102.1546
 *
 * The idea is that the jitter is composed of:
473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
 *
 *  J = N + n
 *
 *   N   : a constant network delay.
 *   n   : random added noise. The noise is concentrated around 0
 *
 * In the receiver we can track the elapsed time at the sender with:
 *
 *  send_diff(i) = (Tsi - Ts0);
 *
 *   Tsi : The time at the sender at packet i
 *   Ts0 : The time at the sender at the first packet
 *
 * This is the difference between the RTP timestamp in the first received packet
 * and the current packet.
 *
 * At the receiver we have to deal with the jitter introduced by the network.
 *
 *  recv_diff(i) = (Tri - Tr0)
 *
 *   Tri : The time at the receiver at packet i
 *   Tr0 : The time at the receiver at the first packet
 *
 * Both of these values contain a jitter Ji, a jitter for packet i, so we can
 * write:
 *
 *  recv_diff(i) = (Cri + D + ni) - (Cr0 + D + n0))
 *
 *    Cri    : The time of the clock at the receiver for packet i
 *    D + ni : The jitter when receiving packet i
 *
 * We see that the network delay is irrelevant here as we can elliminate D:
 *
 *  recv_diff(i) = (Cri + ni) - (Cr0 + n0))
 *
 * The drift is now expressed as:
 *
 *  Drift(i) = recv_diff(i) - send_diff(i);
 *
 * We now keep the W latest values of Drift and find the minimum (this is the
 * one with the lowest network jitter and thus the one which is least affected
 * by it). We average this lowest value to smooth out the resulting network skew.
 *
 * Both the window and the weighting used for averaging influence the accuracy
 * of the drift estimation. Finding the correct parameters turns out to be a
518
 * compromise between accuracy and inertia.
519 520 521 522
 *
 * We use a 2 second window or up to 512 data points, which is statistically big
 * enough to catch spikes (FIXME, detect spikes).
 * We also use a rather large weighting factor (125) to smoothly adapt. During
Wim Taymans's avatar
Wim Taymans committed
523
 * startup, when filling the window, we use a parabolic weighting factor, the
524 525 526
 * more the window is filled, the faster we move to the detected possible skew.
 *
 * Returns: @time adjusted with the clock skew.
527
 */
528
static GstClockTime
529 530
calculate_skew (RTPJitterBuffer * jbuf, guint64 ext_rtptime,
    GstClockTime gstrtptime, GstClockTime time)
531 532 533 534 535
{
  guint64 send_diff, recv_diff;
  gint64 delta;
  gint64 old;
  gint pos, i;
536
  GstClockTime out_time;
537
  guint64 slope;
538

539 540
  /* elapsed time at sender */
  send_diff = gstrtptime - jbuf->base_rtptime;
541

542 543
  /* we don't have an arrival timestamp so we can't do skew detection. we
   * should still apply a timestamp based on RTP timestamp and base_time */
544
  if (time == -1 || jbuf->base_time == -1)
545 546
    goto no_skew;

547 548 549 550 551 552
  /* elapsed time at receiver, includes the jitter */
  recv_diff = time - jbuf->base_time;

  /* measure the diff */
  delta = ((gint64) recv_diff) - ((gint64) send_diff);

553 554 555 556 557 558 559 560 561 562 563 564
  /* measure the slope, this gives a rought estimate between the sender speed
   * and the receiver speed. This should be approximately 8, higher values
   * indicate a burst (especially when the connection starts) */
  if (recv_diff > 0)
    slope = (send_diff * 8) / recv_diff;
  else
    slope = 8;

  GST_DEBUG ("time %" GST_TIME_FORMAT ", base %" GST_TIME_FORMAT ", recv_diff %"
      GST_TIME_FORMAT ", slope %" G_GUINT64_FORMAT, GST_TIME_ARGS (time),
      GST_TIME_ARGS (jbuf->base_time), GST_TIME_ARGS (recv_diff), slope);

565 566 567 568
  /* if the difference between the sender timeline and the receiver timeline
   * changed too quickly we have to resync because the server likely restarted
   * its timestamps. */
  if (ABS (delta - jbuf->skew) > GST_SECOND) {
569
    GST_WARNING ("delta - skew: %" GST_TIME_FORMAT " too big, reset skew",
570
        GST_TIME_ARGS (ABS (delta - jbuf->skew)));
571
    rtp_jitter_buffer_resync (jbuf, time, gstrtptime, ext_rtptime, TRUE);
572 573 574 575
    send_diff = 0;
    delta = 0;
  }

576 577
  pos = jbuf->window_pos;

578
  if (G_UNLIKELY (jbuf->window_filling)) {
579
    /* we are filling the window */
580
    GST_DEBUG ("filling %d, delta %" G_GINT64_FORMAT, pos, delta);
581 582
    jbuf->window[pos++] = delta;
    /* calc the min delta we observed */
583
    if (G_UNLIKELY (pos == 1 || delta < jbuf->window_min))
584 585
      jbuf->window_min = delta;

586
    if (G_UNLIKELY (send_diff >= MAX_TIME || pos >= MAX_WINDOW)) {
587 588
      jbuf->window_size = pos;

589
      /* window filled */
590 591
      GST_DEBUG ("min %" G_GINT64_FORMAT, jbuf->window_min);

592
      /* the skew is now the min */
593 594
      jbuf->skew = jbuf->window_min;
      jbuf->window_filling = FALSE;
595
    } else {
596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611
      gint perc_time, perc_window, perc;

      /* figure out how much we filled the window, this depends on the amount of
       * time we have or the max number of points we keep. */
      perc_time = send_diff * 100 / MAX_TIME;
      perc_window = pos * 100 / MAX_WINDOW;
      perc = MAX (perc_time, perc_window);

      /* make a parabolic function, the closer we get to the MAX, the more value
       * we give to the scaling factor of the new value */
      perc = perc * perc;

      /* quickly go to the min value when we are filling up, slowly when we are
       * just starting because we're not sure it's a good value yet. */
      jbuf->skew =
          (perc * jbuf->window_min + ((10000 - perc) * jbuf->skew)) / 10000;
612
      jbuf->window_size = pos + 1;
613 614 615 616 617 618 619
    }
  } else {
    /* pick old value and store new value. We keep the previous value in order
     * to quickly check if the min of the window changed */
    old = jbuf->window[pos];
    jbuf->window[pos++] = delta;

620
    if (G_UNLIKELY (delta <= jbuf->window_min)) {
621 622 623
      /* if the new value we inserted is smaller or equal to the current min,
       * it becomes the new min */
      jbuf->window_min = delta;
624
    } else if (G_UNLIKELY (old == jbuf->window_min)) {
625 626 627
      gint64 min = G_MAXINT64;

      /* if we removed the old min, we have to find a new min */
628
      for (i = 0; i < jbuf->window_size; i++) {
629 630 631 632 633 634 635 636 637 638 639
        /* we found another value equal to the old min, we can stop searching now */
        if (jbuf->window[i] == old) {
          min = old;
          break;
        }
        if (jbuf->window[i] < min)
          min = jbuf->window[i];
      }
      jbuf->window_min = min;
    }
    /* average the min values */
640
    jbuf->skew = (jbuf->window_min + (124 * jbuf->skew)) / 125;
641 642
    GST_DEBUG ("delta %" G_GINT64_FORMAT ", new min: %" G_GINT64_FORMAT,
        delta, jbuf->window_min);
643 644
  }
  /* wrap around in the window */
645
  if (G_UNLIKELY (pos >= jbuf->window_size))
646 647
    pos = 0;
  jbuf->window_pos = pos;
648

649
no_skew:
650 651
  /* the output time is defined as the base timestamp plus the RTP time
   * adjusted for the clock skew .*/
652
  if (jbuf->base_time != -1) {
653 654 655 656 657 658 659
    out_time = jbuf->base_time + send_diff;
    /* skew can be negative and we don't want to make invalid timestamps */
    if (jbuf->skew < 0 && out_time < -jbuf->skew) {
      out_time = 0;
    } else {
      out_time += jbuf->skew;
    }
660
  } else
661
    out_time = -1;
662

663
  GST_DEBUG ("skew %" G_GINT64_FORMAT ", out %" GST_TIME_FORMAT,
664 665 666
      jbuf->skew, GST_TIME_ARGS (out_time));

  return out_time;
667 668
}

669 670 671 672 673
static void
queue_do_insert (RTPJitterBuffer * jbuf, GList * list, GList * item)
{
  GQueue *queue = jbuf->packets;

674
  /* It's more likely that the packet was inserted at the tail of the queue */
675
  if (G_LIKELY (list)) {
676 677 678
    item->prev = list;
    item->next = list->next;
    list->next = item;
679
  } else {
680 681 682
    item->prev = NULL;
    item->next = queue->head;
    queue->head = item;
683
  }
684 685 686 687
  if (item->next)
    item->next->prev = item;
  else
    queue->tail = item;
688 689 690
  queue->length++;
}

691 692
GstClockTime
rtp_jitter_buffer_calculate_pts (RTPJitterBuffer * jbuf, GstClockTime dts,
693
    gboolean estimated_dts, guint32 rtptime, GstClockTime base_time)
694
{
695
  guint64 ext_rtptime;
696
  GstClockTime gstrtptime, pts;
697 698 699
  GstClock *media_clock, *pipeline_clock;
  guint64 media_clock_offset;
  gboolean rfc7273_mode;
700

701
  /* rtp time jumps are checked for during skew calculation, but bypassed
702 703 704 705
   * in other mode, so mind those here and reset jb if needed.
   * Only reset if valid input time, which is likely for UDP input
   * where we expect this might happen due to async thread effects
   * (in seek and state change cycles), but not so much for TCP input */
706
  if (GST_CLOCK_TIME_IS_VALID (dts) && !estimated_dts &&
707
      jbuf->mode != RTP_JITTER_BUFFER_MODE_SLAVE &&
708 709 710 711
      jbuf->base_time != -1 && jbuf->last_rtptime != -1) {
    GstClockTime ext_rtptime = jbuf->ext_rtptime;

    ext_rtptime = gst_rtp_buffer_ext_timestamp (&ext_rtptime, rtptime);
712 713
    if (ext_rtptime > jbuf->last_rtptime + 3 * jbuf->clock_rate ||
        ext_rtptime + 3 * jbuf->clock_rate < jbuf->last_rtptime) {
714 715 716 717 718 719 720
      /* reset even if we don't have valid incoming time;
       * still better than producing possibly very bogus output timestamp */
      GST_WARNING ("rtp delta too big, reset skew");
      rtp_jitter_buffer_reset_skew (jbuf);
    }
  }

721 722 723
  /* Return the last time if we got the same RTP timestamp again */
  ext_rtptime = gst_rtp_buffer_ext_timestamp (&jbuf->ext_rtptime, rtptime);
  if (jbuf->last_rtptime != -1 && ext_rtptime == jbuf->last_rtptime) {
724
    return jbuf->prev_out_time;
725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750
  }

  /* keep track of the last extended rtptime */
  jbuf->last_rtptime = ext_rtptime;

  g_mutex_lock (&jbuf->clock_lock);
  media_clock = jbuf->media_clock ? gst_object_ref (jbuf->media_clock) : NULL;
  pipeline_clock =
      jbuf->pipeline_clock ? gst_object_ref (jbuf->pipeline_clock) : NULL;
  media_clock_offset = jbuf->media_clock_offset;
  g_mutex_unlock (&jbuf->clock_lock);

  gstrtptime =
      gst_util_uint64_scale_int (ext_rtptime, GST_SECOND, jbuf->clock_rate);

  if (G_LIKELY (jbuf->base_rtptime != -1)) {
    /* check elapsed time in RTP units */
    if (gstrtptime < jbuf->base_rtptime) {
      /* elapsed time at sender, timestamps can go backwards and thus be
       * smaller than our base time, schedule to take a new base time in
       * that case. */
      GST_WARNING ("backward timestamps at server, schedule resync");
      jbuf->need_resync = TRUE;
    }
  }

751 752
  switch (jbuf->mode) {
    case RTP_JITTER_BUFFER_MODE_NONE:
753
    case RTP_JITTER_BUFFER_MODE_BUFFER:
754
      /* send 0 as the first timestamp and -1 for the other ones. This will
Sangkyu Park's avatar
Sangkyu Park committed
755
       * interpolate them from the RTP timestamps with a 0 origin. In buffering
756 757
       * mode we will adjust the outgoing timestamps according to the amount of
       * time we spent buffering. */
758
      if (jbuf->base_time == -1)
759
        dts = 0;
760
      else
761
        dts = -1;
762
      break;
763 764 765
    case RTP_JITTER_BUFFER_MODE_SYNCED:
      /* synchronized clocks, take first timestamp as base, use RTP timestamps
       * to interpolate */
766
      if (jbuf->base_time != -1 && !jbuf->need_resync)
767 768
        dts = -1;
      break;
769
    case RTP_JITTER_BUFFER_MODE_SLAVE:
770 771 772
    default:
      break;
  }
773 774 775 776 777

  /* need resync, lock on to time and gstrtptime if we can, otherwise we
   * do with the previous values */
  if (G_UNLIKELY (jbuf->need_resync && dts != -1)) {
    GST_INFO ("resync to time %" GST_TIME_FORMAT ", rtptime %"
778
        GST_TIME_FORMAT, GST_TIME_ARGS (dts), GST_TIME_ARGS (gstrtptime));
779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831
    rtp_jitter_buffer_resync (jbuf, dts, gstrtptime, ext_rtptime, FALSE);
  }

  GST_DEBUG ("extrtp %" G_GUINT64_FORMAT ", gstrtp %" GST_TIME_FORMAT ", base %"
      GST_TIME_FORMAT ", send_diff %" GST_TIME_FORMAT, ext_rtptime,
      GST_TIME_ARGS (gstrtptime), GST_TIME_ARGS (jbuf->base_rtptime),
      GST_TIME_ARGS (gstrtptime - jbuf->base_rtptime));

  rfc7273_mode = media_clock && pipeline_clock
      && gst_clock_is_synced (media_clock);

  if (rfc7273_mode && jbuf->mode == RTP_JITTER_BUFFER_MODE_SLAVE
      && (media_clock_offset == -1 || !jbuf->rfc7273_sync)) {
    GstClockTime internal, external;
    GstClockTime rate_num, rate_denom;
    GstClockTime nsrtptimediff, rtpntptime, rtpsystime;

    gst_clock_get_calibration (media_clock, &internal, &external, &rate_num,
        &rate_denom);

    /* Slave to the RFC7273 media clock instead of trying to estimate it
     * based on receive times and RTP timestamps */

    if (jbuf->media_clock_base_time == -1) {
      if (jbuf->base_time != -1) {
        jbuf->media_clock_base_time =
            gst_clock_unadjust_with_calibration (media_clock,
            jbuf->base_time + base_time, internal, external, rate_num,
            rate_denom);
      } else {
        if (dts != -1)
          jbuf->media_clock_base_time =
              gst_clock_unadjust_with_calibration (media_clock, dts + base_time,
              internal, external, rate_num, rate_denom);
        else
          jbuf->media_clock_base_time =
              gst_clock_get_internal_time (media_clock);
        jbuf->base_rtptime = gstrtptime;
      }
    }

    if (gstrtptime > jbuf->base_rtptime)
      nsrtptimediff = gstrtptime - jbuf->base_rtptime;
    else
      nsrtptimediff = 0;

    rtpntptime = nsrtptimediff + jbuf->media_clock_base_time;

    rtpsystime =
        gst_clock_adjust_with_calibration (media_clock, rtpntptime, internal,
        external, rate_num, rate_denom);

    if (rtpsystime > base_time)
832
      pts = rtpsystime - base_time;
833
    else
834
      pts = 0;
835 836

    GST_DEBUG ("RFC7273 clock time %" GST_TIME_FORMAT ", out %" GST_TIME_FORMAT,
837
        GST_TIME_ARGS (rtpsystime), GST_TIME_ARGS (pts));
838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885
  } else if (rfc7273_mode && (jbuf->mode == RTP_JITTER_BUFFER_MODE_SLAVE
          || jbuf->mode == RTP_JITTER_BUFFER_MODE_SYNCED)
      && media_clock_offset != -1 && jbuf->rfc7273_sync) {
    GstClockTime ntptime, rtptime_tmp;
    GstClockTime ntprtptime, rtpsystime;
    GstClockTime internal, external;
    GstClockTime rate_num, rate_denom;

    /* Don't do any of the dts related adjustments further down */
    dts = -1;

    /* Calculate the actual clock time on the sender side based on the
     * RFC7273 clock and convert it to our pipeline clock
     */

    gst_clock_get_calibration (media_clock, &internal, &external, &rate_num,
        &rate_denom);

    ntptime = gst_clock_get_internal_time (media_clock);

    ntprtptime = gst_util_uint64_scale (ntptime, jbuf->clock_rate, GST_SECOND);
    ntprtptime += media_clock_offset;
    ntprtptime &= 0xffffffff;

    rtptime_tmp = rtptime;
    /* Check for wraparounds, we assume that the diff between current RTP
     * timestamp and current media clock time can't be bigger than
     * 2**31 clock units */
    if (ntprtptime > rtptime_tmp && ntprtptime - rtptime_tmp >= 0x80000000)
      rtptime_tmp += G_GUINT64_CONSTANT (0x100000000);
    else if (rtptime_tmp > ntprtptime && rtptime_tmp - ntprtptime >= 0x80000000)
      ntprtptime += G_GUINT64_CONSTANT (0x100000000);

    if (ntprtptime > rtptime_tmp)
      ntptime -=
          gst_util_uint64_scale (ntprtptime - rtptime_tmp, jbuf->clock_rate,
          GST_SECOND);
    else
      ntptime +=
          gst_util_uint64_scale (rtptime_tmp - ntprtptime, jbuf->clock_rate,
          GST_SECOND);

    rtpsystime =
        gst_clock_adjust_with_calibration (media_clock, ntptime, internal,
        external, rate_num, rate_denom);
    /* All this assumes that the pipeline has enough additional
     * latency to cover for the network delay */
    if (rtpsystime > base_time)
886
      pts = rtpsystime - base_time;
887
    else
888
      pts = 0;
889 890

    GST_DEBUG ("RFC7273 clock time %" GST_TIME_FORMAT ", out %" GST_TIME_FORMAT,
891
        GST_TIME_ARGS (rtpsystime), GST_TIME_ARGS (pts));
892 893 894 895 896 897 898
  } else {
    /* If we used the RFC7273 clock before and not anymore,
     * we need to resync it later again */
    jbuf->media_clock_base_time = -1;

    /* do skew calculation by measuring the difference between rtptime and the
     * receive dts, this function will return the skew corrected rtptime. */
899
    pts = calculate_skew (jbuf, ext_rtptime, gstrtptime, dts);
900 901 902 903
  }

  /* check if timestamps are not going backwards, we can only check this if we
   * have a previous out time and a previous send_diff */
904
  if (G_LIKELY (pts != -1 && jbuf->prev_out_time != -1
905 906 907 908 909
          && jbuf->prev_send_diff != -1)) {
    /* now check for backwards timestamps */
    if (G_UNLIKELY (
            /* if the server timestamps went up and the out_time backwards */
            (gstrtptime - jbuf->base_rtptime > jbuf->prev_send_diff
910
                && pts < jbuf->prev_out_time) ||
911 912
            /* if the server timestamps went backwards and the out_time forwards */
            (gstrtptime - jbuf->base_rtptime < jbuf->prev_send_diff
913
                && pts > jbuf->prev_out_time) ||
914 915 916
            /* if the server timestamps did not change */
            gstrtptime - jbuf->base_rtptime == jbuf->prev_send_diff)) {
      GST_DEBUG ("backwards timestamps, using previous time");
917
      pts = jbuf->prev_out_time;
918 919
    }
  }
920 921

  if (dts != -1 && pts + jbuf->delay < dts) {
922 923 924 925
    /* if we are going to produce a timestamp that is later than the input
     * timestamp, we need to reset the jitterbuffer. Likely the server paused
     * temporarily */
    GST_DEBUG ("out %" GST_TIME_FORMAT " + %" G_GUINT64_FORMAT " < time %"
926
        GST_TIME_FORMAT ", reset jitterbuffer", GST_TIME_ARGS (pts),
927
        jbuf->delay, GST_TIME_ARGS (dts));
928
    rtp_jitter_buffer_resync (jbuf, dts, gstrtptime, ext_rtptime, TRUE);
929
    pts = dts;
930 931
  }

932
  jbuf->prev_out_time = pts;
933 934 935 936 937 938
  jbuf->prev_send_diff = gstrtptime - jbuf->base_rtptime;

  if (media_clock)
    gst_object_unref (media_clock);
  if (pipeline_clock)
    gst_object_unref (pipeline_clock);
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
  return pts;
}


/**
 * rtp_jitter_buffer_insert:
 * @jbuf: an #RTPJitterBuffer
 * @item: an #RTPJitterBufferItem to insert
 * @head: TRUE when the head element changed.
 * @percent: the buffering percent after insertion
 *
 * Inserts @item into the packet queue of @jbuf. The sequence number of the
 * packet will be used to sort the packets. This function takes ownerhip of
 * @buf when the function returns %TRUE.
 *
 * When @head is %TRUE, the new packet was added at the head of the queue and
 * will be available with the next call to rtp_jitter_buffer_pop() and
 * rtp_jitter_buffer_peek().
 *
 * Returns: %FALSE if a packet with the same number already existed.
 */
gboolean
rtp_jitter_buffer_insert (RTPJitterBuffer * jbuf, RTPJitterBufferItem * item,
    gboolean * head, gint * percent)
{
  GList *list, *event = NULL;
  guint16 seqnum;

  g_return_val_if_fail (jbuf != NULL, FALSE);
  g_return_val_if_fail (item != NULL, FALSE);

  list = jbuf->packets->tail;

  /* no seqnum, simply append then */
  if (item->seqnum == -1)
    goto append;

  seqnum = item->seqnum;

  /* loop the list to skip strictly larger seqnum buffers */
  for (; list; list = g_list_previous (list)) {
    guint16 qseq;
    gint gap;
    RTPJitterBufferItem *qitem = (RTPJitterBufferItem *) list;

    if (qitem->seqnum == -1) {
      /* keep a pointer to the first consecutive event if not already
       * set. we will insert the packet after the event if we can't find
       * a packet with lower sequence number before the event. */
      if (event == NULL)
        event = list;
      continue;
    }

    qseq = qitem->seqnum;

    /* compare the new seqnum to the one in the buffer */
    gap = gst_rtp_buffer_compare_seqnum (seqnum, qseq);

    /* we hit a packet with the same seqnum, notify a duplicate */
    if (G_UNLIKELY (gap == 0))
      goto duplicate;

    /* seqnum > qseq, we can stop looking */
    if (G_LIKELY (gap < 0))
      break;

    /* if we've found a packet with greater sequence number, cleanup the
     * event pointer as the packet will be inserted before the event */
    event = NULL;
  }

  /* if event is set it means that packets before the event had smaller
   * sequence number, so we will insert our packet after the event */
  if (event)
    list = event;

1017
append:
1018
  queue_do_insert (jbuf, list, (GList *) item);
1019

1020 1021
  /* buffering mode, update buffer stats */
  if (jbuf->mode == RTP_JITTER_BUFFER_MODE_BUFFER)
Wim Taymans's avatar
Wim Taymans committed
1022
    update_buffer_level (jbuf, percent);
1023
  else if (percent)
Wim Taymans's avatar
Wim Taymans committed
1024
    *percent = -1;
1025

1026
  /* head was changed when we did not find a previous packet, we set the return
1027
   * flag when requested. */
1028 1029
  if (G_LIKELY (head))
    *head = (list == NULL);
1030

1031
  return TRUE;
1032 1033 1034 1035

  /* ERRORS */
duplicate:
  {
1036
    GST_DEBUG ("duplicate packet %d found", (gint) seqnum);
1037 1038
    if (G_LIKELY (head))
      *head = FALSE;
1039 1040
    return FALSE;
  }
1041 1042 1043 1044 1045
}

/**
 * rtp_jitter_buffer_pop:
 * @jbuf: an #RTPJitterBuffer
Wim Taymans's avatar
Wim Taymans committed
1046
 * @percent: the buffering percent
1047
 *
1048 1049 1050
 * Pops the oldest buffer from the packet queue of @jbuf. The popped buffer will
 * have its timestamp adjusted with the incomming running_time and the detected
 * clock skew.
1051 1052 1053
 *
 * Returns: a #GstBuffer or %NULL when there was no packet in the queue.
 */
1054
RTPJitterBufferItem *
Wim Taymans's avatar
Wim Taymans committed
1055
rtp_jitter_buffer_pop (RTPJitterBuffer * jbuf, gint * percent)
1056
{
1057 1058
  GList *item = NULL;
  GQueue *queue;
1059

1060
  g_return_val_if_fail (jbuf != NULL, NULL);
1061

1062 1063
  queue = jbuf->packets;

1064
  item = queue->head;
1065
  if (item) {
1066 1067 1068
    queue->head = item->next;
    if (queue->head)
      queue->head->prev = NULL;
1069
    else
1070
      queue->tail = NULL;
1071 1072
    queue->length--;
  }
1073

1074 1075
  /* buffering mode, update buffer stats */
  if (jbuf->mode == RTP_JITTER_BUFFER_MODE_BUFFER)
Wim Taymans's avatar
Wim Taymans committed
1076
    update_buffer_level (jbuf, percent);
1077
  else if (percent)
Wim Taymans's avatar
Wim Taymans committed
1078
    *percent = -1;
1079

1080
  return (RTPJitterBufferItem *) item;
1081 1082
}

1083 1084 1085 1086
/**
 * rtp_jitter_buffer_peek:
 * @jbuf: an #RTPJitterBuffer
 *
1087 1088 1089 1090
 * Peek the oldest buffer from the packet queue of @jbuf.
 *
 * See rtp_jitter_buffer_insert() to check when an older packet was
 * added.
1091 1092 1093
 *
 * Returns: a #GstBuffer or %NULL when there was no packet in the queue.
 */
1094
RTPJitterBufferItem *
1095 1096
rtp_jitter_buffer_peek (RTPJitterBuffer * jbuf)
{
1097
  g_return_val_if_fail (jbuf != NULL, NULL);
1098

1099
  return (RTPJitterBufferItem *) jbuf->packets->head;
1100 1101
}

1102 1103 1104
/**
 * rtp_jitter_buffer_flush:
 * @jbuf: an #RTPJitterBuffer
Wim Taymans's avatar
Wim Taymans committed
1105 1106
 * @free_func: function to free each item
 * @user_data: user data passed to @free_func
1107 1108 1109 1110
 *
 * Flush all packets from the jitterbuffer.
 */
void
Wim Taymans's avatar
Wim Taymans committed
1111 1112
rtp_jitter_buffer_flush (RTPJitterBuffer * jbuf, GFunc free_func,
    gpointer user_data)
1113
{
Wim Taymans's avatar
Wim Taymans committed
1114
  GList *item;
1115 1116

  g_return_if_fail (jbuf != NULL);
Wim Taymans's avatar
Wim Taymans committed
1117
  g_return_if_fail (free_func != NULL);
1118

Wim Taymans's avatar
Wim Taymans committed
1119 1120
  while ((item = g_queue_pop_head_link (jbuf->packets)))
    free_func ((RTPJitterBufferItem *) item, user_data);
1121 1122
}

1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134
/**
 * rtp_jitter_buffer_is_buffering:
 * @jbuf: an #RTPJitterBuffer
 *
 * Check if @jbuf is buffering currently. Users of the jitterbuffer should not
 * pop packets while in buffering mode.
 *
 * Returns: the buffering state of @jbuf
 */
gboolean
rtp_jitter_buffer_is_buffering (RTPJitterBuffer * jbuf)
{
1135
  return jbuf->buffering && !jbuf->buffering_disabled;
1136 1137
}

Wim Taymans's avatar
Wim Taymans committed
1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164
/**
 * rtp_jitter_buffer_set_buffering:
 * @jbuf: an #RTPJitterBuffer
 * @buffering: the new buffering state
 *
 * Forces @jbuf to go into the buffering state.
 */
void
rtp_jitter_buffer_set_buffering (RTPJitterBuffer * jbuf, gboolean buffering)
{
  jbuf->buffering = buffering;
}

/**
 * rtp_jitter_buffer_get_percent:
 * @jbuf: an #RTPJitterBuffer
 *
 * Get the buffering percent of the jitterbuffer.
 *
 * Returns: the buffering percent
 */
gint
rtp_jitter_buffer_get_percent (RTPJitterBuffer * jbuf)
{
  gint percent;
  guint64 level;

1165 1166 1167
  if (G_UNLIKELY (jbuf->high_level == 0))
    return 100;

1168 1169 1170
  if (G_UNLIKELY (jbuf->buffering_disabled))
    return 100;

Wim Taymans's avatar
Wim Taymans committed
1171 1172 1173 1174 1175 1176 1177
  level = get_buffer_level (jbuf);
  percent = (level * 100 / jbuf->high_level);
  percent = MIN (percent, 100);

  return percent;
}

1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190
/**
 * rtp_jitter_buffer_num_packets:
 * @jbuf: an #RTPJitterBuffer
 *
 * Get the number of packets currently in "jbuf.
 *
 * Returns: The number of packets in @jbuf.
 */
guint
rtp_jitter_buffer_num_packets (RTPJitterBuffer * jbuf)
{
  g_return_val_if_fail (jbuf != NULL, 0);

1191
  return jbuf->packets->length;
1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205
}

/**
 * rtp_jitter_buffer_get_ts_diff:
 * @jbuf: an #RTPJitterBuffer
 *
 * Get the difference between the timestamps of first and last packet in the
 * jitterbuffer.
 *
 * Returns: The difference expressed in the timestamp units of the packets.
 */
guint32
rtp_jitter_buffer_get_ts_diff (RTPJitterBuffer * jbuf)
{
1206
  guint64 high_ts, low_ts;
1207
  RTPJitterBufferItem *high_buf, *low_buf;
1208
  guint32 result;
1209 1210 1211

  g_return_val_if_fail (jbuf != NULL, 0);

1212 1213
  high_buf = (RTPJitterBufferItem *) g_queue_peek_tail_link (jbuf->packets);
  low_buf = (RTPJitterBufferItem *) g_queue_peek_head_link (jbuf->packets);
1214 1215 1216 1217

  if (!high_buf || !low_buf || high_buf == low_buf)
    return 0;

1218 1219
  high_ts = high_buf->rtptime;
  low_ts = low_buf->rtptime;
1220 1221 1222

  /* it needs to work if ts wraps */
  if (high_ts >= low_ts) {
1223
    result = (guint32) (high_ts - low_ts);
1224
  } else {
1225
    result = (guint32) (high_ts + G_MAXUINT32 + 1 - low_ts);
1226
  }
1227
  return result;
1228
}
1229 1230 1231 1232 1233 1234

/**
 * rtp_jitter_buffer_get_sync:
 * @jbuf: an #RTPJitterBuffer
 * @rtptime: result RTP time
 * @timestamp: result GStreamer timestamp
1235
 * @clock_rate: clock-rate of @rtptime
1236
 * @last_rtptime: last seen rtptime.
1237
 *
Stefan Kost's avatar
Stefan Kost committed
1238
 * Calculates the relation between the RTP timestamp and the GStreamer timestamp
1239
 * used for constructing timestamps.
1240 1241 1242 1243 1244 1245
 *
 * For extended RTP timestamp @rtptime with a clock-rate of @clock_rate,
 * the GStreamer timestamp is currently @timestamp.
 *
 * The last seen extended RTP timestamp with clock-rate @clock-rate is returned in
 * @last_rtptime.
1246 1247 1248
 */
void
rtp_jitter_buffer_get_sync (RTPJitterBuffer * jbuf, guint64 * rtptime,
1249
    guint64 * timestamp, guint32 * clock_rate, guint64 * last_rtptime)
1250 1251 1252 1253 1254
{
  if (rtptime)
    *rtptime = jbuf->base_extrtp;
  if (timestamp)
    *timestamp = jbuf->base_time + jbuf->skew;
1255 1256
  if (clock_rate)
    *clock_rate = jbuf->clock_rate;
1257 1258
  if (last_rtptime)
    *last_rtptime = jbuf->last_rtptime;
1259
}
1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297

/**
 * rtp_jitter_buffer_can_fast_start:
 * @jbuf: an #RTPJitterBuffer
 * @num_packets: Number of consecutive packets needed
 *
 * Check if in the queue if there is enough packets with consecutive seqnum in
 * order to start delivering them.
 *
 * Returns: %TRUE if the required number of consecutive packets was found.
 */
gboolean
rtp_jitter_buffer_can_fast_start (RTPJitterBuffer * jbuf, gint num_packet)
{
  gboolean ret = TRUE;
  RTPJitterBufferItem *last_item = NULL, *item;
  gint i;

  if (rtp_jitter_buffer_num_packets (jbuf) < num_packet)
    return FALSE;

  item = rtp_jitter_buffer_peek (jbuf);
  for (i = 0; i < num_packet; i++) {
    if (G_LIKELY (last_item)) {
      guint16 expected_seqnum = last_item->seqnum + 1;

      if (expected_seqnum != item->seqnum) {
        ret = FALSE;
        break;
      }
    }

    last_item = item;
    item = (RTPJitterBufferItem *) last_item->next;
  }

  return ret;
}