From 33a6b9cfd159986108ca78d15ed3471110a7cac4 Mon Sep 17 00:00:00 2001 From: Sangchul Lee Date: Wed, 16 Mar 2022 11:23:40 +0900 Subject: [PATCH] webrtc_stats: Add user callback parameters to _webrtcbin_get_stats() Some improvements are also applied : Use gst_promise_new_with_change_func()'s notify parameter to free userdata : Rename __gststructure_foreach_cb() to __stats_field_foreach_cb() : Separate user data structure for __stats_field_foreach_cb() [Version] 0.3.67 [Issue Type] Improvement Change-Id: Iad04f0b544b0c2c311a83c0497996da6f47a6d72 Signed-off-by: Sangchul Lee --- include/webrtc_private.h | 4 +- packaging/capi-media-webrtc.spec | 2 +- src/webrtc_stats.c | 163 ++++++++++++++++++------------- 3 files changed, 95 insertions(+), 74 deletions(-) diff --git a/include/webrtc_private.h b/include/webrtc_private.h index bf03fd78..e0b1ef13 100644 --- a/include/webrtc_private.h +++ b/include/webrtc_private.h @@ -475,8 +475,6 @@ typedef struct _webrtc_s { webrtc_callbacks_s encoded_video_frame_cb; webrtc_callbacks_s data_channel_cb; - webrtc_callbacks_s stats_cb; - webrtc_negotiation_states_s negotiation_states; #ifndef TIZEN_TV webrtc_resource_s resource; @@ -713,7 +711,7 @@ 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, int type_mask); +void _webrtcbin_get_stats(webrtc_s *webrtc, int type_mask, void *callback, void *user_data); void _set_stats_timer(webrtc_s *webrtc); void _unset_stats_timer(webrtc_s *webrtc); void _init_stats_all_fields_list(void); diff --git a/packaging/capi-media-webrtc.spec b/packaging/capi-media-webrtc.spec index fdd1fd37..625332ee 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.3.66 +Version: 0.3.67 Release: 0 Group: Multimedia/API License: Apache-2.0 diff --git a/src/webrtc_stats.c b/src/webrtc_stats.c index c2c4f755..271e8636 100644 --- a/src/webrtc_stats.c +++ b/src/webrtc_stats.c @@ -256,19 +256,45 @@ static stats_field_s *__stats_transport_fields_list[] = { NULL }; -typedef struct _stats_userdata_s { +/* Note that stats_type_mask_e below follows GstWebRTCStatsType of webrtc_fwd.h */ +typedef enum { + STATS_TYPE_ALL_MASK = 0xFFFF, + STATS_TYPE_CODEC_MASK = 0x0001, + STATS_TYPE_INBOUND_RTP_MASK = 0x0002, + STATS_TYPE_OUTBOUND_RTP_MASK = 0x0004, + STATS_TYPE_REMOTE_INBOUND_RTP_MASK = 0x0008, + STATS_TYPE_REMOTE_OUTBOUND_RTP_MASK = 0x0010, + STATS_TYPE_CSRC_MASK = 0x0020, + STATS_TYPE_PEER_CONNECTION_MASK = 0x0040, + STATS_TYPE_DATA_CHANNEL_MASK = 0x0080, + STATS_TYPE_STREAM_MASK = 0x0100, + STATS_TYPE_TRANSPORT_MASK = 0x0200, + STATS_TYPE_CANDIDATE_PAIR_MASK = 0x0400, + STATS_TYPE_LOCAL_CANDIDATE_MASK = 0x0800, + STATS_TYPE_REMOTE_CANDIDATE_MASK = 0x1000, + STATS_TYPE_CERTIFICATE_MASK = 0x2000, +} stats_type_mask_e; + +typedef struct _promise_userdata_s { webrtc_s *webrtc; int type_mask; + webrtc_callbacks_s stats_cb; +} promise_userdata_s; + +typedef struct _stats_userdata_s { + promise_userdata_s *p_userdata; + stats_type_mask_e type; stats_field_s **fields_list; } stats_userdata_s; -static gboolean __gststructure_foreach_cb(GQuark field_id, const GValue *val, gpointer user_data) +static gboolean __stats_field_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->p_userdata == NULL, FALSE, "p_userdata is NULL"); + RET_VAL_IF(stats->p_userdata->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 */ @@ -334,17 +360,20 @@ invoke_cb: return TRUE; } -static void __stats_codec_invoke_callback(const GstStructure *s, stats_userdata_s *user_data) +static void __stats_codec_invoke_callback(const GstStructure *s, stats_type_mask_e type_mask, stats_field_s **fields_list, promise_userdata_s *user_data) { + stats_userdata_s stats_userdata = { .p_userdata = user_data, .type = type_mask, .fields_list = fields_list }; + RET_IF(user_data == NULL, "user_data is NULL"); LOG_DEBUG_ENTER(); - gst_structure_foreach(s, __gststructure_foreach_cb, user_data); + gst_structure_foreach(s, __stats_field_foreach_cb, &stats_userdata); } -static void __stats_inbound_rtp_invoke_callback(const GstStructure *s, stats_userdata_s *user_data) +static void __stats_inbound_rtp_invoke_callback(const GstStructure *s, stats_type_mask_e type_mask, stats_field_s **fields_list, promise_userdata_s *user_data) { + stats_userdata_s stats_userdata = { .p_userdata = user_data, .type = type_mask, .fields_list = fields_list }; GstStructure *rtpjitterbuffer_stats; GstStructure *rtpsource_stats; @@ -352,160 +381,153 @@ static void __stats_inbound_rtp_invoke_callback(const GstStructure *s, stats_use LOG_DEBUG_ENTER(); - gst_structure_foreach(s, __gststructure_foreach_cb, user_data); + gst_structure_foreach(s, __stats_field_foreach_cb, &stats_userdata); gst_structure_get(s, "gst-rtpjitterbuffer-stats", GST_TYPE_STRUCTURE, &rtpjitterbuffer_stats, "gst-rtpsource-stats", GST_TYPE_STRUCTURE, &rtpsource_stats, NULL); LOG_DEBUG("gst-rtpjitterbuffer-stats ---> "); - gst_structure_foreach(rtpjitterbuffer_stats, __gststructure_foreach_cb, user_data); + gst_structure_foreach(rtpjitterbuffer_stats, __stats_field_foreach_cb, &stats_userdata); LOG_DEBUG("gst-rtpsource-stats ---> "); - gst_structure_foreach(rtpsource_stats, __gststructure_foreach_cb, user_data); + gst_structure_foreach(rtpsource_stats, __stats_field_foreach_cb, &stats_userdata); } -static void __stats_outbound_rtp_invoke_callback(const GstStructure *s, stats_userdata_s *user_data) +static void __stats_outbound_rtp_invoke_callback(const GstStructure *s, stats_type_mask_e type_mask, stats_field_s **fields_list, promise_userdata_s *user_data) { + stats_userdata_s stats_userdata = { .p_userdata = user_data, .type = type_mask, .fields_list = fields_list }; GstStructure *rtpsource_stats; RET_IF(user_data == NULL, "user_data is NULL"); LOG_DEBUG_ENTER(); - gst_structure_foreach(s, __gststructure_foreach_cb, user_data); + gst_structure_foreach(s, __stats_field_foreach_cb, &stats_userdata); gst_structure_get(s, "gst-rtpsource-stats", GST_TYPE_STRUCTURE, &rtpsource_stats, NULL); LOG_DEBUG("gst-rtpsource-stats ---> "); - gst_structure_foreach(rtpsource_stats, __gststructure_foreach_cb, user_data); + gst_structure_foreach(rtpsource_stats, __stats_field_foreach_cb, &stats_userdata); } -static void __stats_remote_inbound_rtp_invoke_callback(const GstStructure *s, stats_userdata_s *user_data) +static void __stats_remote_inbound_rtp_invoke_callback(const GstStructure *s, stats_type_mask_e type_mask, stats_field_s **fields_list, promise_userdata_s *user_data) { + stats_userdata_s stats_userdata = { .p_userdata = user_data, .type = type_mask, .fields_list = fields_list }; RET_IF(user_data == NULL, "user_data is NULL"); LOG_DEBUG_ENTER(); /* FIXME: type of 'packets-lost(int)' should be fixed.*/ - gst_structure_foreach(s, __gststructure_foreach_cb, user_data); + gst_structure_foreach(s, __stats_field_foreach_cb, &stats_userdata); } -static void __stats_remote_outbound_rtp_invoke_callback(const GstStructure *s, stats_userdata_s *user_data) +static void __stats_remote_outbound_rtp_invoke_callback(const GstStructure *s, stats_type_mask_e type_mask, stats_field_s **fields_list, promise_userdata_s *user_data) { + stats_userdata_s stats_userdata = { .p_userdata = user_data, .type = type_mask, .fields_list = fields_list }; RET_IF(user_data == NULL, "user_data is NULL"); LOG_DEBUG_ENTER(); - gst_structure_foreach(s, __gststructure_foreach_cb, user_data); + gst_structure_foreach(s, __stats_field_foreach_cb, &stats_userdata); } -static void __stats_csrc_invoke_callback(const GstStructure *s, stats_userdata_s *user_data) +static void __stats_csrc_invoke_callback(const GstStructure *s, stats_type_mask_e type_mask, stats_field_s **fields_list, promise_userdata_s *user_data) { + stats_userdata_s stats_userdata = { .p_userdata = user_data, .type = type_mask, .fields_list = fields_list }; RET_IF(user_data == NULL, "user_data is NULL"); LOG_DEBUG_ENTER(); - gst_structure_foreach(s, __gststructure_foreach_cb, user_data); + gst_structure_foreach(s, __stats_field_foreach_cb, &stats_userdata); /* not implemented */ } -static void __stats_peer_connection_invoke_callback(const GstStructure *s, stats_userdata_s *user_data) +static void __stats_peer_connection_invoke_callback(const GstStructure *s, stats_type_mask_e type_mask, stats_field_s **fields_list, promise_userdata_s *user_data) { + stats_userdata_s stats_userdata = { .p_userdata = user_data, .type = type_mask, .fields_list = fields_list }; RET_IF(user_data == NULL, "user_data is NULL"); LOG_DEBUG_ENTER(); - gst_structure_foreach(s, __gststructure_foreach_cb, user_data); + gst_structure_foreach(s, __stats_field_foreach_cb, &stats_userdata); } -static void __stats_data_channel_invoke_callback(const GstStructure *s, stats_userdata_s *user_data) +static void __stats_data_channel_invoke_callback(const GstStructure *s, stats_type_mask_e type_mask, stats_field_s **fields_list, promise_userdata_s *user_data) { + stats_userdata_s stats_userdata = { .p_userdata = user_data, .type = type_mask, .fields_list = fields_list }; RET_IF(user_data == NULL, "user_data is NULL"); LOG_DEBUG_ENTER(); - gst_structure_foreach(s, __gststructure_foreach_cb, user_data); + gst_structure_foreach(s, __stats_field_foreach_cb, &stats_userdata); /* not implemented */ } -static void __stats_stream_invoke_callback(const GstStructure *s, stats_userdata_s *user_data) +static void __stats_stream_invoke_callback(const GstStructure *s, stats_type_mask_e type_mask, stats_field_s **fields_list, promise_userdata_s *user_data) { + stats_userdata_s stats_userdata = { .p_userdata = user_data, .type = type_mask, .fields_list = fields_list }; RET_IF(user_data == NULL, "user_data is NULL"); LOG_DEBUG_ENTER(); - gst_structure_foreach(s, __gststructure_foreach_cb, user_data); + gst_structure_foreach(s, __stats_field_foreach_cb, &stats_userdata); /* not implemented */ } -static void __stats_transport_invoke_callback(const GstStructure *s, stats_userdata_s *user_data) +static void __stats_transport_invoke_callback(const GstStructure *s, stats_type_mask_e type_mask, stats_field_s **fields_list, promise_userdata_s *user_data) { + stats_userdata_s stats_userdata = { .p_userdata = user_data, .type = type_mask, .fields_list = fields_list }; RET_IF(user_data == NULL, "user_data is NULL"); LOG_DEBUG_ENTER(); - gst_structure_foreach(s, __gststructure_foreach_cb, user_data); + gst_structure_foreach(s, __stats_field_foreach_cb, &stats_userdata); } -static void __stats_candidate_pair_invoke_callback(const GstStructure *s, stats_userdata_s *user_data) +static void __stats_candidate_pair_invoke_callback(const GstStructure *s, stats_type_mask_e type_mask, stats_field_s **fields_list, promise_userdata_s *user_data) { + stats_userdata_s stats_userdata = { .p_userdata = user_data, .type = type_mask, .fields_list = fields_list }; RET_IF(user_data == NULL, "user_data is NULL"); LOG_DEBUG_ENTER(); - gst_structure_foreach(s, __gststructure_foreach_cb, user_data); + gst_structure_foreach(s, __stats_field_foreach_cb, &stats_userdata); /* not implemented */ } -static void __stats_local_candidate_invoke_callback(const GstStructure *s, stats_userdata_s *user_data) +static void __stats_local_candidate_invoke_callback(const GstStructure *s, stats_type_mask_e type_mask, stats_field_s **fields_list, promise_userdata_s *user_data) { + stats_userdata_s stats_userdata = { .p_userdata = user_data, .type = type_mask, .fields_list = fields_list }; RET_IF(user_data == NULL, "user_data is NULL"); LOG_DEBUG_ENTER(); - gst_structure_foreach(s, __gststructure_foreach_cb, user_data); + gst_structure_foreach(s, __stats_field_foreach_cb, &stats_userdata); /* not implemented */ } -static void __stats_remote_candidate_invoke_callback(const GstStructure *s, stats_userdata_s *user_data) +static void __stats_remote_candidate_invoke_callback(const GstStructure *s, stats_type_mask_e type_mask, stats_field_s **fields_list, promise_userdata_s *user_data) { + stats_userdata_s stats_userdata = { .p_userdata = user_data, .type = type_mask, .fields_list = fields_list }; RET_IF(user_data == NULL, "user_data is NULL"); LOG_DEBUG_ENTER(); - gst_structure_foreach(s, __gststructure_foreach_cb, user_data); + gst_structure_foreach(s, __stats_field_foreach_cb, &stats_userdata); /* not implemented */ } -static void __stats_certificate_invoke_callback(const GstStructure *s, stats_userdata_s *user_data) +static void __stats_certificate_invoke_callback(const GstStructure *s, stats_type_mask_e type_mask, stats_field_s **fields_list, promise_userdata_s *user_data) { + stats_userdata_s stats_userdata = { .p_userdata = user_data, .type = type_mask, .fields_list = fields_list }; RET_IF(user_data == NULL, "user_data is NULL"); LOG_DEBUG_ENTER(); - gst_structure_foreach(s, __gststructure_foreach_cb, user_data); + gst_structure_foreach(s, __stats_field_foreach_cb, &stats_userdata); /* not implemented */ } -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 { - STATS_TYPE_ALL_MASK = 0xFFFF, - STATS_TYPE_CODEC_MASK = 0x0001, - STATS_TYPE_INBOUND_RTP_MASK = 0x0002, - STATS_TYPE_OUTBOUND_RTP_MASK = 0x0004, - STATS_TYPE_REMOTE_INBOUND_RTP_MASK = 0x0008, - STATS_TYPE_REMOTE_OUTBOUND_RTP_MASK = 0x0010, - STATS_TYPE_CSRC_MASK = 0x0020, - STATS_TYPE_PEER_CONNECTION_MASK = 0x0040, - STATS_TYPE_DATA_CHANNEL_MASK = 0x0080, - STATS_TYPE_STREAM_MASK = 0x0100, - STATS_TYPE_TRANSPORT_MASK = 0x0200, - STATS_TYPE_CANDIDATE_PAIR_MASK = 0x0400, - STATS_TYPE_LOCAL_CANDIDATE_MASK = 0x0800, - STATS_TYPE_REMOTE_CANDIDATE_MASK = 0x1000, - STATS_TYPE_CERTIFICATE_MASK = 0x2000, -} stats_type_mask_e; +typedef void (*stats_func)(const GstStructure *s, stats_type_mask_e type_mask, stats_field_s **fields_list, promise_userdata_s *user_data); typedef struct { stats_func func; @@ -546,11 +568,11 @@ static parse_stats_s parse_stats[] = { static gboolean __webrtcbin_stats_cb(GQuark field_id, const GValue *value, gpointer user_data) { - stats_userdata_s *stats_userdata = (stats_userdata_s *)user_data; + promise_userdata_s *userdata = (promise_userdata_s *)user_data; const GstStructure *s; GstWebRTCStatsType type; - RET_VAL_IF(stats_userdata == NULL, TRUE, "stats_userdata is NULL"); + RET_VAL_IF(userdata == NULL, TRUE, "userdata is NULL"); if (GST_VALUE_HOLDS_STRUCTURE(value)) { s = gst_value_get_structure(value); @@ -559,13 +581,12 @@ static gboolean __webrtcbin_stats_cb(GQuark field_id, const GValue *value, gpoin RET_VAL_IF((type < GST_WEBRTC_STATS_CODEC || type > GST_WEBRTC_STATS_CERTIFICATE), TRUE, "invalid type(%u)", type); - if (!(stats_userdata->type_mask & parse_stats[type].type_mask)) { - LOG_DEBUG("skip this type[%u], type_mask[0x%x]", type, stats_userdata->type_mask); + if (!(userdata->type_mask & parse_stats[type].type_mask)) { + LOG_DEBUG("skip this type[%u], type_mask[0x%x]", type, userdata->type_mask); return TRUE; } - stats_userdata->fields_list = parse_stats[type].fields_list; - parse_stats[type].func(s, stats_userdata); + parse_stats[type].func(s, parse_stats[type].type_mask, parse_stats[type].fields_list, userdata); } else { LOG_ERROR("unknown field \'%s\' value type: \'%s\'", @@ -586,23 +607,25 @@ static void __webrtcbin_get_stats_cb(GstPromise *promise, gpointer user_data) RET_IF(stats == NULL, "failed to gst_promise_get_reply()"); gst_structure_foreach(stats, __webrtcbin_stats_cb, user_data); - - g_free(user_data); } -void _webrtcbin_get_stats(webrtc_s *webrtc, int type_mask) +void _webrtcbin_get_stats(webrtc_s *webrtc, int type_mask, void *callback, void *user_data) { GstPromise *promise; - stats_userdata_s *stats_userdata; + promise_userdata_s *userdata; RET_IF(webrtc == NULL, "webrtc is NULL"); RET_IF(webrtc->gst.webrtcbin == NULL, "webrtcbin is NULL"); - stats_userdata = g_new0(stats_userdata_s, 1); - stats_userdata->webrtc = webrtc; - stats_userdata->type_mask = type_mask; + userdata = g_new0(promise_userdata_s, 1); + userdata->webrtc = webrtc; + userdata->type_mask = type_mask; + if (callback) { + userdata->stats_cb.callback = callback; + userdata->stats_cb.user_data = user_data; + } - promise = gst_promise_new_with_change_func((GstPromiseChangeFunc)__webrtcbin_get_stats_cb, stats_userdata, NULL); + promise = gst_promise_new_with_change_func((GstPromiseChangeFunc)__webrtcbin_get_stats_cb, userdata, g_free); g_signal_emit_by_name(webrtc->gst.webrtcbin, "get-stats", NULL, promise); LOG_DEBUG("emitting 'get-stats' on %p", webrtc->gst.webrtcbin); @@ -615,7 +638,7 @@ static gboolean __get_stats_periodically(gpointer user_data) webrtc_s *webrtc = (webrtc_s *)user_data; if (webrtc->state == WEBRTC_STATE_PLAYING) - _webrtcbin_get_stats(webrtc, STATS_TYPE_ALL_MASK); + _webrtcbin_get_stats(webrtc, STATS_TYPE_ALL_MASK, NULL, NULL); return G_SOURCE_CONTINUE; } @@ -657,4 +680,4 @@ void _init_stats_all_fields_list(void) 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 +//LCOV_EXCL_STOP -- 2.34.1