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
gst_rtp_packet_rate_ctx_reset(RTPPacketRateCtx * ctx,gint32 clock_rate)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
gst_rtp_packet_rate_ctx_update(RTPPacketRateCtx * ctx,guint16 seqnum,guint32 ts)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
gst_rtp_packet_rate_ctx_get(RTPPacketRateCtx * ctx)104 gst_rtp_packet_rate_ctx_get (RTPPacketRateCtx * ctx)
105 {
106 return ctx->avg_packet_rate;
107 }
108
109 guint32
gst_rtp_packet_rate_ctx_get_max_dropout(RTPPacketRateCtx * ctx,gint32 time_ms)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
gst_rtp_packet_rate_ctx_get_max_misorder(RTPPacketRateCtx * ctx,gint32 time_ms)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
rtp_stats_init_defaults(RTPSessionStats * stats)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
rtp_stats_set_bandwidths(RTPSessionStats * stats,guint rtp_bw,gdouble rtcp_bw,guint rs,guint rr)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
rtp_stats_calculate_rtcp_interval(RTPSessionStats * stats,gboolean we_send,GstRTPProfile profile,gboolean ptp,gboolean first)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
rtp_stats_add_rtcp_jitter(RTPSessionStats * stats,GstClockTime interval)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
rtp_stats_calculate_bye_interval(RTPSessionStats * stats)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
rtp_stats_get_packets_lost(const RTPSourceStats * stats)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
rtp_stats_set_min_interval(RTPSessionStats * stats,gdouble min_interval)413 rtp_stats_set_min_interval (RTPSessionStats * stats, gdouble min_interval)
414 {
415 stats->min_interval = min_interval;
416 }
417
418 gboolean
__g_socket_address_equal(GSocketAddress * a,GSocketAddress * b)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 *
__g_socket_address_to_string(GSocketAddress * addr)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
_append_structure_to_value_array(GValueArray * array,GstStructure * s)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
_structure_take_value_array(GstStructure * s,const gchar * field_name,GValueArray * array)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 *
rtp_twcc_stats_get_packets_structure(GArray * twcc_packets)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 "payload-type", G_TYPE_UCHAR, pkt->pt,
488 "size", G_TYPE_UINT, pkt->size,
489 "lost", G_TYPE_BOOLEAN, pkt->status == RTP_TWCC_PACKET_STATUS_NOT_RECV,
490 NULL);
491 _append_structure_to_value_array (array, pkt_s);
492 }
493
494 _structure_take_value_array (ret, "packets", array);
495 return ret;
496 }
497
498 static void
rtp_twcc_stats_calculate_stats(RTPTWCCStats * stats,GArray * twcc_packets)499 rtp_twcc_stats_calculate_stats (RTPTWCCStats * stats, GArray * twcc_packets)
500 {
501 guint packets_recv = 0;
502 guint i;
503
504 for (i = 0; i < twcc_packets->len; i++) {
505 RTPTWCCPacket *pkt = &g_array_index (twcc_packets, RTPTWCCPacket, i);
506
507 if (pkt->status != RTP_TWCC_PACKET_STATUS_NOT_RECV)
508 packets_recv++;
509
510 if (GST_CLOCK_TIME_IS_VALID (pkt->local_ts) &&
511 GST_CLOCK_TIME_IS_VALID (stats->last_local_ts)) {
512 pkt->local_delta = GST_CLOCK_DIFF (stats->last_local_ts, pkt->local_ts);
513 }
514
515 if (GST_CLOCK_TIME_IS_VALID (pkt->remote_ts) &&
516 GST_CLOCK_TIME_IS_VALID (stats->last_remote_ts)) {
517 pkt->remote_delta =
518 GST_CLOCK_DIFF (stats->last_remote_ts, pkt->remote_ts);
519 }
520
521 if (GST_CLOCK_STIME_IS_VALID (pkt->local_delta) &&
522 GST_CLOCK_STIME_IS_VALID (pkt->remote_delta)) {
523 pkt->delta_delta = pkt->remote_delta - pkt->local_delta;
524 }
525
526 stats->last_local_ts = pkt->local_ts;
527 stats->last_remote_ts = pkt->remote_ts;
528 }
529
530 stats->packets_sent = twcc_packets->len;
531 stats->packets_recv = packets_recv;
532 }
533
534 static gint
_get_window_start_index(RTPTWCCStats * stats,GstClockTime duration,GstClockTime * local_duration,GstClockTime * remote_duration)535 _get_window_start_index (RTPTWCCStats * stats, GstClockTime duration,
536 GstClockTime * local_duration, GstClockTime * remote_duration)
537 {
538 RTPTWCCPacket *last = NULL;
539 guint i;
540
541 if (stats->packets->len < 2)
542 return -1;
543
544 for (i = 0; i < stats->packets->len; i++) {
545 guint start_index = stats->packets->len - 1 - i;
546 RTPTWCCPacket *pkt =
547 &g_array_index (stats->packets, RTPTWCCPacket, start_index);
548 if (GST_CLOCK_TIME_IS_VALID (pkt->local_ts)
549 && GST_CLOCK_TIME_IS_VALID (pkt->remote_ts)) {
550 /* first find the last valid packet */
551 if (last == NULL) {
552 last = pkt;
553 } else {
554 /* and then get the duration in local ts */
555 GstClockTimeDiff ld = GST_CLOCK_DIFF (pkt->local_ts, last->local_ts);
556 if (ld >= duration) {
557 *local_duration = ld;
558 *remote_duration = GST_CLOCK_DIFF (pkt->remote_ts, last->remote_ts);
559 return start_index;
560 }
561 }
562 }
563 }
564
565 return -1;
566 }
567
568 static void
rtp_twcc_stats_calculate_windowed_stats(RTPTWCCStats * stats)569 rtp_twcc_stats_calculate_windowed_stats (RTPTWCCStats * stats)
570 {
571 guint i;
572 gint start_idx;
573 guint bits_sent = 0;
574 guint bits_recv = 0;
575 guint packets_sent = 0;
576 guint packets_recv = 0;
577 guint packets_lost;
578 GstClockTimeDiff delta_delta_sum = 0;
579 guint delta_delta_count = 0;
580 GstClockTime local_duration;
581 GstClockTime remote_duration;
582
583 start_idx = _get_window_start_index (stats, stats->window_size,
584 &local_duration, &remote_duration);
585 if (start_idx == -1) {
586 return;
587 }
588
589 /* remove the old packets */
590 if (start_idx > 0)
591 g_array_remove_range (stats->packets, 0, start_idx);
592
593 packets_sent = stats->packets->len - 1;
594
595 for (i = 0; i < packets_sent; i++) {
596 RTPTWCCPacket *pkt = &g_array_index (stats->packets, RTPTWCCPacket, i);
597
598 if (GST_CLOCK_TIME_IS_VALID (pkt->local_ts)) {
599 bits_sent += pkt->size * 8;
600 }
601
602 if (GST_CLOCK_TIME_IS_VALID (pkt->remote_ts)) {
603 bits_recv += pkt->size * 8;
604 packets_recv++;
605 }
606
607 if (GST_CLOCK_STIME_IS_VALID (pkt->delta_delta)) {
608 delta_delta_sum += pkt->delta_delta;
609 delta_delta_count++;
610 }
611 }
612
613 packets_lost = packets_sent - packets_recv;
614 stats->packet_loss_pct = (packets_lost * 100) / (gfloat) packets_sent;
615
616 if (delta_delta_count) {
617 GstClockTimeDiff avg_delta_of_delta = delta_delta_sum / delta_delta_count;
618 if (GST_CLOCK_STIME_IS_VALID (stats->avg_delta_of_delta)) {
619 stats->avg_delta_of_delta_change =
620 (avg_delta_of_delta -
621 stats->avg_delta_of_delta) / (250 * GST_USECOND);
622 }
623 stats->avg_delta_of_delta = avg_delta_of_delta;
624 }
625
626 if (local_duration > 0)
627 stats->bitrate_sent =
628 gst_util_uint64_scale (bits_sent, GST_SECOND, local_duration);
629 if (remote_duration > 0)
630 stats->bitrate_recv =
631 gst_util_uint64_scale (bits_recv, GST_SECOND, remote_duration);
632
633 GST_DEBUG ("Got stats: bits_sent: %u, bits_recv: %u, packets_sent = %u, "
634 "packets_recv: %u, packetlost_pct = %f, sent_bitrate = %u, "
635 "recv_bitrate = %u, delta-delta-avg = %" GST_STIME_FORMAT ", "
636 "delta-delta-change: %f", bits_sent, bits_recv, stats->packets_sent,
637 packets_recv, stats->packet_loss_pct, stats->bitrate_sent,
638 stats->bitrate_recv, GST_STIME_ARGS (stats->avg_delta_of_delta),
639 stats->avg_delta_of_delta_change);
640 }
641
642 RTPTWCCStats *
rtp_twcc_stats_new(void)643 rtp_twcc_stats_new (void)
644 {
645 RTPTWCCStats *stats = g_new0 (RTPTWCCStats, 1);
646 stats->packets = g_array_new (FALSE, FALSE, sizeof (RTPTWCCPacket));
647 stats->last_local_ts = GST_CLOCK_TIME_NONE;
648 stats->last_remote_ts = GST_CLOCK_TIME_NONE;
649 stats->avg_delta_of_delta = GST_CLOCK_STIME_NONE;
650 stats->window_size = 300 * GST_MSECOND; /* FIXME: could be configurable? */
651 return stats;
652 }
653
654 void
rtp_twcc_stats_free(RTPTWCCStats * stats)655 rtp_twcc_stats_free (RTPTWCCStats * stats)
656 {
657 g_array_unref (stats->packets);
658 g_free (stats);
659 }
660
661 static GstStructure *
rtp_twcc_stats_get_stats_structure(RTPTWCCStats * stats)662 rtp_twcc_stats_get_stats_structure (RTPTWCCStats * stats)
663 {
664 return gst_structure_new ("RTPTWCCStats",
665 "bitrate-sent", G_TYPE_UINT, stats->bitrate_sent,
666 "bitrate-recv", G_TYPE_UINT, stats->bitrate_recv,
667 "packets-sent", G_TYPE_UINT, stats->packets_sent,
668 "packets-recv", G_TYPE_UINT, stats->packets_recv,
669 "packet-loss-pct", G_TYPE_DOUBLE, stats->packet_loss_pct,
670 "avg-delta-of-delta", G_TYPE_INT64, stats->avg_delta_of_delta, NULL);
671 }
672
673 GstStructure *
rtp_twcc_stats_process_packets(RTPTWCCStats * stats,GArray * twcc_packets)674 rtp_twcc_stats_process_packets (RTPTWCCStats * stats, GArray * twcc_packets)
675 {
676 rtp_twcc_stats_calculate_stats (stats, twcc_packets);
677 g_array_append_vals (stats->packets, twcc_packets->data, twcc_packets->len);
678 rtp_twcc_stats_calculate_windowed_stats (stats);
679 return rtp_twcc_stats_get_stats_structure (stats);
680 }
681