changeset 26044:b04508e5cc6c

merge of 'd43b253ca7568a3f07aa77bf397fe4351db73a90' and 'dcc53c9783aecbdde6269c4310ec4b0cf511bd9e'
author Marcus Lundblad <ml@update.uu.se>
date Mon, 12 Jan 2009 19:27:55 +0000
parents e5f9cf20c4aa (current diff) 5275c7ef9edf (diff)
children 62b41bb71a60
files libpurple/media.c
diffstat 5 files changed, 694 insertions(+), 96 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/media.c	Sat Jan 10 01:32:34 2009 +0000
+++ b/libpurple/media.c	Mon Jan 12 19:27:55 2009 +0000
@@ -239,7 +239,7 @@
 					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
 					 purple_smarshal_VOID__POINTER_POINTER_OBJECT,
 					 G_TYPE_NONE, 3, G_TYPE_POINTER,
-					 G_TYPE_POINTER, FS_TYPE_CANDIDATE);
+					 G_TYPE_POINTER, PURPLE_TYPE_MEDIA_CANDIDATE);
 	purple_media_signals[CANDIDATES_PREPARED] = g_signal_new("candidates-prepared", G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
 					 purple_smarshal_VOID__STRING_STRING,
@@ -247,7 +247,8 @@
 	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_NONE, 2, PURPLE_TYPE_MEDIA_CANDIDATE,
+					 PURPLE_TYPE_MEDIA_CANDIDATE);
 	purple_media_signals[CODECS_READY] = g_signal_new("codecs-ready", G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
 					 g_cclosure_marshal_VOID__STRING,
@@ -413,6 +414,169 @@
 
 }
 
+PurpleMediaCandidate *
+purple_media_candidate_new(const gchar *foundation, guint component_id,
+		PurpleMediaCandidateType type,
+		PurpleMediaNetworkProtocol proto,
+		const gchar *ip, guint port)
+{
+	PurpleMediaCandidate *candidate = g_new0(PurpleMediaCandidate, 1);
+	candidate->foundation = g_strdup(foundation);
+	candidate->component_id = component_id;
+	candidate->type = type;
+	candidate->proto = proto;
+	candidate->ip = g_strdup(ip);
+	candidate->port = port;
+	return candidate;
+}
+
+static PurpleMediaCandidate *
+purple_media_candidate_copy(PurpleMediaCandidate *candidate)
+{
+	PurpleMediaCandidate *new_candidate;
+
+	if (candidate == NULL)
+		return NULL;
+
+	new_candidate = g_new0(PurpleMediaCandidate, 1);
+	new_candidate->foundation = g_strdup(candidate->foundation);
+	new_candidate->component_id = candidate->component_id;
+	new_candidate->ip = g_strdup(candidate->ip);
+	new_candidate->port = candidate->port;
+	new_candidate->base_ip = g_strdup(candidate->base_ip);
+	new_candidate->base_port = candidate->base_port;
+	new_candidate->proto = candidate->proto;
+	new_candidate->priority = candidate->priority;
+	new_candidate->type = candidate->type;
+	new_candidate->username = g_strdup(candidate->username);
+	new_candidate->password = g_strdup(candidate->password);
+	new_candidate->ttl = candidate->ttl;
+	return new_candidate;
+}
+
+static void
+purple_media_candidate_free(PurpleMediaCandidate *candidate)
+{
+	if (candidate == NULL)
+		return;
+
+	g_free((gchar*)candidate->foundation);
+	g_free((gchar*)candidate->ip);
+	g_free((gchar*)candidate->base_ip);
+	g_free((gchar*)candidate->username);
+	g_free((gchar*)candidate->password);
+	g_free(candidate);
+}
+
+static FsCandidate *
+purple_media_candidate_to_fs(PurpleMediaCandidate *candidate)
+{
+	FsCandidate *fscandidate;
+
+	if (candidate == NULL)
+		return NULL;
+
+	fscandidate = fs_candidate_new(candidate->foundation,
+		candidate->component_id, candidate->type,
+		candidate->proto, candidate->ip, candidate->port);
+
+	fscandidate->base_ip = g_strdup(candidate->base_ip);
+	fscandidate->base_port = candidate->base_port;
+	fscandidate->priority = candidate->priority;
+	fscandidate->username = g_strdup(candidate->username);
+	fscandidate->password = g_strdup(candidate->password);
+	fscandidate->ttl = candidate->ttl;
+	return fscandidate;
+}
+
+static PurpleMediaCandidate *
+purple_media_candidate_from_fs(FsCandidate *fscandidate)
+{
+	PurpleMediaCandidate *candidate;
+
+	if (fscandidate == NULL)
+		return NULL;
+
+	candidate = purple_media_candidate_new(fscandidate->foundation,
+		fscandidate->component_id, fscandidate->type,
+		fscandidate->proto, fscandidate->ip, fscandidate->port);
+	candidate->base_ip = g_strdup(fscandidate->base_ip);
+	candidate->base_port = fscandidate->base_port;
+	candidate->priority = fscandidate->priority;
+	candidate->username = g_strdup(fscandidate->username);
+	candidate->password = g_strdup(fscandidate->password);
+	candidate->ttl = fscandidate->ttl;
+	return candidate;
+}
+
+static GList *
+purple_media_candidate_list_from_fs(GList *candidates)
+{
+	GList *new_list = NULL;
+
+	for (; candidates; candidates = g_list_next(candidates)) {
+		new_list = g_list_prepend(new_list,
+				purple_media_candidate_from_fs(
+				candidates->data));
+	}
+
+	new_list = g_list_reverse(new_list);
+	return new_list;
+}
+
+static GList *
+purple_media_candidate_list_to_fs(GList *candidates)
+{
+	GList *new_list = NULL;
+
+	for (; candidates; candidates = g_list_next(candidates)) {
+		new_list = g_list_prepend(new_list,
+				purple_media_candidate_to_fs(
+				candidates->data));
+	}
+
+	new_list = g_list_reverse(new_list);
+	return new_list;
+}
+
+GList *
+purple_media_candidate_list_copy(GList *candidates)
+{
+	GList *new_list = NULL;
+
+	for (; candidates; candidates = g_list_next(candidates)) {
+		new_list = g_list_prepend(new_list, g_boxed_copy(
+				PURPLE_TYPE_MEDIA_CANDIDATE,
+				candidates->data));
+	}
+
+	new_list = g_list_reverse(new_list);
+	return new_list;
+}
+
+void
+purple_media_candidate_list_free(GList *candidates)
+{
+	for (; candidates; candidates =
+			g_list_delete_link(candidates, candidates)) {
+		g_boxed_free(PURPLE_TYPE_MEDIA_CANDIDATE,
+				candidates->data);
+	}
+}
+
+GType
+purple_media_candidate_get_type()
+{
+	static GType type = 0;
+
+	if (type == 0) {
+		type = g_boxed_type_register_static("PurpleMediaCandidate",
+				(GBoxedCopyFunc)purple_media_candidate_copy,
+				(GBoxedFreeFunc)purple_media_candidate_free);
+	}
+	return type;
+}
+
 static FsMediaType
 purple_media_to_fs_media_type(PurpleMediaSessionType type)
 {
@@ -458,6 +622,232 @@
 	return result;
 }
 
+void
+purple_media_codec_add_optional_parameter(PurpleMediaCodec *codec,
+		const gchar *name, const gchar *value)
+{
+	PurpleMediaCodecParameter *new_param;
+
+	g_return_if_fail(name != NULL && value != NULL);
+
+	new_param = g_new0(PurpleMediaCodecParameter, 1);
+	new_param->name = g_strdup(name);
+	new_param->value = g_strdup(value);
+	codec->optional_params = g_list_append(
+			codec->optional_params, new_param);
+}
+
+void
+purple_media_codec_remove_optional_parameter(PurpleMediaCodec *codec,
+		PurpleMediaCodecParameter *param)
+{
+	g_free(param->name);
+	g_free(param->value);
+	g_free(param);
+	codec->optional_params =
+			g_list_remove(codec->optional_params, param);
+}
+
+PurpleMediaCodecParameter *
+purple_media_codec_get_optional_parameter(PurpleMediaCodec *codec,
+		const gchar *name, const gchar *value)
+{
+	GList *iter;
+
+	g_return_val_if_fail(codec != NULL, NULL);
+	g_return_val_if_fail(name != NULL, NULL);
+
+	for (iter = codec->optional_params; iter; iter = g_list_next(iter)) {
+		PurpleMediaCodecParameter *param = iter->data;
+		if (!g_ascii_strcasecmp(param->name, name) &&
+				(value == NULL ||
+				!g_ascii_strcasecmp(param->value, value)))
+			return param;
+	}
+
+	return NULL;
+}
+
+PurpleMediaCodec *
+purple_media_codec_new(int id, const char *encoding_name,
+		PurpleMediaSessionType media_type, guint clock_rate)
+{
+	PurpleMediaCodec *codec = g_new0(PurpleMediaCodec, 1);
+
+	codec->id = id;
+	codec->encoding_name = g_strdup(encoding_name);
+	codec->media_type = media_type;
+	codec->clock_rate = clock_rate;
+	return codec;
+}
+
+static PurpleMediaCodec *
+purple_media_codec_copy(PurpleMediaCodec *codec)
+{
+	PurpleMediaCodec *new_codec;
+	GList *iter;
+
+	if (codec == NULL)
+		return NULL;
+
+	new_codec = purple_media_codec_new(codec->id, codec->encoding_name,
+			codec->media_type, codec->clock_rate);
+	new_codec->channels = codec->channels;
+
+	for (iter = codec->optional_params; iter; iter = g_list_next(iter)) {
+		PurpleMediaCodecParameter *param =
+				(PurpleMediaCodecParameter*)iter->data;
+		purple_media_codec_add_optional_parameter(new_codec,
+				param->name, param->value);
+	}
+
+	return new_codec;
+}
+
+static void
+purple_media_codec_free(PurpleMediaCodec *codec)
+{
+	if (codec == NULL)
+		return;
+
+	g_free(codec->encoding_name);
+
+	for (; codec->optional_params; codec->optional_params =
+			g_list_delete_link(codec->optional_params,
+			codec->optional_params)) {
+		purple_media_codec_remove_optional_parameter(codec,
+				codec->optional_params->data);
+	}
+
+	g_free(codec);
+}
+
+static FsCodec *
+purple_media_codec_to_fs(const PurpleMediaCodec *codec)
+{
+	FsCodec *new_codec;
+	GList *iter;
+
+	if (codec == NULL)
+		return NULL;
+
+	new_codec = fs_codec_new(codec->id, codec->encoding_name,
+			purple_media_to_fs_media_type(codec->media_type),
+			codec->clock_rate);
+	new_codec->channels = codec->channels;
+
+	for (iter = codec->optional_params; iter; iter = g_list_next(iter)) {
+		PurpleMediaCodecParameter *param =
+				(PurpleMediaCodecParameter*)iter->data;
+		fs_codec_add_optional_parameter(new_codec,
+				param->name, param->value);
+	}
+
+	return new_codec;
+}
+
+static PurpleMediaCodec *
+purple_media_codec_from_fs(const FsCodec *codec)
+{
+	PurpleMediaCodec *new_codec;
+	GList *iter;
+
+	if (codec == NULL)
+		return NULL;
+
+	new_codec = purple_media_codec_new(codec->id, codec->encoding_name,
+			purple_media_from_fs(codec->media_type,
+			FS_DIRECTION_BOTH), codec->clock_rate);
+	new_codec->channels = codec->channels;
+
+	for (iter = codec->optional_params; iter; iter = g_list_next(iter)) {
+		FsCodecParameter *param = (FsCodecParameter*)iter->data;
+		purple_media_codec_add_optional_parameter(new_codec,
+				param->name, param->value);
+	}
+
+	return new_codec;
+}
+
+gchar *
+purple_media_codec_to_string(const PurpleMediaCodec *codec)
+{
+	FsCodec *fscodec = purple_media_codec_to_fs(codec);
+	gchar *str = fs_codec_to_string(fscodec);
+	fs_codec_destroy(fscodec);
+	return str;
+}
+
+static GList *
+purple_media_codec_list_from_fs(GList *codecs)
+{
+	GList *new_list = NULL;
+
+	for (; codecs; codecs = g_list_next(codecs)) {
+		new_list = g_list_prepend(new_list,
+				purple_media_codec_from_fs(
+				codecs->data));
+	}
+
+	new_list = g_list_reverse(new_list);
+	return new_list;
+}
+
+static GList *
+purple_media_codec_list_to_fs(GList *codecs)
+{
+	GList *new_list = NULL;
+
+	for (; codecs; codecs = g_list_next(codecs)) {
+		new_list = g_list_prepend(new_list,
+				purple_media_codec_to_fs(
+				codecs->data));
+	}
+
+	new_list = g_list_reverse(new_list);
+	return new_list;
+}
+
+GList *
+purple_media_codec_list_copy(GList *codecs)
+{
+	GList *new_list = NULL;
+
+	for (; codecs; codecs = g_list_next(codecs)) {
+		new_list = g_list_prepend(new_list, g_boxed_copy(
+				PURPLE_TYPE_MEDIA_CODEC,
+				codecs->data));
+	}
+
+	new_list = g_list_reverse(new_list);
+	return new_list;
+}
+
+void
+purple_media_codec_list_free(GList *codecs)
+{
+	for (; codecs; codecs =
+			g_list_delete_link(codecs, codecs)) {
+		g_boxed_free(PURPLE_TYPE_MEDIA_CODEC,
+				codecs->data);
+	}
+}
+
+GType
+purple_media_codec_get_type()
+{
+	static GType type = 0;
+
+	if (type == 0) {
+		type = g_boxed_type_register_static("PurpleMediaCodec",
+				(GBoxedCopyFunc)purple_media_codec_copy,
+				(GBoxedFreeFunc)purple_media_codec_free);
+	}
+	return type;
+}
+
+
+
 PurpleMediaSessionType
 purple_media_get_overall_type(PurpleMedia *media)
 {
@@ -1069,8 +1459,6 @@
 	const gchar *audio_device = purple_prefs_get_string("/purple/media/audio/device");
 	double input_volume = purple_prefs_get_int("/purple/media/audio/volume/input")/10.0;
 
-	purple_debug_info("media", "purple_media_audio_init_src\n");
-
 	*sendbin = gst_bin_new("purplesendaudiobin");
 	src = gst_element_factory_make("alsasrc", "asrc");
 	volume = gst_element_factory_make("volume", "purpleaudioinputvolume");
@@ -1099,8 +1487,6 @@
 	const gchar *video_device = purple_prefs_get_string(
 			"/purple/media/video/device");
 
-	purple_debug_info("media", "purple_media_video_init_src\n");
-
 	*sendbin = gst_bin_new("purplesendvideobin");
 	src = gst_element_factory_make(video_plugin, "purplevideosource");
 	gst_bin_add(GST_BIN(*sendbin), src);
@@ -1143,8 +1529,6 @@
 	double output_volume = purple_prefs_get_int(
 			"/purple/media/audio/volume/output")/10.0;
 
-	purple_debug_info("media", "purple_media_audio_init_recv\n");
-
 	*recvbin = gst_bin_new("pidginrecvaudiobin");
 	sink = gst_element_factory_make("alsasink", "asink");
 	g_object_set(G_OBJECT(sink), "sync", FALSE, NULL);
@@ -1158,8 +1542,6 @@
 	ghost = gst_ghost_pad_new("ghostsink", pad);
 	gst_element_add_pad(*recvbin, ghost);
 	g_object_set(G_OBJECT(*recvlevel), "message", TRUE, NULL);
-
-	purple_debug_info("media", "purple_media_audio_init_recv end\n");
 }
 
 void
@@ -1168,16 +1550,12 @@
 	GstElement *sink;
 	GstPad *pad, *ghost;
 
-	purple_debug_info("media", "purple_media_video_init_recv\n");
-
 	*recvbin = gst_bin_new("pidginrecvvideobin");
 	sink = gst_element_factory_make("autovideosink", "purplevideosink");
 	gst_bin_add(GST_BIN(*recvbin), sink);
 	pad = gst_element_get_pad(sink, "sink");
 	ghost = gst_ghost_pad_new("ghostsink", pad);
 	gst_element_add_pad(*recvbin, ghost);
-
-	purple_debug_info("media", "purple_media_video_init_recv end\n");
 }
 
 static void
@@ -1187,7 +1565,7 @@
 {
 	gchar *name;
 	FsParticipant *participant;
-	FsCandidate *candidate;
+	PurpleMediaCandidate *candidate;
 	purple_debug_info("media", "got new local candidate: %s\n", local_candidate->foundation);
 	g_object_get(stream, "participant", &participant, NULL);
 	g_object_get(participant, "cname", &name, NULL);
@@ -1195,10 +1573,10 @@
 
 	purple_media_insert_local_candidate(session, name, fs_candidate_copy(local_candidate));
 
-	candidate = fs_candidate_copy(local_candidate);
+	candidate = purple_media_candidate_from_fs(local_candidate);
 	g_signal_emit(session->media, purple_media_signals[NEW_CANDIDATE],
 		      0, session->id, name, candidate);
-	fs_candidate_destroy(candidate);
+	purple_media_candidate_free(candidate);
 
 	g_free(name);
 }
@@ -1234,8 +1612,10 @@
 {
 	gchar *name;
 	FsParticipant *participant;
-	FsCandidate *local = fs_candidate_copy(native_candidate);
-	FsCandidate *remote = fs_candidate_copy(remote_candidate);
+	PurpleMediaCandidate *local =
+			purple_media_candidate_from_fs(native_candidate);
+	PurpleMediaCandidate *remote =
+			purple_media_candidate_from_fs(remote_candidate);
 	PurpleMediaStream *stream;
 
 	g_object_get(fsstream, "participant", &participant, NULL);
@@ -1251,8 +1631,18 @@
 	g_signal_emit(session->media, purple_media_signals[CANDIDATE_PAIR], 0,
 		      local, remote);
 
-	fs_candidate_destroy(local);
-	fs_candidate_destroy(remote);
+	purple_media_candidate_free(local);
+	purple_media_candidate_free(remote);
+}
+
+static gboolean
+purple_media_connected_cb(PurpleMediaStream *stream)
+{
+	g_signal_emit(stream->session->media,
+			purple_media_signals[STATE_CHANGED],
+			0, PURPLE_MEDIA_STATE_CHANGED_CONNECTED,
+			stream->session->id, stream->participant);
+	return FALSE;
 }
 
 static void
@@ -1268,13 +1658,10 @@
 	gst_bin_add(GST_BIN(purple_media_get_pipeline(stream->session->media)),
 		    stream->sink);
 	sinkpad = gst_element_get_static_pad(stream->sink, "ghostsink");
-	purple_debug_info("media", "connecting new src pad: %s\n", 
-			  gst_pad_link(srcpad, sinkpad) == GST_PAD_LINK_OK ? "success" : "failure");
+	gst_pad_link(srcpad, sinkpad);
 	gst_element_set_state(stream->sink, GST_STATE_PLAYING);
 
-	g_signal_emit(stream->session->media, purple_media_signals[STATE_CHANGED],
-				0, PURPLE_MEDIA_STATE_CHANGED_CONNECTED,
-				stream->session->id, stream->participant);
+	g_timeout_add(0, (GSourceFunc)purple_media_connected_cb, stream);
 }
 
 static gchar *
@@ -1476,9 +1863,12 @@
 GList *
 purple_media_get_codecs(PurpleMedia *media, const gchar *sess_id)
 {
+	GList *fscodecs;
 	GList *codecs;
 	g_object_get(G_OBJECT(purple_media_get_session(media, sess_id)->session),
-		     "codecs", &codecs, NULL);
+		     "codecs", &fscodecs, NULL);
+	codecs = purple_media_codec_list_from_fs(fscodecs);
+	fs_codec_list_destroy(fscodecs);
 	return codecs;
 }
 
@@ -1486,7 +1876,7 @@
 purple_media_get_local_candidates(PurpleMedia *media, const gchar *sess_id, const gchar *name)
 {
 	PurpleMediaStream *stream = purple_media_get_stream(media, sess_id, name);
-	return fs_candidate_list_copy(stream->local_candidates);
+	return purple_media_candidate_list_from_fs(stream->local_candidates);
 }
 
 void
@@ -1495,32 +1885,36 @@
 {
 	PurpleMediaStream *stream = purple_media_get_stream(media, sess_id, name);
 	stream->remote_candidates = g_list_concat(stream->remote_candidates,
-			fs_candidate_list_copy(remote_candidates));
+			purple_media_candidate_list_to_fs(remote_candidates));
 
 	if (stream->session->accepted == TRUE) {
 		purple_media_set_remote_candidates(stream);
 	}
 }
 
-FsCandidate *
+PurpleMediaCandidate *
 purple_media_get_local_candidate(PurpleMedia *media, const gchar *sess_id, const gchar *name)
 {
-	return purple_media_get_stream(media, sess_id, name)->local_candidate;
+	return purple_media_candidate_from_fs(purple_media_get_stream(
+			media, sess_id, name)->local_candidate);
 }
 
-FsCandidate *
+PurpleMediaCandidate *
 purple_media_get_remote_candidate(PurpleMedia *media, const gchar *sess_id, const gchar *name)
 {
-	return purple_media_get_stream(media, sess_id, name)->remote_candidate;
+	return purple_media_candidate_from_fs(purple_media_get_stream(
+			media, sess_id, name)->remote_candidate);
 }
 
 gboolean
 purple_media_set_remote_codecs(PurpleMedia *media, const gchar *sess_id, const gchar *name, GList *codecs)
 {
 	FsStream *stream = purple_media_get_stream(media, sess_id, name)->stream;
+	GList *fscodecs = purple_media_codec_list_to_fs(codecs);
 	GError *err = NULL;
 
-	fs_stream_set_remote_codecs(stream, codecs, &err);
+	fs_stream_set_remote_codecs(stream, fscodecs, &err);
+	fs_codec_list_destroy(fscodecs);
 
 	if (err) {
 		purple_debug_error("media", "Error setting remote codecs: %s\n",
@@ -1547,12 +1941,14 @@
 }
 
 gboolean
-purple_media_set_send_codec(PurpleMedia *media, const gchar *sess_id, FsCodec *codec)
+purple_media_set_send_codec(PurpleMedia *media, const gchar *sess_id, PurpleMediaCodec *codec)
 {
 	PurpleMediaSession *session = purple_media_get_session(media, sess_id);
+	FsCodec *fscodec = purple_media_codec_to_fs(codec);
 	GError *err = NULL;
 
-	fs_session_set_send_codec(session->session, codec, &err);
+	fs_session_set_send_codec(session->session, fscodec, &err);
+	fs_codec_destroy(fscodec);
 
 	if (err) {
 		purple_debug_error("media", "Error setting send codec\n");
--- a/libpurple/media.h	Sat Jan 10 01:32:34 2009 +0000
+++ b/libpurple/media.h	Mon Jan 12 19:27:55 2009 +0000
@@ -30,13 +30,14 @@
 #ifdef USE_VV
 
 #include <gst/gst.h>
-#include <gst/farsight/fs-stream.h>
 #include <glib.h>
 #include <glib-object.h>
 
 G_BEGIN_DECLS
 
 #define PURPLE_TYPE_MEDIA            (purple_media_get_type())
+#define PURPLE_TYPE_MEDIA_CANDIDATE  (purple_media_candidate_get_type())
+#define PURPLE_TYPE_MEDIA_CODEC      (purple_media_codec_get_type())
 #define PURPLE_MEDIA(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_MEDIA, PurpleMedia))
 #define PURPLE_MEDIA_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_MEDIA, PurpleMediaClass))
 #define PURPLE_IS_MEDIA(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_MEDIA))
@@ -51,6 +52,12 @@
 typedef struct _PurpleMediaClass PurpleMediaClass;
 /** @copydoc _PurpleMediaPrivate */
 typedef struct _PurpleMediaPrivate PurpleMediaPrivate;
+/** @copydoc _PurpleMediaCandidate */
+typedef struct _PurpleMediaCandidate PurpleMediaCandidate;
+/** @copydoc _PurpleMediaCodec */
+typedef struct _PurpleMediaCodec PurpleMediaCodec;
+/** @copydoc _PurpleMediaCodecParameter */
+typedef struct _PurpleMediaCodecParameter PurpleMediaCodecParameter;
 
 #else
 
@@ -76,6 +83,25 @@
 	PURPLE_MEDIA_STATE_CHANGED_END,
 } PurpleMediaStateChangedType;
 
+typedef enum {
+	PURPLE_MEDIA_CANDIDATE_TYPE_HOST,
+	PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX,
+	PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX,
+	PURPLE_MEDIA_CANDIDATE_TYPE_RELAY,
+	PURPLE_MEDIA_CANDIDATE_TYPE_MULTICAST,
+} PurpleMediaCandidateType;
+
+typedef enum {
+	PURPLE_MEDIA_COMPONENT_NONE = 0,
+	PURPLE_MEDIA_COMPONENT_RTP = 1,
+	PURPLE_MEDIA_COMPONENT_RTCP = 2,
+} PurpleMediaComponentType;
+
+typedef enum {
+	PURPLE_MEDIA_NETWORK_PROTOCOL_UDP,
+	PURPLE_MEDIA_NETWORK_PROTOCOL_TCP,
+} PurpleMediaNetworkProtocol;
+
 #ifdef USE_VV
 
 /** The media class */
@@ -91,6 +117,38 @@
 	PurpleMediaPrivate *priv;      /**< The private data of this object. */
 };
 
+struct _PurpleMediaCandidate
+{
+	const gchar *foundation;
+	guint component_id;
+	const gchar *ip;
+	guint16 port;
+	const gchar *base_ip;
+	guint16 base_port;
+	PurpleMediaNetworkProtocol proto;
+	guint32 priority;
+	PurpleMediaCandidateType type;
+	const gchar *username;
+	const gchar *password;
+	guint ttl;
+};
+
+struct _PurpleMediaCodecParameter
+{
+	gchar *name;
+	gchar *value;
+};
+
+struct _PurpleMediaCodec
+{
+	gint id;
+	char *encoding_name;
+	PurpleMediaSessionType media_type;
+	guint clock_rate;
+	guint channels;
+	GList *optional_params;
+};
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -110,6 +168,124 @@
 GType purple_media_state_changed_get_type(void);
 
 /**
+ * Gets the type of the media candidate structure.
+ *
+ * @return The media canditate's GType
+ */
+GType purple_media_candidate_get_type(void);
+
+/**
+ * Creates a PurpleMediaCandidate instance.
+ *
+ * @param foundation The foundation of the candidate.
+ * @param component_id The component this candidate is for.
+ * @param type The type of candidate.
+ * @param proto The protocol this component is for.
+ * @param ip The IP address of this component.
+ * @param port The network port.
+ *
+ * @return The newly created PurpleMediaCandidate instance.
+ */
+PurpleMediaCandidate *purple_media_candidate_new(
+		const gchar *foundation, guint component_id,
+		PurpleMediaCandidateType type,
+		PurpleMediaNetworkProtocol proto,
+		const gchar *ip, guint port);
+
+/**
+ * Copies a GList of PurpleMediaCandidate and its contents.
+ *
+ * @param candidates The list of candidates to be copied.
+ *
+ * @return The copy of the GList.
+ */
+GList *purple_media_candidate_list_copy(GList *candidates);
+
+/**
+ * Frees a GList of PurpleMediaCandidate and its contents.
+ *
+ * @param candidates The list of candidates to be freed.
+ */
+void purple_media_candidate_list_free(GList *candidates);
+
+/**
+ * Gets the type of the media codec structure.
+ *
+ * @return The media codec's GType
+ */
+GType purple_media_codec_get_type(void);
+
+/**
+ * Creates a new PurpleMediaCodec instance.
+ *
+ * @param id Codec identifier.
+ * @param encoding_name Name of the media type this encodes.
+ * @param media_type PurpleMediaSessionType of this codec.
+ * @param clock_rate The clock rate this codec encodes at, if applicable.
+ *
+ * @return The newly created PurpleMediaCodec.
+ */
+PurpleMediaCodec *purple_media_codec_new(int id, const char *encoding_name,
+		PurpleMediaSessionType media_type, guint clock_rate);
+
+/**
+ * Creates a string representation of the codec.
+ *
+ * @param codec The codec to create the string of.
+ *
+ * @return The new string representation.
+ */
+gchar *purple_media_codec_to_string(const PurpleMediaCodec *codec);
+
+/**
+ * Adds an optional parameter to the codec.
+ *
+ * @param codec The codec to add the parameter to.
+ * @param name The name of the parameter to add.
+ * @param value The value of the parameter to add.
+ */
+void purple_media_codec_add_optional_parameter(PurpleMediaCodec *codec,
+		const gchar *name, const gchar *value);
+
+/**
+ * Removes an optional parameter from the codec.
+ *
+ * @param codec The codec to remove the parameter from.
+ * @param param A pointer to the parameter to remove.
+ */
+void purple_media_codec_remove_optional_parameter(PurpleMediaCodec *codec,
+		PurpleMediaCodecParameter *param);
+
+/**
+ * Gets an optional parameter based on the values given.
+ *
+ * @param codec The codec to find the parameter in.
+ * @param name The name of the parameter to search for.
+ * @param value The value to search for or NULL.
+ *
+ * @return The value found or NULL.
+ */
+PurpleMediaCodecParameter *purple_media_codec_get_optional_parameter(
+		PurpleMediaCodec *codec, const gchar *name,
+		const gchar *value);
+
+/**
+ * Copies a GList of PurpleMediaCodec and its contents.
+ *
+ * @param codecs The list of codecs to be copied.
+ *
+ * @return The copy of the GList.
+ */
+GList *purple_media_codec_list_copy(GList *codecs);
+
+/**
+ * Frees a GList of PurpleMediaCodec and its contents.
+ *
+ * @param codecs The list of codecs to be freed.
+ */
+void purple_media_codec_list_free(GList *codecs);
+
+/**
  * Combines all the separate session types into a single PurpleMediaSessionType.
  *
  * @param media The media session to retrieve session types from.
@@ -388,7 +564,8 @@
  *
  * @return The active candidate retrieved.
  */
-FsCandidate *purple_media_get_local_candidate(PurpleMedia *media, const gchar *sess_id, const gchar *name);
+PurpleMediaCandidate *purple_media_get_local_candidate(PurpleMedia *media,
+		const gchar *sess_id, const gchar *name);
 
 /**
  * Gets the active remote candidate for the stream.
@@ -399,7 +576,8 @@
  *
  * @return The remote candidate retrieved.
  */
-FsCandidate *purple_media_get_remote_candidate(PurpleMedia *media, const gchar *sess_id, const gchar *name);
+PurpleMediaCandidate *purple_media_get_remote_candidate(PurpleMedia *media,
+		const gchar *sess_id, const gchar *name);
 
 /**
  * Gets remote candidates from the stream.
@@ -432,7 +610,7 @@
  *
  * @return @c TRUE The codec was successfully changed, or @c FALSE otherwise.
  */
-gboolean purple_media_set_send_codec(PurpleMedia *media, const gchar *sess_id, FsCodec *codec);
+gboolean purple_media_set_send_codec(PurpleMedia *media, const gchar *sess_id, PurpleMediaCodec *codec);
 
 /**
  * Gets whether a session's codecs are ready to be used.
--- a/libpurple/protocols/jabber/google.c	Sat Jan 10 01:32:34 2009 +0000
+++ b/libpurple/protocols/jabber/google.c	Mon Jan 12 19:27:55 2009 +0000
@@ -111,7 +111,7 @@
 	xmlnode_set_namespace(desc, "http://www.google.com/session/phone");
 
 	for (;codecs; codecs = codecs->next) {
-		FsCodec *codec = (FsCodec*)codecs->data;
+		PurpleMediaCodec *codec = (PurpleMediaCodec*)codecs->data;
 		char id[8], clockrate[10];
 		payload = xmlnode_new_child(desc, "payload-type");
 		g_snprintf(id, sizeof(id), "%d", codec->id);
@@ -121,7 +121,7 @@
 		xmlnode_set_attrib(payload, "clockrate", clockrate);
 	}
 
-	fs_codec_list_destroy(codecs);
+	purple_media_codec_list_free(codecs);
 	jabber_iq_send(iq);
 }
 
@@ -161,7 +161,7 @@
 	JabberIq *iq = jabber_iq_new(session->js, JABBER_IQ_SET);
 	GList *candidates = purple_media_get_local_candidates(session->media, "google-voice",
 							      session->remote_jid);
-	FsCandidate *transport;
+	PurpleMediaCandidate *transport;
 	xmlnode *sess;
 	xmlnode *candidate;
 	sess = google_session_create_xmlnode(session, "candidates");
@@ -171,7 +171,7 @@
 	for (;candidates;candidates = candidates->next) {
 		char port[8];
 		char pref[8];
-		transport = (FsCandidate*)(candidates->data);
+		transport = (PurpleMediaCandidate*)(candidates->data);
 
 		if (!strcmp(transport->ip, "127.0.0.1"))
 			continue;
@@ -194,10 +194,14 @@
 				transport->password != NULL ?
 				transport->password : "");
 		xmlnode_set_attrib(candidate, "preference", pref);
-		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_SRFLX ? "stun" :
-					       	      transport->type == FS_CANDIDATE_TYPE_RELAY ? "relay" : NULL);
+		xmlnode_set_attrib(candidate, "protocol", transport->proto ==
+				PURPLE_MEDIA_NETWORK_PROTOCOL_UDP ? "udp" : "tcp");
+		xmlnode_set_attrib(candidate, "type", transport->type ==
+				PURPLE_MEDIA_CANDIDATE_TYPE_HOST ? "local" :
+						      transport->type ==
+				PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX ? "stun" :
+					       	      transport->type ==
+				PURPLE_MEDIA_CANDIDATE_TYPE_RELAY ? "relay" : NULL);
 		xmlnode_set_attrib(candidate, "generation", "0");
 		xmlnode_set_attrib(candidate, "network", "0");
 		xmlnode_insert_child(sess, candidate);
@@ -230,7 +234,7 @@
 			codecs = purple_media_get_codecs(media, "google-voice");
 	
 			for (iter = codecs; iter; iter = g_list_next(iter)) {
-				FsCodec *codec = (FsCodec*)iter->data;
+				PurpleMediaCodec *codec = (PurpleMediaCodec*)iter->data;
 				gchar *id = g_strdup_printf("%d", codec->id);
 				gchar *clock_rate = g_strdup_printf("%d", codec->clock_rate);
 				payload = xmlnode_new_child(desc, "payload-type");
@@ -240,7 +244,7 @@
 				g_free(clock_rate);
 				g_free(id);
 			}
-			fs_codec_list_destroy(codecs);
+			purple_media_codec_list_free(codecs);
 
 			jabber_iq_send(iq);
 	
@@ -329,7 +333,7 @@
 	JabberIq *result;
 	GList *codecs = NULL;
 	xmlnode *desc_element, *codec_element;
-	FsCodec *codec;
+	PurpleMediaCodec *codec;
 	const char *id, *encoding_name,  *clock_rate;
 	GParameter param;
 		
@@ -364,7 +368,7 @@
 		id = xmlnode_get_attrib(codec_element, "id");
 		clock_rate = xmlnode_get_attrib(codec_element, "clockrate");
 
-		codec = fs_codec_new(atoi(id), encoding_name, FS_MEDIA_TYPE_AUDIO,
+		codec = purple_media_codec_new(atoi(id), encoding_name, PURPLE_MEDIA_AUDIO,
 				     clock_rate ? atoi(clock_rate) : 0);
 		codecs = g_list_append(codecs, codec);
 	}
@@ -381,7 +385,7 @@
 			 G_CALLBACK(google_session_candidates_prepared), session);
 	purple_media_ready(session->media);
 
-	fs_codec_list_destroy(codecs);
+	purple_media_codec_list_free(codecs);
 	
 	result = jabber_iq_new(js, JABBER_IQ_RESULT);
 	jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id"));
@@ -399,17 +403,21 @@
 	char n[4];	
 		
 	for (cand = xmlnode_get_child(sess, "candidate"); cand; cand = xmlnode_get_next_twin(cand)) {
-		FsCandidate *info;
+		PurpleMediaCandidate *info;
 		g_snprintf(n, sizeof(n), "S%d", name++);
-		info = fs_candidate_new(n, FS_COMPONENT_RTP, !strcmp(xmlnode_get_attrib(cand, "type"), "local") ?
-					FS_CANDIDATE_TYPE_HOST :
+		info = purple_media_candidate_new(n, PURPLE_MEDIA_COMPONENT_RTP,
+				!strcmp(xmlnode_get_attrib(cand, "type"), "local") ?
+					PURPLE_MEDIA_CANDIDATE_TYPE_HOST :
 			     		!strcmp(xmlnode_get_attrib(cand, "type"), "stun") ?
-						FS_CANDIDATE_TYPE_PRFLX :
+						PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX :
 			     			!strcmp(xmlnode_get_attrib(cand, "type"), "relay") ?
-							FS_CANDIDATE_TYPE_RELAY : FS_CANDIDATE_TYPE_HOST,
+							PURPLE_MEDIA_CANDIDATE_TYPE_RELAY :
+							PURPLE_MEDIA_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")));
+							PURPLE_MEDIA_NETWORK_PROTOCOL_UDP :
+							PURPLE_MEDIA_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"));
@@ -418,7 +426,7 @@
 	}
 
 	purple_media_add_remote_candidates(session->media, "google-voice", session->remote_jid, list);
-	fs_candidate_list_destroy(list);
+	purple_media_candidate_list_free(list);
 
 	result = jabber_iq_new(js, JABBER_IQ_RESULT);
 	jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id"));
@@ -441,9 +449,9 @@
 		const gchar *clock_rate =
 				xmlnode_get_attrib(codec_element, "clockrate");
 
-		FsCodec *codec = fs_codec_new(atoi(id), encoding_name,
-				FS_MEDIA_TYPE_AUDIO, clock_rate ?
-				atoi(clock_rate) : 0);
+		PurpleMediaCodec *codec = purple_media_codec_new(atoi(id),
+				encoding_name, PURPLE_MEDIA_AUDIO,
+				clock_rate ? atoi(clock_rate) : 0);
 		codecs = g_list_append(codecs, codec);
 	}
 
--- a/libpurple/protocols/jabber/jingle/rtp.c	Sat Jan 10 01:32:34 2009 +0000
+++ b/libpurple/protocols/jabber/jingle/rtp.c	Mon Jan 12 19:27:55 2009 +0000
@@ -193,7 +193,7 @@
 		JingleTransport *transport = jingle_transport_create(JINGLE_TRANSPORT_RAWUDP);
 		JingleRawUdpCandidate *rawudp_candidate;
 		for (; candidates; candidates = g_list_next(candidates)) {
-			FsCandidate *candidate = candidates->data;
+			PurpleMediaCandidate *candidate = candidates->data;
 			id = jabber_get_next_id(jingle_session_get_js(session));
 			rawudp_candidate = jingle_rawudp_candidate_new(id,
 					generation, candidate->component_id,
@@ -206,14 +206,14 @@
 		JingleTransport *transport = jingle_transport_create(JINGLE_TRANSPORT_ICEUDP);
 		JingleIceUdpCandidate *iceudp_candidate;
 		for (; candidates; candidates = g_list_next(candidates)) {
-			FsCandidate *candidate = candidates->data;
+			PurpleMediaCandidate *candidate = candidates->data;
 			iceudp_candidate = jingle_iceudp_candidate_new(candidate->component_id,
 					candidate->foundation, generation, candidate->ip,
 					0, candidate->port, candidate->priority, "udp",
-					candidate->type == FS_CANDIDATE_TYPE_HOST ? "host" :
-					candidate->type == FS_CANDIDATE_TYPE_SRFLX ? "srflx" :
-					candidate->type == FS_CANDIDATE_TYPE_PRFLX ? "prflx" :
-					candidate->type == FS_CANDIDATE_TYPE_RELAY ? "relay" : "",
+					candidate->type == PURPLE_MEDIA_CANDIDATE_TYPE_HOST ? "host" :
+					candidate->type == PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX ? "srflx" :
+					candidate->type == PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX ? "prflx" :
+					candidate->type == PURPLE_MEDIA_CANDIDATE_TYPE_RELAY ? "relay" : "",
 					candidate->username, candidate->password);
 			jingle_iceudp_add_local_candidate(JINGLE_ICEUDP(transport), iceudp_candidate);
 		}
@@ -233,8 +233,10 @@
 
 		for (; candidates; candidates = g_list_delete_link(candidates, candidates)) {
 			JingleRawUdpCandidate *candidate = candidates->data;
-			ret = g_list_append(ret, fs_candidate_new("", candidate->component,
-					FS_CANDIDATE_TYPE_SRFLX, FS_NETWORK_PROTOCOL_UDP,
+			ret = g_list_append(ret, purple_media_candidate_new(
+					"", candidate->component,
+					PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX,
+					PURPLE_MEDIA_NETWORK_PROTOCOL_UDP,
 					candidate->ip, candidate->port));
 		}
 
@@ -244,17 +246,22 @@
 
 		for (; candidates; candidates = g_list_delete_link(candidates, candidates)) {
 			JingleIceUdpCandidate *candidate = candidates->data;
-			FsCandidate *fscandidate = fs_candidate_new(
+			PurpleMediaCandidate *new_candidate = purple_media_candidate_new(
 					candidate->foundation, candidate->component,
-					!strcmp(candidate->type, "host") ? FS_CANDIDATE_TYPE_HOST :
-					!strcmp(candidate->type, "srflx") ? FS_CANDIDATE_TYPE_SRFLX :
-					!strcmp(candidate->type, "prflx") ? FS_CANDIDATE_TYPE_PRFLX :
-					!strcmp(candidate->type, "relay") ? FS_CANDIDATE_TYPE_RELAY : 0,
-					FS_NETWORK_PROTOCOL_UDP, candidate->ip, candidate->port);
-			fscandidate->username = g_strdup(candidate->username);
-			fscandidate->password = g_strdup(candidate->password);
-			fscandidate->priority = candidate->priority;
-			ret = g_list_append(ret, fscandidate);
+					!strcmp(candidate->type, "host") ?
+					PURPLE_MEDIA_CANDIDATE_TYPE_HOST :
+					!strcmp(candidate->type, "srflx") ?
+					PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX :
+					!strcmp(candidate->type, "prflx") ?
+					PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX :
+					!strcmp(candidate->type, "relay") ?
+					PURPLE_MEDIA_CANDIDATE_TYPE_RELAY : 0,
+					PURPLE_MEDIA_NETWORK_PROTOCOL_UDP,
+					candidate->ip, candidate->port);
+			new_candidate->username = g_strdup(candidate->username);
+			new_candidate->password = g_strdup(candidate->password);
+			new_candidate->priority = candidate->priority;
+			ret = g_list_append(ret, new_candidate);
 		}
 
 		return ret;
@@ -264,7 +271,7 @@
 }
 
 static void
-jingle_rtp_new_candidate_cb(PurpleMedia *media, gchar *sid, gchar *name, FsCandidate *candidate, JingleSession *session)
+jingle_rtp_new_candidate_cb(PurpleMedia *media, gchar *sid, gchar *name, PurpleMediaCandidate *candidate, JingleSession *session)
 {
 	purple_debug_info("jingle-rtp", "jingle_rtp_new_candidate_cb\n");
 }
@@ -427,10 +434,11 @@
 	GList *codecs = NULL;
 	xmlnode *codec_element = NULL;
 	const char *encoding_name,*id, *clock_rate;
-	FsCodec *codec;
+	PurpleMediaCodec *codec;
 	const gchar *media = xmlnode_get_attrib(description, "media");
-	FsMediaType type = !strcmp(media, "video") ? FS_MEDIA_TYPE_VIDEO :
-			!strcmp(media, "audio") ? FS_MEDIA_TYPE_AUDIO : 0;
+	PurpleMediaSessionType type =
+			!strcmp(media, "video") ? PURPLE_MEDIA_VIDEO :
+			!strcmp(media, "audio") ? PURPLE_MEDIA_AUDIO : 0;
 
 	for (codec_element = xmlnode_get_child(description, "payload-type") ;
 		 codec_element ;
@@ -442,18 +450,18 @@
 		id = xmlnode_get_attrib(codec_element, "id");
 		clock_rate = xmlnode_get_attrib(codec_element, "clockrate");
 
-		codec = fs_codec_new(atoi(id), encoding_name, 
+		codec = purple_media_codec_new(atoi(id), encoding_name, 
 				     type, 
 				     clock_rate ? atoi(clock_rate) : 0);
 
 		for (param = xmlnode_get_child(codec_element, "parameter");
 				param; param = xmlnode_get_next_twin(param)) {
-			fs_codec_add_optional_parameter(codec,
+			purple_media_codec_add_optional_parameter(codec,
 					xmlnode_get_attrib(param, "name"),
 					xmlnode_get_attrib(param, "value"));
 		}
 
-		codec_str = fs_codec_to_string(codec);
+		codec_str = purple_media_codec_to_string(codec);
 		purple_debug_info("jingle-rtp", "received codec: %s\n", codec_str);
 		g_free(codec_str);
 
@@ -477,7 +485,7 @@
 jingle_rtp_add_payloads(xmlnode *description, GList *codecs)
 {
 	for (; codecs ; codecs = codecs->next) {
-		FsCodec *codec = (FsCodec*)codecs->data;
+		PurpleMediaCodec *codec = (PurpleMediaCodec*)codecs->data;
 		GList *iter = codec->optional_params;
 		char id[8], clockrate[10], channels[10];
 		gchar *codec_str;
@@ -493,13 +501,13 @@
 		xmlnode_set_attrib(payload, "channels", channels);
 
 		for (; iter; iter = g_list_next(iter)) {
-			FsCodecParameter *fsparam = iter->data;
+			PurpleMediaCodecParameter *mparam = iter->data;
 			xmlnode *param = xmlnode_new_child(payload, "parameter");
-			xmlnode_set_attrib(param, "name", fsparam->name);
-			xmlnode_set_attrib(param, "value", fsparam->value);
+			xmlnode_set_attrib(param, "name", mparam->name);
+			xmlnode_set_attrib(param, "value", mparam->value);
 		}
 
-		codec_str = fs_codec_to_string(codec);
+		codec_str = purple_media_codec_to_string(codec);
 		purple_debug_info("jingle", "adding codec: %s\n", codec_str);
 		g_free(codec_str);
 	}
--- a/pidgin/gtkmedia.c	Sat Jan 10 01:32:34 2009 +0000
+++ b/pidgin/gtkmedia.c	Mon Jan 12 19:27:55 2009 +0000
@@ -337,7 +337,6 @@
 		return TRUE;
 
 	name = gst_object_get_name(GST_MESSAGE_SRC (message));
-	purple_debug_info("gtkmedia", "prepare-xwindow-id object name: %s\n", name);
 
 	/* The XOverlay's name is the sink's name with a suffix */
 	if (!strncmp(name, "purplevideosink", strlen("purplevideosink")))
@@ -350,10 +349,18 @@
 	return TRUE;
 }
 
+static gboolean
+realize_cb_cb(GstElement *element)
+{
+	gst_element_set_locked_state(element, FALSE);
+	gst_element_set_state(element, GST_STATE_PLAYING);
+	return FALSE;
+}
+
 static void
 realize_cb(GtkWidget *widget, GstElement *element)
 {
-	gst_element_set_state(element, GST_STATE_PLAYING);
+	g_timeout_add(0, (GSourceFunc)realize_cb_cb, element);
 }
 
 static void
@@ -408,6 +415,7 @@
 		} else if (type & PURPLE_MEDIA_VIDEO) {
 			if (!videosendbin && (type & PURPLE_MEDIA_SEND_VIDEO)) {
 				purple_media_video_init_src(&videosendbin);
+				gst_element_set_locked_state(videosendbin, TRUE);
 				purple_media_set_src(media, sessions->data, videosendbin);
 			}
 			if (!videorecvbool && (type & PURPLE_MEDIA_RECV_VIDEO)) {