d56c17dbd90b0b53bbfaafc5dec796c58a0320dd
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / 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, const GstStructure * twcc_stats,
561     GstStructure * s)
562 {
563   GstStructure *stats;
564   gchar *id;
565   double ts;
566
567   gst_structure_get_double (s, "timestamp", &ts);
568
569   id = g_strdup_printf ("ice-candidate-pair_%s", GST_OBJECT_NAME (transport));
570   stats = gst_structure_new_empty (id);
571   _set_base_stats (stats, GST_WEBRTC_STATS_TRANSPORT, ts, id);
572
573 /* XXX: RTCIceCandidatePairStats
574     DOMString                     transportId;
575     DOMString                     localCandidateId;
576     DOMString                     remoteCandidateId;
577     RTCStatsIceCandidatePairState state;
578     unsigned long long            priority;
579     boolean                       nominated;
580     unsigned long                 packetsSent;
581     unsigned long                 packetsReceived;
582     unsigned long long            bytesSent;
583     unsigned long long            bytesReceived;
584     DOMHighResTimeStamp           lastPacketSentTimestamp;
585     DOMHighResTimeStamp           lastPacketReceivedTimestamp;
586     DOMHighResTimeStamp           firstRequestTimestamp;
587     DOMHighResTimeStamp           lastRequestTimestamp;
588     DOMHighResTimeStamp           lastResponseTimestamp;
589     double                        totalRoundTripTime;
590     double                        currentRoundTripTime;
591     double                        availableOutgoingBitrate;
592     double                        availableIncomingBitrate;
593     unsigned long                 circuitBreakerTriggerCount;
594     unsigned long long            requestsReceived;
595     unsigned long long            requestsSent;
596     unsigned long long            responsesReceived;
597     unsigned long long            responsesSent;
598     unsigned long long            retransmissionsReceived;
599     unsigned long long            retransmissionsSent;
600     unsigned long long            consentRequestsSent;
601     DOMHighResTimeStamp           consentExpiredTimestamp;
602 */
603
604 /* XXX: RTCIceCandidateStats
605     DOMString           transportId;
606     boolean             isRemote;
607     RTCNetworkType      networkType;
608     DOMString           ip;
609     long                port;
610     DOMString           protocol;
611     RTCIceCandidateType candidateType;
612     long                priority;
613     DOMString           url;
614     DOMString           relayProtocol;
615     boolean             deleted = false;
616 };
617 */
618
619   /* XXX: these stats are at the rtp session level but there isn't a specific
620    * stats structure for that. The RTCIceCandidatePairStats is the closest with
621    * the 'availableIncomingBitrate' and 'availableOutgoingBitrate' fields
622    */
623   if (twcc_stats)
624     gst_structure_set (stats, "gst-twcc-stats", GST_TYPE_STRUCTURE, twcc_stats,
625         NULL);
626
627   gst_structure_set (s, id, GST_TYPE_STRUCTURE, stats, NULL);
628   gst_structure_free (stats);
629
630   return id;
631 }
632
633 /* https://www.w3.org/TR/webrtc-stats/#dom-rtctransportstats */
634 static gchar *
635 _get_stats_from_dtls_transport (GstWebRTCBin * webrtc,
636     GstWebRTCDTLSTransport * transport, const GstStructure * twcc_stats,
637     GstStructure * s)
638 {
639   GstStructure *stats;
640   gchar *id;
641   double ts;
642   gchar *ice_id;
643
644   gst_structure_get_double (s, "timestamp", &ts);
645
646   id = g_strdup_printf ("transport-stats_%s", GST_OBJECT_NAME (transport));
647   stats = gst_structure_new_empty (id);
648   _set_base_stats (stats, GST_WEBRTC_STATS_TRANSPORT, ts, id);
649
650 /* XXX: RTCTransportStats
651     unsigned long         packetsSent;
652     unsigned long         packetsReceived;
653     unsigned long long    bytesSent;
654     unsigned long long    bytesReceived;
655     DOMString             rtcpTransportStatsId;
656     RTCIceRole            iceRole;
657     RTCDtlsTransportState dtlsState;
658     DOMString             selectedCandidatePairId;
659     DOMString             localCertificateId;
660     DOMString             remoteCertificateId;
661 */
662
663 /* XXX: RTCCertificateStats
664     DOMString fingerprint;
665     DOMString fingerprintAlgorithm;
666     DOMString base64Certificate;
667     DOMString issuerCertificateId;
668 */
669
670 /* XXX: RTCIceCandidateStats
671     DOMString           transportId;
672     boolean             isRemote;
673     DOMString           ip;
674     long                port;
675     DOMString           protocol;
676     RTCIceCandidateType candidateType;
677     long                priority;
678     DOMString           url;
679     boolean             deleted = false;
680 */
681
682   gst_structure_set (s, id, GST_TYPE_STRUCTURE, stats, NULL);
683   gst_structure_free (stats);
684
685   ice_id =
686       _get_stats_from_ice_transport (webrtc, transport->transport, twcc_stats,
687       s);
688   g_free (ice_id);
689
690   return id;
691 }
692
693 static void
694 _get_stats_from_transport_channel (GstWebRTCBin * webrtc,
695     TransportStream * stream, const gchar * codec_id, guint ssrc,
696     guint clock_rate, GstStructure * s)
697 {
698   GstWebRTCDTLSTransport *transport;
699   GObject *rtp_session;
700   GObject *gst_rtp_session;
701   GstStructure *rtp_stats, *twcc_stats;
702   GValueArray *source_stats;
703   gchar *transport_id;
704   double ts;
705   int i;
706
707   gst_structure_get_double (s, "timestamp", &ts);
708
709   transport = stream->transport;
710   if (!transport)
711     return;
712
713   g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
714       stream->session_id, &rtp_session);
715   g_object_get (rtp_session, "stats", &rtp_stats, NULL);
716   g_signal_emit_by_name (webrtc->rtpbin, "get-session",
717       stream->session_id, &gst_rtp_session);
718   g_object_get (gst_rtp_session, "twcc-stats", &twcc_stats, NULL);
719
720   gst_structure_get (rtp_stats, "source-stats", G_TYPE_VALUE_ARRAY,
721       &source_stats, NULL);
722
723   GST_DEBUG_OBJECT (webrtc, "retrieving rtp stream stats from transport %"
724       GST_PTR_FORMAT " rtp session %" GST_PTR_FORMAT " with %u rtp sources, "
725       "transport %" GST_PTR_FORMAT, stream, rtp_session, source_stats->n_values,
726       transport);
727
728   transport_id =
729       _get_stats_from_dtls_transport (webrtc, transport, twcc_stats, s);
730
731   /* construct stats objects */
732   for (i = 0; i < source_stats->n_values; i++) {
733     const GstStructure *stats;
734     const GValue *val = g_value_array_get_nth (source_stats, i);
735     guint stats_ssrc = 0;
736
737     stats = gst_value_get_structure (val);
738
739     /* skip foreign sources */
740     gst_structure_get (stats, "ssrc", G_TYPE_UINT, &stats_ssrc, NULL);
741     if (gst_structure_get_uint (stats, "ssrc", &stats_ssrc) &&
742         ssrc == stats_ssrc)
743       _get_stats_from_rtp_source_stats (webrtc, stream, stats, codec_id,
744           transport_id, s);
745     else if (gst_structure_get_uint (stats, "rb-ssrc", &stats_ssrc) &&
746         ssrc == stats_ssrc)
747       _get_stats_from_remote_rtp_source_stats (webrtc, stream, stats, ssrc,
748           clock_rate, codec_id, transport_id, s);
749   }
750
751   g_object_unref (rtp_session);
752   g_object_unref (gst_rtp_session);
753   gst_structure_free (rtp_stats);
754   if (twcc_stats)
755     gst_structure_free (twcc_stats);
756   g_value_array_free (source_stats);
757   g_free (transport_id);
758 }
759
760 /* https://www.w3.org/TR/webrtc-stats/#codec-dict* */
761 static void
762 _get_codec_stats_from_pad (GstWebRTCBin * webrtc, GstPad * pad,
763     GstStructure * s, gchar ** out_id, guint * out_ssrc, guint * out_clock_rate)
764 {
765   GstStructure *stats;
766   GstCaps *caps;
767   gchar *id;
768   double ts;
769   guint ssrc = 0;
770   gint clock_rate = 0;
771
772   gst_structure_get_double (s, "timestamp", &ts);
773
774   stats = gst_structure_new_empty ("unused");
775   id = g_strdup_printf ("codec-stats-%s", GST_OBJECT_NAME (pad));
776   _set_base_stats (stats, GST_WEBRTC_STATS_CODEC, ts, id);
777
778   caps = gst_pad_get_current_caps (pad);
779   if (caps && gst_caps_is_fixed (caps)) {
780     GstStructure *caps_s = gst_caps_get_structure (caps, 0);
781     gint pt;
782
783     if (gst_structure_get_int (caps_s, "payload", &pt))
784       gst_structure_set (stats, "payload-type", G_TYPE_UINT, pt, NULL);
785
786     if (gst_structure_get_int (caps_s, "clock-rate", &clock_rate))
787       gst_structure_set (stats, "clock-rate", G_TYPE_UINT, clock_rate, NULL);
788
789     if (gst_structure_get_uint (caps_s, "ssrc", &ssrc))
790       gst_structure_set (stats, "ssrc", G_TYPE_UINT, ssrc, NULL);
791
792     /* FIXME: codecType, mimeType, channels, sdpFmtpLine, implementation, transportId */
793   }
794
795   if (caps)
796     gst_caps_unref (caps);
797
798   gst_structure_set (s, id, GST_TYPE_STRUCTURE, stats, NULL);
799   gst_structure_free (stats);
800
801   if (out_id)
802     *out_id = id;
803   else
804     g_free (id);
805
806   if (out_ssrc)
807     *out_ssrc = ssrc;
808
809   if (out_clock_rate)
810     *out_clock_rate = clock_rate;
811 }
812
813 static gboolean
814 _get_stats_from_pad (GstWebRTCBin * webrtc, GstPad * pad, GstStructure * s)
815 {
816   GstWebRTCBinPad *wpad = GST_WEBRTC_BIN_PAD (pad);
817   TransportStream *stream;
818   gchar *codec_id;
819   guint ssrc, clock_rate;
820
821   _get_codec_stats_from_pad (webrtc, pad, s, &codec_id, &ssrc, &clock_rate);
822
823   if (!wpad->trans)
824     goto out;
825
826   stream = WEBRTC_TRANSCEIVER (wpad->trans)->stream;
827   if (!stream)
828     goto out;
829
830   _get_stats_from_transport_channel (webrtc, stream, codec_id, ssrc,
831       clock_rate, s);
832
833 out:
834   g_free (codec_id);
835   return TRUE;
836 }
837
838 GstStructure *
839 gst_webrtc_bin_create_stats (GstWebRTCBin * webrtc, GstPad * pad)
840 {
841   GstStructure *s = gst_structure_new_empty ("application/x-webrtc-stats");
842   double ts = monotonic_time_as_double_milliseconds ();
843   GstStructure *pc_stats;
844
845   _init_debug ();
846
847   gst_structure_set (s, "timestamp", G_TYPE_DOUBLE, ts, NULL);
848
849   /* FIXME: better unique IDs */
850   /* FIXME: rate limitting stat updates? */
851   /* FIXME: all stats need to be kept forever */
852
853   GST_DEBUG_OBJECT (webrtc, "updating stats at time %f", ts);
854
855   if ((pc_stats = _get_peer_connection_stats (webrtc))) {
856     const gchar *id = "peer-connection-stats";
857     _set_base_stats (pc_stats, GST_WEBRTC_STATS_PEER_CONNECTION, ts, id);
858     gst_structure_set (s, id, GST_TYPE_STRUCTURE, pc_stats, NULL);
859     gst_structure_free (pc_stats);
860   }
861
862   if (pad)
863     _get_stats_from_pad (webrtc, pad, s);
864   else
865     gst_element_foreach_pad (GST_ELEMENT (webrtc),
866         (GstElementForeachPadFunc) _get_stats_from_pad, s);
867
868   gst_structure_remove_field (s, "timestamp");
869
870   return s;
871 }