From: Sangchul Lee Date: Tue, 12 Jan 2021 03:53:06 +0000 (+0900) Subject: webrtc_signaling_client: Add internal API set for client of signaling server X-Git-Tag: submit/tizen/20210729.023123~147 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=16688b55af78c989d5830fc788f57f290b441d7f;p=platform%2Fcore%2Fapi%2Fwebrtc.git webrtc_signaling_client: Add internal API set for client of signaling server Functions are added as below. - webrtc_signaling_connect() - webrtc_signaling_request_session() - webrtc_signaling_send_message() - webrtc_signaling_get_id() - webrtc_signaling_disconnect() [Version] 0.1.94 [Issue Type] API Change-Id: I95fe182d70cb6f255abee96a7e99793b3777a269 Signed-off-by: Sangchul Lee --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e07c522..c1f7afa6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,7 @@ INCLUDE_DIRECTORIES(${INC_DIR}) SET(dependents "dlog glib-2.0 gstreamer-1.0 gstreamer-webrtc-1.0 gstreamer-video-1.0 gstreamer-audio-1.0 \ json-glib-1.0 iniparser mm-common mm-display-interface capi-media-tool libtbm libwebsockets \ - cynara-client libsmack") + cynara-client libsmack libsoup-2.4") IF(TIZEN_FEATURE_RESOURCE_MANAGER) SET(dependents "${dependents} mm-resource-manager") ADD_DEFINITIONS("-DTIZEN_FEATURE_RES_MGR") diff --git a/include/webrtc_internal.h b/include/webrtc_internal.h index 1e2c53b9..26cf05ac 100644 --- a/include/webrtc_internal.h +++ b/include/webrtc_internal.h @@ -38,6 +38,39 @@ extern "C" { */ typedef void *webrtc_signaling_server_h; +/** + * @internal + * @brief WebRTC signaling client handle type. + * @since_tizen 6.5 + */ +typedef void *webrtc_signaling_client_h; + +/** + * @internal + * @brief Enumeration for WebRTC signaling message type. + * @since_tizen 6.5 + */ +typedef enum { + SIGNALING_MESSAGE_TYPE_CONNECTED, /**< Connected */ + SIGNALING_MESSAGE_TYPE_DISCONNECTED, /**< Disconnected */ + SIGNALING_MESSAGE_TYPE_SESSION_ESTABLISHED, /**< Session established */ + SIGNALING_MESSAGE_TYPE_SESSION_CLOSED, /**< Session closed */ + SIGNALING_MESSAGE_TYPE_SDP, /**< SDP */ + SIGNALING_MESSAGE_TYPE_ICE_CANDIDATE, /**< ICE candidate */ + SIGNALING_MESSAGE_TYPE_ERROR, /**< Error */ +} webrtc_signaling_message_type_e; + +/** + * @internal + * @brief Called when a message to be handled is sent from the remote peer or the signaling server. + * @since_tizen 6.5 + * @param[in] type The signaling message type + * @param[in] message The message + * @param[in] user_data The user data passed from the callback registration function + * @see webrtc_signaling_connect_server() + */ +typedef void (*webrtc_signaling_message_cb)(webrtc_signaling_message_type_e type, const char *message, void *user_data); + /** * @internal * @brief Set an ecore wayland display to the media track. @@ -121,6 +154,96 @@ int webrtc_signaling_server_stop(webrtc_signaling_server_h server); */ int webrtc_signaling_server_destroy(webrtc_signaling_server_h server); +/** + * @internal + * @brief Connects to a signaling server for private network. + * @since_tizen 6.5 + * @param[in] server_ip The signaling server IP address + * @param[in] port The port number + * @param[in] callback Callback function pointer + * @param[in] user_data The user data to be passed to the callback function + * @param[out] client Signaling client handle + * @return @c 0 on success, + * otherwise a negative error value + * @retval #WEBRTC_ERROR_NONE Successful + * @retval #WEBRTC_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #WEBRTC_ERROR_INVALID_OPERATION Invalid operation + * @see webrtc_signaling_get_id() + * @see webrtc_signaling_request_session() + * @see webrtc_signaling_send_message() + * @see webrtc_signaling_disconnect_server() + */ +int webrtc_signaling_connect(const char *server_ip, int port, webrtc_signaling_message_cb callback, void *user_data, webrtc_signaling_client_h *client); + +/** + * @internal + * @brief Requests session to the peer id. + * @since_tizen 6.5 + * @param[in] client Signaling client handle + * @param[in] peer_id The peer client handle id + * @return @c 0 on success, + * otherwise a negative error value + * @retval #WEBRTC_ERROR_NONE Successful + * @retval #WEBRTC_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #WEBRTC_ERROR_INVALID_OPERATION Invalid operation + * @pre Call webrtc_signaling_connect_server() before calling this function. + * @see webrtc_signaling_get_id() + * @see webrtc_signaling_send_message() + */ +int webrtc_signaling_request_session(webrtc_signaling_client_h client, int peer_id); + +/** + * @internal + * @brief Sends message to the peer client. + * @since_tizen 6.5 + * @param[in] client Signaling client handle + * @param[in] message The message + * @return @c 0 on success, + * otherwise a negative error value + * @retval #WEBRTC_ERROR_NONE Successful + * @retval #WEBRTC_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #WEBRTC_ERROR_INVALID_OPERATION Invalid operation + * @retval #WEBRTC_ERROR_INVALID_STATE Invalid state + * @pre Call webrtc_signaling_connect_server() before calling this function. + * @pre Call webrtc_signaling_request_session() before calling this function. + * @see webrtc_signaling_get_id() + * @see webrtc_signaling_request_session() + */ +int webrtc_signaling_send_message(webrtc_signaling_client_h client, const char *message); + +/** + * @internal + * @brief Gets the id of the signaling client handle. + * @since_tizen 6.5 + * @param[in] client Signaling client handle + * @param[out] id Signaling client handle id + * @return @c 0 on success, + * otherwise a negative error value + * @retval #WEBRTC_ERROR_NONE Successful + * @retval #WEBRTC_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #WEBRTC_ERROR_INVALID_OPERATION Invalid operation + * @pre Call webrtc_signaling_connect_server() before calling this function. + * @see webrtc_signaling_request_session() + * @see webrtc_signaling_send_message() + */ +int webrtc_signaling_get_id(webrtc_signaling_client_h client, int *id); + +/** + * @internal + * @brief Disconnects from the signaling server. + * @since_tizen 6.5 + * @param[in] client Signaling client handle + * @return @c 0 on success, + * otherwise a negative error value + * @retval #WEBRTC_ERROR_NONE Successful + * @retval #WEBRTC_ERROR_INVALID_PARAMETER Invalid parameter + * @pre Call webrtc_signaling_connect_server() before calling this function. + * @see webrtc_signaling_get_id() + * @see webrtc_signaling_request_session() + * @see webrtc_signaling_send_message() + */ +int webrtc_signaling_disconnect(webrtc_signaling_client_h client); + /** * @} */ diff --git a/include/webrtc_private.h b/include/webrtc_private.h index b9583196..6019e5d4 100644 --- a/include/webrtc_private.h +++ b/include/webrtc_private.h @@ -31,6 +31,7 @@ #endif #include #include +#include #ifdef __cplusplus extern "C" { @@ -374,6 +375,14 @@ typedef struct _webrtc_signaling_server_s { GHashTable *clients; } webrtc_signaling_server_s; +typedef struct webrtc_signaling_client_s { + SoupSession *session; + SoupWebsocketConnection *ws_conn; + int id; + + webrtc_callbacks_s message_cb; +} webrtc_signaling_client_s; + int _load_ini(webrtc_s *webrtc); void _unload_ini(webrtc_s *webrtc); ini_item_media_source_s* _ini_get_source_by_type(webrtc_ini_s *ini, webrtc_media_source_type_e type); diff --git a/packaging/capi-media-webrtc.spec b/packaging/capi-media-webrtc.spec index 8947b966..4f72d4d5 100644 --- a/packaging/capi-media-webrtc.spec +++ b/packaging/capi-media-webrtc.spec @@ -1,6 +1,6 @@ Name: capi-media-webrtc Summary: A WebRTC library in Tizen Native API -Version: 0.1.93 +Version: 0.1.94 Release: 0 Group: Multimedia/API License: Apache-2.0 diff --git a/src/webrtc_signaling_client.c b/src/webrtc_signaling_client.c new file mode 100644 index 00000000..db66d0cc --- /dev/null +++ b/src/webrtc_signaling_client.c @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2021 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.h" +#include "webrtc_private.h" +#include "webrtc_internal.h" + +static void __websocket_message_cb(SoupWebsocketConnection *ws_conn, SoupWebsocketDataType type, GBytes *message, gpointer user_data) +{ + gchar *text; + gsize size; + const gchar *data; + webrtc_signaling_client_s *client = (webrtc_signaling_client_s *)user_data; + + RET_IF(client == NULL, "client is NULL"); + RET_IF(client->ws_conn == NULL, "ws_conn is NULL"); + RET_IF(client->ws_conn != ws_conn, "invalid 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); + + LOG_DEBUG("receive message:\n%s", text); + + /* FIXME: assign id and invoke message callback */ + + g_free(text); +} + +static void __close_websocket(webrtc_signaling_client_s *client) +{ + RET_IF(client == NULL, "client is NULL"); + + if (client->ws_conn == NULL) { + LOG_DEBUG("ws_conn is NULL, it might be already closed"); + return; + } + + if (soup_websocket_connection_get_state(client->ws_conn) == SOUP_WEBSOCKET_STATE_OPEN) + soup_websocket_connection_close(client->ws_conn, 1000, NULL); /* 1000 for the normal closure */ + else + g_object_unref(client->ws_conn); + + LOG_DEBUG("client[%p, ws_conn:%p, id:%d] is closed\n", client, client->ws_conn, client->id); + + client->ws_conn = NULL; + client->id = 0; +} + +static void __websocket_closed_cb(SoupWebsocketConnection *ws_conn, gpointer user_data) +{ + webrtc_signaling_client_s *client = (webrtc_signaling_client_s *)user_data; + + RET_IF(client == NULL, "client is NULL"); + RET_IF(client->ws_conn == NULL, "ws_conn is NULL"); + RET_IF(client->ws_conn != ws_conn, "invalid ws_conn"); + + __close_websocket(client); +} + +static void __websocket_connected_cb(SoupSession *session, GAsyncResult *res, void *user_data) +{ + GError *error = NULL; + SoupWebsocketConnection *ws_conn; + webrtc_signaling_client_s *client = (webrtc_signaling_client_s *)user_data; + + RET_IF(client == NULL, "conn is NULL"); + + ws_conn = soup_session_websocket_connect_finish(session, res, &error); + if (!ws_conn) { + LOG_ERROR("failed to soup_session_websocket_connect_finish(), error[%s]", error->message); + g_error_free(error); + return; + } + + g_signal_connect(ws_conn, "closed", G_CALLBACK(__websocket_closed_cb), client); + g_signal_connect(ws_conn, "message", G_CALLBACK(__websocket_message_cb), client); + + client->ws_conn = ws_conn; + + LOG_DEBUG("client[%p, ws_conn:%p, id:%d]", client, client->ws_conn, client->id); +} + +static SoupSession* __connect_websocket(const char *ip, int port, webrtc_signaling_client_s *client) +{ + SoupSession *session; + SoupMessage *message; + const char *http_aliases[] = {"ws", NULL}; + gchar *url; + + RET_VAL_IF(ip == NULL, NULL, "ip is NULL"); + RET_VAL_IF(port <= 0, NULL, "invalid port(%d)", port); + RET_VAL_IF(client == NULL, NULL, "client is NULL"); + + session = soup_session_new_with_options(SOUP_SESSION_HTTP_ALIASES, http_aliases, NULL); + + url = g_strdup_printf("ws://%s:%d", ip, port); + + message = soup_message_new(SOUP_METHOD_GET, url); + + LOG_DEBUG("connecting to signaling server[%s]...\n", url); + + g_free(url); + + soup_session_websocket_connect_async(session, message, NULL, NULL, NULL, + (GAsyncReadyCallback)__websocket_connected_cb, client); + + return session; +} + +int webrtc_signaling_connect(const char *server_ip, int port, webrtc_signaling_message_cb callback, void *user_data, webrtc_signaling_client_h *client) +{ + webrtc_signaling_client_s *_client; + + RET_VAL_IF(server_ip == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "server_ip is NULL"); + RET_VAL_IF(port <= 0, WEBRTC_ERROR_INVALID_PARAMETER, "invalid port(%d)", port); + RET_VAL_IF(callback == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "callback is NULL"); + RET_VAL_IF(client == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "client is NULL"); + + _client = g_new0(webrtc_signaling_client_s, 1); + + _client->message_cb.callback = callback; + _client->message_cb.user_data = user_data; + + _client->session = __connect_websocket(server_ip, port, _client); + if (!_client->session) { + LOG_ERROR("failed_to __connect_websocket()"); + g_free(_client); + return WEBRTC_ERROR_INVALID_OPERATION; + } + + *client = _client; + + LOG_DEBUG("client[%p, session:%p, callback:%p]", _client, _client->session, _client->message_cb.callback); + + return WEBRTC_ERROR_NONE; +} + +int webrtc_signaling_request_session(webrtc_signaling_client_h client, int peer_id) +{ + webrtc_signaling_client_s *_client = (webrtc_signaling_client_s *)client; + gchar *message; + + RET_VAL_IF(_client == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "client is NULL"); + RET_VAL_IF(peer_id <= 0, WEBRTC_ERROR_INVALID_PARAMETER, "invalid peer_id(%d)", peer_id); + RET_VAL_IF(_client->ws_conn == NULL, WEBRTC_ERROR_INVALID_OPERATION, "ws_conn is NULL"); + RET_VAL_IF(soup_websocket_connection_get_state(_client->ws_conn) != SOUP_WEBSOCKET_STATE_OPEN, + WEBRTC_ERROR_INVALID_STATE, "websocket is not opened yet"); + RET_VAL_IF(_client->id == 0, WEBRTC_ERROR_INVALID_STATE, "not ready, id is 0"); + + LOG_DEBUG("request session with %d", peer_id); + + message = g_strdup_printf("REQUEST_SESSION %d", peer_id); + + soup_websocket_connection_send_text(_client->ws_conn, message); + + g_free(message); + + return WEBRTC_ERROR_NONE; +} + +int webrtc_signaling_send_message(webrtc_signaling_client_h client, const char *message) +{ + webrtc_signaling_client_s *_client = (webrtc_signaling_client_s *)client; + + RET_VAL_IF(_client == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "client is NULL"); + RET_VAL_IF(message == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "message is NULL"); + RET_VAL_IF(_client->ws_conn == NULL, WEBRTC_ERROR_INVALID_OPERATION, "ws_conn is NULL"); + RET_VAL_IF(soup_websocket_connection_get_state(_client->ws_conn) != SOUP_WEBSOCKET_STATE_OPEN, + WEBRTC_ERROR_INVALID_STATE, "websocket is not opened yet"); + RET_VAL_IF(_client->id == 0, WEBRTC_ERROR_INVALID_STATE, "not ready, id is 0"); + + LOG_DEBUG("send message:\n%s", message); + + soup_websocket_connection_send_text(_client->ws_conn, message); + + return WEBRTC_ERROR_NONE; +} + +int webrtc_signaling_disconnect(webrtc_signaling_client_h client) +{ + webrtc_signaling_client_s *_client = (webrtc_signaling_client_s *)client; + + RET_VAL_IF(client == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "client is NULL"); + + LOG_DEBUG("client[%p, session:%p, callback:%p]", _client, _client->session, _client->message_cb.callback); + + __close_websocket(_client); + + g_object_unref(_client->session); + g_free(_client); + + return WEBRTC_ERROR_NONE; +} + +int webrtc_signaling_get_id(webrtc_signaling_client_h client, int *id) +{ + webrtc_signaling_client_s *_client = (webrtc_signaling_client_s *)client; + + RET_VAL_IF(_client == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "client is NULL"); + RET_VAL_IF(id == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "id is NULL"); + RET_VAL_IF(_client->ws_conn == NULL, WEBRTC_ERROR_INVALID_OPERATION, "ws_conn is NULL"); + RET_VAL_IF(soup_websocket_connection_get_state(_client->ws_conn) != SOUP_WEBSOCKET_STATE_OPEN, + WEBRTC_ERROR_INVALID_STATE, "websocket is not opened yet"); + RET_VAL_IF(_client->id == 0, WEBRTC_ERROR_INVALID_STATE, "not ready, id is 0"); + + LOG_DEBUG("client[%p, id:%d]", _client, _client->id); + + *id = _client->id; + + return WEBRTC_ERROR_NONE; +} \ No newline at end of file