webrtc_test: Add support for making up to four connections 60/248660/11
authorSangchul Lee <sc11.lee@samsung.com>
Tue, 1 Dec 2020 08:09:05 +0000 (17:09 +0900)
committerSangchul Lee <sc11.lee@samsung.com>
Wed, 9 Dec 2020 04:04:09 +0000 (13:04 +0900)
Now it can make up to four connections to the signaling server.
Each connection can have one webrtc handle.

A test case for webrtc_set_display() is added.

[Version] 0.1.67
[Issue Type] Test application

Change-Id: I4d7d3d7991ef3a74018435c4e95ca16df6a0c1b7
Signed-off-by: Sangchul Lee <sc11.lee@samsung.com>
packaging/capi-media-webrtc.spec
test/webrtc_test.c

index 699a64764a01c58b01bbfd5fb6161d8618ef8f19..cc168e3d4e55b0a5606042f1193c891dc2921697 100644 (file)
@@ -1,6 +1,6 @@
 Name:       capi-media-webrtc
 Summary:    A WebRTC library in Tizen Native API
-Version:    0.1.66
+Version:    0.1.67
 Release:    0
 Group:      Multimedia/API
 License:    Apache-2.0
index b8b1105db9cbcae0814e5d0c9e764aebdb5008c8..c1d7ad2fbc6ed06dfe80b602eb10e5ac804504f0 100644 (file)
@@ -36,6 +36,7 @@
 
 #define MAX_STRING_LEN 512
 #define MAX_CHANNEL_LEN 10
+#define MAX_CONNECTION_LEN 4
 
 enum {
        CURRENT_STATUS_MAINMENU,
@@ -43,6 +44,7 @@ enum {
        CURRENT_STATUS_REMOVE_MEDIA_SOURCE,
        CURRENT_STATUS_GET_TRANSCEIVER_DIRECTION,
        CURRENT_STATUS_SET_TRANSCEIVER_DIRECTION,
+       CURRENT_STATUS_SET_DISPLAY_TYPE,
        CURRENT_STATUS_DATA_CHANNEL_SEND_STRING,
        CURRENT_STATUS_DATA_CHANNEL_SEND_STRING_AS_BYTES,
        CURRENT_STATUS_DATA_CHANNEL_SEND_FILE,
@@ -51,6 +53,7 @@ enum {
        CURRENT_STATUS_SET_REMOTE_DESCRIPTION,
        CURRENT_STATUS_SETTING_SIGNALING_SERVER,
        CURRENT_STATUS_SETTING_PROXY,
+       CURRENT_STATUS_CHANGE_CONNECTION,
        CURRENT_STATUS_REQUEST_SESSION,
        CURRENT_STATUS_SEND_LOCAL_DESCRIPTION,
 };
@@ -87,36 +90,47 @@ static const char* g_webrtc_transceiver_direction_str[] = {
 };
 
 /* for video display */
-static Evas_Object *g_eo;
 static Evas_Object *g_win_id;
 static Evas_Object *g_selected_win_id;
 
 typedef struct {
        Evas_Object *win;
        Evas_Object *layout_main;
-} appdata;
-
-static appdata ad;
-static webrtc_h g_webrtc;
-static webrtc_data_channel_h g_channels[MAX_CHANNEL_LEN];
-static int g_channel_index;
-static webrtc_data_channel_h g_recv_channels[MAX_CHANNEL_LEN];
-static char *g_offer;
-static char *g_answer;
-static char *g_remote_desc;
-static GList *g_ice_candidates;
-static SoupWebsocketConnection *g_ws_conn;
-static gint32 g_local_peer_id;
+} appdata_s;
+
+typedef struct _connection_s {
+       SoupWebsocketConnection *ws_conn;
+       gint32 local_peer_id;
+       int server_status;
+
+       webrtc_h webrtc;
+       webrtc_data_channel_h channels[MAX_CHANNEL_LEN];
+       int channel_index;
+       webrtc_data_channel_h recv_channels[MAX_CHANNEL_LEN];
+       char *offer;
+       char *answer;
+       char *remote_desc;
+       GList *ice_candidates;
+
+       int menu_state;
+       int cnt;
+
+       /* receive data & dump file */
+       int sum_size;
+       gchar *expected_name;
+       int expected_size;
+       char* receive_buffer;
+
+       webrtc_display_type_e display_type;
+       Evas_Object *eo;
+} connection_s;
+
 static gchar g_signaling_server[MAX_STRING_LEN];
 static gchar g_proxy[MAX_STRING_LEN];
-static int g_server_status = SERVER_STATUS_DISCONNECTED;
-static int g_menu_state = CURRENT_STATUS_MAINMENU;
-static int g_cnt;
 
-/* receive data & dump file */
-static gchar *g_expected_name;
-static int g_expected_size;
-static char* g_receive_buffer;
+static appdata_s ad;
+static connection_s g_conns[MAX_CONNECTION_LEN];
+static int g_conn_index;
 
 static void win_del(void *data, Evas_Object *obj, void *event)
 {
@@ -137,7 +151,7 @@ static Evas_Object *create_win(const char *name)
                elm_win_borderless_set(eo, EINA_TRUE);
                evas_object_smart_callback_add(eo, "delete,request", win_del, NULL);
                elm_win_screen_size_get(eo, NULL, NULL, &w, &h);
-               g_print("window size :%d,%d", w, h);
+               g_print("window size: %d x %d\n", w, h);
                evas_object_resize(eo, w, h);
                elm_win_autodel_set(eo, EINA_TRUE);
                elm_win_alpha_set(eo, EINA_TRUE);
@@ -147,14 +161,17 @@ static Evas_Object *create_win(const char *name)
 
 static Evas_Object *create_image_object(Evas_Object *eo_parent)
 {
+       Evas_Object *eo = NULL;
+       Evas *evas;
+
        if (!eo_parent)
                return NULL;
 
-       Evas *evas = evas_object_evas_get(eo_parent);
-       Evas_Object *eo = NULL;
-
+       evas = evas_object_evas_get(eo_parent);
        eo = evas_object_image_add(evas);
 
+       g_print("eo [%p]\n", eo);
+
        return eo;
 }
 
@@ -185,10 +202,14 @@ void create_render_rect_and_bg(Evas_Object *win)
        evas_object_show(win);
 }
 
+#define EO_ONE_SIDE_LEN 512
+#define PADDING_HIGHT 480
+
 static int app_create(void *data)
 {
-       appdata *ad = data;
+       appdata_s *ad = data;
        Evas_Object *win = NULL;
+       int i;
 
        /* use gl backend */
        elm_config_accel_preference_set("opengl");
@@ -201,12 +222,15 @@ static int app_create(void *data)
        g_win_id = win;
        g_selected_win_id = g_win_id;
        create_render_rect_and_bg(ad->win);
-       /* Create evas image object for EVAS surface */
-       g_eo = create_image_object(ad->win);
-       evas_object_image_size_set(g_eo, 500, 500);
-       evas_object_image_fill_set(g_eo, 0, 0, 500, 500);
-       evas_object_resize(g_eo, 500, 500);
 
+       /* Create evas image object for EVAS surface */
+       for (i = 0; i < MAX_CONNECTION_LEN; i++) {
+               g_conns[i].eo = create_image_object(ad->win);
+               evas_object_image_size_set(g_conns[i].eo, EO_ONE_SIDE_LEN, EO_ONE_SIDE_LEN);
+               evas_object_image_fill_set(g_conns[i].eo, 0, 0, EO_ONE_SIDE_LEN, EO_ONE_SIDE_LEN);
+               evas_object_resize(g_conns[i].eo, EO_ONE_SIDE_LEN, EO_ONE_SIDE_LEN);
+               evas_object_move(g_conns[i].eo, (i % 2) * EO_ONE_SIDE_LEN, ((i / 2) * EO_ONE_SIDE_LEN) + PADDING_HIGHT);
+       }
        elm_win_activate(win);
        evas_object_show(win);
 
@@ -215,7 +239,15 @@ static int app_create(void *data)
 
 static int app_terminate(void *data)
 {
-       appdata *ad = data;
+       appdata_s *ad = data;
+       int i;
+
+       for (i = 0; i < MAX_CONNECTION_LEN; i++) {
+               if (g_conns[i].eo) {
+                       evas_object_del(g_conns[i].eo);
+                       g_conns[i].eo = NULL;
+               }
+       }
 
        if (g_win_id) {
                evas_object_del(g_win_id);
@@ -233,127 +265,127 @@ struct appcore_ops ops = {
        .terminate = app_terminate,
 };
 
-static void _webrtc_create()
+static void _webrtc_create(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
 
-       if (g_webrtc) {
+       if (g_conns[index].webrtc) {
                g_print("failed to _webrtc_create(), already created\n");
                return;
        }
 
-       ret = webrtc_create(&g_webrtc);
+       ret = webrtc_create(&g_conns[index].webrtc);
        if (ret != WEBRTC_ERROR_NONE)
                g_print("webrtc_create() returned [0x%x]\n", ret);
        else
-               g_print("webrtc_create() success, g_webrtc[%p]\n", g_webrtc);
+               g_print("webrtc[%p, index:%d] is created\n", g_conns[index].webrtc, index);
 }
 
-static void _webrtc_destroy()
+static void _webrtc_destroy(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
        int i;
 
-       if (!g_webrtc) {
-               g_print("failed to _webrtc_destroy(), g_webrtc is NULL\n");
+       if (!g_conns[index].webrtc) {
+               g_print("failed to _webrtc_destroy(), webrtc is NULL\n");
                return;
        }
 
-       ret = webrtc_destroy(g_webrtc);
+       ret = webrtc_destroy(g_conns[index].webrtc);
        if (ret != WEBRTC_ERROR_NONE) {
                g_print("webrtc_destroy() returned [0x%x]\n", ret);
        } else {
-               g_print("webrtc_destroy() success\n");
-               g_webrtc = NULL;
+               g_print("webrtc[%p, index:%d] is destroyed\n", g_conns[index].webrtc, index);
+               g_conns[index].webrtc = NULL;
                for (i = 0; i < MAX_CHANNEL_LEN; i++) {
-                       if (g_channels[i] != NULL)
-                               g_channels[i] = NULL;
-                       if (g_recv_channels[i] != NULL)
-                               g_recv_channels[i] = NULL;
+                       if (g_conns[index].channels[i] != NULL)
+                               g_conns[index].channels[i] = NULL;
+                       if (g_conns[index].recv_channels[i] != NULL)
+                               g_conns[index].recv_channels[i] = NULL;
                }
-               g_channel_index = 0;
+               g_conns[index].channel_index = 0;
        }
 }
 
-static void _webrtc_start()
+static void _webrtc_start(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
 
-       if (!g_webrtc) {
-               g_print("failed to _webrtc_start(), g_webrtc is NULL\n");
+       if (!g_conns[index].webrtc) {
+               g_print("failed to _webrtc_start(), webrtc is NULL\n");
                return;
        }
 
-       ret = webrtc_start(g_webrtc);
+       ret = webrtc_start(g_conns[index].webrtc);
        if (ret != WEBRTC_ERROR_NONE)
                g_print("webrtc_start() returned [0x%x]\n", ret);
        else
                g_print("webrtc_start() success\n");
 }
 
-static void _webrtc_stop()
+static void _webrtc_stop(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
        int i;
 
-       if (!g_webrtc) {
-               g_print("failed to _webrtc_stop(), g_webrtc is NULL\n");
+       if (!g_conns[index].webrtc) {
+               g_print("failed to _webrtc_stop(), webrtc is NULL\n");
                return;
        }
 
-       ret = webrtc_stop(g_webrtc);
+       ret = webrtc_stop(g_conns[index].webrtc);
        if (ret != WEBRTC_ERROR_NONE) {
                g_print("webrtc_stop() returned [0x%x]\n", ret);
        } else {
                g_print("webrtc_stop() success\n");
                for (i = 0; i < MAX_CHANNEL_LEN; i++) {
-                       if (g_recv_channels[i] != NULL)
-                               g_recv_channels[i] = NULL;
+                       if (g_conns[index].recv_channels[i] != NULL)
+                               g_conns[index].recv_channels[i] = NULL;
                }
        }
 }
 
-static void _webrtc_get_state()
+static void _webrtc_get_state(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
        webrtc_state_e state;
 
-       ret = webrtc_get_state(g_webrtc, &state);
+       ret = webrtc_get_state(g_conns[index].webrtc, &state);
        if (ret != WEBRTC_ERROR_NONE)
                g_print("webrtc_get_state() returned [0x%x]\n", ret);
        else
                g_print("state: [%s]\n", g_webrtc_state_str[state]);
 }
 
-static void _webrtc_add_media_source(int type)
+static void _webrtc_add_media_source(int index, int type)
 {
        int ret = WEBRTC_ERROR_NONE;
        unsigned int source_id = 0;
 
-       ret = webrtc_add_media_source(g_webrtc, (webrtc_media_source_type_e)type, &source_id);
+       ret = webrtc_add_media_source(g_conns[index].webrtc, (webrtc_media_source_type_e)type, &source_id);
        if (ret != WEBRTC_ERROR_NONE)
                g_print("failed to webrtc_add_media_source(), ret[0x%x]\n", ret);
        else
                g_print("webrtc_add_media_source() success, source_id[%u]\n", source_id);
 }
 
-static void _webrtc_remove_media_source(unsigned int source_id)
+static void _webrtc_remove_media_source(int index, unsigned int source_id)
 {
        int ret = WEBRTC_ERROR_NONE;
 
-       ret = webrtc_remove_media_source(g_webrtc, source_id);
+       ret = webrtc_remove_media_source(g_conns[index].webrtc, source_id);
        if (ret != WEBRTC_ERROR_NONE)
                g_print("failed to webrtc_remove_media_source(), source_id[%u], ret[0x%x]\n", source_id, ret);
        else
                g_print("webrtc_remove_media_source() success, source_id[%u]\n", source_id);
 }
 
-static void _webrtc_get_transceiver_direction(unsigned int source_id, webrtc_media_type_e media_type)
+static void _webrtc_get_transceiver_direction(int index, unsigned int source_id, webrtc_media_type_e media_type)
 {
        int ret = WEBRTC_ERROR_NONE;
        webrtc_transceiver_direction_e direction;
 
-       ret = webrtc_get_transceiver_direction(g_webrtc, source_id, media_type, &direction);
+       ret = webrtc_get_transceiver_direction(g_conns[index].webrtc, source_id, media_type, &direction);
        if (ret != WEBRTC_ERROR_NONE)
                g_print("failed to webrtc_get_transceiver_direction(), ret[0x%x]\n", ret);
        else
@@ -361,11 +393,11 @@ static void _webrtc_get_transceiver_direction(unsigned int source_id, webrtc_med
                        source_id, g_webrtc_media_type_str[media_type], g_webrtc_transceiver_direction_str[direction]);
 }
 
-static void _webrtc_set_transceiver_direction(unsigned int source_id, webrtc_media_type_e media_type, webrtc_transceiver_direction_e direction)
+static void _webrtc_set_transceiver_direction(int index, unsigned int source_id, webrtc_media_type_e media_type, webrtc_transceiver_direction_e direction)
 {
        int ret = WEBRTC_ERROR_NONE;
 
-       ret = webrtc_set_transceiver_direction(g_webrtc, source_id, media_type, direction);
+       ret = webrtc_set_transceiver_direction(g_conns[index].webrtc, source_id, media_type, direction);
        if (ret != WEBRTC_ERROR_NONE)
                g_print("failed to webrtc_set_transceiver_direction(), source_id[%u], ret[0x%x]\n", source_id, ret);
        else
@@ -373,6 +405,13 @@ static void _webrtc_set_transceiver_direction(unsigned int source_id, webrtc_med
                        source_id, g_webrtc_media_type_str[media_type], g_webrtc_transceiver_direction_str[direction]);
 }
 
+static void _webrtc_set_display_type(int index, int type)
+{
+       g_conns[index].display_type = type;
+
+       g_print("display type[%d] is set, it'll be applied when starting rendering video.\n", type);
+}
+
 static int __copy_string_arr(gchar *dest_arr, char *string)
 {
        int len = 0;
@@ -392,16 +431,16 @@ static int __copy_string_arr(gchar *dest_arr, char *string)
        return 0;
 }
 
-static void _webrtc_data_channel_send_string(const char *string)
+static void _webrtc_data_channel_send_string(int index, const char *string)
 {
        int ret = WEBRTC_ERROR_NONE;
        int i;
 
        for (i = 0; i < MAX_CHANNEL_LEN; i++) {
-               if (g_channels[i] == NULL)
+               if (g_conns[index].channels[i] == NULL)
                        continue;
-               g_print("data_channel[%p], index[%d]: ", g_channels[i], i);
-               ret = webrtc_data_channel_send_string(g_channels[i], string);
+               g_print("data_channel[%p], index[%d]: ", g_conns[index].channels[i], i);
+               ret = webrtc_data_channel_send_string(g_conns[index].channels[i], string);
                if (ret != WEBRTC_ERROR_NONE)
                        g_print("failed to webrtc_data_channel_send_string(), string[%s]\n", string);
                else
@@ -409,16 +448,16 @@ static void _webrtc_data_channel_send_string(const char *string)
        }
 }
 
-static void _webrtc_data_channel_send_string_as_bytes(const char *string)
+static void _webrtc_data_channel_send_string_as_bytes(int index, const char *string)
 {
        int ret = WEBRTC_ERROR_NONE;
        int i;
 
        for (i = 0; i < MAX_CHANNEL_LEN; i++) {
-               if (g_channels[i] == NULL)
+               if (g_conns[index].channels[i] == NULL)
                        continue;
-               g_print("data_channel[%p], index[%d]: ", g_channels[i], i);
-               ret = webrtc_data_channel_send_bytes(g_channels[i], string, strlen(string));
+               g_print("data_channel[%p], index[%d]: ", g_conns[index].channels[i], i);
+               ret = webrtc_data_channel_send_bytes(g_conns[index].channels[i], string, strlen(string));
                if (ret != WEBRTC_ERROR_NONE)
                        g_print("failed to webrtc_data_channel_send_bytes(), string[%s]\n", string);
                else
@@ -426,7 +465,7 @@ static void _webrtc_data_channel_send_string_as_bytes(const char *string)
        }
 }
 
-static void _webrtc_data_channel_send_file(const char *file_path)
+static void _webrtc_data_channel_send_file(int index, const char *file_path)
 {
        #define BUFFER_SIZE 16 * 1024 /* 16 kbytes */
        int ret = WEBRTC_ERROR_NONE;
@@ -440,9 +479,9 @@ static void _webrtc_data_channel_send_file(const char *file_path)
        int fd;
 
        for (i = 0; i < MAX_CHANNEL_LEN; i++) {
-               if (g_channels[i] == NULL)
+               if (g_conns[index].channels[i] == NULL)
                        continue;
-               g_print("data_channel[%p], index[%d]: ", g_channels[i], i);
+               g_print("data_channel[%p], index[%d]: ", g_conns[index].channels[i], i);
 
                fd = open(file_path, O_RDONLY);
                if (fd == -1) {
@@ -460,13 +499,13 @@ static void _webrtc_data_channel_send_file(const char *file_path)
                expected_size = g_strdup_printf("expected size:%llu", st.st_size);
                expected_name = g_strdup_printf("expected name:%s", basename((char *)file_path));
 
-               ret = webrtc_data_channel_send_string(g_channels[i], expected_size);
+               ret = webrtc_data_channel_send_string(g_conns[index].channels[i], expected_size);
                if (ret != WEBRTC_ERROR_NONE)
                        g_print("failed to webrtc_data_channel_send_string(), string[%s]\n", expected_size);
                else
                        g_print("webrtc_data_channel_send_string() success, string[%s]\n", expected_size);
 
-               ret = webrtc_data_channel_send_string(g_channels[i], expected_name);
+               ret = webrtc_data_channel_send_string(g_conns[index].channels[i], expected_name);
                if (ret != WEBRTC_ERROR_NONE)
                        g_print("failed to webrtc_data_channel_send_string(), string[%s]\n", expected_name);
                else
@@ -480,7 +519,7 @@ static void _webrtc_data_channel_send_file(const char *file_path)
                        sum_size += read_size;
                        g_print("[%zd / %llu]bytes is read", sum_size, st.st_size);
 
-                       ret = webrtc_data_channel_send_bytes(g_channels[i], buffer, (unsigned int)read_size);
+                       ret = webrtc_data_channel_send_bytes(g_conns[index].channels[i], buffer, (unsigned int)read_size);
                        if (ret != WEBRTC_ERROR_NONE)
                                g_print("failed to webrtc_data_channel_send_bytes(), file_path[%s], size[%zd]\n", file_path, read_size);
                        else
@@ -491,26 +530,26 @@ static void _webrtc_data_channel_send_file(const char *file_path)
        }
 }
 
-static void _webrtc_set_stun_server(char *uri)
+static void _webrtc_set_stun_server(int index, char *uri)
 {
        int ret = 0;
 
        if (!uri)
                return;
 
-       webrtc_set_stun_server(g_webrtc, uri);
+       webrtc_set_stun_server(g_conns[index].webrtc, uri);
        if (ret != WEBRTC_ERROR_NONE)
                g_print("failed to webrtc_set_stun_server(), uri[%s], ret[0x%x]\n", uri, ret);
        else
                g_print("webrtc_set_stun_server() success, uri[%s]\n", uri);
 }
 
-static void _webrtc_get_stun_server(void)
+static void _webrtc_get_stun_server(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
        char *stun_server;
 
-       ret = webrtc_get_stun_server(g_webrtc, &stun_server);
+       ret = webrtc_get_stun_server(g_conns[index].webrtc, &stun_server);
        if (ret != WEBRTC_ERROR_NONE) {
                g_print("failed to webrtc_get_stun_server(), ret[0x%x]\n", ret);
        } else {
@@ -521,7 +560,9 @@ static void _webrtc_get_stun_server(void)
 
 static void __data_channel_open_cb(webrtc_data_channel_h channel, void *user_data)
 {
-       g_print("__data_channel_open_cb() is called, channel[%p]\n", channel);
+       connection_s *conn = (connection_s*)user_data;
+
+       g_print("__data_channel_open_cb() is called, channel[%p], conn[%p]\n", channel, conn);
 }
 
 static void __file_dump(const gchar *file_name, void *src_buffer, int size)
@@ -558,7 +599,14 @@ end:
 
 static void __data_channel_message_cb(webrtc_data_channel_h channel, webrtc_data_channel_type_e type, void *message, void *user_data)
 {
-       g_print("__data_channel_message_cb() is called, channel[%p], type[%d], ", channel, type);
+       connection_s *conn = (connection_s*)user_data;
+
+       g_print("__data_channel_message_cb() is called, channel[%p], type[%d], conn[%p]", channel, type, conn);
+
+       if (conn == NULL) {
+               g_printerr("conn is NULL\n");
+               return;
+       }
 
        if (type == WEBRTC_DATA_CHANNEL_TYPE_STRING) {
                gchar **str_arr = NULL;
@@ -567,16 +615,16 @@ static void __data_channel_message_cb(webrtc_data_channel_h channel, webrtc_data
 
                if (g_str_has_prefix((const gchar *)message, "expected name:")) {
                        str_arr = g_strsplit((const gchar *)message, ":", 2);
-                       g_free(g_expected_name);
-                       g_expected_name = g_strdup(str_arr[1]);
+                       g_free(conn->expected_name);
+                       conn->expected_name = g_strdup(str_arr[1]);
 
                } else if (g_str_has_prefix((const gchar *)message, "expected size:")) {
                        str_arr = g_strsplit((const gchar *)message, ":", 2);
-                       g_expected_size = atoi(str_arr[1]);
+                       conn->expected_size = atoi(str_arr[1]);
 
-                       if (g_receive_buffer)
-                               free(g_receive_buffer);
-                       g_receive_buffer = (char *)calloc(g_expected_size, sizeof(char));
+                       if (conn->receive_buffer)
+                               free(conn->receive_buffer);
+                       conn->receive_buffer = (char *)calloc(conn->expected_size, sizeof(char));
                }
 
                if (str_arr)
@@ -591,24 +639,22 @@ static void __data_channel_message_cb(webrtc_data_channel_h channel, webrtc_data
                webrtc_get_data(data, &data_p, &size);
                g_print("bytes message[%p, size:%u]\n", data_p, size);
 
-               if (g_expected_size > 0 && g_expected_name) {
-                       static int sum_size = 0;
-
-                       g_print("downloading [%s], size[%d / %d]\n", g_expected_name, sum_size, g_expected_size);
+               if (conn->expected_size > 0 && conn->expected_name) {
+                       g_print("downloading [%s], size[%d / %d]\n", conn->expected_name, conn->sum_size, conn->expected_size);
 
-                       memcpy(&g_receive_buffer[sum_size], ((uint8_t*)data_p), size);
+                       memcpy(&conn->receive_buffer[conn->sum_size], ((uint8_t*)data_p), size);
 
-                       sum_size += size;
-                       if (sum_size >= g_expected_size) {
-                               __file_dump(g_expected_name, g_receive_buffer, sum_size);
+                       conn->sum_size += size;
+                       if (conn->sum_size >= conn->expected_size) {
+                               __file_dump(conn->expected_name, conn->receive_buffer, conn->sum_size);
 
-                               sum_size = 0;
-                               g_expected_size = 0;
-                               g_free(g_expected_name);
-                               g_expected_name = NULL;
-                               if (g_receive_buffer) {
-                                       free(g_receive_buffer);
-                                       g_receive_buffer = NULL;
+                               conn->sum_size = 0;
+                               conn->expected_size = 0;
+                               g_free(conn->expected_name);
+                               conn->expected_name = NULL;
+                               if (conn->receive_buffer) {
+                                       free(conn->receive_buffer);
+                                       conn->receive_buffer = NULL;
                                }
                        }
 
@@ -622,49 +668,59 @@ static void __data_channel_message_cb(webrtc_data_channel_h channel, webrtc_data
 
 static void __data_channel_error_cb(webrtc_data_channel_h channel, webrtc_error_e error, void *user_data)
 {
-       g_print("__data_channel_error_cb() is called, channel[%p]\n", channel);
+       connection_s *conn = (connection_s*)user_data;
+
+       g_print("__data_channel_error_cb() is called, channel[%p], conn[%p]\n", channel, conn);
 }
 
 static void __data_channel_close_cb(webrtc_data_channel_h channel, void *user_data)
 {
-       g_print("__data_channel_close_cb() is called, channel[%p]\n", channel);
+       connection_s *conn = (connection_s*)user_data;
+
+       g_print("__data_channel_close_cb() is called, channel[%p], conn[%p]\n", channel, conn);
 }
 
 static void __data_channel_cb(webrtc_h webrtc, webrtc_data_channel_h channel, void *user_data)
 {
        int i;
+       connection_s *conn = (connection_s*)user_data;
+
+       if (conn == NULL) {
+               g_printerr("conn is NULL\n");
+               return;
+       }
 
        for (i = 0; i < MAX_CHANNEL_LEN; i++) {
-               if (g_recv_channels[i] == NULL) {
-                       g_print("__data_channel_cb() is called, append all the callbacks.\n");
-
-                       g_recv_channels[i] = channel;
-                       webrtc_data_channel_set_open_cb(channel, __data_channel_open_cb, webrtc);
-                       webrtc_data_channel_set_message_cb(channel, __data_channel_message_cb, webrtc);
-                       webrtc_data_channel_set_error_cb(channel, __data_channel_error_cb, webrtc);
-                       webrtc_data_channel_set_close_cb(channel, __data_channel_close_cb, webrtc);
+               if (conn->recv_channels[i] == NULL) {
+                       g_print("__data_channel_cb() is called for conn[%p], append all the callbacks.\n", conn);
+
+                       conn->recv_channels[i] = channel;
+                       webrtc_data_channel_set_open_cb(channel, __data_channel_open_cb, (void *)conn);
+                       webrtc_data_channel_set_message_cb(channel, __data_channel_message_cb, (void *)conn);
+                       webrtc_data_channel_set_error_cb(channel, __data_channel_error_cb, (void *)conn);
+                       webrtc_data_channel_set_close_cb(channel, __data_channel_close_cb, (void *)conn);
                        return;
                }
        }
        g_print("__data_channel_cb() is called, but g_recv_channels is full\n");
 }
 
-static void _webrtc_set_data_channel_cb()
+static void _webrtc_set_data_channel_cb(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
 
-       ret = webrtc_set_data_channel_cb(g_webrtc, __data_channel_cb, g_webrtc);
+       ret = webrtc_set_data_channel_cb(g_conns[index].webrtc, __data_channel_cb, &g_conns[index]);
        if (ret != WEBRTC_ERROR_NONE)
                g_print("failed to webrtc_set_data_channel_cb()\n");
        else
                g_print("webrtc_set_data_channel_cb() success\n");
 }
 
-static void _webrtc_unset_data_channel_cb()
+static void _webrtc_unset_data_channel_cb(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
 
-       ret = webrtc_unset_data_channel_cb(g_webrtc);
+       ret = webrtc_unset_data_channel_cb(g_conns[index].webrtc);
        if (ret != WEBRTC_ERROR_NONE)
                g_print("failed to webrtc_unset_data_channel_cb()\n");
        else
@@ -677,22 +733,22 @@ static void __error_cb(webrtc_h webrtc, webrtc_error_e error, webrtc_state_e sta
                webrtc, error, state, user_data);
 }
 
-static void _webrtc_set_error_cb()
+static void _webrtc_set_error_cb(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
 
-       ret = webrtc_set_error_cb(g_webrtc, __error_cb, g_webrtc);
+       ret = webrtc_set_error_cb(g_conns[index].webrtc, __error_cb, g_conns[index].webrtc);
        if (ret != WEBRTC_ERROR_NONE)
                g_print("failed to webrtc_set_error_cb()\n");
        else
                g_print("webrtc_set_error_cb() success\n");
 }
 
-static void _webrtc_unset_error_cb()
+static void _webrtc_unset_error_cb(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
 
-       ret = webrtc_unset_error_cb(g_webrtc);
+       ret = webrtc_unset_error_cb(g_conns[index].webrtc);
        if (ret != WEBRTC_ERROR_NONE)
                g_print("failed to webrtc_unset_error_cb()\n");
        else
@@ -705,22 +761,22 @@ static void __state_changed_cb(webrtc_h webrtc, webrtc_state_e previous, webrtc_
                webrtc, g_webrtc_state_str[previous], g_webrtc_state_str[current], user_data);
 }
 
-static void _webrtc_set_state_changed_cb()
+static void _webrtc_set_state_changed_cb(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
 
-       ret = webrtc_set_state_changed_cb(g_webrtc, __state_changed_cb, g_webrtc);
+       ret = webrtc_set_state_changed_cb(g_conns[index].webrtc, __state_changed_cb, g_conns[index].webrtc);
        if (ret != WEBRTC_ERROR_NONE)
                g_print("failed to webrtc_set_state_changed_cb()\n");
        else
                g_print("webrtc_set_state_changed_cb() success\n");
 }
 
-static void _webrtc_unset_state_changed_cb()
+static void _webrtc_unset_state_changed_cb(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
 
-       ret = webrtc_unset_state_changed_cb(g_webrtc);
+       ret = webrtc_unset_state_changed_cb(g_conns[index].webrtc);
        if (ret != WEBRTC_ERROR_NONE)
                g_print("failed to webrtc_unset_state_changed_cb()\n");
        else
@@ -732,22 +788,22 @@ 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 _webrtc_set_negotiation_needed_cb()
+static void _webrtc_set_negotiation_needed_cb(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
 
-       ret = webrtc_set_negotiation_needed_cb(g_webrtc, __negotiation_needed_cb, g_webrtc);
+       ret = webrtc_set_negotiation_needed_cb(g_conns[index].webrtc, __negotiation_needed_cb, g_conns[index].webrtc);
        if (ret != WEBRTC_ERROR_NONE)
                g_print("failed to webrtc_set_negotiation_needed_cb()\n");
        else
                g_print("webrtc_set_negotiation_needed_cb() success\n");
 }
 
-static void _webrtc_unset_negotiation_needed_cb()
+static void _webrtc_unset_negotiation_needed_cb(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
 
-       ret = webrtc_unset_negotiation_needed_cb(g_webrtc);
+       ret = webrtc_unset_negotiation_needed_cb(g_conns[index].webrtc);
        if (ret != WEBRTC_ERROR_NONE)
                g_print("failed to webrtc_unset_negotiation_needed_cb()\n");
        else
@@ -759,25 +815,25 @@ static void __ice_candidate_cb(webrtc_h webrtc, const char *candidate, void *use
        g_print("__ice_candidate_cb() is invoked\n");
 
        g_print("\n[to SERVER > ICE]\n%s\n", candidate);
-       soup_websocket_connection_send_text(g_ws_conn, candidate);
+       soup_websocket_connection_send_text(g_conns[g_conn_index].ws_conn, candidate);
 }
 
-static void _webrtc_set_ice_candidate_cb()
+static void _webrtc_set_ice_candidate_cb(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
 
-       ret = webrtc_set_ice_candidate_cb(g_webrtc, __ice_candidate_cb, g_webrtc);
+       ret = webrtc_set_ice_candidate_cb(g_conns[index].webrtc, __ice_candidate_cb, g_conns[index].webrtc);
        if (ret != WEBRTC_ERROR_NONE)
                g_print("failed to webrtc_set_ice_candidate_cb()\n");
        else
                g_print("webrtc_set_ice_candidate_cb() success\n");
 }
 
-static void _webrtc_unset_ice_candidate_cb()
+static void _webrtc_unset_ice_candidate_cb(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
 
-       ret = webrtc_unset_ice_candidate_cb(g_webrtc);
+       ret = webrtc_unset_ice_candidate_cb(g_conns[index].webrtc);
        if (ret != WEBRTC_ERROR_NONE)
                g_print("failed to webrtc_unset_ice_candidate_cb()\n");
        else
@@ -786,79 +842,103 @@ static void _webrtc_unset_ice_candidate_cb()
 
 static void __track_added_cb(webrtc_h webrtc, webrtc_media_type_e type, unsigned int id, void *user_data)
 {
-       g_print("__track_added_cb() is invoked, webrtc[%p], type[%d], id[%u], user_data[%p]\n", webrtc, type, id, user_data);
+       connection_s *conn = (connection_s *)user_data;
+
+       if (conn == NULL) {
+               g_printerr("conn is NULL\n");
+               return;
+       }
+
+       g_print("__track_added_cb() is invoked, webrtc[%p], type[%d], id[%u], conn[%p]\n", webrtc, type, id, conn);
+
+       if (type == WEBRTC_MEDIA_TYPE_VIDEO) {
+               g_print("Video track is added, ");
+               if (conn->display_type == WEBRTC_DISPLAY_TYPE_OVERLAY) {
+                       g_print("set display - overlay\n");
+                       /* FIXME: set window id here */
+
+               } else if (conn->display_type == WEBRTC_DISPLAY_TYPE_EVAS) {
+                       g_print("set display - evas object[%p]\n", conn->eo);
+                       webrtc_set_display(conn->webrtc, id, WEBRTC_DISPLAY_TYPE_EVAS, conn->eo);
+
+               } else {
+                       g_print("invalid display type[%d]\n", conn->display_type);
+               }
+       } else if (type == WEBRTC_MEDIA_TYPE_AUDIO) {
+               g_print("Audio track is added\n");
+       }
 }
 
-static void _webrtc_set_track_added_cb()
+static void _webrtc_set_track_added_cb(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
 
-       ret = webrtc_set_track_added_cb(g_webrtc, __track_added_cb, g_webrtc);
+       ret = webrtc_set_track_added_cb(g_conns[index].webrtc, __track_added_cb, &g_conns[index]);
        if (ret != WEBRTC_ERROR_NONE)
                g_print("failed to webrtc_set_track_added_cb()\n");
        else
                g_print("webrtc_set_track_added_cb() success\n");
 }
 
-static void _webrtc_unset_track_added_cb()
+static void _webrtc_unset_track_added_cb(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
 
-       ret = webrtc_unset_track_added_cb(g_webrtc);
+       ret = webrtc_unset_track_added_cb(g_conns[index].webrtc);
        if (ret != WEBRTC_ERROR_NONE)
                g_print("failed to webrtc_unset_track_added_cb()\n");
        else
                g_print("webrtc_unset_track_added_cb() success\n");
 }
 
-static void _webrtc_create_offer()
+static void _webrtc_create_offer(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
 
-       if (g_offer) {
-               free(g_offer);
-               g_offer = NULL;
+       if (g_conns[index].offer) {
+               free(g_conns[index].offer);
+               g_conns[index].offer = NULL;
        }
 
-       ret = webrtc_create_offer(g_webrtc, &g_offer);
+       ret = webrtc_create_offer(g_conns[index].webrtc, &g_conns[index].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);
+               g_print("webrtc_create_offer() success\noffer:\n%s\n", g_conns[index].offer);
 }
 
-static void _webrtc_create_answer()
+static void _webrtc_create_answer(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
 
-       if (g_answer) {
-               free(g_answer);
-               g_answer = NULL;
+       if (g_conns[index].answer) {
+               free(g_conns[index].answer);
+               g_conns[index].answer = NULL;
        }
 
-       ret = webrtc_create_answer(g_webrtc, &g_answer);
+       ret = webrtc_create_answer(g_conns[index].webrtc, &g_conns[index].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);
+               g_print("webrtc_create_answer() success\nanswer:\n%s\n", g_conns[index].answer);
 }
 
-static void _webrtc_set_local_description(char *desc)
+static void _webrtc_set_local_description(int index, bool is_offer)
 {
        int ret = WEBRTC_ERROR_NONE;
 
-       ret = webrtc_set_local_description(g_webrtc, desc);
+       ret = webrtc_set_local_description(g_conns[index].webrtc, is_offer ? g_conns[index].offer : g_conns[index].answer);
        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(void)
+static void _webrtc_set_remote_description(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
 
-       ret = webrtc_set_remote_description(g_webrtc, g_remote_desc);
+       ret = webrtc_set_remote_description(g_conns[index].webrtc, g_conns[index].remote_desc);
        if (ret != WEBRTC_ERROR_NONE)
                g_print("failed to webrtc_set_remote_description()\n");
        else
@@ -869,65 +949,66 @@ static void __foreach_ice_candidate(gpointer data, gpointer user_data)
 {
        int ret = WEBRTC_ERROR_NONE;
 
-       ret = webrtc_add_ice_candidate(g_webrtc, (const char *)data);
+       ret = webrtc_add_ice_candidate(g_conns[g_conn_index].webrtc, (const char *)data);
        if (ret != WEBRTC_ERROR_NONE)
                g_print("failed to webrtc_ice_candidate()\n");
        else
                g_print("webrtc_ice_candidate() success\n");
 }
 
-static void _webrtc_add_ice_candidate(void)
+static void _webrtc_add_ice_candidate(int index)
 {
-       g_list_foreach(g_ice_candidates, __foreach_ice_candidate, NULL);
+       g_list_foreach(g_conns[index].ice_candidates, __foreach_ice_candidate, NULL);
 
-       g_list_free_full(g_ice_candidates, free);
+       g_list_free_full(g_conns[index].ice_candidates, free);
 
-       g_ice_candidates = NULL;
+       g_conns[index].ice_candidates = NULL;
 }
 
-static void _webrtc_create_data_channel(void)
+static void _webrtc_create_data_channel(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
        gchar *label;
 
-       if (g_channel_index == MAX_CHANNEL_LEN) {
+       if (g_conns[index].channel_index == MAX_CHANNEL_LEN) {
                g_print("%d channels are already created, skip it\n", MAX_CHANNEL_LEN);
                return;
        }
 
-       label = g_strdup_printf("data_channel_%d", g_channel_index);
+       label = g_strdup_printf("data_channel_%d_%d", index, g_conns[index].channel_index);
 
-       ret = webrtc_create_data_channel(g_webrtc, label, &g_channels[g_channel_index]);
+       ret = webrtc_create_data_channel(g_conns[index].webrtc, label, &g_conns[index].channels[g_conns[index].channel_index]);
        if (ret != WEBRTC_ERROR_NONE) {
                g_print("failed to webrtc_create_data_channel()\n");
        } else {
-               g_print("webrtc_create_data_channel() success, channel[%p], index[%d]\n", g_channels[g_channel_index], g_channel_index);
-               webrtc_data_channel_set_open_cb(g_channels[g_channel_index], __data_channel_open_cb, g_webrtc);
-               webrtc_data_channel_set_error_cb(g_channels[g_channel_index], __data_channel_error_cb, g_webrtc);
-               webrtc_data_channel_set_close_cb(g_channels[g_channel_index], __data_channel_close_cb, g_webrtc);
-               g_channel_index++;
+               g_print("webrtc_create_data_channel() success, channel[%p], index[%d]\n",
+                       g_conns[index].channels[g_conns[index].channel_index], g_conns[index].channel_index);
+               webrtc_data_channel_set_open_cb(g_conns[index].channels[g_conns[index].channel_index], __data_channel_open_cb, &g_conns[index]);
+               webrtc_data_channel_set_error_cb(g_conns[index].channels[g_conns[index].channel_index], __data_channel_error_cb, &g_conns[index]);
+               webrtc_data_channel_set_close_cb(g_conns[index].channels[g_conns[index].channel_index], __data_channel_close_cb, &g_conns[index]);
+               g_conns[index].channel_index++;
        }
 
        g_free(label);
 }
 
-static void _webrtc_destroy_data_channel(void)
+static void _webrtc_destroy_data_channel(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
        int i;
 
        for (i = 0; i < MAX_CHANNEL_LEN; i++) {
-               if (g_channels[i] == NULL)
+               if (g_conns[index].channels[i] == NULL)
                        continue;
-               ret = webrtc_destroy_data_channel(g_channels[i]);
+               ret = webrtc_destroy_data_channel(g_conns[index].channels[i]);
                if (ret != WEBRTC_ERROR_NONE) {
                        g_print("failed to _webrtc_destroy_data_channel(), index[%d]\n", i);
                } else {
                        g_print("_webrtc_destroy_data_channel() success, index[%d]\n", i);
-                       g_channels[i] = NULL;
+                       g_conns[index].channels[i] = NULL;
                }
        }
-       g_channel_index = 0;
+       g_conns[index].channel_index = 0;
 }
 
 static void _setting_uri(gchar *dest_arr, char *uri)
@@ -946,16 +1027,16 @@ static void _setting_uri(gchar *dest_arr, char *uri)
        }
 }
 
-static void _request_session(int remote_peer_id)
+static void _request_session(int index, int remote_peer_id)
 {
        gchar *msg;
 
-       if (!g_ws_conn) {
+       if (!g_conns[index].ws_conn) {
                g_printerr("server[%s] is not connected\n", g_signaling_server);
                return;
        }
 
-       if (soup_websocket_connection_get_state(g_ws_conn) != SOUP_WEBSOCKET_STATE_OPEN) {
+       if (soup_websocket_connection_get_state(g_conns[index].ws_conn) != SOUP_WEBSOCKET_STATE_OPEN) {
                g_printerr("websocket is not opened\n");
                return;
        }
@@ -963,50 +1044,71 @@ static void _request_session(int remote_peer_id)
        msg = g_strdup_printf("SESSION %d", remote_peer_id);
 
        g_print("\n[to SERVER > %s]\n", msg);
-       soup_websocket_connection_send_text(g_ws_conn, msg);
+       soup_websocket_connection_send_text(g_conns[index].ws_conn, msg);
 
        g_free(msg);
 }
 
-static void _send_local_description(char *desc)
+static void _send_local_description(int index, bool is_offer)
 {
-       if (!g_ws_conn) {
+       char *desc;
+
+       if (!g_conns[index].ws_conn) {
                g_printerr("server[%s] is not connected\n", g_signaling_server);
                return;
        }
 
+       desc = is_offer ? g_conns[index].offer : g_conns[index].answer;
+
        g_print("\n[to SERVER > local description]\n%s\n", desc);
-       soup_websocket_connection_send_text(g_ws_conn, desc);
+       soup_websocket_connection_send_text(g_conns[index].ws_conn, desc);
 }
 
-static void __close_websocket(SoupWebsocketConnection *conn)
+static void __close_websocket(connection_s *conn)
 {
        if (!conn) {
                g_printerr("conn is NULL\n");
                return;
        }
 
-       if (soup_websocket_connection_get_state(conn) == SOUP_WEBSOCKET_STATE_OPEN)
-               soup_websocket_connection_close(conn, 1000, "");
+       if (soup_websocket_connection_get_state(conn->ws_conn) == SOUP_WEBSOCKET_STATE_OPEN)
+               soup_websocket_connection_close(conn->ws_conn, 1000, "");
        else
-               g_object_unref(conn);
+               g_object_unref(conn->ws_conn);
+
+       g_print("conn[%p] is closed\n",conn->ws_conn);
 
-       g_ws_conn = NULL;
-       g_server_status = SERVER_STATUS_DISCONNECTED;
+       conn->ws_conn = NULL;
+       conn->server_status = SERVER_STATUS_DISCONNECTED;
 }
 
-static void __websocket_closed_cb(SoupWebsocketConnection *conn, gpointer user_data)
+static void __websocket_closed_cb(SoupWebsocketConnection *ws_conn, gpointer user_data)
 {
-       g_print("[%s] is closed\n", g_signaling_server);
+       connection_s *conn = (connection_s *)user_data;
+
+       if (!conn || !ws_conn) {
+               g_printerr("conn[%p] or ws_conn[%p] is NULL\n", conn, ws_conn);
+               return;
+       }
+       if (conn->ws_conn != ws_conn) {
+               g_printerr("not matched ws_conn[%p, %p]\n", conn->ws_conn, ws_conn);
+               return;
+       }
+
        __close_websocket(conn);
 }
 
-static void __handle_json_structured_message(const gchar *text)
+static void __handle_json_structured_message(connection_s *conn, const gchar *text)
 {
        JsonNode *root;
        JsonObject *object;
        JsonParser *parser;
 
+       if (!conn) {
+               g_printerr("conn is NULL\n");
+               return;
+       }
+
        if (!text) {
                g_printerr("text is NULL\n");
                return;
@@ -1030,13 +1132,13 @@ static void __handle_json_structured_message(const gchar *text)
        object = json_node_get_object(root);
        if (json_object_has_member(object, "sdp")){
                g_print("\n[from SERVER > SDP]\n%s\n", text);
-               if (g_remote_desc)
-                       free(g_remote_desc);
-               g_remote_desc = strdup(text);
+               if (conn->remote_desc)
+                       free(conn->remote_desc);
+               conn->remote_desc = strdup(text);
 
        } else if (json_object_has_member(object, "ice")){
                g_print("\n[from SERVER > ICE]\n%s\n", text);
-               g_ice_candidates = g_list_append(g_ice_candidates, strdup(text));
+               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);
@@ -1045,11 +1147,21 @@ static void __handle_json_structured_message(const gchar *text)
        g_object_unref(parser);
 }
 
-static void __websocket_message_cb(SoupWebsocketConnection *conn, SoupWebsocketDataType type, GBytes *message, gpointer user_data)
+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;
+
+       if (!conn || !ws_conn) {
+               g_printerr("conn[%p] or ws_conn[%p] is NULL\n", conn, ws_conn);
+               return;
+       }
+       if (conn->ws_conn != ws_conn) {
+               g_printerr("not matched ws_conn[%p, %p]\n", conn->ws_conn, ws_conn);
+               return;
+       }
 
        if (type != SOUP_WEBSOCKET_DATA_TEXT) {
                g_printerr("invalid data type(%d)\n", type);
@@ -1060,18 +1172,18 @@ static void __websocket_message_cb(SoupWebsocketConnection *conn, SoupWebsocketD
 
        /* NOTE: Logics below can be different in each server */
        if (g_strcmp0(text, "HELLO") == 0) {
-               g_print("\n[from SERVER > %s] registered done, g_local_peer_id[%d]\n", text, g_local_peer_id);
+               g_print("\n[from SERVER > %s] registered done, g_local_peer_id[%d]\n", text, conn->local_peer_id);
 
        } else if (g_strcmp0(text, "SESSION_OK") == 0) {
                g_print("\n[from SERVER > %s]\n", text);
-               g_server_status = SERVER_STATUS_SESSION_ESTABLISHED;
+               conn->server_status = SERVER_STATUS_SESSION_ESTABLISHED;
 
        } else if (g_str_has_prefix(text, "ERROR")) {
                g_print("\n[from SERVER > %s]\n", text);
-               g_server_status = SERVER_STATUS_ERROR_FOUND;
+               conn->server_status = SERVER_STATUS_ERROR_FOUND;
 
        } else {
-               __handle_json_structured_message(text);
+               __handle_json_structured_message(conn, text);
        }
 
        g_free(text);
@@ -1102,29 +1214,41 @@ static gint32 __send_greeting_to_register(SoupWebsocketConnection *conn)
        return id;
 }
 
-static void __websocket_connected_cb(SoupSession *session, GAsyncResult *res, SoupMessage *message)
+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;
+
+       if (conn == NULL) {
+               g_printerr("conn is NULL\n");
+               return;
+       }
 
-       g_print("\n[%s] is connected\n", g_signaling_server);
        ws_conn = soup_session_websocket_connect_finish(session, res, &error);
        if (error) {
-               __close_websocket(ws_conn);
+               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_signaling_server, conn);
+
+       conn->ws_conn = ws_conn;
+       conn->server_status = SERVER_STATUS_CONNECTED;
 
-       g_signal_connect(ws_conn, "closed", G_CALLBACK(__websocket_closed_cb), NULL);
-       g_signal_connect(ws_conn, "message", G_CALLBACK(__websocket_message_cb), NULL);
+       g_signal_connect(ws_conn, "closed", G_CALLBACK(__websocket_closed_cb), conn);
+       g_signal_connect(ws_conn, "message", G_CALLBACK(__websocket_message_cb), conn);
 
-       g_local_peer_id = __send_greeting_to_register(ws_conn);
-       g_ws_conn = ws_conn;
-       g_server_status = SERVER_STATUS_CONNECTED;
+       conn->local_peer_id = __send_greeting_to_register(ws_conn);
 }
 
-static void _connect_signaling_server(void)
+static void _connect_signaling_server(int index)
 {
        SoupMessage *message;
        SoupSession *session;
@@ -1150,19 +1274,26 @@ static void _connect_signaling_server(void)
        g_print("connecting to signaling server[%s]...\n", g_signaling_server);
 
        soup_session_websocket_connect_async(session, message, NULL, NULL, NULL,
-               (GAsyncReadyCallback) __websocket_connected_cb, message);
+               (GAsyncReadyCallback) __websocket_connected_cb, &g_conns[index]);
 }
 
 void quit_program()
 {
-       if (g_webrtc != NULL) {
-               _webrtc_stop();
-               _webrtc_destroy();
+       int i;
+       for (i = 0; i < MAX_CONNECTION_LEN; i++) {
+               if (g_conns[i].webrtc != NULL) {
+                       _webrtc_stop(i);
+                       _webrtc_destroy(i);
+               }
+               if (g_conns[i].offer)
+                       free(g_conns[i].offer);
+               if (g_conns[i].answer)
+                       free(g_conns[i].answer);
+               if (g_conns[i].remote_desc)
+                       free(g_conns[i].remote_desc);
+               g_list_free_full(g_conns[i].ice_candidates, free);
        }
-       free(g_offer);
-       free(g_answer);
-       free(g_remote_desc);
-       g_list_free_full(g_ice_candidates, free);
+
        elm_exit();
 }
 
@@ -1171,25 +1302,25 @@ void _interpret_main_menu(char *cmd)
        int len = strlen(cmd);
        if (len == 1) {
                if (strncmp(cmd, "c", 1) == 0) {
-                       _webrtc_create();
+                       _webrtc_create(g_conn_index);
 
                } else if (strncmp(cmd, "a", 1) == 0) {
-                       g_menu_state = CURRENT_STATUS_ADD_MEDIA_SOURCE;
+                       g_conns[g_conn_index].menu_state = CURRENT_STATUS_ADD_MEDIA_SOURCE;
 
                } else if (strncmp(cmd, "r", 1) == 0) {
-                       g_menu_state = CURRENT_STATUS_REMOVE_MEDIA_SOURCE;
+                       g_conns[g_conn_index].menu_state = CURRENT_STATUS_REMOVE_MEDIA_SOURCE;
 
                } else if (strncmp(cmd, "s", 1) == 0) {
-                       _webrtc_start();
+                       _webrtc_start(g_conn_index);
 
                } else if (strncmp(cmd, "t", 1) == 0) {
-                       _webrtc_stop();
+                       _webrtc_stop(g_conn_index);
 
                } else if (strncmp(cmd, "d", 1) == 0) {
-                       _webrtc_destroy();
+                       _webrtc_destroy(g_conn_index);
 
                } else if (strncmp(cmd, "g", 1) == 0) {
-                       _webrtc_get_state();
+                       _webrtc_get_state(g_conn_index);
 
                } else {
                        g_print("unknown menu \n");
@@ -1197,97 +1328,103 @@ void _interpret_main_menu(char *cmd)
 
        } else if (len == 2) {
                if (strncmp(cmd, "gd", 2) == 0) {
-                       g_menu_state = CURRENT_STATUS_GET_TRANSCEIVER_DIRECTION;
+                       g_conns[g_conn_index].menu_state = CURRENT_STATUS_GET_TRANSCEIVER_DIRECTION;
 
                } else if (strncmp(cmd, "td", 2) == 0) {
-                       g_menu_state = CURRENT_STATUS_SET_TRANSCEIVER_DIRECTION;
+                       g_conns[g_conn_index].menu_state = CURRENT_STATUS_SET_TRANSCEIVER_DIRECTION;
+
+               } else if (strncmp(cmd, "dt", 2) == 0) {
+                       g_conns[g_conn_index].menu_state = CURRENT_STATUS_SET_DISPLAY_TYPE;
 
                } else if (strncmp(cmd, "cd", 2) == 0) {
-                       _webrtc_create_data_channel();
+                       _webrtc_create_data_channel(g_conn_index);
 
                } else if (strncmp(cmd, "dd", 2) == 0) {
-                       _webrtc_destroy_data_channel();
+                       _webrtc_destroy_data_channel(g_conn_index);
 
                } else if (strncmp(cmd, "sz", 2) == 0) {
-                       _webrtc_set_data_channel_cb();
+                       _webrtc_set_data_channel_cb(g_conn_index);
 
                } else if (strncmp(cmd, "uz", 2) == 0) {
-                       _webrtc_unset_data_channel_cb();
+                       _webrtc_unset_data_channel_cb(g_conn_index);
 
                } else if (strncmp(cmd, "zs", 2) == 0) {
-                       g_menu_state = CURRENT_STATUS_DATA_CHANNEL_SEND_STRING;
+                       g_conns[g_conn_index].menu_state = CURRENT_STATUS_DATA_CHANNEL_SEND_STRING;
 
                } else if (strncmp(cmd, "zb", 2) == 0) {
-                       g_menu_state = CURRENT_STATUS_DATA_CHANNEL_SEND_STRING_AS_BYTES;
+                       g_conns[g_conn_index].menu_state = CURRENT_STATUS_DATA_CHANNEL_SEND_STRING_AS_BYTES;
 
                } else if (strncmp(cmd, "zf", 2) == 0) {
-                       g_menu_state = CURRENT_STATUS_DATA_CHANNEL_SEND_FILE;
+                       g_conns[g_conn_index].menu_state = CURRENT_STATUS_DATA_CHANNEL_SEND_FILE;
 
                } else if (strncmp(cmd, "se", 2) == 0) {
-                       _webrtc_set_error_cb();
+                       _webrtc_set_error_cb(g_conn_index);
 
                } else if (strncmp(cmd, "ue", 2) == 0) {
-                       _webrtc_unset_error_cb();
+                       _webrtc_unset_error_cb(g_conn_index);
 
                } else if (strncmp(cmd, "sc", 2) == 0) {
-                       _webrtc_set_state_changed_cb();
+                       _webrtc_set_state_changed_cb(g_conn_index);
 
                } else if (strncmp(cmd, "us", 2) == 0) {
-                       _webrtc_unset_state_changed_cb();
+                       _webrtc_unset_state_changed_cb(g_conn_index);
 
                } else if (strncmp(cmd, "sn", 2) == 0) {
-                       _webrtc_set_negotiation_needed_cb();
+                       _webrtc_set_negotiation_needed_cb(g_conn_index);
 
                } else if (strncmp(cmd, "un", 2) == 0) {
-                       _webrtc_unset_negotiation_needed_cb();
+                       _webrtc_unset_negotiation_needed_cb(g_conn_index);
 
                } else if (strncmp(cmd, "si", 2) == 0) {
-                       _webrtc_set_ice_candidate_cb();
+                       _webrtc_set_ice_candidate_cb(g_conn_index);
 
                } else if (strncmp(cmd, "ui", 2) == 0) {
-                       _webrtc_unset_ice_candidate_cb();
+                       _webrtc_unset_ice_candidate_cb(g_conn_index);
 
                } else if (strncmp(cmd, "sk", 2) == 0) {
-                       _webrtc_set_track_added_cb();
+                       _webrtc_set_track_added_cb(g_conn_index);
 
                } else if (strncmp(cmd, "uk", 2) == 0) {
-                       _webrtc_unset_track_added_cb();
+                       _webrtc_unset_track_added_cb(g_conn_index);
 
                } else if (strncmp(cmd, "co", 2) == 0) {
-                       _webrtc_create_offer();
+                       _webrtc_create_offer(g_conn_index);
 
                } else if (strncmp(cmd, "ca", 2) == 0) {
-                       _webrtc_create_answer();
+                       _webrtc_create_answer(g_conn_index);
 
                } else if (strncmp(cmd, "sl", 2) == 0) {
-                       g_menu_state = CURRENT_STATUS_SET_LOCAL_DESCRIPTION;
+                       g_conns[g_conn_index].menu_state = CURRENT_STATUS_SET_LOCAL_DESCRIPTION;
 
                } else if (strncmp(cmd, "sr", 2) == 0) {
-                       _webrtc_set_remote_description();
+                       _webrtc_set_remote_description(g_conn_index);
 
                } else if (strncmp(cmd, "st", 2) == 0) {
-                       g_menu_state = CURRENT_STATUS_SET_STUN_SERVER;
+                       g_conns[g_conn_index].menu_state = CURRENT_STATUS_SET_STUN_SERVER;
 
                } else if (strncmp(cmd, "gt", 2) == 0) {
-                       _webrtc_get_stun_server();
+                       _webrtc_get_stun_server(g_conn_index);
 
                } else if (strncmp(cmd, "ss", 2) == 0) {
-                       g_menu_state = CURRENT_STATUS_SETTING_SIGNALING_SERVER;
+                       g_conns[g_conn_index].menu_state = CURRENT_STATUS_SETTING_SIGNALING_SERVER;
 
                } else if (strncmp(cmd, "px", 2) == 0) {
-                       g_menu_state = CURRENT_STATUS_SETTING_PROXY;
+                       g_conns[g_conn_index].menu_state = CURRENT_STATUS_SETTING_PROXY;
+
+               }  else if (strncmp(cmd, "cc", 2) == 0) {
+                       g_conns[g_conn_index].menu_state = CURRENT_STATUS_CHANGE_CONNECTION;
 
                } else if (strncmp(cmd, "cs", 2) == 0) {
-                       _connect_signaling_server();
+                       _connect_signaling_server(g_conn_index);
 
                } else if (strncmp(cmd, "rs", 2) == 0) {
-                       g_menu_state = CURRENT_STATUS_REQUEST_SESSION;
+                       g_conns[g_conn_index].menu_state = CURRENT_STATUS_REQUEST_SESSION;
 
                } else if (strncmp(cmd, "sd", 2) == 0) {
-                       g_menu_state = CURRENT_STATUS_SEND_LOCAL_DESCRIPTION;
+                       g_conns[g_conn_index].menu_state = CURRENT_STATUS_SEND_LOCAL_DESCRIPTION;
 
                } else if (strncmp(cmd, "ac", 2) == 0) {
-                       _webrtc_add_ice_candidate();
+                       _webrtc_add_ice_candidate(g_conn_index);
 
                } else {
                        g_print("unknown menu \n");
@@ -1295,12 +1432,12 @@ void _interpret_main_menu(char *cmd)
 
        } else if (len == 3) {
                if (strncmp(cmd, "sac", 3) == 0) {
-                       _webrtc_set_error_cb();
-                       _webrtc_set_state_changed_cb();
-                       _webrtc_set_negotiation_needed_cb();
-                       _webrtc_set_ice_candidate_cb();
-                       _webrtc_set_track_added_cb();
-                       _webrtc_set_data_channel_cb();
+                       _webrtc_set_error_cb(g_conn_index);
+                       _webrtc_set_state_changed_cb(g_conn_index);
+                       _webrtc_set_negotiation_needed_cb(g_conn_index);
+                       _webrtc_set_ice_candidate_cb(g_conn_index);
+                       _webrtc_set_track_added_cb(g_conn_index);
+                       _webrtc_set_data_channel_cb(g_conn_index);
 
                } else {
                        g_print("unknown menu \n");
@@ -1311,23 +1448,24 @@ void _interpret_main_menu(char *cmd)
        }
 }
 
-void display_handle_status()
+void display_handle_status(int index)
 {
        int ret = WEBRTC_ERROR_NONE;
        webrtc_state_e state;
        char *stun_server;
 
-       if (g_webrtc == NULL)
+       if (g_conns[index].webrtc == NULL)
                return;
 
-       ret = webrtc_get_state(g_webrtc, &state);
+       ret = webrtc_get_state(g_conns[index].webrtc, &state);
        if (ret != WEBRTC_ERROR_NONE)
                return;
 
-       ret = webrtc_get_stun_server(g_webrtc, &stun_server);
+       ret = webrtc_get_stun_server(g_conns[index].webrtc, &stun_server);
        if (ret != WEBRTC_ERROR_NONE)
                return;
 
+       g_print("  webrtc[%p]", g_conns[index].webrtc);
        g_print("  state[%s]", g_webrtc_state_str[state]);
        if (strlen(stun_server) > 0)
                g_print("  STUN server[%s]", stun_server);
@@ -1341,6 +1479,7 @@ void display_setting_status()
 {
        int len_proxy = strlen(g_proxy);
        int len_server = strlen(g_signaling_server);
+       int i;
 
        if (len_proxy == 0 && len_server == 0)
                return;
@@ -1348,10 +1487,16 @@ void display_setting_status()
        if (len_proxy > 0)
                g_print("  proxy[%s]", g_proxy);
        if (len_server > 0) {
-               g_print("  server[%s]", g_signaling_server);
-               g_print("[%s]", g_server_status_str[g_server_status]);
+               g_print("  server[%s]\n", g_signaling_server);
+               for (i = 0; i < MAX_CONNECTION_LEN; i++) {
+                       if (i == g_conn_index)
+                               g_print("  [*]");
+                       else
+                               g_print("  [ ]");
+                       g_print("[%d][%s]\n", i, g_server_status_str[g_conns[i].server_status]);
+               }
        }
-       g_print("\n-----------------------------------------------------------------------------------------\n");
+       g_print("-----------------------------------------------------------------------------------------\n");
 }
 
 void display_sub_basic()
@@ -1360,7 +1505,7 @@ void display_sub_basic()
        g_print("=========================================================================================\n");
        g_print("                          Native WebRTC Test (press q to quit) \n");
        g_print("-----------------------------------------------------------------------------------------\n");
-       display_handle_status();
+       display_handle_status(g_conn_index);
        g_print("c. Create\t");
        g_print("d. Destroy\n");
        g_print("s. Start\t");
@@ -1370,6 +1515,7 @@ void display_sub_basic()
        g_print("r. Remove media source\n");
        g_print("gd. Get transceiver direction\t");
        g_print("td. Set transceiver direction\n");
+       g_print("dt. Set display type\n");
        g_print("cd. Create data channel\t");
        g_print("dd. Destroy data channel\n");
        g_print("zs. Send string via data channel\n");
@@ -1401,6 +1547,7 @@ void display_sub_basic()
        display_setting_status();
        g_print("px. Set proxy URL\n");
        g_print("ss. Set signaling server URL\n");
+       g_print("cc. Change connection\n");
        g_print("cs. Connect to the signaling server\n");
        g_print("rs. Request session of remote peer id\n");
        g_print("sd. Send local description\n");
@@ -1410,54 +1557,60 @@ void display_sub_basic()
 
 static void displaymenu()
 {
-       if (g_menu_state == CURRENT_STATUS_MAINMENU) {
+       if (g_conns[g_conn_index].menu_state == CURRENT_STATUS_MAINMENU) {
                display_sub_basic();
 
-       } else if (g_menu_state == CURRENT_STATUS_ADD_MEDIA_SOURCE) {
+       } else if (g_conns[g_conn_index].menu_state == CURRENT_STATUS_ADD_MEDIA_SOURCE) {
                g_print("*** input media source type.(1:camera, 2:mic, 3:audiotest, 4:videotest)\n");
 
-       } else if (g_menu_state == CURRENT_STATUS_REMOVE_MEDIA_SOURCE) {
+       } else if (g_conns[g_conn_index].menu_state == CURRENT_STATUS_REMOVE_MEDIA_SOURCE) {
                g_print("*** input media source id to remove.\n");
 
-       } else if (g_menu_state == CURRENT_STATUS_GET_TRANSCEIVER_DIRECTION) {
-               if (g_cnt == 0)
+       } else if (g_conns[g_conn_index].menu_state == CURRENT_STATUS_GET_TRANSCEIVER_DIRECTION) {
+               if (g_conns[g_conn_index].cnt == 0)
                        g_print("*** input source id.\n");
-               else if (g_cnt == 1)
+               else if (g_conns[g_conn_index].cnt == 1)
                        g_print("*** input media type.(1:audio 2:video)\n");
 
-       } else if (g_menu_state == CURRENT_STATUS_SET_TRANSCEIVER_DIRECTION) {
-               if (g_cnt == 0)
+       } else if (g_conns[g_conn_index].menu_state == CURRENT_STATUS_SET_TRANSCEIVER_DIRECTION) {
+               if (g_conns[g_conn_index].cnt == 0)
                        g_print("*** input source id.\n");
-               else if (g_cnt == 1)
+               else if (g_conns[g_conn_index].cnt == 1)
                        g_print("*** input media type.(1:audio 2:video)\n");
-               else if (g_cnt == 2)
+               else if (g_conns[g_conn_index].cnt == 2)
                        g_print("*** input transceiver direction.(1:sendonly 2:recvonly, 3:sendrecv)\n");
 
-       } else if (g_menu_state == CURRENT_STATUS_DATA_CHANNEL_SEND_STRING) {
+       } else if (g_conns[g_conn_index].menu_state == CURRENT_STATUS_SET_DISPLAY_TYPE) {
+               g_print("*** input display type.(1:overlay, 2:evas)\n");
+
+       } else if (g_conns[g_conn_index].menu_state == CURRENT_STATUS_DATA_CHANNEL_SEND_STRING) {
                g_print("*** input string to send.\n");
 
-       } else if (g_menu_state == CURRENT_STATUS_DATA_CHANNEL_SEND_STRING_AS_BYTES) {
+       } else if (g_conns[g_conn_index].menu_state == CURRENT_STATUS_DATA_CHANNEL_SEND_STRING_AS_BYTES) {
                g_print("*** input string to send.(it will be converted to bytes data)\n");
 
-       } else if (g_menu_state == CURRENT_STATUS_DATA_CHANNEL_SEND_FILE) {
+       } else if (g_conns[g_conn_index].menu_state == CURRENT_STATUS_DATA_CHANNEL_SEND_FILE) {
                g_print("*** input file path to send.\n");
 
-       } else if (g_menu_state == CURRENT_STATUS_SET_STUN_SERVER) {
+       } else if (g_conns[g_conn_index].menu_state == CURRENT_STATUS_SET_STUN_SERVER) {
                g_print("*** input STUN server address.\n");
 
-       } else if (g_menu_state == CURRENT_STATUS_SET_LOCAL_DESCRIPTION) {
+       } else if (g_conns[g_conn_index].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_SETTING_SIGNALING_SERVER) {
+       } else if (g_conns[g_conn_index].menu_state == CURRENT_STATUS_SETTING_SIGNALING_SERVER) {
                g_print("*** input signaling server URL.\n");
 
-       } else if (g_menu_state == CURRENT_STATUS_SETTING_PROXY) {
+       } else if (g_conns[g_conn_index].menu_state == CURRENT_STATUS_SETTING_PROXY) {
                g_print("*** input proxy URL.\n");
 
-       } else if (g_menu_state == CURRENT_STATUS_REQUEST_SESSION) {
+       } else if (g_conns[g_conn_index].menu_state == CURRENT_STATUS_CHANGE_CONNECTION) {
+               g_print("*** input to connection index.(0 ~ 3)\n");
+
+       } else if (g_conns[g_conn_index].menu_state == CURRENT_STATUS_REQUEST_SESSION) {
                g_print("*** input remote peer id.\n");
 
-       } else if (g_menu_state == CURRENT_STATUS_SEND_LOCAL_DESCRIPTION) {
+       } else if (g_conns[g_conn_index].menu_state == CURRENT_STATUS_SEND_LOCAL_DESCRIPTION) {
                g_print("*** input type of local description to send to the server.(1:offer, 2:answer)\n");
 
        } else {
@@ -1482,26 +1635,26 @@ gboolean timeout_quit_program(void *data)
 
 void reset_menu_state(void)
 {
-       g_menu_state = CURRENT_STATUS_MAINMENU;
+       g_conns[g_conn_index].menu_state = CURRENT_STATUS_MAINMENU;
 }
 
 static void interpret(char *cmd)
 {
        int value;
 
-       switch (g_menu_state) {
+       switch (g_conns[g_conn_index].menu_state) {
        case CURRENT_STATUS_MAINMENU:
                _interpret_main_menu(cmd);
                break;
        case CURRENT_STATUS_ADD_MEDIA_SOURCE: {
                value = atoi(cmd);
-               _webrtc_add_media_source(value - 1);
+               _webrtc_add_media_source(g_conn_index, value - 1);
                reset_menu_state();
                break;
        }
        case CURRENT_STATUS_REMOVE_MEDIA_SOURCE: {
                value = atoi(cmd);
-               _webrtc_remove_media_source(value);
+               _webrtc_remove_media_source(g_conn_index, value);
                reset_menu_state();
                break;
        }
@@ -1510,16 +1663,16 @@ static void interpret(char *cmd)
                static unsigned int media_type;
                value = atoi(cmd);
 
-               switch (g_cnt) {
+               switch (g_conns[g_conn_index].cnt) {
                case 0:
                        id = value;
-                       g_cnt++;
+                       g_conns[g_conn_index].cnt++;
                        break;
                case 1:
                        media_type = value - 1;
-                       _webrtc_get_transceiver_direction(id, media_type);
+                       _webrtc_get_transceiver_direction(g_conn_index, id, media_type);
                        id = media_type = 0;
-                       g_cnt = 0;
+                       g_conns[g_conn_index].cnt = 0;
                        reset_menu_state();
                        break;
                }
@@ -1531,51 +1684,57 @@ static void interpret(char *cmd)
                static unsigned int direction;
                value = atoi(cmd);
 
-               switch (g_cnt) {
+               switch (g_conns[g_conn_index].cnt) {
                case 0:
                        id = value;
-                       g_cnt++;
+                       g_conns[g_conn_index].cnt++;
                        break;
                case 1:
                        media_type = value - 1;
-                       g_cnt++;
+                       g_conns[g_conn_index].cnt++;
                        break;
                case 2:
                        direction =  value - 1;
-                       _webrtc_set_transceiver_direction(id, media_type, direction);
+                       _webrtc_set_transceiver_direction(g_conn_index, id, media_type, direction);
                        id = media_type = direction = 0;
-                       g_cnt = 0;
+                       g_conns[g_conn_index].cnt = 0;
                        reset_menu_state();
                        break;
                }
                break;
        }
+       case CURRENT_STATUS_SET_DISPLAY_TYPE: {
+               value = atoi(cmd);
+               _webrtc_set_display_type(g_conn_index, value - 1);
+               reset_menu_state();
+               break;
+       }
        case CURRENT_STATUS_DATA_CHANNEL_SEND_STRING: {
-               _webrtc_data_channel_send_string(cmd);
+               _webrtc_data_channel_send_string(g_conn_index, cmd);
                reset_menu_state();
                break;
        }
        case CURRENT_STATUS_DATA_CHANNEL_SEND_STRING_AS_BYTES: {
-               _webrtc_data_channel_send_string_as_bytes(cmd);
+               _webrtc_data_channel_send_string_as_bytes(g_conn_index, cmd);
                reset_menu_state();
                break;
        }
        case CURRENT_STATUS_DATA_CHANNEL_SEND_FILE: {
-               _webrtc_data_channel_send_file(cmd);
+               _webrtc_data_channel_send_file(g_conn_index, cmd);
                reset_menu_state();
                break;
        }
        case CURRENT_STATUS_SET_STUN_SERVER: {
-               _webrtc_set_stun_server(cmd);
+               _webrtc_set_stun_server(g_conn_index, cmd);
                reset_menu_state();
                break;
        }
        case CURRENT_STATUS_SET_LOCAL_DESCRIPTION: {
                value = atoi(cmd);
                if (value == 1)
-                       _webrtc_set_local_description(g_offer);
+                       _webrtc_set_local_description(g_conn_index, true);
                else if (value == 2)
-                       _webrtc_set_local_description(g_answer);
+                       _webrtc_set_local_description(g_conn_index, false);
                else
                        g_print("invalid value[%d]\n", value);
 
@@ -1592,18 +1751,27 @@ static void interpret(char *cmd)
                reset_menu_state();
                break;
        }
+       case CURRENT_STATUS_CHANGE_CONNECTION: {
+               value = atoi(cmd);
+               if (value < 0 || value > 3)
+                       g_print("invalid value[%d]", value);
+               else
+                       g_conn_index = value;
+               reset_menu_state();
+               break;
+       }
        case CURRENT_STATUS_REQUEST_SESSION: {
                value = atoi(cmd);
-               _request_session(value);
+               _request_session(g_conn_index, value);
                reset_menu_state();
                break;
        }
        case CURRENT_STATUS_SEND_LOCAL_DESCRIPTION: {
                value = atoi(cmd);
                if (value == 1)
-                       _send_local_description(g_offer);
+                       _send_local_description(g_conn_index, true);
                else if (value == 2)
-                       _send_local_description(g_answer);
+                       _send_local_description(g_conn_index, false);
                else
                        g_print("invalid value[%d]\n", value);
 
@@ -1637,7 +1805,7 @@ int main(int argc, char *argv[])
        g_io_add_watch(stdin_channel, G_IO_IN, (GIOFunc)input, NULL);
 
        displaymenu();
-       memset(&ad, 0x0, sizeof(appdata));
+       memset(&ad, 0x0, sizeof(appdata_s));
        ops.data = &ad;
 
        return appcore_efl_main(PACKAGE, &argc, &argv, &ops);