rtpsession: Use bandwidth calculation by default instead of some arbitrary hardcoded...
[platform/upstream/gst-plugins-good.git] / gst / rtpmanager / rtpsession.c
index 9dc1525..a260186 100644 (file)
@@ -49,12 +49,14 @@ enum
   SIGNAL_ON_SENDING_RTCP,
   SIGNAL_ON_FEEDBACK_RTCP,
   SIGNAL_SEND_RTCP,
+  SIGNAL_SEND_RTCP_FULL,
+  SIGNAL_ON_RECEIVING_RTCP,
   LAST_SIGNAL
 };
 
 #define DEFAULT_INTERNAL_SOURCE      NULL
-#define DEFAULT_BANDWIDTH            RTP_STATS_BANDWIDTH
-#define DEFAULT_RTCP_FRACTION        (RTP_STATS_RTCP_FRACTION * RTP_STATS_BANDWIDTH)
+#define DEFAULT_BANDWIDTH            0.0
+#define DEFAULT_RTCP_FRACTION        RTP_STATS_RTCP_FRACTION
 #define DEFAULT_RTCP_RR_BANDWIDTH    -1
 #define DEFAULT_RTCP_RS_BANDWIDTH    -1
 #define DEFAULT_RTCP_MTU             1400
@@ -86,8 +88,7 @@ enum
   PROP_RTCP_FEEDBACK_RETENTION_WINDOW,
   PROP_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD,
   PROP_PROBATION,
-  PROP_STATS,
-  PROP_LAST
+  PROP_STATS
 };
 
 /* update average packet size */
@@ -107,7 +108,8 @@ static void rtp_session_set_property (GObject * object, guint prop_id,
 static void rtp_session_get_property (GObject * object, guint prop_id,
     GValue * value, GParamSpec * pspec);
 
-static void rtp_session_send_rtcp (RTPSession * sess, GstClockTime max_delay);
+static gboolean rtp_session_send_rtcp (RTPSession * sess,
+    GstClockTime max_delay);
 
 static guint rtp_session_signals[LAST_SIGNAL] = { 0 };
 
@@ -117,7 +119,7 @@ static guint32 rtp_session_create_new_ssrc (RTPSession * sess);
 static RTPSource *obtain_source (RTPSession * sess, guint32 ssrc,
     gboolean * created, RTPPacketInfo * pinfo, gboolean rtp);
 static RTPSource *obtain_internal_source (RTPSession * sess,
-    guint32 ssrc, gboolean * created);
+    guint32 ssrc, gboolean * created, GstClockTime current_time);
 static GstFlowReturn rtp_session_schedule_bye_locked (RTPSession * sess,
     GstClockTime current_time);
 static GstClockTime calculate_rtcp_interval (RTPSession * sess,
@@ -318,6 +320,42 @@ rtp_session_class_init (RTPSessionClass * klass)
       G_STRUCT_OFFSET (RTPSessionClass, send_rtcp), NULL, NULL,
       g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_UINT64);
 
+  /**
+   * RTPSession::send-rtcp-full:
+   * @session: the object which received the signal
+   * @max_delay: The maximum delay after which the feedback will not be useful
+   *  anymore
+   *
+   * Requests that the #RTPSession initiate a new RTCP packet as soon as
+   * possible within the requested delay.
+   *
+   * Returns: TRUE if the new RTCP packet could be scheduled within the
+   * requested delay, FALSE otherwise.
+   *
+   * Since: 1.6
+   */
+  rtp_session_signals[SIGNAL_SEND_RTCP_FULL] =
+      g_signal_new ("send-rtcp-full", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+      G_STRUCT_OFFSET (RTPSessionClass, send_rtcp), NULL, NULL,
+      g_cclosure_marshal_generic, G_TYPE_BOOLEAN, 1, G_TYPE_UINT64);
+
+  /**
+   * RTPSession::on-receiving-rtcp
+   * @session: the object which received the signal
+   * @buffer: the #GstBuffer containing the RTCP packet that was received
+   *
+   * This signal is emitted when receiving an RTCP packet before it is handled
+   * by the session. It can be used to extract custom information from RTCP packets.
+   *
+   * Since: 1.6
+   */
+  rtp_session_signals[SIGNAL_ON_RECEIVING_RTCP] =
+      g_signal_new ("on-receiving-rtcp", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_receiving_rtcp),
+      NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
+      GST_TYPE_BUFFER | G_SIGNAL_TYPE_STATIC_SCOPE);
+
   g_object_class_install_property (gobject_class, PROP_INTERNAL_SSRC,
       g_param_spec_uint ("internal-ssrc", "Internal SSRC",
           "The internal SSRC used for the session (deprecated)",
@@ -428,9 +466,9 @@ rtp_session_class_init (RTPSessionClass * klass)
       g_param_spec_uint ("rtcp-immediate-feedback-threshold",
           "RTCP Immediate Feedback threshold",
           "The maximum number of members of a RTP session for which immediate"
-          " feedback is used",
+          " feedback is used (DEPRECATED: has no effect and is not needed)",
           0, G_MAXUINT, DEFAULT_RTCP_IMMEDIATE_FEEDBACK_THRESHOLD,
-          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_DEPRECATED));
 
   g_object_class_install_property (gobject_class, PROP_PROBATION,
       g_param_spec_uint ("probation", "Number of probations",
@@ -474,7 +512,11 @@ rtp_session_init (RTPSession * sess)
   sess->mask_idx = 0;
   sess->mask = 0;
 
-  for (i = 0; i < 32; i++) {
+  /* TODO: We currently only use the first hash table but this is the
+   * beginning of an implementation for RFC2762
+   for (i = 0; i < 32; i++) {
+   */
+  for (i = 0; i < 1; i++) {
     sess->ssrcs[i] =
         g_hash_table_new_full (NULL, NULL, NULL,
         (GDestroyNotify) g_object_unref);
@@ -519,6 +561,7 @@ rtp_session_init (RTPSession * sess)
 
   sess->first_rtcp = TRUE;
   sess->next_rtcp_check_time = GST_CLOCK_TIME_NONE;
+  sess->last_rtcp_send_time = GST_CLOCK_TIME_NONE;
 
   sess->allow_early = TRUE;
   sess->next_early_rtcp_time = GST_CLOCK_TIME_NONE;
@@ -544,7 +587,10 @@ rtp_session_finalize (GObject * object)
   g_list_free_full (sess->conflicting_addresses,
       (GDestroyNotify) rtp_conflicting_address_free);
 
-  for (i = 0; i < 32; i++)
+  /* TODO: Change this again when implementing RFC 2762
+   * for (i = 0; i < 32; i++)
+   */
+  for (i = 0; i < 1; i++)
     g_hash_table_destroy (sess->ssrcs[i]);
 
   g_mutex_clear (&sess->lock);
@@ -1409,8 +1455,8 @@ session_update_ptp (RTPSession * sess)
    * of each non-internal (=remotes) source have to be compared
    * to each other.
    */
-  gboolean is_doing_rtp_ptp = FALSE;
-  gboolean is_doing_rtcp_ptp = FALSE;
+  gboolean is_doing_rtp_ptp;
+  gboolean is_doing_rtcp_ptp;
   CompareAddrData data;
 
   /* compare the first remote source's ip addr that receive rtp packets
@@ -1526,7 +1572,8 @@ obtain_source (RTPSession * sess, guint32 ssrc, gboolean * created,
 /* must be called with the session lock, the returned source needs to be
  * unreffed after usage. */
 static RTPSource *
-obtain_internal_source (RTPSession * sess, guint32 ssrc, gboolean * created)
+obtain_internal_source (RTPSession * sess, guint32 ssrc, gboolean * created,
+    GstClockTime current_time)
 {
   RTPSource *source;
 
@@ -1548,6 +1595,11 @@ obtain_internal_source (RTPSession * sess, guint32 ssrc, gboolean * created)
   } else {
     *created = FALSE;
   }
+  /* update last activity */
+  if (current_time != GST_CLOCK_TIME_NONE) {
+    source->last_activity = current_time;
+    source->last_rtp_activity = current_time;
+  }
   g_object_ref (source);
 
   return source;
@@ -2305,7 +2357,7 @@ rtp_session_request_local_key_unit (RTPSession * sess, RTPSource * src,
           "RTT (%" GST_TIME_FORMAT " < %" GST_TIME_FORMAT ")",
           fir ? "FIR" : "PLI",
           GST_TIME_ARGS (current_time - sess->last_keyframe_request),
-          GST_TIME_ARGS (round_trip_in_ns));;
+          GST_TIME_ARGS (round_trip_in_ns));
       return FALSE;
     }
   }
@@ -2338,6 +2390,8 @@ rtp_session_process_pli (RTPSession * sess, guint32 sender_ssrc,
     return;
 
   rtp_session_request_local_key_unit (sess, src, FALSE, current_time);
+
+  src->stats.recv_pli_count++;
 }
 
 static void
@@ -2394,6 +2448,7 @@ rtp_session_process_fir (RTPSession * sess, guint32 sender_ssrc,
     return;
 
   rtp_session_request_local_key_unit (sess, src, TRUE, current_time);
+  src->stats.recv_fir_count++;
 }
 
 static void
@@ -2532,6 +2587,9 @@ rtp_session_process_rtcp (RTPSession * sess, GstBuffer * buffer,
 
   GST_DEBUG ("received RTCP packet");
 
+  g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_RECEIVING_RTCP], 0,
+      buffer);
+
   RTP_SESSION_LOCK (sess);
   /* update pinfo stats */
   update_packet_info (sess, &pinfo, FALSE, FALSE, FALSE, buffer, current_time,
@@ -2637,11 +2695,20 @@ rtp_session_update_send_caps (RTPSession * sess, GstCaps * caps)
     gboolean created;
 
     RTP_SESSION_LOCK (sess);
-    source = obtain_internal_source (sess, ssrc, &created);
+    source = obtain_internal_source (sess, ssrc, &created, GST_CLOCK_TIME_NONE);
     if (source) {
       rtp_source_update_caps (source, caps);
       g_object_unref (source);
     }
+
+    if (gst_structure_get_uint (s, "rtx-ssrc", &ssrc)) {
+      source =
+          obtain_internal_source (sess, ssrc, &created, GST_CLOCK_TIME_NONE);
+      if (source) {
+        rtp_source_update_caps (source, caps);
+        g_object_unref (source);
+      }
+    }
     RTP_SESSION_UNLOCK (sess);
   }
 }
@@ -2680,10 +2747,7 @@ rtp_session_send_rtp (RTPSession * sess, gpointer data, gboolean is_list,
           current_time, running_time, -1))
     goto invalid_packet;
 
-  source = obtain_internal_source (sess, pinfo.ssrc, &created);
-
-  /* update last activity */
-  source->last_rtp_activity = current_time;
+  source = obtain_internal_source (sess, pinfo.ssrc, &created, current_time);
 
   prevsender = RTP_SOURCE_IS_SENDER (source);
   oldrate = source->bitrate;
@@ -2737,9 +2801,8 @@ calculate_rtcp_interval (RTPSession * sess, gboolean deterministic,
 
       g_hash_table_foreach (sess->ssrcs[sess->mask_idx],
           (GHFunc) add_bitrates, &bandwidth);
-      bandwidth /= 8.0;
     }
-    if (bandwidth < 8000)
+    if (bandwidth < RTP_STATS_BANDWIDTH)
       bandwidth = RTP_STATS_BANDWIDTH;
 
     rtp_stats_set_bandwidths (&sess->stats, bandwidth,
@@ -2849,7 +2912,7 @@ done:
 GstFlowReturn
 rtp_session_schedule_bye (RTPSession * sess, GstClockTime current_time)
 {
-  GstFlowReturn result = GST_FLOW_OK;
+  GstFlowReturn result;
 
   g_return_val_if_fail (RTP_IS_SESSION (sess), GST_FLOW_ERROR);
 
@@ -3088,6 +3151,7 @@ session_add_fir (const gchar * key, RTPSource * source, ReportData * data)
   fci_data[1] = fci_data[2] = fci_data[3] = 0;
 
   source->send_fir = FALSE;
+  source->stats.sent_fir_count++;
 }
 
 static void
@@ -3156,6 +3220,8 @@ session_pli (const gchar * key, RTPSource * source, ReportData * data)
 
   source->send_pli = FALSE;
   data->may_suppress = FALSE;
+
+  source->stats.sent_pli_count++;
 }
 
 /* construct NACK */
@@ -3258,6 +3324,11 @@ session_cleanup (const gchar * key, RTPSource * source, ReportData * data)
     }
   }
 
+  if (source->internal && source->sent_bye) {
+    GST_DEBUG ("removing internal source that has sent BYE %08x", source->ssrc);
+    remove = TRUE;
+  }
+
   /* sources that were inactive for more than 5 times the deterministic reporting
    * interval get timed out. the min timeout is 5 seconds. */
   /* mind old time that might pre-date last time going to PLAYING */
@@ -3290,16 +3361,9 @@ session_cleanup (const gchar * key, RTPSource * source, ReportData * data)
     if (data->current_time > btime) {
       interval = MAX (binterval * 2, 5 * GST_SECOND);
       if (data->current_time - btime > interval) {
-        if (source->internal && source->sent_bye) {
-          /* an internal source is BYE and stopped sending RTP, remove */
-          GST_DEBUG ("internal BYE source %08x timed out, last %"
-              GST_TIME_FORMAT, source->ssrc, GST_TIME_ARGS (btime));
-          remove = TRUE;
-        } else {
-          GST_DEBUG ("sender source %08x timed out and became receiver, last %"
-              GST_TIME_FORMAT, source->ssrc, GST_TIME_ARGS (btime));
-          sendertimeout = TRUE;
-        }
+        GST_DEBUG ("sender source %08x timed out and became receiver, last %"
+            GST_TIME_FORMAT, source->ssrc, GST_TIME_ARGS (btime));
+        sendertimeout = TRUE;
       }
     }
   }
@@ -3424,7 +3488,7 @@ make_source_bye (RTPSession * sess, RTPSource * source, ReportData * data)
 static gboolean
 is_rtcp_time (RTPSession * sess, GstClockTime current_time, ReportData * data)
 {
-  GstClockTime new_send_time, elapsed;
+  GstClockTime new_send_time;
   GstClockTime interval;
   RTPSessionStats *stats;
 
@@ -3455,21 +3519,33 @@ is_rtcp_time (RTPSession * sess, GstClockTime current_time, ReportData * data)
   }
 
 early:
-  /* get elapsed time since we last reported */
-  elapsed = current_time - sess->last_rtcp_send_time;
 
   /* take interval and add jitter */
   interval = data->interval;
   if (interval != GST_CLOCK_TIME_NONE)
     interval = rtp_stats_add_rtcp_jitter (stats, interval);
 
-  /* perform forward reconsideration */
-  if (interval != GST_CLOCK_TIME_NONE) {
-    GST_DEBUG ("forward reconsideration %" GST_TIME_FORMAT ", elapsed %"
-        GST_TIME_FORMAT, GST_TIME_ARGS (interval), GST_TIME_ARGS (elapsed));
-    new_send_time = interval + sess->last_rtcp_send_time;
+  if (sess->last_rtcp_send_time != GST_CLOCK_TIME_NONE) {
+    /* perform forward reconsideration */
+    if (interval != GST_CLOCK_TIME_NONE) {
+      GstClockTime elapsed;
+
+      /* get elapsed time since we last reported */
+      elapsed = current_time - sess->last_rtcp_send_time;
+
+      GST_DEBUG ("forward reconsideration %" GST_TIME_FORMAT ", elapsed %"
+          GST_TIME_FORMAT, GST_TIME_ARGS (interval), GST_TIME_ARGS (elapsed));
+      new_send_time = interval + sess->last_rtcp_send_time;
+    } else {
+      new_send_time = sess->last_rtcp_send_time;
+    }
   } else {
-    new_send_time = sess->last_rtcp_send_time;
+    /* If this is the first RTCP packet, we can reconsider anything based
+     * on the last RTCP send time because there was none.
+     */
+    g_warn_if_fail (!data->is_early);
+    data->is_early = FALSE;
+    new_send_time = current_time;
   }
 
   if (!data->is_early) {
@@ -3661,7 +3737,8 @@ rtp_session_on_timeout (RTPSession * sess, GstClockTime current_time,
     RTPSource *source;
     gboolean created;
 
-    source = obtain_internal_source (sess, sess->suggested_ssrc, &created);
+    source = obtain_internal_source (sess, sess->suggested_ssrc, &created,
+        current_time);
     g_object_unref (source);
   }
 
@@ -3710,6 +3787,11 @@ rtp_session_on_timeout (RTPSession * sess, GstClockTime current_time,
   sess->next_early_rtcp_time = GST_CLOCK_TIME_NONE;
   sess->scheduled_bye = FALSE;
 
+  /*  RFC 4585 section 3.5.2 step 6 */
+  if (!data.is_early) {
+    sess->allow_early = TRUE;
+  }
+
 done:
   RTP_SESSION_UNLOCK (sess);
 
@@ -3755,12 +3837,15 @@ done:
  * @max_delay: maximum delay
  *
  * Request transmission of early RTCP
+ *
+ * Returns: %TRUE if the related RTCP can be scheduled.
  */
-void
+gboolean
 rtp_session_request_early_rtcp (RTPSession * sess, GstClockTime current_time,
     GstClockTime max_delay)
 {
-  GstClockTime T_dither_max;
+  GstClockTime T_dither_max, T_rr;
+  gboolean ret;
 
   /* Implements the algorithm described in RFC 4585 section 3.5.2 */
 
@@ -3770,23 +3855,44 @@ rtp_session_request_early_rtcp (RTPSession * sess, GstClockTime current_time,
   /*  RFC 4585 section 3.5.2 step 2 */
   if (GST_CLOCK_TIME_IS_VALID (sess->next_early_rtcp_time)) {
     GST_LOG_OBJECT (sess, "already have next early rtcp time");
-    goto dont_send;
+    ret = TRUE;
+    goto end;
   }
 
   if (!GST_CLOCK_TIME_IS_VALID (sess->next_rtcp_check_time)) {
     GST_LOG_OBJECT (sess, "no next RTCP check time");
-    goto dont_send;
+    ret = FALSE;
+    goto end;
   }
 
-  /* Ignore the request a scheduled packet will be in time anyway */
-  if (current_time + max_delay > sess->next_rtcp_check_time) {
-    GST_LOG_OBJECT (sess, "next scheduled time is soon %" GST_TIME_FORMAT " + %"
-        GST_TIME_FORMAT " > %" GST_TIME_FORMAT,
-        GST_TIME_ARGS (current_time),
-        GST_TIME_ARGS (max_delay), GST_TIME_ARGS (sess->next_rtcp_check_time));
-    goto dont_send;
+  /* RFC 4585 section 3.5.3 step 1
+   * If no regular RTCP packet has been sent before, then a regular
+   * RTCP packet has to be scheduled first and FB messages might be
+   * included there
+   */
+  if (!GST_CLOCK_TIME_IS_VALID (sess->last_rtcp_send_time)) {
+    GST_LOG_OBJECT (sess, "no RTCP sent yet");
+
+    if (current_time + max_delay > sess->next_rtcp_check_time) {
+      GST_LOG_OBJECT (sess,
+          "next scheduled time is soon %" GST_TIME_FORMAT " + %" GST_TIME_FORMAT
+          " > %" GST_TIME_FORMAT, GST_TIME_ARGS (current_time),
+          GST_TIME_ARGS (max_delay),
+          GST_TIME_ARGS (sess->next_rtcp_check_time));
+      ret = TRUE;
+    } else {
+      GST_LOG_OBJECT (sess,
+          "can't allow early feedback, next scheduled time is too late %"
+          GST_TIME_FORMAT " + %" GST_TIME_FORMAT " < %" GST_TIME_FORMAT,
+          GST_TIME_ARGS (current_time), GST_TIME_ARGS (max_delay),
+          GST_TIME_ARGS (sess->next_rtcp_check_time));
+      ret = FALSE;
+    }
+    goto end;
   }
 
+  T_rr = sess->next_rtcp_check_time - sess->last_rtcp_send_time;
+
   /*  RFC 4585 section 3.5.2 step 2b */
   /* If the total sources is <=2, then there is only us and one peer */
   /* When there is one auxiliary stream the session can still do point
@@ -3796,27 +3902,43 @@ rtp_session_request_early_rtcp (RTPSession * sess, GstClockTime current_time,
     T_dither_max = 0;
   } else {
     /* Divide by 2 because l = 0.5 */
-    T_dither_max = sess->next_rtcp_check_time - sess->last_rtcp_send_time;
+    T_dither_max = T_rr;
     T_dither_max /= 2;
   }
 
   /*  RFC 4585 section 3.5.2 step 3 */
   if (current_time + T_dither_max > sess->next_rtcp_check_time) {
-    GST_LOG_OBJECT (sess, "don't send because of dither");
-    goto dont_send;
-  }
-
-  /*  RFC 4585 section 3.5.2 step 4
-   * Don't send if allow_early is FALSE, but not if we are in
-   * immediate mode, meaning we are part of a group of at most the
-   * application-specific threshold.
-   */
-  if (sess->total_sources > sess->rtcp_immediate_feedback_threshold &&
-      sess->allow_early == FALSE) {
-    GST_LOG_OBJECT (sess, "can't allow early feedback");
-    goto dont_send;
+    GST_LOG_OBJECT (sess,
+        "don't send because of dither, next scheduled time is soon %"
+        GST_TIME_FORMAT " + %" GST_TIME_FORMAT " > %" GST_TIME_FORMAT,
+        GST_TIME_ARGS (current_time), GST_TIME_ARGS (T_dither_max),
+        GST_TIME_ARGS (sess->next_rtcp_check_time));
+    ret = TRUE;
+    goto end;
+  }
+
+  /*  RFC 4585 section 3.5.2 step 4a */
+  if (sess->allow_early == FALSE) {
+    /* Ignore the request a scheduled packet will be in time anyway */
+    if (current_time + max_delay > sess->next_rtcp_check_time) {
+      GST_LOG_OBJECT (sess,
+          "next scheduled time is soon %" GST_TIME_FORMAT " + %" GST_TIME_FORMAT
+          " > %" GST_TIME_FORMAT, GST_TIME_ARGS (current_time),
+          GST_TIME_ARGS (max_delay),
+          GST_TIME_ARGS (sess->next_rtcp_check_time));
+      ret = TRUE;
+    } else {
+      GST_LOG_OBJECT (sess,
+          "can't allow early feedback, next scheduled time is too late %"
+          GST_TIME_FORMAT " + %" GST_TIME_FORMAT " < %" GST_TIME_FORMAT,
+          GST_TIME_ARGS (current_time), GST_TIME_ARGS (max_delay),
+          GST_TIME_ARGS (sess->next_rtcp_check_time));
+      ret = FALSE;
+    }
+    goto end;
   }
 
+  /*  RFC 4585 section 3.5.2 step 4b */
   if (T_dither_max) {
     /* Schedule an early transmission later */
     sess->next_early_rtcp_time = g_random_double () * T_dither_max +
@@ -3826,8 +3948,18 @@ rtp_session_request_early_rtcp (RTPSession * sess, GstClockTime current_time,
     sess->next_early_rtcp_time = current_time;
   }
 
-  GST_LOG_OBJECT (sess, "next early RTCP time %" GST_TIME_FORMAT,
-      GST_TIME_ARGS (sess->next_early_rtcp_time));
+  /*  RFC 4585 section 3.5.2 step 6 */
+  sess->allow_early = FALSE;
+  /* Delay next regular RTCP packet to not exceed the short-term
+   * RTCP bandwidth when using early feedback as compared to
+   * without */
+  sess->next_rtcp_check_time = sess->last_rtcp_send_time + 2 * T_rr;
+  sess->last_rtcp_send_time += T_rr;
+
+  GST_LOG_OBJECT (sess, "next early RTCP time %" GST_TIME_FORMAT
+      ", next regular RTCP time %" GST_TIME_FORMAT,
+      GST_TIME_ARGS (sess->next_early_rtcp_time),
+      GST_TIME_ARGS (sess->next_rtcp_check_time));
   RTP_SESSION_UNLOCK (sess);
 
   /* notify app of need to send packet early
@@ -3835,24 +3967,26 @@ rtp_session_request_early_rtcp (RTPSession * sess, GstClockTime current_time,
   if (sess->callbacks.reconsider)
     sess->callbacks.reconsider (sess, sess->reconsider_user_data);
 
-  return;
+  return TRUE;
 
-dont_send:
+end:
 
   RTP_SESSION_UNLOCK (sess);
+
+  return ret;
 }
 
-static void
+static gboolean
 rtp_session_send_rtcp (RTPSession * sess, GstClockTime max_delay)
 {
   GstClockTime now;
 
   if (!sess->callbacks.send_rtcp)
-    return;
+    return FALSE;
 
   now = sess->callbacks.request_time (sess, sess->request_time_user_data);
 
-  rtp_session_request_early_rtcp (sess, now, max_delay);
+  return rtp_session_request_early_rtcp (sess, now, max_delay);
 }
 
 gboolean
@@ -3861,6 +3995,11 @@ rtp_session_request_key_unit (RTPSession * sess, guint32 ssrc,
 {
   RTPSource *src;
 
+  if (!rtp_session_send_rtcp (sess, 5 * GST_SECOND)) {
+    GST_DEBUG ("FIR/PLI not sent");
+    return FALSE;
+  }
+
   RTP_SESSION_LOCK (sess);
   src = find_source (sess, ssrc);
   if (src == NULL)
@@ -3878,8 +4017,6 @@ rtp_session_request_key_unit (RTPSession * sess, guint32 ssrc,
   }
   RTP_SESSION_UNLOCK (sess);
 
-  rtp_session_send_rtcp (sess, 200 * GST_MSECOND);
-
   return TRUE;
 
   /* ERRORS */
@@ -3907,6 +4044,11 @@ rtp_session_request_nack (RTPSession * sess, guint32 ssrc, guint16 seqnum,
 {
   RTPSource *source;
 
+  if (!rtp_session_send_rtcp (sess, max_delay)) {
+    GST_DEBUG ("NACK not sent");
+    return FALSE;
+  }
+
   RTP_SESSION_LOCK (sess);
   source = find_source (sess, ssrc);
   if (source == NULL)
@@ -3916,8 +4058,6 @@ rtp_session_request_nack (RTPSession * sess, guint32 ssrc, guint16 seqnum,
   rtp_source_register_nack (source, seqnum);
   RTP_SESSION_UNLOCK (sess);
 
-  rtp_session_send_rtcp (sess, max_delay);
-
   return TRUE;
 
   /* ERRORS */