*/
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
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
*/
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
*/
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);
+
/**
* @}
*/
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;
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;
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);
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
}
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
_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;
_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);
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);
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
--- /dev/null
+/*
+ * 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;
+}
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");
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;
}
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;
}
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;
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));
}
*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);
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;
}
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));
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) {
#define LOG_TAG "WEBRTC_TEST"
#define MAX_STRING_LEN 512
+#define MAX_CHANNEL_LEN 10
enum {
CURRENT_STATUS_MAINMENU,
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,
static appdata ad;
static webrtc_h g_webrtc;
+static webrtc_data_channel_h g_channels[MAX_CHANNEL_LEN];
+static int g_channel_index;
+static webrtc_data_channel_h g_recv_channels[MAX_CHANNEL_LEN];
static char *g_offer;
static char *g_answer;
static char *g_remote_desc;
static void _webrtc_destroy()
{
int ret = WEBRTC_ERROR_NONE;
+ int i;
if (!g_webrtc) {
g_print("failed to _webrtc_destroy(), g_webrtc is NULL\n");
} 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;
}
}
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");
}
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()
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;
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",
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)
} 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();
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");
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");
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");
}
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();