[1.0.27] Add ConnectionStats abstraction
[platform/core/api/mediatransporter.git] / src / MediaTransporterConnectionStats.cpp
1 /**
2  * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <gio/gio.h>
18
19 #include "MediaTransporterConnectionStats.h"
20 #include "MediaTransporterLog.h"
21
22 using namespace tizen_media_transporter;
23
24 // AbstractConnectionStats
25 const GstStructure* AbstractConnectionStats::stats()
26 {
27         const GstStructure* structure = nullptr;
28         g_object_get(const_cast<GstElement*>(_element), "stats", &structure, NULL);
29
30         gchar* str = gst_structure_to_string(structure);
31         SECURE_LOG_DEBUG(">>> stats : %s", str);
32         g_free(str);
33
34         return structure;
35 }
36
37 gboolean AbstractConnectionStats::_statsForeachCb(GQuark fieldId, const GValue* val, gpointer userData)
38 {
39         std::string fieldName = g_quark_to_string(fieldId);
40
41         auto [ callback, prop ] = *(static_cast<std::pair<IInvokable*, ConnectionStatPropMap>*>(userData));
42
43         auto propInfo = translateGvalue(val, prop, fieldName);
44
45         if (propInfo.has_value()) {
46                 if (!callback || !callback->invokeReturn(propInfo.value())) {
47                         LOG_WARNING("stop calling stats callback");
48                         return FALSE;
49                 }
50         }
51
52         return TRUE;
53 }
54
55 std::optional<mtprConnectionStatsPropInfo> AbstractConnectionStats::translateGvalue(const GValue* val,
56                                                                                                                                                         const ConnectionStatPropMap& propList,
57                                                                                                                                                         const std::string& fieldName)
58 {
59         g_autofree gchar* addr = NULL;
60
61         if (propList.find(fieldName) == propList.end()) {
62                 LOG_DEBUG("not supported [%s]", fieldName.c_str());
63                 return std::nullopt;
64         }
65
66         mtprConnectionStatsProp prop = propList.at(fieldName);
67
68         switch (G_VALUE_TYPE(val)) {
69         case G_TYPE_BOOLEAN: {
70                 gboolean result = g_value_get_boolean(val);
71                 LOG_DEBUG("field[%s] GType[%s] value[%u]",
72                         fieldName.c_str(), g_type_name(G_VALUE_TYPE(val)), result);
73
74                 return mtprConnectionStatsPropInfo {
75                         fieldName.c_str(), prop, MTPR_CONNECTION_STATS_PROP_TYPE_BOOL, .v_bool = static_cast<bool>(result)
76                 };
77         }
78         case G_TYPE_INT: {
79                 gint result = g_value_get_int(val);
80                 LOG_DEBUG("field[%s] GType[%s] value[%d]",
81                         fieldName.c_str(), g_type_name(G_VALUE_TYPE(val)), result);
82
83                 return mtprConnectionStatsPropInfo {
84                         fieldName.c_str(), prop, MTPR_CONNECTION_STATS_PROP_TYPE_INT, .v_int = result
85                 };
86
87         }
88         case G_TYPE_UINT: {
89                 guint result = g_value_get_uint(val);
90                 LOG_DEBUG("field[%s] GType[%s] value[%u]",
91                         fieldName.c_str(), g_type_name(G_VALUE_TYPE(val)), result);
92
93                 return mtprConnectionStatsPropInfo {
94                         fieldName.c_str(), prop, MTPR_CONNECTION_STATS_PROP_TYPE_UINT, .v_uint = result
95                 };
96         }
97         case G_TYPE_INT64: {
98                 gint64 result = g_value_get_int64(val);
99                 LOG_DEBUG("field[%s] GType[%s] value[%" G_GINT64_FORMAT "]",
100                         fieldName.c_str(), g_type_name(G_VALUE_TYPE(val)), result);
101
102                 return mtprConnectionStatsPropInfo {
103                         fieldName.c_str(), prop, MTPR_CONNECTION_STATS_PROP_TYPE_INT64, .v_int64 = result
104                 };
105         }
106         case G_TYPE_UINT64: {
107                 guint64 result = g_value_get_uint64(val);
108                 LOG_DEBUG("field[%s] GType[%s] value[%" G_GUINT64_FORMAT "]",
109                         fieldName.c_str(), g_type_name(G_VALUE_TYPE(val)), result);
110
111                 return mtprConnectionStatsPropInfo {
112                         fieldName.c_str(), prop, MTPR_CONNECTION_STATS_PROP_TYPE_UINT64, .v_uint64 = result
113                 };
114         }
115         case G_TYPE_FLOAT: {
116                 gfloat result = g_value_get_float(val);
117                 LOG_DEBUG("field[%s] GType[%s] value[%f]",
118                         fieldName.c_str(), g_type_name(G_VALUE_TYPE(val)), result);
119
120                 return mtprConnectionStatsPropInfo {
121                         fieldName.c_str(), prop, MTPR_CONNECTION_STATS_PROP_TYPE_FLOAT, .v_float = result
122                 };
123         }
124         case G_TYPE_DOUBLE: {
125                 gdouble result = g_value_get_double(val);
126                 LOG_DEBUG("field[%s] GType[%s] value[%f]",
127                         fieldName.c_str(), g_type_name(G_VALUE_TYPE(val)), result);
128
129                 return mtprConnectionStatsPropInfo {
130                         fieldName.c_str(), prop, MTPR_CONNECTION_STATS_PROP_TYPE_DOUBLE, .v_double = result
131                 };
132         }
133         case G_TYPE_STRING: {
134                 const gchar *result = g_value_get_string(val);
135                 LOG_DEBUG("field[%s] GType[%s] value[%s]",
136                         fieldName.c_str(), g_type_name(G_VALUE_TYPE(val)), result);
137
138                 return mtprConnectionStatsPropInfo {
139                         fieldName.c_str(), prop, MTPR_CONNECTION_STATS_PROP_TYPE_STRING, .v_string = result
140                 };
141         }
142         default:
143                 if (G_VALUE_TYPE(val) == G_TYPE_SOCKET_ADDRESS) { /* non-constexpr : g_socket_address_get_type() */
144                         addr = g_inet_address_to_string
145                                 (g_inet_socket_address_get_address(G_INET_SOCKET_ADDRESS(g_value_get_object(val))));
146                         LOG_DEBUG("field[%s] GType[%s] value[%s]",
147                                 fieldName.c_str(), g_type_name(G_VALUE_TYPE(val)), addr);
148
149                         return mtprConnectionStatsPropInfo {
150                                 fieldName.c_str(), prop, MTPR_CONNECTION_STATS_PROP_TYPE_STRING, .v_string = addr
151                         };
152                 }
153
154                 LOG_ERROR("invalid type, field[%s] GType[%s]",
155                         fieldName.c_str(), g_type_name(G_VALUE_TYPE(val)));
156
157                 return std::nullopt;
158         }
159 }
160
161 // ConnectionStatsFactory
162 ForeachablePtr ConnectionStatsFactory::createReceiverSrtConnectionStats(const GstElement* element, InvokablePtr callback)
163 {
164         return ForeachablePtr(new ReceiverSrtConnectionStats(element, std::move(callback)));
165 }
166
167 ForeachablePtr ConnectionStatsFactory::createSenderSrtConnectionStats(const GstElement* element, InvokablePtr callback)
168 {
169         return ForeachablePtr(new SenderSrtConnectionStats(element, std::move(callback)));
170 }
171
172 // ReceiverSrtConnectionStats
173 ReceiverSrtConnectionStats::ReceiverSrtConnectionStats(const GstElement* element, InvokablePtr callback)
174         : AbstractConnectionStats(element, std::move(callback), {
175                 { "packets-received", MTPR_CONNECTION_STATS_PROP_PACKET_RECEIVED },
176                 { "packets-received-lost", MTPR_CONNECTION_STATS_PROP_PACKET_RECEIVED_LOST },
177                 { "packets-received-rate-mbps", MTPR_CONNECTION_STATS_PROP_RECEIVED_RATE_MBPS },
178                 { "negotiated-latency-ms", MTPR_CONNECTION_STATS_PROP_NEGOTIATED_LATENCY_MS },
179                 { "bandwidth-mbps", MTPR_CONNECTION_STATS_PROP_BANDWIDTH_MPBS },
180                 { "rtt-ms", MTPR_CONNECTION_STATS_PROP_RTT_MS },
181                 { "bytes-received-total", MTPR_CONNECTION_STATS_PROP_BYTES_RECEIVED_TOTAL }
182         })
183 {
184 }
185
186 void ReceiverSrtConnectionStats::foreach()
187 {
188         const GstStructure* structure = stats();
189         auto userdata = std::make_pair(_callback.get(), _prop);
190
191         gst_structure_foreach(structure, _statsForeachCb, &userdata);
192 }
193
194 // SenderSrtConnectionStats
195 SenderSrtConnectionStats::SenderSrtConnectionStats(const GstElement* element, InvokablePtr callback)
196         : AbstractConnectionStats(element, std::move(callback), {
197                 { "packets-sent", MTPR_CONNECTION_STATS_PROP_PACKET_SENT },
198                 { "packets-sent-lost", MTPR_CONNECTION_STATS_PROP_PACKET_SENT_LOST },
199                 { "send-rate-mbps", MTPR_CONNECTION_STATS_PROP_SEND_RATE_MPBS },
200                 { "negotiated-latency-ms", MTPR_CONNECTION_STATS_PROP_NEGOTIATED_LATENCY_MS },
201                 { "bandwidth-mbps", MTPR_CONNECTION_STATS_PROP_BANDWIDTH_MPBS },
202                 { "rtt-ms", MTPR_CONNECTION_STATS_PROP_RTT_MS },
203                 { "caller-address", MTPR_CONNECTION_STATS_PROP_CALLER_ADDRESS },
204                 { "bytes-sent-total", MTPR_CONNECTION_STATS_PROP_BYTES_SENT_TOTAL }
205         })
206 {
207 }
208
209 void SenderSrtConnectionStats::foreach()
210 {
211         const GstStructure* structure = stats();
212         auto userdata = std::make_pair(_callback.get(), _prop);
213         gboolean ret = FALSE;
214
215         if (gst_structure_has_field(structure, "callers")) {
216                 auto callers = static_cast<GValueArray *>(g_value_get_boxed(
217                         gst_structure_get_value(structure, "callers")
218                 ));
219
220                 LOG_DEBUG("SENDER >>> num of callers : %d", callers->n_values);
221                 for (int i = 0; i < (int)callers->n_values; i++) {
222                         const GstStructure* callerStats = (GstStructure *)g_value_get_boxed(&callers->values[i]);
223
224                         LOG_DEBUG("SENDER >>> caller idx : %d", i);
225                         ret = gst_structure_foreach(callerStats, _statsForeachCb, &userdata);
226                         if (!ret)
227                                 break;
228                 }
229         }
230
231         if (ret && gst_structure_has_field(structure, "bytes-sent-total")) {
232                 auto propInfo = translateGvalue(gst_structure_get_value(structure, "bytes-sent-total"),
233                                                                 _prop, "bytes-sent-total");
234                 if (propInfo.has_value()) {
235                         if (!_callback || !_callback->invokeReturn(propInfo.value())) {
236                                 LOG_WARNING("stop calling stats callback");
237                                 return;
238                         }
239                 }
240         }
241 }