--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "webrtc.h"
+#include "webrtc_private.h"
+
+typedef void (*__parse_stats_func)(const GstStructure *s, webrtc_callbacks_s *cb);
+
+/*
+ * Description below is extracted from GstWebRTCBin::get-stats:
+ * Each statistics structure contains the following values as defined by
+ * the RTCStats dictionary (https://www.w3.org/TR/webrtc/#rtcstats-dictionary).
+ *
+ * "timestamp" G_TYPE_DOUBLE timestamp the statistics were generated
+ * "type" GST_TYPE_WEBRTC_STATS_TYPE the type of statistics reported
+ * "id" G_TYPE_STRING unique identifier
+ */
+typedef struct _stats_common_s {
+ gdouble timestamp;
+ GstWebRTCStatsType type;
+ gchar *id;
+} stats_common_s;
+
+/*
+ * Description below is extracted from GstWebRTCBin::get-stats:
+ * RTCCodecStats supported fields (https://w3c.github.io/webrtc-stats/#codec-dict*)
+ *
+ * "payload-type" G_TYPE_UINT the rtp payload number in use
+ * "clock-rate" G_TYPE_UINT the rtp clock-rate
+ */
+typedef struct _stats_codec_s {
+ guint payload_type;
+ guint clock_rate;
+ guint ssrc;
+} stats_codec_s;
+
+/*
+ * Description below is extracted from GstWebRTCBin::get-stats:
+ * RTCRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#streamstats-dict*)
+ *
+ * "ssrc" G_TYPE_STRING the rtp sequence src in use
+ * "transport-id" G_TYPE_STRING identifier for the associated RTCTransportStats for this stream
+ * "codec-id" G_TYPE_STRING identifier for the associated RTCCodecStats for this stream
+ * "fir-count" G_TYPE_UINT FIR requests received by the sender (only for local statistics)
+ * "pli-count" G_TYPE_UINT PLI requests received by the sender (only for local statistics)
+ * "nack-count" G_TYPE_UINT NACK requests received by the sender (only for local statistics)
+ */
+typedef struct _stats_rtp_stream_s {
+ guint ssrc;
+ gchar *transport_id;
+ gchar *codec_id;
+} stats_rtp_stream_s;
+
+/*
+ * Description below is extracted from GstWebRTCBin::get-stats:
+ * RTCReceivedStreamStats supported fields (https://w3c.github.io/webrtc-stats/#receivedrtpstats-dict*)
+ *
+ * "packets-received" G_TYPE_UINT64 number of packets received (only for local inbound)
+ * "bytes-received" G_TYPE_UINT64 number of bytes received (only for local inbound)
+ * "packets-lost" G_TYPE_UINT number of packets lost
+ * "jitter" G_TYPE_DOUBLE packet jitter measured in seconds
+ */
+typedef struct _stats_received_rtp_stream_s {
+ guint64 packets_received;
+ guint64 packets_lost;
+ guint64 packets_discarded;
+ guint64 packets_repaired;
+ gdouble jitter;
+} stats_received_rtp_stream_s;
+
+/*
+ * Description below is extracted from GstWebRTCBin::get-stats:
+ * RTCInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#inboundrtpstats-dict*)
+ *
+ * "remote-id" G_TYPE_STRING identifier for the associated RTCRemoteOutboundRTPStreamStats
+ */
+typedef struct _stats_inbound_rtp_stream_s {
+ gchar *remote_id;
+ guint64 bytes_received;
+ guint64 packets_duplicated;
+ guint fir_count;
+ guint pli_count;
+ guint nack_count;
+} stats_inbound_rtp_stream_s;
+
+/*
+ * Description below is extracted from GstWebRTCBin::get-stats:
+ * RTCSentRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#sentrtpstats-dict*)
+ *
+ * "packets-sent" G_TYPE_UINT64 number of packets sent (only for local outbound)
+ * "bytes-sent" G_TYPE_UINT64 number of packets sent (only for local outbound)
+ */
+typedef struct _stats_sent_rtp_stream_s {
+ guint64 packets_sent;
+ guint64 bytes_sent;
+} stats_sent_rtp_stream_s;
+
+/*
+ * Description below is extracted from GstWebRTCBin::get-stats:
+ * RTCOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#outboundrtpstats-dict*)
+ *
+ * "remote-id" G_TYPE_STRING identifier for the associated RTCRemoteInboundRTPSTreamStats
+ */
+typedef struct _stats_outbound_rtp_stream_s {
+ gchar *remote_id;
+ guint fir_count; /* FIR/PLI/NACK counts exists here that differ from WebRTC SPEC */
+ guint pli_count;
+ guint nack_count;
+} stats_outbound_rtp_stream_s;
+
+/*
+ * Description below is extracted from GstWebRTCBin::get-stats:
+ * RTCRemoteInboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteinboundrtpstats-dict*)
+ *
+ * "local-id" G_TYPE_STRING identifier for the associated RTCOutboundRTPSTreamStats
+ * "round-trip-time" G_TYPE_DOUBLE round trip time of packets measured in seconds
+ */
+typedef struct _stats_remote_inbound_rtp_stream_s {
+ gchar *local_id;
+ gdouble round_trip_time;
+} stats_remote_inbound_rtp_stream_s;
+
+/*
+ * Description below is extracted from GstWebRTCBin::get-stats:
+ * RTCRemoteOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#remoteoutboundrtpstats-dict*)
+ *
+ * "local-id" G_TYPE_STRING identifier for the associated RTCInboundRTPSTreamStats
+ */
+typedef struct _stats_remote_outbound_rtp_stream_s {
+ gchar *local_id;
+ gdouble remote_timestamp;
+} stats_remote_outbound_rtp_stream_s;
+
+typedef struct _stats_peer_connection_s {
+ guint data_channels_opened;
+ guint data_channels_closed;
+ guint data_channels_requested;
+ guint data_channels_accepted;
+} stats_peer_connection_s;
+
+/* This is for debug purpose to check which fields are actually provided via webrtcbin. */
+static gboolean __gststructure_foreach_cb(GQuark field_id, const GValue *val, gpointer data)
+{
+ LOG_DEBUG("field_id[%s] GType[%s]", g_quark_to_string(field_id), g_type_name(G_VALUE_TYPE(val)));
+ return TRUE;
+}
+
+static void __get_common_stats(const GstStructure *s, stats_common_s *stats)
+{
+ RET_IF(s == NULL, "s is NULL");
+ RET_IF(stats == NULL, "stats is NULL");
+
+ gst_structure_get(s,
+ "timestamp", G_TYPE_DOUBLE, &stats->timestamp,
+ "type", GST_TYPE_WEBRTC_STATS_TYPE, &stats->type,
+ "id", G_TYPE_STRING, &stats->id,
+ NULL);
+
+ LOG_DEBUG("timestamp[%lf] type[%u] id[%s]", stats->timestamp, stats->type, stats->id);
+}
+
+static void __get_codec_stats(const GstStructure *s, stats_codec_s *stats)
+{
+ RET_IF(s == NULL, "s is NULL");
+ RET_IF(stats == NULL, "stats is NULL");
+
+ gst_structure_get(s,
+ "payload-type", G_TYPE_UINT, &stats->payload_type,
+ "clock-rate", G_TYPE_UINT, &stats->clock_rate,
+ "ssrc", G_TYPE_UINT, &stats->ssrc,
+ NULL);
+
+ LOG_DEBUG("payload-type[%u] clock-rate[%u] ssrc[%u]", stats->payload_type, stats->clock_rate, stats->ssrc);
+}
+
+static void __get_rtp_stream_stats(const GstStructure *s, stats_rtp_stream_s *stats)
+{
+ RET_IF(s == NULL, "s is NULL");
+ RET_IF(stats == NULL, "stats is NULL");
+
+ gst_structure_get(s,
+ "ssrc", G_TYPE_UINT, &stats->ssrc,
+ "transport-id", G_TYPE_STRING, &stats->transport_id,
+ "codec-id", G_TYPE_STRING, &stats->codec_id,
+ NULL);
+
+ LOG_DEBUG("ssrc[%u] transport-id[%s] codec-id[%s] ", stats->ssrc, stats->transport_id, stats->codec_id);
+}
+
+static void __get_received_rtp_stream_stats(const GstStructure *s, stats_received_rtp_stream_s *stats)
+{
+ RET_IF(s == NULL, "s is NULL");
+ RET_IF(stats == NULL, "stats is NULL");
+
+ gst_structure_get(s,
+ "packets-received", G_TYPE_UINT64, &stats->packets_received,
+ "packets-lost", G_TYPE_UINT64, &stats->packets_lost,
+ "packets-discarded", G_TYPE_UINT64, &stats->packets_discarded,
+ "packets-repaired", G_TYPE_UINT64, &stats->packets_repaired,
+ "jitter", G_TYPE_DOUBLE, &stats->jitter,
+ NULL);
+
+ LOG_DEBUG("packet-received[%"G_GUINT64_FORMAT"] packets-lost[%"G_GUINT64_FORMAT"] \
+ packets-discarded[%"G_GUINT64_FORMAT"] packets-repaired[%"G_GUINT64_FORMAT"] jitter[%lf]",
+ stats->packets_received, stats->packets_lost, stats->packets_discarded, stats->packets_repaired, stats->jitter);
+}
+
+static void __get_inbound_rtp_stream_stats(const GstStructure *s, stats_inbound_rtp_stream_s *stats)
+{
+ RET_IF(s == NULL, "s is NULL");
+ RET_IF(stats == NULL, "stats is NULL");
+
+ gst_structure_get(s,
+ "remote-id", G_TYPE_STRING, &stats->remote_id,
+ "bytes-received", G_TYPE_UINT64, &stats->bytes_received,
+ "packets-duplicated", G_TYPE_UINT64, &stats->packets_duplicated,
+ "fir-count", G_TYPE_UINT, &stats->fir_count,
+ "pli-count", G_TYPE_UINT, &stats->pli_count,
+ "nack-count", G_TYPE_UINT, &stats->nack_count,
+ NULL);
+
+ LOG_DEBUG("remote-id[%s] bytes-received[%"G_GUINT64_FORMAT"] packets-duplicated[%"G_GUINT64_FORMAT"] \
+ fir-count[%u] pli-count[%u] nack-count[%u]", stats->remote_id, stats->bytes_received, stats->packets_duplicated,
+ stats->fir_count, stats->pli_count, stats->nack_count);
+}
+
+static void __get_sent_rtp_stream_stats(const GstStructure *s, stats_sent_rtp_stream_s *stats)
+{
+ RET_IF(s == NULL, "s is NULL");
+ RET_IF(stats == NULL, "stats is NULL");
+
+ gst_structure_get(s,
+ "packets-sent", G_TYPE_UINT64, &stats->packets_sent,
+ "bytes-sent", G_TYPE_UINT64, &stats->bytes_sent,
+ NULL);
+
+ LOG_DEBUG("packet-sent[%"G_GUINT64_FORMAT"] bytes-sent[%"G_GUINT64_FORMAT"]", stats->packets_sent, stats->bytes_sent);
+}
+
+static void __get_outbound_rtp_stream_stats(const GstStructure *s, stats_outbound_rtp_stream_s *stats)
+{
+ RET_IF(s == NULL, "s is NULL");
+ RET_IF(stats == NULL, "stats is NULL");
+
+ gst_structure_get(s,
+ "remote-id", G_TYPE_STRING, &stats->remote_id,
+ "fir-count", G_TYPE_UINT, &stats->fir_count,
+ "pli-count", G_TYPE_UINT, &stats->pli_count,
+ "nack-count", G_TYPE_UINT, &stats->nack_count,
+ NULL);
+
+ LOG_DEBUG("remote-id[%s] fir-count[%u] pli-count[%u] nack-count[%u]",
+ stats->remote_id, stats->fir_count, stats->pli_count, stats->nack_count);
+}
+
+static void __get_remote_inbound_rtp_stream_stats(const GstStructure *s, stats_remote_inbound_rtp_stream_s *stats)
+{
+ RET_IF(s == NULL, "s is NULL");
+ RET_IF(stats == NULL, "stats is NULL");
+
+ gst_structure_get(s,
+ "local-id", G_TYPE_STRING, &stats->local_id,
+ "round-trip-time", G_TYPE_DOUBLE, &stats->round_trip_time,
+ NULL);
+ /* FIXME: fraction-lost should be added */
+
+ LOG_DEBUG("local-id[%s] round-trip-time[%lf]", stats->local_id, stats->round_trip_time);
+}
+
+static void __get_remote_outbound_rtp_stream_stats(const GstStructure *s, stats_remote_outbound_rtp_stream_s *stats)
+{
+ RET_IF(s == NULL, "s is NULL");
+ RET_IF(stats == NULL, "stats is NULL");
+
+ gst_structure_get(s,
+ "local-id", G_TYPE_STRING, &stats->local_id,
+ "remote-timestamp", G_TYPE_DOUBLE, &stats->remote_timestamp,
+ NULL);
+
+ LOG_DEBUG("local-id[%s] remote-timestamp[%lf]", stats->local_id, stats->remote_timestamp);
+}
+
+static void __get_peer_connection_stats(const GstStructure *s, stats_peer_connection_s *stats)
+{
+ RET_IF(s == NULL, "s is NULL");
+ RET_IF(stats == NULL, "stats is NULL");
+
+ gst_structure_get(s,
+ "data-channels-opened", G_TYPE_UINT, &stats->data_channels_opened,
+ "data-channels-closed", G_TYPE_UINT, &stats->data_channels_closed,
+ "data-channels-requested", G_TYPE_UINT, &stats->data_channels_requested,
+ "data-channels-accepted", G_TYPE_UINT, &stats->data_channels_accepted,
+ NULL);
+
+ LOG_DEBUG("data-channels-[opened:%u, closed:%u, requested:%u, accepted:%u]",
+ stats->data_channels_opened, stats->data_channels_closed, stats->data_channels_requested, stats->data_channels_accepted);
+}
+
+static void __parse_codec_and_invoke_callback(const GstStructure *s, webrtc_callbacks_s *cb)
+{
+ stats_common_s common;
+ stats_codec_s codec;
+
+ RET_IF(cb == NULL, "cb is NULL");
+
+ __get_common_stats(s, &common);
+ __get_codec_stats(s, &codec);
+
+ /* TODO: invoke callback */
+}
+
+static void __parse_inbound_rtp_and_invoke_callback(const GstStructure *s, webrtc_callbacks_s *cb)
+{
+ stats_common_s common;
+ stats_rtp_stream_s rtp_stream;
+ stats_received_rtp_stream_s received_rtp_stream;
+ stats_inbound_rtp_stream_s inbound_rtp_stream;
+ GstStructure *rtpjitterbuffer_stats;
+ GstStructure *rtpsource_stats;
+
+ RET_IF(cb == NULL, "cb is NULL");
+
+ __get_common_stats(s, &common);
+ __get_rtp_stream_stats(s, &rtp_stream);
+ __get_received_rtp_stream_stats(s, &received_rtp_stream);
+ __get_inbound_rtp_stream_stats(s, &inbound_rtp_stream);
+
+ gst_structure_get(s,
+ "gst-rtpjitterbuffer-stats", GST_TYPE_STRUCTURE, &rtpjitterbuffer_stats,
+ "gst-rtpsource-stats", GST_TYPE_STRUCTURE, &rtpsource_stats,
+ NULL);
+
+ gst_structure_foreach(rtpjitterbuffer_stats, __gststructure_foreach_cb, NULL);
+ gst_structure_foreach(rtpsource_stats, __gststructure_foreach_cb, NULL);
+
+ /* TODO: invoke callback */
+}
+
+static void __parse_outbound_rtp_and_invoke_callback(const GstStructure *s, webrtc_callbacks_s *cb)
+{
+ stats_common_s common;
+ stats_rtp_stream_s rtp_stream;
+ stats_sent_rtp_stream_s sent_rtp_stream;
+ stats_outbound_rtp_stream_s outbound_rtp_stream;
+ GstStructure *rtpsource_stats;
+
+ RET_IF(cb == NULL, "cb is NULL");
+
+ __get_common_stats(s, &common);
+ __get_rtp_stream_stats(s, &rtp_stream);
+ __get_sent_rtp_stream_stats(s, &sent_rtp_stream);
+ __get_outbound_rtp_stream_stats(s, &outbound_rtp_stream);
+
+ gst_structure_get(s, "gst-rtpsource-stats", GST_TYPE_STRUCTURE, &rtpsource_stats, NULL);
+ gst_structure_foreach(rtpsource_stats, __gststructure_foreach_cb, NULL);
+
+ /* TODO: invoke callback */
+}
+
+static void __parse_remote_inbound_rtp_and_invoke_callback(const GstStructure *s, webrtc_callbacks_s *cb)
+{
+ stats_common_s common;
+ stats_rtp_stream_s rtp_stream;
+ stats_remote_inbound_rtp_stream_s remote_inbound_rtp_stream;
+
+ RET_IF(cb == NULL, "cb is NULL");
+
+ __get_common_stats(s, &common);
+ __get_rtp_stream_stats(s, &rtp_stream);
+ /* FIXME: only 'jitter' and 'packet-lost'(int) are available. type of 'packet-lost' should be fixed.
+ * __get_received_rtp_stream_stats(s, &received_rtp_stream); */
+ __get_remote_inbound_rtp_stream_stats(s, &remote_inbound_rtp_stream);
+
+ /* TODO: invoke callback */
+}
+
+static void __parse_remote_outbound_rtp_and_invoke_callback(const GstStructure *s, webrtc_callbacks_s *cb)
+{
+ stats_common_s common;
+ stats_rtp_stream_s rtp_stream;
+ stats_remote_outbound_rtp_stream_s remote_outbound_rtp_stream;
+ /* NOTE: stats_sent_rtp_stream_s does not exist that differs from WebRTC SPEC. */
+
+ RET_IF(cb == NULL, "cb is NULL");
+
+ __get_common_stats(s, &common);
+ __get_rtp_stream_stats(s, &rtp_stream);
+ __get_remote_outbound_rtp_stream_stats(s, &remote_outbound_rtp_stream);
+
+ /* TODO: invoke callback */
+}
+
+static void __parse_csrc_and_invoke_callback(const GstStructure *s, webrtc_callbacks_s *cb)
+{
+ RET_IF(cb == NULL, "cb is NULL");
+
+ gst_structure_foreach(s, __gststructure_foreach_cb, NULL);
+ /* not implemented */
+}
+
+static void __parse_peer_connection_and_invoke_callback(const GstStructure *s, webrtc_callbacks_s *cb)
+{
+ stats_common_s common;
+ stats_peer_connection_s peer_connection;
+
+ RET_IF(cb == NULL, "cb is NULL");
+
+ __get_common_stats(s, &common);
+ __get_peer_connection_stats(s, &peer_connection);
+
+ /* TODO: invoke callback */
+}
+
+static void __parse_data_channel_and_invoke_callback(const GstStructure *s, webrtc_callbacks_s *cb)
+{
+ RET_IF(cb == NULL, "cb is NULL");
+
+ gst_structure_foreach(s, __gststructure_foreach_cb, NULL);
+ /* not implemented */
+}
+
+static void __parse_stream_and_invoke_callback(const GstStructure *s, webrtc_callbacks_s *cb)
+{
+ RET_IF(cb == NULL, "cb is NULL");
+
+ gst_structure_foreach(s, __gststructure_foreach_cb, NULL);
+ /* not implemented */
+}
+
+static void __parse_transport_and_invoke_callback(const GstStructure *s, webrtc_callbacks_s *cb)
+{
+ RET_IF(cb == NULL, "cb is NULL");
+
+ gst_structure_foreach(s, __gststructure_foreach_cb, NULL);
+ /* not implemented */
+}
+
+static void __parse_candidate_pair_and_invoke_callback(const GstStructure *s, webrtc_callbacks_s *cb)
+{
+ RET_IF(cb == NULL, "cb is NULL");
+
+ gst_structure_foreach(s, __gststructure_foreach_cb, NULL);
+ /* not implemented */
+}
+
+static void __parse_local_candidate_and_invoke_callback(const GstStructure *s, webrtc_callbacks_s *cb)
+{
+ RET_IF(cb == NULL, "cb is NULL");
+
+ gst_structure_foreach(s, __gststructure_foreach_cb, NULL);
+ /* not implemented */
+}
+
+static void __parse_remote_candidate_and_invoke_callback(const GstStructure *s, webrtc_callbacks_s *cb)
+{
+ RET_IF(cb == NULL, "cb is NULL");
+
+ gst_structure_foreach(s, __gststructure_foreach_cb, NULL);
+ /* not implemented */
+}
+
+static void __parse_certificate_and_invoke_callback(const GstStructure *s, webrtc_callbacks_s *cb)
+{
+ RET_IF(cb == NULL, "cb is NULL");
+
+ gst_structure_foreach(s, __gststructure_foreach_cb, NULL);
+ /* not implemented */
+}
+
+static __parse_stats_func __parse_stats_funcs[] = {
+ [GST_WEBRTC_STATS_CODEC] = __parse_codec_and_invoke_callback,
+ [GST_WEBRTC_STATS_INBOUND_RTP] = __parse_inbound_rtp_and_invoke_callback,
+ [GST_WEBRTC_STATS_OUTBOUND_RTP] = __parse_outbound_rtp_and_invoke_callback,
+ [GST_WEBRTC_STATS_REMOTE_INBOUND_RTP] = __parse_remote_inbound_rtp_and_invoke_callback,
+ [GST_WEBRTC_STATS_REMOTE_OUTBOUND_RTP] = __parse_remote_outbound_rtp_and_invoke_callback,
+ [GST_WEBRTC_STATS_CSRC] = __parse_csrc_and_invoke_callback,
+ [GST_WEBRTC_STATS_PEER_CONNECTION] = __parse_peer_connection_and_invoke_callback,
+ [GST_WEBRTC_STATS_DATA_CHANNEL] = __parse_data_channel_and_invoke_callback,
+ [GST_WEBRTC_STATS_STREAM] = __parse_stream_and_invoke_callback,
+ [GST_WEBRTC_STATS_TRANSPORT] = __parse_transport_and_invoke_callback,
+ [GST_WEBRTC_STATS_CANDIDATE_PAIR] = __parse_candidate_pair_and_invoke_callback,
+ [GST_WEBRTC_STATS_LOCAL_CANDIDATE] = __parse_local_candidate_and_invoke_callback,
+ [GST_WEBRTC_STATS_REMOTE_CANDIDATE] = __parse_remote_candidate_and_invoke_callback,
+ [GST_WEBRTC_STATS_CERTIFICATE] = __parse_certificate_and_invoke_callback,
+};
+
+static gboolean __webrtcbin_stats_cb(GQuark field_id, const GValue *value, gpointer user_data)
+{
+ webrtc_s *webrtc = (webrtc_s *)user_data;
+ const GstStructure *s;
+ GstWebRTCStatsType type;
+
+ if (GST_VALUE_HOLDS_STRUCTURE(value)) {
+ s = gst_value_get_structure(value);
+ gst_structure_get(s, "type", GST_TYPE_WEBRTC_STATS_TYPE, &type, NULL);
+
+ RET_VAL_IF((type < GST_WEBRTC_STATS_CODEC || type > GST_WEBRTC_STATS_CERTIFICATE),
+ TRUE, "invalid type(%u)", type);
+
+ __parse_stats_funcs[type](s, &webrtc->stats_cb);
+
+ } else {
+ LOG_ERROR("unknown field \'%s\' value type: \'%s\'",
+ g_quark_to_string(field_id), g_type_name(G_VALUE_TYPE(value)));
+ }
+
+ return TRUE;
+}
+
+static void __webrtcbin_get_stats_cb(GstPromise *promise, webrtc_s *webrtc)
+{
+ const GstStructure *stats;
+
+ RET_IF(gst_promise_wait(promise) != GST_PROMISE_RESULT_REPLIED, "failed to gst_promise_wait()");
+
+ stats = gst_promise_get_reply(promise);
+ gst_structure_foreach(stats, __webrtcbin_stats_cb, webrtc);
+}
+
+void _webrtcbin_get_stats(webrtc_s *webrtc)
+{
+ GstPromise *promise;
+
+ RET_IF(webrtc == NULL, "webrtc is NULL");
+ RET_IF(webrtc->gst.webrtcbin == NULL, "webrtcbin is NULL");
+
+ promise = gst_promise_new_with_change_func((GstPromiseChangeFunc)__webrtcbin_get_stats_cb, webrtc, NULL);
+
+ g_signal_emit_by_name(webrtc->gst.webrtcbin, "get-stats", NULL, promise);
+ LOG_DEBUG("emitting 'get-stats' on %p", webrtc->gst.webrtcbin);
+
+ gst_promise_unref(promise);
+}