#include <Elementary.h>
#include <appcore-efl.h>
+/* For signalling */
+#include <libsoup/soup.h>
+#include <json-glib/json-glib.h>
#include <media_streamer.h>
#ifdef PACKAGE
#endif
#define PACKAGE "media_streamer_test"
+/* webrtc */
+enum AppState {
+ APP_STATE_UNKNOWN = 0,
+ APP_STATE_ERROR = 1, /* generic error */
+ SERVER_CONNECTING = 1000,
+ SERVER_CONNECTION_ERROR,
+ SERVER_CONNECTED, /* Ready to register */
+ SERVER_REGISTERING = 2000,
+ SERVER_REGISTRATION_ERROR,
+ SERVER_REGISTERED, /* Ready to call a peer */
+ SERVER_CLOSED, /* server connection closed by us or the server */
+ PEER_CONNECTING = 3000,
+ PEER_CONNECTION_ERROR,
+ PEER_CONNECTED,
+ PEER_CALL_NEGOTIATING = 4000,
+ PEER_CALL_WAITING,
+ PEER_CALL_STARTED,
+ PEER_CALL_STOPPING,
+ PEER_CALL_STOPPED,
+ PEER_CALL_ERROR,
+};
+
typedef enum {
MENU_STATE_UNKNOWN = 0,
MENU_STATE_MAIN_MENU,
#define DEFAULT_SEGMENT_PATH "/tmp/segment%05d.ts"
#define DEFAULT_PLAYLIST_PATH "/tmp/playlist.m3u8"
+#define ENTER g_print ("%s:%d>%s\n",__FILE__, __LINE__, __FUNCTION__);
+
/*---------------------------------------------------------------------------
| GLOBAL VARIABLE DEFINITIONS: |
---------------------------------------------------------------------------*/
gboolean g_video_is_on = FALSE;
gboolean g_audio_is_on = FALSE;
gboolean g_use_proxy = FALSE;
+static gint32 our_id = 0;
+static SoupWebsocketConnection *ws_conn = NULL;
+static const gchar *server_url = "wss://webrtc.nirbheek.in:8443";
+static enum AppState app_state = 0;
+static gboolean disable_ssl = FALSE;
+media_streamer_node_h webrtcbin = NULL;
media_format_h vfmt_vp8 = NULL;
media_format_h vfmt_i420 = NULL;
g_print("media_format_set_container_mime failed!\n");
}
+static gboolean
+cleanup_webrtc (const gchar * msg, enum AppState state)
+{
+ ENTER;
+ if (msg)
+ g_printerr ("%s\n", msg);
+ if (state > 0)
+ app_state = state;
+
+ if (ws_conn) {
+ if (soup_websocket_connection_get_state (ws_conn) ==
+ SOUP_WEBSOCKET_STATE_OPEN)
+ /* This will call us again */
+ soup_websocket_connection_close (ws_conn, 1000, "");
+ else
+ g_object_unref (ws_conn);
+ }
+ /* To allow usage as a GSourceFunc */
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean
+setup_call (void)
+{
+ gchar *msg;
+ ENTER;
+
+ if (soup_websocket_connection_get_state (ws_conn) !=
+ SOUP_WEBSOCKET_STATE_OPEN)
+ return FALSE;
+ if (!g_peer_id)
+ return FALSE;
+
+ g_print ("Setting up signalling server call with %d\n", g_peer_id);
+ app_state = PEER_CONNECTING;
+ msg = g_strdup_printf ("SESSION %d", g_peer_id);
+ soup_websocket_connection_send_text (ws_conn, msg);
+ g_free (msg);
+ return TRUE;
+}
+
+/* One mega message handler for our asynchronous calling mechanism */
+static void
+on_server_message (SoupWebsocketConnection * conn, SoupWebsocketDataType type,
+ GBytes * message, gpointer user_data)
+{
+ gchar *text;
+ ENTER;
+
+ switch (type) {
+ case SOUP_WEBSOCKET_DATA_BINARY:
+ g_printerr ("Received unknown binary message, ignoring\n");
+ return;
+ case SOUP_WEBSOCKET_DATA_TEXT: {
+ gsize size;
+ const gchar *data = g_bytes_get_data (message, &size);
+ /* Convert to NULL-terminated string */
+ text = g_strndup (data, size);
+ g_print ("Received text message, [%s]\n", text);
+ break;
+ }
+ default:
+ g_assert_not_reached ();
+ }
+
+ /* Server has accepted our registration, we are ready to send commands */
+ if (g_strcmp0 (text, "HELLO") == 0) {
+ if (app_state != SERVER_REGISTERING) {
+ cleanup_webrtc ("ERROR: Received HELLO when not registering",
+ APP_STATE_ERROR);
+ goto out;
+ }
+ app_state = SERVER_REGISTERED;
+ g_print ("Registered with server\n");
+ /* Ask signalling server to connect us with a specific peer */
+ if (g_peer_id) {
+ if (!setup_call ()) {
+ cleanup_webrtc ("ERROR: Failed to setup call", PEER_CALL_ERROR);
+ goto out;
+ }
+ } else {
+ /* should WAIT for another peer */
+ g_print ("need to wait for another peer...(our id:%d)\n", our_id);
+ app_state = PEER_CALL_WAITING;
+ /* Start negotiation (exchange SDP and ICE candidates) */
+ /* need to start pipeline_answer */
+ }
+ /* Call has been setup by the server, now we can start negotiation */
+ } else if (g_strcmp0 (text, "SESSION_OK") == 0) {
+ if (app_state != PEER_CONNECTING) {
+ cleanup_webrtc ("ERROR: Received SESSION_OK when not calling",
+ PEER_CONNECTION_ERROR);
+ goto out;
+ }
+
+ app_state = PEER_CONNECTED;
+ /* Start negotiation (exchange SDP and ICE candidates) */
+ /* need to start pipeline */
+ /* Handle errors */
+ } else if (g_str_has_prefix (text, "ERROR")) {
+ switch (app_state) {
+ case SERVER_CONNECTING:
+ app_state = SERVER_CONNECTION_ERROR;
+ break;
+ case SERVER_REGISTERING:
+ app_state = SERVER_REGISTRATION_ERROR;
+ break;
+ case PEER_CONNECTING:
+ app_state = PEER_CONNECTION_ERROR;
+ break;
+ case PEER_CALL_WAITING:
+ case PEER_CONNECTED:
+ case PEER_CALL_NEGOTIATING:
+ app_state = PEER_CALL_ERROR;
+ break;
+ default:
+ app_state = APP_STATE_ERROR;
+ }
+ cleanup_webrtc (text, 0);
+ /* Look for JSON messages containing SDP and ICE candidates */
+ } else {
+ JsonNode *root;
+ JsonObject *object, *child;
+ JsonParser *parser = json_parser_new ();
+ if (!json_parser_load_from_data (parser, text, -1, NULL)) {
+ g_printerr ("Unknown message '%s', ignoring", text);
+ g_object_unref (parser);
+ goto out;
+ }
+
+ root = json_parser_get_root (parser);
+ if (!JSON_NODE_HOLDS_OBJECT (root)) {
+ g_printerr ("Unknown json message '%s', ignoring", text);
+ g_object_unref (parser);
+ goto out;
+ }
+
+ object = json_node_get_object (root);
+ /* Check type of JSON message */
+ if (json_object_has_member (object, "sdp")) {
+ const gchar *text, *sdptype;
+
+ if (g_peer_id)
+ g_assert_cmphex (app_state, ==, PEER_CALL_NEGOTIATING);
+ else
+ g_assert_cmphex (app_state, ==, PEER_CALL_WAITING);
+
+ child = json_object_get_object_member (object, "sdp");
+
+ if (!json_object_has_member (child, "type")) {
+ cleanup_webrtc ("ERROR: received SDP without 'type'",
+ PEER_CALL_ERROR);
+ goto out;
+ }
+
+ sdptype = json_object_get_string_member (child, "type");
+ text = json_object_get_string_member (child, "sdp");
+
+
+ if (g_str_equal (sdptype, "answer")) {
+ g_print ("Received answer:\n%s\n", text);
+
+ /* need to API: set remote description */
+
+ app_state = PEER_CALL_STARTED;
+ } else {
+ g_print ("Received offer:\n%s\n", text);
+ /* need to API: set remote description */
+ }
+
+ } else if (json_object_has_member (object, "ice")) {
+ /*need to API: Add ice candidate sent by remote peer */
+ } else {
+ g_printerr ("Ignoring unknown JSON message:\n%s\n", text);
+ }
+ g_object_unref (parser);
+ }
+
+out:
+ g_free (text);
+}
+
+static gint32
+register_with_server (void)
+{
+ gchar *hello;
+ gint32 our_id;
+ ENTER;
+
+ if (soup_websocket_connection_get_state (ws_conn) !=
+ SOUP_WEBSOCKET_STATE_OPEN)
+ return -1;
+
+ our_id = g_random_int_range (10, 10000);
+ g_print ("Registering id %i with server\n", our_id);
+ app_state = SERVER_REGISTERING;
+
+ /* Register with the server with a random integer id. Reply will be received
+ * by on_server_message() */
+ hello = g_strdup_printf ("HELLO %i", our_id);
+ soup_websocket_connection_send_text (ws_conn, hello);
+ g_free (hello);
+
+ return our_id;
+}
+
+/* Answer created by our pipeline, to be sent to the peer */
+static void
+on_server_closed (SoupWebsocketConnection * conn G_GNUC_UNUSED,
+ gpointer user_data G_GNUC_UNUSED)
+{
+ app_state = SERVER_CLOSED;
+ ENTER;
+
+ cleanup_webrtc ("Server connection closed", 0);
+}
+
+static void
+on_server_connected (SoupSession * session, GAsyncResult * res,
+ SoupMessage *msg)
+{
+ GError *error = NULL;
+ ENTER;
+
+ g_print("on_server_connected\n");
+ ws_conn = soup_session_websocket_connect_finish (session, res, &error);
+ if (error) {
+ cleanup_webrtc (error->message, SERVER_CONNECTION_ERROR);
+ g_error_free (error);
+ return;
+ }
+
+ g_assert_nonnull (ws_conn);
+
+ app_state = SERVER_CONNECTED;
+ g_print ("Connected to signalling server\n");
+
+ g_signal_connect (ws_conn, "closed", G_CALLBACK (on_server_closed), NULL);
+ g_signal_connect (ws_conn, "message", G_CALLBACK (on_server_message), NULL);
+
+ /* Register with the server so it knows about us and can accept commands */
+ our_id = register_with_server ();
+}
+
+/* TIZEN: add for log */
+static inline gchar
+gst_soup_util_log_make_level_tag (SoupLoggerLogLevel level)
+{
+ gchar c;
+
+ if (G_UNLIKELY ((gint) level > 9))
+ return '?';
+
+ switch (level) {
+ case SOUP_LOGGER_LOG_MINIMAL:
+ c = 'M';
+ break;
+ case SOUP_LOGGER_LOG_HEADERS:
+ c = 'H';
+ break;
+ case SOUP_LOGGER_LOG_BODY:
+ c = 'B';
+ break;
+ default:
+ /* Unknown level. If this is hit libsoup likely added a new
+ * log level to SoupLoggerLogLevel and it should be added
+ * as a case */
+ c = level + '0';
+ break;
+ }
+ return c;
+}
+
+static void
+_log_printer_cb (SoupLogger G_GNUC_UNUSED * logger,
+ SoupLoggerLogLevel level, char direction, const char *data,
+ gpointer user_data)
+{
+ gchar c;
+
+ c = gst_soup_util_log_make_level_tag (level);
+ g_print("HTTP_SESSION(%c): %c %s\n", c, direction, data);
+}
+
+static void
+connect_to_websocket_server_async (void)
+{
+ SoupLogger *logger;
+ SoupMessage *message;
+ SoupSession *session;
+ SoupURI *proxy_uri;
+ const char *https_aliases[] = {"wss", NULL};
+ ENTER;
+
+ if (!g_use_proxy){
+ session = soup_session_new_with_options (SOUP_SESSION_SSL_STRICT, !disable_ssl,
+ SOUP_SESSION_HTTPS_ALIASES, https_aliases, NULL);
+ } else {
+ proxy_uri = soup_uri_new (g_proxy_address);
+ session = soup_session_new_with_options (SOUP_SESSION_SSL_STRICT, !disable_ssl,
+ SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE,
+ SOUP_SESSION_PROXY_URI, proxy_uri,
+ SOUP_SESSION_SSL_CA_FILE, "/opt/var/lib/ca-certificates/ca-bundle.pem",
+ SOUP_SESSION_HTTPS_ALIASES, https_aliases, NULL);
+ soup_uri_free (proxy_uri);
+ }
+
+ logger = soup_logger_new (SOUP_LOGGER_LOG_BODY, -1);
+
+ /* TIZEN: add for log */
+ soup_logger_set_printer (logger, _log_printer_cb, NULL, NULL);
+
+ soup_session_add_feature (session, SOUP_SESSION_FEATURE (logger));
+ g_object_unref (logger);
+
+ message = soup_message_new (SOUP_METHOD_GET, server_url);
+
+ g_print ("Connecting to server[%s]...\n", server_url);
+
+ /* Once connected, we will register */
+ soup_session_websocket_connect_async (session, message, NULL, NULL, NULL,
+ (GAsyncReadyCallback) on_server_connected, message);
+ app_state = SERVER_CONNECTING;
+}
+
static void set_rtp_params(media_streamer_node_h rtp_node, const char *ip, int video_port, int audio_port, gboolean port_reverse)
{
bundle *params = bundle_create();
media_streamer_node_link(videoconverter, "src", video_sink, "sink");
}
-static void _create_webrtc_sendrec_video_audio(void)
-{
-
-}
-
static void _create_file_sub_playing(void)
{
media_streamer_node_h file_sub_src = NULL;
{
if ((g_scenario_mode == SCENARIO_MODE_WEBRTC_SENDRECV_VIDEO_AUDIO)) {
create_formats();
- _create_webrtc_sendrec_video_audio();
+ connect_to_websocket_server_async ();
} else {
g_print("Invalid scenario menu preset was selected!");
}