1 /* GStreamer
2 * Copyright (C) 2017 Matthew Waters <matthew@centricular.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23
24 /* for GValueArray... */
25 #define GLIB_DISABLE_DEPRECATION_WARNINGS
26
27 #include "gstwebrtcstats.h"
28 #include "gstwebrtcbin.h"
29 #include "transportstream.h"
30 #include "transportreceivebin.h"
31 #include "utils.h"
32 #include "webrtctransceiver.h"
33
34 #define GST_CAT_DEFAULT gst_webrtc_stats_debug
35 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
36
37 static void
_init_debug(void)38 _init_debug (void)
39 {
40 static gsize _init = 0;
41
42 if (g_once_init_enter (&_init)) {
43 GST_DEBUG_CATEGORY_INIT (gst_webrtc_stats_debug, "webrtcstats", 0,
44 "webrtcstats");
45 g_once_init_leave (&_init, 1);
46 }
47 }
48
49 static double
monotonic_time_as_double_milliseconds(void)50 monotonic_time_as_double_milliseconds (void)
51 {
52 return g_get_monotonic_time () / 1000.0;
53 }
54
55 static void
_set_base_stats(GstStructure * s,GstWebRTCStatsType type,double ts,const char * id)56 _set_base_stats (GstStructure * s, GstWebRTCStatsType type, double ts,
57 const char *id)
58 {
59 gchar *name = _enum_value_to_string (GST_TYPE_WEBRTC_STATS_TYPE,
60 type);
61
62 g_return_if_fail (name != NULL);
63
64 gst_structure_set_name (s, name);
65 gst_structure_set (s, "type", GST_TYPE_WEBRTC_STATS_TYPE, type, "timestamp",
66 G_TYPE_DOUBLE, ts, "id", G_TYPE_STRING, id, NULL);
67
68 g_free (name);
69 }
70
71 static GstStructure *
_get_peer_connection_stats(GstWebRTCBin * webrtc)72 _get_peer_connection_stats (GstWebRTCBin * webrtc)
73 {
74 GstStructure *s = gst_structure_new_empty ("unused");
75
76 /* FIXME: datachannel */
77 gst_structure_set (s, "data-channels-opened", G_TYPE_UINT, 0,
78 "data-channels-closed", G_TYPE_UINT, 0, "data-channels-requested",
79 G_TYPE_UINT, 0, "data-channels-accepted", G_TYPE_UINT, 0, NULL);
80
81 return s;
82 }
83
84 #define CLOCK_RATE_VALUE_TO_SECONDS(v,r) ((double) v / (double) clock_rate)
85
86 /* https://www.w3.org/TR/webrtc-stats/#inboundrtpstats-dict*
87 https://www.w3.org/TR/webrtc-stats/#outboundrtpstats-dict* */
88 static void
_get_stats_from_rtp_source_stats(GstWebRTCBin * webrtc,const GstStructure * source_stats,const gchar * codec_id,const gchar * transport_id,GstStructure * s)89 _get_stats_from_rtp_source_stats (GstWebRTCBin * webrtc,
90 const GstStructure * source_stats, const gchar * codec_id,
91 const gchar * transport_id, GstStructure * s)
92 {
93 GstStructure *in, *out, *r_in, *r_out;
94 gchar *in_id, *out_id, *r_in_id, *r_out_id;
95 guint ssrc, fir, pli, nack, jitter;
96 int lost, clock_rate;
97 guint64 packets, bytes;
98 gboolean have_rb = FALSE, sent_rb = FALSE;
99 double ts;
100
101 gst_structure_get_double (s, "timestamp", &ts);
102 gst_structure_get_uint (source_stats, "ssrc", &ssrc);
103 gst_structure_get (source_stats, "have-rb", G_TYPE_BOOLEAN, &have_rb,
104 "sent_rb", G_TYPE_BOOLEAN, &sent_rb, "clock-rate", G_TYPE_INT,
105 &clock_rate, NULL);
106
107 in_id = g_strdup_printf ("rtp-inbound-stream-stats_%u", ssrc);
108 out_id = g_strdup_printf ("rtp-outbound-stream-stats_%u", ssrc);
109 r_in_id = g_strdup_printf ("rtp-remote-inbound-stream-stats_%u", ssrc);
110 r_out_id = g_strdup_printf ("rtp-remote-outbound-stream-stats_%u", ssrc);
111
112 in = gst_structure_new_empty (in_id);
113 _set_base_stats (in, GST_WEBRTC_STATS_INBOUND_RTP, ts, in_id);
114
115 /* RTCStreamStats */
116 gst_structure_set (in, "ssrc", G_TYPE_UINT, ssrc, NULL);
117 gst_structure_set (in, "codec-id", G_TYPE_STRING, codec_id, NULL);
118 gst_structure_set (in, "transport-id", G_TYPE_STRING, transport_id, NULL);
119 if (gst_structure_get_uint (source_stats, "recv-fir-count", &fir))
120 gst_structure_set (in, "fir-count", G_TYPE_UINT, fir, NULL);
121 if (gst_structure_get_uint (source_stats, "recv-pli-count", &pli))
122 gst_structure_set (in, "pli-count", G_TYPE_UINT, pli, NULL);
123 if (gst_structure_get_uint (source_stats, "recv-nack-count", &nack))
124 gst_structure_set (in, "nack-count", G_TYPE_UINT, nack, NULL);
125 /* XXX: mediaType, trackId, sliCount, qpSum */
126
127 /* RTCReceivedRTPStreamStats */
128 if (gst_structure_get_uint64 (source_stats, "packets-received", &packets))
129 gst_structure_set (in, "packets-received", G_TYPE_UINT64, packets, NULL);
130 if (gst_structure_get_uint64 (source_stats, "octets-received", &bytes))
131 gst_structure_set (in, "bytes-received", G_TYPE_UINT64, bytes, NULL);
132 if (gst_structure_get_int (source_stats, "packets-lost", &lost))
133 gst_structure_set (in, "packets-lost", G_TYPE_INT, lost, NULL);
134 if (gst_structure_get_uint (source_stats, "jitter", &jitter))
135 gst_structure_set (in, "jitter", G_TYPE_DOUBLE,
136 CLOCK_RATE_VALUE_TO_SECONDS (jitter, clock_rate), NULL);
137 /*
138 RTCReceivedRTPStreamStats
139 double fractionLost;
140 unsigned long packetsDiscarded;
141 unsigned long packetsFailedDecryption;
142 unsigned long packetsRepaired;
143 unsigned long burstPacketsLost;
144 unsigned long burstPacketsDiscarded;
145 unsigned long burstLossCount;
146 unsigned long burstDiscardCount;
147 double burstLossRate;
148 double burstDiscardRate;
149 double gapLossRate;
150 double gapDiscardRate;
151 */
152
153 /* RTCInboundRTPStreamStats */
154 gst_structure_set (in, "remote-id", G_TYPE_STRING, r_out_id, NULL);
155 /* XXX: framesDecoded, lastPacketReceivedTimestamp */
156
157 r_in = gst_structure_new_empty (r_in_id);
158 _set_base_stats (r_in, GST_WEBRTC_STATS_REMOTE_INBOUND_RTP, ts, r_in_id);
159
160 /* RTCStreamStats */
161 gst_structure_set (r_in, "ssrc", G_TYPE_UINT, ssrc, NULL);
162 gst_structure_set (r_in, "codec-id", G_TYPE_STRING, codec_id, NULL);
163 gst_structure_set (r_in, "transport-id", G_TYPE_STRING, transport_id, NULL);
164 /* XXX: mediaType, trackId, sliCount, qpSum */
165
166 /* RTCReceivedRTPStreamStats */
167 if (sent_rb) {
168 if (gst_structure_get_uint (source_stats, "sent-rb-jitter", &jitter))
169 gst_structure_set (r_in, "jitter", G_TYPE_DOUBLE,
170 CLOCK_RATE_VALUE_TO_SECONDS (jitter, clock_rate), NULL);
171 if (gst_structure_get_int (source_stats, "sent-rb-packetslost", &lost))
172 gst_structure_set (r_in, "packets-lost", G_TYPE_INT, lost, NULL);
173 /* packetsReceived, bytesReceived */
174 } else {
175 /* default values */
176 gst_structure_set (r_in, "jitter", G_TYPE_DOUBLE, 0.0, "packets-lost",
177 G_TYPE_INT, 0, NULL);
178 }
179 /* XXX: RTCReceivedRTPStreamStats
180 double fractionLost;
181 unsigned long packetsDiscarded;
182 unsigned long packetsFailedDecryption;
183 unsigned long packetsRepaired;
184 unsigned long burstPacketsLost;
185 unsigned long burstPacketsDiscarded;
186 unsigned long burstLossCount;
187 unsigned long burstDiscardCount;
188 double burstLossRate;
189 double burstDiscardRate;
190 double gapLossRate;
191 double gapDiscardRate;
192 */
193
194 /* RTCRemoteInboundRTPStreamStats */
195 gst_structure_set (r_in, "local-id", G_TYPE_STRING, out_id, NULL);
196 if (have_rb) {
197 guint32 rtt;
198 if (gst_structure_get_uint (source_stats, "rb-round-trip", &rtt)) {
199 /* 16.16 fixed point to double */
200 double val =
201 (double) ((rtt & 0xffff0000) >> 16) + ((rtt & 0xffff) / 65536.0);
202 gst_structure_set (r_in, "round-trip-time", G_TYPE_DOUBLE, val, NULL);
203 }
204 } else {
205 /* default values */
206 gst_structure_set (r_in, "round-trip-time", G_TYPE_DOUBLE, 0.0, NULL);
207 }
208 /* XXX: framesDecoded, lastPacketReceivedTimestamp */
209
210 out = gst_structure_new_empty (out_id);
211 _set_base_stats (out, GST_WEBRTC_STATS_OUTBOUND_RTP, ts, out_id);
212
213 /* RTCStreamStats */
214 gst_structure_set (out, "ssrc", G_TYPE_UINT, ssrc, NULL);
215 gst_structure_set (out, "codec-id", G_TYPE_STRING, codec_id, NULL);
216 gst_structure_set (out, "transport-id", G_TYPE_STRING, transport_id, NULL);
217 if (gst_structure_get_uint (source_stats, "sent-fir-count", &fir))
218 gst_structure_set (out, "fir-count", G_TYPE_UINT, fir, NULL);
219 if (gst_structure_get_uint (source_stats, "sent-pli-count", &pli))
220 gst_structure_set (out, "pli-count", G_TYPE_UINT, pli, NULL);
221 if (gst_structure_get_uint (source_stats, "sent-nack-count", &nack))
222 gst_structure_set (out, "nack-count", G_TYPE_UINT, nack, NULL);
223 /* XXX: mediaType, trackId, sliCount, qpSum */
224
225 /* RTCSentRTPStreamStats */
226 if (gst_structure_get_uint64 (source_stats, "octets-sent", &bytes))
227 gst_structure_set (out, "bytes-sent", G_TYPE_UINT64, bytes, NULL);
228 if (gst_structure_get_uint64 (source_stats, "packets-sent", &packets))
229 gst_structure_set (out, "packets-sent", G_TYPE_UINT64, packets, NULL);
230 /* XXX:
231 unsigned long packetsDiscardedOnSend;
232 unsigned long long bytesDiscardedOnSend;
233 */
234
235 /* RTCOutboundRTPStreamStats */
236 gst_structure_set (out, "remote-id", G_TYPE_STRING, r_in_id, NULL);
237 /* XXX:
238 DOMHighResTimeStamp lastPacketSentTimestamp;
239 double targetBitrate;
240 unsigned long framesEncoded;
241 double totalEncodeTime;
242 double averageRTCPInterval;
243 */
244
245 r_out = gst_structure_new_empty (r_out_id);
246 _set_base_stats (r_out, GST_WEBRTC_STATS_REMOTE_OUTBOUND_RTP, ts, r_out_id);
247 /* RTCStreamStats */
248 gst_structure_set (r_out, "ssrc", G_TYPE_UINT, ssrc, NULL);
249 gst_structure_set (r_out, "codec-id", G_TYPE_STRING, codec_id, NULL);
250 gst_structure_set (r_out, "transport-id", G_TYPE_STRING, transport_id, NULL);
251 /* XXX: mediaType, trackId, sliCount, qpSum */
252
253 /* RTCSentRTPStreamStats */
254 /* if (gst_structure_get_uint64 (source_stats, "octets-sent", &bytes))
255 gst_structure_set (r_out, "bytes-sent", G_TYPE_UINT64, bytes, NULL);
256 if (gst_structure_get_uint64 (source_stats, "packets-sent", &packets))
257 gst_structure_set (r_out, "packets-sent", G_TYPE_UINT64, packets, NULL);*/
258 /* XXX:
259 unsigned long packetsDiscardedOnSend;
260 unsigned long long bytesDiscardedOnSend;
261 */
262
263 gst_structure_set (r_out, "local-id", G_TYPE_STRING, in_id, NULL);
264
265 gst_structure_set (s, in_id, GST_TYPE_STRUCTURE, in, NULL);
266 gst_structure_set (s, out_id, GST_TYPE_STRUCTURE, out, NULL);
267 gst_structure_set (s, r_in_id, GST_TYPE_STRUCTURE, r_in, NULL);
268 gst_structure_set (s, r_out_id, GST_TYPE_STRUCTURE, r_out, NULL);
269
270 gst_structure_free (in);
271 gst_structure_free (out);
272 gst_structure_free (r_in);
273 gst_structure_free (r_out);
274
275 g_free (in_id);
276 g_free (out_id);
277 g_free (r_in_id);
278 g_free (r_out_id);
279 }
280
281 /* https://www.w3.org/TR/webrtc-stats/#candidatepair-dict* */
282 static gchar *
_get_stats_from_ice_transport(GstWebRTCBin * webrtc,GstWebRTCICETransport * transport,GstStructure * s)283 _get_stats_from_ice_transport (GstWebRTCBin * webrtc,
284 GstWebRTCICETransport * transport, GstStructure * s)
285 {
286 GstStructure *stats;
287 gchar *id;
288 double ts;
289
290 gst_structure_get_double (s, "timestamp", &ts);
291
292 id = g_strdup_printf ("ice-candidate-pair_%s", GST_OBJECT_NAME (transport));
293 stats = gst_structure_new_empty (id);
294 _set_base_stats (stats, GST_WEBRTC_STATS_TRANSPORT, ts, id);
295
296 /* XXX: RTCIceCandidatePairStats
297 DOMString transportId;
298 DOMString localCandidateId;
299 DOMString remoteCandidateId;
300 RTCStatsIceCandidatePairState state;
301 unsigned long long priority;
302 boolean nominated;
303 unsigned long packetsSent;
304 unsigned long packetsReceived;
305 unsigned long long bytesSent;
306 unsigned long long bytesReceived;
307 DOMHighResTimeStamp lastPacketSentTimestamp;
308 DOMHighResTimeStamp lastPacketReceivedTimestamp;
309 DOMHighResTimeStamp firstRequestTimestamp;
310 DOMHighResTimeStamp lastRequestTimestamp;
311 DOMHighResTimeStamp lastResponseTimestamp;
312 double totalRoundTripTime;
313 double currentRoundTripTime;
314 double availableOutgoingBitrate;
315 double availableIncomingBitrate;
316 unsigned long circuitBreakerTriggerCount;
317 unsigned long long requestsReceived;
318 unsigned long long requestsSent;
319 unsigned long long responsesReceived;
320 unsigned long long responsesSent;
321 unsigned long long retransmissionsReceived;
322 unsigned long long retransmissionsSent;
323 unsigned long long consentRequestsSent;
324 DOMHighResTimeStamp consentExpiredTimestamp;
325 */
326
327 /* XXX: RTCIceCandidateStats
328 DOMString transportId;
329 boolean isRemote;
330 RTCNetworkType networkType;
331 DOMString ip;
332 long port;
333 DOMString protocol;
334 RTCIceCandidateType candidateType;
335 long priority;
336 DOMString url;
337 DOMString relayProtocol;
338 boolean deleted = false;
339 };
340 */
341
342 gst_structure_set (s, id, GST_TYPE_STRUCTURE, stats, NULL);
343 gst_structure_free (stats);
344
345 return id;
346 }
347
348 /* https://www.w3.org/TR/webrtc-stats/#dom-rtctransportstats */
349 static gchar *
_get_stats_from_dtls_transport(GstWebRTCBin * webrtc,GstWebRTCDTLSTransport * transport,GstStructure * s)350 _get_stats_from_dtls_transport (GstWebRTCBin * webrtc,
351 GstWebRTCDTLSTransport * transport, GstStructure * s)
352 {
353 GstStructure *stats;
354 gchar *id;
355 double ts;
356
357 gst_structure_get_double (s, "timestamp", &ts);
358
359 id = g_strdup_printf ("transport-stats_%s", GST_OBJECT_NAME (transport));
360 stats = gst_structure_new_empty (id);
361 _set_base_stats (stats, GST_WEBRTC_STATS_TRANSPORT, ts, id);
362
363 /* XXX: RTCTransportStats
364 unsigned long packetsSent;
365 unsigned long packetsReceived;
366 unsigned long long bytesSent;
367 unsigned long long bytesReceived;
368 DOMString rtcpTransportStatsId;
369 RTCIceRole iceRole;
370 RTCDtlsTransportState dtlsState;
371 DOMString selectedCandidatePairId;
372 DOMString localCertificateId;
373 DOMString remoteCertificateId;
374 */
375
376 /* XXX: RTCCertificateStats
377 DOMString fingerprint;
378 DOMString fingerprintAlgorithm;
379 DOMString base64Certificate;
380 DOMString issuerCertificateId;
381 */
382
383 /* XXX: RTCIceCandidateStats
384 DOMString transportId;
385 boolean isRemote;
386 DOMString ip;
387 long port;
388 DOMString protocol;
389 RTCIceCandidateType candidateType;
390 long priority;
391 DOMString url;
392 boolean deleted = false;
393 */
394
395 gst_structure_set (s, id, GST_TYPE_STRUCTURE, stats, NULL);
396 gst_structure_free (stats);
397
398 _get_stats_from_ice_transport (webrtc, transport->transport, s);
399
400 return id;
401 }
402
403 static void
_get_stats_from_transport_channel(GstWebRTCBin * webrtc,TransportStream * stream,const gchar * codec_id,guint ssrc,GstStructure * s)404 _get_stats_from_transport_channel (GstWebRTCBin * webrtc,
405 TransportStream * stream, const gchar * codec_id, guint ssrc,
406 GstStructure * s)
407 {
408 GstWebRTCDTLSTransport *transport;
409 GObject *rtp_session;
410 GstStructure *rtp_stats;
411 GValueArray *source_stats;
412 gchar *transport_id;
413 double ts;
414 int i;
415
416 gst_structure_get_double (s, "timestamp", &ts);
417
418 transport = stream->transport;
419 if (!transport)
420 transport = stream->transport;
421 if (!transport)
422 return;
423
424 g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
425 stream->session_id, &rtp_session);
426 g_object_get (rtp_session, "stats", &rtp_stats, NULL);
427
428 gst_structure_get (rtp_stats, "source-stats", G_TYPE_VALUE_ARRAY,
429 &source_stats, NULL);
430
431 GST_DEBUG_OBJECT (webrtc, "retrieving rtp stream stats from transport %"
432 GST_PTR_FORMAT " rtp session %" GST_PTR_FORMAT " with %u rtp sources, "
433 "transport %" GST_PTR_FORMAT, stream, rtp_session, source_stats->n_values,
434 transport);
435
436 transport_id = _get_stats_from_dtls_transport (webrtc, transport, s);
437
438 /* construct stats objects */
439 for (i = 0; i < source_stats->n_values; i++) {
440 const GstStructure *stats;
441 const GValue *val = g_value_array_get_nth (source_stats, i);
442 gboolean internal;
443 guint stats_ssrc = 0;
444
445 stats = gst_value_get_structure (val);
446
447 /* skip internal or foreign sources */
448 gst_structure_get (stats,
449 "internal", G_TYPE_BOOLEAN, &internal,
450 "ssrc", G_TYPE_UINT, &stats_ssrc, NULL);
451 if (internal || (ssrc && stats_ssrc && ssrc != stats_ssrc))
452 continue;
453
454 _get_stats_from_rtp_source_stats (webrtc, stats, codec_id, transport_id, s);
455 }
456
457 g_object_unref (rtp_session);
458 gst_structure_free (rtp_stats);
459 g_value_array_free (source_stats);
460 g_free (transport_id);
461 }
462
463 /* https://www.w3.org/TR/webrtc-stats/#codec-dict* */
464 static void
_get_codec_stats_from_pad(GstWebRTCBin * webrtc,GstPad * pad,GstStructure * s,gchar ** out_id,guint * out_ssrc)465 _get_codec_stats_from_pad (GstWebRTCBin * webrtc, GstPad * pad,
466 GstStructure * s, gchar ** out_id, guint * out_ssrc)
467 {
468 GstStructure *stats;
469 GstCaps *caps;
470 gchar *id;
471 double ts;
472 guint ssrc = 0;
473
474 gst_structure_get_double (s, "timestamp", &ts);
475
476 stats = gst_structure_new_empty ("unused");
477 id = g_strdup_printf ("codec-stats-%s", GST_OBJECT_NAME (pad));
478 _set_base_stats (stats, GST_WEBRTC_STATS_CODEC, ts, id);
479
480 caps = gst_pad_get_current_caps (pad);
481 if (caps && gst_caps_is_fixed (caps)) {
482 GstStructure *caps_s = gst_caps_get_structure (caps, 0);
483 gint pt, clock_rate;
484
485 if (gst_structure_get_int (caps_s, "payload", &pt))
486 gst_structure_set (stats, "payload-type", G_TYPE_UINT, pt, NULL);
487
488 if (gst_structure_get_int (caps_s, "clock-rate", &clock_rate))
489 gst_structure_set (stats, "clock-rate", G_TYPE_UINT, clock_rate, NULL);
490
491 if (gst_structure_get_uint (caps_s, "ssrc", &ssrc))
492 gst_structure_set (stats, "ssrc", G_TYPE_UINT, ssrc, NULL);
493
494 /* FIXME: codecType, mimeType, channels, sdpFmtpLine, implementation, transportId */
495 }
496
497 if (caps)
498 gst_caps_unref (caps);
499
500 gst_structure_set (s, id, GST_TYPE_STRUCTURE, stats, NULL);
501 gst_structure_free (stats);
502
503 if (out_id)
504 *out_id = id;
505 else
506 g_free (id);
507
508 if (out_ssrc)
509 *out_ssrc = ssrc;
510 }
511
512 static gboolean
_get_stats_from_pad(GstWebRTCBin * webrtc,GstPad * pad,GstStructure * s)513 _get_stats_from_pad (GstWebRTCBin * webrtc, GstPad * pad, GstStructure * s)
514 {
515 GstWebRTCBinPad *wpad = GST_WEBRTC_BIN_PAD (pad);
516 TransportStream *stream;
517 gchar *codec_id;
518 guint ssrc;
519
520 _get_codec_stats_from_pad (webrtc, pad, s, &codec_id, &ssrc);
521
522 if (!wpad->trans)
523 goto out;
524
525 stream = WEBRTC_TRANSCEIVER (wpad->trans)->stream;
526 if (!stream)
527 goto out;
528
529 _get_stats_from_transport_channel (webrtc, stream, codec_id, ssrc, s);
530
531 out:
532 g_free (codec_id);
533 return TRUE;
534 }
535
536 void
gst_webrtc_bin_update_stats(GstWebRTCBin * webrtc)537 gst_webrtc_bin_update_stats (GstWebRTCBin * webrtc)
538 {
539 GstStructure *s = gst_structure_new_empty ("application/x-webrtc-stats");
540 double ts = monotonic_time_as_double_milliseconds ();
541 GstStructure *pc_stats;
542
543 _init_debug ();
544
545 gst_structure_set (s, "timestamp", G_TYPE_DOUBLE, ts, NULL);
546
547 /* FIXME: better unique IDs */
548 /* FIXME: rate limitting stat updates? */
549 /* FIXME: all stats need to be kept forever */
550
551 GST_DEBUG_OBJECT (webrtc, "updating stats at time %f", ts);
552
553 if ((pc_stats = _get_peer_connection_stats (webrtc))) {
554 const gchar *id = "peer-connection-stats";
555 _set_base_stats (pc_stats, GST_WEBRTC_STATS_PEER_CONNECTION, ts, id);
556 gst_structure_set (s, id, GST_TYPE_STRUCTURE, pc_stats, NULL);
557 gst_structure_free (pc_stats);
558 }
559
560 gst_element_foreach_pad (GST_ELEMENT (webrtc),
561 (GstElementForeachPadFunc) _get_stats_from_pad, s);
562
563 gst_structure_remove_field (s, "timestamp");
564
565 if (webrtc->priv->stats)
566 gst_structure_free (webrtc->priv->stats);
567 webrtc->priv->stats = s;
568 }
569