rtpstats: Casting with gfloat before division
[platform/upstream/gst-plugins-good.git] / gst / rtpmanager / rtpstats.c
1 /* GStreamer
2  * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
3  * Copyright (C)  2015 Kurento (http://kurento.org/)
4  *   @author: Miguel ParĂ­s <mparisdiaz@gmail.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 #define GLIB_DISABLE_DEPRECATION_WARNINGS
23
24 #include "rtpstats.h"
25 #include "rtptwcc.h"
26
27 void
28 gst_rtp_packet_rate_ctx_reset (RTPPacketRateCtx * ctx, gint32 clock_rate)
29 {
30   ctx->clock_rate = clock_rate;
31   ctx->probed = FALSE;
32   ctx->avg_packet_rate = -1;
33   ctx->last_ts = -1;
34 }
35
36 guint32
37 gst_rtp_packet_rate_ctx_update (RTPPacketRateCtx * ctx, guint16 seqnum,
38     guint32 ts)
39 {
40   guint64 new_ts, diff_ts;
41   gint diff_seqnum;
42   gint32 new_packet_rate;
43   gint32 base;
44
45   if (ctx->clock_rate <= 0) {
46     return ctx->avg_packet_rate;
47   }
48
49   new_ts = ctx->last_ts;
50   gst_rtp_buffer_ext_timestamp (&new_ts, ts);
51
52   if (!ctx->probed) {
53     ctx->probed = TRUE;
54     goto done_but_save;
55   }
56
57   diff_seqnum = gst_rtp_buffer_compare_seqnum (ctx->last_seqnum, seqnum);
58   /* Ignore seqnums that are over 15,000 away from the latest one, it's close
59    * to 2^14 but far enough to avoid any risk of computing error.
60    */
61   if (diff_seqnum > 15000)
62     goto done_but_save;
63
64   /* Ignore any packet that is in the past, we're only interested in newer
65    * packets to compute the packet rate.
66    */
67   if (diff_seqnum <= 0 || new_ts <= ctx->last_ts)
68     goto done;
69
70   diff_ts = new_ts - ctx->last_ts;
71   diff_ts = gst_util_uint64_scale_int (diff_ts, GST_SECOND, ctx->clock_rate);
72   new_packet_rate = gst_util_uint64_scale (diff_seqnum, GST_SECOND, diff_ts);
73
74   /* The goal is that higher packet rates "win".
75    * If there's a sudden burst, the average will go up fast,
76    * but it will go down again slowly.
77    * This is useful for bursty cases, where a lot of packets are close
78    * to each other and should allow a higher reorder/dropout there.
79    * Round up the new average.
80    * We do it on different rates depending on the packet rate, so it's not too
81    * jumpy.
82    */
83   if (ctx->avg_packet_rate > new_packet_rate)
84     base = MAX (ctx->avg_packet_rate / 3, 8);   /* about 333 ms */
85   else
86     base = MAX (ctx->avg_packet_rate / 15, 2);  /* about 66 ms */
87
88   diff_seqnum = MIN (diff_seqnum, base - 1);
89
90   ctx->avg_packet_rate = (((base - diff_seqnum) * ctx->avg_packet_rate) +
91       (new_packet_rate * diff_seqnum)) / base;
92
93
94 done_but_save:
95
96   ctx->last_seqnum = seqnum;
97   ctx->last_ts = new_ts;
98 done:
99
100   return ctx->avg_packet_rate;
101 }
102
103 guint32
104 gst_rtp_packet_rate_ctx_get (RTPPacketRateCtx * ctx)
105 {
106   return ctx->avg_packet_rate;
107 }
108
109 guint32
110 gst_rtp_packet_rate_ctx_get_max_dropout (RTPPacketRateCtx * ctx, gint32 time_ms)
111 {
112   if (time_ms <= 0 || !ctx->probed || ctx->avg_packet_rate == -1) {
113     return RTP_DEF_DROPOUT;
114   }
115
116   return MAX (RTP_MIN_DROPOUT, ctx->avg_packet_rate * time_ms / 1000);
117 }
118
119 guint32
120 gst_rtp_packet_rate_ctx_get_max_misorder (RTPPacketRateCtx * ctx,
121     gint32 time_ms)
122 {
123   if (time_ms <= 0 || !ctx->probed || ctx->avg_packet_rate == -1) {
124     return RTP_DEF_MISORDER;
125   }
126
127   return MAX (RTP_MIN_MISORDER, ctx->avg_packet_rate * time_ms / 1000);
128 }
129
130 /**
131  * rtp_stats_init_defaults:
132  * @stats: an #RTPSessionStats struct
133  *
134  * Initialize @stats with its default values.
135  */
136 void
137 rtp_stats_init_defaults (RTPSessionStats * stats)
138 {
139   rtp_stats_set_bandwidths (stats, -1, -1, -1, -1);
140   stats->min_interval = RTP_STATS_MIN_INTERVAL;
141   stats->bye_timeout = RTP_STATS_BYE_TIMEOUT;
142   stats->nacks_dropped = 0;
143   stats->nacks_sent = 0;
144   stats->nacks_received = 0;
145 }
146
147 /**
148  * rtp_stats_set_bandwidths:
149  * @stats: an #RTPSessionStats struct
150  * @rtp_bw: RTP bandwidth
151  * @rtcp_bw: RTCP bandwidth
152  * @rs: sender RTCP bandwidth
153  * @rr: receiver RTCP bandwidth
154  *
155  * Configure the bandwidth parameters in the stats. When an input variable is
156  * set to -1, it will be calculated from the other input variables and from the
157  * defaults.
158  */
159 void
160 rtp_stats_set_bandwidths (RTPSessionStats * stats, guint rtp_bw,
161     gdouble rtcp_bw, guint rs, guint rr)
162 {
163   GST_DEBUG ("recalc bandwidths: RTP %u, RTCP %f, RS %u, RR %u", rtp_bw,
164       rtcp_bw, rs, rr);
165
166   /* when given, sender and receive bandwidth add up to the total
167    * rtcp bandwidth */
168   if (rs != -1 && rr != -1)
169     rtcp_bw = rs + rr;
170
171   /* If rtcp_bw is between 0 and 1, it is a fraction of rtp_bw */
172   if (rtcp_bw > 0.0 && rtcp_bw < 1.0) {
173     if (rtp_bw > 0.0)
174       rtcp_bw = rtp_bw * rtcp_bw;
175     else
176       rtcp_bw = -1.0;
177   }
178
179   /* RTCP is 5% of the RTP bandwidth */
180   if (rtp_bw == -1 && rtcp_bw > 1.0)
181     rtp_bw = rtcp_bw * 20;
182   else if (rtp_bw != -1 && rtcp_bw < 0.0)
183     rtcp_bw = rtp_bw / 20;
184   else if (rtp_bw == -1 && rtcp_bw < 0.0) {
185     /* nothing given, take defaults */
186     rtp_bw = RTP_STATS_BANDWIDTH;
187     rtcp_bw = rtp_bw * RTP_STATS_RTCP_FRACTION;
188   }
189
190   stats->bandwidth = rtp_bw;
191   stats->rtcp_bandwidth = rtcp_bw;
192
193   /* now figure out the fractions */
194   if (rs == -1) {
195     /* rs unknown */
196     if (rr == -1) {
197       /* both not given, use defaults */
198       rs = stats->rtcp_bandwidth * RTP_STATS_SENDER_FRACTION;
199       rr = stats->rtcp_bandwidth * RTP_STATS_RECEIVER_FRACTION;
200     } else {
201       /* rr known, calculate rs */
202       if (stats->rtcp_bandwidth > rr)
203         rs = stats->rtcp_bandwidth - rr;
204       else
205         rs = 0;
206     }
207   } else if (rr == -1) {
208     /* rs known, calculate rr */
209     if (stats->rtcp_bandwidth > rs)
210       rr = stats->rtcp_bandwidth - rs;
211     else
212       rr = 0;
213   }
214
215   if (stats->rtcp_bandwidth > 0) {
216     stats->sender_fraction = ((gdouble) rs) / ((gdouble) stats->rtcp_bandwidth);
217     stats->receiver_fraction = 1.0 - stats->sender_fraction;
218   } else {
219     /* no RTCP bandwidth, set dummy values */
220     stats->sender_fraction = 0.0;
221     stats->receiver_fraction = 0.0;
222   }
223   GST_DEBUG ("bandwidths: RTP %u, RTCP %u, RS %f, RR %f", stats->bandwidth,
224       stats->rtcp_bandwidth, stats->sender_fraction, stats->receiver_fraction);
225 }
226
227 /**
228  * rtp_stats_calculate_rtcp_interval:
229  * @stats: an #RTPSessionStats struct
230  * @sender: if we are a sender
231  * @profile: RTP profile of this session
232  * @ptp: if this session is a point-to-point session
233  * @first: if this is the first time
234  *
235  * Calculate the RTCP interval. The result of this function is the amount of
236  * time to wait (in nanoseconds) before sending a new RTCP message.
237  *
238  * Returns: the RTCP interval.
239  */
240 GstClockTime
241 rtp_stats_calculate_rtcp_interval (RTPSessionStats * stats, gboolean we_send,
242     GstRTPProfile profile, gboolean ptp, gboolean first)
243 {
244   gdouble members, senders, n;
245   gdouble avg_rtcp_size, rtcp_bw;
246   gdouble interval;
247   gdouble rtcp_min_time;
248
249   if (profile == GST_RTP_PROFILE_AVPF || profile == GST_RTP_PROFILE_SAVPF) {
250     /* RFC 4585 3.4d), 3.5.1 */
251
252     if (first && !ptp)
253       rtcp_min_time = 1.0;
254     else
255       rtcp_min_time = 0.0;
256   } else {
257     /* Very first call at application start-up uses half the min
258      * delay for quicker notification while still allowing some time
259      * before reporting for randomization and to learn about other
260      * sources so the report interval will converge to the correct
261      * interval more quickly.
262      */
263     rtcp_min_time = stats->min_interval;
264     if (first)
265       rtcp_min_time /= 2.0;
266   }
267
268   /* Dedicate a fraction of the RTCP bandwidth to senders unless
269    * the number of senders is large enough that their share is
270    * more than that fraction.
271    */
272   n = members = stats->active_sources;
273   senders = (gdouble) stats->sender_sources;
274   rtcp_bw = stats->rtcp_bandwidth;
275
276   if (senders <= members * stats->sender_fraction) {
277     if (we_send) {
278       rtcp_bw *= stats->sender_fraction;
279       n = senders;
280     } else {
281       rtcp_bw *= stats->receiver_fraction;
282       n -= senders;
283     }
284   }
285
286   /* no bandwidth for RTCP, return NONE to signal that we don't want to send
287    * RTCP packets */
288   if (rtcp_bw <= 0.0001)
289     return GST_CLOCK_TIME_NONE;
290
291   avg_rtcp_size = 8.0 * stats->avg_rtcp_packet_size;
292   /*
293    * The effective number of sites times the average packet size is
294    * the total number of octets sent when each site sends a report.
295    * Dividing this by the effective bandwidth gives the time
296    * interval over which those packets must be sent in order to
297    * meet the bandwidth target, with a minimum enforced.  In that
298    * time interval we send one report so this time is also our
299    * average time between reports.
300    */
301   GST_DEBUG ("avg size %f, n %f, rtcp_bw %f", avg_rtcp_size, n, rtcp_bw);
302   interval = avg_rtcp_size * n / rtcp_bw;
303   if (interval < rtcp_min_time)
304     interval = rtcp_min_time;
305
306   return interval * GST_SECOND;
307 }
308
309 /**
310  * rtp_stats_add_rtcp_jitter:
311  * @stats: an #RTPSessionStats struct
312  * @interval: an RTCP interval
313  *
314  * Apply a random jitter to the @interval. @interval is typically obtained with
315  * rtp_stats_calculate_rtcp_interval().
316  *
317  * Returns: the new RTCP interval.
318  */
319 GstClockTime
320 rtp_stats_add_rtcp_jitter (RTPSessionStats * stats, GstClockTime interval)
321 {
322   gdouble temp;
323
324   /* see RFC 3550 p 30
325    * To compensate for "unconditional reconsideration" converging to a
326    * value below the intended average.
327    */
328 #define COMPENSATION  (2.71828 - 1.5);
329
330   temp = (interval * g_random_double_range (0.5, 1.5)) / COMPENSATION;
331
332   return (GstClockTime) temp;
333 }
334
335
336 /**
337  * rtp_stats_calculate_bye_interval:
338  * @stats: an #RTPSessionStats struct
339  *
340  * Calculate the BYE interval. The result of this function is the amount of
341  * time to wait (in nanoseconds) before sending a BYE message.
342  *
343  * Returns: the BYE interval.
344  */
345 GstClockTime
346 rtp_stats_calculate_bye_interval (RTPSessionStats * stats)
347 {
348   gdouble members;
349   gdouble avg_rtcp_size, rtcp_bw;
350   gdouble interval;
351   gdouble rtcp_min_time;
352
353   /* no interval when we have less than 50 members */
354   if (stats->active_sources < 50)
355     return 0;
356
357   rtcp_min_time = (stats->min_interval) / 2.0;
358
359   /* Dedicate a fraction of the RTCP bandwidth to senders unless
360    * the number of senders is large enough that their share is
361    * more than that fraction.
362    */
363   members = stats->bye_members;
364   rtcp_bw = stats->rtcp_bandwidth * stats->receiver_fraction;
365
366   /* no bandwidth for RTCP, return NONE to signal that we don't want to send
367    * RTCP packets */
368   if (rtcp_bw <= 0.0001)
369     return GST_CLOCK_TIME_NONE;
370
371   avg_rtcp_size = 8.0 * stats->avg_rtcp_packet_size;
372   /*
373    * The effective number of sites times the average packet size is
374    * the total number of octets sent when each site sends a report.
375    * Dividing this by the effective bandwidth gives the time
376    * interval over which those packets must be sent in order to
377    * meet the bandwidth target, with a minimum enforced.  In that
378    * time interval we send one report so this time is also our
379    * average time between reports.
380    */
381   interval = avg_rtcp_size * members / rtcp_bw;
382   if (interval < rtcp_min_time)
383     interval = rtcp_min_time;
384
385   return interval * GST_SECOND;
386 }
387
388 /**
389  * rtp_stats_get_packets_lost:
390  * @stats: an #RTPSourceStats struct
391  *
392  * Calculate the total number of RTP packets lost since beginning of
393  * reception. Packets that arrive late are not considered lost, and
394  * duplicates are not taken into account. Hence, the loss may be negative
395  * if there are duplicates.
396  *
397  * Returns: total RTP packets lost.
398  */
399 gint64
400 rtp_stats_get_packets_lost (const RTPSourceStats * stats)
401 {
402   gint64 lost;
403   guint64 extended_max, expected;
404
405   extended_max = stats->cycles + stats->max_seq;
406   expected = extended_max - stats->base_seq + 1;
407   lost = expected - stats->packets_received;
408
409   return lost;
410 }
411
412 void
413 rtp_stats_set_min_interval (RTPSessionStats * stats, gdouble min_interval)
414 {
415   stats->min_interval = min_interval;
416 }
417
418 gboolean
419 __g_socket_address_equal (GSocketAddress * a, GSocketAddress * b)
420 {
421   GInetSocketAddress *ia, *ib;
422   GInetAddress *iaa, *iab;
423
424   ia = G_INET_SOCKET_ADDRESS (a);
425   ib = G_INET_SOCKET_ADDRESS (b);
426
427   if (g_inet_socket_address_get_port (ia) !=
428       g_inet_socket_address_get_port (ib))
429     return FALSE;
430
431   iaa = g_inet_socket_address_get_address (ia);
432   iab = g_inet_socket_address_get_address (ib);
433
434   return g_inet_address_equal (iaa, iab);
435 }
436
437 gchar *
438 __g_socket_address_to_string (GSocketAddress * addr)
439 {
440   GInetSocketAddress *ia;
441   gchar *ret, *tmp;
442
443   ia = G_INET_SOCKET_ADDRESS (addr);
444
445   tmp = g_inet_address_to_string (g_inet_socket_address_get_address (ia));
446   ret = g_strdup_printf ("%s:%u", tmp, g_inet_socket_address_get_port (ia));
447   g_free (tmp);
448
449   return ret;
450 }
451
452 static void
453 _append_structure_to_value_array (GValueArray * array, GstStructure * s)
454 {
455   GValue *val;
456   g_value_array_append (array, NULL);
457   val = g_value_array_get_nth (array, array->n_values - 1);
458   g_value_init (val, GST_TYPE_STRUCTURE);
459   g_value_take_boxed (val, s);
460 }
461
462 static void
463 _structure_take_value_array (GstStructure * s,
464     const gchar * field_name, GValueArray * array)
465 {
466   GValue value = G_VALUE_INIT;
467   g_value_init (&value, G_TYPE_VALUE_ARRAY);
468   g_value_take_boxed (&value, array);
469   gst_structure_take_value (s, field_name, &value);
470   g_value_unset (&value);
471 }
472
473 GstStructure *
474 rtp_twcc_stats_get_packets_structure (GArray * twcc_packets)
475 {
476   GstStructure *ret = gst_structure_new_empty ("RTPTWCCPackets");
477   GValueArray *array = g_value_array_new (0);
478   guint i;
479
480   for (i = 0; i < twcc_packets->len; i++) {
481     RTPTWCCPacket *pkt = &g_array_index (twcc_packets, RTPTWCCPacket, i);
482
483     GstStructure *pkt_s = gst_structure_new ("RTPTWCCPacket",
484         "seqnum", G_TYPE_UINT, pkt->seqnum,
485         "local-ts", G_TYPE_UINT64, pkt->local_ts,
486         "remote-ts", G_TYPE_UINT64, pkt->remote_ts,
487         "size", G_TYPE_UINT, pkt->size,
488         "lost", G_TYPE_BOOLEAN, pkt->status == RTP_TWCC_PACKET_STATUS_NOT_RECV,
489         NULL);
490     _append_structure_to_value_array (array, pkt_s);
491   }
492
493   _structure_take_value_array (ret, "packets", array);
494   return ret;
495 }
496
497 static void
498 rtp_twcc_stats_calculate_stats (RTPTWCCStats * stats, GArray * twcc_packets)
499 {
500   guint packets_recv = 0;
501   guint i;
502
503   for (i = 0; i < twcc_packets->len; i++) {
504     RTPTWCCPacket *pkt = &g_array_index (twcc_packets, RTPTWCCPacket, i);
505
506     if (pkt->status != RTP_TWCC_PACKET_STATUS_NOT_RECV)
507       packets_recv++;
508
509     if (GST_CLOCK_TIME_IS_VALID (pkt->local_ts) &&
510         GST_CLOCK_TIME_IS_VALID (stats->last_local_ts)) {
511       pkt->local_delta = GST_CLOCK_DIFF (stats->last_local_ts, pkt->local_ts);
512     }
513
514     if (GST_CLOCK_TIME_IS_VALID (pkt->remote_ts) &&
515         GST_CLOCK_TIME_IS_VALID (stats->last_remote_ts)) {
516       pkt->remote_delta =
517           GST_CLOCK_DIFF (stats->last_remote_ts, pkt->remote_ts);
518     }
519
520     if (GST_CLOCK_STIME_IS_VALID (pkt->local_delta) &&
521         GST_CLOCK_STIME_IS_VALID (pkt->remote_delta)) {
522       pkt->delta_delta = pkt->remote_delta - pkt->local_delta;
523     }
524
525     stats->last_local_ts = pkt->local_ts;
526     stats->last_remote_ts = pkt->remote_ts;
527   }
528
529   stats->packets_sent = twcc_packets->len;
530   stats->packets_recv = packets_recv;
531 }
532
533 static gint
534 _get_window_start_index (RTPTWCCStats * stats, GstClockTime duration,
535     GstClockTime * local_duration, GstClockTime * remote_duration)
536 {
537   RTPTWCCPacket *last = NULL;
538   guint i;
539
540   if (stats->packets->len < 2)
541     return -1;
542
543   for (i = 0; i < stats->packets->len; i++) {
544     guint start_index = stats->packets->len - 1 - i;
545     RTPTWCCPacket *pkt =
546         &g_array_index (stats->packets, RTPTWCCPacket, start_index);
547     if (GST_CLOCK_TIME_IS_VALID (pkt->local_ts)
548         && GST_CLOCK_TIME_IS_VALID (pkt->remote_ts)) {
549       /* first find the last valid packet */
550       if (last == NULL) {
551         last = pkt;
552       } else {
553         /* and then get the duration in local ts */
554         GstClockTimeDiff ld = GST_CLOCK_DIFF (pkt->local_ts, last->local_ts);
555         if (ld >= duration) {
556           *local_duration = ld;
557           *remote_duration = GST_CLOCK_DIFF (pkt->remote_ts, last->remote_ts);
558           return start_index;
559         }
560       }
561     }
562   }
563
564   return -1;
565 }
566
567 static void
568 rtp_twcc_stats_calculate_windowed_stats (RTPTWCCStats * stats)
569 {
570   guint i;
571   gint start_idx;
572   guint bits_sent = 0;
573   guint bits_recv = 0;
574   guint packets_sent = 0;
575   guint packets_recv = 0;
576   guint packets_lost;
577   GstClockTimeDiff delta_delta_sum = 0;
578   guint delta_delta_count = 0;
579   GstClockTime local_duration;
580   GstClockTime remote_duration;
581
582   start_idx = _get_window_start_index (stats, stats->window_size,
583       &local_duration, &remote_duration);
584   if (start_idx == -1) {
585     return;
586   }
587
588   /* remove the old packets */
589   if (start_idx > 0)
590     g_array_remove_range (stats->packets, 0, start_idx);
591
592   packets_sent = stats->packets->len - 1;
593
594   for (i = 0; i < packets_sent; i++) {
595     RTPTWCCPacket *pkt = &g_array_index (stats->packets, RTPTWCCPacket, i);
596
597     if (GST_CLOCK_TIME_IS_VALID (pkt->local_ts)) {
598       bits_sent += pkt->size * 8;
599     }
600
601     if (GST_CLOCK_TIME_IS_VALID (pkt->remote_ts)) {
602       bits_recv += pkt->size * 8;
603       packets_recv++;
604     }
605
606     if (GST_CLOCK_STIME_IS_VALID (pkt->delta_delta)) {
607       delta_delta_sum += pkt->delta_delta;
608       delta_delta_count++;
609     }
610   }
611
612   packets_lost = packets_sent - packets_recv;
613   stats->packet_loss_pct = (packets_lost * 100) / (gfloat) packets_sent;
614
615   if (delta_delta_count) {
616     GstClockTimeDiff avg_delta_of_delta = delta_delta_sum / delta_delta_count;
617     if (GST_CLOCK_STIME_IS_VALID (stats->avg_delta_of_delta)) {
618       stats->avg_delta_of_delta_change =
619           (avg_delta_of_delta -
620 #ifndef __TIZEN__
621           stats->avg_delta_of_delta) / (250 * GST_USECOND);
622 #else
623           stats->avg_delta_of_delta) / (gfloat) (250 * GST_USECOND);
624 #endif
625     }
626     stats->avg_delta_of_delta = avg_delta_of_delta;
627   }
628
629   if (local_duration > 0)
630     stats->bitrate_sent =
631         gst_util_uint64_scale (bits_sent, GST_SECOND, local_duration);
632   if (remote_duration > 0)
633     stats->bitrate_recv =
634         gst_util_uint64_scale (bits_recv, GST_SECOND, remote_duration);
635
636   GST_DEBUG ("Got stats: bits_sent: %u, bits_recv: %u, packets_sent = %u, "
637       "packets_recv: %u, packetlost_pct = %f, sent_bitrate = %u, "
638       "recv_bitrate = %u, delta-delta-avg = %" GST_STIME_FORMAT ", "
639       "delta-delta-change: %f", bits_sent, bits_recv, stats->packets_sent,
640       packets_recv, stats->packet_loss_pct, stats->bitrate_sent,
641       stats->bitrate_recv, GST_STIME_ARGS (stats->avg_delta_of_delta),
642       stats->avg_delta_of_delta_change);
643 }
644
645 RTPTWCCStats *
646 rtp_twcc_stats_new (void)
647 {
648   RTPTWCCStats *stats = g_new0 (RTPTWCCStats, 1);
649   stats->packets = g_array_new (FALSE, FALSE, sizeof (RTPTWCCPacket));
650   stats->last_local_ts = GST_CLOCK_TIME_NONE;
651   stats->last_remote_ts = GST_CLOCK_TIME_NONE;
652   stats->avg_delta_of_delta = GST_CLOCK_STIME_NONE;
653   stats->window_size = 300 * GST_MSECOND;       /* FIXME: could be configurable? */
654   return stats;
655 }
656
657 void
658 rtp_twcc_stats_free (RTPTWCCStats * stats)
659 {
660   g_array_unref (stats->packets);
661   g_free (stats);
662 }
663
664 static GstStructure *
665 rtp_twcc_stats_get_stats_structure (RTPTWCCStats * stats)
666 {
667   return gst_structure_new ("RTPTWCCStats",
668       "bitrate-sent", G_TYPE_UINT, stats->bitrate_sent,
669       "bitrate-recv", G_TYPE_UINT, stats->bitrate_recv,
670       "packets-sent", G_TYPE_UINT, stats->packets_sent,
671       "packets-recv", G_TYPE_UINT, stats->packets_recv,
672       "packet-loss-pct", G_TYPE_DOUBLE, stats->packet_loss_pct,
673       "avg-delta-of-delta", G_TYPE_INT64, stats->avg_delta_of_delta, NULL);
674 }
675
676 GstStructure *
677 rtp_twcc_stats_process_packets (RTPTWCCStats * stats, GArray * twcc_packets)
678 {
679   rtp_twcc_stats_calculate_stats (stats, twcc_packets);
680   g_array_append_vals (stats->packets, twcc_packets->data, twcc_packets->len);
681   rtp_twcc_stats_calculate_windowed_stats (stats);
682   return rtp_twcc_stats_get_stats_structure (stats);
683 }