From 97aae22b6bd6a4be487f9d14d4a2a278a18b9028 Mon Sep 17 00:00:00 2001 From: Hyunil Date: Fri, 5 Jun 2020 14:48:15 +0900 Subject: [PATCH] Add new param for setting remote session description and ICE candidiate for WebRTC peer connection - add MEDIA_STREAMER_PARAM_WEBRTC_REMOTE_SESSION_DESCRIPTION - add MEDIA_STREAMER_PARAM_WEBRTC_ADD_ICE_CANDIDATE Add internal APIs to set remote session description and ICE candidate to WebRTC node - int ms_webrtcbin_set_remote_session_description(media_streamer_node_s *node, const char *sdp_msg) - int ms_webrtcbin_add_ice_candidate(media_streamer_node_s *node, const char *ice_msg) [Version] 0.1.54 [Issue Type] New feature Change-Id: I5a1a66a61dcb511cebf62628d3bb8217912bc8cd Signed-off-by: Hyunil --- include/media_streamer.h | 16 +++ include/media_streamer_gst_webrtc.h | 5 + include/media_streamer_node.h | 1 - include/media_streamer_util.h | 4 +- packaging/capi-media-streamer.spec | 2 +- src/media_streamer_gst_webrtc.c | 209 +++++++++++++++++++++++++++--------- src/media_streamer_node.c | 19 +++- src/media_streamer_util.c | 8 +- 8 files changed, 204 insertions(+), 60 deletions(-) diff --git a/include/media_streamer.h b/include/media_streamer.h index f686645..3f11575 100644 --- a/include/media_streamer.h +++ b/include/media_streamer.h @@ -303,6 +303,22 @@ typedef enum { #define MEDIA_STREAMER_PARAM_WEBRTC_PEER_TYPE "webrtc-peer-type" /** + * @brief Definition for remote session description. + * @details Value of the session description of the remote peer over its signaling channel + * Data type is string. + * @since_tizen 6.0 + * @see media_streamer_node_get_params() */ +#define MEDIA_STREAMER_PARAM_WEBRTC_REMOTE_SESSION_DESCRIPTION "webrtc-remote-session-description" + +/** + * @brief Definition for ICE candidate of WebRTC node. + * @details Value of ICE candidate of the remote peer over its signaling channel + * Data type is string. + * @since_tizen 6.0 + * @see media_streamer_node_get_params() */ +#define MEDIA_STREAMER_PARAM_WEBRTC_ADD_ICE_CANDIDATE "webrtc-add-ice-candidate" + +/** * @brief Definition for audio device name parameter of source or sink node. * @details ALSA device, as defined in an asound configuration file. * ex) "hw:0,0", "hw:0,1" diff --git a/include/media_streamer_gst_webrtc.h b/include/media_streamer_gst_webrtc.h index 0b89f50..6f1fe74 100644 --- a/include/media_streamer_gst_webrtc.h +++ b/include/media_streamer_gst_webrtc.h @@ -40,6 +40,11 @@ void ms_webrtcbin_on_negotiation_needed_cb(GstElement *webrtcbin, gpointer user_ void ms_webrtcbin_on_negotiation_process_answer(GstElement *webrtcbin, media_streamer_node_s *webrtc_node); +int ms_webrtcbin_set_remote_session_description(media_streamer_node_s *node, const char *sdp_msg); + +int ms_webrtcbin_add_ice_candidate(media_streamer_node_s *node, const char *ice_msg); + + #ifdef __cplusplus } #endif diff --git a/include/media_streamer_node.h b/include/media_streamer_node.h index 071cae9..b93bfd1 100644 --- a/include/media_streamer_node.h +++ b/include/media_streamer_node.h @@ -123,7 +123,6 @@ gboolean ms_src_node_prepare_iter(const GValue *item, GValue *g_ret, gpointer us gboolean ms_node_resources_acquire_iter(const GValue *item, GValue *ret, gpointer user_data); gboolean ms_node_resources_release_iter(const GValue *item, GValue *ret, gpointer user_data); gboolean ms_node_dpm_policy_check_iter(const GValue *item, GValue *ret, gpointer user_data); - #ifdef __cplusplus } #endif diff --git a/include/media_streamer_util.h b/include/media_streamer_util.h index 910283c..bbfe35d 100644 --- a/include/media_streamer_util.h +++ b/include/media_streamer_util.h @@ -312,8 +312,8 @@ int ms_util_uri_path_check(const char *file_uri); /* Use g_free() to free the return value. */ gchar* ms_get_string_from_json_object(JsonObject *object); -/* Use g_free() to free the sdp parameter. */ -int ms_webrtc_get_sdp_from_message(const char *sdp_msg, gchar **sdp); +/* Use g_free() to free the sdp and type parameter. */ +int ms_webrtc_get_sdp_from_message(const char *sdp_msg, gchar **sdp, gchar **type); /* Use g_free() to free the candidate parameter. */ int ms_webrtc_get_ice_candidate_from_message(const char *ice_msg, gchar **candidate, gint *mlineindex); diff --git a/packaging/capi-media-streamer.spec b/packaging/capi-media-streamer.spec index 40ef7b7..b5fa0e0 100644 --- a/packaging/capi-media-streamer.spec +++ b/packaging/capi-media-streamer.spec @@ -1,6 +1,6 @@ Name: capi-media-streamer Summary: A Media Streamer API -Version: 0.1.53 +Version: 0.1.54 Release: 0 Group: Multimedia/API License: Apache-2.0 diff --git a/src/media_streamer_gst_webrtc.c b/src/media_streamer_gst_webrtc.c index 371ec64..f19b0a8 100644 --- a/src/media_streamer_gst_webrtc.c +++ b/src/media_streamer_gst_webrtc.c @@ -24,6 +24,27 @@ #include "media_streamer_gst_webrtc.h" #include "media_streamer_node.h" +static gchar *__make_ice_candidate_message(guint mlineindex, gchar *candidate) +{ + JsonObject *ice, *msg; + gchar *text; + + ms_retvm_if(candidate == NULL, NULL, "candidate is NULL"); + + ice = json_object_new(); + json_object_set_string_member(ice, "candidate", candidate); + json_object_set_int_member(ice, "sdpMLineIndex", mlineindex); + + msg = json_object_new(); + json_object_set_object_member(msg, "ice", ice); + + text = ms_get_string_from_json_object(msg); + + json_object_unref(msg); + + return text; +} + static gchar* __make_sdp_message(GstWebRTCSessionDescription *desc) { gchar *text; @@ -71,9 +92,25 @@ static void __trigger_message_callback(media_streamer_node_s *webrtc_node, gchar } } -static void __on_answer_created_cb(GstPromise *promise, gpointer user_data) +static void __ms_webrtcbin_set_session_description(GstElement *webrtcbin, GstWebRTCSessionDescription *session_description, gboolean is_remote) { - GstWebRTCSessionDescription *answer = NULL; + GstPromise *promise; + ms_retm_if(session_description == NULL, "session_description is NULL"); + ms_retm_if(webrtcbin == NULL, "webrtcbin is NULL"); + + ms_debug_fenter(); + + promise = gst_promise_new(); + g_signal_emit_by_name(webrtcbin, is_remote? "set-remote-description" : "set-local-description", session_description, promise); + gst_promise_interrupt(promise); + gst_promise_unref(promise); + + ms_debug_fleave(); +} + +static void __on_offer_created_cb(GstPromise *promise, gpointer user_data) +{ + GstWebRTCSessionDescription *offer = NULL; const GstStructure *reply; media_streamer_node_s *webrtc_node = (media_streamer_node_s *)user_data; node_info_s *node_klass_type = NULL; @@ -93,74 +130,161 @@ static void __on_answer_created_cb(GstPromise *promise, gpointer user_data) } reply = gst_promise_get_reply(promise); - gst_structure_get(reply, "answer", - GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &answer, NULL); - gst_promise_unref(promise); - - promise = gst_promise_new(); - g_signal_emit_by_name(G_OBJECT(webrtcbin), "set-local-description", answer, promise); - gst_promise_interrupt(promise); + gst_structure_get(reply, "offer", + GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &offer, NULL); gst_promise_unref(promise); - sdp_msg = __make_sdp_message(answer); + __ms_webrtcbin_set_session_description(webrtcbin, offer, FALSE); - /* TODO: need to add to send this message to signalling server */ - ms_debug("SDP message is sent: %s", sdp_msg); + sdp_msg = __make_sdp_message(offer); + gst_webrtc_session_description_free(offer); + /* Send local description to peer */ + __trigger_message_callback(webrtc_node, sdp_msg); g_free(sdp_msg); - gst_webrtc_session_description_free(answer); - ms_debug_fleave(); } -static void __on_offer_created_cb(GstPromise *promise, gpointer user_data) +static void __on_answer_created_cb(GstPromise * promise, gpointer user_data) { - GstWebRTCSessionDescription *offer = NULL; + GstWebRTCSessionDescription *answer = NULL; const GstStructure *reply; - media_streamer_node_s *webrtc_node = (media_streamer_node_s *)user_data; + media_streamer_node_s *node = (media_streamer_node_s *)user_data; node_info_s *node_klass_type = NULL; - GstElement *webrtcbin = NULL; gchar *sdp_msg; + GstElement *webrtcbin; ms_retm_if(promise == NULL, "promise is NULL"); - ms_retm_if(webrtc_node == NULL, "webrtc_node is NULL"); + ms_retm_if(node == NULL, "node is NULL"); + ms_retm_if(node->gst_element == NULL, "webrtc_container is NULL"); ms_retm_if(gst_promise_wait(promise) != GST_PROMISE_RESULT_REPLIED, "promise is not for replied result"); ms_debug_fenter(); node_klass_type = ms_node_get_klass_by_its_type(MEDIA_STREAMER_NODE_TYPE_WEBRTC); - if (!(webrtcbin = ms_find_element_in_bin_by_type(webrtc_node->gst_element, node_klass_type))) { + if (!(webrtcbin = ms_find_element_in_bin_by_type(node->gst_element, node_klass_type))) { ms_error("Could not find webrtcbin by type[%s, %s]", node_klass_type->klass_name, node_klass_type->default_name); return; } reply = gst_promise_get_reply(promise); - gst_structure_get(reply, "offer", - GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &offer, NULL); - gst_promise_unref(promise); - - promise = gst_promise_new(); - g_signal_emit_by_name(G_OBJECT(webrtcbin), "set-local-description", offer, promise); - gst_promise_interrupt(promise); + gst_structure_get(reply, "answer", GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &answer, NULL); gst_promise_unref(promise); - sdp_msg = __make_sdp_message(offer); + __ms_webrtcbin_set_session_description(webrtcbin, answer, FALSE); - __trigger_message_callback(webrtc_node, sdp_msg); + sdp_msg = __make_sdp_message(answer); + gst_webrtc_session_description_free(answer); + /* Send local description to peer */ + __trigger_message_callback(node, sdp_msg); g_free(sdp_msg); - gst_webrtc_session_description_free(offer); ms_debug_fleave(); } +int ms_webrtcbin_set_remote_session_description(media_streamer_node_s *node, const char *sdp_msg) +{ + GstSDPMessage *gst_sdp; + gchar *sdp; + gchar *type; + GstWebRTCSessionDescription *answer, *offer; + node_info_s *node_klass_type; + GstElement *webrtcbin; + int ret = MEDIA_STREAMER_ERROR_NONE; + + ms_retvm_if(node == NULL || node->gst_element == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "Error: empty webrtcbin"); + ms_retvm_if(sdp_msg == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "sdp_msg is NULL"); + + ms_debug_fenter(); + + + node_klass_type = ms_node_get_klass_by_its_type(MEDIA_STREAMER_NODE_TYPE_WEBRTC); + if (!(webrtcbin = ms_find_element_in_bin_by_type(node->gst_element, node_klass_type))) { + ms_error("Could not find webrtcbin by type[%s, %s]", node_klass_type->klass_name, node_klass_type->default_name); + return MEDIA_STREAMER_ERROR_INVALID_OPERATION; + } + + ret = ms_webrtc_get_sdp_from_message(sdp_msg, &sdp, &type); + if (ret != MEDIA_STREAMER_ERROR_NONE) + goto end; + + ret = gst_sdp_message_new(&gst_sdp); + if (ret != GST_SDP_OK) { + ret = MEDIA_STREAMER_ERROR_INVALID_OPERATION; + goto end; + } + + ret = gst_sdp_message_parse_buffer((guint8 *)sdp, strlen(sdp), gst_sdp); + if (ret != GST_SDP_OK) { + ret = MEDIA_STREAMER_ERROR_INVALID_OPERATION; + goto end; + } + + if (g_str_equal(type, "answer")) { + answer = gst_webrtc_session_description_new(GST_WEBRTC_SDP_TYPE_ANSWER, gst_sdp); + g_assert_nonnull(answer); + + __ms_webrtcbin_set_session_description(webrtcbin, answer, TRUE); + gst_webrtc_session_description_free(answer); + } else if (g_str_equal(type, "offer")) { + offer = gst_webrtc_session_description_new(GST_WEBRTC_SDP_TYPE_OFFER, gst_sdp); + g_assert_nonnull(offer); + + __ms_webrtcbin_set_session_description(webrtcbin, offer, TRUE); + gst_webrtc_session_description_free(offer); + + ms_webrtcbin_on_negotiation_process_answer(webrtcbin, node); + } else { + ms_error("type is %s, it is not a answer or offer", type); + } + +end: + MS_SAFE_GFREE(sdp); + MS_SAFE_GFREE(type); + + ms_debug_fleave(); + + return ret; +} + +int ms_webrtcbin_add_ice_candidate(media_streamer_node_s *node, const char *ice_msg) +{ + gchar *candidate; + gint sdpmlineindex; + node_info_s *node_klass_type; + GstElement *webrtcbin; + int ret; + + ms_retvm_if(node == NULL || node->gst_element == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "Error: empty webrtcbin"); + ms_retvm_if(ice_msg == NULL, MEDIA_STREAMER_ERROR_INVALID_PARAMETER, "ice_msg is NULL"); + + ms_debug_fenter(); + + node_klass_type = ms_node_get_klass_by_its_type(MEDIA_STREAMER_NODE_TYPE_WEBRTC); + if (!(webrtcbin = ms_find_element_in_bin_by_type(node->gst_element, node_klass_type))) { + ms_error("Could not find webrtcbin by type[%s, %s]", node_klass_type->klass_name, node_klass_type->default_name); + return MEDIA_STREAMER_ERROR_INVALID_OPERATION; + } + + ret = ms_webrtc_get_ice_candidate_from_message(ice_msg, &candidate, &sdpmlineindex); + if (ret != MEDIA_STREAMER_ERROR_NONE) + return ret; + + /*Add ice candidate sent by remote peer */ + g_signal_emit_by_name(webrtcbin, "add-ice-candidate", sdpmlineindex, candidate); + + g_free(candidate); + + return MEDIA_STREAMER_ERROR_NONE; +} + void ms_webrtcbin_on_negotiation_process_answer(GstElement *webrtcbin, media_streamer_node_s *webrtc_node) { GstPromise *promise; ms_retm_if(webrtcbin == NULL, "webrtcbin is NULL"); - ms_retm_if(webrtc_node == NULL, "webrtc_node is NULL"); ms_debug_fenter(); @@ -185,27 +309,6 @@ void ms_webrtcbin_on_negotiation_needed_cb(GstElement *webrtcbin, gpointer user_ ms_debug_fleave(); } -static gchar *__make_ice_candidate_message(guint mlineindex, gchar *candidate) -{ - JsonObject *ice, *msg; - gchar *text; - - ms_retvm_if(candidate == NULL, NULL, "candidate is NULL"); - - ice = json_object_new(); - json_object_set_string_member(ice, "candidate", candidate); - json_object_set_int_member(ice, "sdpMLineIndex", mlineindex); - - msg = json_object_new(); - json_object_set_object_member(msg, "ice", ice); - - text = ms_get_string_from_json_object(msg); - - json_object_unref(msg); - - return text; -} - void ms_webrtcbin_on_ice_candidate_cb(GstElement *webrtcbin, guint mlineindex, gchar *candidate, gpointer user_data) { gchar *ice_candidate_msg = NULL; @@ -345,6 +448,8 @@ GstElement *ms_webrtc_element_create(void) ms_add_no_target_ghostpad(webrtc_container, MS_RTP_PAD_VIDEO_IN, GST_PAD_SINK); MS_SET_INT_STATIC_STRING_PARAM(webrtc_container, MEDIA_STREAMER_PARAM_WEBRTC_PEER_TYPE, DEFAULT_WEBRTC_PEER); + MS_SET_INT_STATIC_STRING_PARAM(webrtc_container, MEDIA_STREAMER_PARAM_WEBRTC_REMOTE_SESSION_DESCRIPTION, NULL); + MS_SET_INT_STATIC_STRING_PARAM(webrtc_container, MEDIA_STREAMER_PARAM_WEBRTC_ADD_ICE_CANDIDATE, NULL); if (!(webrtcbin = ms_element_create("webrtcbin", NULL))) { ms_error("Failed to create webrtcbin element"); diff --git a/src/media_streamer_node.c b/src/media_streamer_node.c index 01ced4b..cd1576f 100644 --- a/src/media_streamer_node.c +++ b/src/media_streamer_node.c @@ -51,6 +51,8 @@ static param_s param_table[] = { {MEDIA_STREAMER_PARAM_AUDIO_OUT_PORT, "audio_out_port"}, {MEDIA_STREAMER_PARAM_IP_ADDRESS, "address"}, {MEDIA_STREAMER_PARAM_WEBRTC_PEER_TYPE, "webrtc-peer-type"}, + {MEDIA_STREAMER_PARAM_WEBRTC_REMOTE_SESSION_DESCRIPTION, "webrtc-remote-session-description"}, + {MEDIA_STREAMER_PARAM_WEBRTC_ADD_ICE_CANDIDATE, "webrtc-add-ice-candidate"}, {MEDIA_STREAMER_PARAM_AUDIO_DEVICE, "audio_device"}, {MEDIA_STREAMER_PARAM_CLOCK_SYNCHRONIZED, "sync"}, {MEDIA_STREAMER_PARAM_ROTATE, "rotate"}, @@ -222,6 +224,7 @@ static gboolean __ms_webrtc_node_has_property(media_streamer_node_s *node, const static int __ms_webrtc_node_set_property(media_streamer_node_s *node, param_s *param, const char *param_value) { GValue *val = NULL; + int ret; ms_debug_fenter(); @@ -244,6 +247,21 @@ static int __ms_webrtc_node_set_property(media_streamer_node_s *node, param_s *p g_value_unset(val); g_value_init(val, G_TYPE_STRING); g_value_set_string(val, param_value); + } else if (!strcmp(param->param_name, MEDIA_STREAMER_PARAM_WEBRTC_REMOTE_SESSION_DESCRIPTION)) { + ret = ms_webrtcbin_set_remote_session_description(node, param_value); + if (ret != MEDIA_STREAMER_ERROR_NONE) { + ms_error("failed to set remote session description\n%s", param_value); + return ret; + } + g_value_unset(val); + g_value_init(val, G_TYPE_STRING); + g_value_set_string(val, param_value); + } else if (!strcmp(param->param_name, MEDIA_STREAMER_PARAM_WEBRTC_ADD_ICE_CANDIDATE)) { + ret = ms_webrtcbin_add_ice_candidate(node, param_value); + if (ret != MEDIA_STREAMER_ERROR_NONE) { + ms_error("failed to add ICE candidate\n%s", param_value); + return ret; + } } else { ms_error("failed to set property, undefined param name[%s]", param->param_name); return MEDIA_STREAMER_ERROR_INVALID_PARAMETER; @@ -1555,7 +1573,6 @@ static int __ms_node_set_display(media_streamer_node_s *node, const char *param_ return ret; } //LCOV_EXCL_STOP - int ms_node_set_param_value(media_streamer_node_s *node, param_s *param, const char *param_value) { diff --git a/src/media_streamer_util.c b/src/media_streamer_util.c index 44c71f2..27344c2 100644 --- a/src/media_streamer_util.c +++ b/src/media_streamer_util.c @@ -424,8 +424,8 @@ gchar* ms_get_string_from_json_object(JsonObject *object) return text; } -/* Use g_free() to free the sdp parameter. */ -int ms_webrtc_get_sdp_from_message(const char *sdp_msg, gchar **sdp) +/* Use g_free() to free the sdp and type parameter. */ +int ms_webrtc_get_sdp_from_message(const char *sdp_msg, gchar **sdp, gchar **type) { int ret = MEDIA_STREAMER_ERROR_NONE; JsonNode *root; @@ -470,6 +470,7 @@ int ms_webrtc_get_sdp_from_message(const char *sdp_msg, gchar **sdp) ret = MEDIA_STREAMER_ERROR_INVALID_PARAMETER; goto end; } + *type = g_strdup(member_type); member_sdp = json_object_get_string_member(child, "sdp"); if (!member_sdp) { @@ -480,7 +481,8 @@ int ms_webrtc_get_sdp_from_message(const char *sdp_msg, gchar **sdp) *sdp = g_strdup(member_sdp); - ms_debug("sdp: %s", *sdp); + ms_info("type: %s", *type); + ms_info("sdp:\n%s", *sdp); end: g_object_unref (parser); return ret; -- 2.7.4