From: Sangchul Lee Date: Tue, 17 Aug 2021 11:25:54 +0000 (+0900) Subject: Add support for statistic functionality X-Git-Tag: submit/tizen/20210825.083902~1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=033f9f59c5616359fd66219ae767e191f0b549c6;p=platform%2Fcore%2Fapi%2Fwebrtc.git Add support for statistic functionality [Version] 0.2.80 [Issue Type] New feature Change-Id: I284137c02bc53c24e731c90265e6df3e420bbdef Signed-off-by: Sangchul Lee --- diff --git a/include/webrtc_private.h b/include/webrtc_private.h index b5a36abf..6b2ad59c 100644 --- a/include/webrtc_private.h +++ b/include/webrtc_private.h @@ -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); diff --git a/packaging/capi-media-webrtc.spec b/packaging/capi-media-webrtc.spec index 040f1cbe..64acc98d 100644 --- a/packaging/capi-media-webrtc.spec +++ b/packaging/capi-media-webrtc.spec @@ -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 index 00000000..5a33f42f --- /dev/null +++ b/src/webrtc_stats.c @@ -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); +}