Add support for statistic functionality 71/262671/8
authorSangchul Lee <sc11.lee@samsung.com>
Tue, 17 Aug 2021 11:25:54 +0000 (20:25 +0900)
committerSangchul Lee <sc11.lee@samsung.com>
Tue, 24 Aug 2021 07:36:28 +0000 (16:36 +0900)
[Version] 0.2.80
[Issue Type] New feature

Change-Id: I284137c02bc53c24e731c90265e6df3e420bbdef
Signed-off-by: Sangchul Lee <sc11.lee@samsung.com>
include/webrtc_private.h
packaging/capi-media-webrtc.spec
src/webrtc_stats.c [new file with mode: 0644]

index b5a36abfb90cd7f4b7fa8d4269c198b64b6a7fe0..6b2ad59ccefc26decff91e5db25baae1a821b469 100644 (file)
@@ -410,6 +410,8 @@ typedef struct _webrtc_s {
        webrtc_callbacks_s encoded_video_frame_cb;
        webrtc_callbacks_s data_channel_cb;
 
+       webrtc_callbacks_s stats_cb;
+
        /* FIXME: Once signal is emitted by webrtcbin, it'll not be triggered
         * even if the pipeline state is changed to NULL to PLAYING again.
         * This variable could be removed, some improvements are applied to webrtcbin. */
@@ -624,6 +626,8 @@ int _destroy_data_channel(webrtc_data_channel_s *channel);
 int _data_channel_send_string(webrtc_data_channel_s *channel, const char *string);
 int _data_channel_send_bytes(webrtc_data_channel_s *channel, const char *data, unsigned int size);
 
+void _webrtcbin_get_stats(webrtc_s *webrtc);
+
 typedef int (*_websocket_cb)(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len);
 webrtc_websocket_s *_alloc_websocket(const int port, const char *ssl_cert_path, const char *ssl_private_key_path, const char *ssl_ca_path, _websocket_cb callback, void *user_data);
 void _release_websocket(webrtc_websocket_s *ws);
index 040f1cbe45b40f85852cfacfa1e2481a17684d0e..64acc98d553940a3aef0628a46308be2188492e5 100644 (file)
@@ -1,6 +1,6 @@
 Name:       capi-media-webrtc
 Summary:    A WebRTC library in Tizen Native API
-Version:    0.2.79
+Version:    0.2.80
 Release:    0
 Group:      Multimedia/API
 License:    Apache-2.0
diff --git a/src/webrtc_stats.c b/src/webrtc_stats.c
new file mode 100644 (file)
index 0000000..5a33f42
--- /dev/null
@@ -0,0 +1,546 @@
+/*
+ * 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);
+}