Blame tests/check/elements/rtpjitterbuffer.c

Packit 1f69a5
/* GStreamer
Packit 1f69a5
 *
Packit 1f69a5
 * Copyright (C) 2009 Nokia Corporation and its subsidary(-ies)
Packit 1f69a5
 *               contact: <stefan.kost@nokia.com>
Packit 1f69a5
 * Copyright (C) 2012 Cisco Systems, Inc
Packit 1f69a5
 *               Authors: Kelley Rogers <kelro@cisco.com>
Packit 1f69a5
 *               Havard Graff <hgraff@cisco.com>
Packit 1f69a5
 * Copyright (C) 2013-2016 Pexip AS
Packit 1f69a5
 *               Stian Selnes <stian@pexip>
Packit 1f69a5
 *               Havard Graff <havard@pexip>
Packit 1f69a5
 *
Packit 1f69a5
 * This library is free software; you can redistribute it and/or
Packit 1f69a5
 * modify it under the terms of the GNU Library General Public
Packit 1f69a5
 * License as published by the Free Software Foundation; either
Packit 1f69a5
 * version 2 of the License, or (at your option) any later version.
Packit 1f69a5
 *
Packit 1f69a5
 * This library is distributed in the hope that it will be useful,
Packit 1f69a5
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 1f69a5
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 1f69a5
 * Library General Public License for more details.
Packit 1f69a5
 *
Packit 1f69a5
 * You should have received a copy of the GNU Library General Public
Packit 1f69a5
 * License along with this library; if not, write to the
Packit 1f69a5
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
Packit 1f69a5
 * Boston, MA 02110-1301, USA.
Packit 1f69a5
 */
Packit 1f69a5
Packit 1f69a5
#include <gst/check/gstcheck.h>
Packit 1f69a5
#include <gst/check/gsttestclock.h>
Packit 1f69a5
#include <gst/check/gstharness.h>
Packit 1f69a5
Packit 1f69a5
#include <gst/rtp/gstrtpbuffer.h>
Packit 1f69a5
Packit 1f69a5
/* For ease of programming we use globals to keep refs for our floating
Packit 1f69a5
 * src and sink pads we create; otherwise we always have to do get_pad,
Packit 1f69a5
 * get_peer, and then remove references in every test function */
Packit 1f69a5
static GstPad *mysrcpad, *mysinkpad;
Packit 1f69a5
/* we also have a list of src buffers */
Packit 1f69a5
static GList *inbuffers = NULL;
Packit 1f69a5
static gint num_dropped = 0;
Packit 1f69a5
Packit 1f69a5
#define RTP_CAPS_STRING    \
Packit 1f69a5
    "application/x-rtp, "               \
Packit 1f69a5
    "media = (string)audio, "           \
Packit 1f69a5
    "payload = (int) 0, "               \
Packit 1f69a5
    "clock-rate = (int) 8000, "         \
Packit 1f69a5
    "encoding-name = (string)PCMU"
Packit 1f69a5
Packit 1f69a5
#define RTP_FRAME_SIZE 20
Packit 1f69a5
Packit 1f69a5
static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
Packit 1f69a5
    GST_PAD_SINK,
Packit 1f69a5
    GST_PAD_ALWAYS,
Packit 1f69a5
    GST_STATIC_CAPS ("application/x-rtp")
Packit 1f69a5
    );
Packit 1f69a5
static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
Packit 1f69a5
    GST_PAD_SRC,
Packit 1f69a5
    GST_PAD_ALWAYS,
Packit 1f69a5
    GST_STATIC_CAPS ("application/x-rtp, "
Packit 1f69a5
        "clock-rate = (int) [ 1, 2147483647 ]")
Packit 1f69a5
    );
Packit 1f69a5
Packit 1f69a5
static void
Packit 1f69a5
buffer_dropped (gpointer data, GstMiniObject * obj)
Packit 1f69a5
{
Packit 1f69a5
  GST_DEBUG ("dropping buffer %p", obj);
Packit 1f69a5
  num_dropped++;
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
static GstElement *
Packit 1f69a5
setup_jitterbuffer (gint num_buffers)
Packit 1f69a5
{
Packit 1f69a5
  GstElement *jitterbuffer;
Packit 1f69a5
  GstClock *clock;
Packit 1f69a5
  GstBuffer *buffer;
Packit 1f69a5
  GstCaps *caps;
Packit 1f69a5
  /* a 20 sample audio block (2,5 ms) generated with
Packit 1f69a5
   * gst-launch audiotestsrc wave=silence blocksize=40 num-buffers=3 !
Packit 1f69a5
   *    "audio/x-raw,channels=1,rate=8000" ! mulawenc ! rtppcmupay !
Packit 1f69a5
   *     fakesink dump=1
Packit 1f69a5
   */
Packit 1f69a5
  guint8 in[] = {
Packit 1f69a5
    /* first 4 bytes are rtp-header, next 4 bytes are timestamp */
Packit 1f69a5
    0x80, 0x80, 0x1c, 0x24, 0x46, 0xcd, 0xb7, 0x11, 0x3c, 0x3a, 0x7c, 0x5b,
Packit 1f69a5
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
Packit 1f69a5
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
Packit 1f69a5
  };
Packit 1f69a5
  GstClockTime ts = G_GUINT64_CONSTANT (0);
Packit 1f69a5
  GstClockTime tso = gst_util_uint64_scale (RTP_FRAME_SIZE, GST_SECOND, 8000);
Packit 1f69a5
  /*guint latency = GST_TIME_AS_MSECONDS (num_buffers * tso); */
Packit 1f69a5
  gint i;
Packit 1f69a5
Packit 1f69a5
  GST_DEBUG ("setup_jitterbuffer");
Packit 1f69a5
  jitterbuffer = gst_check_setup_element ("rtpjitterbuffer");
Packit 1f69a5
  /* we need a clock here */
Packit 1f69a5
  clock = gst_system_clock_obtain ();
Packit 1f69a5
  gst_element_set_clock (jitterbuffer, clock);
Packit 1f69a5
  gst_object_unref (clock);
Packit 1f69a5
  /* setup latency */
Packit 1f69a5
  /* latency would be 7 for 3 buffers here, default is 200
Packit 1f69a5
     g_object_set (G_OBJECT (jitterbuffer), "latency", latency, NULL);
Packit 1f69a5
     GST_INFO_OBJECT (jitterbuffer, "set latency to %u ms", latency);
Packit 1f69a5
   */
Packit 1f69a5
Packit 1f69a5
  mysrcpad = gst_check_setup_src_pad (jitterbuffer, &srctemplate);
Packit 1f69a5
  mysinkpad = gst_check_setup_sink_pad (jitterbuffer, &sinktemplate);
Packit 1f69a5
  gst_pad_set_active (mysrcpad, TRUE);
Packit 1f69a5
  gst_pad_set_active (mysinkpad, TRUE);
Packit 1f69a5
Packit 1f69a5
  /* create n buffers */
Packit 1f69a5
  caps = gst_caps_from_string (RTP_CAPS_STRING);
Packit 1f69a5
  gst_check_setup_events (mysrcpad, jitterbuffer, caps, GST_FORMAT_TIME);
Packit 1f69a5
  gst_caps_unref (caps);
Packit 1f69a5
Packit 1f69a5
  for (i = 0; i < num_buffers; i++) {
Packit 1f69a5
    buffer = gst_buffer_new_and_alloc (sizeof (in));
Packit 1f69a5
    gst_buffer_fill (buffer, 0, in, sizeof (in));
Packit 1f69a5
    GST_BUFFER_DTS (buffer) = ts;
Packit 1f69a5
    GST_BUFFER_PTS (buffer) = ts;
Packit 1f69a5
    GST_BUFFER_DURATION (buffer) = tso;
Packit 1f69a5
    gst_mini_object_weak_ref (GST_MINI_OBJECT (buffer), buffer_dropped, NULL);
Packit 1f69a5
    GST_DEBUG ("created buffer: %p", buffer);
Packit 1f69a5
Packit 1f69a5
    if (!i)
Packit 1f69a5
      GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
Packit 1f69a5
Packit 1f69a5
    inbuffers = g_list_append (inbuffers, buffer);
Packit 1f69a5
Packit 1f69a5
    /* hackish way to update the rtp header */
Packit 1f69a5
    in[1] = 0x00;
Packit 1f69a5
    in[3]++;                    /* seqnumber */
Packit 1f69a5
    in[7] += RTP_FRAME_SIZE;    /* inc. timestamp with framesize */
Packit 1f69a5
    ts += tso;
Packit 1f69a5
  }
Packit 1f69a5
  num_dropped = 0;
Packit 1f69a5
Packit 1f69a5
  return jitterbuffer;
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
static GstStateChangeReturn
Packit 1f69a5
start_jitterbuffer (GstElement * jitterbuffer)
Packit 1f69a5
{
Packit 1f69a5
  GstStateChangeReturn ret;
Packit 1f69a5
  GstClockTime now;
Packit 1f69a5
  GstClock *clock;
Packit 1f69a5
Packit 1f69a5
  clock = gst_element_get_clock (jitterbuffer);
Packit 1f69a5
  now = gst_clock_get_time (clock);
Packit 1f69a5
  gst_object_unref (clock);
Packit 1f69a5
Packit 1f69a5
  gst_element_set_base_time (jitterbuffer, now);
Packit 1f69a5
  ret = gst_element_set_state (jitterbuffer, GST_STATE_PLAYING);
Packit 1f69a5
Packit 1f69a5
  return ret;
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
static void
Packit 1f69a5
cleanup_jitterbuffer (GstElement * jitterbuffer)
Packit 1f69a5
{
Packit 1f69a5
  GST_DEBUG ("cleanup_jitterbuffer");
Packit 1f69a5
Packit 1f69a5
  g_list_foreach (buffers, (GFunc) gst_mini_object_unref, NULL);
Packit 1f69a5
  g_list_free (buffers);
Packit 1f69a5
  buffers = NULL;
Packit 1f69a5
Packit 1f69a5
  g_list_free (inbuffers);
Packit 1f69a5
  inbuffers = NULL;
Packit 1f69a5
Packit 1f69a5
  gst_pad_set_active (mysrcpad, FALSE);
Packit 1f69a5
  gst_pad_set_active (mysinkpad, FALSE);
Packit 1f69a5
  gst_check_teardown_src_pad (jitterbuffer);
Packit 1f69a5
  gst_check_teardown_sink_pad (jitterbuffer);
Packit 1f69a5
  gst_check_teardown_element (jitterbuffer);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
static void
Packit 1f69a5
check_jitterbuffer_results (GstElement * jitterbuffer, gint num_buffers)
Packit 1f69a5
{
Packit 1f69a5
  GstBuffer *buffer;
Packit 1f69a5
  GList *node;
Packit 1f69a5
  GstClockTime ts = G_GUINT64_CONSTANT (0);
Packit 1f69a5
  GstClockTime tso = gst_util_uint64_scale (RTP_FRAME_SIZE, GST_SECOND, 8000);
Packit 1f69a5
  GstMapInfo map;
Packit 1f69a5
  guint16 prev_sn = 0, cur_sn;
Packit 1f69a5
  guint32 prev_ts = 0, cur_ts;
Packit 1f69a5
Packit 1f69a5
  /* sleep for twice the latency */
Packit 1f69a5
  g_usleep (400 * 1000);
Packit 1f69a5
Packit 1f69a5
  GST_INFO ("of %d buffer %d/%d received/dropped", num_buffers,
Packit 1f69a5
      g_list_length (buffers), num_dropped);
Packit 1f69a5
  /* if this fails, not all buffers have been processed */
Packit 1f69a5
  fail_unless_equals_int ((g_list_length (buffers) + num_dropped), num_buffers);
Packit 1f69a5
Packit 1f69a5
  /* check the buffer list */
Packit 1f69a5
  fail_unless_equals_int (g_list_length (buffers), num_buffers);
Packit 1f69a5
  for (node = buffers; node; node = g_list_next (node)) {
Packit 1f69a5
    fail_if ((buffer = (GstBuffer *) node->data) == NULL);
Packit 1f69a5
    fail_if (GST_BUFFER_PTS (buffer) != ts);
Packit 1f69a5
    fail_if (GST_BUFFER_DTS (buffer) != ts);
Packit 1f69a5
    gst_buffer_map (buffer, &map, GST_MAP_READ);
Packit 1f69a5
    cur_sn = ((guint16) map.data[2] << 8) | map.data[3];
Packit 1f69a5
    cur_ts = ((guint32) map.data[4] << 24) | ((guint32) map.data[5] << 16) |
Packit 1f69a5
        ((guint32) map.data[6] << 8) | map.data[7];
Packit 1f69a5
    gst_buffer_unmap (buffer, &map);
Packit 1f69a5
Packit 1f69a5
    if (node != buffers) {
Packit 1f69a5
      fail_unless (cur_sn > prev_sn);
Packit 1f69a5
      fail_unless (cur_ts > prev_ts);
Packit 1f69a5
Packit 1f69a5
      prev_sn = cur_sn;
Packit 1f69a5
      prev_ts = cur_ts;
Packit 1f69a5
    }
Packit 1f69a5
    ts += tso;
Packit 1f69a5
  }
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_push_forward_seq)
Packit 1f69a5
{
Packit 1f69a5
  GstElement *jitterbuffer;
Packit 1f69a5
  const guint num_buffers = 3;
Packit 1f69a5
  GstBuffer *buffer;
Packit 1f69a5
  GList *node;
Packit 1f69a5
Packit 1f69a5
  jitterbuffer = setup_jitterbuffer (num_buffers);
Packit 1f69a5
  fail_unless (start_jitterbuffer (jitterbuffer)
Packit 1f69a5
      == GST_STATE_CHANGE_SUCCESS, "could not set to playing");
Packit 1f69a5
Packit 1f69a5
  /* push buffers: 0,1,2, */
Packit 1f69a5
  for (node = inbuffers; node; node = g_list_next (node)) {
Packit 1f69a5
    buffer = (GstBuffer *) node->data;
Packit 1f69a5
    fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
Packit 1f69a5
  }
Packit 1f69a5
Packit 1f69a5
  /* check the buffer list */
Packit 1f69a5
  check_jitterbuffer_results (jitterbuffer, num_buffers);
Packit 1f69a5
Packit 1f69a5
  /* cleanup */
Packit 1f69a5
  cleanup_jitterbuffer (jitterbuffer);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_push_backward_seq)
Packit 1f69a5
{
Packit 1f69a5
  GstElement *jitterbuffer;
Packit 1f69a5
  const guint num_buffers = 4;
Packit 1f69a5
  GstBuffer *buffer;
Packit 1f69a5
  GList *node;
Packit 1f69a5
Packit 1f69a5
  jitterbuffer = setup_jitterbuffer (num_buffers);
Packit 1f69a5
  fail_unless (start_jitterbuffer (jitterbuffer)
Packit 1f69a5
      == GST_STATE_CHANGE_SUCCESS, "could not set to playing");
Packit 1f69a5
Packit 1f69a5
  /* push buffers: 0,3,2,1 */
Packit 1f69a5
  buffer = (GstBuffer *) inbuffers->data;
Packit 1f69a5
  fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
Packit 1f69a5
  for (node = g_list_last (inbuffers); node != inbuffers;
Packit 1f69a5
      node = g_list_previous (node)) {
Packit 1f69a5
    buffer = (GstBuffer *) node->data;
Packit 1f69a5
    fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
Packit 1f69a5
  }
Packit 1f69a5
Packit 1f69a5
  /* check the buffer list */
Packit 1f69a5
  check_jitterbuffer_results (jitterbuffer, num_buffers);
Packit 1f69a5
Packit 1f69a5
  /* cleanup */
Packit 1f69a5
  cleanup_jitterbuffer (jitterbuffer);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_push_unordered)
Packit 1f69a5
{
Packit 1f69a5
  GstElement *jitterbuffer;
Packit 1f69a5
  const guint num_buffers = 4;
Packit 1f69a5
  GstBuffer *buffer;
Packit 1f69a5
Packit 1f69a5
  jitterbuffer = setup_jitterbuffer (num_buffers);
Packit 1f69a5
  fail_unless (start_jitterbuffer (jitterbuffer)
Packit 1f69a5
      == GST_STATE_CHANGE_SUCCESS, "could not set to playing");
Packit 1f69a5
Packit 1f69a5
  /* push buffers; 0,2,1,3 */
Packit 1f69a5
  buffer = (GstBuffer *) inbuffers->data;
Packit 1f69a5
  fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
Packit 1f69a5
  buffer = g_list_nth_data (inbuffers, 2);
Packit 1f69a5
  fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
Packit 1f69a5
  buffer = g_list_nth_data (inbuffers, 1);
Packit 1f69a5
  fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
Packit 1f69a5
  buffer = g_list_nth_data (inbuffers, 3);
Packit 1f69a5
  fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
Packit 1f69a5
Packit 1f69a5
  /* check the buffer list */
Packit 1f69a5
  check_jitterbuffer_results (jitterbuffer, num_buffers);
Packit 1f69a5
Packit 1f69a5
  /* cleanup */
Packit 1f69a5
  cleanup_jitterbuffer (jitterbuffer);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_basetime)
Packit 1f69a5
{
Packit 1f69a5
  GstElement *jitterbuffer;
Packit 1f69a5
  const guint num_buffers = 3;
Packit 1f69a5
  GstBuffer *buffer;
Packit 1f69a5
  GList *node;
Packit 1f69a5
  GstClockTime tso = gst_util_uint64_scale (RTP_FRAME_SIZE, GST_SECOND, 8000);
Packit 1f69a5
Packit 1f69a5
  jitterbuffer = setup_jitterbuffer (num_buffers);
Packit 1f69a5
  fail_unless (start_jitterbuffer (jitterbuffer)
Packit 1f69a5
      == GST_STATE_CHANGE_SUCCESS, "could not set to playing");
Packit 1f69a5
Packit 1f69a5
  /* push buffers: 2,1,0 */
Packit 1f69a5
  for (node = g_list_last (inbuffers); node; node = g_list_previous (node)) {
Packit 1f69a5
    buffer = (GstBuffer *) node->data;
Packit 1f69a5
    fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
Packit 1f69a5
  }
Packit 1f69a5
Packit 1f69a5
  /* sleep for twice the latency */
Packit 1f69a5
  g_usleep (400 * 1000);
Packit 1f69a5
Packit 1f69a5
  /* if this fails, not all buffers have been processed */
Packit 1f69a5
  fail_unless_equals_int ((g_list_length (buffers) + num_dropped), num_buffers);
Packit 1f69a5
Packit 1f69a5
  buffer = (GstBuffer *) buffers->data;
Packit 1f69a5
  fail_unless (GST_BUFFER_DTS (buffer) != (num_buffers * tso));
Packit 1f69a5
  fail_unless (GST_BUFFER_PTS (buffer) != (num_buffers * tso));
Packit 1f69a5
Packit 1f69a5
  /* cleanup */
Packit 1f69a5
  cleanup_jitterbuffer (jitterbuffer);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
static GstCaps *
Packit 1f69a5
request_pt_map (GstElement * jitterbuffer, guint pt)
Packit 1f69a5
{
Packit 1f69a5
  fail_unless (pt == 0);
Packit 1f69a5
Packit 1f69a5
  return gst_caps_from_string (RTP_CAPS_STRING);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_clear_pt_map)
Packit 1f69a5
{
Packit 1f69a5
  GstElement *jitterbuffer;
Packit 1f69a5
  const guint num_buffers = 10;
Packit 1f69a5
  gint i;
Packit 1f69a5
  GstBuffer *buffer;
Packit 1f69a5
  GList *node;
Packit 1f69a5
Packit 1f69a5
  jitterbuffer = setup_jitterbuffer (num_buffers);
Packit 1f69a5
  fail_unless (start_jitterbuffer (jitterbuffer)
Packit 1f69a5
      == GST_STATE_CHANGE_SUCCESS, "could not set to playing");
Packit 1f69a5
Packit 1f69a5
  g_signal_connect (jitterbuffer, "request-pt-map", (GCallback)
Packit 1f69a5
      request_pt_map, NULL);
Packit 1f69a5
Packit 1f69a5
  /* push buffers: 0,1,2, */
Packit 1f69a5
  for (node = inbuffers, i = 0; node && i < 3; node = g_list_next (node), i++) {
Packit 1f69a5
    buffer = (GstBuffer *) node->data;
Packit 1f69a5
    fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
Packit 1f69a5
  }
Packit 1f69a5
Packit 1f69a5
  g_usleep (400 * 1000);
Packit 1f69a5
Packit 1f69a5
  g_signal_emit_by_name (jitterbuffer, "clear-pt-map", NULL);
Packit 1f69a5
Packit 1f69a5
  for (; node && i < 10; node = g_list_next (node), i++) {
Packit 1f69a5
    buffer = (GstBuffer *) node->data;
Packit 1f69a5
    fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK);
Packit 1f69a5
  }
Packit 1f69a5
Packit 1f69a5
  /* check the buffer list */
Packit 1f69a5
  check_jitterbuffer_results (jitterbuffer, num_buffers);
Packit 1f69a5
Packit 1f69a5
  /* cleanup */
Packit 1f69a5
  cleanup_jitterbuffer (jitterbuffer);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
#define TEST_BUF_CLOCK_RATE 8000
Packit 1f69a5
#define TEST_BUF_PT 0
Packit 1f69a5
#define TEST_BUF_SSRC 0x01BADBAD
Packit 1f69a5
#define TEST_BUF_MS  20
Packit 1f69a5
#define TEST_BUF_DURATION (TEST_BUF_MS * GST_MSECOND)
Packit 1f69a5
#define TEST_BUF_SIZE (64000 * TEST_BUF_MS / 1000)
Packit 1f69a5
#define TEST_RTP_TS_DURATION (TEST_BUF_CLOCK_RATE * TEST_BUF_MS / 1000)
Packit 1f69a5
Packit 1f69a5
static GstCaps *
Packit 1f69a5
generate_caps (void)
Packit 1f69a5
{
Packit 1f69a5
  return gst_caps_new_simple ("application/x-rtp",
Packit 1f69a5
      "media", G_TYPE_STRING, "audio",
Packit 1f69a5
      "clock-rate", G_TYPE_INT, TEST_BUF_CLOCK_RATE,
Packit 1f69a5
      "encoding-name", G_TYPE_STRING, "TEST",
Packit 1f69a5
      "payload", G_TYPE_INT, TEST_BUF_PT,
Packit 1f69a5
      "ssrc", G_TYPE_UINT, TEST_BUF_SSRC, NULL);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
static GstBuffer *
Packit 1f69a5
generate_test_buffer_full (GstClockTime dts, guint seq_num, guint32 rtp_ts)
Packit 1f69a5
{
Packit 1f69a5
  GstBuffer *buf;
Packit 1f69a5
  guint8 *payload;
Packit 1f69a5
  guint i;
Packit 1f69a5
  GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
Packit 1f69a5
Packit 1f69a5
  buf = gst_rtp_buffer_new_allocate (TEST_BUF_SIZE, 0, 0);
Packit 1f69a5
  GST_BUFFER_DTS (buf) = dts;
Packit 1f69a5
Packit 1f69a5
  gst_rtp_buffer_map (buf, GST_MAP_READWRITE, &rtp;;
Packit 1f69a5
  gst_rtp_buffer_set_payload_type (&rtp, TEST_BUF_PT);
Packit 1f69a5
  gst_rtp_buffer_set_seq (&rtp, seq_num);
Packit 1f69a5
  gst_rtp_buffer_set_timestamp (&rtp, rtp_ts);
Packit 1f69a5
  gst_rtp_buffer_set_ssrc (&rtp, TEST_BUF_SSRC);
Packit 1f69a5
Packit 1f69a5
  payload = gst_rtp_buffer_get_payload (&rtp;;
Packit 1f69a5
  for (i = 0; i < TEST_BUF_SIZE; i++)
Packit 1f69a5
    payload[i] = 0xff;
Packit 1f69a5
Packit 1f69a5
  gst_rtp_buffer_unmap (&rtp;;
Packit 1f69a5
Packit 1f69a5
  return buf;
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
static GstBuffer *
Packit 1f69a5
generate_test_buffer (guint seq_num)
Packit 1f69a5
{
Packit 1f69a5
  return generate_test_buffer_full (seq_num * TEST_BUF_DURATION,
Packit 1f69a5
      seq_num, seq_num * TEST_RTP_TS_DURATION);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
static GstBuffer *
Packit 1f69a5
generate_test_buffer_rtx (GstClockTime dts, guint seq_num)
Packit 1f69a5
{
Packit 1f69a5
  GstBuffer *buffer = generate_test_buffer_full (dts, seq_num,
Packit 1f69a5
      seq_num * TEST_RTP_TS_DURATION);
Packit 1f69a5
  GST_BUFFER_FLAG_SET (buffer, GST_RTP_BUFFER_FLAG_RETRANSMISSION);
Packit 1f69a5
  return buffer;
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
static void
Packit 1f69a5
push_test_buffer (GstHarness * h, guint seq_num)
Packit 1f69a5
{
Packit 1f69a5
  gst_harness_set_time (h, seq_num * TEST_BUF_DURATION);
Packit 1f69a5
  fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (h,
Packit 1f69a5
          generate_test_buffer (seq_num)));
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
static gint
Packit 1f69a5
get_rtp_seq_num (GstBuffer * buf)
Packit 1f69a5
{
Packit 1f69a5
  GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
Packit 1f69a5
  gint seq;
Packit 1f69a5
  gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp;;
Packit 1f69a5
  seq = gst_rtp_buffer_get_seq (&rtp;;
Packit 1f69a5
  gst_rtp_buffer_unmap (&rtp;;
Packit 1f69a5
  return seq;
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
#define verify_lost_event(h, exp_seq, exp_ts, exp_dur)                         \
Packit 1f69a5
  G_STMT_START {                                                               \
Packit 1f69a5
    GstEvent *_event;                                                          \
Packit 1f69a5
    const GstStructure *_s;                                                    \
Packit 1f69a5
    const GValue *_value;                                                      \
Packit 1f69a5
    guint _seq;                                                                \
Packit 1f69a5
    GstClockTime _ts;                                                          \
Packit 1f69a5
    GstClockTime _dur;                                                         \
Packit 1f69a5
    _event = gst_harness_pull_event (h);                                       \
Packit 1f69a5
    fail_unless (_event != NULL);                                              \
Packit 1f69a5
    _s = gst_event_get_structure (_event);                                     \
Packit 1f69a5
    fail_unless (_s != NULL);                                                  \
Packit 1f69a5
    fail_unless (gst_structure_get_uint (_s, "seqnum", &_seq));                \
Packit 1f69a5
    _value = gst_structure_get_value (_s, "timestamp");                        \
Packit 1f69a5
    fail_unless (_value && G_VALUE_HOLDS_UINT64 (_value));                     \
Packit 1f69a5
    _ts = g_value_get_uint64 (_value);                                         \
Packit 1f69a5
    _value = gst_structure_get_value (_s, "duration");                         \
Packit 1f69a5
    fail_unless (_value && G_VALUE_HOLDS_UINT64 (_value));                     \
Packit 1f69a5
    _dur = g_value_get_uint64 (_value);                                        \
Packit 1f69a5
    fail_unless_equals_int ((guint16)(exp_seq), _seq);                         \
Packit 1f69a5
    fail_unless_equals_uint64 (exp_ts, _ts);                                   \
Packit 1f69a5
    fail_unless_equals_uint64 (exp_dur, _dur);                                 \
Packit 1f69a5
    gst_event_unref (_event);                                                  \
Packit 1f69a5
  } G_STMT_END
Packit 1f69a5
Packit 1f69a5
Packit 1f69a5
#define verify_rtx_event(h, exp_seq, exp_ts, exp_delay, exp_spacing)           \
Packit 1f69a5
  G_STMT_START {                                                               \
Packit 1f69a5
    GstEvent *_event;                                                          \
Packit 1f69a5
    const GstStructure *_s;                                                    \
Packit 1f69a5
    const GValue *_value;                                                      \
Packit 1f69a5
    guint _seq;                                                                \
Packit 1f69a5
    GstClockTime _ts;                                                          \
Packit 1f69a5
    guint _delay;                                                              \
Packit 1f69a5
    GstClockTime _spacing;                                                     \
Packit 1f69a5
    _event = gst_harness_pull_upstream_event (h);                              \
Packit 1f69a5
    fail_unless (_event != NULL);                                              \
Packit 1f69a5
    _s = gst_event_get_structure (_event);                                     \
Packit 1f69a5
    fail_unless (_s != NULL);                                                  \
Packit 1f69a5
    fail_unless (gst_structure_get_uint (_s, "seqnum", &_seq));                \
Packit 1f69a5
    _value = gst_structure_get_value (_s, "running-time");                     \
Packit 1f69a5
    fail_unless (_value && G_VALUE_HOLDS_UINT64 (_value));                     \
Packit 1f69a5
    _ts = g_value_get_uint64 (_value);                                         \
Packit 1f69a5
    fail_unless (gst_structure_get_uint (_s, "delay", &_delay));               \
Packit 1f69a5
    _value = gst_structure_get_value (_s, "packet-spacing");                   \
Packit 1f69a5
    fail_unless (_value && G_VALUE_HOLDS_UINT64 (_value));                     \
Packit 1f69a5
    _spacing = g_value_get_uint64 (_value);                                    \
Packit 1f69a5
    fail_unless_equals_int ((guint16)(exp_seq), _seq);                         \
Packit 1f69a5
    fail_unless_equals_uint64 (exp_ts, _ts);                                   \
Packit 1f69a5
    fail_unless_equals_int (exp_delay, _delay);                                \
Packit 1f69a5
    fail_unless_equals_uint64 (exp_spacing, _spacing);                         \
Packit 1f69a5
    gst_event_unref (_event);                                                  \
Packit 1f69a5
  } G_STMT_END
Packit 1f69a5
Packit 1f69a5
static gboolean
Packit 1f69a5
verify_jb_stats (GstElement * jb, GstStructure * expected)
Packit 1f69a5
{
Packit 1f69a5
  gboolean ret;
Packit 1f69a5
  GstStructure *actual;
Packit 1f69a5
  g_object_get (jb, "stats", &actual, NULL);
Packit 1f69a5
Packit 1f69a5
  ret = gst_structure_is_subset (actual, expected);
Packit 1f69a5
Packit 1f69a5
  if (!ret) {
Packit 1f69a5
    gchar *e_str = gst_structure_to_string (expected);
Packit 1f69a5
    gchar *a_str = gst_structure_to_string (actual);
Packit 1f69a5
    fail_unless (ret, "%s is not a subset of %s", e_str, a_str);
Packit 1f69a5
    g_free (e_str);
Packit 1f69a5
    g_free (a_str);
Packit 1f69a5
  }
Packit 1f69a5
Packit 1f69a5
  gst_structure_free (expected);
Packit 1f69a5
  gst_structure_free (actual);
Packit 1f69a5
Packit 1f69a5
  return ret;
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
static guint
Packit 1f69a5
construct_deterministic_initial_state (GstHarness * h, gint latency_ms)
Packit 1f69a5
{
Packit 1f69a5
  guint next_seqnum = latency_ms / TEST_BUF_MS + 1;
Packit 1f69a5
  guint seqnum;
Packit 1f69a5
  gint i;
Packit 1f69a5
Packit 1f69a5
  g_assert (latency_ms % TEST_BUF_MS == 0);
Packit 1f69a5
Packit 1f69a5
  gst_harness_set_src_caps (h, generate_caps ());
Packit 1f69a5
  g_object_set (h->element, "latency", latency_ms, NULL);
Packit 1f69a5
Packit 1f69a5
  /* When the first packet arrives in the jitterbuffer, it will create a
Packit 1f69a5
   * timeout for this packet equal to the latency of the jitterbuffer.
Packit 1f69a5
   * This is known as DEADLINE internally, and is meant to allow the stream
Packit 1f69a5
   * to buffer a bit before starting to push it out, to get some ideas about
Packit 1f69a5
   * the nature of the stream. (packetspacing, jitter etc.)
Packit 1f69a5
   *
Packit 1f69a5
   * When writing tests using the test-clock, it it hence important to know
Packit 1f69a5
   * that by simply advancing the clock to this timeout, you are basically
Packit 1f69a5
   * describing a stream that had one initial packet, and then nothing at all
Packit 1f69a5
   * for the duration of the latency (100ms in this test), which is not a very
Packit 1f69a5
   * usual scenario.
Packit 1f69a5
   *
Packit 1f69a5
   * Instead, a pattern used throughout this test-suite, is to keep the buffers
Packit 1f69a5
   * arriving at their optimal time, until the DEADLINE is reached, and that
Packit 1f69a5
   * then becomes the "starting-point" for the test, because at this time
Packit 1f69a5
   * there should now be no waiting timers (unless using rtx) and we have
Packit 1f69a5
   * a "clean" state to craft the test from.
Packit 1f69a5
   */
Packit 1f69a5
Packit 1f69a5
  /* Packet 0 arrives at time 0ms, Packet 5 arrives at time 100ms */
Packit 1f69a5
  for (seqnum = 0; seqnum < next_seqnum; seqnum++) {
Packit 1f69a5
    push_test_buffer (h, seqnum);
Packit 1f69a5
    gst_harness_wait_for_clock_id_waits (h, 1, 60);
Packit 1f69a5
  }
Packit 1f69a5
Packit 1f69a5
  /* We release the DEADLINE timer for packet 0, verify the time is indeed
Packit 1f69a5
   * @latency_ms (100ms) and pull out all the buffers that have been released,
Packit 1f69a5
   * and verify their PTS and sequence numbers.
Packit 1f69a5
   */
Packit 1f69a5
  gst_harness_crank_single_clock_wait (h);
Packit 1f69a5
  fail_unless_equals_int64 (latency_ms * GST_MSECOND,
Packit 1f69a5
      gst_clock_get_time (GST_ELEMENT_CLOCK (h->element)));
Packit 1f69a5
  for (seqnum = 0; seqnum < next_seqnum; seqnum++) {
Packit 1f69a5
    GstBuffer *buf = gst_harness_pull (h);
Packit 1f69a5
    fail_unless_equals_uint64 (seqnum * TEST_BUF_DURATION,
Packit 1f69a5
        GST_BUFFER_PTS (buf));
Packit 1f69a5
    fail_unless_equals_int (seqnum, get_rtp_seq_num (buf));
Packit 1f69a5
    gst_buffer_unref (buf);
Packit 1f69a5
  }
Packit 1f69a5
Packit 1f69a5
  /* drop GstEventStreamStart & GstEventCaps & GstEventSegment */
Packit 1f69a5
  for (i = 0; i < 3; i++)
Packit 1f69a5
    gst_event_unref (gst_harness_pull_event (h));
Packit 1f69a5
Packit 1f69a5
  /* drop reconfigure event */
Packit 1f69a5
  gst_event_unref (gst_harness_pull_upstream_event (h));
Packit 1f69a5
Packit 1f69a5
  /* Verify that at this point our queues are empty */
Packit 1f69a5
  fail_unless_equals_int (0, gst_harness_buffers_in_queue (h));
Packit 1f69a5
  fail_unless_equals_int (0, gst_harness_events_in_queue (h));
Packit 1f69a5
Packit 1f69a5
  return next_seqnum;
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_lost_event)
Packit 1f69a5
{
Packit 1f69a5
  GstHarness *h = gst_harness_new ("rtpjitterbuffer");
Packit 1f69a5
  GstBuffer *buf;
Packit 1f69a5
  gint latency_ms = 100;
Packit 1f69a5
  guint next_seqnum;
Packit 1f69a5
  guint missing_seqnum;
Packit 1f69a5
Packit 1f69a5
  g_object_set (h->element, "do-lost", TRUE, NULL);
Packit 1f69a5
  next_seqnum = construct_deterministic_initial_state (h, latency_ms);
Packit 1f69a5
Packit 1f69a5
  /* We will now create a gap in the stream, by skipping one sequence-number,
Packit 1f69a5
   * and push the following packet.
Packit 1f69a5
   */
Packit 1f69a5
  missing_seqnum = next_seqnum;
Packit 1f69a5
  next_seqnum += 1;
Packit 1f69a5
  push_test_buffer (h, next_seqnum);
Packit 1f69a5
Packit 1f69a5
  /* This packet (@next_seqnum) will now be held back, awaiting the missing one,
Packit 1f69a5
   * verify that this is the case:
Packit 1f69a5
   */
Packit 1f69a5
  fail_unless_equals_int (0, gst_harness_buffers_in_queue (h));
Packit 1f69a5
  fail_unless_equals_int (0, gst_harness_events_in_queue (h));
Packit 1f69a5
Packit 1f69a5
  /* The lost-timeout for the missing packet will now be its pts + latency, so
Packit 1f69a5
   * now we will simply crank the clock to advance to this point in time, and
Packit 1f69a5
   * check that we get a lost-event, as well as the last packet we pushed in.
Packit 1f69a5
   */
Packit 1f69a5
  gst_harness_crank_single_clock_wait (h);
Packit 1f69a5
  verify_lost_event (h, missing_seqnum,
Packit 1f69a5
      missing_seqnum * TEST_BUF_DURATION, TEST_BUF_DURATION);
Packit 1f69a5
Packit 1f69a5
  buf = gst_harness_pull (h);
Packit 1f69a5
  fail_unless_equals_uint64 (next_seqnum * TEST_BUF_DURATION,
Packit 1f69a5
      GST_BUFFER_PTS (buf));
Packit 1f69a5
  fail_unless_equals_int (next_seqnum, get_rtp_seq_num (buf));
Packit 1f69a5
  gst_buffer_unref (buf);
Packit 1f69a5
Packit 1f69a5
  fail_unless (verify_jb_stats (h->element,
Packit 1f69a5
          gst_structure_new ("application/x-rtp-jitterbuffer-stats",
Packit 1f69a5
              "num-pushed", G_TYPE_UINT64, (guint64) next_seqnum,
Packit 1f69a5
              "num-lost", G_TYPE_UINT64, (guint64) 1, NULL)));
Packit 1f69a5
Packit 1f69a5
  gst_harness_teardown (h);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_only_one_lost_event_on_large_gaps)
Packit 1f69a5
{
Packit 1f69a5
  GstHarness *h = gst_harness_new ("rtpjitterbuffer");
Packit 1f69a5
  GstTestClock *testclock;
Packit 1f69a5
  GstBuffer *out_buf;
Packit 1f69a5
  guint next_seqnum;
Packit 1f69a5
  gint latency_ms = 200;
Packit 1f69a5
  gint num_lost_events = latency_ms / TEST_BUF_MS;
Packit 1f69a5
  gint i;
Packit 1f69a5
Packit 1f69a5
  testclock = gst_harness_get_testclock (h);
Packit 1f69a5
  /* Need to set max-misorder-time and max-dropout-time to 0 so the
Packit 1f69a5
   * jitterbuffer does not base them on packet rate calculations.
Packit 1f69a5
   * If it does, out gap is big enough to be considered a new stream and
Packit 1f69a5
   * we wait for a few consecutive packets just to be sure
Packit 1f69a5
   */
Packit 1f69a5
  g_object_set (h->element, "do-lost", TRUE,
Packit 1f69a5
      "max-misorder-time", 0, "max-dropout-time", 0, NULL);
Packit 1f69a5
  next_seqnum = construct_deterministic_initial_state (h, latency_ms);
Packit 1f69a5
Packit 1f69a5
  /* move time ahead to just before 10 seconds */
Packit 1f69a5
  gst_harness_set_time (h, 10 * GST_SECOND - 1);
Packit 1f69a5
Packit 1f69a5
  /* check that we have no pending waits */
Packit 1f69a5
  fail_unless_equals_int (0, gst_test_clock_peek_id_count (testclock));
Packit 1f69a5
Packit 1f69a5
  /* a buffer now arrives perfectly on time */
Packit 1f69a5
  fail_unless_equals_int (GST_FLOW_OK,
Packit 1f69a5
      gst_harness_push (h, generate_test_buffer (500)));
Packit 1f69a5
Packit 1f69a5
  /* release the wait, advancing the clock to 10 sec */
Packit 1f69a5
  fail_unless (gst_harness_crank_single_clock_wait (h));
Packit 1f69a5
Packit 1f69a5
  /* we should now receive a packet-lost-event for buffers 11 through 489 ... */
Packit 1f69a5
  verify_lost_event (h, next_seqnum,
Packit 1f69a5
      next_seqnum * TEST_BUF_DURATION, TEST_BUF_DURATION * (490 - next_seqnum));
Packit 1f69a5
Packit 1f69a5
  /* ... as well as 490 (since at 10 sec 490 is too late) */
Packit 1f69a5
  verify_lost_event (h, 490, 490 * TEST_BUF_DURATION, TEST_BUF_DURATION);
Packit 1f69a5
Packit 1f69a5
  /* we get as many lost events as the the number of *
Packit 1f69a5
   * buffers the jitterbuffer is able to wait for */
Packit 1f69a5
  for (i = 1; i < num_lost_events; i++) {
Packit 1f69a5
    fail_unless (gst_harness_crank_single_clock_wait (h));
Packit 1f69a5
    verify_lost_event (h, 490 + i, (490 + i) * TEST_BUF_DURATION,
Packit 1f69a5
        TEST_BUF_DURATION);
Packit 1f69a5
  }
Packit 1f69a5
Packit 1f69a5
  /* and then the buffer is released */
Packit 1f69a5
  out_buf = gst_harness_pull (h);
Packit 1f69a5
  fail_unless (GST_BUFFER_FLAG_IS_SET (out_buf, GST_BUFFER_FLAG_DISCONT));
Packit 1f69a5
  fail_unless_equals_int (500, get_rtp_seq_num (out_buf));
Packit 1f69a5
  fail_unless_equals_uint64 (10 * GST_SECOND, GST_BUFFER_DTS (out_buf));
Packit 1f69a5
  fail_unless_equals_uint64 (10 * GST_SECOND, GST_BUFFER_PTS (out_buf));
Packit 1f69a5
  gst_buffer_unref (out_buf);
Packit 1f69a5
Packit 1f69a5
  fail_unless (verify_jb_stats (h->element,
Packit 1f69a5
          gst_structure_new ("application/x-rtp-jitterbuffer-stats",
Packit 1f69a5
              "num-lost", G_TYPE_UINT64, (guint64) 489, NULL)));
Packit 1f69a5
Packit 1f69a5
  gst_object_unref (testclock);
Packit 1f69a5
  gst_harness_teardown (h);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_two_lost_one_arrives_in_time)
Packit 1f69a5
{
Packit 1f69a5
  GstHarness *h = gst_harness_new ("rtpjitterbuffer");
Packit 1f69a5
  GstTestClock *testclock;
Packit 1f69a5
  GstClockID id;
Packit 1f69a5
  GstBuffer *buf;
Packit 1f69a5
  gint latency_ms = 100;
Packit 1f69a5
  guint next_seqnum;
Packit 1f69a5
  guint first_missing;
Packit 1f69a5
  guint second_missing;
Packit 1f69a5
  guint current_arrived;
Packit 1f69a5
Packit 1f69a5
  testclock = gst_harness_get_testclock (h);
Packit 1f69a5
  g_object_set (h->element, "do-lost", TRUE, NULL);
Packit 1f69a5
  next_seqnum = construct_deterministic_initial_state (h, latency_ms);
Packit 1f69a5
Packit 1f69a5
  /* hop over 2 packets and make another one (gap of 2) */
Packit 1f69a5
  first_missing = next_seqnum;
Packit 1f69a5
  second_missing = next_seqnum + 1;
Packit 1f69a5
  current_arrived = next_seqnum + 2;
Packit 1f69a5
  push_test_buffer (h, current_arrived);
Packit 1f69a5
Packit 1f69a5
  /* verify that the jitterbuffer now wait for the latest moment it can push the
Packit 1f69a5
   * @first_missing packet out.
Packit 1f69a5
   */
Packit 1f69a5
  gst_test_clock_wait_for_next_pending_id (testclock, &id;;
Packit 1f69a5
  fail_unless_equals_uint64 (first_missing * TEST_BUF_DURATION +
Packit 1f69a5
      latency_ms * GST_MSECOND, gst_clock_id_get_time (id));
Packit 1f69a5
  gst_clock_id_unref (id);
Packit 1f69a5
Packit 1f69a5
  /* let the time expire... */
Packit 1f69a5
  fail_unless (gst_harness_crank_single_clock_wait (h));
Packit 1f69a5
Packit 1f69a5
  /* we should now receive a packet-lost-event */
Packit 1f69a5
  verify_lost_event (h, first_missing,
Packit 1f69a5
      first_missing * TEST_BUF_DURATION, TEST_BUF_DURATION);
Packit 1f69a5
Packit 1f69a5
  /* @second_missing now arrives just in time */
Packit 1f69a5
  fail_unless_equals_int (GST_FLOW_OK,
Packit 1f69a5
      gst_harness_push (h, generate_test_buffer (second_missing)));
Packit 1f69a5
Packit 1f69a5
  /* verify that @second_missing made it through! */
Packit 1f69a5
  buf = gst_harness_pull (h);
Packit 1f69a5
  fail_unless (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT));
Packit 1f69a5
  fail_unless_equals_int (second_missing, get_rtp_seq_num (buf));
Packit 1f69a5
  gst_buffer_unref (buf);
Packit 1f69a5
Packit 1f69a5
  /* and see that @current_arrived now also is pushed */
Packit 1f69a5
  buf = gst_harness_pull (h);
Packit 1f69a5
  fail_unless (!GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT));
Packit 1f69a5
  fail_unless_equals_int (current_arrived, get_rtp_seq_num (buf));
Packit 1f69a5
  gst_buffer_unref (buf);
Packit 1f69a5
Packit 1f69a5
  fail_unless (verify_jb_stats (h->element,
Packit 1f69a5
          gst_structure_new ("application/x-rtp-jitterbuffer-stats",
Packit 1f69a5
              "num-pushed", G_TYPE_UINT64, (guint64) next_seqnum + 2,
Packit 1f69a5
              "num-lost", G_TYPE_UINT64, (guint64) 1, NULL)));
Packit 1f69a5
Packit 1f69a5
  gst_object_unref (testclock);
Packit 1f69a5
  gst_harness_teardown (h);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_late_packets_still_makes_lost_events)
Packit 1f69a5
{
Packit 1f69a5
  GstHarness *h = gst_harness_new ("rtpjitterbuffer");
Packit 1f69a5
  GstBuffer *out_buf;
Packit 1f69a5
  gint latency_ms = 100;
Packit 1f69a5
  guint next_seqnum;
Packit 1f69a5
  guint seqnum;
Packit 1f69a5
  GstClockTime now;
Packit 1f69a5
Packit 1f69a5
  g_object_set (h->element, "do-lost", TRUE, NULL);
Packit 1f69a5
  next_seqnum = construct_deterministic_initial_state (h, latency_ms);
Packit 1f69a5
Packit 1f69a5
  /* jump 10 seconds forward in time */
Packit 1f69a5
  now = 10 * GST_SECOND;
Packit 1f69a5
  gst_harness_set_time (h, now);
Packit 1f69a5
Packit 1f69a5
  /* push a packet with a gap of 2, that now is very late */
Packit 1f69a5
  seqnum = next_seqnum + 2;
Packit 1f69a5
  fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (h,
Packit 1f69a5
          generate_test_buffer_full (now,
Packit 1f69a5
              seqnum, seqnum * TEST_RTP_TS_DURATION)));
Packit 1f69a5
Packit 1f69a5
  /* we should now receive packet-lost-events for the gap 
Packit 1f69a5
   * FIXME: The timeout and duration here are a bit crap...
Packit 1f69a5
   */
Packit 1f69a5
  verify_lost_event (h, next_seqnum, 3400 * GST_MSECOND, 6500 * GST_MSECOND);
Packit 1f69a5
  verify_lost_event (h, next_seqnum + 1,
Packit 1f69a5
      9900 * GST_MSECOND, 3300 * GST_MSECOND);
Packit 1f69a5
Packit 1f69a5
  /* verify that packet @seqnum made it through! */
Packit 1f69a5
  out_buf = gst_harness_pull (h);
Packit 1f69a5
  fail_unless (GST_BUFFER_FLAG_IS_SET (out_buf, GST_BUFFER_FLAG_DISCONT));
Packit 1f69a5
  fail_unless_equals_int (seqnum, get_rtp_seq_num (out_buf));
Packit 1f69a5
  gst_buffer_unref (out_buf);
Packit 1f69a5
Packit 1f69a5
  fail_unless (verify_jb_stats (h->element,
Packit 1f69a5
          gst_structure_new ("application/x-rtp-jitterbuffer-stats",
Packit 1f69a5
              "num-pushed", G_TYPE_UINT64, (guint64) next_seqnum + 1,
Packit 1f69a5
              "num-lost", G_TYPE_UINT64, (guint64) 2, NULL)));
Packit 1f69a5
Packit 1f69a5
  gst_harness_teardown (h);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_num_late_when_considered_lost_arrives)
Packit 1f69a5
{
Packit 1f69a5
  GstHarness *h = gst_harness_new ("rtpjitterbuffer");
Packit 1f69a5
  gboolean do_lost = __i__ != 0;
Packit 1f69a5
  gint latency_ms = 100;
Packit 1f69a5
  guint next_seqnum;
Packit 1f69a5
Packit 1f69a5
  g_object_set (h->element, "do-lost", do_lost, NULL);
Packit 1f69a5
  next_seqnum = construct_deterministic_initial_state (h, latency_ms);
Packit 1f69a5
Packit 1f69a5
  /* gap of 1 */
Packit 1f69a5
  push_test_buffer (h, next_seqnum + 1);
Packit 1f69a5
Packit 1f69a5
  /* crank to trigger lost-event */
Packit 1f69a5
  gst_harness_crank_single_clock_wait (h);
Packit 1f69a5
Packit 1f69a5
  if (do_lost) {
Packit 1f69a5
    /* we should now receive packet-lost-events for the missing packet */
Packit 1f69a5
    verify_lost_event (h, next_seqnum,
Packit 1f69a5
        next_seqnum * TEST_BUF_DURATION, TEST_BUF_DURATION);
Packit 1f69a5
  }
Packit 1f69a5
Packit 1f69a5
  /* pull out the pushed packet */
Packit 1f69a5
  gst_buffer_unref (gst_harness_pull (h));
Packit 1f69a5
Packit 1f69a5
  /* we have one lost packet in the stats */
Packit 1f69a5
  fail_unless (verify_jb_stats (h->element,
Packit 1f69a5
          gst_structure_new ("application/x-rtp-jitterbuffer-stats",
Packit 1f69a5
              "num-pushed", G_TYPE_UINT64, (guint64) next_seqnum + 1,
Packit 1f69a5
              "num-lost", G_TYPE_UINT64, (guint64) 1,
Packit 1f69a5
              "num-late", G_TYPE_UINT64, (guint64) 0, NULL)));
Packit 1f69a5
Packit 1f69a5
  /* the missing packet now arrives (too late) */
Packit 1f69a5
  fail_unless_equals_int (GST_FLOW_OK,
Packit 1f69a5
      gst_harness_push (h, generate_test_buffer (next_seqnum)));
Packit 1f69a5
Packit 1f69a5
  /* and this increments num-late */
Packit 1f69a5
  fail_unless (verify_jb_stats (h->element,
Packit 1f69a5
          gst_structure_new ("application/x-rtp-jitterbuffer-stats",
Packit 1f69a5
              "num-pushed", G_TYPE_UINT64, (guint64) next_seqnum + 1,
Packit 1f69a5
              "num-lost", G_TYPE_UINT64, (guint64) 1,
Packit 1f69a5
              "num-late", G_TYPE_UINT64, (guint64) 1, NULL)));
Packit 1f69a5
Packit 1f69a5
  gst_harness_teardown (h);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_lost_event_uses_pts)
Packit 1f69a5
{
Packit 1f69a5
  GstHarness *h = gst_harness_new ("rtpjitterbuffer");
Packit 1f69a5
  GstClockTime now;
Packit 1f69a5
  gint latency_ms = 100;
Packit 1f69a5
  guint next_seqnum;
Packit 1f69a5
  guint lost_seqnum;
Packit 1f69a5
Packit 1f69a5
  g_object_set (h->element, "do-lost", TRUE, NULL);
Packit 1f69a5
  next_seqnum = construct_deterministic_initial_state (h, latency_ms);
Packit 1f69a5
Packit 1f69a5
  /* hop over 1 packets and make another one (gap of 1), but due to
Packit 1f69a5
     network delays, this packets is also grossly late */
Packit 1f69a5
  lost_seqnum = next_seqnum;
Packit 1f69a5
  next_seqnum += 1;
Packit 1f69a5
Packit 1f69a5
  /* advance the clock to the latest time packet @next_seqnum could arrive */
Packit 1f69a5
  now = next_seqnum * TEST_BUF_DURATION + latency_ms * GST_MSECOND;
Packit 1f69a5
  gst_harness_set_time (h, now);
Packit 1f69a5
  gst_harness_push (h, generate_test_buffer_full (now, next_seqnum,
Packit 1f69a5
          next_seqnum * TEST_RTP_TS_DURATION));
Packit 1f69a5
Packit 1f69a5
  /* we should now have received a packet-lost-event for buffer 3 */
Packit 1f69a5
  verify_lost_event (h, lost_seqnum,
Packit 1f69a5
      lost_seqnum * TEST_BUF_DURATION, TEST_BUF_DURATION);
Packit 1f69a5
Packit 1f69a5
  /* and pull out packet 4 */
Packit 1f69a5
  gst_buffer_unref (gst_harness_pull (h));
Packit 1f69a5
Packit 1f69a5
  fail_unless (verify_jb_stats (h->element,
Packit 1f69a5
          gst_structure_new ("application/x-rtp-jitterbuffer-stats",
Packit 1f69a5
              "num-pushed", G_TYPE_UINT64, (guint64) next_seqnum,
Packit 1f69a5
              "num-lost", G_TYPE_UINT64, (guint64) 1, NULL)));
Packit 1f69a5
Packit 1f69a5
  gst_harness_teardown (h);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_lost_event_with_backwards_rtptime)
Packit 1f69a5
{
Packit 1f69a5
  GstHarness *h = gst_harness_new ("rtpjitterbuffer");
Packit 1f69a5
  gint latency_ms = 40;
Packit 1f69a5
Packit 1f69a5
  g_object_set (h->element, "do-lost", TRUE, NULL);
Packit 1f69a5
  construct_deterministic_initial_state (h, latency_ms);
Packit 1f69a5
Packit 1f69a5
  /*
Packit 1f69a5
   * For video using B-frames, an expected sequence
Packit 1f69a5
   * could be like this:
Packit 1f69a5
   * (I = I-frame, P = P-frame, B = B-frame)
Packit 1f69a5
   *               ___   ___   ___   ___   ___
Packit 1f69a5
   *          ... | 3 | | 4 | | 5 | | 6 | | 7 |
Packit 1f69a5
   *               –––   –––   –––   –––   –––
Packit 1f69a5
   * rtptime:       3(I)  5(P)  5(P)  4(B)  6(P)
Packit 1f69a5
   * arrival(dts):  3     5     5     5     6
Packit 1f69a5
   *
Packit 1f69a5
   * Notice here that packet 6 (the B frame) make
Packit 1f69a5
   * the rtptime go backwards.
Packit 1f69a5
   *
Packit 1f69a5
   * But we get this:
Packit 1f69a5
   *               ___   ___   _ _   ___   ___
Packit 1f69a5
   *          ... | 3 | | 4 | |   | | 6 | | 7 |
Packit 1f69a5
   *               –––   –––   - -   –––   –––
Packit 1f69a5
   * rtptime:       3(I)  5(P)        4(B)  6(P)
Packit 1f69a5
   * arrival(dts):  3     5           5     6
Packit 1f69a5
   *
Packit 1f69a5
   */
Packit 1f69a5
Packit 1f69a5
  /* seqnum 3 */
Packit 1f69a5
  push_test_buffer (h, 3);
Packit 1f69a5
  gst_buffer_unref (gst_harness_pull (h));
Packit 1f69a5
Packit 1f69a5
  /* seqnum 4, arriving at time 5 with rtptime 5 */
Packit 1f69a5
  gst_harness_push (h,
Packit 1f69a5
      generate_test_buffer_full (5 * TEST_BUF_DURATION,
Packit 1f69a5
          4, 5 * TEST_RTP_TS_DURATION));
Packit 1f69a5
  gst_buffer_unref (gst_harness_pull (h));
Packit 1f69a5
Packit 1f69a5
  /* seqnum 6, arriving at time 5 with rtptime 4,
Packit 1f69a5
     making a gap for missing seqnum 5 */
Packit 1f69a5
  gst_harness_push (h,
Packit 1f69a5
      generate_test_buffer_full (5 * TEST_BUF_DURATION,
Packit 1f69a5
          6, 4 * TEST_RTP_TS_DURATION));
Packit 1f69a5
Packit 1f69a5
  /* seqnum 7, arriving at time 6 with rtptime 6 */
Packit 1f69a5
  gst_harness_push (h,
Packit 1f69a5
      generate_test_buffer_full (6 * TEST_BUF_DURATION,
Packit 1f69a5
          7, 6 * TEST_RTP_TS_DURATION));
Packit 1f69a5
Packit 1f69a5
  /* we should now have received a packet-lost-event for seqnum 5,
Packit 1f69a5
     with time 5 and 0 duration */
Packit 1f69a5
  gst_harness_crank_single_clock_wait (h);
Packit 1f69a5
  verify_lost_event (h, 5, 5 * TEST_BUF_DURATION, 0);
Packit 1f69a5
Packit 1f69a5
  /* and pull out 6 and 7 */
Packit 1f69a5
  gst_buffer_unref (gst_harness_pull (h));
Packit 1f69a5
  gst_buffer_unref (gst_harness_pull (h));
Packit 1f69a5
Packit 1f69a5
  fail_unless (verify_jb_stats (h->element,
Packit 1f69a5
          gst_structure_new ("application/x-rtp-jitterbuffer-stats",
Packit 1f69a5
              "num-pushed", G_TYPE_UINT64, (guint64) 7,
Packit 1f69a5
              "num-lost", G_TYPE_UINT64, (guint64) 1, NULL)));
Packit 1f69a5
Packit 1f69a5
  gst_harness_teardown (h);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_all_packets_are_timestamped_zero)
Packit 1f69a5
{
Packit 1f69a5
  GstHarness *h = gst_harness_new ("rtpjitterbuffer");
Packit 1f69a5
  GstBuffer *out_buf;
Packit 1f69a5
  gint jb_latency_ms = 100;
Packit 1f69a5
  gint i, b;
Packit 1f69a5
Packit 1f69a5
  gst_harness_set_src_caps (h, generate_caps ());
Packit 1f69a5
  g_object_set (h->element, "do-lost", TRUE, "latency", jb_latency_ms, NULL);
Packit 1f69a5
Packit 1f69a5
  /* advance the clock with 10 seconds */
Packit 1f69a5
  gst_harness_set_time (h, 10 * GST_SECOND);
Packit 1f69a5
Packit 1f69a5
  /* push the first buffer through */
Packit 1f69a5
  gst_buffer_unref (gst_harness_push_and_pull (h, generate_test_buffer (0)));
Packit 1f69a5
Packit 1f69a5
  /* push some buffers in, all timestamped 0 */
Packit 1f69a5
  for (b = 1; b < 3; b++) {
Packit 1f69a5
    fail_unless_equals_int (GST_FLOW_OK,
Packit 1f69a5
        gst_harness_push (h,
Packit 1f69a5
            generate_test_buffer_full (0 * GST_MSECOND, b, 0)));
Packit 1f69a5
Packit 1f69a5
    /* check for the buffer coming out that was pushed in */
Packit 1f69a5
    out_buf = gst_harness_pull (h);
Packit 1f69a5
    fail_unless_equals_uint64 (0, GST_BUFFER_DTS (out_buf));
Packit 1f69a5
    fail_unless_equals_uint64 (0, GST_BUFFER_PTS (out_buf));
Packit 1f69a5
    gst_buffer_unref (out_buf);
Packit 1f69a5
  }
Packit 1f69a5
Packit 1f69a5
  /* hop over 2 packets and make another one (gap of 2) */
Packit 1f69a5
  b = 5;
Packit 1f69a5
  fail_unless_equals_int (GST_FLOW_OK,
Packit 1f69a5
      gst_harness_push (h, generate_test_buffer_full (0 * GST_MSECOND, b, 0)));
Packit 1f69a5
Packit 1f69a5
  /* drop GstEventStreamStart & GstEventCaps & GstEventSegment */
Packit 1f69a5
  for (i = 0; i < 3; i++)
Packit 1f69a5
    gst_event_unref (gst_harness_pull_event (h));
Packit 1f69a5
Packit 1f69a5
  /* we should now receive packet-lost-events for buffer 3 and 4 */
Packit 1f69a5
  verify_lost_event (h, 3, 0, 0);
Packit 1f69a5
  verify_lost_event (h, 4, 0, 0);
Packit 1f69a5
Packit 1f69a5
  /* verify that buffer 5 made it through! */
Packit 1f69a5
  out_buf = gst_harness_pull (h);
Packit 1f69a5
  fail_unless (GST_BUFFER_FLAG_IS_SET (out_buf, GST_BUFFER_FLAG_DISCONT));
Packit 1f69a5
  fail_unless_equals_int (5, get_rtp_seq_num (out_buf));
Packit 1f69a5
  gst_buffer_unref (out_buf);
Packit 1f69a5
Packit 1f69a5
  fail_unless (verify_jb_stats (h->element,
Packit 1f69a5
          gst_structure_new ("application/x-rtp-jitterbuffer-stats",
Packit 1f69a5
              "num-pushed", G_TYPE_UINT64, (guint64) 4,
Packit 1f69a5
              "num-lost", G_TYPE_UINT64, (guint64) 2, NULL)));
Packit 1f69a5
Packit 1f69a5
  gst_harness_teardown (h);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_reorder_of_non_equidistant_packets)
Packit 1f69a5
{
Packit 1f69a5
  GstHarness *h = gst_harness_new ("rtpjitterbuffer");
Packit 1f69a5
  GstTestClock *testclock;
Packit 1f69a5
  gint latency_ms = 5;
Packit 1f69a5
  GstClockID pending_id;
Packit 1f69a5
  GstClockTime time;
Packit 1f69a5
  gint seq, frame;
Packit 1f69a5
  gint num_init_frames = 1;
Packit 1f69a5
  const GstClockTime frame_dur = TEST_BUF_DURATION;
Packit 1f69a5
  const guint32 frame_rtp_ts_dur = TEST_RTP_TS_DURATION;
Packit 1f69a5
Packit 1f69a5
  gst_harness_set_src_caps (h, generate_caps ());
Packit 1f69a5
  testclock = gst_harness_get_testclock (h);
Packit 1f69a5
  g_object_set (h->element, "do-lost", TRUE, "latency", latency_ms, NULL);
Packit 1f69a5
Packit 1f69a5
  for (frame = 0, seq = 0; frame < num_init_frames; frame++, seq += 2) {
Packit 1f69a5
    /* Push a couple of packets with identical timestamp, typical for a video
Packit 1f69a5
     * stream where one frame generates multiple packets. */
Packit 1f69a5
    gst_harness_set_time (h, frame * frame_dur);
Packit 1f69a5
    gst_harness_push (h, generate_test_buffer_full (frame * frame_dur,
Packit 1f69a5
            seq, frame * frame_rtp_ts_dur));
Packit 1f69a5
    gst_harness_push (h, generate_test_buffer_full (frame * frame_dur,
Packit 1f69a5
            seq + 1, frame * frame_rtp_ts_dur));
Packit 1f69a5
Packit 1f69a5
    if (frame == 0)
Packit 1f69a5
      /* deadline for buffer 0 expires */
Packit 1f69a5
      gst_harness_crank_single_clock_wait (h);
Packit 1f69a5
Packit 1f69a5
    gst_buffer_unref (gst_harness_pull (h));
Packit 1f69a5
    gst_buffer_unref (gst_harness_pull (h));
Packit 1f69a5
  }
Packit 1f69a5
Packit 1f69a5
  /* Finally push the last frame reordered */
Packit 1f69a5
  gst_harness_set_time (h, frame * frame_dur);
Packit 1f69a5
  gst_harness_push (h, generate_test_buffer_full (frame * frame_dur,
Packit 1f69a5
          seq + 1, frame * frame_rtp_ts_dur));
Packit 1f69a5
Packit 1f69a5
  /* Check the scheduled lost timer. The expected arrival of this packet
Packit 1f69a5
   * should be assumed to be the same as the last packet received since we
Packit 1f69a5
   * don't know wether the missing packet belonged to this or previous
Packit 1f69a5
   * frame. */
Packit 1f69a5
  gst_test_clock_wait_for_next_pending_id (testclock, &pending_id);
Packit 1f69a5
  time = gst_clock_id_get_time (pending_id);
Packit 1f69a5
  fail_unless_equals_int64 (time, frame * frame_dur + latency_ms * GST_MSECOND);
Packit 1f69a5
  gst_clock_id_unref (pending_id);
Packit 1f69a5
Packit 1f69a5
  /* And then missing packet arrives just in time */
Packit 1f69a5
  gst_harness_set_time (h, time - 1);
Packit 1f69a5
  gst_harness_push (h, generate_test_buffer_full (time - 1, seq,
Packit 1f69a5
          frame * frame_rtp_ts_dur));
Packit 1f69a5
Packit 1f69a5
  gst_buffer_unref (gst_harness_pull (h));
Packit 1f69a5
  gst_buffer_unref (gst_harness_pull (h));
Packit 1f69a5
Packit 1f69a5
  gst_object_unref (testclock);
Packit 1f69a5
  gst_harness_teardown (h);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_loss_equidistant_spacing_with_parameter_packets)
Packit 1f69a5
{
Packit 1f69a5
  GstHarness *h = gst_harness_new ("rtpjitterbuffer");
Packit 1f69a5
  gint latency_ms = 5;
Packit 1f69a5
  gint seq, frame;
Packit 1f69a5
  gint num_init_frames = 10;
Packit 1f69a5
  gint i;
Packit 1f69a5
Packit 1f69a5
  gst_harness_set_src_caps (h, generate_caps ());
Packit 1f69a5
  g_object_set (h->element, "do-lost", TRUE, "latency", latency_ms, NULL);
Packit 1f69a5
Packit 1f69a5
  /* drop stream-start, caps, segment */
Packit 1f69a5
  for (i = 0; i < 3; i++)
Packit 1f69a5
    gst_event_unref (gst_harness_pull_event (h));
Packit 1f69a5
Packit 1f69a5
  for (frame = 0, seq = 0; frame < num_init_frames; frame++, seq++) {
Packit 1f69a5
    gst_harness_set_time (h, frame * TEST_BUF_DURATION);
Packit 1f69a5
    gst_harness_push (h, generate_test_buffer_full (frame * TEST_BUF_DURATION,
Packit 1f69a5
            seq, frame * TEST_RTP_TS_DURATION));
Packit 1f69a5
Packit 1f69a5
    if (frame == 0)
Packit 1f69a5
      /* deadline for buffer 0 expires */
Packit 1f69a5
      gst_harness_crank_single_clock_wait (h);
Packit 1f69a5
Packit 1f69a5
    gst_buffer_unref (gst_harness_pull (h));
Packit 1f69a5
  }
Packit 1f69a5
Packit 1f69a5
  /* Push three packets with same rtptime, simulating parameter packets +
Packit 1f69a5
   * frame. This should not disable equidistant mode as it is common for
Packit 1f69a5
   * certain audio codecs. */
Packit 1f69a5
  for (i = 0; i < 3; i++) {
Packit 1f69a5
    gst_harness_set_time (h, frame * TEST_BUF_DURATION);
Packit 1f69a5
    gst_harness_push (h, generate_test_buffer_full (frame * TEST_BUF_DURATION,
Packit 1f69a5
            seq++, frame * TEST_RTP_TS_DURATION));
Packit 1f69a5
    gst_buffer_unref (gst_harness_pull (h));
Packit 1f69a5
  }
Packit 1f69a5
  frame++;
Packit 1f69a5
Packit 1f69a5
  /* Finally push the last packet introducing a gap */
Packit 1f69a5
  gst_harness_set_time (h, frame * TEST_BUF_DURATION);
Packit 1f69a5
  gst_harness_push (h, generate_test_buffer_full (frame * TEST_BUF_DURATION,
Packit 1f69a5
          seq + 1, frame * TEST_RTP_TS_DURATION));
Packit 1f69a5
Packit 1f69a5
  /* Check that the lost event has been generated assuming equidistant
Packit 1f69a5
   * spacing. */
Packit 1f69a5
  verify_lost_event (h, seq,
Packit 1f69a5
      frame * TEST_BUF_DURATION - TEST_BUF_DURATION / 2, TEST_BUF_DURATION / 2);
Packit 1f69a5
Packit 1f69a5
  gst_buffer_unref (gst_harness_pull (h));
Packit 1f69a5
Packit 1f69a5
  gst_harness_teardown (h);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
Packit 1f69a5
static void
Packit 1f69a5
gst_test_clock_set_time_and_process (GstTestClock * testclock,
Packit 1f69a5
    GstClockTime time)
Packit 1f69a5
{
Packit 1f69a5
  GstClockID id, tid;
Packit 1f69a5
  gst_test_clock_wait_for_next_pending_id (testclock, &id;;
Packit 1f69a5
  gst_test_clock_set_time (testclock, time);
Packit 1f69a5
  tid = gst_test_clock_process_next_clock_id (testclock);
Packit 1f69a5
  g_assert (tid == id);
Packit 1f69a5
  gst_clock_id_unref (tid);
Packit 1f69a5
  gst_clock_id_unref (id);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_rtx_expected_next)
Packit 1f69a5
{
Packit 1f69a5
  GstHarness *h = gst_harness_new ("rtpjitterbuffer");
Packit 1f69a5
  gint latency_ms = 200;
Packit 1f69a5
  guint next_seqnum;
Packit 1f69a5
  GstClockTime timeout;
Packit 1f69a5
  gint rtx_delay_ms;
Packit 1f69a5
  const GstClockTime rtx_retry_timeout_ms = 40;
Packit 1f69a5
Packit 1f69a5
  g_object_set (h->element, "do-lost", TRUE, NULL);
Packit 1f69a5
  g_object_set (h->element, "do-retransmission", TRUE, NULL);
Packit 1f69a5
  g_object_set (h->element, "rtx-retry-period", 120, NULL);
Packit 1f69a5
  next_seqnum = construct_deterministic_initial_state (h, latency_ms);
Packit 1f69a5
Packit 1f69a5
  /* At this point there is already existing a rtx-timer for @next_seqnum,
Packit 1f69a5
   * that will have a timeout of the expected arrival-time for that seqnum,
Packit 1f69a5
   * and a delay equal to 2*jitter==0 and 0.5*packet_spacing==10ms */
Packit 1f69a5
  timeout = next_seqnum * TEST_BUF_DURATION;
Packit 1f69a5
  rtx_delay_ms = 0.5 * TEST_BUF_MS;
Packit 1f69a5
Packit 1f69a5
  /* We crank the clock to time-out the next scheduled timer */
Packit 1f69a5
  gst_harness_crank_single_clock_wait (h);
Packit 1f69a5
  verify_rtx_event (h, next_seqnum, timeout, rtx_delay_ms, TEST_BUF_DURATION);
Packit 1f69a5
Packit 1f69a5
  /* now we wait for the next timeout, all following timeouts 40ms in the
Packit 1f69a5
   * future because this is rtx-retry-timeout */
Packit 1f69a5
  rtx_delay_ms += rtx_retry_timeout_ms;
Packit 1f69a5
  gst_harness_crank_single_clock_wait (h);
Packit 1f69a5
  verify_rtx_event (h, next_seqnum, timeout, rtx_delay_ms, TEST_BUF_DURATION);
Packit 1f69a5
Packit 1f69a5
  /* And a third time... */
Packit 1f69a5
  rtx_delay_ms += rtx_retry_timeout_ms;
Packit 1f69a5
  gst_harness_crank_single_clock_wait (h);
Packit 1f69a5
  verify_rtx_event (h, next_seqnum, timeout, rtx_delay_ms, TEST_BUF_DURATION);
Packit 1f69a5
Packit 1f69a5
  /* we should now receive a packet-lost-event for packet @next_seqnum */
Packit 1f69a5
  gst_harness_crank_single_clock_wait (h);
Packit 1f69a5
  verify_lost_event (h, next_seqnum, timeout, TEST_BUF_DURATION);
Packit 1f69a5
Packit 1f69a5
  gst_harness_teardown (h);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_rtx_two_missing)
Packit 1f69a5
{
Packit 1f69a5
  GstHarness *h = gst_harness_new ("rtpjitterbuffer");
Packit 1f69a5
  gint latency_ms = 200;
Packit 1f69a5
  guint next_seqnum;
Packit 1f69a5
  GstClockTime last_rtx_request, now;
Packit 1f69a5
  gint rtx_delay_ms = 0.5 * TEST_BUF_MS;
Packit 1f69a5
Packit 1f69a5
  g_object_set (h->element, "do-retransmission", TRUE, NULL);
Packit 1f69a5
  next_seqnum = construct_deterministic_initial_state (h, latency_ms);
Packit 1f69a5
  fail_unless_equals_int (11, next_seqnum);
Packit 1f69a5
Packit 1f69a5
  /*
Packit 1f69a5
   * The expected sequence of buffers is this:
Packit 1f69a5
   *      ____   ____   ____   ____
Packit 1f69a5
   * ... | 10 | | 11 | | 12 | | 13 |
Packit 1f69a5
   *      ––––   ––––   ––––   ––––
Packit 1f69a5
   *      200ms  220ms  240ms  260ms
Packit 1f69a5
   *
Packit 1f69a5
   * But instead we get this:
Packit 1f69a5
   *      ____    _ _    _ _   ____
Packit 1f69a5
   * ... | 10 |  |   |  |   | | 13 |
Packit 1f69a5
   *      ––––    - -    - -   ––––
Packit 1f69a5
   *      200ms                260ms
Packit 1f69a5
   *
Packit 1f69a5
   * Now it is important to note that the next thing that happens is that
Packit 1f69a5
   * the RTX timeout for packet 11 will happen at time 230ms, so we crank
Packit 1f69a5
   * the timer thread to advance the time to this:
Packit 1f69a5
   */
Packit 1f69a5
  gst_harness_crank_single_clock_wait (h);
Packit 1f69a5
  verify_rtx_event (h, 11, 11 * TEST_BUF_DURATION,
Packit 1f69a5
      rtx_delay_ms, TEST_BUF_DURATION);
Packit 1f69a5
  last_rtx_request = gst_clock_get_time (GST_ELEMENT_CLOCK (h->element));
Packit 1f69a5
  fail_unless_equals_int64 (last_rtx_request,
Packit 1f69a5
      11 * TEST_BUF_DURATION + rtx_delay_ms * GST_MSECOND);
Packit 1f69a5
  gst_harness_wait_for_clock_id_waits (h, 1, 60);
Packit 1f69a5
Packit 1f69a5
  /* The next scheduled RTX for packet 11 is now at 230 + 40 = 270ms,
Packit 1f69a5
     so the next thing that happens is that buffer 13 arrives in perfect time: */
Packit 1f69a5
  now = 13 * TEST_BUF_DURATION;
Packit 1f69a5
  gst_harness_set_time (h, now);
Packit 1f69a5
  fail_unless_equals_int (GST_FLOW_OK,
Packit 1f69a5
      gst_harness_push (h,
Packit 1f69a5
          generate_test_buffer_full (now, 13, 13 * TEST_RTP_TS_DURATION)));
Packit 1f69a5
Packit 1f69a5
  /*
Packit 1f69a5
   *
Packit 1f69a5
   * This will estimate the dts on the two missing packets to:
Packit 1f69a5
   *      ____   ____
Packit 1f69a5
   * ... | 11 | | 12 | ...
Packit 1f69a5
   *      ––––   ––––
Packit 1f69a5
   *      220ms  240ms
Packit 1f69a5
   *
Packit 1f69a5
   * And given their regular interspacing of 20ms, it will schedule two RTX
Packit 1f69a5
   * timers for them like so:
Packit 1f69a5
   *
Packit 1f69a5
   *      ____   ____
Packit 1f69a5
   * ... | 11 | | 12 | ...
Packit 1f69a5
   *      ––––   ––––
Packit 1f69a5
   *      230ms  250ms
Packit 1f69a5
   *
Packit 1f69a5
   * There are however two problems, packet 11 we have already sent one RTX for
Packit 1f69a5
   * and its timeout is currently at 270ms, so we should not tamper with that,
Packit 1f69a5
   * and as for packet 12, 250ms has already expired, so we now expect to see
Packit 1f69a5
   * an rtx-event being sent for packet 12 immediately:
Packit 1f69a5
   */
Packit 1f69a5
  verify_rtx_event (h, 12, 12 * TEST_BUF_DURATION,
Packit 1f69a5
      rtx_delay_ms, TEST_BUF_DURATION);
Packit 1f69a5
Packit 1f69a5
  /* and another crank will see the second RTX event being sent for packet 11 */
Packit 1f69a5
  gst_harness_crank_single_clock_wait (h);
Packit 1f69a5
  rtx_delay_ms += 40;
Packit 1f69a5
  verify_rtx_event (h, 11, 11 * TEST_BUF_DURATION,
Packit 1f69a5
      rtx_delay_ms, TEST_BUF_DURATION);
Packit 1f69a5
  last_rtx_request = gst_clock_get_time (GST_ELEMENT_CLOCK (h->element));
Packit 1f69a5
  fail_unless_equals_int64 (last_rtx_request,
Packit 1f69a5
      11 * TEST_BUF_DURATION + rtx_delay_ms * GST_MSECOND);
Packit 1f69a5
Packit 1f69a5
  gst_harness_teardown (h);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_rtx_buffer_arrives_just_in_time)
Packit 1f69a5
{
Packit 1f69a5
  GstHarness *h = gst_harness_new ("rtpjitterbuffer");
Packit 1f69a5
  gint latency_ms = 5 * TEST_BUF_MS;
Packit 1f69a5
  gint next_seqnum;
Packit 1f69a5
  GstBuffer *buffer;
Packit 1f69a5
  GstClockTime now, last_rtx_request;
Packit 1f69a5
  gint rtx_delay_ms = 0.5 * TEST_BUF_MS;
Packit 1f69a5
Packit 1f69a5
  g_object_set (h->element, "do-retransmission", TRUE,
Packit 1f69a5
      "rtx-max-retries", 1, NULL);
Packit 1f69a5
  next_seqnum = construct_deterministic_initial_state (h, latency_ms);
Packit 1f69a5
Packit 1f69a5
  /* Crank clock to send retransmission events requesting seqnum 6 which has
Packit 1f69a5
   * not arrived yet. */
Packit 1f69a5
  gst_harness_crank_single_clock_wait (h);
Packit 1f69a5
  verify_rtx_event (h, next_seqnum,
Packit 1f69a5
      next_seqnum * TEST_BUF_DURATION, rtx_delay_ms, TEST_BUF_DURATION);
Packit 1f69a5
Packit 1f69a5
  last_rtx_request = gst_clock_get_time (GST_ELEMENT_CLOCK (h->element));
Packit 1f69a5
  fail_unless_equals_int64 (last_rtx_request,
Packit 1f69a5
      next_seqnum * TEST_BUF_DURATION + rtx_delay_ms * GST_MSECOND);
Packit 1f69a5
Packit 1f69a5
  /* seqnum 6 arrives just before it times out and is considered lost */
Packit 1f69a5
  now = 200 * GST_MSECOND;
Packit 1f69a5
  gst_harness_set_time (h, now);
Packit 1f69a5
  fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (h,
Packit 1f69a5
          generate_test_buffer_rtx (now, next_seqnum)));
Packit 1f69a5
  buffer = gst_harness_pull (h);
Packit 1f69a5
  fail_unless_equals_int (next_seqnum, get_rtp_seq_num (buffer));
Packit 1f69a5
  gst_buffer_unref (buffer);
Packit 1f69a5
Packit 1f69a5
  fail_unless (verify_jb_stats (h->element,
Packit 1f69a5
          gst_structure_new ("application/x-rtp-jitterbuffer-stats",
Packit 1f69a5
              "num-pushed", G_TYPE_UINT64, (guint64) next_seqnum + 1,
Packit 1f69a5
              "num-lost", G_TYPE_UINT64, (guint64) 0,
Packit 1f69a5
              "rtx-count", G_TYPE_UINT64, (guint64) 1,
Packit 1f69a5
              "rtx-success-count", G_TYPE_UINT64, (guint64) 1,
Packit 1f69a5
              "rtx-per-packet", G_TYPE_DOUBLE, 1.0,
Packit 1f69a5
              "rtx-rtt", G_TYPE_UINT64, (guint64) (now - last_rtx_request),
Packit 1f69a5
              NULL)));
Packit 1f69a5
Packit 1f69a5
  gst_harness_teardown (h);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_rtx_buffer_arrives_too_late)
Packit 1f69a5
{
Packit 1f69a5
  GstHarness *h = gst_harness_new ("rtpjitterbuffer");
Packit 1f69a5
  gint latency_ms = 5 * TEST_BUF_MS;
Packit 1f69a5
  gint next_seqnum;
Packit 1f69a5
  GstClockTime now, last_rtx_request;
Packit 1f69a5
  gint rtx_delay_ms = 0.5 * TEST_BUF_MS;
Packit 1f69a5
Packit 1f69a5
  g_object_set (h->element, "do-retransmission", TRUE,
Packit 1f69a5
      "do-lost", TRUE, "rtx-max-retries", 1, NULL);
Packit 1f69a5
  next_seqnum = construct_deterministic_initial_state (h, latency_ms);
Packit 1f69a5
Packit 1f69a5
  /* Crank clock to send retransmission events requesting seqnum 6 which has
Packit 1f69a5
   * not arrived yet. */
Packit 1f69a5
  gst_harness_crank_single_clock_wait (h);
Packit 1f69a5
  verify_rtx_event (h, next_seqnum,
Packit 1f69a5
      next_seqnum * TEST_BUF_DURATION, rtx_delay_ms, TEST_BUF_DURATION);
Packit 1f69a5
Packit 1f69a5
  last_rtx_request = gst_clock_get_time (GST_ELEMENT_CLOCK (h->element));
Packit 1f69a5
  fail_unless_equals_int64 (last_rtx_request,
Packit 1f69a5
      next_seqnum * TEST_BUF_DURATION + rtx_delay_ms * GST_MSECOND);
Packit 1f69a5
Packit 1f69a5
  /* packet @next_seqnum is considered lost */
Packit 1f69a5
  gst_harness_crank_single_clock_wait (h);
Packit 1f69a5
  verify_lost_event (h, next_seqnum,
Packit 1f69a5
      next_seqnum * TEST_BUF_DURATION, TEST_BUF_DURATION);
Packit 1f69a5
Packit 1f69a5
  /* packet @next_seqnum arrives too late */
Packit 1f69a5
  now = gst_clock_get_time (GST_ELEMENT_CLOCK (h->element));
Packit 1f69a5
  fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (h,
Packit 1f69a5
          generate_test_buffer_rtx (now, next_seqnum)));
Packit 1f69a5
Packit 1f69a5
  fail_unless (verify_jb_stats (h->element,
Packit 1f69a5
          gst_structure_new ("application/x-rtp-jitterbuffer-stats",
Packit 1f69a5
              "num-pushed", G_TYPE_UINT64, (guint64) next_seqnum,
Packit 1f69a5
              "num-lost", G_TYPE_UINT64, (guint64) 1,
Packit 1f69a5
              "num-late", G_TYPE_UINT64, (guint64) 1,
Packit 1f69a5
              "num-duplicates", G_TYPE_UINT64, (guint64) 0,
Packit 1f69a5
              "rtx-count", G_TYPE_UINT64, (guint64) 1,
Packit 1f69a5
              "rtx-success-count", G_TYPE_UINT64, (guint64) 0,
Packit 1f69a5
              "rtx-per-packet", G_TYPE_DOUBLE, 1.0,
Packit 1f69a5
              "rtx-rtt", G_TYPE_UINT64, (guint64) (now - last_rtx_request),
Packit 1f69a5
              NULL)));
Packit 1f69a5
Packit 1f69a5
  gst_harness_teardown (h);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_rtx_original_buffer_does_not_update_rtx_stats)
Packit 1f69a5
{
Packit 1f69a5
  GstHarness *h = gst_harness_new ("rtpjitterbuffer");
Packit 1f69a5
  gint latency_ms = 100;
Packit 1f69a5
  gint next_seqnum;
Packit 1f69a5
  GstBuffer *buffer;
Packit 1f69a5
  GstClockTime now, last_rtx_request;
Packit 1f69a5
  gint rtx_delay_ms = 0.5 * TEST_BUF_MS;
Packit 1f69a5
Packit 1f69a5
  g_object_set (h->element, "do-retransmission", TRUE,
Packit 1f69a5
      "rtx-max-retries", 1, NULL);
Packit 1f69a5
  next_seqnum = construct_deterministic_initial_state (h, latency_ms);
Packit 1f69a5
  fail_unless_equals_int (6, next_seqnum);
Packit 1f69a5
Packit 1f69a5
  /* Crank clock to send retransmission events requesting @next_seqnum which has
Packit 1f69a5
   * not arrived yet. */
Packit 1f69a5
  gst_harness_crank_single_clock_wait (h);
Packit 1f69a5
  verify_rtx_event (h, next_seqnum,
Packit 1f69a5
      next_seqnum * TEST_BUF_DURATION, rtx_delay_ms, TEST_BUF_DURATION);
Packit 1f69a5
Packit 1f69a5
  last_rtx_request = gst_clock_get_time (GST_ELEMENT_CLOCK (h->element));
Packit 1f69a5
  fail_unless_equals_int64 (last_rtx_request,
Packit 1f69a5
      next_seqnum * TEST_BUF_DURATION + rtx_delay_ms * GST_MSECOND);
Packit 1f69a5
Packit 1f69a5
  /* ORIGINAL seqnum 6 arrives just before it times out and is considered
Packit 1f69a5
   * lost. */
Packit 1f69a5
  now = 200 * GST_MSECOND;
Packit 1f69a5
  gst_harness_set_time (h, now);
Packit 1f69a5
  fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (h,
Packit 1f69a5
          generate_test_buffer_full (now,
Packit 1f69a5
              next_seqnum, next_seqnum * TEST_RTP_TS_DURATION)));
Packit 1f69a5
  buffer = gst_harness_pull (h);
Packit 1f69a5
  fail_unless_equals_int (next_seqnum, get_rtp_seq_num (buffer));
Packit 1f69a5
  gst_buffer_unref (buffer);
Packit 1f69a5
Packit 1f69a5
  /* due to the advance in time, we will now also have sent
Packit 1f69a5
     an rtx-request for 7 */
Packit 1f69a5
  next_seqnum++;
Packit 1f69a5
  verify_rtx_event (h, next_seqnum,
Packit 1f69a5
      next_seqnum * TEST_BUF_DURATION, rtx_delay_ms, TEST_BUF_DURATION);
Packit 1f69a5
Packit 1f69a5
  /* The original buffer does not count in the RTX stats. */
Packit 1f69a5
  fail_unless (verify_jb_stats (h->element,
Packit 1f69a5
          gst_structure_new ("application/x-rtp-jitterbuffer-stats",
Packit 1f69a5
              "num-pushed", G_TYPE_UINT64, (guint64) next_seqnum,
Packit 1f69a5
              "num-lost", G_TYPE_UINT64, (guint64) 0,
Packit 1f69a5
              "num-late", G_TYPE_UINT64, (guint64) 0,
Packit 1f69a5
              "num-duplicates", G_TYPE_UINT64, (guint64) 0,
Packit 1f69a5
              "rtx-count", G_TYPE_UINT64, (guint64) 2,
Packit 1f69a5
              "rtx-success-count", G_TYPE_UINT64, (guint64) 0,
Packit 1f69a5
              "rtx-per-packet", G_TYPE_DOUBLE, 0.0,
Packit 1f69a5
              "rtx-rtt", G_TYPE_UINT64, (guint64) 0, NULL)));
Packit 1f69a5
Packit 1f69a5
  /* Now the retransmitted packet arrives and stats should be updated. Note
Packit 1f69a5
   * that the buffer arrives in time and should not be considered late, but
Packit 1f69a5
   * a duplicate. */
Packit 1f69a5
  fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (h,
Packit 1f69a5
          generate_test_buffer_rtx (now, 6)));
Packit 1f69a5
Packit 1f69a5
  fail_unless (verify_jb_stats (h->element,
Packit 1f69a5
          gst_structure_new ("application/x-rtp-jitterbuffer-stats",
Packit 1f69a5
              "num-pushed", G_TYPE_UINT64, (guint64) next_seqnum,
Packit 1f69a5
              "num-lost", G_TYPE_UINT64, (guint64) 0,
Packit 1f69a5
              "num-late", G_TYPE_UINT64, (guint64) 0,
Packit 1f69a5
              "num-duplicates", G_TYPE_UINT64, (guint64) 1,
Packit 1f69a5
              "rtx-count", G_TYPE_UINT64, (guint64) 2,
Packit 1f69a5
              "rtx-success-count", G_TYPE_UINT64, (guint64) 0,
Packit 1f69a5
              "rtx-per-packet", G_TYPE_DOUBLE, 1.0,
Packit 1f69a5
              "rtx-rtt", G_TYPE_UINT64, (guint64) (now - last_rtx_request),
Packit 1f69a5
              NULL)));
Packit 1f69a5
Packit 1f69a5
  gst_harness_teardown (h);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_rtx_duplicate_packet_updates_rtx_stats)
Packit 1f69a5
{
Packit 1f69a5
  GstHarness *h = gst_harness_new ("rtpjitterbuffer");
Packit 1f69a5
  gint latency_ms = 100;
Packit 1f69a5
  gint next_seqnum;
Packit 1f69a5
  GstClockTime now, rtx_request_6, rtx_request_7;
Packit 1f69a5
  gint rtx_delay_ms = 0.5 * TEST_BUF_MS;
Packit 1f69a5
  gint i;
Packit 1f69a5
Packit 1f69a5
  g_object_set (h->element, "do-retransmission", TRUE, NULL);
Packit 1f69a5
  next_seqnum = construct_deterministic_initial_state (h, latency_ms);
Packit 1f69a5
  fail_unless_equals_int (6, next_seqnum);
Packit 1f69a5
Packit 1f69a5
  /* Push packet 8 so that 6 and 7 is missing */
Packit 1f69a5
  fail_unless_equals_int (GST_FLOW_OK,
Packit 1f69a5
      gst_harness_push (h, generate_test_buffer (8)));
Packit 1f69a5
Packit 1f69a5
  /* Wait for NACKs on 6 and 7 */
Packit 1f69a5
  gst_harness_crank_single_clock_wait (h);
Packit 1f69a5
  verify_rtx_event (h, 6, 6 * TEST_BUF_DURATION,
Packit 1f69a5
      rtx_delay_ms, TEST_BUF_DURATION);
Packit 1f69a5
  rtx_request_6 = gst_clock_get_time (GST_ELEMENT_CLOCK (h->element));
Packit 1f69a5
  fail_unless_equals_int64 (rtx_request_6,
Packit 1f69a5
      6 * TEST_BUF_DURATION + rtx_delay_ms * GST_MSECOND);
Packit 1f69a5
Packit 1f69a5
  gst_harness_crank_single_clock_wait (h);
Packit 1f69a5
  verify_rtx_event (h,
Packit 1f69a5
      7, 7 * TEST_BUF_DURATION, rtx_delay_ms, TEST_BUF_DURATION);
Packit 1f69a5
  rtx_request_7 = gst_clock_get_time (GST_ELEMENT_CLOCK (h->element));
Packit 1f69a5
  fail_unless_equals_int64 (rtx_request_7,
Packit 1f69a5
      7 * TEST_BUF_DURATION + rtx_delay_ms * GST_MSECOND);
Packit 1f69a5
Packit 1f69a5
  /* Original packet 7 arrives */
Packit 1f69a5
  now = 150 * GST_MSECOND;
Packit 1f69a5
  gst_harness_set_time (h, now);
Packit 1f69a5
  fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (h,
Packit 1f69a5
          generate_test_buffer_full (now, 7, 7 * TEST_RTP_TS_DURATION)));
Packit 1f69a5
Packit 1f69a5
  /* We're still waiting for packet 6, so 7 should not be pushed */
Packit 1f69a5
  gst_harness_wait_for_clock_id_waits (h, 1, 60);
Packit 1f69a5
  fail_unless_equals_int (gst_harness_buffers_in_queue (h), 0);
Packit 1f69a5
Packit 1f69a5
  /* The original buffer does not count in the RTX stats. */
Packit 1f69a5
  fail_unless (verify_jb_stats (h->element,
Packit 1f69a5
          gst_structure_new ("application/x-rtp-jitterbuffer-stats",
Packit 1f69a5
              "num-lost", G_TYPE_UINT64, (guint64) 0,
Packit 1f69a5
              "num-late", G_TYPE_UINT64, (guint64) 0,
Packit 1f69a5
              "num-duplicates", G_TYPE_UINT64, (guint64) 0,
Packit 1f69a5
              "rtx-count", G_TYPE_UINT64, (guint64) 2,
Packit 1f69a5
              "rtx-success-count", G_TYPE_UINT64, (guint64) 0,
Packit 1f69a5
              "rtx-per-packet", G_TYPE_DOUBLE, 0.0,
Packit 1f69a5
              "rtx-rtt", G_TYPE_UINT64, (guint64) 0, NULL)));
Packit 1f69a5
Packit 1f69a5
  /* Push RTX packet 7. Should be dropped as duplicate but update RTX stats. */
Packit 1f69a5
  now = 160 * GST_MSECOND;
Packit 1f69a5
  gst_harness_set_time (h, now);
Packit 1f69a5
  fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (h,
Packit 1f69a5
          generate_test_buffer_rtx (now, 7)));
Packit 1f69a5
  gst_harness_wait_for_clock_id_waits (h, 1, 60);
Packit 1f69a5
  fail_unless_equals_int (gst_harness_buffers_in_queue (h), 0);
Packit 1f69a5
Packit 1f69a5
  /* Check RTX stats with updated num-duplicates and rtx-rtt fields */
Packit 1f69a5
  fail_unless (verify_jb_stats (h->element,
Packit 1f69a5
          gst_structure_new ("application/x-rtp-jitterbuffer-stats",
Packit 1f69a5
              "num-pushed", G_TYPE_UINT64, (guint64) next_seqnum,
Packit 1f69a5
              "num-lost", G_TYPE_UINT64, (guint64) 0,
Packit 1f69a5
              "num-late", G_TYPE_UINT64, (guint64) 0,
Packit 1f69a5
              "num-duplicates", G_TYPE_UINT64, (guint64) 1,
Packit 1f69a5
              "rtx-count", G_TYPE_UINT64, (guint64) 2,
Packit 1f69a5
              "rtx-success-count", G_TYPE_UINT64, (guint64) 0,
Packit 1f69a5
              "rtx-per-packet", G_TYPE_DOUBLE, 1.0,
Packit 1f69a5
              "rtx-rtt", G_TYPE_UINT64, (guint64) (now - rtx_request_7),
Packit 1f69a5
              NULL)));
Packit 1f69a5
Packit 1f69a5
  /* RTX packet 6 arrives, both 6, 7 and 8 is ready to be pulled */
Packit 1f69a5
  fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (h,
Packit 1f69a5
          generate_test_buffer_rtx (now, 6)));
Packit 1f69a5
Packit 1f69a5
  for (i = 6; i <= 8; i++) {
Packit 1f69a5
    GstBuffer *buf = gst_harness_pull (h);
Packit 1f69a5
    fail_unless_equals_int (i, get_rtp_seq_num (buf));
Packit 1f69a5
    gst_buffer_unref (buf);
Packit 1f69a5
  }
Packit 1f69a5
Packit 1f69a5
  /* RTX stats is updated with success count increased. */
Packit 1f69a5
  fail_unless (verify_jb_stats (h->element,
Packit 1f69a5
          gst_structure_new ("application/x-rtp-jitterbuffer-stats",
Packit 1f69a5
              "num-pushed", G_TYPE_UINT64, (guint64) next_seqnum + 3,
Packit 1f69a5
              "num-lost", G_TYPE_UINT64, (guint64) 0,
Packit 1f69a5
              "num-late", G_TYPE_UINT64, (guint64) 0,
Packit 1f69a5
              "num-duplicates", G_TYPE_UINT64, (guint64) 1,
Packit 1f69a5
              "rtx-count", G_TYPE_UINT64, (guint64) 2,
Packit 1f69a5
              "rtx-success-count", G_TYPE_UINT64, (guint64) 1,
Packit 1f69a5
              "rtx-per-packet", G_TYPE_DOUBLE, 1.0,
Packit 1f69a5
              "rtx-rtt", G_TYPE_UINT64, (guint64)
Packit 1f69a5
              /* Use the rtx-rtt formula. Can be subject to change though. */
Packit 1f69a5
              ((now - rtx_request_6) + 47 * (now - rtx_request_7)) / 48,
Packit 1f69a5
              NULL)));
Packit 1f69a5
Packit 1f69a5
  gst_harness_teardown (h);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_rtx_buffer_arrives_after_lost_updates_rtx_stats)
Packit 1f69a5
{
Packit 1f69a5
  GstHarness *h = gst_harness_new ("rtpjitterbuffer");
Packit 1f69a5
  gint latency_ms = 100;
Packit 1f69a5
  gint next_seqnum;
Packit 1f69a5
  GstClockTime now, last_rtx_request;
Packit 1f69a5
  gint rtx_delay_ms = 0.5 * TEST_BUF_MS;
Packit 1f69a5
Packit 1f69a5
  g_object_set (h->element, "do-retransmission", TRUE,
Packit 1f69a5
      "do-lost", TRUE, "rtx-max-retries", 1, NULL);
Packit 1f69a5
  next_seqnum = construct_deterministic_initial_state (h, latency_ms);
Packit 1f69a5
Packit 1f69a5
  /* Crank clock to send retransmission events requesting seqnum 6 which has
Packit 1f69a5
   * not arrived yet. */
Packit 1f69a5
  gst_harness_crank_single_clock_wait (h);
Packit 1f69a5
  verify_rtx_event (h, next_seqnum,
Packit 1f69a5
      next_seqnum * TEST_BUF_DURATION, rtx_delay_ms, TEST_BUF_DURATION);
Packit 1f69a5
Packit 1f69a5
  last_rtx_request = gst_clock_get_time (GST_ELEMENT_CLOCK (h->element));
Packit 1f69a5
  fail_unless_equals_int64 (last_rtx_request,
Packit 1f69a5
      next_seqnum * TEST_BUF_DURATION + rtx_delay_ms * GST_MSECOND);
Packit 1f69a5
Packit 1f69a5
  /* seqnum 6 is considered lost */
Packit 1f69a5
  gst_harness_crank_single_clock_wait (h);
Packit 1f69a5
  verify_lost_event (h, next_seqnum,
Packit 1f69a5
      next_seqnum * TEST_BUF_DURATION, TEST_BUF_DURATION);
Packit 1f69a5
Packit 1f69a5
  /* seqnum 6 arrives too late */
Packit 1f69a5
  now = gst_clock_get_time (GST_ELEMENT_CLOCK (h->element));
Packit 1f69a5
  fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (h,
Packit 1f69a5
          generate_test_buffer_rtx (now, next_seqnum)));
Packit 1f69a5
Packit 1f69a5
  fail_unless (verify_jb_stats (h->element,
Packit 1f69a5
          gst_structure_new ("application/x-rtp-jitterbuffer-stats",
Packit 1f69a5
              "num-pushed", G_TYPE_UINT64, (guint64) next_seqnum,
Packit 1f69a5
              "num-lost", G_TYPE_UINT64, (guint64) 1,
Packit 1f69a5
              "num-late", G_TYPE_UINT64, (guint64) 1,
Packit 1f69a5
              "num-duplicates", G_TYPE_UINT64, (guint64) 0,
Packit 1f69a5
              "rtx-count", G_TYPE_UINT64, (guint64) 1,
Packit 1f69a5
              "rtx-success-count", G_TYPE_UINT64, (guint64) 0,
Packit 1f69a5
              "rtx-per-packet", G_TYPE_DOUBLE, 1.0,
Packit 1f69a5
              "rtx-rtt", G_TYPE_UINT64, (guint64) (now - last_rtx_request),
Packit 1f69a5
              NULL)));
Packit 1f69a5
Packit 1f69a5
  gst_harness_teardown (h);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_rtx_rtt_larger_than_retry_timeout)
Packit 1f69a5
{
Packit 1f69a5
  /* When RTT is larger than retry period we will send two or more requests
Packit 1f69a5
   * before receiving any retransmission packets */
Packit 1f69a5
  GstHarness *h = gst_harness_new ("rtpjitterbuffer");
Packit 1f69a5
  gint latency_ms = 100;
Packit 1f69a5
  gint next_seqnum;
Packit 1f69a5
  gint rtx_retry_timeout_ms = 20;
Packit 1f69a5
  gint rtx_delay_ms = 0.5 * TEST_BUF_MS;
Packit 1f69a5
  gint rtt = rtx_retry_timeout_ms * GST_MSECOND + 1;
Packit 1f69a5
  GstClockTime now, first_request, second_request;
Packit 1f69a5
Packit 1f69a5
  g_object_set (h->element, "do-retransmission", TRUE,
Packit 1f69a5
      "rtx-retry-timeout", rtx_retry_timeout_ms, NULL);
Packit 1f69a5
  next_seqnum = construct_deterministic_initial_state (h, latency_ms);
Packit 1f69a5
Packit 1f69a5
  /* Wait for first NACK on 6 */
Packit 1f69a5
  gst_harness_crank_single_clock_wait (h);
Packit 1f69a5
  verify_rtx_event (h, next_seqnum,
Packit 1f69a5
      next_seqnum * TEST_BUF_DURATION, rtx_delay_ms, TEST_BUF_DURATION);
Packit 1f69a5
  first_request = gst_clock_get_time (GST_ELEMENT_CLOCK (h->element));
Packit 1f69a5
  fail_unless_equals_int64 (first_request,
Packit 1f69a5
      next_seqnum * TEST_BUF_DURATION + rtx_delay_ms * GST_MSECOND);
Packit 1f69a5
Packit 1f69a5
  /* Packet @next_seqnum + 1 arrives in time (so that we avoid its EXPECTED
Packit 1f69a5
   * timers to interfer with our test) */
Packit 1f69a5
  push_test_buffer (h, next_seqnum + 1);
Packit 1f69a5
Packit 1f69a5
  /* Simulating RTT > rtx-retry-timeout, we send a new NACK before receiving
Packit 1f69a5
   * the RTX packet. Wait for second NACK on @next_seqnum */
Packit 1f69a5
  gst_harness_crank_single_clock_wait (h);
Packit 1f69a5
  rtx_delay_ms += rtx_retry_timeout_ms;
Packit 1f69a5
  verify_rtx_event (h, next_seqnum,
Packit 1f69a5
      next_seqnum * TEST_BUF_DURATION, rtx_delay_ms, TEST_BUF_DURATION);
Packit 1f69a5
  second_request = gst_clock_get_time (GST_ELEMENT_CLOCK (h->element));
Packit 1f69a5
  fail_unless_equals_int64 (second_request,
Packit 1f69a5
      next_seqnum * TEST_BUF_DURATION + rtx_delay_ms * GST_MSECOND);
Packit 1f69a5
Packit 1f69a5
  /* The first retransmitted packet arrives */
Packit 1f69a5
  now = first_request + rtt;
Packit 1f69a5
  gst_harness_set_time (h, now);
Packit 1f69a5
  fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (h,
Packit 1f69a5
          generate_test_buffer_rtx (now, next_seqnum)));
Packit 1f69a5
Packit 1f69a5
  /* Pull packets @next_seqnum and @next_seqnum + 1 */
Packit 1f69a5
  gst_buffer_unref (gst_harness_pull (h));
Packit 1f69a5
  gst_buffer_unref (gst_harness_pull (h));
Packit 1f69a5
Packit 1f69a5
  /* Stats should be updated. Note that RTT is not updated since we cannot be
Packit 1f69a5
   * sure whether the RTX packet is in response to the first or second NACK. */
Packit 1f69a5
  fail_unless (verify_jb_stats (h->element,
Packit 1f69a5
          gst_structure_new ("application/x-rtp-jitterbuffer-stats",
Packit 1f69a5
              "num-pushed", G_TYPE_UINT64, (guint64) next_seqnum + 2,
Packit 1f69a5
              "num-lost", G_TYPE_UINT64, (guint64) 0,
Packit 1f69a5
              "num-late", G_TYPE_UINT64, (guint64) 0,
Packit 1f69a5
              "num-duplicates", G_TYPE_UINT64, (guint64) 0,
Packit 1f69a5
              "rtx-count", G_TYPE_UINT64, (guint64) 2,
Packit 1f69a5
              "rtx-success-count", G_TYPE_UINT64, (guint64) 1,
Packit 1f69a5
              "rtx-per-packet", G_TYPE_DOUBLE, 2.0,
Packit 1f69a5
              "rtx-rtt", G_TYPE_UINT64, (guint64) 0, NULL)));
Packit 1f69a5
Packit 1f69a5
  /* Packet @next_seqnum + 2 arrives in time */
Packit 1f69a5
  push_test_buffer (h, next_seqnum + 2);
Packit 1f69a5
  gst_buffer_unref (gst_harness_pull (h));
Packit 1f69a5
Packit 1f69a5
  /* Now the second retransmitted packet arrives */
Packit 1f69a5
  now = second_request + rtt;
Packit 1f69a5
  gst_harness_set_time (h, now);
Packit 1f69a5
  fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (h,
Packit 1f69a5
          generate_test_buffer_rtx (now, next_seqnum)));
Packit 1f69a5
Packit 1f69a5
  /* The stats is updated with the correct RTT. */
Packit 1f69a5
  fail_unless (verify_jb_stats (h->element,
Packit 1f69a5
          gst_structure_new ("application/x-rtp-jitterbuffer-stats",
Packit 1f69a5
              "num-pushed", G_TYPE_UINT64, (guint64) next_seqnum + 3,
Packit 1f69a5
              "num-lost", G_TYPE_UINT64, (guint64) 0,
Packit 1f69a5
              "num-late", G_TYPE_UINT64, (guint64) 0,
Packit 1f69a5
              "num-duplicates", G_TYPE_UINT64, (guint64) 1,
Packit 1f69a5
              "rtx-count", G_TYPE_UINT64, (guint64) 2,
Packit 1f69a5
              "rtx-success-count", G_TYPE_UINT64, (guint64) 1,
Packit 1f69a5
              "rtx-per-packet", G_TYPE_DOUBLE, 2.0,
Packit 1f69a5
              "rtx-rtt", G_TYPE_UINT64, (guint64) rtt, NULL)));
Packit 1f69a5
Packit 1f69a5
  gst_harness_teardown (h);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_rtx_no_request_if_time_past_retry_period)
Packit 1f69a5
{
Packit 1f69a5
  GstHarness *h = gst_harness_new ("rtpjitterbuffer");
Packit 1f69a5
  const gint latency_ms = 200;
Packit 1f69a5
  const gint retry_period_ms = 120;
Packit 1f69a5
  GstTestClock *testclock;
Packit 1f69a5
  GstClockID pending_id;
Packit 1f69a5
  GstClockTime time;
Packit 1f69a5
  gint i;
Packit 1f69a5
Packit 1f69a5
  gst_harness_set_src_caps (h, generate_caps ());
Packit 1f69a5
  testclock = gst_harness_get_testclock (h);
Packit 1f69a5
Packit 1f69a5
  g_object_set (h->element, "do-lost", TRUE, NULL);
Packit 1f69a5
  g_object_set (h->element, "do-retransmission", TRUE, NULL);
Packit 1f69a5
  g_object_set (h->element, "latency", latency_ms, NULL);
Packit 1f69a5
  g_object_set (h->element, "rtx-retry-period", retry_period_ms, NULL);
Packit 1f69a5
Packit 1f69a5
  /* push the first couple of buffers */
Packit 1f69a5
  push_test_buffer (h, 0);
Packit 1f69a5
  push_test_buffer (h, 1);
Packit 1f69a5
Packit 1f69a5
  /* drop reconfigure event */
Packit 1f69a5
  gst_event_unref (gst_harness_pull_upstream_event (h));
Packit 1f69a5
  /* drop GstEventStreamStart & GstEventCaps & GstEventSegment */
Packit 1f69a5
  for (i = 0; i < 3; i++)
Packit 1f69a5
    gst_event_unref (gst_harness_pull_event (h));
Packit 1f69a5
Packit 1f69a5
  /* Wait for the first EXPECTED timer to be scheduled */
Packit 1f69a5
  gst_test_clock_wait_for_next_pending_id (testclock, &pending_id);
Packit 1f69a5
  time = gst_clock_id_get_time (pending_id);
Packit 1f69a5
  gst_clock_id_unref (pending_id);
Packit 1f69a5
  fail_unless_equals_int64 (time, 2 * TEST_BUF_DURATION + 10 * GST_MSECOND);
Packit 1f69a5
Packit 1f69a5
  /* Let the first EXPECTED timer time out and be sent. However, set the 'now'
Packit 1f69a5
   * time to be past the retry-period simulating that the jitterbuffer has too
Packit 1f69a5
   * much to do and is not able to process all timers in real-time. In this
Packit 1f69a5
   * case the jitterbuffer should not schedule a new EXPECTED timer as that
Packit 1f69a5
   * would just make matters worse (more unnecessary processing of a request
Packit 1f69a5
   * that is already too late to be valuable). In practice this typically
Packit 1f69a5
   * happens for high loss networks with low RTT. */
Packit 1f69a5
  gst_test_clock_set_time_and_process (testclock,
Packit 1f69a5
      2 * TEST_BUF_DURATION + retry_period_ms * GST_MSECOND + 1);
Packit 1f69a5
Packit 1f69a5
  /* Verify the event. It could be argued that this request is already too
Packit 1f69a5
   * late and unnecessary. However, in order to keep things simple (for now)
Packit 1f69a5
   * we just keep the already scehduled EXPECTED timer, but refrain from
Packit 1f69a5
   * scheduled another EXPECTED timer */
Packit 1f69a5
  verify_rtx_event (h, 2, 2 * TEST_BUF_DURATION, 10, TEST_BUF_DURATION);
Packit 1f69a5
Packit 1f69a5
  /* "crank" to reach the DEADLINE for packet 0 */
Packit 1f69a5
  gst_harness_crank_single_clock_wait (h);
Packit 1f69a5
  gst_buffer_unref (gst_harness_pull (h));
Packit 1f69a5
  gst_buffer_unref (gst_harness_pull (h));
Packit 1f69a5
Packit 1f69a5
  fail_unless_equals_int (0, gst_harness_upstream_events_in_queue (h));
Packit 1f69a5
  fail_unless_equals_int (0, gst_harness_events_in_queue (h));
Packit 1f69a5
Packit 1f69a5
  /* "crank" to time out the LOST event */
Packit 1f69a5
  gst_harness_crank_single_clock_wait (h);
Packit 1f69a5
  verify_lost_event (h, 2, 2 * TEST_BUF_DURATION, TEST_BUF_DURATION);
Packit 1f69a5
Packit 1f69a5
  gst_object_unref (testclock);
Packit 1f69a5
  gst_harness_teardown (h);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_rtx_same_delay_and_retry_timeout)
Packit 1f69a5
{
Packit 1f69a5
  GstHarness *h = gst_harness_new ("rtpjitterbuffer");
Packit 1f69a5
  gint latency_ms = 5 * TEST_BUF_MS;
Packit 1f69a5
  gint next_seqnum;
Packit 1f69a5
  gint rtx_delay_ms = 20;
Packit 1f69a5
  GstClockTime last_rtx_request;
Packit 1f69a5
Packit 1f69a5
  g_object_set (h->element, "do-retransmission", TRUE,
Packit 1f69a5
      "rtx-max-retries", 3, "rtx-delay", rtx_delay_ms,
Packit 1f69a5
      "rtx-retry-timeout", rtx_delay_ms, NULL);
Packit 1f69a5
  next_seqnum = construct_deterministic_initial_state (h, latency_ms);
Packit 1f69a5
Packit 1f69a5
  /* Crank clock to send retransmission events requesting seqnum 6 which has
Packit 1f69a5
   * not arrived yet. */
Packit 1f69a5
  gst_harness_crank_single_clock_wait (h);
Packit 1f69a5
  verify_rtx_event (h, next_seqnum,
Packit 1f69a5
      next_seqnum * TEST_BUF_DURATION, rtx_delay_ms, TEST_BUF_DURATION);
Packit 1f69a5
  /* first rtx for packet @next_seqnum should arrive at the right time */
Packit 1f69a5
  last_rtx_request = gst_clock_get_time (GST_ELEMENT_CLOCK (h->element));
Packit 1f69a5
  fail_unless_equals_int64 (last_rtx_request,
Packit 1f69a5
      next_seqnum * TEST_BUF_DURATION + rtx_delay_ms * GST_MSECOND);
Packit 1f69a5
Packit 1f69a5
  /* verify we have pulled out all rtx-events */
Packit 1f69a5
  fail_unless_equals_int (0, gst_harness_upstream_events_in_queue (h));
Packit 1f69a5
Packit 1f69a5
  /* now crank to get the second attempt at packet @next_seqnum */
Packit 1f69a5
  gst_harness_crank_single_clock_wait (h);
Packit 1f69a5
  verify_rtx_event (h, next_seqnum,
Packit 1f69a5
      next_seqnum * TEST_BUF_DURATION, rtx_delay_ms * 2, TEST_BUF_DURATION);
Packit 1f69a5
Packit 1f69a5
  /* second rtx for seqnum 6 should arrive at 140 + 20ms */
Packit 1f69a5
  last_rtx_request = gst_clock_get_time (GST_ELEMENT_CLOCK (h->element));
Packit 1f69a5
  fail_unless_equals_int64 (last_rtx_request,
Packit 1f69a5
      next_seqnum * TEST_BUF_DURATION + rtx_delay_ms * 2 * GST_MSECOND);
Packit 1f69a5
Packit 1f69a5
  /* verify we have pulled out all rtx-events */
Packit 1f69a5
  fail_unless_equals_int (0, gst_harness_upstream_events_in_queue (h));
Packit 1f69a5
Packit 1f69a5
  fail_unless (verify_jb_stats (h->element,
Packit 1f69a5
          gst_structure_new ("application/x-rtp-jitterbuffer-stats",
Packit 1f69a5
              "num-pushed", G_TYPE_UINT64, (guint64) next_seqnum,
Packit 1f69a5
              "num-lost", G_TYPE_UINT64, (guint64) 0,
Packit 1f69a5
              "rtx-count", G_TYPE_UINT64, (guint64) 2, NULL)));
Packit 1f69a5
Packit 1f69a5
  gst_harness_teardown (h);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_rtx_with_backwards_rtptime)
Packit 1f69a5
{
Packit 1f69a5
  GstHarness *h = gst_harness_new ("rtpjitterbuffer");
Packit 1f69a5
  gint latency_ms = 40;
Packit 1f69a5
Packit 1f69a5
  g_object_set (h->element, "do-retransmission", TRUE, NULL);
Packit 1f69a5
  construct_deterministic_initial_state (h, latency_ms);
Packit 1f69a5
Packit 1f69a5
  /*
Packit 1f69a5
   * For video using B-frames, an expected sequence
Packit 1f69a5
   * could be like this:
Packit 1f69a5
   * (I = I-frame, P = P-frame, B = B-frame)
Packit 1f69a5
   *               ___   ___   ___
Packit 1f69a5
   *          ... | 3 | | 4 | | 5 |
Packit 1f69a5
   *               –––   –––   –––
Packit 1f69a5
   * rtptime:       3(I)  5(P)  4(B)
Packit 1f69a5
   * arrival(dts):  3     5     5
Packit 1f69a5
   *
Packit 1f69a5
   * Notice here that packet 5 (the B frame) make
Packit 1f69a5
   * the rtptime go backwards.
Packit 1f69a5
   */
Packit 1f69a5
Packit 1f69a5
  /* seqnum 3, arriving at time 3 with rtptime 3 */
Packit 1f69a5
  push_test_buffer (h, 3);
Packit 1f69a5
  gst_buffer_unref (gst_harness_pull (h));
Packit 1f69a5
Packit 1f69a5
  /* seqnum 4, arriving at time 5 with rtptime 5 */
Packit 1f69a5
  gst_harness_push (h, generate_test_buffer_full (5 * TEST_BUF_DURATION,
Packit 1f69a5
          4, 5 * TEST_RTP_TS_DURATION));
Packit 1f69a5
  gst_buffer_unref (gst_harness_pull (h));
Packit 1f69a5
Packit 1f69a5
  /* seqnum 5, arriving at time 5 with rtptime 4 */
Packit 1f69a5
  gst_harness_push (h, generate_test_buffer_full (5 * TEST_BUF_DURATION,
Packit 1f69a5
          5, 4 * TEST_RTP_TS_DURATION));
Packit 1f69a5
  gst_buffer_unref (gst_harness_pull (h));
Packit 1f69a5
Packit 1f69a5
  /* crank to time-out the rtx-request for seqnum 6, the point here
Packit 1f69a5
     being that the backwards rtptime did not mess up the timeout for
Packit 1f69a5
     the rtx event */
Packit 1f69a5
  gst_harness_crank_single_clock_wait (h);
Packit 1f69a5
  verify_rtx_event (h, 6, 5 * TEST_BUF_DURATION + 15 * GST_MSECOND,
Packit 1f69a5
      17, 35 * GST_MSECOND);
Packit 1f69a5
Packit 1f69a5
  fail_unless (verify_jb_stats (h->element,
Packit 1f69a5
          gst_structure_new ("application/x-rtp-jitterbuffer-stats",
Packit 1f69a5
              "num-pushed", G_TYPE_UINT64, (guint64) 6,
Packit 1f69a5
              "rtx-count", G_TYPE_UINT64, (guint64) 1,
Packit 1f69a5
              "num-lost", G_TYPE_UINT64, (guint64) 0, NULL)));
Packit 1f69a5
Packit 1f69a5
  gst_harness_teardown (h);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_rtx_timer_reuse)
Packit 1f69a5
{
Packit 1f69a5
  GstHarness *h = gst_harness_new ("rtpjitterbuffer");
Packit 1f69a5
  gint latency_ms = 5 * TEST_BUF_MS;
Packit 1f69a5
  gint rtx_delay_ms = 0.5 * TEST_BUF_MS;
Packit 1f69a5
  guint next_seqnum;
Packit 1f69a5
Packit 1f69a5
  g_object_set (h->element, "do-retransmission", TRUE,
Packit 1f69a5
      "do-lost", TRUE, "rtx-max-retries", 1, NULL);
Packit 1f69a5
  next_seqnum = construct_deterministic_initial_state (h, latency_ms);
Packit 1f69a5
Packit 1f69a5
  /* crank to timeout the only rtx-request, and the timer will
Packit 1f69a5
   * now reschedule as a lost-timer internally */
Packit 1f69a5
  gst_harness_crank_single_clock_wait (h);
Packit 1f69a5
  verify_rtx_event (h, next_seqnum,
Packit 1f69a5
      next_seqnum * TEST_BUF_DURATION, rtx_delay_ms, TEST_BUF_DURATION);
Packit 1f69a5
Packit 1f69a5
  /* but now buffer 6 arrives, and this should now reuse the lost-timer
Packit 1f69a5
   * for 6, as an expected-timer for 7 */
Packit 1f69a5
  fail_unless_equals_int (GST_FLOW_OK,
Packit 1f69a5
      gst_harness_push (h, generate_test_buffer (next_seqnum)));
Packit 1f69a5
Packit 1f69a5
  /* now crank to timeout the expected-timer for 7 and verify */
Packit 1f69a5
  next_seqnum++;
Packit 1f69a5
  gst_harness_crank_single_clock_wait (h);
Packit 1f69a5
  verify_rtx_event (h, next_seqnum,
Packit 1f69a5
      next_seqnum * TEST_BUF_DURATION, rtx_delay_ms, TEST_BUF_DURATION);
Packit 1f69a5
Packit 1f69a5
  gst_harness_teardown (h);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_deadline_ts_offset)
Packit 1f69a5
{
Packit 1f69a5
  GstHarness *h = gst_harness_new ("rtpjitterbuffer");
Packit 1f69a5
  GstTestClock *testclock;
Packit 1f69a5
  GstClockID id;
Packit 1f69a5
  const gint jb_latency_ms = 10;
Packit 1f69a5
Packit 1f69a5
  gst_harness_set_src_caps (h, generate_caps ());
Packit 1f69a5
  testclock = gst_harness_get_testclock (h);
Packit 1f69a5
Packit 1f69a5
  g_object_set (h->element, "latency", jb_latency_ms, NULL);
Packit 1f69a5
Packit 1f69a5
  /* push the first buffer in */
Packit 1f69a5
  fail_unless_equals_int (GST_FLOW_OK,
Packit 1f69a5
      gst_harness_push (h, generate_test_buffer (0)));
Packit 1f69a5
Packit 1f69a5
  /* wait_next_timeout() syncs on the deadline timer */
Packit 1f69a5
  gst_test_clock_wait_for_next_pending_id (testclock, &id;;
Packit 1f69a5
  fail_unless_equals_uint64 (jb_latency_ms * GST_MSECOND,
Packit 1f69a5
      gst_clock_id_get_time (id));
Packit 1f69a5
  gst_clock_id_unref (id);
Packit 1f69a5
Packit 1f69a5
  /* add ts-offset while waiting */
Packit 1f69a5
  g_object_set (h->element, "ts-offset", 20 * GST_MSECOND, NULL);
Packit 1f69a5
Packit 1f69a5
  gst_test_clock_set_time_and_process (testclock, jb_latency_ms * GST_MSECOND);
Packit 1f69a5
Packit 1f69a5
  /* wait_next_timeout() syncs on the new deadline timer */
Packit 1f69a5
  gst_test_clock_wait_for_next_pending_id (testclock, &id;;
Packit 1f69a5
  fail_unless_equals_uint64 ((20 + jb_latency_ms) * GST_MSECOND,
Packit 1f69a5
      gst_clock_id_get_time (id));
Packit 1f69a5
  gst_clock_id_unref (id);
Packit 1f69a5
Packit 1f69a5
  /* now make deadline timer timeout */
Packit 1f69a5
  gst_test_clock_set_time_and_process (testclock,
Packit 1f69a5
      (20 + jb_latency_ms) * GST_MSECOND);
Packit 1f69a5
Packit 1f69a5
  gst_buffer_unref (gst_harness_pull (h));
Packit 1f69a5
Packit 1f69a5
  gst_object_unref (testclock);
Packit 1f69a5
  gst_harness_teardown (h);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_push_big_gap)
Packit 1f69a5
{
Packit 1f69a5
  GstHarness *h = gst_harness_new ("rtpjitterbuffer");
Packit 1f69a5
  GstBuffer *buf;
Packit 1f69a5
  const gint num_consecutive = 5;
Packit 1f69a5
  gint i;
Packit 1f69a5
Packit 1f69a5
  gst_harness_set_src_caps (h, generate_caps ());
Packit 1f69a5
Packit 1f69a5
  for (i = 0; i < num_consecutive; i++)
Packit 1f69a5
    fail_unless_equals_int (GST_FLOW_OK,
Packit 1f69a5
        gst_harness_push (h, generate_test_buffer (1000 + i)));
Packit 1f69a5
Packit 1f69a5
  fail_unless (gst_harness_crank_single_clock_wait (h));
Packit 1f69a5
Packit 1f69a5
  for (i = 0; i < num_consecutive; i++) {
Packit 1f69a5
    GstBuffer *buf = gst_harness_pull (h);
Packit 1f69a5
    fail_unless_equals_int (1000 + i, get_rtp_seq_num (buf));
Packit 1f69a5
    gst_buffer_unref (buf);
Packit 1f69a5
  }
Packit 1f69a5
Packit 1f69a5
  /* Push more packets from a different sequence number domain
Packit 1f69a5
   * to trigger "big gap" logic. */
Packit 1f69a5
  for (i = 0; i < num_consecutive; i++)
Packit 1f69a5
    fail_unless_equals_int (GST_FLOW_OK,
Packit 1f69a5
        gst_harness_push (h, generate_test_buffer (20000 + i)));
Packit 1f69a5
Packit 1f69a5
  fail_unless (gst_harness_crank_single_clock_wait (h));
Packit 1f69a5
Packit 1f69a5
  for (i = 0; i < num_consecutive; i++) {
Packit 1f69a5
    GstBuffer *buf = gst_harness_pull (h);
Packit 1f69a5
    fail_unless_equals_int (20000 + i, get_rtp_seq_num (buf));
Packit 1f69a5
    gst_buffer_unref (buf);
Packit 1f69a5
  }
Packit 1f69a5
Packit 1f69a5
  /* Final buffer should be pushed straight through */
Packit 1f69a5
  fail_unless_equals_int (GST_FLOW_OK,
Packit 1f69a5
      gst_harness_push (h, generate_test_buffer (20000 + num_consecutive)));
Packit 1f69a5
  buf = gst_harness_pull (h);
Packit 1f69a5
  fail_unless_equals_int (20000 + num_consecutive, get_rtp_seq_num (buf));
Packit 1f69a5
  gst_buffer_unref (buf);
Packit 1f69a5
Packit 1f69a5
  gst_harness_teardown (h);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
typedef struct
Packit 1f69a5
{
Packit 1f69a5
  guint seqnum_offset;
Packit 1f69a5
  guint late_buffer;
Packit 1f69a5
} TestLateArrivalInput;
Packit 1f69a5
Packit 1f69a5
static const TestLateArrivalInput
Packit 1f69a5
    test_considered_lost_packet_in_large_gap_arrives_input[] = {
Packit 1f69a5
  {0, 1}, {0, 2}, {65535, 1}, {65535, 2}, {65534, 1}, {65534, 2}
Packit 1f69a5
};
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_considered_lost_packet_in_large_gap_arrives)
Packit 1f69a5
{
Packit 1f69a5
  GstHarness *h = gst_harness_new ("rtpjitterbuffer");
Packit 1f69a5
  GstTestClock *testclock;
Packit 1f69a5
  GstClockID id;
Packit 1f69a5
  GstBuffer *buffer;
Packit 1f69a5
  gint jb_latency_ms = 20;
Packit 1f69a5
  const TestLateArrivalInput *test_input =
Packit 1f69a5
      &test_considered_lost_packet_in_large_gap_arrives_input[__i__];
Packit 1f69a5
  guint seq_offset = test_input->seqnum_offset;
Packit 1f69a5
  guint late_buffer = test_input->late_buffer;
Packit 1f69a5
  gint i;
Packit 1f69a5
Packit 1f69a5
  gst_harness_set_src_caps (h, generate_caps ());
Packit 1f69a5
  testclock = gst_harness_get_testclock (h);
Packit 1f69a5
  g_object_set (h->element, "do-lost", TRUE, "latency", jb_latency_ms, NULL);
Packit 1f69a5
Packit 1f69a5
  /* first push buffer 0 */
Packit 1f69a5
  fail_unless_equals_int (GST_FLOW_OK,
Packit 1f69a5
      gst_harness_push (h, generate_test_buffer_full (0 * TEST_BUF_DURATION,
Packit 1f69a5
              0 + seq_offset, 0 * TEST_RTP_TS_DURATION)));
Packit 1f69a5
  fail_unless (gst_harness_crank_single_clock_wait (h));
Packit 1f69a5
  gst_buffer_unref (gst_harness_pull (h));
Packit 1f69a5
Packit 1f69a5
  /* drop GstEventStreamStart & GstEventCaps & GstEventSegment */
Packit 1f69a5
  for (i = 0; i < 3; i++)
Packit 1f69a5
    gst_event_unref (gst_harness_pull_event (h));
Packit 1f69a5
Packit 1f69a5
  /* hop over 3 packets, and push buffer 4 (gap of 3) */
Packit 1f69a5
  fail_unless_equals_int (GST_FLOW_OK,
Packit 1f69a5
      gst_harness_push (h, generate_test_buffer_full (4 * TEST_BUF_DURATION,
Packit 1f69a5
              4 + seq_offset, 4 * TEST_RTP_TS_DURATION)));
Packit 1f69a5
Packit 1f69a5
  /* the jitterbuffer should be waiting for the timeout of a "large gap timer"
Packit 1f69a5
   * for buffer 1 and 2 */
Packit 1f69a5
  gst_test_clock_wait_for_next_pending_id (testclock, &id;;
Packit 1f69a5
  fail_unless_equals_uint64 (1 * TEST_BUF_DURATION +
Packit 1f69a5
      jb_latency_ms * GST_MSECOND, gst_clock_id_get_time (id));
Packit 1f69a5
  gst_clock_id_unref (id);
Packit 1f69a5
Packit 1f69a5
  /* now buffer 1 sneaks in before the lost event for buffer 1 and 2 is
Packit 1f69a5
   * processed */
Packit 1f69a5
  fail_unless_equals_int (GST_FLOW_OK,
Packit 1f69a5
      gst_harness_push (h,
Packit 1f69a5
          generate_test_buffer_full (late_buffer * TEST_BUF_DURATION,
Packit 1f69a5
              late_buffer + seq_offset, late_buffer * TEST_RTP_TS_DURATION)));
Packit 1f69a5
Packit 1f69a5
  /* time out for lost packets 1 and 2 (one event, double duration) */
Packit 1f69a5
  fail_unless (gst_harness_crank_single_clock_wait (h));
Packit 1f69a5
  verify_lost_event (h, 1 + seq_offset, 1 * TEST_BUF_DURATION,
Packit 1f69a5
      2 * TEST_BUF_DURATION);
Packit 1f69a5
Packit 1f69a5
  /* time out for lost packets 3 */
Packit 1f69a5
  fail_unless (gst_harness_crank_single_clock_wait (h));
Packit 1f69a5
  verify_lost_event (h, 3 + seq_offset, 3 * TEST_BUF_DURATION,
Packit 1f69a5
      1 * TEST_BUF_DURATION);
Packit 1f69a5
Packit 1f69a5
  /* buffer 4 is pushed as normal */
Packit 1f69a5
  buffer = gst_harness_pull (h);
Packit 1f69a5
  fail_unless_equals_int ((4 + seq_offset) & 0xffff, get_rtp_seq_num (buffer));
Packit 1f69a5
  gst_buffer_unref (buffer);
Packit 1f69a5
Packit 1f69a5
  /* we have lost 3, and one of them arrived eventually, but too late */
Packit 1f69a5
  fail_unless (verify_jb_stats (h->element,
Packit 1f69a5
          gst_structure_new ("application/x-rtp-jitterbuffer-stats",
Packit 1f69a5
              "num-pushed", G_TYPE_UINT64, (guint64) 2,
Packit 1f69a5
              "num-lost", G_TYPE_UINT64, (guint64) 3,
Packit 1f69a5
              "num-late", G_TYPE_UINT64, (guint64) 1, NULL)));
Packit 1f69a5
Packit 1f69a5
  gst_object_unref (testclock);
Packit 1f69a5
  gst_harness_teardown (h);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
GST_START_TEST (test_performance)
Packit 1f69a5
{
Packit 1f69a5
  GstHarness *h =
Packit 1f69a5
      gst_harness_new_parse
Packit 1f69a5
      ("rtpjitterbuffer do-lost=1 do-retransmission=1 latency=1000");
Packit 1f69a5
  GTimer *timer = g_timer_new ();
Packit 1f69a5
  const gdouble test_duration = 2.0;
Packit 1f69a5
  guint buffers_pushed = 0;
Packit 1f69a5
  guint buffers_received;
Packit 1f69a5
Packit 1f69a5
  gst_harness_set_src_caps (h, generate_caps ());
Packit 1f69a5
  gst_harness_use_systemclock (h);
Packit 1f69a5
Packit 1f69a5
  while (g_timer_elapsed (timer, NULL) < test_duration) {
Packit 1f69a5
    /* Simulate 1ms packets */
Packit 1f69a5
    guint n = buffers_pushed * 2;       // every packet also produces a gap
Packit 1f69a5
    guint16 seqnum = n & 0xffff;
Packit 1f69a5
    guint32 rtp_ts = n * 8;
Packit 1f69a5
    GstClockTime dts = n * GST_MSECOND;
Packit 1f69a5
    gst_harness_push (h, generate_test_buffer_full (dts, seqnum, rtp_ts));
Packit 1f69a5
    buffers_pushed++;
Packit 1f69a5
    g_usleep (G_USEC_PER_SEC / 10000);
Packit 1f69a5
  }
Packit 1f69a5
  g_timer_destroy (timer);
Packit 1f69a5
Packit 1f69a5
  buffers_received = gst_harness_buffers_received (h);
Packit 1f69a5
  GST_INFO ("Pushed %d, received %d (%.1f%%)", buffers_pushed, buffers_received,
Packit 1f69a5
      100.0 * buffers_received / buffers_pushed);
Packit 1f69a5
Packit 1f69a5
  gst_harness_teardown (h);
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_END_TEST;
Packit 1f69a5
Packit 1f69a5
static Suite *
Packit 1f69a5
rtpjitterbuffer_suite (void)
Packit 1f69a5
{
Packit 1f69a5
  Suite *s = suite_create ("rtpjitterbuffer");
Packit 1f69a5
  TCase *tc_chain = tcase_create ("general");
Packit 1f69a5
Packit 1f69a5
  suite_add_tcase (s, tc_chain);
Packit 1f69a5
  tcase_add_test (tc_chain, test_push_forward_seq);
Packit 1f69a5
  tcase_add_test (tc_chain, test_push_backward_seq);
Packit 1f69a5
  tcase_add_test (tc_chain, test_push_unordered);
Packit 1f69a5
  tcase_add_test (tc_chain, test_basetime);
Packit 1f69a5
  tcase_add_test (tc_chain, test_clear_pt_map);
Packit 1f69a5
Packit 1f69a5
  tcase_add_test (tc_chain, test_lost_event);
Packit 1f69a5
  tcase_add_test (tc_chain, test_only_one_lost_event_on_large_gaps);
Packit 1f69a5
  tcase_add_test (tc_chain, test_two_lost_one_arrives_in_time);
Packit 1f69a5
  tcase_add_test (tc_chain, test_late_packets_still_makes_lost_events);
Packit 1f69a5
  tcase_add_test (tc_chain, test_lost_event_uses_pts);
Packit 1f69a5
  tcase_add_test (tc_chain, test_lost_event_with_backwards_rtptime);
Packit 1f69a5
Packit 1f69a5
  tcase_add_test (tc_chain, test_all_packets_are_timestamped_zero);
Packit 1f69a5
  tcase_add_loop_test (tc_chain, test_num_late_when_considered_lost_arrives, 0,
Packit 1f69a5
      2);
Packit 1f69a5
  tcase_add_test (tc_chain, test_reorder_of_non_equidistant_packets);
Packit 1f69a5
  tcase_add_test (tc_chain,
Packit 1f69a5
      test_loss_equidistant_spacing_with_parameter_packets);
Packit 1f69a5
Packit 1f69a5
  tcase_add_test (tc_chain, test_rtx_expected_next);
Packit 1f69a5
  tcase_add_test (tc_chain, test_rtx_two_missing);
Packit 1f69a5
  tcase_add_test (tc_chain, test_rtx_buffer_arrives_just_in_time);
Packit 1f69a5
  tcase_add_test (tc_chain, test_rtx_buffer_arrives_too_late);
Packit 1f69a5
  tcase_add_test (tc_chain, test_rtx_original_buffer_does_not_update_rtx_stats);
Packit 1f69a5
  tcase_add_test (tc_chain, test_rtx_duplicate_packet_updates_rtx_stats);
Packit 1f69a5
  tcase_add_test (tc_chain,
Packit 1f69a5
      test_rtx_buffer_arrives_after_lost_updates_rtx_stats);
Packit 1f69a5
  tcase_add_test (tc_chain, test_rtx_rtt_larger_than_retry_timeout);
Packit 1f69a5
  tcase_add_test (tc_chain, test_rtx_no_request_if_time_past_retry_period);
Packit 1f69a5
  tcase_add_test (tc_chain, test_rtx_same_delay_and_retry_timeout);
Packit 1f69a5
  tcase_add_test (tc_chain, test_rtx_with_backwards_rtptime);
Packit 1f69a5
  tcase_add_test (tc_chain, test_rtx_timer_reuse);
Packit 1f69a5
Packit 1f69a5
  tcase_add_test (tc_chain, test_deadline_ts_offset);
Packit 1f69a5
  tcase_add_test (tc_chain, test_push_big_gap);
Packit 1f69a5
Packit 1f69a5
  tcase_add_loop_test (tc_chain,
Packit 1f69a5
      test_considered_lost_packet_in_large_gap_arrives, 0,
Packit 1f69a5
      G_N_ELEMENTS (test_considered_lost_packet_in_large_gap_arrives_input));
Packit 1f69a5
Packit 1f69a5
  tcase_add_test (tc_chain, test_performance);
Packit 1f69a5
Packit 1f69a5
  return s;
Packit 1f69a5
}
Packit 1f69a5
Packit 1f69a5
GST_CHECK_MAIN (rtpjitterbuffer);