changeset 25647:e1c8ec1259de

Updates voice and video to use Farsight 2, gets XMPP voice conferences closer to XEP-0167, and fixes a lot of bugs.
author Mike Ruprecht <maiku@soc.pidgin.im>
date Fri, 23 May 2008 02:42:32 +0000
parents 69d54f84350a
children 16543025aed1 f47be691e588
files configure.ac finch/gntmedia.c finch/gntmedia.h libpurple/Makefile.am libpurple/marshallers.list libpurple/media.c libpurple/media.h libpurple/mediamanager.c libpurple/mediamanager.h libpurple/protocols/jabber/google.c libpurple/protocols/jabber/jabber.c libpurple/protocols/jabber/jingle.c libpurple/protocols/jabber/jingle.h pidgin/gtkmedia.c pidgin/gtkmedia.h
diffstat 15 files changed, 842 insertions(+), 704 deletions(-) [+]
line wrap: on
line diff
--- a/configure.ac	Sun Apr 13 17:53:46 2008 +0000
+++ b/configure.ac	Fri May 23 02:42:32 2008 +0000
@@ -318,6 +318,9 @@
 AC_SUBST(GLIB_CFLAGS)
 AC_SUBST(GLIB_LIBS)
 
+GLIB_GENMARSHAL=`pkg-config --variable=glib_genmarshal glib-2.0`
+AC_SUBST(GLIB_GENMARSHAL)
+
 AC_ARG_WITH([extraversion],
 			AC_HELP_STRING([--with-extraversion=STRING],
 						   [extra version number to be displayed in Help->About and --help (for packagers)]),
@@ -721,11 +724,11 @@
 dnl #######################################################################
 dnl # Check for Farsight
 dnl #######################################################################
-AC_ARG_ENABLE(farsight,
+AC_ARG_ENABLE(farsight2,
 	[AC_HELP_STRING([--disable-vv], [compile without voice and video support])],
 	enable_farsight="$enableval", enable_farsight="yes")
 if test "x$enable_farsight" != "xno"; then
-	PKG_CHECK_MODULES(FARSIGHT, [farsight-0.1], [
+	PKG_CHECK_MODULES(FARSIGHT, [farsight2-0.10], [
 		AC_DEFINE(USE_FARSIGHT, 1, [Use Farsight for voice and video])
 		AC_SUBST(FARSIGHT_CFLAGS)
 		AC_SUBST(FARSIGHT_LIBS)
--- a/finch/gntmedia.c	Sun Apr 13 17:53:46 2008 +0000
+++ b/finch/gntmedia.c	Fri May 23 02:42:32 2008 +0000
@@ -42,8 +42,6 @@
 /* An incredibly large part of the following is from gtkmedia.c */
 #ifdef USE_FARSIGHT
 
-#include <farsight/farsight.h>
-
 #undef hangup
 
 struct _FinchMediaPrivate
--- a/finch/gntmedia.h	Sun Apr 13 17:53:46 2008 +0000
+++ b/finch/gntmedia.h	Fri May 23 02:42:32 2008 +0000
@@ -33,7 +33,6 @@
 
 #ifdef USE_FARSIGHT
 
-#include <farsight/farsight.h>
 #include <glib-object.h>
 #include "gntbox.h"
 
--- a/libpurple/Makefile.am	Sun Apr 13 17:53:46 2008 +0000
+++ b/libpurple/Makefile.am	Fri May 23 02:42:32 2008 +0000
@@ -1,6 +1,7 @@
 EXTRA_DIST = \
 		dbus-analyze-functions.py \
 		dbus-analyze-types.py \
+		marshallers.list \
 		purple-notifications-example \
 		purple-remote \
 		purple-send \
@@ -51,6 +52,7 @@
 	idle.c \
 	imgstore.c \
 	log.c \
+	marshallers.c \
 	media.c \
 	mediamanager.c \
 	mime.c \
@@ -105,6 +107,7 @@
 	idle.h \
 	imgstore.h \
 	log.h \
+	marshallers.h \
 	media.h \
 	mediamanager.h \
 	mime.h \
@@ -139,6 +142,14 @@
 
 purple_builtheaders = purple.h version.h
 
+marshallers.h: marshallers.list
+	@echo "Generating marshallers.h"
+	$(GLIB_GENMARSHAL) --prefix=purple_smarshal $(srcdir)/marshallers.list --header > marshallers.h
+
+marshallers.c: marshallers.list
+	@echo "Generating marshallers.c"
+	$(GLIB_GENMARSHAL) --prefix=purple_smarshal $(srcdir)/marshallers.list --body > marshallers.c
+
 if ENABLE_DBUS
 
 CLEANFILES = \
@@ -147,6 +158,8 @@
 	dbus-client-binding.h \
 	dbus-types.c \
 	dbus-types.h \
+	marshallers.c \
+	marshallers.h \
 	purple-client-bindings.c \
 	purple-client-bindings.h \
 	purple.service
@@ -217,6 +230,8 @@
 	dbus-types.c \
 	dbus-types.h \
 	dbus-bindings.c \
+	marshallers.c \
+	marshallers.h \
 	purple-client-bindings.c \
 	purple-client-bindings.h
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/marshallers.list	Fri May 23 02:42:32 2008 +0000
@@ -0,0 +1,1 @@
+VOID:BOXED,BOXED
--- a/libpurple/media.c	Sun Apr 13 17:53:46 2008 +0000
+++ b/libpurple/media.c	Fri May 23 02:42:32 2008 +0000
@@ -29,6 +29,7 @@
 
 #include "connection.h"
 #include "media.h"
+#include "marshallers.h"
 
 #include "debug.h"
 
@@ -36,11 +37,11 @@
 #ifdef USE_GSTPROPS
 
 #include <gst/interfaces/propertyprobe.h>
-#include <farsight/farsight.h>
+#include <gst/farsight/fs-conference-iface.h>
 
 struct _PurpleMediaPrivate
 {
-	FarsightSession *farsight_session;
+	FsConference *conference;
 
 	char *name;
 	PurpleConnection *connection;
@@ -49,8 +50,23 @@
 	GstElement *video_src;
 	GstElement *video_sink;
 
-	FarsightStream *audio_stream;
-	FarsightStream *video_stream;
+	FsSession *audio_session;
+	FsSession *video_session;
+
+	GList *participants; 	/* FsParticipant list */
+	GList *audio_streams;	/* FsStream list */
+	GList *video_streams;	/* FsStream list */
+
+	/* might be able to just combine these two */
+	GstElement *audio_pipeline;
+	GstElement *video_pipeline;
+
+	/* this will need to be stored/handled per stream
+	 * once having multiple streams is supported */
+	GList *local_candidates;
+
+	FsCandidate *local_candidate;
+	FsCandidate *remote_candidate;
 };
 
 #define PURPLE_MEDIA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA, PurpleMediaPrivate))
@@ -73,21 +89,23 @@
 	REJECT,
 	GOT_HANGUP,
 	GOT_ACCEPT,
+	CANDIDATES_PREPARED,
+	CANDIDATE_PAIR,
 	LAST_SIGNAL
 };
 static guint purple_media_signals[LAST_SIGNAL] = {0};
 
 enum {
 	PROP_0,
-	PROP_FARSIGHT_SESSION,
+	PROP_FS_CONFERENCE,
 	PROP_NAME,
 	PROP_CONNECTION,
 	PROP_AUDIO_SRC,
 	PROP_AUDIO_SINK,
 	PROP_VIDEO_SRC,
 	PROP_VIDEO_SINK,
-	PROP_VIDEO_STREAM,
-	PROP_AUDIO_STREAM
+	PROP_VIDEO_SESSION,
+	PROP_AUDIO_SESSION
 };
 
 GType
@@ -113,7 +131,6 @@
 	return type;
 }
 
-
 static void
 purple_media_class_init (PurpleMediaClass *klass)
 {
@@ -124,11 +141,11 @@
 	gobject_class->set_property = purple_media_set_property;
 	gobject_class->get_property = purple_media_get_property;
 
-	g_object_class_install_property(gobject_class, PROP_FARSIGHT_SESSION,
-			g_param_spec_object("farsight-session",
-			"Farsight session",
-			"The FarsightSession associated with this media.",
-			FARSIGHT_TYPE_SESSION,
+	g_object_class_install_property(gobject_class, PROP_FS_CONFERENCE,
+			g_param_spec_object("farsight-conference",
+			"Farsight conference",
+			"The FsConference associated with this media.",
+			FS_TYPE_CONFERENCE,
 			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
 
 	g_object_class_install_property(gobject_class, PROP_NAME,
@@ -172,18 +189,18 @@
 			GST_TYPE_ELEMENT,
 			G_PARAM_READWRITE));
 
-	g_object_class_install_property(gobject_class, PROP_VIDEO_STREAM,
-			g_param_spec_object("video-stream",
+	g_object_class_install_property(gobject_class, PROP_VIDEO_SESSION,
+			g_param_spec_object("video-session",
 			"Video stream",
 			"The FarsightStream used for video",
-			FARSIGHT_TYPE_STREAM,
+			FS_TYPE_SESSION,
 			G_PARAM_READWRITE));
 
-	g_object_class_install_property(gobject_class, PROP_AUDIO_STREAM,
-			g_param_spec_object("audio-stream",
+	g_object_class_install_property(gobject_class, PROP_AUDIO_SESSION,
+			g_param_spec_object("audio-session",
 			"Audio stream",
 			"The FarsightStream used for audio",
-			FARSIGHT_TYPE_STREAM,
+			FS_TYPE_SESSION,
 			G_PARAM_READWRITE));
 
 	purple_media_signals[READY] = g_signal_new("ready", G_TYPE_FROM_CLASS(klass),
@@ -214,6 +231,14 @@
 					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
 					 g_cclosure_marshal_VOID__VOID,
 					 G_TYPE_NONE, 0);
+	purple_media_signals[CANDIDATES_PREPARED] = g_signal_new("candidates-prepared", G_TYPE_FROM_CLASS(klass),
+					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+					 g_cclosure_marshal_VOID__VOID,
+					 G_TYPE_NONE, 0);
+	purple_media_signals[CANDIDATE_PAIR] = g_signal_new("candidate-pair", G_TYPE_FROM_CLASS(klass),
+					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+					 purple_smarshal_VOID__BOXED_BOXED,
+					 G_TYPE_NONE, 2, FS_TYPE_CANDIDATE, FS_TYPE_CANDIDATE);
 
 	g_type_class_add_private(klass, sizeof(PurpleMediaPrivate));
 }
@@ -241,11 +266,11 @@
 	media = PURPLE_MEDIA(object);
 
 	switch (prop_id) {
-		case PROP_FARSIGHT_SESSION:
-			if (media->priv->farsight_session)
-				g_object_unref(media->priv->farsight_session);
-			media->priv->farsight_session = g_value_get_object(value);
-			g_object_ref(media->priv->farsight_session);
+		case PROP_FS_CONFERENCE:
+			if (media->priv->conference)
+				g_object_unref(media->priv->conference);
+			media->priv->conference = g_value_get_object(value);
+			g_object_ref(media->priv->conference);
 			break;
 		case PROP_NAME:
 			g_free(media->priv->name);
@@ -259,12 +284,16 @@
 				gst_object_unref(media->priv->audio_src);
 			media->priv->audio_src = g_value_get_object(value);
 			gst_object_ref(media->priv->audio_src);
+			gst_bin_add(GST_BIN(purple_media_get_audio_pipeline(media)),
+				    media->priv->audio_src);
 			break;
 		case PROP_AUDIO_SINK:
 			if (media->priv->audio_sink)
 				gst_object_unref(media->priv->audio_sink);
 			media->priv->audio_sink = g_value_get_object(value);
 			gst_object_ref(media->priv->audio_sink);
+			gst_bin_add(GST_BIN(purple_media_get_audio_pipeline(media)),
+				    media->priv->audio_sink);
 			break;
 		case PROP_VIDEO_SRC:
 			if (media->priv->video_src)
@@ -278,17 +307,17 @@
 			media->priv->video_sink = g_value_get_object(value);
 			gst_object_ref(media->priv->video_sink);
 			break;
-		case PROP_VIDEO_STREAM:
-			if (media->priv->video_stream)
-				g_object_unref(media->priv->video_stream);
-			media->priv->video_stream = g_value_get_object(value);
-			gst_object_ref(media->priv->video_stream);
+		case PROP_VIDEO_SESSION:
+			if (media->priv->video_session)
+				g_object_unref(media->priv->video_session);
+			media->priv->video_session = g_value_get_object(value);
+			gst_object_ref(media->priv->video_session);
 			break;
-		case PROP_AUDIO_STREAM:
-			if (media->priv->audio_stream)
-				g_object_unref(media->priv->audio_stream);
-			media->priv->audio_stream = g_value_get_object(value);
-			gst_object_ref(media->priv->audio_stream);
+		case PROP_AUDIO_SESSION:
+			if (media->priv->audio_session)
+				g_object_unref(media->priv->audio_session);
+			media->priv->audio_session = g_value_get_object(value);
+			gst_object_ref(media->priv->audio_session);
 			break;
 
 		default:	
@@ -306,8 +335,8 @@
 	media = PURPLE_MEDIA(object);
 
 	switch (prop_id) {
-		case PROP_FARSIGHT_SESSION:
-			g_value_set_object(value, media->priv->farsight_session);
+		case PROP_FS_CONFERENCE:
+			g_value_set_object(value, media->priv->conference);
 			break;
 		case PROP_NAME:
 			g_value_set_string(value, media->priv->name);
@@ -327,11 +356,11 @@
 		case PROP_VIDEO_SINK:
 			g_value_set_object(value, media->priv->video_sink);
 			break;
-		case PROP_VIDEO_STREAM:
-			g_value_set_object(value, media->priv->video_stream);
+		case PROP_VIDEO_SESSION:
+			g_value_set_object(value, media->priv->video_session);
 			break;
-		case PROP_AUDIO_STREAM:
-			g_value_set_object(value, media->priv->audio_stream);
+		case PROP_AUDIO_SESSION:
+			g_value_set_object(value, media->priv->audio_session);
 			break;
 
 		default:	
@@ -415,12 +444,12 @@
 GstElement *
 purple_media_get_audio_pipeline(PurpleMedia *media)
 {
-	FarsightStream *stream;
-	g_object_get(G_OBJECT(media), "audio-stream", &stream, NULL);
-printf("stream: %d\n\n\n", stream);
-GstElement *l = farsight_stream_get_pipeline(stream);
-printf("Element: %d\n", l);
-	return farsight_stream_get_pipeline(stream);
+	if (!media->priv->audio_pipeline) {
+		media->priv->audio_pipeline = gst_pipeline_new(media->priv->name);
+		gst_bin_add(GST_BIN(media->priv->audio_pipeline), GST_ELEMENT(media->priv->conference));
+	}
+
+	return media->priv->audio_pipeline;
 }
 
 PurpleConnection *
@@ -631,5 +660,237 @@
 	purple_debug_info("media", "purple_media_audio_init_recv end\n");
 }
 
+static void
+purple_media_new_local_candidate(FsStream *stream,
+				  FsCandidate *local_candidate,
+				  PurpleMedia *media)
+{
+	purple_debug_info("media", "got new local candidate: %s\n", local_candidate->candidate_id);
+	media->priv->local_candidates = g_list_append(media->priv->local_candidates, 
+						      fs_candidate_copy(local_candidate));
+}
+
+static void
+purple_media_candidates_prepared(FsStream *stream, PurpleMedia *media)
+{
+	g_signal_emit(media, purple_media_signals[CANDIDATES_PREPARED], 0);
+}
+
+/* callback called when a pair of transport candidates (local and remote)
+ * has been established */
+static void
+purple_media_candidate_pair_established(FsStream *stream,
+					 FsCandidate *native_candidate,
+					 FsCandidate *remote_candidate,
+					 PurpleMedia *media)
+{
+	media->priv->local_candidate = fs_candidate_copy(native_candidate);
+	media->priv->remote_candidate = fs_candidate_copy(remote_candidate);
+
+	purple_debug_info("media", "candidate pair established\n");
+	g_signal_emit(media, purple_media_signals[CANDIDATE_PAIR], 0,
+		      media->priv->local_candidate,
+		      media->priv->remote_candidate);
+}
+
+static void
+purple_media_src_pad_added(FsStream *stream, GstPad *srcpad,
+			    FsCodec *codec, PurpleMedia *media)
+{
+	GstElement *pipeline = purple_media_get_audio_pipeline(media);
+	GstPad *sinkpad = gst_element_get_static_pad(purple_media_get_audio_sink(media), "ghostsink");
+	purple_debug_info("media", "connecting new src pad: %s\n", 
+			  gst_pad_link(srcpad, sinkpad) == GST_PAD_LINK_OK ? "success" : "failure");
+	gst_element_set_state(pipeline, GST_STATE_PLAYING);
+}
+
+static void
+purple_media_add_stream_internal(PurpleMedia *media, FsSession **session, GList **streams,
+				 GstElement *src, const gchar *who, FsMediaType type,
+				 FsStreamDirection type_direction, const gchar *transmitter)
+{
+	char *cname = NULL;
+	FsParticipant *participant = NULL;
+	GList *l = NULL;
+	FsStream *stream = NULL;
+	FsParticipant *p = NULL;
+	FsStreamDirection *direction = NULL;
+	FsSession *s = NULL;
+
+	if (!*session) {
+		*session = fs_conference_new_session(media->priv->conference, type, NULL);
+		if (src) {
+			GstPad *sinkpad;
+			GstPad *srcpad;
+			g_object_get(*session, "sink-pad", &sinkpad, NULL);
+			srcpad = gst_element_get_static_pad(src, "ghostsrc");
+			purple_debug_info("media", "connecting pad: %s\n", 
+					  gst_pad_link(srcpad, sinkpad) == GST_PAD_LINK_OK
+					  ? "success" : "failure");
+		}
+	}
+	
+	for (l = media->priv->participants; l != NULL; l = g_list_next(l)) {
+		g_object_get(l->data, "cname", cname, NULL);
+		if (!strcmp(cname, who)) {
+			g_free(cname);
+			participant = l->data;
+			break;
+		}
+		g_free(cname);
+	}
+
+	if (!participant) {
+		participant = fs_conference_new_participant(media->priv->conference, (gchar*)who, NULL);
+		media->priv->participants = g_list_prepend(media->priv->participants, participant);
+	}
+	
+	for (l = *streams; l != NULL; l = g_list_next(l)) {
+		g_object_get(l->data, "participant", &p, "direction", &direction, "session", &s, NULL);
+
+		if (participant == p && *session == s) {
+			stream = l->data;
+			break;
+		}
+	}
+
+	if (!stream) {
+		stream = fs_session_new_stream(*session, participant, 
+					       type_direction, transmitter, 0, NULL, NULL);
+		*streams = g_list_prepend(*streams, stream);
+		/* callback for new local candidate (new local candidate retreived) */
+		g_signal_connect(G_OBJECT(stream),
+				 "new-local-candidate", G_CALLBACK(purple_media_new_local_candidate), media);
+		/* callback for source pad added (new stream source ready) */
+		g_signal_connect(G_OBJECT(stream),
+				 "src-pad-added", G_CALLBACK(purple_media_src_pad_added), media);
+		/* callback for local candidates prepared (local candidates ready to send) */
+		g_signal_connect(G_OBJECT(stream), 
+				 "local-candidates-prepared", 
+				 G_CALLBACK(purple_media_candidates_prepared), media);
+		/* callback for new active candidate pair (established connection) */
+		g_signal_connect(G_OBJECT(stream),
+				 "new-active-candidate-pair", 
+				 G_CALLBACK(purple_media_candidate_pair_established), media);
+	} else if (*direction != type_direction) {	
+		/* change direction */
+		g_object_set(stream, "direction", type_direction, NULL);
+	}
+}
+
+void
+purple_media_add_stream(PurpleMedia *media, const gchar *who,
+			PurpleMediaStreamType type,
+			const gchar *transmitter)
+{
+	FsStreamDirection type_direction;
+
+	if (type & PURPLE_MEDIA_AUDIO) {
+		if (type & PURPLE_MEDIA_SEND_AUDIO && type & PURPLE_MEDIA_RECV_AUDIO)
+			type_direction = FS_DIRECTION_BOTH;
+		else if (type & PURPLE_MEDIA_SEND_AUDIO)
+			type_direction = FS_DIRECTION_SEND;
+		else if (type & PURPLE_MEDIA_RECV_AUDIO)
+			type_direction = FS_DIRECTION_RECV;
+		else
+			type_direction = FS_DIRECTION_NONE;
+
+		purple_media_add_stream_internal(media, &media->priv->audio_session,
+						 &media->priv->audio_streams,
+				 		 media->priv->audio_src, who,
+						 FS_MEDIA_TYPE_AUDIO, type_direction,
+						 transmitter);
+	}
+	if (type & PURPLE_MEDIA_VIDEO) {
+		if (type & PURPLE_MEDIA_SEND_VIDEO && type & PURPLE_MEDIA_RECV_VIDEO)
+			type_direction = FS_DIRECTION_BOTH;
+		else if (type & PURPLE_MEDIA_SEND_VIDEO)
+			type_direction = FS_DIRECTION_SEND;
+		else if (type & PURPLE_MEDIA_RECV_VIDEO)
+			type_direction = FS_DIRECTION_RECV;
+		else
+			type_direction = FS_DIRECTION_NONE;
+
+		purple_media_add_stream_internal(media, &media->priv->video_session,
+						 &media->priv->video_streams,
+				 		 media->priv->video_src, who,
+						 FS_MEDIA_TYPE_VIDEO, type_direction,
+						 transmitter);
+	}
+}
+
+void
+purple_media_remove_stream(PurpleMedia *media, const gchar *who, PurpleMediaStreamType type)
+{
+	
+}
+
+static FsStream *
+purple_media_get_audio_stream(PurpleMedia *media, const gchar *name)
+{
+	GList *streams = media->priv->audio_streams;
+	for (; streams; streams = streams->next) {
+		FsParticipant *participant;
+		gchar *cname;
+		g_object_get(streams->data, "participant", &participant, NULL);
+		g_object_get(participant, "cname", &cname, NULL);
+
+		if (!strcmp(cname, name)) {
+			return streams->data;
+		}
+	}
+
+	return NULL;
+}
+
+GList *
+purple_media_get_local_audio_codecs(PurpleMedia *media)
+{
+	GList *codecs;
+	g_object_get(G_OBJECT(media->priv->audio_session), "local-codecs", &codecs, NULL);
+	return codecs;
+}
+
+GList *
+purple_media_get_local_audio_candidates(PurpleMedia *media)
+{
+	return media->priv->local_candidates;
+}
+
+GList *
+purple_media_get_negotiated_audio_codecs(PurpleMedia *media)
+{
+	GList *codec_intersection;
+	g_object_get(media->priv->audio_session, "negotiated-codecs", &codec_intersection, NULL);
+	return codec_intersection;
+}
+
+void
+purple_media_add_remote_audio_candidates(PurpleMedia *media, const gchar *name, GList *remote_candidates)
+{
+	FsStream *stream = purple_media_get_audio_stream(media, name);
+	GList *candidates = remote_candidates;
+	for (; candidates; candidates = candidates->next)
+		fs_stream_add_remote_candidate(stream, candidates->data, NULL);
+}
+
+FsCandidate *
+purple_media_get_local_candidate(PurpleMedia *media)
+{
+	return media->priv->local_candidate;
+}
+
+FsCandidate *
+purple_media_get_remote_candidate(PurpleMedia *media)
+{
+	return media->priv->remote_candidate;
+}
+
+void
+purple_media_set_remote_audio_codecs(PurpleMedia *media, const gchar *name, GList *codecs)
+{
+	fs_stream_set_remote_codecs(purple_media_get_audio_stream(media, name), codecs, NULL);
+}
+
 #endif  /* USE_GSTPROPS */
 #endif  /* USE_FARSIGHT */
--- a/libpurple/media.h	Sun Apr 13 17:53:46 2008 +0000
+++ b/libpurple/media.h	Fri May 23 02:42:32 2008 +0000
@@ -29,7 +29,9 @@
 #ifdef USE_FARSIGHT
 #ifdef USE_GSTPROPS
 
-#include <farsight/farsight.h>
+#include <gst/gst.h>
+#include <gst/farsight/fs-stream.h>
+#include <gst/farsight/fs-session.h>
 #include <glib.h>
 #include <glib-object.h>
 
@@ -53,6 +55,8 @@
 	PURPLE_MEDIA_SEND_AUDIO = 1 << 1,
 	PURPLE_MEDIA_RECV_VIDEO = 1 << 2,
 	PURPLE_MEDIA_SEND_VIDEO = 1 << 3,
+	PURPLE_MEDIA_AUDIO = PURPLE_MEDIA_RECV_AUDIO | PURPLE_MEDIA_SEND_AUDIO,
+	PURPLE_MEDIA_VIDEO = PURPLE_MEDIA_RECV_VIDEO | PURPLE_MEDIA_SEND_VIDEO
 } PurpleMediaStreamType;
 
 struct _PurpleMediaClass
@@ -109,6 +113,20 @@
 
 void purple_media_audio_init_recv(GstElement **recvbin, GstElement **recvlevel);
 
+void purple_media_add_stream(PurpleMedia *media, const gchar *who,
+			     PurpleMediaStreamType type, const gchar *transmitter);
+void purple_media_remove_stream(PurpleMedia *media, const gchar *who, PurpleMediaStreamType type);
+
+GList *purple_media_get_local_audio_candidates(PurpleMedia *media);
+GList *purple_media_get_negotiated_audio_codecs(PurpleMedia *media);
+
+GList *purple_media_get_local_audio_codecs(PurpleMedia *media);
+void purple_media_add_remote_audio_candidates(PurpleMedia *media, const gchar *name,	
+					       GList *remote_candidates);
+FsCandidate *purple_media_get_local_candidate(PurpleMedia *media);
+FsCandidate *purple_media_get_remote_candidate(PurpleMedia *media);
+void purple_media_set_remote_audio_codecs(PurpleMedia *media, const gchar *name, GList *codecs);
+
 G_END_DECLS
 
 #endif  /* USE_GSTPROPS */
--- a/libpurple/mediamanager.c	Sun Apr 13 17:53:46 2008 +0000
+++ b/libpurple/mediamanager.c	Fri May 23 02:42:32 2008 +0000
@@ -31,7 +31,7 @@
 
 #ifdef USE_FARSIGHT
 
-#include <farsight/farsight.h>
+#include <gst/farsight/fs-conference-iface.h>
 
 struct _PurpleMediaManagerPrivate
 {
@@ -127,18 +127,19 @@
 	return manager;
 }
 
-PurpleMedia*
-purple_media_manager_create_media(PurpleMediaManager *manager, 
+PurpleMedia *
+purple_media_manager_create_media(PurpleMediaManager *manager,
 				  PurpleConnection *gc,
-				  const char *screenname,
-				  FarsightStream *audio_stream,
-				  FarsightStream *video_stream)
+				  const char *conference_type,
+				  const char *remote_user)
 {
+	FsConference *conference = FS_CONFERENCE(gst_element_factory_make(conference_type, NULL));
+
 	PurpleMedia *media = PURPLE_MEDIA(g_object_new(purple_media_get_type(),
-					  "screenname", screenname,
+					  "screenname", remote_user,
 					  "connection", gc, 
-					  "audio-stream", audio_stream,
-					  "video-stream", video_stream, NULL));
+					  "farsight-conference", conference,
+					  NULL));
 	manager->priv->medias = g_list_append(manager->priv->medias, media);
 	g_signal_emit(manager, purple_media_manager_signals[INIT_MEDIA], 0, media);
 	return media;
--- a/libpurple/mediamanager.h	Sun Apr 13 17:53:46 2008 +0000
+++ b/libpurple/mediamanager.h	Fri May 23 02:42:32 2008 +0000
@@ -28,7 +28,7 @@
 
 #ifdef USE_FARSIGHT
 
-#include <farsight/farsight.h>
+#include <gst/farsight/fs-session.h>
 #include <glib.h>
 #include <glib-object.h>
 
@@ -61,12 +61,10 @@
 
 GType purple_media_manager_get_type(void);
 PurpleMediaManager *purple_media_manager_get(void);
-
 PurpleMedia *purple_media_manager_create_media(PurpleMediaManager *manager,
-					       PurpleConnection *gc,
-					       const char *screenname,
-					       FarsightStream *audio_stream,
-					       FarsightStream *video_stream);
+						PurpleConnection *gc,
+						const char *conference_type,
+						const char *remote_user);
 
 G_END_DECLS
 
--- a/libpurple/protocols/jabber/google.c	Sun Apr 13 17:53:46 2008 +0000
+++ b/libpurple/protocols/jabber/google.c	Fri May 23 02:42:32 2008 +0000
@@ -18,7 +18,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
  */
 
-#include <farsight/farsight-transport.h>
+#include <gst/farsight/fs-conference-iface.h>
 
 #include "internal.h"
 #include "debug.h"
@@ -49,7 +49,6 @@
 	GoogleSessionId id;
 	GoogleSessionState state;
 	PurpleMedia *media;
-	FarsightStream *stream;
 	JabberStream *js; 
 	char *remote_jid;
 } GoogleSession;
@@ -84,7 +83,6 @@
 	g_free(session->id.initiator);
 	g_free(session->remote_jid);
 	g_object_unref(session->media);
-	g_object_unref(session->stream);
 	g_free(session);
 }
 
@@ -103,7 +101,7 @@
 google_session_send_accept(GoogleSession *session)
 {
 	xmlnode *sess, *desc, *payload;
-	GList *codecs = farsight_stream_get_codec_intersection(session->stream);
+	GList *codecs = purple_media_get_negotiated_audio_codecs(session->media);
 	JabberIq *iq = jabber_iq_new(session->js, JABBER_IQ_SET);
 
 	xmlnode_set_attrib(iq->node, "to", session->remote_jid);
@@ -113,7 +111,7 @@
 	xmlnode_set_namespace(desc, "http://www.google.com/session/phone");
 
 	for (;codecs; codecs = codecs->next) {
-		FarsightCodec *codec = (FarsightCodec*)codecs->data;
+		FsCodec *codec = (FsCodec*)codecs->data;
 		char id[8], clockrate[10];
 		payload = xmlnode_new_child(desc, "payload-type");
 		g_snprintf(id, sizeof(id), "%d", codec->id);
@@ -123,15 +121,15 @@
 		xmlnode_set_attrib(payload, "clockrate", clockrate);
 	}
 
+	fs_codec_list_destroy(codecs);
 	jabber_iq_send(iq);
-	farsight_stream_start(session->stream);
+	gst_element_set_state(purple_media_get_audio_pipeline(session->media), GST_STATE_PLAYING);
 }
 
 static void
 google_session_send_terminate(GoogleSession *session)
 {
 	xmlnode *sess;
-	GList *codecs = farsight_stream_get_codec_intersection(session->stream);
 	JabberIq *iq = jabber_iq_new(session->js, JABBER_IQ_SET);
 
 	xmlnode_set_attrib(iq->node, "to", session->remote_jid);
@@ -139,7 +137,6 @@
 	xmlnode_insert_child(iq->node, sess);
 	
 	jabber_iq_send(iq);
-	farsight_stream_stop(session->stream);
 	google_session_destroy(session);
 }
 
@@ -147,7 +144,6 @@
 google_session_send_reject(GoogleSession *session)
 {
 	xmlnode *sess;
-	GList *codecs = farsight_stream_get_codec_intersection(session->stream);
 	JabberIq *iq = jabber_iq_new(session->js, JABBER_IQ_SET);
 
 	xmlnode_set_attrib(iq->node, "to", session->remote_jid);
@@ -155,17 +151,16 @@
 	xmlnode_insert_child(iq->node, sess);
 	
 	jabber_iq_send(iq);
-	farsight_stream_stop(session->stream);
 	google_session_destroy(session);
 }
 
 
 static void 
-google_session_candidates_prepared (FarsightStream *stream, gchar *candidate_id, GoogleSession *session)
+google_session_candidates_prepared (PurpleMedia *media, GoogleSession *session)
 {
 	JabberIq *iq = jabber_iq_new(session->js, JABBER_IQ_SET);
-	GList *candidates = farsight_stream_get_native_candidate_list(stream);
-	FarsightTransportInfo *transport;
+	GList *candidates = purple_media_get_local_audio_candidates(session->media);
+	FsCandidate *transport;
 	xmlnode *sess;
 	xmlnode *candidate;
 	sess = google_session_create_xmlnode(session, "candidates");
@@ -173,9 +168,9 @@
 	xmlnode_set_attrib(iq->node, "to", session->remote_jid);
 	
 	for (;candidates;candidates = candidates->next) {
-		transport = (FarsightTransportInfo*)(candidates->data);
 		char port[8];
 		char pref[8];
+		transport = (FsCandidate*)(candidates->data);
 
 		if (!strcmp(transport->ip, "127.0.0.1"))
 			continue;
@@ -183,7 +178,7 @@
 		candidate = xmlnode_new("candidate");
 
 		g_snprintf(port, sizeof(port), "%d", transport->port);
-		g_snprintf(pref, sizeof(pref), "%f", transport->preference);
+		g_snprintf(pref, sizeof(pref), "%d", transport->priority);
 
 		xmlnode_set_attrib(candidate, "address", transport->ip);
 		xmlnode_set_attrib(candidate, "port", port);
@@ -191,10 +186,10 @@
 		xmlnode_set_attrib(candidate, "username", transport->username);
 		xmlnode_set_attrib(candidate, "password", transport->password);
 		xmlnode_set_attrib(candidate, "preference", pref);
-		xmlnode_set_attrib(candidate, "protocol", transport->proto == FARSIGHT_NETWORK_PROTOCOL_UDP ? "udp" : "tcp");
-		xmlnode_set_attrib(candidate, "type", transport->type == FARSIGHT_CANDIDATE_TYPE_LOCAL ? "local" :
-						      transport->type == FARSIGHT_CANDIDATE_TYPE_DERIVED ? "stun" :
-					       	      transport->type == FARSIGHT_CANDIDATE_TYPE_RELAY ? "relay" : NULL);
+		xmlnode_set_attrib(candidate, "protocol", transport->proto == FS_NETWORK_PROTOCOL_UDP ? "udp" : "tcp");
+		xmlnode_set_attrib(candidate, "type", transport->type == FS_CANDIDATE_TYPE_HOST ? "local" :
+						      transport->type == FS_CANDIDATE_TYPE_PRFLX ? "stun" :
+					       	      transport->type == FS_CANDIDATE_TYPE_RELAY ? "relay" : NULL);
 		xmlnode_set_attrib(candidate, "generation", "0");
 		xmlnode_set_attrib(candidate, "network", "0");
 		xmlnode_insert_child(sess, candidate);
@@ -206,30 +201,24 @@
 static void
 google_session_handle_initiate(JabberStream *js, GoogleSession *session, xmlnode *packet, xmlnode *sess)
 {
-	PurpleMedia *media;
 	JabberIq *result;
-	FarsightSession *fs;
 	GList *codecs = NULL;
 	xmlnode *desc_element, *codec_element;
-	FarsightCodec *codec;
+	FsCodec *codec;
 	const char *id, *encoding_name,  *clock_rate;
-	int res;
 		
 	if (session->state != UNINIT) {
 		purple_debug_error("jabber", "Received initiate for active session.\n");
-		return FALSE;
+		return;
 	}
 
-	fs = farsight_session_factory_make("rtp");
-	if (!fs) {
-		purple_debug_error("jabber", "Farsight's rtp plugin not installed");
-		return FALSE;
-	}
-	
-	session->stream = farsight_session_create_stream(fs, FARSIGHT_MEDIA_TYPE_AUDIO, FARSIGHT_STREAM_DIRECTION_BOTH);
+	session->media = purple_media_manager_create_media(purple_media_manager_get(), js->gc,
+							   "fsrtpconference", session->remote_jid);
 
-	g_object_set(G_OBJECT(session->stream), "transmitter", "libjingle", NULL);
-	
+	/* "rawudp" will need to be changed to "nice" when libnice is finished */
+	purple_media_add_stream(session->media, session->remote_jid, 
+				PURPLE_MEDIA_AUDIO, "rawudp");
+
 	desc_element = xmlnode_get_child(sess, "description");
 	
 	for (codec_element = xmlnode_get_child(desc_element, "payload-type"); 
@@ -239,31 +228,25 @@
 		id = xmlnode_get_attrib(codec_element, "id");
 		clock_rate = xmlnode_get_attrib(codec_element, "clockrate");
 
-		codec = g_new0(FarsightCodec, 1);
-		farsight_codec_init(codec, atoi(id), encoding_name, FARSIGHT_MEDIA_TYPE_AUDIO, clock_rate ? atoi(clock_rate) : 0);
+		codec = fs_codec_new(atoi(id), encoding_name, FS_MEDIA_TYPE_AUDIO,
+				     clock_rate ? atoi(clock_rate) : 0);
 		codecs = g_list_append(codecs, codec);
 	}
 
-	session->media = media = purple_media_manager_create_media(purple_media_manager_get(), js->gc, session->remote_jid, session->stream, NULL);
+	purple_media_set_remote_audio_codecs(session->media, session->remote_jid, codecs);
 
-	g_signal_connect_swapped(G_OBJECT(media), "accepted", G_CALLBACK(google_session_send_accept), session);
-	g_signal_connect_swapped(G_OBJECT(media), "reject", G_CALLBACK(google_session_send_reject), session);
-	g_signal_connect_swapped(G_OBJECT(media), "hangup", G_CALLBACK(google_session_send_terminate), session);
+	g_signal_connect_swapped(G_OBJECT(session->media), "accepted",
+				 G_CALLBACK(google_session_send_accept), session);
+	g_signal_connect_swapped(G_OBJECT(session->media), "reject",
+				 G_CALLBACK(google_session_send_reject), session);
+	g_signal_connect_swapped(G_OBJECT(session->media), "hangup",
+				 G_CALLBACK(google_session_send_terminate), session);
+	g_signal_connect(G_OBJECT(session->media), "candidates-prepared", 
+			 G_CALLBACK(google_session_candidates_prepared), session);
+	purple_media_ready(session->media);
 
-	
-	GstElement *e = purple_media_get_audio_src(media);
-	farsight_stream_set_source(session->stream, e);	
+	fs_codec_list_destroy(codecs);
 	
-	e = purple_media_get_audio_sink(media);
-	farsight_stream_set_sink(session->stream, e);
-	
-	farsight_stream_prepare_transports(session->stream);
-	res = farsight_stream_set_remote_codecs(session->stream, codecs);
-	
-	purple_media_ready(media);
-
-	farsight_codec_list_destroy(codecs);
-	g_signal_connect(G_OBJECT(session->stream), "new-native-candidate", G_CALLBACK(google_session_candidates_prepared), session);
 	result = jabber_iq_new(js, JABBER_IQ_RESULT);
 	jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id"));
 	xmlnode_set_attrib(result->node, "to", session->remote_jid);
@@ -280,24 +263,26 @@
 	char n[4];	
 		
 	for (cand = xmlnode_get_child(sess, "candidate"); cand; cand = xmlnode_get_next_twin(cand)) {
-		FarsightTransportInfo *info = g_new0(FarsightTransportInfo, 1);
+		FsCandidate *info;
 		g_snprintf(n, sizeof(n), "S%d", name++);
-		info->ip = xmlnode_get_attrib(cand, "address");
-		info->port = atoi(xmlnode_get_attrib(cand, "port"));
-		info->proto = !strcmp(xmlnode_get_attrib(cand, "protocol"),"udp") ? FARSIGHT_NETWORK_PROTOCOL_UDP : FARSIGHT_NETWORK_PROTOCOL_TCP;
-		info->preference = atof(xmlnode_get_attrib(cand, "preference"));
-		info->type = !strcmp(xmlnode_get_attrib(cand, "type"), "local") ? FARSIGHT_CANDIDATE_TYPE_LOCAL :
-			     !strcmp(xmlnode_get_attrib(cand, "type"), "stun") ? FARSIGHT_CANDIDATE_TYPE_DERIVED :
-			     !strcmp(xmlnode_get_attrib(cand, "type"), "relay") ? FARSIGHT_CANDIDATE_TYPE_RELAY : FARSIGHT_CANDIDATE_TYPE_LOCAL;
-		info->candidate_id = n;
-		info->username = xmlnode_get_attrib(cand, "username");
-		info->password = xmlnode_get_attrib(cand, "password");
+		info = fs_candidate_new(n, FS_COMPONENT_RTP, !strcmp(xmlnode_get_attrib(cand, "type"), "local") ?
+					FS_CANDIDATE_TYPE_HOST :
+			     		!strcmp(xmlnode_get_attrib(cand, "type"), "stun") ?
+						FS_CANDIDATE_TYPE_PRFLX :
+			     			!strcmp(xmlnode_get_attrib(cand, "type"), "relay") ?
+							FS_CANDIDATE_TYPE_RELAY : FS_CANDIDATE_TYPE_HOST,
+						!strcmp(xmlnode_get_attrib(cand, "protocol"),"udp") ?
+							FS_NETWORK_PROTOCOL_UDP : FS_NETWORK_PROTOCOL_TCP,
+					xmlnode_get_attrib(cand, "address"), atoi(xmlnode_get_attrib(cand, "port")));
+
+		info->username = g_strdup(xmlnode_get_attrib(cand, "username"));
+		info->password = g_strdup(xmlnode_get_attrib(cand, "password"));
+
 		list = g_list_append(list, info);
 	}
 
-	farsight_stream_add_remote_candidate(session->stream, list);
-	g_list_foreach(list, g_free, NULL);
-	g_list_free(list);
+	purple_media_add_remote_audio_candidates(session->media, session->remote_jid, list);
+	fs_candidate_list_destroy(list);
 
 	result = jabber_iq_new(js, JABBER_IQ_RESULT);
 	jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id"));
@@ -308,7 +293,6 @@
 static void
 google_session_handle_reject(JabberStream *js, GoogleSession *session, xmlnode *packet, xmlnode *sess)
 {
-	farsight_stream_stop(session->stream);
 	purple_media_got_hangup(session->media);
 	
 	google_session_destroy(session);
@@ -317,7 +301,6 @@
 static void
 google_session_handle_terminate(JabberStream *js, GoogleSession *session, xmlnode *packet, xmlnode *sess)
 {
-	farsight_stream_stop(session->stream);
 	purple_media_got_hangup(session->media);
 
 	google_session_destroy(session);
@@ -346,7 +329,6 @@
 {
 	GoogleSession *session;
 	GoogleSessionId id;
-	JabberIq *result;
 
 	xmlnode *session_node;
 	xmlnode *desc_node;
@@ -358,11 +340,11 @@
 	if (!session_node)
 		return;
 
-	id.id = xmlnode_get_attrib(session_node, "id");
+	id.id = (gchar*)xmlnode_get_attrib(session_node, "id");
 	if (!id.id)
 		return;
 
-	id.initiator = xmlnode_get_attrib(session_node, "initiator");
+	id.initiator = (gchar*)xmlnode_get_attrib(session_node, "initiator");
 	if (!id.initiator)
 		return;
 
--- a/libpurple/protocols/jabber/jabber.c	Sun Apr 13 17:53:46 2008 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Fri May 23 02:42:32 2008 +0000
@@ -58,6 +58,8 @@
 #include "adhoccommands.h"
 #include "jingle.h"
 
+#include <gst/farsight/fs-conference-iface.h>
+
 #define JABBER_CONNECT_STEPS (js->gsc ? 9 : 5)
 
 static PurplePlugin *my_protocol = NULL;
@@ -1240,6 +1242,16 @@
 {
 	JabberStream *js = gc->proto_data;
 
+#ifdef USE_FARSIGHT
+	/* Close all of the open media sessions on this stream */
+	GList *iter = jabber_jingle_session_find_by_js(js);
+
+	for (; iter; iter = iter->next) {
+		JingleSession *session = (JingleSession *)iter->data;
+		purple_media_hangup(session->media);
+	}
+#endif
+
 	/* Don't perform any actions on the ssl connection
 	 * if we were forcibly disconnected because it will crash
 	 * on some SSL backends.
@@ -1862,10 +1874,20 @@
 	JabberID *jid;
 	JabberBuddy *jb;
 	JabberBuddyResource *jbr;
-
+#ifdef USE_FARSIGHT
+	JingleSession *session;
+#endif
 	if(!(jid = jabber_id_new(who)))
 		return;
 
+#ifdef USE_FARSIGHT
+	session = jabber_jingle_session_find_by_jid(js, who);
+
+	if (session) {
+		purple_media_hangup(session->media);
+	}
+
+#endif
 	if((jb = jabber_buddy_find(js, who, TRUE)) &&
 			(jbr = jabber_buddy_find_resource(jb, jid->resource))) {
 		if(jbr->thread_id) {
@@ -2357,11 +2379,9 @@
 	xmlnode_insert_child(result->node, jingle);
 	jabber_iq_send(result);
 	purple_debug_info("jabber", "Sent session accept, starting stream\n");
-	farsight_stream_start(jabber_jingle_session_get_stream(session));
-	farsight_stream_set_remote_codecs(
-			jabber_jingle_session_get_stream(session),
-			jabber_jingle_session_get_remote_codecs(session));
-
+	gst_element_set_state(purple_media_get_audio_pipeline(session->media), GST_STATE_PLAYING);
+
+	session->session_started = TRUE;
 }
 
 static void
@@ -2388,7 +2408,6 @@
 					   jabber_jingle_session_get_remote_jid(session));
 	xmlnode_insert_child(result->node, jingle);
 	jabber_iq_send(result);
-	farsight_stream_stop(jabber_jingle_session_get_stream(session));
 	jabber_jingle_session_destroy(session);
 }
 
@@ -2403,117 +2422,135 @@
 					   jabber_jingle_session_get_remote_jid(session));
 	xmlnode_insert_child(result->node, jingle);
 	jabber_iq_send(result);
-	farsight_stream_stop(jabber_jingle_session_get_stream(session));
 	jabber_jingle_session_destroy(session);
 }
 
 /* callback called when new local transport candidate(s) are available on the
 	Farsight stream */
 static void
-jabber_session_candidates_prepared(FarsightStream *stream, gchar *candidate_id, 
-								   JingleSession *session)
+jabber_session_candidates_prepared(PurpleMedia *media, JingleSession *session)
 {
-	/* create transport-info package */
-	JabberIq *result = jabber_iq_new(jabber_jingle_session_get_js(session),
-									 JABBER_IQ_SET);
-	xmlnode *jingle = jabber_jingle_session_create_transport_info(session,
-																  candidate_id);
-	purple_debug_info("jabber", "jabber_session_candidates_prepared called for candidate_id = %s\n",
-					  candidate_id);
-	xmlnode_set_attrib(result->node, "to",
-					   jabber_jingle_session_get_remote_jid(session));
-	
-	xmlnode_insert_child(result->node, jingle);
-	jabber_iq_send(result);
+	if (!jabber_jingle_session_is_initiator(session)) {
+		/* create transport-info package */
+		JabberIq *result = jabber_iq_new(jabber_jingle_session_get_js(session),
+						 JABBER_IQ_SET);
+		xmlnode *jingle = jabber_jingle_session_create_transport_info(session);
+		purple_debug_info("jabber", "jabber_session_candidates_prepared: %d candidates\n",
+				  g_list_length(purple_media_get_local_audio_candidates(session->media)));
+		xmlnode_set_attrib(result->node, "to",
+				   jabber_jingle_session_get_remote_jid(session));
+
+		xmlnode_insert_child(result->node, jingle);
+		jabber_iq_send(result);
+	}
 }
 
 /* callback called when a pair of transport candidates (local and remote)
 	has been established */
 static void
-jabber_session_candidate_pair_established(FarsightStream *stream,
-										  gchar *native_candidate_id,
-										  gchar *remote_candidate_id,
-										  JingleSession *session)
+jabber_session_candidate_pair_established(PurpleMedia *media,
+					  FsCandidate *native_candidate,
+					  FsCandidate *remote_candidate,
+					  JingleSession *session)
 {	
-	purple_debug_info("jabber", "jabber_candidate_pair_established called");
+	purple_debug_info("jabber", "jabber_candidate_pair_established called\n");
 	/* if we are the initiator, we should send a content-modify message */
 	if (jabber_jingle_session_is_initiator(session)) {
-		purple_debug_info("jabber", 
-						  "we are the initiator, let's send conten-modify\n");
-		JabberIq *result = jabber_iq_new(jabber_jingle_session_get_js(session),
-										 JABBER_IQ_SET);
+		JabberIq *result;
+		xmlnode *jingle;
+		
+		purple_debug_info("jabber", "we are the initiator, let's send content-modify\n");
+
+		result = jabber_iq_new(jabber_jingle_session_get_js(session), JABBER_IQ_SET);
+
 		/* shall change this to a "content-replace" */
-		xmlnode *jingle = 
+		jingle =
 			jabber_jingle_session_create_content_replace(session,
-														 native_candidate_id,
-														 remote_candidate_id);
+								     native_candidate,
+								     remote_candidate);
 		xmlnode_set_attrib(result->node, "to",
 						   jabber_jingle_session_get_remote_jid(session));
 		xmlnode_insert_child(result->node, jingle);
 		jabber_iq_send(result);
 	}
-	/*
-	farsight_stream_set_active_candidate_pair(stream, native_candidate_id,
-											  remote_candidate_id);
-	*/
+}
+
+static void
+jabber_initiate_media_internal(JingleSession *session, const char *initiator, const char *remote_jid)
+{
+	PurpleMedia *media = NULL;
+
+	media = purple_media_manager_create_media(purple_media_manager_get(), 
+						  session->js->gc, "fsrtpconference", remote_jid);
+	/* this will need to be changed to "nice" once the libnice transmitter is finished */
+	purple_media_add_stream(media, remote_jid, PURPLE_MEDIA_AUDIO, "rawudp");
+
+	jabber_jingle_session_set_remote_jid(session, remote_jid);
+	jabber_jingle_session_set_initiator(session, initiator);
+	jabber_jingle_session_set_media(session, media);
+	
+	/* connect callbacks */
+	g_signal_connect_swapped(G_OBJECT(media), "accepted", 
+				 G_CALLBACK(jabber_session_send_accept), session);
+	g_signal_connect_swapped(G_OBJECT(media), "reject", 
+				 G_CALLBACK(jabber_session_send_reject), session);
+	g_signal_connect_swapped(G_OBJECT(media), "hangup", 
+				 G_CALLBACK(jabber_session_send_terminate), session);
+	g_signal_connect(G_OBJECT(media), "candidates-prepared", 
+				 G_CALLBACK(jabber_session_candidates_prepared), session);
+	g_signal_connect(G_OBJECT(media), "candidate-pair", 
+				 G_CALLBACK(jabber_session_candidate_pair_established), session);
+
+	purple_media_ready(media);
 }
 
-
-PurpleMedia *jabber_initiate_media(PurpleConnection *gc, const char *who, 
+static void
+jabber_session_initiate_result_cb(JabberStream *js, xmlnode *packet, gpointer data)
+{
+	const char *from = xmlnode_get_attrib(packet, "from");
+	JingleSession *session = jabber_jingle_session_find_by_jid(js, from);
+	PurpleMedia *media = session->media;
+	JabberIq *result;
+	xmlnode *jingle;
+
+	if (!strcmp(xmlnode_get_attrib(packet, "type"), "error")) {
+		purple_media_got_hangup(media);
+		return;
+	}
+
+	/* catch errors */
+	if (xmlnode_get_child(packet, "error")) {
+		purple_media_got_hangup(media);
+		return;
+	}
+
+	/* create transport-info package */
+	result = jabber_iq_new(jabber_jingle_session_get_js(session), JABBER_IQ_SET);
+	jingle = jabber_jingle_session_create_transport_info(session);
+	purple_debug_info("jabber", "jabber_session_candidates_prepared: %d candidates\n",
+			  g_list_length(purple_media_get_local_audio_candidates(session->media)));
+	xmlnode_set_attrib(result->node, "to",
+			   jabber_jingle_session_get_remote_jid(session));
+
+	xmlnode_insert_child(result->node, jingle);
+	jabber_iq_send(result);
+}
+
+PurpleMedia *
+jabber_initiate_media(PurpleConnection *gc, const char *who, 
 								   PurpleMediaStreamType type)
 {
 	/* create content negotiation */
 	JabberStream *js = gc->proto_data;
 	JabberIq *request = jabber_iq_new(js, JABBER_IQ_SET);
-	xmlnode *jingle, *content, *description, *payload_type, *transport;
-	FarsightSession *fs = NULL;
-	FarsightStream *audio = NULL; /* only audio for now... */
+	xmlnode *jingle, *content, *description, *transport;
 	GList *codecs;
-	GList *codec_iter = NULL;
-	FarsightCodec *codec = NULL;
-	PurpleMedia *media = NULL;
 	JingleSession *session;
 	JabberBuddy *jb;
 	JabberBuddyResource *jbr;
 	
-	char id[10];
-	char clock_rate[10];
-	char channels[10];
-	char jid[256];
-	char me[256];
-	
-	/* for debug */
-	char *output;
-	int len;
-		
-	/* setup stream */
-	fs = farsight_session_factory_make("rtp");
-	if (fs == NULL) {
-		purple_debug_error("jabber", "Farsight's rtp plugin not installed");
-		return NULL;
-	}
-	
-	/* check media stream type, and so on... */
-	/* only do audio for now... */
-	
-	/* get stuff from Farsight... */
-	audio = farsight_session_create_stream(fs, FARSIGHT_MEDIA_TYPE_AUDIO, 
-										   FARSIGHT_STREAM_DIRECTION_BOTH);
-	g_object_set(G_OBJECT(audio), "transmitter", "libjingle", NULL);
-	
-	purple_debug_info("jabber", "Getting local codecs\n");
-	codecs = farsight_stream_get_local_codecs(audio);
-	purple_debug_info("jabber", "number of codecs: %d\n", g_list_length(codecs));
-	
- 	if (audio == NULL) {
-		purple_debug_error("jabber", "Unable to create Farsight stream for audio");
-		/* destroy FarsightSession? */
-		return NULL;
-	}
-	
-	media = purple_media_manager_create_media(purple_media_manager_get(),
-											  gc, who, audio, NULL);
-	purple_debug_info("jabber", "After purple_media_manager_create_media\n");
+	char *jid = NULL, *me = NULL;
+
 	/* construct JID to send to */
 	jb = jabber_buddy_find(js, who, FALSE);
 	if (!jb) {
@@ -2524,42 +2561,24 @@
 	if (!jbr) {
 		purple_debug_error("jabber", "Could not find buddy's resource\n");
 	}
-	
-	g_snprintf(jid, 255, "%s/%s", who, jbr->name);
+
+	if ((strchr(who, '/') == NULL) && jbr && (jbr->name != NULL)) {
+		jid = g_strdup_printf("%s/%s", who, jbr->name);
+	} else {
+		jid = g_strdup(who);
+	}
 	
 	session = jabber_jingle_session_create(js);
-	jabber_jingle_session_set_remote_jid(session, jid);
 	/* set ourselves as initiator */
-	g_snprintf(me, sizeof(me), "%s@%s/%s", js->user->node, js->user->domain,
-			   js->user->resource);
-	jabber_jingle_session_set_initiator(session, me);
-	
-	jabber_jingle_session_set_stream(session, audio);
-	jabber_jingle_session_set_media(session, media);
-	
-	g_signal_connect_swapped(G_OBJECT(media), "accepted", 
-							 G_CALLBACK(jabber_session_send_accept), session);
-	g_signal_connect_swapped(G_OBJECT(media), "reject", 
-							 G_CALLBACK(jabber_session_send_reject), session);
-	g_signal_connect_swapped(G_OBJECT(media), "hangup", 
-							 G_CALLBACK(jabber_session_send_terminate), session);
-	
-	GstElement *e = purple_media_get_audio_src(media);
-	farsight_stream_set_source(jabber_jingle_session_get_stream(session), e);	
-	e = purple_media_get_audio_sink(media);
-	farsight_stream_set_sink(jabber_jingle_session_get_stream(session), e);
-	
-	farsight_stream_prepare_transports(audio);
-	/* callback for new native (local) transport candidates for the stream */
-	g_signal_connect(G_OBJECT(audio), 
-					 "new-native-candidate", 
-					 G_CALLBACK(jabber_session_candidates_prepared), session);
-	/* callback for new active candidate pair (established connection) */
-	g_signal_connect(G_OBJECT(audio),
-					 "new-active-candidate-pair",
-					 G_CALLBACK(jabber_session_candidate_pair_established),
-					 session);
-	
+	me = g_strdup_printf("%s@%s/%s", js->user->node, js->user->domain, js->user->resource);
+
+	jabber_initiate_media_internal(session, me, jid);
+
+	g_free(jid);
+	g_free(me);
+
+	codecs = purple_media_get_local_audio_codecs(session->media);
+
 	/* create request */
 	
 	xmlnode_set_attrib(request->node, "to", 
@@ -2568,40 +2587,27 @@
 	xmlnode_set_namespace(jingle, "urn:xmpp:tmp:jingle");
 	xmlnode_set_attrib(jingle, "action", "session-initiate");
 	/* get our JID and a session id... */
-	xmlnode_set_attrib(jingle, "initiator", me);
+	xmlnode_set_attrib(jingle, "initiator", jabber_jingle_session_get_initiator(session));
 	xmlnode_set_attrib(jingle, "sid", jabber_jingle_session_get_id(session));
 	
 	content = xmlnode_new_child(jingle, "content");
 	xmlnode_set_attrib(content, "name", "audio-content");
 	xmlnode_set_attrib(content, "profile", "RTP/AVP");
-	
-	description = xmlnode_new_child(content, "description");
-	xmlnode_set_namespace(description, "urn:xmpp:tmp:jingle:apps:audio-rtp");
-	
-	/* create payload-type nodes */
-	purple_debug_info("jabber", "Generating payload_type elements\n");
-	for (; codecs ; codecs = codecs->next) {
-		codec = (FarsightCodec *) codecs->data;
-		purple_debug_info("jabber", "Generating payload_type for (%d) %s\n",
-						  codec->id, codec->encoding_name);
-		sprintf(id, "%d", codec->id);
-		sprintf(clock_rate, "%d", codec->clock_rate);
-		sprintf(channels, "%d", codec->channels);
-		
-		payload_type = xmlnode_new_child(description, "payload-type");
-		xmlnode_set_attrib(payload_type, "id", id);
-		xmlnode_set_attrib(payload_type, "name", codec->encoding_name);
-		xmlnode_set_attrib(payload_type, "clockrate", clock_rate);
-		xmlnode_set_attrib(payload_type, "channels", channels);
-	}
-		
+
+	description = jabber_jingle_session_create_description(session);
+	xmlnode_insert_child(content, description);
+
 	transport = xmlnode_new_child(content, "transport");
-	xmlnode_set_namespace(transport, "urn:xmpp:tmp:jingle:transports:ice-tcp");
-	
+	xmlnode_set_namespace(transport, "urn:xmpp:tmp:jingle:transports:ice-udp");
+
+	jabber_iq_set_callback(request, jabber_session_initiate_result_cb, NULL);
+
 	/* send request to other part */	
 	jabber_iq_send(request);
 
-	return media;
+	fs_codec_list_destroy(codecs);
+
+	return session->media;
 }
 
 gboolean jabber_can_do_media(PurpleConnection *gc, const char *who, 
@@ -2620,11 +2626,10 @@
 	const char *sid = xmlnode_get_attrib(jingle, "sid");
 	const char *action = xmlnode_get_attrib(jingle, "action");
 	JingleSession *session = jabber_jingle_session_find_by_id(sid);
-	FarsightStream *stream = jabber_jingle_session_get_stream(session);
 	GList *remote_codecs = NULL;
 	GList *remote_transports = NULL;
-	GList *codec_intersection = NULL;
-	FarsightCodec *top = NULL;
+	GList *codec_intersection;
+	FsCodec *top = NULL;
 	xmlnode *description = NULL;
 	xmlnode *transport = NULL;
 
@@ -2634,7 +2639,7 @@
 			jabber_jingle_session_get_remote_jid(session));
 	jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id"));
 
-	description = xmlnode_get_child(jingle, "description");
+	description = xmlnode_get_child(content, "description");
 	transport = xmlnode_get_child(content, "transport");
 
 	/* fetch codecs from remote party */
@@ -2650,27 +2655,29 @@
 
 	purple_debug_info("jabber", "Setting remote codecs on stream\n");
 
-	farsight_stream_set_remote_codecs(stream, remote_codecs);
-	if (!strcmp(action, "session-accept")) {
-		jabber_jingle_session_set_remote_codecs(session, remote_codecs);
-	}
-
-	codec_intersection = farsight_stream_get_codec_intersection(stream);
+	purple_media_set_remote_audio_codecs(session->media, 
+					     jabber_jingle_session_get_remote_jid(session),
+					     remote_codecs);
+
+	codec_intersection = purple_media_get_negotiated_audio_codecs(session->media);
 	purple_debug_info("jabber", "codec_intersection contains %d elems\n",
 			g_list_length(codec_intersection));
 	/* get the top codec */
 	if (g_list_length(codec_intersection) > 0) {
-		top = (FarsightCodec *) codec_intersection->data;
-		purple_debug_info("jabber", "setting active codec on stream = %d\n",
+		top = (FsCodec *) codec_intersection->data;
+		purple_debug_info("jabber", "Found a suitable codec on stream = %d\n",
 				top->id);
-		farsight_stream_set_active_codec(stream, top->id);
+
 		/* we have found a suitable codec, but we will not start the stream
 		   just yet, wait for transport negotiation to complete... */
 	}
 	/* if we also got transport candidates, add them to our streams
 	   list of known remote candidates */
 	if (g_list_length(remote_transports) > 0) {
-		farsight_stream_set_remote_candidate_list(stream, remote_transports);
+		purple_media_add_remote_audio_candidates(session->media,
+							 jabber_jingle_session_get_remote_jid(session),
+							 remote_transports);
+		fs_candidate_list_destroy(remote_transports);
 	}
 	if (g_list_length(codec_intersection) == 0 &&
 			g_list_length(remote_transports)) {
@@ -2685,10 +2692,12 @@
 	if (!strcmp(action, "session-accept")) {
 		purple_media_got_accept(jabber_jingle_session_get_media(session));
 		purple_debug_info("jabber", "Got session-accept, starting stream\n");
-		farsight_stream_start(jabber_jingle_session_get_stream(session));
+		gst_element_set_state(purple_media_get_audio_pipeline(session->media), GST_STATE_PLAYING);
 	}
 
 	jabber_iq_send(result);
+
+	session->session_started = TRUE;
 }
 
 void
@@ -2707,11 +2716,10 @@
 
 	/* maybe we should look at the reasoncode to determine if it was
 	   a hangup or a reject, and call different callbacks to purple_media */
-
+	gst_element_set_state(purple_media_get_audio_pipeline(session->media), GST_STATE_NULL);
 
 	purple_media_got_hangup(jabber_jingle_session_get_media(session));
 	jabber_iq_send(result);
-	farsight_stream_stop(jabber_jingle_session_get_stream(session));
 	jabber_jingle_session_destroy(session);
 }
 
@@ -2726,6 +2734,9 @@
 	const char *sid = xmlnode_get_attrib(jingle, "sid");
 	JingleSession *session = jabber_jingle_session_find_by_id(sid);
 
+	if(!session)
+		purple_debug_error("jabber", "jabber_handle_session_candidates couldn't find session\n");
+
 	/* send acknowledement */
 	xmlnode_set_attrib(result->node, "id", xmlnode_get_attrib(packet, "id"));
 	xmlnode_set_attrib(result->node, "to", xmlnode_get_attrib(packet, "from"));
@@ -2733,10 +2744,10 @@
 
 	/* add candidates to our list of remote candidates */
 	if (g_list_length(remote_candidates) > 0) {
-		farsight_stream_add_remote_candidate(
-				jabber_jingle_session_get_stream(session),
-				remote_candidates);
-		jabber_jingle_session_add_remote_candidate(session, remote_candidates);
+		purple_media_add_remote_audio_candidates(session->media,
+							 xmlnode_get_attrib(packet, "from"),
+							 remote_candidates);
+		fs_candidate_list_destroy(remote_candidates);
 	}
 }
 
@@ -2744,25 +2755,28 @@
 void
 jabber_handle_session_content_replace(JabberStream *js, xmlnode *packet)
 {
-	JabberIq *result = jabber_iq_new(js, JABBER_IQ_RESULT);
-	JabberIq *accept = jabber_iq_new(js, JABBER_IQ_SET);
-	xmlnode *content_accept = NULL;
 	xmlnode *jingle = xmlnode_get_child(packet, "jingle");
 	const char *sid = xmlnode_get_attrib(jingle, "sid");
 	JingleSession *session = jabber_jingle_session_find_by_id(sid);
 
-	/* send acknowledement */
-	xmlnode_set_attrib(result->node, "id", xmlnode_get_attrib(packet, "id"));
-	xmlnode_set_attrib(result->node, "to", xmlnode_get_attrib(packet, "from"));
-	jabber_iq_send(result);
-
-	/* send content-accept */
-	content_accept = jabber_jingle_session_create_content_accept(session);
-	xmlnode_set_attrib(accept->node, "id", xmlnode_get_attrib(packet, "id"));
-	xmlnode_set_attrib(accept->node, "to", xmlnode_get_attrib(packet, "from"));
-	xmlnode_insert_child(accept->node, content_accept);
-
-	jabber_iq_send(accept);
+	if (!jabber_jingle_session_is_initiator(session) && session->session_started) {
+		JabberIq *result = jabber_iq_new(js, JABBER_IQ_RESULT);
+		JabberIq *accept = jabber_iq_new(js, JABBER_IQ_SET);
+		xmlnode *content_accept = NULL;
+
+		/* send acknowledement */
+		xmlnode_set_attrib(result->node, "id", xmlnode_get_attrib(packet, "id"));
+		xmlnode_set_attrib(result->node, "to", xmlnode_get_attrib(packet, "from"));
+		jabber_iq_send(result);
+
+		/* send content-accept */
+		content_accept = jabber_jingle_session_create_content_accept(session);
+		xmlnode_set_attrib(accept->node, "id", xmlnode_get_attrib(packet, "id"));
+		xmlnode_set_attrib(accept->node, "to", xmlnode_get_attrib(packet, "from"));
+		xmlnode_insert_child(accept->node, content_accept);
+
+		jabber_iq_send(accept);
+	}
 }
 
 void 
@@ -2772,18 +2786,10 @@
 	xmlnode *jingle = xmlnode_get_child(packet, "jingle");
 	xmlnode *content = NULL;
 	xmlnode *description = NULL;
-	char *sid = NULL;
-	char *initiator = NULL;
+	const char *sid = NULL;
+	const char *initiator = NULL;
 	GList *codecs = NULL;
-	FarsightSession *fs = NULL;
-	FarsightStream *audio = NULL;
-	PurpleMedia *media = NULL;
 	JabberIq *result = NULL;
-	JabberIq *content_accept = NULL;
-	xmlnode *content_accept_jingle = NULL;
-	GList *codec_intersection = NULL;
-
-	int res;
 
 	if (!jingle) {
 		purple_debug_error("jabber", "Malformed request");
@@ -2791,14 +2797,14 @@
 	}
 
 	sid = xmlnode_get_attrib(jingle, "sid");
-	//initiator = xmlnode_get_attrib(jingle, "initiator");
-	initiator = xmlnode_get_attrib(packet, "from");
+	initiator = xmlnode_get_attrib(jingle, "initiator");
+
+	if (jabber_jingle_session_find_by_id(sid)) {
+		/* This should only happen if you start a session with yourself */
+		purple_debug_error("jabber", "Jingle session with id={%s} already exists\n", sid);
+		return;
+	}
 	session = jabber_jingle_session_create_by_id(js, sid);
-	jabber_jingle_session_set_remote_jid(session,
-			xmlnode_get_attrib(packet, "from"));
-	/* set "from" as iniator (we are responder) */
-	jabber_jingle_session_set_initiator(session,
-			xmlnode_get_attrib(packet, "from"));
 
 	/* init media */
 	content = xmlnode_get_child(jingle, "content");
@@ -2811,92 +2817,21 @@
 	description = xmlnode_get_child(content, "description");
 
 	if (!description) {
-		purple_debug_error("jabber", "content tag must contain description tag");
+		purple_debug_error("jabber", "content tag must contain description tag\n");
 		/* we should create an error iq here */
 		return;
 	}
 
+	jabber_initiate_media_internal(session, initiator, initiator);
+
 	codecs = jabber_jingle_get_codecs(description);
 
-	fs = farsight_session_factory_make("rtp");
-	if (!fs) {
-		purple_debug_error("jabber", 
-				"Could not initialize Farsight's RTP plugin");
-		return;
-	}
-
-	audio = farsight_session_create_stream(fs, 
-			FARSIGHT_MEDIA_TYPE_AUDIO, 
-			FARSIGHT_STREAM_DIRECTION_BOTH);
-
-	g_object_set(G_OBJECT(audio), "transmitter", "libjingle", NULL);
-
-	media = purple_media_manager_create_media(purple_media_manager_get(), 
-			js->gc, initiator, audio, NULL);
-	jabber_jingle_session_set_media(session, media);
-	jabber_jingle_session_set_stream(session, audio);
-
-
-	g_signal_connect_swapped(G_OBJECT(media), "accepted", 
-			G_CALLBACK(jabber_session_send_accept), session);
-	g_signal_connect_swapped(G_OBJECT(media), "reject", 
-			G_CALLBACK(jabber_session_send_reject), session);
-	g_signal_connect_swapped(G_OBJECT(media), "hangup", 
-			G_CALLBACK(jabber_session_send_terminate), session);
-
-
-	GstElement *e = purple_media_get_audio_src(media);
-	farsight_stream_set_source(jabber_jingle_session_get_stream(session), e);	
-
-	e = purple_media_get_audio_sink(media);
-	farsight_stream_set_sink(jabber_jingle_session_get_stream(session), e);
-
-	farsight_stream_prepare_transports(jabber_jingle_session_get_stream(session));
-	/* For some reason Farsight starts the stream immediatly when calling
-	   farsight_stream_set_remote_codecs, before having called farsight_stream_start
-	   As a "workaround" (maybe this gets fixed in FS2) I'll store the list in
-	   the session to call it later when accepting the call */
-	/*
-	   res = 
-	   farsight_stream_set_remote_codecs(jabber_jingle_session_get_stream(session), 
-	   codecs);
-	   */
-	jabber_jingle_session_set_remote_codecs(session, codecs);
-
-	purple_media_ready(media);
-
-	/* We store the remote candidates in the session object... */
-	/*
-	   farsight_codec_list_destroy(codecs);
-	   */
-
-	/* callback for new native (local) transport candidates for the stream */
-	g_signal_connect(G_OBJECT(jabber_jingle_session_get_stream(session)),
-			"new-native-candidate",
-			G_CALLBACK(jabber_session_candidates_prepared), session);
-	/* callback for new active candidate pair (established connection) */
-	g_signal_connect(G_OBJECT(jabber_jingle_session_get_stream(session)),
-			"new-active-candidate-pair",
-			G_CALLBACK(jabber_session_candidate_pair_established),
-			session);
+	purple_media_set_remote_audio_codecs(session->media, initiator, codecs);
 
 	result = jabber_iq_new(js, JABBER_IQ_RESULT);
 	jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id"));
 	xmlnode_set_attrib(result->node, "to", xmlnode_get_attrib(packet, "from"));
 	jabber_iq_send(result);
-
-	/* should send a content-accept */
-	/* It crashes after this gets sent, also the id of this iq is set to
-	   "purple0", that seems odd... maybe I'm making some mistake here... */
-	/*
-	   content_accept = jabber_iq_new(jabber_jingle_session_get_stream(session),
-	   JABBER_IQ_SET);
-	   content_accept_jingle = jabber_jingle_session_create_content_accept(session);
-	   xmlnode_set_attrib(content_accept->node, "to", 
-	   jabber_jingle_session_get_remote_jid(session));
-	   xmlnode_insert_child(content_accept->node, content_accept_jingle);
-	   jabber_iq_send(content_accept);
-	   */
 }
 
 #endif
--- a/libpurple/protocols/jabber/jingle.c	Sun Apr 13 17:53:46 2008 +0000
+++ b/libpurple/protocols/jabber/jingle.c	Fri May 23 02:42:32 2008 +0000
@@ -28,8 +28,7 @@
 
 #ifdef USE_FARSIGHT
 
-#include <farsight/farsight.h>
-#include <farsight/farsight-transport.h>
+#include <gst/farsight/fs-candidate.h>
 
 /* keep a hash table of JingleSessions */
 static GHashTable *sessions = NULL;
@@ -66,10 +65,9 @@
 	purple_debug_info("jingle", "inserting session with key: %s into table\n",
 					  sess->id);
 	g_hash_table_insert(sessions, sess->id, sess);
-	
-	sess->remote_candidates = NULL;
-    sess->remote_codecs = NULL;
-	
+
+	sess->session_started = FALSE;
+
 	return sess;
 }
 
@@ -106,8 +104,6 @@
 {
 	g_hash_table_remove(sessions, sess->id);
 	g_free(sess->id);
-    farsight_codec_list_destroy(sess->remote_codecs);
-    g_list_free(sess->remote_candidates);
 	g_free(sess);
 }
 
@@ -115,20 +111,55 @@
 jabber_jingle_session_find_by_id(const char *id)
 {
 	purple_debug_info("jingle", "find_by_id %s\n", id);
-	purple_debug_info("jingle", "hash table: %lx\n", sessions);
+	purple_debug_info("jingle", "hash table: %p\n", sessions);
 	purple_debug_info("jingle", "hash table size %d\n",
 					  g_hash_table_size(sessions));
-	purple_debug_info("jingle", "lookup: %lx\n", g_hash_table_lookup(sessions, id));  
+	purple_debug_info("jingle", "lookup: %p\n", g_hash_table_lookup(sessions, id));  
 	return (JingleSession *) g_hash_table_lookup(sessions, id);
 }
 
 GList *
+jabber_jingle_session_find_by_js(JabberStream *js)
+{
+	GList *values = g_hash_table_get_values(sessions);
+	GList *iter = values;
+	GList *found = NULL;
+
+	for (; iter; iter = iter->next) {
+		JingleSession *session = (JingleSession *)iter->data;
+		if (session->js == js) {
+			found = g_list_prepend(found, session);
+		}
+	}
+
+	g_list_free(values);
+	return found;
+}
+
+JingleSession *jabber_jingle_session_find_by_jid(JabberStream *js, const char *jid)
+{
+	GList *values = g_hash_table_get_values(sessions);
+	GList *iter = values;
+
+	for (; iter; iter = iter->next) {
+		JingleSession *session = (JingleSession *)iter->data;
+		if (session->js == js && !strcmp(jid, session->remote_jid)) {
+			g_list_free(values);
+			return session;
+		}
+	}
+
+	g_list_free(values);
+	return NULL;	
+}
+
+GList *
 jabber_jingle_get_codecs(const xmlnode *description)
 {
 	GList *codecs = NULL;
 	xmlnode *codec_element = NULL;
 	const char *encoding_name,*id, *clock_rate;
-	FarsightCodec *codec;
+	FsCodec *codec;
 	
 	for (codec_element = xmlnode_get_child(description, "payload-type") ;
 		 codec_element ;
@@ -137,10 +168,9 @@
 		id = xmlnode_get_attrib(codec_element, "id");
 		clock_rate = xmlnode_get_attrib(codec_element, "clockrate");
 
-		codec = g_new0(FarsightCodec, 1);
-		farsight_codec_init(codec, atoi(id), encoding_name, 
-							FARSIGHT_MEDIA_TYPE_AUDIO, 
-							clock_rate ? atoi(clock_rate) : 0);
+		codec = fs_codec_new(atoi(id), encoding_name, 
+				     FS_MEDIA_TYPE_AUDIO, 
+				     clock_rate ? atoi(clock_rate) : 0);
 		codecs = g_list_append(codecs, codec);		 
 	}
 	return codecs;
@@ -151,48 +181,31 @@
 {
 	GList *candidates = NULL;
 	xmlnode *candidate = NULL;
-	FarsightTransportInfo *ti;
+	FsCandidate *c;
 	
 	for (candidate = xmlnode_get_child(transport, "candidate") ;
 		 candidate ;
 		 candidate = xmlnode_get_next_twin(candidate)) {
 		const char *type = xmlnode_get_attrib(candidate, "type");
-		ti = g_new0(FarsightTransportInfo, 1);
-		ti->component = atoi(xmlnode_get_attrib(candidate, "component"));
-		ti->ip = xmlnode_get_attrib(candidate, "ip");
-		ti->port = atoi(xmlnode_get_attrib(candidate, "port"));
-		ti->proto = strcmp(xmlnode_get_attrib(candidate, "protocol"),
+		c = fs_candidate_new(xmlnode_get_attrib(candidate, "component"), 
+							atoi(xmlnode_get_attrib(candidate, "component")),
+							strcmp(type, "host") == 0 ?
+								FS_CANDIDATE_TYPE_HOST :
+								strcmp(type, "prflx") == 0 ?
+									FS_CANDIDATE_TYPE_PRFLX :
+									FS_CANDIDATE_TYPE_RELAY,
+							strcmp(xmlnode_get_attrib(candidate, "protocol"),
 							  "udp") == 0 ? 
-				 				FARSIGHT_NETWORK_PROTOCOL_UDP :
-				 				FARSIGHT_NETWORK_PROTOCOL_TCP;
-		/* it seems Farsight RTP doesn't handle this correctly right now */
-		ti->username = xmlnode_get_attrib(candidate, "ufrag");
-		ti->password = xmlnode_get_attrib(candidate, "pwd");
-		/* not quite sure about this */
-		ti->type = strcmp(type, "host") == 0 ?
-					FARSIGHT_CANDIDATE_TYPE_LOCAL :
-					strcmp(type, "prflx") == 0 ?
-					 FARSIGHT_CANDIDATE_TYPE_DERIVED :
-					 FARSIGHT_CANDIDATE_TYPE_RELAY;
-			 
-		candidates = g_list_append(candidates, ti);
+				 				FS_NETWORK_PROTOCOL_UDP :
+				 				FS_NETWORK_PROTOCOL_TCP,
+							xmlnode_get_attrib(candidate, "ip"),
+							atoi(xmlnode_get_attrib(candidate, "port")));
+		candidates = g_list_append(candidates, c);
 	}
 	
 	return candidates;
 }
 
-FarsightStream *
-jabber_jingle_session_get_stream(const JingleSession *sess)
-{
-	return sess->stream;
-}
-
-void
-jabber_jingle_session_set_stream(JingleSession *sess, FarsightStream *stream)
-{
-	sess->stream = stream;
-}
-
 PurpleMedia *
 jabber_jingle_session_get_media(const JingleSession *sess)
 {
@@ -237,36 +250,6 @@
 	return sess->is_initiator;
 }
 
-void
-jabber_jingle_session_add_remote_candidate(JingleSession *sess,
-										   const GList *candidate)
-{
-	/* the length of the candidate list should be 1... */
-	GList *cand = candidate;
-	for (; cand ; cand = cand->next) {
-		purple_debug_info("jingle", "Adding remote candidate with id = %s\n",
-						  ((FarsightTransportInfo *) cand->data)->candidate_id);
-		sess->remote_candidates = g_list_append(sess->remote_candidates, 
-												cand->data);
-	}
-}
-
-static GList *
-jabber_jingle_session_get_remote_candidate(const JingleSession *sess,
-										   const gchar *id)
-{
-	GList *candidates = NULL;
-	GList *find = sess->remote_candidates;
-	for (; find ; find = find->next) {
-		const FarsightTransportInfo *candidate =
-			(FarsightTransportInfo *) find->data;
-		if (!strcmp(candidate->candidate_id, id)) {
-			candidates = g_list_append(candidates, (void *) candidate);
-		}
-	}
-	return candidates;
-}
-
 static xmlnode *
 jabber_jingle_session_create_jingle_element(const JingleSession *sess,
 											const char *action)
@@ -301,32 +284,105 @@
 	return jingle;
 }
 
-static xmlnode *
+xmlnode *
 jabber_jingle_session_create_description(const JingleSession *sess)
 {
-    GList *codecs =
-		farsight_stream_get_local_codecs(jabber_jingle_session_get_stream(sess));
-    
+    GList *codecs = purple_media_get_local_audio_codecs(sess->media);
     xmlnode *description = xmlnode_new("description");
+
 	xmlnode_set_namespace(description, "urn:xmpp:tmp:jingle:apps:audio-rtp");
 	
 	/* get codecs */
 	for (; codecs ; codecs = codecs->next) {
-		FarsightCodec *codec = (FarsightCodec*)codecs->data;
-		char id[8], clockrate[10];
+		FsCodec *codec = (FsCodec*)codecs->data;
+		char id[8], clockrate[10], channels[10];
 		xmlnode *payload = xmlnode_new_child(description, "payload-type");
 		
 		g_snprintf(id, sizeof(id), "%d", codec->id);
 		g_snprintf(clockrate, sizeof(clockrate), "%d", codec->clock_rate);
+		g_snprintf(channels, sizeof(channels), "%d", codec->channels);
 		
 		xmlnode_set_attrib(payload, "name", codec->encoding_name);
 		xmlnode_set_attrib(payload, "id", id);
 		xmlnode_set_attrib(payload, "clockrate", clockrate);
+		xmlnode_set_attrib(payload, "channels", channels);
     }
     
+    fs_codec_list_destroy(codecs);
     return description;
 }
     
+static guint
+jabber_jingle_get_priority(guint type, guint network)
+{
+	switch (type) {
+		case FS_CANDIDATE_TYPE_HOST:
+			return network == 0 ? 4096 : network == 1 ? 2048 : 1024;
+			break;
+		case FS_CANDIDATE_TYPE_PRFLX:
+			return 126;
+			break;
+		case FS_CANDIDATE_TYPE_RELAY:
+			return 100;
+			break;
+		default:
+			return 0; /* unknown type, should not happen */
+	}
+}
+
+static xmlnode *
+jabber_jingle_session_create_candidate_info(FsCandidate *c, FsCandidate *remote)
+{
+	char port[8];
+	char prio[8];
+	char component[8];
+	xmlnode *candidate = NULL;
+	
+	candidate = xmlnode_new("candidate");
+	
+	g_snprintf(port, sizeof(port), "%d", c->port);
+	g_snprintf(prio, sizeof(prio), "%d", 
+		   jabber_jingle_get_priority(c->type, 0));
+	g_snprintf(component, sizeof(component), "%d", c->component_id);
+	
+	xmlnode_set_attrib(candidate, "component", component);
+	xmlnode_set_attrib(candidate, "foundation", "1"); /* what about this? */
+	xmlnode_set_attrib(candidate, "generation", "0"); /* ? */
+	xmlnode_set_attrib(candidate, "ip", c->ip);
+	xmlnode_set_attrib(candidate, "network", "0"); /* ? */
+	xmlnode_set_attrib(candidate, "port", port);
+	xmlnode_set_attrib(candidate, "priority", prio); /* Is this correct? */
+	xmlnode_set_attrib(candidate, "protocol",
+			   c->proto == FS_NETWORK_PROTOCOL_UDP ?
+			   "udp" : "tcp");
+	if (c->username)
+		xmlnode_set_attrib(candidate, "ufrag", c->username);
+	if (c->password)
+		xmlnode_set_attrib(candidate, "pwd", c->password);
+	
+	xmlnode_set_attrib(candidate, "type", 
+			   c->type == FS_CANDIDATE_TYPE_HOST ? 
+			   "host" :
+			   c->type == FS_CANDIDATE_TYPE_PRFLX ? 
+			   "prflx" :
+		       	   c->type == FS_CANDIDATE_TYPE_RELAY ? 
+			   "relay" : NULL);
+
+	/* relay */
+	if (c->type == FS_CANDIDATE_TYPE_RELAY) {
+		/* set rel-addr and rel-port? How? */
+	}
+
+	if (remote) {
+		char remote_port[8];
+		g_snprintf(remote_port, sizeof(remote_port), "%d", remote->port);
+		xmlnode_set_attrib(candidate, "rem-addr", remote->ip);
+		xmlnode_set_attrib(candidate, "rem-port", remote_port);
+	}
+
+	return candidate;
+}
+
 /* split into two separate methods, one to generate session-accept
 	(includes codecs) and one to generate transport-info (includes transports
 	candidates) */
@@ -337,6 +393,8 @@
 		jabber_jingle_session_create_jingle_element(sess, "session-accept");
 	xmlnode *content = NULL;
 	xmlnode *description = NULL;
+	xmlnode *transport = NULL;
+	xmlnode *candidate = NULL;
 	
 	
 	content = xmlnode_new_child(jingle, "content");
@@ -345,199 +403,82 @@
 	xmlnode_set_attrib(content, "profile", "RTP/AVP");
 	
 	description = jabber_jingle_session_create_description(sess);
-    xmlnode_insert_child(jingle, description);
-	
+	xmlnode_insert_child(content, description);
+
+	transport = xmlnode_new_child(content, "transport");
+	xmlnode_set_namespace(transport, "urn:xmpp:tmp:jingle:transports:ice-udp");
+	candidate = jabber_jingle_session_create_candidate_info(
+			purple_media_get_local_candidate(sess->media),
+			purple_media_get_remote_candidate(sess->media));
+	xmlnode_insert_child(transport, candidate);
+
 	return jingle;
 }
 
-static guint
-jabber_jingle_get_priority(guint type, guint network)
-{
-	switch (type) {
-		case FARSIGHT_CANDIDATE_TYPE_LOCAL:
-			return network == 0 ? 4096 : network == 1 ? 2048 : 1024;
-			break;
-		case FARSIGHT_CANDIDATE_TYPE_DERIVED:
-			return 126;
-			break;
-		case FARSIGHT_CANDIDATE_TYPE_RELAY:
-			return 100;
-			break;
-		default:
-			return 0; /* unknown type, should not happen */
-	}
-}
 
 xmlnode *
-jabber_jingle_session_create_transport_info(const JingleSession *sess,
-											gchar *candidate_id)
+jabber_jingle_session_create_transport_info(const JingleSession *sess)
 {
 	xmlnode *jingle = 
 		jabber_jingle_session_create_jingle_element(sess, "transport-info");
 	xmlnode *content = NULL;
 	xmlnode *transport = NULL;
-	GList *candidates =
-		farsight_stream_get_native_candidate(
-			jabber_jingle_session_get_stream(sess), candidate_id);
-	
+	GList *candidates = purple_media_get_local_audio_candidates(sess->media);
+
 	content = xmlnode_new_child(jingle, "content");
 	xmlnode_set_attrib(content, "creator", "initiator");
 	xmlnode_set_attrib(content, "name", "audio-content");
 	xmlnode_set_attrib(content, "profile", "RTP/AVP");
 		
 	transport = xmlnode_new_child(content, "transport");
-	xmlnode_set_namespace(transport, "urn:xmpp:tmp:jingle:transports:ice-tcp");
+	xmlnode_set_namespace(transport, "urn:xmpp:tmp:jingle:transports:ice-udp");
 	
 	/* get transport candidate */
 	for (; candidates ; candidates = candidates->next) {
-		FarsightTransportInfo *transport_info = 
-			(FarsightTransportInfo *) candidates->data;
-		char port[8];
-		char prio[8];
-		char component[8];
-		xmlnode *candidate = NULL;
-		
-		if (!strcmp(transport_info->ip, "127.0.0.1")) {
+		FsCandidate *c = (FsCandidate *) candidates->data;
+
+		if (!strcmp(c->ip, "127.0.0.1")) {
 			continue;
 		}
-		
-		candidate = xmlnode_new_child(transport, "candidate");
-		
-		g_snprintf(port, sizeof(port), "%d", transport_info->port);
-		g_snprintf(prio, sizeof(prio), "%d", 
-				   jabber_jingle_get_priority(transport_info->type, 0));
-		g_snprintf(component, sizeof(component), "%d", transport_info->component);
-		
-		xmlnode_set_attrib(candidate, "component", component);
-		xmlnode_set_attrib(candidate, "foundation", "1"); /* what about this? */
-		xmlnode_set_attrib(candidate, "generation", "0"); /* ? */
-		xmlnode_set_attrib(candidate, "ip", transport_info->ip);
-		xmlnode_set_attrib(candidate, "network", "0"); /* ? */
-		xmlnode_set_attrib(candidate, "port", port);
-		xmlnode_set_attrib(candidate, "priority", prio); /* Is this correct? */
-		xmlnode_set_attrib(candidate, "protocol",
-						   transport_info->proto == FARSIGHT_NETWORK_PROTOCOL_UDP ?
-						   "udp" : "tcp");
-		if (transport_info->username)
-			xmlnode_set_attrib(candidate, "ufrag", transport_info->username);
-		if (transport_info->password)
-			xmlnode_set_attrib(candidate, "pwd", transport_info->password);
-		
-		xmlnode_set_attrib(candidate, "type", 
-						   transport_info->type == FARSIGHT_CANDIDATE_TYPE_LOCAL ? 
-						   "host" :
-						   transport_info->type == FARSIGHT_CANDIDATE_TYPE_DERIVED ? 
-						   "prflx" :
-					       transport_info->type == FARSIGHT_CANDIDATE_TYPE_RELAY ? 
-						   "relay" : NULL);
-		
+
+		xmlnode_insert_child(transport,
+				     jabber_jingle_session_create_candidate_info(c, NULL));
 	}
-	farsight_transport_list_destroy(candidates);
+	fs_candidate_list_destroy(candidates);
 	
 	return jingle;
 }
 
 xmlnode *
 jabber_jingle_session_create_content_replace(const JingleSession *sess,
-											 gchar *native_candidate_id,
-											 gchar *remote_candidate_id)
+					     FsCandidate *native_candidate,
+					     FsCandidate *remote_candidate)
 {
 	xmlnode *jingle = 
 		jabber_jingle_session_create_jingle_element(sess, "content-replace");
 	xmlnode *content = NULL;
 	xmlnode *transport = NULL;
-	xmlnode *candidate = NULL;
-	xmlnode *description = NULL;
-	xmlnode *payload_type = NULL;
-	char local_port[8];
-	char remote_port[8];
-	char prio[8];
-	char component[8];
-	
+
 	purple_debug_info("jingle", "creating content-modify for native candidate %s " \
-					  ", remote candidate %s\n", native_candidate_id,
-					  remote_candidate_id);
-	
-	/* It seems the ID's we get from the Farsight callback is phony... */
-	/*
-	GList *native_candidates = 
-		farsight_stream_get_native_candidate(jabber_jingle_session_get_stream(sess),
-											 native_candidate_id);
-	purple_debug_info("jingle", "found %d native candidates with id %s\n",
-					  g_list_length(native_candidates), native_candidate_id);
-	GList *remote_candidates =
-		jabber_jingle_session_get_remote_candidate(sess, remote_candidate_id);
-	*/
-	
-	/* we assume lists have length == 1, I think they must... */
-	/*
-	FarsightTransportInfo *native = 
-		(FarsightTransportInfo *) native_candidates->data;
-	FarsightTransportInfo *remote =
-		(FarsightTransportInfo *) remote_candidates->data;
-	*/
-	
+			  ", remote candidate %s\n", native_candidate->candidate_id,
+			  remote_candidate->candidate_id);
+
 	content = xmlnode_new_child(jingle, "content");
 	xmlnode_set_attrib(content, "creator", "initiator");
 	xmlnode_set_attrib(content, "name", "audio-content");
 	xmlnode_set_attrib(content, "profile", "RTP/AVP");
 	
-	description = xmlnode_new_child(content, "description");
-	xmlnode_set_namespace(description, "urn:xmpp:tmp:jingle:apps:audio-rtp");
-	
-	payload_type = xmlnode_new_child(description, "payload-type");
 	/* get top codec from codec_intersection to put here... */
 	/* later on this should probably handle changing codec */
-	
-	/* Skip creating the actual "content" element for now, since we don't
-		get enough info in the Farsight callback */
-#if 0
-	transport = xmlnode_new_child(content, "transport");
-	xmlnode_set_namespace(transport, "urn:xmpp:tmp:jingle:transports:ice-tcp");
-	
-	candidate = xmlnode_new_child(transport, "candidate");
-	
-	g_snprintf(local_port, sizeof(local_port), "%d", native->port);
-	g_snprintf(remote_port, sizeof(remote_port), "%d", remote->port);
-	g_snprintf(prio, sizeof(prio), "%d",
-			   jabber_jingle_get_priority(native->type, 0));
-	g_snprintf(component, sizeof(component), "%d", native->component);
+
+	xmlnode_insert_child(content, jabber_jingle_session_create_description(sess));
 
-	xmlnode_set_attrib(candidate, "component", component);
-	xmlnode_set_attrib(candidate, "foundation", "1"); /* what about this? */
-	xmlnode_set_attrib(candidate, "generation", "0"); /* ? */
-	xmlnode_set_attrib(candidate, "ip", native->ip);
-	xmlnode_set_attrib(candidate, "network", "1"); /* ? */
-	xmlnode_set_attrib(candidate, "port", local_port);
-	xmlnode_set_attrib(candidate, "priority", prio); /* Is this correct? */
-	xmlnode_set_attrib(candidate, "protocol", 
-					   native->proto == FARSIGHT_NETWORK_PROTOCOL_UDP ? 
-					   "udp" : "tcp");
-	if (native->username)
-		xmlnode_set_attrib(candidate, "ufrag", native->username);
-	if (native->password)
-		xmlnode_set_attrib(candidate, "pwd", native->password);
-		
-	xmlnode_set_attrib(candidate, "type", 
-					   native->type == FARSIGHT_CANDIDATE_TYPE_LOCAL ? 
-					   "host" : 
-					   native->type == FARSIGHT_CANDIDATE_TYPE_DERIVED ? 
-					   "prflx" : 
-					   native->type == FARSIGHT_CANDIDATE_TYPE_RELAY ? 
-					   "relay" : NULL);
-	/* relay */
-	if (native->type == FARSIGHT_CANDIDATE_TYPE_RELAY) {
-		/* set rel-addr and rel-port? How? */
-	}
-	
-	xmlnode_set_attrib(candidate, "rem-addr", remote->ip);
-	xmlnode_set_attrib(candidate, "rem-port", remote_port);
-	
-	farsight_transport_list_destroy(native_candidates);
-	farsight_transport_list_destroy(remote_candidates);
-	
-#endif /* 0 */
-	
+	transport = xmlnode_new_child(content, "transport");
+	xmlnode_set_namespace(transport, "urn:xmpp:tmp:jingle:transports:ice-udp");
+	xmlnode_insert_child(transport,
+			     jabber_jingle_session_create_candidate_info(native_candidate,
+									 remote_candidate));
+
 	purple_debug_info("jingle", "End create content modify\n");
 	
 	return jingle;
@@ -556,24 +497,9 @@
 	xmlnode_set_attrib(content, "name", "audio-content");
 	xmlnode_set_attrib(content, "profile", "RTP/AVP");
 	
-    xmlnode_insert_child(jingle, description);
+    xmlnode_insert_child(content, description);
     
 	return jingle;
 }
 
-void
-jabber_jingle_session_set_remote_codecs(JingleSession *sess, GList *codecs)
-{
-    if (sess->remote_codecs) {
-        farsight_codec_list_destroy(sess->remote_codecs);
-    }
-    sess->remote_codecs = codecs;
-}
-
-GList *
-jabber_jingle_session_get_remote_codecs(const JingleSession *sess)
-{
-    return sess->remote_codecs;
-}
-
 #endif /* USE_FARSIGHT */
--- a/libpurple/protocols/jabber/jingle.h	Sun Apr 13 17:53:46 2008 +0000
+++ b/libpurple/protocols/jabber/jingle.h	Fri May 23 02:42:32 2008 +0000
@@ -26,20 +26,16 @@
 
 #ifdef USE_FARSIGHT
 
-#include <farsight/farsight.h>
-
 G_BEGIN_DECLS
 
 typedef struct {
 	char *id;
 	JabberStream *js;
-	FarsightStream *stream;
 	PurpleMedia *media;
 	char *remote_jid;
 	char *initiator;
 	gboolean is_initiator;
-	GList *remote_candidates;
-    GList *remote_codecs;
+	gboolean session_started;
 } JingleSession;
 
 JingleSession *jabber_jingle_session_create(JabberStream *js);
@@ -52,9 +48,8 @@
 void jabber_jingle_session_destroy(JingleSession *sess);
 
 JingleSession *jabber_jingle_session_find_by_id(const char *id);
-
-FarsightStream *jabber_jingle_session_get_stream(const JingleSession *sess);
-void jabber_jingle_session_set_stream(JingleSession *sess, FarsightStream *stream);
+GList *jabber_jingle_session_find_by_js(JabberStream *js);
+JingleSession *jabber_jingle_session_find_by_jid(JabberStream *js, const char *jid);
 
 PurpleMedia *jabber_jingle_session_get_media(const JingleSession *sess);
 void jabber_jingle_session_set_media(JingleSession *sess, PurpleMedia *media);
@@ -70,20 +65,16 @@
 void jabber_jingle_session_set_initiator(JingleSession *sess, 
 										 const char *initiator);
 
-void jabber_jingle_session_add_remote_candidate(JingleSession *sess,
-												const GList *candidate);
-
 xmlnode *jabber_jingle_session_create_terminate(const JingleSession *sess,
 												const char *reasoncode,
 												const char *reasontext);
-
 xmlnode *jabber_jingle_session_create_session_accept(const JingleSession *sess);
-xmlnode *jabber_jingle_session_create_transport_info(const JingleSession *sess,
-													 gchar *candidate_id);
+xmlnode *jabber_jingle_session_create_transport_info(const JingleSession *sess);
 xmlnode *jabber_jingle_session_create_content_replace(const JingleSession *sess,
-													  gchar *native_candidate_id,
-													  gchar *remote_candidate_id);
+						      FsCandidate *native_candidate,
+						      FsCandidate *remote_candidate);
 xmlnode *jabber_jingle_session_create_content_accept(const JingleSession *sess);
+xmlnode *jabber_jingle_session_create_description(const JingleSession *sess);
 
 /**
  * Gets a list of Farsight codecs from a Jingle <description> tag
@@ -95,10 +86,6 @@
 
 GList *jabber_jingle_get_candidates(const xmlnode *transport);
 
-void jabber_jingle_session_set_remote_codecs(JingleSession *sess,
-                                             GList *codecs);
-GList *jabber_jingle_session_get_remote_codecs(const JingleSession *sess);
-
 G_END_DECLS
 
 #endif /* USE_FARSIGHT */
--- a/pidgin/gtkmedia.c	Sun Apr 13 17:53:46 2008 +0000
+++ b/pidgin/gtkmedia.c	Fri May 23 02:42:32 2008 +0000
@@ -32,8 +32,6 @@
 
 #ifdef USE_FARSIGHT
 
-#include <farsight/farsight.h>
-
 typedef enum
 {
 	/* Waiting for response */
@@ -181,37 +179,18 @@
 	gtk_widget_show_all(media->priv->reject);
 }
 
-static void
-pidgin_media_finalize (GObject *media)
-{
-	PidginMedia *gtkmedia = PIDGIN_MEDIA(media);
-	if (gtkmedia->priv->media)
-		g_object_unref(gtkmedia->priv->media);
-	if (gtkmedia->priv->send_level)
-		gst_object_unref(gtkmedia->priv->send_level);
-	if (gtkmedia->priv->recv_level)
-		gst_object_unref(gtkmedia->priv->recv_level);
-}
-
-static void
-pidgin_media_emit_message(PidginMedia *gtkmedia, const char *msg)
-{
-	g_signal_emit(gtkmedia, pidgin_media_signals[MESSAGE], 0, msg);
-}
-
 static gboolean
 level_message_cb(GstBus *bus, GstMessage *message, PidginMedia *gtkmedia)
 {
 	const GstStructure *s;
 	const gchar *name;
 
-	int channels;
-	gdouble rms_db, peak_db, decay_db;
-	gdouble rms;
+	gdouble rms_db;
+	gdouble percent;
 	const GValue *list;
 	const GValue *value;
 
-	GstElement *src = GST_ELEMENT(message);
+	GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(message));
 
 	if (message->type != GST_MESSAGE_ELEMENT)
 		return TRUE;
@@ -228,14 +207,50 @@
 	value = gst_value_list_get_value(list, 0);
 	rms_db = g_value_get_double(value);
 
+	percent = pow(10, rms_db / 20) * 5;
+
+	if(percent > 1.0)
+		percent = 1.0;
+
 	if (!strcmp(gst_element_get_name(src), "sendlevel"))	
-		gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(gtkmedia->priv->send_progress), pow(10, rms_db / 20) * 5);
+		gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(gtkmedia->priv->send_progress), percent);
 	else
-		gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(gtkmedia->priv->recv_progress), pow(10, rms_db / 20) * 5);
+		gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(gtkmedia->priv->recv_progress), percent);
 
 	return TRUE;
 }
 
+
+static void
+pidgin_media_disconnect_levels(PurpleMedia *media, PidginMedia *gtkmedia)
+{
+	GstElement *element = purple_media_get_audio_pipeline(media);
+	gulong handler_id = g_signal_handler_find(G_OBJECT(gst_pipeline_get_bus(GST_PIPELINE(element))),
+						  G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0, 
+						  NULL, G_CALLBACK(level_message_cb), gtkmedia);
+	g_signal_handler_disconnect(G_OBJECT(gst_pipeline_get_bus(GST_PIPELINE(element))), handler_id);
+}
+
+static void
+pidgin_media_finalize (GObject *media)
+{
+	PidginMedia *gtkmedia = PIDGIN_MEDIA(media);
+	if (gtkmedia->priv->media) {
+		pidgin_media_disconnect_levels(gtkmedia->priv->media, gtkmedia);
+		g_object_unref(gtkmedia->priv->media);
+	}
+	if (gtkmedia->priv->send_level)
+		gst_object_unref(gtkmedia->priv->send_level);
+	if (gtkmedia->priv->recv_level)
+		gst_object_unref(gtkmedia->priv->recv_level);
+}
+
+static void
+pidgin_media_emit_message(PidginMedia *gtkmedia, const char *msg)
+{
+	g_signal_emit(gtkmedia, pidgin_media_signals[MESSAGE], 0, msg);
+}
+
 static void
 pidgin_media_ready_cb(PurpleMedia *media, PidginMedia *gtkmedia)
 {
--- a/pidgin/gtkmedia.h	Sun Apr 13 17:53:46 2008 +0000
+++ b/pidgin/gtkmedia.h	Fri May 23 02:42:32 2008 +0000
@@ -28,7 +28,6 @@
 
 #ifdef USE_FARSIGHT
 
-#include <farsight/farsight.h>
 #include <gtk/gtk.h>
 #include <glib-object.h>