Add webrtc_set_local[remote]_description() API 09/243109/11
authorSangchul Lee <sc11.lee@samsung.com>
Thu, 3 Sep 2020 06:35:52 +0000 (15:35 +0900)
committerSangchul Lee <sc11.lee@samsung.com>
Fri, 11 Sep 2020 05:05:31 +0000 (14:05 +0900)
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 <sc11.lee@samsung.com>
include/webrtc.h
include/webrtc_private.h
packaging/capi-media-webrtc.spec
src/webrtc.c
src/webrtc_private.c
test/webrtc_test.c

index 4a1de933495cb7d6d2ae15d82bddf7578c4ff449..53e3c3bc9e5ef2fc994ee76fc08b3bcfcace7cb6 100644 (file)
@@ -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);
+
 /**
  * @}
  */
index 47bf9e611116f87aa2eb65ea50dc7882674dde21..4b1c4edb67948f28cf2af0274cff7ba7a9d155c7 100644 (file)
@@ -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
 }
index 0b5a3baa05eaa4a5fa85067ef1066a9206849356..2f7fb69b644cf2a5b1823d90e8f8d4dc3d4a8a4e 100644 (file)
@@ -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
index cca116e1e4027d166d53a7fbb625d19c437dbb47..f5a48c2c429e34c480bf12a59536de41707004a7 100644 (file)
@@ -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
index 13fa87918ad96e47baf4031e55a6c811ae7cf267..0b5e85d3dfff156602f65dd8f8718f5aa3a8a047 100644 (file)
@@ -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;
+}
+
index f7465232e8ddb744d4a88d8945be1900aa6230fb..6dc9dbd618b0f3f4eb8034422e045ecadaf1a51f 100644 (file)
@@ -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();