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