Add new APIs to set/unset decoded ready callback of the node
[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_ice_candidate_message(guint mlineindex, gchar *candidate)
28 {
29         JsonObject *ice, *msg;
30         gchar *text;
31
32         ms_retvm_if(candidate == NULL, NULL, "candidate is NULL");
33
34         ice = json_object_new();
35         json_object_set_string_member(ice, "candidate", candidate);
36         json_object_set_int_member(ice, "sdpMLineIndex", mlineindex);
37
38         msg = json_object_new();
39         json_object_set_object_member(msg, "ice", ice);
40
41         text = ms_get_string_from_json_object(msg);
42
43         json_object_unref(msg);
44
45         return text;
46 }
47
48 static gchar* __make_sdp_message(GstWebRTCSessionDescription *desc)
49 {
50         gchar *text;
51         JsonObject *msg, *sdp;
52
53         text = gst_sdp_message_as_text(desc->sdp);
54         sdp = json_object_new();
55
56         if (desc->type == GST_WEBRTC_SDP_TYPE_OFFER) {
57                 ms_info("Making offer message:\n%s", text);
58                 json_object_set_string_member(sdp, "type", "offer");
59         } else if (desc->type == GST_WEBRTC_SDP_TYPE_ANSWER) {
60                 ms_info("Making answer message:\n%s", text);
61                 json_object_set_string_member(sdp, "type", "answer");
62         } else {
63                 ms_error("invalid description type");
64                 return NULL;
65         }
66
67         json_object_set_string_member(sdp, "sdp", text);
68         g_free(text);
69
70         msg = json_object_new();
71         json_object_set_object_member(msg, "sdp", sdp);
72
73         text = ms_get_string_from_json_object(msg);
74
75         json_object_unref(msg);
76
77         return text;
78 }
79
80 static void __trigger_message_callback(media_streamer_node_s *webrtc_node, gchar *message)
81 {
82         media_streamer_webrtc_callbacks_s *_callbacks;
83
84         ms_retm_if(webrtc_node == NULL, "webrtc_node is NULL");
85         ms_retm_if(message == NULL, "message is NULL");
86
87         ms_debug("message is : \n%s", message);
88
89         _callbacks = (media_streamer_webrtc_callbacks_s *) webrtc_node->callbacks_structure;
90         if (_callbacks->message_cb.callback) {
91                 ms_debug("=====> invoke message callback(%p)", _callbacks->message_cb.callback);
92                 ((media_streamer_webrtc_message_cb)(_callbacks->message_cb.callback))(webrtc_node, message, _callbacks->message_cb.user_data);
93                 ms_debug("<===== end of the callback");
94         } else {
95                 ms_warning("message callback is NULL");
96         }
97 }
98
99 static void __ms_webrtcbin_set_session_description(GstElement *webrtcbin, GstWebRTCSessionDescription *session_description, gboolean is_remote)
100 {
101         GstPromise *promise;
102         ms_retm_if(session_description == NULL, "session_description is NULL");
103         ms_retm_if(webrtcbin == NULL, "webrtcbin is NULL");
104
105         ms_debug_fenter();
106
107         promise = gst_promise_new();
108         g_signal_emit_by_name(webrtcbin, is_remote? "set-remote-description" : "set-local-description", session_description, promise);
109         gst_promise_interrupt(promise);
110         gst_promise_unref(promise);
111
112         ms_debug_fleave();
113 }
114
115 static void __on_offer_created_cb(GstPromise *promise, gpointer user_data)
116 {
117         GstWebRTCSessionDescription *offer = NULL;
118         const GstStructure *reply;
119         media_streamer_node_s *webrtc_node = (media_streamer_node_s *)user_data;
120         node_info_s *node_klass_type = NULL;
121         GstElement *webrtcbin = NULL;
122         gchar *sdp_msg;
123
124         ms_retm_if(promise == NULL, "promise is NULL");
125         ms_retm_if(webrtc_node == NULL, "webrtc_node is NULL");
126         ms_retm_if(gst_promise_wait(promise) != GST_PROMISE_RESULT_REPLIED, "promise is not for replied result");
127
128         ms_debug_fenter();
129
130         node_klass_type = ms_node_get_klass_by_its_type(MEDIA_STREAMER_NODE_TYPE_WEBRTC);
131         if (!(webrtcbin = ms_find_element_in_bin_by_type(webrtc_node->gst_element, node_klass_type))) {
132                 ms_error("Could not find webrtcbin by type[%s, %s]", node_klass_type->klass_name, node_klass_type->default_name);
133                 return;
134         }
135
136         reply = gst_promise_get_reply(promise);
137         gst_structure_get(reply, "offer",
138                 GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &offer, NULL);
139         gst_promise_unref(promise);
140
141         __ms_webrtcbin_set_session_description(webrtcbin, offer, FALSE);
142
143         sdp_msg = __make_sdp_message(offer);
144         gst_webrtc_session_description_free(offer);
145
146         /* Send local description to peer */
147         __trigger_message_callback(webrtc_node, sdp_msg);
148         g_free(sdp_msg);
149
150         ms_debug_fleave();
151 }
152
153 static void __on_answer_created_cb(GstPromise * promise, gpointer user_data)
154 {
155         GstWebRTCSessionDescription *answer = NULL;
156         const GstStructure *reply;
157         media_streamer_node_s *node = (media_streamer_node_s *)user_data;
158         node_info_s *node_klass_type = NULL;
159         gchar *sdp_msg;
160         GstElement *webrtcbin;
161
162         ms_retm_if(promise == NULL, "promise is NULL");
163         ms_retm_if(node == NULL, "node is NULL");
164         ms_retm_if(node->gst_element == NULL, "webrtc_container is NULL");
165         ms_retm_if(gst_promise_wait(promise) != GST_PROMISE_RESULT_REPLIED, "promise is not for replied result");
166
167         ms_debug_fenter();
168
169         node_klass_type = ms_node_get_klass_by_its_type(MEDIA_STREAMER_NODE_TYPE_WEBRTC);
170         if (!(webrtcbin = ms_find_element_in_bin_by_type(node->gst_element, node_klass_type))) {
171                 ms_error("Could not find webrtcbin by type[%s, %s]", node_klass_type->klass_name, node_klass_type->default_name);
172                 return;
173         }
174
175         reply = gst_promise_get_reply(promise);
176         gst_structure_get(reply, "answer", GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &answer, NULL);
177         gst_promise_unref(promise);
178
179         __ms_webrtcbin_set_session_description(webrtcbin, answer, FALSE);
180
181         sdp_msg = __make_sdp_message(answer);
182         gst_webrtc_session_description_free(answer);
183
184         /* Send local description to peer */
185         __trigger_message_callback(node, sdp_msg);
186         g_free(sdp_msg);
187
188         ms_debug_fleave();
189 }
190
191 int ms_webrtcbin_set_remote_session_description(media_streamer_node_s *node, const char *sdp_msg)
192 {
193         GstSDPMessage *gst_sdp;
194         gchar *sdp;
195         gchar *type;
196         GstWebRTCSessionDescription *answer, *offer;
197         node_info_s *node_klass_type;
198         GstElement *webrtcbin;
199         int ret = MEDIA_STREAMER_ERROR_NONE;
200
201         ms_retvm_if(node == NULL || node->gst_element == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "Error: empty webrtcbin");
202         ms_retvm_if(sdp_msg == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "sdp_msg is NULL");
203
204         ms_debug_fenter();
205
206
207         node_klass_type = ms_node_get_klass_by_its_type(MEDIA_STREAMER_NODE_TYPE_WEBRTC);
208         if (!(webrtcbin = ms_find_element_in_bin_by_type(node->gst_element, node_klass_type))) {
209                 ms_error("Could not find webrtcbin by type[%s, %s]", node_klass_type->klass_name, node_klass_type->default_name);
210                 return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
211         }
212
213         ret = ms_webrtc_get_sdp_from_message(sdp_msg, &sdp, &type);
214         if (ret != MEDIA_STREAMER_ERROR_NONE)
215                 goto end;
216
217         ret = gst_sdp_message_new(&gst_sdp);
218         if (ret != GST_SDP_OK) {
219                 ret = MEDIA_STREAMER_ERROR_INVALID_OPERATION;
220                 goto end;
221         }
222
223         ret = gst_sdp_message_parse_buffer((guint8 *)sdp, strlen(sdp), gst_sdp);
224         if (ret != GST_SDP_OK) {
225                 ret = MEDIA_STREAMER_ERROR_INVALID_OPERATION;
226                 goto end;
227         }
228
229         if (g_str_equal(type, "answer")) {
230                 answer = gst_webrtc_session_description_new(GST_WEBRTC_SDP_TYPE_ANSWER, gst_sdp);
231                 g_assert_nonnull(answer);
232
233                 __ms_webrtcbin_set_session_description(webrtcbin, answer, TRUE);
234                 gst_webrtc_session_description_free(answer);
235         } else if (g_str_equal(type, "offer")) {
236                 offer = gst_webrtc_session_description_new(GST_WEBRTC_SDP_TYPE_OFFER, gst_sdp);
237                 g_assert_nonnull(offer);
238
239                 __ms_webrtcbin_set_session_description(webrtcbin, offer, TRUE);
240                 gst_webrtc_session_description_free(offer);
241
242                 ms_webrtcbin_on_negotiation_process_answer(webrtcbin, node);
243         } else {
244                 ms_error("type is %s, it is not a answer or offer", type);
245         }
246
247 end:
248         MS_SAFE_GFREE(sdp);
249         MS_SAFE_GFREE(type);
250
251         ms_debug_fleave();
252
253         return ret;
254 }
255
256 int ms_webrtcbin_add_ice_candidate(media_streamer_node_s *node, const char *ice_msg)
257 {
258         gchar *candidate;
259         gint sdpmlineindex;
260         node_info_s *node_klass_type;
261         GstElement *webrtcbin;
262         int ret;
263
264         ms_retvm_if(node == NULL || node->gst_element == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "Error: empty webrtcbin");
265         ms_retvm_if(ice_msg == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "ice_msg is NULL");
266
267         ms_debug_fenter();
268
269         node_klass_type = ms_node_get_klass_by_its_type(MEDIA_STREAMER_NODE_TYPE_WEBRTC);
270         if (!(webrtcbin = ms_find_element_in_bin_by_type(node->gst_element, node_klass_type))) {
271                 ms_error("Could not find webrtcbin by type[%s, %s]", node_klass_type->klass_name, node_klass_type->default_name);
272                 return MEDIA_STREAMER_ERROR_INVALID_OPERATION;
273         }
274
275         ret = ms_webrtc_get_ice_candidate_from_message(ice_msg, &candidate, &sdpmlineindex);
276         if (ret != MEDIA_STREAMER_ERROR_NONE)
277                 return ret;
278
279         /*Add ice candidate sent by remote peer */
280         g_signal_emit_by_name(webrtcbin, "add-ice-candidate", sdpmlineindex, candidate);
281
282         g_free(candidate);
283
284         return MEDIA_STREAMER_ERROR_NONE;
285 }
286
287 void ms_webrtcbin_on_negotiation_process_answer(GstElement *webrtcbin, media_streamer_node_s *webrtc_node)
288 {
289         GstPromise *promise;
290
291         ms_retm_if(webrtcbin == NULL, "webrtcbin is NULL");
292
293         ms_debug_fenter();
294
295         promise = gst_promise_new_with_change_func(__on_answer_created_cb, webrtc_node, NULL);
296         g_signal_emit_by_name(G_OBJECT(webrtcbin), "create-answer", NULL, promise);
297
298         ms_debug_fleave();
299 }
300
301 void ms_webrtcbin_on_negotiation_needed_cb(GstElement *webrtcbin, gpointer user_data)
302 {
303         GstPromise *promise;
304
305         ms_retm_if(webrtcbin == NULL, "webrtcbin is NULL");
306         ms_retm_if(user_data == NULL, "user_data is NULL");
307
308         ms_debug_fenter();
309
310         promise = gst_promise_new_with_change_func(__on_offer_created_cb, user_data, NULL);
311         g_signal_emit_by_name(G_OBJECT(webrtcbin), "create-offer", NULL, promise);
312
313         ms_debug_fleave();
314 }
315
316 void ms_webrtcbin_on_ice_candidate_cb(GstElement *webrtcbin, guint mlineindex, gchar *candidate, gpointer user_data)
317 {
318         gchar *ice_candidate_msg = NULL;
319         media_streamer_node_s *webrtc_node = (media_streamer_node_s *)user_data;
320
321         ms_retm_if(webrtcbin == NULL, "webrtcbin is NULL");
322         ms_retm_if(candidate == NULL, "candidate is NULL");
323         ms_retm_if(webrtc_node == NULL, "webrtc_node is NULL");
324
325         ice_candidate_msg = __make_ice_candidate_message(mlineindex, candidate);
326
327         __trigger_message_callback(webrtc_node, ice_candidate_msg);
328
329         g_free(ice_candidate_msg);
330 }
331
332 void ms_webrtcbin_notify_ice_gathering_state_cb(GstElement *webrtcbin, GParamSpec * pspec, gpointer user_data)
333 {
334         GstWebRTCICEGatheringState ice_gather_state;
335         const gchar *new_state = "UNKNOWN";
336
337         g_object_get(webrtcbin, "ice-gathering-state", &ice_gather_state, NULL);
338
339         switch (ice_gather_state) {
340         case GST_WEBRTC_ICE_GATHERING_STATE_NEW:
341                 new_state = "NEW";
342                 break;
343         case GST_WEBRTC_ICE_GATHERING_STATE_GATHERING:
344                 new_state = "GATHERING";
345                 break;
346         case GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE:
347                 new_state = "COMPLETE";
348                 break;
349         }
350
351         ms_info("ICE gathering state changed to [%s]", new_state);
352 }
353
354 static void __data_channel_on_error_cb(GObject *data_channel, gpointer user_data)
355 {
356         ms_retm_if(data_channel == NULL, "data_channel is NULL");
357
358         ms_debug_fenter();
359
360         ms_debug_fleave();
361 }
362
363 static void __data_channel_on_open_cb(GObject *data_channel, gpointer user_data)
364 {
365         GBytes *bytes = NULL;
366
367         ms_retm_if(data_channel == NULL, "data_channel is NULL");
368
369         ms_debug_fenter();
370
371         bytes = g_bytes_new("data", strlen("data"));
372
373         g_signal_emit_by_name(data_channel, "send-string", "Hi! from GStreamer");
374         g_signal_emit_by_name(data_channel, "send-data", bytes);
375
376         g_bytes_unref(bytes);
377
378         ms_debug_fleave();
379 }
380
381 static void __data_channel_on_close_cb(GObject *data_channel, gpointer user_data)
382 {
383         ms_retm_if(data_channel == NULL, "data_channel is NULL");
384
385         ms_debug_fenter();
386
387         ms_debug_fleave();
388 }
389
390 static void __data_channel_on_message_string_cb(GObject *data_channel, gchar *message, gpointer user_data)
391 {
392         ms_retm_if(data_channel == NULL, "data_channel is NULL");
393         ms_retm_if(message == NULL, "message is NULL");
394
395         ms_info("Received message: %s", message);
396 }
397
398 static void __connect_data_channel_signals(GObject *data_channel)
399 {
400         ms_retm_if(data_channel == NULL, "data_channel is NULL");
401
402         ms_debug_fenter();
403
404         g_signal_connect(data_channel, "on-error", G_CALLBACK(__data_channel_on_error_cb), NULL);
405         g_signal_connect(data_channel, "on-open", G_CALLBACK(__data_channel_on_open_cb), NULL);
406         g_signal_connect(data_channel, "on-close", G_CALLBACK(__data_channel_on_close_cb), NULL);
407         g_signal_connect(data_channel, "on-message-string", G_CALLBACK(__data_channel_on_message_string_cb), NULL);
408
409         ms_debug_fleave();
410 }
411
412 void ms_webrtcbin_on_data_channel_cb(GstElement *webrtcbin, GObject *data_channel, gpointer user_data)
413 {
414         media_streamer_s *ms_streamer = (media_streamer_s *)user_data;
415
416         ms_retm_if(ms_streamer == NULL, "ms_streamer is NULL");
417         ms_retm_if(data_channel == NULL, "data_channel is NULL");
418
419         ms_debug_fenter();
420
421         __connect_data_channel_signals(data_channel);
422
423         ms_debug_fleave();
424 }
425
426 static void __trigger_decoded_ready_callback(media_streamer_node_s *webrtc_node, const gchar *new_pad_name, const gchar *media_type)
427 {
428         media_streamer_webrtc_callbacks_s *_callbacks;
429
430         ms_retm_if(webrtc_node == NULL, "webrtc_node is NULL");
431         ms_retm_if(new_pad_name == NULL, "new_pad_name is NULL");
432         ms_retm_if(media_type == NULL, "media_type is NULL");
433
434         _callbacks = (media_streamer_webrtc_callbacks_s *) webrtc_node->callbacks_structure;
435         if (!_callbacks->decoded_ready_cb.callback) {
436                 ms_warning("decoded_ready_cb.callback is NULL");
437                 return;
438         }
439
440         ms_debug("=====> invoke decoded ready callback(%p)", _callbacks->decoded_ready_cb.callback);
441         ((media_streamer_node_decoded_ready_cb)(_callbacks->decoded_ready_cb.callback))(webrtc_node,
442                                                                                                                                         (const char *)new_pad_name,
443                                                                                                                                         (const char *)media_type,
444                                                                                                                                         _callbacks->decoded_ready_cb.user_data);
445         ms_debug("<===== end of the callback");
446 }
447
448 static void __decodebin_pad_added_cb(GstElement *decodebin, GstPad *new_pad, gpointer user_data)
449 {
450         media_streamer_node_s *webrtc_node = (media_streamer_node_s *)user_data;
451         const gchar *new_pad_name;
452         const gchar *media_type;
453
454         ms_retm_if(decodebin == NULL, "decodebin is NULL");
455         ms_retm_if(new_pad == NULL, "new_pad is NULL");
456         ms_retm_if(webrtc_node == NULL, "webrtc_node is NULL");
457         ms_retm_if(GST_PAD_DIRECTION(new_pad) != GST_PAD_SRC, "new_pad is not for source");
458         ms_retm_if(gst_pad_has_current_caps(new_pad) == FALSE, "new_pad does not have caps");
459
460         new_pad_name = GST_PAD_NAME(new_pad);
461         media_type = gst_structure_get_name(gst_caps_get_structure(gst_pad_get_current_caps(new_pad), 0));
462
463         ms_debug("new_pad_name[%s], media_type[%s]", new_pad_name, media_type);
464
465         __trigger_decoded_ready_callback(webrtc_node, new_pad_name, media_type);
466 }
467
468 void ms_webrtcbin_pad_added_cb(GstElement *webrtcbin, GstPad *new_pad, gpointer user_data)
469 {
470         media_streamer_node_s *webrtc_node = (media_streamer_node_s *)user_data;
471         media_streamer_webrtc_callbacks_s *_callbacks;
472         GstPad *sink_pad;
473         GstElement *decodebin;
474
475         ms_retm_if(new_pad == NULL, "new_pad is NULL");
476         ms_retm_if(webrtc_node == NULL, "webrtc_node is NULL");
477         ms_retm_if(webrtc_node->callbacks_structure == NULL, "callbacks_structure is NULL");
478         ms_retm_if(GST_PAD_DIRECTION(new_pad) != GST_PAD_SRC, "new_pad is not for source");
479
480         ms_debug_fenter();
481
482         ms_debug("Pad [%s] added on [%s]", GST_PAD_NAME(new_pad), GST_ELEMENT_NAME(webrtcbin));
483
484         _callbacks = (media_streamer_webrtc_callbacks_s *) webrtc_node->callbacks_structure;
485         if (!_callbacks->decoded_ready_cb.callback) {
486                 ms_warning("decoded_ready_cb.callback is null, skip it");
487                 return;
488         }
489
490         decodebin = ms_element_create(DEFAULT_DECODEBIN, NULL);
491         ms_retm_if(decodebin == NULL, "decodebin is NULL");
492
493         ms_retm_if(webrtc_node->parent_streamer == NULL, "parent_streamer is NULL");
494         ms_retm_if(webrtc_node->parent_streamer->pipeline == NULL, "pipeline is NULL");
495         gst_bin_add(GST_BIN(webrtc_node->parent_streamer->pipeline), decodebin);
496
497         gst_element_sync_state_with_parent(decodebin);
498
499         g_signal_connect(decodebin, "pad-added", G_CALLBACK(__decodebin_pad_added_cb), webrtc_node);
500
501         sink_pad = gst_element_get_static_pad(decodebin, "sink");
502         ms_retm_if(sink_pad == NULL, "sink_pad is NULL");
503
504         gst_pad_link(new_pad, sink_pad);
505         gst_object_unref(sink_pad);
506
507         ms_debug_fleave();
508 }
509
510 GstElement *ms_webrtc_element_create(void)
511 {
512         GstElement *webrtc_container;
513         GstElement *webrtcbin;
514         GstGhostPad *ghost_pad_video_in;
515
516         ms_debug_fenter();
517
518         webrtc_container = gst_bin_new("webrtc_container");
519         ms_retvm_if(!webrtc_container, (GstElement *) NULL, "Error: creating elements for webrtc container");
520
521         ms_add_no_target_ghostpad(webrtc_container, MS_RTP_PAD_VIDEO_IN, GST_PAD_SINK);
522
523         MS_SET_INT_STATIC_STRING_PARAM(webrtc_container, MEDIA_STREAMER_PARAM_WEBRTC_PEER_TYPE, DEFAULT_WEBRTC_PEER);
524         MS_SET_INT_STATIC_STRING_PARAM(webrtc_container, MEDIA_STREAMER_PARAM_WEBRTC_REMOTE_SESSION_DESCRIPTION, NULL);
525         MS_SET_INT_STATIC_STRING_PARAM(webrtc_container, MEDIA_STREAMER_PARAM_WEBRTC_ADD_ICE_CANDIDATE, NULL);
526
527         if (!(webrtcbin = ms_element_create("webrtcbin", NULL))) {
528                 ms_error("Failed to create webrtcbin element");
529                 return NULL;
530         }
531
532         /* FIXME: these should be set from user */
533         g_object_set(G_OBJECT(webrtcbin), "bundle-policy", 3, NULL); // 3:max-bundle
534         g_object_set(G_OBJECT(webrtcbin), "stun-server", "stun://stun.l.google.com:19302", NULL);
535
536         ms_bin_add_element(webrtc_container, webrtcbin, FALSE);
537
538         if (!(ghost_pad_video_in = (GstGhostPad *)gst_element_get_static_pad(webrtc_container, MS_RTP_PAD_VIDEO_IN))) {
539                 ms_error("Failed to get ghost pad for webrtc_container");
540                 return NULL;
541         }
542
543         if (!(gst_ghost_pad_set_target(ghost_pad_video_in, gst_element_get_request_pad(webrtcbin, "sink_%u")))) {
544                 ms_info("Failed to gst_ghost_pad_set_target() for %s", MS_RTP_PAD_VIDEO_IN);
545                 /* release resources */
546                 return NULL;
547         }
548
549         ms_debug_fleave();
550
551         return webrtc_container;
552 }