rtpsession: Call on-new-ssrc earlier
[platform/upstream/gst-plugins-good.git] / gst / rtpmanager / gstrtpjitterbuffer.c
index 7add9d5..1bd3668 100644 (file)
@@ -47,7 +47,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.
  *
@@ -69,7 +69,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
@@ -131,6 +131,7 @@ 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_MODE                RTP_JITTER_BUFFER_MODE_SLAVE
 #define DEFAULT_PERCENT             0
@@ -149,6 +150,7 @@ enum
 #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)
@@ -159,6 +161,7 @@ enum
   PROP_LATENCY,
   PROP_DROP_ON_LATENCY,
   PROP_TS_OFFSET,
+  PROP_MAX_TS_OFFSET_ADJUSTMENT,
   PROP_DO_LOST,
   PROP_MODE,
   PROP_PERCENT,
@@ -177,7 +180,8 @@ enum
   PROP_MAX_RTCP_RTP_TIME_DIFF,
   PROP_MAX_DROPOUT_TIME,
   PROP_MAX_MISORDER_TIME,
-  PROP_RFC7273_SYNC
+  PROP_RFC7273_SYNC,
+  PROP_FASTSTART_MIN_PACKETS
 };
 
 #define JBUF_LOCK(priv)   G_STMT_START {                       \
@@ -196,16 +200,30 @@ enum
     (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
@@ -259,6 +277,8 @@ struct _GstRtpJitterBufferPrivate
 
   RTPJitterBuffer *jbuf;
   GMutex jbuf_lock;
+  gboolean waiting_queue;
+  GCond jbuf_queue;
   gboolean waiting_timer;
   GCond jbuf_timer;
   gboolean waiting_event;
@@ -270,6 +290,7 @@ struct _GstRtpJitterBufferPrivate
   gboolean ts_discont;
   gboolean active;
   guint64 out_offset;
+  guint32 segment_seqnum;
 
   gboolean timer_running;
   GThread *timer_thread;
@@ -279,6 +300,7 @@ struct _GstRtpJitterBufferPrivate
   guint64 latency_ns;
   gboolean drop_on_latency;
   gint64 ts_offset;
+  guint64 max_ts_offset_adjustment;
   gboolean do_lost;
   gboolean do_retransmission;
   gboolean rtx_next_seqnum;
@@ -294,6 +316,7 @@ struct _GstRtpJitterBufferPrivate
   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;
@@ -304,7 +327,7 @@ 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;
@@ -312,7 +335,7 @@ struct _GstRtpJitterBufferPrivate
   GQueue gap_packets;
 
   /* the next expected seqnum we receive */
-  GstClockTime last_in_dts;
+  GstClockTime last_in_pts;
   guint32 next_in_seqnum;
 
   GArray *timers;
@@ -334,7 +357,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;
@@ -365,6 +388,7 @@ struct _GstRtpJitterBufferPrivate
 
   /* for the jitter */
   GstClockTime last_dts;
+  GstClockTime last_pts;
   guint64 last_rtptime;
   GstClockTime avg_jitter;
 };
@@ -393,10 +417,6 @@ typedef struct
   guint num_rtx_received;
 } TimerData;
 
-#define GST_RTP_JITTER_BUFFER_GET_PRIVATE(o) \
-  (G_TYPE_INSTANCE_GET_PRIVATE ((o), GST_TYPE_RTP_JITTER_BUFFER, \
-                                GstRtpJitterBufferPrivate))
-
 static GstStaticPadTemplate gst_rtp_jitter_buffer_sink_template =
 GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_SINK,
@@ -429,7 +449,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,
@@ -459,6 +480,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);
@@ -507,8 +530,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;
@@ -547,6 +568,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
@@ -650,7 +685,7 @@ gst_rtp_jitter_buffer_class_init (GstRtpJitterBufferClass * klass)
   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 "
-          "(0 disable, -1 automatic)",
+          "(0 disable)",
           -1, G_MAXINT, DEFAULT_RTX_DELAY_REORDER,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
   /**
@@ -852,6 +887,23 @@ gst_rtp_jitter_buffer_class_init (GstRtpJitterBufferClass * klass)
           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
@@ -947,6 +999,7 @@ 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);
 }
 
 static void
@@ -954,12 +1007,14 @@ 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->do_retransmission = DEFAULT_DO_RETRANSMISSION;
   priv->rtx_next_seqnum = DEFAULT_RTX_NEXT_SEQNUM;
@@ -975,14 +1030,19 @@ gst_rtp_jitter_buffer_init (GstRtpJitterBuffer * jitterbuffer)
   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->segment_seqnum = GST_SEQNUM_INVALID;
   priv->timers = g_array_new (FALSE, TRUE, sizeof (TimerData));
   priv->rtx_stats_timers = 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);
@@ -1012,6 +1072,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,
@@ -1086,6 +1148,7 @@ gst_rtp_jitter_buffer_finalize (GObject * object)
   g_array_free (priv->timers, TRUE);
   timer_queue_free (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);
@@ -1298,7 +1361,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;
@@ -1369,7 +1432,7 @@ gst_jitter_buffer_sink_parse_caps (GstRtpJitterBuffer * jitterbuffer,
   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)", payload, pt);
+        "Got caps with wrong payload type (got %d, expected %d)", pt, payload);
     return FALSE;
   }
 
@@ -1534,6 +1597,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);
 }
 
@@ -1550,11 +1614,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;
@@ -1566,8 +1630,9 @@ 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;
   GST_DEBUG_OBJECT (jitterbuffer, "flush and reset jitterbuffer");
   rtp_jitter_buffer_flush (priv->jbuf, (GFunc) free_item, NULL);
   rtp_jitter_buffer_disable_buffering (priv->jbuf, FALSE);
@@ -1638,6 +1703,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);
@@ -1676,9 +1742,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;
@@ -1759,6 +1827,8 @@ queue_event (GstRtpJitterBuffer * jitterbuffer, GstEvent * event)
       GstSegment segment;
       gst_event_copy_segment (event, &segment);
 
+      priv->segment_seqnum = gst_event_get_seqnum (event);
+
       /* we need time for now */
       if (segment.format != GST_FORMAT_TIME) {
         GST_DEBUG_OBJECT (jitterbuffer, "ignoring non-TIME newsegment");
@@ -1766,6 +1836,7 @@ queue_event (GstRtpJitterBuffer * jitterbuffer, GstEvent * event)
 
         gst_segment_init (&segment, GST_FORMAT_TIME);
         event = gst_event_new_segment (&segment);
+        gst_event_set_seqnum (event, priv->segment_seqnum);
       }
 
       priv->segment = segment;
@@ -1782,8 +1853,8 @@ queue_event (GstRtpJitterBuffer * jitterbuffer, GstEvent * event)
 
   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, -1);
-  if (head)
+  rtp_jitter_buffer_insert (priv->jbuf, item, &head, NULL);
+  if (head || priv->eos)
     JBUF_SIGNAL_EVENT (priv);
 
   return TRUE;
@@ -1969,6 +2040,32 @@ check_buffering_percent (GstRtpJitterBuffer * jitterbuffer, gint percent)
   return message;
 }
 
+static void
+update_offset (GstRtpJitterBuffer * jitterbuffer)
+{
+  GstRtpJitterBufferPrivate *priv;
+
+  priv = jitterbuffer->priv;
+
+  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;
+    }
+  }
+}
+
 static GstClockTime
 apply_offset (GstRtpJitterBuffer * jitterbuffer, GstClockTime timestamp)
 {
@@ -2234,6 +2331,8 @@ remove_timer (GstRtpJitterBuffer * jitterbuffer, TimerData * timer)
   GST_DEBUG_OBJECT (jitterbuffer, "removed index %d", idx);
   g_array_remove_index_fast (priv->timers, idx);
   timer->idx = idx;
+
+  JBUF_SIGNAL_TIMER (priv);
 }
 
 static void
@@ -2243,6 +2342,7 @@ remove_all_timers (GstRtpJitterBuffer * jitterbuffer)
   GST_DEBUG_OBJECT (jitterbuffer, "removed all timers");
   g_array_set_size (priv->timers, 0);
   unschedule_current_timer (jitterbuffer);
+  JBUF_SIGNAL_TIMER (priv);
 }
 
 /* get the extra delay to wait before sending RTX */
@@ -2304,8 +2404,8 @@ already_lost (GstRtpJitterBuffer * jitterbuffer, guint16 seqnum)
  */
 static void
 update_timers (GstRtpJitterBuffer * jitterbuffer, guint16 seqnum,
-    GstClockTime dts, gboolean do_next_seqnum, gboolean is_rtx,
-    TimerData * timer)
+    GstClockTime dts, GstClockTime pts, gboolean do_next_seqnum,
+    gboolean is_rtx, TimerData * timer)
 {
   GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
 
@@ -2351,16 +2451,16 @@ update_timers (GstRtpJitterBuffer * jitterbuffer, guint16 seqnum,
          * several retransmitted packets. For accuracy we should update the
          * stats also when the redundant retransmitted packets arrives. */
         timer_queue_append (priv->rtx_stats_timers, timer,
-            dts + priv->rtx_stats_timeout * GST_MSECOND, FALSE);
+            pts + priv->rtx_stats_timeout * GST_MSECOND, FALSE);
       }
     }
   }
 
-  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);
 
@@ -2372,6 +2472,7 @@ update_timers (GstRtpJitterBuffer * jitterbuffer, guint16 seqnum,
         GST_TIME_ARGS (priv->packet_spacing), GST_TIME_ARGS (priv->avg_jitter));
 
     if (timer) {
+      timer->type = TIMER_TYPE_EXPECTED;
       reschedule_timer (jitterbuffer, timer, priv->next_in_seqnum, expected,
           delay, TRUE);
     } else {
@@ -2387,16 +2488,16 @@ update_timers (GstRtpJitterBuffer * jitterbuffer, guint16 seqnum,
 
 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
@@ -2421,33 +2522,33 @@ 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)
+    guint16 seqnum, GstClockTime pts, gint gap)
 {
   GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
-  GstClockTime duration, expected_dts, delay;
+  GstClockTime duration, expected_pts, delay;
   TimerType type;
   gboolean equidistant = priv->equidistant > 0;
 
   GST_DEBUG_OBJECT (jitterbuffer,
-      "dts %" GST_TIME_FORMAT ", last %" GST_TIME_FORMAT,
-      GST_TIME_ARGS (dts), GST_TIME_ARGS (priv->last_in_dts));
+      "pts %" GST_TIME_FORMAT ", last %" GST_TIME_FORMAT,
+      GST_TIME_ARGS (pts), GST_TIME_ARGS (priv->last_in_pts));
 
-  if (dts == GST_CLOCK_TIME_NONE) {
-    GST_WARNING_OBJECT (jitterbuffer, "Have no DTS");
+  if (pts == GST_CLOCK_TIME_NONE) {
+    GST_WARNING_OBJECT (jitterbuffer, "Have no PTS");
     return;
   }
 
   if (equidistant) {
     GstClockTime total_duration;
     /* the total duration spanned by the missing packets */
-    if (dts >= priv->last_in_dts)
-      total_duration = dts - priv->last_in_dts;
+    if (pts >= priv->last_in_pts)
+      total_duration = pts - priv->last_in_pts;
     else
       total_duration = 0;
 
@@ -2488,19 +2589,19 @@ calculate_expected (GstRtpJitterBuffer * jitterbuffer, guint32 expected,
        * the timer thread */
       if (lost_packets > 0) {
         add_timer (jitterbuffer, TIMER_TYPE_LOST, expected, lost_packets,
-            priv->last_in_dts + duration, 0, gap_time);
+            priv->last_in_pts + duration, 0, gap_time);
         expected += lost_packets;
-        priv->last_in_dts += gap_time;
+        priv->last_in_pts += gap_time;
       }
     }
 
-    expected_dts = priv->last_in_dts + duration;
+    expected_pts = priv->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 dts not later than
-     * the last received dts. */
+     * for sure is that the missing packets have expected pts not later than
+     * the last received pts. */
     duration = 0;
-    expected_dts = dts;
+    expected_pts = pts;
   }
 
   delay = 0;
@@ -2516,20 +2617,20 @@ calculate_expected (GstRtpJitterBuffer * jitterbuffer, guint32 expected,
       GstClockTime timeout = timer->timeout;
 
       timer->duration = duration;
-      if (timeout > (expected_dts + delay) && timer->num_rtx_retry == 0) {
-        reschedule_timer (jitterbuffer, timer, timer->seqnum, expected_dts,
+      if (timeout > (expected_pts + delay) && timer->num_rtx_retry == 0) {
+        reschedule_timer (jitterbuffer, timer, timer->seqnum, expected_pts,
             delay, 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, delay, duration);
-    expected_dts += duration;
+    add_timer (jitterbuffer, type, expected, 0, expected_pts, delay, duration);
+    expected_pts += duration;
     expected++;
   }
 }
@@ -2617,13 +2718,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, guint max_dropout,
-    guint max_misorder)
+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;
 
@@ -2712,6 +2813,109 @@ 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;
+  gboolean head;
+
+  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;
+
+  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) {
+    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_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;
+  TimerData *timer;
+
+  priv = jitterbuffer->priv;
+
+  if (priv->faststart_min_packets == 0)
+    return FALSE;
+
+  item = rtp_jitter_buffer_peek (priv->jbuf);
+  if (!item)
+    return FALSE;
+
+  timer = find_timer (jitterbuffer, item->seqnum);
+  if (!timer || timer->type != TIMER_TYPE_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;
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+static GstFlowReturn
 gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
     GstBuffer * buffer)
 {
@@ -2730,7 +2934,7 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
   RTPJitterBufferItem *item;
   GstMessage *msg = NULL;
   gboolean estimated_dts = FALSE;
-  guint32 packet_rate, max_dropout, max_misorder;
+  gint32 packet_rate, max_dropout, max_misorder;
   TimerData *timer = NULL;
 
   jitterbuffer = GST_RTP_JITTER_BUFFER_CAST (parent);
@@ -2828,7 +3032,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
@@ -2853,156 +3056,93 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
       max_dropout, max_misorder);
 
   /* 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)));
+
+    /* 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, pts);
 
+    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 < -max_misorder)) {
-          reset =
-              handle_big_gap_buffer (jitterbuffer, FALSE, buffer, pt, seqnum,
-              gap, max_dropout, max_misorder);
-          buffer = NULL;
-        } else {
-          GST_DEBUG_OBJECT (jitterbuffer, "old packet received");
-        }
-      } else {
-        /* new packet, we are missing some packets */
-        if (G_UNLIKELY (priv->timers->len >= 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,
-              max_dropout);
-          reset = TRUE;
-          gst_buffer_unref (buffer);
-          buffer = NULL;
-        } else if (G_UNLIKELY (gap >= max_dropout)) {
-          reset =
-              handle_big_gap_buffer (jitterbuffer, TRUE, buffer, pt, seqnum,
-              gap, max_dropout, max_misorder);
-          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 && priv->timers->len >= 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, max_dropout);
+      gst_buffer_unref (buffer);
+      return gst_rtp_jitter_buffer_reset (jitterbuffer, pad, parent, seqnum);
+    }
 
-          do_next_seqnum = TRUE;
-        }
+    /* Special handling of large gaps */
+    if ((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;
-
-        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_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, -1);
-        }
-        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)));
 
-        priv->ips_rtptime = -1;
-        priv->ips_dts = 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);
+    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;
   }
 
@@ -3014,6 +3154,17 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
       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_get_seqnum_diff (priv->jbuf) >= 32765 &&
+      rtp_jitter_buffer_num_packets (priv->jbuf) > 10000 &&
+      priv->srcresult == GST_FLOW_OK)
+    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)) {
@@ -3073,23 +3224,26 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
   if (estimated_dts)
     item =
         alloc_item (buffer, ITEM_TYPE_BUFFER, GST_CLOCK_TIME_NONE,
-        GST_CLOCK_TIME_NONE, seqnum, 1, rtptime);
+        pts, seqnum, 1, rtptime);
   else
     item = alloc_item (buffer, ITEM_TYPE_BUFFER, dts, pts, seqnum, 1, rtptime);
 
   /* 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,
-              gst_element_get_base_time (GST_ELEMENT_CAST (jitterbuffer))))) {
+  if (G_UNLIKELY (!rtp_jitter_buffer_insert (priv->jbuf, item, &head,
+              &percent))) {
     if (GST_BUFFER_IS_RETRANSMISSION (buffer) && 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,
       GST_BUFFER_IS_RETRANSMISSION (buffer), timer);
 
   /* we had an unhandled SR, handle it now */
@@ -3187,6 +3341,31 @@ rtx_duplicate:
   }
 }
 
+/* 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
 compute_elapsed (GstRtpJitterBuffer * jitterbuffer, RTPJitterBufferItem * item)
 {
@@ -3258,7 +3437,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);
@@ -3324,6 +3503,11 @@ pop_and_push_next (GstRtpJitterBuffer * jitterbuffer, guint seqnum)
           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);
       GST_BUFFER_PTS (outbuf) = apply_offset (jitterbuffer, pts);
@@ -3331,6 +3515,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:
@@ -3353,6 +3538,17 @@ 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 (priv->timers->len > 0) {
+      /* Stopping timers */
+      unschedule_current_timer (jitterbuffer);
+      JBUF_WAIT_TIMER (priv);
+    }
+  }
+
   JBUF_UNLOCK (priv);
 
   item->data = NULL;
@@ -3483,7 +3679,13 @@ 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 */
+      if (priv->eos) {
+        result = pop_and_push_next (jitterbuffer, seqnum);
+        result = GST_FLOW_OK;
+      } else {
+        result = GST_FLOW_WAIT;
+      }
     }
   }
 
@@ -3731,7 +3933,7 @@ do_lost_timeout (GstRtpJitterBuffer * jitterbuffer, TimerData * timer,
   /* 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_dts = apply_offset (jitterbuffer, timer->timeout);
+    priv->last_in_pts = apply_offset (jitterbuffer, timer->timeout);
   }
 
   /* Avoid creating events if we don't need it. Note that we still need to create
@@ -3752,7 +3954,9 @@ do_lost_timeout (GstRtpJitterBuffer * jitterbuffer, TimerData * timer,
             "retry", G_TYPE_UINT, num_rtx_retry, NULL));
   }
   item = alloc_item (event, ITEM_TYPE_LOST, -1, -1, seqnum, lost_packets, -1);
-  rtp_jitter_buffer_insert (priv->jbuf, item, &head, NULL, -1);
+  if (!rtp_jitter_buffer_insert (priv->jbuf, item, &head, NULL))
+    /* Duplicate */
+    free_item (item);
 
   if (GST_CLOCK_TIME_IS_VALID (timer->rtx_last)) {
     /* Store info to update stats if the packet arrives too late */
@@ -3776,8 +3980,13 @@ do_eos_timeout (GstRtpJitterBuffer * jitterbuffer, TimerData * timer,
   GST_INFO_OBJECT (jitterbuffer, "got the NPT timeout");
   remove_timer (jitterbuffer, 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);
 
@@ -3851,7 +4060,9 @@ wait_next_timeout (GstRtpJitterBuffer * jitterbuffer)
      * 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;
@@ -3926,7 +4137,7 @@ wait_next_timeout (GstRtpJitterBuffer * jitterbuffer)
       GstClockReturn ret;
       GstClockTimeDiff clock_jitter;
 
-      if (timer_timeout == -1 || timer_timeout <= now) {
+      if (timer_timeout == -1 || timer_timeout <= now || priv->eos) {
         /* We have normally removed all lost timers in the loop above */
         g_assert (timer->type != TIMER_TYPE_LOST);
 
@@ -4013,6 +4224,7 @@ gst_rtp_jitter_buffer_loop (GstRtpJitterBuffer * jitterbuffer)
   JBUF_LOCK_CHECK (priv, flushing);
   do {
     result = handle_next_buffer (jitterbuffer);
+    JBUF_SIGNAL_QUEUE (priv);
     if (G_LIKELY (result == GST_FLOW_WAIT)) {
       /* now wait for the next event */
       JBUF_WAIT_EVENT (priv, flushing);
@@ -4042,6 +4254,8 @@ 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;
@@ -4250,7 +4464,7 @@ gst_rtp_jitter_buffer_sink_query (GstPad * pad, GstObject * parent,
             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, -1);
+          rtp_jitter_buffer_insert (priv->jbuf, item, &head, NULL);
           if (head)
             JBUF_SIGNAL_EVENT (priv);
           JBUF_WAIT_QUERY (priv, out_flushing);
@@ -4414,10 +4628,26 @@ 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;
+      }
       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);
@@ -4504,6 +4734,11 @@ gst_rtp_jitter_buffer_set_property (GObject * object,
           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;
@@ -4536,6 +4771,11 @@ 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);
@@ -4640,6 +4880,11 @@ gst_rtp_jitter_buffer_get_property (GObject * object,
           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;