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