X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gst%2Frtpmanager%2Frtpstats.c;h=45fff3705851e3cd1166a2210cfc9f4ba3df381d;hb=deeb3be3ec26feef48f277d85bf55e816a228d4e;hp=8d1f5cfa1fa7a4bfd46b03d0e1c538715f7cc051;hpb=1f17b334ff2c31061dbeff0a5b42b77872cdb97e;p=platform%2Fupstream%2Fgst-plugins-good.git diff --git a/gst/rtpmanager/rtpstats.c b/gst/rtpmanager/rtpstats.c index 8d1f5cf..45fff37 100644 --- a/gst/rtpmanager/rtpstats.c +++ b/gst/rtpmanager/rtpstats.c @@ -1,5 +1,7 @@ /* GStreamer * Copyright (C) <2007> Wim Taymans + * Copyright (C) 2015 Kurento (http://kurento.org/) + * @author: Miguel París * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -13,11 +15,117 @@ * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. */ +#define GLIB_DISABLE_DEPRECATION_WARNINGS + #include "rtpstats.h" +#include "rtptwcc.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; + gint32 base; + + 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_but_save; + } + + diff_seqnum = gst_rtp_buffer_compare_seqnum (ctx->last_seqnum, seqnum); + /* Ignore seqnums that are over 15,000 away from the latest one, it's close + * to 2^14 but far enough to avoid any risk of computing error. + */ + if (diff_seqnum > 15000) + goto done_but_save; + + /* Ignore any packet that is in the past, we're only interested in newer + * packets to compute the packet rate. + */ + if (diff_seqnum <= 0 || new_ts <= ctx->last_ts) + 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); + + /* The goal is that higher packet rates "win". + * If there's a sudden burst, the average will go up fast, + * but it will go down again slowly. + * This is useful for bursty cases, where a lot of packets are close + * to each other and should allow a higher reorder/dropout there. + * Round up the new average. + * We do it on different rates depending on the packet rate, so it's not too + * jumpy. + */ + if (ctx->avg_packet_rate > new_packet_rate) + base = MAX (ctx->avg_packet_rate / 3, 8); /* about 333 ms */ + else + base = MAX (ctx->avg_packet_rate / 15, 2); /* about 66 ms */ + + diff_seqnum = MIN (diff_seqnum, base - 1); + + ctx->avg_packet_rate = (((base - diff_seqnum) * ctx->avg_packet_rate) + + (new_packet_rate * diff_seqnum)) / base; + + +done_but_save: + + ctx->last_seqnum = seqnum; + ctx->last_ts = new_ts; +done: + + 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 == -1) { + 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 == -1) { + return RTP_DEF_MISORDER; + } + + return MAX (RTP_MIN_MISORDER, ctx->avg_packet_rate * time_ms / 1000); +} /** * rtp_stats_init_defaults: @@ -31,6 +139,9 @@ 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; } /** @@ -49,7 +160,7 @@ void rtp_stats_set_bandwidths (RTPSessionStats * stats, guint rtp_bw, gdouble rtcp_bw, guint rs, guint rr) { - GST_DEBUG ("recalc bandwidths: RTP %u, RTCP %u, RS %u, RR %u", rtp_bw, + GST_DEBUG ("recalc bandwidths: RTP %u, RTCP %f, RS %u, RR %u", rtp_bw, rtcp_bw, rs, rr); /* when given, sender and receive bandwidth add up to the total @@ -58,22 +169,22 @@ rtp_stats_set_bandwidths (RTPSessionStats * stats, guint rtp_bw, rtcp_bw = rs + rr; /* If rtcp_bw is between 0 and 1, it is a fraction of rtp_bw */ - if (rtcp_bw > 0 && rtcp_bw < 1) { - if (rtp_bw > 0) + if (rtcp_bw > 0.0 && rtcp_bw < 1.0) { + if (rtp_bw > 0.0) rtcp_bw = rtp_bw * rtcp_bw; else - rtcp_bw = -1; + rtcp_bw = -1.0; } /* RTCP is 5% of the RTP bandwidth */ - if (rtp_bw == -1 && rtcp_bw > 0) + if (rtp_bw == -1 && rtcp_bw > 1.0) rtp_bw = rtcp_bw * 20; - else if (rtp_bw != -1 && rtcp_bw < 0) + else if (rtp_bw != -1 && rtcp_bw < 0.0) rtcp_bw = rtp_bw / 20; - else if (rtp_bw == -1 && rtcp_bw < 0) { + else if (rtp_bw == -1 && rtcp_bw < 0.0) { /* nothing given, take defaults */ rtp_bw = RTP_STATS_BANDWIDTH; - rtcp_bw = rtp_bw = RTP_STATS_RTCP_FRACTION; + rtcp_bw = rtp_bw * RTP_STATS_RTCP_FRACTION; } stats->bandwidth = rtp_bw; @@ -117,6 +228,8 @@ rtp_stats_set_bandwidths (RTPSessionStats * stats, guint rtp_bw, * rtp_stats_calculate_rtcp_interval: * @stats: an #RTPSessionStats struct * @sender: if we are a sender + * @profile: RTP profile of this session + * @ptp: if this session is a point-to-point session * @first: if this is the first time * * Calculate the RTCP interval. The result of this function is the amount of @@ -126,22 +239,31 @@ rtp_stats_set_bandwidths (RTPSessionStats * stats, guint rtp_bw, */ GstClockTime rtp_stats_calculate_rtcp_interval (RTPSessionStats * stats, gboolean we_send, - gboolean first) + GstRTPProfile profile, gboolean ptp, gboolean first) { gdouble members, senders, n; gdouble avg_rtcp_size, rtcp_bw; gdouble interval; gdouble rtcp_min_time; - /* Very first call at application start-up uses half the min - * delay for quicker notification while still allowing some time - * before reporting for randomization and to learn about other - * sources so the report interval will converge to the correct - * interval more quickly. - */ - rtcp_min_time = stats->min_interval; - if (first) - rtcp_min_time /= 2.0; + if (profile == GST_RTP_PROFILE_AVPF || profile == GST_RTP_PROFILE_SAVPF) { + /* RFC 4585 3.4d), 3.5.1 */ + + if (first && !ptp) + rtcp_min_time = 1.0; + else + rtcp_min_time = 0.0; + } else { + /* Very first call at application start-up uses half the min + * delay for quicker notification while still allowing some time + * before reporting for randomization and to learn about other + * sources so the report interval will converge to the correct + * interval more quickly. + */ + rtcp_min_time = stats->min_interval; + if (first) + rtcp_min_time /= 2.0; + } /* Dedicate a fraction of the RTCP bandwidth to senders unless * the number of senders is large enough that their share is @@ -163,10 +285,10 @@ rtp_stats_calculate_rtcp_interval (RTPSessionStats * stats, gboolean we_send, /* no bandwidth for RTCP, return NONE to signal that we don't want to send * RTCP packets */ - if (rtcp_bw <= 0.00001) + if (rtcp_bw <= 0.0001) return GST_CLOCK_TIME_NONE; - avg_rtcp_size = stats->avg_rtcp_packet_size / 16.0; + avg_rtcp_size = 8.0 * stats->avg_rtcp_packet_size; /* * The effective number of sites times the average packet size is * the total number of octets sent when each site sends a report. @@ -176,6 +298,7 @@ rtp_stats_calculate_rtcp_interval (RTPSessionStats * stats, gboolean we_send, * time interval we send one report so this time is also our * average time between reports. */ + 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; @@ -245,7 +368,7 @@ rtp_stats_calculate_bye_interval (RTPSessionStats * stats) if (rtcp_bw <= 0.0001) return GST_CLOCK_TIME_NONE; - avg_rtcp_size = stats->avg_rtcp_packet_size / 16.0; + avg_rtcp_size = 8.0 * stats->avg_rtcp_packet_size; /* * The effective number of sites times the average packet size is * the total number of octets sent when each site sends a report. @@ -261,3 +384,296 @@ rtp_stats_calculate_bye_interval (RTPSessionStats * stats) return interval * GST_SECOND; } + +/** + * rtp_stats_get_packets_lost: + * @stats: an #RTPSourceStats struct + * + * Calculate the total number of RTP packets lost since beginning of + * reception. Packets that arrive late are not considered lost, and + * duplicates are not taken into account. Hence, the loss may be negative + * if there are duplicates. + * + * Returns: total RTP packets lost. + */ +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; +} + +static void +_append_structure_to_value_array (GValueArray * array, GstStructure * s) +{ + GValue *val; + g_value_array_append (array, NULL); + val = g_value_array_get_nth (array, array->n_values - 1); + g_value_init (val, GST_TYPE_STRUCTURE); + g_value_take_boxed (val, s); +} + +static void +_structure_take_value_array (GstStructure * s, + const gchar * field_name, GValueArray * array) +{ + GValue value = G_VALUE_INIT; + g_value_init (&value, G_TYPE_VALUE_ARRAY); + g_value_take_boxed (&value, array); + gst_structure_take_value (s, field_name, &value); + g_value_unset (&value); +} + +GstStructure * +rtp_twcc_stats_get_packets_structure (GArray * twcc_packets) +{ + GstStructure *ret = gst_structure_new_empty ("RTPTWCCPackets"); + GValueArray *array = g_value_array_new (0); + guint i; + + for (i = 0; i < twcc_packets->len; i++) { + RTPTWCCPacket *pkt = &g_array_index (twcc_packets, RTPTWCCPacket, i); + + GstStructure *pkt_s = gst_structure_new ("RTPTWCCPacket", + "seqnum", G_TYPE_UINT, pkt->seqnum, + "local-ts", G_TYPE_UINT64, pkt->local_ts, + "remote-ts", G_TYPE_UINT64, pkt->remote_ts, + "size", G_TYPE_UINT, pkt->size, + "lost", G_TYPE_BOOLEAN, pkt->status == RTP_TWCC_PACKET_STATUS_NOT_RECV, + NULL); + _append_structure_to_value_array (array, pkt_s); + } + + _structure_take_value_array (ret, "packets", array); + return ret; +} + +static void +rtp_twcc_stats_calculate_stats (RTPTWCCStats * stats, GArray * twcc_packets) +{ + guint packets_recv = 0; + guint i; + + for (i = 0; i < twcc_packets->len; i++) { + RTPTWCCPacket *pkt = &g_array_index (twcc_packets, RTPTWCCPacket, i); + + if (pkt->status != RTP_TWCC_PACKET_STATUS_NOT_RECV) + packets_recv++; + + if (GST_CLOCK_TIME_IS_VALID (pkt->local_ts) && + GST_CLOCK_TIME_IS_VALID (stats->last_local_ts)) { + pkt->local_delta = GST_CLOCK_DIFF (stats->last_local_ts, pkt->local_ts); + } + + if (GST_CLOCK_TIME_IS_VALID (pkt->remote_ts) && + GST_CLOCK_TIME_IS_VALID (stats->last_remote_ts)) { + pkt->remote_delta = + GST_CLOCK_DIFF (stats->last_remote_ts, pkt->remote_ts); + } + + if (GST_CLOCK_STIME_IS_VALID (pkt->local_delta) && + GST_CLOCK_STIME_IS_VALID (pkt->remote_delta)) { + pkt->delta_delta = pkt->remote_delta - pkt->local_delta; + } + + stats->last_local_ts = pkt->local_ts; + stats->last_remote_ts = pkt->remote_ts; + } + + stats->packets_sent = twcc_packets->len; + stats->packets_recv = packets_recv; +} + +static gint +_get_window_start_index (RTPTWCCStats * stats, GstClockTime duration, + GstClockTime * local_duration, GstClockTime * remote_duration) +{ + RTPTWCCPacket *last = NULL; + guint i; + + if (stats->packets->len < 2) + return -1; + + for (i = 0; i < stats->packets->len; i++) { + guint start_index = stats->packets->len - 1 - i; + RTPTWCCPacket *pkt = + &g_array_index (stats->packets, RTPTWCCPacket, start_index); + if (GST_CLOCK_TIME_IS_VALID (pkt->local_ts) + && GST_CLOCK_TIME_IS_VALID (pkt->remote_ts)) { + /* first find the last valid packet */ + if (last == NULL) { + last = pkt; + } else { + /* and then get the duration in local ts */ + GstClockTimeDiff ld = GST_CLOCK_DIFF (pkt->local_ts, last->local_ts); + if (ld >= duration) { + *local_duration = ld; + *remote_duration = GST_CLOCK_DIFF (pkt->remote_ts, last->remote_ts); + return start_index; + } + } + } + } + + return -1; +} + +static void +rtp_twcc_stats_calculate_windowed_stats (RTPTWCCStats * stats) +{ + guint i; + gint start_idx; + guint bits_sent = 0; + guint bits_recv = 0; + guint packets_sent = 0; + guint packets_recv = 0; + guint packets_lost; + GstClockTimeDiff delta_delta_sum = 0; + guint delta_delta_count = 0; + GstClockTime local_duration; + GstClockTime remote_duration; + + start_idx = _get_window_start_index (stats, stats->window_size, + &local_duration, &remote_duration); + if (start_idx == -1) { + return; + } + + /* remove the old packets */ + if (start_idx > 0) + g_array_remove_range (stats->packets, 0, start_idx); + + packets_sent = stats->packets->len - 1; + + for (i = 0; i < packets_sent; i++) { + RTPTWCCPacket *pkt = &g_array_index (stats->packets, RTPTWCCPacket, i); + + if (GST_CLOCK_TIME_IS_VALID (pkt->local_ts)) { + bits_sent += pkt->size * 8; + } + + if (GST_CLOCK_TIME_IS_VALID (pkt->remote_ts)) { + bits_recv += pkt->size * 8; + packets_recv++; + } + + if (GST_CLOCK_STIME_IS_VALID (pkt->delta_delta)) { + delta_delta_sum += pkt->delta_delta; + delta_delta_count++; + } + } + + packets_lost = packets_sent - packets_recv; + stats->packet_loss_pct = (packets_lost * 100) / (gfloat) packets_sent; + + if (delta_delta_count) { + GstClockTimeDiff avg_delta_of_delta = delta_delta_sum / delta_delta_count; + if (GST_CLOCK_STIME_IS_VALID (stats->avg_delta_of_delta)) { + stats->avg_delta_of_delta_change = + (avg_delta_of_delta - + stats->avg_delta_of_delta) / (250 * GST_USECOND); + } + stats->avg_delta_of_delta = avg_delta_of_delta; + } + + if (local_duration > 0) + stats->bitrate_sent = + gst_util_uint64_scale (bits_sent, GST_SECOND, local_duration); + if (remote_duration > 0) + stats->bitrate_recv = + gst_util_uint64_scale (bits_recv, GST_SECOND, remote_duration); + + GST_DEBUG ("Got stats: bits_sent: %u, bits_recv: %u, packets_sent = %u, " + "packets_recv: %u, packetlost_pct = %f, sent_bitrate = %u, " + "recv_bitrate = %u, delta-delta-avg = %" GST_STIME_FORMAT ", " + "delta-delta-change: %f", bits_sent, bits_recv, stats->packets_sent, + packets_recv, stats->packet_loss_pct, stats->bitrate_sent, + stats->bitrate_recv, GST_STIME_ARGS (stats->avg_delta_of_delta), + stats->avg_delta_of_delta_change); +} + +RTPTWCCStats * +rtp_twcc_stats_new (void) +{ + RTPTWCCStats *stats = g_new0 (RTPTWCCStats, 1); + stats->packets = g_array_new (FALSE, FALSE, sizeof (RTPTWCCPacket)); + stats->last_local_ts = GST_CLOCK_TIME_NONE; + stats->last_remote_ts = GST_CLOCK_TIME_NONE; + stats->avg_delta_of_delta = GST_CLOCK_STIME_NONE; + stats->window_size = 300 * GST_MSECOND; /* FIXME: could be configurable? */ + return stats; +} + +void +rtp_twcc_stats_free (RTPTWCCStats * stats) +{ + g_array_unref (stats->packets); + g_free (stats); +} + +static GstStructure * +rtp_twcc_stats_get_stats_structure (RTPTWCCStats * stats) +{ + return gst_structure_new ("RTPTWCCStats", + "bitrate-sent", G_TYPE_UINT, stats->bitrate_sent, + "bitrate-recv", G_TYPE_UINT, stats->bitrate_recv, + "packets-sent", G_TYPE_UINT, stats->packets_sent, + "packets-recv", G_TYPE_UINT, stats->packets_recv, + "packet-loss-pct", G_TYPE_DOUBLE, stats->packet_loss_pct, + "avg-delta-of-delta", G_TYPE_INT64, stats->avg_delta_of_delta, NULL); +} + +GstStructure * +rtp_twcc_stats_process_packets (RTPTWCCStats * stats, GArray * twcc_packets) +{ + rtp_twcc_stats_calculate_stats (stats, twcc_packets); + g_array_append_vals (stats->packets, twcc_packets->data, twcc_packets->len); + rtp_twcc_stats_calculate_windowed_stats (stats); + return rtp_twcc_stats_get_stats_structure (stats); +}