webrtc_test: Add webrtc_test_signaling.c and move related codes to it 71/278871/5 accepted/tizen/unified/20220729.131707 submit/tizen/20220729.014452
authorSangchul Lee <sc11.lee@samsung.com>
Wed, 27 Jul 2022 08:02:44 +0000 (17:02 +0900)
committerSangchul Lee <sc11.lee@samsung.com>
Thu, 28 Jul 2022 04:01:18 +0000 (13:01 +0900)
[Version] 0.3.180
[Issue Type] Refactoring

Change-Id: I4b75e65616d28a2bda6da4bc95f9c45160ff5ac5
Signed-off-by: Sangchul Lee <sc11.lee@samsung.com>
packaging/capi-media-webrtc.spec
test/webrtc_test.c
test/webrtc_test_priv.h
test/webrtc_test_signaling.c [new file with mode: 0644]

index 0b125f804428bfef36676774850c43e0b1bb7ace..6a72dfa6576809ef056589a0ebac02908ea937ee 100644 (file)
@@ -1,6 +1,6 @@
 Name:       capi-media-webrtc
 Summary:    A WebRTC library in Tizen Native API
-Version:    0.3.179
+Version:    0.3.180
 Release:    0
 Group:      Multimedia/API
 License:    Apache-2.0
index 1b475eadab1fdf7bda03c1f93e07e619c0c86e9a..fb138960fe04c00b4a1838820cde5dccde9b5671 100644 (file)
@@ -17,7 +17,6 @@
 #include "webrtc_test_priv.h"
 
 #include <tbm_surface_internal.h>
-#include <json-glib/json-glib.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>
@@ -162,7 +161,7 @@ static void __release_packet_source(int conn_idx, unsigned int source_idx)
        g_cond_clear(&packet_source->cond);
 }
 
-static void _webrtc_create(int index)
+void _webrtc_create(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
 
@@ -175,7 +174,7 @@ static void _webrtc_create(int index)
        g_print("webrtc[%p, index:%d] is created\n", g_ad.conns[index].webrtc, index);
 }
 
-static void _webrtc_destroy(int index)
+void _webrtc_destroy(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
        int i;
@@ -236,7 +235,7 @@ static void _webrtc_destroy(int index)
 #endif
 }
 
-static void _webrtc_start(int index)
+void _webrtc_start(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
 
@@ -251,7 +250,7 @@ static void _webrtc_start(int index)
 #endif
 }
 
-static void _webrtc_stop(int index)
+void _webrtc_stop(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
        int i;
@@ -452,7 +451,7 @@ error:
        return -1;
 }
 
-static void _webrtc_add_media_source(int index, int type, unsigned int *source_id)
+void _webrtc_add_media_source(int index, int type, unsigned int *source_id)
 {
        int ret = WEBRTC_ERROR_NONE;
        unsigned int _source_id = 0;
@@ -492,7 +491,7 @@ static void _webrtc_add_media_source(int index, int type, unsigned int *source_i
                *source_id = _source_id;
 }
 
-static void _webrtc_add_mic_source(int index, bool aec, unsigned int *source_id)
+void _webrtc_add_mic_source(int index, bool aec, unsigned int *source_id)
 {
        int ret = WEBRTC_ERROR_NONE;
        unsigned int _source_id = 0;
@@ -783,7 +782,7 @@ static void _webrtc_media_source_set_transceiver_codec(int index, unsigned int s
                source_id, g_webrtc_media_type_str[media_type], g_webrtc_transceiver_codec_str[codec]);
 }
 
-static void _webrtc_set_display_type(int index, int type)
+void _webrtc_set_display_type(int index, int type)
 {
        g_ad.conns[index].render.display_type = type;
 
@@ -858,7 +857,7 @@ static void _webrtc_media_source_unset_audio_loopback(int index, unsigned int so
        g_print("webrtc_media_source_unset_audio_loopback() success, source_id[%u]\n", source_id);
 }
 
-static void _webrtc_media_source_set_video_loopback(int index, unsigned int source_id)
+void _webrtc_media_source_set_video_loopback(int index, unsigned int source_id)
 {
        int ret = WEBRTC_ERROR_NONE;
        unsigned int track_id;
@@ -880,25 +879,6 @@ static void _webrtc_media_source_unset_video_loopback(int index, unsigned int so
        g_print("webrtc_media_source_unset_video_loopback() success, source_id[%u]\n", source_id);
 }
 
-static int __copy_string_arr(gchar *dest_arr, char *string)
-{
-       int len = 0;
-       gsize src_size = 0;
-
-       if (!string)
-               return -1;
-
-       len = strlen(string);
-
-       src_size = g_strlcpy(dest_arr, string, MAX_STRING_LEN);
-       if ((int)src_size != len) {
-               g_print("failed to g_strlcpy()\n");
-               return -1;
-       }
-
-       return 0;
-}
-
 static void _webrtc_data_channel_send_string(const char *string, bool send_as_bytes)
 {
        int ret;
@@ -1119,7 +1099,7 @@ static void _webrtc_set_local_description(connection_s *conn, bool is_offer)
        g_print("webrtc_set_local_description() success\n");
 }
 
-static void _webrtc_set_remote_description(connection_s *conn)
+void _webrtc_set_remote_description(connection_s *conn)
 {
        int ret = WEBRTC_ERROR_NONE;
 
@@ -1144,7 +1124,7 @@ static void __foreach_ice_candidate(gpointer data, gpointer user_data)
        g_print("webrtc_ice_candidate() success\n");
 }
 
-static void _webrtc_add_ice_candidate(connection_s *conn, const gchar *candidate)
+void _webrtc_add_ice_candidate(connection_s *conn, const gchar *candidate)
 {
        RET_IF(!conn, "conn is NULL");
 
@@ -1648,19 +1628,6 @@ static void __error_cb(webrtc_h webrtc, webrtc_error_e error, webrtc_state_e sta
                webrtc, error, state, user_data);
 }
 
-static void _websocket_connection_send_text_for_room(connection_s *conn, int remote_peer_id, const char *message)
-{
-       gchar *message_for_room;
-
-       RET_IF(!conn, "conn is NULL");
-       RET_IF(!conn->is_for_room, "conn[%p] is not for room", conn);
-       RET_IF(!message, "message is NULL");
-
-       message_for_room = g_strdup_printf ("ROOM_PEER_MSG %d %s", remote_peer_id, message);
-       soup_websocket_connection_send_text(g_ad.signaling_server.public.ws_conn, message_for_room);
-       g_free(message_for_room);
-}
-
 static void __state_changed_cb(webrtc_h webrtc, webrtc_state_e previous, webrtc_state_e current, void *user_data)
 {
        connection_s *conn = (connection_s *)user_data;
@@ -1677,7 +1644,7 @@ static void __state_changed_cb(webrtc_h webrtc, webrtc_state_e previous, webrtc_
                if (conn->is_offer) {
                        _webrtc_create_offer(conn, false);
                        _webrtc_set_local_description(conn, true);
-                       _websocket_connection_send_text_for_room(conn, conn->remote_peer_id, conn->offer);
+                       _websocket_send_text_for_room(conn, conn->remote_peer_id, conn->offer);
                } else {
                        if (conn->remote_desc)
                                _webrtc_set_remote_description(conn);
@@ -1692,104 +1659,6 @@ static void __negotiation_needed_cb(webrtc_h webrtc, void *user_data)
        g_print("__negotiation_needed_cb() is invoked, webrtc[%p], user_data[%p]\n", webrtc, user_data);
 }
 
-static void __signaling_message_cb(webrtc_signaling_message_type_e type, const char *message, void *user_data)
-{
-       connection_s *conn = (connection_s *)user_data;
-
-       g_print("__signaling_message_cb(), type[%d] message[%s] conn[%p]\n", type, message, conn);
-
-       if (type == SIGNALING_MESSAGE_TYPE_CONNECTED) {
-               g_print("\n[from SERVER > CONNECTED %s]\n", message);
-               g_ad.signaling_server.is_connected = true;
-               g_ad.signaling_server.server_status = SERVER_STATUS_CONNECTED;
-               g_ad.signaling_server.local_peer_id = atoi(message);
-
-       } else if (type == SIGNALING_MESSAGE_TYPE_DISCONNECTED) {
-               g_print("\n[from SERVER > DISCONNECTED %s]\n", message);
-               g_ad.signaling_server.is_connected = false;
-               g_ad.signaling_server.server_status = SERVER_STATUS_DISCONNECTED;
-
-       } else if (type == SIGNALING_MESSAGE_TYPE_SESSION_ESTABLISHED) {
-               g_print("\n[from SERVER > SESSION_ESTABLISHED with %s]\n", message);
-               g_ad.signaling_server.server_status = SERVER_STATUS_SESSION_ESTABLISHED;
-
-       } else if (type == SIGNALING_MESSAGE_TYPE_SESSION_CLOSED) {
-               g_print("\n[from SERVER > SESSION_CLOSED with %s]\n", message);
-               g_ad.signaling_server.server_status = SERVER_STATUS_SESSION_CLOSED;
-
-       } else if (type == SIGNALING_MESSAGE_TYPE_SDP) {
-               g_print("\n[from SERVER > SDP]\n%s\n", message);
-               if (conn->remote_desc)
-                       free(conn->remote_desc);
-               conn->remote_desc = strdup(message);
-
-       } else if (type == SIGNALING_MESSAGE_TYPE_ICE_CANDIDATE) {
-               g_print("\n[from SERVER > ICE]\n%s\n", message);
-               conn->ice_candidates = g_list_append(conn->ice_candidates, strdup(message));
-       }
-}
-
-static int _webrtc_signaling_connect(const char *ip, int port)
-{
-       int ret;
-
-       if (strlen(g_ad.signaling_server.public.url) > 0) {
-               g_printerr("server[%s] is already set by 'ss'\n", g_ad.signaling_server.public.url);
-               return -1;
-       }
-
-       ret = webrtc_signaling_connect(ip, port, __signaling_message_cb, &g_ad.conns[0], &g_ad.signaling_server.private.client);
-       RET_VAL_IF(ret != WEBRTC_ERROR_NONE, -1, "failed to webrtc_signaling_connect(), ret[0x%x]", ret);
-
-       if (g_ad.signaling_server.private.ip)
-               free(g_ad.signaling_server.private.ip);
-       g_ad.signaling_server.private.ip = strdup(ip);
-       g_ad.signaling_server.private.port = port;
-
-       g_print("webrtc_signaling_connect() success\n");
-
-       return 0;
-}
-
-static void _webrtc_signaling_disconnect(void)
-{
-       int ret;
-
-       RET_IF(!g_ad.signaling_server.is_connected, "signaling server is not connected");
-
-       ret = webrtc_signaling_disconnect(g_ad.signaling_server.private.client);
-       RET_IF(ret != WEBRTC_ERROR_NONE, "ret[0x%x]", ret);
-
-       g_print("webrtc_signaling_disconnect() success\n");
-
-       g_ad.signaling_server.private.client = NULL;
-}
-
-static void _webrtc_signaling_request_session(int peer_id)
-{
-       int ret;
-
-       RET_IF(!g_ad.signaling_server.is_connected, "signaling server is not connected");
-
-       ret = webrtc_signaling_request_session(g_ad.signaling_server.private.client, peer_id);
-       RET_IF(ret != WEBRTC_ERROR_NONE, "ret[0x%x]", ret);
-
-       g_print("webrtc_signaling_request_session() success\n");
-}
-
-static void _webrtc_signaling_send_message(const char *message)
-{
-       int ret;
-
-       RET_IF(!message, "message is NULL");
-       RET_IF(!g_ad.signaling_server.is_connected, "signaling server is not connected");
-
-       ret = webrtc_signaling_send_message(g_ad.signaling_server.private.client, message);
-       RET_IF(ret != WEBRTC_ERROR_NONE, "ret[0x%x]", ret);
-
-       g_print("webrtc_signaling_send_message() success\n");
-}
-
 static void __ice_candidate_cb(webrtc_h webrtc, const char *candidate, void *user_data)
 {
        connection_s *conn = (connection_s *)user_data;
@@ -1799,17 +1668,7 @@ static void __ice_candidate_cb(webrtc_h webrtc, const char *candidate, void *use
 
        g_print("\n[to SERVER > ICE]\n%s\n", candidate);
 
-       RET_IF(!g_ad.signaling_server.is_connected, "signaling server is not connected");
-
-       if (g_ad.signaling_server.public.ws_conn) {
-               if (conn->is_for_room)
-                       _websocket_connection_send_text_for_room(conn, conn->remote_peer_id, candidate);
-               else
-                       soup_websocket_connection_send_text(g_ad.signaling_server.public.ws_conn, candidate);
-
-       } else if (g_ad.signaling_server.private.client) {
-               _webrtc_signaling_send_message(candidate);
-       }
+       _send_ice_candidate(conn, candidate);
 }
 
 static void __peer_connection_state_change_cb(webrtc_h webrtc, webrtc_peer_connection_state_e state, void *user_data)
@@ -1833,7 +1692,7 @@ static void __signaling_state_change_cb(webrtc_h webrtc, webrtc_signaling_state_
        if (conn->is_for_room && state == WEBRTC_SIGNALING_STATE_HAVE_REMOTE_OFFER) {
                _webrtc_create_answer(conn, false);
                _webrtc_set_local_description(conn, false);
-               _websocket_connection_send_text_for_room(conn, conn->remote_peer_id, conn->answer);
+               _websocket_send_text_for_room(conn, conn->remote_peer_id, conn->answer);
        }
 }
 
@@ -1855,7 +1714,7 @@ static void __ice_connection_state_change_cb(webrtc_h webrtc, webrtc_ice_connect
        g_print("__ice_connection_state_change_cb() is invoked, state[%u, %u]\n", state, _state);
 }
 
-static void _webrtc_set_all_negotiation_state_change_cbs(int index)
+void _webrtc_set_all_negotiation_state_change_cbs(int index)
 {
        int ret = webrtc_set_peer_connection_state_change_cb(g_ad.conns[index].webrtc, __peer_connection_state_change_cb, &g_ad.conns[index]);
        if (ret != WEBRTC_ERROR_NONE)
@@ -2006,7 +1865,7 @@ static void __track_added_cb(webrtc_h webrtc, webrtc_media_type_e type, unsigned
        }
 }
 
-static void _webrtc_set_all_basic_cbs(int index)
+void _webrtc_set_all_basic_cbs(int index)
 {
        /* COMMON */
        int ret = webrtc_set_error_cb(g_ad.conns[index].webrtc, __error_cb, g_ad.conns[index].webrtc);
@@ -2267,7 +2126,7 @@ static void _webrtc_unset_all_cbs(int index)
        }
 }
 
-static void _webrtc_create_data_channel(int index)
+void _webrtc_create_data_channel(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
        gchar *label;
@@ -2292,7 +2151,7 @@ static void _webrtc_create_data_channel(int index)
        g_free(label);
 }
 
-static void _webrtc_destroy_data_channel(int index)
+void _webrtc_destroy_data_channel(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
        int i;
@@ -2334,95 +2193,6 @@ static void _webrtc_data_channel_get_label(int index)
        }
 }
 
-static int _setting_uri(gchar *dest_arr, char *uri)
-{
-       int ret = 0;
-
-       if (!uri)
-               return -1;
-
-       if (g_ad.signaling_server.private.client) {
-               g_printerr("already set by 'scc'\n");
-               return -1;
-       }
-
-       if (strlen(uri) > strlen("0.0.0.0")) {
-               ret = __copy_string_arr(dest_arr, uri);
-               if (ret != 0) {
-                       g_print("failed to __copy_string_arr()\n");
-                       return -1;
-               }
-       } else {
-               g_print("invalid value, uri[%s]\n", uri);
-               return -1;
-       }
-
-       return 0;
-}
-
-static void _request_session(int remote_peer_id)
-{
-       RET_IF(!g_ad.signaling_server.is_connected, "signaling server is not connected");
-
-       if (g_ad.signaling_server.public.ws_conn) {
-               gchar *msg;
-               if (soup_websocket_connection_get_state(g_ad.signaling_server.public.ws_conn) != SOUP_WEBSOCKET_STATE_OPEN) {
-                       g_printerr("websocket is not opened\n");
-                       return;
-               }
-
-               msg = g_strdup_printf("SESSION %d", remote_peer_id);
-
-               g_print("\n[to SERVER > %s]\n", msg);
-               soup_websocket_connection_send_text(g_ad.signaling_server.public.ws_conn, msg);
-               g_free(msg);
-
-       } else if (g_ad.signaling_server.private.client) {
-               _webrtc_signaling_request_session(remote_peer_id);
-
-       } else {
-               g_printerr("Neither public nor private signaling server is connected\n");
-       }
-}
-
-static void _request_join_room(char *room_name)
-{
-       gchar *msg;
-
-       RET_IF(!room_name, "room_name is NULL");
-       RET_IF(!g_ad.signaling_server.is_connected, "signaling server is not connected");
-       RET_IF(!g_ad.signaling_server.public.ws_conn, "ws_conn is NULL");
-
-       if (soup_websocket_connection_get_state(g_ad.signaling_server.public.ws_conn) != SOUP_WEBSOCKET_STATE_OPEN) {
-               g_printerr("websocket is not opened\n");
-               return;
-       }
-
-       msg = g_strdup_printf("ROOM %s", room_name);
-
-       g_print("\n[to SERVER > %s]\n", msg);
-       soup_websocket_connection_send_text(g_ad.signaling_server.public.ws_conn, msg);
-       g_free(msg);
-}
-
-static void _send_local_description(bool is_offer)
-{
-       char *desc;
-
-       RET_IF(!g_ad.signaling_server.is_connected, "signaling server is not connected");
-
-       desc = is_offer ? g_ad.conns[0].offer : g_ad.conns[0].answer;
-
-       g_print("\n[to SERVER > local description]\n%s\n", desc);
-
-       if (g_ad.signaling_server.public.ws_conn)
-               soup_websocket_connection_send_text(g_ad.signaling_server.public.ws_conn, desc);
-       else if (g_ad.signaling_server.private.client)
-               _webrtc_signaling_send_message(desc);
-       else
-               g_printerr("Neither public nor private signaling server is connected\n");
-}
-
 gulong _connect_signal(GObject *obj, const char *signal_name, GCallback callback, gpointer user_data)
 {
        gulong signal_id;
@@ -3135,427 +2905,6 @@ static void _webrtc_file_source_get_looping(int index, unsigned int source_id)
        g_print("webrtc_file_source_get_looping() success, source_id[%u] looping_state[%u]\n", source_id, looping_state);
 }
 
-static void __close_websocket(signaling_server_s *ss)
-{
-       RET_IF(!ss, "ss is NULL");
-
-       g_print("close websocket, ws_conn[%p]\n", ss->public.ws_conn);
-
-       if (soup_websocket_connection_get_state(ss->public.ws_conn) == SOUP_WEBSOCKET_STATE_OPEN)
-               soup_websocket_connection_close(ss->public.ws_conn, 1000, "");
-       else
-               g_object_unref(ss->public.ws_conn);
-
-       ss->public.ws_conn = NULL;
-       ss->server_status = SERVER_STATUS_DISCONNECTED;
-       ss->is_connected = false;
-}
-
-static void __websocket_closed_cb(SoupWebsocketConnection *ws_conn, gpointer user_data)
-{
-       signaling_server_s *ss = (signaling_server_s *)user_data;
-
-       RET_IF(!ss, "ss is NULL");
-       RET_IF(!ws_conn, "ws_conn is NULL");
-       RET_IF(ss->public.ws_conn != ws_conn, "not matched ws_conn[%p, %p]", ss->public.ws_conn, ws_conn);
-
-       __close_websocket(ss);
-}
-
-static void __auto_configure_add_peer(gchar *peer_id, bool is_offer)
-{
-       int i;
-
-       RET_IF(!peer_id, "peer_id is NULL");
-
-       if (!strcmp(peer_id,""))
-               return;
-
-       g_print("add peer[%s] to make an %s\n", peer_id, is_offer ? "offer" : "answer");
-
-       for (i = 0; i < MAX_CONNECTION_LEN; i++) {
-               unsigned int source_id = 0;
-
-               if (g_ad.conns[i].webrtc)
-                       continue;
-
-               g_ad.conns[i].remote_peer_id = atoi((const char *)peer_id);
-               g_ad.conns[i].is_for_room = true;
-               g_ad.conns[i].is_offer = is_offer;
-               if (i > 0) /* follow the source type of the first one */
-                       g_ad.conns[i].room_source_type = g_ad.conns[0].room_source_type;
-
-               _webrtc_create(i);
-               _webrtc_set_all_basic_cbs(i);
-               _webrtc_set_all_negotiation_state_change_cbs(i);
-               _webrtc_create_data_channel(i);
-
-               switch (g_ad.conns[i].room_source_type) {
-               case 1:
-                       _webrtc_add_media_source(i, WEBRTC_MEDIA_SOURCE_TYPE_AUDIOTEST, NULL);
-                       _webrtc_add_media_source(i, WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST, &source_id);
-                       if (i == 0) /* only set loopback video of the first one */
-                               _webrtc_media_source_set_video_loopback(i, source_id);
-                       break;
-               case 2:
-                       _webrtc_add_mic_source(i, false, NULL);
-                       _webrtc_add_media_source(i, WEBRTC_MEDIA_SOURCE_TYPE_CAMERA, &source_id);
-                       if (i == 0) /* only set loopback video of the first one */
-                               _webrtc_media_source_set_video_loopback(i, source_id);
-                       break;
-               case 3:
-                       _webrtc_add_mic_source(i, false, NULL);
-                       break;
-               default:
-                       return;
-               }
-
-               _webrtc_start(i);
-               _webrtc_set_display_type(i, WEBRTC_DISPLAY_TYPE_EVAS);
-
-               return;
-       }
-       g_printerr("failed to __auto_configure_add_peer_to_offer()\n");
-}
-
-static connection_s *__get_conn_from_peer_id(gchar *peer_id, int *index)
-{
-       int i;
-
-       if (!peer_id) {
-               g_printerr("peer_id is NULL\n");
-               return NULL;
-       }
-
-       for (i = 0; i < MAX_CONNECTION_LEN; i++) {
-               if (g_ad.conns[i].remote_peer_id <= 0)
-                       continue;
-               if (g_ad.conns[i].remote_peer_id == atoi((const char *)peer_id)) {
-                       if (index)
-                               *index = i;
-                       return &g_ad.conns[i];
-               }
-       }
-
-       g_printerr("not found matching conn from peer_id[%s]", peer_id);
-       return NULL;
-}
-
-static void __auto_configure_release_peer(gchar *peer_id)
-{
-       connection_s *conn;
-       int i;
-
-       RET_IF(!peer_id, "peer_id is NULL");
-
-       g_print("release peer[%s] related resources\n", peer_id);
-
-       if (!(conn = __get_conn_from_peer_id(peer_id, &i)))
-               return;
-
-       _webrtc_stop(i);
-       _webrtc_destroy_data_channel(i);
-       _webrtc_destroy(i);
-}
-
-static void __handle_json_structured_message(connection_s *conn, const gchar *text)
-{
-       JsonNode *root;
-       JsonObject *object;
-       JsonParser *parser;
-
-       RET_IF(!conn, "conn is NULL");
-       RET_IF(!text, "text is NULL");
-
-       parser = json_parser_new();
-
-       if (!json_parser_load_from_data(parser, text, -1, NULL)) {
-               g_printerr("unknown message [%s]\n", text);
-               g_object_unref(parser);
-               return;
-       }
-
-       root = json_parser_get_root(parser);
-       if (!JSON_NODE_HOLDS_OBJECT(root)) {
-               g_printerr("unknown JSON message [%s]\n", text);
-               g_object_unref(parser);
-               return;
-       }
-
-       object = json_node_get_object(root);
-       if (json_object_has_member(object, "sdp")){
-               g_print("\n[from SERVER > SDP]\n%s\n", text);
-               if (conn->remote_desc)
-                       free(conn->remote_desc);
-               conn->remote_desc = strdup(text);
-
-               if (conn->is_for_room) {
-                       webrtc_state_e state = WEBRTC_STATE_IDLE;
-                       webrtc_get_state(conn->webrtc, &state);
-                       if (state == WEBRTC_STATE_NEGOTIATING)
-                               _webrtc_set_remote_description(conn);
-               }
-
-       } else if (json_object_has_member(object, "ice")){
-               g_print("\n[from SERVER > ICE]\n%s\n", text);
-               if (conn->is_for_room) {
-                       webrtc_state_e state = WEBRTC_STATE_IDLE;
-                       webrtc_get_state(conn->webrtc, &state);
-                       if (state == WEBRTC_STATE_NEGOTIATING)
-                               _webrtc_add_ice_candidate(conn, text);
-                       else
-                               conn->ice_candidates = g_list_append(conn->ice_candidates, strdup(text));
-               } else {
-                       conn->ice_candidates = g_list_append(conn->ice_candidates, strdup(text));
-               }
-
-       } else {
-               g_printerr("neither 'sdp' nor 'ice' member exist in JSON message [%s]\n", text);
-       }
-
-       g_object_unref(parser);
-}
-
-static void __auto_configure_handle_room_message(gchar *peer_id, gchar *message)
-{
-       connection_s *conn;
-
-       RET_IF(!peer_id, "peer_id is NULL");
-       RET_IF(!message, "message is NULL");
-
-       g_print("peer[%s], message[%s]\n", peer_id, message);
-
-       if (!(conn = __get_conn_from_peer_id(peer_id, NULL)))
-               return;
-
-       __handle_json_structured_message(conn, message);
-}
-
-static void __handle_room_related_message(const gchar *text)
-{
-       g_auto(GStrv) tokens = NULL;
-       guint len;
-       guint i;
-
-       RET_IF(!text, "text is NULL");
-
-       if (g_str_has_prefix(text, "ROOM_OK")) {
-               g_print("\n[from SERVER > %s]\n", text);
-               g_ad.signaling_server.server_status = SERVER_STATUS_ROOM_ESTABLISHED;
-
-               /* parse text, get the previous peers in the room and negotiate to each other */
-               if (strlen(text) > strlen("ROOM_OK")) {
-                       /* e.g. ROOM_OK 1234 5311 324 */
-                       tokens = g_strsplit(text, " ", 0);
-                       len = g_strv_length(tokens);
-                       for (i = 1; i < len; i++) {
-                               if (i > MAX_CONNECTION_LEN) {
-                                       g_print("connection[%d], out of range\n", i);
-                                       break;
-                               }
-                               __auto_configure_add_peer(tokens[i], true);
-                       }
-               }
-
-       } else if (g_str_has_prefix(text, "ROOM_PEER_JOINED")) {
-               g_print("\n[from SERVER > %s]\n", text);
-               /* e.g. ROOM_PEER_JOINED 1234 */
-               tokens = g_strsplit(text, " ", 2);
-               __auto_configure_add_peer(tokens[1], false);
-
-       } else if (g_str_has_prefix(text, "ROOM_PEER_LEFT")) {
-               g_print("\n[from SERVER > %s]\n", text);
-               /* e.g. ROOM_PEER_LEFT 1234 */
-               tokens = g_strsplit(text, " ", 2);
-               __auto_configure_release_peer(tokens[1]);
-
-       } else if (g_str_has_prefix(text, "ROOM_PEER_MSG")) {
-               g_print("\n[from SERVER > %s]\n", text);
-               /* e.g. ROOM_PEER_MSG 1234 msg */
-               tokens = g_strsplit(text, " ", 3);
-               __auto_configure_handle_room_message(tokens[1], tokens[2]);
-
-       } else {
-               g_print("\n[from SERVER > %s]\n", text);
-               g_ad.signaling_server.server_status = SERVER_STATUS_ERROR_FOUND;
-       }
-}
-
-static void __websocket_message_cb(SoupWebsocketConnection *ws_conn, SoupWebsocketDataType type, GBytes *message, gpointer user_data)
-{
-       gchar *text;
-       gsize size;
-       const gchar *data;
-       connection_s *conn = (connection_s *)user_data;
-
-       RET_IF(!conn, "conn is NULL");
-       RET_IF(!ws_conn, "ws_conn is NULL");
-       RET_IF(g_ad.signaling_server.public.ws_conn != ws_conn, "not matched ws_conn[%p, %p]", g_ad.signaling_server.public.ws_conn, ws_conn);
-       RET_IF(type != SOUP_WEBSOCKET_DATA_TEXT, "invalid data type(%d)", type);
-
-       data = g_bytes_get_data(message, &size);
-       text = g_strndup(data, size);
-
-       /* NOTE: Logics below can be different in each server */
-       if (g_strcmp0(text, "HELLO") == 0) {
-               g_print("\n[from SERVER > %s] registered done, local_peer_id[%d]\n", text, g_ad.signaling_server.local_peer_id);
-
-       } else if (g_strcmp0(text, "SESSION_OK") == 0) {
-               g_print("\n[from SERVER > %s]\n", text);
-               g_ad.signaling_server.server_status = SERVER_STATUS_SESSION_ESTABLISHED;
-
-       } else if (g_str_has_prefix(text, "ROOM_")) {
-               __handle_room_related_message(text);
-
-       } else if (g_str_has_prefix(text, "ERROR")) {
-               g_print("\n[from SERVER > %s]\n", text);
-               g_ad.signaling_server.server_status = SERVER_STATUS_ERROR_FOUND;
-
-       } else {
-               __handle_json_structured_message(conn, text);
-       }
-
-       g_free(text);
-}
-
-static gint32 __send_greeting_to_register(SoupWebsocketConnection *conn)
-{
-       gchar *hello;
-       gint32 id;
-
-       if (!conn) {
-               g_printerr("conn is NULL\n");
-               return -1;
-       }
-
-       if (soup_websocket_connection_get_state(conn) != SOUP_WEBSOCKET_STATE_OPEN) {
-               g_printerr("websocket is not opened\n");
-               return -1;
-       }
-
-       id = g_random_int_range(10, 10000);
-       hello = g_strdup_printf("HELLO %i", id);
-
-       soup_websocket_connection_send_text(conn, hello);
-
-       g_free(hello);
-
-       return id;
-}
-
-static void __websocket_connected_cb(SoupSession *session, GAsyncResult *res, void *user_data)
-{
-       GError *error = NULL;
-       SoupWebsocketConnection *ws_conn;
-       connection_s *conn = (connection_s *)user_data;
-
-       RET_IF(!conn, "conn is NULL");
-
-       ws_conn = soup_session_websocket_connect_finish(session, res, &error);
-       if (error) {
-               if (soup_websocket_connection_get_state(ws_conn) == SOUP_WEBSOCKET_STATE_OPEN)
-                       soup_websocket_connection_close(ws_conn, 1000, "");
-               else {
-                       g_print("NOT OPENED\n");
-                       g_object_unref(ws_conn);
-               }
-               g_print("failed to soup_session_websocket_connect_finish(), error[%s]\n", error->message);
-               g_error_free(error);
-               return;
-       }
-       g_print("\n[%s] is connected, conn[%p]\n", g_ad.signaling_server.public.url, conn);
-
-       g_ad.signaling_server.public.ws_conn = ws_conn;
-       g_ad.signaling_server.server_status = SERVER_STATUS_CONNECTED;
-
-       g_signal_connect(ws_conn, "closed", G_CALLBACK(__websocket_closed_cb), &g_ad.signaling_server);
-       g_signal_connect(ws_conn, "message", G_CALLBACK(__websocket_message_cb), conn);
-
-       g_ad.signaling_server.local_peer_id = __send_greeting_to_register(ws_conn);
-       g_ad.signaling_server.is_connected = true;
-}
-
-static int _connect_signaling_server(void)
-{
-       SoupMessage *message;
-       SoupSession *session;
-       SoupURI *proxy_uri;
-       const char *https_aliases[] = {"wss", NULL};
-
-       RET_VAL_IF(g_ad.signaling_server.private.client, -1, "already set by 'scc'");
-
-       if (strlen(g_ad.proxy) == 0) {
-               session = soup_session_new_with_options(SOUP_SESSION_SSL_STRICT, TRUE,
-                       SOUP_SESSION_HTTPS_ALIASES, https_aliases, NULL);
-       } else {
-               g_print("use proxy [%s]\n", g_ad.proxy);
-               proxy_uri = soup_uri_new(g_ad.proxy);
-               session = soup_session_new_with_options(SOUP_SESSION_SSL_STRICT, TRUE,
-                       SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE,
-                       SOUP_SESSION_PROXY_URI, proxy_uri,
-                       SOUP_SESSION_SSL_CA_FILE, "/opt/var/lib/ca-certificates/ca-bundle.pem",
-                       SOUP_SESSION_HTTPS_ALIASES, https_aliases, NULL);
-                       soup_uri_free(proxy_uri);
-       }
-
-       message = soup_message_new(SOUP_METHOD_GET, g_ad.signaling_server.public.url);
-
-       g_print("connecting to signaling server[%s]...\n", g_ad.signaling_server.public.url);
-
-       soup_session_websocket_connect_async(session, message, NULL, NULL, NULL,
-               (GAsyncReadyCallback) __websocket_connected_cb, &g_ad.conns[0]);
-
-       return 0;
-}
-
-static int _webrtc_signaling_server_create(int port)
-{
-       int ret;
-
-       ret = webrtc_signaling_server_create(port, &(g_ad.signaling_server.private.server));
-       RET_VAL_IF(ret != WEBRTC_ERROR_NONE, -1, "failed to webrtc_signaling_server_create(), ret[0x%x]", ret);
-
-       g_print("webrtc_signaling_server_create() success, port[%d] g_ad.signaling_server.private.server[%p]\n",
-               port, g_ad.signaling_server.private.server);
-
-       g_ad.signaling_server.private.server_port = port;
-
-       return 0;
-}
-
-static void _webrtc_signaling_server_destroy(void)
-{
-       int ret;
-
-       ret = webrtc_signaling_server_destroy(g_ad.signaling_server.private.server);
-       RET_IF(ret != WEBRTC_ERROR_NONE, "failed to webrtc_signaling_server_destroy(), ret[0x%x]", ret);
-
-       g_print("webrtc_signaling_server_destroy() success\n");
-       g_ad.signaling_server.private.server = NULL;
-}
-
-static int _webrtc_signaling_server_start(void)
-{
-       int ret;
-
-       ret = webrtc_signaling_server_start(g_ad.signaling_server.private.server);
-       RET_VAL_IF(ret != WEBRTC_ERROR_NONE, -1, "webrtc_signaling_server_start(), ret[0x%x]", ret);
-
-       g_print("webrtc_signaling_server_start() success\n");
-
-       return 0;
-}
-
-static void _webrtc_signaling_server_stop(void)
-{
-       int ret;
-
-       ret = webrtc_signaling_server_stop(g_ad.signaling_server.private.server);
-       RET_IF(ret != WEBRTC_ERROR_NONE, "ret[0x%x]", ret);
-
-       g_print("webrtc_signaling_server_stop() success\n");
-}
-
 void quit_program(void)
 {
        int i;
@@ -4344,7 +3693,7 @@ static void app_setting_and_signaling(char *cmd)
                        g_ad.input_count++;
                        break;
                case 1:
-                       _request_join_room(cmd);
+                       _websocket_request_join_room(cmd);
                        g_ad.input_count = 0;
                        reset_menu_state();
                        break;
index bf51f6071321fe58820628da78f2ff4ff2ac06a7..e006a703820e227a2ac451439be667f6c455e6fd 100644 (file)
@@ -296,6 +296,21 @@ extern menu_info_s g_menu_infos[];
 extern const char *g_server_status_str[];
 extern const char *g_webrtc_state_str[];
 
+void _webrtc_create(int index);
+void _webrtc_start(int index);
+void _webrtc_stop(int index);
+void _webrtc_destroy(int index);
+void _webrtc_set_all_basic_cbs(int index);
+void _webrtc_set_all_negotiation_state_change_cbs(int index);
+void _webrtc_add_media_source(int index, int type, unsigned int *source_id);
+void _webrtc_add_mic_source(int index, bool aec, unsigned int *source_id);
+void _webrtc_media_source_set_video_loopback(int index, unsigned int source_id);
+void _webrtc_create_data_channel(int index);
+void _webrtc_destroy_data_channel(int index);
+void _webrtc_set_display_type(int index, int type);
+void _webrtc_set_remote_description(connection_s *conn);
+void _webrtc_add_ice_candidate(connection_s *conn, const gchar *candidate);
+
 appdata_s *get_appdata(void);
 void display_handle_status(int index);
 void display_setting_status(void);
@@ -326,6 +341,22 @@ void _render_text_message(void **eo, int i, const char *text);
 void _app_start(int *argc, char **argv);
 void _app_stop(void);
 
+int _setting_uri(gchar *dest_arr, char *uri);
+int _connect_signaling_server(void);
+int _webrtc_signaling_server_create(int port);
+void _webrtc_signaling_server_destroy(void);
+int _webrtc_signaling_server_start(void);
+void _webrtc_signaling_server_stop(void);
+int _webrtc_signaling_connect(const char *ip, int port);
+void _webrtc_signaling_disconnect(void);
+void _webrtc_signaling_request_session(int peer_id);
+void _webrtc_signaling_send_message(const char *message);
+void _request_session(int remote_peer_id);
+void _send_local_description(bool is_offer);
+void _send_ice_candidate(connection_s *conn, const char *candidate);
+void _websocket_request_join_room(char *room_name);
+void _websocket_send_text_for_room(connection_s *conn, int remote_peer_id, const char *message);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
diff --git a/test/webrtc_test_signaling.c b/test/webrtc_test_signaling.c
new file mode 100644 (file)
index 0000000..7cae3a8
--- /dev/null
@@ -0,0 +1,674 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "webrtc_test_priv.h"
+#include <json-glib/json-glib.h>
+
+static int __copy_string_arr(gchar *dest_arr, char *string)
+{
+       int len = 0;
+       gsize src_size = 0;
+
+       if (!string)
+               return -1;
+
+       len = strlen(string);
+
+       src_size = g_strlcpy(dest_arr, string, MAX_STRING_LEN);
+       if ((int)src_size != len) {
+               g_print("failed to g_strlcpy()\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+int _setting_uri(gchar *dest_arr, char *uri)
+{
+       int ret = 0;
+
+       if (!uri)
+               return -1;
+
+       if (get_appdata()->signaling_server.private.client) {
+               g_printerr("already set by 'scc'\n");
+               return -1;
+       }
+
+       if (strlen(uri) > strlen("0.0.0.0")) {
+               ret = __copy_string_arr(dest_arr, uri);
+               if (ret != 0) {
+                       g_print("failed to __copy_string_arr()\n");
+                       return -1;
+               }
+       } else {
+               g_print("invalid value, uri[%s]\n", uri);
+               return -1;
+       }
+
+       return 0;
+}
+
+static gint32 __send_greeting_to_register(SoupWebsocketConnection *conn)
+{
+       gchar *hello;
+       gint32 id;
+
+       if (!conn) {
+               g_printerr("conn is NULL\n");
+               return -1;
+       }
+
+       if (soup_websocket_connection_get_state(conn) != SOUP_WEBSOCKET_STATE_OPEN) {
+               g_printerr("websocket is not opened\n");
+               return -1;
+       }
+
+       id = g_random_int_range(10, 10000);
+       hello = g_strdup_printf("HELLO %i", id);
+
+       soup_websocket_connection_send_text(conn, hello);
+
+       g_free(hello);
+
+       return id;
+}
+
+static void __close_websocket(signaling_server_s *ss)
+{
+       RET_IF(!ss, "ss is NULL");
+
+       g_print("close websocket, ws_conn[%p]\n", ss->public.ws_conn);
+
+       if (soup_websocket_connection_get_state(ss->public.ws_conn) == SOUP_WEBSOCKET_STATE_OPEN)
+               soup_websocket_connection_close(ss->public.ws_conn, 1000, "");
+       else
+               g_object_unref(ss->public.ws_conn);
+
+       ss->public.ws_conn = NULL;
+       ss->server_status = SERVER_STATUS_DISCONNECTED;
+       ss->is_connected = false;
+}
+
+static void __websocket_closed_cb(SoupWebsocketConnection *ws_conn, gpointer user_data)
+{
+       signaling_server_s *ss = (signaling_server_s *)user_data;
+
+       RET_IF(!ss, "ss is NULL");
+       RET_IF(!ws_conn, "ws_conn is NULL");
+       RET_IF(ss->public.ws_conn != ws_conn, "not matched ws_conn[%p, %p]", ss->public.ws_conn, ws_conn);
+
+       __close_websocket(ss);
+}
+
+static void __auto_configure_add_peer(gchar *peer_id, bool is_offer)
+{
+       int i;
+
+       RET_IF(!peer_id, "peer_id is NULL");
+
+       if (!strcmp(peer_id,""))
+               return;
+
+       g_print("add peer[%s] to make an %s\n", peer_id, is_offer ? "offer" : "answer");
+
+       for (i = 0; i < MAX_CONNECTION_LEN; i++) {
+               unsigned int source_id = 0;
+
+               if (get_appdata()->conns[i].webrtc)
+                       continue;
+
+               get_appdata()->conns[i].remote_peer_id = atoi((const char *)peer_id);
+               get_appdata()->conns[i].is_for_room = true;
+               get_appdata()->conns[i].is_offer = is_offer;
+               if (i > 0) /* follow the source type of the first one */
+                       get_appdata()->conns[i].room_source_type = get_appdata()->conns[0].room_source_type;
+
+               _webrtc_create(i);
+               _webrtc_set_all_basic_cbs(i);
+               _webrtc_set_all_negotiation_state_change_cbs(i);
+               _webrtc_create_data_channel(i);
+
+               switch (get_appdata()->conns[i].room_source_type) {
+               case 1:
+                       _webrtc_add_media_source(i, WEBRTC_MEDIA_SOURCE_TYPE_AUDIOTEST, NULL);
+                       _webrtc_add_media_source(i, WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST, &source_id);
+                       if (i == 0) /* only set loopback video of the first one */
+                               _webrtc_media_source_set_video_loopback(i, source_id);
+                       break;
+               case 2:
+                       _webrtc_add_mic_source(i, false, NULL);
+                       _webrtc_add_media_source(i, WEBRTC_MEDIA_SOURCE_TYPE_CAMERA, &source_id);
+                       if (i == 0) /* only set loopback video of the first one */
+                               _webrtc_media_source_set_video_loopback(i, source_id);
+                       break;
+               case 3:
+                       _webrtc_add_mic_source(i, false, NULL);
+                       break;
+               default:
+                       return;
+               }
+
+               _webrtc_start(i);
+               _webrtc_set_display_type(i, WEBRTC_DISPLAY_TYPE_EVAS);
+
+               return;
+       }
+       g_printerr("failed to __auto_configure_add_peer_to_offer()\n");
+}
+
+static connection_s *__get_conn_from_peer_id(gchar *peer_id, int *index)
+{
+       int i;
+
+       if (!peer_id) {
+               g_printerr("peer_id is NULL\n");
+               return NULL;
+       }
+
+       for (i = 0; i < MAX_CONNECTION_LEN; i++) {
+               if (get_appdata()->conns[i].remote_peer_id <= 0)
+                       continue;
+               if (get_appdata()->conns[i].remote_peer_id == atoi((const char *)peer_id)) {
+                       if (index)
+                               *index = i;
+                       return &get_appdata()->conns[i];
+               }
+       }
+
+       g_printerr("not found matching conn from peer_id[%s]", peer_id);
+       return NULL;
+}
+
+static void __auto_configure_release_peer(gchar *peer_id)
+{
+       connection_s *conn;
+       int i;
+
+       RET_IF(!peer_id, "peer_id is NULL");
+
+       g_print("release peer[%s] related resources\n", peer_id);
+
+       if (!(conn = __get_conn_from_peer_id(peer_id, &i)))
+               return;
+
+       _webrtc_stop(i);
+       _webrtc_destroy_data_channel(i);
+       _webrtc_destroy(i);
+}
+
+static void __handle_json_structured_message(connection_s *conn, const gchar *text)
+{
+       JsonNode *root;
+       JsonObject *object;
+       JsonParser *parser;
+
+       RET_IF(!conn, "conn is NULL");
+       RET_IF(!text, "text is NULL");
+
+       parser = json_parser_new();
+
+       if (!json_parser_load_from_data(parser, text, -1, NULL)) {
+               g_printerr("unknown message [%s]\n", text);
+               g_object_unref(parser);
+               return;
+       }
+
+       root = json_parser_get_root(parser);
+       if (!JSON_NODE_HOLDS_OBJECT(root)) {
+               g_printerr("unknown JSON message [%s]\n", text);
+               g_object_unref(parser);
+               return;
+       }
+
+       object = json_node_get_object(root);
+       if (json_object_has_member(object, "sdp")){
+               g_print("\n[from SERVER > SDP]\n%s\n", text);
+               if (conn->remote_desc)
+                       free(conn->remote_desc);
+               conn->remote_desc = strdup(text);
+
+               if (conn->is_for_room) {
+                       webrtc_state_e state = WEBRTC_STATE_IDLE;
+                       webrtc_get_state(conn->webrtc, &state);
+                       if (state == WEBRTC_STATE_NEGOTIATING)
+                               _webrtc_set_remote_description(conn);
+               }
+
+       } else if (json_object_has_member(object, "ice")){
+               g_print("\n[from SERVER > ICE]\n%s\n", text);
+               if (conn->is_for_room) {
+                       webrtc_state_e state = WEBRTC_STATE_IDLE;
+                       webrtc_get_state(conn->webrtc, &state);
+                       if (state == WEBRTC_STATE_NEGOTIATING)
+                               _webrtc_add_ice_candidate(conn, text);
+                       else
+                               conn->ice_candidates = g_list_append(conn->ice_candidates, strdup(text));
+               } else {
+                       conn->ice_candidates = g_list_append(conn->ice_candidates, strdup(text));
+               }
+
+       } else {
+               g_printerr("neither 'sdp' nor 'ice' member exist in JSON message [%s]\n", text);
+       }
+
+       g_object_unref(parser);
+}
+
+static void __auto_configure_handle_room_message(gchar *peer_id, gchar *message)
+{
+       connection_s *conn;
+
+       RET_IF(!peer_id, "peer_id is NULL");
+       RET_IF(!message, "message is NULL");
+
+       g_print("peer[%s], message[%s]\n", peer_id, message);
+
+       if (!(conn = __get_conn_from_peer_id(peer_id, NULL)))
+               return;
+
+       __handle_json_structured_message(conn, message);
+}
+
+static void __handle_room_related_message(const gchar *text)
+{
+       g_auto(GStrv) tokens = NULL;
+       guint len;
+       guint i;
+
+       RET_IF(!text, "text is NULL");
+
+       if (g_str_has_prefix(text, "ROOM_OK")) {
+               g_print("\n[from SERVER > %s]\n", text);
+               get_appdata()->signaling_server.server_status = SERVER_STATUS_ROOM_ESTABLISHED;
+
+               /* parse text, get the previous peers in the room and negotiate to each other */
+               if (strlen(text) > strlen("ROOM_OK")) {
+                       /* e.g. ROOM_OK 1234 5311 324 */
+                       tokens = g_strsplit(text, " ", 0);
+                       len = g_strv_length(tokens);
+                       for (i = 1; i < len; i++) {
+                               if (i > MAX_CONNECTION_LEN) {
+                                       g_print("connection[%d], out of range\n", i);
+                                       break;
+                               }
+                               __auto_configure_add_peer(tokens[i], true);
+                       }
+               }
+
+       } else if (g_str_has_prefix(text, "ROOM_PEER_JOINED")) {
+               g_print("\n[from SERVER > %s]\n", text);
+               /* e.g. ROOM_PEER_JOINED 1234 */
+               tokens = g_strsplit(text, " ", 2);
+               __auto_configure_add_peer(tokens[1], false);
+
+       } else if (g_str_has_prefix(text, "ROOM_PEER_LEFT")) {
+               g_print("\n[from SERVER > %s]\n", text);
+               /* e.g. ROOM_PEER_LEFT 1234 */
+               tokens = g_strsplit(text, " ", 2);
+               __auto_configure_release_peer(tokens[1]);
+
+       } else if (g_str_has_prefix(text, "ROOM_PEER_MSG")) {
+               g_print("\n[from SERVER > %s]\n", text);
+               /* e.g. ROOM_PEER_MSG 1234 msg */
+               tokens = g_strsplit(text, " ", 3);
+               __auto_configure_handle_room_message(tokens[1], tokens[2]);
+
+       } else {
+               g_print("\n[from SERVER > %s]\n", text);
+               get_appdata()->signaling_server.server_status = SERVER_STATUS_ERROR_FOUND;
+       }
+}
+
+static void __websocket_message_cb(SoupWebsocketConnection *ws_conn, SoupWebsocketDataType type, GBytes *message, gpointer user_data)
+{
+       gchar *text;
+       gsize size;
+       const gchar *data;
+       connection_s *conn = (connection_s *)user_data;
+
+       RET_IF(!conn, "conn is NULL");
+       RET_IF(!ws_conn, "ws_conn is NULL");
+       RET_IF(get_appdata()->signaling_server.public.ws_conn != ws_conn, "not matched ws_conn[%p, %p]", get_appdata()->signaling_server.public.ws_conn, ws_conn);
+       RET_IF(type != SOUP_WEBSOCKET_DATA_TEXT, "invalid data type(%d)", type);
+
+       data = g_bytes_get_data(message, &size);
+       text = g_strndup(data, size);
+
+       /* NOTE: Logics below can be different in each server */
+       if (g_strcmp0(text, "HELLO") == 0) {
+               g_print("\n[from SERVER > %s] registered done, local_peer_id[%d]\n", text, get_appdata()->signaling_server.local_peer_id);
+
+       } else if (g_strcmp0(text, "SESSION_OK") == 0) {
+               g_print("\n[from SERVER > %s]\n", text);
+               get_appdata()->signaling_server.server_status = SERVER_STATUS_SESSION_ESTABLISHED;
+
+       } else if (g_str_has_prefix(text, "ROOM_")) {
+               __handle_room_related_message(text);
+
+       } else if (g_str_has_prefix(text, "ERROR")) {
+               g_print("\n[from SERVER > %s]\n", text);
+               get_appdata()->signaling_server.server_status = SERVER_STATUS_ERROR_FOUND;
+
+       } else {
+               __handle_json_structured_message(conn, text);
+       }
+
+       g_free(text);
+}
+
+static void __websocket_connected_cb(SoupSession *session, GAsyncResult *res, void *user_data)
+{
+       GError *error = NULL;
+       SoupWebsocketConnection *ws_conn;
+       connection_s *conn = (connection_s *)user_data;
+
+       RET_IF(!conn, "conn is NULL");
+
+       ws_conn = soup_session_websocket_connect_finish(session, res, &error);
+       if (error) {
+               if (soup_websocket_connection_get_state(ws_conn) == SOUP_WEBSOCKET_STATE_OPEN)
+                       soup_websocket_connection_close(ws_conn, 1000, "");
+               else {
+                       g_print("NOT OPENED\n");
+                       g_object_unref(ws_conn);
+               }
+               g_print("failed to soup_session_websocket_connect_finish(), error[%s]\n", error->message);
+               g_error_free(error);
+               return;
+       }
+       g_print("\n[%s] is connected, conn[%p]\n", get_appdata()->signaling_server.public.url, conn);
+
+       get_appdata()->signaling_server.public.ws_conn = ws_conn;
+       get_appdata()->signaling_server.server_status = SERVER_STATUS_CONNECTED;
+
+       g_signal_connect(ws_conn, "closed", G_CALLBACK(__websocket_closed_cb), &get_appdata()->signaling_server);
+       g_signal_connect(ws_conn, "message", G_CALLBACK(__websocket_message_cb), conn);
+
+       get_appdata()->signaling_server.local_peer_id = __send_greeting_to_register(ws_conn);
+       get_appdata()->signaling_server.is_connected = true;
+}
+
+int _connect_signaling_server(void)
+{
+       SoupMessage *message;
+       SoupSession *session;
+       SoupURI *proxy_uri;
+       const char *https_aliases[] = {"wss", NULL};
+
+       RET_VAL_IF(get_appdata()->signaling_server.private.client, -1, "already set by 'scc'");
+
+       if (strlen(get_appdata()->proxy) == 0) {
+               session = soup_session_new_with_options(SOUP_SESSION_SSL_STRICT, TRUE,
+                       SOUP_SESSION_HTTPS_ALIASES, https_aliases, NULL);
+       } else {
+               g_print("use proxy [%s]\n", get_appdata()->proxy);
+               proxy_uri = soup_uri_new(get_appdata()->proxy);
+               session = soup_session_new_with_options(SOUP_SESSION_SSL_STRICT, TRUE,
+                       SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE,
+                       SOUP_SESSION_PROXY_URI, proxy_uri,
+                       SOUP_SESSION_SSL_CA_FILE, "/opt/var/lib/ca-certificates/ca-bundle.pem",
+                       SOUP_SESSION_HTTPS_ALIASES, https_aliases, NULL);
+                       soup_uri_free(proxy_uri);
+       }
+
+       message = soup_message_new(SOUP_METHOD_GET, get_appdata()->signaling_server.public.url);
+
+       g_print("connecting to signaling server[%s]...\n", get_appdata()->signaling_server.public.url);
+
+       soup_session_websocket_connect_async(session, message, NULL, NULL, NULL,
+               (GAsyncReadyCallback) __websocket_connected_cb, &get_appdata()->conns[0]);
+
+       return 0;
+}
+
+int _webrtc_signaling_server_create(int port)
+{
+       int ret;
+
+       ret = webrtc_signaling_server_create(port, &(get_appdata()->signaling_server.private.server));
+       RET_VAL_IF(ret != WEBRTC_ERROR_NONE, -1, "failed to webrtc_signaling_server_create(), ret[0x%x]", ret);
+
+       g_print("webrtc_signaling_server_create() success, port[%d] signaling_server.private.server[%p]\n",
+               port, get_appdata()->signaling_server.private.server);
+
+       get_appdata()->signaling_server.private.server_port = port;
+
+       return 0;
+}
+
+void _webrtc_signaling_server_destroy(void)
+{
+       int ret;
+
+       ret = webrtc_signaling_server_destroy(get_appdata()->signaling_server.private.server);
+       RET_IF(ret != WEBRTC_ERROR_NONE, "failed to webrtc_signaling_server_destroy(), ret[0x%x]", ret);
+
+       g_print("webrtc_signaling_server_destroy() success\n");
+       get_appdata()->signaling_server.private.server = NULL;
+}
+
+int _webrtc_signaling_server_start(void)
+{
+       int ret;
+
+       ret = webrtc_signaling_server_start(get_appdata()->signaling_server.private.server);
+       RET_VAL_IF(ret != WEBRTC_ERROR_NONE, -1, "webrtc_signaling_server_start(), ret[0x%x]", ret);
+
+       g_print("webrtc_signaling_server_start() success\n");
+
+       return 0;
+}
+
+void _webrtc_signaling_server_stop(void)
+{
+       int ret;
+
+       ret = webrtc_signaling_server_stop(get_appdata()->signaling_server.private.server);
+       RET_IF(ret != WEBRTC_ERROR_NONE, "ret[0x%x]", ret);
+
+       g_print("webrtc_signaling_server_stop() success\n");
+}
+
+static void __signaling_message_cb(webrtc_signaling_message_type_e type, const char *message, void *user_data)
+{
+       connection_s *conn = (connection_s *)user_data;
+
+       g_print("__signaling_message_cb(), type[%d] message[%s] conn[%p]\n", type, message, conn);
+
+       if (type == SIGNALING_MESSAGE_TYPE_CONNECTED) {
+               g_print("\n[from SERVER > CONNECTED %s]\n", message);
+               get_appdata()->signaling_server.is_connected = true;
+               get_appdata()->signaling_server.server_status = SERVER_STATUS_CONNECTED;
+               get_appdata()->signaling_server.local_peer_id = atoi(message);
+
+       } else if (type == SIGNALING_MESSAGE_TYPE_DISCONNECTED) {
+               g_print("\n[from SERVER > DISCONNECTED %s]\n", message);
+               get_appdata()->signaling_server.is_connected = false;
+               get_appdata()->signaling_server.server_status = SERVER_STATUS_DISCONNECTED;
+
+       } else if (type == SIGNALING_MESSAGE_TYPE_SESSION_ESTABLISHED) {
+               g_print("\n[from SERVER > SESSION_ESTABLISHED with %s]\n", message);
+               get_appdata()->signaling_server.server_status = SERVER_STATUS_SESSION_ESTABLISHED;
+
+       } else if (type == SIGNALING_MESSAGE_TYPE_SESSION_CLOSED) {
+               g_print("\n[from SERVER > SESSION_CLOSED with %s]\n", message);
+               get_appdata()->signaling_server.server_status = SERVER_STATUS_SESSION_CLOSED;
+
+       } else if (type == SIGNALING_MESSAGE_TYPE_SDP) {
+               g_print("\n[from SERVER > SDP]\n%s\n", message);
+               if (conn->remote_desc)
+                       free(conn->remote_desc);
+               conn->remote_desc = strdup(message);
+
+       } else if (type == SIGNALING_MESSAGE_TYPE_ICE_CANDIDATE) {
+               g_print("\n[from SERVER > ICE]\n%s\n", message);
+               conn->ice_candidates = g_list_append(conn->ice_candidates, strdup(message));
+       }
+}
+
+int _webrtc_signaling_connect(const char *ip, int port)
+{
+       int ret;
+
+       if (strlen(get_appdata()->signaling_server.public.url) > 0) {
+               g_printerr("server[%s] is already set by 'ss'\n", get_appdata()->signaling_server.public.url);
+               return -1;
+       }
+
+       ret = webrtc_signaling_connect(ip, port, __signaling_message_cb, &get_appdata()->conns[0], &get_appdata()->signaling_server.private.client);
+       RET_VAL_IF(ret != WEBRTC_ERROR_NONE, -1, "failed to webrtc_signaling_connect(), ret[0x%x]", ret);
+
+       if (get_appdata()->signaling_server.private.ip)
+               free(get_appdata()->signaling_server.private.ip);
+       get_appdata()->signaling_server.private.ip = strdup(ip);
+       get_appdata()->signaling_server.private.port = port;
+
+       g_print("webrtc_signaling_connect() success\n");
+
+       return 0;
+}
+
+void _webrtc_signaling_disconnect(void)
+{
+       int ret;
+
+       RET_IF(!get_appdata()->signaling_server.is_connected, "signaling server is not connected");
+
+       ret = webrtc_signaling_disconnect(get_appdata()->signaling_server.private.client);
+       RET_IF(ret != WEBRTC_ERROR_NONE, "ret[0x%x]", ret);
+
+       g_print("webrtc_signaling_disconnect() success\n");
+
+       get_appdata()->signaling_server.private.client = NULL;
+}
+
+void _webrtc_signaling_request_session(int peer_id)
+{
+       int ret;
+
+       RET_IF(!get_appdata()->signaling_server.is_connected, "signaling server is not connected");
+
+       ret = webrtc_signaling_request_session(get_appdata()->signaling_server.private.client, peer_id);
+       RET_IF(ret != WEBRTC_ERROR_NONE, "ret[0x%x]", ret);
+
+       g_print("webrtc_signaling_request_session() success\n");
+}
+
+void _webrtc_signaling_send_message(const char *message)
+{
+       int ret;
+
+       RET_IF(!message, "message is NULL");
+       RET_IF(!get_appdata()->signaling_server.is_connected, "signaling server is not connected");
+
+       ret = webrtc_signaling_send_message(get_appdata()->signaling_server.private.client, message);
+       RET_IF(ret != WEBRTC_ERROR_NONE, "ret[0x%x]", ret);
+
+       g_print("webrtc_signaling_send_message() success\n");
+}
+
+void _request_session(int remote_peer_id)
+{
+       RET_IF(!get_appdata()->signaling_server.is_connected, "signaling server is not connected");
+
+       if (get_appdata()->signaling_server.public.ws_conn) {
+               gchar *msg;
+               if (soup_websocket_connection_get_state(get_appdata()->signaling_server.public.ws_conn) != SOUP_WEBSOCKET_STATE_OPEN) {
+                       g_printerr("websocket is not opened\n");
+                       return;
+               }
+
+               msg = g_strdup_printf("SESSION %d", remote_peer_id);
+
+               g_print("\n[to SERVER > %s]\n", msg);
+               soup_websocket_connection_send_text(get_appdata()->signaling_server.public.ws_conn, msg);
+               g_free(msg);
+
+       } else if (get_appdata()->signaling_server.private.client) {
+               _webrtc_signaling_request_session(remote_peer_id);
+
+       } else {
+               g_printerr("Neither public nor private signaling server is connected\n");
+       }
+}
+
+void _send_local_description(bool is_offer)
+{
+       char *desc;
+
+       RET_IF(!get_appdata()->signaling_server.is_connected, "signaling server is not connected");
+
+       desc = is_offer ? get_appdata()->conns[0].offer : get_appdata()->conns[0].answer;
+
+       g_print("\n[to SERVER > local description]\n%s\n", desc);
+
+       if (get_appdata()->signaling_server.public.ws_conn)
+               soup_websocket_connection_send_text(get_appdata()->signaling_server.public.ws_conn, desc);
+       else if (get_appdata()->signaling_server.private.client)
+               _webrtc_signaling_send_message(desc);
+       else
+               g_printerr("Neither public nor private signaling server is connected\n");
+}
+
+void _send_ice_candidate(connection_s *conn, const char *candidate)
+{
+       RET_IF(!conn, "conn is NULL");
+       RET_IF(!get_appdata()->signaling_server.is_connected, "signaling server is not connected");
+
+       if (get_appdata()->signaling_server.public.ws_conn) {
+               if (conn->is_for_room)
+                       _websocket_send_text_for_room(conn, conn->remote_peer_id, candidate);
+               else
+                       soup_websocket_connection_send_text(get_appdata()->signaling_server.public.ws_conn, candidate);
+
+       } else if (get_appdata()->signaling_server.private.client) {
+               _webrtc_signaling_send_message(candidate);
+       }
+}
+
+void _websocket_request_join_room(char *room_name)
+{
+       gchar *msg;
+
+       RET_IF(!room_name, "room_name is NULL");
+       RET_IF(!get_appdata()->signaling_server.is_connected, "signaling server is not connected");
+       RET_IF(!get_appdata()->signaling_server.public.ws_conn, "ws_conn is NULL");
+
+       if (soup_websocket_connection_get_state(get_appdata()->signaling_server.public.ws_conn) != SOUP_WEBSOCKET_STATE_OPEN) {
+               g_printerr("websocket is not opened\n");
+               return;
+       }
+
+       msg = g_strdup_printf("ROOM %s", room_name);
+
+       g_print("\n[to SERVER > %s]\n", msg);
+       soup_websocket_connection_send_text(get_appdata()->signaling_server.public.ws_conn, msg);
+       g_free(msg);
+}
+
+void _websocket_send_text_for_room(connection_s *conn, int remote_peer_id, const char *message)
+{
+       gchar *message_for_room;
+
+       RET_IF(!conn, "conn is NULL");
+       RET_IF(!conn->is_for_room, "conn[%p] is not for room", conn);
+       RET_IF(!message, "message is NULL");
+
+       message_for_room = g_strdup_printf ("ROOM_PEER_MSG %d %s", remote_peer_id, message);
+       soup_websocket_connection_send_text(get_appdata()->signaling_server.public.ws_conn, message_for_room);
+       g_free(message_for_room);
+}
\ No newline at end of file