+
+void
+rtp_session_request_early_rtcp (RTPSession * sess, GstClockTime current_time,
+ GstClockTimeDiff max_delay)
+{
+ GstClockTime T_dither_max;
+
+ /* Implements the algorithm described in RFC 4585 section 3.5.2 */
+
+ RTP_SESSION_LOCK (sess);
+
+ /* Check if already requested */
+ /* RFC 4585 section 3.5.2 step 2 */
+ if (GST_CLOCK_TIME_IS_VALID (sess->next_early_rtcp_time))
+ goto dont_send;
+
+ /* Ignore the request a scheduled packet will be in time anyway */
+ if (current_time + max_delay > sess->next_rtcp_check_time)
+ goto dont_send;
+
+ /* RFC 4585 section 3.5.2 step 2b */
+ /* If the total sources is <=2, then there is only us and one peer */
+ if (sess->total_sources <= 2) {
+ 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 /= 2;
+ }
+
+ /* RFC 4585 section 3.5.2 step 3 */
+ if (current_time + T_dither_max > sess->next_rtcp_check_time)
+ goto dont_send;
+
+ /* RFC 4585 section 3.5.2 step 4 */
+ if (sess->allow_early == FALSE)
+ goto dont_send;
+
+ if (T_dither_max) {
+ /* Schedule an early transmission later */
+ sess->next_early_rtcp_time = g_random_double () * T_dither_max +
+ current_time;
+ } else {
+ /* If no dithering, schedule it for NOW */
+ sess->next_early_rtcp_time = current_time;
+ }
+
+ RTP_SESSION_UNLOCK (sess);
+
+ /* notify app of need to send packet early
+ * and therefore of timeout change */
+ if (sess->callbacks.reconsider)
+ sess->callbacks.reconsider (sess, sess->reconsider_user_data);
+
+ return;
+
+dont_send:
+
+ RTP_SESSION_UNLOCK (sess);
+
+}
+
+void
+rtp_session_request_key_unit (RTPSession * sess, guint32 ssrc)
+{
+ guint i;
+
+ for (i = 0; i < sess->rtcp_pli_requests->len; i++)
+ if (ssrc == g_array_index (sess->rtcp_pli_requests, guint32, i))
+ return;
+
+ g_array_append_val (sess->rtcp_pli_requests, ssrc);
+}
+
+static gboolean
+has_pli_compare_func (gconstpointer a, gconstpointer ignored)
+{
+ GstRTCPPacket packet;
+
+ packet.buffer = (GstBuffer *) a;
+ packet.offset = 0;
+
+ if (gst_rtcp_packet_get_type (&packet) == GST_RTCP_TYPE_PSFB &&
+ gst_rtcp_packet_fb_get_type (&packet) == GST_RTCP_PSFB_TYPE_PLI)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static gboolean
+rtp_session_on_sending_rtcp (RTPSession * sess, GstBuffer * buffer,
+ gboolean early)
+{
+ gboolean ret = FALSE;
+
+ RTP_SESSION_LOCK (sess);
+
+ while (sess->rtcp_pli_requests->len) {
+ GstRTCPPacket rtcppacket;
+ guint media_ssrc = g_array_index (sess->rtcp_pli_requests, guint32, 0);
+ RTPSource *media_src = g_hash_table_lookup (sess->ssrcs[sess->mask_idx],
+ GUINT_TO_POINTER (media_ssrc));
+
+ if (media_src && !rtp_source_has_retained (media_src,
+ has_pli_compare_func, NULL)) {
+ if (gst_rtcp_buffer_add_packet (buffer, GST_RTCP_TYPE_PSFB, &rtcppacket)) {
+ gst_rtcp_packet_fb_set_type (&rtcppacket, GST_RTCP_PSFB_TYPE_PLI);
+ gst_rtcp_packet_fb_set_sender_ssrc (&rtcppacket,
+ rtp_source_get_ssrc (sess->source));
+ gst_rtcp_packet_fb_set_media_ssrc (&rtcppacket, media_ssrc);
+ ret = TRUE;
+ } else {
+ /* Break because the packet is full, will put next request in a
+ * further packet
+ */
+ break;
+ }
+ }
+
+ g_array_remove_index (sess->rtcp_pli_requests, 0);
+ }
+
+ RTP_SESSION_UNLOCK (sess);
+
+ return ret;
+}
+
+static void
+rtp_session_send_rtcp (RTPSession * sess, GstClockTimeDiff max_delay)
+{
+ GstClockTime now;
+
+ if (!sess->callbacks.send_rtcp)
+ return;
+
+ now = sess->callbacks.request_time (sess, sess->request_time_user_data);
+
+ rtp_session_request_early_rtcp (sess, now, max_delay);
+}