# HG changeset patch # User Paul Aurich # Date 1265438449 0 # Node ID 419415a431e1ab49da5cafaec5f74adf3092c13a # Parent 020f46d39cf7beca0cefd9f204140dd5e64ad1a2 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. diff -r 020f46d39cf7 -r 419415a431e1 libpurple/Makefile.am --- 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 \ diff -r 020f46d39cf7 -r 419415a431e1 libpurple/media.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 */ + diff -r 020f46d39cf7 -r 419415a431e1 libpurple/media/media.c --- 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 */ -