gstreamer/ext/webrtc/gstwebrtcstats.c

872 lines
29 KiB
C
Raw Normal View History

/* GStreamer
* Copyright (C) 2017 Matthew Waters <matthew@centricular.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
/* for GValueArray... */
#define GLIB_DISABLE_DEPRECATION_WARNINGS
#include "gstwebrtcstats.h"
#include "gstwebrtcbin.h"
#include "transportstream.h"
#include "transportreceivebin.h"
#include "utils.h"
#include "webrtctransceiver.h"
#define GST_CAT_DEFAULT gst_webrtc_stats_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
static void
_init_debug (void)
{
static gsize _init = 0;
if (g_once_init_enter (&_init)) {
GST_DEBUG_CATEGORY_INIT (gst_webrtc_stats_debug, "webrtcstats", 0,
"webrtcstats");
g_once_init_leave (&_init, 1);
}
}
static double
monotonic_time_as_double_milliseconds (void)
{
return g_get_monotonic_time () / 1000.0;
}
static void
_set_base_stats (GstStructure * s, GstWebRTCStatsType type, double ts,
const char *id)
{
gchar *name = _enum_value_to_string (GST_TYPE_WEBRTC_STATS_TYPE,
type);
g_return_if_fail (name != NULL);
gst_structure_set_name (s, name);
gst_structure_set (s, "type", GST_TYPE_WEBRTC_STATS_TYPE, type, "timestamp",
G_TYPE_DOUBLE, ts, "id", G_TYPE_STRING, id, NULL);
g_free (name);
}
static GstStructure *
_get_peer_connection_stats (GstWebRTCBin * webrtc)
{
GstStructure *s = gst_structure_new_empty ("unused");
/* FIXME: datachannel */
gst_structure_set (s, "data-channels-opened", G_TYPE_UINT, 0,
"data-channels-closed", G_TYPE_UINT, 0, "data-channels-requested",
G_TYPE_UINT, 0, "data-channels-accepted", G_TYPE_UINT, 0, NULL);
return s;
}
static void
_gst_structure_take_structure (GstStructure * s, const char *fieldname,
GstStructure ** value_s)
{
GValue v = G_VALUE_INIT;
g_value_init (&v, GST_TYPE_STRUCTURE);
g_value_take_boxed (&v, *value_s);
gst_structure_take_value (s, fieldname, &v);
*value_s = NULL;
}
#define CLOCK_RATE_VALUE_TO_SECONDS(v,r) ((double) v / (double) clock_rate)
#define FIXED_16_16_TO_DOUBLE(v) ((double) ((v & 0xffff0000) >> 16) + ((v & 0xffff) / 65536.0))
#define FIXED_32_32_TO_DOUBLE(v) ((double) ((v & G_GUINT64_CONSTANT (0xffffffff00000000)) >> 32) + ((v & G_GUINT64_CONSTANT (0xffffffff)) / 4294967296.0))
/* https://www.w3.org/TR/webrtc-stats/#remoteinboundrtpstats-dict* */
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)
{
gboolean have_rb = FALSE, internal = FALSE;
int lost;
GstStructure *r_in;
gchar *r_in_id, *out_id;
guint32 rtt;
guint fraction_lost, jitter;
double ts;
gst_structure_get_double (s, "timestamp", &ts);
gst_structure_get (source_stats, "internal", G_TYPE_BOOLEAN, &internal,
"have-rb", G_TYPE_BOOLEAN, &have_rb, NULL);
/* This isn't what we're looking for */
if (internal == TRUE || have_rb == FALSE)
return FALSE;
r_in_id = g_strdup_printf ("rtp-remote-inbound-stream-stats_%u", ssrc);
out_id = g_strdup_printf ("rtp-outbound-stream-stats_%u", ssrc);
r_in = gst_structure_new_empty (r_in_id);
_set_base_stats (r_in, GST_WEBRTC_STATS_REMOTE_INBOUND_RTP, ts, r_in_id);
/* RTCRtpStreamStats */
gst_structure_set (r_in, "local-id", G_TYPE_STRING, out_id, NULL);
gst_structure_set (r_in, "ssrc", G_TYPE_UINT, ssrc, NULL);
gst_structure_set (r_in, "codec-id", G_TYPE_STRING, codec_id, NULL);
gst_structure_set (r_in, "transport-id", G_TYPE_STRING, transport_id, NULL);
/* To be added: kind */
/* RTCReceivedRtpStreamStats */
if (gst_structure_get_int (source_stats, "rb-packetslost", &lost))
gst_structure_set (r_in, "packets-lost", G_TYPE_INT, lost, NULL);
if (clock_rate && gst_structure_get_uint (source_stats, "rb-jitter", &jitter))
gst_structure_set (r_in, "jitter", G_TYPE_DOUBLE,
CLOCK_RATE_VALUE_TO_SECONDS (jitter, clock_rate), NULL);
/* RTCReceivedRtpStreamStats:
unsigned long long packetsReceived;
unsigned long packetsDiscarded;
unsigned long packetsRepaired;
unsigned long burstPacketsLost;
unsigned long burstPacketsDiscarded;
unsigned long burstLossCount;
unsigned long burstDiscardCount;
double burstLossRate;
double burstDiscardRate;
double gapLossRate;
double gapDiscardRate;
Can't be implemented frame re-assembly happens after rtpbin:
unsigned long framesDropped;
unsigned long partialFramesLost;
unsigned long fullFramesLost;
*/
/* RTCRemoteInboundRTPStreamStats */
if (gst_structure_get_uint (source_stats, "rb-fractionlost", &fraction_lost))
gst_structure_set (r_in, "fraction-lost", G_TYPE_DOUBLE,
(double) fraction_lost / 256.0, NULL);
if (gst_structure_get_uint (source_stats, "rb-round-trip", &rtt)) {
/* 16.16 fixed point to double */
double val = FIXED_16_16_TO_DOUBLE (rtt);
gst_structure_set (r_in, "round-trip-time", G_TYPE_DOUBLE, val, NULL);
}
/* RTCRemoteInboundRTPStreamStats:
To be added:
DOMString localId;
double totalRoundTripTime;
unsigned long long reportsReceived;
unsigned long long roundTripTimeMeasurements;
*/
gst_structure_set (r_in, "gst-rtpsource-stats", GST_TYPE_STRUCTURE,
source_stats, NULL);
_gst_structure_take_structure (s, r_in_id, &r_in);
g_free (r_in_id);
g_free (out_id);
return TRUE;
}
/* https://www.w3.org/TR/webrtc-stats/#inboundrtpstats-dict*
https://www.w3.org/TR/webrtc-stats/#outboundrtpstats-dict* */
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)
{
guint ssrc, fir, pli, nack, jitter;
int clock_rate;
guint64 packets, bytes;
gboolean internal;
double ts;
gst_structure_get_double (s, "timestamp", &ts);
gst_structure_get (source_stats, "ssrc", G_TYPE_UINT, &ssrc, "clock-rate",
G_TYPE_INT, &clock_rate, "internal", G_TYPE_BOOLEAN, &internal, NULL);
if (internal) {
GstStructure *out;
gchar *out_id, *r_in_id;
out_id = g_strdup_printf ("rtp-outbound-stream-stats_%u", ssrc);
out = gst_structure_new_empty (out_id);
_set_base_stats (out, GST_WEBRTC_STATS_OUTBOUND_RTP, ts, out_id);
/* RTCStreamStats */
gst_structure_set (out, "ssrc", G_TYPE_UINT, ssrc, NULL);
gst_structure_set (out, "codec-id", G_TYPE_STRING, codec_id, NULL);
gst_structure_set (out, "transport-id", G_TYPE_STRING, transport_id, NULL);
/* To be added: kind */
/* RTCSentRtpStreamStats */
if (gst_structure_get_uint64 (source_stats, "octets-sent", &bytes))
gst_structure_set (out, "bytes-sent", G_TYPE_UINT64, bytes, NULL);
if (gst_structure_get_uint64 (source_stats, "packets-sent", &packets))
gst_structure_set (out, "packets-sent", G_TYPE_UINT64, packets, NULL);
/* RTCOutboundRTPStreamStats */
if (gst_structure_get_uint (source_stats, "recv-fir-count", &fir))
gst_structure_set (out, "fir-count", G_TYPE_UINT, fir, NULL);
if (gst_structure_get_uint (source_stats, "recv-pli-count", &pli))
gst_structure_set (out, "pli-count", G_TYPE_UINT, pli, NULL);
if (gst_structure_get_uint (source_stats, "recv-nack-count", &nack))
gst_structure_set (out, "nack-count", G_TYPE_UINT, nack, NULL);
/* XXX: mediaType, trackId, sliCount, qpSum */
r_in_id = g_strdup_printf ("rtp-remote-inbound-stream-stats_%u", ssrc);
if (gst_structure_has_field (s, r_in_id))
gst_structure_set (out, "remote-id", G_TYPE_STRING, r_in_id, NULL);
g_free (r_in_id);
/* RTCOutboundRTPStreamStats:
To be added:
unsigned long sliCount;
unsigned long rtxSsrc;
DOMString mediaSourceId;
DOMString senderId;
DOMString remoteId;
DOMString rid;
DOMHighResTimeStamp lastPacketSentTimestamp;
unsigned long long headerBytesSent;
unsigned long packetsDiscardedOnSend;
unsigned long long bytesDiscardedOnSend;
unsigned long fecPacketsSent;
unsigned long long retransmittedPacketsSent;
unsigned long long retransmittedBytesSent;
double averageRtcpInterval;
record<USVString, unsigned long long> perDscpPacketsSent;
Not relevant because webrtcbin doesn't encode:
double targetBitrate;
unsigned long long totalEncodedBytesTarget;
unsigned long frameWidth;
unsigned long frameHeight;
unsigned long frameBitDepth;
double framesPerSecond;
unsigned long framesSent;
unsigned long hugeFramesSent;
unsigned long framesEncoded;
unsigned long keyFramesEncoded;
unsigned long framesDiscardedOnSend;
unsigned long long qpSum;
unsigned long long totalSamplesSent;
unsigned long long samplesEncodedWithSilk;
unsigned long long samplesEncodedWithCelt;
boolean voiceActivityFlag;
double totalEncodeTime;
double totalPacketSendDelay;
RTCQualityLimitationReason qualityLimitationReason;
record<DOMString, double> qualityLimitationDurations;
unsigned long qualityLimitationResolutionChanges;
DOMString encoderImplementation;
*/
/* Store the raw stats from GStreamer into the structure for advanced
* information.
*/
gst_structure_set (out, "gst-rtpsource-stats", GST_TYPE_STRUCTURE,
source_stats, NULL);
_gst_structure_take_structure (s, out_id, &out);
g_free (out_id);
} else {
GstStructure *in, *r_out;
gchar *r_out_id, *in_id;
gboolean have_sr = FALSE;
GstStructure *jb_stats = NULL;
guint i;
guint64 jb_lost, duplicates, late, rtx_success;
gst_structure_get (source_stats, "have-sr", G_TYPE_BOOLEAN, &have_sr, NULL);
for (i = 0; i < stream->remote_ssrcmap->len; i++) {
SsrcMapItem *item = g_ptr_array_index (stream->remote_ssrcmap, i);
if (item->ssrc == ssrc) {
GObject *jb = g_weak_ref_get (&item->rtpjitterbuffer);
if (jb) {
g_object_get (jb, "stats", &jb_stats, NULL);
g_object_unref (jb);
}
break;
}
}
if (jb_stats)
gst_structure_get (jb_stats, "num-lost", G_TYPE_UINT64, &jb_lost,
"num-duplicates", G_TYPE_UINT64, &duplicates, "num-late",
G_TYPE_UINT64, &late, "rtx-success-count", G_TYPE_UINT64,
&rtx_success, NULL);
in_id = g_strdup_printf ("rtp-inbound-stream-stats_%u", ssrc);
r_out_id = g_strdup_printf ("rtp-remote-outbound-stream-stats_%u", ssrc);
in = gst_structure_new_empty (in_id);
_set_base_stats (in, GST_WEBRTC_STATS_INBOUND_RTP, ts, in_id);
/* RTCRtpStreamStats */
gst_structure_set (in, "ssrc", G_TYPE_UINT, ssrc, NULL);
gst_structure_set (in, "codec-id", G_TYPE_STRING, codec_id, NULL);
gst_structure_set (in, "transport-id", G_TYPE_STRING, transport_id, NULL);
/* To be added: kind */
/* RTCReceivedRtpStreamStats */
if (gst_structure_get_uint64 (source_stats, "packets-received", &packets))
gst_structure_set (in, "packets-received", G_TYPE_UINT64, packets, NULL);
if (jb_stats)
gst_structure_set (in, "packets-lost", G_TYPE_UINT64, jb_lost, NULL);
if (gst_structure_get_uint (source_stats, "jitter", &jitter))
gst_structure_set (in, "jitter", G_TYPE_DOUBLE,
CLOCK_RATE_VALUE_TO_SECONDS (jitter, clock_rate), NULL);
if (jb_stats)
gst_structure_set (in, "packets-discarded", G_TYPE_UINT64, late,
"packets-repaired", G_TYPE_UINT64, rtx_success, NULL);
/*
RTCReceivedRtpStreamStats
To be added:
unsigned long long burstPacketsLost;
unsigned long long burstPacketsDiscarded;
unsigned long burstLossCount;
unsigned long burstDiscardCount;
double burstLossRate;
double burstDiscardRate;
double gapLossRate;
double gapDiscardRate;
Not relevant because webrtcbin doesn't decode:
unsigned long framesDropped;
unsigned long partialFramesLost;
unsigned long fullFramesLost;
*/
/* RTCInboundRtpStreamStats */
gst_structure_set (in, "remote-id", G_TYPE_STRING, r_out_id, NULL);
if (gst_structure_get_uint64 (source_stats, "octets-received", &bytes))
gst_structure_set (in, "bytes-received", G_TYPE_UINT64, bytes, NULL);
if (gst_structure_get_uint (source_stats, "sent-fir-count", &fir))
gst_structure_set (in, "fir-count", G_TYPE_UINT, fir, NULL);
if (gst_structure_get_uint (source_stats, "sent-pli-count", &pli))
gst_structure_set (in, "pli-count", G_TYPE_UINT, pli, NULL);
if (gst_structure_get_uint (source_stats, "sent-nack-count", &nack))
gst_structure_set (in, "nack-count", G_TYPE_UINT, nack, NULL);
if (jb_stats)
gst_structure_set (in, "packets-duplicated", G_TYPE_UINT64, duplicates,
NULL);
/* RTCInboundRtpStreamStats:
To be added:
required DOMString receiverId;
double averageRtcpInterval;
unsigned long long headerBytesReceived;
unsigned long long fecPacketsReceived;
unsigned long long fecPacketsDiscarded;
unsigned long long bytesReceived;
unsigned long long packetsFailedDecryption;
record<USVString, unsigned long long> perDscpPacketsReceived;
unsigned long nackCount;
unsigned long firCount;
unsigned long pliCount;
unsigned long sliCount;
double jitterBufferDelay;
Not relevant because webrtcbin doesn't decode or depayload:
unsigned long framesDecoded;
unsigned long keyFramesDecoded;
unsigned long frameWidth;
unsigned long frameHeight;
unsigned long frameBitDepth;
double framesPerSecond;
unsigned long long qpSum;
double totalDecodeTime;
double totalInterFrameDelay;
double totalSquaredInterFrameDelay;
boolean voiceActivityFlag;
DOMHighResTimeStamp lastPacketReceivedTimestamp;
double totalProcessingDelay;
DOMHighResTimeStamp estimatedPlayoutTimestamp;
unsigned long long jitterBufferEmittedCount;
unsigned long long totalSamplesReceived;
unsigned long long totalSamplesDecoded;
unsigned long long samplesDecodedWithSilk;
unsigned long long samplesDecodedWithCelt;
unsigned long long concealedSamples;
unsigned long long silentConcealedSamples;
unsigned long long concealmentEvents;
unsigned long long insertedSamplesForDeceleration;
unsigned long long removedSamplesForAcceleration;
double audioLevel;
double totalAudioEnergy;
double totalSamplesDuration;
unsigned long framesReceived;
DOMString decoderImplementation;
*/
r_out = gst_structure_new_empty (r_out_id);
_set_base_stats (r_out, GST_WEBRTC_STATS_REMOTE_OUTBOUND_RTP, ts, r_out_id);
/* RTCStreamStats */
gst_structure_set (r_out, "ssrc", G_TYPE_UINT, ssrc, NULL);
gst_structure_set (r_out, "codec-id", G_TYPE_STRING, codec_id, NULL);
gst_structure_set (r_out, "transport-id", G_TYPE_STRING, transport_id,
NULL);
/* XXX: mediaType, trackId */
/* RTCSentRtpStreamStats */
if (have_sr) {
guint sr_bytes, sr_packets;
if (gst_structure_get_uint (source_stats, "sr-octet-count", &sr_bytes))
gst_structure_set (r_out, "bytes-sent", G_TYPE_UINT, sr_bytes, NULL);
if (gst_structure_get_uint (source_stats, "sr-packet-count", &sr_packets))
gst_structure_set (r_out, "packets-sent", G_TYPE_UINT, sr_packets,
NULL);
}
/* RTCSentRtpStreamStats:
To be added:
unsigned long rtxSsrc;
DOMString mediaSourceId;
DOMString senderId;
DOMString remoteId;
DOMString rid;
DOMHighResTimeStamp lastPacketSentTimestamp;
unsigned long long headerBytesSent;
unsigned long packetsDiscardedOnSend;
unsigned long long bytesDiscardedOnSend;
unsigned long fecPacketsSent;
unsigned long long retransmittedPacketsSent;
unsigned long long retransmittedBytesSent;
double averageRtcpInterval;
unsigned long sliCount;
Can't be implemented because we don't decode:
double targetBitrate;
unsigned long long totalEncodedBytesTarget;
unsigned long frameWidth;
unsigned long frameHeight;
unsigned long frameBitDepth;
double framesPerSecond;
unsigned long framesSent;
unsigned long hugeFramesSent;
unsigned long framesEncoded;
unsigned long keyFramesEncoded;
unsigned long framesDiscardedOnSend;
unsigned long long qpSum;
unsigned long long totalSamplesSent;
unsigned long long samplesEncodedWithSilk;
unsigned long long samplesEncodedWithCelt;
boolean voiceActivityFlag;
double totalEncodeTime;
double totalPacketSendDelay;
RTCQualityLimitationReason qualityLimitationReason;
record<DOMString, double> qualityLimitationDurations;
unsigned long qualityLimitationResolutionChanges;
record<USVString, unsigned long long> perDscpPacketsSent;
DOMString encoderImplementation;
*/
/* RTCRemoteOutboundRtpStreamStats */
if (have_sr) {
guint64 ntptime;
if (gst_structure_get_uint64 (source_stats, "sr-ntptime", &ntptime)) {
/* 16.16 fixed point to double */
double val = FIXED_32_32_TO_DOUBLE (ntptime);
gst_structure_set (r_out, "remote-timestamp", G_TYPE_DOUBLE, val, NULL);
}
} else {
/* default values */
gst_structure_set (r_out, "remote-timestamp", G_TYPE_DOUBLE, 0.0, NULL);
}
gst_structure_set (r_out, "local-id", G_TYPE_STRING, in_id, NULL);
/* To be added:
reportsSent
*/
/* Store the raw stats from GStreamer into the structure for advanced
* information.
*/
_gst_structure_take_structure (in, "gst-rtpjitterbuffer-stats", &jb_stats);
gst_structure_set (in, "gst-rtpsource-stats", GST_TYPE_STRUCTURE,
source_stats, NULL);
_gst_structure_take_structure (s, in_id, &in);
_gst_structure_take_structure (s, r_out_id, &r_out);
g_free (in_id);
g_free (r_out_id);
}
}
/* https://www.w3.org/TR/webrtc-stats/#candidatepair-dict* */
static gchar *
_get_stats_from_ice_transport (GstWebRTCBin * webrtc,
GstWebRTCICETransport * transport, const GstStructure * twcc_stats,
GstStructure * s)
{
GstStructure *stats;
gchar *id;
double ts;
gst_structure_get_double (s, "timestamp", &ts);
id = g_strdup_printf ("ice-candidate-pair_%s", GST_OBJECT_NAME (transport));
stats = gst_structure_new_empty (id);
_set_base_stats (stats, GST_WEBRTC_STATS_TRANSPORT, ts, id);
/* XXX: RTCIceCandidatePairStats
DOMString transportId;
DOMString localCandidateId;
DOMString remoteCandidateId;
RTCStatsIceCandidatePairState state;
unsigned long long priority;
boolean nominated;
unsigned long packetsSent;
unsigned long packetsReceived;
unsigned long long bytesSent;
unsigned long long bytesReceived;
DOMHighResTimeStamp lastPacketSentTimestamp;
DOMHighResTimeStamp lastPacketReceivedTimestamp;
DOMHighResTimeStamp firstRequestTimestamp;
DOMHighResTimeStamp lastRequestTimestamp;
DOMHighResTimeStamp lastResponseTimestamp;
double totalRoundTripTime;
double currentRoundTripTime;
double availableOutgoingBitrate;
double availableIncomingBitrate;
unsigned long circuitBreakerTriggerCount;
unsigned long long requestsReceived;
unsigned long long requestsSent;
unsigned long long responsesReceived;
unsigned long long responsesSent;
unsigned long long retransmissionsReceived;
unsigned long long retransmissionsSent;
unsigned long long consentRequestsSent;
DOMHighResTimeStamp consentExpiredTimestamp;
*/
/* XXX: RTCIceCandidateStats
DOMString transportId;
boolean isRemote;
RTCNetworkType networkType;
DOMString ip;
long port;
DOMString protocol;
RTCIceCandidateType candidateType;
long priority;
DOMString url;
DOMString relayProtocol;
boolean deleted = false;
};
*/
/* XXX: these stats are at the rtp session level but there isn't a specific
* stats structure for that. The RTCIceCandidatePairStats is the closest with
* the 'availableIncomingBitrate' and 'availableOutgoingBitrate' fields
*/
if (twcc_stats)
gst_structure_set (stats, "gst-twcc-stats", GST_TYPE_STRUCTURE, twcc_stats,
NULL);
gst_structure_set (s, id, GST_TYPE_STRUCTURE, stats, NULL);
gst_structure_free (stats);
return id;
}
/* https://www.w3.org/TR/webrtc-stats/#dom-rtctransportstats */
static gchar *
_get_stats_from_dtls_transport (GstWebRTCBin * webrtc,
GstWebRTCDTLSTransport * transport, const GstStructure * twcc_stats,
GstStructure * s)
{
GstStructure *stats;
gchar *id;
double ts;
gchar *ice_id;
gst_structure_get_double (s, "timestamp", &ts);
id = g_strdup_printf ("transport-stats_%s", GST_OBJECT_NAME (transport));
stats = gst_structure_new_empty (id);
_set_base_stats (stats, GST_WEBRTC_STATS_TRANSPORT, ts, id);
/* XXX: RTCTransportStats
unsigned long packetsSent;
unsigned long packetsReceived;
unsigned long long bytesSent;
unsigned long long bytesReceived;
DOMString rtcpTransportStatsId;
RTCIceRole iceRole;
RTCDtlsTransportState dtlsState;
DOMString selectedCandidatePairId;
DOMString localCertificateId;
DOMString remoteCertificateId;
*/
/* XXX: RTCCertificateStats
DOMString fingerprint;
DOMString fingerprintAlgorithm;
DOMString base64Certificate;
DOMString issuerCertificateId;
*/
/* XXX: RTCIceCandidateStats
DOMString transportId;
boolean isRemote;
DOMString ip;
long port;
DOMString protocol;
RTCIceCandidateType candidateType;
long priority;
DOMString url;
boolean deleted = false;
*/
gst_structure_set (s, id, GST_TYPE_STRUCTURE, stats, NULL);
gst_structure_free (stats);
ice_id =
_get_stats_from_ice_transport (webrtc, transport->transport, twcc_stats,
s);
g_free (ice_id);
return id;
}
static void
_get_stats_from_transport_channel (GstWebRTCBin * webrtc,
TransportStream * stream, const gchar * codec_id, guint ssrc,
guint clock_rate, GstStructure * s)
{
GstWebRTCDTLSTransport *transport;
GObject *rtp_session;
GObject *gst_rtp_session;
GstStructure *rtp_stats, *twcc_stats;
GValueArray *source_stats;
gchar *transport_id;
double ts;
int i;
gst_structure_get_double (s, "timestamp", &ts);
transport = stream->transport;
if (!transport)
return;
g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
stream->session_id, &rtp_session);
g_object_get (rtp_session, "stats", &rtp_stats, NULL);
g_signal_emit_by_name (webrtc->rtpbin, "get-session",
stream->session_id, &gst_rtp_session);
g_object_get (gst_rtp_session, "twcc-stats", &twcc_stats, NULL);
gst_structure_get (rtp_stats, "source-stats", G_TYPE_VALUE_ARRAY,
&source_stats, NULL);
GST_DEBUG_OBJECT (webrtc, "retrieving rtp stream stats from transport %"
GST_PTR_FORMAT " rtp session %" GST_PTR_FORMAT " with %u rtp sources, "
"transport %" GST_PTR_FORMAT, stream, rtp_session, source_stats->n_values,
transport);
transport_id =
_get_stats_from_dtls_transport (webrtc, transport, twcc_stats, s);
/* construct stats objects */
for (i = 0; i < source_stats->n_values; i++) {
const GstStructure *stats;
const GValue *val = g_value_array_get_nth (source_stats, i);
guint stats_ssrc = 0;
stats = gst_value_get_structure (val);
/* skip foreign sources */
gst_structure_get (stats, "ssrc", G_TYPE_UINT, &stats_ssrc, NULL);
if (gst_structure_get_uint (stats, "ssrc", &stats_ssrc) &&
ssrc == stats_ssrc)
_get_stats_from_rtp_source_stats (webrtc, stream, stats, codec_id,
transport_id, s);
else if (gst_structure_get_uint (stats, "rb-ssrc", &stats_ssrc) &&
ssrc == stats_ssrc)
_get_stats_from_remote_rtp_source_stats (webrtc, stream, stats, ssrc,
clock_rate, codec_id, transport_id, s);
}
g_object_unref (rtp_session);
g_object_unref (gst_rtp_session);
gst_structure_free (rtp_stats);
if (twcc_stats)
gst_structure_free (twcc_stats);
g_value_array_free (source_stats);
g_free (transport_id);
}
/* https://www.w3.org/TR/webrtc-stats/#codec-dict* */
2019-03-07 13:12:47 +00:00
static void
_get_codec_stats_from_pad (GstWebRTCBin * webrtc, GstPad * pad,
GstStructure * s, gchar ** out_id, guint * out_ssrc, guint * out_clock_rate)
{
GstStructure *stats;
GstCaps *caps;
gchar *id;
double ts;
guint ssrc = 0;
gint clock_rate = 0;
gst_structure_get_double (s, "timestamp", &ts);
stats = gst_structure_new_empty ("unused");
id = g_strdup_printf ("codec-stats-%s", GST_OBJECT_NAME (pad));
_set_base_stats (stats, GST_WEBRTC_STATS_CODEC, ts, id);
caps = gst_pad_get_current_caps (pad);
if (caps && gst_caps_is_fixed (caps)) {
GstStructure *caps_s = gst_caps_get_structure (caps, 0);
gint pt;
if (gst_structure_get_int (caps_s, "payload", &pt))
gst_structure_set (stats, "payload-type", G_TYPE_UINT, pt, NULL);
if (gst_structure_get_int (caps_s, "clock-rate", &clock_rate))
gst_structure_set (stats, "clock-rate", G_TYPE_UINT, clock_rate, NULL);
if (gst_structure_get_uint (caps_s, "ssrc", &ssrc))
gst_structure_set (stats, "ssrc", G_TYPE_UINT, ssrc, NULL);
/* FIXME: codecType, mimeType, channels, sdpFmtpLine, implementation, transportId */
}
if (caps)
gst_caps_unref (caps);
gst_structure_set (s, id, GST_TYPE_STRUCTURE, stats, NULL);
gst_structure_free (stats);
2019-03-07 13:12:47 +00:00
if (out_id)
*out_id = id;
else
g_free (id);
if (out_ssrc)
*out_ssrc = ssrc;
if (out_clock_rate)
*out_clock_rate = clock_rate;
}
static gboolean
_get_stats_from_pad (GstWebRTCBin * webrtc, GstPad * pad, GstStructure * s)
{
GstWebRTCBinPad *wpad = GST_WEBRTC_BIN_PAD (pad);
2019-03-07 13:12:47 +00:00
TransportStream *stream;
gchar *codec_id;
guint ssrc, clock_rate;
_get_codec_stats_from_pad (webrtc, pad, s, &codec_id, &ssrc, &clock_rate);
2019-03-07 13:12:47 +00:00
if (!wpad->trans)
goto out;
stream = WEBRTC_TRANSCEIVER (wpad->trans)->stream;
if (!stream)
goto out;
_get_stats_from_transport_channel (webrtc, stream, codec_id, ssrc,
clock_rate, s);
2019-03-07 13:12:47 +00:00
out:
g_free (codec_id);
return TRUE;
}
GstStructure *
gst_webrtc_bin_create_stats (GstWebRTCBin * webrtc, GstPad * pad)
{
GstStructure *s = gst_structure_new_empty ("application/x-webrtc-stats");
double ts = monotonic_time_as_double_milliseconds ();
GstStructure *pc_stats;
_init_debug ();
gst_structure_set (s, "timestamp", G_TYPE_DOUBLE, ts, NULL);
/* FIXME: better unique IDs */
/* FIXME: rate limitting stat updates? */
/* FIXME: all stats need to be kept forever */
GST_DEBUG_OBJECT (webrtc, "updating stats at time %f", ts);
if ((pc_stats = _get_peer_connection_stats (webrtc))) {
const gchar *id = "peer-connection-stats";
_set_base_stats (pc_stats, GST_WEBRTC_STATS_PEER_CONNECTION, ts, id);
gst_structure_set (s, id, GST_TYPE_STRUCTURE, pc_stats, NULL);
gst_structure_free (pc_stats);
}
if (pad)
_get_stats_from_pad (webrtc, pad, s);
else
gst_element_foreach_pad (GST_ELEMENT (webrtc),
(GstElementForeachPadFunc) _get_stats_from_pad, s);
gst_structure_remove_field (s, "timestamp");
return s;
}