besser82 / rpms / pidgin

Forked from rpms/pidgin 6 years ago
Clone
David Woodhouse 313cf00
David Woodhouse 313cf00
# HG changeset patch
David Woodhouse 313cf00
# User Youness Alaoui <kakaroto@kakaroto.homelinux.net>
David Woodhouse 313cf00
# Date 1405979621 14400
David Woodhouse 313cf00
# Node ID 4fe1034f3dce1c5cd3c929ab8c58db8e27655beb
David Woodhouse 313cf00
# Parent  d729a9b2126594df3e38647e926ac7c0a7db807b
David Woodhouse 313cf00
Add application media type and APIs
David Woodhouse 313cf00
David Woodhouse 313cf00
Fixes #16315
David Woodhouse 313cf00
David Woodhouse 313cf00
diff --git a/configure.ac b/configure.ac
David Woodhouse 313cf00
--- a/configure.ac
David Woodhouse 313cf00
+++ b/configure.ac
David Woodhouse 313cf00
@@ -916,6 +916,20 @@
David Woodhouse 313cf00
 AM_CONDITIONAL(USE_VV, test "x$enable_vv" != "xno")
David Woodhouse 313cf00
 
David Woodhouse 313cf00
 dnl #######################################################################
David Woodhouse 313cf00
+dnl # Check for Raw data streams support in Farstream 
David Woodhouse 313cf00
+dnl #######################################################################
David Woodhouse 313cf00
+if test "x$enable_vv" != "xno" -a "x$with_gstreamer" == "x1.0"; then
David Woodhouse 313cf00
+	AC_MSG_CHECKING(for raw data support in Farstream)
David Woodhouse 313cf00
+	PKG_CHECK_MODULES(GSTAPP, [gstreamer-app-1.0], [
David Woodhouse 313cf00
+		AC_DEFINE(USE_GSTAPP, 1, [Use GStreamer Video Overlay support])
David Woodhouse 313cf00
+		AC_SUBST(GSTAPP_CFLAGS)
David Woodhouse 313cf00
+		AC_SUBST(GSTAPP_LIBS)
David Woodhouse 313cf00
+		AC_DEFINE(HAVE_MEDIA_APPLICATION, 1, [Define if we have support for application media type.])
David Woodhouse 313cf00
+		AC_MSG_RESULT(yes)
David Woodhouse 313cf00
+		], [AC_MSG_RESULT(no)])
David Woodhouse 313cf00
+fi
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+dnl #######################################################################
David Woodhouse 313cf00
 dnl # Check for Internationalized Domain Name support
David Woodhouse 313cf00
 dnl #######################################################################
David Woodhouse 313cf00
 
David Woodhouse 313cf00
diff --git a/libpurple/Makefile.am b/libpurple/Makefile.am
David Woodhouse 313cf00
--- a/libpurple/Makefile.am
David Woodhouse 313cf00
+++ b/libpurple/Makefile.am
David Woodhouse 313cf00
@@ -309,6 +309,7 @@
David Woodhouse 313cf00
 	$(FARSTREAM_LIBS) \
David Woodhouse 313cf00
 	$(GSTREAMER_LIBS) \
David Woodhouse 313cf00
 	$(GSTVIDEO_LIBS) \
David Woodhouse 313cf00
+	$(GSTAPP_LIBS) \
David Woodhouse 313cf00
 	$(GSTINTERFACES_LIBS) \
David Woodhouse 313cf00
 	$(IDN_LIBS) \
David Woodhouse 313cf00
 	ciphers/libpurple-ciphers.la \
David Woodhouse 313cf00
@@ -326,6 +327,7 @@
David Woodhouse 313cf00
 	$(FARSTREAM_CFLAGS) \
David Woodhouse 313cf00
 	$(GSTREAMER_CFLAGS) \
David Woodhouse 313cf00
 	$(GSTVIDEO_CFLAGS) \
David Woodhouse 313cf00
+	$(GSTAPP_CFLAGS) \
David Woodhouse 313cf00
 	$(GSTINTERFACES_CFLAGS) \
David Woodhouse 313cf00
 	$(IDN_CFLAGS) \
David Woodhouse 313cf00
 	$(NETWORKMANAGER_CFLAGS)
David Woodhouse 313cf00
diff --git a/libpurple/media-gst.h b/libpurple/media-gst.h
David Woodhouse 313cf00
--- a/libpurple/media-gst.h
David Woodhouse 313cf00
+++ b/libpurple/media-gst.h
David Woodhouse 313cf00
@@ -71,6 +71,7 @@
David Woodhouse 313cf00
 
David Woodhouse 313cf00
 	PURPLE_MEDIA_ELEMENT_SRC = 1 << 9,		/** can be set as an active src */
David Woodhouse 313cf00
 	PURPLE_MEDIA_ELEMENT_SINK = 1 << 10,		/** can be set as an active sink */
David Woodhouse 313cf00
+	PURPLE_MEDIA_ELEMENT_APPLICATION = 1 << 11,	/** supports application data */
David Woodhouse 313cf00
 } PurpleMediaElementType;
David Woodhouse 313cf00
 
David Woodhouse 313cf00
 #ifdef __cplusplus
David Woodhouse 313cf00
diff --git a/libpurple/media/backend-fs2.c b/libpurple/media/backend-fs2.c
David Woodhouse 313cf00
--- a/libpurple/media/backend-fs2.c
David Woodhouse 313cf00
+++ b/libpurple/media/backend-fs2.c
David Woodhouse 313cf00
@@ -577,6 +577,10 @@
David Woodhouse 313cf00
 		return FS_MEDIA_TYPE_AUDIO;
David Woodhouse 313cf00
 	else if (type & PURPLE_MEDIA_VIDEO)
David Woodhouse 313cf00
 		return FS_MEDIA_TYPE_VIDEO;
David Woodhouse 313cf00
+#ifdef HAVE_MEDIA_APPLICATION
David Woodhouse 313cf00
+	else if (type & PURPLE_MEDIA_APPLICATION)
David Woodhouse 313cf00
+		return FS_MEDIA_TYPE_APPLICATION;
David Woodhouse 313cf00
+#endif
David Woodhouse 313cf00
 	else
David Woodhouse 313cf00
 		return 0;
David Woodhouse 313cf00
 }
David Woodhouse 313cf00
@@ -585,7 +589,7 @@
David Woodhouse 313cf00
 session_type_to_fs_stream_direction(PurpleMediaSessionType type)
David Woodhouse 313cf00
 {
David Woodhouse 313cf00
 	if ((type & PURPLE_MEDIA_AUDIO) == PURPLE_MEDIA_AUDIO ||
David Woodhouse 313cf00
-			(type & PURPLE_MEDIA_VIDEO) == PURPLE_MEDIA_VIDEO)
David Woodhouse 313cf00
+		(type & PURPLE_MEDIA_VIDEO) == PURPLE_MEDIA_VIDEO)
David Woodhouse 313cf00
 		return FS_DIRECTION_BOTH;
David Woodhouse 313cf00
 	else if ((type & PURPLE_MEDIA_SEND_AUDIO) ||
David Woodhouse 313cf00
 			(type & PURPLE_MEDIA_SEND_VIDEO))
David Woodhouse 313cf00
@@ -593,6 +597,14 @@
David Woodhouse 313cf00
 	else if ((type & PURPLE_MEDIA_RECV_AUDIO) ||
David Woodhouse 313cf00
 			(type & PURPLE_MEDIA_RECV_VIDEO))
David Woodhouse 313cf00
 		return FS_DIRECTION_RECV;
David Woodhouse 313cf00
+#ifdef HAVE_MEDIA_APPLICATION
David Woodhouse 313cf00
+	else if ((type & PURPLE_MEDIA_APPLICATION) == PURPLE_MEDIA_APPLICATION)
David Woodhouse 313cf00
+		return FS_DIRECTION_BOTH;
David Woodhouse 313cf00
+	else if (type & PURPLE_MEDIA_SEND_APPLICATION)
David Woodhouse 313cf00
+		return FS_DIRECTION_SEND;
David Woodhouse 313cf00
+	else if (type & PURPLE_MEDIA_RECV_APPLICATION)
David Woodhouse 313cf00
+		return FS_DIRECTION_RECV;
David Woodhouse 313cf00
+#endif
David Woodhouse 313cf00
 	else
David Woodhouse 313cf00
 		return FS_DIRECTION_NONE;
David Woodhouse 313cf00
 }
David Woodhouse 313cf00
@@ -611,6 +623,13 @@
David Woodhouse 313cf00
 			result |= PURPLE_MEDIA_SEND_VIDEO;
David Woodhouse 313cf00
 		if (direction & FS_DIRECTION_RECV)
David Woodhouse 313cf00
 			result |= PURPLE_MEDIA_RECV_VIDEO;
David Woodhouse 313cf00
+#ifdef HAVE_MEDIA_APPLICATION
David Woodhouse 313cf00
+	} else if (type == FS_MEDIA_TYPE_APPLICATION) {
David Woodhouse 313cf00
+		if (direction & FS_DIRECTION_SEND)
David Woodhouse 313cf00
+			result |= PURPLE_MEDIA_SEND_APPLICATION;
David Woodhouse 313cf00
+		if (direction & FS_DIRECTION_RECV)
David Woodhouse 313cf00
+			result |= PURPLE_MEDIA_RECV_APPLICATION;
David Woodhouse 313cf00
+#endif
David Woodhouse 313cf00
 	}
David Woodhouse 313cf00
 	return result;
David Woodhouse 313cf00
 }
David Woodhouse 313cf00
@@ -1367,7 +1386,8 @@
David Woodhouse 313cf00
 				& PURPLE_MEDIA_AUDIO)
David Woodhouse 313cf00
 			purple_media_error(priv->media,
David Woodhouse 313cf00
 					_("Error with your microphone"));
David Woodhouse 313cf00
-		else
David Woodhouse 313cf00
+		else if (purple_media_get_session_type(priv->media,
David Woodhouse 313cf00
+                        sessions->data) & PURPLE_MEDIA_VIDEO)
David Woodhouse 313cf00
 			purple_media_error(priv->media,
David Woodhouse 313cf00
 					_("Error with your webcam"));
David Woodhouse 313cf00
 
David Woodhouse 313cf00
@@ -1790,6 +1810,21 @@
David Woodhouse 313cf00
 	session->session = fs_conference_new_session(priv->conference,
David Woodhouse 313cf00
 			session_type_to_fs_media_type(type), &err;;
David Woodhouse 313cf00
 
David Woodhouse 313cf00
+#ifdef HAVE_MEDIA_APPLICATION
David Woodhouse 313cf00
+	if (type == PURPLE_MEDIA_APPLICATION) {
David Woodhouse 313cf00
+		GstCaps *caps;
David Woodhouse 313cf00
+		GObject *rtpsession = NULL;
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+		caps = gst_caps_new_empty_simple ("application/octet-stream");
David Woodhouse 313cf00
+		fs_session_set_allowed_caps (session->session, caps, caps, NULL);
David Woodhouse 313cf00
+		gst_caps_unref (caps);
David Woodhouse 313cf00
+		g_object_get (session->session, "internal-session", &rtpsession, NULL);
David Woodhouse 313cf00
+		if (rtpsession) {
David Woodhouse 313cf00
+			g_object_set (rtpsession, "probation", 0, NULL);
David Woodhouse 313cf00
+			g_object_unref (rtpsession);
David Woodhouse 313cf00
+		}
David Woodhouse 313cf00
+	}
David Woodhouse 313cf00
+#endif
David Woodhouse 313cf00
 	if (err != NULL) {
David Woodhouse 313cf00
 		purple_media_error(priv->media,
David Woodhouse 313cf00
 				_("Error creating session: %s"),
David Woodhouse 313cf00
@@ -2004,6 +2039,21 @@
David Woodhouse 313cf00
 			gst_bin_add(GST_BIN(priv->confbin), sink);
David Woodhouse 313cf00
 			gst_element_set_state(sink, GST_STATE_PLAYING);
David Woodhouse 313cf00
 			stream->fakesink = sink;
David Woodhouse 313cf00
+#ifdef HAVE_MEDIA_APPLICATION
David Woodhouse 313cf00
+		} else if (codec->media_type == FS_MEDIA_TYPE_APPLICATION) {
David Woodhouse 313cf00
+#if GST_CHECK_VERSION(1,0,0)
David Woodhouse 313cf00
+			stream->src = gst_element_factory_make("funnel", NULL);
David Woodhouse 313cf00
+#else
David Woodhouse 313cf00
+			stream->src = gst_element_factory_make("fsfunnel", NULL);
David Woodhouse 313cf00
+#endif
David Woodhouse 313cf00
+			sink = purple_media_manager_get_element(
David Woodhouse 313cf00
+					purple_media_get_manager(priv->media),
David Woodhouse 313cf00
+					PURPLE_MEDIA_RECV_APPLICATION, priv->media,
David Woodhouse 313cf00
+					stream->session->id,
David Woodhouse 313cf00
+					stream->participant);
David Woodhouse 313cf00
+			gst_bin_add(GST_BIN(priv->confbin), sink);
David Woodhouse 313cf00
+			gst_element_set_state(sink, GST_STATE_PLAYING);
David Woodhouse 313cf00
+#endif
David Woodhouse 313cf00
 		}
David Woodhouse 313cf00
 		stream->tee = gst_element_factory_make("tee", NULL);
David Woodhouse 313cf00
 		gst_bin_add_many(GST_BIN(priv->confbin),
David Woodhouse 313cf00
@@ -2366,6 +2416,9 @@
David Woodhouse 313cf00
 			return FALSE;
David Woodhouse 313cf00
 
David Woodhouse 313cf00
 		if (session->type & (PURPLE_MEDIA_SEND_AUDIO |
David Woodhouse 313cf00
+#ifdef HAVE_MEDIA_APPLICATION
David Woodhouse 313cf00
+				PURPLE_MEDIA_SEND_APPLICATION |
David Woodhouse 313cf00
+#endif
David Woodhouse 313cf00
 				PURPLE_MEDIA_SEND_VIDEO)) {
David Woodhouse 313cf00
 #ifdef HAVE_FARSIGHT
David Woodhouse 313cf00
 			g_object_get(session->session,
David Woodhouse 313cf00
@@ -2389,6 +2442,9 @@
David Woodhouse 313cf00
 			PurpleMediaBackendFs2Session *session = values->data;
David Woodhouse 313cf00
 
David Woodhouse 313cf00
 			if (session->type & (PURPLE_MEDIA_SEND_AUDIO |
David Woodhouse 313cf00
+#ifdef HAVE_MEDIA_APPLICATION
David Woodhouse 313cf00
+					PURPLE_MEDIA_SEND_APPLICATION |
David Woodhouse 313cf00
+#endif
David Woodhouse 313cf00
 					PURPLE_MEDIA_SEND_VIDEO)) {
David Woodhouse 313cf00
 #ifdef HAVE_FARSIGHT
David Woodhouse 313cf00
 				g_object_get(session->session,
David Woodhouse 313cf00
diff --git a/libpurple/media/codec.c b/libpurple/media/codec.c
David Woodhouse 313cf00
--- a/libpurple/media/codec.c
David Woodhouse 313cf00
+++ b/libpurple/media/codec.c
David Woodhouse 313cf00
@@ -188,7 +188,7 @@
David Woodhouse 313cf00
 	g_object_class_install_property(gobject_class, PROP_MEDIA_TYPE,
David Woodhouse 313cf00
 			g_param_spec_flags("media-type",
David Woodhouse 313cf00
 			"Media Type",
David Woodhouse 313cf00
-			"Whether this is an audio of video codec.",
David Woodhouse 313cf00
+			"Whether this is an audio, video or application codec.",
David Woodhouse 313cf00
 			PURPLE_TYPE_MEDIA_SESSION_TYPE,
David Woodhouse 313cf00
 			PURPLE_MEDIA_NONE,
David Woodhouse 313cf00
 			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
David Woodhouse 313cf00
@@ -402,6 +402,8 @@
David Woodhouse 313cf00
 		media_type_str = "audio";
David Woodhouse 313cf00
 	else if (priv->media_type & PURPLE_MEDIA_VIDEO)
David Woodhouse 313cf00
 		media_type_str = "video";
David Woodhouse 313cf00
+	else if (priv->media_type & PURPLE_MEDIA_APPLICATION)
David Woodhouse 313cf00
+		media_type_str = "application";
David Woodhouse 313cf00
 
David Woodhouse 313cf00
 	g_string_printf(string, "%d: %s %s clock:%d channels:%d", priv->id,
David Woodhouse 313cf00
 			media_type_str, priv->encoding_name,
David Woodhouse 313cf00
diff --git a/libpurple/media/enum-types.c b/libpurple/media/enum-types.c
David Woodhouse 313cf00
--- a/libpurple/media/enum-types.c
David Woodhouse 313cf00
+++ b/libpurple/media/enum-types.c
David Woodhouse 313cf00
@@ -182,10 +182,16 @@
David Woodhouse 313cf00
 				"PURPLE_MEDIA_RECV_VIDEO", "recv-video" },
David Woodhouse 313cf00
 			{ PURPLE_MEDIA_SEND_VIDEO,
David Woodhouse 313cf00
 				"PURPLE_MEDIA_SEND_VIDEO", "send-video" },
David Woodhouse 313cf00
+			{ PURPLE_MEDIA_RECV_APPLICATION,
David Woodhouse 313cf00
+				"PURPLE_MEDIA_RECV_APPLICATION", "recv-application" },
David Woodhouse 313cf00
+			{ PURPLE_MEDIA_SEND_APPLICATION,
David Woodhouse 313cf00
+				"PURPLE_MEDIA_SEND_APPLICATION", "send-application" },
David Woodhouse 313cf00
 			{ PURPLE_MEDIA_AUDIO,
David Woodhouse 313cf00
 				"PURPLE_MEDIA_AUDIO", "audio" },
David Woodhouse 313cf00
 			{ PURPLE_MEDIA_VIDEO,
David Woodhouse 313cf00
 				"PURPLE_MEDIA_VIDEO", "video" },
David Woodhouse 313cf00
+			{ PURPLE_MEDIA_APPLICATION,
David Woodhouse 313cf00
+				"PURPLE_MEDIA_APPLICATION", "application" },
David Woodhouse 313cf00
 			{ 0, NULL, NULL }
David Woodhouse 313cf00
 		};
David Woodhouse 313cf00
 		type = g_flags_register_static(
David Woodhouse 313cf00
diff --git a/libpurple/media/enum-types.h b/libpurple/media/enum-types.h
David Woodhouse 313cf00
--- a/libpurple/media/enum-types.h
David Woodhouse 313cf00
+++ b/libpurple/media/enum-types.h
David Woodhouse 313cf00
@@ -94,8 +94,12 @@
David Woodhouse 313cf00
 	PURPLE_MEDIA_SEND_AUDIO = 1 << 1,
David Woodhouse 313cf00
 	PURPLE_MEDIA_RECV_VIDEO = 1 << 2,
David Woodhouse 313cf00
 	PURPLE_MEDIA_SEND_VIDEO = 1 << 3,
David Woodhouse 313cf00
+	PURPLE_MEDIA_RECV_APPLICATION = 1 << 4,
David Woodhouse 313cf00
+	PURPLE_MEDIA_SEND_APPLICATION = 1 << 5,
David Woodhouse 313cf00
 	PURPLE_MEDIA_AUDIO = PURPLE_MEDIA_RECV_AUDIO | PURPLE_MEDIA_SEND_AUDIO,
David Woodhouse 313cf00
-	PURPLE_MEDIA_VIDEO = PURPLE_MEDIA_RECV_VIDEO | PURPLE_MEDIA_SEND_VIDEO
David Woodhouse 313cf00
+	PURPLE_MEDIA_VIDEO = PURPLE_MEDIA_RECV_VIDEO | PURPLE_MEDIA_SEND_VIDEO,
David Woodhouse 313cf00
+	PURPLE_MEDIA_APPLICATION = PURPLE_MEDIA_RECV_APPLICATION |
David Woodhouse 313cf00
+                                   PURPLE_MEDIA_SEND_APPLICATION
David Woodhouse 313cf00
 } PurpleMediaSessionType;
David Woodhouse 313cf00
 
David Woodhouse 313cf00
 /** Media state-changed types */
David Woodhouse 313cf00
diff --git a/libpurple/mediamanager.c b/libpurple/mediamanager.c
David Woodhouse 313cf00
--- a/libpurple/mediamanager.c
David Woodhouse 313cf00
+++ b/libpurple/mediamanager.c
David Woodhouse 313cf00
@@ -44,6 +44,9 @@
David Woodhouse 313cf00
 #else
David Woodhouse 313cf00
 #include <farstream/fs-element-added-notifier.h>
David Woodhouse 313cf00
 #endif
David Woodhouse 313cf00
+#ifdef HAVE_MEDIA_APPLICATION
David Woodhouse 313cf00
+#include <gst/app/app.h>
David Woodhouse 313cf00
+#endif
David Woodhouse 313cf00
 
David Woodhouse 313cf00
 #if GST_CHECK_VERSION(1,0,0)
David Woodhouse 313cf00
 #include <gst/video/videooverlay.h>
David Woodhouse 313cf00
@@ -97,14 +100,45 @@
David Woodhouse 313cf00
 	PurpleMediaElementInfo *video_sink;
David Woodhouse 313cf00
 	PurpleMediaElementInfo *audio_src;
David Woodhouse 313cf00
 	PurpleMediaElementInfo *audio_sink;
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+#ifdef HAVE_MEDIA_APPLICATION
David Woodhouse 313cf00
+	/* Application data streams */
David Woodhouse 313cf00
+	GList *appdata_info; /* holds PurpleMediaAppDataInfo */
David Woodhouse 313cf00
+	GMutex appdata_mutex;
David Woodhouse 313cf00
+#endif
David Woodhouse 313cf00
 };
David Woodhouse 313cf00
 
David Woodhouse 313cf00
+#ifdef HAVE_MEDIA_APPLICATION
David Woodhouse 313cf00
+typedef struct {
David Woodhouse 313cf00
+	PurpleMedia *media;
David Woodhouse 313cf00
+	GWeakRef media_ref;
David Woodhouse 313cf00
+	gchar *session_id;
David Woodhouse 313cf00
+	gchar *participant;
David Woodhouse 313cf00
+	PurpleMediaAppDataCallbacks callbacks;
David Woodhouse 313cf00
+	gpointer user_data;
David Woodhouse 313cf00
+	GDestroyNotify notify;
David Woodhouse 313cf00
+	GstAppSrc *appsrc;
David Woodhouse 313cf00
+	GstAppSink *appsink;
David Woodhouse 313cf00
+	gint num_samples;
David Woodhouse 313cf00
+	GstSample *current_sample;
David Woodhouse 313cf00
+	guint sample_offset;
David Woodhouse 313cf00
+	gboolean writable;
David Woodhouse 313cf00
+	gboolean connected;
David Woodhouse 313cf00
+	guint writable_timer_id;
David Woodhouse 313cf00
+	guint readable_timer_id;
David Woodhouse 313cf00
+	GCond readable_cond;
David Woodhouse 313cf00
+} PurpleMediaAppDataInfo;
David Woodhouse 313cf00
+#endif
David Woodhouse 313cf00
+
David Woodhouse 313cf00
 #define PURPLE_MEDIA_MANAGER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA_MANAGER, PurpleMediaManagerPrivate))
David Woodhouse 313cf00
 #define PURPLE_MEDIA_ELEMENT_INFO_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA_ELEMENT_INFO, PurpleMediaElementInfoPrivate))
David Woodhouse 313cf00
 
David Woodhouse 313cf00
 static void purple_media_manager_class_init (PurpleMediaManagerClass *klass);
David Woodhouse 313cf00
 static void purple_media_manager_init (PurpleMediaManager *media);
David Woodhouse 313cf00
 static void purple_media_manager_finalize (GObject *object);
David Woodhouse 313cf00
+#ifdef HAVE_MEDIA_APPLICATION
David Woodhouse 313cf00
+static void free_appdata_info_locked (PurpleMediaAppDataInfo *info);
David Woodhouse 313cf00
+#endif
David Woodhouse 313cf00
 
David Woodhouse 313cf00
 static GObjectClass *parent_class = NULL;
David Woodhouse 313cf00
 
David Woodhouse 313cf00
@@ -190,8 +224,10 @@
David Woodhouse 313cf00
 	media->priv->medias = NULL;
David Woodhouse 313cf00
 	media->priv->private_medias = NULL;
David Woodhouse 313cf00
 	media->priv->next_output_window_id = 1;
David Woodhouse 313cf00
-#ifdef USE_VV
David Woodhouse 313cf00
 	media->priv->backend_type = PURPLE_TYPE_MEDIA_BACKEND_FS2;
David Woodhouse 313cf00
+#ifdef HAVE_MEDIA_APPLICATION
David Woodhouse 313cf00
+	media->priv->appdata_info = NULL;
David Woodhouse 313cf00
+	g_mutex_init (&media->priv->appdata_mutex);
David Woodhouse 313cf00
 #endif
David Woodhouse 313cf00
 
David Woodhouse 313cf00
 	purple_prefs_add_none("/purple/media");
David Woodhouse 313cf00
@@ -220,6 +256,13 @@
David Woodhouse 313cf00
 	}
David Woodhouse 313cf00
 	if (priv->video_caps)
David Woodhouse 313cf00
 		gst_caps_unref(priv->video_caps);
David Woodhouse 313cf00
+#ifdef HAVE_MEDIA_APPLICATION
David Woodhouse 313cf00
+	if (priv->appdata_info)
David Woodhouse 313cf00
+		g_list_free_full (priv->appdata_info,
David Woodhouse 313cf00
+			(GDestroyNotify) free_appdata_info_locked);
David Woodhouse 313cf00
+	g_mutex_clear (&priv->appdata_mutex);
David Woodhouse 313cf00
+#endif
David Woodhouse 313cf00
+
David Woodhouse 313cf00
 	parent_class->finalize(media);
David Woodhouse 313cf00
 }
David Woodhouse 313cf00
 #endif
David Woodhouse 313cf00
@@ -440,8 +483,23 @@
David Woodhouse 313cf00
 		medias = &manager->priv->private_medias;
David Woodhouse 313cf00
 	}
David Woodhouse 313cf00
 
David Woodhouse 313cf00
-	if (list)
David Woodhouse 313cf00
+	if (list) {
David Woodhouse 313cf00
 		*medias = g_list_delete_link(*medias, list);
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+#ifdef HAVE_MEDIA_APPLICATION
David Woodhouse 313cf00
+		g_mutex_lock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+		for (list = manager->priv->appdata_info; list; list = list->next) {
David Woodhouse 313cf00
+			PurpleMediaAppDataInfo *info = list->data;
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+			if (info->media == media) {
David Woodhouse 313cf00
+				manager->priv->appdata_info = g_list_delete_link (
David Woodhouse 313cf00
+					manager->priv->appdata_info, list);
David Woodhouse 313cf00
+				free_appdata_info_locked (info);
David Woodhouse 313cf00
+			}
David Woodhouse 313cf00
+		}
David Woodhouse 313cf00
+		g_mutex_unlock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+#endif
David Woodhouse 313cf00
+	}
David Woodhouse 313cf00
 #endif
David Woodhouse 313cf00
 }
David Woodhouse 313cf00
 
David Woodhouse 313cf00
@@ -493,6 +551,92 @@
David Woodhouse 313cf00
 	return get_media_by_account (manager, account, TRUE);
David Woodhouse 313cf00
 }
David Woodhouse 313cf00
 
David Woodhouse 313cf00
+#ifdef HAVE_MEDIA_APPLICATION
David Woodhouse 313cf00
+static void
David Woodhouse 313cf00
+free_appdata_info_locked (PurpleMediaAppDataInfo *info)
David Woodhouse 313cf00
+{
David Woodhouse 313cf00
+	if (info->notify)
David Woodhouse 313cf00
+		info->notify (info->user_data);
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	/* Make sure no other thread is using the structure */
David Woodhouse 313cf00
+	g_free (info->session_id);
David Woodhouse 313cf00
+	g_free (info->participant);
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	if (info->readable_timer_id) {
David Woodhouse 313cf00
+		purple_timeout_remove (info->readable_timer_id);
David Woodhouse 313cf00
+		info->readable_timer_id = 0;
David Woodhouse 313cf00
+	}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	if (info->writable_timer_id) {
David Woodhouse 313cf00
+		purple_timeout_remove (info->writable_timer_id);
David Woodhouse 313cf00
+		info->writable_timer_id = 0;
David Woodhouse 313cf00
+	}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	if (info->current_sample)
David Woodhouse 313cf00
+		gst_sample_unref (info->current_sample);
David Woodhouse 313cf00
+	info->current_sample = NULL;
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	/* Unblock any reading thread before destroying the GCond */
David Woodhouse 313cf00
+	g_cond_broadcast (&info->readable_cond);
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	g_cond_clear (&info->readable_cond);
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	g_slice_free (PurpleMediaAppDataInfo, info);
David Woodhouse 313cf00
+}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+/*
David Woodhouse 313cf00
+ * Get an app data info struct associated with a session and lock the mutex
David Woodhouse 313cf00
+ * We don't want to return an info struct and unlock then it gets destroyed
David Woodhouse 313cf00
+ * so we need to return it with the lock still taken
David Woodhouse 313cf00
+ */
David Woodhouse 313cf00
+static PurpleMediaAppDataInfo *
David Woodhouse 313cf00
+get_app_data_info_and_lock (PurpleMediaManager *manager,
David Woodhouse 313cf00
+	PurpleMedia *media, const gchar *session_id, const gchar *participant)
David Woodhouse 313cf00
+{
David Woodhouse 313cf00
+	GList *i;
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	g_mutex_lock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+	for (i = manager->priv->appdata_info; i; i = i->next) {
David Woodhouse 313cf00
+		PurpleMediaAppDataInfo *info = i->data;
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+		if (info->media == media &&
David Woodhouse 313cf00
+			g_strcmp0 (info->session_id, session_id) == 0 &&
David Woodhouse 313cf00
+			(participant == NULL ||
David Woodhouse 313cf00
+				g_strcmp0 (info->participant, participant) == 0)) {
David Woodhouse 313cf00
+			return info;
David Woodhouse 313cf00
+		}
David Woodhouse 313cf00
+	}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	return NULL;
David Woodhouse 313cf00
+}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+/*
David Woodhouse 313cf00
+ * Get an app data info struct associated with a session and lock the mutex
David Woodhouse 313cf00
+ * if it doesn't exist, we create it.
David Woodhouse 313cf00
+ */
David Woodhouse 313cf00
+static PurpleMediaAppDataInfo *
David Woodhouse 313cf00
+ensure_app_data_info_and_lock (PurpleMediaManager *manager, PurpleMedia *media,
David Woodhouse 313cf00
+	const gchar *session_id, const gchar *participant)
David Woodhouse 313cf00
+{
David Woodhouse 313cf00
+	PurpleMediaAppDataInfo * info = get_app_data_info_and_lock (manager, media,
David Woodhouse 313cf00
+		session_id, participant);
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	if (info == NULL) {
David Woodhouse 313cf00
+		info = g_slice_new0 (PurpleMediaAppDataInfo);
David Woodhouse 313cf00
+		info->media = media;
David Woodhouse 313cf00
+		g_weak_ref_init (&info->media_ref, media);
David Woodhouse 313cf00
+		info->session_id = g_strdup (session_id);
David Woodhouse 313cf00
+		info->participant = g_strdup (participant);
David Woodhouse 313cf00
+		g_cond_init (&info->readable_cond);
David Woodhouse 313cf00
+		manager->priv->appdata_info = g_list_prepend (
David Woodhouse 313cf00
+			manager->priv->appdata_info, info);
David Woodhouse 313cf00
+	}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	return info;
David Woodhouse 313cf00
+}
David Woodhouse 313cf00
+#endif
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+
David Woodhouse 313cf00
 #ifdef USE_VV
David Woodhouse 313cf00
 static void
David Woodhouse 313cf00
 request_pad_unlinked_cb(GstPad *pad, GstPad *peer, gpointer user_data)
David Woodhouse 313cf00
@@ -587,6 +731,351 @@
David Woodhouse 313cf00
 #endif
David Woodhouse 313cf00
 }
David Woodhouse 313cf00
 
David Woodhouse 313cf00
+#ifdef HAVE_MEDIA_APPLICATION
David Woodhouse 313cf00
+/*
David Woodhouse 313cf00
+ * Calls the appdata writable callback from the main thread.
David Woodhouse 313cf00
+ * This needs to grab the appdata lock and make sure it didn't get destroyed
David Woodhouse 313cf00
+ * before calling the callback.
David Woodhouse 313cf00
+ */
David Woodhouse 313cf00
+static gboolean
David Woodhouse 313cf00
+appsrc_writable (gpointer user_data)
David Woodhouse 313cf00
+{
David Woodhouse 313cf00
+	PurpleMediaManager *manager = purple_media_manager_get ();
David Woodhouse 313cf00
+	PurpleMediaAppDataInfo *info = user_data;
David Woodhouse 313cf00
+	void (*writable_cb) (PurpleMediaManager *manager, PurpleMedia *media,
David Woodhouse 313cf00
+		const gchar *session_id, const gchar *participant, gboolean writable,
David Woodhouse 313cf00
+		gpointer user_data);
David Woodhouse 313cf00
+	PurpleMedia *media;
David Woodhouse 313cf00
+	gchar *session_id;
David Woodhouse 313cf00
+	gchar *participant;
David Woodhouse 313cf00
+	gboolean writable;
David Woodhouse 313cf00
+	gpointer cb_data;
David Woodhouse 313cf00
+	guint *timer_id_ptr = &info->writable_timer_id;
David Woodhouse 313cf00
+	guint timer_id = *timer_id_ptr;
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	g_mutex_lock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+	if (timer_id == 0 || timer_id != *timer_id_ptr) {
David Woodhouse 313cf00
+		/* In case info was freed while we were waiting for the mutex to unlock
David Woodhouse 313cf00
+		 * we still have a pointer to the timer_id which should still be
David Woodhouse 313cf00
+		 * accessible since it's in the Glib slice allocator. It gets set to 0
David Woodhouse 313cf00
+		 * just after the timeout is canceled which happens also before the
David Woodhouse 313cf00
+		 * AppDataInfo is freed, so even if that memory slice gets reused, the
David Woodhouse 313cf00
+		 * timer_id would be different from its previous value (unless
David Woodhouse 313cf00
+		 * extremely unlucky). So checking if the value for the timer_id changed
David Woodhouse 313cf00
+		 * should be enough to prevent any kind of race condition in which the
David Woodhouse 313cf00
+		 * media/AppDataInfo gets destroyed in one thread while the timeout was
David Woodhouse 313cf00
+		 * triggered and is waiting on the mutex to get unlocked in this thread
David Woodhouse 313cf00
+		 */
David Woodhouse 313cf00
+		g_mutex_unlock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+		return FALSE;
David Woodhouse 313cf00
+	}
David Woodhouse 313cf00
+	writable_cb = info->callbacks.writable;
David Woodhouse 313cf00
+	media = g_weak_ref_get (&info->media_ref);
David Woodhouse 313cf00
+	session_id = g_strdup (info->session_id);
David Woodhouse 313cf00
+	participant = g_strdup (info->participant);
David Woodhouse 313cf00
+	writable = info->writable && info->connected;
David Woodhouse 313cf00
+	cb_data = info->user_data;
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+    info->writable_timer_id = 0;
David Woodhouse 313cf00
+	g_mutex_unlock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	if (writable_cb && media)
David Woodhouse 313cf00
+		writable_cb (manager, media, session_id, participant, writable,
David Woodhouse 313cf00
+			cb_data);
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	g_object_unref (media);
David Woodhouse 313cf00
+	g_free (session_id);
David Woodhouse 313cf00
+	g_free (participant);
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	return FALSE;
David Woodhouse 313cf00
+}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+/*
David Woodhouse 313cf00
+ * Schedule a writable callback to be called from the main thread.
David Woodhouse 313cf00
+ * We need to do this because need-data/enough-data signals from appsrc
David Woodhouse 313cf00
+ * will come from the streaming thread and we need to create
David Woodhouse 313cf00
+ * a source that we attach to the main context but we can't use
David Woodhouse 313cf00
+ * g_main_context_invoke since we need to be able to cancel the source if the
David Woodhouse 313cf00
+ * media gets destroyed.
David Woodhouse 313cf00
+ * We use a timeout source instead of idle source, so the callback gets a higher
David Woodhouse 313cf00
+ * priority
David Woodhouse 313cf00
+ */
David Woodhouse 313cf00
+static void
David Woodhouse 313cf00
+call_appsrc_writable_locked (PurpleMediaAppDataInfo *info)
David Woodhouse 313cf00
+{
David Woodhouse 313cf00
+	/* We already have a writable callback scheduled, don't create another one */
David Woodhouse 313cf00
+	if (info->writable_timer_id || info->callbacks.writable == NULL)
David Woodhouse 313cf00
+		return;
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	info->writable_timer_id = purple_timeout_add (0, appsrc_writable, info);
David Woodhouse 313cf00
+}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+static void
David Woodhouse 313cf00
+appsrc_need_data (GstAppSrc *appsrc, guint length, gpointer user_data)
David Woodhouse 313cf00
+{
David Woodhouse 313cf00
+	PurpleMediaAppDataInfo *info = user_data;
David Woodhouse 313cf00
+	PurpleMediaManager *manager = purple_media_manager_get ();
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	g_mutex_lock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+	if (!info->writable) {
David Woodhouse 313cf00
+		info->writable = TRUE;
David Woodhouse 313cf00
+		/* Only signal writable if we also established a connection */
David Woodhouse 313cf00
+		if (info->connected)
David Woodhouse 313cf00
+			call_appsrc_writable_locked (info);
David Woodhouse 313cf00
+	}
David Woodhouse 313cf00
+	g_mutex_unlock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+static void
David Woodhouse 313cf00
+appsrc_enough_data (GstAppSrc *appsrc, gpointer user_data)
David Woodhouse 313cf00
+{
David Woodhouse 313cf00
+	PurpleMediaAppDataInfo *info = user_data;
David Woodhouse 313cf00
+	PurpleMediaManager *manager = purple_media_manager_get ();
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	g_mutex_lock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+	if (info->writable) {
David Woodhouse 313cf00
+		info->writable = FALSE;
David Woodhouse 313cf00
+		call_appsrc_writable_locked (info);
David Woodhouse 313cf00
+	}
David Woodhouse 313cf00
+	g_mutex_unlock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+static gboolean
David Woodhouse 313cf00
+appsrc_seek_data (GstAppSrc *appsrc, guint64 offset, gpointer user_data)
David Woodhouse 313cf00
+{
David Woodhouse 313cf00
+	return FALSE;
David Woodhouse 313cf00
+}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+static void
David Woodhouse 313cf00
+appsrc_destroyed (PurpleMediaAppDataInfo *info)
David Woodhouse 313cf00
+{
David Woodhouse 313cf00
+	PurpleMediaManager *manager = purple_media_manager_get ();
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	g_mutex_lock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+	info->appsrc = NULL;
David Woodhouse 313cf00
+	if (info->writable) {
David Woodhouse 313cf00
+		info->writable = FALSE;
David Woodhouse 313cf00
+		call_appsrc_writable_locked (info);
David Woodhouse 313cf00
+	}
David Woodhouse 313cf00
+	g_mutex_unlock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+static void
David Woodhouse 313cf00
+media_established_cb (PurpleMedia *media,const gchar *session_id,
David Woodhouse 313cf00
+	const gchar *participant, PurpleMediaCandidate *local_candidate,
David Woodhouse 313cf00
+	PurpleMediaCandidate *remote_candidate, PurpleMediaAppDataInfo *info)
David Woodhouse 313cf00
+{
David Woodhouse 313cf00
+	PurpleMediaManager *manager = purple_media_manager_get ();
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	g_mutex_lock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+	info->connected = TRUE;
David Woodhouse 313cf00
+	/* We established the connection, if we were writable, then we need to
David Woodhouse 313cf00
+	 * signal it now */
David Woodhouse 313cf00
+	if (info->writable)
David Woodhouse 313cf00
+		call_appsrc_writable_locked (info);
David Woodhouse 313cf00
+	g_mutex_unlock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+static GstElement *
David Woodhouse 313cf00
+create_send_appsrc(PurpleMedia *media,
David Woodhouse 313cf00
+		const gchar *session_id, const gchar *participant)
David Woodhouse 313cf00
+{
David Woodhouse 313cf00
+	PurpleMediaManager *manager = purple_media_manager_get ();
David Woodhouse 313cf00
+	PurpleMediaAppDataInfo * info = ensure_app_data_info_and_lock (manager,
David Woodhouse 313cf00
+		media, session_id, participant);
David Woodhouse 313cf00
+	GstElement *appsrc = (GstElement *)info->appsrc;
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	if (appsrc == NULL) {
David Woodhouse 313cf00
+		GstAppSrcCallbacks callbacks = {appsrc_need_data, appsrc_enough_data,
David Woodhouse 313cf00
+										appsrc_seek_data, {NULL}};
David Woodhouse 313cf00
+		GstCaps *caps = gst_caps_new_empty_simple ("application/octet-stream");
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+		appsrc = gst_element_factory_make("appsrc", NULL);
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+		info->appsrc = (GstAppSrc *)appsrc;
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+		gst_app_src_set_caps (info->appsrc, caps);
David Woodhouse 313cf00
+		gst_app_src_set_callbacks (info->appsrc,
David Woodhouse 313cf00
+			&callbacks, info, (GDestroyNotify) appsrc_destroyed);
David Woodhouse 313cf00
+		g_signal_connect (media, "candidate-pair-established",
David Woodhouse 313cf00
+			(GCallback) media_established_cb, info);
David Woodhouse 313cf00
+		gst_caps_unref (caps);
David Woodhouse 313cf00
+	}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	g_mutex_unlock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+	return appsrc;
David Woodhouse 313cf00
+}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+static void
David Woodhouse 313cf00
+appsink_eos (GstAppSink *appsink, gpointer user_data)
David Woodhouse 313cf00
+{
David Woodhouse 313cf00
+}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+static GstFlowReturn
David Woodhouse 313cf00
+appsink_new_preroll (GstAppSink *appsink, gpointer user_data)
David Woodhouse 313cf00
+{
David Woodhouse 313cf00
+	return GST_FLOW_OK;
David Woodhouse 313cf00
+}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+static gboolean
David Woodhouse 313cf00
+appsink_readable (gpointer user_data)
David Woodhouse 313cf00
+{
David Woodhouse 313cf00
+	PurpleMediaManager *manager = purple_media_manager_get ();
David Woodhouse 313cf00
+	PurpleMediaAppDataInfo *info = user_data;
David Woodhouse 313cf00
+	void (*readable_cb) (PurpleMediaManager *manager, PurpleMedia *media,
David Woodhouse 313cf00
+		const gchar *session_id, const gchar *participant, gpointer user_data);
David Woodhouse 313cf00
+	PurpleMedia *media;
David Woodhouse 313cf00
+	gchar *session_id;
David Woodhouse 313cf00
+	gchar *participant;
David Woodhouse 313cf00
+	gpointer cb_data;
David Woodhouse 313cf00
+	guint *timer_id_ptr = &info->readable_timer_id;
David Woodhouse 313cf00
+	guint timer_id = *timer_id_ptr;
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	g_mutex_lock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+	if (timer_id == 0 || timer_id != *timer_id_ptr) {
David Woodhouse 313cf00
+		/* Avoided a race condition (see writable callback) */
David Woodhouse 313cf00
+		g_mutex_unlock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+		return FALSE;
David Woodhouse 313cf00
+	}
David Woodhouse 313cf00
+	/* We need to signal readable until there are no more samples */
David Woodhouse 313cf00
+	while (info->callbacks.readable &&
David Woodhouse 313cf00
+		(info->num_samples > 0 || info->current_sample != NULL)) {
David Woodhouse 313cf00
+		readable_cb = info->callbacks.readable;
David Woodhouse 313cf00
+		media = g_weak_ref_get (&info->media_ref);
David Woodhouse 313cf00
+		session_id = g_strdup (info->session_id);
David Woodhouse 313cf00
+		participant = g_strdup (info->participant);
David Woodhouse 313cf00
+		cb_data = info->user_data;
David Woodhouse 313cf00
+		g_mutex_unlock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+		if (readable_cb)
David Woodhouse 313cf00
+			readable_cb (manager, media, session_id, participant, cb_data);
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+		g_mutex_lock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+		g_object_unref (media);
David Woodhouse 313cf00
+		g_free (session_id);
David Woodhouse 313cf00
+		g_free (participant);
David Woodhouse 313cf00
+		if (timer_id == 0 || timer_id != *timer_id_ptr) {
David Woodhouse 313cf00
+			/* We got cancelled */
David Woodhouse 313cf00
+			g_mutex_unlock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+			return FALSE;
David Woodhouse 313cf00
+		}
David Woodhouse 313cf00
+	}
David Woodhouse 313cf00
+    info->readable_timer_id = 0;
David Woodhouse 313cf00
+	g_mutex_unlock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+	return FALSE;
David Woodhouse 313cf00
+}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+static void
David Woodhouse 313cf00
+call_appsink_readable_locked (PurpleMediaAppDataInfo *info)
David Woodhouse 313cf00
+{
David Woodhouse 313cf00
+	/* We must signal that a new sample has arrived to release blocking reads */
David Woodhouse 313cf00
+	g_cond_broadcast (&info->readable_cond);
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	/* We already have a writable callback scheduled, don't create another one */
David Woodhouse 313cf00
+	if (info->readable_timer_id || info->callbacks.readable == NULL)
David Woodhouse 313cf00
+		return;
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	info->readable_timer_id = purple_timeout_add (0, appsink_readable, info);
David Woodhouse 313cf00
+}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+static GstFlowReturn
David Woodhouse 313cf00
+appsink_new_sample (GstAppSink *appsink, gpointer user_data)
David Woodhouse 313cf00
+{
David Woodhouse 313cf00
+	PurpleMediaManager *manager = purple_media_manager_get ();
David Woodhouse 313cf00
+	PurpleMediaAppDataInfo *info = user_data;
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	g_mutex_lock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+	info->num_samples++;
David Woodhouse 313cf00
+	call_appsink_readable_locked (info);
David Woodhouse 313cf00
+	g_mutex_unlock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	return GST_FLOW_OK;
David Woodhouse 313cf00
+}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+static void
David Woodhouse 313cf00
+appsink_destroyed (PurpleMediaAppDataInfo *info)
David Woodhouse 313cf00
+{
David Woodhouse 313cf00
+	PurpleMediaManager *manager = purple_media_manager_get ();
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	g_mutex_lock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+	info->appsink = NULL;
David Woodhouse 313cf00
+	info->num_samples = 0;
David Woodhouse 313cf00
+	g_mutex_unlock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+static GstElement *
David Woodhouse 313cf00
+create_recv_appsink(PurpleMedia *media,
David Woodhouse 313cf00
+		const gchar *session_id, const gchar *participant)
David Woodhouse 313cf00
+{
David Woodhouse 313cf00
+	PurpleMediaManager *manager = purple_media_manager_get ();
David Woodhouse 313cf00
+	PurpleMediaAppDataInfo * info = ensure_app_data_info_and_lock (manager,
David Woodhouse 313cf00
+		media, session_id, participant);
David Woodhouse 313cf00
+	GstElement *appsink = (GstElement *)info->appsink;
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	if (appsink == NULL) {
David Woodhouse 313cf00
+		GstAppSinkCallbacks callbacks = {appsink_eos, appsink_new_preroll,
David Woodhouse 313cf00
+										 appsink_new_sample, {NULL}};
David Woodhouse 313cf00
+		GstCaps *caps = gst_caps_new_empty_simple ("application/octet-stream");
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+		appsink = gst_element_factory_make("appsink", NULL);
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+		info->appsink = (GstAppSink *)appsink;
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+		gst_app_sink_set_caps (info->appsink, caps);
David Woodhouse 313cf00
+		gst_app_sink_set_callbacks (info->appsink,
David Woodhouse 313cf00
+			&callbacks, info, (GDestroyNotify) appsink_destroyed);
David Woodhouse 313cf00
+		gst_caps_unref (caps);
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	g_mutex_unlock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+	return appsink;
David Woodhouse 313cf00
+}
David Woodhouse 313cf00
+#endif
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+static PurpleMediaElementInfo *
David Woodhouse 313cf00
+get_send_application_element_info ()
David Woodhouse 313cf00
+{
David Woodhouse 313cf00
+	static PurpleMediaElementInfo *info = NULL;
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+#ifdef HAVE_MEDIA_APPLICATION
David Woodhouse 313cf00
+	if (info == NULL) {
David Woodhouse 313cf00
+		info = g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO,
David Woodhouse 313cf00
+			"id", "pidginappsrc",
David Woodhouse 313cf00
+			"name", "Pidgin Application Source",
David Woodhouse 313cf00
+			"type", PURPLE_MEDIA_ELEMENT_APPLICATION
David Woodhouse 313cf00
+					| PURPLE_MEDIA_ELEMENT_SRC
David Woodhouse 313cf00
+					| PURPLE_MEDIA_ELEMENT_ONE_SRC,
David Woodhouse 313cf00
+			"create-cb", create_send_appsrc, NULL);
David Woodhouse 313cf00
+	}
David Woodhouse 313cf00
+#endif
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	return info;
David Woodhouse 313cf00
+}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+static PurpleMediaElementInfo *
David Woodhouse 313cf00
+get_recv_application_element_info ()
David Woodhouse 313cf00
+{
David Woodhouse 313cf00
+	static PurpleMediaElementInfo *info = NULL;
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+#ifdef HAVE_MEDIA_APPLICATION
David Woodhouse 313cf00
+	if (info == NULL) {
David Woodhouse 313cf00
+		info = g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO,
David Woodhouse 313cf00
+			"id", "pidginappsink",
David Woodhouse 313cf00
+			"name", "Pidgin Application Sink",
David Woodhouse 313cf00
+			"type", PURPLE_MEDIA_ELEMENT_APPLICATION
David Woodhouse 313cf00
+					| PURPLE_MEDIA_ELEMENT_SINK
David Woodhouse 313cf00
+					| PURPLE_MEDIA_ELEMENT_ONE_SINK,
David Woodhouse 313cf00
+			"create-cb", create_recv_appsink, NULL);
David Woodhouse 313cf00
+	}
David Woodhouse 313cf00
+#endif
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	return info;
David Woodhouse 313cf00
+}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
 GstElement *
David Woodhouse 313cf00
 purple_media_manager_get_element(PurpleMediaManager *manager,
David Woodhouse 313cf00
 		PurpleMediaSessionType type, PurpleMedia *media,
David Woodhouse 313cf00
@@ -605,6 +1094,10 @@
David Woodhouse 313cf00
 		info = manager->priv->video_src;
David Woodhouse 313cf00
 	else if (type & PURPLE_MEDIA_RECV_VIDEO)
David Woodhouse 313cf00
 		info = manager->priv->video_sink;
David Woodhouse 313cf00
+	else if (type & PURPLE_MEDIA_SEND_APPLICATION)
David Woodhouse 313cf00
+		info = get_send_application_element_info ();
David Woodhouse 313cf00
+	else if (type & PURPLE_MEDIA_RECV_APPLICATION)
David Woodhouse 313cf00
+		info = get_recv_application_element_info ();
David Woodhouse 313cf00
 
David Woodhouse 313cf00
 	if (info == NULL)
David Woodhouse 313cf00
 		return NULL;
David Woodhouse 313cf00
@@ -848,11 +1341,16 @@
David Woodhouse 313cf00
 			return manager->priv->audio_src;
David Woodhouse 313cf00
 		else if (type & PURPLE_MEDIA_ELEMENT_VIDEO)
David Woodhouse 313cf00
 			return manager->priv->video_src;
David Woodhouse 313cf00
+		else if (type & PURPLE_MEDIA_ELEMENT_APPLICATION)
David Woodhouse 313cf00
+			return get_send_application_element_info ();
David Woodhouse 313cf00
 	} else if (type & PURPLE_MEDIA_ELEMENT_SINK) {
David Woodhouse 313cf00
 		if (type & PURPLE_MEDIA_ELEMENT_AUDIO)
David Woodhouse 313cf00
 			return manager->priv->audio_sink;
David Woodhouse 313cf00
 		else if (type & PURPLE_MEDIA_ELEMENT_VIDEO)
David Woodhouse 313cf00
 			return manager->priv->video_sink;
David Woodhouse 313cf00
+		else if (type & PURPLE_MEDIA_ELEMENT_APPLICATION)
David Woodhouse 313cf00
+			return get_recv_application_element_info ();
David Woodhouse 313cf00
+
David Woodhouse 313cf00
 	}
David Woodhouse 313cf00
 #endif
David Woodhouse 313cf00
 
David Woodhouse 313cf00
@@ -1152,6 +1650,174 @@
David Woodhouse 313cf00
 #endif
David Woodhouse 313cf00
 }
David Woodhouse 313cf00
 
David Woodhouse 313cf00
+void
David Woodhouse 313cf00
+purple_media_manager_set_application_data_callbacks(PurpleMediaManager *manager,
David Woodhouse 313cf00
+		PurpleMedia *media, const gchar *session_id,
David Woodhouse 313cf00
+		const gchar *participant, PurpleMediaAppDataCallbacks *callbacks,
David Woodhouse 313cf00
+		gpointer user_data, GDestroyNotify notify)
David Woodhouse 313cf00
+{
David Woodhouse 313cf00
+#ifdef HAVE_MEDIA_APPLICATION
David Woodhouse 313cf00
+	PurpleMediaAppDataInfo * info = ensure_app_data_info_and_lock (manager,
David Woodhouse 313cf00
+		media, session_id, participant);
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	if (info->notify)
David Woodhouse 313cf00
+		info->notify (info->user_data);
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	if (info->readable_timer_id) {
David Woodhouse 313cf00
+		purple_timeout_remove (info->readable_timer_id);
David Woodhouse 313cf00
+		info->readable_timer_id = 0;
David Woodhouse 313cf00
+	}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	if (info->writable_timer_id) {
David Woodhouse 313cf00
+		purple_timeout_remove (info->writable_timer_id);
David Woodhouse 313cf00
+		info->writable_timer_id = 0;
David Woodhouse 313cf00
+	}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	if (callbacks) {
David Woodhouse 313cf00
+		info->callbacks = *callbacks;
David Woodhouse 313cf00
+	} else {
David Woodhouse 313cf00
+		info->callbacks.writable = NULL;
David Woodhouse 313cf00
+		info->callbacks.readable = NULL;
David Woodhouse 313cf00
+	}
David Woodhouse 313cf00
+	info->user_data = user_data;
David Woodhouse 313cf00
+	info->notify = notify;
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	call_appsrc_writable_locked (info);
David Woodhouse 313cf00
+	if (info->num_samples > 0 || info->current_sample != NULL)
David Woodhouse 313cf00
+		call_appsink_readable_locked (info);
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	g_mutex_unlock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+#endif
David Woodhouse 313cf00
+}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+gint
David Woodhouse 313cf00
+purple_media_manager_send_application_data (
David Woodhouse 313cf00
+	PurpleMediaManager *manager, PurpleMedia *media, const gchar *session_id,
David Woodhouse 313cf00
+	const gchar *participant, gpointer buffer, guint size, gboolean blocking)
David Woodhouse 313cf00
+{
David Woodhouse 313cf00
+#ifdef HAVE_MEDIA_APPLICATION
David Woodhouse 313cf00
+	PurpleMediaAppDataInfo * info = get_app_data_info_and_lock (manager,
David Woodhouse 313cf00
+		media, session_id, participant);
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	if (info && info->appsrc && info->connected) {
David Woodhouse 313cf00
+		GstBuffer *gstbuffer = gst_buffer_new_wrapped (g_memdup (buffer, size),
David Woodhouse 313cf00
+			size);
David Woodhouse 313cf00
+		GstAppSrc *appsrc = gst_object_ref (info->appsrc);
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+		g_mutex_unlock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+		if (gst_app_src_push_buffer (appsrc, gstbuffer) == GST_FLOW_OK) {
David Woodhouse 313cf00
+			if (blocking) {
David Woodhouse 313cf00
+				GstPad *srcpad;
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+				srcpad = gst_element_get_static_pad (GST_ELEMENT (appsrc),
David Woodhouse 313cf00
+					"src");
David Woodhouse 313cf00
+				if (srcpad) {
David Woodhouse 313cf00
+					gst_pad_peer_query (srcpad, gst_query_new_drain ());
David Woodhouse 313cf00
+					gst_object_unref (srcpad);
David Woodhouse 313cf00
+				}
David Woodhouse 313cf00
+			}
David Woodhouse 313cf00
+			gst_object_unref (appsrc);
David Woodhouse 313cf00
+			return size;
David Woodhouse 313cf00
+		} else {
David Woodhouse 313cf00
+			gst_object_unref (appsrc);
David Woodhouse 313cf00
+			return -1;
David Woodhouse 313cf00
+		}
David Woodhouse 313cf00
+	}
David Woodhouse 313cf00
+	g_mutex_unlock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+	return -1;
David Woodhouse 313cf00
+#else
David Woodhouse 313cf00
+	return -1;
David Woodhouse 313cf00
+#endif
David Woodhouse 313cf00
+}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+gint
David Woodhouse 313cf00
+purple_media_manager_receive_application_data (
David Woodhouse 313cf00
+	PurpleMediaManager *manager, PurpleMedia *media, const gchar *session_id,
David Woodhouse 313cf00
+	const gchar *participant, gpointer buffer, guint max_size,
David Woodhouse 313cf00
+	gboolean blocking)
David Woodhouse 313cf00
+{
David Woodhouse 313cf00
+#ifdef HAVE_MEDIA_APPLICATION
David Woodhouse 313cf00
+	PurpleMediaAppDataInfo * info = get_app_data_info_and_lock (manager,
David Woodhouse 313cf00
+		media, session_id, participant);
David Woodhouse 313cf00
+	guint bytes_read = 0;
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+	if (info) {
David Woodhouse 313cf00
+		/* If we are in a blocking read, we need to loop until max_size data
David Woodhouse 313cf00
+		 * is read into the buffer, if we're not, then we need to read as much
David Woodhouse 313cf00
+		 * data as possible
David Woodhouse 313cf00
+		 */
David Woodhouse 313cf00
+		do {
David Woodhouse 313cf00
+			if (!info->current_sample && info->appsink && info->num_samples > 0) {
David Woodhouse 313cf00
+				info->current_sample = gst_app_sink_pull_sample (info->appsink);
David Woodhouse 313cf00
+				info->sample_offset = 0;
David Woodhouse 313cf00
+				if (info->current_sample)
David Woodhouse 313cf00
+					info->num_samples--;
David Woodhouse 313cf00
+			}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+			if (info->current_sample) {
David Woodhouse 313cf00
+				GstBuffer *gstbuffer = gst_sample_get_buffer (
David Woodhouse 313cf00
+					info->current_sample);
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+				if (gstbuffer) {
David Woodhouse 313cf00
+					GstMapInfo mapinfo;
David Woodhouse 313cf00
+					guint bytes_to_copy;
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+					gst_buffer_map (gstbuffer, &mapinfo, GST_MAP_READ);
David Woodhouse 313cf00
+					/* We must copy only the data remaining in the buffer without
David Woodhouse 313cf00
+					 * overflowing the buffer */
David Woodhouse 313cf00
+					bytes_to_copy = max_size - bytes_read;
David Woodhouse 313cf00
+					if (bytes_to_copy > mapinfo.size - info->sample_offset)
David Woodhouse 313cf00
+						bytes_to_copy = mapinfo.size - info->sample_offset;
David Woodhouse 313cf00
+					memcpy ((guint8 *)buffer + bytes_read,
David Woodhouse 313cf00
+						mapinfo.data + info->sample_offset,	bytes_to_copy);
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+					gst_buffer_unmap (gstbuffer, &mapinfo);
David Woodhouse 313cf00
+					info->sample_offset += bytes_to_copy;
David Woodhouse 313cf00
+					bytes_read += bytes_to_copy;
David Woodhouse 313cf00
+					if (info->sample_offset == mapinfo.size) {
David Woodhouse 313cf00
+						gst_sample_unref (info->current_sample);
David Woodhouse 313cf00
+						info->current_sample = NULL;
David Woodhouse 313cf00
+						info->sample_offset = 0;
David Woodhouse 313cf00
+					}
David Woodhouse 313cf00
+				} else {
David Woodhouse 313cf00
+					/* In case there's no buffer in the sample (should never
David Woodhouse 313cf00
+					 * happen), we need to at least unref it */
David Woodhouse 313cf00
+					gst_sample_unref (info->current_sample);
David Woodhouse 313cf00
+					info->current_sample = NULL;
David Woodhouse 313cf00
+					info->sample_offset = 0;
David Woodhouse 313cf00
+				}
David Woodhouse 313cf00
+			}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+			/* If blocking, wait until there's an available sample */
David Woodhouse 313cf00
+			while (bytes_read < max_size && blocking &&
David Woodhouse 313cf00
+				info->current_sample == NULL && info->num_samples == 0) {
David Woodhouse 313cf00
+				g_cond_wait (&info->readable_cond, &manager->priv->appdata_mutex);
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+				/* We've been signaled, we need to unlock and regrab the info
David Woodhouse 313cf00
+				 * struct to make sure nothing changed */
David Woodhouse 313cf00
+				g_mutex_unlock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+				info = get_app_data_info_and_lock (manager,
David Woodhouse 313cf00
+					media, session_id, participant);
David Woodhouse 313cf00
+				if (info == NULL || info->appsink == NULL) {
David Woodhouse 313cf00
+					/* The session was destroyed while we were waiting, we
David Woodhouse 313cf00
+					 * should return here */
David Woodhouse 313cf00
+					g_mutex_unlock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+					return bytes_read;
David Woodhouse 313cf00
+				}
David Woodhouse 313cf00
+			}
David Woodhouse 313cf00
+		} while (bytes_read < max_size &&
David Woodhouse 313cf00
+			(blocking || info->num_samples > 0));
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+		g_mutex_unlock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+		return bytes_read;
David Woodhouse 313cf00
+	}
David Woodhouse 313cf00
+	g_mutex_unlock (&manager->priv->appdata_mutex);
David Woodhouse 313cf00
+	return -1;
David Woodhouse 313cf00
+#else
David Woodhouse 313cf00
+	return -1;
David Woodhouse 313cf00
+#endif
David Woodhouse 313cf00
+}
David Woodhouse 313cf00
+
David Woodhouse 313cf00
 #ifdef USE_GSTREAMER
David Woodhouse 313cf00
 
David Woodhouse 313cf00
 /*
David Woodhouse 313cf00
@@ -1199,6 +1865,8 @@
David Woodhouse 313cf00
 				"PURPLE_MEDIA_ELEMENT_SRC", "src" },
David Woodhouse 313cf00
 			{ PURPLE_MEDIA_ELEMENT_SINK,
David Woodhouse 313cf00
 				"PURPLE_MEDIA_ELEMENT_SINK", "sink" },
David Woodhouse 313cf00
+			{ PURPLE_MEDIA_ELEMENT_APPLICATION,
David Woodhouse 313cf00
+				"PURPLE_MEDIA_ELEMENT_APPLICATION", "application" },
David Woodhouse 313cf00
 			{ 0, NULL, NULL }
David Woodhouse 313cf00
 		};
David Woodhouse 313cf00
 		type = g_flags_register_static(
David Woodhouse 313cf00
@@ -1424,5 +2092,6 @@
David Woodhouse 313cf00
 	return NULL;
David Woodhouse 313cf00
 }
David Woodhouse 313cf00
 
David Woodhouse 313cf00
+
David Woodhouse 313cf00
 #endif /* USE_GSTREAMER */
David Woodhouse 313cf00
 
David Woodhouse 313cf00
diff --git a/libpurple/mediamanager.h b/libpurple/mediamanager.h
David Woodhouse 313cf00
--- a/libpurple/mediamanager.h
David Woodhouse 313cf00
+++ b/libpurple/mediamanager.h
David Woodhouse 313cf00
@@ -38,6 +38,30 @@
David Woodhouse 313cf00
 #include "account.h"
David Woodhouse 313cf00
 #include "media.h"
David Woodhouse 313cf00
 
David Woodhouse 313cf00
+/**
David Woodhouse 313cf00
+ * PurpleMediaAppDataCallbacks:
David Woodhouse 313cf00
+ * @readable: Called when the stream has received data and is readable.
David Woodhouse 313cf00
+ * @writable: Called when the stream has become writable or has stopped being
David Woodhouse 313cf00
+ * writable.
David Woodhouse 313cf00
+ *
David Woodhouse 313cf00
+ * A set of callbacks that can be installed on an Application data session with
David Woodhouse 313cf00
+ * purple_media_manager_set_application_data_callbacks()
David Woodhouse 313cf00
+ *
David Woodhouse 313cf00
+ * Once installed the @readable callback will get called as long as data is
David Woodhouse 313cf00
+ * available to read, so the data must be read completely.
David Woodhouse 313cf00
+ * The @writable callback will only be called when the writable state of the
David Woodhouse 313cf00
+ * stream changes. The @writable argument defines whether the stream has
David Woodhouse 313cf00
+ * become writable or stopped being writable.
David Woodhouse 313cf00
+ *
David Woodhouse 313cf00
+ */
David Woodhouse 313cf00
+typedef struct {
David Woodhouse 313cf00
+	void      (*readable)    (PurpleMediaManager *manager, PurpleMedia *media,
David Woodhouse 313cf00
+		const gchar *session_id, const gchar *participant, gpointer user_data);
David Woodhouse 313cf00
+	void      (*writable)    (PurpleMediaManager *manager, PurpleMedia *media,
David Woodhouse 313cf00
+		const gchar *session_id, const gchar *participant, gboolean writable,
David Woodhouse 313cf00
+		gpointer user_data);
David Woodhouse 313cf00
+} PurpleMediaAppDataCallbacks;
David Woodhouse 313cf00
+
David Woodhouse 313cf00
 G_BEGIN_DECLS
David Woodhouse 313cf00
 
David Woodhouse 313cf00
 #define PURPLE_TYPE_MEDIA_MANAGER            (purple_media_manager_get_type())
David Woodhouse 313cf00
@@ -285,6 +309,71 @@
David Woodhouse 313cf00
  */
David Woodhouse 313cf00
 GType purple_media_manager_get_backend_type(PurpleMediaManager *manager);
David Woodhouse 313cf00
 
David Woodhouse 313cf00
+/**
David Woodhouse 313cf00
+ * purple_media_manager_set_application_data_callbacks:
David Woodhouse 313cf00
+ * @manager: The manager to register the callbacks with.
David Woodhouse 313cf00
+ * @media: The media instance to register the callbacks with.
David Woodhouse 313cf00
+ * @session_id: The session to register the callbacks with.
David Woodhouse 313cf00
+ * @participant: The participant to register the callbacks with.
David Woodhouse 313cf00
+ * @callbacks: The callbacks to be set on the session.
David Woodhouse 313cf00
+ * @user_data: a user_data argument for the callbacks.
David Woodhouse 313cf00
+ * @notify: a destroy notify function.
David Woodhouse 313cf00
+ *
David Woodhouse 313cf00
+ * Set callbacks on a session to be called when the stream becomes writable
David Woodhouse 313cf00
+ * or readable for media sessions of type #PURPLE_MEDIA_APPLICATION
David Woodhouse 313cf00
+ */
David Woodhouse 313cf00
+void purple_media_manager_set_application_data_callbacks(
David Woodhouse 313cf00
+	PurpleMediaManager *manager, PurpleMedia *media, const gchar *session_id,
David Woodhouse 313cf00
+	const gchar *participant, PurpleMediaAppDataCallbacks *callbacks,
David Woodhouse 313cf00
+	gpointer user_data, GDestroyNotify notify);
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+/**
David Woodhouse 313cf00
+ * purple_media_manager_send_application_data:
David Woodhouse 313cf00
+ * @manager: The manager to send data with.
David Woodhouse 313cf00
+ * @media: The media instance to which the session belongs.
David Woodhouse 313cf00
+ * @session_id: The session to send data to.
David Woodhouse 313cf00
+ * @participant: The participant to send data to.
David Woodhouse 313cf00
+ * @buffer: The buffer of data to send.
David Woodhouse 313cf00
+ * @size: The size of @buffer
David Woodhouse 313cf00
+ * @blocking: Whether to block until the data was send or not.
David Woodhouse 313cf00
+ *
David Woodhouse 313cf00
+ * Sends a buffer of data to a #PURPLE_MEDIA_APPLICATION session.
David Woodhouse 313cf00
+ * If @blocking is set, unless an error occured, the function will not return
David Woodhouse 313cf00
+ * until the data has been flushed into the network.
David Woodhouse 313cf00
+ * If the stream is not writable, the data will be queued. It is the
David Woodhouse 313cf00
+ * responsability of the user to stop sending data when the stream isn't
David Woodhouse 313cf00
+ * writable anymore. It is also the responsability of the user to only start
David Woodhouse 313cf00
+ * sending data after the stream has been configured correctly (encryption
David Woodhouse 313cf00
+ * parameters for example).
David Woodhouse 313cf00
+ *
David Woodhouse 313cf00
+ * Returns: Number of bytes sent or -1 in case of error.
David Woodhouse 313cf00
+ */
David Woodhouse 313cf00
+gint purple_media_manager_send_application_data (
David Woodhouse 313cf00
+	PurpleMediaManager *manager, PurpleMedia *media, const gchar *session_id,
David Woodhouse 313cf00
+	const gchar *participant, gpointer buffer, guint size, gboolean blocking);
David Woodhouse 313cf00
+
David Woodhouse 313cf00
+/**
David Woodhouse 313cf00
+ * purple_media_manager_receive_application_data:
David Woodhouse 313cf00
+ * @manager: The manager to receive data with.
David Woodhouse 313cf00
+ * @media: The media instance to which the session belongs.
David Woodhouse 313cf00
+ * @session_id: The session to receive data from.
David Woodhouse 313cf00
+ * @participant: The participant to receive data from.
David Woodhouse 313cf00
+ * @buffer: The buffer to receive data into.
David Woodhouse 313cf00
+ * @max_size: The max_size of @buffer
David Woodhouse 313cf00
+ * @blocking: Whether to block until the buffer is entirely filled or return
David Woodhouse 313cf00
+ * with currently available data.
David Woodhouse 313cf00
+ *
David Woodhouse 313cf00
+ * Receive a buffer of data from a #PURPLE_MEDIA_APPLICATION session.
David Woodhouse 313cf00
+ * If @blocking is set, unless an error occured, the function will not return
David Woodhouse 313cf00
+ * until @max_size bytes are read.
David Woodhouse 313cf00
+ *
David Woodhouse 313cf00
+ * Returns: Number of bytes received or -1 in case of error.
David Woodhouse 313cf00
+ */
David Woodhouse 313cf00
+gint purple_media_manager_receive_application_data (
David Woodhouse 313cf00
+	PurpleMediaManager *manager, PurpleMedia *media, const gchar *session_id,
David Woodhouse 313cf00
+	const gchar *participant, gpointer buffer, guint max_size,
David Woodhouse 313cf00
+	gboolean blocking);
David Woodhouse 313cf00
+
David Woodhouse 313cf00
 /*}@*/
David Woodhouse 313cf00
 
David Woodhouse 313cf00
 #ifdef __cplusplus
David Woodhouse 313cf00