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