From 641ec03cb6d36e8d58e347197d6c559d3d8b1826 Mon Sep 17 00:00:00 2001 From: Sangchul Lee Date: Fri, 27 Aug 2021 18:27:18 +0900 Subject: [PATCH] Add new asynchronous API to create offer/answer Functions are added as below. - webrtc_create_offer_async() - webrtc_create_answer_async() [Version] 0.2.91 [Issue Type] API Change-Id: I5641f98fcd272ddd52f5173c048a9db3a94a9222 Signed-off-by: Sangchul Lee --- include/webrtc.h | 72 ++++++++++++++++++++++++++++ include/webrtc_private.h | 1 + packaging/capi-media-webrtc.spec | 2 +- src/webrtc.c | 31 ++++++++++++ src/webrtc_private.c | 81 ++++++++++++++++++++++++++++---- 5 files changed, 177 insertions(+), 10 deletions(-) diff --git a/include/webrtc.h b/include/webrtc.h index a0115f42..f07aae62 100644 --- a/include/webrtc.h +++ b/include/webrtc.h @@ -297,6 +297,24 @@ typedef void (*webrtc_error_cb)(webrtc_h webrtc, webrtc_error_e error, webrtc_st */ typedef void (*webrtc_state_changed_cb)(webrtc_h webrtc, webrtc_state_e previous, webrtc_state_e current, void *user_data); +/** + * @brief Called when the session description is created. + * @since_tizen 6.5 + * @remarks The @a webrtc is the same object for which the callback was set.\n + * The @a webrtc should not be released.\n + * @a description is a JSON string.\n + * It will be {"sdp":{"type":"offer or answer","sdp":"..."}}. + * @param[in] webrtc WebRTC handle + * @param[in] description The session description + * @param[in] user_data The user data passed from the callback registration function + * @post @a description must be set as a local description by calling webrtc_set_local_description() + * @post @a description must be sent to the remote peer via the signaling channel. + * @see webrtc_create_offer_async() + * @see webrtc_create_answer_async() + * @see webrtc_set_local_description() + */ +typedef void (*webrtc_session_description_created_cb)(webrtc_h webrtc, const char *description, void *user_data); + /** * @brief Called when the WebRTC peer connection state is changed. * @since_tizen 6.5 @@ -670,6 +688,7 @@ int webrtc_destroy(webrtc_h webrtc); * @see webrtc_state_changed_cb() * @see webrtc_get_state() * @see webrtc_create_offer() + * @see webrtc_create_offer_async() */ int webrtc_set_state_changed_cb(webrtc_h webrtc, webrtc_state_changed_cb callback, void *user_data); @@ -1701,6 +1720,57 @@ int webrtc_create_offer(webrtc_h webrtc, bundle *options, char **offer); */ int webrtc_create_answer(webrtc_h webrtc, bundle *options, char **answer); +/** + * @brief Creates SDP offer asynchronously to start a new WebRTC connection to a remote peer. + * @since_tizen 6.5 + * @remarks The registered callback will be invoked in the main thread.\n + * The @a options currently has no effect. + * @param[in] webrtc WebRTC handle + * @param[in] options Configuration options for the offer (optional, this can be NULL) + * @param[in] callback Callback function pointer + * @param[in] user_data The user data to be passed to the callback function + * @return @c 0 on success, + * otherwise a negative error value + * @retval #WEBRTC_ERROR_NONE Successful + * @retval #WEBRTC_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #WEBRTC_ERROR_INVALID_OPERATION Invalid operation + * @retval #WEBRTC_ERROR_INVALID_STATE Invalid state + * @pre @a webrtc state must be set to #WEBRTC_STATE_NEGOTIATING. + * @post webrtc_session_description_created_cb() will be invoked. + * @see webrtc_state_changed_cb() + * @see webrtc_negotiation_needed_cb() + * @see webrtc_set_local_description() + * @see webrtc_session_description_created_cb() + */ +int webrtc_create_offer_async(webrtc_h webrtc, bundle *options, webrtc_session_description_created_cb callback, void *user_data); + +/** + * @brief Creates SDP answer asynchronously to an offer received from a remote peer during the negotiation of a WebRTC connection. + * @since_tizen 6.5 + * @remarks The registered callback will be invoked in the main thread.\n + * The @a options currently has no effect. + * @param[in] webrtc WebRTC handle + * @param[in] options Configuration options for the answer (optional, this can be NULL) + * @param[in] callback Callback function pointer + * @param[in] user_data The user data to be passed to the callback function + * @return @c 0 on success, + * otherwise a negative error value + * @retval #WEBRTC_ERROR_NONE Successful + * @retval #WEBRTC_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #WEBRTC_ERROR_INVALID_OPERATION Invalid operation + * @retval #WEBRTC_ERROR_INVALID_STATE Invalid state + * @pre @a webrtc state must be set to #WEBRTC_STATE_NEGOTIATING. + * @pre The remote SDP offer must be set by calling webrtc_set_remote_description(). + * @pre The signaling state must be set to #WEBRTC_SIGNALING_STATE_HAVE_REMOTE_OFFER. + * @post webrtc_session_description_created_cb() will be invoked. + * @see webrtc_set_remote_description() + * @see webrtc_set_local_description() + * @see webrtc_get_signaling_state() + * @see webrtc_set_signaling_state_change_cb() + * @see webrtc_session_description_created_cb() + */ +int webrtc_create_answer_async(webrtc_h webrtc, bundle *options, webrtc_session_description_created_cb callback, void *user_data); + /** * @brief Sets the session description for a local peer associated with a WebRTC connection. * @since_tizen 6.5 @@ -1716,7 +1786,9 @@ int webrtc_create_answer(webrtc_h webrtc, bundle *options, char **answer); * @retval #WEBRTC_ERROR_INVALID_STATE Invalid state * @pre @a webrtc state must be set to #WEBRTC_STATE_NEGOTIATING. * @see webrtc_create_offer() + * @see webrtc_create_offer_async() * @see webrtc_create_answer() + * @see webrtc_create_answer_async() * @see webrtc_set_signaling_state_change_cb() * @see webrtc_get_signaling_state() */ diff --git a/include/webrtc_private.h b/include/webrtc_private.h index 921f6058..9dd72b2b 100644 --- a/include/webrtc_private.h +++ b/include/webrtc_private.h @@ -619,6 +619,7 @@ void *_get_unused_tbm_bo(webrtc_tbm_s *tbm, unsigned int timeout_sec); void _release_tbm_bo(webrtc_tbm_s *tbm, void *bo); int _webrtcbin_create_session_description(webrtc_s *webrtc, bool is_offer, char **desc); +int _webrtcbin_create_session_description_async(webrtc_s *webrtc, bool is_offer, webrtc_session_description_created_cb callback, void *user_data); int _webrtcbin_set_session_description(webrtc_s *webrtc, const char *description, bool is_remote); int _webrtcbin_add_ice_candidate(webrtc_s *webrtc, const char *candidate); void _webrtcbin_on_data_channel_cb(GstElement *webrtcbin, GObject *data_channel, gpointer user_data); diff --git a/packaging/capi-media-webrtc.spec b/packaging/capi-media-webrtc.spec index 37249099..aa4de3bf 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.2.90 +Version: 0.2.91 Release: 0 Group: Multimedia/API License: Apache-2.0 diff --git a/src/webrtc.c b/src/webrtc.c index 98a03f2c..c0123e64 100644 --- a/src/webrtc.c +++ b/src/webrtc.c @@ -1398,6 +1398,37 @@ int webrtc_create_answer(webrtc_h webrtc, bundle *options, char **answer) return _webrtcbin_create_session_description(webrtc, false, answer); } +int webrtc_create_offer_async(webrtc_h webrtc, bundle *options, webrtc_session_description_created_cb callback, void *user_data) +{ + g_autoptr(GMutexLocker) locker = NULL; + webrtc_s *_webrtc = (webrtc_s*)webrtc; + + RET_VAL_IF(_webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL"); + RET_VAL_IF(callback == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "callback is NULL"); + + locker = g_mutex_locker_new(&_webrtc->mutex); + + RET_VAL_IF(_webrtc->state != WEBRTC_STATE_NEGOTIATING, WEBRTC_ERROR_INVALID_STATE, "the state should be NEGOTIATING"); + + return _webrtcbin_create_session_description_async(webrtc, true, callback, user_data); +} + +int webrtc_create_answer_async(webrtc_h webrtc, bundle *options, webrtc_session_description_created_cb callback, void *user_data) +{ + g_autoptr(GMutexLocker) locker = NULL; + webrtc_s *_webrtc = (webrtc_s*)webrtc; + + RET_VAL_IF(_webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL"); + RET_VAL_IF(callback == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "callback is NULL"); + + locker = g_mutex_locker_new(&_webrtc->mutex); + + RET_VAL_IF(_webrtc->state != WEBRTC_STATE_NEGOTIATING, WEBRTC_ERROR_INVALID_STATE, "the state should be NEGOTIATING"); + RET_VAL_IF(!_webrtcbin_have_remote_offer(_webrtc), WEBRTC_ERROR_INVALID_STATE, "remote offer should be set"); + + return _webrtcbin_create_session_description_async(webrtc, false, callback, user_data); +} + int webrtc_set_local_description(webrtc_h webrtc, const char *description) { g_autoptr(GMutexLocker) locker = NULL; diff --git a/src/webrtc_private.c b/src/webrtc_private.c index e9471bfd..bda78f9e 100644 --- a/src/webrtc_private.c +++ b/src/webrtc_private.c @@ -409,6 +409,12 @@ typedef struct _idle_userdata { } new; } idle_userdata_s; +typedef struct _description_created_userdata { + webrtc_s *webrtc; + webrtc_session_description_created_cb callback; + void *user_data; +} description_created_userdata; + static gboolean __idle_cb(gpointer user_data) { idle_userdata_s *data = (idle_userdata_s*)user_data; @@ -1362,39 +1368,71 @@ static void __update_session_description(GstPromise *promise, bool is_offer, gpo static void __offer_created_cb(GstPromise *promise, gpointer user_data) { - webrtc_s *webrtc = (webrtc_s *)user_data; + description_created_userdata *data = (description_created_userdata *)user_data; + webrtc_s *webrtc; - RET_IF(promise == NULL, "promise is NULL"); - RET_IF(webrtc == NULL, "webrtc is NULL"); + RET_IF(data == NULL, "data is NULL"); + + if (!(webrtc = data->webrtc)) { + LOG_ERROR("webrtc is NULL"); + g_free(data); + return; + } LOG_DEBUG_ENTER(); __update_session_description(promise, true, webrtc); + if (data->callback) { + LOG_DEBUG(">>> callback[%p], user_data[%p]", data->callback, data->user_data); + ((webrtc_session_description_created_cb)(data->callback))((webrtc_h)webrtc, webrtc->desc_offer, data->user_data); + LOG_DEBUG("<<< end of the callback"); + g_mutex_unlock(&webrtc->desc_mutex); - g_cond_signal(&webrtc->desc_cond); + } else { + g_cond_signal(&webrtc->desc_cond); + } LOG_DEBUG_LEAVE(); + + g_free(data); } static void __answer_created_cb(GstPromise *promise, gpointer user_data) { - webrtc_s *webrtc = (webrtc_s *)user_data; + description_created_userdata *data = (description_created_userdata *)user_data; + webrtc_s *webrtc; - RET_IF(promise == NULL, "promise is NULL"); - RET_IF(webrtc == NULL, "webrtc is NULL"); + RET_IF(data == NULL, "data is NULL"); + + if (!(webrtc = data->webrtc)) { + LOG_ERROR("webrtc is NULL"); + g_free(data); + return; + } LOG_DEBUG_ENTER(); __update_session_description(promise, false, webrtc); - g_cond_signal(&webrtc->desc_cond); + if (data->callback) { + LOG_DEBUG(">>> callback[%p], user_data[%p]", data->callback, data->user_data); + ((webrtc_session_description_created_cb)(data->callback))((webrtc_h)webrtc, webrtc->desc_answer, data->user_data); + LOG_DEBUG("<<< end of the callback"); + g_mutex_unlock(&webrtc->desc_mutex); + + } else { + g_cond_signal(&webrtc->desc_cond); + } LOG_DEBUG_LEAVE(); + + g_free(data); } int _webrtcbin_create_session_description(webrtc_s *webrtc, bool is_offer, char **desc) { GstPromise *promise; + description_created_userdata *data; gint64 end_time; RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL"); @@ -1403,7 +1441,10 @@ int _webrtcbin_create_session_description(webrtc_s *webrtc, bool is_offer, char g_mutex_lock(&webrtc->desc_mutex); - promise = gst_promise_new_with_change_func(is_offer ? __offer_created_cb : __answer_created_cb, webrtc, NULL); + data = g_new0(description_created_userdata, 1); + data->webrtc = webrtc; + + promise = gst_promise_new_with_change_func(is_offer ? __offer_created_cb : __answer_created_cb, data, NULL); g_signal_emit_by_name(G_OBJECT(webrtc->gst.webrtcbin), is_offer ? "create-offer" : "create-answer", NULL, promise); end_time = g_get_monotonic_time() + 30 * G_TIME_SPAN_SECOND; @@ -1422,6 +1463,28 @@ int _webrtcbin_create_session_description(webrtc_s *webrtc, bool is_offer, char return WEBRTC_ERROR_NONE; } +int _webrtcbin_create_session_description_async(webrtc_s *webrtc, bool is_offer, webrtc_session_description_created_cb callback, void *user_data) +{ + GstPromise *promise; + description_created_userdata *data; + + RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL"); + RET_VAL_IF(callback == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "callback is NULL"); + RET_VAL_IF(webrtc->gst.webrtcbin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "webrtcbin is NULL"); + + g_mutex_lock(&webrtc->desc_mutex); + + data = g_new0(description_created_userdata, 1); + data->webrtc = webrtc; + data->callback = callback; + data->user_data = user_data; + + promise = gst_promise_new_with_change_func(is_offer ? __offer_created_cb : __answer_created_cb, data, NULL); + g_signal_emit_by_name(G_OBJECT(webrtc->gst.webrtcbin), is_offer ? "create-offer" : "create-answer", NULL, promise); + + return WEBRTC_ERROR_NONE; +} + /* Use g_free() to release the sdp and type parameter. */ static int __get_sdp_from_description(const char *description, gchar **sdp, gchar **type) { -- 2.34.1