2 * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #ifndef GST_USE_UNSTABLE_API
18 #define GST_USE_UNSTABLE_API
19 #include <gst/webrtc/webrtc.h>
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"
27 static gchar* __make_sdp_message(GstWebRTCSessionDescription *desc)
30 JsonObject *msg, *sdp;
32 text = gst_sdp_message_as_text(desc->sdp);
33 sdp = json_object_new();
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");
42 ms_error("invalid description type");
46 json_object_set_string_member(sdp, "sdp", text);
49 msg = json_object_new();
50 json_object_set_object_member(msg, "sdp", sdp);
52 text = ms_get_string_from_json_object(msg);
54 json_object_unref(msg);
59 static void __trigger_message_callback(media_streamer_node_s *webrtc_node, gchar *message)
61 ms_retm_if(webrtc_node == NULL, "webrtc_node is NULL");
63 ms_debug("message is : \n%s", message);
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");
70 ms_warning("message callback is NULL");
74 static void __on_answer_created_cb(GstPromise *promise, gpointer user_data)
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;
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");
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);
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);
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);
105 sdp_msg = __make_sdp_message(answer);
107 /* TODO: need to add to send this message to signalling server */
108 ms_debug("SDP message is sent: %s", sdp_msg);
112 gst_webrtc_session_description_free(answer);
117 static void __on_offer_created_cb(GstPromise *promise, gpointer user_data)
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;
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");
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);
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);
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);
148 sdp_msg = __make_sdp_message(offer);
150 __trigger_message_callback(webrtc_node, sdp_msg);
153 gst_webrtc_session_description_free(offer);
158 void ms_webrtcbin_on_negotiation_process_answer(GstElement *webrtcbin, media_streamer_node_s *webrtc_node)
162 ms_retm_if(webrtcbin == NULL, "webrtcbin is NULL");
163 ms_retm_if(webrtc_node == NULL, "webrtc_node is NULL");
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);
173 void ms_webrtcbin_on_negotiation_needed_cb(GstElement *webrtcbin, gpointer user_data)
177 ms_retm_if(webrtcbin == NULL, "webrtcbin is NULL");
178 ms_retm_if(user_data == NULL, "user_data is NULL");
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);
188 static gchar *__make_ice_candidate_message(guint mlineindex, gchar *candidate)
190 JsonObject *ice, *msg;
193 ms_retvm_if(candidate == NULL, NULL, "candidate is NULL");
195 ice = json_object_new();
196 json_object_set_string_member(ice, "candidate", candidate);
197 json_object_set_int_member(ice, "sdpMLineIndex", mlineindex);
199 msg = json_object_new();
200 json_object_set_object_member(msg, "ice", ice);
202 text = ms_get_string_from_json_object(msg);
204 json_object_unref(msg);
209 void ms_webrtcbin_on_ice_candidate_cb(GstElement *webrtcbin, guint mlineindex, gchar *candidate, gpointer user_data)
211 gchar *ice_candidate_msg = NULL;
212 media_streamer_node_s *webrtc_node = (media_streamer_node_s *)user_data;
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");
218 ice_candidate_msg = __make_ice_candidate_message(mlineindex, candidate);
220 __trigger_message_callback(webrtc_node, ice_candidate_msg);
222 g_free(ice_candidate_msg);
225 void ms_webrtcbin_notify_ice_gathering_state_cb(GstElement *webrtcbin, GParamSpec * pspec, gpointer user_data)
227 GstWebRTCICEGatheringState ice_gather_state;
228 const gchar *new_state = "UNKNOWN";
230 g_object_get(webrtcbin, "ice-gathering-state", &ice_gather_state, NULL);
232 switch (ice_gather_state) {
233 case GST_WEBRTC_ICE_GATHERING_STATE_NEW:
236 case GST_WEBRTC_ICE_GATHERING_STATE_GATHERING:
237 new_state = "GATHERING";
239 case GST_WEBRTC_ICE_GATHERING_STATE_COMPLETE:
240 new_state = "COMPLETE";
244 ms_info("ICE gathering state changed to [%s]", new_state);
247 static void __data_channel_on_error_cb(GObject *data_channel, gpointer user_data)
249 ms_retm_if(data_channel == NULL, "data_channel is NULL");
256 static void __data_channel_on_open_cb(GObject *data_channel, gpointer user_data)
258 GBytes *bytes = NULL;
260 ms_retm_if(data_channel == NULL, "data_channel is NULL");
264 bytes = g_bytes_new("data", strlen("data"));
266 g_signal_emit_by_name(data_channel, "send-string", "Hi! from GStreamer");
267 g_signal_emit_by_name(data_channel, "send-data", bytes);
269 g_bytes_unref(bytes);
274 static void __data_channel_on_close_cb(GObject *data_channel, gpointer user_data)
276 ms_retm_if(data_channel == NULL, "data_channel is NULL");
283 static void __data_channel_on_message_string_cb(GObject *data_channel, gchar *message, gpointer user_data)
285 ms_retm_if(data_channel == NULL, "data_channel is NULL");
286 ms_retm_if(message == NULL, "message is NULL");
288 ms_info("Received message: %s", message);
291 static void __connect_data_channel_signals(GObject *data_channel)
293 ms_retm_if(data_channel == NULL, "data_channel is NULL");
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);
305 void ms_webrtcbin_on_data_channel_cb(GstElement *webrtcbin, GObject *data_channel, gpointer user_data)
307 media_streamer_s *ms_streamer = (media_streamer_s *)user_data;
309 ms_retm_if(ms_streamer == NULL, "ms_streamer is NULL");
310 ms_retm_if(data_channel == NULL, "data_channel is NULL");
314 __connect_data_channel_signals(data_channel);
319 void ms_webrtcbin_pad_added_cb(GstElement *webrtcbin, GstPad *new_pad, gpointer user_data)
321 media_streamer_s *ms_streamer = (media_streamer_s *)user_data;
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");
329 ms_debug("Pad [%s] added on [%s]", GST_PAD_NAME(new_pad), GST_ELEMENT_NAME(webrtcbin));
334 GstElement *ms_webrtc_element_create(void)
336 GstElement *webrtc_container;
337 GstElement *webrtcbin;
338 GstGhostPad *ghost_pad_video_in;
342 webrtc_container = gst_bin_new("webrtc_container");
343 ms_retvm_if(!webrtc_container, (GstElement *) NULL, "Error: creating elements for webrtc container");
345 ms_add_no_target_ghostpad(webrtc_container, MS_RTP_PAD_VIDEO_IN, GST_PAD_SINK);
347 MS_SET_INT_STATIC_STRING_PARAM(webrtc_container, MEDIA_STREAMER_PARAM_WEBRTC_PEER_TYPE, DEFAULT_WEBRTC_PEER);
349 if (!(webrtcbin = ms_element_create("webrtcbin", NULL))) {
350 ms_error("Failed to create webrtcbin element");
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);
358 ms_bin_add_element(webrtc_container, webrtcbin, FALSE);
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");
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 */
373 return webrtc_container;