webrtcstats: PLI/FIR/NACK direction are the opposite of the media
[platform/upstream/gstreamer.git] / ext / webrtc / gstwebrtcstats.c
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
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
50 monotonic_time_as_double_milliseconds (void)
51 {
52   return g_get_monotonic_time () / 1000.0;
53 }
54
55 static void
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 *
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 static void
85 _gst_structure_take_structure (GstStructure * s, const char *fieldname,
86     GstStructure ** value_s)
87 {
88   GValue v = G_VALUE_INIT;
89
90   g_value_init (&v, GST_TYPE_STRUCTURE);
91   g_value_take_boxed (&v, *value_s);
92
93   gst_structure_take_value (s, fieldname, &v);
94
95   *value_s = NULL;
96 }
97
98 #define CLOCK_RATE_VALUE_TO_SECONDS(v,r) ((double) v / (double) clock_rate)
99 #define FIXED_16_16_TO_DOUBLE(v) ((double) ((v & 0xffff0000) >> 16) + ((v & 0xffff) / 65536.0))
100 #define FIXED_32_32_TO_DOUBLE(v) ((double) ((v & G_GUINT64_CONSTANT (0xffffffff00000000)) >> 32) + ((v & G_GUINT64_CONSTANT (0xffffffff)) / 4294967296.0))
101
102 /* https://www.w3.org/TR/webrtc-stats/#remoteinboundrtpstats-dict* */
103 static gboolean
104 _get_stats_from_remote_rtp_source_stats (GstWebRTCBin * webrtc,
105     TransportStream * stream, const GstStructure * source_stats,
106     guint ssrc, guint clock_rate, const gchar * codec_id,
107     const gchar * transport_id, GstStructure * s)
108 {
109   gboolean have_rb = FALSE, internal = FALSE;
110   int lost;
111   GstStructure *r_in;
112   gchar *r_in_id, *out_id;
113   guint32 rtt;
114   guint fraction_lost, jitter;
115   double ts;
116
117   gst_structure_get_double (s, "timestamp", &ts);
118   gst_structure_get (source_stats, "internal", G_TYPE_BOOLEAN, &internal,
119       "have-rb", G_TYPE_BOOLEAN, &have_rb, NULL);
120
121   /* This isn't what we're looking for */
122   if (internal == TRUE || have_rb == FALSE)
123     return FALSE;
124
125   r_in_id = g_strdup_printf ("rtp-remote-inbound-stream-stats_%u", ssrc);
126   out_id = g_strdup_printf ("rtp-outbound-stream-stats_%u", ssrc);
127
128   r_in = gst_structure_new_empty (r_in_id);
129   _set_base_stats (r_in, GST_WEBRTC_STATS_REMOTE_INBOUND_RTP, ts, r_in_id);
130
131   /* RTCRtpStreamStats */
132   gst_structure_set (r_in, "local-id", G_TYPE_STRING, out_id, NULL);
133   gst_structure_set (r_in, "ssrc", G_TYPE_UINT, ssrc, NULL);
134   gst_structure_set (r_in, "codec-id", G_TYPE_STRING, codec_id, NULL);
135   gst_structure_set (r_in, "transport-id", G_TYPE_STRING, transport_id, NULL);
136   /* To be added: kind */
137
138   /* RTCReceivedRtpStreamStats */
139
140   if (gst_structure_get_int (source_stats, "rb-packetslost", &lost))
141     gst_structure_set (r_in, "packets-lost", G_TYPE_INT, lost, NULL);
142
143   if (clock_rate && gst_structure_get_uint (source_stats, "rb-jitter", &jitter))
144     gst_structure_set (r_in, "jitter", G_TYPE_DOUBLE,
145         CLOCK_RATE_VALUE_TO_SECONDS (jitter, clock_rate), NULL);
146
147   /* RTCReceivedRtpStreamStats:
148
149      unsigned long long  packetsReceived;
150      unsigned long      packetsDiscarded;
151      unsigned long      packetsRepaired;
152      unsigned long      burstPacketsLost;
153      unsigned long      burstPacketsDiscarded;
154      unsigned long      burstLossCount;
155      unsigned long      burstDiscardCount;
156      double             burstLossRate;
157      double             burstDiscardRate;
158      double             gapLossRate;
159      double             gapDiscardRate;
160
161      Can't be implemented frame re-assembly happens after rtpbin:
162
163      unsigned long        framesDropped;
164      unsigned long        partialFramesLost;
165      unsigned long        fullFramesLost;
166    */
167
168   /* RTCRemoteInboundRTPStreamStats */
169
170   if (gst_structure_get_uint (source_stats, "rb-fractionlost", &fraction_lost))
171     gst_structure_set (r_in, "fraction-lost", G_TYPE_DOUBLE,
172         (double) fraction_lost / 256.0, NULL);
173
174   if (gst_structure_get_uint (source_stats, "rb-round-trip", &rtt)) {
175     /* 16.16 fixed point to double */
176     double val = FIXED_16_16_TO_DOUBLE (rtt);
177     gst_structure_set (r_in, "round-trip-time", G_TYPE_DOUBLE, val, NULL);
178   }
179
180   /* RTCRemoteInboundRTPStreamStats:
181
182      To be added:
183
184      DOMString            localId;
185      double               totalRoundTripTime;
186      unsigned long long   reportsReceived;
187      unsigned long long   roundTripTimeMeasurements;
188    */
189
190   gst_structure_set (r_in, "gst-rtpsource-stats", GST_TYPE_STRUCTURE,
191       source_stats, NULL);
192
193   _gst_structure_take_structure (s, r_in_id, &r_in);
194
195   g_free (r_in_id);
196   g_free (out_id);
197
198   return TRUE;
199 }
200
201 /* https://www.w3.org/TR/webrtc-stats/#inboundrtpstats-dict*
202    https://www.w3.org/TR/webrtc-stats/#outboundrtpstats-dict* */
203 static void
204 _get_stats_from_rtp_source_stats (GstWebRTCBin * webrtc,
205     TransportStream * stream, const GstStructure * source_stats,
206     const gchar * codec_id, const gchar * transport_id, GstStructure * s)
207 {
208   guint ssrc, fir, pli, nack, jitter;
209   int clock_rate;
210   guint64 packets, bytes;
211   gboolean internal;
212   double ts;
213
214   gst_structure_get_double (s, "timestamp", &ts);
215   gst_structure_get (source_stats, "ssrc", G_TYPE_UINT, &ssrc, "clock-rate",
216       G_TYPE_INT, &clock_rate, "internal", G_TYPE_BOOLEAN, &internal, NULL);
217
218   if (internal) {
219     GstStructure *out;
220     gchar *out_id, *r_in_id;
221
222     out_id = g_strdup_printf ("rtp-outbound-stream-stats_%u", ssrc);
223
224     out = gst_structure_new_empty (out_id);
225     _set_base_stats (out, GST_WEBRTC_STATS_OUTBOUND_RTP, ts, out_id);
226
227     /* RTCStreamStats */
228     gst_structure_set (out, "ssrc", G_TYPE_UINT, ssrc, NULL);
229     gst_structure_set (out, "codec-id", G_TYPE_STRING, codec_id, NULL);
230     gst_structure_set (out, "transport-id", G_TYPE_STRING, transport_id, NULL);
231     /* To be added: kind */
232
233
234     /* RTCSentRtpStreamStats  */
235     if (gst_structure_get_uint64 (source_stats, "octets-sent", &bytes))
236       gst_structure_set (out, "bytes-sent", G_TYPE_UINT64, bytes, NULL);
237     if (gst_structure_get_uint64 (source_stats, "packets-sent", &packets))
238       gst_structure_set (out, "packets-sent", G_TYPE_UINT64, packets, NULL);
239
240     /* RTCOutboundRTPStreamStats */
241
242     if (gst_structure_get_uint (source_stats, "recv-fir-count", &fir))
243       gst_structure_set (out, "fir-count", G_TYPE_UINT, fir, NULL);
244     if (gst_structure_get_uint (source_stats, "recv-pli-count", &pli))
245       gst_structure_set (out, "pli-count", G_TYPE_UINT, pli, NULL);
246     if (gst_structure_get_uint (source_stats, "recv-nack-count", &nack))
247       gst_structure_set (out, "nack-count", G_TYPE_UINT, nack, NULL);
248     /* XXX: mediaType, trackId, sliCount, qpSum */
249
250     r_in_id = g_strdup_printf ("rtp-remote-inbound-stream-stats_%u", ssrc);
251     if (gst_structure_has_field (s, r_in_id))
252       gst_structure_set (out, "remote-id", G_TYPE_STRING, r_in_id, NULL);
253     g_free (r_in_id);
254
255     /*  RTCOutboundRTPStreamStats:
256
257        To be added:
258
259        unsigned long        sliCount;
260        unsigned long        rtxSsrc;
261        DOMString            mediaSourceId;
262        DOMString            senderId;
263        DOMString            remoteId;
264        DOMString            rid;
265        DOMHighResTimeStamp  lastPacketSentTimestamp;
266        unsigned long long   headerBytesSent;
267        unsigned long        packetsDiscardedOnSend;
268        unsigned long long   bytesDiscardedOnSend;
269        unsigned long        fecPacketsSent;
270        unsigned long long   retransmittedPacketsSent;
271        unsigned long long   retransmittedBytesSent;
272        double               averageRtcpInterval;
273        record<USVString, unsigned long long> perDscpPacketsSent;
274
275        Not relevant because webrtcbin doesn't encode:
276
277        double               targetBitrate;
278        unsigned long long   totalEncodedBytesTarget;
279        unsigned long        frameWidth;
280        unsigned long        frameHeight;
281        unsigned long        frameBitDepth;
282        double               framesPerSecond;
283        unsigned long        framesSent;
284        unsigned long        hugeFramesSent;
285        unsigned long        framesEncoded;
286        unsigned long        keyFramesEncoded;
287        unsigned long        framesDiscardedOnSend;
288        unsigned long long   qpSum;
289        unsigned long long   totalSamplesSent;
290        unsigned long long   samplesEncodedWithSilk;
291        unsigned long long   samplesEncodedWithCelt;
292        boolean              voiceActivityFlag;
293        double               totalEncodeTime;
294        double               totalPacketSendDelay;
295        RTCQualityLimitationReason                 qualityLimitationReason;
296        record<DOMString, double> qualityLimitationDurations;
297        unsigned long        qualityLimitationResolutionChanges;
298        DOMString            encoderImplementation;
299      */
300
301     /* Store the raw stats from GStreamer into the structure for advanced
302      * information.
303      */
304     gst_structure_set (out, "gst-rtpsource-stats", GST_TYPE_STRUCTURE,
305         source_stats, NULL);
306
307     _gst_structure_take_structure (s, out_id, &out);
308
309     g_free (out_id);
310   } else {
311     GstStructure *in, *r_out;
312     gchar *r_out_id, *in_id;
313     gboolean have_sr = FALSE;
314     GstStructure *jb_stats = NULL;
315     guint i;
316     guint64 jb_lost, duplicates, late, rtx_success;
317
318     gst_structure_get (source_stats, "have-sr", G_TYPE_BOOLEAN, &have_sr, NULL);
319
320     for (i = 0; i < stream->remote_ssrcmap->len; i++) {
321       SsrcMapItem *item = g_ptr_array_index (stream->remote_ssrcmap, i);
322
323       if (item->ssrc == ssrc) {
324         GObject *jb = g_weak_ref_get (&item->rtpjitterbuffer);
325
326         if (jb) {
327           g_object_get (jb, "stats", &jb_stats, NULL);
328           g_object_unref (jb);
329         }
330         break;
331       }
332     }
333
334     if (jb_stats)
335       gst_structure_get (jb_stats, "num-lost", G_TYPE_UINT64, &jb_lost,
336           "num-duplicates", G_TYPE_UINT64, &duplicates, "num-late",
337           G_TYPE_UINT64, &late, "rtx-success-count", G_TYPE_UINT64,
338           &rtx_success, NULL);
339
340     in_id = g_strdup_printf ("rtp-inbound-stream-stats_%u", ssrc);
341     r_out_id = g_strdup_printf ("rtp-remote-outbound-stream-stats_%u", ssrc);
342
343     in = gst_structure_new_empty (in_id);
344     _set_base_stats (in, GST_WEBRTC_STATS_INBOUND_RTP, ts, in_id);
345
346     /* RTCRtpStreamStats */
347     gst_structure_set (in, "ssrc", G_TYPE_UINT, ssrc, NULL);
348     gst_structure_set (in, "codec-id", G_TYPE_STRING, codec_id, NULL);
349     gst_structure_set (in, "transport-id", G_TYPE_STRING, transport_id, NULL);
350     /* To be added: kind */
351
352
353     /* RTCReceivedRtpStreamStats */
354
355     if (gst_structure_get_uint64 (source_stats, "packets-received", &packets))
356       gst_structure_set (in, "packets-received", G_TYPE_UINT64, packets, NULL);
357     if (jb_stats)
358       gst_structure_set (in, "packets-lost", G_TYPE_UINT64, jb_lost, NULL);
359     if (gst_structure_get_uint (source_stats, "jitter", &jitter))
360       gst_structure_set (in, "jitter", G_TYPE_DOUBLE,
361           CLOCK_RATE_VALUE_TO_SECONDS (jitter, clock_rate), NULL);
362
363     if (jb_stats)
364       gst_structure_set (in, "packets-discarded", G_TYPE_UINT64, late,
365           "packets-repaired", G_TYPE_UINT64, rtx_success, NULL);
366
367     /*
368        RTCReceivedRtpStreamStats
369
370        To be added:
371
372        unsigned long long   burstPacketsLost;
373        unsigned long long   burstPacketsDiscarded;
374        unsigned long        burstLossCount;
375        unsigned long        burstDiscardCount;
376        double               burstLossRate;
377        double               burstDiscardRate;
378        double               gapLossRate;
379        double               gapDiscardRate;
380
381        Not relevant because webrtcbin doesn't decode:
382
383        unsigned long        framesDropped;
384        unsigned long        partialFramesLost;
385        unsigned long        fullFramesLost;
386      */
387
388     /* RTCInboundRtpStreamStats */
389     gst_structure_set (in, "remote-id", G_TYPE_STRING, r_out_id, NULL);
390
391     if (gst_structure_get_uint64 (source_stats, "octets-received", &bytes))
392       gst_structure_set (in, "bytes-received", G_TYPE_UINT64, bytes, NULL);
393
394     if (gst_structure_get_uint (source_stats, "sent-fir-count", &fir))
395       gst_structure_set (in, "fir-count", G_TYPE_UINT, fir, NULL);
396     if (gst_structure_get_uint (source_stats, "sent-pli-count", &pli))
397       gst_structure_set (in, "pli-count", G_TYPE_UINT, pli, NULL);
398     if (gst_structure_get_uint (source_stats, "sent-nack-count", &nack))
399       gst_structure_set (in, "nack-count", G_TYPE_UINT, nack, NULL);
400     if (jb_stats)
401       gst_structure_set (in, "packets-duplicated", G_TYPE_UINT64, duplicates,
402           NULL);
403
404     /* RTCInboundRtpStreamStats:
405
406        To be added:
407
408        required DOMString   receiverId;
409        double               averageRtcpInterval;
410        unsigned long long   headerBytesReceived;
411        unsigned long long   fecPacketsReceived;
412        unsigned long long   fecPacketsDiscarded;
413        unsigned long long   bytesReceived;
414        unsigned long long   packetsFailedDecryption;
415        record<USVString, unsigned long long> perDscpPacketsReceived;
416        unsigned long        nackCount;
417        unsigned long        firCount;
418        unsigned long        pliCount;
419        unsigned long        sliCount;
420        double               jitterBufferDelay;
421
422        Not relevant because webrtcbin doesn't decode or depayload:
423        unsigned long        framesDecoded;
424        unsigned long        keyFramesDecoded;
425        unsigned long        frameWidth;
426        unsigned long        frameHeight;
427        unsigned long        frameBitDepth;
428        double               framesPerSecond;
429        unsigned long long   qpSum;
430        double               totalDecodeTime;
431        double               totalInterFrameDelay;
432        double               totalSquaredInterFrameDelay;
433        boolean              voiceActivityFlag;
434        DOMHighResTimeStamp  lastPacketReceivedTimestamp;
435        double               totalProcessingDelay;
436        DOMHighResTimeStamp  estimatedPlayoutTimestamp;
437        unsigned long long   jitterBufferEmittedCount;
438        unsigned long long   totalSamplesReceived;
439        unsigned long long   totalSamplesDecoded;
440        unsigned long long   samplesDecodedWithSilk;
441        unsigned long long   samplesDecodedWithCelt;
442        unsigned long long   concealedSamples;
443        unsigned long long   silentConcealedSamples;
444        unsigned long long   concealmentEvents;
445        unsigned long long   insertedSamplesForDeceleration;
446        unsigned long long   removedSamplesForAcceleration;
447        double               audioLevel;
448        double               totalAudioEnergy;
449        double               totalSamplesDuration;
450        unsigned long        framesReceived;
451        DOMString            decoderImplementation;
452      */
453
454     r_out = gst_structure_new_empty (r_out_id);
455     _set_base_stats (r_out, GST_WEBRTC_STATS_REMOTE_OUTBOUND_RTP, ts, r_out_id);
456     /* RTCStreamStats */
457     gst_structure_set (r_out, "ssrc", G_TYPE_UINT, ssrc, NULL);
458     gst_structure_set (r_out, "codec-id", G_TYPE_STRING, codec_id, NULL);
459     gst_structure_set (r_out, "transport-id", G_TYPE_STRING, transport_id,
460         NULL);
461     /* XXX: mediaType, trackId */
462
463     /* RTCSentRtpStreamStats */
464
465     if (have_sr) {
466       guint sr_bytes, sr_packets;
467
468       if (gst_structure_get_uint (source_stats, "sr-octet-count", &sr_bytes))
469         gst_structure_set (r_out, "bytes-sent", G_TYPE_UINT, sr_bytes, NULL);
470       if (gst_structure_get_uint (source_stats, "sr-packet-count", &sr_packets))
471         gst_structure_set (r_out, "packets-sent", G_TYPE_UINT, sr_packets,
472             NULL);
473     }
474
475     /* RTCSentRtpStreamStats:
476
477        To be added:
478
479        unsigned long        rtxSsrc;
480        DOMString            mediaSourceId;
481        DOMString            senderId;
482        DOMString            remoteId;
483        DOMString            rid;
484        DOMHighResTimeStamp  lastPacketSentTimestamp;
485        unsigned long long   headerBytesSent;
486        unsigned long        packetsDiscardedOnSend;
487        unsigned long long   bytesDiscardedOnSend;
488        unsigned long        fecPacketsSent;
489        unsigned long long   retransmittedPacketsSent;
490        unsigned long long   retransmittedBytesSent;
491        double               averageRtcpInterval;
492        unsigned long        sliCount;
493
494        Can't be implemented because we don't decode:
495
496        double               targetBitrate;
497        unsigned long long   totalEncodedBytesTarget;
498        unsigned long        frameWidth;
499        unsigned long        frameHeight;
500        unsigned long        frameBitDepth;
501        double               framesPerSecond;
502        unsigned long        framesSent;
503        unsigned long        hugeFramesSent;
504        unsigned long        framesEncoded;
505        unsigned long        keyFramesEncoded;
506        unsigned long        framesDiscardedOnSend;
507        unsigned long long   qpSum;
508        unsigned long long   totalSamplesSent;
509        unsigned long long   samplesEncodedWithSilk;
510        unsigned long long   samplesEncodedWithCelt;
511        boolean              voiceActivityFlag;
512        double               totalEncodeTime;
513        double               totalPacketSendDelay;
514        RTCQualityLimitationReason                 qualityLimitationReason;
515        record<DOMString, double> qualityLimitationDurations;
516        unsigned long        qualityLimitationResolutionChanges;
517        record<USVString, unsigned long long> perDscpPacketsSent;
518        DOMString            encoderImplementation;
519      */
520
521     /* RTCRemoteOutboundRtpStreamStats */
522
523     if (have_sr) {
524       guint64 ntptime;
525       if (gst_structure_get_uint64 (source_stats, "sr-ntptime", &ntptime)) {
526         /* 16.16 fixed point to double */
527         double val = FIXED_32_32_TO_DOUBLE (ntptime);
528         gst_structure_set (r_out, "remote-timestamp", G_TYPE_DOUBLE, val, NULL);
529       }
530     } else {
531       /* default values */
532       gst_structure_set (r_out, "remote-timestamp", G_TYPE_DOUBLE, 0.0, NULL);
533     }
534
535     gst_structure_set (r_out, "local-id", G_TYPE_STRING, in_id, NULL);
536
537     /* To be added:
538        reportsSent
539      */
540
541     /* Store the raw stats from GStreamer into the structure for advanced
542      * information.
543      */
544     _gst_structure_take_structure (in, "gst-rtpjitterbuffer-stats", &jb_stats);
545
546     gst_structure_set (in, "gst-rtpsource-stats", GST_TYPE_STRUCTURE,
547         source_stats, NULL);
548
549     _gst_structure_take_structure (s, in_id, &in);
550     _gst_structure_take_structure (s, r_out_id, &r_out);
551
552     g_free (in_id);
553     g_free (r_out_id);
554   }
555 }
556
557 /* https://www.w3.org/TR/webrtc-stats/#candidatepair-dict* */
558 static gchar *
559 _get_stats_from_ice_transport (GstWebRTCBin * webrtc,
560     GstWebRTCICETransport * transport, GstStructure * s)
561 {
562   GstStructure *stats;
563   gchar *id;
564   double ts;
565
566   gst_structure_get_double (s, "timestamp", &ts);
567
568   id = g_strdup_printf ("ice-candidate-pair_%s", GST_OBJECT_NAME (transport));
569   stats = gst_structure_new_empty (id);
570   _set_base_stats (stats, GST_WEBRTC_STATS_TRANSPORT, ts, id);
571
572 /* XXX: RTCIceCandidatePairStats
573     DOMString                     transportId;
574     DOMString                     localCandidateId;
575     DOMString                     remoteCandidateId;
576     RTCStatsIceCandidatePairState state;
577     unsigned long long            priority;
578     boolean                       nominated;
579     unsigned long                 packetsSent;
580     unsigned long                 packetsReceived;
581     unsigned long long            bytesSent;
582     unsigned long long            bytesReceived;
583     DOMHighResTimeStamp           lastPacketSentTimestamp;
584     DOMHighResTimeStamp           lastPacketReceivedTimestamp;
585     DOMHighResTimeStamp           firstRequestTimestamp;
586     DOMHighResTimeStamp           lastRequestTimestamp;
587     DOMHighResTimeStamp           lastResponseTimestamp;
588     double                        totalRoundTripTime;
589     double                        currentRoundTripTime;
590     double                        availableOutgoingBitrate;
591     double                        availableIncomingBitrate;
592     unsigned long                 circuitBreakerTriggerCount;
593     unsigned long long            requestsReceived;
594     unsigned long long            requestsSent;
595     unsigned long long            responsesReceived;
596     unsigned long long            responsesSent;
597     unsigned long long            retransmissionsReceived;
598     unsigned long long            retransmissionsSent;
599     unsigned long long            consentRequestsSent;
600     DOMHighResTimeStamp           consentExpiredTimestamp;
601 */
602
603 /* XXX: RTCIceCandidateStats
604     DOMString           transportId;
605     boolean             isRemote;
606     RTCNetworkType      networkType;
607     DOMString           ip;
608     long                port;
609     DOMString           protocol;
610     RTCIceCandidateType candidateType;
611     long                priority;
612     DOMString           url;
613     DOMString           relayProtocol;
614     boolean             deleted = false;
615 };
616 */
617
618   gst_structure_set (s, id, GST_TYPE_STRUCTURE, stats, NULL);
619   gst_structure_free (stats);
620
621   return id;
622 }
623
624 /* https://www.w3.org/TR/webrtc-stats/#dom-rtctransportstats */
625 static gchar *
626 _get_stats_from_dtls_transport (GstWebRTCBin * webrtc,
627     GstWebRTCDTLSTransport * transport, GstStructure * s)
628 {
629   GstStructure *stats;
630   gchar *id;
631   double ts;
632   gchar *ice_id;
633
634   gst_structure_get_double (s, "timestamp", &ts);
635
636   id = g_strdup_printf ("transport-stats_%s", GST_OBJECT_NAME (transport));
637   stats = gst_structure_new_empty (id);
638   _set_base_stats (stats, GST_WEBRTC_STATS_TRANSPORT, ts, id);
639
640 /* XXX: RTCTransportStats
641     unsigned long         packetsSent;
642     unsigned long         packetsReceived;
643     unsigned long long    bytesSent;
644     unsigned long long    bytesReceived;
645     DOMString             rtcpTransportStatsId;
646     RTCIceRole            iceRole;
647     RTCDtlsTransportState dtlsState;
648     DOMString             selectedCandidatePairId;
649     DOMString             localCertificateId;
650     DOMString             remoteCertificateId;
651 */
652
653 /* XXX: RTCCertificateStats
654     DOMString fingerprint;
655     DOMString fingerprintAlgorithm;
656     DOMString base64Certificate;
657     DOMString issuerCertificateId;
658 */
659
660 /* XXX: RTCIceCandidateStats
661     DOMString           transportId;
662     boolean             isRemote;
663     DOMString           ip;
664     long                port;
665     DOMString           protocol;
666     RTCIceCandidateType candidateType;
667     long                priority;
668     DOMString           url;
669     boolean             deleted = false;
670 */
671
672   gst_structure_set (s, id, GST_TYPE_STRUCTURE, stats, NULL);
673   gst_structure_free (stats);
674
675   ice_id = _get_stats_from_ice_transport (webrtc, transport->transport, s);
676   g_free (ice_id);
677
678   return id;
679 }
680
681 static void
682 _get_stats_from_transport_channel (GstWebRTCBin * webrtc,
683     TransportStream * stream, const gchar * codec_id, guint ssrc,
684     guint clock_rate, GstStructure * s)
685 {
686   GstWebRTCDTLSTransport *transport;
687   GObject *rtp_session;
688   GstStructure *rtp_stats;
689   GValueArray *source_stats;
690   gchar *transport_id;
691   double ts;
692   int i;
693
694   gst_structure_get_double (s, "timestamp", &ts);
695
696   transport = stream->transport;
697   if (!transport)
698     return;
699
700   g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
701       stream->session_id, &rtp_session);
702   g_object_get (rtp_session, "stats", &rtp_stats, NULL);
703
704   gst_structure_get (rtp_stats, "source-stats", G_TYPE_VALUE_ARRAY,
705       &source_stats, NULL);
706
707   GST_DEBUG_OBJECT (webrtc, "retrieving rtp stream stats from transport %"
708       GST_PTR_FORMAT " rtp session %" GST_PTR_FORMAT " with %u rtp sources, "
709       "transport %" GST_PTR_FORMAT, stream, rtp_session, source_stats->n_values,
710       transport);
711
712   transport_id = _get_stats_from_dtls_transport (webrtc, transport, s);
713
714   /* construct stats objects */
715   for (i = 0; i < source_stats->n_values; i++) {
716     const GstStructure *stats;
717     const GValue *val = g_value_array_get_nth (source_stats, i);
718     guint stats_ssrc = 0;
719
720     stats = gst_value_get_structure (val);
721
722     /* skip foreign sources */
723     gst_structure_get (stats, "ssrc", G_TYPE_UINT, &stats_ssrc, NULL);
724     if (gst_structure_get_uint (stats, "ssrc", &stats_ssrc) &&
725         ssrc == stats_ssrc)
726       _get_stats_from_rtp_source_stats (webrtc, stream, stats, codec_id,
727           transport_id, s);
728     else if (gst_structure_get_uint (stats, "rb-ssrc", &stats_ssrc) &&
729         ssrc == stats_ssrc)
730       _get_stats_from_remote_rtp_source_stats (webrtc, stream, stats, ssrc,
731           clock_rate, codec_id, transport_id, s);
732   }
733
734   g_object_unref (rtp_session);
735   gst_structure_free (rtp_stats);
736   g_value_array_free (source_stats);
737   g_free (transport_id);
738 }
739
740 /* https://www.w3.org/TR/webrtc-stats/#codec-dict* */
741 static void
742 _get_codec_stats_from_pad (GstWebRTCBin * webrtc, GstPad * pad,
743     GstStructure * s, gchar ** out_id, guint * out_ssrc, guint * out_clock_rate)
744 {
745   GstStructure *stats;
746   GstCaps *caps;
747   gchar *id;
748   double ts;
749   guint ssrc = 0;
750   gint clock_rate = 0;
751
752   gst_structure_get_double (s, "timestamp", &ts);
753
754   stats = gst_structure_new_empty ("unused");
755   id = g_strdup_printf ("codec-stats-%s", GST_OBJECT_NAME (pad));
756   _set_base_stats (stats, GST_WEBRTC_STATS_CODEC, ts, id);
757
758   caps = gst_pad_get_current_caps (pad);
759   if (caps && gst_caps_is_fixed (caps)) {
760     GstStructure *caps_s = gst_caps_get_structure (caps, 0);
761     gint pt;
762
763     if (gst_structure_get_int (caps_s, "payload", &pt))
764       gst_structure_set (stats, "payload-type", G_TYPE_UINT, pt, NULL);
765
766     if (gst_structure_get_int (caps_s, "clock-rate", &clock_rate))
767       gst_structure_set (stats, "clock-rate", G_TYPE_UINT, clock_rate, NULL);
768
769     if (gst_structure_get_uint (caps_s, "ssrc", &ssrc))
770       gst_structure_set (stats, "ssrc", G_TYPE_UINT, ssrc, NULL);
771
772     /* FIXME: codecType, mimeType, channels, sdpFmtpLine, implementation, transportId */
773   }
774
775   if (caps)
776     gst_caps_unref (caps);
777
778   gst_structure_set (s, id, GST_TYPE_STRUCTURE, stats, NULL);
779   gst_structure_free (stats);
780
781   if (out_id)
782     *out_id = id;
783   else
784     g_free (id);
785
786   if (out_ssrc)
787     *out_ssrc = ssrc;
788
789   if (out_clock_rate)
790     *out_clock_rate = clock_rate;
791 }
792
793 static gboolean
794 _get_stats_from_pad (GstWebRTCBin * webrtc, GstPad * pad, GstStructure * s)
795 {
796   GstWebRTCBinPad *wpad = GST_WEBRTC_BIN_PAD (pad);
797   TransportStream *stream;
798   gchar *codec_id;
799   guint ssrc, clock_rate;
800
801   _get_codec_stats_from_pad (webrtc, pad, s, &codec_id, &ssrc, &clock_rate);
802
803   if (!wpad->trans)
804     goto out;
805
806   stream = WEBRTC_TRANSCEIVER (wpad->trans)->stream;
807   if (!stream)
808     goto out;
809
810   _get_stats_from_transport_channel (webrtc, stream, codec_id, ssrc,
811       clock_rate, s);
812
813 out:
814   g_free (codec_id);
815   return TRUE;
816 }
817
818 GstStructure *
819 gst_webrtc_bin_create_stats (GstWebRTCBin * webrtc, GstPad * pad)
820 {
821   GstStructure *s = gst_structure_new_empty ("application/x-webrtc-stats");
822   double ts = monotonic_time_as_double_milliseconds ();
823   GstStructure *pc_stats;
824
825   _init_debug ();
826
827   gst_structure_set (s, "timestamp", G_TYPE_DOUBLE, ts, NULL);
828
829   /* FIXME: better unique IDs */
830   /* FIXME: rate limitting stat updates? */
831   /* FIXME: all stats need to be kept forever */
832
833   GST_DEBUG_OBJECT (webrtc, "updating stats at time %f", ts);
834
835   if ((pc_stats = _get_peer_connection_stats (webrtc))) {
836     const gchar *id = "peer-connection-stats";
837     _set_base_stats (pc_stats, GST_WEBRTC_STATS_PEER_CONNECTION, ts, id);
838     gst_structure_set (s, id, GST_TYPE_STRUCTURE, pc_stats, NULL);
839     gst_structure_free (pc_stats);
840   }
841
842   if (pad)
843     _get_stats_from_pad (webrtc, pad, s);
844   else
845     gst_element_foreach_pad (GST_ELEMENT (webrtc),
846         (GstElementForeachPadFunc) _get_stats_from_pad, s);
847
848   gst_structure_remove_field (s, "timestamp");
849
850   return s;
851 }