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