Add API set for data channel 11/245011/13
authorSangchul Lee <sc11.lee@samsung.com>
Tue, 29 Sep 2020 06:13:34 +0000 (15:13 +0900)
committerSangchul Lee <sc11.lee@samsung.com>
Tue, 20 Oct 2020 00:08:33 +0000 (09:08 +0900)
These correspond to methods and event handlers of the RTCPeerConnection
and RTCDataChannel as below.
 - RTCPeerConnection: createDataChannel(), ondatachannel
 - RTCDataChannel: send(), onopen, onclose, onerror, onmessage

Functions are added as below.
 - webrtc_set[unset]_data_channel_cb()
 - webrtc_create[destroy]_data_channel()
 - webrtc_data_channel_set[unset]_open_cb()
 - webrtc_data_channel_set[unset]_message_cb()
 - webrtc_data_channel_set[unset]_error_cb()
 - webrtc_data_channel_set[unset]_close_cb()
 - webrtc_data_channel_send_string()

Test cases for these functions are added to webrtc_test.

[Version] 0.1.40
[Issue Type] API

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

index 641c612672c0f5c3d654f91f547c066e835ff022..ed575c5a87b01639d5e8ecaf10614b73aa082899 100644 (file)
@@ -43,6 +43,12 @@ extern "C" {
  */
 typedef void *webrtc_h;
 
+/**
+ * @brief WebRTC data channel handle type.
+ * @since_tizen 6.5
+ */
+typedef void *webrtc_data_channel_h;
+
 /**
  * @brief Enumeration for WebRTC error.
  * @since_tizen 6.5
@@ -102,6 +108,16 @@ typedef enum {
        WEBRTC_TRANSCEIVER_DIRECTION_SENDRECV,
 } webrtc_transceiver_direction_e;
 
+/**
+ * @brief Enumeration for WebRTC data channel type.
+ * @since_tizen 6.5
+ */
+typedef enum
+{
+       WEBRTC_DATA_CHANNEL_TYPE_STRING,  /**<  String data */
+       WEBRTC_DATA_CHANNEL_TYPE_BYTES,   /**<  Bytes data */
+} webrtc_data_channel_type_e;
+
 /**
  * @brief Called when an error occurs.
  * @details The following error codes can be received:\n
@@ -155,6 +171,79 @@ typedef void (*webrtc_negotiation_needed_cb)(webrtc_h webrtc, void *user_data);
  */
 typedef void (*webrtc_ice_candidate_cb)(webrtc_h webrtc, const char *candidate, void *user_data);
 
+/**
+ * @brief Called when the data channel is created to the connection by the remote peer.
+ * @since_tizen 6.5
+ * @remarks The @a webrtc is the same object for which the callback was set.\nThe @a webrtc should not be released.
+ * @remarks The @a channel should not be released.
+ * @param[in] channel    WebRTC data channel handle
+ * @param[in] user_data  The user data passed from the callback registration function
+ * @see webrtc_data_channel_set_open_cb()
+ * @see webrtc_data_channel_unset_open_cb()
+ * @see webrtc_data_channel_set_message_cb()
+ * @see webrtc_data_channel_unset_message_cb()
+ * @see webrtc_data_channel_set_error_cb()
+ * @see webrtc_data_channel_unset_error_cb()
+ * @see webrtc_data_channel_set_close_cb()
+ * @see webrtc_data_channel_unset_close_cb()
+ */
+typedef void (*webrtc_data_channel_cb)(webrtc_h webrtc, webrtc_data_channel_h channel, void *user_data);
+
+/**
+ * @brief Called when the data channel's underlying data transport is established.
+ * @since_tizen 6.5
+ * @remarks The @a channel is the same object for which the callback was set.\nThe @a channel should not be released.
+ * @param[in] channel    WebRTC data channel handle
+ * @param[in] user_data  The user data passed from the callback registration function
+ * @see webrtc_data_channel_create()
+ * @see webrtc_data_channel_destroy()
+ * @see webrtc_data_channel_set_open_cb()
+ * @see webrtc_data_channel_unset_open_cb()
+ */
+typedef void (*webrtc_data_channel_open_cb)(webrtc_data_channel_h channel, void *user_data);
+
+/**
+ * @brief Called when a message is received from other peer via the data channel.
+ * @since_tizen 6.5
+ * @remarks The @a channel is the same object for which the callback was set.\nThe @a channel should not be released.
+ * @param[in] channel    WebRTC data channel handle
+ * @param[in] type       The data type
+ * @param[in] message    The messsage from the remote peer
+ * @param[in] user_data  The user data passed from the callback registration function
+ * @see webrtc_data_channel_create()
+ * @see webrtc_data_channel_destroy()
+ * @see webrtc_data_channel_set_message_cb()
+ * @see webrtc_data_channel_unset_message_cb()
+ */
+typedef void (*webrtc_data_channel_message_cb)(webrtc_data_channel_h channel, webrtc_data_channel_type_e type, void *message, void *user_data);
+
+/**
+ * @brief Called when an error occurs on the data channel.
+ * @since_tizen 6.5
+ * @remarks The @a channel is the same object for which the callback was set.\nThe @a channel should not be released.
+ * @param[in] channel    WebRTC data channel handle
+ * @param[in] error      The error code
+ * @param[in] user_data  The user data passed from the callback registration function
+ * @see webrtc_data_channel_create()
+ * @see webrtc_data_channel_destroy()
+ * @see webrtc_data_channel_set_error_cb()
+ * @see webrtc_data_channel_unset_error_cb()
+ */
+typedef void (*webrtc_data_channel_error_cb)(webrtc_data_channel_h channel, webrtc_error_e error, void *user_data);
+
+/**
+ * @brief Called when the data channel has closed down.
+ * @since_tizen 6.5
+ * @remarks The @a channel is the same object for which the callback was set.\nThe @a channel should not be released.
+ * @param[in] channel    WebRTC data channel handle
+ * @param[in] user_data  The user data passed from the callback registration function
+ * @see webrtc_data_channel_create()
+ * @see webrtc_data_channel_destroy()
+ * @see webrtc_data_channel_set_close_cb()
+ * @see webrtc_data_channel_unset_close_cb()
+ */
+typedef void (*webrtc_data_channel_close_cb)(webrtc_data_channel_h channel, void *user_data);
+
 /**
  * @brief Sets a callback function to be invoked when an asynchronous operation error occurs.
  * @since_tizen 6.5
@@ -527,6 +616,198 @@ int webrtc_set_remote_description(webrtc_h webrtc, const char *description);
  */
 int webrtc_add_ice_candidate(webrtc_h webrtc, const char *candidate);
 
+/**
+ * @brief Sets a data channel callback function to be invoked when the data channel is created to the connection by the remote peer.
+ * @since_tizen 6.5
+ * @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_STATE Invalid state
+ * @pre @a webrtc state must be set to #WEBRTC_STATE_IDLE.
+ * @post webrtc_data_channel_cb() will be invoked.
+ * @see webrtc_unset_data_channel_cb()
+ * @see webrtc_data_channel_cb()
+ */
+int webrtc_set_data_channel_cb(webrtc_h webrtc, webrtc_data_channel_cb callback, void *user_data);
+
+/**
+ * @brief Unsets the data channel callback function.
+ * @since_tizen 6.5
+ * @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_data_channel_cb()
+ */
+int webrtc_unset_data_channel_cb(webrtc_h webrtc);
+
+/**
+ * @brief Creates a new data channel which is linked with the remote peer.
+ * @since_tizen 6.5
+ * @param[in] webrtc      WebRTC handle
+ * @param[in] label       Name for the channel
+ * @param[out] channel    Data channel 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
+ * @retval #WEBRTC_ERROR_INVALID_STATE Invalid state
+ * @pre @a webrtc state must be set to #WEBRTC_STATE_IDLE.
+ * @see webrtc_destroy_data_channel()
+ */
+int webrtc_create_data_channel(webrtc_h webrtc, const char *label, webrtc_data_channel_h *channel);
+
+/**
+ * @brief Destroys the data channel.
+ * @since_tizen 6.5
+ * @param[in] channel    Data channel handle
+ * @return @c 0 on success,
+ *         otherwise a negative error value
+ * @retval #WEBRTC_ERROR_NONE    Successful
+ * @retval #WEBRTC_ERROR_INVALID_PARAMETER Invalid parameter
+ * @see webrtc_create_data_channel()
+ */
+int webrtc_destroy_data_channel(webrtc_data_channel_h channel);
+
+/**
+ * @brief Sets a data channel open callback function to be invoked when the data channel's underlying data transport is established.
+ * @since_tizen 6.5
+ * @param[in] channel     Data channel 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
+ * @post webrtc_data_channel_open_cb() will be invoked.
+ * @see webrtc_data_channel_unset_open_cb()
+ * @see webrtc_data_channel_open_cb()
+ */
+int webrtc_data_channel_set_open_cb(webrtc_data_channel_h channel, webrtc_data_channel_open_cb callback, void *user_data);
+
+/**
+ * @brief Unsets the data channel open callback function.
+ * @since_tizen 6.5
+ * @param[in] channel     Data channel 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_data_channel_set_open_cb()
+ */
+int webrtc_data_channel_unset_open_cb(webrtc_data_channel_h channel);
+
+/**
+ * @brief Sets a data channel message callback function to be invoked when a message is received from the remote peer.
+ * @since_tizen 6.5
+ * @param[in] channel     Data channel 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
+ * @post webrtc_data_channel_message_cb() will be invoked.
+ * @see webrtc_data_channel_unset_message_cb()
+ * @see webrtc_data_channel_message_cb()
+ */
+int webrtc_data_channel_set_message_cb(webrtc_data_channel_h channel, webrtc_data_channel_message_cb callback, void *user_data);
+
+/**
+ * @brief Unsets the data channel message callback function.
+ * @since_tizen 6.5
+ * @param[in] channel     Data channel 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_data_channel_set_message_cb()
+ */
+int webrtc_data_channel_unset_message_cb(webrtc_data_channel_h channel);
+
+/**
+ * @brief Sets a data channel error callback function to be invoked when an error occurs on the data channel.
+ * @since_tizen 6.5
+ * @param[in] channel     Data channel 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
+ * @post webrtc_data_channel_error_cb() will be invoked.
+ * @see webrtc_data_channel_unset_error_cb()
+ * @see webrtc_data_channel_error_cb()
+ */
+int webrtc_data_channel_set_error_cb(webrtc_data_channel_h channel, webrtc_data_channel_error_cb callback, void *user_data);
+
+/**
+ * @brief Unsets the data channel error callback function.
+ * @since_tizen 6.5
+ * @param[in] channel     Data channel 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_data_channel_set_error_cb()
+ */
+int webrtc_data_channel_unset_error_cb(webrtc_data_channel_h channel);
+
+/**
+ * @brief Sets a data channel close callback function to be invoked when the data channel has closed down.
+ * @since_tizen 6.5
+ * @param[in] channel     Data channel 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
+ * @post webrtc_data_channel_close_cb() will be invoked.
+ * @see webrtc_data_channel_unset_close_cb()
+ * @see webrtc_data_channel_close_cb()
+ */
+int webrtc_data_channel_set_close_cb(webrtc_data_channel_h channel, webrtc_data_channel_close_cb callback, void *user_data);
+
+/**
+ * @brief Unsets the data channel close callback function.
+ * @since_tizen 6.5
+ * @param[in] channel     Data channel 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_data_channel_set_close_cb()
+ */
+int webrtc_data_channel_unset_close_cb(webrtc_data_channel_h channel);
+
+/**
+ * @brief Sends a string data across the data channel to the remote peer.
+ * @since_tizen 6.5
+ * @param[in] channel     Data channel handle
+ * @param[in] string      String data to send
+ * @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 channel should be opened before calling this function.
+ * @see webrtc_data_channel_open_cb()
+ */
+int webrtc_data_channel_send_string(webrtc_data_channel_h channel, const char *string);
+
 /**
  * @}
  */
index 2406bdc5968da66fbdf6e7933168419f87b8d094..6bff3699946c9fb348ed984257ff66b77ffa623e 100644 (file)
@@ -143,19 +143,24 @@ do { \
        g_free(dot_name); \
 } while (0)
 
-typedef struct _webrtc_ini_s {
-       gboolean generate_dot;
-       gchar **gst_args;
-} webrtc_ini_s;
-
 typedef enum {
        MEDIA_TYPE_AUDIO = 0x01,
        MEDIA_TYPE_VIDEO = 0x02,
 } media_type_e;
 
+typedef enum {
+       DATA_CHANNEL_TYPE_STRING,
+       DATA_CHANNEL_TYPE_BYTES
+} data_channel_type_e;
+
 #define MLINES_IDX_AUDIO 0
 #define MLINES_IDX_VIDEO 1
 
+typedef struct _webrtc_ini_s {
+       gboolean generate_dot;
+       gchar **gst_args;
+} webrtc_ini_s;
+
 typedef struct _webrtc_gst_slot_s {
        unsigned int id;
        GstElement *bin;
@@ -196,12 +201,28 @@ typedef struct _webrtc_s {
 
        GList *signals;
 
+       GHashTable *data_channels;
+
        webrtc_callbacks_s error_cb;
        webrtc_callbacks_s state_changed_cb;
        webrtc_callbacks_s negotiation_needed_cb;
        webrtc_callbacks_s ice_candidate_cb;
+       webrtc_callbacks_s data_channel_cb;
 } webrtc_s;
 
+typedef struct _webrtc_data_channel_s {
+       GMutex mutex;
+
+       GObject *channel;
+
+       GList *signals;
+
+       webrtc_callbacks_s open_cb;
+       webrtc_callbacks_s message_cb;
+       webrtc_callbacks_s error_cb;
+       webrtc_callbacks_s close_cb;
+} webrtc_data_channel_s;
+
 typedef struct _element_info_s {
        const gchar *klass_name;
        GstCaps *src_caps;
@@ -223,7 +244,10 @@ int _remove_media_source(webrtc_s *webrtc, unsigned int source_id);
 int _get_transceiver_direction(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e media_type, webrtc_transceiver_direction_e *direction);
 int _set_transceiver_direction(webrtc_s *webrtc, unsigned int source_id, webrtc_media_type_e media_type, webrtc_transceiver_direction_e direction);
 void _post_state_in_idle(webrtc_s *webrtc, webrtc_state_e new_state);
+void _invoke_state_changed_cb(webrtc_s *webrtc, webrtc_state_e old, webrtc_state_e new);
 
+void _connect_and_append_signal(GList **signals, GObject *obj, const char *sig_name, GCallback cb, gpointer user_data);
+void _disconnect_signal(gpointer data);
 GstElement *_create_element(const char *factory_name, const char *name);
 GstElement *_create_element_from_registry(element_info_s *elem_info);
 int _add_no_target_ghostpad_to_slot(webrtc_gst_slot_s *slot, gboolean is_src, GstPad **new_pad);
@@ -237,6 +261,13 @@ int _webrtcbin_create_offer(webrtc_s *webrtc, char **offer);
 int _webrtcbin_create_answer(webrtc_s *webrtc, char **answer);
 int _webrtcbin_set_session_description(webrtc_s *webrtc, const char *description, gboolean 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);
+
+void _init_data_channels(webrtc_s *webrtc);
+void _destroy_data_channels(webrtc_s *webrtc);
+int _create_data_channel(webrtc_s *webrtc, const char *label, webrtc_data_channel_s **channel);
+int _destroy_data_channel(webrtc_data_channel_s *channel);
+int _data_channel_send_string(webrtc_data_channel_s *channel, const char *string);
 
 #ifdef __cplusplus
 }
index 79f8820642fba3aea514ca406de7f3478b5ccdbb..5544d7542731871c2b93994c429aa57e7068223f 100644 (file)
@@ -1,6 +1,6 @@
 Name:       capi-media-webrtc
 Summary:    A WebRTC library in Tizen Native API
-Version:    0.1.39
+Version:    0.1.40
 Release:    0
 Group:      Multimedia/API
 License:    Apache-2.0
index 0e2637b4c4a9c798a16ba9008911de0adb0fc1d5..0c20169b5172b8cdc03aa1aebea65e39d1064953 100644 (file)
@@ -119,6 +119,7 @@ int webrtc_create(webrtc_h *webrtc)
        _ini_load(_webrtc);
        _gst_init(_webrtc);
        _gst_build_pipeline(_webrtc);
+       _init_data_channels(_webrtc);
 
        _webrtc->pend_state = WEBRTC_STATE_IDLE;
        _webrtc->state = _webrtc->pend_state;
@@ -145,6 +146,7 @@ int webrtc_destroy(webrtc_h webrtc)
        _webrtc->pend_state = WEBRTC_STATE_IDLE;
        _webrtc->state = _webrtc->pend_state;
 
+       _destroy_data_channels(_webrtc);
        _gst_destroy_pipeline(_webrtc);
 
        g_mutex_clear(&_webrtc->desc_mutex);
@@ -199,6 +201,7 @@ int webrtc_stop(webrtc_h webrtc)
        LOG_INFO("webrtc[%p] is stopping", webrtc);
 
        g_hash_table_remove_all(_webrtc->gst.sink_slots);
+       g_hash_table_remove_all(_webrtc->data_channels);
 
        g_mutex_unlock(&_webrtc->mutex);
 
@@ -517,3 +520,257 @@ int webrtc_add_ice_candidate(webrtc_h webrtc, const char *candidate)
 
        return ret;
 }
+
+int webrtc_create_data_channel(webrtc_h webrtc, const char *label, webrtc_data_channel_h *channel)
+{
+       int ret = WEBRTC_ERROR_NONE;
+       webrtc_s *_webrtc = (webrtc_s*)webrtc;
+
+       RET_VAL_IF(_webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+       RET_VAL_IF(label == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "label is NULL");
+       RET_VAL_IF(channel == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "channel 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");
+
+       _gst_pipeline_set_state(_webrtc, GST_STATE_READY);
+
+       ret = _create_data_channel(webrtc, label, (webrtc_data_channel_s **)channel);
+
+       g_mutex_unlock(&_webrtc->mutex);
+
+       return ret;
+}
+
+int webrtc_destroy_data_channel(webrtc_data_channel_h channel)
+{
+       int ret = WEBRTC_ERROR_NONE;
+
+       RET_VAL_IF(channel == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "channel is NULL");
+
+       ret = _destroy_data_channel((webrtc_data_channel_s *)channel);
+
+       return ret;
+}
+
+int webrtc_set_data_channel_cb(webrtc_h webrtc, webrtc_data_channel_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->data_channel_cb.callback = callback;
+       _webrtc->data_channel_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_data_channel_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->data_channel_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->data_channel_cb.callback, _webrtc->data_channel_cb.user_data);
+
+       _webrtc->data_channel_cb.callback = NULL;
+       _webrtc->data_channel_cb.user_data = NULL;
+
+       g_mutex_unlock(&_webrtc->mutex);
+
+       return WEBRTC_ERROR_NONE;
+}
+
+int webrtc_data_channel_set_open_cb(webrtc_data_channel_h channel, webrtc_data_channel_open_cb callback, void *user_data)
+{
+       webrtc_data_channel_s *_channel = (webrtc_data_channel_s*)channel;
+
+       RET_VAL_IF(_channel == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "channel is NULL");
+       RET_VAL_IF(callback == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "callback is NULL");
+
+       g_mutex_lock(&_channel->mutex);
+
+       _channel->open_cb.callback = callback;
+       _channel->open_cb.user_data = user_data;
+
+       LOG_INFO("callback[%p] user_data[%p]", callback, user_data);
+
+       g_mutex_unlock(&_channel->mutex);
+
+       return WEBRTC_ERROR_NONE;
+}
+
+int webrtc_data_channel_unset_open_cb(webrtc_data_channel_h channel)
+{
+       webrtc_data_channel_s *_channel = (webrtc_data_channel_s*)channel;
+
+       RET_VAL_IF(_channel == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "channel is NULL");
+
+       g_mutex_lock(&_channel->mutex);
+
+       RET_VAL_WITH_UNLOCK_IF(_channel->open_cb.callback == NULL, WEBRTC_ERROR_INVALID_OPERATION, &_channel->mutex, "callback was not set");
+
+       LOG_INFO("callback[%p] user_data[%p] is reset to NULL",
+               _channel->open_cb.callback, _channel->open_cb.user_data);
+
+       _channel->open_cb.callback = NULL;
+       _channel->open_cb.user_data = NULL;
+
+       g_mutex_unlock(&_channel->mutex);
+
+       return WEBRTC_ERROR_NONE;
+}
+
+int webrtc_data_channel_set_message_cb(webrtc_data_channel_h channel, webrtc_data_channel_message_cb callback, void *user_data)
+{
+       webrtc_data_channel_s *_channel = (webrtc_data_channel_s*)channel;
+
+       RET_VAL_IF(_channel == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "channel is NULL");
+       RET_VAL_IF(callback == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "callback is NULL");
+
+       g_mutex_lock(&_channel->mutex);
+
+       _channel->message_cb.callback = callback;
+       _channel->message_cb.user_data = user_data;
+
+       LOG_INFO("callback[%p] user_data[%p]", callback, user_data);
+
+       g_mutex_unlock(&_channel->mutex);
+
+       return WEBRTC_ERROR_NONE;
+}
+
+int webrtc_data_channel_unset_message_cb(webrtc_data_channel_h channel)
+{
+       webrtc_data_channel_s *_channel = (webrtc_data_channel_s*)channel;
+
+       RET_VAL_IF(_channel == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "channel is NULL");
+
+       g_mutex_lock(&_channel->mutex);
+
+       RET_VAL_WITH_UNLOCK_IF(_channel->message_cb.callback == NULL, WEBRTC_ERROR_INVALID_OPERATION, &_channel->mutex, "callback was not set");
+
+       LOG_INFO("callback[%p] user_data[%p] is reset to NULL",
+               _channel->message_cb.callback, _channel->message_cb.user_data);
+
+       _channel->message_cb.callback = NULL;
+       _channel->message_cb.user_data = NULL;
+
+       g_mutex_unlock(&_channel->mutex);
+
+       return WEBRTC_ERROR_NONE;
+}
+
+int webrtc_data_channel_set_error_cb(webrtc_data_channel_h channel, webrtc_data_channel_error_cb callback, void *user_data)
+{
+       webrtc_data_channel_s *_channel = (webrtc_data_channel_s*)channel;
+
+       RET_VAL_IF(_channel == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "channel is NULL");
+       RET_VAL_IF(callback == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "callback is NULL");
+
+       g_mutex_lock(&_channel->mutex);
+
+       _channel->error_cb.callback = callback;
+       _channel->error_cb.user_data = user_data;
+
+       LOG_INFO("callback[%p] user_data[%p]", callback, user_data);
+
+       g_mutex_unlock(&_channel->mutex);
+
+       return WEBRTC_ERROR_NONE;
+}
+
+int webrtc_data_channel_unset_error_cb(webrtc_data_channel_h channel)
+{
+       webrtc_data_channel_s *_channel = (webrtc_data_channel_s*)channel;
+
+       RET_VAL_IF(_channel == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "channel is NULL");
+
+       g_mutex_lock(&_channel->mutex);
+
+       RET_VAL_WITH_UNLOCK_IF(_channel->error_cb.callback == NULL, WEBRTC_ERROR_INVALID_OPERATION, &_channel->mutex, "callback was not set");
+
+       LOG_INFO("callback[%p] user_data[%p] is reset to NULL",
+               _channel->error_cb.callback, _channel->error_cb.user_data);
+
+       _channel->error_cb.callback = NULL;
+       _channel->error_cb.user_data = NULL;
+
+       g_mutex_unlock(&_channel->mutex);
+
+       return WEBRTC_ERROR_NONE;
+}
+
+int webrtc_data_channel_set_close_cb(webrtc_data_channel_h channel, webrtc_data_channel_close_cb callback, void *user_data)
+{
+       webrtc_data_channel_s *_channel = (webrtc_data_channel_s*)channel;
+
+       RET_VAL_IF(_channel == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "channel is NULL");
+       RET_VAL_IF(callback == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "callback is NULL");
+
+       g_mutex_lock(&_channel->mutex);
+
+       _channel->close_cb.callback = callback;
+       _channel->close_cb.user_data = user_data;
+
+       LOG_INFO("callback[%p] user_data[%p]", callback, user_data);
+
+       g_mutex_unlock(&_channel->mutex);
+
+       return WEBRTC_ERROR_NONE;
+}
+
+int webrtc_data_channel_unset_close_cb(webrtc_data_channel_h channel)
+{
+       webrtc_data_channel_s *_channel = (webrtc_data_channel_s*)channel;
+
+       RET_VAL_IF(_channel == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "channel is NULL");
+
+       g_mutex_lock(&_channel->mutex);
+
+       RET_VAL_WITH_UNLOCK_IF(_channel->close_cb.callback == NULL, WEBRTC_ERROR_INVALID_OPERATION, &_channel->mutex, "callback was not set");
+
+       LOG_INFO("callback[%p] user_data[%p] is reset to NULL",
+               _channel->close_cb.callback, _channel->close_cb.user_data);
+
+       _channel->close_cb.callback = NULL;
+       _channel->close_cb.user_data = NULL;
+
+       g_mutex_unlock(&_channel->mutex);
+
+       return WEBRTC_ERROR_NONE;
+}
+
+int webrtc_data_channel_send_string(webrtc_data_channel_h channel, const char *string)
+{
+       webrtc_data_channel_s *_channel = (webrtc_data_channel_s*)channel;
+       int ret = WEBRTC_ERROR_NONE;
+
+       RET_VAL_IF(_channel == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "channel is NULL");
+       RET_VAL_IF(string == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "string is NULL");
+       RET_VAL_IF(_channel->channel == NULL, WEBRTC_ERROR_INVALID_OPERATION, "data_channel is NULL");
+
+       g_mutex_lock(&_channel->mutex);
+
+       ret = _data_channel_send_string(channel, string);
+
+       g_mutex_unlock(&_channel->mutex);
+
+       return ret;
+}
\ No newline at end of file
diff --git a/src/webrtc_data_channel.c b/src/webrtc_data_channel.c
new file mode 100644 (file)
index 0000000..be787df
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2020 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"
+
+static void __data_channel_on_open_cb(GObject *data_channel, gpointer user_data)
+{
+       webrtc_data_channel_s *channel = (webrtc_data_channel_s *)user_data;
+
+       RET_IF(channel == NULL, "channel is NULL");
+
+       LOG_DEBUG("data_channel[%s] user_data[%p]", GST_OBJECT_NAME(data_channel), user_data);
+
+       if (channel->open_cb.callback) {
+               LOG_DEBUG(">>> callback[%p] user_data[%p]", channel->open_cb.callback, channel->open_cb.user_data);
+               ((webrtc_data_channel_open_cb)(channel->open_cb.callback))((webrtc_data_channel_h)channel, channel->open_cb.user_data);
+               LOG_DEBUG("<<< end of the callback");
+       }
+}
+
+static void __data_channel_on_message_string_cb(GObject *data_channel, gchar *string, gpointer user_data)
+{
+       webrtc_data_channel_s *channel = (webrtc_data_channel_s *)user_data;
+
+       RET_IF(channel == NULL, "channel is NULL");
+
+       LOG_INFO("data_channel[%s] <<<<< [%s]", GST_OBJECT_NAME(data_channel), string);
+
+       if (channel->message_cb.callback) {
+               LOG_DEBUG(">>> callback[%p] user_data[%p]", channel->message_cb.callback, channel->message_cb.user_data);
+               ((webrtc_data_channel_message_cb)(channel->message_cb.callback))((webrtc_data_channel_h)channel, WEBRTC_DATA_CHANNEL_TYPE_STRING, string, channel->message_cb.user_data);
+               LOG_DEBUG("<<< end of the callback");
+       }
+}
+
+static void __data_channel_on_message_data_cb(GObject *data_channel, GBytes *data, gpointer user_data)
+{
+       webrtc_data_channel_s *channel = (webrtc_data_channel_s *)user_data;
+
+       RET_IF(channel == NULL, "channel is NULL");
+
+       LOG_DEBUG("data_channel[%s] data[%p, size:%u] user_data[%p]", GST_OBJECT_NAME(data_channel), data, g_bytes_get_size(data), user_data);
+
+       if (channel->message_cb.callback) {
+               LOG_DEBUG(">>> callback[%p] user_data[%p]", channel->message_cb.callback, channel->message_cb.user_data);
+               ((webrtc_data_channel_message_cb)(channel->message_cb.callback))((webrtc_data_channel_h)channel, WEBRTC_DATA_CHANNEL_TYPE_BYTES, data, channel->message_cb.user_data);
+               LOG_DEBUG("<<< end of the callback");
+       }
+}
+
+static void __data_channel_on_error_cb(GObject *data_channel, GError *error, gpointer user_data)
+{
+       webrtc_data_channel_s *channel = (webrtc_data_channel_s *)user_data;
+       int err = WEBRTC_ERROR_INVALID_OPERATION;
+
+       RET_IF(channel == NULL, "channel is NULL");
+
+       if (error->domain == GST_RESOURCE_ERROR)
+               err = WEBRTC_ERROR_RESOURCE_FAILED;
+       else if (error->domain == GST_STREAM_ERROR)
+               err = WEBRTC_ERROR_STREAM_FAILED;
+
+       LOG_DEBUG("data_channel[%s] error[0x%x] user_data[%p]", GST_OBJECT_NAME(data_channel), err, user_data);
+
+       if (channel->error_cb.callback) {
+               LOG_DEBUG(">>> callback[%p] user_data[%p]", channel->error_cb.callback, channel->error_cb.user_data);
+               ((webrtc_data_channel_error_cb)(channel->error_cb.callback))((webrtc_data_channel_h)channel, err, channel->error_cb.user_data);
+               LOG_DEBUG("<<< end of the callback");
+       }
+}
+
+static void __data_channel_on_close_cb(GObject *data_channel, gpointer user_data)
+{
+       webrtc_data_channel_s *channel = (webrtc_data_channel_s *)user_data;
+
+       RET_IF(channel == NULL, "channel is NULL");
+
+       LOG_DEBUG("data_channel[%s] user_data[%p]", GST_OBJECT_NAME(data_channel), user_data);
+
+       if (channel->close_cb.callback) {
+               LOG_DEBUG(">>> callback[%p] user_data[%p]", channel->close_cb.callback, channel->close_cb.user_data);
+               ((webrtc_data_channel_close_cb)(channel->close_cb.callback))((webrtc_data_channel_h)channel, channel->close_cb.user_data);
+               LOG_DEBUG("<<< end of the callback");
+       }
+}
+
+static void __invoke_data_channel_cb(webrtc_s *webrtc, webrtc_data_channel_s *channel)
+{
+       RET_IF(webrtc == NULL, "webrtc is NULL");
+       RET_IF(channel == NULL, "channel is NULL");
+
+       LOG_INFO("data_channel[%s]", GST_OBJECT_NAME(channel->channel));
+
+       if (webrtc->data_channel_cb.callback) {
+               LOG_DEBUG(">>> callback[%p] user_data[%p]", webrtc->data_channel_cb.callback, webrtc->data_channel_cb.user_data);
+               ((webrtc_data_channel_cb)(webrtc->data_channel_cb.callback))((webrtc_h)webrtc, channel, webrtc->data_channel_cb.user_data);
+               LOG_DEBUG("<<< end of the callback");
+       }
+}
+
+void _webrtcbin_on_data_channel_cb(GstElement *webrtcbin, GObject *data_channel, gpointer user_data)
+{
+       webrtc_s *webrtc = (webrtc_s *)user_data;
+       webrtc_data_channel_s *channel;
+
+       RET_IF(webrtc == NULL, "webrtc is NULL");
+
+       LOG_DEBUG("data_channel[%s] user_data[%p]", GST_OBJECT_NAME(data_channel), user_data);
+
+       channel = g_new0(webrtc_data_channel_s, 1);
+       g_mutex_init(&channel->mutex);
+       g_mutex_lock(&channel->mutex);
+
+       channel->channel = data_channel;
+
+       g_hash_table_insert(webrtc->data_channels, (gpointer)channel, (gpointer)channel);
+
+       _connect_and_append_signal(&channel->signals, channel->channel, "on-open", G_CALLBACK(__data_channel_on_open_cb), channel);
+       _connect_and_append_signal(&channel->signals, channel->channel, "on-message-string", G_CALLBACK(__data_channel_on_message_string_cb), channel);
+       _connect_and_append_signal(&channel->signals, channel->channel, "on-message-data", G_CALLBACK(__data_channel_on_message_data_cb), channel);
+       _connect_and_append_signal(&channel->signals, channel->channel, "on-error", G_CALLBACK(__data_channel_on_error_cb), channel);
+       _connect_and_append_signal(&channel->signals, channel->channel, "on-close", G_CALLBACK(__data_channel_on_close_cb), channel);
+
+       g_mutex_unlock(&channel->mutex);
+
+       g_mutex_lock(&webrtc->mutex);
+       if (webrtc->state == WEBRTC_STATE_NEGOTIATING) {
+               webrtc_state_e old_state = webrtc->state;
+               webrtc->state = webrtc->pend_state = WEBRTC_STATE_PLAYING;
+               g_mutex_unlock(&webrtc->mutex);
+               _invoke_state_changed_cb(webrtc, old_state, webrtc->state);
+       } else {
+               g_mutex_unlock(&webrtc->mutex);
+       }
+       __invoke_data_channel_cb(webrtc, channel);
+}
+
+int _create_data_channel(webrtc_s *webrtc, const char *label, webrtc_data_channel_s **channel)
+{
+       webrtc_data_channel_s *_channel;
+       GObject *data_channel;
+
+       RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+       RET_VAL_IF(label == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "label is NULL");
+       RET_VAL_IF(channel == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "channel is NULL");
+
+       g_signal_emit_by_name(webrtc->gst.webrtcbin, "create-data-channel", label, NULL, &data_channel);
+       if (!data_channel) {
+               LOG_ERROR("failed to create data channel");
+               return WEBRTC_ERROR_INVALID_OPERATION;
+       }
+
+       LOG_INFO("data channel[%s] is created", GST_OBJECT_NAME(data_channel));
+
+       _channel = g_new0(webrtc_data_channel_s, 1);
+       g_mutex_init(&_channel->mutex);
+       g_mutex_lock(&_channel->mutex);
+
+       _channel->channel = data_channel;
+
+       _connect_and_append_signal(&_channel->signals, data_channel, "on-open", G_CALLBACK(__data_channel_on_open_cb), _channel);
+       _connect_and_append_signal(&_channel->signals, data_channel, "on-message-string", G_CALLBACK(__data_channel_on_message_string_cb), _channel);
+       _connect_and_append_signal(&_channel->signals, data_channel, "on-message-data", G_CALLBACK(__data_channel_on_message_data_cb), _channel);
+       _connect_and_append_signal(&_channel->signals, data_channel, "on-error", G_CALLBACK(__data_channel_on_error_cb), _channel);
+       _connect_and_append_signal(&_channel->signals, data_channel, "on-close", G_CALLBACK(__data_channel_on_close_cb), _channel);
+
+       *channel = _channel;
+
+       g_mutex_unlock(&_channel->mutex);
+
+       return WEBRTC_ERROR_NONE;
+}
+
+int _destroy_data_channel(webrtc_data_channel_s *channel)
+{
+       RET_VAL_IF(channel == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "channel is NULL");
+
+       g_mutex_lock(&channel->mutex);
+
+       channel->open_cb.callback = NULL;
+       channel->message_cb.callback = NULL;
+       channel->error_cb.callback = NULL;
+       channel->close_cb.callback = NULL;
+
+       g_list_free_full(channel->signals, _disconnect_signal);
+       channel->signals = NULL;
+
+       LOG_INFO("data_channel[%s] is destroyed", GST_OBJECT_NAME(channel->channel));
+
+       g_object_unref(channel->channel);
+
+       g_mutex_unlock(&channel->mutex);
+       g_mutex_clear(&channel->mutex);
+
+       g_free(channel);
+
+       return WEBRTC_ERROR_NONE;
+}
+
+static void __data_channel_destroy_cb(gpointer data)
+{
+       webrtc_data_channel_s *data_channel = (webrtc_data_channel_s *)data;
+
+       RET_IF(data_channel == NULL, "data_channel is NULL");
+
+       _destroy_data_channel(data_channel);
+}
+
+void _init_data_channels(webrtc_s *webrtc)
+{
+       RET_IF(webrtc == NULL, "webrtc is NULL");
+       RET_IF(webrtc->data_channels, "data_channels is not NULL");
+
+       webrtc->data_channels = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, __data_channel_destroy_cb);
+}
+
+void _destroy_data_channels(webrtc_s *webrtc)
+{
+       RET_IF(webrtc == NULL, "webrtc is NULL");
+       RET_IF(webrtc->data_channels == NULL, "data_channels is NULL");
+
+       g_hash_table_destroy(webrtc->data_channels);
+       webrtc->data_channels = NULL;
+}
+
+int _data_channel_send_string(webrtc_data_channel_s *channel, const char *string)
+{
+       GstWebRTCDataChannelState state;
+
+       RET_VAL_IF(channel == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "channel is NULL");
+       RET_VAL_IF(string == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "string is NULL");
+       RET_VAL_IF(channel->channel == NULL, WEBRTC_ERROR_INVALID_OPERATION, "data_channel is NULL");
+
+       g_object_get(channel->channel, "ready-state", &state, NULL);
+
+       RET_VAL_IF(state != GST_WEBRTC_DATA_CHANNEL_STATE_OPEN, WEBRTC_ERROR_INVALID_STATE, "channel is not opened");
+
+       g_signal_emit_by_name(channel->channel, "send-string", string);
+
+       LOG_INFO("data_channel[%s] >>>>> [%s]", GST_OBJECT_NAME(channel->channel), string);
+
+       return WEBRTC_ERROR_NONE;
+}
index 410cd3c19c8cbe0bc4cee235b195a7467bee7d2f..5bdcd321c6117b8916bcf1d764977eaa55694185 100644 (file)
@@ -147,7 +147,7 @@ static gboolean __meet_gst_state(webrtc_state_e state, GstState gst_state)
        return FALSE;
 }
 
-static void __invoke_state_changed_cb(webrtc_s *webrtc, webrtc_state_e old, webrtc_state_e new)
+void _invoke_state_changed_cb(webrtc_s *webrtc, webrtc_state_e old, webrtc_state_e new)
 {
        RET_IF(webrtc == NULL, "webrtc is NULL");
 
@@ -234,7 +234,7 @@ static gboolean __bus_watch_cb(GstBus *bus, GstMessage *message, gpointer user_d
                        webrtc_state_e old_state = webrtc->state;
                        webrtc->state = webrtc->pend_state;
                        g_mutex_unlock(&webrtc->mutex);
-                       __invoke_state_changed_cb(webrtc, old_state, webrtc->state);
+                       _invoke_state_changed_cb(webrtc, old_state, webrtc->state);
                        break;
                }
 
@@ -271,7 +271,7 @@ static gboolean __idle_cb(gpointer user_data)
 
        g_mutex_unlock(&webrtc->mutex);
 
-       __invoke_state_changed_cb(webrtc, old_state, webrtc->state);
+       _invoke_state_changed_cb(webrtc, old_state, webrtc->state);
 
        return G_SOURCE_REMOVE;
 }
@@ -443,7 +443,7 @@ 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)
+void _connect_and_append_signal(GList **signals, GObject *obj, const char *sig_name, GCallback cb, gpointer user_data)
 {
        webrtc_signal_s *sig_data;
 
@@ -453,7 +453,7 @@ static void __connect_and_append_signal(GList **signals, GstElement *obj, const
        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->obj = 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));
@@ -462,20 +462,18 @@ static void __connect_and_append_signal(GList **signals, GstElement *obj, const
        }
 
        *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));
+       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)
+void _disconnect_signal(gpointer data)
 {
        webrtc_signal_s *sig_data = (webrtc_signal_s *)data;
 
        RET_IF(sig_data == NULL, "sig_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));
-               }
+       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);
@@ -546,7 +544,7 @@ static void __webrtcbin_peer_connection_state_cb(GstElement *webrtcbin, GParamSp
                        webrtc_state_e old_state = webrtc->state;
                        webrtc->state = webrtc->pend_state = WEBRTC_STATE_PLAYING;
                        g_mutex_unlock(&webrtc->mutex);
-                       __invoke_state_changed_cb(webrtc, old_state, webrtc->state);
+                       _invoke_state_changed_cb(webrtc, old_state, webrtc->state);
                        break;
                }
 
@@ -846,15 +844,16 @@ int _gst_build_pipeline(webrtc_s *webrtc)
                goto error;
        }
 
-       __connect_and_append_signal(&webrtc->signals, webrtc->gst.webrtcbin, "on-negotiation-needed", G_CALLBACK(__webrtcbin_on_negotiation_needed_cb), webrtc);
-       __connect_and_append_signal(&webrtc->signals, webrtc->gst.webrtcbin, "on-ice-candidate", G_CALLBACK(__webrtcbin_on_ice_candidate_cb), webrtc);
-       __connect_and_append_signal(&webrtc->signals, webrtc->gst.webrtcbin, "notify::connection-state", G_CALLBACK(__webrtcbin_peer_connection_state_cb), webrtc);
-       __connect_and_append_signal(&webrtc->signals, webrtc->gst.webrtcbin, "notify::signaling-state", G_CALLBACK(__webrtcbin_signaling_state_cb), webrtc);
-       __connect_and_append_signal(&webrtc->signals, webrtc->gst.webrtcbin, "notify::ice-gathering-state", G_CALLBACK(__webrtcbin_ice_gathering_state_cb), webrtc);
-       __connect_and_append_signal(&webrtc->signals, webrtc->gst.webrtcbin, "notify::ice-connection-state", G_CALLBACK(__webrtcbin_ice_connection_state_cb), webrtc);
-       __connect_and_append_signal(&webrtc->signals, webrtc->gst.webrtcbin, "pad-added", G_CALLBACK(__webrtcbin_pad_added_cb), webrtc);
-       __connect_and_append_signal(&webrtc->signals, webrtc->gst.webrtcbin, "no-more-pads", G_CALLBACK(__webrtcbin_no_more_pads_cb), webrtc);
-       __connect_and_append_signal(&webrtc->signals, webrtc->gst.webrtcbin, "on-new-transceiver", G_CALLBACK(__webrtcbin_on_new_transceiver_cb), webrtc);
+       _connect_and_append_signal(&webrtc->signals, (GObject *)webrtc->gst.webrtcbin, "on-negotiation-needed", G_CALLBACK(__webrtcbin_on_negotiation_needed_cb), webrtc);
+       _connect_and_append_signal(&webrtc->signals, (GObject *)webrtc->gst.webrtcbin, "on-ice-candidate", G_CALLBACK(__webrtcbin_on_ice_candidate_cb), webrtc);
+       _connect_and_append_signal(&webrtc->signals, (GObject *)webrtc->gst.webrtcbin, "notify::connection-state", G_CALLBACK(__webrtcbin_peer_connection_state_cb), webrtc);
+       _connect_and_append_signal(&webrtc->signals, (GObject *)webrtc->gst.webrtcbin, "notify::signaling-state", G_CALLBACK(__webrtcbin_signaling_state_cb), webrtc);
+       _connect_and_append_signal(&webrtc->signals, (GObject *)webrtc->gst.webrtcbin, "notify::ice-gathering-state", G_CALLBACK(__webrtcbin_ice_gathering_state_cb), webrtc);
+       _connect_and_append_signal(&webrtc->signals, (GObject *)webrtc->gst.webrtcbin, "notify::ice-connection-state", G_CALLBACK(__webrtcbin_ice_connection_state_cb), webrtc);
+       _connect_and_append_signal(&webrtc->signals, (GObject *)webrtc->gst.webrtcbin, "pad-added", G_CALLBACK(__webrtcbin_pad_added_cb), webrtc);
+       _connect_and_append_signal(&webrtc->signals, (GObject *)webrtc->gst.webrtcbin, "no-more-pads", G_CALLBACK(__webrtcbin_no_more_pads_cb), webrtc);
+       _connect_and_append_signal(&webrtc->signals, (GObject *)webrtc->gst.webrtcbin, "on-new-transceiver", G_CALLBACK(__webrtcbin_on_new_transceiver_cb), webrtc);
+       _connect_and_append_signal(&webrtc->signals, (GObject *)webrtc->gst.webrtcbin, "on-data-channel", G_CALLBACK(_webrtcbin_on_data_channel_cb), 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));
@@ -893,7 +892,7 @@ void _gst_destroy_pipeline(webrtc_s *webrtc)
                webrtc->gst.bus = NULL;
        }
        if (webrtc->signals) {
-               g_list_free_full(webrtc->signals, __disconnect_signal);
+               g_list_free_full(webrtc->signals, _disconnect_signal);
                webrtc->signals = NULL;
        }
        if (webrtc->gst.webrtcbin) {
index f5b1f4aad72e69dbe5b57b0138626bb4f918645c..b914ed012afe2c5e4363c5178d955eadd1e48765 100644 (file)
@@ -32,6 +32,7 @@
 #define LOG_TAG "WEBRTC_TEST"
 
 #define MAX_STRING_LEN 512
+#define MAX_CHANNEL_LEN 10
 
 enum {
        CURRENT_STATUS_MAINMENU,
@@ -39,6 +40,7 @@ enum {
        CURRENT_STATUS_REMOVE_MEDIA_SOURCE,
        CURRENT_STATUS_GET_TRANSCEIVER_DIRECTION,
        CURRENT_STATUS_SET_TRANSCEIVER_DIRECTION,
+       CURRENT_STATUS_DATA_CHANNEL_SEND_STRING,
        CURRENT_STATUS_SET_STUN_SERVER,
        CURRENT_STATUS_SET_LOCAL_DESCRIPTION,
        CURRENT_STATUS_SET_REMOTE_DESCRIPTION,
@@ -91,6 +93,9 @@ typedef struct {
 
 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;
@@ -238,6 +243,7 @@ static void _webrtc_create()
 static void _webrtc_destroy()
 {
        int ret = WEBRTC_ERROR_NONE;
+       int i;
 
        if (!g_webrtc) {
                g_print("failed to _webrtc_destroy(), g_webrtc is NULL\n");
@@ -250,6 +256,13 @@ static void _webrtc_destroy()
        } else {
                g_print("webrtc_destroy() success\n");
                g_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;
+               }
+               g_channel_index = 0;
        }
 }
 
@@ -272,6 +285,7 @@ static void _webrtc_start()
 static void _webrtc_stop()
 {
        int ret = WEBRTC_ERROR_NONE;
+       int i;
 
        if (!g_webrtc) {
                g_print("failed to _webrtc_stop(), g_webrtc is NULL\n");
@@ -279,10 +293,15 @@ static void _webrtc_stop()
        }
 
        ret = webrtc_stop(g_webrtc);
-       if (ret != WEBRTC_ERROR_NONE)
+       if (ret != WEBRTC_ERROR_NONE) {
                g_print("webrtc_stop() returned [0x%x]\n", ret);
-       else
+       } 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;
+               }
+       }
 }
 
 static void _webrtc_get_state()
@@ -364,6 +383,34 @@ static int __copy_string_arr(gchar *dest_arr, char *string)
        return 0;
 }
 
+static void _webrtc_data_channel_send_string(const char *string)
+{
+       int ret = WEBRTC_ERROR_NONE;
+       int i;
+
+       for (i = 0; i < MAX_CHANNEL_LEN; i++) {
+               if (g_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);
+               if (ret != WEBRTC_ERROR_NONE)
+                       g_print("failed to webrtc_data_channel_send_string(), string[%s]\n", string);
+               else
+                       g_print("webrtc_data_channel_send_string() success, string[%s]\n", string);
+       }
+
+       for (i = 0; i < MAX_CHANNEL_LEN; i++) {
+               if (g_recv_channels[i] == NULL)
+                       continue;
+               g_print("received data_channel[%p], index[%d]: ", g_recv_channels[i], i);
+               ret = webrtc_data_channel_send_string(g_recv_channels[i], string);
+               if (ret != WEBRTC_ERROR_NONE)
+                       g_print("failed to webrtc_data_channel_send_string(), string[%s]\n", string);
+               else
+                       g_print("webrtc_data_channel_send_string() success, string[%s]\n", string);
+       }
+}
+
 static void _webrtc_set_stun_server(char *uri)
 {
        int ret = 0;
@@ -382,6 +429,71 @@ static void _webrtc_set_stun_server(char *uri)
                g_print("webrtc_set_stun_server() success, uri[%s]\n", g_stun_server);
 }
 
+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);
+}
+
+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);
+       if (type == WEBRTC_DATA_CHANNEL_TYPE_STRING)
+               g_print("string message[%s]\n", (char *)message);
+       else if (type == WEBRTC_DATA_CHANNEL_TYPE_BYTES)
+               g_print("bytes message[%p, size:%u]\n", message, g_bytes_get_size(message));
+}
+
+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);
+}
+
+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);
+}
+
+static void __data_channel_cb(webrtc_h webrtc, webrtc_data_channel_h channel, void *user_data)
+{
+       int i;
+
+       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);
+                       return;
+               }
+       }
+       g_print("__data_channel_cb() is called, but g_recv_channels is full\n");
+}
+
+static void _webrtc_set_data_channel_cb()
+{
+       int ret = WEBRTC_ERROR_NONE;
+
+       ret = webrtc_set_data_channel_cb(g_webrtc, __data_channel_cb, g_webrtc);
+       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()
+{
+       int ret = WEBRTC_ERROR_NONE;
+
+       ret = webrtc_unset_data_channel_cb(g_webrtc);
+       if (ret != WEBRTC_ERROR_NONE)
+               g_print("failed to webrtc_unset_data_channel_cb()\n");
+       else
+               g_print("webrtc_unset_data_channel_cb() success\n");
+}
+
 static void __error_cb(webrtc_h webrtc, webrtc_error_e error, webrtc_state_e state, void *user_data)
 {
        g_print("__error_cb() is invoked, webrtc[%p], error[0x%x], state[%d], user_data[%p]\n",
@@ -565,6 +677,54 @@ static void _webrtc_add_ice_candidate(void)
        g_list_foreach(g_ice_candidates, __foreach_ice_candidate, NULL);
 
        g_list_free_full(g_ice_candidates, free);
+
+       g_ice_candidates = NULL;
+}
+
+static void _webrtc_create_data_channel(void)
+{
+       int ret = WEBRTC_ERROR_NONE;
+       gchar *label;
+
+       if (g_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);
+
+       ret = webrtc_create_data_channel(g_webrtc, label, &g_channels[g_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_message_cb(g_channels[g_channel_index], __data_channel_message_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_free(label);
+}
+
+static void _webrtc_destroy_data_channel(void)
+{
+       int ret = WEBRTC_ERROR_NONE;
+       int i;
+
+       for (i = 0; i < MAX_CHANNEL_LEN; i++) {
+               if (g_channels[i] == NULL)
+                       continue;
+               ret = webrtc_destroy_data_channel(g_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_channel_index = 0;
 }
 
 static void _setting_uri(gchar *dest_arr, char *uri)
@@ -839,6 +999,21 @@ void _interpret_main_menu(char *cmd)
                } else if (strncmp(cmd, "td", 2) == 0) {
                        g_menu_state = CURRENT_STATUS_SET_TRANSCEIVER_DIRECTION;
 
+               } else if (strncmp(cmd, "cd", 2) == 0) {
+                       _webrtc_create_data_channel();
+
+               } else if (strncmp(cmd, "dd", 2) == 0) {
+                       _webrtc_destroy_data_channel();
+
+               } else if (strncmp(cmd, "sz", 2) == 0) {
+                       _webrtc_set_data_channel_cb();
+
+               } else if (strncmp(cmd, "uz", 2) == 0) {
+                       _webrtc_unset_data_channel_cb();
+
+               } else if (strncmp(cmd, "zs", 2) == 0) {
+                       g_menu_state = CURRENT_STATUS_DATA_CHANNEL_SEND_STRING;
+
                } else if (strncmp(cmd, "se", 2) == 0) {
                        _webrtc_set_error_cb();
 
@@ -958,6 +1133,10 @@ 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("cd. Create data channel\t");
+       g_print("dd. Destroy data channel\n");
+       g_print("zs. Send string via data channel\n");
+       g_print("------------------------------------- Callbacks -----------------------------------------\n");
        g_print("se. Set error callback\t");
        g_print("ue. Unset error callback\n");
        g_print("sc. Set state changed callback\t");
@@ -966,13 +1145,16 @@ void display_sub_basic()
        g_print("un. Unset negotiation needed callback\n");
        g_print("si. Set ICE candidate callback\t");
        g_print("ui. Unset ICE candidate callback\n");
+       g_print("sz. Set data channel callback\t");
+       g_print("uz. Unset data channel callback\n");
+       g_print("------------------------------------- Negotiation ---------------------------------------\n");
+       g_print("st. Set STUN server\n");
        g_print("co. Create offer\t");
        g_print("ca. Create answer\n");
        g_print("sl. Set local description\t");
        g_print("sr. Set remote description\n");
        g_print("ac. Add ICE candidate\n");
-       g_print("st. Set STUN server\n");
-       g_print("----------------------------------- App. Setting ----------------------------------------\n");
+       g_print("------------------------------------- App. Setting --------------------------------------\n");
        display_setting_status();
        g_print("px. Set proxy URL\n");
        g_print("ss. Set signaling server URL\n");
@@ -1008,6 +1190,9 @@ static void displaymenu()
                else if (g_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) {
+               g_print("*** input string to send.\n");
+
        } else if (g_menu_state == CURRENT_STATUS_SET_STUN_SERVER) {
                g_print("*** input STUN server address.\n");
 
@@ -1116,6 +1301,11 @@ static void interpret(char *cmd)
                }
                break;
        }
+       case CURRENT_STATUS_DATA_CHANNEL_SEND_STRING: {
+               _webrtc_data_channel_send_string(cmd);
+               reset_menu_state();
+               break;
+       }
        case CURRENT_STATUS_SET_STUN_SERVER: {
                _webrtc_set_stun_server(cmd);
                reset_menu_state();