#include "rtpstats.h"
void
gst_rtp_packet_rate_ctx_reset (RTPPacketRateCtx * ctx, gint32 clock_rate)
{
ctx->clock_rate = clock_rate;
ctx->probed = FALSE;
ctx->avg_packet_rate = -1;
ctx->last_ts = -1;
}
guint32
gst_rtp_packet_rate_ctx_update (RTPPacketRateCtx * ctx, guint16 seqnum,
guint32 ts)
{
guint64 new_ts, diff_ts;
gint diff_seqnum;
gint32 new_packet_rate;
if (ctx->clock_rate <= 0) {
return ctx->avg_packet_rate;
}
new_ts = ctx->last_ts;
gst_rtp_buffer_ext_timestamp (&new_ts, ts);
if (!ctx->probed) {
ctx->probed = TRUE;
goto done;
}
diff_seqnum = gst_rtp_buffer_compare_seqnum (ctx->last_seqnum, seqnum);
if (diff_seqnum <= 0 || new_ts <= ctx->last_ts || diff_seqnum > 1) {
goto done;
}
diff_ts = new_ts - ctx->last_ts;
diff_ts = gst_util_uint64_scale_int (diff_ts, GST_SECOND, ctx->clock_rate);
new_packet_rate = gst_util_uint64_scale (diff_seqnum, GST_SECOND, diff_ts);
if ((gint32) ctx->avg_packet_rate > new_packet_rate) {
ctx->avg_packet_rate = (7 * ctx->avg_packet_rate + new_packet_rate + 7) / 8;
} else {
ctx->avg_packet_rate = (ctx->avg_packet_rate + new_packet_rate + 1) / 2;
}
done:
ctx->last_seqnum = seqnum;
ctx->last_ts = new_ts;
return ctx->avg_packet_rate;
}
guint32
gst_rtp_packet_rate_ctx_get (RTPPacketRateCtx * ctx)
{
return ctx->avg_packet_rate;
}
guint32
gst_rtp_packet_rate_ctx_get_max_dropout (RTPPacketRateCtx * ctx, gint32 time_ms)
{
if (time_ms <= 0 || !ctx->probed || ctx->avg_packet_rate == G_MAXUINT32) {
return RTP_DEF_DROPOUT;
}
return MAX (RTP_MIN_DROPOUT, ctx->avg_packet_rate * time_ms / 1000);
}
guint32
gst_rtp_packet_rate_ctx_get_max_misorder (RTPPacketRateCtx * ctx,
gint32 time_ms)
{
if (time_ms <= 0 || !ctx->probed || ctx->avg_packet_rate == G_MAXUINT32) {
return RTP_DEF_MISORDER;
}
return MAX (RTP_MIN_MISORDER, ctx->avg_packet_rate * time_ms / 1000);
}
void
rtp_stats_init_defaults (RTPSessionStats * stats)
{
rtp_stats_set_bandwidths (stats, -1, -1, -1, -1);
stats->min_interval = RTP_STATS_MIN_INTERVAL;
stats->bye_timeout = RTP_STATS_BYE_TIMEOUT;
stats->nacks_dropped = 0;
stats->nacks_sent = 0;
stats->nacks_received = 0;
}
void
rtp_stats_set_bandwidths (RTPSessionStats * stats, guint rtp_bw,
gdouble rtcp_bw, guint rs, guint rr)
{
GST_DEBUG ("recalc bandwidths: RTP %u, RTCP %f, RS %u, RR %u", rtp_bw,
rtcp_bw, rs, rr);
if (rs != G_MAXUINT && rr != G_MAXUINT)
rtcp_bw = rs + rr;
if (rtcp_bw > 0.0 && rtcp_bw < 1.0) {
if (rtp_bw > 0.0)
rtcp_bw = rtp_bw * rtcp_bw;
else
rtcp_bw = -1.0;
}
if (rtp_bw == G_MAXUINT && rtcp_bw > 1.0)
rtp_bw = rtcp_bw * 20;
else if (rtp_bw != G_MAXUINT && rtcp_bw < 0.0)
rtcp_bw = rtp_bw / 20;
else if (rtp_bw == G_MAXUINT && rtcp_bw < 0.0) {
rtp_bw = RTP_STATS_BANDWIDTH;
rtcp_bw = rtp_bw * RTP_STATS_RTCP_FRACTION;
}
stats->bandwidth = rtp_bw;
stats->rtcp_bandwidth = rtcp_bw;
if (rs == G_MAXUINT) {
if (rr == G_MAXUINT) {
rs = stats->rtcp_bandwidth * RTP_STATS_SENDER_FRACTION;
rr = stats->rtcp_bandwidth * RTP_STATS_RECEIVER_FRACTION;
} else {
if (stats->rtcp_bandwidth > rr)
rs = stats->rtcp_bandwidth - rr;
else
rs = 0;
}
} else if (rr == G_MAXUINT) {
if (stats->rtcp_bandwidth > rs)
rr = stats->rtcp_bandwidth - rs;
else
rr = 0;
}
if (stats->rtcp_bandwidth > 0) {
stats->sender_fraction = ((gdouble) rs) / ((gdouble) stats->rtcp_bandwidth);
stats->receiver_fraction = 1.0 - stats->sender_fraction;
} else {
stats->sender_fraction = 0.0;
stats->receiver_fraction = 0.0;
}
GST_DEBUG ("bandwidths: RTP %u, RTCP %u, RS %f, RR %f", stats->bandwidth,
stats->rtcp_bandwidth, stats->sender_fraction, stats->receiver_fraction);
}
GstClockTime
rtp_stats_calculate_rtcp_interval (RTPSessionStats * stats, gboolean we_send,
GstRTPProfile profile, gboolean ptp, gboolean first)
{
gdouble members, senders, n;
gdouble avg_rtcp_size, rtcp_bw;
gdouble interval;
gdouble rtcp_min_time;
if (profile == GST_RTP_PROFILE_AVPF || profile == GST_RTP_PROFILE_SAVPF) {
if (first && !ptp)
rtcp_min_time = 1.0;
else
rtcp_min_time = 0.0;
} else {
rtcp_min_time = stats->min_interval;
if (first)
rtcp_min_time /= 2.0;
}
n = members = stats->active_sources;
senders = (gdouble) stats->sender_sources;
rtcp_bw = stats->rtcp_bandwidth;
if (senders <= members * stats->sender_fraction) {
if (we_send) {
rtcp_bw *= stats->sender_fraction;
n = senders;
} else {
rtcp_bw *= stats->receiver_fraction;
n -= senders;
}
}
if (rtcp_bw <= 0.0001)
return GST_CLOCK_TIME_NONE;
avg_rtcp_size = 8.0 * stats->avg_rtcp_packet_size;
GST_DEBUG ("avg size %f, n %f, rtcp_bw %f", avg_rtcp_size, n, rtcp_bw);
interval = avg_rtcp_size * n / rtcp_bw;
if (interval < rtcp_min_time)
interval = rtcp_min_time;
return interval * GST_SECOND;
}
GstClockTime
rtp_stats_add_rtcp_jitter (RTPSessionStats * stats G_GNUC_UNUSED,
GstClockTime interval)
{
gdouble temp;
#define COMPENSATION (2.71828 - 1.5);
temp = (interval * g_random_double_range (0.5, 1.5)) / COMPENSATION;
return (GstClockTime) temp;
}
GstClockTime
rtp_stats_calculate_bye_interval (RTPSessionStats * stats)
{
gdouble members;
gdouble avg_rtcp_size, rtcp_bw;
gdouble interval;
gdouble rtcp_min_time;
if (stats->active_sources < 50)
return 0;
rtcp_min_time = (stats->min_interval) / 2.0;
members = stats->bye_members;
rtcp_bw = stats->rtcp_bandwidth * stats->receiver_fraction;
if (rtcp_bw <= 0.0001)
return GST_CLOCK_TIME_NONE;
avg_rtcp_size = 8.0 * stats->avg_rtcp_packet_size;
interval = avg_rtcp_size * members / rtcp_bw;
if (interval < rtcp_min_time)
interval = rtcp_min_time;
return interval * GST_SECOND;
}
gint64
rtp_stats_get_packets_lost (const RTPSourceStats * stats)
{
gint64 lost;
guint64 extended_max, expected;
extended_max = stats->cycles + stats->max_seq;
expected = extended_max - stats->base_seq + 1;
lost = expected - stats->packets_received;
return lost;
}
void
rtp_stats_set_min_interval (RTPSessionStats * stats, gdouble min_interval)
{
stats->min_interval = min_interval;
}
gboolean
__g_socket_address_equal (GSocketAddress * a, GSocketAddress * b)
{
GInetSocketAddress *ia, *ib;
GInetAddress *iaa, *iab;
ia = G_INET_SOCKET_ADDRESS (a);
ib = G_INET_SOCKET_ADDRESS (b);
if (g_inet_socket_address_get_port (ia) !=
g_inet_socket_address_get_port (ib))
return FALSE;
iaa = g_inet_socket_address_get_address (ia);
iab = g_inet_socket_address_get_address (ib);
return g_inet_address_equal (iaa, iab);
}
gchar *
__g_socket_address_to_string (GSocketAddress * addr)
{
GInetSocketAddress *ia;
gchar *ret, *tmp;
ia = G_INET_SOCKET_ADDRESS (addr);
tmp = g_inet_address_to_string (g_inet_socket_address_get_address (ia));
ret = g_strdup_printf ("%s:%u", tmp, g_inet_socket_address_get_port (ia));
g_free (tmp);
return ret;
}