changeset 29746:419415a431e1

Move libpurple/media/media.c to libpurple/media.c. This failed to build before on both OS X and Debian Sid, so for lack of a better solution, here we are.
author Paul Aurich <paul@darkrain42.org>
date Sat, 06 Feb 2010 06:40:49 +0000
parents 020f46d39cf7
children 3f84b7069366
files libpurple/Makefile.am libpurple/media.c libpurple/media/media.c
diffstat 3 files changed, 1408 insertions(+), 1408 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/Makefile.am	Sat Feb 06 06:32:21 2010 +0000
+++ b/libpurple/Makefile.am	Sat Feb 06 06:40:49 2010 +0000
@@ -58,7 +58,7 @@
 	media/candidate.c \
 	media/codec.c \
 	media/enum-types.c \
-	media/media.c \
+	media.c \
 	mediamanager.c \
 	mime.c \
 	nat-pmp.c \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/media.c	Sat Feb 06 06:40:49 2010 +0000
@@ -0,0 +1,1407 @@
+/**
+ * @file media.c Media API
+ * @ingroup core
+ */
+
+/* purple
+ *
+ * Purple is the legal property of its developers, whose names are too numerous
+ * to list here.  Please refer to the COPYRIGHT file distributed with this
+ * source distribution.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
+ */
+#include "internal.h"
+
+#include "account.h"
+#include "media.h"
+#include "media/backend-iface.h"
+#include "mediamanager.h"
+
+#include "debug.h"
+
+#ifdef USE_GSTREAMER
+#include "media/backend-fs2.h"
+#include "marshallers.h"
+#include "media-gst.h"
+#endif
+
+#ifdef USE_VV
+
+/** @copydoc _PurpleMediaSession */
+typedef struct _PurpleMediaSession PurpleMediaSession;
+/** @copydoc _PurpleMediaStream */
+typedef struct _PurpleMediaStream PurpleMediaStream;
+/** @copydoc _PurpleMediaClass */
+typedef struct _PurpleMediaClass PurpleMediaClass;
+/** @copydoc _PurpleMediaPrivate */
+typedef struct _PurpleMediaPrivate PurpleMediaPrivate;
+
+/** The media class */
+struct _PurpleMediaClass
+{
+	GObjectClass parent_class;     /**< The parent class. */
+};
+
+/** The media class's private data */
+struct _PurpleMedia
+{
+	GObject parent;                /**< The parent of this object. */
+	PurpleMediaPrivate *priv;      /**< The private data of this object. */
+};
+
+struct _PurpleMediaSession
+{
+	gchar *id;
+	PurpleMedia *media;
+	PurpleMediaSessionType type;
+	gboolean initiator;
+};
+
+struct _PurpleMediaStream
+{
+	PurpleMediaSession *session;
+	gchar *participant;
+
+	GList *local_candidates;
+	GList *remote_candidates;
+
+	gboolean initiator;
+	gboolean accepted;
+	gboolean candidates_prepared;
+
+	GList *active_local_candidates;
+	GList *active_remote_candidates;
+};
+#endif
+
+struct _PurpleMediaPrivate
+{
+#ifdef USE_VV
+	PurpleMediaManager *manager;
+	PurpleAccount *account;
+	PurpleMediaBackend *backend;
+	gchar *conference_type;
+	gboolean initiator;
+	gpointer prpl_data;
+
+	GHashTable *sessions;	/* PurpleMediaSession table */
+	GList *participants;
+	GList *streams;		/* PurpleMediaStream table */
+#else
+	gpointer dummy;
+#endif
+};
+
+#ifdef USE_VV
+#define PURPLE_MEDIA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA, PurpleMediaPrivate))
+
+static void purple_media_class_init (PurpleMediaClass *klass);
+static void purple_media_init (PurpleMedia *media);
+static void purple_media_dispose (GObject *object);
+static void purple_media_finalize (GObject *object);
+static void purple_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
+static void purple_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
+
+static void purple_media_new_local_candidate_cb(PurpleMediaBackend *backend,
+		const gchar *sess_id, const gchar *participant,
+		PurpleMediaCandidate *candidate, PurpleMedia *media);
+static void purple_media_candidates_prepared_cb(PurpleMediaBackend *backend,
+		const gchar *sess_id, const gchar *name, PurpleMedia *media);
+static void purple_media_candidate_pair_established_cb(
+		PurpleMediaBackend *backend,
+		const gchar *sess_id, const gchar *name,
+		PurpleMediaCandidate *local_candidate,
+		PurpleMediaCandidate *remote_candidate,
+		PurpleMedia *media);
+static void purple_media_codecs_changed_cb(PurpleMediaBackend *backend,
+		const gchar *sess_id, PurpleMedia *media);
+
+static GObjectClass *parent_class = NULL;
+
+
+
+enum {
+	S_ERROR,
+	CANDIDATES_PREPARED,
+	CODECS_CHANGED,
+	LEVEL,
+	NEW_CANDIDATE,
+	STATE_CHANGED,
+	STREAM_INFO,
+	LAST_SIGNAL
+};
+static guint purple_media_signals[LAST_SIGNAL] = {0};
+
+enum {
+	PROP_0,
+	PROP_MANAGER,
+	PROP_BACKEND,
+	PROP_ACCOUNT,
+	PROP_CONFERENCE_TYPE,
+	PROP_INITIATOR,
+	PROP_PRPL_DATA,
+};
+#endif
+
+
+GType
+purple_media_get_type()
+{
+#ifdef USE_VV
+	static GType type = 0;
+
+	if (type == 0) {
+		static const GTypeInfo info = {
+			sizeof(PurpleMediaClass),
+			NULL,
+			NULL,
+			(GClassInitFunc) purple_media_class_init,
+			NULL,
+			NULL,
+			sizeof(PurpleMedia),
+			0,
+			(GInstanceInitFunc) purple_media_init,
+			NULL
+		};
+		type = g_type_register_static(G_TYPE_OBJECT, "PurpleMedia", &info, 0);
+	}
+	return type;
+#else
+	return G_TYPE_NONE;
+#endif
+}
+
+#ifdef USE_VV
+static void
+purple_media_class_init (PurpleMediaClass *klass)
+{
+	GObjectClass *gobject_class = (GObjectClass*)klass;
+	parent_class = g_type_class_peek_parent(klass);
+	
+	gobject_class->dispose = purple_media_dispose;
+	gobject_class->finalize = purple_media_finalize;
+	gobject_class->set_property = purple_media_set_property;
+	gobject_class->get_property = purple_media_get_property;
+
+	g_object_class_install_property(gobject_class, PROP_MANAGER,
+			g_param_spec_object("manager",
+			"Purple Media Manager",
+			"The media manager that contains this media session.",
+			PURPLE_TYPE_MEDIA_MANAGER,
+			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+
+	/*
+	 * This one should be PURPLE_TYPE_MEDIA_BACKEND, but it doesn't
+	 * like interfaces because they "aren't GObjects"
+	 */
+	g_object_class_install_property(gobject_class, PROP_BACKEND,
+			g_param_spec_object("backend",
+			"Purple Media Backend",
+			"The backend object this media object uses.",
+			G_TYPE_OBJECT,
+			G_PARAM_READABLE));
+
+	g_object_class_install_property(gobject_class, PROP_ACCOUNT,
+			g_param_spec_pointer("account",
+			"PurpleAccount",
+			"The account this media session is on.",
+			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+
+	g_object_class_install_property(gobject_class, PROP_CONFERENCE_TYPE,
+			g_param_spec_string("conference-type",
+			"Conference Type",
+			"The type of conference that this media object "
+			"has been created to provide.",
+			NULL,
+			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+
+	g_object_class_install_property(gobject_class, PROP_INITIATOR,
+			g_param_spec_boolean("initiator",
+			"initiator",
+			"If the local user initiated the conference.",
+			FALSE,
+			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+
+	g_object_class_install_property(gobject_class, PROP_PRPL_DATA,
+			g_param_spec_pointer("prpl-data",
+			"gpointer",
+			"Data the prpl plugin set on the media session.",
+			G_PARAM_READWRITE));
+
+	purple_media_signals[S_ERROR] = g_signal_new("error", G_TYPE_FROM_CLASS(klass),
+					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+					 g_cclosure_marshal_VOID__STRING,
+					 G_TYPE_NONE, 1, G_TYPE_STRING);
+	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,
+					 G_TYPE_NONE, 2, G_TYPE_STRING,
+					 G_TYPE_STRING);
+	purple_media_signals[CODECS_CHANGED] = g_signal_new("codecs-changed", G_TYPE_FROM_CLASS(klass),
+					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+					 g_cclosure_marshal_VOID__STRING,
+					 G_TYPE_NONE, 1, G_TYPE_STRING);
+	purple_media_signals[LEVEL] = g_signal_new("level", G_TYPE_FROM_CLASS(klass),
+					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+					 purple_smarshal_VOID__STRING_STRING_DOUBLE,
+					 G_TYPE_NONE, 3, G_TYPE_STRING,
+					 G_TYPE_STRING, G_TYPE_DOUBLE);
+	purple_media_signals[NEW_CANDIDATE] = g_signal_new("new-candidate", G_TYPE_FROM_CLASS(klass),
+					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+					 purple_smarshal_VOID__POINTER_POINTER_OBJECT,
+					 G_TYPE_NONE, 3, G_TYPE_POINTER,
+					 G_TYPE_POINTER, PURPLE_TYPE_MEDIA_CANDIDATE);
+	purple_media_signals[STATE_CHANGED] = g_signal_new("state-changed", G_TYPE_FROM_CLASS(klass),
+					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+					 purple_smarshal_VOID__ENUM_STRING_STRING,
+					 G_TYPE_NONE, 3, PURPLE_MEDIA_TYPE_STATE,
+					 G_TYPE_STRING, G_TYPE_STRING);
+	purple_media_signals[STREAM_INFO] = g_signal_new("stream-info", G_TYPE_FROM_CLASS(klass),
+					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+					 purple_smarshal_VOID__ENUM_STRING_STRING_BOOLEAN,
+					 G_TYPE_NONE, 4, PURPLE_MEDIA_TYPE_INFO_TYPE,
+					 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
+	g_type_class_add_private(klass, sizeof(PurpleMediaPrivate));
+}
+
+
+static void
+purple_media_init (PurpleMedia *media)
+{
+	media->priv = PURPLE_MEDIA_GET_PRIVATE(media);
+	memset(media->priv, 0, sizeof(*media->priv));
+}
+
+static void
+purple_media_stream_free(PurpleMediaStream *stream)
+{
+	if (stream == NULL)
+		return;
+
+	g_free(stream->participant);
+
+	if (stream->local_candidates)
+		purple_media_candidate_list_free(stream->local_candidates);
+	if (stream->remote_candidates)
+		purple_media_candidate_list_free(stream->remote_candidates);
+
+	if (stream->active_local_candidates)
+		purple_media_candidate_list_free(
+				stream->active_local_candidates);
+	if (stream->active_remote_candidates)
+		purple_media_candidate_list_free(
+				stream->active_remote_candidates);
+
+	g_free(stream);
+}
+
+static void
+purple_media_session_free(PurpleMediaSession *session)
+{
+	if (session == NULL)
+		return;
+
+	g_free(session->id);
+	g_free(session);
+}
+
+static void
+purple_media_dispose(GObject *media)
+{
+	PurpleMediaPrivate *priv = PURPLE_MEDIA_GET_PRIVATE(media);
+
+	purple_debug_info("media","purple_media_dispose\n");
+
+	purple_media_manager_remove_media(priv->manager, PURPLE_MEDIA(media));
+
+	if (priv->backend) {
+		g_object_unref(priv->backend);
+		priv->backend = NULL;
+	}
+
+	if (priv->manager) {
+		g_object_unref(priv->manager);
+		priv->manager = NULL;
+	}
+
+	G_OBJECT_CLASS(parent_class)->dispose(media);
+}
+
+static void
+purple_media_finalize(GObject *media)
+{
+	PurpleMediaPrivate *priv = PURPLE_MEDIA_GET_PRIVATE(media);
+	purple_debug_info("media","purple_media_finalize\n");
+
+	for (; priv->streams; priv->streams = g_list_delete_link(priv->streams, priv->streams))
+		purple_media_stream_free(priv->streams->data);
+
+	for (; priv->participants; priv->participants = g_list_delete_link(
+			priv->participants, priv->participants))
+		g_free(priv->participants->data);
+
+	if (priv->sessions) {
+		GList *sessions = g_hash_table_get_values(priv->sessions);
+		for (; sessions; sessions = g_list_delete_link(sessions, sessions)) {
+			purple_media_session_free(sessions->data);
+		}
+		g_hash_table_destroy(priv->sessions);
+	}
+
+	G_OBJECT_CLASS(parent_class)->finalize(media);
+}
+
+static void
+purple_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+	PurpleMedia *media;
+	g_return_if_fail(PURPLE_IS_MEDIA(object));
+
+	media = PURPLE_MEDIA(object);
+
+	switch (prop_id) {
+		case PROP_MANAGER:
+			media->priv->manager = g_value_dup_object(value);
+			break;
+		case PROP_ACCOUNT:
+			media->priv->account = g_value_get_pointer(value);
+			break;
+		case PROP_CONFERENCE_TYPE:
+			media->priv->conference_type =
+					g_value_dup_string(value);
+			media->priv->backend = g_object_new(
+					purple_media_manager_get_backend_type(
+					purple_media_manager_get()),
+					"conference-type",
+					media->priv->conference_type,
+					"media", media,
+					NULL);
+			g_signal_connect(media->priv->backend,
+					"active-candidate-pair",
+					G_CALLBACK(
+					purple_media_candidate_pair_established_cb),
+					media);
+			g_signal_connect(media->priv->backend,
+					"candidates-prepared",
+					G_CALLBACK(
+					purple_media_candidates_prepared_cb),
+					media);
+			g_signal_connect(media->priv->backend,
+					"codecs-changed",
+					G_CALLBACK(
+					purple_media_codecs_changed_cb),
+					media);
+			g_signal_connect(media->priv->backend,
+					"new-candidate",
+					G_CALLBACK(
+					purple_media_new_local_candidate_cb),
+					media);
+			break;
+		case PROP_INITIATOR:
+			media->priv->initiator = g_value_get_boolean(value);
+			break;
+		case PROP_PRPL_DATA:
+			media->priv->prpl_data = g_value_get_pointer(value);
+			break;
+		default:	
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+			break;
+	}
+}
+
+static void
+purple_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+	PurpleMedia *media;
+	g_return_if_fail(PURPLE_IS_MEDIA(object));
+	
+	media = PURPLE_MEDIA(object);
+
+	switch (prop_id) {
+		case PROP_MANAGER:
+			g_value_set_object(value, media->priv->manager);
+			break;
+		case PROP_BACKEND:
+			g_value_set_object(value, media->priv->backend);
+			break;
+		case PROP_ACCOUNT:
+			g_value_set_pointer(value, media->priv->account);
+			break;
+		case PROP_CONFERENCE_TYPE:
+			g_value_set_string(value,
+					media->priv->conference_type);
+			break;
+		case PROP_INITIATOR:
+			g_value_set_boolean(value, media->priv->initiator);
+			break;
+		case PROP_PRPL_DATA:
+			g_value_set_pointer(value, media->priv->prpl_data);
+			break;
+		default:	
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);	
+			break;
+	}
+
+}
+
+static PurpleMediaSession*
+purple_media_get_session(PurpleMedia *media, const gchar *sess_id)
+{
+	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
+	return (PurpleMediaSession*) (media->priv->sessions) ?
+			g_hash_table_lookup(media->priv->sessions, sess_id) : NULL;
+}
+
+static PurpleMediaStream*
+purple_media_get_stream(PurpleMedia *media, const gchar *session, const gchar *participant)
+{
+	GList *streams;
+
+	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
+
+	streams = media->priv->streams;
+
+	for (; streams; streams = g_list_next(streams)) {
+		PurpleMediaStream *stream = streams->data;
+		if (!strcmp(stream->session->id, session) &&
+				!strcmp(stream->participant, participant))
+			return stream;
+	}
+
+	return NULL;
+}
+
+static GList *
+purple_media_get_streams(PurpleMedia *media, const gchar *session,
+		const gchar *participant)
+{
+	GList *streams;
+	GList *ret = NULL;
+	
+	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
+
+	streams = media->priv->streams;
+
+	for (; streams; streams = g_list_next(streams)) {
+		PurpleMediaStream *stream = streams->data;
+		if ((session == NULL ||
+				!strcmp(stream->session->id, session)) &&
+				(participant == NULL ||
+				!strcmp(stream->participant, participant)))
+			ret = g_list_append(ret, stream);
+	}
+
+	return ret;
+}
+
+static void
+purple_media_add_session(PurpleMedia *media, PurpleMediaSession *session)
+{
+	g_return_if_fail(PURPLE_IS_MEDIA(media));
+	g_return_if_fail(session != NULL);
+
+	if (!media->priv->sessions) {
+		purple_debug_info("media", "Creating hash table for sessions\n");
+		media->priv->sessions = g_hash_table_new(g_str_hash, g_str_equal);
+	}
+	g_hash_table_insert(media->priv->sessions, g_strdup(session->id), session);
+}
+
+#if 0
+static gboolean
+purple_media_remove_session(PurpleMedia *media, PurpleMediaSession *session)
+{
+	g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
+	return g_hash_table_remove(media->priv->sessions, session->id);
+}
+#endif
+
+static PurpleMediaStream *
+purple_media_insert_stream(PurpleMediaSession *session,
+		const gchar *name, gboolean initiator)
+{
+	PurpleMediaStream *media_stream;
+	
+	g_return_val_if_fail(session != NULL, NULL);
+
+	media_stream = g_new0(PurpleMediaStream, 1);
+	media_stream->participant = g_strdup(name);
+	media_stream->session = session;
+	media_stream->initiator = initiator;
+
+	session->media->priv->streams =
+			g_list_append(session->media->priv->streams, media_stream);
+
+	return media_stream;
+}
+
+static void
+purple_media_insert_local_candidate(PurpleMediaSession *session, const gchar *name,
+				     PurpleMediaCandidate *candidate)
+{
+	PurpleMediaStream *stream;
+
+	g_return_if_fail(session != NULL);
+
+	stream = purple_media_get_stream(session->media, session->id, name);
+	stream->local_candidates = g_list_append(stream->local_candidates, candidate);
+}
+#endif
+
+GList *
+purple_media_get_session_ids(PurpleMedia *media)
+{
+#ifdef USE_VV
+	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
+	return media->priv->sessions != NULL ?
+			g_hash_table_get_keys(media->priv->sessions) : NULL;
+#else
+	return NULL;
+#endif
+}
+
+#ifdef USE_GSTREAMER
+GstElement *
+purple_media_get_src(PurpleMedia *media, const gchar *sess_id)
+{
+#ifdef USE_VV
+	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
+
+	if (PURPLE_IS_MEDIA_BACKEND_FS2(media->priv->backend))
+		return purple_media_backend_fs2_get_src(
+				PURPLE_MEDIA_BACKEND_FS2(
+				media->priv->backend), sess_id);
+
+	g_return_val_if_reached(NULL);
+#else
+	return NULL;
+#endif
+}
+#endif /* USE_GSTREAMER */
+
+PurpleAccount *
+purple_media_get_account(PurpleMedia *media)
+{
+#ifdef USE_VV
+	PurpleAccount *account;
+	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
+	g_object_get(G_OBJECT(media), "account", &account, NULL);
+	return account;
+#else
+	return NULL;
+#endif
+}
+
+gpointer
+purple_media_get_prpl_data(PurpleMedia *media)
+{
+#ifdef USE_VV
+	gpointer prpl_data;
+	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
+	g_object_get(G_OBJECT(media), "prpl-data", &prpl_data, NULL);
+	return prpl_data;
+#else
+	return NULL;
+#endif
+}
+
+void
+purple_media_set_prpl_data(PurpleMedia *media, gpointer prpl_data)
+{
+#ifdef USE_VV
+	g_return_if_fail(PURPLE_IS_MEDIA(media));
+	g_object_set(G_OBJECT(media), "prpl-data", prpl_data, NULL);
+#endif
+}
+
+void
+purple_media_error(PurpleMedia *media, const gchar *error, ...)
+{
+#ifdef USE_VV
+	va_list args;
+	gchar *message;
+
+	g_return_if_fail(PURPLE_IS_MEDIA(media));
+
+	va_start(args, error);
+	message = g_strdup_vprintf(error, args);
+	va_end(args);
+
+	purple_debug_error("media", "%s\n", message);
+	g_signal_emit(media, purple_media_signals[S_ERROR], 0, message);
+
+	g_free(message);
+#endif
+}
+
+void
+purple_media_end(PurpleMedia *media,
+		const gchar *session_id, const gchar *participant)
+{
+#ifdef USE_VV
+	GList *iter, *sessions = NULL, *participants = NULL;
+
+	g_return_if_fail(PURPLE_IS_MEDIA(media));
+
+	iter = purple_media_get_streams(media, session_id, participant);
+
+	/* Free matching streams */
+	for (; iter; iter = g_list_delete_link(iter, iter)) {
+		PurpleMediaStream *stream = iter->data;
+
+		g_signal_emit(media, purple_media_signals[STATE_CHANGED],
+				0, PURPLE_MEDIA_STATE_END,
+				stream->session->id, stream->participant);
+
+		media->priv->streams =
+				g_list_remove(media->priv->streams, stream);
+
+		if (g_list_find(sessions, stream->session) == NULL)
+			sessions = g_list_prepend(sessions, stream->session);
+
+		if (g_list_find_custom(participants, stream->participant,
+				(GCompareFunc)strcmp) == NULL)
+			participants = g_list_prepend(participants,
+					g_strdup(stream->participant));
+
+		purple_media_stream_free(stream);
+	}
+
+	iter = media->priv->streams;
+
+	/* Reduce to list of sessions to remove */
+	for (; iter; iter = g_list_next(iter)) {
+		PurpleMediaStream *stream = iter->data;
+
+		sessions = g_list_remove(sessions, stream->session);
+	}
+
+	/* Free sessions with no streams left */
+	for (; sessions; sessions = g_list_delete_link(sessions, sessions)) {
+		PurpleMediaSession *session = sessions->data;
+
+		g_signal_emit(media, purple_media_signals[STATE_CHANGED],
+				0, PURPLE_MEDIA_STATE_END,
+				session->id, NULL);
+
+		g_hash_table_remove(media->priv->sessions, session->id);
+		purple_media_session_free(session);
+	}
+
+	iter = media->priv->streams;
+
+	/* Reduce to list of participants to remove */
+	for (; iter; iter = g_list_next(iter)) {
+		PurpleMediaStream *stream = iter->data;
+		GList *tmp;
+
+		tmp = g_list_find_custom(participants,
+				stream->participant, (GCompareFunc)strcmp);
+
+		if (tmp != NULL) {
+			g_free(tmp->data);
+			participants = g_list_delete_link(participants,	tmp);
+		}
+	}
+
+	/* Remove participants with no streams left (just emit the signal) */
+	for (; participants; participants =
+			g_list_delete_link(participants, participants)) {
+		gchar *participant = participants->data;
+		GList *link = g_list_find_custom(media->priv->participants,
+				participant, (GCompareFunc)strcmp);
+
+		g_signal_emit(media, purple_media_signals[STATE_CHANGED],
+				0, PURPLE_MEDIA_STATE_END,
+				NULL, participant);
+
+		if (link != NULL) {
+			g_free(link->data);
+			media->priv->participants = g_list_delete_link(
+					media->priv->participants, link);
+		}
+
+		g_free(participant);
+	}
+
+	/* Free the conference if no sessions left */
+	if (media->priv->sessions != NULL &&
+			g_hash_table_size(media->priv->sessions) == 0) {
+		g_signal_emit(media, purple_media_signals[STATE_CHANGED],
+				0, PURPLE_MEDIA_STATE_END,
+				NULL, NULL);
+		g_object_unref(media);
+		return;
+	}
+#endif
+}
+
+void
+purple_media_stream_info(PurpleMedia *media, PurpleMediaInfoType type,
+		const gchar *session_id, const gchar *participant,
+		gboolean local)
+{
+#ifdef USE_VV
+	g_return_if_fail(PURPLE_IS_MEDIA(media));
+
+	if (type == PURPLE_MEDIA_INFO_ACCEPT) {
+		GList *streams, *sessions = NULL, *participants = NULL;
+
+		g_return_if_fail(PURPLE_IS_MEDIA(media));
+
+		streams = purple_media_get_streams(media,
+				session_id, participant);
+
+		/* Emit stream acceptance */
+		for (; streams; streams =
+				g_list_delete_link(streams, streams)) {
+			PurpleMediaStream *stream = streams->data;
+
+			stream->accepted = TRUE;
+
+			g_signal_emit(media,
+					purple_media_signals[STREAM_INFO],
+					0, type, stream->session->id,
+					stream->participant, local);
+
+			if (g_list_find(sessions, stream->session) == NULL)
+				sessions = g_list_prepend(sessions,
+						stream->session);
+
+			if (g_list_find_custom(participants,
+					stream->participant,
+					(GCompareFunc)strcmp) == NULL)
+				participants = g_list_prepend(participants,
+						g_strdup(stream->participant));
+		}
+
+		/* Emit session acceptance */
+		for (; sessions; sessions =
+				g_list_delete_link(sessions, sessions)) {
+			PurpleMediaSession *session = sessions->data;
+
+			if (purple_media_accepted(media, session->id, NULL))
+				g_signal_emit(media, purple_media_signals[
+						STREAM_INFO], 0,
+						PURPLE_MEDIA_INFO_ACCEPT,
+						session->id, NULL, local);
+		}
+
+		/* Emit participant acceptance */
+		for (; participants; participants = g_list_delete_link(
+				participants, participants)) {
+			gchar *participant = participants->data;
+
+			if (purple_media_accepted(media, NULL, participant))
+				g_signal_emit(media, purple_media_signals[
+						STREAM_INFO], 0,
+						PURPLE_MEDIA_INFO_ACCEPT,
+						NULL, participant, local);
+
+			g_free(participant);
+		}
+
+		/* Emit conference acceptance */
+		if (purple_media_accepted(media, NULL, NULL))
+			g_signal_emit(media,
+					purple_media_signals[STREAM_INFO],
+					0, PURPLE_MEDIA_INFO_ACCEPT,
+					NULL, NULL, local);
+
+		return;
+	} else if (type == PURPLE_MEDIA_INFO_HANGUP ||
+			type == PURPLE_MEDIA_INFO_REJECT) {
+		GList *streams;
+
+		g_return_if_fail(PURPLE_IS_MEDIA(media));
+
+		streams = purple_media_get_streams(media,
+				session_id, participant);
+
+		/* Emit for stream */
+		for (; streams; streams =
+				g_list_delete_link(streams, streams)) {
+			PurpleMediaStream *stream = streams->data;
+
+			g_signal_emit(media,
+					purple_media_signals[STREAM_INFO],
+					0, type, stream->session->id,
+					stream->participant, local);
+		}
+
+		if (session_id != NULL && participant != NULL) {
+			/* Everything that needs to be emitted has been */
+		} else if (session_id == NULL && participant == NULL) {
+			/* Emit for everything in the conference */
+			GList *sessions = NULL;
+			GList *participants = media->priv->participants;
+
+			if (media->priv->sessions != NULL)
+				sessions = g_hash_table_get_values(
+					media->priv->sessions);
+
+			/* Emit for sessions */
+			for (; sessions; sessions = g_list_delete_link(
+					sessions, sessions)) {
+				PurpleMediaSession *session = sessions->data;
+
+				g_signal_emit(media, purple_media_signals[
+						STREAM_INFO], 0, type,
+						session->id, NULL, local);
+			}
+
+			/* Emit for participants */
+			for (; participants; participants =
+					g_list_next(participants)) {
+				gchar *participant = participants->data;
+
+				g_signal_emit(media, purple_media_signals[
+						STREAM_INFO], 0, type,
+						NULL, participant, local);
+			}
+
+			/* Emit for conference */
+			g_signal_emit(media,
+					purple_media_signals[STREAM_INFO],
+					0, type, NULL, NULL, local);
+		} else if (session_id != NULL) {
+			/* Emit just the specific session */
+			PurpleMediaSession *session =
+					purple_media_get_session(
+					media, session_id);
+
+			if (session == NULL) {
+				purple_debug_warning("media",
+						"Couldn't find session"
+						" to hangup/reject.\n");
+			} else {
+				g_signal_emit(media, purple_media_signals[
+						STREAM_INFO], 0, type,
+						session->id, NULL, local);
+			}
+		} else if (participant != NULL) {
+			/* Emit just the specific participant */
+			if (!g_list_find_custom(media->priv->participants,
+					participant, (GCompareFunc)strcmp)) {
+				purple_debug_warning("media",
+						"Couldn't find participant"
+						" to hangup/reject.\n");
+			} else {
+				g_signal_emit(media, purple_media_signals[
+						STREAM_INFO], 0, type, NULL,
+						participant, local);
+			}
+		}
+
+		purple_media_end(media, session_id, participant);
+		return;
+	}
+
+	g_signal_emit(media, purple_media_signals[STREAM_INFO],
+			0, type, session_id, participant, local);
+#endif
+}
+
+#ifdef USE_VV
+static void
+purple_media_new_local_candidate_cb(PurpleMediaBackend *backend,
+		const gchar *sess_id, const gchar *participant,
+		PurpleMediaCandidate *candidate, PurpleMedia *media)
+{
+	PurpleMediaSession *session =
+			purple_media_get_session(media, sess_id);
+
+	purple_media_insert_local_candidate(session, participant,
+			purple_media_candidate_copy(candidate));
+
+	g_signal_emit(session->media, purple_media_signals[NEW_CANDIDATE],
+		      0, session->id, participant, candidate);
+}
+
+static void
+purple_media_candidates_prepared_cb(PurpleMediaBackend *backend,
+		const gchar *sess_id, const gchar *name, PurpleMedia *media)
+{
+	PurpleMediaStream *stream_data;
+
+	g_return_if_fail(PURPLE_IS_MEDIA(media));
+
+	stream_data = purple_media_get_stream(media, sess_id, name);
+	stream_data->candidates_prepared = TRUE;
+
+	g_signal_emit(media, purple_media_signals[CANDIDATES_PREPARED],
+			0, sess_id, name);
+}
+
+/* callback called when a pair of transport candidates (local and remote)
+ * has been established */
+static void
+purple_media_candidate_pair_established_cb(PurpleMediaBackend *backend,
+		const gchar *sess_id, const gchar *name,
+		PurpleMediaCandidate *local_candidate,
+		PurpleMediaCandidate *remote_candidate,
+		PurpleMedia *media)
+{
+	PurpleMediaStream *stream;
+	GList *iter;
+	guint id;
+
+	g_return_if_fail(PURPLE_IS_MEDIA(media));
+
+	stream = purple_media_get_stream(media, sess_id, name);
+	id = purple_media_candidate_get_component_id(local_candidate);
+
+	iter = stream->active_local_candidates;
+	for(; iter; iter = g_list_next(iter)) {
+		PurpleMediaCandidate *c = iter->data;
+		if (id == purple_media_candidate_get_component_id(c)) {
+			g_object_unref(c);
+			stream->active_local_candidates =
+					g_list_delete_link(iter, iter);
+			stream->active_local_candidates = g_list_prepend(
+					stream->active_local_candidates,
+					purple_media_candidate_copy(
+					local_candidate));
+			break;
+		}
+	}
+	if (iter == NULL)
+		stream->active_local_candidates = g_list_prepend(
+				stream->active_local_candidates,
+				purple_media_candidate_copy(
+				local_candidate));
+
+	id = purple_media_candidate_get_component_id(local_candidate);
+
+	iter = stream->active_remote_candidates;
+	for(; iter; iter = g_list_next(iter)) {
+		PurpleMediaCandidate *c = iter->data;
+		if (id == purple_media_candidate_get_component_id(c)) {
+			g_object_unref(c);
+			stream->active_remote_candidates =
+					g_list_delete_link(iter, iter);
+			stream->active_remote_candidates = g_list_prepend(
+					stream->active_remote_candidates,
+					purple_media_candidate_copy(
+					remote_candidate));
+			break;
+		}
+	}
+	if (iter == NULL)
+		stream->active_remote_candidates = g_list_prepend(
+				stream->active_remote_candidates,
+				purple_media_candidate_copy(
+				remote_candidate));
+
+	purple_debug_info("media", "candidate pair established\n");
+}
+
+static void
+purple_media_codecs_changed_cb(PurpleMediaBackend *backend,
+		const gchar *sess_id, PurpleMedia *media)
+{
+	g_signal_emit(media, purple_media_signals[CODECS_CHANGED], 0, sess_id);
+}
+#endif  /* USE_VV */
+
+gboolean
+purple_media_add_stream(PurpleMedia *media, const gchar *sess_id,
+		const gchar *who, PurpleMediaSessionType type,
+		gboolean initiator, const gchar *transmitter,
+		guint num_params, GParameter *params)
+{
+#ifdef USE_VV
+	PurpleMediaSession *session;
+	PurpleMediaStream *stream = NULL;
+
+	g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
+
+	if (!purple_media_backend_add_stream(media->priv->backend,
+			sess_id, who, type, initiator, transmitter,
+			num_params, params)) {
+		purple_debug_error("media", "Error adding stream.\n");
+		return FALSE;
+	}
+
+	session = purple_media_get_session(media, sess_id);
+
+	if (!session) {
+		session = g_new0(PurpleMediaSession, 1);
+		session->id = g_strdup(sess_id);
+		session->media = media;
+		session->type = type;
+		session->initiator = initiator;
+
+		purple_media_add_session(media, session);
+		g_signal_emit(media, purple_media_signals[STATE_CHANGED],
+				0, PURPLE_MEDIA_STATE_NEW,
+				session->id, NULL);
+	}
+
+	if (!g_list_find_custom(media->priv->participants,
+			who, (GCompareFunc)strcmp)) {
+		media->priv->participants = g_list_prepend(
+				media->priv->participants, g_strdup(who));
+
+		g_signal_emit_by_name(media, "state-changed",
+				PURPLE_MEDIA_STATE_NEW, NULL, who);
+	}
+
+	if (purple_media_get_stream(media, sess_id, who) == NULL) {
+		stream = purple_media_insert_stream(session, who, initiator);
+
+		g_signal_emit(media, purple_media_signals[STATE_CHANGED],
+				0, PURPLE_MEDIA_STATE_NEW,
+				session->id, who);
+	}
+
+	return TRUE;
+#else
+	return FALSE;
+#endif  /* USE_VV */
+}
+
+PurpleMediaManager *
+purple_media_get_manager(PurpleMedia *media)
+{
+	PurpleMediaManager *ret;
+	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
+	g_object_get(media, "manager", &ret, NULL);
+	return ret;
+}
+
+PurpleMediaSessionType
+purple_media_get_session_type(PurpleMedia *media, const gchar *sess_id)
+{
+#ifdef USE_VV
+	PurpleMediaSession *session;
+	g_return_val_if_fail(PURPLE_IS_MEDIA(media), PURPLE_MEDIA_NONE);
+	session = purple_media_get_session(media, sess_id);
+	return session->type;
+#else
+	return PURPLE_MEDIA_NONE;
+#endif
+}
+/* XXX: Should wait until codecs-ready is TRUE before using this function */
+GList *
+purple_media_get_codecs(PurpleMedia *media, const gchar *sess_id)
+{
+#ifdef USE_VV
+	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
+
+	return purple_media_backend_get_codecs(media->priv->backend, sess_id);
+#else
+	return NULL;
+#endif
+}
+
+GList *
+purple_media_get_local_candidates(PurpleMedia *media, const gchar *sess_id,
+                                  const gchar *participant)
+{
+#ifdef USE_VV
+	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
+
+	return purple_media_backend_get_local_candidates(media->priv->backend,
+			sess_id, participant);
+#else
+	return NULL;
+#endif
+}
+
+void
+purple_media_add_remote_candidates(PurpleMedia *media, const gchar *sess_id,
+                                   const gchar *participant,
+                                   GList *remote_candidates)
+{
+#ifdef USE_VV
+	PurpleMediaStream *stream;
+
+	g_return_if_fail(PURPLE_IS_MEDIA(media));
+	stream = purple_media_get_stream(media, sess_id, participant);
+
+	if (stream == NULL) {
+		purple_debug_error("media",
+				"purple_media_add_remote_candidates: "
+				"couldn't find stream %s %s.\n",
+				sess_id ? sess_id : "(null)",
+				participant ? participant : "(null)");
+		return;
+	}
+
+	stream->remote_candidates = g_list_concat(stream->remote_candidates,
+			purple_media_candidate_list_copy(remote_candidates));
+
+	purple_media_backend_add_remote_candidates(media->priv->backend,
+			sess_id, participant, remote_candidates);
+#endif
+}
+
+#if 0
+/*
+ * These two functions aren't being used and I'd rather not lock in the API
+ * until they are needed. If they ever are.
+ */
+
+GList *
+purple_media_get_active_local_candidates(PurpleMedia *media,
+		const gchar *sess_id, const gchar *participant)
+{
+#ifdef USE_VV
+	PurpleMediaStream *stream;
+	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
+	stream = purple_media_get_stream(media, sess_id, participant);
+	return purple_media_candidate_list_copy(
+			stream->active_local_candidates);
+#else
+	return NULL;
+#endif
+}
+
+GList *
+purple_media_get_active_remote_candidates(PurpleMedia *media,
+		const gchar *sess_id, const gchar *participant)
+{
+#ifdef USE_VV
+	PurpleMediaStream *stream;
+	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
+	stream = purple_media_get_stream(media, sess_id, participant);
+	return purple_media_candidate_list_copy(
+			stream->active_remote_candidates);
+#else
+	return NULL;
+#endif
+}
+#endif
+
+gboolean
+purple_media_set_remote_codecs(PurpleMedia *media, const gchar *sess_id,
+                               const gchar *participant, GList *codecs)
+{
+#ifdef USE_VV
+	g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
+
+	return purple_media_backend_set_remote_codecs(media->priv->backend,
+			sess_id, participant, codecs);
+#else
+	return FALSE;
+#endif
+}
+
+gboolean
+purple_media_candidates_prepared(PurpleMedia *media,
+		const gchar *session_id, const gchar *participant)
+{
+#ifdef USE_VV
+	GList *streams;
+	gboolean prepared = TRUE;
+
+	g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
+
+	streams = purple_media_get_streams(media, session_id, participant);
+
+	for (; streams; streams = g_list_delete_link(streams, streams)) {
+		PurpleMediaStream *stream = streams->data;
+		if (stream->candidates_prepared == FALSE) {
+			g_list_free(streams);
+			prepared = FALSE;
+			break;
+		}
+	}
+
+	return prepared;
+#else
+	return FALSE;
+#endif
+}
+
+gboolean
+purple_media_set_send_codec(PurpleMedia *media, const gchar *sess_id, PurpleMediaCodec *codec)
+{
+#ifdef USE_VV
+	g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
+
+	return purple_media_backend_set_send_codec(
+			media->priv->backend, sess_id, codec);
+#else
+	return FALSE;
+#endif
+}
+
+gboolean
+purple_media_codecs_ready(PurpleMedia *media, const gchar *sess_id)
+{
+#ifdef USE_VV
+	g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
+
+	return purple_media_backend_codecs_ready(
+			media->priv->backend, sess_id);
+#else
+	return FALSE;
+#endif
+}
+
+gboolean
+purple_media_is_initiator(PurpleMedia *media,
+		const gchar *sess_id, const gchar *participant)
+{
+#ifdef USE_VV
+	g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
+
+	if (sess_id == NULL && participant == NULL)
+		return media->priv->initiator;
+	else if (sess_id != NULL && participant == NULL) {
+		PurpleMediaSession *session =
+				purple_media_get_session(media, sess_id);
+		return session != NULL ? session->initiator : FALSE;
+	} else if (sess_id != NULL && participant != NULL) {
+		PurpleMediaStream *stream = purple_media_get_stream(
+				media, sess_id, participant);
+		return stream != NULL ? stream->initiator : FALSE;
+	}
+#endif
+	return FALSE;
+}
+
+gboolean
+purple_media_accepted(PurpleMedia *media, const gchar *sess_id,
+		const gchar *participant)
+{
+#ifdef USE_VV
+	gboolean accepted = TRUE;
+
+	g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
+
+	if (sess_id == NULL && participant == NULL) {
+		GList *streams = media->priv->streams;
+
+		for (; streams; streams = g_list_next(streams)) {
+			PurpleMediaStream *stream = streams->data;
+			if (stream->accepted == FALSE) {
+				accepted = FALSE;
+				break;
+			}
+		}
+	} else if (sess_id != NULL && participant == NULL) {
+		GList *streams = purple_media_get_streams(
+				media, sess_id, NULL);
+		for (; streams; streams =
+				g_list_delete_link(streams, streams)) {
+			PurpleMediaStream *stream = streams->data;
+			if (stream->accepted == FALSE) {
+				g_list_free(streams);
+				accepted = FALSE;
+				break;
+			}
+		}
+	} else if (sess_id != NULL && participant != NULL) {
+		PurpleMediaStream *stream = purple_media_get_stream(
+				media, sess_id, participant);
+		if (stream == NULL || stream->accepted == FALSE)
+			accepted = FALSE;
+	}
+
+	return accepted;
+#else
+	return FALSE;
+#endif
+}
+
+void purple_media_set_input_volume(PurpleMedia *media,
+		const gchar *session_id, double level)
+{
+#ifdef USE_VV
+	g_return_if_fail(PURPLE_IS_MEDIA(media));
+	g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(media->priv->backend));
+
+	purple_media_backend_fs2_set_input_volume(
+			PURPLE_MEDIA_BACKEND_FS2(
+			media->priv->backend),
+			session_id, level);
+#endif
+}
+
+void purple_media_set_output_volume(PurpleMedia *media,
+		const gchar *session_id, const gchar *participant,
+		double level)
+{
+#ifdef USE_VV
+	g_return_if_fail(PURPLE_IS_MEDIA(media));
+	g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(media->priv->backend));
+
+	purple_media_backend_fs2_set_output_volume(
+			PURPLE_MEDIA_BACKEND_FS2(
+			media->priv->backend),
+			session_id, participant, level);
+#endif
+}
+
+gulong
+purple_media_set_output_window(PurpleMedia *media, const gchar *session_id,
+		const gchar *participant, gulong window_id)
+{
+#ifdef USE_VV
+	g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
+
+	return purple_media_manager_set_output_window(media->priv->manager,
+			media, session_id, participant, window_id);
+#else
+	return 0;
+#endif
+}
+
+void
+purple_media_remove_output_windows(PurpleMedia *media)
+{
+#ifdef USE_VV
+	GList *iter = media->priv->streams;
+	for (; iter; iter = g_list_next(iter)) {
+		PurpleMediaStream *stream = iter->data;
+		purple_media_manager_remove_output_windows(
+				media->priv->manager, media,
+				stream->session->id, stream->participant);
+	}
+
+	iter = purple_media_get_session_ids(media);
+	for (; iter; iter = g_list_delete_link(iter, iter)) {
+		gchar *session_name = iter->data;
+		purple_media_manager_remove_output_windows(
+				media->priv->manager, media,
+				session_name, NULL);
+	}
+#endif
+}
+
+#ifdef USE_GSTREAMER
+GstElement *
+purple_media_get_tee(PurpleMedia *media,
+		const gchar *session_id, const gchar *participant)
+{
+#ifdef USE_VV
+	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
+
+	if (PURPLE_IS_MEDIA_BACKEND_FS2(media->priv->backend))
+		return purple_media_backend_fs2_get_tee(
+				PURPLE_MEDIA_BACKEND_FS2(
+				media->priv->backend),
+				session_id, participant);
+	g_return_val_if_reached(NULL);
+#else
+	return NULL;
+#endif
+}
+#endif /* USE_GSTREAMER */
+
--- a/libpurple/media/media.c	Sat Feb 06 06:32:21 2010 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1407 +0,0 @@
-/**
- * @file media.c Media API
- * @ingroup core
- */
-
-/* purple
- *
- * Purple is the legal property of its developers, whose names are too numerous
- * to list here.  Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
- */
-#include "internal.h"
-
-#include "account.h"
-#include "media.h"
-#include "media/backend-iface.h"
-#include "mediamanager.h"
-
-#include "debug.h"
-
-#ifdef USE_GSTREAMER
-#include "media/backend-fs2.h"
-#include "marshallers.h"
-#include "media-gst.h"
-#endif
-
-#ifdef USE_VV
-
-/** @copydoc _PurpleMediaSession */
-typedef struct _PurpleMediaSession PurpleMediaSession;
-/** @copydoc _PurpleMediaStream */
-typedef struct _PurpleMediaStream PurpleMediaStream;
-/** @copydoc _PurpleMediaClass */
-typedef struct _PurpleMediaClass PurpleMediaClass;
-/** @copydoc _PurpleMediaPrivate */
-typedef struct _PurpleMediaPrivate PurpleMediaPrivate;
-
-/** The media class */
-struct _PurpleMediaClass
-{
-	GObjectClass parent_class;     /**< The parent class. */
-};
-
-/** The media class's private data */
-struct _PurpleMedia
-{
-	GObject parent;                /**< The parent of this object. */
-	PurpleMediaPrivate *priv;      /**< The private data of this object. */
-};
-
-struct _PurpleMediaSession
-{
-	gchar *id;
-	PurpleMedia *media;
-	PurpleMediaSessionType type;
-	gboolean initiator;
-};
-
-struct _PurpleMediaStream
-{
-	PurpleMediaSession *session;
-	gchar *participant;
-
-	GList *local_candidates;
-	GList *remote_candidates;
-
-	gboolean initiator;
-	gboolean accepted;
-	gboolean candidates_prepared;
-
-	GList *active_local_candidates;
-	GList *active_remote_candidates;
-};
-#endif
-
-struct _PurpleMediaPrivate
-{
-#ifdef USE_VV
-	PurpleMediaManager *manager;
-	PurpleAccount *account;
-	PurpleMediaBackend *backend;
-	gchar *conference_type;
-	gboolean initiator;
-	gpointer prpl_data;
-
-	GHashTable *sessions;	/* PurpleMediaSession table */
-	GList *participants;
-	GList *streams;		/* PurpleMediaStream table */
-#else
-	gpointer dummy;
-#endif
-};
-
-#ifdef USE_VV
-#define PURPLE_MEDIA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA, PurpleMediaPrivate))
-
-static void purple_media_class_init (PurpleMediaClass *klass);
-static void purple_media_init (PurpleMedia *media);
-static void purple_media_dispose (GObject *object);
-static void purple_media_finalize (GObject *object);
-static void purple_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
-static void purple_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
-
-static void purple_media_new_local_candidate_cb(PurpleMediaBackend *backend,
-		const gchar *sess_id, const gchar *participant,
-		PurpleMediaCandidate *candidate, PurpleMedia *media);
-static void purple_media_candidates_prepared_cb(PurpleMediaBackend *backend,
-		const gchar *sess_id, const gchar *name, PurpleMedia *media);
-static void purple_media_candidate_pair_established_cb(
-		PurpleMediaBackend *backend,
-		const gchar *sess_id, const gchar *name,
-		PurpleMediaCandidate *local_candidate,
-		PurpleMediaCandidate *remote_candidate,
-		PurpleMedia *media);
-static void purple_media_codecs_changed_cb(PurpleMediaBackend *backend,
-		const gchar *sess_id, PurpleMedia *media);
-
-static GObjectClass *parent_class = NULL;
-
-
-
-enum {
-	S_ERROR,
-	CANDIDATES_PREPARED,
-	CODECS_CHANGED,
-	LEVEL,
-	NEW_CANDIDATE,
-	STATE_CHANGED,
-	STREAM_INFO,
-	LAST_SIGNAL
-};
-static guint purple_media_signals[LAST_SIGNAL] = {0};
-
-enum {
-	PROP_0,
-	PROP_MANAGER,
-	PROP_BACKEND,
-	PROP_ACCOUNT,
-	PROP_CONFERENCE_TYPE,
-	PROP_INITIATOR,
-	PROP_PRPL_DATA,
-};
-#endif
-
-
-GType
-purple_media_get_type()
-{
-#ifdef USE_VV
-	static GType type = 0;
-
-	if (type == 0) {
-		static const GTypeInfo info = {
-			sizeof(PurpleMediaClass),
-			NULL,
-			NULL,
-			(GClassInitFunc) purple_media_class_init,
-			NULL,
-			NULL,
-			sizeof(PurpleMedia),
-			0,
-			(GInstanceInitFunc) purple_media_init,
-			NULL
-		};
-		type = g_type_register_static(G_TYPE_OBJECT, "PurpleMedia", &info, 0);
-	}
-	return type;
-#else
-	return G_TYPE_NONE;
-#endif
-}
-
-#ifdef USE_VV
-static void
-purple_media_class_init (PurpleMediaClass *klass)
-{
-	GObjectClass *gobject_class = (GObjectClass*)klass;
-	parent_class = g_type_class_peek_parent(klass);
-	
-	gobject_class->dispose = purple_media_dispose;
-	gobject_class->finalize = purple_media_finalize;
-	gobject_class->set_property = purple_media_set_property;
-	gobject_class->get_property = purple_media_get_property;
-
-	g_object_class_install_property(gobject_class, PROP_MANAGER,
-			g_param_spec_object("manager",
-			"Purple Media Manager",
-			"The media manager that contains this media session.",
-			PURPLE_TYPE_MEDIA_MANAGER,
-			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
-
-	/*
-	 * This one should be PURPLE_TYPE_MEDIA_BACKEND, but it doesn't
-	 * like interfaces because they "aren't GObjects"
-	 */
-	g_object_class_install_property(gobject_class, PROP_BACKEND,
-			g_param_spec_object("backend",
-			"Purple Media Backend",
-			"The backend object this media object uses.",
-			G_TYPE_OBJECT,
-			G_PARAM_READABLE));
-
-	g_object_class_install_property(gobject_class, PROP_ACCOUNT,
-			g_param_spec_pointer("account",
-			"PurpleAccount",
-			"The account this media session is on.",
-			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
-
-	g_object_class_install_property(gobject_class, PROP_CONFERENCE_TYPE,
-			g_param_spec_string("conference-type",
-			"Conference Type",
-			"The type of conference that this media object "
-			"has been created to provide.",
-			NULL,
-			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
-
-	g_object_class_install_property(gobject_class, PROP_INITIATOR,
-			g_param_spec_boolean("initiator",
-			"initiator",
-			"If the local user initiated the conference.",
-			FALSE,
-			G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
-
-	g_object_class_install_property(gobject_class, PROP_PRPL_DATA,
-			g_param_spec_pointer("prpl-data",
-			"gpointer",
-			"Data the prpl plugin set on the media session.",
-			G_PARAM_READWRITE));
-
-	purple_media_signals[S_ERROR] = g_signal_new("error", G_TYPE_FROM_CLASS(klass),
-					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
-					 g_cclosure_marshal_VOID__STRING,
-					 G_TYPE_NONE, 1, G_TYPE_STRING);
-	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,
-					 G_TYPE_NONE, 2, G_TYPE_STRING,
-					 G_TYPE_STRING);
-	purple_media_signals[CODECS_CHANGED] = g_signal_new("codecs-changed", G_TYPE_FROM_CLASS(klass),
-					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
-					 g_cclosure_marshal_VOID__STRING,
-					 G_TYPE_NONE, 1, G_TYPE_STRING);
-	purple_media_signals[LEVEL] = g_signal_new("level", G_TYPE_FROM_CLASS(klass),
-					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
-					 purple_smarshal_VOID__STRING_STRING_DOUBLE,
-					 G_TYPE_NONE, 3, G_TYPE_STRING,
-					 G_TYPE_STRING, G_TYPE_DOUBLE);
-	purple_media_signals[NEW_CANDIDATE] = g_signal_new("new-candidate", G_TYPE_FROM_CLASS(klass),
-					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
-					 purple_smarshal_VOID__POINTER_POINTER_OBJECT,
-					 G_TYPE_NONE, 3, G_TYPE_POINTER,
-					 G_TYPE_POINTER, PURPLE_TYPE_MEDIA_CANDIDATE);
-	purple_media_signals[STATE_CHANGED] = g_signal_new("state-changed", G_TYPE_FROM_CLASS(klass),
-					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
-					 purple_smarshal_VOID__ENUM_STRING_STRING,
-					 G_TYPE_NONE, 3, PURPLE_MEDIA_TYPE_STATE,
-					 G_TYPE_STRING, G_TYPE_STRING);
-	purple_media_signals[STREAM_INFO] = g_signal_new("stream-info", G_TYPE_FROM_CLASS(klass),
-					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
-					 purple_smarshal_VOID__ENUM_STRING_STRING_BOOLEAN,
-					 G_TYPE_NONE, 4, PURPLE_MEDIA_TYPE_INFO_TYPE,
-					 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
-	g_type_class_add_private(klass, sizeof(PurpleMediaPrivate));
-}
-
-
-static void
-purple_media_init (PurpleMedia *media)
-{
-	media->priv = PURPLE_MEDIA_GET_PRIVATE(media);
-	memset(media->priv, 0, sizeof(*media->priv));
-}
-
-static void
-purple_media_stream_free(PurpleMediaStream *stream)
-{
-	if (stream == NULL)
-		return;
-
-	g_free(stream->participant);
-
-	if (stream->local_candidates)
-		purple_media_candidate_list_free(stream->local_candidates);
-	if (stream->remote_candidates)
-		purple_media_candidate_list_free(stream->remote_candidates);
-
-	if (stream->active_local_candidates)
-		purple_media_candidate_list_free(
-				stream->active_local_candidates);
-	if (stream->active_remote_candidates)
-		purple_media_candidate_list_free(
-				stream->active_remote_candidates);
-
-	g_free(stream);
-}
-
-static void
-purple_media_session_free(PurpleMediaSession *session)
-{
-	if (session == NULL)
-		return;
-
-	g_free(session->id);
-	g_free(session);
-}
-
-static void
-purple_media_dispose(GObject *media)
-{
-	PurpleMediaPrivate *priv = PURPLE_MEDIA_GET_PRIVATE(media);
-
-	purple_debug_info("media","purple_media_dispose\n");
-
-	purple_media_manager_remove_media(priv->manager, PURPLE_MEDIA(media));
-
-	if (priv->backend) {
-		g_object_unref(priv->backend);
-		priv->backend = NULL;
-	}
-
-	if (priv->manager) {
-		g_object_unref(priv->manager);
-		priv->manager = NULL;
-	}
-
-	G_OBJECT_CLASS(parent_class)->dispose(media);
-}
-
-static void
-purple_media_finalize(GObject *media)
-{
-	PurpleMediaPrivate *priv = PURPLE_MEDIA_GET_PRIVATE(media);
-	purple_debug_info("media","purple_media_finalize\n");
-
-	for (; priv->streams; priv->streams = g_list_delete_link(priv->streams, priv->streams))
-		purple_media_stream_free(priv->streams->data);
-
-	for (; priv->participants; priv->participants = g_list_delete_link(
-			priv->participants, priv->participants))
-		g_free(priv->participants->data);
-
-	if (priv->sessions) {
-		GList *sessions = g_hash_table_get_values(priv->sessions);
-		for (; sessions; sessions = g_list_delete_link(sessions, sessions)) {
-			purple_media_session_free(sessions->data);
-		}
-		g_hash_table_destroy(priv->sessions);
-	}
-
-	G_OBJECT_CLASS(parent_class)->finalize(media);
-}
-
-static void
-purple_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
-{
-	PurpleMedia *media;
-	g_return_if_fail(PURPLE_IS_MEDIA(object));
-
-	media = PURPLE_MEDIA(object);
-
-	switch (prop_id) {
-		case PROP_MANAGER:
-			media->priv->manager = g_value_dup_object(value);
-			break;
-		case PROP_ACCOUNT:
-			media->priv->account = g_value_get_pointer(value);
-			break;
-		case PROP_CONFERENCE_TYPE:
-			media->priv->conference_type =
-					g_value_dup_string(value);
-			media->priv->backend = g_object_new(
-					purple_media_manager_get_backend_type(
-					purple_media_manager_get()),
-					"conference-type",
-					media->priv->conference_type,
-					"media", media,
-					NULL);
-			g_signal_connect(media->priv->backend,
-					"active-candidate-pair",
-					G_CALLBACK(
-					purple_media_candidate_pair_established_cb),
-					media);
-			g_signal_connect(media->priv->backend,
-					"candidates-prepared",
-					G_CALLBACK(
-					purple_media_candidates_prepared_cb),
-					media);
-			g_signal_connect(media->priv->backend,
-					"codecs-changed",
-					G_CALLBACK(
-					purple_media_codecs_changed_cb),
-					media);
-			g_signal_connect(media->priv->backend,
-					"new-candidate",
-					G_CALLBACK(
-					purple_media_new_local_candidate_cb),
-					media);
-			break;
-		case PROP_INITIATOR:
-			media->priv->initiator = g_value_get_boolean(value);
-			break;
-		case PROP_PRPL_DATA:
-			media->priv->prpl_data = g_value_get_pointer(value);
-			break;
-		default:	
-			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-			break;
-	}
-}
-
-static void
-purple_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
-{
-	PurpleMedia *media;
-	g_return_if_fail(PURPLE_IS_MEDIA(object));
-	
-	media = PURPLE_MEDIA(object);
-
-	switch (prop_id) {
-		case PROP_MANAGER:
-			g_value_set_object(value, media->priv->manager);
-			break;
-		case PROP_BACKEND:
-			g_value_set_object(value, media->priv->backend);
-			break;
-		case PROP_ACCOUNT:
-			g_value_set_pointer(value, media->priv->account);
-			break;
-		case PROP_CONFERENCE_TYPE:
-			g_value_set_string(value,
-					media->priv->conference_type);
-			break;
-		case PROP_INITIATOR:
-			g_value_set_boolean(value, media->priv->initiator);
-			break;
-		case PROP_PRPL_DATA:
-			g_value_set_pointer(value, media->priv->prpl_data);
-			break;
-		default:	
-			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);	
-			break;
-	}
-
-}
-
-static PurpleMediaSession*
-purple_media_get_session(PurpleMedia *media, const gchar *sess_id)
-{
-	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
-	return (PurpleMediaSession*) (media->priv->sessions) ?
-			g_hash_table_lookup(media->priv->sessions, sess_id) : NULL;
-}
-
-static PurpleMediaStream*
-purple_media_get_stream(PurpleMedia *media, const gchar *session, const gchar *participant)
-{
-	GList *streams;
-
-	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
-
-	streams = media->priv->streams;
-
-	for (; streams; streams = g_list_next(streams)) {
-		PurpleMediaStream *stream = streams->data;
-		if (!strcmp(stream->session->id, session) &&
-				!strcmp(stream->participant, participant))
-			return stream;
-	}
-
-	return NULL;
-}
-
-static GList *
-purple_media_get_streams(PurpleMedia *media, const gchar *session,
-		const gchar *participant)
-{
-	GList *streams;
-	GList *ret = NULL;
-	
-	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
-
-	streams = media->priv->streams;
-
-	for (; streams; streams = g_list_next(streams)) {
-		PurpleMediaStream *stream = streams->data;
-		if ((session == NULL ||
-				!strcmp(stream->session->id, session)) &&
-				(participant == NULL ||
-				!strcmp(stream->participant, participant)))
-			ret = g_list_append(ret, stream);
-	}
-
-	return ret;
-}
-
-static void
-purple_media_add_session(PurpleMedia *media, PurpleMediaSession *session)
-{
-	g_return_if_fail(PURPLE_IS_MEDIA(media));
-	g_return_if_fail(session != NULL);
-
-	if (!media->priv->sessions) {
-		purple_debug_info("media", "Creating hash table for sessions\n");
-		media->priv->sessions = g_hash_table_new(g_str_hash, g_str_equal);
-	}
-	g_hash_table_insert(media->priv->sessions, g_strdup(session->id), session);
-}
-
-#if 0
-static gboolean
-purple_media_remove_session(PurpleMedia *media, PurpleMediaSession *session)
-{
-	g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
-	return g_hash_table_remove(media->priv->sessions, session->id);
-}
-#endif
-
-static PurpleMediaStream *
-purple_media_insert_stream(PurpleMediaSession *session,
-		const gchar *name, gboolean initiator)
-{
-	PurpleMediaStream *media_stream;
-	
-	g_return_val_if_fail(session != NULL, NULL);
-
-	media_stream = g_new0(PurpleMediaStream, 1);
-	media_stream->participant = g_strdup(name);
-	media_stream->session = session;
-	media_stream->initiator = initiator;
-
-	session->media->priv->streams =
-			g_list_append(session->media->priv->streams, media_stream);
-
-	return media_stream;
-}
-
-static void
-purple_media_insert_local_candidate(PurpleMediaSession *session, const gchar *name,
-				     PurpleMediaCandidate *candidate)
-{
-	PurpleMediaStream *stream;
-
-	g_return_if_fail(session != NULL);
-
-	stream = purple_media_get_stream(session->media, session->id, name);
-	stream->local_candidates = g_list_append(stream->local_candidates, candidate);
-}
-#endif
-
-GList *
-purple_media_get_session_ids(PurpleMedia *media)
-{
-#ifdef USE_VV
-	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
-	return media->priv->sessions != NULL ?
-			g_hash_table_get_keys(media->priv->sessions) : NULL;
-#else
-	return NULL;
-#endif
-}
-
-#ifdef USE_GSTREAMER
-GstElement *
-purple_media_get_src(PurpleMedia *media, const gchar *sess_id)
-{
-#ifdef USE_VV
-	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
-
-	if (PURPLE_IS_MEDIA_BACKEND_FS2(media->priv->backend))
-		return purple_media_backend_fs2_get_src(
-				PURPLE_MEDIA_BACKEND_FS2(
-				media->priv->backend), sess_id);
-
-	g_return_val_if_reached(NULL);
-#else
-	return NULL;
-#endif
-}
-#endif /* USE_GSTREAMER */
-
-PurpleAccount *
-purple_media_get_account(PurpleMedia *media)
-{
-#ifdef USE_VV
-	PurpleAccount *account;
-	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
-	g_object_get(G_OBJECT(media), "account", &account, NULL);
-	return account;
-#else
-	return NULL;
-#endif
-}
-
-gpointer
-purple_media_get_prpl_data(PurpleMedia *media)
-{
-#ifdef USE_VV
-	gpointer prpl_data;
-	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
-	g_object_get(G_OBJECT(media), "prpl-data", &prpl_data, NULL);
-	return prpl_data;
-#else
-	return NULL;
-#endif
-}
-
-void
-purple_media_set_prpl_data(PurpleMedia *media, gpointer prpl_data)
-{
-#ifdef USE_VV
-	g_return_if_fail(PURPLE_IS_MEDIA(media));
-	g_object_set(G_OBJECT(media), "prpl-data", prpl_data, NULL);
-#endif
-}
-
-void
-purple_media_error(PurpleMedia *media, const gchar *error, ...)
-{
-#ifdef USE_VV
-	va_list args;
-	gchar *message;
-
-	g_return_if_fail(PURPLE_IS_MEDIA(media));
-
-	va_start(args, error);
-	message = g_strdup_vprintf(error, args);
-	va_end(args);
-
-	purple_debug_error("media", "%s\n", message);
-	g_signal_emit(media, purple_media_signals[S_ERROR], 0, message);
-
-	g_free(message);
-#endif
-}
-
-void
-purple_media_end(PurpleMedia *media,
-		const gchar *session_id, const gchar *participant)
-{
-#ifdef USE_VV
-	GList *iter, *sessions = NULL, *participants = NULL;
-
-	g_return_if_fail(PURPLE_IS_MEDIA(media));
-
-	iter = purple_media_get_streams(media, session_id, participant);
-
-	/* Free matching streams */
-	for (; iter; iter = g_list_delete_link(iter, iter)) {
-		PurpleMediaStream *stream = iter->data;
-
-		g_signal_emit(media, purple_media_signals[STATE_CHANGED],
-				0, PURPLE_MEDIA_STATE_END,
-				stream->session->id, stream->participant);
-
-		media->priv->streams =
-				g_list_remove(media->priv->streams, stream);
-
-		if (g_list_find(sessions, stream->session) == NULL)
-			sessions = g_list_prepend(sessions, stream->session);
-
-		if (g_list_find_custom(participants, stream->participant,
-				(GCompareFunc)strcmp) == NULL)
-			participants = g_list_prepend(participants,
-					g_strdup(stream->participant));
-
-		purple_media_stream_free(stream);
-	}
-
-	iter = media->priv->streams;
-
-	/* Reduce to list of sessions to remove */
-	for (; iter; iter = g_list_next(iter)) {
-		PurpleMediaStream *stream = iter->data;
-
-		sessions = g_list_remove(sessions, stream->session);
-	}
-
-	/* Free sessions with no streams left */
-	for (; sessions; sessions = g_list_delete_link(sessions, sessions)) {
-		PurpleMediaSession *session = sessions->data;
-
-		g_signal_emit(media, purple_media_signals[STATE_CHANGED],
-				0, PURPLE_MEDIA_STATE_END,
-				session->id, NULL);
-
-		g_hash_table_remove(media->priv->sessions, session->id);
-		purple_media_session_free(session);
-	}
-
-	iter = media->priv->streams;
-
-	/* Reduce to list of participants to remove */
-	for (; iter; iter = g_list_next(iter)) {
-		PurpleMediaStream *stream = iter->data;
-		GList *tmp;
-
-		tmp = g_list_find_custom(participants,
-				stream->participant, (GCompareFunc)strcmp);
-
-		if (tmp != NULL) {
-			g_free(tmp->data);
-			participants = g_list_delete_link(participants,	tmp);
-		}
-	}
-
-	/* Remove participants with no streams left (just emit the signal) */
-	for (; participants; participants =
-			g_list_delete_link(participants, participants)) {
-		gchar *participant = participants->data;
-		GList *link = g_list_find_custom(media->priv->participants,
-				participant, (GCompareFunc)strcmp);
-
-		g_signal_emit(media, purple_media_signals[STATE_CHANGED],
-				0, PURPLE_MEDIA_STATE_END,
-				NULL, participant);
-
-		if (link != NULL) {
-			g_free(link->data);
-			media->priv->participants = g_list_delete_link(
-					media->priv->participants, link);
-		}
-
-		g_free(participant);
-	}
-
-	/* Free the conference if no sessions left */
-	if (media->priv->sessions != NULL &&
-			g_hash_table_size(media->priv->sessions) == 0) {
-		g_signal_emit(media, purple_media_signals[STATE_CHANGED],
-				0, PURPLE_MEDIA_STATE_END,
-				NULL, NULL);
-		g_object_unref(media);
-		return;
-	}
-#endif
-}
-
-void
-purple_media_stream_info(PurpleMedia *media, PurpleMediaInfoType type,
-		const gchar *session_id, const gchar *participant,
-		gboolean local)
-{
-#ifdef USE_VV
-	g_return_if_fail(PURPLE_IS_MEDIA(media));
-
-	if (type == PURPLE_MEDIA_INFO_ACCEPT) {
-		GList *streams, *sessions = NULL, *participants = NULL;
-
-		g_return_if_fail(PURPLE_IS_MEDIA(media));
-
-		streams = purple_media_get_streams(media,
-				session_id, participant);
-
-		/* Emit stream acceptance */
-		for (; streams; streams =
-				g_list_delete_link(streams, streams)) {
-			PurpleMediaStream *stream = streams->data;
-
-			stream->accepted = TRUE;
-
-			g_signal_emit(media,
-					purple_media_signals[STREAM_INFO],
-					0, type, stream->session->id,
-					stream->participant, local);
-
-			if (g_list_find(sessions, stream->session) == NULL)
-				sessions = g_list_prepend(sessions,
-						stream->session);
-
-			if (g_list_find_custom(participants,
-					stream->participant,
-					(GCompareFunc)strcmp) == NULL)
-				participants = g_list_prepend(participants,
-						g_strdup(stream->participant));
-		}
-
-		/* Emit session acceptance */
-		for (; sessions; sessions =
-				g_list_delete_link(sessions, sessions)) {
-			PurpleMediaSession *session = sessions->data;
-
-			if (purple_media_accepted(media, session->id, NULL))
-				g_signal_emit(media, purple_media_signals[
-						STREAM_INFO], 0,
-						PURPLE_MEDIA_INFO_ACCEPT,
-						session->id, NULL, local);
-		}
-
-		/* Emit participant acceptance */
-		for (; participants; participants = g_list_delete_link(
-				participants, participants)) {
-			gchar *participant = participants->data;
-
-			if (purple_media_accepted(media, NULL, participant))
-				g_signal_emit(media, purple_media_signals[
-						STREAM_INFO], 0,
-						PURPLE_MEDIA_INFO_ACCEPT,
-						NULL, participant, local);
-
-			g_free(participant);
-		}
-
-		/* Emit conference acceptance */
-		if (purple_media_accepted(media, NULL, NULL))
-			g_signal_emit(media,
-					purple_media_signals[STREAM_INFO],
-					0, PURPLE_MEDIA_INFO_ACCEPT,
-					NULL, NULL, local);
-
-		return;
-	} else if (type == PURPLE_MEDIA_INFO_HANGUP ||
-			type == PURPLE_MEDIA_INFO_REJECT) {
-		GList *streams;
-
-		g_return_if_fail(PURPLE_IS_MEDIA(media));
-
-		streams = purple_media_get_streams(media,
-				session_id, participant);
-
-		/* Emit for stream */
-		for (; streams; streams =
-				g_list_delete_link(streams, streams)) {
-			PurpleMediaStream *stream = streams->data;
-
-			g_signal_emit(media,
-					purple_media_signals[STREAM_INFO],
-					0, type, stream->session->id,
-					stream->participant, local);
-		}
-
-		if (session_id != NULL && participant != NULL) {
-			/* Everything that needs to be emitted has been */
-		} else if (session_id == NULL && participant == NULL) {
-			/* Emit for everything in the conference */
-			GList *sessions = NULL;
-			GList *participants = media->priv->participants;
-
-			if (media->priv->sessions != NULL)
-				sessions = g_hash_table_get_values(
-					media->priv->sessions);
-
-			/* Emit for sessions */
-			for (; sessions; sessions = g_list_delete_link(
-					sessions, sessions)) {
-				PurpleMediaSession *session = sessions->data;
-
-				g_signal_emit(media, purple_media_signals[
-						STREAM_INFO], 0, type,
-						session->id, NULL, local);
-			}
-
-			/* Emit for participants */
-			for (; participants; participants =
-					g_list_next(participants)) {
-				gchar *participant = participants->data;
-
-				g_signal_emit(media, purple_media_signals[
-						STREAM_INFO], 0, type,
-						NULL, participant, local);
-			}
-
-			/* Emit for conference */
-			g_signal_emit(media,
-					purple_media_signals[STREAM_INFO],
-					0, type, NULL, NULL, local);
-		} else if (session_id != NULL) {
-			/* Emit just the specific session */
-			PurpleMediaSession *session =
-					purple_media_get_session(
-					media, session_id);
-
-			if (session == NULL) {
-				purple_debug_warning("media",
-						"Couldn't find session"
-						" to hangup/reject.\n");
-			} else {
-				g_signal_emit(media, purple_media_signals[
-						STREAM_INFO], 0, type,
-						session->id, NULL, local);
-			}
-		} else if (participant != NULL) {
-			/* Emit just the specific participant */
-			if (!g_list_find_custom(media->priv->participants,
-					participant, (GCompareFunc)strcmp)) {
-				purple_debug_warning("media",
-						"Couldn't find participant"
-						" to hangup/reject.\n");
-			} else {
-				g_signal_emit(media, purple_media_signals[
-						STREAM_INFO], 0, type, NULL,
-						participant, local);
-			}
-		}
-
-		purple_media_end(media, session_id, participant);
-		return;
-	}
-
-	g_signal_emit(media, purple_media_signals[STREAM_INFO],
-			0, type, session_id, participant, local);
-#endif
-}
-
-#ifdef USE_VV
-static void
-purple_media_new_local_candidate_cb(PurpleMediaBackend *backend,
-		const gchar *sess_id, const gchar *participant,
-		PurpleMediaCandidate *candidate, PurpleMedia *media)
-{
-	PurpleMediaSession *session =
-			purple_media_get_session(media, sess_id);
-
-	purple_media_insert_local_candidate(session, participant,
-			purple_media_candidate_copy(candidate));
-
-	g_signal_emit(session->media, purple_media_signals[NEW_CANDIDATE],
-		      0, session->id, participant, candidate);
-}
-
-static void
-purple_media_candidates_prepared_cb(PurpleMediaBackend *backend,
-		const gchar *sess_id, const gchar *name, PurpleMedia *media)
-{
-	PurpleMediaStream *stream_data;
-
-	g_return_if_fail(PURPLE_IS_MEDIA(media));
-
-	stream_data = purple_media_get_stream(media, sess_id, name);
-	stream_data->candidates_prepared = TRUE;
-
-	g_signal_emit(media, purple_media_signals[CANDIDATES_PREPARED],
-			0, sess_id, name);
-}
-
-/* callback called when a pair of transport candidates (local and remote)
- * has been established */
-static void
-purple_media_candidate_pair_established_cb(PurpleMediaBackend *backend,
-		const gchar *sess_id, const gchar *name,
-		PurpleMediaCandidate *local_candidate,
-		PurpleMediaCandidate *remote_candidate,
-		PurpleMedia *media)
-{
-	PurpleMediaStream *stream;
-	GList *iter;
-	guint id;
-
-	g_return_if_fail(PURPLE_IS_MEDIA(media));
-
-	stream = purple_media_get_stream(media, sess_id, name);
-	id = purple_media_candidate_get_component_id(local_candidate);
-
-	iter = stream->active_local_candidates;
-	for(; iter; iter = g_list_next(iter)) {
-		PurpleMediaCandidate *c = iter->data;
-		if (id == purple_media_candidate_get_component_id(c)) {
-			g_object_unref(c);
-			stream->active_local_candidates =
-					g_list_delete_link(iter, iter);
-			stream->active_local_candidates = g_list_prepend(
-					stream->active_local_candidates,
-					purple_media_candidate_copy(
-					local_candidate));
-			break;
-		}
-	}
-	if (iter == NULL)
-		stream->active_local_candidates = g_list_prepend(
-				stream->active_local_candidates,
-				purple_media_candidate_copy(
-				local_candidate));
-
-	id = purple_media_candidate_get_component_id(local_candidate);
-
-	iter = stream->active_remote_candidates;
-	for(; iter; iter = g_list_next(iter)) {
-		PurpleMediaCandidate *c = iter->data;
-		if (id == purple_media_candidate_get_component_id(c)) {
-			g_object_unref(c);
-			stream->active_remote_candidates =
-					g_list_delete_link(iter, iter);
-			stream->active_remote_candidates = g_list_prepend(
-					stream->active_remote_candidates,
-					purple_media_candidate_copy(
-					remote_candidate));
-			break;
-		}
-	}
-	if (iter == NULL)
-		stream->active_remote_candidates = g_list_prepend(
-				stream->active_remote_candidates,
-				purple_media_candidate_copy(
-				remote_candidate));
-
-	purple_debug_info("media", "candidate pair established\n");
-}
-
-static void
-purple_media_codecs_changed_cb(PurpleMediaBackend *backend,
-		const gchar *sess_id, PurpleMedia *media)
-{
-	g_signal_emit(media, purple_media_signals[CODECS_CHANGED], 0, sess_id);
-}
-#endif  /* USE_VV */
-
-gboolean
-purple_media_add_stream(PurpleMedia *media, const gchar *sess_id,
-		const gchar *who, PurpleMediaSessionType type,
-		gboolean initiator, const gchar *transmitter,
-		guint num_params, GParameter *params)
-{
-#ifdef USE_VV
-	PurpleMediaSession *session;
-	PurpleMediaStream *stream = NULL;
-
-	g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
-
-	if (!purple_media_backend_add_stream(media->priv->backend,
-			sess_id, who, type, initiator, transmitter,
-			num_params, params)) {
-		purple_debug_error("media", "Error adding stream.\n");
-		return FALSE;
-	}
-
-	session = purple_media_get_session(media, sess_id);
-
-	if (!session) {
-		session = g_new0(PurpleMediaSession, 1);
-		session->id = g_strdup(sess_id);
-		session->media = media;
-		session->type = type;
-		session->initiator = initiator;
-
-		purple_media_add_session(media, session);
-		g_signal_emit(media, purple_media_signals[STATE_CHANGED],
-				0, PURPLE_MEDIA_STATE_NEW,
-				session->id, NULL);
-	}
-
-	if (!g_list_find_custom(media->priv->participants,
-			who, (GCompareFunc)strcmp)) {
-		media->priv->participants = g_list_prepend(
-				media->priv->participants, g_strdup(who));
-
-		g_signal_emit_by_name(media, "state-changed",
-				PURPLE_MEDIA_STATE_NEW, NULL, who);
-	}
-
-	if (purple_media_get_stream(media, sess_id, who) == NULL) {
-		stream = purple_media_insert_stream(session, who, initiator);
-
-		g_signal_emit(media, purple_media_signals[STATE_CHANGED],
-				0, PURPLE_MEDIA_STATE_NEW,
-				session->id, who);
-	}
-
-	return TRUE;
-#else
-	return FALSE;
-#endif  /* USE_VV */
-}
-
-PurpleMediaManager *
-purple_media_get_manager(PurpleMedia *media)
-{
-	PurpleMediaManager *ret;
-	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
-	g_object_get(media, "manager", &ret, NULL);
-	return ret;
-}
-
-PurpleMediaSessionType
-purple_media_get_session_type(PurpleMedia *media, const gchar *sess_id)
-{
-#ifdef USE_VV
-	PurpleMediaSession *session;
-	g_return_val_if_fail(PURPLE_IS_MEDIA(media), PURPLE_MEDIA_NONE);
-	session = purple_media_get_session(media, sess_id);
-	return session->type;
-#else
-	return PURPLE_MEDIA_NONE;
-#endif
-}
-/* XXX: Should wait until codecs-ready is TRUE before using this function */
-GList *
-purple_media_get_codecs(PurpleMedia *media, const gchar *sess_id)
-{
-#ifdef USE_VV
-	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
-
-	return purple_media_backend_get_codecs(media->priv->backend, sess_id);
-#else
-	return NULL;
-#endif
-}
-
-GList *
-purple_media_get_local_candidates(PurpleMedia *media, const gchar *sess_id,
-                                  const gchar *participant)
-{
-#ifdef USE_VV
-	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
-
-	return purple_media_backend_get_local_candidates(media->priv->backend,
-			sess_id, participant);
-#else
-	return NULL;
-#endif
-}
-
-void
-purple_media_add_remote_candidates(PurpleMedia *media, const gchar *sess_id,
-                                   const gchar *participant,
-                                   GList *remote_candidates)
-{
-#ifdef USE_VV
-	PurpleMediaStream *stream;
-
-	g_return_if_fail(PURPLE_IS_MEDIA(media));
-	stream = purple_media_get_stream(media, sess_id, participant);
-
-	if (stream == NULL) {
-		purple_debug_error("media",
-				"purple_media_add_remote_candidates: "
-				"couldn't find stream %s %s.\n",
-				sess_id ? sess_id : "(null)",
-				participant ? participant : "(null)");
-		return;
-	}
-
-	stream->remote_candidates = g_list_concat(stream->remote_candidates,
-			purple_media_candidate_list_copy(remote_candidates));
-
-	purple_media_backend_add_remote_candidates(media->priv->backend,
-			sess_id, participant, remote_candidates);
-#endif
-}
-
-#if 0
-/*
- * These two functions aren't being used and I'd rather not lock in the API
- * until they are needed. If they ever are.
- */
-
-GList *
-purple_media_get_active_local_candidates(PurpleMedia *media,
-		const gchar *sess_id, const gchar *participant)
-{
-#ifdef USE_VV
-	PurpleMediaStream *stream;
-	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
-	stream = purple_media_get_stream(media, sess_id, participant);
-	return purple_media_candidate_list_copy(
-			stream->active_local_candidates);
-#else
-	return NULL;
-#endif
-}
-
-GList *
-purple_media_get_active_remote_candidates(PurpleMedia *media,
-		const gchar *sess_id, const gchar *participant)
-{
-#ifdef USE_VV
-	PurpleMediaStream *stream;
-	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
-	stream = purple_media_get_stream(media, sess_id, participant);
-	return purple_media_candidate_list_copy(
-			stream->active_remote_candidates);
-#else
-	return NULL;
-#endif
-}
-#endif
-
-gboolean
-purple_media_set_remote_codecs(PurpleMedia *media, const gchar *sess_id,
-                               const gchar *participant, GList *codecs)
-{
-#ifdef USE_VV
-	g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
-
-	return purple_media_backend_set_remote_codecs(media->priv->backend,
-			sess_id, participant, codecs);
-#else
-	return FALSE;
-#endif
-}
-
-gboolean
-purple_media_candidates_prepared(PurpleMedia *media,
-		const gchar *session_id, const gchar *participant)
-{
-#ifdef USE_VV
-	GList *streams;
-	gboolean prepared = TRUE;
-
-	g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
-
-	streams = purple_media_get_streams(media, session_id, participant);
-
-	for (; streams; streams = g_list_delete_link(streams, streams)) {
-		PurpleMediaStream *stream = streams->data;
-		if (stream->candidates_prepared == FALSE) {
-			g_list_free(streams);
-			prepared = FALSE;
-			break;
-		}
-	}
-
-	return prepared;
-#else
-	return FALSE;
-#endif
-}
-
-gboolean
-purple_media_set_send_codec(PurpleMedia *media, const gchar *sess_id, PurpleMediaCodec *codec)
-{
-#ifdef USE_VV
-	g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
-
-	return purple_media_backend_set_send_codec(
-			media->priv->backend, sess_id, codec);
-#else
-	return FALSE;
-#endif
-}
-
-gboolean
-purple_media_codecs_ready(PurpleMedia *media, const gchar *sess_id)
-{
-#ifdef USE_VV
-	g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
-
-	return purple_media_backend_codecs_ready(
-			media->priv->backend, sess_id);
-#else
-	return FALSE;
-#endif
-}
-
-gboolean
-purple_media_is_initiator(PurpleMedia *media,
-		const gchar *sess_id, const gchar *participant)
-{
-#ifdef USE_VV
-	g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
-
-	if (sess_id == NULL && participant == NULL)
-		return media->priv->initiator;
-	else if (sess_id != NULL && participant == NULL) {
-		PurpleMediaSession *session =
-				purple_media_get_session(media, sess_id);
-		return session != NULL ? session->initiator : FALSE;
-	} else if (sess_id != NULL && participant != NULL) {
-		PurpleMediaStream *stream = purple_media_get_stream(
-				media, sess_id, participant);
-		return stream != NULL ? stream->initiator : FALSE;
-	}
-#endif
-	return FALSE;
-}
-
-gboolean
-purple_media_accepted(PurpleMedia *media, const gchar *sess_id,
-		const gchar *participant)
-{
-#ifdef USE_VV
-	gboolean accepted = TRUE;
-
-	g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
-
-	if (sess_id == NULL && participant == NULL) {
-		GList *streams = media->priv->streams;
-
-		for (; streams; streams = g_list_next(streams)) {
-			PurpleMediaStream *stream = streams->data;
-			if (stream->accepted == FALSE) {
-				accepted = FALSE;
-				break;
-			}
-		}
-	} else if (sess_id != NULL && participant == NULL) {
-		GList *streams = purple_media_get_streams(
-				media, sess_id, NULL);
-		for (; streams; streams =
-				g_list_delete_link(streams, streams)) {
-			PurpleMediaStream *stream = streams->data;
-			if (stream->accepted == FALSE) {
-				g_list_free(streams);
-				accepted = FALSE;
-				break;
-			}
-		}
-	} else if (sess_id != NULL && participant != NULL) {
-		PurpleMediaStream *stream = purple_media_get_stream(
-				media, sess_id, participant);
-		if (stream == NULL || stream->accepted == FALSE)
-			accepted = FALSE;
-	}
-
-	return accepted;
-#else
-	return FALSE;
-#endif
-}
-
-void purple_media_set_input_volume(PurpleMedia *media,
-		const gchar *session_id, double level)
-{
-#ifdef USE_VV
-	g_return_if_fail(PURPLE_IS_MEDIA(media));
-	g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(media->priv->backend));
-
-	purple_media_backend_fs2_set_input_volume(
-			PURPLE_MEDIA_BACKEND_FS2(
-			media->priv->backend),
-			session_id, level);
-#endif
-}
-
-void purple_media_set_output_volume(PurpleMedia *media,
-		const gchar *session_id, const gchar *participant,
-		double level)
-{
-#ifdef USE_VV
-	g_return_if_fail(PURPLE_IS_MEDIA(media));
-	g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(media->priv->backend));
-
-	purple_media_backend_fs2_set_output_volume(
-			PURPLE_MEDIA_BACKEND_FS2(
-			media->priv->backend),
-			session_id, participant, level);
-#endif
-}
-
-gulong
-purple_media_set_output_window(PurpleMedia *media, const gchar *session_id,
-		const gchar *participant, gulong window_id)
-{
-#ifdef USE_VV
-	g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
-
-	return purple_media_manager_set_output_window(media->priv->manager,
-			media, session_id, participant, window_id);
-#else
-	return 0;
-#endif
-}
-
-void
-purple_media_remove_output_windows(PurpleMedia *media)
-{
-#ifdef USE_VV
-	GList *iter = media->priv->streams;
-	for (; iter; iter = g_list_next(iter)) {
-		PurpleMediaStream *stream = iter->data;
-		purple_media_manager_remove_output_windows(
-				media->priv->manager, media,
-				stream->session->id, stream->participant);
-	}
-
-	iter = purple_media_get_session_ids(media);
-	for (; iter; iter = g_list_delete_link(iter, iter)) {
-		gchar *session_name = iter->data;
-		purple_media_manager_remove_output_windows(
-				media->priv->manager, media,
-				session_name, NULL);
-	}
-#endif
-}
-
-#ifdef USE_GSTREAMER
-GstElement *
-purple_media_get_tee(PurpleMedia *media,
-		const gchar *session_id, const gchar *participant)
-{
-#ifdef USE_VV
-	g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
-
-	if (PURPLE_IS_MEDIA_BACKEND_FS2(media->priv->backend))
-		return purple_media_backend_fs2_get_tee(
-				PURPLE_MEDIA_BACKEND_FS2(
-				media->priv->backend),
-				session_id, participant);
-	g_return_val_if_reached(NULL);
-#else
-	return NULL;
-#endif
-}
-#endif /* USE_GSTREAMER */
-