webrtc_stats: Revise to allow pre-defined fields per stats type 07/271707/5
authorSangchul Lee <sc11.lee@samsung.com>
Fri, 25 Feb 2022 06:15:14 +0000 (15:15 +0900)
committerSangchul Lee <sc11.lee@samsung.com>
Wed, 2 Mar 2022 10:29:01 +0000 (19:29 +0900)
It is also possible to check easily fields incoming from gstreamer
which are not defined in this library yet.

[Version] 0.3.63
[Issue Type] Refactoring

Change-Id: Ied57fcf1ad4b350588d1456ddca56f6fe4003774
Signed-off-by: Sangchul Lee <sc11.lee@samsung.com>
include/webrtc_private.h
packaging/capi-media-webrtc.spec
src/webrtc_private.c
src/webrtc_stats.c

index 47ff20431ced6601d861949dc4181594deeafa6c..b9849576db6ea689d800508f5b4c6d79772d50f6 100644 (file)
@@ -716,6 +716,7 @@ int _data_channel_send_bytes(webrtc_data_channel_s *channel, const char *data, u
 void _webrtcbin_get_stats(webrtc_s *webrtc, int type_mask);
 void _set_stats_timer(webrtc_s *webrtc);
 void _unset_stats_timer(webrtc_s *webrtc);
+void _init_stats_all_fields_list(void);
 
 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);
index 036245abf2ad7c2a9180651497834379660ea537..9a5327361b08abe1343860181ed0926159598e70 100644 (file)
@@ -1,6 +1,6 @@
 Name:       capi-media-webrtc
 Summary:    A WebRTC library in Tizen Native API
-Version:    0.3.62
+Version:    0.3.63
 Release:    0
 Group:      Multimedia/API
 License:    Apache-2.0
index cb95b7a921b4ed58debae716577600d5cee4e10c..8fddce3f47f180c5dc0b498cf11d97c56fd03a16 100644 (file)
@@ -378,6 +378,7 @@ void _invoke_state_changed_cb(webrtc_s *webrtc, webrtc_state_e old, webrtc_state
        }
 
        if (new == WEBRTC_STATE_PLAYING) {
+               _init_stats_all_fields_list();
                _set_stats_timer(webrtc);
                _remove_filesrc_pad_block_probe(webrtc);
        }
index 88051a132baa77625d9018ff7184f8b2853b365a..13545ada85f7b18f5928ba7f55f6c482b54d31f7 100644 (file)
 
 //LCOV_EXCL_START
 
+typedef struct _stats_field_s {
+       const gchar *name;
+       GQuark id;
+} stats_field_s;
+
 /*
  * Description below is extracted from GstWebRTCBin::get-stats:
  * Each statistics structure contains the following values as defined by
  *  "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;
+static stats_field_s __stats_common_fields[] = {
+       { "timestamp", 0 },
+       { "type", 0 },
+       { "id", 0 },
+       { NULL, 0 }
+};
 
 /*
  * Description below is extracted from GstWebRTCBin::get-stats:
@@ -41,11 +47,12 @@ typedef struct _stats_common_s {
  *  "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;
+static stats_field_s __stats_codec_fields[] = {
+       { "payload-type", 0 },
+       { "clock-rate", 0 },
+       { "ssrc", 0 },
+       { NULL, 0 }
+};
 
 /*
  * Description below is extracted from GstWebRTCBin::get-stats:
@@ -58,11 +65,15 @@ typedef struct _stats_codec_s {
  *  "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;
+static stats_field_s __stats_rtp_stream_fields[] = {
+       { "ssrc", 0 },
+       { "transport-id", 0 },
+       { "codec-id", 0 },
+       { "fir-count", 0 },
+       { "pli-count", 0 },
+       { "nack-count", 0 },
+       { NULL, 0 }
+};
 
 /*
  * Description below is extracted from GstWebRTCBin::get-stats:
@@ -73,13 +84,14 @@ typedef struct _stats_rtp_stream_s {
  *  "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;
+static stats_field_s __stats_received_rtp_stream_fields[] = {
+       { "packets-received", 0 },
+       { "packets-lost", 0 },
+       { "packets-discarded", 0 },
+       { "packets-repaired", 0 },
+       { "jitter", 0 },
+       { NULL, 0 }
+};
 
 /*
  * Description below is extracted from GstWebRTCBin::get-stats:
@@ -87,14 +99,15 @@ typedef struct _stats_received_rtp_stream_s {
  *
  *  "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;
+static stats_field_s __stats_inbound_rtp_stream_fields[] = {
+       { "remote-id", 0 },
+       { "bytes-received", 0 },
+       { "packets-duplicated", 0 },
+       { "fir-count", 0 },
+       { "pli-count", 0 },
+       { "nack-count", 0 },
+       { NULL, 0 }
+};
 
 /*
  * Description below is extracted from GstWebRTCBin::get-stats:
@@ -103,10 +116,11 @@ typedef struct _stats_inbound_rtp_stream_s {
  *  "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;
+static stats_field_s __stats_sent_rtp_stream_fields[] = {
+       { "packets-sent", 0 },
+       { "bytes-sent", 0 },
+       { NULL, 0 }
+};
 
 /*
  * Description below is extracted from GstWebRTCBin::get-stats:
@@ -114,12 +128,10 @@ typedef struct _stats_sent_rtp_stream_s {
  *
  *  "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;
+static stats_field_s __stats_outbound_rtp_stream_fields[] = {
+       { "remote-id", 0 },
+       { NULL, 0 }
+};
 
 /*
  * Description below is extracted from GstWebRTCBin::get-stats:
@@ -128,32 +140,133 @@ typedef struct _stats_outbound_rtp_stream_s {
  *  "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;
+static stats_field_s __stats_remote_inbound_rtp_stream_fields[] = {
+       { "local-id", 0 },
+       { "round-trip-time", 0 },
+       { NULL, 0 }
+};
 
 /*
  * 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
+ *  "remote-timestamp"    G_TYPE_DOUBLE               the remote timestamp at which these statistics were sent by the remote endpoint
  */
-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)
+static stats_field_s __stats_remote_outbound_rtp_stream_fields[] = {
+       { "local-id", 0 },
+       { "remote-timestamp", 0 },
+       { NULL, 0 }
+};
+
+/*
+ * Description below is extracted from GstWebRTCBin::get-stats:
+ * RTCRemoteOutboundRTPStreamStats supported fields (https://w3c.github.io/webrtc-stats/#pcstats-dict*)
+ *
+ *  "data-channels-opened"      G_TYPE_UINT              the number of unique RTCDataChannels that have entered the "open" state during their lifetime
+ *  "data-channels-closed"      G_TYPE_UINT              the number of unique RTCDataChannels that have left the "open" state during their lifetime
+ *  "data-channels-requested"   G_TYPE_UINT              the number of unique RTCDataChannels returned from a successful createDataChannel() call on the RTCPeerConnection
+ *  "data-channels-accepted"    G_TYPE_UINT              the number of unique RTCDataChannels signaled in a ondatachannel event on the RTCPeerConnection
+ */
+static stats_field_s __stats_peer_connection_fields[] = {
+       { "data-channels-opened", 0 },
+       { "data-channels-closed", 0 },
+       { "data-channels-requested", 0 },
+       { "data-channels-accepted", 0 },
+       { NULL, 0 }
+};
+
+static stats_field_s *__stats_all_fields_list[] = {
+       __stats_common_fields,
+       __stats_codec_fields,
+       __stats_rtp_stream_fields,
+       __stats_received_rtp_stream_fields,
+       __stats_inbound_rtp_stream_fields,
+       __stats_sent_rtp_stream_fields,
+       __stats_outbound_rtp_stream_fields,
+       __stats_remote_inbound_rtp_stream_fields,
+       __stats_remote_outbound_rtp_stream_fields,
+       __stats_peer_connection_fields,
+       NULL
+};
+
+static stats_field_s *__stats_codec_fields_list[] = {
+       __stats_common_fields,
+       __stats_codec_fields,
+       NULL
+};
+
+static stats_field_s *__stats_inbound_rtp_fields_list[] = {
+       __stats_common_fields,
+       __stats_rtp_stream_fields,
+       __stats_received_rtp_stream_fields,
+       __stats_inbound_rtp_stream_fields,
+       NULL
+};
+
+static stats_field_s *__stats_outbound_rtp_fields_list[] = {
+       __stats_common_fields,
+       __stats_rtp_stream_fields,
+       __stats_sent_rtp_stream_fields,
+       __stats_outbound_rtp_stream_fields,
+       NULL
+};
+
+/* NOTE: __stats_received_rtp_stream_fields does not exist that differs from WebRTC SPEC. */
+static stats_field_s *__stats_remote_inbound_rtp_fields_list[] = {
+       __stats_common_fields,
+       __stats_rtp_stream_fields,
+       //__stats_received_rtp_stream_fields,
+       __stats_remote_inbound_rtp_stream_fields,
+       NULL
+};
+
+/* NOTE: stats_sent_rtp_stream_s does not exist that differs from WebRTC SPEC. */
+static stats_field_s *__stats_remote_outbound_rtp_fields_list[] = {
+       __stats_common_fields,
+       __stats_rtp_stream_fields,
+       //__stats_sent_rtp_stream_fields,
+       __stats_remote_outbound_rtp_stream_fields,
+       NULL
+};
+
+static stats_field_s *__stats_peer_connection_fields_list[] = {
+       __stats_common_fields,
+       __stats_peer_connection_fields,
+       NULL
+};
+
+typedef struct _stats_userdata_s {
+       webrtc_s *webrtc;
+       int type_mask;
+       stats_field_s **fields_list;
+} stats_userdata_s;
+
+static gboolean __gststructure_foreach_cb(GQuark field_id, const GValue *val, gpointer user_data)
 {
+       stats_userdata_s *stats = (stats_userdata_s *)user_data;
+       int i, j;
+
+       RET_VAL_IF(user_data == NULL, FALSE, "user_data is NULL");
+       RET_VAL_IF(stats->webrtc == NULL, FALSE, "webrtc is NULL");
+       RET_VAL_IF(stats->fields_list == NULL, FALSE, "fields_list is NULL");
+
+       /* Note that it only allow fields pre-defined in list */
+       for (i = 0; stats->fields_list[i]; i++)
+               for (j = 0; stats->fields_list[i][j].name; j++)
+                       if (stats->fields_list[i][j].id == field_id)
+                               goto invoke_cb;
+
+       /* field id below will be skipped here, it should be handled with gst_structure_foreach() */
+       if (!g_strcmp0(g_quark_to_string(field_id), "gst-rtpjitterbuffer-stats") ||
+               !g_strcmp0(g_quark_to_string(field_id), "gst-rtpsource-stats"))
+               return TRUE;
+
+       LOG_WARNING("this field[%s] is not supported", g_quark_to_string(field_id));
+       return TRUE;
+
+invoke_cb:
+       /* TODO: invoke callback */
        switch (G_VALUE_TYPE(val)) {
        case G_TYPE_BOOLEAN:
                LOG_DEBUG("field_id[%s] GType[%s] value[%u]",
@@ -201,357 +314,157 @@ static gboolean __gststructure_foreach_cb(GQuark field_id, const GValue *val, gp
        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)
+static void __stats_codec_invoke_callback(const GstStructure *s, stats_userdata_s *user_data)
 {
-       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");
+       RET_IF(user_data == NULL, "user_data is NULL");
 
        LOG_DEBUG_ENTER();
 
-       __get_common_stats(s, &common);
-       __get_codec_stats(s, &codec);
-
-       /* TODO: invoke callback */
+       gst_structure_foreach(s, __gststructure_foreach_cb, user_data);
 }
 
-static void __parse_inbound_rtp_and_invoke_callback(const GstStructure *s, webrtc_callbacks_s *cb)
+static void __stats_inbound_rtp_invoke_callback(const GstStructure *s, stats_userdata_s *user_data)
 {
-       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");
+       RET_IF(user_data == NULL, "user_data is NULL");
 
        LOG_DEBUG_ENTER();
 
-       __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_foreach(s, __gststructure_foreach_cb, user_data);
 
        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 */
+       gst_structure_foreach(rtpjitterbuffer_stats, __gststructure_foreach_cb, user_data);
+       gst_structure_foreach(rtpsource_stats, __gststructure_foreach_cb, user_data);
 }
 
-static void __parse_outbound_rtp_and_invoke_callback(const GstStructure *s, webrtc_callbacks_s *cb)
+static void __stats_outbound_rtp_invoke_callback(const GstStructure *s, stats_userdata_s *user_data)
 {
-       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");
+       RET_IF(user_data == NULL, "user_data is NULL");
 
        LOG_DEBUG_ENTER();
 
-       __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_foreach(s, __gststructure_foreach_cb, user_data);
 
        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 */
+       gst_structure_foreach(rtpsource_stats, __gststructure_foreach_cb, user_data);
 }
 
-static void __parse_remote_inbound_rtp_and_invoke_callback(const GstStructure *s, webrtc_callbacks_s *cb)
+static void __stats_remote_inbound_rtp_invoke_callback(const GstStructure *s, stats_userdata_s *user_data)
 {
-       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");
+       RET_IF(user_data == NULL, "user_data is NULL");
 
        LOG_DEBUG_ENTER();
 
-       __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 */
+       /* FIXME: only 'jitter' and 'packet-lost'(int) are available. type of 'packet-lost' should be fixed.*/
+       gst_structure_foreach(s, __gststructure_foreach_cb, user_data);
 }
 
-static void __parse_remote_outbound_rtp_and_invoke_callback(const GstStructure *s, webrtc_callbacks_s *cb)
+static void __stats_remote_outbound_rtp_invoke_callback(const GstStructure *s, stats_userdata_s *user_data)
 {
-       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");
+       RET_IF(user_data == NULL, "user_data is NULL");
 
        LOG_DEBUG_ENTER();
 
-       __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 */
+       gst_structure_foreach(s, __gststructure_foreach_cb, user_data);
 }
 
-static void __parse_csrc_and_invoke_callback(const GstStructure *s, webrtc_callbacks_s *cb)
+static void __stats_csrc_invoke_callback(const GstStructure *s, stats_userdata_s *user_data)
 {
-       RET_IF(cb == NULL, "cb is NULL");
+       RET_IF(user_data == NULL, "user_data is NULL");
 
        LOG_DEBUG_ENTER();
 
-       gst_structure_foreach(s, __gststructure_foreach_cb, NULL);
+       gst_structure_foreach(s, __gststructure_foreach_cb, user_data);
        /* not implemented */
 }
 
-static void __parse_peer_connection_and_invoke_callback(const GstStructure *s, webrtc_callbacks_s *cb)
+static void __stats_peer_connection_invoke_callback(const GstStructure *s, stats_userdata_s *user_data)
 {
-       stats_common_s common;
-       stats_peer_connection_s peer_connection;
-
-       RET_IF(cb == NULL, "cb is NULL");
+       RET_IF(user_data == NULL, "user_data is NULL");
 
        LOG_DEBUG_ENTER();
 
-       __get_common_stats(s, &common);
-       __get_peer_connection_stats(s, &peer_connection);
-
-       /* TODO: invoke callback */
+       gst_structure_foreach(s, __gststructure_foreach_cb, user_data);
 }
 
-static void __parse_data_channel_and_invoke_callback(const GstStructure *s, webrtc_callbacks_s *cb)
+static void __stats_data_channel_invoke_callback(const GstStructure *s, stats_userdata_s *user_data)
 {
-       RET_IF(cb == NULL, "cb is NULL");
+       RET_IF(user_data == NULL, "user_data is NULL");
 
        LOG_DEBUG_ENTER();
 
-       gst_structure_foreach(s, __gststructure_foreach_cb, NULL);
+       gst_structure_foreach(s, __gststructure_foreach_cb, user_data);
        /* not implemented */
 }
 
-static void __parse_stream_and_invoke_callback(const GstStructure *s, webrtc_callbacks_s *cb)
+static void __stats_stream_invoke_callback(const GstStructure *s, stats_userdata_s *user_data)
 {
-       RET_IF(cb == NULL, "cb is NULL");
+       RET_IF(user_data == NULL, "user_data is NULL");
 
        LOG_DEBUG_ENTER();
 
-       gst_structure_foreach(s, __gststructure_foreach_cb, NULL);
+       gst_structure_foreach(s, __gststructure_foreach_cb, user_data);
        /* not implemented */
 }
 
-static void __parse_transport_and_invoke_callback(const GstStructure *s, webrtc_callbacks_s *cb)
+static void __stats_transport_invoke_callback(const GstStructure *s, stats_userdata_s *user_data)
 {
-       RET_IF(cb == NULL, "cb is NULL");
+       RET_IF(user_data == NULL, "user_data is NULL");
 
        LOG_DEBUG_ENTER();
 
-       gst_structure_foreach(s, __gststructure_foreach_cb, NULL);
+       gst_structure_foreach(s, __gststructure_foreach_cb, user_data);
        /* not implemented */
 }
 
-static void __parse_candidate_pair_and_invoke_callback(const GstStructure *s, webrtc_callbacks_s *cb)
+static void __stats_candidate_pair_invoke_callback(const GstStructure *s, stats_userdata_s *user_data)
 {
-       RET_IF(cb == NULL, "cb is NULL");
+       RET_IF(user_data == NULL, "user_data is NULL");
 
        LOG_DEBUG_ENTER();
 
-       gst_structure_foreach(s, __gststructure_foreach_cb, NULL);
+       gst_structure_foreach(s, __gststructure_foreach_cb, user_data);
        /* not implemented */
 }
 
-static void __parse_local_candidate_and_invoke_callback(const GstStructure *s, webrtc_callbacks_s *cb)
+static void __stats_local_candidate_invoke_callback(const GstStructure *s, stats_userdata_s *user_data)
 {
-       RET_IF(cb == NULL, "cb is NULL");
+       RET_IF(user_data == NULL, "user_data is NULL");
 
        LOG_DEBUG_ENTER();
 
-       gst_structure_foreach(s, __gststructure_foreach_cb, NULL);
+       gst_structure_foreach(s, __gststructure_foreach_cb, user_data);
        /* not implemented */
 }
 
-static void __parse_remote_candidate_and_invoke_callback(const GstStructure *s, webrtc_callbacks_s *cb)
+static void __stats_remote_candidate_invoke_callback(const GstStructure *s, stats_userdata_s *user_data)
 {
-       RET_IF(cb == NULL, "cb is NULL");
+       RET_IF(user_data == NULL, "user_data is NULL");
 
        LOG_DEBUG_ENTER();
 
-       gst_structure_foreach(s, __gststructure_foreach_cb, NULL);
+       gst_structure_foreach(s, __gststructure_foreach_cb, user_data);
        /* not implemented */
 }
 
-static void __parse_certificate_and_invoke_callback(const GstStructure *s, webrtc_callbacks_s *cb)
+static void __stats_certificate_invoke_callback(const GstStructure *s, stats_userdata_s *user_data)
 {
-       RET_IF(cb == NULL, "cb is NULL");
+       RET_IF(user_data == NULL, "user_data is NULL");
 
        LOG_DEBUG_ENTER();
 
-       gst_structure_foreach(s, __gststructure_foreach_cb, NULL);
+       gst_structure_foreach(s, __gststructure_foreach_cb, user_data);
        /* not implemented */
 }
 
-typedef void (*stats_func)(const GstStructure *s, webrtc_callbacks_s *cb);
+typedef void (*stats_func)(const GstStructure *s, stats_userdata_s *user_data);
 
 /* Note that stats_type_mask_e below follows GstWebRTCStatsType of webrtc_fwd.h */
 typedef enum {
@@ -575,30 +488,40 @@ typedef enum {
 typedef struct {
        stats_func func;
        stats_type_mask_e type_mask;
+       stats_field_s **fields_list;
 } parse_stats_s;
 
 static parse_stats_s parse_stats[] = {
-       [GST_WEBRTC_STATS_CODEC] = { __parse_codec_and_invoke_callback, STATS_TYPE_CODEC_MASK },
-       [GST_WEBRTC_STATS_INBOUND_RTP] = { __parse_inbound_rtp_and_invoke_callback, STATS_TYPE_INBOUND_RTP_MASK },
-       [GST_WEBRTC_STATS_OUTBOUND_RTP] = { __parse_outbound_rtp_and_invoke_callback, STATS_TYPE_OUTBOUND_RTP_MASK },
-       [GST_WEBRTC_STATS_REMOTE_INBOUND_RTP] = { __parse_remote_inbound_rtp_and_invoke_callback, STATS_TYPE_REMOTE_INBOUND_RTP_MASK },
-       [GST_WEBRTC_STATS_REMOTE_OUTBOUND_RTP] = { __parse_remote_outbound_rtp_and_invoke_callback, STATS_TYPE_REMOTE_OUTBOUND_RTP_MASK },
-       [GST_WEBRTC_STATS_CSRC] = { __parse_csrc_and_invoke_callback, STATS_TYPE_CSRC_MASK },
-       [GST_WEBRTC_STATS_PEER_CONNECTION] = { __parse_peer_connection_and_invoke_callback, STATS_TYPE_PEER_CONNECTION_MASK },
-       [GST_WEBRTC_STATS_DATA_CHANNEL] = { __parse_data_channel_and_invoke_callback, STATS_TYPE_DATA_CHANNEL_MASK },
-       [GST_WEBRTC_STATS_STREAM] = { __parse_stream_and_invoke_callback, STATS_TYPE_STREAM_MASK },
-       [GST_WEBRTC_STATS_TRANSPORT] = { __parse_transport_and_invoke_callback, STATS_TYPE_TRANSPORT_MASK },
-       [GST_WEBRTC_STATS_CANDIDATE_PAIR] = { __parse_candidate_pair_and_invoke_callback, STATS_TYPE_CANDIDATE_PAIR_MASK },
-       [GST_WEBRTC_STATS_LOCAL_CANDIDATE] = { __parse_local_candidate_and_invoke_callback, STATS_TYPE_LOCAL_CANDIDATE_MASK },
-       [GST_WEBRTC_STATS_REMOTE_CANDIDATE] = { __parse_remote_candidate_and_invoke_callback, STATS_TYPE_REMOTE_CANDIDATE_MASK },
-       [GST_WEBRTC_STATS_CERTIFICATE] = { __parse_certificate_and_invoke_callback, STATS_TYPE_CERTIFICATE_MASK }
+       [GST_WEBRTC_STATS_CODEC] = {
+               __stats_codec_invoke_callback, STATS_TYPE_CODEC_MASK, __stats_codec_fields_list },
+       [GST_WEBRTC_STATS_INBOUND_RTP] = {
+               __stats_inbound_rtp_invoke_callback, STATS_TYPE_INBOUND_RTP_MASK, __stats_inbound_rtp_fields_list },
+       [GST_WEBRTC_STATS_OUTBOUND_RTP] = {
+               __stats_outbound_rtp_invoke_callback, STATS_TYPE_OUTBOUND_RTP_MASK, __stats_outbound_rtp_fields_list },
+       [GST_WEBRTC_STATS_REMOTE_INBOUND_RTP] = {
+               __stats_remote_inbound_rtp_invoke_callback, STATS_TYPE_REMOTE_INBOUND_RTP_MASK, __stats_remote_inbound_rtp_fields_list },
+       [GST_WEBRTC_STATS_REMOTE_OUTBOUND_RTP] = {
+               __stats_remote_outbound_rtp_invoke_callback, STATS_TYPE_REMOTE_OUTBOUND_RTP_MASK, __stats_remote_outbound_rtp_fields_list },
+       [GST_WEBRTC_STATS_CSRC] = {
+               __stats_csrc_invoke_callback, STATS_TYPE_CSRC_MASK, NULL },
+       [GST_WEBRTC_STATS_PEER_CONNECTION] = {
+               __stats_peer_connection_invoke_callback, STATS_TYPE_PEER_CONNECTION_MASK, __stats_peer_connection_fields_list },
+       [GST_WEBRTC_STATS_DATA_CHANNEL] = {
+               __stats_data_channel_invoke_callback, STATS_TYPE_DATA_CHANNEL_MASK, NULL },
+       [GST_WEBRTC_STATS_STREAM] = {
+               __stats_stream_invoke_callback, STATS_TYPE_STREAM_MASK, NULL },
+       [GST_WEBRTC_STATS_TRANSPORT] = {
+               __stats_transport_invoke_callback, STATS_TYPE_TRANSPORT_MASK, NULL },
+       [GST_WEBRTC_STATS_CANDIDATE_PAIR] = {
+               __stats_candidate_pair_invoke_callback, STATS_TYPE_CANDIDATE_PAIR_MASK, NULL },
+       [GST_WEBRTC_STATS_LOCAL_CANDIDATE] = {
+               __stats_local_candidate_invoke_callback, STATS_TYPE_LOCAL_CANDIDATE_MASK, NULL },
+       [GST_WEBRTC_STATS_REMOTE_CANDIDATE] = {
+               __stats_remote_candidate_invoke_callback, STATS_TYPE_REMOTE_CANDIDATE_MASK, NULL },
+       [GST_WEBRTC_STATS_CERTIFICATE] = {
+               __stats_certificate_invoke_callback, STATS_TYPE_CERTIFICATE_MASK, NULL }
 };
 
-typedef struct _stats_userdata_s {
-       webrtc_s *webrtc;
-       int type_mask;
-} stats_userdata_s;
-
 static gboolean __webrtcbin_stats_cb(GQuark field_id, const GValue *value, gpointer user_data)
 {
        stats_userdata_s *stats_userdata = (stats_userdata_s *)user_data;
@@ -619,7 +542,8 @@ static gboolean __webrtcbin_stats_cb(GQuark field_id, const GValue *value, gpoin
                        return TRUE;
                }
 
-               parse_stats[type].func(s, &stats_userdata->webrtc->stats_cb);
+               stats_userdata->fields_list = parse_stats[type].fields_list;
+               parse_stats[type].func(s, stats_userdata);
 
        } else {
                LOG_ERROR("unknown field \'%s\' value type: \'%s\'",
@@ -702,4 +626,13 @@ void _unset_stats_timer(webrtc_s *webrtc)
        LOG_DEBUG("remove stats_timer_src[%u]", webrtc->stats_timer_src);
        webrtc->stats_timer_src = 0;
 }
+
+void _init_stats_all_fields_list(void)
+{
+       int i, j;
+       for (i = 0; __stats_all_fields_list[i]; i++)
+               for (j = 0; __stats_all_fields_list[i][j].name; j++)
+                       if (__stats_all_fields_list[i][j].id == 0)
+                               __stats_all_fields_list[i][j].id = g_quark_from_string(__stats_all_fields_list[i][j].name);
+}
 //LCOV_EXCL_STOP
\ No newline at end of file