Blame gst/rtpmanager/rtpstats.c

Packit 8ff292
/* GStreamer
Packit 8ff292
 * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
Packit 8ff292
 * Copyright (C)  2015 Kurento (http://kurento.org/)
Packit 8ff292
 *   @author: Miguel ParĂ­s <mparisdiaz@gmail.com>
Packit 8ff292
 *
Packit 8ff292
 * This library is free software; you can redistribute it and/or
Packit 8ff292
 * modify it under the terms of the GNU Library General Public
Packit 8ff292
 * License as published by the Free Software Foundation; either
Packit 8ff292
 * version 2 of the License, or (at your option) any later version.
Packit 8ff292
 *
Packit 8ff292
 * This library is distributed in the hope that it will be useful,
Packit 8ff292
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 8ff292
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 8ff292
 * Library General Public License for more details.
Packit 8ff292
 *
Packit 8ff292
 * You should have received a copy of the GNU Library General Public
Packit 8ff292
 * License along with this library; if not, write to the
Packit 8ff292
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
Packit 8ff292
 * Boston, MA 02110-1301, USA.
Packit 8ff292
 */
Packit 8ff292
Packit 8ff292
#include "rtpstats.h"
Packit 8ff292
Packit 8ff292
void
Packit 8ff292
gst_rtp_packet_rate_ctx_reset (RTPPacketRateCtx * ctx, gint32 clock_rate)
Packit 8ff292
{
Packit 8ff292
  ctx->clock_rate = clock_rate;
Packit 8ff292
  ctx->probed = FALSE;
Packit 8ff292
  ctx->avg_packet_rate = -1;
Packit 8ff292
  ctx->last_ts = -1;
Packit 8ff292
}
Packit 8ff292
Packit 8ff292
guint32
Packit 8ff292
gst_rtp_packet_rate_ctx_update (RTPPacketRateCtx * ctx, guint16 seqnum,
Packit 8ff292
    guint32 ts)
Packit 8ff292
{
Packit 8ff292
  guint64 new_ts, diff_ts;
Packit 8ff292
  gint diff_seqnum;
Packit 8ff292
  gint32 new_packet_rate;
Packit 8ff292
Packit 8ff292
  if (ctx->clock_rate <= 0) {
Packit 8ff292
    return ctx->avg_packet_rate;
Packit 8ff292
  }
Packit 8ff292
Packit 8ff292
  new_ts = ctx->last_ts;
Packit 8ff292
  gst_rtp_buffer_ext_timestamp (&new_ts, ts);
Packit 8ff292
Packit 8ff292
  if (!ctx->probed) {
Packit 8ff292
    ctx->last_seqnum = seqnum;
Packit 8ff292
    ctx->last_ts = new_ts;
Packit 8ff292
    ctx->probed = TRUE;
Packit 8ff292
    return ctx->avg_packet_rate;
Packit 8ff292
  }
Packit 8ff292
Packit 8ff292
  diff_seqnum = gst_rtp_buffer_compare_seqnum (ctx->last_seqnum, seqnum);
Packit 8ff292
  if (diff_seqnum <= 0 || new_ts <= ctx->last_ts) {
Packit 8ff292
    return ctx->avg_packet_rate;
Packit 8ff292
  }
Packit 8ff292
Packit 8ff292
  diff_ts = new_ts - ctx->last_ts;
Packit 8ff292
  diff_ts = gst_util_uint64_scale_int (diff_ts, GST_SECOND, ctx->clock_rate);
Packit 8ff292
  new_packet_rate = gst_util_uint64_scale (diff_seqnum, GST_SECOND, diff_ts);
Packit 8ff292
Packit 8ff292
  /* The goal is that higher packet rates "win".
Packit 8ff292
   * If there's a sudden burst, the average will go up fast,
Packit 8ff292
   * but it will go down again slowly.
Packit 8ff292
   * This is useful for bursty cases, where a lot of packets are close
Packit 8ff292
   * to each other and should allow a higher reorder/dropout there.
Packit 8ff292
   * Round up the new average.
Packit 8ff292
   */
Packit 8ff292
  if (ctx->avg_packet_rate > new_packet_rate) {
Packit 8ff292
    ctx->avg_packet_rate = (7 * ctx->avg_packet_rate + new_packet_rate + 7) / 8;
Packit 8ff292
  } else {
Packit 8ff292
    ctx->avg_packet_rate = (ctx->avg_packet_rate + new_packet_rate + 1) / 2;
Packit 8ff292
  }
Packit 8ff292
Packit 8ff292
  ctx->last_seqnum = seqnum;
Packit 8ff292
  ctx->last_ts = new_ts;
Packit 8ff292
Packit 8ff292
  return ctx->avg_packet_rate;
Packit 8ff292
}
Packit 8ff292
Packit 8ff292
guint32
Packit 8ff292
gst_rtp_packet_rate_ctx_get (RTPPacketRateCtx * ctx)
Packit 8ff292
{
Packit 8ff292
  return ctx->avg_packet_rate;
Packit 8ff292
}
Packit 8ff292
Packit 8ff292
guint32
Packit 8ff292
gst_rtp_packet_rate_ctx_get_max_dropout (RTPPacketRateCtx * ctx, gint32 time_ms)
Packit 8ff292
{
Packit 8ff292
  if (time_ms <= 0 || !ctx->probed) {
Packit 8ff292
    return RTP_DEF_DROPOUT;
Packit 8ff292
  }
Packit 8ff292
Packit 8ff292
  return MAX (RTP_MIN_DROPOUT, ctx->avg_packet_rate * time_ms / 1000);
Packit 8ff292
}
Packit 8ff292
Packit 8ff292
guint32
Packit 8ff292
gst_rtp_packet_rate_ctx_get_max_misorder (RTPPacketRateCtx * ctx,
Packit 8ff292
    gint32 time_ms)
Packit 8ff292
{
Packit 8ff292
  if (time_ms <= 0 || !ctx->probed) {
Packit 8ff292
    return RTP_DEF_MISORDER;
Packit 8ff292
  }
Packit 8ff292
Packit 8ff292
  return MAX (RTP_MIN_MISORDER, ctx->avg_packet_rate * time_ms / 1000);
Packit 8ff292
}
Packit 8ff292
Packit 8ff292
/**
Packit 8ff292
 * rtp_stats_init_defaults:
Packit 8ff292
 * @stats: an #RTPSessionStats struct
Packit 8ff292
 *
Packit 8ff292
 * Initialize @stats with its default values.
Packit 8ff292
 */
Packit 8ff292
void
Packit 8ff292
rtp_stats_init_defaults (RTPSessionStats * stats)
Packit 8ff292
{
Packit 8ff292
  rtp_stats_set_bandwidths (stats, -1, -1, -1, -1);
Packit 8ff292
  stats->min_interval = RTP_STATS_MIN_INTERVAL;
Packit 8ff292
  stats->bye_timeout = RTP_STATS_BYE_TIMEOUT;
Packit 8ff292
  stats->nacks_dropped = 0;
Packit 8ff292
  stats->nacks_sent = 0;
Packit 8ff292
  stats->nacks_received = 0;
Packit 8ff292
}
Packit 8ff292
Packit 8ff292
/**
Packit 8ff292
 * rtp_stats_set_bandwidths:
Packit 8ff292
 * @stats: an #RTPSessionStats struct
Packit 8ff292
 * @rtp_bw: RTP bandwidth
Packit 8ff292
 * @rtcp_bw: RTCP bandwidth
Packit 8ff292
 * @rs: sender RTCP bandwidth
Packit 8ff292
 * @rr: receiver RTCP bandwidth
Packit 8ff292
 *
Packit 8ff292
 * Configure the bandwidth parameters in the stats. When an input variable is
Packit 8ff292
 * set to -1, it will be calculated from the other input variables and from the
Packit 8ff292
 * defaults.
Packit 8ff292
 */
Packit 8ff292
void
Packit 8ff292
rtp_stats_set_bandwidths (RTPSessionStats * stats, guint rtp_bw,
Packit 8ff292
    gdouble rtcp_bw, guint rs, guint rr)
Packit 8ff292
{
Packit 8ff292
  GST_DEBUG ("recalc bandwidths: RTP %u, RTCP %f, RS %u, RR %u", rtp_bw,
Packit 8ff292
      rtcp_bw, rs, rr);
Packit 8ff292
Packit 8ff292
  /* when given, sender and receive bandwidth add up to the total
Packit 8ff292
   * rtcp bandwidth */
Packit 8ff292
  if (rs != -1 && rr != -1)
Packit 8ff292
    rtcp_bw = rs + rr;
Packit 8ff292
Packit 8ff292
  /* If rtcp_bw is between 0 and 1, it is a fraction of rtp_bw */
Packit 8ff292
  if (rtcp_bw > 0.0 && rtcp_bw < 1.0) {
Packit 8ff292
    if (rtp_bw > 0.0)
Packit 8ff292
      rtcp_bw = rtp_bw * rtcp_bw;
Packit 8ff292
    else
Packit 8ff292
      rtcp_bw = -1.0;
Packit 8ff292
  }
Packit 8ff292
Packit 8ff292
  /* RTCP is 5% of the RTP bandwidth */
Packit 8ff292
  if (rtp_bw == -1 && rtcp_bw > 1.0)
Packit 8ff292
    rtp_bw = rtcp_bw * 20;
Packit 8ff292
  else if (rtp_bw != -1 && rtcp_bw < 0.0)
Packit 8ff292
    rtcp_bw = rtp_bw / 20;
Packit 8ff292
  else if (rtp_bw == -1 && rtcp_bw < 0.0) {
Packit 8ff292
    /* nothing given, take defaults */
Packit 8ff292
    rtp_bw = RTP_STATS_BANDWIDTH;
Packit 8ff292
    rtcp_bw = rtp_bw * RTP_STATS_RTCP_FRACTION;
Packit 8ff292
  }
Packit 8ff292
Packit 8ff292
  stats->bandwidth = rtp_bw;
Packit 8ff292
  stats->rtcp_bandwidth = rtcp_bw;
Packit 8ff292
Packit 8ff292
  /* now figure out the fractions */
Packit 8ff292
  if (rs == -1) {
Packit 8ff292
    /* rs unknown */
Packit 8ff292
    if (rr == -1) {
Packit 8ff292
      /* both not given, use defaults */
Packit 8ff292
      rs = stats->rtcp_bandwidth * RTP_STATS_SENDER_FRACTION;
Packit 8ff292
      rr = stats->rtcp_bandwidth * RTP_STATS_RECEIVER_FRACTION;
Packit 8ff292
    } else {
Packit 8ff292
      /* rr known, calculate rs */
Packit 8ff292
      if (stats->rtcp_bandwidth > rr)
Packit 8ff292
        rs = stats->rtcp_bandwidth - rr;
Packit 8ff292
      else
Packit 8ff292
        rs = 0;
Packit 8ff292
    }
Packit 8ff292
  } else if (rr == -1) {
Packit 8ff292
    /* rs known, calculate rr */
Packit 8ff292
    if (stats->rtcp_bandwidth > rs)
Packit 8ff292
      rr = stats->rtcp_bandwidth - rs;
Packit 8ff292
    else
Packit 8ff292
      rr = 0;
Packit 8ff292
  }
Packit 8ff292
Packit 8ff292
  if (stats->rtcp_bandwidth > 0) {
Packit 8ff292
    stats->sender_fraction = ((gdouble) rs) / ((gdouble) stats->rtcp_bandwidth);
Packit 8ff292
    stats->receiver_fraction = 1.0 - stats->sender_fraction;
Packit 8ff292
  } else {
Packit 8ff292
    /* no RTCP bandwidth, set dummy values */
Packit 8ff292
    stats->sender_fraction = 0.0;
Packit 8ff292
    stats->receiver_fraction = 0.0;
Packit 8ff292
  }
Packit 8ff292
  GST_DEBUG ("bandwidths: RTP %u, RTCP %u, RS %f, RR %f", stats->bandwidth,
Packit 8ff292
      stats->rtcp_bandwidth, stats->sender_fraction, stats->receiver_fraction);
Packit 8ff292
}
Packit 8ff292
Packit 8ff292
/**
Packit 8ff292
 * rtp_stats_calculate_rtcp_interval:
Packit 8ff292
 * @stats: an #RTPSessionStats struct
Packit 8ff292
 * @sender: if we are a sender
Packit 8ff292
 * @profile: RTP profile of this session
Packit 8ff292
 * @ptp: if this session is a point-to-point session
Packit 8ff292
 * @first: if this is the first time
Packit 8ff292
 *
Packit 8ff292
 * Calculate the RTCP interval. The result of this function is the amount of
Packit 8ff292
 * time to wait (in nanoseconds) before sending a new RTCP message.
Packit 8ff292
 *
Packit 8ff292
 * Returns: the RTCP interval.
Packit 8ff292
 */
Packit 8ff292
GstClockTime
Packit 8ff292
rtp_stats_calculate_rtcp_interval (RTPSessionStats * stats, gboolean we_send,
Packit 8ff292
    GstRTPProfile profile, gboolean ptp, gboolean first)
Packit 8ff292
{
Packit 8ff292
  gdouble members, senders, n;
Packit 8ff292
  gdouble avg_rtcp_size, rtcp_bw;
Packit 8ff292
  gdouble interval;
Packit 8ff292
  gdouble rtcp_min_time;
Packit 8ff292
Packit 8ff292
  if (profile == GST_RTP_PROFILE_AVPF || profile == GST_RTP_PROFILE_SAVPF) {
Packit 8ff292
    /* RFC 4585 3.4d), 3.5.1 */
Packit 8ff292
Packit 8ff292
    if (first && !ptp)
Packit 8ff292
      rtcp_min_time = 1.0;
Packit 8ff292
    else
Packit 8ff292
      rtcp_min_time = 0.0;
Packit 8ff292
  } else {
Packit 8ff292
    /* Very first call at application start-up uses half the min
Packit 8ff292
     * delay for quicker notification while still allowing some time
Packit 8ff292
     * before reporting for randomization and to learn about other
Packit 8ff292
     * sources so the report interval will converge to the correct
Packit 8ff292
     * interval more quickly.
Packit 8ff292
     */
Packit 8ff292
    rtcp_min_time = stats->min_interval;
Packit 8ff292
    if (first)
Packit 8ff292
      rtcp_min_time /= 2.0;
Packit 8ff292
  }
Packit 8ff292
Packit 8ff292
  /* Dedicate a fraction of the RTCP bandwidth to senders unless
Packit 8ff292
   * the number of senders is large enough that their share is
Packit 8ff292
   * more than that fraction.
Packit 8ff292
   */
Packit 8ff292
  n = members = stats->active_sources;
Packit 8ff292
  senders = (gdouble) stats->sender_sources;
Packit 8ff292
  rtcp_bw = stats->rtcp_bandwidth;
Packit 8ff292
Packit 8ff292
  if (senders <= members * stats->sender_fraction) {
Packit 8ff292
    if (we_send) {
Packit 8ff292
      rtcp_bw *= stats->sender_fraction;
Packit 8ff292
      n = senders;
Packit 8ff292
    } else {
Packit 8ff292
      rtcp_bw *= stats->receiver_fraction;
Packit 8ff292
      n -= senders;
Packit 8ff292
    }
Packit 8ff292
  }
Packit 8ff292
Packit 8ff292
  /* no bandwidth for RTCP, return NONE to signal that we don't want to send
Packit 8ff292
   * RTCP packets */
Packit 8ff292
  if (rtcp_bw <= 0.0001)
Packit 8ff292
    return GST_CLOCK_TIME_NONE;
Packit 8ff292
Packit 8ff292
  avg_rtcp_size = 8.0 * stats->avg_rtcp_packet_size;
Packit 8ff292
  /*
Packit 8ff292
   * The effective number of sites times the average packet size is
Packit 8ff292
   * the total number of octets sent when each site sends a report.
Packit 8ff292
   * Dividing this by the effective bandwidth gives the time
Packit 8ff292
   * interval over which those packets must be sent in order to
Packit 8ff292
   * meet the bandwidth target, with a minimum enforced.  In that
Packit 8ff292
   * time interval we send one report so this time is also our
Packit 8ff292
   * average time between reports.
Packit 8ff292
   */
Packit 8ff292
  GST_DEBUG ("avg size %f, n %f, rtcp_bw %f", avg_rtcp_size, n, rtcp_bw);
Packit 8ff292
  interval = avg_rtcp_size * n / rtcp_bw;
Packit 8ff292
  if (interval < rtcp_min_time)
Packit 8ff292
    interval = rtcp_min_time;
Packit 8ff292
Packit 8ff292
  return interval * GST_SECOND;
Packit 8ff292
}
Packit 8ff292
Packit 8ff292
/**
Packit 8ff292
 * rtp_stats_add_rtcp_jitter:
Packit 8ff292
 * @stats: an #RTPSessionStats struct
Packit 8ff292
 * @interval: an RTCP interval
Packit 8ff292
 *
Packit 8ff292
 * Apply a random jitter to the @interval. @interval is typically obtained with
Packit 8ff292
 * rtp_stats_calculate_rtcp_interval().
Packit 8ff292
 *
Packit 8ff292
 * Returns: the new RTCP interval.
Packit 8ff292
 */
Packit 8ff292
GstClockTime
Packit 8ff292
rtp_stats_add_rtcp_jitter (RTPSessionStats * stats, GstClockTime interval)
Packit 8ff292
{
Packit 8ff292
  gdouble temp;
Packit 8ff292
Packit 8ff292
  /* see RFC 3550 p 30
Packit 8ff292
   * To compensate for "unconditional reconsideration" converging to a
Packit 8ff292
   * value below the intended average.
Packit 8ff292
   */
Packit 8ff292
#define COMPENSATION  (2.71828 - 1.5);
Packit 8ff292
Packit 8ff292
  temp = (interval * g_random_double_range (0.5, 1.5)) / COMPENSATION;
Packit 8ff292
Packit 8ff292
  return (GstClockTime) temp;
Packit 8ff292
}
Packit 8ff292
Packit 8ff292
Packit 8ff292
/**
Packit 8ff292
 * rtp_stats_calculate_bye_interval:
Packit 8ff292
 * @stats: an #RTPSessionStats struct
Packit 8ff292
 *
Packit 8ff292
 * Calculate the BYE interval. The result of this function is the amount of
Packit 8ff292
 * time to wait (in nanoseconds) before sending a BYE message.
Packit 8ff292
 *
Packit 8ff292
 * Returns: the BYE interval.
Packit 8ff292
 */
Packit 8ff292
GstClockTime
Packit 8ff292
rtp_stats_calculate_bye_interval (RTPSessionStats * stats)
Packit 8ff292
{
Packit 8ff292
  gdouble members;
Packit 8ff292
  gdouble avg_rtcp_size, rtcp_bw;
Packit 8ff292
  gdouble interval;
Packit 8ff292
  gdouble rtcp_min_time;
Packit 8ff292
Packit 8ff292
  /* no interval when we have less than 50 members */
Packit 8ff292
  if (stats->active_sources < 50)
Packit 8ff292
    return 0;
Packit 8ff292
Packit 8ff292
  rtcp_min_time = (stats->min_interval) / 2.0;
Packit 8ff292
Packit 8ff292
  /* Dedicate a fraction of the RTCP bandwidth to senders unless
Packit 8ff292
   * the number of senders is large enough that their share is
Packit 8ff292
   * more than that fraction.
Packit 8ff292
   */
Packit 8ff292
  members = stats->bye_members;
Packit 8ff292
  rtcp_bw = stats->rtcp_bandwidth * stats->receiver_fraction;
Packit 8ff292
Packit 8ff292
  /* no bandwidth for RTCP, return NONE to signal that we don't want to send
Packit 8ff292
   * RTCP packets */
Packit 8ff292
  if (rtcp_bw <= 0.0001)
Packit 8ff292
    return GST_CLOCK_TIME_NONE;
Packit 8ff292
Packit 8ff292
  avg_rtcp_size = 8.0 * stats->avg_rtcp_packet_size;
Packit 8ff292
  /*
Packit 8ff292
   * The effective number of sites times the average packet size is
Packit 8ff292
   * the total number of octets sent when each site sends a report.
Packit 8ff292
   * Dividing this by the effective bandwidth gives the time
Packit 8ff292
   * interval over which those packets must be sent in order to
Packit 8ff292
   * meet the bandwidth target, with a minimum enforced.  In that
Packit 8ff292
   * time interval we send one report so this time is also our
Packit 8ff292
   * average time between reports.
Packit 8ff292
   */
Packit 8ff292
  interval = avg_rtcp_size * members / rtcp_bw;
Packit 8ff292
  if (interval < rtcp_min_time)
Packit 8ff292
    interval = rtcp_min_time;
Packit 8ff292
Packit 8ff292
  return interval * GST_SECOND;
Packit 8ff292
}
Packit 8ff292
Packit 8ff292
/**
Packit 8ff292
 * rtp_stats_get_packets_lost:
Packit 8ff292
 * @stats: an #RTPSourceStats struct
Packit 8ff292
 *
Packit 8ff292
 * Calculate the total number of RTP packets lost since beginning of
Packit 8ff292
 * reception. Packets that arrive late are not considered lost, and
Packit 8ff292
 * duplicates are not taken into account. Hence, the loss may be negative
Packit 8ff292
 * if there are duplicates.
Packit 8ff292
 *
Packit 8ff292
 * Returns: total RTP packets lost.
Packit 8ff292
 */
Packit 8ff292
gint64
Packit 8ff292
rtp_stats_get_packets_lost (const RTPSourceStats * stats)
Packit 8ff292
{
Packit 8ff292
  gint64 lost;
Packit 8ff292
  guint64 extended_max, expected;
Packit 8ff292
Packit 8ff292
  extended_max = stats->cycles + stats->max_seq;
Packit 8ff292
  expected = extended_max - stats->base_seq + 1;
Packit 8ff292
  lost = expected - stats->packets_received;
Packit 8ff292
Packit 8ff292
  return lost;
Packit 8ff292
}
Packit 8ff292
Packit 8ff292
void
Packit 8ff292
rtp_stats_set_min_interval (RTPSessionStats * stats, gdouble min_interval)
Packit 8ff292
{
Packit 8ff292
  stats->min_interval = min_interval;
Packit 8ff292
}
Packit 8ff292
Packit 8ff292
gboolean
Packit 8ff292
__g_socket_address_equal (GSocketAddress * a, GSocketAddress * b)
Packit 8ff292
{
Packit 8ff292
  GInetSocketAddress *ia, *ib;
Packit 8ff292
  GInetAddress *iaa, *iab;
Packit 8ff292
Packit 8ff292
  ia = G_INET_SOCKET_ADDRESS (a);
Packit 8ff292
  ib = G_INET_SOCKET_ADDRESS (b);
Packit 8ff292
Packit 8ff292
  if (g_inet_socket_address_get_port (ia) !=
Packit 8ff292
      g_inet_socket_address_get_port (ib))
Packit 8ff292
    return FALSE;
Packit 8ff292
Packit 8ff292
  iaa = g_inet_socket_address_get_address (ia);
Packit 8ff292
  iab = g_inet_socket_address_get_address (ib);
Packit 8ff292
Packit 8ff292
  return g_inet_address_equal (iaa, iab);
Packit 8ff292
}
Packit 8ff292
Packit 8ff292
gchar *
Packit 8ff292
__g_socket_address_to_string (GSocketAddress * addr)
Packit 8ff292
{
Packit 8ff292
  GInetSocketAddress *ia;
Packit 8ff292
  gchar *ret, *tmp;
Packit 8ff292
Packit 8ff292
  ia = G_INET_SOCKET_ADDRESS (addr);
Packit 8ff292
Packit 8ff292
  tmp = g_inet_address_to_string (g_inet_socket_address_get_address (ia));
Packit 8ff292
  ret = g_strdup_printf ("%s:%u", tmp, g_inet_socket_address_get_port (ia));
Packit 8ff292
  g_free (tmp);
Packit 8ff292
Packit 8ff292
  return ret;
Packit 8ff292
}