From 7258b264537408c8595b30198f34c02835cc3c8d Mon Sep 17 00:00:00 2001 From: Sangchul Lee Date: Thu, 3 Sep 2020 15:35:52 +0900 Subject: [PATCH] Add webrtc_set_local[remote]_description() API It corresponds to the setLocal[Remote]Description() method of the RTCPeerConnection respectively. [Version] 0.1.10 [Issue Type] API Change-Id: Ie1e2ddad5d6b3cd5741b034d14d07f13625d492c Signed-off-by: Sangchul Lee --- include/webrtc.h | 38 ++++++++++ include/webrtc_private.h | 1 + packaging/capi-media-webrtc.spec | 2 +- src/webrtc.c | 42 +++++++++++ src/webrtc_private.c | 117 +++++++++++++++++++++++++++++++ test/webrtc_test.c | 111 +++++++++++++++++++++++++---- 6 files changed, 296 insertions(+), 15 deletions(-) diff --git a/include/webrtc.h b/include/webrtc.h index 4a1de933..53e3c3bc 100644 --- a/include/webrtc.h +++ b/include/webrtc.h @@ -261,6 +261,7 @@ int webrtc_unset_negotiation_needed_cb(webrtc_h webrtc); * @retval #WEBRTC_ERROR_INVALID_STATE Invalid state * @pre @a webrtc state must be set to #WEBRTC_STATE_PLAYING. * @see webrtc_negotiation_needed_cb() + * @see webrtc_set_local_description() */ int webrtc_create_offer(webrtc_h webrtc, char **offer); @@ -277,9 +278,46 @@ int webrtc_create_offer(webrtc_h webrtc, char **offer); * @retval #WEBRTC_ERROR_INVALID_OPERATION Invalid operation * @retval #WEBRTC_ERROR_INVALID_STATE Invalid state * @pre @a webrtc state must be set to #WEBRTC_STATE_PLAYING. + * @see webrtc_set_local_description() */ int webrtc_create_answer(webrtc_h webrtc, char **answer); +/** + * @brief Sets the session description for a local peer associated with a WebRTC connection. + * @since_tizen 6.0 + * @remarks @a description is a JSON string.\n + * It will be {"sdp":{"type":"offer or answer","sdp":"..."}}. + * @param [in] webrtc WebRTC handle + * @param [in] description The local session description + * @return @c 0 on success, + * otherwise a negative error value + * @retval #WEBRTC_ERROR_NONE Successful + * @retval #WEBRTC_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #WEBRTC_ERROR_INVALID_OPERATION Invalid operation + * @retval #WEBRTC_ERROR_INVALID_STATE Invalid state + * @pre @a webrtc state must be set to #WEBRTC_STATE_PLAYING. + * @see webrtc_create_offer() + * @see webrtc_create_answer() + */ +int webrtc_set_local_description(webrtc_h webrtc, const char *description); + +/** + * @brief Sets the session description of the remote peer's current offer or answer. + * @since_tizen 6.0 + * @remarks @a description is a JSON string.\n + * It will be {"sdp":{"type":"offer or answer","sdp":"..."}}. + * @param [in] webrtc WebRTC handle + * @param [in] description The remote session description + * @return @c 0 on success, + * otherwise a negative error value + * @retval #WEBRTC_ERROR_NONE Successful + * @retval #WEBRTC_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #WEBRTC_ERROR_INVALID_OPERATION Invalid operation + * @retval #WEBRTC_ERROR_INVALID_STATE Invalid state + * @pre @a webrtc state must be set to #WEBRTC_STATE_PLAYING. + */ +int webrtc_set_remote_description(webrtc_h webrtc, const char *description); + /** * @} */ diff --git a/include/webrtc_private.h b/include/webrtc_private.h index 47bf9e61..4b1c4edb 100644 --- a/include/webrtc_private.h +++ b/include/webrtc_private.h @@ -173,6 +173,7 @@ int _remove_media_source(webrtc_s *webrtc, unsigned int source_id); int _webrtcbin_create_offer(webrtc_s *webrtc, char **offer); int _webrtcbin_create_answer(webrtc_s *webrtc, char **answer); +int _webrtcbin_set_session_description(webrtc_s *webrtc, const char *description, gboolean is_remote); #ifdef __cplusplus } diff --git a/packaging/capi-media-webrtc.spec b/packaging/capi-media-webrtc.spec index 0b5a3baa..2f7fb69b 100644 --- a/packaging/capi-media-webrtc.spec +++ b/packaging/capi-media-webrtc.spec @@ -1,6 +1,6 @@ Name: capi-media-webrtc Summary: A WebRTC library in Tizen Native API -Version: 0.1.9 +Version: 0.1.10 Release: 0 Group: Multimedia/API License: Apache-2.0 diff --git a/src/webrtc.c b/src/webrtc.c index cca116e1..f5a48c2c 100644 --- a/src/webrtc.c +++ b/src/webrtc.c @@ -286,5 +286,47 @@ int webrtc_create_answer(webrtc_h webrtc, char **answer) g_mutex_unlock(&_webrtc->mutex); + return ret; +} + +int webrtc_set_local_description(webrtc_h webrtc, const char *description) +{ + int ret = WEBRTC_ERROR_NONE; + webrtc_s *_webrtc = (webrtc_s*)webrtc; + + RET_VAL_IF(_webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL"); + RET_VAL_IF(description == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "description is NULL"); + + g_mutex_lock(&_webrtc->mutex); + + RET_VAL_WITH_UNLOCK_IF(_webrtc->state != WEBRTC_STATE_PLAYING, WEBRTC_ERROR_INVALID_STATE, &_webrtc->mutex, "the state should be PLAYING"); + + LOG_INFO("description[ %s ]", description); + + ret = _webrtcbin_set_session_description(webrtc, description, FALSE); + + g_mutex_unlock(&_webrtc->mutex); + + return ret; +} + +int webrtc_set_remote_description(webrtc_h webrtc, const char *description) +{ + int ret = WEBRTC_ERROR_NONE; + webrtc_s *_webrtc = (webrtc_s*)webrtc; + + RET_VAL_IF(_webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL"); + RET_VAL_IF(description == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "description is NULL"); + + g_mutex_lock(&_webrtc->mutex); + + RET_VAL_WITH_UNLOCK_IF(_webrtc->state != WEBRTC_STATE_PLAYING, WEBRTC_ERROR_INVALID_STATE, &_webrtc->mutex, "the state should be PLAYING"); + + LOG_INFO("description[ %s ]", description); + + ret = _webrtcbin_set_session_description(webrtc, description, TRUE); + + g_mutex_unlock(&_webrtc->mutex); + return ret; } \ No newline at end of file diff --git a/src/webrtc_private.c b/src/webrtc_private.c index 13fa8791..0b5e85d3 100644 --- a/src/webrtc_private.c +++ b/src/webrtc_private.c @@ -1055,3 +1055,120 @@ int _webrtcbin_create_answer(webrtc_s *webrtc, char **answer) return _create_session_description(webrtc, FALSE, answer); } + +/* Use g_free() to free the sdp and type parameter. */ +static int __get_sdp_from_description(const char *description, gchar **sdp, gchar **type) +{ + int ret = WEBRTC_ERROR_NONE; + JsonNode *root; + JsonObject *object; + JsonObject *child; + JsonParser *parser; + const gchar *member_sdp; + const gchar *member_type; + + RET_VAL_IF(description == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "description is NULL"); + RET_VAL_IF(sdp == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "sdp is NULL"); + RET_VAL_IF(type == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "type is NULL"); + + parser = json_parser_new(); + if (!JSON_IS_PARSER(parser)) + return WEBRTC_ERROR_INVALID_OPERATION; + + if (!json_parser_load_from_data(parser, description, -1, NULL)) { + LOG_ERROR("unknown description: %s", description); + ret = WEBRTC_ERROR_INVALID_PARAMETER; + goto end; + } + + root = json_parser_get_root(parser); + if (!JSON_NODE_HOLDS_OBJECT(root)) { + LOG_ERROR("it does not contain a JsonObject: %s", description); + ret = WEBRTC_ERROR_INVALID_PARAMETER; + goto end; + } + + object = json_node_get_object(root); + if (!json_object_has_member(object, "sdp")) { + LOG_ERROR("it does not contain 'sdp' member: %s", description); + ret = WEBRTC_ERROR_INVALID_PARAMETER; + goto end; + } + + child = json_object_get_object_member(object, "sdp"); + + member_type = json_object_get_string_member(child, "type"); + if (!member_type || !(g_str_equal(member_type, "answer") || g_str_equal(member_type, "offer"))) { + LOG_ERROR("could not find valid type member: %s", description); + ret = WEBRTC_ERROR_INVALID_PARAMETER; + goto end; + } + + member_sdp = json_object_get_string_member(child, "sdp"); + if (!member_sdp) { + LOG_ERROR("could not find sdb member: %s", description); + ret = WEBRTC_ERROR_INVALID_PARAMETER; + goto end; + } + + *type = g_strdup(member_type); + *sdp = g_strdup(member_sdp); + + LOG_DEBUG("type: %s", *type); + LOG_DEBUG("sdp:\n%s", *sdp); +end: + g_object_unref (parser); + return ret; +} + +int _webrtcbin_set_session_description(webrtc_s *webrtc, const char *description, gboolean is_remote) +{ + int ret = WEBRTC_ERROR_NONE; + gchar *sdp; + gchar *type; + GstSDPMessage *gst_sdp; + GstWebRTCSDPType sdp_type; + GstWebRTCSessionDescription *desc; + GstPromise *promise; + + RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL"); + RET_VAL_IF(description == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "description is NULL"); + RET_VAL_IF(webrtc->gst.webrtcbin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "webrtcbin is NULL"); + + ret = __get_sdp_from_description(description, &sdp, &type); + if (ret != WEBRTC_ERROR_NONE) + return ret; + + ret = gst_sdp_message_new(&gst_sdp); + if (ret != GST_SDP_OK) { + LOG_ERROR("failed to gst_sdp_message_new()"); + ret = WEBRTC_ERROR_INVALID_OPERATION; + goto end; + } + + ret = gst_sdp_message_parse_buffer((guint8 *)sdp, strlen(sdp), gst_sdp); + if (ret != GST_SDP_OK) { + LOG_ERROR("failed to gst_sdp_message_parse_buffer()"); + ret = WEBRTC_ERROR_INVALID_OPERATION; + goto end; + } + + sdp_type = g_str_equal(type, "answer") ? GST_WEBRTC_SDP_TYPE_ANSWER : GST_WEBRTC_SDP_TYPE_OFFER; + desc = gst_webrtc_session_description_new(sdp_type, gst_sdp); + + promise = gst_promise_new(); + g_signal_emit_by_name(webrtc->gst.webrtcbin, is_remote? "set-remote-description" : "set-local-description", desc, promise); + gst_promise_interrupt(promise); + gst_promise_unref(promise); + + gst_webrtc_session_description_free(desc); + + LOG_DEBUG("[%s] signal is emitted", is_remote? "set-remote-description" : "set-local-description"); + +end: + g_free(sdp); + g_free(type); + + return ret; +} + diff --git a/test/webrtc_test.c b/test/webrtc_test.c index f7465232..6dc9dbd6 100644 --- a/test/webrtc_test.c +++ b/test/webrtc_test.c @@ -37,6 +37,8 @@ enum { CURRENT_STATUS_ADD_MEDIA_SOURCE, CURRENT_STATUS_REMOVE_MEDIA_SOURCE, CURRENT_STATUS_SET_STUN_SERVER, + CURRENT_STATUS_SET_LOCAL_DESCRIPTION, + CURRENT_STATUS_SET_REMOTE_DESCRIPTION, CURRENT_STATUS_SETTING_SIGNALLING_SERVER, CURRENT_STATUS_SETTING_PROXY, }; @@ -53,6 +55,8 @@ typedef struct { static appdata ad; static webrtc_h g_webrtc; +static char *g_offer; +static char *g_answer; static gchar g_signalling_server[MAX_STRING_LEN]; static gchar g_stun_server[MAX_STRING_LEN]; static gchar g_proxy[MAX_STRING_LEN]; @@ -341,29 +345,55 @@ static void _webrtc_unset_negotiation_needed_cb() static void _webrtc_create_offer() { int ret = WEBRTC_ERROR_NONE; - char *offer = NULL; - ret = webrtc_create_offer(g_webrtc, &offer); - if (ret != WEBRTC_ERROR_NONE) { - g_print("failed to webrtc_create_offer()\n"); - } else { - g_print("webrtc_create_offer() success\noffer:\n%s\n", offer); - free(offer); + if (g_offer) { + free(g_offer); + g_offer = NULL; } + + ret = webrtc_create_offer(g_webrtc, &g_offer); + if (ret != WEBRTC_ERROR_NONE) + g_print("failed to webrtc_create_offer()\n"); + else + g_print("webrtc_create_offer() success\noffer:\n%s\n", g_offer); } static void _webrtc_create_answer() { int ret = WEBRTC_ERROR_NONE; - char *answer = NULL; - ret = webrtc_create_answer(g_webrtc, &answer); - if (ret != WEBRTC_ERROR_NONE) { - g_print("failed to webrtc_create_answer()\n"); - } else { - g_print("webrtc_create_answer() success\nanswer:\n%s\n", answer); - free(answer); + if (g_answer) { + free(g_answer); + g_answer = NULL; } + + ret = webrtc_create_answer(g_webrtc, &g_answer); + if (ret != WEBRTC_ERROR_NONE) + g_print("failed to webrtc_create_answer()\n"); + else + g_print("webrtc_create_answer() success\nanswer:\n%s\n", g_answer); +} + +static void _webrtc_set_local_description(char *desc) +{ + int ret = WEBRTC_ERROR_NONE; + + ret = webrtc_set_local_description(g_webrtc, desc); + if (ret != WEBRTC_ERROR_NONE) + g_print("failed to webrtc_set_local_description()\n"); + else + g_print("webrtc_set_local_description() success\n"); +} + +static void _webrtc_set_remote_description(char *desc) +{ + int ret = WEBRTC_ERROR_NONE; + + ret = webrtc_set_remote_description(g_webrtc, desc); + if (ret != WEBRTC_ERROR_NONE) + g_print("failed to webrtc_set_remote_description()\n"); + else + g_print("webrtc_set_remote_description() success\n"); } static void _setting_signalling_server(char *uri) @@ -451,6 +481,12 @@ void _interpret_main_menu(char *cmd) } else if (strncmp(cmd, "ca", 2) == 0) { _webrtc_create_answer(); + } else if (strncmp(cmd, "sl", 2) == 0) { + g_menu_state = CURRENT_STATUS_SET_LOCAL_DESCRIPTION; + + } else if (strncmp(cmd, "sr", 2) == 0) { + g_menu_state = CURRENT_STATUS_SET_REMOTE_DESCRIPTION; + } else { g_print("unknown menu \n"); } @@ -477,6 +513,8 @@ void display_sub_basic() g_print("un. Unset negotiation needed callback\n"); g_print("co. Create offer\t"); g_print("ca. Create answer\n"); + g_print("sl. Set local description\t"); + g_print("sr. Set remote description\n"); g_print("st. Set STUN server\n"); g_print("----------------------------------- App. Setting ----------------------------------------\n"); g_print("ss. Signalling server\n"); @@ -495,6 +533,10 @@ static void displaymenu() g_print("*** input media source id to remove.\n"); } else if (g_menu_state == CURRENT_STATUS_SET_STUN_SERVER) { g_print("*** input STUN server address.\n"); + } else if (g_menu_state == CURRENT_STATUS_SET_LOCAL_DESCRIPTION) { + g_print("*** input type of local description.(1:offer, 2:answer)\n"); + } else if (g_menu_state == CURRENT_STATUS_SET_REMOTE_DESCRIPTION) { + g_print("*** input type of remote description.(1:offer, 2:answer)\n"); } else if (g_menu_state == CURRENT_STATUS_SETTING_SIGNALLING_SERVER) { g_print("*** input signalling server url.\n"); } else if (g_menu_state == CURRENT_STATUS_SETTING_PROXY) { @@ -548,6 +590,47 @@ static void interpret(char *cmd) reset_menu_state(); break; } + case CURRENT_STATUS_SET_LOCAL_DESCRIPTION: { + value = atoi(cmd); + if (value == 1) { + _webrtc_set_local_description(g_offer); + if (g_offer) { + free(g_offer); + g_offer = NULL; + } + } else if (value == 2) { + _webrtc_set_local_description(g_answer); + if (g_answer) { + free(g_answer); + g_answer = NULL; + } + } else { + g_print("invalid value[%d]\n", value); + } + reset_menu_state(); + break; + } + case CURRENT_STATUS_SET_REMOTE_DESCRIPTION: { + /* FIXME: Setting remote description should be set via signalling server */ + value = atoi(cmd); + if (value == 1) { + _webrtc_set_remote_description(g_offer); + if (g_offer) { + free(g_offer); + g_offer = NULL; + } + } else if (value == 2) { + _webrtc_set_remote_description(g_answer); + if (g_answer) { + free(g_answer); + g_answer = NULL; + } + } else { + g_print("invalid value[%d]\n", value); + } + reset_menu_state(); + break; + } case CURRENT_STATUS_SETTING_SIGNALLING_SERVER: { _setting_signalling_server(cmd); reset_menu_state(); -- 2.34.1