From: Sangchul Lee Date: Tue, 1 Sep 2020 09:29:42 +0000 (+0900) Subject: Add webrtc_set[unset]_negotiation_needed_cb() API X-Git-Tag: submit/tizen/20210729.023123~234 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=9ac536b71611ef488ce7dfc65670a879910e743e;p=platform%2Fcore%2Fapi%2Fwebrtc.git Add webrtc_set[unset]_negotiation_needed_cb() API It corresponds to the negotiationneeded event of RTCPeerConnection. Internal functions regarding signal connection are added. [Version] 0.1.8 [Issue Type] API Change-Id: I14587a3073315d8b2cece0b415d04799747d2ed0 Signed-off-by: Sangchul Lee --- diff --git a/include/webrtc.h b/include/webrtc.h index 2b5717e9..b2867c0f 100644 --- a/include/webrtc.h +++ b/include/webrtc.h @@ -80,6 +80,16 @@ typedef enum { WEBRTC_MEDIA_SOURCE_TYPE_VIDEOTEST } webrtc_media_source_type_e; +/** + * @brief Called when the WebRTC needs session negotiation. + * @since_tizen 6.0 + * @param[in] webrtc WebRTC handle + * @param[in] user_data The user data passed from the callback registration function + * @see webrtc_set_negotiation_needed_cb() + * @see webrtc_unset_negotiation_needed_cb() + */ +typedef void (*webrtc_negotiation_needed_cb)(webrtc_h webrtc, void *user_data); + /** * @brief Creates an instance of WebRTC. * @since_tizen 6.0 @@ -205,6 +215,38 @@ int webrtc_remove_media_source(webrtc_h webrtc, unsigned int source_id); */ int webrtc_set_stun_server(webrtc_h webrtc, const char *stun_server); +/** + * @brief Sets a negotiation needed callback function to be invoked when a change has occurred which requires session negotiation. + * @since_tizen 6.0 + * @param [in] webrtc WebRTC handle + * @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_IDLE. + * @post webrtc_negotiation_needed_cb() will be invoked. + * @see webrtc_unset_negotiation_needed_cb() + * @see webrtc_negotiation_needed_cb() + */ +int webrtc_set_negotiation_needed_cb(webrtc_h webrtc, webrtc_negotiation_needed_cb callback, void *user_data); + +/** + * @brief Unsets the negotiation needed callback function. + * @since_tizen 6.0 + * @param [in] webrtc WebRTC 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_set_negotiation_needed_cb() + */ +int webrtc_unset_negotiation_needed_cb(webrtc_h webrtc); + /** * @} */ diff --git a/include/webrtc_private.h b/include/webrtc_private.h index ddf70b2a..1509645e 100644 --- a/include/webrtc_private.h +++ b/include/webrtc_private.h @@ -122,6 +122,11 @@ typedef struct _webrtc_gst_s { GHashTable *source_slots; } webrtc_gst_s; +typedef struct _webrtc_callbacks { + void *callback; + void *user_data; +} webrtc_callbacks_s; + typedef struct _webrtc_s { webrtc_ini_s ini; @@ -133,6 +138,10 @@ typedef struct _webrtc_s { webrtc_state_e state; webrtc_state_e pend_state; + + GList *signals; + + webrtc_callbacks_s negotiation_needed_cb; } webrtc_s; typedef struct _element_info_s { @@ -141,6 +150,11 @@ typedef struct _element_info_s { GstCaps *sink_caps; } element_info_s; +typedef struct _webrtc_signal_s { + GObject *obj; + gulong signal_id; +} webrtc_signal_s; + int _ini_load(webrtc_s *webrtc); int _gst_init(webrtc_s *webrtc); int _gst_build_pipeline(webrtc_s *webrtc); diff --git a/packaging/capi-media-webrtc.spec b/packaging/capi-media-webrtc.spec index ba7d7be1..5c04bc7f 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.7 +Version: 0.1.8 Release: 0 Group: Multimedia/API License: Apache-2.0 diff --git a/src/webrtc.c b/src/webrtc.c index 84ab7181..335b0622 100644 --- a/src/webrtc.c +++ b/src/webrtc.c @@ -83,8 +83,7 @@ int webrtc_start(webrtc_h webrtc) g_mutex_lock(&_webrtc->mutex); RET_VAL_WITH_UNLOCK_IF(_webrtc->state != WEBRTC_STATE_IDLE, WEBRTC_ERROR_INVALID_STATE, &_webrtc->mutex, "the state should be IDLE"); - - /* Implementation */ + RET_VAL_WITH_UNLOCK_IF(_webrtc->gst.webrtcbin == NULL, WEBRTC_ERROR_INVALID_OPERATION, &_webrtc->mutex, "webrtcbin is NULL"); _gst_pipeline_set_state(webrtc, GST_STATE_PLAYING); _webrtc->pend_state = WEBRTC_STATE_PLAYING; @@ -106,8 +105,6 @@ int webrtc_stop(webrtc_h webrtc) RET_VAL_WITH_UNLOCK_IF(_webrtc->state != WEBRTC_STATE_PLAYING, WEBRTC_ERROR_INVALID_STATE, &_webrtc->mutex, "the state should be PLAYING"); - /* Implementation */ - _gst_pipeline_set_state(webrtc, GST_STATE_READY); _webrtc->pend_state = WEBRTC_STATE_IDLE; @@ -200,3 +197,45 @@ int webrtc_set_stun_server(webrtc_h webrtc, const char *stun_server) return WEBRTC_ERROR_NONE; } +int webrtc_set_negotiation_needed_cb(webrtc_h webrtc, webrtc_negotiation_needed_cb callback, void *user_data) +{ + 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"); + + g_mutex_lock(&_webrtc->mutex); + + RET_VAL_WITH_UNLOCK_IF(_webrtc->state != WEBRTC_STATE_IDLE, WEBRTC_ERROR_INVALID_STATE, &_webrtc->mutex, "the state should be IDLE"); + + _webrtc->negotiation_needed_cb.callback = callback; + _webrtc->negotiation_needed_cb.user_data = user_data; + + LOG_INFO("callback[%p] user_data[%p]", callback, user_data); + + g_mutex_unlock(&_webrtc->mutex); + + return WEBRTC_ERROR_NONE; +} + +int webrtc_unset_negotiation_needed_cb(webrtc_h webrtc) +{ + webrtc_s *_webrtc = (webrtc_s*)webrtc; + + RET_VAL_IF(_webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL"); + + g_mutex_lock(&_webrtc->mutex); + + RET_VAL_WITH_UNLOCK_IF(_webrtc->state != WEBRTC_STATE_IDLE, WEBRTC_ERROR_INVALID_STATE, &_webrtc->mutex, "the state should be IDLE"); + RET_VAL_WITH_UNLOCK_IF(_webrtc->negotiation_needed_cb.callback == NULL, WEBRTC_ERROR_INVALID_OPERATION, &_webrtc->mutex, "callback was not set"); + + LOG_INFO("callback[%p] user_data[%p] is reset to NULL", + _webrtc->negotiation_needed_cb.callback, _webrtc->negotiation_needed_cb.user_data); + + _webrtc->negotiation_needed_cb.callback = NULL; + _webrtc->negotiation_needed_cb.user_data = NULL; + + g_mutex_unlock(&_webrtc->mutex); + + return WEBRTC_ERROR_NONE; +} diff --git a/src/webrtc_private.c b/src/webrtc_private.c index c50b3722..52d8b8fd 100644 --- a/src/webrtc_private.c +++ b/src/webrtc_private.c @@ -312,6 +312,62 @@ int _gst_init(webrtc_s *webrtc) return WEBRTC_ERROR_NONE; } +static void __connect_and_append_signal(GList **signals, GstElement *obj, const char *sig_name, GCallback cb, gpointer user_data) +{ + webrtc_signal_s *sig_data; + + RET_IF(signals == NULL, "sig_list is NULL"); + RET_IF(obj == NULL, "obj is NULL"); + RET_IF(sig_name == NULL, "sig_name is NULL"); + RET_IF(cb == NULL, "cb is NULL"); + + sig_data = (webrtc_signal_s *)g_malloc0(sizeof(webrtc_signal_s)); + sig_data->obj = G_OBJECT(obj); + sig_data->signal_id = g_signal_connect(sig_data->obj, sig_name, cb, user_data); + if (sig_data->signal_id == 0) { + LOG_ERROR("failed to g_signal_connect(), [%s] for object [%s]", sig_name, GST_OBJECT_NAME(obj)); + g_free(sig_data); + return; + } + + *signals = g_list_append(*signals, sig_data); + LOG_DEBUG("signal [%s] with id[%lu] is connected to object [%s].", sig_name, sig_data->signal_id, GST_OBJECT_NAME(sig_data->obj)); +} + +static void __disconnect_signal(gpointer data) +{ + webrtc_signal_s *sig_data = (webrtc_signal_s *)data; + + RET_IF(data == NULL, "data is NULL"); + + if (GST_IS_ELEMENT(sig_data->obj)) { + if (g_signal_handler_is_connected(sig_data->obj, sig_data->signal_id)) { + g_signal_handler_disconnect(sig_data->obj, sig_data->signal_id); + LOG_DEBUG("signal with id[%lu] is disconnected from object [%s].", sig_data->signal_id, GST_OBJECT_NAME(sig_data->obj)); + } + } + + g_free(sig_data); +} + +static void __webrtcbin_on_negotiation_needed(GstElement *webrtcbin, gpointer user_data) +{ + webrtc_s *webrtc = (webrtc_s *)user_data; + + RET_IF(webrtcbin == NULL, "webrtcbin is NULL"); + RET_IF(webrtc == NULL, "webrtc is NULL"); + + if (webrtc->negotiation_needed_cb.callback == NULL) { + LOG_DEBUG("negotiation_needed_cb is NULL, skip it"); + return; + } + + LOG_DEBUG(">>> invoke negotiation_needed_cb[%p], user_data[%p]", + webrtc->negotiation_needed_cb.callback, webrtc->negotiation_needed_cb.user_data); + ((webrtc_negotiation_needed_cb)(webrtc->negotiation_needed_cb.callback))((webrtc_h)webrtc, webrtc->negotiation_needed_cb.user_data); + LOG_DEBUG("<<< end of the callback"); +} + int _gst_build_pipeline(webrtc_s *webrtc) { RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL"); @@ -333,6 +389,7 @@ int _gst_build_pipeline(webrtc_s *webrtc) LOG_ERROR("failed to create webrtcbin"); goto error; } + __connect_and_append_signal(&webrtc->signals, webrtc->gst.webrtcbin, "on-negotiation-needed", G_CALLBACK(__webrtcbin_on_negotiation_needed), webrtc); if (!gst_bin_add(GST_BIN(webrtc->gst.pipeline), webrtc->gst.webrtcbin)) { LOG_ERROR("failed to gst_bin_add(), [%s] -> [%s] pipeline", GST_ELEMENT_NAME(webrtc->gst.webrtcbin), GST_ELEMENT_NAME(webrtc->gst.pipeline)); @@ -365,6 +422,10 @@ void _gst_destroy_pipeline(webrtc_s *webrtc) gst_object_unref(webrtc->gst.bus); webrtc->gst.bus = NULL; } + if (webrtc->signals) { + g_list_free_full(webrtc->signals, __disconnect_signal); + webrtc->signals = NULL; + } if (webrtc->gst.webrtcbin) { gst_bin_remove(GST_BIN(webrtc->gst.pipeline), webrtc->gst.webrtcbin); webrtc->gst.webrtcbin = NULL; @@ -804,4 +865,3 @@ int _remove_media_source(webrtc_s *webrtc, unsigned int source_id) return ret; } - diff --git a/test/webrtc_test.c b/test/webrtc_test.c index 1a56255c..8b4faae8 100644 --- a/test/webrtc_test.c +++ b/test/webrtc_test.c @@ -311,6 +311,33 @@ static void _webrtc_set_stun_server(char *uri) g_print("webrtc_set_stun_server() success, uri[%s]\n", g_stun_server); } +static void __negotiation_needed_cb(webrtc_h webrtc, void *user_data) +{ + g_print("__negotiation_needed_cb() is invoked\n"); +} + +static void _webrtc_set_negotiation_needed_cb() +{ + int ret = WEBRTC_ERROR_NONE; + + ret = webrtc_set_negotiation_needed_cb(g_webrtc, __negotiation_needed_cb, g_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() +{ + int ret = WEBRTC_ERROR_NONE; + + ret = webrtc_unset_negotiation_needed_cb(g_webrtc); + if (ret != WEBRTC_ERROR_NONE) + g_print("failed to webrtc_unset_negotiation_needed_cb()\n"); + else + g_print("webrtc_unset_negotiation_needed_cb() success\n"); +} + static void _setting_signalling_server(char *uri) { int ret = 0; @@ -384,6 +411,12 @@ void _interpret_main_menu(char *cmd) } else if (strncmp(cmd, "px", 2) == 0) { g_menu_state = CURRENT_STATUS_SETTING_PROXY; + } else if (strncmp(cmd, "sn", 2) == 0) { + _webrtc_set_negotiation_needed_cb(); + + } else if (strncmp(cmd, "un", 2) == 0) { + _webrtc_unset_negotiation_needed_cb(); + } else { g_print("unknown menu \n"); } @@ -403,10 +436,12 @@ void display_sub_basic() g_print("d. Destroy\n"); g_print("s. Start\t"); g_print("t. Stop\n"); + g_print("g. Get state\n"); g_print("a. Add media source\t"); g_print("r. Remove media source\n"); + g_print("sn. Set negotiation needed callback\t"); + g_print("un. Unset negotiation needed callback\n"); g_print("st. Set STUN server\n"); - g_print("g. Get state\n"); g_print("----------------------------------- App. Setting ----------------------------------------\n"); g_print("ss. Signalling server\n"); g_print("px. Proxy\n");