rtpmanager: Update codes based on 1.18.4
[platform/upstream/gst-plugins-good.git] / gst / rtpmanager / gstrtpjitterbuffer.c
index 433e61d..9608957 100644 (file)
@@ -5,6 +5,11 @@
  *  Copyright 2007 Nokia Corporation
  *   @author: Philippe Kalaf <philippe.kalaf@collabora.co.uk>.
  *  Copyright 2007 Wim Taymans <wim.taymans@gmail.com>
+ *  Copyright 2015 Kurento (http://kurento.org/)
+ *   @author: Miguel ParĂ­s <mparisdiaz@gmail.com>
+ *  Copyright 2016 Pexip AS
+ *   @author: Havard Graff <havard@pexip.com>
+ *   @author: Stian Selnes <stian@pexip.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -25,6 +30,7 @@
 
 /**
  * SECTION:element-rtpjitterbuffer
+ * @title: rtpjitterbuffer
  *
  * This element reorders and removes duplicate RTP packets as they are received
  * from a network source.
@@ -42,7 +48,7 @@
  * depayloader or other element to create concealment data or some other logic
  * to gracefully handle the missing packets.
  *
- * The jitterbuffer will use the DTS (or PTS if no DTS is set) of the incomming
+ * The jitterbuffer will use the DTS (or PTS if no DTS is set) of the incoming
  * buffer and the rtptime inside the RTP packet to create a PTS on the outgoing
  * buffer.
  *
@@ -64,7 +70,7 @@
  *
  * - If seqnum N arrived, all seqnum older than
  *     N - #GstRtpJitterBuffer:rtx-delay-reorder are considered late
- *     immediately. This is to request fast feedback for abonormally reorder
+ *     immediately. This is to request fast feedback for abnormally reorder
  *     packets before any of the previous timeouts is triggered.
  *
  * A late packet triggers the GstRTPRetransmissionRequest custom upstream
  *
  * This element will automatically be used inside rtpbin.
  *
- * <refsect2>
- * <title>Example pipelines</title>
+ * ## Example pipelines
  * |[
  * gst-launch-1.0 rtspsrc location=rtsp://192.168.1.133:8554/mpeg1or2AudioVideoTest ! rtpjitterbuffer ! rtpmpvdepay ! mpeg2dec ! xvimagesink
  * ]| Connect to a streaming server and decode the MPEG video. The jitterbuffer is
  * inserted into the pipeline to smooth out network jitter and to reorder the
  * out-of-order RTP packets.
- * </refsect2>
+ *
  */
 
 #ifdef HAVE_CONFIG_H
 #endif
 
 #include <stdlib.h>
+#include <stdio.h>
 #include <string.h>
 #include <gst/rtp/gstrtpbuffer.h>
+#include <gst/net/net.h>
 
 #include "gstrtpjitterbuffer.h"
 #include "rtpjitterbuffer.h"
 #include "rtpstats.h"
+#include "rtptimerqueue.h"
 
 #include <gst/glib-compat-private.h>
 
@@ -124,7 +132,10 @@ enum
 #define DEFAULT_LATENCY_MS          200
 #define DEFAULT_DROP_ON_LATENCY     FALSE
 #define DEFAULT_TS_OFFSET           0
+#define DEFAULT_MAX_TS_OFFSET_ADJUSTMENT 0
 #define DEFAULT_DO_LOST             FALSE
+#define DEFAULT_POST_DROP_MESSAGES  FALSE
+#define DEFAULT_DROP_MESSAGES_INTERVAL_MS   200
 #define DEFAULT_MODE                RTP_JITTER_BUFFER_MODE_SLAVE
 #define DEFAULT_PERCENT             0
 #define DEFAULT_DO_RETRANSMISSION   FALSE
@@ -136,6 +147,13 @@ enum
 #define DEFAULT_RTX_MIN_RETRY_TIMEOUT   -1
 #define DEFAULT_RTX_RETRY_PERIOD    -1
 #define DEFAULT_RTX_MAX_RETRIES    -1
+#define DEFAULT_RTX_DEADLINE       -1
+#define DEFAULT_RTX_STATS_TIMEOUT   1000
+#define DEFAULT_MAX_RTCP_RTP_TIME_DIFF 1000
+#define DEFAULT_MAX_DROPOUT_TIME    60000
+#define DEFAULT_MAX_MISORDER_TIME   2000
+#define DEFAULT_RFC7273_SYNC        FALSE
+#define DEFAULT_FASTSTART_MIN_PACKETS 0
 
 #define DEFAULT_AUTO_RTX_DELAY (20 * GST_MSECOND)
 #define DEFAULT_AUTO_RTX_TIMEOUT (40 * GST_MSECOND)
@@ -146,7 +164,10 @@ enum
   PROP_LATENCY,
   PROP_DROP_ON_LATENCY,
   PROP_TS_OFFSET,
+  PROP_MAX_TS_OFFSET_ADJUSTMENT,
   PROP_DO_LOST,
+  PROP_POST_DROP_MESSAGES,
+  PROP_DROP_MESSAGES_INTERVAL,
   PROP_MODE,
   PROP_PERCENT,
   PROP_DO_RETRANSMISSION,
@@ -158,28 +179,56 @@ enum
   PROP_RTX_MIN_RETRY_TIMEOUT,
   PROP_RTX_RETRY_PERIOD,
   PROP_RTX_MAX_RETRIES,
-  PROP_STATS
+  PROP_RTX_DEADLINE,
+  PROP_RTX_STATS_TIMEOUT,
+  PROP_STATS,
+  PROP_MAX_RTCP_RTP_TIME_DIFF,
+  PROP_MAX_DROPOUT_TIME,
+  PROP_MAX_MISORDER_TIME,
+  PROP_RFC7273_SYNC,
+  PROP_FASTSTART_MIN_PACKETS
 };
 
-#define JBUF_LOCK(priv)   (g_mutex_lock (&(priv)->jbuf_lock))
+#define JBUF_LOCK(priv)   G_STMT_START {                       \
+    GST_TRACE("Locking from thread %p", g_thread_self());      \
+    (g_mutex_lock (&(priv)->jbuf_lock));                       \
+    GST_TRACE("Locked from thread %p", g_thread_self());       \
+  } G_STMT_END
 
 #define JBUF_LOCK_CHECK(priv,label) G_STMT_START {    \
   JBUF_LOCK (priv);                                   \
   if (G_UNLIKELY (priv->srcresult != GST_FLOW_OK))    \
     goto label;                                       \
 } G_STMT_END
-#define JBUF_UNLOCK(priv) (g_mutex_unlock (&(priv)->jbuf_lock))
+#define JBUF_UNLOCK(priv) G_STMT_START {                       \
+    GST_TRACE ("Unlocking from thread %p", g_thread_self ());  \
+    (g_mutex_unlock (&(priv)->jbuf_lock));                     \
+} G_STMT_END
+
+#define JBUF_WAIT_QUEUE(priv)   G_STMT_START {            \
+  GST_DEBUG ("waiting queue");                            \
+  (priv)->waiting_queue++;                                \
+  g_cond_wait (&(priv)->jbuf_queue, &(priv)->jbuf_lock);  \
+  (priv)->waiting_queue--;                                \
+  GST_DEBUG ("waiting queue done");                       \
+} G_STMT_END
+#define JBUF_SIGNAL_QUEUE(priv) G_STMT_START {            \
+  if (G_UNLIKELY ((priv)->waiting_queue)) {               \
+    GST_DEBUG ("signal queue, %d waiters", (priv)->waiting_queue); \
+    g_cond_signal (&(priv)->jbuf_queue);                  \
+  }                                                       \
+} G_STMT_END
 
 #define JBUF_WAIT_TIMER(priv)   G_STMT_START {            \
   GST_DEBUG ("waiting timer");                            \
-  (priv)->waiting_timer = TRUE;                           \
+  (priv)->waiting_timer++;                                \
   g_cond_wait (&(priv)->jbuf_timer, &(priv)->jbuf_lock);  \
-  (priv)->waiting_timer = FALSE;                          \
+  (priv)->waiting_timer--;                                \
   GST_DEBUG ("waiting timer done");                       \
 } G_STMT_END
 #define JBUF_SIGNAL_TIMER(priv) G_STMT_START {            \
   if (G_UNLIKELY ((priv)->waiting_timer)) {               \
-    GST_DEBUG ("signal timer");                           \
+    GST_DEBUG ("signal timer, %d waiters", (priv)->waiting_timer); \
     g_cond_signal (&(priv)->jbuf_timer);                  \
   }                                                       \
 } G_STMT_END
@@ -217,6 +266,20 @@ enum
   }                                                      \
 } G_STMT_END
 
+#define GST_BUFFER_IS_RETRANSMISSION(buffer) \
+  GST_BUFFER_FLAG_IS_SET (buffer, GST_RTP_BUFFER_FLAG_RETRANSMISSION)
+
+#if !GLIB_CHECK_VERSION(2, 60, 0)
+#define g_queue_clear_full queue_clear_full
+static void
+queue_clear_full (GQueue * queue, GDestroyNotify free_func)
+{
+  gpointer data;
+
+  while ((data = g_queue_pop_head (queue)) != NULL)
+    free_func (data);
+}
+#endif
 
 struct _GstRtpJitterBufferPrivate
 {
@@ -225,7 +288,9 @@ struct _GstRtpJitterBufferPrivate
 
   RTPJitterBuffer *jbuf;
   GMutex jbuf_lock;
-  gboolean waiting_timer;
+  guint waiting_queue;
+  GCond jbuf_queue;
+  guint waiting_timer;
   GCond jbuf_timer;
   gboolean waiting_event;
   GCond jbuf_event;
@@ -236,6 +301,7 @@ struct _GstRtpJitterBufferPrivate
   gboolean ts_discont;
   gboolean active;
   guint64 out_offset;
+  guint32 segment_seqnum;
 
   gboolean timer_running;
   GThread *timer_thread;
@@ -245,7 +311,10 @@ struct _GstRtpJitterBufferPrivate
   guint64 latency_ns;
   gboolean drop_on_latency;
   gint64 ts_offset;
+  guint64 max_ts_offset_adjustment;
   gboolean do_lost;
+  gboolean post_drop_messages;
+  guint drop_messages_interval_ms;
   gboolean do_retransmission;
   gboolean rtx_next_seqnum;
   gint rtx_delay;
@@ -255,6 +324,12 @@ struct _GstRtpJitterBufferPrivate
   gint rtx_min_retry_timeout;
   gint rtx_retry_period;
   gint rtx_max_retries;
+  guint rtx_stats_timeout;
+  gint rtx_deadline_ms;
+  gint max_rtcp_rtp_time_diff;
+  guint32 max_dropout_time;
+  guint32 max_misorder_time;
+  guint faststart_min_packets;
 
   /* the last seqnum we pushed out */
   guint32 last_popped_seqnum;
@@ -265,17 +340,21 @@ struct _GstRtpJitterBufferPrivate
   /* last output time */
   GstClockTime last_out_time;
   /* last valid input timestamp and rtptime pair */
-  GstClockTime ips_dts;
+  GstClockTime ips_pts;
   guint64 ips_rtptime;
   GstClockTime packet_spacing;
+  gint equidistant;
 
   GQueue gap_packets;
 
   /* the next expected seqnum we receive */
-  GstClockTime last_in_dts;
+  GstClockTime last_in_pts;
   guint32 next_in_seqnum;
 
-  GArray *timers;
+  /* "normal" timers */
+  RtpTimerQueue *timers;
+  /* timers used for RTX statistics backlog */
+  RtpTimerQueue *rtx_stats_timers;
 
   /* start and stop ranges */
   GstClockTime npt_start;
@@ -293,7 +372,7 @@ struct _GstRtpJitterBufferPrivate
   gint last_pt;
   gint32 clock_rate;
   gint64 clock_base;
-  gint64 prev_ts_offset;
+  gint64 ts_offset_remainder;
 
   /* when we are shutting down */
   GstFlowReturn srcresult;
@@ -311,6 +390,8 @@ struct _GstRtpJitterBufferPrivate
   GstBuffer *last_sr;
 
   /* some accounting */
+  guint64 num_pushed;
+  guint64 num_lost;
   guint64 num_late;
   guint64 num_duplicates;
   guint64 num_rtx_requests;
@@ -318,39 +399,25 @@ struct _GstRtpJitterBufferPrivate
   guint64 num_rtx_failed;
   gdouble avg_rtx_num;
   guint64 avg_rtx_rtt;
+  RTPPacketRateCtx packet_rate_ctx;
 
   /* for the jitter */
   GstClockTime last_dts;
+  GstClockTime last_pts;
   guint64 last_rtptime;
   GstClockTime avg_jitter;
-};
 
+  /* for dropped packet messages */
+  GstClockTime last_drop_msg_timestamp;
+  /* accumulators; reset every time a drop message is posted */
+  guint num_too_late;
+  guint num_drop_on_latency;
+};
 typedef enum
 {
-  TIMER_TYPE_EXPECTED,
-  TIMER_TYPE_LOST,
-  TIMER_TYPE_DEADLINE,
-  TIMER_TYPE_EOS
-} TimerType;
-
-typedef struct
-{
-  guint idx;
-  guint16 seqnum;
-  guint num;
-  TimerType type;
-  GstClockTime timeout;
-  GstClockTime duration;
-  GstClockTime rtx_base;
-  GstClockTime rtx_delay;
-  GstClockTime rtx_retry;
-  GstClockTime rtx_last;
-  guint num_rtx_retry;
-} TimerData;
-
-#define GST_RTP_JITTER_BUFFER_GET_PRIVATE(o) \
-  (G_TYPE_INSTANCE_GET_PRIVATE ((o), GST_TYPE_RTP_JITTER_BUFFER, \
-                                GstRtpJitterBufferPrivate))
+  REASON_TOO_LATE,
+  REASON_DROP_ON_LATENCY
+} DropMessageReason;
 
 static GstStaticPadTemplate gst_rtp_jitter_buffer_sink_template =
 GST_STATIC_PAD_TEMPLATE ("sink",
@@ -384,7 +451,8 @@ GST_STATIC_PAD_TEMPLATE ("src",
 static guint gst_rtp_jitter_buffer_signals[LAST_SIGNAL] = { 0 };
 
 #define gst_rtp_jitter_buffer_parent_class parent_class
-G_DEFINE_TYPE (GstRtpJitterBuffer, gst_rtp_jitter_buffer, GST_TYPE_ELEMENT);
+G_DEFINE_TYPE_WITH_PRIVATE (GstRtpJitterBuffer, gst_rtp_jitter_buffer,
+    GST_TYPE_ELEMENT);
 
 /* object overrides */
 static void gst_rtp_jitter_buffer_set_property (GObject * object,
@@ -401,6 +469,8 @@ static GstPad *gst_rtp_jitter_buffer_request_new_pad (GstElement * element,
 static void gst_rtp_jitter_buffer_release_pad (GstElement * element,
     GstPad * pad);
 static GstClock *gst_rtp_jitter_buffer_provide_clock (GstElement * element);
+static gboolean gst_rtp_jitter_buffer_set_clock (GstElement * element,
+    GstClock * clock);
 
 /* pad overrides */
 static GstCaps *gst_rtp_jitter_buffer_getcaps (GstPad * pad, GstCaps * filter);
@@ -412,6 +482,8 @@ static gboolean gst_rtp_jitter_buffer_sink_event (GstPad * pad,
     GstObject * parent, GstEvent * event);
 static GstFlowReturn gst_rtp_jitter_buffer_chain (GstPad * pad,
     GstObject * parent, GstBuffer * buffer);
+static GstFlowReturn gst_rtp_jitter_buffer_chain_list (GstPad * pad,
+    GstObject * parent, GstBufferList * buffer_list);
 
 static gboolean gst_rtp_jitter_buffer_sink_rtcp_event (GstPad * pad,
     GstObject * parent, GstEvent * event);
@@ -438,13 +510,18 @@ gst_rtp_jitter_buffer_set_active (GstRtpJitterBuffer * jitterbuffer,
 static void do_handle_sync (GstRtpJitterBuffer * jitterbuffer);
 
 static void unschedule_current_timer (GstRtpJitterBuffer * jitterbuffer);
-static void remove_all_timers (GstRtpJitterBuffer * jitterbuffer);
 
 static void wait_next_timeout (GstRtpJitterBuffer * jitterbuffer);
 
 static GstStructure *gst_rtp_jitter_buffer_create_stats (GstRtpJitterBuffer *
     jitterbuffer);
 
+static void update_rtx_stats (GstRtpJitterBuffer * jitterbuffer,
+    const RtpTimer * timer, GstClockTime dts, gboolean success);
+
+static GstClockTime get_current_running_time (GstRtpJitterBuffer *
+    jitterbuffer);
+
 static void
 gst_rtp_jitter_buffer_class_init (GstRtpJitterBufferClass * klass)
 {
@@ -454,8 +531,6 @@ gst_rtp_jitter_buffer_class_init (GstRtpJitterBufferClass * klass)
   gobject_class = (GObjectClass *) klass;
   gstelement_class = (GstElementClass *) klass;
 
-  g_type_class_add_private (klass, sizeof (GstRtpJitterBufferPrivate));
-
   gobject_class->finalize = gst_rtp_jitter_buffer_finalize;
 
   gobject_class->set_property = gst_rtp_jitter_buffer_set_property;
@@ -494,6 +569,20 @@ gst_rtp_jitter_buffer_class_init (GstRtpJitterBufferClass * klass)
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   /**
+   * GstRtpJitterBuffer:max-ts-offset-adjustment:
+   *
+   * The maximum number of nanoseconds per frame that time offset may be
+   * adjusted with. This is used to avoid sudden large changes to time stamps.
+   */
+  g_object_class_install_property (gobject_class, PROP_MAX_TS_OFFSET_ADJUSTMENT,
+      g_param_spec_uint64 ("max-ts-offset-adjustment",
+          "Max Timestamp Offset Adjustment",
+          "The maximum number of nanoseconds per frame that time stamp "
+          "offsets may be adjusted (0 = no limit).", 0, G_MAXUINT64,
+          DEFAULT_MAX_TS_OFFSET_ADJUSTMENT, G_PARAM_READWRITE |
+          G_PARAM_STATIC_STRINGS));
+
+  /**
    * GstRtpJitterBuffer:do-lost:
    *
    * Send out a GstRTPPacketLost event downstream when a packet is considered
@@ -505,6 +594,47 @@ gst_rtp_jitter_buffer_class_init (GstRtpJitterBufferClass * klass)
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   /**
+   * GstRtpJitterBuffer:post-drop-messages:
+   *
+   * Post custom messages to the bus when a packet is dropped by the
+   * jitterbuffer due to arriving too late, being already considered lost,
+   * or being dropped due to the drop-on-latency property being enabled.
+   * Message is of type GST_MESSAGE_ELEMENT and contains a GstStructure named
+   * "drop-msg" with the following fields:
+   *
+   * * #guint   `seqnum`: Seqnum of dropped packet.
+   * * #guint64 `timestamp`: PTS timestamp of dropped packet.
+   * * #GString `reason`: Reason for dropping the packet.
+   * * #guint   `num-too-late`: Number of packets arriving too late since
+   *    last drop message.
+   * * #guint   `num-drop-on-latency`: Number of packets dropped due to the
+   *    drop-on-latency property since last drop message.
+   *
+   * Since: 1.18
+   */
+  g_object_class_install_property (gobject_class, PROP_POST_DROP_MESSAGES,
+      g_param_spec_boolean ("post-drop-messages", "Post drop messages",
+          "Post a custom message to the bus when a packet is dropped by the jitterbuffer",
+          DEFAULT_POST_DROP_MESSAGES,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GstRtpJitterBuffer:drop-messages-interval:
+   *
+   * Minimal time in milliseconds between posting dropped packet messages, if enabled
+   * by setting property by setting #GstRtpJitterBuffer:post-drop-messages to %TRUE.
+   * If interval is set to 0, every dropped packet will result in a drop message being posted.
+   *
+   * Since: 1.18
+   */
+  g_object_class_install_property (gobject_class, PROP_DROP_MESSAGES_INTERVAL,
+      g_param_spec_uint ("drop-messages-interval",
+          "Drop message interval",
+          "Minimal time between posting dropped packet messages", 0,
+          G_MAXUINT, DEFAULT_DROP_MESSAGES_INTERVAL_MS,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  /**
    * GstRtpJitterBuffer:mode:
    *
    * Control the buffering and timestamping mode used by the jitterbuffer.
@@ -589,17 +719,19 @@ gst_rtp_jitter_buffer_class_init (GstRtpJitterBufferClass * klass)
    * this much packet reordering.
    *
    * When -1 is used, the value will be estimated based on observed packet
-   * reordering.
+   * reordering. When 0 is used packet reordering alone will not cause a
+   * retransmission event (Since 1.10).
    *
    * Since: 1.2
    */
   g_object_class_install_property (gobject_class, PROP_RTX_DELAY_REORDER,
       g_param_spec_int ("rtx-delay-reorder", "RTX Delay Reorder",
-          "Sending retransmission event when this much reordering (-1 automatic)",
+          "Sending retransmission event when this much reordering "
+          "(0 disable)",
           -1, G_MAXINT, DEFAULT_RTX_DELAY_REORDER,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
   /**
-   * GstRtpJitterBuffer::rtx-retry-timeout:
+   * GstRtpJitterBuffer:rtx-retry-timeout:
    *
    * When no packet has been received after sending a retransmission event
    * for this time, retry sending a retransmission event.
@@ -615,7 +747,7 @@ gst_rtp_jitter_buffer_class_init (GstRtpJitterBufferClass * klass)
           "ms (-1 automatic)", -1, G_MAXINT, DEFAULT_RTX_RETRY_TIMEOUT,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
   /**
-   * GstRtpJitterBuffer::rtx-min-retry-timeout:
+   * GstRtpJitterBuffer:rtx-min-retry-timeout:
    *
    * The minimum amount of time between retry timeouts. When
    * GstRtpJitterBuffer::rtx-retry-timeout is -1, this value ensures a
@@ -662,41 +794,61 @@ gst_rtp_jitter_buffer_class_init (GstRtpJitterBufferClass * klass)
           "(-1 not limited)", -1, G_MAXINT, DEFAULT_RTX_MAX_RETRIES,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
   /**
+   * GstRtpJitterBuffer:rtx-deadline:
+   *
+   * The deadline for a valid RTX request in ms.
+   *
+   * How long the RTX RTCP will be valid for.
+   * When -1 is used, the size of the jitterbuffer will be used.
+   *
+   * Since: 1.10
+   */
+  g_object_class_install_property (gobject_class, PROP_RTX_DEADLINE,
+      g_param_spec_int ("rtx-deadline", "RTX Deadline (ms)",
+          "The deadline for a valid RTX request in milliseconds. "
+          "(-1 automatic)", -1, G_MAXINT, DEFAULT_RTX_DEADLINE,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+/**
+   * GstRtpJitterBuffer:rtx-stats-timeout:
+   *
+   * The time to wait for a retransmitted packet after it has been
+   * considered lost in order to collect RTX statistics.
+   *
+   * Since: 1.10
+   */
+  g_object_class_install_property (gobject_class, PROP_RTX_STATS_TIMEOUT,
+      g_param_spec_uint ("rtx-stats-timeout", "RTX Statistics Timeout",
+          "The time to wait for a retransmitted packet after it has been "
+          "considered lost in order to collect statistics (ms)",
+          0, G_MAXUINT, DEFAULT_RTX_STATS_TIMEOUT,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_MAX_DROPOUT_TIME,
+      g_param_spec_uint ("max-dropout-time", "Max dropout time",
+          "The maximum time (milliseconds) of missing packets tolerated.",
+          0, G_MAXINT32, DEFAULT_MAX_DROPOUT_TIME,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_MAX_MISORDER_TIME,
+      g_param_spec_uint ("max-misorder-time", "Max misorder time",
+          "The maximum time (milliseconds) of misordered packets tolerated.",
+          0, G_MAXUINT, DEFAULT_MAX_MISORDER_TIME,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+  /**
    * GstRtpJitterBuffer:stats:
    *
    * Various jitterbuffer statistics. This property returns a GstStructure
    * with name application/x-rtp-jitterbuffer-stats with the following fields:
    *
-   * <itemizedlist>
-   * <listitem>
-   *   <para>
-   *   #guint64
-   *   <classname>&quot;rtx-count&quot;</classname>:
-   *   the number of retransmissions requested.
-   *   </para>
-   * </listitem>
-   * <listitem>
-   *   <para>
-   *   #guint64
-   *   <classname>&quot;rtx-success-count&quot;</classname>:
-   *   the number of successful retransmissions.
-   *   </para>
-   * </listitem>
-   * <listitem>
-   *   <para>
-   *   #gdouble
-   *   <classname>&quot;rtx-per-packet&quot;</classname>:
-   *   average number of RTX per packet.
-   *   </para>
-   * </listitem>
-   * <listitem>
-   *   <para>
-   *   #guint64
-   *   <classname>&quot;rtx-rtt&quot;</classname>:
-   *   average round trip time per RTX.
-   *   </para>
-   * </listitem>
-   * </itemizedlist>
+   * * #guint64 `num-pushed`: the number of packets pushed out.
+   * * #guint64 `num-lost`: the number of packets considered lost.
+   * * #guint64 `num-late`: the number of packets arriving too late.
+   * * #guint64 `num-duplicates`: the number of duplicate packets.
+   * * #guint64 `avg-jitter`: the average jitter in nanoseconds.
+   * * #guint64 `rtx-count`: the number of retransmissions requested.
+   * * #guint64 `rtx-success-count`: the number of successful retransmissions.
+   * * #gdouble `rtx-per-packet`: average number of RTX per packet.
+   * * #guint64 `rtx-rtt`: average round trip time per RTX.
    *
    * Since: 1.4
    */
@@ -706,6 +858,45 @@ gst_rtp_jitter_buffer_class_init (GstRtpJitterBufferClass * klass)
           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
   /**
+   * GstRtpJitterBuffer:max-rtcp-rtp-time-diff
+   *
+   * The maximum amount of time in ms that the RTP time in the RTCP SRs
+   * is allowed to be ahead of the last RTP packet we received. Use
+   * -1 to disable ignoring of RTCP packets.
+   *
+   * Since: 1.8
+   */
+  g_object_class_install_property (gobject_class, PROP_MAX_RTCP_RTP_TIME_DIFF,
+      g_param_spec_int ("max-rtcp-rtp-time-diff", "Max RTCP RTP Time Diff",
+          "Maximum amount of time in ms that the RTP time in RTCP SRs "
+          "is allowed to be ahead (-1 disabled)", -1, G_MAXINT,
+          DEFAULT_MAX_RTCP_RTP_TIME_DIFF,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class, PROP_RFC7273_SYNC,
+      g_param_spec_boolean ("rfc7273-sync", "Sync on RFC7273 clock",
+          "Synchronize received streams to the RFC7273 clock "
+          "(requires clock and offset to be provided)", DEFAULT_RFC7273_SYNC,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GstRtpJitterBuffer:faststart-min-packets
+   *
+   * The number of consecutive packets needed to start (set to 0 to
+   * disable faststart. The jitterbuffer will by default start after the
+   * latency has elapsed)
+   *
+   * Since: 1.14
+   */
+  g_object_class_install_property (gobject_class, PROP_FASTSTART_MIN_PACKETS,
+      g_param_spec_uint ("faststart-min-packets", "Faststart minimum packets",
+          "The number of consecutive packets needed to start (set to 0 to "
+          "disable faststart. The jitterbuffer will by default start after "
+          "the latency has elapsed)",
+          0, G_MAXUINT, DEFAULT_FASTSTART_MIN_PACKETS,
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  /**
    * GstRtpJitterBuffer::request-pt-map:
    * @buffer: the object which received the signal
    * @pt: the pt
@@ -715,8 +906,7 @@ gst_rtp_jitter_buffer_class_init (GstRtpJitterBufferClass * klass)
   gst_rtp_jitter_buffer_signals[SIGNAL_REQUEST_PT_MAP] =
       g_signal_new ("request-pt-map", G_TYPE_FROM_CLASS (klass),
       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpJitterBufferClass,
-          request_pt_map), NULL, NULL, g_cclosure_marshal_generic,
-      GST_TYPE_CAPS, 1, G_TYPE_UINT);
+          request_pt_map), NULL, NULL, NULL, GST_TYPE_CAPS, 1, G_TYPE_UINT);
   /**
    * GstRtpJitterBuffer::handle-sync:
    * @buffer: the object which received the signal
@@ -727,7 +917,7 @@ gst_rtp_jitter_buffer_class_init (GstRtpJitterBufferClass * klass)
   gst_rtp_jitter_buffer_signals[SIGNAL_HANDLE_SYNC] =
       g_signal_new ("handle-sync", G_TYPE_FROM_CLASS (klass),
       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpJitterBufferClass,
-          handle_sync), NULL, NULL, g_cclosure_marshal_VOID__BOXED,
+          handle_sync), NULL, NULL, NULL,
       G_TYPE_NONE, 1, GST_TYPE_STRUCTURE | G_SIGNAL_TYPE_STATIC_SCOPE);
 
   /**
@@ -740,8 +930,7 @@ gst_rtp_jitter_buffer_class_init (GstRtpJitterBufferClass * klass)
   gst_rtp_jitter_buffer_signals[SIGNAL_ON_NPT_STOP] =
       g_signal_new ("on-npt-stop", G_TYPE_FROM_CLASS (klass),
       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpJitterBufferClass,
-          on_npt_stop), NULL, NULL, g_cclosure_marshal_VOID__VOID,
-      G_TYPE_NONE, 0, G_TYPE_NONE);
+          on_npt_stop), NULL, NULL, NULL, G_TYPE_NONE, 0, G_TYPE_NONE);
 
   /**
    * GstRtpJitterBuffer::clear-pt-map:
@@ -754,7 +943,7 @@ gst_rtp_jitter_buffer_class_init (GstRtpJitterBufferClass * klass)
       g_signal_new ("clear-pt-map", G_TYPE_FROM_CLASS (klass),
       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
       G_STRUCT_OFFSET (GstRtpJitterBufferClass, clear_pt_map), NULL, NULL,
-      g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
+      NULL, G_TYPE_NONE, 0, G_TYPE_NONE);
 
   /**
    * GstRtpJitterBuffer::set-active:
@@ -769,8 +958,7 @@ gst_rtp_jitter_buffer_class_init (GstRtpJitterBufferClass * klass)
       g_signal_new ("set-active", G_TYPE_FROM_CLASS (klass),
       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
       G_STRUCT_OFFSET (GstRtpJitterBufferClass, set_active), NULL, NULL,
-      g_cclosure_marshal_generic, G_TYPE_UINT64, 2, G_TYPE_BOOLEAN,
-      G_TYPE_UINT64);
+      NULL, G_TYPE_UINT64, 2, G_TYPE_BOOLEAN, G_TYPE_UINT64);
 
   gstelement_class->change_state =
       GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_change_state);
@@ -780,13 +968,15 @@ gst_rtp_jitter_buffer_class_init (GstRtpJitterBufferClass * klass)
       GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_release_pad);
   gstelement_class->provide_clock =
       GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_provide_clock);
+  gstelement_class->set_clock =
+      GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_set_clock);
 
-  gst_element_class_add_pad_template (gstelement_class,
-      gst_static_pad_template_get (&gst_rtp_jitter_buffer_src_template));
-  gst_element_class_add_pad_template (gstelement_class,
-      gst_static_pad_template_get (&gst_rtp_jitter_buffer_sink_template));
-  gst_element_class_add_pad_template (gstelement_class,
-      gst_static_pad_template_get (&gst_rtp_jitter_buffer_sink_rtcp_template));
+  gst_element_class_add_static_pad_template (gstelement_class,
+      &gst_rtp_jitter_buffer_src_template);
+  gst_element_class_add_static_pad_template (gstelement_class,
+      &gst_rtp_jitter_buffer_sink_template);
+  gst_element_class_add_static_pad_template (gstelement_class,
+      &gst_rtp_jitter_buffer_sink_rtcp_template);
 
   gst_element_class_set_static_metadata (gstelement_class,
       "RTP packet jitter-buffer", "Filter/Network/RTP",
@@ -799,6 +989,11 @@ gst_rtp_jitter_buffer_class_init (GstRtpJitterBufferClass * klass)
 
   GST_DEBUG_CATEGORY_INIT
       (rtpjitterbuffer_debug, "rtpjitterbuffer", 0, "RTP Jitter Buffer");
+  GST_DEBUG_REGISTER_FUNCPTR (gst_rtp_jitter_buffer_chain_rtcp);
+
+#ifndef TIZEN_FEATURE_GST_UPSTREAM_AVOID_BUILD_BREAK
+  gst_type_mark_as_plugin_api (RTP_TYPE_JITTER_BUFFER_MODE, 0);
+#endif
 }
 
 static void
@@ -806,13 +1001,17 @@ gst_rtp_jitter_buffer_init (GstRtpJitterBuffer * jitterbuffer)
 {
   GstRtpJitterBufferPrivate *priv;
 
-  priv = GST_RTP_JITTER_BUFFER_GET_PRIVATE (jitterbuffer);
+  priv = gst_rtp_jitter_buffer_get_instance_private (jitterbuffer);
   jitterbuffer->priv = priv;
 
   priv->latency_ms = DEFAULT_LATENCY_MS;
   priv->latency_ns = priv->latency_ms * GST_MSECOND;
   priv->drop_on_latency = DEFAULT_DROP_ON_LATENCY;
+  priv->ts_offset = DEFAULT_TS_OFFSET;
+  priv->max_ts_offset_adjustment = DEFAULT_MAX_TS_OFFSET_ADJUSTMENT;
   priv->do_lost = DEFAULT_DO_LOST;
+  priv->post_drop_messages = DEFAULT_POST_DROP_MESSAGES;
+  priv->drop_messages_interval_ms = DEFAULT_DROP_MESSAGES_INTERVAL_MS;
   priv->do_retransmission = DEFAULT_DO_RETRANSMISSION;
   priv->rtx_next_seqnum = DEFAULT_RTX_NEXT_SEQNUM;
   priv->rtx_delay = DEFAULT_RTX_DELAY;
@@ -822,19 +1021,34 @@ gst_rtp_jitter_buffer_init (GstRtpJitterBuffer * jitterbuffer)
   priv->rtx_min_retry_timeout = DEFAULT_RTX_MIN_RETRY_TIMEOUT;
   priv->rtx_retry_period = DEFAULT_RTX_RETRY_PERIOD;
   priv->rtx_max_retries = DEFAULT_RTX_MAX_RETRIES;
-
+  priv->rtx_deadline_ms = DEFAULT_RTX_DEADLINE;
+  priv->rtx_stats_timeout = DEFAULT_RTX_STATS_TIMEOUT;
+  priv->max_rtcp_rtp_time_diff = DEFAULT_MAX_RTCP_RTP_TIME_DIFF;
+  priv->max_dropout_time = DEFAULT_MAX_DROPOUT_TIME;
+  priv->max_misorder_time = DEFAULT_MAX_MISORDER_TIME;
+  priv->faststart_min_packets = DEFAULT_FASTSTART_MIN_PACKETS;
+
+  priv->ts_offset_remainder = 0;
   priv->last_dts = -1;
+  priv->last_pts = -1;
   priv->last_rtptime = -1;
   priv->avg_jitter = 0;
-  priv->timers = g_array_new (FALSE, TRUE, sizeof (TimerData));
+  priv->last_drop_msg_timestamp = GST_CLOCK_TIME_NONE;
+  priv->num_too_late = 0;
+  priv->num_drop_on_latency = 0;
+  priv->segment_seqnum = GST_SEQNUM_INVALID;
+  priv->timers = rtp_timer_queue_new ();
+  priv->rtx_stats_timers = rtp_timer_queue_new ();
   priv->jbuf = rtp_jitter_buffer_new ();
   g_mutex_init (&priv->jbuf_lock);
+  g_cond_init (&priv->jbuf_queue);
   g_cond_init (&priv->jbuf_timer);
   g_cond_init (&priv->jbuf_event);
   g_cond_init (&priv->jbuf_query);
   g_queue_init (&priv->gap_packets);
+  gst_segment_init (&priv->segment, GST_FORMAT_TIME);
 
-  /* reset skew detection initialy */
+  /* reset skew detection initially */
   rtp_jitter_buffer_reset_skew (priv->jbuf);
   rtp_jitter_buffer_set_delay (priv->jbuf, priv->latency_ns);
   rtp_jitter_buffer_set_buffering (priv->jbuf, FALSE);
@@ -857,6 +1071,8 @@ gst_rtp_jitter_buffer_init (GstRtpJitterBuffer * jitterbuffer)
 
   gst_pad_set_chain_function (priv->sinkpad,
       GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_chain));
+  gst_pad_set_chain_list_function (priv->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_chain_list));
   gst_pad_set_event_function (priv->sinkpad,
       GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_sink_event));
   gst_pad_set_query_function (priv->sinkpad,
@@ -868,55 +1084,19 @@ gst_rtp_jitter_buffer_init (GstRtpJitterBuffer * jitterbuffer)
   GST_OBJECT_FLAG_SET (jitterbuffer, GST_ELEMENT_FLAG_PROVIDE_CLOCK);
 }
 
-#define IS_DROPABLE(it) (((it)->type == ITEM_TYPE_BUFFER) || ((it)->type == ITEM_TYPE_LOST))
-
-#define ITEM_TYPE_BUFFER        0
-#define ITEM_TYPE_LOST          1
-#define ITEM_TYPE_EVENT         2
-#define ITEM_TYPE_QUERY         3
-
-static RTPJitterBufferItem *
-alloc_item (gpointer data, guint type, GstClockTime dts, GstClockTime pts,
-    guint seqnum, guint count, guint rtptime)
-{
-  RTPJitterBufferItem *item;
-
-  item = g_slice_new (RTPJitterBufferItem);
-  item->data = data;
-  item->next = NULL;
-  item->prev = NULL;
-  item->type = type;
-  item->dts = dts;
-  item->pts = pts;
-  item->seqnum = seqnum;
-  item->count = count;
-  item->rtptime = rtptime;
-
-  return item;
-}
-
-static void
-free_item (RTPJitterBufferItem * item)
-{
-  g_return_if_fail (item != NULL);
-
-  if (item->data && item->type != ITEM_TYPE_QUERY)
-    gst_mini_object_unref (item->data);
-  g_slice_free (RTPJitterBufferItem, item);
-}
-
 static void
-free_item_and_retain_events (RTPJitterBufferItem * item, gpointer user_data)
+free_item_and_retain_sticky_events (RTPJitterBufferItem * item,
+    gpointer user_data)
 {
   GList **l = user_data;
 
   if (item->data && item->type == ITEM_TYPE_EVENT
       && GST_EVENT_IS_STICKY (item->data)) {
     *l = g_list_prepend (*l, item->data);
-  } else if (item->data && item->type != ITEM_TYPE_QUERY) {
-    gst_mini_object_unref (item->data);
+    item->data = NULL;
   }
-  g_slice_free (RTPJitterBufferItem, item);
+
+  rtp_jitter_buffer_free_item (item);
 }
 
 static void
@@ -928,13 +1108,15 @@ gst_rtp_jitter_buffer_finalize (GObject * object)
   jitterbuffer = GST_RTP_JITTER_BUFFER (object);
   priv = jitterbuffer->priv;
 
-  g_array_free (priv->timers, TRUE);
+  g_object_unref (priv->timers);
+  g_object_unref (priv->rtx_stats_timers);
   g_mutex_clear (&priv->jbuf_lock);
+  g_cond_clear (&priv->jbuf_queue);
   g_cond_clear (&priv->jbuf_timer);
   g_cond_clear (&priv->jbuf_event);
   g_cond_clear (&priv->jbuf_query);
 
-  rtp_jitter_buffer_flush (priv->jbuf, (GFunc) free_item, NULL);
+  rtp_jitter_buffer_flush (priv->jbuf, NULL, NULL);
   g_queue_foreach (&priv->gap_packets, (GFunc) gst_buffer_unref, NULL);
   g_queue_clear (&priv->gap_packets);
   g_object_unref (priv->jbuf);
@@ -1086,6 +1268,16 @@ gst_rtp_jitter_buffer_provide_clock (GstElement * element)
   return gst_system_clock_obtain ();
 }
 
+static gboolean
+gst_rtp_jitter_buffer_set_clock (GstElement * element, GstClock * clock)
+{
+  GstRtpJitterBuffer *jitterbuffer = GST_RTP_JITTER_BUFFER (element);
+
+  rtp_jitter_buffer_set_pipeline_clock (jitterbuffer->priv->jbuf, clock);
+
+  return GST_ELEMENT_CLASS (parent_class)->set_clock (element, clock);
+}
+
 static void
 gst_rtp_jitter_buffer_clear_pt_map (GstRtpJitterBuffer * jitterbuffer)
 {
@@ -1132,7 +1324,7 @@ gst_rtp_jitter_buffer_set_active (GstRtpJitterBuffer * jbuf, gboolean active,
   }
   if ((item = rtp_jitter_buffer_peek (priv->jbuf))) {
     /* head buffer timestamp and offset gives our output time */
-    last_out = item->dts + priv->ts_offset;
+    last_out = item->pts + priv->ts_offset;
   } else {
     /* use last known time when the buffer is empty */
     last_out = priv->last_out_time;
@@ -1178,25 +1370,57 @@ gst_rtp_jitter_buffer_getcaps (GstPad * pad, GstCaps * filter)
   return caps;
 }
 
+/* g_ascii_string_to_unsigned is available since 2.54. Get rid of this wrapper
+ * when we bump the version in 1.18 */
+#if !GLIB_CHECK_VERSION(2,54,0)
+#define g_ascii_string_to_unsigned _gst_jitter_buffer_ascii_string_to_unsigned
+static gboolean
+_gst_jitter_buffer_ascii_string_to_unsigned (const gchar * str, guint base,
+    guint64 min, guint64 max, guint64 * out_num, GError ** error)
+{
+  gchar *endptr = NULL;
+  *out_num = g_ascii_strtoull (str, &endptr, base);
+  if (errno)
+    return FALSE;
+  if (endptr == str)
+    return FALSE;
+  return TRUE;
+}
+#endif
+
 /*
  * Must be called with JBUF_LOCK held
  */
 
 static gboolean
 gst_jitter_buffer_sink_parse_caps (GstRtpJitterBuffer * jitterbuffer,
-    GstCaps * caps)
+    GstCaps * caps, gint pt)
 {
   GstRtpJitterBufferPrivate *priv;
   GstStructure *caps_struct;
   guint val;
+  gint payload = -1;
   GstClockTime tval;
+  const gchar *ts_refclk, *mediaclk;
 
   priv = jitterbuffer->priv;
 
   /* first parse the caps */
   caps_struct = gst_caps_get_structure (caps, 0);
 
-  GST_DEBUG_OBJECT (jitterbuffer, "got caps");
+  GST_DEBUG_OBJECT (jitterbuffer, "got caps %" GST_PTR_FORMAT, caps);
+
+  if (gst_structure_get_int (caps_struct, "payload", &payload) && pt != -1
+      && payload != pt) {
+    GST_ERROR_OBJECT (jitterbuffer,
+        "Got caps with wrong payload type (got %d, expected %d)", pt, payload);
+    return FALSE;
+  }
+
+  if (payload != -1) {
+    GST_DEBUG_OBJECT (jitterbuffer, "Got payload type %d", payload);
+    priv->last_pt = payload;
+  }
 
   /* we need a clock-rate to convert the rtp timestamps to GStreamer time and to
    * measure the amount of data in the buffer */
@@ -1210,6 +1434,8 @@ gst_jitter_buffer_sink_parse_caps (GstRtpJitterBuffer * jitterbuffer,
 
   rtp_jitter_buffer_set_clock_rate (priv->jbuf, priv->clock_rate);
 
+  gst_rtp_packet_rate_ctx_reset (&priv->packet_rate_ctx, priv->clock_rate);
+
   /* The clock base is the RTP timestamp corrsponding to the npt-start value. We
    * can use this to track the amount of time elapsed on the sender. */
   if (gst_structure_get_uint (caps_struct, "clock-base", &val))
@@ -1254,6 +1480,76 @@ gst_jitter_buffer_sink_parse_caps (GstRtpJitterBuffer * jitterbuffer,
       "npt start/stop: %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
       GST_TIME_ARGS (priv->npt_start), GST_TIME_ARGS (priv->npt_stop));
 
+  if ((ts_refclk = gst_structure_get_string (caps_struct, "a-ts-refclk"))) {
+    GstClock *clock = NULL;
+    guint64 clock_offset = -1;
+
+    GST_DEBUG_OBJECT (jitterbuffer, "Have timestamp reference clock %s",
+        ts_refclk);
+
+    if (g_str_has_prefix (ts_refclk, "ntp=")) {
+      if (g_str_has_prefix (ts_refclk, "ntp=/traceable/")) {
+        GST_FIXME_OBJECT (jitterbuffer, "Can't handle traceable NTP clocks");
+      } else {
+        const gchar *host, *portstr;
+        gchar *hostname;
+        guint port;
+
+        host = ts_refclk + sizeof ("ntp=") - 1;
+        if (host[0] == '[') {
+          /* IPv6 */
+          portstr = strchr (host, ']');
+          if (portstr && portstr[1] == ':')
+            portstr = portstr + 1;
+          else
+            portstr = NULL;
+        } else {
+          portstr = strrchr (host, ':');
+        }
+
+
+        if (!portstr || sscanf (portstr, ":%u", &port) != 1)
+          port = 123;
+
+        if (portstr)
+          hostname = g_strndup (host, (portstr - host));
+        else
+          hostname = g_strdup (host);
+
+        clock = gst_ntp_clock_new (NULL, hostname, port, 0);
+        g_free (hostname);
+      }
+    } else if (g_str_has_prefix (ts_refclk, "ptp=IEEE1588-2008:")) {
+      const gchar *domainstr =
+          ts_refclk + sizeof ("ptp=IEEE1588-2008:XX-XX-XX-XX-XX-XX-XX-XX") - 1;
+      guint domain;
+
+      if (domainstr[0] != ':' || sscanf (domainstr, ":%u", &domain) != 1)
+        domain = 0;
+
+      clock = gst_ptp_clock_new (NULL, domain);
+    } else {
+      GST_FIXME_OBJECT (jitterbuffer, "Unsupported timestamp reference clock");
+    }
+
+    if ((mediaclk = gst_structure_get_string (caps_struct, "a-mediaclk"))) {
+      GST_DEBUG_OBJECT (jitterbuffer, "Got media clock %s", mediaclk);
+
+      if (!g_str_has_prefix (mediaclk, "direct=") ||
+          !g_ascii_string_to_unsigned (&mediaclk[8], 10, 0, G_MAXUINT64,
+              &clock_offset, NULL))
+        GST_FIXME_OBJECT (jitterbuffer, "Unsupported media clock");
+      if (strstr (mediaclk, "rate=") != NULL) {
+        GST_FIXME_OBJECT (jitterbuffer, "Rate property not supported");
+        clock_offset = -1;
+      }
+    }
+
+    rtp_jitter_buffer_set_media_clock (priv->jbuf, clock, clock_offset);
+  } else {
+    rtp_jitter_buffer_set_media_clock (priv->jbuf, NULL, -1);
+  }
+
   return TRUE;
 
   /* ERRORS */
@@ -1283,6 +1579,7 @@ gst_rtp_jitter_buffer_flush_start (GstRtpJitterBuffer * jitterbuffer)
   /* this unblocks any waiting pops on the src pad task */
   JBUF_SIGNAL_EVENT (priv);
   JBUF_SIGNAL_QUERY (priv, FALSE);
+  JBUF_SIGNAL_QUEUE (priv);
   JBUF_UNLOCK (priv);
 }
 
@@ -1299,11 +1596,11 @@ gst_rtp_jitter_buffer_flush_stop (GstRtpJitterBuffer * jitterbuffer)
   priv->srcresult = GST_FLOW_OK;
   gst_segment_init (&priv->segment, GST_FORMAT_TIME);
   priv->last_popped_seqnum = -1;
-  priv->last_out_time = -1;
+  priv->last_out_time = GST_CLOCK_TIME_NONE;
   priv->next_seqnum = -1;
   priv->seqnum_base = -1;
   priv->ips_rtptime = -1;
-  priv->ips_dts = GST_CLOCK_TIME_NONE;
+  priv->ips_pts = GST_CLOCK_TIME_NONE;
   priv->packet_spacing = 0;
   priv->next_in_seqnum = -1;
   priv->clock_rate = -1;
@@ -1315,12 +1612,17 @@ gst_rtp_jitter_buffer_flush_stop (GstRtpJitterBuffer * jitterbuffer)
   priv->avg_jitter = 0;
   priv->last_dts = -1;
   priv->last_rtptime = -1;
-  priv->last_in_dts = 0;
+  priv->last_in_pts = 0;
+  priv->equidistant = 0;
+  priv->segment_seqnum = GST_SEQNUM_INVALID;
+  priv->last_drop_msg_timestamp = GST_CLOCK_TIME_NONE;
+  priv->num_too_late = 0;
+  priv->num_drop_on_latency = 0;
   GST_DEBUG_OBJECT (jitterbuffer, "flush and reset jitterbuffer");
-  rtp_jitter_buffer_flush (priv->jbuf, (GFunc) free_item, NULL);
+  rtp_jitter_buffer_flush (priv->jbuf, NULL, NULL);
   rtp_jitter_buffer_disable_buffering (priv->jbuf, FALSE);
   rtp_jitter_buffer_reset_skew (priv->jbuf);
-  remove_all_timers (jitterbuffer);
+  rtp_timer_queue_remove_all (priv->timers);
   g_queue_foreach (&priv->gap_packets, (GFunc) gst_buffer_unref, NULL);
   g_queue_clear (&priv->gap_packets);
   JBUF_UNLOCK (priv);
@@ -1386,6 +1688,7 @@ gst_rtp_jitter_buffer_change_state (GstElement * element,
       /* block until we go to PLAYING */
       priv->blocked = TRUE;
       priv->timer_running = TRUE;
+      priv->srcresult = GST_FLOW_OK;
       priv->timer_thread =
           g_thread_new ("timer", (GThreadFunc) wait_next_timeout, jitterbuffer);
       JBUF_UNLOCK (priv);
@@ -1424,9 +1727,11 @@ gst_rtp_jitter_buffer_change_state (GstElement * element,
       JBUF_LOCK (priv);
       gst_buffer_replace (&priv->last_sr, NULL);
       priv->timer_running = FALSE;
+      priv->srcresult = GST_FLOW_FLUSHING;
       unschedule_current_timer (jitterbuffer);
       JBUF_SIGNAL_TIMER (priv);
       JBUF_SIGNAL_QUERY (priv, FALSE);
+      JBUF_SIGNAL_QUEUE (priv);
       JBUF_UNLOCK (priv);
       g_thread_join (priv->timer_thread);
       priv->timer_thread = NULL;
@@ -1490,7 +1795,6 @@ static gboolean
 queue_event (GstRtpJitterBuffer * jitterbuffer, GstEvent * event)
 {
   GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
-  RTPJitterBufferItem *item;
   gboolean head;
 
   switch (GST_EVENT_TYPE (event)) {
@@ -1499,19 +1803,29 @@ queue_event (GstRtpJitterBuffer * jitterbuffer, GstEvent * event)
       GstCaps *caps;
 
       gst_event_parse_caps (event, &caps);
-      gst_jitter_buffer_sink_parse_caps (jitterbuffer, caps);
+      gst_jitter_buffer_sink_parse_caps (jitterbuffer, caps, -1);
       break;
     }
     case GST_EVENT_SEGMENT:
-      gst_event_copy_segment (event, &priv->segment);
+    {
+      GstSegment segment;
+      gst_event_copy_segment (event, &segment);
+
+      priv->segment_seqnum = gst_event_get_seqnum (event);
 
       /* we need time for now */
-      if (priv->segment.format != GST_FORMAT_TIME)
-        goto newseg_wrong_format;
+      if (segment.format != GST_FORMAT_TIME) {
+        GST_DEBUG_OBJECT (jitterbuffer, "ignoring non-TIME newsegment");
+        gst_event_unref (event);
 
-      GST_DEBUG_OBJECT (jitterbuffer,
-          "segment:  %" GST_SEGMENT_FORMAT, &priv->segment);
+        gst_segment_init (&segment, GST_FORMAT_TIME);
+        event = gst_event_new_segment (&segment);
+        gst_event_set_seqnum (event, priv->segment_seqnum);
+      }
+
+      priv->segment = segment;
       break;
+    }
     case GST_EVENT_EOS:
       priv->eos = TRUE;
       rtp_jitter_buffer_disable_buffering (priv->jbuf, TRUE);
@@ -1520,22 +1834,12 @@ queue_event (GstRtpJitterBuffer * jitterbuffer, GstEvent * event)
       break;
   }
 
-
   GST_DEBUG_OBJECT (jitterbuffer, "adding event");
-  item = alloc_item (event, ITEM_TYPE_EVENT, -1, -1, -1, 0, -1);
-  rtp_jitter_buffer_insert (priv->jbuf, item, &head, NULL);
-  if (head)
+  head = rtp_jitter_buffer_append_event (priv->jbuf, event);
+  if (head || priv->eos)
     JBUF_SIGNAL_EVENT (priv);
 
   return TRUE;
-
-  /* ERRORS */
-newseg_wrong_format:
-  {
-    GST_DEBUG_OBJECT (jitterbuffer, "received non TIME newsegment");
-    gst_event_unref (event);
-    return FALSE;
-  }
 }
 
 static gboolean
@@ -1637,7 +1941,7 @@ gst_rtp_jitter_buffer_sink_rtcp_event (GstPad * pad, GstObject * parent,
 }
 
 /*
- * Must be called with JBUF_LOCK held, will release the LOCK when emiting the
+ * Must be called with JBUF_LOCK held, will release the LOCK when emitting the
  * signal. The function returns GST_FLOW_ERROR when a parsing error happened and
  * GST_FLOW_FLUSHING when the element is shutting down. On success
  * GST_FLOW_OK is returned.
@@ -1671,7 +1975,7 @@ gst_rtp_jitter_buffer_get_clock_rate (GstRtpJitterBuffer * jitterbuffer,
   if (!caps)
     goto no_caps;
 
-  res = gst_jitter_buffer_sink_parse_caps (jitterbuffer, caps);
+  res = gst_jitter_buffer_sink_parse_caps (jitterbuffer, caps, pt);
   gst_caps_unref (caps);
 
   if (G_UNLIKELY (!res))
@@ -1718,201 +2022,175 @@ check_buffering_percent (GstRtpJitterBuffer * jitterbuffer, gint percent)
   return message;
 }
 
-static GstClockTime
-apply_offset (GstRtpJitterBuffer * jitterbuffer, GstClockTime timestamp)
+/* call with jbuf lock held */
+static GstMessage *
+new_drop_message (GstRtpJitterBuffer * jitterbuffer, guint seqnum,
+    GstClockTime timestamp, DropMessageReason reason)
 {
-  GstRtpJitterBufferPrivate *priv;
-
-  priv = jitterbuffer->priv;
-
-  if (timestamp == -1)
-    return -1;
 
-  /* apply the timestamp offset, this is used for inter stream sync */
-  timestamp += priv->ts_offset;
-  /* add the offset, this is used when buffering */
-  timestamp += priv->out_offset;
+  GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
+  GstMessage *drop_msg = NULL;
+  GstStructure *s;
+  GstClockTime current_time;
+  GstClockTime time_diff;
+  const gchar *reason_str;
+
+  current_time = get_current_running_time (jitterbuffer);
+  time_diff = current_time - priv->last_drop_msg_timestamp;
+
+  if (reason == REASON_TOO_LATE) {
+    priv->num_too_late++;
+    reason_str = "too-late";
+  } else if (reason == REASON_DROP_ON_LATENCY) {
+    priv->num_drop_on_latency++;
+    reason_str = "drop-on-latency";
+  } else {
+    GST_WARNING_OBJECT (jitterbuffer, "Invalid reason for drop message");
+    return drop_msg;
+  }
 
-  return timestamp;
+  /* Only create new drop_msg if time since last drop_msg is larger that
+   * that the set interval, or if it is the first drop message posted */
+  if ((time_diff >= priv->drop_messages_interval_ms * GST_MSECOND) ||
+      (priv->last_drop_msg_timestamp == GST_CLOCK_TIME_NONE)) {
+
+    s = gst_structure_new ("drop-msg",
+        "seqnum", G_TYPE_UINT, seqnum,
+        "timestamp", GST_TYPE_CLOCK_TIME, timestamp,
+        "reason", G_TYPE_STRING, reason_str,
+        "num-too-late", G_TYPE_UINT, priv->num_too_late,
+        "num-drop-on-latency", G_TYPE_UINT, priv->num_drop_on_latency, NULL);
+
+    priv->last_drop_msg_timestamp = current_time;
+    priv->num_too_late = 0;
+    priv->num_drop_on_latency = 0;
+    drop_msg = gst_message_new_element (GST_OBJECT (jitterbuffer), s);
+  }
+  return drop_msg;
 }
 
-static TimerData *
-find_timer (GstRtpJitterBuffer * jitterbuffer, TimerType type, guint16 seqnum)
+
+static inline GstClockTimeDiff
+timeout_offset (GstRtpJitterBuffer * jitterbuffer)
 {
   GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
-  TimerData *timer = NULL;
-  gint i, len;
-
-  len = priv->timers->len;
-  for (i = 0; i < len; i++) {
-    TimerData *test = &g_array_index (priv->timers, TimerData, i);
-    if (test->seqnum == seqnum && test->type == type) {
-      timer = test;
-      break;
-    }
-  }
-  return timer;
+  return priv->ts_offset + priv->out_offset + priv->latency_ns;
 }
 
-static void
-unschedule_current_timer (GstRtpJitterBuffer * jitterbuffer)
+static inline GstClockTime
+get_pts_timeout (const RtpTimer * timer)
 {
-  GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
+  if (timer->timeout == -1)
+    return -1;
 
-  if (priv->clock_id) {
-    GST_DEBUG_OBJECT (jitterbuffer, "unschedule current timer");
-    gst_clock_id_unschedule (priv->clock_id);
-    priv->clock_id = NULL;
-  }
+  return timer->timeout - timer->offset;
 }
 
-static GstClockTime
-get_timeout (GstRtpJitterBuffer * jitterbuffer, TimerData * timer)
+static void
+update_timer_offsets (GstRtpJitterBuffer * jitterbuffer)
 {
   GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
-  GstClockTime test_timeout;
-
-  if ((test_timeout = timer->timeout) == -1)
-    return -1;
+  RtpTimer *test = rtp_timer_queue_peek_earliest (priv->timers);
+  GstClockTimeDiff new_offset = timeout_offset (jitterbuffer);
+
+  while (test) {
+    if (test->type != RTP_TIMER_EXPECTED) {
+      test->timeout = get_pts_timeout (test) + new_offset;
+      test->offset = new_offset;
+      /* as we apply the offset on all timers, the order of timers won't
+       * change and we can skip updating the timer queue */
+    }
 
-  if (timer->type != TIMER_TYPE_EXPECTED) {
-    /* add our latency and offset to get output times. */
-    test_timeout = apply_offset (jitterbuffer, test_timeout);
-    test_timeout += priv->latency_ns;
+    test = rtp_timer_get_next (test);
   }
-  return test_timeout;
 }
 
 static void
-recalculate_timer (GstRtpJitterBuffer * jitterbuffer, TimerData * timer)
+update_offset (GstRtpJitterBuffer * jitterbuffer)
 {
-  GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
+  GstRtpJitterBufferPrivate *priv;
 
-  if (priv->clock_id) {
-    GstClockTime timeout = get_timeout (jitterbuffer, timer);
+  priv = jitterbuffer->priv;
 
-    GST_DEBUG ("%" GST_TIME_FORMAT " <> %" GST_TIME_FORMAT,
-        GST_TIME_ARGS (timeout), GST_TIME_ARGS (priv->timer_timeout));
+  if (priv->ts_offset_remainder != 0) {
+    GST_DEBUG ("adjustment %" G_GUINT64_FORMAT " remain %" G_GINT64_FORMAT
+        " off %" G_GINT64_FORMAT, priv->max_ts_offset_adjustment,
+        priv->ts_offset_remainder, priv->ts_offset);
+    if (ABS (priv->ts_offset_remainder) > priv->max_ts_offset_adjustment) {
+      if (priv->ts_offset_remainder > 0) {
+        priv->ts_offset += priv->max_ts_offset_adjustment;
+        priv->ts_offset_remainder -= priv->max_ts_offset_adjustment;
+      } else {
+        priv->ts_offset -= priv->max_ts_offset_adjustment;
+        priv->ts_offset_remainder += priv->max_ts_offset_adjustment;
+      }
+    } else {
+      priv->ts_offset += priv->ts_offset_remainder;
+      priv->ts_offset_remainder = 0;
+    }
 
-    if (timeout == -1 || timeout < priv->timer_timeout)
-      unschedule_current_timer (jitterbuffer);
+    update_timer_offsets (jitterbuffer);
   }
 }
 
-static TimerData *
-add_timer (GstRtpJitterBuffer * jitterbuffer, TimerType type,
-    guint16 seqnum, guint num, GstClockTime timeout, GstClockTime delay,
-    GstClockTime duration)
+static GstClockTime
+apply_offset (GstRtpJitterBuffer * jitterbuffer, GstClockTime timestamp)
 {
-  GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
-  TimerData *timer;
-  gint len;
+  GstRtpJitterBufferPrivate *priv;
 
-  GST_DEBUG_OBJECT (jitterbuffer,
-      "add timer %d for seqnum %d to %" GST_TIME_FORMAT ", delay %"
-      GST_TIME_FORMAT, type, seqnum, GST_TIME_ARGS (timeout),
-      GST_TIME_ARGS (delay));
-
-  len = priv->timers->len;
-  g_array_set_size (priv->timers, len + 1);
-  timer = &g_array_index (priv->timers, TimerData, len);
-  timer->idx = len;
-  timer->type = type;
-  timer->seqnum = seqnum;
-  timer->num = num;
-  timer->timeout = timeout + delay;
-  timer->duration = duration;
-  if (type == TIMER_TYPE_EXPECTED) {
-    timer->rtx_base = timeout;
-    timer->rtx_delay = delay;
-    timer->rtx_retry = 0;
-  }
-  timer->num_rtx_retry = 0;
-  recalculate_timer (jitterbuffer, timer);
-  JBUF_SIGNAL_TIMER (priv);
+  priv = jitterbuffer->priv;
+
+  if (timestamp == -1)
+    return -1;
+
+  /* apply the timestamp offset, this is used for inter stream sync */
+  timestamp += priv->ts_offset;
+  /* add the offset, this is used when buffering */
+  timestamp += priv->out_offset;
 
-  return timer;
+  return timestamp;
 }
 
 static void
-reschedule_timer (GstRtpJitterBuffer * jitterbuffer, TimerData * timer,
-    guint16 seqnum, GstClockTime timeout, GstClockTime delay, gboolean reset)
+unschedule_current_timer (GstRtpJitterBuffer * jitterbuffer)
 {
   GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
-  gboolean seqchange, timechange;
-  guint16 oldseq;
-
-  seqchange = timer->seqnum != seqnum;
-  timechange = timer->timeout != timeout;
-
-  if (!seqchange && !timechange)
-    return;
-
-  oldseq = timer->seqnum;
-
-  GST_DEBUG_OBJECT (jitterbuffer,
-      "replace timer for seqnum %d->%d to %" GST_TIME_FORMAT,
-      oldseq, seqnum, GST_TIME_ARGS (timeout + delay));
-
-  timer->timeout = timeout + delay;
-  timer->seqnum = seqnum;
-  if (reset) {
-    timer->rtx_base = timeout;
-    timer->rtx_delay = delay;
-    timer->rtx_retry = 0;
-  }
-  if (seqchange)
-    timer->num_rtx_retry = 0;
 
   if (priv->clock_id) {
-    /* we changed the seqnum and there is a timer currently waiting with this
-     * seqnum, unschedule it */
-    if (seqchange && priv->timer_seqnum == oldseq)
-      unschedule_current_timer (jitterbuffer);
-    /* we changed the time, check if it is earlier than what we are waiting
-     * for and unschedule if so */
-    else if (timechange)
-      recalculate_timer (jitterbuffer, timer);
+    GST_DEBUG_OBJECT (jitterbuffer, "unschedule current timer");
+    gst_clock_id_unschedule (priv->clock_id);
+    priv->clock_id = NULL;
   }
 }
 
-static TimerData *
-set_timer (GstRtpJitterBuffer * jitterbuffer, TimerType type,
-    guint16 seqnum, GstClockTime timeout)
+static void
+update_current_timer (GstRtpJitterBuffer * jitterbuffer)
 {
-  TimerData *timer;
+  GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
+  RtpTimer *timer;
+
+  timer = rtp_timer_queue_peek_earliest (priv->timers);
 
-  /* find the seqnum timer */
-  timer = find_timer (jitterbuffer, type, seqnum);
+  /* we never need to wakeup the timer thread when there is no more timers, if
+   * it was waiting on a clock id, it will simply do later and then wait on
+   * the conditions */
   if (timer == NULL) {
-    timer = add_timer (jitterbuffer, type, seqnum, 0, timeout, 0, -1);
-  } else {
-    reschedule_timer (jitterbuffer, timer, seqnum, timeout, 0, FALSE);
+    GST_DEBUG_OBJECT (jitterbuffer, "no more timers");
+    return;
   }
-  return timer;
-}
 
-static void
-remove_timer (GstRtpJitterBuffer * jitterbuffer, TimerData * timer)
-{
-  GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
-  guint idx;
+  GST_DEBUG_OBJECT (jitterbuffer, "waiting till %" GST_TIME_FORMAT
+      " and earliest timeout is at %" GST_TIME_FORMAT,
+      GST_TIME_ARGS (priv->timer_timeout), GST_TIME_ARGS (timer->timeout));
 
-  if (priv->clock_id && priv->timer_seqnum == timer->seqnum)
-    unschedule_current_timer (jitterbuffer);
+  /* wakeup the timer thread in case the timer queue was empty */
+  JBUF_SIGNAL_TIMER (priv);
 
-  idx = timer->idx;
-  GST_DEBUG_OBJECT (jitterbuffer, "removed index %d", idx);
-  g_array_remove_index_fast (priv->timers, idx);
-  timer->idx = idx;
-}
+  /* no need to wait if the current wait is earlier or later */
+  if (timer->timeout != -1 && timer->timeout >= priv->timer_timeout)
+    return;
 
-static void
-remove_all_timers (GstRtpJitterBuffer * jitterbuffer)
-{
-  GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
-  GST_DEBUG_OBJECT (jitterbuffer, "removed all timers");
-  g_array_set_size (priv->timers, 0);
+  /* for other cases, force a reschedule of the timer thread */
   unschedule_current_timer (jitterbuffer);
 }
 
@@ -1923,6 +2201,15 @@ get_rtx_delay (GstRtpJitterBufferPrivate * priv)
   GstClockTime delay;
 
   if (priv->rtx_delay == -1) {
+    /* the maximum delay for any RTX-packet is given by the latency, since
+       anything after that is considered lost. For various calulcations,
+       (given large avg_jitter and/or packet_spacing), the resulting delay
+       could exceed the configured latency, ending up issuing an RTX-request
+       that would never arrive in time. To help this we cap the delay
+       for any RTX with the last possible time it could still arrive in time. */
+    GstClockTime delay_max = (priv->latency_ns > priv->avg_rtx_rtt) ?
+        priv->latency_ns - priv->avg_rtx_rtt : priv->latency_ns;
+
     if (priv->avg_jitter == 0 && priv->packet_spacing == 0) {
       delay = DEFAULT_AUTO_RTX_DELAY;
     } else {
@@ -1930,6 +2217,8 @@ get_rtx_delay (GstRtpJitterBufferPrivate * priv)
        * packet spacing is a good margin */
       delay = MAX (priv->avg_jitter * 2, priv->packet_spacing / 2);
     }
+
+    delay = MIN (delay_max, delay);
   } else {
     delay = priv->rtx_delay * GST_MSECOND;
   }
@@ -1951,120 +2240,119 @@ get_rtx_delay (GstRtpJitterBufferPrivate * priv)
  */
 static void
 update_timers (GstRtpJitterBuffer * jitterbuffer, guint16 seqnum,
-    GstClockTime dts, gboolean do_next_seqnum)
+    GstClockTime dts, GstClockTime pts, gboolean do_next_seqnum,
+    gboolean is_rtx, RtpTimer * timer)
 {
   GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
-  TimerData *timer = NULL;
-  gint i, len;
-
-  /* go through all timers and unschedule the ones with a large gap, also find
-   * the timer for the seqnum */
-  len = priv->timers->len;
-  for (i = 0; i < len; i++) {
-    TimerData *test = &g_array_index (priv->timers, TimerData, i);
-    gint gap;
+  gboolean is_stats_timer = FALSE;
+
+  if (timer && rtp_timer_queue_find (priv->rtx_stats_timers, timer->seqnum))
+    is_stats_timer = TRUE;
 
-    gap = gst_rtp_buffer_compare_seqnum (test->seqnum, seqnum);
+  /* schedule immediatly expected timer which exceed the maximum RTX delay
+   * reorder configuration */
+  if (priv->do_retransmission && priv->rtx_delay_reorder > 0) {
+    RtpTimer *test = rtp_timer_queue_peek_earliest (priv->timers);
+    while (test) {
+      gint gap;
 
-    GST_DEBUG_OBJECT (jitterbuffer, "%d, %d, #%d<->#%d gap %d", i,
-        test->type, test->seqnum, seqnum, gap);
+      /* filter the timer type to speed up this loop */
+      if (test->type != RTP_TIMER_EXPECTED) {
+        test = rtp_timer_get_next (test);
+        continue;
+      }
+
+      gap = gst_rtp_buffer_compare_seqnum (test->seqnum, seqnum);
+
+      GST_DEBUG_OBJECT (jitterbuffer, "%d, #%d<->#%d gap %d",
+          test->type, test->seqnum, seqnum, gap);
 
-    if (gap == 0) {
-      GST_DEBUG ("found timer for current seqnum");
-      /* the timer for the current seqnum */
-      timer = test;
-      /* when no retransmission, we can stop now, we only need to find the
-       * timer for the current seqnum */
-      if (!priv->do_retransmission)
+      /* if this expected packet have a smaller gap then the configured one,
+       * then earlier timer are not expected to have bigger gap as the timer
+       * queue is ordered */
+      if (gap <= priv->rtx_delay_reorder)
         break;
-    } else if (gap > priv->rtx_delay_reorder) {
+
       /* max gap, we exceeded the max reorder distance and we don't expect the
        * missing packet to be this reordered */
-      if (test->num_rtx_retry == 0 && test->type == TIMER_TYPE_EXPECTED)
-        reschedule_timer (jitterbuffer, test, test->seqnum, -1, 0, FALSE);
+      if (test->num_rtx_retry == 0 && test->type == RTP_TIMER_EXPECTED)
+        rtp_timer_queue_update_timer (priv->timers, test, test->seqnum,
+            -1, 0, 0, FALSE);
+
+      test = rtp_timer_get_next (test);
     }
   }
 
   do_next_seqnum = do_next_seqnum && priv->packet_spacing > 0
       && priv->do_retransmission && priv->rtx_next_seqnum;
 
-  if (timer && timer->type != TIMER_TYPE_DEADLINE) {
+  if (timer && timer->type != RTP_TIMER_DEADLINE) {
     if (timer->num_rtx_retry > 0) {
-      GstClockTime rtx_last, delay;
-
-      /* we scheduled a retry for this packet and now we have it */
-      priv->num_rtx_success++;
-      /* all the previous retry attempts failed */
-      priv->num_rtx_failed += timer->num_rtx_retry - 1;
-      /* number of retries before receiving the packet */
-      if (priv->avg_rtx_num == 0.0)
-        priv->avg_rtx_num = timer->num_rtx_retry;
-      else
-        priv->avg_rtx_num = (timer->num_rtx_retry + 7 * priv->avg_rtx_num) / 8;
-      /* calculate the delay between retransmission request and receiving this
-       * packet, start with when we scheduled this timeout last */
-      rtx_last = timer->rtx_last;
-      if (dts != GST_CLOCK_TIME_NONE && dts > rtx_last) {
-        /* we have a valid delay if this packet arrived after we scheduled the
-         * request */
-        delay = dts - rtx_last;
-        if (priv->avg_rtx_rtt == 0)
-          priv->avg_rtx_rtt = delay;
-        else
-          priv->avg_rtx_rtt = (delay + 7 * priv->avg_rtx_rtt) / 8;
-      } else
-        delay = 0;
-
-      GST_LOG_OBJECT (jitterbuffer,
-          "RTX success %" G_GUINT64_FORMAT ", failed %" G_GUINT64_FORMAT
-          ", requests %" G_GUINT64_FORMAT ", dups %" G_GUINT64_FORMAT
-          ", avg-num %g, delay %" GST_TIME_FORMAT ", avg-rtt %" GST_TIME_FORMAT,
-          priv->num_rtx_success, priv->num_rtx_failed, priv->num_rtx_requests,
-          priv->num_duplicates, priv->avg_rtx_num, GST_TIME_ARGS (delay),
-          GST_TIME_ARGS (priv->avg_rtx_rtt));
-
-      /* don't try to estimate the next seqnum because this is a retransmitted
-       * packet and it probably did not arrive with the expected packet
-       * spacing. */
-      do_next_seqnum = FALSE;
+      if (is_rtx) {
+        update_rtx_stats (jitterbuffer, timer, dts, TRUE);
+        /* don't try to estimate the next seqnum because this is a retransmitted
+         * packet and it probably did not arrive with the expected packet
+         * spacing. */
+        do_next_seqnum = FALSE;
+      }
+
+      if (!is_stats_timer && (!is_rtx || timer->num_rtx_retry > 1)) {
+        RtpTimer *stats_timer = rtp_timer_dup (timer);
+        /* Store timer in order to record stats when/if the retransmitted
+         * packet arrives. We should also store timer information if we've
+         * requested retransmission more than once since we may receive
+         * several retransmitted packets. For accuracy we should update the
+         * stats also when the redundant retransmitted packets arrives. */
+        stats_timer->timeout = pts + priv->rtx_stats_timeout * GST_MSECOND;
+        stats_timer->type = RTP_TIMER_EXPECTED;
+        rtp_timer_queue_insert (priv->rtx_stats_timers, stats_timer);
+      }
     }
   }
 
-  if (do_next_seqnum && dts != GST_CLOCK_TIME_NONE) {
+  if (do_next_seqnum && pts != GST_CLOCK_TIME_NONE) {
     GstClockTime expected, delay;
 
     /* calculate expected arrival time of the next seqnum */
-    expected = dts + priv->packet_spacing;
+    expected = pts + priv->packet_spacing;
 
     delay = get_rtx_delay (priv);
 
     /* and update/install timer for next seqnum */
-    if (timer) {
-      reschedule_timer (jitterbuffer, timer, priv->next_in_seqnum, expected,
-          delay, TRUE);
+    GST_DEBUG_OBJECT (jitterbuffer, "Add RTX timer #%d, expected %"
+        GST_TIME_FORMAT ", delay %" GST_TIME_FORMAT ", packet-spacing %"
+        GST_TIME_FORMAT ", jitter %" GST_TIME_FORMAT, priv->next_in_seqnum,
+        GST_TIME_ARGS (expected), GST_TIME_ARGS (delay),
+        GST_TIME_ARGS (priv->packet_spacing), GST_TIME_ARGS (priv->avg_jitter));
+
+    if (timer && !is_stats_timer) {
+      timer->type = RTP_TIMER_EXPECTED;
+      rtp_timer_queue_update_timer (priv->timers, timer, priv->next_in_seqnum,
+          expected, delay, 0, TRUE);
     } else {
-      add_timer (jitterbuffer, TIMER_TYPE_EXPECTED, priv->next_in_seqnum, 0,
+      rtp_timer_queue_set_expected (priv->timers, priv->next_in_seqnum,
           expected, delay, priv->packet_spacing);
     }
-  } else if (timer && timer->type != TIMER_TYPE_DEADLINE) {
+  } else if (timer && timer->type != RTP_TIMER_DEADLINE && !is_stats_timer) {
     /* if we had a timer, remove it, we don't know when to expect the next
      * packet. */
-    remove_timer (jitterbuffer, timer);
+    rtp_timer_queue_unschedule (priv->timers, timer);
+    rtp_timer_free (timer);
   }
 }
 
 static void
 calculate_packet_spacing (GstRtpJitterBuffer * jitterbuffer, guint32 rtptime,
-    GstClockTime dts)
+    GstClockTime pts)
 {
   GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
 
   /* we need consecutive seqnums with a different
    * rtptime to estimate the packet spacing. */
   if (priv->ips_rtptime != rtptime) {
-    /* rtptime changed, check dts diff */
-    if (priv->ips_dts != -1 && dts != -1 && dts > priv->ips_dts) {
-      GstClockTime new_packet_spacing = dts - priv->ips_dts;
+    /* rtptime changed, check pts diff */
+    if (priv->ips_pts != -1 && pts != -1 && pts > priv->ips_pts) {
+      GstClockTime new_packet_spacing = pts - priv->ips_pts;
       GstClockTime old_packet_spacing = priv->packet_spacing;
 
       /* Biased towards bigger packet spacings to prevent
@@ -2089,109 +2377,187 @@ calculate_packet_spacing (GstRtpJitterBuffer * jitterbuffer, guint32 rtptime,
           GST_TIME_ARGS (priv->packet_spacing));
     }
     priv->ips_rtptime = rtptime;
-    priv->ips_dts = dts;
+    priv->ips_pts = pts;
   }
 }
 
 static void
-calculate_expected (GstRtpJitterBuffer * jitterbuffer, guint32 expected,
-    guint16 seqnum, GstClockTime dts, gint gap)
+insert_lost_event (GstRtpJitterBuffer * jitterbuffer,
+    guint16 seqnum, guint lost_packets, GstClockTime timestamp,
+    GstClockTime duration, guint num_rtx_retry)
 {
   GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
-  GstClockTime total_duration, duration, expected_dts;
-  TimerType type;
-
-  GST_DEBUG_OBJECT (jitterbuffer,
-      "dts %" GST_TIME_FORMAT ", last %" GST_TIME_FORMAT,
-      GST_TIME_ARGS (dts), GST_TIME_ARGS (priv->last_in_dts));
+  GstEvent *event = NULL;
+  guint next_in_seqnum;
 
-  if (dts == GST_CLOCK_TIME_NONE) {
-    GST_WARNING_OBJECT (jitterbuffer, "Have no DTS");
-    return;
+  /* we had a gap and thus we lost some packets. Create an event for this.  */
+  if (lost_packets > 1)
+    GST_DEBUG_OBJECT (jitterbuffer, "Packets #%d -> #%d lost", seqnum,
+        seqnum + lost_packets - 1);
+  else
+    GST_DEBUG_OBJECT (jitterbuffer, "Packet #%d lost", seqnum);
+
+  priv->num_lost += lost_packets;
+  priv->num_rtx_failed += num_rtx_retry;
+
+  next_in_seqnum = (seqnum + lost_packets) & 0xffff;
+
+  /* we now only accept seqnum bigger than this */
+  if (gst_rtp_buffer_compare_seqnum (priv->next_in_seqnum, next_in_seqnum) > 0) {
+    priv->next_in_seqnum = next_in_seqnum;
+    priv->last_in_pts = timestamp;
   }
 
-  /* the total duration spanned by the missing packets */
-  if (dts >= priv->last_in_dts)
-    total_duration = dts - priv->last_in_dts;
-  else
-    total_duration = 0;
+  /* Avoid creating events if we don't need it. Note that we still need to create
+   * the lost *ITEM* since it will be used to notify the outgoing thread of
+   * lost items (so that we can set discont flags and such) */
+  if (priv->do_lost) {
+    /* create packet lost event */
+    if (duration == GST_CLOCK_TIME_NONE && priv->packet_spacing > 0)
+      duration = priv->packet_spacing;
+    event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
+        gst_structure_new ("GstRTPPacketLost",
+            "seqnum", G_TYPE_UINT, (guint) seqnum,
+            "timestamp", G_TYPE_UINT64, timestamp,
+            "duration", G_TYPE_UINT64, duration,
+            "retry", G_TYPE_UINT, num_rtx_retry, NULL));
+  }
+  if (rtp_jitter_buffer_append_lost_event (priv->jbuf,
+          event, seqnum, lost_packets))
+    JBUF_SIGNAL_EVENT (priv);
+}
+
+static void
+calculate_expected (GstRtpJitterBuffer * jitterbuffer, guint32 expected,
+    guint16 seqnum, GstClockTime pts, gint gap)
+{
+  GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
+  GstClockTime duration, expected_pts;
+  gboolean equidistant = priv->equidistant > 0;
+  GstClockTime last_in_pts = priv->last_in_pts;
+
+  GST_DEBUG_OBJECT (jitterbuffer,
+      "pts %" GST_TIME_FORMAT ", last %" GST_TIME_FORMAT,
+      GST_TIME_ARGS (pts), GST_TIME_ARGS (last_in_pts));
 
-  /* interpolate between the current time and the last time based on
-   * number of packets we are missing, this is the estimated duration
-   * for the missing packet based on equidistant packet spacing. */
-  duration = total_duration / (gap + 1);
+  if (pts == GST_CLOCK_TIME_NONE) {
+    GST_WARNING_OBJECT (jitterbuffer, "Have no PTS");
+    return;
+  }
 
-  GST_DEBUG_OBJECT (jitterbuffer, "duration %" GST_TIME_FORMAT,
-      GST_TIME_ARGS (duration));
+  if (equidistant) {
+    GstClockTime total_duration;
+    /* the total duration spanned by the missing packets */
+    if (pts >= last_in_pts)
+      total_duration = pts - last_in_pts;
+    else
+      total_duration = 0;
 
-  if (total_duration > priv->latency_ns) {
-    GstClockTime gap_time;
-    guint lost_packets;
+    /* interpolate between the current time and the last time based on
+     * number of packets we are missing, this is the estimated duration
+     * for the missing packet based on equidistant packet spacing. */
+    duration = total_duration / (gap + 1);
 
-    if (duration > 0) {
-      GstClockTime gap_dur = gap * duration;
-      if (gap_dur > priv->latency_ns)
-        gap_time = gap_dur - priv->latency_ns;
-      else
-        gap_time = 0;
-      lost_packets = gap_time / duration;
-    } else {
-      gap_time = total_duration - priv->latency_ns;
-      lost_packets = gap;
-    }
+    GST_DEBUG_OBJECT (jitterbuffer, "duration %" GST_TIME_FORMAT,
+        GST_TIME_ARGS (duration));
 
-    /* too many lost packets, some of the missing packets are already
-     * too late and we can generate lost packet events for them. */
-    GST_DEBUG_OBJECT (jitterbuffer,
-        "lost packets (%d, #%d->#%d) duration too large %" GST_TIME_FORMAT
-        " > %" GST_TIME_FORMAT ", consider %u lost (%" GST_TIME_FORMAT ")",
-        gap, expected, seqnum, GST_TIME_ARGS (total_duration),
-        GST_TIME_ARGS (priv->latency_ns), lost_packets,
-        GST_TIME_ARGS (gap_time));
-
-    /* this timer will fire immediately and the lost event will be pushed from
-     * the timer thread */
-    if (lost_packets > 0) {
-      add_timer (jitterbuffer, TIMER_TYPE_LOST, expected, lost_packets,
-          priv->last_in_dts + duration, 0, gap_time);
-      expected += lost_packets;
-      priv->last_in_dts += gap_time;
+    if (total_duration > priv->latency_ns) {
+      GstClockTime gap_time;
+      guint lost_packets;
+
+      if (duration > 0) {
+        GstClockTime gap_dur = gap * duration;
+        if (gap_dur > priv->latency_ns)
+          gap_time = gap_dur - priv->latency_ns;
+        else
+          gap_time = 0;
+        lost_packets = gap_time / duration;
+      } else {
+        gap_time = total_duration - priv->latency_ns;
+        lost_packets = gap;
+      }
+
+      /* too many lost packets, some of the missing packets are already
+       * too late and we can generate lost packet events for them. */
+      GST_INFO_OBJECT (jitterbuffer,
+          "lost packets (%d, #%d->#%d) duration too large %" GST_TIME_FORMAT
+          " > %" GST_TIME_FORMAT ", consider %u lost (%" GST_TIME_FORMAT ")",
+          gap, expected, seqnum - 1, GST_TIME_ARGS (total_duration),
+          GST_TIME_ARGS (priv->latency_ns), lost_packets,
+          GST_TIME_ARGS (gap_time));
+
+      /* this multi-lost-packet event will be inserted directly into the packet-queue
+         for immediate processing */
+      if (lost_packets > 0) {
+        RtpTimer *timer;
+        GstClockTime timestamp =
+            apply_offset (jitterbuffer, last_in_pts + duration);
+        insert_lost_event (jitterbuffer, expected, lost_packets, timestamp,
+            gap_time, 0);
+
+        timer = rtp_timer_queue_find (priv->timers, expected);
+        if (timer && timer->type == RTP_TIMER_EXPECTED) {
+          if (timer->queued)
+            rtp_timer_queue_unschedule (priv->timers, timer);
+          GST_DEBUG_OBJECT (jitterbuffer, "removing timer for seqnum #%u",
+              expected);
+          rtp_timer_free (timer);
+        }
+
+        expected += lost_packets;
+        last_in_pts += gap_time;
+      }
     }
-  }
 
-  expected_dts = priv->last_in_dts + duration;
+    expected_pts = last_in_pts + duration;
+  } else {
+    /* If we cannot assume equidistant packet spacing, the only thing we now
+     * for sure is that the missing packets have expected pts not later than
+     * the last received pts. */
+    duration = 0;
+    expected_pts = pts;
+  }
 
   if (priv->do_retransmission) {
-    TimerData *timer;
+    RtpTimer *timer = rtp_timer_queue_find (priv->timers, expected);
+    GstClockTime rtx_delay = get_rtx_delay (priv);
 
-    type = TIMER_TYPE_EXPECTED;
     /* if we had a timer for the first missing packet, update it. */
-    if ((timer = find_timer (jitterbuffer, type, expected))) {
+    if (timer && timer->type == RTP_TIMER_EXPECTED) {
       GstClockTime timeout = timer->timeout;
+      GstClockTime delay = MAX (rtx_delay, pts - expected_pts);
 
       timer->duration = duration;
-      if (timeout > (expected_dts + timer->rtx_retry)) {
-        GstClockTime delay = timeout - expected_dts - timer->rtx_retry;
-        reschedule_timer (jitterbuffer, timer, timer->seqnum, expected_dts,
-            delay, TRUE);
+      if (timeout > (expected_pts + delay) && timer->num_rtx_retry == 0) {
+        rtp_timer_queue_update_timer (priv->timers, timer, timer->seqnum,
+            expected_pts, delay, 0, TRUE);
       }
       expected++;
-      expected_dts += duration;
+      expected_pts += duration;
     }
-  } else {
-    type = TIMER_TYPE_LOST;
-  }
 
-  while (gst_rtp_buffer_compare_seqnum (expected, seqnum) > 0) {
-    add_timer (jitterbuffer, type, expected, 0, expected_dts, 0, duration);
-    expected_dts += duration;
-    expected++;
+    while (gst_rtp_buffer_compare_seqnum (expected, seqnum) > 0) {
+      /* minimum delay the expected-timer has "waited" is the elapsed time
+       * since expected arrival of the missing packet */
+      GstClockTime delay = MAX (rtx_delay, pts - expected_pts);
+      rtp_timer_queue_set_expected (priv->timers, expected, expected_pts,
+          delay, duration);
+      expected_pts += duration;
+      expected++;
+    }
+  } else {
+    while (gst_rtp_buffer_compare_seqnum (expected, seqnum) > 0) {
+      rtp_timer_queue_set_lost (priv->timers, expected, expected_pts,
+          duration, timeout_offset (jitterbuffer));
+      expected_pts += duration;
+      expected++;
+    }
   }
 }
 
 static void
 calculate_jitter (GstRtpJitterBuffer * jitterbuffer, GstClockTime dts,
-    guint rtptime)
+    guint32 rtptime)
 {
   gint32 rtpdiff;
   GstClockTimeDiff dtsdiff, rtpdiffns, diff;
@@ -2212,6 +2578,15 @@ calculate_jitter (GstRtpJitterBuffer * jitterbuffer, GstClockTime dts,
   else
     rtpdiff = 0;
 
+  /* Guess whether stream currently uses equidistant packet spacing. If we
+   * often see identical timestamps it means the packets are not
+   * equidistant. */
+  if (rtptime == priv->last_rtptime)
+    priv->equidistant -= 2;
+  else
+    priv->equidistant += 1;
+  priv->equidistant = CLAMP (priv->equidistant, -7, 7);
+
   priv->last_dts = dts;
   priv->last_rtptime = rtptime;
 
@@ -2228,10 +2603,10 @@ calculate_jitter (GstRtpJitterBuffer * jitterbuffer, GstClockTime dts,
   priv->avg_jitter = (diff + (15 * priv->avg_jitter)) >> 4;
 
   GST_LOG_OBJECT (jitterbuffer,
-      "dtsdiff %" GST_TIME_FORMAT " rtptime %" GST_TIME_FORMAT
-      ", clock-rate %d, diff %" GST_TIME_FORMAT ", jitter: %" GST_TIME_FORMAT,
-      GST_TIME_ARGS (dtsdiff), GST_TIME_ARGS (rtpdiffns), priv->clock_rate,
-      GST_TIME_ARGS (diff), GST_TIME_ARGS (priv->avg_jitter));
+      "dtsdiff %" GST_STIME_FORMAT " rtptime %" GST_STIME_FORMAT
+      ", clock-rate %d, diff %" GST_STIME_FORMAT ", jitter: %" GST_TIME_FORMAT,
+      GST_STIME_ARGS (dtsdiff), GST_STIME_ARGS (rtpdiffns), priv->clock_rate,
+      GST_STIME_ARGS (diff), GST_TIME_ARGS (priv->avg_jitter));
 
   return;
 
@@ -2263,12 +2638,13 @@ compare_buffer_seqnum (GstBuffer * a, GstBuffer * b, gpointer user_data)
 }
 
 static gboolean
-handle_big_gap_buffer (GstRtpJitterBuffer * jitterbuffer, gboolean future,
-    GstBuffer * buffer, guint8 pt, guint16 seqnum, gint gap)
+handle_big_gap_buffer (GstRtpJitterBuffer * jitterbuffer, GstBuffer * buffer,
+    guint8 pt, guint16 seqnum, gint gap, guint max_dropout, guint max_misorder)
 {
   GstRtpJitterBufferPrivate *priv;
   guint gap_packets_length;
   gboolean reset = FALSE;
+  gboolean future = gap > 0;
 
   priv = jitterbuffer->priv;
 
@@ -2306,7 +2682,7 @@ handle_big_gap_buffer (GstRtpJitterBuffer * jitterbuffer, gboolean future,
       GST_DEBUG_OBJECT (jitterbuffer,
           "buffer too %s %d < %d, got 5 consecutive ones - reset",
           (future ? "new" : "old"), gap,
-          (future ? RTP_MAX_DROPOUT : -RTP_MAX_MISORDER));
+          (future ? max_dropout : -max_misorder));
       reset = TRUE;
     } else if (!all_consecutive) {
       g_queue_foreach (&priv->gap_packets, (GFunc) gst_buffer_unref, NULL);
@@ -2314,20 +2690,19 @@ handle_big_gap_buffer (GstRtpJitterBuffer * jitterbuffer, gboolean future,
       GST_DEBUG_OBJECT (jitterbuffer,
           "buffer too %s %d < %d, got no 5 consecutive ones - dropping",
           (future ? "new" : "old"), gap,
-          (future ? RTP_MAX_DROPOUT : -RTP_MAX_MISORDER));
+          (future ? max_dropout : -max_misorder));
       buffer = NULL;
     } else {
       GST_DEBUG_OBJECT (jitterbuffer,
           "buffer too %s %d < %d, got %u consecutive ones - waiting",
           (future ? "new" : "old"), gap,
-          (future ? RTP_MAX_DROPOUT : -RTP_MAX_MISORDER),
-          gap_packets_length + 1);
+          (future ? max_dropout : -max_misorder), gap_packets_length + 1);
       buffer = NULL;
     }
   } else {
     GST_DEBUG_OBJECT (jitterbuffer,
         "buffer too %s %d < %d, first one - waiting", (future ? "new" : "old"),
-        gap, -RTP_MAX_MISORDER);
+        gap, -max_misorder);
     g_queue_push_tail (&priv->gap_packets, buffer);
     buffer = NULL;
   }
@@ -2358,6 +2733,106 @@ get_current_running_time (GstRtpJitterBuffer * jitterbuffer)
 }
 
 static GstFlowReturn
+gst_rtp_jitter_buffer_reset (GstRtpJitterBuffer * jitterbuffer,
+    GstPad * pad, GstObject * parent, guint16 seqnum)
+{
+  GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
+  GstFlowReturn ret = GST_FLOW_OK;
+  GList *events = NULL, *l;
+  GList *buffers;
+
+  GST_DEBUG_OBJECT (jitterbuffer, "flush and reset jitterbuffer");
+  rtp_jitter_buffer_flush (priv->jbuf,
+      (GFunc) free_item_and_retain_sticky_events, &events);
+  rtp_jitter_buffer_reset_skew (priv->jbuf);
+  rtp_timer_queue_remove_all (priv->timers);
+  priv->discont = TRUE;
+  priv->last_popped_seqnum = -1;
+
+  if (priv->gap_packets.head) {
+    GstBuffer *gap_buffer = priv->gap_packets.head->data;
+    GstRTPBuffer gap_rtp = GST_RTP_BUFFER_INIT;
+
+    gst_rtp_buffer_map (gap_buffer, GST_MAP_READ, &gap_rtp);
+    priv->next_seqnum = gst_rtp_buffer_get_seq (&gap_rtp);
+    gst_rtp_buffer_unmap (&gap_rtp);
+  } else {
+    priv->next_seqnum = seqnum;
+  }
+
+  priv->last_in_pts = -1;
+  priv->next_in_seqnum = -1;
+
+  /* Insert all sticky events again in order, otherwise we would
+   * potentially loose STREAM_START, CAPS or SEGMENT events
+   */
+  events = g_list_reverse (events);
+  for (l = events; l; l = l->next) {
+    rtp_jitter_buffer_append_event (priv->jbuf, l->data);
+  }
+  g_list_free (events);
+
+  JBUF_SIGNAL_EVENT (priv);
+
+  /* reset spacing estimation when gap */
+  priv->ips_rtptime = -1;
+  priv->ips_pts = GST_CLOCK_TIME_NONE;
+
+  buffers = g_list_copy (priv->gap_packets.head);
+  g_queue_clear (&priv->gap_packets);
+
+  priv->ips_rtptime = -1;
+  priv->ips_pts = GST_CLOCK_TIME_NONE;
+  JBUF_UNLOCK (jitterbuffer->priv);
+
+  for (l = buffers; l; l = l->next) {
+    ret = gst_rtp_jitter_buffer_chain (pad, parent, l->data);
+    l->data = NULL;
+    if (ret != GST_FLOW_OK) {
+      l = l->next;
+      break;
+    }
+  }
+  for (; l; l = l->next)
+    gst_buffer_unref (l->data);
+  g_list_free (buffers);
+
+  return ret;
+}
+
+static gboolean
+gst_rtp_jitter_buffer_fast_start (GstRtpJitterBuffer * jitterbuffer)
+{
+  GstRtpJitterBufferPrivate *priv;
+  RTPJitterBufferItem *item;
+  RtpTimer *timer;
+
+  priv = jitterbuffer->priv;
+
+  if (priv->faststart_min_packets == 0)
+    return FALSE;
+
+  item = rtp_jitter_buffer_peek (priv->jbuf);
+  if (!item)
+    return FALSE;
+
+  timer = rtp_timer_queue_find (priv->timers, item->seqnum);
+  if (!timer || timer->type != RTP_TIMER_DEADLINE)
+    return FALSE;
+
+  if (rtp_jitter_buffer_can_fast_start (priv->jbuf,
+          priv->faststart_min_packets)) {
+    GST_INFO_OBJECT (jitterbuffer, "We found %i consecutive packet, start now",
+        priv->faststart_min_packets);
+    timer->timeout = -1;
+    rtp_timer_queue_reschedule (priv->timers, timer);
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+static GstFlowReturn
 gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
     GstBuffer * buffer)
 {
@@ -2369,13 +2844,17 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
   GstClockTime dts, pts;
   guint64 latency_ts;
   gboolean head;
+  gboolean duplicate;
   gint percent = -1;
   guint8 pt;
   GstRTPBuffer rtp = GST_RTP_BUFFER_INIT;
   gboolean do_next_seqnum = FALSE;
-  RTPJitterBufferItem *item;
   GstMessage *msg = NULL;
+  GstMessage *drop_msg = NULL;
   gboolean estimated_dts = FALSE;
+  gint32 packet_rate, max_dropout, max_misorder;
+  RtpTimer *timer = NULL;
+  gboolean is_rtx;
 
   jitterbuffer = GST_RTP_JITTER_BUFFER_CAST (parent);
 
@@ -2389,6 +2868,8 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
   rtptime = gst_rtp_buffer_get_timestamp (&rtp);
   gst_rtp_buffer_unmap (&rtp);
 
+  is_rtx = GST_BUFFER_IS_RETRANSMISSION (buffer);
+
   /* make sure we have PTS and DTS set */
   pts = GST_BUFFER_PTS (buffer);
   dts = GST_BUFFER_DTS (buffer);
@@ -2419,8 +2900,8 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
   }
 
   GST_DEBUG_OBJECT (jitterbuffer,
-      "Received packet #%d at time %" GST_TIME_FORMAT ", discont %d", seqnum,
-      GST_TIME_ARGS (dts), GST_BUFFER_IS_DISCONT (buffer));
+      "Received packet #%d at time %" GST_TIME_FORMAT ", discont %d, rtx %d",
+      seqnum, GST_TIME_ARGS (dts), GST_BUFFER_IS_DISCONT (buffer), is_rtx);
 
   JBUF_LOCK_CHECK (priv, out_flushing);
 
@@ -2437,7 +2918,7 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
     /* Try to get the clock-rate from the caps first if we can. If there are no
      * caps we must fire the signal to get the clock-rate. */
     if ((caps = gst_pad_get_current_caps (pad))) {
-      gst_jitter_buffer_sink_parse_caps (jitterbuffer, caps);
+      gst_jitter_buffer_sink_parse_caps (jitterbuffer, caps, pt);
       gst_caps_unref (caps);
     }
   }
@@ -2450,13 +2931,16 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
 
     if (G_UNLIKELY (priv->clock_rate == -1))
       goto no_clock_rate;
+
+    gst_rtp_packet_rate_ctx_reset (&priv->packet_rate_ctx, priv->clock_rate);
   }
 
   /* don't accept more data on EOS */
   if (G_UNLIKELY (priv->eos))
     goto have_eos;
 
-  calculate_jitter (jitterbuffer, dts, rtptime);
+  if (!is_rtx)
+    calculate_jitter (jitterbuffer, dts, rtptime);
 
   if (priv->seqnum_base != -1) {
     gint gap;
@@ -2468,7 +2952,6 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
           "packet seqnum #%d before seqnum-base #%d", seqnum,
           priv->seqnum_base);
       gst_buffer_unref (buffer);
-      ret = GST_FLOW_OK;
       goto finished;
     } else if (gap > 16384) {
       /* From now on don't compare against the seqnum base anymore as
@@ -2480,148 +2963,164 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
 
   expected = priv->next_in_seqnum;
 
+  /* don't update packet-rate based on RTX, as those arrive highly unregularly */
+  if (!is_rtx) {
+    packet_rate = gst_rtp_packet_rate_ctx_update (&priv->packet_rate_ctx,
+        seqnum, rtptime);
+    GST_TRACE_OBJECT (jitterbuffer, "updated packet_rate: %d", packet_rate);
+  }
+  max_dropout =
+      gst_rtp_packet_rate_ctx_get_max_dropout (&priv->packet_rate_ctx,
+      priv->max_dropout_time);
+  max_misorder =
+      gst_rtp_packet_rate_ctx_get_max_misorder (&priv->packet_rate_ctx,
+      priv->max_misorder_time);
+  GST_TRACE_OBJECT (jitterbuffer, "max_dropout: %d, max_misorder: %d",
+      max_dropout, max_misorder);
+
+  timer = rtp_timer_queue_find (priv->timers, seqnum);
+  if (is_rtx) {
+    if (G_UNLIKELY (!priv->do_retransmission))
+      goto unsolicited_rtx;
+
+    if (!timer)
+      timer = rtp_timer_queue_find (priv->rtx_stats_timers, seqnum);
+
+    /* If the first buffer is an (old) rtx, e.g. from before a reset, or
+     * already lost, ignore it */
+    if (!timer || expected == -1)
+      goto unsolicited_rtx;
+  }
+
   /* now check against our expected seqnum */
-  if (G_LIKELY (expected != -1)) {
-    gint gap;
+  if (G_UNLIKELY (expected == -1)) {
+    GST_DEBUG_OBJECT (jitterbuffer, "First buffer #%d", seqnum);
+
+    /* calculate a pts based on rtptime and arrival time (dts) */
+    pts =
+        rtp_jitter_buffer_calculate_pts (priv->jbuf, dts, estimated_dts,
+        rtptime, gst_element_get_base_time (GST_ELEMENT_CAST (jitterbuffer)),
+        0, FALSE);
+
+    if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (pts))) {
+      /* A valid timestamp cannot be calculated, discard packet */
+      goto discard_invalid;
+    }
+
+    /* we don't know what the next_in_seqnum should be, wait for the last
+     * possible moment to push this buffer, maybe we get an earlier seqnum
+     * while we wait */
+    rtp_timer_queue_set_deadline (priv->timers, seqnum, pts,
+        timeout_offset (jitterbuffer));
+
+    do_next_seqnum = TRUE;
+    /* take rtptime and pts to calculate packet spacing */
+    priv->ips_rtptime = rtptime;
+    priv->ips_pts = pts;
 
+  } else {
+    gint gap;
     /* now calculate gap */
     gap = gst_rtp_buffer_compare_seqnum (expected, seqnum);
-
     GST_DEBUG_OBJECT (jitterbuffer, "expected #%d, got #%d, gap of %d",
         expected, seqnum, gap);
 
-    if (G_LIKELY (gap == 0)) {
-      /* packet is expected */
-      calculate_packet_spacing (jitterbuffer, rtptime, dts);
-      do_next_seqnum = TRUE;
-    } else {
-      gboolean reset = FALSE;
-
-      if (gap < 0) {
-        /* we received an old packet */
-        if (G_UNLIKELY (gap != -1 && gap < -RTP_MAX_MISORDER)) {
-          reset =
-              handle_big_gap_buffer (jitterbuffer, FALSE, buffer, pt, seqnum,
-              gap);
-          buffer = NULL;
-        } else {
-          GST_DEBUG_OBJECT (jitterbuffer, "old packet received");
-        }
-      } else {
-        /* new packet, we are missing some packets */
-        if (G_UNLIKELY (priv->timers->len >= RTP_MAX_DROPOUT)) {
-          /* If we have timers for more than RTP_MAX_DROPOUT packets
-           * pending this means that we have a huge gap overall. We can
-           * reset the jitterbuffer at this point because there's
-           * just too much data missing to be able to do anything
-           * sensible with the past data. Just try again from the
-           * next packet */
-          GST_WARNING_OBJECT (jitterbuffer,
-              "%d pending timers > %d - resetting", priv->timers->len,
-              RTP_MAX_DROPOUT);
-          reset = TRUE;
-          gst_buffer_unref (buffer);
-          buffer = NULL;
-        } else if (G_UNLIKELY (gap >= RTP_MAX_DROPOUT)) {
-          reset =
-              handle_big_gap_buffer (jitterbuffer, TRUE, buffer, pt, seqnum,
-              gap);
-          buffer = NULL;
-        } else {
-          GST_DEBUG_OBJECT (jitterbuffer, "%d missing packets", gap);
-          /* fill in the gap with EXPECTED timers */
-          calculate_expected (jitterbuffer, expected, seqnum, dts, gap);
+    if (G_UNLIKELY (gap > 0 &&
+            rtp_timer_queue_length (priv->timers) >= max_dropout)) {
+      /* If we have timers for more than RTP_MAX_DROPOUT packets
+       * pending this means that we have a huge gap overall. We can
+       * reset the jitterbuffer at this point because there's
+       * just too much data missing to be able to do anything
+       * sensible with the past data. Just try again from the
+       * next packet */
+      GST_WARNING_OBJECT (jitterbuffer, "%d pending timers > %d - resetting",
+          rtp_timer_queue_length (priv->timers), max_dropout);
+      g_queue_insert_sorted (&priv->gap_packets, buffer,
+          (GCompareDataFunc) compare_buffer_seqnum, NULL);
+      return gst_rtp_jitter_buffer_reset (jitterbuffer, pad, parent, seqnum);
+    }
 
-          do_next_seqnum = TRUE;
-        }
+    /* Special handling of large gaps */
+    if (!is_rtx && ((gap != -1 && gap < -max_misorder) || (gap >= max_dropout))) {
+      gboolean reset = handle_big_gap_buffer (jitterbuffer, buffer, pt, seqnum,
+          gap, max_dropout, max_misorder);
+      if (reset) {
+        return gst_rtp_jitter_buffer_reset (jitterbuffer, pad, parent, seqnum);
+      } else {
+        GST_DEBUG_OBJECT (jitterbuffer,
+            "Had big gap, waiting for more consecutive packets");
+        goto finished;
       }
-      if (G_UNLIKELY (reset)) {
-        GList *events = NULL, *l;
-        GList *buffers;
-
-        GST_DEBUG_OBJECT (jitterbuffer, "flush and reset jitterbuffer");
-        rtp_jitter_buffer_flush (priv->jbuf,
-            (GFunc) free_item_and_retain_events, &events);
-        rtp_jitter_buffer_reset_skew (priv->jbuf);
-        remove_all_timers (jitterbuffer);
-        priv->discont = TRUE;
-        priv->last_popped_seqnum = -1;
-        priv->next_seqnum = seqnum;
-
-        priv->last_in_dts = -1;
-        priv->next_in_seqnum = -1;
-
-        /* Insert all sticky events again in order, otherwise we would
-         * potentially loose STREAM_START, CAPS or SEGMENT events
-         */
-        events = g_list_reverse (events);
-        for (l = events; l; l = l->next) {
-          RTPJitterBufferItem *item;
-
-          item = alloc_item (l->data, ITEM_TYPE_EVENT, -1, -1, -1, 0, -1);
-          rtp_jitter_buffer_insert (priv->jbuf, item, &head, NULL);
-        }
-        g_list_free (events);
-
-        JBUF_SIGNAL_EVENT (priv);
+    }
 
-        /* reset spacing estimation when gap */
-        priv->ips_rtptime = -1;
-        priv->ips_dts = GST_CLOCK_TIME_NONE;
+    /* We had no huge gap, let's drop all the gap packets */
+    GST_DEBUG_OBJECT (jitterbuffer, "Clearing gap packets");
+    g_queue_foreach (&priv->gap_packets, (GFunc) gst_buffer_unref, NULL);
+    g_queue_clear (&priv->gap_packets);
 
-        buffers = g_list_copy (priv->gap_packets.head);
-        g_queue_clear (&priv->gap_packets);
+    /* calculate a pts based on rtptime and arrival time (dts) */
+    /* If we estimated the DTS, don't consider it in the clock skew calculations */
+    pts =
+        rtp_jitter_buffer_calculate_pts (priv->jbuf, dts, estimated_dts,
+        rtptime, gst_element_get_base_time (GST_ELEMENT_CAST (jitterbuffer)),
+        gap, is_rtx);
 
-        priv->ips_rtptime = -1;
-        priv->ips_dts = GST_CLOCK_TIME_NONE;
-        JBUF_UNLOCK (jitterbuffer->priv);
+    if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (pts))) {
+      /* A valid timestamp cannot be calculated, discard packet */
+      goto discard_invalid;
+    }
 
-        for (l = buffers; l; l = l->next) {
-          ret = gst_rtp_jitter_buffer_chain (pad, parent, l->data);
-          l->data = NULL;
-          if (ret != GST_FLOW_OK)
-            break;
-        }
-        for (; l; l = l->next)
-          gst_buffer_unref (l->data);
-        g_list_free (buffers);
+    if (G_LIKELY (gap == 0)) {
+      /* packet is expected */
+      calculate_packet_spacing (jitterbuffer, rtptime, pts);
+      do_next_seqnum = TRUE;
+    } else {
 
-        return ret;
+      /* we have a gap */
+      if (gap > 0) {
+        GST_DEBUG_OBJECT (jitterbuffer, "%d missing packets", gap);
+        /* fill in the gap with EXPECTED timers */
+        calculate_expected (jitterbuffer, expected, seqnum, pts, gap);
+        do_next_seqnum = TRUE;
+      } else {
+        GST_DEBUG_OBJECT (jitterbuffer, "old packet received");
+        do_next_seqnum = FALSE;
       }
+
       /* reset spacing estimation when gap */
       priv->ips_rtptime = -1;
-      priv->ips_dts = GST_CLOCK_TIME_NONE;
+      priv->ips_pts = GST_CLOCK_TIME_NONE;
     }
-  } else {
-    GST_DEBUG_OBJECT (jitterbuffer, "First buffer #%d", seqnum);
-
-    /* we don't know what the next_in_seqnum should be, wait for the last
-     * possible moment to push this buffer, maybe we get an earlier seqnum
-     * while we wait */
-    set_timer (jitterbuffer, TIMER_TYPE_DEADLINE, seqnum, dts);
-    do_next_seqnum = TRUE;
-    /* take rtptime and dts to calculate packet spacing */
-    priv->ips_rtptime = rtptime;
-    priv->ips_dts = dts;
-  }
-
-  /* We had no huge gap, let's drop all the gap packets */
-  if (buffer != NULL) {
-    GST_DEBUG_OBJECT (jitterbuffer, "Clearing gap packets");
-    g_queue_foreach (&priv->gap_packets, (GFunc) gst_buffer_unref, NULL);
-    g_queue_clear (&priv->gap_packets);
-  } else {
-    GST_DEBUG_OBJECT (jitterbuffer,
-        "Had big gap, waiting for more consecutive packets");
-    JBUF_UNLOCK (jitterbuffer->priv);
-    return GST_FLOW_OK;
   }
 
   if (do_next_seqnum) {
-    priv->last_in_dts = dts;
+    priv->last_in_pts = pts;
     priv->next_in_seqnum = (seqnum + 1) & 0xffff;
   }
 
+  if (is_rtx)
+    timer->num_rtx_received++;
+
+  /* At 2^15, we would detect a seqnum rollover too early, therefore
+   * limit the queue size. But let's not limit it to a number that is
+   * too small to avoid emptying it needlessly if there is a spurious huge
+   * sequence number, let's allow at least 10k packets in any case. */
+  while (rtp_jitter_buffer_is_full (priv->jbuf) &&
+      priv->srcresult == GST_FLOW_OK) {
+    RtpTimer *timer = rtp_timer_queue_peek_earliest (priv->timers);
+    while (timer) {
+      timer->timeout = -1;
+      if (timer->type == RTP_TIMER_DEADLINE)
+        break;
+      timer = rtp_timer_get_next (timer);
+    }
+
+    update_current_timer (jitterbuffer);
+    JBUF_WAIT_QUEUE (priv);
+    if (priv->srcresult != GST_FLOW_OK)
+      goto out_flushing;
+  }
+
   /* let's check if this buffer is too late, we can only accept packets with
    * bigger seqnum than the one we last pushed. */
   if (G_LIKELY (priv->last_popped_seqnum != -1)) {
@@ -2630,8 +3129,19 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
     gap = gst_rtp_buffer_compare_seqnum (priv->last_popped_seqnum, seqnum);
 
     /* priv->last_popped_seqnum >= seqnum, we're too late. */
-    if (G_UNLIKELY (gap <= 0))
+    if (G_UNLIKELY (gap <= 0)) {
+      if (priv->do_retransmission) {
+        if (is_rtx && timer) {
+          update_rtx_stats (jitterbuffer, timer, dts, FALSE);
+          /* Only count the retranmitted packet too late if it has been
+           * considered lost. If the original packet arrived before the
+           * retransmitted we just count it as a duplicate. */
+          if (timer->type != RTP_TIMER_LOST)
+            goto rtx_duplicate;
+        }
+      }
       goto too_late;
+    }
   }
 
   /* let's drop oldest packet if the queue is already full and drop-on-latency
@@ -2651,8 +3161,13 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
         old_item = rtp_jitter_buffer_pop (priv->jbuf, &percent);
         GST_DEBUG_OBJECT (jitterbuffer, "Queue full, dropping old packet %p",
             old_item);
-        priv->next_seqnum = (old_item->seqnum + 1) & 0xffff;
-        free_item (old_item);
+        priv->next_seqnum = (old_item->seqnum + old_item->count) & 0xffff;
+        if (priv->post_drop_messages) {
+          drop_msg =
+              new_drop_message (jitterbuffer, old_item->seqnum, old_item->pts,
+              REASON_DROP_ON_LATENCY);
+        }
+        rtp_jitter_buffer_free_item (old_item);
       }
       /* we might have removed some head buffers, signal the pushing thread to
        * see if it can push now */
@@ -2664,22 +3179,25 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
    * later. The code above always sets dts to pts or the other way around if
    * any of those is valid in the buffer, so we know that if we estimated the
    * dts that both are unknown */
-  if (estimated_dts)
-    item =
-        alloc_item (buffer, ITEM_TYPE_BUFFER, GST_CLOCK_TIME_NONE,
-        GST_CLOCK_TIME_NONE, seqnum, 1, rtptime);
-  else
-    item = alloc_item (buffer, ITEM_TYPE_BUFFER, dts, pts, seqnum, 1, rtptime);
+  head = rtp_jitter_buffer_append_buffer (priv->jbuf, buffer,
+      estimated_dts ? GST_CLOCK_TIME_NONE : dts, pts, seqnum, rtptime,
+      &duplicate, &percent);
 
   /* now insert the packet into the queue in sorted order. This function returns
    * FALSE if a packet with the same seqnum was already in the queue, meaning we
    * have a duplicate. */
-  if (G_UNLIKELY (!rtp_jitter_buffer_insert (priv->jbuf, item,
-              &head, &percent)))
+  if (G_UNLIKELY (duplicate)) {
+    if (is_rtx && timer)
+      update_rtx_stats (jitterbuffer, timer, dts, FALSE);
     goto duplicate;
+  }
+
+  /* Trigger fast start if needed */
+  if (gst_rtp_jitter_buffer_fast_start (jitterbuffer))
+    head = TRUE;
 
   /* update timers */
-  update_timers (jitterbuffer, seqnum, dts, do_next_seqnum);
+  update_timers (jitterbuffer, seqnum, dts, pts, do_next_seqnum, is_rtx, timer);
 
   /* we had an unhandled SR, handle it now */
   if (priv->last_sr)
@@ -2689,13 +3207,6 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
     /* signal addition of new buffer when the _loop is waiting. */
     if (G_LIKELY (priv->active))
       JBUF_SIGNAL_EVENT (priv);
-
-    /* let's unschedule and unblock any waiting buffers. We only want to do this
-     * when the head buffer changed */
-    if (G_UNLIKELY (priv->clock_id)) {
-      GST_DEBUG_OBJECT (jitterbuffer, "Unscheduling waiting new buffer");
-      unschedule_current_timer (jitterbuffer);
-    }
   }
 
   GST_DEBUG_OBJECT (jitterbuffer,
@@ -2705,10 +3216,13 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
   msg = check_buffering_percent (jitterbuffer, percent);
 
 finished:
+  update_current_timer (jitterbuffer);
   JBUF_UNLOCK (priv);
 
   if (msg)
     gst_element_post_message (GST_ELEMENT_CAST (jitterbuffer), msg);
+  if (drop_msg)
+    gst_element_post_message (GST_ELEMENT_CAST (jitterbuffer), drop_msg);
 
   return ret;
 
@@ -2744,20 +3258,70 @@ have_eos:
   }
 too_late:
   {
-    GST_WARNING_OBJECT (jitterbuffer, "Packet #%d too late as #%d was already"
+    GST_DEBUG_OBJECT (jitterbuffer, "Packet #%d too late as #%d was already"
         " popped, dropping", seqnum, priv->last_popped_seqnum);
     priv->num_late++;
+    if (priv->post_drop_messages) {
+      drop_msg = new_drop_message (jitterbuffer, seqnum, pts, REASON_TOO_LATE);
+    }
     gst_buffer_unref (buffer);
     goto finished;
   }
 duplicate:
   {
-    GST_WARNING_OBJECT (jitterbuffer, "Duplicate packet #%d detected, dropping",
+    GST_DEBUG_OBJECT (jitterbuffer, "Duplicate packet #%d detected, dropping",
         seqnum);
     priv->num_duplicates++;
-    free_item (item);
     goto finished;
   }
+rtx_duplicate:
+  {
+    GST_DEBUG_OBJECT (jitterbuffer,
+        "Duplicate RTX packet #%d detected, dropping", seqnum);
+    priv->num_duplicates++;
+    gst_buffer_unref (buffer);
+    goto finished;
+  }
+unsolicited_rtx:
+  {
+    GST_DEBUG_OBJECT (jitterbuffer,
+        "Unsolicited RTX packet #%d detected, dropping", seqnum);
+    gst_buffer_unref (buffer);
+    goto finished;
+  }
+discard_invalid:
+  {
+    GST_DEBUG_OBJECT (jitterbuffer,
+        "cannot calculate a valid pts for #%d (rtx: %d), discard",
+        seqnum, is_rtx);
+    gst_buffer_unref (buffer);
+    goto finished;
+  }
+}
+
+/* FIXME: hopefully we can do something more efficient here, especially when
+ * all packets are in order and/or outside of the currently cached range.
+ * Still worthwhile to have it, avoids taking/releasing object lock and pad
+ * stream lock for every single buffer in the default chain_list fallback. */
+static GstFlowReturn
+gst_rtp_jitter_buffer_chain_list (GstPad * pad, GstObject * parent,
+    GstBufferList * buffer_list)
+{
+  GstFlowReturn flow_ret = GST_FLOW_OK;
+  guint i, n;
+
+  n = gst_buffer_list_length (buffer_list);
+  for (i = 0; i < n; ++i) {
+    GstBuffer *buf = gst_buffer_list_get (buffer_list, i);
+
+    flow_ret = gst_rtp_jitter_buffer_chain (pad, parent, gst_buffer_ref (buf));
+
+    if (flow_ret != GST_FLOW_OK)
+      break;
+  }
+  gst_buffer_list_unref (buffer_list);
+
+  return flow_ret;
 }
 
 static GstClockTime
@@ -2831,7 +3395,7 @@ update_estimated_eos (GstRtpJitterBuffer * jitterbuffer,
   }
 
   /* this is the current time as running-time */
-  out_time = item->dts;
+  out_time = item->pts;
 
   if (elapsed > 0)
     estimated = gst_util_uint64_scale (out_time, total, elapsed);
@@ -2847,7 +3411,8 @@ update_estimated_eos (GstRtpJitterBuffer * jitterbuffer,
       GST_TIME_FORMAT, GST_TIME_ARGS (elapsed), GST_TIME_ARGS (estimated));
 
   if (estimated != -1 && priv->estimated_eos != estimated) {
-    set_timer (jitterbuffer, TIMER_TYPE_EOS, -1, estimated);
+    rtp_timer_queue_set_eos (priv->timers, estimated,
+        timeout_offset (jitterbuffer));
     priv->estimated_eos = estimated;
   }
 }
@@ -2891,9 +3456,16 @@ pop_and_push_next (GstRtpJitterBuffer * jitterbuffer, guint seqnum)
       }
 
       dts =
-          gst_segment_to_position (&priv->segment, GST_FORMAT_TIME, item->dts);
+          gst_segment_position_from_running_time (&priv->segment,
+          GST_FORMAT_TIME, item->dts);
       pts =
-          gst_segment_to_position (&priv->segment, GST_FORMAT_TIME, item->pts);
+          gst_segment_position_from_running_time (&priv->segment,
+          GST_FORMAT_TIME, item->pts);
+
+      /* if this is a new frame, check if ts_offset needs to be updated */
+      if (pts != priv->last_pts) {
+        update_offset (jitterbuffer);
+      }
 
       /* apply timestamp with offset to buffer now */
       GST_BUFFER_DTS (outbuf) = apply_offset (jitterbuffer, dts);
@@ -2902,6 +3474,7 @@ pop_and_push_next (GstRtpJitterBuffer * jitterbuffer, guint seqnum)
       /* update the elapsed time when we need to check against the npt stop time. */
       update_estimated_eos (jitterbuffer, item);
 
+      priv->last_pts = pts;
       priv->last_out_time = GST_BUFFER_PTS (outbuf);
       break;
     case ITEM_TYPE_LOST:
@@ -2924,10 +3497,21 @@ pop_and_push_next (GstRtpJitterBuffer * jitterbuffer, guint seqnum)
     priv->next_seqnum = (seqnum + item->count) & 0xffff;
   }
   msg = check_buffering_percent (jitterbuffer, percent);
+
+  if (type == ITEM_TYPE_EVENT && outevent &&
+      GST_EVENT_TYPE (outevent) == GST_EVENT_EOS) {
+    g_assert (priv->eos);
+    while (rtp_timer_queue_length (priv->timers) > 0) {
+      /* Stopping timers */
+      unschedule_current_timer (jitterbuffer);
+      JBUF_WAIT_TIMER (priv);
+    }
+  }
+
   JBUF_UNLOCK (priv);
 
   item->data = NULL;
-  free_item (item);
+  rtp_jitter_buffer_free_item (item);
 
   if (msg)
     gst_element_post_message (GST_ELEMENT_CAST (jitterbuffer), msg);
@@ -2939,6 +3523,8 @@ pop_and_push_next (GstRtpJitterBuffer * jitterbuffer, guint seqnum)
           "Pushing buffer %d, dts %" GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT,
           seqnum, GST_TIME_ARGS (GST_BUFFER_DTS (outbuf)),
           GST_TIME_ARGS (GST_BUFFER_PTS (outbuf)));
+      priv->num_pushed++;
+      GST_BUFFER_DTS (outbuf) = GST_CLOCK_TIME_NONE;
       result = gst_pad_push (priv->srcpad, outbuf);
 
       JBUF_LOCK_CHECK (priv, out_flushing);
@@ -2947,7 +3533,7 @@ pop_and_push_next (GstRtpJitterBuffer * jitterbuffer, guint seqnum)
     case ITEM_TYPE_EVENT:
       /* We got not enough consecutive packets with a huge gap, we can
        * as well just drop them here now on EOS */
-      if (GST_EVENT_TYPE (outevent) == GST_EVENT_EOS) {
+      if (outevent && GST_EVENT_TYPE (outevent) == GST_EVENT_EOS) {
         GST_DEBUG_OBJECT (jitterbuffer, "Clearing gap packets on EOS");
         g_queue_foreach (&priv->gap_packets, (GFunc) gst_buffer_unref, NULL);
         g_queue_clear (&priv->gap_packets);
@@ -2958,7 +3544,7 @@ pop_and_push_next (GstRtpJitterBuffer * jitterbuffer, guint seqnum)
 
       if (do_push)
         gst_pad_push_event (priv->srcpad, outevent);
-      else
+      else if (outevent)
         gst_event_unref (outevent);
 
       result = GST_FLOW_OK;
@@ -3045,7 +3631,7 @@ handle_next_buffer (GstRtpJitterBuffer * jitterbuffer)
       GST_DEBUG_OBJECT (jitterbuffer, "Old packet #%d, next #%d dropping",
           seqnum, next_seqnum);
       item = rtp_jitter_buffer_pop (priv->jbuf, NULL);
-      free_item (item);
+      rtp_jitter_buffer_free_item (item);
       result = GST_FLOW_OK;
     } else {
       /* the chain function has scheduled timers to request retransmission or
@@ -3053,7 +3639,14 @@ handle_next_buffer (GstRtpJitterBuffer * jitterbuffer)
       GST_DEBUG_OBJECT (jitterbuffer,
           "Sequence number GAP detected: expected %d instead of %d (%d missing)",
           next_seqnum, seqnum, gap);
-      result = GST_FLOW_WAIT;
+      /* if we have reached EOS, just keep processing */
+      /* Also do the same if we block input because the JB is full */
+      if (priv->eos || rtp_jitter_buffer_is_full (priv->jbuf)) {
+        result = pop_and_push_next (jitterbuffer, seqnum);
+        result = GST_FLOW_OK;
+      } else {
+        result = GST_FLOW_WAIT;
+      }
     }
   }
 
@@ -3118,18 +3711,92 @@ get_rtx_retry_period (GstRtpJitterBufferPrivate * priv,
   return rtx_retry_period;
 }
 
+/*
+  1. For *larger* rtx-rtt, weigh a new measurement as before (1/8th)
+  2. For *smaller* rtx-rtt, be a bit more conservative and weigh a bit less (1/16th)
+  3. For very large measurements (> avg * 2), consider them "outliers"
+     and count them a lot less (1/48th)
+*/
+static void
+update_avg_rtx_rtt (GstRtpJitterBufferPrivate * priv, GstClockTime rtt)
+{
+  gint weight;
+
+  if (priv->avg_rtx_rtt == 0) {
+    priv->avg_rtx_rtt = rtt;
+    return;
+  }
+
+  if (rtt > 2 * priv->avg_rtx_rtt)
+    weight = 48;
+  else if (rtt > priv->avg_rtx_rtt)
+    weight = 8;
+  else
+    weight = 16;
+
+  priv->avg_rtx_rtt = (rtt + (weight - 1) * priv->avg_rtx_rtt) / weight;
+}
+
+static void
+update_rtx_stats (GstRtpJitterBuffer * jitterbuffer, const RtpTimer * timer,
+    GstClockTime dts, gboolean success)
+{
+  GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
+  GstClockTime delay;
+
+  if (success) {
+    /* we scheduled a retry for this packet and now we have it */
+    priv->num_rtx_success++;
+    /* all the previous retry attempts failed */
+    priv->num_rtx_failed += timer->num_rtx_retry - 1;
+  } else {
+    /* All retries failed or was too late */
+    priv->num_rtx_failed += timer->num_rtx_retry;
+  }
+
+  /* number of retries before (hopefully) receiving the packet */
+  if (priv->avg_rtx_num == 0.0)
+    priv->avg_rtx_num = timer->num_rtx_retry;
+  else
+    priv->avg_rtx_num = (timer->num_rtx_retry + 7 * priv->avg_rtx_num) / 8;
+
+  /* Calculate the delay between retransmission request and receiving this
+   * packet. We have a valid delay if and only if this packet is a response to
+   * our last request. If not we don't know if this is a response to an
+   * earlier request and delay could be way off. For RTT is more important
+   * with correct values than to update for every packet. */
+  if (timer->num_rtx_retry == timer->num_rtx_received &&
+      dts != GST_CLOCK_TIME_NONE && dts > timer->rtx_last) {
+    delay = dts - timer->rtx_last;
+    update_avg_rtx_rtt (priv, delay);
+  } else {
+    delay = 0;
+  }
+
+  GST_LOG_OBJECT (jitterbuffer,
+      "RTX #%d, result %d, success %" G_GUINT64_FORMAT ", failed %"
+      G_GUINT64_FORMAT ", requests %" G_GUINT64_FORMAT ", dups %"
+      G_GUINT64_FORMAT ", avg-num %g, delay %" GST_TIME_FORMAT ", avg-rtt %"
+      GST_TIME_FORMAT, timer->seqnum, success, priv->num_rtx_success,
+      priv->num_rtx_failed, priv->num_rtx_requests, priv->num_duplicates,
+      priv->avg_rtx_num, GST_TIME_ARGS (delay),
+      GST_TIME_ARGS (priv->avg_rtx_rtt));
+}
+
 /* the timeout for when we expected a packet expired */
 static gboolean
-do_expected_timeout (GstRtpJitterBuffer * jitterbuffer, TimerData * timer,
-    GstClockTime now)
+do_expected_timeout (GstRtpJitterBuffer * jitterbuffer, RtpTimer * timer,
+    GstClockTime now, GQueue * events)
 {
   GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
   GstEvent *event;
   guint delay, delay_ms, avg_rtx_rtt_ms;
   guint rtx_retry_timeout_ms, rtx_retry_period_ms;
+  guint rtx_deadline_ms;
   GstClockTime rtx_retry_period;
   GstClockTime rtx_retry_timeout;
   GstClock *clock;
+  GstClockTimeDiff offset = 0;
 
   GST_DEBUG_OBJECT (jitterbuffer, "expected %d didn't arrive, now %"
       GST_TIME_FORMAT, timer->seqnum, GST_TIME_ARGS (now));
@@ -3137,16 +3804,14 @@ do_expected_timeout (GstRtpJitterBuffer * jitterbuffer, TimerData * timer,
   rtx_retry_timeout = get_rtx_retry_timeout (priv);
   rtx_retry_period = get_rtx_retry_period (priv, rtx_retry_timeout);
 
-  GST_DEBUG_OBJECT (jitterbuffer, "timeout %" GST_TIME_FORMAT ", period %"
-      GST_TIME_FORMAT, GST_TIME_ARGS (rtx_retry_timeout),
-      GST_TIME_ARGS (rtx_retry_period));
-
   delay = timer->rtx_delay + timer->rtx_retry;
 
   delay_ms = GST_TIME_AS_MSECONDS (delay);
   rtx_retry_timeout_ms = GST_TIME_AS_MSECONDS (rtx_retry_timeout);
   rtx_retry_period_ms = GST_TIME_AS_MSECONDS (rtx_retry_period);
   avg_rtx_rtt_ms = GST_TIME_AS_MSECONDS (priv->avg_rtx_rtt);
+  rtx_deadline_ms =
+      priv->rtx_deadline_ms != -1 ? priv->rtx_deadline_ms : priv->latency_ms;
 
   event = gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM,
       gst_structure_new ("GstRTPRetransmissionRequest",
@@ -3156,9 +3821,11 @@ do_expected_timeout (GstRtpJitterBuffer * jitterbuffer, TimerData * timer,
           "retry", G_TYPE_UINT, timer->num_rtx_retry,
           "frequency", G_TYPE_UINT, rtx_retry_timeout_ms,
           "period", G_TYPE_UINT, rtx_retry_period_ms,
-          "deadline", G_TYPE_UINT, priv->latency_ms,
+          "deadline", G_TYPE_UINT, rtx_deadline_ms,
           "packet-spacing", G_TYPE_UINT64, priv->packet_spacing,
           "avg-rtt", G_TYPE_UINT, avg_rtx_rtt_ms, NULL));
+  g_queue_push_tail (events, event);
+  GST_DEBUG_OBJECT (jitterbuffer, "Request RTX: %" GST_PTR_FORMAT, event);
 
   priv->num_rtx_requests++;
   timer->num_rtx_retry++;
@@ -3174,96 +3841,70 @@ do_expected_timeout (GstRtpJitterBuffer * jitterbuffer, TimerData * timer,
 
   /* calculate the timeout for the next retransmission attempt */
   timer->rtx_retry += rtx_retry_timeout;
-  GST_DEBUG_OBJECT (jitterbuffer, "base %" GST_TIME_FORMAT ", delay %"
+  GST_DEBUG_OBJECT (jitterbuffer, "timer #%i base %" GST_TIME_FORMAT ", delay %"
       GST_TIME_FORMAT ", retry %" GST_TIME_FORMAT ", num_retry %u",
-      GST_TIME_ARGS (timer->rtx_base), GST_TIME_ARGS (timer->rtx_delay),
-      GST_TIME_ARGS (timer->rtx_retry), timer->num_rtx_retry);
+      timer->seqnum, GST_TIME_ARGS (timer->rtx_base),
+      GST_TIME_ARGS (timer->rtx_delay), GST_TIME_ARGS (timer->rtx_retry),
+      timer->num_rtx_retry);
   if ((priv->rtx_max_retries != -1
           && timer->num_rtx_retry >= priv->rtx_max_retries)
-      || (timer->rtx_retry + timer->rtx_delay > rtx_retry_period)) {
-    GST_DEBUG_OBJECT (jitterbuffer, "reschedule as LOST timer");
+      || (timer->rtx_retry + timer->rtx_delay > rtx_retry_period)
+      || (timer->rtx_base + rtx_retry_period < now)) {
+    GST_DEBUG_OBJECT (jitterbuffer, "reschedule #%i as LOST timer",
+        timer->seqnum);
     /* too many retransmission request, we now convert the timer
      * to a lost timer, leave the num_rtx_retry as it is for stats */
-    timer->type = TIMER_TYPE_LOST;
+    timer->type = RTP_TIMER_LOST;
     timer->rtx_delay = 0;
     timer->rtx_retry = 0;
+    offset = timeout_offset (jitterbuffer);
   }
-  reschedule_timer (jitterbuffer, timer, timer->seqnum,
-      timer->rtx_base + timer->rtx_retry, timer->rtx_delay, FALSE);
-
-  JBUF_UNLOCK (priv);
-  gst_pad_push_event (priv->sinkpad, event);
-  JBUF_LOCK (priv);
+  rtp_timer_queue_update_timer (priv->timers, timer, timer->seqnum,
+      timer->rtx_base + timer->rtx_retry, timer->rtx_delay, offset, FALSE);
 
   return FALSE;
 }
 
 /* a packet is lost */
 static gboolean
-do_lost_timeout (GstRtpJitterBuffer * jitterbuffer, TimerData * timer,
+do_lost_timeout (GstRtpJitterBuffer * jitterbuffer, RtpTimer * timer,
     GstClockTime now)
 {
   GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
-  GstClockTime duration, timestamp;
-  guint seqnum, lost_packets, num_rtx_retry, next_in_seqnum;
-  gboolean head;
-  GstEvent *event;
-  RTPJitterBufferItem *item;
-
-  seqnum = timer->seqnum;
-  timestamp = apply_offset (jitterbuffer, timer->timeout);
-  duration = timer->duration;
-  if (duration == GST_CLOCK_TIME_NONE && priv->packet_spacing > 0)
-    duration = priv->packet_spacing;
-  lost_packets = MAX (timer->num, 1);
-  num_rtx_retry = timer->num_rtx_retry;
-
-  /* we had a gap and thus we lost some packets. Create an event for this.  */
-  if (lost_packets > 1)
-    GST_DEBUG_OBJECT (jitterbuffer, "Packets #%d -> #%d lost", seqnum,
-        seqnum + lost_packets - 1);
-  else
-    GST_DEBUG_OBJECT (jitterbuffer, "Packet #%d lost", seqnum);
-
-  priv->num_late += lost_packets;
-  priv->num_rtx_failed += num_rtx_retry;
-
-  next_in_seqnum = (seqnum + lost_packets) & 0xffff;
-
-  /* we now only accept seqnum bigger than this */
-  if (gst_rtp_buffer_compare_seqnum (priv->next_in_seqnum, next_in_seqnum) > 0)
-    priv->next_in_seqnum = next_in_seqnum;
+  GstClockTime timestamp;
 
-  /* create paket lost event */
-  event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
-      gst_structure_new ("GstRTPPacketLost",
-          "seqnum", G_TYPE_UINT, (guint) seqnum,
-          "timestamp", G_TYPE_UINT64, timestamp,
-          "duration", G_TYPE_UINT64, duration,
-          "retry", G_TYPE_UINT, num_rtx_retry, NULL));
+  timestamp = apply_offset (jitterbuffer, get_pts_timeout (timer));
+  insert_lost_event (jitterbuffer, timer->seqnum, 1, timestamp,
+      timer->duration, timer->num_rtx_retry);
 
-  item = alloc_item (event, ITEM_TYPE_LOST, -1, -1, seqnum, lost_packets, -1);
-  rtp_jitter_buffer_insert (priv->jbuf, item, &head, NULL);
-
-  /* remove timer now */
-  remove_timer (jitterbuffer, timer);
-  if (head)
-    JBUF_SIGNAL_EVENT (priv);
+  if (GST_CLOCK_TIME_IS_VALID (timer->rtx_last)) {
+    /* Store info to update stats if the packet arrives too late */
+    timer->timeout = now + priv->rtx_stats_timeout * GST_MSECOND;
+    timer->type = RTP_TIMER_LOST;
+    rtp_timer_queue_insert (priv->rtx_stats_timers, timer);
+  } else {
+    rtp_timer_free (timer);
+  }
 
   return TRUE;
 }
 
 static gboolean
-do_eos_timeout (GstRtpJitterBuffer * jitterbuffer, TimerData * timer,
+do_eos_timeout (GstRtpJitterBuffer * jitterbuffer, RtpTimer * timer,
     GstClockTime now)
 {
   GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
 
   GST_INFO_OBJECT (jitterbuffer, "got the NPT timeout");
-  remove_timer (jitterbuffer, timer);
+  rtp_timer_free (timer);
   if (!priv->eos) {
+    GstEvent *event;
+
     /* there was no EOS in the buffer, put one in there now */
-    queue_event (jitterbuffer, gst_event_new_eos ());
+    event = gst_event_new_eos ();
+    if (priv->segment_seqnum != GST_SEQNUM_INVALID)
+      gst_event_set_seqnum (event, priv->segment_seqnum);
+    queue_event (jitterbuffer, event);
   }
   JBUF_SIGNAL_EVENT (priv);
 
@@ -3271,7 +3912,7 @@ do_eos_timeout (GstRtpJitterBuffer * jitterbuffer, TimerData * timer,
 }
 
 static gboolean
-do_deadline_timeout (GstRtpJitterBuffer * jitterbuffer, TimerData * timer,
+do_deadline_timeout (GstRtpJitterBuffer * jitterbuffer, RtpTimer * timer,
     GstClockTime now)
 {
   GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
@@ -3282,35 +3923,64 @@ do_deadline_timeout (GstRtpJitterBuffer * jitterbuffer, TimerData * timer,
    * only mess with current ongoing seqnum if still unknown */
   if (priv->next_seqnum == -1)
     priv->next_seqnum = timer->seqnum;
-  remove_timer (jitterbuffer, timer);
+  rtp_timer_free (timer);
   JBUF_SIGNAL_EVENT (priv);
 
   return TRUE;
 }
 
 static gboolean
-do_timeout (GstRtpJitterBuffer * jitterbuffer, TimerData * timer,
-    GstClockTime now)
+do_timeout (GstRtpJitterBuffer * jitterbuffer, RtpTimer * timer,
+    GstClockTime now, GQueue * events)
 {
   gboolean removed = FALSE;
 
   switch (timer->type) {
-    case TIMER_TYPE_EXPECTED:
-      removed = do_expected_timeout (jitterbuffer, timer, now);
+    case RTP_TIMER_EXPECTED:
+      removed = do_expected_timeout (jitterbuffer, timer, now, events);
       break;
-    case TIMER_TYPE_LOST:
+    case RTP_TIMER_LOST:
       removed = do_lost_timeout (jitterbuffer, timer, now);
       break;
-    case TIMER_TYPE_DEADLINE:
+    case RTP_TIMER_DEADLINE:
       removed = do_deadline_timeout (jitterbuffer, timer, now);
       break;
-    case TIMER_TYPE_EOS:
+    case RTP_TIMER_EOS:
       removed = do_eos_timeout (jitterbuffer, timer, now);
       break;
   }
   return removed;
 }
 
+static void
+push_rtx_events_unlocked (GstRtpJitterBuffer * jitterbuffer, GQueue * events)
+{
+  GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
+  GstEvent *event;
+
+  while ((event = (GstEvent *) g_queue_pop_head (events)))
+    gst_pad_push_event (priv->sinkpad, event);
+}
+
+/* called with JBUF lock
+ *
+ * Pushes all events in @events queue.
+ *
+ * Returns: %TRUE if the timer thread is not longer running
+ */
+static void
+push_rtx_events (GstRtpJitterBuffer * jitterbuffer, GQueue * events)
+{
+  GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
+
+  if (events->length == 0)
+    return;
+
+  JBUF_UNLOCK (priv);
+  push_rtx_events_unlocked (jitterbuffer, events);
+  JBUF_LOCK (priv);
+}
+
 /* called when we need to wait for the next timeout.
  *
  * We loop over the array of recorded timeouts and wait for the earliest one.
@@ -3326,17 +3996,26 @@ wait_next_timeout (GstRtpJitterBuffer * jitterbuffer)
 
   JBUF_LOCK (priv);
   while (priv->timer_running) {
-    TimerData *timer = NULL;
-    GstClockTime timer_timeout = -1;
-    gint i, len;
-
-    /* If we have a clock, update "now" now with the very latest running time
-     * we have. It is used below when timeouts are triggered to calculate
-     * any next possible timeout. If we only update it after waiting for the
-     * clock, we would give a too old time to the timeout functions.
+    RtpTimer *timer = NULL;
+    GQueue events = G_QUEUE_INIT;
+
+    /* don't produce data in paused */
+    while (priv->blocked) {
+      JBUF_WAIT_TIMER (priv);
+      if (!priv->timer_running)
+        goto stopping;
+    }
+
+    /* If we have a clock, update "now" now with the very
+     * latest running time we have. If timers are unscheduled below we
+     * otherwise wouldn't update now (it's only updated when timers
+     * expire), and also for the very first loop iteration now would
+     * otherwise always be 0
      */
     GST_OBJECT_LOCK (jitterbuffer);
-    if (GST_ELEMENT_CLOCK (jitterbuffer)) {
+    if (priv->eos) {
+      now = GST_CLOCK_TIME_NONE;
+    } else if (GST_ELEMENT_CLOCK (jitterbuffer)) {
       now =
           gst_clock_get_time (GST_ELEMENT_CLOCK (jitterbuffer)) -
           GST_ELEMENT_CAST (jitterbuffer)->base_time;
@@ -3346,57 +4025,25 @@ wait_next_timeout (GstRtpJitterBuffer * jitterbuffer)
     GST_DEBUG_OBJECT (jitterbuffer, "now %" GST_TIME_FORMAT,
         GST_TIME_ARGS (now));
 
-    len = priv->timers->len;
-    for (i = 0; i < len; i++) {
-      TimerData *test = &g_array_index (priv->timers, TimerData, i);
-      GstClockTime test_timeout = get_timeout (jitterbuffer, test);
-      gboolean save_best = FALSE;
-
-      GST_DEBUG_OBJECT (jitterbuffer, "%d, %d, %d, %" GST_TIME_FORMAT,
-          i, test->type, test->seqnum, GST_TIME_ARGS (test_timeout));
-
-      /* find the smallest timeout */
-      if (timer == NULL) {
-        save_best = TRUE;
-      } else if (timer_timeout == -1) {
-        /* we already have an immediate timeout, the new timer must be an
-         * immediate timer with smaller seqnum to become the best */
-        if (test_timeout == -1
-            && (gst_rtp_buffer_compare_seqnum (test->seqnum,
-                    timer->seqnum) > 0))
-          save_best = TRUE;
-      } else if (test_timeout == -1) {
-        /* first immediate timer */
-        save_best = TRUE;
-      } else if (test_timeout < timer_timeout) {
-        /* earlier timer */
-        save_best = TRUE;
-      } else if (test_timeout == timer_timeout
-          && (gst_rtp_buffer_compare_seqnum (test->seqnum,
-                  timer->seqnum) > 0)) {
-        /* same timer, smaller seqnum */
-        save_best = TRUE;
-      }
-      if (save_best) {
-        GST_DEBUG_OBJECT (jitterbuffer, "new best %d", i);
-        timer = test;
-        timer_timeout = test_timeout;
-      }
-    }
-    if (timer && !priv->blocked) {
+    /* Clear expired rtx-stats timers */
+    if (priv->do_retransmission)
+      rtp_timer_queue_remove_until (priv->rtx_stats_timers, now);
+
+    /* Iterate expired "normal" timers */
+    while ((timer = rtp_timer_queue_pop_until (priv->timers, now)))
+      do_timeout (jitterbuffer, timer, now, &events);
+
+    timer = rtp_timer_queue_peek_earliest (priv->timers);
+    if (timer) {
       GstClock *clock;
       GstClockTime sync_time;
       GstClockID id;
       GstClockReturn ret;
       GstClockTimeDiff clock_jitter;
 
-      if (timer_timeout == -1 || timer_timeout <= now) {
-        do_timeout (jitterbuffer, timer, now);
-        /* check here, do_timeout could have released the lock */
-        if (!priv->timer_running)
-          break;
-        continue;
-      }
+      /* we poped all immediate and due timer, so this should just never
+       * happens */
+      g_assert (GST_CLOCK_TIME_IS_VALID (timer->timeout));
 
       GST_OBJECT_LOCK (jitterbuffer);
       clock = GST_ELEMENT_CLOCK (jitterbuffer);
@@ -3404,51 +4051,68 @@ wait_next_timeout (GstRtpJitterBuffer * jitterbuffer)
         GST_OBJECT_UNLOCK (jitterbuffer);
         /* let's just push if there is no clock */
         GST_DEBUG_OBJECT (jitterbuffer, "No clock, timeout right away");
-        now = timer_timeout;
+        now = timer->timeout;
+        push_rtx_events (jitterbuffer, &events);
         continue;
       }
 
       /* prepare for sync against clock */
-      sync_time = timer_timeout + GST_ELEMENT_CAST (jitterbuffer)->base_time;
+      sync_time = timer->timeout + GST_ELEMENT_CAST (jitterbuffer)->base_time;
       /* add latency of peer to get input time */
       sync_time += priv->peer_latency;
 
-      GST_DEBUG_OBJECT (jitterbuffer, "sync to timestamp %" GST_TIME_FORMAT
-          " with sync time %" GST_TIME_FORMAT,
-          GST_TIME_ARGS (timer_timeout), GST_TIME_ARGS (sync_time));
+      GST_DEBUG_OBJECT (jitterbuffer, "timer #%i sync to timestamp %"
+          GST_TIME_FORMAT " with sync time %" GST_TIME_FORMAT, timer->seqnum,
+          GST_TIME_ARGS (get_pts_timeout (timer)), GST_TIME_ARGS (sync_time));
 
       /* create an entry for the clock */
       id = priv->clock_id = gst_clock_new_single_shot_id (clock, sync_time);
-      priv->timer_timeout = timer_timeout;
+      priv->timer_timeout = timer->timeout;
       priv->timer_seqnum = timer->seqnum;
       GST_OBJECT_UNLOCK (jitterbuffer);
 
       /* release the lock so that the other end can push stuff or unlock */
       JBUF_UNLOCK (priv);
 
+      push_rtx_events_unlocked (jitterbuffer, &events);
+
       ret = gst_clock_id_wait (id, &clock_jitter);
 
       JBUF_LOCK (priv);
+
       if (!priv->timer_running) {
+        g_queue_clear_full (&events, (GDestroyNotify) gst_event_unref);
         gst_clock_id_unref (id);
         priv->clock_id = NULL;
         break;
       }
 
       if (ret != GST_CLOCK_UNSCHEDULED) {
-        GST_DEBUG_OBJECT (jitterbuffer, "sync done, %d, #%d, %" G_GINT64_FORMAT,
-            ret, priv->timer_seqnum, clock_jitter);
+        now = priv->timer_timeout + MAX (clock_jitter, 0);
+        GST_DEBUG_OBJECT (jitterbuffer,
+            "sync done, %d, #%d, %" GST_STIME_FORMAT, ret, priv->timer_seqnum,
+            GST_STIME_ARGS (clock_jitter));
       } else {
         GST_DEBUG_OBJECT (jitterbuffer, "sync unscheduled");
       }
+
       /* and free the entry */
       gst_clock_id_unref (id);
       priv->clock_id = NULL;
     } else {
+      push_rtx_events_unlocked (jitterbuffer, &events);
+
+      /* when draining the timers, the pusher thread will reuse our
+       * condition to wait for completion. Signal that thread before
+       * sleeping again here */
+      if (priv->eos)
+        JBUF_SIGNAL_TIMER (priv);
+
       /* no timers, wait for activity */
       JBUF_WAIT_TIMER (priv);
     }
   }
+stopping:
   JBUF_UNLOCK (priv);
 
   GST_DEBUG_OBJECT (jitterbuffer, "we are stopping");
@@ -3456,7 +4120,7 @@ wait_next_timeout (GstRtpJitterBuffer * jitterbuffer)
 }
 
 /*
- * This funcion implements the main pushing loop on the source pad.
+ * This function implements the main pushing loop on the source pad.
  *
  * It first tries to push as many buffers as possible. If there is a seqnum
  * mismatch, we wait for the next timeouts.
@@ -3474,6 +4138,7 @@ gst_rtp_jitter_buffer_loop (GstRtpJitterBuffer * jitterbuffer)
     result = handle_next_buffer (jitterbuffer);
     if (G_LIKELY (result == GST_FLOW_WAIT)) {
       /* now wait for the next event */
+      JBUF_SIGNAL_QUEUE (priv);
       JBUF_WAIT_EVENT (priv, flushing);
       result = GST_FLOW_OK;
     }
@@ -3501,13 +4166,15 @@ pause:
     gst_pad_pause_task (priv->srcpad);
     if (result == GST_FLOW_EOS) {
       event = gst_event_new_eos ();
+      if (priv->segment_seqnum != GST_SEQNUM_INVALID)
+        gst_event_set_seqnum (event, priv->segment_seqnum);
       gst_pad_push_event (priv->srcpad, event);
     }
     return;
   }
 }
 
-/* collect the info from the lastest RTCP packet and the jitterbuffer sync, do
+/* collect the info from the latest RTCP packet and the jitterbuffer sync, do
  * some sanity checks and then emit the handle-sync signal with the parameters.
  * This function must be called with the LOCK */
 static void
@@ -3552,7 +4219,10 @@ do_handle_sync (GstRtpJitterBuffer * jitterbuffer)
         /* check how far ahead it is to our RTP timestamps */
         diff = ext_rtptime - last_rtptime;
         /* if bigger than 1 second, we drop it */
-        if (diff > clock_rate) {
+        if (jitterbuffer->priv->max_rtcp_rtp_time_diff != -1 &&
+            diff >
+            gst_util_uint64_scale (jitterbuffer->priv->max_rtcp_rtp_time_diff,
+                clock_rate, 1000)) {
           GST_DEBUG_OBJECT (jitterbuffer, "too far ahead");
           /* should drop this, but some RTSP servers end up with bogus
            * way too ahead RTCP packet when repeated PAUSE/PLAY,
@@ -3698,16 +4368,11 @@ gst_rtp_jitter_buffer_sink_query (GstPad * pad, GstObject * parent,
     }
     default:
       if (GST_QUERY_IS_SERIALIZED (query)) {
-        RTPJitterBufferItem *item;
-        gboolean head;
-
         JBUF_LOCK_CHECK (priv, out_flushing);
         if (rtp_jitter_buffer_get_mode (priv->jbuf) !=
             RTP_JITTER_BUFFER_MODE_BUFFER) {
           GST_DEBUG_OBJECT (jitterbuffer, "adding serialized query");
-          item = alloc_item (query, ITEM_TYPE_QUERY, -1, -1, -1, 0, -1);
-          rtp_jitter_buffer_insert (priv->jbuf, item, &head, NULL);
-          if (head)
+          if (rtp_jitter_buffer_append_query (priv->jbuf, query))
             JBUF_SIGNAL_EVENT (priv);
           JBUF_WAIT_QUERY (priv, out_flushing);
           res = priv->last_query;
@@ -3870,15 +4535,42 @@ gst_rtp_jitter_buffer_set_property (GObject * object,
       break;
     case PROP_TS_OFFSET:
       JBUF_LOCK (priv);
-      priv->ts_offset = g_value_get_int64 (value);
+      if (priv->max_ts_offset_adjustment != 0) {
+        gint64 new_offset = g_value_get_int64 (value);
+
+        if (new_offset > priv->ts_offset) {
+          priv->ts_offset_remainder = new_offset - priv->ts_offset;
+        } else {
+          priv->ts_offset_remainder = -(priv->ts_offset - new_offset);
+        }
+      } else {
+        priv->ts_offset = g_value_get_int64 (value);
+        priv->ts_offset_remainder = 0;
+        update_timer_offsets (jitterbuffer);
+      }
       priv->ts_discont = TRUE;
       JBUF_UNLOCK (priv);
       break;
+    case PROP_MAX_TS_OFFSET_ADJUSTMENT:
+      JBUF_LOCK (priv);
+      priv->max_ts_offset_adjustment = g_value_get_uint64 (value);
+      JBUF_UNLOCK (priv);
+      break;
     case PROP_DO_LOST:
       JBUF_LOCK (priv);
       priv->do_lost = g_value_get_boolean (value);
       JBUF_UNLOCK (priv);
       break;
+    case PROP_POST_DROP_MESSAGES:
+      JBUF_LOCK (priv);
+      priv->post_drop_messages = g_value_get_boolean (value);
+      JBUF_UNLOCK (priv);
+      break;
+    case PROP_DROP_MESSAGES_INTERVAL:
+      JBUF_LOCK (priv);
+      priv->drop_messages_interval_ms = g_value_get_uint (value);
+      JBUF_UNLOCK (priv);
+      break;
     case PROP_MODE:
       JBUF_LOCK (priv);
       rtp_jitter_buffer_set_mode (priv->jbuf, g_value_get_enum (value));
@@ -3929,6 +4621,42 @@ gst_rtp_jitter_buffer_set_property (GObject * object,
       priv->rtx_max_retries = g_value_get_int (value);
       JBUF_UNLOCK (priv);
       break;
+    case PROP_RTX_DEADLINE:
+      JBUF_LOCK (priv);
+      priv->rtx_deadline_ms = g_value_get_int (value);
+      JBUF_UNLOCK (priv);
+      break;
+    case PROP_RTX_STATS_TIMEOUT:
+      JBUF_LOCK (priv);
+      priv->rtx_stats_timeout = g_value_get_uint (value);
+      JBUF_UNLOCK (priv);
+      break;
+    case PROP_MAX_RTCP_RTP_TIME_DIFF:
+      JBUF_LOCK (priv);
+      priv->max_rtcp_rtp_time_diff = g_value_get_int (value);
+      JBUF_UNLOCK (priv);
+      break;
+    case PROP_MAX_DROPOUT_TIME:
+      JBUF_LOCK (priv);
+      priv->max_dropout_time = g_value_get_uint (value);
+      JBUF_UNLOCK (priv);
+      break;
+    case PROP_MAX_MISORDER_TIME:
+      JBUF_LOCK (priv);
+      priv->max_misorder_time = g_value_get_uint (value);
+      JBUF_UNLOCK (priv);
+      break;
+    case PROP_RFC7273_SYNC:
+      JBUF_LOCK (priv);
+      rtp_jitter_buffer_set_rfc7273_sync (priv->jbuf,
+          g_value_get_boolean (value));
+      JBUF_UNLOCK (priv);
+      break;
+    case PROP_FASTSTART_MIN_PACKETS:
+      JBUF_LOCK (priv);
+      priv->faststart_min_packets = g_value_get_uint (value);
+      JBUF_UNLOCK (priv);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -3961,11 +4689,26 @@ gst_rtp_jitter_buffer_get_property (GObject * object,
       g_value_set_int64 (value, priv->ts_offset);
       JBUF_UNLOCK (priv);
       break;
+    case PROP_MAX_TS_OFFSET_ADJUSTMENT:
+      JBUF_LOCK (priv);
+      g_value_set_uint64 (value, priv->max_ts_offset_adjustment);
+      JBUF_UNLOCK (priv);
+      break;
     case PROP_DO_LOST:
       JBUF_LOCK (priv);
       g_value_set_boolean (value, priv->do_lost);
       JBUF_UNLOCK (priv);
       break;
+    case PROP_POST_DROP_MESSAGES:
+      JBUF_LOCK (priv);
+      g_value_set_boolean (value, priv->post_drop_messages);
+      JBUF_UNLOCK (priv);
+      break;
+    case PROP_DROP_MESSAGES_INTERVAL:
+      JBUF_LOCK (priv);
+      g_value_set_uint (value, priv->drop_messages_interval_ms);
+      JBUF_UNLOCK (priv);
+      break;
     case PROP_MODE:
       JBUF_LOCK (priv);
       g_value_set_enum (value, rtp_jitter_buffer_get_mode (priv->jbuf));
@@ -4030,10 +4773,46 @@ gst_rtp_jitter_buffer_get_property (GObject * object,
       g_value_set_int (value, priv->rtx_max_retries);
       JBUF_UNLOCK (priv);
       break;
+    case PROP_RTX_DEADLINE:
+      JBUF_LOCK (priv);
+      g_value_set_int (value, priv->rtx_deadline_ms);
+      JBUF_UNLOCK (priv);
+      break;
+    case PROP_RTX_STATS_TIMEOUT:
+      JBUF_LOCK (priv);
+      g_value_set_uint (value, priv->rtx_stats_timeout);
+      JBUF_UNLOCK (priv);
+      break;
     case PROP_STATS:
       g_value_take_boxed (value,
           gst_rtp_jitter_buffer_create_stats (jitterbuffer));
       break;
+    case PROP_MAX_RTCP_RTP_TIME_DIFF:
+      JBUF_LOCK (priv);
+      g_value_set_int (value, priv->max_rtcp_rtp_time_diff);
+      JBUF_UNLOCK (priv);
+      break;
+    case PROP_MAX_DROPOUT_TIME:
+      JBUF_LOCK (priv);
+      g_value_set_uint (value, priv->max_dropout_time);
+      JBUF_UNLOCK (priv);
+      break;
+    case PROP_MAX_MISORDER_TIME:
+      JBUF_LOCK (priv);
+      g_value_set_uint (value, priv->max_misorder_time);
+      JBUF_UNLOCK (priv);
+      break;
+    case PROP_RFC7273_SYNC:
+      JBUF_LOCK (priv);
+      g_value_set_boolean (value,
+          rtp_jitter_buffer_get_rfc7273_sync (priv->jbuf));
+      JBUF_UNLOCK (priv);
+      break;
+    case PROP_FASTSTART_MIN_PACKETS:
+      JBUF_LOCK (priv);
+      g_value_set_uint (value, priv->faststart_min_packets);
+      JBUF_UNLOCK (priv);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -4043,15 +4822,21 @@ gst_rtp_jitter_buffer_get_property (GObject * object,
 static GstStructure *
 gst_rtp_jitter_buffer_create_stats (GstRtpJitterBuffer * jbuf)
 {
+  GstRtpJitterBufferPrivate *priv = jbuf->priv;
   GstStructure *s;
 
-  JBUF_LOCK (jbuf->priv);
+  JBUF_LOCK (priv);
   s = gst_structure_new ("application/x-rtp-jitterbuffer-stats",
-      "rtx-count", G_TYPE_UINT64, jbuf->priv->num_rtx_requests,
-      "rtx-success-count", G_TYPE_UINT64, jbuf->priv->num_rtx_success,
-      "rtx-per-packet", G_TYPE_DOUBLE, jbuf->priv->avg_rtx_num,
-      "rtx-rtt", G_TYPE_UINT64, jbuf->priv->avg_rtx_rtt, NULL);
-  JBUF_UNLOCK (jbuf->priv);
+      "num-pushed", G_TYPE_UINT64, priv->num_pushed,
+      "num-lost", G_TYPE_UINT64, priv->num_lost,
+      "num-late", G_TYPE_UINT64, priv->num_late,
+      "num-duplicates", G_TYPE_UINT64, priv->num_duplicates,
+      "avg-jitter", G_TYPE_UINT64, priv->avg_jitter,
+      "rtx-count", G_TYPE_UINT64, priv->num_rtx_requests,
+      "rtx-success-count", G_TYPE_UINT64, priv->num_rtx_success,
+      "rtx-per-packet", G_TYPE_DOUBLE, priv->avg_rtx_num,
+      "rtx-rtt", G_TYPE_UINT64, priv->avg_rtx_rtt, NULL);
+  JBUF_UNLOCK (priv);
 
   return s;
 }