rtp: Add property to disable RTCP reports per internal rtpsource
[platform/upstream/gst-plugins-good.git] / gst / rtpmanager / rtpsource.c
index 7c42205..4fd5f0b 100644 (file)
@@ -1,5 +1,7 @@
 /* GStreamer
  * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
+ * Copyright (C)  2015 Kurento (http://kurento.org/)
+ *   @author: Miguel ParĂ­s <mparisdiaz@gmail.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -40,6 +42,9 @@ enum
 #define DEFAULT_IS_SENDER            FALSE
 #define DEFAULT_SDES                 NULL
 #define DEFAULT_PROBATION            RTP_DEFAULT_PROBATION
+#define DEFAULT_MAX_DROPOUT_TIME     60000
+#define DEFAULT_MAX_MISORDER_TIME    2000
+#define DEFAULT_DISABLE_RTCP         FALSE
 
 enum
 {
@@ -51,7 +56,9 @@ enum
   PROP_SDES,
   PROP_STATS,
   PROP_PROBATION,
-  PROP_LAST
+  PROP_MAX_DROPOUT_TIME,
+  PROP_MAX_MISORDER_TIME,
+  PROP_DISABLE_RTCP
 };
 
 /* GObject vmethods */
@@ -103,7 +110,7 @@ rtp_source_class_init (RTPSourceClass * klass)
    * The current SDES items of the source. Returns a structure with name
    * application/x-rtp-source-sdes and may contain the following fields:
    *
-   *  'cname'       G_TYPE_STRING  : The canonical name
+   *  'cname'       G_TYPE_STRING  : The canonical name in the form user@host
    *  'name'        G_TYPE_STRING  : The user name
    *  'email'       G_TYPE_STRING  : The user's electronic mail address
    *  'phone'       G_TYPE_STRING  : The user's phone number
@@ -111,7 +118,7 @@ rtp_source_class_init (RTPSourceClass * klass)
    *  'tool'        G_TYPE_STRING  : The name of application or tool
    *  'note'        G_TYPE_STRING  : A notice about the source
    *
-   *  other fields may be present and these represent private items in
+   *  Other fields may be present and these represent private items in
    *  the SDES where the field name is the prefix.
    */
   g_object_class_install_property (gobject_class, PROP_SDES,
@@ -122,25 +129,37 @@ rtp_source_class_init (RTPSourceClass * klass)
   /**
    * RTPSource::stats
    *
-   * The statistics of the source. This property returns a GstStructure with
-   * name application/x-rtp-source-stats with the following fields:
+   * This property returns a GstStructure named application/x-rtp-source-stats with
+   * fields useful for statistics and diagnostics.
    *
-   *  "ssrc"         G_TYPE_UINT     The SSRC of this source
-   *  "internal"     G_TYPE_BOOLEAN  If this source is a source of the session
-   *  "validated"    G_TYPE_BOOLEAN  If the source is validated
-   *  "received-bye" G_TYPE_BOOLEAN  If we received a BYE from this source
-   *  "is-csrc"      G_TYPE_BOOLEAN  If this source was found as CSRC
-   *  "is-sender"    G_TYPE_BOOLEAN  If this source is a sender
+   * Take note of each respective field's units:
+   *
+   * - NTP times are in the appropriate 32-bit or 64-bit fixed-point format
+   *   starting from January 1, 1970 (except for timespans).
+   * - RTP times are in clock rate units (i.e. clock rate = 1 second)
+   *   starting at a random offset.
+   * - For fields indicating packet loss, note that late packets are not considered lost,
+   *   and duplicates are not taken into account. Hence, the loss may be negative
+   *   if there are duplicates.
+   *
+   * The following fields are always present.
+   *
+   *  "ssrc"         G_TYPE_UINT     the SSRC of this source
+   *  "internal"     G_TYPE_BOOLEAN  this source is a source of the session
+   *  "validated"    G_TYPE_BOOLEAN  the source is validated
+   *  "received-bye" G_TYPE_BOOLEAN  we received a BYE from this source
+   *  "is-csrc"      G_TYPE_BOOLEAN  this source was found as CSRC
+   *  "is-sender"    G_TYPE_BOOLEAN  this source is a sender
    *  "seqnum-base"  G_TYPE_INT      first seqnum if known
    *  "clock-rate"   G_TYPE_INT      the clock rate of the media
    *
-   * The following two fields are only present when known.
+   * The following fields are only present when known.
    *
    *  "rtp-from"     G_TYPE_STRING   where we received the last RTP packet from
    *  "rtcp-from"    G_TYPE_STRING   where we received the last RTCP packet from
    *
    * The following fields make sense for internal sources and will only increase
-   * when "is-sender" is TRUE:
+   * when "is-sender" is TRUE.
    *
    *  "octets-sent"  G_TYPE_UINT64   number of bytes we sent
    *  "packets-sent" G_TYPE_UINT64   number of packets we sent
@@ -154,15 +173,15 @@ rtp_source_class_init (RTPSourceClass * klass)
    * Following fields are updated when "is-sender" is TRUE.
    *
    *  "bitrate"      G_TYPE_UINT64   bitrate in bits per second
-   *  "jitter"       G_TYPE_UINT     estimated jitter
+   *  "jitter"       G_TYPE_UINT     estimated jitter (in clock rate units)
    *  "packets-lost" G_TYPE_INT      estimated amount of packets lost
    *
    * The last SR report this source sent. This only updates when "is-sender" is
    * TRUE.
    *
    *  "have-sr"         G_TYPE_BOOLEAN  the source has sent SR
-   *  "sr-ntptime"      G_TYPE_UINT64   ntptime of SR
-   *  "sr-rtptime"      G_TYPE_UINT     rtptime of SR
+   *  "sr-ntptime"      G_TYPE_UINT64   NTP time of SR (in NTP Timestamp Format, 32.32 fixed point)
+   *  "sr-rtptime"      G_TYPE_UINT     RTP time of SR (in clock rate units)
    *  "sr-octet-count"  G_TYPE_UINT     the number of bytes in the SR
    *  "sr-packet-count" G_TYPE_UINT     the number of packets in the SR
    *
@@ -171,30 +190,31 @@ rtp_source_class_init (RTPSourceClass * klass)
    * These values are only updated when the source is sending.
    *
    *  "sent-rb"               G_TYPE_BOOLEAN  we have sent an RB
-   *  "sent-rb-fractionlost"  G_TYPE_UINT     calculated lost fraction
+   *  "sent-rb-fractionlost"  G_TYPE_UINT     calculated lost 8-bit fraction
    *  "sent-rb-packetslost"   G_TYPE_INT      lost packets
    *  "sent-rb-exthighestseq" G_TYPE_UINT     last seen seqnum
-   *  "sent-rb-jitter"        G_TYPE_UINT     jitter
-   *  "sent-rb-lsr"           G_TYPE_UINT     last SR time
-   *  "sent-rb-dlsr"          G_TYPE_UINT     delay since last SR
+   *  "sent-rb-jitter"        G_TYPE_UINT     jitter (in clock rate units)
+   *  "sent-rb-lsr"           G_TYPE_UINT     last SR time (seconds in NTP Short Format, 16.16 fixed point)
+   *  "sent-rb-dlsr"          G_TYPE_UINT     delay since last SR (seconds in NTP Short Format, 16.16 fixed point)
    *
    * The following fields are only present for non-internal sources and
    * represents the last RB that this source sent. This is only updated
    * when the source is receiving data and sending RB blocks.
    *
    *  "have-rb"          G_TYPE_BOOLEAN  the source has sent RB
-   *  "rb-fractionlost"  G_TYPE_UINT     lost fraction
+   *  "rb-fractionlost"  G_TYPE_UINT     lost 8-bit fraction
    *  "rb-packetslost"   G_TYPE_INT      lost packets
    *  "rb-exthighestseq" G_TYPE_UINT     highest received seqnum
-   *  "rb-jitter"        G_TYPE_UINT     reception jitter
-   *  "rb-lsr"           G_TYPE_UINT     last SR time
-   *  "rb-dlsr"          G_TYPE_UINT     delay since last SR
+   *  "rb-jitter"        G_TYPE_UINT     reception jitter (in clock rate units)
+   *  "rb-lsr"           G_TYPE_UINT     last SR time (seconds in NTP Short Format, 16.16 fixed point)
+   *  "rb-dlsr"          G_TYPE_UINT     delay since last SR (seconds in NTP Short Format, 16.16 fixed point)
    *
-   * The round trip of this source. This is calculated from the last RB
-   * values and the recption time of the last RB packet. Only present for
+   * The round trip of this source is calculated from the last RB
+   * values and the reception time of the last RB packet. It is only present for
    * non-internal sources.
    *
-   *  "rb-round-trip"    G_TYPE_UINT     the round trip time in nanoseconds
+   *  "rb-round-trip"    G_TYPE_UINT     the round-trip time (seconds in NTP Short Format, 16.16 fixed point)
+   *
    */
   g_object_class_install_property (gobject_class, PROP_STATS,
       g_param_spec_boxed ("stats", "Stats",
@@ -207,6 +227,28 @@ rtp_source_class_init (RTPSourceClass * klass)
           0, G_MAXUINT, DEFAULT_PROBATION,
           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_MAXUINT, 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));
+
+  /**
+   * RTPSession::disable-rtcp:
+   *
+   * Allow disabling the sending of RTCP packets for this source.
+   */
+  g_object_class_install_property (gobject_class, PROP_DISABLE_RTCP,
+      g_param_spec_boolean ("disable-rtcp", "Disable RTCP",
+          "Disable sending RTCP packets for this source",
+          DEFAULT_DISABLE_RTCP, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   GST_DEBUG_CATEGORY_INIT (rtp_source_debug, "rtpsource", 0, "RTP Source");
 }
 
@@ -225,6 +267,9 @@ rtp_source_reset (RTPSource * src)
   src->bye_reason = NULL;
   src->sent_bye = FALSE;
   g_hash_table_remove_all (src->reported_in_sr_of);
+  g_queue_foreach (src->retained_feedback, (GFunc) gst_buffer_unref, NULL);
+  g_queue_clear (src->retained_feedback);
+  src->last_rtptime = -1;
 
   src->stats.cycles = -1;
   src->stats.jitter = 0;
@@ -240,6 +285,9 @@ rtp_source_reset (RTPSource * src)
   g_array_set_size (src->nacks, 0);
 
   src->stats.sent_pli_count = 0;
+  src->stats.sent_fir_count = 0;
+  src->stats.sent_nack_count = 0;
+  src->stats.recv_nack_count = 0;
 }
 
 static void
@@ -252,6 +300,8 @@ rtp_source_init (RTPSource * src)
   src->probation = DEFAULT_PROBATION;
   src->curr_probation = src->probation;
   src->closing = FALSE;
+  src->max_dropout_time = DEFAULT_MAX_DROPOUT_TIME;
+  src->max_misorder_time = DEFAULT_MAX_MISORDER_TIME;
 
   src->sdes = gst_structure_new_empty ("application/x-rtp-source-sdes");
 
@@ -259,14 +309,17 @@ rtp_source_init (RTPSource * src)
   src->clock_rate = -1;
   src->packets = g_queue_new ();
   src->seqnum_offset = -1;
-  src->last_rtptime = -1;
 
   src->retained_feedback = g_queue_new ();
   src->nacks = g_array_new (FALSE, FALSE, sizeof (guint32));
 
   src->reported_in_sr_of = g_hash_table_new (g_direct_hash, g_direct_equal);
 
+  src->last_keyframe_request = GST_CLOCK_TIME_NONE;
+
   rtp_source_reset (src);
+
+  src->pt_set = FALSE;
 }
 
 void
@@ -280,12 +333,10 @@ static void
 rtp_source_finalize (GObject * object)
 {
   RTPSource *src;
-  GstBuffer *buffer;
 
   src = RTP_SOURCE_CAST (object);
 
-  while ((buffer = g_queue_pop_head (src->packets)))
-    gst_buffer_unref (buffer);
+  g_queue_foreach (src->packets, (GFunc) gst_buffer_unref, NULL);
   g_queue_free (src->packets);
 
   gst_structure_free (src->sdes);
@@ -296,8 +347,7 @@ rtp_source_finalize (GObject * object)
 
   g_list_free_full (src->conflicting_addresses,
       (GDestroyNotify) rtp_conflicting_address_free);
-  while ((buffer = g_queue_pop_head (src->retained_feedback)))
-    gst_buffer_unref (buffer);
+  g_queue_foreach (src->retained_feedback, (GFunc) gst_buffer_unref, NULL);
   g_queue_free (src->retained_feedback);
 
   g_array_free (src->nacks, TRUE);
@@ -368,7 +418,11 @@ rtp_source_create_stats (RTPSource * src)
       (gint) rtp_stats_get_packets_lost (&src->stats), "jitter", G_TYPE_UINT,
       (guint) (src->stats.jitter >> 4),
       "sent-pli-count", G_TYPE_UINT, src->stats.sent_pli_count,
-      "recv-pli-count", G_TYPE_UINT, src->stats.recv_pli_count, NULL);
+      "recv-pli-count", G_TYPE_UINT, src->stats.recv_pli_count,
+      "sent-fir-count", G_TYPE_UINT, src->stats.sent_fir_count,
+      "recv-fir-count", G_TYPE_UINT, src->stats.recv_fir_count,
+      "sent-nack-count", G_TYPE_UINT, src->stats.sent_nack_count,
+      "recv-nack-count", G_TYPE_UINT, src->stats.recv_nack_count, NULL);
 
   /* get the last SR. */
   have_sr = rtp_source_get_last_sr (src, &time, &ntptime, &rtptime,
@@ -493,6 +547,15 @@ rtp_source_set_property (GObject * object, guint prop_id,
     case PROP_PROBATION:
       src->probation = g_value_get_uint (value);
       break;
+    case PROP_MAX_DROPOUT_TIME:
+      src->max_dropout_time = g_value_get_uint (value);
+      break;
+    case PROP_MAX_MISORDER_TIME:
+      src->max_misorder_time = g_value_get_uint (value);
+      break;
+    case PROP_DISABLE_RTCP:
+      src->disable_rtcp = g_value_get_boolean (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -529,6 +592,15 @@ rtp_source_get_property (GObject * object, guint prop_id,
     case PROP_PROBATION:
       g_value_set_uint (value, src->probation);
       break;
+    case PROP_MAX_DROPOUT_TIME:
+      g_value_set_uint (value, src->max_dropout_time);
+      break;
+    case PROP_MAX_MISORDER_TIME:
+      g_value_set_uint (value, src->max_misorder_time);
+      break;
+    case PROP_DISABLE_RTCP:
+      g_value_set_boolean (value, src->disable_rtcp);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -745,6 +817,7 @@ rtp_source_update_caps (RTPSource * src, GstCaps * caps)
   GstStructure *s;
   guint val;
   gint ival;
+  gboolean rtx;
 
   /* nothing changed, return */
   if (caps == NULL || src->caps == caps)
@@ -752,11 +825,14 @@ rtp_source_update_caps (RTPSource * src, GstCaps * caps)
 
   s = gst_caps_get_structure (caps, 0);
 
-  if (gst_structure_get_int (s, "payload", &ival))
+  rtx = (gst_structure_get_uint (s, "rtx-ssrc", &val) && val == src->ssrc);
+
+  if (gst_structure_get_int (s, rtx ? "rtx-payload" : "payload", &ival))
     src->payload = ival;
   else
     src->payload = -1;
-  GST_DEBUG ("got payload %d", src->payload);
+
+  GST_DEBUG ("got %spayload %d", rtx ? "rtx " : "", src->payload);
 
   if (gst_structure_get_int (s, "clock-rate", &ival))
     src->clock_rate = ival;
@@ -765,12 +841,14 @@ rtp_source_update_caps (RTPSource * src, GstCaps * caps)
 
   GST_DEBUG ("got clock-rate %d", src->clock_rate);
 
-  if (gst_structure_get_uint (s, "seqnum-offset", &val))
+  if (gst_structure_get_uint (s, rtx ? "rtx-seqnum-offset" : "seqnum-offset",
+          &val))
     src->seqnum_offset = val;
   else
     src->seqnum_offset = -1;
 
-  GST_DEBUG ("got seqnum-offset %" G_GINT32_FORMAT, src->seqnum_offset);
+  GST_DEBUG ("got %sseqnum-offset %" G_GINT32_FORMAT, rtx ? "rtx " : "",
+      src->seqnum_offset);
 
   gst_caps_replace (&src->caps, caps);
 }
@@ -860,6 +938,7 @@ get_clock_rate (RTPSource * src, guint8 payload)
     GST_DEBUG ("got clock-rate %d", clock_rate);
 
     src->clock_rate = clock_rate;
+    gst_rtp_packet_rate_ctx_reset (&src->packet_rate_ctx, clock_rate);
   }
   return src->clock_rate;
 }
@@ -947,6 +1026,7 @@ init_seq (RTPSource * src, guint16 seq)
   src->stats.prev_received = 0;
   src->stats.prev_expected = 0;
   src->stats.recv_pli_count = 0;
+  src->stats.recv_fir_count = 0;
 
   GST_DEBUG ("base_seq %d", seq);
 }
@@ -986,16 +1066,30 @@ do_bitrate_estimation (RTPSource * src, GstClockTime running_time,
 }
 
 static gboolean
-update_receiver_stats (RTPSource * src, RTPPacketInfo * pinfo)
+update_receiver_stats (RTPSource * src, RTPPacketInfo * pinfo,
+    gboolean is_receive)
 {
-  guint16 seqnr, udelta;
+  guint16 seqnr, expected;
   RTPSourceStats *stats;
-  guint16 expected;
+  gint16 delta;
+  gint32 packet_rate, max_dropout, max_misorder;
 
   stats = &src->stats;
 
   seqnr = pinfo->seqnum;
 
+  packet_rate =
+      gst_rtp_packet_rate_ctx_update (&src->packet_rate_ctx, pinfo->seqnum,
+      pinfo->rtptime);
+  max_dropout =
+      gst_rtp_packet_rate_ctx_get_max_dropout (&src->packet_rate_ctx,
+      src->max_dropout_time);
+  max_misorder =
+      gst_rtp_packet_rate_ctx_get_max_misorder (&src->packet_rate_ctx,
+      src->max_misorder_time);
+  GST_TRACE ("SSRC %08x, packet_rate: %d, max_dropout: %d, max_misorder: %d",
+      src->ssrc, packet_rate, max_dropout, max_misorder);
+
   if (stats->cycles == -1) {
     GST_DEBUG ("received first packet");
     /* first time we heard of this source */
@@ -1004,61 +1098,82 @@ update_receiver_stats (RTPSource * src, RTPPacketInfo * pinfo)
     src->curr_probation = src->probation;
   }
 
-  udelta = seqnr - stats->max_seq;
-
-  /* if we are still on probation, check seqnum */
-  if (src->curr_probation) {
+  if (is_receive) {
     expected = src->stats.max_seq + 1;
-
-    /* when in probation, we require consecutive seqnums */
-    if (seqnr == expected) {
-      /* expected packet */
-      GST_DEBUG ("probation: seqnr %d == expected %d", seqnr, expected);
-      src->curr_probation--;
-      src->stats.max_seq = seqnr;
-      if (src->curr_probation == 0) {
-        GST_DEBUG ("probation done!");
+    delta = gst_rtp_buffer_compare_seqnum (expected, seqnr);
+
+    /* if we are still on probation, check seqnum */
+    if (src->curr_probation) {
+      /* when in probation, we require consecutive seqnums */
+      if (delta == 0) {
+        /* expected packet */
+        GST_DEBUG ("probation: seqnr %d == expected %d", seqnr, expected);
+        src->curr_probation--;
+        if (seqnr < stats->max_seq) {
+          /* sequence number wrapped - count another 64K cycle. */
+          stats->cycles += RTP_SEQ_MOD;
+        }
+        src->stats.max_seq = seqnr;
+
+        if (src->curr_probation == 0) {
+          GST_DEBUG ("probation done!");
+          init_seq (src, seqnr);
+        } else {
+          GstBuffer *q;
+
+          GST_DEBUG ("probation %d: queue packet", src->curr_probation);
+          /* when still in probation, keep packets in a list. */
+          g_queue_push_tail (src->packets, pinfo->data);
+          pinfo->data = NULL;
+          /* remove packets from queue if there are too many */
+          while (g_queue_get_length (src->packets) > RTP_MAX_PROBATION_LEN) {
+            q = g_queue_pop_head (src->packets);
+            gst_buffer_unref (q);
+          }
+          goto done;
+        }
+      } else {
+        /* unexpected seqnum in probation */
+        goto probation_seqnum;
+      }
+    } else if (delta >= 0 && delta < max_dropout) {
+      /* Clear bad packets */
+      stats->bad_seq = RTP_SEQ_MOD + 1; /* so seq == bad_seq is false */
+      g_queue_foreach (src->packets, (GFunc) gst_buffer_unref, NULL);
+      g_queue_clear (src->packets);
+
+      /* in order, with permissible gap */
+      if (seqnr < stats->max_seq) {
+        /* sequence number wrapped - count another 64K cycle. */
+        stats->cycles += RTP_SEQ_MOD;
+      }
+      stats->max_seq = seqnr;
+    } else if (delta < -max_misorder || delta >= max_dropout) {
+      /* the sequence number made a very large jump */
+      if (seqnr == stats->bad_seq && src->packets->head) {
+        /* two sequential packets -- assume that the other side
+         * restarted without telling us so just re-sync
+         * (i.e., pretend this was the first packet).  */
         init_seq (src, seqnr);
       } else {
-        GstBuffer *q;
-
-        GST_DEBUG ("probation %d: queue packet", src->curr_probation);
-        /* when still in probation, keep packets in a list. */
+        /* unacceptable jump */
+        stats->bad_seq = (seqnr + 1) & (RTP_SEQ_MOD - 1);
+        g_queue_foreach (src->packets, (GFunc) gst_buffer_unref, NULL);
+        g_queue_clear (src->packets);
         g_queue_push_tail (src->packets, pinfo->data);
         pinfo->data = NULL;
-        /* remove packets from queue if there are too many */
-        while (g_queue_get_length (src->packets) > RTP_MAX_PROBATION_LEN) {
-          q = g_queue_pop_head (src->packets);
-          gst_buffer_unref (q);
-        }
-        goto done;
+        goto bad_sequence;
       }
-    } else {
-      /* unexpected seqnum in probation */
-      goto probation_seqnum;
-    }
-  } else if (udelta < RTP_MAX_DROPOUT) {
-    /* in order, with permissible gap */
-    if (seqnr < stats->max_seq) {
-      /* sequence number wrapped - count another 64K cycle. */
-      stats->cycles += RTP_SEQ_MOD;
+    } else {                    /* delta < 0 && delta >= -max_misorder */
+      /* Clear bad packets */
+      stats->bad_seq = RTP_SEQ_MOD + 1; /* so seq == bad_seq is false */
+      g_queue_foreach (src->packets, (GFunc) gst_buffer_unref, NULL);
+      g_queue_clear (src->packets);
+
+      /* duplicate or reordered packet, will be filtered by jitterbuffer. */
+      GST_INFO ("duplicate or reordered packet (seqnr %u, expected %u)",
+          seqnr, expected);
     }
-    stats->max_seq = seqnr;
-  } else if (udelta <= RTP_SEQ_MOD - RTP_MAX_MISORDER) {
-    /* the sequence number made a very large jump */
-    if (seqnr == stats->bad_seq) {
-      /* two sequential packets -- assume that the other side
-       * restarted without telling us so just re-sync
-       * (i.e., pretend this was the first packet).  */
-      init_seq (src, seqnr);
-    } else {
-      /* unacceptable jump */
-      stats->bad_seq = (seqnr + 1) & (RTP_SEQ_MOD - 1);
-      goto bad_sequence;
-    }
-  } else {
-    /* duplicate or reordered packet, will be filtered by jitterbuffer. */
-    GST_WARNING ("duplicate or reordered packet (seqnr %d)", seqnr);
   }
 
   src->stats.octets_received += pinfo->payload_len;
@@ -1067,7 +1182,7 @@ update_receiver_stats (RTPSource * src, RTPPacketInfo * pinfo)
   /* for the bitrate estimation */
   src->bytes_received += pinfo->payload_len;
 
-  GST_LOG ("seq %d, PC: %" G_GUINT64_FORMAT ", OC: %" G_GUINT64_FORMAT,
+  GST_LOG ("seq %u, PC: %" G_GUINT64_FORMAT ", OC: %" G_GUINT64_FORMAT,
       seqnr, src->stats.packets_received, src->stats.octets_received);
 
   return TRUE;
@@ -1079,7 +1194,9 @@ done:
   }
 bad_sequence:
   {
-    GST_WARNING ("unacceptable seqnum received");
+    GST_WARNING
+        ("unacceptable seqnum received (seqnr %u, delta %d, packet_rate: %d, max_dropout: %d, max_misorder: %d)",
+        seqnr, delta, packet_rate, max_dropout, max_misorder);
     return FALSE;
   }
 probation_seqnum:
@@ -1108,7 +1225,7 @@ rtp_source_process_rtp (RTPSource * src, RTPPacketInfo * pinfo)
   g_return_val_if_fail (RTP_IS_SOURCE (src), GST_FLOW_ERROR);
   g_return_val_if_fail (pinfo != NULL, GST_FLOW_ERROR);
 
-  if (!update_receiver_stats (src, pinfo))
+  if (!update_receiver_stats (src, pinfo, TRUE))
     return GST_FLOW_OK;
 
   /* the source that sent the packet must be a sender */
@@ -1178,12 +1295,22 @@ rtp_source_send_rtp (RTPSource * src, RTPPacketInfo * pinfo)
   /* we are a sender now */
   src->is_sender = TRUE;
 
+  /* we are also a receiver of our packets */
+  if (!update_receiver_stats (src, pinfo, FALSE))
+    return GST_FLOW_OK;
+
+  if (src->pt_set && src->pt != pinfo->pt) {
+    GST_WARNING ("Changing pt from %u to %u for SSRC %u", src->pt, pinfo->pt,
+        src->ssrc);
+  }
+
+  src->pt = pinfo->pt;
+  src->pt_set = TRUE;
+
   /* update stats for the SR */
   src->stats.packets_sent += pinfo->packets;
   src->stats.octets_sent += pinfo->payload_len;
   src->bytes_sent += pinfo->payload_len;
-  /* we are also a receiver of our packets */
-  update_receiver_stats (src, pinfo);
 
   running_time = pinfo->running_time;
 
@@ -1237,10 +1364,10 @@ no_callback:
  * rtp_source_process_sr:
  * @src: an #RTPSource
  * @time: time of packet arrival
- * @ntptime: the NTP time in 32.32 fixed point
- * @rtptime: the RTP time
+ * @ntptime: the NTP time (in NTP Timestamp Format, 32.32 fixed point)
+ * @rtptime: the RTP time (in clock rate units)
  * @packet_count: the packet count
- * @octet_count: the octect count
+ * @octet_count: the octet count
  *
  * Update the sender report in @src.
  */
@@ -1284,11 +1411,13 @@ rtp_source_process_sr (RTPSource * src, GstClockTime time, guint64 ntptime,
  * @src: an #RTPSource
  * @ntpnstime: the current time in nanoseconds since 1970
  * @fractionlost: fraction lost since last SR/RR
- * @packetslost: the cumululative number of packets lost
+ * @packetslost: the cumulative number of packets lost
  * @exthighestseq: the extended last sequence number received
- * @jitter: the interarrival jitter
- * @lsr: the last SR packet from this source
- * @dlsr: the delay since last SR packet
+ * @jitter: the interarrival jitter (in clock rate units)
+ * @lsr: the time of the last SR packet on this source
+ *   (in NTP Short Format, 16.16 fixed point)
+ * @dlsr: the delay since the last SR packet
+ *   (in NTP Short Format, 16.16 fixed point)
  *
  * Update the report block in @src.
  */
@@ -1344,17 +1473,17 @@ rtp_source_process_rb (RTPSource * src, guint64 ntpnstime,
  * rtp_source_get_new_sr:
  * @src: an #RTPSource
  * @ntpnstime: the current time in nanoseconds since 1970
- * @running_time: the current running_time of the pipeline.
- * @ntptime: the NTP time in 32.32 fixed point
- * @rtptime: the RTP time corresponding to @ntptime
+ * @running_time: the current running_time of the pipeline
+ * @ntptime: the NTP time (in NTP Timestamp Format, 32.32 fixed point)
+ * @rtptime: the RTP time corresponding to @ntptime (in clock rate units)
  * @packet_count: the packet count
- * @octet_count: the octect count
+ * @octet_count: the octet count
  *
  * Get new values to put into a new SR report from this source.
  *
  * @running_time and @ntpnstime are captured at the same time and represent the
  * running time of the pipeline clock and the absolute current system time in
- * nanoseconds respectively. Together with the last running_time and rtp timestamp
+ * nanoseconds respectively. Together with the last running_time and RTP timestamp
  * we have observed in the source, we can generate @ntptime and @rtptime for an SR
  * packet. @ntptime is basically the fixed point representation of @ntpnstime
  * and @rtptime the associated RTP timestamp.
@@ -1383,6 +1512,12 @@ rtp_source_get_new_sr (RTPSource * src, guint64 ntpnstime,
   GST_DEBUG ("last_rtime %" GST_TIME_FORMAT ", last_rtptime %"
       G_GUINT64_FORMAT, GST_TIME_ARGS (src->last_rtime), t_rtp);
 
+  if (src->clock_rate == -1 && src->pt_set) {
+    GST_INFO ("no clock-rate, getting for pt %u and SSRC %u", src->pt,
+        src->ssrc);
+    get_clock_rate (src, src->pt);
+  }
+
   if (src->clock_rate != -1) {
     /* get the diff between the clock running_time and the buffer running_time.
      * This is the elapsed time, as measured against the pipeline clock, between
@@ -1391,21 +1526,20 @@ rtp_source_get_new_sr (RTPSource * src, guint64 ntpnstime,
      * We need to apply this diff to the RTP timestamp to get the RTP timestamp
      * for the given ntpnstime. */
     diff = GST_CLOCK_DIFF (src->last_rtime, running_time);
+    GST_DEBUG ("running_time %" GST_TIME_FORMAT ", diff %" GST_STIME_FORMAT,
+        GST_TIME_ARGS (running_time), GST_STIME_ARGS (diff));
 
     /* now translate the diff to RTP time, handle positive and negative cases.
      * If there is no diff, we already set rtptime correctly above. */
     if (diff > 0) {
-      GST_DEBUG ("running_time %" GST_TIME_FORMAT ", diff %" GST_TIME_FORMAT,
-          GST_TIME_ARGS (running_time), GST_TIME_ARGS (diff));
       t_rtp += gst_util_uint64_scale_int (diff, src->clock_rate, GST_SECOND);
     } else {
       diff = -diff;
-      GST_DEBUG ("running_time %" GST_TIME_FORMAT ", diff -%" GST_TIME_FORMAT,
-          GST_TIME_ARGS (running_time), GST_TIME_ARGS (diff));
       t_rtp -= gst_util_uint64_scale_int (diff, src->clock_rate, GST_SECOND);
     }
   } else {
-    GST_WARNING ("no clock-rate, cannot interpolate rtp time");
+    GST_WARNING ("no clock-rate, cannot interpolate rtp time for SSRC %u",
+        src->ssrc);
   }
 
   /* convert the NTP time in nanoseconds to 32.32 fixed point */
@@ -1432,11 +1566,13 @@ rtp_source_get_new_sr (RTPSource * src, guint64 ntpnstime,
  * @src: an #RTPSource
  * @time: the current time of the system clock
  * @fractionlost: fraction lost since last SR/RR
- * @packetslost: the cumululative number of packets lost
+ * @packetslost: the cumulative number of packets lost
  * @exthighestseq: the extended last sequence number received
- * @jitter: the interarrival jitter
- * @lsr: the last SR packet from this source
- * @dlsr: the delay since last SR packet
+ * @jitter: the interarrival jitter (in clock rate units)
+ * @lsr: the time of the last SR packet on this source
+ *   (in NTP Short Format, 16.16 fixed point)
+ * @dlsr: the delay since the last SR packet
+ *   (in NTP Short Format, 16.16 fixed point)
  *
  * Get new values to put into a new report block from this source.
  *
@@ -1522,10 +1658,10 @@ rtp_source_get_new_rb (RTPSource * src, GstClockTime time,
  * rtp_source_get_last_sr:
  * @src: an #RTPSource
  * @time: time of packet arrival
- * @ntptime: the NTP time in 32.32 fixed point
- * @rtptime: the RTP time
+ * @ntptime: the NTP time (in NTP Timestamp Format, 32.32 fixed point)
+ * @rtptime: the RTP time (in clock rate units)
  * @packet_count: the packet count
- * @octet_count: the octect count
+ * @octet_count: the octet count
  *
  * Get the values of the last sender report as set with rtp_source_process_sr().
  *
@@ -1561,12 +1697,15 @@ rtp_source_get_last_sr (RTPSource * src, GstClockTime * time, guint64 * ntptime,
  * rtp_source_get_last_rb:
  * @src: an #RTPSource
  * @fractionlost: fraction lost since last SR/RR
- * @packetslost: the cumululative number of packets lost
+ * @packetslost: the cumulative number of packets lost
  * @exthighestseq: the extended last sequence number received
- * @jitter: the interarrival jitter
- * @lsr: the last SR packet from this source
- * @dlsr: the delay since last SR packet
- * @round_trip: the round trip time
+ * @jitter: the interarrival jitter (in clock rate units)
+ * @lsr: the time of the last SR packet on this source
+ *   (in NTP Short Format, 16.16 fixed point)
+ * @dlsr: the delay since the last SR packet
+ *   (in NTP Short Format, 16.16 fixed point)
+ * @round_trip: the round-trip time
+ *   (in NTP Short Format, 16.16 fixed point)
  *
  * Get the values of the last RB report set with rtp_source_process_rb().
  *
@@ -1710,17 +1849,33 @@ rtp_source_add_conflicting_address (RTPSource * src,
  */
 void
 rtp_source_timeout (RTPSource * src, GstClockTime current_time,
-    GstClockTime feedback_retention_window)
+    GstClockTime running_time, GstClockTime feedback_retention_window)
 {
   GstRTCPPacket *pkt;
+  GstClockTime max_pts_window;
+  guint pruned = 0;
 
   src->conflicting_addresses =
       timeout_conflicting_addresses (src->conflicting_addresses, current_time);
 
+  if (feedback_retention_window == GST_CLOCK_TIME_NONE ||
+      running_time < feedback_retention_window) {
+    return;
+  }
+
+  max_pts_window = running_time - feedback_retention_window;
+
   /* Time out AVPF packets that are older than the desired length */
-  while ((pkt = g_queue_peek_tail (src->retained_feedback)) &&
-      GST_BUFFER_TIMESTAMP (pkt) < feedback_retention_window)
-    gst_buffer_unref (g_queue_pop_tail (src->retained_feedback));
+  while ((pkt = g_queue_peek_head (src->retained_feedback)) &&
+      GST_BUFFER_PTS (pkt) < max_pts_window) {
+    gst_buffer_unref (g_queue_pop_head (src->retained_feedback));
+    pruned++;
+  }
+
+  GST_LOG_OBJECT (src,
+      "%u RTCP packets pruned with PTS less than %" GST_TIME_FORMAT
+      ", queue len: %u", pruned, GST_TIME_ARGS (max_pts_window),
+      g_queue_get_length (src->retained_feedback));
 }
 
 static gint
@@ -1729,7 +1884,16 @@ compare_buffers (gconstpointer a, gconstpointer b, gpointer user_data)
   const GstBuffer *bufa = a;
   const GstBuffer *bufb = b;
 
-  return GST_BUFFER_TIMESTAMP (bufa) - GST_BUFFER_TIMESTAMP (bufb);
+  g_return_val_if_fail (GST_BUFFER_PTS (bufa) != GST_CLOCK_TIME_NONE, -1);
+  g_return_val_if_fail (GST_BUFFER_PTS (bufb) != GST_CLOCK_TIME_NONE, 1);
+
+  if (GST_BUFFER_PTS (bufa) < GST_BUFFER_PTS (bufb)) {
+    return -1;
+  } else if (GST_BUFFER_PTS (bufa) > GST_BUFFER_PTS (bufb)) {
+    return 1;
+  }
+
+  return 0;
 }
 
 void
@@ -1738,12 +1902,17 @@ rtp_source_retain_rtcp_packet (RTPSource * src, GstRTCPPacket * packet,
 {
   GstBuffer *buffer;
 
+  g_return_if_fail (running_time != GST_CLOCK_TIME_NONE);
+
   buffer = gst_buffer_copy_region (packet->rtcp->buffer, GST_BUFFER_COPY_MEMORY,
       packet->offset, (gst_rtcp_packet_get_length (packet) + 1) * 4);
 
-  GST_BUFFER_TIMESTAMP (buffer) = running_time;
+  GST_BUFFER_PTS (buffer) = running_time;
 
   g_queue_insert_sorted (src->retained_feedback, buffer, compare_buffers, NULL);
+
+  GST_LOG_OBJECT (src, "RTCP packet retained with PTS: %" GST_TIME_FORMAT,
+      GST_TIME_ARGS (running_time));
 }
 
 gboolean
@@ -1756,6 +1925,7 @@ rtp_source_has_retained (RTPSource * src, GCompareFunc func, gconstpointer data)
 }
 
 /**
+ * rtp_source_register_nack:
  * @src: The #RTPSource
  * @seqnum: a seqnum
  *
@@ -1803,6 +1973,7 @@ rtp_source_register_nack (RTPSource * src, guint16 seqnum)
 }
 
 /**
+ * rtp_source_get_nacks:
  * @src: The #RTPSource
  * @n_nacks: result number of nacks
  *