371ec64dffc78bea967f87912c58f50d577b2066
[platform/core/api/mediastreamer.git] / src / media_streamer_gst_webrtc.c
1 /*
2  * Copyright (c) 2020 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 #ifndef GST_USE_UNSTABLE_API
18 #define GST_USE_UNSTABLE_API
19 #include <gst/webrtc/webrtc.h>
20 #endif
21 #include "media_streamer_util.h"
22 #include "media_streamer_priv.h"
23 #include "media_streamer_gst.h"
24 #include "media_streamer_gst_webrtc.h"
25 #include "media_streamer_node.h"
26
27 static gchar* __make_sdp_message(GstWebRTCSessionDescription *desc)
28 {
29         gchar *text;
30         JsonObject *msg, *sdp;
31
32         text = gst_sdp_message_as_text(desc->sdp);
33         sdp = json_object_new();
34
35         if (desc->type == GST_WEBRTC_SDP_TYPE_OFFER) {
36                 ms_info("Making offer message:\n%s", text);
37                 json_object_set_string_member(sdp, "type", "offer");
38         } else if (desc->type == GST_WEBRTC_SDP_TYPE_ANSWER) {
39                 ms_info("Making answer message:\n%s", text);
40                 json_object_set_string_member(sdp, "type", "answer");
41         } else {
42                 ms_error("invalid description type");
43                 return NULL;
44         }
45
46         json_object_set_string_member(sdp, "sdp", text);
47         g_free(text);
48
49         msg = json_object_new();
50         json_object_set_object_member(msg, "sdp", sdp);
51
52         text = ms_get_string_from_json_object(msg);
53
54         json_object_unref(msg);
55
56         return text;
57 }
58
59 static void __trigger_message_callback(media_streamer_node_s *webrtc_node, gchar *message)
60 {
61         ms_retm_if(webrtc_node == NULL, "webrtc_node is NULL");
62
63         ms_debug("message is : \n%s", message);
64
65         if (webrtc_node->user_cb.callback) {
66                 ms_debug("=====> Now trigger user callback(%p)", webrtc_node->user_cb.callback);
67                 ((media_streamer_webrtc_message_cb)(webrtc_node->user_cb.callback))(webrtc_node, message, webrtc_node->user_cb.user_data);
68                 ms_debug("<===== End of the callback");
69         } else {
70                 ms_warning("message callback is NULL");
71         }
72 }
73
74 static void __on_answer_created_cb(GstPromise *promise, gpointer user_data)
75 {
76         GstWebRTCSessionDescription *answer = NULL;
77         const GstStructure *reply;
78         media_streamer_node_s *webrtc_node = (media_streamer_node_s *)user_data;
79         node_info_s *node_klass_type = NULL;
80         GstElement *webrtcbin = NULL;
81         gchar *sdp_msg;
82
83         ms_retm_if(promise == NULL, "promise is NULL");
84         ms_retm_if(webrtc_node == NULL, "webrtc_node is NULL");
85         ms_retm_if(gst_promise_wait(promise) != GST_PROMISE_RESULT_REPLIED, "promise is not for replied result");
86
87         ms_debug_fenter();
88
89         node_klass_type = ms_node_get_klass_by_its_type(MEDIA_STREAMER_NODE_TYPE_WEBRTC);
90         if (!(webrtcbin = ms_find_element_in_bin_by_type(webrtc_node->gst_element, node_klass_type))) {
91                 ms_error("Could not find webrtcbin by type[%s, %s]", node_klass_type->klass_name, node_klass_type->default_name);
92                 return;
93         }
94
95         reply = gst_promise_get_reply(promise);
96         gst_structure_get(reply, "answer",
97                 GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &answer, NULL);
98         gst_promise_unref(promise);
99
100         promise = gst_promise_new();
101         g_signal_emit_by_name(G_OBJECT(webrtcbin), "set-local-description", answer, promise);
102         gst_promise_interrupt(promise);
103         gst_promise_unref(promise);
104
105         sdp_msg = __make_sdp_message(answer);
106
107         /* TODO: need to add to send this message to signalling server */
108         ms_debug("SDP message is sent: %s", sdp_msg);
109
110         g_free(sdp_msg);
111
112         gst_webrtc_session_description_free(answer);
113
114         ms_debug_fleave();
115 }
116
117 static void __on_offer_created_cb(GstPromise *promise, gpointer user_data)
118 {
119         GstWebRTCSessionDescription *offer = NULL;
120         const GstStructure *reply;
121         media_streamer_node_s *webrtc_node = (media_streamer_node_s *)user_data;
122         node_info_s *node_klass_type = NULL;
123         GstElement *webrtcbin = NULL;
124         gchar *sdp_msg;
125
126         ms_retm_if(promise == NULL, "promise is NULL");
127         ms_retm_if(webrtc_node == NULL, "webrtc_node is NULL");
128         ms_retm_if(gst_promise_wait(promise) != GST_PROMISE_RESULT_REPLIED, "promise is not for replied result");
129
130         ms_debug_fenter();
131
132         node_klass_type = ms_node_get_klass_by_its_type(MEDIA_STREAMER_NODE_TYPE_WEBRTC);
133         if (!(webrtcbin = ms_find_element_in_bin_by_type(webrtc_node->gst_element, node_klass_type))) {
134                 ms_error("Could not find webrtcbin by type[%s, %s]", node_klass_type->klass_name, node_klass_type->default_name);
135                 return;
136         }
137
138         reply = gst_promise_get_reply(promise);
139         gst_structure_get(reply, "offer",
140                 GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &offer, NULL);
141         gst_promise_unref(promise);
142
143         promise = gst_promise_new();
144         g_signal_emit_by_name(G_OBJECT(webrtcbin), "set-local-description", offer, promise);
145         gst_promise_interrupt(promise);
146         gst_promise_unref(promise);
147
148         sdp_msg = __make_sdp_message(offer);
149
150         __trigger_message_callback(webrtc_node, sdp_msg);
151
152         g_free(sdp_msg);
153         gst_webrtc_session_description_free(offer);
154
155         ms_debug_fleave();
156 }
157
158 void ms_webrtcbin_on_negotiation_process_answer(GstElement *webrtcbin, media_streamer_node_s *webrtc_node)
159 {
160         GstPromise *promise;
161
162         ms_retm_if(webrtcbin == NULL, "webrtcbin is NULL");
163         ms_retm_if(webrtc_node == NULL, "webrtc_node is NULL");
164
165         ms_debug_fenter();
166
167         promise = gst_promise_new_with_change_func(__on_answer_created_cb, webrtc_node, NULL);
168         g_signal_emit_by_name(G_OBJECT(webrtcbin), "create-answer", NULL, promise);
169
170         ms_debug_fleave();
171 }
172
173 void ms_webrtcbin_on_negotiation_needed_cb(GstElement *webrtcbin, gpointer user_data)
174 {
175         GstPromise *promise;
176
177         ms_retm_if(webrtcbin == NULL, "webrtcbin is NULL");
178         ms_retm_if(user_data == NULL, "user_data is NULL");
179
180         ms_debug_fenter();
181
182         promise = gst_promise_new_with_change_func(__on_offer_created_cb, user_data, NULL);
183         g_signal_emit_by_name(G_OBJECT(webrtcbin), "create-offer", NULL, promise);
184
185         ms_debug_fleave();
186 }
187
188 static gchar *__make_ice_candidate_message(guint mlineindex, gchar *candidate)
189 {
190         JsonObject *ice, *msg;
191         gchar *text;
192
193         ms_retvm_if(candidate == NULL, NULL, "candidate is NULL");
194
195         ice = json_object_new();
196         json_object_set_string_member(ice, "candidate", candidate);
197         json_object_set_int_member(ice, "sdpMLineIndex", mlineindex);
198
199         msg = json_object_new();
200         json_object_set_object_member(msg, "ice", ice);
201
202         text = ms_get_string_from_json_object(msg);
203
204         json_object_unref(msg);
205
206         return text;
207 }
208
209 void ms_webrtcbin_on_ice_candidate_cb(GstElement *webrtcbin, guint mlineindex, gchar *candidate, gpointer user_data)
210 {
211         gchar *ice_candidate_msg = NULL;
212         media_streamer_node_s *webrtc_node = (media_streamer_node_s *)user_data;
213
214         ms_retm_if(webrtcbin == NULL, "webrtcbin is NULL");
215         ms_retm_if(candidate == NULL, "candidate is NULL");
216         ms_retm_if(webrtc_node == NULL, "webrtc_node is NULL");
217
218         ice_candidate_msg = __make_ice_candidate_message(mlineindex, candidate);
219
220         __trigger_message_callback(webrtc_node, ice_candidate_msg);
221
222         g_free(ice_candidate_msg);
223 }
224
225 void ms_webrtcbin_notify_ice_gathering_state_cb(GstElement *webrtcbin, GParamSpec * pspec, gpointer user_data)
226 {
227         GstWebRTCICEGatheringState ice_gather_state;
228         const gchar *new_state = "UNKNOWN";
229
230         g_object_get(webrtcbin, "ice-gathering-state", &ice_gather_state, NULL);
231
232         switch (ice_gather_state) {
233         case GST_WEBRTC_ICE_GATHERING_STATE_NEW:
234                 new_state = "NEW";
235                 break;
236         case GST_WEBRTC_ICE_GATHERING_STATE_GATHERING:
237                 new_state = "GATHERING";
238                 break;
239         case GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE:
240                 new_state = "COMPLETE";
241                 break;
242         }
243
244         ms_info("ICE gathering state changed to [%s]", new_state);
245 }
246
247 static void __data_channel_on_error_cb(GObject *data_channel, gpointer user_data)
248 {
249         ms_retm_if(data_channel == NULL, "data_channel is NULL");
250
251         ms_debug_fenter();
252
253         ms_debug_fleave();
254 }
255
256 static void __data_channel_on_open_cb(GObject *data_channel, gpointer user_data)
257 {
258         GBytes *bytes = NULL;
259
260         ms_retm_if(data_channel == NULL, "data_channel is NULL");
261
262         ms_debug_fenter();
263
264         bytes = g_bytes_new("data", strlen("data"));
265
266         g_signal_emit_by_name(data_channel, "send-string", "Hi! from GStreamer");
267         g_signal_emit_by_name(data_channel, "send-data", bytes);
268
269         g_bytes_unref(bytes);
270
271         ms_debug_fleave();
272 }
273
274 static void __data_channel_on_close_cb(GObject *data_channel, gpointer user_data)
275 {
276         ms_retm_if(data_channel == NULL, "data_channel is NULL");
277
278         ms_debug_fenter();
279
280         ms_debug_fleave();
281 }
282
283 static void __data_channel_on_message_string_cb(GObject *data_channel, gchar *message, gpointer user_data)
284 {
285         ms_retm_if(data_channel == NULL, "data_channel is NULL");
286         ms_retm_if(message == NULL, "message is NULL");
287
288         ms_info("Received message: %s", message);
289 }
290
291 static void __connect_data_channel_signals(GObject *data_channel)
292 {
293         ms_retm_if(data_channel == NULL, "data_channel is NULL");
294
295         ms_debug_fenter();
296
297         g_signal_connect(data_channel, "on-error", G_CALLBACK(__data_channel_on_error_cb), NULL);
298         g_signal_connect(data_channel, "on-open", G_CALLBACK(__data_channel_on_open_cb), NULL);
299         g_signal_connect(data_channel, "on-close", G_CALLBACK(__data_channel_on_close_cb), NULL);
300         g_signal_connect(data_channel, "on-message-string", G_CALLBACK(__data_channel_on_message_string_cb), NULL);
301
302         ms_debug_fleave();
303 }
304
305 void ms_webrtcbin_on_data_channel_cb(GstElement *webrtcbin, GObject *data_channel, gpointer user_data)
306 {
307         media_streamer_s *ms_streamer = (media_streamer_s *)user_data;
308
309         ms_retm_if(ms_streamer == NULL, "ms_streamer is NULL");
310         ms_retm_if(data_channel == NULL, "data_channel is NULL");
311
312         ms_debug_fenter();
313
314         __connect_data_channel_signals(data_channel);
315
316         ms_debug_fleave();
317 }
318
319 void ms_webrtcbin_pad_added_cb(GstElement *webrtcbin, GstPad *new_pad, gpointer user_data)
320 {
321         media_streamer_s *ms_streamer = (media_streamer_s *)user_data;
322
323         ms_retm_if(new_pad == NULL, "new_pad is NULL");
324         ms_retm_if(ms_streamer == NULL, "ms_streamer is NULL");
325         ms_retm_if(GST_PAD_DIRECTION(new_pad) != GST_PAD_SRC, "new_pad is not for source");
326
327         ms_debug_fenter();
328
329         ms_debug("Pad [%s] added on [%s]", GST_PAD_NAME(new_pad), GST_ELEMENT_NAME(webrtcbin));
330
331         ms_debug_fleave();
332 }
333
334 GstElement *ms_webrtc_element_create(void)
335 {
336         GstElement *webrtc_container;
337         GstElement *webrtcbin;
338         GstGhostPad *ghost_pad_video_in;
339
340         ms_debug_fenter();
341
342         webrtc_container = gst_bin_new("webrtc_container");
343         ms_retvm_if(!webrtc_container, (GstElement *) NULL, "Error: creating elements for webrtc container");
344
345         ms_add_no_target_ghostpad(webrtc_container, MS_RTP_PAD_VIDEO_IN, GST_PAD_SINK);
346
347         MS_SET_INT_STATIC_STRING_PARAM(webrtc_container, MEDIA_STREAMER_PARAM_WEBRTC_PEER_TYPE, DEFAULT_WEBRTC_PEER);
348
349         if (!(webrtcbin = ms_element_create("webrtcbin", NULL))) {
350                 ms_error("Failed to create webrtcbin element");
351                 return NULL;
352         }
353
354         /* FIXME: these should be set from user */
355         g_object_set(G_OBJECT(webrtcbin), "bundle-policy", 3, NULL); // 3:max-bundle
356         g_object_set(G_OBJECT(webrtcbin), "stun-server", "stun://stun.l.google.com:19302", NULL);
357
358         ms_bin_add_element(webrtc_container, webrtcbin, FALSE);
359
360         if (!(ghost_pad_video_in = (GstGhostPad *)gst_element_get_static_pad(webrtc_container, MS_RTP_PAD_VIDEO_IN))) {
361                 ms_error("Failed to get ghost pad for webrtc_container");
362                 return NULL;
363         }
364
365         if (!(gst_ghost_pad_set_target(ghost_pad_video_in, gst_element_get_request_pad(webrtcbin, "sink_%u")))) {
366                 ms_info("Failed to gst_ghost_pad_set_target() for %s", MS_RTP_PAD_VIDEO_IN);
367                 /* release resources */
368                 return NULL;
369         }
370
371         ms_debug_fleave();
372
373         return webrtc_container;
374 }