*/
int webrtc_unset_negotiation_needed_cb(webrtc_h webrtc);
+/**
+ * @brief Creates SDP offer to start a new WebRTC connection to a remote peer.
+ * @since_tizen 6.0
+ * @remarks The @a offer should be released using free().
+ * @param [in] webrtc WebRTC handle
+ * @param [out] offer SDP offer
+ * @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_PLAYING.
+ * @see webrtc_negotiation_needed_cb()
+ */
+int webrtc_create_offer(webrtc_h webrtc, char **offer);
+
+/**
+ * @brief Creates SDP answer to an offer received from a remote peer during the negotiation of a WebRTC connection.
+ * @since_tizen 6.0
+ * @remarks The @a answer should be released using free().
+ * @param [in] webrtc WebRTC handle
+ * @param [out] answer SDP answer
+ * @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_PLAYING.
+ */
+int webrtc_create_answer(webrtc_h webrtc, char **answer);
+
/**
* @}
*/
g_mutex_init(&_webrtc->mutex);
g_mutex_lock(&_webrtc->mutex);
+ g_mutex_init(&_webrtc->desc_mutex);
+ g_cond_init(&_webrtc->desc_cond);
+
_ini_load(_webrtc);
_gst_init(_webrtc);
_gst_build_pipeline(_webrtc);
_gst_destroy_pipeline(_webrtc);
+ g_mutex_clear(&_webrtc->desc_mutex);
+ g_cond_clear(&_webrtc->desc_cond);
+
g_mutex_unlock(&_webrtc->mutex);
g_mutex_clear(&_webrtc->mutex);
return WEBRTC_ERROR_NONE;
}
+
+int webrtc_create_offer(webrtc_h webrtc, char **offer)
+{
+ 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(offer == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "offer is NULL");
+
+ g_mutex_lock(&_webrtc->mutex);
+
+ RET_VAL_WITH_UNLOCK_IF(_webrtc->state != WEBRTC_STATE_PLAYING, WEBRTC_ERROR_INVALID_STATE, &_webrtc->mutex, "the state should be PLAYING");
+
+ LOG_INFO("offer[%p]", offer);
+
+ ret = _webrtcbin_create_offer(_webrtc, offer);
+
+ g_mutex_unlock(&_webrtc->mutex);
+
+ return ret;
+}
+
+int webrtc_create_answer(webrtc_h webrtc, char **answer)
+{
+ 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(answer == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "answer is NULL");
+
+ g_mutex_lock(&_webrtc->mutex);
+
+ RET_VAL_WITH_UNLOCK_IF(_webrtc->state != WEBRTC_STATE_PLAYING, WEBRTC_ERROR_INVALID_STATE, &_webrtc->mutex, "the state should be PLAYING");
+ /* FIXME: remote description should be set before this API */
+
+ LOG_INFO("answer[%p]", answer);
+
+ ret = _webrtcbin_create_answer(_webrtc, answer);
+
+ g_mutex_unlock(&_webrtc->mutex);
+
+ return ret;
+}
\ No newline at end of file
* limitations under the License.
*/
+#include <json-glib/json-glib.h>
#include "webrtc.h"
#include "webrtc_private.h"
} \
} while (0)
+/* Use g_free() to free the return value. */
+static gchar* __get_string_from_json_object(JsonObject *object)
+{
+ JsonNode *root;
+ JsonGenerator *generator;
+ gchar *text;
+
+ RET_VAL_IF(object == NULL, NULL, "object is NULL");
+
+ root = json_node_init_object(json_node_alloc(), object);
+ generator = json_generator_new();
+ json_generator_set_root(generator, root);
+ text = json_generator_to_data(generator, NULL);
+
+ g_object_unref(generator);
+ json_node_free(root);
+
+ return text;
+}
+
+static gchar* __make_sdp_message(GstWebRTCSessionDescription *desc)
+{
+ gchar *text;
+ JsonObject *msg, *sdp;
+
+ text = gst_sdp_message_as_text(desc->sdp);
+ sdp = json_object_new();
+
+ if (desc->type == GST_WEBRTC_SDP_TYPE_OFFER) {
+ LOG_INFO("making offer message:\n%s", text);
+ json_object_set_string_member(sdp, "type", "offer");
+ } else if (desc->type == GST_WEBRTC_SDP_TYPE_ANSWER) {
+ LOG_INFO("making answer message:\n%s", text);
+ json_object_set_string_member(sdp, "type", "answer");
+ } else {
+ LOG_ERROR("invalid description type");
+ return NULL;
+ }
+
+ json_object_set_string_member(sdp, "sdp", text);
+ g_free(text);
+
+ msg = json_object_new();
+ json_object_set_object_member(msg, "sdp", sdp);
+
+ text = __get_string_from_json_object(msg);
+
+ json_object_unref(msg);
+
+ return text;
+}
+
static gboolean __meet_gst_state(webrtc_state_e state, GstState gst_state)
{
if (state == WEBRTC_STATE_IDLE && gst_state == GST_STATE_READY)
g_free(webrtc->stun_server_url);
webrtc->stun_server_url = NULL;
}
+ if (webrtc->desc_offer) {
+ g_free(webrtc->desc_offer);
+ webrtc->desc_offer = NULL;
+ }
+ if (webrtc->desc_answer) {
+ g_free(webrtc->desc_answer);
+ webrtc->desc_answer = NULL;
+ }
}
int _gst_pipeline_set_state(webrtc_s *webrtc, GstState state)
return ret;
}
+
+void _webrtcbin_on_negotiation_needed(GstElement *webrtcbin, gpointer user_data)
+{
+ webrtc_s *webrtc = (webrtc_s *)user_data;
+
+ RET_IF(webrtcbin == NULL, "webrtcbin is NULL");
+ RET_IF(webrtc == NULL, "webrtc is NULL");
+
+ if (webrtc->negotiation_needed_cb.callback == NULL) {
+ LOG_DEBUG("negotiation_needed_cb is NULL, skip it");
+ return;
+ }
+
+ LOG_DEBUG(">>> invoke negotiation_needed_cb[%p], user_data[%p]",
+ webrtc->negotiation_needed_cb.callback, webrtc->negotiation_needed_cb.user_data);
+ ((webrtc_negotiation_needed_cb)(webrtc->negotiation_needed_cb.callback))((webrtc_h)webrtc, webrtc->negotiation_needed_cb.user_data);
+ LOG_DEBUG("<<< end of the callback");
+}
+
+static void __update_session_description(GstPromise *promise, gboolean is_offer, gpointer user_data)
+{
+ GstWebRTCSessionDescription *desc = NULL;
+ const GstStructure *reply;
+ webrtc_s *webrtc = (webrtc_s *)user_data;
+ gchar *sdp_msg;
+
+ RET_IF(promise == NULL, "promise is NULL");
+ RET_IF(webrtc == NULL, "webrtc is NULL");
+ RET_IF(gst_promise_wait(promise) != GST_PROMISE_RESULT_REPLIED, "promise is not for replied result");
+
+ reply = gst_promise_get_reply(promise);
+ gst_structure_get(reply, is_offer ? "offer" : "answer", GST_TYPE_WEBRTC_SESSION_DESCRIPTION, &desc, NULL);
+ gst_promise_unref(promise);
+
+ sdp_msg = __make_sdp_message(desc);
+ gst_webrtc_session_description_free(desc);
+ if (!sdp_msg) {
+ LOG_ERROR("sdp_msg is NULL");
+ return;
+ }
+
+ if (is_offer) {
+ g_free(webrtc->desc_offer);
+ webrtc->desc_offer = sdp_msg;
+ } else {
+ g_free(webrtc->desc_answer);
+ webrtc->desc_answer = sdp_msg;
+ }
+}
+
+static void __offer_created_cb(GstPromise *promise, gpointer user_data)
+{
+ webrtc_s *webrtc = (webrtc_s *)user_data;
+
+ RET_IF(promise == NULL, "promise is NULL");
+ RET_IF(webrtc == NULL, "webrtc is NULL");
+
+ LOG_DEBUG_ENTER();
+
+ __update_session_description(promise, TRUE, webrtc);
+
+ g_cond_signal(&webrtc->desc_cond);
+
+ LOG_DEBUG_LEAVE();
+}
+
+static void __answer_created_cb(GstPromise *promise, gpointer user_data)
+{
+ webrtc_s *webrtc = (webrtc_s *)user_data;
+
+ RET_IF(promise == NULL, "promise is NULL");
+ RET_IF(webrtc == NULL, "webrtc is NULL");
+
+ LOG_DEBUG_ENTER();
+
+ __update_session_description(promise, FALSE, webrtc);
+
+ g_cond_signal(&webrtc->desc_cond);
+
+ LOG_DEBUG_LEAVE();
+}
+
+static int _create_session_description(webrtc_s *webrtc, gboolean is_offer, char **desc)
+{
+ GstPromise *promise;
+ gint64 end_time;
+
+ RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+ RET_VAL_IF(desc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "desc is NULL");
+ RET_VAL_IF(webrtc->gst.webrtcbin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "webrtcbin is NULL");
+
+ g_mutex_lock(&webrtc->desc_mutex);
+
+ promise = gst_promise_new_with_change_func(is_offer ? __offer_created_cb : __answer_created_cb, webrtc, NULL);
+ g_signal_emit_by_name(G_OBJECT(webrtc->gst.webrtcbin), is_offer ? "create-offer" : "create-answer", NULL, promise);
+
+ end_time = g_get_monotonic_time() + 10 * G_TIME_SPAN_SECOND;
+ if (!g_cond_wait_until(&webrtc->desc_cond, &webrtc->desc_mutex, end_time)) {
+ g_mutex_unlock(&webrtc->desc_mutex);
+ LOG_ERROR("timeout of g_cond_wait_until()");
+ return WEBRTC_ERROR_INVALID_OPERATION;
+ }
+
+ *desc = is_offer ? strdup(webrtc->desc_offer) : strdup(webrtc->desc_answer);
+
+ g_mutex_unlock(&webrtc->desc_mutex);
+
+ LOG_INFO("%s", *desc);
+
+ return WEBRTC_ERROR_NONE;
+}
+
+int _webrtcbin_create_offer(webrtc_s *webrtc, char **offer)
+{
+ RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+ RET_VAL_IF(offer == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "offer is NULL");
+ RET_VAL_IF(webrtc->gst.webrtcbin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "webrtcbin is NULL");
+
+ return _create_session_description(webrtc, TRUE, offer);
+}
+
+int _webrtcbin_create_answer(webrtc_s *webrtc, char **answer)
+{
+ RET_VAL_IF(webrtc == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "webrtc is NULL");
+ RET_VAL_IF(answer == NULL, WEBRTC_ERROR_INVALID_PARAMETER, "answer is NULL");
+ RET_VAL_IF(webrtc->gst.webrtcbin == NULL, WEBRTC_ERROR_INVALID_OPERATION, "webrtcbin is NULL");
+
+ return _create_session_description(webrtc, FALSE, answer);
+}
g_print("webrtc_unset_negotiation_needed_cb() success\n");
}
+static void _webrtc_create_offer()
+{
+ int ret = WEBRTC_ERROR_NONE;
+ char *offer = NULL;
+
+ ret = webrtc_create_offer(g_webrtc, &offer);
+ if (ret != WEBRTC_ERROR_NONE) {
+ g_print("failed to webrtc_create_offer()\n");
+ } else {
+ g_print("webrtc_create_offer() success\noffer:\n%s\n", offer);
+ free(offer);
+ }
+}
+
+static void _webrtc_create_answer()
+{
+ int ret = WEBRTC_ERROR_NONE;
+ char *answer = NULL;
+
+ ret = webrtc_create_answer(g_webrtc, &answer);
+ if (ret != WEBRTC_ERROR_NONE) {
+ g_print("failed to webrtc_create_answer()\n");
+ } else {
+ g_print("webrtc_create_answer() success\nanswer:\n%s\n", answer);
+ free(answer);
+ }
+}
+
static void _setting_signalling_server(char *uri)
{
int ret = 0;
} else if (strncmp(cmd, "un", 2) == 0) {
_webrtc_unset_negotiation_needed_cb();
+ } else if (strncmp(cmd, "co", 2) == 0) {
+ _webrtc_create_offer();
+
+ } else if (strncmp(cmd, "ca", 2) == 0) {
+ _webrtc_create_answer();
+
} else {
g_print("unknown menu \n");
}
g_print("r. Remove media source\n");
g_print("sn. Set negotiation needed callback\t");
g_print("un. Unset negotiation needed callback\n");
+ g_print("co. Create offer\t");
+ g_print("ca. Create answer\n");
g_print("st. Set STUN server\n");
g_print("----------------------------------- App. Setting ----------------------------------------\n");
g_print("ss. Signalling server\n");